1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
//#![feature(const_generics, const_evaluatable_checked)]

use crate::dimutils;


/// Implement by specifying the const DIM to give your implementation a dimension.
pub trait TensorDimension {
    const DIM : usize;
    const ISSCALAR : bool = Self::DIM == 0;
    fn is_scalar(&self) -> bool {Self::ISSCALAR}
}



///  Expresses tensor-like objects with a size. Due to current compiler constraints, takes the dimension as an argument 
///  in addition to it being an associated type, to avoid internal compiler errors in use.
pub trait TensorSize<const DIM:usize> : TensorDimension {
    fn size(&self) -> [usize;DIM];

    fn inbounds(&self,index : [usize;DIM]) -> bool {
        index.iter().zip(self.size().iter()).all(|(i,s)| i < s)   
    }

    /// If this is false, you have an invalid implementation.
    const VALIDDIMS : bool = DIM == Self::DIM;
}

/// The main trait of this crate. Somehat like iterator, is effectively a lazy multidimensional array-like object
/// instead of a lazy sequence.
/// You can map over functions, zip them, take cartesian products, etc etc in a way that does
/// not erase structure, and produces another object with random access indexing unlike iterators.
pub trait Broadcastable<const DIM : usize> : TensorSize<DIM> + Sized {
    type Element;

    /// This should return none iff the default
    /// inbounds method returns false.
    fn bget(&self, index:[usize;DIM]) -> Option<Self::Element>;

    /// Takes indices modulo size. Useful for periodic shifts of indices.
    fn mod_bget(&self,index:[isize;DIM]) -> Self::Element {
        self.bget(dimutils::modular_index(index,self.size())).expect("Broken contract in Broadcast impl. Modular access out of bounds.")
    }

    /// Writes the contents of the Broadcastable to a receiver, such as an actual (multidimensional) array.
    fn feedto(&self,receiver :&mut impl BroadcastReceiver<DIM,Element=Self::Element>) -> Option<()>{
        receiver.receive(self)
    }

    /// Returns an object with the provided size. If NEWDIM > Self::DIM, the additional indices are ignored.
    /// All entries of size input must be either equal to the size of input, except for entries where the original size was 1.
    fn lazy_updim<const NEWDIM : usize>(&self, size : [usize;NEWDIM] ) -> LazyUpdim<Self,DIM,NEWDIM> 
    {
        assert!(Self::VALIDDIMS, "Invalid dimensions entered in Trait implementation."); // These asserts can be eliminated with more advanced
        assert!(NEWDIM >= DIM, "Updimmed tensor cannot have fewer indices than the initial one."); // const generic bounds on nightly. ( )
        LazyUpdim {size,reference:&self}
    }

    /// The main way to create multidimensional arrays with this crate, 
    /// but can resize any broadcastable. Resized broadcastables must have the
    /// same number of elements as the old one. Preserves iteration order.
    /// Reshapes a broadcastable by linearizing and delinearizing indices.
    fn reshaped<const NEWDIM : usize>(self,size: [usize;NEWDIM]) -> ReShaped<Self,NEWDIM,DIM> {
        ReShaped {underlying : self,size}
    }

    /// Provides a more flexible but less safe way to reshape a broadcastable. 
    /// Useful for slicing and dicing arrays and broadcastables, taking diagonals, etc etc.
    fn mapindex<F:Fn([usize;M],[usize;M]) -> [usize;DIM],const M : usize>(&self,indexclosure:F,sizeclosure : impl Fn([usize;DIM]) -> [usize;M] ) 
    -> MapIndex<Self,F,DIM,M>
    {
        MapIndex {reference: self,indexclosure,size:sizeclosure(self.size())}
    }


    /// Elements of returned broadcastable are the output of the provided closure, 
    /// applied to the input broadcastable.
    fn bmap<T,F :Fn(Self::Element) -> T>(&self,foo : F) -> BMap<T,Self,F,DIM>{
        assert!(Self::VALIDDIMS, "Invalid dimensions entered in Trait implementation.");
        BMap {reference:self,closure : foo}
    }

    /// For inputs whose elements are references, clones them.
    fn bcloned(self) -> BCloned<Self,DIM> {
        BCloned {underlying:self}
    }
    
    /// Unique operation not found in Iterators. If the dimensions of the two broadcastable, zips them. 
    /// If not, will lazy_updim all inputs to the same size. As such, can also provide cartesian or tensor products depending on input.
    fn broadcast2<'a,'b,T : Broadcastable<N>,const N : usize >(&'a self,foo: &'b T) 
        -> Broadcast2<LazyUpdim<'a,Self,DIM,{dimutils::cmax(DIM,N)}>,LazyUpdim<'b,T,N,{dimutils::cmax(DIM,N)}>,{dimutils::cmax(DIM,N)}> 
    {
        let commondims : [usize;dimutils::cmax(DIM,N)] = dimutils::commondims(self.size(),foo.size()).expect("F error handling");
        Broadcast2 {first : self.lazy_updim(commondims),second : foo.lazy_updim(commondims)}
    }

    /// Takes a broadcastable and returns an iterator over its elements.
    fn bc_iter(&self) -> BroadcastIterator<Self,DIM> {
        BroadcastIterator {reference:self,state : dimutils::ndim_iterator(self.size())}
    }
}

/// Trait that emulates write access to reshaped arrays.
pub trait BroadcastReceiver<const DIM : usize> : TensorSize<DIM> + Sized {
    type Element;
    fn bget_mut<'a>(&'a mut self, index: [usize;DIM]) -> Option<&'a mut Self::Element>;
    fn receive(&mut self, broadcast: &impl Broadcastable<DIM,Element=Self::Element>) -> Option<()>;
    
    // TODO: properly review the implementation of iter_mut, which is unsafe. This should need GATs to be safe.
    /// To be safe, the BroadcastReceiver must NOT alias bget_mut to the same underlying field for different inputs.
    /// You may implement a safe iter_mut method using this on any receiver where that invariant is preserved.
    unsafe fn bc_iter_mut<'a>(&'a mut self) -> BroadcastIterMut<'a,Self,DIM> {
        BroadcastIterMut {reference : self.into() ,state:  dimutils::ndim_iterator(self.size()),_marker: std::marker::PhantomData}
    }
}


/// Return type of bc_iter.
pub struct BroadcastIterator<'a,T : Broadcastable<DIM>,const DIM : usize> {
    reference : &'a T,
    state : dimutils::NDimIteratorHelper<DIM>,
}

impl<'a,T : Broadcastable<DIM>,const DIM : usize> Iterator for BroadcastIterator<'a,T,DIM> {
    type Item = T::Element;
    fn next(&mut self) -> Option<T::Element> {
        let index = self.state.next()?;
        self.reference.bget(index)
    }
    fn size_hint(&self) -> (usize,Option<usize>){
        self.state.size_hint()
    }
}

/// Return type of bc_iter_mut.
pub struct BroadcastIterMut<'a,T : BroadcastReceiver<DIM>,const DIM : usize> {
    reference : std::ptr::NonNull<T>,
    state :dimutils::NDimIteratorHelper<DIM>,
    _marker: std::marker::PhantomData<&'a mut T>,
}

impl<'a,T : BroadcastReceiver<DIM>,const DIM : usize> Iterator for BroadcastIterMut<'a,T,DIM> {
    type Item = &'a mut T::Element;
    fn next(&mut self) -> Option< &'a mut T::Element> {
        let index = self.state.next()?;
        let castreference =  unsafe {self.reference.as_mut::<'a>()};
        castreference.bget_mut(index)
    }
    fn size_hint(&self) -> (usize,Option<usize>){
        self.state.size_hint()
    }
}


/// Return type of reshaped.
pub struct ReShaped<T:Broadcastable<M>,const N : usize,const M : usize> {
    underlying : T,
    size : [usize;N]
}

impl<T:Broadcastable<M>,const N : usize,const M : usize> TensorDimension for ReShaped<T,N,M> {
    const DIM : usize = T::DIM;
}
impl<T:Broadcastable<M>,const N : usize,const M : usize> TensorSize<N> for ReShaped<T,N,M> {
    fn size(&self) -> [usize;N] {self.size}
}
impl<T:Broadcastable<M>,const N : usize,const M : usize> Broadcastable<N> for ReShaped<T,N,M> {
    type Element = T::Element;
    fn bget(&self,index:[usize;N])-> Option<T::Element> {
        if !self.inbounds(index) {return None}
        let linearindex = dimutils::linearize_index(index,self.size());
        let innerindex = dimutils::delinearize_index(linearindex,self.underlying.size());
        self.underlying.bget(innerindex)
    }
}
impl<'a,T:Broadcastable<M>,const N : usize,const M : usize> IntoIterator for &'a ReShaped<T,N,M> {
    type Item = T::Element;
    type IntoIter = BroadcastIterator<'a,ReShaped<T,N,M>,N>;
    fn into_iter(self) -> Self::IntoIter  {
        self.bc_iter()
    }
}





/// Return type of bcloned.
pub struct BCloned<T:Broadcastable<N>,const N : usize> {
    underlying : T,
}

impl<'a,T:Broadcastable<N>,const N : usize> TensorDimension for BCloned<T,N> {
    const DIM : usize = T::DIM;
}
impl<'a,T:Broadcastable<N>,const N : usize> TensorSize<N> for BCloned<T,N> {
    fn size(&self) -> [usize;N] {self.underlying.size()}
}
impl<'b,T:Broadcastable<N,Element=&'b E>,E : 'b + Clone,const N : usize> Broadcastable<N> for BCloned<T,N> 
{
    type Element = E;
    fn bget(&self,index:[usize;N]) -> Option<Self::Element>  {
         self.underlying.bget(index).cloned()
    }
}
impl<'a,'b,T:Broadcastable<N,Element=&'b E>,E : Clone + 'a + 'b,const N : usize> IntoIterator for &'a BCloned<T,N> {
    type Item = E;
    type IntoIter = BroadcastIterator<'a,BCloned<T,N>,N>;
    fn into_iter(self) -> Self::IntoIter  {
        self.bc_iter()
    }
}


/// Return type of bmap.
pub struct MapIndex<'a,T:Broadcastable<N>, F : Fn([usize;M],[usize;M]) -> [usize;N] ,const N : usize,const M : usize> {
    reference : &'a T,
    indexclosure : F,
    size : [usize;M],
}

impl<'a,T:Broadcastable<N>, F : Fn([usize;M],[usize;M]) -> [usize;N] ,const N : usize,const M : usize> TensorDimension for MapIndex<'a,T,F,N,M> {
    const DIM : usize = M;
}
impl<'a,T:Broadcastable<N>, F : Fn([usize;M],[usize;M]) -> [usize;N] ,const N : usize,const M : usize> TensorSize<M> for MapIndex<'a,T,F,N,M> {
    fn size(&self) -> [usize;M] {self.size}
}
impl<'a,T:Broadcastable<N>, F : Fn([usize;M],[usize;M]) -> [usize;N] ,const N : usize,const M : usize> Broadcastable<M> for MapIndex<'a,T,F,N,M> {
    type Element = T::Element;
    fn bget(&self,index : [usize;M]) -> Option<T::Element> {
        if !self.inbounds(index) {return None};
        let size = self.size;
        let indexclosure = &self.indexclosure;
        let newindex : [usize;N] = indexclosure(index,size);
        self.reference.bget(newindex)
    }
}

impl<'b,'a,T:Broadcastable<N>, F : Fn([usize;M],[usize;M]) -> [usize;N] ,const N : usize,const M : usize> IntoIterator for &'b MapIndex<'a,T,F,N,M> {
    type Item = T::Element;
    type IntoIter = BroadcastIterator<'b,MapIndex<'a,T,F,N,M>,M>;
    fn into_iter(self) -> Self::IntoIter  {
        self.bc_iter()
    }
} 

impl<'a,T:Broadcastable<N>, F : Fn([usize;M],[usize;M]) -> [usize;N] ,const N : usize,const M : usize>  MapIndex<'a,T,F,N,M> {
    pub fn iter(&self) -> BroadcastIterator<MapIndex<'a,T,F,N,M>,M> {
        self.into_iter()
    }
}



/// Return type of broadcast2.
pub struct Broadcast2<A : Broadcastable<N>,B : Broadcastable<N>, const N : usize> {
    first: A,
    second: B
}

impl<A : Broadcastable<N>,B : Broadcastable<N>, const N : usize> TensorDimension for Broadcast2<A,B,N> {
    const DIM : usize = N;
}
impl<A : Broadcastable<N>,B : Broadcastable<N>, const N : usize> TensorSize<N> for Broadcast2<A,B,N> {
    fn size(&self) -> [usize;N] {
        self.first.size()
    }
}
impl<A : Broadcastable<N>,B : Broadcastable<N>, const N : usize> Broadcastable<N> for Broadcast2<A,B,N> {
    type Element = (A::Element,B::Element);
    fn bget(&self, index:[usize;N]) -> Option<Self::Element> {
        Some((self.first.bget(index)?,self.second.bget(index)?))
    }
}

impl<'b,A : Broadcastable<N>,B : Broadcastable<N>, const N : usize> IntoIterator for &'b Broadcast2<A,B,N> {
    type Item = (A::Element,B::Element);
    type IntoIter = BroadcastIterator<'b, Broadcast2<A,B,N> ,N>;
    fn into_iter(self) -> Self::IntoIter  {
        self.bc_iter()
    }
}
impl<A : Broadcastable<N>,B : Broadcastable<N>, const N : usize> Broadcast2<A,B,N> {
    pub fn iter(&self) -> BroadcastIterator<Broadcast2<A,B,N> ,N> {
        self.bc_iter()
    }
}


/// Return type of lazy_updim.
pub struct LazyUpdim<'a,T : Broadcastable<OLDDIM>,const OLDDIM : usize, const DIM : usize> {
    size : [usize;DIM],
    reference : &'a T
}

impl<'a,T : Broadcastable<OLDDIM>,const OLDDIM : usize,const DIM : usize> TensorDimension for LazyUpdim<'a,T,OLDDIM,DIM> {
    const DIM : usize = DIM;
}
impl<'a,T : Broadcastable<OLDDIM>,const OLDDIM : usize,const DIM : usize> TensorSize<DIM> for LazyUpdim<'a,T,OLDDIM,DIM> {
    fn size(&self) -> [usize;DIM] {self.size}
}
impl<'a,T : Broadcastable<OLDDIM>,const OLDDIM : usize,const DIM : usize>  Broadcastable<DIM>  for LazyUpdim<'a,T,OLDDIM,DIM> {
    type Element = T::Element;
    fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
        assert!(DIM >= OLDDIM);
        if !self.inbounds(index) {return None}
        let size = self.size();
        let newindex : [usize;OLDDIM] = array_init::array_init(|i| if size[i] > 1 {index[i]} else {0});
        self.reference.bget(newindex)
    }
}

impl<'b,'a,T : Broadcastable<OLDDIM>,const OLDDIM : usize,const DIM : usize> IntoIterator for &'b LazyUpdim<'a,T,OLDDIM,DIM> {
    type Item = T::Element;
    type IntoIter = BroadcastIterator<'b,LazyUpdim<'a,T,OLDDIM,DIM>,DIM>;
    fn into_iter(self) -> Self::IntoIter  {
        self.bc_iter()
    }
}

impl<'a,T : Broadcastable<OLDDIM>,const OLDDIM : usize,const DIM : usize> LazyUpdim<'a,T,OLDDIM,DIM> {
    pub fn iter(&self) ->  BroadcastIterator<LazyUpdim<'a,T,OLDDIM,DIM>,DIM>  {
        self.bc_iter()
    }
}


/// Return type of bmap.
pub struct BMap<'a,R, T : Broadcastable<DIM>, F :  Fn(T::Element) -> R  , const DIM: usize> {
    reference : &'a T,
    closure : F
}

impl<'a,R, T : Broadcastable<DIM>, F :  Fn(T::Element) -> R  , const DIM: usize> TensorDimension for BMap<'a,R,T,F,DIM> {
    const DIM : usize = DIM;
}
impl<'a,R, T : Broadcastable<DIM>, F :  Fn(T::Element) -> R  , const DIM: usize> TensorSize<DIM> for BMap<'a,R,T,F,DIM> {
    fn size(&self) -> [usize;DIM] {self.reference.size()}
}
impl<'a,R, T : Broadcastable<DIM>, F :  Fn(T::Element) -> R  , const DIM: usize> Broadcastable<DIM> for BMap<'a,R,T,F,DIM> {
    type Element = R;
    fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
        self.reference.bget(index).map(&self.closure)
    }
}

impl<'b,'a,R, T : Broadcastable<DIM>, F :  Fn(T::Element) -> R  , const DIM: usize> IntoIterator for &'b BMap<'a,R,T,F,DIM> {
    type Item = R;
    type IntoIter = BroadcastIterator<'b,BMap<'a,R,T,F,DIM>,DIM>;
    fn into_iter(self) -> Self::IntoIter  {
        self.bc_iter()
    }
}





impl<T> TensorDimension for Vec<T> {
    const DIM : usize = 1;
}
impl<T> TensorSize<1> for Vec<T> {
    fn size(&self) -> [usize;1] {[self.len()]}
}
impl<'a,T> TensorDimension for &'a Vec<T> {
    const DIM : usize = 1;
}
impl<'a, T> TensorSize<1> for &'a Vec<T> {
    fn size(&self) -> [usize;1] {[self.len()]}
}
impl<'a,T> TensorDimension for &'a mut Vec<T> {
    const DIM : usize = 1;
}
impl<'a, T> TensorSize<1> for &'a mut Vec<T> {
    fn size(&self) -> [usize;1] {[self.len()]}
}

impl<'a,T> Broadcastable<1> for &'a Vec<T> {
    type Element = &'a T;
    fn bget(&self,index : [usize;1]) -> Option<&'a T> {
        self.get(index[0])
    }
}
impl<T: Copy> Broadcastable<1> for Vec<T> {
    type Element = T;
    fn bget(&self,index : [usize;1]) -> Option<T> {
        self.get(index[0]).cloned()
    }
}



impl<'a,T> BroadcastReceiver<1> for &'a mut Vec<T> {
    type Element = T;
    fn bget_mut(&mut self,[index] : [usize;1]) -> Option<&mut T> {
        self.get_mut(index)
    }

    fn receive(&mut self, broadcast: &impl Broadcastable<1,Element=Self::Element>) -> Option<()> {
        self.clear();
        for i in 0..broadcast.size()[0] {
            self.push(broadcast.bget([i])?);
        }
        Some(())
    }
}