ndhistogram/
axes.rs

1use super::axis::Axis;
2
3/// Axes provided an interface for a set of ND dimensional set of histograms.
4pub trait Axes: Axis {}
5
6/// Container for a set of [Axis] that implements [Axes].
7
8#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct AxesTuple<T> {
11    axes: T,
12    shape: Vec<usize>,
13}
14
15impl<T> AxesTuple<T> {
16    /// Get the set of container Axis as a tuple
17    pub fn as_tuple(&self) -> &T {
18        &self.axes
19    }
20}
21
22// Count idents in macro from: <https://danielkeep.github.io/tlborm/book/blk-counting.html>
23macro_rules! count_idents {
24    ($($idents:ident),* $(,)*) => {
25        {
26            #[allow(dead_code, non_camel_case_types)]
27            enum Idents { $($idents,)* __CountIdentsLast }
28            const COUNT: usize = Idents::__CountIdentsLast as usize;
29            COUNT
30        }
31    };
32}
33
34macro_rules! impl_axes {
35    () => {
36
37    };
38    //( ($index:tt => $type_parameter:ident), ) => {
39        ( $type_parameter:ident: $index:tt, ) => {
40
41        impl<X: Axis> Axes for AxesTuple<(X,)> {
42        }
43
44        impl<X:Axis> From<(X,)> for AxesTuple<(X,)> {
45            fn from(item: (X,)) -> Self {
46                let shape = vec![item.0.num_bins()];
47                Self { axes: item, shape }
48            }
49        }
50
51        impl<X: Axis> Axis for AxesTuple<(X,)> {
52            type Coordinate = X::Coordinate;
53            type BinInterval = X::BinInterval;
54
55            #[inline]
56            fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
57                self.axes.0.index(coordinate)
58            }
59
60            fn num_bins(&self) -> usize {
61                self.axes.0.num_bins()
62            }
63
64            fn bin(&self, index: usize) -> Option<Self::BinInterval> {
65                self.axes.0.bin(index)
66            }
67
68        }
69
70        impl_axes!();
71    };
72    //( $( ($nth_index:tt => $nth_type_parameter:ident), )+ ) => {
73        ( $($nth_type_parameter:ident: $nth_index:tt, )+ ) => {
74        impl<$($nth_type_parameter: Axis),*> Axes for AxesTuple<($($nth_type_parameter),*)> {
75        }
76
77        impl<$($nth_type_parameter: Axis),*> From<($($nth_type_parameter),*)> for AxesTuple<($($nth_type_parameter),*)> {
78            fn from(item: ($($nth_type_parameter),*)) -> Self {
79                let shape = [$(item.$nth_index.num_bins()),*].iter().scan(1, |acc, nbin| {*acc *= *nbin; Some(*acc)}).collect();
80                Self { axes: item, shape }
81            }
82        }
83
84        impl<$($nth_type_parameter: Axis),*> Axis for AxesTuple<($($nth_type_parameter),*)> {
85            type Coordinate = ($($nth_type_parameter::Coordinate),*);
86            type BinInterval = ($($nth_type_parameter::BinInterval),*);
87
88            #[inline]
89            fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
90                let indices = [$(self.axes.$nth_index.index(&coordinate.$nth_index)?),*];
91
92                let index = self.shape.iter()
93                    .rev()
94                    .skip(1)
95                    .zip(indices.iter().rev())
96                    .fold(indices[0], |acc, (nbin, idx)| acc + nbin*idx);
97                Some(index)
98            }
99
100            fn num_bins(&self) -> usize {
101                //let arr = [self.$index.num_bins(), $(self.$nth_index.num_bins()),*];
102                $(self.axes.$nth_index.num_bins()*)* 1
103            }
104
105            fn num_dim(&self) -> usize {
106                count_idents!($($nth_type_parameter,)*)
107            }
108
109            fn bin(&self, index: usize) -> Option<Self::BinInterval> {
110                let num_bins = [$(self.axes.$nth_index.num_bins()),*];
111                let product = num_bins.iter().scan(1, |acc, it| Some(*acc * *it));
112                let mut index = index;
113                let index: Vec<_> = product.map(|nb| {
114                    let v = index % nb;
115                    index /= nb;
116                    v
117                } ).collect();
118                Some(
119                    (
120                        $(self.axes.$nth_index.bin(index[$nth_index])?),*
121                )
122            )
123            }
124        }
125
126        impl_axes!(@REMOVELAST $([$nth_index AND $nth_type_parameter],)*);
127    };
128
129    // TODO: yuk! so ugly. clean this up by moving it out into another macro
130    // Remove the last element of the impl_axes! { x: 0 .... n-1: N-1, n: N}
131    // and call impl_axes! { x: 0 .... n-1: N-1}
132    (@REMOVELAST [$index:tt AND $type_parameter:ident], $( [$nth_index:tt AND $nth_type_parameter:ident], )+ ) => {
133        impl_axes!(@REMOVELAST [$index AND $type_parameter], @SEPARATOR $([$nth_index AND $nth_type_parameter],)*);
134    };
135    // optimisation to reduce levels of recursion required (move 4 at once)
136    (@REMOVELAST $( [$first_index:tt AND $first_type_parameter:ident], )+ @SEPARATOR [$index1:tt AND $type_parameter1:ident], [$index2:tt AND $type_parameter2:ident], [$index3:tt AND $type_parameter3:ident], [$index4:tt AND $type_parameter4:ident], $( [$nth_index:tt AND $nth_type_parameter:ident], )+ ) => {
137        impl_axes!(@REMOVELAST $([$first_index AND $first_type_parameter],)* [$index1 AND $type_parameter1], [$index2 AND $type_parameter2], [$index3 AND $type_parameter3], [$index4 AND $type_parameter4], @SEPARATOR $([$nth_index AND $nth_type_parameter],)*);
138    };
139    // optimisation to reduce levels of recursion required (move 2 at once)
140    (@REMOVELAST $( [$first_index:tt AND $first_type_parameter:ident], )+ @SEPARATOR [$index1:tt AND $type_parameter1:ident], [$index2:tt AND $type_parameter2:ident], $( [$nth_index:tt AND $nth_type_parameter:ident], )+ ) => {
141        impl_axes!(@REMOVELAST $([$first_index AND $first_type_parameter],)* [$index1 AND $type_parameter1], [$index2 AND $type_parameter2], @SEPARATOR $([$nth_index AND $nth_type_parameter],)*);
142    };
143    (@REMOVELAST $( [$first_index:tt AND $first_type_parameter:ident], )+ @SEPARATOR [$index:tt AND $type_parameter:ident], $( [$nth_index:tt AND $nth_type_parameter:ident], )+ ) => {
144        impl_axes!(@REMOVELAST $([$first_index AND $first_type_parameter],)* [$index AND $type_parameter], @SEPARATOR $([$nth_index AND $nth_type_parameter],)*);
145    };
146    // all pairs have been moved to the left
147    (@REMOVELAST $( [$first_index:tt AND $first_type_parameter:ident], )+ @SEPARATOR [$index:tt AND $type_parameter:ident], ) => {
148        //impl_axes!($(($first_index => $first_type_parameter),)*);
149        impl_axes!($($first_type_parameter: $first_index,)*);
150    };
151}
152
153impl_axes! {
154    X: 0,
155    Y: 1,
156    Z: 2,
157    T: 3,
158    D4: 4,
159    D5: 5,
160    D6: 6,
161    D7: 7,
162    D8: 8,
163    D9: 9,
164    D10: 10,
165    D11: 11,
166    D12: 12,
167    D13: 13,
168    D14: 14,
169    D15: 15,
170    D16: 16,
171    D17: 17,
172    D18: 18,
173    D19: 19,
174    D20: 20,
175}