commit 57e084f53a28e9905d1d2249c257f6922a49c911 Author: Ben Bridle Date: Wed Aug 17 12:09:35 2022 +1200 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..869df07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..46e3b6a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "proportion" +version = "1.0.0" +authors = ["Ben Bridle "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..d5fd229 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,9 @@ +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; +} diff --git a/src/conversions.rs b/src/conversions.rs new file mode 100644 index 0000000..a95d3dc --- /dev/null +++ b/src/conversions.rs @@ -0,0 +1,13 @@ +use crate::Proportion; + +impl From for Proportion { + fn from(value: f64) -> Self { + Self::new(value) + } +} + +impl From for Proportion { + fn from(value: f32) -> Self { + Self::new(value) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cf77a52 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,6 @@ +mod constants; +mod conversions; +mod operator_overloads; +mod proportion; + +pub use proportion::Proportion; diff --git a/src/operator_overloads.rs b/src/operator_overloads.rs new file mode 100644 index 0000000..4b5c442 --- /dev/null +++ b/src/operator_overloads.rs @@ -0,0 +1,27 @@ +use crate::Proportion; + +impl std::ops::Neg for Proportion { + type Output = Self; + + fn neg(mut self) -> Self::Output { + self.value = 1.0 - self.value; + self + } +} + +impl std::ops::Mul for Proportion { + type Output = f64; + + fn mul(self, rhs: f64) -> f64 { + self.value * rhs + } +} + +impl std::ops::Mul for usize { + type Output = usize; + + fn mul(self, rhs: Proportion) -> usize { + let lhs = self as f64; + (lhs * rhs.value).round() as usize + } +} diff --git a/src/proportion.rs b/src/proportion.rs new file mode 100644 index 0000000..ad64e61 --- /dev/null +++ b/src/proportion.rs @@ -0,0 +1,83 @@ +#[derive(Copy, Clone, Debug)] +pub struct Proportion { + pub(crate) value: f64, +} + +impl Proportion { + pub fn new>(value: T) -> Self { + let mut new = Self { + value: value.into(), + }; + new.clamp_to_bounds(); + new + } + + pub fn from_fraction>(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>(&mut self, new_value: T) { + self.value = new_value.into(); + self.clamp_to_bounds(); + } + + pub fn is_zero(&self) -> bool { + self.value.abs() < f64::EPSILON + } + + pub fn is_one(&self) -> bool { + (self.value - 1.0).abs() < f64::EPSILON + } + + 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 for Proportion { + fn as_ref(&self) -> &f64 { + &self.value + } +} + +impl std::ops::Deref for Proportion { + type Target = f64; + fn deref(&self) -> &f64 { + &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 { + 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() + } +}