Math All
(math/all.hpp)
- View this file on GitHub
- Last update: 2026-10-05 22:23:07+09:00
- Include:
#include "math/all.hpp"
Overview
math/all.hpp includes every general math header in this directory, including
the FPS/polynomial and dense-matrix subcategories.
Public namespaces stay flat and short: general math helpers use m1une::math,
FPS helpers use m1une::fps, and dense matrix helpers use m1une::matrix.
Choosing a Header
You usually do not need to include this entire bundle:
- Use
modint.hppfor arithmetic modulo a fixed number such as998244353. - Use
modular_square_root.hppto solvex * x = value (mod prime). - Use
modular_kth_root.hppto solvex^k = value (mod prime). - Use
discrete_logarithm.hppto solvebase^x = target (mod mod)for the smallest nonnegative exponent. - Use
base_n.hppto convert nonnegative integers to and from arbitrary-base digit sequences. - Use
bitwise_convolution.hppfor OR, AND, or XOR convolution over mask indices. - Use
divisor_convolution.hppfor GCD or LCM convolution over positive integer indices. - Use
subset_convolution.hppto combine values indexed by disjoint submasks. - Use
set_power_series.hppfor inverse, division, exponential, logarithm, integer powers, and normalized square roots under subset convolution. - Use
multivariate_convolution.hppfor truncated or cyclic convolution of flattened multidimensional arrays. - Use
xor_basis.hppto maintain the XOR span of a set of unsigned integers. - Use
gaussian_integer.hppfor exact arithmetic, Euclidean division, and GCDs in the Gaussian integers. - Use
generalized_floor_sum.hppfor polynomial moments offloor((a * i + b) / mod). - Use
zeta_mobius_transform.hppfor subset, superset, divisor, and multiple transforms. - Use
combinatorics.hppfor many factorial, combination, or permutation queries under a prime modulus. - Use
binomial_coefficient_mod.hppfor huge binomial arguments modulo one fixed prime-power or composite modulus. - Use
lucas.hppfor binomial coefficients with huge arguments modulo a small prime. - Use
bernoulli.hppfor Bernoulli numbers, Bernoulli polynomials, Faulhaber sums, and polynomial discrete integration. - Use
combinatorial_sequences.hppfor Catalan, Bell, Stirling, partition, or derangement numbers. - Use
partition_function.hppwhen only integer partition numbers are needed. - Use
prefix_sum_of_binom.hppfor online or offline queries of $\sum_{k=0}^{m-1}\binom{n}{k}$. - Use
cyclotomic_polynomial.hppto construct the polynomial of primitive roots of unity of a given order. - Use
prime_sieve.hppwhen all queried integers are at most a manageable limit, usually a few million or tens of millions. - Use
prime_factorization.hppfor isolated 64-bit integers that are too large for a sieve. - Use
primitive_root.hppto find a generator of the multiplicative group modulo an integer when one exists. - Use
two_square_sum.hppto enumerate non-negative pairs(a, b)satisfyingn = a * a + b * b. - Use
number_theory.hppfor extended GCD, modular inverses, simultaneous remainder constraints, and sums involving floor division. - Use
integer_arithmetic.hppfor exact integer roots and powers. - Use
repunit.hppfor a repeated-one numeral or geometric sum in an arbitrary base. - Use
tetration.hppfor modular tetration and arbitrary power towers. - Use
multiplicative_function_prefix_sum.hppfor Min_25 summation of a multiplicative function from its prime-power values. - Use
totient_sum.hppfor summatory Euler totient queries. - Use
rational.hppfor normalized exact fractions. - Use
rational_approximation.hppfor the closest fraction on each side under numerator and denominator bounds. - Use
newton_method.hppfor fixed-count Newton iteration over numeric values or formal power series. - Use
squarefree_count.hppto count square-free integers through a 64-bit limit. - Use
stern_brocot_tree.hppfor positive rational tree paths, ancestors, and LCA operations. - Use
fps/for formal power series, convolution, multipoint evaluation, interpolation, and linear recurrences. - Use
matrix/for dense matrices, Pfaffians, hafnians, sparse determinants, and linear algebra.
math/all.hpp is convenient during a contest when compile time and including a
few unused headers do not matter.
Included Headers
| Header | Contents |
|---|---|
math/base_n.hpp |
Checked conversion between integers and arbitrary-base digits. |
math/bernoulli.hpp |
Bernoulli numbers and polynomials, power sums, and polynomial discrete integration. |
math/binomial_coefficient_mod.hpp |
Binomial coefficients with 64-bit arguments modulo a fixed arbitrary modulus. |
math/bitwise_convolution.hpp |
OR, AND, XOR convolutions and the Walsh-Hadamard transform. |
math/bit_ceil.hpp |
Smallest power of two at least a given value. |
math/integer_arithmetic.hpp |
Exact integer square/k-th roots and overflow-aware powers. |
math/lucas.hpp |
Lucas’s theorem for huge binomial arguments modulo a small prime. |
math/modint.hpp |
Static and tagged dynamic modular integer types. |
math/modular_kth_root.hpp |
Generalized Tonelli–Shanks kth roots modulo a prime. |
math/modular_square_root.hpp |
Modular square roots for prime moduli using Tonelli-Shanks. |
math/multivariate_convolution.hpp |
Truncated and cyclic multidimensional convolution. |
math/multiplicative_function_prefix_sum.hpp |
Min_25 prefix sums of a multiplicative function. |
math/newton_method.hpp |
Generic fixed-count Newton iteration with formal-power-series division support. |
math/fps/all.hpp |
Convolution, formal power series, polynomial algorithms, and linear recurrences. |
math/matrix/all.hpp |
Dense and packed GF(2) matrices, Pfaffian, hafnian, sparse determinant, Gaussian elimination, inverse, and linear systems. |
math/combinatorics.hpp |
Factorials, binomial coefficients, permutations, and multiset counts. |
math/combinatorial_sequences.hpp |
Fast standard counting sequences and special numbers. |
math/cyclotomic_polynomial.hpp |
Integer coefficients of cyclotomic polynomials. |
math/discrete_logarithm.hpp |
Extended baby-step–giant-step for discrete logarithms modulo any positive integer. |
math/divisor_convolution.hpp |
GCD and LCM convolutions over positive integer indices. |
math/gaussian_integer.hpp |
Exact Gaussian-integer arithmetic, Euclidean division, and extended GCD. |
math/generalized_floor_sum.hpp |
Polynomial moments of a linear floor sequence using monoid floor sum. |
math/number_theory.hpp |
Extended GCD, modular power and inverse, CRT, and floor sum. |
math/partition_function.hpp |
Integer partition numbers via Euler’s pentagonal theorem and FPS inversion. |
math/prefix_sum_of_binom.hpp |
Online and offline prefix sums of binomial coefficients. |
math/prime_sieve.hpp |
Linear sieve with smallest prime factors. |
math/prime_factorization.hpp |
Deterministic 64-bit primality test and Pollard-Rho factorization. |
math/primitive_root.hpp |
Smallest primitive root modulo an integer, when one exists. |
math/repunit.hpp |
Repunits, repdigits, digit-block concatenation, and divisibility lengths. |
math/set_power_series.hpp |
Set-series division, inverse, exponential, logarithm, powers, and square roots. |
math/two_square_sum.hpp |
Enumerates representations as a sum of two non-negative squares. |
math/xor_basis.hpp |
XOR linear basis with membership, optimization, merging, and ordered enumeration. |
math/tetration.hpp |
Modular tetration, arbitrary power towers, and bounded tower comparison. |
math/totient_sum.hpp |
Summatory Euler totient function. |
math/rational.hpp |
Exact normalized rational arithmetic over signed integers. |
math/rational_approximation.hpp |
Closest bounded fractions below and above a target rational. |
math/squarefree_count.hpp |
Counts square-free integers in $O(n^{2/5})$. |
math/stern_brocot_tree.hpp |
Compressed Stern-Brocot paths and tree navigation. |
math/subset_convolution.hpp |
Ranked-zeta subset convolution over disjoint masks. |
math/zeta_mobius_transform.hpp |
Subset, superset, divisor, and multiple zeta/Mobius transforms. |
Depends on
Base-N Numbers
(math/base_n.hpp)
Bernoulli Numbers and Power Sums
(math/bernoulli.hpp)
Binomial Coefficient Modulo an Arbitrary Modulus
(math/binomial_coefficient_mod.hpp)
Bit Ceil
(math/bit_ceil.hpp)
Bitwise Convolution
(math/bitwise_convolution.hpp)
Combinatorial Sequences
(math/combinatorial_sequences.hpp)
Combinatorics
(math/combinatorics.hpp)
Cyclotomic Polynomial
(math/cyclotomic_polynomial.hpp)
Discrete Logarithm
(math/discrete_logarithm.hpp)
Divisor Convolution
(math/divisor_convolution.hpp)
Formal Power Series All
(math/fps/all.hpp)
Formal Power Series Composition
(math/fps/composition.hpp)
Compositional Inverse of Formal Power Series
(math/fps/compositional_inverse.hpp)
Convolution
(math/fps/convolution.hpp)
Convolution
(math/fps/convolution.hpp)
Long Long Convolution
(math/fps/convolution_ll.hpp)
Floating-Point Convolution
(math/fps/floating_point_convolution.hpp)
Formal Power Series
(math/fps/formal_power_series.hpp)
Formal Power Series
(math/fps/formal_power_series.hpp)
Formal Power Series
(math/fps/formal_power_series.hpp)
Geometric-Sequence Polynomial Evaluation and Interpolation
(math/fps/geometric_sequence_evaluation.hpp)
Polynomial Half-GCD
(math/fps/half_gcd.hpp)
math/fps/internal/ntt998_faster.hpp
Lagrange Inversion Formula
(math/fps/lagrange_inversion.hpp)
Linear Recurrences and Bostan-Mori
(math/fps/linear_recurrence.hpp)
Multipoint Evaluation and Interpolation
(math/fps/multipoint_evaluation.hpp)
Polynomial Factorization
(math/fps/polynomial_factorization.hpp)
Polynomial Roots over a Finite Field
(math/fps/polynomial_roots.hpp)
Solve Formal Power Series Equation
(math/fps/solve_fps_equation.hpp)
Sparse Formal Power Series
(math/fps/sparse_formal_power_series.hpp)
Gaussian Integer
(math/gaussian_integer.hpp)
Generalized Floor Sum
(math/generalized_floor_sum.hpp)
Integer Roots and Powers
(math/integer_arithmetic.hpp)
Lucas's Theorem
(math/lucas.hpp)
Adjugate Matrix
(math/matrix/adjugate.hpp)
Matrix Bundle
(math/matrix/all.hpp)
Bit Matrix
(math/matrix/bit_matrix.hpp)
Characteristic Polynomial
(math/matrix/characteristic_polynomial.hpp)
Determinant Modulo a Composite Modulus
(math/matrix/determinant_mod.hpp)
Hafnian
(math/matrix/hafnian.hpp)
Matrix Linear Algebra
(math/matrix/linear_algebra.hpp)
Dense Matrix
(math/matrix/matrix.hpp)
Pfaffian
(math/matrix/pfaffian.hpp)
Sparse Determinant
(math/matrix/sparse_determinant.hpp)
ModInt
(math/modint.hpp)
ModInt
(math/modint.hpp)
Modular Kth Root
(math/modular_kth_root.hpp)
Modular Square Root
(math/modular_square_root.hpp)
Modular Square Root
(math/modular_square_root.hpp)
Multiplicative Function Prefix Sum
(math/multiplicative_function_prefix_sum.hpp)
Multidimensional Convolution
(math/multivariate_convolution.hpp)
Newton Method
(math/newton_method.hpp)
Newton Method
(math/newton_method.hpp)
Number Theory
(math/number_theory.hpp)
Partition Function
(math/partition_function.hpp)
Prefix Sum of Binomial Coefficients
(math/prefix_sum_of_binom.hpp)
64-bit Prime Factorization
(math/prime_factorization.hpp)
Prime Sieve
(math/prime_sieve.hpp)
Primitive Root
(math/primitive_root.hpp)
Rational Number
(math/rational.hpp)
Rational Approximation
(math/rational_approximation.hpp)
Repunit
(math/repunit.hpp)
Set Power Series
(math/set_power_series.hpp)
Squarefree Count
(math/squarefree_count.hpp)
Stern-Brocot Tree
(math/stern_brocot_tree.hpp)
Subset Convolution
(math/subset_convolution.hpp)
Tetration
(math/tetration.hpp)
Totient Sum
(math/totient_sum.hpp)
Sum of Two Squares
(math/two_square_sum.hpp)
XOR Basis
(math/xor_basis.hpp)
Zeta and Mobius Transform
(math/zeta_mobius_transform.hpp)
Verified with
Code
#ifndef M1UNE_MATH_ALL_HPP
#define M1UNE_MATH_ALL_HPP 1
#include "base_n.hpp"
#include "bernoulli.hpp"
#include "binomial_coefficient_mod.hpp"
#include "bitwise_convolution.hpp"
#include "bit_ceil.hpp"
#include "combinatorics.hpp"
#include "combinatorial_sequences.hpp"
#include "cyclotomic_polynomial.hpp"
#include "discrete_logarithm.hpp"
#include "divisor_convolution.hpp"
#include "gaussian_integer.hpp"
#include "generalized_floor_sum.hpp"
#include "integer_arithmetic.hpp"
#include "lucas.hpp"
#include "fps/all.hpp"
#include "matrix/all.hpp"
#include "modint.hpp"
#include "modular_kth_root.hpp"
#include "modular_square_root.hpp"
#include "multivariate_convolution.hpp"
#include "multiplicative_function_prefix_sum.hpp"
#include "newton_method.hpp"
#include "number_theory.hpp"
#include "partition_function.hpp"
#include "prefix_sum_of_binom.hpp"
#include "prime_factorization.hpp"
#include "primitive_root.hpp"
#include "prime_sieve.hpp"
#include "rational.hpp"
#include "rational_approximation.hpp"
#include "repunit.hpp"
#include "set_power_series.hpp"
#include "squarefree_count.hpp"
#include "stern_brocot_tree.hpp"
#include "subset_convolution.hpp"
#include "tetration.hpp"
#include "totient_sum.hpp"
#include "two_square_sum.hpp"
#include "xor_basis.hpp"
#include "zeta_mobius_transform.hpp"
#endif // M1UNE_MATH_ALL_HPP#line 1 "math/all.hpp"
#line 1 "math/base_n.hpp"
#include <algorithm>
#include <cassert>
#include <concepts>
#include <limits>
#include <optional>
#include <type_traits>
#include <vector>
namespace m1une {
namespace math {
// Returns the canonical most-significant-first base-n digits of a
// nonnegative integer. Zero is represented by one zero digit.
template <std::integral Integer>
requires(!std::same_as<std::remove_cv_t<Integer>, bool>)
std::vector<int> to_base_n(Integer value, int base) {
assert(2 <= base);
if (base < 2) return {};
if constexpr (std::signed_integral<Integer>) {
assert(0 <= value);
if (value < 0) return {};
}
using Unsigned = std::make_unsigned_t<Integer>;
Unsigned remaining = static_cast<Unsigned>(value);
if (remaining == 0) return {0};
std::vector<int> digits;
const unsigned long long unsigned_base = static_cast<unsigned int>(base);
while (remaining != 0) {
digits.push_back(int(remaining % unsigned_base));
remaining = Unsigned(remaining / unsigned_base);
}
std::reverse(digits.begin(), digits.end());
return digits;
}
// Converts most-significant-first base-n digits to an integer.
// Returns nullopt for an invalid digit or when the result does not fit.
template <std::integral Integer, class DigitSequence>
requires(!std::same_as<std::remove_cv_t<Integer>, bool>)
std::optional<Integer> checked_from_base_n(const DigitSequence& digits,
int base) {
assert(2 <= base);
if (base < 2) return std::nullopt;
using Unsigned = std::make_unsigned_t<Integer>;
constexpr Unsigned integer_limit = [] {
if constexpr (std::signed_integral<Integer>) {
return Unsigned(std::numeric_limits<Integer>::max());
} else {
return std::numeric_limits<Integer>::max();
}
}();
const unsigned __int128 limit = integer_limit;
const unsigned __int128 unsigned_base = static_cast<unsigned int>(base);
unsigned __int128 value = 0;
for (const auto& digit_reference : digits) {
using Digit = std::remove_cvref_t<decltype(digit_reference)>;
static_assert(std::integral<Digit>);
static_assert(!std::same_as<Digit, bool>);
Digit digit = digit_reference;
if constexpr (std::signed_integral<Digit>) {
if (digit < 0) return std::nullopt;
}
using UnsignedDigit = std::make_unsigned_t<Digit>;
UnsignedDigit unsigned_digit = static_cast<UnsignedDigit>(digit);
unsigned __int128 converted_digit = unsigned_digit;
if (converted_digit >= unsigned_base) {
return std::nullopt;
}
if (converted_digit > limit ||
value > (limit - converted_digit) / unsigned_base) {
return std::nullopt;
}
value = value * unsigned_base + converted_digit;
}
return static_cast<Integer>(static_cast<Unsigned>(value));
}
// Converts most-significant-first base-n digits to an integer.
// Every digit must be valid and the result must fit in Integer.
template <std::integral Integer, class DigitSequence>
requires(!std::same_as<std::remove_cv_t<Integer>, bool>)
Integer from_base_n(const DigitSequence& digits, int base) {
std::optional<Integer> result = checked_from_base_n<Integer>(digits, base);
assert(result.has_value());
return result.value_or(Integer(0));
}
} // namespace math
} // namespace m1une
#line 1 "math/bernoulli.hpp"
#line 5 "math/bernoulli.hpp"
#include <cstdint>
#line 7 "math/bernoulli.hpp"
#line 1 "math/fps/formal_power_series.hpp"
#line 8 "math/fps/formal_power_series.hpp"
#include <utility>
#line 10 "math/fps/formal_power_series.hpp"
#line 1 "math/modular_square_root.hpp"
#line 7 "math/modular_square_root.hpp"
namespace m1une {
namespace math {
namespace internal {
inline uint64_t modular_square_root_multiply(uint64_t lhs, uint64_t rhs, uint64_t mod) {
return static_cast<uint64_t>(static_cast<unsigned __int128>(lhs) * rhs % mod);
}
inline uint64_t modular_square_root_power(uint64_t base, uint64_t exponent, uint64_t mod) {
uint64_t result = 1 % mod;
while (exponent > 0) {
if (exponent & 1) result = modular_square_root_multiply(result, base, mod);
base = modular_square_root_multiply(base, base, mod);
exponent >>= 1;
}
return result;
}
} // namespace internal
// Returns x such that x * x = value (mod prime), or nullopt when no such x exists.
// The modulus must be prime.
inline std::optional<uint64_t> modular_square_root(uint64_t value, uint64_t prime) {
assert(prime >= 2);
value %= prime;
if (value == 0 || prime == 2) return value;
if (internal::modular_square_root_power(value, (prime - 1) / 2, prime) != 1) {
return std::nullopt;
}
if (prime % 4 == 3) {
return internal::modular_square_root_power(value, prime / 4 + 1, prime);
}
uint64_t odd_part = prime - 1;
int power_of_two = 0;
while ((odd_part & 1) == 0) {
odd_part >>= 1;
power_of_two++;
}
uint64_t non_residue = 2;
while (internal::modular_square_root_power(non_residue, (prime - 1) / 2, prime) == 1) {
non_residue++;
}
uint64_t c = internal::modular_square_root_power(non_residue, odd_part, prime);
uint64_t root = internal::modular_square_root_power(value, odd_part / 2 + 1, prime);
uint64_t remainder = internal::modular_square_root_power(value, odd_part, prime);
int remaining_power = power_of_two;
while (remainder != 1) {
int exponent = 1;
uint64_t squared = internal::modular_square_root_multiply(remainder, remainder, prime);
while (squared != 1) {
squared = internal::modular_square_root_multiply(squared, squared, prime);
exponent++;
}
uint64_t correction = c;
for (int i = 0; i < remaining_power - exponent - 1; i++) {
correction = internal::modular_square_root_multiply(correction, correction, prime);
}
root = internal::modular_square_root_multiply(root, correction, prime);
c = internal::modular_square_root_multiply(correction, correction, prime);
remainder = internal::modular_square_root_multiply(remainder, c, prime);
remaining_power = exponent;
}
return root;
}
template <class Mint>
std::optional<Mint> modular_square_root(Mint value) {
auto root = modular_square_root(static_cast<uint64_t>(value.val()),
static_cast<uint64_t>(Mint::mod()));
if (!root.has_value()) return std::nullopt;
return Mint(*root);
}
} // namespace math
} // namespace m1une
#line 1 "math/fps/convolution.hpp"
#line 5 "math/fps/convolution.hpp"
#include <array>
#line 8 "math/fps/convolution.hpp"
#include <cstring>
#include <new>
#line 13 "math/fps/convolution.hpp"
#if defined(__GNUC__) && !defined(__clang__) && \
(defined(__x86_64__) || defined(__i386__)) && \
!defined(M1UNE_FPS_DISABLE_X86_SIMD)
#include <immintrin.h>
#define M1UNE_FPS_HAS_X86_SIMD 1
#pragma GCC push_options
#pragma GCC target("avx2,bmi")
#endif
#line 1 "math/fps/internal/ntt998_faster.hpp"
#ifdef M1UNE_FPS_HAS_X86_SIMD
#line 9 "math/fps/internal/ntt998_faster.hpp"
#include <immintrin.h>
namespace m1une {
namespace fps {
namespace internal {
namespace fast998_v2 {
// Fixed-modulus AVX2 transform with an in-register degree-8 residue product.
using u32=unsigned;
using u64=unsigned long long;
using idt=std::size_t;
using I256=__m256i;
inline void store256(void*p,I256 x){
_mm256_store_si256((I256*)p,x);
}
inline I256 load256(const void*p){
return _mm256_load_si256((const I256*)p);
}
constexpr u32 shrk(u32 x,u32 M){
return std::min(x,x-M);
}
constexpr u32 dilt(u32 x,u32 M){
return std::min(x,x+M);
}
constexpr u32 reduce(u64 x,u32 niv,u32 M){
return (x+u64(u32(x)*niv)*M)>>32;
}
constexpr u32 mul(u32 x,u32 y,u32 niv,u32 M){
return reduce(u64(x)*y,niv,M);
}
constexpr u32 mul_s(u32 x,u32 y,u32 niv,u32 M){
return shrk(reduce(u64(x)*y,niv,M),M);
}
constexpr u32 qpw(u32 a,u32 b,u32 niv,u32 M,u32 r){
for(;b;b>>=1,a=mul(a,a,niv,M)){
if(b&1){
r=mul(r,a,niv,M);
}
}
return r;
}
constexpr u32 qpw_s(u32 a,u32 b,u32 niv,u32 M,u32 r){
return shrk(qpw(a,b,niv,M,r),M);
}
inline I256 shrk32(I256 x,I256 M){
return _mm256_min_epu32(x,_mm256_sub_epi32(x,M));
}
inline I256 dilt32(I256 x,I256 M){
return _mm256_min_epu32(x,_mm256_add_epi32(x,M));
}
inline I256 Ladd32(I256 x,I256 y,I256){
return _mm256_add_epi32(x,y);
}
inline I256 Lsub32(I256 x,I256 y,I256 M){
return _mm256_add_epi32(_mm256_sub_epi32(x,y),M);
}
inline I256 add32(I256 x,I256 y,I256 M){
return shrk32(_mm256_add_epi32(x,y),M);
}
inline I256 sub32(I256 x,I256 y,I256 M){
return dilt32(_mm256_sub_epi32(x,y),M);
}
template<int msk>inline I256 neg32_m(I256 x,I256 M){
return _mm256_blend_epi32(x,_mm256_sub_epi32(M,x),msk);
}
inline I256 reduce(I256 a,I256 b,I256 niv,I256 M){
I256 c=_mm256_mul_epu32(a,niv),d=_mm256_mul_epu32(b,niv);
c=_mm256_mul_epu32(c,M),d=_mm256_mul_epu32(d,M);
return _mm256_blend_epi32(_mm256_srli_epi64(_mm256_add_epi64(a,c),32),_mm256_add_epi64(b,d),0xaa);
}
inline I256 mul(I256 a,I256 b,I256 niv,I256 M){
return reduce(_mm256_mul_epu32(a,b),_mm256_mul_epu32(_mm256_srli_epi64(a,32),_mm256_srli_epi64(b,32)),niv,M);
}
inline I256 mul_s(I256 a,I256 b,I256 niv,I256 M){
return shrk32(mul(a,b,niv,M),M);
}
inline I256 mul_bsm(I256 a,I256 b,I256 niv,I256 M){
return reduce(_mm256_mul_epu32(a,b),_mm256_mul_epu32(_mm256_srli_epi64(a,32),b),niv,M);
}
inline I256 mul_bsmfxd(I256 a,I256 b,I256 bniv,I256 M){
I256 cc=_mm256_mul_epu32(a,bniv),dd=_mm256_mul_epu32(_mm256_srli_epi64(a,32),bniv);
I256 c=_mm256_mul_epu32(a,b),d=_mm256_mul_epu32(_mm256_srli_epi64(a,32),b);
cc=_mm256_mul_epu32(cc,M),dd=_mm256_mul_epu32(dd,M);
return _mm256_blend_epi32(_mm256_srli_epi64(_mm256_add_epi64(c,cc),32),_mm256_add_epi64(d,dd),0xaa);
}
inline I256 mul_bfxd(I256 a,I256 b,I256 bniv,I256 M){
I256 cc=_mm256_mul_epu32(a,bniv),dd=_mm256_mul_epu32(_mm256_srli_epi64(a,32),_mm256_srli_epi64(bniv,32));
I256 c=_mm256_mul_epu32(a,b),d=_mm256_mul_epu32(_mm256_srli_epi64(a,32),_mm256_srli_epi64(b,32));
cc=_mm256_mul_epu32(cc,M),dd=_mm256_mul_epu32(dd,M);
return _mm256_blend_epi32(_mm256_srli_epi64(_mm256_add_epi64(c,cc),32),_mm256_add_epi64(d,dd),0xaa);
}
inline I256 mul_upd_rt(I256 a,I256 bu,I256 M){
I256 cc=_mm256_mul_epu32(a,bu),c=_mm256_mul_epu32(a,_mm256_srli_epi64(bu,32));
cc=_mm256_mul_epu32(cc,M);
return shrk32(_mm256_srli_epi64(_mm256_add_epi64(c,cc),32),M);
}
constexpr auto _mxlg=26,_lg_itth=6;
constexpr auto _itth=idt(1)<<_lg_itth;
static_assert(_lg_itth%2==0);
struct FNTT32_info{
u32 mod,mod2,niv,one,r2,r3,img,imgniv,RT1[_mxlg];
alignas(32) std::array<u32,8> rt3[_mxlg-2],rt3i[_mxlg-2],bwbr,bwb,bwbi,rt4[_mxlg-3],rt4niv[_mxlg-3],rt4i[_mxlg-3],rt4iniv[_mxlg-3],pr2,pr4,pr2niv,pr4niv,pr2i,pr2iniv,pr4i,pr4iniv;
constexpr FNTT32_info(const u32 m):mod(m),mod2(m*2),niv([&]{u32 n=2+m;for(int i=0;i<4;++i){n*=2+m*n;}return n;}()),one((-m)%m),r2((-u64(m))%m),r3(mul_s(r2,r2,niv,m)),img{},imgniv{},RT1{},rt3{},rt3i{},bwbr{},bwb{},bwbi{},rt4{},rt4niv{},rt4i{},rt4iniv{},pr2{},pr4{},pr2niv{},pr4niv{},pr2i{},pr2iniv{},pr4i{},pr4iniv{}{
const int k=__builtin_ctz(m-1);
u32 _g=mul(3,r2,niv,mod);
for(;;++_g){
if(qpw_s(_g,mod>>1,niv,mod,one)!=one){
break;
}
}
_g=qpw(_g,mod>>k,niv,mod,one);
u32 rt1[_mxlg-1],rt1i[_mxlg-1];
rt1[k-2]=_g,rt1i[k-2]=qpw(_g,mod-2,niv,mod,one);
for(int i=k-2;i>0;--i){
rt1[i-1]=mul(rt1[i],rt1[i],niv,mod);
rt1i[i-1]=mul(rt1i[i],rt1i[i],niv,mod);
}
RT1[k-1]=qpw_s(_g,3,niv,mod,one);
for(int i=k-1;i>0;--i){
RT1[i-1]=mul_s(RT1[i],RT1[i],niv,mod);
}
img=rt1[0],imgniv=img*niv;
bwbr={one,0,one,0,one};
bwb={rt1[1],0,rt1[0],0,mod-mul_s(rt1[0],rt1[1],niv,mod)};
bwbi={rt1i[1],0,rt1i[0],0,mul_s(rt1i[0],rt1i[1],niv,mod)};
u32 pr=one,pri=one;
for(int i=0;i<k-2;++i){
const u32 r=mul_s(pr,rt1[i+1],niv,mod),ri=mul_s(pri,rt1i[i+1],niv,mod);
const u32 r2=mul_s(r,r,niv,mod),r2i=mul_s(ri,ri,niv,mod);
const u32 r3=mul_s(r,r2,niv,mod),r3i=mul_s(ri,r2i,niv,mod);
rt3[i]={r*niv,r,r2*niv,r2,r3*niv,r3};
rt3i[i]={ri*niv,ri,r2i*niv,r2i,r3i*niv,r3i};
pr=mul(pr,rt1i[i+1],niv,mod),pri=mul(pri,rt1[i+1],niv,mod);
}
pr=one,pri=one;
for(int i=0;i<k-3;++i){
const u32 r=mul_s(pr,rt1[i+2],niv,mod),ri=mul_s(pri,rt1i[i+2],niv,mod);
rt4[i][0]=rt4i[i][0]=one;
for(int j=1;j<8;++j){
rt4[i][j]=mul_s(rt4[i][j-1],r,niv,mod);
rt4i[i][j]=mul_s(rt4i[i][j-1],ri,niv,mod);
}
for(int j=0;j<8;++j){
rt4niv[i][j]=rt4[i][j]*niv;
rt4iniv[i][j]=rt4i[i][j]*niv;
}
pr=mul(pr,rt1i[i+2],niv,mod),pri=mul(pri,rt1[i+2],niv,mod);
}
pr2={one,one,one,img,one,one,one,img};
pr4={one,one,one,one,one,rt1[1],img,mul_s(img,rt1[1],niv,mod)};
const u32 nr2=mod-r2,imgr2=mul_s(img,r2,niv,mod);
pr2i={nr2,nr2,nr2,imgr2,nr2,nr2,nr2,imgr2};
pr4i={one,one,one,one,one,rt1i[1],rt1i[0],mul_s(rt1i[0],rt1i[1],niv,mod)};
for(int j=0;j<8;++j){
pr2niv[j]=pr2[j]*niv,pr4niv[j]=pr4[j]*niv;
pr2iniv[j]=pr2i[j]*niv,pr4iniv[j]=pr4i[j]*niv;
}
}
};
inline void vector_dif(I256*const f,const idt n,const FNTT32_info*info){
alignas(32) std::array<u32,8> st_1[_mxlg>>1];
const I256 Mod=_mm256_set1_epi32(info->mod),Mod2=_mm256_set1_epi32(info->mod2),Niv=_mm256_set1_epi32(info->niv);
const I256 Img=_mm256_set1_epi32(info->img),ImgNiv=_mm256_set1_epi32(info->imgniv),id=_mm256_setr_epi32(0,2,0,4,0,2,0,4);
const int lgn=__builtin_ctzll(n);
std::fill(st_1,st_1+(lgn>>1),info->bwb);
const idt nn=n>>(lgn&1),m=std::min(n,_itth),mm=std::min(nn,_itth);
// I256 rr=_mm256_set1_epi32(info->one);
if(nn!=n){
for(idt i=0;i<nn;++i){
auto const p0=f+i,p1=f+nn+i;
const auto f0=load256(p0),f1=load256(p1);
const auto g0=add32(f0,f1,Mod2),g1=Lsub32(f0,f1,Mod2);
store256(p0,g0),store256(p1,g1);
}
}
for(idt L=nn>>2;L>0;L>>=2){
for(idt i=0;i<L;++i){
auto const p0=f+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f1=load256(p1),f3=load256(p3),f2=load256(p2),f0=load256(p0);
const auto g3=mul_bsmfxd(Lsub32(f1,f3,Mod2),Img,ImgNiv,Mod),g1=add32(f1,f3,Mod2);
const auto g0=add32(f0,f2,Mod2),g2=sub32(f0,f2,Mod2);
const auto h0=add32(g0,g1,Mod2),h1=Lsub32(g0,g1,Mod2);
const auto h2=Ladd32(g2,g3,Mod2),h3=Lsub32(g2,g3,Mod2);
store256(p0,h0),store256(p1,h1),store256(p2,h2),store256(p3,h3);
}
}
for(idt j=0;j<n;j+=m){
int t=((j==0)?std::min(_lg_itth,lgn):__builtin_ctzll(j))&-2,p=(t-2)>>1;
for(idt L=(idt(1)<<t)>>2;L>=_itth;L>>=2,t-=2,--p){
auto rt=load256(st_1+p);
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r1Niv=_mm256_permutevar8x32_epi32(_mm256_mul_epu32(rt,Niv),id);
rt=mul_upd_rt(rt,load256(info->rt3+__builtin_ctzll(~j>>t)),Mod);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB),nr3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
const auto r2Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_BBBB),nr3Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_DDDD);
store256(st_1+p,rt);
for(idt i=0;i<L;++i){
auto const p0=f+i+j,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f1=load256(p1),f3=load256(p3),f2=load256(p2),f0=load256(p0);
const auto g1=mul_bsmfxd(f1,r1,r1Niv,Mod),ng3=mul_bsmfxd(f3,nr3,nr3Niv,Mod);
const auto g2=mul_bsmfxd(f2,r2,r2Niv,Mod),g0=shrk32(f0,Mod2);
const auto h3=mul_bsmfxd(Ladd32(g1,ng3,Mod2),Img,ImgNiv,Mod),h1=sub32(g1,ng3,Mod2);
const auto h0=add32(g0,g2,Mod2),h2=sub32(g0,g2,Mod2);
const auto u0=Ladd32(h0,h1,Mod2),u1=Lsub32(h0,h1,Mod2);
const auto u2=Ladd32(h2,h3,Mod2),u3=Lsub32(h2,h3,Mod2);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
}
I256*const g=f+j;
for(idt l=mm,L=mm>>2;L;l=L,L>>=2,t-=2,--p){
auto rt=load256(st_1+p);
for(idt i=(j==0?l:0),k=(j+i)>>t;i<m;i+=l,++k){
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB);
const auto nr3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
for(idt j=0;j<L;++j){
auto const p0=g+i+j,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f1=load256(p1),f3=load256(p3),f2=load256(p2),f0=load256(p0);
const auto g1=mul_bsm(f1,r1,Niv,Mod),ng3=mul_bsm(f3,nr3,Niv,Mod);
const auto g2=mul_bsm(f2,r2,Niv,Mod),g0=shrk32(f0,Mod2);
const auto h3=mul_bsmfxd(Ladd32(g1,ng3,Mod2),Img,ImgNiv,Mod),h1=sub32(g1,ng3,Mod2);
const auto h0=add32(g0,g2,Mod2),h2=sub32(g0,g2,Mod2);
const auto u0=Ladd32(h0,h1,Mod2),u1=Lsub32(h0,h1,Mod2);
const auto u2=Ladd32(h2,h3,Mod2),u3=Lsub32(h2,h3,Mod2);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
rt=mul_upd_rt(rt,load256(info->rt3+__builtin_ctzll(~k)),Mod);
}
store256(st_1+p,rt);
}
// const auto pr2=load256(&info->pr2),pr4=load256(&info->pr4);
// const auto pr2Niv=load256(&info->pr2niv),pr4Niv=load256(&info->pr4niv);
// for(idt i=j;i<j+m;++i){
// auto fi=load256(f+i);
// fi=mul(fi,rr,Niv,Mod);
// rr=shrk32(mul_bfxd(rr,load256(info->rt4+__builtin_ctzll(~i)),load256(info->rt4niv+__builtin_ctzll(~i)),Mod),Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xf0>(fi,Mod2),_mm256_permute2x128_si256(fi,fi,1),Mod2),pr4,pr4Niv,Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xcc>(fi,Mod2),_mm256_shuffle_epi32(fi,0x4e),Mod2),pr2,pr2Niv,Mod);
// fi=sub32(_mm256_shuffle_epi32(fi,0xb1),neg32_m<0x55>(fi,Mod2),Mod2);
// store256(f+i,fi);
// }
}
}
template<bool shrk=false>inline void vector_dit(I256*const f,idt n,const FNTT32_info*const info){
alignas(32) std::array<u32,8> st_1[_mxlg>>1];
const I256 Mod=_mm256_set1_epi32(info->mod),Mod2=_mm256_set1_epi32(info->mod2),Niv=_mm256_set1_epi32(info->niv);
const I256 Img=_mm256_set1_epi32(info->img),ImgNiv=_mm256_set1_epi32(info->imgniv),id=_mm256_setr_epi32(0,2,0,4,0,2,0,4);
const int lgn=__builtin_ctzll(n);
std::fill(st_1,st_1+(_lg_itth>>1),info->bwbr);
std::fill(st_1+(_lg_itth>>1),st_1+(_mxlg>>1),info->bwbi);
const idt nn=n>>(lgn&1),mm=std::min(nn,_itth);
// I256 rr=_mm256_set1_epi32((info->mod-1)>>(lgn+3));
for(idt j=0;j<n;j+=mm){
// const auto pr2=load256(&info->pr2i),pr4=load256(&info->pr4i);
// const auto pr2Niv=load256(&info->pr2iniv),pr4Niv=load256(&info->pr4iniv);
// for(idt i=j;i<j+mm;++i){
// auto fi=load256(f+i);
// const auto rt=rr;
// rr=shrk32(mul_bfxd(rr,load256(info->rt4i+__builtin_ctzll(~i)),load256(info->rt4iniv+__builtin_ctzll(~i)),Mod),Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xaa>(fi,Mod2),_mm256_shuffle_epi32(fi,0xb1),Mod2),pr2,pr2Niv,Mod);
// fi=mul_bfxd(Ladd32(neg32_m<0xcc>(fi,Mod2),_mm256_shuffle_epi32(fi,0x4e),Mod2),pr4,pr4Niv,Mod);
// fi=mul(Ladd32(neg32_m<0xf0>(fi,Mod2),_mm256_permute2x128_si256(fi,fi,1),Mod2),rt,Niv,Mod);
// store256(f+i,fi);
// }
I256*const g=f+j;
int t=2,p=0;
for(idt l=4,L=1;l<=mm;L=l,l<<=2,t+=2,++p){
auto rt=load256(st_1+p);
for(idt i=0,k=j>>t;i<mm;i+=l,++k){
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB);
const auto r3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
for(idt j=0;j<L;++j){
auto const p0=g+i+j,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f0=load256(p0),f1=load256(p1),f2=load256(p2),f3=load256(p3);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto g2=add32(f2,f3,Mod2),g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod);
const auto h0=Ladd32(g0,g2,Mod2),h1=Ladd32(g1,g3,Mod2);
const auto h2=Lsub32(g0,g2,Mod2),h3=Lsub32(g1,g3,Mod2);
const auto u0=shrk32(h0,Mod2),u1=mul_bsm(h1,r1,Niv,Mod);
const auto u2=mul_bsm(h2,r2,Niv,Mod),u3=mul_bsm(h3,r3,Niv,Mod);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
rt=mul_upd_rt(rt,load256(info->rt3i+__builtin_ctzll(~k)),Mod);
}
store256(st_1+p,rt);
}
int tt=std::min(__builtin_ctzll(~(j>>_lg_itth))+_lg_itth,lgn);
for(idt L=_itth,l=L<<2;t<=tt;L=l,l<<=2,t+=2,++p){
if((j+_itth)==l){
if(shrk && l==n){
for(idt i=0;i<L;++i){
auto const p0=f+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f2=load256(p2),f3=load256(p3),f0=load256(p0),f1=load256(p1);
const auto g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod),g2=add32(f2,f3,Mod2);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto h0=add32(g0,g2,Mod2),h1=add32(g1,g3,Mod2);
const auto h2=sub32(g0,g2,Mod2),h3=sub32(g1,g3,Mod2);
const auto u0=shrk32(h0,Mod),u1=shrk32(h1,Mod);
const auto u2=shrk32(h2,Mod),u3=shrk32(h3,Mod);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
}
else{
for(idt i=0;i<L;++i){
auto const p0=f+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f2=load256(p2),f3=load256(p3),f0=load256(p0),f1=load256(p1);
const auto g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod),g2=add32(f2,f3,Mod2);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto h0=add32(g0,g2,Mod2),h1=add32(g1,g3,Mod2);
const auto h2=sub32(g0,g2,Mod2),h3=sub32(g1,g3,Mod2);
store256(p0,h0),store256(p1,h1),store256(p2,h2),store256(p3,h3);
}
}
}
else{
auto rt=load256(st_1+p);
const auto r1=_mm256_permutevar8x32_epi32(rt,id);
const auto r1Niv=_mm256_permutevar8x32_epi32(_mm256_mul_epu32(rt,Niv),id);
rt=mul_upd_rt(rt,load256(info->rt3i+__builtin_ctzll(~j>>t)),Mod);
const auto r2=_mm256_shuffle_epi32(r1,_MM_PERM_BBBB),r3=_mm256_shuffle_epi32(r1,_MM_PERM_DDDD);
const auto r2Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_BBBB),r3Niv=_mm256_shuffle_epi32(r1Niv,_MM_PERM_DDDD);
store256(st_1+p,rt);
for(idt i=0;i<L;++i){
auto const p0=f+j+_itth-l+i,p1=p0+L,p2=p1+L,p3=p2+L;
const auto f0=load256(p0),f1=load256(p1),f2=load256(p2),f3=load256(p3);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
const auto g2=add32(f2,f3,Mod2),g3=mul_bsmfxd(Lsub32(f3,f2,Mod2),Img,ImgNiv,Mod);
const auto h0=Ladd32(g0,g2,Mod2),h1=Ladd32(g1,g3,Mod2);
const auto h2=Lsub32(g0,g2,Mod2),h3=Lsub32(g1,g3,Mod2);
const auto u0=shrk32(h0,Mod2),u1=mul_bsmfxd(h1,r1,r1Niv,Mod);
const auto u2=mul_bsmfxd(h2,r2,r2Niv,Mod),u3=mul_bsmfxd(h3,r3,r3Niv,Mod);
store256(p0,u0),store256(p1,u1),store256(p2,u2),store256(p3,u3);
}
}
}
}
if(shrk && nn==n && n<=_itth){
for(idt i=0;i<n;++i){
const auto f0=load256(f+i);
store256(f+i,shrk32(f0,Mod));
}
}
if(nn!=n){
for(idt i=0;i<nn;++i){
auto const p0=f+i,p1=f+nn+i;
const auto f0=load256(p0),f1=load256(p1);
const auto g0=add32(f0,f1,Mod2),g1=sub32(f0,f1,Mod2);
if constexpr(shrk){
const auto h0=shrk32(g0,Mod),h1=shrk32(g1,Mod);
store256(p0,h0),store256(p1,h1);
}
else{
store256(p0,g0),store256(p1,g1);
}
}
}
}
// Returns fx * f[0,8) * g[0,8) (mod x^8 - ww).
[[gnu::always_inline]] inline I256 convolve8(const I256*f,const I256*g,I256 ww,I256 fx,I256 Niv,I256 Mod,I256 Mod2){
const auto raa=load256(f),rbb=load256(g);
const auto taa=shrk32(raa,Mod2),bb=shrk32(mul_bsm(rbb,fx,Niv,Mod),Mod);
const auto aw=shrk32(mul_bsm(taa,ww,Niv,Mod),Mod);
const auto aa=shrk32(taa,Mod);
const auto awa=_mm256_permute2x128_si256(aa,aw,3);
const auto b0=_mm256_permute4x64_epi64(bb,0x00),b1=_mm256_shuffle_epi32(b0,_MM_PERM_CDAB);
const auto a0=aa,a1=_mm256_srli_epi64(a0,32);
const auto aw7=_mm256_alignr_epi8(aa,awa,12);
auto res00=_mm256_mul_epu32(a0,b0);
auto res01=_mm256_mul_epu32(a1,b0);
auto res10=_mm256_mul_epu32(aw7,b1);
auto res11=_mm256_mul_epu32(a0,b1);
const auto b2=_mm256_permute4x64_epi64(bb,0x55),b3=_mm256_shuffle_epi32(b2,_MM_PERM_CDAB);
const auto aw6=_mm256_alignr_epi8(aa,awa,8);
const auto aw5=_mm256_alignr_epi8(aa,awa,4);
res00=_mm256_add_epi64(res00,_mm256_mul_epu32(aw6,b2));
res01=_mm256_add_epi64(res01,_mm256_mul_epu32(aw7,b2));
res10=_mm256_add_epi64(res10,_mm256_mul_epu32(aw5,b3));
res11=_mm256_add_epi64(res11,_mm256_mul_epu32(aw6,b3));
const auto b4=_mm256_permute4x64_epi64(bb,0xaa),b5=_mm256_shuffle_epi32(b4,_MM_PERM_CDAB);
const auto aw3=_mm256_alignr_epi8(awa,aw,12);
res00=_mm256_add_epi64(res00,_mm256_mul_epu32(awa,b4));
res01=_mm256_add_epi64(res01,_mm256_mul_epu32(aw5,b4));
res10=_mm256_add_epi64(res10,_mm256_mul_epu32(aw3,b5));
res11=_mm256_add_epi64(res11,_mm256_mul_epu32(awa,b5));
const auto b6=_mm256_permute4x64_epi64(bb,0xff),b7=_mm256_shuffle_epi32(b6,_MM_PERM_CDAB);
const auto aw2=_mm256_alignr_epi8(awa,aw,8);
const auto aw1=_mm256_alignr_epi8(awa,aw,4);
res00=_mm256_add_epi64(res00,_mm256_mul_epu32(aw2,b6));
res01=_mm256_add_epi64(res01,_mm256_mul_epu32(aw3,b6));
res10=_mm256_add_epi64(res10,_mm256_mul_epu32(aw1,b7));
res11=_mm256_add_epi64(res11,_mm256_mul_epu32(aw2,b7));
res00=_mm256_add_epi64(res00,res10);
res01=_mm256_add_epi64(res01,res11);
return shrk32(reduce(res00,res01,Niv,Mod),Mod2);
}
inline void vector_convolution_direct(I256*f,const I256*g,idt lm,const FNTT32_info*const info){
u32 RR=info->one;
const auto mod=info->mod,niv=info->niv;
const auto Fx=_mm256_set1_epi32(mul_s((mod-((mod-1)>>(__builtin_ctzll(lm)))),info->r3,niv,mod));
const auto Niv=_mm256_set1_epi32(niv),Mod=_mm256_set1_epi32(mod),Mod2=_mm256_set1_epi32(info->mod2);
for(idt i=0;i<lm;++i){
store256(f+i,convolve8(f+i,g+i,_mm256_set1_epi32(RR),Fx,Niv,Mod,Mod2));
RR=mul(RR,info->RT1[__builtin_ctzll(~i)],niv,mod);
}
}
inline void vector_convolution_accumulate(I256*const result,const I256*const f,
const I256*const g,idt lm,
const FNTT32_info*const info){
u32 RR=info->one;
const auto mod=info->mod,niv=info->niv;
const auto Fx=_mm256_set1_epi32(mul_s((mod-((mod-1)>>(__builtin_ctzll(lm)))),info->r3,niv,mod));
const auto Niv=_mm256_set1_epi32(niv),Mod=_mm256_set1_epi32(mod),Mod2=_mm256_set1_epi32(info->mod2);
for(idt i=0;i<lm;++i){
const auto product=convolve8(f+i,g+i,_mm256_set1_epi32(RR),Fx,Niv,Mod,Mod2);
store256(result+i,add32(load256(result+i),product,Mod2));
RR=mul(RR,info->RT1[__builtin_ctzll(~i)],niv,mod);
}
}
} // namespace fast998_v2
} // namespace internal
} // namespace fps
} // namespace m1une
#endif // M1UNE_FPS_HAS_X86_SIMD
#line 24 "math/fps/convolution.hpp"
#ifdef M1UNE_FPS_HAS_X86_SIMD
#pragma GCC pop_options
#endif
#line 1 "math/modint.hpp"
#line 6 "math/modint.hpp"
#include <iostream>
#line 9 "math/modint.hpp"
namespace m1une {
namespace math {
template <uint32_t Modulus>
struct ModInt {
static_assert(0 < Modulus, "Modulus must be positive");
private:
uint32_t _v;
public:
static constexpr uint32_t mod() {
return Modulus;
}
static constexpr ModInt raw(uint32_t v) noexcept {
ModInt x;
x._v = v;
return x;
}
constexpr ModInt() noexcept : _v(0) {}
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, int> = 0>
constexpr ModInt(Integer v) noexcept {
if constexpr (std::is_signed_v<Integer>) {
int64_t x = static_cast<int64_t>(v) % static_cast<int64_t>(Modulus);
if (x < 0) x += Modulus;
_v = static_cast<uint32_t>(x);
} else {
_v = static_cast<uint32_t>(static_cast<uint64_t>(v) % Modulus);
}
}
constexpr uint32_t val() const noexcept {
return _v;
}
constexpr ModInt& operator++() noexcept {
_v++;
if (_v == Modulus) _v = 0;
return *this;
}
constexpr ModInt& operator--() noexcept {
if (_v == 0) _v = Modulus;
_v--;
return *this;
}
constexpr ModInt operator++(int) noexcept {
ModInt res = *this;
++*this;
return res;
}
constexpr ModInt operator--(int) noexcept {
ModInt res = *this;
--*this;
return res;
}
constexpr ModInt& operator+=(const ModInt& rhs) noexcept {
_v += rhs._v;
if (_v >= Modulus) _v -= Modulus;
return *this;
}
constexpr ModInt& operator-=(const ModInt& rhs) noexcept {
_v -= rhs._v;
if (_v >= Modulus) _v += Modulus;
return *this;
}
constexpr ModInt& operator*=(const ModInt& rhs) noexcept {
uint64_t z = _v;
z *= rhs._v;
_v = static_cast<uint32_t>(z % Modulus);
return *this;
}
constexpr ModInt& operator/=(const ModInt& rhs) noexcept {
return *this *= rhs.inv();
}
constexpr ModInt operator+(const ModInt& rhs) const noexcept {
return ModInt(*this) += rhs;
}
constexpr ModInt operator-(const ModInt& rhs) const noexcept {
return ModInt(*this) -= rhs;
}
constexpr ModInt operator*(const ModInt& rhs) const noexcept {
return ModInt(*this) *= rhs;
}
constexpr ModInt operator/(const ModInt& rhs) const noexcept {
return ModInt(*this) /= rhs;
}
constexpr bool operator==(const ModInt& rhs) const noexcept {
return _v == rhs._v;
}
constexpr bool operator!=(const ModInt& rhs) const noexcept {
return _v != rhs._v;
}
constexpr ModInt pow(long long n) const noexcept {
ModInt res = raw(1 % Modulus);
ModInt x = n < 0 ? inv() : *this;
uint64_t exponent = n < 0 ? uint64_t(-(n + 1)) + 1 : uint64_t(n);
while (exponent > 0) {
if (exponent & 1) res *= x;
x *= x;
exponent >>= 1;
}
return res;
}
constexpr ModInt inv() const noexcept {
int64_t a = _v, b = Modulus, u = 1, v = 0;
while (b) {
int64_t t = a / b;
a -= t * b;
std::swap(a, b);
u -= t * v;
std::swap(u, v);
}
assert(a == 1);
u %= Modulus;
if (u < 0) u += Modulus;
return raw(static_cast<uint32_t>(u));
}
friend std::ostream& operator<<(std::ostream& os, const ModInt& rhs) {
return os << rhs._v;
}
friend std::istream& operator>>(std::istream& is, ModInt& rhs) {
long long v;
is >> v;
rhs = ModInt(v);
return is;
}
};
using modint998244353 = ModInt<998244353>;
using modint1000000007 = ModInt<1000000007>;
template <int Id = 0>
struct DynamicModInt {
private:
uint32_t _v;
inline static uint32_t _mod = 1;
public:
static uint32_t mod() noexcept {
return _mod;
}
static void set_mod(uint32_t modulus) noexcept {
assert(modulus > 0);
assert(modulus <= uint32_t(1) << 31);
_mod = modulus;
}
static DynamicModInt raw(uint32_t v) noexcept {
assert(v < _mod);
DynamicModInt x;
x._v = v;
return x;
}
DynamicModInt() noexcept : _v(0) {}
template <class Integer, std::enable_if_t<std::is_integral_v<Integer>, int> = 0>
DynamicModInt(Integer v) noexcept {
if constexpr (std::is_signed_v<Integer>) {
int64_t x = static_cast<int64_t>(v) % static_cast<int64_t>(_mod);
if (x < 0) x += _mod;
_v = static_cast<uint32_t>(x);
} else {
_v = static_cast<uint32_t>(static_cast<uint64_t>(v) % _mod);
}
}
uint32_t val() const noexcept {
return _v;
}
DynamicModInt& operator++() noexcept {
_v++;
if (_v == _mod) _v = 0;
return *this;
}
DynamicModInt& operator--() noexcept {
if (_v == 0) _v = _mod;
_v--;
return *this;
}
DynamicModInt operator++(int) noexcept {
DynamicModInt result = *this;
++*this;
return result;
}
DynamicModInt operator--(int) noexcept {
DynamicModInt result = *this;
--*this;
return result;
}
DynamicModInt& operator+=(const DynamicModInt& rhs) noexcept {
_v += rhs._v;
if (_v >= _mod) _v -= _mod;
return *this;
}
DynamicModInt& operator-=(const DynamicModInt& rhs) noexcept {
_v -= rhs._v;
if (_v >= _mod) _v += _mod;
return *this;
}
DynamicModInt& operator*=(const DynamicModInt& rhs) noexcept {
_v = static_cast<uint32_t>(uint64_t(_v) * rhs._v % _mod);
return *this;
}
DynamicModInt& operator/=(const DynamicModInt& rhs) noexcept {
return *this *= rhs.inv();
}
DynamicModInt operator+(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) += rhs;
}
DynamicModInt operator-(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) -= rhs;
}
DynamicModInt operator*(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) *= rhs;
}
DynamicModInt operator/(const DynamicModInt& rhs) const noexcept {
return DynamicModInt(*this) /= rhs;
}
bool operator==(const DynamicModInt& rhs) const noexcept {
return _v == rhs._v;
}
bool operator!=(const DynamicModInt& rhs) const noexcept {
return _v != rhs._v;
}
DynamicModInt pow(long long exponent) const noexcept {
DynamicModInt result = raw(1 % _mod);
DynamicModInt base = exponent < 0 ? inv() : *this;
uint64_t magnitude =
exponent < 0 ? uint64_t(-(exponent + 1)) + 1 : uint64_t(exponent);
while (magnitude > 0) {
if (magnitude & 1) result *= base;
base *= base;
magnitude >>= 1;
}
return result;
}
DynamicModInt inv() const noexcept {
int64_t a = _v, b = _mod, u = 1, v = 0;
while (b) {
int64_t quotient = a / b;
a -= quotient * b;
std::swap(a, b);
u -= quotient * v;
std::swap(u, v);
}
assert(a == 1);
u %= _mod;
if (u < 0) u += _mod;
return raw(static_cast<uint32_t>(u));
}
friend std::ostream& operator<<(std::ostream& os, const DynamicModInt& rhs) {
return os << rhs._v;
}
friend std::istream& operator>>(std::istream& is, DynamicModInt& rhs) {
long long value;
is >> value;
rhs = DynamicModInt(value);
return is;
}
};
} // namespace math
} // namespace m1une
#line 29 "math/fps/convolution.hpp"
namespace m1une {
namespace fps {
namespace internal {
template <class Mint, class = void>
struct has_static_modulus : std::false_type {};
template <class Mint>
struct has_static_modulus<
Mint, std::void_t<decltype(std::integral_constant<uint32_t, Mint::mod()>{})>>
: std::true_type {};
constexpr uint32_t primitive_root_constexpr(uint32_t mod) {
if (mod == 2) return 1;
if (mod == 167772161) return 3;
if (mod == 469762049) return 3;
if (mod == 754974721) return 11;
if (mod == 998244353) return 3;
if (mod == 1224736769) return 3;
uint32_t divisors[32] = {};
int count = 0;
uint32_t x = mod - 1;
for (uint32_t p = 2; uint64_t(p) * p <= x; p++) {
if (x % p != 0) continue;
divisors[count++] = p;
while (x % p == 0) x /= p;
}
if (x > 1) divisors[count++] = x;
for (uint32_t g = 2;; g++) {
bool ok = true;
for (int i = 0; i < count; i++) {
uint64_t value = 1;
uint64_t base = g;
uint32_t exponent = (mod - 1) / divisors[i];
while (exponent > 0) {
if (exponent & 1) value = value * base % mod;
base = base * base % mod;
exponent >>= 1;
}
if (value == 1) {
ok = false;
break;
}
}
if (ok) return g;
}
}
constexpr int two_adic_order(uint32_t x) {
int result = 0;
while ((x & 1) == 0) {
x >>= 1;
result++;
}
return result;
}
template <class Mint>
struct NttRoots {
static constexpr int max_base = two_adic_order(Mint::mod() - 1);
std::array<Mint, max_base + 1> root;
std::array<Mint, max_base + 1> inverse_root;
std::array<Mint, max_base> rate;
std::array<Mint, max_base> inverse_rate;
std::array<Mint, max_base> rate_radix4;
std::array<Mint, max_base> inverse_rate_radix4;
NttRoots() {
constexpr uint32_t primitive_root = primitive_root_constexpr(Mint::mod());
for (int level = 1; level <= max_base; level++) {
root[level] = Mint(primitive_root).pow((Mint::mod() - 1) >> level);
inverse_root[level] = root[level].inv();
}
Mint product = 1;
Mint inverse_product = 1;
for (int i = 0; i + 1 < max_base; i++) {
rate[i] = root[i + 2] * product;
inverse_rate[i] = inverse_root[i + 2] * inverse_product;
product *= inverse_root[i + 2];
inverse_product *= root[i + 2];
}
product = 1;
inverse_product = 1;
for (int i = 0; i + 2 < max_base; i++) {
rate_radix4[i] = root[i + 3] * product;
inverse_rate_radix4[i] = inverse_root[i + 3] * inverse_product;
product *= inverse_root[i + 3];
inverse_product *= root[i + 3];
}
}
};
template <class Mint>
const NttRoots<Mint>& ntt_roots() {
static const NttRoots<Mint> roots;
return roots;
}
template <class Mint>
void ntt(std::vector<Mint>& a, bool inverse, bool normalize = true) {
const int n = int(a.size());
assert(n > 0 && (n & (n - 1)) == 0);
assert((Mint::mod() - 1) % uint32_t(n) == 0);
const auto& roots = ntt_roots<Mint>();
const int height = two_adic_order(uint32_t(n));
if (!inverse) {
int phase = 0;
while (phase < height) {
if (height - phase == 1) {
const int width = 1 << (height - phase - 1);
Mint twiddle = 1;
for (int block = 0; block < (1 << phase); block++) {
const int offset = block << (height - phase);
for (int i = 0; i < width; i++) {
const Mint left = a[offset + i];
const Mint right = a[offset + i + width] * twiddle;
a[offset + i] = left + right;
a[offset + i + width] = left - right;
}
if (block + 1 != (1 << phase))
twiddle *= roots.rate[__builtin_ctz(~uint32_t(block))];
}
phase++;
continue;
}
const int width = 1 << (height - phase - 2);
Mint twiddle = 1;
const Mint imaginary = roots.root[2];
for (int block = 0; block < (1 << phase); block++) {
const Mint twiddle2 = twiddle * twiddle;
const Mint twiddle3 = twiddle2 * twiddle;
const int offset = block << (height - phase);
for (int i = 0; i < width; i++) {
const uint64_t mod2 = uint64_t(Mint::mod()) * Mint::mod();
const uint64_t a0 = a[offset + i].val();
const uint64_t a1 = uint64_t(a[offset + i + width].val()) * twiddle.val();
const uint64_t a2 =
uint64_t(a[offset + i + 2 * width].val()) * twiddle2.val();
const uint64_t a3 =
uint64_t(a[offset + i + 3 * width].val()) * twiddle3.val();
const uint64_t a1na3i =
uint64_t(Mint(a1 + mod2 - a3).val()) * imaginary.val();
const uint64_t negative_a2 = mod2 - a2;
a[offset + i] = Mint(a0 + a2 + a1 + a3);
a[offset + i + width] = Mint(a0 + a2 + 2 * mod2 - a1 - a3);
a[offset + i + 2 * width] = Mint(a0 + negative_a2 + a1na3i);
a[offset + i + 3 * width] = Mint(a0 + negative_a2 + mod2 - a1na3i);
}
if (block + 1 != (1 << phase))
twiddle *= roots.rate_radix4[__builtin_ctz(~uint32_t(block))];
}
phase += 2;
}
} else {
int phase = height;
while (phase > 0) {
if (phase == 1) {
const int width = 1 << (height - phase);
Mint twiddle = 1;
for (int block = 0; block < (1 << (phase - 1)); block++) {
const int offset = block << (height - phase + 1);
for (int i = 0; i < width; i++) {
const Mint left = a[offset + i];
const Mint right = a[offset + i + width];
a[offset + i] = left + right;
a[offset + i + width] = (left - right) * twiddle;
}
if (block + 1 != (1 << (phase - 1)))
twiddle *= roots.inverse_rate[__builtin_ctz(~uint32_t(block))];
}
phase--;
continue;
}
const int width = 1 << (height - phase);
Mint twiddle = 1;
const Mint inverse_imaginary = roots.inverse_root[2];
for (int block = 0; block < (1 << (phase - 2)); block++) {
const Mint twiddle2 = twiddle * twiddle;
const Mint twiddle3 = twiddle2 * twiddle;
const int offset = block << (height - phase + 2);
for (int i = 0; i < width; i++) {
const uint64_t a0 = a[offset + i].val();
const uint64_t a1 = a[offset + i + width].val();
const uint64_t a2 = a[offset + i + 2 * width].val();
const uint64_t a3 = a[offset + i + 3 * width].val();
const uint64_t a2na3i =
uint64_t(Mint((Mint::mod() + a2 - a3) * inverse_imaginary.val()).val());
a[offset + i] = Mint(a0 + a1 + a2 + a3);
a[offset + i + width] =
Mint((a0 + Mint::mod() - a1 + a2na3i) * twiddle.val());
a[offset + i + 2 * width] = Mint(
(a0 + a1 + 2ULL * Mint::mod() - a2 - a3) * twiddle2.val());
a[offset + i + 3 * width] = Mint(
(a0 + Mint::mod() - a1 + Mint::mod() - a2na3i) * twiddle3.val());
}
if (block + 1 != (1 << (phase - 2)))
twiddle *= roots.inverse_rate_radix4[__builtin_ctz(~uint32_t(block))];
}
phase -= 2;
}
if (normalize) {
const Mint inverse_n = Mint(n).inv();
for (Mint& value : a) value *= inverse_n;
}
}
}
#ifdef M1UNE_FPS_HAS_X86_SIMD
#pragma GCC push_options
#pragma GCC target("avx2,bmi")
template <class Mint>
__attribute__((target("avx2,bmi"), hot))
std::vector<Mint> convolution_998244353_simd(const std::vector<Mint>& a,
const std::vector<Mint>& b) {
const int result_size = int(a.size() + b.size() - 1);
int n = 1;
while (n < result_size) n <<= 1;
const bool squaring = &a == &b;
auto* transformed_a = static_cast<uint32_t*>(
::operator new[](sizeof(uint32_t) * n, std::align_val_t(32)));
auto* transformed_b = squaring
? transformed_a
: static_cast<uint32_t*>(::operator new[](
sizeof(uint32_t) * n, std::align_val_t(32)));
if constexpr (std::is_same_v<Mint, math::ModInt<998244353>>) {
static_assert(sizeof(Mint) == sizeof(uint32_t) && std::is_trivially_copyable_v<Mint>);
std::memcpy(transformed_a, a.data(), sizeof(uint32_t) * a.size());
if (!squaring)
std::memcpy(transformed_b, b.data(), sizeof(uint32_t) * b.size());
} else {
for (int i = 0; i < int(a.size()); i++) transformed_a[i] = a[i].val();
if (!squaring)
for (int i = 0; i < int(b.size()); i++) transformed_b[i] = b[i].val();
}
std::memset(transformed_a + a.size(), 0, sizeof(uint32_t) * (n - a.size()));
if (!squaring)
std::memset(transformed_b + b.size(), 0, sizeof(uint32_t) * (n - b.size()));
static constexpr fast998_v2::FNTT32_info transform(998244353);
const std::size_t vector_size = std::size_t(n) >> 3;
fast998_v2::vector_dif(reinterpret_cast<__m256i*>(transformed_a), vector_size, &transform);
if (!squaring)
fast998_v2::vector_dif(reinterpret_cast<__m256i*>(transformed_b), vector_size,
&transform);
fast998_v2::vector_convolution_direct(
reinterpret_cast<__m256i*>(transformed_a),
reinterpret_cast<const __m256i*>(transformed_b), vector_size, &transform);
fast998_v2::vector_dit<true>(reinterpret_cast<__m256i*>(transformed_a), vector_size,
&transform);
std::vector<Mint> result(result_size);
for (int j = 0; j < result_size; j++) result[j] = Mint::raw(transformed_a[j]);
::operator delete[](transformed_a, std::align_val_t(32));
if (!squaring) ::operator delete[](transformed_b, std::align_val_t(32));
return result;
}
#pragma GCC pop_options
#endif
} // namespace internal
template <class Mint>
std::vector<Mint> convolution_naive(const std::vector<Mint>& a, const std::vector<Mint>& b) {
if (a.empty() || b.empty()) return {};
std::vector<Mint> result(a.size() + b.size() - 1);
if (a.size() < b.size()) {
for (int i = 0; i < int(a.size()); i++) {
for (int j = 0; j < int(b.size()); j++) result[i + j] += a[i] * b[j];
}
} else {
for (int j = 0; j < int(b.size()); j++) {
for (int i = 0; i < int(a.size()); i++) result[i + j] += a[i] * b[j];
}
}
return result;
}
template <class Mint>
std::vector<Mint> convolution_ntt(const std::vector<Mint>& a, const std::vector<Mint>& b) {
const int result_size = int(a.size() + b.size() - 1);
int n = 1;
while (n < result_size) n <<= 1;
assert((Mint::mod() - 1) % uint32_t(n) == 0);
#ifdef M1UNE_FPS_HAS_X86_SIMD
if constexpr (Mint::mod() == 998244353) {
if (n >= 64 && __builtin_cpu_supports("avx2"))
return internal::convolution_998244353_simd(a, b);
}
#endif
// Allocate the padded buffers directly. Constructing from the inputs and
// then resizing used to allocate and copy both large operands twice.
const bool squaring = &a == &b;
std::vector<Mint> fa(n);
std::copy(a.begin(), a.end(), fa.begin());
internal::ntt(fa, false);
const Mint inverse_n = Mint(n).inv();
if (squaring) {
for (int i = 0; i < n; i++) fa[i] *= fa[i] * inverse_n;
} else {
std::vector<Mint> fb(n);
std::copy(b.begin(), b.end(), fb.begin());
internal::ntt(fb, false);
for (int i = 0; i < n; i++) fa[i] *= fb[i] * inverse_n;
}
internal::ntt(fa, true, false);
fa.resize(result_size);
return fa;
}
namespace internal {
template <class Mint>
std::vector<Mint> convolution_998244353_blocked_scalar(const std::vector<Mint>& a,
const std::vector<Mint>& b,
int transform_size) {
assert(Mint::mod() == 998244353);
assert(transform_size >= 2 && (transform_size & (transform_size - 1)) == 0);
assert((Mint::mod() - 1) % uint32_t(transform_size) == 0);
const int block_size = transform_size / 2;
const int a_blocks = int((a.size() + block_size - 1) / block_size);
const int b_blocks = int((b.size() + block_size - 1) / block_size);
auto transform_blocks = [&](const std::vector<Mint>& values, int block_count) {
std::vector<std::vector<Mint>> blocks;
blocks.reserve(block_count);
for (int block = 0; block < block_count; block++) {
const int begin = block * block_size;
const int count = std::min(block_size, int(values.size()) - begin);
std::vector<Mint> transformed(transform_size);
std::copy_n(values.begin() + begin, count, transformed.begin());
ntt(transformed, false);
blocks.emplace_back(std::move(transformed));
}
return blocks;
};
std::vector<std::vector<Mint>> transformed_a = transform_blocks(a, a_blocks);
std::vector<std::vector<Mint>> transformed_b = transform_blocks(b, b_blocks);
const int result_size = int(a.size() + b.size() - 1);
std::vector<Mint> result(result_size);
std::vector<Mint> transformed_result(transform_size);
for (int diagonal = 0; diagonal < a_blocks + b_blocks - 1; diagonal++) {
std::fill(transformed_result.begin(), transformed_result.end(), Mint(0));
const int first_a = std::max(0, diagonal - (b_blocks - 1));
const int last_a = std::min(a_blocks - 1, diagonal);
for (int a_block = first_a; a_block <= last_a; a_block++) {
const int b_block = diagonal - a_block;
for (int i = 0; i < transform_size; i++)
transformed_result[i] +=
transformed_a[a_block][i] * transformed_b[b_block][i];
}
ntt(transformed_result, true);
const int output_offset = diagonal * block_size;
const int output_count = std::min(transform_size, result_size - output_offset);
for (int i = 0; i < output_count; i++)
result[output_offset + i] += transformed_result[i];
}
return result;
}
#ifdef M1UNE_FPS_HAS_X86_SIMD
class AlignedUint32Buffer {
private:
uint32_t* data_;
public:
explicit AlignedUint32Buffer(std::size_t size)
: data_(static_cast<uint32_t*>(
::operator new[](sizeof(uint32_t) * size, std::align_val_t(32)))) {}
AlignedUint32Buffer(const AlignedUint32Buffer&) = delete;
AlignedUint32Buffer& operator=(const AlignedUint32Buffer&) = delete;
AlignedUint32Buffer(AlignedUint32Buffer&& other) noexcept : data_(other.data_) {
other.data_ = nullptr;
}
AlignedUint32Buffer& operator=(AlignedUint32Buffer&& other) noexcept {
if (this == &other) return *this;
::operator delete[](data_, std::align_val_t(32));
data_ = other.data_;
other.data_ = nullptr;
return *this;
}
~AlignedUint32Buffer() {
::operator delete[](data_, std::align_val_t(32));
}
uint32_t* data() {
return data_;
}
const uint32_t* data() const {
return data_;
}
};
template <class Mint>
__attribute__((target("avx2,bmi"), hot))
std::vector<Mint> convolution_998244353_blocked_simd(const std::vector<Mint>& a,
const std::vector<Mint>& b,
int transform_size) {
assert(Mint::mod() == 998244353);
assert(transform_size >= 64 && (transform_size & (transform_size - 1)) == 0);
assert((Mint::mod() - 1) % uint32_t(transform_size) == 0);
const int block_size = transform_size / 2;
const int a_blocks = int((a.size() + block_size - 1) / block_size);
const int b_blocks = int((b.size() + block_size - 1) / block_size);
static constexpr fast998_v2::FNTT32_info transform(998244353);
const std::size_t vector_size = std::size_t(transform_size) / 8;
auto transform_blocks = [&](const std::vector<Mint>& values, int block_count) {
std::vector<AlignedUint32Buffer> blocks;
blocks.reserve(block_count);
for (int block = 0; block < block_count; block++) {
const int begin = block * block_size;
const int count = std::min(block_size, int(values.size()) - begin);
AlignedUint32Buffer transformed(transform_size);
if constexpr (std::is_same_v<Mint, math::ModInt<998244353>>) {
static_assert(sizeof(Mint) == sizeof(uint32_t) &&
std::is_trivially_copyable_v<Mint>);
std::memcpy(transformed.data(), values.data() + begin,
sizeof(uint32_t) * count);
} else {
for (int i = 0; i < count; i++)
transformed.data()[i] = values[begin + i].val();
}
std::memset(transformed.data() + count, 0,
sizeof(uint32_t) * (transform_size - count));
fast998_v2::vector_dif(reinterpret_cast<__m256i*>(transformed.data()),
vector_size, &transform);
blocks.emplace_back(std::move(transformed));
}
return blocks;
};
std::vector<AlignedUint32Buffer> transformed_a = transform_blocks(a, a_blocks);
std::vector<AlignedUint32Buffer> transformed_b = transform_blocks(b, b_blocks);
const int result_size = int(a.size() + b.size() - 1);
std::vector<Mint> result(result_size);
AlignedUint32Buffer transformed_result(transform_size);
for (int diagonal = 0; diagonal < a_blocks + b_blocks - 1; diagonal++) {
std::memset(transformed_result.data(), 0, sizeof(uint32_t) * transform_size);
const int first_a = std::max(0, diagonal - (b_blocks - 1));
const int last_a = std::min(a_blocks - 1, diagonal);
for (int a_block = first_a; a_block <= last_a; a_block++) {
const int b_block = diagonal - a_block;
fast998_v2::vector_convolution_accumulate(
reinterpret_cast<__m256i*>(transformed_result.data()),
reinterpret_cast<const __m256i*>(transformed_a[a_block].data()),
reinterpret_cast<const __m256i*>(transformed_b[b_block].data()),
vector_size, &transform);
}
fast998_v2::vector_dit<true>(
reinterpret_cast<__m256i*>(transformed_result.data()), vector_size,
&transform);
const int output_offset = diagonal * block_size;
const int output_count = std::min(transform_size, result_size - output_offset);
for (int i = 0; i < output_count; i++) {
uint32_t value = result[output_offset + i].val() + transformed_result.data()[i];
if (value >= Mint::mod()) value -= Mint::mod();
result[output_offset + i] = Mint::raw(value);
}
}
return result;
}
#endif
template <class Mint>
std::vector<Mint> convolution_998244353_blocked(const std::vector<Mint>& a,
const std::vector<Mint>& b,
int transform_size = 1 << 23) {
#ifdef M1UNE_FPS_HAS_X86_SIMD
if (transform_size >= 64 && __builtin_cpu_supports("avx2"))
return convolution_998244353_blocked_simd(a, b, transform_size);
#endif
return convolution_998244353_blocked_scalar(a, b, transform_size);
}
} // namespace internal
template <class Mint>
std::vector<Mint> convolution(const std::vector<Mint>& a, const std::vector<Mint>& b) {
if (a.empty() || b.empty()) return {};
if (std::min(a.size(), b.size()) <= 32) return convolution_naive(a, b);
const int result_size = int(a.size() + b.size() - 1);
int n = 1;
while (n < result_size) n <<= 1;
if constexpr (internal::has_static_modulus<Mint>::value) {
if constexpr (Mint::mod() == 998244353) {
if (n > (1 << 23))
return internal::convolution_998244353_blocked(a, b);
}
if ((Mint::mod() - 1) % uint32_t(n) == 0) return convolution_ntt(a, b);
}
using Mint1 = math::ModInt<167772161>;
using Mint2 = math::ModInt<469762049>;
using Mint3 = math::ModInt<754974721>;
assert(n <= (1 << 24));
[[maybe_unused]] const unsigned __int128 coefficient_bound =
static_cast<unsigned __int128>(std::min(a.size(), b.size())) * (Mint::mod() - 1) *
(Mint::mod() - 1);
[[maybe_unused]] const unsigned __int128 crt_modulus =
static_cast<unsigned __int128>(Mint1::mod()) * Mint2::mod() * Mint3::mod();
assert(coefficient_bound < crt_modulus);
auto converted_convolution = [&]<class OtherMint>() {
std::vector<OtherMint> converted_a(a.size());
std::vector<OtherMint> converted_b(b.size());
for (int i = 0; i < int(a.size()); i++) converted_a[i] = OtherMint(a[i].val());
for (int i = 0; i < int(b.size()); i++) converted_b[i] = OtherMint(b[i].val());
return convolution_ntt(converted_a, converted_b);
};
std::vector<Mint1> c1 = converted_convolution.template operator()<Mint1>();
std::vector<Mint2> c2 = converted_convolution.template operator()<Mint2>();
std::vector<Mint3> c3 = converted_convolution.template operator()<Mint3>();
static const uint64_t inverse_mod1_mod2 = Mint2(Mint1::mod()).inv().val();
static const uint64_t mod1_mod3 = Mint1::mod() % Mint3::mod();
static const uint64_t mod1_mod2_mod3 =
mod1_mod3 * (Mint2::mod() % Mint3::mod()) % Mint3::mod();
static const uint64_t inverse_mod1_mod2_mod3 = Mint3(uint32_t(mod1_mod2_mod3)).inv().val();
const uint64_t target_mod = Mint::mod();
const uint64_t mod1_target = Mint1::mod() % target_mod;
const uint64_t mod1_mod2_target = mod1_target * (Mint2::mod() % target_mod) % target_mod;
std::vector<Mint> result(result_size);
for (int i = 0; i < result_size; i++) {
const uint64_t r1 = c1[i].val();
const uint64_t r2 = c2[i].val();
const uint64_t r3 = c3[i].val();
const uint64_t first =
(r2 + Mint2::mod() - r1 % Mint2::mod()) % Mint2::mod() * inverse_mod1_mod2 %
Mint2::mod();
const uint64_t combined_mod3 =
(r1 % Mint3::mod() + mod1_mod3 * (first % Mint3::mod())) % Mint3::mod();
const uint64_t second =
(r3 + Mint3::mod() - combined_mod3) % Mint3::mod() * inverse_mod1_mod2_mod3 %
Mint3::mod();
uint64_t value = r1 % target_mod;
value = (value + mod1_target * (first % target_mod)) % target_mod;
value = (value + mod1_mod2_target * (second % target_mod)) % target_mod;
result[i] = Mint::raw(uint32_t(value));
}
return result;
}
} // namespace fps
} // namespace m1une
#ifdef M1UNE_FPS_HAS_X86_SIMD
#undef M1UNE_FPS_HAS_X86_SIMD
#endif
#line 13 "math/fps/formal_power_series.hpp"
namespace m1une {
namespace fps {
template <class Mint>
struct FormalPowerSeries : std::vector<Mint> {
using std::vector<Mint>::vector;
using Fps = FormalPowerSeries;
FormalPowerSeries() = default;
FormalPowerSeries(const std::vector<Mint>& values) : std::vector<Mint>(values) {}
FormalPowerSeries(std::vector<Mint>&& values) : std::vector<Mint>(std::move(values)) {}
Fps& shrink() {
while (!this->empty() && this->back() == Mint(0)) this->pop_back();
return *this;
}
Fps pre(int degree) const {
assert(degree >= 0);
Fps result(this->begin(), this->begin() + std::min<int>(degree, this->size()));
result.resize(degree);
return result;
}
Fps reversed(int size = -1) const {
Fps result = *this;
if (size >= 0) result.resize(size);
std::reverse(result.begin(), result.end());
return result;
}
Fps& operator+=(const Fps& rhs) {
if (this->size() < rhs.size()) this->resize(rhs.size());
for (int i = 0; i < int(rhs.size()); i++) (*this)[i] += rhs[i];
return *this;
}
Fps& operator-=(const Fps& rhs) {
if (this->size() < rhs.size()) this->resize(rhs.size());
for (int i = 0; i < int(rhs.size()); i++) (*this)[i] -= rhs[i];
return *this;
}
Fps& operator*=(const Fps& rhs) {
std::vector<Mint> lhs(this->begin(), this->end());
*this = convolution(lhs, rhs);
return *this;
}
Fps& operator*=(Mint rhs) {
for (Mint& value : *this) value *= rhs;
return *this;
}
Fps& operator/=(Mint rhs) {
return *this *= rhs.inv();
}
Fps& operator<<=(int shift) {
assert(shift >= 0);
this->insert(this->begin(), shift, Mint(0));
return *this;
}
Fps& operator>>=(int shift) {
assert(shift >= 0);
if (shift >= int(this->size())) {
this->clear();
} else {
this->erase(this->begin(), this->begin() + shift);
}
return *this;
}
Fps operator+() const {
return *this;
}
Fps operator-() const {
Fps result = *this;
for (Mint& value : result) value = Mint(0) - value;
return result;
}
friend Fps operator+(Fps lhs, const Fps& rhs) {
return lhs += rhs;
}
friend Fps operator-(Fps lhs, const Fps& rhs) {
return lhs -= rhs;
}
friend Fps operator*(Fps lhs, const Fps& rhs) {
return lhs *= rhs;
}
friend Fps operator*(Fps lhs, Mint rhs) {
return lhs *= rhs;
}
friend Fps operator*(Mint lhs, Fps rhs) {
return rhs *= lhs;
}
friend Fps operator/(Fps lhs, Mint rhs) {
return lhs /= rhs;
}
friend Fps operator<<(Fps lhs, int shift) {
return lhs <<= shift;
}
friend Fps operator>>(Fps lhs, int shift) {
return lhs >>= shift;
}
Fps derivative() const {
if (this->empty()) return {};
Fps result(this->size() - 1);
for (int i = 1; i < int(this->size()); i++) result[i - 1] = (*this)[i] * Mint(i);
return result;
}
Fps integral() const {
Fps result(this->size() + 1);
if (this->empty()) return result;
assert(this->size() < Mint::mod());
std::vector<Mint> inverse(this->size() + 1);
inverse[1] = 1;
for (int i = 2; i <= int(this->size()); i++) {
inverse[i] = Mint(0) - Mint(Mint::mod() / uint32_t(i)) * inverse[Mint::mod() % uint32_t(i)];
}
for (int i = 0; i < int(this->size()); i++) result[i + 1] = (*this)[i] * inverse[i + 1];
return result;
}
Mint evaluate(Mint x) const {
Mint result = 0;
for (auto it = this->rbegin(); it != this->rend(); ++it) result = result * x + *it;
return result;
}
Fps inv(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return {};
assert(!this->empty() && (*this)[0] != Mint(0));
Fps result(1, (*this)[0].inv());
for (int size = 1; size < degree; size <<= 1) {
const int next_size = std::min(size << 1, degree);
const int transform_size = size << 1;
if (size >= 32 && (Mint::mod() - 1) % uint32_t(transform_size) == 0) {
// Newton's g <- g(2-fg), restricted to the newly determined
// half. Keeping g in the frequency domain avoids two general
// convolutions and their 2x larger padding.
std::vector<Mint> transformed_f(transform_size);
std::copy_n(this->begin(), std::min<int>(this->size(), next_size),
transformed_f.begin());
std::vector<Mint> transformed_g(transform_size);
std::copy(result.begin(), result.end(), transformed_g.begin());
internal::ntt(transformed_f, false);
internal::ntt(transformed_g, false);
std::vector<Mint> error(transform_size);
for (int i = 0; i < transform_size; i++)
error[i] = transformed_f[i] * transformed_g[i];
internal::ntt(error, true);
std::fill(error.begin(), error.begin() + size, Mint(0));
internal::ntt(error, false);
for (int i = 0; i < transform_size; i++) error[i] *= transformed_g[i];
internal::ntt(error, true);
result.resize(next_size);
for (int i = size; i < next_size; i++) result[i] = Mint(0) - error[i];
continue;
}
Fps product = this->pre(next_size) * result;
product.resize(next_size);
for (Mint& value : product) value = Mint(0) - value;
product[0] += Mint(2);
result = (result * product).pre(next_size);
}
return result.pre(degree);
}
Fps log(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return {};
assert(!this->empty() && (*this)[0] == Mint(1));
return (derivative() * inv(degree)).pre(degree - 1).integral();
}
Fps exp(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return {};
assert(this->empty() || (*this)[0] == Mint(0));
Fps result(1, Mint(1));
for (int size = 1; size < degree; size <<= 1) {
const int next_size = std::min(size << 1, degree);
Fps correction = this->pre(next_size) - result.log(next_size);
correction[0] += Mint(1);
result = (result * correction).pre(next_size);
}
return result.pre(degree);
}
Fps pow(long long exponent, int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(exponent >= 0 && degree >= 0);
if (degree == 0) return {};
if (exponent == 0) {
Fps result(degree);
result[0] = 1;
return result;
}
int first = 0;
while (first < int(this->size()) && (*this)[first] == Mint(0)) first++;
if (first == int(this->size()) || first > (degree - 1) / exponent) return Fps(degree);
const int shift = int(first * exponent);
const Mint leading = (*this)[first];
Fps normalized = (*this >> first) / leading;
Fps result = (normalized.log(degree - shift) * Mint(exponent)).exp(degree - shift);
result *= leading.pow(exponent);
result <<= shift;
result.resize(degree);
return result;
}
std::optional<Fps> sqrt(int degree = -1) const {
if (degree < 0) degree = int(this->size());
assert(degree >= 0);
if (degree == 0) return Fps();
int first = 0;
while (first < int(this->size()) && (*this)[first] == Mint(0)) first++;
if (first == int(this->size())) return Fps(degree);
if (first >= degree) return Fps(degree);
if (first & 1) return std::nullopt;
const int shift = first / 2;
auto leading_root = m1une::math::modular_square_root((*this)[first]);
if (!leading_root.has_value()) return std::nullopt;
const int result_degree = degree - shift;
Fps normalized = (*this >> first) / (*this)[first];
Fps result = (normalized.log(result_degree) / Mint(2)).exp(result_degree);
result *= *leading_root;
result <<= shift;
result.resize(degree);
return result;
}
std::pair<Fps, Fps> divmod(const Fps& divisor) const {
Fps dividend = *this;
Fps normalized_divisor = divisor;
dividend.shrink();
normalized_divisor.shrink();
assert(!normalized_divisor.empty());
if (dividend.size() < normalized_divisor.size()) return std::make_pair(Fps(), dividend);
const int quotient_size = int(dividend.size() - normalized_divisor.size() + 1);
Fps quotient =
(dividend.reversed().pre(quotient_size) * normalized_divisor.reversed().inv(quotient_size))
.pre(quotient_size)
.reversed();
quotient.shrink();
Fps remainder = dividend - normalized_divisor * quotient;
remainder.resize(normalized_divisor.size() - 1);
remainder.shrink();
return std::make_pair(std::move(quotient), std::move(remainder));
}
Fps& operator/=(const Fps& rhs) {
*this = divmod(rhs).first;
return *this;
}
Fps& operator%=(const Fps& rhs) {
*this = divmod(rhs).second;
return *this;
}
friend Fps operator/(Fps lhs, const Fps& rhs) {
return lhs /= rhs;
}
friend Fps operator%(Fps lhs, const Fps& rhs) {
return lhs %= rhs;
}
Fps taylor_shift(Mint shift) const {
const int n = int(this->size());
if (n == 0) return {};
assert(uint32_t(n) < Mint::mod());
std::vector<Mint> factorial(n, Mint(1));
std::vector<Mint> inverse_factorial(n, Mint(1));
for (int i = 1; i < n; i++) factorial[i] = factorial[i - 1] * Mint(i);
inverse_factorial[n - 1] = factorial[n - 1].inv();
for (int i = n - 1; i > 0; i--) inverse_factorial[i - 1] = inverse_factorial[i] * Mint(i);
Fps left(n);
Fps right(n);
Mint power = 1;
for (int i = 0; i < n; i++) {
left[n - 1 - i] = (*this)[i] * factorial[i];
right[i] = power * inverse_factorial[i];
power *= shift;
}
Fps product = left * right;
Fps result(n);
for (int i = 0; i < n; i++) result[i] = product[n - 1 - i] * inverse_factorial[i];
return result;
}
};
} // namespace fps
} // namespace m1une
#line 1 "math/combinatorics.hpp"
#line 7 "math/combinatorics.hpp"
namespace m1une {
namespace math {
template <class Mint>
struct Combinatorics {
private:
std::vector<Mint> _factorial;
std::vector<Mint> _inverse_factorial;
public:
explicit Combinatorics(int maximum = 0) : _factorial(1, Mint(1)), _inverse_factorial(1, Mint(1)) {
ensure(maximum);
}
int maximum() const {
return int(_factorial.size()) - 1;
}
void ensure(int maximum) {
assert(maximum >= 0);
assert(static_cast<uint64_t>(maximum) < Mint::mod());
if (maximum <= this->maximum()) return;
const int old_maximum = this->maximum();
_factorial.resize(maximum + 1);
_inverse_factorial.resize(maximum + 1);
for (int i = old_maximum + 1; i <= maximum; i++) {
_factorial[i] = _factorial[i - 1] * Mint(i);
}
_inverse_factorial[maximum] = _factorial[maximum].inv();
for (int i = maximum; i > old_maximum; i--) {
_inverse_factorial[i - 1] = _inverse_factorial[i] * Mint(i);
}
}
Mint factorial(int n) const {
assert(0 <= n && n <= maximum());
return _factorial[n];
}
Mint inverse_factorial(int n) const {
assert(0 <= n && n <= maximum());
return _inverse_factorial[n];
}
Mint inverse(int n) const {
assert(1 <= n && n <= maximum());
return _factorial[n - 1] * _inverse_factorial[n];
}
Mint binom(int n, int k) const {
if (k < 0 || k > n) return Mint(0);
assert(n <= maximum());
return _factorial[n] * _inverse_factorial[k] * _inverse_factorial[n - k];
}
Mint perm(int n, int k) const {
if (k < 0 || k > n) return Mint(0);
assert(n <= maximum());
return _factorial[n] * _inverse_factorial[n - k];
}
Mint multiset(int types, int count) const {
if (types < 0 || count < 0) return Mint(0);
if (types == 0) return Mint(count == 0);
const long long total = static_cast<long long>(types) + count - 1;
assert(total <= maximum());
return binom(static_cast<int>(total), count);
}
Mint catalan(int n) const {
assert(n >= 0);
const long long doubled = 2LL * n;
assert(doubled <= maximum());
return binom(int(doubled), n) - binom(int(doubled), n + 1);
}
};
} // namespace math
} // namespace m1une
#line 10 "math/bernoulli.hpp"
namespace m1une {
namespace math {
namespace bernoulli_detail {
template <class Mint>
std::vector<Mint> numbers(
int maximum,
const Combinatorics<Mint>& combinations
) {
using Fps = fps::FormalPowerSeries<Mint>;
Fps denominator(maximum + 1);
for (int index = 0; index <= maximum; ++index) {
denominator[index] = combinations.inverse_factorial(index + 1);
}
Fps generating_function = denominator.inv(maximum + 1);
std::vector<Mint> result(maximum + 1);
for (int index = 0; index <= maximum; ++index) {
result[index] =
generating_function[index] * combinations.factorial(index);
}
return result;
}
template <class Mint>
Mint evaluate_polynomial(const std::vector<Mint>& coefficients, Mint x) {
Mint result = 0;
for (int index = int(coefficients.size()) - 1; index >= 0; --index) {
result = result * x + coefficients[index];
}
return result;
}
} // namespace bernoulli_detail
// Uses x / (exp(x) - 1), so B_1 = -1/2.
template <class Mint>
std::vector<Mint> bernoulli_numbers(int maximum) {
assert(maximum >= 0);
assert(static_cast<uint64_t>(maximum) + 1 < Mint::mod());
Combinatorics<Mint> combinations(maximum + 1);
return bernoulli_detail::numbers(maximum, combinations);
}
template <class Mint>
class Bernoulli {
public:
explicit Bernoulli(int maximum)
: combinations_(checked_maximum(maximum) + 1),
numbers_(bernoulli_detail::numbers(maximum, combinations_)) {}
int maximum() const {
return int(numbers_.size()) - 1;
}
const std::vector<Mint>& numbers() const {
return numbers_;
}
Mint number(int degree) const {
assert(0 <= degree && degree <= maximum());
return numbers_[degree];
}
// Coefficients of B_degree(x), in increasing order of powers of x.
std::vector<Mint> polynomial_coefficients(int degree) const {
assert(0 <= degree && degree <= maximum());
std::vector<Mint> result(degree + 1);
for (int power = 0; power <= degree; ++power) {
result[power] =
combinations_.binom(degree, power) *
numbers_[degree - power];
}
return result;
}
Mint polynomial(int degree, Mint x) const {
assert(0 <= degree && degree <= maximum());
std::vector<Mint> powers(degree + 1, Mint(1));
for (int power = 0; power < degree; ++power) {
powers[power + 1] = powers[power] * x;
}
Mint result = 0;
for (int index = 0; index <= degree; ++index) {
result += combinations_.binom(degree, index) * numbers_[index] *
powers[degree - index];
}
return result;
}
// Returns sum_{i=0}^{n-1} i^degree, evaluated as a polynomial in n.
Mint power_sum(Mint n, int degree) const {
assert(0 <= degree && degree <= maximum());
std::vector<Mint> powers(degree + 2, Mint(1));
for (int power = 0; power <= degree; ++power) {
powers[power + 1] = powers[power] * n;
}
Mint result = 0;
for (int index = 0; index <= degree; ++index) {
result += combinations_.binom(degree + 1, index) *
numbers_[index] * powers[degree + 1 - index];
}
return result * combinations_.inverse(degree + 1);
}
// Returns sum_{i=left}^{right-1} i^degree.
Mint power_sum(Mint left, Mint right, int degree) const {
return power_sum(right, degree) - power_sum(left, degree);
}
// Coefficients of sum_{i=0}^{n-1} i^degree as a polynomial in n.
std::vector<Mint> power_sum_polynomial(int degree) const {
assert(0 <= degree && degree <= maximum());
std::vector<Mint> result(degree + 2);
Mint inverse = combinations_.inverse(degree + 1);
for (int index = 0; index <= degree; ++index) {
result[degree + 1 - index] +=
combinations_.binom(degree + 1, index) * numbers_[index] *
inverse;
}
return result;
}
// If P is given by coefficients, returns coefficients of the unique Q
// with Q(0) = 0 and Q(n) = sum_{i=0}^{n-1} P(i).
std::vector<Mint> polynomial_prefix_sum(
const std::vector<Mint>& coefficients
) const {
if (coefficients.empty()) return std::vector<Mint>{Mint(0)};
int degree = int(coefficients.size()) - 1;
assert(degree <= maximum());
std::vector<Mint> result(degree + 2);
for (int source_degree = 0;
source_degree <= degree;
++source_degree) {
Mint inverse = combinations_.inverse(source_degree + 1);
for (int index = 0; index <= source_degree; ++index) {
result[source_degree + 1 - index] +=
coefficients[source_degree] *
combinations_.binom(source_degree + 1, index) *
numbers_[index] * inverse;
}
}
return result;
}
// Returns sum_{i=left}^{right-1} P(i).
Mint polynomial_sum(
const std::vector<Mint>& coefficients,
Mint left,
Mint right
) const {
std::vector<Mint> prefix = polynomial_prefix_sum(coefficients);
return bernoulli_detail::evaluate_polynomial(prefix, right) -
bernoulli_detail::evaluate_polynomial(prefix, left);
}
// Returns sum_{i=0}^{count-1} (start + step*i)^degree.
Mint arithmetic_progression_power_sum(
Mint start,
Mint step,
Mint count,
int degree
) const {
assert(0 <= degree && degree <= maximum());
std::vector<Mint> start_powers(degree + 1, Mint(1));
std::vector<Mint> step_powers(degree + 1, Mint(1));
for (int power = 0; power < degree; ++power) {
start_powers[power + 1] = start_powers[power] * start;
step_powers[power + 1] = step_powers[power] * step;
}
Mint result = 0;
for (int power = 0; power <= degree; ++power) {
result += combinations_.binom(degree, power) *
start_powers[degree - power] * step_powers[power] *
power_sum(count, power);
}
return result;
}
private:
static int checked_maximum(int maximum) {
assert(maximum >= 0);
assert(static_cast<uint64_t>(maximum) + 1 < Mint::mod());
return maximum;
}
Combinatorics<Mint> combinations_;
std::vector<Mint> numbers_;
};
} // namespace math
} // namespace m1une
#line 1 "math/binomial_coefficient_mod.hpp"
#line 5 "math/binomial_coefficient_mod.hpp"
#include <cstddef>
#line 8 "math/binomial_coefficient_mod.hpp"
#line 1 "math/number_theory.hpp"
#line 7 "math/number_theory.hpp"
#include <tuple>
#line 10 "math/number_theory.hpp"
namespace m1une {
namespace math {
namespace internal {
inline long long safe_mod(long long x, long long mod) {
x %= mod;
if (x < 0) x += mod;
return x;
}
inline unsigned __int128 floor_sum_unsigned(unsigned long long n, unsigned long long mod, unsigned long long a,
unsigned long long b) {
unsigned __int128 answer = 0;
while (true) {
if (a >= mod) {
answer += static_cast<unsigned __int128>(n) * (n - 1) / 2 * (a / mod);
a %= mod;
}
if (b >= mod) {
answer += static_cast<unsigned __int128>(n) * (b / mod);
b %= mod;
}
const unsigned __int128 y_max = static_cast<unsigned __int128>(a) * n + b;
if (y_max < mod) break;
n = static_cast<unsigned long long>(y_max / mod);
b = static_cast<unsigned long long>(y_max % mod);
unsigned long long tmp = mod;
mod = a;
a = tmp;
}
return answer;
}
} // namespace internal
// Returns (g, x, y), where g = gcd(a, b) is nonnegative and
// a * x + b * y = g. Returns (0, 0, 0) when a = b = 0.
inline std::tuple<long long, long long, long long> extended_gcd(long long a,
long long b) {
using i128 = __int128;
if (a == 0 && b == 0) return {0, 0, 0};
i128 old_remainder = a;
i128 remainder = b;
if (old_remainder < 0) old_remainder = -old_remainder;
if (remainder < 0) remainder = -remainder;
i128 old_x = 1;
i128 x = 0;
i128 old_y = 0;
i128 y = 1;
while (remainder != 0) {
i128 quotient = old_remainder / remainder;
i128 next = old_remainder - quotient * remainder;
old_remainder = remainder;
remainder = next;
next = old_x - quotient * x;
old_x = x;
x = next;
next = old_y - quotient * y;
old_y = y;
y = next;
}
if (a < 0) old_x = -old_x;
if (b < 0) old_y = -old_y;
#ifndef NDEBUG
const i128 minimum = std::numeric_limits<long long>::min();
const i128 maximum = std::numeric_limits<long long>::max();
assert(old_remainder <= maximum);
assert(minimum <= old_x && old_x <= maximum);
assert(minimum <= old_y && old_y <= maximum);
#endif
return {static_cast<long long>(old_remainder), static_cast<long long>(old_x),
static_cast<long long>(old_y)};
}
inline long long pow_mod(long long x, unsigned long long exponent, long long mod) {
assert(mod >= 1);
if (mod == 1) return 0;
unsigned long long base = static_cast<unsigned long long>(internal::safe_mod(x, mod));
unsigned long long result = 1;
const unsigned long long unsigned_mod = static_cast<unsigned long long>(mod);
while (exponent > 0) {
if (exponent & 1) {
result = static_cast<unsigned long long>(static_cast<unsigned __int128>(result) * base % unsigned_mod);
}
base = static_cast<unsigned long long>(static_cast<unsigned __int128>(base) * base % unsigned_mod);
exponent >>= 1;
}
return static_cast<long long>(result);
}
// Returns gcd(a, mod) and x such that a * x is congruent to gcd(a, mod)
// modulo mod. The returned x is in [0, mod / gcd(a, mod)).
inline std::pair<long long, long long> inv_gcd(long long a, long long mod) {
assert(mod >= 1);
a = internal::safe_mod(a, mod);
if (a == 0) return {mod, 0};
long long s = mod;
long long t = a;
long long m0 = 0;
long long m1 = 1;
while (t > 0) {
const long long quotient = s / t;
s -= t * quotient;
m0 -= m1 * quotient;
long long tmp = s;
s = t;
t = tmp;
tmp = m0;
m0 = m1;
m1 = tmp;
}
if (m0 < 0) m0 += mod / s;
return {s, m0};
}
inline long long inv_mod(long long x, long long mod) {
const auto result = inv_gcd(x, mod);
assert(result.first == 1);
return result.second;
}
// Returns the smallest nonnegative solution and the least common multiple of
// the moduli. Returns {0, 0} when the system is inconsistent.
inline std::pair<long long, long long> crt(const std::vector<long long>& remainders,
const std::vector<long long>& moduli) {
assert(remainders.size() == moduli.size());
long long r0 = 0;
long long m0 = 1;
for (int i = 0; i < int(remainders.size()); i++) {
assert(moduli[i] >= 1);
long long r1 = internal::safe_mod(remainders[i], moduli[i]);
long long m1 = moduli[i];
if (m0 < m1) {
long long tmp = r0;
r0 = r1;
r1 = tmp;
tmp = m0;
m0 = m1;
m1 = tmp;
}
if (m0 % m1 == 0) {
if (r0 % m1 != r1) return {0, 0};
continue;
}
const auto inverse = inv_gcd(m0, m1);
const long long gcd = inverse.first;
const long long reduced_modulus = m1 / gcd;
const __int128 difference = static_cast<__int128>(r1) - r0;
if (difference % gcd != 0) return {0, 0};
__int128 multiplier = difference / gcd % reduced_modulus;
multiplier = multiplier * inverse.second % reduced_modulus;
if (multiplier < 0) multiplier += reduced_modulus;
const __int128 new_modulus = static_cast<__int128>(m0) * reduced_modulus;
assert(new_modulus <= std::numeric_limits<long long>::max());
__int128 new_remainder = static_cast<__int128>(r0) + multiplier * m0;
new_remainder %= new_modulus;
if (new_remainder < 0) new_remainder += new_modulus;
r0 = static_cast<long long>(new_remainder);
m0 = static_cast<long long>(new_modulus);
}
return {r0, m0};
}
// Returns sum_{i=0}^{n-1} floor((a * i + b) / mod).
inline long long floor_sum(long long n, long long mod, long long a, long long b) {
assert(n >= 0);
assert(mod >= 1);
const long long normalized_a = internal::safe_mod(a, mod);
const long long normalized_b = internal::safe_mod(b, mod);
__int128 answer = (static_cast<__int128>(a) - normalized_a) / mod * n * (n - 1) / 2;
answer += (static_cast<__int128>(b) - normalized_b) / mod * n;
answer += internal::floor_sum_unsigned(static_cast<unsigned long long>(n), static_cast<unsigned long long>(mod),
static_cast<unsigned long long>(normalized_a),
static_cast<unsigned long long>(normalized_b));
assert(answer >= std::numeric_limits<long long>::min());
assert(answer <= std::numeric_limits<long long>::max());
return static_cast<long long>(answer);
}
} // namespace math
} // namespace m1une
#line 10 "math/binomial_coefficient_mod.hpp"
namespace m1une {
namespace math {
// Binomial coefficients modulo a fixed, not necessarily prime, modulus.
class BinomialCoefficientMod {
private:
struct PrimePower {
uint32_t prime;
int exponent;
uint32_t modulus;
uint32_t crt_multiplier;
std::vector<uint32_t> unit_factorial_prefix;
uint32_t multiply(uint32_t lhs, uint32_t rhs) const {
return uint32_t(uint64_t(lhs) * rhs % modulus);
}
uint32_t power(uint32_t base, uint64_t exponent_) const {
uint32_t result = 1 % modulus;
while (exponent_ > 0) {
if (exponent_ & 1) result = multiply(result, base);
base = multiply(base, base);
exponent_ >>= 1;
}
return result;
}
uint64_t factorial_valuation(uint64_t n) const {
uint64_t result = 0;
while (n > 0) {
n /= prime;
result += n;
}
return result;
}
uint32_t unit_factorial(uint64_t n) const {
if (n == 0) return 1 % modulus;
const uint32_t block_product = unit_factorial_prefix.back();
uint32_t result = power(block_product, n / modulus);
result = multiply(result, unit_factorial_prefix[std::size_t(n % modulus)]);
return multiply(result, unit_factorial(n / prime));
}
uint32_t binom(uint64_t n, uint64_t k) const {
if (k > n) return 0;
const uint64_t valuation = factorial_valuation(n) - factorial_valuation(k) -
factorial_valuation(n - k);
if (valuation >= uint64_t(exponent)) return 0;
const uint32_t numerator = unit_factorial(n);
const uint32_t denominator =
multiply(unit_factorial(k), unit_factorial(n - k));
const uint32_t inverse_denominator =
uint32_t(inv_mod(denominator, modulus));
uint32_t result = multiply(numerator, inverse_denominator);
result = multiply(result, power(prime, valuation));
return result;
}
};
uint32_t _modulus;
std::vector<PrimePower> _prime_powers;
public:
explicit BinomialCoefficientMod(uint32_t modulus) : _modulus(modulus) {
assert(modulus >= 1);
uint32_t remaining = modulus;
for (uint32_t prime = 2; uint64_t(prime) * prime <= remaining; prime++) {
if (remaining % prime != 0) continue;
int exponent = 0;
uint32_t prime_power = 1;
do {
remaining /= prime;
prime_power *= prime;
exponent++;
} while (remaining % prime == 0);
_prime_powers.push_back(
PrimePower{prime, exponent, prime_power, 0, {}});
}
if (remaining > 1) {
_prime_powers.push_back(PrimePower{remaining, 1, remaining, 0, {}});
}
for (PrimePower& component : _prime_powers) {
component.unit_factorial_prefix.resize(std::size_t(component.modulus));
component.unit_factorial_prefix[0] = 1;
for (uint32_t value = 1; value < component.modulus; value++) {
component.unit_factorial_prefix[value] =
component.unit_factorial_prefix[value - 1];
if (value % component.prime != 0) {
component.unit_factorial_prefix[value] = component.multiply(
component.unit_factorial_prefix[value], value);
}
}
const uint32_t other = modulus / component.modulus;
const uint32_t inverse =
uint32_t(inv_mod(other, component.modulus));
component.crt_multiplier =
uint32_t(uint64_t(other) * inverse % modulus);
}
}
uint32_t modulus() const {
return _modulus;
}
uint32_t binom(uint64_t n, uint64_t k) const {
if (k > n || _modulus == 1) return 0;
uint64_t result = 0;
for (const PrimePower& component : _prime_powers) {
const uint32_t residue = component.binom(n, k);
result += uint64_t(residue) * component.crt_multiplier % _modulus;
result %= _modulus;
}
return uint32_t(result);
}
uint32_t operator()(uint64_t n, uint64_t k) const {
return binom(n, k);
}
};
using ArbitraryModBinomialCoefficient = BinomialCoefficientMod;
} // namespace math
} // namespace m1une
#line 1 "math/bitwise_convolution.hpp"
#line 8 "math/bitwise_convolution.hpp"
#line 1 "math/zeta_mobius_transform.hpp"
#line 7 "math/zeta_mobius_transform.hpp"
namespace m1une {
namespace math {
namespace zeta_mobius_transform_detail {
inline bool is_power_of_two(std::size_t size) noexcept {
return size != 0 && (size & (size - 1)) == 0;
}
inline std::vector<std::size_t> primes_up_to(std::size_t limit) {
std::vector<std::size_t> primes;
std::vector<bool> is_prime(limit + 1, true);
if (!is_prime.empty()) is_prime[0] = false;
if (limit >= 1) is_prime[1] = false;
for (std::size_t value = 2; value <= limit; ++value) {
if (!is_prime[value]) continue;
primes.emplace_back(value);
if (value > limit / value) continue;
for (
std::size_t multiple = value * value;
multiple <= limit;
multiple += value
) {
is_prime[multiple] = false;
}
}
return primes;
}
} // namespace zeta_mobius_transform_detail
template <typename T>
void subset_zeta_transform(std::vector<T>& values) {
assert(zeta_mobius_transform_detail::is_power_of_two(values.size()));
for (std::size_t bit = 1; bit < values.size(); bit <<= 1) {
for (
std::size_t block = 0;
block < values.size();
block += bit << 1
) {
for (std::size_t offset = 0; offset < bit; ++offset) {
values[block + bit + offset] += values[block + offset];
}
}
}
}
template <typename T>
void subset_mobius_transform(std::vector<T>& values) {
assert(zeta_mobius_transform_detail::is_power_of_two(values.size()));
for (std::size_t bit = 1; bit < values.size(); bit <<= 1) {
for (
std::size_t block = 0;
block < values.size();
block += bit << 1
) {
for (std::size_t offset = 0; offset < bit; ++offset) {
values[block + bit + offset] -= values[block + offset];
}
}
}
}
template <typename T>
void superset_zeta_transform(std::vector<T>& values) {
assert(zeta_mobius_transform_detail::is_power_of_two(values.size()));
for (std::size_t bit = 1; bit < values.size(); bit <<= 1) {
for (
std::size_t block = 0;
block < values.size();
block += bit << 1
) {
for (std::size_t offset = 0; offset < bit; ++offset) {
values[block + offset] += values[block + bit + offset];
}
}
}
}
template <typename T>
void superset_mobius_transform(std::vector<T>& values) {
assert(zeta_mobius_transform_detail::is_power_of_two(values.size()));
for (std::size_t bit = 1; bit < values.size(); bit <<= 1) {
for (
std::size_t block = 0;
block < values.size();
block += bit << 1
) {
for (std::size_t offset = 0; offset < bit; ++offset) {
values[block + offset] -= values[block + bit + offset];
}
}
}
}
template <typename T>
void divisor_zeta_transform(std::vector<T>& values) {
if (values.size() <= 2) return;
const std::size_t limit = values.size() - 1;
const std::vector<std::size_t> primes =
zeta_mobius_transform_detail::primes_up_to(limit);
for (std::size_t prime : primes) {
for (std::size_t value = 1; value <= limit / prime; ++value) {
values[value * prime] += values[value];
}
}
}
template <typename T>
void divisor_mobius_transform(std::vector<T>& values) {
if (values.size() <= 2) return;
const std::size_t limit = values.size() - 1;
const std::vector<std::size_t> primes =
zeta_mobius_transform_detail::primes_up_to(limit);
for (std::size_t prime : primes) {
for (
std::size_t value = limit / prime;
value >= 1;
--value
) {
values[value * prime] -= values[value];
}
}
}
template <typename T>
void multiple_zeta_transform(std::vector<T>& values) {
if (values.size() <= 2) return;
const std::size_t limit = values.size() - 1;
const std::vector<std::size_t> primes =
zeta_mobius_transform_detail::primes_up_to(limit);
for (std::size_t prime : primes) {
for (
std::size_t value = limit / prime;
value >= 1;
--value
) {
values[value] += values[value * prime];
}
}
}
template <typename T>
void multiple_mobius_transform(std::vector<T>& values) {
if (values.size() <= 2) return;
const std::size_t limit = values.size() - 1;
const std::vector<std::size_t> primes =
zeta_mobius_transform_detail::primes_up_to(limit);
for (std::size_t prime : primes) {
for (std::size_t value = 1; value <= limit / prime; ++value) {
values[value] -= values[value * prime];
}
}
}
} // namespace math
} // namespace m1une
#line 10 "math/bitwise_convolution.hpp"
namespace m1une {
namespace math {
namespace bitwise_convolution_detail {
inline std::size_t common_size(
std::size_t first_size,
std::size_t second_size
) {
std::size_t required = first_size > second_size
? first_size
: second_size;
std::size_t size = 1;
while (size < required) size <<= 1;
return size;
}
template <typename T>
std::vector<T> pointwise_product(
std::vector<T> first,
const std::vector<T>& second
) {
assert(first.size() == second.size());
for (std::size_t index = 0; index < first.size(); ++index) {
first[index] *= second[index];
}
return first;
}
} // namespace bitwise_convolution_detail
template <typename T>
void walsh_hadamard_transform(
std::vector<T>& values,
bool inverse = false
) {
assert(zeta_mobius_transform_detail::is_power_of_two(values.size()));
for (std::size_t length = 1; length < values.size(); length <<= 1) {
for (
std::size_t block = 0;
block < values.size();
block += length << 1
) {
for (std::size_t offset = 0; offset < length; ++offset) {
T first = values[block + offset];
T second = values[block + offset + length];
values[block + offset] = first + second;
values[block + offset + length] = first - second;
}
}
}
if (inverse) {
T size = T(static_cast<long long>(values.size()));
for (T& value : values) value /= size;
}
}
template <typename T>
std::vector<T> bitwise_or_convolution(
std::vector<T> first,
std::vector<T> second
) {
if (first.empty() || second.empty()) return {};
std::size_t size = bitwise_convolution_detail::common_size(
first.size(),
second.size()
);
first.resize(size);
second.resize(size);
subset_zeta_transform(first);
subset_zeta_transform(second);
first = bitwise_convolution_detail::pointwise_product(
std::move(first),
second
);
subset_mobius_transform(first);
return first;
}
template <typename T>
std::vector<T> bitwise_and_convolution(
std::vector<T> first,
std::vector<T> second
) {
if (first.empty() || second.empty()) return {};
std::size_t size = bitwise_convolution_detail::common_size(
first.size(),
second.size()
);
first.resize(size);
second.resize(size);
superset_zeta_transform(first);
superset_zeta_transform(second);
first = bitwise_convolution_detail::pointwise_product(
std::move(first),
second
);
superset_mobius_transform(first);
return first;
}
template <typename T>
std::vector<T> bitwise_xor_convolution(
std::vector<T> first,
std::vector<T> second
) {
if (first.empty() || second.empty()) return {};
std::size_t size = bitwise_convolution_detail::common_size(
first.size(),
second.size()
);
first.resize(size);
second.resize(size);
walsh_hadamard_transform(first);
walsh_hadamard_transform(second);
first = bitwise_convolution_detail::pointwise_product(
std::move(first),
second
);
walsh_hadamard_transform(first, true);
return first;
}
} // namespace math
} // namespace m1une
#line 1 "math/bit_ceil.hpp"
namespace m1une {
namespace math {
template <typename T>
constexpr T bit_ceil(T n) {
if (n <= 1) return 1;
T x = 1;
while (x < n) x <<= 1;
return x;
}
} // namespace math
} // namespace m1une
#line 1 "math/combinatorial_sequences.hpp"
#line 7 "math/combinatorial_sequences.hpp"
#line 1 "math/partition_function.hpp"
#line 5 "math/partition_function.hpp"
#line 8 "math/partition_function.hpp"
namespace m1une {
namespace math {
// Returns p(0), p(1), ..., p(maximum), where p(n) is the number of integer
// partitions of n.
template <class Mint>
std::vector<Mint> partition_function(int maximum) {
assert(maximum >= 0);
using Fps = fps::FormalPowerSeries<Mint>;
Fps denominator(maximum + 1);
denominator[0] = 1;
for (long long k = 1;; k++) {
long long first = k * (3 * k - 1) / 2;
long long second = k * (3 * k + 1) / 2;
if (first > maximum) break;
Mint sign = (k & 1) ? Mint(-1) : Mint(1);
denominator[int(first)] += sign;
if (second <= maximum) denominator[int(second)] += sign;
}
return denominator.inv(maximum + 1);
}
template <class Mint>
std::vector<Mint> partition_numbers(int maximum) {
return partition_function<Mint>(maximum);
}
} // namespace math
} // namespace m1une
#line 12 "math/combinatorial_sequences.hpp"
namespace m1une {
namespace math {
template <class Mint>
std::vector<Mint> catalan_numbers(int maximum) {
assert(maximum >= 0);
assert(static_cast<uint64_t>(maximum) + 1 < Mint::mod());
std::vector<Mint> inverse(maximum + 2);
inverse[1] = 1;
for (int i = 2; i <= maximum + 1; i++) {
inverse[i] = Mint(0) - Mint(Mint::mod() / uint32_t(i)) * inverse[Mint::mod() % uint32_t(i)];
}
std::vector<Mint> result(maximum + 1);
result[0] = 1;
for (int n = 0; n < maximum; n++) {
result[n + 1] = result[n] * Mint(2) * Mint(2LL * n + 1) * inverse[n + 2];
}
return result;
}
template <class Mint>
std::vector<Mint> bell_numbers(int maximum) {
assert(maximum >= 0);
assert(static_cast<uint64_t>(maximum) < Mint::mod());
using Fps = fps::FormalPowerSeries<Mint>;
Combinatorics<Mint> combinations(maximum);
Fps exponent(maximum + 1);
for (int i = 1; i <= maximum; i++) {
exponent[i] = combinations.inverse_factorial(i);
}
Fps generating_function = exponent.exp(maximum + 1);
std::vector<Mint> result(maximum + 1);
for (int i = 0; i <= maximum; i++) {
result[i] = generating_function[i] * combinations.factorial(i);
}
return result;
}
template <class Mint>
std::vector<Mint> stirling_numbers_second_kind(int n) {
assert(n >= 0);
assert(static_cast<uint64_t>(n) < Mint::mod());
Combinatorics<Mint> combinations(n);
std::vector<Mint> powers(n + 1);
std::vector<Mint> signs(n + 1);
for (int i = 0; i <= n; i++) {
powers[i] = Mint(i).pow(n) * combinations.inverse_factorial(i);
signs[i] = combinations.inverse_factorial(i);
if (i & 1) signs[i] = Mint(0) - signs[i];
}
std::vector<Mint> result = fps::convolution(powers, signs);
result.resize(n + 1);
return result;
}
template <class Mint>
std::vector<Mint> derangement_numbers(int maximum) {
assert(maximum >= 0);
std::vector<Mint> result(maximum + 1);
result[0] = 1;
if (maximum >= 1) result[1] = 0;
for (int n = 2; n <= maximum; n++) {
result[n] = Mint(n - 1) * (result[n - 1] + result[n - 2]);
}
return result;
}
} // namespace math
} // namespace m1une
#line 1 "math/cyclotomic_polynomial.hpp"
#line 9 "math/cyclotomic_polynomial.hpp"
#line 1 "math/prime_factorization.hpp"
#line 7 "math/prime_factorization.hpp"
#include <numeric>
#line 10 "math/prime_factorization.hpp"
namespace m1une {
namespace math {
namespace internal {
inline uint64_t multiply_mod(uint64_t a, uint64_t b, uint64_t mod) {
return static_cast<uint64_t>(static_cast<unsigned __int128>(a) * b % mod);
}
inline uint64_t power_mod(uint64_t base, uint64_t exponent, uint64_t mod) {
uint64_t result = 1;
while (exponent > 0) {
if (exponent & 1) result = multiply_mod(result, base, mod);
base = multiply_mod(base, base, mod);
exponent >>= 1;
}
return result;
}
inline uint64_t pollard_random() {
static uint64_t state = 0x123456789abcdef0ULL;
state += 0x9e3779b97f4a7c15ULL;
uint64_t value = state;
value = (value ^ (value >> 30)) * 0xbf58476d1ce4e5b9ULL;
value = (value ^ (value >> 27)) * 0x94d049bb133111ebULL;
return value ^ (value >> 31);
}
} // namespace internal
inline bool is_prime(uint64_t value) {
if (value < 2) return false;
for (uint64_t prime : {2ULL, 3ULL, 5ULL, 7ULL, 11ULL, 13ULL, 17ULL, 19ULL, 23ULL, 29ULL, 31ULL, 37ULL}) {
if (value % prime == 0) return value == prime;
}
uint64_t odd_part = value - 1;
int power_of_two = 0;
while ((odd_part & 1) == 0) {
odd_part >>= 1;
power_of_two++;
}
for (uint64_t base : {2ULL, 325ULL, 9375ULL, 28178ULL, 450775ULL, 9780504ULL, 1795265022ULL}) {
if (base % value == 0) continue;
uint64_t x = internal::power_mod(base % value, odd_part, value);
if (x == 1 || x == value - 1) continue;
bool composite = true;
for (int i = 1; i < power_of_two; i++) {
x = internal::multiply_mod(x, x, value);
if (x == value - 1) {
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
namespace internal {
inline uint64_t pollard_rho(uint64_t value) {
for (uint64_t prime : {2ULL, 3ULL, 5ULL, 7ULL, 11ULL, 13ULL, 17ULL, 19ULL, 23ULL, 29ULL, 31ULL, 37ULL}) {
if (value % prime == 0) return prime;
}
while (true) {
const uint64_t constant = pollard_random() % (value - 1) + 1;
uint64_t y = pollard_random() % (value - 1) + 1;
uint64_t x = 0;
uint64_t saved_y = 0;
uint64_t gcd = 1;
uint64_t segment_length = 1;
auto advance = [&](uint64_t current) {
return static_cast<uint64_t>(
(static_cast<unsigned __int128>(multiply_mod(current, current, value)) + constant) % value);
};
while (gcd == 1) {
x = y;
for (uint64_t i = 0; i < segment_length; i++) y = advance(y);
for (uint64_t offset = 0; offset < segment_length && gcd == 1; offset += 128) {
saved_y = y;
uint64_t product = 1;
const uint64_t block = std::min<uint64_t>(128, segment_length - offset);
for (uint64_t i = 0; i < block; i++) {
y = advance(y);
const uint64_t difference = x > y ? x - y : y - x;
product = multiply_mod(product, difference, value);
}
gcd = std::gcd(product, value);
}
segment_length <<= 1;
}
if (gcd == value) {
do {
saved_y = advance(saved_y);
const uint64_t difference = x > saved_y ? x - saved_y : saved_y - x;
gcd = std::gcd(difference, value);
} while (gcd == 1);
}
if (gcd != value) return gcd;
}
}
inline void factor_recursively(uint64_t value, std::vector<uint64_t>& factors) {
if (value == 1) return;
if (is_prime(value)) {
factors.push_back(value);
return;
}
const uint64_t divisor = pollard_rho(value);
factor_recursively(divisor, factors);
factor_recursively(value / divisor, factors);
}
} // namespace internal
inline std::vector<uint64_t> prime_factors(uint64_t value) {
assert(value >= 1);
std::vector<uint64_t> result;
internal::factor_recursively(value, result);
std::sort(result.begin(), result.end());
return result;
}
inline std::vector<std::pair<uint64_t, int>> prime_factorize(uint64_t value) {
std::vector<uint64_t> factors = prime_factors(value);
std::vector<std::pair<uint64_t, int>> result;
for (uint64_t prime : factors) {
if (result.empty() || result.back().first != prime) {
result.emplace_back(prime, 1);
} else {
result.back().second++;
}
}
return result;
}
inline std::vector<uint64_t> divisors(uint64_t value) {
std::vector<uint64_t> result = {1};
for (const auto& factor : prime_factorize(value)) {
const int current_size = int(result.size());
uint64_t power = 1;
for (int exponent = 1; exponent <= factor.second; exponent++) {
power *= factor.first;
for (int i = 0; i < current_size; i++) {
result.push_back(result[i] * power);
}
}
}
std::sort(result.begin(), result.end());
return result;
}
inline uint64_t euler_phi(uint64_t value) {
assert(value >= 1);
uint64_t result = value;
for (const auto& factor : prime_factorize(value)) {
result = result / factor.first * (factor.first - 1);
}
return result;
}
inline int mobius(uint64_t value) {
assert(value >= 1);
int result = 1;
for (const auto& factor : prime_factorize(value)) {
if (factor.second >= 2) return 0;
result = -result;
}
return result;
}
} // namespace math
} // namespace m1une
#line 11 "math/cyclotomic_polynomial.hpp"
namespace m1une {
namespace math {
template <class T = long long>
std::vector<T> cyclotomic_polynomial(std::uint64_t index) {
assert(index >= 1);
if (index == 1) return {T(-1), T(1)};
const std::vector<std::pair<std::uint64_t, int>> factors =
prime_factorize(index);
std::uint64_t degree = index;
for (const auto& factor : factors) {
degree = degree / factor.first * (factor.first - 1);
}
assert(degree < std::numeric_limits<std::size_t>::max());
std::vector<T> result(static_cast<std::size_t>(degree) + 1, T(0));
result[0] = T(1);
const std::size_t subset_count = std::size_t(1) << factors.size();
for (std::size_t mask = 0; mask < subset_count; mask++) {
std::uint64_t exponent = index;
bool negative_mobius = false;
for (std::size_t i = 0; i < factors.size(); i++) {
if ((mask >> i) & 1) {
exponent /= factors[i].first;
negative_mobius = !negative_mobius;
}
}
if (exponent > degree) continue;
const std::size_t shift = static_cast<std::size_t>(exponent);
if (negative_mobius) {
// Divide by 1 - x^shift as a truncated formal power series.
for (std::size_t i = shift; i <= degree; i++) {
result[i] += result[i - shift];
}
} else {
// Multiply by 1 - x^shift.
for (std::size_t i = static_cast<std::size_t>(degree);
i >= shift;
i--) {
result[i] -= result[i - shift];
if (i == shift) break;
}
}
}
return result;
}
} // namespace math
} // namespace m1une
#line 1 "math/discrete_logarithm.hpp"
#line 5 "math/discrete_logarithm.hpp"
#include <cmath>
#line 10 "math/discrete_logarithm.hpp"
#include <unordered_map>
namespace m1une {
namespace math {
namespace internal {
inline uint64_t discrete_logarithm_multiply(
uint64_t first,
uint64_t second,
uint64_t mod
) {
return static_cast<uint64_t>(static_cast<unsigned __int128>(first) * second % mod);
}
inline uint64_t discrete_logarithm_power(
uint64_t base,
uint64_t exponent,
uint64_t mod
) {
uint64_t result = 1 % mod;
base %= mod;
while (exponent > 0) {
if (exponent & 1) result = discrete_logarithm_multiply(result, base, mod);
base = discrete_logarithm_multiply(base, base, mod);
exponent >>= 1;
}
return result;
}
inline uint64_t discrete_logarithm_ceil_sqrt(uint64_t value) {
uint64_t result = static_cast<uint64_t>(std::sqrt(static_cast<long double>(value)));
while (static_cast<unsigned __int128>(result) * result < value) result++;
while (result > 0 &&
static_cast<unsigned __int128>(result - 1) * (result - 1) >= value) {
result--;
}
return result;
}
struct DiscreteLogarithmHash {
std::size_t operator()(uint64_t value) const {
value += 0x9e3779b97f4a7c15ULL;
value = (value ^ (value >> 30)) * 0xbf58476d1ce4e5b9ULL;
value = (value ^ (value >> 27)) * 0x94d049bb133111ebULL;
value ^= value >> 31;
return static_cast<std::size_t>(value);
}
};
// Solves coefficient * base^exponent = target (mod mod) for a positive
// exponent. Both coefficient and base must be invertible modulo mod.
inline std::optional<uint64_t> discrete_logarithm_coprime(
uint64_t coefficient,
uint64_t base,
uint64_t target,
uint64_t mod
) {
const uint64_t step = discrete_logarithm_ceil_sqrt(mod);
std::unordered_map<uint64_t, uint64_t, DiscreteLogarithmHash> baby;
baby.max_load_factor(0.7F);
baby.reserve(static_cast<std::size_t>(step));
uint64_t value = target;
for (uint64_t exponent = 0; exponent < step; exponent++) {
baby[value] = exponent;
value = discrete_logarithm_multiply(value, base, mod);
}
const uint64_t giant_factor = discrete_logarithm_power(base, step, mod);
uint64_t giant = coefficient;
for (uint64_t block = 1; block <= step; block++) {
giant = discrete_logarithm_multiply(giant, giant_factor, mod);
auto it = baby.find(giant);
if (it == baby.end()) continue;
const unsigned __int128 candidate =
static_cast<unsigned __int128>(block) * step - it->second;
if (candidate <= UINT64_MAX) return static_cast<uint64_t>(candidate);
}
return std::nullopt;
}
} // namespace internal
// Returns the smallest nonnegative exponent x satisfying base^x = target
// modulo mod, or nullopt when no such exponent exists. Defines 0^0 as 1.
inline std::optional<uint64_t> discrete_logarithm(
uint64_t base,
uint64_t target,
uint64_t mod
) {
assert(mod >= 1);
if (mod == 1) return uint64_t(0);
base %= mod;
target %= mod;
uint64_t coefficient = 1;
uint64_t offset = 0;
while (true) {
if (coefficient == target) return offset;
const uint64_t divisor = std::gcd(base, mod);
if (divisor == 1) break;
if (target % divisor != 0) return std::nullopt;
target /= divisor;
mod /= divisor;
coefficient = internal::discrete_logarithm_multiply(
coefficient % mod, (base / divisor) % mod, mod);
offset++;
}
auto remaining = internal::discrete_logarithm_coprime(
coefficient, base % mod, target, mod);
if (!remaining.has_value()) return std::nullopt;
return offset + *remaining;
}
} // namespace math
} // namespace m1une
#line 1 "math/divisor_convolution.hpp"
#line 6 "math/divisor_convolution.hpp"
#line 8 "math/divisor_convolution.hpp"
namespace m1une {
namespace math {
template <typename T>
std::vector<T> gcd_convolution(
std::vector<T> first,
std::vector<T> second
) {
if (first.empty() || second.empty()) return {};
const std::size_t size = first.size() > second.size()
? first.size()
: second.size();
first.resize(size);
second.resize(size);
first[0] = T{};
second[0] = T{};
multiple_zeta_transform(first);
multiple_zeta_transform(second);
for (std::size_t index = 1; index < size; ++index) {
first[index] *= second[index];
}
multiple_mobius_transform(first);
return first;
}
template <typename T>
std::vector<T> lcm_convolution(
std::vector<T> first,
std::vector<T> second
) {
if (first.empty() || second.empty()) return {};
const std::size_t size = first.size() > second.size()
? first.size()
: second.size();
first.resize(size);
second.resize(size);
first[0] = T{};
second[0] = T{};
divisor_zeta_transform(first);
divisor_zeta_transform(second);
for (std::size_t index = 1; index < size; ++index) {
first[index] *= second[index];
}
divisor_mobius_transform(first);
return first;
}
} // namespace math
} // namespace m1une
#line 1 "math/gaussian_integer.hpp"
#line 10 "math/gaussian_integer.hpp"
namespace m1une {
namespace math {
template <std::signed_integral T = long long>
struct GaussianInteger {
static_assert(sizeof(T) <= sizeof(long long));
using value_type = T;
using norm_type = __uint128_t;
T real;
T imag;
private:
using wide_type = __int128_t;
struct SignedMagnitude {
norm_type magnitude;
bool negative;
};
static constexpr norm_type magnitude(T value) {
const wide_type wide = value;
if (wide < 0) {
return static_cast<norm_type>(-(wide + 1)) + 1;
}
return static_cast<norm_type>(wide);
}
static constexpr SignedMagnitude signed_value(T value) {
return SignedMagnitude{magnitude(value), value < 0};
}
static constexpr SignedMagnitude negate(SignedMagnitude value) {
if (value.magnitude != 0) value.negative = !value.negative;
return value;
}
static constexpr SignedMagnitude add_signed(
SignedMagnitude first,
SignedMagnitude second
) {
if (first.negative == second.negative) {
return SignedMagnitude{
first.magnitude + second.magnitude,
first.negative
};
}
if (first.magnitude < second.magnitude) {
return SignedMagnitude{
second.magnitude - first.magnitude,
second.negative
};
}
return SignedMagnitude{
first.magnitude - second.magnitude,
first.magnitude == second.magnitude ? false : first.negative
};
}
static constexpr SignedMagnitude product(T first, T second) {
const norm_type result = magnitude(first) * magnitude(second);
return SignedMagnitude{
result,
result != 0 && ((first < 0) != (second < 0))
};
}
static constexpr T narrow(SignedMagnitude value) {
const norm_type maximum =
static_cast<norm_type>(std::numeric_limits<T>::max());
if (!value.negative) {
assert(value.magnitude <= maximum);
return static_cast<T>(value.magnitude);
}
assert(value.magnitude <= maximum + 1);
if (value.magnitude == maximum + 1) {
return std::numeric_limits<T>::min();
}
return static_cast<T>(-static_cast<wide_type>(value.magnitude));
}
static constexpr std::pair<SignedMagnitude, SignedMagnitude>
product_components(
const GaussianInteger& first,
const GaussianInteger& second
) {
const SignedMagnitude product_real = add_signed(
product(first.real, second.real),
negate(product(first.imag, second.imag))
);
const SignedMagnitude product_imag = add_signed(
product(first.real, second.imag),
product(first.imag, second.real)
);
return {product_real, product_imag};
}
static constexpr T round_ratio(
SignedMagnitude numerator,
norm_type denominator
) {
assert(denominator != 0);
norm_type quotient = numerator.magnitude / denominator;
const norm_type remainder = numerator.magnitude % denominator;
if (remainder + remainder >= denominator) quotient++;
return narrow(SignedMagnitude{quotient, numerator.negative});
}
public:
constexpr GaussianInteger() : real(0), imag(0) {}
constexpr GaussianInteger(T real_) : real(real_), imag(0) {}
constexpr GaussianInteger(T real_, T imag_)
: real(real_), imag(imag_) {}
constexpr bool is_zero() const {
return real == 0 && imag == 0;
}
constexpr bool is_unit() const {
return norm() == 1;
}
constexpr norm_type norm() const {
const norm_type real_magnitude = magnitude(real);
const norm_type imag_magnitude = magnitude(imag);
return real_magnitude * real_magnitude +
imag_magnitude * imag_magnitude;
}
constexpr GaussianInteger conjugate() const {
return GaussianInteger(real, narrow(negate(signed_value(imag))));
}
constexpr GaussianInteger normalizing_unit() const {
if (is_zero() || (0 < real && 0 <= imag)) {
return GaussianInteger(1, 0);
}
if (0 < real) return GaussianInteger(0, 1);
if (real < 0 && imag <= 0) return GaussianInteger(-1, 0);
if (real < 0) return GaussianInteger(0, -1);
if (0 < imag) return GaussianInteger(0, -1);
return GaussianInteger(0, 1);
}
constexpr GaussianInteger normalized() const {
return normalizing_unit() * *this;
}
constexpr std::pair<GaussianInteger, GaussianInteger> divmod(
const GaussianInteger& divisor
) const {
const norm_type divisor_norm = divisor.norm();
assert(divisor_norm != 0);
const SignedMagnitude numerator_real = add_signed(
product(real, divisor.real),
product(imag, divisor.imag)
);
const SignedMagnitude numerator_imag = add_signed(
product(imag, divisor.real),
negate(product(real, divisor.imag))
);
const GaussianInteger quotient(
round_ratio(numerator_real, divisor_norm),
round_ratio(numerator_imag, divisor_norm)
);
const auto product = product_components(quotient, divisor);
const GaussianInteger remainder(
narrow(add_signed(signed_value(real), negate(product.first))),
narrow(add_signed(signed_value(imag), negate(product.second)))
);
assert(remainder.norm() < divisor_norm);
return {quotient, remainder};
}
constexpr GaussianInteger operator+() const {
return *this;
}
constexpr GaussianInteger operator-() const {
return GaussianInteger(
narrow(negate(signed_value(real))),
narrow(negate(signed_value(imag)))
);
}
constexpr GaussianInteger& operator+=(const GaussianInteger& other) {
real = narrow(add_signed(signed_value(real), signed_value(other.real)));
imag = narrow(add_signed(signed_value(imag), signed_value(other.imag)));
return *this;
}
constexpr GaussianInteger& operator-=(const GaussianInteger& other) {
real = narrow(add_signed(
signed_value(real),
negate(signed_value(other.real))
));
imag = narrow(add_signed(
signed_value(imag),
negate(signed_value(other.imag))
));
return *this;
}
constexpr GaussianInteger& operator*=(const GaussianInteger& other) {
const auto result = product_components(*this, other);
real = narrow(result.first);
imag = narrow(result.second);
return *this;
}
constexpr GaussianInteger& operator/=(const GaussianInteger& other) {
*this = divmod(other).first;
return *this;
}
constexpr GaussianInteger& operator%=(const GaussianInteger& other) {
*this = divmod(other).second;
return *this;
}
friend constexpr GaussianInteger operator+(
GaussianInteger left,
const GaussianInteger& right
) {
return left += right;
}
friend constexpr GaussianInteger operator-(
GaussianInteger left,
const GaussianInteger& right
) {
return left -= right;
}
friend constexpr GaussianInteger operator*(
GaussianInteger left,
const GaussianInteger& right
) {
return left *= right;
}
friend constexpr GaussianInteger operator/(
GaussianInteger left,
const GaussianInteger& right
) {
return left /= right;
}
friend constexpr GaussianInteger operator%(
GaussianInteger left,
const GaussianInteger& right
) {
return left %= right;
}
friend constexpr bool operator==(
const GaussianInteger& first,
const GaussianInteger& second
) = default;
};
template <std::signed_integral T>
constexpr bool gaussian_divides(
const GaussianInteger<T>& divisor,
const GaussianInteger<T>& value
) {
if (divisor.is_zero()) return value.is_zero();
return (value % divisor).is_zero();
}
template <std::signed_integral T>
constexpr bool gaussian_associates(
const GaussianInteger<T>& first,
const GaussianInteger<T>& second
) {
return first.normalized() == second.normalized();
}
template <std::signed_integral T>
constexpr GaussianInteger<T> gaussian_gcd(
GaussianInteger<T> first,
GaussianInteger<T> second
) {
while (!second.is_zero()) {
first %= second;
std::swap(first, second);
}
return first.normalized();
}
template <std::signed_integral T>
constexpr std::tuple<
GaussianInteger<T>,
GaussianInteger<T>,
GaussianInteger<T>
> extended_gaussian_gcd(
GaussianInteger<T> first,
GaussianInteger<T> second
) {
using G = GaussianInteger<T>;
G old_remainder = first;
G remainder = second;
G old_first_coefficient(1);
G first_coefficient(0);
G old_second_coefficient(0);
G second_coefficient(1);
while (!remainder.is_zero()) {
const G quotient = old_remainder / remainder;
G next = old_remainder - quotient * remainder;
old_remainder = remainder;
remainder = next;
next = old_first_coefficient - quotient * first_coefficient;
old_first_coefficient = first_coefficient;
first_coefficient = next;
next = old_second_coefficient - quotient * second_coefficient;
old_second_coefficient = second_coefficient;
second_coefficient = next;
}
const G unit = old_remainder.normalizing_unit();
return {
unit * old_remainder,
unit * old_first_coefficient,
unit * old_second_coefficient
};
}
} // namespace math
} // namespace m1une
#line 1 "math/generalized_floor_sum.hpp"
#line 9 "math/generalized_floor_sum.hpp"
namespace m1une {
namespace math {
template <class T, int MaxPower, int MaxFloorPower>
using GeneralizedFloorSumTable =
std::array<std::array<T, MaxFloorPower + 1>, MaxPower + 1>;
namespace generalized_floor_sum_detail {
using SignedWide = __int128_t;
using UnsignedWide = __uint128_t;
template <class T>
T from_wide(SignedWide value) {
bool negative = value < 0;
UnsignedWide magnitude;
if (negative) {
magnitude = static_cast<UnsignedWide>(-(value + 1));
++magnitude;
} else {
magnitude = static_cast<UnsignedWide>(value);
}
T result = T();
T binary_place = T(1);
while (magnitude > 0) {
if ((magnitude & 1) != 0) result += binary_place;
magnitude >>= 1;
if (magnitude > 0) binary_place += binary_place;
}
return negative ? T() - result : result;
}
inline SignedWide floor_div(SignedWide numerator, SignedWide denominator) {
assert(denominator > 0);
SignedWide quotient = numerator / denominator;
if (numerator % denominator < 0) --quotient;
return quotient;
}
template <class T, int MaxPower, int MaxFloorPower>
class MomentMonoid {
public:
using Table = GeneralizedFloorSumTable<T, MaxPower, MaxFloorPower>;
struct Data {
Table sums{};
T delta_x = T();
T delta_y = T();
};
static constexpr int MaximumDegree =
MaxPower > MaxFloorPower ? MaxPower : MaxFloorPower;
MomentMonoid() {
binomial_[0][0] = T(1);
for (int degree = 0; degree < MaximumDegree; ++degree) {
for (int index = 0; index <= degree; ++index) {
binomial_[degree + 1][index] += binomial_[degree][index];
binomial_[degree + 1][index + 1] +=
binomial_[degree][index];
}
}
}
const T& binomial(int n, int k) const {
assert(0 <= k && k <= n && n <= MaximumDegree);
return binomial_[n][k];
}
Data unit() const {
return Data();
}
Data x_step() const {
Data result;
result.sums[0][0] = T(1);
result.delta_x = T(1);
return result;
}
Data y_step() const {
Data result;
result.delta_y = T(1);
return result;
}
Data concatenate(Data left, Data right) const {
std::array<T, MaxPower + 1> x_powers{};
std::array<T, MaxFloorPower + 1> y_powers{};
x_powers[0] = T(1);
y_powers[0] = T(1);
for (int power = 0; power < MaxPower; ++power) {
x_powers[power + 1] = x_powers[power] * left.delta_x;
}
for (int power = 0; power < MaxFloorPower; ++power) {
y_powers[power + 1] = y_powers[power] * left.delta_y;
}
// Shift the y-coordinate of every sampled x-step in the right path.
for (int x_power = 0; x_power <= MaxPower; ++x_power) {
for (int old_power = MaxFloorPower; old_power >= 0; --old_power) {
T source = right.sums[x_power][old_power];
for (int new_power = old_power + 1;
new_power <= MaxFloorPower;
++new_power) {
right.sums[x_power][new_power] +=
binomial_[new_power][old_power] *
y_powers[new_power - old_power] * source;
}
}
}
// Shift x, then append all samples from the right path.
for (int y_power = 0; y_power <= MaxFloorPower; ++y_power) {
for (int old_power = 0; old_power <= MaxPower; ++old_power) {
T source = right.sums[old_power][y_power];
for (int new_power = old_power;
new_power <= MaxPower;
++new_power) {
left.sums[new_power][y_power] +=
binomial_[new_power][old_power] *
x_powers[new_power - old_power] * source;
}
}
}
left.delta_x += right.delta_x;
left.delta_y += right.delta_y;
return left;
}
private:
std::array<std::array<T, MaximumDegree + 1>, MaximumDegree + 1>
binomial_{};
};
template <class Monoid>
typename Monoid::Data monoid_power(
const Monoid& monoid,
typename Monoid::Data base,
UnsignedWide exponent
) {
typename Monoid::Data result = monoid.unit();
while (exponent > 0) {
if ((exponent & 1) != 0) {
result = monoid.concatenate(std::move(result), base);
}
exponent >>= 1;
if (exponent > 0) {
base = monoid.concatenate(base, base);
}
}
return result;
}
template <class Monoid>
typename Monoid::Data floor_path_product(
const Monoid& monoid,
UnsignedWide n,
UnsignedWide a,
UnsignedWide b,
UnsignedWide modulus
) {
assert(modulus > 0);
UnsignedWide height = (a * n + b) / modulus;
typename Monoid::Data x = monoid.x_step();
typename Monoid::Data y = monoid.y_step();
typename Monoid::Data prefix = monoid.unit();
typename Monoid::Data suffix = monoid.unit();
while (true) {
UnsignedWide slope_quotient = a / modulus;
UnsignedWide intercept_quotient = b / modulus;
a %= modulus;
b %= modulus;
x = monoid.concatenate(
std::move(x),
monoid_power(monoid, y, slope_quotient)
);
prefix = monoid.concatenate(
std::move(prefix),
monoid_power(monoid, y, intercept_quotient)
);
height -= slope_quotient * n + intercept_quotient;
if (height == 0) break;
assert(a > 0);
UnsignedWide boundary =
(modulus * height - b - 1) / a + 1;
suffix = monoid.concatenate(
y,
monoid.concatenate(
monoid_power(monoid, x, n - boundary),
std::move(suffix)
)
);
b = modulus - b - 1 + a;
n = height - 1;
height = boundary;
std::swap(modulus, a);
std::swap(x, y);
}
x = monoid_power(monoid, x, n);
return monoid.concatenate(
monoid.concatenate(std::move(prefix), std::move(x)),
std::move(suffix)
);
}
template <class T, int MaxPower, int MaxFloorPower>
GeneralizedFloorSumTable<T, MaxPower, MaxFloorPower>
nonnegative_slope_table(
const MomentMonoid<T, MaxPower, MaxFloorPower>& monoid,
SignedWide n,
SignedWide modulus,
SignedWide a,
SignedWide b
) {
assert(n >= 0 && modulus > 0 && a >= 0);
SignedWide y_offset = floor_div(b, modulus);
SignedWide normalized_b = b - y_offset * modulus;
auto path = floor_path_product(
monoid,
static_cast<UnsignedWide>(n),
static_cast<UnsignedWide>(a),
static_cast<UnsignedWide>(normalized_b),
static_cast<UnsignedWide>(modulus)
);
std::array<T, MaxFloorPower + 1> offset_powers{};
offset_powers[0] = T(1);
T offset = from_wide<T>(y_offset);
for (int power = 0; power < MaxFloorPower; ++power) {
offset_powers[power + 1] = offset_powers[power] * offset;
}
GeneralizedFloorSumTable<T, MaxPower, MaxFloorPower> result{};
for (int x_power = 0; x_power <= MaxPower; ++x_power) {
for (int y_power = 0; y_power <= MaxFloorPower; ++y_power) {
for (int inner_power = 0;
inner_power <= y_power;
++inner_power) {
result[x_power][y_power] +=
monoid.binomial(y_power, inner_power) *
offset_powers[y_power - inner_power] *
path.sums[x_power][inner_power];
}
}
}
return result;
}
} // namespace generalized_floor_sum_detail
// Returns every sum of x^p * floor((a*x+b)/mod)^q for 0 <= x < n,
// 0 <= p <= MaxPower, and 0 <= q <= MaxFloorPower.
template <class T, int MaxPower, int MaxFloorPower, class I>
GeneralizedFloorSumTable<T, MaxPower, MaxFloorPower>
generalized_floor_sum_table(I n, I mod, I a, I b) {
static_assert(MaxPower >= 0 && MaxFloorPower >= 0);
static_assert(
std::is_integral_v<I> && std::is_signed_v<I> && sizeof(I) <= 8,
"generalized_floor_sum_table requires signed integer arguments"
);
assert(n >= 0);
assert(mod > 0);
namespace detail = generalized_floor_sum_detail;
using Monoid = detail::MomentMonoid<T, MaxPower, MaxFloorPower>;
static const Monoid monoid;
detail::SignedWide wide_n = n;
detail::SignedWide wide_mod = mod;
detail::SignedWide wide_a = a;
detail::SignedWide wide_b = b;
if (wide_n == 0) {
return GeneralizedFloorSumTable<T, MaxPower, MaxFloorPower>();
}
if (wide_a >= 0) {
return detail::nonnegative_slope_table(
monoid,
wide_n,
wide_mod,
wide_a,
wide_b
);
}
// Substitute x = n - 1 - t to make the slope nonnegative.
auto reflected = detail::nonnegative_slope_table(
monoid,
wide_n,
wide_mod,
-wide_a,
wide_a * (wide_n - 1) + wide_b
);
std::array<T, MaxPower + 1> offset_powers{};
offset_powers[0] = T(1);
T offset = detail::from_wide<T>(wide_n - 1);
for (int power = 0; power < MaxPower; ++power) {
offset_powers[power + 1] = offset_powers[power] * offset;
}
GeneralizedFloorSumTable<T, MaxPower, MaxFloorPower> result{};
for (int x_power = 0; x_power <= MaxPower; ++x_power) {
for (int y_power = 0; y_power <= MaxFloorPower; ++y_power) {
for (int inner_power = 0;
inner_power <= x_power;
++inner_power) {
T coefficient =
monoid.binomial(x_power, inner_power) *
offset_powers[x_power - inner_power];
if ((inner_power & 1) != 0) coefficient = T() - coefficient;
result[x_power][y_power] +=
coefficient * reflected[inner_power][y_power];
}
}
}
return result;
}
template <class T, int Power, int FloorPower, class I>
T generalized_floor_sum(I n, I mod, I a, I b) {
return generalized_floor_sum_table<T, Power, FloorPower>(n, mod, a, b)
[Power][FloorPower];
}
} // namespace math
} // namespace m1une
#line 1 "math/integer_arithmetic.hpp"
#line 9 "math/integer_arithmetic.hpp"
namespace m1une {
namespace math {
namespace integer_arithmetic_detail {
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr std::optional<T> checked_multiply(T first, T second) {
constexpr T minimum = std::numeric_limits<T>::min();
constexpr T maximum = std::numeric_limits<T>::max();
if constexpr (std::unsigned_integral<T>) {
if (second != 0 && maximum / second < first) return std::nullopt;
} else {
if (0 < first) {
if (0 < second) {
if (maximum / second < first) return std::nullopt;
} else if (second < minimum / first) {
return std::nullopt;
}
} else if (first < 0) {
if (0 < second) {
if (first < minimum / second) return std::nullopt;
} else if (second < maximum / first) {
return std::nullopt;
}
}
}
return T(first * second);
}
template <std::unsigned_integral T>
constexpr bool kth_power_leq(T base, unsigned exponent, T limit) {
assert(exponent > 0);
if (base <= 1) return base <= limit;
const T multiplication_limit = limit / base;
T product = 1;
for (unsigned i = 0; i < exponent; i++) {
if (product > multiplication_limit) return false;
product *= base;
}
return true;
}
} // namespace integer_arithmetic_detail
// Returns floor(sqrt(value)) exactly, without floating-point arithmetic.
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr T isqrt(T value) {
if constexpr (std::signed_integral<T>) assert(0 <= value);
if (value <= 1) return value;
T low = 1;
T high = value / 2 + 1;
while (low < high) {
T middle = low + (high - low + 1) / 2;
if (middle <= value / middle) {
low = middle;
} else {
high = middle - 1;
}
}
return low;
}
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr T floor_sqrt(T value) {
return isqrt(value);
}
// Returns ceil(sqrt(value)) exactly, without floating-point arithmetic.
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
constexpr T ceil_sqrt(T value) {
T result = isqrt(value);
if (result == 0) return 0;
if (result != 0 && value / result == result && value % result == 0) {
return result;
}
return result + 1;
}
// Returns floor(value^(1 / degree)) exactly, without floating-point arithmetic.
template <std::integral T, std::integral Degree>
requires(
!std::same_as<std::remove_cv_t<T>, bool>
&& !std::same_as<std::remove_cv_t<Degree>, bool>
)
constexpr T floor_kth_root(T value, Degree degree) {
if constexpr (std::signed_integral<T>) {
assert(0 <= value);
if (value < 0) return T();
}
assert(0 < degree);
if (degree <= 0) return T();
if (value <= 1 || degree == 1) return value;
if (degree == 2) return isqrt(value);
using U = std::make_unsigned_t<T>;
using UDegree = std::make_unsigned_t<Degree>;
constexpr int digits = std::numeric_limits<U>::digits;
const UDegree unsigned_degree = static_cast<UDegree>(degree);
if (unsigned_degree >= static_cast<UDegree>(digits)) return T(1);
const unsigned exponent = static_cast<unsigned>(unsigned_degree);
const U unsigned_value = static_cast<U>(value);
int bit_width = 0;
for (U remaining = unsigned_value; remaining != 0; remaining >>= 1) {
bit_width++;
}
const int root_bits =
(bit_width + static_cast<int>(exponent) - 1) /
static_cast<int>(exponent);
U low = 1;
U high = U(1) << root_bits;
while (high - low > 1) {
const U middle = low + (high - low) / 2;
if (
integer_arithmetic_detail::kth_power_leq(
middle, exponent, unsigned_value
)
) {
low = middle;
} else {
high = middle;
}
}
return static_cast<T>(low);
}
// Returns base^exponent, or nullopt when the result does not fit in T.
template <std::integral T, std::unsigned_integral Exponent>
requires(
!std::same_as<std::remove_cv_t<T>, bool>
&& !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr std::optional<T> checked_ipow(T base, Exponent exponent) {
T result = 1;
while (exponent != 0) {
if (exponent & 1) {
auto product =
integer_arithmetic_detail::checked_multiply(result, base);
if (!product.has_value()) return std::nullopt;
result = *product;
}
exponent >>= 1;
if (exponent != 0) {
auto square =
integer_arithmetic_detail::checked_multiply(base, base);
if (!square.has_value()) return std::nullopt;
base = *square;
}
}
return result;
}
template <std::integral T, std::unsigned_integral Exponent>
requires(
!std::same_as<std::remove_cv_t<T>, bool>
&& !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr std::optional<T> checked_integer_pow(T base, Exponent exponent) {
return checked_ipow(base, exponent);
}
// Returns base^exponent. The result must be representable by T.
template <std::integral T, std::unsigned_integral Exponent>
requires(
!std::same_as<std::remove_cv_t<T>, bool>
&& !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr T ipow(T base, Exponent exponent) {
std::optional<T> result = checked_ipow(base, exponent);
assert(result.has_value());
return result.value_or(T());
}
template <std::integral T, std::unsigned_integral Exponent>
requires(
!std::same_as<std::remove_cv_t<T>, bool>
&& !std::same_as<std::remove_cv_t<Exponent>, bool>
)
constexpr T integer_pow(T base, Exponent exponent) {
return ipow(base, exponent);
}
} // namespace math
} // namespace m1une
#line 1 "math/lucas.hpp"
#line 7 "math/lucas.hpp"
namespace m1une {
namespace math {
template <class Mint>
struct Lucas {
private:
std::vector<Mint> _factorial;
std::vector<Mint> _inverse_factorial;
Mint small_binom(uint32_t n, uint32_t k) const {
if (k > n) return Mint(0);
return _factorial[n] * _inverse_factorial[k] * _inverse_factorial[n - k];
}
public:
Lucas() {
const uint32_t prime = Mint::mod();
assert(2 <= prime);
_factorial.resize(prime);
_inverse_factorial.resize(prime);
_factorial[0] = Mint(1);
for (uint32_t i = 1; i < prime; i++) {
_factorial[i] = _factorial[i - 1] * Mint(i);
}
_inverse_factorial[prime - 1] = _factorial[prime - 1].inv();
for (uint32_t i = prime - 1; i > 0; i--) {
_inverse_factorial[i - 1] = _inverse_factorial[i] * Mint(i);
}
}
uint32_t prime() const {
return Mint::mod();
}
Mint binom(uint64_t n, uint64_t k) const {
if (k > n) return Mint(0);
const uint64_t modulus = Mint::mod();
Mint result = Mint(1);
while (n > 0 || k > 0) {
uint32_t n_digit = uint32_t(n % modulus);
uint32_t k_digit = uint32_t(k % modulus);
if (k_digit > n_digit) return Mint(0);
result *= small_binom(n_digit, k_digit);
n /= modulus;
k /= modulus;
}
return result;
}
Mint operator()(uint64_t n, uint64_t k) const {
return binom(n, k);
}
};
template <class Mint>
using LucasTheorem = Lucas<Mint>;
} // namespace math
} // namespace m1une
#line 1 "math/fps/all.hpp"
#line 1 "math/fps/composition.hpp"
#line 10 "math/fps/composition.hpp"
#line 12 "math/fps/composition.hpp"
namespace m1une {
namespace fps {
namespace internal {
template <class Mint>
using CompositionMatrix = std::vector<std::vector<Mint>>;
template <class Mint>
CompositionMatrix<Mint> composition_convolution_2d(const CompositionMatrix<Mint>& lhs,
const CompositionMatrix<Mint>& rhs) {
const int lhs_height = int(lhs.size());
const int rhs_height = int(rhs.size());
const int lhs_width = int(lhs[0].size());
const int rhs_width = int(rhs[0].size());
const int height = lhs_height + rhs_height - 1;
const int width = lhs_width + rhs_width - 1;
std::vector<Mint> flattened_lhs(std::size_t(lhs_height) * width);
std::vector<Mint> flattened_rhs(std::size_t(rhs_height) * width);
for (int i = 0; i < lhs_height; i++) {
std::copy(lhs[i].begin(), lhs[i].end(),
flattened_lhs.begin() + std::size_t(i) * width);
}
for (int i = 0; i < rhs_height; i++) {
std::copy(rhs[i].begin(), rhs[i].end(),
flattened_rhs.begin() + std::size_t(i) * width);
}
std::vector<Mint> flattened_result = convolution(flattened_lhs, flattened_rhs);
CompositionMatrix<Mint> result(height, std::vector<Mint>(width));
for (int i = 0; i < height; i++) {
std::copy_n(flattened_result.begin() + std::size_t(i) * width, width,
result[i].begin());
}
return result;
}
// result[i] = sum_j kernel[j] * values[i + j]
template <class Mint>
std::vector<Mint> composition_middle_product(const std::vector<Mint>& values,
const std::vector<Mint>& kernel) {
assert(values.size() >= kernel.size() && !kernel.empty());
const int kernel_size = int(kernel.size());
const int result_size = int(values.size()) - kernel_size + 1;
if (std::min(kernel_size, result_size) <= 32) {
std::vector<Mint> result(result_size);
for (int i = 0; i < result_size; i++) {
for (int j = 0; j < kernel_size; j++) result[i] += kernel[j] * values[i + j];
}
return result;
}
int transform_size = 1;
while ((transform_size << 1) <= int(values.size()) * 2 - 1) transform_size <<= 1;
if ((Mint::mod() - 1) % uint32_t(transform_size) == 0) {
std::vector<Mint> transformed_values(transform_size);
std::vector<Mint> transformed_kernel(transform_size);
std::copy(values.begin(), values.end(), transformed_values.begin());
std::reverse_copy(kernel.begin(), kernel.end(), transformed_kernel.begin());
ntt(transformed_values, false);
ntt(transformed_kernel, false);
for (int i = 0; i < transform_size; i++) transformed_values[i] *= transformed_kernel[i];
ntt(transformed_values, true);
return std::vector<Mint>(transformed_values.begin() + kernel_size - 1,
transformed_values.begin() + int(values.size()));
}
std::vector<Mint> reversed_kernel(kernel.rbegin(), kernel.rend());
std::vector<Mint> product = convolution(values, reversed_kernel);
return std::vector<Mint>(product.begin() + kernel_size - 1,
product.begin() + int(values.size()));
}
} // namespace internal
// Returns outer(inner(x)) modulo x^degree. Formal composition requires inner(0) = 0.
template <class Mint>
FormalPowerSeries<Mint> compose(const FormalPowerSeries<Mint>& outer,
const FormalPowerSeries<Mint>& inner, int degree = -1) {
using Matrix = internal::CompositionMatrix<Mint>;
if (degree < 0) degree = int(outer.size());
assert(degree >= 0);
if (degree == 0) return {};
assert(inner.empty() || inner[0] == Mint(0));
FormalPowerSeries<Mint> coefficients = outer.pre(degree);
FormalPowerSeries<Mint> argument = inner.pre(degree);
auto transpose_power_projection = [&](auto&& self, Matrix denominator) -> Matrix {
const int x_degree = int(denominator.size()) - 1;
const int y_degree = int(denominator[0].size()) - 1;
if (x_degree == 0) {
Matrix result(1, std::vector<Mint>(y_degree + 1));
std::copy(coefficients.begin(), coefficients.end(), result[0].begin());
return result;
}
Matrix conjugate = denominator;
for (int i = 1; i <= x_degree; i += 2) {
for (Mint& value : conjugate[i]) value = Mint(0) - value;
}
Matrix reduced = internal::composition_convolution_2d(denominator, conjugate);
for (int i = 1; i <= x_degree / 2; i++) reduced[i] = std::move(reduced[i << 1]);
reduced.resize(x_degree / 2 + 1);
Matrix().swap(denominator);
Matrix projected = self(self, std::move(reduced));
const int block_width = 2 * y_degree + 1;
std::vector<Mint> expanded(std::size_t(2) * (x_degree + 1) * block_width - 1);
for (int i = 0; i < int(projected.size()); i++) {
for (int j = 0; j < int(projected[i].size()); j++) {
const int row = 2 * i + (x_degree & 1);
expanded[std::size_t(row) * block_width + j] = projected[i][j];
}
}
std::vector<Mint> flattened_conjugate(std::size_t(x_degree + 1) * block_width);
for (int i = 0; i <= x_degree; i++) {
std::copy(conjugate[i].begin(), conjugate[i].end(),
flattened_conjugate.begin() + std::size_t(i) * block_width);
}
std::vector<Mint> middle =
internal::composition_middle_product(expanded, flattened_conjugate);
Matrix result(x_degree + 1, std::vector<Mint>(y_degree + 1));
for (int i = 0; i <= x_degree; i++) {
std::copy_n(middle.begin() + std::size_t(i) * block_width, y_degree + 1,
result[i].begin());
}
return result;
};
Matrix denominator(degree, std::vector<Mint>(2));
denominator[0][0] = Mint(1);
for (int i = 0; i < degree; i++) denominator[i][1] = Mint(0) - argument[i];
Matrix transposed =
transpose_power_projection(transpose_power_projection, std::move(denominator));
FormalPowerSeries<Mint> result(degree);
for (int i = 0; i < degree; i++) result[i] = transposed[i][0];
std::reverse(result.begin(), result.end());
return result;
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/compositional_inverse.hpp"
#line 6 "math/fps/compositional_inverse.hpp"
#line 8 "math/fps/compositional_inverse.hpp"
namespace m1une {
namespace fps {
template <class Mint>
FormalPowerSeries<Mint> compositional_inverse(const FormalPowerSeries<Mint>& f,
int degree = -1) {
using Fps = FormalPowerSeries<Mint>;
if (degree < 0) degree = int(f.size());
assert(degree >= 0);
if (degree == 0) return {};
assert(f.size() >= 2 && f[0] == Mint(0) && f[1] != Mint(0));
Fps result(2);
result[1] = f[1].inv();
if (degree == 1) return result.pre(1);
for (int size = 2; size < degree;) {
const int next_size = std::min(size << 1, degree);
Fps composed = compose(f.pre(next_size), result, next_size);
const int correction_size = next_size - size;
Fps inverse_derivative = composed.derivative().inv(correction_size);
composed[1] -= Mint(1);
Fps inverse_jacobian =
(result.derivative().pre(correction_size) * inverse_derivative)
.pre(correction_size);
Fps correction = ((composed >> size) * inverse_jacobian).pre(correction_size);
correction <<= size;
result = result.pre(next_size) - correction;
result.resize(next_size);
size = next_size;
}
return result.pre(degree);
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/convolution_ll.hpp"
#line 8 "math/fps/convolution_ll.hpp"
#line 11 "math/fps/convolution_ll.hpp"
namespace m1une {
namespace fps {
// Exact convolution of signed 64-bit coefficients.
// Every result coefficient must fit in long long.
inline std::vector<long long> convolution_ll(
const std::vector<long long>& first,
const std::vector<long long>& second
) {
if (first.empty() || second.empty()) return {};
std::size_t result_size = first.size() + second.size() - 1;
assert(result_size <= (std::size_t(1) << 24));
using Mint1 = math::ModInt<167772161>;
using Mint2 = math::ModInt<469762049>;
using Mint3 = math::ModInt<754974721>;
auto convolve = [&]<class Mint>() {
std::vector<Mint> converted_first(first.size());
std::vector<Mint> converted_second(second.size());
for (int index = 0; index < int(first.size()); index++) {
converted_first[index] = Mint(first[index]);
}
for (int index = 0; index < int(second.size()); index++) {
converted_second[index] = Mint(second[index]);
}
return convolution(converted_first, converted_second);
};
std::vector<Mint1> result1 = convolve.template operator()<Mint1>();
std::vector<Mint2> result2 = convolve.template operator()<Mint2>();
std::vector<Mint3> result3 = convolve.template operator()<Mint3>();
static const std::uint64_t inverse_mod1_mod2 =
Mint2(Mint1::mod()).inv().val();
static const std::uint64_t mod1_mod2 =
std::uint64_t(Mint1::mod()) * Mint2::mod();
static const std::uint64_t inverse_mod1_mod2_mod3 =
Mint3(mod1_mod2 % Mint3::mod()).inv().val();
static const unsigned __int128 crt_modulus =
static_cast<unsigned __int128>(mod1_mod2) * Mint3::mod();
std::vector<long long> result(result_size);
for (int index = 0; index < int(result_size); index++) {
std::uint64_t residue1 = result1[index].val();
std::uint64_t residue2 = result2[index].val();
std::uint64_t residue3 = result3[index].val();
std::uint64_t second_digit =
(residue2 + Mint2::mod() - residue1 % Mint2::mod()) %
Mint2::mod();
second_digit = second_digit * inverse_mod1_mod2 % Mint2::mod();
std::uint64_t first_two =
residue1 + std::uint64_t(Mint1::mod()) * second_digit;
std::uint64_t third_digit =
(residue3 + Mint3::mod() - first_two % Mint3::mod()) %
Mint3::mod();
third_digit =
third_digit * inverse_mod1_mod2_mod3 % Mint3::mod();
unsigned __int128 reconstructed =
first_two + static_cast<unsigned __int128>(mod1_mod2) * third_digit;
__int128 centered = reconstructed <= crt_modulus / 2
? static_cast<__int128>(reconstructed)
: -static_cast<__int128>(crt_modulus - reconstructed);
assert(std::numeric_limits<long long>::min() <= centered);
assert(centered <= std::numeric_limits<long long>::max());
result[index] = static_cast<long long>(centered);
}
return result;
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/floating_point_convolution.hpp"
#line 5 "math/fps/floating_point_convolution.hpp"
#include <bit>
#line 7 "math/fps/floating_point_convolution.hpp"
#include <complex>
#line 9 "math/fps/floating_point_convolution.hpp"
#include <numbers>
#line 12 "math/fps/floating_point_convolution.hpp"
namespace m1une {
namespace fps {
namespace floating_point_convolution_detail {
template <std::floating_point Real>
void fft(std::vector<std::complex<Real>>& values, bool inverse) {
int size = int(values.size());
for (int index = 1, reversed = 0; index < size; ++index) {
int bit = size >> 1;
while (reversed & bit) {
reversed ^= bit;
bit >>= 1;
}
reversed ^= bit;
if (index < reversed) std::swap(values[index], values[reversed]);
}
for (int length = 2; length <= size; length <<= 1) {
Real angle = Real(2) * std::numbers::pi_v<Real> / Real(length);
if (inverse) angle = -angle;
std::complex<Real> step(std::cos(angle), std::sin(angle));
int half = length >> 1;
for (int offset = 0; offset < size; offset += length) {
std::complex<Real> root(1, 0);
for (int index = 0; index < half; ++index) {
std::complex<Real> even = values[offset + index];
std::complex<Real> odd = values[offset + index + half] * root;
values[offset + index] = even + odd;
values[offset + index + half] = even - odd;
root *= step;
}
}
}
if (inverse) {
Real inverse_size = Real(1) / Real(size);
for (auto& value : values) value *= inverse_size;
}
}
template <std::floating_point Real>
std::vector<std::complex<Real>> complex_convolution(const std::vector<std::complex<Real>>& first,
const std::vector<std::complex<Real>>& second) {
if (first.empty() || second.empty()) return {};
std::size_t result_size = first.size() + second.size() - 1;
std::size_t size = std::bit_ceil(result_size);
std::vector<std::complex<Real>> transformed_first(size);
std::vector<std::complex<Real>> transformed_second(size);
std::copy(first.begin(), first.end(), transformed_first.begin());
std::copy(second.begin(), second.end(), transformed_second.begin());
fft(transformed_first, false);
fft(transformed_second, false);
for (std::size_t index = 0; index < size; ++index) {
transformed_first[index] *= transformed_second[index];
}
fft(transformed_first, true);
transformed_first.resize(result_size);
return transformed_first;
}
} // namespace floating_point_convolution_detail
// Convolution of complex floating-point coefficients.
template <std::floating_point Real>
std::vector<std::complex<Real>> convolution_fft(const std::vector<std::complex<Real>>& first,
const std::vector<std::complex<Real>>& second) {
return floating_point_convolution_detail::complex_convolution(first, second);
}
// Convolution of real floating-point coefficients.
template <std::floating_point Real>
std::vector<Real> convolution_fft(const std::vector<Real>& first, const std::vector<Real>& second) {
if (first.empty() || second.empty()) return {};
std::vector<std::complex<Real>> complex_first(first.size());
std::vector<std::complex<Real>> complex_second(second.size());
for (std::size_t index = 0; index < first.size(); ++index) {
complex_first[index] = std::complex<Real>(first[index], 0);
}
for (std::size_t index = 0; index < second.size(); ++index) {
complex_second[index] = std::complex<Real>(second[index], 0);
}
auto result = floating_point_convolution_detail::complex_convolution(complex_first, complex_second);
std::vector<Real> real_result(result.size());
for (std::size_t index = 0; index < result.size(); ++index) {
real_result[index] = result[index].real();
}
return real_result;
}
// Uses long-double FFT and rounds each coefficient to the nearest integer.
template <std::integral Integer>
std::vector<Integer> convolution_round(const std::vector<Integer>& first, const std::vector<Integer>& second) {
if (first.empty() || second.empty()) return {};
std::vector<long double> real_first(first.begin(), first.end());
std::vector<long double> real_second(second.begin(), second.end());
std::vector<long double> real_result = convolution_fft(real_first, real_second);
std::vector<Integer> result(real_result.size());
for (std::size_t index = 0; index < result.size(); ++index) {
result[index] = static_cast<Integer>(std::round(real_result[index]));
}
return result;
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/geometric_sequence_evaluation.hpp"
#line 8 "math/fps/geometric_sequence_evaluation.hpp"
#line 10 "math/fps/geometric_sequence_evaluation.hpp"
namespace m1une {
namespace fps {
namespace internal {
template <class Mint>
std::vector<Mint> geometric_triangular_powers(Mint ratio, int count) {
assert(count >= 0);
std::vector<Mint> result(count);
if (count == 0) return result;
result[0] = Mint(1);
Mint power = Mint(1);
for (int i = 0; i + 1 < count; ++i) {
result[i + 1] = result[i] * power;
power *= ratio;
}
return result;
}
template <class Mint>
std::vector<Mint> geometric_batch_inverse(const std::vector<Mint>& values) {
const int count = int(values.size());
std::vector<Mint> prefix(count + 1, Mint(1));
for (int i = 0; i < count; ++i) {
assert(values[i] != Mint(0));
prefix[i + 1] = prefix[i] * values[i];
}
std::vector<Mint> result(count);
Mint inverse_suffix = prefix[count].inv();
for (int i = count - 1; i >= 0; --i) {
result[i] = prefix[i] * inverse_suffix;
inverse_suffix *= values[i];
}
return result;
}
template <class Mint>
std::vector<Mint> geometric_middle_product(const std::vector<Mint>& first,
std::vector<Mint> second) {
assert(first.size() >= second.size());
if (second.empty()) return std::vector<Mint>(first.size() + 1);
std::reverse(second.begin(), second.end());
const std::vector<Mint> product = convolution(first, second);
const int result_size = int(first.size() - second.size() + 1);
return std::vector<Mint>(product.begin() + second.size() - 1,
product.begin() + second.size() - 1 + result_size);
}
} // namespace internal
template <class Mint>
std::vector<Mint> multipoint_evaluate_geometric(
const FormalPowerSeries<Mint>& polynomial, Mint initial, Mint ratio, int count) {
assert(count >= 0);
if (count == 0) return {};
if (polynomial.empty()) return std::vector<Mint>(count);
if (initial == Mint(0)) return std::vector<Mint>(count, polynomial[0]);
if (ratio == Mint(0)) {
std::vector<Mint> result(count, polynomial[0]);
result[0] = polynomial.evaluate(initial);
return result;
}
const int coefficient_count = int(polynomial.size());
std::vector<Mint> scaled(polynomial.begin(), polynomial.end());
Mint initial_power = Mint(1);
for (Mint& coefficient : scaled) {
coefficient *= initial_power;
initial_power *= initial;
}
const std::vector<Mint> chirp = internal::geometric_triangular_powers(
ratio, coefficient_count + count - 1);
const std::vector<Mint> inverse_chirp = internal::geometric_triangular_powers(
ratio.inv(), std::max(coefficient_count, count));
for (int i = 0; i < coefficient_count; ++i) scaled[i] *= inverse_chirp[i];
std::reverse(scaled.begin(), scaled.end());
const std::vector<Mint> product = convolution(scaled, chirp);
std::vector<Mint> result(count);
for (int i = 0; i < count; ++i) {
result[i] = product[coefficient_count - 1 + i] * inverse_chirp[i];
}
return result;
}
template <class Mint>
FormalPowerSeries<Mint> polynomial_interpolate_geometric(
const std::vector<Mint>& values, Mint initial, Mint ratio) {
using Fps = FormalPowerSeries<Mint>;
const int count = int(values.size());
if (count == 0) return {};
if (count == 1) return Fps(1, values[0]);
assert(initial != Mint(0));
assert(ratio != Mint(0));
std::vector<Mint> ratio_power(2 * count - 1, Mint(1));
std::vector<Mint> chirp(2 * count - 1, Mint(1));
for (int i = 0; i + 1 < int(ratio_power.size()); ++i) {
ratio_power[i + 1] = ratio_power[i] * ratio;
chirp[i + 1] = chirp[i] * ratio_power[i];
}
const std::vector<Mint> inverse_chirp =
internal::geometric_triangular_powers(ratio.inv(), count);
std::vector<Mint> difference_product(count, Mint(1));
for (int i = 1; i < count; ++i) {
assert(ratio_power[i] != Mint(1));
difference_product[i] =
difference_product[i - 1] * (Mint(1) - ratio_power[i]);
}
const std::vector<Mint> inverse_difference =
internal::geometric_batch_inverse(difference_product);
const Mint complete_product =
difference_product[count - 1] * (Mint(1) - ratio_power[count]);
std::vector<Mint> weighted = values;
for (int i = 0; i < count; ++i) {
weighted[i] *= chirp[count - 1 - i] * inverse_chirp[count - 1] *
inverse_difference[i] * inverse_difference[count - 1 - i];
if (i & 1) weighted[i] = Mint(0) - weighted[i];
weighted[i] *= inverse_chirp[i];
}
std::vector<Mint> coefficients =
internal::geometric_middle_product(chirp, weighted);
for (int i = 0; i < count; ++i) coefficients[i] *= inverse_chirp[i];
std::vector<Mint> product_polynomial(count);
product_polynomial[0] = Mint(1);
for (int i = 1; i < count; ++i) {
product_polynomial[i] = chirp[i] * complete_product * inverse_difference[i] *
inverse_difference[count - i];
if (i & 1) product_polynomial[i] = Mint(0) - product_polynomial[i];
}
coefficients = convolution(coefficients, product_polynomial);
coefficients.resize(count);
std::reverse(coefficients.begin(), coefficients.end());
const Mint inverse_initial = initial.inv();
Mint inverse_initial_power = Mint(1);
for (Mint& coefficient : coefficients) {
coefficient *= inverse_initial_power;
inverse_initial_power *= inverse_initial;
}
return Fps(std::move(coefficients));
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/half_gcd.hpp"
#line 7 "math/fps/half_gcd.hpp"
#line 9 "math/fps/half_gcd.hpp"
namespace m1une {
namespace fps {
template <class Mint>
struct PolynomialMatrix2x2 {
using Fps = FormalPowerSeries<Mint>;
Fps a00;
Fps a01;
Fps a10;
Fps a11;
static PolynomialMatrix2x2 identity() {
return PolynomialMatrix2x2{Fps(1, Mint(1)), Fps(), Fps(), Fps(1, Mint(1))};
}
std::pair<Fps, Fps> apply(const Fps& first, const Fps& second) const {
Fps result_first = a00 * first + a01 * second;
Fps result_second = a10 * first + a11 * second;
result_first.shrink();
result_second.shrink();
return std::make_pair(std::move(result_first), std::move(result_second));
}
friend PolynomialMatrix2x2 operator*(const PolynomialMatrix2x2& lhs,
const PolynomialMatrix2x2& rhs) {
PolynomialMatrix2x2 result;
result.a00 = lhs.a00 * rhs.a00 + lhs.a01 * rhs.a10;
result.a01 = lhs.a00 * rhs.a01 + lhs.a01 * rhs.a11;
result.a10 = lhs.a10 * rhs.a00 + lhs.a11 * rhs.a10;
result.a11 = lhs.a10 * rhs.a01 + lhs.a11 * rhs.a11;
result.a00.shrink();
result.a01.shrink();
result.a10.shrink();
result.a11.shrink();
return result;
}
};
namespace internal {
template <class Mint>
void polynomial_euclidean_step(PolynomialMatrix2x2<Mint>& matrix,
std::pair<FormalPowerSeries<Mint>,
FormalPowerSeries<Mint>>& values) {
using Fps = FormalPowerSeries<Mint>;
assert(!values.second.empty());
auto division = values.first.divmod(values.second);
Fps next_a10 = matrix.a00 - matrix.a10 * division.first;
Fps next_a11 = matrix.a01 - matrix.a11 * division.first;
next_a10.shrink();
next_a11.shrink();
matrix.a00 = std::move(matrix.a10);
matrix.a01 = std::move(matrix.a11);
matrix.a10 = std::move(next_a10);
matrix.a11 = std::move(next_a11);
values.first = std::move(values.second);
values.second = std::move(division.second);
}
template <class Mint>
PolynomialMatrix2x2<Mint> half_gcd_impl(
std::pair<FormalPowerSeries<Mint>, FormalPowerSeries<Mint>> values) {
using Matrix = PolynomialMatrix2x2<Mint>;
const int first_size = int(values.first.size());
const int half_size = (first_size + 1) / 2;
if (int(values.second.size()) <= half_size) return Matrix::identity();
Matrix first_matrix =
half_gcd_impl<Mint>(std::make_pair(values.first >> half_size, values.second >> half_size));
values = first_matrix.apply(values.first, values.second);
if (int(values.second.size()) <= half_size) return first_matrix;
polynomial_euclidean_step(first_matrix, values);
if (int(values.second.size()) <= half_size) return first_matrix;
const int first_degree = int(values.first.size()) - 1;
const int shift = 2 * half_size - first_degree;
assert(shift >= 0);
values.first >>= shift;
values.second >>= shift;
return half_gcd_impl<Mint>(std::move(values)) * first_matrix;
}
template <class Mint>
PolynomialMatrix2x2<Mint> polynomial_gcd_matrix(FormalPowerSeries<Mint> first,
FormalPowerSeries<Mint> second) {
using Matrix = PolynomialMatrix2x2<Mint>;
first.shrink();
second.shrink();
if (first.size() < second.size()) {
Matrix result = polynomial_gcd_matrix(std::move(second), std::move(first));
std::swap(result.a00, result.a01);
std::swap(result.a10, result.a11);
return result;
}
std::pair<FormalPowerSeries<Mint>, FormalPowerSeries<Mint>> values(
std::move(first), std::move(second));
Matrix result = Matrix::identity();
while (true) {
Matrix block = half_gcd_impl<Mint>(values);
values = block.apply(values.first, values.second);
if (values.second.empty()) return block * result;
polynomial_euclidean_step(block, values);
if (values.second.empty()) return block * result;
result = block * result;
}
}
} // namespace internal
template <class Mint>
PolynomialMatrix2x2<Mint> half_gcd(FormalPowerSeries<Mint> first,
FormalPowerSeries<Mint> second) {
first.shrink();
second.shrink();
assert(first.size() >= second.size());
return internal::half_gcd_impl<Mint>(std::make_pair(std::move(first), std::move(second)));
}
template <class Mint>
struct PolynomialExtendedGcdResult {
FormalPowerSeries<Mint> gcd;
FormalPowerSeries<Mint> x;
FormalPowerSeries<Mint> y;
};
template <class Mint>
PolynomialExtendedGcdResult<Mint> polynomial_extended_gcd(
const FormalPowerSeries<Mint>& first, const FormalPowerSeries<Mint>& second) {
PolynomialMatrix2x2<Mint> matrix = internal::polynomial_gcd_matrix(first, second);
auto values = matrix.apply(first, second);
if (!values.first.empty()) {
const Mint inverse_leading = values.first.back().inv();
values.first *= inverse_leading;
matrix.a00 *= inverse_leading;
matrix.a01 *= inverse_leading;
}
return PolynomialExtendedGcdResult<Mint>{std::move(values.first), std::move(matrix.a00),
std::move(matrix.a01)};
}
template <class Mint>
FormalPowerSeries<Mint> polynomial_gcd(const FormalPowerSeries<Mint>& first,
const FormalPowerSeries<Mint>& second) {
return polynomial_extended_gcd(first, second).gcd;
}
template <class Mint>
std::optional<FormalPowerSeries<Mint>> polynomial_inv_mod(
const FormalPowerSeries<Mint>& polynomial, FormalPowerSeries<Mint> modulus) {
modulus.shrink();
assert(!modulus.empty());
auto result = polynomial_extended_gcd(polynomial, modulus);
if (result.gcd.size() != 1) return std::nullopt;
return result.x % modulus;
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/lagrange_inversion.hpp"
#line 7 "math/fps/lagrange_inversion.hpp"
#line 9 "math/fps/lagrange_inversion.hpp"
namespace m1une {
namespace fps {
template <class Mint>
Mint lagrange_inversion_coefficient(const FormalPowerSeries<Mint>& phi, int degree) {
assert(1 <= degree && uint32_t(degree) < Mint::mod());
assert(!phi.empty() && phi[0] != Mint(0));
FormalPowerSeries<Mint> power = phi.pre(degree).pow(degree, degree);
return power[degree - 1] / Mint(degree);
}
template <class Mint>
Mint lagrange_burmann_coefficient(const FormalPowerSeries<Mint>& phi,
const FormalPowerSeries<Mint>& outer, int degree) {
assert(0 <= degree && uint32_t(degree) < Mint::mod());
if (degree == 0) return outer.empty() ? Mint(0) : outer[0];
assert(!phi.empty() && phi[0] != Mint(0));
FormalPowerSeries<Mint> power = phi.pre(degree).pow(degree, degree);
Mint result = 0;
int limit = std::min(degree, int(outer.size()) - 1);
for (int i = 1; i <= limit; i++) {
result += Mint(i) * outer[i] * power[degree - i];
}
return result / Mint(degree);
}
template <class Mint>
Mint compositional_inverse_coefficient(const FormalPowerSeries<Mint>& f, int degree) {
assert(0 <= degree && uint32_t(degree) < Mint::mod());
assert(2 <= int(f.size()) && f[0] == Mint(0) && f[1] != Mint(0));
if (degree == 0) return Mint(0);
FormalPowerSeries<Mint> divided(degree);
for (int i = 0; i < degree && i + 1 < int(f.size()); i++) divided[i] = f[i + 1];
FormalPowerSeries<Mint> phi = divided.inv(degree);
return lagrange_inversion_coefficient(phi, degree);
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/linear_recurrence.hpp"
#line 9 "math/fps/linear_recurrence.hpp"
#line 11 "math/fps/linear_recurrence.hpp"
namespace m1une {
namespace fps {
// Returns a shortest linear recurrence satisfied by the observed sequence.
// The returned coefficients use
// a[n] = recurrence[0] * a[n - 1] + ... + recurrence[d - 1] * a[n - d].
template <class Mint>
std::vector<Mint> berlekamp_massey(const std::vector<Mint>& sequence) {
std::vector<Mint> connection(1, Mint(1));
std::vector<Mint> previous(1, Mint(1));
int order = 0;
int shift = 1;
Mint previous_discrepancy = Mint(1);
for (int index = 0; index < int(sequence.size()); index++) {
Mint discrepancy = sequence[index];
for (int i = 1; i <= order; i++) {
discrepancy += connection[i] * sequence[index - i];
}
if (discrepancy == Mint(0)) {
shift++;
continue;
}
const Mint scale = discrepancy / previous_discrepancy;
std::vector<Mint> old_connection = connection;
if (connection.size() < previous.size() + std::size_t(shift)) {
connection.resize(previous.size() + std::size_t(shift));
}
for (int i = 0; i < int(previous.size()); i++) {
connection[i + shift] -= scale * previous[i];
}
if (2 * order <= index) {
order = index + 1 - order;
previous = std::move(old_connection);
previous_discrepancy = discrepancy;
shift = 1;
} else {
shift++;
}
}
std::vector<Mint> recurrence(order);
for (int i = 0; i < order; i++) recurrence[i] = Mint(0) - connection[i + 1];
return recurrence;
}
template <class Mint>
Mint coefficient_of_rational(FormalPowerSeries<Mint> numerator,
FormalPowerSeries<Mint> denominator, uint64_t index) {
using Fps = FormalPowerSeries<Mint>;
assert(!denominator.empty() && denominator[0] != Mint(0));
while (index > 0) {
Fps denominator_negative = denominator;
for (int i = 1; i < int(denominator_negative.size()); i += 2) {
denominator_negative[i] = Mint(0) - denominator_negative[i];
}
Fps numerator_product = numerator * denominator_negative;
Fps denominator_product = denominator * denominator_negative;
Fps next_numerator;
Fps next_denominator;
next_numerator.reserve((numerator_product.size() + 1) / 2);
next_denominator.reserve((denominator_product.size() + 1) / 2);
for (int i = int(index & 1); i < int(numerator_product.size()); i += 2) {
next_numerator.emplace_back(numerator_product[i]);
}
for (int i = 0; i < int(denominator_product.size()); i += 2) {
next_denominator.emplace_back(denominator_product[i]);
}
numerator = std::move(next_numerator);
denominator = std::move(next_denominator);
index >>= 1;
}
return numerator.empty() ? Mint(0) : numerator[0] / denominator[0];
}
template <class Mint>
Mint linear_recurrence_kth(const std::vector<Mint>& initial,
const std::vector<Mint>& recurrence, uint64_t index) {
using Fps = FormalPowerSeries<Mint>;
assert(!initial.empty() && initial.size() == recurrence.size());
if (index < initial.size()) return initial[index];
const int order = int(recurrence.size());
Fps denominator(order + 1);
denominator[0] = 1;
for (int i = 0; i < order; i++) denominator[i + 1] = Mint(0) - recurrence[i];
Fps numerator = (Fps(initial) * denominator).pre(order);
return coefficient_of_rational(std::move(numerator), std::move(denominator), index);
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/multipoint_evaluation.hpp"
#line 6 "math/fps/multipoint_evaluation.hpp"
#line 8 "math/fps/multipoint_evaluation.hpp"
namespace m1une {
namespace fps {
template <class Mint>
struct SubproductTree {
using Fps = FormalPowerSeries<Mint>;
private:
int point_count_;
int leaf_count_;
std::vector<Mint> points_;
std::vector<Fps> product_;
public:
explicit SubproductTree(const std::vector<Mint>& points)
: point_count_(points.size()), leaf_count_(1), points_(points) {
while (leaf_count_ < point_count_) leaf_count_ <<= 1;
product_.resize(leaf_count_ << 1);
for (int i = 0; i < leaf_count_; i++) {
if (i < point_count_) {
product_[leaf_count_ + i].resize(2);
product_[leaf_count_ + i][0] = Mint(0) - points_[i];
product_[leaf_count_ + i][1] = Mint(1);
} else {
product_[leaf_count_ + i].resize(1, Mint(1));
}
}
for (int node = leaf_count_ - 1; node > 0; node--) {
product_[node] = product_[node << 1] * product_[node << 1 | 1];
}
}
int size() const {
return point_count_;
}
const Fps& product() const {
return product_[1];
}
std::vector<Mint> evaluate(const Fps& polynomial) const {
std::vector<Mint> result(point_count_);
if (point_count_ == 0) return result;
std::vector<Fps> remainder(leaf_count_ << 1);
remainder[1] = polynomial % product_[1];
for (int node = 1; node < leaf_count_; node++) {
remainder[node << 1] = remainder[node] % product_[node << 1];
remainder[node << 1 | 1] = remainder[node] % product_[node << 1 | 1];
}
for (int i = 0; i < point_count_; i++) {
const Fps& value = remainder[leaf_count_ + i];
result[i] = value.empty() ? Mint(0) : value[0];
}
return result;
}
Fps interpolate(const std::vector<Mint>& values) const {
assert(int(values.size()) == point_count_);
if (point_count_ == 0) return {};
std::vector<Mint> derivative_values = evaluate(product_[1].derivative());
std::vector<Fps> polynomial(leaf_count_ << 1);
for (int i = 0; i < leaf_count_; i++) {
polynomial[leaf_count_ + i].resize(1);
if (i < point_count_) {
assert(derivative_values[i] != Mint(0));
polynomial[leaf_count_ + i][0] = values[i] / derivative_values[i];
}
}
for (int node = leaf_count_ - 1; node > 0; node--) {
polynomial[node] = polynomial[node << 1] * product_[node << 1 | 1] +
polynomial[node << 1 | 1] * product_[node << 1];
}
polynomial[1].resize(point_count_);
return polynomial[1];
}
};
template <class Mint>
std::vector<Mint> multipoint_evaluate(const FormalPowerSeries<Mint>& polynomial,
const std::vector<Mint>& points) {
return SubproductTree<Mint>(points).evaluate(polynomial);
}
template <class Mint>
FormalPowerSeries<Mint> polynomial_interpolate(const std::vector<Mint>& points,
const std::vector<Mint>& values) {
return SubproductTree<Mint>(points).interpolate(values);
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/polynomial_factorization.hpp"
#line 9 "math/fps/polynomial_factorization.hpp"
#line 11 "math/fps/polynomial_factorization.hpp"
namespace m1une {
namespace fps {
template <class Mint>
struct PolynomialFactor {
FormalPowerSeries<Mint> polynomial;
int multiplicity;
};
template <class Mint>
struct PolynomialFactorization {
Mint leading_coefficient;
std::vector<PolynomialFactor<Mint>> factors;
};
namespace internal {
template <class Mint>
FormalPowerSeries<Mint> factorization_monic(FormalPowerSeries<Mint> polynomial) {
polynomial.shrink();
if (polynomial.empty()) return polynomial;
polynomial /= polynomial.back();
return polynomial;
}
template <class Mint>
FormalPowerSeries<Mint> factorization_multiply(
const FormalPowerSeries<Mint>& first,
const FormalPowerSeries<Mint>& second
) {
if (first.empty() || second.empty()) return {};
FormalPowerSeries<Mint> result(first.size() + second.size() - 1);
for (int i = 0; i < int(first.size()); i++) {
for (int j = 0; j < int(second.size()); j++) {
result[i + j] += first[i] * second[j];
}
}
result.shrink();
return result;
}
template <class Mint>
std::pair<FormalPowerSeries<Mint>, FormalPowerSeries<Mint>> factorization_divmod(
FormalPowerSeries<Mint> dividend,
FormalPowerSeries<Mint> divisor
) {
dividend.shrink();
divisor.shrink();
assert(!divisor.empty());
if (dividend.size() < divisor.size()) return std::make_pair(FormalPowerSeries<Mint>(), dividend);
FormalPowerSeries<Mint> quotient(dividend.size() - divisor.size() + 1);
const Mint inverse_leading = divisor.back().inv();
for (int i = int(dividend.size()) - 1; i >= int(divisor.size()) - 1; i--) {
const Mint coefficient = dividend[i] * inverse_leading;
const int shift = i - int(divisor.size()) + 1;
quotient[shift] = coefficient;
if (coefficient == Mint(0)) continue;
for (int j = 0; j < int(divisor.size()); j++) {
dividend[shift + j] -= coefficient * divisor[j];
}
}
quotient.shrink();
dividend.resize(divisor.size() - 1);
dividend.shrink();
return std::make_pair(std::move(quotient), std::move(dividend));
}
template <class Mint>
FormalPowerSeries<Mint> factorization_gcd(
FormalPowerSeries<Mint> first,
FormalPowerSeries<Mint> second
) {
first.shrink();
second.shrink();
while (!second.empty()) {
FormalPowerSeries<Mint> remainder =
factorization_divmod(std::move(first), second).second;
first = std::move(second);
second = std::move(remainder);
}
return factorization_monic(std::move(first));
}
template <class Mint>
FormalPowerSeries<Mint> factorization_multiply_mod(
const FormalPowerSeries<Mint>& first,
const FormalPowerSeries<Mint>& second,
const FormalPowerSeries<Mint>& modulus
) {
return factorization_divmod(factorization_multiply(first, second), modulus).second;
}
template <class Mint>
FormalPowerSeries<Mint> factorization_power_mod(
FormalPowerSeries<Mint> base,
uint64_t exponent,
const FormalPowerSeries<Mint>& modulus
) {
FormalPowerSeries<Mint> result(1, Mint(1));
result = factorization_divmod(std::move(result), modulus).second;
base = factorization_divmod(std::move(base), modulus).second;
while (exponent > 0) {
if (exponent & 1) result = factorization_multiply_mod(result, base, modulus);
exponent >>= 1;
if (exponent > 0) base = factorization_multiply_mod(base, base, modulus);
}
return result;
}
struct PolynomialFactorizationRandom {
uint64_t state;
uint64_t operator()() {
state ^= state << 7;
state ^= state >> 9;
return state;
}
};
template <class Mint>
FormalPowerSeries<Mint> factorization_random_polynomial(
int coefficient_count,
PolynomialFactorizationRandom& random
) {
FormalPowerSeries<Mint> result(coefficient_count);
const uint64_t mod = Mint::mod();
for (Mint& coefficient : result) coefficient = Mint(random() % mod);
result.shrink();
return result;
}
template <class Mint>
FormalPowerSeries<Mint> equal_degree_separator_odd(
const FormalPowerSeries<Mint>& random_polynomial,
int factor_degree,
const FormalPowerSeries<Mint>& modulus
) {
FormalPowerSeries<Mint> power = factorization_power_mod(
random_polynomial, (uint64_t(Mint::mod()) - 1) / 2, modulus);
FormalPowerSeries<Mint> result(1, Mint(1));
for (int i = 0; i < factor_degree; i++) {
result = factorization_multiply_mod(result, power, modulus);
if (i + 1 < factor_degree) {
power = factorization_power_mod(power, Mint::mod(), modulus);
}
}
if (result.empty()) result.resize(1);
result[0] -= Mint(1);
result.shrink();
return result;
}
template <class Mint>
FormalPowerSeries<Mint> equal_degree_separator_two(
const FormalPowerSeries<Mint>& random_polynomial,
int factor_degree,
const FormalPowerSeries<Mint>& modulus
) {
FormalPowerSeries<Mint> power =
factorization_divmod(random_polynomial, modulus).second;
FormalPowerSeries<Mint> result;
for (int i = 0; i < factor_degree; i++) {
result += power;
result.shrink();
if (i + 1 < factor_degree) {
power = factorization_multiply_mod(power, power, modulus);
}
}
return result;
}
template <class Mint>
std::vector<FormalPowerSeries<Mint>> equal_degree_factorization(
FormalPowerSeries<Mint> polynomial,
int factor_degree,
PolynomialFactorizationRandom& random
) {
using Fps = FormalPowerSeries<Mint>;
polynomial = factorization_monic(std::move(polynomial));
std::vector<Fps> queue(1, std::move(polynomial));
std::vector<Fps> result;
for (int head = 0; head < int(queue.size()); head++) {
Fps current = factorization_monic(std::move(queue[head]));
const int degree = int(current.size()) - 1;
if (degree == factor_degree) {
result.push_back(std::move(current));
continue;
}
Fps divisor;
do {
Fps random_polynomial =
factorization_random_polynomial<Mint>(degree, random);
Fps separator = Mint::mod() == 2
? equal_degree_separator_two(random_polynomial, factor_degree, current)
: equal_degree_separator_odd(random_polynomial, factor_degree, current);
divisor = factorization_gcd(current, std::move(separator));
} while (divisor.size() <= 1 || divisor.size() == current.size());
Fps quotient = factorization_divmod(current, divisor).first;
queue.push_back(std::move(divisor));
queue.push_back(std::move(quotient));
}
return result;
}
} // namespace internal
template <class Mint>
PolynomialFactorization<Mint> polynomial_factorize(
FormalPowerSeries<Mint> polynomial,
uint64_t seed = 88172645463325252ULL
) {
using Fps = FormalPowerSeries<Mint>;
polynomial.shrink();
assert(!polynomial.empty());
const Mint leading_coefficient = polynomial.back();
polynomial /= leading_coefficient;
std::vector<PolynomialFactor<Mint>> factors;
if (polynomial.size() == 1) return {leading_coefficient, std::move(factors)};
if (seed == 0) seed = 88172645463325252ULL;
internal::PolynomialFactorizationRandom random{seed};
const Fps x = Fps({Mint(0), Mint(1)});
Fps frobenius = x;
for (int degree = 1;
polynomial.size() > 1 && 2 * degree <= int(polynomial.size()) - 1;
degree++) {
frobenius = internal::factorization_power_mod(
std::move(frobenius), Mint::mod(), polynomial);
Fps difference = frobenius - x;
difference.shrink();
Fps product = internal::factorization_gcd(polynomial, std::move(difference));
if (product.size() <= 1) continue;
std::vector<Fps> distinct = internal::equal_degree_factorization(
std::move(product), degree, random);
for (Fps& factor : distinct) {
int multiplicity = 0;
while (factor.size() <= polynomial.size()) {
auto division = internal::factorization_divmod(polynomial, factor);
if (!division.second.empty()) break;
polynomial = std::move(division.first);
multiplicity++;
}
assert(multiplicity > 0);
factors.push_back({std::move(factor), multiplicity});
}
}
if (polynomial.size() > 1) {
factors.push_back({internal::factorization_monic(std::move(polynomial)), 1});
}
auto less = [](const PolynomialFactor<Mint>& first,
const PolynomialFactor<Mint>& second) {
if (first.polynomial.size() != second.polynomial.size()) {
return first.polynomial.size() < second.polynomial.size();
}
for (int i = int(first.polynomial.size()) - 1; i >= 0; i--) {
if (first.polynomial[i].val() != second.polynomial[i].val()) {
return first.polynomial[i].val() < second.polynomial[i].val();
}
}
return false;
};
std::sort(factors.begin(), factors.end(), less);
std::vector<PolynomialFactor<Mint>> merged;
for (PolynomialFactor<Mint>& factor : factors) {
if (!merged.empty() && merged.back().polynomial == factor.polynomial) {
merged.back().multiplicity += factor.multiplicity;
} else {
merged.push_back(std::move(factor));
}
}
return {leading_coefficient, std::move(merged)};
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/polynomial_roots.hpp"
#line 9 "math/fps/polynomial_roots.hpp"
#line 11 "math/fps/polynomial_roots.hpp"
namespace m1une {
namespace fps {
namespace internal {
struct PolynomialRootsRandom {
uint64_t state;
uint64_t operator()() {
state ^= state << 7;
state ^= state >> 9;
return state;
}
};
template <class Mint>
FormalPowerSeries<Mint> polynomial_roots_power_mod(
FormalPowerSeries<Mint> base, uint64_t exponent,
const FormalPowerSeries<Mint>& modulus) {
using Fps = FormalPowerSeries<Mint>;
Fps result(1, Mint(1));
result %= modulus;
base %= modulus;
while (exponent > 0) {
if (exponent & 1) result = (result * base) % modulus;
exponent >>= 1;
if (exponent > 0) base = (base * base) % modulus;
}
return result;
}
} // namespace internal
template <class Mint>
std::vector<Mint> polynomial_roots(
FormalPowerSeries<Mint> polynomial,
uint64_t seed = 88172645463325252ULL) {
using Fps = FormalPowerSeries<Mint>;
polynomial.shrink();
assert(!polynomial.empty());
if (polynomial.size() == 1) return {};
const uint64_t modulus = Mint::mod();
if (modulus == 2) {
std::vector<Mint> result;
if (polynomial.evaluate(Mint(0)) == Mint(0)) result.push_back(Mint(0));
if (polynomial.evaluate(Mint(1)) == Mint(0)) result.push_back(Mint(1));
return result;
}
assert(modulus & 1);
Fps x(2);
x[1] = Mint(1);
Fps frobenius = internal::polynomial_roots_power_mod(x, modulus, polynomial);
frobenius -= x;
frobenius.shrink();
Fps linear_part = polynomial_gcd(polynomial, frobenius);
if (linear_part.size() <= 1) return {};
if (seed == 0) seed = 88172645463325252ULL;
internal::PolynomialRootsRandom random{seed};
std::vector<Mint> result;
auto split = [&](auto&& self, Fps current) -> void {
current.shrink();
if (current.size() <= 1) return;
if (current.size() == 2) {
result.push_back((Mint(0) - current[0]) / current[1]);
return;
}
Fps divisor;
do {
Fps shifted_x(2);
shifted_x[0] = Mint(random() % modulus);
shifted_x[1] = Mint(1);
Fps separator = internal::polynomial_roots_power_mod(
std::move(shifted_x), (modulus - 1) / 2, current);
if (separator.empty()) separator.resize(1);
separator[0] -= Mint(1);
separator.shrink();
divisor = polynomial_gcd(current, separator);
} while (divisor.size() <= 1 || divisor.size() == current.size());
auto division = current.divmod(divisor);
assert(division.second.empty());
self(self, std::move(divisor));
self(self, std::move(division.first));
};
split(split, std::move(linear_part));
std::sort(result.begin(), result.end(), [](Mint lhs, Mint rhs) {
return lhs.val() < rhs.val();
});
result.erase(std::unique(result.begin(), result.end()), result.end());
return result;
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/solve_fps_equation.hpp"
#line 6 "math/fps/solve_fps_equation.hpp"
#line 1 "math/newton_method.hpp"
#line 5 "math/newton_method.hpp"
#line 7 "math/newton_method.hpp"
namespace m1une {
namespace math {
namespace newton_method_detail {
template <class T>
struct DefaultQuotient {
template <class Numerator, class Denominator>
auto operator()(
const Numerator& numerator,
const Denominator& denominator
) const {
return numerator / denominator;
}
};
template <class Mint>
struct DefaultQuotient<fps::FormalPowerSeries<Mint>> {
using Fps = fps::FormalPowerSeries<Mint>;
int degree;
Fps operator()(const Fps& numerator, const Fps& denominator) const {
return (numerator.pre(degree) * denominator.inv(degree)).pre(degree);
}
};
template <class T>
DefaultQuotient<T> make_default_quotient(const T&) {
return {};
}
template <class Mint>
DefaultQuotient<fps::FormalPowerSeries<Mint>> make_default_quotient(
const fps::FormalPowerSeries<Mint>& value
) {
return {int(value.size())};
}
} // namespace newton_method_detail
template <class T, class F, class Derivative, class Quotient>
T newton_method(
T initial,
F function,
Derivative derivative,
int iterations,
Quotient quotient
) {
assert(iterations >= 0);
for (int iteration = 0; iteration < iterations; iteration++) {
auto numerator = function(initial);
auto denominator = derivative(initial);
initial -= quotient(numerator, denominator);
}
return initial;
}
template <class T, class F, class Derivative>
T newton_method(
T initial,
F function,
Derivative derivative,
int iterations
) {
auto quotient = newton_method_detail::make_default_quotient(initial);
return newton_method(initial, function, derivative, iterations, quotient);
}
} // namespace math
} // namespace m1une
#line 8 "math/fps/solve_fps_equation.hpp"
namespace m1une {
namespace fps {
// Extends a solution modulo x^initial.size() to a solution modulo x^degree.
// Both callbacks receive the precision currently requested by Newton lifting.
template <class Mint, class Function, class Derivative>
FormalPowerSeries<Mint> solve_fps_equation(
FormalPowerSeries<Mint> initial,
int degree,
Function function,
Derivative derivative
) {
using Fps = FormalPowerSeries<Mint>;
assert(degree >= 0);
if (degree == 0) return {};
assert(!initial.empty());
if (int(initial.size()) >= degree) return initial.pre(degree);
while (int(initial.size()) < degree) {
const int next_degree = std::min(int(initial.size()) << 1, degree);
initial.resize(next_degree);
auto truncated_function = [&](const Fps& value) {
return function(value, next_degree).pre(next_degree);
};
auto truncated_derivative = [&](const Fps& value) {
return derivative(value, next_degree).pre(next_degree);
};
initial = math::newton_method(
initial, truncated_function, truncated_derivative, 1
);
}
return initial;
}
// Starts Newton lifting from a solution modulo x.
template <class Mint, class Function, class Derivative>
FormalPowerSeries<Mint> solve_fps_equation(
int degree,
Mint constant_solution,
Function function,
Derivative derivative
) {
assert(degree >= 0);
if (degree == 0) return {};
return solve_fps_equation(
FormalPowerSeries<Mint>(1, constant_solution),
degree,
function,
derivative
);
}
} // namespace fps
} // namespace m1une
#line 1 "math/fps/sparse_formal_power_series.hpp"
#line 9 "math/fps/sparse_formal_power_series.hpp"
#line 11 "math/fps/sparse_formal_power_series.hpp"
namespace m1une {
namespace fps {
template <class Mint>
using SparseFormalPowerSeries = std::vector<std::pair<int, Mint>>;
namespace internal {
template <class Mint>
void assert_valid_sparse_fps(const SparseFormalPowerSeries<Mint>& terms, int degree) {
int previous_degree = -1;
for (const auto& [term_degree, coefficient] : terms) {
assert(0 <= term_degree && previous_degree < term_degree && term_degree < degree);
assert(coefficient != Mint(0));
previous_degree = term_degree;
}
}
template <class Mint>
std::vector<Mint> sparse_integer_inverses(int degree) {
assert(degree >= 0 && uint32_t(degree) < Mint::mod());
std::vector<Mint> inverse(degree);
if (degree <= 1) return inverse;
inverse[1] = Mint(1);
for (int i = 2; i < degree; i++) {
const uint32_t quotient = Mint::mod() / uint32_t(i);
const uint32_t remainder = Mint::mod() % uint32_t(i);
inverse[i] = Mint(0) - Mint(quotient) * inverse[remainder];
}
return inverse;
}
template <class Mint>
FormalPowerSeries<Mint> sparse_unit_pow(const SparseFormalPowerSeries<Mint>& terms,
Mint exponent, int degree) {
assert(degree > 0 && !terms.empty());
assert(terms[0].first == 0 && terms[0].second == Mint(1));
std::vector<Mint> inverse = sparse_integer_inverses<Mint>(degree);
FormalPowerSeries<Mint> result(degree);
result[0] = Mint(1);
for (int n = 1; n < degree; n++) {
Mint coefficient = 0;
for (int i = 1; i < int(terms.size()) && terms[i].first <= n; i++) {
const auto& [d, value] = terms[i];
coefficient += value * result[n - d] *
(exponent * Mint(d) - Mint(n - d));
}
result[n] = coefficient * inverse[n];
}
return result;
}
} // namespace internal
template <class Mint>
FormalPowerSeries<Mint> sparse_fps_inv(const SparseFormalPowerSeries<Mint>& terms,
int degree) {
assert(degree >= 0);
if (degree == 0) return {};
internal::assert_valid_sparse_fps(terms, degree);
assert(!terms.empty() && terms[0].first == 0);
const Mint inverse_constant = terms[0].second.inv();
FormalPowerSeries<Mint> result(degree);
result[0] = inverse_constant;
for (int n = 1; n < degree; n++) {
Mint coefficient = 0;
for (int i = 1; i < int(terms.size()) && terms[i].first <= n; i++) {
coefficient -= terms[i].second * result[n - terms[i].first];
}
result[n] = coefficient * inverse_constant;
}
return result;
}
template <class Mint>
FormalPowerSeries<Mint> sparse_fps_log(const SparseFormalPowerSeries<Mint>& terms,
int degree) {
assert(degree >= 0);
if (degree == 0) return {};
assert(uint32_t(degree) < Mint::mod());
internal::assert_valid_sparse_fps(terms, degree);
assert(!terms.empty() && terms[0].first == 0 && terms[0].second == Mint(1));
std::vector<Mint> inverse = internal::sparse_integer_inverses<Mint>(degree);
FormalPowerSeries<Mint> result(degree);
for (int n = 0; n + 1 < degree; n++) {
Mint derivative_quotient = 0;
for (int i = 1; i < int(terms.size()) && terms[i].first <= n + 1; i++) {
const auto& [d, value] = terms[i];
if (d == n + 1) derivative_quotient += Mint(d) * value;
if (d <= n) {
derivative_quotient -=
value * Mint(n - d + 1) * result[n - d + 1];
}
}
result[n + 1] = derivative_quotient * inverse[n + 1];
}
return result;
}
template <class Mint>
FormalPowerSeries<Mint> sparse_fps_exp(const SparseFormalPowerSeries<Mint>& terms,
int degree) {
assert(degree >= 0);
if (degree == 0) return {};
assert(uint32_t(degree) < Mint::mod());
internal::assert_valid_sparse_fps(terms, degree);
assert(terms.empty() || terms[0].first > 0);
std::vector<Mint> inverse = internal::sparse_integer_inverses<Mint>(degree);
FormalPowerSeries<Mint> result(degree);
result[0] = Mint(1);
for (int n = 1; n < degree; n++) {
Mint coefficient = 0;
for (const auto& [d, value] : terms) {
if (d > n) break;
coefficient += Mint(d) * value * result[n - d];
}
result[n] = coefficient * inverse[n];
}
return result;
}
template <class Mint>
FormalPowerSeries<Mint> sparse_fps_pow(const SparseFormalPowerSeries<Mint>& terms,
long long exponent, int degree) {
assert(exponent >= 0 && degree >= 0);
if (degree == 0) return {};
assert(uint32_t(degree) < Mint::mod());
internal::assert_valid_sparse_fps(terms, degree);
if (exponent == 0) {
FormalPowerSeries<Mint> result(degree);
result[0] = Mint(1);
return result;
}
if (terms.empty()) return FormalPowerSeries<Mint>(degree);
const int leading_degree = terms[0].first;
if (leading_degree > 0 && exponent > (degree - 1) / leading_degree) {
return FormalPowerSeries<Mint>(degree);
}
const int offset = int(leading_degree * exponent);
const int normalized_degree = degree - offset;
const Mint leading = terms[0].second;
const Mint inverse_leading = leading.inv();
SparseFormalPowerSeries<Mint> normalized;
normalized.reserve(terms.size());
for (const auto& [d, value] : terms) {
if (d - leading_degree >= normalized_degree) break;
normalized.emplace_back(d - leading_degree, value * inverse_leading);
}
FormalPowerSeries<Mint> unit =
internal::sparse_unit_pow(normalized, Mint(exponent), normalized_degree);
const Mint scale = leading.pow(exponent);
FormalPowerSeries<Mint> result(degree);
for (int i = 0; i < normalized_degree; i++) result[offset + i] = unit[i] * scale;
return result;
}
template <class Mint>
std::optional<FormalPowerSeries<Mint>> sparse_fps_sqrt(
const SparseFormalPowerSeries<Mint>& terms, int degree) {
assert(degree >= 0);
if (degree == 0) return FormalPowerSeries<Mint>();
assert(uint32_t(degree) < Mint::mod());
internal::assert_valid_sparse_fps(terms, degree);
assert(Mint(2) != Mint(0));
if (terms.empty()) return FormalPowerSeries<Mint>(degree);
const int leading_degree = terms[0].first;
if (leading_degree & 1) return std::nullopt;
auto leading_root = m1une::math::modular_square_root(terms[0].second);
if (!leading_root.has_value()) return std::nullopt;
const int normalized_degree = degree - leading_degree;
const Mint inverse_leading = terms[0].second.inv();
SparseFormalPowerSeries<Mint> normalized;
normalized.reserve(terms.size());
for (const auto& [d, value] : terms) {
normalized.emplace_back(d - leading_degree, value * inverse_leading);
}
FormalPowerSeries<Mint> unit = internal::sparse_unit_pow(
normalized, Mint(2).inv(), normalized_degree);
FormalPowerSeries<Mint> result(degree);
const int offset = leading_degree / 2;
for (int i = 0; i < normalized_degree; i++) {
result[offset + i] = unit[i] * *leading_root;
}
return result;
}
} // namespace fps
} // namespace m1une
#line 19 "math/fps/all.hpp"
#line 1 "math/matrix/all.hpp"
#line 1 "math/matrix/adjugate.hpp"
#line 6 "math/matrix/adjugate.hpp"
#line 1 "math/matrix/matrix.hpp"
#line 9 "math/matrix/matrix.hpp"
namespace m1une {
namespace matrix {
template <class T>
class Matrix {
private:
int _rows;
int _cols;
std::vector<T> _data;
static std::size_t storage_size(int rows, int cols) {
assert(rows >= 0);
assert(cols >= 0);
return std::size_t(rows) * std::size_t(cols);
}
public:
using value_type = T;
Matrix() : _rows(0), _cols(0) {}
Matrix(int rows, int cols, const T& value = T())
: _rows(rows), _cols(cols), _data(storage_size(rows, cols), value) {}
Matrix(int rows, int cols, std::vector<T> values)
: _rows(rows), _cols(cols), _data(std::move(values)) {
assert(rows >= 0);
assert(cols >= 0);
assert(_data.size() == std::size_t(rows) * std::size_t(cols));
}
explicit Matrix(const std::vector<std::vector<T>>& values)
: _rows(int(values.size())), _cols(values.empty() ? 0 : int(values[0].size())),
_data(storage_size(_rows, _cols)) {
for (int row = 0; row < _rows; row++) {
assert(int(values[std::size_t(row)].size()) == _cols);
for (int col = 0; col < _cols; col++) {
(*this)[row][col] = values[std::size_t(row)][std::size_t(col)];
}
}
}
int rows() const {
return _rows;
}
int cols() const {
return _cols;
}
bool empty() const {
return _rows == 0 || _cols == 0;
}
std::vector<T>& data() {
return _data;
}
const std::vector<T>& data() const {
return _data;
}
T* operator[](int row) {
assert(0 <= row && row < _rows);
return _data.data() + std::size_t(row) * std::size_t(_cols);
}
const T* operator[](int row) const {
assert(0 <= row && row < _rows);
return _data.data() + std::size_t(row) * std::size_t(_cols);
}
T& operator()(int row, int col) {
assert(0 <= col && col < _cols);
return (*this)[row][col];
}
const T& operator()(int row, int col) const {
assert(0 <= col && col < _cols);
return (*this)[row][col];
}
static Matrix identity(int size) {
assert(size >= 0);
Matrix result(size, size);
for (int i = 0; i < size; i++) result[i][i] = T(1);
return result;
}
Matrix transposed() const {
Matrix result(_cols, _rows);
for (int row = 0; row < _rows; row++) {
for (int col = 0; col < _cols; col++) {
result[col][row] = (*this)[row][col];
}
}
return result;
}
void swap_rows(int first, int second) {
assert(0 <= first && first < _rows);
assert(0 <= second && second < _rows);
if (first == second) return;
for (int col = 0; col < _cols; col++) {
std::swap((*this)[first][col], (*this)[second][col]);
}
}
Matrix& operator+=(const Matrix& rhs) {
assert(_rows == rhs._rows && _cols == rhs._cols);
for (std::size_t i = 0; i < _data.size(); i++) _data[i] += rhs._data[i];
return *this;
}
Matrix& operator-=(const Matrix& rhs) {
assert(_rows == rhs._rows && _cols == rhs._cols);
for (std::size_t i = 0; i < _data.size(); i++) _data[i] -= rhs._data[i];
return *this;
}
Matrix& operator*=(const T& scalar) {
for (T& value : _data) value *= scalar;
return *this;
}
Matrix& operator/=(const T& scalar) {
for (T& value : _data) value /= scalar;
return *this;
}
Matrix& operator*=(const Matrix& rhs) {
return *this = *this * rhs;
}
Matrix operator+() const {
return *this;
}
Matrix operator-() const {
Matrix result = *this;
for (T& value : result._data) value = T() - value;
return result;
}
friend Matrix operator+(Matrix lhs, const Matrix& rhs) {
return lhs += rhs;
}
friend Matrix operator-(Matrix lhs, const Matrix& rhs) {
return lhs -= rhs;
}
friend Matrix operator*(Matrix lhs, const T& rhs) {
return lhs *= rhs;
}
friend Matrix operator*(const T& lhs, Matrix rhs) {
return rhs *= lhs;
}
friend Matrix operator/(Matrix lhs, const T& rhs) {
return lhs /= rhs;
}
friend Matrix operator*(const Matrix& lhs, const Matrix& rhs) {
assert(lhs._cols == rhs._rows);
Matrix result(lhs._rows, rhs._cols);
for (int row = 0; row < lhs._rows; row++) {
T* output = result[row];
for (int middle = 0; middle < lhs._cols; middle++) {
const T coefficient = lhs[row][middle];
if (coefficient == T()) continue;
const T* input = rhs[middle];
for (int col = 0; col < rhs._cols; col++) {
output[col] += coefficient * input[col];
}
}
}
return result;
}
friend std::vector<T> operator*(const Matrix& lhs, const std::vector<T>& rhs) {
assert(lhs._cols == int(rhs.size()));
std::vector<T> result(std::size_t(lhs._rows));
for (int row = 0; row < lhs._rows; row++) {
T value = T();
for (int col = 0; col < lhs._cols; col++) {
value += lhs[row][col] * rhs[std::size_t(col)];
}
result[std::size_t(row)] = value;
}
return result;
}
friend std::vector<T> operator*(const std::vector<T>& lhs, const Matrix& rhs) {
assert(int(lhs.size()) == rhs._rows);
std::vector<T> result(std::size_t(rhs._cols));
for (int row = 0; row < rhs._rows; row++) {
if (lhs[std::size_t(row)] == T()) continue;
for (int col = 0; col < rhs._cols; col++) {
result[std::size_t(col)] += lhs[std::size_t(row)] * rhs[row][col];
}
}
return result;
}
bool operator==(const Matrix& rhs) const {
return _rows == rhs._rows && _cols == rhs._cols && _data == rhs._data;
}
bool operator!=(const Matrix& rhs) const {
return !(*this == rhs);
}
Matrix pow(std::uint64_t exponent) const {
assert(_rows == _cols);
Matrix result = identity(_rows);
Matrix base = *this;
while (exponent > 0) {
if (exponent & 1) result *= base;
exponent >>= 1;
if (exponent > 0) base *= base;
}
return result;
}
};
} // namespace matrix
} // namespace m1une
#line 8 "math/matrix/adjugate.hpp"
namespace m1une {
namespace matrix {
template <class T>
Matrix<T> adjugate(Matrix<T> matrix) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
Matrix<T> augmented(size, size * 2);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
augmented[row][col] = matrix[row][col];
}
augmented[row][size + row] = T(1);
}
std::vector<int> pivot_columns;
T pivot_product = T(1);
bool negate = false;
for (int col = 0; col < size && int(pivot_columns.size()) < size; col++) {
const int pivot_row = int(pivot_columns.size());
int pivot = pivot_row;
while (pivot < size && augmented[pivot][col] == T()) pivot++;
if (pivot == size) continue;
if (pivot != pivot_row) {
augmented.swap_rows(pivot, pivot_row);
negate = !negate;
}
const T pivot_value = augmented[pivot_row][col];
pivot_product *= pivot_value;
const T inverse_pivot = T(1) / pivot_value;
for (int index = col; index < size; index++) {
augmented[pivot_row][index] *= inverse_pivot;
}
for (int index = size; index < size * 2; index++) {
augmented[pivot_row][index] *= inverse_pivot;
}
for (int row = 0; row < size; row++) {
if (row == pivot_row || augmented[row][col] == T()) continue;
const T factor = augmented[row][col];
augmented[row][col] = T();
for (int index = col + 1; index < size; index++) {
augmented[row][index] -= factor * augmented[pivot_row][index];
}
for (int index = size; index < size * 2; index++) {
augmented[row][index] -= factor * augmented[pivot_row][index];
}
}
pivot_columns.push_back(col);
}
const int rank = int(pivot_columns.size());
Matrix<T> result(size, size);
if (rank + 1 < size) return result;
if (rank == size) {
const T determinant = negate ? T() - pivot_product : pivot_product;
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
result[row][col] = determinant * augmented[row][size + col];
}
}
return result;
}
int free_column = 0;
while (free_column < rank && pivot_columns[free_column] == free_column) {
free_column++;
}
std::vector<T> right_null(size);
right_null[free_column] = T(1);
for (int row = 0; row < rank; row++) {
right_null[pivot_columns[row]] = T() - augmented[row][free_column];
}
T scale = pivot_product;
if (negate != bool((size - 1 + free_column) & 1)) scale = T() - scale;
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
result[row][col] =
scale * right_null[row] * augmented[size - 1][size + col];
}
}
return result;
}
} // namespace matrix
} // namespace m1une
#line 1 "math/matrix/bit_matrix.hpp"
#line 10 "math/matrix/bit_matrix.hpp"
#include <string>
#include <string_view>
#line 14 "math/matrix/bit_matrix.hpp"
namespace m1une {
namespace matrix {
class BitMatrix {
private:
int _rows;
int _cols;
int _blocks;
std::vector<std::uint64_t> _data;
static int block_count(int cols) {
assert(cols >= 0);
return (cols + 63) / 64;
}
static std::size_t storage_size(int rows, int blocks) {
assert(rows >= 0);
return std::size_t(rows) * std::size_t(blocks);
}
std::size_t word_index(int row, int col) const {
assert(0 <= row && row < _rows);
assert(0 <= col && col < _cols);
return std::size_t(row) * std::size_t(_blocks) +
std::size_t(col / 64);
}
std::uint64_t trailing_mask() const {
if ((_cols & 63) == 0) return ~std::uint64_t(0);
return (std::uint64_t(1) << (_cols & 63)) - 1;
}
public:
class BitReference {
private:
std::uint64_t* word;
std::uint64_t mask;
public:
BitReference(std::uint64_t& word_value, std::uint64_t mask_value)
: word(&word_value), mask(mask_value) {}
operator bool() const {
return (*word & mask) != 0;
}
BitReference& operator=(bool value) {
if (value) {
*word |= mask;
} else {
*word &= ~mask;
}
return *this;
}
BitReference& operator=(const BitReference& other) {
return *this = bool(other);
}
void flip() {
*word ^= mask;
}
};
class RowReference {
private:
BitMatrix* matrix;
int row;
public:
RowReference(BitMatrix& matrix_value, int row_value)
: matrix(&matrix_value), row(row_value) {}
BitReference operator[](int col) const {
return (*matrix)(row, col);
}
};
class ConstRowReference {
private:
const BitMatrix* matrix;
int row;
public:
ConstRowReference(const BitMatrix& matrix_value, int row_value)
: matrix(&matrix_value), row(row_value) {}
bool operator[](int col) const {
return (*matrix)(row, col);
}
};
BitMatrix() : _rows(0), _cols(0), _blocks(0) {}
BitMatrix(int rows, int cols, bool value = false)
: _rows(rows),
_cols(cols),
_blocks(block_count(cols)),
_data(
storage_size(rows, _blocks),
value ? ~std::uint64_t(0) : std::uint64_t(0)
) {
assert(rows >= 0);
if (value && _blocks > 0) {
const std::uint64_t mask = trailing_mask();
for (int row = 0; row < _rows; row++) {
_data[
std::size_t(row + 1) * std::size_t(_blocks) - 1
] &= mask;
}
}
}
int rows() const {
return _rows;
}
int cols() const {
return _cols;
}
int blocks_per_row() const {
return _blocks;
}
bool empty() const {
return _rows == 0 || _cols == 0;
}
RowReference operator[](int row) {
assert(0 <= row && row < _rows);
return RowReference(*this, row);
}
ConstRowReference operator[](int row) const {
assert(0 <= row && row < _rows);
return ConstRowReference(*this, row);
}
BitReference operator()(int row, int col) {
const std::size_t index = word_index(row, col);
return BitReference(_data[index], std::uint64_t(1) << (col & 63));
}
bool operator()(int row, int col) const {
const std::size_t index = word_index(row, col);
return (_data[index] >> (col & 63)) & 1;
}
bool get(int row, int col) const {
return (*this)(row, col);
}
void set(int row, int col, bool value = true) {
(*this)(row, col) = value;
}
void reset(int row, int col) {
set(row, col, false);
}
void flip(int row, int col) {
(*this)(row, col).flip();
}
void clear() {
std::fill(_data.begin(), _data.end(), std::uint64_t(0));
}
void set_row(int row, std::string_view bits) {
assert(0 <= row && row < _rows);
assert(int(bits.size()) == _cols);
const std::size_t offset =
std::size_t(row) * std::size_t(_blocks);
std::fill(
_data.begin() + std::ptrdiff_t(offset),
_data.begin() + std::ptrdiff_t(offset + std::size_t(_blocks)),
std::uint64_t(0)
);
for (int col = 0; col < _cols; col++) {
assert(bits[std::size_t(col)] == '0' || bits[std::size_t(col)] == '1');
if (bits[std::size_t(col)] == '1') set(row, col);
}
}
std::string row_string(int row) const {
assert(0 <= row && row < _rows);
std::string result(std::size_t(_cols), '0');
for (int col = 0; col < _cols; col++) {
if (get(row, col)) result[std::size_t(col)] = '1';
}
return result;
}
static BitMatrix identity(int size) {
assert(size >= 0);
BitMatrix result(size, size);
for (int index = 0; index < size; index++) result.set(index, index);
return result;
}
BitMatrix transposed() const {
BitMatrix result(_cols, _rows);
for (int row = 0; row < _rows; row++) {
for (int col = 0; col < _cols; col++) {
if (get(row, col)) result.set(col, row);
}
}
return result;
}
void swap_rows(int first, int second) {
assert(0 <= first && first < _rows);
assert(0 <= second && second < _rows);
if (first == second) return;
const std::size_t first_offset =
std::size_t(first) * std::size_t(_blocks);
const std::size_t second_offset =
std::size_t(second) * std::size_t(_blocks);
for (int block = 0; block < _blocks; block++) {
std::swap(
_data[first_offset + std::size_t(block)],
_data[second_offset + std::size_t(block)]
);
}
}
void xor_rows(int target, int source, int first_col = 0) {
assert(0 <= target && target < _rows);
assert(0 <= source && source < _rows);
assert(0 <= first_col && first_col <= _cols);
if (first_col == _cols) return;
const std::size_t target_offset =
std::size_t(target) * std::size_t(_blocks);
const std::size_t source_offset =
std::size_t(source) * std::size_t(_blocks);
const int first_block = first_col / 64;
const int first_bit = first_col & 63;
if (first_bit != 0) {
const std::uint64_t mask = ~std::uint64_t(0) << first_bit;
_data[target_offset + std::size_t(first_block)] ^=
_data[source_offset + std::size_t(first_block)] & mask;
} else {
_data[target_offset + std::size_t(first_block)] ^=
_data[source_offset + std::size_t(first_block)];
}
for (int block = first_block + 1; block < _blocks; block++) {
_data[target_offset + std::size_t(block)] ^=
_data[source_offset + std::size_t(block)];
}
}
BitMatrix& operator^=(const BitMatrix& rhs) {
assert(_rows == rhs._rows && _cols == rhs._cols);
for (std::size_t index = 0; index < _data.size(); index++) {
_data[index] ^= rhs._data[index];
}
return *this;
}
BitMatrix& operator+=(const BitMatrix& rhs) {
return *this ^= rhs;
}
BitMatrix& operator-=(const BitMatrix& rhs) {
return *this ^= rhs;
}
BitMatrix& operator*=(const BitMatrix& rhs) {
return *this = *this * rhs;
}
friend BitMatrix operator^(BitMatrix lhs, const BitMatrix& rhs) {
return lhs ^= rhs;
}
friend BitMatrix operator+(BitMatrix lhs, const BitMatrix& rhs) {
return lhs += rhs;
}
friend BitMatrix operator-(BitMatrix lhs, const BitMatrix& rhs) {
return lhs -= rhs;
}
friend BitMatrix operator*(const BitMatrix& lhs, const BitMatrix& rhs) {
assert(lhs._cols == rhs._rows);
BitMatrix result(lhs._rows, rhs._cols);
for (int row = 0; row < lhs._rows; row++) {
const std::size_t lhs_offset =
std::size_t(row) * std::size_t(lhs._blocks);
const std::size_t result_offset =
std::size_t(row) * std::size_t(result._blocks);
for (int lhs_block = 0; lhs_block < lhs._blocks; lhs_block++) {
std::uint64_t word =
lhs._data[lhs_offset + std::size_t(lhs_block)];
while (word != 0) {
const int bit = std::countr_zero(word);
const int middle = lhs_block * 64 + bit;
const std::size_t rhs_offset =
std::size_t(middle) * std::size_t(rhs._blocks);
for (int block = 0; block < rhs._blocks; block++) {
result._data[result_offset + std::size_t(block)] ^=
rhs._data[rhs_offset + std::size_t(block)];
}
word &= word - 1;
}
}
}
return result;
}
bool operator==(const BitMatrix& rhs) const {
return
_rows == rhs._rows && _cols == rhs._cols && _data == rhs._data;
}
bool operator!=(const BitMatrix& rhs) const {
return !(*this == rhs);
}
BitMatrix pow(std::uint64_t exponent) const {
assert(_rows == _cols);
BitMatrix result = identity(_rows);
BitMatrix base = *this;
while (exponent > 0) {
if (exponent & 1) result *= base;
exponent >>= 1;
if (exponent > 0) base *= base;
}
return result;
}
};
namespace bit_matrix_detail {
inline std::vector<int> row_reduce(
BitMatrix& matrix,
int pivot_col_limit,
bool reduced
) {
assert(0 <= pivot_col_limit && pivot_col_limit <= matrix.cols());
std::vector<int> pivot_columns;
int pivot_row = 0;
for (
int col = 0;
col < pivot_col_limit && pivot_row < matrix.rows();
col++
) {
int pivot = -1;
for (int row = pivot_row; row < matrix.rows(); row++) {
if (matrix.get(row, col)) {
pivot = row;
break;
}
}
if (pivot == -1) continue;
matrix.swap_rows(pivot_row, pivot);
const int first_row = reduced ? 0 : pivot_row + 1;
for (int row = first_row; row < matrix.rows(); row++) {
if (row != pivot_row && matrix.get(row, col)) {
matrix.xor_rows(row, pivot_row, col);
}
}
pivot_columns.push_back(col);
pivot_row++;
}
return pivot_columns;
}
} // namespace bit_matrix_detail
struct BitRowReduction {
BitMatrix matrix;
std::vector<int> pivot_columns;
int rank() const {
return int(pivot_columns.size());
}
};
inline BitRowReduction reduced_row_echelon_form(BitMatrix matrix) {
BitRowReduction result;
result.pivot_columns = bit_matrix_detail::row_reduce(
matrix,
matrix.cols(),
true
);
result.matrix = std::move(matrix);
return result;
}
inline int matrix_rank(BitMatrix matrix) {
if (matrix.rows() > matrix.cols()) matrix = matrix.transposed();
return int(bit_matrix_detail::row_reduce(
matrix,
matrix.cols(),
false
).size());
}
inline bool determinant(const BitMatrix& matrix) {
assert(matrix.rows() == matrix.cols());
return matrix_rank(matrix) == matrix.rows();
}
inline std::optional<BitMatrix> inverse(const BitMatrix& matrix) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
BitMatrix augmented(size, 2 * size);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
if (matrix.get(row, col)) augmented.set(row, col);
}
augmented.set(row, size + row);
}
const std::vector<int> pivots = bit_matrix_detail::row_reduce(
augmented,
size,
true
);
if (int(pivots.size()) != size) return std::nullopt;
BitMatrix result(size, size);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
if (augmented.get(row, size + col)) result.set(row, col);
}
}
return result;
}
struct BitLinearSystemResult {
bool consistent = false;
std::vector<bool> particular_solution;
std::vector<std::vector<bool>> nullspace_basis;
std::vector<int> pivot_columns;
int rank() const {
return int(pivot_columns.size());
}
int nullity() const {
return consistent ? int(nullspace_basis.size()) : 0;
}
bool has_unique_solution() const {
return consistent && nullspace_basis.empty();
}
};
inline BitLinearSystemResult solve_linear_system(
const BitMatrix& coefficients,
const std::vector<bool>& constants
) {
assert(coefficients.rows() == int(constants.size()));
const int equation_count = coefficients.rows();
const int variable_count = coefficients.cols();
BitMatrix augmented(equation_count, variable_count + 1);
for (int row = 0; row < equation_count; row++) {
for (int col = 0; col < variable_count; col++) {
if (coefficients.get(row, col)) augmented.set(row, col);
}
if (constants[std::size_t(row)]) augmented.set(row, variable_count);
}
BitLinearSystemResult result;
result.pivot_columns = bit_matrix_detail::row_reduce(
augmented,
variable_count,
true
);
for (int row = result.rank(); row < equation_count; row++) {
if (augmented.get(row, variable_count)) return result;
}
result.consistent = true;
result.particular_solution.assign(std::size_t(variable_count), false);
std::vector<bool> is_pivot(std::size_t(variable_count), false);
for (int row = 0; row < result.rank(); row++) {
const int col = result.pivot_columns[std::size_t(row)];
is_pivot[std::size_t(col)] = true;
result.particular_solution[std::size_t(col)] =
augmented.get(row, variable_count);
}
for (int free_col = 0; free_col < variable_count; free_col++) {
if (is_pivot[std::size_t(free_col)]) continue;
std::vector<bool> direction(std::size_t(variable_count), false);
direction[std::size_t(free_col)] = true;
for (int row = 0; row < result.rank(); row++) {
const int pivot_col = result.pivot_columns[std::size_t(row)];
direction[std::size_t(pivot_col)] = augmented.get(row, free_col);
}
result.nullspace_basis.push_back(std::move(direction));
}
return result;
}
} // namespace matrix
} // namespace m1une
#line 1 "math/matrix/characteristic_polynomial.hpp"
#line 8 "math/matrix/characteristic_polynomial.hpp"
#line 10 "math/matrix/characteristic_polynomial.hpp"
namespace m1une {
namespace matrix {
template <class T>
std::vector<T> characteristic_polynomial(Matrix<T> matrix) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
for (int col = 0; col + 2 < size; col++) {
int pivot = col + 1;
while (pivot < size && matrix[pivot][col] == T()) pivot++;
if (pivot == size) continue;
if (pivot != col + 1) {
matrix.swap_rows(pivot, col + 1);
for (int row = 0; row < size; row++) {
std::swap(matrix[row][pivot], matrix[row][col + 1]);
}
}
const T inverse_pivot = T(1) / matrix[col + 1][col];
for (int row = col + 2; row < size; row++) {
if (matrix[row][col] == T()) continue;
const T factor = matrix[row][col] * inverse_pivot;
for (int j = col; j < size; j++) {
matrix[row][j] -= factor * matrix[col + 1][j];
}
for (int i = 0; i < size; i++) {
matrix[i][col + 1] += factor * matrix[i][row];
}
}
}
std::vector<std::vector<T>> polynomial(std::size_t(size + 1));
polynomial[0].assign(1, T(1));
for (int leading_size = 1; leading_size <= size; leading_size++) {
const int last = leading_size - 1;
polynomial[std::size_t(leading_size)].assign(
std::size_t(leading_size + 1),
T()
);
const std::vector<T>& previous =
polynomial[std::size_t(leading_size - 1)];
std::vector<T>& current = polynomial[std::size_t(leading_size)];
for (int degree = 0; degree < leading_size; degree++) {
current[std::size_t(degree)] -=
previous[std::size_t(degree)] * matrix[last][last];
current[std::size_t(degree + 1)] +=
previous[std::size_t(degree)];
}
T subdiagonal_product = T(1);
for (int row = last - 1; row >= 0; row--) {
subdiagonal_product *= matrix[row + 1][row];
const T factor = subdiagonal_product * matrix[row][last];
if (factor == T()) continue;
for (int degree = 0; degree <= row; degree++) {
current[std::size_t(degree)] -=
factor * polynomial[std::size_t(row)][std::size_t(degree)];
}
}
}
return polynomial[std::size_t(size)];
}
} // namespace matrix
} // namespace m1une
#line 1 "math/matrix/determinant_mod.hpp"
#line 7 "math/matrix/determinant_mod.hpp"
#line 9 "math/matrix/determinant_mod.hpp"
namespace m1une {
namespace matrix {
namespace detail {
inline std::uint64_t determinant_multiply_mod(std::uint64_t lhs,
std::uint64_t rhs,
std::uint64_t modulus) {
return std::uint64_t(static_cast<unsigned __int128>(lhs) * rhs % modulus);
}
inline std::uint64_t determinant_subtract_product_mod(
std::uint64_t value, std::uint64_t lhs, std::uint64_t rhs,
std::uint64_t modulus) {
const std::uint64_t product = determinant_multiply_mod(lhs, rhs, modulus);
return std::uint64_t((static_cast<unsigned __int128>(value) + modulus - product) %
modulus);
}
inline std::uint64_t determinant_add_products_mod(
std::uint64_t first_lhs, std::uint64_t first_rhs,
std::uint64_t second_lhs, std::uint64_t second_rhs,
std::uint64_t modulus) {
const std::uint64_t first =
determinant_multiply_mod(first_lhs, first_rhs, modulus);
const std::uint64_t second =
determinant_multiply_mod(second_lhs, second_rhs, modulus);
return std::uint64_t((static_cast<unsigned __int128>(first) + second) % modulus);
}
template <class Integer>
std::uint64_t determinant_normalize(Integer value, std::uint64_t modulus) {
static_assert(std::is_integral_v<Integer>);
static_assert(sizeof(Integer) <= sizeof(std::uint64_t));
if constexpr (std::is_signed_v<Integer>) {
__int128 residue = static_cast<__int128>(value) % static_cast<__int128>(modulus);
if (residue < 0) residue += modulus;
return std::uint64_t(residue);
} else {
return std::uint64_t(static_cast<unsigned __int128>(value) % modulus);
}
}
} // namespace detail
template <class Integer>
std::uint64_t determinant_mod(const Matrix<Integer>& matrix,
std::uint64_t modulus) {
static_assert(std::is_integral_v<Integer>);
assert(matrix.rows() == matrix.cols());
assert(modulus > 0);
const int size = matrix.rows();
if (size == 0) return std::uint64_t(1) % modulus;
Matrix<std::uint64_t> reduced(size, size);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
reduced[row][col] =
detail::determinant_normalize(matrix[row][col], modulus);
}
}
std::uint64_t result = std::uint64_t(1) % modulus;
bool negate = false;
for (int col = 0; col < size; col++) {
int pivot = col;
while (pivot < size && reduced[pivot][col] == 0) pivot++;
if (pivot == size) return 0;
if (pivot != col) {
reduced.swap_rows(pivot, col);
negate = !negate;
}
for (int row = col + 1; row < size; row++) {
std::uint64_t upper = reduced[col][col];
std::uint64_t lower = reduced[row][col];
if (lower == 0) continue;
std::uint64_t upper_upper = 1 % modulus;
std::uint64_t upper_lower = 0;
std::uint64_t lower_upper = 0;
std::uint64_t lower_lower = 1 % modulus;
while (upper != 0 && lower != 0) {
if (upper < lower) {
const std::uint64_t quotient = lower / upper;
lower -= quotient * upper;
lower_upper = detail::determinant_subtract_product_mod(
lower_upper, quotient, upper_upper, modulus);
lower_lower = detail::determinant_subtract_product_mod(
lower_lower, quotient, upper_lower, modulus);
} else {
const std::uint64_t quotient = upper / lower;
upper -= quotient * lower;
upper_upper = detail::determinant_subtract_product_mod(
upper_upper, quotient, lower_upper, modulus);
upper_lower = detail::determinant_subtract_product_mod(
upper_lower, quotient, lower_lower, modulus);
}
}
for (int index = col; index < size; index++) {
const std::uint64_t old_upper = reduced[col][index];
const std::uint64_t old_lower = reduced[row][index];
reduced[col][index] = detail::determinant_add_products_mod(
upper_upper, old_upper, upper_lower, old_lower, modulus);
reduced[row][index] = detail::determinant_add_products_mod(
lower_upper, old_upper, lower_lower, old_lower, modulus);
}
if (upper == 0) {
reduced.swap_rows(col, row);
negate = !negate;
}
}
result = detail::determinant_multiply_mod(
result, reduced[col][col], modulus);
if (result == 0) return 0;
}
return negate ? modulus - result : result;
}
} // namespace matrix
} // namespace m1une
#line 1 "math/matrix/hafnian.hpp"
#line 7 "math/matrix/hafnian.hpp"
#line 9 "math/matrix/hafnian.hpp"
namespace m1une {
namespace matrix {
namespace internal {
template <class T>
class HafnianSolver {
using Polynomial = std::vector<T>;
using PolynomialMatrix = std::vector<std::vector<Polynomial>>;
int _degree;
void add_shifted_product(Polynomial& result, const Polynomial& first,
const Polynomial& second) const {
for (int first_degree = 0; first_degree < _degree; first_degree++) {
for (int second_degree = 0;
first_degree + second_degree + 1 < _degree;
second_degree++) {
result[first_degree + second_degree + 1] +=
first[first_degree] * second[second_degree];
}
}
}
Polynomial solve(PolynomialMatrix matrix) const {
if (matrix.empty()) {
Polynomial result(_degree);
result[0] = T(1);
return result;
}
std::vector<Polynomial> first = std::move(matrix.back());
matrix.pop_back();
std::vector<Polynomial> second = std::move(matrix.back());
matrix.pop_back();
const int remaining = int(matrix.size());
Polynomial first_to_pair = std::move(first[remaining]);
Polynomial result = solve(matrix);
for (T& coefficient : result) coefficient = T() - coefficient;
for (int row = 0; row < remaining; row++) {
for (int col = 0; col < row; col++) {
add_shifted_product(matrix[row][col], first[row], second[col]);
add_shifted_product(matrix[row][col], second[row], first[col]);
}
}
Polynomial with_connections = solve(std::move(matrix));
add_shifted_product(result, first_to_pair, with_connections);
for (int degree = 0; degree < _degree; degree++) {
result[degree] += with_connections[degree];
}
return result;
}
public:
explicit HafnianSolver(int size) : _degree(size / 2 + 1) {}
T operator()(const Matrix<T>& matrix) const {
const int size = matrix.rows();
PolynomialMatrix polynomial_matrix(size);
for (int row = 0; row < size; row++) {
polynomial_matrix[row].assign(row, Polynomial(_degree));
for (int col = 0; col < row; col++) {
polynomial_matrix[row][col][0] = matrix[row][col];
}
}
return solve(std::move(polynomial_matrix)).back();
}
};
} // namespace internal
// Returns the hafnian of an even-dimensional symmetric zero-diagonal matrix.
template <class T>
T hafnian(const Matrix<T>& matrix) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
assert(size % 2 == 0);
#ifndef NDEBUG
for (int row = 0; row < size; row++) {
assert(matrix[row][row] == T());
for (int col = row + 1; col < size; col++) {
assert(matrix[row][col] == matrix[col][row]);
}
}
#endif
return internal::HafnianSolver<T>(size)(matrix);
}
} // namespace matrix
} // namespace m1une
#line 1 "math/matrix/linear_algebra.hpp"
#line 7 "math/matrix/linear_algebra.hpp"
#line 9 "math/matrix/linear_algebra.hpp"
namespace m1une {
namespace matrix {
template <class T>
constexpr T default_epsilon() {
if constexpr (std::is_floating_point_v<T>) {
return T(1e-10);
} else {
return T();
}
}
namespace detail {
template <class T>
T matrix_abs(T value) {
return value < T() ? T() - value : value;
}
template <class T>
bool is_zero(const T& value, const T& eps) {
if constexpr (std::is_floating_point_v<T>) {
return matrix_abs(value) <= eps;
} else {
(void)eps;
return value == T();
}
}
template <class T>
int choose_pivot(const Matrix<T>& matrix, int first_row, int col, const T& eps) {
int pivot = -1;
if constexpr (std::is_floating_point_v<T>) {
for (int row = first_row; row < matrix.rows(); row++) {
if (is_zero(matrix[row][col], eps)) continue;
if (pivot == -1 || matrix_abs(matrix[pivot][col]) < matrix_abs(matrix[row][col])) {
pivot = row;
}
}
} else {
for (int row = first_row; row < matrix.rows(); row++) {
if (!is_zero(matrix[row][col], eps)) {
pivot = row;
break;
}
}
}
return pivot;
}
template <class T>
std::vector<int> row_reduce(Matrix<T>& matrix, int pivot_col_limit, const T& eps,
bool reduced) {
std::vector<int> pivot_columns;
int pivot_row = 0;
for (int col = 0; col < pivot_col_limit && pivot_row < matrix.rows(); col++) {
int pivot = choose_pivot(matrix, pivot_row, col, eps);
if (pivot == -1) continue;
matrix.swap_rows(pivot_row, pivot);
const T pivot_value = matrix[pivot_row][col];
if (reduced) {
for (int j = col; j < matrix.cols(); j++) matrix[pivot_row][j] /= pivot_value;
}
const int first_row = reduced ? 0 : pivot_row + 1;
for (int row = first_row; row < matrix.rows(); row++) {
if (row == pivot_row || is_zero(matrix[row][col], eps)) continue;
T factor = matrix[row][col];
if (!reduced) factor /= pivot_value;
matrix[row][col] = T();
for (int j = col + 1; j < matrix.cols(); j++) {
matrix[row][j] -= factor * matrix[pivot_row][j];
}
}
pivot_columns.push_back(col);
pivot_row++;
}
if constexpr (std::is_floating_point_v<T>) {
for (T& value : matrix.data()) {
if (is_zero(value, eps)) value = T();
}
}
return pivot_columns;
}
} // namespace detail
template <class T>
struct RowReduction {
Matrix<T> matrix;
std::vector<int> pivot_columns;
int rank() const {
return int(pivot_columns.size());
}
};
template <class T>
RowReduction<T> reduced_row_echelon_form(Matrix<T> matrix,
T eps = default_epsilon<T>()) {
RowReduction<T> result;
result.pivot_columns = detail::row_reduce(matrix, matrix.cols(), eps, true);
result.matrix = std::move(matrix);
return result;
}
template <class T>
int matrix_rank(Matrix<T> matrix, T eps = default_epsilon<T>()) {
return int(detail::row_reduce(matrix, matrix.cols(), eps, false).size());
}
template <class T>
T determinant(Matrix<T> matrix, T eps = default_epsilon<T>()) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
T result = T(1);
bool negate = false;
for (int col = 0; col < size; col++) {
int pivot = detail::choose_pivot(matrix, col, col, eps);
if (pivot == -1) return T();
if (pivot != col) {
matrix.swap_rows(pivot, col);
negate = !negate;
}
const T pivot_value = matrix[col][col];
result *= pivot_value;
for (int row = col + 1; row < size; row++) {
if (detail::is_zero(matrix[row][col], eps)) continue;
const T factor = matrix[row][col] / pivot_value;
matrix[row][col] = T();
for (int j = col + 1; j < size; j++) {
matrix[row][j] -= factor * matrix[col][j];
}
}
}
return negate ? T() - result : result;
}
template <class T>
std::optional<Matrix<T>> inverse(const Matrix<T>& matrix,
T eps = default_epsilon<T>()) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
Matrix<T> augmented(size, size * 2);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
augmented[row][col] = matrix[row][col];
}
augmented[row][size + row] = T(1);
}
const std::vector<int> pivots = detail::row_reduce(augmented, size, eps, true);
if (int(pivots.size()) != size) return std::nullopt;
Matrix<T> result(size, size);
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
result[row][col] = augmented[row][size + col];
}
}
return result;
}
template <class T>
struct LinearSystemResult {
bool consistent = false;
std::vector<T> particular_solution;
std::vector<std::vector<T>> nullspace_basis;
std::vector<int> pivot_columns;
int rank() const {
return int(pivot_columns.size());
}
int nullity() const {
return consistent ? int(nullspace_basis.size()) : 0;
}
bool has_unique_solution() const {
return consistent && nullspace_basis.empty();
}
};
template <class T>
LinearSystemResult<T> solve_linear_system(const Matrix<T>& coefficients,
const std::vector<T>& constants,
T eps = default_epsilon<T>()) {
assert(coefficients.rows() == int(constants.size()));
const int equation_count = coefficients.rows();
const int variable_count = coefficients.cols();
Matrix<T> augmented(equation_count, variable_count + 1);
for (int row = 0; row < equation_count; row++) {
for (int col = 0; col < variable_count; col++) {
augmented[row][col] = coefficients[row][col];
}
augmented[row][variable_count] = constants[std::size_t(row)];
}
LinearSystemResult<T> result;
result.pivot_columns =
detail::row_reduce(augmented, variable_count, eps, true);
for (int row = result.rank(); row < equation_count; row++) {
bool zero_left = true;
for (int col = 0; col < variable_count; col++) {
if (!detail::is_zero(augmented[row][col], eps)) {
zero_left = false;
break;
}
}
if (zero_left && !detail::is_zero(augmented[row][variable_count], eps)) {
return result;
}
}
result.consistent = true;
result.particular_solution.assign(std::size_t(variable_count), T());
std::vector<bool> is_pivot(std::size_t(variable_count), false);
for (int row = 0; row < result.rank(); row++) {
const int col = result.pivot_columns[std::size_t(row)];
is_pivot[std::size_t(col)] = true;
result.particular_solution[std::size_t(col)] = augmented[row][variable_count];
}
for (int free_col = 0; free_col < variable_count; free_col++) {
if (is_pivot[std::size_t(free_col)]) continue;
std::vector<T> direction(static_cast<std::size_t>(variable_count));
direction[std::size_t(free_col)] = T(1);
for (int row = 0; row < result.rank(); row++) {
const int pivot_col = result.pivot_columns[std::size_t(row)];
direction[std::size_t(pivot_col)] = T() - augmented[row][free_col];
}
result.nullspace_basis.push_back(std::move(direction));
}
return result;
}
} // namespace matrix
} // namespace m1une
#line 1 "math/matrix/pfaffian.hpp"
#line 6 "math/matrix/pfaffian.hpp"
#line 8 "math/matrix/pfaffian.hpp"
namespace m1une {
namespace matrix {
// Returns the Pfaffian of an even-dimensional alternating matrix over a field.
template <class T>
T pfaffian(Matrix<T> matrix) {
assert(matrix.rows() == matrix.cols());
const int size = matrix.rows();
assert(size % 2 == 0);
#ifndef NDEBUG
for (int row = 0; row < size; row++) {
assert(matrix[row][row] == T());
for (int col = row + 1; col < size; col++) {
assert(matrix[row][col] == T() - matrix[col][row]);
}
}
#endif
T result = T(1);
for (int first = 0; first < size; first += 2) {
int pivot = first + 1;
while (pivot < size && matrix[first][pivot] == T()) pivot++;
if (pivot == size) return T();
if (pivot != first + 1) {
matrix.swap_rows(pivot, first + 1);
for (int row = 0; row < size; row++) {
std::swap(matrix[row][pivot], matrix[row][first + 1]);
}
result = T() - result;
}
const int second = first + 1;
const T pivot_value = matrix[first][second];
result *= pivot_value;
const T inverse_pivot = T(1) / pivot_value;
for (int row = second + 1; row < size; row++) {
for (int col = row + 1; col < size; col++) {
matrix[row][col] +=
(matrix[second][row] * matrix[first][col] -
matrix[first][row] * matrix[second][col]) *
inverse_pivot;
matrix[col][row] = T() - matrix[row][col];
}
}
}
return result;
}
} // namespace matrix
} // namespace m1une
#line 1 "math/matrix/sparse_determinant.hpp"
#line 8 "math/matrix/sparse_determinant.hpp"
namespace m1une {
namespace matrix {
template <class T>
struct SparseMatrixEntry {
int row;
int col;
T value;
};
namespace internal {
struct SparseDeterminantRandom {
std::uint64_t state;
explicit SparseDeterminantRandom(std::uint64_t seed) : state(seed) {}
std::uint64_t operator()() {
std::uint64_t value = (state += 0x9e3779b97f4a7c15ULL);
value = (value ^ (value >> 30)) * 0xbf58476d1ce4e5b9ULL;
value = (value ^ (value >> 27)) * 0x94d049bb133111ebULL;
return value ^ (value >> 31);
}
};
template <class T>
std::vector<T> berlekamp_massey(const std::vector<T>& sequence) {
std::vector<T> recurrence(1, T(1));
std::vector<T> previous(1, T(1));
int degree = 0;
int shift = 1;
T previous_discrepancy = T(1);
for (int index = 0; index < int(sequence.size()); index++) {
T discrepancy = sequence[index];
for (int i = 1; i <= degree; i++) {
discrepancy += recurrence[i] * sequence[index - i];
}
if (discrepancy == T()) {
shift++;
continue;
}
const T factor = discrepancy / previous_discrepancy;
std::vector<T> old_recurrence = recurrence;
if (int(recurrence.size()) < int(previous.size()) + shift) {
recurrence.resize(previous.size() + std::size_t(shift), T());
}
for (int i = 0; i < int(previous.size()); i++) {
recurrence[i + shift] -= factor * previous[i];
}
if (2 * degree <= index) {
degree = index + 1 - degree;
previous = std::move(old_recurrence);
previous_discrepancy = discrepancy;
shift = 1;
} else {
shift++;
}
}
recurrence.resize(std::size_t(degree + 1));
return recurrence;
}
} // namespace internal
// Randomized black-box determinant over a finite field. random_nonzero must
// return independent nonzero field elements.
template <class T, class RandomValue>
T sparse_determinant_with_randomizer(
int size, const std::vector<SparseMatrixEntry<T>>& entries,
RandomValue random_nonzero
) {
assert(size >= 0);
for (const SparseMatrixEntry<T>& entry : entries) {
assert(0 <= entry.row && entry.row < size);
assert(0 <= entry.col && entry.col < size);
}
if (size == 0) return T(1);
auto random_vector = [&]() {
std::vector<T> result(size);
for (T& value : result) {
value = random_nonzero();
assert(value != T());
}
return result;
};
while (true) {
std::vector<T> diagonal = random_vector();
std::vector<T> left = random_vector();
std::vector<T> state = random_vector();
std::vector<T> sequence(std::size_t(2 * size));
for (int step = 0; step < 2 * size; step++) {
for (int i = 0; i < size; i++) sequence[step] += left[i] * state[i];
for (int i = 0; i < size; i++) state[i] *= diagonal[i];
std::vector<T> next(size);
for (const SparseMatrixEntry<T>& entry : entries) {
next[entry.row] += entry.value * state[entry.col];
}
state = std::move(next);
}
std::vector<T> recurrence = internal::berlekamp_massey(sequence);
if (recurrence.back() == T()) return T();
if (int(recurrence.size()) != size + 1) continue;
T determinant = recurrence.back();
if (size % 2 == 1) determinant = T() - determinant;
for (const T& value : diagonal) determinant /= value;
return determinant;
}
}
template <class T>
T sparse_determinant(
int size, const std::vector<SparseMatrixEntry<T>>& entries,
std::uint64_t seed = 0x243f6a8885a308d3ULL
) {
const std::uint64_t modulus = T::mod();
assert(modulus > 1);
internal::SparseDeterminantRandom random(seed);
auto random_nonzero = [&]() {
return T(1 + random() % (modulus - 1));
};
return sparse_determinant_with_randomizer<T>(size, entries, random_nonzero);
}
} // namespace matrix
} // namespace m1une
#line 13 "math/matrix/all.hpp"
#line 1 "math/modular_kth_root.hpp"
#line 11 "math/modular_kth_root.hpp"
#line 13 "math/modular_kth_root.hpp"
namespace m1une {
namespace math {
namespace modular_kth_root_detail {
inline uint64_t multiply(uint64_t first, uint64_t second, uint64_t mod) {
return static_cast<uint64_t>(static_cast<__uint128_t>(first) * second % mod);
}
inline uint64_t power(uint64_t base, uint64_t exponent, uint64_t mod) {
uint64_t result = 1 % mod;
while (exponent != 0) {
if (exponent & 1) result = multiply(result, base, mod);
base = multiply(base, base, mod);
exponent >>= 1;
}
return result;
}
inline uint64_t integer_power(uint64_t base, int exponent) {
uint64_t result = 1;
for (int i = 0; i < exponent; i++) result *= base;
return result;
}
inline uint64_t inverse(uint64_t value, uint64_t mod) {
if (mod == 1) return 0;
value %= mod;
uint64_t old_remainder = mod;
uint64_t remainder = value;
__int128_t old_coefficient = 0;
__int128_t coefficient = 1;
while (remainder != 0) {
const uint64_t quotient = old_remainder / remainder;
const uint64_t next_remainder =
old_remainder - quotient * remainder;
old_remainder = remainder;
remainder = next_remainder;
const __int128_t next_coefficient =
old_coefficient - static_cast<__int128_t>(quotient) * coefficient;
old_coefficient = coefficient;
coefficient = next_coefficient;
}
assert(old_remainder == 1);
old_coefficient %= static_cast<__int128_t>(mod);
if (old_coefficient < 0) old_coefficient += mod;
return static_cast<uint64_t>(old_coefficient);
}
inline uint64_t extract_prime_power_root(
uint64_t value,
uint64_t root_prime,
int exponent,
uint64_t prime
) {
uint64_t coprime_part = prime - 1;
int available_exponent = 0;
while (coprime_part % root_prime == 0) {
coprime_part /= root_prime;
available_exponent++;
}
assert(exponent <= available_exponent);
const uint64_t root_prime_power = integer_power(root_prime, exponent);
const uint64_t inverse_coprime_part = inverse(
coprime_part, root_prime_power
);
const uint64_t residue = static_cast<uint64_t>(
static_cast<__uint128_t>(root_prime_power - 1) *
inverse_coprime_part % root_prime_power
);
const uint64_t root_exponent = static_cast<uint64_t>(
(static_cast<__uint128_t>(residue) * coprime_part + 1) /
root_prime_power
);
uint64_t root = power(value, root_exponent, prime);
if (exponent == available_exponent) return root;
uint64_t non_residue = 2;
while (power(non_residue, (prime - 1) / root_prime, prime) == 1) {
non_residue++;
}
const uint64_t generator = power(non_residue, coprime_part, prime);
const uint64_t digit_generator = power(
generator,
integer_power(root_prime, available_exponent - 1),
prime
);
const uint64_t step = isqrt(
static_cast<uint64_t>(available_exponent - exponent) * root_prime
) + 1;
const uint64_t giant_factor = power(digit_generator, step, prime);
std::vector<std::pair<uint64_t, uint64_t>> baby_steps;
baby_steps.reserve(step + 1);
uint64_t current = 1;
for (uint64_t index = 0; index <= step; index++) {
baby_steps.emplace_back(current, index);
current = multiply(current, giant_factor, prime);
}
std::sort(baby_steps.begin(), baby_steps.end());
const uint64_t inverse_digit_generator = power(
digit_generator, prime - 2, prime
);
for (int level = exponent; level < available_exponent; level++) {
const uint64_t root_power = power(root, root_prime_power, prime);
const uint64_t error = multiply(
power(root_power, prime - 2, prime), value, prime
);
uint64_t target = power(
error,
integer_power(root_prime, available_exponent - 1 - level),
prime
);
bool found = false;
uint64_t logarithm = 0;
for (uint64_t remainder = 0; remainder <= step; remainder++) {
auto iterator = std::upper_bound(
baby_steps.begin(),
baby_steps.end(),
target,
[](uint64_t key, const std::pair<uint64_t, uint64_t>& entry) {
return key < entry.first;
}
);
if (iterator != baby_steps.begin()) {
--iterator;
if (iterator->first == target) {
logarithm = remainder + step * iterator->second;
found = true;
break;
}
}
target = multiply(target, inverse_digit_generator, prime);
}
assert(found);
if (!found) return 0;
const uint64_t correction_exponent =
logarithm * integer_power(root_prime, level - exponent);
root = multiply(
root,
power(generator, correction_exponent, prime),
prime
);
}
return root;
}
} // namespace modular_kth_root_detail
// Returns x such that x^degree = value (mod prime), or nullopt when no root
// exists. The modulus must be prime.
inline std::optional<uint64_t> modular_kth_root(
uint64_t value,
uint64_t degree,
uint64_t prime
) {
assert(prime >= 2);
value %= prime;
if (degree == 0) {
if (value == 1) return uint64_t(0);
return std::nullopt;
}
if (value == 0) return uint64_t(0);
if (prime == 2) return uint64_t(1);
const uint64_t group_order = prime - 1;
degree %= group_order;
const uint64_t common_divisor = std::gcd(degree, group_order);
if (
modular_kth_root_detail::power(
value, group_order / common_divisor, prime
) != 1
) {
return std::nullopt;
}
const uint64_t reduced_order = group_order / common_divisor;
uint64_t transformed = 1;
if (reduced_order != 1) {
const uint64_t inverse_degree = modular_kth_root_detail::inverse(
degree / common_divisor, reduced_order
);
transformed = modular_kth_root_detail::power(
value, inverse_degree, prime
);
}
uint64_t remaining = common_divisor;
int exponent = 0;
while ((remaining & 1) == 0) {
remaining >>= 1;
exponent++;
}
if (exponent != 0) {
transformed = modular_kth_root_detail::extract_prime_power_root(
transformed, 2, exponent, prime
);
}
for (uint64_t divisor = 3; divisor <= remaining / divisor; divisor += 2) {
exponent = 0;
while (remaining % divisor == 0) {
remaining /= divisor;
exponent++;
}
if (exponent != 0) {
transformed = modular_kth_root_detail::extract_prime_power_root(
transformed, divisor, exponent, prime
);
}
}
if (remaining != 1) {
transformed = modular_kth_root_detail::extract_prime_power_root(
transformed, remaining, 1, prime
);
}
return transformed;
}
template <class Mint>
std::optional<Mint> modular_kth_root(Mint value, uint64_t degree) {
auto root = modular_kth_root(
static_cast<uint64_t>(value.val()),
degree,
static_cast<uint64_t>(Mint::mod())
);
if (!root.has_value()) return std::nullopt;
return Mint(*root);
}
} // namespace math
} // namespace m1une
#line 1 "math/multivariate_convolution.hpp"
#line 11 "math/multivariate_convolution.hpp"
#line 1 "math/primitive_root.hpp"
#line 9 "math/primitive_root.hpp"
#line 11 "math/primitive_root.hpp"
namespace m1une {
namespace math {
inline bool has_primitive_root(uint64_t mod) {
if (mod == 2 || mod == 4) return true;
if (mod < 2) return false;
uint64_t odd_part = mod;
if ((odd_part & 1) == 0) {
odd_part >>= 1;
if ((odd_part & 1) == 0) return false;
}
return prime_factorize(odd_part).size() == 1;
}
// Returns the smallest positive primitive root modulo mod.
// Returns 0 when no primitive root exists.
inline uint64_t primitive_root(uint64_t mod) {
assert(mod >= 2);
if (mod == 2) return 1;
if (!has_primitive_root(mod)) return 0;
const uint64_t phi = euler_phi(mod);
const std::vector<std::pair<uint64_t, int>> factors = prime_factorize(phi);
for (uint64_t candidate = 2; candidate < mod; candidate++) {
if (std::gcd(candidate, mod) != 1) continue;
bool generator = true;
for (const auto& factor : factors) {
if (internal::power_mod(candidate, phi / factor.first, mod) == 1) {
generator = false;
break;
}
}
if (generator) return candidate;
}
return 0;
}
} // namespace math
} // namespace m1une
#line 14 "math/multivariate_convolution.hpp"
namespace m1une {
namespace math {
namespace internal {
template <class T>
struct nested_vector_traits {
using scalar_type = T;
static constexpr int depth = 0;
};
template <class T, class Allocator>
struct nested_vector_traits<std::vector<T, Allocator>> {
using scalar_type = typename nested_vector_traits<T>::scalar_type;
static constexpr int depth = nested_vector_traits<T>::depth + 1;
};
template <class Nested>
void nested_vector_shape(const Nested& values, std::vector<int>& shape) {
if constexpr (nested_vector_traits<Nested>::depth > 0) {
assert(!values.empty());
assert(values.size() <= std::size_t(std::numeric_limits<int>::max()));
shape.push_back(int(values.size()));
nested_vector_shape(values.front(), shape);
}
}
template <class Nested, class Mint>
void flatten_nested_vector(
const Nested& values,
const std::vector<int>& shape,
int level,
std::vector<Mint>& flattened
) {
if constexpr (nested_vector_traits<Nested>::depth == 0) {
flattened.push_back(values);
} else {
assert(level < int(shape.size()));
assert(int(values.size()) == shape[level]);
for (const auto& child : values) {
flatten_nested_vector(child, shape, level + 1, flattened);
}
}
}
template <class Nested, class Mint>
void rebuild_nested_vector(
Nested& values,
const std::vector<int>& shape,
int level,
const std::vector<Mint>& flattened,
int& position
) {
if constexpr (nested_vector_traits<Nested>::depth == 0) {
assert(position < int(flattened.size()));
values = flattened[position++];
} else {
assert(level < int(shape.size()));
values.resize(shape[level]);
for (auto& child : values) {
rebuild_nested_vector(child, shape, level + 1, flattened, position);
}
}
}
template <class Nested>
std::vector<int> flatten_multivariate_inputs(
const Nested& first,
const Nested& second,
std::vector<typename nested_vector_traits<Nested>::scalar_type>& flattened_first,
std::vector<typename nested_vector_traits<Nested>::scalar_type>& flattened_second
) {
std::vector<int> shape;
nested_vector_shape(first, shape);
assert(int(shape.size()) == nested_vector_traits<Nested>::depth);
std::vector<int> second_shape;
nested_vector_shape(second, second_shape);
assert(second_shape == shape);
flatten_nested_vector(first, shape, 0, flattened_first);
flatten_nested_vector(second, shape, 0, flattened_second);
std::reverse(shape.begin(), shape.end());
return shape;
}
template <class Nested>
Nested rebuild_multivariate_result(
std::vector<int> dimensions,
const std::vector<typename nested_vector_traits<Nested>::scalar_type>& flattened
) {
std::reverse(dimensions.begin(), dimensions.end());
Nested result;
int position = 0;
rebuild_nested_vector(result, dimensions, 0, flattened, position);
assert(position == int(flattened.size()));
return result;
}
inline int multivariate_coefficient_count(const std::vector<int>& dimensions) {
int64_t count = 1;
for (int dimension : dimensions) {
assert(dimension > 0);
count *= dimension;
assert(count <= std::numeric_limits<int>::max());
}
return int(count);
}
inline std::vector<int> multivariate_colors(const std::vector<int>& dimensions) {
const int variable_count = int(dimensions.size());
const int coefficient_count = multivariate_coefficient_count(dimensions);
std::vector<int> color(coefficient_count);
if (variable_count == 0) return color;
for (int index = 0; index < coefficient_count; index++) {
int sum = 0;
int stride = 1;
for (int variable = 0; variable + 1 < variable_count; variable++) {
stride *= dimensions[variable];
sum += index / stride;
}
color[index] = sum % variable_count;
}
return color;
}
template <class Mint>
std::vector<Mint> geometric_evaluation(
const std::vector<Mint>& polynomial, Mint ratio
) {
const int size = int(polynomial.size());
if (size <= 64) {
std::vector<Mint> result(size);
Mint point = 1;
for (int i = 0; i < size; i++) {
Mint power = 1;
for (const Mint& coefficient : polynomial) {
result[i] += coefficient * power;
power *= point;
}
point *= ratio;
}
return result;
}
auto triangular_powers = [](Mint base, int length) {
std::vector<Mint> result(length);
if (length == 0) return result;
result[0] = 1;
Mint power = 1;
for (int i = 0; i + 1 < length; i++) {
result[i + 1] = result[i] * power;
power *= base;
}
return result;
};
std::vector<Mint> positive = triangular_powers(ratio, 2 * size - 1);
std::vector<Mint> negative = triangular_powers(ratio.inv(), size);
std::vector<Mint> scaled(polynomial);
for (int i = 0; i < size; i++) scaled[i] *= negative[i];
std::reverse(scaled.begin(), scaled.end());
std::vector<Mint> product = fps::convolution(scaled, positive);
std::vector<Mint> result(size);
for (int i = 0; i < size; i++) result[i] = product[size - 1 + i] * negative[i];
return result;
}
template <class Mint>
std::vector<Mint> cyclic_fourier_transform(
std::vector<Mint> values, Mint ratio, bool inverse
) {
if constexpr (fps::internal::has_static_modulus<Mint>::value) {
const int size = int(values.size());
if ((size & (size - 1)) == 0) {
// Keep normalization outside the per-axis transforms, matching
// the arbitrary-length DFT path below.
fps::internal::ntt(values, inverse, false);
return values;
}
}
return geometric_evaluation(values, ratio);
}
} // namespace internal
template <class Mint>
std::vector<Mint> multivariate_convolution_truncated(
const std::vector<int>& dimensions,
const std::vector<Mint>& first,
const std::vector<Mint>& second
) {
static_assert(
fps::internal::has_static_modulus<Mint>::value,
"truncated multivariate convolution requires a static-modulus type"
);
const int variable_count = int(dimensions.size());
const int coefficient_count = internal::multivariate_coefficient_count(dimensions);
assert(int(first.size()) == coefficient_count);
assert(int(second.size()) == coefficient_count);
if (variable_count == 0) return {first[0] * second[0]};
int64_t transform_size_64 = 1;
while (transform_size_64 < 2LL * coefficient_count - 1) transform_size_64 <<= 1;
assert(transform_size_64 <= std::numeric_limits<int>::max());
const int transform_size = int(transform_size_64);
assert((Mint::mod() - 1) % uint32_t(transform_size) == 0);
const std::vector<int> color = internal::multivariate_colors(dimensions);
std::vector<std::vector<Mint>> transformed_first(
variable_count, std::vector<Mint>(transform_size)
);
std::vector<std::vector<Mint>> transformed_second(
variable_count, std::vector<Mint>(transform_size)
);
for (int i = 0; i < coefficient_count; i++) {
transformed_first[color[i]][i] = first[i];
transformed_second[color[i]][i] = second[i];
}
for (int group = 0; group < variable_count; group++) {
fps::internal::ntt(transformed_first[group], false);
fps::internal::ntt(transformed_second[group], false);
}
std::vector<std::vector<Mint>> transformed_result(
variable_count, std::vector<Mint>(transform_size)
);
for (int left = 0; left < variable_count; left++) {
for (int right = 0; right < variable_count; right++) {
std::vector<Mint>& destination =
transformed_result[(left + right) % variable_count];
const std::vector<Mint>& left_values = transformed_first[left];
const std::vector<Mint>& right_values = transformed_second[right];
for (int i = 0; i < transform_size; i++) {
destination[i] += left_values[i] * right_values[i];
}
}
}
for (int group = 0; group < variable_count; group++) {
fps::internal::ntt(transformed_result[group], true);
}
std::vector<Mint> result(coefficient_count);
for (int i = 0; i < coefficient_count; i++) {
result[i] = transformed_result[color[i]][i];
}
return result;
}
template <
class Nested,
std::enable_if_t<(internal::nested_vector_traits<Nested>::depth > 0), int> = 0
>
Nested multivariate_convolution_truncated(
const Nested& first,
const Nested& second
) {
using Mint = typename internal::nested_vector_traits<Nested>::scalar_type;
std::vector<Mint> flattened_first, flattened_second;
std::vector<int> dimensions = internal::flatten_multivariate_inputs(
first, second, flattened_first, flattened_second
);
std::vector<Mint> flattened_result = multivariate_convolution_truncated(
dimensions, flattened_first, flattened_second
);
return internal::rebuild_multivariate_result<Nested>(
std::move(dimensions), flattened_result
);
}
template <class Mint>
std::vector<Mint> multivariate_convolution_cyclic(
const std::vector<int>& dimensions,
const std::vector<Mint>& first,
const std::vector<Mint>& second
) {
const int coefficient_count = internal::multivariate_coefficient_count(dimensions);
assert(int(first.size()) == coefficient_count);
assert(int(second.size()) == coefficient_count);
if (dimensions.empty()) return {first[0] * second[0]};
const uint32_t modulus = Mint::mod();
bool has_all_roots = true;
for (int dimension : dimensions) {
if ((modulus - 1) % uint32_t(dimension) != 0) has_all_roots = false;
}
if (!has_all_roots) {
std::vector<int> reduced_dimensions;
for (int dimension : dimensions) {
if (dimension != 1) reduced_dimensions.push_back(dimension);
}
if (reduced_dimensions.empty()) return {first[0] * second[0]};
std::vector<int> widened_dimensions(reduced_dimensions.size());
for (int i = 0; i < int(reduced_dimensions.size()); i++) {
const int64_t widened = 2LL * reduced_dimensions[i] - 1;
assert(widened <= std::numeric_limits<int>::max());
widened_dimensions[i] = int(widened);
}
const int widened_count =
internal::multivariate_coefficient_count(widened_dimensions);
// The largest embedded input index uses coordinate dimension - 1 on
// every axis. Its double is widened_count - 1, so convolving arrays
// ending at this index produces exactly the widened mixed-radix box.
// In particular, fps::convolution chooses the smallest transform that
// contains widened_count coefficients, instead of one that contains
// 2 * widened_count - 1 coefficients due to trailing zeroes.
int64_t maximum_embedded_index = 0;
int64_t widened_stride = 1;
for (int variable = 0; variable < int(reduced_dimensions.size()); variable++) {
maximum_embedded_index +=
int64_t(reduced_dimensions[variable] - 1) * widened_stride;
widened_stride *= widened_dimensions[variable];
}
assert(widened_stride == widened_count);
assert(2 * maximum_embedded_index + 1 == widened_count);
assert(maximum_embedded_index < std::numeric_limits<int>::max());
const int embedded_input_count = int(maximum_embedded_index) + 1;
std::vector<Mint> widened_first(embedded_input_count);
std::vector<Mint> widened_second(embedded_input_count);
for (int index = 0; index < coefficient_count; index++) {
int remaining = index;
int widened_index = 0;
int embedding_stride = 1;
for (int variable = 0; variable < int(reduced_dimensions.size()); variable++) {
const int coordinate = remaining % reduced_dimensions[variable];
remaining /= reduced_dimensions[variable];
widened_index += coordinate * embedding_stride;
embedding_stride *= widened_dimensions[variable];
}
widened_first[widened_index] = first[index];
widened_second[widened_index] = second[index];
}
std::vector<Mint> widened_product =
fps::convolution(widened_first, widened_second);
assert(int(widened_product.size()) == widened_count);
std::vector<Mint> result(coefficient_count);
for (int widened_index = 0; widened_index < widened_count; widened_index++) {
int remaining = widened_index;
int index = 0;
int stride = 1;
for (int variable = 0; variable < int(reduced_dimensions.size()); variable++) {
const int coordinate = remaining % widened_dimensions[variable];
remaining /= widened_dimensions[variable];
index += (coordinate % reduced_dimensions[variable]) * stride;
stride *= reduced_dimensions[variable];
}
result[index] += widened_product[widened_index];
}
return result;
}
const uint64_t generator = primitive_root(modulus);
assert(generator != 0);
std::vector<Mint> transformed_first(first);
std::vector<Mint> transformed_second(second);
int stride = 1;
for (int dimension : dimensions) {
assert((modulus - 1) % uint32_t(dimension) == 0);
const Mint root = Mint(generator).pow((modulus - 1) / dimension);
for (int block = 0; block < coefficient_count; block += stride * dimension) {
for (int offset = 0; offset < stride; offset++) {
std::vector<Mint> first_line(dimension);
std::vector<Mint> second_line(dimension);
for (int i = 0; i < dimension; i++) {
first_line[i] = transformed_first[block + offset + stride * i];
second_line[i] = transformed_second[block + offset + stride * i];
}
first_line = internal::cyclic_fourier_transform(
std::move(first_line), root, false
);
second_line = internal::cyclic_fourier_transform(
std::move(second_line), root, false
);
for (int i = 0; i < dimension; i++) {
transformed_first[block + offset + stride * i] = first_line[i];
transformed_second[block + offset + stride * i] = second_line[i];
}
}
}
stride *= dimension;
}
for (int i = 0; i < coefficient_count; i++) {
transformed_first[i] *= transformed_second[i];
}
stride = 1;
for (int dimension : dimensions) {
const Mint inverse_root =
Mint(generator).pow((modulus - 1) / dimension).inv();
for (int block = 0; block < coefficient_count; block += stride * dimension) {
for (int offset = 0; offset < stride; offset++) {
std::vector<Mint> line(dimension);
for (int i = 0; i < dimension; i++) {
line[i] = transformed_first[block + offset + stride * i];
}
line = internal::cyclic_fourier_transform(
std::move(line), inverse_root, true
);
for (int i = 0; i < dimension; i++) {
transformed_first[block + offset + stride * i] = line[i];
}
}
}
stride *= dimension;
}
const Mint inverse_size = Mint(coefficient_count).inv();
for (Mint& value : transformed_first) value *= inverse_size;
return transformed_first;
}
template <
class Nested,
std::enable_if_t<(internal::nested_vector_traits<Nested>::depth > 0), int> = 0
>
Nested multivariate_convolution_cyclic(
const Nested& first,
const Nested& second
) {
using Mint = typename internal::nested_vector_traits<Nested>::scalar_type;
std::vector<Mint> flattened_first, flattened_second;
std::vector<int> dimensions = internal::flatten_multivariate_inputs(
first, second, flattened_first, flattened_second
);
std::vector<Mint> flattened_result = multivariate_convolution_cyclic(
dimensions, flattened_first, flattened_second
);
return internal::rebuild_multivariate_result<Nested>(
std::move(dimensions), flattened_result
);
}
} // namespace math
} // namespace m1une
#line 1 "math/multiplicative_function_prefix_sum.hpp"
#line 11 "math/multiplicative_function_prefix_sum.hpp"
namespace m1une {
namespace math {
// Computes summatory multiplicative functions with a Min_25 sieve.
// prime_power(p, e) must return f(p^e), and prime_prefix must contain
// sum_{p <= x} f(p) at every x represented by quotient_values().
template <class T, class PrimePower>
struct MultiplicativeFunctionPrefixSum {
private:
uint64_t _n;
uint64_t _sqrt_n;
uint64_t _large_size;
std::vector<int> _primes;
PrimePower _prime_power;
static uint64_t integer_sqrt(uint64_t n) {
uint64_t result = static_cast<uint64_t>(std::sqrt(static_cast<long double>(n)));
while (result != 0 && result > n / result) result--;
while (result + 1 <= n / (result + 1)) result++;
return result;
}
static uint64_t validated_sqrt(uint64_t n) {
const uint64_t result = integer_sqrt(n);
assert(result <= static_cast<uint64_t>(std::numeric_limits<int>::max() / 2));
return result;
}
static std::vector<int> enumerate_primes(uint64_t limit) {
assert(limit <= static_cast<uint64_t>(std::numeric_limits<int>::max()));
const int n = static_cast<int>(limit);
std::vector<bool> is_composite(n + 1);
std::vector<int> primes;
for (int value = 2; value <= n; value++) {
if (!is_composite[value]) primes.push_back(value);
for (int prime : primes) {
if (value > n / prime) break;
is_composite[value * prime] = true;
if (value % prime == 0) break;
}
}
return primes;
}
T triangular(uint64_t n) const {
if ((n & 1) == 0) return T(n / 2) * T(n + 1);
return T(n) * T((n + 1) / 2);
}
public:
explicit MultiplicativeFunctionPrefixSum(uint64_t n, PrimePower prime_power)
: _n(n),
_sqrt_n(validated_sqrt(n)),
_large_size(n == 0 ? 0 : n / _sqrt_n),
_primes(enumerate_primes(_sqrt_n)),
_prime_power(std::move(prime_power)) {
if (n == 0) return;
while (_large_size > 1 && n / (_large_size - 1) == _sqrt_n) _large_size--;
}
uint64_t n() const {
return _n;
}
uint64_t sqrt_n() const {
return _sqrt_n;
}
const std::vector<int>& primes() const {
return _primes;
}
int table_size() const {
if (_n == 0) return 0;
return static_cast<int>(_large_size + _sqrt_n);
}
// Returns the table index representing x. The argument must be one of the
// values returned by quotient_values().
int index(uint64_t x) const {
assert(_n > 0 && 1 <= x && x <= _n);
if (x <= _sqrt_n) return table_size() - static_cast<int>(x);
const uint64_t result = _n / x;
assert(result < _large_size);
return static_cast<int>(result);
}
// table[i] represents the value at quotient_values()[i]. Index zero is a
// dummy entry; the represented values occupy indices [1, table_size()).
std::vector<uint64_t> quotient_values() const {
if (_n == 0) return {};
std::vector<uint64_t> result(table_size());
for (uint64_t i = 1; i < _large_size; i++) result[i] = _n / i;
for (uint64_t value = 1; value <= _sqrt_n; value++) {
result[index(value)] = value;
}
return result;
}
// Returns pi(x) at every represented x.
std::vector<T> prime_count_table() const {
if (_n == 0) return {};
std::vector<uint64_t> large(_large_size);
for (uint64_t i = 1; i < _large_size; i++) large[i] = _n / i - 1;
std::vector<uint64_t> small(_sqrt_n + 1);
for (uint64_t value = 1; value <= _sqrt_n; value++) small[value] = value - 1;
uint64_t prime_count = 0;
for (int prime_int : _primes) {
const uint64_t prime = static_cast<uint64_t>(prime_int);
const uint64_t square = prime * prime;
const uint64_t end = std::min(_large_size, _n / square + 1);
uint64_t product = prime;
for (uint64_t i = 1; i < end; i++, product += prime) {
const uint64_t previous = product < _large_size ? large[product] : small[_n / product];
large[i] -= previous - prime_count;
}
for (uint64_t value = _sqrt_n; value >= square; value--) {
small[value] -= small[value / prime] - prime_count;
}
prime_count++;
}
std::vector<T> result(table_size());
for (uint64_t i = 0; i < _large_size; i++) result[i] = T(large[i]);
for (uint64_t value = 1; value <= _sqrt_n; value++) result[index(value)] = T(small[value]);
return result;
}
// Returns sum_{p <= x} p at every represented x.
std::vector<T> prime_sum_table() const {
if (_n == 0) return {};
std::vector<T> result(table_size());
for (uint64_t i = 1; i < _large_size; i++) result[i] = triangular(_n / i) - T(1);
for (uint64_t value = 1; value <= _sqrt_n; value++) {
result[index(value)] = triangular(value) - T(1);
}
for (int prime_int : _primes) {
const uint64_t prime = static_cast<uint64_t>(prime_int);
const uint64_t square = prime * prime;
const T before = result[index(prime - 1)];
const uint64_t end = std::min(_large_size, _n / square + 1);
uint64_t product = prime;
for (uint64_t i = 1; i < end; i++, product += prime) {
result[i] -= (result[index(_n / product)] - before) * T(prime);
}
for (uint64_t value = _sqrt_n; value >= square; value--) {
result[index(value)] -= (result[index(value / prime)] - before) * T(prime);
}
}
return result;
}
// Returns sum_{k=1}^x f(k) at every represented x.
std::vector<T> prefix_sum_table(const std::vector<T>& prime_prefix) const {
if (_n == 0) {
assert(prime_prefix.empty());
return {};
}
assert(static_cast<int>(prime_prefix.size()) == table_size());
const std::vector<uint64_t> values = quotient_values();
std::vector<T> result = prime_prefix;
std::vector<T> next = prime_prefix;
for (int prime_index = static_cast<int>(_primes.size()) - 1; prime_index >= 0; prime_index--) {
const uint64_t prime = static_cast<uint64_t>(_primes[prime_index]);
uint64_t power = prime;
int exponent = 1;
while (power <= _n / prime) {
const T current_value = _prime_power(prime, exponent);
const T next_value = _prime_power(prime, exponent + 1);
const T primes_before = prime_prefix[index(prime)];
for (int i = 1; i < table_size(); i++) {
const uint64_t value = values[i];
if (value < power * prime) break;
next[i] += current_value * (result[index(value / power)] - primes_before) + next_value;
}
exponent++;
power *= prime;
}
const uint64_t bound = prime * prime;
const int copy_size = std::min(table_size(), index(bound) + 1);
std::copy(next.begin(), next.begin() + copy_size, result.begin());
}
for (int i = 1; i < table_size(); i++) result[i] += T(1);
return result;
}
T prefix_sum(const std::vector<T>& prime_prefix) const {
if (_n == 0) {
assert(prime_prefix.empty());
return T{};
}
return prefix_sum_table(prime_prefix)[index(_n)];
}
};
} // namespace math
} // namespace m1une
#line 1 "math/prefix_sum_of_binom.hpp"
#line 11 "math/prefix_sum_of_binom.hpp"
#line 13 "math/prefix_sum_of_binom.hpp"
namespace m1une {
namespace math {
// Answers sum_{k=0}^{m-1} binom(n, k) after square-root decomposition.
template <class Mint>
struct PrefixSumOfBinom {
private:
int _maximum;
int _block_size;
Combinatorics<Mint> _combinations;
std::vector<Mint> _powers_of_two;
std::vector<std::vector<Mint>> _data;
Mint _block_prefix(int n, int block) const {
const int endpoint = block * _block_size;
if (n <= endpoint) return _powers_of_two[n];
return _data[block][n - endpoint] * _combinations.inverse_factorial(endpoint);
}
Mint _binom_sum(int n, int left, int right) const {
__uint128_t sum = 0;
for (int k = left; k < right; k++) {
sum += static_cast<__uint128_t>(_combinations.inverse_factorial(k).val()) *
_combinations.inverse_factorial(n - k).val();
}
const uint32_t reduced = static_cast<uint32_t>(sum % Mint::mod());
return Mint::raw(reduced) * _combinations.factorial(n);
}
public:
explicit PrefixSumOfBinom(int maximum)
: _maximum(maximum),
_block_size(std::max(1, int(std::sqrt(static_cast<double>(maximum) + 1)))),
_combinations(maximum),
_powers_of_two(maximum + 1, Mint(1)) {
assert(maximum >= 0);
for (int n = 0; n < maximum; n++) {
_powers_of_two[n + 1] = _powers_of_two[n] + _powers_of_two[n];
}
const int block_count = maximum / (2 * _block_size) + 3;
_data.resize(block_count);
for (int block = 0; block < block_count; block++) {
const int endpoint = block * _block_size;
if (endpoint > maximum) continue;
std::vector<Mint>& values = _data[block];
values.resize(maximum - endpoint + 1);
values[0] = _powers_of_two[endpoint] * _combinations.factorial(endpoint);
for (int offset = 0; offset < maximum - endpoint; offset++) {
values[offset + 1] =
values[offset] + values[offset] -
_combinations.factorial(offset + endpoint) *
_combinations.inverse_factorial(offset);
}
}
}
int maximum() const {
return _maximum;
}
// Returns sum_{k=0}^{m-1} binom(n, k). Values m > n + 1 are clamped.
Mint query(int n, int m) const {
assert(0 <= n && n <= _maximum);
assert(m >= 0);
m = std::min(m, n + 1);
if (m == 0) return Mint(0);
if (2 * m > n + 1) {
return _powers_of_two[n] - query(n, n + 1 - m);
}
const int endpoint = m - 1;
const int block = endpoint / _block_size;
const int lower = block * _block_size;
const int upper = (block + 1) * _block_size;
if (endpoint - lower <= upper - endpoint) {
return _block_prefix(n, block) + _binom_sum(n, lower + 1, endpoint + 1);
}
return _block_prefix(n, block + 1) - _binom_sum(n, endpoint + 1, upper + 1);
}
};
// Batches the same queries and evaluates them in Mo order with linear memory.
template <class Mint>
struct OfflinePrefixSumOfBinom {
private:
std::vector<std::pair<int, int>> _queries;
public:
int query_count() const {
return int(_queries.size());
}
bool empty() const {
return _queries.empty();
}
void reserve(int query_capacity) {
assert(query_capacity >= 0);
_queries.reserve(query_capacity);
}
void clear() {
_queries.clear();
}
// Adds sum_{k=0}^{m-1} binom(n, k) and returns its insertion-order ID.
int add_query(int n, int m) {
assert(n >= 0);
assert(m >= 0);
m = std::min(m, n + 1);
const int id = query_count();
_queries.emplace_back(n, m);
return id;
}
std::vector<Mint> calculate() const {
const int count = query_count();
std::vector<Mint> answers(count);
if (count == 0) return answers;
int maximum = 0;
for (const auto& query : _queries) maximum = std::max(maximum, query.first);
assert(static_cast<uint64_t>(maximum) < Mint::mod());
assert(Mint::mod() % 2 == 1);
Combinatorics<Mint> combinations(maximum);
const int block_size =
std::max(1, int(maximum / std::sqrt(static_cast<double>(count))));
std::vector<int> order(count);
std::iota(order.begin(), order.end(), 0);
std::sort(order.begin(), order.end(), [&](int first, int second) {
const int first_block = _queries[first].first / block_size;
const int second_block = _queries[second].first / block_size;
if (first_block != second_block) return first_block < second_block;
if (first_block & 1) return _queries[first].second > _queries[second].second;
return _queries[first].second < _queries[second].second;
});
int n = 0;
int m = 0;
Mint answer = 0;
const Mint inverse_two = Mint(2).inv();
for (int id : order) {
const int next_n = _queries[id].first;
const int next_m = _queries[id].second;
while (n < next_n) {
answer += answer;
answer -= combinations.binom(n, m - 1);
n++;
}
while (n > next_n) {
answer += combinations.binom(n - 1, m - 1);
answer *= inverse_two;
n--;
}
while (m < next_m) answer += combinations.binom(n, m++);
while (m > next_m) answer -= combinations.binom(n, --m);
answers[id] = answer;
}
return answers;
}
};
} // namespace math
} // namespace m1une
#line 1 "math/prime_sieve.hpp"
#line 8 "math/prime_sieve.hpp"
namespace m1une {
namespace math {
struct PrimeSieve {
private:
int _limit;
std::vector<int> _min_prime_factor;
std::vector<int> _primes;
public:
explicit PrimeSieve(int limit = 0) : _limit(0) {
assert(limit >= 0);
_limit = limit;
_min_prime_factor.assign(limit + 1, 0);
if (limit >= 1) _min_prime_factor[1] = 1;
for (int value = 2; value <= limit; value++) {
if (_min_prime_factor[value] == 0) {
_min_prime_factor[value] = value;
_primes.push_back(value);
}
for (int prime : _primes) {
if (prime > _min_prime_factor[value] || value > limit / prime) break;
_min_prime_factor[value * prime] = prime;
}
}
}
int limit() const {
return _limit;
}
const std::vector<int>& primes() const {
return _primes;
}
const std::vector<int>& min_prime_factors() const {
return _min_prime_factor;
}
bool is_prime(int value) const {
assert(0 <= value && value <= _limit);
return value >= 2 && _min_prime_factor[value] == value;
}
int min_prime_factor(int value) const {
assert(2 <= value && value <= _limit);
return _min_prime_factor[value];
}
std::vector<std::pair<int, int>> factorize(int value) const {
assert(1 <= value && value <= _limit);
std::vector<std::pair<int, int>> result;
while (value > 1) {
const int prime = _min_prime_factor[value];
int exponent = 0;
do {
value /= prime;
exponent++;
} while (value > 1 && _min_prime_factor[value] == prime);
result.emplace_back(prime, exponent);
}
return result;
}
std::vector<int> divisors(int value) const {
std::vector<int> result = {1};
for (const auto& factor : factorize(value)) {
const int current_size = int(result.size());
int power = 1;
for (int exponent = 1; exponent <= factor.second; exponent++) {
power *= factor.first;
for (int i = 0; i < current_size; i++) {
result.push_back(result[i] * power);
}
}
}
std::sort(result.begin(), result.end());
return result;
}
int totient(int value) const {
assert(1 <= value && value <= _limit);
int result = value;
for (const auto& factor : factorize(value)) {
result = result / factor.first * (factor.first - 1);
}
return result;
}
int mobius(int value) const {
assert(1 <= value && value <= _limit);
int result = 1;
for (const auto& factor : factorize(value)) {
if (factor.second >= 2) return 0;
result = -result;
}
return result;
}
std::vector<int> totient_table() const {
std::vector<int> result(_limit + 1);
if (_limit >= 1) result[1] = 1;
for (int value = 2; value <= _limit; value++) {
const int prime = _min_prime_factor[value];
const int reduced = value / prime;
result[value] = reduced % prime == 0 ? result[reduced] * prime : result[reduced] * (prime - 1);
}
return result;
}
std::vector<int> mobius_table() const {
std::vector<int> result(_limit + 1);
if (_limit >= 1) result[1] = 1;
for (int value = 2; value <= _limit; value++) {
const int prime = _min_prime_factor[value];
const int reduced = value / prime;
result[value] = reduced % prime == 0 ? 0 : -result[reduced];
}
return result;
}
};
} // namespace math
} // namespace m1une
#line 1 "math/rational.hpp"
#line 7 "math/rational.hpp"
#include <compare>
#line 11 "math/rational.hpp"
#include <sstream>
#line 15 "math/rational.hpp"
namespace m1une {
namespace math {
namespace rational_detail {
template <class T>
concept IntegerLike =
std::signed_integral<T> ||
(!std::integral<T> && std::copyable<T> && requires(T first, T second) {
T(0);
T(1);
{ -first } -> std::same_as<T>;
{ first + second } -> std::same_as<T>;
{ first - second } -> std::same_as<T>;
{ first * second } -> std::same_as<T>;
{ first / second } -> std::same_as<T>;
{ first % second } -> std::same_as<T>;
{ first += second } -> std::same_as<T&>;
{ first -= second } -> std::same_as<T&>;
{ first /= second } -> std::same_as<T&>;
{ first == second } -> std::convertible_to<bool>;
{ first < second } -> std::convertible_to<bool>;
});
} // namespace rational_detail
template <rational_detail::IntegerLike T = long long>
struct Rational {
static_assert(!std::signed_integral<T> || sizeof(T) <= sizeof(long long));
private:
static constexpr bool BUILTIN_INTEGER = std::signed_integral<T>;
using Wide = std::conditional_t<BUILTIN_INTEGER, __int128_t, T>;
using Magnitude = std::conditional_t<BUILTIN_INTEGER, __uint128_t, T>;
T _numerator;
T _denominator;
static constexpr Magnitude magnitude(Wide value) {
if constexpr (BUILTIN_INTEGER) {
if (value < 0) {
return static_cast<Magnitude>(-(value + 1)) + 1;
}
return static_cast<Magnitude>(value);
} else {
return value < 0 ? -value : value;
}
}
static constexpr Magnitude gcd(Magnitude first, Magnitude second) {
while (second != 0) {
Magnitude remainder = first % second;
first = second;
second = remainder;
}
return first;
}
static constexpr T narrow(Wide value) {
if constexpr (BUILTIN_INTEGER) {
assert(Wide(std::numeric_limits<T>::min()) <= value);
assert(value <= Wide(std::numeric_limits<T>::max()));
return static_cast<T>(value);
} else {
return value;
}
}
constexpr void assign_normalized(Wide numerator, Wide denominator) {
assert(denominator != 0);
if (numerator == 0) {
_numerator = 0;
_denominator = 1;
return;
}
Magnitude divisor = gcd(magnitude(numerator), magnitude(denominator));
numerator /= static_cast<Wide>(divisor);
denominator /= static_cast<Wide>(divisor);
if (denominator < 0) {
numerator = -numerator;
denominator = -denominator;
}
_numerator = narrow(numerator);
_denominator = narrow(denominator);
}
static constexpr Rational from_wide(Wide numerator, Wide denominator) {
Rational result;
result.assign_normalized(numerator, denominator);
return result;
}
static std::pair<long double, long long> decimal_scientific(const T& value) {
std::ostringstream output;
output << value;
const std::string text = output.str();
std::size_t begin = 0;
int sign = 1;
if (!text.empty() && (text[0] == '-' || text[0] == '+')) {
if (text[0] == '-') sign = -1;
begin = 1;
}
while (begin < text.size() && text[begin] == '0') ++begin;
if (begin == text.size()) return std::make_pair(0.0L, 0LL);
constexpr int DIGITS = std::numeric_limits<long double>::digits10 + 1;
const std::size_t used = std::min<std::size_t>(DIGITS, text.size() - begin);
long double significand = 0;
for (std::size_t i = 0; i < used; ++i) {
assert('0' <= text[begin + i] && text[begin + i] <= '9');
significand = significand * 10 + (text[begin + i] - '0');
}
for (std::size_t i = 1; i < used; ++i) significand /= 10;
const long long exponent = static_cast<long long>(text.size() - begin - 1);
return std::make_pair(sign * significand, exponent);
}
public:
constexpr Rational() : _numerator(0), _denominator(1) {}
constexpr Rational(T integer) : _numerator(integer), _denominator(1) {}
template <std::integral U>
requires std::constructible_from<T, U> &&
(!std::same_as<std::remove_cv_t<U>, T>)
constexpr Rational(U integer) : Rational(T(integer)) {}
constexpr Rational(T numerator, T denominator) {
assign_normalized(Wide(numerator), Wide(denominator));
}
constexpr T numerator() const {
return _numerator;
}
constexpr T denominator() const {
return _denominator;
}
constexpr bool is_integer() const {
return _denominator == 1;
}
constexpr int sign() const {
return (_numerator > 0) - (_numerator < 0);
}
constexpr Rational reciprocal() const {
assert(_numerator != 0);
return from_wide(Wide(_denominator), Wide(_numerator));
}
constexpr Rational abs() const {
return _numerator < 0 ? -*this : *this;
}
constexpr long double to_long_double() const
requires requires(const T& value) { static_cast<long double>(value); }
{
return static_cast<long double>(_numerator) / static_cast<long double>(_denominator);
}
long double to_long_double() const
requires(!requires(const T& value) { static_cast<long double>(value); })
{
const auto [numerator, numerator_exponent] = decimal_scientific(_numerator);
const auto [denominator, denominator_exponent] = decimal_scientific(_denominator);
return numerator / denominator *
std::pow(10.0L, numerator_exponent - denominator_exponent);
}
template <std::floating_point F>
explicit constexpr operator F() const
requires requires(const T& value) { static_cast<long double>(value); }
{
return static_cast<F>(to_long_double());
}
template <std::floating_point F>
explicit operator F() const
requires(!requires(const T& value) { static_cast<long double>(value); })
{
return static_cast<F>(to_long_double());
}
constexpr T trunc() const {
return _numerator / _denominator;
}
constexpr T floor() const {
T quotient = _numerator / _denominator;
if (_numerator < 0 && _numerator % _denominator != 0) quotient -= T(1);
return quotient;
}
constexpr T ceil() const {
T quotient = _numerator / _denominator;
if (0 < _numerator && _numerator % _denominator != 0) quotient += T(1);
return quotient;
}
constexpr Rational operator+() const {
return *this;
}
constexpr Rational operator-() const {
return from_wide(-Wide(_numerator), Wide(_denominator));
}
constexpr Rational& operator+=(const Rational& other) {
Magnitude common =
gcd(static_cast<Magnitude>(_denominator), static_cast<Magnitude>(other._denominator));
Wide left_scale = Wide(other._denominator) / static_cast<Wide>(common);
Wide right_scale = Wide(_denominator) / static_cast<Wide>(common);
Wide numerator =
Wide(_numerator) * left_scale + Wide(other._numerator) * right_scale;
// With both operands already reduced, every factor shared by the new
// numerator and denominator must divide `common`. Restricting the
// second gcd to that value avoids a full-size gcd against the product
// of both denominators, which is especially important for BigInt.
Magnitude reduction = common == Magnitude(1)
? Magnitude(1)
: gcd(magnitude(numerator), common);
if (reduction != Magnitude(1)) {
numerator /= static_cast<Wide>(reduction);
}
Wide remaining_denominator = Wide(other._denominator);
if (reduction != Magnitude(1)) {
remaining_denominator /= static_cast<Wide>(reduction);
}
_numerator = narrow(numerator);
_denominator = narrow(right_scale * remaining_denominator);
return *this;
}
constexpr Rational& operator-=(const Rational& other) {
return *this += -other;
}
constexpr Rational& operator*=(const Rational& other) {
Magnitude first_gcd = gcd(magnitude(Wide(_numerator)), static_cast<Magnitude>(other._denominator));
Magnitude second_gcd = gcd(magnitude(Wide(other._numerator)), static_cast<Magnitude>(_denominator));
assign_normalized((Wide(_numerator) / static_cast<Wide>(first_gcd)) *
(Wide(other._numerator) / static_cast<Wide>(second_gcd)),
(Wide(_denominator) / static_cast<Wide>(second_gcd)) *
(Wide(other._denominator) / static_cast<Wide>(first_gcd)));
return *this;
}
constexpr Rational& operator/=(const Rational& other) {
return *this *= other.reciprocal();
}
friend constexpr Rational operator+(Rational left, const Rational& right) {
return left += right;
}
friend constexpr Rational operator-(Rational left, const Rational& right) {
return left -= right;
}
friend constexpr Rational operator*(Rational left, const Rational& right) {
return left *= right;
}
friend constexpr Rational operator/(Rational left, const Rational& right) {
return left /= right;
}
friend constexpr bool operator==(const Rational& left, const Rational& right) {
return left._numerator == right._numerator && left._denominator == right._denominator;
}
friend constexpr std::strong_ordering operator<=>(const Rational& left, const Rational& right) {
Wide first = Wide(left._numerator) * Wide(right._denominator);
Wide second = Wide(right._numerator) * Wide(left._denominator);
if (first < second) return std::strong_ordering::less;
if (second < first) return std::strong_ordering::greater;
return std::strong_ordering::equal;
}
friend std::ostream& operator<<(std::ostream& output, const Rational& value) {
output << value._numerator;
if (value._denominator != 1) {
output << '/' << value._denominator;
}
return output;
}
friend std::istream& operator>>(std::istream& input, Rational& value) {
std::string token;
if (!(input >> token)) return input;
std::size_t slash = token.find('/');
if (slash != std::string::npos && token.find('/', slash + 1) != std::string::npos) {
input.setstate(std::ios::failbit);
return input;
}
T numerator = 0;
T denominator = 1;
std::istringstream numerator_input(token.substr(0, slash));
if (!(numerator_input >> numerator) || numerator_input.peek() != std::char_traits<char>::eof()) {
input.setstate(std::ios::failbit);
return input;
}
if (slash != std::string::npos) {
std::istringstream denominator_input(token.substr(slash + 1));
if (!(denominator_input >> denominator) ||
denominator_input.peek() != std::char_traits<char>::eof()) {
input.setstate(std::ios::failbit);
return input;
}
}
value = Rational(numerator, denominator);
return input;
}
};
template <rational_detail::IntegerLike T>
constexpr Rational<T> abs(const Rational<T>& value) {
return value.abs();
}
} // namespace math
} // namespace m1une
namespace std {
// Integer/rational common types already follow from implicit integer
// construction. Mixing a floating scalar explicitly chooses approximation.
template <m1une::math::rational_detail::IntegerLike T, floating_point F>
struct common_type<m1une::math::Rational<T>, F> {
using type = long double;
};
template <floating_point F, m1une::math::rational_detail::IntegerLike T>
struct common_type<F, m1une::math::Rational<T>> {
using type = long double;
};
} // namespace std
#line 1 "math/rational_approximation.hpp"
#line 10 "math/rational_approximation.hpp"
namespace m1une {
namespace math {
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
struct RationalApproximationResult {
using fraction_type = std::pair<T, T>;
fraction_type lower;
fraction_type upper;
};
namespace rational_approximation_detail {
using Wide = __uint128_t;
struct Fraction {
Wide numerator;
Wide denominator;
};
inline bool equal(
const Fraction& fraction,
Wide numerator,
Wide denominator
) {
return fraction.numerator * denominator ==
numerator * fraction.denominator;
}
inline Wide coordinate_bound(
Wide maximum,
Wide offset,
Wide step
) {
return step == 0 ? maximum : (maximum - offset) / step;
}
inline Fraction multiply_add(
const Fraction& first,
Wide multiplier,
const Fraction& second
) {
return {
first.numerator * multiplier + second.numerator,
first.denominator * multiplier + second.denominator
};
}
} // namespace rational_approximation_detail
// Returns the closest reduced fractions below and above numerator/denominator
// whose positive numerator and denominator are both at most maximum.
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
RationalApproximationResult<T> rational_approximation(
T maximum,
T numerator,
T denominator
) {
assert(maximum > 0);
assert(numerator > 0);
assert(denominator > 0);
static_assert(sizeof(T) <= sizeof(uint64_t));
using rational_approximation_detail::Fraction;
using rational_approximation_detail::Wide;
using rational_approximation_detail::coordinate_bound;
using rational_approximation_detail::equal;
using rational_approximation_detail::multiply_add;
const Wide limit = static_cast<std::make_unsigned_t<T>>(maximum);
const Wide target_numerator =
static_cast<std::make_unsigned_t<T>>(numerator);
const Wide target_denominator =
static_cast<std::make_unsigned_t<T>>(denominator);
Fraction lower{0, 1};
Fraction upper{1, 0};
while (true) {
Wide lower_multiplier = limit;
lower_multiplier = std::min(
lower_multiplier,
coordinate_bound(limit, lower.numerator, upper.numerator)
);
lower_multiplier = std::min(
lower_multiplier,
coordinate_bound(limit, lower.denominator, upper.denominator)
);
const Wide lower_slack =
target_numerator * lower.denominator -
lower.numerator * target_denominator;
const Wide lower_step =
upper.numerator * target_denominator -
target_numerator * upper.denominator;
assert(lower_step != 0);
lower_multiplier = std::min(
lower_multiplier, lower_slack / lower_step
);
lower = multiply_add(upper, lower_multiplier, lower);
if (equal(lower, target_numerator, target_denominator)) {
upper = lower;
break;
}
Wide upper_multiplier = limit;
upper_multiplier = std::min(
upper_multiplier,
coordinate_bound(limit, upper.numerator, lower.numerator)
);
upper_multiplier = std::min(
upper_multiplier,
coordinate_bound(limit, upper.denominator, lower.denominator)
);
const Wide upper_slack =
upper.numerator * target_denominator -
target_numerator * upper.denominator;
const Wide upper_step =
target_numerator * lower.denominator -
lower.numerator * target_denominator;
assert(upper_step != 0);
upper_multiplier = std::min(
upper_multiplier, upper_slack / upper_step
);
upper = multiply_add(lower, upper_multiplier, upper);
if (equal(upper, target_numerator, target_denominator)) {
lower = upper;
break;
}
if (lower_multiplier == 0 && upper_multiplier == 0) break;
}
RationalApproximationResult<T> result;
result.lower = {
static_cast<T>(lower.numerator),
static_cast<T>(lower.denominator)
};
result.upper = {
static_cast<T>(upper.numerator),
static_cast<T>(upper.denominator)
};
return result;
}
} // namespace math
} // namespace m1une
#line 1 "math/repunit.hpp"
#line 9 "math/repunit.hpp"
namespace m1une {
namespace math {
template <class T>
constexpr std::pair<T, T> repunit_and_power(
std::uint64_t length,
T base = T(10)
) {
T result = T(0);
T result_power = T(1);
T block = T(1);
T block_power = base;
while (length > 0) {
if (length & 1) {
result = result * block_power + block;
result_power = result_power * block_power;
}
block = block * block_power + block;
block_power = block_power * block_power;
length >>= 1;
}
return std::make_pair(result, result_power);
}
// Returns 1 + base + ... + base^(length - 1).
// The arithmetic, including any modular reduction, is performed by T.
template <class T>
constexpr T repunit(std::uint64_t length, T base = T(10)) {
return repunit_and_power<T>(length, base).first;
}
template <class T>
constexpr T repdigit(std::uint64_t length, T digit, T base = T(10)) {
return digit * repunit<T>(length, base);
}
template <class T>
constexpr T concatenate_digits(
T left,
T right,
std::uint64_t right_length,
T base = T(10)
) {
return left * repunit_and_power<T>(right_length, base).second + right;
}
namespace repunit_detail {
inline std::uint64_t multiply_mod(
std::uint64_t left,
std::uint64_t right,
std::uint64_t mod
) {
return static_cast<std::uint64_t>(
static_cast<unsigned __int128>(left) * right % mod
);
}
inline std::pair<std::uint64_t, std::uint64_t> repunit_and_power_mod(
std::uint64_t length,
std::uint64_t base,
std::uint64_t mod
) {
if (mod == 1) return std::make_pair(0, 0);
std::uint64_t result = 0;
std::uint64_t result_power = 1;
std::uint64_t block = 1;
std::uint64_t block_power = base % mod;
while (length > 0) {
if (length & 1) {
result = (
static_cast<unsigned __int128>(result) * block_power + block
) % mod;
result_power = multiply_mod(result_power, block_power, mod);
}
block = (
static_cast<unsigned __int128>(block) * block_power + block
) % mod;
block_power = multiply_mod(block_power, block_power, mod);
length >>= 1;
}
return std::make_pair(result, result_power);
}
} // namespace repunit_detail
inline std::pair<std::uint64_t, std::uint64_t> repunit_and_power_mod(
std::uint64_t length,
std::uint64_t base,
std::uint64_t mod
) {
assert(mod >= 1);
return repunit_detail::repunit_and_power_mod(length, base, mod);
}
inline std::uint64_t repunit_mod(
std::uint64_t length,
std::uint64_t base,
std::uint64_t mod
) {
return repunit_and_power_mod(length, base, mod).first;
}
inline std::uint64_t repdigit_mod(
std::uint64_t length,
std::uint64_t digit,
std::uint64_t base,
std::uint64_t mod
) {
assert(mod >= 1);
if (mod == 1) return 0;
return repunit_detail::multiply_mod(
digit % mod,
repunit_mod(length, base, mod),
mod
);
}
inline std::uint64_t concatenate_digits_mod(
std::uint64_t left,
std::uint64_t right,
std::uint64_t right_length,
std::uint64_t base,
std::uint64_t mod
) {
assert(mod >= 1);
if (mod == 1) return 0;
const std::uint64_t power =
repunit_and_power_mod(right_length, base, mod).second;
return (
static_cast<unsigned __int128>(left % mod) * power + right % mod
) % mod;
}
inline std::optional<std::uint64_t> minimum_repunit_length(
std::uint64_t divisor,
std::uint64_t base = 10
) {
assert(divisor >= 1);
assert(base >= 2);
if (std::gcd(divisor, base) != 1) return std::nullopt;
std::uint64_t remainder = 0;
for (std::uint64_t length = 1; length <= divisor; length++) {
remainder = (
static_cast<unsigned __int128>(remainder) * base + 1
) % divisor;
if (remainder == 0) return length;
}
return std::nullopt;
}
} // namespace math
} // namespace m1une
#line 1 "math/set_power_series.hpp"
#line 8 "math/set_power_series.hpp"
#include <iterator>
#line 11 "math/set_power_series.hpp"
#line 1 "math/subset_convolution.hpp"
#line 10 "math/subset_convolution.hpp"
namespace m1une {
namespace math {
template <typename T>
std::vector<T> subset_convolution(
std::vector<T> first,
std::vector<T> second
) {
assert(first.size() == second.size());
if (first.empty()) return {};
assert((first.size() & (first.size() - 1)) == 0);
const std::size_t size = first.size();
std::size_t bit_count = 0;
while ((std::size_t(1) << bit_count) < size) ++bit_count;
const std::size_t rank_count = bit_count + 1;
std::vector<T> first_ranked(size * rank_count);
std::vector<T> second_ranked(size * rank_count);
for (std::size_t mask = 0; mask < size; ++mask) {
const std::size_t rank = std::popcount(mask);
first_ranked[mask * rank_count + rank] = std::move(first[mask]);
second_ranked[mask * rank_count + rank] = std::move(second[mask]);
}
for (std::size_t bit = 1; bit < size; bit <<= 1) {
for (std::size_t mask = 0; mask < size; ++mask) {
if ((mask & bit) == 0) continue;
const std::size_t destination = mask * rank_count;
const std::size_t source = (mask ^ bit) * rank_count;
for (std::size_t rank = 0; rank < rank_count; ++rank) {
first_ranked[destination + rank] +=
first_ranked[source + rank];
second_ranked[destination + rank] +=
second_ranked[source + rank];
}
}
}
std::vector<T> product(rank_count);
for (std::size_t mask = 0; mask < size; ++mask) {
for (T& value : product) value = T{};
const std::size_t offset = mask * rank_count;
const std::size_t rank_limit = std::popcount(mask);
for (std::size_t left = 0; left <= rank_limit; ++left) {
const std::size_t right_limit =
std::min(rank_limit, bit_count - left);
for (std::size_t right = 0; right <= right_limit; ++right) {
product[left + right] +=
first_ranked[offset + left] *
second_ranked[offset + right];
}
}
for (std::size_t rank = 0; rank < rank_count; ++rank) {
first_ranked[offset + rank] = std::move(product[rank]);
}
}
for (std::size_t bit = 1; bit < size; bit <<= 1) {
for (std::size_t mask = 0; mask < size; ++mask) {
if ((mask & bit) == 0) continue;
const std::size_t destination = mask * rank_count;
const std::size_t source = (mask ^ bit) * rank_count;
for (std::size_t rank = 0; rank < rank_count; ++rank) {
first_ranked[destination + rank] -=
first_ranked[source + rank];
}
}
}
std::vector<T> result(size);
for (std::size_t mask = 0; mask < size; ++mask) {
result[mask] = std::move(
first_ranked[mask * rank_count + std::popcount(mask)]
);
}
return result;
}
} // namespace math
} // namespace m1une
#line 13 "math/set_power_series.hpp"
namespace m1une {
namespace math {
namespace set_power_series_detail {
inline bool is_power_of_two(std::size_t size) {
return size != 0 && (size & (size - 1)) == 0;
}
template <class T>
std::vector<T> divide(
const std::vector<T>& numerator,
const std::vector<T>& denominator
) {
assert(numerator.size() == denominator.size());
assert(is_power_of_two(numerator.size()));
assert(denominator[0] != T{});
const std::size_t size = numerator.size();
const int bit_count = std::countr_zero(size);
const std::size_t rank_count = std::size_t(bit_count) + 1;
std::vector<T> denominator_ranked(size * rank_count);
std::vector<T> quotient_ranked(size * rank_count);
for (std::size_t mask = 0; mask < size; mask++) {
std::size_t rank = std::popcount(mask);
denominator_ranked[mask * rank_count + rank] = denominator[mask];
}
for (std::size_t bit = 1; bit < size; bit <<= 1) {
for (std::size_t mask = 0; mask < size; mask++) {
if ((mask & bit) == 0) continue;
std::size_t source_mask = mask ^ bit;
std::size_t source = source_mask * rank_count;
std::size_t destination = mask * rank_count;
std::size_t rank_limit = std::popcount(source_mask);
for (std::size_t rank = 0; rank <= rank_limit; rank++) {
denominator_ranked[destination + rank] +=
denominator_ranked[source + rank];
}
}
}
const T inverse_constant = T(1) / denominator[0];
std::vector<T> transformed_product(size);
std::vector<T> quotient(size);
for (int rank = 0; rank <= bit_count; rank++) {
std::fill(
transformed_product.begin(),
transformed_product.end(),
T{}
);
for (std::size_t mask = 0; mask < size; mask++) {
std::size_t offset = mask * rank_count;
for (int left_rank = 0; left_rank <= rank; left_rank++) {
transformed_product[mask] +=
denominator_ranked[offset + left_rank] *
quotient_ranked[offset + rank - left_rank];
}
}
for (std::size_t bit = 1; bit < size; bit <<= 1) {
for (std::size_t mask = 0; mask < size; mask++) {
if (mask & bit) {
transformed_product[mask] -=
transformed_product[mask ^ bit];
}
}
}
for (std::size_t mask = 0; mask < size; mask++) {
if (int(std::popcount(mask)) != rank) continue;
quotient[mask] =
(numerator[mask] - transformed_product[mask]) *
inverse_constant;
quotient_ranked[mask * rank_count + rank] = quotient[mask];
}
for (std::size_t bit = 1; bit < size; bit <<= 1) {
for (std::size_t mask = 0; mask < size; mask++) {
if (mask & bit) {
quotient_ranked[mask * rank_count + rank] +=
quotient_ranked[(mask ^ bit) * rank_count + rank];
}
}
}
}
return quotient;
}
template <class T>
std::vector<T> normalized_power(std::vector<T> series, T exponent) {
assert(is_power_of_two(series.size()));
assert(series[0] == T(1));
std::vector<T> logarithm(series.size());
logarithm[0] = T{};
for (std::size_t half = 1; half < series.size(); half <<= 1) {
std::vector<T> low(series.begin(), series.begin() + half);
std::vector<T> high(
series.begin() + half,
series.begin() + 2 * half
);
std::vector<T> next = divide(high, low);
std::move(next.begin(), next.end(), logarithm.begin() + half);
}
for (T& value : logarithm) value *= exponent;
std::vector<T> result(1, T(1));
result.reserve(series.size());
for (std::size_t half = 1; half < series.size(); half <<= 1) {
std::vector<T> high(
logarithm.begin() + half,
logarithm.begin() + 2 * half
);
std::vector<T> next = subset_convolution(std::move(high), result);
result.insert(
result.end(),
std::make_move_iterator(next.begin()),
std::make_move_iterator(next.end())
);
}
return result;
}
} // namespace set_power_series_detail
// Returns numerator / denominator under subset convolution.
template <class T>
std::vector<T> set_power_series_divide(
const std::vector<T>& numerator,
const std::vector<T>& denominator
) {
return set_power_series_detail::divide(numerator, denominator);
}
template <class T>
std::vector<T> set_power_series_inverse(const std::vector<T>& series) {
assert(set_power_series_detail::is_power_of_two(series.size()));
std::vector<T> identity(series.size());
identity[0] = T(1);
return set_power_series_divide(identity, series);
}
template <class T>
std::vector<T> set_power_series_exp(const std::vector<T>& series) {
assert(set_power_series_detail::is_power_of_two(series.size()));
assert(series[0] == T{});
std::vector<T> result(1, T(1));
result.reserve(series.size());
for (std::size_t half = 1; half < series.size(); half <<= 1) {
std::vector<T> high(
series.begin() + half,
series.begin() + 2 * half
);
std::vector<T> next = subset_convolution(std::move(high), result);
result.insert(
result.end(),
std::make_move_iterator(next.begin()),
std::make_move_iterator(next.end())
);
}
return result;
}
template <class T>
std::vector<T> set_power_series_log(const std::vector<T>& series) {
assert(set_power_series_detail::is_power_of_two(series.size()));
assert(series[0] == T(1));
std::vector<T> result(series.size());
for (std::size_t half = 1; half < series.size(); half <<= 1) {
std::vector<T> low(series.begin(), series.begin() + half);
std::vector<T> high(
series.begin() + half,
series.begin() + 2 * half
);
std::vector<T> next = set_power_series_divide(high, low);
std::move(next.begin(), next.end(), result.begin() + half);
}
return result;
}
template <class T>
std::vector<T> set_power_series_pow(
const std::vector<T>& series,
long long exponent
) {
return set_power_series_detail::normalized_power(
series,
T(exponent)
);
}
template <class T>
std::vector<T> set_power_series_sqrt(const std::vector<T>& series) {
return set_power_series_detail::normalized_power(
series,
T(1) / T(2)
);
}
} // namespace math
} // namespace m1une
#line 1 "math/squarefree_count.hpp"
#line 9 "math/squarefree_count.hpp"
#line 11 "math/squarefree_count.hpp"
namespace m1une {
namespace math {
namespace squarefree_count_detail {
inline std::vector<int> mobius_prefix(int limit) {
std::vector<std::int8_t> mobius(limit + 1, 0);
if (limit >= 1) mobius[1] = 1;
{
std::vector<int> primes;
std::vector<bool> composite(limit + 1, false);
for (int value = 2; value <= limit; value++) {
if (!composite[value]) {
primes.push_back(value);
mobius[value] = -1;
}
for (int prime : primes) {
if (prime > limit / value) break;
const int product = prime * value;
composite[product] = true;
if (value % prime == 0) {
mobius[product] = 0;
break;
}
mobius[product] = static_cast<std::int8_t>(-mobius[value]);
}
}
}
std::vector<int> prefix(limit + 1, 0);
for (int value = 1; value <= limit; value++) {
prefix[value] = prefix[value - 1] + mobius[value];
}
return prefix;
}
} // namespace squarefree_count_detail
// Returns the number of square-free positive integers not greater than n.
inline uint64_t count_squarefree(uint64_t n) {
if (n == 0) return 0;
const uint64_t split = std::max<uint64_t>(1, floor_kth_root(n, 5U));
const uint64_t sieve_limit_u64 = isqrt(n / split);
assert(
sieve_limit_u64 <=
static_cast<uint64_t>(std::numeric_limits<int>::max())
);
const int sieve_limit = static_cast<int>(sieve_limit_u64);
const std::vector<int> mertens =
squarefree_count_detail::mobius_prefix(sieve_limit);
__int128_t direct_sum = 0;
for (int divisor = 1; divisor <= sieve_limit; divisor++) {
direct_sum += static_cast<__int128_t>(
n / static_cast<uint64_t>(divisor) /
static_cast<uint64_t>(divisor)
) * (mertens[divisor] - mertens[divisor - 1]);
}
std::vector<std::int64_t> large_mertens;
large_mertens.reserve(split - 1);
std::int64_t large_mertens_sum = 0;
for (uint64_t index = split; index-- > 1;) {
const uint64_t argument = isqrt(n / index);
const uint64_t square_root = isqrt(argument);
std::int64_t value = 1;
const uint64_t small_quotient_limit =
argument / (square_root + 1);
for (uint64_t quotient = 1;
quotient <= small_quotient_limit;
quotient++) {
const uint64_t multiplicity =
argument / quotient - argument / (quotient + 1);
value -= static_cast<std::int64_t>(multiplicity) *
mertens[static_cast<int>(quotient)];
}
for (uint64_t divisor = 2; divisor <= square_root; divisor++) {
const uint64_t quotient = argument / divisor;
if (quotient <= sieve_limit_u64) {
value -= mertens[static_cast<int>(quotient)];
} else {
const uint64_t previous_argument =
index * divisor * divisor;
assert(previous_argument < split);
const uint64_t position = split - previous_argument - 1;
assert(position < large_mertens.size());
value -= large_mertens[position];
}
}
large_mertens.push_back(value);
large_mertens_sum += value;
}
const std::int64_t grouped_sum =
large_mertens_sum -
static_cast<std::int64_t>(split - 1) * mertens[sieve_limit];
const __int128_t answer = direct_sum + grouped_sum;
assert(answer >= 0);
assert(answer <= std::numeric_limits<uint64_t>::max());
return static_cast<uint64_t>(answer);
}
} // namespace math
} // namespace m1une
#line 1 "math/stern_brocot_tree.hpp"
#line 10 "math/stern_brocot_tree.hpp"
#line 12 "math/stern_brocot_tree.hpp"
namespace m1une {
namespace math {
enum class SternBrocotDirection {
Left,
Right,
};
struct SternBrocotRun {
SternBrocotDirection direction;
uint64_t count;
friend bool operator==(const SternBrocotRun&, const SternBrocotRun&) = default;
};
struct SternBrocotPath {
std::vector<SternBrocotRun> runs;
bool empty() const {
return runs.empty();
}
uint64_t depth() const {
uint64_t result = 0;
for (const SternBrocotRun& run : runs) {
assert(run.count <= std::numeric_limits<uint64_t>::max() - result);
result += run.count;
}
return result;
}
void push(SternBrocotDirection direction, uint64_t count = 1) {
if (count == 0) return;
if (!runs.empty() && runs.back().direction == direction) {
assert(count <= std::numeric_limits<uint64_t>::max() - runs.back().count);
runs.back().count += count;
} else {
runs.push_back(SternBrocotRun{direction, count});
}
}
bool move_up(uint64_t count = 1) {
if (depth() < count) return false;
while (count > 0) {
SternBrocotRun& run = runs.back();
uint64_t removed = run.count < count ? run.count : count;
run.count -= removed;
count -= removed;
if (run.count == 0) runs.pop_back();
}
return true;
}
SternBrocotPath ancestor(uint64_t count) const {
SternBrocotPath result = *this;
[[maybe_unused]] bool valid = result.move_up(count);
assert(valid);
return result;
}
friend bool operator==(const SternBrocotPath&, const SternBrocotPath&) = default;
};
template <std::signed_integral T = long long>
struct SternBrocotBounds {
std::pair<T, T> left;
std::pair<T, T> right;
};
template <std::signed_integral T>
SternBrocotPath stern_brocot_path(T numerator, T denominator) {
assert(0 < numerator);
assert(0 < denominator);
SternBrocotPath result;
while (numerator != denominator) {
if (numerator < denominator) {
T count = (denominator - 1) / numerator;
result.push(SternBrocotDirection::Left, uint64_t(count));
denominator -= count * numerator;
} else {
T count = (numerator - 1) / denominator;
result.push(SternBrocotDirection::Right, uint64_t(count));
numerator -= count * denominator;
}
}
assert(numerator == 1);
return result;
}
template <std::signed_integral T = long long>
Rational<T> stern_brocot_decode(const SternBrocotPath& path) {
using Wide = __int128_t;
Wide left_numerator = 0;
Wide left_denominator = 1;
Wide right_numerator = 1;
Wide right_denominator = 0;
Wide numerator = 1;
Wide denominator = 1;
[[maybe_unused]] const Wide maximum = std::numeric_limits<T>::max();
for (const SternBrocotRun& run : path.runs) {
assert(run.count > 0);
assert(Wide(run.count) <= maximum);
Wide count = run.count;
if (run.direction == SternBrocotDirection::Left) {
right_numerator = numerator + (count - 1) * left_numerator;
right_denominator = denominator + (count - 1) * left_denominator;
numerator += count * left_numerator;
denominator += count * left_denominator;
} else {
left_numerator = numerator + (count - 1) * right_numerator;
left_denominator = denominator + (count - 1) * right_denominator;
numerator += count * right_numerator;
denominator += count * right_denominator;
}
assert(numerator <= maximum);
assert(denominator <= maximum);
assert(left_numerator <= maximum);
assert(left_denominator <= maximum);
assert(right_numerator <= maximum);
assert(right_denominator <= maximum);
}
return Rational<T>(T(numerator), T(denominator));
}
template <std::signed_integral T>
uint64_t stern_brocot_depth(T numerator, T denominator) {
return stern_brocot_path(numerator, denominator).depth();
}
inline SternBrocotPath stern_brocot_lca_path(
const SternBrocotPath& first,
const SternBrocotPath& second
) {
SternBrocotPath result;
int limit = int(first.runs.size() < second.runs.size() ? first.runs.size() : second.runs.size());
for (int i = 0; i < limit; i++) {
if (first.runs[i].direction != second.runs[i].direction) break;
uint64_t common =
first.runs[i].count < second.runs[i].count
? first.runs[i].count
: second.runs[i].count;
result.push(first.runs[i].direction, common);
if (first.runs[i].count != second.runs[i].count) break;
}
return result;
}
template <std::signed_integral T>
Rational<T> stern_brocot_lca(
T first_numerator,
T first_denominator,
T second_numerator,
T second_denominator
) {
SternBrocotPath path = stern_brocot_lca_path(
stern_brocot_path(first_numerator, first_denominator),
stern_brocot_path(second_numerator, second_denominator)
);
return stern_brocot_decode<T>(path);
}
template <std::signed_integral T>
Rational<T> stern_brocot_ancestor(T numerator, T denominator, uint64_t up) {
SternBrocotPath path = stern_brocot_path(numerator, denominator);
[[maybe_unused]] bool valid = path.move_up(up);
assert(valid);
return stern_brocot_decode<T>(path);
}
template <std::signed_integral T>
Rational<T> stern_brocot_parent(T numerator, T denominator) {
return stern_brocot_ancestor(numerator, denominator, 1);
}
template <std::signed_integral T>
Rational<T> stern_brocot_move(
T numerator,
T denominator,
SternBrocotDirection direction,
uint64_t count = 1
) {
SternBrocotPath path = stern_brocot_path(numerator, denominator);
path.push(direction, count);
return stern_brocot_decode<T>(path);
}
template <std::signed_integral T = long long>
SternBrocotBounds<T> stern_brocot_bounds(const SternBrocotPath& path) {
using Wide = __int128_t;
Wide left_numerator = 0;
Wide left_denominator = 1;
Wide right_numerator = 1;
Wide right_denominator = 0;
Wide numerator = 1;
Wide denominator = 1;
[[maybe_unused]] const Wide maximum = std::numeric_limits<T>::max();
for (const SternBrocotRun& run : path.runs) {
assert(run.count > 0);
assert(Wide(run.count) <= maximum);
Wide count = run.count;
if (run.direction == SternBrocotDirection::Left) {
right_numerator = numerator + (count - 1) * left_numerator;
right_denominator = denominator + (count - 1) * left_denominator;
numerator += count * left_numerator;
denominator += count * left_denominator;
} else {
left_numerator = numerator + (count - 1) * right_numerator;
left_denominator = denominator + (count - 1) * right_denominator;
numerator += count * right_numerator;
denominator += count * right_denominator;
}
assert(numerator <= maximum);
assert(denominator <= maximum);
assert(left_numerator <= maximum);
assert(left_denominator <= maximum);
assert(right_numerator <= maximum);
assert(right_denominator <= maximum);
}
SternBrocotBounds<T> result;
result.left = {T(left_numerator), T(left_denominator)};
result.right = {T(right_numerator), T(right_denominator)};
return result;
}
} // namespace math
} // namespace m1une
#line 1 "math/tetration.hpp"
#line 9 "math/tetration.hpp"
#line 11 "math/tetration.hpp"
namespace m1une {
namespace math {
namespace tetration_detail {
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
uint64_t to_uint64(T value) {
if constexpr (std::signed_integral<T>) {
assert(value >= 0);
}
return static_cast<uint64_t>(value);
}
inline uint64_t multiply_mod(uint64_t first, uint64_t second, uint64_t mod) {
return static_cast<uint64_t>(
static_cast<__uint128_t>(first) * second % mod
);
}
inline uint64_t pow_mod(uint64_t base, __uint128_t exponent, uint64_t mod) {
assert(mod >= 1);
if (mod == 1) return 0;
base %= mod;
uint64_t result = 1 % mod;
while (exponent > 0) {
if ((exponent & 1) != 0) result = multiply_mod(result, base, mod);
base = multiply_mod(base, base, mod);
exponent >>= 1;
}
return result;
}
inline uint64_t pow_bounded(uint64_t base, uint64_t exponent, uint64_t limit) {
if (limit == 0) return 0;
__uint128_t result = 1;
for (uint64_t i = 0; i < exponent; i++) {
result *= base;
if (result >= limit) return limit;
}
return static_cast<uint64_t>(result);
}
inline uint64_t exponent_threshold(uint64_t base, uint64_t limit) {
assert(base >= 2);
if (limit <= 1) return 0;
uint64_t exponent = 0;
uint64_t value = 1;
while (value < limit) {
exponent++;
if (value > limit / base) return exponent;
value *= base;
}
return exponent;
}
inline uint64_t tetration_bounded_unsigned(uint64_t base, uint64_t height, uint64_t limit) {
if (limit == 0) return 0;
if (height == 0) return limit < 1 ? limit : 1;
if (height == 1) return base < limit ? base : limit;
if (base == 0) {
const uint64_t value = (height & 1) == 0 ? 1 : 0;
return value < limit ? value : limit;
}
if (base == 1) return limit < 1 ? limit : 1;
const uint64_t threshold = exponent_threshold(base, limit);
const uint64_t exponent = tetration_bounded_unsigned(base, height - 1, threshold);
if (exponent >= threshold) return limit;
return pow_bounded(base, exponent, limit);
}
inline uint64_t tetration_mod_unsigned(uint64_t base, uint64_t height, uint64_t mod) {
assert(mod >= 1);
if (mod == 1) return 0;
if (height == 0) return 1 % mod;
if (height == 1) return base % mod;
if (base == 0) return (height & 1) == 0 ? 1 % mod : 0;
if (base == 1) return 1 % mod;
const uint64_t phi = euler_phi(mod);
uint64_t reduced_exponent = tetration_mod_unsigned(base, height - 1, phi);
__uint128_t exponent = reduced_exponent;
if (tetration_bounded_unsigned(base, height - 1, phi) >= phi) {
exponent += phi;
}
return pow_mod(base, exponent, mod);
}
inline uint64_t power_tower_bounded_unsigned(
const std::vector<uint64_t>& bases,
int index,
uint64_t limit
) {
if (limit == 0) return 0;
if (index == int(bases.size())) return limit < 1 ? limit : 1;
const uint64_t base = bases[index];
if (index + 1 == int(bases.size())) return base < limit ? base : limit;
if (base == 0) {
const uint64_t exponent = power_tower_bounded_unsigned(bases, index + 1, 1);
const uint64_t value = exponent == 0 ? 1 : 0;
return value < limit ? value : limit;
}
if (base == 1) return limit < 1 ? limit : 1;
const uint64_t threshold = exponent_threshold(base, limit);
const uint64_t exponent = power_tower_bounded_unsigned(bases, index + 1, threshold);
if (exponent >= threshold) return limit;
return pow_bounded(base, exponent, limit);
}
inline uint64_t power_tower_mod_unsigned(
const std::vector<uint64_t>& bases,
int index,
uint64_t mod
) {
assert(mod >= 1);
if (mod == 1) return 0;
if (index == int(bases.size())) return 1 % mod;
if (index + 1 == int(bases.size())) return bases[index] % mod;
const uint64_t phi = euler_phi(mod);
uint64_t reduced_exponent = power_tower_mod_unsigned(bases, index + 1, phi);
__uint128_t exponent = reduced_exponent;
if (power_tower_bounded_unsigned(bases, index + 1, phi) >= phi) {
exponent += phi;
}
return pow_mod(bases[index], exponent, mod);
}
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
std::vector<uint64_t> normalize_bases(const std::vector<T>& bases) {
std::vector<uint64_t> result;
result.reserve(bases.size());
for (T base : bases) result.push_back(to_uint64(base));
return result;
}
} // namespace tetration_detail
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
uint64_t tetration_mod(T base, uint64_t height, uint64_t mod) {
assert(mod >= 1);
return tetration_detail::tetration_mod_unsigned(
tetration_detail::to_uint64(base),
height,
mod
);
}
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
uint64_t tetration_bounded(T base, uint64_t height, uint64_t limit) {
return tetration_detail::tetration_bounded_unsigned(
tetration_detail::to_uint64(base),
height,
limit
);
}
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
uint64_t power_tower_mod(const std::vector<T>& bases, uint64_t mod) {
assert(mod >= 1);
std::vector<uint64_t> normalized = tetration_detail::normalize_bases(bases);
return tetration_detail::power_tower_mod_unsigned(normalized, 0, mod);
}
template <std::integral T>
requires(!std::same_as<std::remove_cv_t<T>, bool>)
uint64_t power_tower_bounded(const std::vector<T>& bases, uint64_t limit) {
std::vector<uint64_t> normalized = tetration_detail::normalize_bases(bases);
return tetration_detail::power_tower_bounded_unsigned(normalized, 0, limit);
}
} // namespace math
} // namespace m1une
#line 1 "math/totient_sum.hpp"
#line 8 "math/totient_sum.hpp"
namespace m1une {
namespace math {
struct TotientSum {
private:
int _precalculation_limit;
std::vector<__uint128_t> _prefix_sum;
std::unordered_map<uint64_t, __uint128_t> _memo;
__uint128_t solve(uint64_t n) {
if (n <= static_cast<uint64_t>(_precalculation_limit)) {
return _prefix_sum[static_cast<int>(n)];
}
if (auto it = _memo.find(n); it != _memo.end()) {
return it->second;
}
const __uint128_t wide_n = n;
__uint128_t result = wide_n * (wide_n + 1) / 2;
for (uint64_t left = 2; left <= n;) {
const uint64_t quotient = n / left;
const uint64_t right = n / quotient;
result -= static_cast<__uint128_t>(right - left + 1) * solve(quotient);
if (right == n) break;
left = right + 1;
}
_memo.emplace(n, result);
return result;
}
public:
explicit TotientSum(int precalculation_limit = 1000000) : _precalculation_limit(precalculation_limit) {
assert(precalculation_limit >= 0);
std::vector<uint64_t> phi(precalculation_limit + 1);
std::vector<int> primes;
std::vector<bool> is_composite(precalculation_limit + 1);
if (precalculation_limit >= 1) {
phi[1] = 1;
}
for (int value = 2; value <= precalculation_limit; value++) {
if (!is_composite[value]) {
primes.push_back(value);
phi[value] = static_cast<uint64_t>(value - 1);
}
for (int prime : primes) {
if (value > precalculation_limit / prime) break;
const int next = value * prime;
is_composite[next] = true;
if (value % prime == 0) {
phi[next] = phi[value] * static_cast<uint64_t>(prime);
break;
}
phi[next] = phi[value] * static_cast<uint64_t>(prime - 1);
}
}
_prefix_sum.assign(precalculation_limit + 1, 0);
for (int value = 1; value <= precalculation_limit; value++) {
_prefix_sum[value] = _prefix_sum[value - 1] + phi[value];
}
}
int precalculation_limit() const {
return _precalculation_limit;
}
// Returns sum_{i=1}^n phi(i).
__uint128_t prefix_sum(uint64_t n) {
return solve(n);
}
__uint128_t operator()(uint64_t n) {
return prefix_sum(n);
}
};
inline __uint128_t totient_sum(uint64_t n, int precalculation_limit = 1000000) {
TotientSum solver(precalculation_limit);
return solver.prefix_sum(n);
}
} // namespace math
} // namespace m1une
#line 1 "math/two_square_sum.hpp"
#line 9 "math/two_square_sum.hpp"
#line 12 "math/two_square_sum.hpp"
namespace m1une {
namespace math {
namespace two_square_sum_detail {
struct GaussianInteger {
__int128_t real;
__int128_t imaginary;
};
inline GaussianInteger multiply(GaussianInteger first, GaussianInteger second) {
return GaussianInteger{first.real * second.real - first.imaginary * second.imaginary,
first.real * second.imaginary + first.imaginary * second.real};
}
inline GaussianInteger power(GaussianInteger base, int exponent) {
GaussianInteger result{1, 0};
while (exponent > 0) {
if ((exponent & 1) != 0) result = multiply(result, base);
exponent >>= 1;
if (exponent != 0) base = multiply(base, base);
}
return result;
}
inline uint64_t absolute_value(__int128_t value) {
return static_cast<uint64_t>(value < 0 ? -value : value);
}
inline uint64_t pow_uint64(uint64_t base, int exponent) {
uint64_t result = 1;
while (exponent > 0) {
if ((exponent & 1) != 0) result *= base;
exponent >>= 1;
if (exponent != 0) base *= base;
}
return result;
}
inline GaussianInteger prime_one_mod_four_representation(uint64_t prime) {
assert(prime % 4 == 1);
uint64_t non_residue = 2;
while (internal::power_mod(non_residue, (prime - 1) / 2, prime) != prime - 1) {
non_residue++;
}
uint64_t root = internal::power_mod(non_residue, (prime - 1) / 4, prime);
uint64_t previous = prime;
uint64_t current = root;
while (static_cast<__uint128_t>(current) * current > prime) {
uint64_t next = previous % current;
previous = current;
current = next;
}
const uint64_t real = current;
const uint64_t remaining = prime - real * real;
const uint64_t imaginary = isqrt(remaining);
assert(imaginary * imaginary == remaining);
return GaussianInteger{static_cast<__int128_t>(real), static_cast<__int128_t>(imaginary)};
}
inline std::vector<GaussianInteger> prime_power_choices(GaussianInteger factor, int exponent) {
GaussianInteger conjugate{factor.real, -factor.imaginary};
std::vector<GaussianInteger> positive_powers(exponent + 1);
std::vector<GaussianInteger> negative_powers(exponent + 1);
positive_powers[0] = GaussianInteger{1, 0};
negative_powers[0] = GaussianInteger{1, 0};
for (int i = 0; i < exponent; i++) {
positive_powers[i + 1] = multiply(positive_powers[i], factor);
negative_powers[i + 1] = multiply(negative_powers[i], conjugate);
}
std::vector<GaussianInteger> result;
result.reserve(exponent + 1);
for (int take_positive = 0; take_positive <= exponent; take_positive++) {
result.push_back(multiply(positive_powers[take_positive], negative_powers[exponent - take_positive]));
}
return result;
}
inline void add_nonnegative_pairs(std::vector<std::pair<uint64_t, uint64_t>>& result, GaussianInteger value,
uint64_t scale) {
const uint64_t first = absolute_value(value.real) * scale;
const uint64_t second = absolute_value(value.imaginary) * scale;
result.emplace_back(first, second);
result.emplace_back(second, first);
}
} // namespace two_square_sum_detail
inline std::vector<std::pair<uint64_t, uint64_t>> two_square_sum(uint64_t value) {
using two_square_sum_detail::GaussianInteger;
if (value == 0) return std::vector<std::pair<uint64_t, uint64_t>>{std::pair<uint64_t, uint64_t>(0, 0)};
uint64_t real_scale = 1;
std::vector<std::vector<GaussianInteger>> choices_by_prime;
for (const auto& factor : prime_factorize(value)) {
const uint64_t prime = factor.first;
const int exponent = factor.second;
if (prime % 4 == 3) {
if ((exponent & 1) != 0) return {};
real_scale *= two_square_sum_detail::pow_uint64(prime, exponent / 2);
continue;
}
GaussianInteger gaussian_factor;
if (prime == 2) {
gaussian_factor = GaussianInteger{1, 1};
} else {
gaussian_factor = two_square_sum_detail::prime_one_mod_four_representation(prime);
}
choices_by_prime.push_back(two_square_sum_detail::prime_power_choices(gaussian_factor, exponent));
}
std::vector<GaussianInteger> values;
values.push_back(GaussianInteger{1, 0});
for (const std::vector<GaussianInteger>& choices : choices_by_prime) {
std::vector<GaussianInteger> next;
next.reserve(values.size() * choices.size());
for (GaussianInteger value_so_far : values) {
for (GaussianInteger choice : choices) {
next.push_back(two_square_sum_detail::multiply(value_so_far, choice));
}
}
values.swap(next);
}
std::vector<std::pair<uint64_t, uint64_t>> result;
result.reserve(values.size() * 2);
for (GaussianInteger gaussian : values) {
two_square_sum_detail::add_nonnegative_pairs(result, gaussian, real_scale);
}
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
return result;
}
inline std::vector<std::pair<uint64_t, uint64_t>> represent_as_two_square_sum(uint64_t value) {
return two_square_sum(value);
}
inline bool is_two_square_sum(uint64_t value) {
if (value == 0) return true;
for (const auto& factor : prime_factorize(value)) {
if (factor.first % 4 == 3 && (factor.second & 1) != 0) return false;
}
return true;
}
} // namespace math
} // namespace m1une
#line 1 "math/xor_basis.hpp"
#line 7 "math/xor_basis.hpp"
#include <initializer_list>
#line 11 "math/xor_basis.hpp"
namespace m1une {
namespace math {
template <std::unsigned_integral UInt = unsigned long long>
class XorBasis {
static_assert(!std::same_as<UInt, bool>);
public:
static constexpr int bit_width = std::numeric_limits<UInt>::digits;
XorBasis() = default;
template <std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
requires std::convertible_to<std::iter_reference_t<Iterator>, UInt>
XorBasis(Iterator first, Sentinel last) {
for (; first != last; ++first) insert(UInt(*first));
}
XorBasis(std::initializer_list<UInt> values)
: XorBasis(values.begin(), values.end()) {}
bool insert(UInt value) {
for (int bit = bit_width - 1; bit >= 0; bit--) {
if (((value >> bit) & UInt(1)) == 0) continue;
if (basis_[bit] != 0) {
value ^= basis_[bit];
continue;
}
for (int lower = bit - 1; lower >= 0; lower--) {
if (((value >> lower) & UInt(1)) != 0) value ^= basis_[lower];
}
basis_[bit] = value;
rank_++;
for (int higher = bit + 1; higher < bit_width; higher++) {
if (((basis_[higher] >> bit) & UInt(1)) != 0) {
basis_[higher] ^= value;
}
}
return true;
}
return false;
}
bool contains(UInt value) const {
for (int bit = bit_width - 1; bit >= 0; bit--) {
if (((value >> bit) & UInt(1)) != 0) value ^= basis_[bit];
}
return value == 0;
}
int rank() const {
return rank_;
}
bool empty() const {
return rank_ == 0;
}
void clear() {
basis_.fill(UInt(0));
rank_ = 0;
}
int merge(const XorBasis& other) {
const int old_rank = rank_;
for (UInt value : other.basis_) {
if (value != 0) insert(value);
}
return rank_ - old_rank;
}
UInt min_xor(UInt value = 0) const {
for (int bit = bit_width - 1; bit >= 0; bit--) {
if ((value ^ basis_[bit]) < value) value ^= basis_[bit];
}
return value;
}
UInt max_xor(UInt value = 0) const {
for (int bit = bit_width - 1; bit >= 0; bit--) {
if (value < (value ^ basis_[bit])) value ^= basis_[bit];
}
return value;
}
UInt kth_smallest(UInt index) const {
if (rank_ < bit_width) assert(index < (UInt(1) << rank_));
UInt result = 0;
int coefficient = 0;
for (int bit = 0; bit < bit_width; bit++) {
if (basis_[bit] == 0) continue;
if (((index >> coefficient) & UInt(1)) != 0) result ^= basis_[bit];
coefficient++;
}
return result;
}
std::vector<UInt> vectors() const {
std::vector<UInt> result;
result.reserve(rank_);
for (UInt value : basis_) {
if (value != 0) result.push_back(value);
}
return result;
}
private:
std::array<UInt, bit_width> basis_{};
int rank_ = 0;
};
} // namespace math
} // namespace m1une
#line 44 "math/all.hpp"