1use std::fmt::Debug;
57use std::marker::PhantomData;
58
59use solverforge_core::domain::PlanningSolution;
60use solverforge_scoring::Director;
61
62use crate::heuristic::r#move::ListReverseMove;
63
64use super::entity::EntitySelector;
65use super::list_support::ordered_index;
66use super::move_selector::{
67 CandidateId, CandidateStore, MoveCandidateRef, MoveCursor, MoveSelector, MoveStreamContext,
68};
69use super::precedence_route::{PrecedenceRouteGraph, PrecedenceRouteHooks};
70
71pub struct ListReverseMoveSelector<S, V, ES> {
81 entity_selector: ES,
82 list_len: fn(&S, usize) -> usize,
83 list_get: fn(&S, usize, usize) -> Option<V>,
84 list_reverse: fn(&mut S, usize, usize, usize),
85 precedence_route_hooks: Option<PrecedenceRouteHooks<S, V>>,
86 variable_name: &'static str,
87 descriptor_index: usize,
88 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
89}
90
91pub struct ListReverseMoveCursor<S, V>
92where
93 S: PlanningSolution,
94 V: Clone + Send + Sync + Debug + 'static,
95{
96 store: CandidateStore<S, ListReverseMove<S, V>>,
97 entities: Vec<usize>,
98 route_lens: Vec<usize>,
99 context: MoveStreamContext,
100 entity_idx: usize,
101 start_offset: usize,
102 end_offset: usize,
103 list_len: fn(&S, usize) -> usize,
104 list_get: fn(&S, usize, usize) -> Option<V>,
105 list_reverse: fn(&mut S, usize, usize, usize),
106 precedence_route_graph: Option<PrecedenceRouteGraph>,
107 variable_name: &'static str,
108 descriptor_index: usize,
109}
110
111impl<S, V> ListReverseMoveCursor<S, V>
112where
113 S: PlanningSolution,
114 V: Clone + Send + Sync + Debug + 'static,
115{
116 #[allow(clippy::too_many_arguments)]
117 fn new(
118 entities: Vec<usize>,
119 route_lens: Vec<usize>,
120 context: MoveStreamContext,
121 list_len: fn(&S, usize) -> usize,
122 list_get: fn(&S, usize, usize) -> Option<V>,
123 list_reverse: fn(&mut S, usize, usize, usize),
124 precedence_route_graph: Option<PrecedenceRouteGraph>,
125 variable_name: &'static str,
126 descriptor_index: usize,
127 ) -> Self {
128 Self {
129 store: CandidateStore::new(),
130 entities,
131 route_lens,
132 context,
133 entity_idx: 0,
134 start_offset: 0,
135 end_offset: 0,
136 list_len,
137 list_get,
138 list_reverse,
139 precedence_route_graph,
140 variable_name,
141 descriptor_index,
142 }
143 }
144
145 pub(crate) fn with_precedence_route_graph(
146 mut self,
147 precedence_route_graph: Option<PrecedenceRouteGraph>,
148 ) -> Self {
149 self.precedence_route_graph = precedence_route_graph;
150 self
151 }
152
153 fn push_move(&mut self, entity: usize, start: usize, end: usize) -> CandidateId {
154 self.store.push(ListReverseMove::new(
155 entity,
156 start,
157 end,
158 self.list_len,
159 self.list_get,
160 self.list_reverse,
161 self.variable_name,
162 self.descriptor_index,
163 ))
164 }
165}
166
167impl<S, V> MoveCursor<S, ListReverseMove<S, V>> for ListReverseMoveCursor<S, V>
168where
169 S: PlanningSolution,
170 V: Clone + Send + Sync + Debug + 'static,
171{
172 fn next_candidate(&mut self) -> Option<CandidateId> {
173 loop {
174 let entity = *self.entities.get(self.entity_idx)?;
175 let len = self.route_lens[self.entity_idx];
176 if len < 2 {
177 self.entity_idx += 1;
178 self.start_offset = 0;
179 self.end_offset = 0;
180 continue;
181 }
182
183 while self.start_offset < len {
184 let start = ordered_index(
185 self.start_offset,
186 len,
187 self.context,
188 0x1157_2A07_0000_0002 ^ entity as u64 ^ self.descriptor_index as u64,
189 );
190 let end_count = len.saturating_sub(start + 1);
191 if self.end_offset < end_count {
192 let end = start
193 + 2
194 + ordered_index(
195 self.end_offset,
196 end_count,
197 self.context,
198 0x1157_2A07_0000_0003 ^ entity as u64 ^ start as u64,
199 );
200 self.end_offset += 1;
201 if self.precedence_route_graph.as_ref().is_some_and(|graph| {
202 graph.intra_list_reverse_introduces_cycle(entity, start, end)
203 }) {
204 continue;
205 }
206 return Some(self.push_move(entity, start, end));
207 }
208 self.start_offset += 1;
209 self.end_offset = 0;
210 }
211
212 self.entity_idx += 1;
213 self.start_offset = 0;
214 self.end_offset = 0;
215 }
216 }
217
218 fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, ListReverseMove<S, V>>> {
219 self.store.candidate(id)
220 }
221
222 fn take_candidate(&mut self, id: CandidateId) -> ListReverseMove<S, V> {
223 self.store.take_candidate(id)
224 }
225}
226
227impl<S, V> Iterator for ListReverseMoveCursor<S, V>
228where
229 S: PlanningSolution,
230 V: Clone + Send + Sync + Debug + 'static,
231{
232 type Item = ListReverseMove<S, V>;
233
234 fn next(&mut self) -> Option<Self::Item> {
235 let id = self.next_candidate()?;
236 Some(self.take_candidate(id))
237 }
238}
239
240impl<S, V: Debug, ES: Debug> Debug for ListReverseMoveSelector<S, V, ES> {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 f.debug_struct("ListReverseMoveSelector")
243 .field("entity_selector", &self.entity_selector)
244 .field("variable_name", &self.variable_name)
245 .field("descriptor_index", &self.descriptor_index)
246 .finish()
247 }
248}
249
250impl<S, V, ES> ListReverseMoveSelector<S, V, ES> {
251 pub fn new(
260 entity_selector: ES,
261 list_len: fn(&S, usize) -> usize,
262 list_get: fn(&S, usize, usize) -> Option<V>,
263 list_reverse: fn(&mut S, usize, usize, usize),
264 variable_name: &'static str,
265 descriptor_index: usize,
266 ) -> Self {
267 Self {
268 entity_selector,
269 list_len,
270 list_get,
271 list_reverse,
272 precedence_route_hooks: None,
273 variable_name,
274 descriptor_index,
275 _phantom: PhantomData,
276 }
277 }
278
279 pub(crate) fn with_precedence_route_hooks(
280 mut self,
281 precedence_route_hooks: Option<PrecedenceRouteHooks<S, V>>,
282 ) -> Self {
283 self.precedence_route_hooks = precedence_route_hooks;
284 self
285 }
286
287 pub(crate) fn precedence_route_hooks(&self) -> Option<PrecedenceRouteHooks<S, V>> {
288 self.precedence_route_hooks
289 }
290}
291
292impl<S, V, ES> MoveSelector<S, ListReverseMove<S, V>> for ListReverseMoveSelector<S, V, ES>
293where
294 S: PlanningSolution,
295 V: Clone + Send + Sync + Debug + 'static,
296 ES: EntitySelector<S>,
297{
298 type Cursor<'a>
299 = ListReverseMoveCursor<S, V>
300 where
301 Self: 'a;
302
303 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
304 self.open_cursor_with_context(score_director, MoveStreamContext::default())
305 }
306
307 fn open_cursor_with_context<'a, D: Director<S>>(
308 &'a self,
309 score_director: &D,
310 context: MoveStreamContext,
311 ) -> Self::Cursor<'a> {
312 let mut entities: Vec<usize> = self
313 .entity_selector
314 .iter(score_director)
315 .map(|r| r.entity_index)
316 .collect();
317 let entity_start = context.start_offset(
318 entities.len(),
319 0x1157_2A07_0000_0001 ^ self.descriptor_index as u64,
320 );
321 entities.rotate_left(entity_start);
322
323 let solution = score_director.working_solution();
324 let route_lens = entities
325 .iter()
326 .map(|&entity| (self.list_len)(solution, entity))
327 .collect();
328
329 ListReverseMoveCursor::new(
330 entities,
331 route_lens,
332 context,
333 self.list_len,
334 self.list_get,
335 self.list_reverse,
336 None,
337 self.variable_name,
338 self.descriptor_index,
339 )
340 }
341
342 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
343 let solution = score_director.working_solution();
344 let list_len = self.list_len;
345
346 self.entity_selector
347 .iter(score_director)
348 .map(|r| {
349 let m = list_len(solution, r.entity_index);
350 if m >= 2 {
353 m * (m - 1) / 2
354 } else {
355 0
356 }
357 })
358 .sum()
359 }
360}