#[cfg(test)]
#[path = "../../../tests/unit/models/solution/tour_test.rs"]
mod tour_test;
use crate::models::common::Schedule;
use crate::models::problem::{Actor, Job};
use crate::models::solution::{Activity, Place};
use crate::models::OP_START_MSG;
use hashbrown::HashSet;
use std::iter::once;
use std::ops::RangeBounds;
use std::slice::{Iter, IterMut};
pub struct Tour {
activities: Vec<Activity>,
jobs: HashSet<Job>,
is_closed: bool,
}
impl Default for Tour {
fn default() -> Self {
Tour { activities: Default::default(), jobs: Default::default(), is_closed: false }
}
}
impl Tour {
pub fn new(actor: &Actor) -> Self {
let mut tour = Self::default();
tour.set_start(create_start_activity(&actor));
create_end_activity(&actor).map(|end| tour.set_end(end));
tour
}
pub fn set_start(&mut self, activity: Activity) -> &mut Tour {
assert!(activity.job.is_none());
assert!(self.activities.is_empty());
self.activities.push(activity);
self
}
pub fn set_end(&mut self, activity: Activity) -> &mut Tour {
assert!(activity.job.is_none());
assert!(!self.activities.is_empty());
self.activities.push(activity);
self.is_closed = true;
self
}
pub fn insert_last(&mut self, activity: Activity) -> &mut Tour {
self.insert_at(activity, self.activity_count() + 1);
self
}
pub fn insert_at(&mut self, activity: Activity, index: usize) -> &mut Tour {
assert!(activity.job.is_some());
assert!(!self.activities.is_empty());
self.jobs.insert(activity.retrieve_job().unwrap());
self.activities.insert(index, activity);
self
}
pub fn remove(&mut self, job: &Job) -> bool {
self.activities.retain(|a| !a.has_same_job(job));
self.jobs.remove(job)
}
pub fn remove_activity_at(&mut self, idx: usize) -> Job {
let job = self
.activities
.get(idx)
.and_then(|a| a.retrieve_job())
.expect("Attempt to remove activity without job from the tour!");
self.remove(&job);
job
}
pub fn remove_activities_at<R>(&mut self, range: R) -> Vec<Job>
where
R: RangeBounds<usize>,
{
let jobs: Vec<_> = self
.activities
.drain(range)
.map(|a| a.retrieve_job().expect("Attempt to remove activity without job from the tour!"))
.collect();
jobs.iter().for_each(|job| {
self.remove(job);
});
jobs
}
pub fn all_activities(&self) -> Iter<Activity> {
self.activities.iter()
}
pub fn activities_slice(&self, start: usize, end: usize) -> &[Activity] {
&self.activities[start..=end]
}
pub fn all_activities_mut(&mut self) -> IterMut<Activity> {
self.activities.iter_mut()
}
pub fn job_activities<'a>(&'a self, job: &'a Job) -> impl Iterator<Item = &Activity> + 'a {
self.activities.iter().filter(move |a| a.has_same_job(job))
}
pub fn legs<'a>(&'a self) -> Box<dyn Iterator<Item = (&'a [Activity], usize)> + 'a> {
let last_index = self.activities.len() - 1;
let window_size = if last_index == 0 { 1 } else { 2 };
let legs = self.activities.windows(window_size).zip(0_usize..);
let is_open_tour_with_jobs = !self.is_closed && last_index > 0;
if is_open_tour_with_jobs {
Box::new(legs.chain(once((&self.activities[last_index..], last_index))))
} else {
Box::new(legs)
}
}
pub fn jobs<'a>(&'a self) -> impl Iterator<Item = Job> + 'a {
self.jobs.iter().cloned()
}
pub fn get(&self, index: usize) -> Option<&Activity> {
self.activities.get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut Activity> {
self.activities.get_mut(index)
}
pub fn start(&self) -> Option<&Activity> {
self.activities.first()
}
pub fn end(&self) -> Option<&Activity> {
self.activities.last()
}
pub fn contains(&self, job: &Job) -> bool {
self.jobs.contains(job)
}
pub fn index(&self, job: &Job) -> Option<usize> {
self.activities.iter().position(move |a| a.has_same_job(&job))
}
pub fn has_jobs(&self) -> bool {
!self.jobs.is_empty()
}
pub fn activity_count(&self) -> usize {
if self.activities.is_empty() {
0
} else {
self.activities.len() - (if self.is_closed { 2 } else { 1 })
}
}
pub fn total(&self) -> usize {
self.activities.len()
}
pub fn job_count(&self) -> usize {
self.jobs.len()
}
pub fn deep_copy(&self) -> Tour {
Tour {
activities: self.activities.iter().map(|a| a.deep_copy()).collect(),
jobs: self.jobs.iter().cloned().collect(),
is_closed: self.is_closed,
}
}
}
fn create_start_activity(actor: &Actor) -> Activity {
let start = &actor.detail.start.as_ref().unwrap_or_else(|| unimplemented!("{}", OP_START_MSG));
let time = start.time.to_time_window();
Activity {
schedule: Schedule { arrival: time.start, departure: time.start },
place: Place { location: start.location, duration: 0.0, time },
job: None,
}
}
fn create_end_activity(actor: &Actor) -> Option<Activity> {
actor.detail.end.as_ref().map(|place| {
let time = place.time.to_time_window();
Activity {
schedule: Schedule { arrival: time.start, departure: time.start },
place: Place { location: place.location, duration: 0.0, time },
job: None,
}
})
}