stack.rs

5.2 kB · rust · 189 lines

1use super::field::Field;2use super::layer::{layer, Layer};3use super::{Combine, Lattice, Spec};4use crate::core::error::Result;56/// Folds one boolean layer into the accumulator under the combine, the first layer seeding a meet.7pub fn merge(acc: &mut [f32], mask: &[bool], combine: Combine, first: bool) {8    match combine {9        Combine::Sum => {10            for (a, &b) in acc.iter_mut().zip(mask.iter()) {11                *a += b as u8 as f32;12            }13        }14        Combine::And => {15            for (a, &b) in acc.iter_mut().zip(mask.iter()) {16                *a = if first {17                    b as u8 as f3218                } else {19                    *a * b as u8 as f3220                };21            }22        }23        Combine::Xor => {24            for (a, &b) in acc.iter_mut().zip(mask.iter()) {25                *a = ((*a != 0.0) ^ b) as u8 as f32;26            }27        }28    }29}3031/// Layers one design at several side numbers into a field under the chosen combine.32///33/// ```34/// use mrlyrs::math::moire::{stack, Combine, Lattice, Spec};35/// let field = stack(Spec::new(7, 2, 2), &[3, 5], Combine::Sum, 1, Lattice::Square, 16, &[]).unwrap();36/// assert_eq!(field.data.len(), 256);37/// ```38///39/// # Errors40///41/// Errors below dimension two, or on a code out of range.42pub fn stack(43    spec: Spec,44    numbers: &[usize],45    combine: Combine,46    level: usize,47    lattice: Lattice,48    size: usize,49    slices: &[f64],50) -> Result<Field> {51    let mut acc = vec![0.0f32; size * size];52    let mut first = true;53    for &n in numbers {54        let params = Layer {55            spec,56            number: n,57            level,58            lattice,59            size,60            slices: slices.to_vec(),61        };62        merge(&mut acc, &layer(&params)?, combine, first);63        first = false;64    }65    Field::from_data(acc, size)66}6768/// Sums layers of several designs at one side number into a field.69///70/// # Errors71///72/// Errors below dimension two, or on a code out of range.73pub fn stack_codes(74    specs: &[Spec],75    number: usize,76    level: usize,77    lattice: Lattice,78    size: usize,79    slices: &[f64],80) -> Result<Field> {81    let mut acc = vec![0.0f32; size * size];82    for &spec in specs {83        let params = Layer {84            spec,85            number,86            level,87            lattice,88            size,89            slices: slices.to_vec(),90        };91        let m = layer(&params)?;92        for (a, &b) in acc.iter_mut().zip(m.iter()) {93            *a += b as u8 as f32;94        }95    }96    Field::from_data(acc, size)97}9899#[cfg(test)]100mod tests {101    use super::*;102    fn spec(code: u128) -> Spec {103        Spec::new(code, 2, 2)104    }105    #[test]106    fn sum_stack_counts_layers() {107        let numbers = [1, 3, 5];108        let f = stack(spec(7), &numbers, Combine::Sum, 1, Lattice::Square, 64, &[]).unwrap();109        assert!(f.max() <= numbers.len() as f32);110        assert!(f.min() >= 0.0);111    }112    #[test]113    fn complement_identity() {114        let numbers: Vec<usize> = (1..52).step_by(2).collect();115        let size = 48;116        for code in [1u128, 2, 3, 6, 7] {117            let h = stack(118                spec(code),119                &numbers,120                Combine::Sum,121                1,122                Lattice::Square,123                size,124                &[],125            )126            .unwrap();127            let hc = stack(128                spec(15 - code),129                &numbers,130                Combine::Sum,131                1,132                Lattice::Square,133                size,134                &[],135            )136            .unwrap();137            let n = numbers.len() as f32;138            let max_err = h139                .data140                .iter()141                .zip(hc.data.iter())142                .map(|(&a, &b)| (a + b - n).abs())143                .fold(0.0f32, f32::max);144            assert!(max_err < 1e-3, "code {code} max_err {max_err}");145        }146    }147    #[test]148    fn sponge_slice0_equals_carpet_stack() {149        use crate::math::bang::corners_to_code;150        let carpet_corners: Vec<Vec<u8>> = (0..3)151            .flat_map(|a| (0..3).map(move |b| vec![a as u8, b as u8]))152            .filter(|c| !(c[0] == 1 && c[1] == 1))153            .collect();154        let carpet_code = corners_to_code(&carpet_corners, 2, 3);155        let sponge_corners: Vec<Vec<u8>> = (0..3)156            .flat_map(|a| {157                (0..3).flat_map(move |b| (0..3).map(move |c| vec![a as u8, b as u8, c as u8]))158            })159            .filter(|c| {160                let centers = (c[0] == 1) as u8 + (c[1] == 1) as u8 + (c[2] == 1) as u8;161                centers <= 1162            })163            .collect();164        let sponge_code = corners_to_code(&sponge_corners, 3, 3);165        let numbers: Vec<usize> = (1..20).step_by(2).collect();166        let size = 48;167        let carpet = stack(168            Spec::new(carpet_code.get(), 3, 2),169            &numbers,170            Combine::Sum,171            1,172            Lattice::Square,173            size,174            &[],175        )176        .unwrap();177        let sponge = stack(178            Spec::new(sponge_code.get(), 3, 3),179            &numbers,180            Combine::Sum,181            1,182            Lattice::Square,183            size,184            &[0.0],185        )186        .unwrap();187        assert_eq!(carpet.data, sponge.data);188    }189}