Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion theta/include/theta_set_difference_base_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ template<typename FwdSketch, typename Sketch>
CS theta_set_difference_base<EN, EK, CS, A>::compute(FwdSketch&& a, const Sketch& b, bool ordered) const {
if (a.is_empty() || (a.get_num_retained() > 0 && b.is_empty())) return CS(a, ordered);
if (a.get_seed_hash() != seed_hash_) throw std::invalid_argument("A seed hash mismatch");
if (b.get_seed_hash() != seed_hash_) throw std::invalid_argument("B seed hash mismatch");
// an empty sketch has no hashes, so its seed hash is meaningless and must be ignored,
// consistent with deserialization, theta_union::update() and theta_intersection::update()
if (!b.is_empty() && b.get_seed_hash() != seed_hash_) throw std::invalid_argument("B seed hash mismatch");

const uint64_t theta = std::min(a.get_theta64(), b.get_theta64());
std::vector<EN, A> entries(allocator_);
Expand Down
22 changes: 22 additions & 0 deletions theta/test/theta_a_not_b_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,28 @@ TEST_CASE("theta a-not-b: seed mismatch", "[theta_a_not_b]") {
REQUIRE_THROWS_AS(a_not_b.compute(sketch, sketch), std::invalid_argument);
}

TEST_CASE("theta a-not-b: empty B with different seed must be ignored", "[theta_a_not_b]") {
// An empty sketch carries no hashes, so its seed hash is meaningless. Deserialization,
// theta_union::update() and theta_intersection::update() all skip the seed hash check
// for empty inputs; a-not-b must do the same. This matters across languages because
// datasketches-java serializes every empty compact sketch with a seed hash of 0.

// A: non-empty, but with zero retained entries (all hashes exceeded theta)
update_theta_sketch a = update_theta_sketch::builder().set_p(1e-6f).build();
a.update(1);
REQUIRE_FALSE(a.is_empty());
REQUIRE(a.get_num_retained() == 0);

// B: empty, built with a different seed
compact_theta_sketch b = update_theta_sketch::builder().set_seed(123).build().compact();
REQUIRE(b.is_empty());

theta_a_not_b a_not_b; // default seed
compact_theta_sketch result = a_not_b.compute(a.compact(), b);
REQUIRE_FALSE(result.is_empty());
REQUIRE(result.get_num_retained() == 0);
}

TEST_CASE("theta a-not-b: issue #152", "[theta_a_not_b]") {
update_theta_sketch a = update_theta_sketch::builder().build();
int value = 0;
Expand Down