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}