use crate::autodiff::{Dual1, Dual2};
use core::ops::{Add, Div, Mul, Neg, Sub};
pub trait Scalar:
Copy
+ Clone
+ core::fmt::Debug
+ core::fmt::Display
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ Neg<Output = Self>
+ PartialOrd
+ PartialEq
+ 'static
+ Send
+ Sync
{
fn zero() -> Self;
fn one() -> Self;
fn two() -> Self {
Self::one() + Self::one()
}
fn from_f64(v: f64) -> Self;
fn to_f64(self) -> f64;
fn sin(self) -> Self;
fn cos(self) -> Self;
fn tan(self) -> Self;
fn asin(self) -> Self;
fn acos(self) -> Self;
fn atan(self) -> Self;
fn atan2(self, other: Self) -> Self;
fn sqrt(self) -> Self;
fn cbrt(self) -> Self;
fn exp(self) -> Self;
fn ln(self) -> Self;
fn abs(self) -> Self;
fn powi(self, n: i32) -> Self;
fn powf(self, e: f64) -> Self;
fn sinh(self) -> Self;
fn cosh(self) -> Self;
fn tanh(self) -> Self;
fn hypot(self, other: Self) -> Self;
fn pi() -> Self {
Self::from_f64(core::f64::consts::PI)
}
fn two_pi() -> Self {
Self::from_f64(core::f64::consts::TAU)
}
fn half_pi() -> Self {
Self::from_f64(core::f64::consts::FRAC_PI_2)
}
fn quarter_pi() -> Self {
Self::from_f64(core::f64::consts::FRAC_PI_4)
}
fn rad_to_deg() -> Self {
Self::from_f64(180.0 / core::f64::consts::PI)
}
fn deg_to_rad() -> Self {
Self::from_f64(core::f64::consts::PI / 180.0)
}
}
impl Scalar for f64 {
#[inline]
fn zero() -> Self {
0.0
}
#[inline]
fn one() -> Self {
1.0
}
#[inline]
fn from_f64(v: f64) -> Self {
v
}
#[inline]
fn to_f64(self) -> f64 {
self
}
#[inline]
fn sin(self) -> Self {
f64::sin(self)
}
#[inline]
fn cos(self) -> Self {
f64::cos(self)
}
#[inline]
fn tan(self) -> Self {
f64::tan(self)
}
#[inline]
fn asin(self) -> Self {
f64::asin(self)
}
#[inline]
fn acos(self) -> Self {
f64::acos(self)
}
#[inline]
fn atan(self) -> Self {
f64::atan(self)
}
#[inline]
fn atan2(self, other: Self) -> Self {
f64::atan2(self, other)
}
#[inline]
fn sqrt(self) -> Self {
f64::sqrt(self)
}
#[inline]
fn cbrt(self) -> Self {
f64::cbrt(self)
}
#[inline]
fn exp(self) -> Self {
f64::exp(self)
}
#[inline]
fn ln(self) -> Self {
f64::ln(self)
}
#[inline]
fn abs(self) -> Self {
f64::abs(self)
}
#[inline]
fn powi(self, n: i32) -> Self {
f64::powi(self, n)
}
#[inline]
fn powf(self, e: f64) -> Self {
f64::powf(self, e)
}
#[inline]
fn sinh(self) -> Self {
f64::sinh(self)
}
#[inline]
fn cosh(self) -> Self {
f64::cosh(self)
}
#[inline]
fn tanh(self) -> Self {
f64::tanh(self)
}
#[inline]
fn hypot(self, other: Self) -> Self {
f64::hypot(self, other)
}
}
impl<const N: usize> Scalar for Dual1<N> {
#[inline]
fn zero() -> Self {
Self::constant(0.0)
}
#[inline]
fn one() -> Self {
Self::constant(1.0)
}
#[inline]
fn from_f64(v: f64) -> Self {
Self::constant(v)
}
#[inline]
fn to_f64(self) -> f64 {
self.v
}
#[inline]
fn sin(self) -> Self {
Dual1::sin(self)
}
#[inline]
fn cos(self) -> Self {
Dual1::cos(self)
}
#[inline]
fn tan(self) -> Self {
Dual1::tan(self)
}
#[inline]
fn asin(self) -> Self {
Dual1::asin(self)
}
#[inline]
fn acos(self) -> Self {
Dual1::acos(self)
}
#[inline]
fn atan(self) -> Self {
Dual1::atan(self)
}
#[inline]
fn atan2(self, other: Self) -> Self {
Dual1::atan2(self, other)
}
#[inline]
fn sqrt(self) -> Self {
Dual1::sqrt(self)
}
#[inline]
fn cbrt(self) -> Self {
Dual1::cbrt(self)
}
#[inline]
fn exp(self) -> Self {
Dual1::exp(self)
}
#[inline]
fn ln(self) -> Self {
Dual1::ln(self)
}
#[inline]
fn abs(self) -> Self {
Dual1::abs(self)
}
#[inline]
fn powi(self, n: i32) -> Self {
Dual1::powi(self, n)
}
#[inline]
fn powf(self, e: f64) -> Self {
Dual1::powf(self, e)
}
#[inline]
fn sinh(self) -> Self {
Dual1::sinh(self)
}
#[inline]
fn cosh(self) -> Self {
Dual1::cosh(self)
}
#[inline]
fn tanh(self) -> Self {
Dual1::tanh(self)
}
#[inline]
fn hypot(self, other: Self) -> Self {
Dual1::hypot(self, other)
}
}
impl<const N: usize> Scalar for Dual2<N> {
#[inline]
fn zero() -> Self {
Self::constant(0.0)
}
#[inline]
fn one() -> Self {
Self::constant(1.0)
}
#[inline]
fn from_f64(v: f64) -> Self {
Self::constant(v)
}
#[inline]
fn to_f64(self) -> f64 {
self.v
}
#[inline]
fn sin(self) -> Self {
Dual2::sin(self)
}
#[inline]
fn cos(self) -> Self {
Dual2::cos(self)
}
#[inline]
fn tan(self) -> Self {
Dual2::tan(self)
}
#[inline]
fn asin(self) -> Self {
Dual2::asin(self)
}
#[inline]
fn acos(self) -> Self {
Dual2::acos(self)
}
#[inline]
fn atan(self) -> Self {
Dual2::atan(self)
}
#[inline]
fn atan2(self, other: Self) -> Self {
Dual2::atan2(self, other)
}
#[inline]
fn sqrt(self) -> Self {
Dual2::sqrt(self)
}
#[inline]
fn cbrt(self) -> Self {
Dual2::cbrt(self)
}
#[inline]
fn exp(self) -> Self {
Dual2::exp(self)
}
#[inline]
fn ln(self) -> Self {
Dual2::ln(self)
}
#[inline]
fn abs(self) -> Self {
Dual2::abs(self)
}
#[inline]
fn powi(self, n: i32) -> Self {
Dual2::powi(self, n)
}
#[inline]
fn powf(self, e: f64) -> Self {
Dual2::powf(self, e)
}
#[inline]
fn sinh(self) -> Self {
Dual2::sinh(self)
}
#[inline]
fn cosh(self) -> Self {
Dual2::cosh(self)
}
#[inline]
fn tanh(self) -> Self {
Dual2::tanh(self)
}
#[inline]
fn hypot(self, other: Self) -> Self {
Dual2::hypot(self, other)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn check_scalar_mercator<S: Scalar>(lam_val: f64, phi_val: f64) -> (f64, f64) {
let lam = S::from_f64(lam_val);
let phi = S::from_f64(phi_val);
let x = lam;
let pi4 = S::quarter_pi();
let half = S::from_f64(0.5);
let y = (pi4 + phi * half).tan().ln();
(x.to_f64(), y.to_f64())
}
#[test]
fn scalar_f64_mercator_equator() {
let (x, y) = check_scalar_mercator::<f64>(0.5, 0.0);
assert!((x - 0.5).abs() < 1e-12);
assert!(y.abs() < 1e-12);
}
#[test]
fn scalar_dual1_mercator_matches_f64() {
let phi = 0.3_f64;
let lam = 1.2_f64;
let (xf, yf) = check_scalar_mercator::<f64>(lam, phi);
let (xd, yd) = check_scalar_mercator::<Dual1<2>>(lam, phi);
assert!((xf - xd).abs() < 1e-12);
assert!((yf - yd).abs() < 1e-12);
}
#[test]
fn scalar_dual2_mercator_matches_f64() {
let phi = 0.3_f64;
let lam = 1.2_f64;
let (xf, yf) = check_scalar_mercator::<f64>(lam, phi);
let (xd, yd) = check_scalar_mercator::<Dual2<2>>(lam, phi);
assert!((xf - xd).abs() < 1e-12);
assert!((yf - yd).abs() < 1e-12);
}
#[test]
fn scalar_constants() {
use core::f64::consts::PI;
assert!((f64::pi() - PI).abs() < 1e-15);
assert!((f64::two_pi() - 2.0 * PI).abs() < 1e-15);
assert!((f64::half_pi() - PI / 2.0).abs() < 1e-15);
assert!((f64::quarter_pi() - PI / 4.0).abs() < 1e-15);
assert!((f64::rad_to_deg() - 180.0 / PI).abs() < 1e-12);
assert!((f64::deg_to_rad() - PI / 180.0).abs() < 1e-12);
}
#[test]
fn scalar_zero_one_two() {
assert!((f64::zero() - 0.0).abs() < 1e-15);
assert!((f64::one() - 1.0).abs() < 1e-15);
assert!((f64::two() - 2.0).abs() < 1e-15);
}
}