28 { os << t } -> std::same_as<std::ostream&>;
32template <
typename F,
typename T>
36template <
typename S,
typename Sample>
38 std::invocable<S, const Sample&> && std::is_convertible_v<std::invoke_result_t<S, const Sample&>, std::string>;
47 case '"': o <<
"\\\"";
break;
48 case '\\': o <<
"\\\\";
break;
49 case '\b': o <<
"\\b";
break;
50 case '\f': o <<
"\\f";
break;
51 case '\n': o <<
"\\n";
break;
52 case '\r': o <<
"\\r";
break;
53 case '\t': o <<
"\\t";
break;
55 if (
'\x00' <= c && c <=
'\x1f')
57 o <<
"\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(c);
73inline size_t round_to(
size_t value,
size_t multiple)
noexcept
75 if (multiple == 0)
return value;
76 return ((value + multiple / 2) / multiple) * multiple;
79[[nodiscard]]
inline std::vector<size_t>
82 if (sample_count == 0 || max_sample_size < config.round_to) {
return {}; }
83 if (sample_count == 1) {
return {max_sample_size}; }
85 const double log_min = std::log10(
static_cast<double>(config.round_to));
86 const double log_max = std::log10(
static_cast<double>(max_sample_size));
88 std::set<size_t> seen;
91 const size_t oversample = sample_count * 3;
92 for (
size_t i = 0; i < oversample; ++i)
94 const double t = std::pow(
static_cast<double>(i) / (oversample - 1), config.bias);
95 const double log_size = log_min + t * (log_max - log_min);
96 const size_t raw_size =
static_cast<size_t>(std::round(std::pow(10.0, log_size)));
97 const size_t rounded =
round_to(raw_size, config.round_to);
99 if (rounded <= max_sample_size) { seen.insert(rounded); }
103 if (max_sample_size % config.round_to == 0) { seen.insert(max_sample_size); }
105 std::vector<size_t> all_sizes(seen.begin(), seen.end());
108 while (all_sizes.size() < sample_count)
110 const size_t last = all_sizes.back();
111 const size_t next = last + config.round_to;
112 if (next <= max_sample_size) { all_sizes.push_back(next); }
117 std::vector<size_t> final_sizes;
118 if (sample_count > 0 && !all_sizes.empty())
120 final_sizes.reserve(sample_count);
121 const size_t available = all_sizes.size();
123 if (sample_count == 1) { final_sizes.push_back(all_sizes.back()); }
126 for (
size_t i = 0; i < sample_count; ++i)
128 const double t =
static_cast<double>(i) / (sample_count - 1);
129 const size_t index =
static_cast<size_t>(std::round(t * (available - 1)));
130 final_sizes.push_back(all_sizes[index]);
136 if (!final_sizes.empty() && max_sample_size % config.round_to == 0) { final_sizes.back() = max_sample_size; }
141[[nodiscard]]
inline std::vector<size_t>
generate_sizes(
size_t sample_count,
size_t max_sample_size)
146template <
typename T, detail::FillerFunction<T> F>
147[[nodiscard]] std::vector<std::vector<T>>
generate_samples(F&& filler,
const std::vector<size_t>& sizes)
149 std::vector<std::vector<T>> result;
150 result.reserve(sizes.size());
152 for (
size_t size : sizes)
154 std::vector<T> sample;
155 filler(sample, size);
156 result.push_back(std::move(sample));
162template <std::ranges::range Iterable>
163requires detail::StreamInsertable<std::ranges::range_value_t<Iterable>>
166 if (std::ranges::empty(container)) {
return "[]"; }
168 auto it = std::ranges::begin(container);
169 std::string result = std::format(
"[{}", *it);
171 for (++it; it != std::ranges::end(container); ++it) { result += std::format(
", {}", *it); }
177template <std::ranges::range Container, detail::SampleSerializer<std::ranges::range_value_t<Container>> Serializer>
178void save_samples(
const Container& samples, Serializer&& serializer,
const std::filesystem::path& filename)
180 std::ofstream file(filename);
181 if (!file.is_open()) {
throw std::runtime_error(
"Error: Could not open file " + filename.string()); }
183 const auto ext = filename.extension();
187 for (
const auto& sample : samples) { file << serializer(sample) <<
"\n"; }
189 else if (ext ==
".csv")
191 file <<
"sample_id,sample_data\n";
193 for (
const auto& sample : samples) { file <<
id++ <<
"," << serializer(sample) <<
"\n"; }
195 else if (ext ==
".json")
199 for (
const auto& sample : samples)
201 if (!first) { file <<
",\n"; }
207 else {
throw std::runtime_error(
"Error: Unsupported file extension " + ext.string()); }
210template <std::ranges::range Container>
211requires detail::StreamInsertable<std::ranges::range_value_t<Container>>
212void save_samples(
const Container& samples,
const std::filesystem::path& filename)
Definition sample_utilities.hpp:33
Definition sample_utilities.hpp:37
Definition sample_utilities.hpp:27
std::string escape_json(const std::string &s) noexcept
Definition sample_utilities.hpp:40
Definition runtime_analyzer.hpp:16
size_t round_to(size_t value, size_t multiple) noexcept
Definition sample_utilities.hpp:73
std::string serialize_iterable(const Iterable &container)
Definition sample_utilities.hpp:164
std::vector< std::vector< T > > generate_samples(F &&filler, const std::vector< size_t > &sizes)
Definition sample_utilities.hpp:147
std::vector< size_t > generate_sizes(size_t sample_count, size_t max_sample_size, const SampleSizeConfig &config={})
Definition sample_utilities.hpp:80
void save_samples(const Container &samples, Serializer &&serializer, const std::filesystem::path &filename)
Definition sample_utilities.hpp:178
Definition sample_utilities.hpp:68
size_t round_to
Definition sample_utilities.hpp:69
double bias
Definition sample_utilities.hpp:70