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