rays.rs

3.4 kB · rust · 106 lines

1use crate::sums::{mean, odds};2use mrlynum::series::chi4;34fn triangle(value: f64) -> f64 {5    let rest = value.rem_euclid(2.0);6    1.0 - 2.0 * rest.min(2.0 - rest)7}89fn average(point: f64, cap: usize, twisted: bool) -> f64 {10    let terms: Vec<f64> = odds(cap)11        .map(|n| {12            let sign = if twisted { f64::from(chi4(n)) } else { 1.0 };13            sign * triangle(n as f64 * point)14        })15        .collect();16    mean(&terms)17}1819pub fn twisted() {20    println!("(1/M) sum over odd n <= N of chi4(n) T(n x), the untwisted average beside it");21    let probes = [22        0.0,23        1.0 / 3.0,24        2.0 / 3.0,25        0.2,26        1.0 / 7.0,27        0.5,28        0.25,29        1.0 / 9.0,30        2f64.sqrt() - 1.0,31    ];32    for point in probes {33        println!(34            "  x = {point:.6}: N=55 {:+.5}  N=2001 {:+.6}  N=40001 {:+.7}  untwisted N=40001 {:+.6}",35            average(point, 55, true),36            average(point, 2001, true),37            average(point, 40001, true),38            average(point, 40001, false)39        );40    }41}4243pub const RESOLUTION: usize = 200003;4445fn square(value: f64) -> f64 {46    if (value.floor() as i64) % 2 == 0 {47        1.048    } else {49        -1.050    }51}5253fn field(cap: usize, first: &[f64], second: &[f64]) -> f64 {54    let mut running = vec![0.0f64; first.len()];55    let mut layers = 0;56    for n in odds(cap) {57        let twist = -f64::from(chi4(n));58        let scale = n as f64;59        for (slot, cell) in running.iter_mut().enumerate() {60            let left = square(scale * first[slot]);61            let right = square(scale * second[slot]);62            *cell += 0.5 + twist / 8.0 + (left + right) / 4.0 - twist * left * right / 8.0;63        }64        layers += 1;65    }66    mean(&running) / layers as f6467}6869pub fn crosshairs() {70    let points: Vec<f64> = (0..RESOLUTION)71        .map(|slot| (slot as f64 + 0.5) / RESOLUTION as f64)72        .collect();73    let generic: Vec<f64> = points74        .iter()75        .map(|point| (point * 2f64.sqrt()).rem_euclid(1.0))76        .collect();77    let shifted: Vec<f64> = points78        .iter()79        .map(|point| (point + 1.0 / 3.0).rem_euclid(1.0))80        .collect();81    println!("coarse cut model, {RESOLUTION} samples along a line, excess over the background");82    for cap in [55usize, 555, 5555] {83        let background = field(cap, &points, &generic);84        let diagonal = field(cap, &points, &points) - background;85        let offset = field(cap, &points, &shifted) - background;86        println!("  N = {cap:4}: background {background:.6}  A=C line {diagonal:+.6}  A-C=1/3 line {offset:+.6}");87    }88    let control: Vec<f64> = points89        .iter()90        .map(|point| (point * 3f64.sqrt()).rem_euclid(1.0))91        .collect();92    println!("coarse crosshair at A = a/q, predicted (-1)^a/(4q) for odd q and 0 for even q");93    for cap in [55usize, 5555] {94        let background = field(cap, &control, &points);95        for (a, q) in [(1, 3), (2, 3), (1, 5), (2, 5), (1, 7), (1, 2), (1, 4)] {96            let line = vec![a as f64 / q as f64 + 1e-12; RESOLUTION];97            let excess = field(cap, &line, &points) - background;98            let predicted = if q % 2 == 1 {99                (if a % 2 == 0 { 1.0 } else { -1.0 }) / (4 * q) as f64100            } else {101                0.0102            };103            println!("  N = {cap:4}  A = {a}/{q}: excess {excess:+.6}  predicted {predicted:+.6}");104        }105    }106}