Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_datime/src/time/interval.rs

5.1 KiB, 38 runs

created by r1870400018:6389, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
2//! Anthropic Claude
3
4use crate::{
5 core::{Interval, Time},
6 time::{CalClock, CalClockDuration},
7};
8use oxedyne_fe2o3_core::prelude::*;
9
10#[derive(Clone, Debug, PartialEq)]
11pub struct CalClockInterval {
12 start: CalClock,
13 finish: CalClock,
14}
15
16impl CalClockInterval {
17 pub fn new(start: CalClock, finish: CalClock) -> Outcome<Self> {
18 // Validate that start is before or equal to finish
19 if start <= finish {
20 Ok(Self { start, finish })
21 } else {
22 Err(err!("Start time {} must be before or equal to finish time {}", start, finish; Invalid, Input))
23 }
24 }
25
26 pub fn start(&self) -> &CalClock {
27 &self.start
28 }
29
30 pub fn finish(&self) -> &CalClock {
31 &self.finish
32 }
33
34 pub fn duration(&self) -> Outcome<CalClockDuration> {
35 self.start.duration_until(&self.finish)
36 }
37
38 pub fn contains_time(&self, time: &CalClock) -> bool {
39 time >= &self.start && time <= &self.finish
40 }
41
42 pub fn overlaps_with(&self, other: &Self) -> bool {
43 // Intervals overlap if one starts before the other ends
44 self.start <= other.finish && other.start <= self.finish
45 }
46
47 pub fn intersection(&self, other: &Self) -> Option<Self> {
48 if !self.overlaps_with(other) {
49 return None;
50 }
51
52 let intersection_start = if self.start >= other.start {
53 self.start.clone()
54 } else {
55 other.start.clone()
56 };
57
58 let intersection_finish = if self.finish <= other.finish {
59 self.finish.clone()
60 } else {
61 other.finish.clone()
62 };
63
64 Self::new(intersection_start, intersection_finish).ok()
65 }
66
67 pub fn union(&self, other: &Self) -> Option<Self> {
68 if !self.overlaps_with(other) {
69 return None;
70 }
71
72 let union_start = if self.start <= other.start {
73 self.start.clone()
74 } else {
75 other.start.clone()
76 };
77
78 let union_finish = if self.finish >= other.finish {
79 self.finish.clone()
80 } else {
81 other.finish.clone()
82 };
83
84 Self::new(union_start, union_finish).ok()
85 }
86
87 pub fn is_adjacent_to(&self, other: &Self) -> bool {
88 self.finish == other.start || other.finish == self.start
89 }
90
91 pub fn is_before(&self, other: &Self) -> bool {
92 self.finish < other.start
93 }
94
95 pub fn is_after(&self, other: &Self) -> bool {
96 self.start > other.finish
97 }
98
99 pub fn expand(&self, duration: &CalClockDuration) -> Outcome<Self> {
100 let new_start = res!(self.start.subtract_duration(duration));
101 let new_finish = res!(self.finish.add_duration(duration));
102 Self::new(new_start, new_finish)
103 }
104
105 pub fn contract(&self, duration: &CalClockDuration) -> Outcome<Self> {
106 let new_start = res!(self.start.add_duration(duration));
107 let new_finish = res!(self.finish.subtract_duration(duration));
108
109 if new_start <= new_finish {
110 Self::new(new_start, new_finish)
111 } else {
112 Err(err!("Cannot contract interval by {:?} - would result in negative duration", duration; Invalid, Input))
113 }
114 }
115
116 pub fn shift(&self, duration: &CalClockDuration) -> Outcome<Self> {
117 let new_start = res!(self.start.add_duration(duration));
118 let new_finish = res!(self.finish.add_duration(duration));
119 Self::new(new_start, new_finish)
120 }
121
122 pub fn split_at(&self, split_time: &CalClock) -> Outcome<(Self, Self)> {
123 if !self.contains_time(split_time) {
124 return Err(err!("Split time {} is not within interval", split_time; Invalid, Input));
125 }
126
127 let first_interval = res!(Self::new(self.start.clone(), split_time.clone()));
128 let second_interval = res!(Self::new(split_time.clone(), self.finish.clone()));
129
130 Ok((first_interval, second_interval))
131 }
132
133 pub fn midpoint(&self) -> Outcome<CalClock> {
134 let duration = res!(self.duration());
135 let half_duration = res!(duration.divide_by(2));
136 self.start.add_duration(&half_duration)
137 }
138
139 pub fn contains_interval(&self, other: &Self) -> bool {
140 self.start <= other.start && other.finish <= self.finish
141 }
142
143 pub fn merge_overlapping(intervals: Vec<Self>) -> Vec<Self> {
144 if intervals.is_empty() {
145 return Vec::new();
146 }
147
148 let mut sorted_intervals = intervals;
149 sorted_intervals.sort_by(|a, b| a.start.cmp(&b.start));
150
151 let mut merged = Vec::new();
152 let mut current = sorted_intervals[0].clone();
153
154 for interval in sorted_intervals.into_iter().skip(1) {
155 if current.overlaps_with(&interval) || current.is_adjacent_to(&interval) {
156 // Merge with current
157 if let Some(union) = current.union(&interval) {
158 current = union;
159 } else {
160 // Handle adjacent case
161 let new_finish = if current.finish >= interval.finish {
162 current.finish.clone()
163 } else {
164 interval.finish.clone()
165 };
166 current = Self::new(current.start.clone(), new_finish).unwrap_or(current);
167 }
168 } else {
169 // No overlap, add current to result and start new
170 merged.push(current);
171 current = interval;
172 }
173 }
174
175 merged.push(current);
176 merged
177 }
178}
179
180impl Interval<CalClockDuration> for CalClockInterval {
181 fn get_duration(&self) -> Outcome<CalClockDuration> {
182 self.duration()
183 }
184
185 fn contains<T: Time>(&self, _time: &T) -> bool {
186 // This would need a way to convert T to CalClock for proper comparison
187 // For now, return false as we can't convert arbitrary Time types
188 false
189 }
190
191 fn overlaps(&self, other: &Self) -> bool {
192 self.overlaps_with(other)
193 }
194}