Browse Source

Allow for constants

David Peter 2 years ago
parent
commit
2536e2f8b2
1 changed files with 66 additions and 28 deletions
  1. 66 28
      numbat/src/unit.rs

+ 66 - 28
numbat/src/unit.rs

@@ -1,4 +1,4 @@
-use std::fmt::Display;
+use std::{fmt::Display, ops::Div};
 
 use itertools::Itertools;
 use num_traits::{ToPrimitive, Zero};
@@ -308,7 +308,12 @@ impl Unit {
 
     #[cfg(test)]
     pub fn radian() -> Self {
-        Self::new_base("radian", "rad")
+        Self::new_derived(
+            "radian",
+            "rad",
+            Number::from_f64(1.0),
+            Self::meter() / Self::meter(),
+        )
     }
 
     #[cfg(test)]
@@ -403,41 +408,49 @@ impl Display for Unit {
     }
 }
 
-/// This function attempts to solves the equation a = b^alpha, where alpha
-/// is a rational exponent. If there is a solution, alpha is returned. If
-/// not, None is returned.
+/// This function attempts to solves the equation a = C · b^alpha, where
+/// C is a constant and alpha is a rational exponent. If there is a solution,
+/// `Some(alpha)` is returned. If not, `None` is returned.
 ///
 /// Examples:
-///     - is_multiple_of(m, m²)       = Some(2)
-///     - is_multiple_of(m³, m)       = Some(1/3)
-///     - is_multiple_of(m³, m²)      = Some(2/3)
-///     - is_multiple_of(m·s, m²·s²)  = Some(2)
-///     - is_multiple_of(m, s)        = None
-///     - is_multiple_of(m, m·s)      = None
-///     - is_multiple_of(m·s², m²·s²) = None
+/// - is_multiple_of(m², m)       = Some(2)
+/// - is_multiple_of(m, m³)       = Some(1/3)
+/// - is_multiple_of(m³, m²)      = Some(3/2)
+/// - is_multiple_of(m²·s², m·s)  = Some(2)
+///
+/// - is_multiple_of(m, km)       = Some(1)
+/// - is_multiple_of(m, inch)     = Some(1)
+/// - is_multiple_of(m², inch)    = Some(2)
+///
+/// - is_multiple_of(m, s)        = None
+/// - is_multiple_of(m, m·s)      = None
+/// - is_multiple_of(m·s², m²·s²) = None
 ///
 pub fn is_multiple_of(a: &Unit, b: &Unit) -> Option<Exponent> {
-    if a.is_scalar() {
-        if b.is_scalar() {
-            return Some(Exponent::from_integer(1));
-        } else {
-            return None;
-        }
+    let a_base = a.to_base_unit_representation().0;
+    let b_base = b.to_base_unit_representation().0;
+
+    if (a_base.clone().div(b_base.clone()))
+        .canonicalized()
+        .is_scalar()
+    {
+        return Some(Exponent::from_integer(1));
     }
 
-    let a_first = a
+    if a_base.is_scalar() {
+        return None;
+    }
+
+    let a_first = a_base
         .iter()
         .next()
         .expect("At least one factor in non-scalar unit");
 
-    if let Some(b_corresponding_factor) = b
-        .iter()
-        .find(|fb| fb.prefix == a_first.prefix && fb.unit_id == a_first.unit_id)
-    {
+    if let Some(b_corresponding_factor) = b_base.iter().find(|fb| fb.unit_id == a_first.unit_id) {
         let alpha = a_first.exponent / b_corresponding_factor.exponent;
 
         // Make sure that this is also correct for all other factors:
-        if a == &b.clone().power(alpha) {
+        if a_base.div(b_base.power(alpha)).is_scalar() {
             Some(alpha)
         } else {
             None
@@ -658,8 +671,6 @@ mod tests {
 
         assert_eq!(is_multiple_of(&Unit::scalar(), &Unit::meter()), None);
         assert_eq!(is_multiple_of(&Unit::meter(), &Unit::scalar()), None);
-        assert_eq!(is_multiple_of(&Unit::scalar(), &Unit::radian()), None);
-        assert_eq!(is_multiple_of(&Unit::radian(), &Unit::scalar()), None);
 
         assert_eq!(
             is_multiple_of(&Unit::meter(), &Unit::meter()),
@@ -722,8 +733,35 @@ mod tests {
             ),
             Some(Exponent::new(1, 2))
         );
+    }
 
-        assert_eq!(is_multiple_of(&Unit::meter(), &Unit::kilometer()), None);
-        assert_eq!(is_multiple_of(&Unit::kilometer(), &Unit::meter()), None);
+    #[test]
+    fn is_multiple_of_with_factor() {
+        assert_eq!(
+            is_multiple_of(&Unit::scalar(), &Unit::radian()),
+            Some(Exponent::new(1, 1))
+        );
+        assert_eq!(
+            is_multiple_of(&Unit::radian(), &Unit::scalar()),
+            Some(Exponent::new(1, 1))
+        );
+
+        assert_eq!(
+            is_multiple_of(&Unit::meter(), &Unit::inch()),
+            Some(Exponent::new(1, 1))
+        );
+        assert_eq!(
+            is_multiple_of(&Unit::meter(), &Unit::inch().powi(2)),
+            Some(Exponent::new(1, 2))
+        );
+
+        assert_eq!(
+            is_multiple_of(&Unit::meter(), &Unit::kilometer()),
+            Some(Exponent::new(1, 1))
+        );
+        assert_eq!(
+            is_multiple_of(&Unit::kilometer(), &Unit::meter()),
+            Some(Exponent::new(1, 1))
+        );
     }
 }