use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use super::entity::{EntityReference, EntitySelector};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pillar {
pub entities: Vec<EntityReference>,
}
impl Pillar {
pub fn new(entities: Vec<EntityReference>) -> Self {
Self { entities }
}
pub fn size(&self) -> usize {
self.entities.len()
}
pub fn is_empty(&self) -> bool {
self.entities.is_empty()
}
pub fn first(&self) -> Option<&EntityReference> {
self.entities.first()
}
pub fn iter(&self) -> impl Iterator<Item = &EntityReference> {
self.entities.iter()
}
}
pub trait PillarSelector<S: PlanningSolution>: Send + Debug {
fn iter<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
) -> impl Iterator<Item = Pillar> + 'a;
fn size<D: Director<S>>(&self, score_director: &D) -> usize;
fn is_never_ending(&self) -> bool {
false
}
fn descriptor_index(&self) -> usize;
}
#[derive(Debug, Clone)]
pub struct SubPillarConfig {
pub enabled: bool,
pub minimum_size: usize,
pub maximum_size: usize,
}
impl Default for SubPillarConfig {
fn default() -> Self {
Self {
enabled: false,
minimum_size: 1,
maximum_size: usize::MAX,
}
}
}
impl SubPillarConfig {
pub fn none() -> Self {
Self::default()
}
pub fn all() -> Self {
Self {
enabled: true,
minimum_size: 1,
maximum_size: usize::MAX,
}
}
pub fn with_minimum_size(mut self, size: usize) -> Self {
self.minimum_size = size.max(1);
self
}
pub fn with_maximum_size(mut self, size: usize) -> Self {
self.maximum_size = size;
self
}
}
pub struct DefaultPillarSelector<S, V, ES, E>
where
S: PlanningSolution,
V: Clone + Eq + Hash + Send + Sync + 'static,
ES: EntitySelector<S>,
E: Fn(&dyn Director<S>, usize, usize) -> Option<V> + Send + Sync,
{
entity_selector: ES,
descriptor_index: usize,
variable_name: &'static str,
value_extractor: E,
sub_pillar_config: SubPillarConfig,
_phantom: PhantomData<(fn() -> S, fn() -> V)>,
}
impl<S, V, ES, E> Debug for DefaultPillarSelector<S, V, ES, E>
where
S: PlanningSolution,
V: Clone + Eq + Hash + Send + Sync + 'static,
ES: EntitySelector<S> + Debug,
E: Fn(&dyn Director<S>, usize, usize) -> Option<V> + Send + Sync,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DefaultPillarSelector")
.field("entity_selector", &self.entity_selector)
.field("descriptor_index", &self.descriptor_index)
.field("variable_name", &self.variable_name)
.field("sub_pillar_config", &self.sub_pillar_config)
.finish()
}
}
impl<S, V, ES, E> DefaultPillarSelector<S, V, ES, E>
where
S: PlanningSolution,
V: Clone + Eq + Hash + Send + Sync + 'static,
ES: EntitySelector<S>,
E: Fn(&dyn Director<S>, usize, usize) -> Option<V> + Send + Sync,
{
pub fn new(
entity_selector: ES,
descriptor_index: usize,
variable_name: &'static str,
value_extractor: E,
) -> Self {
Self {
entity_selector,
descriptor_index,
variable_name,
value_extractor,
sub_pillar_config: SubPillarConfig::default(),
_phantom: PhantomData,
}
}
pub fn with_sub_pillar_config(mut self, config: SubPillarConfig) -> Self {
self.sub_pillar_config = config;
self
}
pub fn variable_name(&self) -> &str {
self.variable_name
}
fn build_pillars<D: Director<S>>(&self, score_director: &D) -> Vec<Pillar> {
let mut value_to_entities: HashMap<Option<V>, Vec<EntityReference>> = HashMap::new();
for entity_ref in self.entity_selector.iter(score_director) {
let value = (self.value_extractor)(
score_director,
entity_ref.descriptor_index,
entity_ref.entity_index,
);
value_to_entities.entry(value).or_default().push(entity_ref);
}
let min_size = self.sub_pillar_config.minimum_size;
value_to_entities
.into_values()
.filter(|entities| entities.len() >= min_size)
.map(Pillar::new)
.collect()
}
}
impl<S, V, ES, E> PillarSelector<S> for DefaultPillarSelector<S, V, ES, E>
where
S: PlanningSolution,
V: Clone + Eq + Hash + Send + Sync + 'static,
ES: EntitySelector<S>,
E: Fn(&dyn Director<S>, usize, usize) -> Option<V> + Send + Sync,
{
fn iter<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
) -> impl Iterator<Item = Pillar> + 'a {
let pillars = self.build_pillars(score_director);
pillars.into_iter()
}
fn size<D: Director<S>>(&self, score_director: &D) -> usize {
self.build_pillars(score_director).len()
}
fn descriptor_index(&self) -> usize {
self.descriptor_index
}
}
#[cfg(test)]
#[path = "pillar_tests.rs"]
mod tests;