hyf

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

quantity.mojo (3237B)


      1 from std.collections import Optional
      2 
      3 
      4 @fieldwise_init
      5 struct Quantity(Copyable, Movable):
      6     var value: Int
      7     var scale: Int
      8     var unit: String
      9     var dimension: String
     10     var qualifier: String
     11 
     12 
     13 def _pow10(exponent: Int) -> Int:
     14     var result = 1
     15     for _ in range(exponent):
     16         result *= 10
     17     return result
     18 
     19 
     20 def _checked_mul10(value: Int, times: Int) raises -> Int:
     21     var result = value
     22     for _ in range(times):
     23         var before = result
     24         result = before * 10
     25         if before != 0 and result // 10 != before:
     26             raise Error("quantity overflow")
     27     return result
     28 
     29 
     30 def _checked_add(left: Int, right: Int) raises -> Int:
     31     var result = left + right
     32     if left > 0 and right > 0 and result < 0:
     33         raise Error("quantity overflow")
     34     if left < 0 and right < 0 and result >= 0:
     35         raise Error("quantity overflow")
     36     return result
     37 
     38 
     39 def new_quantity(
     40     value: Int, scale: Int, unit: String, dimension: String, qualifier: String
     41 ) raises -> Quantity:
     42     if scale < 0 or scale > 9:
     43         raise Error("quantity scale must be between 0 and 9")
     44     if qualifier != "exact" and qualifier != "approximate":
     45         raise Error("quantity qualifier must be 'exact' or 'approximate'")
     46     if unit.strip() == "" or dimension.strip() == "":
     47         raise Error("quantity requires unit and dimension")
     48     return Quantity(
     49         value=value,
     50         scale=scale,
     51         unit=String(unit),
     52         dimension=String(dimension),
     53         qualifier=String(qualifier),
     54     )
     55 
     56 
     57 def quantity_rescale(quantity: Quantity, target_scale: Int) raises -> Quantity:
     58     if target_scale < quantity.scale:
     59         raise Error("quantity rescale must not reduce precision")
     60     var value = _checked_mul10(quantity.value, target_scale - quantity.scale)
     61     var result = quantity.copy()
     62     result.value = value
     63     result.scale = target_scale
     64     return result^
     65 
     66 
     67 def _compatible(left: Quantity, right: Quantity) raises:
     68     if left.unit != right.unit:
     69         raise Error("quantity units differ: " + left.unit + " vs " + right.unit)
     70     if left.dimension != right.dimension:
     71         raise Error("quantity dimensions differ")
     72 
     73 
     74 def quantity_add(left: Quantity, right: Quantity) raises -> Quantity:
     75     _compatible(left, right)
     76     var scale = left.scale if left.scale > right.scale else right.scale
     77     var scaled_left = quantity_rescale(left, scale)
     78     var scaled_right = quantity_rescale(right, scale)
     79     var qualifier = "exact"
     80     if left.qualifier == "approximate" or right.qualifier == "approximate":
     81         qualifier = "approximate"
     82     return new_quantity(
     83         _checked_add(scaled_left.value, scaled_right.value),
     84         scale,
     85         left.unit,
     86         left.dimension,
     87         qualifier,
     88     )
     89 
     90 
     91 def quantity_compare(left: Quantity, right: Quantity) raises -> Int:
     92     _compatible(left, right)
     93     var scale = left.scale if left.scale > right.scale else right.scale
     94     var scaled_left = quantity_rescale(left, scale).value
     95     var scaled_right = quantity_rescale(right, scale).value
     96     if scaled_left < scaled_right:
     97         return -1
     98     if scaled_left > scaled_right:
     99         return 1
    100     return 0
    101 
    102 
    103 def quantity_is_negative(quantity: Quantity) -> Bool:
    104     return quantity.value < 0