use ndarray::{ArrayBase, Data, Dimension, IntoDimension, Ix1, OwnedRepr};
use pyo3::Python;
use std::{mem, os::raw::c_int};
use crate::{
npyffi::{self, npy_intp},
Element, PyArray,
};
pub trait IntoPyArray {
type Item: Element;
type Dim: Dimension;
fn into_pyarray<'py>(self, _: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim>;
}
impl<T: Element> IntoPyArray for Box<[T]> {
type Item = T;
type Dim = Ix1;
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let len = self.len();
let strides = [mem::size_of::<T>() as npy_intp];
unsafe { PyArray::from_boxed_slice(py, [len], strides.as_ptr(), self) }
}
}
impl<T: Element> IntoPyArray for Vec<T> {
type Item = T;
type Dim = Ix1;
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
self.into_boxed_slice().into_pyarray(py)
}
}
impl<A, D> IntoPyArray for ArrayBase<OwnedRepr<A>, D>
where
A: Element,
D: Dimension,
{
type Item = A;
type Dim = D;
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let strides = self.npy_strides();
let dim = self.raw_dim();
let boxed = self.into_raw_vec().into_boxed_slice();
unsafe { PyArray::from_boxed_slice(py, dim, strides.as_ptr(), boxed) }
}
}
pub trait ToPyArray {
type Item: Element;
type Dim: Dimension;
fn to_pyarray<'py>(&self, _: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim>;
}
impl<T: Element> ToPyArray for [T] {
type Item = T;
type Dim = Ix1;
fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
PyArray::from_slice(py, self)
}
}
impl<S, D, A> ToPyArray for ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
A: Element,
{
type Item = A;
type Dim = D;
fn to_pyarray<'py>(&self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
let len = self.len();
if let Some(order) = self.order() {
let strides = self.npy_strides();
unsafe {
let array = PyArray::new_(py, self.raw_dim(), strides.as_ptr(), order.to_flag());
array.copy_ptr(self.as_ptr(), len);
array
}
} else {
let dim = self.raw_dim();
let strides = NpyStrides::from_dim(&dim, mem::size_of::<A>());
unsafe {
let array = PyArray::<A, _>::new_(py, dim, strides.as_ptr(), 0);
let data_ptr = array.data();
for (i, item) in self.iter().enumerate() {
data_ptr.add(i).write(item.clone());
}
array
}
}
}
}
enum Order {
Standard,
Fortran,
}
impl Order {
fn to_flag(&self) -> c_int {
match self {
Order::Standard => 0,
Order::Fortran => 1,
}
}
}
trait ArrayExt {
fn npy_strides(&self) -> NpyStrides;
fn order(&self) -> Option<Order>;
}
impl<A, S, D> ArrayExt for ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
{
fn npy_strides(&self) -> NpyStrides {
NpyStrides::new(
self.strides().iter().map(|&x| x as npyffi::npy_intp),
mem::size_of::<A>(),
)
}
fn order(&self) -> Option<Order> {
if self.is_standard_layout() {
Some(Order::Standard)
} else if self.ndim() > 1 && self.raw_view().reversed_axes().is_standard_layout() {
Some(Order::Fortran)
} else {
None
}
}
}
enum NpyStrides {
Short([npyffi::npy_intp; 8]),
Long(Vec<npyffi::npy_intp>),
}
impl NpyStrides {
fn as_ptr(&self) -> *const npy_intp {
match self {
NpyStrides::Short(inner) => inner.as_ptr(),
NpyStrides::Long(inner) => inner.as_ptr(),
}
}
fn from_dim<D: Dimension>(dim: &D, type_size: usize) -> Self {
Self::new(
dim.default_strides()
.slice()
.iter()
.map(|&x| x as npyffi::npy_intp),
type_size,
)
}
fn new(strides: impl ExactSizeIterator<Item = npyffi::npy_intp>, type_size: usize) -> Self {
let len = strides.len();
let type_size = type_size as npyffi::npy_intp;
if len <= 8 {
let mut res = [0; 8];
for (i, s) in strides.enumerate() {
res[i] = s * type_size;
}
NpyStrides::Short(res)
} else {
NpyStrides::Long(strides.map(|n| n as npyffi::npy_intp * type_size).collect())
}
}
}
pub trait ToNpyDims: Dimension {
fn ndim_cint(&self) -> c_int {
self.ndim() as c_int
}
fn as_dims_ptr(&self) -> *mut npyffi::npy_intp {
self.slice().as_ptr() as *mut npyffi::npy_intp
}
fn to_npy_dims(&self) -> npyffi::PyArray_Dims {
npyffi::PyArray_Dims {
ptr: self.as_dims_ptr(),
len: self.ndim_cint(),
}
}
fn __private__(&self) -> PrivateMarker;
}
impl<D: Dimension> ToNpyDims for D {
fn __private__(&self) -> PrivateMarker {
PrivateMarker
}
}
pub trait NpyIndex: IntoDimension {
fn get_checked<T>(self, dims: &[usize], strides: &[isize]) -> Option<isize>;
fn get_unchecked<T>(self, strides: &[isize]) -> isize;
fn __private__(self) -> PrivateMarker;
}
impl<D: IntoDimension> NpyIndex for D {
fn get_checked<T>(self, dims: &[usize], strides: &[isize]) -> Option<isize> {
let indices_ = self.into_dimension();
let indices = indices_.slice();
if indices.len() != dims.len() {
return None;
}
if indices.iter().zip(dims).any(|(i, d)| i >= d) {
return None;
}
Some(get_unchecked_impl(
indices,
strides,
mem::size_of::<T>() as isize,
))
}
fn get_unchecked<T>(self, strides: &[isize]) -> isize {
let indices_ = self.into_dimension();
let indices = indices_.slice();
get_unchecked_impl(indices, strides, mem::size_of::<T>() as isize)
}
fn __private__(self) -> PrivateMarker {
PrivateMarker
}
}
fn get_unchecked_impl(indices: &[usize], strides: &[isize], size: isize) -> isize {
indices
.iter()
.zip(strides)
.map(|(&i, stride)| stride * i as isize / size)
.sum()
}
#[doc(hidden)]
pub struct PrivateMarker;