use std::fmt::Debug;
use std::marker::PhantomData;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
pub trait ValueSelector<S: PlanningSolution, V>: Send + Debug {
fn iter_typed<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
descriptor_index: usize,
entity_index: usize,
) -> impl Iterator<Item = V> + 'a;
fn size<D: Director<S>>(
&self,
score_director: &D,
descriptor_index: usize,
entity_index: usize,
) -> usize;
fn is_never_ending(&self) -> bool {
false
}
}
pub struct StaticValueSelector<S, V> {
values: Vec<V>,
_phantom: PhantomData<fn() -> S>,
}
impl<S, V: Clone> Clone for StaticValueSelector<S, V> {
fn clone(&self) -> Self {
Self {
values: self.values.clone(),
_phantom: PhantomData,
}
}
}
impl<S, V: Debug> Debug for StaticValueSelector<S, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StaticValueSelector")
.field("values", &self.values)
.finish()
}
}
impl<S, V: Clone> StaticValueSelector<S, V> {
pub fn new(values: Vec<V>) -> Self {
Self {
values,
_phantom: PhantomData,
}
}
pub fn values(&self) -> &[V] {
&self.values
}
}
impl<S, V> ValueSelector<S, V> for StaticValueSelector<S, V>
where
S: PlanningSolution,
V: Clone + Send + Debug + 'static,
{
fn iter_typed<'a, D: Director<S>>(
&'a self,
_score_director: &'a D,
_descriptor_index: usize,
_entity_index: usize,
) -> impl Iterator<Item = V> + 'a {
self.values.iter().cloned()
}
fn size<D: Director<S>>(
&self,
_score_director: &D,
_descriptor_index: usize,
_entity_index: usize,
) -> usize {
self.values.len()
}
}
pub struct FromSolutionValueSelector<S, V> {
extractor: fn(&S) -> Vec<V>,
_phantom: PhantomData<(fn() -> S, fn() -> V)>,
}
impl<S, V> Debug for FromSolutionValueSelector<S, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FromSolutionValueSelector").finish()
}
}
impl<S, V> FromSolutionValueSelector<S, V> {
pub fn new(extractor: fn(&S) -> Vec<V>) -> Self {
Self {
extractor,
_phantom: PhantomData,
}
}
}
pub struct PerEntityValueSelector<S, V> {
extractor: fn(&S, usize) -> Vec<V>,
_phantom: PhantomData<(fn() -> S, fn() -> V)>,
}
impl<S, V> Debug for PerEntityValueSelector<S, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PerEntityValueSelector").finish()
}
}
impl<S, V> PerEntityValueSelector<S, V> {
pub fn new(extractor: fn(&S, usize) -> Vec<V>) -> Self {
Self {
extractor,
_phantom: PhantomData,
}
}
}
impl<S, V> ValueSelector<S, V> for PerEntityValueSelector<S, V>
where
S: PlanningSolution,
V: Clone + Send + Debug + 'static,
{
fn iter_typed<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
_descriptor_index: usize,
entity_index: usize,
) -> impl Iterator<Item = V> + 'a {
(self.extractor)(score_director.working_solution(), entity_index).into_iter()
}
fn size<D: Director<S>>(
&self,
score_director: &D,
_descriptor_index: usize,
entity_index: usize,
) -> usize {
(self.extractor)(score_director.working_solution(), entity_index).len()
}
}
impl<S, V> ValueSelector<S, V> for FromSolutionValueSelector<S, V>
where
S: PlanningSolution,
V: Clone + Send + Debug + 'static,
{
fn iter_typed<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
_descriptor_index: usize,
_entity_index: usize,
) -> impl Iterator<Item = V> + 'a {
let values = (self.extractor)(score_director.working_solution());
values.into_iter()
}
fn size<D: Director<S>>(
&self,
score_director: &D,
_descriptor_index: usize,
_entity_index: usize,
) -> usize {
(self.extractor)(score_director.working_solution()).len()
}
}
pub struct RangeValueSelector<S> {
count_fn: fn(&S) -> usize,
_phantom: PhantomData<fn() -> S>,
}
impl<S> Debug for RangeValueSelector<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RangeValueSelector").finish()
}
}
impl<S> RangeValueSelector<S> {
pub fn new(count_fn: fn(&S) -> usize) -> Self {
Self {
count_fn,
_phantom: PhantomData,
}
}
}
impl<S> ValueSelector<S, usize> for RangeValueSelector<S>
where
S: PlanningSolution,
{
fn iter_typed<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
_descriptor_index: usize,
_entity_index: usize,
) -> impl Iterator<Item = usize> + 'a {
let count = (self.count_fn)(score_director.working_solution());
0..count
}
fn size<D: Director<S>>(
&self,
score_director: &D,
_descriptor_index: usize,
_entity_index: usize,
) -> usize {
(self.count_fn)(score_director.working_solution())
}
}
#[cfg(test)]
mod tests {
use super::*;
use solverforge_core::domain::{
EntityCollectionExtractor, EntityDescriptor, SolutionDescriptor,
};
use solverforge_core::score::SoftScore;
use solverforge_scoring::ScoreDirector;
use std::any::TypeId;
#[derive(Clone, Debug)]
struct Task {
id: usize,
priority: Option<i32>,
}
#[derive(Clone, Debug)]
struct TaskSolution {
tasks: Vec<Task>,
score: Option<SoftScore>,
}
impl PlanningSolution for TaskSolution {
type Score = SoftScore;
fn score(&self) -> Option<Self::Score> {
self.score
}
fn set_score(&mut self, score: Option<Self::Score>) {
self.score = score;
}
}
fn create_director(tasks: Vec<Task>) -> ScoreDirector<TaskSolution, ()> {
let solution = TaskSolution { tasks, score: None };
let extractor = Box::new(EntityCollectionExtractor::new(
"Task",
"tasks",
|s: &TaskSolution| &s.tasks,
|s: &mut TaskSolution| &mut s.tasks,
));
let entity_desc =
EntityDescriptor::new("Task", TypeId::of::<Task>(), "tasks").with_extractor(extractor);
let descriptor = SolutionDescriptor::new("TaskSolution", TypeId::of::<TaskSolution>())
.with_entity(entity_desc);
ScoreDirector::simple(solution, descriptor, |s, _| s.tasks.len())
}
#[test]
fn test_static_value_selector_selector() {
let director = create_director(vec![Task {
id: 0,
priority: None,
}]);
let selector = StaticValueSelector::<TaskSolution, i32>::new(vec![1, 2, 3, 4, 5]);
let values: Vec<_> = selector.iter_typed(&director, 0, 0).collect();
assert_eq!(values, vec![1, 2, 3, 4, 5]);
assert_eq!(selector.size(&director, 0, 0), 5);
}
#[test]
fn test_from_solution_value_selector_selector() {
let director = create_director(vec![
Task {
id: 0,
priority: Some(10),
},
Task {
id: 1,
priority: Some(20),
},
]);
let solution = director.working_solution();
assert_eq!(solution.tasks[0].id, 0);
assert_eq!(solution.tasks[1].id, 1);
fn extract_priorities(s: &TaskSolution) -> Vec<i32> {
s.tasks.iter().filter_map(|t| t.priority).collect()
}
let selector = FromSolutionValueSelector::new(extract_priorities);
let values: Vec<_> = selector.iter_typed(&director, 0, 0).collect();
assert_eq!(values, vec![10, 20]);
}
}