Skip to main content

solverforge_solver/heuristic/move/
either.rs

1/* EitherMove - a monomorphized union of ChangeMove and SwapMove.
2
3This allows local search to use both move types without trait-object dispatch.
4The construction phase uses ChangeMove directly, while local search uses
5EitherMove<S, V> = ChangeMove | SwapMove.
6*/
7
8use std::fmt::Debug;
9
10use solverforge_core::domain::PlanningSolution;
11use solverforge_scoring::Director;
12
13use super::{ChangeMove, Move, SwapMove};
14
15/// A monomorphized union of `ChangeMove` and `SwapMove`.
16///
17/// Implements `Move<S>` by delegating to the inner variant.
18/// `Copy` when `V: Copy`, avoiding heap allocation in the move selector hot path.
19pub enum EitherMove<S, V> {
20    Change(ChangeMove<S, V>),
21    Swap(SwapMove<S, V>),
22}
23
24impl<S, V: Clone> Clone for EitherMove<S, V> {
25    fn clone(&self) -> Self {
26        match self {
27            Self::Change(m) => Self::Change(m.clone()),
28            Self::Swap(m) => Self::Swap(*m),
29        }
30    }
31}
32
33impl<S, V: Copy> Copy for EitherMove<S, V> {}
34
35impl<S, V: Debug> Debug for EitherMove<S, V> {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            Self::Change(m) => m.fmt(f),
39            Self::Swap(m) => m.fmt(f),
40        }
41    }
42}
43
44impl<S, V> Move<S> for EitherMove<S, V>
45where
46    S: PlanningSolution,
47    V: Clone + PartialEq + Send + Sync + Debug + 'static,
48{
49    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
50        match self {
51            Self::Change(m) => m.is_doable(score_director),
52            Self::Swap(m) => m.is_doable(score_director),
53        }
54    }
55
56    fn do_move<D: Director<S>>(&self, score_director: &mut D) {
57        match self {
58            Self::Change(m) => m.do_move(score_director),
59            Self::Swap(m) => m.do_move(score_director),
60        }
61    }
62
63    fn descriptor_index(&self) -> usize {
64        match self {
65            Self::Change(m) => m.descriptor_index(),
66            Self::Swap(m) => m.descriptor_index(),
67        }
68    }
69
70    fn entity_indices(&self) -> &[usize] {
71        match self {
72            Self::Change(m) => m.entity_indices(),
73            Self::Swap(m) => m.entity_indices(),
74        }
75    }
76
77    fn variable_name(&self) -> &str {
78        match self {
79            Self::Change(m) => m.variable_name(),
80            Self::Swap(m) => m.variable_name(),
81        }
82    }
83}