sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
pub fn jet_lag_resync_time(time_zones_crossed: f64, resync_rate: f64) -> f64 {
    time_zones_crossed / resync_rate
}

pub fn sleep_pressure(wake_duration: f64, buildup_rate: f64, max_pressure: f64) -> f64 {
    max_pressure * (1.0 - (-buildup_rate * wake_duration).exp())
}

pub fn two_process_model(sleep_pressure: f64, circadian_amplitude: f64, phase: f64) -> f64 {
    sleep_pressure + circadian_amplitude * (2.0 * std::f64::consts::PI * phase / 24.0).sin()
}

pub fn photoperiod(latitude_rad: f64, declination_rad: f64) -> f64 {
    let cos_hour_angle = -(latitude_rad.tan() * declination_rad.tan());
    if cos_hour_angle < -1.0 {
        return 24.0;
    }
    if cos_hour_angle > 1.0 {
        return 0.0;
    }
    2.0 * cos_hour_angle.acos() * 12.0 / std::f64::consts::PI
}

pub fn ultradian_rhythm(amplitudes: &[f64], periods: &[f64], t: f64) -> f64 {
    amplitudes
        .iter()
        .zip(periods.iter())
        .map(|(&a, &p)| a * (2.0 * std::f64::consts::PI * t / p).sin())
        .sum()
}

pub fn chronotype_shift(mid_sleep_free: f64, sleep_debt_correction: f64) -> f64 {
    mid_sleep_free - sleep_debt_correction / 2.0
}

pub fn circadian_acrophase(data: &[f64], period: f64) -> f64 {
    let omega = 2.0 * std::f64::consts::PI / period;
    let mut cos_sum = 0.0;
    let mut sin_sum = 0.0;
    for (i, &di) in data.iter().enumerate() {
        let t = i as f64;
        cos_sum += di * (omega * t).cos();
        sin_sum += di * (omega * t).sin();
    }
    sin_sum.atan2(cos_sum)
}

pub fn cosinor_amplitude(data: &[f64], period: f64) -> f64 {
    let n = data.len();
    let omega = 2.0 * std::f64::consts::PI / period;
    let mut cos_sum = 0.0;
    let mut sin_sum = 0.0;
    for (i, &di) in data.iter().enumerate() {
        let t = i as f64;
        cos_sum += di * (omega * t).cos();
        sin_sum += di * (omega * t).sin();
    }
    let a = 2.0 * cos_sum / n as f64;
    let b = 2.0 * sin_sum / n as f64;
    (a * a + b * b).sqrt()
}

pub fn social_jet_lag(weekday_midsleep: f64, weekend_midsleep: f64) -> f64 {
    (weekend_midsleep - weekday_midsleep).abs()
}

pub fn mesor(data: &[f64]) -> f64 {
    if data.is_empty() {
        return 0.0;
    }
    data.iter().sum::<f64>() / data.len() as f64
}

pub fn sleep_debt(wake_hours: f64, optimal_sleep: f64, actual_sleep: f64) -> f64 {
    let deficit_per_day = optimal_sleep - actual_sleep;
    deficit_per_day * wake_hours / 24.0
}

pub fn circadian_phase_estimate(core_body_temp_min_time: f64) -> f64 {
    (core_body_temp_min_time + 2.0) % 24.0
}

pub fn light_phase_advance(lux: f64, sensitivity: f64, timing_factor: f64) -> f64 {
    sensitivity * lux.ln().max(0.0) * timing_factor
}

pub fn dim_light_melatonin_onset(melatonin_levels: &[f64], threshold: f64) -> Option<usize> {
    melatonin_levels.iter().position(|&m| m >= threshold)
}

pub fn infradian_cycle(base_amplitude: f64, period_days: f64, day: f64) -> f64 {
    base_amplitude * (2.0 * std::f64::consts::PI * day / period_days).sin()
}

pub fn temperature_compensation_q10(rate_t1: f64, rate_t2: f64, t1: f64, t2: f64) -> f64 {
    (rate_t2 / rate_t1).powf(10.0 / (t2 - t1))
}

pub fn masking_effect(endogenous: f64, exogenous_signal: f64, masking_gain: f64) -> f64 {
    endogenous + masking_gain * exogenous_signal
}

pub fn relative_amplitude(max_val: f64, min_val: f64) -> f64 {
    (max_val - min_val) / (max_val + min_val)
}

pub fn interdaily_stability(data: &[f64], period: usize) -> f64 {
    if data.is_empty() || period == 0 {
        return 0.0;
    }
    let n = data.len();
    let grand_mean = data.iter().sum::<f64>() / n as f64;
    let mut hourly_means = vec![0.0; period];
    let mut hourly_counts = vec![0usize; period];
    for (i, &v) in data.iter().enumerate() {
        let bin = i % period;
        hourly_means[bin] += v;
        hourly_counts[bin] += 1;
    }
    for j in 0..period {
        if hourly_counts[j] > 0 {
            hourly_means[j] /= hourly_counts[j] as f64;
        }
    }
    let var_total: f64 = data.iter().map(|&v| (v - grand_mean).powi(2)).sum();
    let var_hourly: f64 = hourly_means
        .iter()
        .zip(hourly_counts.iter())
        .map(|(&m, &c)| c as f64 * (m - grand_mean).powi(2))
        .sum();
    if var_total < 1e-30 {
        return 0.0;
    }
    (n as f64 * var_hourly) / (period as f64 * var_total)
}

pub fn intradaily_variability(data: &[f64]) -> f64 {
    let n = data.len();
    if n < 2 {
        return 0.0;
    }
    let grand_mean = data.iter().sum::<f64>() / n as f64;
    let var_total: f64 = data.iter().map(|&v| (v - grand_mean).powi(2)).sum();
    if var_total < 1e-30 {
        return 0.0;
    }
    let diff_sum: f64 = data.windows(2).map(|w| (w[1] - w[0]).powi(2)).sum();
    (n as f64 * diff_sum) / ((n - 1) as f64 * var_total)
}