1 /** Extensions to std.range. 2 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 3 */ 4 5 module nxt.range_ex; 6 7 import core.internal.traits: hasElaborateDestructor; 8 import std.traits : isSomeString, isNarrowString; 9 import std.range: hasSlicing, hasLength, isInfinite, isInputRange, isBidirectionalRange, ElementType, isRandomAccessRange; 10 import std.traits: hasUnsharedAliasing, isScalarType; 11 12 public import nxt.slicing; 13 14 enum hasPureCopy(T) = (isScalarType!T || // TODO: remove? 15 (!hasUnsharedAliasing!T && 16 !hasElaborateDestructor!T)); 17 18 enum hasStealableElements(R) = (hasPureCopy!(ElementType!R)); // TODO: recurse 19 20 /* template hasStealableElements(T...) */ 21 /* { */ 22 /* import std.range: ElementType; */ 23 /* import std.typecons : Rebindable; */ 24 25 /* static if (is(ElementType!T)) */ 26 /* { */ 27 /* enum hasStealableElements = true; */ 28 /* } */ 29 /* else static if (is(T[0] R: Rebindable!R)) */ 30 /* { */ 31 /* enum hasStealableElements = hasStealableElements!R; */ 32 /* } */ 33 /* else */ 34 /* { */ 35 /* template unsharedDelegate(T) */ 36 /* { */ 37 /* enum bool unsharedDelegate = isDelegate!T */ 38 /* && !is(T == shared) */ 39 /* && !is(T == shared) */ 40 /* && !is(T == immutable) */ 41 /* && !is(FunctionTypeOf!T == shared) */ 42 /* && !is(FunctionTypeOf!T == immutable); */ 43 /* } */ 44 45 /* enum hasStealableElements = */ 46 /* hasRawUnsharedAliasing!(T[0]) || */ 47 /* anySatisfy!(unsharedDelegate, RepresentationTypeTuple!(T[0])) || */ 48 /* hasUnsharedObjects!(T[0]) || */ 49 /* hasStealableElements!(T[1..$]); */ 50 /* } */ 51 /* } */ 52 53 /** Steal front from $(D r) destructively and return it. 54 * 55 * See_Also: http://forum.dlang.org/thread/jkbhlezbcrufowxtthmy@forum.dlang.org#post-konhvblwbmpdrbeqhyuv:40forum.dlang.org 56 * See_Also: http://forum.dlang.org/thread/onibkzepudfisxtrigsi@forum.dlang.org#post-dafmzroxvaeejyxrkbon:40forum.dlang.org 57 * See_Also: https://forum.dlang.org/thread/arufovtvoewhyfuesvco@forum.dlang.org 58 */ 59 ElementType!R frontPop(R)(ref R r) 60 if (isInputRange!R && 61 hasStealableElements!R) 62 { 63 import std.range.primitives : moveFront, popFront; 64 /* scope(success) r.popFront(); */ 65 /* return r.moveFront(); */ 66 auto e = r.moveFront(); 67 68 r.popFront(); 69 70 import std.traits : hasIndirections; 71 static if (hasIndirections!(typeof(return))) // TODO: better trait? 72 { 73 import core.lifetime : move; 74 return move(e); 75 } 76 else 77 return e; 78 } 79 alias stealFront = frontPop; 80 alias pullFront = frontPop; 81 alias takeFront = frontPop; 82 83 version(unittest) 84 { 85 import nxt.array_help : s; 86 } 87 88 @safe pure nothrow unittest 89 { 90 auto x = [11, 22]; 91 assert(x.frontPop() == 11); assert(x == [22]); 92 assert(x.frontPop() == 22); assert(x == []); 93 } 94 95 @safe pure nothrow unittest 96 { 97 auto x = ["a", "b"]; 98 assert(x.frontPop() == "a"); assert(x == ["b"]); 99 } 100 101 @safe pure nothrow unittest 102 { 103 struct V { int x, y; } 104 auto x = [V(11, 12), 105 V(21, 22)]; 106 assert(x.frontPop() == V(11, 12)); assert(x == [V(21, 22)]); 107 assert(x.frontPop() == V(21, 22)); assert(x == []); 108 } 109 110 ElementType!R* frontPtr(R)(R r) @system 111 if (isInputRange!R) 112 { 113 import std.range.primitives: empty, front; 114 if (r.empty) 115 return typeof(return).init; 116 return &(r.front); 117 } 118 119 @trusted pure unittest 120 { 121 auto x = [11, 22]; 122 if (auto y = x.frontPtr) 123 { 124 static assert(is(typeof(y) == int*)); 125 assert(*y == 11); 126 } 127 } 128 129 @trusted pure unittest 130 { 131 const x = [11, 22]; 132 if (auto y = x.frontPtr) 133 { 134 static assert(is(typeof(y) == const(int)*)); 135 assert(*y == 11); 136 } 137 } 138 139 /** Steal back from $(D r) destructively and return it. 140 * 141 * See_Also: http://forum.dlang.org/thread/jkbhlezbcrufowxtthmy@forum.dlang.org#post-konhvblwbmpdrbeqhyuv:40forum.dlang.org 142 * See_Also: http://forum.dlang.org/thread/onibkzepudfisxtrigsi@forum.dlang.org#post-dafmzroxvaeejyxrkbon:40forum.dlang.org 143 * See_Also: https://forum.dlang.org/thread/arufovtvoewhyfuesvco@forum.dlang.org 144 */ 145 ElementType!R backPop(R)(ref R r) 146 if (isInputRange!R && 147 hasStealableElements!R) 148 { 149 import std.range: moveBack, popBack; 150 /* scope(success) r.popBack(); */ 151 /* return r.moveBack(); */ 152 auto e = r.moveBack(); 153 154 r.popBack(); 155 156 import std.traits : hasIndirections; 157 static if (hasIndirections!(typeof(return))) // TODO: better trait? 158 { 159 import core.lifetime : move; 160 return move(e); 161 } 162 else 163 return e; 164 } 165 alias stealBack = backPop; 166 alias pullBack = backPop; 167 alias takeBack = backPop; 168 169 @safe pure nothrow unittest 170 { 171 auto x = [11, 22]; 172 assert(x.backPop() == 22); assert(x == [11]); 173 assert(x.backPop() == 11); assert(x == []); 174 } 175 176 @safe pure nothrow unittest 177 { 178 auto x = ["a", "b"]; 179 assert(x.backPop() == "b"); assert(x == ["a"]); 180 } 181 182 @safe pure nothrow unittest 183 { 184 struct V { int x, y; } 185 auto x = [V(11, 12), 186 V(21, 22)]; 187 assert(x.backPop() == V(21, 22)); assert(x == [V(11, 12)]); 188 assert(x.backPop() == V(11, 12)); assert(x == []); 189 } 190 191 /** Sliding Splitter. 192 * 193 * See_Also: http://forum.dlang.org/thread/dndicafxfubzmndehzux@forum.dlang.org 194 * See_Also: http://forum.dlang.org/thread/uzrbmjonrkixojzflbig@forum.dlang.org#epost-viwkavbmwouiquoqwntm:40forum.dlang.org 195 * 196 * TODO: Use size_t for _lower and _upper instead and reserve _upper = size_t.max 197 * for emptyness? 198 * 199 * TODO: Should lower and upper operate on code units instead of code point if 200 * isNarrowString!Range. ? 201 * 202 * TODO: generalize with stride 203 */ 204 struct SlidingSplitter(Range) 205 if (isSomeString!Range || 206 (hasSlicing!Range && 207 !isInfinite!Range)) 208 { 209 import std.range: isForwardRange; 210 import core.internal.traits : Unqual; 211 import std.typecons : Tuple, tuple; 212 alias R = Unqual!Range; 213 214 this(R)(R data, size_t lower = 0) 215 in (lower <= data.length) 216 { 217 _data = data; 218 static if (hasSlicing!Range) // TODO: should we use isSomeString here instead? 219 { 220 _lower = lower; 221 _upper = data.length; 222 } 223 else 224 { 225 while (lower) 226 { 227 popFront(); 228 --lower; 229 } 230 } 231 _upper = data.length; 232 } 233 234 this(R)(R data, size_t lower, size_t upper) 235 in (lower <= upper + 1 || // the extra + 1 makes empty initialization (lower + 1 == upper) possible in for example opSlice below 236 ((lower <= data.length) && 237 (upper <= data.length))) 238 { 239 _data = data; 240 _lower = lower; 241 _upper = upper; 242 } 243 244 @property Tuple!(R, R) front() 245 { 246 return typeof(return)(_data[0 .. _lower], 247 _data[_lower .. $]); 248 } 249 250 void popFront() 251 { 252 static if (isNarrowString!R) 253 { 254 import std.utf: stride; 255 if (_lower < _upper) 256 _lower += stride(_data, _lower); 257 else // when we can't decode beyond 258 ++_lower; // so just indicate we're beyond back 259 } 260 else 261 ++_lower; 262 } 263 264 static if (!isInfinite!R) 265 { 266 @property Tuple!(R, R) back() 267 { 268 return typeof(return)(_data[0 .. _upper], 269 _data[_upper .. $]); 270 } 271 void popBack() 272 { 273 static if (isNarrowString!R) 274 { 275 import std.utf: strideBack; 276 if (_lower < _upper) 277 _upper -= strideBack(_data, _upper); 278 else // when we can't decode beyond 279 --_upper; // so just indicate we're beyond front 280 } 281 else 282 --_upper; 283 } 284 } 285 286 static if (isForwardRange!R) 287 { 288 @property auto save() 289 { 290 import std.range: save; 291 return typeof(this)(_data.save, _lower, _upper); 292 } 293 } 294 295 static if (isInfinite!R) 296 enum bool empty = false; // propagate infiniteness 297 else 298 { 299 @property bool empty() const 300 { 301 return _upper < _lower; 302 } 303 } 304 305 static if (hasSlicing!R) 306 { 307 Tuple!(R, R) opIndex(size_t i) 308 in (i < length) 309 { 310 return typeof(return)(_data[0 .. _lower + i], 311 _data[_lower + i .. _upper]); 312 } 313 314 typeof(this) opSlice(size_t lower, size_t upper) 315 { 316 if (lower == upper) 317 return slidingSplitter(_data, 318 _upper + 1, // defines empty intialization 319 _upper); 320 else 321 return slidingSplitter(_data, 322 _lower + lower, 323 _lower + (upper - 1)); 324 } 325 326 // TODO: Should length be provided if isNarrowString!Range? 327 @property size_t length() const 328 { 329 return _upper - _lower + 1; 330 } 331 } 332 333 private R _data; 334 private ptrdiff_t _lower; 335 private ptrdiff_t _upper; 336 } 337 338 auto slidingSplitter(R)(R data, size_t lower = 0) 339 { 340 return SlidingSplitter!R(data, lower, data.length); 341 } 342 343 auto slidingSplitter(R)(R data, size_t lower, size_t upper) 344 { 345 return SlidingSplitter!R(data, lower, upper); 346 } 347 348 @safe pure nothrow unittest 349 { 350 import std.typecons : tuple; 351 import std.conv: to; 352 353 auto x = [1, 2, 3]; 354 355 import std.range: isInputRange, isForwardRange, isBidirectionalRange, isRandomAccessRange; 356 357 static assert(isInputRange!(SlidingSplitter!(typeof(x)))); 358 static assert(isForwardRange!(SlidingSplitter!(typeof(x)))); 359 // static assert(isBidirectionalRange!(SlidingSplitter!(typeof(x)))); 360 static assert(isRandomAccessRange!(SlidingSplitter!(typeof(x)))); 361 static assert(!isRandomAccessRange!(SlidingSplitter!string)); 362 static assert(!isRandomAccessRange!(SlidingSplitter!wstring)); 363 static assert(isRandomAccessRange!(SlidingSplitter!dstring)); 364 365 auto y = SlidingSplitter!(typeof(x))(x); 366 367 for (size_t i; i < y.length; ++i) 368 { 369 assert(y[i] == tuple(x[0 .. i], 370 x[i .. 3])); 371 } 372 373 assert(y.front == tuple([], x)); 374 assert(!y.empty); 375 assert(x.length + 1 == y.length); 376 377 assert(!y.empty); assert(y.front == tuple(x[0 .. 0], x[0 .. 3])); y.popFront(); 378 assert(!y.empty); assert(y.front == tuple(x[0 .. 1], x[1 .. 3])); y.popFront(); 379 assert(!y.empty); assert(y.front == tuple(x[0 .. 2], x[2 .. 3])); y.popFront(); 380 assert(!y.empty); assert(y.front == tuple(x[0 .. 3], x[3 .. 3])); y.popFront(); 381 y.popFront(); assert(y.empty); 382 } 383 384 @safe pure unittest // forwards 385 { 386 import std.conv: to; 387 388 size_t lower = 2; 389 390 const name = "Nordlöw"; 391 auto name8 = name.to! string.slidingSplitter(lower); 392 auto name16 = name.to!wstring.slidingSplitter(lower); 393 auto name32 = name.to!dstring.slidingSplitter(lower); 394 395 static assert(!__traits(compiles, { name8.length >= 0; } )); 396 static assert(!__traits(compiles, { name16.length >= 0; } )); 397 assert(name32.length); 398 399 foreach (ch; name8) 400 { 401 static foreach (ix; 0 .. ch.length) // for each part in split 402 { 403 import std.algorithm: equal; 404 assert(ch[ix].equal(name16.front[ix])); 405 assert(ch[ix].equal(name32.front[ix])); 406 407 } 408 name16.popFront(); 409 name32.popFront(); 410 } 411 } 412 413 @safe pure unittest // backwards 414 { 415 import std.conv: to; 416 import std.range: retro; 417 418 size_t lower = 2; 419 420 auto name = "Nordlöw"; 421 auto name8 = name.to! string.slidingSplitter(lower).retro; 422 auto name16 = name.to!wstring.slidingSplitter(lower).retro; 423 auto name32 = name.to!dstring.slidingSplitter(lower).retro; 424 425 foreach (ch; name8) 426 { 427 import std.algorithm: equal; 428 static foreach (ix; 0 .. ch.length) // for each part in split 429 { 430 assert(ch[ix].equal(name16.front[ix])); 431 assert(ch[ix].equal(name32.front[ix])); 432 } 433 name16.popFront(); 434 name32.popFront(); 435 } 436 } 437 438 @safe pure nothrow unittest // radial 439 { 440 auto x = [1, 2, 3]; 441 import std.range: radial; 442 import std.typecons : tuple; 443 auto s = x.slidingSplitter; 444 auto r = s.radial; 445 assert(!r.empty); assert(r.front == tuple(x[0 .. 1], x[1 .. 3])); r.popFront(); 446 assert(!r.empty); assert(r.front == tuple(x[0 .. 2], x[2 .. 3])); r.popFront(); 447 assert(!r.empty); assert(r.front == tuple(x[0 .. 0], x[0 .. 3])); r.popFront(); 448 assert(!r.empty); assert(r.front == tuple(x[0 .. 3], x[3 .. 3])); r.popFront(); 449 assert(r.empty); 450 } 451 452 /** Ring Buffer. 453 * 454 * See_Also: http://forum.dlang.org/thread/ltpaqk$2dav$1@digitalmars.com 455 * TODO: inout 456 */ 457 struct RingBuffer(T) 458 { 459 this(T[] data, size_t length = 0) 460 { 461 enforce(data.length, "empty ring buffer is prohibited"); 462 enforce(length <= data.length, "buffer length shall not be more 463 than buffer capacity"); 464 _data = data; 465 _beginIndex = 0; 466 _length = length; 467 } 468 469 auto opSlice() const 470 { 471 return cycle(_data[0 .. _length]).take(_length); 472 } 473 474 @property auto length() { return _length; } 475 476 private: 477 T[] _data; 478 size_t _beginIndex; 479 size_t _length; 480 } 481 482 /** Same as $(D iota) but with explicit conversion to type $(D T). 483 See_Also: http://forum.dlang.org/thread/mailman.955.1444358510.22025.digitalmars-d@puremagic.com?page=1 484 */ 485 auto iotaOf(T, B, E, S)(B begin = T.min, 486 E end = T.max, 487 S step = 1) 488 { 489 import std.range : iota; 490 import std.algorithm.iteration : map; 491 import std.conv : to; 492 return iota(begin, end, step).map!(a => cast(T)a); 493 } 494 495 // @safe pure unittest 496 // { 497 // import std.array : array; 498 // import std.exception : assertThrown; 499 // import std.meta : AliasSeq; 500 // foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 501 // { 502 // auto x = iotaOf!T(0, T.max + 1); 503 // import nxt.traits_ex : ElementTypeOf; 504 // static assert(is(ElementTypeOf!(x) == T)); 505 // } 506 // } 507 508 auto iotaOfExceptional(T, B, E, S)(B begin = T.min, E end = T.max, S step = 1) 509 { 510 import std.range : iota; 511 import std.algorithm.iteration : map; 512 import std.conv : to; 513 return iota(begin, end, step).map!(a => a.to!T); 514 } 515 516 // @safe pure unittest 517 // { 518 // import std.array : array; 519 // import std.exception : assertThrown; 520 // import std.conv; 521 // alias T = ubyte; 522 // auto x = iotaOfExceptional!T(0, T.max + 1); 523 // import nxt.traits_ex : ElementTypeOf; 524 // static assert(is(ElementTypeOf!() == T)); 525 // assertThrown!ConvOverflowException(iotaOfExceptional!T(0, T.max + 1 + 1).array); 526 // } 527 528 /** Return Array of Key-Value Pairs of Associative Array $(D aa). 529 * 530 * See_Also: https://github.com/D-Programming-Language/druntime/pull/574 531 * See_Also: http://forum.dlang.org/thread/dxotcrutrlmszlidufcr@forum.dlang.org?page=2#post-fhkgitmifgnompkqiscd:40forum.dlang.org 532 */ 533 auto pairs(Key, Value)(Value[Key] aa) 534 { 535 import std.typecons: Tuple, tuple; 536 Tuple!(Key,Value)[] arr; 537 arr.reserve(aa.length); 538 foreach (key; aa.byKey) 539 { 540 arr ~= tuple(key, aa[key]); 541 } 542 return arr; 543 } 544 alias items = pairs; // TODO: Is this Python-style naming better? 545 546 unittest 547 { 548 string[int] x; 549 x[0] = "a"; 550 import std.typecons : tuple; 551 assert(x.pairs == [tuple(0, "a")]); 552 } 553 554 import std.traits: isInstanceOf, Unqual; 555 import std.range: SortedRange; 556 import std.meta: allSatisfy, staticMap; 557 558 /// Is the `CommonType` of the `ElementType`s of the ranges `Rs`. 559 template CommonElementType(Rs...) 560 { 561 import std.traits: CommonType; 562 import std.range: ElementType; 563 alias CommonElementType = CommonType!(staticMap!(ElementType, Rs)); 564 } 565 566 /// 567 @safe pure nothrow @nogc unittest 568 { 569 static assert(is(CommonElementType!(int[], double[]) == double)); 570 } 571 572 enum bool haveCommonElementType(Types...) = !is(CommonElementType!Types == void); 573 574 /// Is `true` iff the ranges `Rs` have a common element type. 575 @safe pure nothrow @nogc unittest 576 { 577 static assert(haveCommonElementType!(bool[], int[])); 578 static assert(!haveCommonElementType!(bool[], int[], string[])); 579 } 580 581 alias isSortedRange(R) = isInstanceOf!(SortedRange, R); // TODO: Or use: __traits(isSame, TemplateOf!R, SortedRange) 582 583 /** True if R is a `SortedRange` 584 * 585 * SeeAlso: 586 * `std.range.SortedRange` 587 */ 588 template isSortedRange_alt(R) 589 { 590 import std.range.primitives : SortedRange; 591 enum isSortedRange = is(R : SortedRange!U, U...); 592 } 593 594 /// 595 unittest 596 { 597 import std.algorithm : sort; 598 import std.range : assumeSorted; 599 static assert(isSortedRange!(typeof([0, 1, 2])) == false); 600 static assert(isSortedRange!(typeof([0, 1, 2].sort)) == true); 601 static assert(isSortedRange!(typeof([0, 1, 2].assumeSorted)) == true); 602 static assert(isSortedRange!int == false); 603 } 604 605 /// See_Also: http://forum.dlang.org/post/gkdqakdogqevwzntpgtu@forum.dlang.org 606 template genTypeList(T, size_t n) 607 { 608 import std.meta : AliasSeq; 609 static if (n <= 1) 610 alias genTypeList = T; 611 else 612 alias genTypeList = AliasSeq!(T, genTypeList!(T, n - 1)); 613 } 614 615 /** Return Static Array $(D arr) as a $(D Tuple). 616 * 617 * See_Also: http://forum.dlang.org/post/gkdqakdogqevwzntpgtu@forum.dlang.org 618 * Check if std.conv.to() support conversion from T[n] to std.typecons.Tuple(T, ...). 619 */ 620 auto asTuple(T, size_t n)(ref T[n] arr) 621 { 622 import std.typecons : Tuple; 623 return Tuple!(genTypeList!(T, n))(arr); 624 } 625 626 /** Return: Adjacent $(D N)-Tuples of $(D r). 627 * 628 * TODO: Support ref return via $(D zip) for non-const case. 629 * TODO: Use a ring buffer instead of copy? 630 * TODO: Add a variant of adjacentTuples that return a static array instead? 631 * See_Also: http://forum.dlang.org/post/gkdqakdogqevwzntpgtu@forum.dlang.org 632 */ 633 auto adjacentTuples(size_t N, R)(R r) 634 if (N >= 2 && 635 isInputRange!R) 636 { 637 struct Range(R) 638 { 639 import core.internal.traits : Unqual; 640 import std.typecons : Tuple; 641 alias E = Unqual!(ElementType!R); 642 enum M = N - 1; // temporary order 643 alias P = E[M]; 644 alias T = Tuple!(genTypeList!(E, N)); // TODO: functionize 645 646 this(R r) 647 { 648 this._source = r; 649 foreach (i; 0 .. M) 650 if (!empty) 651 popFront(); 652 } 653 654 static if (isInfinite!R) 655 enum bool empty = false; // propagate infiniteness 656 else 657 { 658 bool empty() @property // TODO: can't empty be const when R is a MapResult? 659 { 660 import std.range.primitives : empty; 661 return _source.empty; 662 } 663 } 664 665 auto ref front() @property 666 { 667 import std.range.primitives : front; 668 T t; 669 t[0 .. M] = _prevs.asTuple; 670 t[M] = _source.front; 671 return t; 672 } 673 674 void popFront() 675 { 676 static if (N >= 3) 677 { 678 // TODO: use static foreach to do left-shifting 679 680 // Need $(D copy) because $(D source) and $(D dest) may overlap. 681 // See_Also: http://dlang.org/arrays.html#overlapping-copying 682 import std.algorithm.mutation : copy; 683 copy(_prevs[1 .. M], _prevs[0 .. M - 1]); 684 } 685 686 import std.range.primitives : front, popFront; 687 _prevs[M - 1] = _source.front; 688 _source.popFront(); 689 } 690 691 private: 692 P _prevs; 693 R _source; 694 } 695 return Range!R(r); 696 } 697 698 auto adjacentPairs(R)(R r) 699 if (isInputRange!R) 700 { 701 return adjacentTuples!(2, R)(r); 702 } 703 704 auto adjacentTriples(R)(R r) 705 if (isInputRange!R) 706 { 707 return adjacentTuples!(3, R)(r); 708 } 709 710 /// 711 @safe pure nothrow @nogc unittest 712 { 713 import std.typecons : t = tuple; 714 import std.algorithm : equal, map; 715 auto x = [1, 2, 3, 4, 5, 6, 7].s[].map!(a => a); // test with ForwardRange 716 auto y = x.adjacentTuples!4; 717 assert(y.equal([t(1, 2, 3, 4), 718 t(2, 3, 4, 5), 719 t(3, 4, 5, 6), 720 t(4, 5, 6, 7)].s[])); 721 } 722 723 /// 724 @safe pure nothrow @nogc unittest 725 { 726 import std.typecons : t = tuple; 727 import std.algorithm : equal; 728 immutable x = [1, 2, 3, 4].s; 729 auto y = x[].adjacentPairs; 730 assert(y.equal([t(1, 2), t(2, 3), t(3, 4)].s[])); 731 } 732 733 /// 734 @trusted pure nothrow @nogc unittest // TODO: @safe 735 { 736 import std.typecons : t = tuple; 737 import std.algorithm : equal; 738 auto x = ["1", "2", "3", "4"].s; 739 auto y = x[].adjacentPairs; 740 assert(y.equal([t("1", "2"), t("2", "3"), t("3", "4")].s[])); // TODO: @safe 741 } 742 743 /// 744 @trusted pure nothrow @nogc unittest // TODO: @safe 745 { 746 import std.typecons : t = tuple; 747 import std.algorithm : equal; 748 immutable x = ["1", "2", "3", "4"].s; 749 auto y = x[].adjacentPairs; 750 assert(y.equal([t("1", "2"), t("2", "3"), t("3", "4")].s[])); // TODO: @safe 751 } 752 753 auto rangify(T)(T range) 754 if (__traits(hasMember, T, "length") && 755 __traits(hasMember, T, "opIndex")) 756 { 757 struct Range 758 { 759 bool empty() { return _counter == range.length; } 760 auto front() { return range[_counter]; } 761 auto popFront() { _counter++; } 762 T range; 763 ulong _counter; 764 } 765 return Range(range); 766 } 767 768 struct S 769 { 770 int[] arr; 771 auto length() { return arr.length; } 772 int opIndex(size_t i) { return arr[i]; } 773 } 774 775 unittest 776 { 777 import std.algorithm : equal; 778 auto s = S(); 779 s.arr = [1, 2, 3]; 780 assert(s.rangify.equal([1, 2, 3].s[])); 781 } 782 783 /** Overload has questionable memory safety. Would be quite cool if DIP-1000 784 * could support this use case 785 * 786 * See_Also: http://forum.dlang.org/post/qgrbmkqxffgeiqaigdic@forum.dlang.org 787 */ 788 auto staticLengthRange(T, size_t n)(ref T[n] arr) 789 { 790 return .staticLengthRange!(n, T[])(arr[]); // TODO: DIP-1000 scope 791 } 792 793 import std.range.primitives : hasLength, isInputRange; 794 795 auto staticLengthRange(size_t n, R)(R range) 796 if (isInputRange!R && hasLength!R) 797 { 798 static struct Result 799 { 800 enum size_t length = n; 801 R _range; 802 alias _range this; 803 } 804 assert (range.length == n); 805 return Result(range); 806 } 807 808 809 @safe pure nothrow @nogc unittest 810 { 811 import std.algorithm.iteration : map; 812 813 int[3] sarr = [1, 2, 3]; 814 auto r1 = sarr.staticLengthRange; 815 static assert (isInputRange!(typeof(r1))); 816 static assert (r1.length == 3); 817 818 auto arr = [1, 2, 3, 4].s; 819 auto r2 = arr[].map!(a => a * 2).staticLengthRange!4; 820 static assert (r2.length == 4); 821 } 822 823 /** Given a `SortedRange` R, `sortingPredicate!R(a, b)` will call in to the 824 * predicate that was used to create the `SortedRange`. 825 * 826 * Params: 827 * Range = the range to extract the predicate from 828 * fallbackPred = the sorting predicate to fallback to if `Range` is not a `SortedRange` 829 */ 830 template sortingPredicate(Range, alias fallbackPred = "a < b") 831 if (isInputRange!Range) 832 { 833 import std.range : SortedRange; 834 import std.functional : binaryFun; 835 static if (is(Range : SortedRange!P, P...)) 836 alias sortingPredicate = binaryFun!(P[1]); 837 else 838 alias sortingPredicate = binaryFun!fallbackPred; 839 } 840 841 /// 842 unittest 843 { 844 import std.algorithm : sort; 845 assert(sortingPredicate!(typeof([1].sort!"a < b"))(1, 2) == true); 846 assert(sortingPredicate!(typeof([1].sort!"a > b"))(1, 2) == false); 847 assert(sortingPredicate!(typeof([1].sort!((a, b) => a < b)))(1, 2) == true); 848 assert(sortingPredicate!(typeof([1].sort!((a, b) => a > b)))(1, 2) == false); 849 assert(sortingPredicate!(int[])(1, 2) == true); 850 } 851 852 /** Faster than std.range.zip on DMD. 853 * 854 * See_Also: https://forum.dlang.org/post/khvwfwvjiblobfybsurd@forum.dlang.org 855 */ 856 auto zip(R1, R2)(R1 r1, R2 r2) 857 if (isRandomAccessRange!R1 && 858 isRandomAccessRange!R2) 859 { 860 static struct Result(R1, R2) 861 { 862 size_t _length; 863 864 this(R1 r1, R2 r2) 865 { 866 _r1 = r1; 867 _r2 = r2; 868 import std.algorithm.comparison : min; 869 _length = min(r1.length, r2.length); 870 } 871 872 @property auto front() 873 { 874 import std.typecons : tuple; 875 return tuple(_r1[_index], 876 _r2[_index]); 877 } 878 879 @property bool empty() const @safe pure nothrow @nogc 880 { 881 return _index == _length; 882 } 883 884 @property size_t length() const @safe pure nothrow @nogc 885 { 886 return _length; 887 } 888 889 void popFront() @safe pure nothrow @nogc 890 { 891 assert(!empty); 892 _index += 1; 893 } 894 895 private: 896 size_t _index = 0; 897 R1 _r1; 898 R2 _r2; 899 } 900 return Result!(R1,R2)(r1,r2); 901 } 902 903 @safe pure nothrow @nogc unittest 904 { 905 import nxt.array_help : s; 906 import std.algorithm.comparison : equal; 907 import std.typecons : tuple; 908 const r1 = [1, 2, 3].s; 909 const r2 = [4, 5, 6, 7].s; 910 auto r12 = zip(r1[], r2[]); 911 assert(r12.equal([tuple(1, 4), 912 tuple(2, 5), 913 tuple(3, 6)].s[])); 914 } 915 916 /// Returns: n:th element of `r`. 917 ElementType!R nth(R)(R r, size_t n) 918 if (isInputRange!R) 919 { 920 static if (is(typeof(r[n]) == typeof(return))) 921 return r[n]; 922 else 923 { 924 import std.range.primitives: front, popFront; 925 foreach (_; 0..n) 926 r.popFront(); 927 return r.front(); 928 } 929 } 930 931 @safe pure nothrow unittest 932 { 933 const r = [1, 2, 3]; 934 assert(r.nth(0) == 1); 935 assert(r.nth(1) == 2); 936 assert(r.nth(2) == 3); 937 }