use num::Complex;
use num::complex::ComplexFloat;
use std::ops::{Div, DivAssign, Mul, MulAssign};
use crate::error::{ConversionError, NotConvertibleFromComplexF64, RootError, UnitsNotEqual};
use crate::unit::Unit;
#[cfg(feature = "from_str")]
pub mod from_str_impl;
#[cfg(feature = "serde")]
pub mod serde_impl;
#[cfg(feature = "uom")]
pub mod uom_impl;
mod private {
use super::Complex;
pub trait Sealed {}
impl Sealed for f64 {}
impl Sealed for Complex<f64> {}
}
pub trait F64RealOrComplex:
ComplexFloat<Real: std::fmt::Display>
+ std::ops::AddAssign
+ std::ops::SubAssign
+ std::fmt::Display
+ std::ops::Mul<f64, Output = Self>
+ std::ops::MulAssign
+ std::ops::MulAssign<f64>
+ std::ops::Div<f64, Output = Self>
+ std::ops::DivAssign
+ std::ops::DivAssign<f64>
+ std::fmt::Debug
+ private::Sealed
{
fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64>;
fn to_complexf64(self) -> Complex<f64>;
fn from_f64(value: f64) -> Self;
fn set_re_f64(&mut self, value: f64) -> ();
fn set_im_f64(&mut self, value: f64) -> ();
fn nth_root(self, n: i32) -> Self;
}
impl F64RealOrComplex for f64 {
fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64> {
if number.im() == 0.0 {
return Ok(number.re());
} else {
return Err(NotConvertibleFromComplexF64 {
source: number,
target_type: "f64",
});
}
}
fn to_complexf64(self) -> Complex<f64> {
return Complex::new(self, 0.0);
}
fn from_f64(value: f64) -> Self {
return value;
}
fn set_re_f64(&mut self, value: f64) -> () {
*self = value;
}
fn set_im_f64(&mut self, _: f64) -> () {
();
}
fn nth_root(self, n: i32) -> Self {
return self.powf(1.0 / n as f64);
}
}
impl F64RealOrComplex for Complex<f64> {
fn try_from_complexf64(number: Complex<f64>) -> Result<Self, NotConvertibleFromComplexF64> {
return Ok(Complex::<f64>::from(number));
}
fn to_complexf64(self) -> Complex<f64> {
return self;
}
fn from_f64(value: f64) -> Self {
return Complex::new(value, 0.0);
}
fn set_re_f64(&mut self, value: f64) -> () {
self.re = value;
}
fn set_im_f64(&mut self, value: f64) -> () {
self.im = value;
}
fn nth_root(self, n: i32) -> Self {
return self.powf(1.0 / n as f64);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[repr(C)]
pub struct DynQuantity<V: F64RealOrComplex> {
pub value: V,
pub unit: Unit,
}
impl<V: F64RealOrComplex> DynQuantity<V> {
pub fn new<U: Into<Unit>>(value: V, unit: U) -> Self {
return Self {
value,
unit: unit.into(),
};
}
pub fn try_add(&self, other: &Self) -> Result<Self, UnitsNotEqual> {
let mut output = self.clone();
output.try_add_assign(other)?;
return Ok(output);
}
pub fn try_add_assign(&mut self, other: &Self) -> Result<(), UnitsNotEqual> {
if self.unit == other.unit {
self.value += other.value;
return Ok(());
} else {
return Err(UnitsNotEqual(self.unit.clone(), other.unit.clone()));
}
}
pub fn try_sub(&self, other: &Self) -> Result<Self, UnitsNotEqual> {
let mut output = self.clone();
output.try_sub_assign(other)?;
return Ok(output);
}
pub fn try_sub_assign(&mut self, other: &Self) -> Result<(), UnitsNotEqual> {
if self.unit == other.unit {
self.value -= other.value;
return Ok(());
} else {
return Err(UnitsNotEqual(self.unit.clone(), other.unit.clone()));
}
}
pub fn powi(mut self, n: i32) -> Self {
self.value = self.value.powi(n);
self.unit = self.unit.powi(n);
return self;
}
pub fn try_nthroot(mut self, n: i32) -> Result<Self, RootError> {
self.unit = self.unit.try_nthroot(n)?;
self.value = self.value.nth_root(n);
return Ok(self);
}
}
impl<V: F64RealOrComplex> std::fmt::Display for DynQuantity<V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.value.im() == V::zero().im() {
write!(f, "{}", self.value.re())?;
} else {
write!(f, "({})", self.value)?;
}
if self.unit.second != 0 {
if self.unit.second == 1 {
write!(f, " s")?;
} else {
write!(f, " s^{}", self.unit.second)?;
}
}
if self.unit.meter != 0 {
if self.unit.meter == 1 {
write!(f, " m")?;
} else {
write!(f, " m^{}", self.unit.meter)?;
}
}
if self.unit.kilogram != 0 {
if self.unit.kilogram == 1 {
write!(f, " kg")?;
} else {
write!(f, " kg^{}", self.unit.kilogram)?;
}
}
if self.unit.ampere != 0 {
if self.unit.ampere == 1 {
write!(f, " A")?;
} else {
write!(f, " A^{}", self.unit.ampere)?;
}
}
if self.unit.kelvin != 0 {
if self.unit.kelvin == 1 {
write!(f, " K")?;
} else {
write!(f, " K^{}", self.unit.kelvin)?;
}
}
if self.unit.mol != 0 {
if self.unit.mol == 1 {
write!(f, " mol")?;
} else {
write!(f, " mol^{}", self.unit.mol)?;
}
}
if self.unit.candela != 0 {
if self.unit.candela == 1 {
write!(f, " cd")?;
} else {
write!(f, " cd^{}", self.unit.candela)?;
}
}
return Ok(());
}
}
impl<V: F64RealOrComplex> Mul for DynQuantity<V> {
type Output = Self;
fn mul(mut self, rhs: Self) -> Self::Output {
self.mul_assign(rhs);
return self;
}
}
impl<V: F64RealOrComplex> Mul<f64> for DynQuantity<V> {
type Output = Self;
fn mul(mut self, rhs: f64) -> Self::Output {
self.mul_assign(rhs);
return self;
}
}
impl<V: F64RealOrComplex> MulAssign for DynQuantity<V> {
fn mul_assign(&mut self, rhs: Self) {
self.value *= rhs.value;
self.unit *= rhs.unit;
}
}
impl<V: F64RealOrComplex> MulAssign<f64> for DynQuantity<V> {
fn mul_assign(&mut self, rhs: f64) {
self.value *= rhs;
}
}
impl<V: F64RealOrComplex> Div for DynQuantity<V> {
type Output = Self;
fn div(mut self, rhs: Self) -> Self::Output {
self.div_assign(rhs);
return self;
}
}
impl<V: F64RealOrComplex> Div<f64> for DynQuantity<V> {
type Output = Self;
fn div(mut self, rhs: f64) -> Self::Output {
self.div_assign(rhs);
return self;
}
}
impl<V: F64RealOrComplex> DivAssign for DynQuantity<V> {
fn div_assign(&mut self, rhs: Self) {
if self.value.is_infinite() {
let mut value = self.value / rhs.value;
if value.re().is_nan() {
value.set_re_f64(0.0);
}
if value.im().is_nan() {
value.set_im_f64(0.0);
}
self.value = value;
} else {
self.value /= rhs.value;
}
self.unit /= rhs.unit;
}
}
impl<V: F64RealOrComplex> DivAssign<f64> for DynQuantity<V> {
fn div_assign(&mut self, rhs: f64) {
if self.value.is_infinite() {
let mut value: V = self.value / rhs;
if value.re().is_nan() {
value.set_re_f64(0.0);
}
if value.im().is_nan() {
value.set_im_f64(0.0);
}
self.value = value;
} else {
self.value /= rhs;
}
}
}
impl TryFrom<DynQuantity<Complex<f64>>> for DynQuantity<f64> {
type Error = NotConvertibleFromComplexF64;
fn try_from(quantity: DynQuantity<Complex<f64>>) -> Result<Self, Self::Error> {
if quantity.value.im() == 0.0 {
return Ok(DynQuantity::new(quantity.value.re(), quantity.unit));
} else {
return Err(NotConvertibleFromComplexF64 {
source: quantity.value,
target_type: "f64",
});
}
}
}
impl From<DynQuantity<f64>> for DynQuantity<Complex<f64>> {
fn from(value: DynQuantity<f64>) -> Self {
return DynQuantity::new(Complex::new(value.value, 0.0), value.unit);
}
}
impl From<&DynQuantity<f64>> for DynQuantity<f64> {
fn from(value: &DynQuantity<f64>) -> Self {
return DynQuantity::new(value.value, value.unit);
}
}
impl From<&DynQuantity<f64>> for DynQuantity<Complex<f64>> {
fn from(value: &DynQuantity<f64>) -> Self {
return DynQuantity::new(Complex::new(value.value, 0.0), value.unit);
}
}
impl From<&DynQuantity<Complex<f64>>> for DynQuantity<Complex<f64>> {
fn from(value: &DynQuantity<Complex<f64>>) -> Self {
return DynQuantity::new(value.value, value.unit);
}
}
impl From<f64> for DynQuantity<f64> {
fn from(value: f64) -> Self {
return DynQuantity::new(value, Unit::default());
}
}
impl From<f64> for DynQuantity<Complex<f64>> {
fn from(value: f64) -> Self {
return DynQuantity::new(Complex::new(value, 0.0), Unit::default());
}
}
impl From<Complex<f64>> for DynQuantity<Complex<f64>> {
fn from(value: Complex<f64>) -> Self {
return DynQuantity::new(value, Unit::default());
}
}
impl From<&f64> for DynQuantity<f64> {
fn from(value: &f64) -> Self {
return DynQuantity::new(value.clone(), Unit::default());
}
}
impl From<&Complex<f64>> for DynQuantity<Complex<f64>> {
fn from(value: &Complex<f64>) -> Self {
return DynQuantity::new(value.clone(), Unit::default());
}
}
impl TryFrom<DynQuantity<f64>> for f64 {
type Error = ConversionError;
fn try_from(quantity: DynQuantity<f64>) -> Result<Self, Self::Error> {
if quantity.unit.is_dimensionless() {
return Ok(quantity.value);
} else {
return Err(ConversionError::UnitMismatch {
expected: Unit::default(),
found: quantity.unit,
});
}
}
}
impl TryFrom<DynQuantity<f64>> for Complex<f64> {
type Error = ConversionError;
fn try_from(quantity: DynQuantity<f64>) -> Result<Self, Self::Error> {
let re = f64::try_from(quantity)?;
return Ok(Complex::new(re, 0.0));
}
}
impl TryFrom<DynQuantity<Complex<f64>>> for Complex<f64> {
type Error = ConversionError;
fn try_from(quantity: DynQuantity<Complex<f64>>) -> Result<Self, Self::Error> {
if quantity.unit.is_dimensionless() {
return Ok(quantity.value);
} else {
return Err(ConversionError::UnitMismatch {
expected: Unit::default(),
found: quantity.unit,
});
}
}
}
impl TryFrom<DynQuantity<Complex<f64>>> for f64 {
type Error = ConversionError;
fn try_from(quantity: DynQuantity<Complex<f64>>) -> Result<Self, Self::Error> {
let c = Complex::<f64>::try_from(quantity)?;
if c.im == 0.0 {
return Ok(c.re);
} else {
return Err(ConversionError::NotConvertibleFromComplexF64(
NotConvertibleFromComplexF64 {
source: c,
target_type: "f64",
},
));
}
}
}
pub fn to_vec<V: F64RealOrComplex>(quantity_slice: &[DynQuantity<V>]) -> Vec<V> {
let mut output: Vec<V> = Vec::with_capacity(quantity_slice.len());
for element in quantity_slice.iter() {
output.push(element.value)
}
return output;
}
pub fn to_vec_checked(quantity_slice: &[DynQuantity<f64>]) -> Result<Vec<f64>, ConversionError> {
let mut output: Vec<f64> = Vec::with_capacity(quantity_slice.len());
if let Some(first_element) = quantity_slice.first() {
let first_elem_unit = first_element.unit;
for element in quantity_slice.iter() {
if element.unit != first_elem_unit {
return Err(ConversionError::UnitMismatch {
expected: first_elem_unit,
found: element.unit,
});
}
output.push(element.value)
}
}
return Ok(output);
}