Skip to main content

solverforge_solver/builder/context/
coverage.rs

1use std::fmt;
2
3use crate::builder::context::ScalarVariableSlot;
4use crate::planning::{CoverageGroup, CoverageGroupLimits, ScalarEdit};
5
6pub struct CoverageGroupBinding<S> {
7    pub group_name: &'static str,
8    pub target: ScalarVariableSlot<S>,
9    pub required_slot: fn(&S, usize) -> bool,
10    pub capacity_key: Option<fn(&S, usize, usize) -> Option<usize>>,
11    pub entity_order: Option<fn(&S, usize) -> i64>,
12    pub value_order: Option<fn(&S, usize, usize) -> i64>,
13    pub limits: CoverageGroupLimits,
14}
15
16impl<S> Clone for CoverageGroupBinding<S> {
17    fn clone(&self) -> Self {
18        *self
19    }
20}
21
22impl<S> Copy for CoverageGroupBinding<S> {}
23
24impl<S> CoverageGroupBinding<S> {
25    pub fn bind(group: CoverageGroup<S>, scalar_slots: &[ScalarVariableSlot<S>]) -> Self {
26        let target = group.target();
27        let target_slot = scalar_slots
28            .iter()
29            .copied()
30            .find(|slot| {
31                slot.descriptor_index == target.descriptor_index()
32                    && slot.variable_name == target.variable_name()
33            })
34            .unwrap_or_else(|| {
35                panic!(
36                    "coverage group `{}` target {}.{} did not match a scalar planning variable",
37                    group.group_name(),
38                    target.descriptor_index(),
39                    target.variable_name(),
40                )
41            });
42        assert!(
43            target_slot.allows_unassigned,
44            "coverage group `{}` target {}.{} must allow unassigned values",
45            group.group_name(),
46            target_slot.entity_type_name,
47            target_slot.variable_name,
48        );
49        let required_slot = group.required_slot().unwrap_or_else(|| {
50            panic!(
51                "coverage group `{}` requires a required-slot predicate",
52                group.group_name(),
53            )
54        });
55        Self {
56            group_name: group.group_name(),
57            target: target_slot,
58            required_slot,
59            capacity_key: group.capacity_key(),
60            entity_order: group.entity_order(),
61            value_order: group.value_order(),
62            limits: group.limits(),
63        }
64    }
65
66    pub fn entity_count(&self, solution: &S) -> usize {
67        (self.target.entity_count)(solution)
68    }
69
70    pub fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
71        self.target.current_value(solution, entity_index)
72    }
73
74    pub fn is_required(&self, solution: &S, entity_index: usize) -> bool {
75        (self.required_slot)(solution, entity_index)
76    }
77
78    pub fn capacity_key(&self, solution: &S, entity_index: usize, value: usize) -> Option<usize> {
79        self.capacity_key
80            .and_then(|capacity_key| capacity_key(solution, entity_index, value))
81    }
82
83    pub fn entity_order_key(&self, solution: &S, entity_index: usize) -> i64 {
84        self.entity_order
85            .map(|entity_order| entity_order(solution, entity_index))
86            .unwrap_or(entity_index as i64)
87    }
88
89    pub fn value_order_key(&self, solution: &S, entity_index: usize, value: usize) -> i64 {
90        self.value_order
91            .map(|value_order| value_order(solution, entity_index, value))
92            .unwrap_or(value as i64)
93    }
94
95    pub fn candidate_values(
96        &self,
97        solution: &S,
98        entity_index: usize,
99        value_candidate_limit: Option<usize>,
100    ) -> Vec<usize> {
101        let mut values =
102            self.target
103                .candidate_values_for_entity(solution, entity_index, value_candidate_limit);
104        values.sort_by_key(|value| self.value_order_key(solution, entity_index, *value));
105        values
106    }
107
108    pub fn value_is_legal(&self, solution: &S, entity_index: usize, value: Option<usize>) -> bool {
109        self.target.value_is_legal(solution, entity_index, value)
110    }
111
112    pub fn edit(&self, entity_index: usize, value: Option<usize>) -> ScalarEdit<S> {
113        ScalarEdit::from_descriptor_index(
114            self.target.descriptor_index,
115            entity_index,
116            self.target.variable_name,
117            value,
118        )
119    }
120}
121
122impl<S> fmt::Debug for CoverageGroupBinding<S> {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        f.debug_struct("CoverageGroupBinding")
125            .field("group_name", &self.group_name)
126            .field("entity_type_name", &self.target.entity_type_name)
127            .field("variable_name", &self.target.variable_name)
128            .field("has_capacity_key", &self.capacity_key.is_some())
129            .field("limits", &self.limits)
130            .finish()
131    }
132}
133
134pub fn bind_coverage_groups<S>(
135    groups: Vec<CoverageGroup<S>>,
136    scalar_slots: &[ScalarVariableSlot<S>],
137) -> Vec<CoverageGroupBinding<S>> {
138    groups
139        .into_iter()
140        .map(|group| CoverageGroupBinding::bind(group, scalar_slots))
141        .collect()
142}