conspire 0.7.0

The Rust interface to conspire.
Documentation
mod spn;
mod vti;

use crate::{
    geometry::grid::Grid,
    io::{Npy, NpyType},
};
use std::{
    fmt::Display,
    io::{Error as ErrorIO, ErrorKind},
    path::Path,
    str::FromStr,
};

pub enum Input<P>
where
    P: AsRef<Path>,
{
    Npy(P),
    Spn(P, Vec<usize>),
    Vti(P),
}

impl<P> AsRef<Path> for Input<P>
where
    P: AsRef<Path>,
{
    fn as_ref(&self) -> &Path {
        match self {
            Input::Npy(path) => path.as_ref(),
            Input::Spn(path, _) => path.as_ref(),
            Input::Vti(path) => path.as_ref(),
        }
    }
}

impl<const D: usize, T, P> TryFrom<Input<P>> for Grid<D, T>
where
    P: AsRef<Path>,
    T: NpyType + FromStr,
    <T as FromStr>::Err: Display,
{
    type Error = ErrorIO;
    fn try_from(input: Input<P>) -> Result<Self, Self::Error> {
        match input {
            Input::Spn(path, nel) => spn::read(path, nel),
            Input::Npy(path) => {
                let npy = Npy::<T>::read(path)?;
                let nel: [usize; D] = npy.shape.try_into().map_err(|shape: Vec<usize>| {
                    ErrorIO::new(
                        ErrorKind::InvalidData,
                        format!("npy has {} axes but Grid was asked for D={D}", shape.len()),
                    )
                })?;
                let data = if npy.fortran_order {
                    npy.data
                } else {
                    transpose(npy.data, nel)
                };
                Ok(Grid::new(data, nel))
            }
            Input::Vti(path) => vti::read(path),
        }
    }
}

fn transpose<const D: usize, T: Copy>(c_order: Vec<T>, nel: [usize; D]) -> Vec<T> {
    let mut axis_0_stride = [1usize; D];
    for axis in 1..D {
        axis_0_stride[axis] = axis_0_stride[axis - 1] * nel[axis - 1];
    }
    let mut c_stride = [1usize; D];
    for axis in (0..D.saturating_sub(1)).rev() {
        c_stride[axis] = c_stride[axis + 1] * nel[axis + 1];
    }
    (0..nel.iter().product())
        .map(|flat| {
            let c_flat = (0..D)
                .map(|axis| (flat / axis_0_stride[axis]) % nel[axis] * c_stride[axis])
                .sum::<usize>();
            c_order[c_flat]
        })
        .collect()
}