main.rs
12.3 kB · rust · 378 lines
1use mrlyrs::math::three::sponge::{deep, dimension, distance, exact, profile, COVER, EDGE};2use std::time::Instant;34const PLUS: f64 = 7.0 / 27.0;5const SIDE: usize = 40;6const RUN: usize = 800;7const TOP: usize = 4;8const COARSE: usize = 72;9const CENTRE: usize = 120;10const STEPS: usize = 3000;11const DEPTH: i32 = 40;1213// CROSSING1415struct Hole {16 level: usize,17 start: f64,18 side: f64,19 rows: Vec<f64>,20}2122fn crossing(line: f64, top: usize) -> Vec<Hole> {23 let (mut local, mut start, mut rows) = (3.0 * line, 0.0, vec![0.0]);24 let mut out = Vec::new();25 for level in 1..=top {26 let side = 3f64.powi(-(level as i32) - 1);27 let digit = (3.0 * local).floor().clamp(0.0, 2.0);28 local = 3.0 * local - digit;29 if digit == 1.0 {30 let here = rows.iter().map(|q| q + side).collect();31 out.push(Hole {32 level,33 start: start + side,34 side,35 rows: here,36 });37 }38 let choices: &[f64] = if digit == 1.0 {39 &[0.0, 2.0]40 } else {41 &[0.0, 1.0, 2.0]42 };43 rows = rows44 .iter()45 .flat_map(|q| choices.iter().map(move |b| q + b * side))46 .collect();47 start += digit * side;48 }49 out50}5152fn integrand(a: f64, radius: f64, hole: &Hole, line: f64) -> f64 {53 let (half, reach) = (hole.side / 2.0, line - hole.start);54 let fall = (radius * radius - (hole.start + a).powi(2)).max(0.0).sqrt();55 let width = reach.min(hole.side - fall) - a.max(fall);56 if fall >= half || width <= 0.0 {57 return 0.0;58 }59 4.0 * (half - fall) * width60}6162fn simpson(63 f: &dyn Fn(f64) -> f64,64 a: f64,65 b: f64,66 fa: f64,67 fm: f64,68 fb: f64,69 whole: f64,70 depth: usize,71) -> f64 {72 let m = (a + b) / 2.0;73 let (lm, rm) = ((a + m) / 2.0, (m + b) / 2.0);74 let (flm, frm) = (f(lm), f(rm));75 let left = (m - a) * (fa + 4.0 * flm + fm) / 6.0;76 let right = (b - m) * (fm + 4.0 * frm + fb) / 6.0;77 if depth == 0 || (left + right - whole).abs() <= 1e-22 {78 return left + right + (left + right - whole) / 15.0;79 }80 simpson(f, a, m, fa, flm, fm, left, depth - 1) + simpson(f, m, b, fm, frm, fb, right, depth - 1)81}8283fn quadrature(radius: f64, hole: &Hole, line: f64) -> f64 {84 let f = |a: f64| integrand(a, radius, hole, line);85 let reach = line - hole.start;86 let low = ((radius * radius - hole.side * hole.side / 4.0)87 .max(0.0)88 .sqrt()89 - hole.start)90 .clamp(0.0, reach);91 let cuts = 64;92 let mut total = 0.0;93 for k in 0..cuts {94 let a = low + (reach - low) * k as f64 / cuts as f64;95 let b = low + (reach - low) * (k + 1) as f64 / cuts as f64;96 let (fa, fb, fm) = (f(a), f(b), f((a + b) / 2.0));97 total += simpson(98 &f,99 a,100 b,101 fa,102 fm,103 fb,104 (b - a) * (fa + 4.0 * fm + fb) / 6.0,105 40,106 );107 }108 total109}110111// RASTER112113fn box_low(radius: f64, hole: &Hole, line: f64) -> f64 {114 (radius * radius - hole.side * hole.side / 4.0)115 .max(0.0)116 .sqrt()117 .max(hole.start)118 .min(line)119}120121fn rows_raster(radius: f64, hole: &Hole, line: f64) -> [f64; 3] {122 let low = box_low(radius, hole, line);123 let (step, pace) = ((line - low) / SIDE as f64, hole.side / RUN as f64);124 let half = (2.0 * step * step + pace * pace).sqrt() / 2.0;125 let (mut mid, mut inner, mut outer) = (0.0, 0.0, 0.0);126 for row in &hole.rows {127 for a in 0..SIDE {128 for b in 0..SIDE {129 for c in 0..RUN {130 let u = 1.0 / 3.0 + low + (a as f64 + 0.5) * step;131 let v = 1.0 / 3.0 + low + (b as f64 + 0.5) * step;132 let d = distance([row + (c as f64 + 0.5) * pace, u, v]);133 mid += f64::from(u8::from(d > radius));134 inner += f64::from(u8::from(d > radius + half));135 outer += f64::from(u8::from(d > radius - half));136 }137 }138 }139 }140 let cell = step * step * pace;141 [mid * cell, inner * cell, outer * cell]142}143144fn outside(radius: f64, holes: &[Hole], line: f64) -> f64 {145 let cell = 1.0 / (3.0 * COARSE as f64);146 let mut worst: f64 = 0.0;147 for a in 0..COARSE {148 for b in 0..COARSE {149 for c in 0..COARSE {150 let along = (a as f64 + 0.5) * cell;151 let (y, z) = ((b as f64 + 0.5) * cell, (c as f64 + 0.5) * cell);152 let (u, v) = (y.min(1.0 / 3.0 - y), z.min(1.0 / 3.0 - z));153 if u > line || v > line {154 continue;155 }156 let inside = holes.iter().any(|h| {157 let low = box_low(radius, h, line);158 u >= low && v >= low && h.rows.iter().any(|q| along > *q && along < q + h.side)159 });160 if !inside {161 worst = worst.max(distance([along, 1.0 / 3.0 + y, 1.0 / 3.0 + z]));162 }163 }164 }165 }166 worst167}168169fn centre_raster(radius: f64) -> [f64; 3] {170 let lap = (radius * radius - EDGE * EDGE).max(0.0).sqrt();171 let step = (EDGE - lap) / CENTRE as f64;172 let half = step * 3f64.sqrt() / 2.0;173 let (mut mid, mut inner, mut outer) = (0.0, 0.0, 0.0);174 for a in 0..CENTRE {175 for b in 0..CENTRE {176 for c in 0..CENTRE {177 let at = [a, b, c].map(|k| 1.0 / 3.0 + lap + (k as f64 + 0.5) * step);178 let d = distance(at);179 mid += f64::from(u8::from(d > radius));180 inner += f64::from(u8::from(d > radius + half));181 outer += f64::from(u8::from(d > radius - half));182 }183 }184 }185 let cell = 8.0 * step.powi(3);186 [mid * cell, inner * cell, outer * cell]187}188189// PERIOD190191fn periodic(radius: f64) -> f64 {192 let (mut total, mut weight, mut reach) = (20.0 / 27.0, 1.0, radius);193 for _ in 0..=DEPTH {194 total += weight * exact(reach);195 weight *= 27.0 / 20.0;196 reach /= 3.0;197 }198 radius.powf(dimension() - 3.0) * total199}200201fn golden(f: &dyn Fn(f64) -> f64, mut a: f64, mut b: f64, sign: f64) -> f64 {202 let ratio = (5f64.sqrt() - 1.0) / 2.0;203 for _ in 0..200 {204 let (x, y) = (b - ratio * (b - a), a + ratio * (b - a));205 if sign * f(x) > sign * f(y) {206 b = y;207 } else {208 a = x;209 }210 }211 (a + b) / 2.0212}213214fn main() {215 let clock = Instant::now();216 println!("THRESHOLDS");217 for m in 1..=5 {218 let half = 3f64.powi(-m - 1) / 2.0;219 println!(220 "delta_{m} = sqrt(1/36 + s_{m}^2/4) = {:.12}",221 (1.0 / 36.0 + half * half).sqrt()222 );223 }224 println!(225 "arms swallowed at sqrt(10)/18 = {:.12}",226 10f64.sqrt() / 18.0227 );228 println!("plus swallowed at sqrt(2)/6 = {:.12}", COVER);229 println!();230 println!(231 "LEVELS: closed deep, lab quadrature by level, row raster by level (midpoint, bracket)"232 );233 let radii = [234 0.22,235 0.2,236 0.18,237 0.174,238 0.17,239 0.168,240 0.1675,241 0.167,242 0.16675,243 0.1667,244 0.16667,245 EDGE,246 0.148,247 1.0 / 8.0,248 1.0 / 12.0,249 ];250 let mut worst_ratio: f64 = 0.0;251 let mut worst_centre: f64 = 0.0;252 let mut worst_gap: f64 = 0.0;253 for &radius in &radii {254 let line = radius.min(EDGE);255 let holes = crossing(line, 12);256 let parts: Vec<f64> = holes257 .iter()258 .map(|h| h.rows.len() as f64 * quadrature(radius, h, line))259 .collect();260 let sum: f64 = parts.iter().sum();261 worst_gap = worst_gap.max((deep(radius) - sum).abs());262 println!(263 "radius {radius:.6} T = {:.12} deep = {:.6e} lab sum = {:.6e} gap = {:.1e}",264 exact(radius),265 deep(radius),266 sum,267 (deep(radius) - sum).abs()268 );269 for (h, part) in holes.iter().zip(&parts).filter(|(h, _)| h.level <= TOP) {270 if *part == 0.0 {271 println!(272 " level {} x {}: closed 0, raster skipped",273 h.level,274 h.rows.len()275 );276 continue;277 }278 let [mid, inner, outer] = rows_raster(radius, h, line);279 if radius >= EDGE {280 worst_ratio = worst_ratio.max((mid / part - 1.0).abs());281 }282 assert!(283 inner <= *part && *part <= outer,284 "bracket misses level {} at {radius}",285 h.level286 );287 println!(288 " level {} x {}: lab {:.6e} raster {:.6e} in [{:.6e}, {:.6e}]",289 h.level,290 h.rows.len(),291 part,292 mid,293 inner,294 outer295 );296 }297 if radius >= EDGE {298 let far = outside(radius, &holes, line);299 assert!(300 far <= radius,301 "an uncovered point outside the boxes at {radius}"302 );303 let [mid, inner, outer] = centre_raster(radius);304 let closed = PLUS - exact(radius) - 24.0 * deep(radius);305 assert!(306 inner <= closed && closed <= outer,307 "centre bracket misses at {radius}"308 );309 worst_centre = worst_centre.max((mid / closed - 1.0).abs());310 println!(" arm cells outside the boxes: largest distance {far:.9} <= radius");311 println!(" centre deficit: closed {closed:.9e} raster {mid:.9e} in [{inner:.6e}, {outer:.6e}]");312 }313 }314 println!("largest relative gap on [1/6, sqrt(2)/6], row raster midpoint against the level integral: {worst_ratio:.2e}");315 println!(316 "largest gap at all {} radii, crate deep against the lab quadrature: {worst_gap:.1e}",317 radii.len()318 );319 println!("largest relative gap on [1/6, sqrt(2)/6], centre raster midpoint against the closed deficit: {worst_centre:.2e}");320 println!();321 println!("PERIOD: p from exact T on u = ln(1/eps) over one period from ln(1/(sqrt(2)/6))");322 let start = (1.0 / COVER).ln();323 let span = 3f64.ln();324 let at = |u: f64| periodic((-u).exp());325 let grid = |n: usize| -> Vec<(f64, f64)> {326 (0..n)327 .map(|i| start + span * (i as f64 + 0.5) / n as f64)328 .map(|u| (u, at(u)))329 .collect()330 };331 let walk = grid(STEPS);332 let step = span / STEPS as f64;333 let (top_u, _) =334 walk.iter().copied().fold(335 (0.0, f64::NEG_INFINITY),336 |b, x| if x.1 > b.1 { x } else { b },337 );338 let (bottom_u, _) =339 walk.iter()340 .copied()341 .fold((0.0, f64::INFINITY), |b, x| if x.1 < b.1 { x } else { b });342 let crest = golden(&at, top_u - step, top_u + step, 1.0);343 let trough = golden(&at, bottom_u - step, bottom_u + step, -1.0);344 let (high, low) = (at(crest), at(trough));345 println!("maximum p = {high:.9} at eps = {:.9}", (-crest).exp());346 println!("minimum p = {low:.9} at eps = {:.9}", (-trough).exp());347 println!(348 "swing (max - min)/min = {:.6} %",349 100.0 * (high - low) / low350 );351 let mean = |n: usize| grid(n).iter().map(|x| x.1).sum::<f64>() / n as f64;352 println!(353 "logarithmic mean of p over the period: {:.9} ({} steps), {:.9} ({} steps)",354 mean(STEPS),355 STEPS,356 mean(2 * STEPS),357 2 * STEPS358 );359 let window: Vec<f64> = walk360 .iter()361 .filter(|x| (-x.0).exp() > EDGE)362 .map(|x| x.1)363 .collect();364 let window_high = window.iter().copied().fold(f64::NEG_INFINITY, f64::max);365 let window_low = window.iter().copied().fold(f64::INFINITY, f64::min);366 println!("on the phases (1/6, sqrt(2)/6]: p runs over [{window_low:.9}, {window_high:.9}], {} of {STEPS} steps", window.len());367 let falling = window.windows(2).all(|pair| pair[1] > pair[0]);368 println!("p falls as eps grows, at every step across (1/6, sqrt(2)/6]: {falling}");369 for eps in [1.0 / 12.0, 1.0 / 8.0, EDGE, 0.2, COVER] {370 let edge = profile(eps).map_or("none".to_string(), |v| format!("{v:.9}"));371 println!(372 "p({eps:.9}) = {:.9} upper edge (Deep = 0) {edge}",373 periodic(eps)374 );375 }376 println!();377 println!("runtime {:.1} s", clock.elapsed().as_secs_f64());378}