use crate::heuristic::r#move::CutPoint;
pub struct CutCombinationIterator {
k: usize,
len: usize,
min_seg: usize,
entity_idx: usize,
positions: Vec<usize>,
done: bool,
}
impl CutCombinationIterator {
pub fn new(k: usize, len: usize, min_seg: usize, entity_idx: usize) -> Self {
let min_len = (k + 1) * min_seg;
if len < min_len {
return Self {
k,
len,
min_seg,
entity_idx,
positions: vec![],
done: true,
};
}
let mut positions = Vec::with_capacity(k);
for i in 0..k {
positions.push(min_seg * (i + 1));
}
Self {
k,
len,
min_seg,
entity_idx,
positions,
done: false,
}
}
fn advance(&mut self) -> bool {
if self.done || self.positions.is_empty() {
return false;
}
let k = self.k;
let len = self.len;
let min_seg = self.min_seg;
for i in (0..k).rev() {
let max_pos = len - min_seg * (k - i);
if self.positions[i] < max_pos {
self.positions[i] += 1;
for j in (i + 1)..k {
self.positions[j] = self.positions[j - 1] + min_seg;
}
return true;
}
}
self.done = true;
false
}
}
impl Iterator for CutCombinationIterator {
type Item = Vec<CutPoint>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
let cuts: Vec<CutPoint> = self
.positions
.iter()
.map(|&pos| CutPoint::new(self.entity_idx, pos))
.collect();
self.advance();
Some(cuts)
}
}
pub fn count_cut_combinations(k: usize, len: usize, min_seg: usize) -> usize {
let min_len = (k + 1) * min_seg;
if len < min_len {
return 0;
}
let free_slots = len - min_len + k;
binomial(free_slots, k)
}
pub(crate) fn cut_combination_at(
k: usize,
len: usize,
min_seg: usize,
entity_idx: usize,
mut rank: usize,
) -> Option<Vec<CutPoint>> {
if k == 0 || min_seg == 0 {
return None;
}
let choice_count = len.checked_sub((k + 1).checked_mul(min_seg)?)? + k;
if rank >= binomial(choice_count, k) {
return None;
}
let mut choices = Vec::with_capacity(k);
let mut start = 0;
for position in 0..k {
let remaining = k - position - 1;
let maximum = choice_count - (k - position);
let mut selected = None;
for candidate in start..=maximum {
let suffix_count = binomial(choice_count - candidate - 1, remaining);
if rank < suffix_count {
selected = Some(candidate);
break;
}
rank -= suffix_count;
}
let selected = selected?;
choices.push(selected);
start = selected + 1;
}
Some(
choices
.into_iter()
.enumerate()
.map(|(position, choice)| {
CutPoint::new(entity_idx, choice + min_seg + position * (min_seg - 1))
})
.collect(),
)
}
pub fn binomial(n: usize, k: usize) -> usize {
if k > n {
return 0;
}
if k == 0 || k == n {
return 1;
}
let k = k.min(n - k); let mut result = 1;
for i in 0..k {
result = result * (n - i) / (i + 1);
}
result
}