shell.rs

7.1 kB · rust · 212 lines

1use mrlycore::json::parse;2use mrlycore::tensor::Tensor;3use mrlydemo::crop::crop_circle;4use mrlydemo::shell::*;5use mrlymath::bang::factory;6use mrlymath::shape::{crossing_shell, crossing_tree, radial_census};78fn keep(code: u128) -> Vec<bool> {9    factory::create(code, 3, 2, 2, 1)10        .unwrap()11        .bytes()12        .iter()13        .map(|&byte| byte != 0)14        .collect()15}1617fn depth_of(radius: u64) -> u32 {18    let mut depth = 0;19    while 3u64.pow(depth) <= radius {20        depth += 1;21    }22    depth23}2425#[test]26fn every_level_holds_two_floor_plus_one_boxes() {27    for radius in 1..=400u64 {28        for level in 0..8u32 {29            let want = 2 * (radius / 3u64.pow(level)) + 1;30            assert_eq!(31                crossing_shell(radius, 3, level).len() as u64,32                want,33                "r={radius} j={level}"34            );35        }36    }37    for radius in [728u64, 729, 1000, 2186, 2187, 6560] {38        for level in 0..9u32 {39            let want = 2 * (radius / 3u64.pow(level)) + 1;40            assert_eq!(crossing_shell(radius, 3, level).len() as u64, want);41        }42    }43}4445#[test]46fn the_whole_grid_shell_is_the_trees_leaf_row() {47    let ones = Tensor::full(vec![243, 243], 1);48    let table = radial_census(&ones, &[0, 0], 242);49    for radius in 1..=242u64 {50        assert_eq!(table[radius as usize].cut, 2 * radius + 1, "r={radius}");51    }52}5354#[test]55fn every_crossed_box_has_a_crossed_parent() {56    let carpet = keep(7);57    for radius in 1..=242u64 {58        let tree = crossing_tree(radius, 3, &carpet);59        assert_eq!(tree.orphans, 0, "r={radius}");60        assert_eq!(tree.levels.len() as u32, depth_of(radius) + 1);61        assert_eq!(tree.levels.last().unwrap().len(), 1);62        for level in 0..tree.levels.len() - 1 {63            for cell in &tree.levels[level] {64                let parent = tree.levels[level + 1][cell.parent];65                assert_eq!((parent.x, parent.y), (cell.x / 3, cell.y / 3));66                assert!(!cell.live || parent.live);67                assert_eq!(cell.live, parent.live && carpet[cell.seat]);68            }69        }70    }71}7273#[test]74fn the_live_leaves_are_the_crossings_the_page_draws() {75    for code in [7u128, 11, 15] {76        let seats = keep(code);77        for depth in 1..=5u32 {78            let side = 3u64.pow(depth);79            let grid = factory::create(code, 3, 2, 2, depth as usize).unwrap();80            let table = radial_census(&grid, &[0, 0], side - 1);81            for radius in side / 3..side {82                let tree = crossing_tree(radius, 3, &seats);83                let live = tree.levels[0].iter().filter(|cell| cell.live).count() as u64;84                assert_eq!(85                    live, table[radius as usize].cut,86                    "code={code} r={radius} depth={depth}"87                );88            }89        }90    }91}9293#[test]94fn the_shell_exports_answer() {95    let read = parse(&shell_read("7", 3, 2, 100).unwrap()).unwrap();96    assert_eq!(read["depth"], 5);97    assert_eq!(read["side"], 243);98    assert_eq!(read["leaves"], 201);99    assert_eq!(read["orphans"], 0);100    assert_eq!(read["exact"], true);101    let column = |key: &str| {102        read["levels"]103            .as_array()104            .unwrap()105            .iter()106            .map(|row| row[key].to_string())107            .collect::<Vec<String>>()108            .join(",")109    };110    assert_eq!(column("boxes"), "201,67,23,7,3,1");111    assert_eq!(column("want"), column("boxes"));112    assert_eq!(column("live"), "134,48,18,7,3,1");113    assert_eq!(column("three"), "false,true,false,false,false,true");114115    let nodes = shell_nodes("7", 3, 2, 100, None, None).unwrap();116    assert_eq!(nodes.len(), 5 * 302);117    assert_eq!(&nodes[0..5], &[0, 0, 100, 0, 1]);118    assert_eq!(nodes[5 * 301 + 4], 1);119    let leaves = nodes.chunks(5).filter(|node| node[0] == 0).count();120    assert_eq!(leaves, 201);121122    let small = parse(&shell_read("7", 3, 2, 7).unwrap()).unwrap();123    assert_eq!(small["depth"], 2);124    assert_eq!(small["leaves"], 15);125    assert_eq!(small["live"], 10);126127    let art = shell_pixels("7", 3, 2, 100, 3, None, None).unwrap();128    assert_eq!((art.width, art.height), (486, 486));129    assert_eq!(art.rgba.len(), 4 * 486 * 486);130131    assert!(shell_read("7", 3, 2, 0).is_err());132    assert!(shell_read("7", 3, 2, 243).is_err());133    assert!(shell_pixels("7", 3, 2, 100, 6, None, None).is_err());134}135136#[test]137fn the_page_reads_the_same_count_as_the_crop_sweep() {138    let flat = crop_circle("7", 3, 5, 2, 2, "corner").unwrap();139    let width = flat.len() / 3;140    let cut = &flat[2 * width..3 * width];141    for radius in [1u32, 7, 40, 100, 242] {142        let read = parse(&shell_read("7", 3, 2, radius).unwrap()).unwrap();143        assert_eq!(144            read["live"].as_u64().unwrap(),145            u64::from(cut[radius as usize]),146            "r={radius}"147        );148    }149}150151fn widths(nodes: &[u32]) -> Vec<usize> {152    let mut out = Vec::new();153    for node in nodes.chunks(5) {154        let level = node[0] as usize;155        while out.len() <= level {156            out.push(0);157        }158        out[level] += 1;159    }160    out161}162163#[test]164fn one_branch_of_the_tree_is_the_zoom_the_page_draws() {165    let whole = shell_nodes("7", 3, 2, 242, None, None).unwrap();166    assert_eq!(167        whole,168        shell_nodes("7", 3, 2, 242, Some(5), Some(0)).unwrap()169    );170    let boxes = widths(&whole);171    println!("shell_nodes r=242 boxes {boxes:?}");172    assert_eq!(boxes, [485, 161, 53, 17, 5, 1]);173    let read = parse(&shell_read("7", 3, 2, 242).unwrap()).unwrap();174    let (mut leaves, mut live, mut shapes) = (0usize, 0usize, Vec::new());175    for seat in 0..boxes[4] {176        let branch = shell_nodes("7", 3, 2, 242, Some(4), Some(seat as u32)).unwrap();177        let wide = widths(&branch);178        assert_eq!(wide.len(), 5, "seat={seat}");179        assert_eq!(*wide.last().unwrap(), 1, "seat={seat}");180        for node in branch.chunks(5) {181            let level = node[0] as usize;182            if level + 1 < wide.len() {183                assert!(184                    (node[3] as usize) < wide[level + 1],185                    "seat={seat} j={level}"186                );187            } else {188                assert_eq!(node[3], u32::MAX, "seat={seat}");189            }190        }191        leaves += wide[0];192        live += branch193            .chunks(5)194            .filter(|node| node[0] == 0 && node[4] == 1)195            .count();196        shapes.push(format!("{wide:?}"));197    }198    println!("shell_nodes r=242 branches {}", shapes.join(" "));199    assert_eq!(200        shapes.join(" "),201        "[95, 31, 10, 3, 1] [130, 44, 15, 5, 1] [35, 11, 3, 1, 1] [130, 44, 15, 5, 1] [95, 31, 10, 3, 1]"202    );203    println!("shell_nodes r=242 branch leaves {leaves} live {live}");204    assert_eq!(leaves, 485);205    assert_eq!(live as u64, read["live"].as_u64().unwrap());206    let plain = shell_pixels("7", 3, 2, 242, 4, None, None).unwrap();207    let ringed = shell_pixels("7", 3, 2, 242, 4, Some(4), Some(2)).unwrap();208    assert_ne!(plain.rgba, ringed.rgba);209    assert!(shell_nodes("7", 3, 2, 242, Some(6), None).is_err());210    assert!(shell_nodes("7", 3, 2, 242, Some(4), Some(5)).is_err());211    assert!(shell_pixels("7", 3, 2, 242, 4, Some(4), Some(9)).is_err());212}