Skip to content

File Stats.hpp

File List > analysis > stats > Stats.hpp

Go to the documentation of this file

#pragma once

#include <concepts>
#include <string>
#include <vector>

#include <gtl/btree.hpp>
#include <mp-units/concepts.h>
#include <mp-units/systems/si.h>

#include <units/units.hpp>

#include <trace/risesets/RiseSetArray.hpp>
#include <trace/types/enums.hpp>

namespace astrea {
namespace trace {

// Default percentiles to calculate for statistics.
static const std::vector<Unitless> DEFAULT_PERCENTILES{ 1, 5, 10, 25, 50, 75, 90, 95, 99 };

template <class T = Time>
struct Stats {

    Stats() = default;

    Stats(const RiseSetArray& risesets, const RiseSetMetric& metric) = delete;

    Stats(std::vector<T> values)
    {
        using mp_units::one;
        using mp_units::si::unit_symbols::s;

        if (values.size() == 0) { throw std::runtime_error("Cannot calculate statistics on an empty vector."); }

        // Sort automatically gives min, max, and sets up for percentile calcs
        std::sort(values.begin(), values.end());

        min = values[0];
        max = values[values.size() - 1];
        avg = T(); // this should be 0?
        for (const auto& time : values) {
            avg += time;
        }
        avg /= static_cast<double>(values.size());

        const std::size_t nTimes = values.size();
        for (const auto& pct : DEFAULT_PERCENTILES) {
            const std::size_t index = static_cast<std::size_t>(std::ceil(pct.numerical_value_in(one) * nTimes / 100)) - 1;
            percentiles.push_back(values[index]);
        }
    }

    std::vector<std::string> to_string_vector() const
    {
        std::vector<std::string> retval;
        retval.reserve(Stats<T>::size());

        // TODO: this order is weakly enforced. Hopefully it doesn't break anything
        retval.push_back(std::to_string(min));
        retval.push_back(std::to_string(avg));
        retval.push_back(std::to_string(max));
        for (const auto& pct : percentiles) {
            retval.push_back(std::to_string(pct));
        }

        return retval;
    }

    static constexpr std::size_t size() { return DEFAULT_PERCENTILES.size() + 3; }

    T min;                      
    T max;                      
    T avg;                      
    std::vector<T> percentiles; 
};

template <>
inline Stats<Time>::Stats(const RiseSetArray& risesets, const RiseSetMetric& metric)
{
    percentiles.reserve(DEFAULT_PERCENTILES.size());
    switch (metric) {
        case (RiseSetMetric::ACCESS_TIME): {
            *this = Stats(risesets.get_access_times());
            break;
        }
        case (RiseSetMetric::GAP): {
            *this = risesets.size() > 2 ? Stats(risesets.get_gap_times()) : Stats();
            break;
        }
        default: throw std::runtime_error("Unrecognized riseset metric.");
    }
}

template <>
inline std::vector<std::string> Stats<Time>::to_string_vector() const
{
    std::vector<std::string> retval;
    retval.reserve(Stats<Time>::size());

    retval.push_back(to_formatted_string(min));
    retval.push_back(to_formatted_string(avg));
    retval.push_back(to_formatted_string(max));
    for (const auto& pct : percentiles) {
        retval.push_back(to_formatted_string(pct));
    }

    return retval;
}

} // namespace trace
} // namespace astrea