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 "number.h"
8
9namespace faker::helper
10{
11template <typename T>
13 std::ranges::input_range<T> && (std::ranges::sized_range<T> || std::ranges::forward_range<T>);
14
15template <input_range_with_faster_size_compute_than_linear_rng Range>
16decltype(auto) randomElement(Range&& range)
17{
18 if (std::ranges::empty(range))
19 {
20 throw std::invalid_argument{"Range [start, end) is empty."};
21 }
22
23 const auto size = std::ranges::distance(range);
24
25 const auto index = number::integer(size - 1);
26
27 return (*std::ranges::next(range.begin(), index));
28}
29
30template <std::ranges::input_range Range>
31auto randomElement(Range&& range)
32{
33 auto const end = range.end();
34 auto itr = range.begin();
35
36 if (itr == end)
37 {
38 throw std::invalid_argument{"Range [start, end) is empty."};
39 }
40
41 std::random_device rd;
42 std::mt19937 gen(rd());
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
91template <class T>
93{
94 unsigned weight;
96};
97
98template <class T>
99T weightedRandomElement(const std::vector<WeightedElement<T>>& data)
100{
101 if (data.empty())
102 {
103 throw std::invalid_argument{"Data is empty."};
104 }
105
106 const auto sumOfWeights =
107 std::accumulate(data.begin(), data.end(), 0u,
108 [](unsigned sum, const WeightedElement<T>& element) { return sum + element.weight; });
109
110 if (sumOfWeights == 0u)
111 {
112 throw std::invalid_argument{"Sum of weights cannot be zero."};
113 }
114
115 const auto targetWeightValue = number::integer(1u, sumOfWeights);
116
117 auto currentSum = 0u;
118
119 for (const auto& elem : data)
120 {
121 currentSum += elem.weight;
122
123 if (currentSum > targetWeightValue)
124 {
125 return elem.value;
126 }
127 }
128
129 return data.back().value;
130}
131
132}
Definition helper.h:10
T weightedRandomElement(const std::vector< WeightedElement< T > > &data)
Definition helper.h:99
decltype(auto) randomElement(Range &&range)
Definition helper.h:16
I integer(I min, I max)
Generates a random integer number in the given range, bounds included.
Definition number.h:30
Get a random element by weight from a vector.
Definition helper.h:93
unsigned weight
Definition helper.h:94
T value
Definition helper.h:95