#ifndef FAST_DIV_HPP
#define FAST_DIV_HPP
#include <cassert>
#include <limits>
#include <stdint.h>
#include <type_traits>
namespace {
#if defined(ENABLE_DIV32)
template <typename T>
struct make_smaller
{
using type = typename std::conditional<sizeof(T) / 2 <= sizeof(uint32_t), uint32_t,
typename std::conditional<sizeof(T) / 2 <= sizeof(uint64_t), uint64_t,
T>::type>::type;
};
template <typename X, typename Y>
typename std::enable_if<(sizeof(X) == sizeof(Y)), X>::type
fast_div(X x, Y y)
{
using UX = typename std::make_unsigned<X>::type;
return (UX) x / (UX) y;
}
template <typename X, typename Y>
typename std::enable_if<(sizeof(X) > sizeof(Y)), X>::type
fast_div(X x, Y y)
{
using smaller_t = typename make_smaller<X>::type;
if (x <= std::numeric_limits<smaller_t>::max())
return (smaller_t) x / (smaller_t) y;
else
{
using UX = typename std::make_unsigned<X>::type;
using UY = typename std::make_unsigned<Y>::type;
return (UX) x / (UY) y;
}
}
#else
template <typename T>
struct make_smaller
{
using type = typename std::make_unsigned<
typename std::conditional<
sizeof(T) == sizeof(uint64_t) * 2,
uint64_t, T>::type>::type;
};
template <typename X, typename Y>
typename std::enable_if<(sizeof(X) >= sizeof(Y) &&
sizeof(X) <= sizeof(uint64_t)), X>::type
fast_div(X x, Y y)
{
using UX = typename std::make_unsigned<X>::type;
return (UX) x / (UX) y;
}
template <typename X, typename Y>
typename std::enable_if<(sizeof(X) > sizeof(Y) &&
sizeof(X) > sizeof(uint64_t)), X>::type
fast_div(X x, Y y)
{
using smaller_t = typename make_smaller<X>::type;
if (x <= std::numeric_limits<smaller_t>::max())
return (smaller_t) x / (smaller_t) y;
else
{
using UX = typename std::make_unsigned<X>::type;
using UY = typename std::make_unsigned<Y>::type;
return (UX) x / (UY) y;
}
}
#endif
template <typename X, typename Y>
typename std::enable_if<(sizeof(X) == sizeof(uint64_t) * 2 &&
sizeof(Y) <= sizeof(uint64_t)), uint64_t>::type
fast_div64(X x, Y y)
{
#if defined(__x86_64__) && \
(defined(__GNUC__) || defined(__clang__))
assert(x >= 0 && y > 0);
uint64_t x0 = (uint64_t) x;
uint64_t x1 = ((uint64_t*) &x)[1];
uint64_t d = y;
__asm__("divq %[divider]"
: "+a"(x0), "+d"(x1) : [divider] "r"(d));
return x0;
#else
return (uint64_t) fast_div(x, y);
#endif
}
template <typename X, typename Y>
typename std::enable_if<!(sizeof(X) == sizeof(uint64_t) * 2 &&
sizeof(Y) <= sizeof(uint64_t)), uint64_t>::type
fast_div64(X x, Y y)
{
return (uint64_t) fast_div(x, y);
}
}
#endif