Skip to content

Commit be33db5

Browse files
authored
Merge pull request #546 from apache/array-union-num-values
Check the number of values in array tuple union and intersection
2 parents 8b1176c + 248a322 commit be33db5

5 files changed

Lines changed: 93 additions & 0 deletions

File tree

‎tuple/include/array_tuple_intersection.hpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ class array_tuple_intersection: public tuple_intersection<Array, Policy, Allocat
4848
*/
4949
explicit array_tuple_intersection(uint64_t seed = DEFAULT_SEED, const Policy& policy = Policy(), const Allocator& allocator = Allocator());
5050

51+
/**
52+
* Updates the intersection with a given sketch.
53+
* The sketch must have the same number of values as the intersection policy.
54+
* @param sketch to intersect with
55+
* @throw std::invalid_argument if the number of values does not match
56+
*/
57+
template<typename FwdSketch>
58+
void update(FwdSketch&& sketch);
59+
5160
/**
5261
* Produces a copy of the current state of the intersection.
5362
* If update() was not called, the state is the infinite "universe",

‎tuple/include/array_tuple_intersection_impl.hpp‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,26 @@
1717
* under the License.
1818
*/
1919

20+
#include <stdexcept>
21+
#include <string>
22+
2023
namespace datasketches {
2124

2225
template<typename Array, typename Policy, typename Allocator>
2326
array_tuple_intersection<Array, Policy, Allocator>::array_tuple_intersection(uint64_t seed, const Policy& policy, const Allocator& allocator):
2427
Base(seed, policy, allocator) {}
2528

29+
template<typename Array, typename Policy, typename Allocator>
30+
template<typename FwdSketch>
31+
void array_tuple_intersection<Array, Policy, Allocator>::update(FwdSketch&& sketch) {
32+
const uint8_t num_values = this->state_.get_policy().get_external_policy().get_num_values();
33+
if (sketch.get_num_values() != num_values) {
34+
throw std::invalid_argument("number of values mismatch: intersection has " + std::to_string(num_values)
35+
+ ", sketch has " + std::to_string(sketch.get_num_values()));
36+
}
37+
Base::update(std::forward<FwdSketch>(sketch));
38+
}
39+
2640
template<typename Array, typename Policy, typename Allocator>
2741
auto array_tuple_intersection<Array, Policy, Allocator>::get_result(bool ordered) const -> CompactSketch {
2842
return CompactSketch(this->state_.get_policy().get_external_policy().get_num_values(), Base::get_result(ordered));

‎tuple/include/array_tuple_union.hpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ class array_tuple_union: public tuple_union<Array, Policy, Allocator> {
6060

6161
class builder;
6262

63+
/**
64+
* Update the union with a given sketch.
65+
* The sketch must have the same number of values as the union policy.
66+
* @param sketch to update the union with
67+
* @throw std::invalid_argument if the number of values does not match
68+
*/
69+
template<typename FwdSketch>
70+
void update(FwdSketch&& sketch);
71+
6372
CompactSketch get_result(bool ordered = true) const;
6473

6574
private:

‎tuple/include/array_tuple_union_impl.hpp‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,27 @@
1717
* under the License.
1818
*/
1919

20+
#include <stdexcept>
21+
#include <string>
22+
2023
namespace datasketches {
2124

2225
template<typename Array, typename Policy, typename Allocator>
2326
array_tuple_union<Array, Policy, Allocator>::array_tuple_union(uint8_t lg_cur_size, uint8_t lg_nom_size, resize_factor rf, float p, uint64_t theta, uint64_t seed, const Policy& policy, const Allocator& allocator):
2427
Base(lg_cur_size, lg_nom_size, rf, p, theta, seed, policy, allocator)
2528
{}
2629

30+
template<typename Array, typename Policy, typename Allocator>
31+
template<typename FwdSketch>
32+
void array_tuple_union<Array, Policy, Allocator>::update(FwdSketch&& sketch) {
33+
const uint8_t num_values = this->state_.get_policy().get_external_policy().get_num_values();
34+
if (sketch.get_num_values() != num_values) {
35+
throw std::invalid_argument("number of values mismatch: union has " + std::to_string(num_values)
36+
+ ", sketch has " + std::to_string(sketch.get_num_values()));
37+
}
38+
Base::update(std::forward<FwdSketch>(sketch));
39+
}
40+
2741
template<typename Array, typename Policy, typename Allocator>
2842
auto array_tuple_union<Array, Policy, Allocator>::get_result(bool ordered) const -> CompactSketch {
2943
return CompactSketch(this->state_.get_policy().get_external_policy().get_num_values(), Base::get_result(ordered));

‎tuple/test/array_of_doubles_sketch_test.cpp‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,53 @@ TEST_CASE("aod intersection: half overlap", "[tuple_sketch]") {
173173
REQUIRE(result.get_estimate() == Approx(500).margin(0.01));
174174
}
175175

176+
TEST_CASE("aod union: number of values", "[tuple_sketch]") {
177+
auto sketch1 = update_array_of_doubles_sketch::builder(1).build();
178+
sketch1.update(1, std::vector<double>{1});
179+
auto sketch3 = update_array_of_doubles_sketch::builder(3).build();
180+
sketch3.update(1, std::vector<double>{1, 2, 3});
181+
sketch3.update(2, std::vector<double>{4, 5, 6});
182+
183+
// matching number of values: entries for the same key are summed
184+
auto u = array_of_doubles_union::builder(3).build();
185+
u.update(sketch3);
186+
u.update(sketch3.compact());
187+
auto result = u.get_result();
188+
REQUIRE(result.get_num_values() == 3);
189+
REQUIRE(result.get_num_retained() == 2);
190+
for (const auto& entry: result) {
191+
REQUIRE(entry.second.size() == 3);
192+
}
193+
194+
// mismatch is rejected whether or not keys overlap, and for update and compact sketches
195+
REQUIRE_THROWS_AS(u.update(sketch1), std::invalid_argument);
196+
REQUIRE_THROWS_AS(u.update(sketch1.compact()), std::invalid_argument);
197+
auto u1 = array_of_doubles_union::builder(1).build();
198+
REQUIRE_THROWS_AS(u1.update(sketch3), std::invalid_argument);
199+
auto empty3 = update_array_of_doubles_sketch::builder(3).build();
200+
REQUIRE_THROWS_AS(u1.update(empty3), std::invalid_argument);
201+
202+
// a rejected update leaves the union usable
203+
u1.update(sketch1);
204+
REQUIRE(u1.get_result().get_num_retained() == 1);
205+
}
206+
207+
TEST_CASE("aod intersection: number of values", "[tuple_sketch]") {
208+
auto sketch1 = update_array_of_doubles_sketch::builder(1).build();
209+
sketch1.update(1, std::vector<double>{1});
210+
auto sketch3 = update_array_of_doubles_sketch::builder(3).build();
211+
sketch3.update(1, std::vector<double>{1, 2, 3});
212+
213+
array_of_doubles_intersection<default_array_of_doubles_union_policy> intersection(DEFAULT_SEED, 3);
214+
REQUIRE_THROWS_AS(intersection.update(sketch1), std::invalid_argument);
215+
intersection.update(sketch3);
216+
REQUIRE_THROWS_AS(intersection.update(sketch1.compact()), std::invalid_argument);
217+
intersection.update(sketch3.compact());
218+
auto result = intersection.get_result();
219+
REQUIRE(result.get_num_values() == 3);
220+
REQUIRE(result.get_num_retained() == 1);
221+
}
222+
176223
TEST_CASE("aod a-not-b: half overlap", "[tuple_sketch]") {
177224
double a[1] = {1};
178225

0 commit comments

Comments
 (0)