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
+69 -68
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,97 +39,97 @@ func dispose(world) -> void:
func run() -> void:
var world = fixture()
var global: Node = root.get_node("Global")
check(global.CivilizationRules.size() == 44, "Load all ethical civilization stages")
for row: Dictionary in global.CivilizationRules.values():
check(row.ethics.size() == 6 or row.span == 0.0, "Every ethical stage has six rules")
world.state.amount.RD = 12.0
world.state.amount["C0.2"] = 8.0
world.state.binds.RD = {"C2.0": 12.0, "C1.9": 3.0}
check(E.eq(global.CivilizationRules.size(), 44), "Load all ethical civilization stages")
for row : Dictionary in global.CivilizationRules.values():
check((E.eq(row.ethics.size(), 6) or E.eq(row.span, 0)), "Every ethical stage has six rules")
world.state.amount.RD = 12
world.state.amount["C0.2"] = 8
world.state.binds.RD = {"C2.0": 12, "C1.9": 3}
world.evolution.initialize_life_batches()
check(world.state.amount["C2.0"] == 12.0 and world.state.binds.RD["C2.0"] == 0.0, "Old space civilizations detach without loss")
check(world.state.binds.RD["C1.9"] == 3.0, "Planetary civilizations retain their host")
check(world.state.ethics["C0.2"] == {"m": 2.0, "l": 2.0, "w": 1.0, "d": 1.0, "c": 1.0, "b": 1.0}, "Migration uses equal ethics and deterministic remainder")
check(world.state.ethics["C2.0"].values().all(func(n): return n == 2.0), "Bound civilization ethics count physical population")
check((E.eq(world.state.amount["C2.0"], 12) and E.eq(world.state.binds.RD["C2.0"], 0)), "Old space civilizations detach without loss")
check(E.eq(world.state.binds.RD["C1.9"], 3), "Planetary civilizations retain their host")
check(E.eq(world.state.ethics["C0.2"], {"m": 2, "l": 2, "w": 1, "d": 1, "c": 1, "b": 1}), "Migration uses equal ethics and deterministic remainder")
check(world.state.ethics["C2.0"].values().all(func ( n ) : return E.eq(n, 2)), "Bound civilization ethics count physical population")
var before: Dictionary = world.state.duplicate(true)
world.evolution.initialize_life_batches()
check(world.state == before, "Repeated initialization preserves ethics")
world.state.ethics["C0.2"] = {"m": 8.0, "l": 0.0, "w": 0.0, "d": 0.0, "c": 0.0, "b": 0.0}
check(E.eq(world.state, before), "Repeated initialization preserves ethics")
world.state.ethics["C0.2"] = {"m": 8, "l": 0, "w": 0, "d": 0, "c": 0, "b": 0}
world.evolution.initialize_life_batches()
check(world.state.ethics["C0.2"].m == 8.0, "Existing unequal distribution is retained")
world.state.amount["C0.2"] = 3.0
check(E.eq(world.state.ethics["C0.2"].m, 8), "Existing unequal distribution is retained")
world.state.amount["C0.2"] = 3
world.evolution.initialize_life_batches()
check(world.state.ethics["C0.2"].m == 3.0, "Losses reduce ethics without inventing population")
check(root.get_node("SaveManager").validate_data({"format_version": 2, "app_version": "", "ui": {"current_page": 0}, "state": world.state}) == "", "Ethics state validates")
check(E.eq(world.state.ethics["C0.2"].m, 3), "Losses reduce ethics without inventing population")
check(E.eq(root.get_node("SaveManager").validate_data({"format_version": 3, "app_version": "", "ui": {"current_page": 0}, "state": world.state}), ""), "Ethics state validates")
var restored = fixture()
restored.state = bytes_to_var(var_to_bytes(world.state))
restored.state = E.decode(bytes_to_var(var_to_bytes(E.encode(world.state))))
restored.evolution.initialize_life_batches()
check(restored.state == world.state, "Save roundtrip preserves ethics and ownership")
check(E.eq(restored.state, world.state), "Save roundtrip preserves ethics and ownership")
dispose(restored)
dispose(world)
world = fixture()
world.state.amount.RD = 10.0
for row: Dictionary in global.CivilizationRules["C1.9"].ethics.values():
world.state.amount.RD = 10
for row : Dictionary in global.CivilizationRules["C1.9"].ethics.values():
row.progress = "1"
row.degradation = "0"
row.colonization = "0"
world.state.binds.RD = {"C1.9": 10.0}
world.state.batches["C1.9"] = [{"amount": 10.0, "start_year": 1.0, "end_year": 1.0}]
world.evolution.evolve_batches(world.state, 1.0)
check(world.state.amount["C2.0"] == 10.0 and world.state.binds.RD.get("C2.0", 0.0) == 0.0, "Space successor does not inherit planetary binding")
world.state.binds.RD = {"C1.9": 10}
world.state.batches["C1.9"] = [{"amount": 10, "start_year": 1, "end_year": 1}]
world.evolution.evolve_batches(world.state, 1)
check((E.eq(world.state.amount["C2.0"], 10) and E.eq(world.state.binds.RD.get("C2.0", 0), 0)), "Space successor does not inherit planetary binding")
dispose(world)
var capacity = load("res://world/owniverse/civilization_state.gd")
world = fixture()
world.state.amount.RD = 10.0
world.state.amount["C2.0"] = 8.0
world.state.amount.RD = 10
world.state.amount["C2.0"] = 8
var budget: Dictionary = capacity.stellar_budget(world, world.state, global.CivilizationRules)
check(capacity.admit_transition(budget, global.CivilizationRules, "C1.9", "C2.0", 5.0) == 2.0, "Growth consumes only remaining stellar capacity")
check(capacity.admit_transition(budget, global.CivilizationRules, "C1.9", "C2.0", 5.0) == 0.0, "A reservation cannot be spent twice")
world.state.amount["C2.0"] = 1.0
check(E.eq(capacity.admit_transition(budget, global.CivilizationRules, "C1.9", "C2.0", 5), 2), "Growth consumes only remaining stellar capacity")
check(E.eq(capacity.admit_transition(budget, global.CivilizationRules, "C1.9", "C2.0", 5), 0), "A reservation cannot be spent twice")
world.state.amount["C2.0"] = 1
budget = capacity.stellar_budget(world, world.state, global.CivilizationRules)
check(capacity.admit_transition(budget, global.CivilizationRules, "C2.0", "C2.1", 1.0) == 1.0, "A transition reuses the source reservation")
check(budget.free.DwStar == 0.0, "C2.1 reserves ten dwarf stars")
world.state.amount["C2.0"] = 0.0
world.state.binds.SC = {"RD": 10.0}
check(E.eq(capacity.admit_transition(budget, global.CivilizationRules, "C2.0", "C2.1", 1), 1), "A transition reuses the source reservation")
check(E.eq(budget.free.DwStar, 0), "C2.1 reserves ten dwarf stars")
world.state.amount["C2.0"] = 0
world.state.binds.SC = {"RD": 10}
budget = capacity.stellar_budget(world, world.state, global.CivilizationRules)
check(budget.capacity.DwStar == 20.0, "Stars inside structures are counted once")
check(E.eq(budget.capacity.DwStar, 20), "Stars inside structures are counted once")
dispose(world)
world = fixture()
world.state.amount.RD = 12.0
world.state.amount["C2.1"] = 2.0
world.state.amount["C2.0"] = 5.0
world.state.amount.RD = 12
world.state.amount["C2.1"] = 2
world.state.amount["C2.0"] = 5
world.evolution.initialize_life_batches()
check(world.state.amount["C2.1"] == 1.0 and world.state.amount["C2.0"] == 2.0, "Shared budget supports advanced civilizations first")
check(world.state.binds.RD["C1.9"] == 4.0, "Excess cascades to supported planetary stage")
check((E.eq(world.state.amount["C2.1"], 1) and E.eq(world.state.amount["C2.0"], 2)), "Shared budget supports advanced civilizations first")
check(E.eq(world.state.binds.RD["C1.9"], 4), "Excess cascades to supported planetary stage")
var snapshot: Dictionary = world.state.duplicate(true)
world.evolution.initialize_life_batches()
check(world.state == snapshot, "Capacity enforcement is idempotent")
world.state.amount.RD = 0.0
check(E.eq(world.state, snapshot), "Capacity enforcement is idempotent")
world.state.amount.RD = 0
world.evolution.initialize_life_batches()
check(world.state.amount["C2.1"] == 0.0 and world.state.amount["C2.0"] == 0.0, "Vanished stars force all unsupported space civilizations down")
check(world.ledger.physical_amounts(world.state)["C1.9"] == 7.0, "Forced degradation conserves civilizations")
check((E.eq(world.state.amount["C2.1"], 0) and E.eq(world.state.amount["C2.0"], 0)), "Vanished stars force all unsupported space civilizations down")
check(E.eq(world.ledger.physical_amounts(world.state)["C1.9"], 7), "Forced degradation conserves civilizations")
dispose(world)
world = fixture()
world.state.amount.LGW = 20.0
world.state.amount.SMBH = 2.0e22
world.state.amount["C4.2"] = 3.0
world.state.amount.LGW = 20
world.state.amount.SMBH = E.of("2.0e22")
world.state.amount["C4.2"] = 3
world.evolution.initialize_life_batches()
check(world.state.amount["C4.2"] == 2.0, "SMBH capacity uses stellar mass equivalent")
check(world.ledger.physical_amounts(world.state)["C1.9"] == 1.0, "Unsupported civilization degrades through missing habitat families")
check(root.get_node("SaveManager").validate_data({"format_version": 2, "app_version": "", "ui": {"current_page": 0}, "state": world.state}) == "", "Capacity-limited state validates")
check(E.eq(world.state.amount["C4.2"], 2), "SMBH capacity uses stellar mass equivalent")
check(E.eq(world.ledger.physical_amounts(world.state)["C1.9"], 1), "Unsupported civilization degrades through missing habitat families")
check(E.eq(root.get_node("SaveManager").validate_data({"format_version": 3, "app_version": "", "ui": {"current_page": 0}, "state": world.state}), ""), "Capacity-limited state validates")
dispose(world)
world = fixture()
world.state.amount.RD = 2.0
for row: Dictionary in global.CivilizationRules["C1.9"].ethics.values():
world.state.amount.RD = 2
for row : Dictionary in global.CivilizationRules["C1.9"].ethics.values():
row.progress = "1"
row.degradation = "0"
row.colonization = "0"
world.state.binds.RD = {"C1.9": 5.0}
world.state.batches["C1.9"] = [{"amount": 5.0, "start_year": 1.0, "end_year": 1.0}]
world.evolution.evolve_batches(world.state, 1.0)
check(world.state.amount["C2.0"] == 2.0 and world.state.binds.RD["C1.9"] == 3.0, "Unfunded transition retains the original planetary civilization")
world.state.binds.RD = {"C1.9": 5}
world.state.batches["C1.9"] = [{"amount": 5, "start_year": 1, "end_year": 1}]
world.evolution.evolve_batches(world.state, 1)
check((E.eq(world.state.amount["C2.0"], 2) and E.eq(world.state.binds.RD["C1.9"], 3)), "Unfunded transition retains the original planetary civilization")
dispose(world)
print("Civilization state: %d checks, %d failures" % [checks, failures])
quit(0 if failures == 0 else 1)
print(("Civilization state: %d checks, %d failures" % [checks, failures]))
quit((0 if E.eq(failures, 0) else 1))