use crate::{
NANOSECONDS_PER_CENTURY, NANOSECONDS_PER_MICROSECOND, NANOSECONDS_PER_MILLISECOND,
NANOSECONDS_PER_SECOND,
};
use super::{Duration, Freq, Frequencies, TimeUnits, Unit};
use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
#[cfg(not(feature = "std"))]
#[allow(unused_imports)] use num_traits::Float;
macro_rules! impl_ops_for_type {
($type:ident) => {
impl Mul<Unit> for $type {
type Output = Duration;
fn mul(self, q: Unit) -> Duration {
q * self
}
}
impl Mul<$type> for Freq {
type Output = Duration;
fn mul(self, q: $type) -> Duration {
let total_ns = match self {
Freq::GigaHertz => 1.0 / (q as f64),
Freq::MegaHertz => (NANOSECONDS_PER_MICROSECOND as f64) / (q as f64),
Freq::KiloHertz => NANOSECONDS_PER_MILLISECOND as f64 / (q as f64),
Freq::Hertz => (NANOSECONDS_PER_SECOND as f64) / (q as f64),
};
if total_ns.abs() < (i64::MAX as f64) {
Duration::from_truncated_nanoseconds(total_ns as i64)
} else {
Duration::from_total_nanoseconds(total_ns as i128)
}
}
}
impl Mul<Freq> for $type {
type Output = Duration;
fn mul(self, q: Freq) -> Duration {
q * self
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl Div<$type> for Duration {
type Output = Duration;
fn div(self, q: $type) -> Self::Output {
Duration::from_total_nanoseconds(
self.total_nanoseconds()
.saturating_div((q * Unit::Nanosecond).total_nanoseconds()),
)
}
}
impl Mul<Duration> for $type {
type Output = Duration;
fn mul(self, q: Self::Output) -> Self::Output {
q * self
}
}
impl TimeUnits for $type {}
impl Frequencies for $type {}
};
}
impl_ops_for_type!(f64);
impl_ops_for_type!(i64);
impl Mul<i64> for Duration {
type Output = Duration;
fn mul(self, q: i64) -> Self::Output {
Duration::from_total_nanoseconds(
self.total_nanoseconds()
.saturating_mul((q * Unit::Nanosecond).total_nanoseconds()),
)
}
}
impl Mul<f64> for Duration {
type Output = Duration;
fn mul(self, q: f64) -> Self::Output {
let mut p: i32 = 0;
let mut new_val: f64 = q;
let ten: f64 = 10.0;
#[cfg_attr(kani, kani::loop_invariant(p >= 0 && p <= 19))]
while new_val.is_finite() && (new_val.floor() - new_val).abs() >= f64::EPSILON && p < 19 {
p += 1;
new_val = q * ten.powi(p);
}
if !new_val.is_finite() {
if q.is_sign_negative() {
return Duration::MIN;
} else {
return Duration::MAX;
}
}
Duration::from_total_nanoseconds(
self.total_nanoseconds()
.saturating_mul(new_val as i128)
.saturating_div(10_i128.pow(p.try_into().unwrap())),
)
}
}
impl Add for Duration {
type Output = Duration;
#[allow(clippy::absurd_extreme_comparisons)]
fn add(mut self, mut rhs: Self) -> Duration {
self.normalize();
rhs.normalize();
match self.centuries.checked_add(rhs.centuries) {
None => {
if self.centuries < 0 {
return Self::MIN;
} else {
return Self::MAX;
}
}
Some(centuries) => {
self.centuries = centuries;
}
}
if self.centuries == Self::MIN.centuries && self.nanoseconds < Self::MIN.nanoseconds {
match self
.nanoseconds
.checked_sub(NANOSECONDS_PER_CENTURY - rhs.nanoseconds)
{
Some(nanos) => self.nanoseconds = nanos,
None => {
self.centuries += 1; self.nanoseconds = rhs.nanoseconds
}
}
} else {
match self.nanoseconds.checked_add(rhs.nanoseconds) {
Some(nanoseconds) => self.nanoseconds = nanoseconds,
None => {
let mut rhs = rhs;
rhs.normalize();
match self.centuries.checked_add(rhs.centuries) {
None => return Self::MAX,
Some(centuries) => self.centuries = centuries,
};
self.nanoseconds += rhs.nanoseconds;
}
}
}
self.normalize();
self
}
}
impl AddAssign for Duration {
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}
impl Sub for Duration {
type Output = Self;
fn sub(mut self, mut rhs: Self) -> Self {
self.normalize();
rhs.normalize();
match self.centuries.checked_sub(rhs.centuries) {
None => {
return Self::MIN;
}
Some(centuries) => {
self.centuries = centuries;
}
}
match self.nanoseconds.checked_sub(rhs.nanoseconds) {
None => {
match self.centuries.checked_sub(1) {
Some(centuries) => {
self.centuries = centuries;
self.nanoseconds += NANOSECONDS_PER_CENTURY - rhs.nanoseconds;
}
None => {
return Self::MIN;
}
};
}
Some(nanos) => self.nanoseconds = nanos,
};
self.normalize();
self
}
}
impl SubAssign for Duration {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Add<Unit> for Duration {
type Output = Self;
#[allow(clippy::identity_op)]
fn add(self, rhs: Unit) -> Self {
self + rhs * 1
}
}
impl AddAssign<Unit> for Duration {
#[allow(clippy::identity_op)]
fn add_assign(&mut self, rhs: Unit) {
*self = *self + rhs * 1;
}
}
impl Sub<Unit> for Duration {
type Output = Duration;
#[allow(clippy::identity_op)]
fn sub(self, rhs: Unit) -> Duration {
self - rhs * 1
}
}
impl SubAssign<Unit> for Duration {
#[allow(clippy::identity_op)]
fn sub_assign(&mut self, rhs: Unit) {
*self = *self - rhs * 1;
}
}
impl Neg for Duration {
type Output = Self;
fn neg(self) -> Self::Output {
if self == Self::MIN {
Self::MAX
} else if self == Self::MAX {
Self::MIN
} else {
let centuries = -i32::from(self.centuries) - 1;
let nanoseconds = NANOSECONDS_PER_CENTURY - self.nanoseconds;
Self::from_parts(
i16::try_from(centuries).expect("negated duration centuries must fit in i16"),
nanoseconds,
)
}
}
}