///|
/// Stable urgency ordering used by the constructive solver.
pub fn sort_visits_for_scheduling(values : Array[Visit]) -> Array[Visit] {
let sorted = copy_array(values)
for index = 1; index < sorted.length(); index = index + 1 {
let current = sorted[index]
let mut position = index
while position > 0 {
let previous = sorted[position - 1]
if !visit_before(current, previous) {
break
}
sorted[position] = previous
position = position - 1
}
sorted[position] = current
}
sorted
}
///|
/// Compare visits by required status, priority, flexibility, duration and id.
pub fn visit_before(left : Visit, right : Visit) -> Bool {
if left.required != right.required {
return left.required
}
let left_priority = priority_value(left.priority)
let right_priority = priority_value(right.priority)
if left_priority != right_priority {
return left_priority > right_priority
}
let left_slack = left.window.duration() - left.duration_minutes
let right_slack = right.window.duration() - right.duration_minutes
if left_slack != right_slack {
return left_slack < right_slack
}
if left.duration_minutes != right.duration_minutes {
return left.duration_minutes > right.duration_minutes
}
if left.window.start_minute != right.window.start_minute {
return left.window.start_minute < right.window.start_minute
}
left.id < right.id
}
///|
/// Stable assignment comparison for serialized schedules.
pub fn assignment_before(left : Assignment, right : Assignment) -> Bool {
if left.start_minute != right.start_minute {
return left.start_minute < right.start_minute
}
if left.worker_id != right.worker_id {
return left.worker_id < right.worker_id
}
left.visit_id < right.visit_id
}
///|
/// Sort schedule assignments for reproducible output.
pub fn sort_schedule_assignments(
values : Array[Assignment],
) -> Array[Assignment] {
let sorted = copy_array(values)
for index = 1; index < sorted.length(); index = index + 1 {
let current = sorted[index]
let mut position = index
while position > 0 && assignment_before(current, sorted[position - 1]) {
sorted[position] = sorted[position - 1]
position = position - 1
}
sorted[position] = current
}
sorted
}
///|
/// Sort strings lexicographically without mutating the caller's array.
pub fn sort_strings(values : Array[String]) -> Array[String] {
let sorted = copy_array(values)
for index = 1; index < sorted.length(); index = index + 1 {
let current = sorted[index]
let mut position = index
while position > 0 && current < sorted[position - 1] {
sorted[position] = sorted[position - 1]
position = position - 1
}
sorted[position] = current
}
sorted
}