use crate::model::{Expected, ModelSpec};
use microlp::Problem;
use std::time::Duration;
mod incremental;
mod lp_random;
mod lp_unit;
mod milp_families;
mod milp_random;
mod milp_unit;
mod milpbench;
mod milpbench_xhard;
mod miplib;
mod netlib;
mod warm_restart;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Tier {
Easy,
Medium,
Hard,
XHard,
}
impl Tier {
pub fn label(self) -> &'static str {
match self {
Tier::Easy => "easy",
Tier::Medium => "medium",
Tier::Hard => "hard",
Tier::XHard => "xhard",
}
}
fn all() -> [Tier; 4] {
[Tier::Easy, Tier::Medium, Tier::Hard, Tier::XHard]
}
}
pub fn locate(sub: &str, file: &str) -> (std::path::PathBuf, Tier) {
let data = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("suite")
.join("data");
for tier in Tier::all() {
let dir = data.join(tier.label()).join(sub);
let path = dir.join(file);
if path.is_file() {
return (path, tier);
}
let gz_path = dir.join(format!("{}.gz", file));
if gz_path.is_file() {
return (gz_path, tier);
}
}
(data.join("medium").join(sub).join(file), Tier::Medium)
}
pub fn read_instance(path: &std::path::Path) -> Result<String, String> {
let err = |e: &dyn std::fmt::Display| format!("cannot read {}: {}", path.display(), e);
if path.extension().is_some_and(|ext| ext == "gz") {
let file = std::fs::File::open(path).map_err(|e| err(&e))?;
let mut text = String::new();
std::io::Read::read_to_string(
&mut flate2::read::GzDecoder::new(std::io::BufReader::new(file)),
&mut text,
)
.map_err(|e| err(&e))?;
Ok(text)
} else {
std::fs::read_to_string(path).map_err(|e| err(&e))
}
}
pub enum CaseRun {
#[allow(clippy::type_complexity)]
Solve(Box<dyn Fn() -> Result<(ModelSpec, Problem, Expected), String> + Send + Sync>),
Custom(Box<dyn Fn(Duration) -> Result<(), String> + Send + Sync>),
}
pub struct Case {
pub name: String,
pub tier: Tier,
pub budget: Duration,
pub run: CaseRun,
}
impl Case {
pub fn solve(
name: impl Into<String>,
tier: Tier,
budget_secs: u64,
build: impl Fn() -> Result<(ModelSpec, Problem, Expected), String> + Send + Sync + 'static,
) -> Case {
Case {
name: name.into(),
tier,
budget: Duration::from_secs(budget_secs),
run: CaseRun::Solve(Box::new(build)),
}
}
pub fn custom(
name: impl Into<String>,
tier: Tier,
budget_secs: u64,
run: impl Fn(Duration) -> Result<(), String> + Send + Sync + 'static,
) -> Case {
Case {
name: name.into(),
tier,
budget: Duration::from_secs(budget_secs),
run: CaseRun::Custom(Box::new(run)),
}
}
}
pub fn all() -> Vec<Case> {
let mut cases = vec![];
lp_unit::register(&mut cases);
lp_random::register(&mut cases);
milp_unit::register(&mut cases);
milp_random::register(&mut cases);
milp_families::register(&mut cases);
incremental::register(&mut cases);
netlib::register(&mut cases);
miplib::register(&mut cases);
milpbench::register(&mut cases);
milpbench_xhard::register(&mut cases);
warm_restart::register(&mut cases);
let mut seen = std::collections::BTreeSet::new();
for c in &cases {
assert!(
seen.insert(c.name.clone()),
"duplicate case name {}",
c.name
);
}
cases
}