use super::traits::MathBackend;
#[cfg(feature = "floating-point")]
pub struct FloatingPointBackend;
#[cfg(feature = "floating-point")]
impl MathBackend<f32> for FloatingPointBackend {
#[inline]
fn sqrt(&self, x: f32) -> f32 {
micromath::F32Ext::sqrt(x)
}
#[inline]
fn abs(&self, x: f32) -> f32 {
if x < 0.0 {
-x
} else {
x
}
}
#[inline]
fn min(&self, a: f32, b: f32) -> f32 {
if a < b {
a
} else {
b
}
}
#[inline]
fn max(&self, a: f32, b: f32) -> f32 {
if a > b {
a
} else {
b
}
}
#[inline]
fn floor(&self, x: f32) -> f32 {
micromath::F32Ext::floor(x)
}
#[inline]
fn ceil(&self, x: f32) -> f32 {
micromath::F32Ext::ceil(x)
}
#[inline]
fn pow(&self, x: f32, y: f32) -> f32 {
micromath::F32Ext::powf(x, y)
}
#[inline]
fn ln(&self, x: f32) -> f32 {
micromath::F32Ext::ln(x)
}
#[inline]
fn log10(&self, x: f32) -> f32 {
micromath::F32Ext::log10(x)
}
#[inline]
fn sin(&self, x: f32) -> f32 {
micromath::F32Ext::sin(x)
}
#[inline]
fn cos(&self, x: f32) -> f32 {
micromath::F32Ext::cos(x)
}
#[inline]
fn tan(&self, x: f32) -> f32 {
micromath::F32Ext::tan(x)
}
#[inline]
fn to_radians(&self, degrees: f32) -> f32 {
degrees * (core::f32::consts::PI / 180.0)
}
#[inline]
fn to_degrees(&self, radians: f32) -> f32 {
radians * (180.0 / core::f32::consts::PI)
}
#[inline]
fn atan2(&self, y: f32, x: f32) -> f32 {
micromath::F32Ext::atan2(y, x)
}
}
#[cfg(feature = "libm-math")]
pub struct LibmBackend;
#[cfg(feature = "libm-math")]
impl MathBackend<f32> for LibmBackend {
#[inline]
fn sqrt(&self, x: f32) -> f32 {
libm::sqrtf(x)
}
#[inline]
fn abs(&self, x: f32) -> f32 {
libm::fabsf(x)
}
#[inline]
fn min(&self, a: f32, b: f32) -> f32 {
libm::fminf(a, b)
}
#[inline]
fn max(&self, a: f32, b: f32) -> f32 {
libm::fmaxf(a, b)
}
#[inline]
fn floor(&self, x: f32) -> f32 {
libm::floorf(x)
}
#[inline]
fn ceil(&self, x: f32) -> f32 {
libm::ceilf(x)
}
#[inline]
fn pow(&self, x: f32, y: f32) -> f32 {
libm::powf(x, y)
}
#[inline]
fn ln(&self, x: f32) -> f32 {
libm::logf(x)
}
#[inline]
fn log10(&self, x: f32) -> f32 {
libm::log10f(x)
}
#[inline]
fn sin(&self, x: f32) -> f32 {
libm::sinf(x)
}
#[inline]
fn cos(&self, x: f32) -> f32 {
libm::cosf(x)
}
#[inline]
fn tan(&self, x: f32) -> f32 {
libm::tanf(x)
}
#[inline]
fn to_radians(&self, degrees: f32) -> f32 {
degrees * (core::f32::consts::PI / 180.0)
}
#[inline]
fn to_degrees(&self, radians: f32) -> f32 {
radians * (180.0 / core::f32::consts::PI)
}
#[inline]
fn atan2(&self, y: f32, x: f32) -> f32 {
libm::atan2f(y, x)
}
}
#[cfg(any(feature = "fixed-point", feature = "cordic-math"))]
pub struct FixedPointBackend;
#[cfg(any(feature = "fixed-point", feature = "cordic-math"))]
impl MathBackend<fixed::types::I16F16> for FixedPointBackend {
#[inline]
fn sqrt(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
if x <= fixed::types::I16F16::ZERO {
return fixed::types::I16F16::ZERO;
}
let mut guess = x / 2;
for _ in 0..8 {
let new_guess = (guess + x / guess) / 2;
if (new_guess - guess).abs() < fixed::types::I16F16::from_num(0.001) {
break;
}
guess = new_guess;
}
guess
}
#[inline]
fn abs(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
x.abs()
}
#[inline]
fn min(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
if a < b {
a
} else {
b
}
}
#[inline]
fn max(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
if a > b {
a
} else {
b
}
}
#[inline]
fn floor(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
x.floor()
}
#[inline]
fn ceil(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
x.ceil()
}
#[inline]
fn pow(&self, x: fixed::types::I16F16, y: fixed::types::I16F16) -> fixed::types::I16F16 {
if y == fixed::types::I16F16::ZERO {
return fixed::types::I16F16::ONE;
}
let y_int: i32 = y.to_num();
if y_int < 0 {
return fixed::types::I16F16::ONE / self.pow(x, -y);
}
let mut result = fixed::types::I16F16::ONE;
let mut base = x;
let mut exp = y_int as u32;
while exp > 0 {
if exp & 1 == 1 {
result *= base;
}
base = base * base;
exp >>= 1;
}
result
}
#[inline]
fn ln(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
if x <= fixed::types::I16F16::ZERO {
return fixed::types::I16F16::from_num(-100.0); }
let one = fixed::types::I16F16::ONE;
let t = x - one;
let t2 = t * t;
let t3 = t2 * t;
let t4 = t3 * t;
t - t2 / 2 + t3 / 3 - t4 / 4
}
#[inline]
fn log10(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let ln_x = self.ln(x);
let ln_10 = fixed::types::I16F16::from_num(core::f32::consts::LN_10); ln_x / ln_10
}
#[inline]
fn sin(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
let two_pi = pi * 2;
let mut angle = x;
while angle > pi {
angle -= two_pi;
}
while angle < -pi {
angle += two_pi;
}
let x2 = angle * angle;
let x3 = x2 * angle;
let x5 = x3 * x2;
let x7 = x5 * x2;
angle - x3 / 6 + x5 / 120 - x7 / 5040
}
#[inline]
fn cos(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let pi_2 = fixed::types::I16F16::from_num(core::f32::consts::FRAC_PI_2); self.sin(x + pi_2)
}
#[inline]
fn tan(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let sin_x = self.sin(x);
let cos_x = self.cos(x);
if cos_x.abs() < fixed::types::I16F16::from_num(0.001) {
if sin_x >= fixed::types::I16F16::ZERO {
fixed::types::I16F16::from_num(100.0) } else {
fixed::types::I16F16::from_num(-100.0) }
} else {
sin_x / cos_x
}
}
#[inline]
fn to_radians(&self, degrees: fixed::types::I16F16) -> fixed::types::I16F16 {
let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
degrees * pi / fixed::types::I16F16::from_num(180.0)
}
#[inline]
fn to_degrees(&self, radians: fixed::types::I16F16) -> fixed::types::I16F16 {
let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
radians * fixed::types::I16F16::from_num(180.0) / pi
}
#[inline]
fn atan2(&self, y: fixed::types::I16F16, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let zero = fixed::types::I16F16::ZERO;
let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
let pi_2 = pi / 2;
if x == zero {
if y > zero {
return pi_2;
} else if y < zero {
return -pi_2;
} else {
return zero; }
}
let ratio = y / x;
let atan_ratio = self.atan_approx(ratio);
if x > zero {
atan_ratio
} else if y >= zero {
atan_ratio + pi
} else {
atan_ratio - pi
}
}
}
#[cfg(any(feature = "fixed-point", feature = "cordic-math"))]
impl FixedPointBackend {
#[allow(clippy::only_used_in_recursion)]
fn atan_approx(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let abs_x = x.abs();
let pi_4 = fixed::types::I16F16::from_num(core::f32::consts::FRAC_PI_4);
if abs_x <= fixed::types::I16F16::ONE {
let x2 = x * x;
let x3 = x2 * x;
let x5 = x3 * x2;
let x7 = x5 * x2;
x - x3 / 3 + x5 / 5 - x7 / 7
} else {
let inv_x = fixed::types::I16F16::ONE / x;
let atan_inv = self.atan_approx(inv_x);
if x > fixed::types::I16F16::ZERO {
pi_4 * 2 - atan_inv
} else {
-pi_4 * 2 - atan_inv
}
}
}
}
#[cfg(feature = "integer-math")]
pub struct IntegerBackend;
#[cfg(feature = "integer-math")]
impl MathBackend<i32> for IntegerBackend {
#[inline]
fn sqrt(&self, x: i32) -> i32 {
if x <= 0 {
return 0;
}
let mut left = 0i32;
let mut right = x;
let mut result = 0i32;
while left <= right {
let mid = left + (right - left) / 2;
let mid_squared = mid.saturating_mul(mid);
if mid_squared == x {
return mid;
} else if mid_squared < x {
left = mid + 1;
result = mid;
} else {
right = mid - 1;
}
}
result
}
#[inline]
fn abs(&self, x: i32) -> i32 {
x.abs()
}
#[inline]
fn min(&self, a: i32, b: i32) -> i32 {
if a < b {
a
} else {
b
}
}
#[inline]
fn max(&self, a: i32, b: i32) -> i32 {
if a > b {
a
} else {
b
}
}
#[inline]
fn floor(&self, x: i32) -> i32 {
x }
#[inline]
fn ceil(&self, x: i32) -> i32 {
x }
#[inline]
fn pow(&self, x: i32, y: i32) -> i32 {
if y == 0 {
return 1;
}
if y < 0 {
return 0; }
let mut result = 1i32;
let mut base = x;
let mut exp = y as u32;
while exp > 0 {
if exp & 1 == 1 {
result = result.saturating_mul(base);
}
base = base.saturating_mul(base);
exp >>= 1;
}
result
}
#[inline]
fn ln(&self, x: i32) -> i32 {
if x <= 0 {
return -1000; }
match x {
1 => 0,
2..=3 => 693, 4..=7 => 1386, 8..=15 => 2079, 16..=31 => 2772, _ => {
let bit_len = 32 - x.leading_zeros() as i32;
bit_len * 693 }
}
}
#[inline]
fn log10(&self, x: i32) -> i32 {
let ln_x = self.ln(x);
ln_x * 1000 / 2303 }
#[inline]
fn sin(&self, x: i32) -> i32 {
let pi_1000 = 3142; let two_pi_1000 = 6284;
let mut angle = x % two_pi_1000;
if angle < 0 {
angle += two_pi_1000;
}
if angle <= pi_1000 / 2 {
(angle * 1000) / (pi_1000 / 2)
} else if angle <= pi_1000 {
((pi_1000 - angle) * 1000) / (pi_1000 / 2)
} else if angle <= 3 * pi_1000 / 2 {
-((angle - pi_1000) * 1000) / (pi_1000 / 2)
} else {
-((two_pi_1000 - angle) * 1000) / (pi_1000 / 2)
}
}
#[inline]
fn cos(&self, x: i32) -> i32 {
let pi_2_1000 = 1571; self.sin(x + pi_2_1000)
}
#[inline]
fn tan(&self, x: i32) -> i32 {
let sin_x = self.sin(x);
let cos_x = self.cos(x);
if cos_x.abs() < 10 {
if sin_x >= 0 {
100000 } else {
-100000 }
} else {
(sin_x * 1000) / cos_x
}
}
#[inline]
fn to_radians(&self, degrees: i32) -> i32 {
(degrees * 3142) / (180 * 1000)
}
#[inline]
fn to_degrees(&self, radians: i32) -> i32 {
(radians * 180 * 1000) / 3142
}
#[inline]
fn atan2(&self, y: i32, x: i32) -> i32 {
let pi_1000 = 3142; let pi_2_1000 = 1571;
if x == 0 {
if y > 0 {
return pi_2_1000;
} else if y < 0 {
return -pi_2_1000;
} else {
return 0; }
}
let abs_y = y.abs();
let abs_x = x.abs();
let angle = if abs_x >= abs_y {
(abs_y * pi_2_1000) / abs_x / 2 } else {
pi_2_1000 - (abs_x * pi_2_1000) / abs_y / 2
};
if x > 0 && y >= 0 {
angle } else if x <= 0 && y > 0 {
pi_1000 - angle } else if x < 0 && y <= 0 {
-pi_1000 + angle } else {
-angle }
}
}
#[cfg(feature = "cordic-math")]
pub struct CordicBackend;
#[cfg(feature = "cordic-math")]
impl MathBackend<fixed::types::I16F16> for CordicBackend {
#[inline]
fn sqrt(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let fixed_backend = FixedPointBackend;
fixed_backend.sqrt(x)
}
#[inline]
fn abs(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
x.abs()
}
#[inline]
fn min(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
if a < b {
a
} else {
b
}
}
#[inline]
fn max(&self, a: fixed::types::I16F16, b: fixed::types::I16F16) -> fixed::types::I16F16 {
if a > b {
a
} else {
b
}
}
#[inline]
fn floor(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
x.floor()
}
#[inline]
fn ceil(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
x.ceil()
}
#[inline]
fn pow(&self, x: fixed::types::I16F16, y: fixed::types::I16F16) -> fixed::types::I16F16 {
let fixed_backend = FixedPointBackend;
fixed_backend.pow(x, y)
}
#[inline]
fn ln(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let fixed_backend = FixedPointBackend;
fixed_backend.ln(x)
}
#[inline]
fn log10(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let fixed_backend = FixedPointBackend;
fixed_backend.log10(x)
}
#[inline]
fn sin(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let (sin_val, _cos_val) = cordic::sin_cos(x);
sin_val
}
#[inline]
fn cos(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let (_sin_val, cos_val) = cordic::sin_cos(x);
cos_val
}
#[inline]
fn tan(&self, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let sin_x = self.sin(x);
let cos_x = self.cos(x);
if cos_x.abs() < fixed::types::I16F16::from_num(0.001) {
if sin_x >= fixed::types::I16F16::ZERO {
fixed::types::I16F16::from_num(100.0)
} else {
fixed::types::I16F16::from_num(-100.0)
}
} else {
sin_x / cos_x
}
}
#[inline]
fn to_radians(&self, degrees: fixed::types::I16F16) -> fixed::types::I16F16 {
let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
degrees * pi / fixed::types::I16F16::from_num(180.0)
}
#[inline]
fn to_degrees(&self, radians: fixed::types::I16F16) -> fixed::types::I16F16 {
let pi = fixed::types::I16F16::from_num(core::f32::consts::PI);
radians * fixed::types::I16F16::from_num(180.0) / pi
}
#[inline]
fn atan2(&self, y: fixed::types::I16F16, x: fixed::types::I16F16) -> fixed::types::I16F16 {
let fixed_backend = FixedPointBackend;
fixed_backend.atan2(y, x)
}
}
#[cfg(feature = "floating-point")]
pub use self::FloatingPointBackend as DefaultBackend;
#[cfg(all(feature = "libm-math", not(feature = "floating-point")))]
pub use self::LibmBackend as DefaultBackend;
#[cfg(all(
feature = "fixed-point",
not(any(feature = "floating-point", feature = "libm-math"))
))]
pub use self::FixedPointBackend as DefaultBackend;
#[cfg(all(
feature = "cordic-math",
not(any(
feature = "floating-point",
feature = "libm-math",
feature = "fixed-point"
))
))]
pub use self::CordicBackend as DefaultBackend;
#[cfg(all(
feature = "integer-math",
not(any(
feature = "floating-point",
feature = "libm-math",
feature = "fixed-point",
feature = "cordic-math"
))
))]
pub use self::IntegerBackend as DefaultBackend;
#[cfg(not(any(
feature = "floating-point",
feature = "libm-math",
feature = "fixed-point",
feature = "cordic-math",
feature = "integer-math"
)))]
pub struct FallbackBackend;
#[cfg(not(any(
feature = "floating-point",
feature = "libm-math",
feature = "fixed-point",
feature = "cordic-math",
feature = "integer-math"
)))]
impl MathBackend<f32> for FallbackBackend {
fn sqrt(&self, x: f32) -> f32 {
if x <= 0.0 {
return 0.0;
}
let x_int = x as i32;
if x_int <= 0 {
return 0.0;
}
let mut left = 0i32;
let mut right = x_int;
let mut result = 0i32;
while left <= right {
let mid = left + (right - left) / 2;
let mid_squared = mid.saturating_mul(mid);
if mid_squared <= x_int {
result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
result as f32
}
fn abs(&self, x: f32) -> f32 {
if x < 0.0 {
-x
} else {
x
}
}
fn min(&self, a: f32, b: f32) -> f32 {
if a < b {
a
} else {
b
}
}
fn max(&self, a: f32, b: f32) -> f32 {
if a > b {
a
} else {
b
}
}
fn floor(&self, x: f32) -> f32 {
(x as i32) as f32
}
fn ceil(&self, x: f32) -> f32 {
let int_part = x as i32;
if x > int_part as f32 {
(int_part + 1) as f32
} else {
int_part as f32
}
}
fn pow(&self, x: f32, y: f32) -> f32 {
if y == 0.0 {
return 1.0;
}
if y == 1.0 {
return x;
}
if y == 2.0 {
return x * x;
}
x
}
fn ln(&self, _x: f32) -> f32 {
0.0
} fn log10(&self, _x: f32) -> f32 {
0.0
} fn sin(&self, _x: f32) -> f32 {
0.0
} fn cos(&self, _x: f32) -> f32 {
1.0
} fn tan(&self, _x: f32) -> f32 {
0.0
} fn atan2(&self, _y: f32, _x: f32) -> f32 {
0.0
} fn to_radians(&self, degrees: f32) -> f32 {
degrees * 0.017453292
} fn to_degrees(&self, radians: f32) -> f32 {
radians * 57.29578
} }
#[cfg(not(any(
feature = "floating-point",
feature = "libm-math",
feature = "fixed-point",
feature = "cordic-math",
feature = "integer-math"
)))]
pub use self::FallbackBackend as DefaultBackend;