research-integers.rs
5.0 kB · rust · 176 lines
1use mrlycore::errors::Result;2use mrlycore::MrlyError;3use mrlyfig::ink::Ramp;4use mrlyfig::out::root;5use mrlyfig::{ink, save, Board, Grid};6use mrlylab::ledger::{keys, terms, Cost, Key, Tier};7use std::path::PathBuf;89const NAME: &str = "research-integers";10const CAP: usize = 48;11const CELLS: u128 = 100_000;12const CEILING: i128 = 100_000;13const WINDOW: usize = 10_000;14const SIDE: usize = 100;15const BLOCK: usize = 8;1617// CENSUS1819fn footprint(key: &Key, index: usize) -> Option<u128> {20 let (number, level) = key.axis.place(index, key.number());21 let number = number as u128;22 let dimension = key.dimension as u32;23 match key.measure.cost() {24 Cost::Closed => Some(1),25 Cost::Convolved => {26 let tile = number.checked_pow(dimension)?;27 let side = number.checked_pow(level)?;28 let span = key.dimension as u128 * (side - 1) + 1;29 tile.checked_add(span.checked_mul(level as u128)?)30 }31 Cost::Grid => number.checked_pow(dimension.checked_mul(level)?),32 }33}3435fn allowance(key: &Key) -> usize {36 (0..CAP)37 .take_while(|&index| footprint(key, index).is_some_and(|cells| cells <= CELLS))38 .count()39}4041fn ceiling_stop(read: &[i128]) -> Option<usize> {42 let mut previous: Option<i128> = None;43 for (index, &term) in read.iter().enumerate() {44 if previous.is_some_and(|last| term <= last) {45 return None;46 }47 if term > CEILING {48 return Some(index);49 }50 previous = Some(term);51 }52 None53}5455fn rendered(key: &Key) -> Option<Vec<i128>> {56 let allowed = allowance(key);57 let mut count = BLOCK.min(allowed);58 loop {59 let (read, capped) = terms(key, count, CELLS).ok()?;60 if let Some(edge) = ceiling_stop(&read) {61 return Some(read[..=edge].to_vec());62 }63 if capped || read.len() < count || count >= allowed {64 return Some(read);65 }66 count = (count * 2).min(allowed);67 }68}6970fn census() -> Vec<u32> {71 let mut counts = vec![0u32; WINDOW + 1];72 let mut rows = 0usize;73 for tier in Tier::ALL {74 for key in keys(tier) {75 rows += 1;76 let Some(window) = rendered(&key) else {77 continue;78 };79 let mut written: Vec<usize> = window80 .iter()81 .filter(|&&term| term >= 1 && term <= WINDOW as i128)82 .map(|&term| term as usize)83 .collect();84 written.sort_unstable();85 written.dedup();86 for value in written {87 counts[value] += 1;88 }89 }90 }91 assert_eq!(rows, 18066);92 let never = counts[1..].iter().filter(|&&c| c == 0).count();93 let once = counts[1..].iter().filter(|&&c| c == 1).count();94 let many = counts[1..].iter().filter(|&&c| c > 1).count();95 assert_eq!((never, once, many), (3589, 765, 5646));96 counts97}9899// DATA100101fn path() -> PathBuf {102 root()103 .join("files")104 .join("figures")105 .join("data")106 .join(format!("{NAME}.json"))107}108109fn write_data(counts: &[u32]) -> Result<PathBuf> {110 let file = path();111 let folder = file.parent().unwrap().to_path_buf();112 std::fs::create_dir_all(&folder)113 .map_err(|e| MrlyError::Value(format!("cannot make {folder:?}: {e}")))?;114 let body: Vec<String> = counts.iter().map(|count| count.to_string()).collect();115 std::fs::write(&file, format!("[{}]", body.join(",")))116 .map_err(|e| MrlyError::Value(format!("cannot write {file:?}: {e}")))?;117 Ok(file)118}119120fn read_data() -> Result<Vec<u32>> {121 let file = path();122 let text = std::fs::read_to_string(&file)123 .map_err(|e| MrlyError::Value(format!("cannot read {file:?}: {e}; run -- compute")))?;124 Ok(text125 .trim()126 .trim_start_matches('[')127 .trim_end_matches(']')128 .split(',')129 .filter_map(|token| token.trim().parse().ok())130 .collect())131}132133// PRESS134135fn compute() -> Result<()> {136 let counts = census();137 let peak = *counts.iter().max().unwrap();138 assert_eq!(peak, counts[16]);139 let file = write_data(&counts)?;140 println!(141 "{NAME} census {} cells peak {peak} -> {file:?}",142 counts.len()143 );144 Ok(())145}146147fn draw() -> Result<()> {148 let counts = read_data()?;149 assert_eq!(counts.len(), WINDOW + 1);150 let peak = *counts.iter().max().unwrap() as f64;151 assert!(peak > 0.0);152 let mut board = Board::square();153 let area = board.frame(0.08);154 let ramp = Ramp::tone(ink::dim(), ink::yellow());155 let grid = Grid::new(area, SIDE, SIDE, 0.12);156 let scale = (1.0 + peak).ln();157 for row in 0..SIDE {158 for col in 0..SIDE {159 let count = counts[row * SIDE + col + 1];160 if count == 0 {161 continue;162 }163 let tone = ramp.at((1.0 + count as f64).ln() / scale);164 grid.fill(&mut board, col, row, tone);165 }166 }167 save(NAME, &board)?;168 Ok(())169}170171fn main() -> Result<()> {172 if std::env::args().nth(1).as_deref() == Some("compute") {173 return compute();174 }175 draw()176}