1 /+ 2 ## Guide for Slice/BLAS contributors 3 4 1. Make sure functions are 5 a. inlined(!), 6 b. `@nogc`, 7 c. `nothrow`, 8 d. `pure`. 9 For this reason, it is preferable to use _simple_ `assert`s with messages 10 that can be computed at compile time. 11 The goals are: 12 1. to reduce executable size for _any_ compilation mode 13 2. to reduce template bloat in object files 14 3. to reduce compilation time 15 4. to allow users to write extern C bindings for code libraries on `Slice` type. 16 17 2. `std.format`, `std.string`, and `std.conv` should not be used in error 18 message formatting.`"Use" ~ Concatenation.stringof`. 19 20 3. `mixin template`s may be used for pretty error message formatting. 21 22 4. `Exception`s/`enforce`s should no be used to check indices and lengths. 23 Exceptions are only allowed for algorithms where validation of input data is 24 too complicated for the user. `reshape` function is a good example of a case 25 where Exceptions are required. 26 If a function might throw an exception, an example with exception handing should be added. 27 28 5. For simple checks like matrix transposition, compile time flags should not be used. 29 It is much better to opt for runtime matrix transposition. 30 Furthermore, Slice type provides runtime matrix transposition out of the box. 31 32 6. _Fortran_VS_C_ flags should not be used. They are about notation, 33 but not about the algorithm itself. For math world users, 34 a corresponding code example might be included in the documentation. 35 `transposed` / `everted` can be used in cache-friendly codes. 36 37 7. Compile time evaluation should not be used to produce dummy types like `IdentityMatrix`. 38 39 8. Memory allocation and algorithm logic should be separated whenever possible. 40 41 9. CTFE version(mir_test) unittests should be added to new functions. 42 +/ 43 44 /** 45 $(H1 Multidimensional Random Access Ranges) 46 47 The package provides a multidimensional array implementation. 48 It would be well suited to creating machine learning and image 49 processing algorithms, but should also be general enough for use anywhere with 50 homogeneously-typed multidimensional data. 51 In addition, it includes various functions for iteration, accessing, and manipulation. 52 53 Quick_Start: 54 $(SUBREF slice, sliced) is a function designed to create 55 a multidimensional view over a range. 56 Multidimensional view is presented by $(SUBREF slice, Slice) type. 57 58 ------ 59 import mir.ndslice; 60 61 auto matrix = slice!double(3, 4); 62 matrix[] = 0; 63 matrix.diagonal[] = 1; 64 65 auto row = matrix[2]; 66 row[3] = 6; 67 assert(matrix[2, 3] == 6); // D & C index order 68 ------ 69 70 Note: 71 In many examples $(REF iota, mir,_ndslice,topology) is used 72 instead of a regular array, which makes it 73 possible to carry out tests without memory allocation. 74 75 $(SCRIPT inhibitQuickIndex = 1;) 76 77 $(DIVC quickindex, 78 $(BOOKTABLE, 79 80 $(TR $(TH Submodule) $(TH Declarations)) 81 82 $(TR $(TDNW $(SUBMODULE slice) $(BR) 83 $(SMALL $(SUBREF slice, Slice) structure 84 $(BR) Basic constructors)) 85 $(TD 86 $(SUBREF slice, Canonical) 87 $(SUBREF slice, Contiguous) 88 $(SUBREF slice, DeepElementType) 89 $(SUBREF slice, isSlice) 90 $(SUBREF slice, kindOf) 91 $(SUBREF slice, Slice) 92 $(SUBREF slice, sliced) 93 $(SUBREF slice, slicedField) 94 $(SUBREF slice, slicedNdField) 95 $(SUBREF slice, SliceKind) 96 $(SUBREF slice, Structure) 97 $(SUBREF slice, Universal) 98 ) 99 ) 100 101 $(TR $(TDNW $(SUBMODULE allocation) $(BR) 102 $(SMALL Allocation utilities)) 103 $(TD 104 $(SUBREF allocation, bitRcslice) 105 $(SUBREF allocation, bitSlice) 106 $(SUBREF allocation, makeNdarray) 107 $(SUBREF allocation, makeSlice) 108 $(SUBREF allocation, makeUninitSlice) 109 $(SUBREF allocation, mininitRcslice) 110 $(SUBREF allocation, ndarray) 111 $(SUBREF allocation, rcslice) 112 $(SUBREF allocation, shape) 113 $(SUBREF allocation, slice) 114 $(SUBREF allocation, stdcFreeAlignedSlice) 115 $(SUBREF allocation, stdcFreeSlice) 116 $(SUBREF allocation, stdcSlice) 117 $(SUBREF allocation, stdcUninitAlignedSlice) 118 $(SUBREF allocation, stdcUninitSlice) 119 $(SUBREF allocation, uninitAlignedSlice) 120 $(SUBREF allocation, uninitSlice) 121 ) 122 ) 123 124 $(TR $(TDNW $(SUBMODULE topology) $(BR) 125 $(SMALL Subspace manipulations 126 $(BR) Advanced constructors 127 $(BR) SliceKind conversion utilities)) 128 $(TD 129 $(SUBREF topology, alongDim) 130 $(SUBREF topology, as) 131 $(SUBREF topology, asKindOf) 132 $(SUBREF topology, assumeCanonical) 133 $(SUBREF topology, assumeContiguous) 134 $(SUBREF topology, assumeHypercube) 135 $(SUBREF topology, assumeSameShape) 136 $(SUBREF topology, bitpack) 137 $(SUBREF topology, bitwise) 138 $(SUBREF topology, blocks) 139 $(SUBREF topology, byDim) 140 $(SUBREF topology, bytegroup) 141 $(SUBREF topology, cached) 142 $(SUBREF topology, cachedGC) 143 $(SUBREF topology, canonical) 144 $(SUBREF topology, cartesian) 145 $(SUBREF topology, chopped) 146 $(SUBREF topology, cycle) 147 $(SUBREF topology, diagonal) 148 $(SUBREF topology, diff) 149 $(SUBREF topology, dropBorders) 150 $(SUBREF topology, evertPack) 151 $(SUBREF topology, flattened) 152 $(SUBREF topology, indexed) 153 $(SUBREF topology, iota) 154 $(SUBREF topology, ipack) 155 $(SUBREF topology, kronecker) 156 $(SUBREF topology, linspace) 157 $(SUBREF topology, magic) 158 $(SUBREF topology, map) 159 $(SUBREF topology, member) 160 $(SUBREF topology, ndiota) 161 $(SUBREF topology, orthogonalReduceField) 162 $(SUBREF topology, pack) 163 $(SUBREF topology, pairwise) 164 $(SUBREF topology, repeat) 165 $(SUBREF topology, reshape) 166 $(SUBREF topology, ReshapeError) 167 $(SUBREF topology, retro) 168 $(SUBREF topology, slide) 169 $(SUBREF topology, slideAlong) 170 $(SUBREF topology, squeeze) 171 $(SUBREF topology, stairs) 172 $(SUBREF topology, stride) 173 $(SUBREF topology, subSlices) 174 $(SUBREF topology, triplets) 175 $(SUBREF topology, universal) 176 $(SUBREF topology, unsqueeze) 177 $(SUBREF topology, unzip) 178 $(SUBREF topology, vmap) 179 $(SUBREF topology, windows) 180 $(SUBREF topology, zip) 181 ) 182 ) 183 184 $(TR $(TDNW $(SUBMODULE filling) $(BR) 185 $(SMALL Specialized initialisation routines)) 186 $(TD 187 $(SUBREF filling, fillVandermonde) 188 ) 189 ) 190 191 $(TR $(TDNW $(SUBMODULE fuse) $(BR) 192 $(SMALL Data fusing (stacking) 193 $(BR) See also $(SUBMODULE concatenation) submodule. 194 )) 195 $(TD 196 $(SUBREF fuse, fuse) 197 $(SUBREF fuse, fuseAs) 198 $(SUBREF fuse, rcfuse) 199 $(SUBREF fuse, rcfuseAs) 200 $(SUBREF fuse, fuseCells) 201 ) 202 ) 203 204 $(TR $(TDNW $(SUBMODULE concatenation) $(BR) 205 $(SMALL Concatenation, padding, and algorithms 206 $(BR) See also $(SUBMODULE fuse) submodule. 207 )) 208 $(TD 209 $(SUBREF concatenation, forEachFragment) 210 $(SUBREF concatenation, isConcatenation) 211 $(SUBREF concatenation, pad) 212 $(SUBREF concatenation, padEdge) 213 $(SUBREF concatenation, padWrap) 214 $(SUBREF concatenation, padSymmetric) 215 $(SUBREF concatenation, concatenation) 216 $(SUBREF concatenation, Concatenation) 217 $(SUBREF concatenation, concatenationDimension) 218 $(SUBREF concatenation, until) 219 ) 220 ) 221 222 $(TR $(TDNW $(SUBMODULE dynamic) 223 $(BR) $(SMALL Dynamic dimension manipulators)) 224 $(TD 225 $(SUBREF dynamic, allReversed) 226 $(SUBREF dynamic, dropToHypercube) 227 $(SUBREF dynamic, everted) 228 $(SUBREF dynamic, normalizeStructure) 229 $(SUBREF dynamic, reversed) 230 $(SUBREF dynamic, rotated) 231 $(SUBREF dynamic, strided) 232 $(SUBREF dynamic, swapped) 233 $(SUBREF dynamic, transposed) 234 ) 235 ) 236 237 $(TR $(TDNW $(SUBMODULE sorting) 238 $(BR) $(SMALL Sorting utilities)) 239 $(TD 240 $(SUBREF sorting, sort) 241 Examples for `isSorted`, `isStrictlyMonotonic`, `makeIndex`, and `schwartzSort`. 242 ) 243 ) 244 245 $(TR $(TDNW $(SUBMODULE mutation) 246 $(BR) $(SMALL Mutation utilities)) 247 $(TD 248 $(SUBREF mutation, copyMinor) 249 $(SUBREF mutation, reverseInPlace) 250 ) 251 ) 252 253 $(TR $(TDNW $(SUBMODULE iterator) 254 $(BR) $(SMALL Declarations)) 255 $(TD 256 $(SUBREF iterator, BytegroupIterator) 257 $(SUBREF iterator, CachedIterator) 258 $(SUBREF iterator, ChopIterator) 259 $(SUBREF iterator, FieldIterator) 260 $(SUBREF iterator, FlattenedIterator) 261 $(SUBREF iterator, IndexIterator) 262 $(SUBREF iterator, IotaIterator) 263 $(SUBREF iterator, MapIterator) 264 $(SUBREF iterator, MemberIterator) 265 $(SUBREF iterator, RetroIterator) 266 $(SUBREF iterator, SliceIterator) 267 $(SUBREF iterator, SlideIterator) 268 $(SUBREF iterator, StairsIterator) 269 $(SUBREF iterator, StrideIterator) 270 $(SUBREF iterator, SubSliceIterator) 271 $(SUBREF iterator, Triplet) 272 $(SUBREF iterator, TripletIterator) 273 $(SUBREF iterator, ZipIterator) 274 ) 275 ) 276 277 $(TR $(TDNW $(SUBMODULE field) 278 $(BR) $(SMALL Declarations)) 279 $(TD 280 $(SUBREF field, BitField) 281 $(SUBREF field, BitpackField) 282 $(SUBREF field, CycleField) 283 $(SUBREF field, LinspaceField) 284 $(SUBREF field, MagicField) 285 $(SUBREF field, MapField) 286 $(SUBREF field, ndIotaField) 287 $(SUBREF field, OrthogonalReduceField) 288 $(SUBREF field, RepeatField) 289 ) 290 ) 291 292 $(TR $(TDNW $(SUBMODULE ndfield) 293 $(BR) $(SMALL Declarations)) 294 $(TD 295 $(SUBREF ndfield, Cartesian) 296 $(SUBREF ndfield, Kronecker) 297 ) 298 ) 299 300 $(TR $(TDNW $(SUBMODULE chunks) 301 $(BR) $(SMALL Declarations)) 302 $(TD 303 $(SUBREF field, chunks) 304 $(SUBREF field, Chunks) 305 $(SUBREF field, isChunks) 306 $(SUBREF field, popFrontTuple) 307 ) 308 ) 309 310 $(TR $(TDNW $(SUBMODULE traits) 311 $(BR) $(SMALL Declarations)) 312 $(TD 313 $(SUBREF traits, isIterator) 314 $(SUBREF traits, isVector) 315 $(SUBREF traits, isMatrix) 316 $(SUBREF traits, isContiguousSlice) 317 $(SUBREF traits, isCanonicalSlice) 318 $(SUBREF traits, isUniversalSlice) 319 $(SUBREF traits, isContiguousVector) 320 $(SUBREF traits, isUniversalVector) 321 $(SUBREF traits, isContiguousMatrix) 322 $(SUBREF traits, isCanonicalMatrix) 323 $(SUBREF traits, isUniversalMatrix) 324 ) 325 ) 326 327 )) 328 329 $(H2 Example: Image Processing) 330 331 A median filter is implemented as an example. The function 332 `movingWindowByChannel` can also be used with other filters that use a sliding 333 window as the argument, in particular with convolution matrices such as the 334 $(LINK2 https://en.wikipedia.org/wiki/Sobel_operator, Sobel operator). 335 336 `movingWindowByChannel` iterates over an image in sliding window mode. 337 Each window is transferred to a `filter`, which calculates the value of the 338 pixel that corresponds to the given window. 339 340 This function does not calculate border cases in which a window overlaps 341 the image partially. However, the function can still be used to carry out such 342 calculations. That can be done by creating an amplified image, with the edges 343 reflected from the original image, and then applying the given function to the 344 new file. 345 346 Note: You can find the example at 347 $(LINK2 https://github.com/libmir/mir/blob/master/examples/median_filter.d, GitHub). 348 349 ------- 350 /++ 351 Params: 352 filter = unary function. Dimension window 2D is the argument. 353 image = image dimensions `(h, w, c)`, 354 where с is the number of channels in the image 355 nr = number of rows in the window 356 nс = number of columns in the window 357 358 Returns: 359 image dimensions `(h - nr + 1, w - nc + 1, c)`, 360 where с is the number of channels in the image. 361 Dense data layout is guaranteed. 362 +/ 363 Slice!(ubyte*, 3) movingWindowByChannel 364 (Slice!(Universal, [3], ubyte*) image, size_t nr, size_t nc, ubyte delegate(Slice!(Universal, [2], ubyte*)) filter) 365 { 366 // 0. 3D 367 // The last dimension represents the color channel. 368 return image 369 // 1. 2D composed of 1D 370 // Packs the last dimension. 371 .pack!1 372 // 2. 2D composed of 2D composed of 1D 373 // Splits image into overlapping windows. 374 .windows(nr, nc) 375 // 3. 5D 376 // Unpacks the windows. 377 .unpack 378 .transposed!(0, 1, 4) 379 // 4. 5D 380 // Brings the color channel dimension to the third position. 381 .pack!2 382 // 2D to pixel lazy conversion. 383 .map!filter 384 // Creates the new image. The only memory allocation in this function. 385 .slice; 386 } 387 ------- 388 389 A function that calculates the value of iterator median is also necessary. 390 391 ------- 392 /++ 393 394 Params: 395 r = input range 396 buf = buffer with length no less than the number of elements in `r` 397 Returns: 398 median value over the range `r` 399 +/ 400 T median(Range, T)(Slice!(Universal, [2], Range) sl, T[] buf) 401 { 402 import std.algorithm.sorting : topN; 403 // copy sl to the buffer 404 auto retPtr = reduce!( 405 (ptr, elem) { *ptr = elem; return ptr + 1;} )(buf.ptr, sl); 406 auto n = retPtr - buf.ptr; 407 buf[0 .. n].topN(n / 2); 408 return buf[n / 2]; 409 } 410 ------- 411 412 The `main` function: 413 414 ------- 415 void main(string[] args) 416 { 417 import std.conv : to; 418 import std.getopt : getopt, defaultGetoptPrinter; 419 import std.path : stripExtension; 420 421 uint nr, nc, def = 3; 422 auto helpInformation = args.getopt( 423 "nr", "number of rows in window, default value is " ~ def.to!string, &nr, 424 "nc", "number of columns in window, default value is equal to nr", &nc); 425 if (helpInformation.helpWanted) 426 { 427 defaultGetoptPrinter( 428 "Usage: median-filter [<options...>] [<file_names...>]\noptions:", 429 helpInformation.options); 430 return; 431 } 432 if (!nr) nr = def; 433 if (!nc) nc = nr; 434 435 auto buf = new ubyte[nr * nc]; 436 437 foreach (name; args[1 .. $]) 438 { 439 import imageformats; // can be found at code.dlang.org 440 441 IFImage image = read_image(name); 442 443 auto ret = image.pixels 444 .sliced(cast(size_t)image.h, cast(size_t)image.w, cast(size_t)image.c) 445 .movingWindowByChannel 446 !(window => median(window, buf)) 447 (nr, nc); 448 449 write_image( 450 name.stripExtension ~ "_filtered.png", 451 ret.length!1, 452 ret.length!0, 453 (&ret[0, 0, 0])[0 .. ret.elementCount]); 454 } 455 } 456 ------- 457 458 This program works both with color and grayscale images. 459 460 ------- 461 $ median-filter --help 462 Usage: median-filter [<options...>] [<file_names...>] 463 options: 464 --nr number of rows in window, default value is 3 465 --nc number of columns in window default value equals to nr 466 -h --help This help information. 467 ------- 468 469 $(H2 Compared with `numpy.ndarray`) 470 471 numpy is undoubtedly one of the most effective software packages that has 472 facilitated the work of many engineers and scientists. However, due to the 473 specifics of implementation of Python, a programmer who wishes to use the 474 functions not represented in numpy may find that the built-in functions 475 implemented specifically for numpy are not enough, and their Python 476 implementations work at a very low speed. Extending numpy can be done, but 477 is somewhat laborious as even the most basic numpy functions that refer 478 directly to `ndarray` data must be implemented in C for reasonable performance. 479 480 At the same time, while working with `ndslice`, an engineer has access to the 481 whole set of standard D library, so the functions he creates will be as 482 efficient as if they were written in C. 483 484 485 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 486 Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments 487 Authors: Ilya Yaroshenko 488 Acknowledgements: John Loughran Colvin 489 490 Macros: 491 SUBMODULE = $(MREF_ALTTEXT $1, mir, ndslice, $1) 492 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 493 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 494 TDNW2 = <td class="donthyphenate nobr" rowspan="2">$0</td> 495 */ 496 module mir.ndslice; 497 498 public import mir.algorithm.iteration; 499 public import mir.ndslice.allocation; 500 public import mir.ndslice.chunks; 501 public import mir.ndslice.concatenation; 502 public import mir.ndslice.dynamic; 503 public import mir.ndslice.field; 504 public import mir.ndslice.filling; 505 public import mir.ndslice.fuse; 506 public import mir.ndslice.iterator; 507 public import mir.ndslice.mutation; 508 public import mir.ndslice.ndfield; 509 public import mir.ndslice.slice; 510 public import mir.ndslice.topology; 511 public import mir.ndslice.traits; 512 513 514 version(mir_test) unittest 515 { 516 auto matrix = new double[12].sliced(3, 4); 517 matrix[] = 0; 518 matrix.diagonal[] = 1; 519 520 auto row = matrix[2]; 521 row[3] = 6; 522 assert(matrix[2, 3] == 6); // D & C index order 523 //assert(matrix(3, 2) == 6); // Math & Fortran index order 524 } 525 526 // relaxed example 527 version(mir_test) unittest 528 { 529 import mir.qualifier; 530 531 static Slice!(ubyte*, 3) movingWindowByChannel 532 (Slice!(ubyte*, 3, Universal) image, size_t nr, size_t nc, ubyte delegate(LightConstOf!(Slice!(ubyte*, 2, Universal))) filter) 533 { 534 return image 535 .pack!1 536 .windows(nr, nc) 537 .unpack 538 .unpack 539 .transposed!(0, 1, 4) 540 .pack!2 541 .map!filter 542 .slice; 543 } 544 545 static T median(Iterator, T)(Slice!(Iterator, 2, Universal) sl, T[] buf) 546 { 547 import std.algorithm.sorting : topN; 548 // copy sl to the buffer 549 auto retPtr = reduce!( 550 (ptr, elem) { 551 *ptr = elem; 552 return ptr + 1; 553 } )(buf.ptr, sl); 554 auto n = retPtr - buf.ptr; 555 buf[0 .. n].topN(n / 2); 556 return buf[n / 2]; 557 } 558 559 import std.conv : to; 560 import std.getopt : getopt, defaultGetoptPrinter; 561 import std.path : stripExtension; 562 563 auto args = ["std"]; 564 uint nr, nc, def = 3; 565 auto helpInformation = args.getopt( 566 "nr", "number of rows in window, default value is " ~ def.to!string, &nr, 567 "nc", "number of columns in window default value equals to nr", &nc); 568 if (helpInformation.helpWanted) 569 { 570 defaultGetoptPrinter( 571 "Usage: median-filter [<options...>] [<file_names...>]\noptions:", 572 helpInformation.options); 573 return; 574 } 575 if (!nr) nr = def; 576 if (!nc) nc = nr; 577 578 auto buf = new ubyte[nr * nc]; 579 580 foreach (name; args[1 .. $]) 581 { 582 auto ret = 583 movingWindowByChannel 584 (new ubyte[300].sliced(10, 10, 3).universal, nr, nc, window => median(window, buf)); 585 } 586 } 587 588 @safe @nogc pure nothrow version(mir_test) unittest 589 { 590 immutable r = 1000.iota; 591 592 auto t0 = r.sliced(1000); 593 assert(t0.front == 0); 594 assert(t0.back == 999); 595 assert(t0[9] == 9); 596 597 auto t1 = t0[10 .. 20]; 598 assert(t1.front == 10); 599 assert(t1.back == 19); 600 assert(t1[9] == 19); 601 602 t1.popFront(); 603 assert(t1.front == 11); 604 t1.popFront(); 605 assert(t1.front == 12); 606 607 t1.popBack(); 608 assert(t1.back == 18); 609 t1.popBack(); 610 assert(t1.back == 17); 611 612 assert(t1 == iota([6], 12)); 613 } 614 615 pure nothrow version(mir_test) unittest 616 { 617 import mir.ndslice.topology : iota; 618 import mir.array.allocation : array; 619 auto r = 1000.iota.array; 620 621 auto t0 = r.sliced(1000); 622 assert(t0.length == 1000); 623 assert(t0.front == 0); 624 assert(t0.back == 999); 625 assert(t0[9] == 9); 626 627 auto t1 = t0[10 .. 20]; 628 assert(t1.front == 10); 629 assert(t1.back == 19); 630 assert(t1[9] == 19); 631 632 t1.popFront(); 633 assert(t1.front == 11); 634 t1.popFront(); 635 assert(t1.front == 12); 636 637 t1.popBack(); 638 assert(t1.back == 18); 639 t1.popBack(); 640 assert(t1.back == 17); 641 642 assert(t1 == iota([6], 12)); 643 644 t1.front = 13; 645 assert(t1.front == 13); 646 t1.front++; 647 assert(t1.front == 14); 648 t1.front += 2; 649 assert(t1.front == 16); 650 t1.front = 12; 651 assert((t1.front = 12) == 12); 652 653 t1.back = 13; 654 assert(t1.back == 13); 655 t1.back++; 656 assert(t1.back == 14); 657 t1.back += 2; 658 assert(t1.back == 16); 659 t1.back = 12; 660 assert((t1.back = 12) == 12); 661 662 t1[3] = 13; 663 assert(t1[3] == 13); 664 t1[3]++; 665 assert(t1[3] == 14); 666 t1[3] += 2; 667 assert(t1[3] == 16); 668 t1[3] = 12; 669 assert((t1[3] = 12) == 12); 670 671 t1[3 .. 5] = 100; 672 assert(t1[2] != 100); 673 assert(t1[3] == 100); 674 assert(t1[4] == 100); 675 assert(t1[5] != 100); 676 677 t1[3 .. 5] += 100; 678 assert(t1[2] < 100); 679 assert(t1[3] == 200); 680 assert(t1[4] == 200); 681 assert(t1[5] < 100); 682 683 --t1[3 .. 5]; 684 685 assert(t1[2] < 100); 686 assert(t1[3] == 199); 687 assert(t1[4] == 199); 688 assert(t1[5] < 100); 689 690 --t1[]; 691 assert(t1[3] == 198); 692 assert(t1[4] == 198); 693 694 t1[] += 2; 695 assert(t1[3] == 200); 696 assert(t1[4] == 200); 697 698 t1[].opIndexOpAssign!"*"(t1); 699 assert(t1[3] == 40000); 700 assert(t1[4] == 40000); 701 702 703 assert(&t1[$ - 1] is &(t1.back())); 704 } 705 706 @safe @nogc pure nothrow version(mir_test) unittest 707 { 708 import std.range : iota; 709 auto r = (10_000L * 2 * 3 * 4).iota; 710 711 auto t0 = r.slicedField(10, 20, 30, 40); 712 assert(t0.length == 10); 713 assert(t0.length!0 == 10); 714 assert(t0.length!1 == 20); 715 assert(t0.length!2 == 30); 716 assert(t0.length!3 == 40); 717 } 718 719 pure nothrow version(mir_test) unittest 720 { 721 auto tensor = new int[3 * 4 * 8].sliced(3, 4, 8); 722 assert(&(tensor.back.back.back()) is &tensor[2, 3, 7]); 723 assert(&(tensor.front.front.front()) is &tensor[0, 0, 0]); 724 } 725 726 pure nothrow version(mir_test) unittest 727 { 728 auto slice = new int[24].sliced(2, 3, 4); 729 auto r0 = slice.pack!1[1, 2]; 730 slice.pack!1[1, 2][] = 4; 731 auto r1 = slice[1, 2]; 732 assert(slice[1, 2, 3] == 4); 733 } 734 735 pure nothrow version(mir_test) unittest 736 { 737 auto ar = new int[3 * 8 * 9]; 738 739 auto tensor = ar.sliced(3, 8, 9); 740 tensor[0, 1, 2] = 4; 741 tensor[0, 1, 2]++; 742 assert(tensor[0, 1, 2] == 5); 743 tensor[0, 1, 2]--; 744 assert(tensor[0, 1, 2] == 4); 745 tensor[0, 1, 2] += 2; 746 assert(tensor[0, 1, 2] == 6); 747 748 auto matrix = tensor[0 .. $, 1, 0 .. $]; 749 matrix[] = 10; 750 assert(tensor[0, 1, 2] == 10); 751 assert(matrix[0, 2] == tensor[0, 1, 2]); 752 assert(&matrix[0, 2] is &tensor[0, 1, 2]); 753 }