Add exact arithmetic implementation and tests

- Introduced exact arithmetic with support for large integers and fractions in `exact.gd`, `big_integer.gd`, and `exact_value.gd`.
- Implemented a comprehensive test suite in `test_exact_math.gd` to validate arithmetic operations against expected results.
- Added a README in `math` directory to document the exact arithmetic system and its usage.
- Ensured that all arithmetic operations maintain precision without floating-point errors, using a custom representation for large numbers.
- Included methods for encoding and decoding exact values for serialization.
This commit is contained in:
Kirill
2026-09-30 08:08:27 +03:00
parent f2111a376a
commit fb8a3f5658
58 changed files with 5139 additions and 2162 deletions
+42 -41
View File
@@ -1,4 +1,5 @@
extends SceneTree
const E = preload("res://world/owniverse/math/exact.gd")
const Clock = preload("res://world/owniverse/simulation_time.gd")
var checks: int = 0
var failures: int = 0
@@ -7,9 +8,9 @@ func _initialize() -> void:
call_deferred("run")
func check(condition: bool, message: String) -> void:
checks += 1
if not condition:
failures += 1
checks = E.add(checks, 1)
if (not condition):
failures = E.add(failures, 1)
push_error(message)
func fixture():
@@ -18,16 +19,16 @@ func fixture():
for id in global.OwniverseEntities:
var entity = load("res://world/owniverse/owniverse_entity.gd").new(global.OwniverseEntities[id])
world.catalog.entities[id] = entity
world.state.amount[id] = 0.0
world.state.stats.total_amount[id] = 0.0
if entity.group_as != "":
world.state.amount[entity.group_as] = 0.0
world.state.stats.total_amount[entity.group_as] = 0.0
world.state.amount[id] = 0
world.state.stats.total_amount[id] = 0
if E.ne(entity.group_as, ""):
world.state.amount[entity.group_as] = 0
world.state.stats.total_amount[entity.group_as] = 0
world.catalog.entities["N"].life_product = {}
world.catalog.entities["N"].product_per_year_type = "H"
world.catalog.entities["N"].product_per_year_amount = 10.0
world.catalog.entities["N"].auto = 0.0
world.state.amount.N = 1.0
world.catalog.entities["N"].product_per_year_amount = 10
world.catalog.entities["N"].auto = 0
world.state.amount.N = 1
return world
func dispose(world) -> void:
@@ -38,55 +39,55 @@ func dispose(world) -> void:
func run() -> void:
var saves: Node = root.get_node("SaveManager")
var world = fixture()
world.evolute(0.09)
check(world.state.time.year == 0.0, "Do not process before a tenth boundary")
check(world.state.amount.H == 0.0, "No premature production")
world.evolute(0.01)
check(world.state.time.year == 0.1, "Process the first tenth")
check(world.state.amount.H == 1.0, "Production runs at 0.1 year")
world.evolute(0.2)
check(world.state.time.year == 0.3 and world.state.amount.H == 3.0, "Do not reprocess previous tenths")
world.evolute(E.of("0.09"))
check(E.eq(world.state.time.year, 0), "Do not process before a tenth boundary")
check(E.eq(world.state.amount.H, 0), "No premature production")
world.evolute(E.of("0.01"))
check(E.eq(world.state.time.year, E.of("0.1")), "Process the first tenth")
check(E.eq(world.state.amount.H, 1), "Production runs at 0.1 year")
world.evolute(E.of("0.2"))
check((E.eq(world.state.time.year, E.of("0.3")) and E.eq(world.state.amount.H, 3)), "Do not reprocess previous tenths")
var before: Dictionary = world.state.duplicate(true)
for invalid in [0.0, -1.0, NAN, INF, 1e308]:
for invalid in [0, E.neg(1), NAN, INF, E.of("1e308")]:
world.evolute(invalid)
check(world.state == before, "Invalid/overflow interval rejected")
check(saves.validate_data({"format_version": 2, "app_version": "", "ui": {"current_page": 0}, "state": world.state}) == "", "Fractional save is valid")
check(E.eq(world.state, before), "Invalid/overflow interval rejected")
check(E.eq(saves.validate_data({"format_version": 3, "app_version": "", "ui": {"current_page": 0}, "state": world.state}), ""), "Fractional save is valid")
var invalid_save: Dictionary = world.state.duplicate(true)
invalid_save.time.year = 0.25
check(saves.validate_data({"format_version": 2, "app_version": "", "ui": {"current_page": 0}, "state": invalid_save}) != "", "Reject inconsistent fractional clock")
invalid_save.time.year = E.of("0.25")
check(E.ne(saves.validate_data({"format_version": 3, "app_version": "", "ui": {"current_page": 0}, "state": invalid_save}), ""), "Reject inconsistent fractional clock")
dispose(world)
for fps in [30, 60, 144]:
world = fixture()
for frame in fps:
world.evolute(1.0 / fps)
check(world.state.time.year == 1.0, "One second at %d fps" % fps)
check(world.state.amount.H == 10.0, "Production across frames at %d fps" % fps)
world.evolute(E.divide(1, fps))
check(E.eq(world.state.time.year, 1), ("One second at %d fps" % fps))
check(E.eq(world.state.amount.H, 10), ("Production across frames at %d fps" % fps))
dispose(world)
world = fixture()
world.evolute(0.05)
world.evolute(E.of("0.05"))
world.speed = 2
world.evolute(0.005)
check(world.state.time.year == 0.1, "Speed change preserves accumulated fraction")
world.evolute(E.of("0.005"))
check(E.eq(world.state.time.year, E.of("0.1")), "Speed change preserves accumulated fraction")
world.speed = 100
var speed_limit: int = root.get_node("Global").SPEED_LIMIT
check(world.speed == speed_limit and world.state.speed_in_year == pow(10.0, speed_limit - 1), "Configured upper speed limit")
world.speed = -10
check(world.speed == 1 and world.state.speed_in_year == 1.0, "Lower speed limit unchanged")
check((E.eq(world.speed, speed_limit) and E.eq(world.state.speed_in_year, E.power(10, E.subtract(speed_limit, 1)))), "Configured upper speed limit")
world.speed = E.neg(10)
check((E.eq(world.speed, 1) and E.eq(world.state.speed_in_year, 1)), "Lower speed limit unchanged")
dispose(world)
world = fixture()
dispose(world)
world = fixture()
world.catalog.entities.N.output_and = {"He": 1.0}
world.catalog.entities.N.output_and = {"He": 1}
world.catalog.entities.N.output_or = {}
world.state.batches.N = [{"start_year": 0.1, "end_year": 0.1, "amount": 1.0}]
world.evolute(0.1)
check(world.state.amount.N == 0.0 and world.state.amount.He == 1.0, "Existing evolution executes at fractional boundary")
world.state.batches.N = [{"start_year": E.of("0.1"), "end_year": E.of("0.1"), "amount": 1}]
world.evolute(E.of("0.1"))
check((E.eq(world.state.amount.N, 0) and E.eq(world.state.amount.He, 1)), "Existing evolution executes at fractional boundary")
dispose(world)
check(Clock.processed_year(0.099) == 0.0, "Do not round a genuine incomplete tenth forward")
check(Clock.processed_year(1e9 + 0.45) == 1e9 + 0.4, "Large ages preserve fractional precision")
print("Time state: %d checks, %d failures" % [checks, failures])
quit(0 if failures == 0 else 1)
check(E.eq(Clock.processed_year(E.of("0.099")), 0), "Do not round a genuine incomplete tenth forward")
check(E.eq(Clock.processed_year(E.add(E.of("1e9"), E.of("0.45"))), E.add(E.of("1e9"), E.of("0.4"))), "Large ages preserve fractional precision")
print(("Time state: %d checks, %d failures" % [checks, failures]))
quit((0 if E.eq(failures, 0) else 1))