dwarfplanetsfactory 0.0.3

Dwarf planet factory — classify, build and catalogue dwarf planets of any type: Kuiper belt, scattered disk, plutino, cold classical, detached, binary, Ceres-type, and sednoid.
Documentation
use dwarfplanetsfactory::types::*;

// ── KuiperBelt ──────────────────────────────────────────────────────
#[test]
fn kuiper_belt_creation() {
    let kb = KuiperBelt::new(6.0e5, 42.0, 0.08);
    assert!(kb.radius > 0.0);
    assert!((42.0 - kb.semi_major_axis).abs() < 1.0e-6);
}

#[test]
fn kuiper_belt_builder() {
    let kb = KuiperBelt::new(5.0e5, 40.0, 0.1)
        .with_density(1500.0)
        .with_albedo(0.12);
    assert!((kb.density - 1500.0).abs() < 1.0e-6);
    assert!((kb.albedo - 0.12).abs() < 1.0e-6);
}

#[test]
fn kuiper_belt_mass_positive() {
    let kb = KuiperBelt::new(6.0e5, 43.0, 0.05);
    assert!(kb.mass() > 0.0);
}

#[test]
fn kuiper_belt_hill_radius() {
    let kb = KuiperBelt::new(6.0e5, 43.0, 0.05);
    assert!(kb.hill_radius() > 0.0);
}

#[test]
fn kuiper_belt_perihelion_less_than_aphelion() {
    let kb = KuiperBelt::new(5.0e5, 44.0, 0.1);
    assert!(kb.perihelion() < kb.aphelion());
}

// ── ScatteredDisk ───────────────────────────────────────────────────
#[test]
fn scattered_disk_creation() {
    let sd = ScatteredDisk::new(4.0e5, 80.0, 0.6);
    assert!(sd.semi_major_axis >= 30.0);
    assert!(sd.eccentricity >= 0.2);
}

#[test]
fn scattered_disk_actively_scattered() {
    let sd = ScatteredDisk::new(4.0e5, 50.0, 0.35);
    let q = sd.perihelion();
    if (28.0..=38.0).contains(&q) {
        assert!(sd.is_actively_scattered());
    }
}

#[test]
fn scattered_disk_could_become_centaur() {
    let sd = ScatteredDisk::new(3.0e5, 60.0, 0.55);
    let q = sd.perihelion();
    if q < 30.0 {
        assert!(sd.could_become_centaur());
    }
}

#[test]
fn scattered_disk_mass() {
    let sd = ScatteredDisk::new(5.0e5, 100.0, 0.7);
    assert!(sd.mass() > 0.0);
}

// ── Plutino ─────────────────────────────────────────────────────────
#[test]
fn plutino_creation() {
    let p = Plutino::new(5.0e5, 0.25);
    assert!((p.semi_major_axis - 39.4).abs() < 1.0e-6);
}

#[test]
fn plutino_resonance_ratio() {
    let p = Plutino::new(5.0e5, 0.2);
    let ratio = p.resonance_ratio();
    assert!((ratio - 1.5).abs() < 0.05);
}

#[test]
fn plutino_neptune_crossing() {
    let p = Plutino::new(5.0e5, 0.3);
    let q = p.perihelion();
    if q < 30.07 {
        assert!(p.is_neptune_crossing());
    }
}

#[test]
fn plutino_builder() {
    let p = Plutino::new(6.0e5, 0.2)
        .with_density(2200.0)
        .with_inclination(17.0);
    assert!((p.density - 2200.0).abs() < 1.0e-6);
    assert!((p.inclination - 17.0).abs() < 1.0e-6);
}

// ── ColdClassical ───────────────────────────────────────────────────
#[test]
fn cold_classical_creation() {
    let cc = ColdClassical::new(3.0e5, 44.0);
    assert!(cc.semi_major_axis >= 42.0);
    assert!(cc.eccentricity < 0.15);
}

#[test]
fn cold_classical_pristine() {
    let cc = ColdClassical::new(3.0e5, 44.0);
    assert!(cc.is_pristine());
}

#[test]
fn cold_classical_surface_color() {
    use dwarfplanetsfactory::types::cold_classical::SurfaceColor;
    let cc = ColdClassical::new(3.0e5, 44.0).with_surface_color(SurfaceColor::UltraRed);
    match cc.surface_color {
        SurfaceColor::UltraRed => {}
        _ => panic!("expected UltraRed"),
    }
}

#[test]
fn cold_classical_binary_fraction() {
    let cc = ColdClassical::new(3.0e5, 44.0).with_binary_fraction(0.3);
    assert!((cc.binary_fraction - 0.3).abs() < 1.0e-6);
}

// ── Detached ────────────────────────────────────────────────────────
#[test]
fn detached_creation() {
    let d = Detached::new(5.0e5, 70.0, 0.4);
    assert!(d.semi_major_axis >= 40.0);
}

#[test]
fn detached_truly_detached() {
    let d = Detached::new(5.0e5, 80.0, 0.4);
    let q = d.perihelion();
    if q > 40.0 {
        assert!(d.is_truly_detached());
    }
}

#[test]
fn detached_planet_nine_candidate() {
    let d = Detached::new(5.0e5, 200.0, 0.7);
    let q = d.perihelion();
    let a = d.semi_major_axis;
    if a > 150.0 && q > 40.0 {
        assert!(d.planet_nine_candidate());
    }
}

#[test]
fn detached_mass() {
    let d = Detached::new(4.0e5, 90.0, 0.3);
    assert!(d.mass() > 0.0);
}

// ── BinaryDwarf ─────────────────────────────────────────────────────
#[test]
fn binary_dwarf_creation() {
    let bd = BinaryDwarf::new(6.0e5, 3.0e5, 39.5, 0.25);
    assert!(bd.primary_radius > bd.secondary_radius);
}

#[test]
fn binary_dwarf_mass_ratio() {
    let bd = BinaryDwarf::new(6.0e5, 3.0e5, 39.5, 0.25)
        .with_primary_density(2000.0)
        .with_secondary_density(2000.0);
    let ratio = bd.mass_ratio();
    assert!(ratio > 0.0 && ratio <= 1.0);
}

#[test]
fn binary_dwarf_mutual_period() {
    let bd = BinaryDwarf::new(6.0e5, 3.0e5, 39.5, 0.25).with_mutual_separation(2.0e4);
    let period = bd.mutual_orbital_period();
    assert!(period > 0.0);
}

#[test]
fn binary_dwarf_barycenter() {
    let bd = BinaryDwarf::new(6.0e5, 3.0e5, 39.5, 0.25).with_mutual_separation(2.0e4);
    let offset = bd.barycenter_offset();
    assert!(offset > 0.0);
    assert!(offset < bd.mutual_separation);
}

// ── CeresType ───────────────────────────────────────────────────────
#[test]
fn ceres_type_creation() {
    let c = CeresType::new(4.7e5, 2.77, 0.076);
    assert!(c.semi_major_axis >= 2.0);
}

#[test]
fn ceres_type_tisserand() {
    let c = CeresType::new(4.7e5, 2.77, 0.076);
    let tj = c.tisserand_jupiter();
    assert!(tj > 3.0); // Ceres has Tj > 3
}

#[test]
fn ceres_type_ice_mass() {
    let c = CeresType::new(4.7e5, 2.77, 0.076).with_ice_mantle_fraction(0.25);
    let ice_m = c.ice_mass();
    assert!(ice_m > 0.0);
    assert!(ice_m < c.mass());
}

#[test]
fn ceres_type_builder() {
    let c = CeresType::new(4.7e5, 2.77, 0.076)
        .with_density(2162.0)
        .with_cryovolcanism(true);
    assert!((c.density - 2162.0).abs() < 1.0e-6);
    assert!(c.has_cryovolcanism);
}

// ── Sednoid ─────────────────────────────────────────────────────────
#[test]
fn sednoid_creation() {
    let s = Sednoid::new(5.0e5, 500.0, 0.85);
    assert!(s.semi_major_axis >= 150.0);
    assert!(s.eccentricity >= 0.5);
}

#[test]
fn sednoid_is_true() {
    let s = Sednoid::new(5.0e5, 506.0, 0.85);
    let q = s.perihelion();
    if q > 50.0 {
        assert!(s.is_true_sednoid());
    }
}

#[test]
fn sednoid_inner_oort() {
    let s = Sednoid::new(5.0e5, 1200.0, 0.9);
    assert!(s.is_inner_oort_candidate());
}

#[test]
fn sednoid_planet_nine_score() {
    let s = Sednoid::new(5.0e5, 500.0, 0.85).with_inclination(10.0);
    let score = s.planet_nine_alignment_score();
    assert!((0.0..=1.0).contains(&score));
}

#[test]
fn sednoid_equilibrium_temp() {
    let s = Sednoid::new(5.0e5, 500.0, 0.85);
    let temp = s.equilibrium_temp();
    assert!(temp > 0.0);
    assert!(temp < 50.0); // Very cold at 500 AU
}

// ── GeneratedDwarfPlanet enum ───────────────────────────────────────
#[test]
fn enum_kuiper_belt_radius() {
    let kb = KuiperBelt::new(6.0e5, 43.0, 0.05);
    let dwarf = GeneratedDwarfPlanet::KuiperBelt(kb);
    assert!(dwarf.radius() > 0.0);
}

#[test]
fn enum_sednoid_semi_major() {
    let s = Sednoid::new(5.0e5, 500.0, 0.85);
    let dwarf = GeneratedDwarfPlanet::Sednoid(s);
    assert!(dwarf.semi_major_axis() >= 150.0);
}

#[test]
fn enum_binary_eccentricity() {
    let bd = BinaryDwarf::new(6.0e5, 3.0e5, 39.5, 0.25);
    let dwarf = GeneratedDwarfPlanet::BinaryDwarf(bd);
    assert!(dwarf.eccentricity() >= 0.0);
}