1 module mir.ndslice.internal; 2 3 import mir.internal.utility : isFloatingPoint, Iota; 4 import mir.math.common: fmamath; 5 import mir.ndslice.iterator: IotaIterator; 6 import mir.ndslice.slice; 7 import mir.primitives; 8 import std.meta; 9 import std.traits; 10 11 @fmamath: 12 13 template ConstIfPointer(T) 14 { 15 static if (isPointer!T) 16 alias ConstIfPointer = const(PointerTarget!T)*; 17 else 18 alias ConstIfPointer = T; 19 } 20 21 /// 22 public import mir.utility: _expect; 23 24 struct RightOp(string op, T) 25 { 26 T value; 27 28 auto lightConst()() const @property 29 { 30 import mir.qualifier; 31 return RightOp!(op, LightConstOf!T)(value.lightConst); 32 } 33 34 auto lightImmutable()() immutable @property 35 { 36 import mir.qualifier; 37 return RightOp!(op, LightImmutableOf!T)(value.lightImmutable); 38 } 39 40 this()(ref T v) { value = v; } 41 this()(T v) { value = v; } 42 auto ref opCall(F)(auto ref F right) 43 { 44 static if (op == "^^" && isNumeric!T && isFloatingPoint!F) 45 { 46 import mir.math.common: pow; 47 return pow(value, right); 48 } 49 else 50 { 51 return mixin("value " ~ op ~ " right"); 52 } 53 } 54 } 55 56 struct LeftOp(string op, T) 57 { 58 T value; 59 60 auto lightConst()() const @property 61 { 62 import mir.qualifier; 63 return LeftOp!(op, LightConstOf!T)(value.lightConst); 64 } 65 66 auto lightImmutable()() immutable @property 67 { 68 import mir.qualifier; 69 return LeftOp!(op, LightImmutableOf!T)(value.lightImmutable); 70 } 71 72 this()(ref T v) { value = v; } 73 this()(T v) { value = v; } 74 auto ref opCall(F)(auto ref F left) 75 { 76 static if (op == "^^" && isFloatingPoint!T && isNumeric!F) 77 { 78 import mir.math.common: pow; 79 return pow(left, value); 80 } 81 else 82 { 83 return mixin("left " ~ op ~ " value"); 84 } 85 } 86 } 87 88 private template _prod(size_t len) 89 if (len) 90 { 91 static if (len == 1) 92 enum _prod = "elems[0]"; 93 else 94 { 95 enum i = len - 1; 96 enum _prod = ._prod!i ~ " * elems[" ~ i.stringof ~ "]"; 97 } 98 } 99 100 auto product(Elems...)(auto ref Elems elems) 101 { 102 return mixin(_prod!(Elems.length)); 103 } 104 105 106 template _iotaArgs(size_t length, string prefix, string suffix) 107 { 108 static if (length) 109 { 110 enum i = length - 1; 111 enum _iotaArgs = _iotaArgs!(i, prefix, suffix) ~ prefix ~ i.stringof ~ suffix; 112 } 113 else 114 enum _iotaArgs = ""; 115 } 116 117 alias _IteratorOf(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind) = Iterator; 118 119 E maxElem(E)(E[] arr...) 120 { 121 auto ret = Unqual!E.min; 122 foreach(e; arr) 123 if (e > ret) 124 ret = e; 125 return ret; 126 } 127 128 E minElem(E)(E[] arr...) 129 { 130 auto ret = Unqual!E.max; 131 foreach(e; arr) 132 if (e < ret) 133 ret = e; 134 return ret; 135 } 136 137 size_t sum()(size_t[] packs) 138 { 139 size_t s; 140 foreach(pack; packs) 141 s += pack; 142 return s; 143 } 144 145 146 size_t[] reverse()(size_t[] ar) 147 { 148 foreach(i, e; ar[0..$/2]) 149 { 150 ar[i] = ar[$ - i - 1]; 151 ar[$ - i - 1] = e; 152 } 153 return ar; 154 } 155 156 enum indexError(DeepElement, int pos, int N) = 157 N.stringof ~ "D slice of " ~ DeepElement.stringof ~ ": bounds check failed at " ~ (pos + 1).stringof ~ " dimension"; 158 159 enum string tailErrorMessage( 160 string fun = __FUNCTION__, 161 string pfun = __PRETTY_FUNCTION__) = 162 " 163 - - - 164 Error in function 165 " ~ fun ~ " 166 - - - 167 Function prototype 168 " ~ pfun ~ " 169 _____"; 170 171 mixin template DimensionsCountCTError() 172 { 173 static assert(Dimensions.length <= N, 174 "Dimensions list length = " ~ Dimensions.length.stringof 175 ~ " should be less than or equal to N = " ~ N.stringof 176 ~ tailErrorMessage!()); 177 } 178 179 enum DimensionsCountRTError = q{ 180 assert(dimensions.length <= N, 181 "Dimensions list length should be less than or equal to N = " ~ N.stringof 182 ~ tailErrorMessage!()); 183 }; 184 185 mixin template DimensionCTError() 186 { 187 static assert(dimension >= 0, 188 "dimension = " ~ dimension.stringof ~ " at position " 189 ~ i.stringof ~ " should be greater than or equal to 0" 190 ~ tailErrorMessage!()); 191 static assert(dimension < N, 192 "dimension = " ~ dimension.stringof ~ " at position " 193 ~ i.stringof ~ " should be less than N = " ~ N.stringof 194 ~ tailErrorMessage!()); 195 static assert(dimension < slice.S, 196 "dimension = " ~ dimension.stringof ~ " at position " 197 ~ i.stringof ~ " should be less than " ~ (slice.S).stringof ~ ". " 198 ~ "`universal` and `canonical` from `mir.ndslice.topology` can be used to relax slice kind." 199 ~ tailErrorMessage!()); 200 } 201 202 enum DimensionRTError = q{ 203 static if (isSigned!(typeof(dimension))) 204 assert(dimension >= 0, "dimension should be greater than or equal to 0" 205 ~ tailErrorMessage!()); 206 assert(dimension < N, "dimension should be less than N = " ~ N.stringof 207 ~ tailErrorMessage!()); 208 assert(dimension < slice.S, 209 "dimension should be less than " ~ slice.S.stringof ~ ". " 210 ~ "`universal` and `canonical` from `mir.ndslice.topology` can be used to relax slice kind." 211 ~ tailErrorMessage!()); 212 }; 213 214 private alias IncFront(Seq...) = AliasSeq!(Seq[0] + 1, Seq[1 .. $]); 215 216 private alias DecFront(Seq...) = AliasSeq!(Seq[0] - 1, Seq[1 .. $]); 217 218 private enum bool isNotZero(alias t) = t != 0; 219 220 alias NSeqEvert(Seq...) = Filter!(isNotZero, DecFront!(Reverse!(IncFront!Seq))); 221 222 //alias Parts(Seq...) = DecAll!(IncFront!Seq); 223 224 alias Snowball(Seq...) = AliasSeq!(size_t.init, SnowballImpl!(size_t.init, Seq)); 225 226 private template SnowballImpl(size_t val, Seq...) 227 { 228 static if (Seq.length == 0) 229 alias SnowballImpl = AliasSeq!(); 230 else 231 alias SnowballImpl = AliasSeq!(Seq[0] + val, SnowballImpl!(Seq[0] + val, Seq[1 .. $])); 232 } 233 234 private template DecAll(Seq...) 235 { 236 static if (Seq.length == 0) 237 alias DecAll = AliasSeq!(); 238 else 239 alias DecAll = AliasSeq!(Seq[0] - 1, DecAll!(Seq[1 .. $])); 240 } 241 242 //template SliceFromSeq(Range, Seq...) 243 //{ 244 // static if (Seq.length == 0) 245 // alias SliceFromSeq = Range; 246 // else 247 // { 248 // import mir.ndslice.slice : Slice; 249 // alias SliceFromSeq = SliceFromSeq!(Slice!(Seq[$ - 1], Range), Seq[0 .. $ - 1]); 250 // } 251 //} 252 253 template DynamicArrayDimensionsCount(T) 254 { 255 static if (isDynamicArray!T) 256 enum size_t DynamicArrayDimensionsCount = 1 + DynamicArrayDimensionsCount!(typeof(T.init[0])); 257 else 258 enum size_t DynamicArrayDimensionsCount = 0; 259 } 260 261 bool isPermutation(size_t N)(auto ref const scope size_t[N] perm) 262 { 263 int[N] mask; 264 return isValidPartialPermutationImpl(perm, mask); 265 } 266 267 version(mir_ndslice_test) unittest 268 { 269 assert(isPermutation([0, 1])); 270 // all numbers 0..N-1 need to be part of the permutation 271 assert(!isPermutation([1, 2])); 272 assert(!isPermutation([0, 2])); 273 // duplicates are not allowed 274 assert(!isPermutation([0, 1, 1])); 275 276 size_t[0] emptyArr; 277 // empty permutations are not allowed either 278 assert(!isPermutation(emptyArr)); 279 } 280 281 bool isValidPartialPermutation(size_t N)(in size_t[] perm) 282 { 283 int[N] mask; 284 return isValidPartialPermutationImpl(perm, mask); 285 } 286 287 private bool isValidPartialPermutationImpl(size_t N)(in size_t[] perm, ref int[N] mask) 288 { 289 if (perm.length == 0) 290 return false; 291 foreach (j; perm) 292 { 293 if (j >= N) 294 return false; 295 if (mask[j]) //duplicate 296 return false; 297 mask[j] = true; 298 } 299 return true; 300 } 301 302 template ShiftNegativeWith(size_t N) 303 { 304 enum ShiftNegativeWith(sizediff_t i) = i < 0 ? i + N : i; 305 } 306 307 enum toSize_t(size_t i) = i; 308 enum toSizediff_t(sizediff_t i) = i; 309 enum isSize_t(alias i) = is(typeof(i) == size_t); 310 enum isSizediff_t(alias i) = is(typeof(i) == sizediff_t); 311 enum isIndex(I) = is(I : size_t); 312 template is_Slice(S) 313 { 314 static if (is(S : Slice!(IotaIterator!I), I)) 315 enum is_Slice = __traits(isIntegral, I); 316 else 317 enum is_Slice = false; 318 } 319 320 alias Repeat(size_t N : 0, T...) = AliasSeq!(); 321 322 private enum isReference(P) = 323 hasIndirections!P 324 || isFunctionPointer!P 325 || is(P == interface); 326 327 alias ImplicitlyUnqual(T) = Select!(isImplicitlyConvertible!(T, Unqual!T), Unqual!T, T); 328 alias ImplicitlyUnqual(T : T*) = T*; 329 330 size_t lengthsProduct(size_t N)(auto ref const scope size_t[N] lengths) 331 { 332 size_t length = lengths[0]; 333 foreach (i; Iota!(1, N)) 334 length *= lengths[i]; 335 return length; 336 } 337 338 pure nothrow version(mir_ndslice_test) unittest 339 { 340 const size_t[3] lengths = [3, 4, 5]; 341 assert(lengthsProduct(lengths) == 60); 342 assert(lengthsProduct([3, 4, 5]) == 60); 343 } 344 345 package(mir) template strideOf(args...) 346 { 347 static if (args.length == 0) 348 enum strideOf = args; 349 else 350 { 351 @fmamath @property auto ref ls()() 352 { 353 import mir.ndslice.topology: stride; 354 return stride(args[0]); 355 } 356 alias strideOf = AliasSeq!(ls, strideOf!(args[1..$])); 357 } 358 } 359 360 package(mir) template frontOf(size_t n) 361 { 362 enum frontOf = () { 363 string ret; 364 static foreach (i; 0 .. n) 365 { 366 if (i) 367 ret ~= `, `; 368 ret ~= "slices[" ~ i.stringof ~ `].front`; 369 } 370 return ret; 371 } (); 372 } 373 374 package(mir) template frontOf2(args...) 375 { 376 static if (args.length == 0) 377 enum frontOf2 = args; 378 else 379 { 380 @fmamath @property auto frontOf2Mod()() 381 { 382 return args[0].front; 383 } 384 alias frontOf2 = AliasSeq!(frontOf2Mod, frontOf2!(args[1..$])); 385 } 386 } 387 388 package(mir) template backOf(args...) 389 { 390 static if (args.length == 0) 391 enum backOf = args; 392 else 393 { 394 @fmamath @property auto ref backOfMod()() 395 { 396 return args[0].back; 397 } 398 alias backOf = AliasSeq!(backOfMod, backOf!(args[1..$])); 399 } 400 } 401 402 package(mir) template frontOfD(size_t dimension, args...) 403 { 404 static if (args.length == 0) 405 enum frontOfD = args; 406 else 407 { 408 @fmamath @property auto ref frontOfDMod()() 409 { 410 return args[0].front!dimension; 411 } 412 alias frontOfD = AliasSeq!(frontOfDMod, frontOfD!(dimension, args[1..$])); 413 } 414 } 415 416 package(mir) template backOfD(size_t dimension, args...) 417 { 418 static if (args.length == 0) 419 enum backOfD = args; 420 else 421 { 422 @fmamath @property auto ref backOfDMod()() 423 { 424 return args[0].back!dimension; 425 } 426 alias backOfD = AliasSeq!(backOfDMod, backOfD!(dimension, args[1..$])); 427 } 428 } 429 430 package(mir) template frontOfDim(size_t dim, args...) 431 { 432 static if (args.length == 0) 433 enum frontOfDim = args; 434 else 435 { 436 alias arg = args[0]; 437 @fmamath @property auto ref frontOfDimMod() 438 { 439 return arg.front!dim; 440 } 441 alias frontOfDim = AliasSeq!(frontOfDimMod, frontOfDim!(dim, args[1..$])); 442 } 443 } 444 445 package(mir) template selectFrontOf(alias input, args...) 446 { 447 static if (args.length == 0) 448 enum selectFrontOf = args; 449 else 450 { 451 alias arg = args[0]; 452 @fmamath @property auto ref selectFrontOfMod()() 453 { 454 return arg.lightScope.selectFront!0(input); 455 } 456 alias selectFrontOf = AliasSeq!(selectFrontOfMod, selectFrontOf!(input, args[1..$])); 457 } 458 } 459 460 package(mir) template selectBackOf(alias input, args...) 461 { 462 static if (args.length == 0) 463 enum selectBackOf = args; 464 else 465 { 466 alias arg = args[0]; 467 @fmamath @property auto ref selectBackOfMod()() 468 { 469 return arg.selectBack!0(input); 470 } 471 alias selectBackOf = AliasSeq!(selectBackOfMod, selectBackOf!(input, args[1..$])); 472 } 473 } 474 475 package(mir) template frontSelectFrontOf(alias input, args...) 476 { 477 static if (args.length == 0) 478 enum frontSelectFrontOf = args; 479 else 480 { 481 alias arg = args[0]; 482 @fmamath @property auto ref frontSelectFrontOfMod()() 483 { 484 return arg.lightScope.front.selectFront!0(input); 485 } 486 alias frontSelectFrontOf = AliasSeq!(frontSelectFrontOfMod, frontSelectFrontOf!(input, args[1..$])); 487 } 488 } 489 490 package(mir) template frontSelectBackOf(alias input, args...) 491 { 492 static if (args.length == 0) 493 enum frontSelectBackOf = args; 494 else 495 { 496 alias arg = args[0]; 497 @fmamath @property auto ref frontSelectBackOfMod 498 ()() 499 { 500 return arg.lightScope.front.selectBack!0(input); 501 } 502 alias frontSelectBackOf = AliasSeq!(frontSelectBackOfMod 503 , frontSelectBackOf!(input, args[1..$])); 504 } 505 }