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:
+29
-40
@@ -1,29 +1,18 @@
|
||||
extends HBoxContainer
|
||||
class_name OwniverseAction
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
|
||||
signal action_pressed(page_id, amount: float)
|
||||
signal action_pressed ( page_id , amount : Variant )
|
||||
|
||||
@onready var action_labels: Array = [
|
||||
$ActionControl0/Label,
|
||||
$ActionControl1/Label,
|
||||
$ActionControl2/Label,
|
||||
$ActionControl3/Label,
|
||||
$ActionControl4/Label,
|
||||
]
|
||||
@onready var action_labels : Array = [$ActionControl0/Label, $ActionControl1/Label, $ActionControl2/Label, $ActionControl3/Label, $ActionControl4/Label]
|
||||
|
||||
var page_id: int = -1
|
||||
var page_id: int = E.neg(1)
|
||||
var current_item_id = ""
|
||||
|
||||
var action_values: Dictionary = {
|
||||
0: 0,
|
||||
1: 0,
|
||||
2: 0,
|
||||
3: 0,
|
||||
4: 0,
|
||||
}
|
||||
var action_values: Dictionary = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}
|
||||
|
||||
|
||||
func set_actions(item_id: String, value: float, preview: Callable = Callable()) -> void:
|
||||
func set_actions(item_id: String, value: Variant, preview: Callable = Callable()) -> void:
|
||||
current_item_id = item_id
|
||||
var requests: Array = _get_distribution(value)
|
||||
var unique_requests: Array = []
|
||||
@@ -31,32 +20,32 @@ func set_actions(item_id: String, value: float, preview: Callable = Callable())
|
||||
var labels: Dictionary = {}
|
||||
# Prefer the largest request (including MAX) when previews or rounded
|
||||
# scientific labels coincide. Keep the request paired with its preview.
|
||||
for i in range(requests.size() - 1, -1, -1):
|
||||
if requests[i] <= 0.0: continue
|
||||
var amount: float = preview.call(requests[i]) if preview.is_valid() else requests[i]
|
||||
if not is_finite(amount) or amount < 1.0 or amount != floorf(amount): continue
|
||||
for i in range(E.subtract(requests.size(), 1), E.neg(1), E.neg(1)):
|
||||
if E.le(requests[i], 0): continue
|
||||
var amount: Variant = (preview.call(requests[i]) if preview.is_valid() else requests[i])
|
||||
if (((not E.finite(amount)) or E.lt(amount, 1)) or E.ne(amount, E.floor_value(amount))): continue
|
||||
var text: String = _format_action_value(amount)
|
||||
if displayed.has(amount) or labels.has(text): continue
|
||||
if (E.contains(displayed, amount) or labels.has(text)): continue
|
||||
labels[text] = true
|
||||
unique_requests.push_front(requests[i])
|
||||
displayed.push_front(amount)
|
||||
while unique_requests.size() < 5:
|
||||
unique_requests.append(0.0)
|
||||
displayed.append(0.0)
|
||||
while E.lt(unique_requests.size(), 5):
|
||||
unique_requests.append(0)
|
||||
displayed.append(0)
|
||||
_set_action_values(unique_requests, displayed)
|
||||
|
||||
func _get_distribution(value: float) -> Array:
|
||||
if value < 1.0 or not is_finite(value):
|
||||
return [0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
value = floorf(value)
|
||||
func _get_distribution(value: Variant) -> Array:
|
||||
if (E.lt(value, 1) or (not E.finite(value))):
|
||||
return [0, 0, 0, 0, 0]
|
||||
value = E.floor_value(value)
|
||||
var distribution: Array = []
|
||||
var power: float = 1.0
|
||||
while power < value:
|
||||
var power: Variant = 1
|
||||
while E.lt(power, value):
|
||||
distribution.append(power)
|
||||
if distribution.size() > 4: distribution.pop_front()
|
||||
power *= 10.0
|
||||
if E.gt(distribution.size(), 4): distribution.pop_front()
|
||||
power = E.multiply(power, 10)
|
||||
distribution.append(value)
|
||||
while distribution.size() < 5: distribution.append(0.0)
|
||||
while E.lt(distribution.size(), 5): distribution.append(0)
|
||||
return distribution
|
||||
|
||||
func _set_action_values(values: Array, displayed: Array) -> void:
|
||||
@@ -64,17 +53,17 @@ func _set_action_values(values: Array, displayed: Array) -> void:
|
||||
action_values[ind] = values[ind]
|
||||
# Keep the original request for execution; show the exact planned output.
|
||||
var text: String = _format_action_value(displayed[ind])
|
||||
if action_labels[ind].text != text:
|
||||
if E.ne(action_labels[ind].text, text):
|
||||
action_labels[ind].text = text
|
||||
action_labels[ind].get_parent().get_node("TextureButton").disabled = values[ind] <= 0.0
|
||||
action_labels[ind].get_parent().get_node("TextureButton").disabled = E.le(values[ind], 0)
|
||||
|
||||
|
||||
func _format_action_value(value: float) -> String:
|
||||
if value < 1.0 or not is_finite(value) or value != floorf(value):
|
||||
func _format_action_value(value: Variant) -> String:
|
||||
if ((E.lt(value, 1) or (not E.finite(value))) or E.ne(value, E.floor_value(value))):
|
||||
return ""
|
||||
return Global.format_number(value)
|
||||
|
||||
|
||||
|
||||
func _on_action_button_pressed(button_id: int) -> void:
|
||||
if action_values.get(button_id, 0.0) > 0.0:
|
||||
if E.gt(action_values.get(button_id, 0), 0):
|
||||
action_pressed.emit(page_id, action_values[button_id])
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends RefCounted
|
||||
const E = preload("res://world/owniverse/math/exact.gd")
|
||||
|
||||
const Parameters = preload("res://world/owniverse/world_parameters.gd")
|
||||
var energy_bars: Array = []
|
||||
@@ -32,21 +33,21 @@ func _ignore_mouse_on_children(node: Node) -> void:
|
||||
|
||||
func update(world) -> void:
|
||||
var source: Dictionary = world.state
|
||||
var energy: float = clampf(source.energy / Parameters.INITIAL_ENERGY, 0.0, 1.0)
|
||||
var energy: float = E.approx(E.clamp_value(E.divide(source.energy, Parameters.INITIAL_ENERGY), 0, 1))
|
||||
for bar in energy_bars: bar.scale.x = energy
|
||||
var density_value: float = world.environment.stellar_environment(source).density
|
||||
var density_value = world.environment.stellar_environment(source).density
|
||||
var density: Dictionary = Parameters.density_display(density_value)
|
||||
warning_border.self_modulate = Parameters.density_warning_color(density_value)
|
||||
for indicator in density_indicators:
|
||||
for mode in ["Density", "DensityRip", "DensityCrunch"]:
|
||||
indicator.get_node(mode).visible = mode == density.mode
|
||||
var pointer: Control = indicator.get_node("Pointer")
|
||||
pointer.position.x = 16.0 + 288.0 * density.position - pointer.size.x / 2.0
|
||||
pointer.position.x = 16.0 + 288.0 * E.approx(density.position) - pointer.size.x / 2.0
|
||||
pointer.self_modulate = Color.BLACK if density.mode == "DensityCrunch" else Color.WHITE
|
||||
for key in pointers:
|
||||
var pointer: Control = pointers[key]
|
||||
pointer.position.x = 16.0 * clampf(Parameters.value(source, key), 1.0, 19.0) - pointer.size.x / 2.0
|
||||
pointer.position.x = 16.0 * E.approx(E.clamp_value(Parameters.value(source, key), 1, 19)) - pointer.size.x / 2.0
|
||||
for i in trend_bars.size():
|
||||
var bar: Control = trend_bars[i]
|
||||
bar.pivot_offset = Vector2(0.0, bar.size.y)
|
||||
bar.scale.y = clampf(source.get("trends", {}).get(Parameters.TRENDS[i], 0.0), 0.0, 1.0)
|
||||
bar.scale.y = E.approx(E.clamp_value(source.get("trends", {}).get(Parameters.TRENDS[i], 0), 0, 1))
|
||||
|
||||
@@ -19,7 +19,7 @@ var item_sub_page: int = -1
|
||||
var item_type = ""
|
||||
|
||||
|
||||
func set_label(value: float) -> void:
|
||||
func set_label(value) -> void:
|
||||
var text: String = Global.format_number(value)
|
||||
if description.text != text:
|
||||
description.text = text
|
||||
|
||||
Reference in New Issue
Block a user