some more changes

This commit is contained in:
Barrett Ruth 2025-08-30 17:12:46 -05:00
parent 36826b9934
commit 12fd46747e
4 changed files with 143 additions and 34 deletions

View file

@ -1,55 +1,121 @@
#ifndef BMATH_HEADER_ONLY_MATH_LIB
#define BMATH_HEADER_ONLY_MATH_LIB
#include <compare>
#include <concepts>
#include <optional>
#include <format>
#include <iterator>
#include <ostream>
#include <stdexcept>
#include <string>
namespace bmath {
template <std::integral IntegralType, typename Modulus = std::nullopt_t>
requires(std::integral<Modulus> || std::is_same_v<Modulus, std::nullopt_t>)
inline constexpr uint64_t DEFAULT_MOD = 1'000'000'007;
template <std::integral T, T M = static_cast<T>(DEFAULT_MOD)>
requires(M > 0 && DEFAULT_MOD <= std::numeric_limits<T>::max())
class mint {
public:
[[nodiscard]] constexpr explicit mint() : value{} {}
[[nodiscard]] constexpr explicit mint(IntegralType _value) : value{_value} {}
[[nodiscard]] constexpr explicit operator IntegralType() const noexcept {
return value;
}
[[nodiscard]] constexpr explicit mint(T t) : value{t % M} {}
[[nodiscard]] constexpr explicit operator T() const noexcept { return value; }
template <typename OtherIntegralType, typename OtherModulus>
template <std::convertible_to<T> OtherT, T OtherM>
requires(M == OtherM)
[[nodiscard]] constexpr mint operator+(
mint<OtherIntegralType, OtherModulus> const& otherMint) const noexcept {
return mint{value + otherMint.value};
mint<OtherT, OtherM> const other) const noexcept {
if constexpr (M != 0 && OtherM != 0) {
static_assert(M == OtherM,
"Cannot add integral types with differing moduli");
} else if (M != 0 && OtherM != 0) {
assert(M == OtherM && "Cannot add integral types with differing moduli");
}
return mint<T, M>{value + other.get()};
}
[[nodiscard]] constexpr mint& operator-(mint const& other) const noexcept {}
[[nodiscard]] constexpr mint& operator*(mint const& other) const noexcept {}
[[nodiscard]] constexpr mint& operator/(mint const& other) const noexcept {}
[[nodiscard]] constexpr mint& operator%(mint const& other) const noexcept {}
// [[nodiscard]] constexpr mint& operator-(mint const other) const noexcept {}
// [[nodiscard]] constexpr mint& operator*(mint const other) const noexcept {}
// [[nodiscard]] constexpr mint& operator/(mint const other) const noexcept {}
// [[nodiscard]] constexpr mint& operator%(mint const other) const noexcept {}
[[nodiscard]] constexpr T get() const noexcept { return value; }
friend constexpr bool operator==(std::convertible_to<IntegralType> auto a,
mint const& b) noexcept {
return static_cast<IntegralType>(a) == b.value;
}
friend constexpr bool operator==(
mint const& a, std::convertible_to<IntegralType> auto b) noexcept {
return a.value == static_cast<IntegralType>(b);
}
template <typename OtherIntegralType, typename OtherModulus>
template <std::convertible_to<T> OtherT, OtherT OtherM>
[[nodiscard]] constexpr bool operator==(
mint<OtherIntegralType, OtherModulus> const& otherMint) const noexcept {
return value == otherMint.value;
mint<OtherT, OtherM> const other) const noexcept {
return get() == static_cast<T>(other.get());
}
[[nodiscard]] constexpr IntegralType get() const { return value; }
template <std::convertible_to<T> OtherT>
[[nodiscard]] constexpr bool operator==(OtherT const other) const noexcept {
return get() == static_cast<T>(other);
}
template <std::convertible_to<T> OtherT, T OtherM>
friend std::ostream& operator<<(std::ostream& out,
mint<OtherT, OtherM> const other) {
return out << other.get();
}
private:
IntegralType value{};
T value{};
};
template <std::integral T, T M, std::integral U>
requires(M >= 0)
[[nodiscard]] static constexpr bmath::mint<T, M> pow(mint<T, M> base,
U exponent) {
if (exponent < 0) {
throw std::domain_error(
std::format("cannot compute pow({}, {}) with negative exponent {}",
base, exponent, exponent));
}
if (base == 0) {
if (exponent == 0) {
throw std::domain_error("pow(0, 0) is indeterminate");
}
return mint<T, M>{0};
}
if (base == 1 || exponent == 0) {
return mint<T, M>{1};
}
T t{};
while (exponent > 0) {
exponent >>= 1;
}
return mint<T, M>{t};
}
template <std::integral T, T M = DEFAULT_MOD>
requires(M > 0)
[[nodiscard]] std::string to_string(mint<T, M> const number) {
return std::to_string(number.get());
}
} // namespace bmath
template <class CharT, std::integral T, T M>
struct std::formatter<bmath::mint<T, M>, CharT> {
std::formatter<std::basic_string_view<CharT>, CharT> inner;
constexpr auto parse(std::basic_format_parse_context<CharT>& pc) {
return inner.parse(pc);
}
template <class Ctx>
auto format(bmath::mint<T, M> const x, Ctx& ctx) const {
std::basic_string<CharT> tmp;
if constexpr (std::same_as<CharT, wchar_t>) {
std::format_to(std::back_inserter(tmp), L"{}", x.get());
} else {
std::format_to(std::back_inserter(tmp), "{}", x.get());
}
return inner.format(tmp, ctx);
}
};
#endif