use crate::{length, time};
use core::fmt;
use core::marker::PhantomData;
use core::ops::{Add, Div, Mul, Sub};
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Speed<L, P>
where
L: length::Unit,
P: time::Unit,
{
pub quantity: f64,
length: PhantomData<L>,
period: PhantomData<P>,
}
impl<L, P> Add for Speed<L, P>
where
L: length::Unit,
P: time::Unit,
{
type Output = Self;
fn add(self, other: Self) -> Self::Output {
Self::new(self.quantity + other.quantity)
}
}
impl<L, P> Sub for Speed<L, P>
where
L: length::Unit,
P: time::Unit,
{
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Self::new(self.quantity - other.quantity)
}
}
impl<L, P> Mul<f64> for Speed<L, P>
where
L: length::Unit,
P: time::Unit,
{
type Output = Self;
fn mul(self, scalar: f64) -> Self::Output {
Self::new(self.quantity * scalar)
}
}
impl<L, P> Mul<Speed<L, P>> for f64
where
L: length::Unit,
P: time::Unit,
{
type Output = Speed<L, P>;
fn mul(self, other: Speed<L, P>) -> Self::Output {
Speed::new(self * other.quantity)
}
}
impl<L, P> Mul<Speed<L, P>> for i32
where
L: length::Unit,
P: time::Unit,
{
type Output = Speed<L, P>;
fn mul(self, other: Speed<L, P>) -> Self::Output {
Speed::new(f64::from(self) * other.quantity)
}
}
impl<L, P> Div<f64> for Speed<L, P>
where
L: length::Unit,
P: time::Unit,
{
type Output = Self;
fn div(self, scalar: f64) -> Self::Output {
Self::new(self.quantity / scalar)
}
}
impl<L, P> Speed<L, P>
where
L: length::Unit,
P: time::Unit,
{
pub fn new(quantity: f64) -> Self {
Speed::<L, P> {
quantity,
length: PhantomData,
period: PhantomData,
}
}
pub fn to<N, R>(self) -> Speed<N, R>
where
N: length::Unit,
R: time::Unit,
{
let factor = L::factor::<N>() / P::factor::<R>();
Speed::new(self.quantity * factor)
}
}
impl<L, P> fmt::Display for Speed<L, P>
where
L: length::Unit,
P: time::Unit,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.quantity.fmt(f)?;
write!(f, " {}/{}", L::LABEL, P::LABEL)
}
}
#[cfg(test)]
mod test {
extern crate alloc;
use super::super::length::*;
use super::super::time::*;
use super::*;
use alloc::format;
use alloc::string::ToString;
#[test]
fn speed_display() {
assert_eq!((23.4 * m / s).to_string(), "23.4 m/s");
assert_eq!((45.55 * mi / h).to_string(), "45.55 mi/h");
assert_eq!((25.1 * mm / d).to_string(), "25.1 mm/d");
assert_eq!(format!("{:.0}", (88.0 * ft / s).to::<mi, h>()), "60 mi/h");
}
#[test]
fn speed_to() {
assert_eq!((88.0 * ft / s).to(), 59.99999999999999 * mi / h);
assert_eq!((55.0 * mi / h).to(), 88.51392000000001 * km / h);
}
#[test]
fn speed_add() {
assert_eq!(10.1 * nm / s + 15.1 * nm / s, 25.2 * nm / s);
assert_eq!(20. * km / h + 30. * km / h, 50.0 * km / h);
}
#[test]
fn speed_sub() {
assert_eq!(55.6 * mm / d - 33.0 * mm / d, 22.6 * mm / d);
assert_eq!(10.0 * km / ms - 5.5 * km / ms, 4.5 * km / ms);
}
#[test]
fn speed_mul() {
assert_eq!((5.1 * In / s) * 2.0, 10.2 * In / s);
assert_eq!(3.0 * (10.5 * mi / us), 31.5 * mi / us);
assert_eq!((15.0 * m) * (3.0 / ds), 45.0 * m / ds);
assert_eq!((5.0 / s) * (3.0 * yd), 15.0 * yd / s);
}
#[test]
fn speed_div() {
assert_eq!(10.0 * mi / h, Speed::<mi, h>::new(10.0));
assert_eq!((45.5 * km) / (1.0 * h), Speed::<km, h>::new(45.5));
}
}