/* *****************************************************************************
* kblib is a general utility library for C++14 and C++17, intended to provide
* performant high-level abstractions and more expressive ways to do simple
* things.
*
* Copyright (c) 2021 killerbee
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* ****************************************************************************/
/**
* @file
* @brief Provides numerical and mathematical utilities.
*
* @author killerbee
* @date 2019-2021
* @copyright GNU General Public Licence v3.0
*/
#ifndef KBLIB_STATS_H
#define KBLIB_STATS_H
#include
#include
#include
#include
#include
#include
#include "fakestd.h"
#include "logic.h"
#include "tdecl.h"
namespace KBLIB_NS {
template
KBLIB_NODISCARD constexpr auto div(T num, U den) noexcept
-> decltype(std::div(num, den)) {
decltype(std::div(num, den)) ret{};
ret.quot = num / den;
ret.rem = num % den;
return ret;
}
/**
* @brief std::pair isn't constexpr enough, so I'm stuck with this. All I use it
* for is removing a temporary variable from calc_fib_size().
*/
template
struct trivial_pair {
T first;
T second;
};
#if 0 and KBLIB_USE_CXX17
/**
* @brief std::array isn't constexpr enough in C++14, so this is a separate
* class in that version. In C++17, std::array has all the functionality needed,
* so this defines an alias instead.
*
*/
template
using trivial_array = std::array;
#else
/**
* @brief std::array isn't constexpr enough in C++14, so a dedicated array class
* is needed for constexpr functions.
*/
template
struct trivial_array {
T arr[N];
KBLIB_NODISCARD constexpr auto operator[](std::size_t n) -> T& {
return arr[n];
}
KBLIB_NODISCARD constexpr auto operator[](std::size_t n) const -> const T& {
return arr[n];
}
KBLIB_NODISCARD constexpr auto size() const -> std::size_t { return N; }
KBLIB_NODISCARD constexpr auto begin() & noexcept -> T* { return arr; }
KBLIB_NODISCARD constexpr auto begin() const& noexcept -> const T* {
return arr;
}
KBLIB_NODISCARD constexpr auto end() & noexcept -> T* { return arr + N; }
KBLIB_NODISCARD constexpr auto end() const& noexcept -> const T* {
return arr + N;
}
KBLIB_NODISCARD constexpr friend auto operator==(
const trivial_array& a, const trivial_array& b) noexcept -> bool {
return equal(a.begin(), a.end(), b.begin());
}
KBLIB_NODISCARD constexpr friend auto operator!=(
const trivial_array& a, const trivial_array& b) noexcept -> bool {
return not (a == b);
}
};
#endif
/**
* @brief Calculate the index of the largest fibonacci number that can be
* represented by a given unsigned integral type.
*
* @remark Here is a table of the results for common bit-widths:
* @verbatim
bits N fibonacci(N)
--------------------------------------------------
8 13 144
16 24 28657
32 47 1836311903
64 93 7540113804746346429
128 186 205697230343233228174223751303346572685
@endverbatim
*
* @return std::size_t
*/
template
KBLIB_NODISCARD constexpr auto calc_fib_size() noexcept -> std::size_t {
static_assert(std::is_unsigned::value, "U must be unsigned");
std::size_t n{};
trivial_pair state{0, 1};
U& a = state.first;
U& b = state.second;
while (b >= a) {
state = {b, static_cast(a + b)};
++n;
}
return n;
}
/**
* @brief Generates the first N values of the fibonacci sequence.
*
* @pre If N > calc_fib_size(), then U must be an unsigned type, and the
* resulting sequence is modulo 2^bits_of_U.
* @pre N >= 2
*
* @return trivial_array An array containing the first N fibonacci
* numbers.
*/
template () + 1>
KBLIB_NODISCARD constexpr auto make_fib_arr() noexcept -> trivial_array {
static_assert(
implies<(N > calc_fib_size()), std::is_unsigned::value>::value,
"signed U with large N would trigger signed overflow");
// Initialize the first two elements of the array
trivial_array ret{{0, 1}};
// A loop initializes the rest
for (std::size_t i = 2; i < N; ++i) {
ret[i] = ret[i - 1] + ret[i - 2];
}
return ret;
}
/**
* @brief Compile-time table fibonacci function.
*
* @pre n <= calc_fib_size()
*
* @return The nth fibonacci number.
*/
template
KBLIB_NODISCARD constexpr auto fibonacci(int n) noexcept -> U {
constexpr auto arr = make_fib_arr();
assert(n >= 0 and static_cast(n) < arr.size());
return arr[to_unsigned(n)];
}
inline namespace nums {
/**
* @brief Shorthand for std::numeric_limits::max().
*
* Implicitly converts to the maximum representable value of any numeric
* type. For unsigned destination types, -1 is shorter, but much less clear.
* For signed destination types, there is no concise representation for a
* generic maximum.
*
* Also serves as a max(a, b) function object.
*/
constexpr struct max_t {
template ::is_specialized,
void>* = nullptr>
constexpr /* implicit*/ operator T() const
noexcept(noexcept(std::numeric_limits::max())) {
return std::numeric_limits::max();
}
template
KBLIB_NODISCARD constexpr static auto of() noexcept(
noexcept(std::numeric_limits::max())) {
return std::numeric_limits::max();
}
/**
@brief Return the larger of two values. Returns lhs if equal.
*/
template
KBLIB_NODISCARD constexpr auto operator()(L&& lhs, R&& rhs) const noexcept
-> decltype(auto) {
return std::less<>{}(lhs, rhs) ? std::forward(rhs)
: std::forward(lhs);
}
KBLIB_NODISCARD constexpr inline friend auto operator==(max_t, max_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator!=(max_t, max_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<(max_t, max_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>(max_t, max_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<=(max_t, max_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>=(max_t, max_t)
-> std::true_type {
return {};
}
template
KBLIB_NODISCARD constexpr friend auto operator==(T t, max_t) -> bool {
return t == of();
}
template
KBLIB_NODISCARD constexpr friend auto operator==(max_t, T t) -> bool {
return t == of();
}
template
KBLIB_NODISCARD constexpr friend auto operator!=(T t, max_t) -> bool {
return t != of();
}
template
KBLIB_NODISCARD constexpr friend auto operator!=(max_t, T t) -> bool {
return t != of();
}
template
KBLIB_NODISCARD constexpr friend auto operator<(T t, max_t) -> bool {
return t < of();
}
template
KBLIB_NODISCARD constexpr friend auto operator<(max_t, T t) -> bool {
return of() < t;
}
template
KBLIB_NODISCARD constexpr friend auto operator>(T t, max_t) -> bool {
return t > of();
}
template
KBLIB_NODISCARD constexpr friend auto operator>(max_t, T t) -> bool {
return of() > t;
}
template
KBLIB_NODISCARD constexpr friend auto operator<=(T t, max_t) -> bool {
return t <= of();
}
template
KBLIB_NODISCARD constexpr friend auto operator<=(max_t, T t) -> bool {
return of() <= t;
}
template
KBLIB_NODISCARD constexpr friend auto operator>=(T t, max_t) -> bool {
return t >= of();
}
template
KBLIB_NODISCARD constexpr friend auto operator>=(max_t, T t) -> bool {
return of() >= t;
}
} max; /**< A shorthand for the maximum value of the destination type. Also
provides max(a, b). */
/**
* @brief Shorthand for std::numeric_limits::min()
*
* Implicitly converts to the minimum representable value of any numeric
* type. For unsigned destination types, this is always 0. For signed
* destination types, it depends on size.
*
* Also serves as a min(a, b) function object.
*/
constexpr struct min_t {
template ::is_specialized,
void>* = nullptr>
constexpr /* implicit*/ operator T() const
noexcept(noexcept(std::numeric_limits::min())) {
return std::numeric_limits::min();
}
template
KBLIB_NODISCARD constexpr static auto of() noexcept(
noexcept(std::numeric_limits::min())) {
return std::numeric_limits::min();
}
/**
@brief Returns the smaller of two values. Returns rhs if equal.
*/
template
KBLIB_NODISCARD constexpr auto operator()(L&& lhs, R&& rhs) const noexcept
-> decltype(auto) {
return std::less<>{}(lhs, rhs) ? std::forward(lhs)
: std::forward(rhs);
}
template
KBLIB_NODISCARD constexpr friend auto operator==(T t, min_t) -> bool {
return t == of();
}
template
KBLIB_NODISCARD constexpr friend auto operator==(min_t, T t) -> bool {
return t == of();
}
template
KBLIB_NODISCARD constexpr friend auto operator!=(T t, min_t) -> bool {
return t != of();
}
template
KBLIB_NODISCARD constexpr friend auto operator!=(min_t, T t) -> bool {
return t != of();
}
template
KBLIB_NODISCARD constexpr friend auto operator<(T t, min_t) -> bool {
return t < of();
}
template
KBLIB_NODISCARD constexpr friend auto operator<(min_t, T t) -> bool {
return of() < t;
}
template
KBLIB_NODISCARD constexpr friend auto operator>(T t, min_t) -> bool {
return t > of();
}
template
KBLIB_NODISCARD constexpr friend auto operator>(min_t, T t) -> bool {
return of() > t;
}
template
KBLIB_NODISCARD constexpr friend auto operator<=(T t, min_t) -> bool {
return t <= of();
}
template
KBLIB_NODISCARD constexpr friend auto operator<=(min_t, T t) -> bool {
return of() <= t;
}
template
KBLIB_NODISCARD constexpr friend auto operator>=(T t, min_t) -> bool {
return t >= of();
}
template
KBLIB_NODISCARD constexpr friend auto operator>=(min_t, T t) -> bool {
return of() >= t;
}
KBLIB_NODISCARD constexpr inline friend auto operator==(min_t, min_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator!=(min_t, min_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<(min_t, min_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>(min_t, min_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<=(min_t, min_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>=(min_t, min_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator==(max_t, min_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator==(min_t, max_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator!=(max_t, min_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator!=(min_t, max_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<(max_t, min_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<(min_t, max_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>(max_t, min_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>(min_t, max_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<=(max_t, min_t)
-> std::false_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator<=(min_t, max_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>=(max_t, min_t)
-> std::true_type {
return {};
}
KBLIB_NODISCARD constexpr inline friend auto operator>=(min_t, max_t)
-> std::false_type {
return {};
}
} min; /**< A shorthand for the minimum value of the destination type. Also
provides min(a, b). */
} // namespace nums
template
KBLIB_NODISCARD constexpr auto pi() -> T {
return 3.1415926535897932384626433832795028841971693993751l;
}
template
KBLIB_NODISCARD constexpr auto tau() -> T {
return 2 * pi;
}
template
KBLIB_NODISCARD constexpr auto e() -> T {
return 2.7182818284590452353602874713526624977572470937000l;
}
template
KBLIB_NODISCARD constexpr auto root_2() -> T {
return 1.4142135623730950488016887242096980785696718753769l;
}
template
KBLIB_NODISCARD constexpr auto phi() -> T {
return 1.6180339887498948482045868343656381177203091798058l;
}
// saturating to_unsigned
template
KBLIB_NODISCARD constexpr auto saturating_cast(F x) noexcept
-> enable_if_t::value and std::is_integral::value
and std::is_unsigned::value,
A> {
if (x < 0) {
return 0;
} else if (to_unsigned(x) > A(max)) {
return max;
} else {
return static_cast(x);
}
}
// saturating to_signed(signed)
template
KBLIB_NODISCARD constexpr auto saturating_cast(F x) noexcept
-> enable_if_t::value and std::is_integral::value
and std::is_signed::value
and std::is_signed::value,
A> {
if (x < A(min)) {
return min;
} else if (to_unsigned(x) > A(max)) {
return max;
} else {
return x;
}
}
// saturating to_signed(unsigned)
template
KBLIB_NODISCARD constexpr auto saturating_cast(F x) noexcept
-> enable_if_t::value and std::is_integral::value
and std::is_signed::value
and std::is_unsigned::value,
A> {
if (x > to_unsigned(A(max))) {
return max;
} else {
return x;
}
}
/// TODO(killerbee13): write tests and fix style issues for quantization
/// functions
/**
* @brief Quantize a real-valued value into a discrete integer.
*
* @tparam T An unsigned integral type.
* @param min The real value corresponding to 0 in the output.
* @param delta The difference between quantization steps.
* @param val The input value.
* @return The quantized value of the input.
*/
template
KBLIB_NODISCARD constexpr auto quantize_step(F low, F delta, F val) noexcept
-> T {
static_assert(std::is_unsigned::value, "Destination must be unsigned.");
return static_cast((val - low) * static_cast(max) * delta);
}
/**
* @brief Quantize a real-valued value into a discrete integer.
*
* @tparam T An unsigned integral type.
* @param min The real value corresponding to min in the output.
* @param max The real value corresponding to max in the output.
* @param val The input value.
* @return The quantized value of the input.
*/
template
KBLIB_NODISCARD constexpr auto quantize_range(F low, F high, F val) noexcept
-> T {
static_assert(std::is_unsigned::value, "Destination must be unsigned.");
auto delta = (high - low) / static_cast(max);
return static_cast((val - low) * static_cast(max) * delta);
}
} // namespace KBLIB_NS
#endif // KBLIB_STATS_H