memory.rs
3.9 kB · rust · 118 lines
1use crate::{Fault, Grid};2use mrlycore::json;3use mrlynum::memory::{self, Rule};4use wasm_bindgen::prelude::*;56/// The sheet budget: the most sites one drawing may carry.7pub const SITES: usize = 1 << 16;89fn sites(dimension: usize, level: usize) -> u64 {10 let side = 1u64 << level;11 if dimension == 1 {12 side * level as u6413 } else {14 side.pow(dimension as u32)15 }16}1718/// Returns the span `k D` a rule code is held inside, the crate's own bound.19#[wasm_bindgen]20pub fn memory_span() -> usize {21 memory::SPAN22}2324/// Returns the deepest level a sheet is drawn at in a dimension: the largest `L` whose sites are inside the sheet budget.25#[wasm_bindgen]26pub fn memory_cap(dimension: usize) -> usize {27 let mut level = 1;28 while level < 16 && sites(dimension, level + 1) <= SITES as u64 {29 level += 1;30 }31 level32}3334fn rule(dimension: usize, width: usize, code: &str) -> Result<Rule, Fault> {35 let code: u64 = code36 .trim()37 .parse()38 .map_err(|_| Fault::new(format!("code {code:?} is not a whole number.")))?;39 Ok(Rule::new(dimension, width, code)?)40}4142/// Reads a memory rule: its shape, its alphabet, its allowed windows, the accepted word counts to the level, the Perron root, the growth exponent and the memory number `kappa = log_2(card W) / k - log_2 rho`, as JSON.43#[wasm_bindgen]44pub fn memory_read(45 dimension: usize,46 width: usize,47 code: &str,48 levels: usize,49) -> Result<String, Fault> {50 let rule = rule(dimension, width, code)?;51 let rho = memory::perron(&rule);52 let kappa = memory::kappa(&rule);53 Ok(json!({54 "dimension": rule.dimension,55 "width": rule.width,56 "code": rule.code.to_string(),57 "codes": rule.codes().to_string(),58 "letters": rule.letters(),59 "windows": rule.windows(),60 "states": rule.states(),61 "alphabet": rule.alphabet(),62 "allowed": (0..rule.windows()).map(|w| rule.allowed(w)).collect::<Vec<bool>>(),63 "window_count": memory::allowed_windows(&rule),64 "counts": memory::counts(&rule, levels),65 "perron": rho,66 "exponent": if rho > 0.0 { rho.log2() } else { f64::NEG_INFINITY },67 "kappa": if kappa.is_finite() { kappa } else { f64::INFINITY },68 })69 .to_string())70}7172/// Draws the accepted words of a memory rule as a byte grid, one byte a site.73///74/// At dimension one the sheet is the Cantor staircase: one row a level, the row of level `j` filled on the intervals its accepted words hold.75/// At dimension two the sheet is the `2^L` by `2^L` grid of the level's accepted cells, which at width one is the design of the same code cell for cell.76#[wasm_bindgen]77pub fn memory_sheet(78 dimension: usize,79 width: usize,80 code: &str,81 level: usize,82) -> Result<Grid, Fault> {83 let rule = rule(dimension, width, code)?;84 if !(1..=16).contains(&level) {85 return Err(Fault::new(format!("level {level} is out of range.")));86 }87 let side = 1usize << level;88 let count = sites(dimension, level);89 if count > SITES as u64 {90 return Err(Fault::new(format!(91 "level {level} at dimension {dimension} asks for {count} sites, over the sheet budget of {SITES}."92 )));93 }94 if dimension == 1 {95 let mut types = vec![0u8; side * level];96 for depth in 1..=level {97 let span = 1usize << (level - depth);98 for cell in memory::cells(&rule, depth) {99 let start = cell as usize * span;100 types[(depth - 1) * side + start..(depth - 1) * side + start + span].fill(1);101 }102 }103 return Ok(Grid {104 width: side as u32,105 height: level as u32,106 types,107 });108 }109 let mut types = vec![0u8; side.pow(dimension as u32)];110 for cell in memory::cells(&rule, level) {111 types[cell as usize] = 1;112 }113 Ok(Grid {114 width: side as u32,115 height: (types.len() / side) as u32,116 types,117 })118}