use crate::common::{StatError, TailType, TestResult, calculate_ci, calculate_p};
use statrs::distribution::Normal;
pub fn z_test_ind<I1, I2, T>(
data1: I1,
data2: I2,
tail: TailType,
alpha: f64,
pooled: bool,
) -> Result<TestResult, StatError>
where
I1: IntoIterator<Item = T>,
I2: IntoIterator<Item = T>,
T: Into<f64>,
{
let sample1: Vec<f64> = data1.into_iter().map(|x| x.into()).collect();
let sample2: Vec<f64> = data2.into_iter().map(|x| x.into()).collect();
if sample1.is_empty() || sample2.is_empty() {
return Err(StatError::EmptyData);
}
let n1 = sample1.len() as f64;
let n2 = sample2.len() as f64;
let successes1: f64 = sample1.iter().sum();
let successes2: f64 = sample2.iter().sum();
let p1 = successes1 / n1;
let p2 = successes2 / n2;
let std_error = if pooled {
let pooled_p = (successes1 + successes2) / (n1 + n2);
(pooled_p * (1.0 - pooled_p) * (1.0 / n1 + 1.0 / n2)).sqrt()
} else {
((p1 * (1.0 - p1) / n1) + (p2 * (1.0 - p2) / n2)).sqrt()
};
if std_error == 0.0 {
return Err(StatError::ComputeError(
"Standard error is zero; cannot compute test statistic".to_string(),
));
}
let test_statistic = (p1 - p2) / std_error;
let z_dist = Normal::new(0.0, 1.0).map_err(|e| {
StatError::ComputeError(format!("Failed to create Normal distribution: {e}"))
})?;
let p_value = calculate_p(test_statistic, tail.clone(), &z_dist);
let confidence_interval = calculate_ci(p1 - p2, std_error, alpha, &z_dist);
let reject_null = p_value < alpha;
let null_hypothesis = match tail {
TailType::Left => "H0: p1 >= p2".to_string(),
TailType::Right => "H0: p1 <= p2".to_string(),
TailType::Two => "H0: p1 = p2".to_string(),
};
let alt_hypothesis = match tail {
TailType::Left => "Ha: p1 < p2".to_string(),
TailType::Right => "Ha: p1 > p2".to_string(),
TailType::Two => "Ha: p1 ≠ p2".to_string(),
};
Ok(TestResult {
test_statistic,
p_value,
confidence_interval,
null_hypothesis,
alt_hypothesis,
reject_null,
})
}