#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
use core::cmp::Ordering::Equal;
use core::iter::repeat_n;
use num_traits::Float;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BoundaryPolicy {
#[default]
Extend,
Reflect,
Zero,
NoBoundary,
}
impl BoundaryPolicy {
pub fn apply<T: Float>(
&self,
x: &[T],
y: &[T],
dimensions: usize,
window_size: usize,
) -> (Vec<T>, Vec<T>, Vec<usize>) {
let n = x.len() / dimensions;
let d = dimensions;
let mapping: Vec<usize> = (0..n).collect();
if d == 1 {
if *self == BoundaryPolicy::NoBoundary {
return (x.to_vec(), y.to_vec(), mapping);
}
let pad_len = (window_size / 2).min(n - 1);
if pad_len == 0 {
return (x.to_vec(), y.to_vec(), mapping);
}
let total_len = n + 2 * pad_len;
let mut px = Vec::with_capacity(total_len);
let mut py = Vec::with_capacity(total_len);
match self {
BoundaryPolicy::Extend => {
let x0 = x[0];
let y0 = y[0];
let dx = x[1] - x[0];
for i in (1..=pad_len).rev() {
px.push(x0 - T::from(i).unwrap() * dx);
py.push(y0);
}
}
BoundaryPolicy::Reflect => {
let x0 = x[0];
for i in (1..=pad_len).rev() {
px.push(x0 - (x[i] - x0));
py.push(y[i]);
}
}
BoundaryPolicy::Zero => {
let x0 = x[0];
let dx = x[1] - x[0];
for i in (1..=pad_len).rev() {
px.push(x0 - T::from(i).unwrap() * dx);
py.push(T::zero());
}
}
BoundaryPolicy::NoBoundary => unreachable!(),
}
px.extend_from_slice(x);
py.extend_from_slice(y);
match self {
BoundaryPolicy::Extend => {
let xn = x[n - 1];
let yn = y[n - 1];
let dx = x[n - 1] - x[n - 2];
for i in 1..=pad_len {
px.push(xn + T::from(i).unwrap() * dx);
py.push(yn);
}
}
BoundaryPolicy::Reflect => {
let xn = x[n - 1];
for i in 1..=pad_len {
px.push(xn + (xn - x[n - 1 - i]));
py.push(y[n - 1 - i]);
}
}
BoundaryPolicy::Zero => {
let xn = x[n - 1];
let dx = x[n - 1] - x[n - 2];
for i in 1..=pad_len {
px.push(xn + T::from(i).unwrap() * dx);
py.push(T::zero());
}
}
BoundaryPolicy::NoBoundary => unreachable!(),
}
let n_padded = px.len();
let mut full_mapping = Vec::with_capacity(n_padded);
full_mapping.extend(repeat_n(0, pad_len));
full_mapping.extend(0..n);
full_mapping.extend(repeat_n(n - 1, n_padded - n - pad_len));
return (px, py, full_mapping);
}
if d > 1 {
if *self == BoundaryPolicy::NoBoundary || *self == BoundaryPolicy::Extend {
return (x.to_vec(), y.to_vec(), mapping);
}
let mut px = x.to_vec();
let mut py = y.to_vec();
let mut p_mapping = mapping;
let pad_count = (window_size / 2).min(n / 2).max(1);
for j in 0..d {
let mut indices: Vec<usize> = (0..n).collect();
indices.sort_by(|&a, &b| x[a * d + j].partial_cmp(&x[b * d + j]).unwrap_or(Equal));
let mut add_padding = |idx_list: &[usize], is_min: bool| {
let boundary_val = if is_min {
x[indices[0] * d + j]
} else {
x[indices[n - 1] * d + j]
};
for &idx in idx_list {
if (x[idx * d + j] - boundary_val).abs() <= T::epsilon() {
continue;
}
let mut new_point = vec![T::zero(); d];
for k in 0..d {
if k == j {
if is_min {
new_point[k] = boundary_val - (x[idx * d + j] - boundary_val);
} else {
new_point[k] = boundary_val + (boundary_val - x[idx * d + j]);
}
} else {
new_point[k] = x[idx * d + k];
}
}
px.extend_from_slice(&new_point);
let y_val = match self {
BoundaryPolicy::Zero => T::zero(),
BoundaryPolicy::Reflect => y[idx],
_ => unreachable!(),
};
py.push(y_val);
p_mapping.push(idx);
}
};
let min_indices: Vec<usize> = indices.iter().take(pad_count).copied().collect();
add_padding(&min_indices, true);
let max_indices: Vec<usize> = indices
.iter()
.skip(n - pad_count)
.take(pad_count)
.copied()
.collect();
add_padding(&max_indices, false);
}
return (px, py, p_mapping);
}
(x.to_vec(), y.to_vec(), mapping)
}
}