fixture.rs
74.6 kB · rust · 1924 lines
1use mrlycore::colors::{Color, DARK};2use mrlycore::json::parse;3use mrlydemo::automata::*;4use mrlydemo::bang::*;5use mrlydemo::blend::*;6use mrlydemo::carry::*;7use mrlydemo::census::*;8use mrlydemo::formulas::*;9use mrlydemo::gauss::*;10use mrlydemo::graph::*;11use mrlydemo::lab::*;12use mrlydemo::lattice::*;13use mrlydemo::ledger::*;14use mrlydemo::life::*;15use mrlydemo::magic::*;16use mrlydemo::morse::*;17use mrlydemo::prime::*;18use mrlydemo::race::Race;19use mrlydemo::sieve::*;20use mrlydemo::six::*;21use mrlydemo::spectrum::*;22use mrlydemo::spin::*;23use mrlydemo::spiral::*;24use mrlydemo::three::*;25use mrlydemo::tile::*;26use mrlydemo::two::*;27use mrlydemo::volume::*;28use mrlydemo::zeta::*;2930fn column(rows: &mrlycore::Json, key: &str) -> String {31 rows.as_array()32 .unwrap()33 .iter()34 .map(|row| row[key].to_string())35 .collect::<Vec<String>>()36 .join(",")37}3839fn ink(c: Color) -> [u8; 3] {40 [c.r, c.g, c.b]41}4243fn blinker() -> Vec<u8> {44 let mut types = vec![0u8; 25];45 for site in [7, 12, 17] {46 types[site] = 1;47 }48 types49}5051#[test]52fn the_fixture_the_page_prints() {53 let grid = two_grid("7", 3, 3, 0, 2).unwrap();54 assert_eq!((grid.width, grid.height), (27, 27));55 assert_eq!(grid.types.iter().map(|&b| b as usize).sum::<usize>(), 512);56 assert_eq!(fills("23", 3, 3, 3, 2).unwrap(), "8000");57 assert_eq!(voids("23", 3, 3, 3, 2).unwrap(), "11683");58 assert_eq!(counting_sequence(4).unwrap(), ["3", "6", "22", "402"]);59 assert_eq!(baseq_sequence(3, 2).unwrap(), ["4", "26"]);60 assert_eq!(classes_sequence(4), ["4", "12", "64", "700"]);61 let faces = three_faces("23", 3, 3, 2).unwrap();62 assert_eq!(faces[0] as usize / 36, 18048);63 assert_eq!(faces.len(), 2 + faces[0] as usize);64 assert_eq!(three_surface("23", 3, 3, 2).unwrap(), "18048");65 assert_eq!(three_cells("23", 3, 1, 2).unwrap().len() / 3, 20);66 let tally = parse(&three_census("23", 3, 1, 2).unwrap()).unwrap();67 assert_eq!(68 (tally["fills"].clone(), tally["euler"].clone()),69 (20.into(), (-4).into())70 );71 assert_eq!(parse(&universe(3).unwrap()).unwrap()["distinct"], 22);72 assert_eq!(73 parse(&universe(2).unwrap()).unwrap()["designs"][1]["orbit"],74 475 );76 assert_eq!(77 name_of("127", 2, 3).unwrap(),78 "bang dim 2, base 3, code 127"79 );80 assert_eq!(81 parse(&name_parse("bang_dim=3_code=23").unwrap()).unwrap()["code"],82 "23"83 );84 assert_eq!(85 press_members("2", 1, 2, 5).unwrap(),86 ["1", "3", "7", "15", "31"]87 );88 assert_eq!(press_count_below("7", 2, 2, "27").unwrap(), "18");89 let run = parse(&life_run(&blinker(), 5, 5, &[3], &[2, 3], false, 16).unwrap()).unwrap();90 assert_eq!(91 (run["fate"].clone(), run["loop"].clone()),92 ("loop".into(), 2.into())93 );94 assert_eq!(life_sequence("primes", 8).unwrap(), vec![2, 3, 5, 7]);95 assert_eq!(96 moire("heatmap", 9, 32, "fire", 64, false)97 .unwrap()98 .rgba99 .len(),100 4096101 );102 assert!(hex_svg("23", 3, 1, 2, "iso", 10)103 .unwrap()104 .contains("<polygon"));105 let slice = parse(&slice_census("23", 3, 1, 2).unwrap()).unwrap();106 assert_eq!(107 (slice["triangles"].clone(), slice["fills"].clone()),108 (54.into(), 42.into())109 );110 assert_eq!(111 (slice["vertices"].clone(), slice["euler"].clone()),112 (37.into(), 1.into())113 );114 assert_eq!(115 (slice["components"].clone(), slice["holes"].clone()),116 (1.into(), 1.into())117 );118 assert_eq!(slice["closed"]["vertices"], "37");119 assert_eq!(120 parse(&slice_census("232", 3, 1, 2).unwrap()).unwrap()["fills"],121 12122 );123 let deep = parse(&slice_census("23", 3, 2, 2).unwrap()).unwrap();124 assert_eq!(125 (deep["fills"].clone(), deep["holes"].clone()),126 (306.into(), 7.into())127 );128 let series = parse(&slice_series("23", 6).unwrap()).unwrap();129 let column = |key: &str| {130 series131 .as_array()132 .unwrap()133 .iter()134 .map(|row| row[key].to_string())135 .collect::<Vec<String>>()136 .join(",")137 };138 assert_eq!(column("components"), "1,1,7,1,19,1");139 assert_eq!(column("holes"), "0,1,0,7,0,19");140 let carpet = parse(&walsh_spectrum("23", 6).unwrap()).unwrap();141 assert_eq!(carpet["spectrum"].to_string(), "[0,-4,-4,0,-4,0,0,4]");142 assert_eq!(carpet["weights"].to_string(), "[1,3,0,0]");143 assert_eq!(crate::column(&carpet["levels"], "sixteenths"), "8,12,0,-4");144 assert_eq!(145 crate::column(&carpet["law"], "fills"),146 "6,42,72,204,210,486"147 );148 assert_eq!(crate::column(&carpet["law"], "s"), "-1,1,-1,1,-1,1");149 let skew = parse(&walsh_spectrum("11", 6).unwrap()).unwrap();150 assert_eq!(skew["spectrum"].to_string(), "[2,2,-2,-2,-6,2,-2,-2]");151 assert_eq!(skew["weights"].to_string(), "[1,1,1,0]");152 assert_eq!(crate::column(&skew["levels"], "sixteenths"), "6,6,2,2");153 assert_eq!(crate::column(&skew["law"], "fills"), "6,20,76,100,230,240");154 for design in [&carpet, &skew] {155 let code = design["code"].as_str().unwrap();156 let counted = (1..=6)157 .map(|k| {158 parse(&slice_census(code, 2 * k - 1, 1, 2).unwrap()).unwrap()["fills"].to_string()159 })160 .collect::<Vec<String>>()161 .join(",");162 assert_eq!(crate::column(&design["law"], "fills"), counted);163 }164 let flat = parse(&spectrum("flat", "7", 2, 4, true, 0.1).unwrap()).unwrap();165 assert_eq!(166 (flat["nodes"].clone(), flat["distinct"].clone()),167 (81.into(), 43.into())168 );169 assert_eq!(170 (flat["classes"].clone(), flat["one"].clone()),171 (9.into(), 27.into())172 );173 assert_eq!(flat["pair"], parse("[4,4]").unwrap());174 let piece = parse(&spectrum("slice", "23", 3, 1, true, 0.1).unwrap()).unwrap();175 assert_eq!(piece["nodes"], 42);176 let stair = piece["stair"].as_array().unwrap();177 assert_eq!(stair.last().unwrap()[1], 1.0);178 assert!(stair.len() >= piece["distinct"].as_u64().unwrap() as usize && stair.len() < 42);179 assert_eq!(piece["fitted"], 4);180 assert_eq!(181 format!("{:.2}", piece["exponent"].as_f64().unwrap()),182 "0.91"183 );184 assert_eq!(185 format!("{:.4}", piece["fit"][1].as_f64().unwrap() * 2.0),186 format!("{:.4}", piece["exponent"].as_f64().unwrap())187 );188 let plain = parse(&spectrum("flat", "7", 2, 2, false, 0.1).unwrap()).unwrap();189 assert_eq!(190 (plain["nodes"].clone(), plain["distinct"].clone()),191 (9.into(), 8.into())192 );193 assert_eq!(194 (plain["classes"].clone(), plain["one"].clone()),195 (1.into(), 2.into())196 );197 let cut = parse(&diagonal_profile("126", 2, 4, 2).unwrap()).unwrap();198 assert_eq!(cut["side"], 16);199 assert_eq!(cut["support"], parse("[15,30]").unwrap());200 assert_eq!(cut["central"], parse("[22,23]").unwrap());201 assert_eq!(202 (cut["min"].clone(), cut["max"].clone()),203 ("81".into(), "81".into())204 );205 assert_eq!(cut["constant"], true);206 assert_eq!(diagonal_count("126", 2, 7, 2, 190).unwrap(), "2187");207 let art = diagonal_svg("126", 2, 3, 2, vec![10, 11], 4).unwrap();208 assert!(art.contains("<circle"));209 assert_eq!(art.matches("<circle").count(), 54);210 assert_eq!(211 parse(&diagonal_profile("127", 2, 4, 2).unwrap()).unwrap()["max"],212 "162"213 );214 assert_eq!(Race::new("127", 3, 4, 3, 300, 1).unwrap().side(), 81);215 assert_eq!(parse(&farey(5)).unwrap().as_array().unwrap().len(), 11);216 assert_eq!(totients(6), vec![0, 1, 1, 2, 2, 4, 2]);217 let terms: Vec<String> = (1..=8)218 .map(|n| {219 parse(&visible_read(n, 2).unwrap()).unwrap()["lit"]220 .as_str()221 .unwrap()222 .to_string()223 })224 .collect();225 assert_eq!(terms.join(","), "1,3,7,11,19,23,35,43");226 let corner = parse(&visible_read(100, 2).unwrap()).unwrap();227 assert_eq!(corner["lit"], "6087");228 assert_eq!(corner["total"], "10000");229 assert_eq!(corner["name"], "pi");230 assert!((corner["density"].as_f64().unwrap() - 0.6087).abs() < 1e-12);231 assert!((corner["limit"].as_f64().unwrap() - 0.607_927_101_8).abs() < 1e-9);232 assert!((corner["constant"].as_f64().unwrap() - 3.139_597_50).abs() < 1e-8);233 let cube = parse(&visible_read(1000, 3).unwrap()).unwrap();234 assert_eq!(cube["lit"], "832046137");235 assert_eq!(cube["name"], "zeta(3)");236 assert!((cube["constant"].as_f64().unwrap() - 1.201_856_43).abs() < 1e-8);237 let quartic = parse(&visible_read(1000, 4).unwrap()).unwrap();238 assert!((quartic["constant"].as_f64().unwrap() - 3.141_549_67).abs() < 1e-8);239 let lattice = visible_pixels(100, 200, true).unwrap();240 let blue = lattice241 .rgba242 .chunks(4)243 .filter(|dot| dot[..3] == ink(DARK.blue))244 .count();245 assert_eq!((lattice.width, lattice.height, blue), (200, 200, 4 * 6087));246 let flat = visible_pixels(100, 200, false).unwrap();247 assert_eq!(248 flat.rgba249 .chunks(4)250 .filter(|dot| dot[..3] == ink(DARK.line))251 .count(),252 4 * (10000 - 6087)253 );254 let walk = visible_walk(100, 2, 8).unwrap();255 assert_eq!(walk.len(), 16);256 assert_eq!(walk[14], 100.0);257 assert!((walk[15] - 3.139_597_50).abs() < 1e-8);258 assert!(visible_read(0, 2).is_err() && visible_read(10, 9).is_err());259 assert!((dimension("127", 3, 2, 3).unwrap() - 1.7712).abs() < 1e-4);260 let carpet: Vec<f32> = two_grid("495", 3, 3, 0, 3)261 .unwrap()262 .types263 .iter()264 .map(|&b| b as f32)265 .collect();266 let rings = profile(&carpet, 27, 1000).unwrap();267 assert_eq!(rings.len(), 1000);268 assert_eq!(rings[0], 0.0);269 assert_eq!(rings.iter().position(|&v| v > 0.0), Some(236));270 assert!((rings[600] - 0.8972).abs() < 1e-4);271 let stats = parse(&spin_stats(&rings, 27)).unwrap();272 assert!((stats["mass"].as_f64().unwrap() - 512.0).abs() < 0.1);273 assert!((stats["disc"].as_f64().unwrap() - 4.5).abs() < 0.02);274 assert_eq!(275 wheel(&rings, 64, "fire", 16, false).unwrap().rgba.len(),276 64 * 64 * 4277 );278 let hexagon = slice_grid("23", 3, 1, 2, 101).unwrap();279 assert_eq!((hexagon.width, hexagon.types[50 * 101 + 50]), (101, 0));280 let cut: Vec<f32> = hexagon.types.iter().map(|&b| b as f32).collect();281 assert_eq!(profile(&cut, 101, 10).unwrap()[0], 0.0);282 assert_eq!(283 profile(&moire_field("heatmap", 9, 32).unwrap(), 32, 16)284 .unwrap()285 .len(),286 16287 );288 let square = vec![1.0f32; 64];289 let star = radial(&square, 8, 64, 2, 45.0, "union", 1).unwrap();290 assert_eq!((star.len(), star[32 * 64 + 32]), (4096, 1.0));291 assert_eq!(turns(&harmonics(&square, 8, 64, 8).unwrap()), 4);292 assert_eq!(petals(6, 4), 12);293 assert_eq!(294 sheet(&star, 64, "heat", 8, false).unwrap().rgba.len(),295 16384296 );297 assert_eq!(moire_field("heatmap", 9, 32).unwrap().len(), 1024);298 let v = volume("23", 2, 3, "sum", 1, 9).unwrap();299 assert_eq!((v.len(), volume_count(&v, 9, 2.0).unwrap()), (729, 540));300 assert_eq!(volume_faces(&v, 9, 2.0).unwrap()[0] as usize / 36, 648);301 assert_eq!(volume_surface(&v, 9, 2.0).unwrap(), 648);302 let f = parse(&plane_frame(&[1.0, 1.0, 1.0], 0.5).unwrap()).unwrap();303 assert!((f["width"].as_f64().unwrap() - 3.2660).abs() < 1e-3);304 let cut = plane_field(&v, 9, &[1.0, 1.0, 1.0], 0.5, 64).unwrap();305 assert!(cut[0].is_nan() && cut[32 * 64 + 32] == 1.0);306 let sheet = paint_span(&cut, 64, 0.0, 2.0, "fire", 8, false).unwrap();307 assert_eq!(308 (309 sheet.rgba.len(),310 sheet.rgba[3],311 sheet.rgba[(32 * 64 + 32) * 4 + 3]312 ),313 (16384, 0, 255)314 );315 assert_eq!(parse(&volume_stats(&v, 9).unwrap()).unwrap()["max"], 2.0);316 assert_eq!(random_code(3, 2, 7).unwrap(), "160");317 assert_eq!(random_codes(3, 2, 7, 3).unwrap(), ["160", "134", "72"]);318 assert_eq!(random_between(7, &[0, 0, 1], &[3, 1800, 36]), [0, 1023, 17]);319 assert_eq!(320 (321 level_cap(3, 1, 128),322 level_cap(2, 1, 512),323 level_cap(3, 3, 60000)324 ),325 (4, 9, 3)326 );327 assert_eq!(fill_cap("7", 2, 2, 2, 1100).unwrap(), 6);328 assert_eq!(grid_total(3, 2, 4).unwrap(), "6561");329 assert_eq!(odd_scales(9), [1, 3, 5, 7, 9]);330 let stack = parse(&farey_novelty(5)).unwrap();331 assert_eq!(332 (333 stack["lit"].clone(),334 stack["novel"].clone(),335 stack["match"].clone()336 ),337 (11.into(), 11.into(), true.into())338 );339 assert_eq!(stack["primes"], parse("[2,3,5]").unwrap());340 let split = parse(&slice_partition(3).unwrap()).unwrap();341 assert_eq!(342 (343 split["carpet"].clone(),344 split["net"].clone(),345 split["exact"].clone()346 ),347 (42.into(), 12.into(), true.into())348 );349 let shape = parse(&volume_shape(7, 64)).unwrap();350 assert_eq!(351 (shape["layers"].clone(), shape["voxels"].clone()),352 (4.into(), 262144.into())353 );354 assert_eq!(355 format!(356 "{:.1}",357 radial_share(&harmonics(&square, 8, 64, 8).unwrap())358 ),359 "95.3"360 );361 assert_eq!(full_turn(6), 60.0);362 assert_eq!(frame_step(900.0, 60.0), 90.0);363 assert_eq!(diagonal_digits("126", 2, 4, 2, 20).unwrap(), "101");364 assert_eq!(diagonal_total("126", 2, 3, 2, vec![10, 11]).unwrap(), "54");365 let mut sieve = Sieve::new(30).unwrap();366 let mut sweeps = 0;367 while !sieve.done() {368 sieve.step();369 sweeps += 1;370 }371 assert_eq!((sweeps, sieve.count()), (3, 10));372 let mut hundred = Sieve::new(100).unwrap();373 hundred.finish();374 assert_eq!(hundred.count(), 25);375 let stones = parse(&factor("360").unwrap()).unwrap();376 assert_eq!(stones["factors"], parse("[[2,3],[3,2],[5,1]]").unwrap());377 assert_eq!(stones["prime"], false);378 assert_eq!(379 parse(&factor("6").unwrap()).unwrap()["rectangles"],380 parse("[[1,6],[2,3]]").unwrap()381 );382 let chart = parse(&prime_chart(10_000, 400).unwrap()).unwrap();383 assert_eq!(chart["pi"].as_array().unwrap().last().unwrap(), 1229);384 let wide = parse(&prime_chart(100_000, 100).unwrap()).unwrap();385 assert_eq!(wide["pi"].as_array().unwrap().last().unwrap(), 9592);386 let square = parse(&carpet_witness(169).unwrap()).unwrap();387 assert!((square["max"].as_f64().unwrap() - 0.0517383).abs() < 1e-7);388 assert_eq!(square["at"], 13);389 let clear = parse(&carpet_witness(197).unwrap()).unwrap();390 assert_eq!(391 (clear["max"].clone(), clear["prime"].clone()),392 (0.0.into(), true.into())393 );394 assert_eq!(clear["row"].as_array().unwrap().len(), 97);395}396397#[test]398fn the_rest_of_the_exports_answer() {399 let sheet = two_pixels("7", 3, 1, 0, 2).unwrap();400 assert_eq!(sheet.rgba.chunks(4).filter(|p| p[0] == 0).count(), 8);401 assert_eq!(402 parse(&two_census("7", 3, 1, 0, 2).unwrap()).unwrap()["voids"],403 1404 );405 assert!((ratio("23", 3, 3, 3, 2).unwrap() - 8000.0 / 19683.0).abs() < 1e-12);406 let next = life_next(&blinker(), 5, 5, &[3], &[2, 3], false).unwrap();407 assert_eq!(&next[10..15], &[0, 1, 1, 1, 0]);408 assert_eq!(life_noise(8, 8, 0.0, 1).iter().sum::<u8>(), 0);409 assert_eq!(life_noise(8, 8, 1.0, 1).iter().sum::<u8>(), 64);410 assert_eq!(life_noise(64, 64, 0.5, 7), life_noise(64, 64, 0.5, 7));411 assert_eq!(life_sequences().len(), 23);412 assert_eq!(moire_names(), ["heatmap", "weave", "hive", "carpet"]);413 let mut race = Race::new("239", 3, 3, 3, 40, 9).unwrap();414 let mut twin = Race::new("239", 3, 3, 3, 40, 9).unwrap();415 assert_eq!(race.step(30), twin.step(30));416 assert_eq!(race.positions(), twin.positions());417 assert_eq!(race.steps(), 30);418 assert!(race.distance() > 0.0);419 assert!(race420 .positions()421 .iter()422 .all(|&p| race.types()[p as usize] != 0));423 assert!(race.trail().iter().map(|&v| v as usize).sum::<usize>() <= 40 * 30);424 assert_eq!(race.home(), Race::new("239", 3, 3, 3, 1, 1).unwrap().home());425 let mut sieve = Sieve::new(150).unwrap();426 assert_eq!(sieve.step(), 2);427 assert_eq!((sieve.struck(), sieve.rank(), sieve.count()), (74, 1, 1));428 sieve.finish();429 let sheet = sieve.grid(15).unwrap();430 assert_eq!((sheet.width, sheet.height), (15, 10));431 assert_eq!(sheet.types.iter().map(|&b| b as u32).sum::<u32>(), 35);432 assert_eq!(&sheet.types[..7], &[0, 1, 1, 0, 1, 0, 1]);433}434435#[test]436fn the_faults_come_back_as_messages() {437 assert!(two_grid("16", 3, 1, 0, 2).is_err());438 assert!(two_grid("7.5", 3, 1, 0, 2).is_err());439 assert!(three_cells("256", 3, 1, 2).is_err());440 assert!(universe(4).is_err());441 assert!(name_of("16", 2, 2).is_err());442 assert!(name_parse("sequence_dim=3_code=23_measure=fills_axis=side").is_err());443 assert!(press_members("16", 2, 2, 5).is_err());444 assert!(press_count_below("7", 2, 2, "x").is_err());445 assert!(life_next(&blinker(), 4, 5, &[3], &[2, 3], false).is_err());446 assert!(life_sequence("soup", 8).is_err());447 assert!(moire("soup", 9, 32, "fire", 64, false).is_err());448 assert!(profile(&[1.0; 60], 8, 10).is_err());449 assert!(moire_field("soup", 9, 32).is_err());450 assert!(wheel(&[1.0, 0.0], 0, "fire", 8, false).is_err());451 assert!(radial(&[1.0; 64], 8, 64, 2, 45.0, "soup", 1).is_err());452 assert!(radial(&[1.0; 60], 8, 64, 2, 45.0, "mean", 1).is_err());453 assert!(harmonics(&[1.0; 60], 8, 16, 4).is_err());454 assert!(volume("23", 2, 3, "soup", 1, 9).is_err());455 assert!(volume_faces(&[1.0; 8], 3, 1.0).is_err());456 assert!(plane_frame(&[0.0, 0.0, 0.0], 0.5).is_err());457 assert!(plane_frame(&[1.0, 1.0], 0.5).is_err());458 assert!(paint_span(&[1.0; 8], 3, 0.0, 1.0, "fire", 8, false).is_err());459 assert!(hex_svg("256", 3, 1, 2, "iso", 10).is_err());460 assert!(slice_census("23", 4, 1, 2).is_err());461 assert!(slice_series("23", 17).is_err());462 assert!(walsh_spectrum("256", 6).is_err());463 assert!(walsh_spectrum("23", 17).is_err());464 assert!(diagonal_profile("0", 2, 2, 2).is_err());465 assert!(diagonal_count("126", 2, 0, 2, 1).is_err());466 assert!(Race::new("0", 3, 2, 3, 4, 1).is_err());467 assert!(spectrum("flat", "7", 2, 7, true, 0.1).is_err());468 assert!(spectrum("wobble", "7", 2, 2, true, 0.1).is_err());469 assert!(random_code(3, 6, 1).is_err());470 assert!(fill_cap("256", 3, 3, 2, 10).is_err());471 assert!(grid_total(3, 3, 40).is_err());472 assert!(diagonal_digits("0", 2, 2, 2, 1).is_err());473 assert!(Sieve::new(401).is_err());474 assert!(Sieve::new(10).unwrap().grid(0).is_err());475 assert!(factor("x").is_err());476 assert!(factor("1000000000001").is_err());477 assert!(prime_chart(1_000_001, 10).is_err());478 assert!(carpet_witness(4).is_err());479 assert!(carpet_witness(1001).is_err());480}481482#[test]483fn the_spiral_exports_answer() {484 assert_eq!(spiral_xy("square", 10).unwrap(), vec![2, -1, 2]);485 assert_eq!(spiral_xy("square", 25).unwrap(), vec![2, -2, 2]);486 assert_eq!(spiral_xy("hex", 8).unwrap(), vec![1, 1, 2]);487 assert_eq!(spiral_xy("hex", 19).unwrap(), vec![0, 2, 2]);488 assert_eq!(spiral_xy("hex", 20).unwrap(), vec![1, 2, 3]);489 let euler = parse(&spiral_polynomial("square", 201, 4, -2, 41).unwrap()).unwrap();490 assert_eq!(euler["top"], 40401);491 assert_eq!(euler["primes"], 4236);492 assert_eq!(493 (euler["count"].clone(), euler["hits"].clone()),494 (101.into(), 80.into())495 );496 assert_eq!(euler["streak"], 21);497 assert!((euler["density"].as_f64().unwrap() - 4236.0 / 40401.0).abs() < 1e-12);498 assert!((euler["share"].as_f64().unwrap() - 80.0 / 101.0).abs() < 1e-12);499 assert_eq!(euler["values"].as_array().unwrap()[20], 1601);500 assert_eq!(501 euler["cells"].as_array().unwrap()[100],502 parse("[60,100]").unwrap()503 );504 let spoke = parse(&spiral_polynomial("hex", 41, 3, 3, 1).unwrap()).unwrap();505 assert_eq!(spoke["top"], 1261);506 assert_eq!(507 spoke["cells"].as_array().unwrap()[20],508 parse("[0,20]").unwrap()509 );510 let sheet = spiral_pixels("square", 61, 4, -2, 41, "prime", false, 180).unwrap();511 assert_eq!(512 (sheet.width, sheet.height, sheet.rgba.len()),513 (180, 180, 129_600)514 );515 let at =516 |px: usize, py: usize| sheet.rgba[(py * 180 + px) * 4..(py * 180 + px) * 4 + 3].to_vec();517 assert_eq!(at(90, 90), ink(DARK.ground));518 assert_eq!(at(92, 90), ink(DARK.yellow));519 assert_eq!(at(81, 92), ink(DARK.orange));520 let hexes = spiral_pixels("hex", 21, 3, 3, 1, "mobius", true, 200).unwrap();521 assert_eq!(hexes.rgba[..3], ink(DARK.ground));522 assert_eq!(523 hexes.rgba[(100 * 200 + 100) * 4..(100 * 200 + 100) * 4 + 3],524 ink(DARK.blue)525 );526 let centre = parse(&spiral_at("square", 201, 384.0, 384.0, 768).unwrap()).unwrap();527 assert_eq!(528 (centre["n"].clone(), centre["ring"].clone()),529 (1.into(), 0.into())530 );531 let hit = parse(&spiral_at("square", 61, 81.5, 92.5, 180).unwrap()).unwrap();532 assert_eq!(533 (hit["n"].clone(), hit["prime"].clone()),534 (41.into(), true.into())535 );536 assert_eq!(hit["factors"], parse("[[41,1]]").unwrap());537 assert!((hit["span"].as_f64().unwrap() - 180.0 / 61.0).abs() < 1e-12);538 let corner = parse(&spiral_at("hex", 21, 195.0, 100.0, 200).unwrap()).unwrap();539 assert_eq!(540 (corner["x"].clone(), corner["y"].clone()),541 (10.into(), 0.into())542 );543 assert_eq!(corner["n"], 281);544 let centres = spiral_centers("square", 21, 420).unwrap();545 assert_eq!((centres.len(), centres[0], centres[1]), (882, 210.0, 210.0));546 assert_eq!((centres[2], centres[3]), (230.0, 210.0));547 assert_eq!(spiral_centers("hex", 21, 420).unwrap().len(), 662);548 assert_eq!(prime_from(90), 97);549 assert!(spiral_pixels("cube", 21, 1, 0, 2, "prime", true, 100).is_err());550 assert!(spiral_pixels("square", 20, 1, 0, 2, "prime", true, 100).is_err());551 assert!(spiral_pixels("square", 403, 1, 0, 2, "prime", true, 100).is_err());552 assert!(spiral_pixels("square", 21, 0, 0, 2, "prime", true, 100).is_err());553 assert!(spiral_pixels("square", 21, 1, 0, 2, "odd", true, 100).is_err());554 assert!(spiral_pixels("square", 21, 1, 0, 2, "prime", true, 2000).is_err());555 assert!(spiral_polynomial("square", 21, 1, 2_000_000, 2).is_err());556 assert!(spiral_at("hex", 21, 1.0, 1.0, 200).is_err());557 assert!(spiral_centers("square", 101, 400).is_err());558 assert!(spiral_xy("tri", 5).is_err());559}560561#[test]562fn the_gauss_exports_answer() {563 let two = parse(&ring_census("gaussian", 2).unwrap()).unwrap();564 assert_eq!(565 (566 two["points"].clone(),567 two["primes"].clone(),568 two["split"].clone()569 ),570 (25.into(), 12.into(), 8.into())571 );572 assert_eq!(573 (574 two["ramified"].clone(),575 two["inert"].clone(),576 two["units"].clone()577 ),578 (4.into(), 0.into(), 4.into())579 );580 assert_eq!(581 (582 two["composites"].clone(),583 two["symmetry"].clone(),584 two["top"].clone()585 ),586 (8.into(), 8.into(), 8.into())587 );588 let three = parse(&ring_census("gaussian", 3).unwrap()).unwrap();589 assert_eq!(590 (three["primes"].clone(), three["inert"].clone()),591 (24.into(), 4.into())592 );593 let hex = parse(&ring_census("eisenstein", 2).unwrap()).unwrap();594 assert_eq!(595 (596 hex["points"].clone(),597 hex["primes"].clone(),598 hex["composites"].clone()599 ),600 (19.into(), 12.into(), 0.into())601 );602 assert_eq!(603 (604 hex["ramified"].clone(),605 hex["inert"].clone(),606 hex["symmetry"].clone()607 ),608 (6.into(), 6.into(), 12.into())609 );610 let r2 = ring_weights("gaussian", 25).unwrap();611 assert_eq!((r2[3], r2[5], r2[25]), (0, 8, 12));612 assert_eq!(613 ring_weights("eisenstein", 7).unwrap(),614 vec![1, 6, 0, 6, 6, 0, 0, 12]615 );616 assert_eq!(ring_peak("gaussian", 60).unwrap(), vec![25, 12]);617 assert_eq!(ring_peak("eisenstein", 60).unwrap(), vec![49, 18]);618 assert_eq!(619 ring_fates("gaussian", 7).unwrap(),620 vec![0, 0, 3, 2, 0, 1, 0, 2]621 );622 assert_eq!(623 ring_fates("eisenstein", 7).unwrap(),624 vec![0, 0, 2, 3, 0, 2, 0, 1]625 );626 let sheet = ring_pixels("gaussian", 2, "class", true, 100).unwrap();627 let pixel =628 |px: usize, py: usize| sheet.rgba[(py * 100 + px) * 4..(py * 100 + px) * 4 + 3].to_vec();629 assert_eq!((sheet.width, sheet.height), (100, 100));630 assert_eq!(pixel(70, 30), ink(DARK.pink));631 assert_eq!(pixel(90, 30), ink(DARK.blue));632 assert_eq!(pixel(90, 50), ink(DARK.line));633 assert_eq!(pixel(50, 50), ink(DARK.ground));634 assert_eq!(pixel(60, 30), ink(DARK.ground));635 let hexes = ring_pixels("eisenstein", 2, "class", false, 100).unwrap();636 let cell =637 |px: usize, py: usize| hexes.rgba[(py * 100 + px) * 4..(py * 100 + px) * 4 + 3].to_vec();638 assert_eq!(cell(80, 67), ink(DARK.pink));639 assert_eq!(cell(90, 50), ink(DARK.orange));640 assert_eq!(cell(70, 50), ink(DARK.green));641 assert_eq!(cell(50, 50), ink(DARK.ground));642 let plain = ring_pixels("gaussian", 3, "plain", false, 70).unwrap();643 assert_eq!(644 plain.rgba[(25 * 70 + 45) * 4..(25 * 70 + 45) * 4 + 3],645 ink(DARK.yellow)646 );647 let glow = ring_pixels("gaussian", 3, "norm", false, 70).unwrap();648 assert_ne!(649 glow.rgba[(25 * 70 + 45) * 4..(25 * 70 + 45) * 4 + 3],650 ink(DARK.yellow)651 );652 let hit = parse(&ring_at("gaussian", 40, 403.0, 374.0, 768).unwrap()).unwrap();653 assert_eq!(654 (655 hit["a"].clone(),656 hit["b"].clone(),657 hit["norm"].clone(),658 hit["class"].clone()659 ),660 (2.into(), 1.into(), 5.into(), "split".into())661 );662 assert_eq!(hit["prime"], true);663 assert_eq!(hit["associates"].as_array().unwrap().len(), 4);664 assert_eq!(hit["associates"][1][0], -1.0);665 assert_eq!(hit["associates"][1][1], 2.0);666 assert_eq!(hit["conjugate"][1], -1.0);667 assert!((hit["px"].as_f64().unwrap() - 402.963).abs() < 1e-3);668 assert!((hit["py"].as_f64().unwrap() - 374.519).abs() < 1e-3);669 let flake = parse(&ring_at("eisenstein", 5, 140.0, 127.0, 220).unwrap()).unwrap();670 assert_eq!(671 (672 flake["a"].clone(),673 flake["b"].clone(),674 flake["norm"].clone(),675 flake["class"].clone()676 ),677 (1.into(), (-1).into(), 3.into(), "ramified".into())678 );679 assert_eq!(flake["associates"].as_array().unwrap().len(), 6);680 assert_eq!(flake["conjugate"][0], 2.0);681 assert_eq!(flake["conjugate"][1], 1.0);682 assert_eq!(flake["factors"], parse("[[3,1]]").unwrap());683 assert!(ring_pixels("quaternion", 2, "class", true, 100).is_err());684 assert!(ring_pixels("gaussian", 0, "class", true, 100).is_err());685 assert!(ring_pixels("gaussian", 201, "class", true, 100).is_err());686 assert!(ring_pixels("gaussian", 2, "bad", true, 100).is_err());687 assert!(ring_pixels("gaussian", 2, "class", true, 2000).is_err());688 assert!(ring_weights("gaussian", 20_000).is_err());689 assert!(ring_fates("eisenstein", 20_000).is_err());690 assert!(ring_at("eisenstein", 5, 1.0, 1.0, 220).is_err());691 assert!(ring_census("gaussian", 300).is_err());692}693694#[test]695fn the_zeta_exports_answer() {696 let zeros = zeta_zeros(5).unwrap();697 let known = [14.134_725, 21.022_040, 25.010_858, 30.424_876, 32.935_062];698 for (got, want) in zeros.iter().zip(known) {699 assert!((got - want).abs() < 1e-6, "{got} {want}");700 }701 assert_eq!(zeta_count(100.0).unwrap(), 29);702 assert_eq!(zeta_count(200.0).unwrap(), 79);703 let root = zeta_at(14.134_725).unwrap();704 assert!(root[0].hypot(root[1]) < 1e-5);705 assert!(root[2].abs() < 1e-5);706 let half = zeta_at(0.0).unwrap();707 assert!((half[0] + 1.460_354_5).abs() < 1e-7);708 assert_eq!(half[3], 0.0);709 let walk = zeta_line(0.0, 50.0, 600).unwrap();710 assert_eq!(walk.len(), 2404);711 assert_eq!(walk[0], 0.0);712 assert_eq!(walk[2400], 50.0);713 assert!((walk[1] + 1.460_354_5).abs() < 1e-7);714 let seam = zeta_seam(250.0, 500).unwrap();715 assert_eq!(seam[0], 20.0);716 assert!(seam[1] < 5e-5);717 let stair = psi_stair(100).unwrap();718 assert!((stair[9] - 7.832_0).abs() < 1e-4);719 assert!((stair[99] - 94.045_3).abs() < 1e-4);720 let smooth = psi_formula(10.0, &[], 3).unwrap();721 assert_eq!(smooth.len(), 6);722 assert!((smooth[5] - 8.167_1).abs() < 1e-4);723 let hundred = zeta_zeros(100).unwrap();724 let folded = psi_formula(100.0, &hundred, 2).unwrap();725 assert!((folded[3] - stair[99]).abs() < 1.0);726 let gap = psi_gap(100, &hundred).unwrap();727 assert!((gap - (stair[99] - folded[3])).abs() < 1e-12);728 assert!(zeta_line(0.0, 300.0, 10).is_err());729 assert!(zeta_line(0.0, 10.0, 0).is_err());730 assert!(zeta_at(-1.0).is_err());731 assert!(zeta_zeros(101).is_err());732 assert!(zeta_count(251.0).is_err());733 assert!(zeta_seam(100.0, 0).is_err());734 assert!(psi_stair(1).is_err());735 assert!(psi_stair(1001).is_err());736 assert!(psi_formula(10.0, &[], 1).is_err());737 assert!(psi_formula(10.0, &vec![14.0; 101], 10).is_err());738 assert!(psi_gap(1, &[]).is_err());739}740741#[test]742fn the_graph_exports_answer() {743 let carpet = parse(&graph_census("flat", "7", 3, 1, 2, "core").unwrap()).unwrap();744 assert_eq!(745 (carpet["nodes"].clone(), carpet["branches"].clone()),746 (8.into(), 8.into())747 );748 assert_eq!(carpet["components"], 1);749 let knots = graph_nodes("flat", "7", 3, 1, 2, "core").unwrap();750 assert_eq!((knots[0], knots[1], knots.len()), (2.0, 8.0, 18));751 assert_eq!(752 graph_branches("flat", "7", 3, 1, 2, "core").unwrap().len(),753 16754 );755 assert_eq!(756 graph_roles("flat", "7", 3, 1, 2, "core").unwrap(),757 vec![2; 8]758 );759 let sponge = graph_nodes("cube", "23", 3, 1, 2, "core").unwrap();760 assert_eq!((sponge[0], sponge[1], sponge.len()), (3.0, 20.0, 62));761 assert_eq!(762 graph_branches("cube", "255", 1, 1, 2, "edge")763 .unwrap()764 .len(),765 24766 );767 let tally = parse(&graph_census("cube", "23", 3, 1, 2, "core").unwrap()).unwrap();768 assert_eq!(tally["nodes"], 20);769 assert_eq!(tally["branches"], 24);770 assert_eq!(tally["junctions"], 8);771 assert_eq!(tally["euler"], -4);772 let slice = parse(&graph_census("hex", "23", 3, 1, 2, "core").unwrap()).unwrap();773 assert_eq!(774 (slice["nodes"].clone(), slice["branches"].clone()),775 (42.into(), 48.into())776 );777 assert!((slice["length"].as_f64().unwrap() - 48.0 / 3f64.sqrt()).abs() < 1e-9);778 let rim = parse(&graph_census("hex", "23", 3, 1, 2, "edge").unwrap()).unwrap();779 assert_eq!(780 (rim["nodes"].clone(), rim["length"].clone()),781 (36.into(), 78.0.into())782 );783 assert_eq!(graph_size("flat", "495", 3, 2, 3, "core").unwrap(), "64");784 assert_eq!(graph_size("cube", "23", 3, 2, 2, "tunnel").unwrap(), "329");785 assert_eq!(graph_cap("flat", "7", 3, 2, "core", 20000).unwrap(), 4);786 assert_eq!(graph_cap("cube", "23", 3, 2, "core", 2000).unwrap(), 2);787 assert_eq!(graph_cap("hex", "23", 3, 2, "edge", 2000).unwrap(), 2);788 let ring = graph_nodes("flat", "15", 2, 1, 2, "core").unwrap();789 let loop_ = graph_branches("flat", "15", 2, 1, 2, "core").unwrap();790 let mut relax = Layout::new(&ring[2..], &loop_, 2, 1).unwrap();791 let rest = relax.step(500);792 assert!(rest < 1e-3, "energy {rest}");793 let p = relax.positions();794 let gaps: Vec<f32> = loop_795 .chunks(2)796 .map(|b| {797 let (a, c) = (b[0] as usize, b[1] as usize);798 ((p[2 * a] - p[2 * c]).powi(2) + (p[2 * a + 1] - p[2 * c + 1]).powi(2)).sqrt()799 })800 .collect();801 let spread = gaps.iter().cloned().fold(0.0, f32::max)802 - gaps.iter().cloned().fold(f32::INFINITY, f32::min);803 assert!(spread < 1e-3, "gaps {gaps:?}");804 assert_eq!(relax.ticks(), 500);805 assert!(relax.moved() < relax.temperature());806 assert!(graph_nodes("wobble", "7", 3, 1, 2, "core").is_err());807 assert!(graph_roles("flat", "7", 3, 1, 2, "dual").is_err());808 assert!(graph_census("hex", "23", 3, 1, 2, "tunnel").is_err());809 assert!(graph_nodes("flat", "7", 3, 9, 2, "core").is_err());810 assert!(graph_cap("flat", "16", 3, 2, "core", 100).is_err());811 assert!(Layout::new(&[0.0, 0.0, 1.0, 1.0], &[0, 2], 2, 1).is_err());812}813814#[test]815fn the_ledger_exports_answer() {816 assert_eq!(ledger_measures().len(), 12);817 assert_eq!(ledger_measures()[0], "fills");818 assert_eq!(819 ledger_designs(2, 2).unwrap(),820 ["0", "1", "3", "6", "7", "15"]821 );822 assert_eq!(ledger_designs(2, 3).unwrap().len(), 26);823 assert!(ledger_designs(3, 3).is_err());824 let budget = "500000";825 assert_eq!(826 ledger_terms("7", 2, 2, "fills", "level", 3, budget).unwrap(),827 ["8", "64", "512"]828 );829 assert_eq!(830 ledger_terms("23", 3, 2, "surface", "level", 3, budget).unwrap(),831 ["72", "1056", "18048"]832 );833 assert_eq!(834 ledger_terms("7", 2, 2, "fills", "side", 4, budget).unwrap(),835 ["8", "21", "40", "65"]836 );837 assert_eq!(838 ledger_terms("3", 2, 2, "fills", "side", 3, budget).unwrap(),839 ["6", "15", "28"]840 );841 assert_eq!(842 ledger_terms("23", 3, 2, "euler", "level", 8, "1000").unwrap(),843 ["-4", "-80"]844 );845 assert!(ledger_terms("7", 2, 2, "faces", "level", 1, budget).is_err());846 let found = parse(&ledger_identify("6, 42, 306, 2250")).unwrap();847 assert_eq!(848 (found[0]["id"].clone(), found[0]["shift"].clone()),849 ("A299916".into(), 1.into())850 );851 let octagonal = parse(&ledger_identify("8, 21, 40, 65")).unwrap();852 assert_eq!(853 (octagonal[0]["id"].clone(), octagonal[0]["shift"].clone()),854 ("A000567".into(), 2.into())855 );856 assert!(parse(&ledger_identify("x"))857 .unwrap()858 .as_array()859 .unwrap()860 .is_empty());861 assert_eq!(862 ledger_closed("7", 2, 2, "fills", "side").unwrap(),863 "3k^2 - 2k"864 );865 assert_eq!(866 ledger_closed("23", 3, 2, "surface", "level").unwrap(),867 "a(level) = 28 a(level-1) - 160 a(level-2)"868 );869 assert_eq!(ledger_closed("7", 2, 2, "euler", "level").unwrap(), "");870 let records = parse(&ledger_records()).unwrap();871 let records = records.as_array().unwrap();872 assert_eq!(records.len(), 60);873 let octagonal = records.iter().find(|r| r["id"] == "A000567").unwrap();874 assert_eq!(875 (octagonal["key"].clone(), octagonal["shift"].clone()),876 (877 "sequence_dim=2_code=7_measure=fills_axis=side".into(),878 0.into()879 )880 );881 assert_eq!(ledger_build("closed", 4).unwrap(), 7692);882 assert_eq!(ledger_build("closed", 4).unwrap(), 7692);883 assert!(ledger_build("deep", 4).is_err());884 let hits = parse(&ledger_search("8, 21, 40, 65", "", 2, 2, 0, 25)).unwrap();885 assert_eq!(hits["total"], 1);886 let row = &hits["rows"][0];887 assert_eq!(row["name"], "sequence_dim=2_code=7_measure=fills_axis=side");888 assert_eq!(889 (890 row["oeis"].clone(),891 row["shift"].clone(),892 row["tag"].clone()893 ),894 ("A000567".into(), 0.into(), "Proved".into())895 );896 assert_eq!(row["closed"], "3k^2 - 2k");897 let surfaces = parse(&ledger_search("", "surface", 3, 2, 0, 5)).unwrap();898 assert_eq!(surfaces["total"], 44);899 assert_eq!(surfaces["rows"].as_array().unwrap().len(), 5);900 assert_eq!(901 parse(&ledger_search("sequence_dim=3_code=23_", "", 0, 0, 0, 100)).unwrap()["total"],902 6903 );904 assert_eq!(905 parse(&ledger_search("A381517", "", 0, 0, 0, 100)).unwrap()["rows"][0]["terms"][1],906 "80"907 );908 let grown = parse(&ledger_grow("convolved", 4, 100).unwrap()).unwrap();909 assert_eq!(910 (911 grown["rows"].clone(),912 grown["done"].clone(),913 grown["total"].clone()914 ),915 (7792.into(), 100.into(), 5044.into())916 );917 assert!(ledger_grow("deep", 4, 100).is_err());918 let void = parse(&ledger_row("9", 2, 2, "voids", "side", 3, "500000").unwrap()).unwrap();919 assert_eq!(920 void["name"],921 "sequence_dim=2_code=9_measure=voids_axis=side"922 );923 assert_eq!(void["terms"], parse(r#"["4", "12", "24"]"#).unwrap());924 assert_eq!(void["closed"], "2k^2 - 2k");925 assert_eq!(void["number"], 3);926 assert!(ledger_row("7", 2, 2, "faces", "level", 3, "500000").is_err());927 let gasket = ledger_profile("126", 3, 2, 2, 4).unwrap();928 assert_eq!(gasket.len(), 46);929 assert!(gasket[15..=30].iter().all(|count| count == "81"));930 assert_eq!(931 ledger_profile("1", 1, 2, 3, 2).unwrap().join(""),932 "101000101"933 );934}935936#[test]937fn the_tour_exports_answer() {938 let farey = parse(&farey_novelty(7)).unwrap();939 assert_eq!(940 (farey["lit"].clone(), farey["novel"].clone()),941 (19.into(), 19.into())942 );943 let cut = parse(&diagonal_profile("126", 2, 5, 2).unwrap()).unwrap();944 assert_eq!(945 (cut["max"].clone(), cut["constant"].clone()),946 ("243".into(), true.into())947 );948 assert_eq!(949 parse(&slice_census("23", 9, 1, 2).unwrap()).unwrap()["vertices"],950 271951 );952 assert_eq!(953 parse(&slice_census("23", 1, 1, 2).unwrap()).unwrap()["fills"],954 6955 );956 assert_eq!(957 parse(&two_census("9", 5, 1, 0, 2).unwrap()).unwrap()["fills"],958 13959 );960 assert_eq!(baseq_sequence(5, 2).unwrap(), ["8", "172112"]);961 let first = |terms: &str| {962 let found = parse(&ledger_identify(terms)).unwrap();963 (found[0]["id"].clone(), found[0]["shift"].clone())964 };965 assert_eq!(first("3, 9, 27, 81"), ("A000244".into(), 1.into()));966 assert_eq!(first("2, 3, 5, 7, 11, 13"), ("A005728".into(), 1.into()));967 assert_eq!(first("7, 37, 91, 169"), ("A154105".into(), 0.into()));968 assert_eq!(first("4, 12, 64, 700"), ("A129824".into(), 1.into()));969 assert_eq!(first("20, 81, 208"), ("A103532".into(), 1.into()));970}971972#[test]973fn the_census_exports_answer() {974 let window = parse(&census_window()).unwrap();975 assert_eq!(window["registry"], 18066);976 assert_eq!(window["cap"], 48);977 assert_eq!(window["cells"], "100000");978 assert_eq!(window["ceiling"], "1000");979 assert_eq!(window["head"], 8);980 assert_eq!(window["depths"], parse("[8, 16, 32, 48]").unwrap());981 let tiers = window["tiers"].as_array().unwrap();982 let keyed: Vec<(String, u64)> = tiers983 .iter()984 .map(|tier| {985 (986 tier["tier"].as_str().unwrap().to_string(),987 tier["keys"].as_u64().unwrap(),988 )989 })990 .collect();991 assert_eq!(992 keyed,993 [994 ("closed".to_string(), 7692),995 ("convolved".to_string(), 5044),996 ("side".to_string(), 2665),997 ("level".to_string(), 2665),998 ]999 );1000 let walk = parse(&census_walk(7692)).unwrap();1001 assert_eq!(1002 (1003 walk["depth"].clone(),1004 walk["done"].clone(),1005 walk["total"].clone()1006 ),1007 (8.into(), 7692.into(), 18066.into())1008 );1009 assert_eq!(1010 (1011 walk["never"].clone(),1012 walk["once"].clone(),1013 walk["multiple"].clone()1014 ),1015 (396.into(), 102.into(), 502.into())1016 );1017 let report = parse(&census_report()).unwrap();1018 assert_eq!(report["rows"], 7692);1019 assert_eq!(report["written"], 604);1020 assert_eq!(report["first_miss"], 83);1021 assert_eq!(report["incidences"], 30865);1022 assert_eq!(report["low"], 452);1023 assert_eq!(report["ceiling_stopped"], 5048);1024 assert_eq!(report["cap_stopped"], 2644);1025 assert_eq!(report["blank"], 54);1026 assert_eq!(report["bands"][1]["missed"], 2);1027 assert_eq!(report["tiers"][0]["written"], 604);1028 let counts = census_counts();1029 assert_eq!(counts.len(), 1000);1030 assert_eq!((counts[0], counts[15]), (102, 633));1031 let writers = parse(&census_writers(16, 0, 2)).unwrap();1032 assert_eq!(1033 (writers["inside"].clone(), writers["rows"].clone()),1034 (true.into(), 633.into())1035 );1036 assert_eq!(writers["tiers"][0]["rows"], 633);1037 let first = &writers["shown"][0];1038 assert_eq!(1039 first["name"],1040 "sequence_dim=1_code=1_measure=fills_axis=level"1041 );1042 assert_eq!(first["closed"], "2^level");1043 assert_eq!(1044 (first["index"].clone(), first["term"].clone()),1045 (3.into(), 4.into())1046 );1047 assert_eq!(first["head"][3], "16");1048 assert_eq!(1049 writers["shown"][1]["closed"],1050 "a(level) = 3 a(level-1) - 2 a(level-2)"1051 );1052 let paged = parse(&census_writers(16, 0, 633)).unwrap();1053 let sided = paged["shown"]1054 .as_array()1055 .unwrap()1056 .iter()1057 .find(|row| row["axis"] == "side")1058 .unwrap();1059 assert_eq!(1060 (1061 sided["name"].clone(),1062 sided["term"].clone(),1063 sided["side"].clone()1064 ),1065 (1066 "sequence_dim=1_code=1_measure=surface_axis=side".into(),1067 8.into(),1068 15.into()1069 )1070 );1071 let outside = parse(&census_writers(1001, 0, 1)).unwrap();1072 assert_eq!(1073 (outside["inside"].clone(), outside["rows"].clone()),1074 (false.into(), 0.into())1075 );1076 let champions = parse(&census_champions(2)).unwrap();1077 assert_eq!(1078 (champions[0]["value"].clone(), champions[0]["rows"].clone()),1079 (16.into(), 633.into())1080 );1081 let misses = parse(&census_misses(3)).unwrap();1082 assert_eq!(misses, parse("[83, 86, 107]").unwrap());1083}10841085fn word(codes: &[&str], numbers: &[u32], bases: &[u32]) -> (Vec<String>, Vec<u32>, Vec<u32>) {1086 (1087 codes.iter().map(|c| c.to_string()).collect(),1088 numbers.to_vec(),1089 bases.to_vec(),1090 )1091}10921093fn census(codes: &[&str], numbers: &[u32], dimension: usize, bases: &[u32]) -> mrlycore::Json {1094 let (codes, numbers, bases) = word(codes, numbers, bases);1095 parse(&magic_census(codes, numbers, dimension, bases).unwrap()).unwrap()1096}10971098#[test]1099fn the_word_fixture_the_page_prints() {1100 let doctest = census(&["7", "14", "9"], &[3, 7, 5], 2, &[2, 2, 2]);1101 assert_eq!(doctest["side"], "105");1102 assert_eq!(doctest["cells"], "11025");1103 assert_eq!(doctest["fill"], "3432");1104 assert_eq!(1105 format!("{:.9}", doctest["dimension"].as_f64().unwrap()),1106 "1.749241044"1107 );1108 assert_eq!(doctest["components"], "2496");1109 assert_eq!(doctest["counted"], "drawn");1110 let (codes, numbers, bases) = word(&["7", "14", "9"], &[3, 7, 5], &[2, 2, 2]);1111 assert_eq!(1112 word_count(codes.clone(), numbers.clone(), 2, bases.clone()).unwrap(),1113 doctest["fill"].as_str().unwrap()1114 );1115 assert_eq!(1116 word_profile(codes.clone(), numbers.clone(), 2, bases.clone())1117 .unwrap()1118 .len(),1119 2091120 );1121 assert_eq!(1122 magic_name(codes.clone(), numbers.clone(), bases.clone(), 2).unwrap(),1123 "word dim 2, magic [7 14 9], side [3 7 5]"1124 );1125 assert_eq!(1126 magic_key(codes, numbers, bases, 2).unwrap(),1127 "word_dim=2_magic=[7,14,9]_side=[3,7,5]"1128 );1129 let back = parse(&magic_parse("word_dim=2_magic=[7,14,9]_side=[3,7,5]").unwrap()).unwrap();1130 assert_eq!(back["codes"][1], "14");1131 assert_eq!(back["numbers"][2], 5);11321133 let one = census(&["7", "9"], &[3, 5], 2, &[2, 2]);1134 let twice = census(&["7", "9", "7", "9"], &[3, 5, 3, 5], 2, &[2, 2, 2, 2]);1135 let square = |text: &str| text.parse::<u128>().unwrap().pow(2).to_string();1136 assert_eq!(twice["side"], square(one["side"].as_str().unwrap()));1137 assert_eq!(twice["fill"], square(one["fill"].as_str().unwrap()));1138 assert_eq!(twice["dimension"], one["dimension"]);1139 assert_eq!(1140 (twice["periodic"].clone(), one["periodic"].clone()),1141 (true.into(), false.into())1142 );11431144 let ahead = census(&["3", "6"], &[2, 2], 2, &[2, 2]);1145 let behind = census(&["6", "3"], &[2, 2], 2, &[2, 2]);1146 assert_eq!(ahead["fill"], behind["fill"]);1147 assert_eq!(ahead["side"], behind["side"]);1148 assert_eq!(1149 (ahead["components"].clone(), behind["components"].clone()),1150 ("4".into(), "2".into())1151 );1152 assert_eq!(ahead["counted"], "closed");11531154 let ladder = census(1155 &["7", "7", "7", "7", "7"],1156 &[3, 5, 7, 9, 11],1157 2,1158 &[2, 2, 2, 2, 2],1159 );1160 for letter in ladder["letters"].as_array().unwrap() {1161 let side = letter["number"].as_u64().unwrap();1162 let law = side * side - ((side - 1) / 2).pow(2);1163 assert_eq!(letter["fill"], law.to_string(), "side {side}");1164 }1165 let stair = parse(&magic_staircase(5).unwrap()).unwrap();1166 let read = |row: usize| format!("{:.9}", stair["rows"][row]["dimension"].as_f64().unwrap());1167 assert_eq!(read(0), "1.892789261");1168 assert_eq!(read(1), "1.892315261");1169 assert_eq!(stair["rows"][2]["length"], 6);1170 assert!(stair["rows"][1]["dimension"].as_f64() < stair["rows"][0]["dimension"].as_f64());1171 assert_eq!(1172 format!("{:.9}", stair["constant"].as_f64().unwrap()),1173 read(0)1174 );11751176 assert_eq!(magic_cap(vec![3, 7, 5, 3], 2, 243).unwrap(), 3);1177 assert_eq!(magic_cap(vec![3, 3, 3, 3, 3], 3, 128).unwrap(), 4);1178 let menger = census(&["23", "23", "23"], &[3, 3, 3], 3, &[2, 2, 2]);1179 assert_eq!(menger["fill"], "8000");1180 assert_eq!(menger["constant"], true);1181 let (codes, numbers, bases) = word(&["23", "23", "23"], &[3, 3, 3], &[2, 2, 2]);1182 assert_eq!(1183 magic_cells(codes.clone(), numbers.clone(), bases.clone())1184 .unwrap()1185 .len()1186 / 3,1187 80001188 );1189 assert_eq!(magic_surface(codes, numbers, bases).unwrap(), "18048");11901191 let rates = parse(1192 &magic_rates(1193 vec!["3".into(), "7".into()],1194 vec![2, 2],1195 vec![2, 2],1196 "thue-morse",1197 64,1198 )1199 .unwrap(),1200 )1201 .unwrap();1202 assert_eq!(1203 format!("{:.15}", rates["limit"].as_f64().unwrap()),1204 "1.292481250360578"1205 );1206 assert_eq!(rates["phi"], 0.0);1207 assert_eq!(rates["length"], 64);1208 let last = rates["rows"][63][0].as_f64().unwrap();1209 assert_eq!(1210 rates["rows"][63][1].as_f64().unwrap(),1211 rates["limit"].as_f64().unwrap()1212 );1213 assert!(last < rates["limit"].as_f64().unwrap());1214 assert!(last > rates["rows"][31][0].as_f64().unwrap());1215 assert!(rates["control"][63].as_f64().unwrap() > 0.0);12161217 let ahead = magic_grid(vec!["9".into(), "273".into()], vec![2, 3], vec![2, 3]).unwrap();1218 let behind = magic_grid(vec!["273".into(), "9".into()], vec![3, 2], vec![3, 2]).unwrap();1219 assert_eq!((ahead.width, behind.width), (6, 6));1220 assert_eq!(ahead.types, behind.types);1221 assert_eq!(ahead.types.iter().map(|&b| b as usize).sum::<usize>(), 6);1222}12231224fn lift(kind: &str, level: usize) -> Vec<u8> {1225 morse_lift(kind, level).unwrap().types1226}12271228#[test]1229fn the_morse_fixture_the_page_prints() {1230 let read = parse(&morse_word(64).unwrap()).unwrap();1231 assert_eq!(read["agree"], true);1232 assert_eq!(read["ones"], 32);1233 assert_eq!(read["longest"], 2);1234 assert_eq!(read["cube_free"], true);1235 assert_eq!(read["singles"], 22);1236 assert_eq!(read["doubles"], 21);1237 assert_eq!(read["doubling_agree"], true);1238 assert_eq!(read["digits"].as_array().unwrap().len(), 64);1239 assert_eq!(read["boundary"].as_array().unwrap().len(), 63);1240 assert_eq!(read["boundary"][0], 1);1241 assert_eq!(morse_stage(3).unwrap(), vec![0, 1, 1, 0, 1, 0, 0, 1]);1242 assert_eq!(morse_stage(6).unwrap().len(), 64);12431244 let gallery = parse(&morse_gallery(6).unwrap()).unwrap();1245 let row = |at: usize| gallery[at].clone();1246 assert_eq!(row(0)["formula"], "t(i) xor t(j)");1247 assert_eq!(row(0)["folds"], true);1248 assert_eq!(row(0)["tile"], parse("[0,1,1,0]").unwrap());1249 assert_eq!(row(0)["design"], "9");1250 assert_eq!(row(1)["formula"], "t(i and j)");1251 assert_eq!(row(1)["folds"], true);1252 assert_eq!(row(1)["tile"], parse("[0,0,0,1]").unwrap());1253 assert_eq!(row(1)["design"], "7");1254 assert_eq!(row(2)["formula"], "t(i xor j)");1255 assert_eq!(row(2)["folds"], true);1256 assert_eq!(row(2)["twin"], "parity");1257 assert_eq!(row(3)["formula"], "t(i + j)");1258 assert_eq!(row(3)["folds"], false);1259 assert_eq!(row(3)["faults"], 1376);1260 assert_eq!(row(3)["first"], parse("[1,3]").unwrap());1261 assert_eq!(row(3)["design"], mrlycore::Json::Null);12621263 for level in 1..10 {1264 let rows = parse(&morse_gallery(level).unwrap()).unwrap();1265 assert_eq!(rows[2]["twin"], "parity", "level {level}");1266 assert_eq!(rows[3]["folds"], level == 1, "level {level}");1267 }12681269 let side = morse_lift("parity", 6).unwrap();1270 assert_eq!((side.width, side.height), (64, 64));1271 assert_eq!(lift("xor", 6), lift("parity", 6));1272 assert_eq!(morse_signs("9", 2, 2, 6).unwrap().types, lift("parity", 6));1273 assert_eq!(morse_signs("7", 2, 2, 6).unwrap().types, lift("and", 6));1274 assert_eq!(1275 lift("parity", 6).iter().map(|&b| b as usize).sum::<usize>(),1276 20481277 );12781279 let sign = parse(&morse_filter("9", 2, 2, 3, "sign").unwrap()).unwrap();1280 assert_eq!(sign["morse_tile"], true);1281 assert_eq!(sign["form"], "the base tile repeated");1282 assert_eq!(sign["closed_exact"], true);1283 assert_eq!(sign["morse_exact"], false);1284 assert_eq!(sign["side"], 16);1285 assert_eq!(sign["morse_faults"], 128);1286 assert_eq!(sign["lit"], 128);1287 for code in 0..16u32 {1288 for level in 1..5 {1289 let read =1290 parse(&morse_filter(&code.to_string(), 2, 2, level, "sign").unwrap()).unwrap();1291 let side = read["side"].as_u64().unwrap();1292 assert_eq!(read["closed_exact"], true, "code {code} level {level}");1293 assert_eq!(1294 read["morse_faults"],1295 side * side / 2,1296 "code {code} level {level}"1297 );1298 }1299 }13001301 let flat = parse(&morse_filter("7", 2, 2, 3, "design").unwrap()).unwrap();1302 assert_eq!(1303 flat["form"],1304 "the level below, punched by the tile's complement"1305 );1306 assert_eq!(flat["closed_exact"], true);1307 assert_eq!(flat["morse_exact"], false);1308 assert_eq!(1309 (flat["side"].clone(), flat["lit"].clone()),1310 (16.into(), 27.into())1311 );1312 assert_eq!(flat["morse_faults"], 127);1313 let wide = parse(&morse_filter("495", 3, 3, 2, "design").unwrap()).unwrap();1314 assert_eq!(wide["closed_exact"], true);1315 assert_eq!(wide["morse_faults"], mrlycore::Json::Null);1316 assert_eq!(wide["side"], 27);13171318 assert!(morse_word(0).is_err());1319 assert!(morse_lift("cube", 4).is_err());1320 assert!(morse_lift("parity", 10).is_err());1321 assert!(morse_filter("7", 2, 2, 3, "product").is_err());1322}13231324#[test]1325fn the_tile_fixture_the_page_prints() {1326 let read = |dimension: usize,1327 code: &str,1328 number: usize,1329 level: usize,1330 base: usize,1331 projection: &str,1332 reps: Vec<u32>,1333 crop: bool| {1334 parse(&tile_census(code, number, level, base, dimension, projection, reps, crop).unwrap())1335 .unwrap()1336 };1337 let count = |cell: &mrlycore::Json, key: &str| cell[key].as_str().unwrap().to_string();13381339 let wide = read(2, "495", 3, 2, 3, "", vec![5, 5], false);1340 assert_eq!(wide["tile"], parse("[9,9]").unwrap());1341 assert_eq!(wide["sheet"], parse("[45,45]").unwrap());1342 assert_eq!(count(&wide, "fills"), "1600");1343 assert_eq!(count(&wide, "voids"), "425");1344 assert_eq!(count(&wide, "exposed"), "1280");1345 assert_eq!(count(&wide, "tile_exposed"), "80");1346 assert_eq!(count(&wide, "buried"), "720");1347 assert_eq!(1348 (wide["vertices"].clone(), wide["euler"].clone()),1349 (2016.into(), (-224).into())1350 );13511352 let tall = read(2, "495", 3, 2, 3, "", vec![3, 9], false);1353 assert_eq!(tall["sheet"], parse("[27,81]").unwrap());1354 assert_eq!(count(&tall, "fills"), "1728");1355 assert_eq!(count(&tall, "exposed"), "1404");1356 assert_eq!(count(&tall, "buried"), "756");1357 assert_eq!(1358 (tall["vertices"].clone(), tall["euler"].clone()),1359 (2188.into(), (-242).into())1360 );13611362 let block = read(3, "23", 3, 1, 2, "", vec![5, 5, 5], false);1363 assert_eq!(block["sheet"], parse("[15,15,15]").unwrap());1364 assert_eq!(count(&block, "fills"), "2500");1365 assert_eq!(count(&block, "voids"), "875");1366 assert_eq!(count(&block, "exposed"), "4200");1367 assert_eq!(count(&block, "tile_exposed"), "72");1368 assert_eq!(count(&block, "buried"), "4800");1369 assert_eq!(1370 (block["faces"].clone(), block["euler"].clone()),1371 (9600.into(), (-324).into())1372 );13731374 let slab = read(3, "23", 3, 1, 2, "", vec![3, 9, 3], false);1375 assert_eq!(slab["sheet"], parse("[9,27,9]").unwrap());1376 assert_eq!(count(&slab, "fills"), "1620");1377 assert_eq!(count(&slab, "exposed"), "2952");1378 assert_eq!(count(&slab, "buried"), "2880");1379 assert_eq!(1380 (slab["faces"].clone(), slab["euler"].clone()),1381 (6336.into(), (-224).into())1382 );13831384 let mesh = read(6, "23", 3, 1, 2, "cut", vec![5, 5], false);1385 assert_eq!(mesh["tile"], parse("[11,6]").unwrap());1386 assert_eq!(mesh["sheet"], parse("[47,33]").unwrap());1387 assert_eq!(mesh["triangles"], 1350);1388 assert_eq!(count(&mesh, "fills"), "1050");1389 assert_eq!(count(&mesh, "voids"), "300");1390 assert_eq!(count(&mesh, "exposed"), "414");1391 assert_eq!(count(&mesh, "tile_exposed"), "30");1392 assert_eq!(count(&mesh, "buried"), "336");1393 assert_eq!(mesh["euler"], 1);13941395 let strip = read(6, "23", 3, 1, 2, "cut", vec![3, 9], false);1396 assert_eq!(strip["sheet"], parse("[29,57]").unwrap());1397 assert_eq!(strip["triangles"], 1458);1398 assert_eq!(count(&strip, "fills"), "1134");1399 assert_eq!(count(&strip, "exposed"), "462");1400 assert_eq!(strip["euler"], 1);14011402 let trimmed = read(6, "23", 3, 1, 2, "cut", vec![5, 5], true);1403 assert_eq!(trimmed["sheet"], parse("[43,27]").unwrap());1404 assert_eq!(trimmed["triangles"], 1161);1405 assert_eq!(count(&trimmed, "fills"), "891");1406 assert_eq!(count(&trimmed, "exposed"), "357");1407 assert_eq!(trimmed["euler"], 1);14081409 let narrow = read(6, "23", 3, 1, 2, "cut", vec![3, 9], true);1410 assert_eq!(narrow["sheet"], parse("[25,51]").unwrap());1411 assert_eq!(narrow["triangles"], 1275);1412 assert_eq!(count(&narrow, "fills"), "969");1413 assert_eq!(narrow["euler"], 1);14141415 let grid = tile_grid("495", 3, 2, 3, 5, 5).unwrap();1416 assert_eq!((grid.width, grid.height), (45, 45));1417 assert_eq!(grid.types.iter().map(|&b| b as usize).sum::<usize>(), 1600);1418 assert_eq!(tile_cells("23", 3, 1, 2, 5, 5, 5).unwrap().len() / 3, 2500);1419 let art = tile_svg("23", 3, 1, 2, "cut", 5, 5, true, 6).unwrap();1420 assert_eq!(art.matches("<polygon").count(), 1161);14211422 assert!(tile_census("495", 3, 2, 3, 4, "", vec![5, 5], false).is_err());1423 assert!(tile_census("495", 3, 2, 3, 2, "", vec![5, 5, 5], false).is_err());1424 assert!(tile_svg("23", 3, 1, 2, "cut", 1, 5, true, 6).is_err());1425 assert!(tile_grid("495", 3, 6, 3, 5, 5).is_err());1426}14271428#[test]1429fn the_word_reaches_every_dimension_the_tower_draws() {1430 let word = |list: [&str; 2]| list.iter().map(|c| c.to_string()).collect::<Vec<String>>();1431 let sponge = word(["23", "23"]);1432 let sides = vec![3u32, 3];1433 let bases = vec![2u32, 2];14341435 assert_eq!(1436 magic_perimeter(word(["7", "9"]), vec![3, 5], bases.clone()).unwrap(),1437 "368"1438 );1439 assert_eq!(1440 magic_perimeter(1441 vec!["7".into(), "14".into(), "9".into()],1442 vec![3, 7, 5],1443 vec![2, 2, 2]1444 )1445 .unwrap(),1446 "11856"1447 );14481449 let cut =1450 parse(&magic_hex_census(sponge.clone(), sides.clone(), bases.clone(), "cut").unwrap())1451 .unwrap();1452 assert_eq!(cut["grid"], parse("[35,18]").unwrap());1453 assert_eq!(cut["triangles"], 486);1454 assert_eq!(cut["fills"], 306);1455 assert_eq!(cut["voids"], 180);1456 assert_eq!(cut["exposed"], 162);1457 assert_eq!(cut["euler"], 1);1458 let solo = parse(&slice_census("23", 3, 2, 2).unwrap()).unwrap();1459 assert_eq!(cut["fills"], solo["fills"]);1460 assert_eq!(cut["triangles"], solo["triangles"]);14611462 let iso =1463 parse(&magic_hex_census(sponge.clone(), sides.clone(), bases.clone(), "iso").unwrap())1464 .unwrap();1465 assert_eq!(iso["grid"], parse("[18,35]").unwrap());1466 assert_eq!(iso["fills"], 486);1467 assert_eq!(iso["voids"], 0);1468 assert_eq!(iso["exposed"], 88);14691470 let art = magic_hex(sponge, sides, bases, "cut", 2).unwrap();1471 assert_eq!(art.matches("<polygon").count(), 486);1472}14731474#[test]1475fn the_blend_exports_answer() {1476 let budget = "500000";1477 let surface = ledger_terms("23", 3, 2, "surface", "level", 8, budget).unwrap();1478 assert_eq!(surface[7], "51267108864");1479 let rule = parse(&blend_recurrence(surface.clone()).unwrap()).unwrap();1480 assert_eq!(rule["order"], 2);1481 assert_eq!(rule["coefficients"], parse("[[28,1],[-160,1]]").unwrap());1482 assert_eq!(rule["recurrence"], "a(n) = 28 a(n-1) - 160 a(n-2)");1483 let poly = blend_characteristic(&rule["coefficients"].to_string()).unwrap();1484 assert_eq!(1485 parse(&poly).unwrap(),1486 parse("[[1,1],[-28,1],[160,1]]").unwrap()1487 );1488 let root = blend_growth(&rule["coefficients"].to_string()).unwrap();1489 assert!((root - 20.0).abs() < 1e-12, "root {root}");1490 assert_eq!(1491 blend_recurrence(vec![1492 "2".into(),1493 "3".into(),1494 "5".into(),1495 "7".into(),1496 "11".into(),1497 "13".into(),1498 "17".into(),1499 "19".into()1500 ])1501 .unwrap(),1502 "null"1503 );15041505 let series =1506 parse(&blend_series("23", 3, 2, "surface", "level", 8, budget, 4).unwrap()).unwrap();1507 assert_eq!(1508 series["name"],1509 "sequence_dim=3_code=23_measure=surface_axis=level"1510 );1511 assert_eq!(series["oeis"], "A332705");1512 assert_eq!(1513 series["closed"],1514 "a(level) = 28 a(level-1) - 160 a(level-2)"1515 );1516 assert_eq!(series["recurrence"], "a(n) = 28 a(n-1) - 160 a(n-2)");1517 assert_eq!(series["polynomial"], "x^2 - 28 x + 160");1518 assert_eq!(series["growth_from"], "the recurrence root");1519 assert_eq!(series["order"], 2);1520 assert_eq!(series["ratios"][0], "14.6667");1521 assert_eq!(series["differences"][1][0], "984");1522 assert_eq!(series["differences"].as_array().unwrap().len(), 4);1523 let growth = series["growth"].as_f64().unwrap();1524 let exponent = series["exponent"].as_f64().unwrap();1525 assert!((growth - 20.0).abs() < 1e-12, "growth {growth}");1526 assert!(1527 (exponent - 20f64.log10()).abs() < 1e-12,1528 "exponent {exponent}"1529 );1530 let logs = series["log10"].as_array().unwrap();1531 assert!((logs[0].as_f64().unwrap() - 72f64.log10()).abs() < 1e-12);15321533 let side = parse(&blend_series("7", 2, 2, "fills", "side", 12, budget, 5).unwrap()).unwrap();1534 assert_eq!(side["closed"], "3k^2 - 2k");1535 assert_eq!(side["order"], 3);1536 assert_eq!(side["coefficients"], parse("[[3,1],[-3,1],[1,1]]").unwrap());1537 assert_eq!(side["polynomial"], "x^3 - 3 x^2 + 3 x - 1");1538 assert_eq!(side["differences"][3][0], "0");15391540 let family = parse(&blend_family(2, 2, "fills", "level", 3, budget).unwrap()).unwrap();1541 let family = family.as_array().unwrap();1542 assert_eq!(family.len(), 6);1543 assert_eq!(family[4]["code"], "7");1544 assert_eq!(1545 family[4]["name"],1546 "sequence_dim=2_code=7_measure=fills_axis=level"1547 );1548 assert_eq!(family[4]["terms"], parse(r#"["8", "64", "512"]"#).unwrap());1549 assert_eq!(1550 parse(&blend_family(2, 3, "fills", "level", 2, budget).unwrap())1551 .unwrap()1552 .as_array()1553 .unwrap()1554 .len(),1555 261556 );15571558 let fills = ledger_terms("7", 2, 2, "fills", "level", 8, budget).unwrap();1559 let faces = ledger_terms("7", 2, 2, "surface", "level", 8, budget).unwrap();1560 let mix = parse(&blend_mix(faces.clone(), fills.clone(), "hadamard", 0, 3).unwrap()).unwrap();1561 assert_eq!(mix["terms"][0], "128");1562 assert_eq!(mix["terms"][3], "14483456");1563 assert_eq!(mix["order"], 2);1564 assert_eq!(mix["coefficients"], parse("[[88,1],[-1536,1]]").unwrap());1565 assert_eq!(mix["polynomial"], "x^2 - 88 x + 1536");1566 assert_eq!(mix["recurrence"], "a(n) = 88 a(n-1) - 1536 a(n-2)");1567 let mixed = mix["growth"].as_f64().unwrap();1568 assert!((mixed - 64.0).abs() < 1e-12, "growth {mixed}");1569 let sums = parse(&blend_mix(fills.clone(), vec![], "sigma", 0, 2).unwrap()).unwrap();1570 assert_eq!(sums["terms"][2], "584");1571 assert_eq!(sums["order"], 2);1572 let cut = parse(&blend_mix(fills.clone(), vec![], "decimate", 2, 2).unwrap()).unwrap();1573 assert_eq!(1574 cut["terms"],1575 parse(r#"["8", "512", "32768", "2097152"]"#).unwrap()1576 );15771578 assert_eq!(blend_ops().len(), 9);1579 assert_eq!(moire_correlation(3, 5), 0.0);1580 let paired = moire_correlation(3, 9);1581 assert!(1582 (paired - 0.219_264_504_826_757_3).abs() < 1e-12,1583 "r {paired}"1584 );1585 assert_eq!(moire_correlation(1, 9), 0.0);15861587 assert!(blend_recurrence(vec!["x".into()]).is_err());1588 assert!(blend_characteristic("[[1,0]]").is_err());1589 assert!(blend_growth("nonsense").is_err());1590 assert!(blend_series("7", 2, 2, "faces", "level", 3, budget, 3).is_err());1591 assert!(blend_family(3, 3, "fills", "level", 3, budget).is_err());1592 assert!(blend_mix(fills, faces, "twist", 0, 3).is_err());1593}15941595#[test]1596fn the_carry_automaton_reads_the_published_block() {1597 assert_eq!(carry_cap(3).unwrap(), 15);1598 assert_eq!(carry_cap(5).unwrap(), 11);1599 let anchor = parse(&carry_block(3, 3, 6).unwrap()).unwrap();1600 assert_eq!(anchor["digits"].to_string(), "[1,3,3,6,3,3,1]");1601 assert_eq!(anchor["block"].to_string(), "[[6,6],[1,3]]");1602 assert_eq!(anchor["characteristic"].to_string(), r#"["1","-9","12"]"#);1603 assert_eq!(anchor["polynomial"], "x^2 - 9 x + 12");1604 assert_eq!(1605 (1606 anchor["trace"].clone(),1607 anchor["determinant"].clone(),1608 anchor["fill"].clone()1609 ),1610 ("9".into(), "12".into(), "20".into())1611 );1612 let root = anchor["read"]["root"].as_f64().unwrap();1613 assert!((root - (9.0 + 33f64.sqrt()) / 2.0).abs() < 1e-9);1614 assert!((anchor["read"]["log_root"].as_f64().unwrap() - 1.818_410).abs() < 1e-6);1615 assert!((anchor["read"]["log_fill"].as_f64().unwrap() - 1.726_833).abs() < 1e-6);1616 assert_eq!(anchor["read"]["sign"], 1);1617 assert_eq!(1618 anchor["terms"].to_string(),1619 r#"["1","6","42","306","2250","16578","122202"]"#1620 );1621 assert_eq!(1622 anchor["ratios"].to_string(),1623 r#"["6","7","7.2857","7.3529","7.368","7.3713"]"#1624 );1625 let traces: Vec<String> = (2..=7)1626 .map(|dimension| {1627 parse(&carry_block(3, dimension, 1).unwrap()).unwrap()["trace"].to_string()1628 })1629 .collect();1630 assert_eq!(traces.join(","), r#""2","9","11","60","47","336""#);1631 let ladder = |base: usize, dimension: usize, levels: usize| {1632 parse(&carry_block(base, dimension, levels).unwrap()).unwrap()["terms"].to_string()1633 };1634 assert_eq!(1635 ladder(3, 4, 6),1636 r#"["1","6","132","1848","29040","441408","6772128"]"#1637 );1638 assert_eq!(ladder(3, 5, 4), r#"["1","30","1000","35700","1321600"]"#);1639 assert_eq!(ladder(3, 6, 4), r#"["1","20","4030","242300","24642700"]"#);1640 assert_eq!(ladder(5, 3, 4), r#"["1","18","414","9702","227646"]"#);1641 let deep = parse(&carry_block(3, 15, 32).unwrap()).unwrap();1642 assert_eq!(1643 (deep["levels"].clone(), deep["capped"].clone()),1644 (7.into(), true.into())1645 );1646 assert!(carry_block(3, 16, 4).is_err());1647 assert!(carry_block(5, 12, 4).is_err());1648 assert!(carry_block(4, 3, 4).is_err());1649 assert!(carry_block(3, 3, 0).is_err());1650 assert!(carry_signs(1).is_err());1651 assert!(carry_ratios(3, 3).is_err());1652}16531654#[test]1655fn the_carry_ladder_is_the_sponge_diagonal_count() {1656 let anchor = parse(&carry_block(3, 3, 5).unwrap()).unwrap();1657 let counted: Vec<String> = (1..=5)1658 .map(|level| {1659 let height = 3 * (3usize.pow(level as u32) - 1) / 2;1660 diagonal_count("23", 3, level, 2, height).unwrap()1661 })1662 .collect();1663 assert_eq!(counted.join(","), "6,42,306,2250,16578");1664 assert_eq!(1665 anchor["terms"].to_string(),1666 r#"["1","6","42","306","2250","16578"]"#1667 );1668 assert_eq!(1669 parse(&slice_census("23", 3, 1, 2).unwrap()).unwrap()["fills"],1670 421671 );1672 assert_eq!(1673 parse(&slice_census("23", 3, 2, 2).unwrap()).unwrap()["fills"],1674 3061675 );1676 assert_eq!(1677 column(&parse(&slice_series("23", 2).unwrap()).unwrap(), "fills"),1678 "6,42"1679 );1680 for dimension in 2..=6 {1681 let order = dimension / 2 + dimension % 2;1682 let row = parse(&carry_block(3, dimension, 2 * order + 1).unwrap()).unwrap();1683 assert_eq!(row["order"], order, "dimension {dimension}");1684 assert_eq!(row["found"], order, "dimension {dimension}");1685 assert_eq!(row["fits"], true, "dimension {dimension}");1686 }1687}16881689#[test]1690fn the_carry_sign_law_alternates_at_both_bases() {1691 let table = parse(&carry_signs(10).unwrap()).unwrap();1692 let rows = table.as_array().unwrap();1693 let read = |key: &str| {1694 rows.iter()1695 .map(|row| row[key]["sign"].to_string())1696 .collect::<Vec<String>>()1697 .join(",")1698 };1699 assert_eq!(column(&table, "law"), "-1,1,-1,1,-1,1,-1,1,-1");1700 assert_eq!(read("three"), "-1,1,-1,1,-1,1,-1,1,-1");1701 assert_eq!(read("five"), "-1,1,-1,1,-1,1,-1,1,-1");1702 assert_eq!(column(&table, "order"), "1,2,2,3,3,4,4,5,5");1703 assert_eq!(1704 column(&table, "open"),1705 "false,false,false,false,false,true,false,false,false"1706 );1707 let wide = parse(&carry_signs(13).unwrap()).unwrap();1708 let past = wide.as_array().unwrap().last().unwrap();1709 assert_eq!(past["three"]["sign"], 1);1710 assert_eq!(past["five"], mrlycore::Json::Null);1711 let ladder = parse(&carry_ratios(3, 50).unwrap()).unwrap();1712 let last = ladder.as_array().unwrap().last().unwrap();1713 assert_eq!(last["dimension"], 50);1714 assert!((last["ratio"].as_f64().unwrap() - 13.0 / 12.0).abs() < 1e-9);1715 assert!((last["free"].as_f64().unwrap() - 13.0 / 12.0).abs() < 1e-12);1716}17171718#[test]1719fn the_automata_exports_answer() {1720 assert_eq!(eca_next(&[0, 0, 1, 0, 0], 110, false), vec![0, 1, 1, 0, 0]);1721 assert_eq!(eca_next(&[1, 0, 0, 0, 0], 170, true), vec![0, 0, 0, 0, 1]);1722 let run = eca_history(&[0, 0, 1, 0, 0], 110, 3, false);1723 assert_eq!((run.width, run.height), (5, 4));1724 assert_eq!(&run.types[5..10], &[0, 1, 1, 0, 0]);1725 let cone = eca_seed(110, 31);1726 assert_eq!((cone.width, cone.height), (63, 32));1727 assert_eq!(cone.types.iter().map(|&b| b as u32).sum::<u32>(), 326);1728 assert_eq!(1729 eca_seed(90, 8).types.iter().map(|&b| b as u32).sum::<u32>(),1730 291731 );1732 let card = parse(&eca_card(110)).unwrap();1733 assert_eq!(card["name"], "bang dim 3, code 110");1734 assert_eq!(1735 (card["popcount"].clone(), card["degree"].clone()),1736 (5.into(), 3.into())1737 );1738 assert_eq!(card["lambda"], 0.625);1739 assert_eq!(card["genus"], "comp");1740 assert_eq!(1741 (card["b3_rep"].clone(), card["wolfram_rep"].clone()),1742 (61.into(), 110.into())1743 );1744 assert_eq!(card["npn_rep"], 25);1745 assert_eq!(card["b3_orbit"].as_array().unwrap().len(), 24);1746 assert_eq!(card["wolfram_class"].to_string(), "[110,124,137,193]");1747 assert!(!card["surjective"].as_bool().unwrap());1748 assert!(!card["reversible"].as_bool().unwrap());1749 assert!(card["outer_totalistic"].is_null());1750 assert!(card["gasket"].is_null());1751 let gasket = parse(&eca_card(60)).unwrap();1752 assert_eq!(gasket["gasket"], "bang dim 2, code 13");1753 assert_eq!(gasket["b3_rep"], 60);1754 let conway = parse(&eca_card(90)).unwrap();1755 assert_eq!(conway["outer_totalistic"]["birth"].to_string(), "[1]");1756 assert_eq!(conway["outer_totalistic"]["survive"].to_string(), "[1]");1757 assert!(conway["surjective"].as_bool().unwrap());1758 assert_eq!(eca_soup(64, 0.0, 1).iter().sum::<u8>(), 0);1759 assert_eq!(eca_soup(64, 1.0, 1).iter().sum::<u8>(), 64);1760 assert_eq!(eca_soup(64, 0.5, 7), eca_soup(64, 0.5, 7));1761 let moore = life_mask(2, "7", 3, 1).unwrap();1762 assert_eq!((moore.width, moore.height), (3, 3));1763 assert_eq!(moore.types.iter().map(|&b| b as u32).sum::<u32>(), 8);1764 let deep = life_mask(2, "7", 3, 2).unwrap();1765 assert_eq!((deep.width, deep.height), (9, 9));1766 assert_eq!(deep.types.iter().map(|&b| b as u32).sum::<u32>(), 64);1767 let line = life_mask(1, "1", 3, 1).unwrap();1768 assert_eq!((line.width, line.height), (3, 1));1769 assert_eq!(line.types, vec![1, 0, 1]);1770 let wide = life_mask(1, "1", 5, 1).unwrap();1771 assert_eq!(wide.types, vec![1, 0, 0, 0, 1]);1772 assert_eq!(life_mask_index(&deep.types, 9, 9).unwrap(), 1);1773 assert_eq!(life_mask_index(&line.types, 3, 1).unwrap(), 1);1774 assert_eq!(life_mask_index(&wide.types, 5, 1).unwrap(), 2);1775 let diagonal = life_mask(2, "9", 3, 1).unwrap();1776 assert_eq!(life_mask_index(&diagonal.types, 3, 3).unwrap(), 2);1777 let row = [0, 1, 1, 0, 1, 0, 0];1778 let stepped = life_next_masked(&row, 7, 1, &[1], &[0, 1], &line.types, 3, 1, false).unwrap();1779 assert_eq!(stepped, eca_next(&row, 94, false));1780 let paced = parse(1781 &life_run_masked(1782 &blinker(),1783 5,1784 5,1785 &[3],1786 &[2, 3],1787 &moore.types,1788 3,1789 3,1790 false,1791 16,1792 )1793 .unwrap(),1794 )1795 .unwrap();1796 assert_eq!(1797 (paced["fate"].clone(), paced["loop"].clone()),1798 ("loop".into(), 2.into())1799 );1800 assert!(life_mask(3, "7", 3, 1).is_err());1801 assert!(life_mask(2, "7", 4, 1).is_err());1802 assert!(life_mask_index(&[1, 0, 1], 2, 2).is_err());1803 assert!(life_next_masked(&row, 7, 1, &[1], &[], &line.types, 4, 1, false).is_err());1804 assert!(life_next_masked(&row, 7, 1, &[1], &[], &wide.types, 5, 1, false).is_ok());1805}18061807#[test]1808fn the_formulas_exports_answer() {1809 let read = parse(&formulas_read(1000).unwrap()).unwrap();1810 let constants = &read["constants"];1811 assert!((constants["pi"].as_f64().unwrap() - std::f64::consts::PI).abs() < 1e-15);1812 assert!((constants["e"].as_f64().unwrap() - std::f64::consts::E).abs() < 1e-15);1813 assert!((constants["gamma"].as_f64().unwrap() - 0.577_215_664_901_532_9).abs() < 1e-15);1814 let cards = &read["cards"];1815 assert!((cards["wallis"]["value"].as_f64().unwrap() - 1.570_403_873_015_201).abs() < 1e-12);1816 assert!((cards["leibniz"]["value"].as_f64().unwrap() - 0.785_148_163_459_948_3).abs() < 1e-12);1817 assert!((cards["basel"]["value"].as_f64().unwrap() - 1.643_934_566_681_559_7).abs() < 1e-12);1818 assert!((cards["gamma"]["value"].as_f64().unwrap() - 0.577_715_581_568_208_2).abs() < 1e-12);1819 assert!((cards["e"]["value"].as_f64().unwrap() - 2.716_923_932_235_593_6).abs() < 1e-12);1820 for key in ["wallis", "leibniz", "basel", "gamma", "e"] {1821 let card = &cards[key];1822 let value = card["value"].as_f64().unwrap();1823 let limit = card["limit"].as_f64().unwrap();1824 assert!((card["error"].as_f64().unwrap() - (value - limit).abs()).abs() < 1e-15);1825 assert!(card["rel"].as_f64().unwrap() < 1e-3);1826 }1827 assert_eq!(cards["primes"]["value"], 168);1828 assert!((cards["primes"]["li"].as_f64().unwrap() - 177.609_657_990_152_2).abs() < 1e-9);1829 assert!((cards["primes"]["ratio"].as_f64().unwrap() - 144.764_827_301_083_95).abs() < 1e-9);1830 assert!((cards["primes"]["gauge"].as_f64().unwrap() - 0.945_894_507_658_558_8).abs() < 1e-12);1831 assert_eq!(cards["goldbach"]["even"], 2000);1832 assert_eq!(cards["goldbach"]["value"], 37);1833 assert_eq!(cards["goldbach"]["floor"], 1);1834 assert_eq!(cards["mertens"]["value"], 2);1835 assert!((cards["mertens"]["root"].as_f64().unwrap() - 31.622_776_601_683_793).abs() < 1e-12);18361837 let half = parse(&formulas_read(500).unwrap()).unwrap();1838 assert_eq!(half["cards"]["goldbach"]["even"], 1000);1839 assert_eq!(half["cards"]["goldbach"]["value"], 28);1840 assert_eq!(half["cards"]["primes"]["value"], 95);1841 assert_eq!(half["cards"]["mertens"]["value"], -6);18421843 let walk = formulas_walk("basel", 1000, 4).unwrap();1844 assert_eq!(walk.len(), 12);1845 assert_eq!((walk[0], walk[9]), (2.0, 1000.0));1846 assert!((walk[1] - 1.25).abs() < 1e-15);1847 assert!((walk[10] - 1.643_934_566_681_559_7).abs() < 1e-12);1848 let comet = formulas_walk("goldbach", 500, 2).unwrap();1849 assert_eq!((comet[0], comet[3], comet[4]), (4.0, 1000.0, 28.0));1850 assert!((comet[5] - 1.0 / 28.0).abs() < 1e-15);1851 let meter = formulas_walk("mertens", 100, 2).unwrap();1852 assert_eq!((meter[1], meter[4]), (0.0, 1.0));1853 assert!((meter[5] - 0.1).abs() < 1e-15);1854 assert!(formulas_read(1).is_err() && formulas_read(2001).is_err());1855 assert!(formulas_walk("basel", 100, 1).is_err() && formulas_walk("basil", 100, 4).is_err());1856}18571858#[test]1859fn the_wallis_exports_answer() {1860 assert_eq!(wallis_cap("odd", 3, 2).unwrap(), 4);1861 assert_eq!(wallis_cap("odd", 3, 3).unwrap(), 3);1862 assert_eq!(wallis_cap("flat", 3, 2).unwrap(), 6);1863 assert_eq!(wallis_cap("flat", 3, 3).unwrap(), 4);1864 assert_eq!(wallis_cap("flat", 5, 2).unwrap(), 4);18651866 let read = parse(&wallis_read("odd", 3, 4, 2).unwrap()).unwrap();1867 assert_eq!(read["word"].to_string(), "[3,5,7,9]");1868 assert_eq!(read["side"], "945");1869 assert_eq!(read["cells"], "737280");1870 assert_eq!(read["holes"], "9417");1871 assert!((read["ratio"].as_f64().unwrap() - 0.825_598_387_503_149_3).abs() < 1e-15);1872 assert!((read["limit"].as_f64().unwrap() - std::f64::consts::FRAC_PI_4).abs() < 1e-15);1873 assert!((read["exponent"].as_f64().unwrap() - 1.972_027_198_301_296_2).abs() < 1e-12);1874 assert_eq!(read["closed"], true);1875 assert_eq!(column(&read["levels"], "side"), r#""3","15","105","945""#);1876 assert_eq!(1877 column(&read["levels"], "cells"),1878 r#""8","192","9216","737280""#1879 );1880 assert_eq!(1881 column(&read["levels"], "ratio"),1882 "0.8888888888888888,0.8533333333333333,0.8359183673469387,0.8255983875031493"1883 );18841885 let solid = parse(&wallis_read("odd", 3, 3, 3).unwrap()).unwrap();1886 assert_eq!(solid["cells"], "1102608");1887 assert_eq!(solid["holes"], "3251");1888 assert!((solid["ratio"].as_f64().unwrap() - 0.952_474_246_841_593_9).abs() < 1e-15);1889 assert!((solid["limit"].as_f64().unwrap() - 0.948_815_485_719_679_7).abs() < 1e-15);1890 assert!((solid["gap"].as_f64().unwrap() - 0.003_658_761_121_914_21).abs() < 1e-15);18911892 let carpet = parse(&wallis_read("flat", 3, 5, 2).unwrap()).unwrap();1893 assert_eq!(carpet["side"], "243");1894 assert_eq!(carpet["cells"], "32768");1895 assert_eq!(carpet["limit"], 0.0);1896 assert_eq!(carpet["closed"], false);1897 assert!((carpet["exponent"].as_f64().unwrap() - 1.892_789_260_714_372).abs() < 1e-12);18981899 let walk = wallis_walk("odd", 3, 2, 200).unwrap();1900 assert_eq!(walk.len(), 200);1901 assert!((walk[0] - 8.0 / 9.0).abs() < 1e-15);1902 assert!((walk[199] - 0.786_375_633_530_097_9).abs() < 1e-12);1903 let fixed = wallis_walk("flat", 3, 2, 6).unwrap();1904 assert!((fixed[5] - (8.0f64 / 9.0).powi(6)).abs() < 1e-15);19051906 let grid = wallis_grid("odd", 3, 3).unwrap();1907 assert_eq!((grid.width, grid.height), (105, 105));1908 assert_eq!(grid.types.iter().filter(|&&b| b == 1).count(), 9216);19091910 let boxes = wallis_faces("odd", 3, 3).unwrap();1911 assert_eq!(boxes[0] as usize, 3251 * 216);1912 assert_eq!(boxes.len(), 2 + boxes[0] as usize);1913 assert_eq!(1914 &boxes[2..8],1915 &[-1.0 / 3.0, -1.0 / 3.0, -1.0 / 3.0, -1.0, 0.0, 0.0]1916 );19171918 assert!(wallis_read("odd", 3, 17, 2).is_err());1919 assert!(wallis_read("wallis", 3, 2, 2).is_err());1920 assert!(wallis_read("odd", 3, 2, 4).is_err());1921 assert!(wallis_read("flat", 4, 2, 2).is_err());1922 assert!(wallis_grid("odd", 3, 5).is_err());1923 assert!(wallis_faces("odd", 3, 4).is_err());1924}