use std::any::Any;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use solverforge_core::score::Score;
use solverforge_core::ConstraintRef;
#[derive(Clone)]
pub struct EntityRef {
pub type_name: String,
pub display: String,
entity: Arc<dyn Any + Send + Sync>,
}
impl EntityRef {
pub fn new<T: Clone + Debug + Send + Sync + 'static>(entity: &T) -> Self {
Self {
type_name: std::any::type_name::<T>().to_string(),
display: format!("{:?}", entity),
entity: Arc::new(entity.clone()),
}
}
pub fn with_display<T: Clone + Send + Sync + 'static>(entity: &T, display: String) -> Self {
Self {
type_name: std::any::type_name::<T>().to_string(),
display,
entity: Arc::new(entity.clone()),
}
}
pub fn as_entity<T: 'static>(&self) -> Option<&T> {
self.entity.downcast_ref::<T>()
}
pub fn short_type_name(&self) -> &str {
self.type_name
.rsplit("::")
.next()
.unwrap_or(&self.type_name)
}
}
impl Debug for EntityRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EntityRef")
.field("type", &self.short_type_name())
.field("display", &self.display)
.finish()
}
}
impl PartialEq for EntityRef {
fn eq(&self, other: &Self) -> bool {
self.type_name == other.type_name && self.display == other.display
}
}
impl Eq for EntityRef {}
impl Hash for EntityRef {
fn hash<H: Hasher>(&self, state: &mut H) {
self.type_name.hash(state);
self.display.hash(state);
}
}
#[derive(Debug, Clone)]
pub struct ConstraintJustification {
pub entities: Vec<EntityRef>,
pub description: String,
}
impl ConstraintJustification {
pub fn new(entities: Vec<EntityRef>) -> Self {
let description = if entities.is_empty() {
"No entities".to_string()
} else {
entities
.iter()
.map(|e| e.display.as_str())
.collect::<Vec<_>>()
.join(", ")
};
Self {
entities,
description,
}
}
pub fn with_description(entities: Vec<EntityRef>, description: String) -> Self {
Self {
entities,
description,
}
}
}
#[derive(Debug, Clone)]
pub struct DetailedConstraintMatch<Sc: Score> {
pub constraint_ref: ConstraintRef,
pub score: Sc,
pub justification: ConstraintJustification,
}
impl<Sc: Score> DetailedConstraintMatch<Sc> {
pub fn new(
constraint_ref: ConstraintRef,
score: Sc,
justification: ConstraintJustification,
) -> Self {
Self {
constraint_ref,
score,
justification,
}
}
}
#[derive(Debug, Clone)]
pub struct DetailedConstraintEvaluation<Sc: Score> {
pub total_score: Sc,
pub match_count: usize,
pub matches: Vec<DetailedConstraintMatch<Sc>>,
}
impl<Sc: Score> DetailedConstraintEvaluation<Sc> {
pub fn new(total_score: Sc, matches: Vec<DetailedConstraintMatch<Sc>>) -> Self {
let match_count = matches.len();
Self {
total_score,
match_count,
matches,
}
}
pub fn empty() -> Self {
Self {
total_score: Sc::zero(),
match_count: 0,
matches: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ConstraintAnalysis<Sc: Score> {
pub constraint_ref: ConstraintRef,
pub weight: Sc,
pub score: Sc,
pub matches: Vec<DetailedConstraintMatch<Sc>>,
pub is_hard: bool,
}
impl<Sc: Score> ConstraintAnalysis<Sc> {
pub fn new(
constraint_ref: ConstraintRef,
weight: Sc,
score: Sc,
matches: Vec<DetailedConstraintMatch<Sc>>,
is_hard: bool,
) -> Self {
Self {
constraint_ref,
weight,
score,
matches,
is_hard,
}
}
pub fn match_count(&self) -> usize {
self.matches.len()
}
pub fn name(&self) -> &str {
&self.constraint_ref.name
}
}
#[derive(Debug, Clone)]
pub struct ScoreExplanation<Sc: Score> {
pub score: Sc,
pub constraint_analyses: Vec<ConstraintAnalysis<Sc>>,
}
impl<Sc: Score> ScoreExplanation<Sc> {
pub fn new(score: Sc, constraint_analyses: Vec<ConstraintAnalysis<Sc>>) -> Self {
Self {
score,
constraint_analyses,
}
}
pub fn total_match_count(&self) -> usize {
self.constraint_analyses
.iter()
.map(|a| a.match_count())
.sum()
}
pub fn non_zero_constraints(&self) -> Vec<&ConstraintAnalysis<Sc>> {
self.constraint_analyses
.iter()
.filter(|a| a.score != Sc::zero())
.collect()
}
pub fn all_matches(&self) -> Vec<&DetailedConstraintMatch<Sc>> {
self.constraint_analyses
.iter()
.flat_map(|a| &a.matches)
.collect()
}
}
#[derive(Debug, Clone)]
pub struct Indictment<Sc: Score> {
pub entity: EntityRef,
pub score: Sc,
pub constraint_matches: HashMap<ConstraintRef, Vec<DetailedConstraintMatch<Sc>>>,
}
impl<Sc: Score> Indictment<Sc> {
pub fn new(entity: EntityRef) -> Self {
Self {
entity,
score: Sc::zero(),
constraint_matches: HashMap::new(),
}
}
pub fn add_match(&mut self, constraint_match: DetailedConstraintMatch<Sc>) {
self.score = self.score + constraint_match.score;
self.constraint_matches
.entry(constraint_match.constraint_ref.clone())
.or_default()
.push(constraint_match);
}
pub fn match_count(&self) -> usize {
self.constraint_matches
.values()
.map(|v| v.len())
.sum::<usize>()
}
pub fn violated_constraints(&self) -> Vec<&ConstraintRef> {
self.constraint_matches.keys().collect()
}
pub fn constraint_count(&self) -> usize {
self.constraint_matches.len()
}
}
#[derive(Debug, Clone)]
pub struct IndictmentMap<Sc: Score> {
pub indictments: HashMap<EntityRef, Indictment<Sc>>,
}
impl<Sc: Score> IndictmentMap<Sc> {
pub fn new() -> Self {
Self {
indictments: HashMap::new(),
}
}
pub fn from_matches(matches: Vec<DetailedConstraintMatch<Sc>>) -> Self {
let mut map = Self::new();
for m in matches {
for entity in &m.justification.entities {
map.indictments
.entry(entity.clone())
.or_insert_with(|| Indictment::new(entity.clone()))
.add_match(m.clone());
}
}
map
}
pub fn get(&self, entity: &EntityRef) -> Option<&Indictment<Sc>> {
self.indictments.get(entity)
}
pub fn entities(&self) -> impl Iterator<Item = &EntityRef> {
self.indictments.keys()
}
pub fn worst_entities(&self) -> Vec<&EntityRef> {
let mut entities: Vec<_> = self.indictments.keys().collect();
entities.sort_by(|a, b| {
let score_a = &self.indictments[*a].score;
let score_b = &self.indictments[*b].score;
score_a.cmp(score_b)
});
entities
}
pub fn len(&self) -> usize {
self.indictments.len()
}
pub fn is_empty(&self) -> bool {
self.indictments.is_empty()
}
}
impl<Sc: Score> Default for IndictmentMap<Sc> {
fn default() -> Self {
Self::new()
}
}