#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamClass {
pub size: usize,
pub count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StreamLayout {
pub basic: Vec<StreamClass>,
pub recursive: StreamClass,
pub aggregation_workers: usize,
pub unused: usize,
}
const RECURSIVE_STREAM_FLOOR: usize = 1;
const FLEET_FLOOR: usize = 8;
impl StreamLayout {
pub fn n_basic_streams(&self) -> usize {
self.basic.iter().map(|c| c.count).sum()
}
pub fn basic_stream_sizes(&self) -> Vec<usize> {
self.basic.iter().flat_map(|c| std::iter::repeat_n(c.size, c.count)).collect()
}
}
fn n_fit(room: usize, size: usize, cap: usize) -> usize {
room.checked_div(size).map_or(0, |n| cap.min(n))
}
fn buy_streams(room: usize, large: usize, regular: usize, cap: usize, cap_regular: usize) -> (usize, usize) {
debug_assert!(0 < regular && regular < large, "classes must be distinct sizes: {regular} vs {large}");
let pairs = (room / (large + regular)).min(cap / 2).min(cap_regular);
let (mut n_large, mut n_regular) = (pairs, pairs);
let mut left = room - pairs * (large + regular);
loop {
let slot = n_large + n_regular < cap;
if slot && left >= large {
left -= large;
n_large += 1;
} else if slot && n_regular < cap_regular && left >= regular && (2 * regular <= large || n_regular == 1) {
left -= regular;
n_regular += 1;
} else if n_regular > 0 && left >= large - regular {
left -= large - regular;
n_regular -= 1;
n_large += 1;
} else {
return (n_large, n_regular);
}
}
}
pub fn plan_stream_layout(
budget: usize,
basic_sizes: &[usize],
class_floor: usize,
recursive_size: usize,
max_basic_streams: usize,
max_recursive_streams: usize,
) -> Option<StreamLayout> {
if max_basic_streams == 0 {
return None;
}
let class_floor = class_floor.max(recursive_size);
let mut sizes: Vec<usize> = basic_sizes.iter().map(|&s| s.max(class_floor)).collect();
sizes.sort_unstable_by(|a, b| b.cmp(a));
sizes.dedup();
let &large = sizes.first()?;
if large == 0 || budget < large {
return None;
}
let pool_is_redundant = class_floor < 2 * recursive_size;
let pool_size = if pool_is_redundant { 0 } else { recursive_size };
let room = budget.saturating_sub(pool_size * RECURSIVE_STREAM_FLOOR.min(max_recursive_streams));
let bulk_floor = {
let mut asc = basic_sizes.to_vec();
asc.sort_unstable();
asc[asc.len() / 2]
};
let candidates = || sizes.iter().skip(1).copied().filter(|&s| s > 0 && large + s <= room);
let earns_a_fleet = |s: usize| s >= bulk_floor && s * FLEET_FLOOR >= large;
let candidate = candidates().find(|&s| 2 * s <= large && earns_a_fleet(s)).or_else(|| candidates().next());
let (n_large, regular, n_regular) = match candidate {
Some(regular) => {
let cap_regular = if earns_a_fleet(regular) { max_basic_streams } else { 1 };
let (n_large, n_regular) = buy_streams(room, large, regular, max_basic_streams, cap_regular);
(n_large, regular, n_regular)
}
None => (n_fit(room, large, max_basic_streams).max(1), 0, 0),
};
let (regular, n_regular) = if n_regular == 0 && n_large < max_basic_streams {
let left = room.saturating_sub(n_large * large);
candidates().find(|&s| s <= left).map_or((regular, 0), |spare| (spare, 1))
} else {
(regular, n_regular)
};
let mut basic = vec![StreamClass { size: large, count: n_large }];
if n_regular > 0 {
basic.push(StreamClass { size: regular, count: n_regular });
}
let spent = n_large * large + n_regular * regular;
debug_assert!(spent <= budget, "carved {spent} out of a {budget} budget");
let remaining = budget - spent;
let n_pool = n_fit(remaining, pool_size, max_recursive_streams);
let shared_workers = max_recursive_streams.min((n_large + n_regular).saturating_sub(1).max(1));
Some(StreamLayout {
basic,
recursive: StreamClass { size: pool_size, count: n_pool },
aggregation_workers: match n_pool {
0 if recursive_size > 0 => shared_workers,
n => n,
},
unused: remaining - n_pool * pool_size,
})
}
#[cfg(test)]
mod tests {
use super::*;
const ZISK_BASIC_GB: &[f64] = &[14.57, 6.45, 6.26, 5.82, 5.57, 4.74, 4.52, 4.43, 3.82, 2.63, 1.47, 0.75];
const ZISK_COMPRESSOR_GB: f64 = 6.24;
const ZISK_RECURSIVE_GB: f64 = 1.33;
const ZISK_BUDGET_GB: f64 = 25.23;
fn gb(v: f64) -> usize {
(v * (1 << 30) as f64 / 8.0) as usize
}
fn zisk_layout(budget_gb: f64, max_basic: usize, max_recursive: usize) -> StreamLayout {
let sizes: Vec<usize> = ZISK_BASIC_GB.iter().copied().map(gb).collect();
plan_stream_layout(
gb(budget_gb),
&sizes,
gb(ZISK_COMPRESSOR_GB),
gb(ZISK_RECURSIVE_GB),
max_basic,
max_recursive,
)
.expect("zisk budget holds the largest air")
}
#[test]
fn the_input_order_of_the_airs_does_not_matter() {
let sorted: Vec<usize> = ZISK_BASIC_GB.iter().copied().map(gb).collect();
let ascending: Vec<usize> = sorted.iter().rev().copied().collect();
let mut shuffled = sorted.clone();
shuffled.rotate_left(5);
for budget in [20.0, 26.15, 33.0, 40.0, 60.0, 80.0, 200.0] {
let plan = |sizes: &[usize]| {
plan_stream_layout(gb(budget), sizes, gb(ZISK_COMPRESSOR_GB), gb(ZISK_RECURSIVE_GB), 16, 10)
};
let expected = plan(&sorted);
assert_eq!(plan(&ascending), expected, "budget {budget}: ascending input carved differently");
assert_eq!(plan(&shuffled), expected, "budget {budget}: shuffled input carved differently");
}
}
#[test]
fn the_carve_never_has_more_than_two_sizes() {
let uneven = [gb(14.5), gb(12.0), gb(9.1), gb(7.0), gb(3.2)];
for budget in [20.0, 26.15, 33.0, 40.0, 60.0, 80.0, 200.0] {
for sizes in [&ZISK_BASIC_GB.iter().copied().map(gb).collect::<Vec<_>>()[..], &uneven[..]] {
let layout =
plan_stream_layout(gb(budget), sizes, gb(ZISK_COMPRESSOR_GB), gb(ZISK_RECURSIVE_GB), 16, 10);
let Some(layout) = layout else { continue };
assert!(layout.basic.len() <= 2, "budget {budget}: {:?}", layout.basic);
}
}
}
#[test]
fn a_roomy_budget_goes_uniform() {
let layout = zisk_layout(120.0, 4, 10);
assert_eq!(layout.basic, vec![StreamClass { size: gb(14.57), count: 4 }], "{:?}", layout.basic);
}
#[test]
fn the_classes_grow_together() {
let layout = zisk_layout(70.0, 16, 10);
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.57), count: 3 }, StreamClass { size: gb(6.45), count: 3 }]
);
}
#[test]
fn the_remainder_buys_the_largest_stream_that_still_fits() {
let layout = zisk_layout(40.0, 16, 10);
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.57), count: 2 }, StreamClass { size: gb(6.45), count: 1 }],
"a large stream fitted in the remainder: {:?}",
layout.basic
);
let layout = zisk_layout(33.0, 16, 10);
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.57), count: 1 }, StreamClass { size: gb(6.45), count: 2 }],
"only a regular one fitted: {:?}",
layout.basic
);
}
#[test]
fn the_regular_size_does_not_drift_with_the_budget() {
for budget in [ZISK_BUDGET_GB, 33.0, 40.0, 60.0, 80.0] {
let layout = zisk_layout(budget, 16, 10);
assert_eq!(layout.basic[1].size, gb(6.45), "budget {budget}: {:?}", layout.basic);
}
}
#[test]
fn basic_classes_are_funded_before_recursive_streams() {
let layout = zisk_layout(ZISK_BUDGET_GB, 16, 10);
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.57), count: 1 }, StreamClass { size: gb(6.45), count: 1 }]
);
assert!(layout.basic[1].size >= gb(ZISK_COMPRESSOR_GB), "every basic class must hold a compressor");
assert!(layout.recursive.count >= 3, "aggregation still gets the remainder: {:?}", layout.recursive);
}
#[test]
fn preloaded_sizes_put_the_second_class_at_the_compressor_floor() {
let sizes = [gb(14.57), gb(5.9), gb(3.8), gb(2.6), gb(0.75)];
let layout =
plan_stream_layout(gb(26.15), &sizes, gb(ZISK_COMPRESSOR_GB), gb(ZISK_RECURSIVE_GB), 16, 10).unwrap();
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.57), count: 1 }, StreamClass { size: gb(ZISK_COMPRESSOR_GB), count: 1 }]
);
assert_eq!(layout.recursive.count, 4);
}
#[test]
fn a_large_budget_still_gets_recursive_streams() {
let sizes = [gb(14.5), gb(12.0), gb(6.23), gb(3.8), gb(1.5)];
for budget in [33.0, 40.0, 80.0] {
let layout = plan_stream_layout(gb(budget), &sizes, gb(6.24), gb(ZISK_RECURSIVE_GB), 16, 10).unwrap();
assert!(
layout.recursive.count >= RECURSIVE_STREAM_FLOOR,
"budget {budget}: {:?} / {:?}",
layout.basic,
layout.recursive
);
}
}
#[test]
fn the_recursive_cap_still_bounds_the_remainder() {
let layout = zisk_layout(ZISK_BUDGET_GB, 16, 1);
assert_eq!(layout.recursive.count, 1);
assert!(layout.unused > 0);
}
#[test]
fn a_larger_budget_cuts_more_classes() {
let layout = zisk_layout(80.0, 16, 8);
assert!(layout.n_basic_streams() >= 4, "got {:?}", layout.basic);
let capped = zisk_layout(80.0, 3, 8);
assert_eq!(capped.n_basic_streams(), 3);
assert_eq!(capped.recursive.count, 8);
}
#[test]
fn classes_never_fall_below_the_compressor_floor() {
let layout = zisk_layout(ZISK_BUDGET_GB, 16, 0);
for class in &layout.basic {
assert!(class.size >= gb(ZISK_COMPRESSOR_GB), "class {class:?} cannot host a compressor");
}
}
#[test]
fn a_no_aggregation_carve_still_hosts_the_airs() {
let sizes: Vec<usize> = ZISK_BASIC_GB.iter().copied().map(gb).collect();
let layout = plan_stream_layout(gb(29.05), &sizes, 0, 0, 20, 0).expect("budget holds the largest air");
assert!(layout.n_basic_streams() >= 2, "no second stream to overlap with: {:?}", layout.basic);
assert_eq!(layout.basic[1].size, gb(6.45), "second class cannot host the bulk airs: {:?}", layout.basic);
}
#[test]
fn coverage_never_costs_the_second_class() {
let sizes = [gb(14.5), gb(12.0), gb(3.2), gb(3.2), gb(3.2)];
let layout = plan_stream_layout(gb(26.0), &sizes, 0, 0, 16, 0).expect("budget holds the largest air");
assert!(layout.n_basic_streams() >= 2, "fallback did not engage: {:?}", layout.basic);
}
#[test]
fn the_basic_stream_cap_is_respected() {
let layout = zisk_layout(80.0, 2, 0);
assert_eq!(layout.n_basic_streams(), 2);
}
#[test]
fn leftover_is_smaller_than_any_further_class() {
let layout = zisk_layout(ZISK_BUDGET_GB, 16, 3);
assert!(layout.unused < gb(ZISK_COMPRESSOR_GB));
}
#[test]
fn carve_shape_with_a_second_near_outlier() {
let sizes = [gb(14.5), gb(12.0), gb(6.23), gb(3.8), gb(1.5)];
for budget in [20.0, 26.15, 33.0, 40.0, 80.0] {
let layout = plan_stream_layout(gb(budget), &sizes, gb(6.24), gb(ZISK_RECURSIVE_GB), 16, 10).unwrap();
let classes: Vec<String> = layout
.basic
.iter()
.map(|c| format!("{} x {:.2}", c.count, c.size as f64 * 8.0 / (1 << 30) as f64))
.collect();
println!(
"budget {budget:>6.2} -> basic [{}] recursive {} unused {:.2}",
classes.join(" + "),
layout.recursive.count,
layout.unused as f64 * 8.0 / (1 << 30) as f64,
);
}
}
#[test]
fn an_air_too_big_for_the_regular_class_falls_back_to_the_large_one() {
let sizes = [gb(14.5), gb(12.0), gb(6.23)];
let layout = plan_stream_layout(gb(26.15), &sizes, gb(6.24), gb(ZISK_RECURSIVE_GB), 16, 10).unwrap();
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.5), count: 1 }, StreamClass { size: gb(6.24), count: 1 }]
);
assert!(layout.basic[1].size >= gb(6.23));
assert!(layout.recursive.count >= 3);
}
#[test]
fn a_split_that_buys_no_stream_loses_to_uniform() {
let sizes: Vec<usize> =
[7.42, 5.99, 5.82, 5.57, 5.31, 4.52, 4.43, 3.82, 2.60, 1.47, 0.75].iter().map(|&g| gb(g)).collect();
let layout = plan_stream_layout(gb(26.18), &sizes, gb(6.24), gb(1.33), 16, 10).unwrap();
assert_eq!(layout.basic, vec![StreamClass { size: gb(7.42), count: 3 }], "should be uniform");
assert!(sizes.iter().all(|&s| s <= layout.basic[0].size));
assert!(layout.recursive.count >= 1, "aggregation must keep at least the floor");
}
#[test]
fn a_gap_below_half_puts_the_regular_class_above_it() {
let sizes: Vec<usize> = [14.57f64, 8.0, 0.75].iter().copied().map(gb).collect();
for (budget, want) in [(26.0, (1, 1)), (33.0, (1, 2)), (60.0, (3, 2)), (80.0, (4, 2))] {
let layout = plan_stream_layout(gb(budget), &sizes, 0, 0, 20, 0).expect("budget holds the largest air");
let (n_large, n_regular) = want;
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.57), count: n_large }, StreamClass { size: gb(8.0), count: n_regular }],
"budget {budget}: {:?}",
layout.basic
);
}
}
#[test]
fn a_class_the_bulk_cannot_use_is_worth_one_spare_stream() {
let sizes: Vec<usize> = [14.5f64, 12.0, 12.0, 12.0, 1.0].iter().copied().map(gb).collect();
let layout = plan_stream_layout(gb(26.0), &sizes, 0, 0, 20, 0).expect("budget holds the largest air");
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.5), count: 1 }, StreamClass { size: gb(1.0), count: 1 }],
"{:?}",
layout.basic
);
}
#[test]
fn a_buyable_regular_is_bought_before_the_last_one_is_traded_up() {
let sizes: Vec<usize> = [14.5f64, 12.0, 12.0, 12.0, 1.0].iter().copied().map(gb).collect();
let layout = plan_stream_layout(gb(40.0), &sizes, 0, 0, 20, 0).expect("budget holds the largest air");
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.5), count: 1 }, StreamClass { size: gb(12.0), count: 2 }],
"{:?}",
layout.basic
);
}
#[test]
fn added_precompiles_do_not_flip_the_carve_to_a_tiny_fleet() {
let sizes: Vec<usize> = [14.5f64, 12.0, 12.0, 12.0, 1.0, 1.0, 1.0, 1.0].iter().copied().map(gb).collect();
let layout = plan_stream_layout(gb(40.0), &sizes, 0, 0, 20, 0).expect("budget holds the largest air");
assert_eq!(layout.basic[1].size, gb(12.0), "a tiny fleet was bought: {:?}", layout.basic);
}
#[test]
fn a_cap_bound_carve_trades_its_regulars_up() {
let layout = zisk_layout(55.0, 4, 0);
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.57), count: 3 }, StreamClass { size: gb(6.45), count: 1 }],
"{:?}",
layout.basic
);
assert!(layout.unused < gb(14.57 - 6.45), "another trade-up was affordable: {layout:?}");
}
const ZISK_KEY_GB: &[f64] = &[
12.81, 5.99, 5.82, 5.69, 5.32, 4.52, 4.52, 4.43, 4.43, 4.32, 4.19, 4.07, 3.99, 3.89, 3.82, 3.65, 3.63, 3.63,
3.24, 3.09, 2.94, 2.91, 2.88, 2.60, 2.60, 2.47, 2.36, 2.35, 2.28, 2.13, 2.08, 1.94, 1.92, 1.79, 1.79, 1.65,
1.47, 1.43, 1.23, 1.17, 1.11, 0.74, 0.63,
];
#[test]
fn the_regular_class_is_not_shaved_below_the_band_it_serves() {
let sizes: Vec<usize> = ZISK_KEY_GB.iter().copied().map(gb).collect();
for (budget, want) in [(28.21, (1, 2)), (40.0, (2, 2)), (60.0, (3, 3))] {
let layout = plan_stream_layout(gb(budget), &sizes, 0, 0, 16, 0).expect("budget holds the largest air");
let (n_large, n_regular) = want;
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(12.81), count: n_large }, StreamClass { size: gb(5.99), count: n_regular }],
"budget {budget}: {:?}",
layout.basic
);
}
}
#[test]
fn blake3_sized_recursion_shares_the_basic_streams() {
let sizes: Vec<usize> = ZISK_BASIC_GB.iter().copied().map(gb).collect();
for (budget, want_basic) in [(25.23, 2), (33.0, 3), (40.0, 3), (60.0, 5)] {
let layout = plan_stream_layout(gb(budget), &sizes, gb(7.5), gb(7.5), 16, 10).unwrap();
assert_eq!(layout.recursive.count, 0, "budget {budget}: pool not dropped: {:?}", layout.recursive);
assert_eq!(layout.n_basic_streams(), want_basic, "budget {budget}: {:?}", layout.basic);
assert_eq!(layout.aggregation_workers, want_basic - 1, "budget {budget}: {layout:?}");
assert!(layout.aggregation_workers < layout.n_basic_streams(), "basics can stall: {layout:?}");
assert!(layout.basic.iter().all(|c| c.size >= gb(7.5)), "{:?}", layout.basic);
}
}
#[test]
fn one_more_dust_air_does_not_change_the_carve() {
let with_dust: Vec<usize> =
[14.5f64, 12.0, 12.0, 12.0, 1.0, 1.0, 1.0, 1.0, 1.0].iter().copied().map(gb).collect();
let without: Vec<usize> = [14.5f64, 12.0, 12.0, 12.0, 1.0, 1.0, 1.0, 1.0].iter().copied().map(gb).collect();
for budget in [40.0, 60.0] {
let a = plan_stream_layout(gb(budget), &with_dust, 0, 0, 20, 0).unwrap();
let b = plan_stream_layout(gb(budget), &without, 0, 0, 20, 0).unwrap();
assert_eq!(a.basic, b.basic, "budget {budget}: one dust air moved the carve: {:?}", a.basic);
assert!(a.basic.iter().all(|c| c.count == 1 || c.size >= gb(12.0)), "{:?}", a.basic);
}
let layout = plan_stream_layout(gb(40.0), &with_dust, 0, 0, 20, 0).unwrap();
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.5), count: 1 }, StreamClass { size: gb(12.0), count: 2 }],
"{:?}",
layout.basic
);
assert!(layout.unused < gb(2.0), "budget left for dead: {layout:?}");
}
#[test]
fn a_pool_the_budget_cannot_fund_still_gets_workers() {
let sizes = [gb(14.57), gb(6.45)];
let layout = plan_stream_layout(gb(15.0), &sizes, gb(6.24), gb(1.33), 16, 10).unwrap();
assert_eq!(layout.recursive.count, 0, "{layout:?}");
assert!(layout.aggregation_workers > 0, "aggregation left with nothing: {layout:?}");
}
#[test]
fn a_cheap_recursive_class_keeps_its_own_streams() {
let layout = zisk_layout(ZISK_BUDGET_GB, 16, 10);
assert!(layout.recursive.count >= 3, "cheap pool must survive: {:?}", layout.recursive);
assert_eq!(layout.aggregation_workers, layout.recursive.count, "one worker per dedicated stream");
}
#[test]
fn an_oversized_recursive_launch_raises_the_floor() {
let sizes: Vec<usize> = ZISK_BASIC_GB.iter().copied().map(gb).collect();
let layout = plan_stream_layout(gb(60.0), &sizes, gb(6.24), gb(16.0), 16, 10).unwrap();
assert!(layout.basic.iter().all(|c| c.size >= gb(16.0)), "no home for the launch: {:?}", layout.basic);
assert!(layout.aggregation_workers > 0, "and it keeps its priority: {layout:?}");
assert!(plan_stream_layout(gb(15.0), &sizes, gb(6.24), gb(16.0), 16, 10).is_none());
}
#[test]
fn an_all_large_carve_spends_its_change_on_a_spare() {
let sizes: Vec<usize> = [14.0f64, 12.0, 8.0].iter().copied().map(gb).collect();
for (budget, want) in [(36.0, Some(2)), (50.0, Some(3)), (30.0, None), (44.0, None)] {
let layout = plan_stream_layout(gb(budget), &sizes, 0, 0, 16, 0).unwrap();
match want {
Some(n_large) => assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.0), count: n_large }, StreamClass { size: gb(8.0), count: 1 }],
"budget {budget}: {:?}",
layout.basic
),
None => assert_eq!(layout.basic.len(), 1, "budget {budget}: {:?}", layout.basic),
}
}
}
#[test]
fn too_small_a_budget_is_rejected() {
let sizes = [gb(14.57)];
assert!(plan_stream_layout(gb(10.0), &sizes, gb(6.24), gb(1.33), 16, 8).is_none());
}
#[test]
fn first_class_covers_both_the_largest_air_and_the_compressor() {
let layout = plan_stream_layout(gb(40.0), &[gb(2.0)], gb(9.0), gb(1.0), 16, 0).unwrap();
assert_eq!(layout.basic[0].size, gb(9.0));
let layout = plan_stream_layout(gb(40.0), &[gb(12.0)], gb(9.0), gb(1.0), 16, 0).unwrap();
assert_eq!(layout.basic[0].size, gb(12.0));
}
#[test]
fn stream_sizes_expand_classes_largest_first() {
let layout = StreamLayout {
basic: vec![StreamClass { size: 9, count: 1 }, StreamClass { size: 4, count: 2 }],
recursive: StreamClass { size: 1, count: 3 },
aggregation_workers: 3,
unused: 0,
};
assert_eq!(layout.basic_stream_sizes(), vec![9, 4, 4]);
assert_eq!(layout.n_basic_streams(), 3);
}
fn hosted(class: &StreamClass, sizes: &[usize]) -> usize {
sizes.iter().filter(|&&s| s <= class.size).count()
}
#[test]
fn a_near_outlier_rides_the_large_class_rather_than_taking_one() {
let sizes = [gb(14.5), gb(12.0), gb(6.23), gb(5.9), gb(5.4), gb(3.8), gb(2.6), gb(1.5)];
let layout = plan_stream_layout(gb(40.0), &sizes, 0, 0, 20, 0).unwrap();
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.5), count: 2 }, StreamClass { size: gb(6.23), count: 1 }]
);
}
#[test]
fn aggregation_does_not_change_which_size_the_regular_class_takes() {
let sizes = [gb(14.5), gb(12.0), gb(6.23), gb(5.9), gb(5.4), gb(3.8), gb(2.6), gb(1.5)];
let layout = plan_stream_layout(gb(29.05), &sizes, gb(6.24), gb(1.33), 20, 10).unwrap();
assert_eq!(
layout.basic,
vec![StreamClass { size: gb(14.5), count: 1 }, StreamClass { size: gb(6.24), count: 2 }]
);
}
#[test]
fn no_carve_buys_a_fleet_below_half_the_airs() {
let shapes: [&[f64]; 5] = [
&[14.5, 12.0, 6.23, 5.9, 5.4, 3.8, 2.6, 1.5],
ZISK_BASIC_GB,
&[14.57, 5.9, 3.8, 2.6, 0.75],
&[14.57, 8.0, 0.75],
&[14.5, 12.0, 12.0, 12.0, 1.0, 1.0, 1.0, 1.0],
];
for shape in shapes {
let sizes: Vec<usize> = shape.iter().copied().map(gb).collect();
for budget in [26.0, 26.15, 29.05, 33.0, 40.0, 60.0, 80.0] {
for (floor, rec, max_rec) in [(0.0, 0.0, 0), (ZISK_COMPRESSOR_GB, ZISK_RECURSIVE_GB, 10)] {
let layout = plan_stream_layout(gb(budget), &sizes, gb(floor), gb(rec), 20, max_rec).unwrap();
for class in layout.basic.iter().filter(|c| c.count > 1) {
assert!(
hosted(class, &sizes) * 2 >= sizes.len(),
"budget {budget} floor {floor}: class {class:?} hosts {} of {}: {:?}",
hosted(class, &sizes),
sizes.len(),
layout.basic
);
}
}
}
}
}
#[test]
fn no_carve_ever_overspends_its_budget() {
let shapes: [&[f64]; 6] = [
ZISK_KEY_GB,
ZISK_BASIC_GB,
&[14.5, 12.0, 6.23, 5.9, 5.4, 3.8, 2.6, 1.5],
&[7.42, 5.99, 5.82, 5.57, 5.31, 4.52, 0.75],
&[14.57, 8.0, 0.75],
&[14.5, 12.0, 12.0, 12.0, 1.0, 1.0, 1.0, 1.0],
];
for shape in shapes {
let sizes: Vec<usize> = shape.iter().copied().map(gb).collect();
for budget in [14.6, 20.0, 26.15, 28.21, 33.0, 40.0, 60.0, 80.0, 200.0] {
for (floor, rec) in [(0.0, 0.0), (ZISK_COMPRESSOR_GB, ZISK_RECURSIVE_GB), (9.0, 0.5)] {
for (max_basic, max_rec) in [(1, 0), (2, 1), (4, 8), (16, 10), (20, 0)] {
let Some(layout) =
plan_stream_layout(gb(budget), &sizes, gb(floor), gb(rec), max_basic, max_rec)
else {
continue;
};
let where_ =
format!("shape {shape:?} budget {budget} floor {floor} caps {max_basic}/{max_rec}");
let spent: usize = layout.basic.iter().map(|c| c.size * c.count).sum::<usize>()
+ layout.recursive.size * layout.recursive.count;
assert_eq!(spent + layout.unused, gb(budget), "{where_}: {layout:?}");
assert!(!layout.basic.is_empty(), "{where_}: no basic class");
assert_eq!(layout.basic[0].size, sizes[0].max(gb(floor)), "{where_}: {:?}", layout.basic);
assert!(layout.n_basic_streams() <= max_basic, "{where_}: {:?}", layout.basic);
assert!(layout.recursive.count <= max_rec, "{where_}: {:?}", layout.recursive);
assert!(layout.aggregation_workers <= max_rec, "{where_}: {layout:?}");
assert!(
rec == 0.0 || max_rec == 0 || layout.recursive.count > 0 || layout.aggregation_workers > 0,
"{where_}: aggregation has neither streams nor workers: {layout:?}"
);
assert!(
layout.basic.iter().all(|c| c.size >= gb(floor) && c.count > 0),
"{where_}: {:?}",
layout.basic
);
}
}
}
}
}
}