use crate::error::{Error, Result};
pub fn polynomial_features(
x: &[f64],
degree: usize,
center: bool,
x_mean: f64,
) -> Result<Vec<Vec<f64>>> {
if degree < 1 {
return Err(Error::InvalidInput(
"Polynomial degree must be at least 1".into(),
));
}
let n = x.len();
if n < 2 {
return Err(Error::InsufficientData {
required: 2,
available: n,
});
}
for (i, &xi) in x.iter().enumerate() {
if !xi.is_finite() {
return Err(Error::InvalidInput(format!(
"x[{}] is not finite: {}",
i, xi
)));
}
}
let x_centered: Vec<f64> = if center {
x.iter().map(|&xi| xi - x_mean).collect()
} else {
x.to_vec()
};
let mut features = Vec::with_capacity(degree.saturating_sub(1));
for d in 2..=degree {
let power = d as i32;
let poly_feature: Vec<f64> = x_centered.iter().map(|&xi| xi.powi(power)).collect();
for (i, &val) in poly_feature.iter().enumerate() {
if !val.is_finite() {
return Err(Error::InvalidInput(format!(
"x^{} at index {} is not finite: {}",
d, i, val
)));
}
}
features.push(poly_feature);
}
Ok(features)
}
pub fn polynomial_feature_names(degree: usize, centered: bool) -> Vec<String> {
let mut names = Vec::with_capacity(degree);
if centered {
names.push("x_centered".to_string());
for d in 2..=degree {
names.push(format!("x^{}_centered", d));
}
} else {
names.push("x".to_string());
for d in 2..=degree {
names.push(format!("x^{}", d));
}
}
names
}
pub fn compute_mean(x: &[f64]) -> f64 {
x.iter().sum::<f64>() / x.len() as f64
}
pub fn compute_std(x: &[f64], x_mean: f64) -> f64 {
let variance = x
.iter()
.map(|&xi| (xi - x_mean).powi(2))
.sum::<f64>()
/ (x.len() - 1) as f64;
variance.sqrt()
}