use crate::error::{PackError, Result};
use crate::pack::{Bin, Pack, bins_to_packs};
use crate::sequence::{Item, Sequence};
use crate::strategy::PackingAlgorithm;
pub struct NextFit;
impl PackingAlgorithm for NextFit {
fn pack(&self, sequences: Vec<Sequence>, capacity: usize) -> Result<Vec<Pack>> {
let items: Vec<Item> = sequences.iter().map(|s| s.to_item()).collect();
let bins = next_fit_items(&items, capacity)?;
Ok(bins_to_packs(bins, &sequences))
}
fn name(&self) -> &'static str {
"NextFit"
}
}
fn next_fit_items(items: &[Item], capacity: usize) -> Result<Vec<Bin>> {
let mut bins: Vec<Bin> = Vec::new();
let mut current: Option<Bin> = None;
for &item in items {
if item.len > capacity {
return Err(PackError::SequenceTooLong {
length: item.len,
capacity,
});
}
let needs_new_bin = match ¤t {
Some(bin) => bin.remaining() < item.len,
None => true,
};
if needs_new_bin {
if let Some(bin) = current.take() {
bins.push(bin);
}
let mut bin = Bin::new(bins.len(), capacity);
bin.used = item.len;
bin.items.push(item.id);
current = Some(bin);
} else {
let bin = current.as_mut().unwrap();
bin.used += item.len;
bin.items.push(item.id);
}
}
if let Some(bin) = current {
bins.push(bin);
}
Ok(bins)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::validation::validate_solution;
fn seqs(lens: &[usize]) -> Vec<Sequence> {
lens.iter()
.enumerate()
.map(|(id, &len)| Sequence::new(id, len))
.collect()
}
fn items(lens: &[usize]) -> Vec<Item> {
lens.iter()
.enumerate()
.map(|(id, &len)| Item { id, len })
.collect()
}
#[test]
fn test_single_bin_fits() {
let packs = NextFit.pack(seqs(&[3, 3, 4]), 10).unwrap();
assert_eq!(packs.len(), 1);
}
#[test]
fn test_two_bins() {
let itms = items(&[7, 5]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 2);
assert_eq!(&bins[0].items[..], &[0]);
assert_eq!(&bins[1].items[..], &[1]);
}
#[test]
fn test_exact_fill_then_new_bin() {
let itms = items(&[6, 4, 3]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 2);
assert_eq!(&bins[0].items[..], &[0, 1]);
assert_eq!(bins[0].used, 10);
assert_eq!(&bins[1].items[..], &[2]);
}
#[test]
fn test_exact_fill_boundaries() {
let packs = NextFit.pack(seqs(&[10, 10]), 10).unwrap();
assert_eq!(packs.len(), 2);
}
#[test]
fn test_pathological_inefficiency() {
let packs = NextFit.pack(seqs(&[6, 5, 6, 5]), 10).unwrap();
assert_eq!(packs.len(), 4);
}
#[test]
fn test_never_revisits_closed_bins() {
let itms = items(&[8, 3, 2]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 2);
assert_eq!(&bins[0].items[..], &[0]); assert_eq!(&bins[1].items[..], &[1, 2]); assert_eq!(bins[0].remaining(), 2); }
#[test]
fn test_oversize_error() {
let result = NextFit.pack(seqs(&[11]), 10);
assert!(matches!(
result,
Err(PackError::SequenceTooLong {
length: 11,
capacity: 10
})
));
}
#[test]
fn test_oversize_stops_early() {
let itms = items(&[5, 15, 3]);
let result = next_fit_items(&itms, 10);
assert!(result.is_err());
}
#[test]
fn test_empty_input() {
let itms = items(&[]);
let bins = next_fit_items(&itms, 10).unwrap();
assert!(bins.is_empty());
}
#[test]
fn test_single_item() {
let itms = items(&[5]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 1);
assert_eq!(bins[0].used, 5);
assert_eq!(&bins[0].items[..], &[0]);
}
#[test]
fn test_exact_capacity_item() {
let itms = items(&[10]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 1);
assert_eq!(bins[0].remaining(), 0);
}
#[test]
fn test_all_same_size_pairs() {
let itms = items(&[5, 5, 5, 5]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 2);
assert_eq!(&bins[0].items[..], &[0, 1]);
assert_eq!(&bins[1].items[..], &[2, 3]);
}
#[test]
fn test_each_item_needs_own_bin() {
let itms = items(&[6, 7, 8, 9]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 4);
}
#[test]
fn test_many_small_items_one_bin() {
let itms = items(&[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
let bins = next_fit_items(&itms, 10).unwrap();
assert_eq!(bins.len(), 1);
assert_eq!(bins[0].used, 10);
}
#[test]
fn test_validates_basic() {
let itms = items(&[3, 7, 5, 5, 2, 8]);
let bins = next_fit_items(&itms, 10).unwrap();
validate_solution(&itms, &bins, 10).unwrap();
}
#[test]
fn test_validates_pathological() {
let itms = items(&[6, 5, 6, 5]);
let bins = next_fit_items(&itms, 10).unwrap();
validate_solution(&itms, &bins, 10).unwrap();
}
#[test]
fn test_validates_many_items() {
let lens: Vec<usize> = (1..=20).collect();
let itms = items(&lens);
let bins = next_fit_items(&itms, 25).unwrap();
validate_solution(&itms, &bins, 25).unwrap();
}
#[test]
fn test_name() {
assert_eq!(NextFit.name(), "NextFit");
}
#[test]
fn test_pack_through_trait() {
let packs = NextFit.pack(seqs(&[3, 7, 5, 5]), 10).unwrap();
assert_eq!(packs.len(), 2);
}
}