commit 117e865fc88083e3ffc81f258297bbe6f8139088
parent f6b0a5046e32ed5be9f201ba2283add5020a6360
Author: triesap <tyson@radroots.org>
Date: Mon, 21 Sep 2026 12:55:27 +0000
core: add bounded exact quantity arithmetic
Diffstat:
2 files changed, 135 insertions(+), 0 deletions(-)
diff --git a/src/hyf_core/domain/quantity.mojo b/src/hyf_core/domain/quantity.mojo
@@ -0,0 +1,104 @@
+from std.collections import Optional
+
+
+@fieldwise_init
+struct Quantity(Copyable, Movable):
+ var value: Int
+ var scale: Int
+ var unit: String
+ var dimension: String
+ var qualifier: String
+
+
+def _pow10(exponent: Int) -> Int:
+ var result = 1
+ for _ in range(exponent):
+ result *= 10
+ return result
+
+
+def _checked_mul10(value: Int, times: Int) raises -> Int:
+ var result = value
+ for _ in range(times):
+ var before = result
+ result = before * 10
+ if before != 0 and result // 10 != before:
+ raise Error("quantity overflow")
+ return result
+
+
+def _checked_add(left: Int, right: Int) raises -> Int:
+ var result = left + right
+ if left > 0 and right > 0 and result < 0:
+ raise Error("quantity overflow")
+ if left < 0 and right < 0 and result >= 0:
+ raise Error("quantity overflow")
+ return result
+
+
+def new_quantity(
+ value: Int, scale: Int, unit: String, dimension: String, qualifier: String
+) raises -> Quantity:
+ if scale < 0 or scale > 9:
+ raise Error("quantity scale must be between 0 and 9")
+ if qualifier != "exact" and qualifier != "approximate":
+ raise Error("quantity qualifier must be 'exact' or 'approximate'")
+ if unit.strip() == "" or dimension.strip() == "":
+ raise Error("quantity requires unit and dimension")
+ return Quantity(
+ value=value,
+ scale=scale,
+ unit=String(unit),
+ dimension=String(dimension),
+ qualifier=String(qualifier),
+ )
+
+
+def quantity_rescale(quantity: Quantity, target_scale: Int) raises -> Quantity:
+ if target_scale < quantity.scale:
+ raise Error("quantity rescale must not reduce precision")
+ var value = _checked_mul10(quantity.value, target_scale - quantity.scale)
+ var result = quantity.copy()
+ result.value = value
+ result.scale = target_scale
+ return result^
+
+
+def _compatible(left: Quantity, right: Quantity) raises:
+ if left.unit != right.unit:
+ raise Error("quantity units differ: " + left.unit + " vs " + right.unit)
+ if left.dimension != right.dimension:
+ raise Error("quantity dimensions differ")
+
+
+def quantity_add(left: Quantity, right: Quantity) raises -> Quantity:
+ _compatible(left, right)
+ var scale = left.scale if left.scale > right.scale else right.scale
+ var scaled_left = quantity_rescale(left, scale)
+ var scaled_right = quantity_rescale(right, scale)
+ var qualifier = "exact"
+ if left.qualifier == "approximate" or right.qualifier == "approximate":
+ qualifier = "approximate"
+ return new_quantity(
+ _checked_add(scaled_left.value, scaled_right.value),
+ scale,
+ left.unit,
+ left.dimension,
+ qualifier,
+ )
+
+
+def quantity_compare(left: Quantity, right: Quantity) raises -> Int:
+ _compatible(left, right)
+ var scale = left.scale if left.scale > right.scale else right.scale
+ var scaled_left = quantity_rescale(left, scale).value
+ var scaled_right = quantity_rescale(right, scale).value
+ if scaled_left < scaled_right:
+ return -1
+ if scaled_left > scaled_right:
+ return 1
+ return 0
+
+
+def quantity_is_negative(quantity: Quantity) -> Bool:
+ return quantity.value < 0
diff --git a/tests/test_domain.mojo b/tests/test_domain.mojo
@@ -99,3 +99,34 @@ def test_field_known_zero_distinct_from_unknown_and_false() raises:
validate_field_consistency(unknown)
var approximate = known_approximate_field("80", "span")
assert_equal(approximate.qualifier, "approximate")
+
+
+from hyf_core.domain.quantity import (
+ new_quantity,
+ quantity_add,
+ quantity_compare,
+ quantity_is_negative,
+ quantity_rescale,
+)
+
+
+def test_quantity_exact_compare_and_scale() raises:
+ var a = new_quantity(8000, 2, "kg", "mass", "exact")
+ var b = new_quantity(80, 0, "kg", "mass", "exact")
+ assert_equal(quantity_compare(a, b), 0)
+ var c = new_quantity(50, 0, "kg", "mass", "exact")
+ assert_equal(quantity_compare(c, b), -1)
+ assert_equal(quantity_rescale(b, 2).value, 8000)
+
+
+def test_quantity_add_overflow_and_approximation() raises:
+ var approx = new_quantity(80, 0, "lb", "mass", "approximate")
+ var exact = new_quantity(5, 0, "lb", "mass", "exact")
+ var total = quantity_add(approx, exact)
+ assert_equal(total.value, 85)
+ assert_equal(total.qualifier, "approximate")
+ var huge = new_quantity(9223372036854775807, 0, "kg", "mass", "exact")
+ with assert_raises():
+ _ = quantity_add(huge, exact)
+ var adjustment = new_quantity(-5, 0, "kg", "mass", "exact")
+ assert_true(quantity_is_negative(adjustment))