This is an automated email from the ASF dual-hosted git repository.

tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datasketches-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 1e13991  test(hll): cover union rebuild regressions (#206)
1e13991 is described below

commit 1e139915981a410b3000d7227728f0496d3ee1e7
Author: tison <[email protected]>
AuthorDate: Thu Aug 20 00:49:31 2026 +0800

    test(hll): cover union rebuild regressions (#206)
---
 datasketches/tests/hll_test/union.rs | 115 +++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/datasketches/tests/hll_test/union.rs 
b/datasketches/tests/hll_test/union.rs
index ef67b39..529ffc0 100644
--- a/datasketches/tests/hll_test/union.rs
+++ b/datasketches/tests/hll_test/union.rs
@@ -32,6 +32,47 @@ use datasketches::hll::HllSketch;
 use datasketches::hll::HllType;
 use datasketches::hll::HllUnion;
 
+const HLL_TYPES: [HllType; 3] = [HllType::Hll4, HllType::Hll6, HllType::Hll8];
+
+fn make_hll_sketch(hll_type: HllType, lg_config_k: u8, start: u64, end: u64) 
-> HllSketch {
+    let mut sketch = HllSketch::new(lg_config_k, hll_type);
+    for value in start..end {
+        sketch.update(value);
+    }
+    sketch
+}
+
+fn assert_estimate_within(estimate: f64, expected: f64, relative_error: f64) {
+    let actual_relative_error = (estimate - expected).abs() / expected;
+    assert!(
+        actual_relative_error <= relative_error,
+        "Expected estimate within {:.1}% of {}, got {} ({:.1}% error)",
+        relative_error * 100.0,
+        expected,
+        estimate,
+        actual_relative_error * 100.0,
+    );
+}
+
+fn serialize_flat_union(first: &HllSketch, second: &HllSketch, third: 
&HllSketch) -> Vec<u8> {
+    let mut union = HllUnion::new(8);
+    union.update(first);
+    union.update(second);
+    union.update(third);
+    union.to_sketch(HllType::Hll8).serialize()
+}
+
+fn serialize_nested_union(first: &HllSketch, second: &HllSketch, third: 
&HllSketch) -> Vec<u8> {
+    let mut prefix = HllUnion::new(8);
+    prefix.update(first);
+    prefix.update(second);
+
+    let mut union = HllUnion::new(8);
+    union.update(&prefix.to_sketch(HllType::Hll8));
+    union.update(third);
+    union.to_sketch(HllType::Hll8).serialize()
+}
+
 #[test]
 fn test_union_basic_operations() {
     let mut union = HllUnion::new(12);
@@ -287,6 +328,80 @@ fn test_union_lg_k_handling() {
     );
 }
 
+// Regression coverage for https://github.com/apache/datasketches-cpp/pull/512.
+#[test]
+fn test_union_downsampling_merge_is_not_empty() {
+    for hll_type in HLL_TYPES {
+        let sketch = make_hll_sketch(hll_type, 15, 0, 100_000);
+        let mut union = HllUnion::new(8);
+        union.update(&sketch);
+
+        assert!(!union.is_empty(), "{hll_type:?} union should not be empty");
+    }
+}
+
+#[test]
+fn test_union_mixed_lg_k_estimate_is_merge_order_independent() {
+    const N: u64 = 100_000;
+
+    for hll_type in HLL_TYPES {
+        let a = make_hll_sketch(hll_type, 15, 0, N);
+        let b = make_hll_sketch(hll_type, 8, N, 2 * N);
+        let expected = 2.0 * N as f64;
+
+        let mut larger_first = HllUnion::new(8);
+        larger_first.update(&a);
+        larger_first.update(&b);
+        let larger_first_estimate = larger_first.estimate();
+        assert_estimate_within(larger_first_estimate, expected, 0.1);
+
+        let mut smaller_first = HllUnion::new(8);
+        smaller_first.update(&b);
+        smaller_first.update(&a);
+        let smaller_first_estimate = smaller_first.estimate();
+        assert_estimate_within(smaller_first_estimate, expected, 0.1);
+        assert_eq!(
+            larger_first_estimate, smaller_first_estimate,
+            "{hll_type:?} estimate should be merge-order independent",
+        );
+    }
+}
+
+#[test]
+fn test_union_scalar_update_after_downsampling_merge() {
+    const N: u64 = 100_000;
+
+    for hll_type in HLL_TYPES {
+        let sketch = make_hll_sketch(hll_type, 15, 0, N);
+        let mut union = HllUnion::new(8);
+        union.update(&sketch);
+        for value in N..2 * N {
+            union.update_value(value);
+        }
+
+        assert_estimate_within(union.estimate(), 2.0 * N as f64, 0.1);
+    }
+}
+
+#[test]
+fn test_union_serialization_is_grouping_independent() {
+    const N: u64 = 100_000;
+
+    for hll_type in HLL_TYPES {
+        let a = make_hll_sketch(hll_type, 15, 0, N);
+        let b = make_hll_sketch(hll_type, 8, N, 2 * N);
+        let c = make_hll_sketch(hll_type, 11, N / 2, N + N / 2);
+
+        for (first, second, third) in [(&b, &c, &a), (&a, &c, &b), (&a, &b, 
&c)] {
+            assert_eq!(
+                serialize_nested_union(first, second, third),
+                serialize_flat_union(first, second, third),
+                "{hll_type:?} serialization should be grouping independent",
+            );
+        }
+    }
+}
+
 #[test]
 fn test_union_bounds() {
     let mut union = HllUnion::new(12);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to