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