Skip to main content

minidx_core/shapes/
shape.rs

1use super::{Axes, Axis, HasAxes};
2use super::{Axes2, Axes3, Axes4, Axes5, Axes6};
3use super::{Const, ConstDim, Dim};
4
5/// Represents either `[T; N]` or `Vec<T>`
6pub trait Array<T>: IntoIterator<Item = T> {
7    type Dim: Dim;
8    fn dim(&self) -> Self::Dim;
9}
10impl<T, const N: usize> Array<T> for [T; N] {
11    type Dim = Const<N>;
12    fn dim(&self) -> Self::Dim {
13        Const
14    }
15}
16impl<T> Array<T> for std::vec::Vec<T> {
17    type Dim = usize;
18    fn dim(&self) -> Self::Dim {
19        self.len()
20    }
21}
22
23/// A collection of dimensions ([Dim]) that change how a multi-dimensional
24/// array is interacted with.
25pub trait Shape:
26    'static
27    + std::fmt::Debug
28    + Clone
29    + Copy
30    + Send
31    + Sync
32    + Eq
33    + PartialEq
34    + HasAxes<Self::AllAxes>
35    + HasAxes<Self::LastAxis> // + ReduceShapeTo<(), Self::AllAxes>
36// + ReduceShape<Self::LastAxis>
37{
38    /// The number of dimensions the shape has
39    const NUM_DIMS: usize;
40
41    /// Is `[usize; Self::NUM_DIMS]`, but that is not usable yet.
42    type Concrete: std::fmt::Debug
43        + Clone
44        + Copy
45        + Default
46        + Eq
47        + PartialEq
48        + std::ops::Index<usize, Output = usize>
49        + std::ops::IndexMut<usize>
50        + Send
51        + Sync
52        + IntoIterator<Item = usize>
53        + Into<std::vec::Vec<usize>>
54        + AsRef<[usize]>;
55
56    /// All the axes of this shape
57    type AllAxes: Axes;
58
59    /// The last axis of this shape
60    type LastAxis: Axes;
61
62    fn concrete(&self) -> Self::Concrete;
63    fn from_concrete(concrete: &Self::Concrete) -> Option<Self>;
64
65    /// The number of elements in this shape; the product of all dimensions.
66    #[inline(always)]
67    fn num_elements(&self) -> usize {
68        self.concrete().into_iter().product()
69    }
70
71    /// The strides of how this shape is layed out in memory.
72    #[inline(always)]
73    fn strides(&self) -> Self::Concrete {
74        let sizes = self.concrete();
75        let mut strides: Self::Concrete = Default::default();
76        strides[Self::NUM_DIMS - 1] = 1;
77        for i in (0..(Self::NUM_DIMS - 1)).rev() {
78            strides[i] = strides[i + 1] * sizes[i + 1];
79        }
80        strides
81    }
82}
83
84/// Represents a [Shape] that has all [ConstDim]s
85pub trait ConstShape: Default + Shape {
86    const NUMEL: usize;
87}
88
89/// Represents something that has a [Shape].
90pub trait HasShape {
91    type WithShape<New: Shape>: HasShape<Shape = New>;
92    type Shape: Shape;
93    fn shape(&self) -> &Self::Shape;
94}
95
96impl<S: Shape> HasShape for S {
97    type WithShape<New: Shape> = New;
98    type Shape = Self;
99    fn shape(&self) -> &Self::Shape {
100        self
101    }
102}
103
104/// Compile time known shape with 0 dimensions
105pub type Rank0 = ();
106/// Compile time known shape with 1 dimensions
107pub type Rank1<const M: usize> = (Const<M>,);
108/// Compile time known shape with 2 dimensions
109pub type Rank2<const M: usize, const N: usize> = (Const<M>, Const<N>);
110/// Compile time known shape with 3 dimensions
111pub type Rank3<const M: usize, const N: usize, const O: usize> = (Const<M>, Const<N>, Const<O>);
112/// Compile time known shape with 4 dimensions
113pub type Rank4<const M: usize, const N: usize, const O: usize, const P: usize> =
114    (Const<M>, Const<N>, Const<O>, Const<P>);
115/// Compile time known shape with 5 dimensions
116pub type Rank5<const M: usize, const N: usize, const O: usize, const P: usize, const Q: usize> =
117    (Const<M>, Const<N>, Const<O>, Const<P>, Const<Q>);
118#[rustfmt::skip]
119/// Compile time known shape with 6 dimensions
120pub type Rank6<const M: usize, const N: usize, const O: usize, const P: usize, const Q: usize, const R: usize> =
121    (Const<M>, Const<N>, Const<O>, Const<P>, Const<Q>, Const<R>);
122
123macro_rules! shape {
124    (($($D:tt $Idx:tt),*), rank=$Num:expr, all=$All:tt) => {
125        impl<$($D: Dim, )*> Shape for ($($D, )*) {
126            const NUM_DIMS: usize = $Num;
127            type Concrete = [usize; $Num];
128            type AllAxes = $All<$($Idx,)*>;
129            type LastAxis = Axis<{$Num - 1}>;
130            #[inline(always)]
131            fn concrete(&self) -> Self::Concrete {
132                [$(self.$Idx.size(), )*]
133            }
134            #[inline(always)]
135            fn from_concrete(concrete: &Self::Concrete) -> Option<Self> {
136                Some(($(Dim::from_size(concrete[$Idx])?, )*))
137            }
138        }
139        impl<$($D: ConstDim, )*> ConstShape for ($($D, )*) {
140            const NUMEL: usize = $($D::SIZE * )* 1;
141         }
142
143        impl Shape for [usize; $Num] {
144            const NUM_DIMS: usize = $Num;
145            type Concrete = Self;
146            type AllAxes = $All<$($Idx,)*>;
147            type LastAxis = Axis<{$Num - 1}>;
148
149            fn concrete(&self) -> Self::Concrete {
150                *self
151            }
152
153            fn from_concrete(concrete: &Self::Concrete) -> Option<Self> {
154                Some(*concrete)
155            }
156        }
157    };
158}
159
160impl Shape for () {
161    const NUM_DIMS: usize = 0;
162    type Concrete = [usize; 0];
163    type AllAxes = Axis<0>;
164    type LastAxis = Axis<0>;
165    #[inline(always)]
166    fn concrete(&self) -> Self::Concrete {
167        []
168    }
169    #[inline(always)]
170    fn strides(&self) -> Self::Concrete {
171        []
172    }
173    #[inline(always)]
174    fn from_concrete(_: &Self::Concrete) -> Option<Self> {
175        Some(())
176    }
177}
178impl ConstShape for () {
179    const NUMEL: usize = 1;
180}
181
182shape!((D1 0), rank=1, all=Axis);
183shape!((D1 0, D2 1), rank=2, all=Axes2);
184shape!((D1 0, D2 1, D3 2), rank=3, all=Axes3);
185shape!((D1 0, D2 1, D3 2, D4 3), rank=4, all=Axes4);
186shape!((D1 0, D2 1, D3 2, D4 3, D5 4), rank=5, all=Axes5);
187shape!((D1 0, D2 1, D3 2, D4 3, D5 4, D6 5), rank=6, all=Axes6);
188
189/// Marker for shapes that have the same number of elements as `Dst`
190#[allow(dead_code)]
191pub trait AssertSameNumel<Dst: ConstShape>: ConstShape {
192    const TYPE_CHECK: ();
193    fn assert_same_numel() {
194        #[allow(clippy::let_unit_value)]
195        let _ = <Self as AssertSameNumel<Dst>>::TYPE_CHECK;
196    }
197}
198
199impl<Src: ConstShape, Dst: ConstShape> AssertSameNumel<Dst> for Src {
200    const TYPE_CHECK: () = assert!(Src::NUMEL == Dst::NUMEL);
201}