// Copyright Nick Thompson 2019. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_MATH_DISTRIBUTIONS_EMPIRICAL_CUMULATIVE_DISTRIBUTION_FUNCTION_HPP #define BOOST_MATH_DISTRIBUTIONS_EMPIRICAL_CUMULATIVE_DISTRIBUTION_FUNCTION_HPP #include <algorithm> #include <iterator> #include <stdexcept> #include <type_traits> #include <utility> #include <boost/math/tools/is_standalone.hpp> #ifndef BOOST_MATH_STANDALONE #include <boost/config.hpp> #ifdef BOOST_MATH_NO_CXX17_IF_CONSTEXPR #error "The header <boost/math/norms.hpp> can only be used in C++17 and later." #endif #endif namespace boost { namespace math{ template<class RandomAccessContainer> class empirical_cumulative_distribution_function { using Real = typename RandomAccessContainer::value_type; public: empirical_cumulative_distribution_function(RandomAccessContainer && v, bool sorted = false) { if (v.size() == 0) { throw std::domain_error("At least one sample is required to compute an empirical CDF."); } m_v = std::move(v); if (!sorted) { std::sort(m_v.begin(), m_v.end()); } } auto operator()(Real x) const { if constexpr (std::is_integral_v<Real>) { if (x < m_v[0]) { return static_cast<double>(0); } if (x >= m_v[m_v.size()-1]) { return static_cast<double>(1); } auto it = std::upper_bound(m_v.begin(), m_v.end(), x); return static_cast<double>(std::distance(m_v.begin(), it))/static_cast<double>(m_v.size()); } else { if (x < m_v[0]) { return Real(0); } if (x >= m_v[m_v.size()-1]) { return Real(1); } auto it = std::upper_bound(m_v.begin(), m_v.end(), x); return static_cast<Real>(std::distance(m_v.begin(), it))/static_cast<Real>(m_v.size()); } } RandomAccessContainer&& return_data() { return std::move(m_v); } private: RandomAccessContainer m_v; }; }} #endif