use std::mem::MaybeUninit;
use crate::array::owned::Array;
use crate::array::view::ArrayView;
use crate::dimension::{Dimension, Ix1, Ix2, IxDyn};
use crate::dtype::Element;
use crate::error::{FerrayError, FerrayResult};
pub fn array<T: Element, D: Dimension>(dim: D, data: Vec<T>) -> FerrayResult<Array<T, D>> {
Array::from_vec(dim, data)
}
pub fn asarray<T: Element, D: Dimension>(dim: D, data: Vec<T>) -> FerrayResult<Array<T, D>> {
Array::from_vec(dim, data)
}
pub fn frombuffer<T: Element, D: Dimension>(dim: D, buf: &[u8]) -> FerrayResult<Array<T, D>> {
let elem_size = std::mem::size_of::<T>();
if elem_size == 0 {
return Err(FerrayError::invalid_value("zero-sized type"));
}
if buf.len() % elem_size != 0 {
return Err(FerrayError::invalid_value(format!(
"buffer length {} is not a multiple of element size {}",
buf.len(),
elem_size,
)));
}
let n_elems = buf.len() / elem_size;
let expected = dim.size();
if n_elems != expected {
return Err(FerrayError::shape_mismatch(format!(
"buffer contains {} elements but shape {:?} requires {}",
n_elems,
dim.as_slice(),
expected,
)));
}
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<bool>() {
for &byte in buf {
if byte > 1 {
return Err(FerrayError::invalid_value(format!(
"invalid byte {byte:#04x} for bool (must be 0x00 or 0x01)"
)));
}
}
}
let mut data = Vec::with_capacity(n_elems);
for i in 0..n_elems {
let start = i * elem_size;
let end = start + elem_size;
let slice = &buf[start..end];
let val = unsafe {
let mut val = MaybeUninit::<T>::uninit();
std::ptr::copy_nonoverlapping(slice.as_ptr(), val.as_mut_ptr().cast::<u8>(), elem_size);
val.assume_init()
};
data.push(val);
}
Array::from_vec(dim, data)
}
pub fn frombuffer_view<T: Element, D: Dimension>(
dim: D,
buf: &[u8],
) -> FerrayResult<ArrayView<'_, T, D>> {
let elem_size = std::mem::size_of::<T>();
if elem_size == 0 {
return Err(FerrayError::invalid_value("zero-sized type"));
}
if buf.len() % elem_size != 0 {
return Err(FerrayError::invalid_value(format!(
"buffer length {} is not a multiple of element size {}",
buf.len(),
elem_size,
)));
}
let n_elems = buf.len() / elem_size;
let expected = dim.size();
if n_elems != expected {
return Err(FerrayError::shape_mismatch(format!(
"buffer contains {} elements but shape {:?} requires {}",
n_elems,
dim.as_slice(),
expected,
)));
}
let align = std::mem::align_of::<T>();
let addr = buf.as_ptr() as usize;
if addr % align != 0 {
return Err(FerrayError::invalid_value(format!(
"buffer address 0x{addr:x} is not aligned to {align} bytes required by the element type; \
use `frombuffer` for misaligned input"
)));
}
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<bool>() {
for &byte in buf {
if byte > 1 {
return Err(FerrayError::invalid_value(format!(
"invalid byte {byte:#04x} for bool (must be 0x00 or 0x01)"
)));
}
}
}
let ptr = buf.as_ptr().cast::<T>();
let nd_dim = dim.to_ndarray_dim();
let nd_view = unsafe { ndarray::ArrayView::from_shape_ptr(nd_dim, ptr) };
Ok(ArrayView::from_ndarray(nd_view))
}
pub fn fromiter<T: Element>(iter: impl IntoIterator<Item = T>) -> FerrayResult<Array<T, Ix1>> {
Array::from_iter_1d(iter)
}
pub fn zeros<T: Element, D: Dimension>(dim: D) -> FerrayResult<Array<T, D>> {
Array::zeros(dim)
}
pub fn ones<T: Element, D: Dimension>(dim: D) -> FerrayResult<Array<T, D>> {
Array::ones(dim)
}
pub fn full<T: Element, D: Dimension>(dim: D, fill_value: T) -> FerrayResult<Array<T, D>> {
Array::from_elem(dim, fill_value)
}
pub fn zeros_like<T: Element, D: Dimension>(other: &Array<T, D>) -> FerrayResult<Array<T, D>> {
Array::zeros(other.dim().clone())
}
pub fn ones_like<T: Element, D: Dimension>(other: &Array<T, D>) -> FerrayResult<Array<T, D>> {
Array::ones(other.dim().clone())
}
pub fn full_like<T: Element, D: Dimension>(
other: &Array<T, D>,
fill_value: T,
) -> FerrayResult<Array<T, D>> {
Array::from_elem(other.dim().clone(), fill_value)
}
pub struct UninitArray<T: Element, D: Dimension> {
data: Vec<MaybeUninit<T>>,
dim: D,
}
impl<T: Element, D: Dimension> UninitArray<T, D> {
#[inline]
pub fn shape(&self) -> &[usize] {
self.dim.as_slice()
}
#[inline]
pub fn size(&self) -> usize {
self.data.len()
}
#[inline]
pub fn ndim(&self) -> usize {
self.dim.ndim()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut MaybeUninit<T> {
self.data.as_mut_ptr()
}
pub fn write_at(&mut self, flat_index: usize, value: T) -> FerrayResult<()> {
let size = self.size();
if flat_index >= size {
return Err(FerrayError::IndexOutOfBounds {
index: flat_index as isize,
axis: 0,
size,
});
}
self.data[flat_index] = MaybeUninit::new(value);
Ok(())
}
pub unsafe fn assume_init(self) -> Array<T, D> {
let nd_dim = self.dim.to_ndarray_dim();
let len = self.data.len();
let mut raw_vec = std::mem::ManuallyDrop::new(self.data);
let data: Vec<T> = unsafe {
Vec::from_raw_parts(raw_vec.as_mut_ptr().cast::<T>(), len, raw_vec.capacity())
};
let inner = ndarray::Array::from_shape_vec(nd_dim, data)
.expect("UninitArray assume_init: shape/data mismatch (this is a bug)");
Array::from_ndarray(inner)
}
}
pub fn empty<T: Element, D: Dimension>(dim: D) -> UninitArray<T, D> {
let size = dim.size();
let mut data = Vec::with_capacity(size);
unsafe {
data.set_len(size);
}
UninitArray { data, dim }
}
pub fn empty_like<T: Element, D: Dimension>(other: &Array<T, D>) -> UninitArray<T, D> {
empty(other.dim().clone())
}
pub trait ArangeNum: Element + PartialOrd {
fn from_f64(v: f64) -> Self;
fn to_f64(self) -> f64;
}
macro_rules! impl_arange_int {
($($ty:ty),*) => {
$(
impl ArangeNum for $ty {
#[inline]
fn from_f64(v: f64) -> Self { v as Self }
#[inline]
fn to_f64(self) -> f64 { self as f64 }
}
)*
};
}
macro_rules! impl_arange_float {
($($ty:ty),*) => {
$(
impl ArangeNum for $ty {
#[inline]
fn from_f64(v: f64) -> Self { v as Self }
#[inline]
fn to_f64(self) -> f64 { self as f64 }
}
)*
};
}
impl_arange_int!(u8, u16, u32, u64, i8, i16, i32, i64);
impl_arange_float!(f32, f64);
pub fn arange<T: ArangeNum>(start: T, stop: T, step: T) -> FerrayResult<Array<T, Ix1>> {
let step_f = step.to_f64();
if step_f == 0.0 {
return Err(FerrayError::invalid_value("step cannot be zero"));
}
let start_f = start.to_f64();
let stop_f = stop.to_f64();
let n = ((stop_f - start_f) / step_f).ceil();
let n = if n < 0.0 { 0 } else { n as usize };
let mut data = Vec::with_capacity(n);
for i in 0..n {
data.push(T::from_f64((i as f64).mul_add(step_f, start_f)));
}
let dim = Ix1::new([data.len()]);
Array::from_vec(dim, data)
}
pub trait LinspaceNum: Element + PartialOrd {
fn from_f64(v: f64) -> Self;
fn to_f64(self) -> f64;
}
impl LinspaceNum for f32 {
#[inline]
fn from_f64(v: f64) -> Self {
v as Self
}
#[inline]
fn to_f64(self) -> f64 {
self as f64
}
}
impl LinspaceNum for f64 {
#[inline]
fn from_f64(v: f64) -> Self {
v
}
#[inline]
fn to_f64(self) -> f64 {
self
}
}
pub fn linspace<T: LinspaceNum>(
start: T,
stop: T,
num: usize,
endpoint: bool,
) -> FerrayResult<Array<T, Ix1>> {
if num == 0 {
return Array::from_vec(Ix1::new([0]), vec![]);
}
if num == 1 {
return Array::from_vec(Ix1::new([1]), vec![start]);
}
let start_f = start.to_f64();
let stop_f = stop.to_f64();
let divisor = if endpoint {
(num - 1) as f64
} else {
num as f64
};
let step = (stop_f - start_f) / divisor;
let mut data = Vec::with_capacity(num);
for i in 0..num {
data.push(T::from_f64((i as f64).mul_add(step, start_f)));
}
Array::from_vec(Ix1::new([num]), data)
}
pub fn logspace<T: LinspaceNum>(
start: T,
stop: T,
num: usize,
endpoint: bool,
base: f64,
) -> FerrayResult<Array<T, Ix1>> {
let lin = linspace(start, stop, num, endpoint)?;
let data: Vec<T> = lin
.iter()
.map(|v| T::from_f64(base.powf(v.clone().to_f64())))
.collect();
Array::from_vec(Ix1::new([num]), data)
}
pub fn geomspace<T: LinspaceNum>(
start: T,
stop: T,
num: usize,
endpoint: bool,
) -> FerrayResult<Array<T, Ix1>> {
let start_f = start.clone().to_f64();
let stop_f = stop.to_f64();
if start_f == 0.0 || stop_f == 0.0 {
return Err(FerrayError::invalid_value(
"geomspace: start and stop must be non-zero",
));
}
if (start_f < 0.0) != (stop_f < 0.0) {
return Err(FerrayError::invalid_value(
"geomspace: start and stop must have the same sign",
));
}
if num == 0 {
return Array::from_vec(Ix1::new([0]), vec![]);
}
if num == 1 {
return Array::from_vec(Ix1::new([1]), vec![start]);
}
let log_start = start_f.abs().ln();
let log_stop = stop_f.abs().ln();
let sign = if start_f < 0.0 { -1.0 } else { 1.0 };
let divisor = if endpoint {
(num - 1) as f64
} else {
num as f64
};
let step = (log_stop - log_start) / divisor;
let mut data = Vec::with_capacity(num);
for i in 0..num {
let log_val = (i as f64).mul_add(step, log_start);
data.push(T::from_f64(sign * log_val.exp()));
}
Array::from_vec(Ix1::new([num]), data)
}
pub fn meshgrid(
arrays: &[Array<f64, Ix1>],
indexing: &str,
) -> FerrayResult<Vec<Array<f64, IxDyn>>> {
if indexing != "xy" && indexing != "ij" {
return Err(FerrayError::invalid_value(
"meshgrid: indexing must be 'xy' or 'ij'",
));
}
let ndim = arrays.len();
if ndim == 0 {
return Ok(vec![]);
}
let mut shapes: Vec<usize> = arrays.iter().map(|a| a.shape()[0]).collect();
if indexing == "xy" && ndim >= 2 {
shapes.swap(0, 1);
}
let total: usize = shapes.iter().product();
let mut results = Vec::with_capacity(ndim);
for (k, arr) in arrays.iter().enumerate() {
let src_data: Vec<f64> = arr.iter().copied().collect();
let mut data = Vec::with_capacity(total);
let effective_k = if indexing == "xy" && ndim >= 2 {
match k {
0 => 1,
1 => 0,
other => other,
}
} else {
k
};
for flat in 0..total {
let mut rem = flat;
let mut idx_k = 0;
for (d, &s) in shapes.iter().enumerate().rev() {
if d == effective_k {
idx_k = rem % s;
}
rem /= s;
}
data.push(src_data[idx_k]);
}
let dim = IxDyn::new(&shapes);
results.push(Array::from_vec(dim, data)?);
}
Ok(results)
}
pub fn mgrid(ranges: &[(f64, f64, f64)]) -> FerrayResult<Vec<Array<f64, IxDyn>>> {
let mut arrs: Vec<Array<f64, Ix1>> = Vec::with_capacity(ranges.len());
for &(start, stop, step) in ranges {
arrs.push(arange(start, stop, step)?);
}
meshgrid(&arrs, "ij")
}
pub fn ogrid(ranges: &[(f64, f64, f64)]) -> FerrayResult<Vec<Array<f64, IxDyn>>> {
let ndim = ranges.len();
let mut results = Vec::with_capacity(ndim);
for (i, &(start, stop, step)) in ranges.iter().enumerate() {
let arr1d = arange(start, stop, step)?;
let n = arr1d.shape()[0];
let data: Vec<f64> = arr1d.iter().copied().collect();
let mut shape = vec![1usize; ndim];
shape[i] = n;
let dim = IxDyn::new(&shape);
results.push(Array::from_vec(dim, data)?);
}
Ok(results)
}
pub fn identity<T: Element>(n: usize) -> FerrayResult<Array<T, Ix2>> {
eye(n, n, 0)
}
pub fn eye<T: Element>(n: usize, m: usize, k: isize) -> FerrayResult<Array<T, Ix2>> {
let mut data = vec![T::zero(); n * m];
for i in 0..n {
let j = i as isize + k;
if j >= 0 && (j as usize) < m {
data[i * m + j as usize] = T::one();
}
}
Array::from_vec(Ix2::new([n, m]), data)
}
pub fn diag<T: Element>(a: &Array<T, IxDyn>, k: isize) -> FerrayResult<Array<T, IxDyn>> {
let shape = a.shape();
match shape.len() {
1 => {
let n = shape[0];
let size = n + k.unsigned_abs();
let mut data = vec![T::zero(); size * size];
let src: Vec<T> = a.iter().cloned().collect();
for (i, val) in src.into_iter().enumerate() {
let row = if k >= 0 { i } else { i + k.unsigned_abs() };
let col = if k >= 0 { i + k as usize } else { i };
data[row * size + col] = val;
}
Array::from_vec(IxDyn::new(&[size, size]), data)
}
2 => {
let (n, m) = (shape[0], shape[1]);
let src: Vec<T> = a.iter().cloned().collect();
let mut diag_vals = Vec::new();
for i in 0..n {
let j = i as isize + k;
if j >= 0 && (j as usize) < m {
diag_vals.push(src[i * m + j as usize].clone());
}
}
let len = diag_vals.len();
Array::from_vec(IxDyn::new(&[len]), diag_vals)
}
_ => Err(FerrayError::invalid_value("diag: input must be 1-D or 2-D")),
}
}
pub fn diagflat<T: Element>(a: &Array<T, IxDyn>, k: isize) -> FerrayResult<Array<T, IxDyn>> {
let flat: Vec<T> = a.iter().cloned().collect();
let n = flat.len();
let arr1d = Array::from_vec(IxDyn::new(&[n]), flat)?;
diag(&arr1d, k)
}
pub fn tri<T: Element>(n: usize, m: usize, k: isize) -> FerrayResult<Array<T, Ix2>> {
let mut data = vec![T::zero(); n * m];
for i in 0..n {
for j in 0..m {
if (i as isize) >= (j as isize) - k {
data[i * m + j] = T::one();
}
}
}
Array::from_vec(Ix2::new([n, m]), data)
}
pub fn tril<T: Element>(a: &Array<T, IxDyn>, k: isize) -> FerrayResult<Array<T, IxDyn>> {
let shape = a.shape();
if shape.len() != 2 {
return Err(FerrayError::invalid_value("tril: input must be 2-D"));
}
let (n, m) = (shape[0], shape[1]);
let src: Vec<T> = a.iter().cloned().collect();
let mut data = vec![T::zero(); n * m];
for i in 0..n {
for j in 0..m {
if (i as isize) >= (j as isize) - k {
data[i * m + j] = src[i * m + j].clone();
}
}
}
Array::from_vec(IxDyn::new(&[n, m]), data)
}
pub fn triu<T: Element>(a: &Array<T, IxDyn>, k: isize) -> FerrayResult<Array<T, IxDyn>> {
let shape = a.shape();
if shape.len() != 2 {
return Err(FerrayError::invalid_value("triu: input must be 2-D"));
}
let (n, m) = (shape[0], shape[1]);
let src: Vec<T> = a.iter().cloned().collect();
let mut data = vec![T::zero(); n * m];
for i in 0..n {
for j in 0..m {
if (i as isize) <= (j as isize) - k {
data[i * m + j] = src[i * m + j].clone();
}
}
}
Array::from_vec(IxDyn::new(&[n, m]), data)
}
pub fn vander<T>(
x: &Array<T, Ix1>,
n: Option<usize>,
increasing: bool,
) -> FerrayResult<Array<T, IxDyn>>
where
T: Element + std::ops::Mul<Output = T> + Copy,
{
let m = x.shape()[0];
if m == 0 {
return Err(FerrayError::invalid_value(
"vander: input array must not be empty",
));
}
let cols = n.unwrap_or(m);
let xs: Vec<T> = x.iter().copied().collect();
let mut data = vec![<T as Element>::one(); m * cols];
for (i, &xi) in xs.iter().enumerate() {
let mut acc = <T as Element>::one();
let mut powers = Vec::with_capacity(cols);
for _ in 0..cols {
powers.push(acc);
acc = acc * xi;
}
if increasing {
for (j, p) in powers.iter().enumerate() {
data[i * cols + j] = *p;
}
} else {
for (j, p) in powers.iter().enumerate() {
data[i * cols + (cols - 1 - j)] = *p;
}
}
}
Array::from_vec(IxDyn::new(&[m, cols]), data)
}
pub fn copy<T: Element, D: Dimension>(a: &Array<T, D>) -> Array<T, D> {
a.clone()
}
pub fn ascontiguousarray<T: Element, D: Dimension>(a: &Array<T, D>) -> Array<T, D> {
a.clone()
}
pub fn asfortranarray<T: Element, D: Dimension>(a: &Array<T, D>) -> Array<T, D> {
a.clone()
}
pub fn asanyarray<T: Element, D: Dimension>(a: &Array<T, D>) -> Array<T, D> {
a.clone()
}
pub fn asarray_chkfinite<T, D: Dimension>(a: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + num_traits::Float,
{
for v in a.iter() {
if !v.is_finite() {
return Err(FerrayError::invalid_value(
"asarray_chkfinite: array contains non-finite values (NaN or Inf)",
));
}
}
Ok(a.clone())
}
pub fn require<T: Element, D: Dimension>(a: &Array<T, D>, _requirements: &str) -> Array<T, D> {
a.clone()
}
pub fn fromfunction<T, D, F>(shape: D, mut f: F) -> FerrayResult<Array<T, D>>
where
T: Element,
D: Dimension,
F: FnMut(&[usize]) -> T,
{
let dims: Vec<usize> = shape.as_slice().to_vec();
let total: usize = dims.iter().product();
let mut data = Vec::with_capacity(total);
let mut idx = vec![0usize; dims.len()];
for _ in 0..total {
data.push(f(&idx));
for axis in (0..dims.len()).rev() {
idx[axis] += 1;
if idx[axis] < dims[axis] {
break;
}
idx[axis] = 0;
}
}
Array::from_vec(shape, data)
}
pub fn fromstring<T>(s: &str, sep: &str) -> FerrayResult<Array<T, Ix1>>
where
T: Element + std::str::FromStr,
<T as std::str::FromStr>::Err: std::fmt::Display,
{
let parts: Vec<&str> = if sep.is_empty() {
s.split_whitespace().collect()
} else {
s.split(sep)
.map(str::trim)
.filter(|t| !t.is_empty())
.collect()
};
let mut data = Vec::with_capacity(parts.len());
for tok in parts {
let v = tok.parse::<T>().map_err(|e| {
FerrayError::invalid_value(format!("fromstring: failed to parse {tok:?}: {e}"))
})?;
data.push(v);
}
let n = data.len();
Array::from_vec(Ix1::new([n]), data)
}
pub fn fromfile<T, P: AsRef<std::path::Path>>(path: P, sep: &str) -> FerrayResult<Array<T, Ix1>>
where
T: Element + std::str::FromStr,
<T as std::str::FromStr>::Err: std::fmt::Display,
{
let s = std::fs::read_to_string(path)
.map_err(|e| FerrayError::invalid_value(format!("fromfile: {e}")))?;
fromstring(&s, sep)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dimension::{Ix1, Ix2, IxDyn};
#[test]
fn test_array_creation() {
let a = array(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap();
assert_eq!(a.shape(), &[2, 3]);
assert_eq!(a.size(), 6);
}
#[test]
fn test_asarray() {
let a = asarray(Ix1::new([3]), vec![1, 2, 3]).unwrap();
assert_eq!(a.as_slice().unwrap(), &[1, 2, 3]);
}
#[test]
fn test_frombuffer() {
let bytes: Vec<u8> = vec![1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0];
let a = frombuffer::<i32, Ix1>(Ix1::new([3]), &bytes).unwrap();
assert_eq!(a.as_slice().unwrap(), &[1, 2, 3]);
}
#[test]
fn test_frombuffer_bad_length() {
let bytes: Vec<u8> = vec![1, 0, 0];
assert!(frombuffer::<i32, Ix1>(Ix1::new([1]), &bytes).is_err());
}
#[test]
fn test_frombuffer_bool() {
let bytes: Vec<u8> = vec![0, 1, 0, 1, 1];
let a = frombuffer::<bool, Ix1>(Ix1::new([5]), &bytes).unwrap();
assert_eq!(a.as_slice().unwrap(), &[false, true, false, true, true]);
}
#[test]
fn test_frombuffer_bool_wrong_length() {
let bytes: Vec<u8> = vec![0, 1];
assert!(frombuffer::<bool, Ix1>(Ix1::new([3]), &bytes).is_err());
}
fn aligned_bytes<T: Copy>(src: &[T]) -> Vec<u8> {
let n = std::mem::size_of_val(src);
let mut out = vec![0u8; n];
unsafe {
std::ptr::copy_nonoverlapping(src.as_ptr().cast::<u8>(), out.as_mut_ptr(), n);
}
out
}
#[test]
fn test_frombuffer_view_i32_is_zero_copy() {
let source: Vec<i32> = vec![10, 20, 30];
let bytes = aligned_bytes(&source);
let view = frombuffer_view::<i32, Ix1>(Ix1::new([3]), &bytes).unwrap();
assert_eq!(view.shape(), &[3]);
let values: Vec<i32> = view.iter().copied().collect();
assert_eq!(values, vec![10, 20, 30]);
assert_eq!(view.as_ptr().cast::<u8>(), bytes.as_ptr());
}
#[test]
fn test_frombuffer_view_f64_2d() {
let source: Vec<f64> = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let bytes = aligned_bytes(&source);
let view = frombuffer_view::<f64, Ix2>(Ix2::new([2, 3]), &bytes).unwrap();
assert_eq!(view.shape(), &[2, 3]);
let values: Vec<f64> = view.iter().copied().collect();
assert_eq!(values, source);
}
#[test]
fn test_frombuffer_view_bool_valid() {
let bytes: Vec<u8> = vec![0, 1, 0, 1];
let view = frombuffer_view::<bool, Ix1>(Ix1::new([4]), &bytes).unwrap();
let values: Vec<bool> = view.iter().copied().collect();
assert_eq!(values, vec![false, true, false, true]);
}
#[test]
fn test_frombuffer_view_bool_rejects_invalid_byte() {
let bytes: Vec<u8> = vec![0, 1, 42]; assert!(frombuffer_view::<bool, Ix1>(Ix1::new([3]), &bytes).is_err());
}
#[test]
fn test_frombuffer_view_rejects_wrong_length() {
let bytes = vec![0u8; 13];
assert!(frombuffer_view::<i32, Ix1>(Ix1::new([3]), &bytes).is_err());
let bytes = vec![0u8; 8];
assert!(frombuffer_view::<i32, Ix1>(Ix1::new([3]), &bytes).is_err());
}
#[test]
fn test_frombuffer_view_rejects_misalignment() {
let mut backing: Vec<u8> = vec![0u8; 1 + 4 * 3];
for (i, chunk) in backing[1..].chunks_exact_mut(4).enumerate() {
chunk.copy_from_slice(&(i as i32).to_ne_bytes());
}
let misaligned = &backing[1..];
assert!((misaligned.as_ptr() as usize) % 4 != 0);
assert!(frombuffer_view::<i32, Ix1>(Ix1::new([3]), misaligned).is_err());
}
#[test]
fn test_fromiter() {
let a = fromiter((0..5).map(|x| x as f64)).unwrap();
assert_eq!(a.shape(), &[5]);
assert_eq!(a.as_slice().unwrap(), &[0.0, 1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn test_zeros() {
let a = zeros::<f64, Ix2>(Ix2::new([3, 4])).unwrap();
assert_eq!(a.shape(), &[3, 4]);
assert!(a.iter().all(|&v| v == 0.0));
}
#[test]
fn test_ones() {
let a = ones::<f64, Ix1>(Ix1::new([5])).unwrap();
assert!(a.iter().all(|&v| v == 1.0));
}
#[test]
fn test_full() {
let a = full(Ix1::new([4]), 42i32).unwrap();
assert!(a.iter().all(|&v| v == 42));
}
#[test]
fn test_zeros_like() {
let a = ones::<f64, Ix2>(Ix2::new([2, 3])).unwrap();
let b = zeros_like(&a).unwrap();
assert_eq!(b.shape(), &[2, 3]);
assert!(b.iter().all(|&v| v == 0.0));
}
#[test]
fn test_ones_like() {
let a = zeros::<f64, Ix1>(Ix1::new([4])).unwrap();
let b = ones_like(&a).unwrap();
assert!(b.iter().all(|&v| v == 1.0));
}
#[test]
fn test_full_like() {
let a = zeros::<i32, Ix1>(Ix1::new([3])).unwrap();
let b = full_like(&a, 7).unwrap();
assert!(b.iter().all(|&v| v == 7));
}
#[test]
fn test_empty_and_init() {
let mut u = empty::<f64, Ix1>(Ix1::new([3]));
assert_eq!(u.shape(), &[3]);
u.write_at(0, 1.0).unwrap();
u.write_at(1, 2.0).unwrap();
u.write_at(2, 3.0).unwrap();
let a = unsafe { u.assume_init() };
assert_eq!(a.as_slice().unwrap(), &[1.0, 2.0, 3.0]);
}
#[test]
fn test_empty_write_oob() {
let mut u = empty::<f64, Ix1>(Ix1::new([2]));
assert!(u.write_at(5, 1.0).is_err());
}
#[test]
fn test_empty_like_matches_shape_2d() {
use crate::dimension::Ix2;
let src = Array::<f64, Ix2>::from_vec(Ix2::new([2, 3]), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.unwrap();
let mut u = empty_like(&src);
assert_eq!(u.shape(), &[2, 3]);
assert_eq!(u.size(), 6);
assert_eq!(u.ndim(), 2);
for i in 0..6 {
u.write_at(i, -(i as f64)).unwrap();
}
let out = unsafe { u.assume_init() };
assert_eq!(out.shape(), &[2, 3]);
assert_eq!(
out.as_slice().unwrap(),
&[0.0, -1.0, -2.0, -3.0, -4.0, -5.0]
);
assert_eq!(src.as_slice().unwrap(), &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
}
#[test]
fn test_empty_like_zero_sized() {
let src = Array::<f64, Ix1>::from_vec(Ix1::new([0]), vec![]).unwrap();
let u = empty_like(&src);
assert_eq!(u.shape(), &[0]);
assert_eq!(u.size(), 0);
let out = unsafe { u.assume_init() };
assert_eq!(out.size(), 0);
}
#[test]
fn test_arange_int() {
let a = arange(0i32, 5, 1).unwrap();
assert_eq!(a.as_slice().unwrap(), &[0, 1, 2, 3, 4]);
}
#[test]
fn test_arange_float() {
let a = arange(0.0_f64, 1.0, 0.25).unwrap();
assert_eq!(a.shape(), &[4]);
let data = a.as_slice().unwrap();
assert!((data[0] - 0.0).abs() < 1e-10);
assert!((data[1] - 0.25).abs() < 1e-10);
assert!((data[2] - 0.5).abs() < 1e-10);
assert!((data[3] - 0.75).abs() < 1e-10);
}
#[test]
fn test_arange_negative_step() {
let a = arange(5.0_f64, 0.0, -1.0).unwrap();
assert_eq!(a.shape(), &[5]);
}
#[test]
fn test_arange_zero_step() {
assert!(arange(0.0_f64, 1.0, 0.0).is_err());
}
#[test]
fn test_arange_empty() {
let a = arange(5i32, 0, 1).unwrap();
assert_eq!(a.shape(), &[0]);
}
#[test]
fn test_linspace() {
let a = linspace(0.0_f64, 1.0, 5, true).unwrap();
assert_eq!(a.shape(), &[5]);
let data = a.as_slice().unwrap();
assert!((data[0] - 0.0).abs() < 1e-10);
assert!((data[4] - 1.0).abs() < 1e-10);
assert!((data[2] - 0.5).abs() < 1e-10);
}
#[test]
fn test_linspace_no_endpoint() {
let a = linspace(0.0_f64, 1.0, 4, false).unwrap();
assert_eq!(a.shape(), &[4]);
let data = a.as_slice().unwrap();
assert!((data[0] - 0.0).abs() < 1e-10);
assert!((data[1] - 0.25).abs() < 1e-10);
}
#[test]
fn test_linspace_single() {
let a = linspace(5.0_f64, 10.0, 1, true).unwrap();
assert_eq!(a.as_slice().unwrap(), &[5.0]);
}
#[test]
fn test_linspace_empty() {
let a = linspace(0.0_f64, 1.0, 0, true).unwrap();
assert_eq!(a.shape(), &[0]);
}
#[test]
fn test_logspace() {
let a = logspace(0.0_f64, 2.0, 3, true, 10.0).unwrap();
let data = a.as_slice().unwrap();
assert!((data[0] - 1.0).abs() < 1e-10); assert!((data[1] - 10.0).abs() < 1e-10); assert!((data[2] - 100.0).abs() < 1e-10); }
#[test]
fn test_geomspace() {
let a = geomspace(1.0_f64, 1000.0, 4, true).unwrap();
let data = a.as_slice().unwrap();
assert!((data[0] - 1.0).abs() < 1e-10);
assert!((data[1] - 10.0).abs() < 1e-8);
assert!((data[2] - 100.0).abs() < 1e-6);
assert!((data[3] - 1000.0).abs() < 1e-4);
}
#[test]
fn test_geomspace_zero_start() {
assert!(geomspace(0.0_f64, 1.0, 5, true).is_err());
}
#[test]
fn test_geomspace_different_signs() {
assert!(geomspace(-1.0_f64, 1.0, 5, true).is_err());
}
#[test]
fn test_meshgrid_xy() {
let x = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let y = Array::from_vec(Ix1::new([2]), vec![4.0, 5.0]).unwrap();
let grids = meshgrid(&[x, y], "xy").unwrap();
assert_eq!(grids.len(), 2);
assert_eq!(grids[0].shape(), &[2, 3]);
assert_eq!(grids[1].shape(), &[2, 3]);
let xdata: Vec<f64> = grids[0].iter().copied().collect();
assert_eq!(xdata, vec![1.0, 2.0, 3.0, 1.0, 2.0, 3.0]);
let ydata: Vec<f64> = grids[1].iter().copied().collect();
assert_eq!(ydata, vec![4.0, 4.0, 4.0, 5.0, 5.0, 5.0]);
}
#[test]
fn test_meshgrid_ij() {
let x = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let y = Array::from_vec(Ix1::new([2]), vec![4.0, 5.0]).unwrap();
let grids = meshgrid(&[x, y], "ij").unwrap();
assert_eq!(grids.len(), 2);
assert_eq!(grids[0].shape(), &[3, 2]);
assert_eq!(grids[1].shape(), &[3, 2]);
}
#[test]
fn test_meshgrid_bad_indexing() {
assert!(meshgrid(&[], "zz").is_err());
}
#[test]
fn test_mgrid() {
let grids = mgrid(&[(0.0, 3.0, 1.0), (0.0, 2.0, 1.0)]).unwrap();
assert_eq!(grids.len(), 2);
assert_eq!(grids[0].shape(), &[3, 2]);
}
#[test]
fn test_ogrid() {
let grids = ogrid(&[(0.0, 3.0, 1.0), (0.0, 2.0, 1.0)]).unwrap();
assert_eq!(grids.len(), 2);
assert_eq!(grids[0].shape(), &[3, 1]);
assert_eq!(grids[1].shape(), &[1, 2]);
}
#[test]
fn test_identity() {
let a = identity::<f64>(3).unwrap();
assert_eq!(a.shape(), &[3, 3]);
let data = a.as_slice().unwrap();
assert_eq!(data, &[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]);
}
#[test]
fn test_eye() {
let a = eye::<f64>(3, 4, 0).unwrap();
assert_eq!(a.shape(), &[3, 4]);
let data = a.as_slice().unwrap();
assert_eq!(
data,
&[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]
);
}
#[test]
fn test_eye_positive_k() {
let a = eye::<f64>(3, 3, 1).unwrap();
let data = a.as_slice().unwrap();
assert_eq!(data, &[0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]);
}
#[test]
fn test_eye_negative_k() {
let a = eye::<f64>(3, 3, -1).unwrap();
let data = a.as_slice().unwrap();
assert_eq!(data, &[0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0]);
}
#[test]
fn test_diag_from_1d() {
let a = Array::from_vec(IxDyn::new(&[3]), vec![1.0, 2.0, 3.0]).unwrap();
let d = diag(&a, 0).unwrap();
assert_eq!(d.shape(), &[3, 3]);
let data: Vec<f64> = d.iter().copied().collect();
assert_eq!(data, vec![1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 3.0]);
}
#[test]
fn test_diag_from_2d() {
let a = Array::from_vec(
IxDyn::new(&[3, 3]),
vec![1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 3.0],
)
.unwrap();
let d = diag(&a, 0).unwrap();
assert_eq!(d.shape(), &[3]);
let data: Vec<f64> = d.iter().copied().collect();
assert_eq!(data, vec![1.0, 2.0, 3.0]);
}
#[test]
fn test_diag_k_positive() {
let a = Array::from_vec(IxDyn::new(&[2]), vec![1.0, 2.0]).unwrap();
let d = diag(&a, 1).unwrap();
assert_eq!(d.shape(), &[3, 3]);
let data: Vec<f64> = d.iter().copied().collect();
assert_eq!(data, vec![0.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0]);
}
#[test]
fn test_diagflat() {
let a = Array::from_vec(IxDyn::new(&[2, 2]), vec![1.0, 2.0, 3.0, 4.0]).unwrap();
let d = diagflat(&a, 0).unwrap();
assert_eq!(d.shape(), &[4, 4]);
let extracted = diag(&d, 0).unwrap();
let data: Vec<f64> = extracted.iter().copied().collect();
assert_eq!(data, vec![1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn test_tri() {
let a = tri::<f64>(3, 3, 0).unwrap();
let data = a.as_slice().unwrap();
assert_eq!(data, &[1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0]);
}
#[test]
fn test_tril() {
let a = Array::from_vec(
IxDyn::new(&[3, 3]),
vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
)
.unwrap();
let t = tril(&a, 0).unwrap();
let data: Vec<f64> = t.iter().copied().collect();
assert_eq!(data, vec![1.0, 0.0, 0.0, 4.0, 5.0, 0.0, 7.0, 8.0, 9.0]);
}
#[test]
fn test_triu() {
let a = Array::from_vec(
IxDyn::new(&[3, 3]),
vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
)
.unwrap();
let t = triu(&a, 0).unwrap();
let data: Vec<f64> = t.iter().copied().collect();
assert_eq!(data, vec![1.0, 2.0, 3.0, 0.0, 5.0, 6.0, 0.0, 0.0, 9.0]);
}
#[test]
fn test_tril_not_2d() {
let a = Array::from_vec(IxDyn::new(&[3]), vec![1.0, 2.0, 3.0]).unwrap();
assert!(tril(&a, 0).is_err());
}
#[test]
fn test_triu_not_2d() {
let a = Array::from_vec(IxDyn::new(&[3]), vec![1.0, 2.0, 3.0]).unwrap();
assert!(triu(&a, 0).is_err());
}
#[test]
fn test_vander_default_decreasing() {
let x = Array::from_vec(Ix1::new([3]), vec![1.0_f64, 2.0, 3.0]).unwrap();
let v = vander(&x, None, false).unwrap();
assert_eq!(v.shape(), &[3, 3]);
let data: Vec<f64> = v.iter().copied().collect();
assert_eq!(data, vec![1.0, 1.0, 1.0, 4.0, 2.0, 1.0, 9.0, 3.0, 1.0]);
}
#[test]
fn test_vander_increasing() {
let x = Array::from_vec(Ix1::new([3]), vec![1.0_f64, 2.0, 3.0]).unwrap();
let v = vander(&x, None, true).unwrap();
let data: Vec<f64> = v.iter().copied().collect();
assert_eq!(data, vec![1.0, 1.0, 1.0, 1.0, 2.0, 4.0, 1.0, 3.0, 9.0]);
}
#[test]
fn test_vander_explicit_n() {
let x = Array::from_vec(Ix1::new([2]), vec![2.0_f64, 3.0]).unwrap();
let v = vander(&x, Some(4), false).unwrap();
assert_eq!(v.shape(), &[2, 4]);
let data: Vec<f64> = v.iter().copied().collect();
assert_eq!(data, vec![8.0, 4.0, 2.0, 1.0, 27.0, 9.0, 3.0, 1.0]);
}
#[test]
fn test_vander_empty() {
let x: Array<f64, Ix1> = Array::from_vec(Ix1::new([0]), vec![]).unwrap();
assert!(vander(&x, None, false).is_err());
}
#[test]
fn test_copy() {
let a = array(Ix1::new([3]), vec![1.0_f64, 2.0, 3.0]).unwrap();
let b = copy(&a);
assert_eq!(
a.iter().copied().collect::<Vec<_>>(),
b.iter().copied().collect::<Vec<_>>()
);
assert_ne!(
a.as_slice().unwrap().as_ptr(),
b.as_slice().unwrap().as_ptr()
);
}
#[test]
fn test_ascontiguousarray() {
let a = array(Ix1::new([3]), vec![1.0_f64, 2.0, 3.0]).unwrap();
let b = ascontiguousarray(&a);
assert_eq!(b.shape(), a.shape());
}
#[test]
fn test_asarray_chkfinite_ok() {
let a = array(Ix1::new([3]), vec![1.0_f64, 2.0, 3.0]).unwrap();
assert!(asarray_chkfinite(&a).is_ok());
}
#[test]
fn test_asarray_chkfinite_nan_fails() {
let a = array(Ix1::new([3]), vec![1.0_f64, f64::NAN, 3.0]).unwrap();
assert!(asarray_chkfinite(&a).is_err());
}
#[test]
fn test_asarray_chkfinite_inf_fails() {
let a = array(Ix1::new([3]), vec![1.0_f64, f64::INFINITY, 3.0]).unwrap();
assert!(asarray_chkfinite(&a).is_err());
}
#[test]
fn test_require() {
let a = array(Ix1::new([3]), vec![1, 2, 3]).unwrap();
let b = require(&a, "CW");
assert_eq!(b.shape(), a.shape());
}
#[test]
fn test_fromfunction_2d() {
let a = fromfunction(Ix2::new([3, 3]), |idx| (idx[0] + idx[1]) as i32).unwrap();
let data: Vec<i32> = a.iter().copied().collect();
assert_eq!(data, vec![0, 1, 2, 1, 2, 3, 2, 3, 4]);
}
#[test]
fn test_fromfunction_1d() {
let a = fromfunction(Ix1::new([5]), |idx| idx[0] as f64 * 2.0).unwrap();
let data: Vec<f64> = a.iter().copied().collect();
assert_eq!(data, vec![0.0, 2.0, 4.0, 6.0, 8.0]);
}
#[test]
fn test_fromstring_whitespace() {
let a: Array<f64, Ix1> = fromstring("1.5 2.5 3.5", " ").unwrap();
let data: Vec<f64> = a.iter().copied().collect();
assert_eq!(data, vec![1.5, 2.5, 3.5]);
}
#[test]
fn test_fromstring_comma() {
let a: Array<i32, Ix1> = fromstring("1, 2, 3, 4", ",").unwrap();
assert_eq!(a.shape(), &[4]);
let data: Vec<i32> = a.iter().copied().collect();
assert_eq!(data, vec![1, 2, 3, 4]);
}
#[test]
fn test_fromstring_empty_sep_uses_whitespace() {
let a: Array<i32, Ix1> = fromstring("1 2\t3\n4", "").unwrap();
let data: Vec<i32> = a.iter().copied().collect();
assert_eq!(data, vec![1, 2, 3, 4]);
}
#[test]
fn test_fromstring_bad_token() {
let r: FerrayResult<Array<f64, Ix1>> = fromstring("1.0 abc 3.0", " ");
assert!(r.is_err());
}
}