solverforge-solver 0.12.0

Solver engine for SolverForge
Documentation
use std::fmt;

use crate::builder::context::ScalarVariableSlot;
use crate::planning::{CoverageGroup, CoverageGroupLimits, ScalarEdit};

pub struct CoverageGroupBinding<S> {
    pub group_name: &'static str,
    pub target: ScalarVariableSlot<S>,
    pub required_slot: fn(&S, usize) -> bool,
    pub capacity_key: Option<fn(&S, usize, usize) -> Option<usize>>,
    pub entity_order: Option<fn(&S, usize) -> i64>,
    pub value_order: Option<fn(&S, usize, usize) -> i64>,
    pub limits: CoverageGroupLimits,
}

impl<S> Clone for CoverageGroupBinding<S> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<S> Copy for CoverageGroupBinding<S> {}

impl<S> CoverageGroupBinding<S> {
    pub fn bind(group: CoverageGroup<S>, scalar_slots: &[ScalarVariableSlot<S>]) -> Self {
        let target = group.target();
        let target_slot = scalar_slots
            .iter()
            .copied()
            .find(|slot| {
                slot.descriptor_index == target.descriptor_index()
                    && slot.variable_name == target.variable_name()
            })
            .unwrap_or_else(|| {
                panic!(
                    "coverage group `{}` target {}.{} did not match a scalar planning variable",
                    group.group_name(),
                    target.descriptor_index(),
                    target.variable_name(),
                )
            });
        assert!(
            target_slot.allows_unassigned,
            "coverage group `{}` target {}.{} must allow unassigned values",
            group.group_name(),
            target_slot.entity_type_name,
            target_slot.variable_name,
        );
        let required_slot = group.required_slot().unwrap_or_else(|| {
            panic!(
                "coverage group `{}` requires a required-slot predicate",
                group.group_name(),
            )
        });
        Self {
            group_name: group.group_name(),
            target: target_slot,
            required_slot,
            capacity_key: group.capacity_key(),
            entity_order: group.entity_order(),
            value_order: group.value_order(),
            limits: group.limits(),
        }
    }

    pub fn entity_count(&self, solution: &S) -> usize {
        (self.target.entity_count)(solution)
    }

    pub fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
        self.target.current_value(solution, entity_index)
    }

    pub fn is_required(&self, solution: &S, entity_index: usize) -> bool {
        (self.required_slot)(solution, entity_index)
    }

    pub fn capacity_key(&self, solution: &S, entity_index: usize, value: usize) -> Option<usize> {
        self.capacity_key
            .and_then(|capacity_key| capacity_key(solution, entity_index, value))
    }

    pub fn entity_order_key(&self, solution: &S, entity_index: usize) -> i64 {
        self.entity_order
            .map(|entity_order| entity_order(solution, entity_index))
            .unwrap_or(entity_index as i64)
    }

    pub fn value_order_key(&self, solution: &S, entity_index: usize, value: usize) -> i64 {
        self.value_order
            .map(|value_order| value_order(solution, entity_index, value))
            .unwrap_or(value as i64)
    }

    pub fn candidate_values(
        &self,
        solution: &S,
        entity_index: usize,
        value_candidate_limit: Option<usize>,
    ) -> Vec<usize> {
        let mut values =
            self.target
                .candidate_values_for_entity(solution, entity_index, value_candidate_limit);
        values.sort_by_key(|value| self.value_order_key(solution, entity_index, *value));
        values
    }

    pub fn value_is_legal(&self, solution: &S, entity_index: usize, value: Option<usize>) -> bool {
        self.target.value_is_legal(solution, entity_index, value)
    }

    pub fn edit(&self, entity_index: usize, value: Option<usize>) -> ScalarEdit<S> {
        ScalarEdit::from_descriptor_index(
            self.target.descriptor_index,
            entity_index,
            self.target.variable_name,
            value,
        )
    }
}

impl<S> fmt::Debug for CoverageGroupBinding<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CoverageGroupBinding")
            .field("group_name", &self.group_name)
            .field("entity_type_name", &self.target.entity_type_name)
            .field("variable_name", &self.target.variable_name)
            .field("has_capacity_key", &self.capacity_key.is_some())
            .field("limits", &self.limits)
            .finish()
    }
}

pub fn bind_coverage_groups<S>(
    groups: Vec<CoverageGroup<S>>,
    scalar_slots: &[ScalarVariableSlot<S>],
) -> Vec<CoverageGroupBinding<S>> {
    groups
        .into_iter()
        .map(|group| CoverageGroupBinding::bind(group, scalar_slots))
        .collect()
}