Skip to main content

solverforge_solver/descriptor/bindings/
lookup.rs

1use 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}