use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::time::Duration;
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub struct Rate {
value: f64,
}
impl Eq for Rate {}
impl Ord for Rate {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap_or(Ordering::Equal)
}
}
impl PartialOrd for Rate {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd for RateProportion {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
}
impl std::ops::Add for Rate {
type Output = Self;
fn add(self, other: Self) -> Self {
Rate {
value: self.value + other.value,
}
}
}
impl std::ops::Sub for Rate {
type Output = Self;
fn sub(self, other: Self) -> Self {
Rate {
value: self.value - other.value,
}
}
}
impl std::ops::AddAssign for Rate {
fn add_assign(&mut self, other: Self) {
self.value += other.value;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rate() {
let rate = Rate::new(100.0, Duration::from_secs(2));
assert_eq!(rate.per_second(), 50.0);
}
#[test]
fn test_rate_per_second() {
let rate = Rate::new_per_second(100.0);
assert_eq!(rate.per_second(), 100.0);
}
#[test]
fn test_add() {
let rate1 = Rate::new(100.0, Duration::from_secs(2));
let rate2 = Rate::new(200.0, Duration::from_secs(2));
let rate3 = rate1 + rate2;
assert_eq!(rate3.per_second(), 150.0);
}
#[test]
fn test_add_assign() {
let mut rate1 = Rate::new(100.0, Duration::from_secs(2));
let rate2 = Rate::new(200.0, Duration::from_secs(2));
rate1 += rate2;
assert_eq!(rate1.per_second(), 150.0);
}
#[test]
fn test_sub() {
let rate1 = Rate::new(100.0, Duration::from_secs(2));
let rate2 = Rate::new(200.0, Duration::from_secs(2));
let rate3 = rate2 - rate1;
assert_eq!(rate3.per_second(), 50.0);
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct RateProportion {
value: f64,
}
impl Rate {
pub fn new(value: f64, divisor: Duration) -> Self {
debug_assert!(value >= 0.0, "Value must be non-negative");
let divisor_as_secs = divisor.as_secs_f64();
debug_assert!(divisor_as_secs > 0.0, "Divisor must be greater than 0");
Rate {
value: value / divisor_as_secs,
}
}
pub const fn new_per_second(value: f64) -> Self {
Rate { value }
}
pub const fn per_second(&self) -> f64 {
self.value
}
pub fn proportion_of(&self, other: &Rate) -> RateProportion {
debug_assert!(
other.value > 0.0,
"Cannot calculate proportion of zero rate"
);
RateProportion {
value: self.value / other.value,
}
}
}
impl RateProportion {
pub(crate) fn new(value: f64) -> Self {
debug_assert!(
(0.0..=1.0).contains(&value),
"Proportion must be between 0 and 1"
);
RateProportion { value }
}
}