File size: 2,931 Bytes
7596726
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use chrono::NaiveDate;
use std::cmp::Reverse;
use std::collections::{BTreeMap, BTreeSet};

use crate::domain::Employee;

use super::support::{can_mark_preference_date, mark_preference_date, PreferenceKind};

/// Ensures every employee ends up with the minimum amount of preference signal.
pub(super) fn ensure_preference_floor(
    employees: &mut [Employee],
    witness_dates_by_employee: &[BTreeSet<NaiveDate>],
    coverable_dates_by_employee: &[BTreeSet<NaiveDate>],
    date_pressure: &BTreeMap<NaiveDate, usize>,
) {
    for employee_index in 0..employees.len() {
        while employees[employee_index].undesired_dates.len() < 4 {
            let candidate = witness_dates_by_employee[employee_index]
                .iter()
                .chain(coverable_dates_by_employee[employee_index].iter())
                .copied()
                .filter(|&date| {
                    can_mark_preference_date(
                        &employees[employee_index],
                        date,
                        PreferenceKind::Undesired,
                    )
                })
                .max_by_key(|date| (*date_pressure.get(date).unwrap_or(&0), Reverse(*date)));
            let Some(date) = candidate else {
                break;
            };
            let _ = mark_preference_date(
                &mut employees[employee_index],
                date,
                PreferenceKind::Undesired,
            );
        }

        while employees[employee_index].desired_dates.len() < 4 {
            let candidate = coverable_dates_by_employee[employee_index]
                .iter()
                .copied()
                .filter(|&date| !witness_dates_by_employee[employee_index].contains(&date))
                .filter(|&date| {
                    can_mark_preference_date(
                        &employees[employee_index],
                        date,
                        PreferenceKind::Desired,
                    )
                })
                .max_by_key(|date| (*date_pressure.get(date).unwrap_or(&0), Reverse(*date)))
                .or_else(|| {
                    coverable_dates_by_employee[employee_index]
                        .iter()
                        .copied()
                        .filter(|&date| {
                            can_mark_preference_date(
                                &employees[employee_index],
                                date,
                                PreferenceKind::Desired,
                            )
                        })
                        .max_by_key(|date| (*date_pressure.get(date).unwrap_or(&0), Reverse(*date)))
                });
            let Some(date) = candidate else {
                break;
            };
            let _ = mark_preference_date(
                &mut employees[employee_index],
                date,
                PreferenceKind::Desired,
            );
        }
    }
}