blob: 2929407e14973c7dfb102e98687ab6bec01efe94 [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001// Copyright 2019 The Abseil Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15#ifndef ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
16#define ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_
17
18#include <cmath>
19#include <limits>
20#include <type_traits>
21
22#include "absl/meta/type_traits.h"
23
24namespace absl {
25template <typename IntType>
26class uniform_int_distribution;
27
28template <typename RealType>
29class uniform_real_distribution;
30
31// Interval tag types which specify whether the interval is open or closed
32// on either boundary.
33
34namespace random_internal {
35template <typename T>
36struct TagTypeCompare {};
37
38template <typename T>
39constexpr bool operator==(TagTypeCompare<T>, TagTypeCompare<T>) {
40 // Tags are mono-states. They always compare equal.
41 return true;
42}
43template <typename T>
44constexpr bool operator!=(TagTypeCompare<T>, TagTypeCompare<T>) {
45 return false;
46}
47
48} // namespace random_internal
49
50struct IntervalClosedClosedTag
51 : public random_internal::TagTypeCompare<IntervalClosedClosedTag> {};
52struct IntervalClosedOpenTag
53 : public random_internal::TagTypeCompare<IntervalClosedOpenTag> {};
54struct IntervalOpenClosedTag
55 : public random_internal::TagTypeCompare<IntervalOpenClosedTag> {};
56struct IntervalOpenOpenTag
57 : public random_internal::TagTypeCompare<IntervalOpenOpenTag> {};
58
59namespace random_internal {
60// The functions
61// uniform_lower_bound(tag, a, b)
62// and
63// uniform_upper_bound(tag, a, b)
64// are used as implementation-details for absl::Uniform().
65//
66// Conceptually,
67// [a, b] == [uniform_lower_bound(IntervalClosedClosed, a, b),
68// uniform_upper_bound(IntervalClosedClosed, a, b)]
69// (a, b) == [uniform_lower_bound(IntervalOpenOpen, a, b),
70// uniform_upper_bound(IntervalOpenOpen, a, b)]
71// [a, b) == [uniform_lower_bound(IntervalClosedOpen, a, b),
72// uniform_upper_bound(IntervalClosedOpen, a, b)]
73// (a, b] == [uniform_lower_bound(IntervalOpenClosed, a, b),
74// uniform_upper_bound(IntervalOpenClosed, a, b)]
75//
76template <typename IntType, typename Tag>
77typename absl::enable_if_t<
78 absl::conjunction<
79 std::is_integral<IntType>,
80 absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
81 std::is_same<Tag, IntervalOpenOpenTag>>>::value,
82 IntType>
83uniform_lower_bound(Tag, IntType a, IntType) {
84 return a + 1;
85}
86
87template <typename FloatType, typename Tag>
88typename absl::enable_if_t<
89 absl::conjunction<
90 std::is_floating_point<FloatType>,
91 absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>,
92 std::is_same<Tag, IntervalOpenOpenTag>>>::value,
93 FloatType>
94uniform_lower_bound(Tag, FloatType a, FloatType b) {
95 return std::nextafter(a, b);
96}
97
98template <typename NumType, typename Tag>
99typename absl::enable_if_t<
100 absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
101 std::is_same<Tag, IntervalClosedOpenTag>>::value,
102 NumType>
103uniform_lower_bound(Tag, NumType a, NumType) {
104 return a;
105}
106
107template <typename IntType, typename Tag>
108typename absl::enable_if_t<
109 absl::conjunction<
110 std::is_integral<IntType>,
111 absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
112 std::is_same<Tag, IntervalOpenOpenTag>>>::value,
113 IntType>
114uniform_upper_bound(Tag, IntType, IntType b) {
115 return b - 1;
116}
117
118template <typename FloatType, typename Tag>
119typename absl::enable_if_t<
120 absl::conjunction<
121 std::is_floating_point<FloatType>,
122 absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>,
123 std::is_same<Tag, IntervalOpenOpenTag>>>::value,
124 FloatType>
125uniform_upper_bound(Tag, FloatType, FloatType b) {
126 return b;
127}
128
129template <typename IntType, typename Tag>
130typename absl::enable_if_t<
131 absl::conjunction<
132 std::is_integral<IntType>,
133 absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
134 std::is_same<Tag, IntervalOpenClosedTag>>>::value,
135 IntType>
136uniform_upper_bound(Tag, IntType, IntType b) {
137 return b;
138}
139
140template <typename FloatType, typename Tag>
141typename absl::enable_if_t<
142 absl::conjunction<
143 std::is_floating_point<FloatType>,
144 absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>,
145 std::is_same<Tag, IntervalOpenClosedTag>>>::value,
146 FloatType>
147uniform_upper_bound(Tag, FloatType, FloatType b) {
148 return std::nextafter(b, (std::numeric_limits<FloatType>::max)());
149}
150
151template <typename NumType>
152using UniformDistribution =
153 typename std::conditional<std::is_integral<NumType>::value,
154 absl::uniform_int_distribution<NumType>,
155 absl::uniform_real_distribution<NumType>>::type;
156
157template <typename TagType, typename NumType>
158struct UniformDistributionWrapper : public UniformDistribution<NumType> {
159 explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi)
160 : UniformDistribution<NumType>(
161 uniform_lower_bound<NumType>(TagType{}, lo, hi),
162 uniform_upper_bound<NumType>(TagType{}, lo, hi)) {}
163};
164
165} // namespace random_internal
166} // namespace absl
167
168#endif // ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_