use crate::code_unit::CodeUnit;
use crate::near_duplicate::similarity::similarity_score;
use crate::near_duplicate::similarity_pair::SimilarityPair;
pub struct PairScanner {
threshold: f64,
}
impl PairScanner {
#[must_use]
pub const fn new(threshold: f64) -> Self {
Self { threshold }
}
#[must_use]
pub fn scan(&self, candidates: &[&CodeUnit]) -> Vec<SimilarityPair> {
let ordered = Self::by_size(candidates);
let mut pairs = Vec::new();
for (offset, &left) in ordered.iter().enumerate() {
for &right in ordered.iter().skip(offset + 1) {
if !self.could_reach_threshold(
candidates[left].node_count,
candidates[right].node_count,
) {
break;
}
let score = similarity_score(&candidates[left].body, &candidates[right].body);
if score >= self.threshold {
pairs.push(SimilarityPair::new(left, right, score));
}
}
}
pairs
}
#[must_use]
pub fn could_reach_threshold(&self, smaller: usize, larger: usize) -> bool {
let total = smaller + larger;
if total == 0 {
return true;
}
let ceiling = 2.0 * smaller as f64 / total as f64;
ceiling >= self.threshold - f64::EPSILON
}
fn by_size(candidates: &[&CodeUnit]) -> Vec<usize> {
let mut ordered: Vec<usize> = (0..candidates.len()).collect();
ordered.sort_by_key(|&index| candidates[index].node_count);
ordered
}
}