use crate::integer::Integer;
use crate::integer::logic::checked_count_zeros::limbs_count_zeros_neg;
use crate::natural::InnerNatural::{Large, Small};
use crate::natural::Natural;
use crate::natural::logic::count_ones::limbs_count_ones;
use crate::natural::logic::hamming_distance::limbs_hamming_distance_same_length;
use crate::platform::Limb;
use core::cmp::Ordering::*;
use malachite_base::num::logic::traits::{
CheckedHammingDistance, CountOnes, CountZeros, HammingDistance,
};
use malachite_base::slices::slice_leading_zeros;
pub_test! {limbs_hamming_distance_limb_neg(xs: &[Limb], y: Limb) -> u64 {
let x_lo = xs[0].wrapping_neg();
limbs_count_zeros_neg(xs) - CountZeros::count_zeros(x_lo)
+ x_lo.hamming_distance(y.wrapping_neg())
}}
fn limbs_count_zeros(xs: &[Limb]) -> u64 {
xs.iter().map(|&limb| CountZeros::count_zeros(limb)).sum()
}
fn limbs_hamming_distance_neg_leading_limbs_helper(xs: &[Limb], ys: &[Limb], i: usize) -> u64 {
let xs_len = xs.len();
let ys_len = ys.len();
match xs_len.cmp(&ys_len) {
Equal => limbs_hamming_distance_same_length(&xs[i + 1..], &ys[i + 1..]),
Less => {
let (ys_lo, ys_hi) = ys.split_at(xs_len);
limbs_hamming_distance_same_length(&ys_lo[i + 1..], &xs[i + 1..])
+ limbs_count_ones(ys_hi)
}
Greater => {
let (xs_lo, xs_hi) = xs.split_at(ys_len);
limbs_hamming_distance_same_length(&xs_lo[i + 1..], &ys[i + 1..])
+ limbs_count_ones(xs_hi)
}
}
}
fn limbs_hamming_distance_neg_helper(xs: &[Limb], ys: &[Limb], xs_i: usize, ys_i: usize) -> u64 {
let mut distance = CountOnes::count_ones(xs[xs_i].wrapping_neg());
let xs_len = xs.len();
if xs_i == xs_len - 1 {
return distance + limbs_count_zeros_neg(&ys[xs_len..]);
}
if xs_len < ys_i {
return distance
+ limbs_count_zeros(&xs[xs_i + 1..])
+ limbs_count_zeros_neg(&ys[xs_len..]);
}
distance += limbs_count_zeros(&xs[xs_i + 1..ys_i]);
if xs_len == ys_i {
return distance + limbs_count_zeros_neg(&ys[xs_len..]);
}
distance += ys[ys_i].wrapping_neg().hamming_distance(!xs[ys_i]);
if xs_len == ys_i + 1 {
return distance + limbs_count_ones(&ys[xs_len..]);
}
distance + limbs_hamming_distance_neg_leading_limbs_helper(xs, ys, ys_i)
}
pub_test! {limbs_hamming_distance_neg(xs: &[Limb], ys: &[Limb]) -> u64 {
let xs_i = slice_leading_zeros(xs);
let ys_i = slice_leading_zeros(ys);
match xs_i.cmp(&ys_i) {
Equal => {
xs[xs_i]
.wrapping_neg()
.hamming_distance(ys[ys_i].wrapping_neg())
+ limbs_hamming_distance_neg_leading_limbs_helper(xs, ys, xs_i)
}
Less => limbs_hamming_distance_neg_helper(xs, ys, xs_i, ys_i),
Greater => limbs_hamming_distance_neg_helper(ys, xs, ys_i, xs_i),
}
}}
impl Natural {
fn hamming_distance_neg_limb(&self, other: Limb) -> u64 {
match self {
Self(Small(small)) => small.wrapping_neg().hamming_distance(other.wrapping_neg()),
Self(Large(limbs)) => limbs_hamming_distance_limb_neg(limbs, other),
}
}
fn hamming_distance_neg(&self, other: &Self) -> u64 {
match (self, other) {
(&Self(Small(x)), _) => other.hamming_distance_neg_limb(x),
(_, &Self(Small(y))) => self.hamming_distance_neg_limb(y),
(Self(Large(xs)), Self(Large(ys))) => limbs_hamming_distance_neg(xs, ys),
}
}
}
impl CheckedHammingDistance<&Integer> for &Integer {
fn checked_hamming_distance(self, other: &Integer) -> Option<u64> {
match (self.sign, other.sign) {
(true, true) => Some(self.abs.hamming_distance(&other.abs)),
(false, false) => Some(self.abs.hamming_distance_neg(&other.abs)),
_ => None,
}
}
}