use crate::math::ols::ols;
use crate::math::probit::probit;
use crate::OaxacaError;
use nalgebra::{DMatrix, DVector};
use statrs::distribution::{Continuous, ContinuousCDF, Normal};
#[derive(Debug)]
pub struct HeckmanResult {
pub selection_coeffs: DVector<f64>,
pub outcome_coeffs: DVector<f64>,
pub imr_coeff: f64,
pub imr: DVector<f64>,
pub imr_mean: f64,
pub imr_delta: f64,
}
pub fn heckman_two_step(
y_select: &DVector<f64>,
x_select: &DMatrix<f64>,
y_outcome: &DVector<f64>,
x_outcome: &DMatrix<f64>,
x_select_subset: &DMatrix<f64>,
) -> Result<HeckmanResult, OaxacaError> {
let probit_res = probit(y_select, x_select, 100, 1e-6)?;
let gamma = probit_res.coefficients;
let normal = Normal::new(0.0, 1.0).unwrap();
let z_gamma = x_select_subset * γ
let imr_vec: Vec<f64> = z_gamma
.iter()
.map(|&zg| {
let phi = normal.pdf(zg);
let big_phi = normal.cdf(zg);
if big_phi < 1e-10 {
0.0
} else {
phi / big_phi
}
})
.collect();
let imr = DVector::from_vec(imr_vec);
let mut x_augmented = x_outcome.clone();
x_augmented = x_augmented.insert_column(x_outcome.ncols(), 0.0);
x_augmented.set_column(x_outcome.ncols(), &imr);
let ols_res = ols(y_outcome, &x_augmented, None)?;
let full_coeffs = ols_res.coefficients;
let k_outcome = x_outcome.ncols();
let outcome_coeffs = full_coeffs.rows(0, k_outcome).into_owned();
let imr_coeff = full_coeffs[k_outcome];
let imr_mean = imr.mean();
let zg_subset = x_select_subset * γ
let delta_vec: Vec<f64> = imr
.iter()
.zip(zg_subset.iter())
.map(|(&l, &zg)| -l * (l + zg))
.collect();
let imr_delta = DVector::from_vec(delta_vec).mean();
Ok(HeckmanResult {
selection_coeffs: gamma,
outcome_coeffs,
imr_coeff,
imr,
imr_mean,
imr_delta,
})
}