#ifndef CT_INVAR_DIV_HPP
#define CT_INVAR_DIV_HPP
#include <ctbignum/addition.hpp>
#include <ctbignum/config.hpp>
#include <ctbignum/division.hpp>
#include <ctbignum/mult.hpp>
#include <ctbignum/slicing.hpp>
#include <ctbignum/utility.hpp>
#include <cstddef>
#include <limits>
#include <utility>
namespace cbn {
namespace detail {
template <std::size_t N, typename T = uint64_t, T... Divisor, std::size_t... Is>
constexpr auto precompute_m_prime_nontight(std::integer_sequence<T, Divisor...>,
std::index_sequence<Is...>) {
constexpr auto D = sizeof...(Divisor);
constexpr big_int<D, T> d{Divisor...};
constexpr auto ell = bit_length(d - big_int<1, T>{1}); constexpr auto w = std::numeric_limits<T>::digits;
constexpr auto limb_shifts = ell / w;
constexpr auto bit_shifts = ell % w;
constexpr auto pow2ell = place_at<std::max(D, limb_shifts + 1), T>(static_cast<T>(1) << bit_shifts, limb_shifts);
constexpr auto pow2N = unary_encoding<N, N + 1, T>();
constexpr auto divrem = div(mul(pow2N, subtract(pow2ell, d)), d);
constexpr auto mp = to_length<N>(add(divrem.quotient, big_int<1, T>{static_cast<T>(1)}));
return std::integer_sequence<T, mp[Is]...>{};
}
template <std::size_t N, typename T = uint64_t, T... Divisor>
constexpr auto precompute_m_prime(std::integer_sequence<T, Divisor...>) {
auto m = precompute_m_prime_nontight<N, T>(std::integer_sequence<T, Divisor...>{},
std::make_index_sequence<N>{});
return take_first(m, std::make_index_sequence<tight_length(m)>{});
}
}
template <typename T, std::size_t N, T... Divisor>
CBN_ALWAYS_INLINE constexpr auto
quotient(big_int<N, T> n, std::integer_sequence<T, Divisor...>) {
using detail::to_length;
using detail::skip;
constexpr big_int<sizeof...(Divisor), T> d{Divisor...};
if constexpr (sizeof...(Divisor) > N)
return big_int<1,T>{static_cast<T>(0)};
else if constexpr (d == big_int<1,T>{static_cast<T>(1)})
return n;
else {
constexpr auto ell = detail::bit_length(d - big_int<1, T>{1});
constexpr auto w = std::numeric_limits<T>::digits;
constexpr auto m_prime = to_big_int(detail::precompute_m_prime<N>(std::integer_sequence<T, Divisor...>{}));
auto t1 = skip<N>(mul(m_prime, n));
auto q = shift_right(
skip<(ell - 1) / w>(add(t1, shift_right(subtract_ignore_carry(n, to_length<N>(t1)), 1))),
(ell - 1) % w); return to_length<N>(q);
}
}
template <typename T, std::size_t N, T... Modulus>
CBN_ALWAYS_INLINE constexpr auto mod(big_int<N, T> n,
std::integer_sequence<T, Modulus...>)
{
auto d = quotient(n, std::integer_sequence<T, Modulus...>{});
constexpr auto M = sizeof...(Modulus);
return detail::to_length<M>(subtract_ignore_carry(n, partial_mul<N>(big_int<M, T>{Modulus...}, d)));
}
template <typename T, std::size_t N, T... Modulus>
CBN_ALWAYS_INLINE constexpr DivisionResult<big_int<N, T>,
big_int<sizeof...(Modulus), T>>
div(big_int<N, T> n, std::integer_sequence<T, Modulus...>)
{
auto quot = quotient(n, std::integer_sequence<T, Modulus...>{});
constexpr auto M = sizeof...(Modulus);
auto rem = detail::to_length<M>(subtract_ignore_carry(
n, partial_mul<N>(big_int<M, T>{Modulus...}, quot)));
return {quot, rem};
}
}
#endif