Skip to content

File utils.hpp

File List > include > utils.hpp

Go to the documentation of this file

#pragma once

#include <cmath>
#include <span>
#include <vector>
#include <algorithm>


namespace cip {


enum class IndexMethod { BinarySearch, Uniform };


// NOTE: Indexer is non-owning — the caller must guarantee the source vector
// outlives this instance and is never reallocated while it is alive.
template <typename T, IndexMethod Method = IndexMethod::BinarySearch>
class Indexer {
    using Span = std::span<const T>;
public:
    Indexer(Span _x)
    : x(_x),
      index_back(x.size()-2),
      x_front(x[index_front]),
      x_back(x[x.size()-1]),
      x_delta((x_back-x_front)/(x.size()-1))
    {
    }

    [[nodiscard]] std::size_t index(const T xi) const noexcept
    {
        if constexpr (Method == IndexMethod::BinarySearch) {
            return binary_search_index(xi);
        } else if constexpr (Method == IndexMethod::Uniform) {
            return uniform_index(xi);
        } else {
            static_assert(sizeof(T) == 0,
                "Unhandled IndexMethod enumerator in Indexer::index — "
                "add a corresponding branch.");
        }
    }

private:
    bool clamp_index(const T xi, std::size_t &idx) const noexcept
    {
        if (xi < x_front) { idx = index_front; return true; }
        if (xi >= x_back) { idx = index_back;  return true; }
        return false;
    }

    std::size_t uniform_index(const T xi) const noexcept
    {
        std::size_t idx;
        if (clamp_index(xi, idx)) return idx;
        return static_cast<std::size_t>((xi - x_front) / x_delta);
    }

    std::size_t binary_search_index(const T xi) const noexcept
    {
        std::size_t idx;
        if (clamp_index(xi, idx)) return idx;
        return std::upper_bound(x.begin(), x.end(), xi) - x.begin() - 1;
    }

    const Span x;
    static constexpr std::size_t index_front = 0;
    const std::size_t index_back;
    const T x_front;
    const T x_back;
    const T x_delta;
};



constexpr inline int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}


constexpr inline std::size_t binomial(std::size_t n, std::size_t k) noexcept
{
    return
      (        k> n  )? 0 :          // out of range
      (k==0 || k==n  )? 1 :          // edge
      (k==1 || k==n-1)? n :          // first
      (     k+k < n  )?              // recursive:
      (binomial(n-1,k-1) * n)/k :    //  path to k=1   is faster
      (binomial(n-1,k) * n)/(n-k);   //  path to k=n-1 is faster
}


template <typename T>
T binomial_power_coefficient(const T y, const int n, const int k)
{
    return binomial(n, k)*std::pow(y, n-k);
}


template <std::size_t Base, std::size_t Exp>
constexpr std::size_t power()
{
    if constexpr (Exp <= 0)
    {
        return 1;
    } else {
        return Base*power<Base, Exp-1>();
    }
}


} // namespace cip