1use std::fmt::Debug;
4use std::marker::PhantomData;
5
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use crate::heuristic::r#move::SublistSwapMove;
10use crate::list_placement::selected_segment_allows;
11
12use super::list_kernel::{
13 NativeWindowEmitter, SelectedListOwners, SublistSwapCursor, STATIC_SUBLIST_SWAP_ENTITY_SALT,
14};
15
16use super::entity::EntitySelector;
17use super::list_support::collect_selected_entities;
18use super::move_selector::{
19 CandidateId, MoveCandidateRef, MoveCursor, MoveSelector, MoveStreamContext,
20};
21use super::sublist_support::{count_intra_sublist_swap_moves_for_len, count_sublist_segments};
22
23pub struct SublistSwapMoveSelector<S, V, ES> {
24 entity_selector: ES,
25 min_sublist_size: usize,
26 max_sublist_size: usize,
27 list_len: fn(&S, usize) -> usize,
28 list_get: fn(&S, usize, usize) -> Option<V>,
29 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
30 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
31 element_owner_fn: Option<fn(&S, &V) -> Option<usize>>,
32 variable_name: &'static str,
33 descriptor_index: usize,
34 _phantom: PhantomData<(fn() -> S, fn() -> V)>,
35}
36
37pub struct SublistSwapMoveCursor<S, V>
38where
39 S: PlanningSolution,
40 V: Clone + PartialEq + Send + Sync + Debug + 'static,
41{
42 inner: SublistSwapCursor<S, NativeWindowEmitter<S, V>>,
43}
44
45impl<S, V> SublistSwapMoveCursor<S, V>
46where
47 S: PlanningSolution,
48 V: Clone + PartialEq + Send + Sync + Debug + 'static,
49{
50 fn new(inner: SublistSwapCursor<S, NativeWindowEmitter<S, V>>) -> Self {
51 Self { inner }
52 }
53}
54
55impl<S, V> MoveCursor<S, SublistSwapMove<S, V>> for SublistSwapMoveCursor<S, V>
56where
57 S: PlanningSolution,
58 V: Clone + PartialEq + Send + Sync + Debug + 'static,
59{
60 fn next_candidate(&mut self) -> Option<CandidateId> {
61 self.inner.next_candidate()
62 }
63 fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, SublistSwapMove<S, V>>> {
64 self.inner.candidate(id)
65 }
66 fn take_candidate(&mut self, id: CandidateId) -> SublistSwapMove<S, V> {
67 self.inner.take_candidate(id)
68 }
69 fn next_owned_candidate(&mut self) -> Option<SublistSwapMove<S, V>> {
70 self.inner.next_owned_candidate()
71 }
72 fn next_owned_candidate_matching(
73 &mut self,
74 predicate: for<'a> fn(MoveCandidateRef<'a, S, SublistSwapMove<S, V>>) -> bool,
75 ) -> Option<SublistSwapMove<S, V>> {
76 self.inner.next_owned_candidate_matching(predicate)
77 }
78 fn release_candidate(&mut self, id: CandidateId) -> bool {
79 self.inner.release_candidate(id)
80 }
81}
82
83impl<S, V> Iterator for SublistSwapMoveCursor<S, V>
84where
85 S: PlanningSolution,
86 V: Clone + PartialEq + Send + Sync + Debug + 'static,
87{
88 type Item = SublistSwapMove<S, V>;
89 fn next(&mut self) -> Option<Self::Item> {
90 self.next_owned_candidate()
91 }
92}
93
94impl<S, V: Debug, ES: Debug> Debug for SublistSwapMoveSelector<S, V, ES> {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 f.debug_struct("SublistSwapMoveSelector")
97 .field("entity_selector", &self.entity_selector)
98 .field("min_sublist_size", &self.min_sublist_size)
99 .field("max_sublist_size", &self.max_sublist_size)
100 .field("variable_name", &self.variable_name)
101 .field("descriptor_index", &self.descriptor_index)
102 .finish()
103 }
104}
105
106impl<S, V, ES> SublistSwapMoveSelector<S, V, ES> {
107 #[allow(clippy::too_many_arguments)]
108 pub fn new(
109 entity_selector: ES,
110 min_sublist_size: usize,
111 max_sublist_size: usize,
112 list_len: fn(&S, usize) -> usize,
113 list_get: fn(&S, usize, usize) -> Option<V>,
114 sublist_remove: fn(&mut S, usize, usize, usize) -> Vec<V>,
115 sublist_insert: fn(&mut S, usize, usize, Vec<V>),
116 variable_name: &'static str,
117 descriptor_index: usize,
118 ) -> Self {
119 assert!(min_sublist_size >= 1, "min_sublist_size must be at least 1");
120 assert!(
121 max_sublist_size >= min_sublist_size,
122 "max_sublist_size must be >= min_sublist_size"
123 );
124 Self {
125 entity_selector,
126 min_sublist_size,
127 max_sublist_size,
128 list_len,
129 list_get,
130 sublist_remove,
131 sublist_insert,
132 element_owner_fn: None,
133 variable_name,
134 descriptor_index,
135 _phantom: PhantomData,
136 }
137 }
138
139 pub fn with_element_owner_fn(
140 mut self,
141 element_owner_fn: Option<fn(&S, &V) -> Option<usize>>,
142 ) -> Self {
143 self.element_owner_fn = element_owner_fn;
144 self
145 }
146}
147
148impl<S, V, ES> MoveSelector<S, SublistSwapMove<S, V>> for SublistSwapMoveSelector<S, V, ES>
149where
150 S: PlanningSolution,
151 V: Clone + PartialEq + Send + Sync + Debug + 'static,
152 ES: EntitySelector<S>,
153{
154 type Cursor<'a>
155 = SublistSwapMoveCursor<S, V>
156 where
157 Self: 'a;
158
159 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
160 self.open_cursor_with_context(score_director, MoveStreamContext::default())
161 }
162
163 fn open_cursor_with_context<'a, D: Director<S>>(
164 &'a self,
165 score_director: &D,
166 context: MoveStreamContext,
167 ) -> Self::Cursor<'a> {
168 let mut selected =
169 collect_selected_entities(&self.entity_selector, score_director, self.list_len);
170 selected.apply_stream_order(
171 context,
172 STATIC_SUBLIST_SWAP_ENTITY_SALT ^ self.descriptor_index as u64,
173 );
174 let owner_restrictions = crate::list_placement::selected_owner_restrictions(
175 self.element_owner_fn,
176 score_director.working_solution(),
177 score_director
178 .entity_count(self.descriptor_index)
179 .unwrap_or(0),
180 &selected.entities,
181 &selected.route_lens,
182 self.list_get,
183 );
184 let owners = SelectedListOwners::from_selected_restrictions(owner_restrictions);
185 SublistSwapMoveCursor::new(SublistSwapCursor::new(
186 NativeWindowEmitter::new(
187 self.list_len,
188 self.list_get,
189 self.sublist_remove,
190 self.sublist_insert,
191 self.variable_name,
192 self.descriptor_index,
193 ),
194 selected.entities,
195 selected.route_lens,
196 context,
197 self.min_sublist_size,
198 self.max_sublist_size,
199 owners,
200 self.descriptor_index,
201 ))
202 }
203
204 fn size<D: Director<S>>(&self, score_director: &D) -> usize {
205 let selected =
206 collect_selected_entities(&self.entity_selector, score_director, self.list_len);
207 let Some(owner_restrictions) = crate::list_placement::selected_owner_restrictions(
208 self.element_owner_fn,
209 score_director.working_solution(),
210 score_director
211 .entity_count(self.descriptor_index)
212 .unwrap_or(0),
213 &selected.entities,
214 &selected.route_lens,
215 self.list_get,
216 ) else {
217 return unfiltered_sublist_swap_size(
218 &selected.route_lens,
219 self.min_sublist_size,
220 self.max_sublist_size,
221 );
222 };
223 if owner_restrictions.is_fixed_to_current() {
224 return selected
225 .route_lens
226 .iter()
227 .map(|&route_len| {
228 count_intra_sublist_swap_moves_for_len(
229 route_len,
230 self.min_sublist_size,
231 self.max_sublist_size,
232 )
233 })
234 .sum();
235 }
236 let owners = owner_restrictions
237 .mixed()
238 .expect("non-fixed owner restrictions retain their matrix");
239 let mut count = 0;
240 for (first_idx, &first_entity) in selected.entities.iter().enumerate() {
241 let first_len = selected.route_lens[first_idx];
242 for first_start in 0..first_len {
243 let first_max = self.max_sublist_size.min(first_len - first_start);
244 for first_size in self.min_sublist_size..=first_max {
245 let first_end = first_start + first_size;
246 for (second_idx, &second_entity) in
247 selected.entities.iter().enumerate().skip(first_idx)
248 {
249 let second_len = selected.route_lens[second_idx];
250 for second_start in 0..second_len {
251 let second_max = self.max_sublist_size.min(second_len - second_start);
252 for second_size in self.min_sublist_size..=second_max {
253 let second_end = second_start + second_size;
254 if first_idx == second_idx
255 && (second_start < first_end
256 || (first_start == second_start && first_end == second_end))
257 {
258 continue;
259 }
260 let allowed = if first_entity == second_entity {
261 selected_segment_allows(
262 owners,
263 first_idx,
264 first_start,
265 first_end,
266 first_entity,
267 ) && selected_segment_allows(
268 owners,
269 second_idx,
270 second_start,
271 second_end,
272 first_entity,
273 )
274 } else {
275 selected_segment_allows(
276 owners,
277 first_idx,
278 first_start,
279 first_end,
280 second_entity,
281 ) && selected_segment_allows(
282 owners,
283 second_idx,
284 second_start,
285 second_end,
286 first_entity,
287 )
288 };
289 if allowed {
290 count += 1;
291 }
292 }
293 }
294 }
295 }
296 }
297 }
298 count
299 }
300}
301
302fn unfiltered_sublist_swap_size(
303 route_lens: &[usize],
304 min_sublist_size: usize,
305 max_sublist_size: usize,
306) -> usize {
307 let segment_counts = route_lens
308 .iter()
309 .map(|&route_len| count_sublist_segments(route_len, min_sublist_size, max_sublist_size))
310 .collect::<Vec<_>>();
311 let intra = route_lens
312 .iter()
313 .map(|&route_len| {
314 count_intra_sublist_swap_moves_for_len(route_len, min_sublist_size, max_sublist_size)
315 })
316 .sum::<usize>();
317 let inter = (0..route_lens.len())
318 .flat_map(|left| (left + 1..route_lens.len()).map(move |right| (left, right)))
319 .map(|(left, right)| segment_counts[left] * segment_counts[right])
320 .sum::<usize>();
321 intra + inter
322}