Commit e9324d92 by Abseil Team Committed by Gennadiy Rozental

Export of internal Abseil changes.

--
7a6ff16a85beb730c172d5d25cf1b5e1be885c56 by Laramie Leavitt <lar@google.com>:

Internal change.

PiperOrigin-RevId: 254454546

--
ff8f9bafaefc26d451f576ea4a06d150aed63f6f by Andy Soffer <asoffer@google.com>:

Internal changes

PiperOrigin-RevId: 254451562

--
deefc5b651b479ce36f0b4ef203e119c0c8936f2 by CJ Johnson <johnsoncj@google.com>:

Account for subtracting unsigned values from the size of InlinedVector

PiperOrigin-RevId: 254450625

--
3c677316a27bcadc17e41957c809ca472d5fef14 by Andy Soffer <asoffer@google.com>:

Add C++17's std::make_from_tuple to absl/utility/utility.h

PiperOrigin-RevId: 254411573

--
4ee3536a918830eeec402a28fc31a62c7c90b940 by CJ Johnson <johnsoncj@google.com>:

Adds benchmark for the rest of the InlinedVector public API

PiperOrigin-RevId: 254408378

--
e5a21a00700ee83498ff1efbf649169756463ee4 by CJ Johnson <johnsoncj@google.com>:

Updates the definition of InlinedVector::shrink_to_fit() to be exception safe and adds exception safety tests for it.

PiperOrigin-RevId: 254401387

--
2ea82e72b86d82d78b4e4712a63a55981b53c64b by Laramie Leavitt <lar@google.com>:

Use absl::InsecureBitGen in place of std::mt19937
in tests absl/random/...distribution_test.cc

PiperOrigin-RevId: 254289444

--
fa099e02c413a7ffda732415e8105cad26a90337 by Andy Soffer <asoffer@google.com>:

Internal changes

PiperOrigin-RevId: 254286334

--
ce34b7f36933b30cfa35b9c9a5697a792b5666e4 by Andy Soffer <asoffer@google.com>:

Internal changes

PiperOrigin-RevId: 254273059

--
6f9c473da7c2090c2e85a37c5f00622e8a912a89 by Jorg Brown <jorg@google.com>:

Change absl::container_internal::CompressedTuple to instantiate its
internal Storage class with the name of the type it's holding, rather
than the name of the Tuple.  This is not an externally-visible change,
other than less compiler memory is used and less debug information is
generated.

PiperOrigin-RevId: 254269285

--
8bd3c186bf2fc0c55d8a2dd6f28a5327502c9fba by Andy Soffer <asoffer@google.com>:

Adding short-hand IntervalClosed for IntervalClosedClosed and IntervalOpen for
IntervalOpenOpen.

PiperOrigin-RevId: 254252419

--
ea957f99b6a04fccd42aa05605605f3b44b1ecfd by Abseil Team <absl-team@google.com>:

Do not directly use __SIZEOF_INT128__.

In order to avoid linker errors when building with clang-cl (__fixunsdfti, __udivti3 and __fixunssfti are undefined), this CL uses ABSL_HAVE_INTRINSIC_INT128 which is not defined for clang-cl.

PiperOrigin-RevId: 254250739

--
89ab385cd26b34d64130bce856253aaba96d2345 by Andy Soffer <asoffer@google.com>:

Internal changes

PiperOrigin-RevId: 254242321

--
cffc793d93eca6d6bdf7de733847b6ab4a255ae9 by CJ Johnson <johnsoncj@google.com>:

Adds benchmark for InlinedVector::reserve(size_type)

PiperOrigin-RevId: 254199226

--
c90c7a9fa3c8f0c9d5114036979548b055ea2f2a by Gennadiy Rozental <rogeeff@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 254072387

--
c4c388beae016c9570ab54ffa1d52660e4a85b7b by Laramie Leavitt <lar@google.com>:

Internal cleanup.

PiperOrigin-RevId: 254062381

--
d3c992e221cc74e5372d0c8fa410170b6a43c062 by Tom Manshreck <shreck@google.com>:

Update distributions.h to Abseil standards

PiperOrigin-RevId: 254054946

--
d15ad0035c34ef11b14fadc5a4a2d3ec415f5518 by CJ Johnson <johnsoncj@google.com>:

Removes functions with only one caller from the implementation details of InlinedVector by manually inlining the definitions

PiperOrigin-RevId: 254005427

--
2f37e807efc3a8ef1f4b539bdd379917d4151520 by Andy Soffer <asoffer@google.com>:

Initial release of Abseil Random

PiperOrigin-RevId: 253999861

--
24ed1694b6430791d781ed533a8f8ccf6cac5856 by CJ Johnson <johnsoncj@google.com>:

Updates the definition of InlinedVector::assign(...)/InlinedVector::operator=(...) to new, exception-safe implementations with exception safety tests to boot

PiperOrigin-RevId: 253993691

--
5613d95f5a7e34a535cfaeadce801441e990843e by CJ Johnson <johnsoncj@google.com>:

Adds benchmarks for InlinedVector::shrink_to_fit()

PiperOrigin-RevId: 253989647

--
2a96ddfdac40bbb8cb6a7f1aeab90917067c6e63 by Abseil Team <absl-team@google.com>:

Initial release of Abseil Random

PiperOrigin-RevId: 253927497

--
bf1aff8fc9ffa921ad74643e9525ecf25b0d8dc1 by Andy Soffer <asoffer@google.com>:

Initial release of Abseil Random

PiperOrigin-RevId: 253920512

--
bfc03f4a3dcda3cf3a4b84bdb84cda24e3394f41 by Laramie Leavitt <lar@google.com>:

Internal change.

PiperOrigin-RevId: 253886486

--
05036cfcc078ca7c5f581a00dfb0daed568cbb69 by Eric Fiselier <ericwf@google.com>:

Don't include `winsock2.h` because it drags in `windows.h` and friends,
and they define awful macros like OPAQUE, ERROR, and more. This has the
potential to break abseil users.

Instead we only forward declare `timeval` and require Windows users
include `winsock2.h` themselves. This is both inconsistent and poor QoI, but so
including 'windows.h' is bad too.

PiperOrigin-RevId: 253852615
GitOrigin-RevId: 7a6ff16a85beb730c172d5d25cf1b5e1be885c56
Change-Id: Icd6aff87da26f29ec8915da856f051129987cef6
parent 43ef2148
......@@ -127,6 +127,7 @@ cc_library(
"//absl/base:core_headers",
"//absl/memory",
"//absl/meta:type_traits",
"//absl/types:span",
],
)
......
......@@ -126,6 +126,7 @@ absl_cc_library(
absl::compressed_tuple
absl::core_headers
absl::memory
absl::span
absl::type_traits
PUBLIC
)
......
......@@ -599,6 +599,146 @@ void BM_AssignFromMove(benchmark::State& state) {
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_AssignFromMove, NontrivialType);
template <typename T, size_t FromSize, size_t ToSize>
void BM_ResizeSize(benchmark::State& state) {
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[](InlVec<T>* vec, size_t) { vec->resize(ToSize); });
}
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSize, NontrivialType);
template <typename T, size_t FromSize, size_t ToSize>
void BM_ResizeSizeRef(benchmark::State& state) {
auto t = T();
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[&](InlVec<T>* vec, size_t) {
benchmark::DoNotOptimize(t);
vec->resize(ToSize, t);
});
}
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ResizeSizeRef, NontrivialType);
template <typename T, size_t FromSize, size_t ToSize>
void BM_InsertSizeRef(benchmark::State& state) {
auto t = T();
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[&](InlVec<T>* vec, size_t) {
benchmark::DoNotOptimize(t);
auto* pos = vec->data() + (vec->size() / 2);
vec->insert(pos, t);
});
}
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertSizeRef, NontrivialType);
template <typename T, size_t FromSize, size_t ToSize>
void BM_InsertRange(benchmark::State& state) {
InlVec<T> other_vec(ToSize);
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[&](InlVec<T>* vec, size_t) {
benchmark::DoNotOptimize(other_vec);
auto* pos = vec->data() + (vec->size() / 2);
vec->insert(pos, other_vec.begin(), other_vec.end());
});
}
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_InsertRange, NontrivialType);
template <typename T, size_t FromSize>
void BM_EmplaceBack(benchmark::State& state) {
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[](InlVec<T>* vec, size_t) { vec->emplace_back(); });
}
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EmplaceBack, NontrivialType);
template <typename T, size_t FromSize>
void BM_PopBack(benchmark::State& state) {
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[](InlVec<T>* vec, size_t) { vec->pop_back(); });
}
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_PopBack, NontrivialType);
template <typename T, size_t FromSize>
void BM_EraseOne(benchmark::State& state) {
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[](InlVec<T>* vec, size_t) {
auto* pos = vec->data() + (vec->size() / 2);
vec->erase(pos);
});
}
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseOne, NontrivialType);
template <typename T, size_t FromSize>
void BM_EraseRange(benchmark::State& state) {
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[](InlVec<T>* vec, size_t) {
auto* pos = vec->data() + (vec->size() / 2);
vec->erase(pos, pos + 1);
});
}
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_EraseRange, NontrivialType);
template <typename T, size_t FromSize>
void BM_Clear(benchmark::State& state) {
BatchedBenchmark<T>(
......@@ -609,4 +749,56 @@ void BM_Clear(benchmark::State& state) {
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, TrivialType);
ABSL_INTERNAL_BENCHMARK_ONE_SIZE(BM_Clear, NontrivialType);
template <typename T, size_t FromSize, size_t ToCapacity>
void BM_Reserve(benchmark::State& state) {
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(FromSize);
},
/* test_vec = */
[](InlVec<T>* vec, size_t) { vec->reserve(ToCapacity); });
}
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Reserve, NontrivialType);
template <typename T, size_t FromCapacity, size_t ToCapacity>
void BM_ShrinkToFit(benchmark::State& state) {
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[](InlVec<T>* vec, size_t) {
vec->clear();
vec->resize(ToCapacity);
vec->reserve(FromCapacity);
},
/* test_vec = */ [](InlVec<T>* vec, size_t) { vec->shrink_to_fit(); });
}
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_ShrinkToFit, NontrivialType);
template <typename T, size_t FromSize, size_t ToSize>
void BM_Swap(benchmark::State& state) {
using VecT = InlVec<T>;
std::array<VecT, kBatchSize> vector_batch{};
BatchedBenchmark<T>(
state,
/* prepare_vec = */
[&](InlVec<T>* vec, size_t i) {
vector_batch[i].clear();
vector_batch[i].resize(ToSize);
vec->resize(FromSize);
},
/* test_vec = */
[&](InlVec<T>* vec, size_t i) {
using std::swap;
benchmark::DoNotOptimize(vector_batch[i]);
swap(*vec, vector_batch[i]);
});
}
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, TrivialType);
ABSL_INTERNAL_BENCHMARK_TWO_SIZE(BM_Swap, NontrivialType);
} // namespace
......@@ -12,7 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <array>
#include <initializer_list>
#include <iterator>
#include <memory>
#include <utility>
#include "gtest/gtest.h"
#include "absl/base/internal/exception_safety_testing.h"
......@@ -81,6 +85,24 @@ using OneSizeTestParams =
TestParams<ThrowAllocMovableThrowerVec, kLargeSize>,
TestParams<ThrowAllocMovableThrowerVec, kSmallSize>>;
using TwoSizeTestParams = ::testing::Types<
TestParams<ThrowerVec, kLargeSize, kLargeSize>,
TestParams<ThrowerVec, kLargeSize, kSmallSize>,
TestParams<ThrowerVec, kSmallSize, kLargeSize>,
TestParams<ThrowerVec, kSmallSize, kSmallSize>,
TestParams<MovableThrowerVec, kLargeSize, kLargeSize>,
TestParams<MovableThrowerVec, kLargeSize, kSmallSize>,
TestParams<MovableThrowerVec, kSmallSize, kLargeSize>,
TestParams<MovableThrowerVec, kSmallSize, kSmallSize>,
TestParams<ThrowAllocThrowerVec, kLargeSize, kLargeSize>,
TestParams<ThrowAllocThrowerVec, kLargeSize, kSmallSize>,
TestParams<ThrowAllocThrowerVec, kSmallSize, kLargeSize>,
TestParams<ThrowAllocThrowerVec, kSmallSize, kSmallSize>,
TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kLargeSize>,
TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kSmallSize>,
TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kLargeSize>,
TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kSmallSize>>;
template <typename>
struct NoSizeTest : ::testing::Test {};
TYPED_TEST_SUITE(NoSizeTest, NoSizeTestParams);
......@@ -89,6 +111,25 @@ template <typename>
struct OneSizeTest : ::testing::Test {};
TYPED_TEST_SUITE(OneSizeTest, OneSizeTestParams);
template <typename>
struct TwoSizeTest : ::testing::Test {};
TYPED_TEST_SUITE(TwoSizeTest, TwoSizeTestParams);
template <typename VecT>
bool InlinedVectorInvariants(VecT* vec) {
if (*vec != *vec) return false;
if (vec->size() > vec->capacity()) return false;
if (vec->size() > vec->max_size()) return false;
if (vec->capacity() > vec->max_size()) return false;
if (vec->data() != std::addressof(vec->at(0))) return false;
if (vec->data() != vec->begin()) return false;
if (*vec->data() != *vec->begin()) return false;
if (vec->begin() > vec->end()) return false;
if ((vec->end() - vec->begin()) != vec->size()) return false;
if (std::distance(vec->begin(), vec->end()) != vec->size()) return false;
return true;
}
// Function that always returns false is correct, but refactoring is required
// for clarity. It's needed to express that, as a contract, certain operations
// should not throw at all. Execution of this function means an exception was
......@@ -179,6 +220,45 @@ TYPED_TEST(OneSizeTest, MoveConstructor) {
}
}
TYPED_TEST(TwoSizeTest, Assign) {
using VecT = typename TypeParam::VecT;
using value_type = typename VecT::value_type;
constexpr static auto from_size = TypeParam::GetSizeAt(0);
constexpr static auto to_size = TypeParam::GetSizeAt(1);
auto tester = testing::MakeExceptionSafetyTester()
.WithInitialValue(VecT{from_size})
.WithContracts(InlinedVectorInvariants<VecT>);
EXPECT_TRUE(tester.Test([](VecT* vec) {
*vec = ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size);
}));
EXPECT_TRUE(tester.Test([](VecT* vec) {
VecT other_vec{to_size};
*vec = other_vec;
}));
EXPECT_TRUE(tester.Test([](VecT* vec) {
VecT other_vec{to_size};
*vec = std::move(other_vec);
}));
EXPECT_TRUE(tester.Test([](VecT* vec) {
value_type val{};
vec->assign(to_size, val);
}));
EXPECT_TRUE(tester.Test([](VecT* vec) {
vec->assign(ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size));
}));
EXPECT_TRUE(tester.Test([](VecT* vec) {
std::array<value_type, to_size> arr{};
vec->assign(arr.begin(), arr.end());
}));
}
TYPED_TEST(OneSizeTest, PopBack) {
using VecT = typename TypeParam::VecT;
constexpr static auto size = TypeParam::GetSizeAt(0);
......@@ -205,4 +285,17 @@ TYPED_TEST(OneSizeTest, Clear) {
}));
}
TYPED_TEST(OneSizeTest, ShrinkToFit) {
using VecT = typename TypeParam::VecT;
constexpr static auto size = TypeParam::GetSizeAt(0);
auto tester = testing::MakeExceptionSafetyTester()
.WithInitialValue(VecT{size})
.WithContracts(InlinedVectorInvariants<VecT>);
EXPECT_TRUE(tester.Test([](VecT* vec) {
vec->shrink_to_fit(); //
}));
}
} // namespace
......@@ -190,6 +190,12 @@ TEST(IntVec, SimpleOps) {
}
}
TEST(IntVec, PopBackNoOverflow) {
IntVec v = {1};
v.pop_back();
EXPECT_EQ(v.size(), 0);
}
TEST(IntVec, AtThrows) {
IntVec v = {1, 2, 3};
EXPECT_EQ(v.at(2), 3);
......
......@@ -32,6 +32,7 @@
#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
#include <initializer_list>
#include <tuple>
#include <type_traits>
#include <utility>
......@@ -75,17 +76,30 @@ constexpr bool IsFinal() {
#endif
}
// We can't use EBCO on other CompressedTuples because that would mean that we
// derive from multiple Storage<> instantiations with the same I parameter,
// and potentially from multiple identical Storage<> instantiations. So anytime
// we use type inheritance rather than encapsulation, we mark
// CompressedTupleImpl, to make this easy to detect.
struct uses_inheritance {};
template <typename T>
constexpr bool ShouldUseBase() {
return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>();
return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>() &&
!std::is_base_of<uses_inheritance, T>::value;
}
// The storage class provides two specializations:
// - For empty classes, it stores T as a base class.
// - For everything else, it stores T as a member.
template <typename D, size_t I, bool = ShouldUseBase<ElemT<D, I>>()>
template <typename T, size_t I,
#if defined(_MSC_VER)
bool UseBase =
ShouldUseBase<typename std::enable_if<true, T>::type>()>
#else
bool UseBase = ShouldUseBase<T>()>
#endif
struct Storage {
using T = ElemT<D, I>;
T value;
constexpr Storage() = default;
explicit constexpr Storage(T&& v) : value(absl::forward<T>(v)) {}
......@@ -95,10 +109,8 @@ struct Storage {
T&& get() && { return std::move(*this).value; }
};
template <typename D, size_t I>
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<D, I, true>
: ElemT<D, I> {
using T = internal_compressed_tuple::ElemT<D, I>;
template <typename T, size_t I>
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T {
constexpr Storage() = default;
explicit constexpr Storage(T&& v) : T(absl::forward<T>(v)) {}
constexpr const T& get() const& { return *this; }
......@@ -107,29 +119,54 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<D, I, true>
T&& get() && { return std::move(*this); }
};
template <typename D, typename I>
template <typename D, typename I, bool ShouldAnyUseBase>
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl;
template <typename... Ts, size_t... I>
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
CompressedTupleImpl<CompressedTuple<Ts...>, absl::index_sequence<I...>>
template <typename... Ts, size_t... I, bool ShouldAnyUseBase>
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
CompressedTuple<Ts...>, absl::index_sequence<I...>, ShouldAnyUseBase>
// We use the dummy identity function through std::integral_constant to
// convince MSVC of accepting and expanding I in that context. Without it
// you would get:
// error C3548: 'I': parameter pack cannot be used in this context
: Storage<CompressedTuple<Ts...>,
std::integral_constant<size_t, I>::value>... {
: uses_inheritance,
Storage<Ts, std::integral_constant<size_t, I>::value>... {
constexpr CompressedTupleImpl() = default;
explicit constexpr CompressedTupleImpl(Ts&&... args)
: Storage<Ts, I>(absl::forward<Ts>(args))... {}
friend CompressedTuple<Ts...>;
};
template <typename... Ts, size_t... I>
struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
CompressedTuple<Ts...>, absl::index_sequence<I...>, false>
// We use the dummy identity function as above...
: Storage<Ts, std::integral_constant<size_t, I>::value, false>... {
constexpr CompressedTupleImpl() = default;
explicit constexpr CompressedTupleImpl(Ts&&... args)
: Storage<CompressedTuple<Ts...>, I>(absl::forward<Ts>(args))... {}
: Storage<Ts, I, false>(absl::forward<Ts>(args))... {}
friend CompressedTuple<Ts...>;
};
std::false_type Or(std::initializer_list<std::false_type>);
std::true_type Or(std::initializer_list<bool>);
// MSVC requires this to be done separately rather than within the declaration
// of CompressedTuple below.
template <typename... Ts>
constexpr bool ShouldAnyUseBase() {
return decltype(
Or({std::integral_constant<bool, ShouldUseBase<Ts>()>()...})){};
}
} // namespace internal_compressed_tuple
// Helper class to perform the Empty Base Class Optimization.
// Ts can contain classes and non-classes, empty or not. For the ones that
// are empty classes, we perform the CompressedTuple. If all types in Ts are
// empty classes, then CompressedTuple<Ts...> is itself an empty class.
// empty classes, then CompressedTuple<Ts...> is itself an empty class. (This
// does not apply when one or more of those empty classes is itself an empty
// CompressedTuple.)
//
// To access the members, use member .get<N>() function.
//
......@@ -145,7 +182,8 @@ struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
template <typename... Ts>
class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
: private internal_compressed_tuple::CompressedTupleImpl<
CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>> {
CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>,
internal_compressed_tuple::ShouldAnyUseBase<Ts...>()> {
private:
template <int I>
using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>;
......@@ -157,24 +195,24 @@ class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
template <int I>
ElemT<I>& get() & {
return internal_compressed_tuple::Storage<CompressedTuple, I>::get();
return internal_compressed_tuple::Storage<ElemT<I>, I>::get();
}
template <int I>
constexpr const ElemT<I>& get() const& {
return internal_compressed_tuple::Storage<CompressedTuple, I>::get();
return internal_compressed_tuple::Storage<ElemT<I>, I>::get();
}
template <int I>
ElemT<I>&& get() && {
return std::move(*this)
.internal_compressed_tuple::template Storage<CompressedTuple, I>::get();
.internal_compressed_tuple::template Storage<ElemT<I>, I>::get();
}
template <int I>
constexpr const ElemT<I>&& get() const&& {
return absl::move(*this)
.internal_compressed_tuple::template Storage<CompressedTuple, I>::get();
.internal_compressed_tuple::template Storage<ElemT<I>, I>::get();
}
};
......
......@@ -22,10 +22,8 @@
#include "absl/memory/memory.h"
#include "absl/utility/utility.h"
namespace absl {
namespace container_internal {
namespace {
// These are declared at global scope purely so that error messages
// are smaller and easier to understand.
enum class CallType { kConstRef, kConstMove };
template <int>
......@@ -45,6 +43,10 @@ struct TwoValues {
U value2;
};
namespace absl {
namespace container_internal {
namespace {
TEST(CompressedTupleTest, Sizeof) {
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
......@@ -120,9 +122,14 @@ TEST(CompressedTupleTest, Nested) {
EXPECT_EQ(4 * sizeof(char),
sizeof(CompressedTuple<CompressedTuple<char, char>,
CompressedTuple<char, char>>));
EXPECT_TRUE(
(std::is_empty<CompressedTuple<CompressedTuple<Empty<0>>,
CompressedTuple<Empty<1>>>>::value));
EXPECT_TRUE((std::is_empty<CompressedTuple<Empty<0>, Empty<1>>>::value));
// Make sure everything still works when things are nested.
struct CT_Empty : CompressedTuple<Empty<0>> {};
CompressedTuple<Empty<0>, CT_Empty> nested_empty;
auto contained = nested_empty.get<0>();
auto nested = nested_empty.get<1>().get<0>();
EXPECT_TRUE((std::is_same<decltype(contained), decltype(nested)>::value));
}
TEST(CompressedTupleTest, Reference) {
......
......@@ -25,6 +25,7 @@
#include "absl/container/internal/compressed_tuple.h"
#include "absl/memory/memory.h"
#include "absl/meta/type_traits.h"
#include "absl/types/span.h"
namespace absl {
namespace inlined_vector_internal {
......@@ -78,6 +79,14 @@ void ConstructElements(AllocatorType* alloc_ptr, ValueType* construct_first,
}
}
template <typename ValueType, typename ValueAdapter, typename SizeType>
void AssignElements(ValueType* assign_first, ValueAdapter* values_ptr,
SizeType assign_size) {
for (SizeType i = 0; i < assign_size; ++i) {
values_ptr->AssignNext(assign_first + i);
}
}
template <typename AllocatorType>
struct StorageView {
using pointer = typename AllocatorType::pointer;
......@@ -101,6 +110,11 @@ class IteratorValueAdapter {
++it_;
}
void AssignNext(pointer assign_at) {
*assign_at = *it_;
++it_;
}
private:
Iterator it_;
};
......@@ -119,6 +133,8 @@ class CopyValueAdapter {
AllocatorTraits::construct(*alloc_ptr, construct_at, *ptr_);
}
void AssignNext(pointer assign_at) { *assign_at = *ptr_; }
private:
const_pointer ptr_;
};
......@@ -135,6 +151,44 @@ class DefaultValueAdapter {
void ConstructNext(AllocatorType* alloc_ptr, pointer construct_at) {
AllocatorTraits::construct(*alloc_ptr, construct_at);
}
void AssignNext(pointer assign_at) { *assign_at = value_type(); }
};
template <typename AllocatorType>
class AllocationTransaction {
using value_type = typename AllocatorType::value_type;
using pointer = typename AllocatorType::pointer;
using size_type = typename AllocatorType::size_type;
using AllocatorTraits = absl::allocator_traits<AllocatorType>;
public:
explicit AllocationTransaction(AllocatorType* alloc_ptr)
: alloc_data_(*alloc_ptr, nullptr) {}
AllocationTransaction(const AllocationTransaction&) = delete;
void operator=(const AllocationTransaction&) = delete;
AllocatorType& GetAllocator() { return alloc_data_.template get<0>(); }
pointer& GetData() { return alloc_data_.template get<1>(); }
size_type& GetCapacity() { return capacity_; }
bool DidAllocate() { return GetData() != nullptr; }
pointer Allocate(size_type capacity) {
GetData() = AllocatorTraits::allocate(GetAllocator(), capacity);
GetCapacity() = capacity;
return GetData();
}
~AllocationTransaction() {
if (DidAllocate()) {
AllocatorTraits::deallocate(GetAllocator(), GetData(), GetCapacity());
}
}
private:
container_internal::CompressedTuple<AllocatorType, pointer> alloc_data_;
size_type capacity_ = 0;
};
template <typename T, size_t N, typename A>
......@@ -167,6 +221,9 @@ class Storage {
using DefaultValueAdapter =
inlined_vector_internal::DefaultValueAdapter<allocator_type>;
using AllocationTransaction =
inlined_vector_internal::AllocationTransaction<allocator_type>;
Storage() : metadata_() {}
explicit Storage(const allocator_type& alloc)
......@@ -215,19 +272,48 @@ class Storage {
void SetIsAllocated() { GetSizeAndIsAllocated() |= 1; }
void UnsetIsAllocated() {
SetIsAllocated();
GetSizeAndIsAllocated() -= 1;
}
void SetAllocatedSize(size_type size) {
GetSizeAndIsAllocated() = (size << 1) | static_cast<size_type>(1);
}
void SetInlinedSize(size_type size) { GetSizeAndIsAllocated() = size << 1; }
void SetSize(size_type size) {
GetSizeAndIsAllocated() =
(size << 1) | static_cast<size_type>(GetIsAllocated());
}
void AddSize(size_type count) { GetSizeAndIsAllocated() += count << 1; }
void SubtractSize(size_type count) {
assert(count <= GetSize());
GetSizeAndIsAllocated() -= count << 1;
}
void SetAllocatedData(pointer data, size_type capacity) {
data_.allocated.allocated_data = data;
data_.allocated.allocated_capacity = capacity;
}
void DeallocateIfAllocated() {
if (GetIsAllocated()) {
AllocatorTraits::deallocate(*GetAllocPtr(), GetAllocatedData(),
GetAllocatedCapacity());
}
}
void AcquireAllocation(AllocationTransaction* allocation_tx_ptr) {
SetAllocatedData(allocation_tx_ptr->GetData(),
allocation_tx_ptr->GetCapacity());
allocation_tx_ptr->GetData() = nullptr;
allocation_tx_ptr->GetCapacity() = 0;
}
void SwapSizeAndIsAllocated(Storage* other) {
using std::swap;
swap(GetSizeAndIsAllocated(), other->GetSizeAndIsAllocated());
......@@ -238,11 +324,11 @@ class Storage {
swap(data_.allocated, other->data_.allocated);
}
void MemcpyContents(const Storage& other) {
assert(IsMemcpyOk::value);
void MemcpyFrom(const Storage& other_storage) {
assert(IsMemcpyOk::value || other_storage.GetIsAllocated());
GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated();
data_ = other.data_;
GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated();
data_ = other_storage.data_;
}
void DestroyAndDeallocate();
......@@ -250,6 +336,11 @@ class Storage {
template <typename ValueAdapter>
void Initialize(ValueAdapter values, size_type new_size);
template <typename ValueAdapter>
void Assign(ValueAdapter values, size_type new_size);
void ShrinkToFit();
private:
size_type& GetSizeAndIsAllocated() { return metadata_.template get<1>(); }
......@@ -282,15 +373,10 @@ class Storage {
template <typename T, size_t N, typename A>
void Storage<T, N, A>::DestroyAndDeallocate() {
StorageView storage_view = MakeStorageView();
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
storage_view.size);
if (GetIsAllocated()) {
AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data,
storage_view.capacity);
}
inlined_vector_internal::DestroyElements(
GetAllocPtr(), (GetIsAllocated() ? GetAllocatedData() : GetInlinedData()),
GetSize());
DeallocateIfAllocated();
}
template <typename T, size_t N, typename A>
......@@ -323,6 +409,91 @@ auto Storage<T, N, A>::Initialize(ValueAdapter values, size_type new_size)
AddSize(new_size);
}
template <typename T, size_t N, typename A>
template <typename ValueAdapter>
auto Storage<T, N, A>::Assign(ValueAdapter values, size_type new_size) -> void {
StorageView storage_view = MakeStorageView();
AllocationTransaction allocation_tx(GetAllocPtr());
absl::Span<value_type> assign_loop;
absl::Span<value_type> construct_loop;
absl::Span<value_type> destroy_loop;
if (new_size > storage_view.capacity) {
construct_loop = {allocation_tx.Allocate(new_size), new_size};
destroy_loop = {storage_view.data, storage_view.size};
} else if (new_size > storage_view.size) {
assign_loop = {storage_view.data, storage_view.size};
construct_loop = {storage_view.data + storage_view.size,
new_size - storage_view.size};
} else {
assign_loop = {storage_view.data, new_size};
destroy_loop = {storage_view.data + new_size, storage_view.size - new_size};
}
inlined_vector_internal::AssignElements(assign_loop.data(), &values,
assign_loop.size());
inlined_vector_internal::ConstructElements(
GetAllocPtr(), construct_loop.data(), &values, construct_loop.size());
inlined_vector_internal::DestroyElements(GetAllocPtr(), destroy_loop.data(),
destroy_loop.size());
if (allocation_tx.DidAllocate()) {
DeallocateIfAllocated();
AcquireAllocation(&allocation_tx);
SetIsAllocated();
}
SetSize(new_size);
}
template <typename T, size_t N, typename A>
auto Storage<T, N, A>::ShrinkToFit() -> void {
// May only be called on allocated instances!
assert(GetIsAllocated());
StorageView storage_view = {GetAllocatedData(), GetSize(),
GetAllocatedCapacity()};
AllocationTransaction allocation_tx(GetAllocPtr());
IteratorValueAdapter<MoveIterator> move_values(
MoveIterator(storage_view.data));
pointer construct_data;
if (storage_view.size <= static_cast<size_type>(N)) {
construct_data = GetInlinedData();
} else if (storage_view.size < GetAllocatedCapacity()) {
construct_data = allocation_tx.Allocate(storage_view.size);
} else {
return;
}
ABSL_INTERNAL_TRY {
inlined_vector_internal::ConstructElements(GetAllocPtr(), construct_data,
&move_values, storage_view.size);
}
ABSL_INTERNAL_CATCH_ANY {
// Writing to inlined data will trample on the existing state, thus it needs
// to be restored when a construction fails.
SetAllocatedData(storage_view.data, storage_view.capacity);
ABSL_INTERNAL_RETHROW;
}
inlined_vector_internal::DestroyElements(GetAllocPtr(), storage_view.data,
storage_view.size);
AllocatorTraits::deallocate(*GetAllocPtr(), storage_view.data,
storage_view.capacity);
if (allocation_tx.DidAllocate()) {
AcquireAllocation(&allocation_tx);
} else {
UnsetIsAllocated();
}
}
} // namespace inlined_vector_internal
} // namespace absl
......
......@@ -218,3 +218,21 @@ list(APPEND ABSL_MSVC_TEST_FLAGS
"/wd4996"
"/DNOMINMAX"
)
list(APPEND ABSL_RANDOM_HWAES_ARM32_FLAGS
"-mfpu=neon"
)
list(APPEND ABSL_RANDOM_HWAES_ARM64_FLAGS
"-march=armv8-a+crypto"
)
list(APPEND ABSL_RANDOM_HWAES_MSVC_X64_FLAGS
"/O2"
"/Ob2"
)
list(APPEND ABSL_RANDOM_HWAES_X64_FLAGS
"-maes"
"-msse4.1"
)
......@@ -219,3 +219,21 @@ ABSL_MSVC_TEST_FLAGS = [
"/wd4996",
"/DNOMINMAX",
]
ABSL_RANDOM_HWAES_ARM32_FLAGS = [
"-mfpu=neon",
]
ABSL_RANDOM_HWAES_ARM64_FLAGS = [
"-march=armv8-a+crypto",
]
ABSL_RANDOM_HWAES_MSVC_X64_FLAGS = [
"/O2",
"/Ob2",
]
ABSL_RANDOM_HWAES_X64_FLAGS = [
"-maes",
"-msse4.1",
]
......@@ -16,6 +16,10 @@ load(
"ABSL_MSVC_FLAGS",
"ABSL_MSVC_LINKOPTS",
"ABSL_MSVC_TEST_FLAGS",
"ABSL_RANDOM_HWAES_ARM32_FLAGS",
"ABSL_RANDOM_HWAES_ARM64_FLAGS",
"ABSL_RANDOM_HWAES_MSVC_X64_FLAGS",
"ABSL_RANDOM_HWAES_X64_FLAGS",
)
ABSL_DEFAULT_COPTS = select({
......@@ -46,3 +50,40 @@ ABSL_DEFAULT_LINKOPTS = select({
"//absl:windows": ABSL_MSVC_LINKOPTS,
"//conditions:default": [],
})
# ABSL_RANDOM_RANDEN_COPTS blaze copts flags which are required by each
# environment to build an accelerated RandenHwAes library.
ABSL_RANDOM_RANDEN_COPTS = select({
# APPLE
":cpu_darwin_x86_64": ABSL_RANDOM_HWAES_X64_FLAGS,
":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS,
":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
":cpu_haswell": ABSL_RANDOM_HWAES_X64_FLAGS,
":cpu_ppc": ["-mcrypto"],
# Supported by default or unsupported.
"//conditions:default": [],
})
# absl_random_randen_copts_init:
# Initialize the config targets based on cpu, os, etc. used to select
# the required values for ABSL_RANDOM_RANDEN_COPTS
def absl_random_randen_copts_init():
"""Initialize the config_settings used by ABSL_RANDOM_RANDEN_COPTS."""
# CPU configs.
# These configs have consistent flags to enable HWAES intsructions.
cpu_configs = [
"ppc",
"haswell",
"darwin_x86_64",
"darwin",
"x64_windows_msvc",
"x64_windows",
]
for cpu in cpu_configs:
native.config_setting(
name = "cpu_%s" % cpu,
values = {"cpu": cpu},
)
......@@ -199,4 +199,18 @@ COPT_VARS = {
# Object file doesn't export any previously undefined symbols
"-ignore:4221",
],
# "HWAES" is an abbreviation for "hardware AES" (AES - Advanced Encryption
# Standard). These flags are used for detecting whether or not the target
# architecture has hardware support for AES instructions which can be used
# to improve performance of some random bit generators.
"ABSL_RANDOM_HWAES_ARM64_FLAGS": ["-march=armv8-a+crypto"],
"ABSL_RANDOM_HWAES_ARM32_FLAGS": ["-mfpu=neon"],
"ABSL_RANDOM_HWAES_X64_FLAGS": [
"-maes",
"-msse4.1",
],
"ABSL_RANDOM_HWAES_MSVC_X64_FLAGS": [
"/O2", # Maximize speed
"/Ob2", # Aggressive inlining
],
}
// 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.
#ifndef ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_
#define ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_
#include <cstdint>
#include <istream>
#include <limits>
#include "absl/base/optimization.h"
#include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
// absl::bernoulli_distribution is a drop in replacement for
// std::bernoulli_distribution. It guarantees that (given a perfect
// UniformRandomBitGenerator) the acceptance probability is *exactly* equal to
// the given double.
//
// The implementation assumes that double is IEEE754
class bernoulli_distribution {
public:
using result_type = bool;
class param_type {
public:
using distribution_type = bernoulli_distribution;
explicit param_type(double p = 0.5) : prob_(p) {
assert(p >= 0.0 && p <= 1.0);
}
double p() const { return prob_; }
friend bool operator==(const param_type& p1, const param_type& p2) {
return p1.p() == p2.p();
}
friend bool operator!=(const param_type& p1, const param_type& p2) {
return p1.p() != p2.p();
}
private:
double prob_;
};
bernoulli_distribution() : bernoulli_distribution(0.5) {}
explicit bernoulli_distribution(double p) : param_(p) {}
explicit bernoulli_distribution(param_type p) : param_(p) {}
// no-op
void reset() {}
template <typename URBG>
bool operator()(URBG& g) { // NOLINT(runtime/references)
return Generate(param_.p(), g);
}
template <typename URBG>
bool operator()(URBG& g, // NOLINT(runtime/references)
const param_type& param) {
return Generate(param.p(), g);
}
param_type param() const { return param_; }
void param(const param_type& param) { param_ = param; }
double p() const { return param_.p(); }
result_type(min)() const { return false; }
result_type(max)() const { return true; }
friend bool operator==(const bernoulli_distribution& d1,
const bernoulli_distribution& d2) {
return d1.param_ == d2.param_;
}
friend bool operator!=(const bernoulli_distribution& d1,
const bernoulli_distribution& d2) {
return d1.param_ != d2.param_;
}
private:
static constexpr uint64_t kP32 = static_cast<uint64_t>(1) << 32;
template <typename URBG>
static bool Generate(double p, URBG& g); // NOLINT(runtime/references)
param_type param_;
};
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
const bernoulli_distribution& x) {
auto saver = random_internal::make_ostream_state_saver(os);
os.precision(random_internal::stream_precision_helper<double>::kPrecision);
os << x.p();
return os;
}
template <typename CharT, typename Traits>
std::basic_istream<CharT, Traits>& operator>>(
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
bernoulli_distribution& x) { // NOLINT(runtime/references)
auto saver = random_internal::make_istream_state_saver(is);
auto p = random_internal::read_floating_point<double>(is);
if (!is.fail()) {
x.param(bernoulli_distribution::param_type(p));
}
return is;
}
template <typename URBG>
bool bernoulli_distribution::Generate(double p,
URBG& g) { // NOLINT(runtime/references)
random_internal::FastUniformBits<uint32_t> fast_u32;
while (true) {
// There are two aspects of the definition of `c` below that are worth
// commenting on. First, because `p` is in the range [0, 1], `c` is in the
// range [0, 2^32] which does not fit in a uint32_t and therefore requires
// 64 bits.
//
// Second, `c` is constructed by first casting explicitly to a signed
// integer and then converting implicitly to an unsigned integer of the same
// size. This is done because the hardware conversion instructions produce
// signed integers from double; if taken as a uint64_t the conversion would
// be wrong for doubles greater than 2^63 (not relevant in this use-case).
// If converted directly to an unsigned integer, the compiler would end up
// emitting code to handle such large values that are not relevant due to
// the known bounds on `c`. To avoid these extra instructions this
// implementation converts first to the signed type and then use the
// implicit conversion to unsigned (which is a no-op).
const uint64_t c = static_cast<int64_t>(p * kP32);
const uint32_t v = fast_u32(g);
// FAST PATH: this path fails with probability 1/2^32. Note that simply
// returning v <= c would approximate P very well (up to an absolute error
// of 1/2^32); the slow path (taken in that range of possible error, in the
// case of equality) eliminates the remaining error.
if (ABSL_PREDICT_TRUE(v != c)) return v < c;
// It is guaranteed that `q` is strictly less than 1, because if `q` were
// greater than or equal to 1, the same would be true for `p`. Certainly `p`
// cannot be greater than 1, and if `p == 1`, then the fast path would
// necessary have been taken already.
const double q = static_cast<double>(c) / kP32;
// The probability of acceptance on the fast path is `q` and so the
// probability of acceptance here should be `p - q`.
//
// Note that `q` is obtained from `p` via some shifts and conversions, the
// upshot of which is that `q` is simply `p` with some of the
// least-significant bits of its mantissa set to zero. This means that the
// difference `p - q` will not have any rounding errors. To see why, pretend
// that double has 10 bits of resolution and q is obtained from `p` in such
// a way that the 4 least-significant bits of its mantissa are set to zero.
// For example:
// p = 1.1100111011 * 2^-1
// q = 1.1100110000 * 2^-1
// p - q = 1.011 * 2^-8
// The difference `p - q` has exactly the nonzero mantissa bits that were
// "lost" in `q` producing a number which is certainly representable in a
// double.
const double left = p - q;
// By construction, the probability of being on this slow path is 1/2^32, so
// P(accept in slow path) = P(accept| in slow path) * P(slow path),
// which means the probability of acceptance here is `1 / (left * kP32)`:
const double here = left * kP32;
// The simplest way to compute the result of this trial is to repeat the
// whole algorithm with the new probability. This terminates because even
// given arbitrarily unfriendly "random" bits, each iteration either
// multiplies a tiny probability by 2^32 (if c == 0) or strips off some
// number of nonzero mantissa bits. That process is bounded.
if (here == 0) return false;
p = here;
}
}
} // namespace absl
#endif // ABSL_RANDOM_BERNOULLI_DISTRIBUTION_H_
// 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/bernoulli_distribution.h"
#include <cmath>
#include <cstddef>
#include <random>
#include <sstream>
#include <utility>
#include "gtest/gtest.h"
#include "absl/random/internal/sequence_urbg.h"
#include "absl/random/random.h"
namespace {
class BernoulliTest : public testing::TestWithParam<std::pair<double, size_t>> {
};
TEST_P(BernoulliTest, Serialize) {
const double d = GetParam().first;
absl::bernoulli_distribution before(d);
{
absl::bernoulli_distribution via_param{
absl::bernoulli_distribution::param_type(d)};
EXPECT_EQ(via_param, before);
}
std::stringstream ss;
ss << before;
absl::bernoulli_distribution after(0.6789);
EXPECT_NE(before.p(), after.p());
EXPECT_NE(before.param(), after.param());
EXPECT_NE(before, after);
ss >> after;
EXPECT_EQ(before.p(), after.p());
EXPECT_EQ(before.param(), after.param());
EXPECT_EQ(before, after);
}
TEST_P(BernoulliTest, Accuracy) {
// Sadly, the claim to fame for this implementation is precise accuracy, which
// is very, very hard to measure, the improvements come as trials approach the
// limit of double accuracy; thus the outcome differs from the
// std::bernoulli_distribution with a probability of approximately 1 in 2^-53.
const std::pair<double, size_t> para = GetParam();
size_t trials = para.second;
double p = para.first;
absl::InsecureBitGen rng;
size_t yes = 0;
absl::bernoulli_distribution dist(p);
for (size_t i = 0; i < trials; ++i) {
if (dist(rng)) yes++;
}
// Compute the distribution parameters for a binomial test, using a normal
// approximation for the confidence interval, as there are a sufficiently
// large number of trials that the central limit theorem applies.
const double stddev_p = std::sqrt((p * (1.0 - p)) / trials);
const double expected = trials * p;
const double stddev = trials * stddev_p;
// 5 sigma, approved by Richard Feynman
EXPECT_NEAR(yes, expected, 5 * stddev)
<< "@" << p << ", "
<< std::abs(static_cast<double>(yes) - expected) / stddev << " stddev";
}
// There must be many more trials to make the mean approximately normal for `p`
// closes to 0 or 1.
INSTANTIATE_TEST_SUITE_P(
All, BernoulliTest,
::testing::Values(
// Typical values.
std::make_pair(0, 30000), std::make_pair(1e-3, 30000000),
std::make_pair(0.1, 3000000), std::make_pair(0.5, 3000000),
std::make_pair(0.9, 30000000), std::make_pair(0.999, 30000000),
std::make_pair(1, 30000),
// Boundary cases.
std::make_pair(std::nextafter(1.0, 0.0), 1), // ~1 - epsilon
std::make_pair(std::numeric_limits<double>::epsilon(), 1),
std::make_pair(std::nextafter(std::numeric_limits<double>::min(),
1.0), // min + epsilon
1),
std::make_pair(std::numeric_limits<double>::min(), // smallest normal
1),
std::make_pair(
std::numeric_limits<double>::denorm_min(), // smallest denorm
1),
std::make_pair(std::numeric_limits<double>::min() / 2, 1), // denorm
std::make_pair(std::nextafter(std::numeric_limits<double>::min(),
0.0), // denorm_max
1)));
// NOTE: absl::bernoulli_distribution is not guaranteed to be stable.
TEST(BernoulliTest, StabilityTest) {
// absl::bernoulli_distribution stability relies on FastUniformBits and
// integer arithmetic.
absl::random_internal::sequence_urbg urbg({
0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull,
0x4864f22c059bf29eull, 0x247856d8b862665cull, 0xe46e86e9a1337e10ull,
0xd8c8541f3519b133ull, 0xe75b5162c567b9e4ull, 0xf732e5ded7009c5bull,
0xb170b98353121eacull, 0x1ec2e8986d2362caull, 0x814c8e35fe9a961aull,
0x0c3cd59c9b638a02ull, 0xcb3bb6478a07715cull, 0x1224e62c978bbc7full,
0x671ef2cb04e81f6eull, 0x3c1cbd811eaf1808ull, 0x1bbc23cfa8fac721ull,
0xa4c2cda65e596a51ull, 0xb77216fad37adf91ull, 0x836d794457c08849ull,
0xe083df03475f49d7ull, 0xbc9feb512e6b0d6cull, 0xb12d74fdd718c8c5ull,
0x12ff09653bfbe4caull, 0x8dd03a105bc4ee7eull, 0x5738341045ba0d85ull,
0xe3fd722dc65ad09eull, 0x5a14fd21ea2a5705ull, 0x14e6ea4d6edb0c73ull,
0x275b0dc7e0a18acfull, 0x36cebe0d2653682eull, 0x0361e9b23861596bull,
});
// Generate a std::string of '0' and '1' for the distribution output.
auto generate = [&urbg](absl::bernoulli_distribution& dist) {
std::string output;
output.reserve(36);
urbg.reset();
for (int i = 0; i < 35; i++) {
output.append(dist(urbg) ? "1" : "0");
}
return output;
};
const double kP = 0.0331289862362;
{
absl::bernoulli_distribution dist(kP);
auto v = generate(dist);
EXPECT_EQ(35, urbg.invocations());
EXPECT_EQ(v, "00000000000010000000000010000000000") << dist;
}
{
absl::bernoulli_distribution dist(kP * 10.0);
auto v = generate(dist);
EXPECT_EQ(35, urbg.invocations());
EXPECT_EQ(v, "00000100010010010010000011000011010") << dist;
}
{
absl::bernoulli_distribution dist(kP * 20.0);
auto v = generate(dist);
EXPECT_EQ(35, urbg.invocations());
EXPECT_EQ(v, "00011110010110110011011111110111011") << dist;
}
{
absl::bernoulli_distribution dist(1.0 - kP);
auto v = generate(dist);
EXPECT_EQ(35, urbg.invocations());
EXPECT_EQ(v, "11111111111111111111011111111111111") << dist;
}
}
TEST(BernoulliTest, StabilityTest2) {
absl::random_internal::sequence_urbg urbg(
{0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
// Generate a std::string of '0' and '1' for the distribution output.
auto generate = [&urbg](absl::bernoulli_distribution& dist) {
std::string output;
output.reserve(13);
urbg.reset();
for (int i = 0; i < 12; i++) {
output.append(dist(urbg) ? "1" : "0");
}
return output;
};
constexpr double b0 = 1.0 / 13.0 / 0.2;
constexpr double b1 = 2.0 / 13.0 / 0.2;
constexpr double b3 = (5.0 / 13.0 / 0.2) - ((1 - b0) + (1 - b1) + (1 - b1));
{
absl::bernoulli_distribution dist(b0);
auto v = generate(dist);
EXPECT_EQ(12, urbg.invocations());
EXPECT_EQ(v, "000011100101") << dist;
}
{
absl::bernoulli_distribution dist(b1);
auto v = generate(dist);
EXPECT_EQ(12, urbg.invocations());
EXPECT_EQ(v, "001111101101") << dist;
}
{
absl::bernoulli_distribution dist(b3);
auto v = generate(dist);
EXPECT_EQ(12, urbg.invocations());
EXPECT_EQ(v, "001111101111") << dist;
}
}
} // namespace
// 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/discrete_distribution.h"
namespace absl {
namespace random_internal {
// Initializes the distribution table for Walker's Aliasing algorithm, described
// in Knuth, Vol 2. as well as in https://en.wikipedia.org/wiki/Alias_method
std::vector<std::pair<double, size_t>> InitDiscreteDistribution(
std::vector<double>* probabilities) {
// The empty-case should already be handled by the constructor.
assert(probabilities);
assert(!probabilities->empty());
// Step 1. Normalize the input probabilities to 1.0.
double sum = std::accumulate(std::begin(*probabilities),
std::end(*probabilities), 0.0);
if (std::fabs(sum - 1.0) > 1e-6) {
// Scale `probabilities` only when the sum is too far from 1.0. Scaling
// unconditionally will alter the probabilities slightly.
for (double& item : *probabilities) {
item = item / sum;
}
}
// Step 2. At this point `probabilities` is set to the conditional
// probabilities of each element which sum to 1.0, to within reasonable error.
// These values are used to construct the proportional probability tables for
// the selection phases of Walker's Aliasing algorithm.
//
// To construct the table, pick an element which is under-full (i.e., an
// element for which `(*probabilities)[i] < 1.0/n`), and pair it with an
// element which is over-full (i.e., an element for which
// `(*probabilities)[i] > 1.0/n`). The smaller value can always be retired.
// The larger may still be greater than 1.0/n, or may now be less than 1.0/n,
// and put back onto the appropriate collection.
const size_t n = probabilities->size();
std::vector<std::pair<double, size_t>> q;
q.reserve(n);
std::vector<size_t> over;
std::vector<size_t> under;
size_t idx = 0;
for (const double item : *probabilities) {
assert(item >= 0);
const double v = item * n;
q.emplace_back(v, 0);
if (v < 1.0) {
under.push_back(idx++);
} else {
over.push_back(idx++);
}
}
while (!over.empty() && !under.empty()) {
auto lo = under.back();
under.pop_back();
auto hi = over.back();
over.pop_back();
q[lo].second = hi;
const double r = q[hi].first - (1.0 - q[lo].first);
q[hi].first = r;
if (r < 1.0) {
under.push_back(hi);
} else {
over.push_back(hi);
}
}
// Due to rounding errors, there may be un-paired elements in either
// collection; these should all be values near 1.0. For these values, set `q`
// to 1.0 and set the alternate to the identity.
for (auto i : over) {
q[i] = {1.0, i};
}
for (auto i : under) {
q[i] = {1.0, i};
}
return q;
}
} // namespace random_internal
} // namespace absl
// 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.
#ifndef ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_
#define ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_
#include <cassert>
#include <cmath>
#include <istream>
#include <limits>
#include <numeric>
#include <type_traits>
#include <utility>
#include <vector>
#include "absl/random/bernoulli_distribution.h"
#include "absl/random/internal/iostream_state_saver.h"
#include "absl/random/uniform_int_distribution.h"
namespace absl {
// absl::discrete_distribution
//
// A discrete distribution produces random integers i, where 0 <= i < n
// distributed according to the discrete probability function:
//
// P(i|p0,...,pn−1)=pi
//
// This class is an implementation of discrete_distribution (see
// [rand.dist.samp.discrete]).
//
// The algorithm used is Walker's Aliasing algorithm, described in Knuth, Vol 2.
// absl::discrete_distribution takes O(N) time to precompute the probabilities
// (where N is the number of possible outcomes in the distribution) at
// construction, and then takes O(1) time for each variate generation. Many
// other implementations also take O(N) time to construct an ordered sequence of
// partial sums, plus O(log N) time per variate to binary search.
//
template <typename IntType = int>
class discrete_distribution {
public:
using result_type = IntType;
class param_type {
public:
using distribution_type = discrete_distribution;
param_type() { init(); }
template <typename InputIterator>
explicit param_type(InputIterator begin, InputIterator end)
: p_(begin, end) {
init();
}
explicit param_type(std::initializer_list<double> weights) : p_(weights) {
init();
}
template <class UnaryOperation>
explicit param_type(size_t nw, double xmin, double xmax,
UnaryOperation fw) {
if (nw > 0) {
p_.reserve(nw);
double delta = (xmax - xmin) / static_cast<double>(nw);
assert(delta > 0);
double t = delta * 0.5;
for (size_t i = 0; i < nw; ++i) {
p_.push_back(fw(xmin + i * delta + t));
}
}
init();
}
const std::vector<double>& probabilities() const { return p_; }
size_t n() const { return p_.size() - 1; }
friend bool operator==(const param_type& a, const param_type& b) {
return a.probabilities() == b.probabilities();
}
friend bool operator!=(const param_type& a, const param_type& b) {
return !(a == b);
}
private:
friend class discrete_distribution;
void init();
std::vector<double> p_; // normalized probabilities
std::vector<std::pair<double, size_t>> q_; // (acceptance, alternate) pairs
static_assert(std::is_integral<result_type>::value,
"Class-template absl::discrete_distribution<> must be "
"parameterized using an integral type.");
};
discrete_distribution() : param_() {}
explicit discrete_distribution(const param_type& p) : param_(p) {}
template <typename InputIterator>
explicit discrete_distribution(InputIterator begin, InputIterator end)
: param_(begin, end) {}
explicit discrete_distribution(std::initializer_list<double> weights)
: param_(weights) {}
template <class UnaryOperation>
explicit discrete_distribution(size_t nw, double xmin, double xmax,
UnaryOperation fw)
: param_(nw, xmin, xmax, std::move(fw)) {}
void reset() {}
// generating functions
template <typename URBG>
result_type operator()(URBG& g) { // NOLINT(runtime/references)
return (*this)(g, param_);
}
template <typename URBG>
result_type operator()(URBG& g, // NOLINT(runtime/references)
const param_type& p);
const param_type& param() const { return param_; }
void param(const param_type& p) { param_ = p; }
result_type(min)() const { return 0; }
result_type(max)() const {
return static_cast<result_type>(param_.n());
} // inclusive
// NOTE [rand.dist.sample.discrete] returns a std::vector<double> not a
// const std::vector<double>&.
const std::vector<double>& probabilities() const {
return param_.probabilities();
}
friend bool operator==(const discrete_distribution& a,
const discrete_distribution& b) {
return a.param_ == b.param_;
}
friend bool operator!=(const discrete_distribution& a,
const discrete_distribution& b) {
return a.param_ != b.param_;
}
private:
param_type param_;
};
// --------------------------------------------------------------------------
// Implementation details only below
// --------------------------------------------------------------------------
namespace random_internal {
// Using the vector `*probabilities`, whose values are the weights or
// probabilities of an element being selected, constructs the proportional
// probabilities used by the discrete distribution. `*probabilities` will be
// scaled, if necessary, so that its entries sum to a value sufficiently close
// to 1.0.
std::vector<std::pair<double, size_t>> InitDiscreteDistribution(
std::vector<double>* probabilities);
} // namespace random_internal
template <typename IntType>
void discrete_distribution<IntType>::param_type::init() {
if (p_.empty()) {
p_.push_back(1.0);
q_.emplace_back(1.0, 0);
} else {
assert(n() <= (std::numeric_limits<IntType>::max)());
q_ = random_internal::InitDiscreteDistribution(&p_);
}
}
template <typename IntType>
template <typename URBG>
typename discrete_distribution<IntType>::result_type
discrete_distribution<IntType>::operator()(
URBG& g, // NOLINT(runtime/references)
const param_type& p) {
const auto idx = absl::uniform_int_distribution<result_type>(0, p.n())(g);
const auto& q = p.q_[idx];
const bool selected = absl::bernoulli_distribution(q.first)(g);
return selected ? idx : static_cast<result_type>(q.second);
}
template <typename CharT, typename Traits, typename IntType>
std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
const discrete_distribution<IntType>& x) {
auto saver = random_internal::make_ostream_state_saver(os);
const auto& probabilities = x.param().probabilities();
os << probabilities.size();
os.precision(random_internal::stream_precision_helper<double>::kPrecision);
for (const auto& p : probabilities) {
os << os.fill() << p;
}
return os;
}
template <typename CharT, typename Traits, typename IntType>
std::basic_istream<CharT, Traits>& operator>>(
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
discrete_distribution<IntType>& x) { // NOLINT(runtime/references)
using param_type = typename discrete_distribution<IntType>::param_type;
auto saver = random_internal::make_istream_state_saver(is);
size_t n;
std::vector<double> p;
is >> n;
if (is.fail()) return is;
if (n > 0) {
p.reserve(n);
for (IntType i = 0; i < n && !is.fail(); ++i) {
auto tmp = random_internal::read_floating_point<double>(is);
if (is.fail()) return is;
p.push_back(tmp);
}
}
x.param(param_type(p.begin(), p.end()));
return is;
}
} // namespace absl
#endif // ABSL_RANDOM_DISCRETE_DISTRIBUTION_H_
// 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/discrete_distribution.h"
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <numeric>
#include <random>
#include <sstream>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/random/internal/chi_square.h"
#include "absl/random/internal/distribution_test_util.h"
#include "absl/random/internal/sequence_urbg.h"
#include "absl/random/random.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/strip.h"
namespace {
template <typename IntType>
class DiscreteDistributionTypeTest : public ::testing::Test {};
using IntTypes = ::testing::Types<int8_t, uint8_t, int16_t, uint16_t, int32_t,
uint32_t, int64_t, uint64_t>;
TYPED_TEST_SUITE(DiscreteDistributionTypeTest, IntTypes);
TYPED_TEST(DiscreteDistributionTypeTest, ParamSerializeTest) {
using param_type =
typename absl::discrete_distribution<TypeParam>::param_type;
absl::discrete_distribution<TypeParam> empty;
EXPECT_THAT(empty.probabilities(), testing::ElementsAre(1.0));
absl::discrete_distribution<TypeParam> before({1.0, 2.0, 1.0});
// Validate that the probabilities sum to 1.0. We picked values which
// can be represented exactly to avoid floating-point roundoff error.
double s = 0;
for (const auto& x : before.probabilities()) {
s += x;
}
EXPECT_EQ(s, 1.0);
EXPECT_THAT(before.probabilities(), testing::ElementsAre(0.25, 0.5, 0.25));
// Validate the same data via an initializer list.
{
std::vector<double> data({1.0, 2.0, 1.0});
absl::discrete_distribution<TypeParam> via_param{
param_type(std::begin(data), std::end(data))};
EXPECT_EQ(via_param, before);
}
std::stringstream ss;
ss << before;
absl::discrete_distribution<TypeParam> after;
EXPECT_NE(before, after);
ss >> after;
EXPECT_EQ(before, after);
}
TYPED_TEST(DiscreteDistributionTypeTest, Constructor) {
auto fn = [](double x) { return x; };
{
absl::discrete_distribution<int> unary(0, 1.0, 9.0, fn);
EXPECT_THAT(unary.probabilities(), testing::ElementsAre(1.0));
}
{
absl::discrete_distribution<int> unary(2, 1.0, 9.0, fn);
// => fn(1.0 + 0 * 4 + 2) => 3
// => fn(1.0 + 1 * 4 + 2) => 7
EXPECT_THAT(unary.probabilities(), testing::ElementsAre(0.3, 0.7));
}
}
TEST(DiscreteDistributionTest, InitDiscreteDistribution) {
using testing::Pair;
{
std::vector<double> p({1.0, 2.0, 3.0});
std::vector<std::pair<double, size_t>> q =
absl::random_internal::InitDiscreteDistribution(&p);
EXPECT_THAT(p, testing::ElementsAre(1 / 6.0, 2 / 6.0, 3 / 6.0));
// Each bucket is p=1/3, so bucket 0 will send half it's traffic
// to bucket 2, while the rest will retain all of their traffic.
EXPECT_THAT(q, testing::ElementsAre(Pair(0.5, 2), //
Pair(1.0, 1), //
Pair(1.0, 2)));
}
{
std::vector<double> p({1.0, 2.0, 3.0, 5.0, 2.0});
std::vector<std::pair<double, size_t>> q =
absl::random_internal::InitDiscreteDistribution(&p);
EXPECT_THAT(p, testing::ElementsAre(1 / 13.0, 2 / 13.0, 3 / 13.0, 5 / 13.0,
2 / 13.0));
// A more complex bucketing solution: Each bucket has p=0.2
// So buckets 0, 1, 4 will send their alternate traffic elsewhere, which
// happens to be bucket 3.
// However, summing up that alternate traffic gives bucket 3 too much
// traffic, so it will send some traffic to bucket 2.
constexpr double b0 = 1.0 / 13.0 / 0.2;
constexpr double b1 = 2.0 / 13.0 / 0.2;
constexpr double b3 = (5.0 / 13.0 / 0.2) - ((1 - b0) + (1 - b1) + (1 - b1));
EXPECT_THAT(q, testing::ElementsAre(Pair(b0, 3), //
Pair(b1, 3), //
Pair(1.0, 2), //
Pair(b3, 2), //
Pair(b1, 3)));
}
}
TEST(DiscreteDistributionTest, ChiSquaredTest50) {
using absl::random_internal::kChiSquared;
constexpr size_t kTrials = 10000;
constexpr int kBuckets = 50; // inclusive, so actally +1
// 1-in-100000 threshold, but remember, there are about 8 tests
// in this file. And the test could fail for other reasons.
// Empirically validated with --runs_per_test=10000.
const int kThreshold =
absl::random_internal::ChiSquareValue(kBuckets, 0.99999);
std::vector<double> weights(kBuckets, 0);
std::iota(std::begin(weights), std::end(weights), 1);
absl::discrete_distribution<int> dist(std::begin(weights), std::end(weights));
absl::InsecureBitGen rng;
std::vector<int32_t> counts(kBuckets, 0);
for (size_t i = 0; i < kTrials; i++) {
auto x = dist(rng);
counts[x]++;
}
// Scale weights.
double sum = 0;
for (double x : weights) {
sum += x;
}
for (double& x : weights) {
x = kTrials * (x / sum);
}
double chi_square =
absl::random_internal::ChiSquare(std::begin(counts), std::end(counts),
std::begin(weights), std::end(weights));
if (chi_square > kThreshold) {
double p_value =
absl::random_internal::ChiSquarePValue(chi_square, kBuckets);
// Chi-squared test failed. Output does not appear to be uniform.
std::string msg;
for (size_t i = 0; i < counts.size(); i++) {
absl::StrAppend(&msg, i, ": ", counts[i], " vs ", weights[i], "\n");
}
absl::StrAppend(&msg, kChiSquared, " p-value ", p_value, "\n");
absl::StrAppend(&msg, "High ", kChiSquared, " value: ", chi_square, " > ",
kThreshold);
ABSL_RAW_LOG(INFO, "%s", msg.c_str());
FAIL() << msg;
}
}
TEST(DiscreteDistributionTest, StabilityTest) {
// absl::discrete_distribution stabilitiy relies on
// absl::uniform_int_distribution and absl::bernoulli_distribution.
absl::random_internal::sequence_urbg urbg(
{0x0003eb76f6f7f755ull, 0xFFCEA50FDB2F953Bull, 0xC332DDEFBE6C5AA5ull,
0x6558218568AB9702ull, 0x2AEF7DAD5B6E2F84ull, 0x1521B62829076170ull,
0xECDD4775619F1510ull, 0x13CCA830EB61BD96ull, 0x0334FE1EAA0363CFull,
0xB5735C904C70A239ull, 0xD59E9E0BCBAADE14ull, 0xEECC86BC60622CA7ull});
std::vector<int> output(6);
{
absl::discrete_distribution<int32_t> dist({1.0, 2.0, 3.0, 5.0, 2.0});
EXPECT_EQ(0, dist.min());
EXPECT_EQ(4, dist.max());
for (auto& v : output) {
v = dist(urbg);
}
EXPECT_EQ(12, urbg.invocations());
}
// With 12 calls to urbg, each call into discrete_distribution consumes
// precisely 2 values: one for the uniform call, and a second for the
// bernoulli.
//
// Given the alt mapping: 0=>3, 1=>3, 2=>2, 3=>2, 4=>3, we can
//
// uniform: 443210143131
// bernoulli: b0 000011100101
// bernoulli: b1 001111101101
// bernoulli: b2 111111111111
// bernoulli: b3 001111101111
// bernoulli: b4 001111101101
// ...
EXPECT_THAT(output, testing::ElementsAre(3, 3, 1, 3, 3, 3));
{
urbg.reset();
absl::discrete_distribution<int64_t> dist({1.0, 2.0, 3.0, 5.0, 2.0});
EXPECT_EQ(0, dist.min());
EXPECT_EQ(4, dist.max());
for (auto& v : output) {
v = dist(urbg);
}
EXPECT_EQ(12, urbg.invocations());
}
EXPECT_THAT(output, testing::ElementsAre(3, 3, 0, 3, 0, 4));
}
} // namespace
//
// Copyright 2018 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.
//
#ifndef ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_
#define ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_
#include <string>
#include <tuple>
#include <typeinfo>
#include "absl/meta/type_traits.h"
#include "absl/random/bernoulli_distribution.h"
#include "absl/random/beta_distribution.h"
#include "absl/random/exponential_distribution.h"
#include "absl/random/gaussian_distribution.h"
#include "absl/random/log_uniform_int_distribution.h"
#include "absl/random/poisson_distribution.h"
#include "absl/random/uniform_int_distribution.h"
#include "absl/random/uniform_real_distribution.h"
#include "absl/random/zipf_distribution.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
namespace absl {
namespace random_internal {
// ScalarTypeName defines a preferred hierarchy of preferred type names for
// scalars, and is evaluated at compile time for the specific type
// specialization.
template <typename T>
constexpr const char* ScalarTypeName() {
static_assert(std::is_integral<T>() || std::is_floating_point<T>(), "");
// clang-format off
return
std::is_same<T, float>::value ? "float" :
std::is_same<T, double>::value ? "double" :
std::is_same<T, long double>::value ? "long double" :
std::is_same<T, bool>::value ? "bool" :
std::is_signed<T>::value && sizeof(T) == 1 ? "int8_t" :
std::is_signed<T>::value && sizeof(T) == 2 ? "int16_t" :
std::is_signed<T>::value && sizeof(T) == 4 ? "int32_t" :
std::is_signed<T>::value && sizeof(T) == 8 ? "int64_t" :
std::is_unsigned<T>::value && sizeof(T) == 1 ? "uint8_t" :
std::is_unsigned<T>::value && sizeof(T) == 2 ? "uint16_t" :
std::is_unsigned<T>::value && sizeof(T) == 4 ? "uint32_t" :
std::is_unsigned<T>::value && sizeof(T) == 8 ? "uint64_t" :
"undefined";
// clang-format on
// NOTE: It would be nice to use typeid(T).name(), but that's an
// implementation-defined attribute which does not necessarily
// correspond to a name. We could potentially demangle it
// using, e.g. abi::__cxa_demangle.
}
// Distribution traits used by DistributionCaller and internal implementation
// details of the mocking framework.
/*
struct DistributionFormatTraits {
// Returns the parameterized name of the distribution function.
static constexpr const char* FunctionName()
// Format DistrT parameters.
static std::string FormatArgs(DistrT& dist);
// Format DistrT::result_type results.
static std::string FormatResults(DistrT& dist);
};
*/
template <typename DistrT>
struct DistributionFormatTraits;
template <typename R>
struct DistributionFormatTraits<absl::uniform_int_distribution<R>> {
using distribution_t = absl::uniform_int_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Uniform"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrCat("absl::IntervalClosedClosed, ", (d.min)(), ", ",
(d.max)());
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <typename R>
struct DistributionFormatTraits<absl::uniform_real_distribution<R>> {
using distribution_t = absl::uniform_real_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Uniform"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrCat((d.min)(), ", ", (d.max)());
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <typename R>
struct DistributionFormatTraits<absl::exponential_distribution<R>> {
using distribution_t = absl::exponential_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Exponential"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrCat(d.lambda());
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <typename R>
struct DistributionFormatTraits<absl::poisson_distribution<R>> {
using distribution_t = absl::poisson_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Poisson"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrCat(d.mean());
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <>
struct DistributionFormatTraits<absl::bernoulli_distribution> {
using distribution_t = absl::bernoulli_distribution;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Bernoulli"; }
static constexpr const char* FunctionName() { return Name(); }
static std::string FormatArgs(const distribution_t& d) {
return absl::StrCat(d.p());
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <typename R>
struct DistributionFormatTraits<absl::beta_distribution<R>> {
using distribution_t = absl::beta_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Beta"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrCat(d.alpha(), ", ", d.beta());
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <typename R>
struct DistributionFormatTraits<absl::zipf_distribution<R>> {
using distribution_t = absl::zipf_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Zipf"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrCat(d.k(), ", ", d.v(), ", ", d.q());
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <typename R>
struct DistributionFormatTraits<absl::gaussian_distribution<R>> {
using distribution_t = absl::gaussian_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "Gaussian"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrJoin(std::make_tuple(d.mean(), d.stddev()), ", ");
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
template <typename R>
struct DistributionFormatTraits<absl::log_uniform_int_distribution<R>> {
using distribution_t = absl::log_uniform_int_distribution<R>;
using result_t = typename distribution_t::result_type;
static constexpr const char* Name() { return "LogUniform"; }
static std::string FunctionName() {
return absl::StrCat(Name(), "<", ScalarTypeName<R>(), ">");
}
static std::string FormatArgs(const distribution_t& d) {
return absl::StrJoin(std::make_tuple((d.min)(), (d.max)(), d.base()), ", ");
}
static std::string FormatResults(absl::Span<const result_t> results) {
return absl::StrJoin(results, ", ");
}
};
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_DISTRIBUTION_FORMAT_TRAITS_H_
// 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 <cinttypes>
#include <random>
#include <sstream>
#include <vector>
#include "gtest/gtest.h"
#include "absl/random/random.h"
template <typename T>
void Use(T) {}
TEST(Examples, Basic) {
absl::BitGen gen;
std::vector<int> objs = {10, 20, 30, 40, 50};
// Choose an element from a set.
auto elem = objs[absl::Uniform(gen, 0u, objs.size())];
Use(elem);
// Generate a uniform value between 1 and 6.
auto dice_roll = absl::Uniform<int>(absl::IntervalClosedClosed, gen, 1, 6);
Use(dice_roll);
// Generate a random byte.
auto byte = absl::Uniform<uint8_t>(gen);
Use(byte);
// Generate a fractional value from [0f, 1f).
auto fraction = absl::Uniform<float>(gen, 0, 1);
Use(fraction);
// Toss a fair coin; 50/50 probability.
bool coin_toss = absl::Bernoulli(gen, 0.5);
Use(coin_toss);
// Select a file size between 1k and 10MB, biased towards smaller file sizes.
auto file_size = absl::LogUniform<size_t>(gen, 1000, 10 * 1000 * 1000);
Use(file_size);
// Randomize (shuffle) a collection.
std::shuffle(std::begin(objs), std::end(objs), gen);
}
TEST(Examples, CreateingCorrelatedVariateSequences) {
// Unexpected PRNG correlation is often a source of bugs,
// so when using absl::BitGen it must be an intentional choice.
// NOTE: All of these only exhibit process-level stability.
// Create a correlated sequence from system entropy.
{
auto my_seed = absl::MakeSeedSeq();
absl::BitGen gen_1(my_seed);
absl::BitGen gen_2(my_seed); // Produces same variates as gen_1.
EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5));
EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2));
}
// Create a correlated sequence from an existing URBG.
{
absl::BitGen gen;
auto my_seed = absl::CreateSeedSeqFrom(&gen);
absl::BitGen gen_1(my_seed);
absl::BitGen gen_2(my_seed);
EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5));
EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2));
}
// An alternate construction which uses user-supplied data
// instead of a random seed.
{
const char kData[] = "A simple seed string";
std::seed_seq my_seed(std::begin(kData), std::end(kData));
absl::BitGen gen_1(my_seed);
absl::BitGen gen_2(my_seed);
EXPECT_EQ(absl::Bernoulli(gen_1, 0.5), absl::Bernoulli(gen_2, 0.5));
EXPECT_EQ(absl::Uniform<uint32_t>(gen_1), absl::Uniform<uint32_t>(gen_2));
}
}
// 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.
#ifndef ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_
#define ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_
#include <cassert>
#include <cmath>
#include <istream>
#include <limits>
#include <type_traits>
#include "absl/random/internal/distribution_impl.h"
#include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
// absl::exponential_distribution:
// Generates a number conforming to an exponential distribution and is
// equivalent to the standard [rand.dist.pois.exp] distribution.
template <typename RealType = double>
class exponential_distribution {
public:
using result_type = RealType;
class param_type {
public:
using distribution_type = exponential_distribution;
explicit param_type(result_type lambda = 1) : lambda_(lambda) {
assert(lambda > 0);
neg_inv_lambda_ = -result_type(1) / lambda_;
}
result_type lambda() const { return lambda_; }
friend bool operator==(const param_type& a, const param_type& b) {
return a.lambda_ == b.lambda_;
}
friend bool operator!=(const param_type& a, const param_type& b) {
return !(a == b);
}
private:
friend class exponential_distribution;
result_type lambda_;
result_type neg_inv_lambda_;
static_assert(
std::is_floating_point<RealType>::value,
"Class-template absl::exponential_distribution<> must be parameterized "
"using a floating-point type.");
};
exponential_distribution() : exponential_distribution(1) {}
explicit exponential_distribution(result_type lambda) : param_(lambda) {}
explicit exponential_distribution(const param_type& p) : param_(p) {}
void reset() {}
// Generating functions
template <typename URBG>
result_type operator()(URBG& g) { // NOLINT(runtime/references)
return (*this)(g, param_);
}
template <typename URBG>
result_type operator()(URBG& g, // NOLINT(runtime/references)
const param_type& p);
param_type param() const { return param_; }
void param(const param_type& p) { param_ = p; }
result_type(min)() const { return 0; }
result_type(max)() const {
return std::numeric_limits<result_type>::infinity();
}
result_type lambda() const { return param_.lambda(); }
friend bool operator==(const exponential_distribution& a,
const exponential_distribution& b) {
return a.param_ == b.param_;
}
friend bool operator!=(const exponential_distribution& a,
const exponential_distribution& b) {
return a.param_ != b.param_;
}
private:
param_type param_;
random_internal::FastUniformBits<uint64_t> fast_u64_;
};
// --------------------------------------------------------------------------
// Implementation details follow
// --------------------------------------------------------------------------
template <typename RealType>
template <typename URBG>
typename exponential_distribution<RealType>::result_type
exponential_distribution<RealType>::operator()(
URBG& g, // NOLINT(runtime/references)
const param_type& p) {
using random_internal::NegativeValueT;
const result_type u = random_internal::RandU64ToReal<
result_type>::template Value<NegativeValueT, false>(fast_u64_(g));
// log1p(-x) is mathematically equivalent to log(1 - x) but has more
// accuracy for x near zero.
return p.neg_inv_lambda_ * std::log1p(u);
}
template <typename CharT, typename Traits, typename RealType>
std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
const exponential_distribution<RealType>& x) {
auto saver = random_internal::make_ostream_state_saver(os);
os.precision(random_internal::stream_precision_helper<RealType>::kPrecision);
os << x.lambda();
return os;
}
template <typename CharT, typename Traits, typename RealType>
std::basic_istream<CharT, Traits>& operator>>(
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
exponential_distribution<RealType>& x) { // NOLINT(runtime/references)
using result_type = typename exponential_distribution<RealType>::result_type;
using param_type = typename exponential_distribution<RealType>::param_type;
result_type lambda;
auto saver = random_internal::make_istream_state_saver(is);
lambda = random_internal::read_floating_point<result_type>(is);
if (!is.fail()) {
x.param(param_type(lambda));
}
return is;
}
} // namespace absl
#endif // ABSL_RANDOM_EXPONENTIAL_DISTRIBUTION_H_
// BEGIN GENERATED CODE; DO NOT EDIT
// clang-format off
#include "absl/random/gaussian_distribution.h"
namespace absl {
namespace random_internal {
const gaussian_distribution_base::Tables
gaussian_distribution_base::zg_ = {
{3.7130862467425505, 3.442619855899000214, 3.223084984581141565,
3.083228858216868318, 2.978696252647779819, 2.894344007021528942,
2.82312535054891045, 2.761169372387176857, 2.706113573121819549,
2.656406411261359679, 2.610972248431847387, 2.56903362592493778,
2.530009672388827457, 2.493454522095372106, 2.459018177411830486,
2.426420645533749809, 2.395434278011062457, 2.365871370117638595,
2.337575241339236776, 2.310413683698762988, 2.284274059677471769,
2.25905957386919809, 2.234686395590979036, 2.21108140887870297,
2.188180432076048731, 2.165926793748921497, 2.144270182360394905,
2.123165708673976138, 2.102573135189237608, 2.082456237992015957,
2.062782274508307978, 2.043521536655067194, 2.02464697337738464,
2.006133869963471206, 1.987959574127619033, 1.970103260854325633,
1.952545729553555764, 1.935269228296621957, 1.918257300864508963,
1.901494653105150423, 1.884967035707758143, 1.868661140994487768,
1.852564511728090002, 1.836665460258444904, 1.820952996596124418,
1.805416764219227366, 1.790046982599857506, 1.77483439558606837,
1.759770224899592339, 1.744846128113799244, 1.730054160563729182,
1.71538674071366648, 1.700836618569915748, 1.686396846779167014,
1.6720607540975998, 1.657821920954023254, 1.643674156862867441,
1.629611479470633562, 1.615628095043159629, 1.601718380221376581,
1.587876864890574558, 1.574098216022999264, 1.560377222366167382,
1.546708779859908844, 1.533087877674041755, 1.519509584765938559,
1.505969036863201937, 1.492461423781352714, 1.478981976989922842,
1.465525957342709296, 1.452088642889222792, 1.438665316684561546,
1.425251254514058319, 1.411841712447055919, 1.398431914131003539,
1.385017037732650058, 1.371592202427340812, 1.358152454330141534,
1.34469275175354519, 1.331207949665625279, 1.317692783209412299,
1.304141850128615054, 1.290549591926194894, 1.27691027356015363,
1.263217961454619287, 1.249466499573066436, 1.23564948326336066,
1.221760230539994385, 1.207791750415947662, 1.193736707833126465,
1.17958738466398616, 1.165335636164750222, 1.150972842148865416,
1.136489852013158774, 1.121876922582540237, 1.107123647534034028,
1.092218876907275371, 1.077150624892893482, 1.061905963694822042,
1.046470900764042922, 1.030830236068192907, 1.014967395251327842,
0.9988642334929808131, 0.9825008035154263464, 0.9658550794011470098,
0.9489026255113034436, 0.9316161966151479401, 0.9139652510230292792,
0.8959153525809346874, 0.8774274291129204872, 0.8584568431938099931,
0.8389522142975741614, 0.8188539067003538507, 0.7980920606440534693,
0.7765839878947563557, 0.7542306644540520688, 0.7309119106424850631,
0.7064796113354325779, 0.6807479186691505202, 0.6534786387399710295,
0.6243585973360461505, 0.5929629424714434327, 0.5586921784081798625,
0.5206560387620546848, 0.4774378372966830431, 0.4265479863554152429,
0.3628714310970211909, 0.2723208648139477384, 0},
{0.001014352564120377413, 0.002669629083880922793, 0.005548995220771345792,
0.008624484412859888607, 0.01183947865788486861, 0.01516729801054656976,
0.01859210273701129151, 0.02210330461592709475, 0.02569329193593428151,
0.02935631744000685023, 0.03308788614622575758, 0.03688438878665621645,
0.04074286807444417458, 0.04466086220049143157, 0.04863629585986780496,
0.05266740190305100461, 0.05675266348104984759, 0.06089077034804041277,
0.06508058521306804567, 0.06932111739357792179, 0.07361150188411341722,
0.07795098251397346301, 0.08233889824223575293, 0.08677467189478028919,
0.09125780082683036809, 0.095787849121731522, 0.1003644410286559929,
0.1049872554094214289, 0.1096560210148404546, 0.1143705124488661323,
0.1191305467076509556, 0.1239359802028679736, 0.1287867061959434012,
0.1336826525834396151, 0.1386237799845948804, 0.1436100800906280339,
0.1486415742423425057, 0.1537183122081819397, 0.1588403711394795748,
0.1640078546834206341, 0.1692208922373653057, 0.1744796383307898324,
0.1797842721232958407, 0.1851349970089926078, 0.1905320403191375633,
0.1959756531162781534, 0.2014661100743140865, 0.2070037094399269362,
0.2125887730717307134, 0.2182216465543058426, 0.2239026993850088965,
0.229632325232116602, 0.2354109422634795556, 0.2412389935454402889,
0.2471169475123218551, 0.2530452985073261551, 0.2590245673962052742,
0.2650553022555897087, 0.271138079138385224, 0.2772735029191887857,
0.2834622082232336471, 0.2897048604429605656, 0.2960021568469337061,
0.3023548277864842593, 0.3087636380061818397, 0.3152293880650116065,
0.3217529158759855901, 0.3283350983728509642, 0.3349768533135899506,
0.3416791412315512977, 0.3484429675463274756, 0.355269384847918035,
0.3621594953693184626, 0.3691144536644731522, 0.376135469510563536,
0.3832238110559021416, 0.3903808082373155797, 0.3976078564938743676,
0.404906420807223999, 0.4122780401026620578, 0.4197243320495753771,
0.4272469983049970721, 0.4348478302499918513, 0.4425287152754694975,
0.4502916436820402768, 0.458138716267873114, 0.4660721526894572309,
0.4740943006930180559, 0.4822076463294863724, 0.4904148252838453348,
0.4987186354709807201, 0.5071220510755701794, 0.5156282382440030565,
0.5242405726729852944, 0.5329626593838373561, 0.5417983550254266145,
0.5507517931146057588, 0.5598274127040882009, 0.5690299910679523787,
0.5783646811197646898, 0.5878370544347081283, 0.5974531509445183408,
0.6072195366251219584, 0.6171433708188825973, 0.6272324852499290282,
0.6374954773350440806, 0.6479418211102242475, 0.6585820000500898219,
0.6694276673488921414, 0.6804918409973358395, 0.6917891434366769676,
0.7033360990161600101, 0.7151515074105005976, 0.7272569183441868201,
0.7396772436726493094, 0.7524415591746134169, 0.7655841738977066102,
0.7791460859296898134, 0.7931770117713072832, 0.8077382946829627652,
0.8229072113814113187, 0.8387836052959920519, 0.8555006078694531446,
0.873243048910072206, 0.8922816507840289901, 0.9130436479717434217,
0.9362826816850632339, 0.9635996931270905952, 1}};
} // namespace random_internal
} // namespace absl
// clang-format on
// END GENERATED CODE
// 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.
#ifndef ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_
#define ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_
// absl::gaussian_distribution implements the Ziggurat algorithm
// for generating random gaussian numbers.
//
// Implementation based on "The Ziggurat Method for Generating Random Variables"
// by George Marsaglia and Wai Wan Tsang: http://www.jstatsoft.org/v05/i08/
//
#include <cmath>
#include <cstdint>
#include <istream>
#include <limits>
#include <type_traits>
#include "absl/random/internal/distribution_impl.h"
#include "absl/random/internal/fast_uniform_bits.h"
#include "absl/random/internal/iostream_state_saver.h"
namespace absl {
namespace random_internal {
// absl::gaussian_distribution_base implements the underlying ziggurat algorithm
// using the ziggurat tables generated by the gaussian_distribution_gentables
// binary.
//
// The specific algorithm has some of the improvements suggested by the
// 2005 paper, "An Improved Ziggurat Method to Generate Normal Random Samples",
// Jurgen A Doornik. (https://www.doornik.com/research/ziggurat.pdf)
class gaussian_distribution_base {
public:
template <typename URBG>
inline double zignor(URBG& g); // NOLINT(runtime/references)
private:
friend class TableGenerator;
template <typename URBG>
inline double zignor_fallback(URBG& g, // NOLINT(runtime/references)
bool neg);
// Constants used for the gaussian distribution.
static constexpr double kR = 3.442619855899; // Start of the tail.
static constexpr double kRInv = 0.29047645161474317; // ~= (1.0 / kR) .
static constexpr double kV = 9.91256303526217e-3;
static constexpr uint64_t kMask = 0x07f;
// The ziggurat tables store the pdf(f) and inverse-pdf(x) for equal-area
// points on one-half of the normal distribution, where the pdf function,
// pdf = e ^ (-1/2 *x^2), assumes that the mean = 0 & stddev = 1.
//
// These tables are just over 2kb in size; larger tables might improve the
// distributions, but also lead to more cache pollution.
//
// x = {3.71308, 3.44261, 3.22308, ..., 0}
// f = {0.00101, 0.00266, 0.00554, ..., 1}
struct Tables {
double x[kMask + 2];
double f[kMask + 2];
};
static const Tables zg_;
random_internal::FastUniformBits<uint64_t> fast_u64_;
};
} // namespace random_internal
// absl::gaussian_distribution:
// Generates a number conforming to a Gaussian distribution.
template <typename RealType = double>
class gaussian_distribution : random_internal::gaussian_distribution_base {
public:
using result_type = RealType;
class param_type {
public:
using distribution_type = gaussian_distribution;
explicit param_type(result_type mean = 0, result_type stddev = 1)
: mean_(mean), stddev_(stddev) {}
// Returns the mean distribution parameter. The mean specifies the location
// of the peak. The default value is 0.0.
result_type mean() const { return mean_; }
// Returns the deviation distribution parameter. The default value is 1.0.
result_type stddev() const { return stddev_; }
friend bool operator==(const param_type& a, const param_type& b) {
return a.mean_ == b.mean_ && a.stddev_ == b.stddev_;
}
friend bool operator!=(const param_type& a, const param_type& b) {
return !(a == b);
}
private:
result_type mean_;
result_type stddev_;
static_assert(
std::is_floating_point<RealType>::value,
"Class-template absl::gaussian_distribution<> must be parameterized "
"using a floating-point type.");
};
gaussian_distribution() : gaussian_distribution(0) {}
explicit gaussian_distribution(result_type mean, result_type stddev = 1)
: param_(mean, stddev) {}
explicit gaussian_distribution(const param_type& p) : param_(p) {}
void reset() {}
// Generating functions
template <typename URBG>
result_type operator()(URBG& g) { // NOLINT(runtime/references)
return (*this)(g, param_);
}
template <typename URBG>
result_type operator()(URBG& g, // NOLINT(runtime/references)
const param_type& p);
param_type param() const { return param_; }
void param(const param_type& p) { param_ = p; }
result_type(min)() const {
return -std::numeric_limits<result_type>::infinity();
}
result_type(max)() const {
return std::numeric_limits<result_type>::infinity();
}
result_type mean() const { return param_.mean(); }
result_type stddev() const { return param_.stddev(); }
friend bool operator==(const gaussian_distribution& a,
const gaussian_distribution& b) {
return a.param_ == b.param_;
}
friend bool operator!=(const gaussian_distribution& a,
const gaussian_distribution& b) {
return a.param_ != b.param_;
}
private:
param_type param_;
};
// --------------------------------------------------------------------------
// Implementation details only below
// --------------------------------------------------------------------------
template <typename RealType>
template <typename URBG>
typename gaussian_distribution<RealType>::result_type
gaussian_distribution<RealType>::operator()(
URBG& g, // NOLINT(runtime/references)
const param_type& p) {
return p.mean() + p.stddev() * static_cast<result_type>(zignor(g));
}
template <typename CharT, typename Traits, typename RealType>
std::basic_ostream<CharT, Traits>& operator<<(
std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references)
const gaussian_distribution<RealType>& x) {
auto saver = random_internal::make_ostream_state_saver(os);
os.precision(random_internal::stream_precision_helper<RealType>::kPrecision);
os << x.mean() << os.fill() << x.stddev();
return os;
}
template <typename CharT, typename Traits, typename RealType>
std::basic_istream<CharT, Traits>& operator>>(
std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references)
gaussian_distribution<RealType>& x) { // NOLINT(runtime/references)
using result_type = typename gaussian_distribution<RealType>::result_type;
using param_type = typename gaussian_distribution<RealType>::param_type;
auto saver = random_internal::make_istream_state_saver(is);
auto mean = random_internal::read_floating_point<result_type>(is);
if (is.fail()) return is;
auto stddev = random_internal::read_floating_point<result_type>(is);
if (!is.fail()) {
x.param(param_type(mean, stddev));
}
return is;
}
namespace random_internal {
template <typename URBG>
inline double gaussian_distribution_base::zignor_fallback(URBG& g, bool neg) {
// This fallback path happens approximately 0.05% of the time.
double x, y;
do {
// kRInv = 1/r, U(0, 1)
x = kRInv * std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)));
y = -std::log(RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)));
} while ((y + y) < (x * x));
return neg ? (x - kR) : (kR - x);
}
template <typename URBG>
inline double gaussian_distribution_base::zignor(
URBG& g) { // NOLINT(runtime/references)
while (true) {
// We use a single uint64_t to generate both a double and a strip.
// These bits are unused when the generated double is > 1/2^5.
// This may introduce some bias from the duplicated low bits of small
// values (those smaller than 1/2^5, which all end up on the left tail).
uint64_t bits = fast_u64_(g);
int i = static_cast<int>(bits & kMask); // pick a random strip
double j = RandU64ToDouble<SignedValueT, false>(bits); // U(-1, 1)
const double x = j * zg_.x[i];
// Retangular box. Handles >97% of all cases.
// For any given box, this handles between 75% and 99% of values.
// Equivalent to U(01) < (x[i+1] / x[i]), and when i == 0, ~93.5%
if (std::abs(x) < zg_.x[i + 1]) {
return x;
}
// i == 0: Base box. Sample using a ratio of uniforms.
if (i == 0) {
// This path happens about 0.05% of the time.
return zignor_fallback(g, j < 0);
}
// i > 0: Wedge samples using precomputed values.
double v = RandU64ToDouble<PositiveValueT, false>(fast_u64_(g)); // U(0, 1)
if ((zg_.f[i + 1] + v * (zg_.f[i] - zg_.f[i + 1])) <
std::exp(-0.5 * x * x)) {
return x;
}
// The wedge was missed; reject the value and try again.
}
}
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_GAUSSIAN_DISTRIBUTION_H_
// 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 <cstddef>
#include <cstdint>
#include <random>
#include <vector>
#include "gtest/gtest.h"
#include "absl/random/distributions.h"
#include "absl/random/random.h"
namespace {
template <typename URBG>
void TestUniform(URBG* gen) {
// [a, b) default-semantics, inferred types.
absl::Uniform(*gen, 0, 100); // int
absl::Uniform(*gen, 0, 1.0); // Promoted to double
absl::Uniform(*gen, 0.0f, 1.0); // Promoted to double
absl::Uniform(*gen, 0.0, 1.0); // double
absl::Uniform(*gen, -1, 1L); // Promoted to long
// Roll a die.
absl::Uniform(absl::IntervalClosedClosed, *gen, 1, 6);
// Get a fraction.
absl::Uniform(absl::IntervalOpenOpen, *gen, 0.0, 1.0);
// Assign a value to a random element.
std::vector<int> elems = {10, 20, 30, 40, 50};
elems[absl::Uniform(*gen, 0u, elems.size())] = 5;
elems[absl::Uniform<size_t>(*gen, 0, elems.size())] = 3;
// Choose some epsilon around zero.
absl::Uniform(absl::IntervalOpenOpen, *gen, -1.0, 1.0);
// (a, b) semantics, inferred types.
absl::Uniform(absl::IntervalOpenOpen, *gen, 0, 1.0); // Promoted to double
// Explict overriding of types.
absl::Uniform<int>(*gen, 0, 100);
absl::Uniform<int8_t>(*gen, 0, 100);
absl::Uniform<int16_t>(*gen, 0, 100);
absl::Uniform<uint16_t>(*gen, 0, 100);
absl::Uniform<int32_t>(*gen, 0, 1 << 10);
absl::Uniform<uint32_t>(*gen, 0, 1 << 10);
absl::Uniform<int64_t>(*gen, 0, 1 << 10);
absl::Uniform<uint64_t>(*gen, 0, 1 << 10);
absl::Uniform<float>(*gen, 0.0, 1.0);
absl::Uniform<float>(*gen, 0, 1);
absl::Uniform<float>(*gen, -1, 1);
absl::Uniform<double>(*gen, 0.0, 1.0);
absl::Uniform<float>(*gen, -1.0, 0);
absl::Uniform<double>(*gen, -1.0, 0);
// Tagged
absl::Uniform<double>(absl::IntervalClosedClosed, *gen, 0, 1);
absl::Uniform<double>(absl::IntervalClosedOpen, *gen, 0, 1);
absl::Uniform<double>(absl::IntervalOpenOpen, *gen, 0, 1);
absl::Uniform<double>(absl::IntervalOpenClosed, *gen, 0, 1);
absl::Uniform<double>(absl::IntervalClosedClosed, *gen, 0, 1);
absl::Uniform<double>(absl::IntervalOpenOpen, *gen, 0, 1);
absl::Uniform<int>(absl::IntervalClosedClosed, *gen, 0, 100);
absl::Uniform<int>(absl::IntervalClosedOpen, *gen, 0, 100);
absl::Uniform<int>(absl::IntervalOpenOpen, *gen, 0, 100);
absl::Uniform<int>(absl::IntervalOpenClosed, *gen, 0, 100);
absl::Uniform<int>(absl::IntervalClosedClosed, *gen, 0, 100);
absl::Uniform<int>(absl::IntervalOpenOpen, *gen, 0, 100);
// With *generator as an R-value reference.
absl::Uniform<int>(URBG(), 0, 100);
absl::Uniform<double>(URBG(), 0.0, 1.0);
}
template <typename URBG>
void TestExponential(URBG* gen) {
absl::Exponential<float>(*gen);
absl::Exponential<double>(*gen);
absl::Exponential<double>(URBG());
}
template <typename URBG>
void TestPoisson(URBG* gen) {
// [rand.dist.pois] Indicates that the std::poisson_distribution
// is parameterized by IntType, however MSVC does not allow 8-bit
// types.
absl::Poisson<int>(*gen);
absl::Poisson<int16_t>(*gen);
absl::Poisson<uint16_t>(*gen);
absl::Poisson<int32_t>(*gen);
absl::Poisson<uint32_t>(*gen);
absl::Poisson<int64_t>(*gen);
absl::Poisson<uint64_t>(*gen);
absl::Poisson<uint64_t>(URBG());
}
template <typename URBG>
void TestBernoulli(URBG* gen) {
absl::Bernoulli(*gen, 0.5);
absl::Bernoulli(*gen, 0.5);
}
template <typename URBG>
void TestZipf(URBG* gen) {
absl::Zipf<int>(*gen, 100);
absl::Zipf<int8_t>(*gen, 100);
absl::Zipf<int16_t>(*gen, 100);
absl::Zipf<uint16_t>(*gen, 100);
absl::Zipf<int32_t>(*gen, 1 << 10);
absl::Zipf<uint32_t>(*gen, 1 << 10);
absl::Zipf<int64_t>(*gen, 1 << 10);
absl::Zipf<uint64_t>(*gen, 1 << 10);
absl::Zipf<uint64_t>(URBG(), 1 << 10);
}
template <typename URBG>
void TestGaussian(URBG* gen) {
absl::Gaussian<float>(*gen, 1.0, 1.0);
absl::Gaussian<double>(*gen, 1.0, 1.0);
absl::Gaussian<double>(URBG(), 1.0, 1.0);
}
template <typename URBG>
void TestLogNormal(URBG* gen) {
absl::LogUniform<int>(*gen, 0, 100);
absl::LogUniform<int8_t>(*gen, 0, 100);
absl::LogUniform<int16_t>(*gen, 0, 100);
absl::LogUniform<uint16_t>(*gen, 0, 100);
absl::LogUniform<int32_t>(*gen, 0, 1 << 10);
absl::LogUniform<uint32_t>(*gen, 0, 1 << 10);
absl::LogUniform<int64_t>(*gen, 0, 1 << 10);
absl::LogUniform<uint64_t>(*gen, 0, 1 << 10);
absl::LogUniform<uint64_t>(URBG(), 0, 1 << 10);
}
template <typename URBG>
void CompatibilityTest() {
URBG gen;
TestUniform(&gen);
TestExponential(&gen);
TestPoisson(&gen);
TestBernoulli(&gen);
TestZipf(&gen);
TestGaussian(&gen);
TestLogNormal(&gen);
}
TEST(std_mt19937_64, Compatibility) {
// Validate with std::mt19937_64
CompatibilityTest<std::mt19937_64>();
}
TEST(BitGen, Compatibility) {
// Validate with absl::BitGen
CompatibilityTest<absl::BitGen>();
}
TEST(InsecureBitGen, Compatibility) {
// Validate with absl::InsecureBitGen
CompatibilityTest<absl::InsecureBitGen>();
}
} // namespace
// 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/chi_square.h"
#include <cmath>
#include "absl/random/internal/distribution_test_util.h"
namespace absl {
namespace random_internal {
namespace {
#if defined(__EMSCRIPTEN__)
// Workaround __EMSCRIPTEN__ error: llvm_fma_f64 not found.
inline double fma(double x, double y, double z) {
return (x * y) + z;
}
#endif
// Use Horner's method to evaluate a polynomial.
template <typename T, unsigned N>
inline T EvaluatePolynomial(T x, const T (&poly)[N]) {
#if !defined(__EMSCRIPTEN__)
using std::fma;
#endif
T p = poly[N - 1];
for (unsigned i = 2; i <= N; i++) {
p = fma(p, x, poly[N - i]);
}
return p;
}
static constexpr int kLargeDOF = 150;
// Returns the probability of a normal z-value.
//
// Adapted from the POZ function in:
// Ibbetson D, Algorithm 209
// Collected Algorithms of the CACM 1963 p. 616
//
double POZ(double z) {
static constexpr double kP1[] = {
0.797884560593, -0.531923007300, 0.319152932694,
-0.151968751364, 0.059054035642, -0.019198292004,
0.005198775019, -0.001075204047, 0.000124818987,
};
static constexpr double kP2[] = {
0.999936657524, 0.000535310849, -0.002141268741, 0.005353579108,
-0.009279453341, 0.011630447319, -0.010557625006, 0.006549791214,
-0.002034254874, -0.000794620820, 0.001390604284, -0.000676904986,
-0.000019538132, 0.000152529290, -0.000045255659,
};
const double kZMax = 6.0; // Maximum meaningful z-value.
if (z == 0.0) {
return 0.5;
}
double x;
double y = 0.5 * std::fabs(z);
if (y >= (kZMax * 0.5)) {
x = 1.0;
} else if (y < 1.0) {
double w = y * y;
x = EvaluatePolynomial(w, kP1) * y * 2.0;
} else {
y -= 2.0;
x = EvaluatePolynomial(y, kP2);
}
return z > 0.0 ? ((x + 1.0) * 0.5) : ((1.0 - x) * 0.5);
}
// Approximates the survival function of the normal distribution.
//
// Algorithm 26.2.18, from:
// [Abramowitz and Stegun, Handbook of Mathematical Functions,p.932]
// http://people.math.sfu.ca/~cbm/aands/abramowitz_and_stegun.pdf
//
double normal_survival(double z) {
// Maybe replace with the alternate formulation.
// 0.5 * erfc((x - mean)/(sqrt(2) * sigma))
static constexpr double kR[] = {
1.0, 0.196854, 0.115194, 0.000344, 0.019527,
};
double r = EvaluatePolynomial(z, kR);
r *= r;
return 0.5 / (r * r);
}
} // namespace
// Calculates the critical chi-square value given degrees-of-freedom and a
// p-value, usually using bisection. Also known by the name CRITCHI.
double ChiSquareValue(int dof, double p) {
static constexpr double kChiEpsilon =
0.000001; // Accuracy of the approximation.
static constexpr double kChiMax =
99999.0; // Maximum chi-squared value.
const double p_value = 1.0 - p;
if (dof < 1 || p_value > 1.0) {
return 0.0;
}
if (dof > kLargeDOF) {
// For large degrees of freedom, use the normal approximation by
// Wilson, E. B. and Hilferty, M. M. (1931)
// chi^2 - mean
// Z = --------------
// stddev
const double z = InverseNormalSurvival(p_value);
const double mean = 1 - 2.0 / (9 * dof);
const double variance = 2.0 / (9 * dof);
// Cannot use this method if the variance is 0.
if (variance != 0) {
return std::pow(z * std::sqrt(variance) + mean, 3.0) * dof;
}
}
if (p_value <= 0.0) return kChiMax;
// Otherwise search for the p value by bisection
double min_chisq = 0.0;
double max_chisq = kChiMax;
double current = dof / std::sqrt(p_value);
while ((max_chisq - min_chisq) > kChiEpsilon) {
if (ChiSquarePValue(current, dof) < p_value) {
max_chisq = current;
} else {
min_chisq = current;
}
current = (max_chisq + min_chisq) * 0.5;
}
return current;
}
// Calculates the p-value (probability) of a given chi-square value
// and degrees of freedom.
//
// Adapted from the POCHISQ function from:
// Hill, I. D. and Pike, M. C. Algorithm 299
// Collected Algorithms of the CACM 1963 p. 243
//
double ChiSquarePValue(double chi_square, int dof) {
static constexpr double kLogSqrtPi =
0.5723649429247000870717135; // Log[Sqrt[Pi]]
static constexpr double kInverseSqrtPi =
0.5641895835477562869480795; // 1/(Sqrt[Pi])
// For large degrees of freedom, use the normal approximation by
// Wilson, E. B. and Hilferty, M. M. (1931)
// Via Wikipedia:
// By the Central Limit Theorem, because the chi-square distribution is the
// sum of k independent random variables with finite mean and variance, it
// converges to a normal distribution for large k.
if (dof > kLargeDOF) {
// Re-scale everything.
const double chi_square_scaled = std::pow(chi_square / dof, 1.0 / 3);
const double mean = 1 - 2.0 / (9 * dof);
const double variance = 2.0 / (9 * dof);
// If variance is 0, this method cannot be used.
if (variance != 0) {
const double z = (chi_square_scaled - mean) / std::sqrt(variance);
if (z > 0) {
return normal_survival(z);
} else if (z < 0) {
return 1.0 - normal_survival(-z);
} else {
return 0.5;
}
}
}
// The chi square function is >= 0 for any degrees of freedom.
// In other words, probability that the chi square function >= 0 is 1.
if (chi_square <= 0.0) return 1.0;
// If the degrees of freedom is zero, the chi square function is always 0 by
// definition. In other words, the probability that the chi square function
// is > 0 is zero (chi square values <= 0 have been filtered above).
if (dof < 1) return 0;
auto capped_exp = [](double x) { return x < -20 ? 0.0 : std::exp(x); };
static constexpr double kBigX = 20;
double a = 0.5 * chi_square;
const bool even = !(dof & 1); // True if dof is an even number.
const double y = capped_exp(-a);
double s = even ? y : (2.0 * POZ(-std::sqrt(chi_square)));
if (dof <= 2) {
return s;
}
chi_square = 0.5 * (dof - 1.0);
double z = (even ? 1.0 : 0.5);
if (a > kBigX) {
double e = (even ? 0.0 : kLogSqrtPi);
double c = std::log(a);
while (z <= chi_square) {
e = std::log(z) + e;
s += capped_exp(c * z - a - e);
z += 1.0;
}
return s;
}
double e = (even ? 1.0 : (kInverseSqrtPi / std::sqrt(a)));
double c = 0.0;
while (z <= chi_square) {
e = e * (a / z);
c = c + e;
z += 1.0;
}
return c * y + s;
}
} // namespace random_internal
} // namespace absl
// 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.
#ifndef ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
#define ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
// The chi-square statistic.
//
// Useful for evaluating if `D` independent random variables are behaving as
// expected, or if two distributions are similar. (`D` is the degrees of
// freedom).
//
// Each bucket should have an expected count of 10 or more for the chi square to
// be meaningful.
#include <cassert>
namespace absl {
namespace random_internal {
constexpr const char kChiSquared[] = "chi-squared";
// Returns the measured chi square value, using a single expected value. This
// assumes that the values in [begin, end) are uniformly distributed.
template <typename Iterator>
double ChiSquareWithExpected(Iterator begin, Iterator end, double expected) {
// Compute the sum and the number of buckets.
assert(expected >= 10); // require at least 10 samples per bucket.
double chi_square = 0;
for (auto it = begin; it != end; it++) {
double d = static_cast<double>(*it) - expected;
chi_square += d * d;
}
chi_square = chi_square / expected;
return chi_square;
}
// Returns the measured chi square value, taking the actual value of each bucket
// from the first set of iterators, and the expected value of each bucket from
// the second set of iterators.
template <typename Iterator, typename Expected>
double ChiSquare(Iterator it, Iterator end, Expected eit, Expected eend) {
double chi_square = 0;
for (; it != end && eit != eend; ++it, ++eit) {
if (*it > 0) {
assert(*eit > 0);
}
double e = static_cast<double>(*eit);
double d = static_cast<double>(*it - *eit);
if (d != 0) {
assert(e > 0);
chi_square += (d * d) / e;
}
}
assert(it == end && eit == eend);
return chi_square;
}
// ======================================================================
// The following methods can be used for an arbitrary significance level.
//
// Calculates critical chi-square values to produce the given p-value using a
// bisection search for a value within epsilon, relying on the monotonicity of
// ChiSquarePValue().
double ChiSquareValue(int dof, double p);
// Calculates the p-value (probability) of a given chi-square value.
double ChiSquarePValue(double chi_square, int dof);
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_CHI_SQUARE_H_
//
// Copyright 2018 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.
//
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
#include <utility>
namespace absl {
namespace random_internal {
// DistributionCaller provides an opportunity to overload the general
// mechanism for calling a distribution, allowing for mock-RNG classes
// to intercept such calls.
template <typename URBG>
struct DistributionCaller {
// Call the provided distribution type. The parameters are expected
// to be explicitly specified.
// DistrT is the distribution type.
// FormatT is the formatter type:
//
// struct FormatT {
// using result_type = distribution_t::result_type;
// static std::string FormatCall(
// const distribution_t& distr,
// absl::Span<const result_type>);
//
// static std::string FormatExpectation(
// absl::string_view match_args,
// absl::Span<const result_t> results);
// }
//
template <typename DistrT, typename FormatT, typename... Args>
static typename DistrT::result_type Call(URBG* urbg, Args&&... args) {
DistrT dist(std::forward<Args>(args)...);
return dist(*urbg);
}
};
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_CALLER_H_
// 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.
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_
#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_
// This file contains some implementation details which are used by one or more
// of the absl random number distributions.
#include <cfloat>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <type_traits>
#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64)
#include <intrin.h> // NOLINT(build/include_order)
#pragma intrinsic(_umul128)
#define ABSL_INTERNAL_USE_UMUL128 1
#endif
#include "absl/base/config.h"
#include "absl/base/internal/bits.h"
#include "absl/numeric/int128.h"
#include "absl/random/internal/fastmath.h"
#include "absl/random/internal/traits.h"
namespace absl {
namespace random_internal {
// Creates a double from `bits`, with the template fields controlling the
// output.
//
// RandU64To is both more efficient and generates more unique values in the
// result interval than known implementations of std::generate_canonical().
//
// The `Signed` parameter controls whether positive, negative, or both are
// returned (thus affecting the output interval).
// When Signed == SignedValueT, range is U(-1, 1)
// When Signed == NegativeValueT, range is U(-1, 0)
// When Signed == PositiveValueT, range is U(0, 1)
//
// When the `IncludeZero` parameter is true, the function may return 0 for some
// inputs, otherwise it never returns 0.
//
// The `ExponentBias` parameter determines the scale of the output range by
// adjusting the exponent.
//
// When a value in U(0,1) is required, use:
// RandU64ToDouble<PositiveValueT, true, 0>();
//
// When a value in U(-1,1) is required, use:
// RandU64ToDouble<SignedValueT, false, 0>() => U(-1, 1)
// This generates more distinct values than the mathematically equivalent
// expression `U(0, 1) * 2.0 - 1.0`, and is preferable.
//
// Scaling the result by powers of 2 (and avoiding a multiply) is also possible:
// RandU64ToDouble<PositiveValueT, false, 1>(); => U(0, 2)
// RandU64ToDouble<PositiveValueT, false, -1>(); => U(0, 0.5)
//
// Tristate types controlling the output.
struct PositiveValueT {};
struct NegativeValueT {};
struct SignedValueT {};
// RandU64ToDouble is the double-result variant of RandU64To, described above.
template <typename Signed, bool IncludeZero, int ExponentBias = 0>
inline double RandU64ToDouble(uint64_t bits) {
static_assert(std::is_same<Signed, PositiveValueT>::value ||
std::is_same<Signed, NegativeValueT>::value ||
std::is_same<Signed, SignedValueT>::value,
"");
// Maybe use the left-most bit for a sign bit.
uint64_t sign = std::is_same<Signed, NegativeValueT>::value
? 0x8000000000000000ull
: 0; // Sign bits.
if (std::is_same<Signed, SignedValueT>::value) {
sign = bits & 0x8000000000000000ull;
bits = bits & 0x7FFFFFFFFFFFFFFFull;
}
if (IncludeZero) {
if (bits == 0u) return 0;
}
// Number of leading zeros is mapped to the exponent: 2^-clz
int clz = base_internal::CountLeadingZeros64(bits);
// Shift number left to erase leading zeros.
bits <<= IncludeZero ? clz : (clz & 63);
// Shift number right to remove bits that overflow double mantissa. The
// direction of the shift depends on `clz`.
bits >>= (64 - DBL_MANT_DIG);
// Compute IEEE 754 double exponent.
// In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the
// exponent to account for that.
const uint64_t exp =
(std::is_same<Signed, SignedValueT>::value ? 1023U : 1022U) +
static_cast<uint64_t>(ExponentBias - clz);
constexpr int kExp = DBL_MANT_DIG - 1;
// Construct IEEE 754 double from exponent and mantissa.
const uint64_t val = sign | (exp << kExp) | (bits & ((1ULL << kExp) - 1U));
double res;
static_assert(sizeof(res) == sizeof(val), "double is not 64 bit");
// Memcpy value from "val" to "res" to avoid aliasing problems. Assumes that
// endian-ness is same for double and uint64_t.
std::memcpy(&res, &val, sizeof(res));
return res;
}
// RandU64ToFloat is the float-result variant of RandU64To, described above.
template <typename Signed, bool IncludeZero, int ExponentBias = 0>
inline float RandU64ToFloat(uint64_t bits) {
static_assert(std::is_same<Signed, PositiveValueT>::value ||
std::is_same<Signed, NegativeValueT>::value ||
std::is_same<Signed, SignedValueT>::value,
"");
// Maybe use the left-most bit for a sign bit.
uint64_t sign = std::is_same<Signed, NegativeValueT>::value
? 0x80000000ul
: 0; // Sign bits.
if (std::is_same<Signed, SignedValueT>::value) {
uint64_t a = bits & 0x8000000000000000ull;
sign = static_cast<uint32_t>(a >> 32);
bits = bits & 0x7FFFFFFFFFFFFFFFull;
}
if (IncludeZero) {
if (bits == 0u) return 0;
}
// Number of leading zeros is mapped to the exponent: 2^-clz
int clz = base_internal::CountLeadingZeros64(bits);
// Shift number left to erase leading zeros.
bits <<= IncludeZero ? clz : (clz & 63);
// Shift number right to remove bits that overflow double mantissa. The
// direction of the shift depends on `clz`.
bits >>= (64 - FLT_MANT_DIG);
// Construct IEEE 754 float exponent.
// In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the
// exponent to account for that.
const uint32_t exp =
(std::is_same<Signed, SignedValueT>::value ? 127U : 126U) +
static_cast<uint32_t>(ExponentBias - clz);
constexpr int kExp = FLT_MANT_DIG - 1;
const uint32_t val = sign | (exp << kExp) | (bits & ((1U << kExp) - 1U));
float res;
static_assert(sizeof(res) == sizeof(val), "float is not 32 bit");
// Assumes that endian-ness is same for float and uint32_t.
std::memcpy(&res, &val, sizeof(res));
return res;
}
template <typename Result>
struct RandU64ToReal {
template <typename Signed, bool IncludeZero, int ExponentBias = 0>
static inline Result Value(uint64_t bits) {
return RandU64ToDouble<Signed, IncludeZero, ExponentBias>(bits);
}
};
template <>
struct RandU64ToReal<float> {
template <typename Signed, bool IncludeZero, int ExponentBias = 0>
static inline float Value(uint64_t bits) {
return RandU64ToFloat<Signed, IncludeZero, ExponentBias>(bits);
}
};
inline uint128 MultiplyU64ToU128(uint64_t a, uint64_t b) {
#if defined(ABSL_HAVE_INTRINSIC_INT128)
return uint128(static_cast<__uint128_t>(a) * b);
#elif defined(ABSL_INTERNAL_USE_UMUL128)
// uint64_t * uint64_t => uint128 multiply using imul intrinsic on MSVC.
uint64_t high = 0;
const uint64_t low = _umul128(a, b, &high);
return absl::MakeUint128(high, low);
#else
// uint128(a) * uint128(b) in emulated mode computes a full 128-bit x 128-bit
// multiply. However there are many cases where that is not necessary, and it
// is only necessary to support a 64-bit x 64-bit = 128-bit multiply. This is
// for those cases.
const uint64_t a00 = static_cast<uint32_t>(a);
const uint64_t a32 = a >> 32;
const uint64_t b00 = static_cast<uint32_t>(b);
const uint64_t b32 = b >> 32;
const uint64_t c00 = a00 * b00;
const uint64_t c32a = a00 * b32;
const uint64_t c32b = a32 * b00;
const uint64_t c64 = a32 * b32;
const uint32_t carry =
static_cast<uint32_t>(((c00 >> 32) + static_cast<uint32_t>(c32a) +
static_cast<uint32_t>(c32b)) >>
32);
return absl::MakeUint128(c64 + (c32a >> 32) + (c32b >> 32) + carry,
c00 + (c32a << 32) + (c32b << 32));
#endif
}
// wide_multiply<T> multiplies two N-bit values to a 2N-bit result.
template <typename UIntType>
struct wide_multiply {
static constexpr size_t kN = std::numeric_limits<UIntType>::digits;
using input_type = UIntType;
using result_type = typename random_internal::unsigned_bits<kN * 2>::type;
static result_type multiply(input_type a, input_type b) {
return static_cast<result_type>(a) * b;
}
static input_type hi(result_type r) { return r >> kN; }
static input_type lo(result_type r) { return r; }
static_assert(std::is_unsigned<UIntType>::value,
"Class-template wide_multiply<> argument must be unsigned.");
};
#ifndef ABSL_HAVE_INTRINSIC_INT128
template <>
struct wide_multiply<uint64_t> {
using input_type = uint64_t;
using result_type = uint128;
static result_type multiply(uint64_t a, uint64_t b) {
return MultiplyU64ToU128(a, b);
}
static uint64_t hi(result_type r) { return Uint128High64(r); }
static uint64_t lo(result_type r) { return Uint128Low64(r); }
};
#endif
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_
// 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.
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
#include <cstddef>
#include <iostream>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
// NOTE: The functions in this file are test only, and are should not be used in
// non-test code.
namespace absl {
namespace random_internal {
// http://webspace.ship.edu/pgmarr/Geo441/Lectures/Lec%205%20-%20Normality%20Testing.pdf
// Compute the 1st to 4th standard moments:
// mean, variance, skewness, and kurtosis.
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
struct DistributionMoments {
size_t n = 0;
double mean = 0.0;
double variance = 0.0;
double skewness = 0.0;
double kurtosis = 0.0;
};
DistributionMoments ComputeDistributionMoments(
absl::Span<const double> data_points);
std::ostream& operator<<(std::ostream& os, const DistributionMoments& moments);
// Computes the Z-score for a set of data with the given distribution moments
// compared against `expected_mean`.
double ZScore(double expected_mean, const DistributionMoments& moments);
// Returns the probability of success required for a single trial to ensure that
// after `num_trials` trials, the probability of at least one failure is no more
// than `p_fail`.
double RequiredSuccessProbability(double p_fail, int num_trials);
// Computes the maximum distance from the mean tolerable, for Z-Tests that are
// expected to pass with `acceptance_probability`. Will terminate if the
// resulting tolerance is zero (due to passing in 0.0 for
// `acceptance_probability` or rounding errors).
//
// For example,
// MaxErrorTolerance(0.001) = 0.0
// MaxErrorTolerance(0.5) = ~0.47
// MaxErrorTolerance(1.0) = inf
double MaxErrorTolerance(double acceptance_probability);
// Approximation to inverse of the Error Function in double precision.
// (http://people.maths.ox.ac.uk/gilesm/files/gems_erfinv.pdf)
double erfinv(double x);
// Beta(p, q) = Gamma(p) * Gamma(q) / Gamma(p+q)
double beta(double p, double q);
// The inverse of the normal survival function.
double InverseNormalSurvival(double x);
// Returns whether actual is "near" expected, based on the bound.
bool Near(absl::string_view msg, double actual, double expected, double bound);
// Implements the incomplete regularized beta function, AS63, BETAIN.
// https://www.jstor.org/stable/2346797
//
// BetaIncomplete(x, p, q), where
// `x` is the value of the upper limit
// `p` is beta parameter p, `q` is beta parameter q.
//
// NOTE: This is a test-only function which is only accurate to within, at most,
// 1e-13 of the actual value.
//
double BetaIncomplete(double x, double p, double q);
// Implements the inverse of the incomplete regularized beta function, AS109,
// XINBTA.
// https://www.jstor.org/stable/2346798
// https://www.jstor.org/stable/2346887
//
// BetaIncompleteInv(p, q, beta, alhpa)
// `p` is beta parameter p, `q` is beta parameter q.
// `alpha` is the value of the lower tail area.
//
// NOTE: This is a test-only function and, when successful, is only accurate to
// within ~1e-6 of the actual value; there are some cases where it diverges from
// the actual value by much more than that. The function uses Newton's method,
// and thus the runtime is highly variable.
double BetaIncompleteInv(double p, double q, double alpha);
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_TEST_UTIL_H_
// 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/distribution_test_util.h"
#include "gtest/gtest.h"
namespace {
TEST(TestUtil, InverseErf) {
const struct {
const double z;
const double value;
} kErfInvTable[] = {
{0.0000001, 8.86227e-8},
{0.00001, 8.86227e-6},
{0.5, 0.4769362762044},
{0.6, 0.5951160814499},
{0.99999, 3.1234132743},
{0.9999999, 3.7665625816},
{0.999999944, 3.8403850690566985}, // = log((1-x) * (1+x)) =~ 16.004
{0.999999999, 4.3200053849134452},
};
for (const auto& data : kErfInvTable) {
auto value = absl::random_internal::erfinv(data.z);
// Log using the Wolfram-alpha function name & parameters.
EXPECT_NEAR(value, data.value, 1e-8)
<< " InverseErf[" << data.z << "] (expected=" << data.value << ") -> "
<< value;
}
}
const struct {
const double p;
const double q;
const double x;
const double alpha;
} kBetaTable[] = {
{0.5, 0.5, 0.01, 0.06376856085851985},
{0.5, 0.5, 0.1, 0.2048327646991335},
{0.5, 0.5, 1, 1},
{1, 0.5, 0, 0},
{1, 0.5, 0.01, 0.005012562893380045},
{1, 0.5, 0.1, 0.0513167019494862},
{1, 0.5, 0.5, 0.2928932188134525},
{1, 1, 0.5, 0.5},
{2, 2, 0.1, 0.028},
{2, 2, 0.2, 0.104},
{2, 2, 0.3, 0.216},
{2, 2, 0.4, 0.352},
{2, 2, 0.5, 0.5},
{2, 2, 0.6, 0.648},
{2, 2, 0.7, 0.784},
{2, 2, 0.8, 0.896},
{2, 2, 0.9, 0.972},
{5.5, 5, 0.5, 0.4361908850559777},
{10, 0.5, 0.9, 0.1516409096346979},
{10, 5, 0.5, 0.08978271484375},
{10, 5, 1, 1},
{10, 10, 0.5, 0.5},
{20, 5, 0.8, 0.4598773297575791},
{20, 10, 0.6, 0.2146816102371739},
{20, 10, 0.8, 0.9507364826957875},
{20, 20, 0.5, 0.5},
{20, 20, 0.6, 0.8979413687105918},
{30, 10, 0.7, 0.2241297491808366},
{30, 10, 0.8, 0.7586405487192086},
{40, 20, 0.7, 0.7001783247477069},
{1, 0.5, 0.1, 0.0513167019494862},
{1, 0.5, 0.2, 0.1055728090000841},
{1, 0.5, 0.3, 0.1633399734659245},
{1, 0.5, 0.4, 0.2254033307585166},
{1, 2, 0.2, 0.36},
{1, 3, 0.2, 0.488},
{1, 4, 0.2, 0.5904},
{1, 5, 0.2, 0.67232},
{2, 2, 0.3, 0.216},
{3, 2, 0.3, 0.0837},
{4, 2, 0.3, 0.03078},
{5, 2, 0.3, 0.010935},
// These values test small & large points along the range of the Beta
// function.
//
// When selecting test points, remember that if BetaIncomplete(x, p, q)
// returns the same value to within the limits of precision over a large
// domain of the input, x, then BetaIncompleteInv(alpha, p, q) may return an
// essentially arbitrary value where BetaIncomplete(x, p, q) =~ alpha.
// BetaRegularized[x, 0.00001, 0.00001],
// For x in {~0.001 ... ~0.999}, => ~0.5
{1e-5, 1e-5, 1e-5, 0.4999424388184638311},
{1e-5, 1e-5, (1.0 - 1e-8), 0.5000920948389232964},
// BetaRegularized[x, 0.00001, 10000].
// For x in {~epsilon ... 1.0}, => ~1
{1e-5, 1e5, 1e-6, 0.9999817708130066936},
{1e-5, 1e5, (1.0 - 1e-7), 1.0},
// BetaRegularized[x, 10000, 0.00001].
// For x in {0 .. 1-epsilon}, => ~0
{1e5, 1e-5, 1e-6, 0},
{1e5, 1e-5, (1.0 - 1e-6), 1.8229186993306369e-5},
};
TEST(BetaTest, BetaIncomplete) {
for (const auto& data : kBetaTable) {
auto value = absl::random_internal::BetaIncomplete(data.x, data.p, data.q);
// Log using the Wolfram-alpha function name & parameters.
EXPECT_NEAR(value, data.alpha, 1e-12)
<< " BetaRegularized[" << data.x << ", " << data.p << ", " << data.q
<< "] (expected=" << data.alpha << ") -> " << value;
}
}
TEST(BetaTest, BetaIncompleteInv) {
for (const auto& data : kBetaTable) {
auto value =
absl::random_internal::BetaIncompleteInv(data.p, data.q, data.alpha);
// Log using the Wolfram-alpha function name & parameters.
EXPECT_NEAR(value, data.x, 1e-6)
<< " InverseBetaRegularized[" << data.alpha << ", " << data.p << ", "
<< data.q << "] (expected=" << data.x << ") -> " << value;
}
}
TEST(MaxErrorTolerance, MaxErrorTolerance) {
std::vector<std::pair<double, double>> cases = {
{0.0000001, 8.86227e-8 * 1.41421356237},
{0.00001, 8.86227e-6 * 1.41421356237},
{0.5, 0.4769362762044 * 1.41421356237},
{0.6, 0.5951160814499 * 1.41421356237},
{0.99999, 3.1234132743 * 1.41421356237},
{0.9999999, 3.7665625816 * 1.41421356237},
{0.999999944, 3.8403850690566985 * 1.41421356237},
{0.999999999, 4.3200053849134452 * 1.41421356237}};
for (auto entry : cases) {
EXPECT_NEAR(absl::random_internal::MaxErrorTolerance(entry.first),
entry.second, 1e-8);
}
}
TEST(ZScore, WithSameMean) {
absl::random_internal::DistributionMoments m;
m.n = 100;
m.mean = 5;
m.variance = 1;
EXPECT_NEAR(absl::random_internal::ZScore(5, m), 0, 1e-12);
m.n = 1;
m.mean = 0;
m.variance = 1;
EXPECT_NEAR(absl::random_internal::ZScore(0, m), 0, 1e-12);
m.n = 10000;
m.mean = -5;
m.variance = 100;
EXPECT_NEAR(absl::random_internal::ZScore(-5, m), 0, 1e-12);
}
TEST(ZScore, DifferentMean) {
absl::random_internal::DistributionMoments m;
m.n = 100;
m.mean = 5;
m.variance = 1;
EXPECT_NEAR(absl::random_internal::ZScore(4, m), 10, 1e-12);
m.n = 1;
m.mean = 0;
m.variance = 1;
EXPECT_NEAR(absl::random_internal::ZScore(-1, m), 1, 1e-12);
m.n = 10000;
m.mean = -5;
m.variance = 100;
EXPECT_NEAR(absl::random_internal::ZScore(-4, m), -10, 1e-12);
}
} // namespace
// Copyright 2019 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.
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_
#define ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_
#include <type_traits>
#include "absl/meta/type_traits.h"
#include "absl/random/internal/distribution_caller.h"
#include "absl/random/internal/traits.h"
#include "absl/random/internal/uniform_helper.h"
namespace absl {
namespace random_internal {
template <typename D>
struct DistributionFormatTraits;
// UniformImpl implements the core logic of the Uniform<T> call, which is to
// select the correct distribution type, compute the bounds based on the
// interval tag, and then generate a value.
template <typename NumType, typename TagType, typename URBG>
NumType UniformImpl(TagType tag,
URBG& urbg, // NOLINT(runtime/references)
NumType lo, NumType hi) {
static_assert(
std::is_arithmetic<NumType>::value,
"absl::Uniform<T>() must use an integer or real parameter type.");
using distribution_t =
typename std::conditional<std::is_integral<NumType>::value,
absl::uniform_int_distribution<NumType>,
absl::uniform_real_distribution<NumType>>::type;
using format_t = random_internal::DistributionFormatTraits<distribution_t>;
auto a = random_internal::uniform_lower_bound<NumType>(tag, lo, hi);
auto b = random_internal::uniform_upper_bound<NumType>(tag, lo, hi);
// TODO(lar): it doesn't make a lot of sense to ask for a random number in an
// empty range. Right now we just return a boundary--even though that
// boundary is not an acceptable value! Is there something better we can do
// here?
using gen_t = absl::decay_t<URBG>;
if (a > b) return a;
return DistributionCaller<gen_t>::template Call<distribution_t, format_t>(
&urbg, a, b);
}
// In the absence of an explicitly provided return-type, the template
// "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on
// the data-types of the endpoint-arguments {A lo, B hi}.
//
// Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the
// return-type, if one type can be implicitly converted into the other, in a
// lossless way. The template "is_widening_convertible" implements the
// compile-time logic for deciding if such a conversion is possible.
//
// If no such conversion between {A, B} exists, then the overload for
// absl::Uniform() will be discarded, and the call will be ill-formed.
// Return-type for absl::Uniform() when the return-type is inferred.
template <typename A, typename B>
using uniform_inferred_return_t =
absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>,
is_widening_convertible<B, A>>::value,
typename std::conditional<
is_widening_convertible<A, B>::value, B, A>::type>;
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTIONS_H_
// 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.
#ifndef ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
#define ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <vector>
namespace absl {
namespace random_internal {
// This class conforms to the C++ Standard "Seed Sequence" concept
// [rand.req.seedseq].
//
// An "ExplicitSeedSeq" is meant to provide a conformant interface for
// forwarding pre-computed seed material to the constructor of a class
// conforming to the "Uniform Random Bit Generator" concept. This class makes no
// attempt to mutate the state provided by its constructor, and returns it
// directly via ExplicitSeedSeq::generate().
//
// If this class is asked to generate more seed material than was provided to
// the constructor, then the remaining bytes will be filled with deterministic,
// nonrandom data.
class ExplicitSeedSeq {
public:
using result_type = uint32_t;
ExplicitSeedSeq() : state_() {}
// Copy and move both allowed.
ExplicitSeedSeq(const ExplicitSeedSeq& other) = default;
ExplicitSeedSeq& operator=(const ExplicitSeedSeq& other) = default;
ExplicitSeedSeq(ExplicitSeedSeq&& other) = default;
ExplicitSeedSeq& operator=(ExplicitSeedSeq&& other) = default;
template <typename Iterator>
ExplicitSeedSeq(Iterator begin, Iterator end) {
for (auto it = begin; it != end; it++) {
state_.push_back(*it & 0xffffffff);
}
}
template <typename T>
ExplicitSeedSeq(std::initializer_list<T> il)
: ExplicitSeedSeq(il.begin(), il.end()) {}
size_t size() const { return state_.size(); }
template <typename OutIterator>
void param(OutIterator out) const {
std::copy(std::begin(state_), std::end(state_), out);
}
template <typename OutIterator>
void generate(OutIterator begin, OutIterator end) {
for (size_t index = 0; begin != end; begin++) {
*begin = state_.empty() ? 0 : state_[index++];
if (index >= state_.size()) {
index = 0;
}
}
}
protected:
std::vector<uint32_t> state_;
};
} // namespace random_internal
} // namespace absl
#endif // ABSL_RANDOM_INTERNAL_EXPLICIT_SEED_SEQ_H_
// 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/explicit_seed_seq.h"
#include <iterator>
#include <random>
#include <utility>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/random/seed_sequences.h"
namespace {
template <typename Sseq>
bool ConformsToInterface() {
// Check that the SeedSequence can be default-constructed.
{ Sseq default_constructed_seq; }
// Check that the SeedSequence can be constructed with two iterators.
{
uint32_t init_array[] = {1, 3, 5, 7, 9};
Sseq iterator_constructed_seq(init_array, &init_array[5]);
}
// Check that the SeedSequence can be std::initializer_list-constructed.
{ Sseq list_constructed_seq = {1, 3, 5, 7, 9, 11, 13}; }
// Check that param() and size() return state provided to constructor.
{
uint32_t init_array[] = {1, 2, 3, 4, 5};
Sseq seq(init_array, &init_array[ABSL_ARRAYSIZE(init_array)]);
EXPECT_EQ(seq.size(), ABSL_ARRAYSIZE(init_array));
uint32_t state_array[ABSL_ARRAYSIZE(init_array)];
seq.param(state_array);
for (int i = 0; i < ABSL_ARRAYSIZE(state_array); i++) {
EXPECT_EQ(state_array[i], i + 1);
}
}
// Check for presence of generate() method.
{
Sseq seq;
uint32_t seeds[5];
seq.generate(seeds, &seeds[ABSL_ARRAYSIZE(seeds)]);
}
return true;
}
} // namespace
TEST(SeedSequences, CheckInterfaces) {
// Control case
EXPECT_TRUE(ConformsToInterface<std::seed_seq>());
// Abseil classes
EXPECT_TRUE(ConformsToInterface<absl::random_internal::ExplicitSeedSeq>());
}
TEST(ExplicitSeedSeq, DefaultConstructorGeneratesZeros) {
const size_t kNumBlocks = 128;
uint32_t outputs[kNumBlocks];
absl::random_internal::ExplicitSeedSeq seq;
seq.generate(outputs, &outputs[kNumBlocks]);
for (uint32_t& seed : outputs) {
EXPECT_EQ(seed, 0);
}
}
TEST(ExplicitSeeqSeq, SeedMaterialIsForwardedIdentically) {
const size_t kNumBlocks = 128;
uint32_t seed_material[kNumBlocks];
std::random_device urandom{"/dev/urandom"};
for (uint32_t& seed : seed_material) {
seed = urandom();
}
absl::random_internal::ExplicitSeedSeq seq(seed_material,
&seed_material[kNumBlocks]);
// Check that output is same as seed-material provided to constructor.
{
const size_t kNumGenerated = kNumBlocks / 2;
uint32_t outputs[kNumGenerated];
seq.generate(outputs, &outputs[kNumGenerated]);
for (size_t i = 0; i < kNumGenerated; i++) {
EXPECT_EQ(outputs[i], seed_material[i]);
}
}
// Check that SeedSequence is stateless between invocations: Despite the last
// invocation of generate() only consuming half of the input-entropy, the same
// entropy will be recycled for the next invocation.
{
const size_t kNumGenerated = kNumBlocks;
uint32_t outputs[kNumGenerated];
seq.generate(outputs, &outputs[kNumGenerated]);
for (size_t i = 0; i < kNumGenerated; i++) {
EXPECT_EQ(outputs[i], seed_material[i]);
}
}
// Check that when more seed-material is asked for than is provided, nonzero
// values are still written.
{
const size_t kNumGenerated = kNumBlocks * 2;
uint32_t outputs[kNumGenerated];
seq.generate(outputs, &outputs[kNumGenerated]);
for (size_t i = 0; i < kNumGenerated; i++) {
EXPECT_EQ(outputs[i], seed_material[i % kNumBlocks]);
}
}
}
TEST(ExplicitSeedSeq, CopyAndMoveConstructors) {
using testing::Each;
using testing::Eq;
using testing::Not;
using testing::Pointwise;
uint32_t entropy[4];
std::random_device urandom("/dev/urandom");
for (uint32_t& entry : entropy) {
entry = urandom();
}
absl::random_internal::ExplicitSeedSeq seq_from_entropy(std::begin(entropy),
std::end(entropy));
// Copy constructor.
{
absl::random_internal::ExplicitSeedSeq seq_copy(seq_from_entropy);
EXPECT_EQ(seq_copy.size(), seq_from_entropy.size());
std::vector<uint32_t> seeds_1;
seeds_1.resize(1000, 0);
std::vector<uint32_t> seeds_2;
seeds_2.resize(1000, 1);
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
seq_copy.generate(seeds_2.begin(), seeds_2.end());
EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
}
// Assignment operator.
{
for (uint32_t& entry : entropy) {
entry = urandom();
}
absl::random_internal::ExplicitSeedSeq another_seq(std::begin(entropy),
std::end(entropy));
std::vector<uint32_t> seeds_1;
seeds_1.resize(1000, 0);
std::vector<uint32_t> seeds_2;
seeds_2.resize(1000, 0);
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
another_seq.generate(seeds_2.begin(), seeds_2.end());
// Assert precondition: Sequences generated by seed-sequences are not equal.
EXPECT_THAT(seeds_1, Not(Pointwise(Eq(), seeds_2)));
// Apply the assignment-operator.
another_seq = seq_from_entropy;
// Re-generate seeds.
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
another_seq.generate(seeds_2.begin(), seeds_2.end());
// Seeds generated by seed-sequences should now be equal.
EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
}
// Move constructor.
{
// Get seeds from seed-sequence constructed from entropy.
std::vector<uint32_t> seeds_1;
seeds_1.resize(1000, 0);
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
// Apply move-constructor move the sequence to another instance.
absl::random_internal::ExplicitSeedSeq moved_seq(
std::move(seq_from_entropy));
std::vector<uint32_t> seeds_2;
seeds_2.resize(1000, 1);
moved_seq.generate(seeds_2.begin(), seeds_2.end());
// Verify that seeds produced by moved-instance are the same as original.
EXPECT_THAT(seeds_1, Pointwise(Eq(), seeds_2));
// Verify that the moved-from instance now behaves like a
// default-constructed instance.
EXPECT_EQ(seq_from_entropy.size(), 0);
seq_from_entropy.generate(seeds_1.begin(), seeds_1.end());
EXPECT_THAT(seeds_1, Each(Eq(0)));
}
}
// 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/fast_uniform_bits.h"
#include <random>
#include "gtest/gtest.h"
namespace {
template <typename IntType>
class FastUniformBitsTypedTest : public ::testing::Test {};
using IntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>;
TYPED_TEST_SUITE(FastUniformBitsTypedTest, IntTypes);
TYPED_TEST(FastUniformBitsTypedTest, BasicTest) {
using Limits = std::numeric_limits<TypeParam>;
using FastBits = absl::random_internal::FastUniformBits<TypeParam>;
EXPECT_EQ(0, FastBits::min());
EXPECT_EQ(Limits::max(), FastBits::max());
constexpr int kIters = 10000;
std::random_device rd;
std::mt19937 gen(rd());
FastBits fast;
for (int i = 0; i < kIters; i++) {
const auto v = fast(gen);
EXPECT_LE(v, FastBits::max());
EXPECT_GE(v, FastBits::min());
}
}
TEST(FastUniformBitsTest, TypeBoundaries32) {
// Tests that FastUniformBits can adapt to 32-bit boundaries.
absl::random_internal::FastUniformBits<uint32_t, 1> a;
absl::random_internal::FastUniformBits<uint32_t, 31> b;
absl::random_internal::FastUniformBits<uint32_t, 32> c;
{
std::mt19937 gen; // 32-bit
a(gen);
b(gen);
c(gen);
}
{
std::mt19937_64 gen; // 64-bit
a(gen);
b(gen);
c(gen);
}
}
TEST(FastUniformBitsTest, TypeBoundaries64) {
// Tests that FastUniformBits can adapt to 64-bit boundaries.
absl::random_internal::FastUniformBits<uint64_t, 1> a;
absl::random_internal::FastUniformBits<uint64_t, 31> b;
absl::random_internal::FastUniformBits<uint64_t, 32> c;
absl::random_internal::FastUniformBits<uint64_t, 33> d;
absl::random_internal::FastUniformBits<uint64_t, 63> e;
absl::random_internal::FastUniformBits<uint64_t, 64> f;
{
std::mt19937 gen; // 32-bit
a(gen);
b(gen);
c(gen);
d(gen);
e(gen);
f(gen);
}
{
std::mt19937_64 gen; // 64-bit
a(gen);
b(gen);
c(gen);
d(gen);
e(gen);
f(gen);
}
}
class UrngOddbits {
public:
using result_type = uint8_t;
static constexpr result_type min() { return 1; }
static constexpr result_type max() { return 0xfe; }
result_type operator()() { return 2; }
};
class Urng4bits {
public:
using result_type = uint8_t;
static constexpr result_type min() { return 1; }
static constexpr result_type max() { return 0xf + 1; }
result_type operator()() { return 2; }
};
class Urng32bits {
public:
using result_type = uint32_t;
static constexpr result_type min() { return 0; }
static constexpr result_type max() { return 0xffffffff; }
result_type operator()() { return 1; }
};
// Compile-time test to validate the helper classes used by FastUniformBits
TEST(FastUniformBitsTest, FastUniformBitsDetails) {
using absl::random_internal::FastUniformBitsLoopingConstants;
using absl::random_internal::FastUniformBitsURBGConstants;
// 4-bit URBG
{
using constants = FastUniformBitsURBGConstants<Urng4bits>;
static_assert(constants::kPowerOfTwo == true,
"constants::kPowerOfTwo == false");
static_assert(constants::kRange == 16, "constants::kRange == false");
static_assert(constants::kRangeBits == 4, "constants::kRangeBits == false");
static_assert(constants::kRangeMask == 0x0f,
"constants::kRangeMask == false");
}
{
using looping = FastUniformBitsLoopingConstants<uint32_t, 31, Urng4bits>;
// To get 31 bits from a 4-bit generator, issue 8 calls and extract 4 bits
// per call on all except the first.
static_assert(looping::kN0 == 1, "looping::kN0");
static_assert(looping::kW0 == 3, "looping::kW0");
static_assert(looping::kM0 == 0x7, "looping::kM0");
// (The second set of calls, kN1, will not do anything.)
static_assert(looping::kN1 == 8, "looping::kN1");
static_assert(looping::kW1 == 4, "looping::kW1");
static_assert(looping::kM1 == 0xf, "looping::kM1");
}
// ~7-bit URBG
{
using constants = FastUniformBitsURBGConstants<UrngOddbits>;
static_assert(constants::kPowerOfTwo == false,
"constants::kPowerOfTwo == false");
static_assert(constants::kRange == 0xfe, "constants::kRange == 0xfe");
static_assert(constants::kRangeBits == 7, "constants::kRangeBits == 7");
static_assert(constants::kRangeMask == 0x7f,
"constants::kRangeMask == 0x7f");
}
{
using looping = FastUniformBitsLoopingConstants<uint64_t, 60, UrngOddbits>;
// To get 60 bits from a 7-bit generator, issue 10 calls and extract 6 bits
// per call, discarding the excess entropy.
static_assert(looping::kN0 == 10, "looping::kN0");
static_assert(looping::kW0 == 6, "looping::kW0");
static_assert(looping::kM0 == 0x3f, "looping::kM0");
// (The second set of calls, kN1, will not do anything.)
static_assert(looping::kN1 == 10, "looping::kN1");
static_assert(looping::kW1 == 7, "looping::kW1");
static_assert(looping::kM1 == 0x7f, "looping::kM1");
}
{
using looping = FastUniformBitsLoopingConstants<uint64_t, 63, UrngOddbits>;
// To get 63 bits from a 7-bit generator, issue 10 calls--the same as we
// would issue for 60 bits--however this time we use two groups. The first
// group (kN0) will issue 7 calls, extracting 6 bits per call.
static_assert(looping::kN0 == 7, "looping::kN0");
static_assert(looping::kW0 == 6, "looping::kW0");
static_assert(looping::kM0 == 0x3f, "looping::kM0");
// The second group (kN1) will issue 3 calls, extracting 7 bits per call.
static_assert(looping::kN1 == 10, "looping::kN1");
static_assert(looping::kW1 == 7, "looping::kW1");
static_assert(looping::kM1 == 0x7f, "looping::kM1");
}
}
TEST(FastUniformBitsTest, Urng4_VariousOutputs) {
// Tests that how values are composed; the single-bit deltas should be spread
// across each invocation.
Urng4bits urng4;
Urng32bits urng32;
// 8-bit types
{
absl::random_internal::FastUniformBits<uint8_t, 1> fast1;
EXPECT_EQ(0x1, fast1(urng4));
EXPECT_EQ(0x1, fast1(urng32));
}
{
absl::random_internal::FastUniformBits<uint8_t, 2> fast2;
EXPECT_EQ(0x1, fast2(urng4));
EXPECT_EQ(0x1, fast2(urng32));
}
{
absl::random_internal::FastUniformBits<uint8_t, 4> fast4;
EXPECT_EQ(0x1, fast4(urng4));
EXPECT_EQ(0x1, fast4(urng32));
}
{
absl::random_internal::FastUniformBits<uint8_t, 6> fast6;
EXPECT_EQ(0x9, fast6(urng4)); // b001001 (2x3)
EXPECT_EQ(0x1, fast6(urng32));
}
{
absl::random_internal::FastUniformBits<uint8_t, 6> fast7;
EXPECT_EQ(0x9, fast7(urng4)); // b00001001 (1x4 + 1x3)
EXPECT_EQ(0x1, fast7(urng32));
}
{
absl::random_internal::FastUniformBits<uint8_t> fast8;
EXPECT_EQ(0x11, fast8(urng4));
EXPECT_EQ(0x1, fast8(urng32));
}
// 16-bit types
{
absl::random_internal::FastUniformBits<uint16_t, 10> fast10;
EXPECT_EQ(0x91, fast10(urng4)); // b 0010010001 (2x3 + 1x4)
EXPECT_EQ(0x1, fast10(urng32));
}
{
absl::random_internal::FastUniformBits<uint16_t, 11> fast11;
EXPECT_EQ(0x111, fast11(urng4));
EXPECT_EQ(0x1, fast11(urng32));
}
{
absl::random_internal::FastUniformBits<uint16_t, 12> fast12;
EXPECT_EQ(0x111, fast12(urng4));
EXPECT_EQ(0x1, fast12(urng32));
}
{
absl::random_internal::FastUniformBits<uint16_t> fast16;
EXPECT_EQ(0x1111, fast16(urng4));
EXPECT_EQ(0x1, fast16(urng32));
}
// 32-bit types
{
absl::random_internal::FastUniformBits<uint32_t, 21> fast21;
EXPECT_EQ(0x49111, fast21(urng4)); // b 001001001 000100010001 (3x3 + 3x4)
EXPECT_EQ(0x1, fast21(urng32));
}
{
absl::random_internal::FastUniformBits<uint32_t, 24> fast24;
EXPECT_EQ(0x111111, fast24(urng4));
EXPECT_EQ(0x1, fast24(urng32));
}
{
absl::random_internal::FastUniformBits<uint32_t> fast32;
EXPECT_EQ(0x11111111, fast32(urng4));
EXPECT_EQ(0x1, fast32(urng32));
}
// 64-bit types
{
absl::random_internal::FastUniformBits<uint64_t, 5> fast5;
EXPECT_EQ(0x9, fast5(urng4));
EXPECT_EQ(0x1, fast5(urng32));
}
{
absl::random_internal::FastUniformBits<uint64_t, 48> fast48;
EXPECT_EQ(0x111111111111, fast48(urng4));
// computes in 2 steps, should be 24 << 24
EXPECT_EQ(0x000001000001, fast48(urng32));
}
{
absl::random_internal::FastUniformBits<uint64_t> fast64;
EXPECT_EQ(0x1111111111111111, fast64(urng4));
EXPECT_EQ(0x0000000100000001, fast64(urng32));
}
}
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment