1 /*
2 Copyright (c) 2022 Zenw
3 Released under the MIT license
4 https://opensource.org/licenses/mit-license.php
5 */
6 
7 module ggeD.ggeD;
8 import std;
9 import ggeD.indexVec;
10 import mir.ndslice;
11 import ggeD.einsum;
12 import mir.ndslice.topology : iota;
13 
14 
15 auto gged(T,R,Args...)(R value,Args xyz) if(isInputRange!(Unqual!R))
16 {
17     auto gg = gged!T(xyz);
18     ulong idx = 0;
19     foreach(ijk;gg.index)
20     {
21         gg[ijk] = value.front;
22         value.popFront();
23     }
24     return gg;
25 }
26 
27 auto gged(T,size_t N)(ulong[N] xyz)  
28 {
29     return GgedStruct!(T*,N,false,mir_slice_kind.contiguous)(slice!(T)(xyz));
30 }
31 auto gged(T,Args...)(Args xyz)  if(allSameType!(Args) && isIntegral!(Args[0])) 
32 {
33     return GgedStruct!(T*,Args.length,false,mir_slice_kind.contiguous)(slice!(T)(xyz));
34 }
35 auto gged(X,ulong Y,SliceKind Z)(Slice!(X,Y,Z) slice_) 
36 {
37     return GgedStruct!(X,Y,false, Z)(slice_);
38 }
39 T gged(T)(T value) if(!__traits(isSame, TemplateOf!(T), Slice))
40 {
41     return value;
42 }
43 
44 private struct IndexLoop(GGED) 
45 {
46     private alias MakeSerialIndex(X) =  SerialIndex!X;
47     GGED gg;
48     alias IndexTypes =GGED.IndexTypes;
49     alias TypeSerialIndex = staticMap!(MakeSerialIndex,IndexTypes);
50 
51     int opApply(int delegate(TypeSerialIndex) fun) {
52         mixin(genLoop!(false,0));
53         return 1;
54     }
55     alias Rank = GGED.Rank;
56     static if(Rank > 1)
57     {
58         int opApply(int delegate(IndexVec!(IndexTypes)) fun) {
59             mixin(genLoop!(true,0));
60             return 1;
61         }
62         static foreach(N ; 1 .. Rank -1)
63         {
64             int opApply(int delegate(IndexVec!(IndexTypes[0..Rank-N]),IndexTypes[Rank-N .. $]) fun) {
65                 mixin(genLoop!(true,N));
66                 return 1;
67             }
68         }
69     }
70     static string genLoop(bool vec,ulong N)(){
71         string result;
72         static foreach(idx;0..Rank){
73             result ~= "foreach(_" ~idx.to!string~ ";0 .. gg._slice.shape["~idx.to!string~"])\n";
74         }
75         result ~= "fun(";
76 
77        	static if(vec) result ~= "IndexVec!(IndexTypes[0..Rank-"~N.to!string~"])(";
78         static foreach(idx;0..Rank-N)
79 	        result ~= "SerialIndex!(IndexTypes["~idx.to!string~"])(gg.invIndex!"~idx.to!string~"(_" ~idx.to!string ~"),gg._slice.shape["~idx.to!string~"],gg.offsets["~idx.to!string~"]),";
80        	static if(vec) result ~= "),";
81 
82         static foreach(idx;Rank - N .. Rank)
83             result ~=  "SerialIndex!(IndexTypes["~idx.to!string~"])(gg.invIndex!"~idx.to!string~"(_" ~idx.to!string ~"),gg._slice.shape["~idx.to!string~"],gg.offsets["~idx.to!string~"]),";
84 
85         result ~= ");";
86         return result;
87     }
88 }
89 
90 @nogc nothrow auto setOffset(GGED,ulong Rank)(GGED gged,double[Rank] offsets) if(is(GGED == GgedStruct!(T,Rank,isOffsetIndex,kind),T,bool isOffsetIndex,SliceKind kind))
91 {
92     return GgedStruct!(GGED.TypePointer,GGED.Rank,true,GGED.Kind)(gged._slice,offsets.tupleof);
93 }
94 
95 @nogc nothrow auto setOffset(R,ulong Rank)(R value,double[Rank] offsets) if(!is(R == GgedStruct!(T,Rank,isOffsetIndex,kind),T,bool isOffsetIndex,SliceKind kind) )
96 {
97     return value;
98 }
99 
100 
101 @nogc nothrow auto setOffset(GGED,Args...)(GGED gged,Args offsets) 
102 {
103     static if(is(GGED == GgedStruct!(T,Rank,isOffsetIndex,kind),T,ulong Rank,bool isOffsetIndex,SliceKind kind) && Args.length == Rank && allSatisfy!(isFloatingPoint,Args))
104     return GgedStruct!(GGED.TypePointer,GGED.Rank,true,GGED.Kind)(gged._slice,offsets);
105     else
106     return gged;
107 }
108 
109  auto sum(T,ulong N,bool offsetflag,SliceKind kind)(GgedStruct!(T,N,offsetflag,kind) g)
110 {
111     auto result = 0.;
112     foreach(ijk;g.index)
113     {
114         result += g[ijk];
115     }
116     return result;
117 }
118 
119 alias Gged(T,ulong Rank, bool isOffsetIndex = false,SliceKind kind = SliceKind.contiguous) = GgedStruct!(T*,Rank,isOffsetIndex,kind);
120 
121 struct GgedStruct(T,ulong RANK, bool isOffsetIndex = false,SliceKind kind = SliceKind.contiguous)
122 {
123 	import mir.ndslice;
124     alias SliceType = Slice!(T, Rank, kind);
125     SliceType _slice;
126     alias Kind = kind;
127 
128     static if(isOffsetIndex)
129         alias IndexTypes = Repeat!(RANK,double);
130     else
131         alias IndexTypes = Repeat!(RANK,size_t);
132 
133     IndexTypes offsets = 0;
134 
135     private size_t getIndex(ulong n,Arg)(Arg idx)
136     {
137         return cast(size_t)(idx-offsets[n]);
138     }
139 
140     auto dup()
141     {
142         auto d = gged!(Type)(shape).setOffset(offsets);
143         foreach(ijk;this.index)
144         {
145             d[ijk] = this[ijk];
146         }
147         return d;
148     }
149 
150     private IndexTypes[n] invIndex(ulong n)(size_t idx)
151     {
152         return cast(IndexTypes[n])(idx+offsets[n]);
153     }
154     private auto getIndexes(Args...)(Args idxs) if(Args.length == RANK)
155     {
156         template Filting(X)
157         {
158             static if(isContiguousSlice!X) alias Filting = X;
159             else alias Filting = size_t;
160         }
161         struct Dummy{
162             staticMap!(Filting,Args) value;
163             alias value this;
164         }
165         Dummy result;
166         static foreach(i;0..RANK)
167         {
168             static if(isContiguousSlice!(Args[i])) 
169                 result[i] =  idxs[i];
170             else
171                 result[i] = getIndex!i(idxs[i]);
172         }
173         return result;
174     }
175     
176     
177     static if(isPointer!T)
178     {
179     	alias Type = PointerTarget!T;
180     }
181     else static if(isArray!T)
182     {
183         alias Type = ElementType!T;
184     }
185     else
186     {
187         alias Type = ReturnType!(T.opUnary!"*");
188     } 
189 	alias TypePointer = T;
190 	alias Rank = Alias!(RANK);
191 
192     alias _slice this;
193     
194     @nogc nothrow auto shape() => _slice.shape; 
195     
196     @nogc nothrow auto index(){
197         return IndexLoop!(typeof(this))(this);
198     }
199     template PickupArray(Ns...)
200         {
201             alias PickupArray = AliasSeq!();
202             static foreach(i; Ns)
203             {
204                 PickupArray = AliasSeq!(PickupArray,offsets[i]);
205             }
206         }
207 
208     /// Ns次元だけを走査するindexでかえす
209     @nogc nothrow auto index(Ns...)(){
210         template allInDim(ulong N){alias allInDim= Alias!(N<Rank);}
211         static assert(allSatisfy!(allInDim,Ns),"all Ns should be lower than Rank");
212         auto newone = this.pick!(Ns)(offsets).setOffset(PickupArray!(Ns));
213         return IndexLoop!(typeof(newone))(newone);
214     }
215     
216     auto toString()=>_slice.to!string;
217 
218     /// pick!(Args...)(ijk)
219     /// Argsにない次元の要素がijkの位置を含むArgs.length次元のgged配列を返す
220     /// pick!0(ijk)では、ijkを含む0次元目への1次配列になる
221     template pick(ns...)
222     {
223         @nogc nothrow auto pick(Args...)(Args value)
224         {
225             mixin(genPick([ns]));
226         }
227     }
228         
229     private static string genPick(ulong[] ns)
230     {
231         string result =  "return this[";
232         foreach(i ; 0..Rank)
233         {
234             if(!ns.canFind(i))
235                 result ~= "value["~i.to!string~"],";
236             else
237                 result ~= "offsets["~i.to!string~"] .. $,";
238         }
239         result ~= "];";
240         return result;
241     }
242     @nogc nothrow auto opSlice(X,Y)(X start, Y end) if(RANK == 1 && is(X == SerialIndex!(IndexTypes[0])) && is(Y == SerialIndex!(IndexTypes[0])))
243     {
244         return gged(_slice.opSlice(start,end));
245     }
246     @nogc nothrow auto opSlice(size_t dim,X,Y)(X start, Y end) 
247     {
248         return _slice.opSlice!dim(getIndex!dim(start),getIndex!dim(end));
249     }
250     @nogc nothrow auto opIndex(IndexVec!(IndexTypes) args){
251         return gged(_slice.opIndex(getIndexes(args.idx).value ));
252     }
253     static foreach(N; 1 .. Rank-1)
254     {
255         @nogc nothrow auto opIndex(Args...)(IndexVec!(IndexTypes[0..Rank-N]) args1,Args args2) if(Args.length == N) {
256             return gged(_slice.opIndex(getIndexes(args1.idx,args2).value));
257         }
258     }
259     @nogc nothrow auto opIndex(Args...)(Args args) if(Args.length == Rank) {
260         return gged(_slice.opIndex(getIndexes(args).value));
261     }
262     import std.traits;
263     static if( __traits(compiles, () {T itr; itr[0] = Type.init; } ))
264     {
265         @nogc nothrow auto opIndexAssign(AssignType)(AssignType value,IndexVec!(IndexTypes) args){
266             _slice.opIndexAssign(value,getIndexes(args.idx).value);
267         }
268         
269         static foreach(N; 1 .. Rank-1)
270         {
271             @nogc nothrow auto opIndexAssign(AssignType,Args...)(AssignType value,IndexVec!(IndexTypes[0..Rank-N])  args1,Args args2) if(Args.length == N) {
272                 _slice.opIndexAssign(value,getIndexes(args1.idx,args2).value);
273             }
274         }
275         @nogc nothrow auto opIndexAssign(AssignType,Args...)(AssignType value,Args args) if(Args.length == Rank)  {
276             _slice.opIndexAssign(value,getIndexes(args).value);
277         }
278     }
279     @nogc nothrow auto opDollar(ulong dim)(){
280         return invIndex!dim(_slice.opDollar!dim);
281     }
282     @nogc nothrow auto opDispatch(string idx)() if(idx.replace("_","").length == Rank)
283     {
284         return Leaf!("",idx.replace("_",""),typeof(this))(this);
285     }
286     auto opEquals(RHS)(RHS rhs)
287     {
288         static if(is(RHS == GgedStruct!(T2,RANK,isOffsetIndex2,kind2),T2,bool isOffsetIndex2,SliceKind kind2))
289         {
290             bool result = true;
291             foreach(ijk ; index)
292             {
293                 result &= isClose(rhs[ijk] , this[ijk] );
294             }
295             return result;
296         }
297         else
298         {
299             return _slice == rhs;
300         }
301     }
302 
303     auto opBinary(string op,GGED)(GGED rhs) 
304     {
305         static if(is(GGED==GgedStruct!(T2,Rank,isOffsetIndex2,kind2),T2,bool isOffsetIndex2,SliceKind kind2))
306             return _slice.opBinary!(op,T2,Rank,kind2)(rhs._slice).gged.setOffset(offsets);
307         else
308             return _slice.opBinary!(op,GGED)(rhs).gged.setOffset(offsets);
309     }
310     
311     auto opUnary(string op)()
312         if (op == "*" || op == "~" || op == "-" || op == "+")
313     {
314         import mir.ndslice.topology: map;
315         static if (op == "+")
316             return this;
317         else
318             return _slice.opUnary!op.gged.setOffset(offsets);
319     }
320 }