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