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