diman_unit_system 0.5.1

Internal procedural macros for diman.
Documentation
use std::collections::HashMap;

use diman_lib::{dimension_exponent::DimensionExponent, magnitude::Magnitude};
use proc_macro2::Ident;

use crate::{
    types::expression::MulDiv,
    types::{base_dimension::BaseDimension, Exponent},
};

#[derive(Clone)]
pub struct BaseDimensions {
    fields: HashMap<BaseDimension, Exponent>,
}

#[derive(Clone)]
pub struct DimensionsAndMagnitude {
    pub dimensions: BaseDimensions,
    pub magnitude: Magnitude,
}

impl PartialEq for BaseDimensions {
    fn eq(&self, other: &Self) -> bool {
        self.fields.iter().all(|(dimension, value)| {
            if let Some(corresponding_value) = other.fields.get(dimension) {
                value == corresponding_value
            } else {
                false
            }
        })
    }
}
impl BaseDimensions {
    pub fn none() -> Self {
        Self {
            fields: HashMap::default(),
        }
    }

    pub fn for_base_dimension(base_dim: BaseDimension) -> Self {
        let mut fields = HashMap::new();
        fields.insert(base_dim, Exponent::one());
        Self { fields }
    }

    pub(crate) fn fields(&self) -> impl Iterator<Item = (&Ident, &Exponent)> {
        self.fields.iter().map(|(dim, exp)| (&dim.0, exp))
    }

    pub(crate) fn keys(&self) -> impl Iterator<Item = &BaseDimension> {
        self.fields.keys()
    }

    pub(crate) fn num_fields(&self) -> usize {
        self.fields.len()
    }

    pub(crate) fn get(&self, dim: &BaseDimension) -> Option<&Exponent> {
        self.fields.get(dim)
    }
}

impl core::ops::Mul for BaseDimensions {
    type Output = Self;

    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, rhs: Self) -> Self::Output {
        let mut fields = self.fields;
        for (name_rhs, val_rhs) in rhs.fields {
            let same_field = fields.get_mut(&name_rhs);
            if let Some(val) = same_field {
                *val += val_rhs;
            } else {
                fields.insert(name_rhs, val_rhs);
            }
        }
        Self { fields }
    }
}

impl BaseDimensions {
    fn inv(mut self) -> Self {
        for (_, value) in self.fields.iter_mut() {
            *value = -*value;
        }
        self
    }
}

impl core::ops::Div for BaseDimensions {
    type Output = Self;

    #[allow(clippy::suspicious_arithmetic_impl)]
    fn div(self, rhs: Self) -> Self::Output {
        self * rhs.inv()
    }
}

impl MulDiv for BaseDimensions {
    fn pow(self, pow: Exponent) -> Self {
        BaseDimensions {
            fields: self
                .fields
                .into_iter()
                .map(|(ident, value)| (ident, value * pow))
                .collect(),
        }
    }
}

impl DimensionsAndMagnitude {
    pub fn magnitude(magnitude: Magnitude) -> Self {
        Self {
            dimensions: BaseDimensions::none(),
            magnitude,
        }
    }

    pub(crate) fn dimensions(dimensions: BaseDimensions) -> Self {
        Self {
            dimensions,
            magnitude: Magnitude::from_f64(1.0),
        }
    }
}

impl core::ops::Mul for DimensionsAndMagnitude {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self::Output {
        Self {
            dimensions: self.dimensions * rhs.dimensions,
            magnitude: self.magnitude * rhs.magnitude,
        }
    }
}

impl core::ops::Div for DimensionsAndMagnitude {
    type Output = Self;

    fn div(self, rhs: Self) -> Self::Output {
        Self {
            dimensions: self.dimensions / rhs.dimensions,
            magnitude: self.magnitude / rhs.magnitude,
        }
    }
}

impl MulDiv for DimensionsAndMagnitude {
    fn pow(self, pow: Exponent) -> Self {
        Self {
            magnitude: Exponent::float_pow(self.magnitude, pow),
            dimensions: self.dimensions.pow(pow),
        }
    }
}