solverforge-solver 0.12.1

Solver engine for SolverForge
Documentation
use std::fmt;

use crate::planning::{
    ScalarAssignmentDeclaration, ScalarCandidateProvider, ScalarEdit, ScalarGroup, ScalarGroupKind,
    ScalarGroupLimits,
};

use super::value_source::ValueSource;
use super::variable::{ScalarGetter, ScalarSetter, ScalarVariableSlot};

pub struct ScalarGroupMemberBinding<S> {
    pub descriptor_index: usize,
    pub variable_index: usize,
    pub entity_type_name: &'static str,
    pub variable_name: &'static str,
    pub getter: ScalarGetter<S>,
    pub setter: ScalarSetter<S>,
    pub value_source: ValueSource<S>,
    pub entity_count: fn(&S) -> usize,
    pub candidate_values: Option<super::variable::ScalarCandidateValues<S>>,
    pub allows_unassigned: bool,
}

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

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

impl<S> ScalarGroupMemberBinding<S> {
    pub fn from_scalar_slot(slot: ScalarVariableSlot<S>) -> Self {
        Self {
            descriptor_index: slot.descriptor_index,
            variable_index: slot.variable_index,
            entity_type_name: slot.entity_type_name,
            variable_name: slot.variable_name,
            getter: slot.getter,
            setter: slot.setter,
            value_source: slot.value_source,
            entity_count: slot.entity_count,
            candidate_values: slot.candidate_values,
            allows_unassigned: slot.allows_unassigned,
        }
    }

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

    pub fn value_is_legal(
        &self,
        solution: &S,
        entity_index: usize,
        candidate: Option<usize>,
    ) -> bool {
        let Some(value) = candidate else {
            return self.allows_unassigned;
        };
        match self.value_source {
            ValueSource::Empty => false,
            ValueSource::CountableRange { from, to } => from <= value && value < to,
            ValueSource::SolutionCount {
                count_fn,
                provider_index,
            } => value < count_fn(solution, provider_index),
            ValueSource::EntitySlice { values_for_entity } => {
                values_for_entity(solution, entity_index, self.variable_index).contains(&value)
            }
        }
    }

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

    pub fn candidate_values(
        &self,
        solution: &S,
        entity_index: usize,
        value_candidate_limit: Option<usize>,
    ) -> Vec<usize> {
        if let Some(candidate_values) = self.candidate_values {
            let values = candidate_values(solution, entity_index, self.variable_index);
            return match value_candidate_limit {
                Some(limit) => values.iter().copied().take(limit).collect(),
                None => values.to_vec(),
            };
        }
        match self.value_source {
            ValueSource::Empty => Vec::new(),
            ValueSource::CountableRange { from, to } => {
                let end = value_candidate_limit
                    .map(|limit| from.saturating_add(limit).min(to))
                    .unwrap_or(to);
                (from..end).collect()
            }
            ValueSource::SolutionCount {
                count_fn,
                provider_index,
            } => {
                let count = count_fn(solution, provider_index);
                let end = value_candidate_limit
                    .map(|limit| limit.min(count))
                    .unwrap_or(count);
                (0..end).collect()
            }
            ValueSource::EntitySlice { values_for_entity } => {
                let values = values_for_entity(solution, entity_index, self.variable_index);
                match value_candidate_limit {
                    Some(limit) => values.iter().copied().take(limit).collect(),
                    None => values.to_vec(),
                }
            }
        }
    }
}

impl<S> fmt::Debug for ScalarGroupMemberBinding<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ScalarGroupMemberBinding")
            .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("value_source", &self.value_source)
            .field("allows_unassigned", &self.allows_unassigned)
            .finish()
    }
}

pub struct ScalarGroupBinding<S> {
    pub group_name: &'static str,
    pub members: Vec<ScalarGroupMemberBinding<S>>,
    pub kind: ScalarGroupBindingKind<S>,
    pub limits: ScalarGroupLimits,
}

pub enum ScalarGroupBindingKind<S> {
    Candidates {
        candidate_provider: ScalarCandidateProvider<S>,
    },
    Assignment(ScalarAssignmentBinding<S>),
}

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

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

pub struct ScalarAssignmentBinding<S> {
    pub target: ScalarGroupMemberBinding<S>,
    pub required_entity: Option<fn(&S, usize) -> bool>,
    pub capacity_key: Option<fn(&S, usize, usize) -> Option<usize>>,
    pub position_key: Option<fn(&S, usize) -> i64>,
    pub sequence_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>,
}

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

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

impl<S> ScalarAssignmentBinding<S> {
    fn bind(
        group_name: &'static str,
        members: &[ScalarGroupMemberBinding<S>],
        declaration: ScalarAssignmentDeclaration<S>,
    ) -> Self {
        assert_eq!(
            members.len(),
            1,
            "assignment scalar group `{group_name}` must target exactly one scalar planning variable",
        );
        let target = members[0];
        assert!(
            target.allows_unassigned,
            "assignment scalar group `{group_name}` target {}.{} must allow unassigned values",
            target.entity_type_name, target.variable_name,
        );
        Self {
            target,
            required_entity: declaration.required_entity,
            capacity_key: declaration.capacity_key,
            position_key: declaration.position_key,
            sequence_key: declaration.sequence_key,
            entity_order: declaration.entity_order,
            value_order: declaration.value_order,
        }
    }

    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_entity
            .map(|required_entity| required_entity(solution, entity_index))
            .unwrap_or(false)
    }

    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 position_key(&self, solution: &S, entity_index: usize) -> Option<i64> {
        self.position_key
            .map(|position_key| position_key(solution, entity_index))
    }

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

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

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

    pub fn candidate_values(
        &self,
        solution: &S,
        entity_index: usize,
        value_candidate_limit: Option<usize>,
    ) -> Vec<usize> {
        let mut values =
            self.target
                .candidate_values(solution, entity_index, value_candidate_limit);
        values.sort_by_key(|value| (self.value_order_key(solution, entity_index, *value), *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> ScalarGroupBinding<S> {
    pub fn bind(group: ScalarGroup<S>, scalar_slots: &[ScalarVariableSlot<S>]) -> Self {
        let members = group
            .targets()
            .iter()
            .map(|target| {
                let descriptor_index = target.descriptor_index();
                let variable_name = target.variable_name();
                let slot = scalar_slots
                    .iter()
                    .copied()
                    .find(|slot| {
                        slot.descriptor_index == descriptor_index
                            && slot.variable_name == variable_name
                    })
                    .unwrap_or_else(|| {
                        panic!(
                            "scalar group `{}` targets unknown scalar variable `{}` on descriptor {}",
                            group.group_name(),
                            variable_name,
                            descriptor_index
                        )
                    });
                ScalarGroupMemberBinding::from_scalar_slot(slot)
            })
            .collect::<Vec<_>>();

        let kind = match group.kind() {
            ScalarGroupKind::Assignment(declaration) => ScalarGroupBindingKind::Assignment(
                ScalarAssignmentBinding::bind(group.group_name(), &members, declaration),
            ),
            ScalarGroupKind::Candidates { candidate_provider } => {
                ScalarGroupBindingKind::Candidates { candidate_provider }
            }
        };

        Self {
            group_name: group.group_name(),
            members,
            kind,
            limits: group.limits(),
        }
    }

    pub fn member_for_edit(&self, edit: &ScalarEdit<S>) -> Option<ScalarGroupMemberBinding<S>> {
        self.members.iter().copied().find(|member| {
            member.descriptor_index == edit.descriptor_index()
                && member.variable_name == edit.variable_name()
        })
    }
}

impl<S> Clone for ScalarGroupBinding<S> {
    fn clone(&self) -> Self {
        Self {
            group_name: self.group_name,
            members: self.members.clone(),
            kind: self.kind,
            limits: self.limits,
        }
    }
}

impl<S> fmt::Debug for ScalarGroupBinding<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ScalarGroupBinding")
            .field("group_name", &self.group_name)
            .field("members", &self.members)
            .finish_non_exhaustive()
    }
}

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