use crate::array::Array;
use crate::error::{NumRs2Error, Result};
use num_traits::{Float, NumCast, PrimInt, Zero};
use scirs2_core::Complex;
pub fn gcd<T>(x1: &Array<T>, x2: &Array<T>) -> Result<Array<T>>
where
T: Clone + Zero + PartialEq + std::ops::Rem<Output = T> + Copy,
{
if x1.shape() != x2.shape() {
return Err(NumRs2Error::ShapeMismatch {
expected: x1.shape(),
actual: x2.shape(),
});
}
let x1_data = x1.to_vec();
let x2_data = x2.to_vec();
let result_data: Vec<T> = x1_data
.iter()
.zip(x2_data.iter())
.map(|(&a, &b)| gcd_scalar(a, b))
.collect();
Ok(Array::from_vec(result_data).reshape(&x1.shape()))
}
fn gcd_scalar<T>(mut a: T, mut b: T) -> T
where
T: Clone + Zero + PartialEq + std::ops::Rem<Output = T> + Copy,
{
while b != T::zero() {
let temp = b;
b = a % b;
a = temp;
}
a
}
pub fn lcm<T>(x1: &Array<T>, x2: &Array<T>) -> Result<Array<T>>
where
T: Clone
+ Zero
+ PartialEq
+ std::ops::Rem<Output = T>
+ std::ops::Div<Output = T>
+ std::ops::Mul<Output = T>
+ Copy,
{
if x1.shape() != x2.shape() {
return Err(NumRs2Error::ShapeMismatch {
expected: x1.shape(),
actual: x2.shape(),
});
}
let x1_data = x1.to_vec();
let x2_data = x2.to_vec();
let result_data: Vec<T> = x1_data
.iter()
.zip(x2_data.iter())
.map(|(&a, &b)| {
if a == T::zero() || b == T::zero() {
T::zero()
} else {
let gcd_val = gcd_scalar(a, b);
a * b / gcd_val
}
})
.collect();
Ok(Array::from_vec(result_data).reshape(&x1.shape()))
}
pub fn copysign<T>(x1: &Array<T>, x2: &Array<T>) -> Result<Array<T>>
where
T: Float + Clone,
{
if x1.shape() != x2.shape() {
return Err(NumRs2Error::ShapeMismatch {
expected: x1.shape(),
actual: x2.shape(),
});
}
let x1_data = x1.to_vec();
let x2_data = x2.to_vec();
let result_data: Vec<T> = x1_data
.iter()
.zip(x2_data.iter())
.map(|(&val, &sign_val)| {
if sign_val < T::zero() {
-val.abs()
} else {
val.abs()
}
})
.collect();
Ok(Array::from_vec(result_data).reshape(&x1.shape()))
}
pub fn nextafter<T>(x1: &Array<T>, x2: &Array<T>) -> Result<Array<T>>
where
T: Float + Clone,
{
if x1.shape() != x2.shape() {
return Err(NumRs2Error::ShapeMismatch {
expected: x1.shape(),
actual: x2.shape(),
});
}
let x1_data = x1.to_vec();
let x2_data = x2.to_vec();
let epsilon = T::epsilon();
let result_data: Vec<T> = x1_data
.iter()
.zip(x2_data.iter())
.map(|(&from, &to)| {
if from == to {
from
} else if from.is_nan() || to.is_nan() {
T::nan()
} else if from < to {
if from == T::infinity() {
from
} else {
from + from.abs() * epsilon
}
} else {
if from == T::neg_infinity() {
from
} else {
from - from.abs() * epsilon
}
}
})
.collect();
Ok(Array::from_vec(result_data).reshape(&x1.shape()))
}
pub fn sinc<T>(x: &Array<T>) -> Array<T>
where
T: Float + Clone,
{
x.map(|val| {
if val == T::zero() {
T::one()
} else {
let pi = T::from(std::f64::consts::PI).expect("PI constant should be representable");
let pix = pi * val;
pix.sin() / pix
}
})
}
pub fn heaviside<T>(x: &Array<T>, h0: Option<T>) -> Array<T>
where
T: Float + Clone,
{
let h0_val = h0.unwrap_or_else(|| T::from(0.5).expect("0.5 should be representable"));
x.map(|val| {
if val < T::zero() {
T::zero()
} else if val == T::zero() {
h0_val
} else {
T::one()
}
})
}
pub fn real_if_close<T>(a: &Array<Complex<T>>, tol: Option<T>) -> Array<T>
where
T: Float + Clone,
{
let tolerance = tol.unwrap_or_else(|| T::from(100.0).expect("100.0 should be representable"));
let epsilon = T::epsilon();
let threshold = tolerance * epsilon;
let data = a.to_vec();
let all_close = data.iter().all(|c| c.im.abs() <= threshold);
if all_close {
let real_data: Vec<T> = data.iter().map(|c| c.re).collect();
Array::from_vec(real_data).reshape(&a.shape())
} else {
let real_data: Vec<T> = data.iter().map(|c| c.re).collect();
Array::from_vec(real_data).reshape(&a.shape())
}
}
pub fn ldexp<T, I>(x: &Array<T>, exp: &Array<I>) -> Result<Array<T>>
where
T: Float + Clone,
I: Clone + NumCast,
{
if x.shape() != exp.shape() {
return Err(NumRs2Error::DimensionMismatch(format!(
"Shape mismatch: x has shape {:?}, exp has shape {:?}",
x.shape(),
exp.shape()
)));
}
let x_vec = x.to_vec();
let exp_vec = exp.to_vec();
let mut result = Vec::with_capacity(x_vec.len());
for (x_val, exp_val) in x_vec.iter().zip(exp_vec.iter()) {
let exp_f64 = I::to_f64(exp_val).ok_or_else(|| {
NumRs2Error::ConversionError("Failed to convert exponent to f64".to_string())
})?;
let two = T::from(2.0).expect("2.0 should be representable");
result.push(*x_val * two.powf(T::from(exp_f64).expect("exponent should be representable")));
}
Ok(Array::from_vec(result).reshape(&x.shape()))
}
pub fn frexp<T>(x: &Array<T>) -> (Array<T>, Array<i32>)
where
T: Float + Clone,
{
let x_vec = x.to_vec();
let mut mantissa_vec = Vec::with_capacity(x_vec.len());
let mut exponent_vec = Vec::with_capacity(x_vec.len());
for val in x_vec.iter() {
if val.is_zero() {
mantissa_vec.push(T::zero());
exponent_vec.push(0);
} else if val.is_infinite() || val.is_nan() {
mantissa_vec.push(*val);
exponent_vec.push(0);
} else {
let log2_val = val.abs().log2();
let exp = log2_val.floor();
let exp_i32 = T::to_i32(&exp).expect("exponent should be convertible to i32") + 1;
let two = T::from(2.0).expect("2.0 should be representable");
let mantissa =
*val / two.powf(T::from(exp_i32).expect("exponent should be representable"));
mantissa_vec.push(mantissa);
exponent_vec.push(exp_i32);
}
}
(
Array::from_vec(mantissa_vec).reshape(&x.shape()),
Array::from_vec(exponent_vec).reshape(&x.shape()),
)
}
pub fn modf<T>(x: &Array<T>) -> (Array<T>, Array<T>)
where
T: Float + Clone,
{
let x_vec = x.to_vec();
let mut frac_vec = Vec::with_capacity(x_vec.len());
let mut int_vec = Vec::with_capacity(x_vec.len());
for val in x_vec.iter() {
let int_part = val.trunc();
let frac_part = *val - int_part;
frac_vec.push(frac_part);
int_vec.push(int_part);
}
(
Array::from_vec(frac_vec).reshape(&x.shape()),
Array::from_vec(int_vec).reshape(&x.shape()),
)
}
pub fn divmod<T>(x: &Array<T>, y: &Array<T>) -> Result<(Array<T>, Array<T>)>
where
T: Float + Clone,
{
if x.shape() != y.shape() {
return Err(NumRs2Error::DimensionMismatch(format!(
"Shape mismatch: x has shape {:?}, y has shape {:?}",
x.shape(),
y.shape()
)));
}
let x_vec = x.to_vec();
let y_vec = y.to_vec();
let mut quot_vec = Vec::with_capacity(x_vec.len());
let mut rem_vec = Vec::with_capacity(x_vec.len());
for (x_val, y_val) in x_vec.iter().zip(y_vec.iter()) {
let quotient = (*x_val / *y_val).floor();
let remainder = *x_val - quotient * *y_val;
quot_vec.push(quotient);
rem_vec.push(remainder);
}
Ok((
Array::from_vec(quot_vec).reshape(&x.shape()),
Array::from_vec(rem_vec).reshape(&y.shape()),
))
}
pub fn remainder<T>(x: &Array<T>, y: &Array<T>) -> Result<Array<T>>
where
T: Float + Clone,
{
if x.shape() != y.shape() {
return Err(NumRs2Error::DimensionMismatch(format!(
"Shape mismatch: x has shape {:?}, y has shape {:?}",
x.shape(),
y.shape()
)));
}
let x_vec = x.to_vec();
let y_vec = y.to_vec();
let mut result = Vec::with_capacity(x_vec.len());
for (x_val, y_val) in x_vec.iter().zip(y_vec.iter()) {
let n = (*x_val / *y_val).round();
let rem = *x_val - n * *y_val;
result.push(rem);
}
Ok(Array::from_vec(result).reshape(&x.shape()))
}
pub fn fmod<T>(x: &Array<T>, y: &Array<T>) -> Result<Array<T>>
where
T: Float + Clone,
{
if x.shape() != y.shape() {
return Err(NumRs2Error::DimensionMismatch(format!(
"Shape mismatch: x has shape {:?}, y has shape {:?}",
x.shape(),
y.shape()
)));
}
let x_vec = x.to_vec();
let y_vec = y.to_vec();
let mut result = Vec::with_capacity(x_vec.len());
for (x_val, y_val) in x_vec.iter().zip(y_vec.iter()) {
let n = (*x_val / *y_val).trunc();
let rem = *x_val - n * *y_val;
result.push(rem);
}
Ok(Array::from_vec(result).reshape(&x.shape()))
}
#[allow(unused_imports)]
use num_traits::PrimInt as _;