use crate::traits::CordicNumber;
#[derive(Clone, Copy, Debug)]
pub struct NonNegative<T>(T);
impl<T: CordicNumber> NonNegative<T> {
#[inline]
#[must_use]
pub fn new(value: T) -> Option<Self> {
(value >= T::zero()).then_some(Self(value))
}
#[inline]
#[must_use]
pub fn one_plus_square(x: T) -> Self {
let x_sq = x.saturating_mul(x);
Self(T::one().saturating_add(x_sq))
}
#[inline]
#[must_use]
pub fn one_minus_square(x: UnitInterval<T>) -> Self {
let x_sq = x.0.saturating_mul(x.0);
Self(T::one().saturating_sub(x_sq))
}
#[inline]
#[must_use]
pub fn square_minus_one(x: AtLeastOne<T>) -> Self {
let x_sq = x.0.saturating_mul(x.0);
Self(x_sq.saturating_sub(T::one()))
}
#[inline]
#[must_use]
pub const fn get(self) -> T {
self.0
}
}
#[derive(Clone, Copy, Debug)]
pub struct UnitInterval<T>(T);
impl<T: CordicNumber> UnitInterval<T> {
#[inline]
#[must_use]
pub fn new(value: T) -> Option<Self> {
let one = T::one();
(value >= -one && value <= one).then_some(Self(value))
}
#[inline]
#[must_use]
pub const fn get(self) -> T {
self.0
}
}
#[derive(Clone, Copy, Debug)]
pub struct OpenUnitInterval<T>(T);
impl<T: CordicNumber> OpenUnitInterval<T> {
#[inline]
#[must_use]
pub fn new(value: T) -> Option<Self> {
let one = T::one();
(value > -one && value < one).then_some(Self(value))
}
#[inline]
#[must_use]
pub fn from_div_by_sqrt_one_plus_square(x: T, sqrt_one_plus_x_sq: T) -> Self {
Self(x.div(sqrt_one_plus_x_sq))
}
#[inline]
#[must_use]
pub fn from_sqrt_square_minus_one_div(sqrt_x_sq_minus_one: T, x: AtLeastOne<T>) -> Self {
Self(sqrt_x_sq_minus_one.div(x.0))
}
#[inline]
#[must_use]
pub(crate) fn from_normalized_ln_arg(x: NormalizedLnArg<T>) -> Self {
let x_minus_1 = x.0 - T::one();
let x_plus_1 = x.0 + T::one();
Self(x_minus_1.div(x_plus_1))
}
#[inline]
#[must_use]
pub const fn get(self) -> T {
self.0
}
}
#[derive(Clone, Copy, Debug)]
pub struct AtLeastOne<T>(T);
impl<T: CordicNumber> AtLeastOne<T> {
#[inline]
#[must_use]
pub fn new(value: T) -> Option<Self> {
(value >= T::one()).then_some(Self(value))
}
#[inline]
#[must_use]
pub const fn get(self) -> T {
self.0
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct NormalizedLnArg<T>(T);
impl<T: CordicNumber> NormalizedLnArg<T> {
#[inline]
#[must_use]
pub(crate) const fn from_normalized(value: T) -> Self {
Self(value)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, reason = "test code uses unwrap for conciseness")]
mod tests {
use super::*;
use fixed::types::I16F16;
#[test]
fn non_negative_new() {
assert!(NonNegative::new(I16F16::from_num(0)).is_some());
assert!(NonNegative::new(I16F16::from_num(1)).is_some());
assert!(NonNegative::new(I16F16::from_num(-1)).is_none());
}
#[test]
fn non_negative_one_plus_square() {
let x = I16F16::from_num(2);
let nn = NonNegative::one_plus_square(x);
assert_eq!(nn.get(), I16F16::from_num(5));
}
#[test]
fn non_negative_one_minus_square() {
let unit = UnitInterval::new(I16F16::from_num(0.5)).unwrap();
let nn = NonNegative::one_minus_square(unit);
let val: f32 = nn.get().to_num();
assert!((val - 0.75).abs() < 0.01);
}
#[test]
fn non_negative_square_minus_one() {
let at_least = AtLeastOne::new(I16F16::from_num(2)).unwrap();
let nn = NonNegative::square_minus_one(at_least);
assert_eq!(nn.get(), I16F16::from_num(3));
}
#[test]
fn unit_interval_new() {
assert!(UnitInterval::new(I16F16::from_num(0)).is_some());
assert!(UnitInterval::new(I16F16::from_num(1)).is_some());
assert!(UnitInterval::new(I16F16::from_num(-1)).is_some());
assert!(UnitInterval::new(I16F16::from_num(1.1)).is_none());
assert!(UnitInterval::new(I16F16::from_num(-1.1)).is_none());
}
#[test]
fn unit_interval_get() {
let unit = UnitInterval::new(I16F16::from_num(0.5)).unwrap();
assert_eq!(unit.get(), I16F16::from_num(0.5));
}
#[test]
fn open_unit_interval_new() {
assert!(OpenUnitInterval::new(I16F16::from_num(0)).is_some());
assert!(OpenUnitInterval::new(I16F16::from_num(0.5)).is_some());
assert!(OpenUnitInterval::new(I16F16::from_num(1)).is_none());
assert!(OpenUnitInterval::new(I16F16::from_num(-1)).is_none());
}
#[test]
fn open_unit_interval_get() {
let open = OpenUnitInterval::new(I16F16::from_num(0.5)).unwrap();
assert_eq!(open.get(), I16F16::from_num(0.5));
}
#[test]
fn open_unit_interval_from_div() {
let x = I16F16::from_num(1);
let sqrt_1_plus_x_sq = I16F16::SQRT_2;
let open = OpenUnitInterval::from_div_by_sqrt_one_plus_square(x, sqrt_1_plus_x_sq);
let val: f32 = open.get().to_num();
assert!((val - core::f32::consts::FRAC_1_SQRT_2).abs() < 0.01);
}
#[test]
fn open_unit_interval_from_sqrt_div() {
let at_least = AtLeastOne::new(I16F16::from_num(2)).unwrap();
#[allow(
clippy::approx_constant,
reason = "testing with known approximation of sqrt(3)"
)]
let sqrt_x_sq_minus_one = I16F16::from_num(1.732);
let open = OpenUnitInterval::from_sqrt_square_minus_one_div(sqrt_x_sq_minus_one, at_least);
let val: f32 = open.get().to_num();
#[allow(
clippy::approx_constant,
reason = "testing with known approximation of sqrt(3)/2"
)]
let expected = 0.866_f32;
assert!((val - expected).abs() < 0.01);
}
#[test]
fn at_least_one_new() {
assert!(AtLeastOne::new(I16F16::from_num(1)).is_some());
assert!(AtLeastOne::new(I16F16::from_num(2)).is_some());
assert!(AtLeastOne::new(I16F16::from_num(0.9)).is_none());
}
#[test]
fn at_least_one_get() {
let at_least = AtLeastOne::new(I16F16::from_num(2)).unwrap();
assert_eq!(at_least.get(), I16F16::from_num(2));
}
#[test]
fn open_unit_interval_from_normalized_ln_arg() {
let norm = NormalizedLnArg::from_normalized(I16F16::from_num(1.5));
let open = OpenUnitInterval::from_normalized_ln_arg(norm);
let val: f32 = open.get().to_num();
assert!((val - 0.2).abs() < 0.01);
}
}