1 /++ 2 This is a submodule of $(MREF mir, ndslice). 3 4 Safety_note: 5 User-defined iterators should care about their safety except bounds checks. 6 Bounds are checked in ndslice code. 7 8 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 9 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments 10 Authors: Ilya Yaroshenko 11 12 $(BOOKTABLE $(H2 Definitions), 13 $(TR $(TH Name) $(TH Description)) 14 $(T2 Slice, N-dimensional slice.) 15 $(T2 SliceKind, SliceKind of $(LREF Slice) enumeration.) 16 $(T2 Universal, Alias for $(LREF .SliceKind.universal).) 17 $(T2 Canonical, Alias for $(LREF .SliceKind.canonical).) 18 $(T2 Contiguous, Alias for $(LREF .SliceKind.contiguous).) 19 $(T2 sliced, Creates a slice on top of an iterator, a pointer, or an array's pointer.) 20 $(T2 slicedField, Creates a slice on top of a field, a random access range, or an array.) 21 $(T2 slicedNdField, Creates a slice on top of an ndField.) 22 $(T2 kindOf, Extracts $(LREF SliceKind).) 23 $(T2 isSlice, Checks if the type is `Slice` instance.) 24 $(T2 Structure, A tuple of lengths and strides.) 25 ) 26 27 Macros: 28 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 29 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 30 T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 31 STD = $(TD $(SMALL $0)) 32 +/ 33 module mir.ndslice.slice; 34 35 import mir.internal.utility : Iota; 36 import mir.math.common : optmath; 37 import mir.ndslice.concatenation; 38 import mir.ndslice.field; 39 import mir.ndslice.internal; 40 import mir.ndslice.iterator; 41 import mir.ndslice.traits: isIterator; 42 import mir.primitives; 43 import mir.qualifier; 44 import mir.utility; 45 import std.meta; 46 import std.traits; 47 48 public import mir.primitives: DeepElementType; 49 50 /++ 51 Checks if type T has asSlice property and its returns a slices. 52 Aliases itself to a dimension count 53 +/ 54 template hasAsSlice(T) 55 { 56 static if (__traits(hasMember, T, "asSlice")) 57 enum size_t hasAsSlice = typeof(T.init.asSlice).N; 58 else 59 enum size_t hasAsSlice = 0; 60 } 61 62 /// 63 version(mir_test) unittest 64 { 65 import mir.series; 66 static assert(!hasAsSlice!(int[])); 67 static assert(hasAsSlice!(SeriesMap!(int, string)) == 1); 68 } 69 70 /++ 71 Check if $(LREF toConst) function can be called with type T. 72 +/ 73 enum isConvertibleToSlice(T) = isSlice!T || isDynamicArray!T || hasAsSlice!T; 74 75 /// 76 version(mir_test) unittest 77 { 78 import mir.series: SeriesMap; 79 static assert(isConvertibleToSlice!(immutable int[])); 80 static assert(isConvertibleToSlice!(string[])); 81 static assert(isConvertibleToSlice!(SeriesMap!(string, int))); 82 static assert(isConvertibleToSlice!(Slice!(int*))); 83 } 84 85 /++ 86 Reurns: 87 Ndslice view in the same data. 88 See_also: $(LREF isConvertibleToSlice). 89 +/ 90 auto toSlice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) val) 91 { 92 import core.lifetime: move; 93 return val.move; 94 } 95 96 /// ditto 97 auto toSlice(Iterator, size_t N, SliceKind kind)(const Slice!(Iterator, N, kind) val) 98 { 99 return val[]; 100 } 101 102 /// ditto 103 auto toSlice(Iterator, size_t N, SliceKind kind)(immutable Slice!(Iterator, N, kind) val) 104 { 105 return val[]; 106 } 107 108 /// ditto 109 auto toSlice(T)(T[] val) 110 { 111 return val.sliced; 112 } 113 114 /// ditto 115 auto toSlice(T)(T val) 116 if (hasAsSlice!T || __traits(hasMember, T, "moveToSlice")) 117 { 118 static if (__traits(hasMember, T, "moveToSlice")) 119 return val.moveToSlice; 120 else 121 return val.asSlice; 122 } 123 124 /// ditto 125 auto toSlice(T)(ref T val) 126 if (hasAsSlice!T) 127 { 128 return val.asSlice; 129 } 130 131 /// 132 template toSlices(args...) 133 { 134 static if (args.length) 135 { 136 alias arg = args[0]; 137 alias Arg = typeof(arg); 138 static if (isMutable!Arg && isSlice!Arg) 139 alias slc = arg; 140 else 141 @optmath @property auto ref slc()() 142 { 143 return toSlice(arg); 144 } 145 alias toSlices = AliasSeq!(slc, toSlices!(args[1..$])); 146 } 147 else 148 alias toSlices = AliasSeq!(); 149 } 150 151 /++ 152 Checks if the type is `Slice` instance. 153 +/ 154 enum isSlice(T) = is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind); 155 156 /// 157 @safe pure nothrow @nogc 158 version(mir_test) unittest 159 { 160 alias A = uint[]; 161 alias S = Slice!(int*); 162 163 static assert(isSlice!S); 164 static assert(!isSlice!A); 165 } 166 167 /++ 168 SliceKind of $(LREF Slice). 169 See_also: 170 $(SUBREF topology, universal), 171 $(SUBREF topology, canonical), 172 $(SUBREF topology, assumeCanonical), 173 $(SUBREF topology, assumeContiguous). 174 +/ 175 enum mir_slice_kind 176 { 177 /// A slice has strides for all dimensions. 178 universal, 179 /// A slice has >=2 dimensions and row dimension is contiguous. 180 canonical, 181 /// A slice is a flat contiguous data without strides. 182 contiguous, 183 } 184 /// ditto 185 alias SliceKind = mir_slice_kind; 186 187 /++ 188 Alias for $(LREF .SliceKind.universal). 189 190 See_also: 191 Internal Binary Representation section in $(LREF Slice). 192 +/ 193 alias Universal = SliceKind.universal; 194 /++ 195 Alias for $(LREF .SliceKind.canonical). 196 197 See_also: 198 Internal Binary Representation section in $(LREF Slice). 199 +/ 200 alias Canonical = SliceKind.canonical; 201 /++ 202 Alias for $(LREF .SliceKind.contiguous). 203 204 See_also: 205 Internal Binary Representation section in $(LREF Slice). 206 +/ 207 alias Contiguous = SliceKind.contiguous; 208 209 /// Extracts $(LREF SliceKind). 210 enum kindOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind) = kind; 211 212 /// 213 @safe pure nothrow @nogc 214 version(mir_test) unittest 215 { 216 static assert(kindOf!(Slice!(int*, 1, Universal)) == Universal); 217 } 218 219 /// Extracts iterator type from a $(LREF Slice). 220 alias IteratorOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind) = Iterator; 221 222 private template SkipDimension(size_t dimension, size_t index) 223 { 224 static if (index < dimension) 225 enum SkipDimension = index; 226 else 227 static if (index == dimension) 228 static assert (0, "SkipInex: wrong index"); 229 else 230 enum SkipDimension = index - 1; 231 } 232 233 /++ 234 Creates an n-dimensional slice-shell over an iterator. 235 Params: 236 iterator = An iterator, a pointer, or an array. 237 lengths = A list of lengths for each dimension 238 Returns: 239 n-dimensional slice 240 +/ 241 auto sliced(size_t N, Iterator)(Iterator iterator, size_t[N] lengths...) 242 if (!__traits(isStaticArray, Iterator) && N 243 && !is(Iterator : Slice!(_Iterator, _N, kind), _Iterator, size_t _N, SliceKind kind)) 244 { 245 alias C = ImplicitlyUnqual!(typeof(iterator)); 246 size_t[N] _lengths; 247 foreach (i; Iota!N) 248 _lengths[i] = lengths[i]; 249 ptrdiff_t[1] _strides = 0; 250 static if (isDynamicArray!Iterator) 251 { 252 assert(lengthsProduct(_lengths) <= iterator.length, 253 "array length should be greater or equal to the product of constructed ndslice lengths"); 254 auto ptr = iterator.length ? &iterator[0] : null; 255 return Slice!(typeof(C.init[0])*, N)(_lengths, ptr); 256 } 257 else 258 { 259 // break safety 260 if (false) 261 { 262 ++iterator; 263 --iterator; 264 iterator += 34; 265 iterator -= 34; 266 } 267 import core.lifetime: move; 268 return Slice!(C, N)(_lengths, iterator.move); 269 } 270 } 271 272 /// Random access range primitives for slices over user defined types 273 @safe pure nothrow @nogc version(mir_test) unittest 274 { 275 struct MyIota 276 { 277 //`[index]` operator overloading 278 auto opIndex(size_t index) @safe nothrow 279 { 280 return index; 281 } 282 283 auto lightConst()() const @property { return MyIota(); } 284 auto lightImmutable()() immutable @property { return MyIota(); } 285 } 286 287 import mir.ndslice.iterator: FieldIterator; 288 alias Iterator = FieldIterator!MyIota; 289 alias S = Slice!(Iterator, 2); 290 import std.range.primitives; 291 static assert(hasLength!S); 292 static assert(hasSlicing!S); 293 static assert(isRandomAccessRange!S); 294 295 auto slice = Iterator().sliced(20, 10); 296 assert(slice[1, 2] == 12); 297 auto sCopy = slice.save; 298 assert(slice[1, 2] == 12); 299 } 300 301 /++ 302 Creates an 1-dimensional slice-shell over an array. 303 Params: 304 array = An array. 305 Returns: 306 1-dimensional slice 307 +/ 308 Slice!(T*) sliced(T)(T[] array) @trusted 309 { 310 version(LDC) pragma(inline, true); 311 return Slice!(T*)([array.length], array.ptr); 312 } 313 314 /// Creates a slice from an array. 315 @safe pure nothrow version(mir_test) unittest 316 { 317 auto slice = new int[10].sliced; 318 assert(slice.length == 10); 319 static assert(is(typeof(slice) == Slice!(int*))); 320 } 321 322 /++ 323 Creates an n-dimensional slice-shell over the 1-dimensional input slice. 324 Params: 325 slice = slice 326 lengths = A list of lengths for each dimension. 327 Returns: 328 n-dimensional slice 329 +/ 330 Slice!(Iterator, N, kind) 331 sliced 332 (Iterator, size_t N, SliceKind kind) 333 (Slice!(Iterator, 1, kind) slice, size_t[N] lengths...) 334 if (N) 335 { 336 auto structure = typeof(return)._Structure.init; 337 structure[0] = lengths; 338 static if (kind != Contiguous) 339 { 340 import mir.ndslice.topology: iota; 341 structure[1] = structure[0].iota.strides; 342 } 343 import core.lifetime: move; 344 return typeof(return)(structure, slice._iterator.move); 345 } 346 347 /// 348 @safe pure nothrow version(mir_test) unittest 349 { 350 import mir.ndslice.topology : iota; 351 auto data = new int[24]; 352 foreach (i, ref e; data) 353 e = cast(int)i; 354 auto a = data[0..10].sliced(10)[0..6].sliced(2, 3); 355 auto b = iota!int(10)[0..6].sliced(2, 3); 356 assert(a == b); 357 a[] += b; 358 foreach (i, e; data[0..6]) 359 assert(e == 2*i); 360 foreach (i, e; data[6..$]) 361 assert(e == i+6); 362 } 363 364 /++ 365 Creates an n-dimensional slice-shell over a field. 366 Params: 367 field = A field. The length of the 368 array should be equal to or less then the product of 369 lengths. 370 lengths = A list of lengths for each dimension. 371 Returns: 372 n-dimensional slice 373 +/ 374 Slice!(FieldIterator!Field, N) 375 slicedField(Field, size_t N)(Field field, size_t[N] lengths...) 376 if (N) 377 { 378 static if (hasLength!Field) 379 assert(lengths.lengthsProduct <= field.length, "Length product should be less or equal to the field length."); 380 return FieldIterator!Field(0, field).sliced(lengths); 381 } 382 383 ///ditto 384 auto slicedField(Field)(Field field) 385 if(hasLength!Field) 386 { 387 return .slicedField(field, field.length); 388 } 389 390 /// Creates an 1-dimensional slice over a field, array, or random access range. 391 @safe @nogc pure nothrow version(mir_test) unittest 392 { 393 import mir.ndslice.topology : iota; 394 auto slice = 10.iota.slicedField; 395 assert(slice.length == 10); 396 } 397 398 /++ 399 Creates an n-dimensional slice-shell over an ndField. 400 Params: 401 field = A ndField. Lengths should fit into field's shape. 402 lengths = A list of lengths for each dimension. 403 Returns: 404 n-dimensional slice 405 See_also: $(SUBREF concatenation, concatenation) examples. 406 +/ 407 Slice!(IndexIterator!(FieldIterator!(ndIotaField!N), ndField), N) 408 slicedNdField(ndField, size_t N)(ndField field, size_t[N] lengths...) 409 if (N) 410 { 411 static if(hasShape!ndField) 412 { 413 auto shape = field.shape; 414 foreach (i; 0 .. N) 415 assert(lengths[i] <= shape[i], "Lengths should fit into ndfield's shape."); 416 } 417 import mir.ndslice.topology: indexed, ndiota; 418 return indexed(field, ndiota(lengths)); 419 } 420 421 ///ditto 422 auto slicedNdField(ndField)(ndField field) 423 if(hasShape!ndField) 424 { 425 return .slicedNdField(field, field.shape); 426 } 427 428 /++ 429 Combination of coordinate(s) and value. 430 +/ 431 struct CoordinateValue(T, size_t N = 1) 432 { 433 /// 434 size_t[N] index; 435 436 /// 437 T value; 438 439 /// 440 int opCmp()(scope auto ref const typeof(this) rht) const 441 { 442 return cmpCoo(this.index, rht.index); 443 } 444 } 445 446 private int cmpCoo(size_t N)(scope const auto ref size_t[N] a, scope const auto ref size_t[N] b) 447 { 448 foreach (i; Iota!(0, N)) 449 if (a[i] != b[i]) 450 return a[i] > b[i] ? 1 : -1; 451 return 0; 452 } 453 454 /++ 455 Presents $(LREF .Slice.structure). 456 +/ 457 struct Structure(size_t N) 458 { 459 /// 460 size_t[N] lengths; 461 /// 462 sizediff_t[N] strides; 463 } 464 465 package(mir) alias LightConstOfLightScopeOf(Iterator) = LightConstOf!(LightScopeOf!Iterator); 466 package(mir) alias LightImmutableOfLightConstOf(Iterator) = LightImmutableOf!(LightScopeOf!Iterator); 467 package(mir) alias ImmutableOfUnqualOfPointerTarget(Iterator) = immutable(Unqual!(PointerTarget!Iterator))*; 468 package(mir) alias ConstOfUnqualOfPointerTarget(Iterator) = const(Unqual!(PointerTarget!Iterator))*; 469 470 package(mir) template allLightScope(args...) 471 { 472 static if (args.length) 473 { 474 alias arg = args[0]; 475 alias Arg = typeof(arg); 476 static if(!isDynamicArray!Arg) 477 { 478 static if(!is(LightScopeOf!Arg == Arg)) 479 @optmath @property ls()() 480 { 481 import mir.qualifier: lightScope; 482 return lightScope(arg); 483 } 484 else alias ls = arg; 485 } 486 else alias ls = arg; 487 alias allLightScope = AliasSeq!(ls, allLightScope!(args[1..$])); 488 } 489 else 490 alias allLightScope = AliasSeq!(); 491 } 492 493 /++ 494 Presents an n-dimensional view over a range. 495 496 $(H3 Definitions) 497 498 In order to change data in a slice using 499 overloaded operators such as `=`, `+=`, `++`, 500 a syntactic structure of type 501 `<slice to change>[<index and interval sequence...>]` must be used. 502 It is worth noting that just like for regular arrays, operations `a = b` 503 and `a[] = b` have different meanings. 504 In the first case, after the operation is carried out, `a` simply points at the same data as `b` 505 does, and the data which `a` previously pointed at remains unmodified. 506 Here, `а` and `b` must be of the same type. 507 In the second case, `a` points at the same data as before, 508 but the data itself will be changed. In this instance, the number of dimensions of `b` 509 may be less than the number of dimensions of `а`; and `b` can be a Slice, 510 a regular multidimensional array, or simply a value (e.g. a number). 511 512 In the following table you will find the definitions you might come across 513 in comments on operator overloading. 514 515 $(BOOKTABLE 516 $(TR $(TH Operator Overloading) $(TH Examples at `N == 3`)) 517 $(TR $(TD An $(B interval) is a part of a sequence of type `i .. j`.) 518 $(STD `2..$-3`, `0..4`)) 519 $(TR $(TD An $(B index) is a part of a sequence of type `i`.) 520 $(STD `3`, `$-1`)) 521 $(TR $(TD A $(B partially defined slice) is a sequence composed of 522 $(B intervals) and $(B indices) with an overall length strictly less than `N`.) 523 $(STD `[3]`, `[0..$]`, `[3, 3]`, `[0..$,0..3]`, `[0..$,2]`)) 524 $(TR $(TD A $(B fully defined index) is a sequence 525 composed only of $(B indices) with an overall length equal to `N`.) 526 $(STD `[2,3,1]`)) 527 $(TR $(TD A $(B fully defined slice) is an empty sequence 528 or a sequence composed of $(B indices) and at least one 529 $(B interval) with an overall length equal to `N`.) 530 $(STD `[]`, `[3..$,0..3,0..$-1]`, `[2,0..$,1]`)) 531 $(TR $(TD An $(B indexed slice) is syntax sugar for $(SUBREF topology, indexed) and $(SUBREF topology, cartesian).) 532 $(STD `[anNdslice]`, `[$.iota, anNdsliceForCartesian1, $.iota]`)) 533 ) 534 535 See_also: 536 $(SUBREF topology, iota). 537 538 $(H3 Internal Binary Representation) 539 540 Multidimensional Slice is a structure that consists of lengths, strides, and a iterator (pointer). 541 542 $(SUBREF topology, FieldIterator) shell is used to wrap fields and random access ranges. 543 FieldIterator contains a shift of the current initial element of a multidimensional slice 544 and the field itself. 545 546 With the exception of $(MREF mir,ndslice,allocation) module, no functions in this 547 package move or copy data. The operations are only carried out on lengths, strides, 548 and pointers. If a slice is defined over a range, only the shift of the initial element 549 changes instead of the range. 550 551 Mir n-dimensional Slices can be one of the three kinds. 552 553 $(H4 Contiguous slice) 554 555 Contiguous in memory (or in a user-defined iterator's field) row-major tensor that doesn't store strides because they can be computed on the fly using lengths. 556 The row stride is always equaled 1. 557 558 $(H4 Canonical slice) 559 560 Canonical slice as contiguous in memory (or in a user-defined iterator's field) rows of a row-major tensor, it doesn't store the stride for row dimension because it is always equaled 1. 561 BLAS/LAPACK matrices are Canonical but originally have column-major order. 562 In the same time you can use 2D Canonical Slices with LAPACK assuming that rows are columns and columns are rows. 563 564 $(H4 Universal slice) 565 566 A row-major tensor that stores the strides for all dimensions. 567 NumPy strides are Universal. 568 569 $(H4 Internal Representation for Universal Slices) 570 571 Type definition 572 573 ------- 574 Slice!(Iterator, N, Universal) 575 ------- 576 577 Schema 578 579 ------- 580 Slice!(Iterator, N, Universal) 581 size_t[N] _lengths 582 sizediff_t[N] _strides 583 Iterator _iterator 584 ------- 585 586 $(H5 Example) 587 588 Definitions 589 590 ------- 591 import mir.ndslice; 592 auto a = new double[24]; 593 Slice!(double*, 3, Universal) s = a.sliced(2, 3, 4).universal; 594 Slice!(double*, 3, Universal) t = s.transposed!(1, 2, 0); 595 Slice!(double*, 3, Universal) r = t.reversed!1; 596 ------- 597 598 Representation 599 600 ------- 601 s________________________ 602 lengths[0] ::= 2 603 lengths[1] ::= 3 604 lengths[2] ::= 4 605 606 strides[0] ::= 12 607 strides[1] ::= 4 608 strides[2] ::= 1 609 610 iterator ::= &a[0] 611 612 t____transposed!(1, 2, 0) 613 lengths[0] ::= 3 614 lengths[1] ::= 4 615 lengths[2] ::= 2 616 617 strides[0] ::= 4 618 strides[1] ::= 1 619 strides[2] ::= 12 620 621 iterator ::= &a[0] 622 623 r______________reversed!1 624 lengths[0] ::= 2 625 lengths[1] ::= 3 626 lengths[2] ::= 4 627 628 strides[0] ::= 12 629 strides[1] ::= -4 630 strides[2] ::= 1 631 632 iterator ::= &a[8] // (old_strides[1] * (lengths[1] - 1)) = 8 633 ------- 634 635 $(H4 Internal Representation for Canonical Slices) 636 637 Type definition 638 639 ------- 640 Slice!(Iterator, N, Canonical) 641 ------- 642 643 Schema 644 645 ------- 646 Slice!(Iterator, N, Canonical) 647 size_t[N] _lengths 648 sizediff_t[N-1] _strides 649 Iterator _iterator 650 ------- 651 652 $(H4 Internal Representation for Contiguous Slices) 653 654 Type definition 655 656 ------- 657 Slice!(Iterator, N) 658 ------- 659 660 Schema 661 662 ------- 663 Slice!(Iterator, N, Contiguous) 664 size_t[N] _lengths 665 sizediff_t[0] _strides 666 Iterator _iterator 667 ------- 668 +/ 669 struct mir_slice(Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous, Labels_...) 670 if (0 < N_ && N_ < 255 && !(kind_ == Canonical && N_ == 1) && Labels_.length <= N_ && isIterator!Iterator_) 671 { 672 @optmath: 673 674 /// $(LREF SliceKind) 675 enum SliceKind kind = kind_; 676 677 /// Dimensions count 678 enum size_t N = N_; 679 680 /// Strides count 681 enum size_t S = kind == Universal ? N : kind == Canonical ? N - 1 : 0; 682 683 /// Labels count. 684 enum size_t L = Labels_.length; 685 686 /// Data iterator type 687 alias Iterator = Iterator_; 688 689 /// This type 690 alias This = Slice!(Iterator, N, kind); 691 692 /// Data element type 693 alias DeepElement = typeof(Iterator.init[size_t.init]); 694 695 /// 696 alias serdeKeysProxy = DeepElement; 697 698 /// Label Iterators types 699 alias Labels = Labels_; 700 701 /// 702 template Element(size_t dimension) 703 if (dimension < N) 704 { 705 static if (N == 1) 706 alias Element = DeepElement; 707 else 708 { 709 static if (kind == Universal || dimension == N - 1) 710 alias Element = mir_slice!(Iterator, N - 1, Universal); 711 else 712 static if (N == 2 || kind == Contiguous && dimension == 0) 713 alias Element = mir_slice!(Iterator, N - 1); 714 else 715 alias Element = mir_slice!(Iterator, N - 1, Canonical); 716 } 717 } 718 719 package(mir): 720 721 enum doUnittest = is(Iterator == int*) && (N == 1 || N == 2) && kind == Contiguous; 722 723 enum hasAccessByRef = __traits(compiles, &_iterator[0]); 724 725 enum PureIndexLength(Slices...) = Filter!(isIndex, Slices).length; 726 727 enum isPureSlice(Slices...) = 728 Slices.length == 0 729 || Slices.length <= N 730 && PureIndexLength!Slices < N 731 && Filter!(isIndex, Slices).length < Slices.length 732 && allSatisfy!(templateOr!(isIndex, is_Slice), Slices); 733 734 735 enum isFullPureSlice(Slices...) = 736 Slices.length == 0 737 || Slices.length == N 738 && PureIndexLength!Slices < N 739 && allSatisfy!(templateOr!(isIndex, is_Slice), Slices); 740 741 enum isIndexedSlice(Slices...) = 742 Slices.length 743 && Slices.length <= N 744 && allSatisfy!(isSlice, Slices) 745 && anySatisfy!(templateNot!is_Slice, Slices); 746 747 static if (S) 748 { 749 /// 750 public alias _Structure = AliasSeq!(size_t[N], ptrdiff_t[S]); 751 /// 752 public _Structure _structure; 753 /// 754 public alias _lengths = _structure[0]; 755 /// 756 public alias _strides = _structure[1]; 757 } 758 else 759 { 760 /// 761 public alias _Structure = AliasSeq!(size_t[N]); 762 /// 763 public _Structure _structure; 764 /// 765 public alias _lengths = _structure[0]; 766 /// 767 public enum ptrdiff_t[S] _strides = ptrdiff_t[S].init; 768 } 769 770 /// Data Iterator 771 public Iterator _iterator; 772 /// Labels iterators 773 public Labels _labels; 774 775 sizediff_t backIndex(size_t dimension = 0)() @safe @property scope const 776 if (dimension < N) 777 { 778 return _stride!dimension * (_lengths[dimension] - 1); 779 } 780 781 size_t indexStride(size_t I)(size_t[I] _indices) @safe scope const 782 { 783 static if (_indices.length) 784 { 785 static if (kind == Contiguous) 786 { 787 enum E = I - 1; 788 assert(_indices[E] < _lengths[E], indexError!(E, N)); 789 ptrdiff_t ball = this._stride!E; 790 ptrdiff_t stride = _indices[E] * ball; 791 foreach_reverse (i; Iota!E) //static 792 { 793 ball *= _lengths[i + 1]; 794 assert(_indices[i] < _lengths[i], indexError!(i, N)); 795 stride += ball * _indices[i]; 796 } 797 } 798 else 799 static if (kind == Canonical) 800 { 801 enum E = I - 1; 802 assert(_indices[E] < _lengths[E], indexError!(E, N)); 803 static if (I == N) 804 size_t stride = _indices[E]; 805 else 806 size_t stride = _strides[E] * _indices[E]; 807 foreach_reverse (i; Iota!E) //static 808 { 809 assert(_indices[i] < _lengths[i], indexError!(i, N)); 810 stride += _strides[i] * _indices[i]; 811 } 812 } 813 else 814 { 815 enum E = I - 1; 816 assert(_indices[E] < _lengths[E], indexError!(E, N)); 817 size_t stride = _strides[E] * _indices[E]; 818 foreach_reverse (i; Iota!E) //static 819 { 820 assert(_indices[i] < _lengths[i], indexError!(i, N)); 821 stride += _strides[i] * _indices[i]; 822 } 823 } 824 return stride; 825 } 826 else 827 { 828 return 0; 829 } 830 } 831 832 public: 833 834 // static if (S == 0) 835 // { 836 /// Defined for Contiguous Slice only 837 // this()(size_t[N] lengths, in ptrdiff_t[] empty, Iterator iterator, Labels labels) 838 // { 839 // version(LDC) pragma(inline, true); 840 // assert(empty.length == 0); 841 // this._lengths = lengths; 842 // this._iterator = iterator; 843 // } 844 845 // /// ditto 846 // this()(size_t[N] lengths, Iterator iterator, Labels labels) 847 // { 848 // version(LDC) pragma(inline, true); 849 // this._lengths = lengths; 850 // this._iterator = iterator; 851 // } 852 853 // /// ditto 854 // this()(size_t[N] lengths, in ptrdiff_t[] empty, Iterator iterator, Labels labels) 855 // { 856 // version(LDC) pragma(inline, true); 857 // assert(empty.length == 0); 858 // this._lengths = lengths; 859 // this._iterator = iterator; 860 // } 861 862 // /// ditto 863 // this()(size_t[N] lengths, Iterator iterator, Labels labels) 864 // { 865 // version(LDC) pragma(inline, true); 866 // this._lengths = lengths; 867 // this._iterator = iterator; 868 // } 869 // } 870 871 // version(LDC) 872 // private enum classicConstructor = true; 873 // else 874 // private enum classicConstructor = S > 0; 875 876 // static if (classicConstructor) 877 // { 878 /// Defined for Canonical and Universal Slices (DMD, GDC, LDC) and for Contiguous Slices (LDC) 879 // this()(size_t[N] lengths, ptrdiff_t[S] strides, Iterator iterator, Labels labels) 880 // { 881 // version(LDC) pragma(inline, true); 882 // this._lengths = lengths; 883 // this._strides = strides; 884 // this._iterator = iterator; 885 // this._labels = labels; 886 // } 887 888 // /// ditto 889 // this()(size_t[N] lengths, ptrdiff_t[S] strides, ref Iterator iterator, Labels labels) 890 // { 891 // version(LDC) pragma(inline, true); 892 // this._lengths = lengths; 893 // this._strides = strides; 894 // this._iterator = iterator; 895 // this._labels = labels; 896 // } 897 // } 898 899 // /// Construct from null 900 // this()(typeof(null)) 901 // { 902 // version(LDC) pragma(inline, true); 903 // } 904 905 // static if (doUnittest) 906 // /// 907 // @safe pure version(mir_test) unittest 908 // { 909 // import mir.ndslice.slice; 910 // alias Array = Slice!(double*); 911 // Array a = null; 912 // auto b = Array(null); 913 // assert(a.empty); 914 // assert(b.empty); 915 916 // auto fun(Array a = null) 917 // { 918 919 // } 920 // } 921 922 static if (doUnittest) 923 /// Creates a 2-dimentional slice with custom strides. 924 nothrow pure 925 version(mir_test) unittest 926 { 927 uint[8] array = [1, 2, 3, 4, 5, 6, 7, 8]; 928 auto slice = Slice!(uint*, 2, Universal)([2, 2], [4, 1], array.ptr); 929 930 assert(&slice[0, 0] == &array[0]); 931 assert(&slice[0, 1] == &array[1]); 932 assert(&slice[1, 0] == &array[4]); 933 assert(&slice[1, 1] == &array[5]); 934 assert(slice == [[1, 2], [5, 6]]); 935 936 array[2] = 42; 937 assert(slice == [[1, 2], [5, 6]]); 938 939 array[1] = 99; 940 assert(slice == [[1, 99], [5, 6]]); 941 } 942 943 /++ 944 Returns: View with stripped out reference counted context. 945 The lifetime of the result mustn't be longer then the lifetime of the original slice. 946 +/ 947 auto lightScope()() scope return @property 948 { 949 auto ret = Slice!(LightScopeOf!Iterator, N, kind, staticMap!(LightScopeOf, Labels)) 950 (_structure, .lightScope(_iterator)); 951 foreach(i; Iota!L) 952 ret._labels[i] = .lightScope(_labels[i]); 953 return ret; 954 } 955 956 /// ditto 957 auto lightScope()() scope const return @property 958 { 959 auto ret = Slice!(LightConstOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightConstOfLightScopeOf, Labels)) 960 (_structure, .lightScope(_iterator)); 961 foreach(i; Iota!L) 962 ret._labels[i] = .lightScope(_labels[i]); 963 return ret; 964 } 965 966 /// ditto 967 auto lightScope()() scope immutable return @property 968 { 969 auto ret = Slice!(LightImmutableOf!(LightScopeOf!Iterator), N, kind, staticMap!(LightImmutableOfLightConstOf(Labels))) 970 (_structure, .lightScope(_iterator)); 971 foreach(i; Iota!L) 972 ret._labels[i] = .lightScope(_labels[i]); 973 return ret; 974 } 975 976 /// Returns: Mutable slice over immutable data. 977 Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightImmutable()() scope return immutable @property 978 { 979 auto ret = typeof(return)(_structure, .lightImmutable(_iterator)); 980 foreach(i; Iota!L) 981 ret._labels[i] = .lightImmutable(_labels[i]); 982 return ret; 983 } 984 985 /// Returns: Mutable slice over const data. 986 Slice!(LightConstOf!Iterator, N, kind, staticMap!(LightConstOf, Labels)) lightConst()() scope return const @property @trusted 987 { 988 auto ret = typeof(return)(_structure, .lightConst(_iterator)); 989 foreach(i; Iota!L) 990 ret._labels[i] = .lightConst(_labels[i]); 991 return ret; 992 } 993 994 /// ditto 995 Slice!(LightImmutableOf!Iterator, N, kind, staticMap!(LightImmutableOf, Labels)) lightConst()() scope return immutable @property 996 { 997 return this.lightImmutable; 998 } 999 1000 /// Label for the dimensions 'd'. By default returns the row label. 1001 Slice!(Labels[d]) 1002 label(size_t d = 0)() @property 1003 if (d <= L) 1004 { 1005 return typeof(return)(_lengths[d], _labels[d]); 1006 } 1007 1008 /// ditto 1009 void label(size_t d = 0)(Slice!(Labels[d]) rhs) @property 1010 if (d <= L) 1011 { 1012 import core.lifetime: move; 1013 assert(rhs.length == _lengths[d], "ndslice: labels dimension mismatch"); 1014 _labels[d] = rhs._iterator.move; 1015 } 1016 1017 /// ditto 1018 Slice!(LightConstOf!(Labels[d])) 1019 label(size_t d = 0)() @property const 1020 if (d <= L) 1021 { 1022 return typeof(return)(_lengths[d].lightConst, _labels[d]); 1023 } 1024 1025 /// ditto 1026 Slice!(LightImmutableOf!(Labels[d])) 1027 label(size_t d = 0)() @property immutable 1028 if (d <= L) 1029 { 1030 return typeof(return)(_lengths[d].lightImmutable, _labels[d]); 1031 } 1032 1033 /// Strips label off the DataFrame 1034 auto values()() @property 1035 { 1036 return Slice!(Iterator, N, kind)(_structure, _iterator); 1037 } 1038 1039 /// ditto 1040 auto values()() @property const 1041 { 1042 return Slice!(LightConstOf!Iterator, N, kind)(_structure, .lightConst(_iterator)); 1043 } 1044 1045 /// ditto 1046 auto values()() @property immutable 1047 { 1048 return Slice!(LightImmutableOf!Iterator, N, kind)(_structure, .lightImmutable(_iterator)); 1049 } 1050 1051 /// `opIndex` overload for const slice 1052 auto ref opIndex(Indexes...)(Indexes indices) const @trusted 1053 if (isPureSlice!Indexes || isIndexedSlice!Indexes) 1054 { 1055 return lightConst.opIndex(indices); 1056 } 1057 /// `opIndex` overload for immutable slice 1058 auto ref opIndex(Indexes...)(Indexes indices) immutable @trusted 1059 if (isPureSlice!Indexes || isIndexedSlice!Indexes) 1060 { 1061 return lightImmutable.opIndex(indices); 1062 } 1063 1064 static if (allSatisfy!(isPointer, Iterator, Labels)) 1065 { 1066 private alias ConstThis = Slice!(const(Unqual!(PointerTarget!Iterator))*, N, kind); 1067 private alias ImmutableThis = Slice!(immutable(Unqual!(PointerTarget!Iterator))*, N, kind); 1068 1069 /++ 1070 Cast to const and immutable slices in case of underlying range is a pointer. 1071 +/ 1072 auto toImmutable()() scope return immutable @trusted pure nothrow @nogc 1073 { 1074 return Slice!(ImmutableOfUnqualOfPointerTarget!Iterator, N, kind, staticMap!(ImmutableOfUnqualOfPointerTarget, Labels)) 1075 (_structure, _iterator, _labels); 1076 } 1077 1078 /// ditto 1079 auto toConst()() scope return const @trusted pure nothrow @nogc 1080 { 1081 version(LDC) pragma(inline, true); 1082 return Slice!(ConstOfUnqualOfPointerTarget!Iterator, N, kind, staticMap!(ConstOfUnqualOfPointerTarget, Labels)) 1083 (_structure, _iterator, _labels); 1084 } 1085 1086 static if (!is(Slice!(const(Unqual!(PointerTarget!Iterator))*, N, kind) == This)) 1087 /// ditto 1088 alias toConst this; 1089 1090 static if (doUnittest) 1091 /// 1092 version(mir_test) unittest 1093 { 1094 static struct Foo 1095 { 1096 Slice!(int*) bar; 1097 1098 int get(size_t i) immutable 1099 { 1100 return bar[i]; 1101 } 1102 1103 int get(size_t i) const 1104 { 1105 return bar[i]; 1106 } 1107 1108 int get(size_t i) inout 1109 { 1110 return bar[i]; 1111 } 1112 } 1113 } 1114 1115 static if (doUnittest) 1116 /// 1117 version(mir_test) unittest 1118 { 1119 Slice!(double*, 2, Universal) nn; 1120 Slice!(immutable(double)*, 2, Universal) ni; 1121 Slice!(const(double)*, 2, Universal) nc; 1122 1123 const Slice!(double*, 2, Universal) cn; 1124 const Slice!(immutable(double)*, 2, Universal) ci; 1125 const Slice!(const(double)*, 2, Universal) cc; 1126 1127 immutable Slice!(double*, 2, Universal) in_; 1128 immutable Slice!(immutable(double)*, 2, Universal) ii; 1129 immutable Slice!(const(double)*, 2, Universal) ic; 1130 1131 nc = nc; nc = cn; nc = in_; 1132 nc = nc; nc = cc; nc = ic; 1133 nc = ni; nc = ci; nc = ii; 1134 1135 void fun(T, size_t N)(Slice!(const(T)*, N, Universal) sl) 1136 { 1137 //... 1138 } 1139 1140 fun(nn); fun(cn); fun(in_); 1141 fun(nc); fun(cc); fun(ic); 1142 fun(ni); fun(ci); fun(ii); 1143 1144 static assert(is(typeof(cn[]) == typeof(nc))); 1145 static assert(is(typeof(ci[]) == typeof(ni))); 1146 static assert(is(typeof(cc[]) == typeof(nc))); 1147 1148 static assert(is(typeof(in_[]) == typeof(ni))); 1149 static assert(is(typeof(ii[]) == typeof(ni))); 1150 static assert(is(typeof(ic[]) == typeof(ni))); 1151 1152 ni = ci[]; 1153 ni = in_[]; 1154 ni = ii[]; 1155 ni = ic[]; 1156 } 1157 } 1158 1159 /++ 1160 Iterator 1161 Returns: 1162 Iterator (pointer) to the $(LREF .Slice.first) element. 1163 +/ 1164 auto iterator()() inout scope return @property 1165 { 1166 return _iterator; 1167 } 1168 1169 static if (kind == Contiguous && isPointer!Iterator) 1170 /++ 1171 `ptr` alias is available only if the slice kind is $(LREF Contiguous) contiguous and the $(LREF .Slice.iterator) is a pointers. 1172 +/ 1173 alias ptr = iterator; 1174 else 1175 { 1176 import mir.rc.array: mir_rci; 1177 static if (kind == Contiguous && is(Iterator : mir_rci!ET, ET)) 1178 auto ptr() scope return inout @property 1179 { 1180 return _iterator._iterator; 1181 } 1182 } 1183 1184 /++ 1185 Field (array) data. 1186 Returns: 1187 Raw data slice. 1188 Constraints: 1189 Field is defined only for contiguous slices. 1190 +/ 1191 auto field()() scope return @trusted @property 1192 { 1193 static assert(kind == Contiguous, "Slice.field is defined only for contiguous slices. Slice kind is " ~ kind.stringof); 1194 static if (is(typeof(_iterator[size_t(0) .. elementCount]))) 1195 { 1196 return _iterator[size_t(0) .. elementCount]; 1197 } 1198 else 1199 { 1200 import mir.ndslice.topology: flattened; 1201 return this.flattened; 1202 } 1203 } 1204 1205 /// ditto 1206 auto field()() scope const return @trusted @property 1207 { 1208 return this.lightConst.field; 1209 } 1210 1211 /// ditto 1212 auto field()() scope immutable return @trusted @property 1213 { 1214 return this.lightImmutable.field; 1215 } 1216 1217 static if (doUnittest) 1218 /// 1219 @safe version(mir_test) unittest 1220 { 1221 auto arr = [1, 2, 3, 4]; 1222 auto sl0 = arr.sliced; 1223 auto sl1 = arr.slicedField; 1224 1225 assert(sl0.field is arr); 1226 assert(sl1.field is arr); 1227 1228 arr = arr[1 .. $]; 1229 sl0 = sl0[1 .. $]; 1230 sl1 = sl1[1 .. $]; 1231 1232 assert(sl0.field is arr); 1233 assert(sl1.field is arr); 1234 assert((cast(const)sl1).field is arr); 1235 ()@trusted{ assert((cast(immutable)sl1).field is arr); }(); 1236 } 1237 1238 /++ 1239 Returns: static array of lengths 1240 See_also: $(LREF .Slice.structure) 1241 +/ 1242 size_t[N] shape()() @trusted @property scope const 1243 { 1244 return _lengths[0 .. N]; 1245 } 1246 1247 static if (doUnittest) 1248 /// Regular slice 1249 @safe @nogc pure nothrow version(mir_test) unittest 1250 { 1251 import mir.ndslice.topology : iota; 1252 assert(iota(3, 4, 5).shape == cast(size_t[3])[3, 4, 5]); 1253 } 1254 1255 static if (doUnittest) 1256 /// Packed slice 1257 @safe @nogc pure nothrow 1258 version(mir_test) unittest 1259 { 1260 import mir.ndslice.topology : pack, iota; 1261 size_t[3] s = [3, 4, 5]; 1262 assert(iota(3, 4, 5, 6, 7).pack!2.shape == s); 1263 } 1264 1265 /++ 1266 Returns: static array of lengths 1267 See_also: $(LREF .Slice.structure) 1268 +/ 1269 ptrdiff_t[N] strides()() @trusted @property scope const 1270 { 1271 static if (N <= S) 1272 return _strides[0 .. N]; 1273 else 1274 { 1275 typeof(return) ret; 1276 static if (kind == Canonical) 1277 { 1278 foreach (i; Iota!S) 1279 ret[i] = _strides[i]; 1280 ret[$-1] = 1; 1281 } 1282 else 1283 { 1284 ret[$ - 1] = _stride!(N - 1); 1285 foreach_reverse (i; Iota!(N - 1)) 1286 ret[i] = ret[i + 1] * _lengths[i + 1]; 1287 } 1288 return ret; 1289 } 1290 } 1291 1292 static if (doUnittest) 1293 /// Regular slice 1294 @safe @nogc pure nothrow 1295 version(mir_test) unittest 1296 { 1297 import mir.ndslice.topology : iota; 1298 size_t[3] s = [20, 5, 1]; 1299 assert(iota(3, 4, 5).strides == s); 1300 } 1301 1302 static if (doUnittest) 1303 /// Modified regular slice 1304 @safe @nogc pure nothrow version(mir_test) unittest 1305 { 1306 import mir.ndslice.topology : pack, iota, universal; 1307 import mir.ndslice.dynamic : reversed, strided, transposed; 1308 assert(iota(3, 4, 50) 1309 .universal 1310 .reversed!2 //makes stride negative 1311 .strided!2(6) //multiplies stride by 6 and changes corresponding length 1312 .transposed!2 //brings dimension `2` to the first position 1313 .strides == cast(ptrdiff_t[3])[-6, 200, 50]); 1314 } 1315 1316 static if (doUnittest) 1317 /// Packed slice 1318 @safe @nogc pure nothrow version(mir_test) unittest 1319 { 1320 import mir.ndslice.topology : pack, iota; 1321 size_t[3] s = [20 * 42, 5 * 42, 1 * 42]; 1322 assert(iota(3, 4, 5, 6, 7) 1323 .pack!2 1324 .strides == s); 1325 } 1326 1327 /++ 1328 Returns: static array of lengths and static array of strides 1329 See_also: $(LREF .Slice.shape) 1330 +/ 1331 Structure!N structure()() @safe @property scope const 1332 { 1333 return typeof(return)(_lengths, strides); 1334 } 1335 1336 static if (doUnittest) 1337 /// Regular slice 1338 @safe @nogc pure nothrow version(mir_test) unittest 1339 { 1340 import mir.ndslice.topology : iota; 1341 assert(iota(3, 4, 5) 1342 .structure == Structure!3([3, 4, 5], [20, 5, 1])); 1343 } 1344 1345 static if (doUnittest) 1346 /// Modified regular slice 1347 @safe @nogc pure nothrow version(mir_test) unittest 1348 { 1349 import mir.ndslice.topology : pack, iota, universal; 1350 import mir.ndslice.dynamic : reversed, strided, transposed; 1351 assert(iota(3, 4, 50) 1352 .universal 1353 .reversed!2 //makes stride negative 1354 .strided!2(6) //multiplies stride by 6 and changes corresponding length 1355 .transposed!2 //brings dimension `2` to the first position 1356 .structure == Structure!3([9, 3, 4], [-6, 200, 50])); 1357 } 1358 1359 static if (doUnittest) 1360 /// Packed slice 1361 @safe @nogc pure nothrow version(mir_test) unittest 1362 { 1363 import mir.ndslice.topology : pack, iota; 1364 assert(iota(3, 4, 5, 6, 7) 1365 .pack!2 1366 .structure == Structure!3([3, 4, 5], [20 * 42, 5 * 42, 1 * 42])); 1367 } 1368 1369 /++ 1370 Save primitive. 1371 +/ 1372 auto save()() scope return inout @property 1373 { 1374 return this; 1375 } 1376 1377 static if (doUnittest) 1378 /// Save range 1379 @safe @nogc pure nothrow version(mir_test) unittest 1380 { 1381 import mir.ndslice.topology : iota; 1382 auto slice = iota(2, 3).save; 1383 } 1384 1385 static if (doUnittest) 1386 /// Pointer type. 1387 @safe pure nothrow version(mir_test) unittest 1388 { 1389 import mir.ndslice.allocation; 1390 //sl type is `Slice!(2, int*)` 1391 auto sl = slice!int(2, 3).save; 1392 } 1393 1394 /++ 1395 Multidimensional `length` property. 1396 Returns: length of the corresponding dimension 1397 See_also: $(LREF .Slice.shape), $(LREF .Slice.structure) 1398 +/ 1399 size_t length(size_t dimension = 0)() @safe @property scope const 1400 if (dimension < N) 1401 { 1402 return _lengths[dimension]; 1403 } 1404 1405 static if (doUnittest) 1406 /// 1407 @safe @nogc pure nothrow version(mir_test) unittest 1408 { 1409 import mir.ndslice.topology : iota; 1410 auto slice = iota(3, 4, 5); 1411 assert(slice.length == 3); 1412 assert(slice.length!0 == 3); 1413 assert(slice.length!1 == 4); 1414 assert(slice.length!2 == 5); 1415 } 1416 1417 alias opDollar = length; 1418 1419 /++ 1420 Multidimensional `stride` property. 1421 Returns: stride of the corresponding dimension 1422 See_also: $(LREF .Slice.structure) 1423 +/ 1424 sizediff_t _stride(size_t dimension = 0)() @safe @property scope const 1425 if (dimension < N) 1426 { 1427 static if (dimension < S) 1428 { 1429 return _strides[dimension]; 1430 } 1431 else 1432 static if (dimension + 1 == N) 1433 { 1434 return 1; 1435 } 1436 else 1437 { 1438 size_t ball = _lengths[$ - 1]; 1439 foreach_reverse(i; Iota!(dimension + 1, N - 1)) 1440 ball *= _lengths[i]; 1441 return ball; 1442 } 1443 1444 } 1445 1446 static if (doUnittest) 1447 /// Regular slice 1448 @safe @nogc pure nothrow version(mir_test) unittest 1449 { 1450 import mir.ndslice.topology : iota; 1451 auto slice = iota(3, 4, 5); 1452 assert(slice._stride == 20); 1453 assert(slice._stride!0 == 20); 1454 assert(slice._stride!1 == 5); 1455 assert(slice._stride!2 == 1); 1456 } 1457 1458 static if (doUnittest) 1459 /// Modified regular slice 1460 @safe @nogc pure nothrow version(mir_test) unittest 1461 { 1462 import mir.ndslice.dynamic : reversed, strided, swapped; 1463 import mir.ndslice.topology : universal, iota; 1464 assert(iota(3, 4, 50) 1465 .universal 1466 .reversed!2 //makes stride negative 1467 .strided!2(6) //multiplies stride by 6 and changes the corresponding length 1468 .swapped!(1, 2) //swaps dimensions `1` and `2` 1469 ._stride!1 == -6); 1470 } 1471 1472 /++ 1473 Multidimensional input range primitive. 1474 +/ 1475 bool empty(size_t dimension = 0)() @safe @property scope const 1476 if (dimension < N) 1477 { 1478 return _lengths[dimension] == 0; 1479 } 1480 1481 static if (N == 1) 1482 { 1483 ///ditto 1484 auto ref front(size_t dimension = 0)() scope return @trusted @property 1485 if (dimension == 0) 1486 { 1487 assert(!empty!dimension); 1488 return *_iterator; 1489 } 1490 1491 ///ditto 1492 auto ref front(size_t dimension = 0)() scope return @trusted @property const 1493 if (dimension == 0) 1494 { 1495 assert(!empty!dimension); 1496 return *_iterator.lightScope; 1497 } 1498 1499 ///ditto 1500 auto ref front(size_t dimension = 0)() scope return @trusted @property immutable 1501 if (dimension == 0) 1502 { 1503 assert(!empty!dimension); 1504 return *_iterator.lightScope; 1505 } 1506 } 1507 else 1508 { 1509 /// ditto 1510 Element!dimension front(size_t dimension = 0)() scope return @property 1511 if (dimension < N) 1512 { 1513 typeof(return)._Structure structure_ = typeof(return)._Structure.init; 1514 1515 foreach (i; Iota!(typeof(return).N)) 1516 { 1517 enum j = i >= dimension ? i + 1 : i; 1518 structure_[0][i] = _lengths[j]; 1519 } 1520 1521 static if (!typeof(return).S || typeof(return).S + 1 == S) 1522 alias s = _strides; 1523 else 1524 auto s = strides; 1525 1526 foreach (i; Iota!(typeof(return).S)) 1527 { 1528 enum j = i >= dimension ? i + 1 : i; 1529 structure_[1][i] = s[j]; 1530 } 1531 1532 return typeof(return)(structure_, _iterator); 1533 } 1534 1535 ///ditto 1536 auto front(size_t dimension = 0)() scope return @trusted @property const 1537 if (dimension < N) 1538 { 1539 assert(!empty!dimension); 1540 return this.lightConst.front!dimension; 1541 } 1542 1543 ///ditto 1544 auto front(size_t dimension = 0)() scope return @trusted @property immutable 1545 if (dimension < N) 1546 { 1547 assert(!empty!dimension); 1548 return this.lightImmutable.front!dimension; 1549 } 1550 } 1551 1552 static if (N == 1 && isMutable!DeepElement && !hasAccessByRef) 1553 { 1554 ///ditto 1555 auto ref front(size_t dimension = 0, T)(T value) scope return @trusted @property 1556 if (dimension == 0) 1557 { 1558 // check assign safety 1559 static auto ref fun(ref DeepElement t, ref T v) @safe 1560 { 1561 return t = v; 1562 } 1563 assert(!empty!dimension); 1564 static if (__traits(compiles, *_iterator = value)) 1565 return *_iterator = value; 1566 else 1567 return _iterator[0] = value; 1568 } 1569 } 1570 1571 ///ditto 1572 static if (N == 1) 1573 auto ref Element!dimension 1574 back(size_t dimension = 0)() scope return @trusted @property 1575 if (dimension < N) 1576 { 1577 assert(!empty!dimension); 1578 return _iterator[backIndex]; 1579 } 1580 else 1581 auto ref Element!dimension 1582 back(size_t dimension = 0)() scope return @trusted @property 1583 if (dimension < N) 1584 { 1585 assert(!empty!dimension); 1586 auto structure_ = typeof(return)._Structure.init; 1587 1588 foreach (i; Iota!(typeof(return).N)) 1589 { 1590 enum j = i >= dimension ? i + 1 : i; 1591 structure_[0][i] = _lengths[j]; 1592 } 1593 1594 static if (!typeof(return).S || typeof(return).S + 1 == S) 1595 alias s =_strides; 1596 else 1597 auto s = strides; 1598 1599 foreach (i; Iota!(typeof(return).S)) 1600 { 1601 enum j = i >= dimension ? i + 1 : i; 1602 structure_[1][i] = s[j]; 1603 } 1604 1605 return typeof(return)(structure_, _iterator + backIndex!dimension); 1606 } 1607 1608 static if (N == 1 && isMutable!DeepElement && !hasAccessByRef) 1609 { 1610 ///ditto 1611 auto ref back(size_t dimension = 0, T)(T value) scope return @trusted @property 1612 if (dimension == 0) 1613 { 1614 // check assign safety 1615 static auto ref fun(ref DeepElement t, ref T v) @safe 1616 { 1617 return t = v; 1618 } 1619 assert(!empty!dimension); 1620 return _iterator[backIndex] = value; 1621 } 1622 } 1623 1624 ///ditto 1625 void popFront(size_t dimension = 0)() @trusted 1626 if (dimension < N && (dimension == 0 || kind != Contiguous)) 1627 { 1628 assert(_lengths[dimension], __FUNCTION__ ~ ": length!" ~ dimension.stringof ~ " should be greater than 0."); 1629 _lengths[dimension]--; 1630 static if ((kind == Contiguous || kind == Canonical) && dimension + 1 == N) 1631 ++_iterator; 1632 else 1633 static if (kind == Canonical || kind == Universal) 1634 _iterator += _strides[dimension]; 1635 else 1636 _iterator += _stride!dimension; 1637 } 1638 1639 ///ditto 1640 void popBack(size_t dimension = 0)() @safe scope 1641 if (dimension < N && (dimension == 0 || kind != Contiguous)) 1642 { 1643 assert(_lengths[dimension], __FUNCTION__ ~ ": length!" ~ dimension.stringof ~ " should be greater than 0."); 1644 --_lengths[dimension]; 1645 } 1646 1647 ///ditto 1648 void popFrontExactly(size_t dimension = 0)(size_t n) @trusted scope 1649 if (dimension < N && (dimension == 0 || kind != Contiguous)) 1650 { 1651 assert(n <= _lengths[dimension], 1652 __FUNCTION__ ~ ": n should be less than or equal to length!" ~ dimension.stringof); 1653 _lengths[dimension] -= n; 1654 _iterator += _stride!dimension * n; 1655 } 1656 1657 ///ditto 1658 void popBackExactly(size_t dimension = 0)(size_t n) @safe scope 1659 if (dimension < N && (dimension == 0 || kind != Contiguous)) 1660 { 1661 assert(n <= _lengths[dimension], 1662 __FUNCTION__ ~ ": n should be less than or equal to length!" ~ dimension.stringof); 1663 _lengths[dimension] -= n; 1664 } 1665 1666 ///ditto 1667 void popFrontN(size_t dimension = 0)(size_t n) @trusted scope 1668 if (dimension < N && (dimension == 0 || kind != Contiguous)) 1669 { 1670 popFrontExactly!dimension(min(n, _lengths[dimension])); 1671 } 1672 1673 ///ditto 1674 void popBackN(size_t dimension = 0)(size_t n) @safe scope 1675 if (dimension < N && (dimension == 0 || kind != Contiguous)) 1676 { 1677 popBackExactly!dimension(min(n, _lengths[dimension])); 1678 } 1679 1680 static if (doUnittest) 1681 /// 1682 @safe @nogc pure nothrow version(mir_test) unittest 1683 { 1684 import std.range.primitives; 1685 import mir.ndslice.topology : iota, canonical; 1686 auto slice = iota(10, 20, 30).canonical; 1687 1688 static assert(isRandomAccessRange!(typeof(slice))); 1689 static assert(hasSlicing!(typeof(slice))); 1690 static assert(hasLength!(typeof(slice))); 1691 1692 assert(slice.shape == cast(size_t[3])[10, 20, 30]); 1693 slice.popFront; 1694 slice.popFront!1; 1695 slice.popBackExactly!2(4); 1696 assert(slice.shape == cast(size_t[3])[9, 19, 26]); 1697 1698 auto matrix = slice.front!1; 1699 assert(matrix.shape == cast(size_t[2])[9, 26]); 1700 1701 auto column = matrix.back!1; 1702 assert(column.shape == cast(size_t[1])[9]); 1703 1704 slice.popFrontExactly!1(slice.length!1); 1705 assert(slice.empty == false); 1706 assert(slice.empty!1 == true); 1707 assert(slice.empty!2 == false); 1708 assert(slice.shape == cast(size_t[3])[9, 0, 26]); 1709 1710 assert(slice.back.front!1.empty); 1711 1712 slice.popFrontN!0(40); 1713 slice.popFrontN!2(40); 1714 assert(slice.shape == cast(size_t[3])[0, 0, 0]); 1715 } 1716 1717 package(mir) ptrdiff_t lastIndex()() @safe @property scope const 1718 { 1719 static if (kind == Contiguous) 1720 { 1721 return elementCount - 1; 1722 } 1723 else 1724 { 1725 auto strides = strides; 1726 ptrdiff_t shift = 0; 1727 foreach(i; Iota!N) 1728 shift += strides[i] * (_lengths[i] - 1); 1729 return shift; 1730 } 1731 } 1732 1733 static if (N > 1) 1734 { 1735 /// Accesses the first deep element of the slice. 1736 auto ref first()() scope return @trusted @property 1737 { 1738 assert(!anyEmpty); 1739 return *_iterator; 1740 } 1741 1742 static if (isMutable!DeepElement && !hasAccessByRef) 1743 ///ditto 1744 auto ref first(T)(T value) scope return @trusted @property 1745 { 1746 assert(!anyEmpty); 1747 static if (__traits(compiles, *_iterator = value)) 1748 return *_iterator = value; 1749 else 1750 return _iterator[0] = value; 1751 } 1752 1753 static if (doUnittest) 1754 /// 1755 @safe pure nothrow @nogc version(mir_test) unittest 1756 { 1757 import mir.ndslice.topology: iota, universal, canonical; 1758 auto f = 5; 1759 assert([2, 3].iota(f).first == f); 1760 } 1761 1762 /// Accesses the last deep element of the slice. 1763 auto ref last()() @trusted scope return @property 1764 { 1765 assert(!anyEmpty); 1766 return _iterator[lastIndex]; 1767 } 1768 1769 static if (isMutable!DeepElement && !hasAccessByRef) 1770 ///ditto 1771 auto ref last(T)(T value) @trusted scope return @property 1772 { 1773 assert(!anyEmpty); 1774 return _iterator[lastIndex] = value; 1775 } 1776 1777 static if (doUnittest) 1778 /// 1779 @safe pure nothrow @nogc version(mir_test) unittest 1780 { 1781 import mir.ndslice.topology: iota; 1782 auto f = 5; 1783 assert([2, 3].iota(f).last == f + 2 * 3 - 1); 1784 } 1785 1786 static if (kind_ != SliceKind.contiguous) 1787 /// Peforms `popFrontAll` for all dimensions 1788 void popFrontAll() 1789 { 1790 assert(!anyEmpty); 1791 foreach(d; Iota!N_) 1792 popFront!d; 1793 } 1794 1795 static if (doUnittest) 1796 /// 1797 @safe pure nothrow version(mir_test) unittest 1798 { 1799 import mir.ndslice.topology: iota, canonical; 1800 auto v = [2, 3].iota.canonical; 1801 v.popFrontAll; 1802 assert(v == [[4, 5]]); 1803 } 1804 1805 static if (kind_ != SliceKind.contiguous) 1806 /// Peforms `popBackAll` for all dimensions 1807 void popBackAll() 1808 { 1809 assert(!anyEmpty); 1810 foreach(d; Iota!N_) 1811 popBack!d; 1812 } 1813 1814 static if (doUnittest) 1815 /// 1816 @safe pure nothrow version(mir_test) unittest 1817 { 1818 import mir.ndslice.topology: iota, canonical; 1819 auto v = [2, 3].iota.canonical; 1820 v.popBackAll; 1821 assert(v == [[0, 1]]); 1822 } 1823 } 1824 else 1825 { 1826 alias first = front; 1827 alias last = back; 1828 alias popFrontAll = popFront; 1829 alias popBackAll = popBack; 1830 } 1831 1832 /+ 1833 Returns: `true` if for any dimension of completely unpacked slice the length equals to `0`, and `false` otherwise. 1834 +/ 1835 private bool anyRUEmpty()() @trusted @property scope const 1836 { 1837 static if (isInstanceOf!(SliceIterator, Iterator)) 1838 { 1839 import mir.ndslice.topology: unpack; 1840 return this.lightScope.unpack.anyRUEmpty; 1841 } 1842 else 1843 return _lengths[0 .. N].anyEmptyShape; 1844 } 1845 1846 1847 /++ 1848 Returns: `true` if for any dimension the length equals to `0`, and `false` otherwise. 1849 +/ 1850 bool anyEmpty()() @trusted @property scope const 1851 { 1852 return _lengths[0 .. N].anyEmptyShape; 1853 } 1854 1855 static if (doUnittest) 1856 /// 1857 @safe pure nothrow @nogc version(mir_test) unittest 1858 { 1859 import mir.ndslice.topology : iota, canonical; 1860 auto s = iota(2, 3).canonical; 1861 assert(!s.anyEmpty); 1862 s.popFrontExactly!1(3); 1863 assert(s.anyEmpty); 1864 } 1865 1866 /++ 1867 Convenience function for backward indexing. 1868 1869 Returns: `this[$-index[0], $-index[1], ..., $-index[N-1]]` 1870 +/ 1871 auto ref backward()(size_t[N] index) scope return 1872 { 1873 foreach (i; Iota!N) 1874 index[i] = _lengths[i] - index[i]; 1875 return this[index]; 1876 } 1877 1878 /// ditto 1879 auto ref backward()(size_t[N] index) scope return const 1880 { 1881 return this.lightConst.backward(index); 1882 } 1883 1884 /// ditto 1885 auto ref backward()(size_t[N] index) scope return const 1886 { 1887 return this.lightConst.backward(index); 1888 } 1889 1890 static if (doUnittest) 1891 /// 1892 @safe @nogc pure nothrow version(mir_test) unittest 1893 { 1894 import mir.ndslice.topology : iota; 1895 auto s = iota(2, 3); 1896 assert(s[$ - 1, $ - 2] == s.backward([1, 2])); 1897 } 1898 1899 /++ 1900 Returns: Total number of elements in a slice 1901 +/ 1902 size_t elementCount()() @safe @property scope const 1903 { 1904 size_t len = 1; 1905 foreach (i; Iota!N) 1906 len *= _lengths[i]; 1907 return len; 1908 } 1909 1910 static if (doUnittest) 1911 /// Regular slice 1912 @safe @nogc pure nothrow version(mir_test) unittest 1913 { 1914 import mir.ndslice.topology : iota; 1915 assert(iota(3, 4, 5).elementCount == 60); 1916 } 1917 1918 1919 static if (doUnittest) 1920 /// Packed slice 1921 @safe @nogc pure nothrow version(mir_test) unittest 1922 { 1923 import mir.ndslice.topology : pack, evertPack, iota; 1924 auto slice = iota(3, 4, 5, 6, 7, 8); 1925 auto p = slice.pack!2; 1926 assert(p.elementCount == 360); 1927 assert(p[0, 0, 0, 0].elementCount == 56); 1928 assert(p.evertPack.elementCount == 56); 1929 } 1930 1931 /++ 1932 Slice selected dimension. 1933 Params: 1934 begin = initial index of the sub-slice (inclusive) 1935 end = final index of the sub-slice (noninclusive) 1936 Returns: ndslice with `length!dimension` equal to `end - begin`. 1937 +/ 1938 auto select(size_t dimension)(size_t begin, size_t end) @trusted 1939 { 1940 static if (kind == Contiguous && dimension) 1941 { 1942 import mir.ndslice.topology: canonical; 1943 auto ret = this.canonical; 1944 } 1945 else 1946 { 1947 auto ret = this; 1948 } 1949 auto len = end - begin; 1950 assert(len <= ret._lengths[dimension]); 1951 ret._lengths[dimension] = len; 1952 ret._iterator += ret._stride!dimension * begin; 1953 return ret; 1954 } 1955 1956 static if (doUnittest) 1957 /// 1958 @safe @nogc pure nothrow version(mir_test) unittest 1959 { 1960 import mir.ndslice.topology : iota; 1961 auto sl = iota(3, 4); 1962 assert(sl.select!1(1, 3) == sl[0 .. $, 1 .. 3]); 1963 } 1964 1965 /++ 1966 Select the first n elements for the dimension. 1967 Params: 1968 dimension = Dimension to slice. 1969 n = count of elements for the dimension 1970 Returns: ndslice with `length!dimension` equal to `n`. 1971 +/ 1972 auto selectFront(size_t dimension)(size_t n) scope return 1973 { 1974 static if (kind == Contiguous && dimension) 1975 { 1976 import mir.ndslice.topology: canonical; 1977 auto ret = this.canonical; 1978 } 1979 else 1980 { 1981 auto ret = this; 1982 } 1983 assert(n <= ret._lengths[dimension]); 1984 ret._lengths[dimension] = n; 1985 return ret; 1986 } 1987 1988 static if (doUnittest) 1989 /// 1990 @safe @nogc pure nothrow version(mir_test) unittest 1991 { 1992 import mir.ndslice.topology : iota; 1993 auto sl = iota(3, 4); 1994 assert(sl.selectFront!1(2) == sl[0 .. $, 0 .. 2]); 1995 } 1996 1997 /++ 1998 Select the last n elements for the dimension. 1999 Params: 2000 dimension = Dimension to slice. 2001 n = count of elements for the dimension 2002 Returns: ndslice with `length!dimension` equal to `n`. 2003 +/ 2004 auto selectBack(size_t dimension)(size_t n) scope return 2005 { 2006 static if (kind == Contiguous && dimension) 2007 { 2008 import mir.ndslice.topology: canonical; 2009 auto ret = this.canonical; 2010 } 2011 else 2012 { 2013 auto ret = this; 2014 } 2015 assert(n <= ret._lengths[dimension]); 2016 ret._iterator += ret._stride!dimension * (ret._lengths[dimension] - n); 2017 ret._lengths[dimension] = n; 2018 return ret; 2019 } 2020 2021 static if (doUnittest) 2022 /// 2023 @safe @nogc pure nothrow version(mir_test) unittest 2024 { 2025 import mir.ndslice.topology : iota; 2026 auto sl = iota(3, 4); 2027 assert(sl.selectBack!1(2) == sl[0 .. $, $ - 2 .. $]); 2028 } 2029 2030 ///ditto 2031 bool opEquals(IteratorR, SliceKind rkind)(auto ref const Slice!(IteratorR, N, rkind) rslice) @trusted scope const 2032 { 2033 static if ( 2034 __traits(isPOD, Iterator) 2035 && __traits(isPOD, IteratorR) 2036 && __traits(compiles, this._iterator == rslice._iterator) 2037 ) 2038 { 2039 if (this._lengths != rslice._lengths) 2040 return false; 2041 if (anyEmpty) 2042 return true; 2043 if (this._iterator == rslice._iterator) 2044 { 2045 auto ls = this.strides; 2046 auto rs = rslice.strides; 2047 foreach (i; Iota!N) 2048 { 2049 if (this._lengths[i] != 1 && ls[i] != rs[i]) 2050 goto L; 2051 } 2052 return true; 2053 } 2054 } 2055 L: 2056 2057 static if (is(Iterator == IotaIterator!UL, UL) && is(IteratorR == Iterator)) 2058 { 2059 return false; 2060 } 2061 else 2062 { 2063 import mir.algorithm.iteration : equal; 2064 return equal(this.lightScope, rslice.lightScope); 2065 } 2066 } 2067 2068 /// ditto 2069 bool opEquals(T)(scope const(T)[] arr) @trusted scope const 2070 { 2071 auto slice = this.lightConst; 2072 if (slice.length != arr.length) 2073 return false; 2074 if (arr.length) do 2075 { 2076 if (slice.front != arr[0]) 2077 return false; 2078 slice.popFront; 2079 arr = arr[1 .. $]; 2080 } 2081 while (arr.length); 2082 return true; 2083 } 2084 2085 static if (doUnittest) 2086 /// 2087 @safe pure nothrow 2088 version(mir_test) unittest 2089 { 2090 auto a = [1, 2, 3, 4].sliced(2, 2); 2091 2092 assert(a != [1, 2, 3, 4, 5, 6].sliced(2, 3)); 2093 assert(a != [[1, 2, 3], [4, 5, 6]]); 2094 2095 assert(a == [1, 2, 3, 4].sliced(2, 2)); 2096 assert(a == [[1, 2], [3, 4]]); 2097 2098 assert(a != [9, 2, 3, 4].sliced(2, 2)); 2099 assert(a != [[9, 2], [3, 4]]); 2100 } 2101 2102 static if (doUnittest) 2103 @safe pure nothrow version(mir_test) unittest 2104 { 2105 import mir.ndslice.allocation: slice; 2106 import mir.ndslice.topology : iota; 2107 assert(iota(2, 3).slice[0 .. $ - 2] == iota([4, 3], 2)[0 .. $ - 4]); 2108 } 2109 2110 /++ 2111 `Slice!(IotaIterator!size_t)` is the basic type for `[a .. b]` syntax for all ndslice based code. 2112 +/ 2113 Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j) @safe scope const 2114 if (dimension < N) 2115 in 2116 { 2117 assert(i <= j, 2118 "Slice.opSlice!" ~ dimension.stringof ~ ": the left opSlice boundary must be less than or equal to the right bound."); 2119 enum errorMsg = ": right opSlice boundary must be less than or equal to the length of the given dimension."; 2120 assert(j <= _lengths[dimension], 2121 "Slice.opSlice!" ~ dimension.stringof ~ errorMsg); 2122 } 2123 do 2124 { 2125 return typeof(return)(j - i, typeof(return).Iterator(i)); 2126 } 2127 2128 /++ 2129 $(BOLD Fully defined index) 2130 +/ 2131 auto ref opIndex()(size_t[N] _indices...) scope return @trusted 2132 { 2133 return _iterator[indexStride(_indices)]; 2134 } 2135 2136 /// ditto 2137 auto ref opIndex()(size_t[N] _indices...) scope return const @trusted 2138 { 2139 static if (is(typeof(_iterator[indexStride(_indices)]))) 2140 return _iterator[indexStride(_indices)]; 2141 else 2142 return .lightConst(.lightScope(_iterator))[indexStride(_indices)]; 2143 } 2144 2145 /// ditto 2146 auto ref opIndex()(size_t[N] _indices...) scope return immutable @trusted 2147 { 2148 static if (is(typeof(_iterator[indexStride(_indices)]))) 2149 return _iterator[indexStride(_indices)]; 2150 else 2151 return .lightImmutable(.lightScope(_iterator))[indexStride(_indices)]; 2152 } 2153 2154 /++ 2155 $(BOLD Partially defined index) 2156 +/ 2157 auto opIndex(size_t I)(size_t[I] _indices...) scope return @trusted 2158 if (I && I < N) 2159 { 2160 enum size_t diff = N - I; 2161 alias Ret = Slice!(Iterator, diff, diff == 1 && kind == Canonical ? Contiguous : kind); 2162 static if (I < S) 2163 return Ret(_lengths[I .. N], _strides[I .. S], _iterator + indexStride(_indices)); 2164 else 2165 return Ret(_lengths[I .. N], _iterator + indexStride(_indices)); 2166 } 2167 2168 /// ditto 2169 auto opIndex(size_t I)(size_t[I] _indices...) scope return const 2170 if (I && I < N) 2171 { 2172 return this.lightConst.opIndex(_indices); 2173 } 2174 2175 /// ditto 2176 auto opIndex(size_t I)(size_t[I] _indices...) scope return immutable 2177 if (I && I < N) 2178 { 2179 return this.lightImmutable.opIndex(_indices); 2180 } 2181 2182 /++ 2183 $(BOLD Partially or fully defined slice.) 2184 +/ 2185 auto opIndex(Slices...)(Slices slices) scope return @trusted 2186 if (isPureSlice!Slices) 2187 { 2188 static if (Slices.length) 2189 { 2190 enum size_t j(size_t n) = n - Filter!(isIndex, Slices[0 .. n]).length; 2191 enum size_t F = PureIndexLength!Slices; 2192 enum size_t S = Slices.length; 2193 static assert(N - F > 0); 2194 size_t stride; 2195 static if (Slices.length == 1) 2196 enum K = kind; 2197 else 2198 static if (kind == Universal || Slices.length == N && isIndex!(Slices[$-1])) 2199 enum K = Universal; 2200 else 2201 static if (Filter!(isIndex, Slices[0 .. $-1]).length == Slices.length - 1 || N - F == 1) 2202 enum K = Contiguous; 2203 else 2204 enum K = Canonical; 2205 alias Ret = Slice!(Iterator, N - F, K); 2206 auto structure_ = Ret._Structure.init; 2207 2208 enum bool shrink = kind == Canonical && slices.length == N; 2209 static if (shrink) 2210 { 2211 { 2212 enum i = Slices.length - 1; 2213 auto slice = slices[i]; 2214 static if (isIndex!(Slices[i])) 2215 { 2216 assert(slice < _lengths[i], "Slice.opIndex: index must be less than length"); 2217 stride += slice; 2218 } 2219 else 2220 { 2221 stride += slice._iterator._index; 2222 structure_[0][j!i] = slice._lengths[0]; 2223 } 2224 } 2225 } 2226 static if (kind == Universal || kind == Canonical) 2227 { 2228 foreach_reverse (i, slice; slices[0 .. $ - shrink]) //static 2229 { 2230 static if (isIndex!(Slices[i])) 2231 { 2232 assert(slice < _lengths[i], "Slice.opIndex: index must be less than length"); 2233 stride += _strides[i] * slice; 2234 } 2235 else 2236 { 2237 stride += _strides[i] * slice._iterator._index; 2238 structure_[0][j!i] = slice._lengths[0]; 2239 structure_[1][j!i] = _strides[i]; 2240 } 2241 } 2242 } 2243 else 2244 { 2245 ptrdiff_t ball = this._stride!(slices.length - 1); 2246 foreach_reverse (i, slice; slices) //static 2247 { 2248 static if (isIndex!(Slices[i])) 2249 { 2250 assert(slice < _lengths[i], "Slice.opIndex: index must be less than length"); 2251 stride += ball * slice; 2252 } 2253 else 2254 { 2255 stride += ball * slice._iterator._index; 2256 structure_[0][j!i] = slice._lengths[0]; 2257 static if (j!i < Ret.S) 2258 structure_[1][j!i] = ball; 2259 } 2260 static if (i) 2261 ball *= _lengths[i]; 2262 } 2263 } 2264 foreach (i; Iota!(Slices.length, N)) 2265 structure_[0][i - F] = _lengths[i]; 2266 foreach (i; Iota!(Slices.length, N)) 2267 static if (Ret.S > i - F) 2268 structure_[1][i - F] = _strides[i]; 2269 2270 return Ret(structure_, _iterator + stride); 2271 } 2272 else 2273 { 2274 return this; 2275 } 2276 } 2277 2278 static if (doUnittest) 2279 /// 2280 pure nothrow version(mir_test) unittest 2281 { 2282 import mir.ndslice.allocation; 2283 auto slice = slice!int(5, 3); 2284 2285 /// Fully defined slice 2286 assert(slice[] == slice); 2287 auto sublice = slice[0..$-2, 1..$]; 2288 2289 /// Partially defined slice 2290 auto row = slice[3]; 2291 auto col = slice[0..$, 1]; 2292 } 2293 2294 /++ 2295 $(BOLD Indexed slice.) 2296 +/ 2297 auto opIndex(Slices...)(scope return Slices slices) scope return 2298 if (isIndexedSlice!Slices) 2299 { 2300 import mir.ndslice.topology: indexed, cartesian; 2301 static if (Slices.length == 1) 2302 alias index = slices[0]; 2303 else 2304 auto index = slices.cartesian; 2305 return this.indexed(index); 2306 } 2307 2308 static if (doUnittest) 2309 /// 2310 @safe pure nothrow version(mir_test) unittest 2311 { 2312 import mir.ndslice.allocation: slice; 2313 auto sli = slice!int(4, 3); 2314 auto idx = slice!(size_t[2])(3); 2315 idx[] = [ 2316 cast(size_t[2])[0, 2], 2317 cast(size_t[2])[3, 1], 2318 cast(size_t[2])[2, 0]]; 2319 2320 // equivalent to: 2321 // import mir.ndslice.topology: indexed; 2322 // sli.indexed(indx)[] = 1; 2323 sli[idx] = 1; 2324 2325 assert(sli == [ 2326 [0, 0, 1], 2327 [0, 0, 0], 2328 [1, 0, 0], 2329 [0, 1, 0], 2330 ]); 2331 2332 foreach (row; sli[[1, 3].sliced]) 2333 row[] += 2; 2334 2335 assert(sli == [ 2336 [0, 0, 1], 2337 [2, 2, 2], // <-- += 2 2338 [1, 0, 0], 2339 [2, 3, 2], // <-- += 2 2340 ]); 2341 } 2342 2343 static if (doUnittest) 2344 /// 2345 @safe pure nothrow version(mir_test) unittest 2346 { 2347 import mir.ndslice.topology: iota; 2348 import mir.ndslice.allocation: slice; 2349 auto sli = slice!int(5, 6); 2350 2351 // equivalent to 2352 // import mir.ndslice.topology: indexed, cartesian; 2353 // auto a = [0, sli.length!0 / 2, sli.length!0 - 1].sliced; 2354 // auto b = [0, sli.length!1 / 2, sli.length!1 - 1].sliced; 2355 // auto c = cartesian(a, b); 2356 // auto minor = sli.indexed(c); 2357 auto minor = sli[[0, $ / 2, $ - 1].sliced, [0, $ / 2, $ - 1].sliced]; 2358 2359 minor[] = iota!int([3, 3], 1); 2360 2361 assert(sli == [ 2362 // ↓ ↓ ↓︎ 2363 [1, 0, 0, 2, 0, 3], // <--- 2364 [0, 0, 0, 0, 0, 0], 2365 [4, 0, 0, 5, 0, 6], // <--- 2366 [0, 0, 0, 0, 0, 0], 2367 [7, 0, 0, 8, 0, 9], // <--- 2368 ]); 2369 } 2370 2371 /++ 2372 Element-wise binary operator overloading. 2373 Returns: 2374 lazy slice of the same kind and the same structure 2375 Note: 2376 Does not allocate neither new slice nor a closure. 2377 +/ 2378 auto opUnary(string op)() scope return 2379 if (op == "*" || op == "~" || op == "-" || op == "+") 2380 { 2381 import mir.ndslice.topology: map; 2382 static if (op == "+") 2383 return this; 2384 else 2385 return this.map!(op ~ "a"); 2386 } 2387 2388 static if (doUnittest) 2389 /// 2390 version(mir_test) unittest 2391 { 2392 import mir.ndslice.topology; 2393 2394 auto payload = [1, 2, 3, 4]; 2395 auto s = iota([payload.length], payload.ptr); // slice of references; 2396 assert(s[1] == payload.ptr + 1); 2397 2398 auto c = *s; // the same as s.map!"*a" 2399 assert(c[1] == *s[1]); 2400 2401 *s[1] = 3; 2402 assert(c[1] == *s[1]); 2403 } 2404 2405 /++ 2406 Element-wise operator overloading for scalars. 2407 Params: 2408 value = a scalar 2409 Returns: 2410 lazy slice of the same kind and the same structure 2411 Note: 2412 Does not allocate neither new slice nor a closure. 2413 +/ 2414 auto opBinary(string op, T)(scope return T value) scope return 2415 if(!isSlice!T) 2416 { 2417 import mir.ndslice.topology: vmap; 2418 return this.vmap(LeftOp!(op, ImplicitlyUnqual!T)(value)); 2419 } 2420 2421 /// ditto 2422 auto opBinaryRight(string op, T)(scope return T value) scope return 2423 if(!isSlice!T) 2424 { 2425 import mir.ndslice.topology: vmap; 2426 return this.vmap(RightOp!(op, ImplicitlyUnqual!T)(value)); 2427 } 2428 2429 static if (doUnittest) 2430 /// 2431 @safe pure nothrow @nogc version(mir_test) unittest 2432 { 2433 import mir.ndslice.topology; 2434 2435 // 0 1 2 3 2436 auto s = iota([4]); 2437 // 0 1 2 0 2438 assert(s % 3 == iota([4]).map!"a % 3"); 2439 // 0 2 4 6 2440 assert(2 * s == iota([4], 0, 2)); 2441 } 2442 2443 static if (doUnittest) 2444 /// 2445 @safe pure nothrow @nogc version(mir_test) unittest 2446 { 2447 import mir.ndslice.topology; 2448 2449 // 0 1 2 3 2450 auto s = iota([4]); 2451 // 0 1 4 9 2452 assert(s ^^ 2.0 == iota([4]).map!"a ^^ 2.0"); 2453 } 2454 2455 /++ 2456 Element-wise operator overloading for slices. 2457 Params: 2458 rhs = a slice of the same shape. 2459 Returns: 2460 lazy slice the same shape that has $(LREF Contiguous) kind 2461 Note: 2462 Binary operator overloading is allowed if both slices are contiguous or one-dimensional. 2463 $(BR) 2464 Does not allocate neither new slice nor a closure. 2465 +/ 2466 auto opBinary(string op, RIterator, size_t RN, SliceKind rkind) 2467 (scope return Slice!(RIterator, RN, rkind) rhs) scope return 2468 if(N == RN && (kind == Contiguous && rkind == Contiguous || N == 1) && op != "~") 2469 { 2470 import mir.ndslice.topology: zip, map; 2471 return zip(this, rhs).map!("a " ~ op ~ " b"); 2472 } 2473 2474 static if (doUnittest) 2475 /// 2476 @safe pure nothrow @nogc version(mir_test) unittest 2477 { 2478 import mir.ndslice.topology: iota, map, zip; 2479 2480 auto s = iota([2, 3]); 2481 auto c = iota([2, 3], 5, 8); 2482 assert(s * s + c == s.map!"a * a".zip(c).map!"a + b"); 2483 } 2484 2485 /++ 2486 Duplicates slice. 2487 Returns: GC-allocated Contiguous mutable slice. 2488 See_also: $(LREF .Slice.idup) 2489 +/ 2490 Slice!(Unqual!DeepElement*, N) 2491 dup()() scope @property 2492 { 2493 if (__ctfe) 2494 { 2495 import mir.ndslice.topology: flattened; 2496 import mir.array.allocation: array; 2497 return this.flattened.array.dup.sliced(this.shape); 2498 } 2499 else 2500 { 2501 import mir.ndslice.allocation: uninitSlice; 2502 import mir.conv: emplaceRef; 2503 alias E = this.DeepElement; 2504 2505 auto result = (() @trusted => this.shape.uninitSlice!(Unqual!E))(); 2506 2507 import mir.algorithm.iteration: each; 2508 each!(emplaceRef!(Unqual!E))(result, this); 2509 2510 return result; 2511 } 2512 } 2513 2514 /// ditto 2515 Slice!(immutable(DeepElement)*, N) 2516 dup()() scope const @property 2517 { 2518 this.lightScope.dup; 2519 } 2520 2521 /// ditto 2522 Slice!(immutable(DeepElement)*, N) 2523 dup()() scope immutable @property 2524 { 2525 this.lightScope.dup; 2526 } 2527 2528 static if (doUnittest) 2529 /// 2530 @safe pure version(mir_test) unittest 2531 { 2532 import mir.ndslice; 2533 auto x = 3.iota!int; 2534 Slice!(immutable(int)*) imm = x.idup; 2535 Slice!(int*) mut = imm.dup; 2536 assert(imm == x); 2537 assert(mut == x); 2538 } 2539 2540 /++ 2541 Duplicates slice. 2542 Returns: GC-allocated Contiguous immutable slice. 2543 See_also: $(LREF .Slice.dup) 2544 +/ 2545 Slice!(immutable(DeepElement)*, N) 2546 idup()() scope @property 2547 { 2548 if (__ctfe) 2549 { 2550 import mir.ndslice.topology: flattened; 2551 import mir.array.allocation: array; 2552 return this.flattened.array.idup.sliced(this.shape); 2553 } 2554 else 2555 { 2556 import mir.ndslice.allocation: uninitSlice; 2557 import mir.conv: emplaceRef; 2558 alias E = this.DeepElement; 2559 2560 auto result = (() @trusted => this.shape.uninitSlice!(Unqual!E))(); 2561 2562 import mir.algorithm.iteration: each; 2563 each!(emplaceRef!(immutable E))(result, this); 2564 alias R = typeof(return); 2565 return (() @trusted => cast(R) result)(); 2566 } 2567 } 2568 2569 /// ditto 2570 Slice!(immutable(DeepElement)*, N) 2571 idup()() scope const @property 2572 { 2573 this.lightScope.idup; 2574 } 2575 2576 /// ditto 2577 Slice!(immutable(DeepElement)*, N) 2578 idup()() scope immutable @property 2579 { 2580 this.lightScope.idup; 2581 } 2582 2583 static if (doUnittest) 2584 /// 2585 @safe pure version(mir_test) unittest 2586 { 2587 import mir.ndslice; 2588 auto x = 3.iota!int; 2589 Slice!(int*) mut = x.dup; 2590 Slice!(immutable(int)*) imm = mut.idup; 2591 assert(imm == x); 2592 assert(mut == x); 2593 } 2594 2595 /++ 2596 Provides the index location of a slice, taking into account 2597 `Slice._strides`. Similar to `Slice.indexStride`, except the slice is 2598 indexed by a value. Called by `Slice.accessFlat`. 2599 2600 Params: 2601 n = location in slice 2602 Returns: 2603 location in slice, adjusted for `Slice._strides` 2604 See_also: 2605 $(SUBREF topology, flattened), 2606 $(LREF .Slice.indexStride), 2607 $(LREF .Slice.accessFlat) 2608 +/ 2609 private 2610 ptrdiff_t indexStrideValue(ptrdiff_t n) @safe scope const 2611 { 2612 assert(n < elementCount, "indexStrideValue: n must be less than elementCount"); 2613 assert(n >= 0, "indexStrideValue: n must be greater than or equal to zero"); 2614 2615 static if (kind == Contiguous) { 2616 return n; 2617 } else { 2618 ptrdiff_t _shift; 2619 foreach_reverse (i; Iota!(1, N)) 2620 { 2621 immutable v = n / ptrdiff_t(_lengths[i]); 2622 n %= ptrdiff_t(_lengths[i]); 2623 static if (i == S) 2624 _shift += n; 2625 else 2626 _shift += n * _strides[i]; 2627 n = v; 2628 } 2629 _shift += n * _strides[0]; 2630 return _shift; 2631 } 2632 } 2633 2634 /++ 2635 Provides access to a slice as if it were `flattened`. 2636 2637 Params: 2638 index = location in slice 2639 Returns: 2640 value of flattened slice at `index` 2641 See_also: $(SUBREF topology, flattened) 2642 +/ 2643 auto ref accessFlat(size_t index) scope return @trusted 2644 { 2645 return _iterator[indexStrideValue(index)]; 2646 } 2647 2648 /// 2649 version(mir_test) 2650 @safe pure @nogc nothrow 2651 unittest 2652 { 2653 import mir.ndslice.topology: iota, flattened; 2654 2655 auto x = iota(2, 3, 4); 2656 assert(x.accessFlat(9) == x.flattened[9]); 2657 } 2658 2659 static if (isMutable!DeepElement) 2660 { 2661 private void opIndexOpAssignImplSlice(string op, RIterator, size_t RN, SliceKind rkind) 2662 (Slice!(RIterator, RN, rkind) value) scope 2663 { 2664 static if (N > 1 && RN == N && kind == Contiguous && rkind == Contiguous) 2665 { 2666 import mir.ndslice.topology : flattened; 2667 this.flattened.opIndexOpAssignImplSlice!op(value.flattened); 2668 } 2669 else 2670 { 2671 auto ls = this; 2672 do 2673 { 2674 static if (N > RN) 2675 { 2676 ls.front.opIndexOpAssignImplSlice!op(value); 2677 } 2678 else 2679 { 2680 static if (ls.N == 1) 2681 { 2682 static if (isInstanceOf!(SliceIterator, Iterator)) 2683 { 2684 static if (isSlice!(typeof(value.front))) 2685 ls.front.opIndexOpAssignImplSlice!op(value.front); 2686 else 2687 static if (isDynamicArray!(typeof(value.front))) 2688 ls.front.opIndexOpAssignImplSlice!op(value.front); 2689 else 2690 ls.front.opIndexOpAssignImplValue!op(value.front); 2691 } 2692 else 2693 static if (op == "^^" && isFloatingPoint!(typeof(ls.front)) && isFloatingPoint!(typeof(value.front))) 2694 { 2695 import mir.math.common: pow; 2696 ls.front = pow(ls.front, value.front); 2697 } 2698 else 2699 mixin("ls.front " ~ op ~ "= value.front;"); 2700 } 2701 else 2702 static if (RN == 1) 2703 ls.front.opIndexOpAssignImplValue!op(value.front); 2704 else 2705 ls.front.opIndexOpAssignImplSlice!op(value.front); 2706 value.popFront; 2707 } 2708 ls.popFront; 2709 } 2710 while (ls._lengths[0]); 2711 } 2712 } 2713 2714 /++ 2715 Assignment of a value of `Slice` type to a $(B fully defined slice). 2716 +/ 2717 void opIndexAssign(RIterator, size_t RN, SliceKind rkind, Slices...) 2718 (Slice!(RIterator, RN, rkind) value, Slices slices) scope return 2719 if (isFullPureSlice!Slices || isIndexedSlice!Slices) 2720 { 2721 auto sl = this.lightScope.opIndex(slices); 2722 assert(_checkAssignLengths(sl, value)); 2723 if(!sl.anyRUEmpty) 2724 sl.opIndexOpAssignImplSlice!""(value); 2725 } 2726 2727 static if (doUnittest) 2728 /// 2729 @safe pure nothrow version(mir_test) unittest 2730 { 2731 import mir.ndslice.allocation; 2732 auto a = slice!int(2, 3); 2733 auto b = [1, 2, 3, 4].sliced(2, 2); 2734 2735 a[0..$, 0..$-1] = b; 2736 assert(a == [[1, 2, 0], [3, 4, 0]]); 2737 2738 // fills both rows with b[0] 2739 a[0..$, 0..$-1] = b[0]; 2740 assert(a == [[1, 2, 0], [1, 2, 0]]); 2741 2742 a[1, 0..$-1] = b[1]; 2743 assert(a[1] == [3, 4, 0]); 2744 2745 a[1, 0..$-1][] = b[0]; 2746 assert(a[1] == [1, 2, 0]); 2747 } 2748 2749 static if (doUnittest) 2750 /// Left slice is packed 2751 @safe pure nothrow version(mir_test) unittest 2752 { 2753 import mir.ndslice.topology : blocks, iota; 2754 import mir.ndslice.allocation : slice; 2755 auto a = slice!int(4, 4); 2756 a.blocks(2, 2)[] = iota!int(2, 2); 2757 2758 assert(a == 2759 [[0, 0, 1, 1], 2760 [0, 0, 1, 1], 2761 [2, 2, 3, 3], 2762 [2, 2, 3, 3]]); 2763 } 2764 2765 static if (doUnittest) 2766 /// Both slices are packed 2767 @safe pure nothrow version(mir_test) unittest 2768 { 2769 import mir.ndslice.topology : blocks, iota, pack; 2770 import mir.ndslice.allocation : slice; 2771 auto a = slice!int(4, 4); 2772 a.blocks(2, 2)[] = iota!int(2, 2, 2).pack!1; 2773 2774 assert(a == 2775 [[0, 1, 2, 3], 2776 [0, 1, 2, 3], 2777 [4, 5, 6, 7], 2778 [4, 5, 6, 7]]); 2779 } 2780 2781 void opIndexOpAssignImplArray(string op, T, Slices...)(T[] value) scope 2782 { 2783 auto ls = this; 2784 assert(ls.length == value.length, __FUNCTION__ ~ ": argument must have the same length."); 2785 static if (N == 1) 2786 { 2787 do 2788 { 2789 static if (ls.N == 1) 2790 { 2791 static if (isInstanceOf!(SliceIterator, Iterator)) 2792 { 2793 static if (isSlice!(typeof(value[0]))) 2794 ls.front.opIndexOpAssignImplSlice!op(value[0]); 2795 else 2796 static if (isDynamicArray!(typeof(value[0]))) 2797 ls.front.opIndexOpAssignImplSlice!op(value[0]); 2798 else 2799 ls.front.opIndexOpAssignImplValue!op(value[0]); 2800 } 2801 else 2802 static if (op == "^^" && isFloatingPoint!(typeof(ls.front)) && isFloatingPoint!(typeof(value[0]))) 2803 { 2804 import mir.math.common: pow; 2805 ls.front = pow(ls.front, value[0]); 2806 } 2807 else 2808 mixin("ls.front " ~ op ~ "= value[0];"); 2809 } 2810 else 2811 mixin("ls.front[] " ~ op ~ "= value[0];"); 2812 value = value[1 .. $]; 2813 ls.popFront; 2814 } 2815 while (ls.length); 2816 } 2817 else 2818 static if (N == DynamicArrayDimensionsCount!(T[])) 2819 { 2820 do 2821 { 2822 ls.front.opIndexOpAssignImplArray!op(value[0]); 2823 value = value[1 .. $]; 2824 ls.popFront; 2825 } 2826 while (ls.length); 2827 } 2828 else 2829 { 2830 do 2831 { 2832 ls.front.opIndexOpAssignImplArray!op(value); 2833 ls.popFront; 2834 } 2835 while (ls.length); 2836 } 2837 } 2838 2839 /++ 2840 Assignment of a regular multidimensional array to a $(B fully defined slice). 2841 +/ 2842 void opIndexAssign(T, Slices...)(T[] value, Slices slices) scope return 2843 if ((isFullPureSlice!Slices || isIndexedSlice!Slices) 2844 && (!isDynamicArray!DeepElement || isDynamicArray!T) 2845 && DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= typeof(this.opIndex(slices)).N) 2846 { 2847 auto sl = this.lightScope.opIndex(slices); 2848 sl.opIndexOpAssignImplArray!""(value); 2849 } 2850 2851 static if (doUnittest) 2852 /// 2853 pure nothrow version(mir_test) unittest 2854 { 2855 import mir.ndslice.allocation; 2856 auto a = slice!int(2, 3); 2857 auto b = [[1, 2], [3, 4]]; 2858 2859 a[] = [[1, 2, 3], [4, 5, 6]]; 2860 assert(a == [[1, 2, 3], [4, 5, 6]]); 2861 2862 a[0..$, 0..$-1] = [[1, 2], [3, 4]]; 2863 assert(a == [[1, 2, 3], [3, 4, 6]]); 2864 2865 a[0..$, 0..$-1] = [1, 2]; 2866 assert(a == [[1, 2, 3], [1, 2, 6]]); 2867 2868 a[1, 0..$-1] = [3, 4]; 2869 assert(a[1] == [3, 4, 6]); 2870 2871 a[1, 0..$-1][] = [3, 4]; 2872 assert(a[1] == [3, 4, 6]); 2873 } 2874 2875 static if (doUnittest) 2876 /// Packed slices 2877 pure nothrow version(mir_test) unittest 2878 { 2879 import mir.ndslice.allocation : slice; 2880 import mir.ndslice.topology : blocks; 2881 auto a = slice!int(4, 4); 2882 a.blocks(2, 2)[] = [[0, 1], [2, 3]]; 2883 2884 assert(a == 2885 [[0, 0, 1, 1], 2886 [0, 0, 1, 1], 2887 [2, 2, 3, 3], 2888 [2, 2, 3, 3]]); 2889 } 2890 2891 2892 private void opIndexOpAssignImplConcatenation(string op, T)(T value) scope 2893 { 2894 auto sl = this; 2895 static if (concatenationDimension!T) 2896 { 2897 if (!sl.empty) do 2898 { 2899 static if (op == "") 2900 sl.front.opIndexAssign(value.front); 2901 else 2902 sl.front.opIndexOpAssign!op(value.front); 2903 value.popFront; 2904 sl.popFront; 2905 } 2906 while(!sl.empty); 2907 } 2908 else 2909 { 2910 foreach (ref slice; value._slices) 2911 { 2912 static if (op == "") 2913 sl[0 .. slice.length].opIndexAssign(slice); 2914 else 2915 sl[0 .. slice.length].opIndexOpAssign!op(slice); 2916 2917 sl = sl[slice.length .. $]; 2918 } 2919 assert(sl.empty); 2920 } 2921 } 2922 2923 /// 2924 void opIndexAssign(T, Slices...)(T concatenation, Slices slices) scope return 2925 if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T) 2926 { 2927 auto sl = this.lightScope.opIndex(slices); 2928 static assert(typeof(sl).N == T.N, "incompatible dimension count"); 2929 sl.opIndexOpAssignImplConcatenation!""(concatenation); 2930 } 2931 2932 static if (!isNumeric!DeepElement) 2933 /++ 2934 Assignment of a value (e.g. a number) to a $(B fully defined slice). 2935 +/ 2936 void opIndexAssign(T, Slices...)(T value, Slices slices) scope 2937 if ((isFullPureSlice!Slices || isIndexedSlice!Slices) 2938 && (!isDynamicArray!T || isDynamicArray!DeepElement) 2939 && DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement 2940 && !isSlice!T 2941 && !isConcatenation!T) 2942 { 2943 auto sl = this.lightScope.opIndex(slices); 2944 if(!sl.anyRUEmpty) 2945 sl.opIndexOpAssignImplValue!""(value); 2946 } 2947 else 2948 void opIndexAssign(Slices...)(DeepElement value, Slices slices) scope 2949 if (isFullPureSlice!Slices || isIndexedSlice!Slices) 2950 { 2951 auto sl = this.lightScope.opIndex(slices); 2952 if(!sl.anyRUEmpty) 2953 sl.opIndexOpAssignImplValue!""(value); 2954 } 2955 2956 static if (doUnittest) 2957 /// 2958 @safe pure nothrow 2959 version(mir_test) unittest 2960 { 2961 import mir.ndslice.allocation; 2962 auto a = slice!int(2, 3); 2963 2964 a[] = 9; 2965 assert(a == [[9, 9, 9], [9, 9, 9]]); 2966 2967 a[0..$, 0..$-1] = 1; 2968 assert(a == [[1, 1, 9], [1, 1, 9]]); 2969 2970 a[0..$, 0..$-1] = 2; 2971 assert(a == [[2, 2, 9], [2, 2, 9]]); 2972 2973 a[1, 0..$-1] = 3; 2974 //assert(a[1] == [3, 3, 9]); 2975 2976 a[1, 0..$-1] = 4; 2977 //assert(a[1] == [4, 4, 9]); 2978 2979 a[1, 0..$-1][] = 5; 2980 2981 assert(a[1] == [5, 5, 9]); 2982 } 2983 2984 static if (doUnittest) 2985 /// Packed slices have the same behavior. 2986 @safe pure nothrow version(mir_test) unittest 2987 { 2988 import mir.ndslice.allocation; 2989 import mir.ndslice.topology : pack; 2990 auto a = slice!int(2, 3).pack!1; 2991 2992 a[] = 9; 2993 //assert(a == [[9, 9, 9], [9, 9, 9]]); 2994 } 2995 2996 /++ 2997 Assignment of a value (e.g. a number) to a $(B fully defined index). 2998 +/ 2999 auto ref opIndexAssign(T)(T value, size_t[N] _indices...) scope return @trusted 3000 { 3001 // check assign safety 3002 static auto ref fun(ref DeepElement t, ref T v) @safe 3003 { 3004 return t = v; 3005 } 3006 return _iterator[indexStride(_indices)] = value; 3007 } 3008 ///ditto 3009 auto ref opIndexAssign()(DeepElement value, size_t[N] _indices...) scope return @trusted 3010 { 3011 import mir.functional: forward; 3012 // check assign safety 3013 static auto ref fun(ref DeepElement t, ref DeepElement v) @safe 3014 { 3015 return t = v; 3016 } 3017 return _iterator[indexStride(_indices)] = forward!value; 3018 } 3019 3020 static if (doUnittest) 3021 /// 3022 @safe pure nothrow version(mir_test) unittest 3023 { 3024 import mir.ndslice.allocation; 3025 auto a = slice!int(2, 3); 3026 3027 a[1, 2] = 3; 3028 assert(a[1, 2] == 3); 3029 } 3030 3031 static if (doUnittest) 3032 @safe pure nothrow version(mir_test) unittest 3033 { 3034 auto a = new int[6].sliced(2, 3); 3035 3036 a[[1, 2]] = 3; 3037 assert(a[[1, 2]] == 3); 3038 } 3039 3040 /++ 3041 Op Assignment `op=` of a value (e.g. a number) to a $(B fully defined index). 3042 +/ 3043 auto ref opIndexOpAssign(string op, T)(T value, size_t[N] _indices...) scope return @trusted 3044 { 3045 // check op safety 3046 static auto ref fun(ref DeepElement t, ref T v) @safe 3047 { 3048 return mixin(`t` ~ op ~ `= v`); 3049 } 3050 auto str = indexStride(_indices); 3051 static if (op == "^^" && isFloatingPoint!DeepElement && isFloatingPoint!(typeof(value))) 3052 { 3053 import mir.math.common: pow; 3054 _iterator[str] = pow(_iterator[str], value); 3055 } 3056 else 3057 return mixin (`_iterator[str] ` ~ op ~ `= value`); 3058 } 3059 3060 static if (doUnittest) 3061 /// 3062 @safe pure nothrow version(mir_test) unittest 3063 { 3064 import mir.ndslice.allocation; 3065 auto a = slice!int(2, 3); 3066 3067 a[1, 2] += 3; 3068 assert(a[1, 2] == 3); 3069 } 3070 3071 static if (doUnittest) 3072 @safe pure nothrow version(mir_test) unittest 3073 { 3074 auto a = new int[6].sliced(2, 3); 3075 3076 a[[1, 2]] += 3; 3077 assert(a[[1, 2]] == 3); 3078 } 3079 3080 /++ 3081 Op Assignment `op=` of a value of `Slice` type to a $(B fully defined slice). 3082 +/ 3083 void opIndexOpAssign(string op, RIterator, SliceKind rkind, size_t RN, Slices...) 3084 (Slice!(RIterator, RN, rkind) value, Slices slices) scope return 3085 if (isFullPureSlice!Slices || isIndexedSlice!Slices) 3086 { 3087 auto sl = this.lightScope.opIndex(slices); 3088 assert(_checkAssignLengths(sl, value)); 3089 if(!sl.anyRUEmpty) 3090 sl.opIndexOpAssignImplSlice!op(value); 3091 } 3092 3093 static if (doUnittest) 3094 /// 3095 @safe pure nothrow version(mir_test) unittest 3096 { 3097 import mir.ndslice.allocation; 3098 auto a = slice!int(2, 3); 3099 auto b = [1, 2, 3, 4].sliced(2, 2); 3100 3101 a[0..$, 0..$-1] += b; 3102 assert(a == [[1, 2, 0], [3, 4, 0]]); 3103 3104 a[0..$, 0..$-1] += b[0]; 3105 assert(a == [[2, 4, 0], [4, 6, 0]]); 3106 3107 a[1, 0..$-1] += b[1]; 3108 assert(a[1] == [7, 10, 0]); 3109 3110 a[1, 0..$-1][] += b[0]; 3111 assert(a[1] == [8, 12, 0]); 3112 } 3113 3114 static if (doUnittest) 3115 /// Left slice is packed 3116 @safe pure nothrow version(mir_test) unittest 3117 { 3118 import mir.ndslice.allocation : slice; 3119 import mir.ndslice.topology : blocks, iota; 3120 auto a = slice!size_t(4, 4); 3121 a.blocks(2, 2)[] += iota(2, 2); 3122 3123 assert(a == 3124 [[0, 0, 1, 1], 3125 [0, 0, 1, 1], 3126 [2, 2, 3, 3], 3127 [2, 2, 3, 3]]); 3128 } 3129 3130 static if (doUnittest) 3131 /// Both slices are packed 3132 @safe pure nothrow version(mir_test) unittest 3133 { 3134 import mir.ndslice.allocation : slice; 3135 import mir.ndslice.topology : blocks, iota, pack; 3136 auto a = slice!size_t(4, 4); 3137 a.blocks(2, 2)[] += iota(2, 2, 2).pack!1; 3138 3139 assert(a == 3140 [[0, 1, 2, 3], 3141 [0, 1, 2, 3], 3142 [4, 5, 6, 7], 3143 [4, 5, 6, 7]]); 3144 } 3145 3146 /++ 3147 Op Assignment `op=` of a regular multidimensional array to a $(B fully defined slice). 3148 +/ 3149 void opIndexOpAssign(string op, T, Slices...)(T[] value, Slices slices) scope return 3150 if (isFullPureSlice!Slices 3151 && (!isDynamicArray!DeepElement || isDynamicArray!T) 3152 && DynamicArrayDimensionsCount!(T[]) - DynamicArrayDimensionsCount!DeepElement <= typeof(this.opIndex(slices)).N) 3153 { 3154 auto sl = this.lightScope.opIndex(slices); 3155 sl.opIndexOpAssignImplArray!op(value); 3156 } 3157 3158 static if (doUnittest) 3159 /// 3160 @safe pure nothrow version(mir_test) unittest 3161 { 3162 import mir.ndslice.allocation : slice; 3163 auto a = slice!int(2, 3); 3164 3165 a[0..$, 0..$-1] += [[1, 2], [3, 4]]; 3166 assert(a == [[1, 2, 0], [3, 4, 0]]); 3167 3168 a[0..$, 0..$-1] += [1, 2]; 3169 assert(a == [[2, 4, 0], [4, 6, 0]]); 3170 3171 a[1, 0..$-1] += [3, 4]; 3172 assert(a[1] == [7, 10, 0]); 3173 3174 a[1, 0..$-1][] += [1, 2]; 3175 assert(a[1] == [8, 12, 0]); 3176 } 3177 3178 static if (doUnittest) 3179 /// Packed slices 3180 @safe pure nothrow 3181 version(mir_test) unittest 3182 { 3183 import mir.ndslice.allocation : slice; 3184 import mir.ndslice.topology : blocks; 3185 auto a = slice!int(4, 4); 3186 a.blocks(2, 2)[] += [[0, 1], [2, 3]]; 3187 3188 assert(a == 3189 [[0, 0, 1, 1], 3190 [0, 0, 1, 1], 3191 [2, 2, 3, 3], 3192 [2, 2, 3, 3]]); 3193 } 3194 3195 private void opIndexOpAssignImplValue(string op, T)(T value) scope return 3196 { 3197 static if (N > 1 && kind == Contiguous) 3198 { 3199 import mir.ndslice.topology : flattened; 3200 this.flattened.opIndexOpAssignImplValue!op(value); 3201 } 3202 else 3203 { 3204 auto ls = this; 3205 do 3206 { 3207 static if (N == 1) 3208 { 3209 static if (isInstanceOf!(SliceIterator, Iterator)) 3210 ls.front.opIndexOpAssignImplValue!op(value); 3211 else 3212 mixin (`ls.front ` ~ op ~ `= value;`); 3213 } 3214 else 3215 ls.front.opIndexOpAssignImplValue!op(value); 3216 ls.popFront; 3217 } 3218 while(ls._lengths[0]); 3219 } 3220 } 3221 3222 /++ 3223 Op Assignment `op=` of a value (e.g. a number) to a $(B fully defined slice). 3224 +/ 3225 void opIndexOpAssign(string op, T, Slices...)(T value, Slices slices) scope return 3226 if ((isFullPureSlice!Slices || isIndexedSlice!Slices) 3227 && (!isDynamicArray!T || isDynamicArray!DeepElement) 3228 && DynamicArrayDimensionsCount!T == DynamicArrayDimensionsCount!DeepElement 3229 && !isSlice!T 3230 && !isConcatenation!T) 3231 { 3232 auto sl = this.lightScope.opIndex(slices); 3233 if(!sl.anyRUEmpty) 3234 sl.opIndexOpAssignImplValue!op(value); 3235 } 3236 3237 static if (doUnittest) 3238 /// 3239 @safe pure nothrow version(mir_test) unittest 3240 { 3241 import mir.ndslice.allocation; 3242 auto a = slice!int(2, 3); 3243 3244 a[] += 1; 3245 assert(a == [[1, 1, 1], [1, 1, 1]]); 3246 3247 a[0..$, 0..$-1] += 2; 3248 assert(a == [[3, 3, 1], [3, 3, 1]]); 3249 3250 a[1, 0..$-1] += 3; 3251 assert(a[1] == [6, 6, 1]); 3252 } 3253 3254 /// 3255 void opIndexOpAssign(string op,T, Slices...)(T concatenation, Slices slices) scope return 3256 if ((isFullPureSlice!Slices || isIndexedSlice!Slices) && isConcatenation!T) 3257 { 3258 auto sl = this.lightScope.opIndex(slices); 3259 static assert(typeof(sl).N == concatenation.N); 3260 sl.opIndexOpAssignImplConcatenation!op(concatenation); 3261 } 3262 3263 static if (doUnittest) 3264 /// Packed slices have the same behavior. 3265 @safe pure nothrow version(mir_test) unittest 3266 { 3267 import mir.ndslice.allocation; 3268 import mir.ndslice.topology : pack; 3269 auto a = slice!int(2, 3).pack!1; 3270 3271 a[] += 9; 3272 assert(a == [[9, 9, 9], [9, 9, 9]]); 3273 } 3274 3275 3276 /++ 3277 Increment `++` and Decrement `--` operators for a $(B fully defined index). 3278 +/ 3279 auto ref opIndexUnary(string op)(size_t[N] _indices...) scope return 3280 @trusted 3281 // @@@workaround@@@ for Issue 16473 3282 //if (op == `++` || op == `--`) 3283 { 3284 // check op safety 3285 static auto ref fun(DeepElement t) @safe 3286 { 3287 return mixin(op ~ `t`); 3288 } 3289 return mixin (op ~ `_iterator[indexStride(_indices)]`); 3290 } 3291 3292 static if (doUnittest) 3293 /// 3294 @safe pure nothrow version(mir_test) unittest 3295 { 3296 import mir.ndslice.allocation; 3297 auto a = slice!int(2, 3); 3298 3299 ++a[1, 2]; 3300 assert(a[1, 2] == 1); 3301 } 3302 3303 // Issue 16473 3304 static if (doUnittest) 3305 @safe pure nothrow version(mir_test) unittest 3306 { 3307 import mir.ndslice.allocation; 3308 auto sl = slice!double(2, 5); 3309 auto d = -sl[0, 1]; 3310 } 3311 3312 static if (doUnittest) 3313 @safe pure nothrow version(mir_test) unittest 3314 { 3315 auto a = new int[6].sliced(2, 3); 3316 3317 ++a[[1, 2]]; 3318 assert(a[[1, 2]] == 1); 3319 } 3320 3321 private void opIndexUnaryImpl(string op, Slices...)(Slices slices) scope 3322 { 3323 auto ls = this; 3324 do 3325 { 3326 static if (N == 1) 3327 { 3328 static if (isInstanceOf!(SliceIterator, Iterator)) 3329 ls.front.opIndexUnaryImpl!op; 3330 else 3331 mixin (op ~ `ls.front;`); 3332 } 3333 else 3334 ls.front.opIndexUnaryImpl!op; 3335 ls.popFront; 3336 } 3337 while(ls._lengths[0]); 3338 } 3339 3340 /++ 3341 Increment `++` and Decrement `--` operators for a $(B fully defined slice). 3342 +/ 3343 void opIndexUnary(string op, Slices...)(Slices slices) scope return 3344 if (isFullPureSlice!Slices && (op == `++` || op == `--`)) 3345 { 3346 auto sl = this.lightScope.opIndex(slices); 3347 if (!sl.anyRUEmpty) 3348 sl.opIndexUnaryImpl!op; 3349 } 3350 3351 static if (doUnittest) 3352 /// 3353 @safe pure nothrow 3354 version(mir_test) unittest 3355 { 3356 import mir.ndslice.allocation; 3357 auto a = slice!int(2, 3); 3358 3359 ++a[]; 3360 assert(a == [[1, 1, 1], [1, 1, 1]]); 3361 3362 --a[1, 0..$-1]; 3363 3364 assert(a[1] == [0, 0, 1]); 3365 } 3366 } 3367 } 3368 3369 /// ditto 3370 alias Slice = mir_slice; 3371 3372 /++ 3373 Slicing, indexing, and arithmetic operations. 3374 +/ 3375 pure nothrow version(mir_test) unittest 3376 { 3377 import mir.ndslice.allocation; 3378 import mir.ndslice.dynamic : transposed; 3379 import mir.ndslice.topology : iota, universal; 3380 auto tensor = iota(3, 4, 5).slice; 3381 3382 assert(tensor[1, 2] == tensor[1][2]); 3383 assert(tensor[1, 2, 3] == tensor[1][2][3]); 3384 3385 assert( tensor[0..$, 0..$, 4] == tensor.universal.transposed!2[4]); 3386 assert(&tensor[0..$, 0..$, 4][1, 2] is &tensor[1, 2, 4]); 3387 3388 tensor[1, 2, 3]++; //`opIndex` returns value by reference. 3389 --tensor[1, 2, 3]; //`opUnary` 3390 3391 ++tensor[]; 3392 tensor[] -= 1; 3393 3394 // `opIndexAssing` accepts only fully defined indices and slices. 3395 // Use an additional empty slice `[]`. 3396 static assert(!__traits(compiles, tensor[0 .. 2] *= 2)); 3397 3398 tensor[0 .. 2][] *= 2; //OK, empty slice 3399 tensor[0 .. 2, 3, 0..$] /= 2; //OK, 3 index or slice positions are defined. 3400 3401 //fully defined index may be replaced by a static array 3402 size_t[3] index = [1, 2, 3]; 3403 assert(tensor[index] == tensor[1, 2, 3]); 3404 } 3405 3406 /++ 3407 Operations with rvalue slices. 3408 +/ 3409 pure nothrow version(mir_test) unittest 3410 { 3411 import mir.ndslice.allocation; 3412 import mir.ndslice.topology: universal; 3413 import mir.ndslice.dynamic: transposed, everted; 3414 3415 auto tensor = slice!int(3, 4, 5).universal; 3416 auto matrix = slice!int(3, 4).universal; 3417 auto vector = slice!int(3); 3418 3419 foreach (i; 0..3) 3420 vector[i] = i; 3421 3422 // fills matrix columns 3423 matrix.transposed[] = vector; 3424 3425 // fills tensor with vector 3426 // transposed tensor shape is (4, 5, 3) 3427 // vector shape is ( 3) 3428 tensor.transposed!(1, 2)[] = vector; 3429 3430 // transposed tensor shape is (5, 3, 4) 3431 // matrix shape is ( 3, 4) 3432 tensor.transposed!2[] += matrix; 3433 3434 // transposed tensor shape is (5, 4, 3) 3435 // transposed matrix shape is ( 4, 3) 3436 tensor.everted[] ^= matrix.transposed; // XOR 3437 } 3438 3439 /++ 3440 Creating a slice from text. 3441 See also $(MREF std, format). 3442 +/ 3443 version(mir_test) unittest 3444 { 3445 import mir.algorithm.iteration: filter, all; 3446 import mir.array.allocation; 3447 import mir.exception; 3448 import mir.functional: not; 3449 import mir.ndslice.allocation; 3450 import mir.parse; 3451 import mir.primitives: empty; 3452 3453 import std.algorithm: map; 3454 import std..string: lineSplitter, split; 3455 3456 // std.functional, std.string, std.range; 3457 3458 Slice!(int*, 2) toMatrix(string str) 3459 { 3460 string[][] data = str.lineSplitter.filter!(not!empty).map!split.array; 3461 3462 size_t rows = data .length.enforce!"empty input"; 3463 size_t columns = data[0].length.enforce!"empty first row"; 3464 3465 data.all!(a => a.length == columns).enforce!"rows have different lengths"; 3466 auto slice = slice!int(rows, columns); 3467 foreach (i, line; data) 3468 foreach (j, num; line) 3469 slice[i, j] = num.fromString!int; 3470 return slice; 3471 } 3472 3473 auto input = "\r1 2 3\r\n 4 5 6\n"; 3474 3475 auto matrix = toMatrix(input); 3476 assert(matrix == [[1, 2, 3], [4, 5, 6]]); 3477 3478 // back to text 3479 import std.format; 3480 auto text2 = format("%(%(%s %)\n%)\n", matrix); 3481 assert(text2 == "1 2 3\n4 5 6\n"); 3482 } 3483 3484 // Slicing 3485 @safe @nogc pure nothrow version(mir_test) unittest 3486 { 3487 import mir.ndslice.topology : iota; 3488 auto a = iota(10, 20, 30, 40); 3489 auto b = a[0..$, 10, 4 .. 27, 4]; 3490 auto c = b[2 .. 9, 5 .. 10]; 3491 auto d = b[3..$, $-2]; 3492 assert(b[4, 17] == a[4, 10, 21, 4]); 3493 assert(c[1, 2] == a[3, 10, 11, 4]); 3494 assert(d[3] == a[6, 10, 25, 4]); 3495 } 3496 3497 // Operator overloading. # 1 3498 pure nothrow version(mir_test) unittest 3499 { 3500 import mir.ndslice.allocation; 3501 import mir.ndslice.topology : iota; 3502 3503 auto fun(ref sizediff_t x) { x *= 3; } 3504 3505 auto tensor = iota(8, 9, 10).slice; 3506 3507 ++tensor[]; 3508 fun(tensor[0, 0, 0]); 3509 3510 assert(tensor[0, 0, 0] == 3); 3511 3512 tensor[0, 0, 0] *= 4; 3513 tensor[0, 0, 0]--; 3514 assert(tensor[0, 0, 0] == 11); 3515 } 3516 3517 // Operator overloading. # 2 3518 pure nothrow version(mir_test) unittest 3519 { 3520 import mir.ndslice.topology: map, iota; 3521 import mir.array.allocation : array; 3522 //import std.bigint; 3523 3524 auto matrix = 72 3525 .iota 3526 //.map!(i => BigInt(i)) 3527 .array 3528 .sliced(8, 9); 3529 3530 matrix[3 .. 6, 2] += 100; 3531 foreach (i; 0 .. 8) 3532 foreach (j; 0 .. 9) 3533 if (i >= 3 && i < 6 && j == 2) 3534 assert(matrix[i, j] >= 100); 3535 else 3536 assert(matrix[i, j] < 100); 3537 } 3538 3539 // Operator overloading. # 3 3540 pure nothrow version(mir_test) unittest 3541 { 3542 import mir.ndslice.allocation; 3543 import mir.ndslice.topology : iota; 3544 3545 auto matrix = iota(8, 9).slice; 3546 matrix[] = matrix; 3547 matrix[] += matrix; 3548 assert(matrix[2, 3] == (2 * 9 + 3) * 2); 3549 3550 auto vec = iota([9], 100); 3551 matrix[] = vec; 3552 foreach (v; matrix) 3553 assert(v == vec); 3554 3555 matrix[] += vec; 3556 foreach (vector; matrix) 3557 foreach (elem; vector) 3558 assert(elem >= 200); 3559 } 3560 3561 // Type deduction 3562 version(mir_test) unittest 3563 { 3564 // Arrays 3565 foreach (T; AliasSeq!(int, const int, immutable int)) 3566 static assert(is(typeof((T[]).init.sliced(3, 4)) == Slice!(T*, 2))); 3567 3568 // Container Array 3569 import std.container.array; 3570 Array!int ar; 3571 ar.length = 12; 3572 auto arSl = ar[].slicedField(3, 4); 3573 } 3574 3575 // Test for map #1 3576 version(mir_test) unittest 3577 { 3578 import mir.ndslice.topology: map, byDim; 3579 auto slice = [1, 2, 3, 4].sliced(2, 2); 3580 3581 auto r = slice.byDim!0.map!(a => a.map!(a => a * 6)); 3582 assert(r.front.front == 6); 3583 assert(r.front.back == 12); 3584 assert(r.back.front == 18); 3585 assert(r.back.back == 24); 3586 assert(r[0][0] == 6); 3587 assert(r[0][1] == 12); 3588 assert(r[1][0] == 18); 3589 assert(r[1][1] == 24); 3590 3591 import std.range.primitives; 3592 static assert(hasSlicing!(typeof(r))); 3593 static assert(isForwardRange!(typeof(r))); 3594 static assert(isRandomAccessRange!(typeof(r))); 3595 } 3596 3597 // Test for map #2 3598 version(mir_test) unittest 3599 { 3600 import mir.ndslice.topology: map, byDim; 3601 import std.range.primitives; 3602 auto data = [1, 2, 3, 4]; 3603 static assert(hasSlicing!(typeof(data))); 3604 static assert(isForwardRange!(typeof(data))); 3605 static assert(isRandomAccessRange!(typeof(data))); 3606 auto slice = data.sliced(2, 2); 3607 static assert(hasSlicing!(typeof(slice))); 3608 static assert(isForwardRange!(typeof(slice))); 3609 static assert(isRandomAccessRange!(typeof(slice))); 3610 auto r = slice.byDim!0.map!(a => a.map!(a => a * 6)); 3611 static assert(hasSlicing!(typeof(r))); 3612 static assert(isForwardRange!(typeof(r))); 3613 static assert(isRandomAccessRange!(typeof(r))); 3614 assert(r.front.front == 6); 3615 assert(r.front.back == 12); 3616 assert(r.back.front == 18); 3617 assert(r.back.back == 24); 3618 assert(r[0][0] == 6); 3619 assert(r[0][1] == 12); 3620 assert(r[1][0] == 18); 3621 assert(r[1][1] == 24); 3622 } 3623 3624 private enum bool isType(alias T) = false; 3625 3626 private enum bool isType(T) = true; 3627 3628 private enum isStringValue(alias T) = is(typeof(T) : string); 3629 3630 3631 private bool _checkAssignLengths( 3632 LIterator, RIterator, 3633 size_t LN, size_t RN, 3634 SliceKind lkind, SliceKind rkind, 3635 ) 3636 (Slice!(LIterator, LN, lkind) ls, 3637 Slice!(RIterator, RN, rkind) rs) 3638 { 3639 static if (isInstanceOf!(SliceIterator, LIterator)) 3640 { 3641 import mir.ndslice.topology: unpack; 3642 return _checkAssignLengths(ls.unpack, rs); 3643 } 3644 else 3645 static if (isInstanceOf!(SliceIterator, RIterator)) 3646 { 3647 import mir.ndslice.topology: unpack; 3648 return _checkAssignLengths(ls, rs.unpack); 3649 } 3650 else 3651 { 3652 foreach (i; Iota!(0, RN)) 3653 if (ls._lengths[i + LN - RN] != rs._lengths[i]) 3654 return false; 3655 return true; 3656 } 3657 } 3658 3659 @safe pure nothrow @nogc version(mir_test) unittest 3660 { 3661 import mir.ndslice.topology : iota; 3662 3663 assert(_checkAssignLengths(iota(2, 2), iota(2, 2))); 3664 assert(!_checkAssignLengths(iota(2, 2), iota(2, 3))); 3665 assert(!_checkAssignLengths(iota(2, 2), iota(3, 2))); 3666 assert(!_checkAssignLengths(iota(2, 2), iota(3, 3))); 3667 } 3668 3669 pure nothrow version(mir_test) unittest 3670 { 3671 auto slice = new int[15].slicedField(5, 3); 3672 3673 /// Fully defined slice 3674 assert(slice[] == slice); 3675 auto sublice = slice[0..$-2, 1..$]; 3676 3677 /// Partially defined slice 3678 auto row = slice[3]; 3679 auto col = slice[0..$, 1]; 3680 } 3681 3682 pure nothrow version(mir_test) unittest 3683 { 3684 auto a = new int[6].slicedField(2, 3); 3685 auto b = [1, 2, 3, 4].sliced(2, 2); 3686 3687 a[0..$, 0..$-1] = b; 3688 assert(a == [[1, 2, 0], [3, 4, 0]]); 3689 3690 a[0..$, 0..$-1] = b[0]; 3691 assert(a == [[1, 2, 0], [1, 2, 0]]); 3692 3693 a[1, 0..$-1] = b[1]; 3694 assert(a[1] == [3, 4, 0]); 3695 3696 a[1, 0..$-1][] = b[0]; 3697 assert(a[1] == [1, 2, 0]); 3698 } 3699 3700 pure nothrow version(mir_test) unittest 3701 { 3702 auto a = new int[6].slicedField(2, 3); 3703 auto b = [[1, 2], [3, 4]]; 3704 3705 a[] = [[1, 2, 3], [4, 5, 6]]; 3706 assert(a == [[1, 2, 3], [4, 5, 6]]); 3707 3708 a[0..$, 0..$-1] = [[1, 2], [3, 4]]; 3709 assert(a == [[1, 2, 3], [3, 4, 6]]); 3710 3711 a[0..$, 0..$-1] = [1, 2]; 3712 assert(a == [[1, 2, 3], [1, 2, 6]]); 3713 3714 a[1, 0..$-1] = [3, 4]; 3715 assert(a[1] == [3, 4, 6]); 3716 3717 a[1, 0..$-1][] = [3, 4]; 3718 assert(a[1] == [3, 4, 6]); 3719 } 3720 3721 pure nothrow version(mir_test) unittest 3722 { 3723 auto a = new int[6].slicedField(2, 3); 3724 3725 a[] = 9; 3726 //assert(a == [[9, 9, 9], [9, 9, 9]]); 3727 3728 a[0..$, 0..$-1] = 1; 3729 //assert(a == [[1, 1, 9], [1, 1, 9]]); 3730 3731 a[0..$, 0..$-1] = 2; 3732 //assert(a == [[2, 2, 9], [2, 2, 9]]); 3733 3734 a[1, 0..$-1] = 3; 3735 //assert(a[1] == [3, 3, 9]); 3736 3737 a[1, 0..$-1] = 4; 3738 //assert(a[1] == [4, 4, 9]); 3739 3740 a[1, 0..$-1][] = 5; 3741 //assert(a[1] == [5, 5, 9]); 3742 } 3743 3744 pure nothrow version(mir_test) unittest 3745 { 3746 auto a = new int[6].slicedField(2, 3); 3747 3748 a[1, 2] = 3; 3749 assert(a[1, 2] == 3); 3750 } 3751 3752 pure nothrow version(mir_test) unittest 3753 { 3754 auto a = new int[6].slicedField(2, 3); 3755 3756 a[[1, 2]] = 3; 3757 assert(a[[1, 2]] == 3); 3758 } 3759 3760 pure nothrow version(mir_test) unittest 3761 { 3762 auto a = new int[6].slicedField(2, 3); 3763 3764 a[1, 2] += 3; 3765 assert(a[1, 2] == 3); 3766 } 3767 3768 pure nothrow version(mir_test) unittest 3769 { 3770 auto a = new int[6].slicedField(2, 3); 3771 3772 a[[1, 2]] += 3; 3773 assert(a[[1, 2]] == 3); 3774 } 3775 3776 pure nothrow version(mir_test) unittest 3777 { 3778 auto a = new int[6].slicedField(2, 3); 3779 auto b = [1, 2, 3, 4].sliced(2, 2); 3780 3781 a[0..$, 0..$-1] += b; 3782 assert(a == [[1, 2, 0], [3, 4, 0]]); 3783 3784 a[0..$, 0..$-1] += b[0]; 3785 assert(a == [[2, 4, 0], [4, 6, 0]]); 3786 3787 a[1, 0..$-1] += b[1]; 3788 assert(a[1] == [7, 10, 0]); 3789 3790 a[1, 0..$-1][] += b[0]; 3791 assert(a[1] == [8, 12, 0]); 3792 } 3793 3794 pure nothrow version(mir_test) unittest 3795 { 3796 auto a = new int[6].slicedField(2, 3); 3797 3798 a[0..$, 0..$-1] += [[1, 2], [3, 4]]; 3799 assert(a == [[1, 2, 0], [3, 4, 0]]); 3800 3801 a[0..$, 0..$-1] += [1, 2]; 3802 assert(a == [[2, 4, 0], [4, 6, 0]]); 3803 3804 a[1, 0..$-1] += [3, 4]; 3805 assert(a[1] == [7, 10, 0]); 3806 3807 a[1, 0..$-1][] += [1, 2]; 3808 assert(a[1] == [8, 12, 0]); 3809 } 3810 3811 pure nothrow version(mir_test) unittest 3812 { 3813 auto a = new int[6].slicedField(2, 3); 3814 3815 a[] += 1; 3816 assert(a == [[1, 1, 1], [1, 1, 1]]); 3817 3818 a[0..$, 0..$-1] += 2; 3819 assert(a == [[3, 3, 1], [3, 3, 1]]); 3820 3821 a[1, 0..$-1] += 3; 3822 assert(a[1] == [6, 6, 1]); 3823 } 3824 3825 pure nothrow version(mir_test) unittest 3826 { 3827 auto a = new int[6].slicedField(2, 3); 3828 3829 ++a[1, 2]; 3830 assert(a[1, 2] == 1); 3831 } 3832 3833 pure nothrow version(mir_test) unittest 3834 { 3835 auto a = new int[6].slicedField(2, 3); 3836 3837 ++a[[1, 2]]; 3838 assert(a[[1, 2]] == 1); 3839 } 3840 3841 pure nothrow version(mir_test) unittest 3842 { 3843 auto a = new int[6].slicedField(2, 3); 3844 3845 ++a[]; 3846 assert(a == [[1, 1, 1], [1, 1, 1]]); 3847 3848 --a[1, 0..$-1]; 3849 assert(a[1] == [0, 0, 1]); 3850 } 3851 3852 version(mir_test) unittest 3853 { 3854 import mir.ndslice.topology: iota, universal; 3855 3856 auto sl = iota(3, 4).universal; 3857 assert(sl[0 .. $] == sl); 3858 } 3859 3860 version(mir_test) unittest 3861 { 3862 import mir.ndslice.topology: canonical, iota; 3863 static assert(kindOf!(typeof(iota([1, 2]).canonical[1])) == Contiguous); 3864 } 3865 3866 version(mir_test) unittest 3867 { 3868 import mir.ndslice.topology: iota; 3869 auto s = iota(2, 3); 3870 assert(s.front!1 == [0, 3]); 3871 assert(s.back!1 == [2, 5]); 3872 } 3873 3874 /++ 3875 Assignment utility for generic code that works both with scalars and with ndslices. 3876 Params: 3877 op = assign operation (generic, optional) 3878 lside = left side 3879 rside = right side 3880 Returns: 3881 expression value 3882 +/ 3883 auto ndassign(string op = "", L, R)(ref L lside, auto ref R rside) @property 3884 if (!isSlice!L && (op.length == 0 || op[$-1] != '=')) 3885 { 3886 return mixin(`lside ` ~ op ~ `= rside`); 3887 } 3888 3889 /// ditto 3890 auto ndassign(string op = "", L, R)(L lside, auto ref R rside) @property 3891 if (isSlice!L && (op.length == 0 || op[$-1] != '=')) 3892 { 3893 static if (op == "") 3894 return lside.opIndexAssign(rside); 3895 else 3896 return lside.opIndexOpAssign!op(rside); 3897 } 3898 3899 /// 3900 version(mir_test) unittest 3901 { 3902 import mir.ndslice.topology: iota; 3903 import mir.ndslice.allocation: slice; 3904 auto scalar = 3; 3905 auto vector = 3.iota.slice; // [0, 1, 2] 3906 3907 // scalar = 5; 3908 scalar.ndassign = 5; 3909 assert(scalar == 5); 3910 3911 // vector[] = vector * 2; 3912 vector.ndassign = vector * 2; 3913 assert(vector == [0, 2, 4]); 3914 3915 // vector[] += scalar; 3916 vector.ndassign!"+"= scalar; 3917 assert(vector == [5, 7, 9]); 3918 } 3919 3920 version(mir_test) pure nothrow unittest 3921 { 3922 import mir.ndslice.allocation: slice; 3923 import mir.ndslice.topology: universal; 3924 3925 auto df = slice!(double, int, int)(2, 3).universal; 3926 df.label[] = [1, 2]; 3927 df.label!1[] = [1, 2, 3]; 3928 auto lsdf = df.lightScope; 3929 assert(lsdf.label!0[0] == 1); 3930 assert(lsdf.label!1[1] == 2); 3931 3932 auto immdf = (cast(immutable)df).lightImmutable; 3933 assert(immdf.label!0[0] == 1); 3934 assert(immdf.label!1[1] == 2); 3935 3936 auto constdf = df.lightConst; 3937 assert(constdf.label!0[0] == 1); 3938 assert(constdf.label!1[1] == 2); 3939 3940 auto constdf2 = df.toConst; 3941 assert(constdf2.label!0[0] == 1); 3942 assert(constdf2.label!1[1] == 2); 3943 3944 auto immdf2 = (cast(immutable)df).toImmutable; 3945 assert(immdf2.label!0[0] == 1); 3946 assert(immdf2.label!1[1] == 2); 3947 } 3948 3949 version(mir_test) pure nothrow unittest 3950 { 3951 import mir.ndslice.allocation: slice; 3952 import mir.ndslice.topology: universal; 3953 3954 auto df = slice!(double, int, int)(2, 3).universal; 3955 df[] = 5; 3956 3957 Slice!(double*, 2, Universal) values = df.values; 3958 assert(values[0][0] == 5); 3959 Slice!(LightConstOf!(double*), 2, Universal) constvalues = df.values; 3960 assert(constvalues[0][0] == 5); 3961 Slice!(LightImmutableOf!(double*), 2, Universal) immvalues = (cast(immutable)df).values; 3962 assert(immvalues[0][0] == 5); 3963 } 3964 3965 version(mir_test) @safe unittest 3966 { 3967 import mir.ndslice.allocation; 3968 auto a = rcslice!double([2, 3], 0); 3969 auto b = rcslice!double([2, 3], 0); 3970 a[1, 2] = 3; 3971 b[] = a; 3972 assert(a == b); 3973 } 3974 3975 version(mir_test) 3976 @safe pure @nogc nothrow 3977 unittest 3978 { 3979 import mir.ndslice.topology: iota, flattened; 3980 3981 auto m = iota(2, 3, 4); // Contiguous Matrix 3982 auto mFlat = m.flattened; 3983 3984 for (size_t i = 0; i < m.elementCount; i++) { 3985 assert(m.accessFlat(i) == mFlat[i]); 3986 } 3987 } 3988 3989 version(mir_test) 3990 @safe pure @nogc nothrow 3991 unittest 3992 { 3993 import mir.ndslice.topology: iota, flattened; 3994 3995 auto m = iota(3, 4); // Contiguous Matrix 3996 auto x = m.front; // Contiguous Vector 3997 3998 for (size_t i = 0; i < x.elementCount; i++) { 3999 assert(x.accessFlat(i) == m[0, i]); 4000 } 4001 } 4002 4003 version(mir_test) 4004 @safe pure @nogc nothrow 4005 unittest 4006 { 4007 import mir.ndslice.topology: iota, flattened; 4008 4009 auto m = iota(3, 4); // Contiguous Matrix 4010 auto x = m[0 .. $, 0 .. $ - 1]; // Canonical Matrix 4011 auto xFlat = x.flattened; 4012 4013 for (size_t i = 0; i < x.elementCount; i++) { 4014 assert(x.accessFlat(i) == xFlat[i]); 4015 } 4016 } 4017 4018 4019 version(mir_test) 4020 @safe pure @nogc nothrow 4021 unittest 4022 { 4023 import mir.ndslice.topology: iota, flattened; 4024 4025 auto m = iota(2, 3, 4); // Contiguous Matrix 4026 auto x = m[0 .. $, 0 .. $, 0 .. $ - 1]; // Canonical Matrix 4027 auto xFlat = x.flattened; 4028 4029 for (size_t i = 0; i < x.elementCount; i++) { 4030 assert(x.accessFlat(i) == xFlat[i]); 4031 } 4032 } 4033 4034 4035 version(mir_test) 4036 @safe pure @nogc nothrow 4037 unittest 4038 { 4039 import mir.ndslice.topology: iota, flattened; 4040 import mir.ndslice.dynamic: transposed; 4041 4042 auto m = iota(2, 3, 4); // Contiguous Matrix 4043 auto x = m.transposed!(2, 1, 0); // Universal Matrix 4044 auto xFlat = x.flattened; 4045 4046 for (size_t i = 0; i < x.elementCount; i++) { 4047 assert(x.accessFlat(i) == xFlat[i]); 4048 } 4049 } 4050 4051 version(mir_test) 4052 @safe pure @nogc nothrow 4053 unittest 4054 { 4055 import mir.ndslice.topology: iota, flattened; 4056 import mir.ndslice.dynamic: transposed; 4057 4058 auto m = iota(3, 4); // Contiguous Matrix 4059 auto x = m.transposed; // Universal Matrix 4060 auto xFlat = x.flattened; 4061 4062 for (size_t i = 0; i < x.elementCount; i++) { 4063 assert(x.accessFlat(i) == xFlat[i]); 4064 } 4065 } 4066 4067 version(mir_test) 4068 @safe pure @nogc nothrow 4069 unittest 4070 { 4071 import mir.ndslice.topology: iota, flattened, diagonal; 4072 4073 auto m = iota(3, 4); // Contiguous Matrix 4074 auto x = m.diagonal; // Universal Vector 4075 4076 for (size_t i = 0; i < x.elementCount; i++) { 4077 assert(x.accessFlat(i) == m[i, i]); 4078 } 4079 } 4080 4081 version(mir_test) 4082 @safe pure @nogc nothrow 4083 unittest 4084 { 4085 import mir.ndslice.topology: iota, flattened; 4086 4087 auto m = iota(3, 4); // Contiguous Matrix 4088 auto x = m.front!1; // Universal Vector 4089 4090 for (size_t i = 0; i < x.elementCount; i++) { 4091 assert(x.accessFlat(i) == m[i, 0]); 4092 } 4093 } 4094 4095 version(mir_test) 4096 @safe pure @nogc nothrow 4097 unittest // check it can be compiled 4098 { 4099 import mir.algebraic; 4100 alias S = Slice!(double*, 2); 4101 alias D = Variant!S; 4102 } 4103 4104 version(mir_test) 4105 unittest 4106 { 4107 import mir.ndslice; 4108 4109 auto matrix = slice!short(3, 4); 4110 matrix[] = 0; 4111 matrix.diagonal[] = 1; 4112 4113 auto row = matrix[2]; 4114 row[3] = 6; 4115 assert(matrix[2, 3] == 6); // D & C index order 4116 }