rules.rs
1.8 kB · rust · 64 lines
1use mrlycore::errors::{value_error, Result};2use mrlycore::tensor::Tensor;34/// The default residue base.5pub const BASE: usize = 2;67/// Builds a hypercube of the given side and rank, marking each cell whose coordinate residues satisfy the rule.8///9/// ```10/// let carpet = |p: &[u8]| p.iter().map(|&b| b as usize).sum::<usize>() <= 1;11/// let t = mrlymath::rules::render(carpet, 3, 2, 2).unwrap();12/// assert_eq!(t.bytes(), vec![1, 1, 1, 1, 0, 1, 1, 1, 1]);13/// ```14pub fn render<F>(rule: F, number: usize, dimension: usize, base: usize) -> Result<Tensor>15where16 F: Fn(&[u8]) -> bool,17{18 if number < 1 {19 return value_error("number must be at least 1.");20 }21 if dimension < 1 {22 return value_error("dimension must be at least 1.");23 }24 if base < 1 {25 return value_error("base must be at least 1.");26 }27 let mut out = Tensor::new(vec![number; dimension]);28 let mut residue = vec![0u8; dimension];29 for flat in 0..out.size() {30 let mut rem = flat;31 for axis in (0..dimension).rev() {32 residue[axis] = ((rem % number) % base) as u8;33 rem /= number;34 }35 out.bytes_mut()[flat] = rule(&residue) as u8;36 }37 Ok(out)38}3940/// Returns every axis but the free one.41pub fn tree_axes(dimension: usize, free_axis: usize) -> Vec<usize> {42 (0..dimension).filter(|&axis| axis != free_axis).collect()43}4445#[cfg(test)]46mod tests {47 use super::*;48 #[test]49 fn render_carpet_rule() {50 let t = render(51 |p| p.iter().map(|&b| b as usize).sum::<usize>() <= 1,52 3,53 2,54 2,55 )56 .unwrap();57 assert_eq!(t.bytes(), vec![1, 1, 1, 1, 0, 1, 1, 1, 1]);58 }59 #[test]60 fn render_rejects_bad_input() {61 assert!(render(|_| true, 0, 2, 2).is_err());62 assert!(render(|_| true, 3, 0, 2).is_err());63 }64}