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() 1029 { 1030 if (frontIndex == 0) 1031 { 1032 // special handling: we might be in Range.init state! 1033 // For instance, `format!"%s"` uses Range.init to ensure 1034 // that formatting is possible. 1035 // In that case, we must still behave in an internally consistent way. 1036 return source[0].empty; 1037 } 1038 return frontIndex >= backIndex; 1039 } 1040 } 1041 1042 static if (allSatisfy!(isForwardRange, R)) 1043 { 1044 @property auto save() 1045 { 1046 auto saveI(size_t i)() => source[i].save; 1047 1048 // TODO: this has the constructor needlessly refind 1049 // frontIndex and backIndex. It'd be better to just copy 1050 // those from `.this`. 1051 auto saveResult = 1052 Result(staticMap!(saveI, aliasSeqOf!(R.length.iota))); 1053 1054 return saveResult; 1055 } 1056 } 1057 1058 void popFront() 1059 { 1060 sw1: switch (frontIndex) 1061 { 1062 static foreach (i; 0 .. R.length) 1063 { 1064 case i: 1065 source[i].popFront(); 1066 break sw1; 1067 } 1068 1069 case R.length: 1070 assert(0, "Attempt to `popFront` of empty `chain` range"); 1071 1072 default: 1073 assert(0, "Internal library error. Please report it."); 1074 } 1075 1076 sw2: switch (frontIndex) 1077 { 1078 static foreach (i; 0 .. R.length) 1079 { 1080 case i: 1081 if (source[i].empty) 1082 { 1083 frontIndex++; 1084 goto case; 1085 } 1086 else break sw2; 1087 } 1088 1089 // Only possible to reach from goto of previous case. 1090 case R.length: 1091 break; 1092 1093 default: 1094 assert(0, "Internal library error. Please report it."); 1095 } 1096 } 1097 1098 @property auto ref front() 1099 { 1100 switch (frontIndex) 1101 { 1102 static foreach (i; 0 .. R.length) 1103 { 1104 case i: 1105 return fixRef(source[i].front); 1106 } 1107 1108 case R.length: 1109 assert(0, "Attempt to get `front` of empty `chain` range"); 1110 1111 default: 1112 assert(0, "Internal library error. Please report it."); 1113 } 1114 } 1115 1116 static if (assignableElements) 1117 { 1118 // @@@BUG@@@ 1119 //@property void front(T)(T v) if (is(T : RvalueElementType)) 1120 1121 @property void front(RvalueElementType v) 1122 { 1123 import std.algorithm.mutation : move; 1124 1125 sw: switch (frontIndex) 1126 { 1127 static foreach (i; 0 .. R.length) 1128 { 1129 case i: 1130 source[i].front = move(v); 1131 break sw; 1132 } 1133 1134 case R.length: 1135 assert(0, "Attempt to set `front` of empty `chain` range"); 1136 1137 default: 1138 assert(0, "Internal library error. Please report it."); 1139 } 1140 } 1141 } 1142 1143 static if (mobileElements) 1144 { 1145 RvalueElementType moveFront() 1146 { 1147 switch (frontIndex) 1148 { 1149 static foreach (i; 0 .. R.length) 1150 { 1151 case i: 1152 return source[i].moveFront(); 1153 } 1154 1155 case R.length: 1156 assert(0, "Attempt to `moveFront` of empty `chain` range"); 1157 1158 default: 1159 assert(0, "Internal library error. Please report it."); 1160 } 1161 } 1162 } 1163 1164 static if (bidirectional) 1165 { 1166 @property auto ref back() 1167 { 1168 switch (backIndex) 1169 { 1170 static foreach_reverse (i; 1 .. R.length + 1) 1171 { 1172 case i: 1173 return fixRef(source[i-1].back); 1174 } 1175 1176 case 0: 1177 assert(0, "Attempt to get `back` of empty `chain` range"); 1178 1179 default: 1180 assert(0, "Internal library error. Please report it."); 1181 } 1182 } 1183 1184 void popBack() 1185 { 1186 sw1: switch (backIndex) 1187 { 1188 static foreach_reverse (i; 1 .. R.length + 1) 1189 { 1190 case i: 1191 source[i-1].popBack(); 1192 break sw1; 1193 } 1194 1195 case 0: 1196 assert(0, "Attempt to `popFront` of empty `chain` range"); 1197 1198 default: 1199 assert(0, "Internal library error. Please report it."); 1200 } 1201 1202 sw2: switch (backIndex) 1203 { 1204 static foreach_reverse (i; 1 .. R.length + 1) 1205 { 1206 case i: 1207 if (source[i-1].empty) 1208 { 1209 backIndex--; 1210 goto case; 1211 } 1212 else break sw2; 1213 } 1214 1215 // Only possible to reach from goto of previous case. 1216 case 0: 1217 break; 1218 1219 default: 1220 assert(0, "Internal library error. Please report it."); 1221 } 1222 } 1223 1224 static if (mobileElements) 1225 { 1226 RvalueElementType moveBack() 1227 { 1228 switch (backIndex) 1229 { 1230 static foreach_reverse (i; 1 .. R.length + 1) 1231 { 1232 case i: 1233 return source[i-1].moveBack(); 1234 } 1235 1236 case 0: 1237 assert(0, "Attempt to `moveBack` of empty `chain` range"); 1238 1239 default: 1240 assert(0, "Internal library error. Please report it."); 1241 } 1242 } 1243 } 1244 1245 static if (allSameType && allSatisfy!(hasAssignableElements, R)) 1246 { 1247 @property void back(RvalueElementType v) 1248 { 1249 import std.algorithm.mutation : move; 1250 1251 sw: switch (backIndex) 1252 { 1253 static foreach_reverse (i; 1 .. R.length + 1) 1254 { 1255 case i: 1256 source[i-1].back = move(v); 1257 break sw; 1258 } 1259 1260 case 0: 1261 assert(0, "Attempt to set `back` of empty `chain` range"); 1262 1263 default: 1264 assert(0, "Internal library error. Please report it."); 1265 } 1266 } 1267 } 1268 } 1269 1270 static if (allSatisfy!(hasLength, R)) 1271 { 1272 @property size_t length() 1273 { 1274 size_t result = 0; 1275 sw: switch (frontIndex) 1276 { 1277 static foreach (i; 0 .. R.length) 1278 { 1279 case i: 1280 result += source[i].length; 1281 if (backIndex == i+1) break sw; 1282 else goto case; 1283 } 1284 1285 case R.length: 1286 break; 1287 1288 default: 1289 assert(0, "Internal library error. Please report it."); 1290 } 1291 1292 return result; 1293 } 1294 1295 alias opDollar = length; 1296 } 1297 1298 static if (allSatisfy!(isRandomAccessRange, R)) 1299 { 1300 auto ref opIndex(size_t index) 1301 { 1302 switch (frontIndex) 1303 { 1304 static foreach (i; 0 .. R.length) 1305 { 1306 case i: 1307 static if (!isInfinite!(R[i])) 1308 { 1309 immutable length = source[i].length; 1310 if (index >= length) 1311 { 1312 index -= length; 1313 goto case; 1314 } 1315 } 1316 1317 return fixRef(source[i][index]); 1318 } 1319 1320 case R.length: 1321 assert(0, "Attempt to access out-of-bounds index of `chain` range"); 1322 1323 default: 1324 assert(0, "Internal library error. Please report it."); 1325 } 1326 } 1327 1328 static if (mobileElements) 1329 { 1330 RvalueElementType moveAt(size_t index) 1331 { 1332 switch (frontIndex) 1333 { 1334 static foreach (i; 0 .. R.length) 1335 { 1336 case i: 1337 static if (!isInfinite!(R[i])) 1338 { 1339 immutable length = source[i].length; 1340 if (index >= length) 1341 { 1342 index -= length; 1343 goto case; 1344 } 1345 } 1346 1347 return source[i].moveAt(index); 1348 } 1349 1350 case R.length: 1351 assert(0, "Attempt to move out-of-bounds index of `chain` range"); 1352 1353 default: 1354 assert(0, "Internal library error. Please report it."); 1355 } 1356 } 1357 } 1358 1359 static if (allSameType && allSatisfy!(hasAssignableElements, R)) 1360 void opIndexAssign(ElementType v, size_t index) 1361 { 1362 import std.algorithm.mutation : move; 1363 1364 sw: switch (frontIndex) 1365 { 1366 static foreach (i; 0 .. R.length) 1367 { 1368 case i: 1369 static if (!isInfinite!(R[i])) 1370 { 1371 immutable length = source[i].length; 1372 if (index >= length) 1373 { 1374 index -= length; 1375 goto case; 1376 } 1377 } 1378 1379 source[i][index] = move(v); 1380 break sw; 1381 } 1382 1383 case R.length: 1384 assert(0, "Attempt to write out-of-bounds index of `chain` range"); 1385 1386 default: 1387 assert(0, "Internal library error. Please report it."); 1388 } 1389 } 1390 } 1391 1392 static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R)) 1393 auto opSlice(size_t begin, size_t end) return scope 1394 { 1395 // force staticMap type conversion to Rebindable 1396 static struct ResultRanges 1397 { 1398 staticMap!(Rebindable, typeof(source)) fields; 1399 } 1400 auto sourceI(size_t i)() => rebindable(this.source[i]); 1401 auto resultRanges = ResultRanges(staticMap!(sourceI, aliasSeqOf!(R.length.iota))).fields; 1402 size_t resultFrontIndex = this.frontIndex; 1403 static if (bidirectional) 1404 size_t resultBackIndex = this.backIndex; 1405 1406 sw: switch (frontIndex) 1407 { 1408 static foreach (i; 0 .. R.length) 1409 { 1410 case i: 1411 immutable len = resultRanges[i].length; 1412 if (len <= begin) 1413 { 1414 resultRanges[i] = resultRanges[i] 1415 [len .. len]; 1416 begin -= len; 1417 resultFrontIndex++; 1418 goto case; 1419 } 1420 else 1421 { 1422 resultRanges[i] = resultRanges[i] 1423 [begin .. len]; 1424 break sw; 1425 } 1426 } 1427 1428 case R.length: 1429 assert(begin == 0, 1430 "Attempt to access out-of-bounds slice of `chain` range"); 1431 break; 1432 1433 default: 1434 assert(0, "Internal library error. Please report it."); 1435 } 1436 1437 // Overflow intentional if end index too big. 1438 // This will trigger the bounds check failure below. 1439 auto cut = length - end; 1440 1441 sw2: switch (backIndex) 1442 { 1443 static foreach_reverse (i; 1 .. R.length + 1) 1444 { 1445 case i: 1446 immutable len = resultRanges[i-1].length; 1447 if (len <= cut) 1448 { 1449 resultRanges[i-1] = resultRanges[i-1] 1450 [0 .. 0]; 1451 cut -= len; 1452 resultBackIndex--; 1453 goto case; 1454 } 1455 else 1456 { 1457 resultRanges[i-1] = resultRanges[i-1] 1458 [0 .. len - cut]; 1459 break sw2; 1460 } 1461 } 1462 1463 case 0: 1464 assert(cut == 0, end > length? 1465 "Attempt to access out-of-bounds slice of `chain` range": 1466 "Attempt to access negative length slice of `chain` range"); 1467 break sw2; 1468 1469 default: 1470 assert(0, "Internal library error. Please report it."); 1471 } 1472 1473 static if (bidirectional) 1474 return Result(resultRanges, resultFrontIndex, resultBackIndex); 1475 else 1476 return Result(resultRanges, resultFrontIndex); 1477 } 1478 } 1479 return Result(rs); 1480 } 1481 } 1482 1483 /// 1484 pure @safe nothrow unittest 1485 { 1486 import std.algorithm.comparison : equal; 1487 1488 int[] arr1 = [ 1, 2, 3, 4 ]; 1489 int[] arr2 = [ 5, 6 ]; 1490 int[] arr3 = [ 7 ]; 1491 auto s = chain(arr1, arr2, arr3); 1492 assert(s.length == 7); 1493 assert(s[5] == 6); 1494 assert(equal(s, [1, 2, 3, 4, 5, 6, 7][])); 1495 } 1496 1497 /** 1498 * Range primitives are carried over to the returned range if 1499 * all of the ranges provide them 1500 */ 1501 pure @safe nothrow unittest 1502 { 1503 import std.algorithm.comparison : equal; 1504 import std.algorithm.sorting : sort; 1505 1506 int[] arr1 = [5, 2, 8]; 1507 int[] arr2 = [3, 7, 9]; 1508 int[] arr3 = [1, 4, 6]; 1509 1510 // in-place sorting across all of the arrays 1511 auto s = arr1.chain(arr2, arr3).sort; 1512 1513 assert(s.equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); 1514 assert(arr1.equal([1, 2, 3])); 1515 assert(arr2.equal([4, 5, 6])); 1516 assert(arr3.equal([7, 8, 9])); 1517 } 1518 1519 /** 1520 Due to safe type promotion in D, chaining together different 1521 character ranges results in a `uint` range. 1522 1523 Use $(REF_ALTTEXT byChar, byChar,std,utf), $(REF_ALTTEXT byWchar, byWchar,std,utf), 1524 and $(REF_ALTTEXT byDchar, byDchar,std,utf) on the ranges 1525 to get the type you need. 1526 */ 1527 pure @safe nothrow unittest 1528 { 1529 import std.utf : byChar, byCodeUnit; 1530 1531 auto s1 = "string one"; 1532 auto s2 = "string two"; 1533 // s1 and s2 front is dchar because of auto-decoding 1534 static assert(is(typeof(s1.front) == dchar) && is(typeof(s2.front) == dchar)); 1535 1536 auto r1 = s1.chain(s2); 1537 // chains of ranges of the same character type give that same type 1538 static assert(is(typeof(r1.front) == dchar)); 1539 1540 auto s3 = "string three".byCodeUnit; 1541 static assert(is(typeof(s3.front) == immutable char)); 1542 auto r2 = s1.chain(s3); 1543 // chaining ranges of mixed character types gives `dchar` 1544 static assert(is(typeof(r2.front) == dchar)); 1545 1546 // use byChar on character ranges to correctly convert them to UTF-8 1547 auto r3 = s1.byChar.chain(s3); 1548 static assert(is(typeof(r3.front) == immutable char)); 1549 } 1550 1551 pure @safe nothrow unittest 1552 { 1553 import std.algorithm.comparison : equal; 1554 import std.internal.test.dummyrange : AllDummyRanges, dummyLength, 1555 propagatesRangeType; 1556 1557 { 1558 int[] arr1 = [ 1, 2, 3, 4 ]; 1559 int[] arr2 = [ 5, 6 ]; 1560 int[] arr3 = [ 7 ]; 1561 int[] witness = [ 1, 2, 3, 4, 5, 6, 7 ]; 1562 auto s1 = chain(arr1); 1563 static assert(isRandomAccessRange!(typeof(s1))); 1564 auto s2 = chain(arr1, arr2); 1565 static assert(isBidirectionalRange!(typeof(s2))); 1566 static assert(isRandomAccessRange!(typeof(s2))); 1567 s2.front = 1; 1568 auto s = chain(arr1, arr2, arr3); 1569 assert(s[5] == 6); 1570 assert(equal(s, witness)); 1571 assert(s[4 .. 6].equal(arr2)); 1572 assert(s[2 .. 5].equal([3, 4, 5])); 1573 assert(s[0 .. 0].empty); 1574 assert(s[7 .. $].empty); 1575 assert(s[5] == 6); 1576 } 1577 { 1578 int[] arr1 = [ 1, 2, 3, 4 ]; 1579 int[] witness = [ 1, 2, 3, 4 ]; 1580 assert(equal(chain(arr1), witness)); 1581 } 1582 { 1583 uint[] foo = [1,2,3,4,5]; 1584 uint[] bar = [1,2,3,4,5]; 1585 auto c = chain(foo, bar); 1586 c[3] = 42; 1587 assert(c[3] == 42); 1588 assert(c.moveFront() == 1); 1589 assert(c.moveBack() == 5); 1590 assert(c.moveAt(4) == 5); 1591 assert(c.moveAt(5) == 1); 1592 } 1593 1594 1595 // Make sure https://issues.dlang.org/show_bug.cgi?id=3311 is fixed. 1596 // elements are mutable. 1597 assert(equal(chain(iota(0, 3), iota(0, 3)), [0, 1, 2, 0, 1, 2])); 1598 1599 // Test the case where infinite ranges are present. 1600 auto inf = chain([0,1,2][], cycle([4,5,6][]), [7,8,9][]); // infinite range 1601 assert(inf[0] == 0); 1602 assert(inf[3] == 4); 1603 assert(inf[6] == 4); 1604 assert(inf[7] == 5); 1605 static assert(isInfinite!(typeof(inf))); 1606 1607 immutable int[] immi = [ 1, 2, 3 ]; 1608 immutable float[] immf = [ 1, 2, 3 ]; 1609 static assert(is(typeof(chain(immi, immf)))); 1610 1611 // Check that chain at least instantiates and compiles with every possible 1612 // pair of DummyRange types, in either order. 1613 1614 foreach (DummyType1; AllDummyRanges) 1615 (){ // workaround slow optimizations for large functions 1616 // https://issues.dlang.org/show_bug.cgi?id=2396 1617 DummyType1 dummy1; 1618 foreach (DummyType2; AllDummyRanges) 1619 { 1620 DummyType2 dummy2; 1621 auto myChain = chain(dummy1, dummy2); 1622 1623 static assert( 1624 propagatesRangeType!(typeof(myChain), DummyType1, DummyType2) 1625 ); 1626 1627 assert(myChain.front == 1); 1628 foreach (i; 0 .. dummyLength) 1629 { 1630 myChain.popFront(); 1631 } 1632 assert(myChain.front == 1); 1633 1634 static if (isBidirectionalRange!DummyType1 && 1635 isBidirectionalRange!DummyType2) { 1636 assert(myChain.back == 10); 1637 } 1638 1639 static if (isRandomAccessRange!DummyType1 && 1640 isRandomAccessRange!DummyType2) { 1641 assert(myChain[0] == 1); 1642 } 1643 1644 static if (hasLvalueElements!DummyType1 && hasLvalueElements!DummyType2) 1645 { 1646 static assert(hasLvalueElements!(typeof(myChain))); 1647 } 1648 else 1649 { 1650 static assert(!hasLvalueElements!(typeof(myChain))); 1651 } 1652 } 1653 }(); 1654 } 1655 1656 pure @safe nothrow @nogc unittest 1657 { 1658 class Foo{} 1659 immutable(Foo)[] a; 1660 immutable(Foo)[] b; 1661 assert(chain(a, b).empty); 1662 } 1663 1664 // https://issues.dlang.org/show_bug.cgi?id=18657 1665 pure @safe unittest 1666 { 1667 import std.algorithm.comparison : equal; 1668 string s = "foo"; 1669 auto r = refRange(&s).chain("bar"); 1670 assert(equal(r.save, "foobar")); 1671 assert(equal(r, "foobar")); 1672 } 1673 1674 // https://issues.dlang.org/show_bug.cgi?id=23844 1675 pure @safe unittest 1676 { 1677 struct S 1678 { 1679 immutable int value; 1680 } 1681 1682 auto range = chain(only(S(5)), only(S(6))); 1683 assert(range.array == [S(5), S(6)]); 1684 } 1685 1686 /// https://issues.dlang.org/show_bug.cgi?id=24064 1687 pure @safe nothrow unittest 1688 { 1689 import std.algorithm.comparison : equal; 1690 import std.typecons : Nullable; 1691 1692 immutable Nullable!string foo = "b"; 1693 string[] bar = ["a"]; 1694 assert(chain(bar, foo).equal(["a", "b"])); 1695 } 1696 1697 pure @safe nothrow @nogc unittest 1698 { 1699 // support non-copyable items 1700 1701 static struct S { 1702 int v; 1703 @disable this(this); 1704 } 1705 1706 S[2] s0, s1; 1707 foreach (ref el; chain(s0[], s1[])) 1708 { 1709 int n = el.v; 1710 } 1711 1712 S[] s2, s3; 1713 foreach (ref el; chain(s2, s3)) 1714 { 1715 int n = el.v; 1716 } 1717 } 1718 1719 /// https://issues.dlang.org/show_bug.cgi?id=24243 1720 pure @safe nothrow unittest 1721 { 1722 import std.algorithm.iteration : filter; 1723 1724 auto range = chain([2], [3].filter!"a"); 1725 1726 // This might happen in format!"%s"(range), for instance. 1727 assert(typeof(range).init.empty); 1728 } 1729 1730 /** 1731 Choose one of two ranges at runtime depending on a Boolean condition. 1732 1733 The ranges may be different, but they must have compatible element types (i.e. 1734 `CommonType` must exist for the two element types). The result is a range 1735 that offers the weakest capabilities of the two (e.g. `ForwardRange` if $(D 1736 R1) is a random-access range and `R2` is a forward range). 1737 1738 Params: 1739 condition = which range to choose: `r1` if `true`, `r2` otherwise 1740 r1 = the "true" range 1741 r2 = the "false" range 1742 1743 Returns: 1744 A range type dependent on `R1` and `R2`. 1745 */ 1746 auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2) 1747 if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && 1748 !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void)) 1749 { 1750 size_t choice = condition? 0: 1; 1751 return ChooseResult!(R1, R2)(choice, r1, r2); 1752 } 1753 1754 /// 1755 @safe nothrow pure @nogc unittest 1756 { 1757 import std.algorithm.comparison : equal; 1758 import std.algorithm.iteration : filter, map; 1759 1760 auto data1 = only(1, 2, 3, 4).filter!(a => a != 3); 1761 auto data2 = only(5, 6, 7, 8).map!(a => a + 1); 1762 1763 // choose() is primarily useful when you need to select one of two ranges 1764 // with different types at runtime. 1765 static assert(!is(typeof(data1) == typeof(data2))); 1766 1767 auto chooseRange(bool pickFirst) 1768 { 1769 // The returned range is a common wrapper type that can be used for 1770 // returning or storing either range without running into a type error. 1771 return choose(pickFirst, data1, data2); 1772 1773 // Simply returning the chosen range without using choose() does not 1774 // work, because map() and filter() return different types. 1775 //return pickFirst ? data1 : data2; // does not compile 1776 } 1777 1778 auto result = chooseRange(true); 1779 assert(result.equal(only(1, 2, 4))); 1780 1781 result = chooseRange(false); 1782 assert(result.equal(only(6, 7, 8, 9))); 1783 } 1784 1785 1786 private struct ChooseResult(Ranges...) 1787 { 1788 import std.meta : aliasSeqOf, ApplyLeft; 1789 import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor, 1790 lvalueOf; 1791 1792 private union 1793 { 1794 Ranges rs; 1795 } 1796 private size_t chosenI; 1797 1798 private static auto ref actOnChosen(alias foo, ExtraArgs ...) 1799 (ref ChooseResult r, auto ref ExtraArgs extraArgs) 1800 { 1801 ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } 1802 1803 switch (r.chosenI) 1804 { 1805 static foreach (candI; 0 .. rs.length) 1806 { 1807 case candI: return foo(getI!candI(r), extraArgs); 1808 } 1809 1810 default: assert(false); 1811 } 1812 } 1813 1814 // @trusted because of assignment of r which overlap each other 1815 this(size_t chosen, return scope Ranges rs) @trusted 1816 { 1817 import core.lifetime : emplace; 1818 1819 // This should be the only place chosenI is ever assigned 1820 // independently 1821 this.chosenI = chosen; 1822 1823 // Otherwise the compiler will complain about skipping these fields 1824 static foreach (i; 0 .. rs.length) 1825 { 1826 this.rs[i] = Ranges[i].init; 1827 } 1828 1829 // The relevant field needs to be initialized last so it will overwrite 1830 // the other initializations and not the other way around. 1831 sw: switch (chosenI) 1832 { 1833 static foreach (i; 0 .. rs.length) 1834 { 1835 case i: 1836 emplace(&this.rs[i], rs[i]); 1837 break sw; 1838 } 1839 1840 default: assert(false); 1841 } 1842 } 1843 1844 // Some legacy code may still call this with typeof(choose(/*...*/))(/*...*/) 1845 // without this overload the regular constructor would invert the meaning of 1846 // the boolean 1847 static if (rs.length == 2) 1848 pragma(inline, true) 1849 deprecated("Call with size_t (0 = first), or use the choose function") 1850 this(bool firstChosen, Ranges rs) 1851 { 1852 import core.lifetime : move; 1853 this(cast(size_t)(firstChosen? 0: 1), rs[0].move, rs[1].move); 1854 } 1855 1856 void opAssign(ChooseResult r) 1857 { 1858 ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; } 1859 1860 static if (anySatisfy!(hasElaborateDestructor, Ranges)) 1861 if (chosenI != r.chosenI) 1862 { 1863 // destroy the current item 1864 actOnChosen!((ref r) => destroy(r))(this); 1865 } 1866 chosenI = r.chosenI; 1867 1868 sw: switch (chosenI) 1869 { 1870 static foreach (candI; 0 .. rs.length) 1871 { 1872 case candI: getI!candI(this) = getI!candI(r); 1873 break sw; 1874 } 1875 1876 default: assert(false); 1877 } 1878 } 1879 1880 // Carefully defined postblit to postblit the appropriate range 1881 static if (anySatisfy!(hasElaborateCopyConstructor, Ranges)) 1882 this(this) 1883 { 1884 actOnChosen!((ref r) { 1885 static if (hasElaborateCopyConstructor!(typeof(r))) r.__postblit(); 1886 })(this); 1887 } 1888 1889 static if (anySatisfy!(hasElaborateDestructor, Ranges)) 1890 ~this() 1891 { 1892 actOnChosen!((ref r) => destroy(r))(this); 1893 } 1894 1895 // Propagate infiniteness. 1896 static if (allSatisfy!(isInfinite, Ranges)) enum bool empty = false; 1897 else @property bool empty() 1898 { 1899 return actOnChosen!(r => r.empty)(this); 1900 } 1901 1902 @property auto ref front() 1903 { 1904 static auto ref getFront(R)(ref R r) { return r.front; } 1905 return actOnChosen!getFront(this); 1906 } 1907 1908 void popFront() 1909 { 1910 return actOnChosen!((ref r) { r.popFront; })(this); 1911 } 1912 1913 static if (allSatisfy!(isForwardRange, Ranges)) 1914 @property auto save() // return scope inferred 1915 { 1916 auto saveOrInit(size_t i)() 1917 { 1918 ref getI() @trusted { return rs[i]; } 1919 if (i == chosenI) return getI().save; 1920 else return Ranges[i].init; 1921 } 1922 1923 return typeof(this)(chosenI, staticMap!(saveOrInit, 1924 aliasSeqOf!(rs.length.iota))); 1925 } 1926 1927 template front(T) 1928 { 1929 private enum overloadValidFor(alias r) = is(typeof(r.front = T.init)); 1930 1931 static if (allSatisfy!(overloadValidFor, rs)) 1932 void front(T v) 1933 { 1934 actOnChosen!((ref r, T v) { r.front = v; })(this, v); 1935 } 1936 } 1937 1938 static if (allSatisfy!(hasMobileElements, Ranges)) 1939 auto moveFront() 1940 { 1941 return actOnChosen!((ref r) => r.moveFront)(this); 1942 } 1943 1944 static if (allSatisfy!(isBidirectionalRange, Ranges)) 1945 { 1946 @property auto ref back() 1947 { 1948 static auto ref getBack(R)(ref R r) { return r.back; } 1949 return actOnChosen!getBack(this); 1950 } 1951 1952 void popBack() 1953 { 1954 actOnChosen!((ref r) { r.popBack; })(this); 1955 } 1956 1957 static if (allSatisfy!(hasMobileElements, Ranges)) 1958 auto moveBack() 1959 { 1960 return actOnChosen!((ref r) => r.moveBack)(this); 1961 } 1962 1963 template back(T) 1964 { 1965 private enum overloadValidFor(alias r) = is(typeof(r.back = T.init)); 1966 1967 static if (allSatisfy!(overloadValidFor, rs)) 1968 void back(T v) 1969 { 1970 actOnChosen!((ref r, T v) { r.back = v; })(this, v); 1971 } 1972 } 1973 } 1974 1975 static if (allSatisfy!(hasLength, Ranges)) 1976 { 1977 @property size_t length() 1978 { 1979 return actOnChosen!(r => r.length)(this); 1980 } 1981 alias opDollar = length; 1982 } 1983 1984 static if (allSatisfy!(isRandomAccessRange, Ranges)) 1985 { 1986 auto ref opIndex(size_t index) 1987 { 1988 static auto ref get(R)(ref R r, size_t index) { return r[index]; } 1989 return actOnChosen!get(this, index); 1990 } 1991 1992 static if (allSatisfy!(hasMobileElements, Ranges)) 1993 auto moveAt(size_t index) 1994 { 1995 return actOnChosen!((ref r, size_t index) => r.moveAt(index)) 1996 (this, index); 1997 } 1998 1999 private enum indexAssignable(T, R) = is(typeof(lvalueOf!R[1] = T.init)); 2000 2001 template opIndexAssign(T) 2002 if (allSatisfy!(ApplyLeft!(indexAssignable, T), Ranges)) 2003 { 2004 void opIndexAssign(T v, size_t index) 2005 { 2006 return actOnChosen!((ref r, size_t index, T v) { r[index] = v; }) 2007 (this, index, v); 2008 } 2009 } 2010 } 2011 2012 static if (allSatisfy!(hasSlicing, Ranges)) 2013 auto opSlice(size_t begin, size_t end) 2014 { 2015 alias Slice(R) = typeof(R.init[0 .. 1]); 2016 alias Slices = staticMap!(Slice, Ranges); 2017 2018 auto sliceOrInit(size_t i)() 2019 { 2020 ref getI() @trusted { return rs[i]; } 2021 return i == chosenI? getI()[begin .. end]: Slices[i].init; 2022 } 2023 2024 return chooseAmong(chosenI, staticMap!(sliceOrInit, 2025 aliasSeqOf!(rs.length.iota))); 2026 } 2027 } 2028 2029 // https://issues.dlang.org/show_bug.cgi?id=18657 2030 pure @safe unittest 2031 { 2032 import std.algorithm.comparison : equal; 2033 string s = "foo"; 2034 auto r = choose(true, refRange(&s), "bar"); 2035 assert(equal(r.save, "foo")); 2036 assert(equal(r, "foo")); 2037 } 2038 2039 @safe unittest 2040 { 2041 static void* p; 2042 static struct R 2043 { 2044 void* q; 2045 int front; 2046 bool empty; 2047 void popFront() {} 2048 // `p = q;` is only there to prevent inference of `scope return`. 2049 @property @safe R save() { p = q; return this; } 2050 2051 } 2052 R r; 2053 choose(true, r, r).save; 2054 } 2055 2056 // Make sure ChooseResult.save doesn't trust @system user code. 2057 @system unittest // copy is @system 2058 { 2059 static struct R 2060 { 2061 int front; 2062 bool empty; 2063 void popFront() {} 2064 this(this) @system {} 2065 @property R save() { return R(front, empty); } 2066 } 2067 choose(true, R(), R()).save; 2068 choose(true, [0], R()).save; 2069 choose(true, R(), [0]).save; 2070 } 2071 2072 @safe unittest // copy is @system 2073 { 2074 static struct R 2075 { 2076 int front; 2077 bool empty; 2078 void popFront() {} 2079 this(this) @system {} 2080 @property R save() { return R(front, empty); } 2081 } 2082 static assert(!__traits(compiles, choose(true, R(), R()).save)); 2083 static assert(!__traits(compiles, choose(true, [0], R()).save)); 2084 static assert(!__traits(compiles, choose(true, R(), [0]).save)); 2085 } 2086 2087 @system unittest // .save is @system 2088 { 2089 static struct R 2090 { 2091 int front; 2092 bool empty; 2093 void popFront() {} 2094 @property R save() @system { return this; } 2095 } 2096 choose(true, R(), R()).save; 2097 choose(true, [0], R()).save; 2098 choose(true, R(), [0]).save; 2099 } 2100 2101 @safe unittest // .save is @system 2102 { 2103 static struct R 2104 { 2105 int front; 2106 bool empty; 2107 void popFront() {} 2108 @property R save() @system { return this; } 2109 } 2110 static assert(!__traits(compiles, choose(true, R(), R()).save)); 2111 static assert(!__traits(compiles, choose(true, [0], R()).save)); 2112 static assert(!__traits(compiles, choose(true, R(), [0]).save)); 2113 } 2114 2115 //https://issues.dlang.org/show_bug.cgi?id=19738 2116 @safe nothrow pure @nogc unittest 2117 { 2118 static struct EvilRange 2119 { 2120 enum empty = true; 2121 int front; 2122 void popFront() @safe {} 2123 auto opAssign(const ref EvilRange other) 2124 { 2125 *(cast(uint*) 0xcafebabe) = 0xdeadbeef; 2126 return this; 2127 } 2128 } 2129 2130 static assert(!__traits(compiles, () @safe 2131 { 2132 auto c1 = choose(true, EvilRange(), EvilRange()); 2133 auto c2 = c1; 2134 c1 = c2; 2135 })); 2136 } 2137 2138 2139 // https://issues.dlang.org/show_bug.cgi?id=20495 2140 @safe unittest 2141 { 2142 static struct KillableRange 2143 { 2144 int *item; 2145 ref int front() { return *item; } 2146 bool empty() { return *item > 10; } 2147 void popFront() { ++(*item); } 2148 this(this) 2149 { 2150 assert(item is null || cast(size_t) item > 1000); 2151 item = new int(*item); 2152 } 2153 KillableRange save() { return this; } 2154 } 2155 2156 auto kr = KillableRange(new int(1)); 2157 int[] x = [1,2,3,4,5]; // length is first 2158 2159 auto chosen = choose(true, x, kr); 2160 auto chosen2 = chosen.save; 2161 } 2162 2163 pure @safe nothrow unittest 2164 { 2165 static struct S { 2166 int v; 2167 @disable this(this); 2168 } 2169 2170 auto a = [S(1), S(2), S(3)]; 2171 auto b = [S(4), S(5), S(6)]; 2172 2173 auto chosen = choose(true, a, b); 2174 assert(chosen.front.v == 1); 2175 2176 auto chosen2 = choose(false, a, b); 2177 assert(chosen2.front.v == 4); 2178 } 2179 2180 /** 2181 Choose one of multiple ranges at runtime. 2182 2183 The ranges may be different, but they must have compatible element types. The 2184 result is a range that offers the weakest capabilities of all `Ranges`. 2185 2186 Params: 2187 index = which range to choose, must be less than the number of ranges 2188 rs = two or more ranges 2189 2190 Returns: 2191 The indexed range. If rs consists of only one range, the return type is an 2192 alias of that range's type. 2193 */ 2194 auto chooseAmong(Ranges...)(size_t index, return scope Ranges rs) 2195 if (Ranges.length >= 2 2196 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) 2197 && !is(CommonType!(staticMap!(ElementType, Ranges)) == void)) 2198 { 2199 return ChooseResult!Ranges(index, rs); 2200 } 2201 2202 /// 2203 @safe nothrow pure @nogc unittest 2204 { 2205 auto test() 2206 { 2207 import std.algorithm.comparison : equal; 2208 2209 int[4] sarr1 = [1, 2, 3, 4]; 2210 int[2] sarr2 = [5, 6]; 2211 int[1] sarr3 = [7]; 2212 auto arr1 = sarr1[]; 2213 auto arr2 = sarr2[]; 2214 auto arr3 = sarr3[]; 2215 2216 { 2217 auto s = chooseAmong(0, arr1, arr2, arr3); 2218 auto t = s.save; 2219 assert(s.length == 4); 2220 assert(s[2] == 3); 2221 s.popFront(); 2222 assert(equal(t, only(1, 2, 3, 4))); 2223 } 2224 { 2225 auto s = chooseAmong(1, arr1, arr2, arr3); 2226 assert(s.length == 2); 2227 s.front = 8; 2228 assert(equal(s, only(8, 6))); 2229 } 2230 { 2231 auto s = chooseAmong(1, arr1, arr2, arr3); 2232 assert(s.length == 2); 2233 s[1] = 9; 2234 assert(equal(s, only(8, 9))); 2235 } 2236 { 2237 auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3]; 2238 assert(s.length == 2); 2239 assert(equal(s, only(2, 3))); 2240 } 2241 { 2242 auto s = chooseAmong(0, arr1, arr2, arr3); 2243 assert(s.length == 4); 2244 assert(s.back == 4); 2245 s.popBack(); 2246 s.back = 5; 2247 assert(equal(s, only(1, 2, 5))); 2248 s.back = 3; 2249 assert(equal(s, only(1, 2, 3))); 2250 } 2251 { 2252 uint[5] foo = [1, 2, 3, 4, 5]; 2253 uint[5] bar = [6, 7, 8, 9, 10]; 2254 auto c = chooseAmong(1, foo[], bar[]); 2255 assert(c[3] == 9); 2256 c[3] = 42; 2257 assert(c[3] == 42); 2258 assert(c.moveFront() == 6); 2259 assert(c.moveBack() == 10); 2260 assert(c.moveAt(4) == 10); 2261 } 2262 { 2263 import std.range : cycle; 2264 auto s = chooseAmong(0, cycle(arr2), cycle(arr3)); 2265 assert(isInfinite!(typeof(s))); 2266 assert(!s.empty); 2267 assert(s[100] == 8); 2268 assert(s[101] == 9); 2269 assert(s[0 .. 3].equal(only(8, 9, 8))); 2270 } 2271 return 0; 2272 } 2273 // works at runtime 2274 auto a = test(); 2275 // and at compile time 2276 static b = test(); 2277 } 2278 2279 @safe nothrow pure @nogc unittest 2280 { 2281 int[3] a = [1, 2, 3]; 2282 long[3] b = [4, 5, 6]; 2283 auto c = chooseAmong(0, a[], b[]); 2284 c[0] = 42; 2285 assert(c[0] == 42); 2286 } 2287 2288 @safe nothrow pure @nogc unittest 2289 { 2290 static struct RefAccessRange 2291 { 2292 int[] r; 2293 ref front() @property { return r[0]; } 2294 ref back() @property { return r[$ - 1]; } 2295 void popFront() { r = r[1 .. $]; } 2296 void popBack() { r = r[0 .. $ - 1]; } 2297 auto empty() @property { return r.empty; } 2298 ref opIndex(size_t i) { return r[i]; } 2299 auto length() @property { return r.length; } 2300 alias opDollar = length; 2301 auto save() { return this; } 2302 } 2303 static assert(isRandomAccessRange!RefAccessRange); 2304 static assert(isRandomAccessRange!RefAccessRange); 2305 int[4] a = [4, 3, 2, 1]; 2306 int[2] b = [6, 5]; 2307 auto c = chooseAmong(0, RefAccessRange(a[]), RefAccessRange(b[])); 2308 2309 void refFunc(ref int a, int target) { assert(a == target); } 2310 2311 refFunc(c[2], 2); 2312 refFunc(c.front, 4); 2313 refFunc(c.back, 1); 2314 } 2315 2316 2317 /** 2318 $(D roundRobin(r1, r2, r3)) yields `r1.front`, then `r2.front`, 2319 then `r3.front`, after which it pops off one element from each and 2320 continues again from `r1`. For example, if two ranges are involved, 2321 it alternately yields elements off the two ranges. `roundRobin` 2322 stops after it has consumed all ranges (skipping over the ones that 2323 finish early). 2324 */ 2325 auto roundRobin(Rs...)(Rs rs) 2326 if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) 2327 { 2328 struct Result 2329 { 2330 import std.conv : to; 2331 2332 public Rs source; 2333 private size_t _current = size_t.max; 2334 2335 @property bool empty() 2336 { 2337 foreach (i, Unused; Rs) 2338 { 2339 if (!source[i].empty) return false; 2340 } 2341 return true; 2342 } 2343 2344 @property auto ref front() 2345 { 2346 final switch (_current) 2347 { 2348 foreach (i, R; Rs) 2349 { 2350 case i: 2351 assert( 2352 !source[i].empty, 2353 "Attempting to fetch the front of an empty roundRobin" 2354 ); 2355 return source[i].front; 2356 } 2357 } 2358 assert(0); 2359 } 2360 2361 void popFront() 2362 { 2363 final switch (_current) 2364 { 2365 foreach (i, R; Rs) 2366 { 2367 case i: 2368 source[i].popFront(); 2369 break; 2370 } 2371 } 2372 2373 auto next = _current == (Rs.length - 1) ? 0 : (_current + 1); 2374 final switch (next) 2375 { 2376 foreach (i, R; Rs) 2377 { 2378 case i: 2379 if (!source[i].empty) 2380 { 2381 _current = i; 2382 return; 2383 } 2384 if (i == _current) 2385 { 2386 _current = _current.max; 2387 return; 2388 } 2389 goto case (i + 1) % Rs.length; 2390 } 2391 } 2392 } 2393 2394 static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) 2395 @property auto save() 2396 { 2397 auto saveSource(size_t len)() 2398 { 2399 import std.typecons : tuple; 2400 static assert(len > 0); 2401 static if (len == 1) 2402 { 2403 return tuple(source[0].save); 2404 } 2405 else 2406 { 2407 return saveSource!(len - 1)() ~ 2408 tuple(source[len - 1].save); 2409 } 2410 } 2411 return Result(saveSource!(Rs.length).expand, _current); 2412 } 2413 2414 static if (allSatisfy!(hasLength, Rs)) 2415 { 2416 @property size_t length() 2417 { 2418 size_t result; 2419 foreach (i, R; Rs) 2420 { 2421 result += source[i].length; 2422 } 2423 return result; 2424 } 2425 2426 alias opDollar = length; 2427 } 2428 } 2429 2430 return Result(rs, 0); 2431 } 2432 2433 /// 2434 @safe unittest 2435 { 2436 import std.algorithm.comparison : equal; 2437 2438 int[] a = [ 1, 2, 3 ]; 2439 int[] b = [ 10, 20, 30, 40 ]; 2440 auto r = roundRobin(a, b); 2441 assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ])); 2442 } 2443 2444 /** 2445 * roundRobin can be used to create "interleave" functionality which inserts 2446 * an element between each element in a range. 2447 */ 2448 @safe unittest 2449 { 2450 import std.algorithm.comparison : equal; 2451 2452 auto interleave(R, E)(R range, E element) 2453 if ((isInputRange!R && hasLength!R) || isForwardRange!R) 2454 { 2455 static if (hasLength!R) 2456 immutable len = range.length; 2457 else 2458 immutable len = range.save.walkLength; 2459 2460 return roundRobin( 2461 range, 2462 element.repeat(len - 1) 2463 ); 2464 } 2465 2466 assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3])); 2467 } 2468 2469 pure @safe unittest 2470 { 2471 import std.algorithm.comparison : equal; 2472 string f = "foo", b = "bar"; 2473 auto r = roundRobin(refRange(&f), refRange(&b)); 2474 assert(equal(r.save, "fboaor")); 2475 assert(equal(r.save, "fboaor")); 2476 } 2477 pure @safe nothrow unittest 2478 { 2479 import std.algorithm.comparison : equal; 2480 2481 static struct S { 2482 int v; 2483 @disable this(this); 2484 } 2485 2486 S[] a = [ S(1), S(2) ]; 2487 S[] b = [ S(10), S(20) ]; 2488 auto r = roundRobin(a, b); 2489 assert(equal(r, [ S(1), S(10), S(2), S(20) ])); 2490 } 2491 2492 /** 2493 Iterates a random-access range starting from a given point and 2494 progressively extending left and right from that point. If no initial 2495 point is given, iteration starts from the middle of the 2496 range. Iteration spans the entire range. 2497 2498 When `startingIndex` is 0 the range will be fully iterated in order 2499 and in reverse order when `r.length` is given. 2500 2501 Params: 2502 r = a random access range with length and slicing 2503 startingIndex = the index to begin iteration from 2504 2505 Returns: 2506 A forward range with length 2507 */ 2508 auto radial(Range, I)(Range r, I startingIndex) 2509 if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I) 2510 { 2511 if (startingIndex != r.length) ++startingIndex; 2512 return roundRobin(retro(r[0 .. startingIndex]), r[startingIndex .. r.length]); 2513 } 2514 2515 /// Ditto 2516 auto radial(R)(R r) 2517 if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R)) 2518 { 2519 return .radial(r, (r.length - !r.empty) / 2); 2520 } 2521 2522 /// 2523 @safe unittest 2524 { 2525 import std.algorithm.comparison : equal; 2526 int[] a = [ 1, 2, 3, 4, 5 ]; 2527 assert(equal(radial(a), [ 3, 4, 2, 5, 1 ])); 2528 a = [ 1, 2, 3, 4 ]; 2529 assert(equal(radial(a), [ 2, 3, 1, 4 ])); 2530 2531 // If the left end is reached first, the remaining elements on the right 2532 // are concatenated in order: 2533 a = [ 0, 1, 2, 3, 4, 5 ]; 2534 assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ])); 2535 2536 // If the right end is reached first, the remaining elements on the left 2537 // are concatenated in reverse order: 2538 assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ])); 2539 } 2540 2541 @safe unittest 2542 { 2543 import std.algorithm.comparison : equal; 2544 import std.conv : text; 2545 import std.exception : enforce; 2546 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 2547 2548 void test(int[] input, int[] witness) 2549 { 2550 enforce(equal(radial(input), witness), 2551 text(radial(input), " vs. ", witness)); 2552 } 2553 test([], []); 2554 test([ 1 ], [ 1 ]); 2555 test([ 1, 2 ], [ 1, 2 ]); 2556 test([ 1, 2, 3 ], [ 2, 3, 1 ]); 2557 test([ 1, 2, 3, 4 ], [ 2, 3, 1, 4 ]); 2558 test([ 1, 2, 3, 4, 5 ], [ 3, 4, 2, 5, 1 ]); 2559 test([ 1, 2, 3, 4, 5, 6 ], [ 3, 4, 2, 5, 1, 6 ]); 2560 2561 int[] a = [ 1, 2, 3, 4, 5 ]; 2562 assert(equal(radial(a, 1), [ 2, 3, 1, 4, 5 ])); 2563 assert(equal(radial(a, 0), [ 1, 2, 3, 4, 5 ])); // only right subrange 2564 assert(equal(radial(a, a.length), [ 5, 4, 3, 2, 1 ])); // only left subrange 2565 static assert(isForwardRange!(typeof(radial(a, 1)))); 2566 2567 auto r = radial([1,2,3,4,5]); 2568 for (auto rr = r.save; !rr.empty; rr.popFront()) 2569 { 2570 assert(rr.front == moveFront(rr)); 2571 } 2572 r.front = 5; 2573 assert(r.front == 5); 2574 2575 // Test instantiation without lvalue elements. 2576 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random) dummy; 2577 assert(equal(radial(dummy, 4), [5, 6, 4, 7, 3, 8, 2, 9, 1, 10])); 2578 2579 // immutable int[] immi = [ 1, 2 ]; 2580 // static assert(is(typeof(radial(immi)))); 2581 } 2582 2583 @safe unittest 2584 { 2585 import std.algorithm.comparison : equal; 2586 2587 auto LL = iota(1L, 6L); 2588 auto r = radial(LL); 2589 assert(equal(r, [3L, 4L, 2L, 5L, 1L])); 2590 } 2591 2592 /** 2593 Lazily takes only up to `n` elements of a range. This is 2594 particularly useful when using with infinite ranges. 2595 2596 Unlike $(LREF takeExactly), `take` does not require that there 2597 are `n` or more elements in `input`. As a consequence, length 2598 information is not applied to the result unless `input` also has 2599 length information. 2600 2601 Params: 2602 input = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 2603 to iterate over up to `n` times 2604 n = the number of elements to take 2605 2606 Returns: 2607 At minimum, an input range. If the range offers random access 2608 and `length`, `take` offers them as well. 2609 */ 2610 Take!R take(R)(R input, size_t n) 2611 if (isInputRange!(Unqual!R)) 2612 { 2613 alias U = Unqual!R; 2614 static if (is(R T == Take!T)) 2615 { 2616 import std.algorithm.comparison : min; 2617 return R(input.source, min(n, input._maxAvailable)); 2618 } 2619 else static if (!isInfinite!U && hasSlicing!U) 2620 { 2621 import std.algorithm.comparison : min; 2622 return input[0 .. min(n, input.length)]; 2623 } 2624 else 2625 { 2626 return Take!R(input, n); 2627 } 2628 } 2629 2630 /// ditto 2631 struct Take(Range) 2632 if (isInputRange!(Unqual!Range) && 2633 //take _cannot_ test hasSlicing on infinite ranges, because hasSlicing uses 2634 //take for slicing infinite ranges. 2635 !((!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range)) || is(Range T == Take!T))) 2636 { 2637 private alias R = Unqual!Range; 2638 2639 /// User accessible in read and write 2640 public R source; 2641 2642 private size_t _maxAvailable; 2643 2644 alias Source = R; 2645 2646 /// Range primitives 2647 @property bool empty() 2648 { 2649 return _maxAvailable == 0 || source.empty; 2650 } 2651 2652 /// ditto 2653 @property auto ref front() 2654 { 2655 assert(!empty, 2656 "Attempting to fetch the front of an empty " 2657 ~ Take.stringof); 2658 return source.front; 2659 } 2660 2661 /// ditto 2662 void popFront() 2663 { 2664 assert(!empty, 2665 "Attempting to popFront() past the end of a " 2666 ~ Take.stringof); 2667 source.popFront(); 2668 --_maxAvailable; 2669 } 2670 2671 static if (isForwardRange!R) 2672 /// ditto 2673 @property Take save() 2674 { 2675 return Take(source.save, _maxAvailable); 2676 } 2677 2678 static if (hasAssignableElements!R) 2679 /// ditto 2680 @property void front(ElementType!R v) 2681 { 2682 import std.algorithm.mutation : move; 2683 2684 assert(!empty, 2685 "Attempting to assign to the front of an empty " 2686 ~ Take.stringof); 2687 source.front = move(v); 2688 } 2689 2690 static if (hasMobileElements!R) 2691 { 2692 /// ditto 2693 auto moveFront() 2694 { 2695 assert(!empty, 2696 "Attempting to move the front of an empty " 2697 ~ Take.stringof); 2698 return source.moveFront(); 2699 } 2700 } 2701 2702 static if (isInfinite!R) 2703 { 2704 /// ditto 2705 @property size_t length() const 2706 { 2707 return _maxAvailable; 2708 } 2709 2710 /// ditto 2711 alias opDollar = length; 2712 2713 //Note: Due to Take/hasSlicing circular dependency, 2714 //This needs to be a restrained template. 2715 /// ditto 2716 auto opSlice()(size_t i, size_t j) 2717 if (hasSlicing!R) 2718 { 2719 assert(i <= j, "Invalid slice bounds"); 2720 assert(j <= length, "Attempting to slice past the end of a " 2721 ~ Take.stringof); 2722 return source[i .. j]; 2723 } 2724 } 2725 else static if (hasLength!R) 2726 { 2727 /// ditto 2728 @property size_t length() 2729 { 2730 import std.algorithm.comparison : min; 2731 return min(_maxAvailable, source.length); 2732 } 2733 2734 alias opDollar = length; 2735 } 2736 2737 static if (isRandomAccessRange!R) 2738 { 2739 /// ditto 2740 void popBack() 2741 { 2742 assert(!empty, 2743 "Attempting to popBack() past the beginning of a " 2744 ~ Take.stringof); 2745 --_maxAvailable; 2746 } 2747 2748 /// ditto 2749 @property auto ref back() 2750 { 2751 assert(!empty, 2752 "Attempting to fetch the back of an empty " 2753 ~ Take.stringof); 2754 return source[this.length - 1]; 2755 } 2756 2757 /// ditto 2758 auto ref opIndex(size_t index) 2759 { 2760 assert(index < length, 2761 "Attempting to index out of the bounds of a " 2762 ~ Take.stringof); 2763 return source[index]; 2764 } 2765 2766 static if (hasAssignableElements!R) 2767 { 2768 /// ditto 2769 @property void back(ElementType!R v) 2770 { 2771 // This has to return auto instead of void because of 2772 // https://issues.dlang.org/show_bug.cgi?id=4706 2773 assert(!empty, 2774 "Attempting to assign to the back of an empty " 2775 ~ Take.stringof); 2776 source[this.length - 1] = v; 2777 } 2778 2779 /// ditto 2780 void opIndexAssign(ElementType!R v, size_t index) 2781 { 2782 assert(index < length, 2783 "Attempting to index out of the bounds of a " 2784 ~ Take.stringof); 2785 source[index] = v; 2786 } 2787 } 2788 2789 static if (hasMobileElements!R) 2790 { 2791 /// ditto 2792 auto moveBack() 2793 { 2794 assert(!empty, 2795 "Attempting to move the back of an empty " 2796 ~ Take.stringof); 2797 return source.moveAt(this.length - 1); 2798 } 2799 2800 /// ditto 2801 auto moveAt(size_t index) 2802 { 2803 assert(index < length, 2804 "Attempting to index out of the bounds of a " 2805 ~ Take.stringof); 2806 return source.moveAt(index); 2807 } 2808 } 2809 } 2810 2811 /** 2812 Access to maximal length of the range. 2813 Note: the actual length of the range depends on the underlying range. 2814 If it has fewer elements, it will stop before maxLength is reached. 2815 */ 2816 @property size_t maxLength() const 2817 { 2818 return _maxAvailable; 2819 } 2820 } 2821 2822 /// ditto 2823 template Take(R) 2824 if (isInputRange!(Unqual!R) && 2825 ((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T))) 2826 { 2827 alias Take = R; 2828 } 2829 2830 /// 2831 pure @safe nothrow unittest 2832 { 2833 import std.algorithm.comparison : equal; 2834 2835 int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 2836 auto s = take(arr1, 5); 2837 assert(s.length == 5); 2838 assert(s[4] == 5); 2839 assert(equal(s, [ 1, 2, 3, 4, 5 ][])); 2840 } 2841 2842 /** 2843 * If the range runs out before `n` elements, `take` simply returns the entire 2844 * range (unlike $(LREF takeExactly), which will cause an assertion failure if 2845 * the range ends prematurely): 2846 */ 2847 pure @safe nothrow unittest 2848 { 2849 import std.algorithm.comparison : equal; 2850 2851 int[] arr2 = [ 1, 2, 3 ]; 2852 auto t = take(arr2, 5); 2853 assert(t.length == 3); 2854 assert(equal(t, [ 1, 2, 3 ])); 2855 } 2856 2857 pure @safe nothrow unittest 2858 { 2859 import std.algorithm.comparison : equal; 2860 import std.internal.test.dummyrange : AllDummyRanges; 2861 2862 int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 2863 auto s = take(arr1, 5); 2864 assert(s.length == 5); 2865 assert(s[4] == 5); 2866 assert(equal(s, [ 1, 2, 3, 4, 5 ][])); 2867 assert(equal(retro(s), [ 5, 4, 3, 2, 1 ][])); 2868 2869 // Test fix for bug 4464. 2870 static assert(is(typeof(s) == Take!(int[]))); 2871 static assert(is(typeof(s) == int[])); 2872 2873 // Test using narrow strings. 2874 import std.exception : assumeWontThrow; 2875 2876 auto myStr = "This is a string."; 2877 auto takeMyStr = take(myStr, 7); 2878 assert(assumeWontThrow(equal(takeMyStr, "This is"))); 2879 // Test fix for bug 5052. 2880 auto takeMyStrAgain = take(takeMyStr, 4); 2881 assert(assumeWontThrow(equal(takeMyStrAgain, "This"))); 2882 static assert(is (typeof(takeMyStrAgain) == typeof(takeMyStr))); 2883 takeMyStrAgain = take(takeMyStr, 10); 2884 assert(assumeWontThrow(equal(takeMyStrAgain, "This is"))); 2885 2886 foreach (DummyType; AllDummyRanges) 2887 { 2888 DummyType dummy; 2889 auto t = take(dummy, 5); 2890 alias T = typeof(t); 2891 2892 static if (isRandomAccessRange!DummyType) 2893 { 2894 static assert(isRandomAccessRange!T); 2895 assert(t[4] == 5); 2896 2897 assert(moveAt(t, 1) == t[1]); 2898 assert(t.back == moveBack(t)); 2899 } 2900 else static if (isForwardRange!DummyType) 2901 { 2902 static assert(isForwardRange!T); 2903 } 2904 2905 for (auto tt = t; !tt.empty; tt.popFront()) 2906 { 2907 assert(tt.front == moveFront(tt)); 2908 } 2909 2910 // Bidirectional ranges can't be propagated properly if they don't 2911 // also have random access. 2912 2913 assert(equal(t, [1,2,3,4,5])); 2914 2915 //Test that take doesn't wrap the result of take. 2916 assert(take(t, 4) == take(dummy, 4)); 2917 } 2918 2919 immutable myRepeat = repeat(1); 2920 static assert(is(Take!(typeof(myRepeat)))); 2921 } 2922 2923 pure @safe nothrow @nogc unittest 2924 { 2925 //check for correct slicing of Take on an infinite range 2926 import std.algorithm.comparison : equal; 2927 foreach (start; 0 .. 4) 2928 foreach (stop; start .. 4) 2929 assert(iota(4).cycle.take(4)[start .. stop] 2930 .equal(iota(start, stop))); 2931 } 2932 2933 pure @safe nothrow @nogc unittest 2934 { 2935 // Check that one can declare variables of all Take types, 2936 // and that they match the return type of the corresponding 2937 // take(). 2938 // See https://issues.dlang.org/show_bug.cgi?id=4464 2939 int[] r1; 2940 Take!(int[]) t1; 2941 t1 = take(r1, 1); 2942 assert(t1.empty); 2943 2944 string r2; 2945 Take!string t2; 2946 t2 = take(r2, 1); 2947 assert(t2.empty); 2948 2949 Take!(Take!string) t3; 2950 t3 = take(t2, 1); 2951 assert(t3.empty); 2952 } 2953 2954 pure @safe nothrow @nogc unittest 2955 { 2956 alias R1 = typeof(repeat(1)); 2957 alias R2 = typeof(cycle([1])); 2958 alias TR1 = Take!R1; 2959 alias TR2 = Take!R2; 2960 static assert(isBidirectionalRange!TR1); 2961 static assert(isBidirectionalRange!TR2); 2962 } 2963 2964 // https://issues.dlang.org/show_bug.cgi?id=12731 2965 pure @safe nothrow @nogc unittest 2966 { 2967 auto a = repeat(1); 2968 auto s = a[1 .. 5]; 2969 s = s[1 .. 3]; 2970 assert(s.length == 2); 2971 assert(s[0] == 1); 2972 assert(s[1] == 1); 2973 } 2974 2975 // https://issues.dlang.org/show_bug.cgi?id=13151 2976 pure @safe nothrow @nogc unittest 2977 { 2978 import std.algorithm.comparison : equal; 2979 2980 auto r = take(repeat(1, 4), 3); 2981 assert(r.take(2).equal(repeat(1, 2))); 2982 } 2983 2984 2985 /** 2986 Similar to $(LREF take), but assumes that `range` has at least $(D 2987 n) elements. Consequently, the result of $(D takeExactly(range, n)) 2988 always defines the `length` property (and initializes it to `n`) 2989 even when `range` itself does not define `length`. 2990 2991 The result of `takeExactly` is identical to that of $(LREF take) in 2992 cases where the original range defines `length` or is infinite. 2993 2994 Unlike $(LREF take), however, it is illegal to pass a range with less than 2995 `n` elements to `takeExactly`; this will cause an assertion failure. 2996 */ 2997 auto takeExactly(R)(R range, size_t n) 2998 if (isInputRange!R) 2999 { 3000 static if (is(typeof(takeExactly(range._input, n)) == R)) 3001 { 3002 assert(n <= range._n, 3003 "Attempted to take more than the length of the range with takeExactly."); 3004 // takeExactly(takeExactly(r, n1), n2) has the same type as 3005 // takeExactly(r, n1) and simply returns takeExactly(r, n2) 3006 range._n = n; 3007 return range; 3008 } 3009 //Also covers hasSlicing!R for finite ranges. 3010 else static if (hasLength!R) 3011 { 3012 assert(n <= range.length, 3013 "Attempted to take more than the length of the range with takeExactly."); 3014 return take(range, n); 3015 } 3016 else static if (isInfinite!R) 3017 return Take!R(range, n); 3018 else 3019 { 3020 static struct Result 3021 { 3022 R _input; 3023 private size_t _n; 3024 3025 @property bool empty() const { return !_n; } 3026 @property auto ref front() 3027 { 3028 assert(_n > 0, "front() on an empty " ~ Result.stringof); 3029 return _input.front; 3030 } 3031 void popFront() { _input.popFront(); --_n; } 3032 @property size_t length() const { return _n; } 3033 alias opDollar = length; 3034 3035 @property auto _takeExactly_Result_asTake() 3036 { 3037 return take(_input, _n); 3038 } 3039 3040 alias _takeExactly_Result_asTake this; 3041 3042 static if (isForwardRange!R) 3043 @property auto save() 3044 { 3045 return Result(_input.save, _n); 3046 } 3047 3048 static if (hasMobileElements!R) 3049 { 3050 auto moveFront() 3051 { 3052 assert(!empty, 3053 "Attempting to move the front of an empty " 3054 ~ typeof(this).stringof); 3055 return _input.moveFront(); 3056 } 3057 } 3058 3059 static if (hasAssignableElements!R) 3060 { 3061 @property auto ref front(ElementType!R v) 3062 { 3063 import std.algorithm.mutation : move; 3064 3065 assert(!empty, 3066 "Attempting to assign to the front of an empty " 3067 ~ typeof(this).stringof); 3068 return _input.front = move(v); 3069 } 3070 } 3071 } 3072 3073 return Result(range, n); 3074 } 3075 } 3076 3077 /// 3078 pure @safe nothrow unittest 3079 { 3080 import std.algorithm.comparison : equal; 3081 3082 auto a = [ 1, 2, 3, 4, 5 ]; 3083 3084 auto b = takeExactly(a, 3); 3085 assert(equal(b, [1, 2, 3])); 3086 static assert(is(typeof(b.length) == size_t)); 3087 assert(b.length == 3); 3088 assert(b.front == 1); 3089 assert(b.back == 3); 3090 } 3091 3092 pure @safe nothrow unittest 3093 { 3094 import std.algorithm.comparison : equal; 3095 import std.algorithm.iteration : filter; 3096 3097 auto a = [ 1, 2, 3, 4, 5 ]; 3098 auto b = takeExactly(a, 3); 3099 assert(equal(b, [1, 2, 3])); 3100 auto c = takeExactly(b, 2); 3101 assert(equal(c, [1, 2])); 3102 3103 3104 3105 auto d = filter!"a > 2"(a); 3106 auto e = takeExactly(d, 3); 3107 assert(equal(e, [3, 4, 5])); 3108 static assert(is(typeof(e.length) == size_t)); 3109 assert(e.length == 3); 3110 assert(e.front == 3); 3111 3112 assert(equal(takeExactly(e, 3), [3, 4, 5])); 3113 } 3114 3115 pure @safe nothrow unittest 3116 { 3117 import std.algorithm.comparison : equal; 3118 import std.internal.test.dummyrange : AllDummyRanges; 3119 3120 auto a = [ 1, 2, 3, 4, 5 ]; 3121 //Test that take and takeExactly are the same for ranges which define length 3122 //but aren't sliceable. 3123 struct L 3124 { 3125 @property auto front() { return _arr[0]; } 3126 @property bool empty() { return _arr.empty; } 3127 void popFront() { _arr.popFront(); } 3128 @property size_t length() { return _arr.length; } 3129 int[] _arr; 3130 } 3131 static assert(is(typeof(take(L(a), 3)) == typeof(takeExactly(L(a), 3)))); 3132 assert(take(L(a), 3) == takeExactly(L(a), 3)); 3133 3134 //Test that take and takeExactly are the same for ranges which are sliceable. 3135 static assert(is(typeof(take(a, 3)) == typeof(takeExactly(a, 3)))); 3136 assert(take(a, 3) == takeExactly(a, 3)); 3137 3138 //Test that take and takeExactly are the same for infinite ranges. 3139 auto inf = repeat(1); 3140 static assert(is(typeof(take(inf, 5)) == Take!(typeof(inf)))); 3141 assert(take(inf, 5) == takeExactly(inf, 5)); 3142 3143 //Test that take and takeExactly are _not_ the same for ranges which don't 3144 //define length. 3145 static assert(!is(typeof(take(filter!"true"(a), 3)) == typeof(takeExactly(filter!"true"(a), 3)))); 3146 3147 foreach (DummyType; AllDummyRanges) 3148 { 3149 { 3150 DummyType dummy; 3151 auto t = takeExactly(dummy, 5); 3152 3153 //Test that takeExactly doesn't wrap the result of takeExactly. 3154 assert(takeExactly(t, 4) == takeExactly(dummy, 4)); 3155 } 3156 3157 static if (hasMobileElements!DummyType) 3158 { 3159 { 3160 auto t = takeExactly(DummyType.init, 4); 3161 assert(t.moveFront() == 1); 3162 assert(equal(t, [1, 2, 3, 4])); 3163 } 3164 } 3165 3166 static if (hasAssignableElements!DummyType) 3167 { 3168 { 3169 auto t = takeExactly(DummyType.init, 4); 3170 t.front = 9; 3171 assert(equal(t, [9, 2, 3, 4])); 3172 } 3173 } 3174 } 3175 } 3176 3177 pure @safe nothrow unittest 3178 { 3179 import std.algorithm.comparison : equal; 3180 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 3181 3182 alias DummyType = DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward); 3183 auto te = takeExactly(DummyType(), 5); 3184 Take!DummyType t = te; 3185 assert(equal(t, [1, 2, 3, 4, 5])); 3186 assert(equal(t, te)); 3187 } 3188 3189 // https://issues.dlang.org/show_bug.cgi?id=18092 3190 // can't combine take and takeExactly 3191 @safe unittest 3192 { 3193 import std.algorithm.comparison : equal; 3194 import std.internal.test.dummyrange : AllDummyRanges; 3195 3196 static foreach (Range; AllDummyRanges) 3197 {{ 3198 Range r; 3199 assert(r.take(6).takeExactly(2).equal([1, 2])); 3200 assert(r.takeExactly(6).takeExactly(2).equal([1, 2])); 3201 assert(r.takeExactly(6).take(2).equal([1, 2])); 3202 }} 3203 } 3204 3205 /** 3206 Returns a range with at most one element; for example, $(D 3207 takeOne([42, 43, 44])) returns a range consisting of the integer $(D 3208 42). Calling `popFront()` off that range renders it empty. 3209 3210 In effect `takeOne(r)` is somewhat equivalent to $(D take(r, 1)) but in 3211 certain interfaces it is important to know statically that the range may only 3212 have at most one element. 3213 3214 The type returned by `takeOne` is a random-access range with length 3215 regardless of `R`'s capabilities, as long as it is a forward range. 3216 (another feature that distinguishes `takeOne` from `take`). If 3217 (D R) is an input range but not a forward range, return type is an input 3218 range with all random-access capabilities except save. 3219 */ 3220 auto takeOne(R)(R source) 3221 if (isInputRange!R) 3222 { 3223 static if (hasSlicing!R) 3224 { 3225 return source[0 .. !source.empty]; 3226 } 3227 else 3228 { 3229 static struct Result 3230 { 3231 private R _source; 3232 private bool _empty = true; 3233 @property bool empty() const { return _empty; } 3234 @property auto ref front() 3235 { 3236 assert(!empty, "Attempting to fetch the front of an empty takeOne"); 3237 return _source.front; 3238 } 3239 void popFront() 3240 { 3241 assert(!empty, "Attempting to popFront an empty takeOne"); 3242 _source.popFront(); 3243 _empty = true; 3244 } 3245 void popBack() 3246 { 3247 assert(!empty, "Attempting to popBack an empty takeOne"); 3248 _source.popFront(); 3249 _empty = true; 3250 } 3251 static if (isForwardRange!(Unqual!R)) 3252 { 3253 @property auto save() { return Result(_source.save, empty); } 3254 } 3255 @property auto ref back() 3256 { 3257 assert(!empty, "Attempting to fetch the back of an empty takeOne"); 3258 return _source.front; 3259 } 3260 @property size_t length() const { return !empty; } 3261 alias opDollar = length; 3262 auto ref opIndex(size_t n) 3263 { 3264 assert(n < length, "Attempting to index a takeOne out of bounds"); 3265 return _source.front; 3266 } 3267 auto opSlice(size_t m, size_t n) 3268 { 3269 assert( 3270 m <= n, 3271 "Attempting to slice a takeOne range with a larger first argument than the second." 3272 ); 3273 assert( 3274 n <= length, 3275 "Attempting to slice using an out of bounds index on a takeOne range." 3276 ); 3277 return n > m ? this : Result(_source, true); 3278 } 3279 // Non-standard property 3280 @property R source() { return _source; } 3281 } 3282 3283 return Result(source, source.empty); 3284 } 3285 } 3286 3287 /// 3288 pure @safe nothrow unittest 3289 { 3290 auto s = takeOne([42, 43, 44]); 3291 static assert(isRandomAccessRange!(typeof(s))); 3292 assert(s.length == 1); 3293 assert(!s.empty); 3294 assert(s.front == 42); 3295 s.front = 43; 3296 assert(s.front == 43); 3297 assert(s.back == 43); 3298 assert(s[0] == 43); 3299 s.popFront(); 3300 assert(s.length == 0); 3301 assert(s.empty); 3302 } 3303 3304 pure @safe nothrow @nogc unittest 3305 { 3306 struct NonForwardRange 3307 { 3308 enum empty = false; 3309 int front() { return 42; } 3310 void popFront() {} 3311 } 3312 3313 static assert(!isForwardRange!NonForwardRange); 3314 3315 auto s = takeOne(NonForwardRange()); 3316 assert(s.length == 1); 3317 assert(!s.empty); 3318 assert(s.front == 42); 3319 assert(s.back == 42); 3320 assert(s[0] == 42); 3321 3322 auto t = s[0 .. 0]; 3323 assert(t.empty); 3324 assert(t.length == 0); 3325 3326 auto u = s[1 .. 1]; 3327 assert(u.empty); 3328 assert(u.length == 0); 3329 3330 auto v = s[0 .. 1]; 3331 s.popFront(); 3332 assert(s.length == 0); 3333 assert(s.empty); 3334 assert(!v.empty); 3335 assert(v.front == 42); 3336 v.popBack(); 3337 assert(v.empty); 3338 assert(v.length == 0); 3339 } 3340 3341 pure @safe nothrow @nogc unittest 3342 { 3343 struct NonSlicingForwardRange 3344 { 3345 enum empty = false; 3346 int front() { return 42; } 3347 void popFront() {} 3348 @property auto save() { return this; } 3349 } 3350 3351 static assert(isForwardRange!NonSlicingForwardRange); 3352 static assert(!hasSlicing!NonSlicingForwardRange); 3353 3354 auto s = takeOne(NonSlicingForwardRange()); 3355 assert(s.length == 1); 3356 assert(!s.empty); 3357 assert(s.front == 42); 3358 assert(s.back == 42); 3359 assert(s[0] == 42); 3360 auto t = s.save; 3361 s.popFront(); 3362 assert(s.length == 0); 3363 assert(s.empty); 3364 assert(!t.empty); 3365 assert(t.front == 42); 3366 t.popBack(); 3367 assert(t.empty); 3368 assert(t.length == 0); 3369 } 3370 3371 // Test that asserts trigger correctly 3372 @system unittest 3373 { 3374 import std.exception : assertThrown; 3375 import core.exception : AssertError; 3376 3377 struct NonForwardRange 3378 { 3379 enum empty = false; 3380 int front() { return 42; } 3381 void popFront() {} 3382 } 3383 3384 auto s = takeOne(NonForwardRange()); 3385 3386 assertThrown!AssertError(s[1]); 3387 assertThrown!AssertError(s[0 .. 2]); 3388 3389 size_t one = 1; // Avoid style warnings triggered by literals 3390 size_t zero = 0; 3391 assertThrown!AssertError(s[one .. zero]); 3392 3393 s.popFront; 3394 assert(s.empty); 3395 assertThrown!AssertError(s.front); 3396 assertThrown!AssertError(s.back); 3397 assertThrown!AssertError(s.popFront); 3398 assertThrown!AssertError(s.popBack); 3399 } 3400 3401 // https://issues.dlang.org/show_bug.cgi?id=16999 3402 pure @safe unittest 3403 { 3404 auto myIota = new class 3405 { 3406 int front = 0; 3407 @safe void popFront(){front++;} 3408 enum empty = false; 3409 }; 3410 auto iotaPart = myIota.takeOne; 3411 int sum; 3412 foreach (var; chain(iotaPart, iotaPart, iotaPart)) 3413 { 3414 sum += var; 3415 } 3416 assert(sum == 3); 3417 assert(iotaPart.front == 3); 3418 } 3419 3420 /++ 3421 Returns an empty range which is statically known to be empty and is 3422 guaranteed to have `length` and be random access regardless of `R`'s 3423 capabilities. 3424 +/ 3425 auto takeNone(R)() 3426 if (isInputRange!R) 3427 { 3428 return typeof(takeOne(R.init)).init; 3429 } 3430 3431 /// 3432 pure @safe nothrow @nogc unittest 3433 { 3434 auto range = takeNone!(int[])(); 3435 assert(range.length == 0); 3436 assert(range.empty); 3437 } 3438 3439 pure @safe nothrow @nogc unittest 3440 { 3441 enum ctfe = takeNone!(int[])(); 3442 static assert(ctfe.length == 0); 3443 static assert(ctfe.empty); 3444 } 3445 3446 3447 /++ 3448 Creates an empty range from the given range in $(BIGOH 1). If it can, it 3449 will return the same range type. If not, it will return 3450 $(D takeExactly(range, 0)). 3451 +/ 3452 auto takeNone(R)(R range) 3453 if (isInputRange!R) 3454 { 3455 import std.traits : isDynamicArray; 3456 //Makes it so that calls to takeNone which don't use UFCS still work with a 3457 //member version if it's defined. 3458 static if (is(typeof(R.takeNone))) 3459 auto retval = range.takeNone(); 3460 // https://issues.dlang.org/show_bug.cgi?id=8339 3461 else static if (isDynamicArray!R)/+ || 3462 (is(R == struct) && __traits(compiles, {auto r = R.init;}) && R.init.empty))+/ 3463 { 3464 auto retval = R.init; 3465 } 3466 //An infinite range sliced at [0 .. 0] would likely still not be empty... 3467 else static if (hasSlicing!R && !isInfinite!R) 3468 auto retval = range[0 .. 0]; 3469 else 3470 auto retval = takeExactly(range, 0); 3471 3472 // https://issues.dlang.org/show_bug.cgi?id=7892 prevents this from being 3473 // done in an out block. 3474 assert(retval.empty); 3475 return retval; 3476 } 3477 3478 /// 3479 pure @safe nothrow unittest 3480 { 3481 import std.algorithm.iteration : filter; 3482 assert(takeNone([42, 27, 19]).empty); 3483 assert(takeNone("dlang.org").empty); 3484 assert(takeNone(filter!"true"([42, 27, 19])).empty); 3485 } 3486 3487 @safe unittest 3488 { 3489 import std.algorithm.iteration : filter; 3490 import std.meta : AliasSeq; 3491 3492 struct Dummy 3493 { 3494 mixin template genInput() 3495 { 3496 @safe: 3497 @property bool empty() { return _arr.empty; } 3498 @property auto front() { return _arr.front; } 3499 void popFront() { _arr.popFront(); } 3500 static assert(isInputRange!(typeof(this))); 3501 } 3502 } 3503 alias genInput = Dummy.genInput; 3504 3505 static struct NormalStruct 3506 { 3507 //Disabled to make sure that the takeExactly version is used. 3508 @disable this(); 3509 this(int[] arr) { _arr = arr; } 3510 mixin genInput; 3511 int[] _arr; 3512 } 3513 3514 static struct SliceStruct 3515 { 3516 @disable this(); 3517 this(int[] arr) { _arr = arr; } 3518 mixin genInput; 3519 @property auto save() { return this; } 3520 auto opSlice(size_t i, size_t j) { return typeof(this)(_arr[i .. j]); } 3521 @property size_t length() { return _arr.length; } 3522 int[] _arr; 3523 } 3524 3525 static struct InitStruct 3526 { 3527 mixin genInput; 3528 int[] _arr; 3529 } 3530 3531 static struct TakeNoneStruct 3532 { 3533 this(int[] arr) { _arr = arr; } 3534 @disable this(); 3535 mixin genInput; 3536 auto takeNone() { return typeof(this)(null); } 3537 int[] _arr; 3538 } 3539 3540 static class NormalClass 3541 { 3542 this(int[] arr) {_arr = arr;} 3543 mixin genInput; 3544 int[] _arr; 3545 } 3546 3547 static class SliceClass 3548 { 3549 @safe: 3550 this(int[] arr) { _arr = arr; } 3551 mixin genInput; 3552 @property auto save() { return new typeof(this)(_arr); } 3553 auto opSlice(size_t i, size_t j) { return new typeof(this)(_arr[i .. j]); } 3554 @property size_t length() { return _arr.length; } 3555 int[] _arr; 3556 } 3557 3558 static class TakeNoneClass 3559 { 3560 @safe: 3561 this(int[] arr) { _arr = arr; } 3562 mixin genInput; 3563 auto takeNone() { return new typeof(this)(null); } 3564 int[] _arr; 3565 } 3566 3567 import std.format : format; 3568 3569 static foreach (range; AliasSeq!([1, 2, 3, 4, 5], 3570 "hello world", 3571 "hello world"w, 3572 "hello world"d, 3573 SliceStruct([1, 2, 3]), 3574 // https://issues.dlang.org/show_bug.cgi?id=8339 3575 // forces this to be takeExactly `InitStruct([1, 2, 3]), 3576 TakeNoneStruct([1, 2, 3]))) 3577 { 3578 static assert(takeNone(range).empty, typeof(range).stringof); 3579 assert(takeNone(range).empty); 3580 static assert(is(typeof(range) == typeof(takeNone(range))), typeof(range).stringof); 3581 } 3582 3583 static foreach (range; AliasSeq!(NormalStruct([1, 2, 3]), 3584 InitStruct([1, 2, 3]))) 3585 { 3586 static assert(takeNone(range).empty, typeof(range).stringof); 3587 assert(takeNone(range).empty); 3588 static assert(is(typeof(takeExactly(range, 0)) == typeof(takeNone(range))), typeof(range).stringof); 3589 } 3590 3591 //Don't work in CTFE. 3592 auto normal = new NormalClass([1, 2, 3]); 3593 assert(takeNone(normal).empty); 3594 static assert(is(typeof(takeExactly(normal, 0)) == typeof(takeNone(normal))), typeof(normal).stringof); 3595 3596 auto slice = new SliceClass([1, 2, 3]); 3597 assert(takeNone(slice).empty); 3598 static assert(is(SliceClass == typeof(takeNone(slice))), typeof(slice).stringof); 3599 3600 auto taken = new TakeNoneClass([1, 2, 3]); 3601 assert(takeNone(taken).empty); 3602 static assert(is(TakeNoneClass == typeof(takeNone(taken))), typeof(taken).stringof); 3603 3604 auto filtered = filter!"true"([1, 2, 3, 4, 5]); 3605 assert(takeNone(filtered).empty); 3606 // https://issues.dlang.org/show_bug.cgi?id=8339 and 3607 // https://issues.dlang.org/show_bug.cgi?id=5941 force this to be takeExactly 3608 //static assert(is(typeof(filtered) == typeof(takeNone(filtered))), typeof(filtered).stringof); 3609 } 3610 3611 /++ 3612 + Return a range advanced to within `_n` elements of the end of 3613 + `range`. 3614 + 3615 + Intended as the range equivalent of the Unix 3616 + $(HTTP en.wikipedia.org/wiki/Tail_%28Unix%29, _tail) utility. When the length 3617 + of `range` is less than or equal to `_n`, `range` is returned 3618 + as-is. 3619 + 3620 + Completes in $(BIGOH 1) steps for ranges that support slicing and have 3621 + length. Completes in $(BIGOH range.length) time for all other ranges. 3622 + 3623 + Params: 3624 + range = range to get _tail of 3625 + n = maximum number of elements to include in _tail 3626 + 3627 + Returns: 3628 + Returns the _tail of `range` augmented with length information 3629 +/ 3630 auto tail(Range)(Range range, size_t n) 3631 if (isInputRange!Range && !isInfinite!Range && 3632 (hasLength!Range || isForwardRange!Range)) 3633 { 3634 static if (hasLength!Range) 3635 { 3636 immutable length = range.length; 3637 if (n >= length) 3638 return range.takeExactly(length); 3639 else 3640 return range.drop(length - n).takeExactly(n); 3641 } 3642 else 3643 { 3644 Range scout = range.save; 3645 foreach (immutable i; 0 .. n) 3646 { 3647 if (scout.empty) 3648 return range.takeExactly(i); 3649 scout.popFront(); 3650 } 3651 3652 auto tail = range.save; 3653 while (!scout.empty) 3654 { 3655 assert(!tail.empty); 3656 scout.popFront(); 3657 tail.popFront(); 3658 } 3659 3660 return tail.takeExactly(n); 3661 } 3662 } 3663 3664 /// 3665 pure @safe nothrow unittest 3666 { 3667 // tail -c n 3668 assert([1, 2, 3].tail(1) == [3]); 3669 assert([1, 2, 3].tail(2) == [2, 3]); 3670 assert([1, 2, 3].tail(3) == [1, 2, 3]); 3671 assert([1, 2, 3].tail(4) == [1, 2, 3]); 3672 assert([1, 2, 3].tail(0).length == 0); 3673 3674 // tail --lines=n 3675 import std.algorithm.comparison : equal; 3676 import std.algorithm.iteration : joiner; 3677 import std.exception : assumeWontThrow; 3678 import std.string : lineSplitter; 3679 assert("one\ntwo\nthree" 3680 .lineSplitter 3681 .tail(2) 3682 .joiner("\n") 3683 .equal("two\nthree") 3684 .assumeWontThrow); 3685 } 3686 3687 // @nogc prevented by https://issues.dlang.org/show_bug.cgi?id=15408 3688 pure nothrow @safe /+@nogc+/ unittest 3689 { 3690 import std.algorithm.comparison : equal; 3691 import std.internal.test.dummyrange : AllDummyRanges, DummyRange, Length, 3692 RangeType, ReturnBy; 3693 3694 static immutable cheatsheet = [6, 7, 8, 9, 10]; 3695 3696 foreach (R; AllDummyRanges) 3697 { 3698 static if (isInputRange!R && !isInfinite!R && 3699 (hasLength!R || isForwardRange!R)) 3700 { 3701 assert(R.init.tail(5).equal(cheatsheet)); 3702 static assert(R.init.tail(5).equal(cheatsheet)); 3703 3704 assert(R.init.tail(0).length == 0); 3705 assert(R.init.tail(10).equal(R.init)); 3706 assert(R.init.tail(11).equal(R.init)); 3707 } 3708 } 3709 3710 // Infinite ranges are not supported 3711 static assert(!__traits(compiles, repeat(0).tail(0))); 3712 3713 // Neither are non-forward ranges without length 3714 static assert(!__traits(compiles, DummyRange!(ReturnBy.Value, Length.No, 3715 RangeType.Input).init.tail(5))); 3716 } 3717 3718 pure @safe nothrow @nogc unittest 3719 { 3720 static immutable input = [1, 2, 3]; 3721 static immutable expectedOutput = [2, 3]; 3722 assert(input.tail(2) == expectedOutput); 3723 } 3724 3725 /++ 3726 Convenience function which calls 3727 $(REF popFrontN, std, range, primitives)`(range, n)` and returns `range`. 3728 `drop` makes it easier to pop elements from a range 3729 and then pass it to another function within a single expression, 3730 whereas `popFrontN` would require multiple statements. 3731 3732 `dropBack` provides the same functionality but instead calls 3733 $(REF popBackN, std, range, primitives)`(range, n)` 3734 3735 Note: `drop` and `dropBack` will only pop $(I up to) 3736 `n` elements but will stop if the range is empty first. 3737 In other languages this is sometimes called `skip`. 3738 3739 Params: 3740 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from 3741 n = the number of elements to drop 3742 3743 Returns: 3744 `range` with up to `n` elements dropped 3745 3746 See_Also: 3747 $(REF popFront, std, range, primitives), $(REF popBackN, std, range, primitives) 3748 +/ 3749 R drop(R)(R range, size_t n) 3750 if (isInputRange!R) 3751 { 3752 range.popFrontN(n); 3753 return range; 3754 } 3755 3756 /// 3757 @safe unittest 3758 { 3759 import std.algorithm.comparison : equal; 3760 3761 assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]); 3762 assert("hello world".drop(6) == "world"); 3763 assert("hello world".drop(50).empty); 3764 assert("hello world".take(6).drop(3).equal("lo ")); 3765 } 3766 3767 /// ditto 3768 R dropBack(R)(R range, size_t n) 3769 if (isBidirectionalRange!R) 3770 { 3771 range.popBackN(n); 3772 return range; 3773 } 3774 3775 /// 3776 @safe unittest 3777 { 3778 import std.algorithm.comparison : equal; 3779 3780 assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]); 3781 assert("hello world".dropBack(6) == "hello"); 3782 assert("hello world".dropBack(50).empty); 3783 assert("hello world".drop(4).dropBack(4).equal("o w")); 3784 } 3785 3786 @safe unittest 3787 { 3788 import std.algorithm.comparison : equal; 3789 import std.container.dlist : DList; 3790 3791 //Remove all but the first two elements 3792 auto a = DList!int(0, 1, 9, 9, 9, 9); 3793 a.remove(a[].drop(2)); 3794 assert(a[].equal(a[].take(2))); 3795 } 3796 3797 @safe unittest 3798 { 3799 import std.algorithm.comparison : equal; 3800 import std.algorithm.iteration : filter; 3801 3802 assert(drop("", 5).empty); 3803 assert(equal(drop(filter!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3])); 3804 } 3805 3806 @safe unittest 3807 { 3808 import std.algorithm.comparison : equal; 3809 import std.container.dlist : DList; 3810 3811 //insert before the last two elements 3812 auto a = DList!int(0, 1, 2, 5, 6); 3813 a.insertAfter(a[].dropBack(2), [3, 4]); 3814 assert(a[].equal(iota(0, 7))); 3815 } 3816 3817 /++ 3818 Similar to $(LREF drop) and `dropBack` but they call 3819 $(D range.$(LREF popFrontExactly)(n)) and `range.popBackExactly(n)` 3820 instead. 3821 3822 Note: Unlike `drop`, `dropExactly` will assume that the 3823 range holds at least `n` elements. This makes `dropExactly` 3824 faster than `drop`, but it also means that if `range` does 3825 not contain at least `n` elements, it will attempt to call `popFront` 3826 on an empty range, which is undefined behavior. So, only use 3827 `popFrontExactly` when it is guaranteed that `range` holds at least 3828 `n` elements. 3829 3830 Params: 3831 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to drop from 3832 n = the number of elements to drop 3833 3834 Returns: 3835 `range` with `n` elements dropped 3836 3837 See_Also: 3838 $(REF popFrontExcatly, std, range, primitives), 3839 $(REF popBackExcatly, std, range, primitives) 3840 +/ 3841 R dropExactly(R)(R range, size_t n) 3842 if (isInputRange!R) 3843 { 3844 popFrontExactly(range, n); 3845 return range; 3846 } 3847 /// ditto 3848 R dropBackExactly(R)(R range, size_t n) 3849 if (isBidirectionalRange!R) 3850 { 3851 popBackExactly(range, n); 3852 return range; 3853 } 3854 3855 /// 3856 @safe unittest 3857 { 3858 import std.algorithm.comparison : equal; 3859 import std.algorithm.iteration : filterBidirectional; 3860 3861 auto a = [1, 2, 3]; 3862 assert(a.dropExactly(2) == [3]); 3863 assert(a.dropBackExactly(2) == [1]); 3864 3865 string s = "日本語"; 3866 assert(s.dropExactly(2) == "語"); 3867 assert(s.dropBackExactly(2) == "日"); 3868 3869 auto bd = filterBidirectional!"true"([1, 2, 3]); 3870 assert(bd.dropExactly(2).equal([3])); 3871 assert(bd.dropBackExactly(2).equal([1])); 3872 } 3873 3874 /++ 3875 Convenience function which calls 3876 `range.popFront()` and returns `range`. `dropOne` 3877 makes it easier to pop an element from a range 3878 and then pass it to another function within a single expression, 3879 whereas `popFront` would require multiple statements. 3880 3881 `dropBackOne` provides the same functionality but instead calls 3882 `range.popBack()`. 3883 +/ 3884 R dropOne(R)(R range) 3885 if (isInputRange!R) 3886 { 3887 range.popFront(); 3888 return range; 3889 } 3890 /// ditto 3891 R dropBackOne(R)(R range) 3892 if (isBidirectionalRange!R) 3893 { 3894 range.popBack(); 3895 return range; 3896 } 3897 3898 /// 3899 pure @safe nothrow unittest 3900 { 3901 import std.algorithm.comparison : equal; 3902 import std.algorithm.iteration : filterBidirectional; 3903 import std.container.dlist : DList; 3904 3905 auto dl = DList!int(9, 1, 2, 3, 9); 3906 assert(dl[].dropOne().dropBackOne().equal([1, 2, 3])); 3907 3908 auto a = [1, 2, 3]; 3909 assert(a.dropOne() == [2, 3]); 3910 assert(a.dropBackOne() == [1, 2]); 3911 3912 string s = "日本語"; 3913 import std.exception : assumeWontThrow; 3914 assert(assumeWontThrow(s.dropOne() == "本語")); 3915 assert(assumeWontThrow(s.dropBackOne() == "日本")); 3916 3917 auto bd = filterBidirectional!"true"([1, 2, 3]); 3918 assert(bd.dropOne().equal([2, 3])); 3919 assert(bd.dropBackOne().equal([1, 2])); 3920 } 3921 3922 /** 3923 Create a range which repeats one value. 3924 3925 Params: 3926 value = the _value to repeat 3927 n = the number of times to repeat `value` 3928 3929 Returns: 3930 If `n` is not defined, an infinite random access range 3931 with slicing. 3932 3933 If `n` is defined, a random access range with slicing. 3934 */ 3935 struct Repeat(T) 3936 { 3937 private: 3938 import std.typecons : Rebindable2; 3939 3940 // Store a rebindable T to make Repeat assignable. 3941 Rebindable2!T _value; 3942 3943 public: 3944 /// Range primitives 3945 @property inout(T) front() inout { return _value.get; } 3946 3947 /// ditto 3948 @property inout(T) back() inout { return _value.get; } 3949 3950 /// ditto 3951 enum bool empty = false; 3952 3953 /// ditto 3954 void popFront() {} 3955 3956 /// ditto 3957 void popBack() {} 3958 3959 /// ditto 3960 @property auto save() inout { return this; } 3961 3962 /// ditto 3963 inout(T) opIndex(size_t) inout { return _value.get; } 3964 3965 /// ditto 3966 auto opSlice(size_t i, size_t j) 3967 in 3968 { 3969 assert( 3970 i <= j, 3971 "Attempting to slice a Repeat with a larger first argument than the second." 3972 ); 3973 } 3974 do 3975 { 3976 return this.takeExactly(j - i); 3977 } 3978 private static struct DollarToken {} 3979 3980 /// ditto 3981 enum opDollar = DollarToken.init; 3982 3983 /// ditto 3984 auto opSlice(size_t, DollarToken) inout { return this; } 3985 } 3986 3987 /// Ditto 3988 Repeat!T repeat(T)(T value) 3989 { 3990 import std.typecons : Rebindable2; 3991 3992 return Repeat!T(Rebindable2!T(value)); 3993 } 3994 3995 /// 3996 pure @safe nothrow unittest 3997 { 3998 import std.algorithm.comparison : equal; 3999 4000 assert(5.repeat().take(4).equal([5, 5, 5, 5])); 4001 } 4002 4003 pure @safe nothrow unittest 4004 { 4005 import std.algorithm.comparison : equal; 4006 4007 auto r = repeat(5); 4008 alias R = typeof(r); 4009 static assert(isBidirectionalRange!R); 4010 static assert(isForwardRange!R); 4011 static assert(isInfinite!R); 4012 static assert(hasSlicing!R); 4013 4014 assert(r.back == 5); 4015 assert(r.front == 5); 4016 assert(r.take(4).equal([ 5, 5, 5, 5 ])); 4017 assert(r[0 .. 4].equal([ 5, 5, 5, 5 ])); 4018 4019 R r2 = r[5 .. $]; 4020 assert(r2.back == 5); 4021 assert(r2.front == 5); 4022 } 4023 4024 /// ditto 4025 Take!(Repeat!T) repeat(T)(T value, size_t n) 4026 { 4027 return take(repeat(value), n); 4028 } 4029 4030 /// 4031 pure @safe nothrow unittest 4032 { 4033 import std.algorithm.comparison : equal; 4034 4035 assert(5.repeat(4).equal([5, 5, 5, 5])); 4036 } 4037 4038 // https://issues.dlang.org/show_bug.cgi?id=12007 4039 pure @safe nothrow unittest 4040 { 4041 static class C{} 4042 Repeat!(immutable int) ri; 4043 ri = ri.save; 4044 Repeat!(immutable C) rc; 4045 rc = rc.save; 4046 4047 import std.algorithm.setops : cartesianProduct; 4048 import std.algorithm.comparison : equal; 4049 import std.typecons : tuple; 4050 immutable int[] A = [1,2,3]; 4051 immutable int[] B = [4,5,6]; 4052 4053 assert(equal(cartesianProduct(A,B), 4054 [ 4055 tuple(1, 4), tuple(1, 5), tuple(1, 6), 4056 tuple(2, 4), tuple(2, 5), tuple(2, 6), 4057 tuple(3, 4), tuple(3, 5), tuple(3, 6), 4058 ])); 4059 } 4060 4061 /** 4062 Given callable ($(REF isCallable, std,traits)) `fun`, create as a range 4063 whose front is defined by successive calls to `fun()`. 4064 This is especially useful to call function with global side effects (random 4065 functions), or to create ranges expressed as a single delegate, rather than 4066 an entire `front`/`popFront`/`empty` structure. 4067 `fun` maybe be passed either a template alias parameter (existing 4068 function, delegate, struct type defining `static opCall`) or 4069 a run-time value argument (delegate, function object). 4070 The result range models an InputRange 4071 ($(REF isInputRange, std,range,primitives)). 4072 The resulting range will call `fun()` on construction, and every call to 4073 `popFront`, and the cached value will be returned when `front` is called. 4074 4075 Returns: an `inputRange` where each element represents another call to fun. 4076 */ 4077 auto generate(Fun)(Fun fun) 4078 if (isCallable!fun) 4079 { 4080 auto gen = Generator!(Fun)(fun); 4081 gen.popFront(); // prime the first element 4082 return gen; 4083 } 4084 4085 /// ditto 4086 auto generate(alias fun)() 4087 if (isCallable!fun) 4088 { 4089 auto gen = Generator!(fun)(); 4090 gen.popFront(); // prime the first element 4091 return gen; 4092 } 4093 4094 /// 4095 @safe pure nothrow unittest 4096 { 4097 import std.algorithm.comparison : equal; 4098 import std.algorithm.iteration : map; 4099 4100 int i = 1; 4101 auto powersOfTwo = generate!(() => i *= 2)().take(10); 4102 assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"())); 4103 } 4104 4105 /// 4106 @safe pure nothrow unittest 4107 { 4108 import std.algorithm.comparison : equal; 4109 4110 //Returns a run-time delegate 4111 auto infiniteIota(T)(T low, T high) 4112 { 4113 T i = high; 4114 return (){if (i == high) i = low; return i++;}; 4115 } 4116 //adapted as a range. 4117 assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1])); 4118 } 4119 4120 /// 4121 @safe unittest 4122 { 4123 import std.format : format; 4124 import std.random : uniform; 4125 4126 auto r = generate!(() => uniform(0, 6)).take(10); 4127 format("%(%s %)", r); 4128 } 4129 4130 private struct Generator(Fun...) 4131 { 4132 static assert(Fun.length == 1); 4133 static assert(isInputRange!Generator); 4134 import std.traits : FunctionAttribute, functionAttributes, ReturnType; 4135 4136 private: 4137 static if (is(Fun[0])) 4138 Fun[0] fun; 4139 else 4140 alias fun = Fun[0]; 4141 4142 enum returnByRef_ = (functionAttributes!fun & FunctionAttribute.ref_) ? true : false; 4143 4144 import std.traits : hasIndirections; 4145 static if (!hasIndirections!(ReturnType!fun)) 4146 alias RetType = Unqual!(ReturnType!fun); 4147 else 4148 alias RetType = ReturnType!fun; 4149 4150 static if (returnByRef_) 4151 RetType *elem_; 4152 else 4153 RetType elem_; 4154 public: 4155 /// Range primitives 4156 enum empty = false; 4157 4158 static if (returnByRef_) 4159 { 4160 /// ditto 4161 ref front() @property 4162 { 4163 return *elem_; 4164 } 4165 /// ditto 4166 void popFront() 4167 { 4168 elem_ = &fun(); 4169 } 4170 } 4171 else 4172 { 4173 /// ditto 4174 auto front() @property 4175 { 4176 return elem_; 4177 } 4178 /// ditto 4179 void popFront() 4180 { 4181 elem_ = fun(); 4182 } 4183 } 4184 } 4185 4186 @safe nothrow unittest 4187 { 4188 import std.algorithm.comparison : equal; 4189 4190 struct StaticOpCall 4191 { 4192 static ubyte opCall() { return 5 ; } 4193 } 4194 4195 assert(equal(generate!StaticOpCall().take(10), repeat(5).take(10))); 4196 } 4197 4198 @safe pure unittest 4199 { 4200 import std.algorithm.comparison : equal; 4201 4202 struct OpCall 4203 { 4204 ubyte opCall() @safe pure { return 5 ; } 4205 } 4206 4207 OpCall op; 4208 assert(equal(generate(op).take(10), repeat(5).take(10))); 4209 } 4210 4211 // verify ref mechanism works 4212 @system nothrow unittest 4213 { 4214 int[10] arr; 4215 int idx; 4216 4217 ref int fun() { 4218 auto x = idx++; 4219 idx %= arr.length; 4220 return arr[x]; 4221 } 4222 int y = 1; 4223 foreach (ref x; generate!(fun).take(20)) 4224 { 4225 x += y++; 4226 } 4227 import std.algorithm.comparison : equal; 4228 assert(equal(arr[], iota(12, 32, 2))); 4229 } 4230 4231 // assure front isn't the mechanism to make generate go to the next element. 4232 @safe unittest 4233 { 4234 int i; 4235 auto g = generate!(() => ++i); 4236 auto f = g.front; 4237 assert(f == g.front); 4238 g = g.drop(5); // reassign because generate caches 4239 assert(g.front == f + 5); 4240 } 4241 4242 // https://issues.dlang.org/show_bug.cgi?id=23319 4243 @safe pure nothrow unittest 4244 { 4245 auto b = generate!(() => const(int)(42)); 4246 assert(b.front == 42); 4247 } 4248 4249 /** 4250 Repeats the given forward range ad infinitum. If the original range is 4251 infinite (fact that would make `Cycle` the identity application), 4252 `Cycle` detects that and aliases itself to the range type 4253 itself. That works for non-forward ranges too. 4254 If the original range has random access, `Cycle` offers 4255 random access and also offers a constructor taking an initial position 4256 `index`. `Cycle` works with static arrays in addition to ranges, 4257 mostly for performance reasons. 4258 4259 Note: The input range must not be empty. 4260 4261 Tip: This is a great way to implement simple circular buffers. 4262 */ 4263 struct Cycle(R) 4264 if (isForwardRange!R && !isInfinite!R) 4265 { 4266 static if (isRandomAccessRange!R && hasLength!R) 4267 { 4268 private R _original; 4269 private size_t _index; 4270 4271 /// Range primitives 4272 this(R input, size_t index = 0) 4273 { 4274 _original = input; 4275 _index = index % _original.length; 4276 } 4277 4278 /// ditto 4279 @property auto ref front() 4280 { 4281 return _original[_index]; 4282 } 4283 4284 static if (is(typeof((cast(const R)_original)[_index]))) 4285 { 4286 /// ditto 4287 @property auto ref front() const 4288 { 4289 return _original[_index]; 4290 } 4291 } 4292 4293 static if (hasAssignableElements!R) 4294 { 4295 /// ditto 4296 @property void front(ElementType!R val) 4297 { 4298 import std.algorithm.mutation : move; 4299 4300 _original[_index] = move(val); 4301 } 4302 } 4303 4304 /// ditto 4305 enum bool empty = false; 4306 4307 /// ditto 4308 void popFront() 4309 { 4310 ++_index; 4311 if (_index >= _original.length) 4312 _index = 0; 4313 } 4314 4315 /// ditto 4316 auto ref opIndex(size_t n) 4317 { 4318 return _original[(n + _index) % _original.length]; 4319 } 4320 4321 static if (is(typeof((cast(const R)_original)[_index])) && 4322 is(typeof((cast(const R)_original).length))) 4323 { 4324 /// ditto 4325 auto ref opIndex(size_t n) const 4326 { 4327 return _original[(n + _index) % _original.length]; 4328 } 4329 } 4330 4331 static if (hasAssignableElements!R) 4332 { 4333 /// ditto 4334 void opIndexAssign(ElementType!R val, size_t n) 4335 { 4336 _original[(n + _index) % _original.length] = val; 4337 } 4338 } 4339 4340 /// ditto 4341 @property Cycle save() 4342 { 4343 //No need to call _original.save, because Cycle never actually modifies _original 4344 return Cycle(_original, _index); 4345 } 4346 4347 private static struct DollarToken {} 4348 4349 /// ditto 4350 enum opDollar = DollarToken.init; 4351 4352 static if (hasSlicing!R) 4353 { 4354 /// ditto 4355 auto opSlice(size_t i, size_t j) 4356 in 4357 { 4358 assert(i <= j); 4359 } 4360 do 4361 { 4362 return this[i .. $].takeExactly(j - i); 4363 } 4364 4365 /// ditto 4366 auto opSlice(size_t i, DollarToken) 4367 { 4368 return typeof(this)(_original, _index + i); 4369 } 4370 } 4371 } 4372 else 4373 { 4374 private R _original; 4375 private R _current; 4376 4377 /// ditto 4378 this(R input) 4379 { 4380 _original = input; 4381 _current = input.save; 4382 } 4383 4384 private this(R original, R current) 4385 { 4386 _original = original; 4387 _current = current; 4388 } 4389 4390 /// ditto 4391 @property auto ref front() 4392 { 4393 return _current.front; 4394 } 4395 4396 static if (is(typeof((cast(const R)_current).front))) 4397 { 4398 /// ditto 4399 @property auto ref front() const 4400 { 4401 return _current.front; 4402 } 4403 } 4404 4405 static if (hasAssignableElements!R) 4406 { 4407 /// ditto 4408 @property auto front(ElementType!R val) 4409 { 4410 import std.algorithm.mutation : move; 4411 4412 return _current.front = move(val); 4413 } 4414 } 4415 4416 /// ditto 4417 enum bool empty = false; 4418 4419 /// ditto 4420 void popFront() 4421 { 4422 _current.popFront(); 4423 if (_current.empty) 4424 _current = _original.save; 4425 } 4426 4427 /// ditto 4428 @property Cycle save() 4429 { 4430 //No need to call _original.save, because Cycle never actually modifies _original 4431 return Cycle(_original, _current.save); 4432 } 4433 } 4434 } 4435 4436 /// ditto 4437 template Cycle(R) 4438 if (isInfinite!R) 4439 { 4440 alias Cycle = R; 4441 } 4442 4443 /// ditto 4444 struct Cycle(R) 4445 if (isStaticArray!R) 4446 { 4447 private alias ElementType = typeof(R.init[0]); 4448 private ElementType* _ptr; 4449 private size_t _index; 4450 4451 nothrow: 4452 4453 /// Range primitives 4454 this(ref R input, size_t index = 0) @system 4455 { 4456 _ptr = input.ptr; 4457 _index = index % R.length; 4458 } 4459 4460 /// ditto 4461 @property ref inout(ElementType) front() inout @safe 4462 { 4463 static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted 4464 { 4465 return p[idx]; 4466 } 4467 return trustedPtrIdx(_ptr, _index); 4468 } 4469 4470 /// ditto 4471 enum bool empty = false; 4472 4473 /// ditto 4474 void popFront() @safe 4475 { 4476 ++_index; 4477 if (_index >= R.length) 4478 _index = 0; 4479 } 4480 4481 /// ditto 4482 ref inout(ElementType) opIndex(size_t n) inout @safe 4483 { 4484 static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted 4485 { 4486 return p[idx % R.length]; 4487 } 4488 return trustedPtrIdx(_ptr, n + _index); 4489 } 4490 4491 /// ditto 4492 @property inout(Cycle) save() inout @safe 4493 { 4494 return this; 4495 } 4496 4497 private static struct DollarToken {} 4498 /// ditto 4499 enum opDollar = DollarToken.init; 4500 4501 /// ditto 4502 auto opSlice(size_t i, size_t j) @safe 4503 in 4504 { 4505 assert( 4506 i <= j, 4507 "Attempting to slice a Repeat with a larger first argument than the second." 4508 ); 4509 } 4510 do 4511 { 4512 return this[i .. $].takeExactly(j - i); 4513 } 4514 4515 /// ditto 4516 inout(typeof(this)) opSlice(size_t i, DollarToken) inout @safe 4517 { 4518 static auto trustedCtor(typeof(_ptr) p, size_t idx) @trusted 4519 { 4520 return cast(inout) Cycle(*cast(R*)(p), idx); 4521 } 4522 return trustedCtor(_ptr, _index + i); 4523 } 4524 } 4525 4526 /// Ditto 4527 auto cycle(R)(R input) 4528 if (isInputRange!R) 4529 { 4530 static assert(isForwardRange!R || isInfinite!R, 4531 "Cycle requires a forward range argument unless it's statically known" 4532 ~ " to be infinite"); 4533 assert(!input.empty, "Attempting to pass an empty input to cycle"); 4534 static if (isInfinite!R) return input; 4535 else return Cycle!R(input); 4536 } 4537 4538 /// 4539 @safe unittest 4540 { 4541 import std.algorithm.comparison : equal; 4542 import std.range : cycle, take; 4543 4544 // Here we create an infinitive cyclic sequence from [1, 2] 4545 // (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then 4546 // take 5 elements of this sequence (so we have [1, 2, 1, 2, 1]) 4547 // and compare them with the expected values for equality. 4548 assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ])); 4549 } 4550 4551 /// Ditto 4552 Cycle!R cycle(R)(R input, size_t index = 0) 4553 if (isRandomAccessRange!R && !isInfinite!R) 4554 { 4555 assert(!input.empty, "Attempting to pass an empty input to cycle"); 4556 return Cycle!R(input, index); 4557 } 4558 4559 /// Ditto 4560 Cycle!R cycle(R)(ref R input, size_t index = 0) @system 4561 if (isStaticArray!R) 4562 { 4563 return Cycle!R(input, index); 4564 } 4565 4566 @safe nothrow unittest 4567 { 4568 import std.algorithm.comparison : equal; 4569 import std.internal.test.dummyrange : AllDummyRanges; 4570 4571 static assert(isForwardRange!(Cycle!(uint[]))); 4572 4573 // Make sure ref is getting propagated properly. 4574 int[] nums = [1,2,3]; 4575 auto c2 = cycle(nums); 4576 c2[3]++; 4577 assert(nums[0] == 2); 4578 4579 immutable int[] immarr = [1, 2, 3]; 4580 4581 foreach (DummyType; AllDummyRanges) 4582 { 4583 static if (isForwardRange!DummyType) 4584 { 4585 DummyType dummy; 4586 auto cy = cycle(dummy); 4587 static assert(isForwardRange!(typeof(cy))); 4588 auto t = take(cy, 20); 4589 assert(equal(t, [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10])); 4590 4591 const cRange = cy; 4592 assert(cRange.front == 1); 4593 4594 static if (hasAssignableElements!DummyType) 4595 { 4596 { 4597 cy.front = 66; 4598 scope(exit) cy.front = 1; 4599 assert(dummy.front == 66); 4600 } 4601 4602 static if (isRandomAccessRange!DummyType) 4603 { 4604 { 4605 cy[10] = 66; 4606 scope(exit) cy[10] = 1; 4607 assert(dummy.front == 66); 4608 } 4609 4610 assert(cRange[10] == 1); 4611 } 4612 } 4613 4614 static if (hasSlicing!DummyType) 4615 { 4616 auto slice = cy[5 .. 15]; 4617 assert(equal(slice, [6, 7, 8, 9, 10, 1, 2, 3, 4, 5])); 4618 static assert(is(typeof(slice) == typeof(takeExactly(cy, 5)))); 4619 4620 auto infSlice = cy[7 .. $]; 4621 assert(equal(take(infSlice, 5), [8, 9, 10, 1, 2])); 4622 static assert(isInfinite!(typeof(infSlice))); 4623 } 4624 } 4625 } 4626 } 4627 4628 @system nothrow unittest // For static arrays. 4629 { 4630 import std.algorithm.comparison : equal; 4631 4632 int[3] a = [ 1, 2, 3 ]; 4633 static assert(isStaticArray!(typeof(a))); 4634 auto c = cycle(a); 4635 assert(a.ptr == c._ptr); 4636 assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][])); 4637 static assert(isForwardRange!(typeof(c))); 4638 4639 // Test qualifiers on slicing. 4640 alias C = typeof(c); 4641 static assert(is(typeof(c[1 .. $]) == C)); 4642 const cConst = c; 4643 static assert(is(typeof(cConst[1 .. $]) == const(C))); 4644 } 4645 4646 @safe nothrow unittest // For infinite ranges 4647 { 4648 struct InfRange 4649 { 4650 void popFront() { } 4651 @property int front() { return 0; } 4652 enum empty = false; 4653 auto save() { return this; } 4654 } 4655 struct NonForwardInfRange 4656 { 4657 void popFront() { } 4658 @property int front() { return 0; } 4659 enum empty = false; 4660 } 4661 4662 InfRange i; 4663 NonForwardInfRange j; 4664 auto c = cycle(i); 4665 assert(c == i); 4666 //make sure it can alias out even non-forward infinite ranges 4667 static assert(is(typeof(j.cycle) == typeof(j))); 4668 } 4669 4670 @safe unittest 4671 { 4672 import std.algorithm.comparison : equal; 4673 4674 int[5] arr = [0, 1, 2, 3, 4]; 4675 auto cleD = cycle(arr[]); //Dynamic 4676 assert(equal(cleD[5 .. 10], arr[])); 4677 4678 //n is a multiple of 5 worth about 3/4 of size_t.max 4679 auto n = size_t.max/4 + size_t.max/2; 4680 n -= n % 5; 4681 4682 //Test index overflow 4683 foreach (_ ; 0 .. 10) 4684 { 4685 cleD = cleD[n .. $]; 4686 assert(equal(cleD[5 .. 10], arr[])); 4687 } 4688 } 4689 4690 @system @nogc nothrow unittest 4691 { 4692 import std.algorithm.comparison : equal; 4693 4694 int[5] arr = [0, 1, 2, 3, 4]; 4695 auto cleS = cycle(arr); //Static 4696 assert(equal(cleS[5 .. 10], arr[])); 4697 4698 //n is a multiple of 5 worth about 3/4 of size_t.max 4699 auto n = size_t.max/4 + size_t.max/2; 4700 n -= n % 5; 4701 4702 //Test index overflow 4703 foreach (_ ; 0 .. 10) 4704 { 4705 cleS = cleS[n .. $]; 4706 assert(equal(cleS[5 .. 10], arr[])); 4707 } 4708 } 4709 4710 @system unittest 4711 { 4712 import std.algorithm.comparison : equal; 4713 4714 int[1] arr = [0]; 4715 auto cleS = cycle(arr); 4716 cleS = cleS[10 .. $]; 4717 assert(equal(cleS[5 .. 10], 0.repeat(5))); 4718 assert(cleS.front == 0); 4719 } 4720 4721 // https://issues.dlang.org/show_bug.cgi?id=10845 4722 @system unittest 4723 { 4724 import std.algorithm.comparison : equal; 4725 import std.algorithm.iteration : filter; 4726 4727 auto a = inputRangeObject(iota(3).filter!"true"); 4728 assert(equal(cycle(a).take(10), [0, 1, 2, 0, 1, 2, 0, 1, 2, 0])); 4729 } 4730 4731 // https://issues.dlang.org/show_bug.cgi?id=12177 4732 @safe unittest 4733 { 4734 static assert(__traits(compiles, recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0"))); 4735 } 4736 4737 // https://issues.dlang.org/show_bug.cgi?id=13390 4738 @system unittest 4739 { 4740 import core.exception : AssertError; 4741 import std.exception : assertThrown; 4742 assertThrown!AssertError(cycle([0, 1, 2][0 .. 0])); 4743 } 4744 4745 // https://issues.dlang.org/show_bug.cgi?id=18657 4746 pure @safe unittest 4747 { 4748 import std.algorithm.comparison : equal; 4749 string s = "foo"; 4750 auto r = refRange(&s).cycle.take(4); 4751 assert(equal(r.save, "foof")); 4752 assert(equal(r.save, "foof")); 4753 } 4754 4755 private alias lengthType(R) = typeof(R.init.length.init); 4756 4757 /** 4758 Iterate several ranges in lockstep. The element type is a proxy tuple 4759 that allows accessing the current element in the `n`th range by 4760 using `e[n]`. 4761 4762 `zip` is similar to $(LREF lockstep), but `lockstep` doesn't 4763 bundle its elements and uses the `opApply` protocol. 4764 `lockstep` allows reference access to the elements in 4765 `foreach` iterations. 4766 4767 Params: 4768 sp = controls what `zip` will do if the ranges are different lengths 4769 ranges = the ranges to zip together 4770 Returns: 4771 At minimum, an input range. `Zip` offers the lowest range facilities 4772 of all components, e.g. it offers random access iff all ranges offer 4773 random access, and also offers mutation and swapping if all ranges offer 4774 it. Due to this, `Zip` is extremely powerful because it allows manipulating 4775 several ranges in lockstep. 4776 Throws: 4777 An `Exception` if all of the ranges are not the same length and 4778 `sp` is set to `StoppingPolicy.requireSameLength`. 4779 4780 Limitations: The `@nogc` and `nothrow` attributes cannot be inferred for 4781 the `Zip` struct because $(LREF StoppingPolicy) can vary at runtime. This 4782 limitation is not shared by the anonymous range returned by the `zip` 4783 function when not given an explicit `StoppingPolicy` as an argument. 4784 */ 4785 struct Zip(Ranges...) 4786 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 4787 { 4788 import std.format : format; //for generic mixins 4789 import std.typecons : Tuple; 4790 4791 alias R = Ranges; 4792 private R ranges; 4793 alias ElementType = Tuple!(staticMap!(.ElementType, R)); 4794 private StoppingPolicy stoppingPolicy = StoppingPolicy.shortest; 4795 4796 /** 4797 Builds an object. Usually this is invoked indirectly by using the 4798 $(LREF zip) function. 4799 */ 4800 this(R rs, StoppingPolicy s = StoppingPolicy.shortest) 4801 { 4802 ranges[] = rs[]; 4803 stoppingPolicy = s; 4804 } 4805 4806 /** 4807 Returns `true` if the range is at end. The test depends on the 4808 stopping policy. 4809 */ 4810 static if (allSatisfy!(isInfinite, R)) 4811 { 4812 // BUG: Doesn't propagate infiniteness if only some ranges are infinite 4813 // and s == StoppingPolicy.longest. This isn't fixable in the 4814 // current design since StoppingPolicy is known only at runtime. 4815 enum bool empty = false; 4816 } 4817 else 4818 { 4819 /// 4820 @property bool empty() 4821 { 4822 import std.exception : enforce; 4823 import std.meta : anySatisfy; 4824 4825 final switch (stoppingPolicy) 4826 { 4827 case StoppingPolicy.shortest: 4828 foreach (i, Unused; R) 4829 { 4830 if (ranges[i].empty) return true; 4831 } 4832 return false; 4833 case StoppingPolicy.longest: 4834 static if (anySatisfy!(isInfinite, R)) 4835 { 4836 return false; 4837 } 4838 else 4839 { 4840 foreach (i, Unused; R) 4841 { 4842 if (!ranges[i].empty) return false; 4843 } 4844 return true; 4845 } 4846 case StoppingPolicy.requireSameLength: 4847 foreach (i, Unused; R[1 .. $]) 4848 { 4849 enforce(ranges[0].empty == 4850 ranges[i + 1].empty, 4851 "Inequal-length ranges passed to Zip"); 4852 } 4853 return ranges[0].empty; 4854 } 4855 assert(false); 4856 } 4857 } 4858 4859 static if (allSatisfy!(isForwardRange, R)) 4860 { 4861 /// 4862 @property Zip save() 4863 { 4864 //Zip(ranges[0].save, ranges[1].save, ..., stoppingPolicy) 4865 return mixin (q{Zip(%(ranges[%s].save%|, %), stoppingPolicy)}.format(iota(0, R.length))); 4866 } 4867 } 4868 4869 private .ElementType!(R[i]) tryGetInit(size_t i)() 4870 { 4871 alias E = .ElementType!(R[i]); 4872 static if (!is(typeof({static E i;}))) 4873 throw new Exception("Range with non-default constructable elements exhausted."); 4874 else 4875 return E.init; 4876 } 4877 4878 /** 4879 Returns the current iterated element. 4880 */ 4881 @property ElementType front() 4882 { 4883 @property tryGetFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].front;} 4884 //ElementType(tryGetFront!0, tryGetFront!1, ...) 4885 return mixin(q{ElementType(%(tryGetFront!%s, %))}.format(iota(0, R.length))); 4886 } 4887 4888 /** 4889 Sets the front of all iterated ranges. 4890 */ 4891 static if (allSatisfy!(hasAssignableElements, R)) 4892 { 4893 @property void front(ElementType v) 4894 { 4895 foreach (i, Unused; R) 4896 { 4897 if (!ranges[i].empty) 4898 { 4899 ranges[i].front = v[i]; 4900 } 4901 } 4902 } 4903 } 4904 4905 /** 4906 Moves out the front. 4907 */ 4908 static if (allSatisfy!(hasMobileElements, R)) 4909 { 4910 ElementType moveFront() 4911 { 4912 @property tryMoveFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveFront();} 4913 //ElementType(tryMoveFront!0, tryMoveFront!1, ...) 4914 return mixin(q{ElementType(%(tryMoveFront!%s, %))}.format(iota(0, R.length))); 4915 } 4916 } 4917 4918 /** 4919 Returns the rightmost element. 4920 */ 4921 static if (allSatisfy!(isBidirectionalRange, R)) 4922 { 4923 @property ElementType back() 4924 { 4925 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness 4926 4927 @property tryGetBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].back;} 4928 //ElementType(tryGetBack!0, tryGetBack!1, ...) 4929 return mixin(q{ElementType(%(tryGetBack!%s, %))}.format(iota(0, R.length))); 4930 } 4931 4932 /** 4933 Moves out the back. 4934 */ 4935 static if (allSatisfy!(hasMobileElements, R)) 4936 { 4937 ElementType moveBack() 4938 { 4939 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness 4940 4941 @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].moveBack();} 4942 //ElementType(tryMoveBack!0, tryMoveBack!1, ...) 4943 return mixin(q{ElementType(%(tryMoveBack!%s, %))}.format(iota(0, R.length))); 4944 } 4945 } 4946 4947 /** 4948 Returns the current iterated element. 4949 */ 4950 static if (allSatisfy!(hasAssignableElements, R)) 4951 { 4952 @property void back(ElementType v) 4953 { 4954 //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness. 4955 //Not sure the call is even legal for StoppingPolicy.longest 4956 4957 foreach (i, Unused; R) 4958 { 4959 if (!ranges[i].empty) 4960 { 4961 ranges[i].back = v[i]; 4962 } 4963 } 4964 } 4965 } 4966 } 4967 4968 /** 4969 Advances to the next element in all controlled ranges. 4970 */ 4971 void popFront() 4972 { 4973 import std.exception : enforce; 4974 4975 final switch (stoppingPolicy) 4976 { 4977 case StoppingPolicy.shortest: 4978 foreach (i, Unused; R) 4979 { 4980 assert(!ranges[i].empty); 4981 ranges[i].popFront(); 4982 } 4983 break; 4984 case StoppingPolicy.longest: 4985 foreach (i, Unused; R) 4986 { 4987 if (!ranges[i].empty) ranges[i].popFront(); 4988 } 4989 break; 4990 case StoppingPolicy.requireSameLength: 4991 foreach (i, Unused; R) 4992 { 4993 enforce(!ranges[i].empty, "Invalid Zip object"); 4994 ranges[i].popFront(); 4995 } 4996 break; 4997 } 4998 } 4999 5000 /** 5001 Calls `popBack` for all controlled ranges. 5002 */ 5003 static if (allSatisfy!(isBidirectionalRange, R)) 5004 { 5005 void popBack() 5006 { 5007 //TODO: Fixme! In case of jaggedness, this is wrong. 5008 import std.exception : enforce; 5009 5010 final switch (stoppingPolicy) 5011 { 5012 case StoppingPolicy.shortest: 5013 foreach (i, Unused; R) 5014 { 5015 assert(!ranges[i].empty); 5016 ranges[i].popBack(); 5017 } 5018 break; 5019 case StoppingPolicy.longest: 5020 foreach (i, Unused; R) 5021 { 5022 if (!ranges[i].empty) ranges[i].popBack(); 5023 } 5024 break; 5025 case StoppingPolicy.requireSameLength: 5026 foreach (i, Unused; R) 5027 { 5028 enforce(!ranges[i].empty, "Invalid Zip object"); 5029 ranges[i].popBack(); 5030 } 5031 break; 5032 } 5033 } 5034 } 5035 5036 /** 5037 Returns the length of this range. Defined only if all ranges define 5038 `length`. 5039 */ 5040 static if (allSatisfy!(hasLength, R)) 5041 { 5042 @property auto length() 5043 { 5044 static if (Ranges.length == 1) 5045 return ranges[0].length; 5046 else 5047 { 5048 if (stoppingPolicy == StoppingPolicy.requireSameLength) 5049 return ranges[0].length; 5050 5051 //[min|max](ranges[0].length, ranges[1].length, ...) 5052 import std.algorithm.comparison : min, max; 5053 if (stoppingPolicy == StoppingPolicy.shortest) 5054 return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); 5055 else 5056 return mixin(q{max(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); 5057 } 5058 } 5059 5060 alias opDollar = length; 5061 } 5062 5063 /** 5064 Returns a slice of the range. Defined only if all range define 5065 slicing. 5066 */ 5067 static if (allSatisfy!(hasSlicing, R)) 5068 { 5069 auto opSlice(size_t from, size_t to) 5070 { 5071 //Slicing an infinite range yields the type Take!R 5072 //For finite ranges, the type Take!R aliases to R 5073 alias ZipResult = Zip!(staticMap!(Take, R)); 5074 5075 //ZipResult(ranges[0][from .. to], ranges[1][from .. to], ..., stoppingPolicy) 5076 return mixin (q{ZipResult(%(ranges[%s][from .. to]%|, %), stoppingPolicy)}.format(iota(0, R.length))); 5077 } 5078 } 5079 5080 /** 5081 Returns the `n`th element in the composite range. Defined if all 5082 ranges offer random access. 5083 */ 5084 static if (allSatisfy!(isRandomAccessRange, R)) 5085 { 5086 ElementType opIndex(size_t n) 5087 { 5088 //TODO: Fixme! This may create an out of bounds access 5089 //for StoppingPolicy.longest 5090 5091 //ElementType(ranges[0][n], ranges[1][n], ...) 5092 return mixin (q{ElementType(%(ranges[%s][n]%|, %))}.format(iota(0, R.length))); 5093 } 5094 5095 /** 5096 Assigns to the `n`th element in the composite range. Defined if 5097 all ranges offer random access. 5098 */ 5099 static if (allSatisfy!(hasAssignableElements, R)) 5100 { 5101 void opIndexAssign(ElementType v, size_t n) 5102 { 5103 //TODO: Fixme! Not sure the call is even legal for StoppingPolicy.longest 5104 foreach (i, Range; R) 5105 { 5106 ranges[i][n] = v[i]; 5107 } 5108 } 5109 } 5110 5111 /** 5112 Destructively reads the `n`th element in the composite 5113 range. Defined if all ranges offer random access. 5114 */ 5115 static if (allSatisfy!(hasMobileElements, R)) 5116 { 5117 ElementType moveAt(size_t n) 5118 { 5119 //TODO: Fixme! This may create an out of bounds access 5120 //for StoppingPolicy.longest 5121 5122 //ElementType(ranges[0].moveAt(n), ranges[1].moveAt(n), ..., ) 5123 return mixin (q{ElementType(%(ranges[%s].moveAt(n)%|, %))}.format(iota(0, R.length))); 5124 } 5125 } 5126 } 5127 } 5128 5129 /// Ditto 5130 auto zip(Ranges...)(Ranges ranges) 5131 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 5132 { 5133 import std.meta : anySatisfy, templateOr; 5134 static if (allSatisfy!(isInfinite, Ranges) || Ranges.length == 1) 5135 { 5136 return ZipShortest!(Ranges)(ranges); 5137 } 5138 else static if (allSatisfy!(isBidirectionalRange, Ranges)) 5139 { 5140 static if (allSatisfy!(templateOr!(isInfinite, hasLength), Ranges) 5141 && allSatisfy!(templateOr!(isInfinite, hasSlicing), Ranges) 5142 && allSatisfy!(isBidirectionalRange, staticMap!(Take, Ranges))) 5143 { 5144 // If all the ranges are bidirectional, if possible slice them to 5145 // the same length to simplify the implementation. 5146 static assert(anySatisfy!(hasLength, Ranges)); 5147 static foreach (i, Range; Ranges) 5148 static if (hasLength!Range) 5149 { 5150 static if (!is(typeof(minLen) == size_t)) 5151 size_t minLen = ranges[i].length; 5152 else 5153 {{ 5154 const x = ranges[i].length; 5155 if (x < minLen) minLen = x; 5156 }} 5157 } 5158 import std.format : format; 5159 static if (!anySatisfy!(isInfinite, Ranges)) 5160 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~ 5161 `(%(ranges[%s][0 .. minLen]%|, %))`.format(iota(0, Ranges.length))); 5162 else 5163 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))`~ 5164 `(%(take(ranges[%s], minLen)%|, %))`.format(iota(0, Ranges.length))); 5165 } 5166 else static if (allSatisfy!(isRandomAccessRange, Ranges)) 5167 { 5168 // We can't slice but we can still use random access to ensure 5169 // "back" is retrieving the same index for each range. 5170 return ZipShortest!(Ranges)(ranges); 5171 } 5172 else 5173 { 5174 // If bidirectional range operations would not be supported by 5175 // ZipShortest that might have actually been a bug since Zip 5176 // supported `back` without verifying that each range had the 5177 // same length, but for the sake of backwards compatibility 5178 // use the old Zip to continue supporting them. 5179 return Zip!Ranges(ranges); 5180 } 5181 } 5182 else 5183 { 5184 return ZipShortest!(Ranges)(ranges); 5185 } 5186 } 5187 5188 /// 5189 @nogc nothrow pure @safe unittest 5190 { 5191 import std.algorithm.comparison : equal; 5192 import std.algorithm.iteration : map; 5193 5194 // pairwise sum 5195 auto arr = only(0, 1, 2); 5196 auto part1 = zip(arr, arr.dropOne).map!"a[0] + a[1]"; 5197 assert(part1.equal(only(1, 3))); 5198 } 5199 5200 /// 5201 nothrow pure @safe unittest 5202 { 5203 import std.conv : to; 5204 5205 int[] a = [ 1, 2, 3 ]; 5206 string[] b = [ "a", "b", "c" ]; 5207 string[] result; 5208 5209 foreach (tup; zip(a, b)) 5210 { 5211 result ~= tup[0].to!string ~ tup[1]; 5212 } 5213 5214 assert(result == [ "1a", "2b", "3c" ]); 5215 5216 size_t idx = 0; 5217 // unpacking tuple elements with foreach 5218 foreach (e1, e2; zip(a, b)) 5219 { 5220 assert(e1 == a[idx]); 5221 assert(e2 == b[idx]); 5222 ++idx; 5223 } 5224 } 5225 5226 /// `zip` is powerful - the following code sorts two arrays in parallel: 5227 nothrow pure @safe unittest 5228 { 5229 import std.algorithm.sorting : sort; 5230 5231 int[] a = [ 1, 2, 3 ]; 5232 string[] b = [ "a", "c", "b" ]; 5233 zip(a, b).sort!((t1, t2) => t1[0] > t2[0]); 5234 5235 assert(a == [ 3, 2, 1 ]); 5236 // b is sorted according to a's sorting 5237 assert(b == [ "b", "c", "a" ]); 5238 } 5239 5240 /// Ditto 5241 auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges) 5242 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 5243 { 5244 return Zip!Ranges(ranges, sp); 5245 } 5246 5247 /** 5248 Dictates how iteration in a $(LREF zip) and $(LREF lockstep) should stop. 5249 By default stop at the end of the shortest of all ranges. 5250 */ 5251 enum StoppingPolicy 5252 { 5253 /// Stop when the shortest range is exhausted 5254 shortest, 5255 /// Stop when the longest range is exhausted 5256 longest, 5257 /// Require that all ranges are equal 5258 requireSameLength, 5259 } 5260 5261 /// 5262 pure @safe unittest 5263 { 5264 import std.algorithm.comparison : equal; 5265 import std.exception : assertThrown; 5266 import std.range.primitives; 5267 import std.typecons : tuple; 5268 5269 auto a = [1, 2, 3]; 5270 auto b = [4, 5, 6, 7]; 5271 5272 auto shortest = zip(StoppingPolicy.shortest, a, b); 5273 assert(shortest.equal([ 5274 tuple(1, 4), 5275 tuple(2, 5), 5276 tuple(3, 6) 5277 ])); 5278 5279 auto longest = zip(StoppingPolicy.longest, a, b); 5280 assert(longest.equal([ 5281 tuple(1, 4), 5282 tuple(2, 5), 5283 tuple(3, 6), 5284 tuple(0, 7) 5285 ])); 5286 5287 auto same = zip(StoppingPolicy.requireSameLength, a, b); 5288 same.popFrontN(3); 5289 assertThrown!Exception(same.popFront); 5290 } 5291 5292 /+ 5293 Non-public. Like $(LREF Zip) with `StoppingPolicy.shortest` 5294 except it properly implements `back` and `popBack` in the 5295 case of uneven ranges or disables those operations when 5296 it is not possible to guarantee they are correct. 5297 +/ 5298 package template ZipShortest(Ranges...) 5299 if (Ranges.length && __traits(compiles, 5300 { 5301 static assert(allSatisfy!(isInputRange, Ranges)); 5302 })) 5303 { 5304 alias ZipShortest = .ZipShortest!( 5305 Ranges.length == 1 || allSatisfy!(isInfinite, Ranges) 5306 ? Yes.allKnownSameLength 5307 : No.allKnownSameLength, 5308 Ranges); 5309 } 5310 /+ non-public, ditto +/ 5311 package struct ZipShortest(Flag!"allKnownSameLength" allKnownSameLength, Ranges...) 5312 if (Ranges.length && allSatisfy!(isInputRange, Ranges)) 5313 { 5314 import std.format : format; //for generic mixins 5315 import std.meta : anySatisfy, templateOr; 5316 import std.typecons : Tuple; 5317 5318 deprecated("Use of an undocumented alias R.") 5319 alias R = Ranges; // Unused here but defined in case library users rely on it. 5320 private Ranges ranges; 5321 alias ElementType = Tuple!(staticMap!(.ElementType, Ranges)); 5322 5323 /+ 5324 Builds an object. Usually this is invoked indirectly by using the 5325 $(LREF zip) function. 5326 +/ 5327 this(Ranges rs) 5328 { 5329 ranges[] = rs[]; 5330 } 5331 5332 /+ 5333 Returns `true` if the range is at end. 5334 +/ 5335 static if (allKnownSameLength ? anySatisfy!(isInfinite, Ranges) 5336 : allSatisfy!(isInfinite, Ranges)) 5337 { 5338 enum bool empty = false; 5339 } 5340 else 5341 { 5342 @property bool empty() 5343 { 5344 static if (allKnownSameLength) 5345 { 5346 return ranges[0].empty; 5347 } 5348 else 5349 { 5350 static foreach (i; 0 .. Ranges.length) 5351 { 5352 if (ranges[i].empty) 5353 return true; 5354 } 5355 return false; 5356 } 5357 } 5358 } 5359 5360 /+ 5361 Forward range primitive. Only present if each constituent range is a 5362 forward range. 5363 +/ 5364 static if (allSatisfy!(isForwardRange, Ranges)) 5365 @property typeof(this) save() 5366 { 5367 return mixin(`typeof(return)(%(ranges[%s].save%|, %))`.format(iota(0, Ranges.length))); 5368 } 5369 5370 /+ 5371 Returns the current iterated element. 5372 +/ 5373 @property ElementType front() 5374 { 5375 return mixin(`typeof(return)(%(ranges[%s].front%|, %))`.format(iota(0, Ranges.length))); 5376 } 5377 5378 /+ 5379 Sets the front of all iterated ranges. Only present if each constituent 5380 range has assignable elements. 5381 +/ 5382 static if (allSatisfy!(hasAssignableElements, Ranges)) 5383 @property void front()(ElementType v) 5384 { 5385 static foreach (i; 0 .. Ranges.length) 5386 ranges[i].front = v[i]; 5387 } 5388 5389 /+ 5390 Moves out the front. Present if each constituent range has mobile elements. 5391 +/ 5392 static if (allSatisfy!(hasMobileElements, Ranges)) 5393 ElementType moveFront()() 5394 { 5395 return mixin(`typeof(return)(%(ranges[%s].moveFront()%|, %))`.format(iota(0, Ranges.length))); 5396 } 5397 5398 private enum bool isBackWellDefined = allSatisfy!(isBidirectionalRange, Ranges) 5399 && (allKnownSameLength 5400 || allSatisfy!(isRandomAccessRange, Ranges) 5401 // Could also add the case where there is one non-infinite bidirectional 5402 // range that defines `length` and all others are infinite random access 5403 // ranges. Adding this would require appropriate branches in 5404 // back/moveBack/popBack. 5405 ); 5406 5407 /+ 5408 Returns the rightmost element. Present if all constituent ranges are 5409 bidirectional and either there is a compile-time guarantee that all 5410 ranges have the same length (in `allKnownSameLength`) or all ranges 5411 provide random access to elements. 5412 +/ 5413 static if (isBackWellDefined) 5414 @property ElementType back() 5415 { 5416 static if (allKnownSameLength) 5417 { 5418 return mixin(`typeof(return)(%(ranges[%s].back()%|, %))`.format(iota(0, Ranges.length))); 5419 } 5420 else 5421 { 5422 const backIndex = length - 1; 5423 return mixin(`typeof(return)(%(ranges[%s][backIndex]%|, %))`.format(iota(0, Ranges.length))); 5424 } 5425 } 5426 5427 /+ 5428 Moves out the back. Present if `back` is defined and 5429 each constituent range has mobile elements. 5430 +/ 5431 static if (isBackWellDefined && allSatisfy!(hasMobileElements, Ranges)) 5432 ElementType moveBack()() 5433 { 5434 static if (allKnownSameLength) 5435 { 5436 return mixin(`typeof(return)(%(ranges[%s].moveBack()%|, %))`.format(iota(0, Ranges.length))); 5437 } 5438 else 5439 { 5440 const backIndex = length - 1; 5441 return mixin(`typeof(return)(%(ranges[%s].moveAt(backIndex)%|, %))`.format(iota(0, Ranges.length))); 5442 } 5443 } 5444 5445 /+ 5446 Sets the rightmost element. Only present if `back` is defined and 5447 each constituent range has assignable elements. 5448 +/ 5449 static if (isBackWellDefined && allSatisfy!(hasAssignableElements, Ranges)) 5450 @property void back()(ElementType v) 5451 { 5452 static if (allKnownSameLength) 5453 { 5454 static foreach (i; 0 .. Ranges.length) 5455 ranges[i].back = v[i]; 5456 } 5457 else 5458 { 5459 const backIndex = length - 1; 5460 static foreach (i; 0 .. Ranges.length) 5461 ranges[i][backIndex] = v[i]; 5462 } 5463 } 5464 5465 /+ 5466 Calls `popFront` on each constituent range. 5467 +/ 5468 void popFront() 5469 { 5470 static foreach (i; 0 .. Ranges.length) 5471 ranges[i].popFront(); 5472 } 5473 5474 /+ 5475 Pops the rightmost element. Present if `back` is defined. 5476 +/ 5477 static if (isBackWellDefined) 5478 void popBack() 5479 { 5480 static if (allKnownSameLength) 5481 { 5482 static foreach (i; 0 .. Ranges.length) 5483 ranges[i].popBack; 5484 } 5485 else 5486 { 5487 const len = length; 5488 static foreach (i; 0 .. Ranges.length) 5489 static if (!isInfinite!(Ranges[i])) 5490 if (ranges[i].length == len) 5491 ranges[i].popBack(); 5492 } 5493 } 5494 5495 /+ 5496 Returns the length of this range. Defined if at least one 5497 constituent range defines `length` and the other ranges all also 5498 define `length` or are infinite, or if at least one constituent 5499 range defines `length` and there is a compile-time guarantee that 5500 all ranges have the same length (in `allKnownSameLength`). 5501 +/ 5502 static if (allKnownSameLength 5503 ? anySatisfy!(hasLength, Ranges) 5504 : (anySatisfy!(hasLength, Ranges) 5505 && allSatisfy!(templateOr!(isInfinite, hasLength), Ranges))) 5506 { 5507 @property size_t length() 5508 { 5509 static foreach (i, Range; Ranges) 5510 { 5511 static if (hasLength!Range) 5512 { 5513 static if (!is(typeof(minLen) == size_t)) 5514 size_t minLen = ranges[i].length; 5515 else static if (!allKnownSameLength) 5516 {{ 5517 const x = ranges[i].length; 5518 if (x < minLen) minLen = x; 5519 }} 5520 } 5521 } 5522 return minLen; 5523 } 5524 5525 alias opDollar = length; 5526 } 5527 5528 /+ 5529 Returns a slice of the range. Defined if all constituent ranges 5530 support slicing. 5531 +/ 5532 static if (allSatisfy!(hasSlicing, Ranges)) 5533 { 5534 // Note: we will know that all elements of the resultant range 5535 // will have the same length but we cannot change `allKnownSameLength` 5536 // because the `hasSlicing` predicate tests that the result returned 5537 // by `opSlice` has the same type as the receiver. 5538 auto opSlice()(size_t from, size_t to) 5539 { 5540 //(ranges[0][from .. to], ranges[1][from .. to], ...) 5541 enum sliceArgs = `(%(ranges[%s][from .. to]%|, %))`.format(iota(0, Ranges.length)); 5542 static if (__traits(compiles, mixin(`typeof(this)`~sliceArgs))) 5543 return mixin(`typeof(this)`~sliceArgs); 5544 else 5545 // The type is different anyway so we might as well 5546 // explicitly set allKnownSameLength. 5547 return mixin(`ZipShortest!(Yes.allKnownSameLength, staticMap!(Take, Ranges))` 5548 ~sliceArgs); 5549 } 5550 } 5551 5552 /+ 5553 Returns the `n`th element in the composite range. Defined if all 5554 constituent ranges offer random access. 5555 +/ 5556 static if (allSatisfy!(isRandomAccessRange, Ranges)) 5557 ElementType opIndex()(size_t n) 5558 { 5559 return mixin(`typeof(return)(%(ranges[%s][n]%|, %))`.format(iota(0, Ranges.length))); 5560 } 5561 5562 /+ 5563 Sets the `n`th element in the composite range. Defined if all 5564 constituent ranges offer random access and have assignable elements. 5565 +/ 5566 static if (allSatisfy!(isRandomAccessRange, Ranges) 5567 && allSatisfy!(hasAssignableElements, Ranges)) 5568 void opIndexAssign()(ElementType v, size_t n) 5569 { 5570 static foreach (i; 0 .. Ranges.length) 5571 ranges[i][n] = v[i]; 5572 } 5573 5574 /+ 5575 Destructively reads the `n`th element in the composite 5576 range. Defined if all constituent ranges offer random 5577 access and have mobile elements. 5578 +/ 5579 static if (allSatisfy!(isRandomAccessRange, Ranges) 5580 && allSatisfy!(hasMobileElements, Ranges)) 5581 ElementType moveAt()(size_t n) 5582 { 5583 return mixin(`typeof(return)(%(ranges[%s].moveAt(n)%|, %))`.format(iota(0, Ranges.length))); 5584 } 5585 } 5586 5587 pure @system unittest 5588 { 5589 import std.algorithm.comparison : equal; 5590 import std.algorithm.iteration : filter, map; 5591 import std.algorithm.mutation : swap; 5592 import std.algorithm.sorting : sort; 5593 5594 import std.exception : assertThrown, assertNotThrown; 5595 import std.typecons : tuple; 5596 5597 int[] a = [ 1, 2, 3 ]; 5598 float[] b = [ 1.0, 2.0, 3.0 ]; 5599 foreach (e; zip(a, b)) 5600 { 5601 assert(e[0] == e[1]); 5602 } 5603 5604 swap(a[0], a[1]); 5605 { 5606 auto z = zip(a, b); 5607 } 5608 //swap(z.front(), z.back()); 5609 sort!("a[0] < b[0]")(zip(a, b)); 5610 assert(a == [1, 2, 3]); 5611 assert(b == [2.0, 1.0, 3.0]); 5612 5613 auto z = zip(StoppingPolicy.requireSameLength, a, b); 5614 assertNotThrown(z.popBack()); 5615 assertNotThrown(z.popBack()); 5616 assertNotThrown(z.popBack()); 5617 assert(z.empty); 5618 assertThrown(z.popBack()); 5619 5620 a = [ 1, 2, 3 ]; 5621 b = [ 1.0, 2.0, 3.0 ]; 5622 sort!("a[0] > b[0]")(zip(StoppingPolicy.requireSameLength, a, b)); 5623 assert(a == [3, 2, 1]); 5624 assert(b == [3.0, 2.0, 1.0]); 5625 5626 a = []; 5627 b = []; 5628 assert(zip(StoppingPolicy.requireSameLength, a, b).empty); 5629 5630 // Test infiniteness propagation. 5631 static assert(isInfinite!(typeof(zip(repeat(1), repeat(1))))); 5632 5633 // Test stopping policies with both value and reference. 5634 auto a1 = [1, 2]; 5635 auto a2 = [1, 2, 3]; 5636 auto stuff = tuple(tuple(a1, a2), 5637 tuple(filter!"a"(a1), filter!"a"(a2))); 5638 5639 alias FOO = Zip!(immutable(int)[], immutable(float)[]); 5640 5641 foreach (t; stuff.expand) 5642 { 5643 auto arr1 = t[0]; 5644 auto arr2 = t[1]; 5645 auto zShortest = zip(arr1, arr2); 5646 assert(equal(map!"a[0]"(zShortest), [1, 2])); 5647 assert(equal(map!"a[1]"(zShortest), [1, 2])); 5648 5649 try { 5650 auto zSame = zip(StoppingPolicy.requireSameLength, arr1, arr2); 5651 foreach (elem; zSame) {} 5652 assert(0); 5653 } catch (Throwable) { /* It's supposed to throw.*/ } 5654 5655 auto zLongest = zip(StoppingPolicy.longest, arr1, arr2); 5656 assert(!zLongest.ranges[0].empty); 5657 assert(!zLongest.ranges[1].empty); 5658 5659 zLongest.popFront(); 5660 zLongest.popFront(); 5661 assert(!zLongest.empty); 5662 assert(zLongest.ranges[0].empty); 5663 assert(!zLongest.ranges[1].empty); 5664 5665 zLongest.popFront(); 5666 assert(zLongest.empty); 5667 } 5668 5669 // https://issues.dlang.org/show_bug.cgi?id=8900 5670 assert(zip([1, 2], repeat('a')).array == [tuple(1, 'a'), tuple(2, 'a')]); 5671 assert(zip(repeat('a'), [1, 2]).array == [tuple('a', 1), tuple('a', 2)]); 5672 5673 // https://issues.dlang.org/show_bug.cgi?id=18524 5674 // moveBack instead performs moveFront 5675 { 5676 auto r = zip([1,2,3]); 5677 assert(r.moveBack()[0] == 3); 5678 assert(r.moveFront()[0] == 1); 5679 } 5680 5681 // Doesn't work yet. Issues w/ emplace. 5682 // static assert(is(Zip!(immutable int[], immutable float[]))); 5683 5684 5685 // These unittests pass, but make the compiler consume an absurd amount 5686 // of RAM and time. Therefore, they should only be run if explicitly 5687 // uncommented when making changes to Zip. Also, running them using 5688 // make -fwin32.mak unittest makes the compiler completely run out of RAM. 5689 // You need to test just this module. 5690 /+ 5691 foreach (DummyType1; AllDummyRanges) 5692 { 5693 DummyType1 d1; 5694 foreach (DummyType2; AllDummyRanges) 5695 { 5696 DummyType2 d2; 5697 auto r = zip(d1, d2); 5698 assert(equal(map!"a[0]"(r), [1,2,3,4,5,6,7,8,9,10])); 5699 assert(equal(map!"a[1]"(r), [1,2,3,4,5,6,7,8,9,10])); 5700 5701 static if (isForwardRange!DummyType1 && isForwardRange!DummyType2) 5702 { 5703 static assert(isForwardRange!(typeof(r))); 5704 } 5705 5706 static if (isBidirectionalRange!DummyType1 && 5707 isBidirectionalRange!DummyType2) { 5708 static assert(isBidirectionalRange!(typeof(r))); 5709 } 5710 static if (isRandomAccessRange!DummyType1 && 5711 isRandomAccessRange!DummyType2) { 5712 static assert(isRandomAccessRange!(typeof(r))); 5713 } 5714 } 5715 } 5716 +/ 5717 } 5718 5719 nothrow pure @safe unittest 5720 { 5721 import std.algorithm.sorting : sort; 5722 5723 auto a = [5,4,3,2,1]; 5724 auto b = [3,1,2,5,6]; 5725 auto z = zip(a, b); 5726 5727 sort!"a[0] < b[0]"(z); 5728 5729 assert(a == [1, 2, 3, 4, 5]); 5730 assert(b == [6, 5, 2, 1, 3]); 5731 } 5732 5733 nothrow pure @safe unittest 5734 { 5735 import std.algorithm.comparison : equal; 5736 import std.typecons : tuple; 5737 5738 auto LL = iota(1L, 1000L); 5739 auto z = zip(LL, [4]); 5740 5741 assert(equal(z, [tuple(1L,4)])); 5742 5743 auto LL2 = iota(0L, 500L); 5744 auto z2 = zip([7], LL2); 5745 assert(equal(z2, [tuple(7, 0L)])); 5746 } 5747 5748 // Test for https://issues.dlang.org/show_bug.cgi?id=11196 5749 @safe pure unittest 5750 { 5751 import std.exception : assertThrown; 5752 5753 static struct S { @disable this(); } 5754 assert(zip((S[5]).init[]).length == 5); 5755 assert(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).length == 1); 5756 assertThrown(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).front); 5757 } 5758 5759 // https://issues.dlang.org/show_bug.cgi?id=12007 5760 @nogc nothrow @safe pure unittest 5761 { 5762 static struct R 5763 { 5764 enum empty = false; 5765 void popFront(){} 5766 int front(){return 1;} @property 5767 R save(){return this;} @property 5768 void opAssign(R) @disable; 5769 } 5770 R r; 5771 auto z = zip(r, r); 5772 assert(z.save == z); 5773 } 5774 5775 nothrow pure @system unittest 5776 { 5777 import std.typecons : tuple; 5778 5779 auto r1 = [0,1,2]; 5780 auto r2 = [1,2,3]; 5781 auto z1 = zip(refRange(&r1), refRange(&r2)); 5782 auto z2 = z1.save; 5783 z1.popFront(); 5784 assert(z1.front == tuple(1,2)); 5785 assert(z2.front == tuple(0,1)); 5786 } 5787 5788 @nogc nothrow pure @safe unittest 5789 { 5790 // Test zip's `back` and `length` with non-equal ranges. 5791 static struct NonSliceableRandomAccess 5792 { 5793 private int[] a; 5794 @property ref front() 5795 { 5796 return a.front; 5797 } 5798 @property ref back() 5799 { 5800 return a.back; 5801 } 5802 ref opIndex(size_t i) 5803 { 5804 return a[i]; 5805 } 5806 void popFront() 5807 { 5808 a.popFront(); 5809 } 5810 void popBack() 5811 { 5812 a.popBack(); 5813 } 5814 auto moveFront() 5815 { 5816 return a.moveFront(); 5817 } 5818 auto moveBack() 5819 { 5820 return a.moveBack(); 5821 } 5822 auto moveAt(size_t i) 5823 { 5824 return a.moveAt(i); 5825 } 5826 bool empty() const 5827 { 5828 return a.empty; 5829 } 5830 size_t length() const 5831 { 5832 return a.length; 5833 } 5834 typeof(this) save() 5835 { 5836 return this; 5837 } 5838 } 5839 static assert(isRandomAccessRange!NonSliceableRandomAccess); 5840 static assert(!hasSlicing!NonSliceableRandomAccess); 5841 static foreach (iteration; 0 .. 2) 5842 {{ 5843 int[5] data = [101, 102, 103, 201, 202]; 5844 static if (iteration == 0) 5845 { 5846 auto r1 = NonSliceableRandomAccess(data[0 .. 3]); 5847 auto r2 = NonSliceableRandomAccess(data[3 .. 5]); 5848 } 5849 else 5850 { 5851 auto r1 = data[0 .. 3]; 5852 auto r2 = data[3 .. 5]; 5853 } 5854 auto z = zip(r1, r2); 5855 static assert(isRandomAccessRange!(typeof(z))); 5856 assert(z.length == 2); 5857 assert(z.back[0] == 102 && z.back[1] == 202); 5858 z.back = typeof(z.back)(-102, -202);// Assign to back. 5859 assert(z.back[0] == -102 && z.back[1] == -202); 5860 z.popBack(); 5861 assert(z.length == 1); 5862 assert(z.back[0] == 101 && z.back[1] == 201); 5863 z.front = typeof(z.front)(-101, -201); 5864 assert(z.moveBack() == typeof(z.back)(-101, -201)); 5865 z.popBack(); 5866 assert(z.empty); 5867 }} 5868 } 5869 5870 @nogc nothrow pure @safe unittest 5871 { 5872 // Test opSlice on infinite `zip`. 5873 auto z = zip(repeat(1), repeat(2)); 5874 assert(hasSlicing!(typeof(z))); 5875 auto slice = z[10 .. 20]; 5876 assert(slice.length == 10); 5877 static assert(!is(typeof(z) == typeof(slice))); 5878 } 5879 5880 /* 5881 Generate lockstep's opApply function as a mixin string. 5882 If withIndex is true prepend a size_t index to the delegate. 5883 */ 5884 private string lockstepMixin(Ranges...)(bool withIndex, bool reverse) 5885 { 5886 import std.format : format; 5887 5888 string[] params; 5889 string[] emptyChecks; 5890 string[] dgArgs; 5891 string[] popFronts; 5892 string indexDef; 5893 string indexInc; 5894 5895 if (withIndex) 5896 { 5897 params ~= "size_t"; 5898 dgArgs ~= "index"; 5899 if (reverse) 5900 { 5901 indexDef = q{ 5902 size_t index = ranges[0].length-1; 5903 enforce(_stoppingPolicy == StoppingPolicy.requireSameLength, 5904 "lockstep can only be used with foreach_reverse when stoppingPolicy == requireSameLength"); 5905 5906 foreach (range; ranges[1..$]) 5907 enforce(range.length == ranges[0].length); 5908 }; 5909 indexInc = "--index;"; 5910 } 5911 else 5912 { 5913 indexDef = "size_t index = 0;"; 5914 indexInc = "++index;"; 5915 } 5916 } 5917 5918 foreach (idx, Range; Ranges) 5919 { 5920 params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); 5921 emptyChecks ~= format("!ranges[%s].empty", idx); 5922 if (reverse) 5923 { 5924 dgArgs ~= format("ranges[%s].back", idx); 5925 popFronts ~= format("ranges[%s].popBack();", idx); 5926 } 5927 else 5928 { 5929 dgArgs ~= format("ranges[%s].front", idx); 5930 popFronts ~= format("ranges[%s].popFront();", idx); 5931 } 5932 } 5933 5934 string name = reverse ? "opApplyReverse" : "opApply"; 5935 5936 return format( 5937 q{ 5938 int %s(scope int delegate(%s) dg) 5939 { 5940 import std.exception : enforce; 5941 5942 auto ranges = _ranges; 5943 int res; 5944 %s 5945 5946 while (%s) 5947 { 5948 res = dg(%s); 5949 if (res) break; 5950 %s 5951 %s 5952 } 5953 5954 if (_stoppingPolicy == StoppingPolicy.requireSameLength) 5955 { 5956 foreach (range; ranges) 5957 enforce(range.empty); 5958 } 5959 return res; 5960 } 5961 }, name, params.join(", "), indexDef, 5962 emptyChecks.join(" && "), dgArgs.join(", "), 5963 popFronts.join("\n "), 5964 indexInc); 5965 } 5966 5967 /** 5968 Iterate multiple ranges in lockstep using a `foreach` loop. In contrast to 5969 $(LREF zip) it allows reference access to its elements. If only a single 5970 range is passed in, the `Lockstep` aliases itself away. If the 5971 ranges are of different lengths and `s` == `StoppingPolicy.shortest` 5972 stop after the shortest range is empty. If the ranges are of different 5973 lengths and `s` == `StoppingPolicy.requireSameLength`, throw an 5974 exception. `s` may not be `StoppingPolicy.longest`, and passing this 5975 will throw an exception. 5976 5977 Iterating over `Lockstep` in reverse and with an index is only possible 5978 when `s` == `StoppingPolicy.requireSameLength`, in order to preserve 5979 indexes. If an attempt is made at iterating in reverse when `s` == 5980 `StoppingPolicy.shortest`, an exception will be thrown. 5981 5982 By default `StoppingPolicy` is set to `StoppingPolicy.shortest`. 5983 5984 Limitations: The `pure`, `@safe`, `@nogc`, or `nothrow` attributes cannot be 5985 inferred for `lockstep` iteration. $(LREF zip) can infer the first two due to 5986 a different implementation. 5987 5988 See_Also: $(LREF zip) 5989 5990 `lockstep` is similar to $(LREF zip), but `zip` bundles its 5991 elements and returns a range. 5992 `lockstep` also supports reference access. 5993 Use `zip` if you want to pass the result to a range function. 5994 */ 5995 struct Lockstep(Ranges...) 5996 if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges)) 5997 { 5998 /// 5999 this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest) 6000 { 6001 import std.exception : enforce; 6002 6003 _ranges = ranges; 6004 enforce(sp != StoppingPolicy.longest, 6005 "Can't use StoppingPolicy.Longest on Lockstep."); 6006 _stoppingPolicy = sp; 6007 } 6008 6009 mixin(lockstepMixin!Ranges(false, false)); 6010 mixin(lockstepMixin!Ranges(true, false)); 6011 static if (allSatisfy!(isBidirectionalRange, Ranges)) 6012 { 6013 mixin(lockstepMixin!Ranges(false, true)); 6014 static if (allSatisfy!(hasLength, Ranges)) 6015 { 6016 mixin(lockstepMixin!Ranges(true, true)); 6017 } 6018 else 6019 { 6020 mixin(lockstepReverseFailMixin!Ranges(true)); 6021 } 6022 } 6023 else 6024 { 6025 mixin(lockstepReverseFailMixin!Ranges(false)); 6026 mixin(lockstepReverseFailMixin!Ranges(true)); 6027 } 6028 6029 private: 6030 alias R = Ranges; 6031 R _ranges; 6032 StoppingPolicy _stoppingPolicy; 6033 } 6034 6035 /// Ditto 6036 Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges) 6037 if (allSatisfy!(isInputRange, Ranges)) 6038 { 6039 return Lockstep!(Ranges)(ranges); 6040 } 6041 /// Ditto 6042 Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) 6043 if (allSatisfy!(isInputRange, Ranges)) 6044 { 6045 static if (Ranges.length > 1) 6046 return Lockstep!Ranges(ranges, s); 6047 else 6048 return ranges[0]; 6049 } 6050 6051 /// 6052 @system unittest 6053 { 6054 auto arr1 = [1,2,3,4,5,100]; 6055 auto arr2 = [6,7,8,9,10]; 6056 6057 foreach (ref a, b; lockstep(arr1, arr2)) 6058 { 6059 a += b; 6060 } 6061 6062 assert(arr1 == [7,9,11,13,15,100]); 6063 6064 /// Lockstep also supports iterating with an index variable: 6065 foreach (index, a, b; lockstep(arr1, arr2)) 6066 { 6067 assert(arr1[index] == a); 6068 assert(arr2[index] == b); 6069 } 6070 } 6071 6072 // https://issues.dlang.org/show_bug.cgi?id=15860: foreach_reverse on lockstep 6073 @system unittest 6074 { 6075 auto arr1 = [0, 1, 2, 3]; 6076 auto arr2 = [4, 5, 6, 7]; 6077 6078 size_t n = arr1.length -1; 6079 foreach_reverse (index, a, b; lockstep(arr1, arr2, StoppingPolicy.requireSameLength)) 6080 { 6081 assert(n == index); 6082 assert(index == a); 6083 assert(arr1[index] == a); 6084 assert(arr2[index] == b); 6085 n--; 6086 } 6087 6088 auto arr3 = [4, 5]; 6089 n = 1; 6090 foreach_reverse (a, b; lockstep(arr1, arr3)) 6091 { 6092 assert(a == arr1[$-n] && b == arr3[$-n]); 6093 n++; 6094 } 6095 } 6096 6097 @system unittest 6098 { 6099 import std.algorithm.iteration : filter; 6100 import std.conv : to; 6101 6102 // The filters are to make these the lowest common forward denominator ranges, 6103 // i.e. w/o ref return, random access, length, etc. 6104 auto foo = filter!"a"([1,2,3,4,5]); 6105 immutable bar = [6f,7f,8f,9f,10f].idup; 6106 auto l = lockstep(foo, bar); 6107 6108 // Should work twice. These are forward ranges with implicit save. 6109 foreach (i; 0 .. 2) 6110 { 6111 uint[] res1; 6112 float[] res2; 6113 6114 foreach (a, ref b; l) 6115 { 6116 res1 ~= a; 6117 res2 ~= b; 6118 } 6119 6120 assert(res1 == [1,2,3,4,5]); 6121 assert(res2 == [6,7,8,9,10]); 6122 assert(bar == [6f,7f,8f,9f,10f]); 6123 } 6124 6125 // Doc example. 6126 auto arr1 = [1,2,3,4,5]; 6127 auto arr2 = [6,7,8,9,10]; 6128 6129 foreach (ref a, ref b; lockstep(arr1, arr2)) 6130 { 6131 a += b; 6132 } 6133 6134 assert(arr1 == [7,9,11,13,15]); 6135 6136 // Make sure StoppingPolicy.requireSameLength doesn't throw. 6137 auto ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); 6138 6139 int k = 1; 6140 foreach (a, b; ls) 6141 { 6142 assert(a - b == k); 6143 ++k; 6144 } 6145 6146 // Make sure StoppingPolicy.requireSameLength throws. 6147 arr2.popBack(); 6148 ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength); 6149 6150 try { 6151 foreach (a, b; ls) {} 6152 assert(0); 6153 } catch (Exception) {} 6154 6155 // Just make sure 1-range case instantiates. This hangs the compiler 6156 // when no explicit stopping policy is specified due to 6157 // https://issues.dlang.org/show_bug.cgi?id=4652 6158 auto stuff = lockstep([1,2,3,4,5], StoppingPolicy.shortest); 6159 foreach (i, a; stuff) 6160 { 6161 assert(stuff[i] == a); 6162 } 6163 6164 // Test with indexing. 6165 uint[] res1; 6166 float[] res2; 6167 size_t[] indices; 6168 foreach (i, a, b; lockstep(foo, bar)) 6169 { 6170 indices ~= i; 6171 res1 ~= a; 6172 res2 ~= b; 6173 } 6174 6175 assert(indices == to!(size_t[])([0, 1, 2, 3, 4])); 6176 assert(res1 == [1,2,3,4,5]); 6177 assert(res2 == [6f,7f,8f,9f,10f]); 6178 6179 // Make sure we've worked around the relevant compiler bugs and this at least 6180 // compiles w/ >2 ranges. 6181 lockstep(foo, foo, foo); 6182 6183 // Make sure it works with const. 6184 const(int[])[] foo2 = [[1, 2, 3]]; 6185 const(int[])[] bar2 = [[4, 5, 6]]; 6186 auto c = chain(foo2, bar2); 6187 6188 foreach (f, b; lockstep(c, c)) {} 6189 6190 // Regression 10468 6191 foreach (x, y; lockstep(iota(0, 10), iota(0, 10))) { } 6192 } 6193 6194 @system unittest 6195 { 6196 struct RvalueRange 6197 { 6198 int[] impl; 6199 @property bool empty() { return impl.empty; } 6200 @property int front() { return impl[0]; } // N.B. non-ref 6201 void popFront() { impl.popFront(); } 6202 } 6203 auto data1 = [ 1, 2, 3, 4 ]; 6204 auto data2 = [ 5, 6, 7, 8 ]; 6205 auto r1 = RvalueRange(data1); 6206 auto r2 = data2; 6207 foreach (a, ref b; lockstep(r1, r2)) 6208 { 6209 a++; 6210 b++; 6211 } 6212 assert(data1 == [ 1, 2, 3, 4 ]); // changes to a do not propagate to data 6213 assert(data2 == [ 6, 7, 8, 9 ]); // but changes to b do. 6214 6215 // Since r1 is by-value only, the compiler should reject attempts to 6216 // foreach over it with ref. 6217 static assert(!__traits(compiles, { 6218 foreach (ref a, ref b; lockstep(r1, r2)) { a++; } 6219 })); 6220 } 6221 6222 private string lockstepReverseFailMixin(Ranges...)(bool withIndex) 6223 { 6224 import std.format : format; 6225 string[] params; 6226 string message; 6227 6228 if (withIndex) 6229 { 6230 message = "Indexed reverse iteration with lockstep is only supported" 6231 ~"if all ranges are bidirectional and have a length.\n"; 6232 } 6233 else 6234 { 6235 message = "Reverse iteration with lockstep is only supported if all ranges are bidirectional.\n"; 6236 } 6237 6238 if (withIndex) 6239 { 6240 params ~= "size_t"; 6241 } 6242 6243 foreach (idx, Range; Ranges) 6244 { 6245 params ~= format("%sElementType!(Ranges[%s])", hasLvalueElements!Range ? "ref " : "", idx); 6246 } 6247 6248 return format( 6249 q{ 6250 int opApplyReverse()(scope int delegate(%s) dg) 6251 { 6252 static assert(false, "%s"); 6253 } 6254 }, params.join(", "), message); 6255 } 6256 6257 // For generic programming, make sure Lockstep!(Range) is well defined for a 6258 // single range. 6259 template Lockstep(Range) 6260 { 6261 alias Lockstep = Range; 6262 } 6263 6264 /** 6265 Creates a mathematical sequence given the initial values and a 6266 recurrence function that computes the next value from the existing 6267 values. The sequence comes in the form of an infinite forward 6268 range. The type `Recurrence` itself is seldom used directly; most 6269 often, recurrences are obtained by calling the function $(D 6270 recurrence). 6271 6272 When calling `recurrence`, the function that computes the next 6273 value is specified as a template argument, and the initial values in 6274 the recurrence are passed as regular arguments. For example, in a 6275 Fibonacci sequence, there are two initial values (and therefore a 6276 state size of 2) because computing the next Fibonacci value needs the 6277 past two values. 6278 6279 The signature of this function should be: 6280 ---- 6281 auto fun(R)(R state, size_t n) 6282 ---- 6283 where `n` will be the index of the current value, and `state` will be an 6284 opaque state vector that can be indexed with array-indexing notation 6285 `state[i]`, where valid values of `i` range from $(D (n - 1)) to 6286 $(D (n - State.length)). 6287 6288 If the function is passed in string form, the state has name `"a"` 6289 and the zero-based index in the recurrence has name `"n"`. The 6290 given string must return the desired value for `a[n]` given 6291 `a[n - 1]`, `a[n - 2]`, `a[n - 3]`,..., `a[n - stateSize]`. The 6292 state size is dictated by the number of arguments passed to the call 6293 to `recurrence`. The `Recurrence` struct itself takes care of 6294 managing the recurrence's state and shifting it appropriately. 6295 */ 6296 struct Recurrence(alias fun, StateType, size_t stateSize) 6297 { 6298 import std.functional : binaryFun; 6299 6300 StateType[stateSize] _state; 6301 size_t _n; 6302 6303 this(StateType[stateSize] initial) { _state = initial; } 6304 6305 void popFront() 6306 { 6307 static auto trustedCycle(ref typeof(_state) s) @trusted 6308 { 6309 return cycle(s); 6310 } 6311 // The cast here is reasonable because fun may cause integer 6312 // promotion, but needs to return a StateType to make its operation 6313 // closed. Therefore, we have no other choice. 6314 _state[_n % stateSize] = cast(StateType) binaryFun!(fun, "a", "n")( 6315 trustedCycle(_state), _n + stateSize); 6316 ++_n; 6317 } 6318 6319 @property StateType front() 6320 { 6321 return _state[_n % stateSize]; 6322 } 6323 6324 @property typeof(this) save() 6325 { 6326 return this; 6327 } 6328 6329 enum bool empty = false; 6330 } 6331 6332 /// 6333 pure @safe nothrow unittest 6334 { 6335 import std.algorithm.comparison : equal; 6336 6337 // The Fibonacci numbers, using function in string form: 6338 // a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n] 6339 auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); 6340 assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55])); 6341 6342 // The factorials, using function in lambda form: 6343 auto fac = recurrence!((a,n) => a[n-1] * n)(1); 6344 assert(take(fac, 10).equal([ 6345 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 6346 ])); 6347 6348 // The triangular numbers, using function in explicit form: 6349 static size_t genTriangular(R)(R state, size_t n) 6350 { 6351 return state[n-1] + n; 6352 } 6353 auto tri = recurrence!genTriangular(0); 6354 assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45])); 6355 } 6356 6357 /// Ditto 6358 Recurrence!(fun, CommonType!(State), State.length) 6359 recurrence(alias fun, State...)(State initial) 6360 { 6361 CommonType!(State)[State.length] state; 6362 foreach (i, Unused; State) 6363 { 6364 state[i] = initial[i]; 6365 } 6366 return typeof(return)(state); 6367 } 6368 6369 pure @safe nothrow unittest 6370 { 6371 import std.algorithm.comparison : equal; 6372 6373 auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1); 6374 static assert(isForwardRange!(typeof(fib))); 6375 6376 int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]; 6377 assert(equal(take(fib, 10), witness)); 6378 foreach (e; take(fib, 10)) {} 6379 auto fact = recurrence!("n * a[n-1]")(1); 6380 assert( equal(take(fact, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6, 6381 2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) ); 6382 auto piapprox = recurrence!("a[n] + (n & 1 ? 4.0 : -4.0) / (2 * n + 3)")(4.0); 6383 foreach (e; take(piapprox, 20)) {} 6384 // Thanks to yebblies for this test and the associated fix 6385 auto r = recurrence!"a[n-2]"(1, 2); 6386 witness = [1, 2, 1, 2, 1]; 6387 assert(equal(take(r, 5), witness)); 6388 } 6389 6390 /** 6391 `Sequence` is similar to `Recurrence` except that iteration is 6392 presented in the so-called $(HTTP en.wikipedia.org/wiki/Closed_form, 6393 closed form). This means that the `n`th element in the series is 6394 computable directly from the initial values and `n` itself. This 6395 implies that the interface offered by `Sequence` is a random-access 6396 range, as opposed to the regular `Recurrence`, which only offers 6397 forward iteration. 6398 6399 The state of the sequence is stored as a `Tuple` so it can be 6400 heterogeneous. 6401 */ 6402 struct Sequence(alias fun, State) 6403 { 6404 private: 6405 import std.functional : binaryFun; 6406 6407 alias compute = binaryFun!(fun, "a", "n"); 6408 alias ElementType = typeof(compute(State.init, cast(size_t) 1)); 6409 State _state; 6410 size_t _n; 6411 6412 static struct DollarToken{} 6413 6414 public: 6415 this(State initial, size_t n = 0) 6416 { 6417 _state = initial; 6418 _n = n; 6419 } 6420 6421 @property ElementType front() 6422 { 6423 return compute(_state, _n); 6424 } 6425 6426 void popFront() 6427 { 6428 ++_n; 6429 } 6430 6431 enum opDollar = DollarToken(); 6432 6433 auto opSlice(size_t lower, size_t upper) 6434 in 6435 { 6436 assert( 6437 upper >= lower, 6438 "Attempting to slice a Sequence with a larger first argument than the second." 6439 ); 6440 } 6441 do 6442 { 6443 return typeof(this)(_state, _n + lower).take(upper - lower); 6444 } 6445 6446 auto opSlice(size_t lower, DollarToken) 6447 { 6448 return typeof(this)(_state, _n + lower); 6449 } 6450 6451 ElementType opIndex(size_t n) 6452 { 6453 return compute(_state, n + _n); 6454 } 6455 6456 enum bool empty = false; 6457 6458 @property Sequence save() { return this; } 6459 } 6460 6461 /// Ditto 6462 auto sequence(alias fun, State...)(State args) 6463 { 6464 import std.typecons : Tuple, tuple; 6465 alias Return = Sequence!(fun, Tuple!State); 6466 return Return(tuple(args)); 6467 } 6468 6469 /// Odd numbers, using function in string form: 6470 pure @safe nothrow @nogc unittest 6471 { 6472 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 6473 assert(odds.front == 1); 6474 odds.popFront(); 6475 assert(odds.front == 3); 6476 odds.popFront(); 6477 assert(odds.front == 5); 6478 } 6479 6480 /// Triangular numbers, using function in lambda form: 6481 pure @safe nothrow @nogc unittest 6482 { 6483 auto tri = sequence!((a,n) => n*(n+1)/2)(); 6484 6485 // Note random access 6486 assert(tri[0] == 0); 6487 assert(tri[3] == 6); 6488 assert(tri[1] == 1); 6489 assert(tri[4] == 10); 6490 assert(tri[2] == 3); 6491 } 6492 6493 /// Fibonacci numbers, using function in explicit form: 6494 @safe nothrow @nogc unittest 6495 { 6496 import std.math.exponential : pow; 6497 import std.math.rounding : round; 6498 import std.math.algebraic : sqrt; 6499 static ulong computeFib(S)(S state, size_t n) 6500 { 6501 // Binet's formula 6502 return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) / 6503 state[2])); 6504 } 6505 auto fib = sequence!computeFib( 6506 (1.0 + sqrt(5.0)) / 2.0, // Golden Ratio 6507 (1.0 - sqrt(5.0)) / 2.0, // Conjugate of Golden Ratio 6508 sqrt(5.0)); 6509 6510 // Note random access with [] operator 6511 assert(fib[1] == 1); 6512 assert(fib[4] == 5); 6513 assert(fib[3] == 3); 6514 assert(fib[2] == 2); 6515 assert(fib[9] == 55); 6516 } 6517 6518 pure @safe nothrow @nogc unittest 6519 { 6520 import std.typecons : Tuple, tuple; 6521 auto y = Sequence!("a[0] + n * a[1]", Tuple!(int, int))(tuple(0, 4)); 6522 static assert(isForwardRange!(typeof(y))); 6523 6524 //@@BUG 6525 //auto y = sequence!("a[0] + n * a[1]")(0, 4); 6526 //foreach (e; take(y, 15)) 6527 {} //writeln(e); 6528 6529 auto odds = Sequence!("a[0] + n * a[1]", Tuple!(int, int))( 6530 tuple(1, 2)); 6531 for (int currentOdd = 1; currentOdd <= 21; currentOdd += 2) 6532 { 6533 assert(odds.front == odds[0]); 6534 assert(odds[0] == currentOdd); 6535 odds.popFront(); 6536 } 6537 } 6538 6539 pure @safe nothrow @nogc unittest 6540 { 6541 import std.algorithm.comparison : equal; 6542 6543 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 6544 static assert(hasSlicing!(typeof(odds))); 6545 6546 //Note: don't use drop or take as the target of an equal, 6547 //since they'll both just forward to opSlice, making the tests irrelevant 6548 6549 // static slicing tests 6550 assert(equal(odds[0 .. 5], only(1, 3, 5, 7, 9))); 6551 assert(equal(odds[3 .. 7], only(7, 9, 11, 13))); 6552 6553 // relative slicing test, testing slicing is NOT agnostic of state 6554 auto odds_less5 = odds.drop(5); //this should actually call odds[5 .. $] 6555 assert(equal(odds_less5[0 .. 3], only(11, 13, 15))); 6556 assert(equal(odds_less5[0 .. 10], odds[5 .. 15])); 6557 6558 //Infinite slicing tests 6559 odds = odds[10 .. $]; 6560 assert(equal(odds.take(3), only(21, 23, 25))); 6561 } 6562 6563 // https://issues.dlang.org/show_bug.cgi?id=5036 6564 pure @safe nothrow unittest 6565 { 6566 auto s = sequence!((a, n) => new int)(0); 6567 assert(s.front != s.front); // no caching 6568 } 6569 6570 // iota 6571 /** 6572 Creates a range of values that span the given starting and stopping 6573 values. 6574 6575 Params: 6576 begin = The starting value. 6577 end = The value that serves as the stopping criterion. This value is not 6578 included in the range. 6579 step = The value to add to the current value at each iteration. 6580 6581 Returns: 6582 A range that goes through the numbers `begin`, $(D begin + step), 6583 $(D begin + 2 * step), `...`, up to and excluding `end`. 6584 6585 The two-argument overloads have $(D step = 1). If $(D begin < end && step < 6586 0) or $(D begin > end && step > 0) or $(D begin == end), then an empty range 6587 is returned. If $(D step == 0) then $(D begin == end) is an error. 6588 6589 For built-in types, the range returned is a random access range. For 6590 user-defined types that support `++`, the range is an input 6591 range. 6592 6593 An integral iota also supports `in` operator from the right. It takes 6594 the stepping into account, the integral won't be considered 6595 contained if it falls between two consecutive values of the range. 6596 `contains` does the same as in, but from lefthand side. 6597 6598 Example: 6599 --- 6600 void main() 6601 { 6602 import std.stdio; 6603 6604 // The following groups all produce the same output of: 6605 // 0 1 2 3 4 6606 6607 foreach (i; 0 .. 5) 6608 writef("%s ", i); 6609 writeln(); 6610 6611 import std.range : iota; 6612 foreach (i; iota(0, 5)) 6613 writef("%s ", i); 6614 writeln(); 6615 6616 writefln("%(%s %|%)", iota(0, 5)); 6617 6618 import std.algorithm.iteration : map; 6619 import std.algorithm.mutation : copy; 6620 import std.format; 6621 iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter()); 6622 writeln(); 6623 } 6624 --- 6625 */ 6626 auto iota(B, E, S)(B begin, E end, S step) 6627 if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) 6628 && isIntegral!S) 6629 { 6630 import std.conv : unsigned; 6631 6632 alias Value = CommonType!(Unqual!B, Unqual!E); 6633 alias StepType = Unqual!S; 6634 6635 assert(step != 0 || begin == end); 6636 6637 static struct Result 6638 { 6639 private Value current, last; 6640 private StepType step; // by convention, 0 if range is empty 6641 6642 this(Value current, Value pastLast, StepType step) 6643 { 6644 if (current < pastLast && step > 0) 6645 { 6646 // Iterating upward 6647 assert(unsigned((pastLast - current) / step) <= size_t.max); 6648 // Cast below can't fail because current < pastLast 6649 this.last = cast(Value) (pastLast - 1); 6650 this.last -= unsigned(this.last - current) % step; 6651 } 6652 else if (current > pastLast && step < 0) 6653 { 6654 // Iterating downward 6655 assert(unsigned((current - pastLast) / (0 - step)) <= size_t.max); 6656 // Cast below can't fail because current > pastLast 6657 this.last = cast(Value) (pastLast + 1); 6658 this.last += unsigned(current - this.last) % (0 - step); 6659 } 6660 else 6661 { 6662 // Initialize an empty range 6663 this.step = 0; 6664 return; 6665 } 6666 this.step = step; 6667 this.current = current; 6668 } 6669 6670 @property bool empty() const { return step == 0; } 6671 @property inout(Value) front() inout { assert(!empty); return current; } 6672 void popFront() 6673 { 6674 assert(!empty); 6675 if (current == last) step = 0; 6676 else current += step; 6677 } 6678 6679 @property inout(Value) back() inout 6680 { 6681 assert(!empty); 6682 return last; 6683 } 6684 void popBack() 6685 { 6686 assert(!empty); 6687 if (current == last) step = 0; 6688 else last -= step; 6689 } 6690 6691 @property auto save() { return this; } 6692 6693 inout(Value) opIndex(ulong n) inout 6694 { 6695 assert(n < this.length); 6696 6697 // Just cast to Value here because doing so gives overflow behavior 6698 // consistent with calling popFront() n times. 6699 return cast(inout Value) (current + step * n); 6700 } 6701 auto opBinaryRight(string op)(Value val) const 6702 if (op == "in") 6703 { 6704 if (empty) return false; 6705 //cast to avoid becoming unsigned 6706 auto supposedIndex = cast(StepType)(val - current) / step; 6707 return supposedIndex < length && supposedIndex * step + current == val; 6708 } 6709 auto contains(Value x){return x in this;} 6710 inout(Result) opSlice() inout { return this; } 6711 inout(Result) opSlice(ulong lower, ulong upper) inout 6712 { 6713 assert(upper >= lower && upper <= this.length); 6714 6715 return cast(inout Result) Result( 6716 cast(Value)(current + lower * step), 6717 cast(Value)(current + upper * step), 6718 step); 6719 } 6720 @property size_t length() const 6721 { 6722 if (step > 0) 6723 return 1 + cast(size_t) (unsigned(last - current) / step); 6724 if (step < 0) 6725 return 1 + cast(size_t) (unsigned(current - last) / (0 - step)); 6726 return 0; 6727 } 6728 6729 alias opDollar = length; 6730 } 6731 6732 return Result(begin, end, step); 6733 } 6734 6735 /// Ditto 6736 auto iota(B, E)(B begin, E end) 6737 if (isFloatingPoint!(CommonType!(B, E))) 6738 { 6739 return iota(begin, end, CommonType!(B, E)(1)); 6740 } 6741 6742 /// Ditto 6743 auto iota(B, E)(B begin, E end) 6744 if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) 6745 { 6746 import std.conv : unsigned; 6747 6748 alias Value = CommonType!(Unqual!B, Unqual!E); 6749 6750 static struct Result 6751 { 6752 private Value current, pastLast; 6753 6754 this(Value current, Value pastLast) 6755 { 6756 if (current < pastLast) 6757 { 6758 assert(unsigned(pastLast - current) <= size_t.max, 6759 "`iota` range is too long"); 6760 6761 this.current = current; 6762 this.pastLast = pastLast; 6763 } 6764 else 6765 { 6766 // Initialize an empty range 6767 this.current = this.pastLast = current; 6768 } 6769 } 6770 6771 @property bool empty() const { return current == pastLast; } 6772 @property inout(Value) front() inout 6773 { 6774 assert(!empty, "Attempt to access `front` of empty `iota` range"); 6775 return current; 6776 } 6777 void popFront() 6778 { 6779 assert(!empty, "Attempt to `popFront` of empty `iota` range"); 6780 ++current; 6781 } 6782 6783 @property inout(Value) back() inout 6784 { 6785 assert(!empty, "Attempt to access `back` of empty `iota` range"); 6786 return cast(inout(Value))(pastLast - 1); 6787 } 6788 void popBack() 6789 { 6790 assert(!empty, "Attempt to `popBack` of empty `iota` range"); 6791 --pastLast; 6792 } 6793 6794 @property auto save() { return this; } 6795 6796 inout(Value) opIndex(size_t n) inout 6797 { 6798 assert(n < this.length, 6799 "Attempt to read out-of-bounds index of `iota` range"); 6800 6801 // Just cast to Value here because doing so gives overflow behavior 6802 // consistent with calling popFront() n times. 6803 return cast(inout Value) (current + n); 6804 } 6805 auto opBinaryRight(string op)(Value val) const 6806 if (op == "in") 6807 { 6808 return current <= val && val < pastLast; 6809 } 6810 auto contains(Value x){return x in this;} 6811 inout(Result) opSlice() inout { return this; } 6812 inout(Result) opSlice(ulong lower, ulong upper) inout 6813 { 6814 assert(upper >= lower && upper <= this.length, 6815 "Attempt to get out-of-bounds slice of `iota` range"); 6816 6817 return cast(inout Result) Result(cast(Value)(current + lower), 6818 cast(Value)(pastLast - (length - upper))); 6819 } 6820 @property size_t length() const 6821 { 6822 return cast(size_t)(pastLast - current); 6823 } 6824 6825 alias opDollar = length; 6826 } 6827 6828 return Result(begin, end); 6829 } 6830 6831 /// Ditto 6832 auto iota(E)(E end) 6833 if (is(typeof(iota(E(0), end)))) 6834 { 6835 E begin = E(0); 6836 return iota(begin, end); 6837 } 6838 6839 /// Ditto 6840 // Specialization for floating-point types 6841 auto iota(B, E, S)(B begin, E end, S step) 6842 if (isFloatingPoint!(CommonType!(B, E, S))) 6843 in 6844 { 6845 assert(step != 0, "iota: step must not be 0"); 6846 assert((end - begin) / step >= 0, "iota: incorrect startup parameters"); 6847 } 6848 do 6849 { 6850 alias Value = Unqual!(CommonType!(B, E, S)); 6851 static struct Result 6852 { 6853 private Value start, step; 6854 private size_t index, count; 6855 6856 this(Value start, Value end, Value step) 6857 { 6858 import std.conv : to; 6859 6860 this.start = start; 6861 this.step = step; 6862 immutable fcount = (end - start) / step; 6863 count = to!size_t(fcount); 6864 auto pastEnd = start + count * step; 6865 if (step > 0) 6866 { 6867 if (pastEnd < end) ++count; 6868 assert(start + count * step >= end); 6869 } 6870 else 6871 { 6872 if (pastEnd > end) ++count; 6873 assert(start + count * step <= end); 6874 } 6875 } 6876 6877 @property bool empty() const { return index == count; } 6878 @property Value front() const { assert(!empty); return start + step * index; } 6879 void popFront() 6880 { 6881 assert(!empty); 6882 ++index; 6883 } 6884 @property Value back() const 6885 { 6886 assert(!empty); 6887 return start + step * (count - 1); 6888 } 6889 void popBack() 6890 { 6891 assert(!empty); 6892 --count; 6893 } 6894 6895 @property auto save() { return this; } 6896 6897 Value opIndex(size_t n) const 6898 { 6899 assert(n < count); 6900 return start + step * (n + index); 6901 } 6902 inout(Result) opSlice() inout 6903 { 6904 return this; 6905 } 6906 inout(Result) opSlice(size_t lower, size_t upper) inout 6907 { 6908 assert(upper >= lower && upper <= count); 6909 6910 Result ret = this; 6911 ret.index += lower; 6912 ret.count = upper - lower + ret.index; 6913 return cast(inout Result) ret; 6914 } 6915 @property size_t length() const 6916 { 6917 return count - index; 6918 } 6919 6920 alias opDollar = length; 6921 } 6922 6923 return Result(begin, end, step); 6924 } 6925 6926 /// 6927 pure @safe unittest 6928 { 6929 import std.algorithm.comparison : equal; 6930 import std.math.operations : isClose; 6931 6932 auto r = iota(0, 10, 1); 6933 assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); 6934 assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); 6935 assert(3 in r); 6936 assert(r.contains(3)); //Same as above 6937 assert(!(10 in r)); 6938 assert(!(-8 in r)); 6939 r = iota(0, 11, 3); 6940 assert(equal(r, [0, 3, 6, 9])); 6941 assert(r[2] == 6); 6942 assert(!(2 in r)); 6943 auto rf = iota(0.0, 0.5, 0.1); 6944 assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4])); 6945 } 6946 6947 pure nothrow @nogc @safe unittest 6948 { 6949 import std.traits : Signed; 6950 //float overloads use std.conv.to so can't be @nogc or nothrow 6951 alias ssize_t = Signed!size_t; 6952 assert(iota(ssize_t.max, 0, -1).length == ssize_t.max); 6953 assert(iota(ssize_t.max, ssize_t.min, -1).length == size_t.max); 6954 assert(iota(ssize_t.max, ssize_t.min, -2).length == 1 + size_t.max / 2); 6955 assert(iota(ssize_t.min, ssize_t.max, 2).length == 1 + size_t.max / 2); 6956 assert(iota(ssize_t.max, ssize_t.min, -3).length == size_t.max / 3); 6957 } 6958 6959 debug @system unittest 6960 {//check the contracts 6961 import core.exception : AssertError; 6962 import std.exception : assertThrown; 6963 assertThrown!AssertError(iota(1,2,0)); 6964 assertThrown!AssertError(iota(0f,1f,0f)); 6965 assertThrown!AssertError(iota(1f,0f,0.1f)); 6966 assertThrown!AssertError(iota(0f,1f,-0.1f)); 6967 } 6968 6969 pure @system nothrow unittest 6970 { 6971 int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 6972 auto r1 = iota(a.ptr, a.ptr + a.length, 1); 6973 assert(r1.front == a.ptr); 6974 assert(r1.back == a.ptr + a.length - 1); 6975 assert(&a[4] in r1); 6976 } 6977 6978 pure @safe nothrow @nogc unittest 6979 { 6980 assert(iota(1UL, 0UL).length == 0); 6981 assert(iota(1UL, 0UL, 1).length == 0); 6982 assert(iota(0, 1, 1).length == 1); 6983 assert(iota(1, 0, -1).length == 1); 6984 assert(iota(0, 1, -1).length == 0); 6985 assert(iota(ulong.max, 0).length == 0); 6986 } 6987 6988 pure @safe unittest 6989 { 6990 import std.algorithm.comparison : equal; 6991 import std.algorithm.searching : count; 6992 import std.math.operations : isClose, nextUp, nextDown; 6993 import std.meta : AliasSeq; 6994 6995 static assert(is(ElementType!(typeof(iota(0f))) == float)); 6996 6997 static assert(hasLength!(typeof(iota(0, 2)))); 6998 auto r = iota(0, 10, 1); 6999 assert(r[$ - 1] == 9); 7000 assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); 7001 7002 auto rSlice = r[2 .. 8]; 7003 assert(equal(rSlice, [2, 3, 4, 5, 6, 7])); 7004 7005 rSlice.popFront(); 7006 assert(rSlice[0] == rSlice.front); 7007 assert(rSlice.front == 3); 7008 7009 rSlice.popBack(); 7010 assert(rSlice[rSlice.length - 1] == rSlice.back); 7011 assert(rSlice.back == 6); 7012 7013 rSlice = r[0 .. 4]; 7014 assert(equal(rSlice, [0, 1, 2, 3])); 7015 assert(3 in rSlice); 7016 assert(!(4 in rSlice)); 7017 7018 auto rr = iota(10); 7019 assert(equal(rr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); 7020 7021 r = iota(0, -10, -1); 7022 assert(equal(r, [0, -1, -2, -3, -4, -5, -6, -7, -8, -9][])); 7023 rSlice = r[3 .. 9]; 7024 assert(equal(rSlice, [-3, -4, -5, -6, -7, -8])); 7025 7026 r = iota(0, -6, -3); 7027 assert(equal(r, [0, -3][])); 7028 rSlice = r[1 .. 2]; 7029 assert(equal(rSlice, [-3])); 7030 7031 r = iota(0, -7, -3); 7032 assert(equal(r, [0, -3, -6][])); 7033 assert(0 in r); 7034 assert(-6 in r); 7035 rSlice = r[1 .. 3]; 7036 assert(equal(rSlice, [-3, -6])); 7037 assert(!(0 in rSlice)); 7038 assert(!(-2 in rSlice)); 7039 assert(!(-5 in rSlice)); 7040 assert(!(3 in rSlice)); 7041 assert(!(-9 in rSlice)); 7042 7043 r = iota(0, 11, 3); 7044 assert(equal(r, [0, 3, 6, 9][])); 7045 assert(r[2] == 6); 7046 rSlice = r[1 .. 3]; 7047 assert(equal(rSlice, [3, 6])); 7048 7049 auto rf = iota(0.0, 0.5, 0.1); 7050 assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4][])); 7051 assert(rf.length == 5); 7052 7053 rf.popFront(); 7054 assert(rf.length == 4); 7055 7056 auto rfSlice = rf[1 .. 4]; 7057 assert(rfSlice.length == 3); 7058 assert(isClose(rfSlice, [0.2, 0.3, 0.4])); 7059 7060 rfSlice.popFront(); 7061 assert(isClose(rfSlice[0], 0.3)); 7062 7063 rf.popFront(); 7064 assert(rf.length == 3); 7065 7066 rfSlice = rf[1 .. 3]; 7067 assert(rfSlice.length == 2); 7068 assert(isClose(rfSlice, [0.3, 0.4])); 7069 assert(isClose(rfSlice[0], 0.3)); 7070 7071 // With something just above 0.5 7072 rf = iota(0.0, nextUp(0.5), 0.1); 7073 assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][])); 7074 rf.popBack(); 7075 assert(rf[rf.length - 1] == rf.back); 7076 assert(isClose(rf.back, 0.4)); 7077 assert(rf.length == 5); 7078 7079 // going down 7080 rf = iota(0.0, -0.5, -0.1); 7081 assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4][])); 7082 rfSlice = rf[2 .. 5]; 7083 assert(isClose(rfSlice, [-0.2, -0.3, -0.4])); 7084 7085 rf = iota(0.0, nextDown(-0.5), -0.1); 7086 assert(isClose(rf, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][])); 7087 7088 // iota of longs 7089 auto rl = iota(5_000_000L); 7090 assert(rl.length == 5_000_000L); 7091 assert(0 in rl); 7092 assert(4_000_000L in rl); 7093 assert(!(-4_000_000L in rl)); 7094 assert(!(5_000_000L in rl)); 7095 7096 // iota of longs with steps 7097 auto iota_of_longs_with_steps = iota(50L, 101L, 10); 7098 assert(iota_of_longs_with_steps.length == 6); 7099 assert(equal(iota_of_longs_with_steps, [50L, 60L, 70L, 80L, 90L, 100L])); 7100 7101 // iota of unsigned zero length (https://issues.dlang.org/show_bug.cgi?id=6222) 7102 // Actually trying to consume it is the only way to find something is wrong 7103 // because the public properties are all correct. 7104 auto iota_zero_unsigned = iota(0, 0u, 3); 7105 assert(count(iota_zero_unsigned) == 0); 7106 7107 // https://issues.dlang.org/show_bug.cgi?id=7982 7108 // unsigned reverse iota can be buggy if `.length` doesn't 7109 // take them into account 7110 assert(iota(10u, 0u, -1).length == 10); 7111 assert(iota(10u, 0u, -2).length == 5); 7112 assert(iota(uint.max, uint.max-10, -1).length == 10); 7113 assert(iota(uint.max, uint.max-10, -2).length == 5); 7114 assert(iota(uint.max, 0u, -1).length == uint.max); 7115 7116 assert(20 in iota(20u, 10u, -2)); 7117 assert(16 in iota(20u, 10u, -2)); 7118 assert(!(15 in iota(20u, 10u, -2))); 7119 assert(!(10 in iota(20u, 10u, -2))); 7120 assert(!(uint.max in iota(20u, 10u, -1))); 7121 assert(!(int.min in iota(20u, 10u, -1))); 7122 assert(!(int.max in iota(20u, 10u, -1))); 7123 7124 7125 // https://issues.dlang.org/show_bug.cgi?id=8920 7126 static foreach (Type; AliasSeq!(byte, ubyte, short, ushort, 7127 int, uint, long, ulong)) 7128 {{ 7129 Type val; 7130 foreach (i; iota(cast(Type) 0, cast(Type) 10)) { val++; } 7131 assert(val == 10); 7132 }} 7133 } 7134 7135 pure @safe nothrow unittest 7136 { 7137 import std.algorithm.mutation : copy; 7138 auto idx = new size_t[100]; 7139 copy(iota(0, idx.length), idx); 7140 } 7141 7142 @safe unittest 7143 { 7144 import std.meta : AliasSeq; 7145 static foreach (range; AliasSeq!(iota(2, 27, 4), 7146 iota(3, 9), 7147 iota(2.7, 12.3, .1), 7148 iota(3.2, 9.7))) 7149 {{ 7150 const cRange = range; 7151 const e = cRange.empty; 7152 const f = cRange.front; 7153 const b = cRange.back; 7154 const i = cRange[2]; 7155 const s1 = cRange[]; 7156 const s2 = cRange[0 .. 3]; 7157 const l = cRange.length; 7158 }} 7159 } 7160 7161 @system unittest 7162 { 7163 //The ptr stuff can't be done at compile time, so we unfortunately end 7164 //up with some code duplication here. 7165 auto arr = [0, 5, 3, 5, 5, 7, 9, 2, 0, 42, 7, 6]; 7166 7167 { 7168 const cRange = iota(arr.ptr, arr.ptr + arr.length, 3); 7169 const e = cRange.empty; 7170 const f = cRange.front; 7171 const b = cRange.back; 7172 const i = cRange[2]; 7173 const s1 = cRange[]; 7174 const s2 = cRange[0 .. 3]; 7175 const l = cRange.length; 7176 } 7177 7178 { 7179 const cRange = iota(arr.ptr, arr.ptr + arr.length); 7180 const e = cRange.empty; 7181 const f = cRange.front; 7182 const b = cRange.back; 7183 const i = cRange[2]; 7184 const s1 = cRange[]; 7185 const s2 = cRange[0 .. 3]; 7186 const l = cRange.length; 7187 } 7188 } 7189 7190 @nogc nothrow pure @safe unittest 7191 { 7192 { 7193 ushort start = 0, end = 10, step = 2; 7194 foreach (i; iota(start, end, step)) 7195 static assert(is(typeof(i) == ushort)); 7196 } 7197 { 7198 ubyte start = 0, end = 255, step = 128; 7199 uint x; 7200 foreach (i; iota(start, end, step)) 7201 { 7202 static assert(is(typeof(i) == ubyte)); 7203 ++x; 7204 } 7205 assert(x == 2); 7206 } 7207 } 7208 7209 /* Generic overload that handles arbitrary types that support arithmetic 7210 * operations. 7211 * 7212 * User-defined types such as $(REF BigInt, std,bigint) are also supported, as long 7213 * as they can be incremented with `++` and compared with `<` or `==`. 7214 */ 7215 /// ditto 7216 auto iota(B, E)(B begin, E end) 7217 if (!isIntegral!(CommonType!(B, E)) && 7218 !isFloatingPoint!(CommonType!(B, E)) && 7219 !isPointer!(CommonType!(B, E)) && 7220 is(typeof((ref B b) { ++b; })) && 7221 (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))) ) 7222 { 7223 static struct Result 7224 { 7225 B current; 7226 E end; 7227 7228 @property bool empty() 7229 { 7230 static if (is(typeof(B.init < E.init))) 7231 return !(current < end); 7232 else static if (is(typeof(B.init != E.init))) 7233 return current == end; 7234 else 7235 static assert(0); 7236 } 7237 @property auto front() { return current; } 7238 void popFront() 7239 { 7240 assert(!empty); 7241 ++current; 7242 } 7243 @property auto save() { return this; } 7244 } 7245 return Result(begin, end); 7246 } 7247 7248 @safe unittest 7249 { 7250 import std.algorithm.comparison : equal; 7251 7252 // Test iota() for a type that only supports ++ and != but does not have 7253 // '<'-ordering. 7254 struct Cyclic(int wrapAround) 7255 { 7256 int current; 7257 7258 this(int start) { current = start % wrapAround; } 7259 7260 bool opEquals(Cyclic c) const { return current == c.current; } 7261 bool opEquals(int i) const { return current == i; } 7262 void opUnary(string op)() if (op == "++") 7263 { 7264 current = (current + 1) % wrapAround; 7265 } 7266 } 7267 alias Cycle5 = Cyclic!5; 7268 7269 // Easy case 7270 auto i1 = iota(Cycle5(1), Cycle5(4)); 7271 assert(i1.equal([1, 2, 3])); 7272 7273 // Wraparound case 7274 auto i2 = iota(Cycle5(3), Cycle5(2)); 7275 assert(i2.equal([3, 4, 0, 1 ])); 7276 } 7277 7278 // https://issues.dlang.org/show_bug.cgi?id=23453 7279 @safe unittest 7280 { 7281 auto r = iota('a', 'z'); 7282 static assert(isForwardRange!(typeof(r))); 7283 } 7284 7285 /** 7286 Options for the $(LREF FrontTransversal) and $(LREF Transversal) ranges 7287 (below). 7288 */ 7289 enum TransverseOptions 7290 { 7291 /** 7292 When transversed, the elements of a range of ranges are assumed to 7293 have different lengths (e.g. a jagged array). 7294 */ 7295 assumeJagged, //default 7296 /** 7297 The transversal enforces that the elements of a range of ranges have 7298 all the same length (e.g. an array of arrays, all having the same 7299 length). Checking is done once upon construction of the transversal 7300 range. 7301 */ 7302 enforceNotJagged, 7303 /** 7304 The transversal assumes, without verifying, that the elements of a 7305 range of ranges have all the same length. This option is useful if 7306 checking was already done from the outside of the range. 7307 */ 7308 assumeNotJagged, 7309 } 7310 7311 /// 7312 @safe pure unittest 7313 { 7314 import std.algorithm.comparison : equal; 7315 import std.exception : assertThrown; 7316 7317 auto arr = [[1, 2], [3, 4, 5]]; 7318 7319 auto r1 = arr.frontTransversal!(TransverseOptions.assumeJagged); 7320 assert(r1.equal([1, 3])); 7321 7322 // throws on construction 7323 assertThrown!Exception(arr.frontTransversal!(TransverseOptions.enforceNotJagged)); 7324 7325 auto r2 = arr.frontTransversal!(TransverseOptions.assumeNotJagged); 7326 assert(r2.equal([1, 3])); 7327 7328 // either assuming or checking for equal lengths makes 7329 // the result a random access range 7330 assert(r2[0] == 1); 7331 static assert(!__traits(compiles, r1[0])); 7332 } 7333 7334 /** 7335 Given a range of ranges, iterate transversally through the first 7336 elements of each of the enclosed ranges. 7337 */ 7338 struct FrontTransversal(Ror, 7339 TransverseOptions opt = TransverseOptions.assumeJagged) 7340 { 7341 alias RangeOfRanges = Unqual!(Ror); 7342 alias RangeType = .ElementType!RangeOfRanges; 7343 alias ElementType = .ElementType!RangeType; 7344 7345 private void prime() 7346 { 7347 static if (opt == TransverseOptions.assumeJagged) 7348 { 7349 while (!_input.empty && _input.front.empty) 7350 { 7351 _input.popFront(); 7352 } 7353 static if (isBidirectionalRange!RangeOfRanges) 7354 { 7355 while (!_input.empty && _input.back.empty) 7356 { 7357 _input.popBack(); 7358 } 7359 } 7360 } 7361 } 7362 7363 /** 7364 Construction from an input. 7365 */ 7366 this(RangeOfRanges input) 7367 { 7368 _input = input; 7369 prime(); 7370 static if (opt == TransverseOptions.enforceNotJagged) 7371 // (isRandomAccessRange!RangeOfRanges 7372 // && hasLength!RangeType) 7373 { 7374 import std.exception : enforce; 7375 7376 if (empty) return; 7377 immutable commonLength = _input.front.length; 7378 foreach (e; _input) 7379 { 7380 enforce(e.length == commonLength); 7381 } 7382 } 7383 } 7384 7385 /** 7386 Forward range primitives. 7387 */ 7388 static if (isInfinite!RangeOfRanges) 7389 { 7390 enum bool empty = false; 7391 } 7392 else 7393 { 7394 @property bool empty() 7395 { 7396 static if (opt != TransverseOptions.assumeJagged) 7397 { 7398 if (!_input.empty) 7399 return _input.front.empty; 7400 } 7401 7402 return _input.empty; 7403 } 7404 } 7405 7406 /// Ditto 7407 @property auto ref front() 7408 { 7409 assert(!empty, "Attempting to fetch the front of an empty FrontTransversal"); 7410 return _input.front.front; 7411 } 7412 7413 /// Ditto 7414 static if (hasMobileElements!RangeType) 7415 { 7416 ElementType moveFront() 7417 { 7418 return _input.front.moveFront(); 7419 } 7420 } 7421 7422 static if (hasAssignableElements!RangeType) 7423 { 7424 @property void front(ElementType val) 7425 { 7426 import std.algorithm.mutation : move; 7427 7428 _input.front.front = move(val); 7429 } 7430 } 7431 7432 /// Ditto 7433 void popFront() 7434 { 7435 assert(!empty, "Attempting to popFront an empty FrontTransversal"); 7436 _input.popFront(); 7437 prime(); 7438 } 7439 7440 /** 7441 Duplicates this `frontTransversal`. Note that only the encapsulating 7442 range of range will be duplicated. Underlying ranges will not be 7443 duplicated. 7444 */ 7445 static if (isForwardRange!RangeOfRanges) 7446 { 7447 @property FrontTransversal save() 7448 { 7449 return FrontTransversal(_input.save); 7450 } 7451 } 7452 7453 static if (isBidirectionalRange!RangeOfRanges) 7454 { 7455 /** 7456 Bidirectional primitives. They are offered if $(D 7457 isBidirectionalRange!RangeOfRanges). 7458 */ 7459 @property auto ref back() 7460 { 7461 assert(!empty, "Attempting to fetch the back of an empty FrontTransversal"); 7462 return _input.back.front; 7463 } 7464 /// Ditto 7465 void popBack() 7466 { 7467 assert(!empty, "Attempting to popBack an empty FrontTransversal"); 7468 _input.popBack(); 7469 prime(); 7470 } 7471 7472 /// Ditto 7473 static if (hasMobileElements!RangeType) 7474 { 7475 ElementType moveBack() 7476 { 7477 return _input.back.moveFront(); 7478 } 7479 } 7480 7481 static if (hasAssignableElements!RangeType) 7482 { 7483 @property void back(ElementType val) 7484 { 7485 import std.algorithm.mutation : move; 7486 7487 _input.back.front = move(val); 7488 } 7489 } 7490 } 7491 7492 static if (isRandomAccessRange!RangeOfRanges && 7493 (opt == TransverseOptions.assumeNotJagged || 7494 opt == TransverseOptions.enforceNotJagged)) 7495 { 7496 /** 7497 Random-access primitive. It is offered if $(D 7498 isRandomAccessRange!RangeOfRanges && (opt == 7499 TransverseOptions.assumeNotJagged || opt == 7500 TransverseOptions.enforceNotJagged)). 7501 */ 7502 auto ref opIndex(size_t n) 7503 { 7504 return _input[n].front; 7505 } 7506 7507 /// Ditto 7508 static if (hasMobileElements!RangeType) 7509 { 7510 ElementType moveAt(size_t n) 7511 { 7512 return _input[n].moveFront(); 7513 } 7514 } 7515 /// Ditto 7516 static if (hasAssignableElements!RangeType) 7517 { 7518 void opIndexAssign(ElementType val, size_t n) 7519 { 7520 import std.algorithm.mutation : move; 7521 7522 _input[n].front = move(val); 7523 } 7524 } 7525 mixin ImplementLength!_input; 7526 7527 /** 7528 Slicing if offered if `RangeOfRanges` supports slicing and all the 7529 conditions for supporting indexing are met. 7530 */ 7531 static if (hasSlicing!RangeOfRanges) 7532 { 7533 typeof(this) opSlice(size_t lower, size_t upper) 7534 { 7535 return typeof(this)(_input[lower .. upper]); 7536 } 7537 } 7538 } 7539 7540 auto opSlice() { return this; } 7541 7542 private: 7543 RangeOfRanges _input; 7544 } 7545 7546 /// Ditto 7547 FrontTransversal!(RangeOfRanges, opt) frontTransversal( 7548 TransverseOptions opt = TransverseOptions.assumeJagged, 7549 RangeOfRanges) 7550 (RangeOfRanges rr) 7551 { 7552 return typeof(return)(rr); 7553 } 7554 7555 /// 7556 pure @safe nothrow unittest 7557 { 7558 import std.algorithm.comparison : equal; 7559 int[][] x = new int[][2]; 7560 x[0] = [1, 2]; 7561 x[1] = [3, 4]; 7562 auto ror = frontTransversal(x); 7563 assert(equal(ror, [ 1, 3 ][])); 7564 } 7565 7566 @safe unittest 7567 { 7568 import std.algorithm.comparison : equal; 7569 import std.internal.test.dummyrange : AllDummyRanges, DummyRange, ReturnBy; 7570 7571 static assert(is(FrontTransversal!(immutable int[][]))); 7572 7573 foreach (DummyType; AllDummyRanges) 7574 { 7575 auto dummies = 7576 [DummyType.init, DummyType.init, DummyType.init, DummyType.init]; 7577 7578 foreach (i, ref elem; dummies) 7579 { 7580 // Just violate the DummyRange abstraction to get what I want. 7581 elem.arr = elem.arr[i..$ - (3 - i)]; 7582 } 7583 7584 auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(dummies); 7585 static if (isForwardRange!DummyType) 7586 { 7587 static assert(isForwardRange!(typeof(ft))); 7588 } 7589 7590 assert(equal(ft, [1, 2, 3, 4])); 7591 7592 // Test slicing. 7593 assert(equal(ft[0 .. 2], [1, 2])); 7594 assert(equal(ft[1 .. 3], [2, 3])); 7595 7596 assert(ft.front == ft.moveFront()); 7597 assert(ft.back == ft.moveBack()); 7598 assert(ft.moveAt(1) == ft[1]); 7599 7600 7601 // Test infiniteness propagation. 7602 static assert(isInfinite!(typeof(frontTransversal(repeat("foo"))))); 7603 7604 static if (DummyType.r == ReturnBy.Reference) 7605 { 7606 { 7607 ft.front++; 7608 scope(exit) ft.front--; 7609 assert(dummies.front.front == 2); 7610 } 7611 7612 { 7613 ft.front = 5; 7614 scope(exit) ft.front = 1; 7615 assert(dummies[0].front == 5); 7616 } 7617 7618 { 7619 ft.back = 88; 7620 scope(exit) ft.back = 4; 7621 assert(dummies.back.front == 88); 7622 } 7623 7624 { 7625 ft[1] = 99; 7626 scope(exit) ft[1] = 2; 7627 assert(dummies[1].front == 99); 7628 } 7629 } 7630 } 7631 } 7632 7633 // https://issues.dlang.org/show_bug.cgi?id=16363 7634 pure @safe nothrow unittest 7635 { 7636 import std.algorithm.comparison : equal; 7637 7638 int[][] darr = [[0, 1], [4, 5]]; 7639 auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(darr); 7640 7641 assert(equal(ft, [0, 4])); 7642 static assert(isRandomAccessRange!(typeof(ft))); 7643 } 7644 7645 // https://issues.dlang.org/show_bug.cgi?id=16442 7646 pure @safe nothrow unittest 7647 { 7648 int[][] arr = [[], []]; 7649 7650 auto ft = frontTransversal!(TransverseOptions.assumeNotJagged)(arr); 7651 assert(ft.empty); 7652 } 7653 7654 // ditto 7655 pure @safe unittest 7656 { 7657 int[][] arr = [[], []]; 7658 7659 auto ft = frontTransversal!(TransverseOptions.enforceNotJagged)(arr); 7660 assert(ft.empty); 7661 } 7662 7663 /** 7664 Given a range of ranges, iterate transversally through the 7665 `n`th element of each of the enclosed ranges. This function 7666 is similar to `unzip` in other languages. 7667 7668 Params: 7669 opt = Controls the assumptions the function makes about the lengths 7670 of the ranges 7671 rr = An input range of random access ranges 7672 Returns: 7673 At minimum, an input range. Range primitives such as bidirectionality 7674 and random access are given if the element type of `rr` provides them. 7675 */ 7676 struct Transversal(Ror, 7677 TransverseOptions opt = TransverseOptions.assumeJagged) 7678 { 7679 private alias RangeOfRanges = Unqual!Ror; 7680 private alias InnerRange = ElementType!RangeOfRanges; 7681 private alias E = ElementType!InnerRange; 7682 7683 private void prime() 7684 { 7685 static if (opt == TransverseOptions.assumeJagged) 7686 { 7687 while (!_input.empty && _input.front.length <= _n) 7688 { 7689 _input.popFront(); 7690 } 7691 static if (isBidirectionalRange!RangeOfRanges) 7692 { 7693 while (!_input.empty && _input.back.length <= _n) 7694 { 7695 _input.popBack(); 7696 } 7697 } 7698 } 7699 } 7700 7701 /** 7702 Construction from an input and an index. 7703 */ 7704 this(RangeOfRanges input, size_t n) 7705 { 7706 _input = input; 7707 _n = n; 7708 prime(); 7709 static if (opt == TransverseOptions.enforceNotJagged) 7710 { 7711 import std.exception : enforce; 7712 7713 if (empty) return; 7714 immutable commonLength = _input.front.length; 7715 foreach (e; _input) 7716 { 7717 enforce(e.length == commonLength); 7718 } 7719 } 7720 } 7721 7722 /** 7723 Forward range primitives. 7724 */ 7725 static if (isInfinite!(RangeOfRanges)) 7726 { 7727 enum bool empty = false; 7728 } 7729 else 7730 { 7731 @property bool empty() 7732 { 7733 return _input.empty; 7734 } 7735 } 7736 7737 /// Ditto 7738 @property auto ref front() 7739 { 7740 assert(!empty, "Attempting to fetch the front of an empty Transversal"); 7741 return _input.front[_n]; 7742 } 7743 7744 /// Ditto 7745 static if (hasMobileElements!InnerRange) 7746 { 7747 E moveFront() 7748 { 7749 return _input.front.moveAt(_n); 7750 } 7751 } 7752 7753 /// Ditto 7754 static if (hasAssignableElements!InnerRange) 7755 { 7756 @property void front(E val) 7757 { 7758 _input.front[_n] = val; 7759 } 7760 } 7761 7762 7763 /// Ditto 7764 void popFront() 7765 { 7766 assert(!empty, "Attempting to popFront an empty Transversal"); 7767 _input.popFront(); 7768 prime(); 7769 } 7770 7771 /// Ditto 7772 static if (isForwardRange!RangeOfRanges) 7773 { 7774 @property typeof(this) save() 7775 { 7776 auto ret = this; 7777 ret._input = _input.save; 7778 return ret; 7779 } 7780 } 7781 7782 static if (isBidirectionalRange!RangeOfRanges) 7783 { 7784 /** 7785 Bidirectional primitives. They are offered if $(D 7786 isBidirectionalRange!RangeOfRanges). 7787 */ 7788 @property auto ref back() 7789 { 7790 assert(!empty, "Attempting to fetch the back of an empty Transversal"); 7791 return _input.back[_n]; 7792 } 7793 7794 /// Ditto 7795 void popBack() 7796 { 7797 assert(!empty, "Attempting to popBack an empty Transversal"); 7798 _input.popBack(); 7799 prime(); 7800 } 7801 7802 /// Ditto 7803 static if (hasMobileElements!InnerRange) 7804 { 7805 E moveBack() 7806 { 7807 return _input.back.moveAt(_n); 7808 } 7809 } 7810 7811 /// Ditto 7812 static if (hasAssignableElements!InnerRange) 7813 { 7814 @property void back(E val) 7815 { 7816 _input.back[_n] = val; 7817 } 7818 } 7819 7820 } 7821 7822 static if (isRandomAccessRange!RangeOfRanges && 7823 (opt == TransverseOptions.assumeNotJagged || 7824 opt == TransverseOptions.enforceNotJagged)) 7825 { 7826 /** 7827 Random-access primitive. It is offered if $(D 7828 isRandomAccessRange!RangeOfRanges && (opt == 7829 TransverseOptions.assumeNotJagged || opt == 7830 TransverseOptions.enforceNotJagged)). 7831 */ 7832 auto ref opIndex(size_t n) 7833 { 7834 return _input[n][_n]; 7835 } 7836 7837 /// Ditto 7838 static if (hasMobileElements!InnerRange) 7839 { 7840 E moveAt(size_t n) 7841 { 7842 return _input[n].moveAt(_n); 7843 } 7844 } 7845 7846 /// Ditto 7847 static if (hasAssignableElements!InnerRange) 7848 { 7849 void opIndexAssign(E val, size_t n) 7850 { 7851 _input[n][_n] = val; 7852 } 7853 } 7854 7855 mixin ImplementLength!_input; 7856 7857 /** 7858 Slicing if offered if `RangeOfRanges` supports slicing and all the 7859 conditions for supporting indexing are met. 7860 */ 7861 static if (hasSlicing!RangeOfRanges) 7862 { 7863 typeof(this) opSlice(size_t lower, size_t upper) 7864 { 7865 return typeof(this)(_input[lower .. upper], _n); 7866 } 7867 } 7868 } 7869 7870 auto opSlice() { return this; } 7871 7872 private: 7873 RangeOfRanges _input; 7874 size_t _n; 7875 } 7876 7877 /// Ditto 7878 Transversal!(RangeOfRanges, opt) transversal 7879 (TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) 7880 (RangeOfRanges rr, size_t n) 7881 { 7882 return typeof(return)(rr, n); 7883 } 7884 7885 /// 7886 @safe unittest 7887 { 7888 import std.algorithm.comparison : equal; 7889 int[][] x = new int[][2]; 7890 x[0] = [1, 2]; 7891 x[1] = [3, 4]; 7892 auto ror = transversal(x, 1); 7893 assert(equal(ror, [ 2, 4 ])); 7894 } 7895 7896 /// The following code does a full unzip 7897 @safe unittest 7898 { 7899 import std.algorithm.comparison : equal; 7900 import std.algorithm.iteration : map; 7901 int[][] y = [[1, 2, 3], [4, 5, 6]]; 7902 auto z = y.front.walkLength.iota.map!(i => transversal(y, i)); 7903 assert(equal!equal(z, [[1, 4], [2, 5], [3, 6]])); 7904 } 7905 7906 @safe unittest 7907 { 7908 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 7909 7910 int[][] x = new int[][2]; 7911 x[0] = [ 1, 2 ]; 7912 x[1] = [3, 4]; 7913 auto ror = transversal!(TransverseOptions.assumeNotJagged)(x, 1); 7914 auto witness = [ 2, 4 ]; 7915 uint i; 7916 foreach (e; ror) assert(e == witness[i++]); 7917 assert(i == 2); 7918 assert(ror.length == 2); 7919 7920 static assert(is(Transversal!(immutable int[][]))); 7921 7922 // Make sure ref, assign is being propagated. 7923 { 7924 ror.front++; 7925 scope(exit) ror.front--; 7926 assert(x[0][1] == 3); 7927 } 7928 { 7929 ror.front = 5; 7930 scope(exit) ror.front = 2; 7931 assert(x[0][1] == 5); 7932 assert(ror.moveFront() == 5); 7933 } 7934 { 7935 ror.back = 999; 7936 scope(exit) ror.back = 4; 7937 assert(x[1][1] == 999); 7938 assert(ror.moveBack() == 999); 7939 } 7940 { 7941 ror[0] = 999; 7942 scope(exit) ror[0] = 2; 7943 assert(x[0][1] == 999); 7944 assert(ror.moveAt(0) == 999); 7945 } 7946 7947 // Test w/o ref return. 7948 alias D = DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random); 7949 auto drs = [D.init, D.init]; 7950 foreach (num; 0 .. 10) 7951 { 7952 auto t = transversal!(TransverseOptions.enforceNotJagged)(drs, num); 7953 assert(t[0] == t[1]); 7954 assert(t[1] == num + 1); 7955 } 7956 7957 static assert(isInfinite!(typeof(transversal(repeat([1,2,3]), 1)))); 7958 7959 // Test slicing. 7960 auto mat = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]; 7961 auto mat1 = transversal!(TransverseOptions.assumeNotJagged)(mat, 1)[1 .. 3]; 7962 assert(mat1[0] == 6); 7963 assert(mat1[1] == 10); 7964 } 7965 7966 struct Transposed(RangeOfRanges, 7967 TransverseOptions opt = TransverseOptions.assumeJagged) 7968 if (isForwardRange!RangeOfRanges && 7969 isInputRange!(ElementType!RangeOfRanges) && 7970 hasAssignableElements!RangeOfRanges) 7971 { 7972 this(RangeOfRanges input) 7973 { 7974 this._input = input; 7975 static if (opt == TransverseOptions.enforceNotJagged) 7976 { 7977 import std.exception : enforce; 7978 7979 if (empty) return; 7980 immutable commonLength = _input.front.length; 7981 foreach (e; _input) 7982 { 7983 enforce(e.length == commonLength); 7984 } 7985 } 7986 } 7987 7988 @property auto front() 7989 { 7990 import std.algorithm.iteration : filter, map; 7991 return _input.save 7992 .filter!(a => !a.empty) 7993 .map!(a => a.front); 7994 } 7995 7996 void popFront() 7997 { 7998 // Advance the position of each subrange. 7999 auto r = _input.save; 8000 while (!r.empty) 8001 { 8002 auto e = r.front; 8003 if (!e.empty) 8004 { 8005 e.popFront(); 8006 r.front = e; 8007 } 8008 8009 r.popFront(); 8010 } 8011 } 8012 8013 static if (isRandomAccessRange!(ElementType!RangeOfRanges)) 8014 { 8015 auto ref opIndex(size_t n) 8016 { 8017 return transversal!opt(_input, n); 8018 } 8019 } 8020 8021 @property bool empty() 8022 { 8023 if (_input.empty) return true; 8024 foreach (e; _input.save) 8025 { 8026 if (!e.empty) return false; 8027 } 8028 return true; 8029 } 8030 8031 auto opSlice() { return this; } 8032 8033 private: 8034 RangeOfRanges _input; 8035 } 8036 8037 @safe unittest 8038 { 8039 // Boundary case: transpose of empty range should be empty 8040 int[][] ror = []; 8041 assert(transposed(ror).empty); 8042 } 8043 8044 // https://issues.dlang.org/show_bug.cgi?id=9507 8045 @safe unittest 8046 { 8047 import std.algorithm.comparison : equal; 8048 8049 auto r = [[1,2], [3], [4,5], [], [6]]; 8050 assert(r.transposed.equal!equal([ 8051 [1, 3, 4, 6], 8052 [2, 5] 8053 ])); 8054 } 8055 8056 // https://issues.dlang.org/show_bug.cgi?id=17742 8057 @safe unittest 8058 { 8059 import std.algorithm.iteration : map; 8060 import std.algorithm.comparison : equal; 8061 auto ror = 5.iota.map!(y => 5.iota.map!(x => x * y).array).array; 8062 assert(ror[3][2] == 6); 8063 auto result = transposed!(TransverseOptions.assumeNotJagged)(ror); 8064 assert(result[2][3] == 6); 8065 8066 auto x = [[1,2,3],[4,5,6]]; 8067 auto y = transposed!(TransverseOptions.assumeNotJagged)(x); 8068 assert(y.front.equal([1,4])); 8069 assert(y[0].equal([1,4])); 8070 assert(y[0][0] == 1); 8071 assert(y[1].equal([2,5])); 8072 assert(y[1][1] == 5); 8073 8074 auto yy = transposed!(TransverseOptions.enforceNotJagged)(x); 8075 assert(yy.front.equal([1,4])); 8076 assert(yy[0].equal([1,4])); 8077 assert(yy[0][0] == 1); 8078 assert(yy[1].equal([2,5])); 8079 assert(yy[1][1] == 5); 8080 8081 auto z = x.transposed; // assumeJagged 8082 assert(z.front.equal([1,4])); 8083 assert(z[0].equal([1,4])); 8084 assert(!is(typeof(z[0][0]))); 8085 } 8086 8087 @safe unittest 8088 { 8089 import std.exception : assertThrown; 8090 8091 auto r = [[1,2], [3], [4,5], [], [6]]; 8092 assertThrown(r.transposed!(TransverseOptions.enforceNotJagged)); 8093 } 8094 8095 /** 8096 Given a range of ranges, returns a range of ranges where the $(I i)'th subrange 8097 contains the $(I i)'th elements of the original subranges. 8098 8099 Params: 8100 opt = Controls the assumptions the function makes about the lengths of the ranges (i.e. jagged or not) 8101 rr = Range of ranges 8102 */ 8103 Transposed!(RangeOfRanges, opt) transposed 8104 (TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) 8105 (RangeOfRanges rr) 8106 if (isForwardRange!RangeOfRanges && 8107 isInputRange!(ElementType!RangeOfRanges) && 8108 hasAssignableElements!RangeOfRanges) 8109 { 8110 return Transposed!(RangeOfRanges, opt)(rr); 8111 } 8112 8113 /// 8114 @safe unittest 8115 { 8116 import std.algorithm.comparison : equal; 8117 int[][] ror = [ 8118 [1, 2, 3], 8119 [4, 5, 6] 8120 ]; 8121 auto xp = transposed(ror); 8122 assert(equal!"a.equal(b)"(xp, [ 8123 [1, 4], 8124 [2, 5], 8125 [3, 6] 8126 ])); 8127 } 8128 8129 /// 8130 @safe unittest 8131 { 8132 int[][] x = new int[][2]; 8133 x[0] = [1, 2]; 8134 x[1] = [3, 4]; 8135 auto tr = transposed(x); 8136 int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ]; 8137 uint i; 8138 8139 foreach (e; tr) 8140 { 8141 assert(array(e) == witness[i++]); 8142 } 8143 } 8144 8145 // https://issues.dlang.org/show_bug.cgi?id=8764 8146 @safe unittest 8147 { 8148 import std.algorithm.comparison : equal; 8149 ulong[] t0 = [ 123 ]; 8150 8151 assert(!hasAssignableElements!(typeof(t0[].chunks(1)))); 8152 assert(!is(typeof(transposed(t0[].chunks(1))))); 8153 assert(is(typeof(transposed(t0[].chunks(1).array())))); 8154 8155 auto t1 = transposed(t0[].chunks(1).array()); 8156 assert(equal!"a.equal(b)"(t1, [[123]])); 8157 } 8158 8159 /** 8160 This struct takes two ranges, `source` and `indices`, and creates a view 8161 of `source` as if its elements were reordered according to `indices`. 8162 `indices` may include only a subset of the elements of `source` and 8163 may also repeat elements. 8164 8165 `Source` must be a random access range. The returned range will be 8166 bidirectional or random-access if `Indices` is bidirectional or 8167 random-access, respectively. 8168 */ 8169 struct Indexed(Source, Indices) 8170 if (isRandomAccessRange!Source && isInputRange!Indices && 8171 is(typeof(Source.init[ElementType!(Indices).init]))) 8172 { 8173 this(Source source, Indices indices) 8174 { 8175 this._source = source; 8176 this._indices = indices; 8177 } 8178 8179 /// Range primitives 8180 @property auto ref front() 8181 { 8182 assert(!empty, "Attempting to fetch the front of an empty Indexed"); 8183 return _source[_indices.front]; 8184 } 8185 8186 /// Ditto 8187 void popFront() 8188 { 8189 assert(!empty, "Attempting to popFront an empty Indexed"); 8190 _indices.popFront(); 8191 } 8192 8193 static if (isInfinite!Indices) 8194 { 8195 enum bool empty = false; 8196 } 8197 else 8198 { 8199 /// Ditto 8200 @property bool empty() 8201 { 8202 return _indices.empty; 8203 } 8204 } 8205 8206 static if (isForwardRange!Indices) 8207 { 8208 /// Ditto 8209 @property typeof(this) save() 8210 { 8211 // Don't need to save _source because it's never consumed. 8212 return typeof(this)(_source, _indices.save); 8213 } 8214 } 8215 8216 /// Ditto 8217 static if (hasAssignableElements!Source) 8218 { 8219 @property auto ref front(ElementType!Source newVal) 8220 { 8221 assert(!empty); 8222 return _source[_indices.front] = newVal; 8223 } 8224 } 8225 8226 8227 static if (hasMobileElements!Source) 8228 { 8229 /// Ditto 8230 auto moveFront() 8231 { 8232 assert(!empty); 8233 return _source.moveAt(_indices.front); 8234 } 8235 } 8236 8237 static if (isBidirectionalRange!Indices) 8238 { 8239 /// Ditto 8240 @property auto ref back() 8241 { 8242 assert(!empty, "Attempting to fetch the back of an empty Indexed"); 8243 return _source[_indices.back]; 8244 } 8245 8246 /// Ditto 8247 void popBack() 8248 { 8249 assert(!empty, "Attempting to popBack an empty Indexed"); 8250 _indices.popBack(); 8251 } 8252 8253 /// Ditto 8254 static if (hasAssignableElements!Source) 8255 { 8256 @property auto ref back(ElementType!Source newVal) 8257 { 8258 assert(!empty); 8259 return _source[_indices.back] = newVal; 8260 } 8261 } 8262 8263 8264 static if (hasMobileElements!Source) 8265 { 8266 /// Ditto 8267 auto moveBack() 8268 { 8269 assert(!empty); 8270 return _source.moveAt(_indices.back); 8271 } 8272 } 8273 } 8274 8275 mixin ImplementLength!_indices; 8276 8277 static if (isRandomAccessRange!Indices) 8278 { 8279 /// Ditto 8280 auto ref opIndex(size_t index) 8281 { 8282 return _source[_indices[index]]; 8283 } 8284 8285 static if (hasSlicing!Indices) 8286 { 8287 /// Ditto 8288 typeof(this) opSlice(size_t a, size_t b) 8289 { 8290 return typeof(this)(_source, _indices[a .. b]); 8291 } 8292 } 8293 8294 8295 static if (hasAssignableElements!Source) 8296 { 8297 /// Ditto 8298 auto opIndexAssign(ElementType!Source newVal, size_t index) 8299 { 8300 return _source[_indices[index]] = newVal; 8301 } 8302 } 8303 8304 8305 static if (hasMobileElements!Source) 8306 { 8307 /// Ditto 8308 auto moveAt(size_t index) 8309 { 8310 return _source.moveAt(_indices[index]); 8311 } 8312 } 8313 } 8314 8315 // All this stuff is useful if someone wants to index an Indexed 8316 // without adding a layer of indirection. 8317 8318 /** 8319 Returns the source range. 8320 */ 8321 @property Source source() 8322 { 8323 return _source; 8324 } 8325 8326 /** 8327 Returns the indices range. 8328 */ 8329 @property Indices indices() 8330 { 8331 return _indices; 8332 } 8333 8334 static if (isRandomAccessRange!Indices) 8335 { 8336 /** 8337 Returns the physical index into the source range corresponding to a 8338 given logical index. This is useful, for example, when indexing 8339 an `Indexed` without adding another layer of indirection. 8340 */ 8341 size_t physicalIndex(size_t logicalIndex) 8342 { 8343 return _indices[logicalIndex]; 8344 } 8345 8346 /// 8347 @safe unittest 8348 { 8349 auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); 8350 assert(ind.physicalIndex(0) == 1); 8351 } 8352 } 8353 8354 private: 8355 Source _source; 8356 Indices _indices; 8357 8358 } 8359 8360 /// Ditto 8361 Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indices) 8362 { 8363 return typeof(return)(source, indices); 8364 } 8365 8366 /// 8367 @safe unittest 8368 { 8369 import std.algorithm.comparison : equal; 8370 auto source = [1, 2, 3, 4, 5]; 8371 auto indices = [4, 3, 1, 2, 0, 4]; 8372 auto ind = indexed(source, indices); 8373 assert(equal(ind, [5, 4, 2, 3, 1, 5])); 8374 assert(equal(retro(ind), [5, 1, 3, 2, 4, 5])); 8375 } 8376 8377 @safe unittest 8378 { 8379 { 8380 auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); 8381 assert(ind.physicalIndex(0) == 1); 8382 } 8383 8384 auto source = [1, 2, 3, 4, 5]; 8385 auto indices = [4, 3, 1, 2, 0, 4]; 8386 auto ind = indexed(source, indices); 8387 8388 // When elements of indices are duplicated and Source has lvalue elements, 8389 // these are aliased in ind. 8390 ind[0]++; 8391 assert(ind[0] == 6); 8392 assert(ind[5] == 6); 8393 } 8394 8395 @safe unittest 8396 { 8397 import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, 8398 propagatesRangeType, RangeType; 8399 8400 foreach (DummyType; AllDummyRanges) 8401 { 8402 auto d = DummyType.init; 8403 auto r = indexed([1, 2, 3, 4, 5], d); 8404 static assert(propagatesRangeType!(DummyType, typeof(r))); 8405 static assert(propagatesLength!(DummyType, typeof(r))); 8406 } 8407 } 8408 8409 /** 8410 This range iterates over fixed-sized chunks of size `chunkSize` of a 8411 `source` range. `Source` must be an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). 8412 `chunkSize` must be greater than zero. 8413 8414 If `!isInfinite!Source` and `source.walkLength` is not evenly 8415 divisible by `chunkSize`, the back element of this range will contain 8416 fewer than `chunkSize` elements. 8417 8418 If `Source` is a forward range, the resulting range will be forward ranges as 8419 well. Otherwise, the resulting chunks will be input ranges consuming the same 8420 input: iterating over `front` will shrink the chunk such that subsequent 8421 invocations of `front` will no longer return the full chunk, and calling 8422 `popFront` on the outer range will invalidate any lingering references to 8423 previous values of `front`. 8424 8425 Params: 8426 source = Range from which the chunks will be selected 8427 chunkSize = Chunk size 8428 8429 See_Also: $(LREF slide) 8430 8431 Returns: Range of chunks. 8432 */ 8433 struct Chunks(Source) 8434 if (isInputRange!Source) 8435 { 8436 static if (isForwardRange!Source) 8437 { 8438 /// Standard constructor 8439 this(Source source, size_t chunkSize) 8440 { 8441 assert(chunkSize != 0, "Cannot create a Chunk with an empty chunkSize"); 8442 _source = source; 8443 _chunkSize = chunkSize; 8444 } 8445 8446 /// Input range primitives. Always present. 8447 @property auto front() 8448 { 8449 assert(!empty, "Attempting to fetch the front of an empty Chunks"); 8450 return _source.save.take(_chunkSize); 8451 } 8452 8453 /// Ditto 8454 void popFront() 8455 { 8456 assert(!empty, "Attempting to popFront and empty Chunks"); 8457 _source.popFrontN(_chunkSize); 8458 } 8459 8460 static if (!isInfinite!Source) 8461 /// Ditto 8462 @property bool empty() 8463 { 8464 return _source.empty; 8465 } 8466 else 8467 // undocumented 8468 enum empty = false; 8469 8470 /// Forward range primitives. Only present if `Source` is a forward range. 8471 @property typeof(this) save() 8472 { 8473 return typeof(this)(_source.save, _chunkSize); 8474 } 8475 8476 static if (hasLength!Source) 8477 { 8478 /// Length. Only if `hasLength!Source` is `true` 8479 @property size_t length() 8480 { 8481 // Note: _source.length + _chunkSize may actually overflow. 8482 // We cast to ulong to mitigate the problem on x86 machines. 8483 // For x64 machines, we just suppose we'll never overflow. 8484 // The "safe" code would require either an extra branch, or a 8485 // modulo operation, which is too expensive for such a rare case 8486 return cast(size_t)((cast(ulong)(_source.length) + _chunkSize - 1) / _chunkSize); 8487 } 8488 //Note: No point in defining opDollar here without slicing. 8489 //opDollar is defined below in the hasSlicing!Source section 8490 } 8491 8492 static if (hasSlicing!Source) 8493 { 8494 //Used for various purposes 8495 private enum hasSliceToEnd = is(typeof(Source.init[_chunkSize .. $]) == Source); 8496 8497 /** 8498 Indexing and slicing operations. Provided only if 8499 `hasSlicing!Source` is `true`. 8500 */ 8501 auto opIndex(size_t index) 8502 { 8503 immutable start = index * _chunkSize; 8504 immutable end = start + _chunkSize; 8505 8506 static if (isInfinite!Source) 8507 return _source[start .. end]; 8508 else 8509 { 8510 import std.algorithm.comparison : min; 8511 immutable len = _source.length; 8512 assert(start < len, "chunks index out of bounds"); 8513 return _source[start .. min(end, len)]; 8514 } 8515 } 8516 8517 /// Ditto 8518 static if (hasLength!Source) 8519 typeof(this) opSlice(size_t lower, size_t upper) 8520 { 8521 import std.algorithm.comparison : min; 8522 assert(lower <= upper && upper <= length, "chunks slicing index out of bounds"); 8523 immutable len = _source.length; 8524 return chunks(_source[min(lower * _chunkSize, len) .. min(upper * _chunkSize, len)], _chunkSize); 8525 } 8526 else static if (hasSliceToEnd) 8527 //For slicing an infinite chunk, we need to slice the source to the end. 8528 typeof(takeExactly(this, 0)) opSlice(size_t lower, size_t upper) 8529 { 8530 assert(lower <= upper, "chunks slicing index out of bounds"); 8531 return chunks(_source[lower * _chunkSize .. $], _chunkSize).takeExactly(upper - lower); 8532 } 8533 8534 static if (isInfinite!Source) 8535 { 8536 static if (hasSliceToEnd) 8537 { 8538 private static struct DollarToken{} 8539 DollarToken opDollar() 8540 { 8541 return DollarToken(); 8542 } 8543 //Slice to dollar 8544 typeof(this) opSlice(size_t lower, DollarToken) 8545 { 8546 return typeof(this)(_source[lower * _chunkSize .. $], _chunkSize); 8547 } 8548 } 8549 } 8550 else 8551 { 8552 //Dollar token carries a static type, with no extra information. 8553 //It can lazily transform into _source.length on algorithmic 8554 //operations such as : chunks[$/2, $-1]; 8555 private static struct DollarToken 8556 { 8557 Chunks!Source* mom; 8558 @property size_t momLength() 8559 { 8560 return mom.length; 8561 } 8562 alias momLength this; 8563 } 8564 DollarToken opDollar() 8565 { 8566 return DollarToken(&this); 8567 } 8568 8569 //Slice overloads optimized for using dollar. Without this, to slice to end, we would... 8570 //1. Evaluate chunks.length 8571 //2. Multiply by _chunksSize 8572 //3. To finally just compare it (with min) to the original length of source (!) 8573 //These overloads avoid that. 8574 typeof(this) opSlice(DollarToken, DollarToken) 8575 { 8576 static if (hasSliceToEnd) 8577 return chunks(_source[$ .. $], _chunkSize); 8578 else 8579 { 8580 immutable len = _source.length; 8581 return chunks(_source[len .. len], _chunkSize); 8582 } 8583 } 8584 typeof(this) opSlice(size_t lower, DollarToken) 8585 { 8586 import std.algorithm.comparison : min; 8587 assert(lower <= length, "chunks slicing index out of bounds"); 8588 static if (hasSliceToEnd) 8589 return chunks(_source[min(lower * _chunkSize, _source.length) .. $], _chunkSize); 8590 else 8591 { 8592 immutable len = _source.length; 8593 return chunks(_source[min(lower * _chunkSize, len) .. len], _chunkSize); 8594 } 8595 } 8596 typeof(this) opSlice(DollarToken, size_t upper) 8597 { 8598 assert(upper == length, "chunks slicing index out of bounds"); 8599 return this[$ .. $]; 8600 } 8601 } 8602 } 8603 8604 //Bidirectional range primitives 8605 static if (hasSlicing!Source && hasLength!Source) 8606 { 8607 /** 8608 Bidirectional range primitives. Provided only if both 8609 `hasSlicing!Source` and `hasLength!Source` are `true`. 8610 */ 8611 @property auto back() 8612 { 8613 assert(!empty, "back called on empty chunks"); 8614 immutable len = _source.length; 8615 immutable start = (len - 1) / _chunkSize * _chunkSize; 8616 return _source[start .. len]; 8617 } 8618 8619 /// Ditto 8620 void popBack() 8621 { 8622 assert(!empty, "popBack() called on empty chunks"); 8623 immutable end = (_source.length - 1) / _chunkSize * _chunkSize; 8624 _source = _source[0 .. end]; 8625 } 8626 } 8627 8628 private: 8629 Source _source; 8630 size_t _chunkSize; 8631 } 8632 else // is input range only 8633 { 8634 import std.typecons : RefCounted; 8635 8636 static struct Chunk 8637 { 8638 private RefCounted!Impl impl; 8639 8640 @property bool empty() { return impl.curSizeLeft == 0 || impl.r.empty; } 8641 @property auto front() { return impl.r.front; } 8642 void popFront() 8643 { 8644 assert(impl.curSizeLeft > 0 && !impl.r.empty); 8645 impl.curSizeLeft--; 8646 impl.r.popFront(); 8647 } 8648 } 8649 8650 static struct Impl 8651 { 8652 private Source r; 8653 private size_t chunkSize; 8654 private size_t curSizeLeft; 8655 } 8656 8657 private RefCounted!Impl impl; 8658 8659 private this(Source r, size_t chunkSize) 8660 { 8661 impl = RefCounted!Impl(r, r.empty ? 0 : chunkSize, chunkSize); 8662 } 8663 8664 @property bool empty() { return impl.chunkSize == 0; } 8665 @property Chunk front() return { return Chunk(impl); } 8666 8667 void popFront() 8668 { 8669 impl.curSizeLeft -= impl.r.popFrontN(impl.curSizeLeft); 8670 if (!impl.r.empty) 8671 impl.curSizeLeft = impl.chunkSize; 8672 else 8673 impl.chunkSize = 0; 8674 } 8675 8676 static assert(isInputRange!(typeof(this))); 8677 } 8678 } 8679 8680 /// Ditto 8681 Chunks!Source chunks(Source)(Source source, size_t chunkSize) 8682 if (isInputRange!Source) 8683 { 8684 return typeof(return)(source, chunkSize); 8685 } 8686 8687 /// 8688 @safe unittest 8689 { 8690 import std.algorithm.comparison : equal; 8691 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8692 auto chunks = chunks(source, 4); 8693 assert(chunks[0] == [1, 2, 3, 4]); 8694 assert(chunks[1] == [5, 6, 7, 8]); 8695 assert(chunks[2] == [9, 10]); 8696 assert(chunks.back == chunks[2]); 8697 assert(chunks.front == chunks[0]); 8698 assert(chunks.length == 3); 8699 assert(equal(retro(array(chunks)), array(retro(chunks)))); 8700 } 8701 8702 /// Non-forward input ranges are supported, but with limited semantics. 8703 @system /*@safe*/ unittest // FIXME: can't be @safe because RefCounted isn't. 8704 { 8705 import std.algorithm.comparison : equal; 8706 8707 int i; 8708 8709 // The generator doesn't save state, so it cannot be a forward range. 8710 auto inputRange = generate!(() => ++i).take(10); 8711 8712 // We can still process it in chunks, but it will be single-pass only. 8713 auto chunked = inputRange.chunks(2); 8714 8715 assert(chunked.front.equal([1, 2])); 8716 assert(chunked.front.empty); // Iterating the chunk has consumed it 8717 chunked.popFront; 8718 assert(chunked.front.equal([3, 4])); 8719 } 8720 8721 @system /*@safe*/ unittest 8722 { 8723 import std.algorithm.comparison : equal; 8724 import std.internal.test.dummyrange : ReferenceInputRange; 8725 8726 auto data = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 8727 auto r = new ReferenceInputRange!int(data).chunks(3); 8728 assert(r.equal!equal([ 8729 [ 1, 2, 3 ], 8730 [ 4, 5, 6 ], 8731 [ 7, 8, 9 ], 8732 [ 10 ] 8733 ])); 8734 8735 auto data2 = [ 1, 2, 3, 4, 5, 6 ]; 8736 auto r2 = new ReferenceInputRange!int(data2).chunks(3); 8737 assert(r2.equal!equal([ 8738 [ 1, 2, 3 ], 8739 [ 4, 5, 6 ] 8740 ])); 8741 8742 auto data3 = [ 1, 2, 3, 4, 5 ]; 8743 auto r3 = new ReferenceInputRange!int(data3).chunks(2); 8744 assert(r3.front.equal([1, 2])); 8745 r3.popFront(); 8746 assert(!r3.empty); 8747 r3.popFront(); 8748 assert(r3.front.equal([5])); 8749 } 8750 8751 @safe unittest 8752 { 8753 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8754 auto chunks = chunks(source, 4); 8755 auto chunks2 = chunks.save; 8756 chunks.popFront(); 8757 assert(chunks[0] == [5, 6, 7, 8]); 8758 assert(chunks[1] == [9, 10]); 8759 chunks2.popBack(); 8760 assert(chunks2[1] == [5, 6, 7, 8]); 8761 assert(chunks2.length == 2); 8762 8763 static assert(isRandomAccessRange!(typeof(chunks))); 8764 } 8765 8766 @safe unittest 8767 { 8768 import std.algorithm.comparison : equal; 8769 8770 //Extra toying with slicing and indexing. 8771 auto chunks1 = [0, 0, 1, 1, 2, 2, 3, 3, 4].chunks(2); 8772 auto chunks2 = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4].chunks(2); 8773 8774 assert(chunks1.length == 5); 8775 assert(chunks2.length == 5); 8776 assert(chunks1[4] == [4]); 8777 assert(chunks2[4] == [4, 4]); 8778 assert(chunks1.back == [4]); 8779 assert(chunks2.back == [4, 4]); 8780 8781 assert(chunks1[0 .. 1].equal([[0, 0]])); 8782 assert(chunks1[0 .. 2].equal([[0, 0], [1, 1]])); 8783 assert(chunks1[4 .. 5].equal([[4]])); 8784 assert(chunks2[4 .. 5].equal([[4, 4]])); 8785 8786 assert(chunks1[0 .. 0].equal((int[][]).init)); 8787 assert(chunks1[5 .. 5].equal((int[][]).init)); 8788 assert(chunks2[5 .. 5].equal((int[][]).init)); 8789 8790 //Fun with opDollar 8791 assert(chunks1[$ .. $].equal((int[][]).init)); //Quick 8792 assert(chunks2[$ .. $].equal((int[][]).init)); //Quick 8793 assert(chunks1[$ - 1 .. $].equal([[4]])); //Semiquick 8794 assert(chunks2[$ - 1 .. $].equal([[4, 4]])); //Semiquick 8795 assert(chunks1[$ .. 5].equal((int[][]).init)); //Semiquick 8796 assert(chunks2[$ .. 5].equal((int[][]).init)); //Semiquick 8797 8798 assert(chunks1[$ / 2 .. $ - 1].equal([[2, 2], [3, 3]])); //Slow 8799 } 8800 8801 @safe unittest 8802 { 8803 import std.algorithm.comparison : equal; 8804 import std.algorithm.iteration : filter; 8805 8806 //ForwardRange 8807 auto r = filter!"true"([1, 2, 3, 4, 5]).chunks(2); 8808 assert(equal!"equal(a, b)"(r, [[1, 2], [3, 4], [5]])); 8809 8810 //InfiniteRange w/o RA 8811 auto fibsByPairs = recurrence!"a[n-1] + a[n-2]"(1, 1).chunks(2); 8812 assert(equal!`equal(a, b)`(fibsByPairs.take(2), [[ 1, 1], [ 2, 3]])); 8813 8814 //InfiniteRange w/ RA and slicing 8815 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 8816 auto oddsByPairs = odds.chunks(2); 8817 assert(equal!`equal(a, b)`(oddsByPairs.take(2), [[ 1, 3], [ 5, 7]])); 8818 8819 //Requires phobos#991 for Sequence to have slice to end 8820 static assert(hasSlicing!(typeof(odds))); 8821 assert(equal!`equal(a, b)`(oddsByPairs[3 .. 5], [[13, 15], [17, 19]])); 8822 assert(equal!`equal(a, b)`(oddsByPairs[3 .. $].take(2), [[13, 15], [17, 19]])); 8823 } 8824 8825 8826 8827 /** 8828 This range splits a `source` range into `chunkCount` chunks of 8829 approximately equal length. `Source` must be a forward range with 8830 known length. 8831 8832 Unlike $(LREF chunks), `evenChunks` takes a chunk count (not size). 8833 The returned range will contain zero or more $(D source.length / 8834 chunkCount + 1) elements followed by $(D source.length / chunkCount) 8835 elements. If $(D source.length < chunkCount), some chunks will be empty. 8836 8837 `chunkCount` must not be zero, unless `source` is also empty. 8838 */ 8839 struct EvenChunks(Source) 8840 if (isForwardRange!Source && hasLength!Source) 8841 { 8842 /// Standard constructor 8843 this(Source source, size_t chunkCount) 8844 { 8845 assert(chunkCount != 0 || source.empty, "Cannot create EvenChunks with a zero chunkCount"); 8846 _source = source; 8847 _chunkCount = chunkCount; 8848 } 8849 8850 /// Forward range primitives. Always present. 8851 @property auto front() 8852 { 8853 assert(!empty, "Attempting to fetch the front of an empty evenChunks"); 8854 return _source.save.take(_chunkPos(1)); 8855 } 8856 8857 /// Ditto 8858 void popFront() 8859 { 8860 assert(!empty, "Attempting to popFront an empty evenChunks"); 8861 _source.popFrontN(_chunkPos(1)); 8862 _chunkCount--; 8863 } 8864 8865 /// Ditto 8866 @property bool empty() 8867 { 8868 return _chunkCount == 0; 8869 } 8870 8871 /// Ditto 8872 @property typeof(this) save() 8873 { 8874 return typeof(this)(_source.save, _chunkCount); 8875 } 8876 8877 /// Length 8878 @property size_t length() const 8879 { 8880 return _chunkCount; 8881 } 8882 //Note: No point in defining opDollar here without slicing. 8883 //opDollar is defined below in the hasSlicing!Source section 8884 8885 static if (hasSlicing!Source) 8886 { 8887 /** 8888 Indexing, slicing and bidirectional operations and range primitives. 8889 Provided only if `hasSlicing!Source` is `true`. 8890 */ 8891 auto opIndex(size_t index) 8892 { 8893 assert(index < _chunkCount, "evenChunks index out of bounds"); 8894 return _source[_chunkPos(index) .. _chunkPos(index+1)]; 8895 } 8896 8897 /// Ditto 8898 typeof(this) opSlice(size_t lower, size_t upper) 8899 { 8900 assert(lower <= upper && upper <= length, "evenChunks slicing index out of bounds"); 8901 return evenChunks(_source[_chunkPos(lower) .. _chunkPos(upper)], upper - lower); 8902 } 8903 8904 /// Ditto 8905 @property auto back() 8906 { 8907 assert(!empty, "back called on empty evenChunks"); 8908 return _source[_chunkPos(_chunkCount - 1) .. _source.length]; 8909 } 8910 8911 /// Ditto 8912 void popBack() 8913 { 8914 assert(!empty, "popBack() called on empty evenChunks"); 8915 _source = _source[0 .. _chunkPos(_chunkCount - 1)]; 8916 _chunkCount--; 8917 } 8918 } 8919 8920 private: 8921 Source _source; 8922 size_t _chunkCount; 8923 8924 size_t _chunkPos(size_t i) 8925 { 8926 /* 8927 _chunkCount = 5, _source.length = 13: 8928 8929 chunk0 8930 | chunk3 8931 | | 8932 v v 8933 +-+-+-+-+-+ ^ 8934 |0|3|.| | | | 8935 +-+-+-+-+-+ | div 8936 |1|4|.| | | | 8937 +-+-+-+-+-+ v 8938 |2|5|.| 8939 +-+-+-+ 8940 8941 <-----> 8942 mod 8943 8944 <---------> 8945 _chunkCount 8946 8947 One column is one chunk. 8948 popFront and popBack pop the left-most 8949 and right-most column, respectively. 8950 */ 8951 8952 auto div = _source.length / _chunkCount; 8953 auto mod = _source.length % _chunkCount; 8954 auto pos = i <= mod 8955 ? i * (div+1) 8956 : mod * (div+1) + (i-mod) * div 8957 ; 8958 //auto len = i < mod 8959 // ? div+1 8960 // : div 8961 //; 8962 return pos; 8963 } 8964 } 8965 8966 /// Ditto 8967 EvenChunks!Source evenChunks(Source)(Source source, size_t chunkCount) 8968 if (isForwardRange!Source && hasLength!Source) 8969 { 8970 return typeof(return)(source, chunkCount); 8971 } 8972 8973 /// 8974 @safe unittest 8975 { 8976 import std.algorithm.comparison : equal; 8977 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8978 auto chunks = evenChunks(source, 3); 8979 assert(chunks[0] == [1, 2, 3, 4]); 8980 assert(chunks[1] == [5, 6, 7]); 8981 assert(chunks[2] == [8, 9, 10]); 8982 } 8983 8984 @safe unittest 8985 { 8986 import std.algorithm.comparison : equal; 8987 8988 auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 8989 auto chunks = evenChunks(source, 3); 8990 assert(chunks.back == chunks[2]); 8991 assert(chunks.front == chunks[0]); 8992 assert(chunks.length == 3); 8993 assert(equal(retro(array(chunks)), array(retro(chunks)))); 8994 8995 auto chunks2 = chunks.save; 8996 chunks.popFront(); 8997 assert(chunks[0] == [5, 6, 7]); 8998 assert(chunks[1] == [8, 9, 10]); 8999 chunks2.popBack(); 9000 assert(chunks2[1] == [5, 6, 7]); 9001 assert(chunks2.length == 2); 9002 9003 static assert(isRandomAccessRange!(typeof(chunks))); 9004 } 9005 9006 @safe unittest 9007 { 9008 import std.algorithm.comparison : equal; 9009 9010 int[] source = []; 9011 auto chunks = source.evenChunks(0); 9012 assert(chunks.length == 0); 9013 chunks = source.evenChunks(3); 9014 assert(equal(chunks, [[], [], []])); 9015 chunks = [1, 2, 3].evenChunks(5); 9016 assert(equal(chunks, [[1], [2], [3], [], []])); 9017 } 9018 9019 /** 9020 A fixed-sized sliding window iteration 9021 of size `windowSize` over a `source` range by a custom `stepSize`. 9022 9023 The `Source` range must be at least a $(REF_ALTTEXT ForwardRange, isForwardRange, std,range,primitives) 9024 and the `windowSize` must be greater than zero. 9025 9026 For `windowSize = 1` it splits the range into single element groups (aka `unflatten`) 9027 For `windowSize = 2` it is similar to `zip(source, source.save.dropOne)`. 9028 9029 Params: 9030 f = Whether the last element has fewer elements than `windowSize` 9031 it should be be ignored (`No.withPartial`) or added (`Yes.withPartial`) 9032 source = Range from which the slide will be selected 9033 windowSize = Sliding window size 9034 stepSize = Steps between the windows (by default 1) 9035 9036 Returns: Range of all sliding windows with propagated bi-directionality, 9037 forwarding, random access, and slicing. 9038 9039 Note: To avoid performance overhead, $(REF_ALTTEXT bi-directionality, isBidirectionalRange, std,range,primitives) 9040 is only available when $(REF hasSlicing, std,range,primitives) 9041 and $(REF hasLength, std,range,primitives) are true. 9042 9043 See_Also: $(LREF chunks) 9044 */ 9045 auto slide(Flag!"withPartial" f = Yes.withPartial, 9046 Source)(Source source, size_t windowSize, size_t stepSize = 1) 9047 if (isForwardRange!Source) 9048 { 9049 return Slides!(f, Source)(source, windowSize, stepSize); 9050 } 9051 9052 /// Iterate over ranges with windows 9053 @safe pure nothrow unittest 9054 { 9055 import std.algorithm.comparison : equal; 9056 9057 assert([0, 1, 2, 3].slide(2).equal!equal( 9058 [[0, 1], [1, 2], [2, 3]] 9059 )); 9060 9061 assert(5.iota.slide(3).equal!equal( 9062 [[0, 1, 2], [1, 2, 3], [2, 3, 4]] 9063 )); 9064 } 9065 9066 /// set a custom stepsize (default 1) 9067 @safe pure nothrow unittest 9068 { 9069 import std.algorithm.comparison : equal; 9070 9071 assert(6.iota.slide(1, 2).equal!equal( 9072 [[0], [2], [4]] 9073 )); 9074 9075 assert(6.iota.slide(2, 4).equal!equal( 9076 [[0, 1], [4, 5]] 9077 )); 9078 9079 assert(iota(7).slide(2, 2).equal!equal( 9080 [[0, 1], [2, 3], [4, 5], [6]] 9081 )); 9082 9083 assert(iota(12).slide(2, 4).equal!equal( 9084 [[0, 1], [4, 5], [8, 9]] 9085 )); 9086 } 9087 9088 /// Allow the last slide to have fewer elements than windowSize 9089 @safe pure nothrow unittest 9090 { 9091 import std.algorithm.comparison : equal; 9092 9093 assert(3.iota.slide!(No.withPartial)(4).empty); 9094 assert(3.iota.slide!(Yes.withPartial)(4).equal!equal( 9095 [[0, 1, 2]] 9096 )); 9097 } 9098 9099 /// Count all the possible substrings of length 2 9100 @safe pure nothrow unittest 9101 { 9102 import std.algorithm.iteration : each; 9103 9104 int[dstring] d; 9105 "AGAGA"d.slide!(Yes.withPartial)(2).each!(a => d[a]++); 9106 assert(d == ["AG"d: 2, "GA"d: 2]); 9107 } 9108 9109 /// withPartial only has an effect if last element in the range doesn't have the full size 9110 @safe pure nothrow unittest 9111 { 9112 import std.algorithm.comparison : equal; 9113 9114 assert(5.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4]])); 9115 assert(6.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5]])); 9116 assert(7.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]])); 9117 9118 assert(5.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]])); 9119 assert(6.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]])); 9120 assert(7.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]])); 9121 } 9122 9123 private struct Slides(Flag!"withPartial" withPartial = Yes.withPartial, Source) 9124 if (isForwardRange!Source) 9125 { 9126 private: 9127 Source source; 9128 size_t windowSize; 9129 size_t stepSize; 9130 9131 static if (hasLength!Source) 9132 { 9133 enum needsEndTracker = false; 9134 } 9135 else 9136 { 9137 // If there's no information about the length, track needs to be kept manually 9138 Source nextSource; 9139 enum needsEndTracker = true; 9140 } 9141 9142 bool _empty; 9143 9144 static if (hasSlicing!Source) 9145 enum hasSliceToEnd = hasSlicing!Source && is(typeof(Source.init[0 .. $]) == Source); 9146 9147 static if (withPartial) 9148 bool hasShownPartialBefore; 9149 9150 public: 9151 /// Standard constructor 9152 this(Source source, size_t windowSize, size_t stepSize) 9153 { 9154 assert(windowSize > 0, "windowSize must be greater than zero"); 9155 assert(stepSize > 0, "stepSize must be greater than zero"); 9156 this.source = source; 9157 this.windowSize = windowSize; 9158 this.stepSize = stepSize; 9159 9160 static if (needsEndTracker) 9161 { 9162 // `nextSource` is used to "look one step into the future" and check for the end 9163 // this means `nextSource` is advanced by `stepSize` on every `popFront` 9164 nextSource = source.save; 9165 auto poppedElems = nextSource.popFrontN(windowSize); 9166 } 9167 9168 if (source.empty) 9169 { 9170 _empty = true; 9171 return; 9172 } 9173 9174 static if (withPartial) 9175 { 9176 static if (needsEndTracker) 9177 { 9178 if (nextSource.empty) 9179 hasShownPartialBefore = true; 9180 } 9181 else 9182 { 9183 if (source.length <= windowSize) 9184 hasShownPartialBefore = true; 9185 } 9186 } 9187 else 9188 { 9189 // empty source range is needed, s.t. length, slicing etc. works properly 9190 static if (needsEndTracker) 9191 { 9192 if (poppedElems < windowSize) 9193 _empty = true; 9194 } 9195 else 9196 { 9197 if (source.length < windowSize) 9198 _empty = true; 9199 } 9200 } 9201 } 9202 9203 /// Forward range primitives. Always present. 9204 @property auto front() 9205 { 9206 assert(!empty, "Attempting to access front on an empty slide."); 9207 static if (hasSlicing!Source && hasLength!Source) 9208 { 9209 static if (withPartial) 9210 { 9211 import std.algorithm.comparison : min; 9212 return source[0 .. min(windowSize, source.length)]; 9213 } 9214 else 9215 { 9216 assert(windowSize <= source.length, "The last element is smaller than the current windowSize."); 9217 return source[0 .. windowSize]; 9218 } 9219 } 9220 else 9221 { 9222 static if (withPartial) 9223 return source.save.take(windowSize); 9224 else 9225 return source.save.takeExactly(windowSize); 9226 } 9227 } 9228 9229 /// Ditto 9230 void popFront() 9231 { 9232 assert(!empty, "Attempting to call popFront() on an empty slide."); 9233 source.popFrontN(stepSize); 9234 9235 if (source.empty) 9236 { 9237 _empty = true; 9238 return; 9239 } 9240 9241 static if (withPartial) 9242 { 9243 if (hasShownPartialBefore) 9244 _empty = true; 9245 } 9246 9247 static if (needsEndTracker) 9248 { 9249 // Check the upcoming slide 9250 auto poppedElements = nextSource.popFrontN(stepSize); 9251 static if (withPartial) 9252 { 9253 if (poppedElements < stepSize || nextSource.empty) 9254 hasShownPartialBefore = true; 9255 } 9256 else 9257 { 9258 if (poppedElements < stepSize) 9259 _empty = true; 9260 } 9261 } 9262 else 9263 { 9264 static if (withPartial) 9265 { 9266 if (source.length <= windowSize) 9267 hasShownPartialBefore = true; 9268 } 9269 else 9270 { 9271 if (source.length < windowSize) 9272 _empty = true; 9273 } 9274 } 9275 } 9276 9277 static if (!isInfinite!Source) 9278 { 9279 /// Ditto 9280 @property bool empty() const 9281 { 9282 return _empty; 9283 } 9284 } 9285 else 9286 { 9287 // undocumented 9288 enum empty = false; 9289 } 9290 9291 /// Ditto 9292 @property typeof(this) save() 9293 { 9294 return typeof(this)(source.save, windowSize, stepSize); 9295 } 9296 9297 static if (hasLength!Source) 9298 { 9299 // gaps between the last element and the end of the range 9300 private size_t gap() 9301 { 9302 /* 9303 * Note: 9304 * - In the following `end` is the exclusive end as used in opSlice 9305 * - For the trivial case with `stepSize = 1` `end` is at `len`: 9306 * 9307 * iota(4).slide(2) = [[0, 1], [1, 2], [2, 3]] (end = 4) 9308 * iota(4).slide(3) = [[0, 1, 2], [1, 2, 3]] (end = 4) 9309 * 9310 * - For the non-trivial cases, we need to calculate the gap 9311 * between `len` and `end` - this is the number of missing elements 9312 * from the input range: 9313 * 9314 * iota(7).slide(2, 3) = [[0, 1], [3, 4]] || <gap: 2> 6 9315 * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || <gap: 1> 6 9316 * iota(7).slide(1, 5) = [[0], [5]] || <gap: 1> 6 9317 * 9318 * As it can be seen `gap` can be at most `stepSize - 1` 9319 * More generally the elements of the sliding window with 9320 * `w = windowSize` and `s = stepSize` are: 9321 * 9322 * [0, w], [s, s + w], [2 * s, 2 * s + w], ... [n * s, n * s + w] 9323 * 9324 * We can thus calculate the gap between the `end` and `len` as: 9325 * 9326 * gap = len - (n * s + w) = len - w - (n * s) 9327 * 9328 * As we aren't interested in exact value of `n`, but the best 9329 * minimal `gap` value, we can use modulo to "cut" `len - w` optimally: 9330 * 9331 * gap = len - w - (s - s ... - s) = (len - w) % s 9332 * 9333 * So for example: 9334 * 9335 * iota(7).slide(2, 3) = [[0, 1], [3, 4]] 9336 * gap: (7 - 2) % 3 = 5 % 3 = 2 9337 * end: 7 - 2 = 5 9338 * 9339 * iota(7).slide(4, 2) = [[0, 1, 2, 3], [2, 3, 4, 5]] 9340 * gap: (7 - 4) % 2 = 3 % 2 = 1 9341 * end: 7 - 1 = 6 9342 */ 9343 return (source.length - windowSize) % stepSize; 9344 } 9345 9346 private size_t numberOfFullFrames() 9347 { 9348 /** 9349 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4) 9350 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (3) 9351 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (2) 9352 6.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5] (2) 9353 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (2) 9354 9355 As the last window is only added iff its complete, 9356 we don't count the last window except if it's full due to integer rounding. 9357 */ 9358 return 1 + (source.length - windowSize) / stepSize; 9359 } 9360 9361 // Whether the last slide frame size is less than windowSize 9362 private bool hasPartialElements() 9363 { 9364 static if (withPartial) 9365 return gap != 0 && source.length > numberOfFullFrames * stepSize; 9366 else 9367 return 0; 9368 } 9369 9370 /// Length. Only if `hasLength!Source` is `true` 9371 @property size_t length() 9372 { 9373 if (source.length < windowSize) 9374 { 9375 static if (withPartial) 9376 return source.length > 0; 9377 else 9378 return 0; 9379 } 9380 else 9381 { 9382 /*** 9383 We bump the pointer by stepSize for every element. 9384 If withPartial, we don't count the last element if its size 9385 isn't windowSize 9386 9387 At most: 9388 [p, p + stepSize, ..., p + stepSize * n] 9389 9390 5.iota.slides(2, 1) => [0, 1], [1, 2], [2, 3], [3, 4] (4) 9391 7.iota.slides(2, 2) => [0, 1], [2, 3], [4, 5], [6] (4) 9392 7.iota.slides(2, 3) => [0, 1], [3, 4], [6] (3) 9393 7.iota.slides(3, 2) => [0, 1, 2], [2, 3, 4], [4, 5, 6] (3) 9394 7.iota.slides(3, 3) => [0, 1, 2], [3, 4, 5], [6] (3) 9395 */ 9396 return numberOfFullFrames + hasPartialElements; 9397 } 9398 } 9399 } 9400 9401 static if (hasSlicing!Source) 9402 { 9403 /** 9404 Indexing and slicing operations. Provided only if 9405 `hasSlicing!Source` is `true`. 9406 */ 9407 auto opIndex(size_t index) 9408 { 9409 immutable start = index * stepSize; 9410 9411 static if (isInfinite!Source) 9412 { 9413 immutable end = start + windowSize; 9414 } 9415 else 9416 { 9417 import std.algorithm.comparison : min; 9418 9419 immutable len = source.length; 9420 assert(start < len, "slide index out of bounds"); 9421 immutable end = min(start + windowSize, len); 9422 } 9423 9424 return source[start .. end]; 9425 } 9426 9427 static if (!isInfinite!Source) 9428 { 9429 /// ditto 9430 typeof(this) opSlice(size_t lower, size_t upper) 9431 { 9432 import std.algorithm.comparison : min; 9433 9434 assert(upper <= length, "slide slicing index out of bounds"); 9435 assert(lower <= upper, "slide slicing index out of bounds"); 9436 9437 lower *= stepSize; 9438 upper *= stepSize; 9439 9440 immutable len = source.length; 9441 9442 static if (withPartial) 9443 { 9444 import std.algorithm.comparison : max; 9445 9446 if (lower == upper) 9447 return this[$ .. $]; 9448 9449 /* 9450 A) If `stepSize` >= `windowSize` => `rightPos = upper` 9451 9452 [0, 1, 2, 3, 4, 5, 6].slide(2, 3) -> s = [[0, 1], [3, 4], [6]] 9453 rightPos for s[0 .. 2]: (upper=2) * (stepSize=3) = 6 9454 6.iota.slide(2, 3) = [[0, 1], [3, 4]] 9455 9456 B) If `stepSize` < `windowSize` => add `windowSize - stepSize` to `upper` 9457 9458 [0, 1, 2, 3].slide(2) = [[0, 1], [1, 2], [2, 3]] 9459 rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) = 1 9460 1.iota.slide(2) = [[0]] 9461 9462 rightPos for s[0 .. 1]: = (upper=1) * (stepSize=1) + (windowSize-stepSize=1) = 2 9463 1.iota.slide(2) = [[0, 1]] 9464 9465 More complex: 9466 9467 20.iota.slide(7, 6)[0 .. 2] 9468 rightPos: (upper=2) * (stepSize=6) = 12.iota 9469 12.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11]] 9470 9471 Now we add up for the difference between `windowSize` and `stepSize`: 9472 9473 rightPos: (upper=2) * (stepSize=6) + (windowSize-stepSize=1) = 13.iota 9474 13.iota.slide(7, 6) = [[0, 1, 2, 3, 4, 5, 6], [6, 7, 8, 9, 10, 11, 12]] 9475 */ 9476 immutable rightPos = min(len, upper + max(0, windowSize - stepSize)); 9477 } 9478 else 9479 { 9480 /* 9481 After we have normalized `lower` and `upper` by `stepSize`, 9482 we only need to look at the case of `stepSize=1`. 9483 As `leftPos`, is equal to `lower`, we will only look `rightPos`. 9484 Notice that starting from `upper`, 9485 we only need to move for `windowSize - 1` to the right: 9486 9487 - [0, 1, 2, 3].slide(2) -> s = [[0, 1], [1, 2], [2, 3]] 9488 rightPos for s[0 .. 3]: (upper=3) + (windowSize=2) - 1 = 4 9489 9490 - [0, 1, 2, 3].slide(3) -> s = [[0, 1, 2], [1, 2, 3]] 9491 rightPos for s[0 .. 2]: (upper=2) + (windowSize=3) - 1 = 4 9492 9493 - [0, 1, 2, 3, 4].slide(4) -> s = [[0, 1, 2, 3], [1, 2, 3, 4]] 9494 rightPos for s[0 .. 2]: (upper=2) + (windowSize=4) - 1 = 5 9495 */ 9496 immutable rightPos = min(upper + windowSize - 1, len); 9497 } 9498 9499 return typeof(this)(source[min(lower, len) .. rightPos], windowSize, stepSize); 9500 } 9501 } 9502 else static if (hasSliceToEnd) 9503 { 9504 // For slicing an infinite chunk, we need to slice the source to the infinite end. 9505 auto opSlice(size_t lower, size_t upper) 9506 { 9507 assert(lower <= upper, "slide slicing index out of bounds"); 9508 return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize) 9509 .takeExactly(upper - lower); 9510 } 9511 } 9512 9513 static if (isInfinite!Source) 9514 { 9515 static if (hasSliceToEnd) 9516 { 9517 private static struct DollarToken{} 9518 DollarToken opDollar() 9519 { 9520 return DollarToken(); 9521 } 9522 //Slice to dollar 9523 typeof(this) opSlice(size_t lower, DollarToken) 9524 { 9525 return typeof(this)(source[lower * stepSize .. $], windowSize, stepSize); 9526 } 9527 } 9528 } 9529 else 9530 { 9531 // Dollar token carries a static type, with no extra information. 9532 // It can lazily transform into source.length on algorithmic 9533 // operations such as : slide[$/2, $-1]; 9534 private static struct DollarToken 9535 { 9536 private size_t _length; 9537 alias _length this; 9538 } 9539 9540 DollarToken opDollar() 9541 { 9542 return DollarToken(this.length); 9543 } 9544 9545 // Optimized slice overloads optimized for using dollar. 9546 typeof(this) opSlice(DollarToken, DollarToken) 9547 { 9548 static if (hasSliceToEnd) 9549 { 9550 return typeof(this)(source[$ .. $], windowSize, stepSize); 9551 } 9552 else 9553 { 9554 immutable len = source.length; 9555 return typeof(this)(source[len .. len], windowSize, stepSize); 9556 } 9557 } 9558 9559 // Optimized slice overloads optimized for using dollar. 9560 typeof(this) opSlice(size_t lower, DollarToken) 9561 { 9562 import std.algorithm.comparison : min; 9563 assert(lower <= length, "slide slicing index out of bounds"); 9564 lower *= stepSize; 9565 static if (hasSliceToEnd) 9566 { 9567 return typeof(this)(source[min(lower, source.length) .. $], windowSize, stepSize); 9568 } 9569 else 9570 { 9571 immutable len = source.length; 9572 return typeof(this)(source[min(lower, len) .. len], windowSize, stepSize); 9573 } 9574 } 9575 9576 // Optimized slice overloads optimized for using dollar. 9577 typeof(this) opSlice(DollarToken, size_t upper) 9578 { 9579 assert(upper == length, "slide slicing index out of bounds"); 9580 return this[$ .. $]; 9581 } 9582 } 9583 9584 // Bidirectional range primitives 9585 static if (!isInfinite!Source) 9586 { 9587 /** 9588 Bidirectional range primitives. Provided only if both 9589 `hasSlicing!Source` and `!isInfinite!Source` are `true`. 9590 */ 9591 @property auto back() 9592 { 9593 import std.algorithm.comparison : max; 9594 9595 assert(!empty, "Attempting to access front on an empty slide"); 9596 9597 immutable len = source.length; 9598 9599 static if (withPartial) 9600 { 9601 if (source.length <= windowSize) 9602 return source[0 .. source.length]; 9603 9604 if (hasPartialElements) 9605 return source[numberOfFullFrames * stepSize .. len]; 9606 } 9607 9608 // check for underflow 9609 immutable start = (len > windowSize + gap) ? len - windowSize - gap : 0; 9610 return source[start .. len - gap]; 9611 } 9612 9613 /// Ditto 9614 void popBack() 9615 { 9616 assert(!empty, "Attempting to call popBack() on an empty slide"); 9617 9618 // Move by stepSize 9619 immutable end = source.length > stepSize ? source.length - stepSize : 0; 9620 9621 static if (withPartial) 9622 { 9623 if (hasShownPartialBefore || source.empty) 9624 { 9625 _empty = true; 9626 return; 9627 } 9628 9629 // pop by stepSize, except for the partial frame at the end 9630 if (hasPartialElements) 9631 source = source[0 .. source.length - gap]; 9632 else 9633 source = source[0 .. end]; 9634 } 9635 else 9636 { 9637 source = source[0 .. end]; 9638 } 9639 9640 if (source.length < windowSize) 9641 _empty = true; 9642 } 9643 } 9644 } 9645 } 9646 9647 // test @nogc 9648 @safe pure nothrow @nogc unittest 9649 { 9650 import std.algorithm.comparison : equal; 9651 9652 static immutable res1 = [[0], [1], [2], [3]]; 9653 assert(4.iota.slide!(Yes.withPartial)(1).equal!equal(res1)); 9654 9655 static immutable res2 = [[0, 1], [1, 2], [2, 3]]; 9656 assert(4.iota.slide!(Yes.withPartial)(2).equal!equal(res2)); 9657 } 9658 9659 // test different window sizes 9660 @safe pure nothrow unittest 9661 { 9662 import std.array : array; 9663 import std.algorithm.comparison : equal; 9664 9665 assert([0, 1, 2, 3].slide!(Yes.withPartial)(1).array == [[0], [1], [2], [3]]); 9666 assert([0, 1, 2, 3].slide!(Yes.withPartial)(2).array == [[0, 1], [1, 2], [2, 3]]); 9667 assert([0, 1, 2, 3].slide!(Yes.withPartial)(3).array == [[0, 1, 2], [1, 2, 3]]); 9668 assert([0, 1, 2, 3].slide!(Yes.withPartial)(4).array == [[0, 1, 2, 3]]); 9669 assert([0, 1, 2, 3].slide!(No.withPartial)(5).walkLength == 0); 9670 assert([0, 1, 2, 3].slide!(Yes.withPartial)(5).array == [[0, 1, 2, 3]]); 9671 9672 assert(iota(2).slide!(Yes.withPartial)(2).front.equal([0, 1])); 9673 assert(iota(3).slide!(Yes.withPartial)(2).equal!equal([[0, 1],[1, 2]])); 9674 assert(iota(3).slide!(Yes.withPartial)(3).equal!equal([[0, 1, 2]])); 9675 assert(iota(3).slide!(No.withPartial)(4).walkLength == 0); 9676 assert(iota(3).slide!(Yes.withPartial)(4).equal!equal([[0, 1, 2]])); 9677 assert(iota(1, 4).slide!(Yes.withPartial)(1).equal!equal([[1], [2], [3]])); 9678 assert(iota(1, 4).slide!(Yes.withPartial)(3).equal!equal([[1, 2, 3]])); 9679 } 9680 9681 // test combinations 9682 @safe pure nothrow unittest 9683 { 9684 import std.algorithm.comparison : equal; 9685 import std.typecons : tuple; 9686 9687 alias t = tuple; 9688 auto list = [ 9689 t(t(1, 1), [[0], [1], [2], [3], [4], [5]]), 9690 t(t(1, 2), [[0], [2], [4]]), 9691 t(t(1, 3), [[0], [3]]), 9692 t(t(1, 4), [[0], [4]]), 9693 t(t(1, 5), [[0], [5]]), 9694 t(t(2, 1), [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]), 9695 t(t(2, 2), [[0, 1], [2, 3], [4, 5]]), 9696 t(t(2, 3), [[0, 1], [3, 4]]), 9697 t(t(2, 4), [[0, 1], [4, 5]]), 9698 t(t(3, 1), [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]), 9699 t(t(3, 3), [[0, 1, 2], [3, 4, 5]]), 9700 t(t(4, 1), [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]), 9701 t(t(4, 2), [[0, 1, 2, 3], [2, 3, 4, 5]]), 9702 t(t(5, 1), [[0, 1, 2, 3, 4], [1, 2, 3, 4, 5]]), 9703 ]; 9704 9705 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9706 foreach (e; list) 9707 assert(6.iota.slide!Partial(e[0].expand).equal!equal(e[1])); 9708 9709 auto listSpecial = [ 9710 t(t(2, 5), [[0, 1], [5]]), 9711 t(t(3, 2), [[0, 1, 2], [2, 3, 4], [4, 5]]), 9712 t(t(3, 4), [[0, 1, 2], [4, 5]]), 9713 t(t(4, 3), [[0, 1, 2, 3], [3, 4, 5]]), 9714 t(t(5, 2), [[0, 1, 2, 3, 4], [2, 3, 4, 5]]), 9715 t(t(5, 3), [[0, 1, 2, 3, 4], [3, 4, 5]]), 9716 ]; 9717 foreach (e; listSpecial) 9718 { 9719 assert(6.iota.slide!(Yes.withPartial)(e[0].expand).equal!equal(e[1])); 9720 assert(6.iota.slide!(No.withPartial)(e[0].expand).equal!equal(e[1].dropBackOne)); 9721 } 9722 } 9723 9724 // test emptiness and copyability 9725 @safe pure nothrow unittest 9726 { 9727 import std.algorithm.comparison : equal; 9728 import std.algorithm.iteration : map; 9729 9730 // check with empty input 9731 int[] d; 9732 assert(d.slide!(Yes.withPartial)(2).empty); 9733 assert(d.slide!(Yes.withPartial)(2, 2).empty); 9734 9735 // is copyable? 9736 auto e = iota(5).slide!(Yes.withPartial)(2); 9737 e.popFront; 9738 assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); 9739 assert(e.save.equal!equal([[1, 2], [2, 3], [3, 4]])); 9740 assert(e.map!"a.array".array == [[1, 2], [2, 3], [3, 4]]); 9741 } 9742 9743 // test with strings 9744 @safe pure nothrow unittest 9745 { 9746 import std.algorithm.iteration : each; 9747 9748 int[dstring] f; 9749 "AGAGA"d.slide!(Yes.withPartial)(3).each!(a => f[a]++); 9750 assert(f == ["AGA"d: 2, "GAG"d: 1]); 9751 9752 int[dstring] g; 9753 "ABCDEFG"d.slide!(Yes.withPartial)(3, 3).each!(a => g[a]++); 9754 assert(g == ["ABC"d:1, "DEF"d:1, "G": 1]); 9755 g = null; 9756 "ABCDEFG"d.slide!(No.withPartial)(3, 3).each!(a => g[a]++); 9757 assert(g == ["ABC"d:1, "DEF"d:1]); 9758 } 9759 9760 // test with utf8 strings 9761 @safe unittest 9762 { 9763 import std.stdio; 9764 import std.algorithm.comparison : equal; 9765 9766 assert("ä.ö.ü.".slide!(Yes.withPartial)(3, 2).equal!equal(["ä.ö", "ö.ü", "ü."])); 9767 assert("ä.ö.ü.".slide!(No.withPartial)(3, 2).equal!equal(["ä.ö", "ö.ü"])); 9768 9769 "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]); 9770 "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(2, 4).equal!equal(["😄😅", "😈😄", "😇😈"]); 9771 "😄😅😆😇😈😄😅😆😇😈".slide!(Yes.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇", "😈"]); 9772 "😄😅😆😇😈😄😅😆😇😈".slide!(No.withPartial)(3, 3).equal!equal(["😄😅😆", "😇😈😄", "😅😆😇"]); 9773 } 9774 9775 // test length 9776 @safe pure nothrow unittest 9777 { 9778 // Slides with fewer elements are empty or 1 for Yes.withPartial 9779 static foreach (expectedLength, Partial; [No.withPartial, Yes.withPartial]) 9780 {{ 9781 assert(3.iota.slide!(Partial)(4, 2).walkLength == expectedLength); 9782 assert(3.iota.slide!(Partial)(4).walkLength == expectedLength); 9783 assert(3.iota.slide!(Partial)(4, 3).walkLength == expectedLength); 9784 }} 9785 9786 static immutable list = [ 9787 // iota slide expected 9788 [4, 2, 1, 3, 3], 9789 [5, 3, 1, 3, 3], 9790 [7, 2, 2, 4, 3], 9791 [12, 2, 4, 3, 3], 9792 [6, 1, 2, 3, 3], 9793 [6, 2, 4, 2, 2], 9794 [3, 2, 4, 1, 1], 9795 [5, 2, 1, 4, 4], 9796 [7, 2, 2, 4, 3], 9797 [7, 2, 3, 3, 2], 9798 [7, 3, 2, 3, 3], 9799 [7, 3, 3, 3, 2], 9800 ]; 9801 foreach (e; list) 9802 { 9803 assert(e[0].iota.slide!(Yes.withPartial)(e[1], e[2]).length == e[3]); 9804 assert(e[0].iota.slide!(No.withPartial)(e[1], e[2]).length == e[4]); 9805 } 9806 } 9807 9808 // test index and slicing 9809 @safe pure nothrow unittest 9810 { 9811 import std.algorithm.comparison : equal; 9812 import std.array : array; 9813 9814 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9815 { 9816 foreach (s; [5, 7, 10, 15, 20]) 9817 foreach (windowSize; 1 .. 10) 9818 foreach (stepSize; 1 .. 10) 9819 { 9820 auto r = s.iota.slide!Partial(windowSize, stepSize); 9821 auto arr = r.array; 9822 assert(r.length == arr.length); 9823 9824 // test indexing 9825 foreach (i; 0 .. arr.length) 9826 assert(r[i] == arr[i]); 9827 9828 // test slicing 9829 foreach (i; 0 .. arr.length) 9830 { 9831 foreach (j; i .. arr.length) 9832 assert(r[i .. j].equal(arr[i .. j])); 9833 9834 assert(r[i .. $].equal(arr[i .. $])); 9835 } 9836 9837 // test opDollar slicing 9838 assert(r[$/2 .. $].equal(arr[$/2 .. $])); 9839 assert(r[$ .. $].empty); 9840 if (arr.empty) 9841 { 9842 assert(r[$ .. 0].empty); 9843 assert(r[$/2 .. $].empty); 9844 9845 } 9846 } 9847 } 9848 } 9849 9850 // test with infinite ranges 9851 @safe pure nothrow unittest 9852 { 9853 import std.algorithm.comparison : equal; 9854 9855 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9856 {{ 9857 // InfiniteRange without RandomAccess 9858 auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1); 9859 assert(fibs.slide!Partial(2).take(2).equal!equal([[1, 1], [1, 2]])); 9860 assert(fibs.slide!Partial(2, 3).take(2).equal!equal([[1, 1], [3, 5]])); 9861 9862 // InfiniteRange with RandomAccess and slicing 9863 auto odds = sequence!("a[0] + n * a[1]")(1, 2); 9864 auto oddsByPairs = odds.slide!Partial(2); 9865 assert(oddsByPairs.take(2).equal!equal([[ 1, 3], [ 3, 5]])); 9866 assert(oddsByPairs[1].equal([3, 5])); 9867 assert(oddsByPairs[4].equal([9, 11])); 9868 9869 static assert(hasSlicing!(typeof(odds))); 9870 assert(oddsByPairs[3 .. 5].equal!equal([[7, 9], [9, 11]])); 9871 assert(oddsByPairs[3 .. $].take(2).equal!equal([[7, 9], [9, 11]])); 9872 9873 auto oddsWithGaps = odds.slide!Partial(2, 4); 9874 assert(oddsWithGaps.take(3).equal!equal([[1, 3], [9, 11], [17, 19]])); 9875 assert(oddsWithGaps[2].equal([17, 19])); 9876 assert(oddsWithGaps[1 .. 3].equal!equal([[9, 11], [17, 19]])); 9877 assert(oddsWithGaps[1 .. $].take(2).equal!equal([[9, 11], [17, 19]])); 9878 }} 9879 } 9880 9881 // test reverse 9882 @safe pure nothrow unittest 9883 { 9884 import std.algorithm.comparison : equal; 9885 9886 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9887 {{ 9888 foreach (windowSize; 1 .. 15) 9889 foreach (stepSize; 1 .. 15) 9890 { 9891 auto r = 20.iota.slide!Partial(windowSize, stepSize); 9892 auto rArr = r.array.retro; 9893 auto rRetro = r.retro; 9894 9895 assert(rRetro.length == rArr.length); 9896 assert(rRetro.equal(rArr)); 9897 assert(rRetro.array.retro.equal(r)); 9898 } 9899 }} 9900 } 9901 9902 // test with dummy ranges 9903 @safe pure nothrow unittest 9904 { 9905 import std.algorithm.comparison : equal; 9906 import std.internal.test.dummyrange : AllDummyRanges; 9907 import std.meta : Filter; 9908 9909 static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) 9910 {{ 9911 Range r; 9912 9913 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9914 { 9915 assert(r.slide!Partial(1).equal!equal( 9916 [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]] 9917 )); 9918 assert(r.slide!Partial(2).equal!equal( 9919 [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10]] 9920 )); 9921 assert(r.slide!Partial(3).equal!equal( 9922 [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], 9923 [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]] 9924 )); 9925 assert(r.slide!Partial(6).equal!equal( 9926 [[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8], 9927 [4, 5, 6, 7, 8, 9], [5, 6, 7, 8, 9, 10]] 9928 )); 9929 } 9930 9931 // special cases 9932 assert(r.slide!(Yes.withPartial)(15).equal!equal(iota(1, 11).only)); 9933 assert(r.slide!(Yes.withPartial)(15).walkLength == 1); 9934 assert(r.slide!(No.withPartial)(15).empty); 9935 assert(r.slide!(No.withPartial)(15).walkLength == 0); 9936 }} 9937 } 9938 9939 // test with dummy ranges 9940 @safe pure nothrow unittest 9941 { 9942 import std.algorithm.comparison : equal; 9943 import std.internal.test.dummyrange : AllDummyRanges; 9944 import std.meta : Filter; 9945 import std.typecons : tuple; 9946 9947 alias t = tuple; 9948 static immutable list = [ 9949 // iota slide expected 9950 t(6, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6]]), 9951 t(6, t(4, 6), [[1, 2, 3, 4]]), 9952 t(6, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]), 9953 t(7, t(4, 1), [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]), 9954 t(7, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7]]), 9955 t(8, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8]]), 9956 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]]), 9957 t(8, t(3, 4), [[1, 2, 3], [5, 6, 7]]), 9958 t(10, t(3, 7), [[1, 2, 3], [8, 9, 10]]), 9959 ]; 9960 9961 static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) 9962 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9963 foreach (e; list) 9964 assert(Range().take(e[0]).slide!Partial(e[1].expand).equal!equal(e[2])); 9965 9966 static immutable listSpecial = [ 9967 // iota slide expected 9968 t(6, t(4, 3), [[1, 2, 3, 4], [4, 5, 6]]), 9969 t(7, t(4, 5), [[1, 2, 3, 4], [6, 7]]), 9970 t(7, t(4, 4), [[1, 2, 3, 4], [5, 6, 7]]), 9971 t(7, t(4, 2), [[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7]]), 9972 t(8, t(4, 3), [[1, 2, 3, 4], [4, 5, 6, 7], [7, 8]]), 9973 t(8, t(3, 3), [[1, 2, 3], [4, 5, 6], [7, 8]]), 9974 t(8, t(3, 6), [[1, 2, 3], [7, 8]]), 9975 t(10, t(7, 6), [[1, 2, 3, 4, 5, 6, 7], [7, 8, 9, 10]]), 9976 t(10, t(3, 8), [[1, 2, 3], [9, 10]]), 9977 ]; 9978 static foreach (Range; Filter!(isForwardRange, AllDummyRanges)) 9979 static foreach (Partial; [Yes.withPartial, No.withPartial]) 9980 foreach (e; listSpecial) 9981 { 9982 Range r; 9983 assert(r.take(e[0]).slide!(Yes.withPartial)(e[1].expand).equal!equal(e[2])); 9984 assert(r.take(e[0]).slide!(No.withPartial)(e[1].expand).equal!equal(e[2].dropBackOne)); 9985 } 9986 } 9987 9988 // test reverse with dummy ranges 9989 @safe pure nothrow unittest 9990 { 9991 import std.algorithm.comparison : equal; 9992 import std.internal.test.dummyrange : AllDummyRanges; 9993 import std.meta : Filter, templateAnd; 9994 import std.typecons : tuple; 9995 alias t = tuple; 9996 9997 static immutable list = [ 9998 // slide expected 9999 t(1, 1, [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]), 10000 t(2, 1, [[9, 10], [8, 9], [7, 8], [6, 7], [5, 6], [4, 5], [3, 4], [2, 3], [1, 2]]), 10001 t(5, 1, [[6, 7, 8, 9, 10], [5, 6, 7, 8, 9], [4, 5, 6, 7, 8], 10002 [3, 4, 5, 6, 7], [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]]), 10003 t(2, 2, [[9, 10], [7, 8], [5, 6], [3, 4], [1, 2]]), 10004 t(2, 4, [[9, 10], [5, 6], [1, 2]]), 10005 ]; 10006 10007 static foreach (Range; Filter!(templateAnd!(hasSlicing, hasLength, isBidirectionalRange), AllDummyRanges)) 10008 {{ 10009 Range r; 10010 static foreach (Partial; [Yes.withPartial, No.withPartial]) 10011 { 10012 foreach (e; list) 10013 assert(r.slide!Partial(e[0], e[1]).retro.equal!equal(e[2])); 10014 10015 // front = back 10016 foreach (windowSize; 1 .. 10) 10017 foreach (stepSize; 1 .. 10) 10018 { 10019 auto slider = r.slide!Partial(windowSize, stepSize); 10020 auto sliderRetro = slider.retro.array; 10021 assert(slider.length == sliderRetro.length); 10022 assert(sliderRetro.retro.equal!equal(slider)); 10023 } 10024 } 10025 10026 // special cases 10027 assert(r.slide!(No.withPartial)(15).retro.walkLength == 0); 10028 assert(r.slide!(Yes.withPartial)(15).retro.equal!equal(iota(1, 11).only)); 10029 }} 10030 } 10031 10032 // test different sliceable ranges 10033 @safe pure nothrow unittest 10034 { 10035 import std.algorithm.comparison : equal; 10036 import std.internal.test.dummyrange : AllDummyRanges; 10037 import std.meta : AliasSeq; 10038 10039 struct SliceableRange(Range, Flag!"withOpDollar" withOpDollar = No.withOpDollar, 10040 Flag!"withInfiniteness" withInfiniteness = No.withInfiniteness) 10041 { 10042 Range arr = 10.iota.array; // similar to DummyRange 10043 @property auto save() { return typeof(this)(arr); } 10044 @property auto front() { return arr[0]; } 10045 void popFront() { arr.popFront(); } 10046 auto opSlice(size_t i, size_t j) 10047 { 10048 // subslices can't be infinite 10049 return SliceableRange!(Range, withOpDollar, No.withInfiniteness)(arr[i .. j]); 10050 } 10051 10052 static if (withInfiniteness) 10053 { 10054 enum empty = false; 10055 } 10056 else 10057 { 10058 @property bool empty() { return arr.empty; } 10059 @property auto length() { return arr.length; } 10060 } 10061 10062 static if (withOpDollar) 10063 { 10064 static if (withInfiniteness) 10065 { 10066 struct Dollar {} 10067 Dollar opDollar() const { return Dollar.init; } 10068 10069 // Slice to dollar 10070 typeof(this) opSlice(size_t lower, Dollar) 10071 { 10072 return typeof(this)(arr[lower .. $]); 10073 } 10074 10075 } 10076 else 10077 { 10078 alias opDollar = length; 10079 } 10080 } 10081 } 10082 10083 import std.meta : Filter, templateNot; 10084 alias SliceableDummyRanges = Filter!(hasSlicing, AllDummyRanges); 10085 10086 static foreach (Partial; [Yes.withPartial, No.withPartial]) 10087 {{ 10088 static foreach (Range; SliceableDummyRanges) 10089 {{ 10090 Range r; 10091 r.reinit; 10092 r.arr[] -= 1; // use a 0-based array (for clarity) 10093 10094 assert(r.slide!Partial(2)[0].equal([0, 1])); 10095 assert(r.slide!Partial(2)[1].equal([1, 2])); 10096 10097 // saveable 10098 auto s = r.slide!Partial(2); 10099 assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); 10100 s.save.popFront; 10101 assert(s[0 .. 2].equal!equal([[0, 1], [1, 2]])); 10102 10103 assert(r.slide!Partial(3)[1 .. 3].equal!equal([[1, 2, 3], [2, 3, 4]])); 10104 }} 10105 10106 static foreach (Range; Filter!(templateNot!isInfinite, SliceableDummyRanges)) 10107 {{ 10108 Range r; 10109 r.reinit; 10110 r.arr[] -= 1; // use a 0-based array (for clarity) 10111 10112 assert(r.slide!(No.withPartial)(6).equal!equal( 10113 [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], 10114 [3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]] 10115 )); 10116 assert(r.slide!(No.withPartial)(16).empty); 10117 10118 assert(r.slide!Partial(4)[0 .. $].equal(r.slide!Partial(4))); 10119 assert(r.slide!Partial(2)[$/2 .. $].equal!equal([[4, 5], [5, 6], [6, 7], [7, 8], [8, 9]])); 10120 assert(r.slide!Partial(2)[$ .. $].empty); 10121 10122 assert(r.slide!Partial(3).retro.equal!equal( 10123 [[7, 8, 9], [6, 7, 8], [5, 6, 7], [4, 5, 6], [3, 4, 5], [2, 3, 4], [1, 2, 3], [0, 1, 2]] 10124 )); 10125 }} 10126 10127 alias T = int[]; 10128 10129 // separate checks for infinity 10130 auto infIndex = SliceableRange!(T, No.withOpDollar, Yes.withInfiniteness)([0, 1, 2, 3]); 10131 assert(infIndex.slide!Partial(2)[0].equal([0, 1])); 10132 assert(infIndex.slide!Partial(2)[1].equal([1, 2])); 10133 10134 auto infDollar = SliceableRange!(T, Yes.withOpDollar, Yes.withInfiniteness)(); 10135 assert(infDollar.slide!Partial(2)[1 .. $].front.equal([1, 2])); 10136 assert(infDollar.slide!Partial(4)[0 .. $].front.equal([0, 1, 2, 3])); 10137 assert(infDollar.slide!Partial(4)[2 .. $].front.equal([2, 3, 4, 5])); 10138 }} 10139 } 10140 10141 // https://issues.dlang.org/show_bug.cgi?id=19082 10142 @safe unittest 10143 { 10144 import std.algorithm.comparison : equal; 10145 import std.algorithm.iteration : map; 10146 assert([1].map!(x => x).slide(2).equal!equal([[1]])); 10147 } 10148 10149 // https://issues.dlang.org/show_bug.cgi?id=19642 10150 @safe unittest 10151 { 10152 import std.algorithm.comparison : equal; 10153 import std.algorithm.iteration : splitter; 10154 10155 assert("ab cd".splitter(' ').slide!(No.withPartial)(2).equal!equal([["ab", "cd"]])); 10156 } 10157 10158 // https://issues.dlang.org/show_bug.cgi?id=23976 10159 @safe unittest 10160 { 10161 import std.algorithm.comparison : equal; 10162 import std.algorithm.iteration : splitter; 10163 10164 assert("1<2".splitter('<').slide(2).equal!equal([["1", "2"]])); 10165 } 10166 10167 private struct OnlyResult(Values...) 10168 if (Values.length > 1) 10169 { 10170 private enum arity = Values.length; 10171 10172 private alias UnqualValues = staticMap!(Unqual, Values); 10173 10174 private this(return scope ref Values values) 10175 { 10176 ref @trusted unqual(T)(ref T x){return cast() x;} 10177 10178 // TODO: this calls any possible copy constructors without qualifiers. 10179 // Find a way to initialize values using qualified copy constructors. 10180 static foreach (i; 0 .. Values.length) 10181 { 10182 this.values[i] = unqual(values[i]); 10183 } 10184 this.backIndex = arity; 10185 } 10186 10187 bool empty() @property 10188 { 10189 return frontIndex >= backIndex; 10190 } 10191 10192 CommonType!Values front() @property 10193 { 10194 assert(!empty, "Attempting to fetch the front of an empty Only range"); 10195 return this[0]; 10196 } 10197 10198 void popFront() 10199 { 10200 assert(!empty, "Attempting to popFront an empty Only range"); 10201 ++frontIndex; 10202 } 10203 10204 CommonType!Values back() @property 10205 { 10206 assert(!empty, "Attempting to fetch the back of an empty Only range"); 10207 return this[$ - 1]; 10208 } 10209 10210 void popBack() 10211 { 10212 assert(!empty, "Attempting to popBack an empty Only range"); 10213 --backIndex; 10214 } 10215 10216 OnlyResult save() @property 10217 { 10218 return this; 10219 } 10220 10221 size_t length() const @property 10222 { 10223 return backIndex - frontIndex; 10224 } 10225 10226 alias opDollar = length; 10227 10228 @trusted CommonType!Values opIndex(size_t idx) 10229 { 10230 // when i + idx points to elements popped 10231 // with popBack 10232 assert(idx < length, "Attempting to fetch an out of bounds index from an Only range"); 10233 final switch (frontIndex + idx) 10234 static foreach (i, T; Values) 10235 case i: 10236 return cast(T) values[i]; 10237 } 10238 10239 OnlyResult opSlice() 10240 { 10241 return this; 10242 } 10243 10244 OnlyResult opSlice(size_t from, size_t to) 10245 { 10246 OnlyResult result = this; 10247 result.frontIndex += from; 10248 result.backIndex = this.frontIndex + to; 10249 assert( 10250 from <= to, 10251 "Attempting to slice an Only range with a larger first argument than the second." 10252 ); 10253 assert( 10254 to <= length, 10255 "Attempting to slice using an out of bounds index on an Only range" 10256 ); 10257 return result; 10258 } 10259 10260 private size_t frontIndex = 0; 10261 private size_t backIndex = 0; 10262 10263 // https://issues.dlang.org/show_bug.cgi?id=10643 10264 version (none) 10265 { 10266 import std.traits : hasElaborateAssign; 10267 static if (hasElaborateAssign!T) 10268 private UnqualValues values; 10269 else 10270 private UnqualValues values = void; 10271 } 10272 else 10273 // These may alias to shared or immutable data. Do not let the user 10274 // to access these directly, and do not allow mutation without checking 10275 // the qualifier. 10276 private UnqualValues values; 10277 } 10278 10279 // Specialize for single-element results 10280 private struct OnlyResult(T) 10281 { 10282 @property T front() 10283 { 10284 assert(!empty, "Attempting to fetch the front of an empty Only range"); 10285 return fetchFront(); 10286 } 10287 @property T back() 10288 { 10289 assert(!empty, "Attempting to fetch the back of an empty Only range"); 10290 return fetchFront(); 10291 } 10292 @property bool empty() const { return _empty; } 10293 @property size_t length() const { return !_empty; } 10294 @property auto save() { return this; } 10295 void popFront() 10296 { 10297 assert(!_empty, "Attempting to popFront an empty Only range"); 10298 _empty = true; 10299 } 10300 void popBack() 10301 { 10302 assert(!_empty, "Attempting to popBack an empty Only range"); 10303 _empty = true; 10304 } 10305 alias opDollar = length; 10306 10307 private this()(return scope auto ref T value) 10308 { 10309 ref @trusted unqual(ref T x){return cast() x;} 10310 // TODO: this calls the possible copy constructor without qualifiers. 10311 // Find a way to initialize value using a qualified copy constructor. 10312 this._value = unqual(value); 10313 this._empty = false; 10314 } 10315 10316 T opIndex(size_t i) 10317 { 10318 assert(!_empty && i == 0, "Attempting to fetch an out of bounds index from an Only range"); 10319 return fetchFront(); 10320 } 10321 10322 OnlyResult opSlice() 10323 { 10324 return this; 10325 } 10326 10327 OnlyResult opSlice(size_t from, size_t to) 10328 { 10329 assert( 10330 from <= to, 10331 "Attempting to slice an Only range with a larger first argument than the second." 10332 ); 10333 assert( 10334 to <= length, 10335 "Attempting to slice using an out of bounds index on an Only range" 10336 ); 10337 OnlyResult copy = this; 10338 copy._empty = _empty || from == to; 10339 return copy; 10340 } 10341 10342 // This may alias to shared or immutable data. Do not let the user 10343 // to access this directly, and do not allow mutation without checking 10344 // the qualifier. 10345 private Unqual!T _value; 10346 private bool _empty = true; 10347 private @trusted T fetchFront() 10348 { 10349 return *cast(T*)&_value; 10350 } 10351 } 10352 10353 /** 10354 Assemble `values` into a range that carries all its 10355 elements in-situ. 10356 10357 Useful when a single value or multiple disconnected values 10358 must be passed to an algorithm expecting a range, without 10359 having to perform dynamic memory allocation. 10360 10361 As copying the range means copying all elements, it can be 10362 safely returned from functions. For the same reason, copying 10363 the returned range may be expensive for a large number of arguments. 10364 10365 Params: 10366 values = the values to assemble together 10367 10368 Returns: 10369 A `RandomAccessRange` of the assembled values. 10370 10371 See_Also: $(LREF chain) to chain ranges 10372 */ 10373 auto only(Values...)(return scope Values values) 10374 if (!is(CommonType!Values == void)) 10375 { 10376 return OnlyResult!Values(values); 10377 } 10378 10379 /// ditto 10380 auto only()() 10381 { 10382 // cannot use noreturn due to https://issues.dlang.org/show_bug.cgi?id=22383 10383 struct EmptyElementType {} 10384 EmptyElementType[] result; 10385 return result; 10386 } 10387 10388 /// 10389 @safe unittest 10390 { 10391 import std.algorithm.comparison : equal; 10392 import std.algorithm.iteration : filter, joiner, map; 10393 import std.algorithm.searching : findSplitBefore; 10394 import std.uni : isUpper; 10395 10396 assert(equal(only('♡'), "♡")); 10397 assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); 10398 10399 assert(only("one", "two", "three").joiner(" ").equal("one two three")); 10400 10401 string title = "The D Programming Language"; 10402 assert(title 10403 .filter!isUpper // take the upper case letters 10404 .map!only // make each letter its own range 10405 .joiner(".") // join the ranges together lazily 10406 .equal("T.D.P.L")); 10407 } 10408 10409 // https://issues.dlang.org/show_bug.cgi?id=20314 10410 @safe unittest 10411 { 10412 import std.algorithm.iteration : joiner; 10413 10414 const string s = "foo", t = "bar"; 10415 10416 assert([only(s, t), only(t, s)].joiner(only(", ")).join == "foobar, barfoo"); 10417 } 10418 10419 // Tests the zero-element result 10420 @safe unittest 10421 { 10422 import std.algorithm.comparison : equal; 10423 10424 auto emptyRange = only(); 10425 10426 alias EmptyRange = typeof(emptyRange); 10427 static assert(isInputRange!EmptyRange); 10428 static assert(isForwardRange!EmptyRange); 10429 static assert(isBidirectionalRange!EmptyRange); 10430 static assert(isRandomAccessRange!EmptyRange); 10431 static assert(hasLength!EmptyRange); 10432 static assert(hasSlicing!EmptyRange); 10433 10434 assert(emptyRange.empty); 10435 assert(emptyRange.length == 0); 10436 assert(emptyRange.equal(emptyRange[])); 10437 assert(emptyRange.equal(emptyRange.save)); 10438 assert(emptyRange[0 .. 0].equal(emptyRange)); 10439 } 10440 10441 // Tests the single-element result 10442 @safe unittest 10443 { 10444 import std.algorithm.comparison : equal; 10445 import std.typecons : tuple; 10446 foreach (x; tuple(1, '1', 1.0, "1", [1])) 10447 { 10448 auto a = only(x); 10449 typeof(x)[] e = []; 10450 assert(a.front == x); 10451 assert(a.back == x); 10452 assert(!a.empty); 10453 assert(a.length == 1); 10454 assert(equal(a, a[])); 10455 assert(equal(a, a[0 .. 1])); 10456 assert(equal(a[0 .. 0], e)); 10457 assert(equal(a[1 .. 1], e)); 10458 assert(a[0] == x); 10459 10460 auto b = a.save; 10461 assert(equal(a, b)); 10462 a.popFront(); 10463 assert(a.empty && a.length == 0 && a[].empty); 10464 b.popBack(); 10465 assert(b.empty && b.length == 0 && b[].empty); 10466 10467 alias A = typeof(a); 10468 static assert(isInputRange!A); 10469 static assert(isForwardRange!A); 10470 static assert(isBidirectionalRange!A); 10471 static assert(isRandomAccessRange!A); 10472 static assert(hasLength!A); 10473 static assert(hasSlicing!A); 10474 } 10475 10476 auto imm = only!(immutable int)(1); 10477 immutable int[] imme = []; 10478 assert(imm.front == 1); 10479 assert(imm.back == 1); 10480 assert(!imm.empty); 10481 assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441 10482 assert(imm.length == 1); 10483 assert(equal(imm, imm[])); 10484 assert(equal(imm, imm[0 .. 1])); 10485 assert(equal(imm[0 .. 0], imme)); 10486 assert(equal(imm[1 .. 1], imme)); 10487 assert(imm[0] == 1); 10488 } 10489 10490 // Tests multiple-element results 10491 @safe unittest 10492 { 10493 import std.algorithm.comparison : equal; 10494 import std.algorithm.iteration : joiner; 10495 import std.meta : AliasSeq; 10496 static assert(!__traits(compiles, only(1, "1"))); 10497 10498 auto nums = only!(byte, uint, long)(1, 2, 3); 10499 static assert(is(ElementType!(typeof(nums)) == long)); 10500 assert(nums.length == 3); 10501 10502 foreach (i; 0 .. 3) 10503 assert(nums[i] == i + 1); 10504 10505 auto saved = nums.save; 10506 10507 foreach (i; 1 .. 4) 10508 { 10509 assert(nums.front == nums[0]); 10510 assert(nums.front == i); 10511 nums.popFront(); 10512 assert(nums.length == 3 - i); 10513 } 10514 10515 assert(nums.empty); 10516 10517 assert(saved.equal(only(1, 2, 3))); 10518 assert(saved.equal(saved[])); 10519 assert(saved[0 .. 1].equal(only(1))); 10520 assert(saved[0 .. 2].equal(only(1, 2))); 10521 assert(saved[0 .. 3].equal(saved)); 10522 assert(saved[1 .. 3].equal(only(2, 3))); 10523 assert(saved[2 .. 3].equal(only(3))); 10524 assert(saved[0 .. 0].empty); 10525 assert(saved[3 .. 3].empty); 10526 10527 alias data = AliasSeq!("one", "two", "three", "four"); 10528 static joined = 10529 ["one two", "one two three", "one two three four"]; 10530 string[] joinedRange = joined; 10531 10532 static foreach (argCount; 2 .. 5) 10533 {{ 10534 auto values = only(data[0 .. argCount]); 10535 alias Values = typeof(values); 10536 static assert(is(ElementType!Values == string)); 10537 static assert(isInputRange!Values); 10538 static assert(isForwardRange!Values); 10539 static assert(isBidirectionalRange!Values); 10540 static assert(isRandomAccessRange!Values); 10541 static assert(hasSlicing!Values); 10542 static assert(hasLength!Values); 10543 10544 assert(values.length == argCount); 10545 assert(values[0 .. $].equal(values[0 .. values.length])); 10546 assert(values.joiner(" ").equal(joinedRange.front)); 10547 joinedRange.popFront(); 10548 }} 10549 10550 assert(saved.retro.equal(only(3, 2, 1))); 10551 assert(saved.length == 3); 10552 10553 assert(saved.back == 3); 10554 saved.popBack(); 10555 assert(saved.length == 2); 10556 assert(saved.back == 2); 10557 10558 assert(saved.front == 1); 10559 saved.popFront(); 10560 assert(saved.length == 1); 10561 assert(saved.front == 2); 10562 10563 saved.popBack(); 10564 assert(saved.empty); 10565 10566 auto imm = only!(immutable int, immutable int)(42, 24); 10567 alias Imm = typeof(imm); 10568 static assert(is(ElementType!Imm == immutable(int))); 10569 assert(!imm.empty); 10570 assert(imm.init.empty); // https://issues.dlang.org/show_bug.cgi?id=13441 10571 assert(imm.front == 42); 10572 imm.popFront(); 10573 assert(imm.front == 24); 10574 imm.popFront(); 10575 assert(imm.empty); 10576 10577 static struct Test { int* a; } 10578 immutable(Test) test; 10579 cast(void) only(test, test); // Works with mutable indirection 10580 } 10581 10582 // https://issues.dlang.org/show_bug.cgi?id=21129 10583 @safe unittest 10584 { 10585 auto range = () @safe { 10586 const(char)[5] staticStr = "Hello"; 10587 10588 // `only` must store a char[5] - not a char[]! 10589 return only(staticStr, " World"); 10590 } (); 10591 10592 assert(range.join == "Hello World"); 10593 } 10594 10595 // https://issues.dlang.org/show_bug.cgi?id=21129 10596 @safe unittest 10597 { 10598 struct AliasedString 10599 { 10600 const(char)[5] staticStr = "Hello"; 10601 10602 @property const(char)[] slice() const 10603 { 10604 return staticStr[]; 10605 } 10606 alias slice this; 10607 } 10608 10609 auto range = () @safe { 10610 auto hello = AliasedString(); 10611 10612 // a copy of AliasedString is stored in the range. 10613 return only(hello, " World"); 10614 } (); 10615 10616 assert(range.join == "Hello World"); 10617 } 10618 10619 // https://issues.dlang.org/show_bug.cgi?id=21022 10620 @safe pure nothrow unittest 10621 { 10622 struct S 10623 { 10624 int* mem; 10625 } 10626 10627 immutable S x; 10628 immutable(S)[] arr; 10629 auto r1 = arr.chain(x.only, only(x, x)); 10630 } 10631 10632 /** 10633 Iterate over `range` with an attached index variable. 10634 10635 Each element is a $(REF Tuple, std,typecons) containing the index 10636 and the element, in that order, where the index member is named `index` 10637 and the element member is named `value`. 10638 10639 The index starts at `start` and is incremented by one on every iteration. 10640 10641 Overflow: 10642 If `range` has length, then it is an error to pass a value for `start` 10643 so that `start + range.length` is bigger than `Enumerator.max`, thus 10644 it is ensured that overflow cannot happen. 10645 10646 If `range` does not have length, and `popFront` is called when 10647 `front.index == Enumerator.max`, the index will overflow and 10648 continue from `Enumerator.min`. 10649 10650 Params: 10651 range = the $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to attach indexes to 10652 start = the number to start the index counter from 10653 10654 Returns: 10655 At minimum, an input range. All other range primitives are given in the 10656 resulting range if `range` has them. The exceptions are the bidirectional 10657 primitives, which are propagated only if `range` has length. 10658 10659 Example: 10660 Useful for using `foreach` with an index loop variable: 10661 ---- 10662 import std.stdio : stdin, stdout; 10663 import std.range : enumerate; 10664 10665 foreach (lineNum, line; stdin.byLine().enumerate(1)) 10666 stdout.writefln("line #%s: %s", lineNum, line); 10667 ---- 10668 */ 10669 auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0) 10670 if (isIntegral!Enumerator && isInputRange!Range) 10671 in 10672 { 10673 static if (hasLength!Range) 10674 { 10675 // TODO: core.checkedint supports mixed signedness yet? 10676 import core.checkedint : adds, addu; 10677 import std.conv : ConvException, to; 10678 import std.traits : isSigned, Largest, Signed; 10679 10680 alias LengthType = typeof(range.length); 10681 bool overflow; 10682 static if (isSigned!Enumerator && isSigned!LengthType) 10683 auto result = adds(start, range.length, overflow); 10684 else static if (isSigned!Enumerator) 10685 { 10686 alias signed_t = Largest!(Enumerator, Signed!LengthType); 10687 signed_t signedLength; 10688 //This is to trick the compiler because if length is enum 10689 //the compiler complains about unreachable code. 10690 auto getLength() 10691 { 10692 return range.length; 10693 } 10694 //Can length fit in the signed type 10695 assert(getLength() < signed_t.max, 10696 "a signed length type is required but the range's length() is too great"); 10697 signedLength = range.length; 10698 auto result = adds(start, signedLength, overflow); 10699 } 10700 else 10701 { 10702 static if (isSigned!LengthType) 10703 assert(range.length >= 0); 10704 auto result = addu(start, range.length, overflow); 10705 } 10706 10707 assert(!overflow && result <= Enumerator.max); 10708 } 10709 } 10710 do 10711 { 10712 // TODO: Relax isIntegral!Enumerator to allow user-defined integral types 10713 static struct Result 10714 { 10715 import std.typecons : Tuple; 10716 10717 private: 10718 alias ElemType = Tuple!(Enumerator, "index", ElementType!Range, "value"); 10719 Range range; 10720 Unqual!Enumerator index; 10721 10722 public: 10723 ElemType front() @property 10724 { 10725 assert(!range.empty, "Attempting to fetch the front of an empty enumerate"); 10726 return typeof(return)(index, range.front); 10727 } 10728 10729 static if (isInfinite!Range) 10730 enum bool empty = false; 10731 else 10732 { 10733 bool empty() @property 10734 { 10735 return range.empty; 10736 } 10737 } 10738 10739 void popFront() 10740 { 10741 assert(!range.empty, "Attempting to popFront an empty enumerate"); 10742 range.popFront(); 10743 ++index; // When !hasLength!Range, overflow is expected 10744 } 10745 10746 static if (isForwardRange!Range) 10747 { 10748 Result save() @property 10749 { 10750 return typeof(return)(range.save, index); 10751 } 10752 } 10753 10754 static if (hasLength!Range) 10755 { 10756 mixin ImplementLength!range; 10757 10758 static if (isBidirectionalRange!Range) 10759 { 10760 ElemType back() @property 10761 { 10762 assert(!range.empty, "Attempting to fetch the back of an empty enumerate"); 10763 return typeof(return)(cast(Enumerator)(index + range.length - 1), range.back); 10764 } 10765 10766 void popBack() 10767 { 10768 assert(!range.empty, "Attempting to popBack an empty enumerate"); 10769 range.popBack(); 10770 } 10771 } 10772 } 10773 10774 static if (isRandomAccessRange!Range) 10775 { 10776 ElemType opIndex(size_t i) 10777 { 10778 return typeof(return)(cast(Enumerator)(index + i), range[i]); 10779 } 10780 } 10781 10782 static if (hasSlicing!Range) 10783 { 10784 static if (hasLength!Range) 10785 { 10786 Result opSlice(size_t i, size_t j) 10787 { 10788 return typeof(return)(range[i .. j], cast(Enumerator)(index + i)); 10789 } 10790 } 10791 else 10792 { 10793 static struct DollarToken {} 10794 enum opDollar = DollarToken.init; 10795 10796 Result opSlice(size_t i, DollarToken) 10797 { 10798 return typeof(return)(range[i .. $], cast(Enumerator)(index + i)); 10799 } 10800 10801 auto opSlice(size_t i, size_t j) 10802 { 10803 return this[i .. $].takeExactly(j - 1); 10804 } 10805 } 10806 } 10807 } 10808 10809 return Result(range, start); 10810 } 10811 10812 /// Can start enumeration from a negative position: 10813 pure @safe nothrow unittest 10814 { 10815 import std.array : assocArray; 10816 import std.range : enumerate; 10817 10818 bool[int] aa = true.repeat(3).enumerate(-1).assocArray(); 10819 assert(aa[-1]); 10820 assert(aa[0]); 10821 assert(aa[1]); 10822 } 10823 10824 // Make sure passing qualified types works 10825 pure @safe nothrow unittest 10826 { 10827 char[4] v; 10828 immutable start = 2; 10829 v[2 .. $].enumerate(start); 10830 } 10831 10832 pure @safe nothrow unittest 10833 { 10834 import std.internal.test.dummyrange : AllDummyRanges; 10835 import std.meta : AliasSeq; 10836 import std.typecons : tuple; 10837 10838 static struct HasSlicing 10839 { 10840 typeof(this) front() @property { return typeof(this).init; } 10841 bool empty() @property { return true; } 10842 void popFront() {} 10843 10844 typeof(this) opSlice(size_t, size_t) 10845 { 10846 return typeof(this)(); 10847 } 10848 } 10849 10850 static foreach (DummyType; AliasSeq!(AllDummyRanges, HasSlicing)) 10851 {{ 10852 alias R = typeof(enumerate(DummyType.init)); 10853 static assert(isInputRange!R); 10854 static assert(isForwardRange!R == isForwardRange!DummyType); 10855 static assert(isRandomAccessRange!R == isRandomAccessRange!DummyType); 10856 static assert(!hasAssignableElements!R); 10857 10858 static if (hasLength!DummyType) 10859 { 10860 static assert(hasLength!R); 10861 static assert(isBidirectionalRange!R == 10862 isBidirectionalRange!DummyType); 10863 } 10864 10865 static assert(hasSlicing!R == hasSlicing!DummyType); 10866 }} 10867 10868 static immutable values = ["zero", "one", "two", "three"]; 10869 auto enumerated = values[].enumerate(); 10870 assert(!enumerated.empty); 10871 assert(enumerated.front == tuple(0, "zero")); 10872 assert(enumerated.back == tuple(3, "three")); 10873 10874 typeof(enumerated) saved = enumerated.save; 10875 saved.popFront(); 10876 assert(enumerated.front == tuple(0, "zero")); 10877 assert(saved.front == tuple(1, "one")); 10878 assert(saved.length == enumerated.length - 1); 10879 saved.popBack(); 10880 assert(enumerated.back == tuple(3, "three")); 10881 assert(saved.back == tuple(2, "two")); 10882 saved.popFront(); 10883 assert(saved.front == tuple(2, "two")); 10884 assert(saved.back == tuple(2, "two")); 10885 saved.popFront(); 10886 assert(saved.empty); 10887 10888 size_t control = 0; 10889 foreach (i, v; enumerated) 10890 { 10891 static assert(is(typeof(i) == size_t)); 10892 static assert(is(typeof(v) == typeof(values[0]))); 10893 assert(i == control); 10894 assert(v == values[i]); 10895 assert(tuple(i, v) == enumerated[i]); 10896 ++control; 10897 } 10898 10899 assert(enumerated[0 .. $].front == tuple(0, "zero")); 10900 assert(enumerated[$ - 1 .. $].front == tuple(3, "three")); 10901 10902 foreach (i; 0 .. 10) 10903 { 10904 auto shifted = values[0 .. 2].enumerate(i); 10905 assert(shifted.front == tuple(i, "zero")); 10906 assert(shifted[0] == shifted.front); 10907 10908 auto next = tuple(i + 1, "one"); 10909 assert(shifted[1] == next); 10910 shifted.popFront(); 10911 assert(shifted.front == next); 10912 shifted.popFront(); 10913 assert(shifted.empty); 10914 } 10915 10916 static foreach (T; AliasSeq!(ubyte, byte, uint, int)) 10917 {{ 10918 auto inf = 42.repeat().enumerate(T.max); 10919 alias Inf = typeof(inf); 10920 static assert(isInfinite!Inf); 10921 static assert(hasSlicing!Inf); 10922 10923 // test overflow 10924 assert(inf.front == tuple(T.max, 42)); 10925 inf.popFront(); 10926 assert(inf.front == tuple(T.min, 42)); 10927 10928 // test slicing 10929 inf = inf[42 .. $]; 10930 assert(inf.front == tuple(T.min + 42, 42)); 10931 auto window = inf[0 .. 2]; 10932 assert(window.length == 1); 10933 assert(window.front == inf.front); 10934 window.popFront(); 10935 assert(window.empty); 10936 }} 10937 } 10938 10939 pure @safe unittest 10940 { 10941 import std.algorithm.comparison : equal; 10942 import std.meta : AliasSeq; 10943 static immutable int[] values = [0, 1, 2, 3, 4]; 10944 static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 10945 {{ 10946 auto enumerated = values.enumerate!T(); 10947 static assert(is(typeof(enumerated.front.index) == T)); 10948 assert(enumerated.equal(values[].zip(values))); 10949 10950 foreach (T i; 0 .. 5) 10951 { 10952 auto subset = values[cast(size_t) i .. $]; 10953 auto offsetEnumerated = subset.enumerate(i); 10954 static assert(is(typeof(enumerated.front.index) == T)); 10955 assert(offsetEnumerated.equal(subset.zip(subset))); 10956 } 10957 }} 10958 } 10959 @nogc @safe unittest 10960 { 10961 const val = iota(1, 100).enumerate(1); 10962 } 10963 @nogc @safe unittest 10964 { 10965 import core.exception : AssertError; 10966 import std.exception : assertThrown; 10967 struct RangePayload { 10968 enum length = size_t.max; 10969 void popFront() {} 10970 int front() { return 0; } 10971 bool empty() { return true; } 10972 } 10973 RangePayload thePayload; 10974 //Assertion won't happen when contracts are disabled for -release. 10975 debug assertThrown!AssertError(enumerate(thePayload, -10)); 10976 } 10977 // https://issues.dlang.org/show_bug.cgi?id=10939 10978 version (none) 10979 { 10980 // Re-enable (or remove) if 10939 is resolved. 10981 /+pure+/ @safe unittest // Impure because of std.conv.to 10982 { 10983 import core.exception : RangeError; 10984 import std.exception : assertNotThrown, assertThrown; 10985 import std.meta : AliasSeq; 10986 10987 static immutable values = [42]; 10988 10989 static struct SignedLengthRange 10990 { 10991 immutable(int)[] _values = values; 10992 10993 int front() @property { assert(false); } 10994 bool empty() @property { assert(false); } 10995 void popFront() { assert(false); } 10996 10997 int length() @property 10998 { 10999 return cast(int)_values.length; 11000 } 11001 } 11002 11003 SignedLengthRange svalues; 11004 static foreach (Enumerator; AliasSeq!(ubyte, byte, ushort, short, uint, int, ulong, long)) 11005 { 11006 assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max)); 11007 assertNotThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length)); 11008 assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length + 1)); 11009 11010 assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max)); 11011 assertNotThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length)); 11012 assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length + 1)); 11013 } 11014 11015 static foreach (Enumerator; AliasSeq!(byte, short, int)) 11016 { 11017 assertThrown!RangeError(repeat(0, uint.max).enumerate!Enumerator()); 11018 } 11019 11020 assertNotThrown!RangeError(repeat(0, uint.max).enumerate!long()); 11021 } 11022 } 11023 11024 /** 11025 Returns true if `fn` accepts variables of type T1 and T2 in any order. 11026 The following code should compile: 11027 --- 11028 (ref T1 a, ref T2 b) 11029 { 11030 fn(a, b); 11031 fn(b, a); 11032 } 11033 --- 11034 */ 11035 template isTwoWayCompatible(alias fn, T1, T2) 11036 { 11037 enum isTwoWayCompatible = is(typeof((ref T1 a, ref T2 b) 11038 { 11039 cast(void) fn(a, b); 11040 cast(void) fn(b, a); 11041 } 11042 )); 11043 } 11044 11045 /// 11046 @safe unittest 11047 { 11048 void func1(int a, int b); 11049 void func2(int a, float b); 11050 11051 static assert(isTwoWayCompatible!(func1, int, int)); 11052 static assert(isTwoWayCompatible!(func1, short, int)); 11053 static assert(!isTwoWayCompatible!(func2, int, float)); 11054 11055 void func3(ref int a, ref int b); 11056 static assert( isTwoWayCompatible!(func3, int, int)); 11057 static assert(!isTwoWayCompatible!(func3, short, int)); 11058 } 11059 11060 11061 /** 11062 Policy used with the searching primitives `lowerBound`, $(D 11063 upperBound), and `equalRange` of $(LREF SortedRange) below. 11064 */ 11065 enum SearchPolicy 11066 { 11067 /** 11068 Searches in a linear fashion. 11069 */ 11070 linear, 11071 11072 /** 11073 Searches with a step that is grows linearly (1, 2, 3,...) 11074 leading to a quadratic search schedule (indexes tried are 0, 1, 11075 3, 6, 10, 15, 21, 28,...) Once the search overshoots its target, 11076 the remaining interval is searched using binary search. The 11077 search is completed in $(BIGOH sqrt(n)) time. Use it when you 11078 are reasonably confident that the value is around the beginning 11079 of the range. 11080 */ 11081 trot, 11082 11083 /** 11084 Performs a $(LINK2 https://en.wikipedia.org/wiki/Exponential_search, 11085 galloping search algorithm), i.e. searches 11086 with a step that doubles every time, (1, 2, 4, 8, ...) leading 11087 to an exponential search schedule (indexes tried are 0, 1, 3, 11088 7, 15, 31, 63,...) Once the search overshoots its target, the 11089 remaining interval is searched using binary search. A value is 11090 found in $(BIGOH log(n)) time. 11091 */ 11092 gallop, 11093 11094 /** 11095 Searches using a classic interval halving policy. The search 11096 starts in the middle of the range, and each search step cuts 11097 the range in half. This policy finds a value in $(BIGOH log(n)) 11098 time but is less cache friendly than `gallop` for large 11099 ranges. The `binarySearch` policy is used as the last step 11100 of `trot`, `gallop`, `trotBackwards`, and $(D 11101 gallopBackwards) strategies. 11102 */ 11103 binarySearch, 11104 11105 /** 11106 Similar to `trot` but starts backwards. Use it when 11107 confident that the value is around the end of the range. 11108 */ 11109 trotBackwards, 11110 11111 /** 11112 Similar to `gallop` but starts backwards. Use it when 11113 confident that the value is around the end of the range. 11114 */ 11115 gallopBackwards 11116 } 11117 11118 /// 11119 @safe unittest 11120 { 11121 import std.algorithm.comparison : equal; 11122 11123 auto a = assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 11124 auto p1 = a.upperBound!(SearchPolicy.binarySearch)(3); 11125 assert(p1.equal([4, 5, 6, 7, 8, 9])); 11126 11127 auto p2 = a.lowerBound!(SearchPolicy.gallop)(4); 11128 assert(p2.equal([0, 1, 2, 3])); 11129 } 11130 11131 /** 11132 Options for $(LREF SortedRange) ranges (below). 11133 */ 11134 enum SortedRangeOptions 11135 { 11136 /** 11137 Assume, that the range is sorted without checking. 11138 */ 11139 assumeSorted, 11140 11141 /** 11142 All elements of the range are checked to be sorted. 11143 The check is performed in O(n) time. 11144 */ 11145 checkStrictly, 11146 11147 /** 11148 Some elements of the range are checked to be sorted. 11149 For ranges with random order, this will almost surely 11150 detect, that it is not sorted. For almost sorted ranges 11151 it's more likely to fail. The checked elements are choosen 11152 in a deterministic manner, which makes this check reproducable. 11153 The check is performed in O(log(n)) time. 11154 */ 11155 checkRoughly, 11156 } 11157 11158 /// 11159 @safe pure unittest 11160 { 11161 // create a SortedRange, that's checked strictly 11162 SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]); 11163 } 11164 11165 /** 11166 Represents a sorted range. In addition to the regular range 11167 primitives, supports additional operations that take advantage of the 11168 ordering, such as merge and binary search. To obtain a $(D 11169 SortedRange) from an unsorted range `r`, use 11170 $(REF sort, std,algorithm,sorting) which sorts `r` in place and returns the 11171 corresponding `SortedRange`. To construct a `SortedRange` from a range 11172 `r` that is known to be already sorted, use $(LREF assumeSorted). 11173 11174 Params: 11175 pred: The predicate used to define the sortedness 11176 opt: Controls how strongly the range is checked for sortedness. 11177 Will only be used for `RandomAccessRanges`. 11178 Will not be used in CTFE. 11179 */ 11180 struct SortedRange(Range, alias pred = "a < b", 11181 SortedRangeOptions opt = SortedRangeOptions.assumeSorted) 11182 if (isInputRange!Range && !isInstanceOf!(SortedRange, Range)) 11183 { 11184 import std.functional : binaryFun; 11185 11186 private alias predFun = binaryFun!pred; 11187 private bool geq(L, R)(L lhs, R rhs) 11188 { 11189 return !predFun(lhs, rhs); 11190 } 11191 private bool gt(L, R)(L lhs, R rhs) 11192 { 11193 return predFun(rhs, lhs); 11194 } 11195 private Range _input; 11196 11197 // Undocummented because a clearer way to invoke is by calling 11198 // assumeSorted. 11199 this(Range input) 11200 { 11201 static if (opt == SortedRangeOptions.checkRoughly) 11202 { 11203 roughlyVerifySorted(input); 11204 } 11205 static if (opt == SortedRangeOptions.checkStrictly) 11206 { 11207 strictlyVerifySorted(input); 11208 } 11209 this._input = input; 11210 } 11211 11212 // Assertion only. 11213 static if (opt == SortedRangeOptions.checkRoughly) 11214 private void roughlyVerifySorted(Range r) 11215 { 11216 if (!__ctfe) 11217 { 11218 static if (isRandomAccessRange!Range && hasLength!Range) 11219 { 11220 import core.bitop : bsr; 11221 import std.algorithm.sorting : isSorted; 11222 import std.exception : enforce; 11223 11224 // Check the sortedness of the input 11225 if (r.length < 2) return; 11226 11227 immutable size_t msb = bsr(r.length) + 1; 11228 assert(msb > 0 && msb <= r.length); 11229 immutable step = r.length / msb; 11230 auto st = stride(r, step); 11231 11232 enforce(isSorted!pred(st), "Range is not sorted"); 11233 } 11234 } 11235 } 11236 11237 // Assertion only. 11238 static if (opt == SortedRangeOptions.checkStrictly) 11239 private void strictlyVerifySorted(Range r) 11240 { 11241 if (!__ctfe) 11242 { 11243 static if (isRandomAccessRange!Range && hasLength!Range) 11244 { 11245 import std.algorithm.sorting : isSorted; 11246 import std.exception : enforce; 11247 11248 enforce(isSorted!pred(r), "Range is not sorted"); 11249 } 11250 } 11251 } 11252 11253 /// Range primitives. 11254 @property bool empty() //const 11255 { 11256 return this._input.empty; 11257 } 11258 11259 /// Ditto 11260 static if (isForwardRange!Range) 11261 @property auto save() 11262 { 11263 // Avoid the constructor 11264 typeof(this) result = this; 11265 result._input = _input.save; 11266 return result; 11267 } 11268 11269 /// Ditto 11270 @property auto ref front() 11271 { 11272 return _input.front; 11273 } 11274 11275 /// Ditto 11276 void popFront() 11277 { 11278 _input.popFront(); 11279 } 11280 11281 /// Ditto 11282 static if (isBidirectionalRange!Range) 11283 { 11284 @property auto ref back() 11285 { 11286 return _input.back; 11287 } 11288 11289 /// Ditto 11290 void popBack() 11291 { 11292 _input.popBack(); 11293 } 11294 } 11295 11296 /// Ditto 11297 static if (isRandomAccessRange!Range) 11298 auto ref opIndex(size_t i) 11299 { 11300 return _input[i]; 11301 } 11302 11303 /// Ditto 11304 static if (hasSlicing!Range) 11305 auto opSlice(size_t a, size_t b) return scope 11306 { 11307 assert( 11308 a <= b, 11309 "Attempting to slice a SortedRange with a larger first argument than the second." 11310 ); 11311 typeof(this) result = this; 11312 result._input = _input[a .. b];// skip checking 11313 return result; 11314 } 11315 11316 mixin ImplementLength!_input; 11317 11318 /** 11319 Releases the controlled range and returns it. 11320 11321 This does the opposite of $(LREF assumeSorted): instead of turning a range 11322 into a `SortedRange`, it extracts the original range back out of the `SortedRange` 11323 using $(REF, move, std,algorithm,mutation). 11324 */ 11325 auto release() return scope 11326 { 11327 import std.algorithm.mutation : move; 11328 return move(_input); 11329 } 11330 11331 /// 11332 static if (is(Range : int[])) 11333 @safe unittest 11334 { 11335 import std.algorithm.sorting : sort; 11336 int[3] data = [ 1, 2, 3 ]; 11337 auto a = assumeSorted(data[]); 11338 assert(a == sort!"a < b"(data[])); 11339 int[] p = a.release(); 11340 assert(p == [ 1, 2, 3 ]); 11341 } 11342 11343 // Assuming a predicate "test" that returns 0 for a left portion 11344 // of the range and then 1 for the rest, returns the index at 11345 // which the first 1 appears. Used internally by the search routines. 11346 private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) 11347 if (sp == SearchPolicy.binarySearch && isRandomAccessRange!Range && hasLength!Range) 11348 { 11349 size_t first = 0, count = _input.length; 11350 while (count > 0) 11351 { 11352 immutable step = count / 2, it = first + step; 11353 if (!test(_input[it], v)) 11354 { 11355 first = it + 1; 11356 count -= step + 1; 11357 } 11358 else 11359 { 11360 count = step; 11361 } 11362 } 11363 return first; 11364 } 11365 11366 // Specialization for trot and gallop 11367 private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) 11368 if ((sp == SearchPolicy.trot || sp == SearchPolicy.gallop) 11369 && isRandomAccessRange!Range) 11370 { 11371 if (empty || test(front, v)) return 0; 11372 immutable count = length; 11373 if (count == 1) return 1; 11374 size_t below = 0, above = 1, step = 2; 11375 while (!test(_input[above], v)) 11376 { 11377 // Still too small, update below and increase gait 11378 below = above; 11379 immutable next = above + step; 11380 if (next >= count) 11381 { 11382 // Overshot - the next step took us beyond the end. So 11383 // now adjust next and simply exit the loop to do the 11384 // binary search thingie. 11385 above = count; 11386 break; 11387 } 11388 // Still in business, increase step and continue 11389 above = next; 11390 static if (sp == SearchPolicy.trot) 11391 ++step; 11392 else 11393 step <<= 1; 11394 } 11395 return below + this[below .. above].getTransitionIndex!( 11396 SearchPolicy.binarySearch, test, V)(v); 11397 } 11398 11399 // Specialization for trotBackwards and gallopBackwards 11400 private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) 11401 if ((sp == SearchPolicy.trotBackwards || sp == SearchPolicy.gallopBackwards) 11402 && isRandomAccessRange!Range) 11403 { 11404 immutable count = length; 11405 if (empty || !test(back, v)) return count; 11406 if (count == 1) return 0; 11407 size_t below = count - 2, above = count - 1, step = 2; 11408 while (test(_input[below], v)) 11409 { 11410 // Still too large, update above and increase gait 11411 above = below; 11412 if (below < step) 11413 { 11414 // Overshot - the next step took us beyond the end. So 11415 // now adjust next and simply fall through to do the 11416 // binary search thingie. 11417 below = 0; 11418 break; 11419 } 11420 // Still in business, increase step and continue 11421 below -= step; 11422 static if (sp == SearchPolicy.trot) 11423 ++step; 11424 else 11425 step <<= 1; 11426 } 11427 return below + this[below .. above].getTransitionIndex!( 11428 SearchPolicy.binarySearch, test, V)(v); 11429 } 11430 11431 // lowerBound 11432 /** 11433 This function uses a search with policy `sp` to find the 11434 largest left subrange on which $(D pred(x, value)) is `true` for 11435 all `x` (e.g., if `pred` is "less than", returns the portion of 11436 the range with elements strictly smaller than `value`). The search 11437 schedule and its complexity are documented in 11438 $(LREF SearchPolicy). 11439 */ 11440 auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) 11441 if (isTwoWayCompatible!(predFun, ElementType!Range, V) 11442 && hasSlicing!Range) 11443 { 11444 return this[0 .. getTransitionIndex!(sp, geq)(value)]; 11445 } 11446 11447 /// 11448 static if (is(Range : int[])) 11449 @safe unittest 11450 { 11451 import std.algorithm.comparison : equal; 11452 auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]); 11453 auto p = a.lowerBound(4); 11454 assert(equal(p, [ 0, 1, 2, 3 ])); 11455 } 11456 11457 // upperBound 11458 /** 11459 This function searches with policy `sp` to find the largest right 11460 subrange on which $(D pred(value, x)) is `true` for all `x` 11461 (e.g., if `pred` is "less than", returns the portion of the range 11462 with elements strictly greater than `value`). The search schedule 11463 and its complexity are documented in $(LREF SearchPolicy). 11464 11465 For ranges that do not offer random access, `SearchPolicy.linear` 11466 is the only policy allowed (and it must be specified explicitly lest it exposes 11467 user code to unexpected inefficiencies). For random-access searches, all 11468 policies are allowed, and `SearchPolicy.binarySearch` is the default. 11469 */ 11470 auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) 11471 if (isTwoWayCompatible!(predFun, ElementType!Range, V)) 11472 { 11473 static assert(hasSlicing!Range || sp == SearchPolicy.linear, 11474 "Specify SearchPolicy.linear explicitly for " 11475 ~ typeof(this).stringof); 11476 static if (sp == SearchPolicy.linear) 11477 { 11478 for (; !_input.empty && !predFun(value, _input.front); 11479 _input.popFront()) 11480 { 11481 } 11482 return this; 11483 } 11484 else 11485 { 11486 return this[getTransitionIndex!(sp, gt)(value) .. length]; 11487 } 11488 } 11489 11490 /// 11491 static if (is(Range : int[])) 11492 @safe unittest 11493 { 11494 import std.algorithm.comparison : equal; 11495 auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); 11496 auto p = a.upperBound(3); 11497 assert(equal(p, [4, 4, 5, 6])); 11498 } 11499 11500 11501 // equalRange 11502 /** 11503 Returns the subrange containing all elements `e` for which both $(D 11504 pred(e, value)) and $(D pred(value, e)) evaluate to `false` (e.g., 11505 if `pred` is "less than", returns the portion of the range with 11506 elements equal to `value`). Uses a classic binary search with 11507 interval halving until it finds a value that satisfies the condition, 11508 then uses `SearchPolicy.gallopBackwards` to find the left boundary 11509 and `SearchPolicy.gallop` to find the right boundary. These 11510 policies are justified by the fact that the two boundaries are likely 11511 to be near the first found value (i.e., equal ranges are relatively 11512 small). Completes the entire search in $(BIGOH log(n)) time. 11513 */ 11514 auto equalRange(V)(V value) 11515 if (isTwoWayCompatible!(predFun, ElementType!Range, V) 11516 && isRandomAccessRange!Range) 11517 { 11518 size_t first = 0, count = _input.length; 11519 while (count > 0) 11520 { 11521 immutable step = count / 2; 11522 auto it = first + step; 11523 if (predFun(_input[it], value)) 11524 { 11525 // Less than value, bump left bound up 11526 first = it + 1; 11527 count -= step + 1; 11528 } 11529 else if (predFun(value, _input[it])) 11530 { 11531 // Greater than value, chop count 11532 count = step; 11533 } 11534 else 11535 { 11536 // Equal to value, do binary searches in the 11537 // leftover portions 11538 // Gallop towards the left end as it's likely nearby 11539 immutable left = first 11540 + this[first .. it] 11541 .lowerBound!(SearchPolicy.gallopBackwards)(value).length; 11542 first += count; 11543 // Gallop towards the right end as it's likely nearby 11544 immutable right = first 11545 - this[it + 1 .. first] 11546 .upperBound!(SearchPolicy.gallop)(value).length; 11547 return this[left .. right]; 11548 } 11549 } 11550 return this.init; 11551 } 11552 11553 /// 11554 static if (is(Range : int[])) 11555 @safe unittest 11556 { 11557 import std.algorithm.comparison : equal; 11558 auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11559 auto r = a.assumeSorted.equalRange(3); 11560 assert(equal(r, [ 3, 3, 3 ])); 11561 } 11562 11563 // trisect 11564 /** 11565 Returns a tuple `r` such that `r[0]` is the same as the result 11566 of `lowerBound(value)`, `r[1]` is the same as the result of $(D 11567 equalRange(value)), and `r[2]` is the same as the result of $(D 11568 upperBound(value)). The call is faster than computing all three 11569 separately. Uses a search schedule similar to $(D 11570 equalRange). Completes the entire search in $(BIGOH log(n)) time. 11571 */ 11572 auto trisect(V)(V value) 11573 if (isTwoWayCompatible!(predFun, ElementType!Range, V) 11574 && isRandomAccessRange!Range && hasLength!Range) 11575 { 11576 import std.typecons : tuple; 11577 size_t first = 0, count = _input.length; 11578 while (count > 0) 11579 { 11580 immutable step = count / 2; 11581 auto it = first + step; 11582 if (predFun(_input[it], value)) 11583 { 11584 // Less than value, bump left bound up 11585 first = it + 1; 11586 count -= step + 1; 11587 } 11588 else if (predFun(value, _input[it])) 11589 { 11590 // Greater than value, chop count 11591 count = step; 11592 } 11593 else 11594 { 11595 // Equal to value, do binary searches in the 11596 // leftover portions 11597 // Gallop towards the left end as it's likely nearby 11598 immutable left = first 11599 + this[first .. it] 11600 .lowerBound!(SearchPolicy.gallopBackwards)(value).length; 11601 first += count; 11602 // Gallop towards the right end as it's likely nearby 11603 immutable right = first 11604 - this[it + 1 .. first] 11605 .upperBound!(SearchPolicy.gallop)(value).length; 11606 return tuple(this[0 .. left], this[left .. right], 11607 this[right .. length]); 11608 } 11609 } 11610 // No equal element was found 11611 return tuple(this[0 .. first], this.init, this[first .. length]); 11612 } 11613 11614 /// 11615 static if (is(Range : int[])) 11616 @safe unittest 11617 { 11618 import std.algorithm.comparison : equal; 11619 auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11620 auto r = assumeSorted(a).trisect(3); 11621 assert(equal(r[0], [ 1, 2 ])); 11622 assert(equal(r[1], [ 3, 3, 3 ])); 11623 assert(equal(r[2], [ 4, 4, 5, 6 ])); 11624 } 11625 11626 // contains 11627 /** 11628 Returns `true` if and only if `value` can be found in $(D 11629 range), which is assumed to be sorted. Performs $(BIGOH log(r.length)) 11630 evaluations of `pred`. 11631 */ 11632 11633 bool contains(V)(V value) 11634 if (isRandomAccessRange!Range) 11635 { 11636 if (empty) return false; 11637 immutable i = getTransitionIndex!(SearchPolicy.binarySearch, geq)(value); 11638 if (i >= length) return false; 11639 return !predFun(value, _input[i]); 11640 } 11641 11642 /** 11643 Like `contains`, but the value is specified before the range. 11644 */ 11645 bool opBinaryRight(string op, V)(V value) 11646 if (op == "in" && isRandomAccessRange!Range) 11647 { 11648 return contains(value); 11649 } 11650 11651 // groupBy 11652 /** 11653 Returns a range of subranges of elements that are equivalent according to the 11654 sorting relation. 11655 */ 11656 auto groupBy()() 11657 { 11658 import std.algorithm.iteration : chunkBy; 11659 return _input.chunkBy!((a, b) => !predFun(a, b) && !predFun(b, a)); 11660 } 11661 } 11662 11663 /// ditto 11664 template SortedRange(Range, alias pred = "a < b", 11665 SortedRangeOptions opt = SortedRangeOptions.assumeSorted) 11666 if (isInstanceOf!(SortedRange, Range)) 11667 { 11668 // Avoid nesting SortedRange types (see https://issues.dlang.org/show_bug.cgi?id=18933); 11669 alias SortedRange = SortedRange!(Unqual!(typeof(Range._input)), pred, opt); 11670 } 11671 11672 /// 11673 @safe unittest 11674 { 11675 import std.algorithm.sorting : sort; 11676 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11677 auto r = assumeSorted(a); 11678 assert(r.contains(3)); 11679 assert(!(32 in r)); 11680 auto r1 = sort!"a > b"(a); 11681 assert(3 in r1); 11682 assert(!r1.contains(32)); 11683 assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]); 11684 } 11685 11686 /** 11687 `SortedRange` could accept ranges weaker than random-access, but it 11688 is unable to provide interesting functionality for them. Therefore, 11689 `SortedRange` is currently restricted to random-access ranges. 11690 11691 No copy of the original range is ever made. If the underlying range is 11692 changed concurrently with its corresponding `SortedRange` in ways 11693 that break its sorted-ness, `SortedRange` will work erratically. 11694 */ 11695 @safe unittest 11696 { 11697 import std.algorithm.mutation : swap; 11698 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11699 auto r = assumeSorted(a); 11700 assert(r.contains(42)); 11701 swap(a[3], a[5]); // illegal to break sortedness of original range 11702 assert(!r.contains(42)); // passes although it shouldn't 11703 } 11704 11705 /** 11706 `SortedRange` can be searched with predicates that do not take 11707 two elements of the underlying range as arguments. 11708 11709 This is useful, if a range of structs is sorted by a member and you 11710 want to search in that range by only providing a value for that member. 11711 11712 */ 11713 @safe unittest 11714 { 11715 import std.algorithm.comparison : equal; 11716 static struct S { int i; } 11717 static bool byI(A, B)(A a, B b) 11718 { 11719 static if (is(A == S)) 11720 return a.i < b; 11721 else 11722 return a < b.i; 11723 } 11724 auto r = assumeSorted!byI([S(1), S(2), S(3)]); 11725 auto lessThanTwo = r.lowerBound(2); 11726 assert(equal(lessThanTwo, [S(1)])); 11727 } 11728 11729 @safe unittest 11730 { 11731 import std.exception : assertThrown, assertNotThrown; 11732 11733 assertNotThrown(SortedRange!(int[])([ 1, 3, 10, 5, 7 ])); 11734 assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 10, 5, 7 ])); 11735 11736 // these two checks are implementation depended 11737 assertNotThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 12, 2 ])); 11738 assertThrown(SortedRange!(int[],"a < b", SortedRangeOptions.checkRoughly)([ 1, 3, 10, 5, 2, 12 ])); 11739 } 11740 11741 @safe unittest 11742 { 11743 import std.algorithm.comparison : equal; 11744 11745 auto a = [ 10, 20, 30, 30, 30, 40, 40, 50, 60 ]; 11746 auto r = assumeSorted(a).trisect(30); 11747 assert(equal(r[0], [ 10, 20 ])); 11748 assert(equal(r[1], [ 30, 30, 30 ])); 11749 assert(equal(r[2], [ 40, 40, 50, 60 ])); 11750 11751 r = assumeSorted(a).trisect(35); 11752 assert(equal(r[0], [ 10, 20, 30, 30, 30 ])); 11753 assert(r[1].empty); 11754 assert(equal(r[2], [ 40, 40, 50, 60 ])); 11755 } 11756 11757 @safe unittest 11758 { 11759 import std.algorithm.comparison : equal; 11760 auto a = [ "A", "AG", "B", "E", "F" ]; 11761 auto r = assumeSorted!"cmp(a,b) < 0"(a).trisect("B"w); 11762 assert(equal(r[0], [ "A", "AG" ])); 11763 assert(equal(r[1], [ "B" ])); 11764 assert(equal(r[2], [ "E", "F" ])); 11765 r = assumeSorted!"cmp(a,b) < 0"(a).trisect("A"d); 11766 assert(r[0].empty); 11767 assert(equal(r[1], [ "A" ])); 11768 assert(equal(r[2], [ "AG", "B", "E", "F" ])); 11769 } 11770 11771 @safe unittest 11772 { 11773 import std.algorithm.comparison : equal; 11774 static void test(SearchPolicy pol)() 11775 { 11776 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11777 auto r = assumeSorted(a); 11778 assert(equal(r.lowerBound(42), [1, 2, 3])); 11779 11780 assert(equal(r.lowerBound!(pol)(42), [1, 2, 3])); 11781 assert(equal(r.lowerBound!(pol)(41), [1, 2, 3])); 11782 assert(equal(r.lowerBound!(pol)(43), [1, 2, 3, 42])); 11783 assert(equal(r.lowerBound!(pol)(51), [1, 2, 3, 42])); 11784 assert(equal(r.lowerBound!(pol)(3), [1, 2])); 11785 assert(equal(r.lowerBound!(pol)(55), [1, 2, 3, 42, 52])); 11786 assert(equal(r.lowerBound!(pol)(420), a)); 11787 assert(equal(r.lowerBound!(pol)(0), a[0 .. 0])); 11788 11789 assert(equal(r.upperBound!(pol)(42), [52, 64])); 11790 assert(equal(r.upperBound!(pol)(41), [42, 52, 64])); 11791 assert(equal(r.upperBound!(pol)(43), [52, 64])); 11792 assert(equal(r.upperBound!(pol)(51), [52, 64])); 11793 assert(equal(r.upperBound!(pol)(53), [64])); 11794 assert(equal(r.upperBound!(pol)(55), [64])); 11795 assert(equal(r.upperBound!(pol)(420), a[0 .. 0])); 11796 assert(equal(r.upperBound!(pol)(0), a)); 11797 } 11798 11799 test!(SearchPolicy.trot)(); 11800 test!(SearchPolicy.gallop)(); 11801 test!(SearchPolicy.trotBackwards)(); 11802 test!(SearchPolicy.gallopBackwards)(); 11803 test!(SearchPolicy.binarySearch)(); 11804 } 11805 11806 @safe unittest 11807 { 11808 // Check for small arrays 11809 int[] a; 11810 auto r = assumeSorted(a); 11811 a = [ 1 ]; 11812 r = assumeSorted(a); 11813 a = [ 1, 2 ]; 11814 r = assumeSorted(a); 11815 a = [ 1, 2, 3 ]; 11816 r = assumeSorted(a); 11817 } 11818 11819 @safe unittest 11820 { 11821 import std.algorithm.mutation : swap; 11822 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11823 auto r = assumeSorted(a); 11824 assert(r.contains(42)); 11825 swap(a[3], a[5]); // illegal to break sortedness of original range 11826 assert(!r.contains(42)); // passes although it shouldn't 11827 } 11828 11829 @betterC @nogc nothrow @safe unittest 11830 { 11831 static immutable(int)[] arr = [ 1, 2, 3 ]; 11832 auto s = assumeSorted(arr); 11833 } 11834 11835 @system unittest 11836 { 11837 import std.algorithm.comparison : equal; 11838 int[] arr = [100, 101, 102, 200, 201, 300]; 11839 auto s = assumeSorted!((a, b) => a / 100 < b / 100)(arr); 11840 assert(s.groupBy.equal!equal([[100, 101, 102], [200, 201], [300]])); 11841 } 11842 11843 // Test on an input range 11844 @system unittest 11845 { 11846 import std.conv : text; 11847 import std.file : exists, remove, tempDir; 11848 import std.path : buildPath; 11849 import std.stdio : File; 11850 import std.uuid : randomUUID; 11851 auto name = buildPath(tempDir(), "test.std.range.line-" ~ text(__LINE__) ~ 11852 "." ~ randomUUID().toString()); 11853 auto f = File(name, "w"); 11854 scope(exit) if (exists(name)) remove(name); 11855 // write a sorted range of lines to the file 11856 f.write("abc\ndef\nghi\njkl"); 11857 f.close(); 11858 f.open(name, "r"); 11859 auto r = assumeSorted(f.byLine()); 11860 auto r1 = r.upperBound!(SearchPolicy.linear)("def"); 11861 assert(r1.front == "ghi", r1.front); 11862 f.close(); 11863 } 11864 11865 // https://issues.dlang.org/show_bug.cgi?id=19337 11866 @safe unittest 11867 { 11868 import std.algorithm.sorting : sort; 11869 auto a = [ 1, 2, 3, 42, 52, 64 ]; 11870 a.sort.sort!"a > b"; 11871 } 11872 11873 /** 11874 Assumes `r` is sorted by predicate `pred` and returns the 11875 corresponding $(D SortedRange!(pred, R)) having `r` as support. 11876 To check for sorted-ness at 11877 cost $(BIGOH n), use $(REF isSorted, std,algorithm,sorting). 11878 */ 11879 auto assumeSorted(alias pred = "a < b", R)(R r) 11880 if (isInputRange!(Unqual!R)) 11881 { 11882 // Avoid senseless `SortedRange!(SortedRange!(...), pred)` nesting. 11883 static if (is(R == SortedRange!(RRange, RPred), RRange, alias RPred)) 11884 { 11885 static if (isInputRange!R && __traits(isSame, pred, RPred)) 11886 // If the predicate is the same and we don't need to cast away 11887 // constness for the result to be an input range. 11888 return r; 11889 else 11890 return SortedRange!(Unqual!(typeof(r._input)), pred)(r._input); 11891 } 11892 else 11893 { 11894 return SortedRange!(Unqual!R, pred)(r); 11895 } 11896 } 11897 11898 /// 11899 @safe unittest 11900 { 11901 import std.algorithm.comparison : equal; 11902 11903 int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 11904 auto p = assumeSorted(a); 11905 11906 assert(equal(p.lowerBound(4), [0, 1, 2, 3])); 11907 assert(equal(p.lowerBound(5), [0, 1, 2, 3, 4])); 11908 assert(equal(p.lowerBound(6), [0, 1, 2, 3, 4, 5])); 11909 assert(equal(p.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6])); 11910 } 11911 11912 @safe unittest 11913 { 11914 import std.algorithm.comparison : equal; 11915 static assert(isRandomAccessRange!(SortedRange!(int[]))); 11916 int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11917 auto p = assumeSorted(a).upperBound(3); 11918 assert(equal(p, [4, 4, 5, 6 ])); 11919 p = assumeSorted(a).upperBound(4.2); 11920 assert(equal(p, [ 5, 6 ])); 11921 11922 // https://issues.dlang.org/show_bug.cgi?id=18933 11923 // don't create senselessly nested SortedRange types. 11924 assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted(a))))); 11925 assert(is(typeof(assumeSorted(a)) == typeof(assumeSorted(assumeSorted!"a > b"(a))))); 11926 } 11927 11928 @safe unittest 11929 { 11930 import std.algorithm.comparison : equal; 11931 import std.conv : text; 11932 11933 int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11934 auto p = assumeSorted(a).equalRange(3); 11935 assert(equal(p, [ 3, 3, 3 ]), text(p)); 11936 p = assumeSorted(a).equalRange(4); 11937 assert(equal(p, [ 4, 4 ]), text(p)); 11938 p = assumeSorted(a).equalRange(2); 11939 assert(equal(p, [ 2 ])); 11940 p = assumeSorted(a).equalRange(0); 11941 assert(p.empty); 11942 p = assumeSorted(a).equalRange(7); 11943 assert(p.empty); 11944 p = assumeSorted(a).equalRange(3.0); 11945 assert(equal(p, [ 3, 3, 3])); 11946 } 11947 11948 @safe unittest 11949 { 11950 int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]; 11951 if (a.length) 11952 { 11953 auto b = a[a.length / 2]; 11954 //auto r = sort(a); 11955 //assert(r.contains(b)); 11956 } 11957 } 11958 11959 @safe unittest 11960 { 11961 auto a = [ 5, 7, 34, 345, 677 ]; 11962 auto r = assumeSorted(a); 11963 a = null; 11964 r = assumeSorted(a); 11965 a = [ 1 ]; 11966 r = assumeSorted(a); 11967 } 11968 11969 // https://issues.dlang.org/show_bug.cgi?id=15003 11970 @nogc @safe unittest 11971 { 11972 static immutable a = [1, 2, 3, 4]; 11973 auto r = a.assumeSorted; 11974 } 11975 11976 /++ 11977 Wrapper which effectively makes it possible to pass a range by reference. 11978 Both the original range and the RefRange will always have the exact same 11979 elements. Any operation done on one will affect the other. So, for instance, 11980 if it's passed to a function which would implicitly copy the original range 11981 if it were passed to it, the original range is $(I not) copied but is 11982 consumed as if it were a reference type. 11983 11984 Note: 11985 `save` works as normal and operates on a new range, so if 11986 `save` is ever called on the `RefRange`, then no operations on the 11987 saved range will affect the original. 11988 11989 Params: 11990 range = the range to construct the `RefRange` from 11991 11992 Returns: 11993 A `RefRange`. If the given range is a class type 11994 (and thus is already a reference type), then the original 11995 range is returned rather than a `RefRange`. 11996 +/ 11997 struct RefRange(R) 11998 if (isInputRange!R) 11999 { 12000 public: 12001 12002 /++ +/ 12003 this(R* range) @safe pure nothrow 12004 { 12005 _range = range; 12006 } 12007 12008 12009 /++ 12010 This does not assign the pointer of `rhs` to this `RefRange`. 12011 Rather it assigns the range pointed to by `rhs` to the range pointed 12012 to by this `RefRange`. This is because $(I any) operation on a 12013 `RefRange` is the same is if it occurred to the original range. The 12014 one exception is when a `RefRange` is assigned `null` either 12015 directly or because `rhs` is `null`. In that case, `RefRange` 12016 no longer refers to the original range but is `null`. 12017 +/ 12018 auto opAssign(RefRange rhs) 12019 { 12020 if (_range && rhs._range) 12021 *_range = *rhs._range; 12022 else 12023 _range = rhs._range; 12024 12025 return this; 12026 } 12027 12028 /++ +/ 12029 void opAssign(typeof(null) rhs) 12030 { 12031 _range = null; 12032 } 12033 12034 12035 /++ 12036 A pointer to the wrapped range. 12037 +/ 12038 @property inout(R*) ptr() @safe inout pure nothrow 12039 { 12040 return _range; 12041 } 12042 12043 12044 version (StdDdoc) 12045 { 12046 /++ +/ 12047 @property auto front() {assert(0);} 12048 /++ Ditto +/ 12049 @property auto front() const {assert(0);} 12050 /++ Ditto +/ 12051 @property auto front(ElementType!R value) {assert(0);} 12052 } 12053 else 12054 { 12055 @property auto front() 12056 { 12057 return (*_range).front; 12058 } 12059 12060 static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const 12061 { 12062 return (*_range).front; 12063 } 12064 12065 static if (is(typeof((*_range).front = (*_range).front))) @property auto front(ElementType!R value) 12066 { 12067 return (*_range).front = value; 12068 } 12069 } 12070 12071 12072 version (StdDdoc) 12073 { 12074 @property bool empty(); /// 12075 @property bool empty() const; ///Ditto 12076 } 12077 else static if (isInfinite!R) 12078 enum empty = false; 12079 else 12080 { 12081 @property bool empty() 12082 { 12083 return (*_range).empty; 12084 } 12085 12086 static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const 12087 { 12088 return (*_range).empty; 12089 } 12090 } 12091 12092 12093 /++ +/ 12094 void popFront() 12095 { 12096 return (*_range).popFront(); 12097 } 12098 12099 12100 version (StdDdoc) 12101 { 12102 /++ 12103 Only defined if `isForwardRange!R` is `true`. 12104 +/ 12105 @property auto save() {assert(0);} 12106 /++ Ditto +/ 12107 @property auto save() const {assert(0);} 12108 /++ Ditto +/ 12109 auto opSlice() {assert(0);} 12110 /++ Ditto +/ 12111 auto opSlice() const {assert(0);} 12112 } 12113 else static if (isForwardRange!R) 12114 { 12115 import std.traits : isSafe; 12116 private alias S = typeof((*_range).save); 12117 12118 static if (is(typeof((*cast(const R*)_range).save))) 12119 private alias CS = typeof((*cast(const R*)_range).save); 12120 12121 static if (isSafe!((R* r) => (*r).save)) 12122 { 12123 @property RefRange!S save() @trusted 12124 { 12125 mixin(_genSave()); 12126 } 12127 12128 static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const 12129 { 12130 mixin(_genSave()); 12131 } 12132 } 12133 else 12134 { 12135 @property RefRange!S save() 12136 { 12137 mixin(_genSave()); 12138 } 12139 12140 static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const 12141 { 12142 mixin(_genSave()); 12143 } 12144 } 12145 12146 auto opSlice()() 12147 { 12148 return save; 12149 } 12150 12151 auto opSlice()() const 12152 { 12153 return save; 12154 } 12155 12156 private static string _genSave() @safe pure nothrow 12157 { 12158 return `import core.lifetime : emplace;` ~ 12159 `alias S = typeof((*_range).save);` ~ 12160 `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~ 12161 `auto mem = new void[S.sizeof];` ~ 12162 `emplace!S(mem, cast(S)(*_range).save);` ~ 12163 `return RefRange!S(cast(S*) mem.ptr);`; 12164 } 12165 12166 static assert(isForwardRange!RefRange); 12167 } 12168 12169 12170 version (StdDdoc) 12171 { 12172 /++ 12173 Only defined if `isBidirectionalRange!R` is `true`. 12174 +/ 12175 @property auto back() {assert(0);} 12176 /++ Ditto +/ 12177 @property auto back() const {assert(0);} 12178 /++ Ditto +/ 12179 @property auto back(ElementType!R value) {assert(0);} 12180 } 12181 else static if (isBidirectionalRange!R) 12182 { 12183 @property auto back() 12184 { 12185 return (*_range).back; 12186 } 12187 12188 static if (is(typeof((*(cast(const R*)_range)).back))) @property auto back() const 12189 { 12190 return (*_range).back; 12191 } 12192 12193 static if (is(typeof((*_range).back = (*_range).back))) @property auto back(ElementType!R value) 12194 { 12195 return (*_range).back = value; 12196 } 12197 } 12198 12199 12200 /++ Ditto +/ 12201 static if (isBidirectionalRange!R) void popBack() 12202 { 12203 return (*_range).popBack(); 12204 } 12205 12206 12207 version (StdDdoc) 12208 { 12209 /++ 12210 Only defined if `isRandomAccessRange!R` is `true`. 12211 +/ 12212 auto ref opIndex(IndexType)(IndexType index) {assert(0);} 12213 12214 /++ Ditto +/ 12215 auto ref opIndex(IndexType)(IndexType index) const {assert(0);} 12216 } 12217 else static if (isRandomAccessRange!R) 12218 { 12219 auto ref opIndex(IndexType)(IndexType index) 12220 if (is(typeof((*_range)[index]))) 12221 { 12222 return (*_range)[index]; 12223 } 12224 12225 auto ref opIndex(IndexType)(IndexType index) const 12226 if (is(typeof((*cast(const R*)_range)[index]))) 12227 { 12228 return (*_range)[index]; 12229 } 12230 } 12231 12232 12233 /++ 12234 Only defined if `hasMobileElements!R` and `isForwardRange!R` are 12235 `true`. 12236 +/ 12237 static if (hasMobileElements!R && isForwardRange!R) auto moveFront() 12238 { 12239 return (*_range).moveFront(); 12240 } 12241 12242 12243 /++ 12244 Only defined if `hasMobileElements!R` and `isBidirectionalRange!R` 12245 are `true`. 12246 +/ 12247 static if (hasMobileElements!R && isBidirectionalRange!R) auto moveBack() 12248 { 12249 return (*_range).moveBack(); 12250 } 12251 12252 12253 /++ 12254 Only defined if `hasMobileElements!R` and `isRandomAccessRange!R` 12255 are `true`. 12256 +/ 12257 static if (hasMobileElements!R && isRandomAccessRange!R) auto moveAt(size_t index) 12258 { 12259 return (*_range).moveAt(index); 12260 } 12261 12262 12263 version (StdDdoc) 12264 { 12265 /// Only defined if `hasLength!R` is `true`. 12266 @property size_t length(); 12267 /// ditto 12268 @property size_t length() const; 12269 /// Ditto 12270 alias opDollar = length; 12271 } 12272 else static if (hasLength!R) 12273 { 12274 @property auto length() 12275 { 12276 return (*_range).length; 12277 } 12278 static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const 12279 { 12280 return (*_range).length; 12281 } 12282 alias opDollar = length; 12283 } 12284 12285 12286 version (StdDdoc) 12287 { 12288 /++ 12289 Only defined if `hasSlicing!R` is `true`. 12290 +/ 12291 auto opSlice(IndexType1, IndexType2) 12292 (IndexType1 begin, IndexType2 end) {assert(0);} 12293 12294 /++ Ditto +/ 12295 auto opSlice(IndexType1, IndexType2) 12296 (IndexType1 begin, IndexType2 end) const {assert(0);} 12297 } 12298 else static if (hasSlicing!R) 12299 { 12300 private alias T = typeof((*_range)[1 .. 2]); 12301 static if (is(typeof((*cast(const R*)_range)[1 .. 2]))) 12302 { 12303 private alias CT = typeof((*cast(const R*)_range)[1 .. 2]); 12304 } 12305 12306 RefRange!T opSlice(IndexType1, IndexType2) 12307 (IndexType1 begin, IndexType2 end) 12308 if (is(typeof((*_range)[begin .. end]))) 12309 { 12310 mixin(_genOpSlice()); 12311 } 12312 12313 RefRange!CT opSlice(IndexType1, IndexType2) 12314 (IndexType1 begin, IndexType2 end) const 12315 if (is(typeof((*cast(const R*)_range)[begin .. end]))) 12316 { 12317 mixin(_genOpSlice()); 12318 } 12319 12320 private static string _genOpSlice() @safe pure nothrow 12321 { 12322 return `import core.lifetime : emplace;` ~ 12323 `alias S = typeof((*_range)[begin .. end]);` ~ 12324 `static assert(hasSlicing!S, S.stringof ~ " is not sliceable.");` ~ 12325 `auto mem = new void[S.sizeof];` ~ 12326 `emplace!S(mem, cast(S)(*_range)[begin .. end]);` ~ 12327 `return RefRange!S(cast(S*) mem.ptr);`; 12328 } 12329 } 12330 12331 12332 private: 12333 12334 R* _range; 12335 } 12336 12337 /// Basic Example 12338 @system unittest 12339 { 12340 import std.algorithm.searching : find; 12341 ubyte[] buffer = [1, 9, 45, 12, 22]; 12342 auto found1 = find(buffer, 45); 12343 assert(found1 == [45, 12, 22]); 12344 assert(buffer == [1, 9, 45, 12, 22]); 12345 12346 auto wrapped1 = refRange(&buffer); 12347 auto found2 = find(wrapped1, 45); 12348 assert(*found2.ptr == [45, 12, 22]); 12349 assert(buffer == [45, 12, 22]); 12350 12351 auto found3 = find(wrapped1.save, 22); 12352 assert(*found3.ptr == [22]); 12353 assert(buffer == [45, 12, 22]); 12354 12355 string str = "hello world"; 12356 auto wrappedStr = refRange(&str); 12357 assert(str.front == 'h'); 12358 str.popFrontN(5); 12359 assert(str == " world"); 12360 assert(wrappedStr.front == ' '); 12361 assert(*wrappedStr.ptr == " world"); 12362 } 12363 12364 /// opAssign Example. 12365 @system unittest 12366 { 12367 ubyte[] buffer1 = [1, 2, 3, 4, 5]; 12368 ubyte[] buffer2 = [6, 7, 8, 9, 10]; 12369 auto wrapped1 = refRange(&buffer1); 12370 auto wrapped2 = refRange(&buffer2); 12371 assert(wrapped1.ptr is &buffer1); 12372 assert(wrapped2.ptr is &buffer2); 12373 assert(wrapped1.ptr !is wrapped2.ptr); 12374 assert(buffer1 != buffer2); 12375 12376 wrapped1 = wrapped2; 12377 12378 //Everything points to the same stuff as before. 12379 assert(wrapped1.ptr is &buffer1); 12380 assert(wrapped2.ptr is &buffer2); 12381 assert(wrapped1.ptr !is wrapped2.ptr); 12382 12383 //But buffer1 has changed due to the assignment. 12384 assert(buffer1 == [6, 7, 8, 9, 10]); 12385 assert(buffer2 == [6, 7, 8, 9, 10]); 12386 12387 buffer2 = [11, 12, 13, 14, 15]; 12388 12389 //Everything points to the same stuff as before. 12390 assert(wrapped1.ptr is &buffer1); 12391 assert(wrapped2.ptr is &buffer2); 12392 assert(wrapped1.ptr !is wrapped2.ptr); 12393 12394 //But buffer2 has changed due to the assignment. 12395 assert(buffer1 == [6, 7, 8, 9, 10]); 12396 assert(buffer2 == [11, 12, 13, 14, 15]); 12397 12398 wrapped2 = null; 12399 12400 //The pointer changed for wrapped2 but not wrapped1. 12401 assert(wrapped1.ptr is &buffer1); 12402 assert(wrapped2.ptr is null); 12403 assert(wrapped1.ptr !is wrapped2.ptr); 12404 12405 //buffer2 is not affected by the assignment. 12406 assert(buffer1 == [6, 7, 8, 9, 10]); 12407 assert(buffer2 == [11, 12, 13, 14, 15]); 12408 } 12409 12410 @system unittest 12411 { 12412 import std.algorithm.iteration : filter; 12413 { 12414 ubyte[] buffer = [1, 2, 3, 4, 5]; 12415 auto wrapper = refRange(&buffer); 12416 auto p = wrapper.ptr; 12417 auto f = wrapper.front; 12418 wrapper.front = f; 12419 auto e = wrapper.empty; 12420 wrapper.popFront(); 12421 auto s = wrapper.save; 12422 auto b = wrapper.back; 12423 wrapper.back = b; 12424 wrapper.popBack(); 12425 auto i = wrapper[0]; 12426 wrapper.moveFront(); 12427 wrapper.moveBack(); 12428 wrapper.moveAt(0); 12429 auto l = wrapper.length; 12430 auto sl = wrapper[0 .. 1]; 12431 assert(wrapper[0 .. $].length == buffer[0 .. $].length); 12432 } 12433 12434 { 12435 ubyte[] buffer = [1, 2, 3, 4, 5]; 12436 const wrapper = refRange(&buffer); 12437 const p = wrapper.ptr; 12438 const f = wrapper.front; 12439 const e = wrapper.empty; 12440 const s = wrapper.save; 12441 const b = wrapper.back; 12442 const i = wrapper[0]; 12443 const l = wrapper.length; 12444 const sl = wrapper[0 .. 1]; 12445 } 12446 12447 { 12448 ubyte[] buffer = [1, 2, 3, 4, 5]; 12449 auto filtered = filter!"true"(buffer); 12450 auto wrapper = refRange(&filtered); 12451 auto p = wrapper.ptr; 12452 auto f = wrapper.front; 12453 wrapper.front = f; 12454 auto e = wrapper.empty; 12455 wrapper.popFront(); 12456 auto s = wrapper.save; 12457 wrapper.moveFront(); 12458 } 12459 12460 { 12461 ubyte[] buffer = [1, 2, 3, 4, 5]; 12462 auto filtered = filter!"true"(buffer); 12463 const wrapper = refRange(&filtered); 12464 const p = wrapper.ptr; 12465 12466 //Cannot currently be const. filter needs to be updated to handle const. 12467 /+ 12468 const f = wrapper.front; 12469 const e = wrapper.empty; 12470 const s = wrapper.save; 12471 +/ 12472 } 12473 12474 { 12475 string str = "hello world"; 12476 auto wrapper = refRange(&str); 12477 auto p = wrapper.ptr; 12478 auto f = wrapper.front; 12479 auto e = wrapper.empty; 12480 wrapper.popFront(); 12481 auto s = wrapper.save; 12482 auto b = wrapper.back; 12483 wrapper.popBack(); 12484 } 12485 12486 { 12487 // https://issues.dlang.org/show_bug.cgi?id=16534 12488 // opDollar should be defined if the wrapped range defines length. 12489 auto range = 10.iota.takeExactly(5); 12490 auto wrapper = refRange(&range); 12491 assert(wrapper.length == 5); 12492 assert(wrapper[0 .. $ - 1].length == 4); 12493 } 12494 } 12495 12496 //Test assignment. 12497 @system unittest 12498 { 12499 ubyte[] buffer1 = [1, 2, 3, 4, 5]; 12500 ubyte[] buffer2 = [6, 7, 8, 9, 10]; 12501 RefRange!(ubyte[]) wrapper1; 12502 RefRange!(ubyte[]) wrapper2 = refRange(&buffer2); 12503 assert(wrapper1.ptr is null); 12504 assert(wrapper2.ptr is &buffer2); 12505 12506 wrapper1 = refRange(&buffer1); 12507 assert(wrapper1.ptr is &buffer1); 12508 12509 wrapper1 = wrapper2; 12510 assert(wrapper1.ptr is &buffer1); 12511 assert(buffer1 == buffer2); 12512 12513 wrapper1 = RefRange!(ubyte[]).init; 12514 assert(wrapper1.ptr is null); 12515 assert(wrapper2.ptr is &buffer2); 12516 assert(buffer1 == buffer2); 12517 assert(buffer1 == [6, 7, 8, 9, 10]); 12518 12519 wrapper2 = null; 12520 assert(wrapper2.ptr is null); 12521 assert(buffer2 == [6, 7, 8, 9, 10]); 12522 } 12523 12524 @system unittest 12525 { 12526 import std.algorithm.comparison : equal; 12527 import std.algorithm.mutation : bringToFront; 12528 import std.algorithm.searching : commonPrefix, find, until; 12529 import std.algorithm.sorting : sort; 12530 12531 //Test that ranges are properly consumed. 12532 { 12533 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12534 auto wrapper = refRange(&arr); 12535 12536 assert(*find(wrapper, 41).ptr == [41, 3, 40, 4, 42, 9]); 12537 assert(arr == [41, 3, 40, 4, 42, 9]); 12538 12539 assert(*drop(wrapper, 2).ptr == [40, 4, 42, 9]); 12540 assert(arr == [40, 4, 42, 9]); 12541 12542 assert(equal(until(wrapper, 42), [40, 4])); 12543 assert(arr == [42, 9]); 12544 12545 assert(find(wrapper, 12).empty); 12546 assert(arr.empty); 12547 } 12548 12549 { 12550 string str = "Hello, world-like object."; 12551 auto wrapper = refRange(&str); 12552 12553 assert(*find(wrapper, "l").ptr == "llo, world-like object."); 12554 assert(str == "llo, world-like object."); 12555 12556 assert(equal(take(wrapper, 5), "llo, ")); 12557 assert(str == "world-like object."); 12558 } 12559 12560 //Test that operating on saved ranges does not consume the original. 12561 { 12562 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12563 auto wrapper = refRange(&arr); 12564 auto saved = wrapper.save; 12565 saved.popFrontN(3); 12566 assert(*saved.ptr == [41, 3, 40, 4, 42, 9]); 12567 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12568 } 12569 12570 { 12571 string str = "Hello, world-like object."; 12572 auto wrapper = refRange(&str); 12573 auto saved = wrapper.save; 12574 saved.popFrontN(13); 12575 assert(*saved.ptr == "like object."); 12576 assert(str == "Hello, world-like object."); 12577 } 12578 12579 //Test that functions which use save work properly. 12580 { 12581 int[] arr = [1, 42]; 12582 auto wrapper = refRange(&arr); 12583 assert(equal(commonPrefix(wrapper, [1, 27]), [1])); 12584 } 12585 12586 { 12587 int[] arr = [4, 5, 6, 7, 1, 2, 3]; 12588 auto wrapper = refRange(&arr); 12589 assert(bringToFront(wrapper[0 .. 4], wrapper[4 .. arr.length]) == 3); 12590 assert(arr == [1, 2, 3, 4, 5, 6, 7]); 12591 } 12592 12593 //Test bidirectional functions. 12594 { 12595 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12596 auto wrapper = refRange(&arr); 12597 12598 assert(wrapper.back == 9); 12599 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12600 12601 wrapper.popBack(); 12602 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42]); 12603 } 12604 12605 { 12606 string str = "Hello, world-like object."; 12607 auto wrapper = refRange(&str); 12608 12609 assert(wrapper.back == '.'); 12610 assert(str == "Hello, world-like object."); 12611 12612 wrapper.popBack(); 12613 assert(str == "Hello, world-like object"); 12614 } 12615 12616 //Test random access functions. 12617 { 12618 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12619 auto wrapper = refRange(&arr); 12620 12621 assert(wrapper[2] == 2); 12622 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12623 12624 assert(*wrapper[3 .. 6].ptr != null, [41, 3, 40]); 12625 assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]); 12626 } 12627 12628 //Test move functions. 12629 { 12630 int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9]; 12631 auto wrapper = refRange(&arr); 12632 12633 auto t1 = wrapper.moveFront(); 12634 auto t2 = wrapper.moveBack(); 12635 wrapper.front = t2; 12636 wrapper.back = t1; 12637 assert(arr == [9, 42, 2, 41, 3, 40, 4, 42, 1]); 12638 12639 sort(wrapper.save); 12640 assert(arr == [1, 2, 3, 4, 9, 40, 41, 42, 42]); 12641 } 12642 } 12643 12644 @system unittest 12645 { 12646 struct S 12647 { 12648 @property int front() @safe const pure nothrow { return 0; } 12649 enum bool empty = false; 12650 void popFront() @safe pure nothrow { } 12651 @property auto save() @safe pure nothrow return scope { return this; } 12652 } 12653 12654 S s; 12655 auto wrapper = refRange(&s); 12656 static assert(isInfinite!(typeof(wrapper))); 12657 } 12658 12659 @system unittest 12660 { 12661 class C 12662 { 12663 @property int front() @safe const pure nothrow { return 0; } 12664 @property bool empty() @safe const pure nothrow { return false; } 12665 void popFront() @safe pure nothrow { } 12666 @property auto save() @safe pure nothrow return scope { return this; } 12667 } 12668 static assert(isForwardRange!C); 12669 12670 auto c = new C; 12671 auto cWrapper = refRange(&c); 12672 static assert(is(typeof(cWrapper) == C)); 12673 assert(cWrapper is c); 12674 } 12675 12676 // https://issues.dlang.org/show_bug.cgi?id=14373 12677 @system unittest 12678 { 12679 static struct R 12680 { 12681 @property int front() {return 0;} 12682 void popFront() {empty = true;} 12683 bool empty = false; 12684 } 12685 R r; 12686 refRange(&r).popFront(); 12687 assert(r.empty); 12688 } 12689 12690 // https://issues.dlang.org/show_bug.cgi?id=14575 12691 @system unittest 12692 { 12693 struct R 12694 { 12695 Object front; 12696 alias back = front; 12697 bool empty = false; 12698 void popFront() {empty = true;} 12699 alias popBack = popFront; 12700 @property R save() {return this;} 12701 } 12702 static assert(isBidirectionalRange!R); 12703 R r; 12704 auto rr = refRange(&r); 12705 12706 struct R2 12707 { 12708 @property Object front() {return null;} 12709 @property const(Object) front() const {return null;} 12710 alias back = front; 12711 bool empty = false; 12712 void popFront() {empty = true;} 12713 alias popBack = popFront; 12714 @property R2 save() {return this;} 12715 } 12716 static assert(isBidirectionalRange!R2); 12717 R2 r2; 12718 auto rr2 = refRange(&r2); 12719 } 12720 12721 /// ditto 12722 auto refRange(R)(R* range) 12723 if (isInputRange!R) 12724 { 12725 static if (!is(R == class)) 12726 return RefRange!R(range); 12727 else 12728 return *range; 12729 } 12730 12731 // https://issues.dlang.org/show_bug.cgi?id=9060 12732 @safe unittest 12733 { 12734 import std.algorithm.iteration : map, joiner, group; 12735 import std.algorithm.searching : until; 12736 // fix for std.algorithm 12737 auto r = map!(x => 0)([1]); 12738 chain(r, r); 12739 zip(r, r); 12740 roundRobin(r, r); 12741 12742 struct NRAR { 12743 typeof(r) input; 12744 @property empty() { return input.empty; } 12745 @property front() { return input.front; } 12746 void popFront() { input.popFront(); } 12747 @property save() { return NRAR(input.save); } 12748 } 12749 auto n1 = NRAR(r); 12750 cycle(n1); // non random access range version 12751 12752 assumeSorted(r); 12753 12754 // fix for std.range 12755 joiner([r], [9]); 12756 12757 struct NRAR2 { 12758 NRAR input; 12759 @property empty() { return true; } 12760 @property front() { return input; } 12761 void popFront() { } 12762 @property save() { return NRAR2(input.save); } 12763 } 12764 auto n2 = NRAR2(n1); 12765 joiner(n2); 12766 12767 group(r); 12768 12769 until(r, 7); 12770 static void foo(R)(R r) { until!(x => x > 7)(r); } 12771 foo(r); 12772 } 12773 12774 private struct Bitwise(R) 12775 if (isInputRange!R && isIntegral!(ElementType!R)) 12776 { 12777 import std.traits : Unsigned; 12778 private: 12779 alias ElemType = ElementType!R; 12780 alias UnsignedElemType = Unsigned!ElemType; 12781 12782 R parent; 12783 enum bitsNum = ElemType.sizeof * 8; 12784 size_t maskPos = 1; 12785 12786 static if (isBidirectionalRange!R) 12787 { 12788 size_t backMaskPos = bitsNum; 12789 } 12790 12791 public: 12792 this()(auto ref R range) 12793 { 12794 parent = range; 12795 } 12796 12797 static if (isInfinite!R) 12798 { 12799 enum empty = false; 12800 } 12801 else 12802 { 12803 /** 12804 * Check if the range is empty 12805 * 12806 * Returns: a boolean true or false 12807 */ 12808 bool empty() 12809 { 12810 static if (hasLength!R) 12811 { 12812 return length == 0; 12813 } 12814 else static if (isBidirectionalRange!R) 12815 { 12816 if (parent.empty) 12817 { 12818 return true; 12819 } 12820 else 12821 { 12822 /* 12823 If we have consumed the last element of the range both from 12824 the front and the back, then the masks positions will overlap 12825 */ 12826 return parent.save.dropOne.empty && (maskPos > backMaskPos); 12827 } 12828 } 12829 else 12830 { 12831 /* 12832 If we consumed the last element of the range, but not all the 12833 bits in the last element 12834 */ 12835 return parent.empty; 12836 } 12837 } 12838 } 12839 12840 bool front() 12841 { 12842 assert(!empty); 12843 return (parent.front & mask(maskPos)) != 0; 12844 } 12845 12846 void popFront() 12847 { 12848 assert(!empty); 12849 ++maskPos; 12850 if (maskPos > bitsNum) 12851 { 12852 parent.popFront; 12853 maskPos = 1; 12854 } 12855 } 12856 12857 static if (hasLength!R) 12858 { 12859 size_t length() 12860 { 12861 auto len = parent.length * bitsNum - (maskPos - 1); 12862 static if (isBidirectionalRange!R) 12863 { 12864 len -= bitsNum - backMaskPos; 12865 } 12866 return len; 12867 } 12868 12869 alias opDollar = length; 12870 } 12871 12872 static if (isForwardRange!R) 12873 { 12874 typeof(this) save() 12875 { 12876 auto result = this; 12877 result.parent = parent.save; 12878 return result; 12879 } 12880 } 12881 12882 static if (isBidirectionalRange!R) 12883 { 12884 bool back() 12885 { 12886 assert(!empty); 12887 return (parent.back & mask(backMaskPos)) != 0; 12888 } 12889 12890 void popBack() 12891 { 12892 assert(!empty); 12893 --backMaskPos; 12894 if (backMaskPos == 0) 12895 { 12896 parent.popBack; 12897 backMaskPos = bitsNum; 12898 } 12899 } 12900 } 12901 12902 static if (isRandomAccessRange!R) 12903 { 12904 /** 12905 Return the `n`th bit within the range 12906 */ 12907 bool opIndex(size_t n) 12908 in 12909 { 12910 /* 12911 If it does not have the length property, it means that R is 12912 an infinite range 12913 */ 12914 static if (hasLength!R) 12915 { 12916 assert(n < length, "Index out of bounds"); 12917 } 12918 } 12919 do 12920 { 12921 immutable size_t remainingBits = bitsNum - maskPos + 1; 12922 // If n >= maskPos, then the bit sign will be 1, otherwise 0 12923 immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12924 /* 12925 By truncating n with remainingBits bits we have skipped the 12926 remaining bits in parent[0], so we need to add 1 to elemIndex. 12927 12928 Because bitsNum is a power of 2, n / bitsNum == n >> bitsNum.bsf 12929 */ 12930 import core.bitop : bsf; 12931 immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); 12932 12933 /* 12934 Since the indexing is from LSB to MSB, we need to index at the 12935 remainder of (n - remainingBits). 12936 12937 Because bitsNum is a power of 2, n % bitsNum == n & (bitsNum - 1) 12938 */ 12939 immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) 12940 + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); 12941 12942 return (parent[elemIndex] & mask(elemMaskPos)) != 0; 12943 } 12944 12945 static if (hasAssignableElements!R) 12946 { 12947 /** 12948 Assigns `flag` to the `n`th bit within the range 12949 */ 12950 void opIndexAssign(bool flag, size_t n) 12951 in 12952 { 12953 static if (hasLength!R) 12954 { 12955 assert(n < length, "Index out of bounds"); 12956 } 12957 } 12958 do 12959 { 12960 import core.bitop : bsf; 12961 12962 immutable size_t remainingBits = bitsNum - maskPos + 1; 12963 immutable ptrdiff_t sign = (remainingBits - n - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12964 immutable size_t elemIndex = sign * (((n - remainingBits) >> bitsNum.bsf) + 1); 12965 immutable size_t elemMaskPos = (sign ^ 1) * (maskPos + n) 12966 + sign * (1 + ((n - remainingBits) & (bitsNum - 1))); 12967 12968 auto elem = parent[elemIndex]; 12969 auto elemMask = mask(elemMaskPos); 12970 parent[elemIndex] = cast(UnsignedElemType)(flag * (elem | elemMask) 12971 + (flag ^ 1) * (elem & ~elemMask)); 12972 } 12973 } 12974 12975 Bitwise!R opSlice() 12976 { 12977 return this.save; 12978 } 12979 12980 Bitwise!R opSlice(size_t start, size_t end) 12981 in 12982 { 12983 assert(start < end, "Invalid bounds: end <= start"); 12984 } 12985 do 12986 { 12987 import core.bitop : bsf; 12988 12989 size_t remainingBits = bitsNum - maskPos + 1; 12990 ptrdiff_t sign = (remainingBits - start - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12991 immutable size_t startElemIndex = sign * (((start - remainingBits) >> bitsNum.bsf) + 1); 12992 immutable size_t startElemMaskPos = (sign ^ 1) * (maskPos + start) 12993 + sign * (1 + ((start - remainingBits) & (bitsNum - 1))); 12994 12995 immutable size_t sliceLen = end - start - 1; 12996 remainingBits = bitsNum - startElemMaskPos + 1; 12997 sign = (remainingBits - sliceLen - 1) >> (ptrdiff_t.sizeof * 8 - 1); 12998 immutable size_t endElemIndex = startElemIndex 12999 + sign * (((sliceLen - remainingBits) >> bitsNum.bsf) + 1); 13000 immutable size_t endElemMaskPos = (sign ^ 1) * (startElemMaskPos + sliceLen) 13001 + sign * (1 + ((sliceLen - remainingBits) & (bitsNum - 1))); 13002 13003 typeof(return) result; 13004 // Get the slice to be returned from the parent 13005 result.parent = (parent[startElemIndex .. endElemIndex + 1]).save; 13006 result.maskPos = startElemMaskPos; 13007 static if (isBidirectionalRange!R) 13008 { 13009 result.backMaskPos = endElemMaskPos; 13010 } 13011 return result; 13012 } 13013 } 13014 13015 private: 13016 auto mask(size_t maskPos) 13017 { 13018 return (1UL << (maskPos - 1UL)); 13019 } 13020 } 13021 13022 /** 13023 Bitwise adapter over an integral type range. Consumes the range elements bit by 13024 bit, from the least significant bit to the most significant bit. 13025 13026 Params: 13027 R = an integral $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to iterate over 13028 range = range to consume bit by by 13029 13030 Returns: 13031 A `Bitwise` input range with propagated forward, bidirectional 13032 and random access capabilities 13033 */ 13034 auto bitwise(R)(auto ref R range) 13035 if (isInputRange!R && isIntegral!(ElementType!R)) 13036 { 13037 return Bitwise!R(range); 13038 } 13039 13040 /// 13041 @safe pure unittest 13042 { 13043 import std.algorithm.comparison : equal; 13044 import std.format : format; 13045 13046 // 00000011 00001001 13047 ubyte[] arr = [3, 9]; 13048 auto r = arr.bitwise; 13049 13050 // iterate through it as with any other range 13051 assert(format("%(%d%)", r) == "1100000010010000"); 13052 assert(format("%(%d%)", r.retro).equal("1100000010010000".retro)); 13053 13054 auto r2 = r[5 .. $]; 13055 // set a bit 13056 r[2] = 1; 13057 assert(arr[0] == 7); 13058 assert(r[5] == r2[0]); 13059 } 13060 13061 /// You can use bitwise to implement an uniform bool generator 13062 @safe unittest 13063 { 13064 import std.algorithm.comparison : equal; 13065 import std.random : rndGen; 13066 13067 auto rb = rndGen.bitwise; 13068 static assert(isInfinite!(typeof(rb))); 13069 13070 auto rb2 = rndGen.bitwise; 13071 // Don't forget that structs are passed by value 13072 assert(rb.take(10).equal(rb2.take(10))); 13073 } 13074 13075 // Test nogc inference 13076 @safe @nogc unittest 13077 { 13078 static ubyte[] arr = [3, 9]; 13079 auto bw = arr.bitwise; 13080 auto bw2 = bw[]; 13081 auto bw3 = bw[8 .. $]; 13082 bw3[2] = true; 13083 13084 assert(arr[1] == 13); 13085 assert(bw[$ - 6]); 13086 assert(bw[$ - 6] == bw2[$ - 6]); 13087 assert(bw[$ - 6] == bw3[$ - 6]); 13088 } 13089 13090 // Test all range types over all integral types 13091 @safe pure nothrow unittest 13092 { 13093 import std.meta : AliasSeq; 13094 import std.internal.test.dummyrange; 13095 13096 alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, 13097 long, ulong); 13098 foreach (IntegralType; IntegralTypes) 13099 { 13100 foreach (T; AllDummyRangesType!(IntegralType[])) 13101 { 13102 T a; 13103 auto bw = Bitwise!T(a); 13104 13105 static if (isForwardRange!T) 13106 { 13107 auto bwFwdSave = bw.save; 13108 } 13109 13110 static if (isBidirectionalRange!T) 13111 { 13112 auto bwBack = bw.save; 13113 auto bwBackSave = bw.save; 13114 } 13115 13116 static if (hasLength!T) 13117 { 13118 auto bwLength = bw.length; 13119 assert(bw.length == (IntegralType.sizeof * 8 * a.length)); 13120 static if (isForwardRange!T) 13121 { 13122 assert(bw.length == bwFwdSave.length); 13123 } 13124 } 13125 13126 // Make sure front and back are not the mechanisms that modify the range 13127 long numCalls = 42; 13128 bool initialFrontValue; 13129 13130 if (!bw.empty) 13131 { 13132 initialFrontValue = bw.front; 13133 } 13134 13135 while (!bw.empty && (--numCalls)) 13136 { 13137 bw.front; 13138 assert(bw.front == initialFrontValue); 13139 } 13140 13141 /* 13142 Check that empty works properly and that popFront does not get called 13143 more times than it should 13144 */ 13145 numCalls = 0; 13146 while (!bw.empty) 13147 { 13148 ++numCalls; 13149 13150 static if (hasLength!T) 13151 { 13152 assert(bw.length == bwLength); 13153 --bwLength; 13154 } 13155 13156 static if (isForwardRange!T) 13157 { 13158 assert(bw.front == bwFwdSave.front); 13159 bwFwdSave.popFront(); 13160 } 13161 13162 static if (isBidirectionalRange!T) 13163 { 13164 assert(bwBack.front == bwBackSave.front); 13165 bwBack.popBack(); 13166 bwBackSave.popBack(); 13167 } 13168 bw.popFront(); 13169 } 13170 13171 auto rangeLen = numCalls / (IntegralType.sizeof * 8); 13172 assert(numCalls == (IntegralType.sizeof * 8 * rangeLen)); 13173 assert(bw.empty); 13174 static if (isForwardRange!T) 13175 { 13176 assert(bwFwdSave.empty); 13177 } 13178 13179 static if (isBidirectionalRange!T) 13180 { 13181 assert(bwBack.empty); 13182 } 13183 } 13184 } 13185 } 13186 13187 // Test opIndex and opSlice 13188 @system unittest 13189 { 13190 import std.meta : AliasSeq; 13191 alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint, 13192 long, ulong); 13193 foreach (IntegralType; IntegralTypes) 13194 { 13195 size_t bitsNum = IntegralType.sizeof * 8; 13196 13197 auto first = cast(IntegralType)(1); 13198 13199 // 2 ^ (bitsNum - 1) 13200 auto second = cast(IntegralType)(cast(IntegralType)(1) << (bitsNum - 2)); 13201 13202 IntegralType[] a = [first, second]; 13203 auto bw = Bitwise!(IntegralType[])(a); 13204 13205 // Check against lsb of a[0] 13206 assert(bw[0] == true); 13207 // Check against msb - 1 of a[1] 13208 assert(bw[2 * bitsNum - 2] == true); 13209 13210 bw.popFront(); 13211 assert(bw[2 * bitsNum - 3] == true); 13212 13213 import std.exception : assertThrown; 13214 13215 version (D_NoBoundsChecks) {} 13216 else 13217 { 13218 // Check out of bounds error 13219 assertThrown!Error(bw[2 * bitsNum - 1]); 13220 } 13221 13222 bw[2] = true; 13223 assert(bw[2] == true); 13224 bw.popFront(); 13225 assert(bw[1] == true); 13226 13227 auto bw2 = bw[0 .. $ - 5]; 13228 auto bw3 = bw2[]; 13229 assert(bw2.length == (bw.length - 5)); 13230 assert(bw2.length == bw3.length); 13231 bw2.popFront(); 13232 assert(bw2.length != bw3.length); 13233 } 13234 } 13235 13236 /********************************* 13237 * An OutputRange that discards the data it receives. 13238 */ 13239 struct NullSink 13240 { 13241 void put(E)(scope const E) pure @safe @nogc nothrow {} 13242 } 13243 13244 /// ditto 13245 auto ref nullSink() 13246 { 13247 static NullSink sink; 13248 return sink; 13249 } 13250 13251 /// 13252 @safe nothrow unittest 13253 { 13254 import std.algorithm.iteration : map; 13255 import std.algorithm.mutation : copy; 13256 [4, 5, 6].map!(x => x * 2).copy(nullSink); // data is discarded 13257 } 13258 13259 /// 13260 @safe unittest 13261 { 13262 import std.csv : csvNextToken; 13263 13264 string line = "a,b,c"; 13265 13266 // ignore the first column 13267 line.csvNextToken(nullSink, ',', '"'); 13268 line.popFront; 13269 13270 // look at the second column 13271 Appender!string app; 13272 line.csvNextToken(app, ',', '"'); 13273 assert(app.data == "b"); 13274 } 13275 13276 @safe unittest 13277 { 13278 auto r = 10.iota 13279 .tee(nullSink) 13280 .dropOne; 13281 13282 assert(r.front == 1); 13283 } 13284 13285 /++ 13286 13287 Implements a "tee" style pipe, wrapping an input range so that elements of the 13288 range can be passed to a provided function or $(LREF OutputRange) as they are 13289 iterated over. This is useful for printing out intermediate values in a long 13290 chain of range code, performing some operation with side-effects on each call 13291 to `front` or `popFront`, or diverting the elements of a range into an 13292 auxiliary $(LREF OutputRange). 13293 13294 It is important to note that as the resultant range is evaluated lazily, 13295 in the case of the version of `tee` that takes a function, the function 13296 will not actually be executed until the range is "walked" using functions 13297 that evaluate ranges, such as $(REF array, std,array) or 13298 $(REF fold, std,algorithm,iteration). 13299 13300 Params: 13301 pipeOnPop = If `Yes.pipeOnPop`, simply iterating the range without ever 13302 calling `front` is enough to have `tee` mirror elements to `outputRange` (or, 13303 respectively, `fun`). Note that each `popFront()` call will mirror the 13304 old `front` value, not the new one. This means that the last value will 13305 not be forwarded if the range isn't iterated until empty. If 13306 `No.pipeOnPop`, only elements for which `front` does get called will be 13307 also sent to `outputRange`/`fun`. If `front` is called twice for the same 13308 element, it will still be sent only once. If this caching is undesired, 13309 consider using $(REF map, std,algorithm,iteration) instead. 13310 inputRange = The input range being passed through. 13311 outputRange = This range will receive elements of `inputRange` progressively 13312 as iteration proceeds. 13313 fun = This function will be called with elements of `inputRange` 13314 progressively as iteration proceeds. 13315 13316 Returns: 13317 An input range that offers the elements of `inputRange`. Regardless of 13318 whether `inputRange` is a more powerful range (forward, bidirectional etc), 13319 the result is always an input range. Reading this causes `inputRange` to be 13320 iterated and returns its elements in turn. In addition, the same elements 13321 will be passed to `outputRange` or `fun` as well. 13322 13323 See_Also: $(REF each, std,algorithm,iteration) 13324 +/ 13325 auto tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1 inputRange, R2 outputRange) 13326 if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1)) 13327 { 13328 static struct Result 13329 { 13330 private R1 _input; 13331 private R2 _output; 13332 static if (!pipeOnPop) 13333 { 13334 private bool _frontAccessed; 13335 } 13336 13337 mixin ImplementLength!_input; 13338 13339 static if (isInfinite!R1) 13340 { 13341 enum bool empty = false; 13342 } 13343 else 13344 { 13345 @property bool empty() { return _input.empty; } 13346 } 13347 13348 void popFront() 13349 { 13350 assert(!_input.empty, "Attempting to popFront an empty tee"); 13351 static if (pipeOnPop) 13352 { 13353 put(_output, _input.front); 13354 } 13355 else 13356 { 13357 _frontAccessed = false; 13358 } 13359 _input.popFront(); 13360 } 13361 13362 @property auto ref front() 13363 { 13364 assert(!_input.empty, "Attempting to fetch the front of an empty tee"); 13365 static if (!pipeOnPop) 13366 { 13367 if (!_frontAccessed) 13368 { 13369 _frontAccessed = true; 13370 put(_output, _input.front); 13371 } 13372 } 13373 return _input.front; 13374 } 13375 } 13376 13377 return Result(inputRange, outputRange); 13378 } 13379 13380 /// Ditto 13381 auto tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1 inputRange) 13382 if (is(typeof(fun) == void) || isSomeFunction!fun) 13383 { 13384 import std.traits : isDelegate, isFunctionPointer; 13385 /* 13386 Distinguish between function literals and template lambdas 13387 when using either as an $(LREF OutputRange). Since a template 13388 has no type, typeof(template) will always return void. 13389 If it's a template lambda, it's first necessary to instantiate 13390 it with `ElementType!R1`. 13391 */ 13392 static if (is(typeof(fun) == void)) 13393 alias _fun = fun!(ElementType!R1); 13394 else 13395 alias _fun = fun; 13396 13397 static if (isFunctionPointer!_fun || isDelegate!_fun) 13398 { 13399 return tee!pipeOnPop(inputRange, _fun); 13400 } 13401 else 13402 { 13403 return tee!pipeOnPop(inputRange, &_fun); 13404 } 13405 } 13406 13407 /// 13408 @safe unittest 13409 { 13410 import std.algorithm.comparison : equal; 13411 import std.algorithm.iteration : filter, map; 13412 13413 // Sum values while copying 13414 int[] values = [1, 4, 9, 16, 25]; 13415 int sum = 0; 13416 auto newValues = values.tee!(a => sum += a).array; 13417 assert(equal(newValues, values)); 13418 assert(sum == 1 + 4 + 9 + 16 + 25); 13419 13420 // Count values that pass the first filter 13421 int count = 0; 13422 auto newValues4 = values.filter!(a => a < 10) 13423 .tee!(a => count++) 13424 .map!(a => a + 1) 13425 .filter!(a => a < 10); 13426 13427 //Fine, equal also evaluates any lazy ranges passed to it. 13428 //count is not 3 until equal evaluates newValues4 13429 assert(equal(newValues4, [2, 5])); 13430 assert(count == 3); 13431 } 13432 13433 // 13434 @safe unittest 13435 { 13436 import std.algorithm.comparison : equal; 13437 import std.algorithm.iteration : filter, map; 13438 13439 int[] values = [1, 4, 9, 16, 25]; 13440 13441 int count = 0; 13442 auto newValues = values.filter!(a => a < 10) 13443 .tee!(a => count++, No.pipeOnPop) 13444 .map!(a => a + 1) 13445 .filter!(a => a < 10); 13446 13447 auto val = newValues.front; 13448 assert(count == 1); 13449 //front is only evaluated once per element 13450 val = newValues.front; 13451 assert(count == 1); 13452 13453 //popFront() called, fun will be called 13454 //again on the next access to front 13455 newValues.popFront(); 13456 newValues.front; 13457 assert(count == 2); 13458 13459 int[] preMap = new int[](3), postMap = []; 13460 auto mappedValues = values.filter!(a => a < 10) 13461 //Note the two different ways of using tee 13462 .tee(preMap) 13463 .map!(a => a + 1) 13464 .tee!(a => postMap ~= a) 13465 .filter!(a => a < 10); 13466 assert(equal(mappedValues, [2, 5])); 13467 assert(equal(preMap, [1, 4, 9])); 13468 assert(equal(postMap, [2, 5, 10])); 13469 } 13470 13471 // 13472 @safe unittest 13473 { 13474 import std.algorithm.comparison : equal; 13475 import std.algorithm.iteration : filter, map; 13476 13477 char[] txt = "Line one, Line 2".dup; 13478 13479 bool isVowel(dchar c) 13480 { 13481 import std.string : indexOf; 13482 return "AaEeIiOoUu".indexOf(c) != -1; 13483 } 13484 13485 int vowelCount = 0; 13486 int shiftedCount = 0; 13487 auto removeVowels = txt.tee!(c => isVowel(c) ? vowelCount++ : 0) 13488 .filter!(c => !isVowel(c)) 13489 .map!(c => (c == ' ') ? c : c + 1) 13490 .tee!(c => isVowel(c) ? shiftedCount++ : 0); 13491 assert(equal(removeVowels, "Mo o- Mo 3")); 13492 assert(vowelCount == 6); 13493 assert(shiftedCount == 3); 13494 } 13495 13496 @safe unittest 13497 { 13498 // Manually stride to test different pipe behavior. 13499 void testRange(Range)(Range r) 13500 { 13501 const int strideLen = 3; 13502 int i = 0; 13503 ElementType!Range elem1; 13504 ElementType!Range elem2; 13505 while (!r.empty) 13506 { 13507 if (i % strideLen == 0) 13508 { 13509 //Make sure front is only 13510 //evaluated once per item 13511 elem1 = r.front; 13512 elem2 = r.front; 13513 assert(elem1 == elem2); 13514 } 13515 r.popFront(); 13516 i++; 13517 } 13518 } 13519 13520 string txt = "abcdefghijklmnopqrstuvwxyz"; 13521 13522 int popCount = 0; 13523 auto pipeOnPop = txt.tee!(a => popCount++); 13524 testRange(pipeOnPop); 13525 assert(popCount == 26); 13526 13527 int frontCount = 0; 13528 auto pipeOnFront = txt.tee!(a => frontCount++, No.pipeOnPop); 13529 testRange(pipeOnFront); 13530 assert(frontCount == 9); 13531 } 13532 13533 @safe unittest 13534 { 13535 import std.algorithm.comparison : equal; 13536 import std.meta : AliasSeq; 13537 13538 //Test diverting elements to an OutputRange 13539 string txt = "abcdefghijklmnopqrstuvwxyz"; 13540 13541 dchar[] asink1 = []; 13542 auto fsink = (dchar c) { asink1 ~= c; }; 13543 auto result1 = txt.tee(fsink).array; 13544 assert(equal(txt, result1) && (equal(result1, asink1))); 13545 13546 dchar[] _asink1 = []; 13547 auto _result1 = txt.tee!((dchar c) { _asink1 ~= c; })().array; 13548 assert(equal(txt, _result1) && (equal(_result1, _asink1))); 13549 13550 dchar[] asink2 = new dchar[](txt.length); 13551 void fsink2(dchar c) { static int i = 0; asink2[i] = c; i++; } 13552 auto result2 = txt.tee(&fsink2).array; 13553 assert(equal(txt, result2) && equal(result2, asink2)); 13554 13555 dchar[] asink3 = new dchar[](txt.length); 13556 auto result3 = txt.tee(asink3).array; 13557 assert(equal(txt, result3) && equal(result3, asink3)); 13558 13559 static foreach (CharType; AliasSeq!(char, wchar, dchar)) 13560 {{ 13561 auto appSink = appender!(CharType[])(); 13562 auto appResult = txt.tee(appSink).array; 13563 assert(equal(txt, appResult) && equal(appResult, appSink.data)); 13564 }} 13565 13566 static foreach (StringType; AliasSeq!(string, wstring, dstring)) 13567 {{ 13568 auto appSink = appender!StringType(); 13569 auto appResult = txt.tee(appSink).array; 13570 assert(equal(txt, appResult) && equal(appResult, appSink.data)); 13571 }} 13572 } 13573 13574 // https://issues.dlang.org/show_bug.cgi?id=13483 13575 @safe unittest 13576 { 13577 static void func1(T)(T x) {} 13578 void func2(int x) {} 13579 13580 auto r = [1, 2, 3, 4].tee!func1.tee!func2; 13581 } 13582 13583 /** 13584 Extends the length of the input range `r` by padding out the start of the 13585 range with the element `e`. The element `e` must be of a common type with 13586 the element type of the range `r` as defined by $(REF CommonType, std, traits). 13587 If `n` is less than the length of of `r`, then `r` is returned unmodified. 13588 13589 If `r` is a string with Unicode characters in it, `padLeft` follows D's rules 13590 about length for strings, which is not the number of characters, or 13591 graphemes, but instead the number of encoding units. If you want to treat each 13592 grapheme as only one encoding unit long, then call 13593 $(REF byGrapheme, std, uni) before calling this function. 13594 13595 If `r` has a length, then this is $(BIGOH 1). Otherwise, it's $(BIGOH r.length). 13596 13597 Params: 13598 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length, or a forward range 13599 e = element to pad the range with 13600 n = the length to pad to 13601 13602 Returns: 13603 A range containing the elements of the original range with the extra padding 13604 13605 See Also: 13606 $(REF leftJustifier, std, string) 13607 */ 13608 auto padLeft(R, E)(R r, E e, size_t n) 13609 if ( 13610 ((isInputRange!R && hasLength!R) || isForwardRange!R) && 13611 !is(CommonType!(ElementType!R, E) == void) 13612 ) 13613 { 13614 static if (hasLength!R) 13615 auto dataLength = r.length; 13616 else 13617 auto dataLength = r.save.walkLength(n); 13618 13619 return e.repeat(n > dataLength ? n - dataLength : 0).chain(r); 13620 } 13621 13622 /// 13623 @safe pure unittest 13624 { 13625 import std.algorithm.comparison : equal; 13626 13627 assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4])); 13628 assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4])); 13629 13630 assert("abc".padLeft('_', 6).equal("___abc")); 13631 } 13632 13633 @safe pure nothrow unittest 13634 { 13635 import std.algorithm.comparison : equal; 13636 import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy; 13637 import std.meta : AliasSeq; 13638 13639 alias DummyRanges = AliasSeq!( 13640 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input), 13641 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), 13642 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), 13643 DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), 13644 DummyRange!(ReturnBy.Reference, Length.No, RangeType.Forward), 13645 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Input), 13646 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Forward), 13647 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Bidirectional), 13648 DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random), 13649 DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward) 13650 ); 13651 13652 foreach (Range; DummyRanges) 13653 { 13654 Range r; 13655 assert(r 13656 .padLeft(0, 12) 13657 .equal([0, 0, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]) 13658 ); 13659 } 13660 } 13661 13662 // Test nogc inference 13663 @safe @nogc pure unittest 13664 { 13665 import std.algorithm.comparison : equal; 13666 13667 static immutable r1 = [1, 2, 3, 4]; 13668 static immutable r2 = [0, 0, 1, 2, 3, 4]; 13669 assert(r1.padLeft(0, 6).equal(r2)); 13670 } 13671 13672 /** 13673 Extend the length of the input range `r` by padding out the end of the range 13674 with the element `e`. The element `e` must be of a common type with the 13675 element type of the range `r` as defined by $(REF CommonType, std, traits). 13676 If `n` is less than the length of of `r`, then the contents of `r` are 13677 returned. 13678 13679 The range primitives that the resulting range provides depends whether or not `r` 13680 provides them. Except the functions `back` and `popBack`, which also require 13681 the range to have a length as well as `back` and `popBack` 13682 13683 Params: 13684 r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with a length 13685 e = element to pad the range with 13686 n = the length to pad to 13687 13688 Returns: 13689 A range containing the elements of the original range with the extra padding 13690 13691 See Also: 13692 $(REF rightJustifier, std, string) 13693 */ 13694 auto padRight(R, E)(R r, E e, size_t n) 13695 if ( 13696 isInputRange!R && 13697 !isInfinite!R && 13698 !is(CommonType!(ElementType!R, E) == void)) 13699 { 13700 static struct Result 13701 { 13702 private: 13703 R data; 13704 E element; 13705 static if (hasLength!R) 13706 { 13707 size_t padLength; 13708 } 13709 else 13710 { 13711 size_t minLength; 13712 size_t consumed; 13713 } 13714 13715 public: 13716 bool empty() @property 13717 { 13718 static if (hasLength!R) 13719 { 13720 return data.empty && padLength == 0; 13721 } 13722 else 13723 { 13724 return data.empty && consumed >= minLength; 13725 } 13726 } 13727 13728 auto front() @property 13729 { 13730 assert(!empty, "Attempting to fetch the front of an empty padRight"); 13731 return data.empty ? element : data.front; 13732 } 13733 13734 void popFront() 13735 { 13736 assert(!empty, "Attempting to popFront an empty padRight"); 13737 13738 static if (hasLength!R) 13739 { 13740 if (!data.empty) 13741 { 13742 data.popFront; 13743 } 13744 else 13745 { 13746 --padLength; 13747 } 13748 } 13749 else 13750 { 13751 ++consumed; 13752 if (!data.empty) 13753 { 13754 data.popFront; 13755 } 13756 } 13757 } 13758 13759 static if (hasLength!R) 13760 { 13761 size_t length() @property 13762 { 13763 return data.length + padLength; 13764 } 13765 } 13766 13767 static if (isForwardRange!R) 13768 { 13769 auto save() @property 13770 { 13771 typeof(this) result = this; 13772 data = data.save; 13773 return result; 13774 } 13775 } 13776 13777 static if (isBidirectionalRange!R && hasLength!R) 13778 { 13779 auto back() @property 13780 { 13781 assert(!empty, "Attempting to fetch the back of an empty padRight"); 13782 return padLength > 0 ? element : data.back; 13783 } 13784 13785 void popBack() 13786 { 13787 assert(!empty, "Attempting to popBack an empty padRight"); 13788 if (padLength > 0) 13789 { 13790 --padLength; 13791 } 13792 else 13793 { 13794 data.popBack; 13795 } 13796 } 13797 } 13798 13799 static if (isRandomAccessRange!R && hasLength!R) 13800 { 13801 E opIndex(size_t index) 13802 { 13803 assert(index <= this.length, "Index out of bounds"); 13804 return index >= data.length ? element : data[index]; 13805 } 13806 } 13807 13808 static if (hasSlicing!R && hasLength!R) 13809 { 13810 auto opSlice(size_t a, size_t b) 13811 { 13812 assert( 13813 a <= b, 13814 "Attempting to slice a padRight with a larger first argument than the second." 13815 ); 13816 assert( 13817 b <= length, 13818 "Attempting to slice using an out of bounds index on a padRight" 13819 ); 13820 return Result( 13821 a >= data.length ? data[0 .. 0] : b <= data.length ? data[a .. b] : data[a .. data.length], 13822 element, b - a); 13823 } 13824 13825 alias opDollar = length; 13826 } 13827 13828 this(R r, E e, size_t n) 13829 { 13830 data = r; 13831 element = e; 13832 static if (hasLength!R) 13833 { 13834 padLength = n > data.length ? n - data.length : 0; 13835 } 13836 else 13837 { 13838 minLength = n; 13839 } 13840 } 13841 13842 @disable this(); 13843 } 13844 13845 return Result(r, e, n); 13846 } 13847 13848 /// 13849 @safe pure unittest 13850 { 13851 import std.algorithm.comparison : equal; 13852 13853 assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0])); 13854 assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4])); 13855 13856 assert("abc".padRight('_', 6).equal("abc___")); 13857 } 13858 13859 pure @safe unittest 13860 { 13861 import std.algorithm.comparison : equal; 13862 import std.internal.test.dummyrange : AllDummyRanges, ReferenceInputRange; 13863 import std.meta : AliasSeq; 13864 13865 auto string_input_range = new ReferenceInputRange!dchar(['a', 'b', 'c']); 13866 dchar padding = '_'; 13867 assert(string_input_range.padRight(padding, 6).equal("abc___")); 13868 13869 foreach (RangeType; AllDummyRanges) 13870 { 13871 RangeType r1; 13872 assert(r1 13873 .padRight(0, 12) 13874 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) 13875 ); 13876 13877 // test if Result properly uses random access ranges 13878 static if (isRandomAccessRange!RangeType) 13879 { 13880 RangeType r3; 13881 assert(r3.padRight(0, 12)[0] == 1); 13882 assert(r3.padRight(0, 12)[2] == 3); 13883 assert(r3.padRight(0, 12)[9] == 10); 13884 assert(r3.padRight(0, 12)[10] == 0); 13885 assert(r3.padRight(0, 12)[11] == 0); 13886 } 13887 13888 // test if Result properly uses slicing and opDollar 13889 static if (hasSlicing!RangeType) 13890 { 13891 RangeType r4; 13892 assert(r4 13893 .padRight(0, 12)[0 .. 3] 13894 .equal([1, 2, 3]) 13895 ); 13896 assert(r4 13897 .padRight(0, 12)[0 .. 10] 13898 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U]) 13899 ); 13900 assert(r4 13901 .padRight(0, 12)[0 .. 11] 13902 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0]) 13903 ); 13904 assert(r4 13905 .padRight(0, 12)[2 .. $] 13906 .equal([3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) 13907 ); 13908 assert(r4 13909 .padRight(0, 12)[0 .. $] 13910 .equal([1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 0, 0]) 13911 ); 13912 } 13913 13914 // drop & dropBack test opslice ranges when available, popFront/popBack otherwise 13915 RangeType r5; 13916 foreach (i; 1 .. 13) assert(r5.padRight(0, 12).drop(i).walkLength == 12 - i); 13917 } 13918 } 13919 13920 // Test nogc inference 13921 @safe @nogc pure unittest 13922 { 13923 import std.algorithm.comparison : equal; 13924 13925 static immutable r1 = [1, 2, 3, 4]; 13926 static immutable r2 = [1, 2, 3, 4, 0, 0]; 13927 assert(r1.padRight(0, 6).equal(r2)); 13928 } 13929 13930 // Test back, popBack, and save 13931 @safe pure unittest 13932 { 13933 import std.algorithm.comparison : equal; 13934 13935 auto r1 = [1, 2, 3, 4].padRight(0, 6); 13936 assert(r1.back == 0); 13937 13938 r1.popBack; 13939 auto r2 = r1.save; 13940 assert(r1.equal([1, 2, 3, 4, 0])); 13941 assert(r2.equal([1, 2, 3, 4, 0])); 13942 13943 r1.popBackN(2); 13944 assert(r1.back == 3); 13945 assert(r1.length == 3); 13946 assert(r2.length == 5); 13947 assert(r2.equal([1, 2, 3, 4, 0])); 13948 13949 r2.popFront; 13950 assert(r2.length == 4); 13951 assert(r2[0] == 2); 13952 assert(r2[1] == 3); 13953 assert(r2[2] == 4); 13954 assert(r2[3] == 0); 13955 assert(r2.equal([2, 3, 4, 0])); 13956 13957 r2.popBack; 13958 assert(r2.equal([2, 3, 4])); 13959 13960 auto r3 = [1, 2, 3, 4].padRight(0, 6); 13961 size_t len = 0; 13962 while (!r3.empty) 13963 { 13964 ++len; 13965 r3.popBack; 13966 } 13967 assert(len == 6); 13968 } 13969 13970 // https://issues.dlang.org/show_bug.cgi?id=19042 13971 @safe pure unittest 13972 { 13973 import std.algorithm.comparison : equal; 13974 13975 assert([2, 5, 13].padRight(42, 10).chunks(5) 13976 .equal!equal([[2, 5, 13, 42, 42], [42, 42, 42, 42, 42]])); 13977 13978 assert([1, 2, 3, 4].padRight(0, 10)[7 .. 9].equal([0, 0])); 13979 } 13980 13981 /** 13982 This simplifies a commonly used idiom in phobos for accepting any kind of string 13983 parameter. The type `R` can for example be a simple string, chained string using 13984 $(REF chain, std,range), $(REF chainPath, std,path) or any other input range of 13985 characters. 13986 13987 Only finite length character ranges are allowed with this constraint. 13988 13989 This template is equivalent to: 13990 --- 13991 isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) 13992 --- 13993 13994 See_Also: 13995 $(REF isInputRange, std,range,primitives), 13996 $(REF isInfinite, std,range,primitives), 13997 $(LREF isSomeChar), 13998 $(REF ElementEncodingType, std,range,primitives) 13999 */ 14000 template isSomeFiniteCharInputRange(R) 14001 { 14002 import std.traits : isSomeChar; 14003 14004 enum isSomeFiniteCharInputRange = isInputRange!R && !isInfinite!R 14005 && isSomeChar!(ElementEncodingType!R); 14006 } 14007 14008 /// 14009 @safe unittest 14010 { 14011 import std.path : chainPath; 14012 import std.range : chain; 14013 14014 void someLibraryMethod(R)(R argument) 14015 if (isSomeFiniteCharInputRange!R) 14016 { 14017 // implementation detail, would iterate over each character of argument 14018 } 14019 14020 someLibraryMethod("simple strings work"); 14021 someLibraryMethod(chain("chained", " ", "strings", " ", "work")); 14022 someLibraryMethod(chainPath("chained", "paths", "work")); 14023 // you can also use custom structs implementing a char range 14024 } 14025