census.rs

4.2 kB · rust · 136 lines

1use super::graph::edge_graph;2use super::models::Cell2d;3use crate::dim::census;4use mrlycore::errors::Result;56pub use crate::dim::census::{edges, vertices};78/// One reading of a cell: its sites, its outline and its topology.9#[derive(Clone, Debug, PartialEq, Eq)]10pub struct Census {11    /// The count of filled sites.12    pub fills: usize,13    /// The count of empty sites.14    pub voids: usize,15    /// The count of filled faces open to emptiness or the border.16    pub perimeter: u128,17    /// The count of distinct corners the filled sites touch.18    pub vertices: usize,19    /// The count of distinct unit edges the filled sites carry.20    pub edges: usize,21    /// The Euler characteristic, vertices less edges plus filled sites.22    pub euler: i64,23}2425/// Counts the filled sites of the cell.26pub fn fills(cell: &Cell2d) -> usize {27    census::fills(cell)28}2930/// Counts the empty sites of the cell.31pub fn voids(cell: &Cell2d) -> usize {32    census::voids(cell)33}3435/// Counts the faces of filled sites open to emptiness or the border.36pub fn perimeter(cell: &Cell2d) -> u128 {37    census::exposure(cell)38}3940/// Returns the Euler characteristic of the filled sites, vertices less edges plus faces.41///42/// ```43/// let solid = mrlymath::two::ones(4, 1).unwrap();44/// assert_eq!(mrlymath::two::census::euler(&solid).unwrap(), 1);45/// let ring = mrlymath::two::carpet(3, 1).unwrap();46/// assert_eq!(mrlymath::two::census::euler(&ring).unwrap(), 0);47/// ```48pub fn euler(cell: &Cell2d) -> Result<i64> {49    let outline = edge_graph(cell)?;50    Ok(outline.nodes.len() as i64 - outline.branches.len() as i64 + fills(cell) as i64)51}5253/// Takes the cell's full census in one reading.54///55/// ```56/// let cell = mrlymath::two::carpet(3, 1).unwrap();57/// let census = mrlymath::two::census::census(&cell).unwrap();58/// assert_eq!((census.fills, census.voids, census.perimeter), (8, 1, 16));59/// assert_eq!((census.vertices, census.edges, census.euler), (16, 24, 0));60/// ```61pub fn census(cell: &Cell2d) -> Result<Census> {62    let outline = edge_graph(cell)?;63    let (vertices, edges) = (outline.nodes.len(), outline.branches.len());64    let faces = fills(cell);65    Ok(Census {66        fills: faces,67        voids: voids(cell),68        perimeter: perimeter(cell),69        vertices,70        edges,71        euler: vertices as i64 - edges as i64 + faces as i64,72    })73}7475#[cfg(test)]76mod tests {77    use super::*;78    use crate::formulas;79    use crate::two::designs;80    #[test]81    fn census_matches_formulas() {82        for code in [7u128, 14, 9, 5] {83            for level in 1..4u32 {84                let cell = designs::create(code, 3, level as usize, 0, 2).unwrap();85                assert_eq!(86                    fills(&cell) as u128,87                    formulas::fill(code, 3, 2, level, 2).unwrap(),88                    "code={code} l={level}"89                );90                assert_eq!(91                    voids(&cell) as u128,92                    formulas::void(code, 3, 2, level, 2).unwrap()93                );94            }95        }96    }97    #[test]98    fn carpet_census() {99        let c = designs::carpet(3, 1).unwrap();100        assert_eq!(101            census(&c).unwrap(),102            Census {103                fills: 8,104                voids: 1,105                perimeter: 16,106                vertices: 16,107                edges: 24,108                euler: 0,109            }110        );111    }112    #[test]113    fn solid_blocks_are_discs() {114        for n in 1..6 {115            let solid = designs::ones(n, 1).unwrap();116            let reading = census(&solid).unwrap();117            assert_eq!(reading.vertices, (n + 1) * (n + 1), "n={n}");118            assert_eq!(reading.edges, 2 * n * (n + 1));119            assert_eq!(reading.euler, 1);120        }121    }122    #[test]123    fn every_corner_is_touched_but_the_net() {124        for n in [3usize, 5, 7] {125            for cell in [designs::carpet(n, 1), designs::void(n, 1)] {126                assert_eq!(vertices(&cell.unwrap()).unwrap(), (n + 1) * (n + 1));127            }128            assert!(vertices(&designs::net(n, 1).unwrap()).unwrap() < (n + 1) * (n + 1));129        }130    }131    #[test]132    fn euler_counts_the_holes() {133        assert_eq!(euler(&designs::carpet(3, 2).unwrap()).unwrap(), -8);134        assert_eq!(edges(&designs::ones(3, 1).unwrap()).unwrap(), 24);135    }136}