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}