use crate::array::Array;
use crate::error::{NumRs2Error, Result};
pub fn fromfunction<T, F>(function: F, shape: &[usize]) -> Result<Array<T>>
where
T: Clone + num_traits::Zero,
F: Fn(&[usize]) -> T,
{
if shape.is_empty() {
return Ok(Array::from_vec(vec![]));
}
let total_elements: usize = shape.iter().product();
let mut result_data = Vec::with_capacity(total_elements);
let mut indices = vec![0; shape.len()];
for _ in 0..total_elements {
let value = function(&indices);
result_data.push(value);
let mut carry = true;
for dim in (0..shape.len()).rev() {
if carry {
indices[dim] += 1;
carry = indices[dim] >= shape[dim];
if carry {
indices[dim] = 0;
}
}
}
}
Ok(Array::from_vec(result_data).reshape(shape))
}
pub fn frombuffer<T: Clone + Default>(
buffer: &[u8],
dtype_size: usize,
count: isize,
offset: usize,
) -> Result<Array<T>> {
if dtype_size == 0 {
return Err(NumRs2Error::InvalidOperation(
"Data type size cannot be zero".to_string(),
));
}
if offset >= buffer.len() {
return Err(NumRs2Error::IndexOutOfBounds(format!(
"Offset {} is beyond buffer size {}",
offset,
buffer.len()
)));
}
if dtype_size != std::mem::size_of::<T>() {
return Err(NumRs2Error::InvalidOperation(format!(
"Data type size mismatch: expected {}, got {}",
std::mem::size_of::<T>(),
dtype_size
)));
}
let available_bytes = buffer.len() - offset;
let max_elements = available_bytes / dtype_size;
let num_elements = if count < 0 {
max_elements
} else {
let requested = count as usize;
if requested > max_elements {
return Err(NumRs2Error::InvalidOperation(format!(
"Requested {} elements but only {} available in buffer",
requested, max_elements
)));
}
requested
};
if num_elements == 0 {
return Ok(Array::from_vec(vec![]));
}
let mut result = Vec::with_capacity(num_elements);
for i in 0..num_elements {
let byte_offset = offset + i * dtype_size;
let element_bytes = &buffer[byte_offset..byte_offset + dtype_size];
let element = unsafe { std::ptr::read(element_bytes.as_ptr() as *const T) };
result.push(element);
}
Ok(Array::from_vec(result))
}
pub fn fromiter<T: Clone, I: Iterator<Item = T>>(
iter: I,
shape: Option<&[usize]>,
) -> Result<Array<T>> {
let data: Vec<T> = iter.collect();
match shape {
Some(s) => {
let expected_size: usize = s.iter().product();
if data.len() != expected_size {
return Err(NumRs2Error::ShapeMismatch {
expected: vec![expected_size],
actual: vec![data.len()],
});
}
Ok(Array::from_vec(data).reshape(s))
}
None => Ok(Array::from_vec(data)),
}
}
pub fn frommemmap<T: Copy + Clone + Default>(
path: &std::path::Path,
mode: &str,
offset: Option<usize>,
shape: Option<&[usize]>,
order: Option<&str>,
) -> Result<Array<T>> {
use crate::mmap::{open_mmap_info, MmapArray};
let _offset = offset.unwrap_or(0);
let _order = order.unwrap_or("C");
match mode {
"r" | "r+" => {}
_ => {
return Err(NumRs2Error::InvalidOperation(format!(
"Unsupported mode '{}'. Use 'r' for read-only or 'r+' for read-write",
mode
)))
}
}
let meta = open_mmap_info(&path)?;
let array_shape = match shape {
Some(s) => s.to_vec(),
None => meta.shape.clone(),
};
if meta.type_name != std::any::type_name::<T>() {
return Err(NumRs2Error::InvalidOperation(format!(
"Type mismatch: file contains '{}', but requested '{}'",
meta.type_name,
std::any::type_name::<T>()
)));
}
let mmap_array = MmapArray::<T>::new(&path, &array_shape, false)?;
let array = mmap_array.to_array()?;
Ok(array)
}
pub fn fromstring<T>(string: &str) -> Result<Array<T>>
where
T: std::str::FromStr + Clone + num_traits::Zero,
T::Err: std::fmt::Display,
{
if string.trim().is_empty() {
return Ok(Array::from_vec(vec![]));
}
let values: Result<Vec<T>> = string
.split_whitespace()
.map(|s| {
s.parse::<T>()
.map_err(|e| NumRs2Error::ValueError(format!("Failed to parse '{}': {}", s, e)))
})
.collect();
Ok(Array::from_vec(values?))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mmap::MmapArray;
use std::fs;
use std::path::Path;
#[test]
fn test_fromfunction() {
let result = fromfunction(
|indices: &[usize]| (indices[0] + indices[1]) as f64,
&[3, 3],
)
.expect("operation should succeed");
assert_eq!(result.shape(), vec![3, 3]);
assert_eq!(result.get(&[0, 0]).expect("operation should succeed"), 0.0);
assert_eq!(result.get(&[0, 1]).expect("operation should succeed"), 1.0);
assert_eq!(result.get(&[1, 1]).expect("operation should succeed"), 2.0);
assert_eq!(result.get(&[2, 2]).expect("operation should succeed"), 4.0);
let result = fromfunction(|indices: &[usize]| indices[0] as i32 * 2, &[5])
.expect("operation should succeed");
assert_eq!(result.shape(), vec![5]);
assert_eq!(result.to_vec(), vec![0, 2, 4, 6, 8]);
}
#[test]
fn test_frombuffer() {
let data: Vec<i32> = vec![1, 2, 3, 4, 5];
let buffer = unsafe {
std::slice::from_raw_parts(
data.as_ptr() as *const u8,
data.len() * std::mem::size_of::<i32>(),
)
};
let result = frombuffer::<i32>(buffer, std::mem::size_of::<i32>(), -1, 0)
.expect("operation should succeed");
assert_eq!(result.to_vec(), vec![1, 2, 3, 4, 5]);
let result = frombuffer::<i32>(buffer, std::mem::size_of::<i32>(), 3, 0)
.expect("operation should succeed");
assert_eq!(result.to_vec(), vec![1, 2, 3]);
let result = frombuffer::<i32>(
buffer,
std::mem::size_of::<i32>(),
2,
std::mem::size_of::<i32>(),
)
.expect("operation should succeed");
assert_eq!(result.to_vec(), vec![2, 3]);
}
#[test]
fn test_fromiter() {
let result = fromiter((0..5).map(|x| x as f64), None).expect("operation should succeed");
assert_eq!(result.to_vec(), vec![0.0, 1.0, 2.0, 3.0, 4.0]);
let result = fromiter(0..6, Some(&[2, 3])).expect("operation should succeed");
assert_eq!(result.shape(), vec![2, 3]);
assert_eq!(result.to_vec(), vec![0, 1, 2, 3, 4, 5]);
let result = fromiter(0..5, Some(&[2, 3]));
assert!(result.is_err());
}
#[test]
fn test_frommemmap() {
let test_path = std::env::temp_dir().join("test_frommemmap.tmp");
let path = test_path.as_path();
let cleanup = || {
let _ = fs::remove_file(path);
};
cleanup();
let data = vec![1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0];
let shape = vec![2, 3];
let array = Array::from_vec(data.clone()).reshape(&shape);
let mmap_array = match MmapArray::from_array(&array, &path) {
Ok(mmap) => mmap,
Err(_) => {
println!("Skipping frommemmap test due to file permission issues");
return;
}
};
drop(mmap_array);
match frommemmap::<f64>(path, "r", None, None, None) {
Ok(result) => {
assert_eq!(result.shape(), shape);
assert_eq!(result.to_vec(), data);
let result = frommemmap::<f64>(path, "r", None, Some(&[6]), None)
.expect("operation should succeed");
assert_eq!(result.shape(), vec![6]);
assert_eq!(result.to_vec(), data);
}
Err(_) => {
println!("Skipping frommemmap test due to file permission issues");
}
}
cleanup();
}
#[test]
fn test_frommemmap_errors() {
let result = frommemmap::<f64>(Path::new("non_existent.mmap"), "r", None, None, None);
assert!(result.is_err());
let test_path = std::env::temp_dir().join("test_frommemmap_errors.tmp");
let path = test_path.as_path();
let cleanup = || {
let _ = fs::remove_file(path);
};
cleanup();
let data = vec![1.0f64, 2.0, 3.0, 4.0];
let array = Array::from_vec(data).reshape(&[2, 2]);
let _mmap_array = MmapArray::from_array(&array, &path).expect("operation should succeed");
let result = frommemmap::<f64>(path, "invalid_mode", None, None, None);
assert!(result.is_err());
cleanup();
}
}