use crate::Error;
const LOWER_BITS: u64 = 0x5555555555555555;
const UPPER_BITS: u64 = 0xAAAAAAAAAAAAAAAA;
#[inline]
pub fn hdist_scalar(u: u64, v: u64, len: usize) -> Result<u32, Error> {
if len > 32 {
return Err(Error::InvalidLength(len));
}
if len == 0 || u == v {
return Ok(0);
}
let valid_bits = len * 2;
let mask = if valid_bits == 64 {
u64::MAX
} else {
(1u64 << valid_bits) - 1
};
let diff = (u ^ v) & mask;
if diff == 0 {
return Ok(0);
}
let lower_diffs = diff & LOWER_BITS & mask;
let upper_diffs = (diff & UPPER_BITS & mask) >> 1;
let combined_diffs = lower_diffs | upper_diffs;
Ok(combined_diffs.count_ones())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hdist_scalar_validation() {
assert!(hdist_scalar(0, 0, 33).is_err()); assert!(hdist_scalar(0, 0, 0).is_ok()); assert!(hdist_scalar(0, 0, 32).is_ok()); }
#[test]
fn test_hdist_scalar_identical() {
assert_eq!(hdist_scalar(0, 0, 1), Ok(0)); assert_eq!(hdist_scalar(0xFFFFFFFF, 0xFFFFFFFF, 16), Ok(0)); assert_eq!(
hdist_scalar(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 32),
Ok(0)
);
}
#[test]
fn test_hdist_scalar_small_sequences() {
let ac = 0b0001u64;
let ag = 0b0010u64;
let at = 0b0011u64;
assert_eq!(hdist_scalar(ac, ag, 2), Ok(1)); assert_eq!(hdist_scalar(ac, at, 2), Ok(1)); assert_eq!(hdist_scalar(ag, at, 2), Ok(1)); }
#[test]
fn test_hdist_scalar_full_sequences() {
let test_cases: Vec<(&[u8], &[u8], u32)> = vec![
(b"AAAA", b"AAAA", 0),
(b"AAAA", b"AAAT", 1),
(b"AAAA", b"AATT", 2),
(b"AAAA", b"ATTT", 3),
(b"AAAA", b"TTTT", 4),
(b"ACTGACTG", b"TGCATGCA", 8),
];
for (seq1, seq2, expected) in test_cases {
let u = crate::as_2bit(seq1).unwrap();
let v = crate::as_2bit(seq2).unwrap();
let len = seq1.len();
assert_eq!(
hdist_scalar(u, v, len),
Ok(expected),
"Failed for sequences {:?} and {:?}",
std::str::from_utf8(seq1).unwrap(),
std::str::from_utf8(seq2).unwrap()
);
}
}
}