// 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 #include #include #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(IntervalOpenClosed, 0, 1.0), 0); EXPECT_GT(uniform_lower_bound(IntervalOpenOpen, 0, 1.0), 0); EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, 0, 1.0), 0); EXPECT_GT(uniform_lower_bound(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(IntervalClosedClosed, 0, 1.0), 0); EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, 0, 1.0), 0); EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, 0, 1.0), 0); EXPECT_EQ(uniform_lower_bound(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(IntervalOpenOpen, 0, 1.0), 1.0); EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, 0, 1.0), 1.0); EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, 0, 1.0), 1.0); EXPECT_EQ(uniform_upper_bound(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(IntervalOpenClosed, 0, 1.0), 1.0); EXPECT_GT(uniform_upper_bound(IntervalClosedClosed, 0, 1.0), 1.0); EXPECT_GT(uniform_upper_bound(IntervalOpenClosed, 0, 1.0), 1.0); EXPECT_GT(uniform_upper_bound(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(IntervalOpenClosed, -2.0, -1.0), -2.0); EXPECT_GT(uniform_lower_bound(IntervalOpenOpen, -2.0, -1.0), -2.0); EXPECT_GT(uniform_lower_bound(IntervalOpenClosed, -2.0, -1.0), -2.0); EXPECT_GT(uniform_lower_bound(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(IntervalClosedClosed, -2.0, -1.0), -2.0); EXPECT_EQ(uniform_lower_bound(IntervalClosedOpen, -2.0, -1.0), -2.0); EXPECT_EQ(uniform_lower_bound(IntervalClosedClosed, -2.0, -1.0), -2.0); EXPECT_EQ(uniform_lower_bound(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(IntervalOpenOpen, -2.0, -1.0), -1.0); EXPECT_EQ(uniform_upper_bound(IntervalClosedOpen, -2.0, -1.0), -1.0); EXPECT_EQ(uniform_upper_bound(IntervalOpenOpen, -2.0, -1.0), -1.0); EXPECT_EQ(uniform_upper_bound(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(IntervalOpenClosed, -2.0, -1.0), -1.0); EXPECT_GT(uniform_upper_bound(IntervalClosedClosed, -2.0, -1.0), -1.0); EXPECT_GT(uniform_upper_bound(IntervalOpenClosed, -2.0, -1.0), -1.0); EXPECT_GT(uniform_upper_bound(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::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::min)(); constexpr auto r = (std::numeric_limits::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::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::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 auto InferredUniformReturnT(int) -> uniform_inferred_return_t; template Invalid InferredUniformReturnT(...); // Given types , CheckArgsInferType() verifies that // // uniform_inferred_return_t and // uniform_inferred_return_t // // returns the type "Expect". // // This interface can also be used to assert that a given inferred return types // are invalid. Writing: // // CheckArgsInferType() // // will assert that this overload does not exist. template void CheckArgsInferType() { static_assert( absl::conjunction< std::is_same(0))>, std::is_same(0))>>::value, ""); } TEST_F(UniformHelperTest, UniformTypeInference) { // Infers common types. CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); // Properly promotes uint16_t. CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); // Properly promotes int16_t. CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); // Invalid (u)int16_t-pairings do not compile. // See "CheckArgsInferType" comments above, for how this is achieved. CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); // Properly promotes uint32_t. CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); // Properly promotes int32_t. CheckArgsInferType(); CheckArgsInferType(); // Invalid (u)int32_t-pairings do not compile. CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); // Invalid (u)int64_t-pairings do not compile. CheckArgsInferType(); CheckArgsInferType(); CheckArgsInferType(); // Properly promotes float. CheckArgsInferType(); } } // namespace