Skip to main content

csp_solver/constraint/
dispatch.rs

1//! ConstraintEnum: devirtualized dispatch for hot-path constraints.
2
3use crate::domain::Domain;
4use crate::variable::Variable;
5
6use super::all_different::AllDifferent;
7use super::all_different_except::AllDifferentExcept;
8use super::cage::{CageProduct, CageSum};
9use super::not_equal::NotEqual;
10use super::traits::{Constraint, Revision, VarId};
11
12/// Enum-dispatched constraint storage. Avoids vtable indirection for built-in types.
13///
14/// There is deliberately no `Lambda` variant: every closure-based constraint
15/// (`add_equals`, `add_less_than`, `add_greater_than`, and external
16/// `LambdaConstraint`s) enters through [`Custom`](Self::Custom), which boxes
17/// the `dyn Constraint`. A devirtualized `Lambda` arm carried zero
18/// construction sites (Pass-1 R1) and was excised; W10 may re-introduce it
19/// behind profiling if the boxed dispatch shows up hot.
20///
21/// `CageSum`/`CageProduct` are the n-ary arithmetic cages (`super::cage`): their
22/// `revise_impl`s clear the default `revise`'s 3+-variable blindness wall for
23/// the sum/product shapes (Killer / KenKen). They are parameterized by
24/// `D::Value` (like `AllDifferentExcept`) and read their integer values through
25/// a stored `fn` pointer, so the enum keeps its domain-generic bound — the
26/// variants simply go unconstructed for non-integer domains.
27pub enum ConstraintEnum<D: Domain> {
28    NotEqual(NotEqual),
29    AllDifferent(AllDifferent),
30    AllDifferentExcept(AllDifferentExcept<D::Value>),
31    CageSum(CageSum<D::Value>),
32    CageProduct(CageProduct<D::Value>),
33    Custom(Box<dyn Constraint<D>>),
34}
35
36impl<D: Domain> std::fmt::Debug for ConstraintEnum<D> {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        match self {
39            Self::NotEqual(c) => c.fmt(f),
40            Self::AllDifferent(c) => c.fmt(f),
41            Self::AllDifferentExcept(c) => c.fmt(f),
42            Self::CageSum(c) => c.fmt(f),
43            Self::CageProduct(c) => c.fmt(f),
44            Self::Custom(c) => c.fmt(f),
45        }
46    }
47}
48
49impl<D: Domain> ConstraintEnum<D>
50where
51    D::Value: PartialEq,
52{
53    #[inline]
54    pub fn scope(&self) -> &[VarId] {
55        match self {
56            Self::NotEqual(c) => &c.scope,
57            Self::AllDifferent(c) => &c.scope,
58            Self::AllDifferentExcept(c) => &c.scope,
59            Self::CageSum(c) => &c.scope,
60            Self::CageProduct(c) => &c.scope,
61            Self::Custom(c) => c.scope(),
62        }
63    }
64
65    #[inline]
66    pub fn check(&self, assignment: &[Option<D::Value>]) -> bool {
67        match self {
68            Self::NotEqual(c) => c.check_impl(assignment),
69            Self::AllDifferent(c) => c.check_impl(assignment),
70            Self::AllDifferentExcept(c) => c.check_impl(assignment),
71            Self::CageSum(c) => c.check_impl(assignment),
72            Self::CageProduct(c) => c.check_impl(assignment),
73            Self::Custom(c) => c.check(assignment),
74        }
75    }
76}
77
78impl<D: Domain> ConstraintEnum<D>
79where
80    D::Value: PartialEq + 'static,
81{
82    #[inline]
83    pub fn revise(&self, vars: &mut [Variable<D>], depth: usize) -> Revision {
84        match self {
85            Self::NotEqual(c) => c.revise_impl(vars, depth),
86            Self::AllDifferent(c) => c.revise_impl(vars, depth),
87            Self::AllDifferentExcept(c) => c.revise_impl(vars, depth),
88            Self::CageSum(c) => c.revise_impl(vars, depth),
89            Self::CageProduct(c) => c.revise_impl(vars, depth),
90            Self::Custom(c) => c.revise(vars, depth),
91        }
92    }
93}