pub trait Portable {
fn portable_sin(self) -> f64;
fn portable_cos(self) -> f64;
fn portable_sin_cos(self) -> (f64, f64);
fn portable_tan(self) -> f64;
fn portable_asin(self) -> f64;
fn portable_acos(self) -> f64;
fn portable_atan(self) -> f64;
fn portable_atan2(self, x: f64) -> f64;
fn portable_sinh(self) -> f64;
fn portable_cosh(self) -> f64;
fn portable_tanh(self) -> f64;
fn portable_asinh(self) -> f64;
fn portable_acosh(self) -> f64;
fn portable_atanh(self) -> f64;
fn portable_exp(self) -> f64;
fn portable_exp_m1(self) -> f64;
fn portable_ln(self) -> f64;
fn portable_hypot(self, other: f64) -> f64;
}
impl Portable for f64 {
fn portable_sin(self) -> f64 {
libm::sin(self)
}
fn portable_cos(self) -> f64 {
libm::cos(self)
}
fn portable_sin_cos(self) -> (f64, f64) {
libm::sincos(self)
}
fn portable_tan(self) -> f64 {
libm::tan(self)
}
fn portable_asin(self) -> f64 {
libm::asin(self)
}
fn portable_acos(self) -> f64 {
libm::acos(self)
}
fn portable_atan(self) -> f64 {
libm::atan(self)
}
fn portable_atan2(self, x: f64) -> f64 {
libm::atan2(self, x)
}
fn portable_sinh(self) -> f64 {
libm::sinh(self)
}
fn portable_cosh(self) -> f64 {
libm::cosh(self)
}
fn portable_tanh(self) -> f64 {
libm::tanh(self)
}
fn portable_asinh(self) -> f64 {
libm::asinh(self)
}
fn portable_acosh(self) -> f64 {
libm::acosh(self)
}
fn portable_atanh(self) -> f64 {
libm::atanh(self)
}
fn portable_exp(self) -> f64 {
libm::exp(self)
}
fn portable_exp_m1(self) -> f64 {
libm::expm1(self)
}
fn portable_ln(self) -> f64 {
libm::log(self)
}
fn portable_hypot(self, other: f64) -> f64 {
libm::hypot(self, other)
}
}
#[cfg(test)]
mod tests {
use std::f64::consts::{FRAC_PI_4, PI};
use super::Portable;
const EXACT: f64 = 1.0e-15;
fn close(left: f64, right: f64) {
assert!((left - right).abs() <= EXACT, "{left} against {right}");
}
#[test]
fn functions_match_their_closed_values() {
close(0.0_f64.portable_sin(), 0.0);
close((PI / 2.0).portable_sin(), 1.0);
close(PI.portable_cos(), -1.0);
close(FRAC_PI_4.portable_tan(), 1.0);
close(1.0_f64.portable_asin(), PI / 2.0);
close((-1.0_f64).portable_acos(), PI);
close(1.0_f64.portable_atan(), FRAC_PI_4);
close(1.0_f64.portable_atan2(-1.0), 3.0 * FRAC_PI_4);
close(0.0_f64.portable_sinh(), 0.0);
close(0.0_f64.portable_cosh(), 1.0);
close(0.0_f64.portable_tanh(), 0.0);
close(0.0_f64.portable_asinh(), 0.0);
close(1.0_f64.portable_acosh(), 0.0);
close(0.0_f64.portable_atanh(), 0.0);
close(0.0_f64.portable_exp(), 1.0);
close(0.0_f64.portable_exp_m1(), 0.0);
close(1.0_f64.portable_ln(), 0.0);
close(3.0_f64.portable_hypot(4.0), 5.0);
}
#[test]
fn sin_cos_returns_the_sine_first() {
let (sine, cosine) = FRAC_PI_4.portable_sin_cos();
assert_eq!(sine.to_bits(), FRAC_PI_4.portable_sin().to_bits());
assert_eq!(cosine.to_bits(), FRAC_PI_4.portable_cos().to_bits());
}
}