use crate::api::constraint_set::IncrementalConstraint;
use crate::constraint::balance::BalanceConstraint;
use crate::stream::collection_extract::vec;
use crate::stream::filter::TrueFilter;
use solverforge_core::score::SoftScore;
use solverforge_core::{ConstraintRef, ImpactType};
#[derive(Clone)]
struct Shift {
employee_id: Option<usize>,
}
#[derive(Clone)]
struct Solution {
shifts: Vec<Shift>,
}
#[test]
fn test_balance_evaluate_equal_distribution() {
let constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance"),
ImpactType::Penalty,
vec(|s: &Solution| &s.shifts),
TrueFilter,
|shift: &Shift| shift.employee_id,
SoftScore::of(1000), false,
);
let solution = Solution {
shifts: vec![
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(1),
},
Shift {
employee_id: Some(1),
},
],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(0));
}
#[test]
fn test_balance_evaluate_unequal_distribution() {
let constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance"),
ImpactType::Penalty,
vec(|s: &Solution| &s.shifts),
TrueFilter,
|shift: &Shift| shift.employee_id,
SoftScore::of(1000), false,
);
let solution = Solution {
shifts: vec![
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(1),
},
],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(-1000));
}
#[test]
fn test_balance_filters_unassigned() {
let constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance"),
ImpactType::Penalty,
vec(|s: &Solution| &s.shifts),
TrueFilter,
|shift: &Shift| shift.employee_id,
SoftScore::of(1000),
false,
);
let solution = Solution {
shifts: vec![
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(1),
},
Shift {
employee_id: Some(1),
},
Shift { employee_id: None },
],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(0));
}
#[test]
fn test_balance_incremental() {
let mut constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance"),
ImpactType::Penalty,
vec(|s: &Solution| &s.shifts),
TrueFilter,
|shift: &Shift| shift.employee_id,
SoftScore::of(1000),
false,
);
let solution = Solution {
shifts: vec![
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(1),
},
Shift {
employee_id: Some(1),
},
],
};
let initial = constraint.initialize(&solution);
assert_eq!(initial, SoftScore::of(0));
let delta = constraint.on_retract(&solution, 0, 0);
assert_eq!(delta, SoftScore::of(-500));
let delta = constraint.on_insert(&solution, 0, 0);
assert_eq!(delta, SoftScore::of(500));
}
#[test]
fn test_balance_empty_solution() {
let constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance"),
ImpactType::Penalty,
vec(|s: &Solution| &s.shifts),
TrueFilter,
|shift: &Shift| shift.employee_id,
SoftScore::of(1000),
false,
);
let solution = Solution { shifts: vec![] };
assert_eq!(constraint.evaluate(&solution), SoftScore::of(0));
}
#[test]
fn test_balance_single_employee() {
let constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance"),
ImpactType::Penalty,
vec(|s: &Solution| &s.shifts),
TrueFilter,
|shift: &Shift| shift.employee_id,
SoftScore::of(1000),
false,
);
let solution = Solution {
shifts: vec![
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(0));
}
#[test]
fn test_balance_reward() {
let constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance reward"),
ImpactType::Reward,
vec(|s: &Solution| &s.shifts),
TrueFilter,
|shift: &Shift| shift.employee_id,
SoftScore::of(1000),
false,
);
let solution = Solution {
shifts: vec![
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(0),
},
Shift {
employee_id: Some(1),
},
],
};
assert_eq!(constraint.evaluate(&solution), SoftScore::of(1000));
}