Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:55:38

0001 //  (C) Copyright Nick Thompson 2018.
0002 //  (C) Copyright Matt Borland 2020.
0003 //  Use, modification and distribution are subject to the
0004 //  Boost Software License, Version 1.0. (See accompanying file
0005 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_MATH_STATISTICS_UNIVARIATE_STATISTICS_HPP
0008 #define BOOST_MATH_STATISTICS_UNIVARIATE_STATISTICS_HPP
0009 
0010 #include <boost/math/statistics/detail/single_pass.hpp>
0011 #include <boost/math/tools/config.hpp>
0012 #include <boost/math/tools/assert.hpp>
0013 #include <algorithm>
0014 #include <iterator>
0015 #include <tuple>
0016 #include <cmath>
0017 #include <vector>
0018 #include <type_traits>
0019 #include <utility>
0020 #include <numeric>
0021 #include <list>
0022 
0023 #ifdef BOOST_MATH_EXEC_COMPATIBLE
0024 #include <execution>
0025 
0026 namespace boost::math::statistics {
0027 
0028 template<class ExecutionPolicy, class ForwardIterator>
0029 inline auto mean(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0030 {
0031     using Real = typename std::iterator_traits<ForwardIterator>::value_type;
0032     BOOST_MATH_ASSERT_MSG(first != last, "At least one sample is required to compute the mean.");
0033 
0034     if constexpr (std::is_integral_v<Real>)
0035     {
0036         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0037         {
0038             return detail::mean_sequential_impl<double>(first, last);
0039         }
0040         else
0041         {
0042             return std::reduce(exec, first, last, 0.0) / std::distance(first, last);
0043         }
0044     }
0045     else
0046     {
0047         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0048         {
0049             return detail::mean_sequential_impl<Real>(first, last);
0050         }
0051         else
0052         {
0053             return std::reduce(exec, first, last, Real(0.0)) / Real(std::distance(first, last));
0054         }
0055     }
0056 }
0057 
0058 template<class ExecutionPolicy, class Container>
0059 inline auto mean(ExecutionPolicy&& exec, Container const & v)
0060 {
0061     return mean(exec, std::cbegin(v), std::cend(v));
0062 }
0063 
0064 template<class ForwardIterator>
0065 inline auto mean(ForwardIterator first, ForwardIterator last)
0066 {
0067     return mean(std::execution::seq, first, last);
0068 }
0069 
0070 template<class Container>
0071 inline auto mean(Container const & v)
0072 {
0073     return mean(std::execution::seq, std::cbegin(v), std::cend(v));
0074 }
0075 
0076 template<class ExecutionPolicy, class ForwardIterator>
0077 inline auto variance(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0078 {
0079     using Real = typename std::iterator_traits<ForwardIterator>::value_type;
0080 
0081     if constexpr (std::is_integral_v<Real>)
0082     {
0083         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0084         {
0085            return std::get<2>(detail::variance_sequential_impl<std::tuple<double, double, double, double>>(first, last));
0086         }
0087         else
0088         {
0089             const auto results = detail::first_four_moments_parallel_impl<std::tuple<double, double, double, double, double>>(first, last);
0090             return std::get<1>(results) / std::get<4>(results);
0091         }
0092     }
0093     else
0094     {
0095         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0096         {
0097             return std::get<2>(detail::variance_sequential_impl<std::tuple<Real, Real, Real, Real>>(first, last));
0098         }
0099         else
0100         {
0101             const auto results = detail::first_four_moments_parallel_impl<std::tuple<Real, Real, Real, Real, Real>>(first, last);
0102             return std::get<1>(results) / std::get<4>(results);
0103         }
0104     }
0105 }
0106 
0107 template<class ExecutionPolicy, class Container>
0108 inline auto variance(ExecutionPolicy&& exec, Container const & v)
0109 {
0110     return variance(exec, std::cbegin(v), std::cend(v));
0111 }
0112 
0113 template<class ForwardIterator>
0114 inline auto variance(ForwardIterator first, ForwardIterator last)
0115 {
0116     return variance(std::execution::seq, first, last);
0117 }
0118 
0119 template<class Container>
0120 inline auto variance(Container const & v)
0121 {
0122     return variance(std::execution::seq, std::cbegin(v), std::cend(v));
0123 }
0124 
0125 template<class ExecutionPolicy, class ForwardIterator>
0126 inline auto sample_variance(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0127 {
0128     const auto n = std::distance(first, last);
0129     BOOST_MATH_ASSERT_MSG(n > 1, "At least two samples are required to compute the sample variance.");
0130     return n*variance(exec, first, last)/(n-1);
0131 }
0132 
0133 template<class ExecutionPolicy, class Container>
0134 inline auto sample_variance(ExecutionPolicy&& exec, Container const & v)
0135 {
0136     return sample_variance(exec, std::cbegin(v), std::cend(v));
0137 }
0138 
0139 template<class ForwardIterator>
0140 inline auto sample_variance(ForwardIterator first, ForwardIterator last)
0141 {
0142     return sample_variance(std::execution::seq, first, last);
0143 }
0144 
0145 template<class Container>
0146 inline auto sample_variance(Container const & v)
0147 {
0148     return sample_variance(std::execution::seq, std::cbegin(v), std::cend(v));
0149 }
0150 
0151 template<class ExecutionPolicy, class ForwardIterator>
0152 inline auto mean_and_sample_variance(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0153 {
0154     using Real = typename std::iterator_traits<ForwardIterator>::value_type;
0155 
0156     if constexpr (std::is_integral_v<Real>)
0157     {
0158         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0159         {
0160             const auto results = detail::variance_sequential_impl<std::tuple<double, double, double, double>>(first, last);
0161             return std::make_pair(std::get<0>(results), std::get<2>(results)*std::get<3>(results)/(std::get<3>(results)-1.0));
0162         }
0163         else
0164         {
0165             const auto results = detail::first_four_moments_parallel_impl<std::tuple<double, double, double, double, double>>(first, last);
0166             return std::make_pair(std::get<0>(results), std::get<1>(results) / (std::get<4>(results)-1.0));
0167         }
0168     }
0169     else
0170     {
0171         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0172         {
0173             const auto results = detail::variance_sequential_impl<std::tuple<Real, Real, Real, Real>>(first, last);
0174             return std::make_pair(std::get<0>(results), std::get<2>(results)*std::get<3>(results)/(std::get<3>(results)-Real(1)));
0175         }
0176         else
0177         {
0178             const auto results = detail::first_four_moments_parallel_impl<std::tuple<Real, Real, Real, Real, Real>>(first, last);
0179             return std::make_pair(std::get<0>(results), std::get<1>(results) / (std::get<4>(results)-Real(1)));
0180         }
0181     }
0182 }
0183 
0184 template<class ExecutionPolicy, class Container>
0185 inline auto mean_and_sample_variance(ExecutionPolicy&& exec, Container const & v)
0186 {
0187     return mean_and_sample_variance(exec, std::cbegin(v), std::cend(v));
0188 }
0189 
0190 template<class ForwardIterator>
0191 inline auto mean_and_sample_variance(ForwardIterator first, ForwardIterator last)
0192 {
0193     return mean_and_sample_variance(std::execution::seq, first, last);
0194 }
0195 
0196 template<class Container>
0197 inline auto mean_and_sample_variance(Container const & v)
0198 {
0199     return mean_and_sample_variance(std::execution::seq, std::cbegin(v), std::cend(v));
0200 }
0201 
0202 template<class ExecutionPolicy, class ForwardIterator>
0203 inline auto first_four_moments(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0204 {
0205     using Real = typename std::iterator_traits<ForwardIterator>::value_type;
0206 
0207     if constexpr (std::is_integral_v<Real>)
0208     {
0209         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0210         {
0211             const auto results = detail::first_four_moments_sequential_impl<std::tuple<double, double, double, double, double>>(first, last);
0212             return std::make_tuple(std::get<0>(results), std::get<1>(results) / std::get<4>(results), std::get<2>(results) / std::get<4>(results),
0213                                 std::get<3>(results) / std::get<4>(results));
0214         }
0215         else
0216         {
0217             const auto results = detail::first_four_moments_parallel_impl<std::tuple<double, double, double, double, double>>(first, last);
0218             return std::make_tuple(std::get<0>(results), std::get<1>(results) / std::get<4>(results), std::get<2>(results) / std::get<4>(results),
0219                                    std::get<3>(results) / std::get<4>(results));
0220         }
0221     }
0222     else
0223     {
0224         if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0225         {
0226             const auto results = detail::first_four_moments_sequential_impl<std::tuple<Real, Real, Real, Real, Real>>(first, last);
0227             return std::make_tuple(std::get<0>(results), std::get<1>(results) / std::get<4>(results), std::get<2>(results) / std::get<4>(results),
0228                                    std::get<3>(results) / std::get<4>(results));
0229         }
0230         else
0231         {
0232             const auto results = detail::first_four_moments_parallel_impl<std::tuple<Real, Real, Real, Real, Real>>(first, last);
0233             return std::make_tuple(std::get<0>(results), std::get<1>(results) / std::get<4>(results), std::get<2>(results) / std::get<4>(results),
0234                                    std::get<3>(results) / std::get<4>(results));
0235         }
0236     }
0237 }
0238 
0239 template<class ExecutionPolicy, class Container>
0240 inline auto first_four_moments(ExecutionPolicy&& exec, Container const & v)
0241 {
0242     return first_four_moments(exec, std::cbegin(v), std::cend(v));
0243 }
0244 
0245 template<class ForwardIterator>
0246 inline auto first_four_moments(ForwardIterator first, ForwardIterator last)
0247 {
0248     return first_four_moments(std::execution::seq, first, last);
0249 }
0250 
0251 template<class Container>
0252 inline auto first_four_moments(Container const & v)
0253 {
0254     return first_four_moments(std::execution::seq, std::cbegin(v), std::cend(v));
0255 }
0256 
0257 // https://prod.sandia.gov/techlib-noauth/access-control.cgi/2008/086212.pdf
0258 template<class ExecutionPolicy, class ForwardIterator>
0259 inline auto skewness(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0260 {
0261     using Real = typename std::iterator_traits<ForwardIterator>::value_type;
0262     using std::sqrt;
0263 
0264     if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0265     {
0266         if constexpr (std::is_integral_v<Real>)
0267         {
0268             return detail::skewness_sequential_impl<double>(first, last);
0269         }
0270         else
0271         {
0272             return detail::skewness_sequential_impl<Real>(first, last);
0273         }
0274     }
0275     else
0276     {
0277         const auto [M1, M2, M3, M4] = first_four_moments(exec, first, last);
0278         const auto n = std::distance(first, last);
0279         const auto var = M2/(n-1);
0280 
0281         if (M2 == 0)
0282         {
0283             // The limit is technically undefined, but the interpretation here is clear:
0284             // A constant dataset has no skewness.
0285             if constexpr (std::is_integral_v<Real>)
0286             {
0287                 return static_cast<double>(0);
0288             }
0289             else
0290             {
0291                 return Real(0);
0292             }
0293         }
0294         else
0295         {
0296             return M3/(M2*sqrt(var)) / Real(2);
0297         }
0298     }
0299 }
0300 
0301 template<class ExecutionPolicy, class Container>
0302 inline auto skewness(ExecutionPolicy&& exec, Container & v)
0303 {
0304     return skewness(exec, std::cbegin(v), std::cend(v));
0305 }
0306 
0307 template<class ForwardIterator>
0308 inline auto skewness(ForwardIterator first, ForwardIterator last)
0309 {
0310     return skewness(std::execution::seq, first, last);
0311 }
0312 
0313 template<class Container>
0314 inline auto skewness(Container const & v)
0315 {
0316     return skewness(std::execution::seq, std::cbegin(v), std::cend(v));
0317 }
0318 
0319 // Follows equation 1.6 of:
0320 // https://prod.sandia.gov/techlib-noauth/access-control.cgi/2008/086212.pdf
0321 template<class ExecutionPolicy, class ForwardIterator>
0322 inline auto kurtosis(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0323 {
0324     const auto [M1, M2, M3, M4] = first_four_moments(exec, first, last);
0325     if (M2 == 0)
0326     {
0327         return M2;
0328     }
0329     return M4/(M2*M2);
0330 }
0331 
0332 template<class ExecutionPolicy, class Container>
0333 inline auto kurtosis(ExecutionPolicy&& exec, Container const & v)
0334 {
0335     return kurtosis(exec, std::cbegin(v), std::cend(v));
0336 }
0337 
0338 template<class ForwardIterator>
0339 inline auto kurtosis(ForwardIterator first, ForwardIterator last)
0340 {
0341     return kurtosis(std::execution::seq, first, last);
0342 }
0343 
0344 template<class Container>
0345 inline auto kurtosis(Container const & v)
0346 {
0347     return kurtosis(std::execution::seq, std::cbegin(v), std::cend(v));
0348 }
0349 
0350 template<class ExecutionPolicy, class ForwardIterator>
0351 inline auto excess_kurtosis(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0352 {
0353     return kurtosis(exec, first, last) - 3;
0354 }
0355 
0356 template<class ExecutionPolicy, class Container>
0357 inline auto excess_kurtosis(ExecutionPolicy&& exec, Container const & v)
0358 {
0359     return excess_kurtosis(exec, std::cbegin(v), std::cend(v));
0360 }
0361 
0362 template<class ForwardIterator>
0363 inline auto excess_kurtosis(ForwardIterator first, ForwardIterator last)
0364 {
0365     return excess_kurtosis(std::execution::seq, first, last);
0366 }
0367 
0368 template<class Container>
0369 inline auto excess_kurtosis(Container const & v)
0370 {
0371     return excess_kurtosis(std::execution::seq, std::cbegin(v), std::cend(v));
0372 }
0373 
0374 
0375 template<class ExecutionPolicy, class RandomAccessIterator>
0376 auto median(ExecutionPolicy&& exec, RandomAccessIterator first, RandomAccessIterator last)
0377 {
0378     const auto num_elems = std::distance(first, last);
0379     BOOST_MATH_ASSERT_MSG(num_elems > 0, "The median of a zero length vector is undefined.");
0380     if (num_elems & 1)
0381     {
0382         auto middle = first + (num_elems - 1)/2;
0383         std::nth_element(exec, first, middle, last);
0384         return *middle;
0385     }
0386     else
0387     {
0388         auto middle = first + num_elems/2 - 1;
0389         std::nth_element(exec, first, middle, last);
0390         std::nth_element(exec, middle, middle+1, last);
0391         return (*middle + *(middle+1))/2;
0392     }
0393 }
0394 
0395 
0396 template<class ExecutionPolicy, class RandomAccessContainer>
0397 inline auto median(ExecutionPolicy&& exec, RandomAccessContainer & v)
0398 {
0399     return median(exec, std::begin(v), std::end(v));
0400 }
0401 
0402 template<class RandomAccessIterator>
0403 inline auto median(RandomAccessIterator first, RandomAccessIterator last)
0404 {
0405     return median(std::execution::seq, first, last);
0406 }
0407 
0408 template<class RandomAccessContainer>
0409 inline auto median(RandomAccessContainer & v)
0410 {
0411     return median(std::execution::seq, std::begin(v), std::end(v));
0412 }
0413 
0414 #if 0
0415 //
0416 // Parallel gini calculation is curently broken, see:
0417 // https://github.com/boostorg/math/issues/585
0418 // We will fix this at a later date, for now just use a serial implementation:
0419 //
0420 template<class ExecutionPolicy, class RandomAccessIterator>
0421 inline auto gini_coefficient(ExecutionPolicy&& exec, RandomAccessIterator first, RandomAccessIterator last)
0422 {
0423     using Real = typename std::iterator_traits<RandomAccessIterator>::value_type;
0424 
0425     if(!std::is_sorted(exec, first, last))
0426     {
0427         std::sort(exec, first, last);
0428     }
0429 
0430     if constexpr (std::is_same_v<std::remove_reference_t<decltype(exec)>, decltype(std::execution::seq)>)
0431     {
0432         if constexpr (std::is_integral_v<Real>)
0433         {
0434             return detail::gini_coefficient_sequential_impl<double>(first, last);
0435         }
0436         else
0437         {
0438             return detail::gini_coefficient_sequential_impl<Real>(first, last);
0439         }
0440     }
0441 
0442     else if constexpr (std::is_integral_v<Real>)
0443     {
0444         return detail::gini_coefficient_parallel_impl<double>(exec, first, last);
0445     }
0446 
0447     else
0448     {
0449         return detail::gini_coefficient_parallel_impl<Real>(exec, first, last);
0450     }
0451 }
0452 #else
0453 template<class ExecutionPolicy, class RandomAccessIterator>
0454 inline auto gini_coefficient(ExecutionPolicy&& exec, RandomAccessIterator first, RandomAccessIterator last)
0455 {
0456    using Real = typename std::iterator_traits<RandomAccessIterator>::value_type;
0457 
0458    if (!std::is_sorted(exec, first, last))
0459    {
0460       std::sort(exec, first, last);
0461    }
0462 
0463    if constexpr (std::is_integral_v<Real>)
0464    {
0465       return detail::gini_coefficient_sequential_impl<double>(first, last);
0466    }
0467    else
0468    {
0469       return detail::gini_coefficient_sequential_impl<Real>(first, last);
0470    }
0471 }
0472 #endif
0473 
0474 template<class ExecutionPolicy, class RandomAccessContainer>
0475 inline auto gini_coefficient(ExecutionPolicy&& exec, RandomAccessContainer & v)
0476 {
0477     return gini_coefficient(exec, std::begin(v), std::end(v));
0478 }
0479 
0480 template<class RandomAccessIterator>
0481 inline auto gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
0482 {
0483     return gini_coefficient(std::execution::seq, first, last);
0484 }
0485 
0486 template<class RandomAccessContainer>
0487 inline auto gini_coefficient(RandomAccessContainer & v)
0488 {
0489     return gini_coefficient(std::execution::seq, std::begin(v), std::end(v));
0490 }
0491 
0492 template<class ExecutionPolicy, class RandomAccessIterator>
0493 inline auto sample_gini_coefficient(ExecutionPolicy&& exec, RandomAccessIterator first, RandomAccessIterator last)
0494 {
0495     const auto n = std::distance(first, last);
0496     return n*gini_coefficient(exec, first, last)/(n-1);
0497 }
0498 
0499 template<class ExecutionPolicy, class RandomAccessContainer>
0500 inline auto sample_gini_coefficient(ExecutionPolicy&& exec, RandomAccessContainer & v)
0501 {
0502     return sample_gini_coefficient(exec, std::begin(v), std::end(v));
0503 }
0504 
0505 template<class RandomAccessIterator>
0506 inline auto sample_gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
0507 {
0508     return sample_gini_coefficient(std::execution::seq, first, last);
0509 }
0510 
0511 template<class RandomAccessContainer>
0512 inline auto sample_gini_coefficient(RandomAccessContainer & v)
0513 {
0514     return sample_gini_coefficient(std::execution::seq, std::begin(v), std::end(v));
0515 }
0516 
0517 template<class ExecutionPolicy, class RandomAccessIterator>
0518 auto median_absolute_deviation(ExecutionPolicy&& exec, RandomAccessIterator first, RandomAccessIterator last,
0519     typename std::iterator_traits<RandomAccessIterator>::value_type center=std::numeric_limits<typename std::iterator_traits<RandomAccessIterator>::value_type>::quiet_NaN())
0520 {
0521     using std::abs;
0522     using Real = typename std::iterator_traits<RandomAccessIterator>::value_type;
0523     using std::isnan;
0524     if (isnan(center))
0525     {
0526         center = boost::math::statistics::median(exec, first, last);
0527     }
0528     const auto num_elems = std::distance(first, last);
0529     BOOST_MATH_ASSERT_MSG(num_elems > 0, "The median of a zero-length vector is undefined.");
0530     auto comparator = [&center](Real a, Real b) { return abs(a-center) < abs(b-center);};
0531     if (num_elems & 1)
0532     {
0533         auto middle = first + (num_elems - 1)/2;
0534         std::nth_element(exec, first, middle, last, comparator);
0535         return abs(*middle-center);
0536     }
0537     else
0538     {
0539         auto middle = first + num_elems/2 - 1;
0540         std::nth_element(exec, first, middle, last, comparator);
0541         std::nth_element(exec, middle, middle+1, last, comparator);
0542         return (abs(*middle-center) + abs(*(middle+1)-center))/abs(static_cast<Real>(2));
0543     }
0544 }
0545 
0546 template<class ExecutionPolicy, class RandomAccessContainer>
0547 inline auto median_absolute_deviation(ExecutionPolicy&& exec, RandomAccessContainer & v,
0548     typename RandomAccessContainer::value_type center=std::numeric_limits<typename RandomAccessContainer::value_type>::quiet_NaN())
0549 {
0550     return median_absolute_deviation(exec, std::begin(v), std::end(v), center);
0551 }
0552 
0553 template<class RandomAccessIterator>
0554 inline auto median_absolute_deviation(RandomAccessIterator first, RandomAccessIterator last,
0555     typename RandomAccessIterator::value_type center=std::numeric_limits<typename RandomAccessIterator::value_type>::quiet_NaN())
0556 {
0557     return median_absolute_deviation(std::execution::seq, first, last, center);
0558 }
0559 
0560 template<class RandomAccessContainer>
0561 inline auto median_absolute_deviation(RandomAccessContainer & v,
0562     typename RandomAccessContainer::value_type center=std::numeric_limits<typename RandomAccessContainer::value_type>::quiet_NaN())
0563 {
0564     return median_absolute_deviation(std::execution::seq, std::begin(v), std::end(v), center);
0565 }
0566 
0567 template<class ExecutionPolicy, class ForwardIterator>
0568 auto interquartile_range(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0569 {
0570     using Real = typename std::iterator_traits<ForwardIterator>::value_type;
0571     static_assert(!std::is_integral_v<Real>, "Integer values have not yet been implemented.");
0572     auto m = std::distance(first,last);
0573     BOOST_MATH_ASSERT_MSG(m >= 3, "At least 3 samples are required to compute the interquartile range.");
0574     auto k = m/4;
0575     auto j = m - (4*k);
0576     // m = 4k+j.
0577     // If j = 0 or j = 1, then there are an even number of samples below the median, and an even number above the median.
0578     //    Then we must average adjacent elements to get the quartiles.
0579     // If j = 2 or j = 3, there are an odd number of samples above and below the median, these elements may be directly extracted to get the quartiles.
0580 
0581     if (j==2 || j==3)
0582     {
0583         auto q1 = first + k;
0584         auto q3 = first + 3*k + j - 1;
0585         std::nth_element(exec, first, q1, last);
0586         Real Q1 = *q1;
0587         std::nth_element(exec, q1, q3, last);
0588         Real Q3 = *q3;
0589         return Q3 - Q1;
0590     } else {
0591         // j == 0 or j==1:
0592         auto q1 = first + k - 1;
0593         auto q3 = first + 3*k - 1 + j;
0594         std::nth_element(exec, first, q1, last);
0595         Real a = *q1;
0596         std::nth_element(exec, q1, q1 + 1, last);
0597         Real b = *(q1 + 1);
0598         Real Q1 = (a+b)/2;
0599         std::nth_element(exec, q1, q3, last);
0600         a = *q3;
0601         std::nth_element(exec, q3, q3 + 1, last);
0602         b = *(q3 + 1);
0603         Real Q3 = (a+b)/2;
0604         return Q3 - Q1;
0605     }
0606 }
0607 
0608 template<class ExecutionPolicy, class RandomAccessContainer>
0609 inline auto interquartile_range(ExecutionPolicy&& exec, RandomAccessContainer & v)
0610 {
0611     return interquartile_range(exec, std::begin(v), std::end(v));
0612 }
0613 
0614 template<class RandomAccessIterator>
0615 inline auto interquartile_range(RandomAccessIterator first, RandomAccessIterator last)
0616 {
0617     return interquartile_range(std::execution::seq, first, last);
0618 }
0619 
0620 template<class RandomAccessContainer>
0621 inline auto interquartile_range(RandomAccessContainer & v)
0622 {
0623     return interquartile_range(std::execution::seq, std::begin(v), std::end(v));
0624 }
0625 
0626 template<class ExecutionPolicy, class ForwardIterator, class OutputIterator>
0627 inline OutputIterator mode(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last, OutputIterator output)
0628 {
0629     if(!std::is_sorted(exec, first, last))
0630     {
0631         if constexpr (std::is_same_v<typename std::iterator_traits<ForwardIterator>::iterator_category(), std::random_access_iterator_tag>)
0632         {
0633             std::sort(exec, first, last);
0634         }
0635         else
0636         {
0637             BOOST_MATH_ASSERT("Data must be sorted for sequential mode calculation");
0638         }
0639     }
0640 
0641     return detail::mode_impl(first, last, output);
0642 }
0643 
0644 template<class ExecutionPolicy, class Container, class OutputIterator>
0645 inline OutputIterator mode(ExecutionPolicy&& exec, Container & v, OutputIterator output)
0646 {
0647     return mode(exec, std::begin(v), std::end(v), output);
0648 }
0649 
0650 template<class ForwardIterator, class OutputIterator>
0651 inline OutputIterator mode(ForwardIterator first, ForwardIterator last, OutputIterator output)
0652 {
0653     return mode(std::execution::seq, first, last, output);
0654 }
0655 
0656 // Requires enable_if_t to not clash with impl that returns std::list
0657 // Very ugly. std::is_execution_policy_v returns false for the std::execution objects and decltype of the objects (e.g. std::execution::seq)
0658 template<class Container, class OutputIterator, std::enable_if_t<!std::is_convertible_v<std::execution::sequenced_policy, Container> &&
0659                                                                  !std::is_convertible_v<std::execution::parallel_unsequenced_policy, Container> &&
0660                                                                  !std::is_convertible_v<std::execution::parallel_policy, Container>
0661                                                                  #if __cpp_lib_execution > 201900
0662                                                                  && !std::is_convertible_v<std::execution::unsequenced_policy, Container>
0663                                                                  #endif
0664                                                                  , bool> = true>
0665 inline OutputIterator mode(Container & v, OutputIterator output)
0666 {
0667     return mode(std::execution::seq, std::begin(v), std::end(v), output);
0668 }
0669 
0670 // std::list is the return type for the proposed STL stats library
0671 
0672 template<class ExecutionPolicy, class ForwardIterator, class Real = typename std::iterator_traits<ForwardIterator>::value_type>
0673 inline auto mode(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last)
0674 {
0675     std::list<Real> modes;
0676     mode(exec, first, last, std::inserter(modes, modes.begin()));
0677     return modes;
0678 }
0679 
0680 template<class ExecutionPolicy, class Container>
0681 inline auto mode(ExecutionPolicy&& exec, Container & v)
0682 {
0683     return mode(exec, std::begin(v), std::end(v));
0684 }
0685 
0686 template<class ForwardIterator>
0687 inline auto mode(ForwardIterator first, ForwardIterator last)
0688 {
0689     return mode(std::execution::seq, first, last);
0690 }
0691 
0692 template<class Container>
0693 inline auto mode(Container & v)
0694 {
0695     return mode(std::execution::seq, std::begin(v), std::end(v));
0696 }
0697 
0698 } // Namespace boost::math::statistics
0699 
0700 #else // Backwards compatible bindings for C++11 or execution is not implemented
0701 
0702 namespace boost { namespace math { namespace statistics {
0703 
0704 template<bool B, class T = void>
0705 using enable_if_t = typename std::enable_if<B, T>::type;
0706 
0707 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0708          enable_if_t<std::is_integral<Real>::value, bool> = true>
0709 inline double mean(const ForwardIterator first, const ForwardIterator last)
0710 {
0711     BOOST_MATH_ASSERT_MSG(first != last, "At least one sample is required to compute the mean.");
0712     return detail::mean_sequential_impl<double>(first, last);
0713 }
0714 
0715 template<class Container, typename Real = typename Container::value_type,
0716          enable_if_t<std::is_integral<Real>::value, bool> = true>
0717 inline double mean(const Container& c)
0718 {
0719     return mean(std::begin(c), std::end(c));
0720 }
0721 
0722 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0723          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0724 inline Real mean(const ForwardIterator first, const ForwardIterator last)
0725 {
0726     BOOST_MATH_ASSERT_MSG(first != last, "At least one sample is required to compute the mean.");
0727     return detail::mean_sequential_impl<Real>(first, last);
0728 }
0729 
0730 template<class Container, typename Real = typename Container::value_type,
0731          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0732 inline Real mean(const Container& c)
0733 {
0734     return mean(std::begin(c), std::end(c));
0735 }
0736 
0737 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0738          enable_if_t<std::is_integral<Real>::value, bool> = true>
0739 inline double variance(const ForwardIterator first, const ForwardIterator last)
0740 {
0741     return std::get<2>(detail::variance_sequential_impl<std::tuple<double, double, double, double>>(first, last));
0742 }
0743 
0744 template<class Container, typename Real = typename Container::value_type,
0745          enable_if_t<std::is_integral<Real>::value, bool> = true>
0746 inline double variance(const Container& c)
0747 {
0748     return variance(std::begin(c), std::end(c));
0749 }
0750 
0751 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0752          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0753 inline Real variance(const ForwardIterator first, const ForwardIterator last)
0754 {
0755     return std::get<2>(detail::variance_sequential_impl<std::tuple<Real, Real, Real, Real>>(first, last));
0756 
0757 }
0758 
0759 template<class Container, typename Real = typename Container::value_type,
0760          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0761 inline Real variance(const Container& c)
0762 {
0763     return variance(std::begin(c), std::end(c));
0764 }
0765 
0766 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0767          enable_if_t<std::is_integral<Real>::value, bool> = true>
0768 inline double sample_variance(const ForwardIterator first, const ForwardIterator last)
0769 {
0770     const auto n = std::distance(first, last);
0771     BOOST_MATH_ASSERT_MSG(n > 1, "At least two samples are required to compute the sample variance.");
0772     return n*variance(first, last)/(n-1);
0773 }
0774 
0775 template<class Container, typename Real = typename Container::value_type,
0776          enable_if_t<std::is_integral<Real>::value, bool> = true>
0777 inline double sample_variance(const Container& c)
0778 {
0779     return sample_variance(std::begin(c), std::end(c));
0780 }
0781 
0782 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0783          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0784 inline Real sample_variance(const ForwardIterator first, const ForwardIterator last)
0785 {
0786     const auto n = std::distance(first, last);
0787     BOOST_MATH_ASSERT_MSG(n > 1, "At least two samples are required to compute the sample variance.");
0788     return n*variance(first, last)/(n-1);
0789 }
0790 
0791 template<class Container, typename Real = typename Container::value_type,
0792          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0793 inline Real sample_variance(const Container& c)
0794 {
0795     return sample_variance(std::begin(c), std::end(c));
0796 }
0797 
0798 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0799          enable_if_t<std::is_integral<Real>::value, bool> = true>
0800 inline std::pair<double, double> mean_and_sample_variance(const ForwardIterator first, const ForwardIterator last)
0801 {
0802     const auto results = detail::variance_sequential_impl<std::tuple<double, double, double, double>>(first, last);
0803     return std::make_pair(std::get<0>(results), std::get<3>(results)*std::get<2>(results)/(std::get<3>(results)-1.0));
0804 }
0805 
0806 template<class Container, typename Real = typename Container::value_type,
0807          enable_if_t<std::is_integral<Real>::value, bool> = true>
0808 inline std::pair<double, double> mean_and_sample_variance(const Container& c)
0809 {
0810     return mean_and_sample_variance(std::begin(c), std::end(c));
0811 }
0812 
0813 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0814          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0815 inline std::pair<Real, Real> mean_and_sample_variance(const ForwardIterator first, const ForwardIterator last)
0816 {
0817     const auto results = detail::variance_sequential_impl<std::tuple<Real, Real, Real, Real>>(first, last);
0818     return std::make_pair(std::get<0>(results), std::get<3>(results)*std::get<2>(results)/(std::get<3>(results)-Real(1)));
0819 }
0820 
0821 template<class Container, typename Real = typename Container::value_type,
0822          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0823 inline std::pair<Real, Real> mean_and_sample_variance(const Container& c)
0824 {
0825     return mean_and_sample_variance(std::begin(c), std::end(c));
0826 }
0827 
0828 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0829          enable_if_t<std::is_integral<Real>::value, bool> = true>
0830 inline std::tuple<double, double, double, double> first_four_moments(const ForwardIterator first, const ForwardIterator last)
0831 {
0832     const auto results = detail::first_four_moments_sequential_impl<std::tuple<double, double, double, double, double>>(first, last);
0833     return std::make_tuple(std::get<0>(results), std::get<1>(results) / std::get<4>(results), std::get<2>(results) / std::get<4>(results),
0834                            std::get<3>(results) / std::get<4>(results));
0835 }
0836 
0837 template<class Container, typename Real = typename Container::value_type,
0838          enable_if_t<std::is_integral<Real>::value, bool> = true>
0839 inline std::tuple<double, double, double, double> first_four_moments(const Container& c)
0840 {
0841     return first_four_moments(std::begin(c), std::end(c));
0842 }
0843 
0844 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0845          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0846 inline std::tuple<Real, Real, Real, Real> first_four_moments(const ForwardIterator first, const ForwardIterator last)
0847 {
0848     const auto results = detail::first_four_moments_sequential_impl<std::tuple<Real, Real, Real, Real, Real>>(first, last);
0849     return std::make_tuple(std::get<0>(results), std::get<1>(results) / std::get<4>(results), std::get<2>(results) / std::get<4>(results),
0850                            std::get<3>(results) / std::get<4>(results));
0851 }
0852 
0853 template<class Container, typename Real = typename Container::value_type,
0854          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0855 inline std::tuple<Real, Real, Real, Real> first_four_moments(const Container& c)
0856 {
0857     return first_four_moments(std::begin(c), std::end(c));
0858 }
0859 
0860 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0861          enable_if_t<std::is_integral<Real>::value, bool> = true>
0862 inline double skewness(const ForwardIterator first, const ForwardIterator last)
0863 {
0864     return detail::skewness_sequential_impl<double>(first, last);
0865 }
0866 
0867 template<class Container, typename Real = typename Container::value_type,
0868          enable_if_t<std::is_integral<Real>::value, bool> = true>
0869 inline double skewness(const Container& c)
0870 {
0871     return skewness(std::begin(c), std::end(c));
0872 }
0873 
0874 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0875          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0876 inline Real skewness(const ForwardIterator first, const ForwardIterator last)
0877 {
0878     return detail::skewness_sequential_impl<Real>(first, last);
0879 }
0880 
0881 template<class Container, typename Real = typename Container::value_type,
0882          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0883 inline Real skewness(const Container& c)
0884 {
0885     return skewness(std::begin(c), std::end(c));
0886 }
0887 
0888 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0889          enable_if_t<std::is_integral<Real>::value, bool> = true>
0890 inline double kurtosis(const ForwardIterator first, const ForwardIterator last)
0891 {
0892     std::tuple<double, double, double, double> M = first_four_moments(first, last);
0893 
0894     if(std::get<1>(M) == 0)
0895     {
0896         return std::get<1>(M);
0897     }
0898     else
0899     {
0900         return std::get<3>(M)/(std::get<1>(M)*std::get<1>(M));
0901     }
0902 }
0903 
0904 template<class Container, typename Real = typename Container::value_type,
0905          enable_if_t<std::is_integral<Real>::value, bool> = true>
0906 inline double kurtosis(const Container& c)
0907 {
0908     return kurtosis(std::begin(c), std::end(c));
0909 }
0910 
0911 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0912          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0913 inline Real kurtosis(const ForwardIterator first, const ForwardIterator last)
0914 {
0915     std::tuple<Real, Real, Real, Real> M = first_four_moments(first, last);
0916 
0917     if(std::get<1>(M) == 0)
0918     {
0919         return std::get<1>(M);
0920     }
0921     else
0922     {
0923         return std::get<3>(M)/(std::get<1>(M)*std::get<1>(M));
0924     }
0925 }
0926 
0927 template<class Container, typename Real = typename Container::value_type,
0928          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0929 inline Real kurtosis(const Container& c)
0930 {
0931     return kurtosis(std::begin(c), std::end(c));
0932 }
0933 
0934 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0935          enable_if_t<std::is_integral<Real>::value, bool> = true>
0936 inline double excess_kurtosis(const ForwardIterator first, const ForwardIterator last)
0937 {
0938     return kurtosis(first, last) - 3;
0939 }
0940 
0941 template<class Container, typename Real = typename Container::value_type,
0942          enable_if_t<std::is_integral<Real>::value, bool> = true>
0943 inline double excess_kurtosis(const Container& c)
0944 {
0945     return excess_kurtosis(std::begin(c), std::end(c));
0946 }
0947 
0948 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type,
0949          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0950 inline Real excess_kurtosis(const ForwardIterator first, const ForwardIterator last)
0951 {
0952     return kurtosis(first, last) - 3;
0953 }
0954 
0955 template<class Container, typename Real = typename Container::value_type,
0956          enable_if_t<!std::is_integral<Real>::value, bool> = true>
0957 inline Real excess_kurtosis(const Container& c)
0958 {
0959     return excess_kurtosis(std::begin(c), std::end(c));
0960 }
0961 
0962 template<class RandomAccessIterator, typename Real = typename std::iterator_traits<RandomAccessIterator>::value_type>
0963 Real median(RandomAccessIterator first, RandomAccessIterator last)
0964 {
0965     const auto num_elems = std::distance(first, last);
0966     BOOST_MATH_ASSERT_MSG(num_elems > 0, "The median of a zero length vector is undefined.");
0967     if (num_elems & 1)
0968     {
0969         auto middle = first + (num_elems - 1)/2;
0970         std::nth_element(first, middle, last);
0971         return *middle;
0972     }
0973     else
0974     {
0975         auto middle = first + num_elems/2 - 1;
0976         std::nth_element(first, middle, last);
0977         std::nth_element(middle, middle+1, last);
0978         return (*middle + *(middle+1))/2;
0979     }
0980 }
0981 
0982 template<class RandomAccessContainer, typename Real = typename RandomAccessContainer::value_type>
0983 inline Real median(RandomAccessContainer& c)
0984 {
0985     return median(std::begin(c), std::end(c));
0986 }
0987 
0988 template<class RandomAccessIterator, typename Real = typename std::iterator_traits<RandomAccessIterator>::value_type,
0989          enable_if_t<std::is_integral<Real>::value, bool> = true>
0990 inline double gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
0991 {
0992     if(!std::is_sorted(first, last))
0993     {
0994         std::sort(first, last);
0995     }
0996 
0997     return detail::gini_coefficient_sequential_impl<double>(first, last);
0998 }
0999 
1000 template<class RandomAccessContainer, typename Real = typename RandomAccessContainer::value_type,
1001          enable_if_t<std::is_integral<Real>::value, bool> = true>
1002 inline double gini_coefficient(RandomAccessContainer& c)
1003 {
1004     return gini_coefficient(std::begin(c), std::end(c));
1005 }
1006 
1007 template<class RandomAccessIterator, typename Real = typename std::iterator_traits<RandomAccessIterator>::value_type,
1008          enable_if_t<!std::is_integral<Real>::value, bool> = true>
1009 inline Real gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
1010 {
1011     if(!std::is_sorted(first, last))
1012     {
1013         std::sort(first, last);
1014     }
1015 
1016     return detail::gini_coefficient_sequential_impl<Real>(first, last);
1017 }
1018 
1019 template<class RandomAccessContainer, typename Real = typename RandomAccessContainer::value_type,
1020          enable_if_t<!std::is_integral<Real>::value, bool> = true>
1021 inline Real gini_coefficient(RandomAccessContainer& c)
1022 {
1023     return gini_coefficient(std::begin(c), std::end(c));
1024 }
1025 
1026 template<class RandomAccessIterator, typename Real = typename std::iterator_traits<RandomAccessIterator>::value_type,
1027          enable_if_t<std::is_integral<Real>::value, bool> = true>
1028 inline double sample_gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
1029 {
1030     const auto n = std::distance(first, last);
1031     return n*gini_coefficient(first, last)/(n-1);
1032 }
1033 
1034 template<class RandomAccessContainer, typename Real = typename RandomAccessContainer::value_type,
1035          enable_if_t<std::is_integral<Real>::value, bool> = true>
1036 inline double sample_gini_coefficient(RandomAccessContainer& c)
1037 {
1038     return sample_gini_coefficient(std::begin(c), std::end(c));
1039 }
1040 
1041 template<class RandomAccessIterator, typename Real = typename std::iterator_traits<RandomAccessIterator>::value_type,
1042          enable_if_t<!std::is_integral<Real>::value, bool> = true>
1043 inline Real sample_gini_coefficient(RandomAccessIterator first, RandomAccessIterator last)
1044 {
1045     const auto n = std::distance(first, last);
1046     return n*gini_coefficient(first, last)/(n-1);
1047 }
1048 
1049 template<class RandomAccessContainer, typename Real = typename RandomAccessContainer::value_type,
1050          enable_if_t<!std::is_integral<Real>::value, bool> = true>
1051 inline Real sample_gini_coefficient(RandomAccessContainer& c)
1052 {
1053     return sample_gini_coefficient(std::begin(c), std::end(c));
1054 }
1055 
1056 template<class RandomAccessIterator, typename Real = typename std::iterator_traits<RandomAccessIterator>::value_type>
1057 Real median_absolute_deviation(RandomAccessIterator first, RandomAccessIterator last,
1058     typename std::iterator_traits<RandomAccessIterator>::value_type center=std::numeric_limits<typename std::iterator_traits<RandomAccessIterator>::value_type>::quiet_NaN())
1059 {
1060     using std::abs;
1061     using std::isnan;
1062     if (isnan(center))
1063     {
1064         center = boost::math::statistics::median(first, last);
1065     }
1066     const auto num_elems = std::distance(first, last);
1067     BOOST_MATH_ASSERT_MSG(num_elems > 0, "The median of a zero-length vector is undefined.");
1068     auto comparator = [&center](Real a, Real b) { return abs(a-center) < abs(b-center);};
1069     if (num_elems & 1)
1070     {
1071         auto middle = first + (num_elems - 1)/2;
1072         std::nth_element(first, middle, last, comparator);
1073         return abs(*middle-center);
1074     }
1075     else
1076     {
1077         auto middle = first + num_elems/2 - 1;
1078         std::nth_element(first, middle, last, comparator);
1079         std::nth_element(middle, middle+1, last, comparator);
1080         return (abs(*middle-center) + abs(*(middle+1)-center))/abs(static_cast<Real>(2));
1081     }
1082 }
1083 
1084 template<class RandomAccessContainer, typename Real = typename RandomAccessContainer::value_type>
1085 inline Real median_absolute_deviation(RandomAccessContainer& c,
1086     typename RandomAccessContainer::value_type center=std::numeric_limits<typename RandomAccessContainer::value_type>::quiet_NaN())
1087 {
1088     return median_absolute_deviation(std::begin(c), std::end(c), center);
1089 }
1090 
1091 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type>
1092 Real interquartile_range(ForwardIterator first, ForwardIterator last)
1093 {
1094     static_assert(!std::is_integral<Real>::value, "Integer values have not yet been implemented.");
1095     auto m = std::distance(first,last);
1096     BOOST_MATH_ASSERT_MSG(m >= 3, "At least 3 samples are required to compute the interquartile range.");
1097     auto k = m/4;
1098     auto j = m - (4*k);
1099     // m = 4k+j.
1100     // If j = 0 or j = 1, then there are an even number of samples below the median, and an even number above the median.
1101     //    Then we must average adjacent elements to get the quartiles.
1102     // If j = 2 or j = 3, there are an odd number of samples above and below the median, these elements may be directly extracted to get the quartiles.
1103 
1104     if (j==2 || j==3)
1105     {
1106         auto q1 = first + k;
1107         auto q3 = first + 3*k + j - 1;
1108         std::nth_element(first, q1, last);
1109         Real Q1 = *q1;
1110         std::nth_element(q1, q3, last);
1111         Real Q3 = *q3;
1112         return Q3 - Q1;
1113     }
1114     else
1115     {
1116         // j == 0 or j==1:
1117         auto q1 = first + k - 1;
1118         auto q3 = first + 3*k - 1 + j;
1119         std::nth_element(first, q1, last);
1120         Real a = *q1;
1121         std::nth_element(q1, q1 + 1, last);
1122         Real b = *(q1 + 1);
1123         Real Q1 = (a+b)/2;
1124         std::nth_element(q1, q3, last);
1125         a = *q3;
1126         std::nth_element(q3, q3 + 1, last);
1127         b = *(q3 + 1);
1128         Real Q3 = (a+b)/2;
1129         return Q3 - Q1;
1130     }
1131 }
1132 
1133 template<class Container, typename Real = typename Container::value_type>
1134 Real interquartile_range(Container& c)
1135 {
1136     return interquartile_range(std::begin(c), std::end(c));
1137 }
1138 
1139 template<class ForwardIterator, class OutputIterator,
1140     enable_if_t<std::is_same<typename std::iterator_traits<ForwardIterator>::iterator_category(), std::random_access_iterator_tag>::value, bool> = true>
1141 inline OutputIterator mode(ForwardIterator first, ForwardIterator last, OutputIterator output)
1142 {
1143     if(!std::is_sorted(first, last))
1144     {
1145         std::sort(first, last);
1146     }
1147 
1148     return detail::mode_impl(first, last, output);
1149 }
1150 
1151 template<class ForwardIterator, class OutputIterator,
1152     enable_if_t<!std::is_same<typename std::iterator_traits<ForwardIterator>::iterator_category(), std::random_access_iterator_tag>::value, bool> = true>
1153 inline OutputIterator mode(ForwardIterator first, ForwardIterator last, OutputIterator output)
1154 {
1155     if(!std::is_sorted(first, last))
1156     {
1157         BOOST_MATH_ASSERT("Data must be sorted for mode calculation");
1158     }
1159 
1160     return detail::mode_impl(first, last, output);
1161 }
1162 
1163 template<class Container, class OutputIterator>
1164 inline OutputIterator mode(Container& c, OutputIterator output)
1165 {
1166     return mode(std::begin(c), std::end(c), output);
1167 }
1168 
1169 template<class ForwardIterator, typename Real = typename std::iterator_traits<ForwardIterator>::value_type>
1170 inline std::list<Real> mode(ForwardIterator first, ForwardIterator last)
1171 {
1172     std::list<Real> modes;
1173     mode(first, last, std::inserter(modes, modes.begin()));
1174     return modes;
1175 }
1176 
1177 template<class Container, typename Real = typename Container::value_type>
1178 inline std::list<Real> mode(Container& c)
1179 {
1180     return mode(std::begin(c), std::end(c));
1181 }
1182 }}}
1183 #endif
1184 #endif // BOOST_MATH_STATISTICS_UNIVARIATE_STATISTICS_HPP