use crate::array::Array;
use crate::error::{NumRs2Error, Result};
use num_traits::{Float, One, Zero};
use std::ops::Mul;
pub fn nanargmax<T>(array: &Array<T>, axis: Option<usize>, keepdims: bool) -> Result<Array<usize>>
where
T: Float + Clone + Zero,
{
if array.is_empty() {
return Err(crate::error::NumRs2Error::InvalidOperation(
"Cannot find nanargmax of empty array".to_string(),
));
}
match axis {
None => {
let data = array.to_vec();
let mut max_idx = 0;
let mut max_val: Option<T> = None;
for (i, val) in data.iter().enumerate() {
if !val.is_nan() && max_val.is_none_or(|mv| *val > mv) {
max_val = Some(*val);
max_idx = i;
}
}
Ok(Array::from_vec(vec![max_idx]))
}
Some(ax) => {
if ax >= array.ndim() {
return Err(crate::error::NumRs2Error::DimensionMismatch(format!(
"Axis {} out of bounds for array of dimension {}",
ax,
array.ndim()
)));
}
let shape = array.shape();
let axis_size = shape[ax];
let mut out_shape = shape.clone();
if keepdims {
out_shape[ax] = 1;
} else {
out_shape.remove(ax);
}
if out_shape.is_empty() {
out_shape.push(1);
}
let out_size: usize = out_shape.iter().product();
let mut result_data = vec![0_usize; out_size];
let mut strides = vec![1; array.ndim()];
for i in (0..array.ndim() - 1).rev() {
strides[i] = strides[i + 1] * shape[i + 1];
}
for out_idx in 0..out_size {
let mut indices = vec![0; array.ndim()];
let mut temp = out_idx;
for i in 0..array.ndim() {
if i < ax {
let dim_size = shape[i];
indices[i] = temp % dim_size;
temp /= dim_size;
} else if i > ax || (i == ax && keepdims) {
let dim_idx = if keepdims { i } else { i - 1 };
if dim_idx < out_shape.len() {
let dim_size = out_shape[dim_idx];
indices[i] = temp % dim_size;
temp /= dim_size;
}
}
}
let mut max_idx = 0;
let mut max_val: Option<T> = None;
for j in 0..axis_size {
indices[ax] = j;
let val = array.get(&indices)?;
if !val.is_nan() && max_val.is_none_or(|mv| val > mv) {
max_val = Some(val);
max_idx = j;
}
}
result_data[out_idx] = max_idx;
}
Ok(Array::from_vec(result_data).reshape(&out_shape))
}
}
}
pub fn nanargmin<T>(array: &Array<T>, axis: Option<usize>, keepdims: bool) -> Result<Array<usize>>
where
T: Float + Clone + Zero,
{
if array.is_empty() {
return Err(crate::error::NumRs2Error::InvalidOperation(
"Cannot find nanargmin of empty array".to_string(),
));
}
match axis {
None => {
let data = array.to_vec();
let mut min_idx = 0;
let mut min_val: Option<T> = None;
for (i, val) in data.iter().enumerate() {
if !val.is_nan() && min_val.is_none_or(|mv| *val < mv) {
min_val = Some(*val);
min_idx = i;
}
}
Ok(Array::from_vec(vec![min_idx]))
}
Some(ax) => {
if ax >= array.ndim() {
return Err(crate::error::NumRs2Error::DimensionMismatch(format!(
"Axis {} out of bounds for array of dimension {}",
ax,
array.ndim()
)));
}
let shape = array.shape();
let axis_size = shape[ax];
let mut out_shape = shape.clone();
if keepdims {
out_shape[ax] = 1;
} else {
out_shape.remove(ax);
}
if out_shape.is_empty() {
out_shape.push(1);
}
let out_size: usize = out_shape.iter().product();
let mut result_data = vec![0_usize; out_size];
let mut strides = vec![1; array.ndim()];
for i in (0..array.ndim() - 1).rev() {
strides[i] = strides[i + 1] * shape[i + 1];
}
for out_idx in 0..out_size {
let mut indices = vec![0; array.ndim()];
let mut temp = out_idx;
for i in 0..array.ndim() {
if i < ax {
let dim_size = shape[i];
indices[i] = temp % dim_size;
temp /= dim_size;
} else if i > ax || (i == ax && keepdims) {
let dim_idx = if keepdims { i } else { i - 1 };
if dim_idx < out_shape.len() {
let dim_size = out_shape[dim_idx];
indices[i] = temp % dim_size;
temp /= dim_size;
}
}
}
let mut min_idx = 0;
let mut min_val: Option<T> = None;
for j in 0..axis_size {
indices[ax] = j;
let val = array.get(&indices)?;
if !val.is_nan() && min_val.is_none_or(|mv| val < mv) {
min_val = Some(val);
min_idx = j;
}
}
result_data[out_idx] = min_idx;
}
Ok(Array::from_vec(result_data).reshape(&out_shape))
}
}
}
pub fn nancumprod<T>(array: &Array<T>, axis: Option<isize>) -> Result<Array<T>>
where
T: Float + Clone + Mul<Output = T> + Zero + One,
{
if let Some(ax) = axis {
let ax = if ax < 0 {
(array.ndim() as isize + ax) as usize
} else {
ax as usize
};
if ax >= array.ndim() {
return Err(NumRs2Error::DimensionMismatch(format!(
"axis {} is out of bounds for array of dimension {}",
ax,
array.ndim()
)));
}
let shape = array.shape();
let mut result = Array::ones(&shape);
let axis_len = shape[ax];
let mut strides = vec![1; shape.len()];
for i in (0..shape.len() - 1).rev() {
strides[i] = strides[i + 1] * shape[i + 1];
}
let total_elems: usize = shape.iter().product();
let axis_stride = strides[ax];
let group_size = axis_stride * axis_len;
for group_start in (0..total_elems).step_by(group_size) {
for offset in 0..axis_stride {
let mut cumprod = T::one();
for i in 0..axis_len {
let idx = group_start + i * axis_stride + offset;
let flat_idx = idx;
let mut indices = vec![0; shape.len()];
let mut temp = flat_idx;
for j in 0..shape.len() {
indices[j] = temp / strides[j];
temp %= strides[j];
}
let value = array.get(&indices)?;
if !value.is_nan() {
cumprod = cumprod * value;
}
result.set(&indices, cumprod)?;
}
}
}
Ok(result)
} else {
let flat = array.to_vec();
let mut result = Vec::with_capacity(flat.len());
let mut cumprod = T::one();
for value in flat {
if !value.is_nan() {
cumprod = cumprod * value;
}
result.push(cumprod);
}
Ok(Array::from_vec(result).reshape(&array.shape()))
}
}