use num_traits::ConstZero;
use std::fmt::{Debug, Display};
use std::ops::{AddAssign, Sub};
use fixed::types::extra::{U0, U16, U8};
use fixed::{FixedI32, FixedU16};
use num_traits::Float;
pub trait Axis:
Copy
+ PartialEq
+ PartialOrd
+ Sub<Output = Self>
+ AddAssign<Self>
+ Debug
+ Display
+ crate::stem_strategy::CompareBlock3
+ crate::stem_strategy::CompareBlock4
{
const VALUE_WIDTH_BYTES: usize = size_of::<Self::Coord>();
type Coord: Copy;
fn zero() -> Self::Coord;
fn max_value() -> Self::Coord;
fn min_value() -> Self::Coord;
fn is_max_value(coord: Self::Coord) -> bool;
fn cmp(a: Self::Coord, b: Self::Coord) -> std::cmp::Ordering;
fn saturating_dist(a: Self::Coord, b: Self::Coord) -> Self::Coord;
fn saturating_add(a: Self::Coord, b: Self::Coord) -> Self::Coord;
fn max(a: Self::Coord, b: Self::Coord) -> Self::Coord;
}
macro_rules! impl_axis_float {
($t:ty, SIMD_BLOCK_SUPPORT => ( $( $block_size:literal => ($prune_fn:path, $compare_fn:path) ),* $(,)? )) => {
impl_axis_float!($t);
$(
impl_simd_block_support!($t, $block_size, $prune_fn, $compare_fn);
)*
};
($t:ty) => {
impl Axis for $t {
type Coord = $t;
#[inline(always)]
fn zero() -> Self::Coord {
0.0
}
#[inline(always)]
fn max_value() -> Self::Coord {
<$t>::infinity()
}
#[inline(always)]
fn min_value() -> Self::Coord {
<$t>::neg_infinity()
}
#[inline(always)]
fn is_max_value(coord: Self::Coord) -> bool {
coord.is_infinite() && coord.is_sign_positive()
}
#[inline(always)]
fn cmp(a: Self::Coord, b: Self::Coord) -> std::cmp::Ordering {
if a < b {
std::cmp::Ordering::Less
} else if a > b {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
#[inline(always)]
fn saturating_dist(a: Self::Coord, b: Self::Coord) -> Self::Coord {
(a - b).abs()
}
#[inline(always)]
fn saturating_add(a: Self::Coord, b: Self::Coord) -> Self::Coord {
a + b
}
#[inline(always)]
fn max(a: Self::Coord, b: Self::Coord) -> Self::Coord {
a.max(b)
}
}
};
}
impl_axis_float!(f32);
impl_axis_float!(f64);
#[cfg(feature = "fixed")]
macro_rules! impl_axis_fixed {
($t:ty, SIMD_BLOCK_SUPPORT => ( $( $block_size:literal => ($prune_fn:path, $compare_fn:path) ),* $(,)? )) => {
impl_axis_fixed!($t);
$(
impl_simd_block_support!($t, $block_size, $prune_fn, $compare_fn);
)*
};
($t:ty) => {
#[cfg(feature = "fixed")]
impl Axis for $t {
type Coord = $t;
#[inline(always)]
fn zero() -> Self::Coord {
<$t>::from_num(0)
}
#[inline(always)]
fn max_value() -> Self::Coord {
<Self::Coord>::MAX
}
#[inline(always)]
fn min_value() -> Self::Coord {
unimplemented!("min_value not yet implemented for fixed point types")
}
#[inline(always)]
fn is_max_value(coord: Self::Coord) -> bool {
coord == <$t>::max_value()
}
#[inline(always)]
fn cmp(a: Self::Coord, b: Self::Coord) -> std::cmp::Ordering {
a.cmp(&b)
}
#[inline(always)]
fn saturating_dist(a: Self::Coord, b: Self::Coord) -> Self::Coord {
if a >= b {
a - b
} else {
b - a
}
}
#[inline(always)]
fn saturating_add(a: Self::Coord, b: Self::Coord) -> Self::Coord {
a.saturating_add(b)
}
#[inline(always)]
fn max(a: Self::Coord, b: Self::Coord) -> Self::Coord {
a.max(b)
}
}
};
}
#[cfg(feature = "fixed")]
impl_axis_fixed!(FixedI32<U16>);
#[cfg(feature = "fixed")]
impl_axis_fixed!(FixedI32<U0>);
#[cfg(feature = "fixed")]
impl_axis_fixed!(FixedU16<U8>);
#[cfg(feature = "f16")]
impl Axis for half::f16 {
type Coord = half::f16;
#[inline(always)]
fn zero() -> Self::Coord {
half::f16::from_f32(0.0)
}
#[inline(always)]
fn max_value() -> Self::Coord {
half::f16::from_f32(f32::INFINITY)
}
#[inline(always)]
fn min_value() -> Self::Coord {
half::f16::from_f32(f32::NEG_INFINITY)
}
#[inline(always)]
fn is_max_value(coord: Self::Coord) -> bool {
coord.is_infinite() && coord.is_sign_positive()
}
#[allow(clippy::ifs_same_cond)]
#[inline(always)]
fn cmp(a: Self::Coord, b: Self::Coord) -> std::cmp::Ordering {
if a < b {
std::cmp::Ordering::Less
} else if b > a {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
#[inline(always)]
fn saturating_dist(a: Self::Coord, b: Self::Coord) -> Self::Coord {
(a - b).abs()
}
#[inline(always)]
fn saturating_add(a: Self::Coord, b: Self::Coord) -> Self::Coord {
a + b
}
#[inline(always)]
fn max(a: Self::Coord, b: Self::Coord) -> Self::Coord {
if a > b {
a
} else {
b
}
}
}
macro_rules! impl_axis_uint {
($t:ty, SIMD_BLOCK_SUPPORT => ( $( $block_size:literal => ($prune_fn:path, $compare_fn:path) ),* $(,)? )) => {
impl_axis_uint!($t);
$(
impl_simd_block_support!($t, $block_size, $prune_fn, $compare_fn);
)*
};
($t:ty) => {
impl Axis for $t {
type Coord = $t;
#[inline(always)]
fn zero() -> Self::Coord {
<Self::Coord>::ZERO
}
#[inline(always)]
fn max_value() -> Self::Coord {
<Self::Coord>::MAX
}
#[inline(always)]
fn min_value() -> Self::Coord {
<$t>::zero()
}
#[inline(always)]
fn is_max_value(coord: Self::Coord) -> bool {
coord == <$t>::MAX
}
#[inline(always)]
fn cmp(a: Self::Coord, b: Self::Coord) -> std::cmp::Ordering {
a.cmp(&b)
}
#[inline(always)]
fn saturating_dist(a: Self::Coord, b: Self::Coord) -> Self::Coord {
if a >= b {
a - b
} else {
b - a
}
}
#[inline(always)]
fn saturating_add(a: Self::Coord, b: Self::Coord) -> Self::Coord {
a.saturating_add(b)
}
#[inline(always)]
fn max(a: Self::Coord, b: Self::Coord) -> Self::Coord {
a.max(b)
}
}
};
}
impl_axis_uint!(u8);
impl_axis_uint!(u16);
impl_axis_uint!(u32);
#[cfg(test)]
mod tests {
use super::Axis;
#[test]
fn float_axis_helpers_behave_as_expected() {
assert_eq!(<f32 as Axis>::zero(), 0.0);
assert!(<f32 as Axis>::is_max_value(<f32 as Axis>::max_value()));
assert_eq!(<f32 as Axis>::cmp(1.0, 2.0), std::cmp::Ordering::Less);
assert_eq!(<f32 as Axis>::cmp(2.0, 1.0), std::cmp::Ordering::Greater);
assert_eq!(<f32 as Axis>::cmp(2.0, 2.0), std::cmp::Ordering::Equal);
assert_eq!(<f32 as Axis>::saturating_dist(5.0, 2.5), 2.5);
assert_eq!(<f32 as Axis>::saturating_add(1.5, 2.0), 3.5);
assert_eq!(<f32 as Axis>::max(3.0, 4.0), 4.0);
assert_eq!(<f64 as Axis>::zero(), 0.0);
assert!(!<f64 as Axis>::is_max_value(<f64 as Axis>::min_value()));
assert_eq!(<f64 as Axis>::saturating_dist(2.0, 5.5), 3.5);
}
#[test]
fn unsigned_axis_helpers_behave_as_expected() {
assert_eq!(<u8 as Axis>::zero(), 0);
assert_eq!(<u8 as Axis>::min_value(), 0);
assert_eq!(<u8 as Axis>::max_value(), u8::MAX);
assert!(<u8 as Axis>::is_max_value(u8::MAX));
assert_eq!(<u8 as Axis>::cmp(1, 2), std::cmp::Ordering::Less);
assert_eq!(<u8 as Axis>::saturating_dist(2, 5), 3);
assert_eq!(<u8 as Axis>::saturating_add(250, 10), u8::MAX);
assert_eq!(<u8 as Axis>::max(3, 4), 4);
assert_eq!(<u16 as Axis>::saturating_dist(9, 4), 5);
assert_eq!(<u32 as Axis>::saturating_add(u32::MAX, 1), u32::MAX);
}
#[cfg(feature = "fixed")]
#[test]
fn fixed_axis_helpers_behave_as_expected() {
let a = fixed::FixedI32::<fixed::types::extra::U16>::from_num(1.5);
let b = fixed::FixedI32::<fixed::types::extra::U16>::from_num(0.25);
assert_eq!(
<fixed::FixedI32<fixed::types::extra::U16> as Axis>::saturating_dist(a, b),
fixed::FixedI32::<fixed::types::extra::U16>::from_num(1.25)
);
assert_eq!(
<fixed::FixedU16<fixed::types::extra::U8> as Axis>::max(
fixed::FixedU16::<fixed::types::extra::U8>::from_num(2),
fixed::FixedU16::<fixed::types::extra::U8>::from_num(3)
),
fixed::FixedU16::<fixed::types::extra::U8>::from_num(3)
);
}
}