use std::ops::Range;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockRole {
Single,
First,
Middle,
Last,
}
pub fn block_role(rank: u32, world: u32) -> BlockRole {
if world <= 1 {
BlockRole::Single
} else if rank == world - 1 {
BlockRole::First
} else if rank == 0 {
BlockRole::Last
} else {
BlockRole::Middle
}
}
pub fn pipeline_layer_range(num_layers: usize, rank: u32, world: u32) -> Range<usize> {
let world = world.max(1) as usize;
let rank = rank as usize;
assert!(rank < world, "rank {rank} out of range for world {world}");
let per = num_layers / world;
let extra = num_layers % world;
let j = world - 1 - rank;
let size_of = |i: usize| per + if i < extra { 1 } else { 0 };
let start: usize = (0..j).map(size_of).sum();
let len = size_of(j);
start..(start + len)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roles_map_correctly() {
assert_eq!(block_role(0, 1), BlockRole::Single);
assert_eq!(block_role(0, 4), BlockRole::Last);
assert_eq!(block_role(3, 4), BlockRole::First);
assert_eq!(block_role(1, 4), BlockRole::Middle);
assert_eq!(block_role(2, 4), BlockRole::Middle);
}
#[test]
fn even_split_is_reversed() {
assert_eq!(pipeline_layer_range(16, 0, 4), 12..16);
assert_eq!(pipeline_layer_range(16, 1, 4), 8..12);
assert_eq!(pipeline_layer_range(16, 2, 4), 4..8);
assert_eq!(pipeline_layer_range(16, 3, 4), 0..4);
}
#[test]
fn uneven_split_tiles_exactly() {
assert_eq!(pipeline_layer_range(10, 0, 4), 8..10);
assert_eq!(pipeline_layer_range(10, 1, 4), 6..8);
assert_eq!(pipeline_layer_range(10, 2, 4), 3..6);
assert_eq!(pipeline_layer_range(10, 3, 4), 0..3);
let mut covered = [0u8; 10];
for r in 0..4u32 {
for i in pipeline_layer_range(10, r, 4) {
covered[i] += 1;
}
}
assert!(covered.iter().all(|&c| c == 1), "every layer covered once");
}
#[test]
fn single_rank_owns_all() {
assert_eq!(pipeline_layer_range(28, 0, 1), 0..28);
}
#[test]
fn more_ranks_than_layers_is_safe() {
let total: usize = (0..4u32).map(|r| pipeline_layer_range(2, r, 4).len()).sum();
assert_eq!(total, 2);
}
}