summaryrefslogtreecommitdiff
path: root/absl/random
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2020-08-02 14:23:26 -0700
committerGravatar Derek Mauro <dmauro@google.com>2020-08-03 13:24:24 -0400
commit1995c6a3c2f9080160d9d8716504dc004e5e1ec0 (patch)
tree0b1e861b0d2c16f78d0f2e861a94a2bccf38965a /absl/random
parent184cf2524101310a0ba315c743e82cf45fccccf8 (diff)
Export of internal Abseil changes
-- 790f9061df340cd900e8da70e66c363f7af3c2eb by Abseil Team <absl-team@google.com>: Add support for rvalue reference to function types. PiperOrigin-RevId: 324508531 -- 51fe201dbb41a3ebc3d49ff65250b5f464279d43 by Abseil Team <absl-team@google.com>: Cleaning up function comment style; no substantive change. PiperOrigin-RevId: 324497401 -- da8595d5266577d0c170528d12f6de17b8affcc2 by Abseil Team <absl-team@google.com>: Add support for demangling GNU vector types. PiperOrigin-RevId: 324494559 -- 0cb0acf88c1750f6963c9cb85249f9b4f0bd5104 by Abseil Team <absl-team@google.com>: Add support for thread-local types. PiperOrigin-RevId: 324491183 -- c676bc8380560599cd26f7f231e04e6be532e904 by Abseil Team <absl-team@google.com>: Add support for demangling "Du" (char8_t). PiperOrigin-RevId: 324441607 -- b218bf6467bc62b327214782c881e8224ad91509 by Abseil Team <absl-team@google.com>: Update doc comments in header of `any.h` to reflect that `absl::variant` has been released. PiperOrigin-RevId: 324431690 -- e5b579f3f1aa598c1f62e71dba7103b98811de59 by Laramie Leavitt <lar@google.com>: Bugfix: Fix bounds in absl::Uniform where one of the bounds is min/max. When absl::Uniform(rng, tag, a, b) is called, the tag is used in conjunction with the type to determine whether or not to manipulate the bounds to make them inclusive or exclusive through the uniform_*_bound functions. Unfortunately, at limits of the interval the function was not well behaved. The previous implementation used wrapping arithmetic. This causes incorrect bounds computation at the extremes (numeric_limits::min / numeric_limits::max) the bound would wrap. Improve this situation by: 1/ Changing the uniform_*_bound functions to use saturating arithmetic instead of wrapping, thus in the unsigned case, the upper_bound of IntervalOpenOpen for 0 is now 0, rather than numeric_limits::max, likewise for the lower bound. 2/ Adjusting the hi/lo checks in the distributions. When the interval is empty, such as for absl::Uniform(absl::IntervalOpenOpen, gen, 1, 0), the return value is somewhat nonsensical. Now absl::Uniform more consistently returns the low input rather than any adjusted input. In the above case, that means that 1 is returned rather than 2. NOTE: Calls to absl::Uniform where the resolved upper bound is < the lower bound are still ill-formed and should be avoided. 3/ Adding better tests. The underlying uniform_*_distribution classes are not affected. PiperOrigin-RevId: 324240873 GitOrigin-RevId: 790f9061df340cd900e8da70e66c363f7af3c2eb Change-Id: I2a2208650ea3135c575e200b868ce1d275069fc8
Diffstat (limited to 'absl/random')
-rw-r--r--absl/random/CMakeLists.txt15
-rw-r--r--absl/random/distributions.h10
-rw-r--r--absl/random/distributions_test.cc142
-rw-r--r--absl/random/internal/BUILD.bazel12
-rw-r--r--absl/random/internal/uniform_helper.h38
-rw-r--r--absl/random/internal/uniform_helper_test.cc279
6 files changed, 401 insertions, 95 deletions
diff --git a/absl/random/CMakeLists.txt b/absl/random/CMakeLists.txt
index ade9ea10..7d7bec83 100644
--- a/absl/random/CMakeLists.txt
+++ b/absl/random/CMakeLists.txt
@@ -1162,6 +1162,21 @@ absl_cc_library(
# Internal-only target, do not depend on directly.
absl_cc_test(
NAME
+ random_internal_uniform_helper_test
+ SRCS
+ "internal/uniform_helper_test.cc"
+ COPTS
+ ${ABSL_TEST_COPTS}
+ LINKOPTS
+ ${ABSL_DEFAULT_LINKOPTS}
+ DEPS
+ absl::random_internal_uniform_helper
+ gtest_main
+)
+
+# Internal-only target, do not depend on directly.
+absl_cc_test(
+ NAME
random_internal_iostream_state_saver_test
SRCS
"internal/iostream_state_saver_test.cc"
diff --git a/absl/random/distributions.h b/absl/random/distributions.h
index 6775c5d6..31c79694 100644
--- a/absl/random/distributions.h
+++ b/absl/random/distributions.h
@@ -128,7 +128,7 @@ Uniform(TagType tag,
auto a = random_internal::uniform_lower_bound(tag, lo, hi);
auto b = random_internal::uniform_upper_bound(tag, lo, hi);
- if (a > b) return a;
+ if (!random_internal::is_uniform_range_valid(a, b)) return lo;
return random_internal::DistributionCaller<gen_t>::template Call<
distribution_t>(&urbg, tag, lo, hi);
@@ -144,11 +144,11 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references)
R lo, R hi) {
using gen_t = absl::decay_t<URBG>;
using distribution_t = random_internal::UniformDistributionWrapper<R>;
-
constexpr auto tag = absl::IntervalClosedOpen;
+
auto a = random_internal::uniform_lower_bound(tag, lo, hi);
auto b = random_internal::uniform_upper_bound(tag, lo, hi);
- if (a > b) return a;
+ if (!random_internal::is_uniform_range_valid(a, b)) return lo;
return random_internal::DistributionCaller<gen_t>::template Call<
distribution_t>(&urbg, lo, hi);
@@ -172,7 +172,7 @@ Uniform(TagType tag,
auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
- if (a > b) return a;
+ if (!random_internal::is_uniform_range_valid(a, b)) return lo;
return random_internal::DistributionCaller<gen_t>::template Call<
distribution_t>(&urbg, tag, static_cast<return_t>(lo),
@@ -196,7 +196,7 @@ Uniform(URBG&& urbg, // NOLINT(runtime/references)
constexpr auto tag = absl::IntervalClosedOpen;
auto a = random_internal::uniform_lower_bound<return_t>(tag, lo, hi);
auto b = random_internal::uniform_upper_bound<return_t>(tag, lo, hi);
- if (a > b) return a;
+ if (!random_internal::is_uniform_range_valid(a, b)) return lo;
return random_internal::DistributionCaller<gen_t>::template Call<
distribution_t>(&urbg, static_cast<return_t>(lo),
diff --git a/absl/random/distributions_test.cc b/absl/random/distributions_test.cc
index 2d92723a..5866a072 100644
--- a/absl/random/distributions_test.cc
+++ b/absl/random/distributions_test.cc
@@ -29,94 +29,6 @@ constexpr int kSize = 400000;
class RandomDistributionsTest : public testing::Test {};
-TEST_F(RandomDistributionsTest, UniformBoundFunctions) {
- using absl::IntervalClosedClosed;
- using absl::IntervalClosedOpen;
- using absl::IntervalOpenClosed;
- using absl::IntervalOpenOpen;
- using absl::random_internal::uniform_lower_bound;
- using absl::random_internal::uniform_upper_bound;
-
- // absl::uniform_int_distribution natively assumes IntervalClosedClosed
- // absl::uniform_real_distribution natively assumes IntervalClosedOpen
-
- EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1);
- EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1);
- EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0);
- EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0);
- EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0);
- EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0);
-
- EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0);
- EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0);
- EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0);
- EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0);
- EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0);
- EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0);
-
- EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99);
- EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99);
- EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0);
- EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0);
- EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0);
- EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0);
-
- EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100);
- EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100);
- EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0);
- EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0);
- EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0);
- EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0);
-
- // Negative value tests
- EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99);
- EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99);
- EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0);
- EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0);
- EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0);
- EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0);
-
- EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100);
- EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100);
- EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0);
- EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0);
- EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0),
- -2.0);
- EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0);
-
- EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2);
- EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2);
- EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0);
- EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0);
- EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0);
- EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0);
-
- EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1);
- EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1);
- EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0);
- EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0);
- EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0);
- EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0),
- -1.0);
-
- // Edge cases: the next value toward itself is itself.
- const double d = 1.0;
- const float f = 1.0;
- EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, d, d), d);
- EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, f, f), f);
-
- EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0);
- EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0);
- EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0);
- EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0);
-
- EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0.0f,
- std::numeric_limits<float>::max()),
- std::numeric_limits<float>::max());
- EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0.0,
- std::numeric_limits<double>::max()),
- std::numeric_limits<double>::max());
-}
struct Invalid {};
@@ -284,7 +196,9 @@ TEST_F(RandomDistributionsTest, UniformTypeInference) {
// Properly promotes float.
CheckArgsInferType<float, double, double>();
+}
+TEST_F(RandomDistributionsTest, UniformExamples) {
// Examples.
absl::InsecureBitGen gen;
EXPECT_NE(1, absl::Uniform(gen, static_cast<uint16_t>(0), 1.0f));
@@ -307,6 +221,58 @@ TEST_F(RandomDistributionsTest, UniformNoBounds) {
absl::Uniform<uint64_t>(gen);
}
+TEST_F(RandomDistributionsTest, UniformNonsenseRanges) {
+ // The ranges used in this test are undefined behavior.
+ // The results are arbitrary and subject to future changes.
+ absl::InsecureBitGen gen;
+
+ // <uint>
+ EXPECT_EQ(0, absl::Uniform<uint64_t>(gen, 0, 0));
+ EXPECT_EQ(1, absl::Uniform<uint64_t>(gen, 1, 0));
+ EXPECT_EQ(0, absl::Uniform<uint64_t>(absl::IntervalOpenOpen, gen, 0, 0));
+ EXPECT_EQ(1, absl::Uniform<uint64_t>(absl::IntervalOpenOpen, gen, 1, 0));
+
+ constexpr auto m = (std::numeric_limits<uint64_t>::max)();
+
+ EXPECT_EQ(m, absl::Uniform(gen, m, m));
+ EXPECT_EQ(m, absl::Uniform(gen, m, m - 1));
+ EXPECT_EQ(m - 1, absl::Uniform(gen, m - 1, m));
+ EXPECT_EQ(m, absl::Uniform(absl::IntervalOpenOpen, gen, m, m));
+ EXPECT_EQ(m, absl::Uniform(absl::IntervalOpenOpen, gen, m, m - 1));
+ EXPECT_EQ(m - 1, absl::Uniform(absl::IntervalOpenOpen, gen, m - 1, m));
+
+ // <int>
+ EXPECT_EQ(0, absl::Uniform<int64_t>(gen, 0, 0));
+ EXPECT_EQ(1, absl::Uniform<int64_t>(gen, 1, 0));
+ EXPECT_EQ(0, absl::Uniform<int64_t>(absl::IntervalOpenOpen, gen, 0, 0));
+ EXPECT_EQ(1, absl::Uniform<int64_t>(absl::IntervalOpenOpen, gen, 1, 0));
+
+ constexpr auto l = (std::numeric_limits<int64_t>::min)();
+ constexpr auto r = (std::numeric_limits<int64_t>::max)();
+
+ EXPECT_EQ(l, absl::Uniform(gen, l, l));
+ EXPECT_EQ(r, absl::Uniform(gen, r, r));
+ EXPECT_EQ(r, absl::Uniform(gen, r, r - 1));
+ EXPECT_EQ(r - 1, absl::Uniform(gen, r - 1, r));
+ EXPECT_EQ(l, absl::Uniform(absl::IntervalOpenOpen, gen, l, l));
+ EXPECT_EQ(r, absl::Uniform(absl::IntervalOpenOpen, gen, r, r));
+ EXPECT_EQ(r, absl::Uniform(absl::IntervalOpenOpen, gen, r, r - 1));
+ EXPECT_EQ(r - 1, absl::Uniform(absl::IntervalOpenOpen, gen, r - 1, r));
+
+ // <double>
+ const double e = std::nextafter(1.0, 2.0); // 1 + epsilon
+ const double f = std::nextafter(1.0, 0.0); // 1 - epsilon
+ const double g = std::numeric_limits<double>::denorm_min();
+
+ EXPECT_EQ(1.0, absl::Uniform(gen, 1.0, e));
+ EXPECT_EQ(1.0, absl::Uniform(gen, 1.0, f));
+ EXPECT_EQ(0.0, absl::Uniform(gen, 0.0, g));
+
+ EXPECT_EQ(e, absl::Uniform(absl::IntervalOpenOpen, gen, 1.0, e));
+ EXPECT_EQ(f, absl::Uniform(absl::IntervalOpenOpen, gen, 1.0, f));
+ EXPECT_EQ(g, absl::Uniform(absl::IntervalOpenOpen, gen, 0.0, g));
+}
+
// TODO(lar): Validate properties of non-default interval-semantics.
TEST_F(RandomDistributionsTest, UniformReal) {
std::vector<double> values(kSize);
diff --git a/absl/random/internal/BUILD.bazel b/absl/random/internal/BUILD.bazel
index 83843b4c..000cc45b 100644
--- a/absl/random/internal/BUILD.bazel
+++ b/absl/random/internal/BUILD.bazel
@@ -716,3 +716,15 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)
+
+cc_test(
+ name = "uniform_helper_test",
+ size = "small",
+ srcs = ["uniform_helper_test.cc"],
+ copts = ABSL_TEST_COPTS,
+ linkopts = ABSL_DEFAULT_LINKOPTS,
+ deps = [
+ ":uniform_helper",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/absl/random/internal/uniform_helper.h b/absl/random/internal/uniform_helper.h
index 5b2afecb..1243bc1c 100644
--- a/absl/random/internal/uniform_helper.h
+++ b/absl/random/internal/uniform_helper.h
@@ -105,7 +105,7 @@ typename absl::enable_if_t<
std::is_same<Tag, IntervalOpenOpenTag>>>::value,
IntType>
uniform_lower_bound(Tag, IntType a, IntType) {
- return a + 1;
+ return a < (std::numeric_limits<IntType>::max)() ? (a + 1) : a;
}
template <typename FloatType, typename Tag>
@@ -136,7 +136,7 @@ typename absl::enable_if_t<
std::is_same<Tag, IntervalOpenOpenTag>>>::value,
IntType>
uniform_upper_bound(Tag, IntType, IntType b) {
- return b - 1;
+ return b > (std::numeric_limits<IntType>::min)() ? (b - 1) : b;
}
template <typename FloatType, typename Tag>
@@ -172,6 +172,40 @@ uniform_upper_bound(Tag, FloatType, FloatType b) {
return std::nextafter(b, (std::numeric_limits<FloatType>::max)());
}
+// Returns whether the bounds are valid for the underlying distribution.
+// Inputs must have already been resolved via uniform_*_bound calls.
+//
+// The c++ standard constraints in [rand.dist.uni.int] are listed as:
+// requires: lo <= hi.
+//
+// In the uniform_int_distrubtion, {lo, hi} are closed, closed. Thus:
+// [0, 0] is legal.
+// [0, 0) is not legal, but [0, 1) is, which translates to [0, 0].
+// (0, 1) is not legal, but (0, 2) is, which translates to [1, 1].
+// (0, 0] is not legal, but (0, 1] is, which translates to [1, 1].
+//
+// The c++ standard constraints in [rand.dist.uni.real] are listed as:
+// requires: lo <= hi.
+// requires: (hi - lo) <= numeric_limits<T>::max()
+//
+// In the uniform_real_distribution, {lo, hi} are closed, open, Thus:
+// [0, 0] is legal, which is [0, 0+epsilon).
+// [0, 0) is legal.
+// (0, 0) is not legal, but (0-epsilon, 0+epsilon) is.
+// (0, 0] is not legal, but (0, 0+epsilon] is.
+//
+template <typename FloatType>
+absl::enable_if_t<std::is_floating_point<FloatType>::value, bool>
+is_uniform_range_valid(FloatType a, FloatType b) {
+ return a <= b && std::isfinite(b - a);
+}
+
+template <typename IntType>
+absl::enable_if_t<std::is_integral<IntType>::value, bool>
+is_uniform_range_valid(IntType a, IntType b) {
+ return a <= b;
+}
+
// UniformDistribution selects either absl::uniform_int_distribution
// or absl::uniform_real_distribution depending on the NumType parameter.
template <typename NumType>
diff --git a/absl/random/internal/uniform_helper_test.cc b/absl/random/internal/uniform_helper_test.cc
new file mode 100644
index 00000000..173c49b0
--- /dev/null
+++ b/absl/random/internal/uniform_helper_test.cc
@@ -0,0 +1,279 @@
+// Copyright 2017 The Abseil Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/random/internal/uniform_helper.h"
+
+#include <cmath>
+#include <cstdint>
+#include <random>
+
+#include "gtest/gtest.h"
+
+namespace {
+
+using absl::IntervalClosedClosedTag;
+using absl::IntervalClosedOpenTag;
+using absl::IntervalOpenClosedTag;
+using absl::IntervalOpenOpenTag;
+using absl::random_internal::uniform_inferred_return_t;
+using absl::random_internal::uniform_lower_bound;
+using absl::random_internal::uniform_upper_bound;
+
+class UniformHelperTest : public testing::Test {};
+
+TEST_F(UniformHelperTest, UniformBoundFunctionsGeneral) {
+ constexpr IntervalClosedClosedTag IntervalClosedClosed;
+ constexpr IntervalClosedOpenTag IntervalClosedOpen;
+ constexpr IntervalOpenClosedTag IntervalOpenClosed;
+ constexpr IntervalOpenOpenTag IntervalOpenOpen;
+
+ // absl::uniform_int_distribution natively assumes IntervalClosedClosed
+ // absl::uniform_real_distribution natively assumes IntervalClosedOpen
+
+ EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, 0, 100), 1);
+ EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, 0, 100), 1);
+ EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, 0, 1.0), 0);
+ EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, 0, 1.0), 0);
+ EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, 0, 1.0), 0);
+ EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, 0, 1.0), 0);
+
+ EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 100), 0);
+ EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 100), 0);
+ EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, 0, 1.0), 0);
+ EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, 0, 1.0), 0);
+ EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, 0, 1.0), 0);
+ EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, 0, 1.0), 0);
+
+ EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 100), 99);
+ EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 100), 99);
+ EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, 0, 1.0), 1.0);
+ EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, 0, 1.0), 1.0);
+ EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, 0, 1.0), 1.0);
+ EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, 0, 1.0), 1.0);
+
+ EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, 0, 100), 100);
+ EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, 0, 100), 100);
+ EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, 0, 1.0), 1.0);
+ EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, 0, 1.0), 1.0);
+ EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, 0, 1.0), 1.0);
+ EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, 0, 1.0), 1.0);
+
+ // Negative value tests
+ EXPECT_EQ(uniform_lower_bound(IntervalOpenClosed, -100, -1), -99);
+ EXPECT_EQ(uniform_lower_bound(IntervalOpenOpen, -100, -1), -99);
+ EXPECT_GT(uniform_lower_bound<float>(IntervalOpenClosed, -2.0, -1.0), -2.0);
+ EXPECT_GT(uniform_lower_bound<float>(IntervalOpenOpen, -2.0, -1.0), -2.0);
+ EXPECT_GT(uniform_lower_bound<double>(IntervalOpenClosed, -2.0, -1.0), -2.0);
+ EXPECT_GT(uniform_lower_bound<double>(IntervalOpenOpen, -2.0, -1.0), -2.0);
+
+ EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -100, -1), -100);
+ EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -100, -1), -100);
+ EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedClosed, -2.0, -1.0), -2.0);
+ EXPECT_EQ(uniform_lower_bound<float>(IntervalClosedOpen, -2.0, -1.0), -2.0);
+ EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedClosed, -2.0, -1.0),
+ -2.0);
+ EXPECT_EQ(uniform_lower_bound<double>(IntervalClosedOpen, -2.0, -1.0), -2.0);
+
+ EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -100, -1), -2);
+ EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -100, -1), -2);
+ EXPECT_EQ(uniform_upper_bound<float>(IntervalOpenOpen, -2.0, -1.0), -1.0);
+ EXPECT_EQ(uniform_upper_bound<float>(IntervalClosedOpen, -2.0, -1.0), -1.0);
+ EXPECT_EQ(uniform_upper_bound<double>(IntervalOpenOpen, -2.0, -1.0), -1.0);
+ EXPECT_EQ(uniform_upper_bound<double>(IntervalClosedOpen, -2.0, -1.0), -1.0);
+
+ EXPECT_EQ(uniform_upper_bound(IntervalOpenClosed, -100, -1), -1);
+ EXPECT_EQ(uniform_upper_bound(IntervalClosedClosed, -100, -1), -1);
+ EXPECT_GT(uniform_upper_bound<float>(IntervalOpenClosed, -2.0, -1.0), -1.0);
+ EXPECT_GT(uniform_upper_bound<float>(IntervalClosedClosed, -2.0, -1.0), -1.0);
+ EXPECT_GT(uniform_upper_bound<double>(IntervalOpenClosed, -2.0, -1.0), -1.0);
+ EXPECT_GT(uniform_upper_bound<double>(IntervalClosedClosed, -2.0, -1.0),
+ -1.0);
+
+ EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 1.0, 2.0), 1.0);
+ EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, +0.0), 1.0);
+ EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -0.0), 1.0);
+ EXPECT_LT(uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0), 1.0);
+}
+
+TEST_F(UniformHelperTest, UniformBoundFunctionsIntBounds) {
+ // Verifies the saturating nature of uniform_lower_bound and
+ // uniform_upper_bound
+ constexpr IntervalOpenOpenTag IntervalOpenOpen;
+
+ // uint max.
+ constexpr auto m = (std::numeric_limits<uint64_t>::max)();
+
+ EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0u, 0u));
+ EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m, m));
+ EXPECT_EQ(m, uniform_lower_bound(IntervalOpenOpen, m - 1, m - 1));
+ EXPECT_EQ(0, uniform_upper_bound(IntervalOpenOpen, 0u, 0u));
+ EXPECT_EQ(m - 1, uniform_upper_bound(IntervalOpenOpen, m, m));
+
+ // int min/max
+ constexpr auto l = (std::numeric_limits<int64_t>::min)();
+ constexpr auto r = (std::numeric_limits<int64_t>::max)();
+ EXPECT_EQ(1, uniform_lower_bound(IntervalOpenOpen, 0, 0));
+ EXPECT_EQ(l + 1, uniform_lower_bound(IntervalOpenOpen, l, l));
+ EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r - 1, r - 1));
+ EXPECT_EQ(r, uniform_lower_bound(IntervalOpenOpen, r, r));
+ EXPECT_EQ(-1, uniform_upper_bound(IntervalOpenOpen, 0, 0));
+ EXPECT_EQ(l, uniform_upper_bound(IntervalOpenOpen, l, l));
+ EXPECT_EQ(r - 1, uniform_upper_bound(IntervalOpenOpen, r, r));
+}
+
+TEST_F(UniformHelperTest, UniformBoundFunctionsRealBounds) {
+ // absl::uniform_real_distribution natively assumes IntervalClosedOpen;
+ // use the inverse here so each bound has to change.
+ constexpr IntervalOpenClosedTag IntervalOpenClosed;
+
+ // Edge cases: the next value toward itself is itself.
+ EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, 1.0, 1.0));
+ EXPECT_EQ(1.0f, uniform_lower_bound(IntervalOpenClosed, 1.0f, 1.0f));
+
+ // rightmost and leftmost finite values.
+ constexpr auto r = (std::numeric_limits<double>::max)();
+ const auto re = std::nexttoward(r, 0.0);
+ constexpr auto l = -r;
+ const auto le = std::nexttoward(l, 0.0);
+
+ EXPECT_EQ(l, uniform_lower_bound(IntervalOpenClosed, l, l)); // (l,l)
+ EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, r, r)); // (r,r)
+ EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, r)); // (l,r)
+ EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, 0.0)); // (l, 0)
+ EXPECT_EQ(le, uniform_lower_bound(IntervalOpenClosed, l, le)); // (l, le)
+ EXPECT_EQ(r, uniform_lower_bound(IntervalOpenClosed, re, r)); // (re, r)
+
+ EXPECT_EQ(le, uniform_upper_bound(IntervalOpenClosed, l, l)); // (l,l)
+ EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, r, r)); // (r,r)
+ EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, r)); // (l,r)
+ EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, l, re)); // (l,re)
+ EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, 0.0, r)); // (0, r)
+ EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, re, r)); // (re, r)
+ EXPECT_EQ(r, uniform_upper_bound(IntervalOpenClosed, le, re)); // (le, re)
+
+ const double e = std::nextafter(1.0, 2.0); // 1 + epsilon
+ const double f = std::nextafter(1.0, 0.0); // 1 - epsilon
+
+ // (1.0, 1.0 + epsilon)
+ EXPECT_EQ(e, uniform_lower_bound(IntervalOpenClosed, 1.0, e));
+ EXPECT_EQ(std::nextafter(e, 2.0),
+ uniform_upper_bound(IntervalOpenClosed, 1.0, e));
+
+ // (1.0-epsilon, 1.0)
+ EXPECT_EQ(1.0, uniform_lower_bound(IntervalOpenClosed, f, 1.0));
+ EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, f, 1.0));
+
+ // denorm cases.
+ const double g = std::numeric_limits<double>::denorm_min();
+ const double h = std::nextafter(g, 1.0);
+
+ // (0, denorm_min)
+ EXPECT_EQ(g, uniform_lower_bound(IntervalOpenClosed, 0.0, g));
+ EXPECT_EQ(h, uniform_upper_bound(IntervalOpenClosed, 0.0, g));
+
+ // (denorm_min, 1.0)
+ EXPECT_EQ(h, uniform_lower_bound(IntervalOpenClosed, g, 1.0));
+ EXPECT_EQ(e, uniform_upper_bound(IntervalOpenClosed, g, 1.0));
+
+ // Edge cases: invalid bounds.
+ EXPECT_EQ(f, uniform_lower_bound(IntervalOpenClosed, 1.0, -1.0));
+}
+
+struct Invalid {};
+
+template <typename A, typename B>
+auto InferredUniformReturnT(int) -> uniform_inferred_return_t<A, B>;
+
+template <typename, typename>
+Invalid InferredUniformReturnT(...);
+
+// Given types <A, B, Expect>, CheckArgsInferType() verifies that
+//
+// uniform_inferred_return_t<A, B> and
+// uniform_inferred_return_t<B, A>
+//
+// returns the type "Expect".
+//
+// This interface can also be used to assert that a given inferred return types
+// are invalid. Writing:
+//
+// CheckArgsInferType<float, int, Invalid>()
+//
+// will assert that this overload does not exist.
+template <typename A, typename B, typename Expect>
+void CheckArgsInferType() {
+ static_assert(
+ absl::conjunction<
+ std::is_same<Expect, decltype(InferredUniformReturnT<A, B>(0))>,
+ std::is_same<Expect,
+ decltype(InferredUniformReturnT<B, A>(0))>>::value,
+ "");
+}
+
+TEST_F(UniformHelperTest, UniformTypeInference) {
+ // Infers common types.
+ CheckArgsInferType<uint16_t, uint16_t, uint16_t>();
+ CheckArgsInferType<uint32_t, uint32_t, uint32_t>();
+ CheckArgsInferType<uint64_t, uint64_t, uint64_t>();
+ CheckArgsInferType<int16_t, int16_t, int16_t>();
+ CheckArgsInferType<int32_t, int32_t, int32_t>();
+ CheckArgsInferType<int64_t, int64_t, int64_t>();
+ CheckArgsInferType<float, float, float>();
+ CheckArgsInferType<double, double, double>();
+
+ // Properly promotes uint16_t.
+ CheckArgsInferType<uint16_t, uint32_t, uint32_t>();
+ CheckArgsInferType<uint16_t, uint64_t, uint64_t>();
+ CheckArgsInferType<uint16_t, int32_t, int32_t>();
+ CheckArgsInferType<uint16_t, int64_t, int64_t>();
+ CheckArgsInferType<uint16_t, float, float>();
+ CheckArgsInferType<uint16_t, double, double>();
+
+ // Properly promotes int16_t.
+ CheckArgsInferType<int16_t, int32_t, int32_t>();
+ CheckArgsInferType<int16_t, int64_t, int64_t>();
+ CheckArgsInferType<int16_t, float, float>();
+ CheckArgsInferType<int16_t, double, double>();
+
+ // Invalid (u)int16_t-pairings do not compile.
+ // See "CheckArgsInferType" comments above, for how this is achieved.
+ CheckArgsInferType<uint16_t, int16_t, Invalid>();
+ CheckArgsInferType<int16_t, uint32_t, Invalid>();
+ CheckArgsInferType<int16_t, uint64_t, Invalid>();
+
+ // Properly promotes uint32_t.
+ CheckArgsInferType<uint32_t, uint64_t, uint64_t>();
+ CheckArgsInferType<uint32_t, int64_t, int64_t>();
+ CheckArgsInferType<uint32_t, double, double>();
+
+ // Properly promotes int32_t.
+ CheckArgsInferType<int32_t, int64_t, int64_t>();
+ CheckArgsInferType<int32_t, double, double>();
+
+ // Invalid (u)int32_t-pairings do not compile.
+ CheckArgsInferType<uint32_t, int32_t, Invalid>();
+ CheckArgsInferType<int32_t, uint64_t, Invalid>();
+ CheckArgsInferType<int32_t, float, Invalid>();
+ CheckArgsInferType<uint32_t, float, Invalid>();
+
+ // Invalid (u)int64_t-pairings do not compile.
+ CheckArgsInferType<uint64_t, int64_t, Invalid>();
+ CheckArgsInferType<int64_t, float, Invalid>();
+ CheckArgsInferType<int64_t, double, Invalid>();
+
+ // Properly promotes float.
+ CheckArgsInferType<float, double, double>();
+}
+
+} // namespace