use std::ops::{Div, DivAssign, Mul, MulAssign};
use num::Complex;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::error::RootError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(C)]
pub struct Unit {
pub second: i32,
pub meter: i32,
pub kilogram: i32,
pub ampere: i32,
pub kelvin: i32,
pub mol: i32,
pub candela: i32,
}
impl From<[i32; 7]> for Unit {
fn from(array: [i32; 7]) -> Self {
return Unit {
second: array[0],
meter: array[1],
kilogram: array[2],
ampere: array[3],
kelvin: array[4],
mol: array[5],
candela: array[6],
};
}
}
impl From<Unit> for [i32; 7] {
fn from(value: Unit) -> Self {
return [
value.second,
value.meter,
value.kilogram,
value.ampere,
value.kelvin,
value.mol,
value.candela,
];
}
}
impl Unit {
pub fn powi(mut self, n: i32) -> Self {
self.second *= n;
self.meter *= n;
self.kilogram *= n;
self.ampere *= n;
self.kelvin *= n;
self.mol *= n;
self.candela *= n;
return self;
}
pub fn try_nthroot(mut self, n: i32) -> Result<Self, RootError> {
fn try_nthroot_inner(unit: &Unit, exp: i32, n: i32) -> Result<i32, RootError> {
if exp % n == 0 {
return Ok(exp / n);
} else {
return Err(RootError {
n,
unit: unit.clone(),
});
}
}
let init_exp = self.clone();
self.second = try_nthroot_inner(&init_exp, self.second, n)?;
self.meter = try_nthroot_inner(&init_exp, self.meter, n)?;
self.kilogram = try_nthroot_inner(&init_exp, self.kilogram, n)?;
self.ampere = try_nthroot_inner(&init_exp, self.ampere, n)?;
self.kelvin = try_nthroot_inner(&init_exp, self.kelvin, n)?;
self.mol = try_nthroot_inner(&init_exp, self.mol, n)?;
self.candela = try_nthroot_inner(&init_exp, self.candela, n)?;
return Ok(self);
}
pub fn is_dimensionless(&self) -> bool {
return self.second == 0
&& self.meter == 0
&& self.kilogram == 0
&& self.ampere == 0
&& self.kelvin == 0
&& self.mol == 0
&& self.candela == 0;
}
}
impl std::fmt::Display for Unit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"s^{} m^{} kg^{} A^{} K^{} mol^{} cd^{}",
self.second,
self.meter,
self.kilogram,
self.ampere,
self.kelvin,
self.mol,
self.candela
)
}
}
impl Mul for Unit {
type Output = Self;
fn mul(mut self, rhs: Self) -> Self::Output {
self.mul_assign(rhs);
return self;
}
}
impl MulAssign for Unit {
fn mul_assign(&mut self, rhs: Self) {
self.second += rhs.second;
self.meter += rhs.meter;
self.kilogram += rhs.kilogram;
self.ampere += rhs.ampere;
self.kelvin += rhs.kelvin;
self.mol += rhs.mol;
self.candela += rhs.candela;
}
}
impl Div for Unit {
type Output = Self;
fn div(mut self, rhs: Self) -> Self::Output {
self.div_assign(rhs);
return self;
}
}
impl DivAssign for Unit {
fn div_assign(&mut self, rhs: Self) {
self.second -= rhs.second;
self.meter -= rhs.meter;
self.kilogram -= rhs.kilogram;
self.ampere -= rhs.ampere;
self.kelvin -= rhs.kelvin;
self.mol -= rhs.mol;
self.candela -= rhs.candela;
}
}
pub trait UnitFromType {
fn unit_from_type() -> Unit {
return Unit::default();
}
}
impl UnitFromType for f64 {}
impl UnitFromType for Complex<f64> {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum PredefUnit {
None,
Time,
Length,
Mass,
ElectricCurrent,
Temperature,
AmountOfSubstance,
LuminousIntensity,
Area,
Volume,
ElectricVoltage,
Force,
Torque,
Power,
Energy,
Frequency,
Velocity,
AngularVelocity,
MagneticFlux,
MagneticFluxDensity,
MagneticFieldStrength,
Inductance,
ElectricConductance,
ElectricResistance,
ElectricConductivity,
ElectricResistivity,
}
impl From<PredefUnit> for Unit {
fn from(value: PredefUnit) -> Self {
match value {
PredefUnit::None => Default::default(),
PredefUnit::Time => Self {
second: 1,
meter: 0,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Length => Self {
second: 0,
meter: 1,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Mass => Self {
second: 0,
meter: 0,
kilogram: 1,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::ElectricCurrent => Self {
second: 0,
meter: 0,
kilogram: 0,
ampere: 1,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Temperature => Self {
second: 0,
meter: 0,
kilogram: 0,
ampere: 0,
kelvin: 1,
mol: 0,
candela: 0,
},
PredefUnit::AmountOfSubstance => Self {
second: 0,
meter: 0,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 1,
candela: 0,
},
PredefUnit::LuminousIntensity => Self {
second: 0,
meter: 0,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 1,
},
PredefUnit::Area => Self {
second: 0,
meter: 2,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Volume => Self {
second: 0,
meter: 3,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::ElectricVoltage => Self {
second: -3,
meter: 2,
kilogram: 1,
ampere: -1,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Force => Self {
second: -2,
meter: 1,
kilogram: 1,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Torque => Self {
second: -2,
meter: 2,
kilogram: 1,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Power => Self {
second: -3,
meter: 2,
kilogram: 1,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Energy => Self {
second: -2,
meter: 2,
kilogram: 1,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Frequency => Self {
second: -1,
meter: 0,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Velocity => Self {
second: -1,
meter: 1,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::AngularVelocity => Self {
second: -1,
meter: 0,
kilogram: 0,
ampere: 0,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::MagneticFlux => Self {
second: -2,
meter: 2,
kilogram: 1,
ampere: -1,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::MagneticFluxDensity => Self {
second: -2,
meter: 0,
kilogram: 1,
ampere: -1,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::MagneticFieldStrength => Self {
second: 0,
meter: -1,
kilogram: 0,
ampere: 1,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::Inductance => Self {
second: -2,
meter: 2,
kilogram: 1,
ampere: -2,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::ElectricConductance => Self {
second: 3,
meter: -2,
kilogram: -1,
ampere: 2,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::ElectricResistance => Self {
second: -3,
meter: 2,
kilogram: 1,
ampere: -2,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::ElectricConductivity => Self {
second: 3,
meter: -3,
kilogram: -1,
ampere: 2,
kelvin: 0,
mol: 0,
candela: 0,
},
PredefUnit::ElectricResistivity => Self {
second: -3,
meter: 3,
kilogram: 1,
ampere: -2,
kelvin: 0,
mol: 0,
candela: 0,
},
}
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::{PredefUnit, Unit};
use deserialize_untagged_verbose_error::DeserializeUntaggedVerboseError;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, SerializeStruct, Serializer};
impl Serialize for Unit {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("Unit", 7)?;
state.serialize_field("second", &self.second)?;
state.serialize_field("meter", &self.meter)?;
state.serialize_field("kilogram", &self.kilogram)?;
state.serialize_field("ampere", &self.ampere)?;
state.serialize_field("kelvin", &self.kelvin)?;
state.serialize_field("mol", &self.mol)?;
state.serialize_field("candela", &self.candela)?;
state.end()
}
}
#[derive(serde::Deserialize)]
struct UnitAlias {
second: i32,
meter: i32,
kilogram: i32,
ampere: i32,
kelvin: i32,
mol: i32,
candela: i32,
}
#[derive(DeserializeUntaggedVerboseError)]
enum UnitVariants {
Unit(UnitAlias),
PredefUnit(PredefUnit),
}
impl<'de> Deserialize<'de> for Unit {
fn deserialize<D>(deserializer: D) -> Result<Unit, D::Error>
where
D: Deserializer<'de>,
{
let variants = UnitVariants::deserialize(deserializer)?;
match variants {
UnitVariants::Unit(alias) => {
return Ok(Unit {
second: alias.second,
meter: alias.meter,
kilogram: alias.kilogram,
ampere: alias.ampere,
kelvin: alias.kelvin,
mol: alias.mol,
candela: alias.candela,
});
}
UnitVariants::PredefUnit(common_units) => {
return Ok(common_units.into());
}
}
}
}
}