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{
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::random_device rd;
43 std::mt19937 gen(rd());
44
45 using RangeValue = std::ranges::range_value_t<decltype(range)>;
46 auto consume_itr = [&itr]() -> decltype(auto)
47 {
48 using reference_type = std::ranges::range_reference_t<decltype(range)>;
49 if constexpr (std::is_reference_v<reference_type>)
50 {
51 return std::move(*itr);
52 }
53 else
54 {
55 return *itr;
56 }
57 };
58
59 RangeValue result = consume_itr();
60
61 ++itr;
62
63 std::size_t count = 1;
64
65 for (; itr != end; ++itr, ++count)
66 {
67 std::uniform_int_distribution<size_t> distrib(0, count);
68 if (distrib(gen) == 0)
69 {
70 result = consume_itr();
71 }
72 }
73
74 return result;
75}
76
92template <class T>
94{
95 unsigned weight;
97};
98
99template <class T>
100T weightedRandomElement(const std::vector<WeightedElement<T>>& data)
101{
102 if (data.empty())
103 {
104 throw std::invalid_argument{"Data is empty."};
105 }
106
107 const auto sumOfWeights =
108 std::accumulate(data.begin(), data.end(), 0u,
109 [](unsigned sum, const WeightedElement<T>& element) { return sum + element.weight; });
110
111 if (sumOfWeights == 0u)
112 {
113 throw std::invalid_argument{"Sum of weights cannot be zero."};
114 }
115
116 const auto targetWeightValue = number::integer(1u, sumOfWeights);
117
118 auto currentSum = 0u;
119
120 for (const auto& elem : data)
121 {
122 currentSum += elem.weight;
123
124 if (currentSum > targetWeightValue)
125 {
126 return elem.value;
127 }
128 }
129
130 return data.back().value;
131}
132
133}
Definition helper.h:10
T weightedRandomElement(const std::vector< WeightedElement< T > > &data)
Definition helper.h:100
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:31
Get a random element by weight from a vector.
Definition helper.h:94
unsigned weight
Definition helper.h:95
T value
Definition helper.h:96