#ifndef CT_GCD_HPP
#define CT_GCD_HPP
#include <ctbignum/addition.hpp>
#include <ctbignum/bigint.hpp>
#include <ctbignum/division.hpp>
#include <ctbignum/mult.hpp>
#include <ctbignum/slicing.hpp>
#include <ctbignum/utility.hpp>
#include <cstddef>
namespace cbn {
namespace detail {
template <typename T, T... A, T... B, T... Is, std::size_t N = sizeof...(Is)>
constexpr auto ext_gcd_impl(std::integer_sequence<T, A...>,
std::integer_sequence<T, B...>,
std::integer_sequence<T, Is...>) {
using detail::pad;
using detail::first;
using detail::take;
using detail::skip;
using detail::join;
constexpr auto a = big_int<N, T>{A...};
constexpr auto b = big_int<N, T>{B...};
constexpr auto dummy = big_int<N, T>{};
constexpr bool a_equals_zero =
std::is_same<std::integer_sequence<T, A...>,
std::integer_sequence<T, dummy[Is]...>>::value;
if
constexpr(a_equals_zero) return join(
b, join(big_int<N, T>{0}, big_int<N, T>{1}));
else {
constexpr auto qr = div(b, a);
constexpr auto rem = qr.remainder;
constexpr auto arg1 = pad<N - rem.size()>(rem);
constexpr auto triple =
ext_gcd_impl(std::integer_sequence<T, arg1[Is]...>(),
std::integer_sequence<T, a[Is]...>(),
std::integer_sequence<T, Is...>());
constexpr auto x = first<N>(triple);
constexpr auto y = take<N, 2 * N>(triple);
constexpr auto z = skip<2 * N>(triple);
constexpr auto qy = partial_mul<N>(qr.quotient, y);
return join(join(x, subtract_ignore_carry(z, qy)), y);
}
}
}
template <typename T, T... A, T... B>
constexpr auto ext_gcd(std::integer_sequence<T, A...>,
std::integer_sequence<T, B...>) {
constexpr std::size_t N = std::max(sizeof...(A), sizeof...(B));
return detail::ext_gcd_impl(std::integer_sequence<T, A...>{},
std::integer_sequence<T, B...>{},
std::make_integer_sequence<T, N>{});
}
template <typename T, T... X, T... Modulus>
constexpr auto mod_inv(std::integer_sequence<T, X...>,
std::integer_sequence<T, Modulus...>) {
constexpr auto triple = ext_gcd(std::integer_sequence<T, X...>{},
std::integer_sequence<T, Modulus...>{});
constexpr auto N = std::max(sizeof...(X), sizeof...(Modulus));
if (triple[0] != 1) {
throw std::runtime_error("modular inverse does not exist");
} else {
using namespace detail;
constexpr auto mod_inverse = take<N, 2 * N>(triple);
constexpr auto L = tight_length(mod_inverse);
return first<L>(mod_inverse);
}
}
}
#endif