#![cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
use super::domain::check_domain;
use crate::core;
use crate::error::{error_to_json, Result};
#[wasm_bindgen]
pub fn test() -> String {
if let Err(e) = check_domain() {
return error_to_json(&e);
}
"Rust WASM is working!".to_string()
}
#[wasm_bindgen]
pub fn get_version() -> String {
if let Err(e) = check_domain() {
return error_to_json(&e);
}
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen]
pub fn test_t_critical(df: f64, alpha: f64) -> String {
if let Err(e) = check_domain() {
return error_to_json(&e);
}
let t_crit = core::t_critical_quantile(df, alpha);
format!(
r#"{{"df": {}, "alpha": {}, "t_critical": {}}}"#,
df, alpha, t_crit
)
}
#[wasm_bindgen]
pub fn test_ci(coef: f64, se: f64, df: f64, alpha: f64) -> String {
if let Err(e) = check_domain() {
return error_to_json(&e);
}
let t_crit = core::t_critical_quantile(df, alpha);
format!(
r#"{{"lower": {}, "upper": {}}}"#,
coef - t_crit * se,
coef + t_crit * se
)
}
#[wasm_bindgen]
pub fn test_r_accuracy() -> String {
if let Err(e) = check_domain() {
return error_to_json(&e);
}
format!(
r#"{{"two_tail_p": {}, "qt_975": {}}}"#,
core::two_tailed_p_value(1.6717, 21.0),
core::t_critical_quantile(21.0, 0.05)
)
}
#[wasm_bindgen]
pub fn test_housing_regression() -> String {
if let Err(e) = check_domain() {
return error_to_json(&e);
}
match test_housing_regression_native() {
Ok(result) => result,
Err(e) => serde_json::json!({ "status": "ERROR", "error": e.to_string() }).to_string(),
}
}
#[cfg(any(test, feature = "wasm"))]
fn test_housing_regression_native() -> Result<String> {
let y = vec![
245.5, 312.8, 198.4, 425.6, 278.9, 356.2, 189.5, 512.3, 234.7, 298.1, 445.8, 167.9, 367.4,
289.6, 198.2, 478.5, 256.3, 334.7, 178.5, 398.9, 223.4, 312.5, 156.8, 423.7, 267.9,
];
let square_feet = vec![
1200.0, 1800.0, 950.0, 2400.0, 1450.0, 2000.0, 1100.0, 2800.0, 1350.0, 1650.0, 2200.0,
900.0, 1950.0, 1500.0, 1050.0, 2600.0, 1300.0, 1850.0, 1000.0, 2100.0, 1250.0, 1700.0,
850.0, 2350.0, 1400.0,
];
let bedrooms = vec![
3.0, 4.0, 2.0, 4.0, 3.0, 4.0, 2.0, 5.0, 3.0, 3.0, 4.0, 2.0, 4.0, 3.0, 2.0, 5.0, 3.0, 4.0,
2.0, 4.0, 3.0, 3.0, 2.0, 4.0, 3.0,
];
let age = vec![
15.0, 10.0, 25.0, 5.0, 8.0, 12.0, 20.0, 2.0, 18.0, 7.0, 3.0, 30.0, 6.0, 14.0, 22.0, 1.0,
16.0, 9.0, 28.0, 4.0, 19.0, 11.0, 35.0, 3.0, 13.0,
];
let x_vars = vec![square_feet, bedrooms, age];
let names = vec![
"Intercept".to_string(),
"Square_Feet".to_string(),
"Bedrooms".to_string(),
"Age".to_string(),
];
let result = core::ols_regression(&y, &x_vars, &names)?;
let expected_coeffs = [52.1271333, 0.1613877, 0.9545492, -1.1811815];
let expected_std_errs = [31.18201809, 0.01875072, 10.44400198, 0.73219949];
let tolerance = 1e-4;
let mut mismatches = vec![];
for i in 0..4 {
if (result.coefficients[i] - expected_coeffs[i]).abs() > tolerance {
mismatches.push(format!(
"coeff[{}] differs: got {}, expected {}",
i, result.coefficients[i], expected_coeffs[i]
));
}
if (result.std_errors[i] - expected_std_errs[i]).abs() > tolerance {
mismatches.push(format!(
"std_err[{}] differs: got {}, expected {}",
i, result.std_errors[i], expected_std_errs[i]
));
}
}
if mismatches.is_empty() {
Ok(serde_json::json!({ "status": "PASS" }).to_string())
} else {
Ok(serde_json::json!({ "status": "FAIL", "mismatches": mismatches }).to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verify_housing_regression_integrity() {
let result = test_housing_regression_native();
if let Err(e) = result {
panic!("Regression test failed: {}", e);
}
}
#[test]
fn test_housing_regression_json_output() {
let result = test_housing_regression_native().unwrap();
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
assert!(parsed.get("status").is_some());
assert_eq!(parsed["status"], "PASS");
}
#[test]
fn test_housing_regression_coefficients() {
let y = vec![
245.5, 312.8, 198.4, 425.6, 278.9, 356.2, 189.5, 512.3, 234.7, 298.1, 445.8, 167.9,
367.4, 289.6, 198.2, 478.5, 256.3, 334.7, 178.5, 398.9, 223.4, 312.5, 156.8, 423.7,
267.9,
];
let square_feet = vec![
1200.0, 1800.0, 950.0, 2400.0, 1450.0, 2000.0, 1100.0, 2800.0, 1350.0, 1650.0,
2200.0, 900.0, 1950.0, 1500.0, 1050.0, 2600.0, 1300.0, 1850.0, 1000.0, 2100.0,
1250.0, 1700.0, 850.0, 2350.0, 1400.0,
];
let bedrooms = vec![
3.0, 4.0, 2.0, 4.0, 3.0, 4.0, 2.0, 5.0, 3.0, 3.0, 4.0, 2.0, 4.0, 3.0, 2.0, 5.0,
3.0, 4.0, 2.0, 4.0, 3.0, 3.0, 2.0, 4.0, 3.0,
];
let age = vec![
15.0, 10.0, 25.0, 5.0, 8.0, 12.0, 20.0, 2.0, 18.0, 7.0, 3.0, 30.0, 6.0, 14.0,
22.0, 1.0, 16.0, 9.0, 28.0, 4.0, 19.0, 11.0, 35.0, 3.0, 13.0,
];
let x_vars = vec![square_feet, bedrooms, age];
let names = vec![
"Intercept".to_string(),
"Square_Feet".to_string(),
"Bedrooms".to_string(),
"Age".to_string(),
];
let result = core::ols_regression(&y, &x_vars, &names).unwrap();
let expected_coeffs = [52.1271333, 0.1613877, 0.9545492, -1.1811815];
let expected_std_errs = [31.18201809, 0.01875072, 10.44400198, 0.73219949];
let tolerance = 1e-4;
for i in 0..4 {
assert!(
(result.coefficients[i] - expected_coeffs[i]).abs() < tolerance,
"coeff[{}] differs: got {}, expected {}",
i,
result.coefficients[i],
expected_coeffs[i]
);
assert!(
(result.std_errors[i] - expected_std_errs[i]).abs() < tolerance,
"std_err[{}] differs: got {}, expected {}",
i,
result.std_errors[i],
expected_std_errs[i]
);
}
}
#[test]
fn test_housing_regression_r_squared() {
let result = test_housing_regression_native().unwrap();
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
assert_eq!(parsed["status"], "PASS");
}
#[test]
fn test_housing_regression_comprehensive() {
let y = vec![
245.5, 312.8, 198.4, 425.6, 278.9, 356.2, 189.5, 512.3, 234.7, 298.1,
];
let x1 = vec![1200.0, 1800.0, 950.0, 2400.0, 1450.0, 2000.0, 1100.0, 2800.0, 1350.0, 1650.0];
let x2 = vec![3.0, 4.0, 2.0, 4.0, 3.0, 4.0, 2.0, 5.0, 3.0, 3.0];
let result = core::ols_regression(&y, &[x1, x2], &["Intercept".into(), "X1".into(), "X2".into()])
.unwrap();
assert!(!result.coefficients.is_empty());
assert!(!result.std_errors.is_empty());
assert!(!result.t_stats.is_empty());
assert!(!result.p_values.is_empty());
assert!(result.r_squared >= 0.0 && result.r_squared <= 1.0);
assert!(result.residuals.len() == y.len());
}
#[test]
fn test_housing_regression_insufficient_data() {
let y = vec![245.5, 312.8]; let x1 = vec![1200.0, 1800.0];
let x2 = vec![3.0, 4.0];
let result = core::ols_regression(&y, &[x1, x2], &["Intercept".into(), "X1".into(), "X2".into()]);
assert!(result.is_err());
}
#[test]
fn test_housing_regression_tolerance_check() {
let y = vec![
245.5, 312.8, 198.4, 425.6, 278.9, 356.2, 189.5, 512.3, 234.7, 298.1, 445.8, 167.9,
367.4, 289.6, 198.2, 478.5, 256.3, 334.7, 178.5, 398.9, 223.4, 312.5, 156.8, 423.7,
267.9,
];
let square_feet = vec![
1200.0, 1800.0, 950.0, 2400.0, 1450.0, 2000.0, 1100.0, 2800.0, 1350.0, 1650.0,
2200.0, 900.0, 1950.0, 1500.0, 1050.0, 2600.0, 1300.0, 1850.0, 1000.0, 2100.0,
1250.0, 1700.0, 850.0, 2350.0, 1400.0,
];
let bedrooms = vec![
3.0, 4.0, 2.0, 4.0, 3.0, 4.0, 2.0, 5.0, 3.0, 3.0, 4.0, 2.0, 4.0, 3.0, 2.0, 5.0,
3.0, 4.0, 2.0, 4.0, 3.0, 3.0, 2.0, 4.0, 3.0,
];
let age = vec![
15.0, 10.0, 25.0, 5.0, 8.0, 12.0, 20.0, 2.0, 18.0, 7.0, 3.0, 30.0, 6.0, 14.0,
22.0, 1.0, 16.0, 9.0, 28.0, 4.0, 19.0, 11.0, 35.0, 3.0, 13.0,
];
let x_vars = vec![square_feet, bedrooms, age];
let names = vec![
"Intercept".to_string(),
"Square_Feet".to_string(),
"Bedrooms".to_string(),
"Age".to_string(),
];
let result = core::ols_regression(&y, &x_vars, &names).unwrap();
for coef in &result.coefficients {
assert!(coef.is_finite(), "Coefficient should be finite");
}
for se in &result.std_errors {
assert!(se.is_finite(), "Standard error should be finite");
if *se <= 0.0 {
panic!("Standard error should be positive, got {}", se);
}
}
}
}