Change backing type from f64 to generic T

This commit is contained in:
Ben Bridle 2022-08-17 13:30:45 +12:00
parent 57e084f53a
commit 8a6c67519a
6 changed files with 77 additions and 101 deletions

30
src/bounds.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::Internal;
use crate::Proportion;
pub trait Bounded {
const MIN: Self;
const MAX: Self;
}
macro_rules! impl_bounded {
($type:ty) => {
impl Bounded for $type {
const MIN: Self = <$type>::MIN;
const MAX: Self = <$type>::MAX;
}
};
}
impl_bounded!(u8);
impl_bounded!(u16);
impl_bounded!(u32);
impl_bounded!(u64);
impl_bounded!(u128);
impl<T: Internal> Proportion<T> {
pub const MIN: Self = Proportion { value: T::MIN };
pub const MAX: Self = Proportion { value: T::MAX };
pub const ZERO: Self = Self::MIN;
pub const ONE: Self = Self::MAX;
}

View File

@ -1,9 +0,0 @@
use crate::Proportion;
impl Proportion {
pub const MIN: Self = Proportion { value: 0.0 };
pub const MAX: Self = Proportion { value: 1.0 };
pub const ZERO: Self = Self::MIN;
pub const ONE: Self = Self::MAX;
}

View File

@ -1,13 +0,0 @@
use crate::Proportion;
impl From<f64> for Proportion {
fn from(value: f64) -> Self {
Self::new(value)
}
}
impl From<f32> for Proportion {
fn from(value: f32) -> Self {
Self::new(value)
}
}

View File

@ -1,6 +1,8 @@
mod constants;
mod conversions;
mod bounds;
mod operator_overloads;
mod proportion;
pub use bounds::Bounded;
pub use proportion::Proportion;
pub trait Internal: Bounded + PartialEq + PartialOrd + Eq + Ord + Copy + Clone + std::fmt::Debug {}

View File

@ -1,27 +1,16 @@
use crate::Proportion;
impl std::ops::Neg for Proportion {
use crate::*;
use std::ops::*;
impl<T: Internal + Sub<Output = T>> Neg for Proportion<T> {
type Output = Self;
fn neg(mut self) -> Self::Output {
self.value = 1.0 - self.value;
self.value = T::MAX - self.value;
self
}
}
impl std::ops::Mul<f64> for Proportion {
type Output = f64;
fn mul(self, rhs: f64) -> f64 {
impl<T: Internal + Mul<Output = T>> Mul<T> for Proportion<T> {
type Output = T;
fn mul(self, rhs: T) -> T {
self.value * rhs
}
}
impl std::ops::Mul<Proportion> for usize {
type Output = usize;
fn mul(self, rhs: Proportion) -> usize {
let lhs = self as f64;
(lhs * rhs.value).round() as usize
}
}

View File

@ -1,83 +1,60 @@
#[derive(Copy, Clone, Debug)]
pub struct Proportion {
pub(crate) value: f64,
use crate::Internal;
use std::{cmp::*, ops::*};
#[derive(Copy, Clone)]
pub struct Proportion<T: Internal> {
pub(crate) value: T,
}
impl Proportion {
pub fn new<T: Into<f64>>(value: T) -> Self {
let mut new = Self {
value: value.into(),
};
new.clamp_to_bounds();
new
impl<T: Internal> Proportion<T> {
pub fn new(value: T) -> Self {
Self { value }
}
pub fn from_fraction<T: Into<f64>>(numerator: T, denominator: T) -> Self {
let denominator = denominator.into();
if denominator == 0.0 {
return Self::MAX;
}
let numerator = numerator.into();
Self::new(numerator / denominator)
}
pub fn value(&self) -> f64 {
self.value
}
pub fn set<T: Into<f64>>(&mut self, new_value: T) {
self.value = new_value.into();
self.clamp_to_bounds();
pub fn set(&mut self, new_value: T) {
self.value = new_value;
}
pub fn is_zero(&self) -> bool {
self.value.abs() < f64::EPSILON
self.value == T::MIN
}
pub fn is_one(&self) -> bool {
(self.value - 1.0).abs() < f64::EPSILON
}
self.value == T::MAX }
fn clamp_to_bounds(&mut self) {
self.value = if self.value > 1.0 {
1.0
} else if self.value < 0.0 {
0.0
} else {
self.value
}
}
}
impl AsRef<f64> for Proportion {
fn as_ref(&self) -> &f64 {
impl<T: Internal> AsRef<T> for Proportion<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl std::ops::Deref for Proportion {
type Target = f64;
fn deref(&self) -> &f64 {
impl<T: Internal> Deref for Proportion<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl std::cmp::PartialEq for Proportion {
fn eq(&self, other: &Proportion) -> bool {
self.value == other.value
}
}
impl std::cmp::PartialOrd for Proportion {
fn partial_cmp(&self, other: &Proportion) -> Option<std::cmp::Ordering> {
impl<T: Internal> PartialOrd for Proportion<T> {
fn partial_cmp(&self, other: &Proportion<T>) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl std::cmp::Eq for Proportion {}
impl std::cmp::Ord for Proportion {
fn cmp(&self, other: &Proportion) -> std::cmp::Ordering {
self.partial_cmp(&other).unwrap()
impl<T: Internal> Ord for Proportion<T> {
fn cmp(&self, other: &Proportion<T>) -> Ordering {
self.value.cmp(&other.value)
}
}
impl<T: Internal> PartialEq for Proportion<T> {
fn eq(&self, other: &Proportion<T>) -> bool {
self.value.eq(&other.value)
}
}
impl<T: Internal> Eq for Proportion<T> {}
impl<T: Internal> std::fmt::Debug for Proportion<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "Proportion {{ {:?} / {:?} }}", self.value, Self::MAX)
}
}