demo-integers.rs
6.0 kB · rust · 201 lines
1use mrlycore::errors::Result;2use mrlycore::MrlyError;3use mrlyfig::out::root;4use mrlyfig::{ink, save, Board, Frame, Grid};5use mrlylab::ledger::{keys, terms, Cost, Key, Tier};6use std::path::PathBuf;78const NAME: &str = "demo-integers";9const CAP: usize = 48;10const CELLS: u128 = 100_000;11const WINDOW: i128 = 1_000;12const BLOCK: usize = 8;13const DEPTHS: [usize; 4] = [8, 16, 32, 48];14const COLS: usize = 40;15const LINES: usize = 25;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 > WINDOW {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<Vec<u32>> {71 let width = WINDOW as usize + 1;72 let mut counts = vec![vec![0u32; width]; DEPTHS.len()];73 let mut rows = 0usize;74 for tier in Tier::ALL {75 for key in keys(tier) {76 rows += 1;77 let Some(window) = rendered(&key) else {78 continue;79 };80 for (slot, depth) in DEPTHS.iter().enumerate() {81 let head = &window[..window.len().min(*depth)];82 let mut written: Vec<usize> = head83 .iter()84 .filter(|&&term| (1..=WINDOW).contains(&term))85 .map(|&term| term as usize)86 .collect();87 written.sort_unstable();88 written.dedup();89 for value in written {90 counts[slot][value] += 1;91 }92 }93 }94 }95 assert_eq!(rows, 18066);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: &[Vec<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 fields: Vec<String> = DEPTHS115 .iter()116 .zip(counts.iter())117 .map(|(depth, tally)| {118 let body: Vec<String> = tally.iter().map(|count| count.to_string()).collect();119 format!("\"d{depth}\":[{}]", body.join(","))120 })121 .collect();122 std::fs::write(&file, format!("{{{}}}", fields.join(",")))123 .map_err(|e| MrlyError::Value(format!("cannot write {file:?}: {e}")))?;124 Ok(file)125}126127fn field(text: &str, name: &str) -> Vec<u32> {128 let head = format!("\"{name}\":[");129 let Some(start) = text.find(&head) else {130 return Vec::new();131 };132 let body = &text[start + head.len()..];133 let end = body.find(']').unwrap_or(0);134 body[..end]135 .split(',')136 .filter_map(|token| token.parse().ok())137 .collect()138}139140fn read_data() -> Result<Vec<Vec<u32>>> {141 let file = path();142 let raw = std::fs::read_to_string(&file)143 .map_err(|e| MrlyError::Value(format!("cannot read {file:?}: {e}; run -- compute")))?;144 let text: String = raw.chars().filter(|c| !c.is_whitespace()).collect();145 Ok(DEPTHS146 .iter()147 .map(|depth| field(&text, &format!("d{depth}")))148 .collect())149}150151// PRESS152153fn compute() -> Result<()> {154 let counts = census();155 let missed: Vec<usize> = counts156 .iter()157 .map(|row| row[1..].iter().filter(|&&count| count == 0).count())158 .collect();159 assert!(missed.windows(2).all(|pair| pair[1] < pair[0]));160 assert!(missed[DEPTHS.len() - 1] > 0);161 let file = write_data(&counts)?;162 println!("{NAME} census {:?} missed {missed:?} -> {file:?}", DEPTHS);163 Ok(())164}165166fn draw() -> Result<()> {167 let counts = read_data()?;168 assert_eq!(counts.len(), DEPTHS.len());169 assert!(counts170 .iter()171 .all(|tally| tally.len() == WINDOW as usize + 1));172 let mut board = Board::square();173 let area = board.frame(0.08);174 let panels = Grid::new(area, 2, 2, 0.06);175 let mut drawn = 0usize;176 for (slot, tally) in counts.iter().enumerate() {177 let (x, y, w, h) = panels.cell(slot % 2, slot / 2);178 let cells = Grid::new(Frame::new(x, y, w, h), COLS, LINES, 0.10);179 for row in 0..LINES {180 for col in 0..COLS {181 let tone = match tally[row * COLS + col + 1] {182 0 => ink::orange(),183 1 => ink::dim(),184 _ => ink::blue(),185 };186 cells.fill(&mut board, col, row, tone);187 drawn += 1;188 }189 }190 }191 assert_eq!(drawn, 4 * WINDOW as usize);192 save(NAME, &board)?;193 Ok(())194}195196fn main() -> Result<()> {197 if std::env::args().nth(1).as_deref() == Some("compute") {198 return compute();199 }200 draw()201}