extends SceneTree const E = preload("res://world/owniverse/math/exact.gd") const Q = preload("res://world/owniverse/quantity_math.gd") const Clock = preload("res://world/owniverse/simulation_time.gd") var checks: int = 0 var failures: int = 0 func _initialize(): call_deferred("run") func check(actual, expected: String, label: String): checks += 1 if str(actual) != expected: failures += 1 push_error(label + ": " + str(actual) + " != " + expected) func check_number(actual, expected: String, label: String): var reference = E.of(expected) checks += 1 if E.gt(E.absolute(E.subtract(actual, reference)), E.of("1e-12")): failures += 1 push_error(label + ": " + str(actual) + " != " + expected) func run(): # Expected strings were generated independently with Python fractions.Fraction. var cases = JSON.parse_string(FileAccess.get_file_as_string("res://tests/fixtures/exact_oracle.json")) for row in cases: var a = E.of(row[0]) var b = E.of(row[1]) check_number(E.add(a, b), row[2], "sum") check_number(E.subtract(a, b), row[3], "difference") check_number(E.multiply(a, b), row[4], "product") check_number(E.divide(a, b), row[5], "quotient") check(E.floor_value(E.divide(a, b)), row[6], "floor") for text in ["9223372036854775807", "-9223372036854775808", "9007199254740993", "1000000000000000000000000001", "-1/10000000000000"]: var value = E.of(text) check(E.eq(E.decode(JSON.parse_string(JSON.stringify(E.encode(value)))), value), "true", "JSON round trip") check(E.add(E.of("9223372036854775807"), 1), "9223372036854775808", "int64 overflow promoted") check(E.subtract(E.of("-9223372036854775808"), 1), "-9223372036854775809", "int64 underflow promoted") check(Q.production_step([E.of("1e27")]), "1", "large integral recipe") check(Q.production_step([E.of("1e-13")]), "10000000000000", "tiny fractional recipe") var count = E.of("1000000000000000000000000001") var allocation = Q.allocate_whole({"a": count, "b": count, "c": count}, count) check(Q.sum(allocation), str(count), "allocation conserves every unit") check(allocation.a, "333333333333333333333333334", "deterministic remainder") var clock = Clock.advance({"datetime": E.of("1e27")}, 0.000000001, 1) check_number(E.subtract(clock.datetime, E.of("1e27")), "1/1000000000", "single tick at huge age") var world = load("res://world/owniverse/owniverse.gd").new() world.catalog.load_definitions(root.get_node("Global").OwniverseEntities) for id in world.catalog.entities: world.state.amount[id] = 0 world.state.stats.total_amount[id] = 0 var group: String = world.catalog.entities[id].group_as if group != "": world.state.amount[group] = 0 world.state.stats.total_amount[group] = 0 world.production.produce_hs("H", 1) check(world.state.energy, "999999999999999999999999999", "spend one energy") var life = world.life.life_products(world.state, world.catalog.entities.EON5, count) check(Q.sum(life), str(count), "life conservation at 1e27") check(E.bounded(count), str(count), "integer counts never quantized") check(E.mul_div(E.of("1e27"), 15, 100), "150000000000000000000000000", "exact percentage") check(E.mul_div(count, count, count), str(count), "fused product beyond storage range") check(E.subtract(E.add(count, 1), count), "1", "single unit at E27") check(E.floor_value(E.divide(E.subtract(count, 1), count)), "0", "division must not round up a whole object") check(E.fixed(E.add(count, E.of("0.125")), 3), "1000000000000000000000000001.125", "formatting without oversized intermediate") check(E.scientific(E.of("9.999999e27"), 2), "1.00E+28", "display rounding at upper boundary") check(E.eq(E.power(10, -100), E.of("1e-100")), "true", "negative power needs no huge intermediate") var largest = E.of("9999999999999999999999999999") check(E.eq(E.decode(E.encode(largest)), largest), "true", "upper range round trip") check(E.valid_encoded(E.encode(largest)), "true", "upper range encoding valid") for tiny in ["1e-13", "1e-20", "1e-100", "1e-310"]: var value = E.of(tiny) check(E.gt(value, 0), "true", "tiny fraction remains nonzero") check(E.eq(value, E.decode(JSON.parse_string(JSON.stringify(E.encode(value))))), "true", "tiny float round trip") for invalid in [{"$fixed": ["0", "0", "ffffffffffffffff", 1]}, {"$fixed": ["0", "-1", "0000000000000000", 1]}, {"$exact": "1/3"}, {"$exact": "10000000000000000000000000000"}, 1e100]: check(E.valid_encoded(invalid), "false", "reject invalid saved number") world.state.amount.S = E.of("1e24") world.state.amount.H = E.of("1e24") world.state.amount.RD = 1234567 world.state.amount.YD = 765432 world.state.amount.STN = 654321 world.speed = 6 var started: int = Time.get_ticks_msec() for i in 100: world.state.amount.RD = E.add(world.state.amount.RD, i + 1) world.evolute(0.1) check(str(world.state.auto_star_deficits).length() < 500, "true", "fraction storage stays bounded") print("100 changing-composition cycles: %d ms" % (Time.get_ticks_msec() - started)) var saves = root.get_node("SaveManager") check(saves.validate_data({"format_version": 4, "app_version": "", "ui": {"current_page": 0}, "state": world.state}), "", "long-running state saves") for entity in world.catalog.entities.values(): entity.free() world.free() print("Exact math: %d checks, %d failures" % [checks, failures]) quit(0 if failures == 0 else 1)