use super::axis::Axis;
pub trait Axes: Axis {}
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AxesTuple<T> {
axes: T,
shape: Vec<usize>,
}
impl<T> AxesTuple<T> {
pub fn as_tuple(&self) -> &T {
&self.axes
}
}
macro_rules! count_idents {
($($idents:ident),* $(,)*) => {
{
#[allow(dead_code, non_camel_case_types)]
enum Idents { $($idents,)* __CountIdentsLast }
const COUNT: usize = Idents::__CountIdentsLast as usize;
COUNT
}
};
}
macro_rules! impl_axes {
() => {
};
( $type_parameter:ident: $index:tt, ) => {
impl<X: Axis> Axes for AxesTuple<(X,)> {
}
impl<X:Axis> From<(X,)> for AxesTuple<(X,)> {
fn from(item: (X,)) -> Self {
let shape = vec![item.0.num_bins()];
Self { axes: item, shape }
}
}
impl<X: Axis> Axis for AxesTuple<(X,)> {
type Coordinate = X::Coordinate;
type BinInterval = X::BinInterval;
#[inline]
fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
self.axes.0.index(coordinate)
}
fn num_bins(&self) -> usize {
self.axes.0.num_bins()
}
fn bin(&self, index: usize) -> Option<Self::BinInterval> {
self.axes.0.bin(index)
}
}
impl_axes!();
};
( $($nth_type_parameter:ident: $nth_index:tt, )+ ) => {
impl<$($nth_type_parameter: Axis),*> Axes for AxesTuple<($($nth_type_parameter),*)> {
}
impl<$($nth_type_parameter: Axis),*> From<($($nth_type_parameter),*)> for AxesTuple<($($nth_type_parameter),*)> {
fn from(item: ($($nth_type_parameter),*)) -> Self {
let shape = [$(item.$nth_index.num_bins()),*].iter().scan(1, |acc, nbin| {*acc *= *nbin; Some(*acc)}).collect();
Self { axes: item, shape }
}
}
impl<$($nth_type_parameter: Axis),*> Axis for AxesTuple<($($nth_type_parameter),*)> {
type Coordinate = ($($nth_type_parameter::Coordinate),*);
type BinInterval = ($($nth_type_parameter::BinInterval),*);
#[inline]
fn index(&self, coordinate: &Self::Coordinate) -> Option<usize> {
let indices = [$(self.axes.$nth_index.index(&coordinate.$nth_index)?),*];
let index = self.shape.iter()
.rev()
.skip(1)
.zip(indices.iter().rev())
.fold(indices[0], |acc, (nbin, idx)| acc + nbin*idx);
Some(index)
}
fn num_bins(&self) -> usize {
$(self.axes.$nth_index.num_bins()*)* 1
}
fn num_dim(&self) -> usize {
count_idents!($($nth_type_parameter,)*)
}
fn bin(&self, index: usize) -> Option<Self::BinInterval> {
let num_bins = [$(self.axes.$nth_index.num_bins()),*];
let product = num_bins.iter().scan(1, |acc, it| Some(*acc * *it));
let mut index = index;
let index: Vec<_> = product.map(|nb| {
let v = index % nb;
index /= nb;
v
} ).collect();
Some(
(
$(self.axes.$nth_index.bin(index[$nth_index])?),*
)
)
}
}
impl_axes!(@REMOVELAST $([$nth_index AND $nth_type_parameter],)*);
};
(@REMOVELAST [$index:tt AND $type_parameter:ident], $( [$nth_index:tt AND $nth_type_parameter:ident], )+ ) => {
impl_axes!(@REMOVELAST [$index AND $type_parameter], @SEPARATOR $([$nth_index AND $nth_type_parameter],)*);
};
(@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], )+ ) => {
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],)*);
};
(@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], )+ ) => {
impl_axes!(@REMOVELAST $([$first_index AND $first_type_parameter],)* [$index1 AND $type_parameter1], [$index2 AND $type_parameter2], @SEPARATOR $([$nth_index AND $nth_type_parameter],)*);
};
(@REMOVELAST $( [$first_index:tt AND $first_type_parameter:ident], )+ @SEPARATOR [$index:tt AND $type_parameter:ident], $( [$nth_index:tt AND $nth_type_parameter:ident], )+ ) => {
impl_axes!(@REMOVELAST $([$first_index AND $first_type_parameter],)* [$index AND $type_parameter], @SEPARATOR $([$nth_index AND $nth_type_parameter],)*);
};
(@REMOVELAST $( [$first_index:tt AND $first_type_parameter:ident], )+ @SEPARATOR [$index:tt AND $type_parameter:ident], ) => {
impl_axes!($($first_type_parameter: $first_index,)*);
};
}
impl_axes! {
X: 0,
Y: 1,
Z: 2,
T: 3,
D4: 4,
D5: 5,
D6: 6,
D7: 7,
D8: 8,
D9: 9,
D10: 10,
D11: 11,
D12: 12,
D13: 13,
D14: 14,
D15: 15,
D16: 16,
D17: 17,
D18: 18,
D19: 19,
D20: 20,
}