solverforge_solver/heuristic/move/
sublist_swap.rs1use std::fmt::Debug;
12use std::marker::PhantomData;
13
14use solverforge_core::domain::PlanningSolution;
15use solverforge_scoring::Director;
16
17use super::Move;
18
19pub struct SubListSwapMove<S, V> {
72 first_entity_index: usize,
74 first_start: usize,
76 first_end: usize,
78 second_entity_index: usize,
80 second_start: usize,
82 second_end: usize,
84 list_len: fn(&S, usize) -> usize,
85 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
87 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
89 variable_name: &'static str,
90 descriptor_index: usize,
91 indices: [usize; 2],
93 _phantom: PhantomData<fn() -> V>,
94}
95
96impl<S, V> Clone for SubListSwapMove<S, V> {
97 fn clone(&self) -> Self {
98 *self
99 }
100}
101
102impl<S, V> Copy for SubListSwapMove<S, V> {}
103
104impl<S, V: Debug> Debug for SubListSwapMove<S, V> {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 f.debug_struct("SubListSwapMove")
107 .field("first_entity", &self.first_entity_index)
108 .field("first_range", &(self.first_start..self.first_end))
109 .field("second_entity", &self.second_entity_index)
110 .field("second_range", &(self.second_start..self.second_end))
111 .field("variable_name", &self.variable_name)
112 .finish()
113 }
114}
115
116impl<S, V> SubListSwapMove<S, V> {
117 #[allow(clippy::too_many_arguments)]
133 pub fn new(
134 first_entity_index: usize,
135 first_start: usize,
136 first_end: usize,
137 second_entity_index: usize,
138 second_start: usize,
139 second_end: usize,
140 list_len: fn(&S, usize) -> usize,
141 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
142 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
143 variable_name: &'static str,
144 descriptor_index: usize,
145 ) -> Self {
146 Self {
147 first_entity_index,
148 first_start,
149 first_end,
150 second_entity_index,
151 second_start,
152 second_end,
153 list_len,
154 sublist_remove,
155 sublist_insert,
156 variable_name,
157 descriptor_index,
158 indices: [first_entity_index, second_entity_index],
159 _phantom: PhantomData,
160 }
161 }
162
163 pub fn first_entity_index(&self) -> usize {
164 self.first_entity_index
165 }
166
167 pub fn first_start(&self) -> usize {
168 self.first_start
169 }
170
171 pub fn first_end(&self) -> usize {
172 self.first_end
173 }
174
175 pub fn first_len(&self) -> usize {
176 self.first_end.saturating_sub(self.first_start)
177 }
178
179 pub fn second_entity_index(&self) -> usize {
180 self.second_entity_index
181 }
182
183 pub fn second_start(&self) -> usize {
184 self.second_start
185 }
186
187 pub fn second_end(&self) -> usize {
188 self.second_end
189 }
190
191 pub fn second_len(&self) -> usize {
192 self.second_end.saturating_sub(self.second_start)
193 }
194
195 pub fn is_intra_list(&self) -> bool {
196 self.first_entity_index == self.second_entity_index
197 }
198}
199
200impl<S, V> Move<S> for SubListSwapMove<S, V>
201where
202 S: PlanningSolution,
203 V: Clone + Send + Sync + Debug + 'static,
204{
205 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
206 let solution = score_director.working_solution();
207
208 if self.first_start >= self.first_end || self.second_start >= self.second_end {
210 return false;
211 }
212
213 let first_list_len = (self.list_len)(solution, self.first_entity_index);
215 if self.first_end > first_list_len {
216 return false;
217 }
218
219 let second_list_len = (self.list_len)(solution, self.second_entity_index);
221 if self.second_end > second_list_len {
222 return false;
223 }
224
225 if self.is_intra_list() {
227 let overlaps = self.first_start < self.second_end && self.second_start < self.first_end;
229 if overlaps {
230 return false;
231 }
232 }
233
234 true
235 }
236
237 fn do_move<D: Director<S>>(&self, score_director: &mut D) {
238 score_director.before_variable_changed(self.descriptor_index, self.first_entity_index);
240 if !self.is_intra_list() {
241 score_director.before_variable_changed(self.descriptor_index, self.second_entity_index);
242 }
243
244 if self.is_intra_list() {
245 let (early_start, early_end, late_start, late_end) =
248 if self.first_start < self.second_start {
249 (
250 self.first_start,
251 self.first_end,
252 self.second_start,
253 self.second_end,
254 )
255 } else {
256 (
257 self.second_start,
258 self.second_end,
259 self.first_start,
260 self.first_end,
261 )
262 };
263
264 let late_elements = (self.sublist_remove)(
266 score_director.working_solution_mut(),
267 self.first_entity_index,
268 late_start,
269 late_end,
270 );
271
272 let early_elements = (self.sublist_remove)(
274 score_director.working_solution_mut(),
275 self.first_entity_index,
276 early_start,
277 early_end,
278 );
279
280 let late_len = late_end - late_start;
282 let early_len = early_end - early_start;
283 (self.sublist_insert)(
284 score_director.working_solution_mut(),
285 self.first_entity_index,
286 early_start,
287 late_elements,
288 );
289
290 let new_late_pos = late_start - early_len + late_len;
295 (self.sublist_insert)(
296 score_director.working_solution_mut(),
297 self.first_entity_index,
298 new_late_pos,
299 early_elements,
300 );
301
302 let sublist_remove = self.sublist_remove;
304 let sublist_insert = self.sublist_insert;
305 let entity = self.first_entity_index;
306
307 score_director.register_undo(Box::new(move |s: &mut S| {
308 let late_at_early = sublist_remove(s, entity, early_start, early_start + late_len);
310 let early_at_late = sublist_remove(
312 s,
313 entity,
314 new_late_pos - late_len,
315 new_late_pos - late_len + early_len,
316 );
317 sublist_insert(s, entity, early_start, early_at_late);
319 sublist_insert(s, entity, late_start, late_at_early);
321 }));
322 } else {
323 let first_elements = (self.sublist_remove)(
325 score_director.working_solution_mut(),
326 self.first_entity_index,
327 self.first_start,
328 self.first_end,
329 );
330
331 let second_elements = (self.sublist_remove)(
332 score_director.working_solution_mut(),
333 self.second_entity_index,
334 self.second_start,
335 self.second_end,
336 );
337
338 (self.sublist_insert)(
340 score_director.working_solution_mut(),
341 self.first_entity_index,
342 self.first_start,
343 second_elements,
344 );
345
346 (self.sublist_insert)(
347 score_director.working_solution_mut(),
348 self.second_entity_index,
349 self.second_start,
350 first_elements,
351 );
352
353 let sublist_remove = self.sublist_remove;
355 let sublist_insert = self.sublist_insert;
356 let first_entity = self.first_entity_index;
357 let first_start = self.first_start;
358 let second_entity = self.second_entity_index;
359 let second_start = self.second_start;
360 let first_len = self.first_len();
361 let second_len = self.second_len();
362
363 score_director.register_undo(Box::new(move |s: &mut S| {
364 let second_at_first =
366 sublist_remove(s, first_entity, first_start, first_start + second_len);
367 let first_at_second =
369 sublist_remove(s, second_entity, second_start, second_start + first_len);
370 sublist_insert(s, first_entity, first_start, first_at_second);
372 sublist_insert(s, second_entity, second_start, second_at_first);
373 }));
374 }
375
376 score_director.after_variable_changed(self.descriptor_index, self.first_entity_index);
378 if !self.is_intra_list() {
379 score_director.after_variable_changed(self.descriptor_index, self.second_entity_index);
380 }
381 }
382
383 fn descriptor_index(&self) -> usize {
384 self.descriptor_index
385 }
386
387 fn entity_indices(&self) -> &[usize] {
388 if self.is_intra_list() {
389 &self.indices[0..1]
390 } else {
391 &self.indices
392 }
393 }
394
395 fn variable_name(&self) -> &str {
396 self.variable_name
397 }
398}
399
400#[cfg(test)]
401#[path = "sublist_swap_tests.rs"]
402mod tests;