use crate::scalar::Scalar;
pub fn pj_tsfn(phi: f64, sinphi: f64, e: f64) -> f64 {
let cosphi = phi.cos();
(e * (e * sinphi).atanh()).exp()
* if sinphi > 0.0 {
cosphi / (1.0 + sinphi)
} else {
(1.0 - sinphi) / cosphi
}
}
pub fn pj_tsfn_g<S: Scalar>(phi: S, sinphi: S, e: f64) -> S {
let e_s = S::from_f64(e);
let cosphi = phi.cos();
let e_sinphi = e_s * sinphi;
let one = S::one();
let half = S::from_f64(0.5);
let atanh_e_sinphi = half * ((one + e_sinphi) / (one - e_sinphi)).ln();
let exp_part = (e_s * atanh_e_sinphi).exp();
if sinphi.to_f64() > 0.0 {
exp_part * (cosphi / (one + sinphi))
} else {
exp_part * ((one - sinphi) / cosphi)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tsfn_sphere_matches_closed_form() {
let phi = 0.5_f64;
let t = pj_tsfn(phi, phi.sin(), 0.0);
let expected = phi.cos() / (1.0 + phi.sin());
assert!((t - expected).abs() < 1e-12);
}
#[test]
fn tsfn_wgs84_is_positive() {
let phi = 0.6_f64;
let e = 0.0818191908426;
let t = pj_tsfn(phi, phi.sin(), e);
assert!(t > 0.0);
}
}