1use std::fmt::Debug;
12use std::marker::PhantomData;
13
14use smallvec::{smallvec, SmallVec};
15use solverforge_core::domain::PlanningSolution;
16use solverforge_scoring::Director;
17
18use super::metadata::{
19 encode_option_debug, encode_usize, hash_str, MoveTabuScope, ScopedEntityTabuToken,
20 ScopedValueTabuToken,
21};
22use super::segment_layout::derive_segment_swap_layout;
23use super::{Move, MoveTabuSignature};
24
25pub struct SublistSwapMove<S, V> {
84 first_entity_index: usize,
86 first_start: usize,
88 first_end: usize,
90 second_entity_index: usize,
92 second_start: usize,
94 second_end: usize,
96 list_len: fn(&S, usize) -> usize,
97 list_get: fn(&S, usize, usize) -> Option<V>,
98 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
100 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
102 variable_name: &'static str,
103 descriptor_index: usize,
104 indices: [usize; 2],
106 _phantom: PhantomData<fn() -> V>,
107}
108
109impl<S, V> Clone for SublistSwapMove<S, V> {
110 fn clone(&self) -> Self {
111 *self
112 }
113}
114
115impl<S, V> Copy for SublistSwapMove<S, V> {}
116
117impl<S, V: Debug> Debug for SublistSwapMove<S, V> {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.debug_struct("SublistSwapMove")
120 .field("first_entity", &self.first_entity_index)
121 .field("first_range", &(self.first_start..self.first_end))
122 .field("second_entity", &self.second_entity_index)
123 .field("second_range", &(self.second_start..self.second_end))
124 .field("variable_name", &self.variable_name)
125 .finish()
126 }
127}
128
129impl<S, V> SublistSwapMove<S, V> {
130 #[allow(clippy::too_many_arguments)]
146 pub fn new(
147 first_entity_index: usize,
148 first_start: usize,
149 first_end: usize,
150 second_entity_index: usize,
151 second_start: usize,
152 second_end: usize,
153 list_len: fn(&S, usize) -> usize,
154 list_get: fn(&S, usize, usize) -> Option<V>,
155 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
156 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
157 variable_name: &'static str,
158 descriptor_index: usize,
159 ) -> Self {
160 Self {
161 first_entity_index,
162 first_start,
163 first_end,
164 second_entity_index,
165 second_start,
166 second_end,
167 list_len,
168 list_get,
169 sublist_remove,
170 sublist_insert,
171 variable_name,
172 descriptor_index,
173 indices: [first_entity_index, second_entity_index],
174 _phantom: PhantomData,
175 }
176 }
177
178 pub fn first_entity_index(&self) -> usize {
179 self.first_entity_index
180 }
181
182 pub fn first_start(&self) -> usize {
183 self.first_start
184 }
185
186 pub fn first_end(&self) -> usize {
187 self.first_end
188 }
189
190 pub fn first_len(&self) -> usize {
191 self.first_end.saturating_sub(self.first_start)
192 }
193
194 pub fn second_entity_index(&self) -> usize {
195 self.second_entity_index
196 }
197
198 pub fn second_start(&self) -> usize {
199 self.second_start
200 }
201
202 pub fn second_end(&self) -> usize {
203 self.second_end
204 }
205
206 pub fn second_len(&self) -> usize {
207 self.second_end.saturating_sub(self.second_start)
208 }
209
210 pub fn is_intra_list(&self) -> bool {
211 self.first_entity_index == self.second_entity_index
212 }
213}
214
215impl<S, V> Move<S> for SublistSwapMove<S, V>
216where
217 S: PlanningSolution,
218 V: Clone + Send + Sync + Debug + 'static,
219{
220 type Undo = ();
221
222 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
223 let solution = score_director.working_solution();
224
225 if self.first_start >= self.first_end || self.second_start >= self.second_end {
227 return false;
228 }
229
230 let first_list_len = (self.list_len)(solution, self.first_entity_index);
232 if self.first_end > first_list_len {
233 return false;
234 }
235
236 let second_list_len = (self.list_len)(solution, self.second_entity_index);
238 if self.second_end > second_list_len {
239 return false;
240 }
241
242 if self.is_intra_list() {
244 let overlaps = self.first_start < self.second_end && self.second_start < self.first_end;
246 if overlaps {
247 return false;
248 }
249 }
250
251 true
252 }
253
254 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
255 let layout = derive_segment_swap_layout(
256 self.first_entity_index,
257 self.first_start,
258 self.first_end,
259 self.second_entity_index,
260 self.second_start,
261 self.second_end,
262 );
263
264 score_director.before_variable_changed(self.descriptor_index, self.first_entity_index);
266 if !self.is_intra_list() {
267 score_director.before_variable_changed(self.descriptor_index, self.second_entity_index);
268 }
269
270 if self.is_intra_list() {
271 let (early_range, late_range) = layout.exact.ordered_ranges();
272
273 let late_elements = (self.sublist_remove)(
275 score_director.working_solution_mut(),
276 self.first_entity_index,
277 late_range.start,
278 late_range.end,
279 );
280
281 let early_elements = (self.sublist_remove)(
283 score_director.working_solution_mut(),
284 self.first_entity_index,
285 early_range.start,
286 early_range.end,
287 );
288
289 let late_len = late_range.len();
291 let early_len = early_range.len();
292 (self.sublist_insert)(
293 score_director.working_solution_mut(),
294 self.first_entity_index,
295 early_range.start,
296 late_elements,
297 );
298
299 let new_late_pos = late_range.start - early_len + late_len;
304 (self.sublist_insert)(
305 score_director.working_solution_mut(),
306 self.first_entity_index,
307 new_late_pos,
308 early_elements,
309 );
310 } else {
311 let first_elements = (self.sublist_remove)(
313 score_director.working_solution_mut(),
314 self.first_entity_index,
315 self.first_start,
316 self.first_end,
317 );
318
319 let second_elements = (self.sublist_remove)(
320 score_director.working_solution_mut(),
321 self.second_entity_index,
322 self.second_start,
323 self.second_end,
324 );
325
326 (self.sublist_insert)(
328 score_director.working_solution_mut(),
329 self.first_entity_index,
330 self.first_start,
331 second_elements,
332 );
333
334 (self.sublist_insert)(
335 score_director.working_solution_mut(),
336 self.second_entity_index,
337 self.second_start,
338 first_elements,
339 );
340 }
341
342 score_director.after_variable_changed(self.descriptor_index, self.first_entity_index);
344 if !self.is_intra_list() {
345 score_director.after_variable_changed(self.descriptor_index, self.second_entity_index);
346 }
347 }
348
349 fn undo_move<D: Director<S>>(&self, score_director: &mut D, (): Self::Undo) {
350 let inverse = derive_segment_swap_layout(
351 self.first_entity_index,
352 self.first_start,
353 self.first_end,
354 self.second_entity_index,
355 self.second_start,
356 self.second_end,
357 )
358 .inverse;
359 score_director.before_variable_changed(self.descriptor_index, inverse.first_entity_index);
360 if inverse.first_entity_index != inverse.second_entity_index {
361 score_director
362 .before_variable_changed(self.descriptor_index, inverse.second_entity_index);
363 }
364
365 if inverse.first_entity_index == inverse.second_entity_index {
366 let (early_range, late_range) = inverse.ordered_ranges();
367 let late_elements = (self.sublist_remove)(
368 score_director.working_solution_mut(),
369 inverse.first_entity_index,
370 late_range.start,
371 late_range.end,
372 );
373 let early_elements = (self.sublist_remove)(
374 score_director.working_solution_mut(),
375 inverse.first_entity_index,
376 early_range.start,
377 early_range.end,
378 );
379 let late_len = late_range.len();
380 let early_len = early_range.len();
381 (self.sublist_insert)(
382 score_director.working_solution_mut(),
383 inverse.first_entity_index,
384 early_range.start,
385 late_elements,
386 );
387 let new_late_pos = late_range.start - early_len + late_len;
388 (self.sublist_insert)(
389 score_director.working_solution_mut(),
390 inverse.first_entity_index,
391 new_late_pos,
392 early_elements,
393 );
394 } else {
395 let first_elements = (self.sublist_remove)(
396 score_director.working_solution_mut(),
397 inverse.first_entity_index,
398 inverse.first_range.start,
399 inverse.first_range.end,
400 );
401 let second_elements = (self.sublist_remove)(
402 score_director.working_solution_mut(),
403 inverse.second_entity_index,
404 inverse.second_range.start,
405 inverse.second_range.end,
406 );
407 (self.sublist_insert)(
408 score_director.working_solution_mut(),
409 inverse.first_entity_index,
410 inverse.first_range.start,
411 second_elements,
412 );
413 (self.sublist_insert)(
414 score_director.working_solution_mut(),
415 inverse.second_entity_index,
416 inverse.second_range.start,
417 first_elements,
418 );
419 }
420
421 score_director.after_variable_changed(self.descriptor_index, inverse.first_entity_index);
422 if inverse.first_entity_index != inverse.second_entity_index {
423 score_director
424 .after_variable_changed(self.descriptor_index, inverse.second_entity_index);
425 }
426 }
427
428 fn descriptor_index(&self) -> usize {
429 self.descriptor_index
430 }
431
432 fn entity_indices(&self) -> &[usize] {
433 if self.is_intra_list() {
434 &self.indices[0..1]
435 } else {
436 &self.indices
437 }
438 }
439
440 fn variable_name(&self) -> &str {
441 self.variable_name
442 }
443
444 fn telemetry_label(&self) -> &'static str {
445 "sublist_swap"
446 }
447
448 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
449 let layout = derive_segment_swap_layout(
450 self.first_entity_index,
451 self.first_start,
452 self.first_end,
453 self.second_entity_index,
454 self.second_start,
455 self.second_end,
456 );
457 let mut first_value_ids: SmallVec<[u64; 2]> = SmallVec::new();
458 for pos in self.first_start..self.first_end {
459 let value = (self.list_get)(
460 score_director.working_solution(),
461 self.first_entity_index,
462 pos,
463 );
464 first_value_ids.push(encode_option_debug(value.as_ref()));
465 }
466 let mut second_value_ids: SmallVec<[u64; 2]> = SmallVec::new();
467 for pos in self.second_start..self.second_end {
468 let value = (self.list_get)(
469 score_director.working_solution(),
470 self.second_entity_index,
471 pos,
472 );
473 second_value_ids.push(encode_option_debug(value.as_ref()));
474 }
475 let first_entity_id = encode_usize(self.first_entity_index);
476 let second_entity_id = encode_usize(self.second_entity_index);
477 let variable_id = hash_str(self.variable_name);
478 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
479 let mut entity_tokens: SmallVec<[ScopedEntityTabuToken; 2]> =
480 smallvec![scope.entity_token(first_entity_id)];
481 if !self.is_intra_list() {
482 entity_tokens.push(scope.entity_token(second_entity_id));
483 }
484 let destination_value_tokens: SmallVec<[ScopedValueTabuToken; 2]> = first_value_ids
485 .iter()
486 .chain(second_value_ids.iter())
487 .copied()
488 .map(|value_id| scope.value_token(value_id))
489 .collect();
490 let mut move_id = smallvec![
491 encode_usize(self.descriptor_index),
492 variable_id,
493 encode_usize(layout.exact.first_entity_index),
494 encode_usize(layout.exact.first_range.start),
495 encode_usize(layout.exact.first_range.end),
496 encode_usize(layout.exact.second_entity_index),
497 encode_usize(layout.exact.second_range.start),
498 encode_usize(layout.exact.second_range.end)
499 ];
500 move_id.extend(first_value_ids.iter().copied());
501 move_id.extend(second_value_ids.iter().copied());
502 let mut undo_move_id = smallvec![
503 encode_usize(self.descriptor_index),
504 variable_id,
505 encode_usize(layout.inverse.first_entity_index),
506 encode_usize(layout.inverse.first_range.start),
507 encode_usize(layout.inverse.first_range.end),
508 encode_usize(layout.inverse.second_entity_index),
509 encode_usize(layout.inverse.second_range.start),
510 encode_usize(layout.inverse.second_range.end)
511 ];
512 undo_move_id.extend(second_value_ids.iter().copied());
513 undo_move_id.extend(first_value_ids.iter().copied());
514
515 MoveTabuSignature::new(scope, move_id, undo_move_id)
516 .with_entity_tokens(entity_tokens)
517 .with_destination_value_tokens(destination_value_tokens)
518 }
519}