Skip to main content

solverforge_solver/heuristic/move/
either.rs

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