use crate::api::constraint_set::IncrementalConstraint;
use crate::constraint::flattened_bi::FlattenedBiConstraint;
use solverforge_core::score::SoftScore;
use solverforge_core::{ConstraintRef, ImpactType};
#[derive(Clone)]
struct Employee {
id: usize,
unavailable_days: Vec<u32>,
}
#[derive(Clone)]
struct Shift {
employee_id: Option<usize>,
day: u32,
}
#[derive(Clone)]
struct Schedule {
shifts: Vec<Shift>,
employees: Vec<Employee>,
}
fn create_test_constraint() -> FlattenedBiConstraint<
Schedule,
Shift,
Employee,
u32,
Option<usize>,
u32,
fn(&Schedule) -> &[Shift],
fn(&Schedule) -> &[Employee],
impl Fn(&Shift) -> Option<usize>,
impl Fn(&Employee) -> Option<usize>,
impl Fn(&Employee) -> &[u32],
impl Fn(&u32) -> u32,
impl Fn(&Shift) -> u32,
impl Fn(&Schedule, &Shift, &u32) -> bool,
impl Fn(&Shift, &u32) -> SoftScore,
SoftScore,
> {
FlattenedBiConstraint::new(
ConstraintRef::new("", "Unavailable employee"),
ImpactType::Penalty,
(|s: &Schedule| s.shifts.as_slice()) as fn(&Schedule) -> &[Shift],
(|s: &Schedule| s.employees.as_slice()) as fn(&Schedule) -> &[Employee],
|shift: &Shift| shift.employee_id,
|emp: &Employee| Some(emp.id),
|emp: &Employee| emp.unavailable_days.as_slice(),
|day: &u32| *day,
|shift: &Shift| shift.day,
|_s: &Schedule, shift: &Shift, day: &u32| shift.employee_id.is_some() && shift.day == *day,
|_shift: &Shift, _day: &u32| SoftScore::of(1),
false,
)
}
#[test]
fn test_evaluate_single_match() {
let constraint = create_test_constraint();
let schedule = Schedule {
shifts: vec![
Shift {
employee_id: Some(0),
day: 5,
},
Shift {
employee_id: Some(0),
day: 10,
},
],
employees: vec![Employee {
id: 0,
unavailable_days: vec![5, 15],
}],
};
assert_eq!(constraint.evaluate(&schedule), SoftScore::of(-1));
}
#[test]
fn test_evaluate_no_match() {
let constraint = create_test_constraint();
let schedule = Schedule {
shifts: vec![Shift {
employee_id: Some(0),
day: 10,
}],
employees: vec![Employee {
id: 0,
unavailable_days: vec![5, 15],
}],
};
assert_eq!(constraint.evaluate(&schedule), SoftScore::of(0));
}
#[test]
fn test_incremental() {
let mut constraint = create_test_constraint();
let schedule = Schedule {
shifts: vec![
Shift {
employee_id: Some(0),
day: 5,
}, Shift {
employee_id: Some(0),
day: 10,
}, ],
employees: vec![Employee {
id: 0,
unavailable_days: vec![5, 15],
}],
};
let initial = constraint.initialize(&schedule);
assert_eq!(initial, SoftScore::of(-1));
let delta = constraint.on_retract(&schedule, 0, 0);
assert_eq!(delta, SoftScore::of(1));
let delta = constraint.on_insert(&schedule, 0, 0);
assert_eq!(delta, SoftScore::of(-1)); }
#[test]
fn test_unassigned_shift() {
let constraint = create_test_constraint();
let schedule = Schedule {
shifts: vec![Shift {
employee_id: None, day: 5,
}],
employees: vec![Employee {
id: 0,
unavailable_days: vec![5],
}],
};
assert_eq!(constraint.evaluate(&schedule), SoftScore::of(0));
}