uniform_helper.h 9.1 KB

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