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 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments
17 Authors: Ilya Yaroshenko
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 : refTuple;
130         return mixin("refTuple(" ~ _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 }