lattice.rs
4.8 kB · rust · 147 lines
1use crate::{rgba, theme, Fault, Pixels};2use mrlycore::{json, Json};3use mrlynum::factor::gcd;4use mrlynum::{lattice, series};5use wasm_bindgen::prelude::*;67/// Walks the Farey sequence of the order: each node's numerator, denominator and brightness, as JSON.8#[wasm_bindgen]9pub fn farey(order: usize) -> String {10 let nodes: Vec<Json> = lattice::farey(order)11 .iter()12 .map(|node| json!([node.num, node.den, node.brightness]))13 .collect();14 Json::Array(nodes).to_string()15}1617/// Sieves the totients of zero through the limit.18#[wasm_bindgen]19pub fn totients(limit: usize) -> Vec<u32> {20 lattice::totients(limit).iter().map(|&v| v as u32).collect()21}2223/// Reads the Farey stack of the order: the nodes the walk lit, one plus the totients summed, whether the two agree, and the primes as the scales of maximal novelty, as JSON.24#[wasm_bindgen]25pub fn farey_novelty(order: usize) -> String {26 let phi = lattice::totients(order);27 let novel = 1 + phi.iter().skip(1).sum::<u64>();28 let lit = lattice::farey(order).len() as u64;29 let primes: Vec<usize> = (2..=order).filter(|&n| phi[n] == n as u64 - 1).collect();30 json!({31 "lit": lit,32 "novel": novel,33 "match": lit == novel,34 "primes": primes,35 })36 .to_string()37}3839// VISIBLE4041const WINDOW: usize = 4000;42const SHEET: usize = 2048;43const STOPS: usize = 512;4445fn window(n: usize) -> Result<usize, Fault> {46 if n == 0 || n > WINDOW {47 return Err(Fault::new(format!(48 "the window must be between 1 and {WINDOW}."49 )));50 }51 Ok(n)52}5354fn depth(dimension: u32) -> Result<u32, Fault> {55 if !(2..=8).contains(&dimension) {56 return Err(Fault::new("the dimension must be between 2 and 8."));57 }58 Ok(dimension)59}6061fn shade(layer: usize, layers: bool) -> [u8; 4] {62 let ink = theme();63 if layer == 1 {64 return rgba(ink.blue);65 }66 if !layers {67 return rgba(ink.line);68 }69 let t = 1.0 / layer as f64;70 let step = |ground: u8, tone: u8| {71 (f64::from(ground) + (f64::from(tone) - f64::from(ground)) * t).round() as u872 };73 let (ground, dim) = (ink.ground, ink.dim);74 [75 step(ground.r, dim.r),76 step(ground.g, dim.g),77 step(ground.b, dim.b),78 255,79 ]80}8182/// Reads the window the stack lights in the dimension: the lit points, their density, the limit one over zeta, the constant the count recovers, the value it walks to and the gap between them, as JSON.83#[wasm_bindgen]84pub fn visible_read(n: usize, dimension: u32) -> Result<String, Fault> {85 let (n, dimension) = (window(n)?, depth(dimension)?);86 let lit = series::visible(n, dimension);87 let total = (n as u128).pow(dimension);88 let constant = lattice::recovered(n, dimension);89 let even = dimension.is_multiple_of(2);90 let truth = if even {91 std::f64::consts::PI92 } else {93 lattice::zeta_whole(dimension)94 };95 Ok(json!({96 "n": n,97 "dimension": dimension,98 "lit": lit.to_string(),99 "total": total.to_string(),100 "density": lit as f64 / total as f64,101 "limit": lattice::visible_density(dimension),102 "name": if even { "pi".to_string() } else { format!("zeta({dimension})") },103 "constant": constant,104 "truth": truth,105 "error": (constant - truth).abs(),106 })107 .to_string())108}109110/// Paints the corner window of the plane lattice at the pixel side asked for, the origin at the lower left: a point of coprime coordinates in blue, a hidden point in the dim of the stack layer that owns it, flat when the layers are off.111#[wasm_bindgen]112pub fn visible_pixels(n: usize, side: usize, layers: bool) -> Result<Pixels, Fault> {113 let n = window(n)?;114 if !(16..=SHEET).contains(&side) {115 return Err(Fault::new(format!(116 "the side must be between 16 and {SHEET} pixels."117 )));118 }119 let mut colors = Vec::with_capacity(side * side);120 for py in 0..side {121 let b = n - py * n / side;122 for px in 0..side {123 let a = px * n / side + 1;124 colors.push(shade(gcd(a, b), layers));125 }126 }127 Ok(Pixels::of(side, side, colors))128}129130/// Walks the window up to n at the count of stops and returns each stop as a window and the constant its count recovers, two numbers a stop, so the approach can be drawn.131#[wasm_bindgen]132pub fn visible_walk(n: usize, dimension: u32, stops: usize) -> Result<Vec<f64>, Fault> {133 let (n, dimension) = (window(n)?, depth(dimension)?);134 if !(2..=STOPS).contains(&stops) {135 return Err(Fault::new(format!(136 "the stops must be between 2 and {STOPS}."137 )));138 }139 let stops = stops.min(n);140 let mut out = Vec::with_capacity(stops * 2);141 for k in 1..=stops {142 let at = (n * k / stops).max(1);143 out.push(at as f64);144 out.push(lattice::recovered(at, dimension));145 }146 Ok(out)147}