use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
use crate::Real;
use crate::model::dimension::Dimensioned;
use crate::model::quantity::{Quantity, QuantityMarker, QuantityTag};
use crate::model::unit::Unit;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Measure<U: Unit> {
pub(crate) value: Real,
_phantom: PhantomData<U>,
}
impl<U: Unit> Measure<U> {
pub fn new(value: impl Into<Real>) -> Self {
Self {
value: value.into(),
_phantom: PhantomData,
}
}
pub fn value(&self) -> Real {
self.value
}
pub fn from_q(q: U::Quantity) -> Self {
Self::new((q.raw_value() - U::OFFSET) / U::FACTOR)
}
pub fn into_q(self) -> U::Quantity {
U::Quantity::new(self.value * U::FACTOR + U::OFFSET)
}
pub fn convert<UOther>(&self) -> Measure<UOther>
where
UOther: Unit<Quantity = U::Quantity>,
{
Measure::from_q(self.into_q())
}
pub fn is_equal_to<UOther>(&self, other: &Measure<UOther>) -> bool
where
UOther: Unit<Quantity = U::Quantity>,
{
self.into_q() == other.into_q()
}
pub(crate) const fn new_const(value: Real) -> Self {
Self {
value,
_phantom: PhantomData,
}
}
pub(crate) const fn value_const(&self) -> Real {
self.value
}
}
impl<U: Unit> core::fmt::Display for Measure<U> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&self.value, f)?;
write!(f, " {}", U::ABBREV)
}
}
impl<U1, U2> Add<Measure<U2>> for Measure<U1>
where
U1: Unit,
U2: Unit<Quantity = U1::Quantity>,
U1::Quantity: Add<U2::Quantity, Output = U1::Quantity>,
{
type Output = Self;
fn add(self, rhs: Measure<U2>) -> Self::Output {
Self::from_q(self.into_q() + rhs.into_q())
}
}
impl<U, D, T> Add<Quantity<D, T>> for Measure<U>
where
U: Unit<Quantity = Quantity<D, T>>,
D: Dimensioned + Add<Output = D>,
T: QuantityTag,
{
type Output = Self;
fn add(self, rhs: Quantity<D, T>) -> Self::Output {
Self::from_q(self.into_q() + rhs)
}
}
impl<U1, U2> AddAssign<Measure<U2>> for Measure<U1>
where
U1: Unit,
U2: Unit<Quantity = U1::Quantity>,
U1::Quantity: Add<U2::Quantity, Output = U1::Quantity>,
{
fn add_assign(&mut self, rhs: Measure<U2>) {
self.value += rhs.convert::<U1>().value;
}
}
impl<U, D, T> AddAssign<Quantity<D, T>> for Measure<U>
where
U: Unit<Quantity = Quantity<D, T>>,
D: Dimensioned + Add<Output = D>,
T: QuantityTag,
{
fn add_assign(&mut self, rhs: Quantity<D, T>) {
self.value += rhs.as_measure::<U>().value;
}
}
impl<U1, U2> Sub<Measure<U2>> for Measure<U1>
where
U1: Unit,
U2: Unit<Quantity = U1::Quantity>,
U1::Quantity: Sub<U2::Quantity, Output = U1::Quantity>,
{
type Output = Self;
fn sub(self, rhs: Measure<U2>) -> Self::Output {
Self::from_q(self.into_q() - rhs.into_q())
}
}
impl<U, D, T> Sub<Quantity<D, T>> for Measure<U>
where
U: Unit<Quantity = Quantity<D, T>>,
D: Dimensioned + Sub<Output = D>,
T: QuantityTag,
{
type Output = Self;
fn sub(self, rhs: Quantity<D, T>) -> Self::Output {
Self::from_q(self.into_q() - rhs)
}
}
impl<U1, U2> SubAssign<Measure<U2>> for Measure<U1>
where
U1: Unit,
U2: Unit<Quantity = U1::Quantity>,
U1::Quantity: Sub<U2::Quantity, Output = U1::Quantity>,
{
fn sub_assign(&mut self, rhs: Measure<U2>) {
self.value -= rhs.convert::<U1>().value;
}
}
impl<U, D, T> SubAssign<Quantity<D, T>> for Measure<U>
where
U: Unit<Quantity = Quantity<D, T>>,
D: Dimensioned + Sub<Output = D>,
T: QuantityTag,
{
fn sub_assign(&mut self, rhs: Quantity<D, T>) {
self.value -= rhs.as_measure::<U>().value;
}
}
impl<U1: Unit, U2: Unit> Mul<U2> for Measure<U1>
where
U1::Quantity: Mul<U2::Quantity>,
{
type Output = <U1::Quantity as Mul<U2::Quantity>>::Output;
fn mul(self, _rhs: U2) -> Self::Output {
self.into_q() * U2::new(1.0).into_q()
}
}
impl<U1: Unit, U2: Unit> Mul<Measure<U2>> for Measure<U1>
where
U1::Quantity: Mul<U2::Quantity>,
{
type Output = <U1::Quantity as Mul<U2::Quantity>>::Output;
fn mul(self, rhs: Measure<U2>) -> Self::Output {
self.into_q() * rhs.into_q()
}
}
impl<U, D1, D2, T1, T2> Mul<Quantity<D2, T2>> for Measure<U>
where
U: Unit<Quantity = Quantity<D1, T1>>,
D1: Dimensioned + Mul<D2>,
D2: Dimensioned,
T1: QuantityTag,
T2: QuantityTag,
<D1 as Mul<D2>>::Output: Dimensioned,
{
type Output = <Quantity<D1, T1> as Mul<Quantity<D2, T2>>>::Output;
fn mul(self, rhs: Quantity<D2, T2>) -> Self::Output {
self.into_q() * rhs
}
}
impl<U1: Unit, U2: Unit> Div<U2> for Measure<U1>
where
U1::Quantity: Div<U2::Quantity>,
{
type Output = <U1::Quantity as Div<U2::Quantity>>::Output;
fn div(self, _rhs: U2) -> Self::Output {
self.into_q() / U2::new(1.0).into_q()
}
}
impl<U1: Unit, U2: Unit> Div<Measure<U2>> for Measure<U1>
where
U1::Quantity: Div<U2::Quantity>,
{
type Output = <U1::Quantity as Div<U2::Quantity>>::Output;
fn div(self, rhs: Measure<U2>) -> Self::Output {
self.into_q() / rhs.into_q()
}
}
impl<U, D1, D2, T1, T2> Div<Quantity<D2, T2>> for Measure<U>
where
U: Unit<Quantity = Quantity<D1, T1>>,
D1: Dimensioned + Div<D2>,
D2: Dimensioned,
T1: QuantityTag,
T2: QuantityTag,
<D1 as Div<D2>>::Output: Dimensioned,
{
type Output = <Quantity<D1, T1> as Div<Quantity<D2, T2>>>::Output;
fn div(self, rhs: Quantity<D2, T2>) -> Self::Output {
self.into_q() / rhs
}
}
impl<U: Unit> Mul<Real> for Measure<U> {
type Output = Self;
fn mul(self, scalar: Real) -> Self::Output {
Self::new(self.value * scalar)
}
}
impl<U: Unit> Mul<Measure<U>> for Real {
type Output = Measure<U>;
fn mul(self, rhs: Measure<U>) -> Self::Output {
Measure::new(self * rhs.value)
}
}
impl<U: Unit> MulAssign<Real> for Measure<U> {
fn mul_assign(&mut self, scalar: Real) {
self.value *= scalar;
}
}
impl<U: Unit> Div<Real> for Measure<U> {
type Output = Self;
fn div(self, scalar: Real) -> Self::Output {
Self::new(self.value / scalar)
}
}
impl<U: Unit> Div<Measure<U>> for Real
where
Real: Div<U::Quantity>,
{
type Output = <Real as Div<U::Quantity>>::Output;
fn div(self, rhs: Measure<U>) -> Self::Output {
self / rhs.into_q()
}
}
impl<U: Unit> DivAssign<Real> for Measure<U> {
fn div_assign(&mut self, scalar: Real) {
self.value /= scalar;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{common::assert_almost_equal, system::*};
use alloc::format;
#[test]
fn test_new_and_value() {
let distance = Measure::<Metre>::new(100.5);
assert_eq!(distance.value(), 100.5);
let mass = Measure::<Kilogram>::new(5i16); assert_eq!(mass.value(), 5.0);
let time = Measure::<Second>::new(2.34);
assert_eq!(time.value(), 2.34);
let temperature = Measure::<Kelvin>::new(-273.15);
assert_eq!(temperature.value(), -273.15);
}
#[test]
fn test_from_q_and_into_q() {
let original = Metre::new(100.0);
let quantity = original.into_q();
let restored = Measure::<Metre>::from_q(quantity);
assert_eq!(original.value(), restored.value());
assert_eq!(original, restored);
let mass_quantity = Mass::new(2.5);
let mass_kg = Measure::<Kilogram>::from_q(mass_quantity);
let mass_restored = mass_kg.into_q();
assert_eq!(mass_quantity.raw_value(), mass_restored.raw_value());
let mass_g = Measure::<Gram>::from_q(mass_quantity);
assert_eq!(mass_g.value(), 2500.0); }
#[test]
fn test_convert() {
let metres = Metre::new(1000.0);
let kilometres: Measure<Kilometre> = metres.convert();
assert_eq!(kilometres.value(), 1.0);
let centimetres: Measure<Centimetre> = metres.convert();
assert_eq!(centimetres.value(), 100000.0);
let kilograms = Kilogram::new(2.0);
let grams: Measure<Gram> = kilograms.convert();
assert_almost_equal!(grams.value(), 2000.0);
let minutes = Minute::new(2.0);
let seconds: Measure<Second> = minutes.convert();
assert_eq!(seconds.value(), 120.0); }
#[test]
fn test_is_equal_to() {
let metre = Metre::new(1.0);
let centimetres = Centimetre::new(100.0);
let millimetres = Millimetre::new(1000.0);
let kilometre = Kilometre::new(0.001);
assert!(metre.is_equal_to(¢imetres)); assert!(metre.is_equal_to(&millimetres)); assert!(metre.is_equal_to(&kilometre));
assert!(centimetres.is_equal_to(&metre));
assert!(kilometre.is_equal_to(&metre));
let different_metre = Metre::new(2.0);
assert!(!metre.is_equal_to(&different_metre));
let kilogram = Kilogram::new(1.0);
let grams = Gram::new(1000.0);
assert!(kilogram.is_equal_to(&grams)); }
#[test]
#[allow(clippy::clone_on_copy)]
fn test_derived_traits() {
let measure1 = Metre::new(42.0);
let measure2 = Metre::new(42.0);
let measure3 = Metre::new(24.0);
let debug_output = format!("{:?}", measure1);
assert!(debug_output.contains("Measure"));
assert!(debug_output.contains("42"));
let cloned = measure1.clone();
assert_eq!(measure1, cloned);
let copied = measure1;
assert_eq!(measure1, copied);
assert_eq!(measure1, measure2);
assert_ne!(measure1, measure3);
assert!(measure1 == measure2);
assert!(measure1 != measure3);
assert!(measure3 < measure1);
assert!(measure1 > measure3);
}
#[test]
fn test_const_functions_equivalence() {
let const_measure: Measure<Metre> = Measure::new_const(42.5);
let regular_measure = Metre::new(42.5);
assert_eq!(const_measure.value(), regular_measure.value());
assert_eq!(const_measure, regular_measure);
let const_value: Real = const_measure.value_const();
let regular_value = regular_measure.value();
assert_eq!(const_value, regular_value);
assert_eq!(const_value, 42.5);
let const_mass: Measure<Kilogram> = Measure::new_const(123.456);
let regular_mass = Kilogram::new(123.456);
assert_eq!(const_mass.value_const(), regular_mass.value());
assert_eq!(const_mass, regular_mass);
}
}