///|
/// Calendar window metrics for service and scheduling reports.
pub struct CalendarWindow {
start : Int
end : Int
}
///|
/// Create a calendar window.
pub fn calendar_window(start : Int, end : Int) -> CalendarWindow {
{ start, end }
}
///|
pub fn CalendarWindow::duration(self : CalendarWindow) -> Int {
if self.end > self.start {
self.end - self.start
} else {
0
}
}
///|
pub fn CalendarWindow::valid(self : CalendarWindow) -> Bool {
self.start <= self.end
}
///|
pub fn CalendarWindow::contains(self : CalendarWindow, time : Int) -> Bool {
time >= self.start && time < self.end
}
///|
pub fn CalendarWindow::overlaps(
self : CalendarWindow,
other : CalendarWindow,
) -> Bool {
self.start < other.end && other.start < self.end
}
///|
pub fn CalendarWindow::touches(
self : CalendarWindow,
other : CalendarWindow,
) -> Bool {
self.end == other.start || other.end == self.start
}
///|
pub fn CalendarWindow::intersection(
self : CalendarWindow,
other : CalendarWindow,
) -> CalendarWindow? {
if !self.overlaps(other) {
None
} else {
Some(
calendar_window(
fm_max(self.start, other.start),
fm_min(self.end, other.end),
),
)
}
}
///|
pub fn CalendarWindow::shift(
self : CalendarWindow,
offset : Int,
) -> CalendarWindow {
calendar_window(self.start + offset, self.end + offset)
}
///|
pub fn CalendarWindow::expand(
self : CalendarWindow,
amount : Int,
) -> CalendarWindow {
calendar_window(self.start - amount, self.end + amount)
}
///|
pub fn CalendarWindow::describe(self : CalendarWindow) -> String {
"[\{self.start},\{self.end})"
}
///|
/// Merge overlapping and touching windows.
pub fn merge_calendar_windows(
windows : Array[CalendarWindow],
) -> Array[CalendarWindow] {
let ordered = windows.copy()
for left in 0.. 0 {
let previous = result[result.length() - 1]
if previous.end >= window.start {
result[result.length() - 1] = calendar_window(
previous.start,
fm_max(previous.end, window.end),
)
continue
}
}
result.push(window)
}
result
}
///|
/// Return total covered duration.
pub fn calendar_covered_duration(windows : Array[CalendarWindow]) -> Int {
let mut result = 0
for window in merge_calendar_windows(windows) {
result += window.duration()
}
result
}
///|
/// Return total gap duration between windows.
pub fn calendar_gap_duration(windows : Array[CalendarWindow]) -> Int {
let merged = merge_calendar_windows(windows)
let mut result = 0
for index in 1.. Int {
let ordered = merge_calendar_windows(windows)
let mut candidate = after
for window in ordered {
if candidate + duration <= window.start {
return candidate
}
if candidate < window.end {
candidate = window.end
}
}
candidate
}
///|
/// Return all windows that contain a time.
pub fn calendar_active(windows : Array[CalendarWindow], time : Int) -> Int {
let mut result = 0
for window in windows {
if window.contains(time) {
result += 1
}
}
result
}
///|
/// Return the maximum concurrency.
pub fn calendar_peak_concurrency(windows : Array[CalendarWindow]) -> Int {
let mut result = 0
for window in windows {
let current = calendar_active(windows, window.start)
if current > result {
result = current
}
}
result
}
///|
/// Return whether windows are non-overlapping.
pub fn calendar_non_overlapping(windows : Array[CalendarWindow]) -> Bool {
let mut original = 0
for window in windows {
original += window.duration()
}
original == calendar_covered_duration(windows)
}
///|
/// Return a window set fingerprint.
pub fn calendar_window_signature(windows : Array[CalendarWindow]) -> Int {
let mut result = 31
for window in windows {
result = result * 37 + window.start * 3 + window.end * 5
}
result
}
///|
/// Return windows within a horizon.
pub fn calendar_clip(
windows : Array[CalendarWindow],
lower : Int,
upper : Int,
) -> Array[CalendarWindow] {
let result : Array[CalendarWindow] = []
for window in windows {
let start = fm_max(window.start, lower)
let end = fm_min(window.end, upper)
if start < end {
result.push(calendar_window(start, end))
}
}
result
}
///|
/// Return a regular slot grid.
pub fn calendar_slots(
start : Int,
end : Int,
step : Int,
) -> Array[CalendarWindow] {
let result : Array[CalendarWindow] = []
if step <= 0 {
return result
}
let mut current = start
while current < end {
result.push(calendar_window(current, fm_min(current + step, end)))
current += step
}
result
}
///|
/// Return slots occupied by any window.
pub fn occupied_slots(
windows : Array[CalendarWindow],
slots : Array[CalendarWindow],
) -> Array[Int] {
let result : Array[Int] = []
for index, slot in slots {
for window in windows {
if slot.overlaps(window) {
result.push(index)
break
}
}
}
result
}
///|
/// Return free slots.
pub fn free_slots(
windows : Array[CalendarWindow],
slots : Array[CalendarWindow],
) -> Array[Int] {
let occupied = occupied_slots(windows, slots)
let result : Array[Int] = []
for index in 0.. Int {
let free = free_slots(windows, slots)
let mut result = 0
for index in free {
result += slots[index].duration()
}
result
}
///|
/// Return a calendar summary.
pub fn calendar_summary(windows : Array[CalendarWindow]) -> String {
"windows=\{windows.length()}, covered=\{calendar_covered_duration(windows)}, gaps=\{calendar_gap_duration(windows)}, peak=\{calendar_peak_concurrency(windows)}"
}