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}