hyf

Context-aware query service for Radroots
git clone https://radroots.dev/git/hyf.git
Log | Files | Refs | README | LICENSE

units.mojo (2726B)


      1 from hyf_core.domain.quantity import Quantity, new_quantity
      2 
      3 
      4 @fieldwise_init
      5 struct ConversionRule(Copyable, Movable):
      6     var from_unit: String
      7     var to_unit: String
      8     var numerator: Int
      9     var denominator: Int
     10     var version: String
     11 
     12 
     13 def unit_dimension(unit: String) -> String:
     14     var lowered = unit.lower()
     15     if lowered == "kg" or lowered == "lb" or lowered == "g" or lowered == "oz":
     16         return "mass"
     17     if lowered == "l" or lowered == "ml":
     18         return "volume"
     19     if lowered == "each" or lowered == "box" or lowered == "bunch":
     20         return "count"
     21     return "unknown"
     22 
     23 
     24 def _power_of_ten_exponent(denominator: Int) raises -> Int:
     25     if denominator <= 0:
     26         raise Error("conversion denominator must be positive")
     27     var remaining = denominator
     28     var exponent = 0
     29     while remaining > 1:
     30         if remaining % 10 != 0:
     31             raise Error(
     32                 "unsupported conversion denominator (must be a power of ten)"
     33             )
     34         remaining //= 10
     35         exponent += 1
     36     return exponent
     37 
     38 
     39 def conversion_rule(
     40     from_unit: String,
     41     to_unit: String,
     42     numerator: Int,
     43     denominator: Int,
     44     version: String,
     45 ) raises -> ConversionRule:
     46     if from_unit.strip() == "" or to_unit.strip() == "":
     47         raise Error("conversion requires from_unit and to_unit")
     48     if numerator <= 0:
     49         raise Error("conversion numerator must be positive")
     50     if unit_dimension(from_unit) != unit_dimension(to_unit):
     51         raise Error("conversion rule crosses incompatible dimensions")
     52     _ = _power_of_ten_exponent(denominator)
     53     return ConversionRule(
     54         from_unit=String(from_unit),
     55         to_unit=String(to_unit),
     56         numerator=numerator,
     57         denominator=denominator,
     58         version=String(version),
     59     )
     60 
     61 
     62 def apply_conversion(
     63     quantity: Quantity, rule: ConversionRule
     64 ) raises -> Quantity:
     65     if quantity.unit != rule.from_unit:
     66         raise Error("conversion rule does not apply to unit " + quantity.unit)
     67     if quantity.dimension != unit_dimension(rule.to_unit):
     68         raise Error("conversion rule crosses incompatible dimensions")
     69     var exponent = _power_of_ten_exponent(rule.denominator)
     70     var result = quantity.copy()
     71     result.value = quantity.value * rule.numerator
     72     result.scale = quantity.scale + exponent
     73     result.unit = String(rule.to_unit)
     74     result.dimension = String(unit_dimension(rule.to_unit))
     75     return result^
     76 
     77 
     78 def normalize_quantity_with_rule(
     79     quantity: Quantity, rule: ConversionRule
     80 ) raises -> Quantity:
     81     var result = apply_conversion(quantity, rule)
     82     if result.qualifier != quantity.qualifier:
     83         raise Error("normalization must preserve the approximation qualifier")
     84     return result^