#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Coord {
pub lat: f64,
pub lon: f64,
}
impl Coord {
#[must_use]
pub const fn new(lat: f64, lon: f64) -> Self {
Self { lat, lon }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct StopId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct VehicleId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LocationId(pub usize);
impl LocationId {
pub const DEPOT: LocationId = LocationId(0);
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TimeWindow {
pub start: f64,
pub end: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Stop {
pub id: StopId,
pub coord: Coord,
pub demand: u32,
pub time_window: Option<TimeWindow>,
pub service_time: f64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Vehicle {
pub id: VehicleId,
pub capacity: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Problem {
pub depot: Coord,
pub stops: Vec<Stop>,
pub vehicles: Vec<Vehicle>,
pub depot_window: Option<TimeWindow>,
}
impl Problem {
#[must_use]
pub fn location_count(&self) -> usize {
self.stops.len() + 1
}
#[must_use]
pub fn coords(&self) -> Vec<Coord> {
let mut coords = Vec::with_capacity(self.location_count());
coords.push(self.depot);
coords.extend(self.stops.iter().map(|s| s.coord));
coords
}
#[must_use]
pub fn depot_open(&self) -> f64 {
self.depot_window.map_or(0.0, |w| w.start)
}
#[must_use]
pub fn depot_due(&self) -> f64 {
self.depot_window.map_or(f64::INFINITY, |w| w.end)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Route {
pub vehicle_id: VehicleId,
pub stop_ids: Vec<StopId>,
pub load: u32,
pub distance: f64,
pub time: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Solution {
pub routes: Vec<Route>,
pub total_distance: f64,
pub total_time: f64,
pub feasible: bool,
pub unassigned: Vec<Unassigned>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Unassigned {
pub stop: StopId,
pub code: UnassignedCode,
pub detail: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnassignedCode {
TimeWindowInfeasible,
CapacityExceeded,
MissingSkill,
MaxTravelTime,
NoCompatibleVehicle,
}
impl UnassignedCode {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
UnassignedCode::TimeWindowInfeasible => "time_window_infeasible",
UnassignedCode::CapacityExceeded => "capacity_exceeded",
UnassignedCode::MissingSkill => "missing_skill",
UnassignedCode::MaxTravelTime => "max_travel_time",
UnassignedCode::NoCompatibleVehicle => "no_compatible_vehicle",
}
}
}