// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // ntt.hpp — NTT (Number Theoretic Transform) public API // // The implementation is compiled into sangi_ntt.lib (src/math/fft/ntt.cpp). // Callers must link against sangi_ntt. #pragma once #include #include namespace sangi { namespace prime_ntt { struct NttPrime; } class Ntt { public: struct Prime { uint64_t p; uint64_t g; size_t maxNttSize; }; // P1 = 30 * 2^47 + 1, g=19 // P2 = 345 * 2^47 + 1, g=13 // P3 = 420 * 2^47 + 1, g=17 static constexpr Prime prime1() { return { 0x000F'0000'0000'0001ULL, 19, size_t(1) << 47 }; } static constexpr Prime prime2() { return { 0x00AC'8000'0000'0001ULL, 13, size_t(1) << 47 }; } static constexpr Prime prime3() { return { 0x00D2'0000'0000'0001ULL, 17, size_t(1) << 47 }; } static size_t nextSmoothSize(size_t n); static bool isSmooth(size_t n) { if (n == 0) return false; while (n % 2 == 0) n /= 2; return n == 1 || n == 3 || n == 5; } static bool isValidSize(size_t N, const Prime& prime) { return N > 0 && (prime.p - 1) % N == 0; } static void forward(uint64_t* data, size_t N, const Prime& prime); static void inverse(uint64_t* data, size_t N, const Prime& prime); static void pointwiseMul(uint64_t* r, const uint64_t* a, const uint64_t* b, size_t N, const Prime& prime); static void polyMul(uint64_t* r, const uint64_t* a, size_t an, const uint64_t* b, size_t bn, const Prime& prime); private: static prime_ntt::NttPrime makePrime(const Prime& prime); static void forwardSmooth(uint64_t* data, size_t N, const Prime& prime); static void inverseSmooth(uint64_t* data, size_t N, const Prime& prime); static void bluestein(uint64_t* data, size_t N, const Prime& prime, bool is_inverse); static void forwardMont(uint64_t* data, size_t N, const prime_ntt::NttPrime& nttPrime); static void inverseMont(uint64_t* data, size_t N, const prime_ntt::NttPrime& nttPrime); }; } // namespace sangi