research-franel.rs
3.4 kB · rust · 119 lines
1use figures::{ink, save, Board, Grid};2use mrlyrs::core::error::Result;3use mrlyrs::core::Color;4use mrlyrs::num::design::elements;5use mrlyrs::num::factor::{gcd, mobius_sieve};6use std::f64::consts::TAU;78const BASE: u64 = 3;9const DIGITS: [u64; 2] = [0, 1];10const ORDER: usize = 81;11const LIT: [usize; 16] = [1, 2, 6, 8, 14, 15, 18, 20, 28, 30, 31, 36, 37, 39, 40, 81];1213// THE FORM1415fn dilated(holds: &[bool], mu: &[i8]) -> Vec<i64> {16 let mut out = vec![0i64; ORDER + 1];17 for d in 1..=ORDER {18 for c in 1..=ORDER / d {19 if holds[d * c] {20 out[d] += mu[c] as i64;21 }22 }23 }24 out25}2627fn kernel(d: usize, e: usize) -> f64 {28 let g = gcd(d as u128, e as u128) as f64;29 g * g / (d as f64 * e as f64)30}3132fn nodes(dens: &[usize]) -> Vec<(usize, usize)> {33 let mut out = Vec::new();34 for &b in dens {35 for a in 1..=b {36 if gcd(a as u128, b as u128) == 1 {37 out.push((a, b));38 }39 }40 }41 out42}4344// THE MARKS4546fn wash(k: f64) -> Color {47 ink::mix(ink::ground(), ink::dim(), 0.015 + 0.7 * k.sqrt())48}4950fn lit(v: f64, low: f64, high: f64) -> Color {51 let s = (v.abs().ln() - low.ln()) / (high.ln() - low.ln());52 let hue = if v > 0.0 { ink::blue() } else { ink::orange() };53 ink::mix(ink::ground(), hue, 0.55 + 0.45 * s)54}5556fn main() -> Result<()> {57 let design: Vec<usize> = elements(BASE, &DIGITS, 5)58 .into_iter()59 .filter(|&n| n as usize <= ORDER)60 .map(|n| n as usize)61 .collect();62 assert_eq!(design.len(), 16);63 let mut holds = vec![false; ORDER + 1];64 for &b in &design {65 holds[b] = true;66 }67 let mu = mobius_sieve(ORDER);68 let x = dilated(&holds, &mu);69 let support: Vec<usize> = (1..=ORDER).filter(|&d| x[d] != 0).collect();70 assert_eq!(support, LIT);71 assert_eq!(x[1], -2);72 assert_eq!(x.iter().map(|v| v * v).sum::<i64>(), 19);7374 let mut terms = Vec::new();75 for &d in &support {76 for &e in &support {77 terms.push((d, e, kernel(d, e) * (x[d] * x[e]) as f64));78 }79 }80 assert_eq!(terms.iter().filter(|t| t.2 != 0.0).count(), 256);81 let form: f64 = terms.iter().map(|t| t.2).sum();82 assert!((form - 12.574_087).abs() < 1e-5);8384 let farey = nodes(&design);85 assert_eq!(farey.len(), 241);86 for k in 1..=12usize {87 let (mut re, mut im) = (0.0, 0.0);88 for &(a, b) in &farey {89 let angle = TAU * (k * a) as f64 / b as f64;90 re += angle.cos();91 im += angle.sin();92 }93 let divisor: i64 = (1..=k)94 .filter(|d| k % d == 0 && *d <= ORDER)95 .map(|d| d as i64 * x[d])96 .sum();97 assert!((re - divisor as f64).abs() < 1e-9);98 assert!(im.abs() < 1e-9);99 }100101 let low = terms.iter().fold(f64::MAX, |a, t| a.min(t.2.abs()));102 let high = terms.iter().fold(0.0f64, |a, t| a.max(t.2.abs()));103 assert!(low > 3e-4 && high == 4.0);104105 let mut board = Board::square();106 let frame = board.frame(0.08);107 let fine = Grid::new(frame, ORDER, ORDER, 0.34);108 let bold = Grid::new(frame, ORDER, ORDER, 0.08);109 for d in 1..=ORDER {110 for e in 1..=ORDER {111 fine.fill(&mut board, e - 1, d - 1, wash(kernel(d, e)));112 }113 }114 for &(d, e, v) in &terms {115 bold.fill(&mut board, e - 1, d - 1, lit(v, low, high));116 }117 save("research-franel", &board)?;118 Ok(())119}