design.rs

5.0 kB · rust · 205 lines

1use num_bigint::BigUint;2use std::collections::HashSet;34const PERMS: [[usize; 3]; 6] = [5    [0, 1, 2],6    [0, 2, 1],7    [1, 0, 2],8    [1, 2, 0],9    [2, 0, 1],10    [2, 1, 0],11];1213type Matrix = [[i64; 3]; 3];1415const IDENTITY: Matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];1617#[derive(Clone, Copy)]18pub struct Element {19    perm: [usize; 3],20    sign: [i64; 3],21    shift: [i64; 3],22}2324pub fn group(n: usize) -> Vec<Element> {25    let mut out = Vec::with_capacity(48 * n * n * n);26    for perm in PERMS {27        for bits in 0..8i64 {28            let sign = [29                1 - 2 * (bits >> 2 & 1),30                1 - 2 * (bits >> 1 & 1),31                1 - 2 * (bits & 1),32            ];33            for s0 in 0..n as i64 {34                for s1 in 0..n as i64 {35                    for s2 in 0..n as i64 {36                        out.push(Element {37                            perm,38                            sign,39                            shift: [s0, s1, s2],40                        });41                    }42                }43            }44        }45    }46    out47}4849fn image(n: usize, g: &Element, x: [i64; 3]) -> usize {50    let m = n as i64;51    let mut index = 0usize;52    for t in 0..3 {53        let y = (g.sign[t] * x[g.perm[t]] + g.shift[t]).rem_euclid(m);54        index = index * n + y as usize;55    }56    index57}5859fn images(n: usize, g: &Element, img: &mut [u32]) {60    let mut index = 0usize;61    for x0 in 0..n as i64 {62        for x1 in 0..n as i64 {63            for x2 in 0..n as i64 {64                img[index] = image(n, g, [x0, x1, x2]) as u32;65                index += 1;66            }67        }68    }69}7071fn walk(img: &[u32], seen: &mut [bool]) -> usize {72    seen.fill(false);73    let mut count = 0;74    for start in 0..img.len() {75        if seen[start] {76            continue;77        }78        count += 1;79        let mut j = start;80        while !seen[j] {81            seen[j] = true;82            j = img[j] as usize;83        }84    }85    count86}8788fn burnside(n: usize, histogram: &[u64]) -> BigUint {89    let mut total = BigUint::from(0u64);90    for (cycles, tally) in histogram.iter().enumerate() {91        if *tally != 0 {92            total += BigUint::from(*tally) << cycles;93        }94    }95    let order = BigUint::from(48 * (n as u64).pow(3));96    assert!((&total % &order) == BigUint::from(0u64));97    total / order98}99100pub fn by_cycles(n: usize) -> BigUint {101    let cells = n * n * n;102    let mut histogram = vec![0u64; cells + 1];103    let mut img = vec![0u32; cells];104    let mut seen = vec![false; cells];105    for g in group(n) {106        images(n, &g, &mut img);107        histogram[walk(&img, &mut seen)] += 1;108    }109    burnside(n, &histogram)110}111112fn matrix(g: &Element) -> Matrix {113    let mut rows: Matrix = [[0; 3]; 3];114    for t in 0..3 {115        rows[t][g.perm[t]] = g.sign[t];116    }117    rows118}119120fn matmul(a: &Matrix, b: &Matrix, m: i64) -> Matrix {121    let mut out: Matrix = [[0; 3]; 3];122    for i in 0..3 {123        for j in 0..3 {124            out[i][j] = (0..3).map(|k| a[i][k] * b[k][j]).sum::<i64>().rem_euclid(m);125        }126    }127    out128}129130fn affine(a: &Matrix, v: [i64; 3], t: [i64; 3], m: i64) -> [i64; 3] {131    let mut out = [0i64; 3];132    for i in 0..3 {133        out[i] = ((0..3).map(|k| a[i][k] * v[k]).sum::<i64>() + t[i]).rem_euclid(m);134    }135    out136}137138fn fixed_points(a: &Matrix, t: [i64; 3], m: i64) -> u64 {139    let mut count = 0u64;140    for x0 in 0..m {141        for x1 in 0..m {142            for x2 in 0..m {143                let x = [x0, x1, x2];144                if affine(a, x, t, m) == x {145                    count += 1;146                }147            }148        }149    }150    count151}152153fn cycles_by_powers(g: &Element, m: i64) -> usize {154    let base = matrix(g);155    let identity = matmul(&IDENTITY, &IDENTITY, m);156    let mut power = matmul(&base, &IDENTITY, m);157    let mut offset = g.shift;158    let mut total = 0u64;159    let mut k = 0u64;160    loop {161        k += 1;162        total += fixed_points(&power, offset, m);163        if power == identity && offset == [0, 0, 0] {164            break;165        }166        power = matmul(&base, &power, m);167        offset = affine(&base, offset, g.shift, m);168    }169    assert!(total % k == 0);170    (total / k) as usize171}172173pub fn by_affine(n: usize) -> BigUint {174    let cells = n * n * n;175    let mut histogram = vec![0u64; cells + 1];176    for g in group(n) {177        histogram[cycles_by_powers(&g, n as i64)] += 1;178    }179    burnside(n, &histogram)180}181182pub fn by_orbits(n: usize) -> u64 {183    let cells = n * n * n;184    let mut img = vec![0u32; cells];185    let mut maps: HashSet<Vec<u32>> = HashSet::new();186    for g in group(n) {187        images(n, &g, &mut img);188        maps.insert(img.clone());189    }190    let mut canon: HashSet<u64> = HashSet::new();191    for mask in 0u64..1u64 << cells {192        let mut best = mask;193        for map in &maps {194            let mut moved = 0u64;195            for (i, target) in map.iter().enumerate() {196                if mask >> i & 1 == 1 {197                    moved |= 1u64 << target;198                }199            }200            best = best.min(moved);201        }202        canon.insert(best);203    }204    canon.len() as u64205}