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(¶ms)?, 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(¶ms)?;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}