#[cfg(feature = "rand")]
pub mod rand;
mod qty_from;
use core::{
iter::Sum,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
use num_traits::{Inv, MulAdd, NumCast, Pow, real::Real, Signed, Zero};
use crate::{units::{traits::*, UnitAnon}, Value};
type ValueDefault = f64;
pub type QuantityAnon<D, V = ValueDefault> = Quantity<UnitAnon<D>, V>;
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Quantity<U: Unit, V: Value = ValueDefault> {
pub value: V,
pub unit: U,
}
impl<U: Unit, V: Value> Quantity<U, V> {
pub const fn new(unit: U, value: V) -> Self {
Self { value, unit }
}
pub fn set_base(&mut self) where
V: MulAssign,
{
self.set_unit(U::base())
}
pub fn set_unit(&mut self, unit: U) where
V: MulAssign,
{
self.value *= self.unit.scale_factor_v(unit).unwrap();
self.unit = unit;
}
pub fn almost_eq<W>(self, rhs: Quantity<W, V>, limit: V) -> bool where
V: Signed + PartialOrd,
W: Unit<Dim=U::Dim>,
{
(self - rhs).abs().value <= limit
}
pub fn with_anonymous(self) -> QuantityAnon<U::Dim, V> {
self.unit.anonymous().quantity(self.value)
}
pub fn with_base(self) -> Self { self.with_unit(U::base()) }
pub fn with_unit(self, unit: U) -> Self {
if unit == self.unit {
self
} else {
Self {
value: self.value_as(unit),
unit,
}
}
}
pub fn value_as<W: Unit<Dim=U::Dim>>(self, unit: W) -> V {
self.value * self.unit.scale_factor_v(unit).unwrap()
}
pub fn value_as_base(self) -> V {
self.value_as(U::base())
}
pub fn normalize(self) -> Self where
U: UnitStep,
V: Real,
{
if self.value.is_zero() {
self.with_base()
} else {
let limit = crate::_conv_i32(3);
let mut log: V = self.value.log10();
let mut unit: U = self.unit;
if log.is_sign_positive() {
while log >= limit {
let Some(next) = unit.step_up() else { break };
let log_rel = unit.scale_factor_v::<U, V>(next).unwrap().log10();
let log_new = log + log_rel;
if log_new >= V::zero() {
log = log_new;
unit = next;
} else {
break;
}
}
} else {
while log < V::zero() {
let Some(next) = unit.step_down() else { break };
let log_rel = unit.scale_factor_v::<U, V>(next).unwrap().log10();
let log_new = log + log_rel;
if log_new <= limit {
log = log_new;
unit = next;
} else {
break;
}
}
}
self.with_unit(unit)
}
}
pub fn into_si(self) -> Self where
U: UnitMixed,
{
let si: U = self.unit.to_si();
if self.unit == si {
self
} else {
self.with_unit(si)
}
}
#[cfg(feature = "simd")]
pub fn to_simd<const N: usize, S>(self) -> crate::simd::QtySimd<U, V, N, S> where
core::simd::LaneCount<N>: core::simd::SupportedLaneCount,
V: crate::simd::QtySimdValue,
S: crate::simd::QtySimdScale,
f64: num_traits::AsPrimitive<S>,
{
crate::simd::QtySimd::from_qty(self)
}
}
impl<U: Unit, V: Value> Quantity<U, V> {
pub fn squared(self) -> Quantity<<U as CanSquare>::Output, <V as Mul<V>>::Output> where
U: CanSquare,
{
Quantity {
value: self.value.clone() * self.value,
unit: self.unit.squared(),
}
}
pub fn cubed(self) -> Quantity<<U as CanCube>::Output, <V as Mul<V>>::Output> where
U: CanCube,
{
Quantity {
value: self.value.clone() * self.value.clone() * self.value,
unit: self.unit.cubed(),
}
}
pub fn pow<const E: i32>(self) -> Quantity<
U::Output,
<V as Pow<V>>::Output,
> where
U: CanPow<E>,
V: Pow<V>,
<V as Pow<V>>::Output: Value,
{
Quantity {
value: self.value.pow(crate::_conv_i32(E)),
unit: self.unit.pow(),
}
}
pub fn sqrt(self) -> Quantity<<U as CanSquareRoot>::Output, V> where
U: CanSquareRoot,
V: Real,
{
Quantity {
value: self.value.sqrt(),
unit: self.unit.sqrt(),
}
}
pub fn cbrt(self) -> Quantity<<U as CanCubeRoot>::Output, V> where
U: CanCubeRoot,
V: Real,
{
Quantity {
value: self.value.cbrt(),
unit: self.unit.cbrt(),
}
}
pub fn root<const D: i32>(self) -> Quantity<
U::Output,
<V as Pow<<V as Inv>::Output>>::Output,
> where
U: CanRoot<D>,
V: Inv + Pow<<V as Inv>::Output>,
<V as Pow<<V as Inv>::Output>>::Output: Value,
{
Quantity {
value: self.value.pow(crate::_conv_i32::<V>(D).inv()),
unit: self.unit.root(),
}
}
}
impl<U: Unit, V: Value> Quantity<U, V> {
pub fn value_into<X>(self) -> Quantity<U, X> where
V: Into<X>,
X: Value,
{
Quantity::new(self.unit, self.value.into())
}
pub fn value_try_into<X>(self) -> Result<Quantity<U, X>, V::Error> where
V: TryInto<X>,
X: Value,
{
Ok(Quantity::new(self.unit, self.value.try_into()?))
}
}
impl<U: Unit, V: Value> Quantity<U, V> {
pub fn convert<W: Unit>(self) -> Quantity<W, V> where
U: ConvertInto<W>,
{
self.convert_to(W::base())
}
pub fn convert_to<W: Unit>(self, unit: W) -> Quantity<W, V> where
U: ConvertInto<W>,
{
self.unit.conversion_into(unit).quantity(self.value)
}
pub fn cancel(self) -> V where
U: Cancel,
{
self.value * self.unit.cancel()
}
}
impl<U: Unit, V: Value> Quantity<crate::units::UnitPow<U, typenum::P1>, V> where
U::Dim: crate::dimension::CanDimPowType<typenum::P1>,
{
pub fn cancel_exponent(self) -> Quantity<U, V> {
self.unit.0.quantity(self.value)
}
}
impl<U: Unit, V: Value> Neg for Quantity<U, V> where
V: Neg, <V as Neg>::Output: Value,
{
type Output = Quantity<U, <V as Neg>::Output>;
fn neg(self) -> Self::Output {
Quantity {
value: self.value.neg(),
unit: self.unit,
}
}
}
impl<U: Unit, V: Value, W: Unit, X: Value> Add<Quantity<W, X>> for Quantity<U, V> where
W: ConvertInto<U>,
V: Add<X>, <V as Add<X>>::Output: Value,
{
type Output = Quantity<U, <V as Add<X>>::Output>;
fn add(self, rhs: Quantity<W, X>) -> Self::Output {
Quantity {
value: self.value + rhs.convert_to(self.unit).value,
unit: self.unit,
}
}
}
impl<U: Unit, V: Value, W: Unit, X: Value> Sub<Quantity<W, X>> for Quantity<U, V> where
W: ConvertInto<U>,
V: Sub<X>, <V as Sub<X>>::Output: Value,
{
type Output = Quantity<U, <V as Sub<X>>::Output>;
fn sub(self, rhs: Quantity<W, X>) -> Self::Output {
Quantity {
value: self.value - rhs.convert_to(self.unit).value,
unit: self.unit,
}
}
}
impl<U: Unit, V: Value, W: Unit, X: Value> AddAssign<Quantity<W, X>> for Quantity<U, V> where
W: ConvertInto<U>,
V: AddAssign<X>,
{
fn add_assign(&mut self, rhs: Quantity<W, X>) {
self.value += rhs.convert_to(self.unit).value;
}
}
impl<U: Unit, V: Value, W: Unit, X: Value> SubAssign<Quantity<W, X>> for Quantity<U, V> where
W: ConvertInto<U>,
V: SubAssign<X>,
{
fn sub_assign(&mut self, rhs: Quantity<W, X>) {
self.value -= rhs.convert_to(self.unit).value;
}
}
impl<U: Unit, V: Value, W: Unit, X: Value> Div<Quantity<W, X>> for Quantity<U, V> where
U: Div<W>, <U as Div<W>>::Output: Unit,
V: Div<X>, <V as Div<X>>::Output: Value,
{
type Output = Quantity<U::Output, <V as Div<X>>::Output>;
fn div(self, rhs: Quantity<W, X>) -> Self::Output {
Quantity {
value: self.value / rhs.value,
unit: self.unit / rhs.unit,
}
}
}
impl<U: Unit, V: Value, W: Unit, X: Value> Mul<Quantity<W, X>> for Quantity<U, V> where
U: Mul<W>, <U as Mul<W>>::Output: Unit,
V: Mul<X>, <V as Mul<X>>::Output: Value,
{
type Output = Quantity<U::Output, <V as Mul<X>>::Output>;
fn mul(self, rhs: Quantity<W, X>) -> Self::Output {
Quantity {
value: self.value * rhs.value,
unit: self.unit * rhs.unit,
}
}
}
impl<U: Unit, V: Value, X: Value> Div<X> for Quantity<U, V> where
V: Div<X>, <V as Div<X>>::Output: Value,
{
type Output = Quantity<U, <V as Div<X>>::Output>;
fn div(self, rhs: X) -> Self::Output {
Quantity {
value: self.value / rhs,
unit: self.unit,
}
}
}
impl<U: Unit, V: Value, X: Value> Mul<X> for Quantity<U, V> where
V: Mul<X>, <V as Mul<X>>::Output: Value,
{
type Output = Quantity<U, <V as Mul<X>>::Output>;
fn mul(self, rhs: X) -> Self::Output {
Quantity {
value: self.value * rhs,
unit: self.unit,
}
}
}
impl<U: Unit, V: Value, X: Value> DivAssign<X> for Quantity<U, V> where
V: DivAssign<X>,
{
fn div_assign(&mut self, rhs: X) {
self.value /= rhs;
}
}
impl<U: Unit, V: Value, X: Value> MulAssign<X> for Quantity<U, V> where
V: MulAssign<X>,
{
fn mul_assign(&mut self, rhs: X) {
self.value *= rhs;
}
}
impl<U: Unit, V: Value, W: Unit, X: Value> PartialEq<Quantity<W, X>>
for Quantity<U, V> where
W: ConvertInto<U>,
V: PartialEq<X>,
{
fn eq(&self, other: &Quantity<W, X>) -> bool {
let comp = other.clone().convert_to(self.unit);
self.value.eq(&comp.value)
}
}
impl<U: Unit, V: Value + Eq> Eq for Quantity<U, V> where
Quantity<U, V>: PartialEq,
{}
impl<U: Unit, V: Value, W: Unit, X: Value> PartialOrd<Quantity<W, X>>
for Quantity<U, V> where
W: ConvertInto<U>,
V: PartialOrd<X>,
{
fn partial_cmp(&self, other: &Quantity<W, X>) -> Option<core::cmp::Ordering> {
let comp = other.clone().convert_to(self.unit);
self.value.partial_cmp(&comp.value)
}
}
impl<U: Unit, V: Value + Ord> Ord for Quantity<U, V> where
U: ConvertInto<U>,
Quantity<U, V>: PartialOrd,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
let comp = other.clone().convert_to(self.unit);
self.value.cmp(&comp.value)
}
}
impl<U: Unit, V: Value> Sum for Quantity<U, V> {
fn sum<I: Iterator<Item=Self>>(mut iter: I) -> Self {
match iter.next() {
Some(qty) => iter.fold(qty, Add::add),
None => Self::zero(),
}
}
}
impl<U: Unit, V: Value + Signed> Quantity<U, V> {
pub fn abs(self) -> Self {
Self::new(self.unit, self.value.abs())
}
}
impl<U: Unit, V: Value + Real> Quantity<U, V> {
pub fn value_cast<X: Value>(self) -> Option<Quantity<U, X>> {
Some(Quantity::new(self.unit, NumCast::from(self.value)?))
}
pub fn ceil(self) -> Self {
Self::new(self.unit, self.value.ceil())
}
pub fn floor(self) -> Self {
Self::new(self.unit, self.value.floor())
}
pub fn round(self) -> Self {
Self::new(self.unit, self.value.round())
}
pub fn trunc(self) -> Self {
Self::new(self.unit, self.value.trunc())
}
}
impl<U: Unit, V: Value> Inv for Quantity<U, V> where
U: Inv, <U as Inv>::Output: Unit,
V: Inv, <V as Inv>::Output: Value,
{
type Output = Quantity<<U as Inv>::Output, <V as Inv>::Output>;
fn inv(self) -> Self::Output {
Quantity::new(self.unit.inv(), self.value.inv())
}
}
impl<U: Unit, V: Value> Zero for Quantity<U, V> {
fn zero() -> Self {
U::base().zero()
}
fn is_zero(&self) -> bool {
self.value.is_zero()
}
}
impl<U, V, UMul, VMul, UAdd, VAdd> MulAdd<
Quantity<UMul, VMul>,
Quantity<UAdd, VAdd>,
> for Quantity<U, V> where
U: Unit, UMul: Unit, UAdd: Unit,
V: Value, VMul: Value, VAdd: Value,
U: Mul<UMul>, <U as Mul<UMul>>::Output: Unit<Dim=UAdd::Dim>,
V: MulAdd<VMul, VAdd>, <V as MulAdd<VMul, VAdd>>::Output: Value,
{
type Output = Quantity<
<U as Mul<UMul>>::Output,
<V as MulAdd<VMul, VAdd>>::Output,
>;
fn mul_add(
self,
qty_mul: Quantity<UMul, VMul>,
qty_add: Quantity<UAdd, VAdd>,
) -> Self::Output {
let u_out = self.unit * qty_mul.unit;
let v_mul = qty_mul.value;
let v_add = qty_add.convert_to(u_out).value;
let v_out = self.value.mul_add(v_mul, v_add);
u_out.quantity(v_out)
}
}
macro_rules! impl_fmt {
($($fmt:path),+$(,)?) => {$(
impl<U: Unit, V: Value> $fmt for Quantity<U, V> where V: $fmt {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
<V as $fmt>::fmt(&self.value, f)?;
write!(f, " {}", self.unit)
}
}
)+};
}
impl_fmt!(
core::fmt::Display,
core::fmt::Octal,
core::fmt::LowerHex,
core::fmt::UpperHex,
core::fmt::Pointer,
core::fmt::Binary,
core::fmt::LowerExp,
core::fmt::UpperExp,
);