40 { os << t } -> std::same_as<std::ostream&>;
44template <
typename F,
typename T>
48template <
typename S,
typename Sample>
50 std::invocable<S, const Sample&> && std::is_convertible_v<std::invoke_result_t<S, const Sample&>, std::string>;
59 case '"': o <<
"\\\"";
break;
60 case '\\': o <<
"\\\\";
break;
61 case '\b': o <<
"\\b";
break;
62 case '\f': o <<
"\\f";
break;
63 case '\n': o <<
"\\n";
break;
64 case '\r': o <<
"\\r";
break;
65 case '\t': o <<
"\\t";
break;
67 if (
'\x00' <= c && c <=
'\x1f')
69 o <<
"\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(c);
85inline size_t round_to(
size_t value,
size_t multiple)
noexcept
87 if (multiple == 0)
return value;
88 return ((value + multiple / 2) / multiple) * multiple;
91[[nodiscard]]
inline std::vector<size_t>
94 if (sample_count == 0 || max_sample_size < config.round_to) {
return {}; }
95 if (sample_count == 1) {
return {max_sample_size}; }
97 const double log_min = std::log10(
static_cast<double>(config.round_to));
98 const double log_max = std::log10(
static_cast<double>(max_sample_size));
100 std::set<size_t> seen;
103 const size_t oversample = sample_count * 3;
104 for (
size_t i = 0; i < oversample; ++i)
106 const double t = std::pow(
static_cast<double>(i) / (oversample - 1), config.bias);
107 const double log_size = log_min + t * (log_max - log_min);
108 const size_t raw_size =
static_cast<size_t>(std::round(std::pow(10.0, log_size)));
109 const size_t rounded =
round_to(raw_size, config.round_to);
111 if (rounded <= max_sample_size) { seen.insert(rounded); }
115 if (max_sample_size % config.round_to == 0) { seen.insert(max_sample_size); }
117 std::vector<size_t> all_sizes(seen.begin(), seen.end());
120 while (all_sizes.size() < sample_count)
122 const size_t last = all_sizes.back();
123 const size_t next = last + config.round_to;
124 if (next <= max_sample_size) { all_sizes.push_back(next); }
129 std::vector<size_t> final_sizes;
130 if (sample_count > 0 && !all_sizes.empty())
132 final_sizes.reserve(sample_count);
133 const size_t available = all_sizes.size();
135 if (sample_count == 1) { final_sizes.push_back(all_sizes.back()); }
138 for (
size_t i = 0; i < sample_count; ++i)
140 const double t =
static_cast<double>(i) / (sample_count - 1);
141 const size_t index =
static_cast<size_t>(std::round(t * (available - 1)));
142 final_sizes.push_back(all_sizes[index]);
148 if (!final_sizes.empty() && max_sample_size % config.round_to == 0) { final_sizes.back() = max_sample_size; }
153[[nodiscard]]
inline std::vector<size_t>
generate_sizes(
size_t sample_count,
size_t max_sample_size)
158template <
typename T, detail::FillerFunction<T> F>
159[[nodiscard]] std::vector<std::vector<T>>
generate_samples(F&& filler,
const std::vector<size_t>& sizes)
161 std::vector<std::vector<T>> result;
162 result.reserve(sizes.size());
164 for (
size_t size : sizes)
166 std::vector<T> sample;
167 filler(sample, size);
168 result.push_back(std::move(sample));
174template <std::ranges::range Iterable>
175requires detail::StreamInsertable<std::ranges::range_value_t<Iterable>>
178 if (std::ranges::empty(container)) {
return "[]"; }
180 auto it = std::ranges::begin(container);
181 std::string result = std::format(
"[{}", *it);
183 for (++it; it != std::ranges::end(container); ++it) { result += std::format(
", {}", *it); }
189template <std::ranges::range Container, detail::SampleSerializer<std::ranges::range_value_t<Container>> Serializer>
190void save_samples(
const Container& samples, Serializer&& serializer,
const std::filesystem::path& filename)
192 std::ofstream file(filename);
193 if (!file.is_open()) {
throw std::runtime_error(
"Error: Could not open file " + filename.string()); }
195 const auto ext = filename.extension();
199 for (
const auto& sample : samples) { file << serializer(sample) <<
"\n"; }
201 else if (ext ==
".csv")
203 file <<
"sample_id,sample_data\n";
205 for (
const auto& sample : samples) { file <<
id++ <<
"," << serializer(sample) <<
"\n"; }
207 else if (ext ==
".json")
211 for (
const auto& sample : samples)
213 if (!first) { file <<
",\n"; }
219 else {
throw std::runtime_error(
"Error: Unsupported file extension " + ext.string()); }
222template <std::ranges::range Container>
223requires detail::StreamInsertable<std::ranges::range_value_t<Container>>
224void save_samples(
const Container& samples,
const std::filesystem::path& filename)
Definition sample_utilities.hpp:45
Definition sample_utilities.hpp:49
Definition sample_utilities.hpp:39
std::string escape_json(const std::string &s) noexcept
Definition sample_utilities.hpp:52
Definition runtime_analyzer.hpp:28
size_t round_to(size_t value, size_t multiple) noexcept
Definition sample_utilities.hpp:85
std::string serialize_iterable(const Iterable &container)
Definition sample_utilities.hpp:176
std::vector< std::vector< T > > generate_samples(F &&filler, const std::vector< size_t > &sizes)
Definition sample_utilities.hpp:159
std::vector< size_t > generate_sizes(size_t sample_count, size_t max_sample_size, const SampleSizeConfig &config={})
Definition sample_utilities.hpp:92
void save_samples(const Container &samples, Serializer &&serializer, const std::filesystem::path &filename)
Definition sample_utilities.hpp:190
Definition sample_utilities.hpp:80
size_t round_to
Definition sample_utilities.hpp:81
double bias
Definition sample_utilities.hpp:82