use crate::dimension::{DimDiv, DimMul, Dimension};
use crate::scalar::{Exact, Real, Scalar, Transcendental};
use crate::unit::{Per, Prod, Unit};
use core::cmp::Ordering;
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-decimal")]
pub type QuantityDecimal<U> = Quantity<U, rust_decimal::Decimal>;
#[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 mean(self, other: Self) -> Self {
Self::new((self.0 + other.0) / (S::ONE + S::ONE))
}
#[inline]
pub const fn zero() -> Self {
Self::new(S::ZERO)
}
#[inline]
pub const fn one() -> Self {
Self::new(S::ONE)
}
}
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 sqrt(self) -> Self {
Self::new(self.0.sqrt())
}
#[inline]
pub fn ceil(self) -> Self {
Self::new(self.0.ceil())
}
#[inline]
pub fn eq_unit<V: Unit<Dim = U::Dim>>(self, other: &Quantity<V, S>) -> bool {
self.0 == other.to::<U>().value()
}
#[inline]
pub fn cmp_unit<V: Unit<Dim = U::Dim>>(self, other: &Quantity<V, S>) -> Option<Ordering> {
self.0.partial_cmp(&other.to::<U>().value())
}
}
impl<U: Unit, S: Exact> Quantity<U, S> {
#[inline]
pub fn to_lossy<T: Unit<Dim = U::Dim>>(self) -> Quantity<T, S> {
let value_f64 = self.0.to_f64_approx();
let ratio = U::RATIO / T::RATIO;
Quantity::<T, S>::new(S::from_f64_approx(value_f64 * ratio))
}
}
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
}
}
}
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 as f32 / 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> 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-decimal")]
impl<U: Unit> Mul<Quantity<U, rust_decimal::Decimal>> for rust_decimal::Decimal {
type Output = Quantity<U, rust_decimal::Decimal>;
#[inline]
fn mul(self, rhs: Quantity<U, rust_decimal::Decimal>) -> 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> PartialEq<S> for Quantity<U, S> {
#[inline]
fn eq(&self, other: &S) -> bool {
self.0 == *other
}
}
impl<U: Unit, S: Scalar> PartialOrd<S> for Quantity<U, S> {
#[inline]
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
self.0.partial_cmp(other)
}
}
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 + 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<U: Unit> Sum<Quantity<U, f64>> for f64 {
#[inline]
fn sum<I: Iterator<Item = Quantity<U, f64>>>(iter: I) -> Self {
iter.fold(0.0, |acc, q| acc + q.value())
}
}
impl<'a, U: Unit> Sum<&'a Quantity<U, f64>> for f64 {
#[inline]
fn sum<I: Iterator<Item = &'a Quantity<U, f64>>>(iter: I) -> Self {
iter.fold(0.0, |acc, q| acc + q.value())
}
}
impl<N: Unit, D: Unit, S: Scalar> Div<Quantity<D, S>> for Quantity<N, S>
where
N::Dim: DimDiv<D::Dim>,
<N::Dim as DimDiv<D::Dim>>::Output: Dimension,
{
type Output = Quantity<Per<N, D>, S>;
#[inline]
fn div(self, rhs: Quantity<D, S>) -> Self::Output {
Quantity::new(self.0 / rhs.0)
}
}
impl<A: Unit, B: Unit, S: Scalar> Mul<Quantity<B, S>> for Quantity<A, S>
where
A::Dim: DimMul<B::Dim>,
<A::Dim as DimMul<B::Dim>>::Output: Dimension,
{
type Output = Quantity<Prod<A, B>, S>;
#[inline]
fn mul(self, rhs: Quantity<B, S>) -> Self::Output {
Quantity::<Prod<A, B>, S>::new(self.0 * rhs.0)
}
}
impl<U: Unit, S: Transcendental> Quantity<Per<U, U>, S>
where
U::Dim: DimDiv<U::Dim>,
<U::Dim as DimDiv<U::Dim>>::Output: Dimension,
{
#[inline]
pub fn asin(&self) -> S {
self.0.asin()
}
#[inline]
pub fn acos(&self) -> S {
self.0.acos()
}
#[inline]
pub fn atan(&self) -> S {
self.0.atan()
}
}