1#[cfg(test)]
2#[path = "../../../tests/unit/models/common/load_test.rs"]
3mod load_test;
4
5use rosomaxa::prelude::{Float, UnwrapValue};
6use std::cmp::Ordering;
7use std::fmt::{Debug, Display, Formatter};
8use std::iter::Sum;
9use std::ops::{Add, ControlFlow, Mul, Sub};
10
11const LOAD_DIMENSION_SIZE: usize = 8;
12
13pub trait Load: Add + Sub + PartialOrd + Copy + Default + Debug + Send + Sync {
15 fn is_not_empty(&self) -> bool;
17
18 fn max_load(self, other: Self) -> Self;
20
21 fn can_fit(&self, other: &Self) -> bool;
23
24 fn ratio(&self, other: &Self) -> Float;
26}
27
28pub trait LoadOps: Load + Add<Output = Self> + Sub<Output = Self> + 'static
30where
31 Self: Sized,
32{
33}
34
35pub struct Demand<T: LoadOps> {
37 pub pickup: (T, T),
39 pub delivery: (T, T),
41}
42
43impl<T: LoadOps> Demand<T> {
44 pub fn get_type(&self) -> DemandType {
46 match (self.delivery.0.is_not_empty(), self.pickup.0.is_not_empty()) {
47 (true, false) => DemandType::Delivery,
48 (false, true) => DemandType::Pickup,
49 (true, true) => DemandType::Mixed,
50 (false, false) if self.delivery.1.is_not_empty() && self.pickup.1.is_not_empty() => DemandType::Dynamic,
51 _ => DemandType::Mixed,
52 }
53 }
54}
55
56pub enum DemandType {
58 Pickup,
60 Delivery,
62 Dynamic,
64 Mixed,
66}
67
68impl<T: LoadOps> Demand<T> {
69 pub fn change(&self) -> T {
71 self.pickup.0 + self.pickup.1 - self.delivery.0 - self.delivery.1
72 }
73}
74
75impl<T: LoadOps> Default for Demand<T> {
76 fn default() -> Self {
77 Self { pickup: (Default::default(), Default::default()), delivery: (Default::default(), Default::default()) }
78 }
79}
80
81impl<T: LoadOps> Clone for Demand<T> {
82 fn clone(&self) -> Self {
83 Self { pickup: self.pickup, delivery: self.delivery }
84 }
85}
86
87impl<T: LoadOps> Add for Demand<T> {
88 type Output = Self;
89
90 fn add(self, rhs: Self) -> Self::Output {
91 Self {
92 pickup: (self.pickup.0 + rhs.pickup.0, self.pickup.1 + rhs.pickup.1),
93 delivery: (self.delivery.0 + rhs.delivery.0, self.delivery.1 + rhs.delivery.1),
94 }
95 }
96}
97
98impl Demand<SingleDimLoad> {
99 pub fn pickup(value: i32) -> Self {
101 Self {
102 pickup: (SingleDimLoad::new(value), SingleDimLoad::default()),
103 delivery: (SingleDimLoad::default(), SingleDimLoad::default()),
104 }
105 }
106
107 pub fn pudo_pickup(value: i32) -> Self {
109 Self {
110 pickup: (SingleDimLoad::default(), SingleDimLoad::new(value)),
111 delivery: (SingleDimLoad::default(), SingleDimLoad::default()),
112 }
113 }
114
115 pub fn delivery(value: i32) -> Self {
117 Self {
118 pickup: (SingleDimLoad::default(), SingleDimLoad::default()),
119 delivery: (SingleDimLoad::new(value), SingleDimLoad::default()),
120 }
121 }
122
123 pub fn pudo_delivery(value: i32) -> Self {
125 Self {
126 pickup: (SingleDimLoad::default(), SingleDimLoad::default()),
127 delivery: (SingleDimLoad::default(), SingleDimLoad::new(value)),
128 }
129 }
130}
131
132#[derive(Clone, Copy, Debug, Default)]
134pub struct SingleDimLoad {
135 pub value: i32,
137}
138
139impl SingleDimLoad {
140 pub fn new(value: i32) -> Self {
142 Self { value }
143 }
144}
145
146impl LoadOps for SingleDimLoad {}
147
148impl Load for SingleDimLoad {
149 fn is_not_empty(&self) -> bool {
150 self.value != 0
151 }
152
153 fn max_load(self, other: Self) -> Self {
154 let value = self.value.max(other.value);
155 Self { value }
156 }
157
158 fn can_fit(&self, other: &Self) -> bool {
159 self.value >= other.value
160 }
161
162 fn ratio(&self, other: &Self) -> Float {
163 self.value as Float / other.value as Float
164 }
165}
166
167impl Add for SingleDimLoad {
168 type Output = Self;
169
170 fn add(self, rhs: Self) -> Self::Output {
171 let value = self.value + rhs.value;
172 Self { value }
173 }
174}
175
176impl Sub for SingleDimLoad {
177 type Output = Self;
178
179 fn sub(self, rhs: Self) -> Self::Output {
180 let value = self.value - rhs.value;
181 Self { value }
182 }
183}
184
185impl Ord for SingleDimLoad {
186 fn cmp(&self, other: &Self) -> Ordering {
187 self.value.cmp(&other.value)
188 }
189}
190
191impl PartialOrd for SingleDimLoad {
192 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
193 Some(self.cmp(other))
194 }
195}
196
197impl Eq for SingleDimLoad {}
198
199impl PartialEq for SingleDimLoad {
200 fn eq(&self, other: &Self) -> bool {
201 self.cmp(other) == Ordering::Equal
202 }
203}
204
205impl Mul<Float> for SingleDimLoad {
206 type Output = Self;
207
208 fn mul(self, value: Float) -> Self::Output {
209 Self::new((self.value as Float * value).round() as i32)
210 }
211}
212
213impl Display for SingleDimLoad {
214 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
215 write!(f, "{}", self.value)
216 }
217}
218
219#[derive(Clone, Copy, Debug)]
221pub struct MultiDimLoad {
222 pub load: [i32; LOAD_DIMENSION_SIZE],
224 pub size: usize,
226}
227
228impl MultiDimLoad {
229 pub fn new(data: Vec<i32>) -> Self {
231 assert!(data.len() <= LOAD_DIMENSION_SIZE);
232
233 let mut load = [0; LOAD_DIMENSION_SIZE];
234 for (idx, value) in data.iter().enumerate() {
235 load[idx] = *value;
236 }
237
238 Self { load, size: data.len() }
239 }
240
241 fn get(&self, idx: usize) -> i32 {
242 self.load[idx]
243 }
244
245 pub fn as_vec(&self) -> Vec<i32> {
247 if self.size == 0 {
248 vec![0]
249 } else {
250 self.load[..self.size].to_vec()
251 }
252 }
253}
254
255impl Load for MultiDimLoad {
256 fn is_not_empty(&self) -> bool {
257 self.size == 0 || self.load.iter().any(|v| *v != 0)
258 }
259
260 fn max_load(self, other: Self) -> Self {
261 let mut result = self;
262 result.load.iter_mut().zip(other.load.iter()).for_each(|(a, b)| *a = (*a).max(*b));
263
264 result
265 }
266
267 fn can_fit(&self, other: &Self) -> bool {
268 self.load.iter().zip(other.load.iter()).all(|(a, b)| a >= b)
269 }
270
271 fn ratio(&self, other: &Self) -> Float {
272 self.load.iter().zip(other.load.iter()).fold(0., |acc, (a, b)| (*a as Float / *b as Float).max(acc))
273 }
274}
275
276impl LoadOps for MultiDimLoad {}
277
278impl Default for MultiDimLoad {
279 fn default() -> Self {
280 Self { load: [0; LOAD_DIMENSION_SIZE], size: 0 }
281 }
282}
283
284impl Add for MultiDimLoad {
285 type Output = Self;
286
287 fn add(self, rhs: Self) -> Self::Output {
288 fn sum(acc: MultiDimLoad, rhs: &MultiDimLoad) -> MultiDimLoad {
289 let mut dimens = acc;
290
291 for (idx, value) in rhs.load.iter().enumerate() {
292 dimens.load[idx] += *value;
293 }
294
295 dimens.size = dimens.size.max(rhs.size);
296
297 dimens
298 }
299
300 if self.load.len() >= rhs.load.len() {
301 sum(self, &rhs)
302 } else {
303 sum(rhs, &self)
304 }
305 }
306}
307
308impl Sub for MultiDimLoad {
309 type Output = Self;
310
311 fn sub(self, rhs: Self) -> Self::Output {
312 let mut dimens = self;
313
314 for (idx, value) in rhs.load.iter().enumerate() {
315 dimens.load[idx] -= *value;
316 }
317
318 dimens.size = dimens.size.max(rhs.size);
319
320 dimens
321 }
322}
323
324impl PartialOrd for MultiDimLoad {
325 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
326 let size = self.size.max(other.size);
327 (0..size)
328 .try_fold(None, |acc, idx| {
329 let result = self.get(idx).cmp(&other.get(idx));
330 acc.map_or(ControlFlow::Continue(Some(result)), |acc| {
331 if acc != result {
332 ControlFlow::Break(None)
333 } else {
334 ControlFlow::Continue(Some(result))
335 }
336 })
337 })
338 .unwrap_value()
339 }
340}
341
342impl Eq for MultiDimLoad {}
343
344impl PartialEq for MultiDimLoad {
345 fn eq(&self, other: &Self) -> bool {
346 self.partial_cmp(other).map_or(false, |ordering| ordering == Ordering::Equal)
347 }
348}
349
350impl Mul<Float> for MultiDimLoad {
351 type Output = Self;
352
353 fn mul(self, value: Float) -> Self::Output {
354 let mut dimens = self;
355
356 dimens.load.iter_mut().for_each(|item| {
357 *item = (*item as Float * value).round() as i32;
358 });
359
360 dimens
361 }
362}
363
364impl Sum for MultiDimLoad {
365 fn sum<I: Iterator<Item = MultiDimLoad>>(iter: I) -> Self {
366 iter.fold(MultiDimLoad::default(), |acc, item| item + acc)
367 }
368}
369
370impl Display for MultiDimLoad {
371 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
372 write!(f, "{:?}", self.load)
373 }
374}