extends RefCounted const E = preload("res://world/owniverse/math/exact.gd") # Frozen apportionment before the civilization performance refactor. static func allocate_whole(requests: Dictionary, limit: Variant) -> Dictionary: var total: Variant = 0 for amount in requests.values(): total = E.add(total, E.floor_value(E.maximum(0, amount))) var available: Variant = E.floor_value(E.minimum(E.maximum(0, limit), total)) var result: Dictionary = {} var order: Array = [] var assigned: Variant = 0 for id in requests: var expected: Variant = (E.mul_div(E.floor_value(E.maximum(0, requests[id])), available, total) if E.gt(total, 0) else 0) result[id] = E.floor_value(expected) assigned = E.add(assigned, result[id]) order.append({"id": id, "rest": E.subtract(expected, result[id])}) order.sort_custom(func ( a : Dictionary , b : Dictionary ) -> bool : return (E.gt(a.rest, b.rest) if E.ne(a.rest, b.rest) else E.lt(str(a.id), str(b.id)))) for item : Dictionary in order: if E.ge(assigned, available): break if E.lt(result[item.id], E.floor_value(requests[item.id])): result[item.id] = E.add(result[item.id], 1) assigned = E.add(assigned, 1) return result static func sum(pop: Dictionary) -> Variant: var result: Variant = 0 for n in pop.values(): result = E.add(result, n) return result static func split(remainders: Dictionary, key: String, count: Variant, weights: Dictionary) -> Dictionary: var result: Dictionary = {} var sum_weights: Variant = sum(weights) if (E.le(count, 0) or E.le(sum_weights, 0)): return result var expected: Dictionary = {} var positive_sum: Variant = 0 for id in weights: expected[id] = E.add(E.mul_div(count, weights[id], sum_weights), remainders.get(E.add(E.add(key, ":"), id), 0)) positive_sum = E.add(positive_sum, E.maximum(0, expected[id])) var order: Array = [] var assigned: Variant = 0 for id in weights: var normalized: Variant = (E.mul_div(E.maximum(0, expected[id]), count, positive_sum) if E.gt(positive_sum, 0) else 0) result[id] = E.floor_value(normalized) assigned = E.add(assigned, result[id]) order.append({"id": id, "rest": E.subtract(normalized, result[id])}) order.sort_custom(func ( a , b ) : return (E.gt(a.rest, b.rest) if E.ne(a.rest, b.rest) else E.lt(str(a.id), str(b.id)))) for entry in order: if E.ge(assigned, count): break result[entry.id] = E.add(result[entry.id], 1) assigned = E.add(assigned, 1) for id in weights: remainders[E.add(E.add(key, ":"), id)] = E.clamp_value(E.subtract(expected[id], result[id]), E.neg(1), 1) return result