use num_bigint::BigUint;
use std::collections::{BTreeMap, BTreeSet};
pub fn hamming_bits(a: &[bool], b: &[bool]) -> BigUint {
let count = a.iter().zip(b).filter(|(x, y)| x != y).count();
BigUint::from(count)
}
pub fn symmetric_difference(a: &[usize], b: &[usize]) -> BigUint {
let sa: BTreeSet<usize> = a.iter().copied().collect();
let sb: BTreeSet<usize> = b.iter().copied().collect();
BigUint::from(sa.symmetric_difference(&sb).count())
}
pub fn kendall_tau(a: &[usize], b: &[usize]) -> BigUint {
let positions_b: BTreeMap<usize, usize> = b
.iter()
.enumerate()
.map(|(pos, &item)| (item, pos))
.collect();
let mut count = 0usize;
for i in 0..a.len() {
for j in (i + 1)..a.len() {
let Some(&pos_i) = positions_b.get(&a[i]) else {
continue;
};
let Some(&pos_j) = positions_b.get(&a[j]) else {
continue;
};
if pos_i > pos_j {
count += 1;
}
}
}
BigUint::from(count)
}
pub fn l1_u64(a: &[u64], b: &[u64]) -> BigUint {
let mut acc = BigUint::from(0u32);
for (x, y) in a.iter().zip(b) {
acc += BigUint::from(x.abs_diff(*y));
}
acc
}
pub fn l1_i64(a: &[i64], b: &[i64]) -> BigUint {
let mut acc = BigUint::from(0u32);
for (x, y) in a.iter().zip(b) {
let d = (*x as i128 - *y as i128).unsigned_abs();
acc += BigUint::from(d);
}
acc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hamming_counts_differences() {
assert_eq!(
hamming_bits(&[true, false, true], &[true, true, false]),
BigUint::from(2u32)
);
}
#[test]
fn symmetric_difference_counts() {
assert_eq!(
symmetric_difference(&[0, 1, 2], &[1, 2, 3]),
BigUint::from(2u32)
);
}
#[test]
fn kendall_tau_basic() {
assert_eq!(kendall_tau(&[0, 1, 2], &[2, 1, 0]), BigUint::from(3u32));
assert_eq!(kendall_tau(&[0, 1, 2], &[0, 1, 2]), BigUint::from(0u32));
assert_eq!(kendall_tau(&[0, 2, 1], &[1, 2, 0]), BigUint::from(3u32));
}
#[test]
fn l1_distances() {
assert_eq!(l1_u64(&[1, 5, 3], &[4, 1, 3]), BigUint::from(7u32));
assert_eq!(l1_i64(&[-2, 3], &[1, -1]), BigUint::from(7u32));
}
}