Faker C++
Loading...
Searching...
No Matches
helper.h
Go to the documentation of this file.
1#pragma once
2
3#include <numeric>
4#include <random>
5#include <vector>
6
7#include "generator.h"
8#include "number.h"
9
11{
12template <typename T>
14 std::ranges::input_range<T> && (std::ranges::sized_range<T> || std::ranges::forward_range<T>);
15
16template <input_range_with_faster_size_compute_than_linear_rng Range>
17decltype(auto) randomElement(Range&& range)
18{
19 if (std::ranges::empty(range))
20 {
21 throw std::invalid_argument{"Range [start, end) is empty."};
22 }
23
24 const auto size = std::ranges::distance(range);
25
26 const auto index = number::integer(size - 1);
27
28 return (*std::ranges::next(range.begin(), index));
29}
30
31template <std::ranges::input_range Range>
32auto randomElement(Range&& range)
33{
34 auto const end = range.end();
35 auto itr = range.begin();
36
37 if (itr == end)
38 {
39 throw std::invalid_argument{"Range [start, end) is empty."};
40 }
41
42 std::mt19937_64& gen = getGenerator();
43
44 using RangeValue = std::ranges::range_value_t<decltype(range)>;
45 auto consume_itr = [&itr]() -> decltype(auto)
46 {
47 using reference_type = std::ranges::range_reference_t<decltype(range)>;
48 if constexpr (std::is_reference_v<reference_type>)
49 {
50 return std::move(*itr);
51 }
52 else
53 {
54 return *itr;
55 }
56 };
57
58 RangeValue result = consume_itr();
59
60 ++itr;
61
62 std::size_t count = 1;
63
64 for (; itr != end; ++itr, ++count)
65 {
66 std::uniform_int_distribution<size_t> distrib(0, count);
67 if (distrib(gen) == 0)
68 {
69 result = consume_itr();
70 }
71 }
72
73 return result;
74}
75
93template <input_range_with_faster_size_compute_than_linear_rng Range1,
94 input_range_with_faster_size_compute_than_linear_rng Range2>
95decltype(auto) randomElement(Range1&& range1, Range2&& range2)
96{
97 const auto size1 = std::ranges::distance(range1);
98 const auto size2 = std::ranges::distance(range2);
99
100 if (size1 + size2 == 0)
101 {
102 throw std::invalid_argument{"Ranges are empty."};
103 }
104
105 const auto index = number::integer(size1 + size2 - 1);
106
107 if (index < size1)
108 {
109 return (*std::ranges::next(range1.begin(), index));
110 }
111
112 return (*std::ranges::next(range2.begin(), index - size1));
113}
114
130template <class T>
132{
133 unsigned weight;
135};
136
137template <class T>
138T weightedRandomElement(const std::vector<WeightedElement<T>>& data)
139{
140 if (data.empty())
141 {
142 throw std::invalid_argument{"Data is empty."};
143 }
144
145 const auto sumOfWeights =
146 std::accumulate(data.begin(), data.end(), 0u,
147 [](unsigned sum, const WeightedElement<T>& element) { return sum + element.weight; });
148
149 if (sumOfWeights == 0u)
150 {
151 throw std::invalid_argument{"Sum of weights cannot be zero."};
152 }
153
154 const auto targetWeightValue = number::integer(1u, sumOfWeights);
155
156 auto currentSum = 0u;
157
158 for (const auto& elem : data)
159 {
160 currentSum += elem.weight;
161
162 if (currentSum > targetWeightValue)
163 {
164 return elem.value;
165 }
166 }
167
168 return data.back().value;
169}
170
171}
Definition helper.h:11
T weightedRandomElement(const std::vector< WeightedElement< T > > &data)
Definition helper.h:138
decltype(auto) randomElement(Range &&range)
Definition helper.h:17
I integer(I min, I max)
Generates a random integer number in the given range, bounds included.
Definition number.h:33
FAKER_CXX_EXPORT std::mt19937_64 & getGenerator()
Get a random element by weight from a vector.
Definition helper.h:132
unsigned weight
Definition helper.h:133
T value
Definition helper.h:134