boolean.rs

3.8 kB · rust · 141 lines

1/// Returns the Walsh spectrum of an n-input boolean function packed as a truth-table code.2pub fn walsh_spectrum(code: u128, n: usize) -> Vec<i64> {3    let size = 1usize << n;4    let mut t: Vec<i64> = (0..size)5        .map(|x| if (code >> x) & 1 == 1 { -1 } else { 1 })6        .collect();7    let mut len = 1;8    while len < size {9        let mut i = 0;10        while i < size {11            for j in i..i + len {12                let a = t[j];13                let b = t[j + len];14                t[j] = a + b;15                t[j + len] = a - b;16            }17            i += len << 1;18        }19        len <<= 1;20    }21    t22}2324/// Returns how far the packed function sits from every affine function, zero when it is one.25///26/// ```27/// assert_eq!(mrlynum::boolean::nonlinearity(0b0110, 2), 0);28/// assert_eq!(mrlynum::boolean::nonlinearity(0b1000, 2), 1);29/// ```30pub fn nonlinearity(code: u128, n: usize) -> i64 {31    if n == 0 {32        return 0;33    }34    let max_w = walsh_spectrum(code, n)35        .into_iter()36        .map(|w| w.abs())37        .max()38        .unwrap_or(0);39    (1i64 << (n - 1)) - max_w / 240}4142/// Reports whether the packed function outputs one on exactly half of its inputs.43pub fn is_balanced(code: u128, n: usize) -> bool {44    let size = 1u32 << n;45    (code & ((1u128 << size) - 1)).count_ones() == size / 246}4748/// Returns the mean chance that flipping one input bit flips the output, 0.5 at full avalanche.49pub fn sac(code: u128, n: usize) -> f64 {50    if n == 0 {51        return 0.0;52    }53    let size = 1usize << n;54    let mut total = 0.0;55    for bit in 0..n {56        let mut flips = 0usize;57        for x in 0..size {58            let f = (code >> x) & 1;59            let g = (code >> (x ^ (1 << bit))) & 1;60            if f != g {61                flips += 1;62            }63        }64        total += flips as f64 / size as f64;65    }66    total / n as f6467}6869#[cfg(test)]70mod tests {71    use super::*;72    #[test]73    fn linear_function_has_zero_nonlinearity() {74        let n = 3;75        let size = 1usize << n;76        let mut code: u128 = 0;77        for x in 0..size {78            if x & 1 == 1 {79                code |= 1 << x;80            }81        }82        assert_eq!(nonlinearity(code, n), 0);83    }84    #[test]85    fn n3_max_nonlinearity_is_two() {86        let n = 3;87        let max = (0..(1u128 << (1 << n)))88            .map(|c| nonlinearity(c, n))89            .max()90            .unwrap();91        assert_eq!(max, 2);92    }93    #[test]94    fn n4_reaches_bent_level_six() {95        let n = 4;96        let max = (0..(1u128 << (1 << n)))97            .map(|c| nonlinearity(c, n))98            .max()99            .unwrap();100        assert_eq!(max, 6);101    }102    #[test]103    fn balance_detects_half_ones() {104        assert!(is_balanced(0b0011, 2));105        assert!(!is_balanced(0b0111, 2));106    }107    #[test]108    fn carpet_corners_expand_on_four_walsh_characters() {109        let code = 7;110        for x in 0..2u32 {111            for y in 0..2u32 {112                let (sx, sy) = (1 - 2 * (x as i64), 1 - 2 * (y as i64));113                assert_eq!(114                    ((code >> (2 * x + y)) & 1) as i64,115                    (3 + sx + sy - sx * sy) / 4116                );117            }118        }119        let spectrum = walsh_spectrum(code, 2);120        assert_eq!(spectrum, vec![-2, -2, -2, 2]);121        let quarters: Vec<i64> = spectrum122            .iter()123            .enumerate()124            .map(|(s, &w)| ((if s == 0 { 4 } else { 0 }) - w) / 2)125            .collect();126        assert_eq!(quarters, vec![3, 1, 1, -1]);127    }128    #[test]129    fn sac_of_constant_is_zero() {130        assert_eq!(sac(0, 3), 0.0);131        let n = 3;132        let size = 1usize << n;133        let mut code: u128 = 0;134        for x in 0..size {135            if x & 1 == 1 {136                code |= 1 << x;137            }138        }139        assert!((sac(code, n) - 1.0 / 3.0).abs() < 1e-12);140    }141}