use core::ops::{
Add,
Div,
Mul,
Sub,
};
use crate::{
math::{ConstOne, ConstZero},
};
#[cfg_attr(feature = "bitcode", derive(bitcode::Encode, bitcode::Decode))]
#[cfg_attr(
feature = "wincode",
derive(wincode::SchemaWrite, wincode::SchemaRead)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "c_compatible", repr(C))]
pub struct UniformRange<T: Copy + core::fmt::Debug> {
value: T,
}
#[allow(unused_imports)]
use crate::extensions::*;
impl<T> UniformRange<T>
where
T: TryIntoPatch<f64>
+ Copy
+ ConstZero
+ ConstOne
+ PartialOrd
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ core::fmt::Debug
+ Div<Output = T>,
f64: TryIntoPatch<T>,
{
#[must_use]
pub const fn zero() -> Self {
Self {
value: T::ZERO,
}
}
#[must_use]
pub fn one() -> Self {
Self {
value: Self::max_value(),
}
}
#[must_use]
pub const fn from_raw(value: T) -> Self {
Self {
value,
}
}
#[must_use]
pub fn from_f64(f: f64) -> Option<Self> {
let clamped = f.clamp(0.0, 1.0);
let max_val: f64 = (Self::max_value()).try_into_value()?;
let scaled = (clamped * max_val).round();
let value: T = (scaled).try_into_value()?;
Some(Self {
value,
})
}
#[must_use]
#[allow(clippy::cast_lossless)]
pub fn from_f32(f: f32) -> Option<Self> {
Self::from_f64(f as f64)
}
#[must_use]
#[allow(clippy::cast_lossless)]
pub fn from_f16(f: f16) -> Option<Self> {
Self::from_f64(f as f64)
}
#[must_use]
pub const fn raw(&self) -> T {
self.value
}
#[must_use]
pub fn to_f64(&self) -> Option<f64> {
let val: f64 = (self.value).try_into_value()?;
let max_val: f64 = (Self::max_value()).try_into_value()?;
Some(val / max_val)
}
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub fn to_f32(&self) -> Option<f32> {
self.to_f64().and_then(f32::try_from_value)
}
#[must_use]
#[allow(arithmetic_overflow)]
fn max_value() -> T {
let zero = T::ZERO;
let one = T::ONE;
{
let mut max_val = zero;
let mut temp = one;
loop {
let next = temp + temp;
if next < temp {
break;
}
temp = next;
}
while temp + one >= temp {
max_val = temp;
temp = temp + one;
}
max_val
}
}
#[must_use]
pub fn saturating_add(self, other: Self) -> Self {
let max_val = Self::max_value();
let result = if self.value > max_val - other.value {
max_val
} else {
self.value + other.value
};
Self::from_raw(result)
}
#[must_use]
pub fn saturating_sub(self, other: Self) -> Self {
let result = if self.value < other.value {
T::ZERO
} else {
self.value - other.value
};
Self::from_raw(result)
}
}
impl<T> Add for UniformRange<T>
where
T: Copy
+ ConstZero
+ ConstOne
+ core::fmt::Debug
+ PartialOrd
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ TryIntoPatch<f64>,
f64: TryIntoPatch<T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
self.saturating_add(rhs)
}
}
impl<T> Sub for UniformRange<T>
where
T: TryIntoPatch<f64>
+ Copy
+ ConstZero
+ ConstOne
+ PartialOrd
+ core::fmt::Debug
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>,
f64: TryIntoPatch<T>,
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
self.saturating_sub(rhs)
}
}
impl<T> core::fmt::Display for UniformRange<T>
where
T: TryIntoPatch<f64>
+ Copy
+ ConstZero
+ ConstOne
+ PartialOrd
+ core::fmt::Debug
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>,
f64: TryIntoPatch<T>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:.6?}", self.to_f64())
}
}
impl<T> core::fmt::Debug for UniformRange<T>
where
T: TryIntoPatch<f64>
+ Copy
+ ConstZero
+ ConstOne
+ PartialOrd
+ core::fmt::Debug
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>,
f64: TryIntoPatch<T>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"UnitRange(raw: {:?}, value: {:.6?})",
self.value,
self.to_f64()
)
}
}
pub type UnitRangeU8 = UniformRange<u8>;
pub type UnitRangeU16 = UniformRange<u16>;
pub type UnitRangeU32 = UniformRange<u32>;
pub type UnitRangeU64 = UniformRange<u64>;
pub type UnitRangeU128 = UniformRange<u128>;
pub type UnitRangeUsize = UniformRange<usize>;