solverforge_solver/heuristic/move/
sublist_swap.rs1use std::fmt::Debug;
11use std::marker::PhantomData;
12
13use solverforge_core::domain::PlanningSolution;
14use solverforge_scoring::ScoreDirector;
15
16use super::Move;
17
18pub struct SubListSwapMove<S, V> {
71 first_entity_index: usize,
73 first_start: usize,
75 first_end: usize,
77 second_entity_index: usize,
79 second_start: usize,
81 second_end: usize,
83 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)]
132 pub fn new(
133 first_entity_index: usize,
134 first_start: usize,
135 first_end: usize,
136 second_entity_index: usize,
137 second_start: usize,
138 second_end: usize,
139 list_len: fn(&S, usize) -> usize,
140 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
141 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
142 variable_name: &'static str,
143 descriptor_index: usize,
144 ) -> Self {
145 Self {
146 first_entity_index,
147 first_start,
148 first_end,
149 second_entity_index,
150 second_start,
151 second_end,
152 list_len,
153 sublist_remove,
154 sublist_insert,
155 variable_name,
156 descriptor_index,
157 indices: [first_entity_index, second_entity_index],
158 _phantom: PhantomData,
159 }
160 }
161
162 pub fn first_entity_index(&self) -> usize {
164 self.first_entity_index
165 }
166
167 pub fn first_start(&self) -> usize {
169 self.first_start
170 }
171
172 pub fn first_end(&self) -> usize {
174 self.first_end
175 }
176
177 pub fn first_len(&self) -> usize {
179 self.first_end.saturating_sub(self.first_start)
180 }
181
182 pub fn second_entity_index(&self) -> usize {
184 self.second_entity_index
185 }
186
187 pub fn second_start(&self) -> usize {
189 self.second_start
190 }
191
192 pub fn second_end(&self) -> usize {
194 self.second_end
195 }
196
197 pub fn second_len(&self) -> usize {
199 self.second_end.saturating_sub(self.second_start)
200 }
201
202 pub fn is_intra_list(&self) -> bool {
204 self.first_entity_index == self.second_entity_index
205 }
206}
207
208impl<S, V> Move<S> for SubListSwapMove<S, V>
209where
210 S: PlanningSolution,
211 V: Clone + Send + Sync + Debug + 'static,
212{
213 fn is_doable<D: ScoreDirector<S>>(&self, score_director: &D) -> bool {
214 let solution = score_director.working_solution();
215
216 if self.first_start >= self.first_end || self.second_start >= self.second_end {
218 return false;
219 }
220
221 let first_list_len = (self.list_len)(solution, self.first_entity_index);
223 if self.first_end > first_list_len {
224 return false;
225 }
226
227 let second_list_len = (self.list_len)(solution, self.second_entity_index);
229 if self.second_end > second_list_len {
230 return false;
231 }
232
233 if self.is_intra_list() {
235 let overlaps = self.first_start < self.second_end && self.second_start < self.first_end;
237 if overlaps {
238 return false;
239 }
240 }
241
242 true
243 }
244
245 fn do_move<D: ScoreDirector<S>>(&self, score_director: &mut D) {
246 score_director.before_variable_changed(self.descriptor_index, self.first_entity_index);
248 if !self.is_intra_list() {
249 score_director.before_variable_changed(self.descriptor_index, self.second_entity_index);
250 }
251
252 if self.is_intra_list() {
253 let (early_start, early_end, late_start, late_end) =
256 if self.first_start < self.second_start {
257 (
258 self.first_start,
259 self.first_end,
260 self.second_start,
261 self.second_end,
262 )
263 } else {
264 (
265 self.second_start,
266 self.second_end,
267 self.first_start,
268 self.first_end,
269 )
270 };
271
272 let late_elements = (self.sublist_remove)(
274 score_director.working_solution_mut(),
275 self.first_entity_index,
276 late_start,
277 late_end,
278 );
279
280 let early_elements = (self.sublist_remove)(
282 score_director.working_solution_mut(),
283 self.first_entity_index,
284 early_start,
285 early_end,
286 );
287
288 let late_len = late_end - late_start;
290 let early_len = early_end - early_start;
291 (self.sublist_insert)(
292 score_director.working_solution_mut(),
293 self.first_entity_index,
294 early_start,
295 late_elements,
296 );
297
298 let new_late_pos = late_start - early_len + late_len;
302 (self.sublist_insert)(
303 score_director.working_solution_mut(),
304 self.first_entity_index,
305 new_late_pos,
306 early_elements,
307 );
308
309 let sublist_remove = self.sublist_remove;
311 let sublist_insert = self.sublist_insert;
312 let entity = self.first_entity_index;
313
314 score_director.register_undo(Box::new(move |s: &mut S| {
315 let late_at_early = sublist_remove(s, entity, early_start, early_start + late_len);
317 let early_at_late = sublist_remove(
319 s,
320 entity,
321 new_late_pos - late_len,
322 new_late_pos - late_len + early_len,
323 );
324 sublist_insert(s, entity, early_start, early_at_late);
326 sublist_insert(s, entity, late_start, late_at_early);
328 }));
329 } else {
330 let first_elements = (self.sublist_remove)(
332 score_director.working_solution_mut(),
333 self.first_entity_index,
334 self.first_start,
335 self.first_end,
336 );
337
338 let second_elements = (self.sublist_remove)(
339 score_director.working_solution_mut(),
340 self.second_entity_index,
341 self.second_start,
342 self.second_end,
343 );
344
345 (self.sublist_insert)(
347 score_director.working_solution_mut(),
348 self.first_entity_index,
349 self.first_start,
350 second_elements,
351 );
352
353 (self.sublist_insert)(
354 score_director.working_solution_mut(),
355 self.second_entity_index,
356 self.second_start,
357 first_elements,
358 );
359
360 let sublist_remove = self.sublist_remove;
362 let sublist_insert = self.sublist_insert;
363 let first_entity = self.first_entity_index;
364 let first_start = self.first_start;
365 let second_entity = self.second_entity_index;
366 let second_start = self.second_start;
367 let first_len = self.first_len();
368 let second_len = self.second_len();
369
370 score_director.register_undo(Box::new(move |s: &mut S| {
371 let second_at_first =
373 sublist_remove(s, first_entity, first_start, first_start + second_len);
374 let first_at_second =
376 sublist_remove(s, second_entity, second_start, second_start + first_len);
377 sublist_insert(s, first_entity, first_start, first_at_second);
379 sublist_insert(s, second_entity, second_start, second_at_first);
380 }));
381 }
382
383 score_director.after_variable_changed(self.descriptor_index, self.first_entity_index);
385 if !self.is_intra_list() {
386 score_director.after_variable_changed(self.descriptor_index, self.second_entity_index);
387 }
388 }
389
390 fn descriptor_index(&self) -> usize {
391 self.descriptor_index
392 }
393
394 fn entity_indices(&self) -> &[usize] {
395 if self.is_intra_list() {
396 &self.indices[0..1]
397 } else {
398 &self.indices
399 }
400 }
401
402 fn variable_name(&self) -> &str {
403 self.variable_name
404 }
405}
406
407#[cfg(test)]
408#[path = "sublist_swap_tests.rs"]
409mod tests;