tube.rs

4.8 kB · rust · 141 lines

1use mrlydemo::tube::*;2use mrlydemo::two::two_grid;34const SWEEP: usize = 2001;56fn nearest(code: &str, number: usize, level: usize, base: usize) -> Vec<f64> {7    let grid = two_grid(code, number, level, 0, base).unwrap();8    let side = grid.width as usize;9    let filled: Vec<(usize, usize)> = (0..side * side)10        .filter(|&at| grid.types[at] != 0)11        .map(|at| (at / side, at % side))12        .collect();13    let mut out = Vec::with_capacity(side * side);14    for row in 0..side {15        for col in 0..side {16            let mut best = f64::MAX;17            for &(a, b) in &filled {18                let (dr, dc) = (row.abs_diff(a) as f64, col.abs_diff(b) as f64);19                best = best.min(dr * dr + dc * dc);20            }21            out.push(best.sqrt());22        }23    }24    out25}2627fn holes(level: u32, eps: u64) -> u64 {28    let mut total = 8u64.pow(level);29    for m in 1..=level {30        let side = 3u64.pow(level - m);31        let one = if side <= 2 * eps {32            side * side33        } else {34            4 * eps * side - 4 * eps * eps35        };36        total += 8u64.pow(m - 1) * one;37    }38    total39}4041fn band(q: usize, k: usize) -> (f64, f64) {42    let (mut low, mut high) = (f64::MAX, f64::MIN);43    for step in 0..SWEEP {44        let t = 1.0 / q as f64 + (1.0 - 1.0 / q as f64) * step as f64 / (SWEEP - 1) as f64;45        let g = tube_closed(q, k, t).unwrap();46        low = low.min(g);47        high = high.max(g);48    }49    (low, high)50}5152#[test]53fn the_field_is_the_exact_distance_to_the_design() {54    for (code, number, level, base) in [("495", 3, 3, 3), ("127", 3, 3, 3), ("7", 2, 5, 2)] {55        let read = tube_distance(code, number, level, base).unwrap();56        let want = nearest(code, number, level, base);57        assert_eq!(read.len(), want.len());58        let worst = read59            .iter()60            .zip(&want)61            .map(|(&a, &b)| (f64::from(a) - b).abs())62            .fold(0.0f64, f64::max);63        assert!(worst < 1e-6, "{code} at level {level} drifts {worst}");64    }65    let carpet = tube_distance("495", 3, 6, 3).unwrap();66    assert_eq!(carpet.len(), 531441);67    assert_eq!(carpet.iter().filter(|&&v| v == 0.0).count(), 262144);68}6970#[test]71fn the_tube_is_the_hole_sum_of_the_carpet() {72    assert_eq!(tube_volume("495", 3, 6, 3, 21.0).unwrap(), 478872.0);73    assert_eq!(478872.0 / 531441.0, 5912.0 / 6561.0);74    for (level, eps) in [(3, 1), (4, 2), (5, 7), (6, 21), (6, 40), (6, 121)] {75        let read = tube_volume("495", 3, level, 3, eps as f64).unwrap();76        assert_eq!(77            read,78            holes(level as u32, eps) as f64,79            "level {level} eps {eps}"80        );81    }82}8384#[test]85fn the_closed_profile_is_the_carpets_limit() {86    let seam = 379.0 / 280.0;87    assert!((tube_closed(3, 8, 1.0 / 3.0).unwrap() - seam).abs() < 1e-12);88    assert!((tube_closed(3, 8, 1.0).unwrap() - seam).abs() < 1e-12);89    assert!(90        (tube_closed(3, 8, 0.5).unwrap() - (44.0 / 35.0) * 2f64.powf(2.0 - 8f64.ln() / 3f64.ln()))91            .abs()92            < 1e-1293    );94    for step in 1..40 {95        let t = step as f64 / 40.0;96        let g = tube_closed(3, 8, t).unwrap();97        assert!((g - tube_closed(3, 8, t / 3.0).unwrap()).abs() < 1e-12);98    }99    let (low, high) = band(3, 8);100    assert_eq!(101        format!("{high:.8},{low:.8},{:.6}", 100.0 * (high - low) / low),102        "1.35561708,1.35067021,0.366253"103    );104    assert!(high <= 1.35561708227 && low >= 1.3506702097);105    let (five_low, five_high) = band(5, 21);106    assert!(five_high > five_low);107    assert!((tube_closed(5, 21, 0.2).unwrap() - tube_closed(5, 21, 1.0).unwrap()).abs() < 1e-12);108    assert!(tube_closed(3, 3, 0.5).is_err());109    assert!(tube_closed(3, 8, 0.0).is_err());110}111112#[test]113fn the_profile_climbs_onto_the_closed_form() {114    let pairs = tube_profile("495", 3, 6, 3, 9).unwrap();115    assert_eq!(pairs.len(), 18);116    assert_eq!(pairs[0], 3f32.ln());117    assert_eq!(pairs[1], 1.125);118    let deep = f64::from(pairs[17]);119    let limit = tube_closed(3, 8, (-f64::from(pairs[16])).exp()).unwrap();120    assert!(deep < limit && limit - deep < 0.01, "the tail reads {deep}");121    let runner = tube_profile("127", 3, 6, 3, 40).unwrap();122    assert_eq!(runner.len(), 80);123    assert!(runner.chunks(2).all(|pair| pair[1] > 0.0));124    assert!(tube_profile("495", 3, 2, 3, 40).unwrap().is_empty());125}126127#[test]128fn the_class_is_the_isolated_interior_hole() {129    assert!(tube_class("495", 3, 3).unwrap());130    assert!(tube_class("7", 3, 2).unwrap());131    assert!(!tube_class("127", 3, 3).unwrap());132    assert!(!tube_class("9", 2, 2).unwrap());133    assert!(!tube_class("511", 3, 3).unwrap());134}135136#[test]137fn the_grid_is_capped_and_the_empty_design_refused() {138    assert!(tube_distance("495", 3, 7, 3).is_err());139    assert!(tube_distance("0", 3, 3, 3).is_err());140    assert!(tube_volume("495", 3, 0, 3, 1.0).is_err());141}