1#ifndef QUICKSTATS_SINGLE_QUANTILE_HPP
2#define QUICKSTATS_SINGLE_QUANTILE_HPP
13#include "sanisizer/sanisizer.hpp"
27template<
typename Output_>
28void configure_single_quantile(
29 const Output_ quantile,
30 const std::size_t num_m1,
31 std::size_t& upper_index,
32 Output_& upper_fraction,
35 if (quantile < 0 || quantile > 1) {
36 throw std::out_of_range(
"'quantile' should lie in [0, 1]");
39 const Output_ raw_index =
static_cast<Output_
>(num_m1) * quantile;
40 const Output_ raw_upper_index = std::ceil(raw_index);
41 const Output_ raw_lower_index = std::floor(raw_index);
45 const auto converted_upper_index = sanisizer::from_float<std::size_t>(raw_upper_index);
46 if (converted_upper_index <= num_m1) {
47 upper_index = converted_upper_index;
48 upper_fraction = raw_index - raw_lower_index;
49 skip_lower = (raw_upper_index == raw_lower_index);
68template<
typename Output_ =
double>
77 static_assert(std::is_floating_point<Output_>::value);
80 throw std::out_of_range(
"'num' should be positive");
82 const std::size_t num_m1 = num_total - 1;
84 configure_single_quantile(quantile, num_m1, my_upper_index, my_upper_fraction, my_skip_lower);
88 std::size_t my_num_total, my_upper_index;
89 Output_ my_upper_fraction;
115 template<
typename Input_>
117 const auto target = ptr + my_upper_index;
118 std::nth_element(ptr, target, ptr + my_num_total);
121 const Output_ upper = *target;
126 const Output_ lower = *std::max_element(ptr, target);
127 return interpolate(lower, upper, my_upper_fraction);
147 template<
typename Input_>
148 Output_
operator()(
const std::size_t num_non_zero, Input_*
const ptr)
const {
149 assert(num_non_zero <= my_num_total);
150 if (num_non_zero == my_num_total) {
152 }
else if (num_non_zero == 0) {
156 std::size_t num_negative = 0;
157 for (std::size_t i = 0; i < num_non_zero; ++i) {
158 num_negative += (ptr[i] < 0);
161 if (my_upper_index < num_negative) {
162 const auto target = ptr + my_upper_index;
163 std::nth_element(ptr, target, ptr + num_non_zero);
165 const Output_ upper = *target;
170 const Output_ lower = *std::max_element(ptr, target);
171 return interpolate(lower, upper, my_upper_fraction);
174 if (num_negative && my_upper_index == num_negative) {
181 const auto target = ptr + (my_upper_index - 1);
182 std::nth_element(ptr, target, ptr + num_non_zero);
183 return static_cast<Output_
>(*target) * (1 - my_upper_fraction);
186 const std::size_t num_zeros = my_num_total - num_non_zero;
187 const std::size_t num_not_positive = num_zeros + num_negative;
188 if (my_upper_index < num_not_positive) {
192 const auto target = ptr + (my_upper_index - num_zeros);
193 std::nth_element(ptr, target, ptr + num_non_zero);
194 const Output_ upper = *target;
199 if (my_upper_index == num_not_positive) {
200 return upper * my_upper_fraction;
203 const Output_ lower = *std::max_element(ptr, target);
204 return interpolate(lower, upper, my_upper_fraction);
212template<
typename Output_ =
double>
227template<
typename Output_>
237 my_quantile(quantile),
238 my_placeholder(options.placeholder)
240 if (max_num_total >= 2) {
241 sanisizer::resize(my_choices, max_num_total - 1);
254 std::vector<std::optional<SingleQuantileFixedNumber<Output_> > > my_choices;
256 Output_ my_placeholder;
263 return sanisizer::sum_unsafe<std::size_t>(my_choices.size(), 1);
286 template<
typename Input_>
287 Output_
operator()(
const std::size_t num_total, Input_* ptr) {
288 if (num_total == 0) {
289 return my_placeholder;
290 }
else if (num_total == 1) {
293 assert(sanisizer::is_less_than_or_equal(num_total - 1, my_choices.size()));
294 auto& ocalc = my_choices[num_total - 2];
295 if (!ocalc.has_value()) {
296 ocalc.emplace(num_total, my_quantile);
298 return (*ocalc)(ptr);
325 template<
typename Input_>
326 Output_
operator()(
const std::size_t num_total,
const std::size_t num_non_zero, Input_*
const values) {
327 if (num_total == 0) {
328 return my_placeholder;
329 }
else if (num_total == 1) {
330 return (num_non_zero ? *values : 0);
332 assert(sanisizer::is_less_than_or_equal(num_total - 1, my_choices.size()));
333 auto& ocalc = my_choices[num_total - 2];
334 if (!ocalc.has_value()) {
335 ocalc.emplace(num_total, my_quantile);
337 return (*ocalc)(num_non_zero, values);
Calculate a single quantile from a fixed number of elements.
Definition SingleQuantile.hpp:69
std::size_t get_num_total() const
Definition SingleQuantile.hpp:96
Output_ operator()(const std::size_t num_non_zero, Input_ *const ptr) const
Definition SingleQuantile.hpp:148
Output_ operator()(Input_ *const ptr) const
Definition SingleQuantile.hpp:116
SingleQuantileFixedNumber(const std::size_t num_total, const Output_ quantile)
Definition SingleQuantile.hpp:76
Calculate a quantile from a variable number of elements.
Definition SingleQuantile.hpp:228
std::size_t get_max_num_total() const
Definition SingleQuantile.hpp:262
Output_ operator()(const std::size_t num_total, const std::size_t num_non_zero, Input_ *const values)
Definition SingleQuantile.hpp:326
Output_ operator()(const std::size_t num_total, Input_ *ptr)
Definition SingleQuantile.hpp:287
SingleQuantileVariableNumber(const std::size_t max_num_total, const Output_ quantile, const SingleQuantileVariableNumberOptions< Output_ > &options)
Definition SingleQuantile.hpp:236
Quickly compute simple statistics.
Definition mad.hpp:15
constexpr Value_ nan_if_available_else_zero()
Definition utils.hpp:63
Options for SingleQuantileVariableNumber.
Definition SingleQuantile.hpp:213
Output_ placeholder
Definition SingleQuantile.hpp:217