cometsfactory 0.0.1

Comet factory — classify, build and catalogue comets of any type: short-period, long-period, Halley-type, sungrazer, interstellar, main-belt comet, centaur-transition, and extinct.
Documentation
use cometsfactory::engine::coma;
use cometsfactory::engine::evolution;
use cometsfactory::engine::orbits;

// ── Orbits ───────────────────────────────────────────────────

#[test]
fn mean_motion_positive() {
    let n = orbits::mean_motion(3.0);
    assert!(n > 0.0);
}

#[test]
fn orbital_period_earth_approx_1yr() {
    let p = orbits::orbital_period(1.0);
    let years = p / (365.25 * 86400.0);
    assert!((years - 1.0).abs() < 0.02);
}

#[test]
fn tisserand_jfc_range() {
    let tj = orbits::tisserand(3.5, 0.6, 10.0, 5.2);
    assert!(tj > 2.0 && tj < 3.0);
}

#[test]
fn eccentric_anomaly_circular() {
    let ea = orbits::eccentric_anomaly(1.0, 0.0);
    assert!((ea - 1.0).abs() < 1e-10);
}

#[test]
fn heliocentric_distance_at_perihelion() {
    let r = orbits::heliocentric_distance(3.0, 0.5, 0.0);
    let expected_q = 3.0 * (1.0 - 0.25) / (1.0 + 0.5);
    assert!((r - expected_q).abs() < 1e-6);
}

#[test]
fn velocity_at_perihelion() {
    let v = orbits::velocity_at_distance(3.0, 1.5);
    assert!(v > 0.0);
}

#[test]
fn synodic_period_positive() {
    let s = orbits::synodic_period(3.0, 5.0);
    assert!(s > 0.0);
    assert!(s.is_finite());
}

#[test]
fn time_from_perihelion_zero_at_zero() {
    let t = orbits::time_from_perihelion(3.0, 0.5, 0.0);
    assert!(t.abs() < 1.0);
}

#[test]
fn perihelion_passages_count() {
    let count = orbits::perihelion_passages(1.0, 10.0);
    assert!((count - 10.0).abs() < 0.5);
}

// ── Evolution ────────────────────────────────────────────────

#[test]
fn mantle_growth_positive() {
    let g = evolution::mantle_growth_per_orbit(1000.0, 0.05, 1.0, 6.0);
    assert!(g > 0.0);
}

#[test]
fn radius_loss_positive() {
    let loss = evolution::radius_loss_per_orbit(0.001, 6.0, 1000.0, 500.0);
    assert!(loss > 0.0);
}

#[test]
fn orbits_to_exhaustion_finite() {
    let n = evolution::orbits_to_exhaustion(1000.0, 0.01);
    assert!(n.is_finite());
    assert!(n > 0.0);
}

#[test]
fn spinup_timescale_positive() {
    let tau = evolution::spinup_timescale(1000.0, 500.0, 36000.0, 1.0, 500.0);
    assert!(tau > 0.0);
}

#[test]
fn splitting_risk_bounded() {
    let risk = evolution::splitting_risk(0.5, 3600.0);
    assert!((0.0..=1.0).contains(&risk));
}

#[test]
fn brightness_fading_decreases() {
    let f1 = evolution::brightness_fading(10.0, 100.0);
    let f2 = evolution::brightness_fading(100.0, 100.0);
    assert!(f1 > f2);
    assert!(f1 <= 1.0);
}

// ── Coma ─────────────────────────────────────────────────────

#[test]
fn coma_radius_positive() {
    let r = coma::coma_radius(1.0e28, 1.0, 0.04);
    assert!(r > 0.0);
}

#[test]
fn dust_tail_length_positive() {
    let l = coma::dust_tail_length(0.5, 1.0, 30000.0);
    assert!(l > 0.0);
}

#[test]
fn ion_tail_length_positive() {
    let l = coma::ion_tail_length(1.0e28, 1.0);
    assert!(l > 0.0);
}

#[test]
fn afrho_decreases_with_distance() {
    let a1 = coma::afrho(1.0e6, 1.0);
    let a2 = coma::afrho(1.0e6, 5.0);
    assert!(a1 > a2);
}

#[test]
fn coma_optical_depth_positive() {
    let tau = coma::coma_optical_depth(1.0e28, 1.0e6, 1.0, 0.04);
    assert!(tau > 0.0);
}

#[test]
fn lyman_alpha_proportional() {
    let l1 = coma::lyman_alpha_production(1.0e28);
    let l2 = coma::lyman_alpha_production(2.0e28);
    assert!((l2 / l1 - 2.0).abs() < 1e-6);
}