1 // Written in the D programming language. 2 3 /** 4 This module defines the notion of a range. Ranges generalize the concept of 5 arrays, lists, or anything that involves sequential access. This abstraction 6 enables the same set of algorithms (see $(MREF std, algorithm)) to be used 7 with a vast variety of different concrete types. For example, 8 a linear search algorithm such as $(REF find, std, algorithm, searching) 9 works not just for arrays, but for linked-lists, input files, 10 incoming network data, etc. 11 12 Guides: 13 14 There are many articles available that can bolster understanding ranges: 15 16 $(UL 17 $(LI Ali Çehreli's $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges) 18 for the basics of working with and creating range-based code.) 19 $(LI Jonathan M. Davis $(LINK2 http://dconf.org/2015/talks/davis.html, $(I Introduction to Ranges)) 20 talk at DConf 2015 a vivid introduction from its core constructs to practical advice.) 21 $(LI The DLang Tour's $(LINK2 http://tour.dlang.org/tour/en/basics/ranges, chapter on ranges) 22 for an interactive introduction.) 23 $(LI H. S. Teoh's $(LINK2 http://wiki.dlang.org/Component_programming_with_ranges, tutorial on 24 component programming with ranges) for a real-world showcase of the influence 25 of range-based programming on complex algorithms.) 26 $(LI Andrei Alexandrescu's article 27 $(LINK2 http://www.informit.com/articles/printerfriendly.aspx?p=1407357$(AMP)rll=1, 28 $(I On Iteration)) for conceptual aspect of ranges and the motivation 29 ) 30 ) 31 32 Submodules: 33 34 This module has two submodules: 35 36 The $(MREF std, range, primitives) submodule 37 provides basic range functionality. It defines several templates for testing 38 whether a given object is a range, what kind of range it is, and provides 39 some common range operations. 40 41 The $(MREF std, range, interfaces) submodule 42 provides object-based interfaces for working with ranges via runtime 43 polymorphism. 44 45 The remainder of this module provides a rich set of range creation and 46 composition templates that let you construct new ranges out of existing ranges: 47 48 49 $(SCRIPT inhibitQuickIndex = 1;) 50 $(DIVC quickindex, 51 $(BOOKTABLE , 52 $(TR $(TD $(LREF chain)) 53 $(TD Concatenates several ranges into a single range. 54 )) 55 $(TR $(TD $(LREF choose)) 56 $(TD Chooses one of two ranges at runtime based on a boolean condition. 57 )) 58 $(TR $(TD $(LREF chooseAmong)) 59 $(TD Chooses one of several ranges at runtime based on an index. 60 )) 61 $(TR $(TD $(LREF chunks)) 62 $(TD Creates a range that returns fixed-size chunks of the original 63 range. 64 )) 65 $(TR $(TD $(LREF cycle)) 66 $(TD Creates an infinite range that repeats the given forward range 67 indefinitely. Good for implementing circular buffers. 68 )) 69 $(TR $(TD $(LREF drop)) 70 $(TD Creates the range that results from discarding the first $(I n) 71 elements from the given range. 72 )) 73 $(TR $(TD $(LREF dropBack)) 74 $(TD Creates the range that results from discarding the last $(I n) 75 elements from the given range. 76 )) 77 $(TR $(TD $(LREF dropExactly)) 78 $(TD Creates the range that results from discarding exactly $(I n) 79 of the first elements from the given range. 80 )) 81 $(TR $(TD $(LREF dropBackExactly)) 82 $(TD Creates the range that results from discarding exactly $(I n) 83 of the last elements from the given range. 84 )) 85 $(TR $(TD $(LREF dropOne)) 86 $(TD Creates the range that results from discarding 87 the first element from the given range. 88 )) 89 $(TR $(TD $(D $(LREF dropBackOne))) 90 $(TD Creates the range that results from discarding 91 the last element from the given range. 92 )) 93 $(TR $(TD $(LREF enumerate)) 94 $(TD Iterates a range with an attached index variable. 95 )) 96 $(TR $(TD $(LREF evenChunks)) 97 $(TD Creates a range that returns a number of chunks of 98 approximately equal length from the original range. 99 )) 100 $(TR $(TD $(LREF frontTransversal)) 101 $(TD Creates a range that iterates over the first elements of the 102 given ranges. 103 )) 104 $(TR $(TD $(LREF generate)) 105 $(TD Creates a range by successive calls to a given function. This 106 allows to create ranges as a single delegate. 107 )) 108 $(TR $(TD $(LREF indexed)) 109 $(TD Creates a range that offers a view of a given range as though 110 its elements were reordered according to a given range of indices. 111 )) 112 $(TR $(TD $(LREF iota)) 113 $(TD Creates a range consisting of numbers between a starting point 114 and ending point, spaced apart by a given interval. 115 )) 116 $(TR $(TD $(LREF lockstep)) 117 $(TD Iterates $(I n) ranges in lockstep, for use in a `foreach` 118 loop. Similar to `zip`, except that `lockstep` is designed 119 especially for `foreach` loops. 120 )) 121 $(TR $(TD $(LREF nullSink)) 122 $(TD An output range that discards the data it receives. 123 )) 124 $(TR $(TD $(LREF only)) 125 $(TD Creates a range that iterates over the given arguments. 126 )) 127 $(TR $(TD $(LREF padLeft)) 128 $(TD Pads a range to a specified length by adding a given element to 129 the front of the range. Is lazy if the range has a known length. 130 )) 131 $(TR $(TD $(LREF padRight)) 132 $(TD Lazily pads a range to a specified length by adding a given element to 133 the back of the range. 134 )) 135 $(TR $(TD $(LREF radial)) 136 $(TD Given a random-access range and a starting point, creates a 137 range that alternately returns the next left and next right element to 138 the starting point. 139 )) 140 $(TR $(TD $(LREF recurrence)) 141 $(TD Creates a forward range whose values are defined by a 142 mathematical recurrence relation. 143 )) 144 $(TR $(TD $(LREF refRange)) 145 $(TD Pass a range by reference. Both the original range and the RefRange 146 will always have the exact same elements. 147 Any operation done on one will affect the other. 148 )) 149 $(TR $(TD $(LREF repeat)) 150 $(TD Creates a range that consists of a single element repeated $(I n) 151 times, or an infinite range repeating that element indefinitely. 152 )) 153 $(TR $(TD $(LREF retro)) 154 $(TD Iterates a bidirectional range backwards. 155 )) 156 $(TR $(TD $(LREF roundRobin)) 157 $(TD Given $(I n) ranges, creates a new range that return the $(I n) 158 first elements of each range, in turn, then the second element of each 159 range, and so on, in a round-robin fashion. 160 )) 161 $(TR $(TD $(LREF sequence)) 162 $(TD Similar to `recurrence`, except that a random-access range is 163 created. 164 )) 165 $(TR $(TD $(D $(LREF slide))) 166 $(TD Creates a range that returns a fixed-size sliding window 167 over the original range. Unlike chunks, 168 it advances a configurable number of items at a time, 169 not one chunk at a time. 170 )) 171 $(TR $(TD $(LREF stride)) 172 $(TD Iterates a range with stride $(I n). 173 )) 174 $(TR $(TD $(LREF tail)) 175 $(TD Return a range advanced to within `n` elements of the end of 176 the given range. 177 )) 178 $(TR $(TD $(LREF take)) 179 $(TD Creates a sub-range consisting of only up to the first $(I n) 180 elements of the given range. 181 )) 182 $(TR $(TD $(LREF takeExactly)) 183 $(TD Like `take`, but assumes the given range actually has $(I n) 184 elements, and therefore also defines the `length` property. 185 )) 186 $(TR $(TD $(LREF takeNone)) 187 $(TD Creates a random-access range consisting of zero elements of the 188 given range. 189 )) 190 $(TR $(TD $(LREF takeOne)) 191 $(TD Creates a random-access range consisting of exactly the first 192 element of the given range. 193 )) 194 $(TR $(TD $(LREF tee)) 195 $(TD Creates a range that wraps a given range, forwarding along 196 its elements while also calling a provided function with each element. 197 )) 198 $(TR $(TD $(LREF transposed)) 199 $(TD Transposes a range of ranges. 200 )) 201 $(TR $(TD $(LREF transversal)) 202 $(TD Creates a range that iterates over the $(I n)'th elements of the 203 given random-access ranges. 204 )) 205 $(TR $(TD $(LREF zip)) 206 $(TD Given $(I n) ranges, creates a range that successively returns a 207 tuple of all the first elements, a tuple of all the second elements, 208 etc. 209 )) 210 )) 211 212 Sortedness: 213 214 Ranges whose elements are sorted afford better efficiency with certain 215 operations. For this, the $(LREF assumeSorted) function can be used to 216 construct a $(LREF SortedRange) from a pre-sorted range. The $(REF 217 sort, std, algorithm, sorting) function also conveniently 218 returns a $(LREF SortedRange). $(LREF SortedRange) objects provide some additional 219 range operations that take advantage of the fact that the range is sorted. 220 221 Source: $(PHOBOSSRC std/range/package.d) 222 223 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). 224 225 Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, 226 $(HTTP jmdavisprog.com, Jonathan M Davis), and Jack Stouffer. Credit 227 for some of the ideas in building this module goes to 228 $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). 229 */ 230 module std.range; 231 232 public import std.array; 233 public import std.range.interfaces; 234 public import std.range.primitives; 235 public import std.typecons : Flag, Yes, No, Rebindable, rebindable; 236 237 import std.internal.attributes : betterC; 238 import std.meta : aliasSeqOf, allSatisfy, anySatisfy, staticMap; 239 import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral, 240 isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf; 241 242 243 /** 244 Iterates a bidirectional range backwards. The original range can be 245 accessed by using the `source` property. Applying retro twice to 246 the same range yields the original range. 247 248 Params: 249 r = the bidirectional range to iterate backwards 250 251 Returns: 252 A bidirectional range with length if `r` also provides a length. Or, 253 if `r` is a random access range, then the return value will be random 254 access as well. 255 See_Also: 256 $(REF reverse, std,algorithm,mutation) for mutating the source range directly. 257 */ 258 auto retro(Range)(Range r) 259 if (isBidirectionalRange!(Unqual!Range)) 260 { 261 // Check for retro(retro(r)) and just return r in that case 262 static if (is(typeof(retro(r.source)) == Range)) 263 { 264 return r.source; 265 } 266 else 267 { 268 static struct Result() 269 { 270 private alias R = Unqual!Range; 271 272 // User code can get and set source, too 273 R source; 274 275 static if (hasLength!R) 276 { 277 size_t retroIndex(size_t n) 278 { 279 return source.length - n - 1; 280 } 281 } 282 283 public: 284 alias Source = R; 285 286 @property bool empty() { return source.empty; } 287 @property auto save() 288 { 289 return Result(source.save); 290 } 291 @property auto ref front() { return source.back; } 292 void popFront() { source.popBack(); } 293 @property auto ref back() { return source.front; } 294 void popBack() { source.popFront(); } 295 296 static if (is(typeof(source.moveBack()))) 297 { 298 ElementType!R moveFront() 299 { 300 return source.moveBack(); 301 } 302 } 303 304 static if (is(typeof(source.moveFront()))) 305 { 306 ElementType!R moveBack() 307 { 308 return source.moveFront(); 309 } 310 } 311 312 static if (hasAssignableElements!R) 313 { 314 @property void front(ElementType!R val) 315 { 316 import std.algorithm.mutation : move; 317 318 source.back = move(val); 319 } 320 321 @property void back(ElementType!R val) 322 { 323 import std.algorithm.mutation : move; 324 325 source.front = move(val); 326 } 327 } 328 329 static if (isRandomAccessRange!(R) && hasLength!(R)) 330 { 331 auto ref opIndex(size_t n) { return source[retroIndex(n)]; } 332 333 static if (hasAssignableElements!R) 334 { 335 void opIndexAssign(ElementType!R val, size_t n) 336 { 337 import std.algorithm.mutation : move; 338 339 source[retroIndex(n)] = move(val); 340 } 341 } 342 343 static if (is(typeof(source.moveAt(0)))) 344 { 345 ElementType!R moveAt(size_t index) 346 { 347 return source.moveAt(retroIndex(index)); 348 } 349 } 350 351 static if (hasSlicing!R) 352 typeof(this) opSlice(size_t a, size_t b) 353 { 354 return typeof(this)(source[source.length - b .. source.length - a]); 355 } 356 } 357 358 mixin ImplementLength!source; 359 } 360 361 return Result!()(r); 362 } 363 } 364 365 366 /// 367 pure @safe nothrow @nogc unittest 368 { 369 import std.algorithm.comparison : equal; 370 int[5] a = [ 1, 2, 3, 4, 5 ]; 371 int[5] b = [ 5, 4, 3, 2, 1 ]; 372 assert(equal(retro(a[]), b[])); 373 assert(retro(a[]).source is a[]); 374 assert(retro(retro(a[])) is a[]); 375 } 376 377 pure @safe nothrow unittest 378 { 379 import std.algorithm.comparison : equal; 380 static assert(isBidirectionalRange!(typeof(retro("hello")))); 381 int[] a; 382 static assert(is(typeof(a) == typeof(retro(retro(a))))); 383 assert(retro(retro(a)) is a); 384 static assert(isRandomAccessRange!(typeof(retro([1, 2, 3])))); 385 void test(int[] input, int[] witness) 386 { 387 auto r = retro(input); 388 assert(r.front == witness.front); 389 assert(r.back == witness.back); 390 assert(equal(r, witness)); 391 } 392 test([ 1 ], [ 1 ]); 393 test([ 1, 2 ], [ 2, 1 ]); 394 test([ 1, 2, 3 ], [ 3, 2, 1 ]); 395 test([ 1, 2, 3, 4 ], [ 4, 3, 2, 1 ]); 396 test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]); 397 test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]); 398 399 immutable foo = [1,2,3].idup; 400 auto r = retro(foo); 401 assert(equal(r, [3, 2, 1])); 402 } 403 404 pure @safe nothrow unittest 405 { 406 import std.internal.test.dummyrange : AllDummyRanges, propagatesRangeType, 407 ReturnBy; 408 409 foreach (DummyType; AllDummyRanges) 410 { 411 static if (!isBidirectionalRange!DummyType) 412 { 413 static assert(!__traits(compiles, Retro!DummyType)); 414 } 415 else 416 { 417 DummyType dummyRange; 418 dummyRange.reinit(); 419 420 auto myRetro = retro(dummyRange); 421 static assert(propagatesRangeType!(typeof(myRetro), DummyType)); 422 assert(myRetro.front == 10); 423 assert(myRetro.back == 1); 424 assert(myRetro.moveFront() == 10); 425 assert(myRetro.moveBack() == 1); 426 427 static if (isRandomAccessRange!DummyType && hasLength!DummyType) 428 { 429 assert(myRetro[0] == myRetro.front); 430 assert(myRetro.moveAt(2) == 8); 431 432 static if (DummyType.r == ReturnBy.Reference) 433 { 434 { 435 myRetro[9]++; 436 scope(exit) myRetro[9]--; 437 assert(dummyRange[0] == 2); 438 myRetro.front++; 439 scope(exit) myRetro.front--; 440 assert(myRetro.front == 11); 441 myRetro.back++; 442 scope(exit) myRetro.back--; 443 assert(myRetro.back == 3); 444 } 445 446 { 447 myRetro.front = 0xFF; 448 scope(exit) myRetro.front = 10; 449 assert(dummyRange.back == 0xFF); 450 451 myRetro.back = 0xBB; 452 scope(exit) myRetro.back = 1; 453 assert(dummyRange.front == 0xBB); 454 455 myRetro[1] = 11; 456 scope(exit) myRetro[1] = 8; 457 assert(dummyRange[8] == 11); 458 } 459 } 460 } 461 } 462 } 463 } 464 465 pure @safe nothrow @nogc unittest 466 { 467 import std.algorithm.comparison : equal; 468 auto LL = iota(1L, 4L); 469 auto r = retro(LL); 470 long[3] excepted = [3, 2, 1]; 471 assert(equal(r, excepted[])); 472 } 473 474 // https://issues.dlang.org/show_bug.cgi?id=12662 475 pure @safe nothrow @nogc unittest 476 { 477 int[3] src = [1,2,3]; 478 int[] data = src[]; 479 foreach_reverse (x; data) {} 480 foreach (x; data.retro) {} 481 } 482 483 pure @safe nothrow unittest 484 { 485 import std.algorithm.comparison : equal; 486 487 static struct S { 488 int v; 489 @disable this(this); 490 } 491 492 immutable foo = [S(1), S(2), S(3)]; 493 auto r = retro(foo); 494 assert(equal(r, [S(3), S(2), S(1)])); 495 } 496 497 /** 498 Iterates range `r` with stride `n`. If the range is a 499 random-access range, moves by indexing into the range; otherwise, 500 moves by successive calls to `popFront`. Applying stride twice to 501 the same range results in a stride with a step that is the 502 product of the two applications. It is an error for `n` to be 0. 503 504 Params: 505 r = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to stride over 506 n = the number of elements to skip over 507 508 Returns: 509 At minimum, an input range. The resulting range will adopt the 510 range primitives of the underlying range as long as 511 $(REF hasLength, std,range,primitives) is `true`. 512 */ 513 auto stride(Range)(Range r, size_t n) 514 if (isInputRange!(Unqual!Range)) 515 in 516 { 517 assert(n != 0, "stride cannot have step zero."); 518 } 519 do 520 { 521 import std.algorithm.comparison : min; 522 523 static if (is(typeof(stride(r.source, n)) == Range)) 524 { 525 // stride(stride(r, n1), n2) is stride(r, n1 * n2) 526 return stride(r.source, r._n * n); 527 } 528 else 529 { 530 static struct Result 531 { 532 private alias R = Unqual!Range; 533 public R source; 534 private size_t _n; 535 536 // Chop off the slack elements at the end 537 static if (hasLength!R && 538 (isRandomAccessRange!R && hasSlicing!R 539 || isBidirectionalRange!R)) 540 private void eliminateSlackElements() 541 { 542 auto slack = source.length % _n; 543 544 if (slack) 545 { 546 slack--; 547 } 548 else if (!source.empty) 549 { 550 slack = min(_n, source.length) - 1; 551 } 552 else 553 { 554 slack = 0; 555 } 556 if (!slack) return; 557 static if (isRandomAccessRange!R && hasLength!R && hasSlicing!R) 558 { 559 source = source[0 .. source.length - slack]; 560 } 561 else static if (isBidirectionalRange!R) 562 { 563 foreach (i; 0 .. slack) 564 { 565 source.popBack(); 566 } 567 } 568 } 569 570 static if (isForwardRange!R) 571 { 572 @property auto save() 573 { 574 return Result(source.save, _n); 575 } 576 } 577 578 static if (isInfinite!R) 579 { 580 enum bool empty = false; 581 } 582 else 583 { 584 @property bool empty() 585 { 586 return source.empty; 587 } 588 } 589 590 @property auto ref front() 591 { 592 return source.front; 593 } 594 595 static if (is(typeof(.moveFront(source)))) 596 { 597 ElementType!R moveFront() 598 { 599 return source.moveFront(); 600 } 601 } 602 603 static if (hasAssignableElements!R) 604 { 605 @property void front(ElementType!R val) 606 { 607 import std.algorithm.mutation : move; 608 609 source.front = move(val); 610 } 611 } 612 613 void popFront() 614 { 615 source.popFrontN(_n); 616 } 617 618 static if (isBidirectionalRange!R && hasLength!R) 619 { 620 void popBack() 621 { 622 popBackN(source, _n); 623 } 624 625 @property auto ref back() 626 { 627 eliminateSlackElements(); 628 return source.back; 629 } 630 631 static if (is(typeof(.moveBack(source)))) 632 { 633 ElementType!R moveBack() 634 { 635 eliminateSlackElements(); 636 return source.moveBack(); 637 } 638 } 639 640 static if (hasAssignableElements!R) 641 { 642 @property void back(ElementType!R val) 643 { 644 eliminateSlackElements(); 645 source.back = val; 646 } 647 } 648 } 649 650 static if (isRandomAccessRange!R && hasLength!R) 651 { 652 auto ref opIndex(size_t n) 653 { 654 return source[_n * n]; 655 } 656 657 /** 658 Forwards to $(D moveAt(source, n)). 659 */ 660 static if (is(typeof(source.moveAt(0)))) 661 { 662 ElementType!R moveAt(size_t n) 663 { 664 return source.moveAt(_n * n); 665 } 666 } 667 668 static if (hasAssignableElements!R) 669 { 670 void opIndexAssign(ElementType!R val, size_t n) 671 { 672 source[_n * n] = val; 673 } 674 } 675 } 676 677 static if (hasSlicing!R && hasLength!R) 678 typeof(this) opSlice(size_t lower, size_t upper) 679 { 680 assert(upper >= lower && upper <= length, 681 "Attempt to get out-of-bounds slice of `stride` range"); 682 immutable translatedUpper = (upper == 0) ? 0 : 683 (upper * _n - (_n - 1)); 684 immutable translatedLower = min(lower * _n, translatedUpper); 685 686 assert(translatedLower <= translatedUpper, 687 "Overflow when calculating slice of `stride` range"); 688 689 return typeof(this)(source[translatedLower .. translatedUpper], _n); 690 } 691 692 static if (hasLength!R) 693 { 694 @property auto length() 695 { 696 return (source.length + _n - 1) / _n; 697 } 698 699 alias opDollar = length; 700 } 701 } 702 return Result(r, n); 703 } 704 } 705 706 /// 707 pure @safe nothrow unittest 708 { 709 import std.algorithm.comparison : equal; 710 711 int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]; 712 assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][])); 713 assert(stride(stride(a, 2), 3) == stride(a, 6)); 714 } 715 716 pure @safe nothrow @nogc unittest 717 { 718 import std.algorithm.comparison : equal; 719 720 int[4] testArr = [1,2,3,4]; 721 static immutable result = [1, 3]; 722 assert(equal(testArr[].stride(2), result)); 723 } 724 725 debug pure nothrow @system unittest 726 {//check the contract 727 int[4] testArr = [1,2,3,4]; 728 bool passed = false; 729 scope (success) assert(passed); 730 import core.exception : AssertError; 731 //std.exception.assertThrown won't do because it can't infer nothrow 732 // https://issues.dlang.org/show_bug.cgi?id=12647 733 try 734 { 735 auto unused = testArr[].stride(0); 736 } 737 catch (AssertError unused) 738 { 739 passed = true; 740 } 741 } 742 743 pure @safe nothrow unittest 744 { 745 import std.algorithm.comparison : equal; 746 import std.internal.test.dummyrange : AllDummyRanges, propagatesRangeType, 747 ReturnBy; 748 749 static assert(isRandomAccessRange!(typeof(stride([1, 2, 3], 2)))); 750 void test(size_t n, int[] input, int[] witness) 751 { 752 assert(equal(stride(input, n), witness)); 753 } 754 test(1, [], []); 755 int[] arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 756 assert(stride(stride(arr, 2), 3) is stride(arr, 6)); 757 test(1, arr, arr); 758 test(2, arr, [1, 3, 5, 7, 9]); 759 test(3, arr, [1, 4, 7, 10]); 760 test(4, arr, [1, 5, 9]); 761 762 // Test slicing. 763 auto s1 = stride(arr, 1); 764 assert(equal(s1[1 .. 4], [2, 3, 4])); 765 assert(s1[1 .. 4].length == 3); 766 assert(equal(s1[1 .. 5], [2, 3, 4, 5])); 767 assert(s1[1 .. 5].length == 4); 768 assert(s1[0 .. 0].empty); 769 assert(s1[3 .. 3].empty); 770 // assert(s1[$ .. $].empty); 771 assert(s1[s1.opDollar .. s1.opDollar].empty); 772 773 auto s2 = stride(arr, 2); 774 assert(equal(s2[0 .. 2], [1,3])); 775 assert(s2[0 .. 2].length == 2); 776 assert(equal(s2[1 .. 5], [3, 5, 7, 9])); 777 assert(s2[1 .. 5].length == 4); 778 assert(s2[0 .. 0].empty); 779 assert(s2[3 .. 3].empty); 780 // assert(s2[$ .. $].empty); 781 assert(s2[s2.opDollar .. s2.opDollar].empty); 782 783 // Test fix for https://issues.dlang.org/show_bug.cgi?id=5035 784 auto m = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]; // 3 rows, 4 columns 785 auto col = stride(m, 4); 786 assert(equal(col, [1, 1, 1])); 787 assert(equal(retro(col), [1, 1, 1])); 788 789 immutable int[] immi = [ 1, 2, 3 ]; 790 static assert(isRandomAccessRange!(typeof(stride(immi, 1)))); 791 792 // Check for infiniteness propagation. 793 static assert(isInfinite!(typeof(stride(repeat(1), 3)))); 794 795 foreach (DummyType; AllDummyRanges) 796 { 797 DummyType dummyRange; 798 dummyRange.reinit(); 799 800 auto myStride = stride(dummyRange, 4); 801 802 // Should fail if no length and bidirectional b/c there's no way 803 // to know how much slack we have. 804 static if (hasLength!DummyType || !isBidirectionalRange!DummyType) 805 { 806 static assert(propagatesRangeType!(typeof(myStride), DummyType)); 807 } 808 assert(myStride.front == 1); 809 assert(myStride.moveFront() == 1); 810 assert(equal(myStride, [1, 5, 9])); 811 812 static if (hasLength!DummyType) 813 { 814 assert(myStride.length == 3); 815 } 816 817 static if (isBidirectionalRange!DummyType && hasLength!DummyType) 818 { 819 assert(myStride.back == 9); 820 assert(myStride.moveBack() == 9); 821 } 822 823 static if (isRandomAccessRange!DummyType && hasLength!DummyType) 824 { 825 assert(myStride[0] == 1); 826 assert(myStride[1] == 5); 827 assert(myStride.moveAt(1) == 5); 828 assert(myStride[2] == 9); 829 830 static assert(hasSlicing!(typeof(myStride))); 831 } 832 833 static if (DummyType.r == ReturnBy.Reference) 834 { 835 // Make sure reference is propagated. 836 837 { 838 myStride.front++; 839 scope(exit) myStride.front--; 840 assert(dummyRange.front == 2); 841 } 842 { 843 myStride.front = 4; 844 scope(exit) myStride.front = 1; 845 assert(dummyRange.front == 4); 846 } 847 848 static if (isBidirectionalRange!DummyType && hasLength!DummyType) 849 { 850 { 851 myStride.back++; 852 scope(exit) myStride.back--; 853 assert(myStride.back == 10); 854 } 855 { 856 myStride.back = 111; 857 scope(exit) myStride.back = 9; 858 assert(myStride.back == 111); 859 } 860 861 static if (isRandomAccessRange!DummyType) 862 { 863 { 864 myStride[1]++; 865 scope(exit) myStride[1]--; 866 assert(dummyRange[4] == 6); 867 } 868 { 869 myStride[1] = 55; 870 scope(exit) myStride[1] = 5; 871 assert(dummyRange[4] == 55); 872 } 873 } 874 } 875 } 876 } 877 } 878 879 pure @safe nothrow unittest 880 { 881 import std.algorithm.comparison : equal; 882 883 auto LL = iota(1L, 10L); 884 auto s = stride(LL, 3); 885 assert(equal(s, [1L, 4L, 7L])); 886 } 887 888 pure @safe nothrow unittest 889 { 890 import std.algorithm.comparison : equal; 891 892 static struct S { 893 int v; 894 @disable this(this); 895 } 896 897 immutable foo = [S(1), S(2), S(3), S(4), S(5)]; 898 auto r = stride(foo, 3); 899 assert(equal(r, [S(1), S(4)])); 900 } 901 902 /** 903 Spans multiple ranges in sequence. The function `chain` takes any 904 number of ranges and returns a $(D Chain!(R1, R2,...)) object. The 905 ranges may be different, but they must have the same element type. The 906 result is a range that offers the `front`, `popFront`, and $(D 907 empty) primitives. If all input ranges offer random access and $(D 908 length), `Chain` offers them as well. 909 910 Note that repeated random access of the resulting range is likely 911 to perform somewhat badly since lengths of the ranges in the chain have to be 912 added up for each random access operation. Random access to elements of 913 the first remaining range is still efficient. 914 915 If only one range is offered to `Chain` or `chain`, the $(D 916 Chain) type exits the picture by aliasing itself directly to that 917 range's type. 918 919 Params: 920 rs = the $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) to chain together 921 922 Returns: 923 An input range at minimum. If all of the ranges in `rs` provide 924 a range primitive, the returned range will also provide that range 925 primitive. 926 927 See_Also: $(LREF only) to chain values to a range 928 */ 929 auto chain(Ranges...)(Ranges rs) 930 if (Ranges.length > 0 && 931 allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && 932 !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void)) 933 { 934 static if (Ranges.length == 1) 935 { 936 return rs[0]; 937 } 938 else 939 { 940 static struct Result 941 { 942 private: 943 alias R = staticMap!(Unqual, Ranges); 944 alias RvalueElementType = CommonType!(staticMap!(.ElementType, R)); 945 template sameET(A) 946 { 947 enum sameET = is(.ElementType!A == RvalueElementType); 948 } 949 950 enum bool allSameType = allSatisfy!(sameET, R), 951 bidirectional = allSatisfy!(isBidirectionalRange, R), 952 mobileElements = allSatisfy!(hasMobileElements, R), 953 assignableElements = allSameType 954 && allSatisfy!(hasAssignableElements, R); 955 956 alias ElementType = RvalueElementType; 957 958 static if (allSameType && allSatisfy!(hasLvalueElements, R)) 959 { 960 static ref RvalueElementType fixRef(ref RvalueElementType val) 961 { 962 return val; 963 } 964 } 965 else 966 { 967 static RvalueElementType fixRef(RvalueElementType val) 968 { 969 return val; 970 } 971 } 972 973 R source; 974 size_t frontIndex; 975 // Always points to index one past the last non-empty range, 976 // because otherwise decrementing while pointing to first range 977 // would overflow to size_t.max. 978 static if (bidirectional) size_t backIndex; 979 else enum backIndex = source.length; 980 981 this(typeof(Result.tupleof) fields) 982 { 983 this.tupleof = fields; 984 } 985 986 public: 987 this(R input) 988 { 989 frontIndex = source.length; 990 static if (bidirectional) backIndex = 0; 991 992 foreach (i, ref v; input) source[i] = v; 993 994 // We do this separately to avoid invoking `empty` needlessly. 995 // While not recommended, a range may depend on side effects of 996 // `empty` call. 997 foreach (i, ref v; input) if (!v.empty) 998 { 999 frontIndex = i; 1000 static if (bidirectional) backIndex = i+1; 1001 break; 1002 } 1003 1004 // backIndex is already set in the first loop to 1005 // as frontIndex+1, so we'll use that if we don't find a 1006 // non-empty range here. 1007 static if (bidirectional) 1008 static foreach_reverse (i; 1 .. R.length + 1) 1009 { 1010 if (i <= frontIndex + 1) return; 1011 if (!input[i-1].empty) 1012 { 1013 backIndex = i; 1014 return; 1015 } 1016 } 1017 } 1018 1019 import std.meta : anySatisfy; 1020 1021 static if (anySatisfy!(isInfinite, R)) 1022 { 1023 // Propagate infiniteness. 1024 enum bool empty = false; 1025 } 1026 else 1027 { 1028 @property bool empty() => frontIndex >= backIndex; 1029 } 1030 1031 static if (allSatisfy!(isForwardRange, R)) 1032 { 1033 @property auto save() 1034 { 1035 auto saveI(size_t i)() => source[i].save; 1036 1037 // TODO: this has the constructor needlessly refind 1038 // frontIndex and backIndex. It'd be better to just copy 1039 // those from `.this`. 1040 auto saveResult = 1041 Result(staticMap!(saveI, aliasSeqOf!(R.length.iota))); 1042 1043 return saveResult; 1044 } 1045 } 1046 1047 void popFront() 1048 { 1049 sw1: switch (frontIndex) 1050 { 1051 static foreach (i; 0 .. R.length) 1052 { 1053 case i: 1054 source[i].popFront(); 1055 break sw1; 1056 } 1057 1058 case R.length: 1059 assert(0, "Attempt to `popFront` of empty `chain` range"); 1060 1061 default: 1062 assert(0, "Internal library error. Please report it."); 1063 } 1064 1065 sw2: switch (frontIndex) 1066 { 1067 static foreach (i; 0 .. R.length) 1068 { 1069 case i: 1070 if (source[i].empty) 1071 { 1072 frontIndex++; 1073 goto case; 1074 } 1075 else break sw2; 1076 } 1077 1078 // Only possible to reach from goto of previous case. 1079 case R.length: 1080 break; 1081 1082 default: 1083 assert(0, "Internal library error. Please report it."); 1084 } 1085 } 1086 1087 @property auto ref front() 1088 { 1089 switch (frontIndex) 1090 { 1091 static foreach (i; 0 .. R.length) 1092 { 1093 case i: 1094 return fixRef(source[i].front); 1095 } 1096 1097 case R.length: 1098 assert(0, "Attempt to get `front` of empty `chain` range"); 1099 1100 default: 1101 assert(0, "Internal library error. Please report it."); 1102 } 1103 } 1104 1105 static if (assignableElements) 1106 { 1107 // @@@BUG@@@ 1108 //@property void front(T)(T v) if (is(T : RvalueElementType)) 1109 1110 @property void front(RvalueElementType v) 1111 { 1112 import std.algorithm.mutation : move; 1113 1114 sw: switch (frontIndex) 1115 { 1116 static foreach (i; 0 .. R.length) 1117 { 1118 case i: 1119 source[i].front = move(v); 1120 break sw; 1121 } 1122 1123 case R.length: 1124 assert(0, "Attempt to set `front` of empty `chain` range"); 1125 1126 default: 1127 assert(0, "Internal library error. Please report it."); 1128 } 1129 } 1130 } 1131 1132 static if (mobileElements) 1133 { 1134 RvalueElementType moveFront() 1135 { 1136 switch (frontIndex) 1137 { 1138 static foreach (i; 0 .. R.length) 1139 { 1140 case i: 1141 return source[i].moveFront(); 1142 } 1143 1144 case R.length: 1145 assert(0, "Attempt to `moveFront` of empty `chain` range"); 1146 1147 default: 1148 assert(0, "Internal library error. Please report it."); 1149 } 1150 } 1151 } 1152 1153 static if (bidirectional) 1154 { 1155 @property auto ref back() 1156 { 1157 switch (backIndex) 1158 { 1159 static foreach_reverse (i; 1 .. R.length + 1) 1160 { 1161 case i: 1162 return fixRef(source[i-1].back); 1163 } 1164 1165 case 0: 1166 assert(0, "Attempt to get `back` of empty `chain` range"); 1167 1168 default: 1169 assert(0, "Internal library error. Please report it."); 1170 } 1171 } 1172 1173 void popBack() 1174 { 1175 sw1: switch (backIndex) 1176 { 1177 static foreach_reverse (i; 1 .. R.length + 1) 1178 { 1179 case i: 1180 source[i-1].popBack(); 1181 break sw1; 1182 } 1183 1184 case 0: 1185 assert(0, "Attempt to `popFront` of empty `chain` range"); 1186 1187 default: 1188 assert(0, "Internal library error. Please report it."); 1189 } 1190 1191 sw2: switch (backIndex) 1192 { 1193 static foreach_reverse (i; 1 .. R.length + 1) 1194 { 1195 case i: 1196 if (source[i-1].empty) 1197 { 1198 backIndex--; 1199 goto case; 1200 } 1201 else break sw2; 1202 } 1203 1204 // Only possible to reach from goto of previous case. 1205 case 0: 1206 break; 1207 1208 default: 1209 assert(0, "Internal library error. Please report it."); 1210 } 1211 } 1212 1213 static if (mobileElements) 1214 { 1215 RvalueElementType moveBack() 1216 { 1217 switch (backIndex) 1218 { 1219 static foreach_reverse (i; 1 .. R.length + 1) 1220 { 1221 case i: 1222 return source[i-1].moveBack(); 1223 } 1224 1225 case 0: 1226 assert(0, "Attempt to `moveBack` of empty `chain` range"); 1227 1228 default: 1229 assert(0, "Internal library error. Please report it."); 1230 } 1231 } 1232 } 1233 1234 static if (allSameType && allSatisfy!(hasAssignableElements, R)) 1235 { 1236 @property void back(RvalueElementType v) 1237 { 1238 import std.algorithm.mutation : move; 1239 1240 sw: switch (backIndex) 1241 { 1242 static foreach_reverse (i; 1 .. R.length + 1) 1243 { 1244 case i: 1245 source[i-1].back = move(v); 1246 break sw; 1247 } 1248 1249 case 0: 1250 assert(0, "Attempt to set `back` of empty `chain` range"); 1251 1252 default: 1253 assert(0, "Internal library error. Please report it."); 1254 } 1255 } 1256 } 1257 } 1258 1259 static if (allSatisfy!(hasLength, R)) 1260 { 1261 @property size_t length() 1262 { 1263 size_t result = 0; 1264 sw: switch (frontIndex) 1265 { 1266 static foreach (i; 0 .. R.length) 1267 { 1268 case i: 1269 result += source[i].length; 1270 if (backIndex == i+1) break sw; 1271 else goto case; 1272 } 1273 1274 case R.length: 1275 break; 1276 1277 default: 1278 assert(0, "Internal library error. Please report it."); 1279 } 1280 1281 return result; 1282 } 1283 1284 alias opDollar = length; 1285 } 1286 1287 static if (allSatisfy!(isRandomAccessRange, R)) 1288 { 1289 auto ref opIndex(size_t index) 1290 { 1291 switch (frontIndex) 1292 { 1293 static foreach (i; 0 .. R.length) 1294 { 1295 case i: 1296 static if (!isInfinite!(R[i])) 1297 { 1298 immutable length = source[i].length; 1299 if (index >= length) 1300 { 1301 index -= length; 1302 goto case; 1303 } 1304 } 1305 1306 return fixRef(source[i][index]); 1307 } 1308 1309 case R.length: 1310 assert(0, "Attempt to access out-of-bounds index of `chain` range"); 1311 1312 default: 1313 assert(0, "Internal library error. Please report it."); 1314 } 1315 } 1316 1317 static if (mobileElements) 1318 { 1319 RvalueElementType moveAt(size_t index) 1320 { 1321 switch (frontIndex) 1322 { 1323 static foreach (i; 0 .. R.length) 1324 { 1325 case i: 1326 static if (!isInfinite!(R[i])) 1327 { 1328 immutable length = source[i].length; 1329 if (index >= length) 1330 { 1331 index -= length; 1332 goto case; 1333 } 1334 } 1335 1336 return source[i].moveAt(index); 1337 } 1338 1339 case R.length: 1340 assert(0, "Attempt to move out-of-bounds index of `chain` range"); 1341 1342 default: 1343 assert(0, "Internal library error. Please report it."); 1344 } 1345 } 1346 } 1347 1348 static if (allSameType && allSatisfy!(hasAssignableElements, R)) 1349 void opIndexAssign(ElementType v, size_t index) 1350 { 1351 import std.algorithm.mutation : move; 1352 1353 sw: switch (frontIndex) 1354 { 1355 static foreach (i; 0 .. R.length) 1356 { 1357 case i: 1358 static if (!isInfinite!(R[i])) 1359 { 1360 immutable length = source[i].length; 1361 if (index >= length) 1362 { 1363 index -= length; 1364 goto case; 1365 } 1366 } 1367 1368 source[i][index] = move(v); 1369 break sw; 1370 } 1371 1372 case R.length: 1373 assert(0, "Attempt to write out-of-bounds index of `chain` range"); 1374 1375 default: 1376 assert(0, "Internal library error. Please report it."); 1377 } 1378 } 1379 } 1380 1381 static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R)) 1382 auto opSlice(size_t begin, size_t end) return scope 1383 { 1384 // force staticMap type conversion to Rebindable 1385 static struct ResultRanges 1386 { 1387 staticMap!(Rebindable, typeof(source)) fields; 1388 } 1389 auto sourceI(size_t i)() => rebindable(this.source[i]); 1390 auto resultRanges = ResultRanges(staticMap!(sourceI, aliasSeqOf!(R.length.iota))).fields; 1391 size_t resultFrontIndex = this.frontIndex; 1392 static if (bidirectional) 1393 size_t resultBackIndex = this.backIndex; 1394 1395 sw: switch (frontIndex) 1396 { 1397 static foreach (i; 0 .. R.length) 1398 { 1399 case i: 1400 immutable len = resultRanges[i].length; 1401 if (len <= begin) 1402 { 1403 resultRanges[i] = resultRanges[i] 1404 [len .. len]; 1405 begin -= len; 1406 resultFrontIndex++; 1407 goto case; 1408 } 1409 else 1410 { 1411 resultRanges[i] = resultRanges[i] 1412 [begin .. len]; 1413 break sw; 1414 } 1415 } 1416 1417 case R.length: 1418 assert(begin == 0, 1419 "Attempt to access out-of-bounds slice of `chain` range"); 1420 break; 1421 1422 default: 1423 assert(0, "Internal library error. Please report it."); 1424 } 1425 1426 // Overflow intentional if end index too big. 1427 // This will trigger the bounds check failure below. 1428 auto cut = length - end; 1429 1430 sw2: switch (backIndex) 1431 { 1432 static foreach_reverse (i; 1 .. R.length + 1) 1433 { 1434 case i: 1435 immutable len = resultRanges[i-1].length; 1436 if (len <= cut) 1437 { 1438 resultRanges[i-1] = resultRanges[i-1] 1439 [0 .. 0]; 1440 cut -= len; 1441 resultBackIndex--; 1442 goto case; 1443 } 1444 else 1445 { 1446 resultRanges[i-1] = resultRanges[i-1] 1447 [0 .. len - cut]; 1448 break sw2; 1449 } 1450 } 1451 1452 case 0: 1453 assert(cut == 0, end > length? 1454 "Attempt to access out-of-bounds slice of `chain` range": 1455 "Attempt to access negative length slice of `chain` range"); 1456 break sw2; 1457 1458 default: 1459 assert(0, "Internal library error. Please report it."); 1460 } 1461 1462 static if (bidirectional) 1463 return Result(resultRanges, resultFrontIndex, resultBackIndex); 1464 else 1465 return Result(resultRanges, resultFrontIndex); 1466 } 1467 } 1468 return Result(rs); 1469 } 1470 } 1471 1472 /// 1473 pure @safe nothrow unittest 1474 { 1475 import std.algorithm.comparison : equal; 1476 1477 int[] arr1 = [ 1, 2, 3, 4 ]; 1478 int[] arr2 = [ 5, 6 ]; 1479 int[] arr3 = [ 7 ]; 1480 auto s = chain(arr1, arr2, arr3); 1481 assert(s.length == 7); 1482 assert(s[5] == 6); 1483 assert(equal(s, [1, 2, 3, 4, 5, 6, 7][])); 1484 } 1485 1486 /** 1487 * Range primitives are carried over to the returned range if 1488 * all of the ranges provide them 1489 */ 1490 pure @safe nothrow unittest 1491 { 1492 import std.algorithm.comparison : equal; 1493 import std.algorithm.sorting : sort; 1494 1495 int[] arr1 = [5, 2, 8]; 1496 int[] arr2 = [3, 7, 9]; 1497 int[] arr3 = [1, 4, 6]; 1498 1499 // in-place sorting across all of the arrays 1500 auto s = arr1.chain(arr2, arr3).sort; 1501 1502 assert(s.equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); 1503 assert(arr1.equal([1, 2, 3])); 1504 assert(arr2.equal([4, 5, 6])); 1505 assert(arr3.equal([7, 8, 9])); 1506 } 1507 1508 /** 1509 Due to safe type promotion in D, chaining together different 1510 character ranges results in a `uint` range. 1511 1512 Use $(REF_ALTTEXT byChar, byChar,std,utf), $(REF_ALTTEXT byWchar, byWchar,std,utf), 1513 and $(REF_ALTTEXT byDchar, byDchar,std,utf) on the ranges 1514 to get the type you need. 1515 */ 1516 pure @safe nothrow unittest 1517 { 1518 import std.utf : byChar, byCodeUnit; 1519 1520 auto s1 = "string one"; 1521 auto s2 = "string two"; 1522 // s1 and s2 front is dchar because of auto-decoding 1523 static assert(is(typeof(s1.front) == dchar) && is(typeof(s2.front) == dchar)); 1524 1525 auto r1 = s1.chain(s2); 1526 // chains of ranges of the same character type give that same type 1527 static assert(is(typeof(r1.front) == dchar)); 1528 1529 auto s3 = "string three".byCodeUnit; 1530 static assert(is(typeof(s3.front) == immutable char)); 1531 auto r2 = s1.chain(s3); 1532 // chaining ranges of mixed character types gives `dchar` 1533 static assert(is(typeof(r2.front) == dchar)); 1534 1535 // use byChar on character ranges to correctly convert them to UTF-8 1536 auto r3 = s1.byChar.chain(s3); 1537 static assert(is(typeof(r3.front) == immutable char)); 1538 } 1539 1540 pure @safe nothrow unittest 1541 { 1542 import std.algorithm.comparison : equal; 1543 import std.internal.test.dummyrange : AllDummyRanges, dummyLength, 1544 propagatesRangeType; 1545 1546 { 1547 int[] arr1 = [ 1, 2, 3, 4 ]; 1548 int[] arr2 = [ 5, 6 ]; 1549 int[] arr3 = [ 7 ]; 1550 int[] witness = [ 1, 2, 3, 4, 5, 6, 7 ]; 1551 auto s1 = chain(arr1); 1552 static assert(isRandomAccessRange!(typeof(s1))); 1553 auto s2 = chain(arr1, arr2); 1554 static assert(isBidirectionalRange!(typeof(s2))); 1555 static assert(isRandomAccessRange!(typeof(s2))); 1556 s2.front = 1; 1557 auto s = chain(arr1, arr2, arr3); 1558 assert(s[5] == 6); 1559 assert(equal(s, witness)); 1560 assert(s[4 .. 6].equal(arr2)); 1561 assert(s[2 .. 5].equal([3, 4, 5])); 1562 assert(s[0 .. 0].empty); 1563 assert(s[7 .. $].empty); 1564 assert(s[5] == 6); 1565 } 1566 { 1567 int[] arr1 = [ 1, 2, 3, 4 ]; 1568 int[] witness = [ 1, 2, 3, 4 ]; 1569 assert(equal(chain(arr1), witness)); 1570 } 1571 { 1572 uint[] foo = [1,2,3,4,5]; 1573 uint[] bar = [1,2,3,4,5]; 1574 auto c = chain(foo, bar); 1575 c[3] = 42; 1576 assert(c[3] == 42); 1577 assert(c.moveFront() == 1); 1578 assert(c.moveBack() == 5); 1579 assert(c.moveAt(4) == 5); 1580 assert(c.moveAt(5) == 1); 1581 } 1582 1583 1584 // Make sure https://issues.dlang.org/show_bug.cgi?id=3311 is fixed. 1585 // elements are mutable. 1586 assert(equal(chain(iota(0, 3), iota(0, 3)), [0, 1, 2, 0, 1, 2])); 1587 1588 // Test the case where infinite ranges are present. 1589 auto inf = chain([0,1,2][], cycle([4,5,6][]), [7,8,9][]); // infinite range 1590 assert(inf[0] == 0); 1591 assert(inf[3] == 4); 1592 assert(inf[6] == 4); 1593 assert(inf[7] == 5); 1594 static assert(isInfinite!(typeof(inf))); 1595 1596 immutable int[] immi = [ 1, 2, 3 ]; 1597 immutable float[] immf = [ 1, 2, 3 ]; 1598 static assert(is(typeof(chain(immi, immf)))); 1599 1600 // Check that chain at least instantiates and compiles with every possible 1601 // pair of DummyRange types, in either order. 1602 1603 foreach (DummyType1; AllDummyRanges) 1604 (){ // workaround slow optimizations for large functions 1605 // https://issues.dlang.org/show_bug.cgi?id=2396 1606 DummyType1 dummy1; 1607 foreach (DummyType2; AllDummyRanges) 1608 { 1609 DummyType2 dummy2; 1610 auto myChain = chain(dummy1, dummy2); 1611 1612 static assert( 1613 propagatesRangeType!(typeof(myChain), DummyType1, DummyType2) 1614 ); 1615 1616 assert(myChain.front == 1); 1617 foreach (i; 0 .. dummyLength) 1618 { 1619 myChain.popFront(); 1620 } 1621 assert(myChain.front == 1); 1622 1623 static if (isBidirectionalRange!DummyType1 && 1624 isBidirectionalRange!DummyType2) { 1625 assert(myChain.back == 10); 1626 } 1627 1628 static if (isRandomAccessRange!DummyType1 && 1629 isRandomAccessRange!DummyType2) { 1630 assert(myChain[0] == 1); 1631 } 1632 1633 static if (hasLvalueElements!DummyType1 && hasLvalueElements!DummyType2) 1634 { 1635 static assert(hasLvalueElements!(typeof(myChain))); 1636 } 1637 else 1638 { 1639 static assert(!hasLvalueElements!(typeof(myChain))); 1640 } 1641 } 1642 }(); 1643 } 1644 1645 pure @safe nothrow @nogc unittest 1646 { 1647 class Foo{} 1648 immutable(Foo)[] a; 1649 immutable(Foo)[] b; 1650 assert(chain(a, b).empty); 1651 } 1652 1653 // https://issues.dlang.org/show_bug.cgi?id=18657 1654 pure @safe unittest 1655 { 1656 import std.algorithm.comparison : equal; 1657 string s = "foo"; 1658 auto r = refRange(&s).chain("bar"); 1659 assert(equal(r.save, "foobar")); 1660 assert(equal(r, "foobar")); 1661 } 1662 1663 // https://issues.dlang.org/show_bug.cgi?id=23844 1664 pure @safe unittest 1665 { 1666 struct S 1667 { 1668 immutable int value; 1669 } 1670 1671 auto range = chain(only(S(5)), only(S(6))); 1672 assert(range.array == [S(5), S(6)]); 1673 } 1674 1675 /// https://issues.dlang.org/show_bug.cgi?id=24064 1676 pure @safe nothrow unittest 1677 { 1678 import std.algorithm.comparison : equal; 1679 import std.typecons : Nullable; 1680 1681 immutable Nullable!string foo = "b"; 1682 string[] bar = ["a"]; 1683 assert(chain(bar, foo).equal(["a", "b"])); 1684 } 1685 1686 pure @safe nothrow @nogc unittest 1687 { 1688 // support non-copyable items 1689 1690 static struct S { 1691 int v; 1692 @disable this(this); 1693 } 1694 1695 S[2] s0, s1; 1696 foreach (ref el; chain(s0[], s1[])) 1697 { 1698 int n = el.v; 1699 } 1700 1701 S[] s2, s3; 1702 foreach (ref el; chain(s2, s3)) 1703 { 1704 int n = el.v; 1705 } 1706 } 1707 1708 /** 1709 Choose one of two ranges at runtime depending on a Boolean condition. 1710 1711 The ranges may be different, but they must have compatible element types (i.e. 1712 `CommonType` must exist for the two element types). The result is a range 1713 that offers the weakest capabilities of the two (e.g. `ForwardRange` if $(D 1714 R1) is a random-access range and `R2` is a forward range). 1715 1716 Params: 1717 condition = which range to choose: `r1` if `true`, `r2` otherwise 1718 r1 = the "true" range 1719 r2 = the "false" range 1720 1721 Returns: 1722 A range type dependent on `R1` and `R2`. 1723 */ 1724 auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2) 1725 if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && 1726 !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) 1727 { 1728 size_t choice = condition? 0: 1; 1729 return ChooseResult!(R1, R2)(choice, r1, r2); 1730 } 1731 1732 /// 1733 @safe nothrow pure @nogc unittest 1734 { 1735 import std.algorithm.comparison : equal; 1736 import std.algorithm.iteration : filter, map; 1737 1738 auto data1 = only(1, 2, 3, 4).filter!(a => a != 3); 1739 auto data2 = only(5, 6, 7, 8).map!(a => a + 1); 1740 1741 // choose() is primarily useful when you need to select one of two ranges 1742 // with different types at runtime. 1743 static assert(!is(typeof(data1) == typeof(data2))); 1744 1745 auto chooseRange(bool pickFirst) 1746 { 1747 // The returned range is a common wrapper type that can be used for 1748 // returning or storing either range without running into a type error. 1749 return choose(pickFirst, data1, data2); 1750 1751 // Simply returning the chosen range without using choose() does not 1752 // work, because map() and filter() return different types. 1753 //return pickFirst ? data1 : data2; // does not compile 1754 } 1755 1756 auto result = chooseRange(true); 1757 assert(result.equal(only(1, 2, 4))); 1758 1759 result = chooseRange(false); 1760 assert(result.equal(only(6, 7, 8, 9))); 1761 } 1762 1763 1764 private struct ChooseResult(Ranges...) 1765 { 1766 import std.meta : aliasSeqOf, ApplyLeft; 1767 import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor, 1768 lvalueOf; 1769 1770 private union 1771 { 1772 Ranges rs; 1773 } 1774 private size_t chosenI; 1775 1776 private static auto ref actOnChosen(alias foo, ExtraArgs ...) 1777 (ref ChooseResult r, auto ref ExtraArgs extraArgs) 1778 { 1779 ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } 1780 1781 switch (r.chosenI) 1782 { 1783 static foreach (candI; 0 .. rs.length) 1784 { 1785 case candI: return foo(getI!candI(r), extraArgs); 1786 } 1787 1788 default: assert(false); 1789 } 1790 } 1791 1792 // @trusted because of assignment of r which overlap each other 1793 this(size_t chosen, return scope Ranges rs) @trusted 1794 { 1795 import core.lifetime : emplace; 1796 1797 // This should be the only place chosenI is ever assigned 1798 // independently 1799 this.chosenI = chosen; 1800 1801 // Otherwise the compiler will complain about skipping these fields 1802 static foreach (i; 0 .. rs.length) 1803 { 1804 this.rs[i] = Ranges[i].init; 1805 } 1806 1807 // The relevant field needs to be initialized last so it will overwrite 1808 // the other initializations and not the other way around. 1809 sw: switch (chosenI) 1810 { 1811 static foreach (i; 0 .. rs.length) 1812 { 1813 case i: 1814 emplace(&this.rs[i], rs[i]); 1815 break sw; 1816 } 1817 1818 default: assert(false); 1819 } 1820 } 1821 1822 // Some legacy code may still call this with typeof(choose(/*...*/))(/*...*/) 1823 // without this overload the regular constructor would invert the meaning of 1824 // the boolean 1825 static if (rs.length == 2) 1826 pragma(inline, true) 1827 deprecated("Call with size_t (0 = first), or use the choose function") 1828 this(bool firstChosen, Ranges rs) 1829 { 1830 import core.lifetime : move; 1831 this(cast(size_t)(firstChosen? 0: 1), rs[0].move, rs[1].move); 1832 } 1833 1834 void opAssign(ChooseResult r) 1835 { 1836 ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } 1837 1838 static if (anySatisfy!(hasElaborateDestructor, Ranges)) 1839 if (chosenI != r.chosenI) 1840 { 1841 // destroy the current item 1842 actOnChosen!((ref r) => destroy(r))(this); 1843 } 1844 chosenI = r.chosenI; 1845 1846 sw: switch (chosenI) 1847 { 1848 static foreach (candI; 0 .. rs.length) 1849 { 1850 case candI: getI!candI(this) = getI!candI(r); 1851 break sw; 1852 } 1853 1854 default: assert(false); 1855 } 1856 } 1857 1858 // Carefully defined postblit to postblit the appropriate range 1859 static if (anySatisfy!(hasElaborateCopyConstructor, Ranges)) 1860 this(this) 1861 { 1862 actOnChosen!((ref r) { 1863 static if (hasElaborateCopyConstructor!(typeof(r))) r.__postblit(); 1864 })(this); 1865 } 1866 1867 static if (anySatisfy!(hasElaborateDestructor, Ranges)) 1868 ~this() 1869 { 1870 actOnChosen!((ref r) => destroy(r))(this); 1871 } 1872 1873 // Propagate infiniteness. 1874 static if (allSatisfy!(isInfinite, Ranges)) enum bool empty = false; 1875 else @property bool empty() 1876 { 1877 return actOnChosen!(r => r.empty)(this); 1878 } 1879 1880 @property auto ref front() 1881 { 1882 static auto ref getFront(R)(ref R r) { return r.front; } 1883 return actOnChosen!getFront(this); 1884 } 1885 1886 void popFront() 1887 { 1888 return actOnChosen!((ref r) { r.popFront; })(this); 1889 } 1890 1891 static if (allSatisfy!(isForwardRange, Ranges)) 1892 @property auto save() // return scope inferred 1893 { 1894 auto saveOrInit(size_t i)() 1895 { 1896 ref getI() @trusted { return rs[i]; } 1897 if (i == chosenI) return getI().save; 1898 else return Ranges[i].init; 1899 } 1900 1901 return typeof(this)(chosenI, staticMap!(saveOrInit, 1902 aliasSeqOf!(rs.length.iota))); 1903 } 1904 1905 template front(T) 1906 { 1907 private enum overloadValidFor(alias r) = is(typeof(r.front = T.init)); 1908 1909 static if (allSatisfy!(overloadValidFor, rs)) 1910 void front(T v) 1911 { 1912 actOnChosen!((ref r, T v) { r.front = v; })(this, v); 1913 } 1914 } 1915 1916 static if (allSatisfy!(hasMobileElements, Ranges)) 1917 auto moveFront() 1918 { 1919 return actOnChosen!((ref r) => r.moveFront)(this); 1920 } 1921 1922 static if (allSatisfy!(isBidirectionalRange, Ranges)) 1923 { 1924 @property auto ref back() 1925 { 1926 static auto ref getBack(R)(ref R r) { return r.back; } 1927 return actOnChosen!getBack(this); 1928 } 1929 1930 void popBack() 1931 { 1932 actOnChosen!((ref r) { r.popBack; })(this); 1933 } 1934 1935 static if (allSatisfy!(hasMobileElements, Ranges)) 1936 auto moveBack() 1937 { 1938 return actOnChosen!((ref r) => r.moveBack)(this); 1939 } 1940 1941 template back(T) 1942 { 1943 private enum overloadValidFor(alias r) = is(typeof(r.back = T.init)); 1944 1945 static if (allSatisfy!(overloadValidFor, rs)) 1946 void back(T v) 1947 { 1948 actOnChosen!((ref r, T v) { r.back = v; })(this, v); 1949 } 1950 } 1951 } 1952 1953 static if (allSatisfy!(hasLength, Ranges)) 1954 { 1955 @property size_t length() 1956 { 1957 return actOnChosen!(r => r.length)(this); 1958 } 1959 alias opDollar = length; 1960 } 1961 1962 static if (allSatisfy!(isRandomAccessRange, Ranges)) 1963 { 1964 auto ref opIndex(size_t index) 1965 { 1966 static auto ref get(R)(ref R r, size_t index) { return r[index]; } 1967 return actOnChosen!get(this, index); 1968 } 1969 1970 static if (allSatisfy!(hasMobileElements, Ranges)) 1971 auto moveAt(size_t index) 1972 { 1973 return actOnChosen!((ref r, size_t index) => r.moveAt(index)) 1974 (this, index); 1975 } 1976 1977 private enum indexAssignable(T, R) = is(typeof(lvalueOf!R[1] = T.init)); 1978 1979 template opIndexAssign(T) 1980 if (allSatisfy!(ApplyLeft!(indexAssignable, T), Ranges)) 1981 { 1982 void opIndexAssign(T v, size_t index) 1983 { 1984 return actOnChosen!((ref r, size_t index, T v) { r[index] = v; }) 1985 (this, index, v); 1986 } 1987 } 1988 } 1989 1990 static if (allSatisfy!(hasSlicing, Ranges)) 1991 auto opSlice(size_t begin, size_t end) 1992 { 1993 alias Slice(R) = typeof(R.init[0 .. 1]); 1994 alias Slices = staticMap!(Slice, Ranges); 1995 1996 auto sliceOrInit(size_t i)() 1997 { 1998 ref getI() @trusted { return rs[i]; } 1999 return i == chosenI? getI()[begin .. end]: Slices[i].init; 2000 } 2001 2002 return chooseAmong(chosenI, staticMap!(sliceOrInit, 2003 aliasSeqOf!(rs.length.iota))); 2004 } 2005 } 2006 2007 // https://issues.dlang.org/show_bug.cgi?id=18657 2008 pure @safe unittest 2009 { 2010 import std.algorithm.comparison : equal; 2011 string s = "foo"; 2012 auto r = choose(true, refRange(&s), "bar"); 2013 assert(equal(r.save, "foo")); 2014 assert(equal(r, "foo")); 2015 } 2016 2017 @safe unittest 2018 { 2019 static void* p; 2020 static struct R 2021 { 2022 void* q; 2023 int front; 2024 bool empty; 2025 void popFront() {} 2026 // `p = q;` is only there to prevent inference of `scope return`. 2027 @property @safe R save() { p = q; return this; } 2028 2029 } 2030 R r; 2031 choose(true, r, r).save; 2032 } 2033 2034 // Make sure ChooseResult.save doesn't trust @system user code. 2035 @system unittest // copy is @system 2036 { 2037 static struct R 2038 { 2039 int front; 2040 bool empty; 2041 void popFront() {} 2042 this(this) @system {} 2043 @property R save() { return R(front, empty); } 2044 } 2045 choose(true, R(), R()).save; 2046 choose(true, [0], R()).save; 2047 choose(true, R(), [0]).save; 2048 } 2049 2050 @safe unittest // copy is @system 2051 { 2052 static struct R 2053 { 2054 int front; 2055 bool empty; 2056 void popFront() {} 2057 this(this) @system {} 2058 @property R save() { return R(front, empty); } 2059 } 2060 static assert(!__traits(compiles, choose(true, R(), R()).save)); 2061 static assert(!__traits(compiles, choose(true, [0], R()).save)); 2062 static assert(!__traits(compiles, choose(true, R(), [0]).save)); 2063 } 2064 2065 @system unittest // .save is @system 2066 { 2067 static struct R 2068 { 2069 int front; 2070 bool empty; 2071 void popFront() {} 2072 @property R save() @system { return this; } 2073 } 2074 choose(true, R(), R()).save; 2075 choose(true, [0], R()).save; 2076 choose(true, R(), [0]).save; 2077 } 2078 2079 @safe unittest // .save is @system 2080 { 2081 static struct R 2082 { 2083 int front; 2084 bool empty; 2085 void popFront() {} 2086 @property R save() @system { return this; } 2087 } 2088 static assert(!__traits(compiles, choose(true, R(), R()).save)); 2089 static assert(!__traits(compiles, choose(true, [0], R()).save)); 2090 static assert(!__traits(compiles, choose(true, R(), [0]).save)); 2091 } 2092 2093 //https://issues.dlang.org/show_bug.cgi?id=19738 2094 @safe nothrow pure @nogc unittest 2095 { 2096 static struct EvilRange 2097 { 2098 enum empty = true; 2099 int front; 2100 void popFront() @safe {} 2101 auto opAssign(const ref EvilRange other) 2102 { 2103 *(cast(uint*) 0xcafebabe) = 0xdeadbeef; 2104 return this; 2105 } 2106 } 2107 2108 static assert(!__traits(compiles, () @safe 2109 { 2110 auto c1 = choose(true, EvilRange(), EvilRange()); 2111 auto c2 = c1; 2112 c1 = c2; 2113 })); 2114 } 2115 2116 2117 // https://issues.dlang.org/show_bug.cgi?id=20495 2118 @safe unittest 2119 { 2120 static struct KillableRange 2121 { 2122 int *item; 2123 ref int front() { return *item; } 2124 bool empty() { return *item > 10; } 2125 void popFront() { ++(*item); } 2126 this(this) 2127 { 2128 assert(item is null || cast(size_t) item > 1000); 2129 item = new int(*item); 2130 } 2131 KillableRange save() { return this; } 2132 } 2133 2134 auto kr = KillableRange(new int(1)); 2135 int[] x = [1,2,3,4,5]; // length is first 2136 2137 auto chosen = choose(true, x, kr); 2138 auto chosen2 = chosen.save; 2139 } 2140 2141 pure @safe nothrow unittest 2142 { 2143 static struct S { 2144 int v; 2145 @disable this(this); 2146 } 2147 2148 auto a = [S(1), S(2), S(3)]; 2149 auto b = [S(4), S(5), S(6)]; 2150 2151 auto chosen = choose(true, a, b); 2152 assert(chosen.front.v == 1); 2153 2154 auto chosen2 = choose(false, a, b); 2155 assert(chosen2.front.v == 4); 2156 } 2157 2158 /** 2159 Choose one of multiple ranges at runtime. 2160 2161 The ranges may be different, but they must have compatible element types. The 2162 result is a range that offers the weakest capabilities of all `Ranges`. 2163 2164 Params: 2165 index = which range to choose, must be less than the number of ranges 2166 rs = two or more ranges 2167 2168 Returns: 2169 The indexed range. If rs consists of only one range, the return type is an 2170 alias of that range's type. 2171 */ 2172 auto chooseAmong(Ranges...)(size_t index, return scope Ranges rs) 2173 if (Ranges.length >= 2 2174 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) 2175 && !is(CommonType!(staticMap!(ElementType, Ranges)) == void)) 2176 { 2177 return ChooseResult!Ranges(index, rs); 2178 } 2179 2180 /// 2181 @safe nothrow pure @nogc unittest 2182 { 2183 auto test() 2184 { 2185 import std.algorithm.comparison : equal; 2186 2187 int[4] sarr1 = [1, 2, 3, 4]; 2188 int[2] sarr2 = [5, 6]; 2189 int[1] sarr3 = [7]; 2190 auto arr1 = sarr1[]; 2191 auto arr2 = sarr2[]; 2192 auto arr3 = sarr3[]; 2193 2194 { 2195 auto s = chooseAmong(0, arr1, arr2, arr3); 2196 auto t = s.save; 2197 assert(s.length == 4); 2198 assert(s[2] == 3); 2199 s.popFront(); 2200 assert(equal(t, only(1, 2, 3, 4))); 2201 } 2202 { 2203 auto s = chooseAmong(1, arr1, arr2, arr3); 2204 assert(s.length == 2); 2205 s.front = 8; 2206 assert(equal(s, only(8, 6))); 2207 } 2208 { 2209 auto s = chooseAmong(1, arr1, arr2, arr3); 2210 assert(s.length == 2); 2211 s[1] = 9; 2212 assert(equal(s, only(8, 9))); 2213 } 2214 { 2215 auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3]; 2216 assert(s.length == 2); 2217 assert(equal(s, only(2, 3))); 2218 } 2219 { 2220 auto s = chooseAmong(0, arr1, arr2, arr3); 2221 assert(s.length == 4); 2222 assert(s.back == 4); 2223 s.popBack(); 2224 s.back = 5; 2225 assert(equal(s, only(1, 2, 5))); 2226 s.back = 3; 2227 assert(equal(s, only(1, 2, 3))); 2228 } 2229 { 2230 uint[5] foo = [1, 2, 3, 4, 5]; 2231 uint[5] bar = [6, 7, 8, 9, 10]; 2232 auto c = chooseAmong(1, foo[], bar[]); 2233 assert(c[3] == 9); 2234 c[3] = 42; 2235 assert(c[3] == 42); 2236 assert(c.moveFront() == 6); 2237 assert(c.moveBack() == 10); 2238 assert(c.moveAt(4) == 10); 2239 } 2240 { 2241 import std.range : cycle; 2242 auto s = chooseAmong(0, cycle(arr2), cycle(arr3)); 2243 assert(isInfinite!(typeof(s))); 2244 assert(!s.empty); 2245 assert(s[100] == 8); 2246 assert(s[101] == 9); 2247 assert(s[0 .. 3].equal(only(8, 9, 8))); 2248 } 2249 return 0; 2250 } 2251 // works at runtime 2252 auto a = test(); 2253 // and at compile time 2254 static b = test(); 2255 } 2256 2257 @safe nothrow pure @nogc unittest 2258 { 2259 int[3] a = [1, 2, 3]; 2260 long[3] b = [4, 5, 6]; 2261 auto c = chooseAmong(0, a[], b[]); 2262 c[0] = 42; 2263 assert(c[0] == 42); 2264 } 2265 2266 @safe nothrow pure @nogc unittest 2267 { 2268 static struct RefAccessRange 2269 { 2270 int[] r; 2271 ref front() @property { return r[0]; } 2272 ref back() @property { return r[$ - 1]; } 2273 void popFront() { r = r[1 .. $]; } 2274 void popBack() { r = r[0 .. $ - 1]; } 2275 auto empty() @property { return r.empty; } 2276 ref opIndex(size_t i) { return r[i]; } 2277 auto length() @property { return r.length; } 2278 alias opDollar = length; 2279 auto save() { return this; } 2280 } 2281 static assert(isRandomAccessRange!RefAccessRange); 2282 static assert(isRandomAccessRange!RefAccessRange); 2283 int[4] a = [4, 3, 2, 1]; 2284 int[2] b = [6, 5]; 2285 auto c = chooseAmong(0, RefAccessRange(a[]), RefAccessRange(b[])); 2286 2287 void refFunc(ref int a, int target) { assert(a == target); } 2288 2289 refFunc(c[2], 2); 2290 refFunc(c.front, 4); 2291 refFunc(c.back, 1); 2292 } 2293 2294 2295 /** 2296 $(D roundRobin(r1, r2, r3)) yields `r1.front`, then `r2.front`, 2297 then `r3.front`, after which it pops off one element from each and 2298 continues again from `r1`. For example, if two ranges are involved, 2299 it alternately yields elements off the two ranges. `roundRobin` 2300 stops after it has consumed all ranges (skipping over the ones that 2301 finish early). 2302 */ 2303 auto roundRobin(Rs...)(Rs rs) 2304 if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) 2305 { 2306 struct Result 2307 { 2308 import std.conv : to; 2309 2310 public Rs source; 2311 private size_t _current = size_t.max; 2312 2313 @property bool empty() 2314 { 2315 foreach (i, Unused; Rs) 2316 { 2317 if (!source[i].empty) return false; 2318 } 2319 return true; 2320 } 2321 2322 @property auto ref front() 2323 { 2324 final switch (_current) 2325 { 2326 foreach (i, R; Rs) 2327 { 2328 case i: 2329 assert( 2330 !source[i].empty, 2331 "Attempting to fetch the front of an empty roundRobin" 2332 ); 2333 return source[i].front; 2334 } 2335 } 2336 assert(0); 2337 } 2338 2339 void popFront() 2340 { 2341 final switch (_current) 2342 { 2343 foreach (i, R; Rs) 2344 { 2345 case i: 2346 source[i].popFront(); 2347 break; 2348 } 2349 } 2350 2351 auto next = _current == (Rs.length - 1) ? 0 : (_current + 1); 2352 final switch (next) 2353 { 2354 foreach (i, R; Rs) 2355 { 2356 case i: 2357 if (!source[i].empty) 2358 { 2359 _current = i; 2360 return; 2361 } 2362 if (i == _current) 2363 { 2364 _current = _current.max; 2365 return; 2366 } 2367 goto case (i + 1) % Rs.length; 2368 } 2369 } 2370 } 2371 2372 static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) 2373 @property auto save() 2374 { 2375 auto saveSource(size_t len)() 2376 { 2377 import std.typecons : tuple; 2378 static assert(len > 0); 2379 static if (len == 1) 2380 { 2381 return tuple(source[0].save); 2382 } 2383 else 2384 { 2385 return saveSource!(len - 1)() ~ 2386 tuple(source[len - 1].save); 2387 } 2388 } 2389 return Result(saveSource!(Rs.length).expand, _current); 2390 } 2391 2392 static if (allSatisfy!(hasLength, Rs)) 2393 { 2394 @property size_t length() 2395 { 2396 size_t result; 2397 foreach (i, R; Rs) 2398 { 2399 result += source[i].length; 2400 } 2401 return result; 2402 } 2403 2404 alias opDollar = length; 2405 } 2406 } 2407 2408 return Result(rs, 0); 2409 } 2410 2411 /// 2412 @safe unittest 2413 { 2414 import std.algorithm.comparison : equal; 2415 2416 int[] a = [ 1, 2, 3 ]; 2417 int[] b = [ 10, 20, 30, 40 ]; 2418 auto r = roundRobin(a, b); 2419 assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ])); 2420 } 2421 2422 /** 2423 * roundRobin can be used to create "interleave" functionality which inserts 2424 * an element between each element in a range. 2425 */ 2426 @safe unittest 2427 { 2428 import std.algorithm.comparison : equal; 2429 2430 auto interleave(R, E)(R range, E element) 2431 if ((isInputRange!R && hasLength!R) || isForwardRange!R) 2432 { 2433 static if (hasLength!R) 2434 immutable len = range.length; 2435 else 2436 immutable len = range.save.walkLength; 2437 2438 return roundRobin( 2439 range, 2440 element.repeat(len - 1) 2441 ); 2442 } 2443 2444 assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3])); 2445 } 2446 2447 pure @safe unittest 2448 { 2449 import std.algorithm.comparison : equal; 2450 string f = "foo", b = "bar"; 2451 auto r = roundRobin(refRange(&f), refRange(&b)); 2452 assert(equal(r.save, "fboaor")); 2453 assert(equal(r.save, "fboaor")); 2454 } 2455 pure @safe nothrow unittest 2456 { 2457 import std.algorithm.comparison : equal; 2458 2459 static struct S { 2460 int v; 2461 @disable this(this); 2462 } 2463 2464 S[] a = [ S(1), S(2) ]; 2465 S[] b = [ S(10), S(20) ]; 2466 auto r = roundRobin(a, b); 2467 assert(equal(r, [ S(1), S(10), S(2), S(20) ])); 2468 } 2469 2470 /** 2471 Iterates a random-access range starting from a given point and 2472 progressively extending left and right from that point. If no initial 2473 point is given, iteration starts from the middle of the 2474 range. Iteration spans the entire range. 2475 2476 When `startingIndex` is 0 the range will be fully iterated in order 2477 and in reverse order when `r.length` is given. 2478 2479 Params: 2480 r = a random access range with length and slicing 2481 startingIndex = the index to begin iteration from 2482 2483 Returns: 2484 A forward range with length 2485 */ 2486 auto radial(Range, I)(Range r, I startingIndex) 2487 if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I) 2488 { 2489 if (startingIndex != r.length) ++startingIndex; 2490 return roundRobin(retro(r[0 .. startingIndex]), r[startingIndex .. r.length]); 2491 } 2492 2493 /// Ditto 2494 auto radial(R)(R r) 2495 if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R)) 2496 { 2497 return .radial(r, (r.length - !r.empty) / 2); 2498 } 2499 2500 /// 2501 @safe unittest 2502 { 2503 import std.algorithm.comparison : equal; 2504 int[] a = [ 1, 2, 3, 4, 5 ]; 2505 assert(equal(radial(a), [ 3, 4, 2, 5, 1 ])); 2506 a = [ 1, 2, 3, 4 ]; 2507 assert(equal(radial(a), [ 2, 3, 1, 4 ])); 2508 2509 // If the left end is reached first, the remaining elements on the right 2510 // are concatenated in order: 2511 a = [ 0, 1, 2, 3, 4, 5 ]; 2512 assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ])); 2513 2514 // If the right end is reached first, the remaining elements on the left 2515 // are concatenated in reverse order: 2516 assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ])); 2517 } 2518 2519 @safe unittest 2520 { 2521 import std.algorithm.comparison : equal; 2522 import std.conv : text; 2523 import std.exception : enforce; 2524 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 2525 2526 void test(int[] input, int[] witness) 2527 { 2528 enforce(equal(radial(input), witness), 2529 text(radial(input), " vs. ", witness)); 2530 } 2531 test([], []); 2532 test([ 1 ], [ 1 ]); 2533 test([ 1, 2 ], [ 1, 2 ]); 2534 test([ 1, 2, 3 ], [ 2, 3, 1 ]); 2535 test([ 1, 2, 3, 4 ], [ 2, 3, 1, 4 ]); 2536 test([ 1, 2, 3, 4, 5 ], [ 3, 4, 2, 5, 1 ]); 2537 test([ 1, 2, 3, 4, 5, 6 ], [ 3, 4, 2, 5, 1, 6 ]); 2538 2539 int[] a = [ 1, 2, 3, 4, 5 ]; 2540 assert(equal(radial(a, 1), [ 2, 3, 1, 4, 5 ])); 2541 assert(equal(radial(a, 0), [ 1, 2, 3, 4, 5 ])); // only right subrange 2542 assert(equal(radial(a, a.length), [ 5, 4, 3, 2, 1 ])); // only left subrange 2543 static assert(isForwardRange!(typeof(radial(a, 1)))); 2544 2545 auto r = radial([1,2,3,4,5]); 2546 for (auto rr = r.save; !rr.empty; rr.popFront()) 2547 { 2548 assert(rr.front == moveFront(rr)); 2549 } 2550 r.front = 5; 2551 assert(r.front == 5); 2552 2553 // Test instantiation without lvalue elements. 2554 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random) dummy; 2555 assert(equal(radial(dummy, 4), [5, 6, 4, 7, 3, 8, 2, 9, 1, 10])); 2556 2557 // immutable int[] immi = [ 1, 2 ]; 2558 // static assert(is(typeof(radial(immi)))); 2559 } 2560 2561 @safe unittest 2562 { 2563 import std.algorithm.comparison : equal; 2564 2565 auto LL = iota(1L, 6L); 2566 auto r = radial(LL); 2567 assert(equal(r, [3L, 4L, 2L, 5L, 1L])); 2568 } 2569 2570 /** 2571 Lazily takes only up to `n` elements of a range. This is 2572 particularly useful when using with infinite ranges. 2573 2574 Unlike $(LREF takeExactly), `take` does not require that there 2575 are `n` or more elements in `input`. As a consequence, length 2576 information is not applied to the result unless `input` also has 2577 length information. 2578 2579 Params: 2580 input = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 2581 to iterate over up to `n` times 2582 n = the number of elements to take 2583 2584 Returns: 2585 At minimum, an input range. If the range offers random access 2586 and `length`, `take` offers them as well. 2587 */ 2588 Take!R take(R)(R input, size_t n) 2589 if (isInputRange!(Unqual!R)) 2590 { 2591 alias U = Unqual!R; 2592 static if (is(R T == Take!T)) 2593 { 2594 import std.algorithm.comparison : min; 2595 return R(input.source, min(n, input._maxAvailable)); 2596 } 2597 else static if (!isInfinite!U && hasSlicing!U) 2598 { 2599 import std.algorithm.comparison : min; 2600 return input[0 .. min(n, input.length)]; 2601 } 2602 else 2603 { 2604 return Take!R(input, n); 2605 } 2606 } 2607 2608 /// ditto 2609 struct Take(Range) 2610 if (isInputRange!(Unqual!Range) && 2611 //take _cannot_ test hasSlicing on infinite ranges, because hasSlicing uses 2612 //take for slicing infinite ranges. 2613 !((!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range)) || is(Range T == Take!T))) 2614 { 2615 private alias R = Unqual!Range; 2616 2617 /// User accessible in read and write 2618 public R source; 2619 2620 private size_t _maxAvailable; 2621 2622 alias Source = R; 2623 2624 /// Range primitives 2625 @property bool empty() 2626 { 2627 return _maxAvailable == 0 || source.empty; 2628 } 2629 2630 /// ditto 2631 @property auto ref front() 2632 { 2633 assert(!empty, 2634 "Attempting to fetch the front of an empty " 2635 ~ Take.stringof); 2636 return source.front; 2637 } 2638 2639 /// ditto 2640 void popFront() 2641 { 2642 assert(!empty, 2643 "Attempting to popFront() past the end of a " 2644 ~ Take.stringof); 2645 source.popFront(); 2646 --_maxAvailable; 2647 } 2648 2649 static if (isForwardRange!R) 2650 /// ditto 2651 @property Take save() 2652 { 2653 return Take(source.save, _maxAvailable); 2654 } 2655 2656 static if (hasAssignableElements!R) 2657 /// ditto 2658 @property void front(ElementType!R v) 2659 { 2660 import std.algorithm.mutation : move; 2661 2662 assert(!empty, 2663 "Attempting to assign to the front of an empty " 2664 ~ Take.stringof); 2665 source.front = move(v); 2666 } 2667 2668 static if (hasMobileElements!R) 2669 { 2670 /// ditto 2671 auto moveFront() 2672 { 2673 assert(!empty, 2674 "Attempting to move the front of an empty " 2675 ~ Take.stringof); 2676 return source.moveFront(); 2677 } 2678 } 2679 2680 static if (isInfinite!R) 2681 { 2682 /// ditto 2683 @property size_t length() const 2684 { 2685 return _maxAvailable; 2686 } 2687 2688 /// ditto 2689 alias opDollar = length; 2690 2691 //Note: Due to Take/hasSlicing circular dependency, 2692 //This needs to be a restrained template. 2693 /// ditto 2694 auto opSlice()(size_t i, size_t j) 2695 if (hasSlicing!R) 2696 { 2697 assert(i <= j, "Invalid slice bounds"); 2698 assert(j <= length, "Attempting to slice past the end of a " 2699 ~ Take.stringof); 2700 return source[i .. j]; 2701 } 2702 } 2703 else static if (hasLength!R) 2704 { 2705 /// ditto 2706 @property size_t length() 2707 { 2708 import std.algorithm.comparison : min; 2709 return min(_maxAvailable, source.length); 2710 } 2711 2712 alias opDollar = length; 2713 } 2714 2715 static if (isRandomAccessRange!R) 2716 { 2717 /// ditto 2718 void popBack() 2719 { 2720 assert(!empty, 2721 "Attempting to popBack() past the beginning of a " 2722 ~ Take.stringof); 2723 --_maxAvailable; 2724 } 2725 2726 /// ditto 2727 @property auto ref back() 2728 { 2729 assert(!empty, 2730 "Attempting to fetch the back of an empty " 2731 ~ Take.stringof); 2732 return source[this.length - 1]; 2733 } 2734 2735 /// ditto 2736 auto ref opIndex(size_t index) 2737 { 2738 assert(index < length, 2739 "Attempting to index out of the bounds of a " 2740 ~ Take.stringof); 2741 return source[index]; 2742 } 2743 2744 static if (hasAssignableElements!R) 2745 { 2746 /// ditto 2747 @property void back(ElementType!R v) 2748 { 2749 // This has to return auto instead of void because of 2750 // https://issues.dlang.org/show_bug.cgi?id=4706 2751 assert(!empty, 2752 "Attempting to assign to the back of an empty " 2753 ~ Take.stringof); 2754 source[this.length - 1] = v; 2755 } 2756 2757 /// ditto 2758 void opIndexAssign(ElementType!R v, size_t index) 2759 { 2760 assert(index < length, 2761 "Attempting to index out of the bounds of a " 2762 ~ Take.stringof); 2763 source[index] = v; 2764 } 2765 } 2766 2767 static if (hasMobileElements!R) 2768 { 2769 /// ditto 2770 auto moveBack() 2771 { 2772 assert(!empty, 2773 "Attempting to move the back of an empty " 2774 ~ Take.stringof); 2775 return source.moveAt(this.length - 1); 2776 } 2777 2778 /// ditto 2779 auto moveAt(size_t index) 2780 { 2781 assert(index < length, 2782 "Attempting to index out of the bounds of a " 2783 ~ Take.stringof); 2784 return source.moveAt(index); 2785 } 2786 } 2787 } 2788 2789 /** 2790 Access to maximal length of the range. 2791 Note: the actual length of the range depends on the underlying range. 2792 If it has fewer elements, it will stop before maxLength is reached. 2793 */ 2794 @property size_t maxLength() const 2795 { 2796 return _maxAvailable; 2797 } 2798 } 2799 2800 /// ditto 2801 template Take(R) 2802 if (isInputRange!(Unqual!R) && 2803 ((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T))) 2804 { 2805 alias Take = R; 2806 } 2807 2808 /// 2809 pure @safe nothrow unittest 2810 { 2811 import std.algorithm.comparison : equal; 2812 2813 int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 2814 auto s = take(arr1, 5); 2815 assert(s.length == 5); 2816 assert(s[4] == 5); 2817 assert(equal(s, [ 1, 2, 3, 4, 5 ][])); 2818 } 2819 2820 /** 2821 * If the range runs out before `n` elements, `take` simply returns the entire 2822 * range (unlike $(LREF takeExactly), which will cause an assertion failure if 2823 * the range ends prematurely): 2824 */ 2825 pure @safe nothrow unittest 2826 { 2827 import std.algorithm.comparison : equal; 2828 2829 int[] arr2 = [ 1, 2, 3 ]; 2830 auto t = take(arr2, 5); 2831 assert(t.length == 3); 2832 assert(equal(t, [ 1, 2, 3 ])); 2833 } 2834 2835 pure @safe nothrow unittest 2836 { 2837 import std.algorithm.comparison : equal; 2838 import std.internal.test.dummyrange : AllDummyRanges; 2839 2840 int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 2841 auto s = take(arr1, 5); 2842 assert(s.length == 5); 2843 assert(s[4] == 5); 2844 assert(equal(s, [ 1, 2, 3, 4, 5 ][])); 2845 assert(equal(retro(s), [ 5, 4, 3, 2, 1 ][])); 2846 2847 // Test fix for bug 4464. 2848 static assert(is(typeof(s) == Take!(int[]))); 2849 static assert(is(typeof(s) == int[])); 2850 2851 // Test using narrow strings. 2852 import std.exception : assumeWontThrow; 2853 2854 auto myStr = "This is a string."; 2855 auto takeMyStr = take(myStr, 7); 2856 assert(assumeWontThrow(equal(takeMyStr, "This is"))); 2857 // Test fix for bug 5052. 2858 auto takeMyStrAgain = take(takeMyStr, 4); 2859 assert(assumeWontThrow(equal(takeMyStrAgain, "This"))); 2860 static assert(is (typeof(takeMyStrAgain) == typeof(takeMyStr))); 2861 takeMyStrAgain = take(takeMyStr, 10); 2862 assert(assumeWontThrow(equal(takeMyStrAgain, "This is"))); 2863 2864 foreach (DummyType; AllDummyRanges) 2865 { 2866 DummyType dummy; 2867 auto t = take(dummy, 5); 2868 alias T = typeof(t); 2869 2870 static if (isRandomAccessRange!DummyType) 2871 { 2872 static assert(isRandomAccessRange!T); 2873 assert(t[4] == 5); 2874 2875 assert(moveAt(t, 1) == t[1]); 2876 assert(t.back == moveBack(t)); 2877 } 2878 else static if (isForwardRange!DummyType) 2879 { 2880 static assert(isForwardRange!T); 2881 } 2882 2883 for (auto tt = t; !tt.empty; tt.popFront()) 2884 { 2885 assert(tt.front == moveFront(tt)); 2886 } 2887 2888 // Bidirectional ranges can't be propagated properly if they don't 2889 // also have random access. 2890 2891 assert(equal(t, [1,2,3,4,5])); 2892 2893 //Test that take doesn't wrap the result of take. 2894 assert(take(t, 4) == take(dummy, 4)); 2895 } 2896 2897 immutable myRepeat = repeat(1); 2898 static assert(is(Take!(typeof(myRepeat)))); 2899 } 2900 2901 pure @safe nothrow @nogc unittest 2902 { 2903 //check for correct slicing of Take on an infinite range 2904 import std.algorithm.comparison : equal; 2905 foreach (start; 0 .. 4) 2906 foreach (stop; start .. 4) 2907 assert(iota(4).cycle.take(4)[start .. stop] 2908 .equal(iota(start, stop))); 2909 } 2910 2911 pure @safe nothrow @nogc unittest 2912 { 2913 // Check that one can declare variables of all Take types, 2914 // and that they match the return type of the corresponding 2915 // take(). 2916 // See https://issues.dlang.org/show_bug.cgi?id=4464 2917 int[] r1; 2918 Take!(int[]) t1; 2919 t1 = take(r1, 1); 2920 assert(t1.empty); 2921 2922 string r2; 2923 Take!string t2; 2924 t2 = take(r2, 1); 2925 assert(t2.empty); 2926 2927 Take!(Take!string) t3; 2928 t3 = take(t2, 1); 2929 assert(t3.empty); 2930 } 2931 2932 pure @safe nothrow @nogc unittest 2933 { 2934 alias R1 = typeof(repeat(1)); 2935 alias R2 = typeof(cycle([1])); 2936 alias TR1 = Take!R1; 2937 alias TR2 = Take!R2; 2938 static assert(isBidirectionalRange!TR1); 2939 static assert(isBidirectionalRange!TR2); 2940 } 2941 2942 // https://issues.dlang.org/show_bug.cgi?id=12731 2943 pure @safe nothrow @nogc unittest 2944 { 2945 auto a = repeat(1); 2946 auto s = a[1 .. 5]; 2947 s = s[1 .. 3]; 2948 assert(s.length == 2); 2949 assert(s[0] == 1); 2950 assert(s[1] == 1); 2951 } 2952 2953 // https://issues.dlang.org/show_bug.cgi?id=13151 2954 pure @safe nothrow @nogc unittest 2955 { 2956 import std.algorithm.comparison : equal; 2957 2958 auto r = take(repeat(1, 4), 3); 2959 assert(r.take(2).equal(repeat(1, 2))); 2960 } 2961 2962 2963 /** 2964 Similar to $(LREF take), but assumes that `range` has at least $(D 2965 n) elements. Consequently, the result of $(D takeExactly(range, n)) 2966 always defines the `length` property (and initializes it to `n`) 2967 even when `range` itself does not define `length`. 2968 2969 The result of `takeExactly` is identical to that of $(LREF take) in 2970 cases where the original range defines `length` or is infinite. 2971 2972 Unlike $(LREF take), however, it is illegal to pass a range with less than 2973 `n` elements to `takeExactly`; this will cause an assertion failure. 2974 */ 2975 auto takeExactly(R)(R range, size_t n) 2976 if (isInputRange!R) 2977 { 2978 static if (is(typeof(takeExactly(range._input, n)) == R)) 2979 { 2980 assert(n <= range._n, 2981 "Attempted to take more than the length of the range with takeExactly."); 2982 // takeExactly(takeExactly(r, n1), n2) has the same type as 2983 // takeExactly(r, n1) and simply returns takeExactly(r, n2) 2984 range._n = n; 2985 return range; 2986 } 2987 //Also covers hasSlicing!R for finite ranges. 2988 else static if (hasLength!R) 2989 { 2990 assert(n <= range.length, 2991 "Attempted to take more than the length of the range with takeExactly."); 2992 return take(range, n); 2993 } 2994 else static if (isInfinite!R) 2995 return Take!R(range, n); 2996 else 2997 { 2998 static struct Result 2999 { 3000 R _input; 3001 private size_t _n; 3002 3003 @property bool empty() const { return !_n; } 3004 @property auto ref front() 3005 { 3006 assert(_n > 0, "front() on an empty " ~ Result.stringof); 3007 return _input.front; 3008 } 3009 void popFront() { _input.popFront(); --_n; } 3010 @property size_t length() const { return _n; } 3011 alias opDollar = length; 3012 3013 @property auto _takeExactly_Result_asTake() 3014 { 3015 return take(_input, _n); 3016 } 3017 3018 alias _takeExactly_Result_asTake this; 3019 3020 static if (isForwardRange!R) 3021 @property auto save() 3022 { 3023 return Result(_input.save, _n); 3024 } 3025 3026 static if (hasMobileElements!R) 3027 { 3028 auto moveFront() 3029 { 3030 assert(!empty, 3031 "Attempting to move the front of an empty " 3032 ~ typeof(this).stringof); 3033 return _input.moveFront(); 3034 } 3035 } 3036 3037 static if (hasAssignableElements!R) 3038 { 3039 @property auto ref front(ElementType!R v) 3040 { 3041 import std.algorithm.mutation : move; 3042 3043 assert(!empty, 3044 "Attempting to assign to the front of an empty " 3045 ~ typeof(this).stringof); 3046 return _input.front = move(v); 3047 } 3048 } 3049 } 3050 3051 return Result(range, n); 3052 } 3053 } 3054 3055 /// 3056 pure @safe nothrow unittest 3057 { 3058 import std.algorithm.comparison : equal; 3059 3060 auto a = [ 1, 2, 3, 4, 5 ]; 3061 3062 auto b = takeExactly(a, 3); 3063 assert(equal(b, [1, 2, 3])); 3064 static assert(is(typeof(b.length) == size_t)); 3065 assert(b.length == 3); 3066 assert(b.front == 1); 3067 assert(b.back == 3); 3068 } 3069 3070 pure @safe nothrow unittest 3071 { 3072 import std.algorithm.comparison : equal; 3073 import std.algorithm.iteration : filter; 3074 3075 auto a = [ 1, 2, 3, 4, 5 ]; 3076 auto b = takeExactly(a, 3); 3077 assert(equal(b, [1, 2, 3])); 3078 auto c = takeExactly(b, 2); 3079 assert(equal(c, [1, 2])); 3080 3081 3082 3083 auto d = filter!"a > 2"(a); 3084 auto e = takeExactly(d, 3); 3085 assert(equal(e, [3, 4, 5])); 3086 static assert(is(typeof(e.length) == size_t)); 3087 assert(e.length == 3); 3088 assert(e.front == 3); 3089 3090 assert(equal(takeExactly(e, 3), [3, 4, 5])); 3091 } 3092 3093 pure @safe nothrow unittest 3094 { 3095 import std.algorithm.comparison : equal; 3096 import std.internal.test.dummyrange : AllDummyRanges; 3097 3098 auto a = [ 1, 2, 3, 4, 5 ]; 3099 //Test that take and takeExactly are the same for ranges which define length 3100 //but aren't sliceable. 3101 struct L 3102 { 3103 @property auto front() { return _arr[0]; } 3104 @property bool empty() { return _arr.empty; } 3105 void popFront() { _arr.popFront(); } 3106 @property size_t length() { return _arr.length; } 3107 int[] _arr; 3108 } 3109 static assert(is(typeof(take(L(a), 3)) == typeof(takeExactly(L(a), 3)))); 3110 assert(take(L(a), 3) == takeExactly(L(a), 3)); 3111 3112 //Test that take and takeExactly are the same for ranges which are sliceable. 3113 static assert(is(typeof(take(a, 3)) == typeof(takeExactly(a, 3)))); 3114 assert(take(a, 3) == takeExactly(a, 3)); 3115 3116 //Test that take and takeExactly are the same for infinite ranges. 3117 auto inf = repeat(1); 3118 static assert(is(typeof(take(inf, 5)) == Take!(typeof(inf)))); 3119 assert(take(inf, 5) == takeExactly(inf, 5)); 3120 3121 //Test that take and takeExactly are _not_ the same for ranges which don't 3122 //define length. 3123 static assert(!is(typeof(take(filter!"true"(a), 3)) == typeof(takeExactly(filter!"true"(a), 3)))); 3124 3125 foreach (DummyType; AllDummyRanges) 3126 { 3127 { 3128 DummyType dummy; 3129 auto t = takeExactly(dummy, 5); 3130 3131 //Test that takeExactly doesn't wrap the result of takeExactly. 3132 assert(takeExactly(t, 4) == takeExactly(dummy, 4)); 3133 } 3134 3135 static if (hasMobileElements!DummyType) 3136 { 3137 { 3138 auto t = takeExactly(DummyType.init, 4); 3139 assert(t.moveFront() == 1); 3140 assert(equal(t, [1, 2, 3, 4])); 3141 } 3142 } 3143 3144 static if (hasAssignableElements!DummyType) 3145 { 3146 { 3147 auto t = takeExactly(DummyType.init, 4); 3148 t.front = 9; 3149 assert(equal(t, [9, 2, 3, 4])); 3150 } 3151 } 3152 } 3153 } 3154 3155 pure @safe nothrow unittest 3156 { 3157 import std.algorithm.comparison : equal; 3158 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 3159 3160 alias DummyType = DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward); 3161 auto te = takeExactly(DummyType(), 5); 3162 Take!DummyType t = te; 3163 assert(equal(t, [1, 2, 3, 4, 5])); 3164 assert(equal(t, te)); 3165 } 3166 3167 // https://issues.dlang.org/show_bug.cgi?id=18092 3168 // can't combine take and takeExactly 3169 @safe unittest 3170 { 3171 import std.algorithm.comparison : equal; 3172 import std.internal.test.dummyrange : AllDummyRanges; 3173 3174 static foreach (Range; AllDummyRanges) 3175 {{ 3176 Range r; 3177 assert(r.take(6).takeExactly(2).equal([1, 2])); 3178 assert(r.takeExactly(6).takeExactly(2).equal([1, 2])); 3179 assert(r.takeExactly(6).take(2).equal([1, 2])); 3180 }} 3181 } 3182 3183 /** 3184 Returns a range with at most one element; for example, $(D 3185 takeOne([42, 43, 44])) returns a range consisting of the integer $(D 3186 42). Calling `popFront()` off that range renders it empty. 3187 3188 In effect `takeOne(r)` is somewhat equivalent to $(D take(r, 1)) but in 3189 certain interfaces it is important to know statically that the range may only 3190 have at most one element. 3191 3192 The type returned by `takeOne` is a random-access range with length 3193 regardless of `R`'s capabilities, as long as it is a forward range. 3194 (another feature that distinguishes `takeOne` from `take`). If 3195 (D R) is an input range but not a forward range, return type is an input 3196 range with all random-access capabilities except save. 3197 */ 3198 auto takeOne(R)(R source) 3199 if (isInputRange!R) 3200 { 3201 static if (hasSlicing!R) 3202 { 3203 return source[0 .. !source.empty]; 3204 } 3205 else 3206 { 3207 static struct Result 3208 { 3209 private R _source; 3210 private bool _empty = true; 3211 @property bool empty() const { return _empty; } 3212 @property auto ref front() 3213 { 3214 assert(!empty, "Attempting to fetch the front of an empty takeOne"); 3215 return _source.front; 3216 } 3217 void popFront() 3218 { 3219 assert(!empty, "Attempting to popFront an empty takeOne"); 3220 _source.popFront(); 3221 _empty = true; 3222 } 3223 void popBack() 3224 { 3225 assert(!empty, "Attempting to popBack an empty takeOne"); 3226 _source.popFront(); 3227 _empty = true; 3228 } 3229 static if (isForwardRange!(Unqual!R)) 3230 { 3231 @property auto save() { return Result(_source.save, empty); } 3232 } 3233 @property auto ref back() 3234 { 3235 assert(!empty, "Attempting to fetch the back of an empty takeOne"); 3236 return _source.front; 3237 } 3238 @property size_t length() const { return !empty; } 3239 alias opDollar = length; 3240 auto ref opIndex(size_t n) 3241 { 3242 assert(n < length, "Attempting to index a takeOne out of bounds"); 3243 return _source.front; 3244 } 3245 auto opSlice(size_t m, size_t n) 3246 { 3247 assert( 3248 m <= n, 3249 "Attempting to slice a takeOne range with a larger first argument than the second." 3250 ); 3251 assert( 3252 n <= length, 3253 "Attempting to slice using an out of bounds index on a takeOne range." 3254 ); 3255 return n > m ? this : Result(_source, true); 3256 } 3257 // Non-standard property 3258 @property R source() { return _source; } 3259 } 3260 3261 return Result(source, source.empty); 3262 } 3263 } 3264 3265 /// 3266 pure @safe nothrow unittest 3267 { 3268 auto s = takeOne([42, 43, 44]); 3269 static assert(isRandomAccessRange!(typeof(s))); 3270 assert(s.length == 1); 3271 assert(!s.empty); 3272 assert(s.front == 42); 3273 s.front = 43; 3274 assert(s.front == 43); 3275 assert(s.back == 43); 3276 assert(s[0] == 43); 3277 s.popFront(); 3278 assert(s.length == 0); 3279 assert(s.empty); 3280 } 3281 3282 pure @safe nothrow @nogc unittest 3283 { 3284 struct NonForwardRange 3285 { 3286 enum empty = false; 3287 int front() { return 42; } 3288 void popFront() {} 3289 } 3290 3291 static assert(!isForwardRange!NonForwardRange); 3292 3293 auto s = takeOne(NonForwardRange()); 3294 assert(s.length == 1); 3295 assert(!s.empty); 3296 assert(s.front == 42); 3297 assert(s.back == 42); 3298 assert(s[0] == 42); 3299 3300 auto t = s[0 .. 0]; 3301 assert(t.empty); 3302 assert(t.length == 0); 3303 3304 auto u = s[1 .. 1]; 3305 assert(u.empty); 3306 assert(u.length == 0); 3307 3308 auto v = s[0 .. 1]; 3309 s.popFront(); 3310 assert(s.length == 0); 3311 assert(s.empty); 3312 assert(!v.empty); 3313 assert(v.front == 42); 3314 v.popBack(); 3315 assert(v.empty); 3316 assert(v.length == 0); 3317 } 3318 3319 pure @safe nothrow @nogc unittest 3320 { 3321 struct NonSlicingForwardRange 3322 { 3323 enum empty = false; 3324 int front() { return 42; } 3325 void popFront() {} 3326 @property auto save() { return this; } 3327 } 3328 3329 static assert(isForwardRange!NonSlicingForwardRange); 3330 static assert(!hasSlicing!NonSlicingForwardRange); 3331 3332 auto s = takeOne(NonSlicingForwardRange()); 3333 assert(s.length == 1); 3334 assert(!s.empty); 3335 assert(s.front == 42); 3336 assert(s.back == 42); 3337 assert(s[0] == 42); 3338 auto t = s.save; 3339 s.popFront(); 3340 assert(s.length == 0); 3341 assert(s.empty); 3342 assert(!t.empty); 3343 assert(t.front == 42); 3344 t.popBack(); 3345 assert(t.empty); 3346 assert(t.length == 0); 3347 } 3348 3349 // Test that asserts trigger correctly 3350 @system unittest 3351 { 3352 import std.exception : assertThrown; 3353 import core.exception : AssertError; 3354 3355 struct NonForwardRange 3356 { 3357 enum empty = false; 3358 int front() { return 42; } 3359 void popFront() {} 3360 } 3361 3362 auto s = takeOne(NonForwardRange()); 3363 3364 assertThrown!AssertError(s[1]); 3365 assertThrown!AssertError(s[0 .. 2]); 3366 3367 size_t one = 1; // Avoid style warnings triggered by literals 3368 size_t zero = 0; 3369 assertThrown!AssertError(s[one .. zero]); 3370 3371 s.popFront; 3372 assert(s.empty); 3373 assertThrown!AssertError(s.front); 3374 assertThrown!AssertError(s.back); 3375 assertThrown!AssertError(s.popFront); 3376 assertThrown!AssertError(s.popBack); 3377 } 3378 3379 // https://issues.dlang.org/show_bug.cgi?id=16999 3380 pure @safe unittest 3381 { 3382 auto myIota = new class 3383 { 3384 int front = 0; 3385 @safe void popFront(){front++;} 3386 enum empty = false; 3387 }; 3388 auto iotaPart = myIota.takeOne; 3389 int sum; 3390 foreach (var; chain(iotaPart, iotaPart, iotaPart)) 3391 { 3392 sum += var; 3393 } 3394 assert(sum == 3); 3395 assert(iotaPart.front == 3); 3396 } 3397 3398 /++ 3399 Returns an empty range which is statically known to be empty and is 3400 guaranteed to have `length` and be random access regardless of `R`'s 3401 capabilities. 3402 +/ 3403 auto takeNone(R)() 3404 if (isInputRange!R) 3405 { 3406 return typeof(takeOne(R.init)).init; 3407 } 3408 3409 /// 3410 pure @safe nothrow @nogc unittest 3411 { 3412 auto range = takeNone!(int[])(); 3413 assert(range.length == 0); 3414 assert(range.empty); 3415 } 3416 3417 pure @safe nothrow @nogc unittest 3418 { 3419 enum ctfe = takeNone!(int[])(); 3420 static assert(ctfe.length == 0); 3421 static assert(ctfe.empty); 3422 } 3423 3424 3425 /++ 3426 Creates an empty range from the given range in $(BIGOH 1). If it can, it 3427 will return the same range type. If not, it will return 3428 $(D takeExactly(range, 0)). 3429 +/ 3430 auto takeNone(R)(R range) 3431 if (isInputRange!R) 3432 { 3433 import std.traits : isDynamicArray; 3434 //Makes it so that calls to takeNone which don't use UFCS still work with a 3435 //member version if it's defined. 3436 static if (is(typeof(R.takeNone))) 3437 auto retval = range.takeNone(); 3438 // https://issues.dlang.org/show_bug.cgi?id=8339 3439 else static if (isDynamicArray!R)/+ || 3440 (is(R == struct) && __traits(compiles, {auto r = R.init;}) && R.init.empty))+/ 3441 { 3442 auto retval = R.init; 3443 } 3444 //An infinite range sliced at [0 .. 0] would likely still not be empty... 3445 else static if (hasSlicing!R && !isInfinite!R) 3446 auto retval = range[0 .. 0]; 3447 else 3448 auto retval = takeExactly(range, 0); 3449 3450 // https://issues.dlang.org/show_bug.cgi?id=7892 prevents this from being 3451 // done in an out block. 3452 assert(retval.empty); 3453 return retval; 3454 } 3455 3456 /// 3457 pure @safe nothrow unittest 3458 { 3459 import std.algorithm.iteration : filter; 3460 assert(takeNone([42, 27, 19]).empty); 3461 assert(takeNone("dlang.org").empty); 3462 assert(takeNone(filter!"true"([42, 27, 19])).empty); 3463 } 3464 3465 @safe unittest 3466 { 3467 import std.algorithm.iteration : filter; 3468 import std.meta : AliasSeq; 3469 3470 struct Dummy 3471 { 3472 mixin template genInput() 3473 { 3474 @safe: 3475 @property bool empty() { return _arr.empty; } 3476 @property auto front() { return _arr.front; } 3477 void popFront() { _arr.popFront(); } 3478 static assert(isInputRange!(typeof(this))); 3479 } 3480 } 3481 alias genInput = Dummy.genInput; 3482 3483 static struct NormalStruct 3484 { 3485 //Disabled to make sure that the takeExactly version is used. 3486 @disable this(); 3487 this(int[] arr) { _arr = arr; } 3488 mixin genInput; 3489 int[] _arr; 3490 } 3491 3492 static struct SliceStruct 3493 { 3494 @disable this(); 3495 this(int[] arr) { _arr = arr; } 3496 mixin genInput; 3497 @property auto save() { return this; } 3498 auto opSlice(size_t i, size_t j) { return typeof(this)(_arr[i .. j]); } 3499 @property size_t length() { return _arr.length; } 3500 int[] _arr; 3501 } 3502 3503 static struct InitStruct 3504 { 3505 mixin genInput; 3506 int[] _arr; 3507 } 3508 3509 static struct TakeNoneStruct 3510 { 3511 this(int[] arr) { _arr = arr; } 3512 @disable this(); 3513 mixin genInput; 3514 auto takeNone() { return typeof(this)(null); } 3515 int[] _arr; 3516 } 3517 3518 static class NormalClass 3519 { 3520 this(int[] arr) {_arr = arr;} 3521 mixin genInput; 3522 int[] _arr; 3523 } 3524 3525 static class SliceClass 3526 { 3527 @safe: 3528 this(int[] arr) { _arr = arr; } 3529 mixin genInput; 3530 @property auto save() { return new typeof(this)(_arr); } 3531 auto opSlice(size_t i, size_t j) { return new typeof(this)(_arr[i .. j]); } 3532 @property size_t length() { return _arr.length; } 3533 int[] _arr; 3534 } 3535 3536 static class TakeNoneClass 3537 { 3538 @safe: 3539 this(int[] arr) { _arr = arr; } 3540 mixin genInput; 3541 auto takeNone() { return new typeof(this)(null); } 3542 int[] _arr; 3543 } 3544 3545 import std.format : format; 3546 3547 static foreach (range; AliasSeq!([1, 2, 3, 4, 5], 3548 "hello world", 3549 "hello world"w, 3550 "hello world"d, 3551 SliceStruct([1, 2, 3]), 3552 // https://issues.dlang.org/show_bug.cgi?id=8339 3553 // forces this to be takeExactly `InitStruct([1, 2, 3]), 3554 TakeNoneStruct([1, 2, 3]))) 3555 { 3556 static assert(takeNone(range).empty, typeof(range).stringof); 3557 assert(takeNone(range).empty); 3558 static assert(is(typeof(range) == typeof(takeNone(range))), typeof(range).stringof); 3559 } 3560 3561 static foreach (range; AliasSeq!(NormalStruct([1, 2, 3]), 3562 InitStruct([1, 2, 3]))) 3563 { 3564 static assert(takeNone(range).empty, typeof(range).stringof); 3565 assert(takeNone(range).empty); 3566 static assert(is(typeof(takeExactly(range, 0)) == typeof(takeNone(range))), typeof(range).stringof); 3567 } 3568 3569 //Don't work in CTFE. 3570 auto normal = new NormalClass([1, 2, 3]); 3571 assert(takeNone(normal).empty); 3572 static assert(is(typeof(takeExactly(normal, 0)) == typeof(takeNone(normal))), typeof(normal).stringof); 3573 3574 auto slice = new SliceClass([1, 2, 3]); 3575 assert(takeNone(slice).empty); 3576 static assert(is(SliceClass == typeof(takeNone(slice))), typeof(slice).stringof); 3577 3578 auto taken = new TakeNoneClass([1, 2, 3]); 3579 assert(takeNone(taken).empty); 3580 static assert(is(TakeNoneClass == typeof(takeNone(taken))), typeof(taken).stringof); 3581 3582 auto filtered = filter!"true"([1, 2, 3, 4, 5]); 3583 assert(takeNone(filtered).empty); 3584 // https://issues.dlang.org/show_bug.cgi?id=8339 and 3585 // https://issues.dlang.org/show_bug.cgi?id=5941 force this to be takeExactly 3586 //static assert(is(typeof(filtered) == typeof(takeNone(filtered))), typeof(filtered).stringof); 3587 } 3588 3589 /++ 3590 + Return a range advanced to within `_n` elements of the end of 3591 + `range`. 3592 + 3593 + Intended as the range equivalent of the Unix 3594 + $(HTTP en.wikipedia.org/wiki/Tail_%28Unix%29, _tail) utility. When the length 3595 + of `range` is less than or equal to `_n`, `range` is returned 3596 + as-is. 3597 + 3598 + Completes in $(BIGOH 1) steps for ranges that support slicing and have 3599 + length. Completes in $(BIGOH range.length) time for all other ranges. 3600 + 3601 + Params: 3602 + range = range to get _tail of 3603 + n = maximum number of elements to include in _tail 3604 + 3605 + Returns: 3606 + Returns the _tail of `range` augmented with length information 3607 +/ 3608 auto tail(Range)(Range range, size_t n) 3609 if (isInputRange!Range && !isInfinite!Range && 3610 (hasLength!Range || isForwardRange!Range)) 3611 { 3612 static if (hasLength!Range) 3613 { 3614 immutable length = range.length; 3615 if (n >= length) 3616 return range.takeExactly(length); 3617 else 3618 return range.drop(length - n).takeExactly(n); 3619 } 3620 else 3621 { 3622 Range scout = range.save; 3623 foreach (immutable i; 0 .. n) 3624 { 3625 if (scout.empty) 3626 return range.takeExactly(i); 3627 scout.popFront(); 3628 } 3629 3630 auto tail = range.save; 3631 while (!scout.empty) 3632 { 3633 assert(!tail.empty); 3634 scout.popFront(); 3635 tail.popFront(); 3636 } 3637 3638 return tail.takeExactly(n); 3639 } 3640 } 3641 3642 /// 3643 pure @safe nothrow unittest 3644 { 3645 // tail -c n 3646 assert([1, 2, 3].tail(1) == [3]); 3647 assert([1, 2, 3].tail(2) == [2, 3]); 3648 assert([1, 2, 3].tail(3) == [1, 2, 3]); 3649 assert([1, 2, 3].tail(4) == [1, 2, 3]); 3650 assert([1, 2, 3].tail(0).length == 0); 3651 3652 // tail --lines=n 3653 import std.algorithm.comparison : equal; 3654 import std.algorithm.iteration : joiner; 3655 import std.exception : assumeWontThrow; 3656 import std.string : lineSplitter; 3657 assert("one\ntwo\nthree" 3658 .lineSplitter 3659 .tail(2) 3660 .joiner("\n") 3661 .equal("two\nthree") 3662 .assumeWontThrow); 3663 } 3664 3665 // @nogc prevented by https://issues.dlang.org/show_bug.cgi?id=15408 3666 pure nothrow @safe /+@nogc+/ unittest 3667 { 3668 import std.algorithm.comparison : equal; 3669 import std.internal.test.dummyrange : AllDummyRanges, DummyRange, Length, 3670 RangeType, ReturnBy; 3671 3672 static immutable cheatsheet = [6, 7, 8, 9, 10]; 3673 3674 foreach (R; AllDummyRanges) 3675 { 3676 static if (isInputRange!R && !isInfinite!R && 3677 (hasLength!R || isForwardRange!R)) 3678 { 3679 assert(R.init.tail(5).equal(cheatsheet)); 3680 static assert(R.init.tail(5).equal(cheatsheet)); 3681 3682 assert(R.init.tail(0).length == 0); 3683 assert(R.init.tail(10).equal(R.init)); 3684 assert(R.init.tail(11).equal(R.init)); 3685 } 3686 } 3687 3688 // Infinite ranges are not supported 3689 static assert(!__traits(compiles, repeat(0).tail(0))); 3690 3691 // Neither are non-forward ranges without length 3692 static assert(!__traits(compiles, DummyRange!(ReturnBy.Value, Length.No, 3693 RangeType.Input).init.tail(5))); 3694 } 3695 3696 pure @safe nothrow @nogc unittest 3697 { 3698 static immutable input = [1, 2, 3]; 3699 static immutable expectedOutput = [2, 3]; 3700 assert(input.tail(2) == expectedOutput); 3701 } 3702 3703 /++ 3704 Convenience function which calls 3705 $(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`. 3706 `drop` makes it easier to pop elements from a range 3707 and then pass it to another function within a single expression, 3708 whereas `popFrontN` would require multiple statements. 3709 3710 `dropBack` provides the same functionality but instead calls 3711 $(REF popBackN, std, range, primitives)`(range, n)` 3712 3713 Note: `drop` and `dropBack` will only pop $(I up to) 3714 `n` elements but will stop if the range is empty first. 3715 In other languages this is sometimes called `skip`. 3716 3717 Params: 3718 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from 3719 n = the number of elements to drop 3720 3721 Returns: 3722 `range` with up to `n` elements dropped 3723 3724 See_Also: 3725 $(REF popFront, std, range, primitives), $(REF popBackN, std, range, primitives) 3726 +/ 3727 R drop(R)(R range, size_t n) 3728 if (isInputRange!R) 3729 { 3730 range.popFrontN(n); 3731 return range; 3732 } 3733 3734 /// 3735 @safe unittest 3736 { 3737 import std.algorithm.comparison : equal; 3738 3739 assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]); 3740 assert("hello world".drop(6) == "world"); 3741 assert("hello world".drop(50).empty); 3742 assert("hello world".take(6).drop(3).equal("lo ")); 3743 } 3744 3745 /// ditto 3746 R dropBack(R)(R range, size_t n) 3747 if (isBidirectionalRange!R) 3748 { 3749 range.popBackN(n); 3750 return range; 3751 } 3752 3753 /// 3754 @safe unittest 3755 { 3756 import std.algorithm.comparison : equal; 3757 3758 assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]); 3759 assert("hello world".dropBack(6) == "hello"); 3760 assert("hello world".dropBack(50).empty); 3761 assert("hello world".drop(4).dropBack(4).equal("o w")); 3762 } 3763 3764 @safe unittest 3765 { 3766 import std.algorithm.comparison : equal; 3767 import std.container.dlist : DList; 3768 3769 //Remove all but the first two elements 3770 auto a = DList!int(0, 1, 9, 9, 9, 9); 3771 a.remove(a[].drop(2)); 3772 assert(a[].equal(a[].take(2))); 3773 } 3774 3775 @safe unittest 3776 { 3777 import std.algorithm.comparison : equal; 3778 import std.algorithm.iteration : filter; 3779 3780 assert(drop("", 5).empty); 3781 assert(equal(drop(filter!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3])); 3782 } 3783 3784 @safe unittest 3785 { 3786 import std.algorithm.comparison : equal; 3787 import std.container.dlist : DList; 3788 3789 //insert before the last two elements 3790 auto a = DList!int(0, 1, 2, 5, 6); 3791 a.insertAfter(a[].dropBack(2), [3, 4]); 3792 assert(a[].equal(iota(0, 7))); 3793 } 3794 3795 /++ 3796 Similar to $(LREF drop) and `dropBack` but they call 3797 $(D range.$(LREF popFrontExactly)(n)) and `range.popBackExactly(n)` 3798 instead. 3799 3800 Note: Unlike `drop`, `dropExactly` will assume that the 3801 range holds at least `n` elements. This makes `dropExactly` 3802 faster than `drop`, but it also means that if `range` does 3803 not contain at least `n` elements, it will attempt to call `popFront` 3804 on an empty range, which is undefined behavior. So, only use 3805 `popFrontExactly` when it is guaranteed that `range` holds at least 3806 `n` elements. 3807 3808 Params: 3809 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from 3810 n = the number of elements to drop 3811 3812 Returns: 3813 `range` with `n` elements dropped 3814 3815 See_Also: 3816 $(REF popFrontExcatly, std, range, primitives), 3817 $(REF popBackExcatly, std, range, primitives) 3818 +/ 3819 R dropExactly(R)(R range, size_t n) 3820 if (isInputRange!R) 3821 { 3822 popFrontExactly(range, n); 3823 return range; 3824 } 3825 /// ditto 3826 R dropBackExactly(R)(R range, size_t n) 3827 if (isBidirectionalRange!R) 3828 { 3829 popBackExactly(range, n); 3830 return range; 3831 } 3832 3833 /// 3834 @safe unittest 3835 { 3836 import std.algorithm.comparison : equal; 3837 import std.algorithm.iteration : filterBidirectional; 3838 3839 auto a = [1, 2, 3]; 3840 assert(a.dropExactly(2) == [3]); 3841 assert(a.dropBackExactly(2) == [1]); 3842 3843 string s = "日本語"; 3844 assert(s.dropExactly(2) == "語"); 3845 assert(s.dropBackExactly(2) == "日"); 3846 3847 auto bd = filterBidirectional!"true"([1, 2, 3]); 3848 assert(bd.dropExactly(2).equal([3])); 3849 assert(bd.dropBackExactly(2).equal([1])); 3850 } 3851 3852 /++ 3853 Convenience function which calls 3854 `range.popFront()` and returns `range`. `dropOne` 3855 makes it easier to pop an element from a range 3856 and then pass it to another function within a single expression, 3857 whereas `popFront` would require multiple statements. 3858 3859 `dropBackOne` provides the same functionality but instead calls 3860 `range.popBack()`. 3861 +/ 3862 R dropOne(R)(R range) 3863 if (isInputRange!R) 3864 { 3865 range.popFront(); 3866 return range; 3867 } 3868 /// ditto 3869 R dropBackOne(R)(R range) 3870 if (isBidirectionalRange!R) 3871 { 3872 range.popBack(); 3873 return range; 3874 } 3875 3876 /// 3877 pure @safe nothrow unittest 3878 { 3879 import std.algorithm.comparison : equal; 3880 import std.algorithm.iteration : filterBidirectional; 3881 import std.container.dlist : DList; 3882 3883 auto dl = DList!int(9, 1, 2, 3, 9); 3884 assert(dl[].dropOne().dropBackOne().equal([1, 2, 3])); 3885 3886 auto a = [1, 2, 3]; 3887 assert(a.dropOne() == [2, 3]); 3888 assert(a.dropBackOne() == [1, 2]); 3889 3890 string s = "日本語"; 3891 import std.exception : assumeWontThrow; 3892 assert(assumeWontThrow(s.dropOne() == "本語")); 3893 assert(assumeWontThrow(s.dropBackOne() == "日本")); 3894 3895 auto bd = filterBidirectional!"true"([1, 2, 3]); 3896 assert(bd.dropOne().equal([2, 3])); 3897 assert(bd.dropBackOne().equal([1, 2])); 3898 } 3899 3900 /** 3901 Create a range which repeats one value. 3902 3903 Params: 3904 value = the _value to repeat 3905 n = the number of times to repeat `value` 3906 3907 Returns: 3908 If `n` is not defined, an infinite random access range 3909 with slicing. 3910 3911 If `n` is defined, a random access range with slicing. 3912 */ 3913 struct Repeat(T) 3914 { 3915 private: 3916 import std.typecons : Rebindable2; 3917 3918 // Store a rebindable T to make Repeat assignable. 3919 Rebindable2!T _value; 3920 3921 public: 3922 /// Range primitives 3923 @property inout(T) front() inout { return _value.get; } 3924 3925 /// ditto 3926 @property inout(T) back() inout { return _value.get; } 3927 3928 /// ditto 3929 enum bool empty = false; 3930 3931 /// ditto 3932 void popFront() {} 3933 3934 /// ditto 3935 void popBack() {} 3936 3937 /// ditto 3938 @property auto save() inout { return this; } 3939 3940 /// ditto 3941 inout(T) opIndex(size_t) inout { return _value.get; } 3942 3943 /// ditto 3944 auto opSlice(size_t i, size_t j) 3945 in 3946 { 3947 assert( 3948 i <= j, 3949 "Attempting to slice a Repeat with a larger first argument than the second." 3950 ); 3951 } 3952 do 3953 { 3954 return this.takeExactly(j - i); 3955 } 3956 private static struct DollarToken {} 3957 3958 /// ditto 3959 enum opDollar = DollarToken.init; 3960 3961 /// ditto 3962 auto opSlice(size_t, DollarToken) inout { return this; } 3963 } 3964 3965 /// Ditto 3966 Repeat!T repeat(T)(T value) 3967 { 3968 import std.typecons : Rebindable2; 3969 3970 return Repeat!T(Rebindable2!T(value)); 3971 } 3972 3973 /// 3974 pure @safe nothrow unittest 3975 { 3976 import std.algorithm.comparison : equal; 3977 3978 assert(5.repeat().take(4).equal([5, 5, 5, 5])); 3979 } 3980 3981 pure @safe nothrow unittest 3982 { 3983 import std.algorithm.comparison : equal; 3984 3985 auto r = repeat(5); 3986 alias R = typeof(r); 3987 static assert(isBidirectionalRange!R); 3988 static assert(isForwardRange!R); 3989 static assert(isInfinite!R); 3990 static assert(hasSlicing!R); 3991 3992 assert(r.back == 5); 3993 assert(r.front == 5); 3994 assert(r.take(4).equal([ 5, 5, 5, 5 ])); 3995 assert(r[0 .. 4].equal([ 5, 5, 5, 5 ])); 3996 3997 R r2 = r[5 .. $]; 3998 assert(r2.back == 5); 3999 assert(r2.front == 5); 4000 } 4001 4002 /// ditto 4003 Take!(Repeat!T) repeat(T)(T value, size_t n) 4004 { 4005 return take(repeat(value), n); 4006 } 4007 4008 /// 4009 pure @safe nothrow unittest 4010 { 4011 import std.algorithm.comparison : equal; 4012 4013 assert(5.repeat(4).equal([5, 5, 5, 5])); 4014 } 4015 4016 // https://issues.dlang.org/show_bug.cgi?id=12007 4017 pure @safe nothrow unittest 4018 { 4019 static class C{} 4020 Repeat!(immutable int) ri; 4021 ri = ri.save; 4022 Repeat!(immutable C) rc; 4023 rc = rc.save; 4024 4025 import std.algorithm.setops : cartesianProduct; 4026 import std.algorithm.comparison : equal; 4027 import std.typecons : tuple; 4028 immutable int[] A = [1,2,3]; 4029 immutable int[] B = [4,5,6]; 4030 4031 assert(equal(cartesianProduct(A,B), 4032 [ 4033 tuple(1, 4), tuple(1, 5), tuple(1, 6), 4034 tuple(2, 4), tuple(2, 5), tuple(2, 6), 4035 tuple(3, 4), tuple(3, 5), tuple(3, 6), 4036 ])); 4037 } 4038 4039 /** 4040 Given callable ($(REF isCallable, std,traits)) `fun`, create as a range 4041 whose front is defined by successive calls to `fun()`. 4042 This is especially useful to call function with global side effects (random 4043 functions), or to create ranges expressed as a single delegate, rather than 4044 an entire `front`/`popFront`/`empty` structure. 4045 `fun` maybe be passed either a template alias parameter (existing 4046 function, delegate, struct type defining `static opCall`) or 4047 a run-time value argument (delegate, function object). 4048 The result range models an InputRange 4049 ($(REF isInputRange, std,range,primitives)). 4050 The resulting range will call `fun()` on construction, and every call to 4051 `popFront`, and the cached value will be returned when `front` is called. 4052 4053 Returns: an `inputRange` where each element represents another call to fun. 4054 */ 4055 auto generate(Fun)(Fun fun) 4056 if (isCallable!fun) 4057 { 4058 auto gen = Generator!(Fun)(fun); 4059 gen.popFront(); // prime the first element 4060 return gen; 4061 } 4062 4063 /// ditto 4064 auto generate(alias fun)() 4065 if (isCallable!fun) 4066 { 4067 auto gen = Generator!(fun)(); 4068 gen.popFront(); // prime the first element 4069 return gen; 4070 } 4071 4072 /// 4073 @safe pure nothrow unittest 4074 { 4075 import std.algorithm.comparison : equal; 4076 import std.algorithm.iteration : map; 4077 4078 int i = 1; 4079 auto powersOfTwo = generate!(() => i *= 2)().take(10); 4080 assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"())); 4081 } 4082 4083 /// 4084 @safe pure nothrow unittest 4085 { 4086 import std.algorithm.comparison : equal; 4087 4088 //Returns a run-time delegate 4089 auto infiniteIota(T)(T low, T high) 4090 { 4091 T i = high; 4092 return (){if (i == high) i = low; return i++;}; 4093 } 4094 //adapted as a range. 4095 assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1])); 4096 } 4097 4098 /// 4099 @safe unittest 4100 { 4101 import std.format : format; 4102 import std.random : uniform; 4103 4104 auto r = generate!(() => uniform(0, 6)).take(10); 4105 format("%(%s %)", r); 4106 } 4107 4108 private struct Generator(Fun...) 4109 { 4110 static assert(Fun.length == 1); 4111 static assert(isInputRange!Generator); 4112 import std.traits : FunctionAttribute, functionAttributes, ReturnType; 4113 4114 private: 4115 static if (is(Fun[0])) 4116 Fun[0] fun; 4117 else 4118 alias fun = Fun[0]; 4119 4120 enum returnByRef_ = (functionAttributes!fun & FunctionAttribute.ref_) ? true : false; 4121 4122 import std.traits : hasIndirections; 4123 static if (!hasIndirections!(ReturnType!fun)) 4124 alias RetType = Unqual!(ReturnType!fun); 4125 else 4126 alias RetType = ReturnType!fun; 4127 4128 static if (returnByRef_) 4129 RetType *elem_; 4130 else 4131 RetType elem_; 4132 public: 4133 /// Range primitives 4134 enum empty = false; 4135 4136 static if (returnByRef_) 4137 { 4138 /// ditto 4139 ref front() @property 4140 { 4141 return *elem_; 4142 } 4143 /// ditto 4144 void popFront() 4145 { 4146 elem_ = &fun(); 4147 } 4148 } 4149 else 4150 { 4151 /// ditto 4152 auto front() @property 4153 { 4154 return elem_; 4155 } 4156 /// ditto 4157 void popFront() 4158 { 4159 elem_ = fun(); 4160 } 4161 } 4162 } 4163 4164 @safe nothrow unittest 4165 { 4166 import std.algorithm.comparison : equal; 4167 4168 struct StaticOpCall 4169 { 4170 static ubyte opCall() { return 5 ; } 4171 } 4172 4173 assert(equal(generate!StaticOpCall().take(10), repeat(5).take(10))); 4174 } 4175 4176 @safe pure unittest 4177 { 4178 import std.algorithm.comparison : equal; 4179 4180 struct OpCall 4181 { 4182 ubyte opCall() @safe pure { return 5 ; } 4183 } 4184 4185 OpCall op; 4186 assert(equal(generate(op).take(10), repeat(5).take(10))); 4187 } 4188 4189 // verify ref mechanism works 4190 @system nothrow unittest 4191 { 4192 int[10] arr; 4193 int idx; 4194 4195 ref int fun() { 4196 auto x = idx++; 4197 idx %= arr.length; 4198 return arr[x]; 4199 } 4200 int y = 1; 4201 foreach (ref x; generate!(fun).take(20)) 4202 { 4203 x += y++; 4204 } 4205 import std.algorithm.comparison : equal; 4206 assert(equal(arr[], iota(12, 32, 2))); 4207 } 4208 4209 // assure front isn't the mechanism to make generate go to the next element. 4210 @safe unittest 4211 { 4212 int i; 4213 auto g = generate!(() => ++i); 4214 auto f = g.front; 4215 assert(f == g.front); 4216 g = g.drop(5); // reassign because generate caches 4217 assert(g.front == f + 5); 4218 } 4219 4220 // https://issues.dlang.org/show_bug.cgi?id=23319 4221 @safe pure nothrow unittest 4222 { 4223 auto b = generate!(() => const(int)(42)); 4224 assert(b.front == 42); 4225 } 4226 4227 /** 4228 Repeats the given forward range ad infinitum. If the original range is 4229 infinite (fact that would make `Cycle` the identity application), 4230 `Cycle` detects that and aliases itself to the range type 4231 itself. That works for non-forward ranges too. 4232 If the original range has random access, `Cycle` offers 4233 random access and also offers a constructor taking an initial position 4234 `index`. `Cycle` works with static arrays in addition to ranges, 4235 mostly for performance reasons. 4236 4237 Note: The input range must not be empty. 4238 4239 Tip: This is a great way to implement simple circular buffers. 4240 */ 4241 struct Cycle(R) 4242 if (isForwardRange!R && !isInfinite!R) 4243 { 4244 static if (isRandomAccessRange!R && hasLength!R) 4245 { 4246 private R _original; 4247 private size_t _index; 4248 4249 /// Range primitives 4250 this(R input, size_t index = 0) 4251 { 4252 _original = input; 4253 _index = index % _original.length; 4254 } 4255 4256 /// ditto 4257 @property auto ref front() 4258 { 4259 return _original[_index]; 4260 } 4261 4262 static if (is(typeof((cast(const R)_original)[_index]))) 4263 { 4264 /// ditto 4265 @property auto ref front() const 4266 { 4267 return _original[_index]; 4268 } 4269 } 4270 4271 static if (hasAssignableElements!R) 4272 { 4273 /// ditto 4274 @property void front(ElementType!R val) 4275 { 4276 import std.algorithm.mutation : move; 4277 4278 _original[_index] = move(val); 4279 } 4280 } 4281 4282 /// ditto 4283 enum bool empty = false; 4284 4285 /// ditto 4286 void popFront() 4287 { 4288 ++_index; 4289 if (_index >= _original.length) 4290 _index = 0; 4291 } 4292 4293 /// ditto 4294 auto ref opIndex(size_t n) 4295 { 4296 return _original[(n + _index) % _original.length]; 4297 } 4298 4299 static if (is(typeof((cast(const R)_original)[_index])) && 4300 is(typeof((cast(const R)_original).length))) 4301 { 4302 /// ditto 4303 auto ref opIndex(size_t n) const 4304 { 4305 return _original[(n + _index) % _original.length]; 4306 } 4307 } 4308 4309 static if (hasAssignableElements!R) 4310 { 4311 /// ditto 4312 void opIndexAssign(ElementType!R val, size_t n) 4313 { 4314 _original[(n + _index) % _original.length] = val; 4315 } 4316 } 4317 4318 /// ditto 4319 @property Cycle save() 4320 { 4321 //No need to call _original.save, because Cycle never actually modifies _original 4322 return Cycle(_original, _index); 4323 } 4324 4325 private static struct DollarToken {} 4326 4327 /// ditto 4328 enum opDollar = DollarToken.init; 4329 4330 static if (hasSlicing!R) 4331 { 4332 /// ditto 4333 auto opSlice(size_t i, size_t j) 4334 in 4335 { 4336 assert(i <= j); 4337 } 4338 do 4339 { 4340 return this[i .. $].takeExactly(j - i); 4341 } 4342 4343 /// ditto 4344 auto opSlice(size_t i, DollarToken) 4345 { 4346 return typeof(this)(_original, _index + i); 4347 } 4348 } 4349 } 4350 else 4351 { 4352 private R _original; 4353 private R _current; 4354 4355 /// ditto 4356 this(R input) 4357 { 4358 _original = input; 4359 _current = input.save; 4360 } 4361 4362 private this(R original, R current) 4363 { 4364 _original = original; 4365 _current = current; 4366 } 4367 4368 /// ditto 4369 @property auto ref front() 4370 { 4371 return _current.front; 4372 } 4373 4374 static if (is(typeof((cast(const R)_current).front))) 4375 { 4376 /// ditto 4377 @property auto ref front() const 4378 { 4379 return _current.front; 4380 } 4381 } 4382 4383 static if (hasAssignableElements!R) 4384 { 4385 /// ditto 4386 @property auto front(ElementType!R val) 4387 { 4388 import std.algorithm.mutation : move; 4389 4390 return _current.front = move(val); 4391 } 4392 } 4393 4394 /// ditto 4395 enum bool empty = false; 4396 4397 /// ditto 4398 void popFront() 4399 { 4400 _current.popFront(); 4401 if (_current.empty) 4402 _current = _original.save; 4403 } 4404 4405 /// ditto 4406 @property Cycle save() 4407 { 4408 //No need to call _original.save, because Cycle never actually modifies _original 4409 return Cycle(_original, _current.save); 4410 } 4411 } 4412 } 4413 4414 /// ditto 4415 template Cycle(R) 4416 if (isInfinite!R) 4417 { 4418 alias Cycle = R; 4419 } 4420 4421 /// ditto 4422 struct Cycle(R) 4423 if (isStaticArray!R) 4424 { 4425 private alias ElementType = typeof(R.init[0]); 4426 private ElementType* _ptr; 4427 private size_t _index; 4428 4429 nothrow: 4430 4431 /// Range primitives 4432 this(ref R input, size_t index = 0) @system 4433 { 4434 _ptr = input.ptr; 4435 _index = index % R.length; 4436 } 4437 4438 /// ditto 4439 @property ref inout(ElementType) front() inout @safe 4440 { 4441 static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted 4442 { 4443 return p[idx]; 4444 } 4445 return trustedPtrIdx(_ptr, _index); 4446 } 4447 4448 /// ditto 4449 enum bool empty = false; 4450 4451 /// ditto 4452 void popFront() @safe 4453 { 4454 ++_index; 4455 if (_index >= R.length) 4456 _index = 0; 4457 } 4458 4459 /// ditto 4460 ref inout(ElementType) opIndex(size_t n) inout @safe 4461 { 4462 static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted 4463 { 4464 return p[idx % R.length]; 4465 } 4466 return trustedPtrIdx(_ptr, n + _index); 4467 } 4468 4469 /// ditto 4470 @property inout(Cycle) save() inout @safe 4471 { 4472 return this; 4473 } 4474 4475 private static struct DollarToken {} 4476 /// ditto 4477 enum opDollar = DollarToken.init; 4478 4479 /// ditto 4480 auto opSlice(size_t i, size_t j) @safe 4481 in 4482 { 4483 assert( 4484 i <= j, 4485 "Attempting to slice a Repeat with a larger first argument than the second." 4486 ); 4487 } 4488 do 4489 { 4490 return this[i .. $].takeExactly(j - i); 4491 } 4492 4493 /// ditto 4494 inout(typeof(this)) opSlice(size_t i, DollarToken) inout @safe 4495 { 4496 static auto trustedCtor(typeof(_ptr) p, size_t idx) @trusted 4497 { 4498 return cast(inout) Cycle(*cast(R*)(p), idx); 4499 } 4500 return trustedCtor(_ptr, _index + i); 4501 } 4502 } 4503 4504 /// Ditto 4505 auto cycle(R)(R input) 4506 if (isInputRange!R) 4507 { 4508 static assert(isForwardRange!R || isInfinite!R, 4509 "Cycle requires a forward range argument unless it's statically known" 4510 ~ " to be infinite"); 4511 assert(!input.empty, "Attempting to pass an empty input to cycle"); 4512 static if (isInfinite!R) return input; 4513 else return Cycle!R(input); 4514 } 4515 4516 /// 4517 @safe unittest 4518 { 4519 import std.algorithm.comparison : equal; 4520 import std.range : cycle, take; 4521 4522 // Here we create an infinitive cyclic sequence from [1, 2] 4523 // (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then 4524 // take 5 elements of this sequence (so we have [1, 2, 1, 2, 1]) 4525 // and compare them with the expected values for equality. 4526 assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ])); 4527 } 4528 4529 /// Ditto 4530 Cycle!R cycle(R)(R input, size_t index = 0) 4531 if (isRandomAccessRange!R && !isInfinite!R) 4532 { 4533 assert(!input.empty, "Attempting to pass an empty input to cycle"); 4534 return Cycle!R(input, index); 4535 } 4536 4537 /// Ditto 4538 Cycle!R cycle(R)(ref R input, size_t index = 0) @system 4539 if (isStaticArray!R) 4540 { 4541 return Cycle!R(input, index); 4542 } 4543 4544 @safe nothrow unittest 4545 { 4546 import std.algorithm.comparison : equal; 4547 import std.internal.test.dummyrange : AllDummyRanges; 4548 4549 static assert(isForwardRange!(Cycle!(uint[]))); 4550 4551 // Make sure ref is getting propagated properly. 4552 int[] nums = [1,2,3]; 4553 auto c2 = cycle(nums); 4554 c2[3]++; 4555 assert(nums[0] == 2); 4556 4557 immutable int[] immarr = [1, 2, 3]; 4558 4559 foreach (DummyType; AllDummyRanges) 4560 { 4561 static if (isForwardRange!DummyType) 4562 { 4563 DummyType dummy; 4564 auto cy = cycle(dummy); 4565 static assert(isForwardRange!(typeof(cy))); 4566 auto t = take(cy, 20); 4567 assert(equal(t, [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10])); 4568 4569 const cRange = cy; 4570 assert(cRange.front == 1); 4571 4572 static if (hasAssignableElements!DummyType) 4573 { 4574 { 4575 cy.front = 66; 4576 scope(exit) cy.front = 1; 4577 assert(dummy.front == 66); 4578 } 4579 4580 static if (isRandomAccessRange!DummyType) 4581 { 4582 { 4583 cy[10] = 66; 4584 scope(exit) cy[10] = 1; 4585 assert(dummy.front == 66); 4586 } 4587 4588 assert(cRange[10] == 1); 4589 } 4590 } 4591 4592 static if (hasSlicing!DummyType) 4593 { 4594 auto slice = cy[5 .. 15]; 4595 assert(equal(slice, [6, 7, 8, 9, 10, 1, 2, 3, 4, 5])); 4596 static assert(is(typeof(slice) == typeof(takeExactly(cy, 5)))); 4597 4598 auto infSlice = cy[7 .. $]; 4599 assert(equal(take(infSlice, 5), [8, 9, 10, 1, 2])); 4600 static assert(isInfinite!(typeof(infSlice))); 4601 } 4602 } 4603 } 4604 } 4605 4606 @system nothrow unittest // For static arrays. 4607 { 4608 import std.algorithm.comparison : equal; 4609 4610 int[3] a = [ 1, 2, 3 ]; 4611 static assert(isStaticArray!(typeof(a))); 4612 auto c = cycle(a); 4613 assert(a.ptr == c._ptr); 4614 assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][])); 4615 static assert(isForwardRange!(typeof(c))); 4616 4617 // Test qualifiers on slicing. 4618 alias C = typeof(c); 4619 static assert(is(typeof(c[1 .. $]) == C)); 4620 const cConst = c; 4621 static assert(is(typeof(cConst[1 .. $]) == const(C))); 4622 } 4623 4624 @safe nothrow unittest // For infinite ranges 4625 { 4626 struct InfRange 4627 { 4628 void popFront() { } 4629 @property int front() { return 0; } 4630 enum empty = false; 4631 auto save() { return this; } 4632 } 4633 struct NonForwardInfRange 4634 { 4635 void popFront() { } 4636 @property int front() { return 0; } 4637 enum empty = false; 4638 } 4639 4640 InfRange i; 4641 NonForwardInfRange j; 4642 auto c = cycle(i); 4643 assert(c == i); 4644 //make sure it can alias out even non-forward infinite ranges 4645 static assert(is(typeof(j.cycle) == typeof(j))); 4646 } 4647 4648 @safe unittest 4649 { 4650 import std.algorithm.comparison : equal; 4651 4652 int[5] arr = [0, 1, 2, 3, 4]; 4653 auto cleD = cycle(arr[]); //Dynamic 4654 assert(equal(cleD[5 .. 10], arr[])); 4655 4656 //n is a multiple of 5 worth about 3/4 of size_t.max 4657 auto n = size_t.max/4 + size_t.max/2; 4658 n -= n % 5; 4659 4660 //Test index overflow 4661 foreach (_ ; 0 .. 10) 4662 { 4663 cleD = cleD[n .. $]; 4664 assert(equal(cleD[5 .. 10], arr[])); 4665 } 4666 } 4667 4668 @system @nogc nothrow unittest 4669 { 4670 import std.algorithm.comparison : equal; 4671 4672 int[5] arr = [0, 1, 2, 3, 4]; 4673 auto cleS = cycle(arr); //Static 4674 assert(equal(cleS[5 .. 10], arr[])); 4675 4676 //n is a multiple of 5 worth about 3/4 of size_t.max 4677 auto n = size_t.max/4 + size_t.max/2; 4678 n -= n % 5; 4679 4680 //Test index overflow 4681 foreach (_ ; 0 .. 10) 4682 { 4683 cleS = cleS[n .. $]; 4684 assert(equal(cleS[5 .. 10], arr[])); 4685 } 4686 } 4687 4688 @system unittest 4689 { 4690 import std.algorithm.comparison : equal; 4691 4692 int[1] arr = [0]; 4693 auto cleS = cycle(arr); 4694 cleS = cleS[10 .. $]; 4695 assert(equal(cleS[5 .. 10], 0.repeat(5))); 4696 assert(cleS.front == 0); 4697 } 4698 4699 // https://issues.dlang.org/show_bug.cgi?id=10845 4700 @system unittest 4701 { 4702 import std.algorithm.comparison : equal; 4703 import std.algorithm.iteration : filter; 4704 4705 auto a = inputRangeObject(iota(3).filter!"true"); 4706 assert(equal(cycle(a).take(10), [0, 1, 2, 0, 1, 2, 0, 1, 2, 0])); 4707 } 4708 4709 // https://issues.dlang.org/show_bug.cgi?id=12177 4710 @safe unittest 4711 { 4712 static assert(__traits(compiles, recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0"))); 4713 } 4714 4715 // https://issues.dlang.org/show_bug.cgi?id=13390 4716 @system unittest 4717 { 4718 import core.exception : AssertError; 4719 import std.exception : assertThrown; 4720 assertThrown!AssertError(cycle([0, 1, 2][0 .. 0])); 4721 } 4722 4723 // https://issues.dlang.org/show_bug.cgi?id=18657 4724 pure @safe unittest 4725 { 4726 import std.algorithm.comparison : equal; 4727 string s = "foo"; 4728 auto r = refRange(&s).cycle.take(4); 4729 assert(equal(r.save, "foof")); 4730 assert(equal(r.save, "foof")); 4731 } 4732 4733 private alias lengthType(R) = typeof(R.init.length.init); 4734 4735 /** 4736 Iterate several ranges in lockstep. The element type is a proxy tuple 4737 that allows accessing the current element in the `n`th range by 4738 using `e[n]`. 4739 4740 `zip` is similar to $(LREF lockstep), but `lockstep` doesn't 4741 bundle its elements and uses the `opApply` protocol. 4742 `lockstep` allows reference access to the elements in 4743 `foreach` iterations. 4744 4745 Params: 4746 sp = controls what `zip` will do if the ranges are different lengths 4747 ranges = the ranges to zip together 4748 Returns: 4749 At minimum, an input range. `Zip` offers the lowest range facilities 4750 of all components, e.g. it offers random access iff all ranges offer 4751 random access, and also offers mutation and swapping if all ranges offer 4752 it. Due to this, `Zip` is extremely powerful because it allows manipulating 4753 several ranges in lockstep. 4754 Throws: 4755 An `Exception` if all of the ranges are not the same length and 4756 `sp` is set to `StoppingPolicy.requireSameLength`. 4757 4758 Limitations: The `@nogc` and `nothrow` attributes cannot be inferred for 4759 the `Zip` struct because $(LREF StoppingPolicy) can vary at runtime. This 4760 limitation is not shared by the anonymous range returned by the `zip` 4761 function when not given an explicit `StoppingPolicy` as an argument. 4762 */ 4763 struct Zip(Ranges...) 4764 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 4765 { 4766 import std.format : format; //for generic mixins 4767 import std.typecons : Tuple; 4768 4769 alias R = Ranges; 4770 private R ranges; 4771 alias ElementType = Tuple!(staticMap!(.ElementType, R)); 4772 private StoppingPolicy stoppingPolicy = StoppingPolicy.shortest; 4773 4774 /** 4775 Builds an object. Usually this is invoked indirectly by using the 4776 $(LREF zip) function. 4777 */ 4778 this(R rs, StoppingPolicy s = StoppingPolicy.shortest) 4779 { 4780 ranges[] = rs[]; 4781 stoppingPolicy = s; 4782 } 4783 4784 /** 4785 Returns `true` if the range is at end. The test depends on the 4786 stopping policy. 4787 */ 4788 static if (allSatisfy!(isInfinite, R)) 4789 { 4790 // BUG: Doesn't propagate infiniteness if only some ranges are infinite 4791 // and s == StoppingPolicy.longest. This isn't fixable in the 4792 // current design since StoppingPolicy is known only at runtime. 4793 enum bool empty = false; 4794 } 4795 else 4796 { 4797 /// 4798 @property bool empty() 4799 { 4800 import std.exception : enforce; 4801 import std.meta : anySatisfy; 4802 4803 final switch (stoppingPolicy) 4804 { 4805 case StoppingPolicy.shortest: 4806 foreach (i, Unused; R) 4807 { 4808 if (ranges[i].empty) return true; 4809 } 4810 return false; 4811 case StoppingPolicy.longest: 4812 static if (anySatisfy!(isInfinite, R)) 4813 { 4814 return false; 4815 } 4816 else 4817 { 4818 foreach (i, Unused; R) 4819 { 4820 if (!ranges[i].empty) return false; 4821 } 4822 return true; 4823 } 4824 case StoppingPolicy.requireSameLength: 4825 foreach (i, Unused; R[1 .. $]) 4826 { 4827 enforce(ranges[0].empty == 4828 ranges[i + 1].empty, 4829 "Inequal-length ranges passed to Zip"); 4830 } 4831 return ranges[0].empty; 4832 } 4833 assert(false); 4834 } 4835 } 4836 4837 static if (allSatisfy!(isForwardRange, R)) 4838 { 4839 /// 4840 @property Zip save() 4841 { 4842 //Zip(ranges[0].save, ranges[1].save, ..., stoppingPolicy) 4843 return mixin (q{Zip(%(ranges[%s].save%|, %), stoppingPolicy)}.format(iota(0, R.length))); 4844 } 4845 } 4846 4847 private .ElementType!(R[i]) tryGetInit(size_t i)() 4848 { 4849 alias E = .ElementType!(R[i]); 4850 static if (!is(typeof({static E i;}))) 4851 throw new Exception("Range with non-default constructable elements exhausted."); 4852 else 4853 return E.init; 4854 } 4855 4856 /** 4857 Returns the current iterated element. 4858 */ 4859 @property ElementType front() 4860 { 4861 @property tryGetFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].front;} 4862 //ElementType(tryGetFront!0, tryGetFront!1, ...) 4863 return mixin(q{ElementType(%(tryGetFront!%s, %))}.format(iota(0, R.length))); 4864 } 4865 4866 /** 4867 Sets the front of all iterated ranges. 4868 */ 4869 static if (allSatisfy!(hasAssignableElements, R)) 4870 { 4871 @property void front(ElementType v) 4872 { 4873 foreach (i, Unused; R) 4874 { 4875 if (!ranges[i].empty) 4876 { 4877 ranges[i].front = v[i]; 4878 } 4879 } 4880 } 4881 } 4882 4883 /** 4884 Moves out the front. 4885 */ 4886 static if (allSatisfy!(hasMobileElements, R)) 4887 { 4888 ElementType moveFront() 4889 { 4890 @property tryMoveFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveFront();} 4891 //ElementType(tryMoveFront!0, tryMoveFront!1, ...) 4892 return mixin(q{ElementType(%(tryMoveFront!%s, %))}.format(iota(0, R.length))); 4893 } 4894 } 4895 4896 /** 4897 Returns the rightmost element. 4898 */ 4899 static if (allSatisfy!(isBidirectionalRange, R)) 4900 { 4901 @property ElementType back() 4902 { 4903 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness 4904 4905 @property tryGetBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].back;} 4906 //ElementType(tryGetBack!0, tryGetBack!1, ...) 4907 return mixin(q{ElementType(%(tryGetBack!%s, %))}.format(iota(0, R.length))); 4908 } 4909 4910 /** 4911 Moves out the back. 4912 */ 4913 static if (allSatisfy!(hasMobileElements, R)) 4914 { 4915 ElementType moveBack() 4916 { 4917 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness 4918 4919 @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveBack();} 4920 //ElementType(tryMoveBack!0, tryMoveBack!1, ...) 4921 return mixin(q{ElementType(%(tryMoveBack!%s, %))}.format(iota(0, R.length))); 4922 } 4923 } 4924 4925 /** 4926 Returns the current iterated element. 4927 */ 4928 static if (allSatisfy!(hasAssignableElements, R)) 4929 { 4930 @property void back(ElementType v) 4931 { 4932 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness. 4933 //Not sure the call is even legal for StoppingPolicy.longest 4934 4935 foreach (i, Unused; R) 4936 { 4937 if (!ranges[i].empty) 4938 { 4939 ranges[i].back = v[i]; 4940 } 4941 } 4942 } 4943 } 4944 } 4945 4946 /** 4947 Advances to the next element in all controlled ranges. 4948 */ 4949 void popFront() 4950 { 4951 import std.exception : enforce; 4952 4953 final switch (stoppingPolicy) 4954 { 4955 case StoppingPolicy.shortest: 4956 foreach (i, Unused; R) 4957 { 4958 assert(!ranges[i].empty); 4959 ranges[i].popFront(); 4960 } 4961 break; 4962 case StoppingPolicy.longest: 4963 foreach (i, Unused; R) 4964 { 4965 if (!ranges[i].empty) ranges[i].popFront(); 4966 } 4967 break; 4968 case StoppingPolicy.requireSameLength: 4969 foreach (i, Unused; R) 4970 { 4971 enforce(!ranges[i].empty, "Invalid Zip object"); 4972 ranges[i].popFront(); 4973 } 4974 break; 4975 } 4976 } 4977 4978 /** 4979 Calls `popBack` for all controlled ranges. 4980 */ 4981 static if (allSatisfy!(isBidirectionalRange, R)) 4982 { 4983 void popBack() 4984 { 4985 //TODO: Fixme! In case of jaggedness, this is wrong. 4986 import std.exception : enforce; 4987 4988 final switch (stoppingPolicy) 4989 { 4990 case StoppingPolicy.shortest: 4991 foreach (i, Unused; R) 4992 { 4993 assert(!ranges[i].empty); 4994 ranges[i].popBack(); 4995 } 4996 break; 4997 case StoppingPolicy.longest: 4998 foreach (i, Unused; R) 4999 { 5000 if (!ranges[i].empty) ranges[i].popBack(); 5001 } 5002 break; 5003 case StoppingPolicy.requireSameLength: 5004 foreach (i, Unused; R) 5005 { 5006 enforce(!ranges[i].empty, "Invalid Zip object"); 5007 ranges[i].popBack(); 5008 } 5009 break; 5010 } 5011 } 5012 } 5013 5014 /** 5015 Returns the length of this range. Defined only if all ranges define 5016 `length`. 5017 */ 5018 static if (allSatisfy!(hasLength, R)) 5019 { 5020 @property auto length() 5021 { 5022 static if (Ranges.length == 1) 5023 return ranges[0].length; 5024 else 5025 { 5026 if (stoppingPolicy == StoppingPolicy.requireSameLength) 5027 return ranges[0].length; 5028 5029 //[min|max](ranges[0].length, ranges[1].length, ...) 5030 import std.algorithm.comparison : min, max; 5031 if (stoppingPolicy == StoppingPolicy.shortest) 5032 return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); 5033 else 5034 return mixin(q{max(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); 5035 } 5036 } 5037 5038 alias opDollar = length; 5039 } 5040 5041 /** 5042 Returns a slice of the range. Defined only if all range define 5043 slicing. 5044 */ 5045 static if (allSatisfy!(hasSlicing, R)) 5046 { 5047 auto opSlice(size_t from, size_t to) 5048 { 5049 //Slicing an infinite range yields the type Take!R 5050 //For finite ranges, the type Take!R aliases to R 5051 alias ZipResult = Zip!(staticMap!(Take, R)); 5052 5053 //ZipResult(ranges[0][from .. to], ranges[1][from .. to], ..., stoppingPolicy) 5054 return mixin (q{ZipResult(%(ranges[%s][from .. to]%|, %), stoppingPolicy)}.format(iota(0, R.length))); 5055 } 5056 } 5057 5058 /** 5059 Returns the `n`th element in the composite range. Defined if all 5060 ranges offer random access. 5061 */ 5062 static if (allSatisfy!(isRandomAccessRange, R)) 5063 { 5064 ElementType opIndex(size_t n) 5065 { 5066 //TODO: Fixme! This may create an out of bounds access 5067 //for StoppingPolicy.longest 5068 5069 //ElementType(ranges[0][n], ranges[1][n], ...) 5070 return mixin (q{ElementType(%(ranges[%s][n]%|, %))}.format(iota(0, R.length))); 5071 } 5072 5073 /** 5074 Assigns to the `n`th element in the composite range. Defined if 5075 all ranges offer random access. 5076 */ 5077 static if (allSatisfy!(hasAssignableElements, R)) 5078 { 5079 void opIndexAssign(ElementType v, size_t n) 5080 { 5081 //TODO: Fixme! Not sure the call is even legal for StoppingPolicy.longest 5082 foreach (i, Range; R) 5083 { 5084 ranges[i][n] = v[i]; 5085 } 5086 } 5087 } 5088 5089 /** 5090 Destructively reads the `n`th element in the composite 5091 range. Defined if all ranges offer random access. 5092 */ 5093 static if (allSatisfy!(hasMobileElements, R)) 5094 { 5095 ElementType moveAt(size_t n) 5096 { 5097 //TODO: Fixme! This may create an out of bounds access 5098 //for StoppingPolicy.longest 5099 5100 //ElementType(ranges[0].moveAt(n), ranges[1].moveAt(n), ..., ) 5101 return mixin (q{ElementType(%(ranges[%s].moveAt(n)%|, %))}.format(iota(0, R.length))); 5102 } 5103 } 5104 } 5105 } 5106 5107 /// Ditto 5108 auto zip(Ranges...)(Ranges ranges) 5109 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 5110 { 5111 import std.meta : anySatisfy, templateOr; 5112 static if (allSatisfy!(isInfinite, Ranges) || Ranges.length == 1) 5113 { 5114 return ZipShortest!(Ranges)(ranges); 5115 } 5116 else static if (allSatisfy!(isBidirectionalRange, Ranges)) 5117 { 5118 static if (allSatisfy!(templateOr!(isInfinite, hasLength), Ranges) 5119 && allSatisfy!(templateOr!(isInfinite, hasSlicing), Ranges) 5120 && allSatisfy!(isBidirectionalRange, staticMap!(Take, Ranges))) 5121 { 5122 // If all the ranges are bidirectional, if possible slice them to 5123 // the same length to simplify the implementation. 5124 static assert(anySatisfy!(hasLength, Ranges)); 5125 static foreach (i, Range; Ranges) 5126 static if (hasLength!Range) 5127 { 5128 static if (!is(typeof(minLen) == size_t)) 5129 size_t minLen = ranges[i].length; 5130 else 5131 {{ 5132 const x = ranges[i].length; 5133 if (x < minLen) minLen = x; 5134 }} 5135 } 5136 import std.format : format; 5137 static if (!anySatisfy!(isInfinite, Ranges)) 5138 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~ 5139 `(%(ranges[%s][0 .. minLen]%|, %))`.format(iota(0, Ranges.length))); 5140 else 5141 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~ 5142 `(%(take(ranges[%s], minLen)%|, %))`.format(iota(0, Ranges.length))); 5143 } 5144 else static if (allSatisfy!(isRandomAccessRange, Ranges)) 5145 { 5146 // We can't slice but we can still use random access to ensure 5147 // "back" is retrieving the same index for each range. 5148 return ZipShortest!(Ranges)(ranges); 5149 } 5150 else 5151 { 5152 // If bidirectional range operations would not be supported by 5153 // ZipShortest that might have actually been a bug since Zip 5154 // supported `back` without verifying that each range had the 5155 // same length, but for the sake of backwards compatibility 5156 // use the old Zip to continue supporting them. 5157 return Zip!Ranges(ranges); 5158 } 5159 } 5160 else 5161 { 5162 return ZipShortest!(Ranges)(ranges); 5163 } 5164 } 5165 5166 /// 5167 @nogc nothrow pure @safe unittest 5168 { 5169 import std.algorithm.comparison : equal; 5170 import std.algorithm.iteration : map; 5171 5172 // pairwise sum 5173 auto arr = only(0, 1, 2); 5174 auto part1 = zip(arr, arr.dropOne).map!"a[0] + a[1]"; 5175 assert(part1.equal(only(1, 3))); 5176 } 5177 5178 /// 5179 nothrow pure @safe unittest 5180 { 5181 import std.conv : to; 5182 5183 int[] a = [ 1, 2, 3 ]; 5184 string[] b = [ "a", "b", "c" ]; 5185 string[] result; 5186 5187 foreach (tup; zip(a, b)) 5188 { 5189 result ~= tup[0].to!string ~ tup[1]; 5190 } 5191 5192 assert(result == [ "1a", "2b", "3c" ]); 5193 5194 size_t idx = 0; 5195 // unpacking tuple elements with foreach 5196 foreach (e1, e2; zip(a, b)) 5197 { 5198 assert(e1 == a[idx]); 5199 assert(e2 == b[idx]); 5200 ++idx; 5201 } 5202 } 5203 5204 /// `zip` is powerful - the following code sorts two arrays in parallel: 5205 nothrow pure @safe unittest 5206 { 5207 import std.algorithm.sorting : sort; 5208 5209 int[] a = [ 1, 2, 3 ]; 5210 string[] b = [ "a", "c", "b" ]; 5211 zip(a, b).sort!((t1, t2) => t1[0] > t2[0]); 5212 5213 assert(a == [ 3, 2, 1 ]); 5214 // b is sorted according to a's sorting 5215 assert(b == [ "b", "c", "a" ]); 5216 } 5217 5218 /// Ditto 5219 auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges) 5220 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 5221 { 5222 return Zip!Ranges(ranges, sp); 5223 } 5224 5225 /** 5226 Dictates how iteration in a $(LREF zip) and $(LREF lockstep) should stop. 5227 By default stop at the end of the shortest of all ranges. 5228 */ 5229 enum StoppingPolicy 5230 { 5231 /// Stop when the shortest range is exhausted 5232 shortest, 5233 /// Stop when the longest range is exhausted 5234 longest, 5235 /// Require that all ranges are equal 5236 requireSameLength, 5237 } 5238 5239 /// 5240 pure @safe unittest 5241 { 5242 import std.algorithm.comparison : equal; 5243 import std.exception : assertThrown; 5244 import std.range.primitives; 5245 import std.typecons : tuple; 5246 5247 auto a = [1, 2, 3]; 5248 auto b = [4, 5, 6, 7]; 5249 5250 auto shortest = zip(StoppingPolicy.shortest, a, b); 5251 assert(shortest.equal([ 5252 tuple(1, 4), 5253 tuple(2, 5), 5254 tuple(3, 6) 5255 ])); 5256 5257 auto longest = zip(StoppingPolicy.longest, a, b); 5258 assert(longest.equal([ 5259 tuple(1, 4), 5260 tuple(2, 5), 5261 tuple(3, 6), 5262 tuple(0, 7) 5263 ])); 5264 5265 auto same = zip(StoppingPolicy.requireSameLength, a, b); 5266 same.popFrontN(3); 5267 assertThrown!Exception(same.popFront); 5268 } 5269 5270 /+ 5271 Non-public. Like $(LREF Zip) with `StoppingPolicy.shortest` 5272 except it properly implements `back` and `popBack` in the 5273 case of uneven ranges or disables those operations when 5274 it is not possible to guarantee they are correct. 5275 +/ 5276 package template ZipShortest(Ranges...) 5277 if (Ranges.length && __traits(compiles, 5278 { 5279 static assert(allSatisfy!(isInputRange, Ranges)); 5280 })) 5281 { 5282 alias ZipShortest = .ZipShortest!( 5283 Ranges.length == 1 || allSatisfy!(isInfinite, Ranges) 5284 ? Yes.allKnownSameLength 5285 : No.allKnownSameLength, 5286 Ranges); 5287 } 5288 /+ non-public, ditto +/ 5289 package struct ZipShortest(Flag!"allKnownSameLength" allKnownSameLength, Ranges...) 5290 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 5291 { 5292 import std.format : format; //for generic mixins 5293 import std.meta : anySatisfy, templateOr; 5294 import std.typecons : Tuple; 5295 5296 deprecated("Use of an undocumented alias R.") 5297 alias R = Ranges; // Unused here but defined in case library users rely on it. 5298 private Ranges ranges; 5299 alias ElementType = Tuple!(staticMap!(.ElementType, Ranges)); 5300 5301 /+ 5302 Builds an object. Usually this is invoked indirectly by using the 5303 $(LREF zip) function. 5304 +/ 5305 this(Ranges rs) 5306 { 5307 ranges[] = rs[]; 5308 } 5309 5310 /+ 5311 Returns `true` if the range is at end. 5312 +/ 5313 static if (allKnownSameLength ? anySatisfy!(isInfinite, Ranges) 5314 : allSatisfy!(isInfinite, Ranges)) 5315 { 5316 enum bool empty = false; 5317 } 5318 else 5319 { 5320 @property bool empty() 5321 { 5322 static if (allKnownSameLength) 5323 { 5324 return ranges[0].empty; 5325 } 5326 else 5327 { 5328 static foreach (i; 0 .. Ranges.length) 5329 { 5330 if (ranges[i].empty) 5331 return true; 5332 } 5333 return false; 5334 } 5335 } 5336 } 5337 5338 /+ 5339 Forward range primitive. Only present if each constituent range is a 5340 forward range. 5341 +/ 5342 static if (allSatisfy!(isForwardRange, Ranges)) 5343 @property typeof(this) save() 5344 { 5345 return mixin(`typeof(return)(%(ranges[%s].save%|, %))`.format(iota(0, Ranges.length))); 5346 } 5347 5348 /+ 5349 Returns the current iterated element. 5350 +/ 5351 @property ElementType front() 5352 { 5353 return mixin(`typeof(return)(%(ranges[%s].front%|, %))`.format(iota(0, Ranges.length))); 5354 } 5355 5356 /+ 5357 Sets the front of all iterated ranges. Only present if each constituent 5358 range has assignable elements. 5359 +/ 5360 static if (allSatisfy!(hasAssignableElements, Ranges)) 5361 @property void front()(ElementType v) 5362 { 5363 static foreach (i; 0 .. Ranges.length) 5364 ranges[i].front = v[i]; 5365 } 5366 5367 /+ 5368 Moves out the front. Present if each constituent range has mobile elements. 5369 +/ 5370 static if (allSatisfy!(hasMobileElements, Ranges)) 5371 ElementType moveFront()() 5372 { 5373 return mixin(`typeof(return)(%(ranges[%s].moveFront()%|, %))`.format(iota(0, Ranges.length))); 5374 } 5375 5376 private enum bool isBackWellDefined = allSatisfy!(isBidirectionalRange, Ranges) 5377 && (allKnownSameLength 5378 || allSatisfy!(isRandomAccessRange, Ranges) 5379 // Could also add the case where there is one non-infinite bidirectional 5380 // range that defines `length` and all others are infinite random access 5381 // ranges. Adding this would require appropriate branches in 5382 // back/moveBack/popBack. 5383 ); 5384 5385 /+ 5386 Returns the rightmost element. Present if all constituent ranges are 5387 bidirectional and either there is a compile-time guarantee that all 5388 ranges have the same length (in `allKnownSameLength`) or all ranges 5389 provide random access to elements. 5390 +/ 5391 static if (isBackWellDefined) 5392 @property ElementType back() 5393 { 5394 static if (allKnownSameLength) 5395 { 5396 return mixin(`typeof(return)(%(ranges[%s].back()%|, %))`.format(iota(0, Ranges.length))); 5397 } 5398 else 5399 { 5400 const backIndex = length - 1; 5401 return mixin(`typeof(return)(%(ranges[%s][backIndex]%|, %))`.format(iota(0, Ranges.length))); 5402 } 5403 } 5404 5405 /+ 5406 Moves out the back. Present if `back` is defined and 5407 each constituent range has mobile elements. 5408 +/ 5409 static if (isBackWellDefined && allSatisfy!(hasMobileElements, Ranges)) 5410 ElementType moveBack()() 5411 { 5412 static if (allKnownSameLength) 5413 { 5414 return mixin(`typeof(return)(%(ranges[%s].moveBack()%|, %))`.format(iota(0, Ranges.length))); 5415 } 5416 else 5417 { 5418 const backIndex = length - 1; 5419 return mixin(`typeof(return)(%(ranges[%s].moveAt(backIndex)%|, %))`.format(iota(0, Ranges.length))); 5420 } 5421 } 5422 5423 /+ 5424 Sets the rightmost element. Only present if `back` is defined and 5425 each constituent range has assignable elements. 5426 +/ 5427 static if (isBackWellDefined && allSatisfy!(hasAssignableElements, Ranges)) 5428 @property void back()(ElementType v) 5429 { 5430 static if (allKnownSameLength) 5431 { 5432 static foreach (i; 0 .. Ranges.length) 5433 ranges[i].back = v[i]; 5434 } 5435 else 5436 { 5437 const backIndex = length - 1; 5438 static foreach (i; 0 .. Ranges.length) 5439 ranges[i][backIndex] = v[i]; 5440 } 5441 } 5442 5443 /+ 5444 Calls `popFront` on each constituent range. 5445 +/ 5446 void popFront() 5447 { 5448 static foreach (i; 0 .. Ranges.length) 5449 ranges[i].popFront(); 5450 } 5451 5452 /+ 5453 Pops the rightmost element. Present if `back` is defined. 5454 +/ 5455 static if (isBackWellDefined) 5456 void popBack() 5457 { 5458 static if (allKnownSameLength) 5459 { 5460 static foreach (i; 0 .. Ranges.length) 5461 ranges[i].popBack; 5462 } 5463 else 5464 { 5465 const len = length; 5466 static foreach (i; 0 .. Ranges.length) 5467 static if (!isInfinite!(Ranges[i])) 5468 if (ranges[i].length == len) 5469 ranges[i].popBack(); 5470 } 5471 } 5472 5473 /+ 5474 Returns the length of this range. Defined if at least one 5475 constituent range defines `length` and the other ranges all also 5476 define `length` or are infinite, or if at least one constituent 5477 range defines `length` and there is a compile-time guarantee that 5478 all ranges have the same length (in `allKnownSameLength`). 5479 +/ 5480 static if (allKnownSameLength 5481 ? anySatisfy!(hasLength, Ranges) 5482 : (anySatisfy!(hasLength, Ranges) 5483 && allSatisfy!(templateOr!(isInfinite, hasLength), Ranges))) 5484 { 5485 @property size_t length() 5486 { 5487 static foreach (i, Range; Ranges) 5488 { 5489 static if (hasLength!Range) 5490 { 5491 static if (!is(typeof(minLen) == size_t)) 5492 size_t minLen = ranges[i].length; 5493 else static if (!allKnownSameLength) 5494 {{ 5495 const x = ranges[i].length; 5496 if (x < minLen) minLen = x; 5497 }} 5498 } 5499 } 5500 return minLen; 5501 } 5502 5503 alias opDollar = length; 5504 } 5505 5506 /+ 5507 Returns a slice of the range. Defined if all constituent ranges 5508 support slicing. 5509 +/ 5510 static if (allSatisfy!(hasSlicing, Ranges)) 5511 { 5512 // Note: we will know that all elements of the resultant range 5513 // will have the same length but we cannot change `allKnownSameLength` 5514 // because the `hasSlicing` predicate tests that the result returned 5515 // by `opSlice` has the same type as the receiver. 5516 auto opSlice()(size_t from, size_t to) 5517 { 5518 //(ranges[0][from .. to], ranges[1][from .. to], ...) 5519 enum sliceArgs = `(%(ranges[%s][from .. to]%|, %))`.format(iota(0, Ranges.length)); 5520 static if (__traits(compiles, mixin(`typeof(this)`~sliceArgs))) 5521 return mixin(`typeof(this)`~sliceArgs); 5522 else 5523 // The type is different anyway so we might as well 5524 // explicitly set allKnownSameLength. 5525 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))` 5526 ~sliceArgs); 5527 } 5528 } 5529 5530 /+ 5531 Returns the `n`th element in the composite range. Defined if all 5532 constituent ranges offer random access. 5533 +/ 5534 static if (allSatisfy!(isRandomAccessRange, Ranges)) 5535 ElementType opIndex()(size_t n) 5536 { 5537 return mixin(`typeof(return)(%(ranges[%s][n]%|, %))`.format(iota(0, Ranges.length))); 5538 } 5539 5540 /+ 5541 Sets the `n`th element in the composite range. Defined if all 5542 constituent ranges offer random access and have assignable elements. 5543 +/ 5544 static if (allSatisfy!(isRandomAccessRange, Ranges) 5545 && allSatisfy!(hasAssignableElements, Ranges)) 5546 void opIndexAssign()(ElementType v, size_t n) 5547 { 5548 static foreach (i; 0 .. Ranges.length) 5549 ranges[i][n] = v[i]; 5550 } 5551 5552 /+ 5553 Destructively reads the `n`th element in the composite 5554 range. Defined if all constituent ranges offer random 5555 access and have mobile elements. 5556 +/ 5557 static if (allSatisfy!(isRandomAccessRange, Ranges) 5558 && allSatisfy!(hasMobileElements, Ranges)) 5559 ElementType moveAt()(size_t n) 5560 { 5561 return mixin(`typeof(return)(%(ranges[%s].moveAt(n)%|, %))`.format(iota(0, Ranges.length))); 5562 } 5563 } 5564 5565 pure @system unittest 5566 { 5567 import std.algorithm.comparison : equal; 5568 import std.algorithm.iteration : filter, map; 5569 import std.algorithm.mutation : swap; 5570 import std.algorithm.sorting : sort; 5571 5572 import std.exception : assertThrown, assertNotThrown; 5573 import std.typecons : tuple; 5574 5575 int[] a = [ 1, 2, 3 ]; 5576 float[] b = [ 1.0, 2.0, 3.0 ]; 5577 foreach (e; zip(a, b)) 5578 { 5579 assert(e[0] == e[1]); 5580 } 5581 5582 swap(a[0], a[1]); 5583 { 5584 auto z = zip(a, b); 5585 } 5586 //swap(z.front(), z.back()); 5587 sort!("a[0] < b[0]")(zip(a, b)); 5588 assert(a == [1, 2, 3]); 5589 assert(b == [2.0, 1.0, 3.0]); 5590 5591 auto z = zip(StoppingPolicy.requireSameLength, a, b); 5592 assertNotThrown(z.popBack()); 5593 assertNotThrown(z.popBack()); 5594 assertNotThrown(z.popBack()); 5595 assert(z.empty); 5596 assertThrown(z.popBack()); 5597 5598 a = [ 1, 2, 3 ]; 5599 b = [ 1.0, 2.0, 3.0 ]; 5600 sort!("a[0] > b[0]")(zip(StoppingPolicy.requireSameLength, a, b)); 5601 assert(a == [3, 2, 1]); 5602 assert(b == [3.0, 2.0, 1.0]); 5603 5604 a = []; 5605 b = []; 5606 assert(zip(StoppingPolicy.requireSameLength, a, b).empty); 5607 5608 // Test infiniteness propagation. 5609 static assert(isInfinite!(typeof(zip(repeat(1), repeat(1))))); 5610 5611 // Test stopping policies with both value and reference. 5612 auto a1 = [1, 2]; 5613 auto a2 = [1, 2, 3]; 5614 auto stuff = tuple(tuple(a1, a2), 5615 tuple(filter!"a"(a1), filter!"a"(a2))); 5616 5617 alias FOO = Zip!(immutable(int)[], immutable(float)[]); 5618 5619 foreach (t; stuff.expand) 5620 { 5621 auto arr1 = t[0]; 5622 auto arr2 = t[1]; 5623 auto zShortest = zip(arr1, arr2); 5624 assert(equal(map!"a[0]"(zShortest), [1, 2])); 5625 assert(equal(map!"a[1]"(zShortest), [1, 2])); 5626 5627 try { 5628 auto zSame = zip(StoppingPolicy.requireSameLength, arr1, arr2); 5629 foreach (elem; zSame) {} 5630 assert(0); 5631 } catch (Throwable) { /* It's supposed to throw.*/ } 5632 5633 auto zLongest = zip(StoppingPolicy.longest, arr1, arr2); 5634 assert(!zLongest.ranges[0].empty); 5635 assert(!zLongest.ranges[1].empty); 5636 5637 zLongest.popFront(); 5638 zLongest.popFront(); 5639 assert(!zLongest.empty); 5640 assert(zLongest.ranges[0].empty); 5641 assert(!zLongest.ranges[1].empty); 5642 5643 zLongest.popFront(); 5644 assert(zLongest.empty); 5645 } 5646 5647 // https://issues.dlang.org/show_bug.cgi?id=8900 5648 assert(zip([1, 2], repeat('a')).array == [tuple(1, 'a'), tuple(2, 'a')]); 5649 assert(zip(repeat('a'), [1, 2]).array == [tuple('a', 1), tuple('a', 2)]); 5650 5651 // https://issues.dlang.org/show_bug.cgi?id=18524 5652 // moveBack instead performs moveFront 5653 { 5654 auto r = zip([1,2,3]); 5655 assert(r.moveBack()[0] == 3); 5656 assert(r.moveFront()[0] == 1); 5657 } 5658 5659 // Doesn't work yet. Issues w/ emplace. 5660 // static assert(is(Zip!(immutable int[], immutable float[]))); 5661 5662 5663 // These unittests pass, but make the compiler consume an absurd amount 5664 // of RAM and time. Therefore, they should only be run if explicitly 5665 // uncommented when making changes to Zip. Also, running them using 5666 // make -fwin32.mak unittest makes the compiler completely run out of RAM. 5667 // You need to test just this module. 5668 /+ 5669 foreach (DummyType1; AllDummyRanges) 5670 { 5671 DummyType1 d1; 5672 foreach (DummyType2; AllDummyRanges) 5673 { 5674 DummyType2 d2; 5675 auto r = zip(d1, d2); 5676 assert(equal(map!"a[0]"(r), [1,2,3,4,5,6,7,8,9,10])); 5677 assert(equal(map!"a[1]"(r), [1,2,3,4,5,6,7,8,9,10])); 5678 5679 static if (isForwardRange!DummyType1 && isForwardRange!DummyType2) 5680 { 5681 static assert(isForwardRange!(typeof(r))); 5682 } 5683 5684 static if (isBidirectionalRange!DummyType1 && 5685 isBidirectionalRange!DummyType2) { 5686 static assert(isBidirectionalRange!(typeof(r))); 5687 } 5688 static if (isRandomAccessRange!DummyType1 && 5689 isRandomAccessRange!DummyType2) { 5690 static assert(isRandomAccessRange!(typeof(r))); 5691 } 5692 } 5693 } 5694 +/ 5695 } 5696 5697 nothrow pure @safe unittest 5698 { 5699 import std.algorithm.sorting : sort; 5700 5701 auto a = [5,4,3,2,1]; 5702 auto b = [3,1,2,5,6]; 5703 auto z = zip(a, b); 5704 5705 sort!"a[0] < b[0]"(z); 5706 5707 assert(a == [1, 2, 3, 4, 5]); 5708 assert(b == [6, 5, 2, 1, 3]); 5709 } 5710 5711 nothrow pure @safe unittest 5712 { 5713 import std.algorithm.comparison : equal; 5714 import std.typecons : tuple; 5715 5716 auto LL = iota(1L, 1000L); 5717 auto z = zip(LL, [4]); 5718 5719 assert(equal(z, [tuple(1L,4)])); 5720 5721 auto LL2 = iota(0L, 500L); 5722 auto z2 = zip([7], LL2); 5723 assert(equal(z2, [tuple(7, 0L)])); 5724 } 5725 5726 // Test for https://issues.dlang.org/show_bug.cgi?id=11196 5727 @safe pure unittest 5728 { 5729 import std.exception : assertThrown; 5730 5731 static struct S { @disable this(); } 5732 assert(zip((S[5]).init[]).length == 5); 5733 assert(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).length == 1); 5734 assertThrown(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).front); 5735 } 5736 5737 // https://issues.dlang.org/show_bug.cgi?id=12007 5738 @nogc nothrow @safe pure unittest 5739 { 5740 static struct R 5741 { 5742 enum empty = false; 5743 void popFront(){} 5744 int front(){return 1;} @property 5745 R save(){return this;} @property 5746 void opAssign(R) @disable; 5747 } 5748 R r; 5749 auto z = zip(r, r); 5750 assert(z.save == z); 5751 } 5752 5753 nothrow pure @system unittest 5754 { 5755 import std.typecons : tuple; 5756 5757 auto r1 = [0,1,2]; 5758 auto r2 = [1,2,3]; 5759 auto z1 = zip(refRange(&r1), refRange(&r2)); 5760 auto z2 = z1.save; 5761 z1.popFront(); 5762 assert(z1.front == tuple(1,2)); 5763 assert(z2.front == tuple(0,1)); 5764 } 5765 5766 @nogc nothrow pure @safe unittest 5767 { 5768 // Test zip's `back` and `length` with non-equal ranges. 5769 static struct NonSliceableRandomAccess 5770 { 5771 private int[] a; 5772 @property ref front() 5773 { 5774 return a.front; 5775 } 5776 @property ref back() 5777 { 5778 return a.back; 5779 } 5780 ref opIndex(size_t i) 5781 { 5782 return a[i]; 5783 } 5784 void popFront() 5785 { 5786 a.popFront(); 5787 } 5788 void popBack() 5789 { 5790 a.popBack(); 5791 } 5792 auto moveFront() 5793 { 5794 return a.moveFront(); 5795 } 5796 auto moveBack() 5797 { 5798 return a.moveBack(); 5799 } 5800 auto moveAt(size_t i) 5801 { 5802 return a.moveAt(i); 5803 } 5804 bool empty() const 5805 { 5806 return a.empty; 5807 } 5808 size_t length() const 5809 { 5810 return a.length; 5811 } 5812 typeof(this) save() 5813 { 5814 return this; 5815 } 5816 } 5817 static assert(isRandomAccessRange!NonSliceableRandomAccess); 5818 static assert(!hasSlicing!NonSliceableRandomAccess); 5819 static foreach (iteration; 0 .. 2) 5820 {{ 5821 int[5] data = [101, 102, 103, 201, 202]; 5822 static if (iteration == 0) 5823 { 5824 auto r1 = NonSliceableRandomAccess(data[0 .. 3]); 5825 auto r2 = NonSliceableRandomAccess(data[3 .. 5]); 5826 } 5827 else 5828 { 5829 auto r1 = data[0 .. 3]; 5830 auto r2 = data[3 .. 5]; 5831 } 5832 auto z = zip(r1, r2); 5833 static assert(isRandomAccessRange!(typeof(z))); 5834 assert(z.length == 2); 5835 assert(z.back[0] == 102 && z.back[1] == 202); 5836 z.back = typeof(z.back)(-102, -202);// Assign to back. 5837 assert(z.back[0] == -102 && z.back[1] == -202); 5838 z.popBack(); 5839 assert(z.length == 1); 5840 assert(z.back[0] == 101 && z.back[1] == 201); 5841 z.front = typeof(z.front)(-101, -201); 5842 assert(z.moveBack() == typeof(z.back)(-101, -201)); 5843 z.popBack(); 5844 assert(z.empty); 5845 }} 5846 } 5847 5848 @nogc nothrow pure @safe unittest 5849 { 5850 // Test opSlice on infinite `zip`. 5851 auto z = zip(repeat(1), repeat(2)); 5852 assert(hasSlicing!(typeof(z))); 5853 auto slice = z[10 .. 20]; 5854 assert(slice.length == 10); 5855 static assert(!is(typeof(z) == typeof(slice))); 5856 } 5857 5858 /* 5859 Generate lockstep's opApply function as a mixin string. 5860 If withIndex is true prepend a size_t index to the delegate. 5861 */ 5862 private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) 5863 { 5864 import std.format : format; 5865 5866 string[] params; 5867 string[] emptyChecks; 5868 string[] dgArgs; 5869 string[] popFronts; 5870 string indexDef; 5871 string indexInc; 5872 5873 if (withIndex) 5874 { 5875 params ~= "size_t"; 5876 dgArgs ~= "index"; 5877 if (reverse) 5878 { 5879 indexDef = q{ 5880 size_t index = ranges[0].length-1; 5881 enforce(_stoppingPolicy == StoppingPolicy.requireSameLength, 5882 "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); 5883 5884 foreach (range; ranges[1..$]) 5885 enforce(range.length == ranges[0].length); 5886 }; 5887 indexInc = "--index;"; 5888 } 5889 else 5890 { 5891 indexDef = "size_t index = 0;"; 5892 indexInc = "++index;"; 5893 } 5894 } 5895 5896 foreach (idx, Range; Ranges) 5897 { 5898 params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); 5899 emptyChecks ~= format("!ranges[%s].empty", idx); 5900 if (reverse) 5901 { 5902 dgArgs ~= format("ranges[%s].back", idx); 5903 popFronts ~= format("ranges[%s].popBack();", idx); 5904 } 5905 else 5906 { 5907 dgArgs ~= format("ranges[%s].front", idx); 5908 popFronts ~= format("ranges[%s].popFront();", idx); 5909 } 5910 } 5911 5912 string name = reverse ? "opApplyReverse" : "opApply"; 5913 5914 return format( 5915 q{ 5916 int %s(scope int delegate(%s) dg) 5917 { 5918 import std.exception : enforce; 5919 5920 auto ranges = _ranges; 5921 int res; 5922 %s 5923 5924 while (%s) 5925 { 5926 res = dg(%s); 5927 if (res) break; 5928 %s 5929 %s 5930 } 5931 5932 if (_stoppingPolicy == StoppingPolicy.requireSameLength) 5933 { 5934 foreach (range; ranges) 5935 enforce(range.empty); 5936 } 5937 return res; 5938 } 5939 }, name, params.join(", "), indexDef, 5940 emptyChecks.join(" && "), dgArgs.join(", "), 5941 popFronts.join("\n "), 5942 indexInc); 5943 } 5944 5945 /** 5946 Iterate multiple ranges in lockstep using a `foreach` loop. In contrast to 5947 $(LREF zip) it allows reference access to its elements. If only a single 5948 range is passed in, the `Lockstep` aliases itself away. If the 5949 ranges are of different lengths and `s` == `StoppingPolicy.shortest` 5950 stop after the shortest range is empty. If the ranges are of different 5951 lengths and `s` == `StoppingPolicy.requireSameLength`, throw an 5952 exception. `s` may not be `StoppingPolicy.longest`, and passing this 5953 will throw an exception. 5954 5955 Iterating over `Lockstep` in reverse and with an index is only possible 5956 when `s` == `StoppingPolicy.requireSameLength`, in order to preserve 5957 indexes. If an attempt is made at iterating in reverse when `s` == 5958 `StoppingPolicy.shortest`, an exception will be thrown. 5959 5960 By default `StoppingPolicy` is set to `StoppingPolicy.shortest`. 5961 5962 Limitations: The `pure`, `@safe`, `@nogc`, or `nothrow` attributes cannot be 5963 inferred for `lockstep` iteration. $(LREF zip) can infer the first two due to 5964 a different implementation. 5965 5966 See_Also: $(LREF zip) 5967 5968 `lockstep` is similar to $(LREF zip), but `zip` bundles its 5969 elements and returns a range. 5970 `lockstep` also supports reference access. 5971 Use `zip` if you want to pass the result to a range function. 5972 */ 5973 struct Lockstep(Ranges...) 5974 if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges)) 5975 { 5976 /// 5977 this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest) 5978 { 5979 import std.exception : enforce; 5980 5981 _ranges = ranges; 5982 enforce(sp != StoppingPolicy.longest, 5983 "Can't use StoppingPolicy.Longest on Lockstep."); 5984 _stoppingPolicy = sp; 5985 } 5986 5987 mixin(lockstepMixin!Ranges(false, false)); 5988 mixin(lockstepMixin!Ranges(true, false)); 5989 static if (allSatisfy!(isBidirectionalRange, Ranges)) 5990 { 5991 mixin(lockstepMixin!Ranges(false, true)); 5992 static if (allSatisfy!(hasLength, Ranges)) 5993 { 5994 mixin(lockstepMixin!Ranges(true, true)); 5995 } 5996 else 5997 { 5998 mixin(lockstepReverseFailMixin!Ranges(true)); 5999 } 6000 } 6001 else 6002 { 6003 mixin(lockstepReverseFailMixin!Ranges(false)); 6004 mixin(lockstepReverseFailMixin!Ranges(true)); 6005 } 6006 6007 private: 6008 alias R = Ranges; 6009 R _ranges; 6010 StoppingPolicy _stoppingPolicy; 6011 } 6012 6013 /// Ditto 6014 Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges) 6015 if (allSatisfy!(isInputRange, Ranges)) 6016 { 6017 return Lockstep!(Ranges)(ranges); 6018 } 6019 /// Ditto 6020 Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) 6021 if (allSatisfy!(isInputRange, Ranges)) 6022 { 6023 static if (Ranges.length > 1) 6024 return Lockstep!Ranges(ranges, s); 6025 else 6026 return ranges[0]; 6027 } 6028 6029 /// 6030 @system unittest 6031 { 6032 auto arr1 = [1,2,3,4,5,100]; 6033 auto arr2 = [6,7,8,9,10]; 6034 6035 foreach (ref a, b; lockstep(arr1, arr2)) 6036 { 6037 a += b; 6038 } 6039 6040 assert(arr1 == [7,9,11,13,15,100]); 6041 6042 /// Lockstep also supports iterating with an index variable: 6043 foreach (index, a, b; lockstep(arr1, arr2)) 6044 { 6045 assert(arr1[index] == a); 6046 assert(arr2[index] == b); 6047 } 6048 } 6049 6050 // https://issues.dlang.org/show_bug.cgi?id=15860: foreach_reverse on lockstep 6051 @system unittest 6052 { 6053 auto arr1 = [0, 1, 2, 3]; 6054 auto arr2 = [4, 5, 6, 7]; 6055 6056 size_t n = arr1.length -1; 6057 foreach_reverse (index, a, b; lockstep(arr1, arr2, StoppingPolicy.requireSameLength)) 6058 { 6059 assert(n == index); 6060 assert(index == a); 6061 assert(arr1[index] == a); 6062 assert(arr2[index] == b); 6063 n--; 6064 } 6065 6066 auto arr3 = [4, 5]; 6067 n = 1; 6068 foreach_reverse (a, b; lockstep(arr1, arr3)) 6069 { 6070 assert(a == arr1[$-n] && b == arr3[$-n]); 6071 n++; 6072 } 6073 } 6074 6075 @system unittest 6076 { 6077 import std.algorithm.iteration : filter; 6078 import std.conv : to; 6079 6080 // The filters are to make these the lowest common forward denominator ranges, 6081 // i.e. w/o ref return, random access, length, etc. 6082 auto foo = filter!"a"([1,2,3,4,5]); 6083 immutable bar = [6f,7f,8f,9f,10f].idup; 6084 auto l = lockstep(foo, bar); 6085 6086 // Should work twice. These are forward ranges with implicit save. 6087 foreach (i; 0 .. 2) 6088 { 6089 uint[] res1; 6090 float[] res2; 6091 6092 foreach (a, ref b; l) 6093 { 6094 res1 ~= a; 6095 res2 ~= b; 6096 } 6097 6098 assert(res1 == [1,2,3,4,5]); 6099 assert(res2 == [6,7,8,9,10]); 6100 assert(bar == [6f,7f,8f,9f,10f]); 6101 } 6102 6103 // Doc example. 6104 auto arr1 = [1,2,3,4,5]; 6105 auto arr2 = [6,7,8,9,10]; 6106 6107 foreach (ref a, ref b; lockstep(arr1, arr2)) 6108 { 6109 a += b; 6110 } 6111 6112 assert(arr1 == [7,9,11,13,15]); 6113 6114 // Make sure StoppingPolicy.requireSameLength doesn't throw. 6115 auto ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); 6116 6117 int k = 1; 6118 foreach (a, b; ls) 6119 { 6120 assert(a - b == k); 6121 ++k; 6122 } 6123 6124 // Make sure StoppingPolicy.requireSameLength throws. 6125 arr2.popBack(); 6126 ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); 6127 6128 try { 6129 foreach (a, b; ls) {} 6130 assert(0); 6131 } catch (Exception) {} 6132 6133 // Just make sure 1-range case instantiates. This hangs the compiler 6134 // when no explicit stopping policy is specified due to 6135 // https://issues.dlang.org/show_bug.cgi?id=4652 6136 auto stuff = lockstep([1,2,3,4,5], StoppingPolicy.shortest); 6137 foreach (i, a; stuff) 6138 { 6139 assert(stuff[i] == a); 6140 } 6141 6142 // Test with indexing. 6143 uint[] res1; 6144 float[] res2; 6145 size_t[] indices; 6146 foreach (i, a, b; lockstep(foo, bar)) 6147 { 6148 indices ~= i; 6149 res1 ~= a; 6150 res2 ~= b; 6151 } 6152 6153 assert(indices == to!(size_t[])([0, 1, 2, 3, 4])); 6154 assert(res1 == [1,2,3,4,5]); 6155 assert(res2 == [6f,7f,8f,9f,10f]); 6156 6157 // Make sure we've worked around the relevant compiler bugs and this at least 6158 // compiles w/ >2 ranges. 6159 lockstep(foo, foo, foo); 6160 6161 // Make sure it works with const. 6162 const(int[])[] foo2 = [[1, 2, 3]]; 6163 const(int[])[] bar2 = [[4, 5, 6]]; 6164 auto c = chain(foo2, bar2); 6165 6166 foreach (f, b; lockstep(c, c)) {} 6167 6168 // Regression 10468 6169 foreach (x, y; lockstep(iota(0, 10), iota(0, 10))) { } 6170 } 6171 6172 @system unittest 6173 { 6174 struct RvalueRange 6175 { 6176 int[] impl; 6177 @property bool empty() { return impl.empty; } 6178 @property int front() { return impl[0]; } // N.B. non-ref 6179 void popFront() { impl.popFront(); } 6180 } 6181 auto data1 = [ 1, 2, 3, 4 ]; 6182 auto data2 = [ 5, 6, 7, 8 ]; 6183 auto r1 = RvalueRange(data1); 6184 auto r2 = data2; 6185 foreach (a, ref b; lockstep(r1, r2)) 6186 { 6187 a++; 6188 b++; 6189 } 6190 assert(data1 == [ 1, 2, 3, 4 ]); // changes to a do not propagate to data 6191 assert(data2 == [ 6, 7, 8, 9 ]); // but changes to b do. 6192 6193 // Since r1 is by-value only, the compiler should reject attempts to 6194 // foreach over it with ref. 6195 static assert(!__traits(compiles, { 6196 foreach (ref a, ref b; lockstep(r1, r2)) { a++; } 6197 })); 6198 } 6199 6200 private string lockstepReverseFailMixin(Ranges...)(bool withIndex) 6201 { 6202 import std.format : format; 6203 string[] params; 6204 string message; 6205 6206 if (withIndex) 6207 { 6208 message = "Indexed reverse iteration with lockstep is only supported" 6209 ~"if all ranges are bidirectional and have a length.\n"; 6210 } 6211 else 6212 { 6213 message = "Reverse iteration with lockstep is only supported if all ranges are bidirectional.\n"; 6214 } 6215 6216 if (withIndex) 6217 { 6218 params ~= "size_t"; 6219 } 6220 6221 foreach (idx, Range; Ranges) 6222 { 6223 params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); 6224 } 6225 6226 return format( 6227 q{ 6228 int opApplyReverse()(scope int delegate(%s) dg) 6229 { 6230 static assert(false, "%s"); 6231 } 6232 }, params.join(", "), message); 6233 } 6234 6235 // For generic programming, make sure Lockstep!(Range) is well defined for a 6236 // single range. 6237 template Lockstep(Range) 6238 { 6239 alias Lockstep = Range; 6240 } 6241 6242 /** 6243 Creates a mathematical sequence given the initial values and a 6244 recurrence function that computes the next value from the existing 6245 values. The sequence comes in the form of an infinite forward 6246 range. The type `Recurrence` itself is seldom used directly; most 6247 often, recurrences are obtained by calling the function $(D 6248 recurrence). 6249 6250 When calling `recurrence`, the function that computes the next 6251 value is specified as a template argument, and the initial values in 6252 the recurrence are passed as regular arguments. For example, in a 6253 Fibonacci sequence, there are two initial values (and therefore a 6254 state size of 2) because computing the next Fibonacci value needs the 6255 past two values. 6256 6257 The signature of this function should be: 6258 ---- 6259 auto fun(R)(R state, size_t n) 6260 ---- 6261 where `n` will be the index of the current value, and `state` will be an 6262 opaque state vector that can be indexed with array-indexing notation 6263 `state[i]`, where valid values of `i` range from $(D (n - 1)) to 6264 $(D (n - State.length)). 6265 6266 If the function is passed in string form, the state has name `"a"` 6267 and the zero-based index in the recurrence has name `"n"`. The 6268 given string must return the desired value for `a[n]` given 6269 `a[n - 1]`, `a[n - 2]`, `a[n - 3]`,..., `a[n - stateSize]`. The 6270 state size is dictated by the number of arguments passed to the call 6271 to `recurrence`. The `Recurrence` struct itself takes care of 6272 managing the recurrence's state and shifting it appropriately. 6273 */ 6274 struct Recurrence(alias fun, StateType, size_t stateSize) 6275 { 6276 import std.functional : binaryFun; 6277 6278 StateType[stateSize] _state; 6279 size_t _n; 6280 6281 this(StateType[stateSize] initial) { _state = initial; } 6282 6283 void popFront() 6284 { 6285 static auto trustedCycle(ref typeof(_state) s) @trusted 6286 { 6287 return cycle(s); 6288 } 6289 // The cast here is reasonable because fun may cause integer 6290 // promotion, but needs to return a StateType to make its operation 6291 // closed. Therefore, we have no other choice. 6292 _state[_n % stateSize] = cast(StateType) binaryFun!(fun, "a", "n")( 6293 trustedCycle(_state), _n + stateSize); 6294 ++_n; 6295 } 6296 6297 @property StateType front() 6298 { 6299 return _state[_n % stateSize]; 6300 } 6301 6302 @property typeof(this) save() 6303 { 6304 return this; 6305 } 6306 6307 enum bool empty = false; 6308 } 6309 6310 /// 6311 pure @safe nothrow unittest 6312 { 6313 import std.algorithm.comparison : equal; 6314 6315 // The Fibonacci numbers, using function in string form: 6316 // a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n] 6317 auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); 6318 assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])); 6319 6320 // The factorials, using function in lambda form: 6321 auto fac = recurrence!((a,n) => a[n-1] * n)(1); 6322 assert(take(fac, 10).equal([ 6323 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 6324 ])); 6325 6326 // The triangular numbers, using function in explicit form: 6327 static size_t genTriangular(R)(R state, size_t n) 6328 { 6329 return state[n-1] + n; 6330 } 6331 auto tri = recurrence!genTriangular(0); 6332 assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45])); 6333 } 6334 6335 /// Ditto 6336 Recurrence!(fun, CommonType!(State), State.length) 6337 recurrence(alias fun, State...)(State initial) 6338 { 6339 CommonType!(State)[State.length] state; 6340 foreach (i, Unused; State) 6341 { 6342 state[i] = initial[i]; 6343 } 6344 return typeof(return)(state); 6345 } 6346 6347 pure @safe nothrow unittest 6348 { 6349 import std.algorithm.comparison : equal; 6350 6351 auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); 6352 static assert(isForwardRange!(typeof(fib))); 6353 6354 int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]; 6355 assert(equal(take(fib, 10), witness)); 6356 foreach (e; take(fib, 10)) {} 6357 auto fact = recurrence!("n * a[n-1]")(1); 6358 assert( equal(take(fact, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6, 6359 2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) ); 6360 auto piapprox = recurrence!("a[n] + (n & 1 ? 4.0 : -4.0) / (2 * n + 3)")(4.0); 6361 foreach (e; take(piapprox, 20)) {} 6362 // Thanks to yebblies for this test and the associated fix 6363 auto r = recurrence!"a[n-2]"(1, 2); 6364 witness = [1, 2, 1, 2, 1]; 6365 assert(equal(take(r, 5), witness)); 6366 } 6367 6368 /** 6369 `Sequence` is similar to `Recurrence` except that iteration is 6370 presented in the so-called $(HTTP en.wikipedia.org/wiki/Closed_form, 6371 closed form). This means that the `n`th element in the series is 6372 computable directly from the initial values and `n` itself. This 6373 implies that the interface offered by `Sequence` is a random-access 6374 range, as opposed to the regular `Recurrence`, which only offers 6375 forward iteration. 6376 6377 The state of the sequence is stored as a `Tuple` so it can be 6378 heterogeneous. 6379 */ 6380 struct Sequence(alias fun, State) 6381 { 6382 private: 6383 import std.functional : binaryFun; 6384 6385 alias compute = binaryFun!(fun, "a", "n"); 6386 alias ElementType = typeof(compute(State.init, cast(size_t) 1)); 6387 State _state; 6388 size_t _n; 6389 6390 static struct DollarToken{} 6391 6392 public: 6393 this(State initial, size_t n = 0) 6394 { 6395 _state = initial; 6396 _n = n; 6397 } 6398 6399 @property ElementType front() 6400 { 6401 return compute(_state, _n); 6402 } 6403 6404 void popFront() 6405 { 6406 ++_n; 6407 } 6408 6409 enum opDollar = DollarToken(); 6410 6411 auto opSlice(size_t lower, size_t upper) 6412 in 6413 { 6414 assert( 6415 upper >= lower, 6416 "Attempting to slice a Sequence with a larger first argument than the second." 6417 ); 6418 } 6419 do 6420 { 6421 return typeof(this)(_state, _n + lower).take(upper - lower); 6422 } 6423 6424 auto opSlice(size_t lower, DollarToken) 6425 { 6426 return typeof(this)(_state, _n + lower); 6427 } 6428 6429 ElementType opIndex(size_t n) 6430 { 6431 return compute(_state, n + _n); 6432 } 6433 6434 enum bool empty = false; 6435 6436 @property Sequence save() { return this; } 6437 } 6438 6439 /// Ditto 6440 auto sequence(alias fun, State...)(State args) 6441 { 6442 import std.typecons : Tuple, tuple; 6443 alias Return = Sequence!(fun, Tuple!State); 6444 return Return(tuple(args)); 6445 } 6446 6447 /// Odd numbers, using function in string form: 6448 pure @safe nothrow @nogc unittest 6449 { 6450 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 6451 assert(odds.front == 1); 6452 odds.popFront(); 6453 assert(odds.front == 3); 6454 odds.popFront(); 6455 assert(odds.front == 5); 6456 } 6457 6458 /// Triangular numbers, using function in lambda form: 6459 pure @safe nothrow @nogc unittest 6460 { 6461 auto tri = sequence!((a,n) => n*(n+1)/2)(); 6462 6463 // Note random access 6464 assert(tri[0] == 0); 6465 assert(tri[3] == 6); 6466 assert(tri[1] == 1); 6467 assert(tri[4] == 10); 6468 assert(tri[2] == 3); 6469 } 6470 6471 /// Fibonacci numbers, using function in explicit form: 6472 @safe nothrow @nogc unittest 6473 { 6474 import std.math.exponential : pow; 6475 import std.math.rounding : round; 6476 import std.math.algebraic : sqrt; 6477 static ulong computeFib(S)(S state, size_t n) 6478 { 6479 // Binet's formula 6480 return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) / 6481 state[2])); 6482 } 6483 auto fib = sequence!computeFib( 6484 (1.0 + sqrt(5.0)) / 2.0, // Golden Ratio 6485 (1.0 - sqrt(5.0)) / 2.0, // Conjugate of Golden Ratio 6486 sqrt(5.0)); 6487 6488 // Note random access with [] operator 6489 assert(fib[1] == 1); 6490 assert(fib[4] == 5); 6491 assert(fib[3] == 3); 6492 assert(fib[2] == 2); 6493 assert(fib[9] == 55); 6494 } 6495 6496 pure @safe nothrow @nogc unittest 6497 { 6498 import std.typecons : Tuple, tuple; 6499 auto y = Sequence!("a[0] + n * a[1]", Tuple!(int, int))(tuple(0, 4)); 6500 static assert(isForwardRange!(typeof(y))); 6501 6502 //@@BUG 6503 //auto y = sequence!("a[0] + n * a[1]")(0, 4); 6504 //foreach (e; take(y, 15)) 6505 {} //writeln(e); 6506 6507 auto odds = Sequence!("a[0] + n * a[1]", Tuple!(int, int))( 6508 tuple(1, 2)); 6509 for (int currentOdd = 1; currentOdd <= 21; currentOdd += 2) 6510 { 6511 assert(odds.front == odds[0]); 6512 assert(odds[0] == currentOdd); 6513 odds.popFront(); 6514 } 6515 } 6516 6517 pure @safe nothrow @nogc unittest 6518 { 6519 import std.algorithm.comparison : equal; 6520 6521 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 6522 static assert(hasSlicing!(typeof(odds))); 6523 6524 //Note: don't use drop or take as the target of an equal, 6525 //since they'll both just forward to opSlice, making the tests irrelevant 6526 6527 // static slicing tests 6528 assert(equal(odds[0 .. 5], only(1, 3, 5, 7, 9))); 6529 assert(equal(odds[3 .. 7], only(7, 9, 11, 13))); 6530 6531 // relative slicing test, testing slicing is NOT agnostic of state 6532 auto odds_less5 = odds.drop(5); //this should actually call odds[5 .. $] 6533 assert(equal(odds_less5[0 .. 3], only(11, 13, 15))); 6534 assert(equal(odds_less5[0 .. 10], odds[5 .. 15])); 6535 6536 //Infinite slicing tests 6537 odds = odds[10 .. $]; 6538 assert(equal(odds.take(3), only(21, 23, 25))); 6539 } 6540 6541 // https://issues.dlang.org/show_bug.cgi?id=5036 6542 pure @safe nothrow unittest 6543 { 6544 auto s = sequence!((a, n) => new int)(0); 6545 assert(s.front != s.front); // no caching 6546 } 6547 6548 // iota 6549 /** 6550 Creates a range of values that span the given starting and stopping 6551 values. 6552 6553 Params: 6554 begin = The starting value. 6555 end = The value that serves as the stopping criterion. This value is not 6556 included in the range. 6557 step = The value to add to the current value at each iteration. 6558 6559 Returns: 6560 A range that goes through the numbers `begin`, $(D begin + step), 6561 $(D begin + 2 * step), `...`, up to and excluding `end`. 6562 6563 The two-argument overloads have $(D step = 1). If $(D begin < end && step < 6564 0) or $(D begin > end && step > 0) or $(D begin == end), then an empty range 6565 is returned. If $(D step == 0) then $(D begin == end) is an error. 6566 6567 For built-in types, the range returned is a random access range. For 6568 user-defined types that support `++`, the range is an input 6569 range. 6570 6571 An integral iota also supports `in` operator from the right. It takes 6572 the stepping into account, the integral won't be considered 6573 contained if it falls between two consecutive values of the range. 6574 `contains` does the same as in, but from lefthand side. 6575 6576 Example: 6577 --- 6578 void main() 6579 { 6580 import std.stdio; 6581 6582 // The following groups all produce the same output of: 6583 // 0 1 2 3 4 6584 6585 foreach (i; 0 .. 5) 6586 writef("%s ", i); 6587 writeln(); 6588 6589 import std.range : iota; 6590 foreach (i; iota(0, 5)) 6591 writef("%s ", i); 6592 writeln(); 6593 6594 writefln("%(%s %|%)", iota(0, 5)); 6595 6596 import std.algorithm.iteration : map; 6597 import std.algorithm.mutation : copy; 6598 import std.format; 6599 iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter()); 6600 writeln(); 6601 } 6602 --- 6603 */ 6604 auto iota(B, E, S)(B begin, E end, S step) 6605 if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) 6606 && isIntegral!S) 6607 { 6608 import std.conv : unsigned; 6609 6610 alias Value = CommonType!(Unqual!B, Unqual!E); 6611 alias StepType = Unqual!S; 6612 6613 assert(step != 0 || begin == end); 6614 6615 static struct Result 6616 { 6617 private Value current, last; 6618 private StepType step; // by convention, 0 if range is empty 6619 6620 this(Value current, Value pastLast, StepType step) 6621 { 6622 if (current < pastLast && step > 0) 6623 { 6624 // Iterating upward 6625 assert(unsigned((pastLast - current) / step) <= size_t.max); 6626 // Cast below can't fail because current < pastLast 6627 this.last = cast(Value) (pastLast - 1); 6628 this.last -= unsigned(this.last - current) % step; 6629 } 6630 else if (current > pastLast && step < 0) 6631 { 6632 // Iterating downward 6633 assert(unsigned((current - pastLast) / (0 - step)) <= size_t.max); 6634 // Cast below can't fail because current > pastLast 6635 this.last = cast(Value) (pastLast + 1); 6636 this.last += unsigned(current - this.last) % (0 - step); 6637 } 6638 else 6639 { 6640 // Initialize an empty range 6641 this.step = 0; 6642 return; 6643 } 6644 this.step = step; 6645 this.current = current; 6646 } 6647 6648 @property bool empty() const { return step == 0; } 6649 @property inout(Value) front() inout { assert(!empty); return current; } 6650 void popFront() 6651 { 6652 assert(!empty); 6653 if (current == last) step = 0; 6654 else current += step; 6655 } 6656 6657 @property inout(Value) back() inout 6658 { 6659 assert(!empty); 6660 return last; 6661 } 6662 void popBack() 6663 { 6664 assert(!empty); 6665 if (current == last) step = 0; 6666 else last -= step; 6667 } 6668 6669 @property auto save() { return this; } 6670 6671 inout(Value) opIndex(ulong n) inout 6672 { 6673 assert(n < this.length); 6674 6675 // Just cast to Value here because doing so gives overflow behavior 6676 // consistent with calling popFront() n times. 6677 return cast(inout Value) (current + step * n); 6678 } 6679 auto opBinaryRight(string op)(Value val) const 6680 if (op == "in") 6681 { 6682 if (empty) return false; 6683 //cast to avoid becoming unsigned 6684 auto supposedIndex = cast(StepType)(val - current) / step; 6685 return supposedIndex < length && supposedIndex * step + current == val; 6686 } 6687 auto contains(Value x){return x in this;} 6688 inout(Result) opSlice() inout { return this; } 6689 inout(Result) opSlice(ulong lower, ulong upper) inout 6690 { 6691 assert(upper >= lower && upper <= this.length); 6692 6693 return cast(inout Result) Result( 6694 cast(Value)(current + lower * step), 6695 cast(Value)(current + upper * step), 6696 step); 6697 } 6698 @property size_t length() const 6699 { 6700 if (step > 0) 6701 return 1 + cast(size_t) (unsigned(last - current) / step); 6702 if (step < 0) 6703 return 1 + cast(size_t) (unsigned(current - last) / (0 - step)); 6704 return 0; 6705 } 6706 6707 alias opDollar = length; 6708 } 6709 6710 return Result(begin, end, step); 6711 } 6712 6713 /// Ditto 6714 auto iota(B, E)(B begin, E end) 6715 if (isFloatingPoint!(CommonType!(B, E))) 6716 { 6717 return iota(begin, end, CommonType!(B, E)(1)); 6718 } 6719 6720 /// Ditto 6721 auto iota(B, E)(B begin, E end) 6722 if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) 6723 { 6724 import std.conv : unsigned; 6725 6726 alias Value = CommonType!(Unqual!B, Unqual!E); 6727 6728 static struct Result 6729 { 6730 private Value current, pastLast; 6731 6732 this(Value current, Value pastLast) 6733 { 6734 if (current < pastLast) 6735 { 6736 assert(unsigned(pastLast - current) <= size_t.max, 6737 "`iota` range is too long"); 6738 6739 this.current = current; 6740 this.pastLast = pastLast; 6741 } 6742 else 6743 { 6744 // Initialize an empty range 6745 this.current = this.pastLast = current; 6746 } 6747 } 6748 6749 @property bool empty() const { return current == pastLast; } 6750 @property inout(Value) front() inout 6751 { 6752 assert(!empty, "Attempt to access `front` of empty `iota` range"); 6753 return current; 6754 } 6755 void popFront() 6756 { 6757 assert(!empty, "Attempt to `popFront` of empty `iota` range"); 6758 ++current; 6759 } 6760 6761 @property inout(Value) back() inout 6762 { 6763 assert(!empty, "Attempt to access `back` of empty `iota` range"); 6764 return cast(inout(Value))(pastLast - 1); 6765 } 6766 void popBack() 6767 { 6768 assert(!empty, "Attempt to `popBack` of empty `iota` range"); 6769 --pastLast; 6770 } 6771 6772 @property auto save() { return this; } 6773 6774 inout(Value) opIndex(size_t n) inout 6775 { 6776 assert(n < this.length, 6777 "Attempt to read out-of-bounds index of `iota` range"); 6778 6779 // Just cast to Value here because doing so gives overflow behavior 6780 // consistent with calling popFront() n times. 6781 return cast(inout Value) (current + n); 6782 } 6783 auto opBinaryRight(string op)(Value val) const 6784 if (op == "in") 6785 { 6786 return current <= val && val < pastLast; 6787 } 6788 auto contains(Value x){return x in this;} 6789 inout(Result) opSlice() inout { return this; } 6790 inout(Result) opSlice(ulong lower, ulong upper) inout 6791 { 6792 assert(upper >= lower && upper <= this.length, 6793 "Attempt to get out-of-bounds slice of `iota` range"); 6794 6795 return cast(inout Result) Result(cast(Value)(current + lower), 6796 cast(Value)(pastLast - (length - upper))); 6797 } 6798 @property size_t length() const 6799 { 6800 return cast(size_t)(pastLast - current); 6801 } 6802 6803 alias opDollar = length; 6804 } 6805 6806 return Result(begin, end); 6807 } 6808 6809 /// Ditto 6810 auto iota(E)(E end) 6811 if (is(typeof(iota(E(0), end)))) 6812 { 6813 E begin = E(0); 6814 return iota(begin, end); 6815 } 6816 6817 /// Ditto 6818 // Specialization for floating-point types 6819 auto iota(B, E, S)(B begin, E end, S step) 6820 if (isFloatingPoint!(CommonType!(B, E, S))) 6821 in 6822 { 6823 assert(step != 0, "iota: step must not be 0"); 6824 assert((end - begin) / step >= 0, "iota: incorrect startup parameters"); 6825 } 6826 do 6827 { 6828 alias Value = Unqual!(CommonType!(B, E, S)); 6829 static struct Result 6830 { 6831 private Value start, step; 6832 private size_t index, count; 6833 6834 this(Value start, Value end, Value step) 6835 { 6836 import std.conv : to; 6837 6838 this.start = start; 6839 this.step = step; 6840 immutable fcount = (end - start) / step; 6841 count = to!size_t(fcount); 6842 auto pastEnd = start + count * step; 6843 if (step > 0) 6844 { 6845 if (pastEnd < end) ++count; 6846 assert(start + count * step >= end); 6847 } 6848 else 6849 { 6850 if (pastEnd > end) ++count; 6851 assert(start + count * step <= end); 6852 } 6853 } 6854 6855 @property bool empty() const { return index == count; } 6856 @property Value front() const { assert(!empty); return start + step * index; } 6857 void popFront() 6858 { 6859 assert(!empty); 6860 ++index; 6861 } 6862 @property Value back() const 6863 { 6864 assert(!empty); 6865 return start + step * (count - 1); 6866 } 6867 void popBack() 6868 { 6869 assert(!empty); 6870 --count; 6871 } 6872 6873 @property auto save() { return this; } 6874 6875 Value opIndex(size_t n) const 6876 { 6877 assert(n < count); 6878 return start + step * (n + index); 6879 } 6880 inout(Result) opSlice() inout 6881 { 6882 return this; 6883 } 6884 inout(Result) opSlice(size_t lower, size_t upper) inout 6885 { 6886 assert(upper >= lower && upper <= count); 6887 6888 Result ret = this; 6889 ret.index += lower; 6890 ret.count = upper - lower + ret.index; 6891 return cast(inout Result) ret; 6892 } 6893 @property size_t length() const 6894 { 6895 return count - index; 6896 } 6897 6898 alias opDollar = length; 6899 } 6900 6901 return Result(begin, end, step); 6902 } 6903 6904 /// 6905 pure @safe unittest 6906 { 6907 import std.algorithm.comparison : equal; 6908 import std.math.operations : isClose; 6909 6910 auto r = iota(0, 10, 1); 6911 assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); 6912 assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); 6913 assert(3 in r); 6914 assert(r.contains(3)); //Same as above 6915 assert(!(10 in r)); 6916 assert(!(-8 in r)); 6917 r = iota(0, 11, 3); 6918 assert(equal(r, [0, 3, 6, 9])); 6919 assert(r[2] == 6); 6920 assert(!(2 in r)); 6921 auto rf = iota(0.0, 0.5, 0.1); 6922 assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4])); 6923 } 6924 6925 pure nothrow @nogc @safe unittest 6926 { 6927 import std.traits : Signed; 6928 //float overloads use std.conv.to so can't be @nogc or nothrow 6929 alias ssize_t = Signed!size_t; 6930 assert(iota(ssize_t.max, 0, -1).length == ssize_t.max); 6931 assert(iota(ssize_t.max, ssize_t.min, -1).length == size_t.max); 6932 assert(iota(ssize_t.max, ssize_t.min, -2).length == 1 + size_t.max / 2); 6933 assert(iota(ssize_t.min, ssize_t.max, 2).length == 1 + size_t.max / 2); 6934 assert(iota(ssize_t.max, ssize_t.min, -3).length == size_t.max / 3); 6935 } 6936 6937 debug @system unittest 6938 {//check the contracts 6939 import core.exception : AssertError; 6940 import std.exception : assertThrown; 6941 assertThrown!AssertError(iota(1,2,0)); 6942 assertThrown!AssertError(iota(0f,1f,0f)); 6943 assertThrown!AssertError(iota(1f,0f,0.1f)); 6944 assertThrown!AssertError(iota(0f,1f,-0.1f)); 6945 } 6946 6947 pure @system nothrow unittest 6948 { 6949 int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 6950 auto r1 = iota(a.ptr, a.ptr + a.length, 1); 6951 assert(r1.front == a.ptr); 6952 assert(r1.back == a.ptr + a.length - 1); 6953 assert(&a[4] in r1); 6954 } 6955 6956 pure @safe nothrow @nogc unittest 6957 { 6958 assert(iota(1UL, 0UL).length == 0); 6959 assert(iota(1UL, 0UL, 1).length == 0); 6960 assert(iota(0, 1, 1).length == 1); 6961 assert(iota(1, 0, -1).length == 1); 6962 assert(iota(0, 1, -1).length == 0); 6963 assert(iota(ulong.max, 0).length == 0); 6964 } 6965 6966 pure @safe unittest 6967 { 6968 import std.algorithm.comparison : equal; 6969 import std.algorithm.searching : count; 6970 import std.math.operations : isClose, nextUp, nextDown; 6971 import std.meta : AliasSeq; 6972 6973 static assert(is(ElementType!(typeof(iota(0f))) == float)); 6974 6975 static assert(hasLength!(typeof(iota(0, 2)))); 6976 auto r = iota(0, 10, 1); 6977 assert(r[$ - 1] == 9); 6978 assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); 6979 6980 auto rSlice = r[2 .. 8]; 6981 assert(equal(rSlice, [2, 3, 4, 5, 6, 7])); 6982 6983 rSlice.popFront(); 6984 assert(rSlice[0] == rSlice.front); 6985 assert(rSlice.front == 3); 6986 6987 rSlice.popBack(); 6988 assert(rSlice[rSlice.length - 1] == rSlice.back); 6989 assert(rSlice.back == 6); 6990 6991 rSlice = r[0 .. 4]; 6992 assert(equal(rSlice, [0, 1, 2, 3])); 6993 assert(3 in rSlice); 6994 assert(!(4 in rSlice)); 6995 6996 auto rr = iota(10); 6997 assert(equal(rr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); 6998 6999 r = iota(0, -10, -1); 7000 assert(equal(r, [0, -1, -2, -3, -4, -5, -6, -7, -8, -9][])); 7001 rSlice = r[3 .. 9]; 7002 assert(equal(rSlice, [-3, -4, -5, -6, -7, -8])); 7003 7004 r = iota(0, -6, -3); 7005 assert(equal(r, [0, -3][])); 7006 rSlice = r[1 .. 2]; 7007 assert(equal(rSlice, [-3])); 7008 7009 r = iota(0, -7, -3); 7010 assert(equal(r, [0, -3, -6][])); 7011 assert(0 in r); 7012 assert(-6 in r); 7013 rSlice = r[1 .. 3]; 7014 assert(equal(rSlice, [-3, -6])); 7015 assert(!(0 in rSlice)); 7016 assert(!(-2 in rSlice)); 7017 assert(!(-5 in rSlice)); 7018 assert(!(3 in rSlice)); 7019 assert(!(-9 in rSlice)); 7020 7021 r = iota(0, 11, 3); 7022 assert(equal(r, [0, 3, 6, 9][])); 7023 assert(r[2] == 6); 7024 rSlice = r[1 .. 3]; 7025 assert(equal(rSlice, [3, 6])); 7026 7027 auto rf = iota(0.0, 0.5, 0.1); 7028 assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4][])); 7029 assert(rf.length == 5); 7030 7031 rf.popFront(); 7032 assert(rf.length == 4); 7033 7034 auto rfSlice = rf[1 .. 4]; 7035 assert(rfSlice.length == 3); 7036 assert(isClose(rfSlice, [0.2, 0.3, 0.4])); 7037 7038 rfSlice.popFront(); 7039 assert(isClose(rfSlice[0], 0.3)); 7040 7041 rf.popFront(); 7042 assert(rf.length == 3); 7043 7044 rfSlice = rf[1 .. 3]; 7045 assert(rfSlice.length == 2); 7046 assert(isClose(rfSlice, [0.3, 0.4])); 7047 assert(isClose(rfSlice[0], 0.3)); 7048 7049 // With something just above 0.5 7050 rf = iota(0.0, nextUp(0.5), 0.1); 7051 assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][])); 7052 rf.popBack(); 7053 assert(rf[rf.length - 1] == rf.back); 7054 assert(isClose(rf.back, 0.4)); 7055 assert(rf.length == 5); 7056 7057 // going down 7058 rf = iota(0.0, -0.5, -0.1); 7059 assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4][])); 7060 rfSlice = rf[2 .. 5]; 7061 assert(isClose(rfSlice, [-0.2, -0.3, -0.4])); 7062 7063 rf = iota(0.0, nextDown(-0.5), -0.1); 7064 assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][])); 7065 7066 // iota of longs 7067 auto rl = iota(5_000_000L); 7068 assert(rl.length == 5_000_000L); 7069 assert(0 in rl); 7070 assert(4_000_000L in rl); 7071 assert(!(-4_000_000L in rl)); 7072 assert(!(5_000_000L in rl)); 7073 7074 // iota of longs with steps 7075 auto iota_of_longs_with_steps = iota(50L, 101L, 10); 7076 assert(iota_of_longs_with_steps.length == 6); 7077 assert(equal(iota_of_longs_with_steps, [50L, 60L, 70L, 80L, 90L, 100L])); 7078 7079 // iota of unsigned zero length (https://issues.dlang.org/show_bug.cgi?id=6222) 7080 // Actually trying to consume it is the only way to find something is wrong 7081 // because the public properties are all correct. 7082 auto iota_zero_unsigned = iota(0, 0u, 3); 7083 assert(count(iota_zero_unsigned) == 0); 7084 7085 // https://issues.dlang.org/show_bug.cgi?id=7982 7086 // unsigned reverse iota can be buggy if `.length` doesn't 7087 // take them into account 7088 assert(iota(10u, 0u, -1).length == 10); 7089 assert(iota(10u, 0u, -2).length == 5); 7090 assert(iota(uint.max, uint.max-10, -1).length == 10); 7091 assert(iota(uint.max, uint.max-10, -2).length == 5); 7092 assert(iota(uint.max, 0u, -1).length == uint.max); 7093 7094 assert(20 in iota(20u, 10u, -2)); 7095 assert(16 in iota(20u, 10u, -2)); 7096 assert(!(15 in iota(20u, 10u, -2))); 7097 assert(!(10 in iota(20u, 10u, -2))); 7098 assert(!(uint.max in iota(20u, 10u, -1))); 7099 assert(!(int.min in iota(20u, 10u, -1))); 7100 assert(!(int.max in iota(20u, 10u, -1))); 7101 7102 7103 // https://issues.dlang.org/show_bug.cgi?id=8920 7104 static foreach (Type; AliasSeq!(byte, ubyte, short, ushort, 7105 int, uint, long, ulong)) 7106 {{ 7107 Type val; 7108 foreach (i; iota(cast(Type) 0, cast(Type) 10)) { val++; } 7109 assert(val == 10); 7110 }} 7111 } 7112 7113 pure @safe nothrow unittest 7114 { 7115 import std.algorithm.mutation : copy; 7116 auto idx = new size_t[100]; 7117 copy(iota(0, idx.length), idx); 7118 } 7119 7120 @safe unittest 7121 { 7122 import std.meta : AliasSeq; 7123 static foreach (range; AliasSeq!(iota(2, 27, 4), 7124 iota(3, 9), 7125 iota(2.7, 12.3, .1), 7126 iota(3.2, 9.7))) 7127 {{ 7128 const cRange = range; 7129 const e = cRange.empty; 7130 const f = cRange.front; 7131 const b = cRange.back; 7132 const i = cRange[2]; 7133 const s1 = cRange[]; 7134 const s2 = cRange[0 .. 3]; 7135 const l = cRange.length; 7136 }} 7137 } 7138 7139 @system unittest 7140 { 7141 //The ptr stuff can't be done at compile time, so we unfortunately end 7142 //up with some code duplication here. 7143 auto arr = [0, 5, 3, 5, 5, 7, 9, 2, 0, 42, 7, 6]; 7144 7145 { 7146 const cRange = iota(arr.ptr, arr.ptr + arr.length, 3); 7147 const e = cRange.empty; 7148 const f = cRange.front; 7149 const b = cRange.back; 7150 const i = cRange[2]; 7151 const s1 = cRange[]; 7152 const s2 = cRange[0 .. 3]; 7153 const l = cRange.length; 7154 } 7155 7156 { 7157 const cRange = iota(arr.ptr, arr.ptr + arr.length); 7158 const e = cRange.empty; 7159 const f = cRange.front; 7160 const b = cRange.back; 7161 const i = cRange[2]; 7162 const s1 = cRange[]; 7163 const s2 = cRange[0 .. 3]; 7164 const l = cRange.length; 7165 } 7166 } 7167 7168 @nogc nothrow pure @safe unittest 7169 { 7170 { 7171 ushort start = 0, end = 10, step = 2; 7172 foreach (i; iota(start, end, step)) 7173 static assert(is(typeof(i) == ushort)); 7174 } 7175 { 7176 ubyte start = 0, end = 255, step = 128; 7177 uint x; 7178 foreach (i; iota(start, end, step)) 7179 { 7180 static assert(is(typeof(i) == ubyte)); 7181 ++x; 7182 } 7183 assert(x == 2); 7184 } 7185 } 7186 7187 /* Generic overload that handles arbitrary types that support arithmetic 7188 * operations. 7189 * 7190 * User-defined types such as $(REF BigInt, std,bigint) are also supported, as long 7191 * as they can be incremented with `++` and compared with `<` or `==`. 7192 */ 7193 /// ditto 7194 auto iota(B, E)(B begin, E end) 7195 if (!isIntegral!(CommonType!(B, E)) && 7196 !isFloatingPoint!(CommonType!(B, E)) && 7197 !isPointer!(CommonType!(B, E)) && 7198 is(typeof((ref B b) { ++b; })) && 7199 (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) ) 7200 { 7201 static struct Result 7202 { 7203 B current; 7204 E end; 7205 7206 @property bool empty() 7207 { 7208 static if (is(typeof(B.init < E.init))) 7209 return !(current < end); 7210 else static if (is(typeof(B.init != E.init))) 7211 return current == end; 7212 else 7213 static assert(0); 7214 } 7215 @property auto front() { return current; } 7216 void popFront() 7217 { 7218 assert(!empty); 7219 ++current; 7220 } 7221 @property auto save() { return this; } 7222 } 7223 return Result(begin, end); 7224 } 7225 7226 @safe unittest 7227 { 7228 import std.algorithm.comparison : equal; 7229 7230 // Test iota() for a type that only supports ++ and != but does not have 7231 // '<'-ordering. 7232 struct Cyclic(int wrapAround) 7233 { 7234 int current; 7235 7236 this(int start) { current = start % wrapAround; } 7237 7238 bool opEquals(Cyclic c) const { return current == c.current; } 7239 bool opEquals(int i) const { return current == i; } 7240 void opUnary(string op)() if (op == "++") 7241 { 7242 current = (current + 1) % wrapAround; 7243 } 7244 } 7245 alias Cycle5 = Cyclic!5; 7246 7247 // Easy case 7248 auto i1 = iota(Cycle5(1), Cycle5(4)); 7249 assert(i1.equal([1, 2, 3])); 7250 7251 // Wraparound case 7252 auto i2 = iota(Cycle5(3), Cycle5(2)); 7253 assert(i2.equal([3, 4, 0, 1 ])); 7254 } 7255 7256 // https://issues.dlang.org/show_bug.cgi?id=23453 7257 @safe unittest 7258 { 7259 auto r = iota('a', 'z'); 7260 static assert(isForwardRange!(typeof(r))); 7261 } 7262 7263 /** 7264 Options for the $(LREF FrontTransversal) and $(LREF Transversal) ranges 7265 (below). 7266 */ 7267 enum TransverseOptions 7268 { 7269 /** 7270 When transversed, the elements of a range of ranges are assumed to 7271 have different lengths (e.g. a jagged array). 7272 */ 7273 assumeJagged, //default 7274 /** 7275 The transversal enforces that the elements of a range of ranges have 7276 all the same length (e.g. an array of arrays, all having the same 7277 length). Checking is done once upon construction of the transversal 7278 range. 7279 */ 7280 enforceNotJagged, 7281 /** 7282 The transversal assumes, without verifying, that the elements of a 7283 range of ranges have all the same length. This option is useful if 7284 checking was already done from the outside of the range. 7285 */ 7286 assumeNotJagged, 7287 } 7288 7289 /// 7290 @safe pure unittest 7291 { 7292 import std.algorithm.comparison : equal; 7293 import std.exception : assertThrown; 7294 7295 auto arr = [[1, 2], [3, 4, 5]]; 7296 7297 auto r1 = arr.frontTransversal!(TransverseOptions.assumeJagged); 7298 assert(r1.equal([1, 3])); 7299 7300 // throws on construction 7301 assertThrown!Exception(arr.frontTransversal!(TransverseOptions.enforceNotJagged)); 7302 7303 auto r2 = arr.frontTransversal!(TransverseOptions.assumeNotJagged); 7304 assert(r2.equal([1, 3])); 7305 7306 // either assuming or checking for equal lengths makes 7307 // the result a random access range 7308 assert(r2[0] == 1); 7309 static assert(!__traits(compiles, r1[0])); 7310 } 7311 7312 /** 7313 Given a range of ranges, iterate transversally through the first 7314 elements of each of the enclosed ranges. 7315 */ 7316 struct FrontTransversal(Ror, 7317 TransverseOptions opt = TransverseOptions.assumeJagged) 7318 { 7319 alias RangeOfRanges = Unqual!(Ror); 7320 alias RangeType = .ElementType!RangeOfRanges; 7321 alias ElementType = .ElementType!RangeType; 7322 7323 private void prime() 7324 { 7325 static if (opt == TransverseOptions.assumeJagged) 7326 { 7327 while (!_input.empty && _input.front.empty) 7328 { 7329 _input.popFront(); 7330 } 7331 static if (isBidirectionalRange!RangeOfRanges) 7332 { 7333 while (!_input.empty && _input.back.empty) 7334 { 7335 _input.popBack(); 7336 } 7337 } 7338 } 7339 } 7340 7341 /** 7342 Construction from an input. 7343 */ 7344 this(RangeOfRanges input) 7345 { 7346 _input = input; 7347 prime(); 7348 static if (opt == TransverseOptions.enforceNotJagged) 7349 // (isRandomAccessRange!RangeOfRanges 7350 // && hasLength!RangeType) 7351 { 7352 import std.exception : enforce; 7353 7354 if (empty) return; 7355 immutable commonLength = _input.front.length; 7356 foreach (e; _input) 7357 { 7358 enforce(e.length == commonLength); 7359 } 7360 } 7361 } 7362 7363 /** 7364 Forward range primitives. 7365 */ 7366 static if (isInfinite!RangeOfRanges) 7367 { 7368 enum bool empty = false; 7369 } 7370 else 7371 { 7372 @property bool empty() 7373 { 7374 static if (opt != TransverseOptions.assumeJagged) 7375 { 7376 if (!_input.empty) 7377 return _input.front.empty; 7378 } 7379 7380 return _input.empty; 7381 } 7382 } 7383 7384 /// Ditto 7385 @property auto ref front() 7386 { 7387 assert(!empty, "Attempting to fetch the front of an empty FrontTransversal"); 7388 return _input.front.front; 7389 } 7390 7391 /// Ditto 7392 static if (hasMobileElements!RangeType) 7393 { 7394 ElementType moveFront() 7395 { 7396 return _input.front.moveFront(); 7397 } 7398 } 7399 7400 static if (hasAssignableElements!RangeType) 7401 { 7402 @property void front(ElementType val) 7403 { 7404 import std.algorithm.mutation : move; 7405 7406 _input.front.front = move(val); 7407 } 7408 } 7409 7410 /// Ditto 7411 void popFront() 7412 { 7413 assert(!empty, "Attempting to popFront an empty FrontTransversal"); 7414 _input.popFront(); 7415 prime(); 7416 } 7417 7418 /** 7419 Duplicates this `frontTransversal`. Note that only the encapsulating 7420 range of range will be duplicated. Underlying ranges will not be 7421 duplicated. 7422 */ 7423 static if (isForwardRange!RangeOfRanges) 7424 { 7425 @property FrontTransversal save() 7426 { 7427 return FrontTransversal(_input.save); 7428 } 7429 } 7430 7431 static if (isBidirectionalRange!RangeOfRanges) 7432 { 7433 /** 7434 Bidirectional primitives. They are offered if $(D 7435 isBidirectionalRange!RangeOfRanges). 7436 */ 7437 @property auto ref back() 7438 { 7439 assert(!empty, "Attempting to fetch the back of an empty FrontTransversal"); 7440 return _input.back.front; 7441 } 7442 /// Ditto 7443 void popBack() 7444 { 7445 assert(!empty, "Attempting to popBack an empty FrontTransversal"); 7446 _input.popBack(); 7447 prime(); 7448 } 7449 7450 /// Ditto 7451 static if (hasMobileElements!RangeType) 7452 { 7453 ElementType moveBack() 7454 { 7455 return _input.back.moveFront(); 7456 } 7457 } 7458 7459 static if (hasAssignableElements!RangeType) 7460 { 7461 @property void back(ElementType val) 7462 { 7463 import std.algorithm.mutation : move; 7464 7465 _input.back.front = move(val); 7466 } 7467 } 7468 } 7469 7470 static if (isRandomAccessRange!RangeOfRanges && 7471 (opt == TransverseOptions.assumeNotJagged || 7472 opt == TransverseOptions.enforceNotJagged)) 7473 { 7474 /** 7475 Random-access primitive. It is offered if $(D 7476 isRandomAccessRange!RangeOfRanges && (opt == 7477 TransverseOptions.assumeNotJagged || opt == 7478 TransverseOptions.enforceNotJagged)). 7479 */ 7480 auto ref opIndex(size_t n) 7481 { 7482 return _input[n].front; 7483 } 7484 7485 /// Ditto 7486 static if (hasMobileElements!RangeType) 7487 { 7488 ElementType moveAt(size_t n) 7489 { 7490 return _input[n].moveFront(); 7491 } 7492 } 7493 /// Ditto 7494 static if (hasAssignableElements!RangeType) 7495 { 7496 void opIndexAssign(ElementType val, size_t n) 7497 { 7498 import std.algorithm.mutation : move; 7499 7500 _input[n].front = move(val); 7501 } 7502 } 7503 mixin ImplementLength!_input; 7504 7505 /** 7506 Slicing if offered if `RangeOfRanges` supports slicing and all the 7507 conditions for supporting indexing are met. 7508 */ 7509 static if (hasSlicing!RangeOfRanges) 7510 { 7511 typeof(this) opSlice(size_t lower, size_t upper) 7512 { 7513 return typeof(this)(_input[lower .. upper]); 7514 } 7515 } 7516 } 7517 7518 auto opSlice() { return this; } 7519 7520 private: 7521 RangeOfRanges _input; 7522 } 7523 7524 /// Ditto 7525 FrontTransversal!(RangeOfRanges, opt) frontTransversal( 7526 TransverseOptions opt = TransverseOptions.assumeJagged, 7527 RangeOfRanges) 7528 (RangeOfRanges rr) 7529 { 7530 return typeof(return)(rr); 7531 } 7532 7533 /// 7534 pure @safe nothrow unittest 7535 { 7536 import std.algorithm.comparison : equal; 7537 int[][] x = new int[][2]; 7538 x[0] = [1, 2]; 7539 x[1] = [3, 4]; 7540 auto ror = frontTransversal(x); 7541 assert(equal(ror, [ 1, 3 ][])); 7542 } 7543 7544 @safe unittest 7545 { 7546 import std.algorithm.comparison : equal; 7547 import std.internal.test.dummyrange : AllDummyRanges, DummyRange, ReturnBy; 7548 7549 static assert(is(FrontTransversal!(immutable int[][]))); 7550 7551 foreach (DummyType; AllDummyRanges) 7552 { 7553 auto dummies = 7554 [DummyType.init, DummyType.init, DummyType.init, DummyType.init]; 7555 7556 foreach (i, ref elem; dummies) 7557 { 7558 // Just violate the DummyRange abstraction to get what I want. 7559 elem.arr = elem.arr[i..$ - (3 - i)]; 7560 } 7561 7562 auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(dummies); 7563 static if (isForwardRange!DummyType) 7564 { 7565 static assert(isForwardRange!(typeof(ft))); 7566 } 7567 7568 assert(equal(ft, [1, 2, 3, 4])); 7569 7570 // Test slicing. 7571 assert(equal(ft[0 .. 2], [1, 2])); 7572 assert(equal(ft[1 .. 3], [2, 3])); 7573 7574 assert(ft.front == ft.moveFront()); 7575 assert(ft.back == ft.moveBack()); 7576 assert(ft.moveAt(1) == ft[1]); 7577 7578 7579 // Test infiniteness propagation. 7580 static assert(isInfinite!(typeof(frontTransversal(repeat("foo"))))); 7581 7582 static if (DummyType.r == ReturnBy.Reference) 7583 { 7584 { 7585 ft.front++; 7586 scope(exit) ft.front--; 7587 assert(dummies.front.front == 2); 7588 } 7589 7590 { 7591 ft.front = 5; 7592 scope(exit) ft.front = 1; 7593 assert(dummies[0].front == 5); 7594 } 7595 7596 { 7597 ft.back = 88; 7598 scope(exit) ft.back = 4; 7599 assert(dummies.back.front == 88); 7600 } 7601 7602 { 7603 ft[1] = 99; 7604 scope(exit) ft[1] = 2; 7605 assert(dummies[1].front == 99); 7606 } 7607 } 7608 } 7609 } 7610 7611 // https://issues.dlang.org/show_bug.cgi?id=16363 7612 pure @safe nothrow unittest 7613 { 7614 import std.algorithm.comparison : equal; 7615 7616 int[][] darr = [[0, 1], [4, 5]]; 7617 auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(darr); 7618 7619 assert(equal(ft, [0, 4])); 7620 static assert(isRandomAccessRange!(typeof(ft))); 7621 } 7622 7623 // https://issues.dlang.org/show_bug.cgi?id=16442 7624 pure @safe nothrow unittest 7625 { 7626 int[][] arr = [[], []]; 7627 7628 auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(arr); 7629 assert(ft.empty); 7630 } 7631 7632 // ditto 7633 pure @safe unittest 7634 { 7635 int[][] arr = [[], []]; 7636 7637 auto ft = frontTransversal!(TransverseOptions.enforceNotJagged)(arr); 7638 assert(ft.empty); 7639 } 7640 7641 /** 7642 Given a range of ranges, iterate transversally through the 7643 `n`th element of each of the enclosed ranges. This function 7644 is similar to `unzip` in other languages. 7645 7646 Params: 7647 opt = Controls the assumptions the function makes about the lengths 7648 of the ranges 7649 rr = An input range of random access ranges 7650 Returns: 7651 At minimum, an input range. Range primitives such as bidirectionality 7652 and random access are given if the element type of `rr` provides them. 7653 */ 7654 struct Transversal(Ror, 7655 TransverseOptions opt = TransverseOptions.assumeJagged) 7656 { 7657 private alias RangeOfRanges = Unqual!Ror; 7658 private alias InnerRange = ElementType!RangeOfRanges; 7659 private alias E = ElementType!InnerRange; 7660 7661 private void prime() 7662 { 7663 static if (opt == TransverseOptions.assumeJagged) 7664 { 7665 while (!_input.empty && _input.front.length <= _n) 7666 { 7667 _input.popFront(); 7668 } 7669 static if (isBidirectionalRange!RangeOfRanges) 7670 { 7671 while (!_input.empty && _input.back.length <= _n) 7672 { 7673 _input.popBack(); 7674 } 7675 } 7676 } 7677 } 7678 7679 /** 7680 Construction from an input and an index. 7681 */ 7682 this(RangeOfRanges input, size_t n) 7683 { 7684 _input = input; 7685 _n = n; 7686 prime(); 7687 static if (opt == TransverseOptions.enforceNotJagged) 7688 { 7689 import std.exception : enforce; 7690 7691 if (empty) return; 7692 immutable commonLength = _input.front.length; 7693 foreach (e; _input) 7694 { 7695 enforce(e.length == commonLength); 7696 } 7697 } 7698 } 7699 7700 /** 7701 Forward range primitives. 7702 */ 7703 static if (isInfinite!(RangeOfRanges)) 7704 { 7705 enum bool empty = false; 7706 } 7707 else 7708 { 7709 @property bool empty() 7710 { 7711 return _input.empty; 7712 } 7713 } 7714 7715 /// Ditto 7716 @property auto ref front() 7717 { 7718 assert(!empty, "Attempting to fetch the front of an empty Transversal"); 7719 return _input.front[_n]; 7720 } 7721 7722 /// Ditto 7723 static if (hasMobileElements!InnerRange) 7724 { 7725 E moveFront() 7726 { 7727 return _input.front.moveAt(_n); 7728 } 7729 } 7730 7731 /// Ditto 7732 static if (hasAssignableElements!InnerRange) 7733 { 7734 @property void front(E val) 7735 { 7736 _input.front[_n] = val; 7737 } 7738 } 7739 7740 7741 /// Ditto 7742 void popFront() 7743 { 7744 assert(!empty, "Attempting to popFront an empty Transversal"); 7745 _input.popFront(); 7746 prime(); 7747 } 7748 7749 /// Ditto 7750 static if (isForwardRange!RangeOfRanges) 7751 { 7752 @property typeof(this) save() 7753 { 7754 auto ret = this; 7755 ret._input = _input.save; 7756 return ret; 7757 } 7758 } 7759 7760 static if (isBidirectionalRange!RangeOfRanges) 7761 { 7762 /** 7763 Bidirectional primitives. They are offered if $(D 7764 isBidirectionalRange!RangeOfRanges). 7765 */ 7766 @property auto ref back() 7767 { 7768 assert(!empty, "Attempting to fetch the back of an empty Transversal"); 7769 return _input.back[_n]; 7770 } 7771 7772 /// Ditto 7773 void popBack() 7774 { 7775 assert(!empty, "Attempting to popBack an empty Transversal"); 7776 _input.popBack(); 7777 prime(); 7778 } 7779 7780 /// Ditto 7781 static if (hasMobileElements!InnerRange) 7782 { 7783 E moveBack() 7784 { 7785 return _input.back.moveAt(_n); 7786 } 7787 } 7788 7789 /// Ditto 7790 static if (hasAssignableElements!InnerRange) 7791 { 7792 @property void back(E val) 7793 { 7794 _input.back[_n] = val; 7795 } 7796 } 7797 7798 } 7799 7800 static if (isRandomAccessRange!RangeOfRanges && 7801 (opt == TransverseOptions.assumeNotJagged || 7802 opt == TransverseOptions.enforceNotJagged)) 7803 { 7804 /** 7805 Random-access primitive. It is offered if $(D 7806 isRandomAccessRange!RangeOfRanges && (opt == 7807 TransverseOptions.assumeNotJagged || opt == 7808 TransverseOptions.enforceNotJagged)). 7809 */ 7810 auto ref opIndex(size_t n) 7811 { 7812 return _input[n][_n]; 7813 } 7814 7815 /// Ditto 7816 static if (hasMobileElements!InnerRange) 7817 { 7818 E moveAt(size_t n) 7819 { 7820 return _input[n].moveAt(_n); 7821 } 7822 } 7823 7824 /// Ditto 7825 static if (hasAssignableElements!InnerRange) 7826 { 7827 void opIndexAssign(E val, size_t n) 7828 { 7829 _input[n][_n] = val; 7830 } 7831 } 7832 7833 mixin ImplementLength!_input; 7834 7835 /** 7836 Slicing if offered if `RangeOfRanges` supports slicing and all the 7837 conditions for supporting indexing are met. 7838 */ 7839 static if (hasSlicing!RangeOfRanges) 7840 { 7841 typeof(this) opSlice(size_t lower, size_t upper) 7842 { 7843 return typeof(this)(_input[lower .. upper], _n); 7844 } 7845 } 7846 } 7847 7848 auto opSlice() { return this; } 7849 7850 private: 7851 RangeOfRanges _input; 7852 size_t _n; 7853 } 7854 7855 /// Ditto 7856 Transversal!(RangeOfRanges, opt) transversal 7857 (TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) 7858 (RangeOfRanges rr, size_t n) 7859 { 7860 return typeof(return)(rr, n); 7861 } 7862 7863 /// 7864 @safe unittest 7865 { 7866 import std.algorithm.comparison : equal; 7867 int[][] x = new int[][2]; 7868 x[0] = [1, 2]; 7869 x[1] = [3, 4]; 7870 auto ror = transversal(x, 1); 7871 assert(equal(ror, [ 2, 4 ])); 7872 } 7873 7874 /// The following code does a full unzip 7875 @safe unittest 7876 { 7877 import std.algorithm.comparison : equal; 7878 import std.algorithm.iteration : map; 7879 int[][] y = [[1, 2, 3], [4, 5, 6]]; 7880 auto z = y.front.walkLength.iota.map!(i => transversal(y, i)); 7881 assert(equal!equal(z, [[1, 4], [2, 5], [3, 6]])); 7882 } 7883 7884 @safe unittest 7885 { 7886 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 7887 7888 int[][] x = new int[][2]; 7889 x[0] = [ 1, 2 ]; 7890 x[1] = [3, 4]; 7891 auto ror = transversal!(TransverseOptions.assumeNotJagged)(x, 1); 7892 auto witness = [ 2, 4 ]; 7893 uint i; 7894 foreach (e; ror) assert(e == witness[i++]); 7895 assert(i == 2); 7896 assert(ror.length == 2); 7897 7898 static assert(is(Transversal!(immutable int[][]))); 7899 7900 // Make sure ref, assign is being propagated. 7901 { 7902 ror.front++; 7903 scope(exit) ror.front--; 7904 assert(x[0][1] == 3); 7905 } 7906 { 7907 ror.front = 5; 7908 scope(exit) ror.front = 2; 7909 assert(x[0][1] == 5); 7910 assert(ror.moveFront() == 5); 7911 } 7912 { 7913 ror.back = 999; 7914 scope(exit) ror.back = 4; 7915 assert(x[1][1] == 999); 7916 assert(ror.moveBack() == 999); 7917 } 7918 { 7919 ror[0] = 999; 7920 scope(exit) ror[0] = 2; 7921 assert(x[0][1] == 999); 7922 assert(ror.moveAt(0) == 999); 7923 } 7924 7925 // Test w/o ref return. 7926 alias D = DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random); 7927 auto drs = [D.init, D.init]; 7928 foreach (num; 0 .. 10) 7929 { 7930 auto t = transversal!(TransverseOptions.enforceNotJagged)(drs, num); 7931 assert(t[0] == t[1]); 7932 assert(t[1] == num + 1); 7933 } 7934 7935 static assert(isInfinite!(typeof(transversal(repeat([1,2,3]), 1)))); 7936 7937 // Test slicing. 7938 auto mat = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]; 7939 auto mat1 = transversal!(TransverseOptions.assumeNotJagged)(mat, 1)[1 .. 3]; 7940 assert(mat1[0] == 6); 7941 assert(mat1[1] == 10); 7942 } 7943 7944 struct Transposed(RangeOfRanges, 7945 TransverseOptions opt = TransverseOptions.assumeJagged) 7946 if (isForwardRange!RangeOfRanges && 7947 isInputRange!(ElementType!RangeOfRanges) && 7948 hasAssignableElements!RangeOfRanges) 7949 { 7950 this(RangeOfRanges input) 7951 { 7952 this._input = input; 7953 static if (opt == TransverseOptions.enforceNotJagged) 7954 { 7955 import std.exception : enforce; 7956 7957 if (empty) return; 7958 immutable commonLength = _input.front.length; 7959 foreach (e; _input) 7960 { 7961 enforce(e.length == commonLength); 7962 } 7963 } 7964 } 7965 7966 @property auto front() 7967 { 7968 import std.algorithm.iteration : filter, map; 7969 return _input.save 7970 .filter!(a => !a.empty) 7971 .map!(a => a.front); 7972 } 7973 7974 void popFront() 7975 { 7976 // Advance the position of each subrange. 7977 auto r = _input.save; 7978 while (!r.empty) 7979 { 7980 auto e = r.front; 7981 if (!e.empty) 7982 { 7983 e.popFront(); 7984 r.front = e; 7985 } 7986 7987 r.popFront(); 7988 } 7989 } 7990 7991 static if (isRandomAccessRange!(ElementType!RangeOfRanges)) 7992 { 7993 auto ref opIndex(size_t n) 7994 { 7995 return transversal!opt(_input, n); 7996 } 7997 } 7998 7999 @property bool empty() 8000 { 8001 if (_input.empty) return true; 8002 foreach (e; _input.save) 8003 { 8004 if (!e.empty) return false; 8005 } 8006 return true; 8007 } 8008 8009 auto opSlice() { return this; } 8010 8011 private: 8012 RangeOfRanges _input; 8013 } 8014 8015 @safe unittest 8016 { 8017 // Boundary case: transpose of empty range should be empty 8018 int[][] ror = []; 8019 assert(transposed(ror).empty); 8020 } 8021 8022 // https://issues.dlang.org/show_bug.cgi?id=9507 8023 @safe unittest 8024 { 8025 import std.algorithm.comparison : equal; 8026 8027 auto r = [[1,2], [3], [4,5], [], [6]]; 8028 assert(r.transposed.equal!equal([ 8029 [1, 3, 4, 6], 8030 [2, 5] 8031 ])); 8032 } 8033 8034 // https://issues.dlang.org/show_bug.cgi?id=17742 8035 @safe unittest 8036 { 8037 import std.algorithm.iteration : map; 8038 import std.algorithm.comparison : equal; 8039 auto ror = 5.iota.map!(y => 5.iota.map!(x => x * y).array).array; 8040 assert(ror[3][2] == 6); 8041 auto result = transposed!(TransverseOptions.assumeNotJagged)(ror); 8042 assert(result[2][3] == 6); 8043 8044 auto x = [[1,2,3],[4,5,6]]; 8045 auto y = transposed!(TransverseOptions.assumeNotJagged)(x); 8046 assert(y.front.equal([1,4])); 8047 assert(y[0].equal([1,4])); 8048 assert(y[0][0] == 1); 8049 assert(y[1].equal([2,5])); 8050 assert(y[1][1] == 5); 8051 8052 auto yy = transposed!(TransverseOptions.enforceNotJagged)(x); 8053 assert(yy.front.equal([1,4])); 8054 assert(yy[0].equal([1,4])); 8055 assert(yy[0][0] == 1); 8056 assert(yy[1].equal([2,5])); 8057 assert(yy[1][1] == 5); 8058 8059 auto z = x.transposed; // assumeJagged 8060 assert(z.front.equal([1,4])); 8061 assert(z[0].equal([1,4])); 8062 assert(!is(typeof(z[0][0]))); 8063 } 8064 8065 @safe unittest 8066 { 8067 import std.exception : assertThrown; 8068 8069 auto r = [[1,2], [3], [4,5], [], [6]]; 8070 assertThrown(r.transposed!(TransverseOptions.enforceNotJagged)); 8071 } 8072 8073 /** 8074 Given a range of ranges, returns a range of ranges where the $(I i)'th subrange 8075 contains the $(I i)'th elements of the original subranges. 8076 8077 Params: 8078 opt = Controls the assumptions the function makes about the lengths of the ranges (i.e. jagged or not) 8079 rr = Range of ranges 8080 */ 8081 Transposed!(RangeOfRanges, opt) transposed 8082 (TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) 8083 (RangeOfRanges rr) 8084 if (isForwardRange!RangeOfRanges && 8085 isInputRange!(ElementType!RangeOfRanges) && 8086 hasAssignableElements!RangeOfRanges) 8087 { 8088 return Transposed!(RangeOfRanges, opt)(rr); 8089 } 8090 8091 /// 8092 @safe unittest 8093 { 8094 import std.algorithm.comparison : equal; 8095 int[][] ror = [ 8096 [1, 2, 3], 8097 [4, 5, 6] 8098 ]; 8099 auto xp = transposed(ror); 8100 assert(equal!"a.equal(b)"(xp, [ 8101 [1, 4], 8102 [2, 5], 8103 [3, 6] 8104 ])); 8105 } 8106 8107 /// 8108 @safe unittest 8109 { 8110 int[][] x = new int[][2]; 8111 x[0] = [1, 2]; 8112 x[1] = [3, 4]; 8113 auto tr = transposed(x); 8114 int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ]; 8115 uint i; 8116 8117 foreach (e; tr) 8118 { 8119 assert(array(e) == witness[i++]); 8120 } 8121 } 8122 8123 // https://issues.dlang.org/show_bug.cgi?id=8764 8124 @safe unittest 8125 { 8126 import std.algorithm.comparison : equal; 8127 ulong[] t0 = [ 123 ]; 8128 8129 assert(!hasAssignableElements!(typeof(t0[].chunks(1)))); 8130 assert(!is(typeof(transposed(t0[].chunks(1))))); 8131 assert(is(typeof(transposed(t0[].chunks(1).array())))); 8132 8133 auto t1 = transposed(t0[].chunks(1).array()); 8134 assert(equal!"a.equal(b)"(t1, [[123]])); 8135 } 8136 8137 /** 8138 This struct takes two ranges, `source` and `indices`, and creates a view 8139 of `source` as if its elements were reordered according to `indices`. 8140 `indices` may include only a subset of the elements of `source` and 8141 may also repeat elements. 8142 8143 `Source` must be a random access range. The returned range will be 8144 bidirectional or random-access if `Indices` is bidirectional or 8145 random-access, respectively. 8146 */ 8147 struct Indexed(Source, Indices) 8148 if (isRandomAccessRange!Source && isInputRange!Indices && 8149 is(typeof(Source.init[ElementType!(Indices).init]))) 8150 { 8151 this(Source source, Indices indices) 8152 { 8153 this._source = source; 8154 this._indices = indices; 8155 } 8156 8157 /// Range primitives 8158 @property auto ref front() 8159 { 8160 assert(!empty, "Attempting to fetch the front of an empty Indexed"); 8161 return _source[_indices.front]; 8162 } 8163 8164 /// Ditto 8165 void popFront() 8166 { 8167 assert(!empty, "Attempting to popFront an empty Indexed"); 8168 _indices.popFront(); 8169 } 8170 8171 static if (isInfinite!Indices) 8172 { 8173 enum bool empty = false; 8174 } 8175 else 8176 { 8177 /// Ditto 8178 @property bool empty() 8179 { 8180 return _indices.empty; 8181 } 8182 } 8183 8184 static if (isForwardRange!Indices) 8185 { 8186 /// Ditto 8187 @property typeof(this) save() 8188 { 8189 // Don't need to save _source because it's never consumed. 8190 return typeof(this)(_source, _indices.save); 8191 } 8192 } 8193 8194 /// Ditto 8195 static if (hasAssignableElements!Source) 8196 { 8197 @property auto ref front(ElementType!Source newVal) 8198 { 8199 assert(!empty); 8200 return _source[_indices.front] = newVal; 8201 } 8202 } 8203 8204 8205 static if (hasMobileElements!Source) 8206 { 8207 /// Ditto 8208 auto moveFront() 8209 { 8210 assert(!empty); 8211 return _source.moveAt(_indices.front); 8212 } 8213 } 8214 8215 static if (isBidirectionalRange!Indices) 8216 { 8217 /// Ditto 8218 @property auto ref back() 8219 { 8220 assert(!empty, "Attempting to fetch the back of an empty Indexed"); 8221 return _source[_indices.back]; 8222 } 8223 8224 /// Ditto 8225 void popBack() 8226 { 8227 assert(!empty, "Attempting to popBack an empty Indexed"); 8228 _indices.popBack(); 8229 } 8230 8231 /// Ditto 8232 static if (hasAssignableElements!Source) 8233 { 8234 @property auto ref back(ElementType!Source newVal) 8235 { 8236 assert(!empty); 8237 return _source[_indices.back] = newVal; 8238 } 8239 } 8240 8241 8242 static if (hasMobileElements!Source) 8243 { 8244 /// Ditto 8245 auto moveBack() 8246 { 8247 assert(!empty); 8248 return _source.moveAt(_indices.back); 8249 } 8250 } 8251 } 8252 8253 mixin ImplementLength!_indices; 8254 8255 static if (isRandomAccessRange!Indices) 8256 { 8257 /// Ditto 8258 auto ref opIndex(size_t index) 8259 { 8260 return _source[_indices[index]]; 8261 } 8262 8263 static if (hasSlicing!Indices) 8264 { 8265 /// Ditto 8266 typeof(this) opSlice(size_t a, size_t b) 8267 { 8268 return typeof(this)(_source, _indices[a .. b]); 8269 } 8270 } 8271 8272 8273 static if (hasAssignableElements!Source) 8274 { 8275 /// Ditto 8276 auto opIndexAssign(ElementType!Source newVal, size_t index) 8277 { 8278 return _source[_indices[index]] = newVal; 8279 } 8280 } 8281 8282 8283 static if (hasMobileElements!Source) 8284 { 8285 /// Ditto 8286 auto moveAt(size_t index) 8287 { 8288 return _source.moveAt(_indices[index]); 8289 } 8290 } 8291 } 8292 8293 // All this stuff is useful if someone wants to index an Indexed 8294 // without adding a layer of indirection. 8295 8296 /** 8297 Returns the source range. 8298 */ 8299 @property Source source() 8300 { 8301 return _source; 8302 } 8303 8304 /** 8305 Returns the indices range. 8306 */ 8307 @property Indices indices() 8308 { 8309 return _indices; 8310 } 8311 8312 static if (isRandomAccessRange!Indices) 8313 { 8314 /** 8315 Returns the physical index into the source range corresponding to a 8316 given logical index. This is useful, for example, when indexing 8317 an `Indexed` without adding another layer of indirection. 8318 */ 8319 size_t physicalIndex(size_t logicalIndex) 8320 { 8321 return _indices[logicalIndex]; 8322 } 8323 8324 /// 8325 @safe unittest 8326 { 8327 auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); 8328 assert(ind.physicalIndex(0) == 1); 8329 } 8330 } 8331 8332 private: 8333 Source _source; 8334 Indices _indices; 8335 8336 } 8337 8338 /// Ditto 8339 Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indices) 8340 { 8341 return typeof(return)(source, indices); 8342 } 8343 8344 /// 8345 @safe unittest 8346 { 8347 import std.algorithm.comparison : equal; 8348 auto source = [1, 2, 3, 4, 5]; 8349 auto indices = [4, 3, 1, 2, 0, 4]; 8350 auto ind = indexed(source, indices); 8351 assert(equal(ind, [5, 4, 2, 3, 1, 5])); 8352 assert(equal(retro(ind), [5, 1, 3, 2, 4, 5])); 8353 } 8354 8355 @safe unittest 8356 { 8357 { 8358 auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); 8359 assert(ind.physicalIndex(0) == 1); 8360 } 8361 8362 auto source = [1, 2, 3, 4, 5]; 8363 auto indices = [4, 3, 1, 2, 0, 4]; 8364 auto ind = indexed(source, indices); 8365 8366 // When elements of indices are duplicated and Source has lvalue elements, 8367 // these are aliased in ind. 8368 ind[0]++; 8369 assert(ind[0] == 6); 8370 assert(ind[5] == 6); 8371 } 8372 8373 @safe unittest 8374 { 8375 import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, 8376 propagatesRangeType, RangeType; 8377 8378 foreach (DummyType; AllDummyRanges) 8379 { 8380 auto d = DummyType.init; 8381 auto r = indexed([1, 2, 3, 4, 5], d); 8382 static assert(propagatesRangeType!(DummyType, typeof(r))); 8383 static assert(propagatesLength!(DummyType, typeof(r))); 8384 } 8385 } 8386 8387 /** 8388 This range iterates over fixed-sized chunks of size `chunkSize` of a 8389 `source` range. `Source` must be an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). 8390 `chunkSize` must be greater than zero. 8391 8392 If `!isInfinite!Source` and `source.walkLength` is not evenly 8393 divisible by `chunkSize`, the back element of this range will contain 8394 fewer than `chunkSize` elements. 8395 8396 If `Source` is a forward range, the resulting range will be forward ranges as 8397 well. Otherwise, the resulting chunks will be input ranges consuming the same 8398 input: iterating over `front` will shrink the chunk such that subsequent 8399 invocations of `front` will no longer return the full chunk, and calling 8400 `popFront` on the outer range will invalidate any lingering references to 8401 previous values of `front`. 8402 8403 Params: 8404 source = Range from which the chunks will be selected 8405 chunkSize = Chunk size 8406 8407 See_Also: $(LREF slide) 8408 8409 Returns: Range of chunks. 8410 */ 8411 struct Chunks(Source) 8412 if (isInputRange!Source) 8413 { 8414 static if (isForwardRange!Source) 8415 { 8416 /// Standard constructor 8417 this(Source source, size_t chunkSize) 8418 { 8419 assert(chunkSize != 0, "Cannot create a Chunk with an empty chunkSize"); 8420 _source = source; 8421 _chunkSize = chunkSize; 8422 } 8423 8424 /// Input range primitives. Always present. 8425 @property auto front() 8426 { 8427 assert(!empty, "Attempting to fetch the front of an empty Chunks"); 8428 return _source.save.take(_chunkSize); 8429 } 8430 8431 /// Ditto 8432 void popFront() 8433 { 8434 assert(!empty, "Attempting to popFront and empty Chunks"); 8435 _source.popFrontN(_chunkSize); 8436 } 8437 8438 static if (!isInfinite!Source) 8439 /// Ditto 8440 @property bool empty() 8441 { 8442 return _source.empty; 8443 } 8444 else 8445 // undocumented 8446 enum empty = false; 8447 8448 /// Forward range primitives. Only present if `Source` is a forward range. 8449 @property typeof(this) save() 8450 { 8451 return typeof(this)(_source.save, _chunkSize); 8452 } 8453 8454 static if (hasLength!Source) 8455 { 8456 /// Length. Only if `hasLength!Source` is `true` 8457 @property size_t length() 8458 { 8459 // Note: _source.length + _chunkSize may actually overflow. 8460 // We cast to ulong to mitigate the problem on x86 machines. 8461 // For x64 machines, we just suppose we'll never overflow. 8462 // The "safe" code would require either an extra branch, or a 8463 // modulo operation, which is too expensive for such a rare case 8464 return cast(size_t)((cast(ulong)(_source.length) + _chunkSize - 1) / _chunkSize); 8465 } 8466 //Note: No point in defining opDollar here without slicing. 8467 //opDollar is defined below in the hasSlicing!Source section 8468 } 8469 8470 static if (hasSlicing!Source) 8471 { 8472 //Used for various purposes 8473 private enum hasSliceToEnd = is(typeof(Source.init[_chunkSize .. $]) == Source); 8474 8475 /** 8476 Indexing and slicing operations. Provided only if 8477 `hasSlicing!Source` is `true`. 8478 */ 8479 auto opIndex(size_t index) 8480 { 8481 immutable start = index * _chunkSize; 8482 immutable end = start + _chunkSize; 8483 8484 static if (isInfinite!Source) 8485 return _source[start .. end]; 8486 else 8487 { 8488 import std.algorithm.comparison : min; 8489 immutable len = _source.length; 8490 assert(start < len, "chunks index out of bounds"); 8491 return _source[start .. min(end, len)]; 8492 } 8493 } 8494 8495 /// Ditto 8496 static if (hasLength!Source) 8497 typeof(this) opSlice(size_t lower, size_t upper) 8498 { 8499 import std.algorithm.comparison : min; 8500 assert(lower <= upper && upper <= length, "chunks slicing index out of bounds"); 8501 immutable len = _source.length; 8502 return chunks(_source[min(lower * _chunkSize, len) .. min(upper * _chunkSize, len)], _chunkSize); 8503 } 8504 else static if (hasSliceToEnd) 8505 //For slicing an infinite chunk, we need to slice the source to the end. 8506 typeof(takeExactly(this, 0)) opSlice(size_t lower, size_t upper) 8507 { 8508 assert(lower <= upper, "chunks slicing index out of bounds"); 8509 return chunks(_source[lower * _chunkSize .. $], _chunkSize).takeExactly(upper - lower); 8510 } 8511 8512 static if (isInfinite!Source) 8513 { 8514 static if (hasSliceToEnd) 8515 { 8516 private static struct DollarToken{} 8517 DollarToken opDollar() 8518 { 8519 return DollarToken(); 8520 } 8521 //Slice to dollar 8522 typeof(this) opSlice(size_t lower, DollarToken) 8523 { 8524 return typeof(this)(_source[lower * _chunkSize .. $], _chunkSize); 8525 } 8526 } 8527 } 8528 else 8529 { 8530 //Dollar token carries a static type, with no extra information. 8531 //It can lazily transform into _source.length on algorithmic 8532 //operations such as : chunks[$/2, $-1]; 8533 private static struct DollarToken 8534 { 8535 Chunks!Source* mom; 8536 @property size_t momLength() 8537 { 8538 return mom.length; 8539 } 8540 alias momLength this; 8541 } 8542 DollarToken opDollar() 8543 { 8544 return DollarToken(&this); 8545 } 8546 8547 //Slice overloads optimized for using dollar. Without this, to slice to end, we would... 8548 //1. Evaluate chunks.length 8549 //2. Multiply by _chunksSize 8550 //3. To finally just compare it (with min) to the original length of source (!) 8551 //These overloads avoid that. 8552 typeof(this) opSlice(DollarToken, DollarToken) 8553 { 8554 static if (hasSliceToEnd) 8555 return chunks(_source[$ .. $], _chunkSize); 8556 else 8557 { 8558 immutable len = _source.length; 8559 return chunks(_source[len .. len], _chunkSize); 8560 } 8561 } 8562 typeof(this) opSlice(size_t lower, DollarToken) 8563 { 8564 import std.algorithm.comparison : min; 8565 assert(lower <= length, "chunks slicing index out of bounds"); 8566 static if (hasSliceToEnd) 8567 return chunks(_source[min(lower * _chunkSize, _source.length) .. $], _chunkSize); 8568 else 8569 { 8570 immutable len = _source.length; 8571 return chunks(_source[min(lower * _chunkSize, len) .. len], _chunkSize); 8572 } 8573 } 8574 typeof(this) opSlice(DollarToken, size_t upper) 8575 { 8576 assert(upper == length, "chunks slicing index out of bounds"); 8577 return this[$ .. $]; 8578 } 8579 } 8580 } 8581 8582 //Bidirectional range primitives 8583 static if (hasSlicing!Source && hasLength!Source) 8584 { 8585 /** 8586 Bidirectional range primitives. Provided only if both 8587 `hasSlicing!Source` and `hasLength!Source` are `true`. 8588 */ 8589 @property auto back() 8590 { 8591 assert(!empty, "back called on empty chunks"); 8592 immutable len = _source.length; 8593 immutable start = (len - 1) / _chunkSize * _chunkSize; 8594 return _source[start .. len]; 8595 } 8596 8597 /// Ditto 8598 void popBack() 8599 { 8600 assert(!empty, "popBack() called on empty chunks"); 8601 immutable end = (_source.length - 1) / _chunkSize * _chunkSize; 8602 _source = _source[0 .. end]; 8603 } 8604 } 8605 8606 private: 8607 Source _source; 8608 size_t _chunkSize; 8609 } 8610 else // is input range only 8611 { 8612 import std.typecons : RefCounted; 8613 8614 static struct Chunk 8615 { 8616 private RefCounted!Impl impl; 8617 8618 @property bool empty() { return impl.curSizeLeft == 0 || impl.r.empty; } 8619 @property auto front() { return impl.r.front; } 8620 void popFront() 8621 { 8622 assert(impl.curSizeLeft > 0 && !impl.r.empty); 8623 impl.curSizeLeft--; 8624 impl.r.popFront(); 8625 } 8626 } 8627 8628 static struct Impl 8629 { 8630 private Source r; 8631 private size_t chunkSize; 8632 private size_t curSizeLeft; 8633 } 8634 8635 private RefCounted!Impl impl; 8636 8637 private this(Source r, size_t chunkSize) 8638 { 8639 impl = RefCounted!Impl(r, r.empty ? 0 : chunkSize, chunkSize); 8640 } 8641 8642 @property bool empty() { return impl.chunkSize == 0; } 8643 @property Chunk front() return { return Chunk(impl); } 8644 8645 void popFront() 8646 { 8647 impl.curSizeLeft -= impl.r.popFrontN(impl.curSizeLeft); 8648 if (!impl.r.empty) 8649 impl.curSizeLeft = impl.chunkSize; 8650 else 8651 impl.chunkSize = 0; 8652 } 8653 8654 static assert(isInputRange!(typeof(this))); 8655 } 8656 } 8657 8658 /// Ditto 8659 Chunks!Source chunks(Source)(Source source, size_t chunkSize) 8660 if (isInputRange!Source) 8661 { 8662 return typeof(return)(source, chunkSize); 8663 } 8664 8665 /// 8666 @safe unittest 8667 { 8668 import std.algorithm.comparison : equal; 8669 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8670 auto chunks = chunks(source, 4); 8671 assert(chunks[0] == [1, 2, 3, 4]); 8672 assert(chunks[1] == [5, 6, 7, 8]); 8673 assert(chunks[2] == [9, 10]); 8674 assert(chunks.back == chunks[2]); 8675 assert(chunks.front == chunks[0]); 8676 assert(chunks.length == 3); 8677 assert(equal(retro(array(chunks)), array(retro(chunks)))); 8678 } 8679 8680 /// Non-forward input ranges are supported, but with limited semantics. 8681 @system /*@safe*/ unittest // FIXME: can't be @safe because RefCounted isn't. 8682 { 8683 import std.algorithm.comparison : equal; 8684 8685 int i; 8686 8687 // The generator doesn't save state, so it cannot be a forward range. 8688 auto inputRange = generate!(() => ++i).take(10); 8689 8690 // We can still process it in chunks, but it will be single-pass only. 8691 auto chunked = inputRange.chunks(2); 8692 8693 assert(chunked.front.equal([1, 2])); 8694 assert(chunked.front.empty); // Iterating the chunk has consumed it 8695 chunked.popFront; 8696 assert(chunked.front.equal([3, 4])); 8697 } 8698 8699 @system /*@safe*/ unittest 8700 { 8701 import std.algorithm.comparison : equal; 8702 import std.internal.test.dummyrange : ReferenceInputRange; 8703 8704 auto data = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 8705 auto r = new ReferenceInputRange!int(data).chunks(3); 8706 assert(r.equal!equal([ 8707 [ 1, 2, 3 ], 8708 [ 4, 5, 6 ], 8709 [ 7, 8, 9 ], 8710 [ 10 ] 8711 ])); 8712 8713 auto data2 = [ 1, 2, 3, 4, 5, 6 ]; 8714 auto r2 = new ReferenceInputRange!int(data2).chunks(3); 8715 assert(r2.equal!equal([ 8716 [ 1, 2, 3 ], 8717 [ 4, 5, 6 ] 8718 ])); 8719 8720 auto data3 = [ 1, 2, 3, 4, 5 ]; 8721 auto r3 = new ReferenceInputRange!int(data3).chunks(2); 8722 assert(r3.front.equal([1, 2])); 8723 r3.popFront(); 8724 assert(!r3.empty); 8725 r3.popFront(); 8726 assert(r3.front.equal([5])); 8727 } 8728 8729 @safe unittest 8730 { 8731 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8732 auto chunks = chunks(source, 4); 8733 auto chunks2 = chunks.save; 8734 chunks.popFront(); 8735 assert(chunks[0] == [5, 6, 7, 8]); 8736 assert(chunks[1] == [9, 10]); 8737 chunks2.popBack(); 8738 assert(chunks2[1] == [5, 6, 7, 8]); 8739 assert(chunks2.length == 2); 8740 8741 static assert(isRandomAccessRange!(typeof(chunks))); 8742 } 8743 8744 @safe unittest 8745 { 8746 import std.algorithm.comparison : equal; 8747 8748 //Extra toying with slicing and indexing. 8749 auto chunks1 = [0, 0, 1, 1, 2, 2, 3, 3, 4].chunks(2); 8750 auto chunks2 = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4].chunks(2); 8751 8752 assert(chunks1.length == 5); 8753 assert(chunks2.length == 5); 8754 assert(chunks1[4] == [4]); 8755 assert(chunks2[4] == [4, 4]); 8756 assert(chunks1.back == [4]); 8757 assert(chunks2.back == [4, 4]); 8758 8759 assert(chunks1[0 .. 1].equal([[0, 0]])); 8760 assert(chunks1[0 .. 2].equal([[0, 0], [1, 1]])); 8761 assert(chunks1[4 .. 5].equal([[4]])); 8762 assert(chunks2[4 .. 5].equal([[4, 4]])); 8763 8764 assert(chunks1[0 .. 0].equal((int[][]).init)); 8765 assert(chunks1[5 .. 5].equal((int[][]).init)); 8766 assert(chunks2[5 .. 5].equal((int[][]).init)); 8767 8768 //Fun with opDollar 8769 assert(chunks1[$ .. $].equal((int[][]).init)); //Quick 8770 assert(chunks2[$ .. $].equal((int[][]).init)); //Quick 8771 assert(chunks1[$ - 1 .. $].equal([[4]])); //Semiquick 8772 assert(chunks2[$ - 1 .. $].equal([[4, 4]])); //Semiquick 8773 assert(chunks1[$ .. 5].equal((int[][]).init)); //Semiquick 8774 assert(chunks2[$ .. 5].equal((int[][]).init)); //Semiquick 8775 8776 assert(chunks1[$ / 2 .. $ - 1].equal([[2, 2], [3, 3]])); //Slow 8777 } 8778 8779 @safe unittest 8780 { 8781 import std.algorithm.comparison : equal; 8782 import std.algorithm.iteration : filter; 8783 8784 //ForwardRange 8785 auto r = filter!"true"([1, 2, 3, 4, 5]).chunks(2); 8786 assert(equal!"equal(a, b)"(r, [[1, 2], [3, 4], [5]])); 8787 8788 //InfiniteRange w/o RA 8789 auto fibsByPairs = recurrence!"a[n-1] + a[n-2]"(1, 1).chunks(2); 8790 assert(equal!`equal(a, b)`(fibsByPairs.take(2), [[ 1, 1], [ 2, 3]])); 8791 8792 //InfiniteRange w/ RA and slicing 8793 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 8794 auto oddsByPairs = odds.chunks(2); 8795 assert(equal!`equal(a, b)`(oddsByPairs.take(2), [[ 1, 3], [ 5, 7]])); 8796 8797 //Requires phobos#991 for Sequence to have slice to end 8798 static assert(hasSlicing!(typeof(odds))); 8799 assert(equal!`equal(a, b)`(oddsByPairs[3 .. 5], [[13, 15], [17, 19]])); 8800 assert(equal!`equal(a, b)`(oddsByPairs[3 .. $].take(2), [[13, 15], [17, 19]])); 8801 } 8802 8803 8804 8805 /** 8806 This range splits a `source` range into `chunkCount` chunks of 8807 approximately equal length. `Source` must be a forward range with 8808 known length. 8809 8810 Unlike $(LREF chunks), `evenChunks` takes a chunk count (not size). 8811 The returned range will contain zero or more $(D source.length / 8812 chunkCount + 1) elements followed by $(D source.length / chunkCount) 8813 elements. If $(D source.length < chunkCount), some chunks will be empty. 8814 8815 `chunkCount` must not be zero, unless `source` is also empty. 8816 */ 8817 struct EvenChunks(Source) 8818 if (isForwardRange!Source && hasLength!Source) 8819 { 8820 /// Standard constructor 8821 this(Source source, size_t chunkCount) 8822 { 8823 assert(chunkCount != 0 || source.empty, "Cannot create EvenChunks with a zero chunkCount"); 8824 _source = source; 8825 _chunkCount = chunkCount; 8826 } 8827 8828 /// Forward range primitives. Always present. 8829 @property auto front() 8830 { 8831 assert(!empty, "Attempting to fetch the front of an empty evenChunks"); 8832 return _source.save.take(_chunkPos(1)); 8833 } 8834 8835 /// Ditto 8836 void popFront() 8837 { 8838 assert(!empty, "Attempting to popFront an empty evenChunks"); 8839 _source.popFrontN(_chunkPos(1)); 8840 _chunkCount--; 8841 } 8842 8843 /// Ditto 8844 @property bool empty() 8845 { 8846 return _chunkCount == 0; 8847 } 8848 8849 /// Ditto 8850 @property typeof(this) save() 8851 { 8852 return typeof(this)(_source.save, _chunkCount); 8853 } 8854 8855 /// Length 8856 @property size_t length() const 8857 { 8858 return _chunkCount; 8859 } 8860 //Note: No point in defining opDollar here without slicing. 8861 //opDollar is defined below in the hasSlicing!Source section 8862 8863 static if (hasSlicing!Source) 8864 { 8865 /** 8866 Indexing, slicing and bidirectional operations and range primitives. 8867 Provided only if `hasSlicing!Source` is `true`. 8868 */ 8869 auto opIndex(size_t index) 8870 { 8871 assert(index < _chunkCount, "evenChunks index out of bounds"); 8872 return _source[_chunkPos(index) .. _chunkPos(index+1)]; 8873 } 8874 8875 /// Ditto 8876 typeof(this) opSlice(size_t lower, size_t upper) 8877 { 8878 assert(lower <= upper && upper <= length, "evenChunks slicing index out of bounds"); 8879 return evenChunks(_source[_chunkPos(lower) .. _chunkPos(upper)], upper - lower); 8880 } 8881 8882 /// Ditto 8883 @property auto back() 8884 { 8885 assert(!empty, "back called on empty evenChunks"); 8886 return _source[_chunkPos(_chunkCount - 1) .. _source.length]; 8887 } 8888 8889 /// Ditto 8890 void popBack() 8891 { 8892 assert(!empty, "popBack() called on empty evenChunks"); 8893 _source = _source[0 .. _chunkPos(_chunkCount - 1)]; 8894 _chunkCount--; 8895 } 8896 } 8897 8898 private: 8899 Source _source; 8900 size_t _chunkCount; 8901 8902 size_t _chunkPos(size_t i) 8903 { 8904 /* 8905 _chunkCount = 5, _source.length = 13: 8906 8907 chunk0 8908 | chunk3 8909 | | 8910 v v 8911 +-+-+-+-+-+ ^ 8912 |0|3|.| | | | 8913 +-+-+-+-+-+ | div 8914 |1|4|.| | | | 8915 +-+-+-+-+-+ v 8916 |2|5|.| 8917 +-+-+-+ 8918 8919 <-----> 8920 mod 8921 8922 <---------> 8923 _chunkCount 8924 8925 One column is one chunk. 8926 popFront and popBack pop the left-most 8927 and right-most column, respectively. 8928 */ 8929 8930 auto div = _source.length / _chunkCount; 8931 auto mod = _source.length % _chunkCount; 8932 auto pos = i <= mod 8933 ? i * (div+1) 8934 : mod * (div+1) + (i-mod) * div 8935 ; 8936 //auto len = i < mod 8937 // ? div+1 8938 // : div 8939 //; 8940 return pos; 8941 } 8942 } 8943 8944 /// Ditto 8945 EvenChunks!Source evenChunks(Source)(Source source, size_t chunkCount) 8946 if (isForwardRange!Source && hasLength!Source) 8947 { 8948 return typeof(return)(source, chunkCount); 8949 } 8950 8951 /// 8952 @safe unittest 8953 { 8954 import std.algorithm.comparison : equal; 8955 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8956 auto chunks = evenChunks(source, 3); 8957 assert(chunks[0] == [1, 2, 3, 4]); 8958 assert(chunks[1] == [5, 6, 7]); 8959 assert(chunks[2] == [8, 9, 10]); 8960 } 8961 8962 @safe unittest 8963 { 8964 import std.algorithm.comparison : equal; 8965 8966 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8967 auto chunks = evenChunks(source, 3); 8968 assert(chunks.back == chunks[2]); 8969 assert(chunks.front == chunks[0]); 8970 assert(chunks.length == 3); 8971 assert(equal(retro(array(chunks)), array(retro(chunks)))); 8972 8973 auto chunks2 = chunks.save; 8974 chunks.popFront(); 8975 assert(chunks[0] == [5, 6, 7]); 8976 assert(chunks[1] == [8, 9, 10]); 8977 chunks2.popBack(); 8978 assert(chunks2[1] == [5, 6, 7]); 8979 assert(chunks2.length == 2); 8980 8981 static assert(isRandomAccessRange!(typeof(chunks))); 8982 } 8983 8984 @safe unittest 8985 { 8986 import std.algorithm.comparison : equal; 8987 8988 int[] source = []; 8989 auto chunks = source.evenChunks(0); 8990 assert(chunks.length == 0); 8991 chunks = source.evenChunks(3); 8992 assert(equal(chunks, [[], [], []])); 8993 chunks = [1, 2, 3].evenChunks(5); 8994 assert(equal(chunks, [[1], [2], [3], [], []])); 8995 } 8996 8997 /** 8998 A fixed-sized sliding window iteration 8999 of size `windowSize` over a `source` range by a custom `stepSize`. 9000 9001 The `Source` range must be at least a $(REF_ALTTEXT ForwardRange, isForwardRange, std,range,primitives) 9002 and the `windowSize` must be greater than zero. 9003 9004 For `windowSize = 1` it splits the range into single element groups (aka `unflatten`) 9005 For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`. 9006 9007 Params: 9008 f = Whether the last element has fewer elements than `windowSize` 9009 it should be be ignored (`No.withPartial`) or added (`Yes.withPartial`) 9010 source = Range from which the slide will be selected 9011 windowSize = Sliding window size 9012 stepSize = Steps between the windows (by default 1) 9013 9014 Returns: Range of all sliding windows with propagated bi-directionality, 9015 forwarding, random access, and slicing. 9016 9017 Note: To avoid performance overhead, $(REF_ALTTEXT bi-directionality, isBidirectionalRange, std,range,primitives) 9018 is only available when $(REF hasSlicing, std,range,primitives) 9019 and $(REF hasLength, std,range,primitives) are true. 9020 9021 See_Also: $(LREF chunks) 9022 */ 9023 auto slide(Flag!"withPartial" f = Yes.withPartial, 9024 Source)(Source source, size_t windowSize, size_t stepSize = 1) 9025 if (isForwardRange!Source) 9026 { 9027 return Slides!(f, Source)(source, windowSize, stepSize); 9028 } 9029 9030 /// Iterate over ranges with windows 9031 @safe pure nothrow unittest 9032 { 9033 import std.algorithm.comparison : equal; 9034 9035 assert([0, 1, 2, 3].slide(2).equal!equal( 9036 [[0, 1], [1, 2], [2, 3]] 9037 )); 9038 9039 assert(5.iota.slide(3).equal!equal( 9040 [[0, 1, 2], [1, 2, 3], [2, 3, 4]] 9041 )); 9042 } 9043 9044 /// set a custom stepsize (default 1) 9045 @safe pure nothrow unittest 9046 { 9047 import std.algorithm.comparison : equal; 9048 9049 assert(6.iota.slide(1, 2).equal!equal( 9050 [[0], [2], [4]] 9051 )); 9052 9053 assert(6.iota.slide(2, 4).equal!equal( 9054 [[0, 1], [4, 5]] 9055 )); 9056 9057 assert(iota(7).slide(2, 2).equal!equal( 9058 [[0, 1], [2, 3], [4, 5], [6]] 9059 )); 9060 9061 assert(iota(12).slide(2, 4).equal!equal( 9062 [[0, 1], [4, 5], [8, 9]] 9063 )); 9064 } 9065 9066 /// Allow the last slide to have fewer elements than windowSize 9067 @safe pure nothrow unittest 9068 { 9069 import std.algorithm.comparison : equal; 9070 9071 assert(3.iota.slide!(No.withPartial)(4).empty); 9072 assert(3.iota.slide!(Yes.withPartial)(4).equal!equal( 9073 [[0, 1, 2]] 9074 )); 9075 } 9076 9077 /// Count all the possible substrings of length 2 9078 @safe pure nothrow unittest 9079 { 9080 import std.algorithm.iteration : each; 9081 9082 int[dstring] d; 9083 "AGAGA"d.slide!(Yes.withPartial)(2).each!(a => d[a]++); 9084 assert(d == ["AG"d: 2, "GA"d: 2]); 9085 } 9086 9087 /// withPartial only has an effect if last element in the range doesn't have the full size 9088 @safe pure nothrow unittest 9089 { 9090 import std.algorithm.comparison : equal; 9091 9092 assert(5.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4]])); 9093 assert(6.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5]])); 9094 assert(7.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]])); 9095 9096 assert(5.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]])); 9097 assert(6.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]])); 9098 assert(7.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]])); 9099 } 9100 9101 private struct Slides(Flag!"withPartial" withPartial = Yes.withPartial, Source) 9102 if (isForwardRange!Source) 9103 { 9104 private: 9105 Source source; 9106 size_t windowSize; 9107 size_t stepSize; 9108 9109 static if (hasLength!Source) 9110 { 9111 enum needsEndTracker = false; 9112 } 9113 else 9114 { 9115 // If there's no information about the length, track needs to be kept manually 9116 Source nextSource; 9117 enum needsEndTracker = true; 9118 } 9119 9120 bool _empty; 9121 9122 static if (hasSlicing!Source) 9123 enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source); 9124 9125 static if (withPartial) 9126 bool hasShownPartialBefore; 9127 9128 public: 9129 /// Standard constructor 9130 this(Source source, size_t windowSize, size_t stepSize) 9131 { 9132 assert(windowSize > 0, "windowSize must be greater than zero"); 9133 assert(stepSize > 0, "stepSize must be greater than zero"); 9134 this.source = source; 9135 this.windowSize = windowSize; 9136 this.stepSize = stepSize; 9137 9138 static if (needsEndTracker) 9139 { 9140 // `nextSource` is used to "look one step into the future" and check for the end 9141 // this means `nextSource` is advanced by `stepSize` on every `popFront` 9142 nextSource = source.save; 9143 auto poppedElems = nextSource.popFrontN(windowSize); 9144 } 9145 9146 if (source.empty) 9147 { 9148 _empty = true; 9149 return; 9150 } 9151 9152 static if (withPartial) 9153 { 9154 static if (needsEndTracker) 9155 { 9156 if (nextSource.empty) 9157 hasShownPartialBefore = true; 9158 } 9159 else 9160 { 9161 if (source.length <= windowSize) 9162 hasShownPartialBefore = true; 9163 } 9164 } 9165 else 9166 { 9167 // empty source range is needed, s.t. length, slicing etc. works properly 9168 static if (needsEndTracker) 9169 { 9170 if (poppedElems < windowSize) 9171 _empty = true; 9172 } 9173 else 9174 { 9175 if (source.length < windowSize) 9176 _empty = true; 9177 } 9178 } 9179 } 9180 9181 /// Forward range primitives. Always present. 9182 @property auto front() 9183 { 9184 assert(!empty, "Attempting to access front on an empty slide."); 9185 static if (hasSlicing!Source && hasLength!Source) 9186 { 9187 static if (withPartial) 9188 { 9189 import std.algorithm.comparison : min; 9190 return source[0 .. min(windowSize, source.length)]; 9191 } 9192 else 9193 { 9194 assert(windowSize <= source.length, "The last element is smaller than the current windowSize."); 9195 return source[0 .. windowSize]; 9196 } 9197 } 9198 else 9199 { 9200 static if (withPartial) 9201 return source.save.take(windowSize); 9202 else 9203 return source.save.takeExactly(windowSize); 9204 } 9205 } 9206 9207 /// Ditto 9208 void popFront() 9209 { 9210 assert(!empty, "Attempting to call popFront() on an empty slide."); 9211 source.popFrontN(stepSize); 9212 9213 if (source.empty) 9214 { 9215 _empty = true; 9216 return; 9217 } 9218 9219 static if (withPartial) 9220 { 9221 if (hasShownPartialBefore) 9222 _empty = true; 9223 } 9224 9225 static if (needsEndTracker) 9226 { 9227 // Check the upcoming slide 9228 auto poppedElements = nextSource.popFrontN(stepSize); 9229 static if (withPartial) 9230 { 9231 if (poppedElements < stepSize || nextSource.empty) 9232 hasShownPartialBefore = true; 9233 } 9234 else 9235 { 9236 if (poppedElements < stepSize) 9237 _empty = true; 9238 } 9239 } 9240 else 9241 { 9242 static if (withPartial) 9243 { 9244 if (source.length <= windowSize) 9245 hasShownPartialBefore = true; 9246 } 9247 else 9248 { 9249 if (source.length < windowSize) 9250 _empty = true; 9251 } 9252 } 9253 } 9254 9255 static if (!isInfinite!Source) 9256 { 9257 /// Ditto 9258 @property bool empty() const 9259 { 9260 return _empty; 9261 } 9262 } 9263 else 9264 { 9265 // undocumented 9266 enum empty = false; 9267 } 9268 9269 /// Ditto 9270 @property typeof(this) save() 9271 { 9272 return typeof(this)(source.save, windowSize, stepSize); 9273 } 9274 9275 static if (hasLength!Source) 9276 { 9277 // gaps between the last element and the end of the range 9278 private size_t gap() 9279 { 9280 /* 9281 * Note: 9282 * - In the following `end` is the exclusive end as used in opSlice 9283 * - For the trivial case with `stepSize = 1` `end` is at `len`: 9284 * 9285 * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3]] (end = 4) 9286 * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4) 9287 * 9288 * - For the non-trivial cases, we need to calculate the gap 9289 * between `len` and `end` - this is the number of missing elements 9290 * from the input range: 9291 * 9292 * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || <gap: 2> 6 9293 * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || <gap: 1> 6 9294 * iota(7).slide(1, 5) = [[0], [5]] || <gap: 1> 6 9295 * 9296 * As it can be seen `gap` can be at most `stepSize - 1` 9297 * More generally the elements of the sliding window with 9298 * `w = windowSize` and `s = stepSize` are: 9299 * 9300 * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w] 9301 * 9302 * We can thus calculate the gap between the `end` and `len` as: 9303 * 9304 * gap = len - (n * s + w) = len - w - (n * s) 9305 * 9306 * As we aren't interested in exact value of `n`, but the best 9307 * minimal `gap` value, we can use modulo to "cut" `len - w` optimally: 9308 * 9309 * gap = len - w - (s - s ... - s) = (len - w) % s 9310 * 9311 * So for example: 9312 * 9313 * iota(7).slide(2, 3) = [[0, 1], [3, 4]] 9314 * gap: (7 - 2) % 3 = 5 % 3 = 2 9315 * end: 7 - 2 = 5 9316 * 9317 * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]] 9318 * gap: (7 - 4) % 2 = 3 % 2 = 1 9319 * end: 7 - 1 = 6 9320 */ 9321 return (source.length - windowSize) % stepSize; 9322 } 9323 9324 private size_t numberOfFullFrames() 9325 { 9326 /** 9327 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4) 9328 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (3) 9329 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (2) 9330 6.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5] (2) 9331 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (2) 9332 9333 As the last window is only added iff its complete, 9334 we don't count the last window except if it's full due to integer rounding. 9335 */ 9336 return 1 + (source.length - windowSize) / stepSize; 9337 } 9338 9339 // Whether the last slide frame size is less than windowSize 9340 private bool hasPartialElements() 9341 { 9342 static if (withPartial) 9343 return gap != 0 && source.length > numberOfFullFrames * stepSize; 9344 else 9345 return 0; 9346 } 9347 9348 /// Length. Only if `hasLength!Source` is `true` 9349 @property size_t length() 9350 { 9351 if (source.length < windowSize) 9352 { 9353 static if (withPartial) 9354 return source.length > 0; 9355 else 9356 return 0; 9357 } 9358 else 9359 { 9360 /*** 9361 We bump the pointer by stepSize for every element. 9362 If withPartial, we don't count the last element if its size 9363 isn't windowSize 9364 9365 At most: 9366 [p, p + stepSize, ..., p + stepSize * n] 9367 9368 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4) 9369 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (4) 9370 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (3) 9371 7.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5, 6] (3) 9372 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (3) 9373 */ 9374 return numberOfFullFrames + hasPartialElements; 9375 } 9376 } 9377 } 9378 9379 static if (hasSlicing!Source) 9380 { 9381 /** 9382 Indexing and slicing operations. Provided only if 9383 `hasSlicing!Source` is `true`. 9384 */ 9385 auto opIndex(size_t index) 9386 { 9387 immutable start = index * stepSize; 9388 9389 static if (isInfinite!Source) 9390 { 9391 immutable end = start + windowSize; 9392 } 9393 else 9394 { 9395 import std.algorithm.comparison : min; 9396 9397 immutable len = source.length; 9398 assert(start < len, "slide index out of bounds"); 9399 immutable end = min(start + windowSize, len); 9400 } 9401 9402 return source[start .. end]; 9403 } 9404 9405 static if (!isInfinite!Source) 9406 { 9407 /// ditto 9408 typeof(this) opSlice(size_t lower, size_t upper) 9409 { 9410 import std.algorithm.comparison : min; 9411 9412 assert(upper <= length, "slide slicing index out of bounds"); 9413 assert(lower <= upper, "slide slicing index out of bounds"); 9414 9415 lower *= stepSize; 9416 upper *= stepSize; 9417 9418 immutable len = source.length; 9419 9420 static if (withPartial) 9421 { 9422 import std.algorithm.comparison : max; 9423 9424 if (lower == upper) 9425 return this[$ .. $]; 9426 9427 /* 9428 A) If `stepSize` >= `windowSize` => `rightPos = upper` 9429 9430 [0, 1, 2, 3, 4, 5, 6].slide(2, 3) -> s = [[0, 1], [3, 4], [6]] 9431 rightPos for s[0 .. 2]: (upper=2) * (stepSize=3) = 6 9432 6.iota.slide(2, 3) = [[0, 1], [3, 4]] 9433 9434 B) If `stepSize` < `windowSize` => add `windowSize - stepSize` to `upper` 9435 9436 [0, 1, 2, 3].slide(2) = [[0, 1], [1, 2], [2, 3]] 9437 rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) = 1 9438 1.iota.slide(2) = [[0]] 9439 9440 rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) + (windowSize-stepSize=1) = 2 9441 1.iota.slide(2) = [[0, 1]] 9442 9443 More complex: 9444 9445 20.iota.slide(7, 6)[0 .. 2] 9446 rightPos: (upper=2) * (stepSize=6) = 12.iota 9447 12.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11]] 9448 9449 Now we add up for the difference between `windowSize` and `stepSize`: 9450 9451 rightPos: (upper=2) * (stepSize=6) + (windowSize-stepSize=1) = 13.iota 9452 13.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11, 12]] 9453 */ 9454 immutable rightPos = min(len, upper + max(0, windowSize - stepSize)); 9455 } 9456 else 9457 { 9458 /* 9459 After we have normalized `lower` and `upper` by `stepSize`, 9460 we only need to look at the case of `stepSize=1`. 9461 As `leftPos`, is equal to `lower`, we will only look `rightPos`. 9462 Notice that starting from `upper`, 9463 we only need to move for `windowSize - 1` to the right: 9464 9465 - [0, 1, 2, 3].slide(2) -> s = [[0, 1], [1, 2], [2, 3]] 9466 rightPos for s[0 .. 3]: (upper=3) + (windowSize=2) - 1 = 4 9467 9468 - [0, 1, 2, 3].slide(3) -> s = [[0, 1, 2], [1, 2, 3]] 9469 rightPos for s[0 .. 2]: (upper=2) + (windowSize=3) - 1 = 4 9470 9471 - [0, 1, 2, 3, 4].slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] 9472 rightPos for s[0 .. 2]: (upper=2) + (windowSize=4) - 1 = 5 9473 */ 9474 immutable rightPos = min(upper + windowSize - 1, len); 9475 } 9476 9477 return typeof(this)(source[min(lower, len) .. rightPos], windowSize, stepSize); 9478 } 9479 } 9480 else static if (hasSliceToEnd) 9481 { 9482 // For slicing an infinite chunk, we need to slice the source to the infinite end. 9483 auto opSlice(size_t lower, size_t upper) 9484 { 9485 assert(lower <= upper, "slide slicing index out of bounds"); 9486 return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize) 9487 .takeExactly(upper - lower); 9488 } 9489 } 9490 9491 static if (isInfinite!Source) 9492 { 9493 static if (hasSliceToEnd) 9494 { 9495 private static struct DollarToken{} 9496 DollarToken opDollar() 9497 { 9498 return DollarToken(); 9499 } 9500 //Slice to dollar 9501 typeof(this) opSlice(size_t lower, DollarToken) 9502 { 9503 return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize); 9504 } 9505 } 9506 } 9507 else 9508 { 9509 // Dollar token carries a static type, with no extra information. 9510 // It can lazily transform into source.length on algorithmic 9511 // operations such as : slide[$/2, $-1]; 9512 private static struct DollarToken 9513 { 9514 private size_t _length; 9515 alias _length this; 9516 } 9517 9518 DollarToken opDollar() 9519 { 9520 return DollarToken(this.length); 9521 } 9522 9523 // Optimized slice overloads optimized for using dollar. 9524 typeof(this) opSlice(DollarToken, DollarToken) 9525 { 9526 static if (hasSliceToEnd) 9527 { 9528 return typeof(this)(source[$ .. $], windowSize, stepSize); 9529 } 9530 else 9531 { 9532 immutable len = source.length; 9533 return typeof(this)(source[len .. len], windowSize, stepSize); 9534 } 9535 } 9536 9537 // Optimized slice overloads optimized for using dollar. 9538 typeof(this) opSlice(size_t lower, DollarToken) 9539 { 9540 import std.algorithm.comparison : min; 9541 assert(lower <= length, "slide slicing index out of bounds"); 9542 lower *= stepSize; 9543 static if (hasSliceToEnd) 9544 { 9545 return typeof(this)(source[min(lower, source.length) .. $], windowSize, stepSize); 9546 } 9547 else 9548 { 9549 immutable len = source.length; 9550 return typeof(this)(source[min(lower, len) .. len], windowSize, stepSize); 9551 } 9552 } 9553 9554 // Optimized slice overloads optimized for using dollar. 9555 typeof(this) opSlice(DollarToken, size_t upper) 9556 { 9557 assert(upper == length, "slide slicing index out of bounds"); 9558 return this[$ .. $]; 9559 } 9560 } 9561 9562 // Bidirectional range primitives 9563 static if (!isInfinite!Source) 9564 { 9565 /** 9566 Bidirectional range primitives. Provided only if both 9567 `hasSlicing!Source` and `!isInfinite!Source` are `true`. 9568 */ 9569 @property auto back() 9570 { 9571 import std.algorithm.comparison : max; 9572 9573 assert(!empty, "Attempting to access front on an empty slide"); 9574 9575 immutable len = source.length; 9576 9577 static if (withPartial) 9578 { 9579 if (source.length <= windowSize) 9580 return source[0 .. source.length]; 9581 9582 if (hasPartialElements) 9583 return source[numberOfFullFrames * stepSize .. len]; 9584 } 9585 9586 // check for underflow 9587 immutable start = (len > windowSize + gap) ? len - windowSize - gap : 0; 9588 return source[start .. len - gap]; 9589 } 9590 9591 /// Ditto 9592 void popBack() 9593 { 9594 assert(!empty, "Attempting to call popBack() on an empty slide"); 9595 9596 // Move by stepSize 9597 immutable end = source.length > stepSize ? source.length - stepSize : 0; 9598 9599 static if (withPartial) 9600 { 9601 if (hasShownPartialBefore || source.empty) 9602 { 9603 _empty = true; 9604 return; 9605 } 9606 9607 // pop by stepSize, except for the partial frame at the end 9608 if (hasPartialElements) 9609 source = source[0 .. source.length - gap]; 9610 else 9611 source = source[0 .. end]; 9612 } 9613 else 9614 { 9615 source = source[0 .. end]; 9616 } 9617 9618 if (source.length < windowSize) 9619 _empty = true; 9620 } 9621 } 9622 } 9623 } 9624 9625 // test @nogc 9626 @safe pure nothrow @nogc unittest 9627 { 9628 import std.algorithm.comparison : equal; 9629 9630 static immutable res1 = [[0], [1], [2], [3]]; 9631 assert(4.iota.slide!(Yes.withPartial)(1).equal!equal(res1)); 9632 9633 static immutable res2 = [[0, 1], [1, 2], [2, 3]]; 9634 assert(4.iota.slide!(Yes.withPartial)(2).equal!equal(res2)); 9635 } 9636 9637 // test different window sizes 9638 @safe pure nothrow unittest 9639 { 9640 import std.array : array; 9641 import std.algorithm.comparison : equal; 9642 9643 assert([0, 1, 2, 3].slide!(Yes.withPartial)(1).array == [[0], [1], [2], [3]]); 9644 assert([0, 1, 2, 3].slide!(Yes.withPartial)(2).array == [[0, 1], [1, 2], [2, 3]]); 9645 assert([0, 1, 2, 3].slide!(Yes.withPartial)(3).array == [[0, 1, 2], [1, 2, 3]]); 9646 assert([0, 1, 2, 3].slide!(Yes.withPartial)(4).array == [[0, 1, 2, 3]]); 9647 assert([0, 1, 2, 3].slide!(No.withPartial)(5).walkLength == 0); 9648 assert([0, 1, 2, 3].slide!(Yes.withPartial)(5).array == [[0, 1, 2, 3]]); 9649 9650 assert(iota(2).slide!(Yes.withPartial)(2).front.equal([0, 1])); 9651 assert(iota(3).slide!(Yes.withPartial)(2).equal!equal([[0, 1],[1, 2]])); 9652 assert(iota(3).slide!(Yes.withPartial)(3).equal!equal([[0, 1, 2]])); 9653 assert(iota(3).slide!(No.withPartial)(4).walkLength == 0); 9654 assert(iota(3).slide!(Yes.withPartial)(4).equal!equal([[0, 1, 2]])); 9655 assert(iota(1, 4).slide!(Yes.withPartial)(1).equal!equal([[1], [2], [3]])); 9656 assert(iota(1, 4).slide!(Yes.withPartial)(3).equal!equal([[1, 2, 3]])); 9657 } 9658 9659 // test combinations 9660 @safe pure nothrow unittest 9661 { 9662 import std.algorithm.comparison : equal; 9663 import std.typecons : tuple; 9664 9665 alias t = tuple; 9666 auto list = [ 9667 t(t(1, 1), [[0], [1], [2], [3], [4], [5]]), 9668 t(t(1, 2), [[0], [2], [4]]), 9669 t(t(1, 3), [[0], [3]]), 9670 t(t(1, 4), [[0], [4]]), 9671 t(t(1, 5), [[0], [5]]), 9672 t(t(2, 1), [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]), 9673 t(t(2, 2), [[0, 1], [2, 3], [4, 5]]), 9674 t(t(2, 3), [[0, 1], [3, 4]]), 9675 t(t(2, 4), [[0, 1], [4, 5]]), 9676 t(t(3, 1), [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]), 9677 t(t(3, 3), [[0, 1, 2], [3, 4, 5]]), 9678 t(t(4, 1), [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]), 9679 t(t(4, 2), [[0, 1, 2, 3], [2, 3, 4, 5]]), 9680 t(t(5, 1), [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]]), 9681 ]; 9682 9683 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9684 foreach (e; list) 9685 assert(6.iota.slide!Partial(e[0].expand).equal!equal(e[1])); 9686 9687 auto listSpecial = [ 9688 t(t(2, 5), [[0, 1], [5]]), 9689 t(t(3, 2), [[0, 1, 2], [2, 3, 4], [4, 5]]), 9690 t(t(3, 4), [[0, 1, 2], [4, 5]]), 9691 t(t(4, 3), [[0, 1, 2, 3], [3, 4, 5]]), 9692 t(t(5, 2), [[0, 1, 2, 3, 4], [2, 3, 4, 5]]), 9693 t(t(5, 3), [[0, 1, 2, 3, 4], [3, 4, 5]]), 9694 ]; 9695 foreach (e; listSpecial) 9696 { 9697 assert(6.iota.slide!(Yes.withPartial)(e[0].expand).equal!equal(e[1])); 9698 assert(6.iota.slide!(No.withPartial)(e[0].expand).equal!equal(e[1].dropBackOne)); 9699 } 9700 } 9701 9702 // test emptiness and copyability 9703 @safe pure nothrow unittest 9704 { 9705 import std.algorithm.comparison : equal; 9706 import std.algorithm.iteration : map; 9707 9708 // check with empty input 9709 int[] d; 9710 assert(d.slide!(Yes.withPartial)(2).empty); 9711 assert(d.slide!(Yes.withPartial)(2, 2).empty); 9712 9713 // is copyable? 9714 auto e = iota(5).slide!(Yes.withPartial)(2); 9715 e.popFront; 9716 assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); 9717 assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); 9718 assert(e.map!"a.array".array == [[1, 2], [2, 3], [3, 4]]); 9719 } 9720 9721 // test with strings 9722 @safe pure nothrow unittest 9723 { 9724 import std.algorithm.iteration : each; 9725 9726 int[dstring] f; 9727 "AGAGA"d.slide!(Yes.withPartial)(3).each!(a => f[a]++); 9728 assert(f == ["AGA"d: 2, "GAG"d: 1]); 9729 9730 int[dstring] g; 9731 "ABCDEFG"d.slide!(Yes.withPartial)(3, 3).each!(a => g[a]++); 9732 assert(g == ["ABC"d:1, "DEF"d:1, "G": 1]); 9733 g = null; 9734 "ABCDEFG"d.slide!(No.withPartial)(3, 3).each!(a => g[a]++); 9735 assert(g == ["ABC"d:1, "DEF"d:1]); 9736 } 9737 9738 // test with utf8 strings 9739 @safe unittest 9740 { 9741 import std.stdio; 9742 import std.algorithm.comparison : equal; 9743 9744 assert("ä.ö.ü.".slide!(Yes.withPartial)(3, 2).equal!equal(["ä.ö", "ö.ü", "ü."])); 9745 assert("ä.ö.ü.".slide!(No.withPartial)(3, 2).equal!equal(["ä.ö", "ö.ü"])); 9746 9747 "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]); 9748 "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]); 9749 "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇", "😈"]); 9750 "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇"]); 9751 } 9752 9753 // test length 9754 @safe pure nothrow unittest 9755 { 9756 // Slides with fewer elements are empty or 1 for Yes.withPartial 9757 static foreach (expectedLength, Partial; [No.withPartial, Yes.withPartial]) 9758 {{ 9759 assert(3.iota.slide!(Partial)(4, 2).walkLength == expectedLength); 9760 assert(3.iota.slide!(Partial)(4).walkLength == expectedLength); 9761 assert(3.iota.slide!(Partial)(4, 3).walkLength == expectedLength); 9762 }} 9763 9764 static immutable list = [ 9765 // iota slide expected 9766 [4, 2, 1, 3, 3], 9767 [5, 3, 1, 3, 3], 9768 [7, 2, 2, 4, 3], 9769 [12, 2, 4, 3, 3], 9770 [6, 1, 2, 3, 3], 9771 [6, 2, 4, 2, 2], 9772 [3, 2, 4, 1, 1], 9773 [5, 2, 1, 4, 4], 9774 [7, 2, 2, 4, 3], 9775 [7, 2, 3, 3, 2], 9776 [7, 3, 2, 3, 3], 9777 [7, 3, 3, 3, 2], 9778 ]; 9779 foreach (e; list) 9780 { 9781 assert(e[0].iota.slide!(Yes.withPartial)(e[1], e[2]).length == e[3]); 9782 assert(e[0].iota.slide!(No.withPartial)(e[1], e[2]).length == e[4]); 9783 } 9784 } 9785 9786 // test index and slicing 9787 @safe pure nothrow unittest 9788 { 9789 import std.algorithm.comparison : equal; 9790 import std.array : array; 9791 9792 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9793 { 9794 foreach (s; [5, 7, 10, 15, 20]) 9795 foreach (windowSize; 1 .. 10) 9796 foreach (stepSize; 1 .. 10) 9797 { 9798 auto r = s.iota.slide!Partial(windowSize, stepSize); 9799 auto arr = r.array; 9800 assert(r.length == arr.length); 9801 9802 // test indexing 9803 foreach (i; 0 .. arr.length) 9804 assert(r[i] == arr[i]); 9805 9806 // test slicing 9807 foreach (i; 0 .. arr.length) 9808 { 9809 foreach (j; i .. arr.length) 9810 assert(r[i .. j].equal(arr[i .. j])); 9811 9812 assert(r[i .. $].equal(arr[i .. $])); 9813 } 9814 9815 // test opDollar slicing 9816 assert(r[$/2 .. $].equal(arr[$/2 .. $])); 9817 assert(r[$ .. $].empty); 9818 if (arr.empty) 9819 { 9820 assert(r[$ .. 0].empty); 9821 assert(r[$/2 .. $].empty); 9822 9823 } 9824 } 9825 } 9826 } 9827 9828 // test with infinite ranges 9829 @safe pure nothrow unittest 9830 { 9831 import std.algorithm.comparison : equal; 9832 9833 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9834 {{ 9835 // InfiniteRange without RandomAccess 9836 auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); 9837 assert(fibs.slide!Partial(2).take(2).equal!equal([[1, 1], [1, 2]])); 9838 assert(fibs.slide!Partial(2, 3).take(2).equal!equal([[1, 1], [3, 5]])); 9839 9840 // InfiniteRange with RandomAccess and slicing 9841 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 9842 auto oddsByPairs = odds.slide!Partial(2); 9843 assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]])); 9844 assert(oddsByPairs[1].equal([3, 5])); 9845 assert(oddsByPairs[4].equal([9, 11])); 9846 9847 static assert(hasSlicing!(typeof(odds))); 9848 assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]])); 9849 assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]])); 9850 9851 auto oddsWithGaps = odds.slide!Partial(2, 4); 9852 assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]])); 9853 assert(oddsWithGaps[2].equal([17, 19])); 9854 assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]])); 9855 assert(oddsWithGaps[1 .. $].take(2).equal!equal([[9, 11], [17, 19]])); 9856 }} 9857 } 9858 9859 // test reverse 9860 @safe pure nothrow unittest 9861 { 9862 import std.algorithm.comparison : equal; 9863 9864 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9865 {{ 9866 foreach (windowSize; 1 .. 15) 9867 foreach (stepSize; 1 .. 15) 9868 { 9869 auto r = 20.iota.slide!Partial(windowSize, stepSize); 9870 auto rArr = r.array.retro; 9871 auto rRetro = r.retro; 9872 9873 assert(rRetro.length == rArr.length); 9874 assert(rRetro.equal(rArr)); 9875 assert(rRetro.array.retro.equal(r)); 9876 } 9877 }} 9878 } 9879 9880 // test with dummy ranges 9881 @safe pure nothrow unittest 9882 { 9883 import std.algorithm.comparison : equal; 9884 import std.internal.test.dummyrange : AllDummyRanges; 9885 import std.meta : Filter; 9886 9887 static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) 9888 {{ 9889 Range r; 9890 9891 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9892 { 9893 assert(r.slide!Partial(1).equal!equal( 9894 [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]] 9895 )); 9896 assert(r.slide!Partial(2).equal!equal( 9897 [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] 9898 )); 9899 assert(r.slide!Partial(3).equal!equal( 9900 [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], 9901 [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]] 9902 )); 9903 assert(r.slide!Partial(6).equal!equal( 9904 [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], 9905 [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]] 9906 )); 9907 } 9908 9909 // special cases 9910 assert(r.slide!(Yes.withPartial)(15).equal!equal(iota(1, 11).only)); 9911 assert(r.slide!(Yes.withPartial)(15).walkLength == 1); 9912 assert(r.slide!(No.withPartial)(15).empty); 9913 assert(r.slide!(No.withPartial)(15).walkLength == 0); 9914 }} 9915 } 9916 9917 // test with dummy ranges 9918 @safe pure nothrow unittest 9919 { 9920 import std.algorithm.comparison : equal; 9921 import std.internal.test.dummyrange : AllDummyRanges; 9922 import std.meta : Filter; 9923 import std.typecons : tuple; 9924 9925 alias t = tuple; 9926 static immutable list = [ 9927 // iota slide expected 9928 t(6, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6]]), 9929 t(6, t(4, 6), [[1, 2, 3, 4]]), 9930 t(6, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]), 9931 t(7, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]), 9932 t(7, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7]]), 9933 t(8, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8]]), 9934 t(8, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8]]), 9935 t(8, t(3, 4), [[1, 2, 3], [5, 6, 7]]), 9936 t(10, t(3, 7), [[1, 2, 3], [8, 9, 10]]), 9937 ]; 9938 9939 static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) 9940 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9941 foreach (e; list) 9942 assert(Range().take(e[0]).slide!Partial(e[1].expand).equal!equal(e[2])); 9943 9944 static immutable listSpecial = [ 9945 // iota slide expected 9946 t(6, t(4, 3), [[1, 2, 3, 4], [4, 5, 6]]), 9947 t(7, t(4, 5), [[1, 2, 3, 4], [6, 7]]), 9948 t(7, t(4, 4), [[1, 2, 3, 4], [5, 6, 7]]), 9949 t(7, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7]]), 9950 t(8, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7], [7, 8]]), 9951 t(8, t(3, 3), [[1, 2, 3], [4, 5, 6], [7, 8]]), 9952 t(8, t(3, 6), [[1, 2, 3], [7, 8]]), 9953 t(10, t(7, 6), [[1, 2, 3, 4, 5, 6, 7], [7, 8, 9, 10]]), 9954 t(10, t(3, 8), [[1, 2, 3], [9, 10]]), 9955 ]; 9956 static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) 9957 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9958 foreach (e; listSpecial) 9959 { 9960 Range r; 9961 assert(r.take(e[0]).slide!(Yes.withPartial)(e[1].expand).equal!equal(e[2])); 9962 assert(r.take(e[0]).slide!(No.withPartial)(e[1].expand).equal!equal(e[2].dropBackOne)); 9963 } 9964 } 9965 9966 // test reverse with dummy ranges 9967 @safe pure nothrow unittest 9968 { 9969 import std.algorithm.comparison : equal; 9970 import std.internal.test.dummyrange : AllDummyRanges; 9971 import std.meta : Filter, templateAnd; 9972 import std.typecons : tuple; 9973 alias t = tuple; 9974 9975 static immutable list = [ 9976 // slide expected 9977 t(1, 1, [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]), 9978 t(2, 1, [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]]), 9979 t(5, 1, [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8], 9980 [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]]), 9981 t(2, 2, [[9, 10], [7, 8], [5, 6], [3, 4], [1, 2]]), 9982 t(2, 4, [[9, 10], [5, 6], [1, 2]]), 9983 ]; 9984 9985 static foreach (Range; Filter!(templateAnd!(hasSlicing, hasLength, isBidirectionalRange), AllDummyRanges)) 9986 {{ 9987 Range r; 9988 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9989 { 9990 foreach (e; list) 9991 assert(r.slide!Partial(e[0], e[1]).retro.equal!equal(e[2])); 9992 9993 // front = back 9994 foreach (windowSize; 1 .. 10) 9995 foreach (stepSize; 1 .. 10) 9996 { 9997 auto slider = r.slide!Partial(windowSize, stepSize); 9998 auto sliderRetro = slider.retro.array; 9999 assert(slider.length == sliderRetro.length); 10000 assert(sliderRetro.retro.equal!equal(slider)); 10001 } 10002 } 10003 10004 // special cases 10005 assert(r.slide!(No.withPartial)(15).retro.walkLength == 0); 10006 assert(r.slide!(Yes.withPartial)(15).retro.equal!equal(iota(1, 11).only)); 10007 }} 10008 } 10009 10010 // test different sliceable ranges 10011 @safe pure nothrow unittest 10012 { 10013 import std.algorithm.comparison : equal; 10014 import std.internal.test.dummyrange : AllDummyRanges; 10015 import std.meta : AliasSeq; 10016 10017 struct SliceableRange(Range, Flag!"withOpDollar" withOpDollar = No.withOpDollar, 10018 Flag!"withInfiniteness" withInfiniteness = No.withInfiniteness) 10019 { 10020 Range arr = 10.iota.array; // similar to DummyRange 10021 @property auto save() { return typeof(this)(arr); } 10022 @property auto front() { return arr[0]; } 10023 void popFront() { arr.popFront(); } 10024 auto opSlice(size_t i, size_t j) 10025 { 10026 // subslices can't be infinite 10027 return SliceableRange!(Range, withOpDollar, No.withInfiniteness)(arr[i .. j]); 10028 } 10029 10030 static if (withInfiniteness) 10031 { 10032 enum empty = false; 10033 } 10034 else 10035 { 10036 @property bool empty() { return arr.empty; } 10037 @property auto length() { return arr.length; } 10038 } 10039 10040 static if (withOpDollar) 10041 { 10042 static if (withInfiniteness) 10043 { 10044 struct Dollar {} 10045 Dollar opDollar() const { return Dollar.init; } 10046 10047 // Slice to dollar 10048 typeof(this) opSlice(size_t lower, Dollar) 10049 { 10050 return typeof(this)(arr[lower .. $]); 10051 } 10052 10053 } 10054 else 10055 { 10056 alias opDollar = length; 10057 } 10058 } 10059 } 10060 10061 import std.meta : Filter, templateNot; 10062 alias SliceableDummyRanges = Filter!(hasSlicing, AllDummyRanges); 10063 10064 static foreach (Partial; [Yes.withPartial, No.withPartial]) 10065 {{ 10066 static foreach (Range; SliceableDummyRanges) 10067 {{ 10068 Range r; 10069 r.reinit; 10070 r.arr[] -= 1; // use a 0-based array (for clarity) 10071 10072 assert(r.slide!Partial(2)[0].equal([0, 1])); 10073 assert(r.slide!Partial(2)[1].equal([1, 2])); 10074 10075 // saveable 10076 auto s = r.slide!Partial(2); 10077 assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); 10078 s.save.popFront; 10079 assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); 10080 10081 assert(r.slide!Partial(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]])); 10082 }} 10083 10084 static foreach (Range; Filter!(templateNot!isInfinite, SliceableDummyRanges)) 10085 {{ 10086 Range r; 10087 r.reinit; 10088 r.arr[] -= 1; // use a 0-based array (for clarity) 10089 10090 assert(r.slide!(No.withPartial)(6).equal!equal( 10091 [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], 10092 [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]] 10093 )); 10094 assert(r.slide!(No.withPartial)(16).empty); 10095 10096 assert(r.slide!Partial(4)[0 .. $].equal(r.slide!Partial(4))); 10097 assert(r.slide!Partial(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); 10098 assert(r.slide!Partial(2)[$ .. $].empty); 10099 10100 assert(r.slide!Partial(3).retro.equal!equal( 10101 [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] 10102 )); 10103 }} 10104 10105 alias T = int[]; 10106 10107 // separate checks for infinity 10108 auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]); 10109 assert(infIndex.slide!Partial(2)[0].equal([0, 1])); 10110 assert(infIndex.slide!Partial(2)[1].equal([1, 2])); 10111 10112 auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)(); 10113 assert(infDollar.slide!Partial(2)[1 .. $].front.equal([1, 2])); 10114 assert(infDollar.slide!Partial(4)[0 .. $].front.equal([0, 1, 2, 3])); 10115 assert(infDollar.slide!Partial(4)[2 .. $].front.equal([2, 3, 4, 5])); 10116 }} 10117 } 10118 10119 // https://issues.dlang.org/show_bug.cgi?id=19082 10120 @safe unittest 10121 { 10122 import std.algorithm.comparison : equal; 10123 import std.algorithm.iteration : map; 10124 assert([1].map!(x => x).slide(2).equal!equal([[1]])); 10125 } 10126 10127 // https://issues.dlang.org/show_bug.cgi?id=19642 10128 @safe unittest 10129 { 10130 import std.algorithm.comparison : equal; 10131 import std.algorithm.iteration : splitter; 10132 10133 assert("ab cd".splitter(' ').slide!(No.withPartial)(2).equal!equal([["ab", "cd"]])); 10134 } 10135 10136 // https://issues.dlang.org/show_bug.cgi?id=23976 10137 @safe unittest 10138 { 10139 import std.algorithm.comparison : equal; 10140 import std.algorithm.iteration : splitter; 10141 10142 assert("1<2".splitter('<').slide(2).equal!equal([["1", "2"]])); 10143 } 10144 10145 private struct OnlyResult(Values...) 10146 if (Values.length > 1) 10147 { 10148 private enum arity = Values.length; 10149 10150 private alias UnqualValues = staticMap!(Unqual, Values); 10151 10152 private this(return scope ref Values values) 10153 { 10154 ref @trusted unqual(T)(ref T x){return cast() x;} 10155 10156 // TODO: this calls any possible copy constructors without qualifiers. 10157 // Find a way to initialize values using qualified copy constructors. 10158 static foreach (i; 0 .. Values.length) 10159 { 10160 this.values[i] = unqual(values[i]); 10161 } 10162 this.backIndex = arity; 10163 } 10164 10165 bool empty() @property 10166 { 10167 return frontIndex >= backIndex; 10168 } 10169 10170 CommonType!Values front() @property 10171 { 10172 assert(!empty, "Attempting to fetch the front of an empty Only range"); 10173 return this[0]; 10174 } 10175 10176 void popFront() 10177 { 10178 assert(!empty, "Attempting to popFront an empty Only range"); 10179 ++frontIndex; 10180 } 10181 10182 CommonType!Values back() @property 10183 { 10184 assert(!empty, "Attempting to fetch the back of an empty Only range"); 10185 return this[$ - 1]; 10186 } 10187 10188 void popBack() 10189 { 10190 assert(!empty, "Attempting to popBack an empty Only range"); 10191 --backIndex; 10192 } 10193 10194 OnlyResult save() @property 10195 { 10196 return this; 10197 } 10198 10199 size_t length() const @property 10200 { 10201 return backIndex - frontIndex; 10202 } 10203 10204 alias opDollar = length; 10205 10206 @trusted CommonType!Values opIndex(size_t idx) 10207 { 10208 // when i + idx points to elements popped 10209 // with popBack 10210 assert(idx < length, "Attempting to fetch an out of bounds index from an Only range"); 10211 final switch (frontIndex + idx) 10212 static foreach (i, T; Values) 10213 case i: 10214 return cast(T) values[i]; 10215 } 10216 10217 OnlyResult opSlice() 10218 { 10219 return this; 10220 } 10221 10222 OnlyResult opSlice(size_t from, size_t to) 10223 { 10224 OnlyResult result = this; 10225 result.frontIndex += from; 10226 result.backIndex = this.frontIndex + to; 10227 assert( 10228 from <= to, 10229 "Attempting to slice an Only range with a larger first argument than the second." 10230 ); 10231 assert( 10232 to <= length, 10233 "Attempting to slice using an out of bounds index on an Only range" 10234 ); 10235 return result; 10236 } 10237 10238 private size_t frontIndex = 0; 10239 private size_t backIndex = 0; 10240 10241 // https://issues.dlang.org/show_bug.cgi?id=10643 10242 version (none) 10243 { 10244 import std.traits : hasElaborateAssign; 10245 static if (hasElaborateAssign!T) 10246 private UnqualValues values; 10247 else 10248 private UnqualValues values = void; 10249 } 10250 else 10251 // These may alias to shared or immutable data. Do not let the user 10252 // to access these directly, and do not allow mutation without checking 10253 // the qualifier. 10254 private UnqualValues values; 10255 } 10256 10257 // Specialize for single-element results 10258 private struct OnlyResult(T) 10259 { 10260 @property T front() 10261 { 10262 assert(!empty, "Attempting to fetch the front of an empty Only range"); 10263 return fetchFront(); 10264 } 10265 @property T back() 10266 { 10267 assert(!empty, "Attempting to fetch the back of an empty Only range"); 10268 return fetchFront(); 10269 } 10270 @property bool empty() const { return _empty; } 10271 @property size_t length() const { return !_empty; } 10272 @property auto save() { return this; } 10273 void popFront() 10274 { 10275 assert(!_empty, "Attempting to popFront an empty Only range"); 10276 _empty = true; 10277 } 10278 void popBack() 10279 { 10280 assert(!_empty, "Attempting to popBack an empty Only range"); 10281 _empty = true; 10282 } 10283 alias opDollar = length; 10284 10285 private this()(return scope auto ref T value) 10286 { 10287 ref @trusted unqual(ref T x){return cast() x;} 10288 // TODO: this calls the possible copy constructor without qualifiers. 10289 // Find a way to initialize value using a qualified copy constructor. 10290 this._value = unqual(value); 10291 this._empty = false; 10292 } 10293 10294 T opIndex(size_t i) 10295 { 10296 assert(!_empty && i == 0, "Attempting to fetch an out of bounds index from an Only range"); 10297 return fetchFront(); 10298 } 10299 10300 OnlyResult opSlice() 10301 { 10302 return this; 10303 } 10304 10305 OnlyResult opSlice(size_t from, size_t to) 10306 { 10307 assert( 10308 from <= to, 10309 "Attempting to slice an Only range with a larger first argument than the second." 10310 ); 10311 assert( 10312 to <= length, 10313 "Attempting to slice using an out of bounds index on an Only range" 10314 ); 10315 OnlyResult copy = this; 10316 copy._empty = _empty || from == to; 10317 return copy; 10318 } 10319 10320 // This may alias to shared or immutable data. Do not let the user 10321 // to access this directly, and do not allow mutation without checking 10322 // the qualifier. 10323 private Unqual!T _value; 10324 private bool _empty = true; 10325 private @trusted T fetchFront() 10326 { 10327 return *cast(T*)&_value; 10328 } 10329 } 10330 10331 /** 10332 Assemble `values` into a range that carries all its 10333 elements in-situ. 10334 10335 Useful when a single value or multiple disconnected values 10336 must be passed to an algorithm expecting a range, without 10337 having to perform dynamic memory allocation. 10338 10339 As copying the range means copying all elements, it can be 10340 safely returned from functions. For the same reason, copying 10341 the returned range may be expensive for a large number of arguments. 10342 10343 Params: 10344 values = the values to assemble together 10345 10346 Returns: 10347 A `RandomAccessRange` of the assembled values. 10348 10349 See_Also: $(LREF chain) to chain ranges 10350 */ 10351 auto only(Values...)(return scope Values values) 10352 if (!is(CommonType!Values == void)) 10353 { 10354 return OnlyResult!Values(values); 10355 } 10356 10357 /// ditto 10358 auto only()() 10359 { 10360 // cannot use noreturn due to https://issues.dlang.org/show_bug.cgi?id=22383 10361 struct EmptyElementType {} 10362 EmptyElementType[] result; 10363 return result; 10364 } 10365 10366 /// 10367 @safe unittest 10368 { 10369 import std.algorithm.comparison : equal; 10370 import std.algorithm.iteration : filter, joiner, map; 10371 import std.algorithm.searching : findSplitBefore; 10372 import std.uni : isUpper; 10373 10374 assert(equal(only('♡'), "♡")); 10375 assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); 10376 10377 assert(only("one", "two", "three").joiner(" ").equal("one two three")); 10378 10379 string title = "The D Programming Language"; 10380 assert(title 10381 .filter!isUpper // take the upper case letters 10382 .map!only // make each letter its own range 10383 .joiner(".") // join the ranges together lazily 10384 .equal("T.D.P.L")); 10385 } 10386 10387 // https://issues.dlang.org/show_bug.cgi?id=20314 10388 @safe unittest 10389 { 10390 import std.algorithm.iteration : joiner; 10391 10392 const string s = "foo", t = "bar"; 10393 10394 assert([only(s, t), only(t, s)].joiner(only(", ")).join == "foobar, barfoo"); 10395 } 10396 10397 // Tests the zero-element result 10398 @safe unittest 10399 { 10400 import std.algorithm.comparison : equal; 10401 10402 auto emptyRange = only(); 10403 10404 alias EmptyRange = typeof(emptyRange); 10405 static assert(isInputRange!EmptyRange); 10406 static assert(isForwardRange!EmptyRange); 10407 static assert(isBidirectionalRange!EmptyRange); 10408 static assert(isRandomAccessRange!EmptyRange); 10409 static assert(hasLength!EmptyRange); 10410 static assert(hasSlicing!EmptyRange); 10411 10412 assert(emptyRange.empty); 10413 assert(emptyRange.length == 0); 10414 assert(emptyRange.equal(emptyRange[])); 10415 assert(emptyRange.equal(emptyRange.save)); 10416 assert(emptyRange[0 .. 0].equal(emptyRange)); 10417 } 10418 10419 // Tests the single-element result 10420 @safe unittest 10421 { 10422 import std.algorithm.comparison : equal; 10423 import std.typecons : tuple; 10424 foreach (x; tuple(1, '1', 1.0, "1", [1])) 10425 { 10426 auto a = only(x); 10427 typeof(x)[] e = []; 10428 assert(a.front == x); 10429 assert(a.back == x); 10430 assert(!a.empty); 10431 assert(a.length == 1); 10432 assert(equal(a, a[])); 10433 assert(equal(a, a[0 .. 1])); 10434 assert(equal(a[0 .. 0], e)); 10435 assert(equal(a[1 .. 1], e)); 10436 assert(a[0] == x); 10437 10438 auto b = a.save; 10439 assert(equal(a, b)); 10440 a.popFront(); 10441 assert(a.empty && a.length == 0 && a[].empty); 10442 b.popBack(); 10443 assert(b.empty && b.length == 0 && b[].empty); 10444 10445 alias A = typeof(a); 10446 static assert(isInputRange!A); 10447 static assert(isForwardRange!A); 10448 static assert(isBidirectionalRange!A); 10449 static assert(isRandomAccessRange!A); 10450 static assert(hasLength!A); 10451 static assert(hasSlicing!A); 10452 } 10453 10454 auto imm = only!(immutable int)(1); 10455 immutable int[] imme = []; 10456 assert(imm.front == 1); 10457 assert(imm.back == 1); 10458 assert(!imm.empty); 10459 assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441 10460 assert(imm.length == 1); 10461 assert(equal(imm, imm[])); 10462 assert(equal(imm, imm[0 .. 1])); 10463 assert(equal(imm[0 .. 0], imme)); 10464 assert(equal(imm[1 .. 1], imme)); 10465 assert(imm[0] == 1); 10466 } 10467 10468 // Tests multiple-element results 10469 @safe unittest 10470 { 10471 import std.algorithm.comparison : equal; 10472 import std.algorithm.iteration : joiner; 10473 import std.meta : AliasSeq; 10474 static assert(!__traits(compiles, only(1, "1"))); 10475 10476 auto nums = only!(byte, uint, long)(1, 2, 3); 10477 static assert(is(ElementType!(typeof(nums)) == long)); 10478 assert(nums.length == 3); 10479 10480 foreach (i; 0 .. 3) 10481 assert(nums[i] == i + 1); 10482 10483 auto saved = nums.save; 10484 10485 foreach (i; 1 .. 4) 10486 { 10487 assert(nums.front == nums[0]); 10488 assert(nums.front == i); 10489 nums.popFront(); 10490 assert(nums.length == 3 - i); 10491 } 10492 10493 assert(nums.empty); 10494 10495 assert(saved.equal(only(1, 2, 3))); 10496 assert(saved.equal(saved[])); 10497 assert(saved[0 .. 1].equal(only(1))); 10498 assert(saved[0 .. 2].equal(only(1, 2))); 10499 assert(saved[0 .. 3].equal(saved)); 10500 assert(saved[1 .. 3].equal(only(2, 3))); 10501 assert(saved[2 .. 3].equal(only(3))); 10502 assert(saved[0 .. 0].empty); 10503 assert(saved[3 .. 3].empty); 10504 10505 alias data = AliasSeq!("one", "two", "three", "four"); 10506 static joined = 10507 ["one two", "one two three", "one two three four"]; 10508 string[] joinedRange = joined; 10509 10510 static foreach (argCount; 2 .. 5) 10511 {{ 10512 auto values = only(data[0 .. argCount]); 10513 alias Values = typeof(values); 10514 static assert(is(ElementType!Values == string)); 10515 static assert(isInputRange!Values); 10516 static assert(isForwardRange!Values); 10517 static assert(isBidirectionalRange!Values); 10518 static assert(isRandomAccessRange!Values); 10519 static assert(hasSlicing!Values); 10520 static assert(hasLength!Values); 10521 10522 assert(values.length == argCount); 10523 assert(values[0 .. $].equal(values[0 .. values.length])); 10524 assert(values.joiner(" ").equal(joinedRange.front)); 10525 joinedRange.popFront(); 10526 }} 10527 10528 assert(saved.retro.equal(only(3, 2, 1))); 10529 assert(saved.length == 3); 10530 10531 assert(saved.back == 3); 10532 saved.popBack(); 10533 assert(saved.length == 2); 10534 assert(saved.back == 2); 10535 10536 assert(saved.front == 1); 10537 saved.popFront(); 10538 assert(saved.length == 1); 10539 assert(saved.front == 2); 10540 10541 saved.popBack(); 10542 assert(saved.empty); 10543 10544 auto imm = only!(immutable int, immutable int)(42, 24); 10545 alias Imm = typeof(imm); 10546 static assert(is(ElementType!Imm == immutable(int))); 10547 assert(!imm.empty); 10548 assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441 10549 assert(imm.front == 42); 10550 imm.popFront(); 10551 assert(imm.front == 24); 10552 imm.popFront(); 10553 assert(imm.empty); 10554 10555 static struct Test { int* a; } 10556 immutable(Test) test; 10557 cast(void) only(test, test); // Works with mutable indirection 10558 } 10559 10560 // https://issues.dlang.org/show_bug.cgi?id=21129 10561 @safe unittest 10562 { 10563 auto range = () @safe { 10564 const(char)[5] staticStr = "Hello"; 10565 10566 // `only` must store a char[5] - not a char[]! 10567 return only(staticStr, " World"); 10568 } (); 10569 10570 assert(range.join == "Hello World"); 10571 } 10572 10573 // https://issues.dlang.org/show_bug.cgi?id=21129 10574 @safe unittest 10575 { 10576 struct AliasedString 10577 { 10578 const(char)[5] staticStr = "Hello"; 10579 10580 @property const(char)[] slice() const 10581 { 10582 return staticStr[]; 10583 } 10584 alias slice this; 10585 } 10586 10587 auto range = () @safe { 10588 auto hello = AliasedString(); 10589 10590 // a copy of AliasedString is stored in the range. 10591 return only(hello, " World"); 10592 } (); 10593 10594 assert(range.join == "Hello World"); 10595 } 10596 10597 // https://issues.dlang.org/show_bug.cgi?id=21022 10598 @safe pure nothrow unittest 10599 { 10600 struct S 10601 { 10602 int* mem; 10603 } 10604 10605 immutable S x; 10606 immutable(S)[] arr; 10607 auto r1 = arr.chain(x.only, only(x, x)); 10608 } 10609 10610 /** 10611 Iterate over `range` with an attached index variable. 10612 10613 Each element is a $(REF Tuple, std,typecons) containing the index 10614 and the element, in that order, where the index member is named `index` 10615 and the element member is named `value`. 10616 10617 The index starts at `start` and is incremented by one on every iteration. 10618 10619 Overflow: 10620 If `range` has length, then it is an error to pass a value for `start` 10621 so that `start + range.length` is bigger than `Enumerator.max`, thus 10622 it is ensured that overflow cannot happen. 10623 10624 If `range` does not have length, and `popFront` is called when 10625 `front.index == Enumerator.max`, the index will overflow and 10626 continue from `Enumerator.min`. 10627 10628 Params: 10629 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to attach indexes to 10630 start = the number to start the index counter from 10631 10632 Returns: 10633 At minimum, an input range. All other range primitives are given in the 10634 resulting range if `range` has them. The exceptions are the bidirectional 10635 primitives, which are propagated only if `range` has length. 10636 10637 Example: 10638 Useful for using `foreach` with an index loop variable: 10639 ---- 10640 import std.stdio : stdin, stdout; 10641 import std.range : enumerate; 10642 10643 foreach (lineNum, line; stdin.byLine().enumerate(1)) 10644 stdout.writefln("line #%s: %s", lineNum, line); 10645 ---- 10646 */ 10647 auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0) 10648 if (isIntegral!Enumerator && isInputRange!Range) 10649 in 10650 { 10651 static if (hasLength!Range) 10652 { 10653 // TODO: core.checkedint supports mixed signedness yet? 10654 import core.checkedint : adds, addu; 10655 import std.conv : ConvException, to; 10656 import std.traits : isSigned, Largest, Signed; 10657 10658 alias LengthType = typeof(range.length); 10659 bool overflow; 10660 static if (isSigned!Enumerator && isSigned!LengthType) 10661 auto result = adds(start, range.length, overflow); 10662 else static if (isSigned!Enumerator) 10663 { 10664 alias signed_t = Largest!(Enumerator, Signed!LengthType); 10665 signed_t signedLength; 10666 //This is to trick the compiler because if length is enum 10667 //the compiler complains about unreachable code. 10668 auto getLength() 10669 { 10670 return range.length; 10671 } 10672 //Can length fit in the signed type 10673 assert(getLength() < signed_t.max, 10674 "a signed length type is required but the range's length() is too great"); 10675 signedLength = range.length; 10676 auto result = adds(start, signedLength, overflow); 10677 } 10678 else 10679 { 10680 static if (isSigned!LengthType) 10681 assert(range.length >= 0); 10682 auto result = addu(start, range.length, overflow); 10683 } 10684 10685 assert(!overflow && result <= Enumerator.max); 10686 } 10687 } 10688 do 10689 { 10690 // TODO: Relax isIntegral!Enumerator to allow user-defined integral types 10691 static struct Result 10692 { 10693 import std.typecons : Tuple; 10694 10695 private: 10696 alias ElemType = Tuple!(Enumerator, "index", ElementType!Range, "value"); 10697 Range range; 10698 Unqual!Enumerator index; 10699 10700 public: 10701 ElemType front() @property 10702 { 10703 assert(!range.empty, "Attempting to fetch the front of an empty enumerate"); 10704 return typeof(return)(index, range.front); 10705 } 10706 10707 static if (isInfinite!Range) 10708 enum bool empty = false; 10709 else 10710 { 10711 bool empty() @property 10712 { 10713 return range.empty; 10714 } 10715 } 10716 10717 void popFront() 10718 { 10719 assert(!range.empty, "Attempting to popFront an empty enumerate"); 10720 range.popFront(); 10721 ++index; // When !hasLength!Range, overflow is expected 10722 } 10723 10724 static if (isForwardRange!Range) 10725 { 10726 Result save() @property 10727 { 10728 return typeof(return)(range.save, index); 10729 } 10730 } 10731 10732 static if (hasLength!Range) 10733 { 10734 mixin ImplementLength!range; 10735 10736 static if (isBidirectionalRange!Range) 10737 { 10738 ElemType back() @property 10739 { 10740 assert(!range.empty, "Attempting to fetch the back of an empty enumerate"); 10741 return typeof(return)(cast(Enumerator)(index + range.length - 1), range.back); 10742 } 10743 10744 void popBack() 10745 { 10746 assert(!range.empty, "Attempting to popBack an empty enumerate"); 10747 range.popBack(); 10748 } 10749 } 10750 } 10751 10752 static if (isRandomAccessRange!Range) 10753 { 10754 ElemType opIndex(size_t i) 10755 { 10756 return typeof(return)(cast(Enumerator)(index + i), range[i]); 10757 } 10758 } 10759 10760 static if (hasSlicing!Range) 10761 { 10762 static if (hasLength!Range) 10763 { 10764 Result opSlice(size_t i, size_t j) 10765 { 10766 return typeof(return)(range[i .. j], cast(Enumerator)(index + i)); 10767 } 10768 } 10769 else 10770 { 10771 static struct DollarToken {} 10772 enum opDollar = DollarToken.init; 10773 10774 Result opSlice(size_t i, DollarToken) 10775 { 10776 return typeof(return)(range[i .. $], cast(Enumerator)(index + i)); 10777 } 10778 10779 auto opSlice(size_t i, size_t j) 10780 { 10781 return this[i .. $].takeExactly(j - 1); 10782 } 10783 } 10784 } 10785 } 10786 10787 return Result(range, start); 10788 } 10789 10790 /// Can start enumeration from a negative position: 10791 pure @safe nothrow unittest 10792 { 10793 import std.array : assocArray; 10794 import std.range : enumerate; 10795 10796 bool[int] aa = true.repeat(3).enumerate(-1).assocArray(); 10797 assert(aa[-1]); 10798 assert(aa[0]); 10799 assert(aa[1]); 10800 } 10801 10802 // Make sure passing qualified types works 10803 pure @safe nothrow unittest 10804 { 10805 char[4] v; 10806 immutable start = 2; 10807 v[2 .. $].enumerate(start); 10808 } 10809 10810 pure @safe nothrow unittest 10811 { 10812 import std.internal.test.dummyrange : AllDummyRanges; 10813 import std.meta : AliasSeq; 10814 import std.typecons : tuple; 10815 10816 static struct HasSlicing 10817 { 10818 typeof(this) front() @property { return typeof(this).init; } 10819 bool empty() @property { return true; } 10820 void popFront() {} 10821 10822 typeof(this) opSlice(size_t, size_t) 10823 { 10824 return typeof(this)(); 10825 } 10826 } 10827 10828 static foreach (DummyType; AliasSeq!(AllDummyRanges, HasSlicing)) 10829 {{ 10830 alias R = typeof(enumerate(DummyType.init)); 10831 static assert(isInputRange!R); 10832 static assert(isForwardRange!R == isForwardRange!DummyType); 10833 static assert(isRandomAccessRange!R == isRandomAccessRange!DummyType); 10834 static assert(!hasAssignableElements!R); 10835 10836 static if (hasLength!DummyType) 10837 { 10838 static assert(hasLength!R); 10839 static assert(isBidirectionalRange!R == 10840 isBidirectionalRange!DummyType); 10841 } 10842 10843 static assert(hasSlicing!R == hasSlicing!DummyType); 10844 }} 10845 10846 static immutable values = ["zero", "one", "two", "three"]; 10847 auto enumerated = values[].enumerate(); 10848 assert(!enumerated.empty); 10849 assert(enumerated.front == tuple(0, "zero")); 10850 assert(enumerated.back == tuple(3, "three")); 10851 10852 typeof(enumerated) saved = enumerated.save; 10853 saved.popFront(); 10854 assert(enumerated.front == tuple(0, "zero")); 10855 assert(saved.front == tuple(1, "one")); 10856 assert(saved.length == enumerated.length - 1); 10857 saved.popBack(); 10858 assert(enumerated.back == tuple(3, "three")); 10859 assert(saved.back == tuple(2, "two")); 10860 saved.popFront(); 10861 assert(saved.front == tuple(2, "two")); 10862 assert(saved.back == tuple(2, "two")); 10863 saved.popFront(); 10864 assert(saved.empty); 10865 10866 size_t control = 0; 10867 foreach (i, v; enumerated) 10868 { 10869 static assert(is(typeof(i) == size_t)); 10870 static assert(is(typeof(v) == typeof(values[0]))); 10871 assert(i == control); 10872 assert(v == values[i]); 10873 assert(tuple(i, v) == enumerated[i]); 10874 ++control; 10875 } 10876 10877 assert(enumerated[0 .. $].front == tuple(0, "zero")); 10878 assert(enumerated[$ - 1 .. $].front == tuple(3, "three")); 10879 10880 foreach (i; 0 .. 10) 10881 { 10882 auto shifted = values[0 .. 2].enumerate(i); 10883 assert(shifted.front == tuple(i, "zero")); 10884 assert(shifted[0] == shifted.front); 10885 10886 auto next = tuple(i + 1, "one"); 10887 assert(shifted[1] == next); 10888 shifted.popFront(); 10889 assert(shifted.front == next); 10890 shifted.popFront(); 10891 assert(shifted.empty); 10892 } 10893 10894 static foreach (T; AliasSeq!(ubyte, byte, uint, int)) 10895 {{ 10896 auto inf = 42.repeat().enumerate(T.max); 10897 alias Inf = typeof(inf); 10898 static assert(isInfinite!Inf); 10899 static assert(hasSlicing!Inf); 10900 10901 // test overflow 10902 assert(inf.front == tuple(T.max, 42)); 10903 inf.popFront(); 10904 assert(inf.front == tuple(T.min, 42)); 10905 10906 // test slicing 10907 inf = inf[42 .. $]; 10908 assert(inf.front == tuple(T.min + 42, 42)); 10909 auto window = inf[0 .. 2]; 10910 assert(window.length == 1); 10911 assert(window.front == inf.front); 10912 window.popFront(); 10913 assert(window.empty); 10914 }} 10915 } 10916 10917 pure @safe unittest 10918 { 10919 import std.algorithm.comparison : equal; 10920 import std.meta : AliasSeq; 10921 static immutable int[] values = [0, 1, 2, 3, 4]; 10922 static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 10923 {{ 10924 auto enumerated = values.enumerate!T(); 10925 static assert(is(typeof(enumerated.front.index) == T)); 10926 assert(enumerated.equal(values[].zip(values))); 10927 10928 foreach (T i; 0 .. 5) 10929 { 10930 auto subset = values[cast(size_t) i .. $]; 10931 auto offsetEnumerated = subset.enumerate(i); 10932 static assert(is(typeof(enumerated.front.index) == T)); 10933 assert(offsetEnumerated.equal(subset.zip(subset))); 10934 } 10935 }} 10936 } 10937 @nogc @safe unittest 10938 { 10939 const val = iota(1, 100).enumerate(1); 10940 } 10941 @nogc @safe unittest 10942 { 10943 import core.exception : AssertError; 10944 import std.exception : assertThrown; 10945 struct RangePayload { 10946 enum length = size_t.max; 10947 void popFront() {} 10948 int front() { return 0; } 10949 bool empty() { return true; } 10950 } 10951 RangePayload thePayload; 10952 //Assertion won't happen when contracts are disabled for -release. 10953 debug assertThrown!AssertError(enumerate(thePayload, -10)); 10954 } 10955 // https://issues.dlang.org/show_bug.cgi?id=10939 10956 version (none) 10957 { 10958 // Re-enable (or remove) if 10939 is resolved. 10959 /+pure+/ @safe unittest // Impure because of std.conv.to 10960 { 10961 import core.exception : RangeError; 10962 import std.exception : assertNotThrown, assertThrown; 10963 import std.meta : AliasSeq; 10964 10965 static immutable values = [42]; 10966 10967 static struct SignedLengthRange 10968 { 10969 immutable(int)[] _values = values; 10970 10971 int front() @property { assert(false); } 10972 bool empty() @property { assert(false); } 10973 void popFront() { assert(false); } 10974 10975 int length() @property 10976 { 10977 return cast(int)_values.length; 10978 } 10979 } 10980 10981 SignedLengthRange svalues; 10982 static foreach (Enumerator; AliasSeq!(ubyte, byte, ushort, short, uint, int, ulong, long)) 10983 { 10984 assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max)); 10985 assertNotThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length)); 10986 assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length + 1)); 10987 10988 assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max)); 10989 assertNotThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length)); 10990 assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length + 1)); 10991 } 10992 10993 static foreach (Enumerator; AliasSeq!(byte, short, int)) 10994 { 10995 assertThrown!RangeError(repeat(0, uint.max).enumerate!Enumerator()); 10996 } 10997 10998 assertNotThrown!RangeError(repeat(0, uint.max).enumerate!long()); 10999 } 11000 } 11001 11002 /** 11003 Returns true if `fn` accepts variables of type T1 and T2 in any order. 11004 The following code should compile: 11005 --- 11006 (ref T1 a, ref T2 b) 11007 { 11008 fn(a, b); 11009 fn(b, a); 11010 } 11011 --- 11012 */ 11013 template isTwoWayCompatible(alias fn, T1, T2) 11014 { 11015 enum isTwoWayCompatible = is(typeof((ref T1 a, ref T2 b) 11016 { 11017 cast(void) fn(a, b); 11018 cast(void) fn(b, a); 11019 } 11020 )); 11021 } 11022 11023 /// 11024 @safe unittest 11025 { 11026 void func1(int a, int b); 11027 void func2(int a, float b); 11028 11029 static assert(isTwoWayCompatible!(func1, int, int)); 11030 static assert(isTwoWayCompatible!(func1, short, int)); 11031 static assert(!isTwoWayCompatible!(func2, int, float)); 11032 11033 void func3(ref int a, ref int b); 11034 static assert( isTwoWayCompatible!(func3, int, int)); 11035 static assert(!isTwoWayCompatible!(func3, short, int)); 11036 } 11037 11038 11039 /** 11040 Policy used with the searching primitives `lowerBound`, $(D 11041 upperBound), and `equalRange` of $(LREF SortedRange) below. 11042 */ 11043 enum SearchPolicy 11044 { 11045 /** 11046 Searches in a linear fashion. 11047 */ 11048 linear, 11049 11050 /** 11051 Searches with a step that is grows linearly (1, 2, 3,...) 11052 leading to a quadratic search schedule (indexes tried are 0, 1, 11053 3, 6, 10, 15, 21, 28,...) Once the search overshoots its target, 11054 the remaining interval is searched using binary search. The 11055 search is completed in $(BIGOH sqrt(n)) time. Use it when you 11056 are reasonably confident that the value is around the beginning 11057 of the range. 11058 */ 11059 trot, 11060 11061 /** 11062 Performs a $(LINK2 https://en.wikipedia.org/wiki/Exponential_search, 11063 galloping search algorithm), i.e. searches 11064 with a step that doubles every time, (1, 2, 4, 8, ...) leading 11065 to an exponential search schedule (indexes tried are 0, 1, 3, 11066 7, 15, 31, 63,...) Once the search overshoots its target, the 11067 remaining interval is searched using binary search. A value is 11068 found in $(BIGOH log(n)) time. 11069 */ 11070 gallop, 11071 11072 /** 11073 Searches using a classic interval halving policy. The search 11074 starts in the middle of the range, and each search step cuts 11075 the range in half. This policy finds a value in $(BIGOH log(n)) 11076 time but is less cache friendly than `gallop` for large 11077 ranges. The `binarySearch` policy is used as the last step 11078 of `trot`, `gallop`, `trotBackwards`, and $(D 11079 gallopBackwards) strategies. 11080 */ 11081 binarySearch, 11082 11083 /** 11084 Similar to `trot` but starts backwards. Use it when 11085 confident that the value is around the end of the range. 11086 */ 11087 trotBackwards, 11088 11089 /** 11090 Similar to `gallop` but starts backwards. Use it when 11091 confident that the value is around the end of the range. 11092 */ 11093 gallopBackwards 11094 } 11095 11096 /// 11097 @safe unittest 11098 { 11099 import std.algorithm.comparison : equal; 11100 11101 auto a = assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 11102 auto p1 = a.upperBound!(SearchPolicy.binarySearch)(3); 11103 assert(p1.equal([4, 5, 6, 7, 8, 9])); 11104 11105 auto p2 = a.lowerBound!(SearchPolicy.gallop)(4); 11106 assert(p2.equal([0, 1, 2, 3])); 11107 } 11108 11109 /** 11110 Options for $(LREF SortedRange) ranges (below). 11111 */ 11112 enum SortedRangeOptions 11113 { 11114 /** 11115 Assume, that the range is sorted without checking. 11116 */ 11117 assumeSorted, 11118 11119 /** 11120 All elements of the range are checked to be sorted. 11121 The check is performed in O(n) time. 11122 */ 11123 checkStrictly, 11124 11125 /** 11126 Some elements of the range are checked to be sorted. 11127 For ranges with random order, this will almost surely 11128 detect, that it is not sorted. For almost sorted ranges 11129 it's more likely to fail. The checked elements are choosen 11130 in a deterministic manner, which makes this check reproducable. 11131 The check is performed in O(log(n)) time. 11132 */ 11133 checkRoughly, 11134 } 11135 11136 /// 11137 @safe pure unittest 11138 { 11139 // create a SortedRange, that's checked strictly 11140 SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]); 11141 } 11142 11143 /** 11144 Represents a sorted range. In addition to the regular range 11145 primitives, supports additional operations that take advantage of the 11146 ordering, such as merge and binary search. To obtain a $(D 11147 SortedRange) from an unsorted range `r`, use 11148 $(REF sort, std,algorithm,sorting) which sorts `r` in place and returns the 11149 corresponding `SortedRange`. To construct a `SortedRange` from a range 11150 `r` that is known to be already sorted, use $(LREF assumeSorted). 11151 11152 Params: 11153 pred: The predicate used to define the sortedness 11154 opt: Controls how strongly the range is checked for sortedness. 11155 Will only be used for `RandomAccessRanges`. 11156 Will not be used in CTFE. 11157 */ 11158 struct SortedRange(Range, alias pred = "a < b", 11159 SortedRangeOptions opt = SortedRangeOptions.assumeSorted) 11160 if (isInputRange!Range && !isInstanceOf!(SortedRange, Range)) 11161 { 11162 import std.functional : binaryFun; 11163 11164 private alias predFun = binaryFun!pred; 11165 private bool geq(L, R)(L lhs, R rhs) 11166 { 11167 return !predFun(lhs, rhs); 11168 } 11169 private bool gt(L, R)(L lhs, R rhs) 11170 { 11171 return predFun(rhs, lhs); 11172 } 11173 private Range _input; 11174 11175 // Undocummented because a clearer way to invoke is by calling 11176 // assumeSorted. 11177 this(Range input) 11178 { 11179 static if (opt == SortedRangeOptions.checkRoughly) 11180 { 11181 roughlyVerifySorted(input); 11182 } 11183 static if (opt == SortedRangeOptions.checkStrictly) 11184 { 11185 strictlyVerifySorted(input); 11186 } 11187 this._input = input; 11188 } 11189 11190 // Assertion only. 11191 static if (opt == SortedRangeOptions.checkRoughly) 11192 private void roughlyVerifySorted(Range r) 11193 { 11194 if (!__ctfe) 11195 { 11196 static if (isRandomAccessRange!Range && hasLength!Range) 11197 { 11198 import core.bitop : bsr; 11199 import std.algorithm.sorting : isSorted; 11200 import std.exception : enforce; 11201 11202 // Check the sortedness of the input 11203 if (r.length < 2) return; 11204 11205 immutable size_t msb = bsr(r.length) + 1; 11206 assert(msb > 0 && msb <= r.length); 11207 immutable step = r.length / msb; 11208 auto st = stride(r, step); 11209 11210 enforce(isSorted!pred(st), "Range is not sorted"); 11211 } 11212 } 11213 } 11214 11215 // Assertion only. 11216 static if (opt == SortedRangeOptions.checkStrictly) 11217 private void strictlyVerifySorted(Range r) 11218 { 11219 if (!__ctfe) 11220 { 11221 static if (isRandomAccessRange!Range && hasLength!Range) 11222 { 11223 import std.algorithm.sorting : isSorted; 11224 import std.exception : enforce; 11225 11226 enforce(isSorted!pred(r), "Range is not sorted"); 11227 } 11228 } 11229 } 11230 11231 /// Range primitives. 11232 @property bool empty() //const 11233 { 11234 return this._input.empty; 11235 } 11236 11237 /// Ditto 11238 static if (isForwardRange!Range) 11239 @property auto save() 11240 { 11241 // Avoid the constructor 11242 typeof(this) result = this; 11243 result._input = _input.save; 11244 return result; 11245 } 11246 11247 /// Ditto 11248 @property auto ref front() 11249 { 11250 return _input.front; 11251 } 11252 11253 /// Ditto 11254 void popFront() 11255 { 11256 _input.popFront(); 11257 } 11258 11259 /// Ditto 11260 static if (isBidirectionalRange!Range) 11261 { 11262 @property auto ref back() 11263 { 11264 return _input.back; 11265 } 11266 11267 /// Ditto 11268 void popBack() 11269 { 11270 _input.popBack(); 11271 } 11272 } 11273 11274 /// Ditto 11275 static if (isRandomAccessRange!Range) 11276 auto ref opIndex(size_t i) 11277 { 11278 return _input[i]; 11279 } 11280 11281 /// Ditto 11282 static if (hasSlicing!Range) 11283 auto opSlice(size_t a, size_t b) return scope 11284 { 11285 assert( 11286 a <= b, 11287 "Attempting to slice a SortedRange with a larger first argument than the second." 11288 ); 11289 typeof(this) result = this; 11290 result._input = _input[a .. b];// skip checking 11291 return result; 11292 } 11293 11294 mixin ImplementLength!_input; 11295 11296 /** 11297 Releases the controlled range and returns it. 11298 11299 This does the opposite of $(LREF assumeSorted): instead of turning a range 11300 into a `SortedRange`, it extracts the original range back out of the `SortedRange` 11301 using $(REF, move, std,algorithm,mutation). 11302 */ 11303 auto release() return scope 11304 { 11305 import std.algorithm.mutation : move; 11306 return move(_input); 11307 } 11308 11309 /// 11310 static if (is(Range : int[])) 11311 @safe unittest 11312 { 11313 import std.algorithm.sorting : sort; 11314 int[3] data = [ 1, 2, 3 ]; 11315 auto a = assumeSorted(data[]); 11316 assert(a == sort!"a < b"(data[])); 11317 int[] p = a.release(); 11318 assert(p == [ 1, 2, 3 ]); 11319 } 11320 11321 // Assuming a predicate "test" that returns 0 for a left portion 11322 // of the range and then 1 for the rest, returns the index at 11323 // which the first 1 appears. Used internally by the search routines. 11324 private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) 11325 if (sp == SearchPolicy.binarySearch && isRandomAccessRange!Range && hasLength!Range) 11326 { 11327 size_t first = 0, count = _input.length; 11328 while (count > 0) 11329 { 11330 immutable step = count / 2, it = first + step; 11331 if (!test(_input[it], v)) 11332 { 11333 first = it + 1; 11334 count -= step + 1; 11335 } 11336 else 11337 { 11338 count = step; 11339 } 11340 } 11341 return first; 11342 } 11343 11344 // Specialization for trot and gallop 11345 private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) 11346 if ((sp == SearchPolicy.trot || sp == SearchPolicy.gallop) 11347 && isRandomAccessRange!Range) 11348 { 11349 if (empty || test(front, v)) return 0; 11350 immutable count = length; 11351 if (count == 1) return 1; 11352 size_t below = 0, above = 1, step = 2; 11353 while (!test(_input[above], v)) 11354 { 11355 // Still too small, update below and increase gait 11356 below = above; 11357 immutable next = above + step; 11358 if (next >= count) 11359 { 11360 // Overshot - the next step took us beyond the end. So 11361 // now adjust next and simply exit the loop to do the 11362 // binary search thingie. 11363 above = count; 11364 break; 11365 } 11366 // Still in business, increase step and continue 11367 above = next; 11368 static if (sp == SearchPolicy.trot) 11369 ++step; 11370 else 11371 step <<= 1; 11372 } 11373 return below + this[below .. above].getTransitionIndex!( 11374 SearchPolicy.binarySearch, test, V)(v); 11375 } 11376 11377 // Specialization for trotBackwards and gallopBackwards 11378 private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) 11379 if ((sp == SearchPolicy.trotBackwards || sp == SearchPolicy.gallopBackwards) 11380 && isRandomAccessRange!Range) 11381 { 11382 immutable count = length; 11383 if (empty || !test(back, v)) return count; 11384 if (count == 1) return 0; 11385 size_t below = count - 2, above = count - 1, step = 2; 11386 while (test(_input[below], v)) 11387 { 11388 // Still too large, update above and increase gait 11389 above = below; 11390 if (below < step) 11391 { 11392 // Overshot - the next step took us beyond the end. So 11393 // now adjust next and simply fall through to do the 11394 // binary search thingie. 11395 below = 0; 11396 break; 11397 } 11398 // Still in business, increase step and continue 11399 below -= step; 11400 static if (sp == SearchPolicy.trot) 11401 ++step; 11402 else 11403 step <<= 1; 11404 } 11405 return below + this[below .. above].getTransitionIndex!( 11406 SearchPolicy.binarySearch, test, V)(v); 11407 } 11408 11409 // lowerBound 11410 /** 11411 This function uses a search with policy `sp` to find the 11412 largest left subrange on which $(D pred(x, value)) is `true` for 11413 all `x` (e.g., if `pred` is "less than", returns the portion of 11414 the range with elements strictly smaller than `value`). The search 11415 schedule and its complexity are documented in 11416 $(LREF SearchPolicy). 11417 */ 11418 auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) 11419 if (isTwoWayCompatible!(predFun, ElementType!Range, V) 11420 && hasSlicing!Range) 11421 { 11422 return this[0 .. getTransitionIndex!(sp, geq)(value)]; 11423 } 11424 11425 /// 11426 static if (is(Range : int[])) 11427 @safe unittest 11428 { 11429 import std.algorithm.comparison : equal; 11430 auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]); 11431 auto p = a.lowerBound(4); 11432 assert(equal(p, [ 0, 1, 2, 3 ])); 11433 } 11434 11435 // upperBound 11436 /** 11437 This function searches with policy `sp` to find the largest right 11438 subrange on which $(D pred(value, x)) is `true` for all `x` 11439 (e.g., if `pred` is "less than", returns the portion of the range 11440 with elements strictly greater than `value`). The search schedule 11441 and its complexity are documented in $(LREF SearchPolicy). 11442 11443 For ranges that do not offer random access, `SearchPolicy.linear` 11444 is the only policy allowed (and it must be specified explicitly lest it exposes 11445 user code to unexpected inefficiencies). For random-access searches, all 11446 policies are allowed, and `SearchPolicy.binarySearch` is the default. 11447 */ 11448 auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) 11449 if (isTwoWayCompatible!(predFun, ElementType!Range, V)) 11450 { 11451 static assert(hasSlicing!Range || sp == SearchPolicy.linear, 11452 "Specify SearchPolicy.linear explicitly for " 11453 ~ typeof(this).stringof); 11454 static if (sp == SearchPolicy.linear) 11455 { 11456 for (; !_input.empty && !predFun(value, _input.front); 11457 _input.popFront()) 11458 { 11459 } 11460 return this; 11461 } 11462 else 11463 { 11464 return this[getTransitionIndex!(sp, gt)(value) .. length]; 11465 } 11466 } 11467 11468 /// 11469 static if (is(Range : int[])) 11470 @safe unittest 11471 { 11472 import std.algorithm.comparison : equal; 11473 auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); 11474 auto p = a.upperBound(3); 11475 assert(equal(p, [4, 4, 5, 6])); 11476 } 11477 11478 11479 // equalRange 11480 /** 11481 Returns the subrange containing all elements `e` for which both $(D 11482 pred(e, value)) and $(D pred(value, e)) evaluate to `false` (e.g., 11483 if `pred` is "less than", returns the portion of the range with 11484 elements equal to `value`). Uses a classic binary search with 11485 interval halving until it finds a value that satisfies the condition, 11486 then uses `SearchPolicy.gallopBackwards` to find the left boundary 11487 and `SearchPolicy.gallop` to find the right boundary. These 11488 policies are justified by the fact that the two boundaries are likely 11489 to be near the first found value (i.e., equal ranges are relatively 11490 small). Completes the entire search in $(BIGOH log(n)) time. 11491 */ 11492 auto equalRange(V)(V value) 11493 if (isTwoWayCompatible!(predFun, ElementType!Range, V) 11494 && isRandomAccessRange!Range) 11495 { 11496 size_t first = 0, count = _input.length; 11497 while (count > 0) 11498 { 11499 immutable step = count / 2; 11500 auto it = first + step; 11501 if (predFun(_input[it], value)) 11502 { 11503 // Less than value, bump left bound up 11504 first = it + 1; 11505 count -= step + 1; 11506 } 11507 else if (predFun(value, _input[it])) 11508 { 11509 // Greater than value, chop count 11510 count = step; 11511 } 11512 else 11513 { 11514 // Equal to value, do binary searches in the 11515 // leftover portions 11516 // Gallop towards the left end as it's likely nearby 11517 immutable left = first 11518 + this[first .. it] 11519 .lowerBound!(SearchPolicy.gallopBackwards)(value).length; 11520 first += count; 11521 // Gallop towards the right end as it's likely nearby 11522 immutable right = first 11523 - this[it + 1 .. first] 11524 .upperBound!(SearchPolicy.gallop)(value).length; 11525 return this[left .. right]; 11526 } 11527 } 11528 return this.init; 11529 } 11530 11531 /// 11532 static if (is(Range : int[])) 11533 @safe unittest 11534 { 11535 import std.algorithm.comparison : equal; 11536 auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11537 auto r = a.assumeSorted.equalRange(3); 11538 assert(equal(r, [ 3, 3, 3 ])); 11539 } 11540 11541 // trisect 11542 /** 11543 Returns a tuple `r` such that `r[0]` is the same as the result 11544 of `lowerBound(value)`, `r[1]` is the same as the result of $(D 11545 equalRange(value)), and `r[2]` is the same as the result of $(D 11546 upperBound(value)). The call is faster than computing all three 11547 separately. Uses a search schedule similar to $(D 11548 equalRange). Completes the entire search in $(BIGOH log(n)) time. 11549 */ 11550 auto trisect(V)(V value) 11551 if (isTwoWayCompatible!(predFun, ElementType!Range, V) 11552 && isRandomAccessRange!Range && hasLength!Range) 11553 { 11554 import std.typecons : tuple; 11555 size_t first = 0, count = _input.length; 11556 while (count > 0) 11557 { 11558 immutable step = count / 2; 11559 auto it = first + step; 11560 if (predFun(_input[it], value)) 11561 { 11562 // Less than value, bump left bound up 11563 first = it + 1; 11564 count -= step + 1; 11565 } 11566 else if (predFun(value, _input[it])) 11567 { 11568 // Greater than value, chop count 11569 count = step; 11570 } 11571 else 11572 { 11573 // Equal to value, do binary searches in the 11574 // leftover portions 11575 // Gallop towards the left end as it's likely nearby 11576 immutable left = first 11577 + this[first .. it] 11578 .lowerBound!(SearchPolicy.gallopBackwards)(value).length; 11579 first += count; 11580 // Gallop towards the right end as it's likely nearby 11581 immutable right = first 11582 - this[it + 1 .. first] 11583 .upperBound!(SearchPolicy.gallop)(value).length; 11584 return tuple(this[0 .. left], this[left .. right], 11585 this[right .. length]); 11586 } 11587 } 11588 // No equal element was found 11589 return tuple(this[0 .. first], this.init, this[first .. length]); 11590 } 11591 11592 /// 11593 static if (is(Range : int[])) 11594 @safe unittest 11595 { 11596 import std.algorithm.comparison : equal; 11597 auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11598 auto r = assumeSorted(a).trisect(3); 11599 assert(equal(r[0], [ 1, 2 ])); 11600 assert(equal(r[1], [ 3, 3, 3 ])); 11601 assert(equal(r[2], [ 4, 4, 5, 6 ])); 11602 } 11603 11604 // contains 11605 /** 11606 Returns `true` if and only if `value` can be found in $(D 11607 range), which is assumed to be sorted. Performs $(BIGOH log(r.length)) 11608 evaluations of `pred`. 11609 */ 11610 11611 bool contains(V)(V value) 11612 if (isRandomAccessRange!Range) 11613 { 11614 if (empty) return false; 11615 immutable i = getTransitionIndex!(SearchPolicy.binarySearch, geq)(value); 11616 if (i >= length) return false; 11617 return !predFun(value, _input[i]); 11618 } 11619 11620 /** 11621 Like `contains`, but the value is specified before the range. 11622 */ 11623 bool opBinaryRight(string op, V)(V value) 11624 if (op == "in" && isRandomAccessRange!Range) 11625 { 11626 return contains(value); 11627 } 11628 11629 // groupBy 11630 /** 11631 Returns a range of subranges of elements that are equivalent according to the 11632 sorting relation. 11633 */ 11634 auto groupBy()() 11635 { 11636 import std.algorithm.iteration : chunkBy; 11637 return _input.chunkBy!((a, b) => !predFun(a, b) && !predFun(b, a)); 11638 } 11639 } 11640 11641 /// ditto 11642 template SortedRange(Range, alias pred = "a < b", 11643 SortedRangeOptions opt = SortedRangeOptions.assumeSorted) 11644 if (isInstanceOf!(SortedRange, Range)) 11645 { 11646 // Avoid nesting SortedRange types (see https://issues.dlang.org/show_bug.cgi?id=18933); 11647 alias SortedRange = SortedRange!(Unqual!(typeof(Range._input)), pred, opt); 11648 } 11649 11650 /// 11651 @safe unittest 11652 { 11653 import std.algorithm.sorting : sort; 11654 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11655 auto r = assumeSorted(a); 11656 assert(r.contains(3)); 11657 assert(!(32 in r)); 11658 auto r1 = sort!"a > b"(a); 11659 assert(3 in r1); 11660 assert(!r1.contains(32)); 11661 assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]); 11662 } 11663 11664 /** 11665 `SortedRange` could accept ranges weaker than random-access, but it 11666 is unable to provide interesting functionality for them. Therefore, 11667 `SortedRange` is currently restricted to random-access ranges. 11668 11669 No copy of the original range is ever made. If the underlying range is 11670 changed concurrently with its corresponding `SortedRange` in ways 11671 that break its sorted-ness, `SortedRange` will work erratically. 11672 */ 11673 @safe unittest 11674 { 11675 import std.algorithm.mutation : swap; 11676 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11677 auto r = assumeSorted(a); 11678 assert(r.contains(42)); 11679 swap(a[3], a[5]); // illegal to break sortedness of original range 11680 assert(!r.contains(42)); // passes although it shouldn't 11681 } 11682 11683 /** 11684 `SortedRange` can be searched with predicates that do not take 11685 two elements of the underlying range as arguments. 11686 11687 This is useful, if a range of structs is sorted by a member and you 11688 want to search in that range by only providing a value for that member. 11689 11690 */ 11691 @safe unittest 11692 { 11693 import std.algorithm.comparison : equal; 11694 static struct S { int i; } 11695 static bool byI(A, B)(A a, B b) 11696 { 11697 static if (is(A == S)) 11698 return a.i < b; 11699 else 11700 return a < b.i; 11701 } 11702 auto r = assumeSorted!byI([S(1), S(2), S(3)]); 11703 auto lessThanTwo = r.lowerBound(2); 11704 assert(equal(lessThanTwo, [S(1)])); 11705 } 11706 11707 @safe unittest 11708 { 11709 import std.exception : assertThrown, assertNotThrown; 11710 11711 assertNotThrown(SortedRange!(int[])([ 1, 3, 10, 5, 7 ])); 11712 assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 10, 5, 7 ])); 11713 11714 // these two checks are implementation depended 11715 assertNotThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 12, 2 ])); 11716 assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 2, 12 ])); 11717 } 11718 11719 @safe unittest 11720 { 11721 import std.algorithm.comparison : equal; 11722 11723 auto a = [ 10, 20, 30, 30, 30, 40, 40, 50, 60 ]; 11724 auto r = assumeSorted(a).trisect(30); 11725 assert(equal(r[0], [ 10, 20 ])); 11726 assert(equal(r[1], [ 30, 30, 30 ])); 11727 assert(equal(r[2], [ 40, 40, 50, 60 ])); 11728 11729 r = assumeSorted(a).trisect(35); 11730 assert(equal(r[0], [ 10, 20, 30, 30, 30 ])); 11731 assert(r[1].empty); 11732 assert(equal(r[2], [ 40, 40, 50, 60 ])); 11733 } 11734 11735 @safe unittest 11736 { 11737 import std.algorithm.comparison : equal; 11738 auto a = [ "A", "AG", "B", "E", "F" ]; 11739 auto r = assumeSorted!"cmp(a,b) < 0"(a).trisect("B"w); 11740 assert(equal(r[0], [ "A", "AG" ])); 11741 assert(equal(r[1], [ "B" ])); 11742 assert(equal(r[2], [ "E", "F" ])); 11743 r = assumeSorted!"cmp(a,b) < 0"(a).trisect("A"d); 11744 assert(r[0].empty); 11745 assert(equal(r[1], [ "A" ])); 11746 assert(equal(r[2], [ "AG", "B", "E", "F" ])); 11747 } 11748 11749 @safe unittest 11750 { 11751 import std.algorithm.comparison : equal; 11752 static void test(SearchPolicy pol)() 11753 { 11754 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11755 auto r = assumeSorted(a); 11756 assert(equal(r.lowerBound(42), [1, 2, 3])); 11757 11758 assert(equal(r.lowerBound!(pol)(42), [1, 2, 3])); 11759 assert(equal(r.lowerBound!(pol)(41), [1, 2, 3])); 11760 assert(equal(r.lowerBound!(pol)(43), [1, 2, 3, 42])); 11761 assert(equal(r.lowerBound!(pol)(51), [1, 2, 3, 42])); 11762 assert(equal(r.lowerBound!(pol)(3), [1, 2])); 11763 assert(equal(r.lowerBound!(pol)(55), [1, 2, 3, 42, 52])); 11764 assert(equal(r.lowerBound!(pol)(420), a)); 11765 assert(equal(r.lowerBound!(pol)(0), a[0 .. 0])); 11766 11767 assert(equal(r.upperBound!(pol)(42), [52, 64])); 11768 assert(equal(r.upperBound!(pol)(41), [42, 52, 64])); 11769 assert(equal(r.upperBound!(pol)(43), [52, 64])); 11770 assert(equal(r.upperBound!(pol)(51), [52, 64])); 11771 assert(equal(r.upperBound!(pol)(53), [64])); 11772 assert(equal(r.upperBound!(pol)(55), [64])); 11773 assert(equal(r.upperBound!(pol)(420), a[0 .. 0])); 11774 assert(equal(r.upperBound!(pol)(0), a)); 11775 } 11776 11777 test!(SearchPolicy.trot)(); 11778 test!(SearchPolicy.gallop)(); 11779 test!(SearchPolicy.trotBackwards)(); 11780 test!(SearchPolicy.gallopBackwards)(); 11781 test!(SearchPolicy.binarySearch)(); 11782 } 11783 11784 @safe unittest 11785 { 11786 // Check for small arrays 11787 int[] a; 11788 auto r = assumeSorted(a); 11789 a = [ 1 ]; 11790 r = assumeSorted(a); 11791 a = [ 1, 2 ]; 11792 r = assumeSorted(a); 11793 a = [ 1, 2, 3 ]; 11794 r = assumeSorted(a); 11795 } 11796 11797 @safe unittest 11798 { 11799 import std.algorithm.mutation : swap; 11800 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11801 auto r = assumeSorted(a); 11802 assert(r.contains(42)); 11803 swap(a[3], a[5]); // illegal to break sortedness of original range 11804 assert(!r.contains(42)); // passes although it shouldn't 11805 } 11806 11807 @betterC @nogc nothrow @safe unittest 11808 { 11809 static immutable(int)[] arr = [ 1, 2, 3 ]; 11810 auto s = assumeSorted(arr); 11811 } 11812 11813 @system unittest 11814 { 11815 import std.algorithm.comparison : equal; 11816 int[] arr = [100, 101, 102, 200, 201, 300]; 11817 auto s = assumeSorted!((a, b) => a / 100 < b / 100)(arr); 11818 assert(s.groupBy.equal!equal([[100, 101, 102], [200, 201], [300]])); 11819 } 11820 11821 // Test on an input range 11822 @system unittest 11823 { 11824 import std.conv : text; 11825 import std.file : exists, remove, tempDir; 11826 import std.path : buildPath; 11827 import std.stdio : File; 11828 import std.uuid : randomUUID; 11829 auto name = buildPath(tempDir(), "test.std.range.line-" ~ text(__LINE__) ~ 11830 "." ~ randomUUID().toString()); 11831 auto f = File(name, "w"); 11832 scope(exit) if (exists(name)) remove(name); 11833 // write a sorted range of lines to the file 11834 f.write("abc\ndef\nghi\njkl"); 11835 f.close(); 11836 f.open(name, "r"); 11837 auto r = assumeSorted(f.byLine()); 11838 auto r1 = r.upperBound!(SearchPolicy.linear)("def"); 11839 assert(r1.front == "ghi", r1.front); 11840 f.close(); 11841 } 11842 11843 // https://issues.dlang.org/show_bug.cgi?id=19337 11844 @safe unittest 11845 { 11846 import std.algorithm.sorting : sort; 11847 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11848 a.sort.sort!"a > b"; 11849 } 11850 11851 /** 11852 Assumes `r` is sorted by predicate `pred` and returns the 11853 corresponding $(D SortedRange!(pred, R)) having `r` as support. 11854 To check for sorted-ness at 11855 cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting). 11856 */ 11857 auto assumeSorted(alias pred = "a < b", R)(R r) 11858 if (isInputRange!(Unqual!R)) 11859 { 11860 // Avoid senseless `SortedRange!(SortedRange!(...), pred)` nesting. 11861 static if (is(R == SortedRange!(RRange, RPred), RRange, alias RPred)) 11862 { 11863 static if (isInputRange!R && __traits(isSame, pred, RPred)) 11864 // If the predicate is the same and we don't need to cast away 11865 // constness for the result to be an input range. 11866 return r; 11867 else 11868 return SortedRange!(Unqual!(typeof(r._input)), pred)(r._input); 11869 } 11870 else 11871 { 11872 return SortedRange!(Unqual!R, pred)(r); 11873 } 11874 } 11875 11876 /// 11877 @safe unittest 11878 { 11879 import std.algorithm.comparison : equal; 11880 11881 int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 11882 auto p = assumeSorted(a); 11883 11884 assert(equal(p.lowerBound(4), [0, 1, 2, 3])); 11885 assert(equal(p.lowerBound(5), [0, 1, 2, 3, 4])); 11886 assert(equal(p.lowerBound(6), [0, 1, 2, 3, 4, 5])); 11887 assert(equal(p.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6])); 11888 } 11889 11890 @safe unittest 11891 { 11892 import std.algorithm.comparison : equal; 11893 static assert(isRandomAccessRange!(SortedRange!(int[]))); 11894 int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11895 auto p = assumeSorted(a).upperBound(3); 11896 assert(equal(p, [4, 4, 5, 6 ])); 11897 p = assumeSorted(a).upperBound(4.2); 11898 assert(equal(p, [ 5, 6 ])); 11899 11900 // https://issues.dlang.org/show_bug.cgi?id=18933 11901 // don't create senselessly nested SortedRange types. 11902 assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted(a))))); 11903 assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted!"a > b"(a))))); 11904 } 11905 11906 @safe unittest 11907 { 11908 import std.algorithm.comparison : equal; 11909 import std.conv : text; 11910 11911 int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11912 auto p = assumeSorted(a).equalRange(3); 11913 assert(equal(p, [ 3, 3, 3 ]), text(p)); 11914 p = assumeSorted(a).equalRange(4); 11915 assert(equal(p, [ 4, 4 ]), text(p)); 11916 p = assumeSorted(a).equalRange(2); 11917 assert(equal(p, [ 2 ])); 11918 p = assumeSorted(a).equalRange(0); 11919 assert(p.empty); 11920 p = assumeSorted(a).equalRange(7); 11921 assert(p.empty); 11922 p = assumeSorted(a).equalRange(3.0); 11923 assert(equal(p, [ 3, 3, 3])); 11924 } 11925 11926 @safe unittest 11927 { 11928 int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11929 if (a.length) 11930 { 11931 auto b = a[a.length / 2]; 11932 //auto r = sort(a); 11933 //assert(r.contains(b)); 11934 } 11935 } 11936 11937 @safe unittest 11938 { 11939 auto a = [ 5, 7, 34, 345, 677 ]; 11940 auto r = assumeSorted(a); 11941 a = null; 11942 r = assumeSorted(a); 11943 a = [ 1 ]; 11944 r = assumeSorted(a); 11945 } 11946 11947 // https://issues.dlang.org/show_bug.cgi?id=15003 11948 @nogc @safe unittest 11949 { 11950 static immutable a = [1, 2, 3, 4]; 11951 auto r = a.assumeSorted; 11952 } 11953 11954 /++ 11955 Wrapper which effectively makes it possible to pass a range by reference. 11956 Both the original range and the RefRange will always have the exact same 11957 elements. Any operation done on one will affect the other. So, for instance, 11958 if it's passed to a function which would implicitly copy the original range 11959 if it were passed to it, the original range is $(I not) copied but is 11960 consumed as if it were a reference type. 11961 11962 Note: 11963 `save` works as normal and operates on a new range, so if 11964 `save` is ever called on the `RefRange`, then no operations on the 11965 saved range will affect the original. 11966 11967 Params: 11968 range = the range to construct the `RefRange` from 11969 11970 Returns: 11971 A `RefRange`. If the given range is a class type 11972 (and thus is already a reference type), then the original 11973 range is returned rather than a `RefRange`. 11974 +/ 11975 struct RefRange(R) 11976 if (isInputRange!R) 11977 { 11978 public: 11979 11980 /++ +/ 11981 this(R* range) @safe pure nothrow 11982 { 11983 _range = range; 11984 } 11985 11986 11987 /++ 11988 This does not assign the pointer of `rhs` to this `RefRange`. 11989 Rather it assigns the range pointed to by `rhs` to the range pointed 11990 to by this `RefRange`. This is because $(I any) operation on a 11991 `RefRange` is the same is if it occurred to the original range. The 11992 one exception is when a `RefRange` is assigned `null` either 11993 directly or because `rhs` is `null`. In that case, `RefRange` 11994 no longer refers to the original range but is `null`. 11995 +/ 11996 auto opAssign(RefRange rhs) 11997 { 11998 if (_range && rhs._range) 11999 *_range = *rhs._range; 12000 else 12001 _range = rhs._range; 12002 12003 return this; 12004 } 12005 12006 /++ +/ 12007 void opAssign(typeof(null) rhs) 12008 { 12009 _range = null; 12010 } 12011 12012 12013 /++ 12014 A pointer to the wrapped range. 12015 +/ 12016 @property inout(R*) ptr() @safe inout pure nothrow 12017 { 12018 return _range; 12019 } 12020 12021 12022 version (StdDdoc) 12023 { 12024 /++ +/ 12025 @property auto front() {assert(0);} 12026 /++ Ditto +/ 12027 @property auto front() const {assert(0);} 12028 /++ Ditto +/ 12029 @property auto front(ElementType!R value) {assert(0);} 12030 } 12031 else 12032 { 12033 @property auto front() 12034 { 12035 return (*_range).front; 12036 } 12037 12038 static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const 12039 { 12040 return (*_range).front; 12041 } 12042 12043 static if (is(typeof((*_range).front = (*_range).front))) @property auto front(ElementType!R value) 12044 { 12045 return (*_range).front = value; 12046 } 12047 } 12048 12049 12050 version (StdDdoc) 12051 { 12052 @property bool empty(); /// 12053 @property bool empty() const; ///Ditto 12054 } 12055 else static if (isInfinite!R) 12056 enum empty = false; 12057 else 12058 { 12059 @property bool empty() 12060 { 12061 return (*_range).empty; 12062 } 12063 12064 static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const 12065 { 12066 return (*_range).empty; 12067 } 12068 } 12069 12070 12071 /++ +/ 12072 void popFront() 12073 { 12074 return (*_range).popFront(); 12075 } 12076 12077 12078 version (StdDdoc) 12079 { 12080 /++ 12081 Only defined if `isForwardRange!R` is `true`. 12082 +/ 12083 @property auto save() {assert(0);} 12084 /++ Ditto +/ 12085 @property auto save() const {assert(0);} 12086 /++ Ditto +/ 12087 auto opSlice() {assert(0);} 12088 /++ Ditto +/ 12089 auto opSlice() const {assert(0);} 12090 } 12091 else static if (isForwardRange!R) 12092 { 12093 import std.traits : isSafe; 12094 private alias S = typeof((*_range).save); 12095 12096 static if (is(typeof((*cast(const R*)_range).save))) 12097 private alias CS = typeof((*cast(const R*)_range).save); 12098 12099 static if (isSafe!((R* r) => (*r).save)) 12100 { 12101 @property RefRange!S save() @trusted 12102 { 12103 mixin(_genSave()); 12104 } 12105 12106 static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const 12107 { 12108 mixin(_genSave()); 12109 } 12110 } 12111 else 12112 { 12113 @property RefRange!S save() 12114 { 12115 mixin(_genSave()); 12116 } 12117 12118 static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const 12119 { 12120 mixin(_genSave()); 12121 } 12122 } 12123 12124 auto opSlice()() 12125 { 12126 return save; 12127 } 12128 12129 auto opSlice()() const 12130 { 12131 return save; 12132 } 12133 12134 private static string _genSave() @safe pure nothrow 12135 { 12136 return `import core.lifetime : emplace;` ~ 12137 `alias S = typeof((*_range).save);` ~ 12138 `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~ 12139 `auto mem = new void[S.sizeof];` ~ 12140 `emplace!S(mem, cast(S)(*_range).save);` ~ 12141 `return RefRange!S(cast(S*) mem.ptr);`; 12142 } 12143 12144 static assert(isForwardRange!RefRange); 12145 } 12146 12147 12148 version (StdDdoc) 12149 { 12150 /++ 12151 Only defined if `isBidirectionalRange!R` is `true`. 12152 +/ 12153 @property auto back() {assert(0);} 12154 /++ Ditto +/ 12155 @property auto back() const {assert(0);} 12156 /++ Ditto +/ 12157 @property auto back(ElementType!R value) {assert(0);} 12158 } 12159 else static if (isBidirectionalRange!R) 12160 { 12161 @property auto back() 12162 { 12163 return (*_range).back; 12164 } 12165 12166 static if (is(typeof((*(cast(const R*)_range)).back))) @property auto back() const 12167 { 12168 return (*_range).back; 12169 } 12170 12171 static if (is(typeof((*_range).back = (*_range).back))) @property auto back(ElementType!R value) 12172 { 12173 return (*_range).back = value; 12174 } 12175 } 12176 12177 12178 /++ Ditto +/ 12179 static if (isBidirectionalRange!R) void popBack() 12180 { 12181 return (*_range).popBack(); 12182 } 12183 12184 12185 version (StdDdoc) 12186 { 12187 /++ 12188 Only defined if `isRandomAccessRange!R` is `true`. 12189 +/ 12190 auto ref opIndex(IndexType)(IndexType index) {assert(0);} 12191 12192 /++ Ditto +/ 12193 auto ref opIndex(IndexType)(IndexType index) const {assert(0);} 12194 } 12195 else static if (isRandomAccessRange!R) 12196 { 12197 auto ref opIndex(IndexType)(IndexType index) 12198 if (is(typeof((*_range)[index]))) 12199 { 12200 return (*_range)[index]; 12201 } 12202 12203 auto ref opIndex(IndexType)(IndexType index) const 12204 if (is(typeof((*cast(const R*)_range)[index]))) 12205 { 12206 return (*_range)[index]; 12207 } 12208 } 12209 12210 12211 /++ 12212 Only defined if `hasMobileElements!R` and `isForwardRange!R` are 12213 `true`. 12214 +/ 12215 static if (hasMobileElements!R && isForwardRange!R) auto moveFront() 12216 { 12217 return (*_range).moveFront(); 12218 } 12219 12220 12221 /++ 12222 Only defined if `hasMobileElements!R` and `isBidirectionalRange!R` 12223 are `true`. 12224 +/ 12225 static if (hasMobileElements!R && isBidirectionalRange!R) auto moveBack() 12226 { 12227 return (*_range).moveBack(); 12228 } 12229 12230 12231 /++ 12232 Only defined if `hasMobileElements!R` and `isRandomAccessRange!R` 12233 are `true`. 12234 +/ 12235 static if (hasMobileElements!R && isRandomAccessRange!R) auto moveAt(size_t index) 12236 { 12237 return (*_range).moveAt(index); 12238 } 12239 12240 12241 version (StdDdoc) 12242 { 12243 /// Only defined if `hasLength!R` is `true`. 12244 @property size_t length(); 12245 /// ditto 12246 @property size_t length() const; 12247 /// Ditto 12248 alias opDollar = length; 12249 } 12250 else static if (hasLength!R) 12251 { 12252 @property auto length() 12253 { 12254 return (*_range).length; 12255 } 12256 static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const 12257 { 12258 return (*_range).length; 12259 } 12260 alias opDollar = length; 12261 } 12262 12263 12264 version (StdDdoc) 12265 { 12266 /++ 12267 Only defined if `hasSlicing!R` is `true`. 12268 +/ 12269 auto opSlice(IndexType1, IndexType2) 12270 (IndexType1 begin, IndexType2 end) {assert(0);} 12271 12272 /++ Ditto +/ 12273 auto opSlice(IndexType1, IndexType2) 12274 (IndexType1 begin, IndexType2 end) const {assert(0);} 12275 } 12276 else static if (hasSlicing!R) 12277 { 12278 private alias T = typeof((*_range)[1 .. 2]); 12279 static if (is(typeof((*cast(const R*)_range)[1 .. 2]))) 12280 { 12281 private alias CT = typeof((*cast(const R*)_range)[1 .. 2]); 12282 } 12283 12284 RefRange!T opSlice(IndexType1, IndexType2) 12285 (IndexType1 begin, IndexType2 end) 12286 if (is(typeof((*_range)[begin .. end]))) 12287 { 12288 mixin(_genOpSlice()); 12289 } 12290 12291 RefRange!CT opSlice(IndexType1, IndexType2) 12292 (IndexType1 begin, IndexType2 end) const 12293 if (is(typeof((*cast(const R*)_range)[begin .. end]))) 12294 { 12295 mixin(_genOpSlice()); 12296 } 12297 12298 private static string _genOpSlice() @safe pure nothrow 12299 { 12300 return `import core.lifetime : emplace;` ~ 12301 `alias S = typeof((*_range)[begin .. end]);` ~ 12302 `static assert(hasSlicing!S, S.stringof ~ " is not sliceable.");` ~ 12303 `auto mem = new void[S.sizeof];` ~ 12304 `emplace!S(mem, cast(S)(*_range)[begin .. end]);` ~ 12305 `return RefRange!S(cast(S*) mem.ptr);`; 12306 } 12307 } 12308 12309 12310 private: 12311 12312 R* _range; 12313 } 12314 12315 /// Basic Example 12316 @system unittest 12317 { 12318 import std.algorithm.searching : find; 12319 ubyte[] buffer = [1, 9, 45, 12, 22]; 12320 auto found1 = find(buffer, 45); 12321 assert(found1 == [45, 12, 22]); 12322 assert(buffer == [1, 9, 45, 12, 22]); 12323 12324 auto wrapped1 = refRange(&buffer); 12325 auto found2 = find(wrapped1, 45); 12326 assert(*found2.ptr == [45, 12, 22]); 12327 assert(buffer == [45, 12, 22]); 12328 12329 auto found3 = find(wrapped1.save, 22); 12330 assert(*found3.ptr == [22]); 12331 assert(buffer == [45, 12, 22]); 12332 12333 string str = "hello world"; 12334 auto wrappedStr = refRange(&str); 12335 assert(str.front == 'h'); 12336 str.popFrontN(5); 12337 assert(str == " world"); 12338 assert(wrappedStr.front == ' '); 12339 assert(*wrappedStr.ptr == " world"); 12340 } 12341 12342 /// opAssign Example. 12343 @system unittest 12344 { 12345 ubyte[] buffer1 = [1, 2, 3, 4, 5]; 12346 ubyte[] buffer2 = [6, 7, 8, 9, 10]; 12347 auto wrapped1 = refRange(&buffer1); 12348 auto wrapped2 = refRange(&buffer2); 12349 assert(wrapped1.ptr is &buffer1); 12350 assert(wrapped2.ptr is &buffer2); 12351 assert(wrapped1.ptr !is wrapped2.ptr); 12352 assert(buffer1 != buffer2); 12353 12354 wrapped1 = wrapped2; 12355 12356 //Everything points to the same stuff as before. 12357 assert(wrapped1.ptr is &buffer1); 12358 assert(wrapped2.ptr is &buffer2); 12359 assert(wrapped1.ptr !is wrapped2.ptr); 12360 12361 //But buffer1 has changed due to the assignment. 12362 assert(buffer1 == [6, 7, 8, 9, 10]); 12363 assert(buffer2 == [6, 7, 8, 9, 10]); 12364 12365 buffer2 = [11, 12, 13, 14, 15]; 12366 12367 //Everything points to the same stuff as before. 12368 assert(wrapped1.ptr is &buffer1); 12369 assert(wrapped2.ptr is &buffer2); 12370 assert(wrapped1.ptr !is wrapped2.ptr); 12371 12372 //But buffer2 has changed due to the assignment. 12373 assert(buffer1 == [6, 7, 8, 9, 10]); 12374 assert(buffer2 == [11, 12, 13, 14, 15]); 12375 12376 wrapped2 = null; 12377 12378 //The pointer changed for wrapped2 but not wrapped1. 12379 assert(wrapped1.ptr is &buffer1); 12380 assert(wrapped2.ptr is null); 12381 assert(wrapped1.ptr !is wrapped2.ptr); 12382 12383 //buffer2 is not affected by the assignment. 12384 assert(buffer1 == [6, 7, 8, 9, 10]); 12385 assert(buffer2 == [11, 12, 13, 14, 15]); 12386 } 12387 12388 @system unittest 12389 { 12390 import std.algorithm.iteration : filter; 12391 { 12392 ubyte[] buffer = [1, 2, 3, 4, 5]; 12393 auto wrapper = refRange(&buffer); 12394 auto p = wrapper.ptr; 12395 auto f = wrapper.front; 12396 wrapper.front = f; 12397 auto e = wrapper.empty; 12398 wrapper.popFront(); 12399 auto s = wrapper.save; 12400 auto b = wrapper.back; 12401 wrapper.back = b; 12402 wrapper.popBack(); 12403 auto i = wrapper[0]; 12404 wrapper.moveFront(); 12405 wrapper.moveBack(); 12406 wrapper.moveAt(0); 12407 auto l = wrapper.length; 12408 auto sl = wrapper[0 .. 1]; 12409 assert(wrapper[0 .. $].length == buffer[0 .. $].length); 12410 } 12411 12412 { 12413 ubyte[] buffer = [1, 2, 3, 4, 5]; 12414 const wrapper = refRange(&buffer); 12415 const p = wrapper.ptr; 12416 const f = wrapper.front; 12417 const e = wrapper.empty; 12418 const s = wrapper.save; 12419 const b = wrapper.back; 12420 const i = wrapper[0]; 12421 const l = wrapper.length; 12422 const sl = wrapper[0 .. 1]; 12423 } 12424 12425 { 12426 ubyte[] buffer = [1, 2, 3, 4, 5]; 12427 auto filtered = filter!"true"(buffer); 12428 auto wrapper = refRange(&filtered); 12429 auto p = wrapper.ptr; 12430 auto f = wrapper.front; 12431 wrapper.front = f; 12432 auto e = wrapper.empty; 12433 wrapper.popFront(); 12434 auto s = wrapper.save; 12435 wrapper.moveFront(); 12436 } 12437 12438 { 12439 ubyte[] buffer = [1, 2, 3, 4, 5]; 12440 auto filtered = filter!"true"(buffer); 12441 const wrapper = refRange(&filtered); 12442 const p = wrapper.ptr; 12443 12444 //Cannot currently be const. filter needs to be updated to handle const. 12445 /+ 12446 const f = wrapper.front; 12447 const e = wrapper.empty; 12448 const s = wrapper.save; 12449 +/ 12450 } 12451 12452 { 12453 string str = "hello world"; 12454 auto wrapper = refRange(&str); 12455 auto p = wrapper.ptr; 12456 auto f = wrapper.front; 12457 auto e = wrapper.empty; 12458 wrapper.popFront(); 12459 auto s = wrapper.save; 12460 auto b = wrapper.back; 12461 wrapper.popBack(); 12462 } 12463 12464 { 12465 // https://issues.dlang.org/show_bug.cgi?id=16534 12466 // opDollar should be defined if the wrapped range defines length. 12467 auto range = 10.iota.takeExactly(5); 12468 auto wrapper = refRange(&range); 12469 assert(wrapper.length == 5); 12470 assert(wrapper[0 .. $ - 1].length == 4); 12471 } 12472 } 12473 12474 //Test assignment. 12475 @system unittest 12476 { 12477 ubyte[] buffer1 = [1, 2, 3, 4, 5]; 12478 ubyte[] buffer2 = [6, 7, 8, 9, 10]; 12479 RefRange!(ubyte[]) wrapper1; 12480 RefRange!(ubyte[]) wrapper2 = refRange(&buffer2); 12481 assert(wrapper1.ptr is null); 12482 assert(wrapper2.ptr is &buffer2); 12483 12484 wrapper1 = refRange(&buffer1); 12485 assert(wrapper1.ptr is &buffer1); 12486 12487 wrapper1 = wrapper2; 12488 assert(wrapper1.ptr is &buffer1); 12489 assert(buffer1 == buffer2); 12490 12491 wrapper1 = RefRange!(ubyte[]).init; 12492 assert(wrapper1.ptr is null); 12493 assert(wrapper2.ptr is &buffer2); 12494 assert(buffer1 == buffer2); 12495 assert(buffer1 == [6, 7, 8, 9, 10]); 12496 12497 wrapper2 = null; 12498 assert(wrapper2.ptr is null); 12499 assert(buffer2 == [6, 7, 8, 9, 10]); 12500 } 12501 12502 @system unittest 12503 { 12504 import std.algorithm.comparison : equal; 12505 import std.algorithm.mutation : bringToFront; 12506 import std.algorithm.searching : commonPrefix, find, until; 12507 import std.algorithm.sorting : sort; 12508 12509 //Test that ranges are properly consumed. 12510 { 12511 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12512 auto wrapper = refRange(&arr); 12513 12514 assert(*find(wrapper, 41).ptr == [41, 3, 40, 4, 42, 9]); 12515 assert(arr == [41, 3, 40, 4, 42, 9]); 12516 12517 assert(*drop(wrapper, 2).ptr == [40, 4, 42, 9]); 12518 assert(arr == [40, 4, 42, 9]); 12519 12520 assert(equal(until(wrapper, 42), [40, 4])); 12521 assert(arr == [42, 9]); 12522 12523 assert(find(wrapper, 12).empty); 12524 assert(arr.empty); 12525 } 12526 12527 { 12528 string str = "Hello, world-like object."; 12529 auto wrapper = refRange(&str); 12530 12531 assert(*find(wrapper, "l").ptr == "llo, world-like object."); 12532 assert(str == "llo, world-like object."); 12533 12534 assert(equal(take(wrapper, 5), "llo, ")); 12535 assert(str == "world-like object."); 12536 } 12537 12538 //Test that operating on saved ranges does not consume the original. 12539 { 12540 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12541 auto wrapper = refRange(&arr); 12542 auto saved = wrapper.save; 12543 saved.popFrontN(3); 12544 assert(*saved.ptr == [41, 3, 40, 4, 42, 9]); 12545 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12546 } 12547 12548 { 12549 string str = "Hello, world-like object."; 12550 auto wrapper = refRange(&str); 12551 auto saved = wrapper.save; 12552 saved.popFrontN(13); 12553 assert(*saved.ptr == "like object."); 12554 assert(str == "Hello, world-like object."); 12555 } 12556 12557 //Test that functions which use save work properly. 12558 { 12559 int[] arr = [1, 42]; 12560 auto wrapper = refRange(&arr); 12561 assert(equal(commonPrefix(wrapper, [1, 27]), [1])); 12562 } 12563 12564 { 12565 int[] arr = [4, 5, 6, 7, 1, 2, 3]; 12566 auto wrapper = refRange(&arr); 12567 assert(bringToFront(wrapper[0 .. 4], wrapper[4 .. arr.length]) == 3); 12568 assert(arr == [1, 2, 3, 4, 5, 6, 7]); 12569 } 12570 12571 //Test bidirectional functions. 12572 { 12573 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12574 auto wrapper = refRange(&arr); 12575 12576 assert(wrapper.back == 9); 12577 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12578 12579 wrapper.popBack(); 12580 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42]); 12581 } 12582 12583 { 12584 string str = "Hello, world-like object."; 12585 auto wrapper = refRange(&str); 12586 12587 assert(wrapper.back == '.'); 12588 assert(str == "Hello, world-like object."); 12589 12590 wrapper.popBack(); 12591 assert(str == "Hello, world-like object"); 12592 } 12593 12594 //Test random access functions. 12595 { 12596 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12597 auto wrapper = refRange(&arr); 12598 12599 assert(wrapper[2] == 2); 12600 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12601 12602 assert(*wrapper[3 .. 6].ptr != null, [41, 3, 40]); 12603 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12604 } 12605 12606 //Test move functions. 12607 { 12608 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12609 auto wrapper = refRange(&arr); 12610 12611 auto t1 = wrapper.moveFront(); 12612 auto t2 = wrapper.moveBack(); 12613 wrapper.front = t2; 12614 wrapper.back = t1; 12615 assert(arr == [9, 42, 2, 41, 3, 40, 4, 42, 1]); 12616 12617 sort(wrapper.save); 12618 assert(arr == [1, 2, 3, 4, 9, 40, 41, 42, 42]); 12619 } 12620 } 12621 12622 @system unittest 12623 { 12624 struct S 12625 { 12626 @property int front() @safe const pure nothrow { return 0; } 12627 enum bool empty = false; 12628 void popFront() @safe pure nothrow { } 12629 @property auto save() @safe pure nothrow return scope { return this; } 12630 } 12631 12632 S s; 12633 auto wrapper = refRange(&s); 12634 static assert(isInfinite!(typeof(wrapper))); 12635 } 12636 12637 @system unittest 12638 { 12639 class C 12640 { 12641 @property int front() @safe const pure nothrow { return 0; } 12642 @property bool empty() @safe const pure nothrow { return false; } 12643 void popFront() @safe pure nothrow { } 12644 @property auto save() @safe pure nothrow return scope { return this; } 12645 } 12646 static assert(isForwardRange!C); 12647 12648 auto c = new C; 12649 auto cWrapper = refRange(&c); 12650 static assert(is(typeof(cWrapper) == C)); 12651 assert(cWrapper is c); 12652 } 12653 12654 // https://issues.dlang.org/show_bug.cgi?id=14373 12655 @system unittest 12656 { 12657 static struct R 12658 { 12659 @property int front() {return 0;} 12660 void popFront() {empty = true;} 12661 bool empty = false; 12662 } 12663 R r; 12664 refRange(&r).popFront(); 12665 assert(r.empty); 12666 } 12667 12668 // https://issues.dlang.org/show_bug.cgi?id=14575 12669 @system unittest 12670 { 12671 struct R 12672 { 12673 Object front; 12674 alias back = front; 12675 bool empty = false; 12676 void popFront() {empty = true;} 12677 alias popBack = popFront; 12678 @property R save() {return this;} 12679 } 12680 static assert(isBidirectionalRange!R); 12681 R r; 12682 auto rr = refRange(&r); 12683 12684 struct R2 12685 { 12686 @property Object front() {return null;} 12687 @property const(Object) front() const {return null;} 12688 alias back = front; 12689 bool empty = false; 12690 void popFront() {empty = true;} 12691 alias popBack = popFront; 12692 @property R2 save() {return this;} 12693 } 12694 static assert(isBidirectionalRange!R2); 12695 R2 r2; 12696 auto rr2 = refRange(&r2); 12697 } 12698 12699 /// ditto 12700 auto refRange(R)(R* range) 12701 if (isInputRange!R) 12702 { 12703 static if (!is(R == class)) 12704 return RefRange!R(range); 12705 else 12706 return *range; 12707 } 12708 12709 // https://issues.dlang.org/show_bug.cgi?id=9060 12710 @safe unittest 12711 { 12712 import std.algorithm.iteration : map, joiner, group; 12713 import std.algorithm.searching : until; 12714 // fix for std.algorithm 12715 auto r = map!(x => 0)([1]); 12716 chain(r, r); 12717 zip(r, r); 12718 roundRobin(r, r); 12719 12720 struct NRAR { 12721 typeof(r) input; 12722 @property empty() { return input.empty; } 12723 @property front() { return input.front; } 12724 void popFront() { input.popFront(); } 12725 @property save() { return NRAR(input.save); } 12726 } 12727 auto n1 = NRAR(r); 12728 cycle(n1); // non random access range version 12729 12730 assumeSorted(r); 12731 12732 // fix for std.range 12733 joiner([r], [9]); 12734 12735 struct NRAR2 { 12736 NRAR input; 12737 @property empty() { return true; } 12738 @property front() { return input; } 12739 void popFront() { } 12740 @property save() { return NRAR2(input.save); } 12741 } 12742 auto n2 = NRAR2(n1); 12743 joiner(n2); 12744 12745 group(r); 12746 12747 until(r, 7); 12748 static void foo(R)(R r) { until!(x => x > 7)(r); } 12749 foo(r); 12750 } 12751 12752 private struct Bitwise(R) 12753 if (isInputRange!R && isIntegral!(ElementType!R)) 12754 { 12755 import std.traits : Unsigned; 12756 private: 12757 alias ElemType = ElementType!R; 12758 alias UnsignedElemType = Unsigned!ElemType; 12759 12760 R parent; 12761 enum bitsNum = ElemType.sizeof * 8; 12762 size_t maskPos = 1; 12763 12764 static if (isBidirectionalRange!R) 12765 { 12766 size_t backMaskPos = bitsNum; 12767 } 12768 12769 public: 12770 this()(auto ref R range) 12771 { 12772 parent = range; 12773 } 12774 12775 static if (isInfinite!R) 12776 { 12777 enum empty = false; 12778 } 12779 else 12780 { 12781 /** 12782 * Check if the range is empty 12783 * 12784 * Returns: a boolean true or false 12785 */ 12786 bool empty() 12787 { 12788 static if (hasLength!R) 12789 { 12790 return length == 0; 12791 } 12792 else static if (isBidirectionalRange!R) 12793 { 12794 if (parent.empty) 12795 { 12796 return true; 12797 } 12798 else 12799 { 12800 /* 12801 If we have consumed the last element of the range both from 12802 the front and the back, then the masks positions will overlap 12803 */ 12804 return parent.save.dropOne.empty && (maskPos > backMaskPos); 12805 } 12806 } 12807 else 12808 { 12809 /* 12810 If we consumed the last element of the range, but not all the 12811 bits in the last element 12812 */ 12813 return parent.empty; 12814 } 12815 } 12816 } 12817 12818 bool front() 12819 { 12820 assert(!empty); 12821 return (parent.front & mask(maskPos)) != 0; 12822 } 12823 12824 void popFront() 12825 { 12826 assert(!empty); 12827 ++maskPos; 12828 if (maskPos > bitsNum) 12829 { 12830 parent.popFront; 12831 maskPos = 1; 12832 } 12833 } 12834 12835 static if (hasLength!R) 12836 { 12837 size_t length() 12838 { 12839 auto len = parent.length * bitsNum - (maskPos - 1); 12840 static if (isBidirectionalRange!R) 12841 { 12842 len -= bitsNum - backMaskPos; 12843 } 12844 return len; 12845 } 12846 12847 alias opDollar = length; 12848 } 12849 12850 static if (isForwardRange!R) 12851 { 12852 typeof(this) save() 12853 { 12854 auto result = this; 12855 result.parent = parent.save; 12856 return result; 12857 } 12858 } 12859 12860 static if (isBidirectionalRange!R) 12861 { 12862 bool back() 12863 { 12864 assert(!empty); 12865 return (parent.back & mask(backMaskPos)) != 0; 12866 } 12867 12868 void popBack() 12869 { 12870 assert(!empty); 12871 --backMaskPos; 12872 if (backMaskPos == 0) 12873 { 12874 parent.popBack; 12875 backMaskPos = bitsNum; 12876 } 12877 } 12878 } 12879 12880 static if (isRandomAccessRange!R) 12881 { 12882 /** 12883 Return the `n`th bit within the range 12884 */ 12885 bool opIndex(size_t n) 12886 in 12887 { 12888 /* 12889 If it does not have the length property, it means that R is 12890 an infinite range 12891 */ 12892 static if (hasLength!R) 12893 { 12894 assert(n < length, "Index out of bounds"); 12895 } 12896 } 12897 do 12898 { 12899 immutable size_t remainingBits = bitsNum - maskPos + 1; 12900 // If n >= maskPos, then the bit sign will be 1, otherwise 0 12901 immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12902 /* 12903 By truncating n with remainingBits bits we have skipped the 12904 remaining bits in parent[0], so we need to add 1 to elemIndex. 12905 12906 Because bitsNum is a power of 2, n / bitsNum == n >> bitsNum.bsf 12907 */ 12908 import core.bitop : bsf; 12909 immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); 12910 12911 /* 12912 Since the indexing is from LSB to MSB, we need to index at the 12913 remainder of (n - remainingBits). 12914 12915 Because bitsNum is a power of 2, n % bitsNum == n & (bitsNum - 1) 12916 */ 12917 immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) 12918 + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); 12919 12920 return (parent[elemIndex] & mask(elemMaskPos)) != 0; 12921 } 12922 12923 static if (hasAssignableElements!R) 12924 { 12925 /** 12926 Assigns `flag` to the `n`th bit within the range 12927 */ 12928 void opIndexAssign(bool flag, size_t n) 12929 in 12930 { 12931 static if (hasLength!R) 12932 { 12933 assert(n < length, "Index out of bounds"); 12934 } 12935 } 12936 do 12937 { 12938 import core.bitop : bsf; 12939 12940 immutable size_t remainingBits = bitsNum - maskPos + 1; 12941 immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12942 immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); 12943 immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) 12944 + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); 12945 12946 auto elem = parent[elemIndex]; 12947 auto elemMask = mask(elemMaskPos); 12948 parent[elemIndex] = cast(UnsignedElemType)(flag * (elem | elemMask) 12949 + (flag ^ 1) * (elem & ~elemMask)); 12950 } 12951 } 12952 12953 Bitwise!R opSlice() 12954 { 12955 return this.save; 12956 } 12957 12958 Bitwise!R opSlice(size_t start, size_t end) 12959 in 12960 { 12961 assert(start < end, "Invalid bounds: end <= start"); 12962 } 12963 do 12964 { 12965 import core.bitop : bsf; 12966 12967 size_t remainingBits = bitsNum - maskPos + 1; 12968 ptrdiff_t sign = (remainingBits - start - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12969 immutable size_t startElemIndex = sign * (((start - remainingBits) >> bitsNum.bsf) + 1); 12970 immutable size_t startElemMaskPos = (sign ^ 1) * (maskPos + start) 12971 + sign * (1 + ((start - remainingBits) & (bitsNum - 1))); 12972 12973 immutable size_t sliceLen = end - start - 1; 12974 remainingBits = bitsNum - startElemMaskPos + 1; 12975 sign = (remainingBits - sliceLen - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12976 immutable size_t endElemIndex = startElemIndex 12977 + sign * (((sliceLen - remainingBits) >> bitsNum.bsf) + 1); 12978 immutable size_t endElemMaskPos = (sign ^ 1) * (startElemMaskPos + sliceLen) 12979 + sign * (1 + ((sliceLen - remainingBits) & (bitsNum - 1))); 12980 12981 typeof(return) result; 12982 // Get the slice to be returned from the parent 12983 result.parent = (parent[startElemIndex .. endElemIndex + 1]).save; 12984 result.maskPos = startElemMaskPos; 12985 static if (isBidirectionalRange!R) 12986 { 12987 result.backMaskPos = endElemMaskPos; 12988 } 12989 return result; 12990 } 12991 } 12992 12993 private: 12994 auto mask(size_t maskPos) 12995 { 12996 return (1UL << (maskPos - 1UL)); 12997 } 12998 } 12999 13000 /** 13001 Bitwise adapter over an integral type range. Consumes the range elements bit by 13002 bit, from the least significant bit to the most significant bit. 13003 13004 Params: 13005 R = an integral $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to iterate over 13006 range = range to consume bit by by 13007 13008 Returns: 13009 A `Bitwise` input range with propagated forward, bidirectional 13010 and random access capabilities 13011 */ 13012 auto bitwise(R)(auto ref R range) 13013 if (isInputRange!R && isIntegral!(ElementType!R)) 13014 { 13015 return Bitwise!R(range); 13016 } 13017 13018 /// 13019 @safe pure unittest 13020 { 13021 import std.algorithm.comparison : equal; 13022 import std.format : format; 13023 13024 // 00000011 00001001 13025 ubyte[] arr = [3, 9]; 13026 auto r = arr.bitwise; 13027 13028 // iterate through it as with any other range 13029 assert(format("%(%d%)", r) == "1100000010010000"); 13030 assert(format("%(%d%)", r.retro).equal("1100000010010000".retro)); 13031 13032 auto r2 = r[5 .. $]; 13033 // set a bit 13034 r[2] = 1; 13035 assert(arr[0] == 7); 13036 assert(r[5] == r2[0]); 13037 } 13038 13039 /// You can use bitwise to implement an uniform bool generator 13040 @safe unittest 13041 { 13042 import std.algorithm.comparison : equal; 13043 import std.random : rndGen; 13044 13045 auto rb = rndGen.bitwise; 13046 static assert(isInfinite!(typeof(rb))); 13047 13048 auto rb2 = rndGen.bitwise; 13049 // Don't forget that structs are passed by value 13050 assert(rb.take(10).equal(rb2.take(10))); 13051 } 13052 13053 // Test nogc inference 13054 @safe @nogc unittest 13055 { 13056 static ubyte[] arr = [3, 9]; 13057 auto bw = arr.bitwise; 13058 auto bw2 = bw[]; 13059 auto bw3 = bw[8 .. $]; 13060 bw3[2] = true; 13061 13062 assert(arr[1] == 13); 13063 assert(bw[$ - 6]); 13064 assert(bw[$ - 6] == bw2[$ - 6]); 13065 assert(bw[$ - 6] == bw3[$ - 6]); 13066 } 13067 13068 // Test all range types over all integral types 13069 @safe pure nothrow unittest 13070 { 13071 import std.meta : AliasSeq; 13072 import std.internal.test.dummyrange; 13073 13074 alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, 13075 long, ulong); 13076 foreach (IntegralType; IntegralTypes) 13077 { 13078 foreach (T; AllDummyRangesType!(IntegralType[])) 13079 { 13080 T a; 13081 auto bw = Bitwise!T(a); 13082 13083 static if (isForwardRange!T) 13084 { 13085 auto bwFwdSave = bw.save; 13086 } 13087 13088 static if (isBidirectionalRange!T) 13089 { 13090 auto bwBack = bw.save; 13091 auto bwBackSave = bw.save; 13092 } 13093 13094 static if (hasLength!T) 13095 { 13096 auto bwLength = bw.length; 13097 assert(bw.length == (IntegralType.sizeof * 8 * a.length)); 13098 static if (isForwardRange!T) 13099 { 13100 assert(bw.length == bwFwdSave.length); 13101 } 13102 } 13103 13104 // Make sure front and back are not the mechanisms that modify the range 13105 long numCalls = 42; 13106 bool initialFrontValue; 13107 13108 if (!bw.empty) 13109 { 13110 initialFrontValue = bw.front; 13111 } 13112 13113 while (!bw.empty && (--numCalls)) 13114 { 13115 bw.front; 13116 assert(bw.front == initialFrontValue); 13117 } 13118 13119 /* 13120 Check that empty works properly and that popFront does not get called 13121 more times than it should 13122 */ 13123 numCalls = 0; 13124 while (!bw.empty) 13125 { 13126 ++numCalls; 13127 13128 static if (hasLength!T) 13129 { 13130 assert(bw.length == bwLength); 13131 --bwLength; 13132 } 13133 13134 static if (isForwardRange!T) 13135 { 13136 assert(bw.front == bwFwdSave.front); 13137 bwFwdSave.popFront(); 13138 } 13139 13140 static if (isBidirectionalRange!T) 13141 { 13142 assert(bwBack.front == bwBackSave.front); 13143 bwBack.popBack(); 13144 bwBackSave.popBack(); 13145 } 13146 bw.popFront(); 13147 } 13148 13149 auto rangeLen = numCalls / (IntegralType.sizeof * 8); 13150 assert(numCalls == (IntegralType.sizeof * 8 * rangeLen)); 13151 assert(bw.empty); 13152 static if (isForwardRange!T) 13153 { 13154 assert(bwFwdSave.empty); 13155 } 13156 13157 static if (isBidirectionalRange!T) 13158 { 13159 assert(bwBack.empty); 13160 } 13161 } 13162 } 13163 } 13164 13165 // Test opIndex and opSlice 13166 @system unittest 13167 { 13168 import std.meta : AliasSeq; 13169 alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, 13170 long, ulong); 13171 foreach (IntegralType; IntegralTypes) 13172 { 13173 size_t bitsNum = IntegralType.sizeof * 8; 13174 13175 auto first = cast(IntegralType)(1); 13176 13177 // 2 ^ (bitsNum - 1) 13178 auto second = cast(IntegralType)(cast(IntegralType)(1) << (bitsNum - 2)); 13179 13180 IntegralType[] a = [first, second]; 13181 auto bw = Bitwise!(IntegralType[])(a); 13182 13183 // Check against lsb of a[0] 13184 assert(bw[0] == true); 13185 // Check against msb - 1 of a[1] 13186 assert(bw[2 * bitsNum - 2] == true); 13187 13188 bw.popFront(); 13189 assert(bw[2 * bitsNum - 3] == true); 13190 13191 import std.exception : assertThrown; 13192 13193 version (D_NoBoundsChecks) {} 13194 else 13195 { 13196 // Check out of bounds error 13197 assertThrown!Error(bw[2 * bitsNum - 1]); 13198 } 13199 13200 bw[2] = true; 13201 assert(bw[2] == true); 13202 bw.popFront(); 13203 assert(bw[1] == true); 13204 13205 auto bw2 = bw[0 .. $ - 5]; 13206 auto bw3 = bw2[]; 13207 assert(bw2.length == (bw.length - 5)); 13208 assert(bw2.length == bw3.length); 13209 bw2.popFront(); 13210 assert(bw2.length != bw3.length); 13211 } 13212 } 13213 13214 /********************************* 13215 * An OutputRange that discards the data it receives. 13216 */ 13217 struct NullSink 13218 { 13219 void put(E)(scope const E) pure @safe @nogc nothrow {} 13220 } 13221 13222 /// ditto 13223 auto ref nullSink() 13224 { 13225 static NullSink sink; 13226 return sink; 13227 } 13228 13229 /// 13230 @safe nothrow unittest 13231 { 13232 import std.algorithm.iteration : map; 13233 import std.algorithm.mutation : copy; 13234 [4, 5, 6].map!(x => x * 2).copy(nullSink); // data is discarded 13235 } 13236 13237 /// 13238 @safe unittest 13239 { 13240 import std.csv : csvNextToken; 13241 13242 string line = "a,b,c"; 13243 13244 // ignore the first column 13245 line.csvNextToken(nullSink, ',', '"'); 13246 line.popFront; 13247 13248 // look at the second column 13249 Appender!string app; 13250 line.csvNextToken(app, ',', '"'); 13251 assert(app.data == "b"); 13252 } 13253 13254 @safe unittest 13255 { 13256 auto r = 10.iota 13257 .tee(nullSink) 13258 .dropOne; 13259 13260 assert(r.front == 1); 13261 } 13262 13263 /++ 13264 13265 Implements a "tee" style pipe, wrapping an input range so that elements of the 13266 range can be passed to a provided function or $(LREF OutputRange) as they are 13267 iterated over. This is useful for printing out intermediate values in a long 13268 chain of range code, performing some operation with side-effects on each call 13269 to `front` or `popFront`, or diverting the elements of a range into an 13270 auxiliary $(LREF OutputRange). 13271 13272 It is important to note that as the resultant range is evaluated lazily, 13273 in the case of the version of `tee` that takes a function, the function 13274 will not actually be executed until the range is "walked" using functions 13275 that evaluate ranges, such as $(REF array, std,array) or 13276 $(REF fold, std,algorithm,iteration). 13277 13278 Params: 13279 pipeOnPop = If `Yes.pipeOnPop`, simply iterating the range without ever 13280 calling `front` is enough to have `tee` mirror elements to `outputRange` (or, 13281 respectively, `fun`). Note that each `popFront()` call will mirror the 13282 old `front` value, not the new one. This means that the last value will 13283 not be forwarded if the range isn't iterated until empty. If 13284 `No.pipeOnPop`, only elements for which `front` does get called will be 13285 also sent to `outputRange`/`fun`. If `front` is called twice for the same 13286 element, it will still be sent only once. If this caching is undesired, 13287 consider using $(REF map, std,algorithm,iteration) instead. 13288 inputRange = The input range being passed through. 13289 outputRange = This range will receive elements of `inputRange` progressively 13290 as iteration proceeds. 13291 fun = This function will be called with elements of `inputRange` 13292 progressively as iteration proceeds. 13293 13294 Returns: 13295 An input range that offers the elements of `inputRange`. Regardless of 13296 whether `inputRange` is a more powerful range (forward, bidirectional etc), 13297 the result is always an input range. Reading this causes `inputRange` to be 13298 iterated and returns its elements in turn. In addition, the same elements 13299 will be passed to `outputRange` or `fun` as well. 13300 13301 See_Also: $(REF each, std,algorithm,iteration) 13302 +/ 13303 auto tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1 inputRange, R2 outputRange) 13304 if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) 13305 { 13306 static struct Result 13307 { 13308 private R1 _input; 13309 private R2 _output; 13310 static if (!pipeOnPop) 13311 { 13312 private bool _frontAccessed; 13313 } 13314 13315 mixin ImplementLength!_input; 13316 13317 static if (isInfinite!R1) 13318 { 13319 enum bool empty = false; 13320 } 13321 else 13322 { 13323 @property bool empty() { return _input.empty; } 13324 } 13325 13326 void popFront() 13327 { 13328 assert(!_input.empty, "Attempting to popFront an empty tee"); 13329 static if (pipeOnPop) 13330 { 13331 put(_output, _input.front); 13332 } 13333 else 13334 { 13335 _frontAccessed = false; 13336 } 13337 _input.popFront(); 13338 } 13339 13340 @property auto ref front() 13341 { 13342 assert(!_input.empty, "Attempting to fetch the front of an empty tee"); 13343 static if (!pipeOnPop) 13344 { 13345 if (!_frontAccessed) 13346 { 13347 _frontAccessed = true; 13348 put(_output, _input.front); 13349 } 13350 } 13351 return _input.front; 13352 } 13353 } 13354 13355 return Result(inputRange, outputRange); 13356 } 13357 13358 /// Ditto 13359 auto tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1 inputRange) 13360 if (is(typeof(fun) == void) || isSomeFunction!fun) 13361 { 13362 import std.traits : isDelegate, isFunctionPointer; 13363 /* 13364 Distinguish between function literals and template lambdas 13365 when using either as an $(LREF OutputRange). Since a template 13366 has no type, typeof(template) will always return void. 13367 If it's a template lambda, it's first necessary to instantiate 13368 it with `ElementType!R1`. 13369 */ 13370 static if (is(typeof(fun) == void)) 13371 alias _fun = fun!(ElementType!R1); 13372 else 13373 alias _fun = fun; 13374 13375 static if (isFunctionPointer!_fun || isDelegate!_fun) 13376 { 13377 return tee!pipeOnPop(inputRange, _fun); 13378 } 13379 else 13380 { 13381 return tee!pipeOnPop(inputRange, &_fun); 13382 } 13383 } 13384 13385 /// 13386 @safe unittest 13387 { 13388 import std.algorithm.comparison : equal; 13389 import std.algorithm.iteration : filter, map; 13390 13391 // Sum values while copying 13392 int[] values = [1, 4, 9, 16, 25]; 13393 int sum = 0; 13394 auto newValues = values.tee!(a => sum += a).array; 13395 assert(equal(newValues, values)); 13396 assert(sum == 1 + 4 + 9 + 16 + 25); 13397 13398 // Count values that pass the first filter 13399 int count = 0; 13400 auto newValues4 = values.filter!(a => a < 10) 13401 .tee!(a => count++) 13402 .map!(a => a + 1) 13403 .filter!(a => a < 10); 13404 13405 //Fine, equal also evaluates any lazy ranges passed to it. 13406 //count is not 3 until equal evaluates newValues4 13407 assert(equal(newValues4, [2, 5])); 13408 assert(count == 3); 13409 } 13410 13411 // 13412 @safe unittest 13413 { 13414 import std.algorithm.comparison : equal; 13415 import std.algorithm.iteration : filter, map; 13416 13417 int[] values = [1, 4, 9, 16, 25]; 13418 13419 int count = 0; 13420 auto newValues = values.filter!(a => a < 10) 13421 .tee!(a => count++, No.pipeOnPop) 13422 .map!(a => a + 1) 13423 .filter!(a => a < 10); 13424 13425 auto val = newValues.front; 13426 assert(count == 1); 13427 //front is only evaluated once per element 13428 val = newValues.front; 13429 assert(count == 1); 13430 13431 //popFront() called, fun will be called 13432 //again on the next access to front 13433 newValues.popFront(); 13434 newValues.front; 13435 assert(count == 2); 13436 13437 int[] preMap = new int[](3), postMap = []; 13438 auto mappedValues = values.filter!(a => a < 10) 13439 //Note the two different ways of using tee 13440 .tee(preMap) 13441 .map!(a => a + 1) 13442 .tee!(a => postMap ~= a) 13443 .filter!(a => a < 10); 13444 assert(equal(mappedValues, [2, 5])); 13445 assert(equal(preMap, [1, 4, 9])); 13446 assert(equal(postMap, [2, 5, 10])); 13447 } 13448 13449 // 13450 @safe unittest 13451 { 13452 import std.algorithm.comparison : equal; 13453 import std.algorithm.iteration : filter, map; 13454 13455 char[] txt = "Line one, Line 2".dup; 13456 13457 bool isVowel(dchar c) 13458 { 13459 import std.string : indexOf; 13460 return "AaEeIiOoUu".indexOf(c) != -1; 13461 } 13462 13463 int vowelCount = 0; 13464 int shiftedCount = 0; 13465 auto removeVowels = txt.tee!(c => isVowel(c) ? vowelCount++ : 0) 13466 .filter!(c => !isVowel(c)) 13467 .map!(c => (c == ' ') ? c : c + 1) 13468 .tee!(c => isVowel(c) ? shiftedCount++ : 0); 13469 assert(equal(removeVowels, "Mo o- Mo 3")); 13470 assert(vowelCount == 6); 13471 assert(shiftedCount == 3); 13472 } 13473 13474 @safe unittest 13475 { 13476 // Manually stride to test different pipe behavior. 13477 void testRange(Range)(Range r) 13478 { 13479 const int strideLen = 3; 13480 int i = 0; 13481 ElementType!Range elem1; 13482 ElementType!Range elem2; 13483 while (!r.empty) 13484 { 13485 if (i % strideLen == 0) 13486 { 13487 //Make sure front is only 13488 //evaluated once per item 13489 elem1 = r.front; 13490 elem2 = r.front; 13491 assert(elem1 == elem2); 13492 } 13493 r.popFront(); 13494 i++; 13495 } 13496 } 13497 13498 string txt = "abcdefghijklmnopqrstuvwxyz"; 13499 13500 int popCount = 0; 13501 auto pipeOnPop = txt.tee!(a => popCount++); 13502 testRange(pipeOnPop); 13503 assert(popCount == 26); 13504 13505 int frontCount = 0; 13506 auto pipeOnFront = txt.tee!(a => frontCount++, No.pipeOnPop); 13507 testRange(pipeOnFront); 13508 assert(frontCount == 9); 13509 } 13510 13511 @safe unittest 13512 { 13513 import std.algorithm.comparison : equal; 13514 import std.meta : AliasSeq; 13515 13516 //Test diverting elements to an OutputRange 13517 string txt = "abcdefghijklmnopqrstuvwxyz"; 13518 13519 dchar[] asink1 = []; 13520 auto fsink = (dchar c) { asink1 ~= c; }; 13521 auto result1 = txt.tee(fsink).array; 13522 assert(equal(txt, result1) && (equal(result1, asink1))); 13523 13524 dchar[] _asink1 = []; 13525 auto _result1 = txt.tee!((dchar c) { _asink1 ~= c; })().array; 13526 assert(equal(txt, _result1) && (equal(_result1, _asink1))); 13527 13528 dchar[] asink2 = new dchar[](txt.length); 13529 void fsink2(dchar c) { static int i = 0; asink2[i] = c; i++; } 13530 auto result2 = txt.tee(&fsink2).array; 13531 assert(equal(txt, result2) && equal(result2, asink2)); 13532 13533 dchar[] asink3 = new dchar[](txt.length); 13534 auto result3 = txt.tee(asink3).array; 13535 assert(equal(txt, result3) && equal(result3, asink3)); 13536 13537 static foreach (CharType; AliasSeq!(char, wchar, dchar)) 13538 {{ 13539 auto appSink = appender!(CharType[])(); 13540 auto appResult = txt.tee(appSink).array; 13541 assert(equal(txt, appResult) && equal(appResult, appSink.data)); 13542 }} 13543 13544 static foreach (StringType; AliasSeq!(string, wstring, dstring)) 13545 {{ 13546 auto appSink = appender!StringType(); 13547 auto appResult = txt.tee(appSink).array; 13548 assert(equal(txt, appResult) && equal(appResult, appSink.data)); 13549 }} 13550 } 13551 13552 // https://issues.dlang.org/show_bug.cgi?id=13483 13553 @safe unittest 13554 { 13555 static void func1(T)(T x) {} 13556 void func2(int x) {} 13557 13558 auto r = [1, 2, 3, 4].tee!func1.tee!func2; 13559 } 13560 13561 /** 13562 Extends the length of the input range `r` by padding out the start of the 13563 range with the element `e`. The element `e` must be of a common type with 13564 the element type of the range `r` as defined by $(REF CommonType, std, traits). 13565 If `n` is less than the length of of `r`, then `r` is returned unmodified. 13566 13567 If `r` is a string with Unicode characters in it, `padLeft` follows D's rules 13568 about length for strings, which is not the number of characters, or 13569 graphemes, but instead the number of encoding units. If you want to treat each 13570 grapheme as only one encoding unit long, then call 13571 $(REF byGrapheme, std, uni) before calling this function. 13572 13573 If `r` has a length, then this is $(BIGOH 1). Otherwise, it's $(BIGOH r.length). 13574 13575 Params: 13576 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length, or a forward range 13577 e = element to pad the range with 13578 n = the length to pad to 13579 13580 Returns: 13581 A range containing the elements of the original range with the extra padding 13582 13583 See Also: 13584 $(REF leftJustifier, std, string) 13585 */ 13586 auto padLeft(R, E)(R r, E e, size_t n) 13587 if ( 13588 ((isInputRange!R && hasLength!R) || isForwardRange!R) && 13589 !is(CommonType!(ElementType!R, E) == void) 13590 ) 13591 { 13592 static if (hasLength!R) 13593 auto dataLength = r.length; 13594 else 13595 auto dataLength = r.save.walkLength(n); 13596 13597 return e.repeat(n > dataLength ? n - dataLength : 0).chain(r); 13598 } 13599 13600 /// 13601 @safe pure unittest 13602 { 13603 import std.algorithm.comparison : equal; 13604 13605 assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4])); 13606 assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4])); 13607 13608 assert("abc".padLeft('_', 6).equal("___abc")); 13609 } 13610 13611 @safe pure nothrow unittest 13612 { 13613 import std.algorithm.comparison : equal; 13614 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 13615 import std.meta : AliasSeq; 13616 13617 alias DummyRanges = AliasSeq!( 13618 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input), 13619 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), 13620 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), 13621 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), 13622 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), 13623 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), 13624 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), 13625 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), 13626 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), 13627 DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward) 13628 ); 13629 13630 foreach (Range; DummyRanges) 13631 { 13632 Range r; 13633 assert(r 13634 .padLeft(0, 12) 13635 .equal([0, 0, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]) 13636 ); 13637 } 13638 } 13639 13640 // Test nogc inference 13641 @safe @nogc pure unittest 13642 { 13643 import std.algorithm.comparison : equal; 13644 13645 static immutable r1 = [1, 2, 3, 4]; 13646 static immutable r2 = [0, 0, 1, 2, 3, 4]; 13647 assert(r1.padLeft(0, 6).equal(r2)); 13648 } 13649 13650 /** 13651 Extend the length of the input range `r` by padding out the end of the range 13652 with the element `e`. The element `e` must be of a common type with the 13653 element type of the range `r` as defined by $(REF CommonType, std, traits). 13654 If `n` is less than the length of of `r`, then the contents of `r` are 13655 returned. 13656 13657 The range primitives that the resulting range provides depends whether or not `r` 13658 provides them. Except the functions `back` and `popBack`, which also require 13659 the range to have a length as well as `back` and `popBack` 13660 13661 Params: 13662 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length 13663 e = element to pad the range with 13664 n = the length to pad to 13665 13666 Returns: 13667 A range containing the elements of the original range with the extra padding 13668 13669 See Also: 13670 $(REF rightJustifier, std, string) 13671 */ 13672 auto padRight(R, E)(R r, E e, size_t n) 13673 if ( 13674 isInputRange!R && 13675 !isInfinite!R && 13676 !is(CommonType!(ElementType!R, E) == void)) 13677 { 13678 static struct Result 13679 { 13680 private: 13681 R data; 13682 E element; 13683 static if (hasLength!R) 13684 { 13685 size_t padLength; 13686 } 13687 else 13688 { 13689 size_t minLength; 13690 size_t consumed; 13691 } 13692 13693 public: 13694 bool empty() @property 13695 { 13696 static if (hasLength!R) 13697 { 13698 return data.empty && padLength == 0; 13699 } 13700 else 13701 { 13702 return data.empty && consumed >= minLength; 13703 } 13704 } 13705 13706 auto front() @property 13707 { 13708 assert(!empty, "Attempting to fetch the front of an empty padRight"); 13709 return data.empty ? element : data.front; 13710 } 13711 13712 void popFront() 13713 { 13714 assert(!empty, "Attempting to popFront an empty padRight"); 13715 13716 static if (hasLength!R) 13717 { 13718 if (!data.empty) 13719 { 13720 data.popFront; 13721 } 13722 else 13723 { 13724 --padLength; 13725 } 13726 } 13727 else 13728 { 13729 ++consumed; 13730 if (!data.empty) 13731 { 13732 data.popFront; 13733 } 13734 } 13735 } 13736 13737 static if (hasLength!R) 13738 { 13739 size_t length() @property 13740 { 13741 return data.length + padLength; 13742 } 13743 } 13744 13745 static if (isForwardRange!R) 13746 { 13747 auto save() @property 13748 { 13749 typeof(this) result = this; 13750 data = data.save; 13751 return result; 13752 } 13753 } 13754 13755 static if (isBidirectionalRange!R && hasLength!R) 13756 { 13757 auto back() @property 13758 { 13759 assert(!empty, "Attempting to fetch the back of an empty padRight"); 13760 return padLength > 0 ? element : data.back; 13761 } 13762 13763 void popBack() 13764 { 13765 assert(!empty, "Attempting to popBack an empty padRight"); 13766 if (padLength > 0) 13767 { 13768 --padLength; 13769 } 13770 else 13771 { 13772 data.popBack; 13773 } 13774 } 13775 } 13776 13777 static if (isRandomAccessRange!R && hasLength!R) 13778 { 13779 E opIndex(size_t index) 13780 { 13781 assert(index <= this.length, "Index out of bounds"); 13782 return index >= data.length ? element : data[index]; 13783 } 13784 } 13785 13786 static if (hasSlicing!R && hasLength!R) 13787 { 13788 auto opSlice(size_t a, size_t b) 13789 { 13790 assert( 13791 a <= b, 13792 "Attempting to slice a padRight with a larger first argument than the second." 13793 ); 13794 assert( 13795 b <= length, 13796 "Attempting to slice using an out of bounds index on a padRight" 13797 ); 13798 return Result( 13799 a >= data.length ? data[0 .. 0] : b <= data.length ? data[a .. b] : data[a .. data.length], 13800 element, b - a); 13801 } 13802 13803 alias opDollar = length; 13804 } 13805 13806 this(R r, E e, size_t n) 13807 { 13808 data = r; 13809 element = e; 13810 static if (hasLength!R) 13811 { 13812 padLength = n > data.length ? n - data.length : 0; 13813 } 13814 else 13815 { 13816 minLength = n; 13817 } 13818 } 13819 13820 @disable this(); 13821 } 13822 13823 return Result(r, e, n); 13824 } 13825 13826 /// 13827 @safe pure unittest 13828 { 13829 import std.algorithm.comparison : equal; 13830 13831 assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0])); 13832 assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4])); 13833 13834 assert("abc".padRight('_', 6).equal("abc___")); 13835 } 13836 13837 pure @safe unittest 13838 { 13839 import std.algorithm.comparison : equal; 13840 import std.internal.test.dummyrange : AllDummyRanges, ReferenceInputRange; 13841 import std.meta : AliasSeq; 13842 13843 auto string_input_range = new ReferenceInputRange!dchar(['a', 'b', 'c']); 13844 dchar padding = '_'; 13845 assert(string_input_range.padRight(padding, 6).equal("abc___")); 13846 13847 foreach (RangeType; AllDummyRanges) 13848 { 13849 RangeType r1; 13850 assert(r1 13851 .padRight(0, 12) 13852 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) 13853 ); 13854 13855 // test if Result properly uses random access ranges 13856 static if (isRandomAccessRange!RangeType) 13857 { 13858 RangeType r3; 13859 assert(r3.padRight(0, 12)[0] == 1); 13860 assert(r3.padRight(0, 12)[2] == 3); 13861 assert(r3.padRight(0, 12)[9] == 10); 13862 assert(r3.padRight(0, 12)[10] == 0); 13863 assert(r3.padRight(0, 12)[11] == 0); 13864 } 13865 13866 // test if Result properly uses slicing and opDollar 13867 static if (hasSlicing!RangeType) 13868 { 13869 RangeType r4; 13870 assert(r4 13871 .padRight(0, 12)[0 .. 3] 13872 .equal([1, 2, 3]) 13873 ); 13874 assert(r4 13875 .padRight(0, 12)[0 .. 10] 13876 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]) 13877 ); 13878 assert(r4 13879 .padRight(0, 12)[0 .. 11] 13880 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0]) 13881 ); 13882 assert(r4 13883 .padRight(0, 12)[2 .. $] 13884 .equal([3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) 13885 ); 13886 assert(r4 13887 .padRight(0, 12)[0 .. $] 13888 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) 13889 ); 13890 } 13891 13892 // drop & dropBack test opslice ranges when available, popFront/popBack otherwise 13893 RangeType r5; 13894 foreach (i; 1 .. 13) assert(r5.padRight(0, 12).drop(i).walkLength == 12 - i); 13895 } 13896 } 13897 13898 // Test nogc inference 13899 @safe @nogc pure unittest 13900 { 13901 import std.algorithm.comparison : equal; 13902 13903 static immutable r1 = [1, 2, 3, 4]; 13904 static immutable r2 = [1, 2, 3, 4, 0, 0]; 13905 assert(r1.padRight(0, 6).equal(r2)); 13906 } 13907 13908 // Test back, popBack, and save 13909 @safe pure unittest 13910 { 13911 import std.algorithm.comparison : equal; 13912 13913 auto r1 = [1, 2, 3, 4].padRight(0, 6); 13914 assert(r1.back == 0); 13915 13916 r1.popBack; 13917 auto r2 = r1.save; 13918 assert(r1.equal([1, 2, 3, 4, 0])); 13919 assert(r2.equal([1, 2, 3, 4, 0])); 13920 13921 r1.popBackN(2); 13922 assert(r1.back == 3); 13923 assert(r1.length == 3); 13924 assert(r2.length == 5); 13925 assert(r2.equal([1, 2, 3, 4, 0])); 13926 13927 r2.popFront; 13928 assert(r2.length == 4); 13929 assert(r2[0] == 2); 13930 assert(r2[1] == 3); 13931 assert(r2[2] == 4); 13932 assert(r2[3] == 0); 13933 assert(r2.equal([2, 3, 4, 0])); 13934 13935 r2.popBack; 13936 assert(r2.equal([2, 3, 4])); 13937 13938 auto r3 = [1, 2, 3, 4].padRight(0, 6); 13939 size_t len = 0; 13940 while (!r3.empty) 13941 { 13942 ++len; 13943 r3.popBack; 13944 } 13945 assert(len == 6); 13946 } 13947 13948 // https://issues.dlang.org/show_bug.cgi?id=19042 13949 @safe pure unittest 13950 { 13951 import std.algorithm.comparison : equal; 13952 13953 assert([2, 5, 13].padRight(42, 10).chunks(5) 13954 .equal!equal([[2, 5, 13, 42, 42], [42, 42, 42, 42, 42]])); 13955 13956 assert([1, 2, 3, 4].padRight(0, 10)[7 .. 9].equal([0, 0])); 13957 } 13958 13959 /** 13960 This simplifies a commonly used idiom in phobos for accepting any kind of string 13961 parameter. The type `R` can for example be a simple string, chained string using 13962 $(REF chain, std,range), $(REF chainPath, std,path) or any other input range of 13963 characters. 13964 13965 Only finite length character ranges are allowed with this constraint. 13966 13967 This template is equivalent to: 13968 --- 13969 isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) 13970 --- 13971 13972 See_Also: 13973 $(REF isInputRange, std,range,primitives), 13974 $(REF isInfinite, std,range,primitives), 13975 $(LREF isSomeChar), 13976 $(REF ElementEncodingType, std,range,primitives) 13977 */ 13978 template isSomeFiniteCharInputRange(R) 13979 { 13980 import std.traits : isSomeChar; 13981 13982 enum isSomeFiniteCharInputRange = isInputRange!R && !isInfinite!R 13983 && isSomeChar!(ElementEncodingType!R); 13984 } 13985 13986 /// 13987 @safe unittest 13988 { 13989 import std.path : chainPath; 13990 import std.range : chain; 13991 13992 void someLibraryMethod(R)(R argument) 13993 if (isSomeFiniteCharInputRange!R) 13994 { 13995 // implementation detail, would iterate over each character of argument 13996 } 13997 13998 someLibraryMethod("simple strings work"); 13999 someLibraryMethod(chain("chained", " ", "strings", " ", "work")); 14000 someLibraryMethod(chainPath("chained", "paths", "work")); 14001 // you can also use custom structs implementing a char range 14002 } 14003