six.rs
9.8 kB · rust · 304 lines
1use crate::core::error::{value_error, Result};2use crate::math::bang::code_to_corners;3use crate::math::bang::Code;4use crate::math::counts::counting::positions;5use std::collections::HashSet;67/// Returns the triangles of the full hexagon with side number to the level.8pub fn grid_triangles(number: usize, level: u32) -> u128 {9 6 * (number as u128).pow(2 * level)10}1112fn odd_k(number: usize) -> Result<u128> {13 if number.is_multiple_of(2) {14 return value_error("solid slice closed form is defined for odd number = 2k-1.");15 }16 Ok(number.div_ceil(2) as u128)17}1819/// Returns the core node count of the solid slice, defined for odd number.20///21/// # Errors22///23/// Errors at an even side number.24pub fn solid_slice_core_nodes(number: usize) -> Result<u128> {25 let k = odd_k(number)? as i128;26 Ok((24 * k * k - 24 * k + 6) as u128)27}2829/// Returns the core edge count of the solid slice, defined for odd number.30///31/// # Errors32///33/// Errors at an even side number.34pub fn solid_slice_core_edges(number: usize) -> Result<u128> {35 let k = odd_k(number)? as i128;36 Ok((36 * k * k - 42 * k + 12) as u128)37}3839/// Returns the triangle count of the solid slice, defined for odd number.40///41/// # Errors42///43/// Errors at an even side number.44pub fn solid_slice_triangles(number: usize) -> Result<u128> {45 odd_k(number)?;46 Ok(6 * (number as u128).pow(2))47}4849/// Returns the boundary edge count of the solid slice, defined for odd number.50///51/// # Errors52///53/// Errors at an even side number.54pub fn solid_slice_boundary(number: usize) -> Result<u128> {55 odd_k(number)?;56 Ok(6 * number as u128)57}5859/// Returns the vertex count of the solid slice, defined for odd number.60///61/// # Errors62///63/// Errors at an even side number.64pub fn solid_slice_vertices(number: usize) -> Result<u128> {65 let k = odd_k(number)? as i128;66 Ok((12 * k * k - 6 * k + 1) as u128)67}6869/// Returns the interior edge count of the solid slice, defined for odd number.70///71/// # Errors72///73/// Errors at an even side number.74pub fn solid_slice_interior(number: usize) -> Result<u128> {75 let k = odd_k(number)? as i128;76 Ok((36 * k * k - 42 * k + 12) as u128)77}7879/// Returns the centered hexagonal number at the index, the lattice points of a hexagon of side m-1.80pub fn centered_hexagonal(m: usize) -> u128 {81 let m = m as u128;82 3 * m * m - 3 * m + 183}8485/// Returns the distinct triangle-edge count of the solid slice, defined for odd number.86///87/// # Errors88///89/// Errors at an even side number.90pub fn solid_slice_edges(number: usize) -> Result<u128> {91 let k = odd_k(number)? as i128;92 Ok((36 * k * k - 30 * k + 6) as u128)93}9495/// Returns the filled triangle count of the code's pro projection at the given level, without rendering it.96///97/// # Errors98///99/// Errors when the code is out of range for a base-2 cube.100pub fn pro_fills(code: Code, number: usize, level: u32) -> Result<u128> {101 let filled = code_to_corners(code, 3, 2)?;102 let boundary = ((number - 1) % 2) as u8;103 let mut total: u128 = 0;104 for axis in 0..3 {105 let slab: u128 = filled106 .iter()107 .filter(|c| c[axis] == boundary)108 .map(|c| {109 (0..3)110 .filter(|&j| j != axis)111 .map(|j| positions(c[j] as usize, number, 2))112 .product::<u128>()113 })114 .sum();115 total += slab.pow(level);116 }117 Ok(2 * total)118}119120/// Returns the empty triangle count of the code's pro projection at the given level.121///122/// # Errors123///124/// Errors when the code is out of range for a base-2 cube.125pub fn pro_voids(code: Code, number: usize, level: u32) -> Result<u128> {126 Ok(grid_triangles(number, level) - pro_fills(code, number, level)?)127}128129/// Returns the filled triangle count of the code's cut section at the given level, without rendering it.130///131/// # Errors132///133/// Errors when the code is out of range for a base-2 cube.134pub fn cut_fills(code: Code, number: usize, level: u32) -> Result<u128> {135 let filled: HashSet<Vec<u8>> = code_to_corners(code, 3, 2)?.into_iter().collect();136 let scaled = number.pow(level);137 let size = 4 * scaled;138 let k = (3 * (size - 1)) / 2;139 let mut total: u128 = 0;140 for z in (0..size).step_by(2) {141 let target = k - z;142 let min_x = target.saturating_sub(size - 1);143 let max_x = (size - 1).min(target);144 for x in min_x..=max_x {145 let y = target - x;146 let (mut a, mut b, mut c) = (x / 4, y / 4, z / 4);147 let mut inside = true;148 for _ in 0..level {149 let corner = vec![150 (a % number % 2) as u8,151 (b % number % 2) as u8,152 (c % number % 2) as u8,153 ];154 if !filled.contains(&corner) {155 inside = false;156 break;157 }158 a /= number;159 b /= number;160 c /= number;161 }162 if inside {163 total += 1;164 }165 }166 }167 Ok(total)168}169170/// Returns the empty triangle count of the code's cut section at the given level.171///172/// # Errors173///174/// Errors when the code is out of range for a base-2 cube.175pub fn cut_voids(code: Code, number: usize, level: u32) -> Result<u128> {176 Ok(grid_triangles(number, level) - cut_fills(code, number, level)?)177}178179#[cfg(test)]180mod tests {181 use super::*;182 #[test]183 fn pro_and_cut_match_census() {184 use crate::math::six::{cut, pro};185 use crate::math::three;186 for code in [187 Code(0),188 Code(8),189 Code(17),190 Code(23),191 Code(129),192 Code(232),193 Code(255),194 ] {195 for number in [1usize, 2, 3, 4, 5, 7] {196 for level in 1..3u32 {197 if number.pow(level) > 9 {198 continue;199 }200 let cell = three::create(code, number, level as usize, 2).unwrap();201 let p = pro(&cell).unwrap();202 assert_eq!(203 pro_fills(code, number, level).unwrap(),204 p.cell.types().count(1) as u128,205 "pro code={code} n={number} l={level}"206 );207 assert_eq!(208 pro_voids(code, number, level).unwrap(),209 p.cell.types().count(0) as u128210 );211 let q = cut(&cell).unwrap();212 assert_eq!(213 cut_fills(code, number, level).unwrap(),214 q.cell.types().count(1) as u128,215 "cut code={code} n={number} l={level}"216 );217 assert_eq!(218 cut_voids(code, number, level).unwrap(),219 q.cell.types().count(0) as u128220 );221 }222 }223 }224 }225 #[test]226 fn menger_projections() {227 assert_eq!(pro_fills(Code(23), 3, 1).unwrap(), 48);228 assert_eq!(pro_fills(Code(23), 3, 2).unwrap(), 384);229 assert_eq!(cut_fills(Code(23), 3, 1).unwrap(), 42);230 assert_eq!(cut_fills(Code(23), 3, 2).unwrap(), 306);231 assert_eq!(cut_fills(Code(255), 3, 1).unwrap(), 54);232 }233 #[test]234 fn closed_forms_at_small_numbers() {235 assert_eq!(grid_triangles(3, 1), 54);236 assert_eq!(solid_slice_triangles(3).unwrap(), 54);237 assert_eq!(solid_slice_boundary(3).unwrap(), 18);238 assert!(solid_slice_vertices(4).is_err());239 assert_eq!(solid_slice_core_nodes(1).unwrap(), 6);240 assert_eq!(solid_slice_core_edges(1).unwrap(), 6);241 assert_eq!(solid_slice_core_nodes(3).unwrap(), 54);242 assert_eq!(solid_slice_core_edges(3).unwrap(), 72);243 assert_eq!(solid_slice_vertices(3).unwrap(), 37);244 assert_eq!(solid_slice_edges(1).unwrap(), 12);245 assert_eq!(solid_slice_edges(3).unwrap(), 90);246 assert!(solid_slice_edges(4).is_err());247 }248}249250#[cfg(test)]251mod theorems {252 use super::*;253 use crate::num::prime::is_prime;254255 fn slice_vertices(k: usize) -> u128 {256 solid_slice_vertices(2 * k - 1).unwrap()257 }258259 #[test]260 fn the_slice_vertex_count_is_centered_hexagonal_at_even_index() {261 let opening: Vec<u128> = (1..6).map(centered_hexagonal).collect();262 assert_eq!(opening, [1, 7, 19, 37, 61]);263 for k in 1..41usize {264 let m = 2 * k as u128;265 assert_eq!(slice_vertices(k), centered_hexagonal(2 * k), "k={k}");266 assert_eq!(slice_vertices(k) % 3, 1, "k={k}");267 assert_eq!(268 slice_vertices(k),269 m * m + m * (m - 1) + (m - 1) * (m - 1),270 "k={k}"271 );272 }273 }274275 #[test]276 fn the_prime_vertex_counts_are_cuban_with_norm_witnesses() {277 let mut prime_at = Vec::new();278 let mut primes = Vec::new();279 let mut composites = Vec::new();280 for k in 1..21usize {281 if is_prime(slice_vertices(k) as usize) {282 prime_at.push(k);283 primes.push(slice_vertices(k));284 } else {285 composites.push(slice_vertices(k));286 }287 }288 assert_eq!(prime_at, [1, 2, 5, 6, 7, 9, 12, 13, 14, 19]);289 assert_eq!(primes, [7, 37, 271, 397, 547, 919, 1657, 1951, 2269, 4219]);290 assert_eq!(291 composites,292 [91, 169, 721, 1141, 1387, 2611, 2977, 3367, 3781, 4681]293 );294 let far: Vec<u128> = (21..41usize)295 .map(slice_vertices)296 .filter(|&v| is_prime(v as usize))297 .collect();298 assert_eq!(far, [5167, 6211, 7351, 9241, 12097, 13669]);299 for value in primes.iter().chain(far.iter()) {300 assert_eq!(value % 3, 1, "value={value}");301 }302 assert_eq!(4219u128, 37 * 37 + 37 * 38 + 38 * 38);303 }304}