1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct Interval {
16 pub start: i64,
18 pub end: i64,
20}
21
22impl Interval {
23 pub fn new(start: i64, end: i64) -> Self {
25 Interval { start, end }
26 }
27}
28
29#[derive(Clone, Debug, PartialEq, Eq)]
31pub enum ScheduleOutcome {
32 Feasible(Vec<usize>),
35 Infeasible(Vec<usize>),
38}
39
40#[inline]
42fn overlaps(a: &Interval, b: &Interval) -> bool {
43 a.start < b.end && b.start < a.end
44}
45
46pub fn schedule_or_overflow(tasks: &[Interval], machines: usize) -> ScheduleOutcome {
50 let mut events: Vec<(i64, i8, usize)> = Vec::with_capacity(tasks.len() * 2);
53 for (i, t) in tasks.iter().enumerate() {
54 if t.start < t.end {
55 events.push((t.start, 1, i));
56 events.push((t.end, -1, i));
57 }
58 }
59 events.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
60 let mut active: Vec<usize> = Vec::new();
61 for (_, delta, idx) in &events {
62 if *delta == 1 {
63 active.push(*idx);
64 if active.len() > machines {
65 return ScheduleOutcome::Infeasible(active.clone());
67 }
68 } else {
69 active.retain(|&x| x != *idx);
70 }
71 }
72 ScheduleOutcome::Feasible(greedy_assign(tasks, machines))
73}
74
75pub fn peak_concurrency(tasks: &[Interval]) -> usize {
78 let mut events: Vec<(i64, i8)> = Vec::with_capacity(tasks.len() * 2);
79 for t in tasks {
80 if t.start < t.end {
81 events.push((t.start, 1));
82 events.push((t.end, -1));
83 }
84 }
85 events.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
86 let (mut cur, mut peak) = (0i64, 0i64);
87 for (_, d) in events {
88 cur += d as i64;
89 peak = peak.max(cur);
90 }
91 peak as usize
92}
93
94fn greedy_assign(tasks: &[Interval], machines: usize) -> Vec<usize> {
97 let mut order: Vec<usize> = (0..tasks.len()).collect();
98 order.sort_by_key(|&i| tasks[i].start);
99 let mut free_at = vec![i64::MIN; machines.max(1)];
100 let mut assignment = vec![0usize; tasks.len()];
101 for &i in &order {
102 let m = (0..free_at.len())
103 .find(|&m| free_at[m] <= tasks[i].start)
104 .unwrap_or(0);
105 free_at[m] = tasks[i].end;
106 assignment[i] = m;
107 }
108 assignment
109}
110
111pub fn is_valid_schedule(tasks: &[Interval], machines: usize, assignment: &[usize]) -> bool {
113 if assignment.len() != tasks.len() || assignment.iter().any(|&m| m >= machines) {
114 return false;
115 }
116 for i in 0..tasks.len() {
117 for j in (i + 1)..tasks.len() {
118 if assignment[i] == assignment[j] && overlaps(&tasks[i], &tasks[j]) {
119 return false;
120 }
121 }
122 }
123 true
124}
125
126pub fn is_overflow_witness(tasks: &[Interval], machines: usize, witness: &[usize]) -> bool {
129 if witness.len() <= machines {
130 return false;
131 }
132 witness.iter().enumerate().all(|(a, &i)| {
133 i < tasks.len()
134 && witness
135 .iter()
136 .skip(a + 1)
137 .all(|&j| j < tasks.len() && overlaps(&tasks[i], &tasks[j]))
138 })
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 fn iv(s: i64, e: i64) -> Interval {
146 Interval::new(s, e)
147 }
148
149 fn peak_overlap(tasks: &[Interval]) -> usize {
151 tasks
152 .iter()
153 .filter(|t| t.start < t.end)
154 .map(|t| {
155 tasks
156 .iter()
157 .filter(|o| o.start <= t.start && t.start < o.end)
158 .count()
159 })
160 .max()
161 .unwrap_or(0)
162 }
163
164 #[test]
165 fn fitting_tasks_are_scheduled() {
166 let tasks = vec![iv(0, 2), iv(1, 3), iv(3, 4)];
168 match schedule_or_overflow(&tasks, 2) {
169 ScheduleOutcome::Feasible(a) => assert!(is_valid_schedule(&tasks, 2, &a), "{a:?}"),
170 o => panic!("expected Feasible, got {o:?}"),
171 }
172 }
173
174 #[test]
175 fn overload_yields_a_clique_witness() {
176 let tasks = vec![iv(0, 3), iv(1, 4), iv(2, 5)];
178 match schedule_or_overflow(&tasks, 2) {
179 ScheduleOutcome::Infeasible(w) => {
180 assert!(is_overflow_witness(&tasks, 2, &w), "witness invalid: {w:?}");
181 assert!(w.len() >= 3);
182 }
183 o => panic!("expected Infeasible, got {o:?}"),
184 }
185 }
186
187 #[test]
188 fn touching_intervals_do_not_overlap() {
189 let tasks = vec![iv(0, 1), iv(1, 2)];
191 match schedule_or_overflow(&tasks, 1) {
192 ScheduleOutcome::Feasible(a) => assert!(is_valid_schedule(&tasks, 1, &a)),
193 o => panic!("touching intervals fit on 1 machine, got {o:?}"),
194 }
195 }
196
197 #[test]
198 fn matches_peak_overlap_oracle_on_random_instances() {
199 let mut s: u64 = 0x243F6A8885A308D3;
200 let mut next = || {
201 s ^= s << 13;
202 s ^= s >> 7;
203 s ^= s << 17;
204 s
205 };
206 for _ in 0..500 {
207 let n = (next() % 10) as usize + 1;
208 let machines = (next() % 5) as usize + 1;
209 let tasks: Vec<Interval> = (0..n)
210 .map(|_| {
211 let a = (next() % 12) as i64;
212 let len = (next() % 6) as i64 + 1;
213 iv(a, a + len)
214 })
215 .collect();
216 let feasible_oracle = peak_overlap(&tasks) <= machines;
217 match schedule_or_overflow(&tasks, machines) {
218 ScheduleOutcome::Feasible(a) => {
219 assert!(feasible_oracle, "we said Feasible but peak > m: {tasks:?} m={machines}");
220 assert!(is_valid_schedule(&tasks, machines, &a), "invalid schedule {a:?}");
221 }
222 ScheduleOutcome::Infeasible(w) => {
223 assert!(!feasible_oracle, "we said Infeasible but peak ≤ m: {tasks:?} m={machines}");
224 assert!(is_overflow_witness(&tasks, machines, &w), "bogus witness {w:?}");
225 }
226 }
227 }
228 }
229
230 #[test]
231 fn empty_is_feasible() {
232 assert!(matches!(schedule_or_overflow(&[], 3), ScheduleOutcome::Feasible(_)));
233 }
234}