1use std::fmt::Debug;
65use std::marker::PhantomData;
66
67use solverforge_core::domain::PlanningSolution;
68use solverforge_scoring::Director;
69
70use crate::heuristic::r#move::SublistSwapMove;
71
72use super::entity::EntitySelector;
73use super::list_support::{collect_selected_entities, ordered_index};
74use super::move_selector::{
75 CandidateId, CandidateStore, MoveCandidateRef, MoveCursor, MoveSelector, MoveStreamContext,
76};
77use super::sublist_support::{count_intra_sublist_swap_moves_for_len, count_sublist_segments};
78
79pub struct SublistSwapMoveSelector<S, V, ES> {
89 entity_selector: ES,
90 min_sublist_size: usize,
92 max_sublist_size: usize,
94 list_len: fn(&S, usize) -> usize,
95 list_get: fn(&S, usize, usize) -> Option<V>,
96 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
97 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
98 variable_name: &'static str,
99 descriptor_index: usize,
100 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
101}
102
103#[derive(Clone, Copy)]
104struct ListSegment {
105 start: usize,
106 end: usize,
107}
108
109pub struct SublistSwapMoveCursor<S, V>
110where
111 S: PlanningSolution,
112 V: Clone + PartialEq + Send + Sync + Debug + 'static,
113{
114 store: CandidateStore<S, SublistSwapMove<S, V>>,
115 entities: Vec<usize>,
116 segments_by_entity: Vec<Vec<ListSegment>>,
117 entity_a_idx: usize,
118 segment_a_idx: usize,
119 entity_b_idx: usize,
120 segment_b_idx: usize,
121 list_len: fn(&S, usize) -> usize,
122 list_get: fn(&S, usize, usize) -> Option<V>,
123 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
124 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
125 variable_name: &'static str,
126 descriptor_index: usize,
127}
128
129impl<S, V> SublistSwapMoveCursor<S, V>
130where
131 S: PlanningSolution,
132 V: Clone + PartialEq + Send + Sync + Debug + 'static,
133{
134 #[allow(clippy::too_many_arguments)]
135 fn new(
136 entities: Vec<usize>,
137 segments_by_entity: Vec<Vec<ListSegment>>,
138 list_len: fn(&S, usize) -> usize,
139 list_get: fn(&S, usize, usize) -> Option<V>,
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 store: CandidateStore::new(),
147 entities,
148 segments_by_entity,
149 entity_a_idx: 0,
150 segment_a_idx: 0,
151 entity_b_idx: 0,
152 segment_b_idx: 0,
153 list_len,
154 list_get,
155 sublist_remove,
156 sublist_insert,
157 variable_name,
158 descriptor_index,
159 }
160 }
161
162 fn push_move(
163 &mut self,
164 entity_a: usize,
165 first: ListSegment,
166 entity_b: usize,
167 second: ListSegment,
168 ) -> CandidateId {
169 self.store.push(SublistSwapMove::new(
170 entity_a,
171 first.start,
172 first.end,
173 entity_b,
174 second.start,
175 second.end,
176 self.list_len,
177 self.list_get,
178 self.sublist_remove,
179 self.sublist_insert,
180 self.variable_name,
181 self.descriptor_index,
182 ))
183 }
184}
185
186impl<S, V> MoveCursor<S, SublistSwapMove<S, V>> for SublistSwapMoveCursor<S, V>
187where
188 S: PlanningSolution,
189 V: Clone + PartialEq + Send + Sync + Debug + 'static,
190{
191 fn next_candidate(&mut self) -> Option<CandidateId> {
192 loop {
193 if self.entity_a_idx >= self.entities.len() {
194 return None;
195 }
196 if self.segment_a_idx >= self.segments_by_entity[self.entity_a_idx].len() {
197 self.entity_a_idx += 1;
198 self.segment_a_idx = 0;
199 self.entity_b_idx = self.entity_a_idx;
200 self.segment_b_idx = 0;
201 continue;
202 }
203
204 let entity_a = self.entities[self.entity_a_idx];
205 let first = self.segments_by_entity[self.entity_a_idx][self.segment_a_idx];
206 if self.entity_b_idx < self.entity_a_idx {
207 self.entity_b_idx = self.entity_a_idx;
208 self.segment_b_idx = 0;
209 }
210
211 while self.entity_b_idx < self.entities.len() {
212 let entity_b = self.entities[self.entity_b_idx];
213 while self.segment_b_idx < self.segments_by_entity[self.entity_b_idx].len() {
214 let second = self.segments_by_entity[self.entity_b_idx][self.segment_b_idx];
215 self.segment_b_idx += 1;
216 if self.entity_a_idx == self.entity_b_idx {
217 if second.start < first.end {
218 continue;
219 }
220 if first.start == second.start && first.end == second.end {
221 continue;
222 }
223 }
224 return Some(self.push_move(entity_a, first, entity_b, second));
225 }
226 self.entity_b_idx += 1;
227 self.segment_b_idx = 0;
228 }
229
230 self.segment_a_idx += 1;
231 self.entity_b_idx = self.entity_a_idx;
232 self.segment_b_idx = 0;
233 }
234 }
235
236 fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, SublistSwapMove<S, V>>> {
237 self.store.candidate(id)
238 }
239
240 fn take_candidate(&mut self, id: CandidateId) -> SublistSwapMove<S, V> {
241 self.store.take_candidate(id)
242 }
243}
244
245impl<S, V> Iterator for SublistSwapMoveCursor<S, V>
246where
247 S: PlanningSolution,
248 V: Clone + PartialEq + Send + Sync + Debug + 'static,
249{
250 type Item = SublistSwapMove<S, V>;
251
252 fn next(&mut self) -> Option<Self::Item> {
253 let id = self.next_candidate()?;
254 Some(self.take_candidate(id))
255 }
256}
257
258fn build_segments_for_entity(
259 entity: usize,
260 route_len: usize,
261 min_seg: usize,
262 max_seg: usize,
263 context: MoveStreamContext,
264 descriptor_index: usize,
265) -> Vec<ListSegment> {
266 if route_len < min_seg {
267 return Vec::new();
268 }
269
270 let mut segments = Vec::new();
271 for start_offset in 0..route_len {
272 let start = ordered_index(
273 start_offset,
274 route_len,
275 context,
276 0x5B15_75A0_9000_0002 ^ entity as u64 ^ descriptor_index as u64,
277 );
278 let max_valid = max_seg.min(route_len - start);
279 if max_valid < min_seg {
280 continue;
281 }
282 let size_count = max_valid - min_seg + 1;
283 for size_offset in 0..size_count {
284 let segment_size = min_seg
285 + ordered_index(
286 size_offset,
287 size_count,
288 context,
289 0x5B15_75A0_9000_0003 ^ entity as u64 ^ start as u64,
290 );
291 segments.push(ListSegment {
292 start,
293 end: start + segment_size,
294 });
295 }
296 }
297 segments
298}
299
300impl<S, V: Debug, ES: Debug> Debug for SublistSwapMoveSelector<S, V, ES> {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 f.debug_struct("SublistSwapMoveSelector")
303 .field("entity_selector", &self.entity_selector)
304 .field("min_sublist_size", &self.min_sublist_size)
305 .field("max_sublist_size", &self.max_sublist_size)
306 .field("variable_name", &self.variable_name)
307 .field("descriptor_index", &self.descriptor_index)
308 .finish()
309 }
310}
311
312impl<S, V, ES> SublistSwapMoveSelector<S, V, ES> {
313 #[allow(clippy::too_many_arguments)]
329 pub fn new(
330 entity_selector: ES,
331 min_sublist_size: usize,
332 max_sublist_size: usize,
333 list_len: fn(&S, usize) -> usize,
334 list_get: fn(&S, usize, usize) -> Option<V>,
335 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
336 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
337 variable_name: &'static str,
338 descriptor_index: usize,
339 ) -> Self {
340 assert!(min_sublist_size >= 1, "min_sublist_size must be at least 1");
341 assert!(
342 max_sublist_size >= min_sublist_size,
343 "max_sublist_size must be >= min_sublist_size"
344 );
345 Self {
346 entity_selector,
347 min_sublist_size,
348 max_sublist_size,
349 list_len,
350 list_get,
351 sublist_remove,
352 sublist_insert,
353 variable_name,
354 descriptor_index,
355 _phantom: PhantomData,
356 }
357 }
358}
359
360impl<S, V, ES> MoveSelector<S, SublistSwapMove<S, V>> for SublistSwapMoveSelector<S, V, ES>
361where
362 S: PlanningSolution,
363 V: Clone + PartialEq + Send + Sync + Debug + 'static,
364 ES: EntitySelector<S>,
365{
366 type Cursor<'a>
367 = SublistSwapMoveCursor<S, V>
368 where
369 Self: 'a;
370
371 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
372 self.open_cursor_with_context(score_director, MoveStreamContext::default())
373 }
374
375 fn open_cursor_with_context<'a, D: Director<S>>(
376 &'a self,
377 score_director: &D,
378 context: MoveStreamContext,
379 ) -> Self::Cursor<'a> {
380 let min_seg = self.min_sublist_size;
381 let max_seg = self.max_sublist_size;
382
383 let mut selected =
384 collect_selected_entities(&self.entity_selector, score_director, self.list_len);
385 selected.apply_stream_order(
386 context,
387 0x5B15_75A0_9000_0001 ^ self.descriptor_index as u64,
388 );
389 let segments_by_entity = selected
390 .route_lens
391 .iter()
392 .enumerate()
393 .map(|(entity_offset, &route_len)| {
394 let entity = selected.entities[entity_offset];
395 build_segments_for_entity(
396 entity,
397 route_len,
398 min_seg,
399 max_seg,
400 context,
401 self.descriptor_index,
402 )
403 })
404 .collect();
405
406 SublistSwapMoveCursor::new(
407 selected.entities,
408 segments_by_entity,
409 self.list_len,
410 self.list_get,
411 self.sublist_remove,
412 self.sublist_insert,
413 self.variable_name,
414 self.descriptor_index,
415 )
416 }
417
418 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
419 let selected =
420 collect_selected_entities(&self.entity_selector, score_director, self.list_len);
421 let segment_counts: Vec<usize> = selected
422 .route_lens
423 .iter()
424 .map(|&route_len| {
425 count_sublist_segments(route_len, self.min_sublist_size, self.max_sublist_size)
426 })
427 .collect();
428 let intra: usize = selected
429 .route_lens
430 .iter()
431 .map(|&route_len| {
432 count_intra_sublist_swap_moves_for_len(
433 route_len,
434 self.min_sublist_size,
435 self.max_sublist_size,
436 )
437 })
438 .sum();
439 let inter: usize = (0..selected.route_lens.len())
440 .flat_map(|left| (left + 1..selected.route_lens.len()).map(move |right| (left, right)))
441 .map(|(left, right)| segment_counts[left] * segment_counts[right])
442 .sum();
443 intra + inter
444 }
445}