1 /++ 2 This is a submodule of $(MREF mir,ndslice). 3 4 Iterator is a type with a pointer like behavior. 5 An ndslice can be created on top of an iterator using $(SUBREF slice, sliced). 6 7 $(BOOKTABLE $(H2 Iterators), 8 $(TR $(TH Iterator Name) $(TH Used By)) 9 $(T2 BytegroupIterator, $(SUBREF topology, bytegroup).) 10 $(T2 CachedIterator, $(SUBREF topology, cached), $(SUBREF topology, cachedGC).) 11 $(T2 ChopIterator, $(SUBREF topology, chopped)) 12 $(T2 FieldIterator, $(SUBREF slice, slicedField), $(SUBREF topology, bitwise), $(SUBREF topology, ndiota), and others.) 13 $(T2 FlattenedIterator, $(SUBREF topology, flattened)) 14 $(T2 IndexIterator, $(SUBREF topology, indexed)) 15 $(T2 IotaIterator, $(SUBREF topology, iota)) 16 $(T2 MapIterator, $(SUBREF topology, map)) 17 $(T2 MemberIterator, $(SUBREF topology, member)) 18 $(T2 NeighboursIterator, $(SUBREF topology, withNeighboursSum)) 19 $(T2 RetroIterator, $(SUBREF topology, retro)) 20 $(T2 SliceIterator, $(SUBREF topology, map) in composition with $(LREF MapIterator) for packed slices.) 21 $(T2 SlideIterator, $(SUBREF topology, diff), $(SUBREF topology, pairwise), and $(SUBREF topology, slide).) 22 $(T2 StairsIterator, $(SUBREF topology, stairs)) 23 $(T2 StrideIterator, $(SUBREF topology, stride)) 24 $(T2 SubSliceIterator, $(SUBREF topology, subSlices)) 25 $(T2 TripletIterator, $(SUBREF topology, triplets)) 26 $(T2 ZipIterator, $(SUBREF topology, zip)) 27 ) 28 29 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 30 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments 31 Authors: Ilya Yaroshenko 32 33 Macros: 34 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 35 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 36 +/ 37 module mir.ndslice.iterator; 38 39 import mir.internal.utility: Iota; 40 import mir.math.common: optmath; 41 import mir.ndslice.field; 42 import mir.ndslice.internal; 43 import mir.ndslice.slice: SliceKind, Slice, Universal, Canonical, Contiguous, isSlice; 44 import mir.qualifier; 45 import mir.conv; 46 import std.traits; 47 48 private static immutable assumeZeroShiftExceptionMsg = "*.assumeFieldsHaveZeroShift: shift is not zero!"; 49 version(D_Exceptions) 50 private static immutable assumeZeroShiftException = new Exception(assumeZeroShiftExceptionMsg); 51 52 @optmath: 53 54 enum std_ops = q{ 55 void opUnary(string op)() scope 56 if (op == "--" || op == "++") 57 { mixin(op ~ "_iterator;"); } 58 59 void opOpAssign(string op)(ptrdiff_t index) scope 60 if (op == "-" || op == "+") 61 { mixin("_iterator " ~ op ~ "= index;"); } 62 63 auto opBinary(string op)(ptrdiff_t index) 64 if (op == "+" || op == "-") 65 { 66 auto ret = this; 67 mixin(`ret ` ~ op ~ `= index;`); 68 return ret; 69 } 70 71 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 72 { return this._iterator - right._iterator; } 73 74 bool opEquals()(scope ref const typeof(this) right) scope const 75 { return this._iterator == right._iterator; } 76 77 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 78 { 79 static if (isPointer!Iterator) 80 return this._iterator - right._iterator; 81 else 82 return this._iterator.opCmp(right._iterator); 83 } 84 }; 85 86 /++ 87 Step counter. 88 89 `IotaIterator` is used by $(SUBREF topology, iota). 90 +/ 91 struct IotaIterator(I) 92 if (isIntegral!I || isPointer!I) 93 { 94 @optmath: 95 96 /// 97 I _index; 98 99 static if (isPointer!I) 100 /// 101 auto lightConst()() const @property 102 { 103 static if (isIntegral!I) 104 return IotaIterator!I(_index); 105 else 106 return IotaIterator!(LightConstOf!I)(_index); 107 } 108 109 static if (isPointer!I) 110 /// 111 auto lightImmutable()() immutable @property 112 { 113 static if (isIntegral!I) 114 return IotaIterator!I(_index); 115 else 116 return IotaIterator!(LightImmutableOf!I)(_index); 117 } 118 119 pure: 120 121 I opUnary(string op : "*")() 122 { return _index; } 123 124 void opUnary(string op)() 125 if (op == "--" || op == "++") 126 { mixin(op ~ `_index;`); } 127 128 I opIndex()(ptrdiff_t index) const 129 { return cast(I)(_index + index); } 130 131 void opOpAssign(string op)(ptrdiff_t index) 132 if (op == `+` || op == `-`) 133 { mixin(`_index ` ~ op ~ `= index;`); } 134 135 auto opBinary(string op)(ptrdiff_t index) 136 if (op == "+" || op == "-") 137 { 138 auto ret = this; 139 mixin(`ret ` ~ op ~ `= index;`); 140 return ret; 141 } 142 143 ptrdiff_t opBinary(string op : "-")(const typeof(this) right) const 144 { return cast(ptrdiff_t)(this._index - right._index); } 145 146 bool opEquals()(const typeof(this) right) const 147 { return this._index == right._index; } 148 149 auto opCmp()(const typeof(this) right) const 150 { return this._index - right._index; } 151 } 152 153 /// 154 @safe pure nothrow @nogc version(mir_test) unittest 155 { 156 IotaIterator!int iota; 157 assert(*iota == 0); 158 159 // iteration 160 ++iota; 161 assert(*iota == 1); 162 163 assert(iota[2] == 3); 164 assert(iota[-1] == 0); 165 166 --iota; 167 assert(*iota == 0); 168 169 // opBinary 170 assert(*(iota + 2) == 2); 171 assert(*(iota - 3) == -3); 172 assert((iota - 3) - iota == -3); 173 174 // construction 175 assert(*IotaIterator!int(3) == 3); 176 assert(iota - 1 < iota); 177 } 178 179 /// 180 pure nothrow @nogc version(mir_test) unittest 181 { 182 int[32] data; 183 auto iota = IotaIterator!(int*)(data.ptr); 184 assert(*iota == data.ptr); 185 186 // iteration 187 ++iota; 188 assert(*iota == 1 + data.ptr); 189 190 assert(iota[2] == 3 + data.ptr); 191 assert(iota[-1] == 0 + data.ptr); 192 193 --iota; 194 assert(*iota == 0 + data.ptr); 195 196 // opBinary 197 assert(*(iota + 2) == 2 + data.ptr); 198 assert(*(iota - 3) == -3 + data.ptr); 199 assert((iota - 3) - iota == -3); 200 201 // construction 202 assert(*IotaIterator!(int*)(data.ptr) == data.ptr); 203 assert(iota - 1 < iota); 204 } 205 206 auto RetroIterator__map(Iterator, alias fun)(ref RetroIterator!Iterator it) 207 { 208 auto iterator = it._iterator._mapIterator!fun; 209 return RetroIterator!(typeof(iterator))(iterator); 210 } 211 212 version(mir_test) unittest 213 { 214 import mir.ndslice.topology; 215 import mir.ndslice.allocation; 216 auto v = iota(9).retro.map!(a => a).slice; 217 uint r; 218 auto w = iota(9).retro.map!(a => a).map!(a => a * r).slice; 219 } 220 221 /++ 222 Reverse directions for an iterator. 223 224 `RetroIterator` is used by $(SUBREF topology, retro). 225 +/ 226 struct RetroIterator(Iterator) 227 { 228 @optmath: 229 /// 230 Iterator _iterator; 231 232 /// 233 auto lightConst()() const @property 234 { 235 return RetroIterator!(LightConstOf!Iterator)(.lightConst(_iterator)); 236 } 237 238 /// 239 auto lightImmutable()() immutable @property 240 { 241 return RetroIterator!(LightImmutableOf!Iterator)(.lightImmutable(_iterator)); 242 } 243 244 /// 245 static alias __map(alias fun) = RetroIterator__map!(Iterator, fun); 246 247 auto ref opUnary(string op : "*")() 248 { return *_iterator; } 249 250 void opUnary(string op : "--")() 251 { ++_iterator; } 252 253 void opUnary(string op : "++")() pure 254 { --_iterator; } 255 256 auto ref opIndex()(ptrdiff_t index) 257 { return _iterator[-index]; } 258 259 void opOpAssign(string op : "-")(ptrdiff_t index) scope 260 { _iterator += index; } 261 262 void opOpAssign(string op : "+")(ptrdiff_t index) scope 263 { _iterator -= index; } 264 265 auto opBinary(string op)(ptrdiff_t index) 266 if (op == "+" || op == "-") 267 { 268 auto ret = this; 269 mixin(`ret ` ~ op ~ `= index;`); 270 return ret; 271 } 272 273 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 274 { return right._iterator - this._iterator; } 275 276 bool opEquals()(scope ref const typeof(this) right) scope const 277 { return right._iterator == this._iterator; } 278 279 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 280 { 281 static if (isPointer!Iterator) 282 return right._iterator - this._iterator; 283 else 284 return right._iterator.opCmp(this._iterator); 285 } 286 } 287 288 /// 289 @safe pure nothrow @nogc version(mir_test) unittest 290 { 291 IotaIterator!int iota; 292 RetroIterator!(IotaIterator!int) retro; 293 294 ++iota; 295 --retro; 296 assert(*retro == *iota); 297 298 --iota; 299 ++retro; 300 assert(*retro == *iota); 301 302 assert(retro[-7] == iota[7]); 303 304 iota += 100; 305 retro -= 100; 306 assert(*retro == *iota); 307 308 iota -= 100; 309 retro += 100; 310 assert(*retro == *iota); 311 312 assert(*(retro + 10) == *(iota - 10)); 313 314 assert(retro - 1 < retro); 315 316 assert((retro - 5) - retro == -5); 317 318 iota = IotaIterator!int(3); 319 retro = RetroIterator!(IotaIterator!int)(iota); 320 assert(*retro == *iota); 321 } 322 323 auto StrideIterator__map(Iterator, alias fun)(StrideIterator!Iterator it) 324 { 325 auto iterator = it._iterator._mapIterator!fun; 326 return StrideIterator!(typeof(iterator))(it._stride, iterator); 327 } 328 329 version(mir_test) unittest 330 { 331 import mir.ndslice.topology; 332 import mir.ndslice.allocation; 333 auto v = iota([3], 0, 3).map!(a => a).slice; 334 uint r; 335 auto w = iota([3], 0, 3).map!(a => a).map!(a => a * r).slice; 336 } 337 338 /++ 339 Iterates an iterator with a fixed strides. 340 341 `StrideIterator` is used by $(SUBREF topology, stride). 342 +/ 343 struct StrideIterator(Iterator) 344 { 345 @optmath: 346 /// 347 ptrdiff_t _stride; 348 /// 349 Iterator _iterator; 350 351 /// 352 auto lightConst()() const @property 353 { 354 return StrideIterator!(LightConstOf!Iterator)(_stride, .lightConst(_iterator)); 355 } 356 357 /// 358 auto lightImmutable()() immutable @property 359 { 360 return StrideIterator!(LightImmutableOf!Iterator)(_stride, .lightImmutable(_iterator)); 361 } 362 363 /// 364 static alias __map(alias fun) = StrideIterator__map!(Iterator, fun); 365 366 auto ref opUnary(string op : "*")() 367 { return *_iterator; } 368 369 void opUnary(string op)() scope 370 if (op == "--" || op == "++") 371 { mixin("_iterator " ~ op[0] ~ "= _stride;"); } 372 373 auto ref opIndex()(ptrdiff_t index) 374 { return _iterator[index * _stride]; } 375 376 void opOpAssign(string op)(ptrdiff_t index) scope 377 if (op == "-" || op == "+") 378 { mixin("_iterator " ~ op ~ "= index * _stride;"); } 379 380 auto opBinary(string op)(ptrdiff_t index) 381 if (op == "+" || op == "-") 382 { 383 auto ret = this; 384 mixin(`ret ` ~ op ~ `= index;`); 385 return ret; 386 } 387 388 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 389 { return (this._iterator - right._iterator) / _stride; } 390 391 bool opEquals()(scope ref const typeof(this) right) scope const 392 { return this._iterator == right._iterator; } 393 394 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 395 { 396 static if (isPointer!Iterator) 397 ptrdiff_t ret = this._iterator - right._iterator; 398 else 399 ptrdiff_t ret = this._iterator.opCmp(right._iterator); 400 return _stride >= 0 ? ret : -ret; 401 } 402 } 403 404 /// 405 @safe pure nothrow @nogc version(mir_test) unittest 406 { 407 IotaIterator!int iota; 408 StrideIterator!(IotaIterator!int) stride; 409 stride._stride = -3; 410 411 iota -= stride._stride; 412 --stride; 413 assert(*stride == *iota); 414 415 iota += stride._stride; 416 ++stride; 417 assert(*stride == *iota); 418 419 assert(stride[7] == iota[7 * stride._stride]); 420 421 iota -= 100 * stride._stride; 422 stride -= 100; 423 assert(*stride == *iota); 424 425 iota += 100 * stride._stride; 426 stride += 100; 427 assert(*stride == *iota); 428 429 assert(*(stride + 10) == *(iota + 10 * stride._stride)); 430 431 assert(stride - 1 < stride); 432 433 assert((stride - 5) - stride == -5); 434 435 iota = IotaIterator!int(3); 436 stride = StrideIterator!(IotaIterator!int)(3, iota); 437 assert(*stride == *iota); 438 } 439 440 auto StrideIterator__map(Iterator, size_t factor, alias fun)(StrideIterator!(Iterator, factor) it) 441 { 442 auto iterator = it._iterator._mapIterator!fun; 443 return StrideIterator!(typeof(iterator), factor)(iterator); 444 } 445 446 /++ 447 Iterates an iterator with a fixed strides. 448 449 `StrideIterator` is used by $(SUBREF topology, stride). 450 +/ 451 struct StrideIterator(Iterator, ptrdiff_t factor) 452 { 453 @optmath: 454 /// 455 enum _stride = factor; 456 457 /// 458 Iterator _iterator; 459 460 /// 461 auto lightConst()() const @property 462 { 463 return StrideIterator!(LightConstOf!Iterator, _stride)(.lightConst(_iterator)); 464 } 465 466 /// 467 auto lightImmutable()() immutable @property 468 { 469 return StrideIterator!(LightImmutableOf!Iterator, _stride)(.lightImmutable(_iterator)); 470 } 471 472 /// 473 static alias __map(alias fun) = StrideIterator__map!(Iterator, _stride, fun); 474 475 auto ref opUnary(string op : "*")() 476 { return *_iterator; } 477 478 void opUnary(string op)() scope 479 if (op == "--" || op == "++") 480 { mixin("_iterator " ~ op[0] ~ "= _stride;"); } 481 482 auto ref opIndex()(ptrdiff_t index) 483 { return _iterator[index * _stride]; } 484 485 void opOpAssign(string op)(ptrdiff_t index) scope 486 if (op == "-" || op == "+") 487 { mixin("_iterator " ~ op ~ "= index * _stride;"); } 488 489 auto opBinary(string op)(ptrdiff_t index) 490 if (op == "+" || op == "-") 491 { 492 auto ret = this; 493 mixin(`ret ` ~ op ~ `= index;`); 494 return ret; 495 } 496 497 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 498 { return (this._iterator - right._iterator) / _stride; } 499 500 bool opEquals()(scope ref const typeof(this) right) scope const 501 { return this._iterator == right._iterator; } 502 503 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 504 { 505 static if (isPointer!Iterator) 506 ptrdiff_t ret = this._iterator - right._iterator; 507 else 508 ptrdiff_t ret = this._iterator.opCmp(right._iterator); 509 return _stride >= 0 ? ret : -ret; 510 } 511 } 512 513 /// 514 @safe pure nothrow @nogc version(mir_test) unittest 515 { 516 IotaIterator!int iota; 517 StrideIterator!(IotaIterator!int, -3) stride; 518 519 iota -= stride._stride; 520 --stride; 521 assert(*stride == *iota); 522 523 iota += stride._stride; 524 ++stride; 525 assert(*stride == *iota); 526 527 assert(stride[7] == iota[7 * stride._stride]); 528 529 iota -= 100 * stride._stride; 530 stride -= 100; 531 assert(*stride == *iota); 532 533 iota += 100 * stride._stride; 534 stride += 100; 535 assert(*stride == *iota); 536 537 assert(*(stride + 10) == *(iota + 10 * stride._stride)); 538 539 assert(stride - 1 < stride); 540 541 assert((stride - 5) - stride == -5); 542 } 543 544 package template _zip_types(Iterators...) 545 { 546 alias AliasSeq(T...) = T; 547 static if (Iterators.length) 548 { 549 enum i = Iterators.length - 1; 550 alias T = typeof(Iterators[i].init[sizediff_t.init]); 551 static if (__traits(compiles, &Iterators[i].init[sizediff_t.init])) 552 { 553 import mir.functional: Ref; 554 alias _zip_types = AliasSeq!(_zip_types!(Iterators[0 .. i]), Ref!T); 555 } 556 else 557 alias _zip_types = AliasSeq!(_zip_types!(Iterators[0 .. i]), T); 558 } 559 else 560 alias _zip_types = AliasSeq!(); 561 } 562 563 package template _zip_fronts(Iterators...) 564 { 565 static if (Iterators.length) 566 { 567 enum i = Iterators.length - 1; 568 static if (__traits(compiles, &Iterators[i].init[sizediff_t.init])) 569 enum _zip_fronts = _zip_fronts!(Iterators[0 .. i]) ~ "_ref(*_iterators[" ~ i.stringof ~ "]), "; 570 else 571 enum _zip_fronts = _zip_fronts!(Iterators[0 .. i]) ~ "*_iterators[" ~ i.stringof ~ "], "; 572 } 573 else 574 enum _zip_fronts = ""; 575 } 576 577 package template _zip_index(Iterators...) 578 { 579 static if (Iterators.length) 580 { 581 enum i = Iterators.length - 1; 582 static if (__traits(compiles, &Iterators[i].init[sizediff_t.init])) 583 enum _zip_index = _zip_index!(Iterators[0 .. i]) ~ "_ref(_iterators[" ~ i.stringof ~ "][index]), "; 584 else 585 enum _zip_index = _zip_index!(Iterators[0 .. i]) ~ "_iterators[" ~ i.stringof ~ "][index], "; 586 } 587 else 588 enum _zip_index = ""; 589 } 590 591 /++ 592 Iterates multiple iterators in lockstep. 593 594 `ZipIterator` is used by $(SUBREF topology, zip). 595 +/ 596 struct ZipIterator(Iterators...) 597 if (Iterators.length > 1) 598 { 599 @optmath: 600 import std.traits: ConstOf, ImmutableOf; 601 import std.meta: staticMap; 602 import mir.functional: RefTuple, Ref, _ref; 603 /// 604 Iterators _iterators; 605 606 /// 607 auto lightConst()() const @property 608 { 609 import std.format; 610 import mir.ndslice.topology: iota; 611 import std.meta: staticMap; 612 alias Ret = ZipIterator!(staticMap!(LightConstOf, Iterators)); 613 enum ret = "Ret(%(.lightConst(_iterators[%s]),%)]))".format(_iterators.length.iota); 614 return mixin(ret); 615 } 616 617 /// 618 auto lightImmutable()() immutable @property 619 { 620 import std.format; 621 import mir.ndslice.topology: iota; 622 import std.meta: staticMap; 623 alias Ret = ZipIterator!(staticMap!(LightImmutableOf, Iterators)); 624 enum ret = "Ret(%(.lightImmutable(_iterators[%s]),%)]))".format(_iterators.length.iota); 625 return mixin(ret); 626 } 627 628 auto opUnary(string op : "*")() 629 { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); } 630 631 632 auto opUnary(string op : "*")() const 633 { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); } 634 635 auto opUnary(string op : "*")() immutable 636 { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); } 637 638 void opUnary(string op)() scope 639 if (op == "++" || op == "--") 640 { 641 foreach (ref _iterator; _iterators) 642 mixin(op ~ `_iterator;`); 643 } 644 645 auto opIndex()(ptrdiff_t index) 646 { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_index!Iterators ~ ")"); } 647 648 auto opIndexAssign(Types...)(RefTuple!(Types) value, ptrdiff_t index) 649 if (Types.length == Iterators.length) 650 { 651 foreach(i, ref val; value.expand) 652 { 653 import mir.functional: unref; 654 _iterators[i][index] = unref(val); 655 } 656 return opIndex(index); 657 } 658 659 void opOpAssign(string op)(ptrdiff_t index) scope 660 if (op == "+" || op == "-") 661 { 662 foreach (ref _iterator; _iterators) 663 mixin(`_iterator ` ~ op ~ `= index;`); 664 } 665 666 auto opBinary(string op)(ptrdiff_t index) 667 if (op == "+" || op == "-") 668 { 669 auto ret = this; 670 mixin(`ret ` ~ op ~ `= index;`); 671 return ret; 672 } 673 674 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 675 { return this._iterators[0] - right._iterators[0]; } 676 677 bool opEquals()(scope ref const typeof(this) right) scope const 678 { return this._iterators[0] == right._iterators[0]; } 679 680 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 681 { 682 static if (isPointer!(Iterators[0])) 683 return this._iterators[0] - right._iterators[0]; 684 else 685 return this._iterators[0].opCmp(right._iterators[0]); 686 } 687 688 import std.meta: anySatisfy; 689 static if (anySatisfy!(hasZeroShiftFieldMember, Iterators)) 690 /// Defined if at least one of `Iterators` has member `assumeFieldsHaveZeroShift`. 691 auto assumeFieldsHaveZeroShift() @property 692 { 693 import std.meta: staticMap; 694 alias _fields = _iterators; 695 return mixin("ZipField!(staticMap!(ZeroShiftField, Iterators))(" ~ applyAssumeZeroShift!Iterators ~ ")"); 696 } 697 } 698 699 /// 700 pure nothrow @nogc version(mir_test) unittest 701 { 702 import mir.ndslice.traits: isIterator; 703 704 double[10] data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 705 alias ItA = IotaIterator!int; 706 alias ItB = double*; 707 alias ItZ = ZipIterator!(ItA, ItB); 708 auto zip = ItZ(ItA(3), data.ptr); 709 assert((*zip).a == 3); 710 assert((*zip).b == 1); 711 712 // iteration 713 ++zip; 714 assert((*zip).a == 3 + 1); 715 assert((*zip).b == 1 + 1); 716 assert(&(*zip).b() == data.ptr + 1); 717 718 assert(zip[4].a == 3 + 5); 719 assert(zip[4].b == 1 + 5); 720 assert(&zip[4].b() == data.ptr + 5); 721 722 --zip; 723 assert((*zip).a == 3); 724 assert((*zip).b == 1); 725 726 assert((*(zip + 2)).a == 3 + 2); 727 assert((*(zip - 3)).a == 3 + -3); 728 assert((*(zip + 2)).b == 1 + 2); 729 assert((*(zip + 3 - 3)).b == 1); 730 assert((zip - 3).opBinary!"-"(zip) == -3); 731 732 assert(zip == zip); 733 assert(zip - 1 < zip); 734 735 static assert(isIterator!(ZipIterator!(double*, int*))); 736 static assert(isIterator!(ZipIterator!(immutable(double)*, immutable(int)*))); 737 } 738 739 /// 740 struct CachedIterator(Iterator, CacheIterator, FlagIterator) 741 { 742 /// 743 Iterator _iterator; 744 /// 745 CacheIterator _caches; 746 /// 747 FlagIterator _flags; 748 749 @optmath: 750 751 /// 752 auto lightScope()() scope @property 753 { 754 return CachedIterator!(LightScopeOf!Iterator, LightScopeOf!CacheIterator, LightScopeOf!FlagIterator)( 755 .lightScope(_iterator), 756 .lightScope(_caches), 757 .lightScope(_flags), 758 ); 759 } 760 761 /// 762 auto lightScope()() scope const @property 763 { 764 return lightConst.lightScope; 765 } 766 767 /// 768 auto lightScope()() scope immutable @property 769 { 770 return lightImmutable.lightScope; 771 } 772 773 /// 774 auto lightConst()() const @property 775 { 776 return CachedIterator!(LightConstOf!Iterator, CacheIterator, FlagIterator)( 777 .lightConst(_iterator), 778 *cast(CacheIterator*)&_caches, 779 *cast(FlagIterator*)&_flags, 780 ); 781 } 782 783 /// 784 auto lightImmutable()() immutable @property @trusted 785 { 786 return CachedIterator!(LightImmutableOf!Iterator, CacheIterator, FlagIterator)( 787 .lightImmutable(_iterator), 788 *cast(CacheIterator*)&_caches, 789 *cast(FlagIterator*)&_flags, 790 ); 791 } 792 793 private alias T = typeof(Iterator.init[0]); 794 private alias UT = Unqual!T; 795 796 auto opUnary(string op : "*")() 797 { 798 if (_expect(!*_flags, false)) 799 { 800 _flags[0] = true; 801 emplaceRef!T(*cast(UT*)&*_caches, *_iterator); 802 } 803 return *_caches; 804 } 805 806 auto opIndex()(ptrdiff_t index) 807 { 808 if (_expect(!_flags[index], false)) 809 { 810 _flags[index] = true; 811 emplaceRef!T(*cast(UT*)&(_caches[index]), _iterator[index]); 812 } 813 return _caches[index]; 814 } 815 816 auto ref opIndexAssign(T)(auto ref T val, ptrdiff_t index) 817 { 818 _flags[index] = true; 819 return _caches[index] = val; 820 } 821 822 void opUnary(string op)() scope 823 if (op == "--" || op == "++") 824 { 825 mixin(op ~ "_iterator;"); 826 mixin(op ~ "_caches;"); 827 mixin(op ~ "_flags;"); 828 } 829 830 void opOpAssign(string op)(ptrdiff_t index) scope 831 if (op == "-" || op == "+") 832 { 833 mixin("_iterator" ~ op ~ "= index;"); 834 mixin("_caches" ~ op ~ "= index;"); 835 mixin("_flags" ~ op ~ "= index;"); 836 } 837 838 auto opBinary(string op)(ptrdiff_t index) 839 if (op == "+" || op == "-") 840 { 841 auto ret = this; 842 mixin(`ret ` ~ op ~ `= index;`); 843 return ret; 844 } 845 846 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 847 { return this._iterator - right._iterator; } 848 849 bool opEquals()(scope ref const typeof(this) right) scope const 850 { return this._iterator == right._iterator; } 851 852 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 853 { 854 static if (isPointer!Iterator) 855 return this._iterator - right._iterator; 856 else 857 return this._iterator.opCmp(right._iterator); 858 } 859 } 860 861 private enum map_primitives = q{ 862 863 import mir.functional: RefTuple, unref; 864 865 auto ref opUnary(string op : "*")() 866 { 867 static if (is(typeof(*_iterator) : RefTuple!T, T...)) 868 { 869 auto t = *_iterator; 870 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 871 } 872 else 873 return _fun(*_iterator); 874 } 875 876 auto ref opIndex(ptrdiff_t index) scope 877 { 878 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 879 { 880 auto t = _iterator[index]; 881 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 882 } 883 else 884 return _fun(_iterator[index]); 885 } 886 887 static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) 888 { 889 auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope 890 { 891 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 892 { 893 auto t = _iterator[index]; 894 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value"); 895 } 896 else 897 return _fun(_iterator[index]) = value; 898 } 899 900 auto ref opIndexUnary(string op)(ptrdiff_t index) 901 { 902 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 903 { 904 auto t = _iterator[index]; 905 return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 906 } 907 else 908 return mixin(op ~ "_fun(_iterator[index])"); 909 } 910 911 auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) 912 { 913 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 914 { 915 auto t = _iterator[index]; 916 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value"); 917 } 918 else 919 return mixin("_fun(_iterator[index])" ~ op ~ "= value"); 920 } 921 } 922 }; 923 924 /++ 925 `VmapIterator` is used by $(SUBREF topology, map). 926 +/ 927 struct VmapIterator(Iterator, Fun) 928 { 929 @optmath: 930 931 /// 932 Iterator _iterator; 933 /// 934 Fun _fun; 935 936 /// 937 auto lightConst()() const @property 938 { 939 return VmapIterator!(LightConstOf!Iterator, LightConstOf!Fun)(.lightConst(_iterator), .lightConst(_fun)); 940 } 941 942 /// 943 auto lightImmutable()() immutable @property 944 { 945 return VmapIterator!(LightImmutableOf!Iterator, LightImmutableOf!Fun)(.lightImmutable(_iterator), .lightImmutable(_fun)); 946 } 947 948 import mir.functional: RefTuple, unref; 949 950 auto ref opUnary(string op : "*")() 951 { 952 static if (is(typeof(*_iterator) : RefTuple!T, T...)) 953 { 954 auto t = *_iterator; 955 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 956 } 957 else 958 return _fun(*_iterator); 959 } 960 961 auto ref opIndex(ptrdiff_t index) scope 962 { 963 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 964 { 965 auto t = _iterator[index]; 966 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 967 } 968 else 969 return _fun(_iterator[index]); 970 } 971 972 static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) 973 { 974 auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope 975 { 976 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 977 { 978 auto t = _iterator[index]; 979 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value"); 980 } 981 else 982 return _fun(_iterator[index]) = value; 983 } 984 985 auto ref opIndexUnary(string op)(ptrdiff_t index) 986 { 987 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 988 { 989 auto t = _iterator[index]; 990 return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 991 } 992 else 993 return mixin(op ~ "_fun(_iterator[index])"); 994 } 995 996 auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) 997 { 998 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 999 { 1000 auto t = _iterator[index]; 1001 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value"); 1002 } 1003 else 1004 return mixin("_fun(_iterator[index])" ~ op ~ "= value"); 1005 } 1006 } 1007 1008 mixin(std_ops); 1009 1010 static if (hasZeroShiftFieldMember!Iterator) 1011 /// 1012 auto assumeFieldsHaveZeroShift() @property 1013 { 1014 return _vmapField(_iterator.assumeFieldsHaveZeroShift, _fun); 1015 } 1016 } 1017 1018 auto MapIterator__map(Iterator, alias fun0, alias fun)(ref MapIterator!(Iterator, fun0) it) 1019 { 1020 return MapIterator!(Iterator, fun)(it._iterator); 1021 } 1022 1023 /++ 1024 `MapIterator` is used by $(SUBREF topology, map). 1025 +/ 1026 struct MapIterator(Iterator, alias _fun) 1027 { 1028 @optmath: 1029 /// 1030 Iterator _iterator; 1031 1032 /// 1033 auto lightConst()() const @property 1034 { 1035 return MapIterator!(LightConstOf!Iterator, _fun)(.lightConst(_iterator)); 1036 } 1037 1038 /// 1039 auto lightImmutable()() immutable @property 1040 { 1041 return MapIterator!(LightImmutableOf!Iterator, _fun)(.lightImmutable(_iterator)); 1042 } 1043 1044 import mir.functional: pipe; 1045 /// 1046 static alias __map(alias fun1) = MapIterator__map!(Iterator, _fun, pipe!(_fun, fun1)); 1047 1048 import mir.functional: RefTuple, unref; 1049 1050 auto ref opUnary(string op : "*")() 1051 { 1052 static if (is(typeof(*_iterator) : RefTuple!T, T...)) 1053 { 1054 auto t = *_iterator; 1055 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 1056 } 1057 else 1058 return _fun(*_iterator); 1059 } 1060 1061 auto ref opIndex(ptrdiff_t index) scope 1062 { 1063 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1064 { 1065 auto t = _iterator[index]; 1066 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 1067 } 1068 else 1069 return _fun(_iterator[index]); 1070 } 1071 1072 static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) 1073 { 1074 auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope 1075 { 1076 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1077 { 1078 auto t = _iterator[index]; 1079 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value"); 1080 } 1081 else 1082 return _fun(_iterator[index]) = value; 1083 } 1084 1085 auto ref opIndexUnary(string op)(ptrdiff_t index) 1086 { 1087 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1088 { 1089 auto t = _iterator[index]; 1090 return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); 1091 } 1092 else 1093 return mixin(op ~ "_fun(_iterator[index])"); 1094 } 1095 1096 auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) 1097 { 1098 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1099 { 1100 auto t = _iterator[index]; 1101 return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value"); 1102 } 1103 else 1104 return mixin("_fun(_iterator[index])" ~ op ~ "= value"); 1105 } 1106 } 1107 1108 mixin(std_ops); 1109 1110 static if (hasZeroShiftFieldMember!Iterator) 1111 /// 1112 auto assumeFieldsHaveZeroShift() @property 1113 { 1114 return _mapField!_fun(_iterator.assumeFieldsHaveZeroShift); 1115 } 1116 } 1117 1118 /+ 1119 Creates a mapped iterator. Uses `__map` if possible. 1120 +/ 1121 auto _mapIterator(alias fun, Iterator)(Iterator iterator) 1122 { 1123 import core.lifetime: move; 1124 static if (__traits(hasMember, Iterator, "__map")) 1125 { 1126 static if (is(Iterator : MapIterator!(Iter0, fun0), Iter0, alias fun0) 1127 && !__traits(compiles, Iterator.__map!fun(iterator))) 1128 { 1129 // https://github.com/libmir/mir-algorithm/issues/111 1130 debug(mir) pragma(msg, __FUNCTION__~" not coalescing chained map calls into a single lambda, possibly because of multiple embedded context pointers"); 1131 return MapIterator!(Iterator, fun)(move(iterator)); 1132 } 1133 else 1134 return Iterator.__map!fun(iterator); 1135 } 1136 else 1137 return MapIterator!(Iterator, fun)(move(iterator)); 1138 } 1139 1140 1141 /+ 1142 Creates a mapped iterator. Uses `__vmap` if possible. 1143 +/ 1144 auto _vmapIterator(Iterator, Fun)(Iterator iterator, Fun fun) 1145 { 1146 static if (__traits(hasMember, Iterator, "__vmap")) 1147 return Iterator.__vmap(iterator, fun); 1148 else 1149 return MapIterator!(Iterator, fun)(iterator); 1150 } 1151 1152 @safe pure nothrow @nogc version(mir_test) unittest 1153 { 1154 // https://github.com/libmir/mir-algorithm/issues/111 1155 import mir.ndslice.topology : iota, map; 1156 import mir.functional : pipe; 1157 1158 static auto foo(T)(T x) 1159 { 1160 return x.map!(a => a + 1); 1161 } 1162 1163 static auto bar(T)(T x) 1164 { 1165 return foo(x).map!(a => a + 2); 1166 } 1167 1168 auto data = iota(5); 1169 auto result = iota([5], 3); 1170 1171 auto x = data.map!(a => a + 1).map!(a => a + 2); 1172 assert(x == result); 1173 1174 auto y = bar(data); 1175 assert(y == result); 1176 } 1177 1178 /++ 1179 `NeighboursIterator` is used by $(SUBREF topology, map). 1180 +/ 1181 struct NeighboursIterator(Iterator, size_t N, alias _fun, bool around) 1182 { 1183 import std.meta: AliasSeq; 1184 @optmath: 1185 /// 1186 Iterator _iterator; 1187 static if (N) 1188 Iterator[2][N] _neighbours; 1189 else alias _neighbours = AliasSeq!(); 1190 1191 /// 1192 auto lightConst()() const @property 1193 { 1194 LightConstOf!Iterator[2][N] neighbours; 1195 foreach (i; 0 .. N) 1196 { 1197 neighbours[i][0] = .lightConst(_neighbours[i][0]); 1198 neighbours[i][1] = .lightConst(_neighbours[i][1]); 1199 } 1200 return NeighboursIterator!(LightConstOf!Iterator, N, _fun, around)(.lightConst(_iterator), neighbours); 1201 } 1202 1203 /// 1204 auto lightImmutable()() immutable @property 1205 { 1206 LightImmutableOf!Iterator[2][N] neighbours; 1207 foreach (i; 0 .. N) 1208 { 1209 neighbours[i][0] = .lightImmutable(_neighbours[i][0]); 1210 neighbours[i][1] = .lightImmutable(_neighbours[i][1]); 1211 } 1212 return NeighboursIterator!(LightImmutableOf!Iterator, N, _fun, around)(.lightImmutable(_iterator), neighbours); 1213 } 1214 1215 import mir.functional: RefTuple, _ref; 1216 1217 private alias RA = Unqual!(typeof(_fun(_iterator[-1], _iterator[+1]))); 1218 private alias Result = RefTuple!(_zip_types!Iterator, RA); 1219 1220 auto ref opUnary(string op : "*")() 1221 { 1222 return opIndex(0); 1223 } 1224 1225 auto ref opIndex(ptrdiff_t index) scope 1226 { 1227 static if (around) 1228 RA result = _fun(_iterator[index - 1], _iterator[index + 1]); 1229 1230 foreach (i; Iota!N) 1231 { 1232 static if (i == 0 && !around) 1233 RA result = _fun(_neighbours[i][0][index], _neighbours[i][1][index]); 1234 else 1235 result = _fun(result, _fun(_neighbours[i][0][index], _neighbours[i][1][index])); 1236 } 1237 static if (__traits(compiles, &_iterator[index])) 1238 return Result(_ref(_iterator[index]), result); 1239 else 1240 return Result(_iterator[index], result); 1241 } 1242 1243 void opUnary(string op)() scope 1244 if (op == "--" || op == "++") 1245 { 1246 mixin(op ~ "_iterator;"); 1247 foreach (i; Iota!N) 1248 { 1249 mixin(op ~ "_neighbours[i][0];"); 1250 mixin(op ~ "_neighbours[i][1];"); 1251 } 1252 } 1253 1254 void opOpAssign(string op)(ptrdiff_t index) scope 1255 if (op == "-" || op == "+") 1256 { 1257 1258 mixin("_iterator " ~ op ~ "= index;"); 1259 foreach (i; Iota!N) 1260 { 1261 mixin("_neighbours[i][0] " ~ op ~ "= index;"); 1262 mixin("_neighbours[i][1] " ~ op ~ "= index;"); 1263 } 1264 } 1265 1266 auto opBinary(string op)(ptrdiff_t index) 1267 if (op == "+" || op == "-") 1268 { 1269 auto ret = this; 1270 mixin(`ret ` ~ op ~ `= index;`); 1271 return ret; 1272 } 1273 1274 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 1275 { return this._iterator - right._iterator; } 1276 1277 bool opEquals()(scope ref const typeof(this) right) scope const 1278 { return this._iterator == right._iterator; } 1279 1280 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 1281 { 1282 static if (isPointer!Iterator) 1283 return this._iterator - right._iterator; 1284 else 1285 return this._iterator.opCmp(right._iterator); 1286 } 1287 } 1288 1289 /++ 1290 `MemberIterator` is used by $(SUBREF topology, member). 1291 +/ 1292 struct MemberIterator(Iterator, string member) 1293 { 1294 @optmath: 1295 /// 1296 Iterator _iterator; 1297 1298 /// 1299 auto lightConst()() const @property 1300 { 1301 return MemberIterator!(LightConstOf!Iterator, member)(.lightConst(_iterator)); 1302 } 1303 1304 /// 1305 auto lightImmutable()() immutable @property 1306 { 1307 return MemberIterator!(LightImmutableOf!Iterator, member)(.lightImmutable(_iterator)); 1308 } 1309 1310 auto ref opUnary(string op : "*")() 1311 { 1312 return __traits(getMember, *_iterator, member); 1313 } 1314 1315 auto ref opIndex()(ptrdiff_t index) 1316 { 1317 return __traits(getMember, _iterator[index], member); 1318 } 1319 1320 static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) 1321 { 1322 auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope 1323 { 1324 return __traits(getMember, _iterator[index], member) = value; 1325 } 1326 1327 auto ref opIndexUnary(string op)(ptrdiff_t index) 1328 { 1329 return mixin(op ~ "__traits(getMember, _iterator[index], member)"); 1330 } 1331 1332 auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) 1333 { 1334 return mixin("__traits(getMember, _iterator[index], member)" ~ op ~ "= value"); 1335 } 1336 } 1337 1338 mixin(std_ops); 1339 } 1340 1341 /++ 1342 `BytegroupIterator` is used by $(SUBREF topology, Bytegroup) and $(SUBREF topology, bytegroup). 1343 +/ 1344 struct BytegroupIterator(Iterator, size_t count, DestinationType) 1345 if (count) 1346 { 1347 @optmath: 1348 /// 1349 Iterator _iterator; 1350 1351 /// 1352 auto lightConst()() const @property 1353 { 1354 return BytegroupIterator!(LightConstOf!Iterator, count, DestinationType)(.lightConst(_iterator)); 1355 } 1356 1357 /// 1358 auto lightImmutable()() immutable @property 1359 { 1360 return BytegroupIterator!(LightImmutableOf!Iterator, count, DestinationType)(.lightImmutable(_iterator)); 1361 } 1362 1363 package(mir) alias Byte = Unqual!(typeof(_iterator[0])); 1364 1365 version(LittleEndian) 1366 private enum BE = false; 1367 else 1368 private enum BE = true; 1369 1370 private union U 1371 { 1372 DestinationType value; 1373 static if (DestinationType.sizeof > Byte[count].sizeof && BE && isScalarType!DestinationType) 1374 { 1375 struct 1376 { 1377 ubyte[DestinationType.sizeof - Byte[count].sizeof] shiftPayload; 1378 Byte[count] bytes; 1379 } 1380 } 1381 else 1382 { 1383 Byte[count] bytes; 1384 } 1385 } 1386 1387 DestinationType opUnary(string op : "*")() 1388 { 1389 U ret = { value: DestinationType.init }; 1390 foreach (i; Iota!count) 1391 ret.bytes[i] = _iterator[i]; 1392 return ret.value; 1393 } 1394 1395 DestinationType opIndex()(ptrdiff_t index) 1396 { 1397 return *(this + index); 1398 } 1399 1400 DestinationType opIndexAssign(T)(T val, ptrdiff_t index) scope 1401 { 1402 auto it = this + index; 1403 U ret = { value: val }; 1404 foreach (i; Iota!count) 1405 it._iterator[i] = ret.bytes[i]; 1406 return ret.value; 1407 } 1408 1409 void opUnary(string op)() scope 1410 if (op == "--" || op == "++") 1411 { mixin("_iterator " ~ op[0] ~ "= count;"); } 1412 1413 void opOpAssign(string op)(ptrdiff_t index) scope 1414 if (op == "-" || op == "+") 1415 { mixin("_iterator " ~ op ~ "= index * count;"); } 1416 1417 auto opBinary(string op)(ptrdiff_t index) 1418 if (op == "+" || op == "-") 1419 { 1420 auto ret = this; 1421 mixin(`ret ` ~ op ~ `= index;`); 1422 return ret; 1423 } 1424 1425 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 1426 { return (this._iterator - right._iterator) / count; } 1427 1428 bool opEquals()(scope ref const typeof(this) right) scope const 1429 { return this._iterator == right._iterator; } 1430 1431 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 1432 { 1433 static if (isPointer!Iterator) 1434 return this._iterator - right._iterator; 1435 else 1436 return this._iterator.opCmp(right._iterator); 1437 } 1438 } 1439 1440 auto SlideIterator__map(Iterator, size_t params, alias fun0, alias fun)(SlideIterator!(Iterator, params, fun0) it) 1441 { 1442 return SlideIterator!(Iterator, params, fun)(it._iterator); 1443 } 1444 1445 /++ 1446 `SlideIterator` is used by $(SUBREF topology, diff) and $(SUBREF topology, slide). 1447 +/ 1448 struct SlideIterator(Iterator, size_t params, alias fun) 1449 if (params > 1) 1450 { 1451 @optmath: 1452 /// 1453 Iterator _iterator; 1454 1455 /// 1456 auto lightConst()() const @property 1457 { 1458 return SlideIterator!(LightConstOf!Iterator, params, fun)(.lightConst(_iterator)); 1459 } 1460 1461 /// 1462 auto lightImmutable()() immutable @property 1463 { 1464 return SlideIterator!(LightImmutableOf!Iterator, params, fun)(.lightImmutable(_iterator)); 1465 } 1466 1467 import mir.functional: pipe; 1468 /// 1469 static alias __map(alias fun1) = SlideIterator__map!(Iterator, params, fun, pipe!(fun, fun1)); 1470 1471 auto ref opUnary(string op : "*")() 1472 { 1473 return mixin("fun(" ~ _iotaArgs!(params, "_iterator[", "], ") ~ ")"); 1474 } 1475 1476 auto ref opIndex()(ptrdiff_t index) 1477 { 1478 return mixin("fun(" ~ _iotaArgs!(params, "_iterator[index + ", "], ") ~ ")"); 1479 } 1480 1481 mixin(std_ops); 1482 } 1483 1484 /// 1485 version(mir_test) unittest 1486 { 1487 import mir.functional: naryFun; 1488 auto data = [1, 3, 8, 18]; 1489 auto diff = SlideIterator!(int*, 2, naryFun!"b - a")(data.ptr); 1490 assert(*diff == 2); 1491 assert(diff[1] == 5); 1492 assert(diff[2] == 10); 1493 } 1494 1495 auto IndexIterator__map(Iterator, Field, alias fun)(ref IndexIterator!(Iterator, Field) it) 1496 { 1497 auto field = it._field._mapField!fun; 1498 return IndexIterator!(Iterator, typeof(field))(it._iterator, field); 1499 } 1500 1501 version(mir_test) unittest 1502 { 1503 import mir.ndslice.topology; 1504 import mir.ndslice.allocation; 1505 import mir.ndslice.slice; 1506 auto indices = [4, 3, 1, 2, 0, 4].sliced; 1507 auto v = iota(5).indexed(indices).map!(a => a).slice; 1508 uint r; 1509 auto w = iota(5).indexed(indices).map!(a => a).map!(a => a * r).slice; 1510 } 1511 1512 /++ 1513 Iterates a field using an iterator. 1514 1515 `IndexIterator` is used by $(SUBREF topology, indexed). 1516 +/ 1517 struct IndexIterator(Iterator, Field) 1518 { 1519 import mir.functional: RefTuple, unref; 1520 1521 @optmath: 1522 /// 1523 Iterator _iterator; 1524 /// 1525 Field _field; 1526 1527 /// 1528 auto lightConst()() const @property 1529 { 1530 return IndexIterator!(LightConstOf!Iterator, LightConstOf!Field)(.lightConst(_iterator), .lightConst(_field)); 1531 } 1532 1533 /// 1534 auto lightImmutable()() immutable @property 1535 { 1536 return IndexIterator!(LightImmutableOf!Iterator, LightImmutableOf!Field)(.lightImmutable(_iterator), _field.lightImmutable); 1537 } 1538 1539 /// 1540 static alias __map(alias fun) = IndexIterator__map!(Iterator, Field, fun); 1541 1542 auto ref opUnary(string op : "*")() 1543 { 1544 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1545 { 1546 auto t = *_iterator; 1547 return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]"); 1548 } 1549 else 1550 return _field[*_iterator]; 1551 } 1552 1553 auto ref opIndex()(ptrdiff_t index) 1554 { 1555 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1556 { 1557 auto t = _iterator[index]; 1558 return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]"); 1559 } 1560 else 1561 return _field[_iterator[index]]; 1562 } 1563 1564 static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) 1565 { 1566 auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope 1567 { 1568 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1569 { 1570 auto t = _iterator[index]; 1571 return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "] = value"); 1572 } 1573 else 1574 return _field[_iterator[index]] = value; 1575 } 1576 1577 auto ref opIndexUnary(string op)(ptrdiff_t index) 1578 { 1579 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1580 { 1581 auto t = _iterator[index]; 1582 return mixin(op ~ "_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]"); 1583 } 1584 else 1585 return mixin(op ~ "_field[_iterator[index]]"); 1586 } 1587 1588 auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) 1589 { 1590 static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) 1591 { 1592 auto t = _iterator[index]; 1593 return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]" ~ op ~ "= value"); 1594 } 1595 else 1596 return mixin("_field[_iterator[index]]" ~ op ~ "= value"); 1597 } 1598 } 1599 1600 mixin(std_ops); 1601 } 1602 1603 /++ 1604 Iterates chunks in a sliceable using an iterator composed of indices. 1605 1606 Definition: 1607 ---- 1608 auto index = iterator[i]; 1609 auto elem = sliceable[index[0] .. index[1]]; 1610 ---- 1611 +/ 1612 struct SubSliceIterator(Iterator, Sliceable) 1613 { 1614 @optmath: 1615 /// 1616 Iterator _iterator; 1617 /// 1618 Sliceable _sliceable; 1619 1620 /// 1621 auto lightConst()() const @property 1622 { 1623 return SubSliceIterator!(LightConstOf!Iterator, LightConstOf!Sliceable)(.lightConst(_iterator), _sliceable.lightConst); 1624 } 1625 1626 /// 1627 auto lightImmutable()() immutable @property 1628 { 1629 return SubSliceIterator!(LightImmutableOf!Iterator, LightImmutableOf!Sliceable)(.lightImmutable(_iterator), _sliceable.lightImmutable); 1630 } 1631 1632 auto ref opUnary(string op : "*")() 1633 { 1634 auto i = *_iterator; 1635 return _sliceable[i[0] .. i[1]]; 1636 } 1637 1638 auto ref opIndex()(ptrdiff_t index) 1639 { 1640 auto i = _iterator[index]; 1641 return _sliceable[i[0] .. i[1]]; 1642 } 1643 1644 mixin(std_ops); 1645 } 1646 1647 /++ 1648 Iterates chunks in a sliceable using an iterator composed of indices stored consequently. 1649 1650 Definition: 1651 ---- 1652 auto elem = _sliceable[_iterator[index] .. _iterator[index + 1]]; 1653 ---- 1654 +/ 1655 struct ChopIterator(Iterator, Sliceable) 1656 { 1657 @optmath: 1658 /// 1659 Iterator _iterator; 1660 /// 1661 Sliceable _sliceable; 1662 1663 /// 1664 auto lightConst()() const @property 1665 { 1666 return ChopIterator!(LightConstOf!Iterator, LightConstOf!Sliceable)(.lightConst(_iterator), _sliceable.lightConst); 1667 } 1668 1669 /// 1670 auto lightImmutable()() immutable @property 1671 { 1672 return ChopIterator!(LightImmutableOf!Iterator, LightImmutableOf!Sliceable)(.lightImmutable(_iterator), _sliceable.lightImmutable); 1673 } 1674 1675 auto ref opUnary(string op : "*")() 1676 { 1677 return _sliceable[*_iterator .. _iterator[1]]; 1678 } 1679 1680 auto ref opIndex()(ptrdiff_t index) 1681 { 1682 return _sliceable[_iterator[index] .. _iterator[index + 1]]; 1683 } 1684 1685 mixin(std_ops); 1686 } 1687 1688 /++ 1689 Iterates on top of another iterator and returns a slice 1690 as a multidimensional window at the current position. 1691 1692 `SliceIterator` is used by $(SUBREF topology, map) for packed slices. 1693 +/ 1694 struct SliceIterator(Iterator, size_t N = 1, SliceKind kind = Contiguous) 1695 { 1696 @optmath: 1697 /// 1698 alias Element = Slice!(Iterator, N, kind); 1699 /// 1700 Element._Structure _structure; 1701 /// 1702 Iterator _iterator; 1703 1704 /// 1705 auto lightConst()() const @property 1706 { 1707 return SliceIterator!(LightConstOf!Iterator, N, kind)(_structure, .lightConst(_iterator)); 1708 } 1709 1710 /// 1711 auto lightImmutable()() immutable @property 1712 { 1713 return SliceIterator!(LightImmutableOf!Iterator, N, kind)(_structure, .lightImmutable(_iterator)); 1714 } 1715 1716 auto opUnary(string op : "*")() 1717 { 1718 return Element(_structure, _iterator); 1719 } 1720 1721 auto opIndex()(ptrdiff_t index) 1722 { 1723 return Element(_structure, _iterator + index); 1724 } 1725 1726 mixin(std_ops); 1727 } 1728 1729 public auto FieldIterator__map(Field, alias fun)(FieldIterator!(Field) it) 1730 { 1731 import mir.ndslice.field: _mapField; 1732 auto field = it._field._mapField!fun; 1733 return FieldIterator!(typeof(field))(it._index, field); 1734 } 1735 1736 version(mir_test) unittest 1737 { 1738 import mir.ndslice.topology; 1739 import mir.ndslice.allocation; 1740 auto v = ndiota(3, 3).map!(a => a).slice; 1741 uint r; 1742 auto w = ndiota(3, 3).map!(a => a).map!(a => a[0] * r).slice; 1743 } 1744 1745 /++ 1746 Creates an iterator on top of a field. 1747 1748 `FieldIterator` is used by $(SUBREF slice, slicedField), $(SUBREF topology, bitwise), $(SUBREF topology, ndiota), and others. 1749 +/ 1750 struct FieldIterator(Field) 1751 { 1752 @optmath: 1753 /// 1754 ptrdiff_t _index; 1755 /// 1756 Field _field; 1757 1758 /// 1759 auto lightConst()() const @property 1760 { 1761 return FieldIterator!(LightConstOf!Field)(_index, .lightConst(_field)); 1762 } 1763 1764 /// 1765 auto lightImmutable()() immutable @property 1766 { 1767 return FieldIterator!(LightImmutableOf!Field)(_index, .lightImmutable(_field)); 1768 } 1769 1770 /// 1771 static alias __map(alias fun) = FieldIterator__map!(Field, fun); 1772 1773 /// 1774 Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j) scope const 1775 { 1776 assert(i <= j); 1777 return typeof(return)(j - i, typeof(return).Iterator(i)); 1778 } 1779 1780 /++ 1781 Returns: 1782 `_field[_index + sl.i .. _index + sl.j]`. 1783 +/ 1784 auto opIndex()(Slice!(IotaIterator!size_t) sl) 1785 { 1786 auto idx = _index + sl._iterator._index; 1787 return _field[idx .. idx + sl.length]; 1788 } 1789 1790 auto ref opUnary(string op : "*")() 1791 { return _field[_index]; } 1792 1793 void opUnary(string op)() scope 1794 if (op == "++" || op == "--") 1795 { mixin(op ~ `_index;`); } 1796 1797 auto ref opIndex()(ptrdiff_t index) 1798 { return _field[_index + index]; } 1799 1800 static if (!__traits(compiles, &_field[_index])) 1801 { 1802 auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) 1803 { return _field[_index + index] = value; } 1804 1805 auto ref opIndexUnary(string op)(ptrdiff_t index) 1806 { mixin (`return ` ~ op ~ `_field[_index + index];`); } 1807 1808 auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) 1809 { mixin (`return _field[_index + index] ` ~ op ~ `= value;`); } 1810 } 1811 1812 void opOpAssign(string op)(ptrdiff_t index) scope 1813 if (op == "+" || op == "-") 1814 { mixin(`_index ` ~ op ~ `= index;`); } 1815 1816 auto opBinary(string op)(ptrdiff_t index) 1817 if (op == "+" || op == "-") 1818 { 1819 auto ret = this; 1820 mixin(`ret ` ~ op ~ `= index;`); 1821 return ret; 1822 } 1823 1824 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 1825 { return this._index - right._index; } 1826 1827 bool opEquals()(scope ref const typeof(this) right) scope const 1828 { return this._index == right._index; } 1829 1830 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 1831 { return this._index - right._index; } 1832 1833 /// 1834 auto assumeFieldsHaveZeroShift() @property 1835 { 1836 if (_expect(_index != 0, false)) 1837 { 1838 version (D_Exceptions) 1839 throw assumeZeroShiftException; 1840 else 1841 assert(0, assumeZeroShiftExceptionMsg); 1842 } 1843 static if (hasZeroShiftFieldMember!Field) 1844 return _field.assumeFieldsHaveZeroShift; 1845 else 1846 return _field; 1847 } 1848 } 1849 1850 auto FlattenedIterator__map(Iterator, size_t N, SliceKind kind, alias fun)(FlattenedIterator!(Iterator, N, kind) it) 1851 { 1852 import mir.ndslice.topology: map; 1853 auto slice = it._slice.map!fun; 1854 return FlattenedIterator!(TemplateArgsOf!(typeof(slice)))(it._indices, slice); 1855 } 1856 1857 version(mir_test) unittest 1858 { 1859 import mir.ndslice.topology; 1860 import mir.ndslice.allocation; 1861 auto v = iota(3, 3).universal.flattened.map!(a => a).slice; 1862 uint r; 1863 auto w = iota(3, 3).universal.flattened.map!(a => a).map!(a => a * r).slice; 1864 } 1865 1866 /++ 1867 Creates an iterator on top of all elements in a slice. 1868 1869 `FieldIterator` is used by $(SUBREF topology, bitwise), $(SUBREF topology, ndiota), and others. 1870 +/ 1871 struct FlattenedIterator(Iterator, size_t N, SliceKind kind) 1872 if (N > 1 && (kind == Universal || kind == Canonical)) 1873 { 1874 @optmath: 1875 /// 1876 ptrdiff_t[N] _indices; 1877 /// 1878 Slice!(Iterator, N, kind) _slice; 1879 1880 /// 1881 auto lightConst()() const @property 1882 { 1883 return FlattenedIterator!(LightConstOf!Iterator, N, kind)(_indices, _slice.lightConst); 1884 } 1885 1886 /// 1887 auto lightImmutable()() immutable @property 1888 { 1889 return FlattenedIterator!(LightImmutableOf!Iterator, N, kind)(_indices, _slice.lightImmutable); 1890 } 1891 1892 /// 1893 static alias __map(alias fun) = FlattenedIterator__map!(Iterator, N, kind, fun); 1894 1895 private ptrdiff_t getShift()(ptrdiff_t n) 1896 { 1897 ptrdiff_t _shift; 1898 n += _indices[$ - 1]; 1899 foreach_reverse (i; Iota!(1, N)) 1900 { 1901 immutable v = n / ptrdiff_t(_slice._lengths[i]); 1902 n %= ptrdiff_t(_slice._lengths[i]); 1903 static if (i == _slice.S) 1904 _shift += (n - _indices[i]); 1905 else 1906 _shift += (n - _indices[i]) * _slice._strides[i]; 1907 n = _indices[i - 1] + v; 1908 } 1909 _shift += (n - _indices[0]) * _slice._strides[0]; 1910 return _shift; 1911 } 1912 1913 auto ref opUnary(string op : "*")() 1914 { 1915 return *_slice._iterator; 1916 } 1917 1918 void opUnary(string op)() scope 1919 if (op == "--" || op == "++") 1920 { 1921 foreach_reverse (i; Iota!N) 1922 { 1923 static if (i == _slice.S) 1924 mixin(op ~ `_slice._iterator;`); 1925 else 1926 mixin(`_slice._iterator ` ~ op[0] ~ `= _slice._strides[i];`); 1927 mixin (op ~ `_indices[i];`); 1928 static if (i) 1929 { 1930 static if (op == "++") 1931 { 1932 if (_indices[i] < _slice._lengths[i]) 1933 return; 1934 static if (i == _slice.S) 1935 _slice._iterator -= _slice._lengths[i]; 1936 else 1937 _slice._iterator -= _slice._lengths[i] * _slice._strides[i]; 1938 _indices[i] = 0; 1939 } 1940 else 1941 { 1942 if (_indices[i] >= 0) 1943 return; 1944 static if (i == _slice.S) 1945 _slice._iterator += _slice._lengths[i]; 1946 else 1947 _slice._iterator += _slice._lengths[i] * _slice._strides[i]; 1948 _indices[i] = _slice._lengths[i] - 1; 1949 } 1950 } 1951 } 1952 } 1953 1954 auto ref opIndex()(ptrdiff_t index) 1955 { 1956 return _slice._iterator[getShift(index)]; 1957 } 1958 1959 static if (isMutable!(_slice.DeepElement) && !_slice.hasAccessByRef) 1960 /// 1961 auto ref opIndexAssign(E)(scope ref E elem, size_t index) scope return 1962 { 1963 return _slice._iterator[getShift(index)] = elem; 1964 } 1965 1966 void opOpAssign(string op : "+")(ptrdiff_t n) scope 1967 { 1968 ptrdiff_t _shift; 1969 n += _indices[$ - 1]; 1970 foreach_reverse (i; Iota!(1, N)) 1971 { 1972 immutable v = n / ptrdiff_t(_slice._lengths[i]); 1973 n %= ptrdiff_t(_slice._lengths[i]); 1974 static if (i == _slice.S) 1975 _shift += (n - _indices[i]); 1976 else 1977 _shift += (n - _indices[i]) * _slice._strides[i]; 1978 _indices[i] = n; 1979 n = _indices[i - 1] + v; 1980 } 1981 _shift += (n - _indices[0]) * _slice._strides[0]; 1982 _indices[0] = n; 1983 foreach_reverse (i; Iota!(1, N)) 1984 { 1985 if (_indices[i] >= 0) 1986 break; 1987 _indices[i] += _slice._lengths[i]; 1988 _indices[i - 1]--; 1989 } 1990 _slice._iterator += _shift; 1991 } 1992 1993 void opOpAssign(string op : "-")(ptrdiff_t n) scope 1994 { this += -n; } 1995 1996 auto opBinary(string op)(ptrdiff_t index) 1997 if (op == "+" || op == "-") 1998 { 1999 auto ret = this; 2000 mixin(`ret ` ~ op ~ `= index;`); 2001 return ret; 2002 } 2003 2004 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 2005 { 2006 ptrdiff_t ret = this._indices[0] - right._indices[0]; 2007 foreach (i; Iota!(1, N)) 2008 { 2009 ret *= _slice._lengths[i]; 2010 ret += this._indices[i] - right._indices[i]; 2011 } 2012 return ret; 2013 } 2014 2015 bool opEquals()(scope ref const typeof(this) right) scope const 2016 { 2017 foreach_reverse (i; Iota!N) 2018 if (this._indices[i] != right._indices[i]) 2019 return false; 2020 return true; 2021 } 2022 2023 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 2024 { 2025 foreach (i; Iota!(N - 1)) 2026 if (auto ret = this._indices[i] - right._indices[i]) 2027 return ret; 2028 return this._indices[$ - 1] - right._indices[$ - 1]; 2029 } 2030 } 2031 2032 version(mir_test) unittest 2033 { 2034 import mir.ndslice.topology; 2035 import mir.ndslice.slice; 2036 2037 auto it0 = iota(3, 4).universal.flattened._iterator; 2038 auto it1 = it0; 2039 assert(it0 == it1); 2040 it0 += 5; 2041 assert(it0 > it1); 2042 it0 -= 5; 2043 assert(*it0 == *it1); 2044 assert(it0 == it1); 2045 it0 += 5; 2046 it0 += 7; 2047 it0 -= 9; 2048 assert(it0 > it1); 2049 it1 += 3; 2050 assert(*it0 == *it1); 2051 assert(it0 == it1); 2052 assert(it0 <= it1); 2053 assert(it0 >= it1); 2054 2055 ++it0; 2056 ++it0; 2057 ++it0; 2058 ++it0; 2059 ++it0; 2060 ++it0; 2061 ++it0; 2062 ++it0; 2063 ++it0; 2064 2065 assert(it0 - it1 == 9); 2066 assert(it1 - it0 == -9); 2067 2068 ++it0; 2069 2070 assert(it0 - it1 == 10); 2071 assert(it1 - it0 == -10); 2072 2073 --it0; 2074 2075 assert(it0 - it1 == 9); 2076 assert(it1 - it0 == -9); 2077 assert(it0[-9] == *it1); 2078 assert(*it0 == it1[9]); 2079 2080 --it0; 2081 --it0; 2082 --it0; 2083 --it0; 2084 --it0; 2085 --it0; 2086 --it0; 2087 --it0; 2088 --it0; 2089 assert(*it0 == *it1); 2090 assert(it0 == it1); 2091 assert(it0 <= it1); 2092 assert(it0 >= it1); 2093 } 2094 2095 /++ 2096 `StairsIterator` is used by $(SUBREF topology, stairs). 2097 +/ 2098 struct StairsIterator(Iterator, string direction) 2099 if (direction == "+" || direction == "-") 2100 { 2101 /// 2102 size_t _length; 2103 2104 /// 2105 Iterator _iterator; 2106 2107 /// 2108 auto lightConst()() const @property 2109 { 2110 return StairsIterator!(LightConstOf!Iterator, direction)(_length, .lightConst(_iterator)); 2111 } 2112 2113 /// 2114 auto lightImmutable()() immutable @property 2115 { 2116 return StairsIterator!(LightImmutableOf!Iterator, direction)(_length, .lightImmutable(_iterator)); 2117 } 2118 2119 @optmath: 2120 2121 /// 2122 Slice!Iterator opUnary(string op : "*")() 2123 { 2124 import mir.ndslice.slice: sliced; 2125 return _iterator.sliced(_length); 2126 } 2127 2128 /// 2129 Slice!Iterator opIndex()(ptrdiff_t index) 2130 { 2131 import mir.ndslice.slice: sliced; 2132 static if (direction == "+") 2133 { 2134 auto newLength = _length + index; 2135 auto shift = ptrdiff_t(_length + newLength - 1) * index / 2; 2136 } 2137 else 2138 { 2139 auto newLength = _length - index; 2140 auto shift = ptrdiff_t(_length + newLength + 1) * index / 2; 2141 } 2142 assert(ptrdiff_t(newLength) >= 0); 2143 return (_iterator + shift).sliced(newLength); 2144 } 2145 2146 void opUnary(string op)() scope 2147 if (op == "--" || op == "++") 2148 { 2149 static if (op == "++") 2150 { 2151 _iterator += _length; 2152 static if (direction == "+") 2153 ++_length; 2154 else 2155 --_length; 2156 } 2157 else 2158 { 2159 assert(_length); 2160 static if (direction == "+") 2161 --_length; 2162 else 2163 ++_length; 2164 _iterator -= _length; 2165 } 2166 } 2167 2168 void opOpAssign(string op)(ptrdiff_t index) scope 2169 if (op == "-" || op == "+") 2170 { 2171 static if (op == direction) 2172 auto newLength = _length + index; 2173 else 2174 auto newLength = _length - index; 2175 static if (direction == "+") 2176 auto shift = ptrdiff_t(_length + newLength - 1) * index / 2; 2177 else 2178 auto shift = ptrdiff_t(_length + newLength + 1) * index / 2; 2179 assert(ptrdiff_t(newLength) >= 0); 2180 _length = newLength; 2181 static if (op == "+") 2182 _iterator += shift; 2183 else 2184 _iterator -= shift; 2185 } 2186 2187 auto opBinary(string op)(ptrdiff_t index) 2188 if (op == "+" || op == "-") 2189 { 2190 auto ret = this; 2191 mixin(`ret ` ~ op ~ `= index;`); 2192 return ret; 2193 } 2194 2195 ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 2196 { 2197 static if (direction == "+") 2198 return this._length - right._length; 2199 else 2200 return right._length - this._length; 2201 } 2202 2203 bool opEquals()(scope ref const typeof(this) right) scope const 2204 { return this._length == right._length; } 2205 2206 ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 2207 { return this - right; } 2208 } 2209 2210 /// 2211 version(mir_test) unittest 2212 { 2213 // 0 2214 // 1 2 2215 // 3 4 5 2216 // 6 7 8 9 2217 // 10 11 12 13 14 2218 auto it = StairsIterator!(IotaIterator!size_t, "+")(1, IotaIterator!size_t()); 2219 assert(*it == [0]); 2220 assert(it[4] == [10, 11, 12, 13, 14]); 2221 assert(*(it + 4) == [10, 11, 12, 13, 14]); 2222 ++it; 2223 assert(*it == [1, 2]); 2224 it += 3; 2225 assert(*it == [10, 11, 12, 13, 14]); 2226 assert(it[-3] == [1, 2]); 2227 assert(*(it - 3) == [1, 2]); 2228 assert(it + 1 > it); 2229 assert(it + 1 - 1 == it); 2230 assert(it - 3 - it == -3); 2231 --it; 2232 assert(*it == [6, 7, 8, 9]); 2233 } 2234 2235 /// 2236 version(mir_test) unittest 2237 { 2238 // [0, 1, 2, 3, 4], 2239 // [5, 6, 7, 8], 2240 // [9, 10, 11], 2241 // [12, 13], 2242 // [14]]); 2243 2244 auto it = StairsIterator!(IotaIterator!size_t, "-")(5, IotaIterator!size_t()); 2245 assert(*it == [0, 1, 2, 3, 4]); 2246 assert(it[4] == [14]); 2247 assert(*(it + 4) == [14]); 2248 ++it; 2249 assert(*it == [5, 6, 7, 8]); 2250 it += 3; 2251 assert(*it == [14]); 2252 assert(it[-3] == [5, 6, 7, 8]); 2253 assert(*(it - 3) == [5, 6, 7, 8]); 2254 assert(it + 1 > it); 2255 assert(it + 1 - 1 == it); 2256 assert(it - 3 - it == -3); 2257 --it; 2258 assert(*it == [12, 13]); 2259 } 2260 2261 /++ 2262 Element type of $(LREF TripletIterator). 2263 +/ 2264 struct Triplet(Iterator, SliceKind kind = Contiguous) 2265 { 2266 @optmath: 2267 /// 2268 size_t _iterator; 2269 /// 2270 Slice!(Iterator, 1, kind) _slice; 2271 2272 /// 2273 auto lightConst()() const @property 2274 { 2275 return Triplet!(LightConstOf!Iterator, kind)(_iterator, slice.lightConst); 2276 } 2277 2278 /// 2279 auto lightImmutable()() immutable @property 2280 { 2281 return Triplet!(LightImmutableOf!Iterator, kind)(_iterator, slice.lightImmutable); 2282 } 2283 2284 @property 2285 { 2286 /// 2287 auto ref center() 2288 { 2289 assert(_iterator < _slice.length); 2290 return _slice[_iterator]; 2291 } 2292 2293 /// 2294 Slice!(Iterator, 1, kind) left() 2295 { 2296 assert(_iterator < _slice.length); 2297 return _slice[0 .. _iterator]; 2298 } 2299 2300 /// 2301 Slice!(Iterator, 1, kind) right() 2302 { 2303 assert(_iterator < _slice.length); 2304 return _slice[_iterator + 1 .. $]; 2305 } 2306 } 2307 } 2308 2309 /++ 2310 Iterates triplets position in a slice. 2311 2312 `TripletIterator` is used by $(SUBREF topology, triplets). 2313 +/ 2314 struct TripletIterator(Iterator, SliceKind kind = Contiguous) 2315 { 2316 @optmath: 2317 2318 /// 2319 size_t _iterator; 2320 /// 2321 Slice!(Iterator, 1, kind) _slice; 2322 2323 /// 2324 auto lightConst()() const @property 2325 { 2326 return TripletIterator!(LightConstOf!Iterator, kind)(_iterator, _slice.lightConst); 2327 } 2328 2329 /// 2330 auto lightImmutable()() immutable @property 2331 { 2332 return TripletIterator!(LightImmutableOf!Iterator, kind)(_iterator, _slice.lightImmutable); 2333 } 2334 2335 /// 2336 Triplet!(Iterator, kind) opUnary(string op : "*")() 2337 { 2338 return typeof(return)(_iterator, _slice); 2339 } 2340 2341 /// 2342 Triplet!(Iterator, kind) opIndex()(ptrdiff_t index) 2343 { 2344 return typeof(return)(_iterator + index, _slice); 2345 } 2346 2347 mixin(std_ops); 2348 }