Skip to content

Commit 3c07e75

Browse files
authored
Merge pull request #470 from proost/fix-division-0
fix: division by 0
2 parents 7bb979d + a83254d commit 3c07e75

3 files changed

Lines changed: 282 additions & 2 deletions

File tree

common/include/binomial_bounds.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,8 @@ class binomial_bounds {
441441
}
442442

443443
static void check_theta(double theta) {
444-
if (theta < 0 || theta > 1) {
445-
throw std::invalid_argument("theta must be in [0, 1]");
444+
if (theta <= 0 || theta > 1) {
445+
throw std::invalid_argument("theta must be in (0, 1]");
446446
}
447447
}
448448

common/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ target_sources(common_test
6969
PRIVATE
7070
quantiles_sorted_view_test.cpp
7171
optional_test.cpp
72+
binomial_bounds_test.cpp
7273
)
7374

7475
# now the integration test part
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include <catch2/catch.hpp>
21+
22+
#include "binomial_bounds.hpp"
23+
24+
namespace datasketches {
25+
26+
TEST_CASE("binomial_bounds: get_lower_bound", "[common]") {
27+
28+
SECTION("num_samples == 0") {
29+
double result = binomial_bounds::get_lower_bound(0, 0.5, 1);
30+
REQUIRE(result == 0.0);
31+
}
32+
33+
SECTION("theta == 1.0") {
34+
double result = binomial_bounds::get_lower_bound(100, 1.0, 1);
35+
REQUIRE(result == 100.0);
36+
}
37+
38+
SECTION("num_samples == 1") {
39+
double result = binomial_bounds::get_lower_bound(1, 0.5, 1);
40+
REQUIRE(result >= 0.0);
41+
}
42+
43+
SECTION("num_samples == 1, stddev=2") {
44+
double result = binomial_bounds::get_lower_bound(1, 0.5, 2);
45+
REQUIRE(result >= 0.0);
46+
}
47+
48+
SECTION("num_samples == 1, stddev=3") {
49+
double result = binomial_bounds::get_lower_bound(1, 0.5, 3);
50+
REQUIRE(result >= 0.0);
51+
}
52+
53+
SECTION("num_samples > 120") {
54+
double result = binomial_bounds::get_lower_bound(121, 0.5, 1);
55+
REQUIRE(result >= 0.0);
56+
}
57+
58+
SECTION("num_samples > 120, stddev=2") {
59+
double result = binomial_bounds::get_lower_bound(200, 0.5, 2);
60+
REQUIRE(result >= 0.0);
61+
}
62+
63+
SECTION("num_samples > 120, stddev=3") {
64+
double result = binomial_bounds::get_lower_bound(500, 0.5, 3);
65+
REQUIRE(result >= 0.0);
66+
}
67+
68+
SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5)") {
69+
double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 1);
70+
REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
71+
}
72+
73+
SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5), stddev=2") {
74+
double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 2);
75+
REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
76+
}
77+
78+
SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5), stddev=3") {
79+
double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 3);
80+
REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
81+
}
82+
83+
SECTION("2 <= num_samples <= 120 AND theta < num_samples/360") {
84+
double result = binomial_bounds::get_lower_bound(100, 0.001, 1);
85+
REQUIRE(result >= 0.0);
86+
}
87+
88+
SECTION("2 <= num_samples <= 120 AND theta < num_samples/360, stddev=2") {
89+
double result = binomial_bounds::get_lower_bound(100, 0.001, 2);
90+
REQUIRE(result >= 0.0);
91+
}
92+
93+
SECTION("2 <= num_samples <= 120 AND theta < num_samples/360, stddev=3") {
94+
double result = binomial_bounds::get_lower_bound(100, 0.001, 3);
95+
REQUIRE(result >= 0.0);
96+
}
97+
98+
SECTION("2 <= num_samples <= 120 AND middle range theta (exact calculation)") {
99+
double result = binomial_bounds::get_lower_bound(10, 0.5, 1);
100+
REQUIRE(result >= 0.0);
101+
}
102+
103+
SECTION("2 <= num_samples <= 120 AND middle range theta, stddev=2") {
104+
double result = binomial_bounds::get_lower_bound(10, 0.5, 2);
105+
REQUIRE(result >= 0.0);
106+
}
107+
108+
SECTION("2 <= num_samples <= 120 AND middle range theta, stddev=3") {
109+
double result = binomial_bounds::get_lower_bound(10, 0.5, 3);
110+
REQUIRE(result >= 0.0);
111+
}
112+
113+
SECTION("theta=0") {
114+
REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(10, 0.0, 1), std::invalid_argument);
115+
}
116+
117+
SECTION("theta very close to 0") {
118+
double result = binomial_bounds::get_lower_bound(10, 1e-10, 1);
119+
REQUIRE(result >= 0.0);
120+
}
121+
122+
SECTION("num_samples=2 boundary") {
123+
double result = binomial_bounds::get_lower_bound(2, 0.5, 1);
124+
REQUIRE(result >= 0.0);
125+
}
126+
127+
SECTION("num_samples=120 boundary") {
128+
double result = binomial_bounds::get_lower_bound(120, 0.5, 1);
129+
REQUIRE(result >= 0.0);
130+
}
131+
132+
SECTION("estimate clamping case") {
133+
double result = binomial_bounds::get_lower_bound(10, 0.9, 1);
134+
double estimate = 10.0 / 0.9;
135+
REQUIRE(result <= estimate);
136+
}
137+
138+
SECTION("invalid theta < 0") {
139+
REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, -0.1, 1), std::invalid_argument);
140+
}
141+
142+
SECTION("invalid theta > 1") {
143+
REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 1.1, 1), std::invalid_argument);
144+
}
145+
146+
SECTION("invalid stddev = 0") {
147+
REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 0.5, 0), std::invalid_argument);
148+
}
149+
150+
SECTION("invalid stddev = 4") {
151+
REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 0.5, 4), std::invalid_argument);
152+
}
153+
}
154+
155+
TEST_CASE("binomial_bounds: get_upper_bound", "[common]") {
156+
157+
SECTION("theta == 1.0") {
158+
double result = binomial_bounds::get_upper_bound(100, 1.0, 1);
159+
REQUIRE(result == 100.0);
160+
}
161+
162+
SECTION("num_samples == 0") {
163+
double result = binomial_bounds::get_upper_bound(0, 0.5, 1);
164+
REQUIRE(result > 0.0);
165+
}
166+
167+
SECTION("num_samples == 0, stddev=2") {
168+
double result = binomial_bounds::get_upper_bound(0, 0.5, 2);
169+
REQUIRE(result > 0.0);
170+
}
171+
172+
SECTION("num_samples == 0, stddev=3") {
173+
double result = binomial_bounds::get_upper_bound(0, 0.5, 3);
174+
REQUIRE(result > 0.0);
175+
}
176+
177+
SECTION("num_samples > 120") {
178+
double result = binomial_bounds::get_upper_bound(121, 0.5, 1);
179+
REQUIRE(result >= 0.0);
180+
}
181+
182+
SECTION("num_samples > 120, stddev=2") {
183+
double result = binomial_bounds::get_upper_bound(200, 0.5, 2);
184+
REQUIRE(result >= 0.0);
185+
}
186+
187+
SECTION("num_samples > 120, stddev=3") {
188+
double result = binomial_bounds::get_upper_bound(500, 0.5, 3);
189+
REQUIRE(result >= 0.0);
190+
}
191+
192+
SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5)") {
193+
double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 1);
194+
REQUIRE(result == 51.0);
195+
}
196+
197+
SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5), stddev=2") {
198+
double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 2);
199+
REQUIRE(result == 51.0);
200+
}
201+
202+
SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5), stddev=3") {
203+
double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 3);
204+
REQUIRE(result == 51.0);
205+
}
206+
207+
SECTION("1 <= num_samples <= 120 AND theta < num_samples/360") {
208+
double result = binomial_bounds::get_upper_bound(100, 0.001, 1);
209+
REQUIRE(result >= 0.0);
210+
}
211+
212+
SECTION("1 <= num_samples <= 120 AND theta < num_samples/360, stddev=2") {
213+
double result = binomial_bounds::get_upper_bound(100, 0.001, 2);
214+
REQUIRE(result >= 0.0);
215+
}
216+
217+
SECTION("1 <= num_samples <= 120 AND theta < num_samples/360, stddev=3") {
218+
double result = binomial_bounds::get_upper_bound(100, 0.001, 3);
219+
REQUIRE(result >= 0.0);
220+
}
221+
222+
SECTION("1 <= num_samples <= 120 AND middle range theta (exact calculation)") {
223+
double result = binomial_bounds::get_upper_bound(10, 0.5, 1);
224+
REQUIRE(result >= 0.0);
225+
}
226+
227+
SECTION("1 <= num_samples <= 120 AND middle range theta, stddev=2") {
228+
double result = binomial_bounds::get_upper_bound(10, 0.5, 2);
229+
REQUIRE(result >= 0.0);
230+
}
231+
232+
SECTION("1 <= num_samples <= 120 AND middle range theta, stddev=3") {
233+
double result = binomial_bounds::get_upper_bound(10, 0.5, 3);
234+
REQUIRE(result >= 0.0);
235+
}
236+
237+
SECTION("theta=0") {
238+
REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(10, 0.0, 1), std::invalid_argument);
239+
}
240+
241+
SECTION("theta very close to 0") {
242+
double result = binomial_bounds::get_upper_bound(10, 1e-10, 1);
243+
REQUIRE(result >= 0.0);
244+
}
245+
246+
SECTION("num_samples=1 boundary") {
247+
double result = binomial_bounds::get_upper_bound(1, 0.5, 1);
248+
REQUIRE(result >= 0.0);
249+
}
250+
251+
SECTION("num_samples=120 boundary") {
252+
double result = binomial_bounds::get_upper_bound(120, 0.5, 1);
253+
REQUIRE(result >= 0.0);
254+
}
255+
256+
SECTION("estimate clamping case") {
257+
double result = binomial_bounds::get_upper_bound(10, 0.9, 1);
258+
double estimate = 10.0 / 0.9;
259+
REQUIRE(result >= estimate);
260+
}
261+
262+
SECTION("invalid theta < 0") {
263+
REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, -0.1, 1), std::invalid_argument);
264+
}
265+
266+
SECTION("invalid theta > 1") {
267+
REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 1.1, 1), std::invalid_argument);
268+
}
269+
270+
SECTION("invalid stddev = 0") {
271+
REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 0.5, 0), std::invalid_argument);
272+
}
273+
274+
SECTION("invalid stddev = 4") {
275+
REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 0.5, 4), std::invalid_argument);
276+
}
277+
}
278+
279+
} /* namespace datasketches */

0 commit comments

Comments
 (0)