zeta.rs
3.8 kB · rust · 112 lines
1use crate::Fault;2use mrlynum::zeta::{self, Line, JOIN};3use wasm_bindgen::prelude::*;45const REACH: f64 = 250.0;6const POINTS: usize = 20_000;7const ZEROS: usize = 100;8const STAIR: usize = 1_000;9const SAMPLES: usize = 2_000;1011fn checked(t: f64) -> Result<f64, Fault> {12 if !(0.0..=REACH).contains(&t) {13 return Err(Fault::new(format!("the line runs from t = 0 to {REACH}.")));14 }15 Ok(t)16}1718fn stones(x: f64, gammas: &[f64]) -> Result<(), Fault> {19 if !(2.0..=STAIR as f64).contains(&x) {20 return Err(Fault::new(format!("the staircase runs from 2 to {STAIR}.")));21 }22 if gammas.len() > ZEROS {23 return Err(Fault::new(format!(24 "the formula takes at most {ZEROS} zeros."25 )));26 }27 Ok(())28}2930/// Walks the critical line from one t to another in the given count of steps: for every point t, the real and the imaginary part of zeta at one half plus i t, and Z(t), as flat quadruples.31#[wasm_bindgen]32pub fn zeta_line(t0: f64, t1: f64, steps: usize) -> Result<Vec<f64>, Fault> {33 checked(t0)?;34 checked(t1)?;35 if steps == 0 || steps > POINTS {36 return Err(Fault::new(format!("the walk takes 1 to {POINTS} steps.")));37 }38 let line = Line::new();39 let mut out = Vec::with_capacity(4 * (steps + 1));40 for k in 0..=steps {41 let t = t0 + (t1 - t0) * k as f64 / steps as f64;42 let (value, z) = line.point(t);43 out.extend([t, value.re, value.im, z]);44 }45 Ok(out)46}4748/// Reads one point of the critical line: the real and the imaginary part of zeta, Z(t) and theta(t).49#[wasm_bindgen]50pub fn zeta_at(t: f64) -> Result<Vec<f64>, Fault> {51 let line = Line::new();52 let (value, z) = line.point(checked(t)?);53 Ok(vec![value.re, value.im, z, line.theta(t)])54}5556/// Returns the first zeros of zeta on the critical line, at most a hundred: sign changes of Z between Gram points, refined by bisection to a billionth.57#[wasm_bindgen]58pub fn zeta_zeros(count: usize) -> Result<Vec<f64>, Fault> {59 if count > ZEROS {60 return Err(Fault::new(format!("the list holds {ZEROS} zeros.")));61 }62 Ok(Line::new().zeros(count))63}6465/// Counts the zeros on the critical line below t.66#[wasm_bindgen]67pub fn zeta_count(t: f64) -> Result<u32, Fault> {68 Ok(Line::new().count(checked(t)?) as u32)69}7071/// Returns the join where Riemann-Siegel takes over from Euler-Maclaurin, and the largest gap between the two up to the given t on a grid of the given steps.72#[wasm_bindgen]73pub fn zeta_seam(t1: f64, steps: usize) -> Result<Vec<f64>, Fault> {74 if steps == 0 || steps > POINTS {75 return Err(Fault::new(format!("the seam takes 1 to {POINTS} steps.")));76 }77 Ok(vec![78 JOIN,79 Line::new().seam(JOIN, checked(t1)?.max(JOIN), steps),80 ])81}8283/// Returns the Chebyshev staircase psi at every whole number from one to x, at most a thousand.84#[wasm_bindgen]85pub fn psi_stair(x: usize) -> Result<Vec<f64>, Fault> {86 stones(x as f64, &[])?;87 Ok(zeta::psi_stair(x))88}8990/// Samples the explicit formula over the given zero ordinates at evenly spaced points from two to x, as flat pairs of the point and the value.91#[wasm_bindgen]92pub fn psi_formula(x: f64, gammas: &[f64], samples: usize) -> Result<Vec<f64>, Fault> {93 stones(x, gammas)?;94 if !(2..=SAMPLES).contains(&samples) {95 return Err(Fault::new(format!(96 "the formula takes 2 to {SAMPLES} samples."97 )));98 }99 let mut out = Vec::with_capacity(2 * samples);100 for k in 0..samples {101 let u = 2.0 + (x - 2.0) * k as f64 / (samples - 1) as f64;102 out.extend([u, zeta::psi_formula(u, gammas)]);103 }104 Ok(out)105}106107/// Returns psi(x) less the explicit formula over the given zeros at x.108#[wasm_bindgen]109pub fn psi_gap(x: usize, gammas: &[f64]) -> Result<f64, Fault> {110 stones(x as f64, gammas)?;111 Ok(zeta::psi_stair(x)[x - 1] - zeta::psi_formula(x as f64, gammas))112}