use super::helpers::{chi_squared_p_value, fit_ols};
use super::types::DiagnosticTestResult;
use crate::error::{Error, Result};
use crate::linalg::{vec_mean, Matrix};
pub fn jarque_bera_test(y: &[f64], x_vars: &[Vec<f64>]) -> Result<DiagnosticTestResult> {
let n = y.len();
let k = x_vars.len(); let p = k + 1;
if n <= p {
return Err(Error::InsufficientData {
required: p + 1,
available: n,
});
}
super::helpers::validate_regression_data(y, x_vars)?;
let mut x_data = vec![1.0; n * p];
for row in 0..n {
x_data[row * p] = 1.0; for (col, x_var) in x_vars.iter().enumerate() {
x_data[row * p + col + 1] = x_var[row];
}
}
let x_full = Matrix::new(n, p, x_data);
let beta = fit_ols(y, &x_full)?;
let predictions = x_full.mul_vec(&beta);
let residuals: Vec<f64> = y
.iter()
.zip(predictions.iter())
.map(|(&yi, &yi_hat)| yi - yi_hat)
.collect();
let mean = vec_mean(&residuals);
let variance: f64 = residuals
.iter()
.map(|&r| {
let diff = r - mean;
diff * diff
})
.sum::<f64>()
/ (n as f64);
if variance <= 0.0 || !variance.is_finite() {
return Err(Error::InvalidInput("Invalid residual variance".to_string()));
}
let std_dev = variance.sqrt();
let std_dev_cubed = std_dev * std_dev * std_dev;
let std_dev_fourth = std_dev_cubed * std_dev;
let skewness: f64 = residuals
.iter()
.map(|&r| {
let diff = r - mean;
diff * diff * diff
})
.sum::<f64>()
/ (n as f64 * std_dev_cubed);
let kurtosis: f64 = residuals
.iter()
.map(|&r| {
let diff = r - mean;
let diff_sq = diff * diff;
diff_sq * diff_sq
})
.sum::<f64>()
/ (n as f64 * std_dev_fourth);
let jb_stat = (n as f64) / 6.0 * (skewness * skewness + (kurtosis - 3.0).powi(2) / 4.0);
let df = 2.0;
let p_value = chi_squared_p_value(jb_stat, df);
let alpha = 0.05;
let passed = p_value > alpha;
let (interpretation, guidance) = if passed {
(
format!(
"p-value = {:.4} is greater than {:.2}. Cannot reject H0. No significant evidence that residuals deviate from normality.",
p_value, alpha
),
"The normality assumption appears to be met. Jarque-Bera test does not detect significant skewness or excess kurtosis."
)
} else {
(
format!(
"p-value = {:.4} is less than or equal to {:.2}. Reject H0. Significant evidence that residuals deviate from normality.",
p_value, alpha
),
"Consider transforming the dependent variable (e.g., log, Box-Cox transformation), using robust standard errors, or applying a different estimation method."
)
};
Ok(DiagnosticTestResult {
test_name: "Jarque-Bera Test for Normality".to_string(),
statistic: jb_stat,
p_value,
passed,
interpretation,
guidance: guidance.to_string(),
})
}