use crate::{wrapper::SizedWrapper, Bin, Pack};
pub fn first_fit<T>(bin_size: usize, items: impl IntoIterator<Item = T>) -> Vec<Bin<T>>
where
T: Pack,
{
assert!(bin_size > 0, "Bin size must be greater than 0");
__internal_first_fit(bin_size, items, 1)
}
pub fn first_fit_by_key<T, SizeFunc>(
bin_size: usize,
items: impl IntoIterator<Item = T>,
key_func: SizeFunc,
) -> Vec<Bin<T>>
where
SizeFunc: Fn(&T) -> usize + Clone,
{
assert!(bin_size > 0, "Bin size must be greater than 0");
__internal_first_fit(
bin_size,
items
.into_iter()
.map(|item| SizedWrapper::new(key_func.clone(), item)),
1,
)
.into_iter()
.map(|bin| bin.map(|item| item.take()))
.collect()
}
#[doc(hidden)]
pub(crate) fn __internal_first_fit<T>(
bin_size: usize,
items: impl IntoIterator<Item = T>,
lower_bound: usize,
) -> Vec<Bin<T>>
where
T: Pack,
{
let mut bins = Vec::<Bin<T>>::with_capacity(lower_bound);
bins.push(Bin::with_capacity(bin_size));
for item in items.into_iter() {
match bins
.iter_mut()
.filter(|bin| item.size() <= bin.remaining_capacity)
.next()
{
Some(bin) => bin.add(item),
None => bins.push(Bin::with_item(bin_size, item)),
}
}
bins
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{generate_test_bins, generate_test_set_a};
#[test]
fn it_works() {
let (test_data, bin_size) = generate_test_set_a();
let result = first_fit(bin_size, test_data);
let expected = generate_test_bins(
20,
vec![
vec![1, 1, 1, 1, 3, 4], vec![10, 10], vec![10], vec![19], vec![19], ],
);
assert_eq!(expected, result)
}
#[test]
fn it_works_by_key() {
let (test_data, bin_size) = generate_test_set_a();
let test_data = test_data
.into_iter()
.map(|item| item.make_unpacked())
.collect::<Vec<_>>();
let result = first_fit_by_key(bin_size, test_data, |item| item.size);
let expected: Vec<_> = generate_test_bins(
20,
vec![
vec![1, 1, 1, 1, 3, 4], vec![10, 10], vec![10], vec![19], vec![19], ],
)
.into_iter()
.map(|bin| bin.map(|item| item.make_unpacked()))
.collect();
assert_eq!(expected, result)
}
}