use crate::chart::Chart;
#[must_use]
pub fn merge(inputs: &[Vec<Chart>]) -> Vec<Chart> {
merge_reporting(inputs).0
}
#[must_use]
pub fn merge_reporting(inputs: &[Vec<Chart>]) -> (Vec<Chart>, Vec<String>) {
let mut result: Vec<Chart> = Vec::new();
let mut skipped: Vec<String> = Vec::new();
for batch in inputs {
for chart in batch {
if is_duplicate(chart, &result) {
skipped.push(chart.name.clone());
} else {
result.push(chart.clone());
}
}
}
(result, skipped)
}
fn is_duplicate(candidate: &Chart, existing: &[Chart]) -> bool {
existing.iter().any(|c| charts_match(candidate, c))
}
#[must_use]
pub fn is_candidate(a: &Chart, b: &Chart) -> bool {
a.year == b.year
&& a.month == b.month
&& a.day == b.day
&& time_diff_seconds(a, b) <= 7200
&& (a.latitude.degrees() - b.latitude.degrees()).abs() <= 0.1
&& (a.longitude.degrees() - b.longitude.degrees()).abs() <= 0.1
}
#[must_use]
pub fn find_candidate(chart: &Chart, existing: &[Chart]) -> Option<usize> {
existing.iter().position(|c| is_candidate(chart, c))
}
fn uf_find(parent: &mut [usize], mut i: usize) -> usize {
while parent[i] != i {
parent[i] = parent[parent[i]]; i = parent[i];
}
i
}
#[must_use]
pub fn group_candidates(charts: &[Chart]) -> Vec<Vec<usize>> {
let n = charts.len();
if n == 0 {
return Vec::new();
}
let mut parent: Vec<usize> = (0..n).collect();
for i in 0..n {
for j in (i + 1)..n {
if is_candidate(&charts[i], &charts[j]) {
let ri = uf_find(&mut parent, i);
let rj = uf_find(&mut parent, j);
if ri != rj {
let (lo, hi) = if ri < rj { (ri, rj) } else { (rj, ri) };
parent[hi] = lo;
}
}
}
}
let mut buckets: std::collections::BTreeMap<usize, Vec<usize>> =
std::collections::BTreeMap::new();
for i in 0..n {
let r = uf_find(&mut parent, i);
buckets.entry(r).or_default().push(i);
}
buckets.into_values().collect()
}
fn charts_match(a: &Chart, b: &Chart) -> bool {
a.name == b.name
&& a.year == b.year
&& a.month == b.month
&& a.day == b.day
&& time_diff_seconds(a, b) <= 7200
&& (a.latitude.degrees() - b.latitude.degrees()).abs() <= 0.1
&& (a.longitude.degrees() - b.longitude.degrees()).abs() <= 0.1
}
fn time_diff_seconds(a: &Chart, b: &Chart) -> u32 {
let a_sec = u32::from(a.hour) * 3600 + u32::from(a.minute) * 60 + u32::from(a.second);
let b_sec = u32::from(b.hour) * 3600 + u32::from(b.minute) * 60 + u32::from(b.second);
a_sec.abs_diff(b_sec)
}