/// @file   util/template_math.hpp
/// @brief  a place for some template math to live
/// @author will sheffler

#pragma once
/** \file */

#include <stdint.h>

namespace hgeom {
namespace util {

template <int X, int Y> struct POW {
  static const int VAL = X * POW<X, Y - 1>::VAL;
};
template <int X /*1*/> struct POW<X, 1> {
  static const int VAL = X;
};
template <int X /*0*/> struct POW<X, 0> {
  static const int VAL = 1;
};

namespace LOG_IMPL {
template <uint64_t val, uint64_t base> struct FLOOR_LOG_IMPL {
  static const uint64_t VAL = FLOOR_LOG_IMPL<val / base, base>::VAL + 1ul;
};
template <uint64_t base> struct FLOOR_LOG_IMPL<0ul, base> {
  static const uint64_t VAL = 0ul;
};
template <uint64_t val, uint64_t base, uint64_t remainder>
struct CEIL_LOG_TEST {
  static const uint64_t VAL = 1ul;
};
template <uint64_t base> struct CEIL_LOG_TEST<1ul, base, 0ul> {
  static const uint64_t VAL = 0ul;
};
template <uint64_t base> struct CEIL_LOG_TEST<0ul, base, 0ul> {
  static const uint64_t VAL = 0ul;
};
template <uint64_t val, uint64_t base> struct CEIL_LOG_TEST<val, base, 0ul> {
  static const uint64_t VAL = CEIL_LOG_TEST<val / base, base, val % base>::VAL;
};
} // namespace LOG_IMPL
template <uint64_t val, uint64_t base> struct FLOOR_LOG {
  static const uint64_t VAL = LOG_IMPL::FLOOR_LOG_IMPL<val / base, base>::VAL;
};
template <uint64_t val, uint64_t base> struct CEIL_LOG {
  static const uint64_t VAL =
      FLOOR_LOG<val, base>::VAL +
      LOG_IMPL::CEIL_LOG_TEST<val / base, base, val % base>::VAL;
};
template <uint64_t base> struct CEIL_LOG<1ul, base> {
  static const uint64_t VAL = 0ul;
};
template <uint64_t base> struct FLOOR_LOG<0ul, base> {}; // ERROR 0ul
template <uint64_t base> struct CEIL_LOG<0ul, base> {};
template <uint64_t val> struct FLOOR_LOG<val, 1ul> {}; // ERROR base 1ul
template <uint64_t val> struct CEIL_LOG<val, 1ul> {};
template <uint64_t val> struct FLOOR_LOG<val, 0ul> {}; // ERROR base 0ul
template <uint64_t val> struct CEIL_LOG<val, 0ul> {};

// G prevent compiler from complaining about out-of-bound shifts in SAFE_*SHIFT
// leave at bottom for safety I guess....
#pragma GCC system_header
template <class T, T val, uint64_t shift> struct SAFE_LSHIFT {
  static const T VAL = (shift >= (sizeof(T) * 8)) ? 0 : (val << shift);
};
template <class T, T val, uint64_t shift> struct SAFE_RSHIFT {
  static const T VAL = (shift >= (sizeof(T) * 8)) ? 0 : (val >> shift);
};
} // namespace util
} // namespace hgeom
