use std::collections::HashSet;
use crate::error::Error;
use crate::error::ErrorKind;
use crate::hash::check_seed_hash;
use crate::thetacommon::EntrySketch;
use crate::thetacommon::KeySketch;
use crate::thetacommon::SketchEntry;
use crate::thetacommon::constants::MAX_THETA;
use crate::thetacommon::sketch_state::CompactSketchState;
use crate::thetacommon::sketch_state::ThetaFamilySketchMetadata;
pub fn compute<A, B>(
seed_hash: u16,
a: A,
b: B,
ordered: bool,
) -> Result<CompactSketchState<A::Entry>, Error>
where
A: EntrySketch,
B: KeySketch,
{
let (a_seed_hash, a_theta, a_ordered) = match a.metadata() {
ThetaFamilySketchMetadata::Empty { .. } => {
return Ok(copy_to_compact_state(a, ordered));
}
ThetaFamilySketchMetadata::NonEmpty {
seed_hash,
theta,
ordered,
..
} => (seed_hash, theta, ordered),
};
check_seed_hash(seed_hash, a_seed_hash, "A", ErrorKind::InvalidArgument)?;
let (b_seed_hash, b_theta, b_ordered, b_num_retained) = match b.metadata() {
ThetaFamilySketchMetadata::Empty { .. } => {
return Ok(copy_to_compact_state(a, ordered));
}
ThetaFamilySketchMetadata::NonEmpty {
seed_hash,
theta,
ordered,
num_retained,
} => (seed_hash, theta, ordered, num_retained),
};
check_seed_hash(seed_hash, b_seed_hash, "B", ErrorKind::InvalidArgument)?;
let theta = a_theta.min(b_theta);
let entries: Vec<A::Entry> = if b_num_retained == 0 {
a.entries().filter(|entry| entry.hash() < theta).collect()
} else if a_ordered && b_ordered {
let mut b_hashes = b.hashes().peekable();
let mut entries = vec![];
for entry in a.entries() {
let hash = entry.hash();
if hash >= theta {
break;
}
while let Some(&b_hash) = b_hashes.peek() {
if b_hash < hash {
b_hashes.next();
} else {
break;
}
}
if b_hashes.peek() != Some(&hash) {
entries.push(entry);
}
}
entries
} else {
let mut b_keys: HashSet<u64> = HashSet::with_capacity(b_num_retained);
for hash in b.hashes() {
if hash < theta {
b_keys.insert(hash);
} else if b_ordered {
break;
}
}
let mut entries = vec![];
for entry in a.entries() {
let hash = entry.hash();
if hash < theta {
if !b_keys.contains(&hash) {
entries.push(entry);
}
} else if a_ordered {
break;
}
}
entries
};
if entries.is_empty() && theta == MAX_THETA {
return Ok(CompactSketchState::empty(seed_hash));
}
let mut entries = entries;
if ordered && !a_ordered && entries.len() > 1 {
entries.sort_unstable_by_key(SketchEntry::hash);
}
let out_ordered = ordered || a_ordered || (entries.len() == 1 && theta == MAX_THETA);
Ok(CompactSketchState::non_empty(
entries,
theta,
seed_hash,
out_ordered,
))
}
fn copy_to_compact_state<S>(sketch: S, ordered: bool) -> CompactSketchState<S::Entry>
where
S: EntrySketch,
{
let (seed_hash, theta, input_ordered) = match sketch.metadata() {
ThetaFamilySketchMetadata::Empty { seed_hash } => {
return CompactSketchState::empty(seed_hash);
}
ThetaFamilySketchMetadata::NonEmpty {
seed_hash,
theta,
ordered,
..
} => (seed_hash, theta, ordered),
};
let mut entries: Vec<S::Entry> = sketch.entries().collect();
if ordered && !input_ordered && entries.len() > 1 {
entries.sort_unstable_by_key(SketchEntry::hash);
}
let out_ordered = ordered || input_ordered || (entries.len() == 1 && theta == MAX_THETA);
CompactSketchState::non_empty(entries, theta, seed_hash, out_ordered)
}