#[cfg(feature = "std")]
mod imp {
pub(crate) fn sin(x: f64) -> f64 {
x.sin()
}
pub(crate) fn cos(x: f64) -> f64 {
x.cos()
}
pub(crate) fn asin(x: f64) -> f64 {
x.asin()
}
pub(crate) fn atan2(y: f64, x: f64) -> f64 {
y.atan2(x)
}
pub(crate) fn sqrt(x: f64) -> f64 {
x.sqrt()
}
pub(crate) fn abs(x: f64) -> f64 {
x.abs()
}
pub(crate) fn tan(x: f64) -> f64 {
x.tan()
}
pub(crate) fn atan(x: f64) -> f64 {
x.atan()
}
pub(crate) fn acos(x: f64) -> f64 {
x.acos()
}
pub(crate) fn ln(x: f64) -> f64 {
x.ln()
}
pub(crate) fn exp(x: f64) -> f64 {
x.exp()
}
pub(crate) fn hypot(x: f64, y: f64) -> f64 {
x.hypot(y)
}
pub(crate) fn round(x: f64) -> f64 {
x.round()
}
pub(crate) fn ceil(x: f64) -> f64 {
x.ceil()
}
pub(crate) fn trunc(x: f64) -> f64 {
x.trunc()
}
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
mod imp {
pub(crate) fn sin(x: f64) -> f64 {
libm::sin(x)
}
pub(crate) fn cos(x: f64) -> f64 {
libm::cos(x)
}
pub(crate) fn asin(x: f64) -> f64 {
libm::asin(x)
}
pub(crate) fn atan2(y: f64, x: f64) -> f64 {
libm::atan2(y, x)
}
pub(crate) fn sqrt(x: f64) -> f64 {
libm::sqrt(x)
}
pub(crate) fn abs(x: f64) -> f64 {
libm::fabs(x)
}
pub(crate) fn tan(x: f64) -> f64 {
libm::tan(x)
}
pub(crate) fn atan(x: f64) -> f64 {
libm::atan(x)
}
pub(crate) fn acos(x: f64) -> f64 {
libm::acos(x)
}
pub(crate) fn ln(x: f64) -> f64 {
libm::log(x)
}
pub(crate) fn exp(x: f64) -> f64 {
libm::exp(x)
}
pub(crate) fn hypot(x: f64, y: f64) -> f64 {
libm::hypot(x, y)
}
pub(crate) fn round(x: f64) -> f64 {
libm::round(x)
}
pub(crate) fn ceil(x: f64) -> f64 {
libm::ceil(x)
}
pub(crate) fn trunc(x: f64) -> f64 {
libm::trunc(x)
}
}
#[cfg(not(any(feature = "std", feature = "libm")))]
compile_error!(
"bearingpro needs floating point math: enable the default `std` feature, \
or build with `--no-default-features --features libm` for `no_std` targets"
);
pub(crate) use imp::{
abs, acos, asin, atan, atan2, ceil, cos, exp, hypot, ln, round, sin, sqrt, tan, trunc,
};
const DEGREES_PER_RADIAN: f64 = 180.0 / core::f64::consts::PI;
pub(crate) fn to_radians(degrees: f64) -> f64 {
degrees / DEGREES_PER_RADIAN
}
pub(crate) fn to_degrees(radians: f64) -> f64 {
radians * DEGREES_PER_RADIAN
}
#[allow(clippy::float_cmp)]
pub(crate) fn is_integral(value: f64) -> bool {
value == trunc(value)
}
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn round_to_i32(value: f64) -> i32 {
let rounded = round(value);
if rounded > f64::from(i32::MAX) || rounded < f64::from(i32::MIN) {
return 0;
}
rounded as i32
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub(crate) fn to_usize(value: f64) -> usize {
if !(0.0..=1e9).contains(&value) {
return 0;
}
value as usize
}
#[allow(clippy::cast_precision_loss)]
pub(crate) fn count_to_f64(count: usize) -> f64 {
count as f64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn radian_conversion_round_trips() {
for degrees in [0.0, 1.0, 45.0, 90.0, 180.0, 359.9] {
let back = to_degrees(to_radians(degrees));
assert!((back - degrees).abs() < 1e-12);
}
}
#[test]
fn trig_matches_known_values() {
assert!(abs(sin(to_radians(90.0)) - 1.0) < 1e-12);
assert!(abs(cos(to_radians(180.0)) + 1.0) < 1e-12);
assert!(abs(to_degrees(atan2(1.0, 0.0)) - 90.0) < 1e-12);
assert!(abs(hypot(3.0, 4.0) - 5.0) < 1e-12);
assert!(abs(sqrt(9.0) - 3.0) < 1e-12);
assert!(abs(to_degrees(asin(0.5)) - 30.0) < 1e-12);
}
}