rubbl_core/
num.rs

1// Copyright 2017-2020 Peter Williams
2// Licensed under the MIT License.
3
4//! General helpers for numerics.
5
6use ndarray::{IntoDimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6};
7use thiserror::Error;
8
9/// An error type used when two arrays should have the same dimensionality,
10/// but do not.
11#[derive(Error, Debug)]
12#[error("Expected a {expected}-dimensional array, but got one with {actual} dimensions")]
13pub struct DimensionMismatchError {
14    /// The number of dimensions that the array was expected to have.
15    pub expected: usize,
16
17    /// The number of dimensions that the array actually had.
18    pub actual: usize,
19}
20
21/// Adapt a slice representing an array shape into an `ndarray::Dimension` type.
22///
23/// In `ndarray` array dimensionalities are statically typed, but it is often
24/// the case that we are reading arrays from data files where the array
25/// dimensionality may not match the expectations of the compiled code. This
26/// trait makes it possible to convert a runtime-flexible array shape into one
27/// of the compile-time types … if the two dimensionalities are in fact the
28/// same.
29pub trait DimFromShapeSlice<T>: Sized {
30    /// Try to create the implementing type from the specified array shape,
31    /// specified as a slice.
32    ///
33    /// Returns an Err with an ErrorKind of DimensionMismatch if the slice
34    /// size does not match the expected dimensionality.
35    fn from_shape_slice(shape: &[T]) -> Result<Self, DimensionMismatchError>;
36}
37
38macro_rules! impl_dim_from_shape_slice {
39    ($dimtype:ty; $ndim:expr; $($numbers:expr);*) => {
40        impl DimFromShapeSlice<u64> for $dimtype {
41            fn from_shape_slice(shape: &[u64]) -> Result<Self, DimensionMismatchError> {
42                if shape.len() == $ndim {
43                    Ok([$(shape[$numbers] as usize),*].into_dimension())
44                } else {
45                    Err(DimensionMismatchError { expected: $ndim, actual: shape.len() })
46                }
47            }
48        }
49
50        impl DimFromShapeSlice<usize> for $dimtype {
51            fn from_shape_slice(shape: &[usize]) -> Result<Self, DimensionMismatchError> {
52                if shape.len() == $ndim {
53                    Ok([$(shape[$numbers] as usize),*].into_dimension())
54                } else {
55                    Err(DimensionMismatchError { expected: $ndim, actual: shape.len() })
56                }
57            }
58        }
59    }
60}
61
62impl_dim_from_shape_slice! { Ix0; 0; }
63impl_dim_from_shape_slice! { Ix1; 1; 0 }
64impl_dim_from_shape_slice! { Ix2; 2; 0;1 }
65impl_dim_from_shape_slice! { Ix3; 3; 0;1;2 }
66impl_dim_from_shape_slice! { Ix4; 4; 0;1;2;3 }
67impl_dim_from_shape_slice! { Ix5; 5; 0;1;2;3;4 }
68impl_dim_from_shape_slice! { Ix6; 6; 0;1;2;3;4;5 }