1use crate::NQuin;
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct TemporalInterval {
10 pub id: u64,
11 pub start: i64, pub end: i64, pub duration: i64,
14}
15
16impl TemporalInterval {
17 pub fn new(id: u64, start: i64, end: i64) -> Self {
19 assert!(start <= end, "Interval start must be <= end");
20 let duration = end - start;
21 Self {
22 id,
23 start,
24 end,
25 duration,
26 }
27 }
28
29 pub fn contains(&self, point: i64) -> bool {
31 point >= self.start && point <= self.end
32 }
33
34 pub fn overlaps(&self, other: &TemporalInterval) -> bool {
36 self.start <= other.end && other.start <= self.end
37 }
38
39 pub fn intersection(&self, other: &TemporalInterval) -> Option<TemporalInterval> {
41 if !self.overlaps(other) {
42 return None;
43 }
44
45 let start = self.start.max(other.start);
46 let end = self.end.min(other.end);
47 Some(TemporalInterval::new(0, start, end))
48 }
49
50 pub fn union(&self, other: &TemporalInterval) -> TemporalInterval {
52 let start = self.start.min(other.start);
53 let end = self.end.max(other.end);
54 TemporalInterval::new(0, start, end)
55 }
56
57 pub fn gap(&self, other: &TemporalInterval) -> Option<i64> {
59 if self.overlaps(other) {
60 return None;
61 }
62
63 if self.end < other.start {
64 Some(other.start - self.end)
65 } else {
66 Some(self.start - other.end)
67 }
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum AllenRelation {
74 Before, After, Meets, MetBy, Overlaps, OverlappedBy, Starts, StartedBy, During, Contains, Ends, EndedBy, Equal, }
88
89pub struct IntervalCSP {
91 pub intervals: HashMap<u64, TemporalInterval>,
92 pub constraints: HashMap<(u64, u64), Vec<AllenRelation>>,
93 pub solution: Option<HashMap<u64, TemporalInterval>>,
94}
95
96impl IntervalCSP {
97 pub fn new() -> Self {
99 Self {
100 intervals: HashMap::new(),
101 constraints: HashMap::new(),
102 solution: None,
103 }
104 }
105
106 pub fn add_interval(&mut self, interval: TemporalInterval) {
108 self.intervals.insert(interval.id, interval);
109 }
110
111 pub fn add_constraint(&mut self, id1: u64, id2: u64, relation: AllenRelation) {
113 self.constraints
114 .entry((id1, id2))
115 .or_insert_with(Vec::new)
116 .push(relation);
117 }
118
119 pub fn satisfies_relation(
121 &self,
122 interval1: &TemporalInterval,
123 interval2: &TemporalInterval,
124 relation: &AllenRelation,
125 ) -> bool {
126 match relation {
127 AllenRelation::Before => interval1.end < interval2.start,
128 AllenRelation::After => interval1.start > interval2.end,
129 AllenRelation::Meets => interval1.end == interval2.start,
130 AllenRelation::MetBy => interval1.start == interval2.end,
131 AllenRelation::Overlaps => {
132 interval1.start < interval2.start
133 && interval2.start < interval1.end
134 && interval1.end < interval2.end
135 }
136 AllenRelation::OverlappedBy => {
137 interval2.start < interval1.start
138 && interval1.start < interval2.end
139 && interval2.end < interval1.end
140 }
141 AllenRelation::Starts => {
142 interval1.start == interval2.start && interval1.end < interval2.end
143 }
144 AllenRelation::StartedBy => {
145 interval2.start == interval1.start && interval2.end < interval1.end
146 }
147 AllenRelation::During => {
148 interval1.start > interval2.start && interval1.end < interval2.end
149 }
150 AllenRelation::Contains => {
151 interval2.start > interval1.start && interval2.end < interval1.end
152 }
153 AllenRelation::Ends => {
154 interval1.start > interval2.start && interval1.end == interval2.end
155 }
156 AllenRelation::EndedBy => {
157 interval2.start > interval1.start && interval2.end == interval1.end
158 }
159 AllenRelation::Equal => {
160 interval1.start == interval2.start && interval1.end == interval2.end
161 }
162 }
163 }
164
165 pub fn solve(&mut self) -> bool {
167 let mut solution = HashMap::new();
168
169 for (id, interval) in &self.intervals {
171 solution.insert(*id, interval.clone());
172 }
173
174 for ((id1, id2), relations) in &self.constraints {
176 if let (Some(interval1), Some(interval2)) = (solution.get(id1), solution.get(id2)) {
177 let satisfied = relations
178 .iter()
179 .any(|relation| self.satisfies_relation(interval1, interval2, relation));
180
181 if !satisfied {
182 return false;
183 }
184 }
185 }
186
187 self.solution = Some(solution);
188 true
189 }
190
191 pub fn get_solution(&self) -> Option<&HashMap<u64, TemporalInterval>> {
193 self.solution.as_ref()
194 }
195}
196
197pub struct TemporalPlanner {
199 pub tasks: HashMap<u64, Task>,
200 pub schedule: Option<Vec<ScheduledTask>>,
201}
202
203#[derive(Debug, Clone)]
204pub struct Task {
205 pub id: u64,
206 pub name: String,
207 pub duration: i64,
208 pub dependencies: Vec<u64>,
209 pub constraints: Vec<TaskConstraint>,
210}
211
212#[derive(Debug, Clone)]
213pub enum TaskConstraint {
214 MustStartAfter(i64),
215 MustEndBefore(i64),
216 MustStartBefore(i64),
217 MustEndAfter(i64),
218 FixedStart(i64),
219 FixedEnd(i64),
220}
221
222#[derive(Debug, Clone)]
223pub struct ScheduledTask {
224 pub task: Task,
225 pub interval: TemporalInterval,
226}
227
228impl TemporalPlanner {
229 pub fn new() -> Self {
231 Self {
232 tasks: HashMap::new(),
233 schedule: None,
234 }
235 }
236
237 pub fn add_task(&mut self, task: Task) {
239 self.tasks.insert(task.id, task);
240 }
241
242 pub fn generate_schedule(&mut self) -> Result<(), String> {
244 let mut scheduled: Vec<ScheduledTask> = Vec::new();
245 let mut task_queue: Vec<&Task> = self.tasks.values().collect();
246
247 task_queue.sort_by(|a, b| a.dependencies.len().cmp(&b.dependencies.len()));
249
250 let mut current_time = 0i64;
251
252 for task in task_queue {
253 let mut dependencies_satisfied = true;
255 let mut earliest_start = current_time;
256
257 for &dep_id in &task.dependencies {
258 if let Some(scheduled_dep) = scheduled.iter().find(|st| st.task.id == dep_id) {
259 earliest_start = earliest_start.max(scheduled_dep.interval.end);
260 } else {
261 dependencies_satisfied = false;
262 break;
263 }
264 }
265
266 if !dependencies_satisfied {
267 return Err(format!("Task {} has unsatisfied dependencies", task.id));
268 }
269
270 let mut start_time = earliest_start;
272 let end_time = start_time + task.duration;
273
274 for constraint in &task.constraints {
275 match constraint {
276 TaskConstraint::MustStartAfter(time) => start_time = start_time.max(*time),
277 TaskConstraint::MustEndBefore(time) => {
278 if end_time > *time {
279 return Err(format!("Task {} must end before {}", task.id, time));
280 }
281 }
282 TaskConstraint::MustStartBefore(time) => {
283 if start_time > *time {
284 return Err(format!("Task {} must start before {}", task.id, time));
285 }
286 }
287 TaskConstraint::MustEndAfter(time) => {
288 if end_time < *time {
289 start_time = *time - task.duration;
290 }
291 }
292 TaskConstraint::FixedStart(time) => start_time = *time,
293 TaskConstraint::FixedEnd(time) => start_time = *time - task.duration,
294 }
295 }
296
297 let interval = TemporalInterval::new(task.id, start_time, start_time + task.duration);
299 scheduled.push(ScheduledTask {
300 task: task.clone(),
301 interval,
302 });
303
304 current_time = start_time + task.duration;
305 }
306
307 self.schedule = Some(scheduled);
308 Ok(())
309 }
310
311 pub fn get_schedule(&self) -> Option<&Vec<ScheduledTask>> {
313 self.schedule.as_ref()
314 }
315
316 pub fn validate_schedule(&self) -> bool {
318 if let Some(schedule) = &self.schedule {
319 for (i, task1) in schedule.iter().enumerate() {
321 for task2 in schedule.iter().skip(i + 1) {
322 if task1.interval.overlaps(&task2.interval) {
324 if !task1.task.dependencies.contains(&task2.task.id)
326 && !task2.task.dependencies.contains(&task1.task.id)
327 {
328 return false;
329 }
330 }
331 }
332 }
333 true
334 } else {
335 false
336 }
337 }
338}
339
340pub struct TemporalAlgebra;
342
343impl TemporalAlgebra {
344 pub fn transitive_closure(
346 intervals: &HashMap<u64, TemporalInterval>,
347 ) -> HashMap<(u64, u64), AllenRelation> {
348 let mut relations = HashMap::new();
349
350 for (id1, interval1) in intervals {
351 for (id2, interval2) in intervals {
352 if id1 != id2 {
353 let relation = Self::determine_relation(interval1, interval2);
354 relations.insert((*id1, *id2), relation);
355 }
356 }
357 }
358
359 relations
360 }
361
362 pub fn determine_relation(
364 interval1: &TemporalInterval,
365 interval2: &TemporalInterval,
366 ) -> AllenRelation {
367 if interval1.end < interval2.start {
368 AllenRelation::Before
369 } else if interval1.start > interval2.end {
370 AllenRelation::After
371 } else if interval1.end == interval2.start {
372 AllenRelation::Meets
373 } else if interval1.start == interval2.end {
374 AllenRelation::MetBy
375 } else if interval1.start < interval2.start
376 && interval2.start < interval1.end
377 && interval1.end < interval2.end
378 {
379 AllenRelation::Overlaps
380 } else if interval2.start < interval1.start
381 && interval1.start < interval2.end
382 && interval2.end < interval1.end
383 {
384 AllenRelation::OverlappedBy
385 } else if interval1.start == interval2.start && interval1.end < interval2.end {
386 AllenRelation::Starts
387 } else if interval2.start == interval1.start && interval2.end < interval1.end {
388 AllenRelation::StartedBy
389 } else if interval1.start > interval2.start && interval1.end < interval2.end {
390 AllenRelation::During
391 } else if interval2.start > interval1.start && interval2.end < interval1.end {
392 AllenRelation::Contains
393 } else if interval1.start > interval2.start && interval1.end == interval2.end {
394 AllenRelation::Ends
395 } else if interval2.start > interval1.start && interval2.end == interval1.end {
396 AllenRelation::EndedBy
397 } else {
398 AllenRelation::Equal
399 }
400 }
401
402 pub fn compose_relations(rel1: AllenRelation, rel2: AllenRelation) -> Vec<AllenRelation> {
404 match (rel1, rel2) {
406 (AllenRelation::Before, AllenRelation::Before) => vec![AllenRelation::Before],
407 (AllenRelation::Before, AllenRelation::Meets) => vec![AllenRelation::Before],
408 (AllenRelation::Meets, AllenRelation::Before) => vec![AllenRelation::Before],
409 (AllenRelation::During, AllenRelation::During) => vec![AllenRelation::During],
410 (AllenRelation::Starts, AllenRelation::Starts) => vec![AllenRelation::Starts],
411 _ => vec![rel1], }
413 }
414}
415
416pub fn interval_to_quin(interval: &TemporalInterval, context: u64) -> NQuin {
418 let mut quin = NQuin {
419 subject: interval.id,
420 predicate: crate::q_hash("has_temporal_interval"),
421 object: ((interval.start as u64) << 32) | (interval.duration as u64 & 0xFFFFFFFF),
422 context,
423 metadata: interval.end as u64,
424 parity: 0,
425 };
426
427 quin.parity = quin.subject ^ quin.predicate ^ quin.object ^ quin.context;
428 quin
429}
430
431pub fn schedule_to_quins(schedule: &[ScheduledTask], context: u64) -> Vec<NQuin> {
433 let mut quins = Vec::new();
434
435 for scheduled_task in schedule {
436 let interval_quin = interval_to_quin(&scheduled_task.interval, context);
437 quins.push(interval_quin);
438
439 let task_quin = NQuin {
441 subject: scheduled_task.task.id,
442 predicate: crate::q_hash("has_task_metadata"),
443 object: scheduled_task.task.duration as u64,
444 context,
445 metadata: crate::q_hash(&scheduled_task.task.name),
446 parity: 0,
447 };
448 quins.push(task_quin);
449 }
450
451 quins
452}
453
454#[cfg(test)]
455mod tests {
456 use super::*;
457
458 #[test]
459 fn test_temporal_interval_creation() {
460 let interval = TemporalInterval::new(1, 100, 200);
461 assert_eq!(interval.start, 100);
462 assert_eq!(interval.end, 200);
463 assert_eq!(interval.duration, 100);
464 }
465
466 #[test]
467 fn test_interval_operations() {
468 let interval1 = TemporalInterval::new(1, 100, 200);
469 let interval2 = TemporalInterval::new(2, 150, 250);
470
471 assert!(interval1.overlaps(&interval2));
472
473 let intersection = interval1.intersection(&interval2).unwrap();
474 assert_eq!(intersection.start, 150);
475 assert_eq!(intersection.end, 200);
476
477 let union = interval1.union(&interval2);
478 assert_eq!(union.start, 100);
479 assert_eq!(union.end, 250);
480 }
481
482 #[test]
483 fn test_allen_relations() {
484 let interval1 = TemporalInterval::new(1, 100, 200);
485 let interval2 = TemporalInterval::new(2, 50, 150);
486 let interval3 = TemporalInterval::new(3, 200, 300);
487
488 assert_eq!(
489 TemporalAlgebra::determine_relation(&interval1, &interval2),
490 AllenRelation::OverlappedBy
491 );
492 assert_eq!(
493 TemporalAlgebra::determine_relation(&interval1, &interval3),
494 AllenRelation::Meets
495 );
496 }
497
498 #[test]
499 fn test_interval_csp() {
500 let mut csp = IntervalCSP::new();
501
502 let interval1 = TemporalInterval::new(1, 100, 200);
503 let interval2 = TemporalInterval::new(2, 200, 300);
504
505 csp.add_interval(interval1);
506 csp.add_interval(interval2);
507 csp.add_constraint(1, 2, AllenRelation::Meets);
508
509 assert!(csp.solve());
510 assert!(csp.get_solution().is_some());
511 }
512
513 #[test]
514 fn test_temporal_planner() {
515 let mut planner = TemporalPlanner::new();
516
517 let task1 = Task {
518 id: 1,
519 name: "Task 1".to_string(),
520 duration: 100,
521 dependencies: vec![],
522 constraints: vec![TaskConstraint::FixedStart(100)],
523 };
524
525 let task2 = Task {
526 id: 2,
527 name: "Task 2".to_string(),
528 duration: 50,
529 dependencies: vec![1],
530 constraints: vec![],
531 };
532
533 planner.add_task(task1);
534 planner.add_task(task2);
535
536 assert!(planner.generate_schedule().is_ok());
537 assert!(planner.validate_schedule());
538 }
539
540 #[test]
541 fn test_interval_to_quin() {
542 let interval = TemporalInterval::new(42, 1000, 1500);
543 let quin = interval_to_quin(&interval, 123);
544
545 assert_eq!(quin.subject, 42);
546 assert_eq!(quin.context, 123);
547 assert_eq!(quin.metadata, 1500); }
549}