pub const DEFAULT_R_VALUES: [f64; 5] = [0.2, 0.5, 1.0, 2.0, 5.0];
pub const DEFAULT_X_VALUES: [f64; 5] = [0.2, 0.5, 1.0, 2.0, 5.0];
pub fn gamma_from_z(r: f64, x: f64) -> (f64, f64) {
let denom = (r + 1.0).powi(2) + x * x;
((r * r + x * x - 1.0) / denom, 2.0 * x / denom)
}
pub fn z_from_gamma(gr: f64, gi: f64) -> (f64, f64) {
let denom = (1.0 - gr).powi(2) + gi * gi;
if denom == 0.0 {
return (f64::NAN, f64::NAN);
}
((1.0 - gr * gr - gi * gi) / denom, 2.0 * gi / denom)
}
pub fn r_circle(r: f64, n: usize) -> (Vec<f64>, Vec<f64>) {
let cx = r / (r + 1.0);
let rad = 1.0 / (r + 1.0);
trace_circle(cx, 0.0, rad, n)
}
pub fn x_arc(x: f64, n: usize) -> (Vec<f64>, Vec<f64>) {
let cx = 1.0;
let cy = 1.0 / x;
let rad = 1.0 / x.abs();
trace_circle(cx, cy, rad, n)
}
fn trace_circle(cx: f64, cy: f64, rad: f64, n: usize) -> (Vec<f64>, Vec<f64>) {
let n = n.max(3);
let step = std::f64::consts::TAU / n as f64;
let mut xs = Vec::with_capacity(n + 1);
let mut ys = Vec::with_capacity(n + 1);
for i in 0..=n {
let phi = i as f64 * step;
xs.push(cx + rad * phi.cos());
ys.push(cy + rad * phi.sin());
}
(xs, ys)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn z_eq_1_maps_to_origin() {
let (gr, gi) = gamma_from_z(1.0, 0.0);
assert!(gr.abs() < 1e-9);
assert!(gi.abs() < 1e-9);
}
#[test]
fn short_maps_to_minus_one() {
let (gr, gi) = gamma_from_z(0.0, 0.0);
assert!((gr + 1.0).abs() < 1e-9);
assert!(gi.abs() < 1e-9);
}
}