1use std::fmt::Debug;
58use std::marker::PhantomData;
59
60use solverforge_core::domain::PlanningSolution;
61use solverforge_scoring::Director;
62
63use crate::heuristic::r#move::ListSwapMove;
64
65use super::entity::EntitySelector;
66use super::list_support::{collect_selected_entities, ordered_index};
67use super::move_selector::{
68 CandidateId, CandidateStore, MoveCandidateRef, MoveCursor, MoveSelector, MoveStreamContext,
69};
70
71pub struct ListSwapMoveSelector<S, V, ES> {
82 entity_selector: ES,
83 list_len: fn(&S, usize) -> usize,
84 list_get: fn(&S, usize, usize) -> Option<V>,
85 list_set: fn(&mut S, usize, usize, V),
86 variable_name: &'static str,
87 descriptor_index: usize,
88 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
89}
90
91enum ListSwapStage {
92 Intra,
93 Inter,
94}
95
96pub struct ListSwapMoveCursor<S, V>
97where
98 S: PlanningSolution,
99 V: Clone + PartialEq + Send + Sync + Debug + 'static,
100{
101 store: CandidateStore<S, ListSwapMove<S, V>>,
102 entities: Vec<usize>,
103 route_lens: Vec<usize>,
104 context: MoveStreamContext,
105 entity_idx: usize,
106 stage: ListSwapStage,
107 pos_a_offset: usize,
108 pos_b_offset: usize,
109 dst_idx: usize,
110 inter_pos_a_offset: usize,
111 inter_pos_b_offset: usize,
112 list_len: fn(&S, usize) -> usize,
113 list_get: fn(&S, usize, usize) -> Option<V>,
114 list_set: fn(&mut S, usize, usize, V),
115 variable_name: &'static str,
116 descriptor_index: usize,
117}
118
119impl<S, V> ListSwapMoveCursor<S, V>
120where
121 S: PlanningSolution,
122 V: Clone + PartialEq + Send + Sync + Debug + 'static,
123{
124 #[allow(clippy::too_many_arguments)]
125 fn new(
126 entities: Vec<usize>,
127 route_lens: Vec<usize>,
128 context: MoveStreamContext,
129 list_len: fn(&S, usize) -> usize,
130 list_get: fn(&S, usize, usize) -> Option<V>,
131 list_set: fn(&mut S, usize, usize, V),
132 variable_name: &'static str,
133 descriptor_index: usize,
134 ) -> Self {
135 Self {
136 store: CandidateStore::new(),
137 entities,
138 route_lens,
139 context,
140 entity_idx: 0,
141 stage: ListSwapStage::Intra,
142 pos_a_offset: 0,
143 pos_b_offset: 0,
144 dst_idx: 1,
145 inter_pos_a_offset: 0,
146 inter_pos_b_offset: 0,
147 list_len,
148 list_get,
149 list_set,
150 variable_name,
151 descriptor_index,
152 }
153 }
154
155 fn push_move(
156 &mut self,
157 entity_a: usize,
158 pos_a: usize,
159 entity_b: usize,
160 pos_b: usize,
161 ) -> CandidateId {
162 self.store.push(ListSwapMove::new(
163 entity_a,
164 pos_a,
165 entity_b,
166 pos_b,
167 self.list_len,
168 self.list_get,
169 self.list_set,
170 self.variable_name,
171 self.descriptor_index,
172 ))
173 }
174
175 fn advance_entity(&mut self) {
176 self.entity_idx += 1;
177 self.stage = ListSwapStage::Intra;
178 self.pos_a_offset = 0;
179 self.pos_b_offset = 0;
180 self.dst_idx = self.entity_idx + 1;
181 self.inter_pos_a_offset = 0;
182 self.inter_pos_b_offset = 0;
183 }
184}
185
186impl<S, V> MoveCursor<S, ListSwapMove<S, V>> for ListSwapMoveCursor<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_idx >= self.entities.len() {
194 return None;
195 }
196
197 let entity_a = self.entities[self.entity_idx];
198 let len_a = self.route_lens[self.entity_idx];
199 if len_a == 0 {
200 self.advance_entity();
201 continue;
202 }
203
204 match self.stage {
205 ListSwapStage::Intra => {
206 while self.pos_a_offset < len_a {
207 let pos_a = ordered_index(
208 self.pos_a_offset,
209 len_a,
210 self.context,
211 0x1157_5A09_0000_0002 ^ entity_a as u64 ^ self.descriptor_index as u64,
212 );
213 let pos_b_count = len_a.saturating_sub(pos_a + 1);
214 if self.pos_b_offset < pos_b_count {
215 let pos_b = pos_a
216 + 1
217 + ordered_index(
218 self.pos_b_offset,
219 pos_b_count,
220 self.context,
221 0x1157_5A09_0000_0003 ^ entity_a as u64 ^ pos_a as u64,
222 );
223 self.pos_b_offset += 1;
224 return Some(self.push_move(entity_a, pos_a, entity_a, pos_b));
225 }
226 self.pos_a_offset += 1;
227 self.pos_b_offset = 0;
228 }
229 self.stage = ListSwapStage::Inter;
230 self.dst_idx = self.entity_idx + 1;
231 self.inter_pos_a_offset = 0;
232 self.inter_pos_b_offset = 0;
233 }
234 ListSwapStage::Inter => {
235 while self.dst_idx < self.entities.len() {
236 let entity_b = self.entities[self.dst_idx];
237 let len_b = self.route_lens[self.dst_idx];
238 if len_b == 0 {
239 self.dst_idx += 1;
240 continue;
241 }
242
243 while self.inter_pos_a_offset < len_a {
244 let pos_a = ordered_index(
245 self.inter_pos_a_offset,
246 len_a,
247 self.context,
248 0x1157_5A09_0000_0004 ^ entity_a as u64 ^ entity_b as u64,
249 );
250 if self.inter_pos_b_offset < len_b {
251 let pos_b = ordered_index(
252 self.inter_pos_b_offset,
253 len_b,
254 self.context,
255 0x1157_5A09_0000_0005
256 ^ entity_a as u64
257 ^ entity_b as u64
258 ^ pos_a as u64,
259 );
260 self.inter_pos_b_offset += 1;
261 return Some(self.push_move(entity_a, pos_a, entity_b, pos_b));
262 }
263 self.inter_pos_a_offset += 1;
264 self.inter_pos_b_offset = 0;
265 }
266 self.dst_idx += 1;
267 self.inter_pos_a_offset = 0;
268 self.inter_pos_b_offset = 0;
269 }
270 self.advance_entity();
271 }
272 }
273 }
274 }
275
276 fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, ListSwapMove<S, V>>> {
277 self.store.candidate(id)
278 }
279
280 fn take_candidate(&mut self, id: CandidateId) -> ListSwapMove<S, V> {
281 self.store.take_candidate(id)
282 }
283}
284
285impl<S, V> Iterator for ListSwapMoveCursor<S, V>
286where
287 S: PlanningSolution,
288 V: Clone + PartialEq + Send + Sync + Debug + 'static,
289{
290 type Item = ListSwapMove<S, V>;
291
292 fn next(&mut self) -> Option<Self::Item> {
293 let id = self.next_candidate()?;
294 Some(self.take_candidate(id))
295 }
296}
297
298impl<S, V: Debug, ES: Debug> Debug for ListSwapMoveSelector<S, V, ES> {
299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
300 f.debug_struct("ListSwapMoveSelector")
301 .field("entity_selector", &self.entity_selector)
302 .field("variable_name", &self.variable_name)
303 .field("descriptor_index", &self.descriptor_index)
304 .finish()
305 }
306}
307
308impl<S, V, ES> ListSwapMoveSelector<S, V, ES> {
309 pub fn new(
319 entity_selector: ES,
320 list_len: fn(&S, usize) -> usize,
321 list_get: fn(&S, usize, usize) -> Option<V>,
322 list_set: fn(&mut S, usize, usize, V),
323 variable_name: &'static str,
324 descriptor_index: usize,
325 ) -> Self {
326 Self {
327 entity_selector,
328 list_len,
329 list_get,
330 list_set,
331 variable_name,
332 descriptor_index,
333 _phantom: PhantomData,
334 }
335 }
336}
337
338impl<S, V, ES> MoveSelector<S, ListSwapMove<S, V>> for ListSwapMoveSelector<S, V, ES>
339where
340 S: PlanningSolution,
341 V: Clone + PartialEq + Send + Sync + Debug + 'static,
342 ES: EntitySelector<S>,
343{
344 type Cursor<'a>
345 = ListSwapMoveCursor<S, V>
346 where
347 Self: 'a;
348
349 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
350 self.open_cursor_with_context(score_director, MoveStreamContext::default())
351 }
352
353 fn open_cursor_with_context<'a, D: Director<S>>(
354 &'a self,
355 score_director: &D,
356 context: MoveStreamContext,
357 ) -> Self::Cursor<'a> {
358 let mut selected =
359 collect_selected_entities(&self.entity_selector, score_director, self.list_len);
360 selected.apply_stream_order(
361 context,
362 0x1157_5A09_0000_0001 ^ self.descriptor_index as u64,
363 );
364 ListSwapMoveCursor::new(
365 selected.entities,
366 selected.route_lens,
367 context,
368 self.list_len,
369 self.list_get,
370 self.list_set,
371 self.variable_name,
372 self.descriptor_index,
373 )
374 }
375
376 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
377 collect_selected_entities(&self.entity_selector, score_director, self.list_len)
378 .list_swap_move_capacity()
379 }
380}