use crate::error::{CoreError, CoreResult, ErrorContext};
use ::ndarray::{Array, Array1, Array2, ArrayBase, Data, Dimension};
use num_traits::{Float, FromPrimitive, Num, NumCast};
use std::fmt::Debug;
#[inline]
pub fn const_f64<F: Float + NumCast>(value: f64) -> F {
F::from(value).expect("Failed to convert constant to target float type")
}
#[must_use]
#[allow(dead_code)]
pub fn is_close<F: Float>(a: F, b: F, abs_tol: F, reltol: F) -> bool {
let abs_diff = (a - b).abs();
if abs_diff <= abs_tol {
true
} else {
let abs_a = a.abs();
let abs_b = b.abs();
let max_abs = if abs_a > abs_b { abs_a } else { abs_b };
abs_diff <= max_abs * reltol
}
}
#[must_use]
#[allow(dead_code)]
pub fn points_equal<T>(point1: &[T], point2: &[T], tol: Option<T>) -> bool
where
T: PartialOrd + std::ops::Sub<Output = T> + Copy + FromPrimitive + num_traits::Zero,
{
if point1.is_empty() || point2.is_empty() {
return point1.is_empty() && point2.is_empty();
}
let tol = match tol {
Some(t) => t,
None => match T::from_f64(1e-8) {
Some(t) => t,
None => {
T::from_f64(0.0).unwrap_or_else(|| {
T::zero()
})
}
},
};
point1.len() == point2.len()
&& point1.iter().zip(point2.iter()).all(|(&a, &b)| {
let diff = if a > b { a - b } else { b - a };
diff <= tol
})
}
#[must_use]
#[allow(dead_code)]
pub fn arrays_equal<S1, S2, D, T>(
array1: &ArrayBase<S1, D>,
array2: &ArrayBase<S2, D>,
tol: Option<T>,
) -> bool
where
S1: Data<Elem = T>,
S2: Data<Elem = T>,
D: Dimension,
T: PartialOrd + std::ops::Sub<Output = T> + Copy + FromPrimitive + num_traits::Zero,
{
if array1.shape() != array2.shape() {
return false;
}
let points1: Vec<T> = array1.iter().copied().collect();
let points2: Vec<T> = array2.iter().copied().collect();
points_equal(&points1, &points2, tol)
}
#[must_use]
#[allow(dead_code)]
pub fn fill_diagonal<T: Clone>(mut a: Array2<T>, val: T) -> Array2<T> {
let min_dim = a.nrows().min(a.ncols());
for i in 0..min_dim {
a[[i, i]] = val.clone();
}
a
}
#[must_use]
#[allow(dead_code)]
pub fn prod<I, T>(iter: I) -> T
where
I: IntoIterator<Item = T>,
T: std::ops::Mul<Output = T> + From<u8>,
{
iter.into_iter().fold(T::from(1), |a, b| a * b)
}
#[allow(dead_code)]
pub fn arange<F: Float + std::iter::Sum>(start: F, end: F, step: F) -> CoreResult<Vec<F>> {
if step == F::zero() {
return Err(CoreError::ValueError(ErrorContext::new(
"Step size cannot be zero".to_string(),
)));
}
let mut result = Vec::new();
let mut current = start;
if step > F::zero() {
while current < end {
result.push(current);
current = current + step;
}
} else {
while current > end {
result.push(current);
current = current + step;
}
}
Ok(result)
}
#[must_use]
#[allow(dead_code)]
pub fn arange_unchecked<F: Float + std::iter::Sum>(start: F, end: F, step: F) -> Vec<F> {
arange(start, end, step).expect("Operation failed")
}
#[must_use]
#[allow(dead_code)]
pub fn all<I, T, F>(iter: I, predicate: F) -> bool
where
I: IntoIterator<Item = T>,
F: Fn(T) -> bool,
{
iter.into_iter().all(predicate)
}
#[must_use]
#[allow(dead_code)]
pub fn any<I, T, F>(iter: I, predicate: F) -> bool
where
I: IntoIterator<Item = T>,
F: Fn(T) -> bool,
{
iter.into_iter().any(predicate)
}
#[must_use]
#[allow(dead_code)]
pub fn linspace<F: Float + std::iter::Sum + Send + Sync>(
start: F,
end: F,
num: usize,
) -> Array1<F> {
if num < 2 {
return Array::from_vec(vec![start]);
}
#[cfg(feature = "parallel")]
{
if num >= 1000 {
use crate::parallel_ops::*;
let step = (end - start) / F::from(num - 1).expect("Failed to convert to float");
let result: Vec<F> = (0..num)
.into_par_iter()
.map(|i| {
if i == num - 1 {
end
} else {
start + step * F::from(i).expect("Failed to convert to float")
}
})
.collect::<Vec<F>>();
return Array::from_vec(result);
}
}
let step = (end - start) / F::from(num - 1).expect("Failed to convert to float");
let mut result = Vec::with_capacity(num);
for i in 0..num {
let value = start + step * F::from(i).expect("Failed to convert to float");
result.push(value);
}
if let Some(last) = result.last_mut() {
*last = end;
}
Array::from_vec(result)
}
#[must_use]
#[allow(dead_code)]
pub fn logspace<F: Float + std::iter::Sum + Send + Sync>(
start: F,
end: F,
num: usize,
base: Option<F>,
) -> Array1<F> {
let base = base.unwrap_or_else(|| F::from(10.0).expect("Failed to convert constant to float"));
let linear = linspace(start, end, num);
linear.mapv(|x| base.powf(x))
}
#[must_use]
#[allow(dead_code)]
pub fn maximum<S1, S2, D, T>(
a: &crate::ndarray::ArrayBase<S1, D>,
b: &crate::ndarray::ArrayBase<S2, D>,
) -> Array<T, D>
where
S1: crate::ndarray::Data<Elem = T>,
S2: crate::ndarray::Data<Elem = T>,
D: Dimension,
T: Num + PartialOrd + Copy + Send + Sync,
{
assert_eq!(
a.shape(),
b.shape(),
"Arrays must have the same shape for element-wise maximum"
);
#[cfg(feature = "parallel")]
{
if a.len() > 1000 {
use crate::parallel_ops::*;
let (a_vec_, _) = a.to_owned().into_raw_vec_and_offset();
let (b_vec_, _) = b.to_owned().into_raw_vec_and_offset();
let result_vec: Vec<T> = a_vec_
.into_par_iter()
.zip(b_vec_.into_par_iter())
.map(|(a_val, b_val)| if b_val > a_val { b_val } else { a_val })
.collect();
return Array::from_shape_vec(a.raw_dim(), result_vec)
.expect("Shape mismatch in parallel maximum");
}
}
let mut result = a.to_owned();
for (i, elem) in result.iter_mut().enumerate() {
if let Some(b_slice) = b.as_slice() {
let b_val = b_slice[i];
if b_val > *elem {
*elem = b_val;
}
} else {
let b_val = b.iter().nth(i).expect("Operation failed");
if *b_val > *elem {
*elem = *b_val;
}
}
}
result
}
#[must_use]
#[allow(dead_code)]
pub fn minimum<S1, S2, D, T>(
a: &crate::ndarray::ArrayBase<S1, D>,
b: &crate::ndarray::ArrayBase<S2, D>,
) -> Array<T, D>
where
S1: crate::ndarray::Data<Elem = T>,
S2: crate::ndarray::Data<Elem = T>,
D: Dimension,
T: Num + PartialOrd + Copy + Send + Sync,
{
assert_eq!(
a.shape(),
b.shape(),
"Arrays must have the same shape for element-wise minimum"
);
#[cfg(feature = "parallel")]
{
if a.len() > 1000 {
use crate::parallel_ops::*;
let (a_vec_, _) = a.to_owned().into_raw_vec_and_offset();
let (b_vec_, _) = b.to_owned().into_raw_vec_and_offset();
let result_vec: Vec<T> = a_vec_
.into_par_iter()
.zip(b_vec_.into_par_iter())
.map(|(a_val, b_val)| if b_val < a_val { b_val } else { a_val })
.collect();
return Array::from_shape_vec(a.raw_dim(), result_vec)
.expect("Shape mismatch in parallel minimum");
}
}
let mut result = a.to_owned();
for (i, elem) in result.iter_mut().enumerate() {
if let Some(b_slice) = b.as_slice() {
let b_val = b_slice[i];
if b_val < *elem {
*elem = b_val;
}
} else {
let b_val = b.iter().nth(i).expect("Operation failed");
if *b_val < *elem {
*elem = *b_val;
}
}
}
result
}
#[allow(dead_code)]
pub fn normalize<T>(x: &[T], norm: &str) -> Result<Vec<f64>, &'static str>
where
T: Float + NumCast + Debug,
{
if x.is_empty() {
return Err("Input signal is empty");
}
let x_f64: Vec<f64> = x
.iter()
.map(|&val| NumCast::from(val).ok_or("Could not convert value to f64"))
.collect::<Result<Vec<_>, _>>()?;
match norm.to_lowercase().as_str() {
"energy" => {
let sum_of_squares: f64 = x_f64.iter().map(|&x| x * x).sum();
if sum_of_squares.abs() < f64::EPSILON {
return Err("Signal has zero energy, cannot normalize");
}
let scale = 1.0 / sum_of_squares.sqrt();
let normalized = x_f64.iter().map(|&x| x * scale).collect();
Ok(normalized)
}
"peak" => {
let peak = x_f64.iter().fold(0.0, |a, &b| a.max(b.abs()));
if peak.abs() < f64::EPSILON {
return Err("Signal has zero peak, cannot normalize");
}
let scale = 1.0 / peak;
let normalized = x_f64.iter().map(|&x| x * scale).collect();
Ok(normalized)
}
"sum" => {
let sum: f64 = x_f64.iter().sum();
if sum.abs() < f64::EPSILON {
return Err("Signal has zero sum, cannot normalize");
}
let scale = 1.0 / sum;
let normalized = x_f64.iter().map(|&x| x * scale).collect();
Ok(normalized)
}
"max" => {
let max_val = x_f64.iter().fold(0.0, |a, &b| a.max(b.abs()));
if max_val.abs() < f64::EPSILON {
return Err("Signal has zero maximum, cannot normalize");
}
let scale = 1.0 / max_val;
let normalized = x_f64.iter().map(|&x| x * scale).collect();
Ok(normalized)
}
_ => Err("Unknown normalization type. Supported types: 'energy', 'peak', 'sum', 'max'"),
}
}
#[allow(dead_code)]
pub fn pad_array<T, D>(
input: &Array<T, D>,
pad_width: &[(usize, usize)],
mode: &str,
constant_value: Option<T>,
) -> Result<Array<T, D>, String>
where
T: Float + FromPrimitive + Debug + Clone,
D: Dimension,
{
if input.ndim() == 0 {
return Err("Input array cannot be 0-dimensional".to_string());
}
if pad_width.len() != input.ndim() {
return Err(format!(
"Pad _width must have same length as input dimensions (got {} expected {})",
pad_width.len(),
input.ndim()
));
}
if pad_width.iter().all(|&(a, b)| a == 0 && b == 0) {
return Ok(input.to_owned());
}
let mut newshape = Vec::with_capacity(input.ndim());
for (dim, &(pad_before, pad_after)) in pad_width.iter().enumerate().take(input.ndim()) {
newshape.push(input.shape()[dim] + pad_before + pad_after);
}
let const_val = constant_value.unwrap_or_else(|| T::zero());
let mut output = Array::<T, D>::from_elem(
D::from_dimension(&crate::ndarray::IxDyn(&newshape))
.expect("Could not create dimension from shape"),
const_val,
);
if input.ndim() == 1 {
let inputarray1 = input
.view()
.into_dimensionality::<crate::ndarray::Ix1>()
.map_err(|_| "Failed to convert to 1D array".to_string())?;
let mut output_array1 = output
.view_mut()
.into_dimensionality::<crate::ndarray::Ix1>()
.map_err(|_| "Failed to convert output to 1D array".to_string())?;
let input_len = inputarray1.len();
let start = pad_width[0].0;
for i in 0..input_len {
output_array1[start + i] = inputarray1[i];
}
match mode.to_lowercase().as_str() {
"constant" => {
}
"edge" => {
for i in 0..pad_width[0].0 {
output_array1[i] = inputarray1[0];
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
output_array1[offset + i] = inputarray1[input_len - 1];
}
}
"reflect" => {
for i in 0..pad_width[0].0 {
let src_idx = pad_width[0].0 - i;
if src_idx < input_len {
output_array1[i] = inputarray1[src_idx];
}
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
let src_idx = input_len - 2 - i;
if src_idx < input_len {
output_array1[offset + i] = inputarray1[src_idx];
}
}
}
"wrap" => {
for i in 0..pad_width[0].0 {
let src_idx = (input_len - (pad_width[0].0 - i) % input_len) % input_len;
output_array1[i] = inputarray1[src_idx];
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
let src_idx = i % input_len;
output_array1[offset + i] = inputarray1[src_idx];
}
}
"maximum" => {
let max_val = inputarray1.iter().fold(T::neg_infinity(), |a, &b| a.max(b));
for i in 0..pad_width[0].0 {
output_array1[i] = max_val;
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
output_array1[offset + i] = max_val;
}
}
"minimum" => {
let min_val = inputarray1.iter().fold(T::infinity(), |a, &b| a.min(b));
for i in 0..pad_width[0].0 {
output_array1[i] = min_val;
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
output_array1[offset + i] = min_val;
}
}
"mean" => {
let sum = inputarray1.iter().fold(T::zero(), |a, &b| a + b);
let mean_val = sum / T::from_usize(input_len).expect("Operation failed");
for i in 0..pad_width[0].0 {
output_array1[i] = mean_val;
}
let offset = start + input_len;
for i in 0..pad_width[0].1 {
output_array1[offset + i] = mean_val;
}
}
_ => return Err(format!("Unsupported padding mode: {mode}")),
}
return Ok(output);
}
if mode.to_lowercase() != "constant" {
return Err(format!(
"Padding mode '{mode}' is not yet implemented for arrays with more than 1 dimension"
));
}
Ok(output)
}
#[allow(dead_code)]
pub fn generate_window(
window_type: &str,
length: usize,
periodic: bool,
) -> Result<Vec<f64>, String> {
if length == 0 {
return Err("Window length must be positive".to_string());
}
let mut window = Vec::with_capacity(length);
let n = if periodic { length + 1 } else { length };
match window_type.to_lowercase().as_str() {
"hamming" => {
for i in 0..length {
let w =
0.54 - 0.46 * (2.0 * std::f64::consts::PI * i as f64 / (n - 1) as f64).cos();
window.push(w);
}
}
"hanning" | "hann" => {
for i in 0..length {
let w =
0.5 * (1.0 - (2.0 * std::f64::consts::PI * i as f64 / (n - 1) as f64).cos());
window.push(w);
}
}
"blackman" => {
for i in 0..length {
let w = 0.42 - 0.5 * (2.0 * std::f64::consts::PI * i as f64 / (n - 1) as f64).cos()
+ 0.08 * (4.0 * std::f64::consts::PI * i as f64 / (n - 1) as f64).cos();
window.push(w);
}
}
"bartlett" => {
let m = (n - 1) as f64 / 2.0;
for i in 0..length {
let w = 1.0 - ((i as f64 - m) / m).abs();
window.push(w);
}
}
"boxcar" | "rectangular" => {
window.extend(std::iter::repeat_n(1.0, length));
}
"triang" => {
let m = (length - 1) as f64 / 2.0;
for i in 0..length {
let w = 1.0 - ((i as f64 - m) / (m + 1.0)).abs();
window.push(w);
}
}
_ => {
return Err(format!("Unknown window type: {window_type}"));
}
}
Ok(window)
}
#[allow(dead_code)]
pub fn get_window(window_type: &str, length: usize, periodic: bool) -> CoreResult<Vec<f64>> {
generate_window(window_type, length, periodic)
.map_err(|e| CoreError::ValueError(ErrorContext::new(e)))
}
#[allow(dead_code)]
pub fn differentiate<F, Func>(x: F, h: F, evalfn: Func) -> Result<F, String>
where
F: Float + FromPrimitive + Debug,
Func: Fn(F) -> Result<F, String>,
{
let f_plus = evalfn(x + h).map_err(|e| format!("Error evaluating function at x+h: {e}"))?;
let f_minus = evalfn(x - h).map_err(|e| format!("Error evaluating function at x-h: {e}"))?;
let derivative =
(f_plus - f_minus) / (F::from(2.0).expect("Failed to convert constant to float") * h);
Ok(derivative)
}
#[allow(dead_code)]
pub fn integrate<F, Func>(a: F, b: F, n: usize, evalfn: Func) -> Result<F, String>
where
F: Float + FromPrimitive + Debug,
Func: Fn(F) -> Result<F, String>,
{
if a > b {
return integrate(b, a, n, evalfn).map(|result| -result);
}
if n < 2 {
return Err("number of intervals must be at least 2".to_string());
}
if n % 2 != 0 {
return Err("number of intervals must be even".to_string());
}
let h = (b - a) / F::from_usize(n).expect("Operation failed");
let mut sum = evalfn(a).map_err(|e| format!("Error evaluating function at a: {e}"))?
+ evalfn(b).map_err(|e| format!("Error evaluating function at b: {e}"))?;
for i in 1..n {
if i % 2 == 0 {
let x_i = a + F::from_usize(i).expect("Operation failed") * h;
sum = sum
+ F::from(2.0).expect("Failed to convert constant to float")
* evalfn(x_i)
.map_err(|e| format!("Error evaluating function at x_{i}: {e}"))?;
}
}
for i in 1..n {
if i % 2 == 1 {
let x_i = a + F::from_usize(i).expect("Operation failed") * h;
sum = sum
+ F::from(4.0).expect("Failed to convert constant to float")
* evalfn(x_i)
.map_err(|e| format!("Error evaluating function at x_{i}: {e}"))?;
}
}
let integral = h * sum / F::from(3.0).expect("Failed to convert constant to float");
Ok(integral)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use ndarray::{arr1, array};
#[test]
fn test_is_close() {
assert!(is_close(1.0, 1.0, 1e-10, 1e-10));
assert!(is_close(1.0, 1.0 + 1e-11, 1e-10, 1e-10));
assert!(!is_close(1.0, 1.1, 1e-10, 1e-10));
assert!(is_close(1e10, 1e10 + 1.0, 1e-10, 1e-9));
}
#[test]
fn test_fill_diagonal() {
let a = Array2::<f64>::zeros((3, 3));
let a_diag = fill_diagonal(a, 5.0);
assert_relative_eq!(a_diag[[0, 0]], 5.0);
assert_relative_eq!(a_diag[[1, 1]], 5.0);
assert_relative_eq!(a_diag[[2, 2]], 5.0);
assert_relative_eq!(a_diag[[0, 1]], 0.0);
}
#[test]
fn test_prod() {
assert_eq!(prod(vec![1, 2, 3, 4]), 24);
assert_eq!(prod(vec![2.0, 3.0, 4.0]), 24.0);
}
#[test]
fn test_arange() {
let result = arange(0.0, 5.0, 1.0).expect("Operation failed");
assert_eq!(result, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
let result = arange(5.0, 0.0, -1.0).expect("Operation failed");
assert_eq!(result, vec![5.0, 4.0, 3.0, 2.0, 1.0]);
}
#[test]
fn test_arange_zero_step() {
let result = arange(0.0, 5.0, 0.0);
assert!(result.is_err());
}
#[test]
fn test_all() {
assert!(all(vec![2, 4, 6, 8], |x| x % 2 == 0));
assert!(!all(vec![2, 4, 5, 8], |x| x % 2 == 0));
}
#[test]
fn test_any() {
assert!(any(vec![1, 2, 3, 4], |x| x % 2 == 0));
assert!(!any(vec![1, 3, 5, 7], |x| x % 2 == 0));
}
#[test]
fn test_linspace() {
let result = linspace(0.0, 1.0, 5);
let expected = arr1(&[0.0, 0.25, 0.5, 0.75, 1.0]);
assert_eq!(result.len(), 5);
for (a, b) in result.iter().zip(expected.iter()) {
assert_relative_eq!(a, b, epsilon = 1e-14);
}
let result = linspace(5.0, 5.0, 1);
assert_eq!(result.len(), 1);
assert_relative_eq!(result[0], 5.0);
let result = linspace(-10.0, 10.0, 5);
assert_relative_eq!(result[0], -10.0);
assert_relative_eq!(result[4], 10.0);
}
#[test]
fn testlogspace() {
let result = logspace(0.0, 3.0, 4, None);
let expected = arr1(&[1.0, 10.0, 100.0, 1000.0]);
assert_eq!(result.len(), 4);
for (a, b) in result.iter().zip(expected.iter()) {
assert_relative_eq!(a, b, epsilon = 1e-10);
}
let result = logspace(0.0, 3.0, 4, Some(2.0));
let expected = arr1(&[1.0, 2.0, 4.0, 8.0]);
for (a, b) in result.iter().zip(expected.iter()) {
assert_relative_eq!(a, b, epsilon = 1e-10);
}
}
#[test]
fn test_maximum() {
let a = array![[1, 2], [3, 4]];
let b = array![[5, 1], [7, 2]];
let result = maximum(&a, &b);
let expected = array![[5, 2], [7, 4]];
assert_eq!(result, expected);
}
#[test]
fn test_minimum() {
let a = array![[1, 2], [3, 4]];
let b = array![[5, 1], [7, 2]];
let result = minimum(&a, &b);
let expected = array![[1, 1], [3, 2]];
assert_eq!(result, expected);
}
#[test]
#[should_panic]
fn test_maximum_differentshapes() {
let a = array![[1, 2], [3, 4]];
let b = array![[5, 1, 2], [7, 2, 3]];
let result = maximum(&a, &b);
}
#[test]
fn test_points_equal() {
let point1 = [1.0, 2.0, 3.0];
let point2 = [1.0, 2.0, 3.0];
let point3 = [1.0, 2.0, 3.001];
let point4 = [1.0, 2.0, 4.0];
assert!(points_equal(&point1, &point2, None));
assert!(!points_equal(&point1, &point3, None));
assert!(points_equal(&point1, &point3, Some(0.01)));
assert!(!points_equal(&point1, &point4, Some(0.01)));
}
#[test]
fn test_arrays_equal() {
let arr1 = array![[1.0, 2.0], [3.0, 4.0]];
let arr2 = array![[1.0, 2.0], [3.0, 4.0]];
let arr3 = array![[1.0, 2.0], [3.0, 4.001]];
assert!(arrays_equal(&arr1, &arr2, None));
assert!(!arrays_equal(&arr1, &arr3, None));
assert!(arrays_equal(&arr1, &arr3, Some(0.01)));
}
#[test]
fn test_normalize() {
let signal = vec![1.0, 2.0, 3.0, 4.0];
let normalized = normalize(&signal, "energy").expect("Operation failed");
let sum_of_squares: f64 = normalized.iter().map(|&x| x * x).sum();
assert_relative_eq!(sum_of_squares, 1.0, epsilon = 1e-10);
let signal = vec![1.0, -2.0, 3.0, -4.0];
let normalized = normalize(&signal, "peak").expect("Operation failed");
let peak = normalized.iter().fold(0.0, |a, &b| a.max(b.abs()));
assert_relative_eq!(peak, 1.0, epsilon = 1e-10);
assert_relative_eq!(normalized[3], -1.0, epsilon = 1e-10);
let signal = vec![1.0, 2.0, 3.0, 4.0];
let normalized = normalize(&signal, "sum").expect("Operation failed");
let sum: f64 = normalized.iter().sum();
assert_relative_eq!(sum, 1.0, epsilon = 1e-10);
}
#[test]
fn test_pad_array() {
let arr = array![1.0, 2.0, 3.0];
let padded = pad_array(&arr, &[(1, 2)], "constant", Some(0.0)).expect("Operation failed");
assert_eq!(padded.shape(), &[6]);
assert_eq!(padded, array![0.0, 1.0, 2.0, 3.0, 0.0, 0.0]);
let arr = array![1.0, 2.0, 3.0];
let padded = pad_array(&arr, &[(2, 2)], "edge", None).expect("Operation failed");
assert_eq!(padded.shape(), &[7]);
assert_eq!(padded, array![1.0, 1.0, 1.0, 2.0, 3.0, 3.0, 3.0]);
let arr = array![1.0, 2.0, 3.0];
let padded = pad_array(&arr, &[(1, 1)], "maximum", None).expect("Operation failed");
assert_eq!(padded.shape(), &[5]);
assert_eq!(padded, array![3.0, 1.0, 2.0, 3.0, 3.0]);
}
#[test]
fn test_get_window() {
let window = get_window("hamming", 5, false).expect("Operation failed");
assert_eq!(window.len(), 5);
assert!(window[0] > 0.0 && window[0] < 0.6); assert!(window[2] > 0.9);
let window = get_window("hann", 5, false).expect("Operation failed");
assert_eq!(window.len(), 5);
assert!((window[0] - 0.0).abs() < 1e-10);
assert!(window[2] > 0.9);
let window = get_window("rectangular", 5, false).expect("Operation failed");
assert_eq!(window.len(), 5);
assert!(window.iter().all(|&x| (x - 1.0).abs() < 1e-10));
}
#[test]
fn test_differentiate_integrate() {
let f = |x: f64| -> Result<f64, String> { Ok(x * x) };
let derivative = differentiate(3.0, 0.001, f).expect("Operation failed");
assert_relative_eq!(derivative, 6.0, epsilon = 1e-3);
let integral = integrate(0.0, 1.0, 100, f).expect("Operation failed");
assert_relative_eq!(integral, 1.0 / 3.0, epsilon = 1e-5);
let integral = integrate(0.0, 2.0, 100, f).expect("Operation failed");
assert_relative_eq!(integral, 8.0 / 3.0, epsilon = 1e-5); }
}