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}