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}