1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use crate::models::common::{Distance, Duration, Location, Schedule, TimeWindow};
use crate::models::problem::{Actor, Job, Multi, Single};
use crate::models::solution::Tour;
use crate::utils::compare_shared;
use rosomaxa::prelude::compare_floats;
use std::cmp::Ordering;
use std::sync::Arc;

/// Specifies an extra commute information to reach the actual place.
#[derive(Clone, Default)]
pub struct Commute {
    /// An commute information to reach place from other location.
    pub forward: CommuteInfo,

    /// An commute information to get out from the place to the next location.
    pub backward: CommuteInfo,
}

/// Commute information.
#[derive(Clone)]
pub struct CommuteInfo {
    /// A previous or next location.
    pub location: Location,

    /// Travelled distance.
    pub distance: Distance,

    /// Travelled duration.
    pub duration: Duration,
}

/// Specifies activity place.
#[derive(Clone, Debug)]
pub struct Place {
    /// Location where activity is performed.
    pub location: Location,

    /// Specifies activity's duration.
    pub duration: Duration,

    /// Specifies activity's time window: an interval when job is allowed to be started.
    pub time: TimeWindow,
}

/// Represents activity which is needed to be performed.
pub struct Activity {
    /// Specifies activity details.
    pub place: Place,

    /// Specifies activity's schedule including commute time.
    pub schedule: Schedule,

    /// Specifies associated job. Empty if it has no association with a single job (e.g. tour start or end).
    /// If single job is part of multi job, then original job can be received via `retrieve_job` method.
    pub job: Option<Arc<Single>>,

    /// An extra commute time to the place.
    pub commute: Option<Commute>,
}

/// Represents a tour performing jobs.
pub struct Route {
    /// An actor associated within route.
    pub actor: Arc<Actor>,

    /// Specifies job tour assigned to this route.
    pub tour: Tour,
}

impl Route {
    /// Returns a deep copy of `Route`.
    pub fn deep_copy(&self) -> Self {
        Self { actor: self.actor.clone(), tour: self.tour.deep_copy() }
    }
}

impl Activity {
    /// Creates an activity with a job.
    pub fn new_with_job(job: Arc<Single>) -> Self {
        Activity {
            place: Place { location: 0, duration: 0.0, time: TimeWindow { start: 0.0, end: f64::MAX } },
            schedule: Schedule { arrival: 0.0, departure: 0.0 },
            job: Some(job),
            commute: None,
        }
    }

    /// Creates a deep copy of `Activity`.
    pub fn deep_copy(&self) -> Self {
        Self {
            place: Place {
                location: self.place.location,
                duration: self.place.duration,
                time: self.place.time.clone(),
            },
            schedule: self.schedule.clone(),
            job: self.job.clone(),
            commute: self.commute.clone(),
        }
    }

    /// Checks whether activity has given job.
    pub fn has_same_job(&self, job: &Job) -> bool {
        match self.retrieve_job() {
            Some(j) => match (&j, job) {
                (Job::Multi(lhs), Job::Multi(rhs)) => compare_shared(lhs, rhs),
                (Job::Single(lhs), Job::Single(rhs)) => compare_shared(lhs, rhs),
                _ => false,
            },
            _ => false,
        }
    }

    /// Returns job if activity has it.
    pub fn retrieve_job(&self) -> Option<Job> {
        match self.job.as_ref() {
            Some(single) => Multi::roots(single).map(Job::Multi).or_else(|| Some(Job::Single(single.clone()))),
            _ => None,
        }
    }
}

impl Commute {
    /// Checks whether there is no distance costs for commute.
    pub fn is_zero_distance(&self) -> bool {
        self.forward.is_zero_distance() & self.backward.is_zero_distance()
    }

    /// Gets total commute duration.
    pub fn duration(&self) -> Duration {
        self.forward.duration + self.backward.duration
    }
}

impl Default for CommuteInfo {
    fn default() -> Self {
        Self { location: 0, distance: 0., duration: 0. }
    }
}

impl CommuteInfo {
    /// Checks whether there is no distance costs for part of commute.
    pub fn is_zero_distance(&self) -> bool {
        let is_zero_distance = compare_floats(self.distance, 0.) == Ordering::Equal;

        if is_zero_distance && compare_floats(self.duration, 0.) != Ordering::Equal {
            unreachable!("expected to have duration to be zero, got: {}", self.duration);
        }

        is_zero_distance
    }
}