solverforge_solver/descriptor/bindings/
lookup.rs1use std::any::Any;
2
3use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
4
5use crate::phase::construction::ConstructionFrontier;
6
7use super::variable::{ResolvedVariableBinding, VariableBinding};
8
9pub(crate) fn collect_bindings(descriptor: &SolutionDescriptor) -> Vec<VariableBinding> {
10 let mut bindings = Vec::new();
11 for (descriptor_index, entity_descriptor) in descriptor.entity_descriptors.iter().enumerate() {
12 let mut variable_index = 0usize;
13 for variable in entity_descriptor.genuine_variable_descriptors() {
14 let Some(getter) = variable.usize_getter else {
15 continue;
16 };
17 let Some(setter) = variable.usize_setter else {
18 continue;
19 };
20 bindings.push(VariableBinding {
21 binding_index: bindings.len(),
22 descriptor_index,
23 variable_index,
24 entity_type_name: entity_descriptor.type_name,
25 variable_name: variable.name,
26 allows_unassigned: variable.allows_unassigned,
27 getter,
28 setter,
29 value_range_provider: variable.value_range_provider,
30 provider: variable.entity_value_provider,
31 candidate_values: variable.candidate_values,
32 nearby_value_candidates: variable.nearby_value_candidates,
33 nearby_entity_candidates: variable.nearby_entity_candidates,
34 range_type: variable.value_range_type.clone(),
35 nearby_value_distance_meter: variable.nearby_value_distance_meter,
36 nearby_entity_distance_meter: variable.nearby_entity_distance_meter,
37 construction_entity_order_key: variable.construction_entity_order_key,
38 construction_value_order_key: variable.construction_value_order_key,
39 });
40 variable_index += 1;
41 }
42 }
43 bindings
44}
45
46pub(crate) fn find_binding(
47 bindings: &[VariableBinding],
48 entity_class: Option<&str>,
49 variable_name: Option<&str>,
50) -> Vec<VariableBinding> {
51 bindings
52 .iter()
53 .filter(|binding| entity_class.is_none_or(|name| name == binding.entity_type_name))
54 .filter(|binding| variable_name.is_none_or(|name| name == binding.variable_name))
55 .cloned()
56 .collect()
57}
58
59pub(crate) fn find_resolved_binding<S>(
60 bindings: &[ResolvedVariableBinding<S>],
61 entity_class: Option<&str>,
62 variable_name: Option<&str>,
63) -> Vec<ResolvedVariableBinding<S>> {
64 bindings
65 .iter()
66 .filter(|binding| entity_class.is_none_or(|name| name == binding.entity_type_name))
67 .filter(|binding| variable_name.is_none_or(|name| name == binding.variable_name))
68 .cloned()
69 .collect()
70}
71
72pub fn descriptor_has_bindings(descriptor: &SolutionDescriptor) -> bool {
73 !collect_bindings(descriptor).is_empty()
74}
75
76pub(crate) fn scalar_work_remaining_with_frontier<S>(
77 descriptor: &SolutionDescriptor,
78 frontier: &ConstructionFrontier,
79 solution_revision: u64,
80 entity_class: Option<&str>,
81 variable_name: Option<&str>,
82 solution: &S,
83) -> bool
84where
85 S: PlanningSolution + 'static,
86{
87 let bindings = find_binding(&collect_bindings(descriptor), entity_class, variable_name);
88 for binding in bindings {
89 let Some(entity_count) = descriptor
90 .entity_descriptors
91 .get(binding.descriptor_index)
92 .and_then(|entity| entity.entity_count(solution as &dyn Any))
93 else {
94 continue;
95 };
96 for entity_index in 0..entity_count {
97 let entity = descriptor
98 .get_entity(solution as &dyn Any, binding.descriptor_index, entity_index)
99 .expect("entity lookup failed while checking scalar work");
100 if (binding.getter)(entity).is_some()
101 || frontier.is_scalar_completed(binding.slot_id(entity_index), solution_revision)
102 {
103 continue;
104 }
105 if !binding
106 .values_for_entity(descriptor, solution as &dyn Any, entity)
107 .is_empty()
108 {
109 return true;
110 }
111 }
112 }
113 false
114}
115
116pub fn scalar_work_remaining<S>(
117 descriptor: &SolutionDescriptor,
118 entity_class: Option<&str>,
119 variable_name: Option<&str>,
120 solution: &S,
121) -> bool
122where
123 S: PlanningSolution + 'static,
124{
125 let bindings = find_binding(&collect_bindings(descriptor), entity_class, variable_name);
126 for binding in bindings {
127 let Some(entity_count) = descriptor
128 .entity_descriptors
129 .get(binding.descriptor_index)
130 .and_then(|entity| entity.entity_count(solution as &dyn Any))
131 else {
132 continue;
133 };
134 for entity_index in 0..entity_count {
135 let entity = descriptor
136 .get_entity(solution as &dyn Any, binding.descriptor_index, entity_index)
137 .expect("entity lookup failed while checking scalar work");
138 if (binding.getter)(entity).is_none()
139 && !binding
140 .values_for_entity(descriptor, solution as &dyn Any, entity)
141 .is_empty()
142 {
143 return true;
144 }
145 }
146 }
147 false
148}
149
150pub fn scalar_target_matches(
151 descriptor: &SolutionDescriptor,
152 entity_class: Option<&str>,
153 variable_name: Option<&str>,
154) -> bool {
155 !find_binding(&collect_bindings(descriptor), entity_class, variable_name).is_empty()
156}