use crate::UnitIntervalFloat;
use core::{
cmp::Ordering,
error::Error,
fmt,
ops::{Add, Deref, Div, Mul, Neg, Rem, Sub},
};
#[cfg_attr(
feature = "rkyv",
derive(::rkyv::Archive, ::rkyv::Serialize),
rkyv(crate = ::rkyv)
)]
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct UnitInterval<T = f32>(T);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct UnitIntervalError;
impl fmt::Display for UnitIntervalError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("value is outside the unit interval")
}
}
impl Error for UnitIntervalError {}
#[cfg(feature = "rkyv")]
#[cfg_attr(docsrs, doc(cfg(feature = "rkyv")))]
mod rkyv {
use super::*;
use ::rkyv::{
Archive, Deserialize,
rancor::{Fallible, Source, fail},
};
impl<T, D> Deserialize<UnitInterval<T>, D> for ArchivedUnitInterval<T>
where
T: Archive + UnitIntervalFloat,
T::Archived: Deserialize<T, D>,
D: Fallible + ?Sized,
D::Error: Source,
{
#[inline]
fn deserialize(&self, deserializer: &mut D) -> Result<UnitInterval<T>, D::Error> {
let value = self.0.deserialize(deserializer)?;
if let Some(value) = UnitInterval::new(value) {
Ok(value)
} else {
fail!(UnitIntervalError);
}
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
mod serde {
use super::*;
use ::serde::{Deserialize, Deserializer, Serialize, Serializer, de};
impl<T: Serialize> Serialize for UnitInterval<T> {
#[inline]
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
impl<'de, T> Deserialize<'de> for UnitInterval<T>
where
T: UnitIntervalFloat + Deserialize<'de>,
{
#[inline]
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = T::deserialize(deserializer)?;
Self::new(value).ok_or_else(|| de::Error::custom(UnitIntervalError))
}
}
}
#[cfg(feature = "bytemuck")]
#[cfg_attr(docsrs, doc(cfg(feature = "bytemuck")))]
mod bytemuck {
use super::*;
unsafe impl<T> ::bytemuck::Zeroable for UnitInterval<T> where
T: UnitIntervalFloat + ::bytemuck::Zeroable
{
}
unsafe impl<T> ::bytemuck::NoUninit for UnitInterval<T> where
T: UnitIntervalFloat + ::bytemuck::NoUninit
{
}
unsafe impl<T> ::bytemuck::CheckedBitPattern for UnitInterval<T>
where
T: UnitIntervalFloat + ::bytemuck::AnyBitPattern,
{
type Bits = T;
#[inline]
fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
UnitInterval::contains(*bits)
}
}
}
#[cfg(feature = "arbitrary")]
#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
mod arbitrary {
use super::*;
use ::arbitrary::{Arbitrary, Result, Unstructured};
macro_rules! impl_arbitrary_unit_interval {
($float:ty, $unsigned:ty) => {
impl<'a> Arbitrary<'a> for UnitInterval<$float> {
#[inline]
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
let raw = <$unsigned as Arbitrary<'a>>::arbitrary(u)?;
let value = raw as $float / <$unsigned>::MAX as $float;
Ok(Self::from_inner(value))
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<$unsigned as Arbitrary<'a>>::size_hint(depth)
}
}
};
}
impl_arbitrary_unit_interval!(f32, u32);
impl_arbitrary_unit_interval!(f64, u64);
}
#[cfg(feature = "num-traits")]
#[cfg_attr(docsrs, doc(cfg(feature = "num-traits")))]
mod num_traits {
use super::*;
use ::num_traits::{
AsPrimitive, Bounded, ConstOne, FromPrimitive, NumCast, One, Pow, ToBytes, ToPrimitive,
ops::{checked::CheckedMul, saturating::SaturatingMul},
};
macro_rules! impl_num_traits_unit_interval {
($float:ty) => {
impl ToPrimitive for UnitInterval<$float> {
#[inline]
fn to_isize(&self) -> Option<isize> {
self.0.to_isize()
}
#[inline]
fn to_i8(&self) -> Option<i8> {
self.0.to_i8()
}
#[inline]
fn to_i16(&self) -> Option<i16> {
self.0.to_i16()
}
#[inline]
fn to_i32(&self) -> Option<i32> {
self.0.to_i32()
}
#[inline]
fn to_i64(&self) -> Option<i64> {
self.0.to_i64()
}
#[inline]
fn to_i128(&self) -> Option<i128> {
self.0.to_i128()
}
#[inline]
fn to_usize(&self) -> Option<usize> {
self.0.to_usize()
}
#[inline]
fn to_u8(&self) -> Option<u8> {
self.0.to_u8()
}
#[inline]
fn to_u16(&self) -> Option<u16> {
self.0.to_u16()
}
#[inline]
fn to_u32(&self) -> Option<u32> {
self.0.to_u32()
}
#[inline]
fn to_u64(&self) -> Option<u64> {
self.0.to_u64()
}
#[inline]
fn to_u128(&self) -> Option<u128> {
self.0.to_u128()
}
#[inline]
fn to_f32(&self) -> Option<f32> {
self.0.to_f32()
}
#[inline]
fn to_f64(&self) -> Option<f64> {
self.0.to_f64()
}
}
impl FromPrimitive for UnitInterval<$float> {
#[inline]
fn from_i64(n: i64) -> Option<Self> {
<$float as FromPrimitive>::from_i64(n).and_then(Self::new)
}
#[inline]
fn from_u64(n: u64) -> Option<Self> {
<$float as FromPrimitive>::from_u64(n).and_then(Self::new)
}
#[inline]
fn from_f32(n: f32) -> Option<Self> {
<$float as FromPrimitive>::from_f32(n).and_then(Self::new)
}
#[inline]
fn from_f64(n: f64) -> Option<Self> {
<$float as FromPrimitive>::from_f64(n).and_then(Self::new)
}
}
impl NumCast for UnitInterval<$float> {
#[inline]
fn from<T: ToPrimitive>(n: T) -> Option<Self> {
<$float as NumCast>::from(n).and_then(Self::new)
}
}
impl One for UnitInterval<$float> {
#[inline]
fn one() -> Self {
Self::ONE
}
#[inline]
fn is_one(&self) -> bool {
UnitInterval::is_one(*self)
}
}
impl ConstOne for UnitInterval<$float> {
const ONE: Self = Self::ONE;
}
impl Bounded for UnitInterval<$float> {
#[inline]
fn min_value() -> Self {
Self::ZERO
}
#[inline]
fn max_value() -> Self {
Self::ONE
}
}
impl ToBytes for UnitInterval<$float> {
type Bytes = <$float as ToBytes>::Bytes;
#[inline]
fn to_be_bytes(&self) -> Self::Bytes {
self.0.to_be_bytes()
}
#[inline]
fn to_le_bytes(&self) -> Self::Bytes {
self.0.to_le_bytes()
}
#[inline]
fn to_ne_bytes(&self) -> Self::Bytes {
self.0.to_ne_bytes()
}
}
impl CheckedMul for UnitInterval<$float> {
#[inline]
fn checked_mul(&self, v: &Self) -> Option<Self> {
Some(*self * *v)
}
}
impl SaturatingMul for UnitInterval<$float> {
#[inline]
fn saturating_mul(&self, v: &Self) -> Self {
*self * *v
}
}
};
}
macro_rules! impl_pow_unit_interval {
($rhs:ty) => {
impl<T: UnitIntervalFloat> Pow<$rhs> for UnitInterval<T> {
type Output = Self;
#[inline]
fn pow(self, rhs: $rhs) -> Self::Output {
pow_unit_interval(self, rhs as usize)
}
}
impl<T: UnitIntervalFloat> Pow<&$rhs> for UnitInterval<T> {
type Output = Self;
#[inline]
fn pow(self, rhs: &$rhs) -> Self::Output {
pow_unit_interval(self, *rhs as usize)
}
}
impl<T: UnitIntervalFloat> Pow<$rhs> for &UnitInterval<T> {
type Output = UnitInterval<T>;
#[inline]
fn pow(self, rhs: $rhs) -> Self::Output {
pow_unit_interval(*self, rhs as usize)
}
}
impl<T: UnitIntervalFloat> Pow<&$rhs> for &UnitInterval<T> {
type Output = UnitInterval<T>;
#[inline]
fn pow(self, rhs: &$rhs) -> Self::Output {
pow_unit_interval(*self, *rhs as usize)
}
}
};
}
macro_rules! impl_as_primitive_unit_interval {
($float:ty => $($target:ty),+ $(,)?) => {
$(
impl AsPrimitive<$target> for UnitInterval<$float> {
#[inline]
fn as_(self) -> $target {
self.0 as $target
}
}
)+
};
}
impl_num_traits_unit_interval!(f32);
impl_num_traits_unit_interval!(f64);
impl_pow_unit_interval!(u8);
impl_pow_unit_interval!(u16);
impl_pow_unit_interval!(u32);
impl_pow_unit_interval!(usize);
impl_as_primitive_unit_interval!(f32 => f32, f64);
impl_as_primitive_unit_interval!(f64 => f32, f64);
impl AsPrimitive<UnitInterval<f32>> for UnitInterval<f32> {
#[inline]
fn as_(self) -> UnitInterval<f32> {
self
}
}
impl AsPrimitive<UnitInterval<f64>> for UnitInterval<f32> {
#[inline]
fn as_(self) -> UnitInterval<f64> {
UnitInterval::from_inner(self.0 as f64)
}
}
impl AsPrimitive<UnitInterval<f32>> for UnitInterval<f64> {
#[inline]
fn as_(self) -> UnitInterval<f32> {
UnitInterval::from_inner(self.0 as f32)
}
}
impl AsPrimitive<UnitInterval<f64>> for UnitInterval<f64> {
#[inline]
fn as_(self) -> UnitInterval<f64> {
self
}
}
#[inline]
fn pow_unit_interval<T: UnitIntervalFloat>(
base: UnitInterval<T>,
exp: usize,
) -> UnitInterval<T> {
let mut result = UnitInterval::ONE;
let mut factor = base;
let mut exp = exp;
while exp > 0 {
if exp & 1 == 1 {
result = result * factor;
}
exp >>= 1;
if exp > 0 {
factor = factor * factor;
}
}
result
}
}
impl<T: UnitIntervalFloat> UnitInterval<T> {
pub const ZERO: Self = Self(T::ZERO);
pub const ONE: Self = Self(T::ONE);
pub const HALF: Self = Self(T::HALF);
#[inline(always)]
pub fn new(v: T) -> Option<Self> {
if Self::contains(v) {
Some(Self::from_inner(v))
} else {
None
}
}
#[cfg(feature = "unsafe")]
#[inline(always)]
pub const unsafe fn new_unchecked(v: T) -> Self {
Self(v)
}
#[inline]
pub fn contains(v: T) -> bool {
v >= T::ZERO && v <= T::ONE
}
#[inline]
pub fn saturating(v: T) -> Self {
Self::from_inner(v.clamp_unit())
}
#[inline(always)]
pub(crate) fn from_inner(v: T) -> Self {
Self::assert_contains(v);
Self(v)
}
#[cfg(any(test, feature = "assertions"))]
#[inline(always)]
fn assert_contains(v: T) {
assert!(
Self::contains(v),
"UnitInterval invariant violated: value is outside [0, 1]"
);
}
#[cfg(not(any(test, feature = "assertions")))]
#[cfg_attr(docsrs, doc(cfg(feature = "assertions")))]
#[inline(always)]
fn assert_contains(_v: T) {}
#[inline(always)]
pub const fn get(self) -> T {
self.0
}
#[inline(always)]
pub const fn into_inner(self) -> T {
self.0
}
#[inline(always)]
pub fn is_zero(self) -> bool {
self.0 == T::ZERO
}
#[inline(always)]
pub fn is_one(self) -> bool {
self.0 == T::ONE
}
#[inline(always)]
pub fn complement(self) -> Self {
Self::from_inner(T::ONE - self.0)
}
#[inline]
pub fn min(self, rhs: Self) -> Self {
if self.0 <= rhs.0 { self } else { rhs }
}
#[inline]
pub fn max(self, rhs: Self) -> Self {
if self.0 >= rhs.0 { self } else { rhs }
}
#[inline]
pub fn midpoint(self, rhs: Self) -> Self {
Self::from_inner((self.0 + rhs.0) * T::HALF)
}
#[inline]
pub fn distance_to(self, rhs: Self) -> Self {
if self.0 >= rhs.0 {
Self::from_inner(self.0 - rhs.0)
} else {
Self::from_inner(rhs.0 - self.0)
}
}
#[inline(always)]
pub fn checked_add(self, rhs: Self) -> Option<Self> {
Self::new(self.0 + rhs.0)
}
#[cfg(feature = "unsafe")]
#[cfg_attr(docsrs, doc(cfg(feature = "unsafe")))]
#[inline(always)]
pub unsafe fn add_unchecked(self, rhs: Self) -> Self {
unsafe { Self::new_unchecked(self.0 + rhs.0) }
}
#[inline(always)]
pub fn saturating_add(self, rhs: Self) -> Self {
Self::saturating(self.0 + rhs.0)
}
#[inline(always)]
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
Self::new(self.0 - rhs.0)
}
#[cfg(feature = "unsafe")]
#[cfg_attr(docsrs, doc(cfg(feature = "unsafe")))]
#[inline(always)]
pub unsafe fn sub_unchecked(self, rhs: Self) -> Self {
unsafe { Self::new_unchecked(self.0 - rhs.0) }
}
#[inline(always)]
pub fn saturating_sub(self, rhs: Self) -> Self {
Self::saturating(self.0 - rhs.0)
}
#[inline(always)]
pub fn checked_div(self, rhs: Self) -> Option<Self> {
Self::new(self.0 / rhs.0)
}
#[cfg(feature = "unsafe")]
#[cfg_attr(docsrs, doc(cfg(feature = "unsafe")))]
#[inline(always)]
pub unsafe fn div_unchecked(self, rhs: Self) -> Self {
unsafe { Self::new_unchecked(self.0 / rhs.0) }
}
#[inline(always)]
pub fn saturating_div(self, rhs: Self) -> Self {
Self::saturating(self.0 / rhs.0)
}
#[inline(always)]
pub fn checked_scale(self, factor: T) -> Option<Self> {
Self::new(self.0 * factor)
}
#[cfg(feature = "unsafe")]
#[cfg_attr(docsrs, doc(cfg(feature = "unsafe")))]
#[inline(always)]
pub unsafe fn scale_unchecked(self, factor: T) -> Self {
unsafe { Self::new_unchecked(self.0 * factor) }
}
#[inline(always)]
pub fn saturating_scale(self, factor: T) -> Self {
Self::saturating(self.0 * factor)
}
#[inline]
pub fn lerp(self, start: T, end: T) -> T {
start + (end - start) * self.0
}
}
impl<T: UnitIntervalFloat> Default for UnitInterval<T> {
#[inline(always)]
fn default() -> Self {
Self::ZERO
}
}
impl<T> Deref for UnitInterval<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> AsRef<T> for UnitInterval<T> {
#[inline(always)]
fn as_ref(&self) -> &T {
&self.0
}
}
macro_rules! impl_unit_interval_float {
($float:ty) => {
impl crate::private::Sealed for $float {}
impl UnitIntervalFloat for $float {
const ZERO: Self = 0.0;
const NEG_ONE: Self = -1.0;
const ONE: Self = 1.0;
const HALF: Self = 0.5;
#[inline]
fn clamp_unit(self) -> Self {
if self.is_nan() {
return Self::ZERO;
}
self.clamp(Self::ZERO, Self::ONE)
}
#[inline]
fn clamp_signed_unit(self) -> Self {
if self.is_nan() {
return Self::ZERO;
}
self.clamp(Self::NEG_ONE, Self::ONE)
}
}
impl From<UnitInterval<$float>> for $float {
#[inline(always)]
fn from(u: UnitInterval<$float>) -> Self {
u.0
}
}
impl TryFrom<$float> for UnitInterval<$float> {
type Error = UnitIntervalError;
#[inline]
fn try_from(value: $float) -> Result<Self, Self::Error> {
Self::new(value).ok_or(UnitIntervalError)
}
}
impl PartialEq<$float> for UnitInterval<$float> {
#[inline(always)]
fn eq(&self, other: &$float) -> bool {
self.0 == *other
}
}
impl PartialEq<UnitInterval<$float>> for $float {
#[inline(always)]
fn eq(&self, other: &UnitInterval<$float>) -> bool {
*self == other.0
}
}
impl PartialOrd<$float> for UnitInterval<$float> {
#[inline(always)]
fn partial_cmp(&self, other: &$float) -> Option<Ordering> {
self.0.partial_cmp(other)
}
}
impl PartialOrd<UnitInterval<$float>> for $float {
#[inline(always)]
fn partial_cmp(&self, other: &UnitInterval<$float>) -> Option<Ordering> {
self.partial_cmp(&other.0)
}
}
impl Add for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
self.0 + rhs.0
}
}
impl Add<$float> for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn add(self, rhs: $float) -> Self::Output {
self.0 + rhs
}
}
impl Add<UnitInterval<$float>> for $float {
type Output = $float;
#[inline(always)]
fn add(self, rhs: UnitInterval<$float>) -> Self::Output {
self + rhs.0
}
}
impl Sub for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
self.0 - rhs.0
}
}
impl Sub<$float> for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn sub(self, rhs: $float) -> Self::Output {
self.0 - rhs
}
}
impl Sub<UnitInterval<$float>> for $float {
type Output = $float;
#[inline(always)]
fn sub(self, rhs: UnitInterval<$float>) -> Self::Output {
self - rhs.0
}
}
impl Mul<$float> for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn mul(self, rhs: $float) -> Self::Output {
self.0 * rhs
}
}
impl Mul<UnitInterval<$float>> for $float {
type Output = $float;
#[inline(always)]
fn mul(self, rhs: UnitInterval<$float>) -> Self::Output {
self * rhs.0
}
}
impl Div for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn div(self, rhs: Self) -> Self::Output {
self.0 / rhs.0
}
}
impl Div<$float> for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn div(self, rhs: $float) -> Self::Output {
self.0 / rhs
}
}
impl Div<UnitInterval<$float>> for $float {
type Output = $float;
#[inline(always)]
fn div(self, rhs: UnitInterval<$float>) -> Self::Output {
self / rhs.0
}
}
impl Rem for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn rem(self, rhs: Self) -> Self::Output {
self.0 % rhs.0
}
}
impl Rem<$float> for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn rem(self, rhs: $float) -> Self::Output {
self.0 % rhs
}
}
impl Rem<UnitInterval<$float>> for $float {
type Output = $float;
#[inline(always)]
fn rem(self, rhs: UnitInterval<$float>) -> Self::Output {
self % rhs.0
}
}
impl Neg for UnitInterval<$float> {
type Output = $float;
#[inline(always)]
fn neg(self) -> Self::Output {
-self.0
}
}
impl UnitInterval<$float> {
#[inline]
pub fn abs(self) -> Self {
Self::from_inner(self.0.abs())
}
#[inline(always)]
pub fn signum(self) -> $float {
self.0.signum()
}
#[inline(always)]
pub fn copysign(self, sign: $float) -> $float {
self.0.copysign(sign)
}
#[inline(always)]
pub fn is_sign_positive(self) -> bool {
self.0.is_sign_positive()
}
#[inline(always)]
pub fn is_sign_negative(self) -> bool {
self.0.is_sign_negative()
}
#[inline(always)]
pub fn is_finite(self) -> bool {
self.0.is_finite()
}
#[inline(always)]
pub fn is_infinite(self) -> bool {
self.0.is_infinite()
}
#[inline(always)]
pub fn is_nan(self) -> bool {
self.0.is_nan()
}
#[inline(always)]
pub fn recip(self) -> $float {
self.0.recip()
}
}
#[cfg(any(test, feature = "std"))]
impl UnitInterval<$float> {
#[inline]
pub fn floor(self) -> Self {
Self::from_inner(self.0.floor())
}
#[inline]
pub fn ceil(self) -> Self {
Self::from_inner(self.0.ceil())
}
#[inline]
pub fn round(self) -> Self {
Self::from_inner(self.0.round())
}
#[inline]
pub fn trunc(self) -> Self {
Self::from_inner(self.0.trunc())
}
#[inline]
pub fn fract(self) -> Self {
Self::from_inner(self.0.fract())
}
#[inline(always)]
pub fn powi(self, n: i32) -> $float {
self.0.powi(n)
}
#[inline(always)]
pub fn powf(self, n: $float) -> $float {
self.0.powf(n)
}
#[inline(always)]
pub fn sqrt(self) -> Self {
Self::from_inner(self.0.sqrt())
}
#[inline(always)]
pub fn cbrt(self) -> Self {
Self::from_inner(self.0.cbrt())
}
#[inline(always)]
pub fn mul_add(self, a: $float, b: $float) -> $float {
self.0.mul_add(a, b)
}
#[inline(always)]
pub fn div_euclid(self, rhs: $float) -> $float {
self.0.div_euclid(rhs)
}
#[inline(always)]
pub fn rem_euclid(self, rhs: $float) -> $float {
self.0.rem_euclid(rhs)
}
#[inline(always)]
pub fn exp(self) -> $float {
self.0.exp()
}
#[inline(always)]
pub fn exp2(self) -> $float {
self.0.exp2()
}
#[inline(always)]
pub fn ln(self) -> $float {
self.0.ln()
}
#[inline(always)]
pub fn log(self, base: $float) -> $float {
self.0.log(base)
}
#[inline(always)]
pub fn log2(self) -> $float {
self.0.log2()
}
#[inline(always)]
pub fn log10(self) -> $float {
self.0.log10()
}
#[inline(always)]
pub fn sin(self) -> $float {
self.0.sin()
}
#[inline(always)]
pub fn cos(self) -> $float {
self.0.cos()
}
#[inline(always)]
pub fn tan(self) -> $float {
self.0.tan()
}
#[inline(always)]
pub fn sin_cos(self) -> ($float, $float) {
self.0.sin_cos()
}
#[inline(always)]
pub fn asin(self) -> $float {
self.0.asin()
}
#[inline(always)]
pub fn acos(self) -> $float {
self.0.acos()
}
#[inline(always)]
pub fn atan(self) -> Self {
Self::from_inner(self.0.atan())
}
#[inline(always)]
pub fn atan2(self, other: $float) -> $float {
self.0.atan2(other)
}
#[inline(always)]
pub fn sinh(self) -> $float {
self.0.sinh()
}
#[inline(always)]
pub fn cosh(self) -> $float {
self.0.cosh()
}
#[inline(always)]
pub fn tanh(self) -> Self {
Self::from_inner(self.0.tanh())
}
#[inline(always)]
pub fn asinh(self) -> Self {
Self::from_inner(self.0.asinh())
}
#[inline(always)]
pub fn acosh(self) -> $float {
self.0.acosh()
}
#[inline(always)]
pub fn atanh(self) -> $float {
self.0.atanh()
}
#[inline(always)]
pub fn hypot(self, other: $float) -> $float {
self.0.hypot(other)
}
}
};
}
impl_unit_interval_float!(f32);
impl_unit_interval_float!(f64);
impl From<UnitInterval<f32>> for f64 {
#[inline]
fn from(u: UnitInterval) -> Self {
u.0 as f64
}
}
impl From<UnitInterval<f32>> for UnitInterval<f64> {
#[inline]
fn from(u: UnitInterval<f32>) -> Self {
Self::from_inner(u.0 as f64)
}
}
impl From<UnitInterval<f64>> for UnitInterval<f32> {
#[inline]
fn from(u: UnitInterval<f64>) -> Self {
Self::from_inner(u.0 as f32)
}
}
impl<T: UnitIntervalFloat> Mul for UnitInterval<T> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self::from_inner(self.0 * rhs.0)
}
}
#[cfg(test)]
mod tests {
use super::UnitInterval;
#[test]
#[should_panic(expected = "UnitInterval invariant violated")]
fn test_configuration_enables_internal_assertions() {
UnitInterval::<f32>::from_inner(1.1);
}
#[cfg(feature = "rkyv")]
#[test]
fn rkyv_deserialization_rejects_invalid_archived_inner_value() {
let invalid = super::ArchivedUnitInterval(rkyv::Archived::<f32>::from_native(1.25));
assert!(rkyv::deserialize::<UnitInterval<f32>, rkyv::rancor::Error>(&invalid).is_err());
}
}