use super::refs_tables::{InboundRow, OutboundRow};
fn inbound_collide(a: &InboundRow, b: &InboundRow) -> bool {
a.file == b.file
&& a.line == b.line
&& a.heuristic == b.heuristic
&& a.tier == b.tier
&& a.source == b.source
}
fn outbound_collide(a: &OutboundRow, b: &OutboundRow) -> bool {
a.file == b.file
&& a.line == b.line
&& a.to_file == b.to_file
&& a.to == b.to
&& a.heuristic == b.heuristic
&& a.tier == b.tier
&& a.source == b.source
}
pub(super) fn tag_inbound_rows(rows: &mut [InboundRow]) {
let assigned = assign_occurrence_indices(rows, inbound_collide);
for (row, idx) in rows.iter_mut().zip(assigned) {
row.occurrence_index = idx;
}
}
pub(super) fn tag_outbound_rows(rows: &mut [OutboundRow]) {
let assigned = assign_occurrence_indices(rows, outbound_collide);
for (row, idx) in rows.iter_mut().zip(assigned) {
row.occurrence_index = idx;
}
}
pub(super) fn assign_occurrence_indices<T>(
rows: &[T],
eq: impl Fn(&T, &T) -> bool,
) -> Vec<Option<usize>> {
let n = rows.len();
let mut assigned: Vec<Option<usize>> = vec![None; n];
for i in 0..n {
if assigned[i].is_some() {
continue;
}
let group: Vec<usize> = (i..n).filter(|&j| eq(&rows[i], &rows[j])).collect();
if group.len() < 2 {
continue;
}
for (position, &idx) in group.iter().enumerate() {
assigned[idx] = Some(position);
}
}
assigned
}
#[cfg(test)]
mod tests {
use super::assign_occurrence_indices;
#[derive(Debug, Clone, PartialEq)]
struct Row {
line: usize,
target: &'static str,
}
fn same(a: &Row, b: &Row) -> bool {
a.line == b.line && a.target == b.target
}
#[test]
fn a_singleton_row_gets_no_occurrence_index() {
let rows = vec![
Row {
line: 1,
target: "A",
},
Row {
line: 2,
target: "B",
},
];
assert_eq!(assign_occurrence_indices(&rows, same), vec![None, None]);
}
#[test]
fn two_colliding_rows_get_zero_and_one_in_emission_order() {
let rows = vec![
Row {
line: 5,
target: "A",
},
Row {
line: 5,
target: "A",
},
];
assert_eq!(
assign_occurrence_indices(&rows, same),
vec![Some(0), Some(1)]
);
}
#[test]
fn a_run_boundary_does_not_bleed_into_an_unrelated_group() {
let rows = vec![
Row {
line: 5,
target: "A",
},
Row {
line: 5,
target: "A",
},
Row {
line: 9,
target: "B",
},
Row {
line: 9,
target: "B",
},
Row {
line: 9,
target: "B",
},
];
assert_eq!(
assign_occurrence_indices(&rows, same),
vec![Some(0), Some(1), Some(0), Some(1), Some(2)]
);
}
#[test]
fn a_non_adjacent_collision_is_still_grouped_by_equality_not_adjacency() {
let rows = vec![
Row {
line: 5,
target: "A",
},
Row {
line: 9,
target: "B",
},
Row {
line: 5,
target: "A",
},
];
assert_eq!(
assign_occurrence_indices(&rows, same),
vec![Some(0), None, Some(1)]
);
}
#[test]
fn three_way_collision_assigns_a_position_to_every_member() {
let rows = vec![
Row {
line: 5,
target: "A",
},
Row {
line: 5,
target: "A",
},
Row {
line: 5,
target: "A",
},
];
assert_eq!(
assign_occurrence_indices(&rows, same),
vec![Some(0), Some(1), Some(2)]
);
}
#[test]
fn an_empty_table_assigns_nothing() {
let rows: Vec<Row> = Vec::new();
assert_eq!(
assign_occurrence_indices(&rows, same),
Vec::<Option<usize>>::new()
);
}
}