use crate::error::Result;
use crate::pack::Pack;
use crate::sequence::Sequence;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum PackStrategy {
NextFit,
FirstFit,
BestFit,
WorstFit,
FirstFitDecreasing,
BestFitDecreasing,
FirstFitShuffle,
ModifiedFirstFitDecreasing,
#[default]
OptimizedBestFitDecreasing,
ParallelOptimizedBestFitDecreasing,
Harmonic,
}
impl PackStrategy {
pub fn name(&self) -> &'static str {
match self {
Self::NextFit => "NextFit",
Self::FirstFit => "FirstFit",
Self::BestFit => "BestFit",
Self::WorstFit => "WorstFit",
Self::FirstFitDecreasing => "FirstFitDecreasing",
Self::BestFitDecreasing => "BestFitDecreasing",
Self::FirstFitShuffle => "FirstFitShuffle",
Self::ModifiedFirstFitDecreasing => "MFFD",
Self::OptimizedBestFitDecreasing => "OBFD",
Self::ParallelOptimizedBestFitDecreasing => "OBFDP",
Self::Harmonic => "Harmonic",
}
}
pub fn short_name(&self) -> Option<&'static str> {
match self {
Self::NextFit => Some("NF"),
Self::FirstFit => Some("FF"),
Self::BestFit => Some("BF"),
Self::WorstFit => Some("WF"),
Self::FirstFitDecreasing => Some("FFD"),
Self::BestFitDecreasing => Some("BFD"),
Self::FirstFitShuffle => Some("FFS"),
Self::ModifiedFirstFitDecreasing => Some("MFFD"),
Self::OptimizedBestFitDecreasing => Some("OBFD"),
Self::ParallelOptimizedBestFitDecreasing => Some("OBFDP"),
Self::Harmonic => Some("HK"),
}
}
pub fn from_short_name(name: &str) -> Option<Self> {
match name.to_uppercase().as_str() {
"NF" => Some(Self::NextFit),
"FF" => Some(Self::FirstFit),
"BF" => Some(Self::BestFit),
"WF" => Some(Self::WorstFit),
"FFD" => Some(Self::FirstFitDecreasing),
"BFD" => Some(Self::BestFitDecreasing),
"FFS" => Some(Self::FirstFitShuffle),
"MFFD" => Some(Self::ModifiedFirstFitDecreasing),
"OBFD" => Some(Self::OptimizedBestFitDecreasing),
"OBFDP" => Some(Self::ParallelOptimizedBestFitDecreasing),
"HK" | "HARMONIC" => Some(Self::Harmonic),
_ => None,
}
}
}
pub const ALL_STRATEGIES: [PackStrategy; 11] = [
PackStrategy::NextFit,
PackStrategy::FirstFit,
PackStrategy::BestFit,
PackStrategy::WorstFit,
PackStrategy::FirstFitDecreasing,
PackStrategy::BestFitDecreasing,
PackStrategy::FirstFitShuffle,
PackStrategy::ModifiedFirstFitDecreasing,
PackStrategy::OptimizedBestFitDecreasing,
PackStrategy::ParallelOptimizedBestFitDecreasing,
PackStrategy::Harmonic,
];
pub trait PackingAlgorithm: Send + Sync {
fn pack(&self, sequences: Vec<Sequence>, capacity: usize) -> Result<Vec<Pack>>;
fn name(&self) -> &'static str;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_is_obfd() {
assert_eq!(
PackStrategy::default(),
PackStrategy::OptimizedBestFitDecreasing
);
}
#[test]
fn test_strategy_is_copy() {
let a = PackStrategy::OptimizedBestFitDecreasing;
let b = a; assert_eq!(a, b); }
#[test]
fn test_name_returns_expected_strings() {
assert_eq!(PackStrategy::NextFit.name(), "NextFit");
assert_eq!(PackStrategy::FirstFit.name(), "FirstFit");
assert_eq!(PackStrategy::BestFit.name(), "BestFit");
assert_eq!(PackStrategy::WorstFit.name(), "WorstFit");
assert_eq!(
PackStrategy::FirstFitDecreasing.name(),
"FirstFitDecreasing"
);
assert_eq!(PackStrategy::BestFitDecreasing.name(), "BestFitDecreasing");
assert_eq!(PackStrategy::FirstFitShuffle.name(), "FirstFitShuffle");
assert_eq!(PackStrategy::ModifiedFirstFitDecreasing.name(), "MFFD");
assert_eq!(PackStrategy::OptimizedBestFitDecreasing.name(), "OBFD");
assert_eq!(
PackStrategy::ParallelOptimizedBestFitDecreasing.name(),
"OBFDP"
);
assert_eq!(PackStrategy::Harmonic.name(), "Harmonic");
}
#[test]
fn test_all_variants_have_unique_names() {
let all = ALL_STRATEGIES;
let names: Vec<&str> = all.iter().map(|s| s.name()).collect();
let mut deduped = names.clone();
deduped.sort();
deduped.dedup();
assert_eq!(names.len(), deduped.len(), "duplicate strategy names found");
}
#[test]
fn test_short_name_roundtrip() {
for strategy in ALL_STRATEGIES {
if let Some(short) = strategy.short_name() {
let resolved = PackStrategy::from_short_name(short).unwrap();
assert_eq!(resolved, strategy);
}
}
}
#[test]
fn test_from_short_name_case_insensitive() {
assert_eq!(
PackStrategy::from_short_name("obfd"),
Some(PackStrategy::OptimizedBestFitDecreasing),
);
assert_eq!(
PackStrategy::from_short_name("Ffd"),
Some(PackStrategy::FirstFitDecreasing),
);
}
#[test]
fn test_from_short_name_unknown() {
assert_eq!(PackStrategy::from_short_name("UNKNOWN"), None);
}
}