sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
pub fn linear_quadratic_survival(dose: f64, alpha: f64, beta: f64) -> f64 {
    (-alpha * dose - beta * dose * dose).exp()
}

pub fn biologically_effective_dose(n: f64, d: f64, alpha_beta: f64) -> f64 {
    n * d * (1.0 + d / alpha_beta)
}

pub fn equivalent_dose_2gy(bed: f64, alpha_beta: f64) -> f64 {
    bed / (1.0 + 2.0 / alpha_beta)
}

pub fn tcp(n_cells: f64, survival_fraction: f64) -> f64 {
    (-n_cells * survival_fraction).exp()
}

pub fn ntcp_lyman(dose: f64, td50: f64, m: f64) -> f64 {
    let t = (dose - td50) / (m * td50);
    0.5 * (1.0 + erf_approx(t / std::f64::consts::SQRT_2))
}

fn erf_approx(x: f64) -> f64 {
    let sign = if x >= 0.0 { 1.0 } else { -1.0 };
    let x = x.abs();
    let t = 1.0 / (1.0 + 0.3275911 * x);
    let poly = t
        * (0.254829592
            + t * (-0.284496736 + t * (1.421413741 + t * (-1.453152027 + t * 1.061405429))));
    sign * (1.0 - poly * (-x * x).exp())
}

pub fn oxygen_enhancement_ratio(dose_hypoxic: f64, dose_oxic: f64) -> f64 {
    dose_hypoxic / dose_oxic
}

pub fn dna_dsb_yield(dose: f64, yield_per_gray: f64) -> f64 {
    dose * yield_per_gray
}

pub fn repair_kinetics(dsb0: f64, repair_rate: f64, t: f64) -> f64 {
    dsb0 * (-repair_rate * t).exp()
}

pub fn fractionation_survival(
    n_fractions: usize,
    dose_per_fraction: f64,
    alpha: f64,
    beta: f64,
    repair_factor: f64,
) -> f64 {
    let sf_per_fraction = linear_quadratic_survival(dose_per_fraction, alpha, beta * repair_factor);
    sf_per_fraction.powi(n_fractions as i32)
}

pub fn relative_biological_effectiveness(dose_ref: f64, dose_test: f64) -> f64 {
    dose_ref / dose_test
}

pub fn let_to_rbe(let_val: f64, rbe_max: f64, let_half: f64) -> f64 {
    rbe_max * let_val / (let_half + let_val)
}

pub fn protraction_factor(dose_rate: f64, repair_half_time: f64, total_time: f64) -> f64 {
    let mu = std::f64::consts::LN_2 / repair_half_time;
    let total_dose = dose_rate * total_time;
    let x = mu * total_time;
    if x < 1e-6 {
        return 1.0;
    }
    let g = 2.0 * (x - 1.0 + (-x).exp()) / (x * x);
    if total_dose > 0.0 { g } else { 1.0 }
}

pub fn bystander_effect(dose: f64, max_effect: f64, dose_half: f64) -> f64 {
    max_effect * (1.0 - (-dose / dose_half).exp())
}

pub fn adaptive_response(
    priming_dose: f64,
    challenge_dose: f64,
    alpha: f64,
    beta: f64,
    reduction_factor: f64,
) -> f64 {
    let sf_unprimed = linear_quadratic_survival(challenge_dose, alpha, beta);
    let sf_primed = linear_quadratic_survival(
        challenge_dose,
        alpha * reduction_factor,
        beta * reduction_factor,
    );
    if priming_dose > 0.0 {
        sf_primed
    } else {
        sf_unprimed
    }
}

pub fn low_dose_hypersensitivity(dose: f64, alpha_r: f64, alpha_s: f64, dc: f64, beta: f64) -> f64 {
    let alpha_eff = alpha_r + (alpha_s - alpha_r) * (-dose / dc).exp();
    (-alpha_eff * dose - beta * dose * dose).exp()
}

pub fn tumor_growth_delay(dose: f64, alpha: f64, beta: f64, doubling_time: f64) -> f64 {
    let sf = linear_quadratic_survival(dose, alpha, beta);
    -sf.ln() * doubling_time / std::f64::consts::LN_2
}

pub fn complication_free_cure(tcp_val: f64, ntcp_val: f64) -> f64 {
    tcp_val * (1.0 - ntcp_val)
}

pub fn isoeffect_dose(n1: f64, d1: f64, alpha_beta: f64, n2: f64) -> f64 {
    let bed = n1 * d1 * (1.0 + d1 / alpha_beta);
    let disc = alpha_beta * alpha_beta + 4.0 * alpha_beta * bed / n2;
    (-alpha_beta + disc.sqrt()) / 2.0
}