pub fn round_layout_stable(
targets: &[f64],
total: u16,
prev: PreviousAllocation,
) -> Vec<u16>Expand description
Round real-valued layout targets to integer cells with exact sum conservation.
§Mathematical Model
Given real-valued targets r_i (from the constraint solver) and a required
integer total, find integer allocations x_i that:
minimize Σ_i |x_i − r_i| + μ · Σ_i |x_i − x_i_prev|
subject to Σ_i x_i = total
x_i ≥ 0where x_i_prev is the previous frame’s allocation and μ is the temporal
stability weight (default 0.1).
§Algorithm: Largest Remainder with Temporal Tie-Breaking
This uses a variant of the Largest Remainder Method (Hamilton’s method), which provides optimal bounded displacement (|x_i − r_i| < 1 for all i):
- Floor phase: Set
x_i = floor(r_i)for each element. - Deficit: Compute
D = total − Σ floor(r_i)extra cells to distribute. - Priority sort: Rank elements by remainder
r_i − floor(r_i)(descending). Break ties using a composite key: a. Prefer elements wherex_i_prev = ceil(r_i)(temporal stability). b. Prefer elements with smaller index (determinism). - Distribute: Award one extra cell to each of the top
Delements.
§Properties
- Sum conservation:
Σ x_i = totalexactly (proven by construction). - Bounded displacement:
|x_i − r_i| < 1for alli(since each x_i is eitherfloor(r_i)orceil(r_i)). - Deterministic: Same inputs → identical outputs (temporal tie-break + index tie-break provide total ordering).
- Temporal coherence: When targets change slightly, allocations tend to stay the same (preferring the previous frame’s rounding direction).
- Optimal displacement: Among all integer allocations summing to
totalwithfloor(r_i) ≤ x_i ≤ ceil(r_i), the Largest Remainder Method minimizes total absolute displacement.
§Failure Modes
- All-zero targets: Returns all zeros. Harmless (empty layout).
- Negative deficit: Can occur if targets sum to less than
totalafter flooring. The algorithm handles this via the clamp in step 2. - Very large N: O(N log N) due to sorting. Acceptable for typical layout counts (< 100 items).
§Example
use ftui_layout::round_layout_stable;
// Targets: [10.4, 20.6, 9.0] must sum to 40
let result = round_layout_stable(&[10.4, 20.6, 9.0], 40, None);
assert_eq!(result.iter().sum::<u16>(), 40);
// 10.4 → 10, 20.6 → 21, 9.0 → 9 = 40 ✓
assert_eq!(result, vec![10, 21, 9]);