use super::traits::{FloatOps, NumericOps};
pub(super) fn relu_cpu<T: FloatOps>(data: &[T]) -> Vec<T> {
data.iter().map(|&x| x.max(T::zero())).collect()
}
pub(super) fn relu_backward_cpu<T: FloatOps>(grad: &[T], input: &[T]) -> Vec<T> {
grad.iter()
.zip(input.iter())
.map(|(&g, &x)| {
if x.partial_lt(T::zero()) || x.to_f64() == 0.0 {
T::zero()
} else {
g
}
})
.collect()
}
pub(super) fn sigmoid_cpu<T: FloatOps>(data: &[T]) -> Vec<T> {
data.iter()
.map(|&x| {
let exp_neg_x = x.neg().exp();
T::one().div(T::one().add(exp_neg_x))
})
.collect()
}
pub(super) fn sigmoid_backward_cpu<T: FloatOps>(grad: &[T], input: &[T]) -> Vec<T> {
grad.iter()
.zip(input.iter())
.map(|(&g, &x)| {
let exp_neg_x = x.neg().exp();
let sig = T::one().div(T::one().add(exp_neg_x));
g.mul(sig.mul(T::one().sub(sig)))
})
.collect()
}
pub(super) fn tanh_cpu<T: FloatOps>(data: &[T]) -> Vec<T> {
data.iter().map(|&x| x.tanh_val()).collect()
}
pub(super) fn tanh_backward_cpu<T: FloatOps>(grad: &[T], input: &[T]) -> Vec<T> {
grad.iter()
.zip(input.iter())
.map(|(&g, &x)| {
let t = x.tanh_val();
g.mul(T::one().sub(t.mul(t)))
})
.collect()
}
pub(super) fn gelu_cpu<T: FloatOps>(data: &[T]) -> Vec<T> {
let sqrt2 = T::from_f64(std::f64::consts::SQRT_2);
let half = T::from_f64(0.5);
data.iter()
.map(|&x| x.mul(half.mul(T::one().add(x.div(sqrt2).erf()))))
.collect()
}
pub(super) fn gelu_backward_cpu<T: FloatOps>(grad: &[T], input: &[T]) -> Vec<T> {
let sqrt2 = T::from_f64(std::f64::consts::SQRT_2);
let half = T::from_f64(0.5);
let inv_sqrt2pi = T::from_f64(1.0 / (2.0 * std::f64::consts::PI).sqrt());
grad.iter()
.zip(input.iter())
.map(|(&g, &x)| {
let cdf = half.mul(T::one().add(x.div(sqrt2).erf()));
let neg_x_sq_half = x.mul(x).mul(half).neg();
let pdf = inv_sqrt2pi.mul(neg_x_sq_half.exp());
g.mul(cdf.add(x.mul(pdf)))
})
.collect()
}