solverforge-solver 0.11.1

Solver engine for SolverForge
Documentation
use std::any::Any;
use std::fmt::{self, Debug};
use std::ops::Deref;

use solverforge_core::domain::{
    PlanningSolution, SolutionDescriptor, UsizeCandidateValues, UsizeConstructionEntityOrderKey,
    UsizeConstructionValueOrderKey, UsizeEntityValueProvider, UsizeGetter, UsizeSetter,
    ValueRangeType,
};

use crate::builder::context::{ConstructionEntityOrderKey, ConstructionValueOrderKey};
use crate::phase::construction::ConstructionSlotId;

#[derive(Clone)]
pub(crate) struct VariableBinding {
    pub(crate) binding_index: usize,
    pub(crate) descriptor_index: usize,
    pub(crate) variable_index: usize,
    pub(crate) entity_type_name: &'static str,
    pub(crate) variable_name: &'static str,
    pub(crate) allows_unassigned: bool,
    pub(crate) getter: UsizeGetter,
    pub(crate) setter: UsizeSetter,
    pub(crate) value_range_provider: Option<&'static str>,
    pub(crate) provider: Option<UsizeEntityValueProvider>,
    pub(crate) candidate_values: Option<UsizeCandidateValues>,
    pub(crate) nearby_value_candidates: Option<UsizeCandidateValues>,
    pub(crate) nearby_entity_candidates: Option<UsizeCandidateValues>,
    pub(crate) range_type: ValueRangeType,
    pub(crate) nearby_value_distance_meter:
        Option<solverforge_core::domain::UsizeNearbyValueDistanceMeter>,
    pub(crate) nearby_entity_distance_meter:
        Option<solverforge_core::domain::UsizeNearbyEntityDistanceMeter>,
    pub(crate) construction_entity_order_key: Option<UsizeConstructionEntityOrderKey>,
    pub(crate) construction_value_order_key: Option<UsizeConstructionValueOrderKey>,
}

impl Debug for VariableBinding {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("VariableBinding")
            .field("binding_index", &self.binding_index)
            .field("descriptor_index", &self.descriptor_index)
            .field("variable_index", &self.variable_index)
            .field("entity_type_name", &self.entity_type_name)
            .field("variable_name", &self.variable_name)
            .field("allows_unassigned", &self.allows_unassigned)
            .field("range_type", &self.range_type)
            .finish()
    }
}

impl VariableBinding {
    pub(crate) fn slot_id(&self, entity_index: usize) -> ConstructionSlotId {
        ConstructionSlotId::new(self.binding_index, entity_index)
    }

    pub(crate) fn entity_for_index<'a>(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &'a dyn Any,
        entity_index: usize,
    ) -> &'a dyn Any {
        solution_descriptor
            .get_entity(solution, self.descriptor_index, entity_index)
            .expect("entity lookup failed for descriptor scalar binding")
    }

    pub(crate) fn values_for_entity(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
        entity: &dyn Any,
    ) -> Vec<usize> {
        match (&self.provider, &self.range_type) {
            (Some(provider), _) => provider(entity),
            (_, ValueRangeType::CountableRange { from, to }) => {
                let start = *from;
                let end = *to;
                (start..end)
                    .filter_map(|value| usize::try_from(value).ok())
                    .collect()
            }
            _ => self
                .solution_value_count(solution_descriptor, solution)
                .map(|count| (0..count).collect())
                .unwrap_or_default(),
        }
    }

    pub(crate) fn values_for_entity_index(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
        entity_index: usize,
    ) -> Vec<usize> {
        let entity = self.entity_for_index(solution_descriptor, solution, entity_index);
        self.values_for_entity(solution_descriptor, solution, entity)
    }

    pub(crate) fn candidate_values_for_entity_index(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
        entity_index: usize,
        value_candidate_limit: Option<usize>,
    ) -> Vec<usize> {
        if let Some(provider) = self.candidate_values {
            let values = provider(solution, entity_index, self.variable_index);
            return match value_candidate_limit {
                Some(limit) => values.iter().copied().take(limit).collect(),
                None => values.to_vec(),
            };
        }

        let entity = self.entity_for_index(solution_descriptor, solution, entity_index);
        match (&self.provider, &self.range_type) {
            (Some(provider), _) => {
                let values = provider(entity);
                match value_candidate_limit {
                    Some(limit) => values.into_iter().take(limit).collect(),
                    None => values,
                }
            }
            (_, ValueRangeType::CountableRange { from, to }) => {
                let iter = (*from..*to).filter_map(|value| usize::try_from(value).ok());
                match value_candidate_limit {
                    Some(limit) => iter.take(limit).collect(),
                    None => iter.collect(),
                }
            }
            _ => {
                let count = self
                    .solution_value_count(solution_descriptor, solution)
                    .unwrap_or_default();
                let end = value_candidate_limit
                    .map(|limit| limit.min(count))
                    .unwrap_or(count);
                (0..end).collect()
            }
        }
    }

    pub(crate) fn has_values_for_entity_index(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
        entity_index: usize,
    ) -> bool {
        let entity = self.entity_for_index(solution_descriptor, solution, entity_index);
        match (&self.provider, &self.range_type) {
            (Some(provider), _) => !provider(entity).is_empty(),
            (_, ValueRangeType::CountableRange { from, to }) => from < to,
            _ => self
                .solution_value_count(solution_descriptor, solution)
                .is_some_and(|count| count > 0),
        }
    }

    pub(crate) fn has_candidate_values_for_entity_index(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
        entity_index: usize,
        value_candidate_limit: Option<usize>,
    ) -> bool {
        if matches!(value_candidate_limit, Some(0)) {
            return false;
        }
        if let Some(provider) = self.candidate_values {
            return !provider(solution, entity_index, self.variable_index).is_empty();
        }

        self.has_values_for_entity_index(solution_descriptor, solution, entity_index)
    }

    pub(crate) fn solution_value_count(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
    ) -> Option<usize> {
        self.value_range_provider.and_then(|provider_name| {
            solution_descriptor
                .problem_fact_descriptors
                .iter()
                .find(|descriptor| descriptor.solution_field == provider_name)
                .and_then(|descriptor| descriptor.extractor.as_ref())
                .and_then(|extractor| extractor.count(solution))
                .or_else(|| {
                    solution_descriptor
                        .entity_descriptors
                        .iter()
                        .find(|descriptor| descriptor.solution_field == provider_name)
                        .and_then(|descriptor| descriptor.extractor.as_ref())
                        .and_then(|extractor| extractor.count(solution))
                })
        })
    }

    pub(crate) fn has_unspecified_value_range(&self) -> bool {
        self.provider.is_none()
            && self.value_range_provider.is_none()
            && !matches!(self.range_type, ValueRangeType::CountableRange { .. })
    }

    pub(crate) fn countable_range_contains(from: i64, to: i64, value: usize) -> bool {
        i64::try_from(value).is_ok_and(|value| from <= value && value < to)
    }

    pub(crate) fn value_is_legal_for_entity_index(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
        entity_index: usize,
        candidate: Option<usize>,
    ) -> bool {
        let entity = self.entity_for_index(solution_descriptor, solution, entity_index);
        self.value_is_legal_for_entity(solution_descriptor, solution, entity, candidate)
    }

    pub(crate) fn value_is_legal_for_entity(
        &self,
        solution_descriptor: &SolutionDescriptor,
        solution: &dyn Any,
        entity: &dyn Any,
        candidate: Option<usize>,
    ) -> bool {
        let Some(value) = candidate else {
            return self.allows_unassigned;
        };
        match (&self.provider, &self.range_type) {
            (Some(provider), _) => provider(entity).into_iter().any(|allowed| allowed == value),
            (_, ValueRangeType::CountableRange { from, to }) => {
                Self::countable_range_contains(*from, *to, value)
            }
            _ => self
                .solution_value_count(solution_descriptor, solution)
                .is_some_and(|count| value < count),
        }
    }

    pub(crate) fn entity_order_key(&self, solution: &dyn Any, entity_index: usize) -> Option<i64> {
        self.construction_entity_order_key
            .map(|order_key| order_key(solution, entity_index))
    }

    pub(crate) fn value_order_key(
        &self,
        solution: &dyn Any,
        entity_index: usize,
        value: usize,
    ) -> Option<i64> {
        self.construction_value_order_key
            .map(|order_key| order_key(solution, entity_index, value))
    }
}

pub(crate) struct ResolvedVariableBinding<S> {
    binding: VariableBinding,
    runtime_construction_entity_order_key: Option<ConstructionEntityOrderKey<S>>,
    runtime_construction_value_order_key: Option<ConstructionValueOrderKey<S>>,
}

impl<S> Clone for ResolvedVariableBinding<S> {
    fn clone(&self) -> Self {
        Self {
            binding: self.binding.clone(),
            runtime_construction_entity_order_key: self.runtime_construction_entity_order_key,
            runtime_construction_value_order_key: self.runtime_construction_value_order_key,
        }
    }
}

impl<S> Debug for ResolvedVariableBinding<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ResolvedVariableBinding")
            .field("binding", &self.binding)
            .field(
                "has_runtime_construction_entity_order_key",
                &self.runtime_construction_entity_order_key.is_some(),
            )
            .field(
                "has_runtime_construction_value_order_key",
                &self.runtime_construction_value_order_key.is_some(),
            )
            .finish()
    }
}

impl<S> Deref for ResolvedVariableBinding<S> {
    type Target = VariableBinding;

    fn deref(&self) -> &Self::Target {
        &self.binding
    }
}

impl<S> ResolvedVariableBinding<S> {
    pub(crate) fn new(binding: VariableBinding) -> Self {
        Self {
            binding,
            runtime_construction_entity_order_key: None,
            runtime_construction_value_order_key: None,
        }
    }

    pub(crate) fn with_runtime_construction_hooks(
        mut self,
        entity_order_key: Option<ConstructionEntityOrderKey<S>>,
        value_order_key: Option<ConstructionValueOrderKey<S>>,
    ) -> Self {
        self.runtime_construction_entity_order_key = entity_order_key;
        self.runtime_construction_value_order_key = value_order_key;
        self
    }

    pub(crate) fn has_entity_order_key(&self) -> bool {
        self.runtime_construction_entity_order_key.is_some()
            || self.binding.construction_entity_order_key.is_some()
    }

    pub(crate) fn has_value_order_key(&self) -> bool {
        self.runtime_construction_value_order_key.is_some()
            || self.binding.construction_value_order_key.is_some()
    }

    pub(crate) fn runtime_value_order_key(&self) -> Option<ConstructionValueOrderKey<S>> {
        self.runtime_construction_value_order_key
    }

    pub(crate) fn clone_binding(&self) -> VariableBinding {
        self.binding.clone()
    }
}

impl<S> ResolvedVariableBinding<S>
where
    S: PlanningSolution + 'static,
{
    pub(crate) fn entity_order_key(&self, solution: &S, entity_index: usize) -> Option<i64> {
        self.runtime_construction_entity_order_key
            .and_then(|order_key| order_key(solution, entity_index, self.binding.variable_index))
            .or_else(|| {
                self.binding
                    .entity_order_key(solution as &dyn Any, entity_index)
            })
    }

    pub(crate) fn value_order_key(
        &self,
        solution: &S,
        entity_index: usize,
        value: usize,
    ) -> Option<i64> {
        self.runtime_construction_value_order_key
            .and_then(|order_key| {
                order_key(solution, entity_index, self.binding.variable_index, value)
            })
            .or_else(|| {
                self.binding
                    .value_order_key(solution as &dyn Any, entity_index, value)
            })
    }
}