uniform_helper.h (9203B)
1 // 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/base/config.h" 23 #include "absl/meta/type_traits.h" 24 #include "absl/random/internal/traits.h" 25 26 namespace absl { 27 ABSL_NAMESPACE_BEGIN 28 29 template <typename IntType> 30 class uniform_int_distribution; 31 32 template <typename RealType> 33 class uniform_real_distribution; 34 35 // Interval tag types which specify whether the interval is open or closed 36 // on either boundary. 37 38 namespace random_internal { 39 template <typename T> 40 struct TagTypeCompare {}; 41 42 template <typename T> 43 constexpr bool operator==(TagTypeCompare<T>, TagTypeCompare<T>) { 44 // Tags are mono-states. They always compare equal. 45 return true; 46 } 47 template <typename T> 48 constexpr bool operator!=(TagTypeCompare<T>, TagTypeCompare<T>) { 49 return false; 50 } 51 52 } // namespace random_internal 53 54 struct IntervalClosedClosedTag 55 : public random_internal::TagTypeCompare<IntervalClosedClosedTag> {}; 56 struct IntervalClosedOpenTag 57 : public random_internal::TagTypeCompare<IntervalClosedOpenTag> {}; 58 struct IntervalOpenClosedTag 59 : public random_internal::TagTypeCompare<IntervalOpenClosedTag> {}; 60 struct IntervalOpenOpenTag 61 : public random_internal::TagTypeCompare<IntervalOpenOpenTag> {}; 62 63 namespace random_internal { 64 65 // In the absence of an explicitly provided return-type, the template 66 // "uniform_inferred_return_t<A, B>" is used to derive a suitable type, based on 67 // the data-types of the endpoint-arguments {A lo, B hi}. 68 // 69 // Given endpoints {A lo, B hi}, one of {A, B} will be chosen as the 70 // return-type, if one type can be implicitly converted into the other, in a 71 // lossless way. The template "is_widening_convertible" implements the 72 // compile-time logic for deciding if such a conversion is possible. 73 // 74 // If no such conversion between {A, B} exists, then the overload for 75 // absl::Uniform() will be discarded, and the call will be ill-formed. 76 // Return-type for absl::Uniform() when the return-type is inferred. 77 template <typename A, typename B> 78 using uniform_inferred_return_t = 79 absl::enable_if_t<absl::disjunction<is_widening_convertible<A, B>, 80 is_widening_convertible<B, A>>::value, 81 typename std::conditional< 82 is_widening_convertible<A, B>::value, B, A>::type>; 83 84 // The functions 85 // uniform_lower_bound(tag, a, b) 86 // and 87 // uniform_upper_bound(tag, a, b) 88 // are used as implementation-details for absl::Uniform(). 89 // 90 // Conceptually, 91 // [a, b] == [uniform_lower_bound(IntervalClosedClosed, a, b), 92 // uniform_upper_bound(IntervalClosedClosed, a, b)] 93 // (a, b) == [uniform_lower_bound(IntervalOpenOpen, a, b), 94 // uniform_upper_bound(IntervalOpenOpen, a, b)] 95 // [a, b) == [uniform_lower_bound(IntervalClosedOpen, a, b), 96 // uniform_upper_bound(IntervalClosedOpen, a, b)] 97 // (a, b] == [uniform_lower_bound(IntervalOpenClosed, a, b), 98 // uniform_upper_bound(IntervalOpenClosed, a, b)] 99 // 100 template <typename IntType, typename Tag> 101 typename absl::enable_if_t< 102 absl::conjunction< 103 IsIntegral<IntType>, 104 absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, 105 std::is_same<Tag, IntervalOpenOpenTag>>>::value, 106 IntType> 107 uniform_lower_bound(Tag, IntType a, IntType) { 108 return a < (std::numeric_limits<IntType>::max)() ? (a + 1) : a; 109 } 110 111 template <typename FloatType, typename Tag> 112 typename absl::enable_if_t< 113 absl::conjunction< 114 std::is_floating_point<FloatType>, 115 absl::disjunction<std::is_same<Tag, IntervalOpenClosedTag>, 116 std::is_same<Tag, IntervalOpenOpenTag>>>::value, 117 FloatType> 118 uniform_lower_bound(Tag, FloatType a, FloatType b) { 119 return std::nextafter(a, b); 120 } 121 122 template <typename NumType, typename Tag> 123 typename absl::enable_if_t< 124 absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, 125 std::is_same<Tag, IntervalClosedOpenTag>>::value, 126 NumType> 127 uniform_lower_bound(Tag, NumType a, NumType) { 128 return a; 129 } 130 131 template <typename IntType, typename Tag> 132 typename absl::enable_if_t< 133 absl::conjunction< 134 IsIntegral<IntType>, 135 absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, 136 std::is_same<Tag, IntervalOpenOpenTag>>>::value, 137 IntType> 138 uniform_upper_bound(Tag, IntType, IntType b) { 139 return b > (std::numeric_limits<IntType>::min)() ? (b - 1) : b; 140 } 141 142 template <typename FloatType, typename Tag> 143 typename absl::enable_if_t< 144 absl::conjunction< 145 std::is_floating_point<FloatType>, 146 absl::disjunction<std::is_same<Tag, IntervalClosedOpenTag>, 147 std::is_same<Tag, IntervalOpenOpenTag>>>::value, 148 FloatType> 149 uniform_upper_bound(Tag, FloatType, FloatType b) { 150 return b; 151 } 152 153 template <typename IntType, typename Tag> 154 typename absl::enable_if_t< 155 absl::conjunction< 156 IsIntegral<IntType>, 157 absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, 158 std::is_same<Tag, IntervalOpenClosedTag>>>::value, 159 IntType> 160 uniform_upper_bound(Tag, IntType, IntType b) { 161 return b; 162 } 163 164 template <typename FloatType, typename Tag> 165 typename absl::enable_if_t< 166 absl::conjunction< 167 std::is_floating_point<FloatType>, 168 absl::disjunction<std::is_same<Tag, IntervalClosedClosedTag>, 169 std::is_same<Tag, IntervalOpenClosedTag>>>::value, 170 FloatType> 171 uniform_upper_bound(Tag, FloatType, FloatType b) { 172 return std::nextafter(b, (std::numeric_limits<FloatType>::max)()); 173 } 174 175 // Returns whether the bounds are valid for the underlying distribution. 176 // Inputs must have already been resolved via uniform_*_bound calls. 177 // 178 // The c++ standard constraints in [rand.dist.uni.int] are listed as: 179 // requires: lo <= hi. 180 // 181 // In the uniform_int_distrubtion, {lo, hi} are closed, closed. Thus: 182 // [0, 0] is legal. 183 // [0, 0) is not legal, but [0, 1) is, which translates to [0, 0]. 184 // (0, 1) is not legal, but (0, 2) is, which translates to [1, 1]. 185 // (0, 0] is not legal, but (0, 1] is, which translates to [1, 1]. 186 // 187 // The c++ standard constraints in [rand.dist.uni.real] are listed as: 188 // requires: lo <= hi. 189 // requires: (hi - lo) <= numeric_limits<T>::max() 190 // 191 // In the uniform_real_distribution, {lo, hi} are closed, open, Thus: 192 // [0, 0] is legal, which is [0, 0+epsilon). 193 // [0, 0) is legal. 194 // (0, 0) is not legal, but (0-epsilon, 0+epsilon) is. 195 // (0, 0] is not legal, but (0, 0+epsilon] is. 196 // 197 template <typename FloatType> 198 absl::enable_if_t<std::is_floating_point<FloatType>::value, bool> 199 is_uniform_range_valid(FloatType a, FloatType b) { 200 return a <= b && std::isfinite(b - a); 201 } 202 203 template <typename IntType> 204 absl::enable_if_t<IsIntegral<IntType>::value, bool> is_uniform_range_valid( 205 IntType a, IntType b) { 206 return a <= b; 207 } 208 209 // UniformDistribution selects either absl::uniform_int_distribution 210 // or absl::uniform_real_distribution depending on the NumType parameter. 211 template <typename NumType> 212 using UniformDistribution = 213 typename std::conditional<IsIntegral<NumType>::value, 214 absl::uniform_int_distribution<NumType>, 215 absl::uniform_real_distribution<NumType>>::type; 216 217 // UniformDistributionWrapper is used as the underlying distribution type 218 // by the absl::Uniform template function. It selects the proper Abseil 219 // uniform distribution and provides constructor overloads that match the 220 // expected parameter order as well as adjusting distribution bounds based 221 // on the tag. 222 template <typename NumType> 223 struct UniformDistributionWrapper : public UniformDistribution<NumType> { 224 template <typename TagType> 225 explicit UniformDistributionWrapper(TagType, NumType lo, NumType hi) 226 : UniformDistribution<NumType>( 227 uniform_lower_bound<NumType>(TagType{}, lo, hi), 228 uniform_upper_bound<NumType>(TagType{}, lo, hi)) {} 229 230 explicit UniformDistributionWrapper(NumType lo, NumType hi) 231 : UniformDistribution<NumType>( 232 uniform_lower_bound<NumType>(IntervalClosedOpenTag(), lo, hi), 233 uniform_upper_bound<NumType>(IntervalClosedOpenTag(), lo, hi)) {} 234 235 explicit UniformDistributionWrapper() 236 : UniformDistribution<NumType>(std::numeric_limits<NumType>::lowest(), 237 (std::numeric_limits<NumType>::max)()) {} 238 }; 239 240 } // namespace random_internal 241 ABSL_NAMESPACE_END 242 } // namespace absl 243 244 #endif // ABSL_RANDOM_INTERNAL_UNIFORM_HELPER_H_