use alloc::vec::Vec;
#[derive(Clone, Copy, Debug)]
pub struct Extent {
pub ideal: f32,
pub min: f32,
pub priority: i32,
}
pub fn compress_to_fit(extents: &[Extent], available: f32) -> Vec<f32> {
let mut resolved: Vec<f32> = extents.iter().map(|extent| extent.ideal).collect();
let total: f32 = resolved.iter().sum();
if !exceeds(total, available, resolved.len()) {
return resolved;
}
let mut priorities: Vec<i32> = extents.iter().map(|extent| extent.priority).collect();
priorities.sort_unstable();
priorities.dedup();
let mut deficit = total - available;
for priority in priorities {
if deficit <= 0.0 {
break;
}
let band: Vec<usize> = extents
.iter()
.enumerate()
.filter(|(_, extent)| extent.priority == priority)
.map(|(index, _)| index)
.collect();
let band_ideal: f32 = band.iter().map(|&index| extents[index].ideal).sum();
let band_floor: f32 = band.iter().map(|&index| extents[index].min).sum();
let absorbed = deficit.min(band_ideal - band_floor).max(0.0);
if absorbed <= 0.0 {
continue;
}
let band_extents: Vec<Extent> = band.iter().map(|&index| extents[index]).collect();
for (slot, width) in band
.iter()
.zip(water_fill(&band_extents, band_ideal - absorbed))
{
resolved[*slot] = width;
}
deficit -= absorbed;
}
resolved
}
fn water_fill(extents: &[Extent], target: f32) -> Vec<f32> {
let at_cap = |cap: f32| -> f32 {
extents
.iter()
.map(|extent| extent.ideal.min(cap).max(extent.min))
.sum()
};
let mut low = extents
.iter()
.map(|extent| extent.min)
.fold(0.0_f32, f32::min);
let mut high = extents
.iter()
.map(|extent| extent.ideal)
.fold(0.0_f32, f32::max);
for _ in 0..40 {
let mid = f32::midpoint(low, high);
if at_cap(mid) > target {
high = mid;
} else {
low = mid;
}
}
extents
.iter()
.map(|extent| extent.ideal.min(low).max(extent.min))
.collect()
}
pub fn exceeds(total: f32, available: f32, terms: usize) -> bool {
let magnitude = total.abs().max(available.abs()).max(1.0);
total - available > f32::EPSILON * magnitude * usize_to_f32(terms.max(1))
}
fn usize_to_f32(value: usize) -> f32 {
use num_traits::ToPrimitive;
value
.to_f32()
.expect("child count must be representable as f32")
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn extent(ideal: f32, min: f32) -> Extent {
Extent {
ideal,
min,
priority: 0,
}
}
#[test]
fn everything_fits_is_a_no_op() {
let extents = vec![extent(30.0, 10.0), extent(40.0, 10.0)];
assert_eq!(compress_to_fit(&extents, 100.0), vec![30.0, 40.0]);
}
#[test]
fn equal_children_shrink_equally() {
let extents: Vec<Extent> = (0..7).map(|_| extent(40.0, 0.0)).collect();
let resolved = compress_to_fit(&extents, 224.0);
for width in resolved {
assert!((width - 32.0).abs() < 0.01, "expected 32pt, got {width}");
}
}
#[test]
fn the_widest_child_absorbs_the_deficit_alone() {
let extents = vec![extent(50.0, 0.0), extent(200.0, 0.0)];
let resolved = compress_to_fit(&extents, 140.0);
assert!((resolved[0] - 50.0).abs() < 0.01, "got {}", resolved[0]);
assert!((resolved[1] - 90.0).abs() < 0.01, "got {}", resolved[1]);
}
#[test]
fn a_child_never_shrinks_below_its_reported_minimum() {
let extents = vec![extent(100.0, 20.0), extent(100.0, 80.0)];
let resolved = compress_to_fit(&extents, 120.0);
assert!(resolved[1] >= 80.0 - 0.01, "got {}", resolved[1]);
assert!(resolved[0] >= 20.0 - 0.01, "got {}", resolved[0]);
}
#[test]
fn lower_priority_gives_way_first() {
let extents = vec![
Extent {
ideal: 100.0,
min: 0.0,
priority: 1,
},
Extent {
ideal: 100.0,
min: 0.0,
priority: 0,
},
];
let resolved = compress_to_fit(&extents, 150.0);
assert!(
(resolved[0] - 100.0).abs() < 0.01,
"the prioritized child keeps its ideal, got {}",
resolved[0]
);
assert!(
(resolved[1] - 50.0).abs() < 0.01,
"the lower-priority child gives up the whole deficit, got {}",
resolved[1]
);
}
}