#[derive(Debug, Clone)]
pub struct CutRowMap {
slot_to_row: Vec<Option<usize>>,
base_row_offset: usize,
next_row: usize,
}
impl CutRowMap {
#[must_use]
pub fn new(pool_capacity: usize, base_row_offset: usize) -> Self {
Self {
slot_to_row: vec![None; pool_capacity],
base_row_offset,
next_row: base_row_offset,
}
}
pub fn insert(&mut self, slot: usize) -> usize {
debug_assert!(
slot < self.slot_to_row.len(),
"CutRowMap::insert: slot {slot} >= pool_capacity {}",
self.slot_to_row.len()
);
debug_assert!(
self.slot_to_row[slot].is_none(),
"CutRowMap::insert: slot {slot} is already mapped to LP row {:?}",
self.slot_to_row[slot]
);
let lp_row = self.next_row;
self.slot_to_row[slot] = Some(lp_row);
self.next_row += 1;
lp_row
}
#[must_use]
#[inline]
pub fn lp_row_for_slot(&self, slot: usize) -> Option<usize> {
self.slot_to_row.get(slot).copied().flatten()
}
#[must_use]
#[inline]
pub fn total_cut_rows(&self) -> usize {
self.next_row - self.base_row_offset
}
#[must_use]
#[inline]
pub fn base_row_offset(&self) -> usize {
self.base_row_offset
}
#[must_use]
#[inline]
pub fn next_row(&self) -> usize {
self.next_row
}
pub fn reset(&mut self, pool_capacity: usize, base_row_offset: usize) {
if self.slot_to_row.len() < pool_capacity {
self.slot_to_row.resize(pool_capacity, None);
}
self.slot_to_row.fill(None);
self.base_row_offset = base_row_offset;
self.next_row = base_row_offset;
}
}
#[cfg(test)]
mod tests {
use super::CutRowMap;
#[test]
fn new_creates_empty_map() {
let map = CutRowMap::new(100, 50);
assert_eq!(map.base_row_offset(), 50);
assert_eq!(map.next_row(), 50);
assert_eq!(map.total_cut_rows(), 0);
}
#[test]
fn new_zero_capacity_is_valid() {
let map = CutRowMap::new(0, 10);
assert_eq!(map.total_cut_rows(), 0);
}
#[test]
fn insert_assigns_sequential_rows_from_base_offset() {
let mut map = CutRowMap::new(100, 50);
let r0 = map.insert(7);
assert_eq!(r0, 50);
let r1 = map.insert(3);
assert_eq!(r1, 51);
let r2 = map.insert(42);
assert_eq!(r2, 52);
assert_eq!(map.next_row(), 53);
assert_eq!(map.total_cut_rows(), 3);
}
#[test]
fn insert_records_slot_to_row_mapping() {
let mut map = CutRowMap::new(100, 50);
map.insert(7);
assert_eq!(map.lp_row_for_slot(7), Some(50));
}
#[test]
fn lp_row_for_slot_returns_none_for_unmapped_slot() {
let map = CutRowMap::new(100, 50);
assert_eq!(map.lp_row_for_slot(7), None);
}
#[test]
fn lp_row_for_slot_returns_none_for_out_of_range() {
let map = CutRowMap::new(10, 50);
assert_eq!(map.lp_row_for_slot(999), None);
}
#[test]
fn multiple_inserts_preserve_mappings() {
let mut map = CutRowMap::new(100, 10);
map.insert(5); map.insert(10); map.insert(15); assert_eq!(map.total_cut_rows(), 3);
let row = map.insert(20);
assert_eq!(row, 13);
assert_eq!(map.total_cut_rows(), 4);
assert_eq!(map.lp_row_for_slot(5), Some(10));
assert_eq!(map.lp_row_for_slot(10), Some(11));
assert_eq!(map.lp_row_for_slot(15), Some(12));
assert_eq!(map.lp_row_for_slot(20), Some(13));
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "already mapped")]
fn insert_same_slot_twice_panics_in_debug() {
let mut map = CutRowMap::new(100, 50);
map.insert(0);
map.insert(0); }
#[test]
fn cut_row_map_derives_debug_and_clone() {
let mut map = CutRowMap::new(10, 5);
map.insert(0);
map.insert(3);
let cloned = map.clone();
assert_eq!(cloned.total_cut_rows(), 2);
assert_eq!(cloned.lp_row_for_slot(0), Some(5));
assert_eq!(cloned.lp_row_for_slot(3), Some(6));
let debug_str = format!("{map:?}");
assert!(!debug_str.is_empty());
}
#[test]
fn base_row_offset_zero_works() {
let mut map = CutRowMap::new(10, 0);
let row = map.insert(0);
assert_eq!(row, 0);
}
}