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}