main.rs

5.0 kB · rust · 191 lines

1mod design;23use std::collections::BTreeSet;45use mrlynum::factor::gcd;6use mrlynum::lattice::{farey, new_nodes, nodes, totients};78const QS: [usize; 7] = [125, 250, 500, 1000, 2000, 4000, 8000];910const SMALL: [usize; 4] = [10, 30, 60, 125];1112fn literal_stack(q: usize) -> BTreeSet<(usize, usize)> {13    let mut lit = BTreeSet::new();14    for n in 1..=q {15        for k in 1..=n {16            let g = gcd(k, n);17            lit.insert((k / g, n / g));18        }19    }20    lit21}2223fn window_pairs(q: usize) -> BTreeSet<(usize, usize)> {24    nodes(q)25        .into_iter()26        .filter(|node| node.num > 0)27        .map(|node| (node.num as usize, node.den as usize))28        .collect()29}3031fn walk_pairs(q: usize) -> BTreeSet<(usize, usize)> {32    farey(q)33        .into_iter()34        .filter(|node| node.num > 0)35        .map(|node| (node.num as usize, node.den as usize))36        .collect()37}3839fn drawn_by(q: usize, pair: (usize, usize)) -> usize {40    let (a, b) = pair;41    (1..=q).filter(|n| (a * n) % b == 0).count()42}4344fn brightness_ok(q: usize) -> bool {45    nodes(q)46        .into_iter()47        .filter(|node| node.num > 0)48        .all(|node| {49            let b = node.den as usize;50            node.brightness == (q / b) as u64 && drawn_by(q, (node.num as usize, b)) == q / b51        })52}5354fn totient_sum(q: usize, phi: &[u64]) -> u64 {55    phi[1..=q].iter().sum()56}5758fn meter_walk(q: usize, m: u64) -> (f64, f64, u64) {59    let mut s1 = 0.0f64;60    let mut s2 = 0.0f64;61    let mut j = 0u64;62    for node in farey(q) {63        if node.num == 0 {64            continue;65        }66        j += 1;67        let delta = node.num as f64 / node.den as f64 - j as f64 / m as f64;68        s1 += delta.abs();69        s2 += delta * delta;70    }71    (s1, s2, j)72}7374fn meter_window(q: usize, m: u64) -> (f64, f64, u64) {75    let mut s1 = 0.0f64;76    let mut s2 = 0.0f64;77    let mut j = 0u64;78    for node in nodes(q) {79        if node.num == 0 {80            continue;81        }82        j += 1;83        let delta = node.num as f64 / node.den as f64 - j as f64 / m as f64;84        s1 += delta.abs();85        s2 += delta * delta;86    }87    (s1, s2, j)88}8990fn verdict(ok: bool) -> &'static str {91    if ok {92        "PASS"93    } else {94        "FAIL"95    }96}9798fn stack() {99    let phi = totients(*QS.last().unwrap());100    println!("LIT NODES ARE THE FAREY NODES");101    println!("      Q   lit set   window   mediant walk   sum phi(k)   floor(Q/b)   agree");102    for q in SMALL {103        let lit = literal_stack(q);104        let win = window_pairs(q);105        let walk = walk_pairs(q);106        let control = totient_sum(q, &phi);107        let bright = brightness_ok(q);108        let ok = lit == win && win == walk && lit.len() as u64 == control && bright;109        println!(110            "  {:>5}   {:>7}   {:>6}   {:>12}   {:>10}   {:>10}   {}",111            q,112            lit.len(),113            win.len(),114            walk.len(),115            control,116            verdict(bright),117            verdict(ok)118        );119    }120121    let fresh = (2..=60).all(|n| new_nodes(n) == phi[n]);122    println!(123        "  new nodes at scale n equals phi(n), n = 2..60 : {}",124        verdict(fresh)125    );126}127128fn meter() {129    let phi = totients(*QS.last().unwrap());130    println!("THE DISCREPANCY METER");131    println!("      Q       nodes   sum phi(k)     S2*Q   S1/sqrt(Q)   exp S2   exp S1");132    let mut prev: Option<(usize, f64, f64)> = None;133    for q in QS {134        let m = totient_sum(q, &phi);135        let (s1, s2, seen) = meter_walk(q, m);136        let (mut e2, mut e1) = ("-".to_string(), "-".to_string());137        if let Some((pq, p1, p2)) = prev {138            let span = (q as f64 / pq as f64).ln();139            e2 = format!("{:+.3}", (s2 / p2).ln() / span);140            e1 = format!("{:+.3}", (s1 / p1).ln() / span);141        }142        prev = Some((q, s1, s2));143        println!(144            "  {:>5}  {:>10}  {:>11}   {:.4}       {:.4}   {:>6}   {:>6}",145            q,146            seen,147            m,148            s2 * q as f64,149            s1 / (q as f64).sqrt(),150            e2,151            e1152        );153    }154155    println!();156    println!("CROSS-CHECK  the sorted window route against the mediant walk");157    println!("      Q       nodes     S2*Q   S1/sqrt(Q)   agrees");158    for q in [125usize, 250, 500, 1000] {159        let m = totient_sum(q, &phi);160        let (w1, w2, wn) = meter_window(q, m);161        let (r1, r2, rn) = meter_walk(q, m);162        let ok = wn == rn && wn == m && (w1 - r1).abs() < 1e-9 && (w2 - r2).abs() < 1e-12;163        println!(164            "  {:>5}  {:>10}   {:.4}       {:.4}   {}",165            q,166            wn,167            w2 * q as f64,168            w1 / (q as f64).sqrt(),169            verdict(ok)170        );171    }172}173174fn main() {175    let want = std::env::args().nth(1);176    let pick = |name: &str| match want.as_deref() {177        Some(verb) => verb == name,178        None => true,179    };180    if pick("stack") {181        stack();182        println!();183    }184    if pick("meter") {185        meter();186        println!();187    }188    if pick("design") {189        design::run();190    }191}