solverforge-solver 0.11.1

Solver engine for SolverForge
Documentation
use std::fmt;

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

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScalarGroupLimits {
    pub value_candidate_limit: Option<usize>,
    pub group_candidate_limit: Option<usize>,
    pub max_moves_per_step: Option<usize>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ScalarGroupEdit {
    pub descriptor_index: usize,
    pub entity_index: usize,
    pub variable_name: &'static str,
    pub to_value: Option<usize>,
}

impl ScalarGroupEdit {
    pub fn set_scalar(
        descriptor_index: usize,
        entity_index: usize,
        variable_name: &'static str,
        to_value: Option<usize>,
    ) -> Self {
        Self {
            descriptor_index,
            entity_index,
            variable_name,
            to_value,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ScalarGroupCandidate {
    pub reason: &'static str,
    pub edits: Vec<ScalarGroupEdit>,
    pub construction_slot_key: Option<usize>,
    pub construction_entity_order_key: Option<i64>,
    pub construction_value_order_key: Option<i64>,
}

impl ScalarGroupCandidate {
    pub fn new(reason: &'static str, edits: Vec<ScalarGroupEdit>) -> Self {
        Self {
            reason,
            edits,
            construction_slot_key: None,
            construction_entity_order_key: None,
            construction_value_order_key: None,
        }
    }

    pub fn with_construction_slot_key(mut self, key: usize) -> Self {
        self.construction_slot_key = Some(key);
        self
    }

    pub fn with_construction_entity_order_key(mut self, key: i64) -> Self {
        self.construction_entity_order_key = Some(key);
        self
    }

    pub fn with_construction_value_order_key(mut self, key: i64) -> Self {
        self.construction_value_order_key = Some(key);
        self
    }
}

pub type ScalarGroupCandidateProvider<S> = fn(&S, ScalarGroupLimits) -> Vec<ScalarGroupCandidate>;

pub struct ScalarGroupMember<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 allows_unassigned: bool,
}

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

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

impl<S> ScalarGroupMember<S> {
    pub fn from_scalar_context(context: ScalarVariableContext<S>) -> Self {
        Self {
            descriptor_index: context.descriptor_index,
            variable_index: context.variable_index,
            entity_type_name: context.entity_type_name,
            variable_name: context.variable_name,
            getter: context.getter,
            setter: context.setter,
            value_source: context.value_source,
            allows_unassigned: context.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)
            }
        }
    }
}

impl<S> fmt::Debug for ScalarGroupMember<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ScalarGroupMember")
            .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 ScalarGroupContext<S> {
    pub group_name: &'static str,
    pub members: Vec<ScalarGroupMember<S>>,
    pub candidate_provider: ScalarGroupCandidateProvider<S>,
}

impl<S> ScalarGroupContext<S> {
    pub fn new(
        group_name: &'static str,
        members: Vec<ScalarGroupMember<S>>,
        candidate_provider: ScalarGroupCandidateProvider<S>,
    ) -> Self {
        Self {
            group_name,
            members,
            candidate_provider,
        }
    }

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

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

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