use std::collections::HashMap;
use std::fmt;
use std::ops::Add;
use std::ops::AddAssign;
use std::ops::Div;
use std::ops::DivAssign;
use std::ops::Mul;
use std::ops::MulAssign;
use std::ops::Sub;
use std::ops::SubAssign;
use std::time::Duration;
const NANOS_PER_SEC: f64 = 1_000_000_000.0;
const SECS_PER_MIN: u64 = 60;
const SECS_PER_HOUR: u64 = 3600;
const SECS_PER_DAY: u64 = 86400;
const SECS_PER_WEEK: u64 = 604_800;
const DAYS_PER_WEEK: u64 = 7;
const HOURS_PER_DAY: u64 = 24;
const MINS_PER_HOUR: u64 = 60;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ConstantRateDuration {
count: u64,
rate: u64,
}
impl ConstantRateDuration {
#[must_use]
pub fn new(count: u64, rate: u64) -> ConstantRateDuration {
ConstantRateDuration { count, rate }
}
#[must_use]
pub fn as_secs(&self) -> u64 {
self.count / self.rate
}
#[must_use]
pub fn as_mins(&self) -> u64 {
self.as_secs() / SECS_PER_MIN
}
#[must_use]
pub fn as_hours(&self) -> u64 {
self.as_secs() / SECS_PER_HOUR
}
#[must_use]
pub fn as_days(&self) -> u64 {
self.as_secs() / SECS_PER_DAY
}
#[must_use]
pub fn as_weeks(&self) -> u64 {
self.as_secs() / SECS_PER_WEEK
}
#[must_use]
pub fn subsec_samples(&self) -> u64 {
self.count % self.rate
}
#[must_use]
pub fn subsec_nanos(&self) -> u32 {
let nanos_as_f64 = self.subsec_secs() * NANOS_PER_SEC;
nanos_as_f64 as u32
}
#[must_use]
pub fn subsec_secs(&self) -> f64 {
self.subsec_samples() as f64 / self.rate as f64
}
#[must_use]
pub fn submin_secs(&self) -> u64 {
self.as_secs() % SECS_PER_MIN
}
#[must_use]
pub fn subhour_mins(&self) -> u64 {
self.as_mins() % MINS_PER_HOUR
}
#[must_use]
pub fn subday_hours(&self) -> u64 {
self.as_hours() % HOURS_PER_DAY
}
#[must_use]
pub fn subweek_days(&self) -> u64 {
self.as_days() % DAYS_PER_WEEK
}
#[must_use]
pub fn to_duration(&self) -> Duration {
Duration::new(self.as_secs(), self.subsec_nanos())
}
pub fn try_add(
self,
other: ConstantRateDuration,
) -> Result<ConstantRateDuration, MixedRateDuration> {
if self.rate == other.rate {
return Ok(ConstantRateDuration::new(
self.count
.checked_add(other.count)
.expect("overflow when adding ConstantRateDurations"),
self.rate,
));
}
let mut map: HashMap<u64, u64> = HashMap::with_capacity(2);
map.insert(self.rate, self.count);
map.insert(other.rate, other.count);
let mut mrd = MixedRateDuration {
duration: Duration::new(0, 0),
map,
};
mrd.update_duration();
Err(mrd)
}
pub fn try_add_assign(&mut self, crd: ConstantRateDuration) -> Result<(), MixedRateDuration> {
if self.rate == crd.rate {
self.count = self
.count
.checked_add(crd.count)
.expect("overflow when add-assigning ConstantRateDurations");
return Ok(());
}
let mut map: HashMap<u64, u64> = HashMap::with_capacity(2);
map.insert(self.rate, self.count);
map.insert(crd.rate, crd.count);
let mut mixed_rate_duration = MixedRateDuration {
duration: Duration::new(0, 0),
map,
};
mixed_rate_duration.update_duration();
Err(mixed_rate_duration)
}
pub fn try_saturating_sub(
self,
other: ConstantRateDuration,
) -> Result<ConstantRateDuration, Error> {
if self.rate == other.rate {
return Ok(ConstantRateDuration::new(
self.count.saturating_sub(other.count),
self.rate,
));
}
Err(Error::IncommensurateRates)
}
pub fn try_saturating_sub_assign(&mut self, crd: ConstantRateDuration) -> Result<(), Error> {
if self.rate == crd.rate {
self.count = self.count.saturating_sub(crd.count);
return Ok(());
}
Err(Error::IncommensurateRates)
}
}
impl fmt::Display for ConstantRateDuration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let hours = self.as_hours();
if hours < 10 {
return write!(
f,
"{:02}:{:02}:{:02};{}",
hours,
self.subhour_mins(),
self.submin_secs(),
self.subsec_samples()
);
}
write!(
f,
"{}:{:02}:{:02};{}",
hours,
self.subhour_mins(),
self.submin_secs(),
self.subsec_samples()
)
}
}
impl Div<u64> for ConstantRateDuration {
type Output = Self;
fn div(self, rhs: u64) -> Self {
ConstantRateDuration::new(
self.count
.checked_div(rhs)
.expect("divide by zero when dividing a ConstantRateDuration"),
self.rate,
)
}
}
impl DivAssign<u64> for ConstantRateDuration {
fn div_assign(&mut self, rhs: u64) {
self.count = self
.count
.checked_div(rhs)
.expect("divide by zero when divide-assigning a ConstantRateDuration");
}
}
impl Mul<u64> for ConstantRateDuration {
type Output = Self;
fn mul(self, rhs: u64) -> Self {
ConstantRateDuration::new(
self.count
.checked_mul(rhs)
.expect("overflow when multiplying ConstantRateDuration"),
self.rate,
)
}
}
impl MulAssign<u64> for ConstantRateDuration {
fn mul_assign(&mut self, rhs: u64) {
self.count = self
.count
.checked_mul(rhs)
.expect("overflow when multiply-assigning a ConstantRateDuration");
}
}
#[cfg(test)]
mod constant_rate_duration {
use super::*;
#[test]
fn new() {
let count: u64 = 15_345_873;
let rate: u64 = 44100;
let mut crd: ConstantRateDuration = ConstantRateDuration::new(count, rate);
assert_eq!(crd.count, count);
assert_eq!(crd.rate, rate);
let updated_count: u64 = 26_326_888;
crd.count = updated_count;
assert_eq!(crd.count, updated_count);
let updated_rate: u64 = 48000;
crd.rate = updated_rate;
assert_eq!(crd.rate, updated_rate);
}
#[test]
fn as_and_sub_time_unit_methods() {
let subsec_samples: u64 = 24_000;
let submin_secs: u64 = 32;
let subhour_mins: u64 = 23;
let subday_hours: u64 = 19;
let subweek_days: u64 = 4;
let weeks: u64 = 1;
let rate: u64 = 48_000;
let as_days = weeks * DAYS_PER_WEEK + subweek_days;
let as_hours = as_days * HOURS_PER_DAY + subday_hours;
let as_mins = as_hours * MINS_PER_HOUR + subhour_mins;
let as_secs = as_mins * SECS_PER_MIN + submin_secs;
let total_secs: u64 = weeks * SECS_PER_WEEK
+ subweek_days * SECS_PER_DAY
+ subday_hours * SECS_PER_HOUR
+ subhour_mins * SECS_PER_MIN
+ submin_secs;
let count: u64 = total_secs * rate + subsec_samples;
let crd: ConstantRateDuration = ConstantRateDuration::new(count, rate);
assert_eq!(crd.count, count);
assert_eq!(crd.rate, rate);
assert_eq!(crd.as_secs(), as_secs);
assert_eq!(crd.as_mins(), as_mins);
assert_eq!(crd.as_hours(), as_hours);
assert_eq!(crd.as_days(), as_days);
assert_eq!(crd.as_weeks(), weeks);
assert_eq!(crd.subsec_samples(), subsec_samples);
assert_eq!(crd.subsec_nanos(), 500_000_000);
assert_eq!(crd.subsec_secs(), 0.5);
assert_eq!(crd.submin_secs(), submin_secs);
assert_eq!(crd.subhour_mins(), subhour_mins);
assert_eq!(crd.subday_hours(), subday_hours);
assert_eq!(crd.subweek_days(), subweek_days);
let mut crd: ConstantRateDuration = ConstantRateDuration::new(47999, 48000);
assert_eq!(crd.as_secs(), 0);
assert_eq!(crd.subsec_samples(), 47999);
assert_eq!(crd.subsec_nanos(), 1_000_000_000 - 20834);
crd.count += 1;
assert_eq!(crd.as_secs(), 1);
assert_eq!(crd.subsec_samples(), 0);
assert_eq!(crd.subsec_nanos(), 0);
crd.count += 1;
assert_eq!(crd.as_secs(), 1);
assert_eq!(crd.subsec_samples(), 1);
assert_eq!(crd.subsec_nanos(), 20833);
}
#[test]
fn to_duration() {
let crd = ConstantRateDuration::new(48000, 48000);
assert_eq!(crd.to_duration(), Duration::new(1, 0));
let crd = ConstantRateDuration::new(48001, 48000);
assert_eq!(crd.to_duration(), Duration::new(1, 20833));
}
#[test]
fn try_add() {
let a = ConstantRateDuration::new(48000, 48000);
assert_eq!(a.as_secs(), 1);
assert_eq!(a.subsec_samples(), 0);
let b = ConstantRateDuration::new(1, 48000);
assert_eq!(b.as_secs(), 0);
assert_eq!(b.subsec_samples(), 1);
let result = a.try_add(b);
assert!(result.is_ok());
let c = result.unwrap();
assert_eq!(c.as_secs(), 1);
assert_eq!(c.subsec_samples(), 1);
let a = ConstantRateDuration::new(48000, 48000);
assert_eq!(a.as_secs(), 1);
assert_eq!(a.subsec_samples(), 0);
let b = ConstantRateDuration::new(96000, 96000);
assert_eq!(b.as_secs(), 1);
assert_eq!(b.subsec_samples(), 0);
let result = a.try_add(b);
assert!(result.is_err());
let c = result.unwrap_err();
assert_eq!(c.as_secs(), 2);
}
#[test]
#[should_panic(expected = "overflow when adding ConstantRateDurations")]
fn try_add_overflow() {
let a = ConstantRateDuration::new(u64::MAX, 48000);
let b = ConstantRateDuration::new(1, 48000);
let _c = a.try_add(b);
}
#[test]
fn try_add_assign() {
let mut a = ConstantRateDuration::new(48000, 48000);
let b = ConstantRateDuration::new(48000, 48000);
assert_eq!(a.as_secs(), 1);
assert_eq!(a.subsec_samples(), 0);
assert_eq!(b.as_secs(), 1);
assert_eq!(b.subsec_samples(), 0);
assert!(a.try_add_assign(b).is_ok());
assert_eq!(a.as_secs(), 2);
assert_eq!(a.subsec_samples(), 0);
let mut a = ConstantRateDuration::new(48000, 48000);
let b = ConstantRateDuration::new(96000, 96000);
assert_eq!(a.as_secs(), 1);
assert_eq!(a.subsec_samples(), 0);
assert_eq!(b.as_secs(), 1);
assert_eq!(b.subsec_samples(), 0);
let result = a.try_add_assign(b);
assert!(result.is_err());
let c = result.unwrap_err();
assert_eq!(c.as_secs(), 2);
}
#[test]
#[should_panic(expected = "overflow when add-assigning ConstantRateDurations")]
fn try_add_assign_overflow() {
let mut a = ConstantRateDuration::new(u64::MAX, 48000);
let b = ConstantRateDuration::new(1, 48000);
let _c = a.try_add_assign(b);
}
#[test]
fn display() {
let crd = ConstantRateDuration::new(48000 + 12345, 48000);
assert_eq!(crd.to_string(), "00:00:01;12345");
}
#[test]
fn div() {
let a = ConstantRateDuration::new(96002, 48000);
assert_eq!(a.as_secs(), 2);
assert_eq!(a.subsec_samples(), 2);
let b = a / 2;
assert_eq!(b.as_secs(), 1);
assert_eq!(b.subsec_samples(), 1);
}
#[test]
#[should_panic(expected = "divide by zero when dividing a ConstantRateDuration")]
fn div_by_zero() {
let a = ConstantRateDuration::new(48000, 48000);
let _ = a / 0;
}
#[test]
fn div_assign() {
let mut a = ConstantRateDuration::new(96002, 48000);
assert_eq!(a.as_secs(), 2);
assert_eq!(a.subsec_samples(), 2);
a /= 2;
assert_eq!(a.as_secs(), 1);
assert_eq!(a.subsec_samples(), 1);
}
#[test]
#[should_panic(expected = "divide by zero when divide-assigning a ConstantRateDuration")]
fn div_assign_by_zero() {
let mut a = ConstantRateDuration::new(48000, 48000);
a /= 0;
}
#[test]
fn mul() {
let a = ConstantRateDuration::new(48001, 48000);
assert_eq!(a.as_secs(), 1);
assert_eq!(a.subsec_samples(), 1);
let b = a * 2;
assert_eq!(b.as_secs(), 2);
assert_eq!(b.subsec_samples(), 2);
}
#[test]
#[should_panic(expected = "overflow when multiplying ConstantRateDuration")]
fn mul_overflow() {
let a = ConstantRateDuration::new(u64::MAX, 48000);
let _ = a * 2;
}
#[test]
fn mul_assign() {
let mut a = ConstantRateDuration::new(48001, 48000);
assert_eq!(a.as_secs(), 1);
assert_eq!(a.subsec_samples(), 1);
a *= 2;
assert_eq!(a.as_secs(), 2);
assert_eq!(a.subsec_samples(), 2);
}
#[test]
#[should_panic(expected = "overflow when multiply-assigning a ConstantRateDuration")]
fn mul_assign_overflow() {
let mut a = ConstantRateDuration::new(u64::MAX, 48000);
a *= 2;
}
#[test]
fn try_saturating_sub() {
let a = ConstantRateDuration::new(2, 48000);
let b = ConstantRateDuration::new(1, 48000);
assert_eq!(
a.try_saturating_sub(b),
Ok(ConstantRateDuration::new(1, 48000))
);
let a = ConstantRateDuration::new(10, 48000);
let b = ConstantRateDuration::new(1, 48000);
assert_eq!(
b.try_saturating_sub(a),
Ok(ConstantRateDuration::new(0, 48000))
);
let a = ConstantRateDuration::new(96000, 96000);
let b = ConstantRateDuration::new(48000, 48000);
assert_eq!(a.try_saturating_sub(b), Err(Error::IncommensurateRates));
}
#[test]
fn try_saturating_sub_assign() {
let mut a = ConstantRateDuration::new(10, 48000);
let b = ConstantRateDuration::new(2, 48000);
assert!(a.try_saturating_sub_assign(b).is_ok());
assert_eq!(a.subsec_samples(), 8);
let mut a = ConstantRateDuration::new(1, 48000);
let b = ConstantRateDuration::new(5, 48000);
assert!(a.try_saturating_sub_assign(b).is_ok());
assert_eq!(a.subsec_samples(), 0);
let a = ConstantRateDuration::new(96000, 96000);
let b = ConstantRateDuration::new(48000, 48000);
assert!(a.try_saturating_sub(b).is_err());
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct MixedRateDuration {
duration: Duration,
map: HashMap<u64, u64>,
}
impl MixedRateDuration {
#[must_use]
pub fn new() -> MixedRateDuration {
MixedRateDuration {
duration: Duration::new(0, 0),
map: HashMap::new(),
}
}
#[must_use]
pub fn as_secs(&self) -> u64 {
self.duration.as_secs()
}
#[must_use]
pub fn as_mins(&self) -> u64 {
self.as_secs() / SECS_PER_MIN
}
#[must_use]
pub fn as_hours(&self) -> u64 {
self.as_secs() / SECS_PER_HOUR
}
#[must_use]
pub fn as_days(&self) -> u64 {
self.as_secs() / SECS_PER_DAY
}
#[must_use]
pub fn as_weeks(&self) -> u64 {
self.as_secs() / SECS_PER_WEEK
}
#[must_use]
pub fn num_rates(&self) -> usize {
self.map.len()
}
#[must_use]
pub fn subsec_nanos(&self) -> u32 {
let nanos_as_f64 = self.subsec_secs() * NANOS_PER_SEC;
nanos_as_f64 as u32
}
#[must_use]
pub fn subsec_secs(&self) -> f64 {
f64::from(self.duration.subsec_nanos()) / NANOS_PER_SEC
}
#[must_use]
pub fn submin_secs(&self) -> u64 {
self.as_secs() % SECS_PER_MIN
}
#[must_use]
pub fn subhour_mins(&self) -> u64 {
self.as_mins() % MINS_PER_HOUR
}
#[must_use]
pub fn subday_hours(&self) -> u64 {
self.as_hours() % HOURS_PER_DAY
}
#[must_use]
pub fn subweek_days(&self) -> u64 {
self.as_days() % DAYS_PER_WEEK
}
#[must_use]
pub fn to_duration(&self) -> Duration {
self.duration
}
fn update_duration(&mut self) {
let mut duration = Duration::new(0, 0);
for (rate, count) in &self.map {
let crd = ConstantRateDuration::new(*count, *rate);
duration += crd.to_duration();
}
self.duration = duration;
}
}
impl Default for MixedRateDuration {
fn default() -> Self {
MixedRateDuration::new()
}
}
impl From<ConstantRateDuration> for MixedRateDuration {
fn from(crd: ConstantRateDuration) -> Self {
let mut map: HashMap<u64, u64> = HashMap::with_capacity(1);
map.insert(crd.rate, crd.count);
MixedRateDuration {
duration: crd.to_duration(),
map,
}
}
}
impl fmt::Display for MixedRateDuration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.num_rates() == 1 {
if let Some((rate, count)) = self.map.iter().next() {
let crd = ConstantRateDuration::new(*count, *rate);
return write!(
f,
"{:02}:{:02}:{:02};{}",
crd.as_hours(),
crd.subhour_mins(),
crd.submin_secs(),
crd.subsec_samples()
);
}
}
if self.submin_secs() < 10 {
return write!(
f,
"{:02}:{:02}:0{}",
self.as_hours(),
self.subhour_mins(),
self.submin_secs() as f64 + self.subsec_secs(),
);
}
return write!(
f,
"{:02}:{:02}:{:2.}",
self.as_hours(),
self.subhour_mins(),
self.submin_secs() as f64 + self.subsec_secs(),
);
}
}
impl Add<ConstantRateDuration> for MixedRateDuration {
type Output = MixedRateDuration;
fn add(self, crd: ConstantRateDuration) -> MixedRateDuration {
let mut result_map = self.map;
if let Some(current_count) = result_map.get_mut(&crd.rate) {
*current_count = current_count
.checked_add(crd.count)
.expect("overflow when adding a ConstantRateDuration to a MixedRateDuration");
} else {
result_map.insert(crd.rate, crd.count);
}
let mut result_mrd = MixedRateDuration {
duration: Duration::new(0, 0),
map: result_map,
};
result_mrd.update_duration();
result_mrd
}
}
impl Add<MixedRateDuration> for MixedRateDuration {
type Output = MixedRateDuration;
fn add(self, mrd: MixedRateDuration) -> MixedRateDuration {
let mut result_map = self.map;
for (rate, count) in &mrd.map {
if let Some(current_count) = result_map.get_mut(rate) {
*current_count = current_count
.checked_add(*count)
.expect("overflow when adding a ConstantRateDuration to a MixedRateDuration");
} else {
result_map.insert(*rate, *count);
}
}
let mut result_mrd = MixedRateDuration {
duration: Duration::new(0, 0),
map: result_map,
};
result_mrd.update_duration();
result_mrd
}
}
impl AddAssign<ConstantRateDuration> for MixedRateDuration {
fn add_assign(&mut self, crd: ConstantRateDuration) {
if let Some(current_count) = self.map.get_mut(&crd.rate) {
*current_count = current_count.checked_add(crd.count).expect(
"overflow when add-assigning a ConstantRateDuration to a MixedRateDuration",
);
} else {
self.map.insert(crd.rate, crd.count);
}
self.update_duration();
}
}
impl AddAssign<MixedRateDuration> for MixedRateDuration {
fn add_assign(&mut self, rhs: MixedRateDuration) {
for (rhs_rate, rhs_count) in &rhs.map {
if let Some(lhs_count) = self.map.get_mut(rhs_rate) {
*lhs_count = lhs_count
.checked_add(*rhs_count)
.expect("overflow when add-assigning MixedRateDurations");
} else {
self.map.insert(*rhs_rate, *rhs_count);
}
}
self.update_duration();
}
}
impl Div<u64> for MixedRateDuration {
type Output = Self;
fn div(self, rhs: u64) -> Self {
let mut result_map: HashMap<u64, u64> = HashMap::with_capacity(self.map.len());
for (rate, count) in &self.map {
result_map.insert(
*rate,
count
.checked_div(rhs)
.expect("divide by zero when dividing a MixedRateDuration"),
);
}
let mut result_mrd = MixedRateDuration {
duration: Duration::new(0, 0),
map: result_map,
};
result_mrd.update_duration();
result_mrd
}
}
impl DivAssign<u64> for MixedRateDuration {
fn div_assign(&mut self, rhs: u64) {
for count in self.map.values_mut() {
*count = count
.checked_div(rhs)
.expect("divide by zero when divide-assigning a MixedRateDuration");
}
self.update_duration();
}
}
impl Mul<u64> for MixedRateDuration {
type Output = Self;
fn mul(self, rhs: u64) -> Self {
let mut result_map: HashMap<u64, u64> = HashMap::with_capacity(self.map.len());
for (rate, count) in &self.map {
result_map.insert(
*rate,
count
.checked_mul(rhs)
.expect("overflow when multiplying a MixedRateDuration"),
);
}
let mut result_mrd = MixedRateDuration {
duration: Duration::new(0, 0),
map: result_map,
};
result_mrd.update_duration();
result_mrd
}
}
impl MulAssign<u64> for MixedRateDuration {
fn mul_assign(&mut self, rhs: u64) {
for count in self.map.values_mut() {
*count = count
.checked_mul(rhs)
.expect("overflow when multiply-assigning a MixedRateDuration");
}
self.update_duration();
}
}
impl Sub<ConstantRateDuration> for MixedRateDuration {
type Output = MixedRateDuration;
fn sub(self, crd: ConstantRateDuration) -> MixedRateDuration {
let mut result_map = self.map;
if let Some(current_count) = result_map.get_mut(&crd.rate) {
*current_count = current_count.saturating_sub(crd.count);
}
let mut result_mrd = MixedRateDuration {
duration: Duration::new(0, 0),
map: result_map,
};
result_mrd.update_duration();
result_mrd
}
}
impl Sub<MixedRateDuration> for MixedRateDuration {
type Output = MixedRateDuration;
fn sub(self, mrd: MixedRateDuration) -> MixedRateDuration {
let mut result_map = self.map;
for (rate, count) in &mrd.map {
if let Some(current_count) = result_map.get_mut(rate) {
*current_count = current_count.saturating_sub(*count);
}
}
let mut result_mrd = MixedRateDuration {
duration: Duration::new(0, 0),
map: result_map,
};
result_mrd.update_duration();
result_mrd
}
}
impl SubAssign<ConstantRateDuration> for MixedRateDuration {
fn sub_assign(&mut self, crd: ConstantRateDuration) {
if let Some(current_count) = self.map.get_mut(&crd.rate) {
*current_count = current_count.saturating_sub(crd.count);
self.update_duration();
}
}
}
impl SubAssign<MixedRateDuration> for MixedRateDuration {
fn sub_assign(&mut self, rhs: MixedRateDuration) {
for (rhs_rate, rhs_count) in &rhs.map {
if let Some(lhs_count) = self.map.get_mut(rhs_rate) {
*lhs_count = lhs_count.saturating_sub(*rhs_count);
}
}
self.update_duration();
}
}
#[cfg(test)]
mod mixed_rate_duration {
use super::*;
#[test]
fn new() {
let count: u64 = 12345;
let rate: u64 = 48000;
let mrd = MixedRateDuration::from(ConstantRateDuration::new(count, rate));
assert_eq!(mrd.map.len(), 1);
assert!(mrd.map.contains_key(&rate));
assert_eq!(mrd.map.get(&rate), Some(&count));
}
#[test]
fn as_and_sub_time_unit_methods() {
let subsec_samples: u64 = 24_000;
let submin_secs: u64 = 32;
let subhour_mins: u64 = 23;
let subday_hours: u64 = 19;
let subweek_days: u64 = 4;
let weeks: u64 = 1;
let rate: u64 = 48_000;
let as_days = weeks * DAYS_PER_WEEK + subweek_days;
let as_hours = as_days * HOURS_PER_DAY + subday_hours;
let as_mins = as_hours * MINS_PER_HOUR + subhour_mins;
let as_secs = as_mins * SECS_PER_MIN + submin_secs;
let total_secs: u64 = weeks * SECS_PER_WEEK
+ subweek_days * SECS_PER_DAY
+ subday_hours * SECS_PER_HOUR
+ subhour_mins * SECS_PER_MIN
+ submin_secs;
let count: u64 = total_secs * rate + subsec_samples;
let mrd = MixedRateDuration::from(ConstantRateDuration::new(count, rate));
assert_eq!(mrd.as_secs(), as_secs);
assert_eq!(mrd.as_mins(), as_mins);
assert_eq!(mrd.as_hours(), as_hours);
assert_eq!(mrd.as_days(), as_days);
assert_eq!(mrd.as_weeks(), weeks);
assert_eq!(mrd.subsec_nanos(), 500_000_000);
assert_eq!(mrd.subsec_secs(), 0.5);
assert_eq!(mrd.submin_secs(), submin_secs);
assert_eq!(mrd.subhour_mins(), subhour_mins);
assert_eq!(mrd.subday_hours(), subday_hours);
assert_eq!(mrd.subweek_days(), subweek_days);
let mut mrd = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
assert_eq!(mrd.as_secs(), 1);
assert_eq!(mrd.subsec_nanos(), 0);
mrd += ConstantRateDuration::new(96000 + 48000, 96000);
assert_eq!(mrd.map.len(), 2);
assert_eq!(mrd.as_secs(), 2);
assert_eq!(mrd.subsec_nanos(), 500_000_000);
}
#[test]
fn to_duration() {
let mrd = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
assert_eq!(mrd.to_duration(), Duration::new(1, 0));
}
#[test]
fn display() {
let mut mrd = MixedRateDuration::from(ConstantRateDuration::new(48000 + 24000, 48000));
assert_eq!(mrd.to_string(), "00:00:01;24000");
mrd += ConstantRateDuration::new(96000, 96000);
assert_eq!(mrd.map.len(), 2);
assert_eq!(mrd.as_secs(), 2);
assert_eq!(mrd.subsec_nanos(), 500_000_000);
assert_eq!(mrd.to_string(), "00:00:02.5");
mrd += ConstantRateDuration::new(441_000, 44100);
assert_eq!(mrd.map.len(), 3);
assert_eq!(mrd.as_secs(), 12);
assert_eq!(mrd.subsec_nanos(), 500_000_000);
assert_eq!(mrd.to_string(), "00:00:12.5");
}
#[test]
fn add() {
let a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let b = ConstantRateDuration::new(48000, 48000);
let c = a + b;
assert_eq!(c.to_string(), "00:00:02;0");
let a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let b = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let c = a + b;
assert_eq!(c.to_string(), "00:00:02;0");
let a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let b = MixedRateDuration::from(ConstantRateDuration::new(96000 + 48000, 96000));
let c = a + b;
assert_eq!(c.to_string(), "00:00:02.5");
}
#[test]
fn add_assign() {
let mut a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let b = ConstantRateDuration::new(48000, 48000);
a += b;
assert_eq!(a.to_string(), "00:00:02;0");
let c = MixedRateDuration::from(ConstantRateDuration::new(96000 + 48000, 96000));
a += c;
assert_eq!(a.to_string(), "00:00:03.5");
}
#[test]
fn div() {
let a = MixedRateDuration::from(ConstantRateDuration::new(96002, 48000));
assert_eq!(
a / 2,
MixedRateDuration::from(ConstantRateDuration::new(48001, 48000))
);
}
#[test]
#[should_panic(expected = "divide by zero when dividing a MixedRateDuration")]
fn div_by_zero() {
let a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let b = a / 0;
unreachable!(
"The above line should have caused a divide by zero error and b={} should be NaN.",
b
);
}
#[test]
fn div_assign() {
let mut a = MixedRateDuration::from(ConstantRateDuration::new(96002, 48000));
a /= 2;
assert_eq!(
a,
MixedRateDuration::from(ConstantRateDuration::new(48001, 48000))
);
}
#[test]
#[should_panic(expected = "divide by zero when divide-assigning a MixedRateDuration")]
fn div_assign_by_zero() {
let mut a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
a /= 0;
}
#[test]
fn mul() {
let a = MixedRateDuration::from(ConstantRateDuration::new(1, 48000));
let b = a * 2;
assert_eq!(
b,
MixedRateDuration::from(ConstantRateDuration::new(2, 48000))
);
let a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let b = MixedRateDuration::from(ConstantRateDuration::new(48000, 96000));
let c = (a + b) * 2;
assert_eq!(c.to_string(), "00:00:03");
}
#[test]
#[should_panic(expected = "overflow when multiplying a MixedRateDuration")]
fn mul_overflow() {
let a = MixedRateDuration::from(ConstantRateDuration::new(u64::MAX, 48000));
let b = a * 2;
unreachable!(
"The above line should have caused an overflow and b={} should have overflown.",
b
);
}
#[test]
fn mul_assign() {
let mut a = MixedRateDuration::from(ConstantRateDuration::new(1, 48000));
a *= 2;
assert_eq!(
a,
MixedRateDuration::from(ConstantRateDuration::new(2, 48000))
);
let a = MixedRateDuration::from(ConstantRateDuration::new(48000, 48000));
let b = MixedRateDuration::from(ConstantRateDuration::new(48000, 96000));
let mut c = a + b;
c *= 2;
assert_eq!(c.to_string(), "00:00:03");
}
#[test]
#[should_panic(expected = "overflow when multiply-assigning a MixedRateDuration")]
fn mul_assign_overflow() {
let mut a = MixedRateDuration::from(ConstantRateDuration::new(u64::MAX, 48000));
a *= 2;
}
#[test]
fn sub() {
let a = MixedRateDuration::from(ConstantRateDuration::new(100, 48000));
let b = ConstantRateDuration::new(25, 48000);
let c = a - b;
assert_eq!(
c,
MixedRateDuration::from(ConstantRateDuration::new(75, 48000))
);
let a = MixedRateDuration::from(ConstantRateDuration::new(100, 48000));
let b = MixedRateDuration::from(ConstantRateDuration::new(25, 48000));
let c = a - b;
assert_eq!(
c,
MixedRateDuration::from(ConstantRateDuration::new(75, 48000))
);
let mut a = MixedRateDuration::from(ConstantRateDuration::new(100, 48000));
a += ConstantRateDuration::new(42, 96000);
let mut b = MixedRateDuration::from(ConstantRateDuration::new(25, 48000));
b += ConstantRateDuration::new(123, 44100);
let c = a - b;
let mut expected_c = MixedRateDuration::from(ConstantRateDuration::new(75, 48000));
expected_c += ConstantRateDuration::new(42, 96000);
assert_eq!(c, expected_c);
}
#[test]
fn sub_assign() {
let mut a = MixedRateDuration::from(ConstantRateDuration::new(10, 48000));
let b = ConstantRateDuration::new(1, 48000);
a -= b;
assert_eq!(
a,
MixedRateDuration::from(ConstantRateDuration::new(9, 48000))
);
let mut c = MixedRateDuration::from(ConstantRateDuration::new(1, 96000));
c += ConstantRateDuration::new(2, 48000);
a -= c;
assert_eq!(
a,
MixedRateDuration::from(ConstantRateDuration::new(7, 48000))
);
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
IncommensurateRates,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Incommensurate sampling rates, the sampling rates must be equal."
)
}
}
impl std::error::Error for Error {}