1 /++ 2 This is a submodule of $(MREF mir,ndslice). 3 4 NdField is a type with `opIndex(size_t[N] index...)` primitive. 5 An ndslice can be created on top of a ndField using $(SUBREF slice, slicedNdField). 6 7 $(BOOKTABLE $(H2 NdFields), 8 $(TR $(TH NdField Name) $(TH Used By)) 9 $(T2 Cartesian, $(SUBREF topology, cartesian)) 10 $(T2 Kronecker, $(SUBREF topology, kronecker)) 11 ) 12 13 See_also: $(SUBREF concatenation, concatenation). 14 15 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 16 Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments 17 Authors: Ilia Ki 18 19 Macros: 20 SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) 21 T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 22 +/ 23 module mir.ndslice.ndfield; 24 25 import mir.qualifier; 26 import mir.internal.utility; 27 import mir.ndslice.internal; 28 import mir.ndslice.slice; 29 import mir.primitives; 30 import std.meta; 31 32 private template _indices(NdFields...) 33 { 34 static if (NdFields.length == 0) 35 enum _indices = ""; 36 else 37 { 38 alias Next = NdFields[0 .. $ - 1]; 39 enum i = Next.length; 40 enum _indices = ._indices!Next ~ 41 "_fields[" ~ i.stringof ~ "][" ~ _indices_range!([staticMap!(DimensionCount, Next)].sum, DimensionCount!(NdFields[$ - 1])) ~ "], "; 42 } 43 } 44 45 private template _indices_range(size_t begin, size_t count) 46 { 47 static if (count == 0) 48 enum _indices_range = ""; 49 else 50 { 51 enum next = count - 1; 52 enum elem = begin + next; 53 enum _indices_range = ._indices_range!(begin, next) ~ "indices[" ~ elem.stringof ~ "], "; 54 } 55 } 56 57 /// 58 struct Cartesian(NdFields...) 59 if (NdFields.length > 1) 60 { 61 /// 62 NdFields _fields; 63 64 package(mir) enum size_t M(size_t f) = [staticMap!(DimensionCount, NdFields[0..f])].sum; 65 package(mir) enum size_t N = M!(NdFields.length); 66 67 /// 68 auto lightConst()() const @property 69 { 70 import std.format; 71 import mir.ndslice.topology: iota; 72 return mixin("Cartesian!(staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota)); 73 } 74 75 /// 76 auto lightImmutable()() immutable @property 77 { 78 import std.format; 79 import mir.ndslice.topology: iota; 80 return mixin("Cartesian!(staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota)); 81 } 82 83 /// 84 size_t length(size_t d = 0)() @safe scope const @property 85 { 86 foreach(f, NdField; NdFields) 87 static if (M!f <= d && M!(f + 1) > d) 88 { 89 enum d = d - M!f; 90 static if (d) 91 return _fields[f].length!(d - M!f); 92 else 93 return _fields[f].length; 94 } 95 } 96 97 /// 98 size_t[N] shape()() @safe scope const @property 99 { 100 typeof(return) ret; 101 foreach(f, NdField; NdFields) 102 { 103 static if (hasShape!NdField) 104 { 105 auto s = _fields[f].shape; 106 foreach(j; Iota!(s.length)) 107 ret[M!f + j] = s[j]; 108 } 109 else 110 { 111 ret[M!f] = _fields[f].length; 112 } 113 } 114 return ret; 115 } 116 117 /// 118 size_t elementCount()() @safe scope const @property 119 { 120 size_t ret = 1; 121 foreach (f, NdField; NdFields) 122 ret *= _fields[f].elementCount; 123 return ret; 124 } 125 126 /// 127 auto opIndex(size_t[N] indices...) 128 { 129 import mir.functional : tuple; 130 return mixin("tuple(" ~ _indices!(NdFields) ~ ")"); 131 } 132 } 133 134 private template _kr_indices(size_t n) 135 { 136 static if (n == 0) 137 enum _kr_indices = ""; 138 else 139 { 140 enum i = n - 1; 141 enum _kr_indices = ._kr_indices!i ~ "_fields[" ~ i.stringof ~ "][ind[" ~ i.stringof ~ "]], "; 142 } 143 } 144 145 /// 146 struct Kronecker(alias fun, NdFields...) 147 if (NdFields.length > 1 && allSatisfy!(templateOr!(hasShape, hasLength), NdFields[1 .. $])) 148 { 149 /// 150 NdFields _fields; 151 152 /// 153 auto lightConst()() const @property 154 { 155 import std.format; 156 import mir.ndslice.topology: iota; 157 return mixin("Kronecker!(fun, staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota)); 158 } 159 160 /// 161 auto lightImmutable()() immutable @property 162 { 163 import std.format; 164 import mir.ndslice.topology: iota; 165 return mixin("Kronecker!(fun, staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota)); 166 } 167 168 private enum N = DimensionCount!(NdFields[$-1]); 169 170 /// 171 size_t length(size_t d = 0)() scope const @property 172 { 173 static if (d == 0) 174 { 175 size_t ret = 1; 176 foreach (f, NdField; NdFields) 177 ret *= _fields[f].length; 178 } 179 else 180 { 181 size_t ret = 1; 182 foreach (f, NdField; NdFields) 183 ret *= _fields[f].length!d; 184 } 185 return ret; 186 } 187 188 189 /// 190 size_t[N] shape()() scope const @property 191 { 192 static if (N > 1) 193 { 194 size_t[N] ret = 1; 195 foreach (f, NdField; NdFields) 196 { 197 auto s = _fields[f].shape; 198 foreach(i; Iota!N) 199 ret[i] *= s[i]; 200 } 201 return ret; 202 } 203 else 204 { 205 size_t[1] ret = 1; 206 foreach (f, NdField; NdFields) 207 ret[0] *= _fields[f].length; 208 return ret; 209 } 210 } 211 212 /// 213 size_t elementCount()() scope const @property 214 { 215 size_t ret = 1; 216 foreach (f, NdField; NdFields) 217 ret *= _fields[f].elementCount; 218 ret; 219 } 220 221 /// 222 auto ref opIndex()(size_t[N] indices...) 223 { 224 static if (N > 1) 225 size_t[N][NdFields.length] ind; 226 else 227 size_t[NdFields.length] ind; 228 foreach_reverse (f, NdField; NdFields) 229 { 230 static if (f) 231 { 232 static if (hasShape!(NdFields[f])) 233 { 234 auto s = _fields[f].shape; 235 } 236 else 237 { 238 size_t[1] s; 239 s[0] = _fields[f].length; 240 } 241 static if (N > 1) 242 { 243 foreach(i; Iota!N) 244 { 245 ind[f][i] = indices[i] % s[i]; 246 indices[i] /= s[i]; 247 } 248 } 249 else 250 { 251 ind[f] = indices[0] % s[0]; 252 indices[0] /= s[0]; 253 } 254 } 255 else 256 { 257 static if (N > 1) 258 { 259 foreach(i; Iota!N) 260 ind[f][i] = indices[i]; 261 } 262 else 263 { 264 ind[f] = indices[0]; 265 } 266 } 267 } 268 return mixin("fun(" ~ _kr_indices!(ind.length) ~ ")"); 269 } 270 }