use crate::scalar::{Exact, Real, Scalar, Transcendental};
use crate::unit::Unit;
use crate::unit_arithmetic::{QuantityDivOutput, UnitDiv, UnitMul, UnitSqrt};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::iter::Sum;
use core::marker::PhantomData;
use core::ops::*;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Quantity<U: Unit, S: Scalar = f64>(S, PhantomData<U>);
pub type Quantity64<U> = Quantity<U, f64>;
pub type Quantity32<U> = Quantity<U, f32>;
#[cfg(feature = "scalar-rational")]
pub type QuantityRational<U> = Quantity<U, num_rational::Rational64>;
pub type QuantityI8<U> = Quantity<U, i8>;
pub type QuantityI16<U> = Quantity<U, i16>;
pub type QuantityI32<U> = Quantity<U, i32>;
pub type QuantityI64<U> = Quantity<U, i64>;
pub type QuantityI128<U> = Quantity<U, i128>;
impl<U: Unit, S: Scalar> Quantity<U, S> {
#[inline]
pub const fn new(value: S) -> Self {
Self(value, PhantomData)
}
#[inline]
pub const fn value(self) -> S {
self.0
}
#[inline]
pub const fn value_ref(&self) -> &S {
&self.0
}
#[inline]
pub fn abs(self) -> Self {
Self::new(self.0.abs())
}
#[inline]
pub fn min(self, other: Self) -> Self {
Self::new(self.0.min(other.0))
}
#[inline]
pub fn max(self, other: Self) -> Self {
Self::new(self.0.max(other.0))
}
#[inline]
pub fn clamp(self, min_val: Self, max_val: Self) -> Self {
debug_assert!(
min_val.0 <= max_val.0,
"Quantity::clamp requires min_val <= max_val"
);
self.max(min_val).min(max_val)
}
#[inline]
pub fn mean(self, other: Self) -> Self {
let two = S::ONE + S::ONE;
let a = self.0;
let b = other.0;
if a == b {
return Self::new(a);
}
if (a >= S::ZERO) == (b >= S::ZERO) {
let ha = a / two;
let hb = b / two;
let ra = if ha == a { S::ZERO } else { a - ha * two };
let rb = if hb == b { S::ZERO } else { b - hb * two };
Self::new(ha + hb + (ra + rb) / two)
} else {
Self::new((a + b) / two)
}
}
#[inline]
pub const fn zero() -> Self {
Self::new(S::ZERO)
}
#[inline]
pub const fn one() -> Self {
Self::new(S::ONE)
}
#[inline]
pub fn erase_unit_raw(self) -> S {
self.0
}
}
impl<U: Unit, S: Real> Quantity<U, S> {
pub const NAN: Self = Self(S::NAN, PhantomData);
pub const INFINITY: Self = Self(S::INFINITY, PhantomData);
pub const NEG_INFINITY: Self = Self(S::NEG_INFINITY, PhantomData);
#[inline]
pub fn is_nan(self) -> bool {
self.0.is_nan()
}
#[inline]
pub fn is_infinite(self) -> bool {
self.0.is_infinite()
}
#[inline]
pub fn is_finite(self) -> bool {
self.0.is_finite()
}
#[inline]
pub fn to<T: Unit<Dim = U::Dim>>(self) -> Quantity<T, S> {
let ratio = S::from_f64(U::RATIO / T::RATIO);
Quantity::<T, S>::new(self.0 * ratio)
}
#[inline]
pub fn cast<T: Real>(self) -> Quantity<U, T> {
Quantity::new(T::from_f64(self.0.to_f64()))
}
#[inline]
pub fn signum(self) -> S {
self.0.signum()
}
#[inline]
pub fn scalar_sqrt(self) -> S {
self.0.sqrt()
}
#[inline]
pub fn floor(self) -> Self {
Self::new(self.0.floor())
}
#[inline]
pub fn ceil(self) -> Self {
Self::new(self.0.ceil())
}
#[inline]
pub fn round(self) -> Self {
Self::new(self.0.round())
}
#[inline]
pub fn trunc(self) -> Self {
Self::new(self.0.trunc())
}
#[inline]
pub fn fract(self) -> Self {
Self::new(self.0.fract())
}
#[inline]
pub fn eq_unit<V: Unit<Dim = U::Dim>>(self, other: &Quantity<V, S>) -> bool {
if U::RATIO >= V::RATIO {
self.0 == other.value() * S::from_f64(V::RATIO / U::RATIO)
} else {
self.0 * S::from_f64(U::RATIO / V::RATIO) == other.value()
}
}
#[inline]
pub fn cmp_unit<V: Unit<Dim = U::Dim>>(self, other: &Quantity<V, S>) -> Option<Ordering> {
if U::RATIO >= V::RATIO {
self.0
.partial_cmp(&(other.value() * S::from_f64(V::RATIO / U::RATIO)))
} else {
(self.0 * S::from_f64(U::RATIO / V::RATIO)).partial_cmp(&other.value())
}
}
}
impl<U: Unit, S: Exact> Quantity<U, S> {
#[inline]
pub fn to_lossy<T: Unit<Dim = U::Dim>>(self) -> Quantity<T, S> {
let ratio = U::RATIO / T::RATIO;
if ratio == 1.0 {
return Quantity::<T, S>::new(self.0);
}
let value_f64 = self.0.to_f64_approx();
Quantity::<T, S>::new(S::from_f64_approx(value_f64 * ratio))
}
#[inline]
pub fn checked_to_lossy<T: Unit<Dim = U::Dim>>(self) -> Option<Quantity<T, S>> {
let ratio = U::RATIO / T::RATIO;
if ratio == 1.0 {
return Some(Quantity::<T, S>::new(self.0));
}
let value_f64 = self.0.to_f64_approx();
S::checked_from_f64(value_f64 * ratio).map(Quantity::<T, S>::new)
}
}
impl<U: Unit + Copy> Quantity<U, f64> {
#[inline]
pub const fn const_add(self, other: Self) -> Self {
Self(self.0 + other.0, PhantomData)
}
#[inline]
pub const fn const_sub(self, other: Self) -> Self {
Self(self.0 - other.0, PhantomData)
}
#[inline]
pub const fn const_mul(self, rhs: f64) -> Self {
Self(self.0 * rhs, PhantomData)
}
#[inline]
pub const fn const_div(self, rhs: f64) -> Self {
Self(self.0 / rhs, PhantomData)
}
#[inline]
pub const fn to_const<T: Unit<Dim = U::Dim> + Copy>(self) -> Quantity<T, f64> {
Quantity::<T, f64>(self.0 * (U::RATIO / T::RATIO), PhantomData)
}
#[inline]
pub const fn min_const(self, other: Self) -> Self {
if self.0 < other.0 {
self
} else {
other
}
}
#[inline]
pub const fn max_const(self, other: Self) -> Self {
if self.0 > other.0 {
self
} else {
other
}
}
#[inline]
pub fn rem_euclid(self, rhs: f64) -> Self {
Self::new(self.0.rem_euclid(rhs))
}
}
impl<U: Unit + Copy> Quantity<U, f32> {
#[inline]
pub const fn const_add(self, other: Self) -> Self {
Self(self.0 + other.0, PhantomData)
}
#[inline]
pub const fn const_sub(self, other: Self) -> Self {
Self(self.0 - other.0, PhantomData)
}
#[inline]
pub const fn const_mul(self, rhs: f32) -> Self {
Self(self.0 * rhs, PhantomData)
}
#[inline]
pub const fn const_div(self, rhs: f32) -> Self {
Self(self.0 / rhs, PhantomData)
}
#[inline]
pub const fn to_const<T: Unit<Dim = U::Dim> + Copy>(self) -> Quantity<T, f32> {
Quantity::<T, f32>(self.0 * ((U::RATIO / T::RATIO) as f32), PhantomData)
}
#[inline]
pub const fn min_const(self, other: Self) -> Self {
if self.0 < other.0 {
self
} else {
other
}
}
#[inline]
pub const fn max_const(self, other: Self) -> Self {
if self.0 > other.0 {
self
} else {
other
}
}
}
macro_rules! impl_const_for_int {
($($t:ty),*) => { $(
impl<U: Unit + Copy> Quantity<U, $t> {
#[inline]
pub const fn const_add(self, other: Self) -> Self {
Self(self.0 + other.0, PhantomData)
}
#[inline]
pub const fn const_sub(self, other: Self) -> Self {
Self(self.0 - other.0, PhantomData)
}
#[inline]
pub const fn const_mul(self, rhs: $t) -> Self {
Self(self.0 * rhs, PhantomData)
}
#[inline]
pub const fn const_div(self, rhs: $t) -> Self {
Self(self.0 / rhs, PhantomData)
}
#[inline]
pub const fn min_const(self, other: Self) -> Self {
if self.0 < other.0 {
self
} else {
other
}
}
#[inline]
pub const fn max_const(self, other: Self) -> Self {
if self.0 > other.0 {
self
} else {
other
}
}
}
)* };
}
impl_const_for_int!(i8, i16, i32, i64, i128);
impl<U: Unit, S: Scalar> Add for Quantity<U, S> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self::new(self.0 + rhs.0)
}
}
impl<U: Unit, S: Scalar> AddAssign for Quantity<U, S> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl<U: Unit, S: Scalar> Sub for Quantity<U, S> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self::new(self.0 - rhs.0)
}
}
impl<U: Unit, S: Scalar> SubAssign for Quantity<U, S> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
impl<U: Unit, S: Scalar> Mul<S> for Quantity<U, S> {
type Output = Self;
#[inline]
fn mul(self, rhs: S) -> Self {
Self::new(self.0 * rhs)
}
}
impl<U: Unit, S: Scalar> MulAssign<S> for Quantity<U, S> {
#[inline]
fn mul_assign(&mut self, rhs: S) {
self.0 *= rhs;
}
}
impl<U: Unit, S: Scalar> Div<S> for Quantity<U, S> {
type Output = Self;
#[inline]
fn div(self, rhs: S) -> Self {
Self::new(self.0 / rhs)
}
}
impl<U: Unit, S: Scalar> DivAssign<S> for Quantity<U, S> {
#[inline]
fn div_assign(&mut self, rhs: S) {
self.0 /= rhs;
}
}
impl<U: Unit, S: Scalar> Neg for Quantity<U, S> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self::new(-self.0)
}
}
impl<U: Unit> Mul<Quantity<U, f64>> for f64 {
type Output = Quantity<U, f64>;
#[inline]
fn mul(self, rhs: Quantity<U, f64>) -> Self::Output {
rhs * self
}
}
impl<U: Unit> Mul<Quantity<U, f32>> for f32 {
type Output = Quantity<U, f32>;
#[inline]
fn mul(self, rhs: Quantity<U, f32>) -> Self::Output {
rhs * self
}
}
#[cfg(feature = "scalar-rational")]
impl<U: Unit> Mul<Quantity<U, num_rational::Rational64>> for num_rational::Rational64 {
type Output = Quantity<U, num_rational::Rational64>;
#[inline]
fn mul(self, rhs: Quantity<U, num_rational::Rational64>) -> Self::Output {
rhs * self
}
}
#[cfg(feature = "scalar-rational")]
impl<U: Unit> Mul<Quantity<U, num_rational::Rational32>> for num_rational::Rational32 {
type Output = Quantity<U, num_rational::Rational32>;
#[inline]
fn mul(self, rhs: Quantity<U, num_rational::Rational32>) -> Self::Output {
rhs * self
}
}
macro_rules! impl_int_commutative_mul {
($($t:ty),*) => { $(
impl<U: Unit> Mul<Quantity<U, $t>> for $t {
type Output = Quantity<U, $t>;
#[inline]
fn mul(self, rhs: Quantity<U, $t>) -> Self::Output {
rhs * self
}
}
)* };
}
impl_int_commutative_mul!(i8, i16, i32, i64, i128);
impl<U: Unit, S: Scalar + Rem<Output = S>> Rem<S> for Quantity<U, S> {
type Output = Self;
#[inline]
fn rem(self, rhs: S) -> Self {
Self::new(self.0 % rhs)
}
}
impl<U: Unit, S: Scalar + Rem<Output = S>> Rem for Quantity<U, S> {
type Output = Self;
#[inline]
fn rem(self, rhs: Self) -> Self {
Self::new(self.0 % rhs.0)
}
}
impl<U: Unit, S: Scalar> PartialOrd for Quantity<U, S> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<U: Unit, S: Scalar + Eq> Eq for Quantity<U, S> {}
impl<U: Unit, S: Scalar + Hash> Hash for Quantity<U, S> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<U: Unit, S: Scalar + Ord> Ord for Quantity<U, S> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<U: Unit, S: Scalar> From<S> for Quantity<U, S> {
#[inline]
fn from(value: S) -> Self {
Self::new(value)
}
}
impl<U: Unit, S: Scalar> Sum for Quantity<U, S> {
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::zero(), |acc, q| acc + q)
}
}
impl<'a, U: Unit, S: Scalar> Sum<&'a Quantity<U, S>> for Quantity<U, S> {
#[inline]
fn sum<I: Iterator<Item = &'a Quantity<U, S>>>(iter: I) -> Self {
iter.fold(Self::zero(), |acc, q| acc + *q)
}
}
impl<N: Unit, D: Unit, S: Scalar> Div<Quantity<D, S>> for Quantity<N, S>
where
N: UnitDiv<D>,
<N as UnitDiv<D>>::Output: QuantityDivOutput<S>,
{
type Output = <<N as UnitDiv<D>>::Output as QuantityDivOutput<S>>::Output;
#[inline]
fn div(self, rhs: Quantity<D, S>) -> Self::Output {
<<N as UnitDiv<D>>::Output as QuantityDivOutput<S>>::wrap(self.0 / rhs.0)
}
}
impl<A: Unit, B: Unit, S: Scalar> Mul<Quantity<B, S>> for Quantity<A, S>
where
A: UnitMul<B>,
{
type Output = Quantity<<A as UnitMul<B>>::Output, S>;
#[inline]
fn mul(self, rhs: Quantity<B, S>) -> Self::Output {
Quantity::new(self.0 * rhs.0)
}
}
impl<U, S> Quantity<U, S>
where
U: UnitSqrt,
S: Real,
{
#[inline]
pub fn sqrt(self) -> Quantity<U::Root, S> {
Quantity::new(self.0.sqrt())
}
}
impl<U, S> Quantity<U, S>
where
U: Unit<Dim = crate::dimension::Dimensionless>,
S: Transcendental,
{
#[inline]
pub fn asin_angle(&self) -> Quantity<crate::units::angular::Radian, S> {
Quantity::new(self.0.asin())
}
#[inline]
pub fn acos_angle(&self) -> Quantity<crate::units::angular::Radian, S> {
Quantity::new(self.0.acos())
}
#[inline]
pub fn atan_angle(&self) -> Quantity<crate::units::angular::Radian, S> {
Quantity::new(self.0.atan())
}
}