Multiplication modulo 2^N + 1
#include <flint/fft_small.h>sd_fft_mpn_mulmod_2expp1_ctx_structsd_fft_mpn_mulmod_2expp1_ctx_tsd_fft_mpn_mulmod_2expp1_scratch_structsd_fft_mpn_mulmod_2expp1_scratch_tA context for one ring , holding , the digit size , the digit count , the number of primes and the twisting tables, and a scratch object with the mutable state of one multiplication.
void sd_fft_mpn_mulmod_2expp1_ctx_init(sd_fft_mpn_mulmod_2expp1_ctx_struct * C, mpn_ctx_struct * R, slong N, slong m)sourcevoid sd_fft_mpn_mulmod_2expp1_ctx_clear(sd_fft_mpn_mulmod_2expp1_ctx_struct * C)sourceInitialises C for multiplication modulo with digits of bits, taking the primes from R, normally get_default_mpn_ctx(), and frees it. must be a positive multiple of FLINT_BITS, and a power of two at least 16 dividing , or 0 to let the function choose. Throws if is not a positive multiple of FLINT_BITS or no configuration exists.
The number of primes is the least whose product exceeds and for which digits of bits can be converted: any , or with a vectorised conversion for primes, which exists for and suitable . With the digit count is chosen among the powers of two with , and by an estimated cost , with weights measured per prime count. For example give with three primes, gives with four primes, and gives with two primes. That is a multiple of FLINT_BITS is not checked: other values give wrong results.
void sd_fft_mpn_mulmod_2expp1_scratch_init(sd_fft_mpn_mulmod_2expp1_scratch_struct * S, const sd_fft_mpn_mulmod_2expp1_ctx_struct * C)sourcevoid sd_fft_mpn_mulmod_2expp1_scratch_clear(sd_fft_mpn_mulmod_2expp1_scratch_struct * S)sourceInitialise and free the scratch object for multiplications with C. The context is read-only during multiplication, so several threads can share it, each with its own scratch object.
void sd_fft_mpn_mulmod_2expp1(sd_fft_mpn_mulmod_2expp1_ctx_struct * C, nn_ptr z, nn_srcptr x, nn_srcptr y, sd_fft_mpn_mulmod_2expp1_scratch_struct * S)sourceSets . z may alias x or y; if x == y a square is computed with one forward transform.
slong sd_fft_mpn_mulmod_2expp1_transformed_size(const sd_fft_mpn_mulmod_2expp1_ctx_struct * C)sourcevoid sd_fft_mpn_mulmod_2expp1_transform(sd_fft_mpn_mulmod_2expp1_ctx_struct * C, double * F, nn_srcptr y, sd_fft_mpn_mulmod_2expp1_scratch_struct * S)sourcevoid sd_fft_mpn_mulmod_2expp1_mul_cached(sd_fft_mpn_mulmod_2expp1_ctx_struct * C, nn_ptr z, nn_srcptr x, const double * F, sd_fft_mpn_mulmod_2expp1_scratch_struct * S)sourceMultiplication by a fixed operand. transform stores the transform of in F, an array of transformed_size(C) doubles; mul_cached then sets , like sd_fft_mpn_mulmod_2expp1 with one forward transform less.
slong sd_fft_mpn_mulmod_2expp1_choose_np(mpn_ctx_struct * R, slong b, slong depth)sourceReturns the number of primes the context uses for digits of bits and digits, as described for the context, or if there is none.
Schönhage–Strassen multiplication reduces a large product to many products modulo for a moderately large (Schönhage–Strassen multiplication). The fft module computes these pointwise products with the functions here when the ring has at least 16384 bits (FLINT_FFT_SD_FFT_MIN_RING), choosing with fft_adjust_limbs_sd_fft so that a good digit splitting exists. They compute a product modulo as a negacyclic convolution of -bit digits with the small-prime transforms.
Usage#
- Create a context for with
sd_fft_mpn_mulmod_2expp1_ctx_init, passingget_default_mpn_ctx()and to let it choose the digit count. must be a positive multiple ofFLINT_BITS; otherwise an exception is raised. - Create one scratch object per thread with
sd_fft_mpn_mulmod_2expp1_scratch_init. The context is read-only during multiplications and can be shared. - Multiply with
sd_fft_mpn_mulmod_2expp1. Operands and result have limbs, ; operands may have top limb 0 or 1 with arbitrary low limbs, and the result is fully reduced into . The result may alias an operand. - For many products with the same , store its transform once with
sd_fft_mpn_mulmod_2expp1_transformin an array ofsd_fft_mpn_mulmod_2expp1_transformed_size(C)doubles, aligned to 32 bytes, and multiply withsd_fft_mpn_mulmod_2expp1_mul_cached.
Algorithm#
Write with digits , where and a top limb 1 contributes , which is subtracted from . Since , the product is with the negacyclic convolution
where . Modulo each prime , with a primitive -th root of unity (), the weighted sequences and have the cyclic convolution [CF1994], which a transform of length computes. The residues are untwisted by , the factor of the inverse transform and the Chinese remainder normalisation being folded into the same table. To use the unsigned reconstruction, is added to every coefficient, which makes them lie in , so the product of the primes must be at least . After reconstructing the bias polynomial is subtracted, and the part of the signed result above is folded back by subtraction.
The number of primes is the least with enough capacity for which digits of bits can be converted: digits of at most 50 bits are converted directly, larger ones need one of the vectorised conversions of the integer multiplication profiles. With the context compares the admissible digit counts (with ) by an estimate with measured weights per prime count, which favours two primes slightly and penalises eight.
| primes | |||
|---|---|---|---|
| to | 64 | 3 | |
| 10752 | 256 | 42 | 2 |
| 30720 | 256 | 120 | 6 |
| 47104 | 512 | 92 | 4 |
| 344064 | 8192 | 42 | 2 |
fft_small multiplies integers and polynomials whose product coefficients exceed one prime: chunk sizes and the number of primes, the two-prime and multi-prime pipelines, Chinese remaindering with the normalisation folded in, windows computed with wrap-around, sums of products in transform space, precomputed and shared operands, and transposed products for middle products.
Example#
#include <flint/flint.h>
#include <flint/fmpz.h>
#include <flint/fft_small.h>
int main(void)
{
slong N = 65536, n = N / FLINT_BITS, i;
sd_fft_mpn_mulmod_2expp1_ctx_t C;
sd_fft_mpn_mulmod_2expp1_scratch_t S;
nn_ptr x = flint_calloc(n + 1, sizeof(ulong));
nn_ptr y = flint_calloc(n + 1, sizeof(ulong));
nn_ptr z = flint_calloc(n + 1, sizeof(ulong));
fmpz_t X, Y, Z, M;
sd_fft_mpn_mulmod_2expp1_ctx_init(C, get_default_mpn_ctx(), N, 0);
sd_fft_mpn_mulmod_2expp1_scratch_init(S, C);
flint_printf("N = %wd: %wd digits of %wd bits, %wd primes\n",
C->N, C->m, C->b, C->np);
for (i = 0; i < n; i++)
{
x[i] = UWORD(0x0123456789abcdef) * (i + 1);
y[i] = UWORD(0xfedcba9876543210) ^ i;
}
y[n] = 0;
x[n] = 1; /* x = 2^N + (low limbs), not reduced */
sd_fft_mpn_mulmod_2expp1(C, z, x, y, S);
fmpz_init(X); fmpz_init(Y); fmpz_init(Z); fmpz_init(M);
fmpz_set_ui_array(X, x, n + 1);
fmpz_set_ui_array(Y, y, n + 1);
fmpz_one(M);
fmpz_mul_2exp(M, M, N);
fmpz_add_ui(M, M, 1);
fmpz_mul(Z, X, Y);
fmpz_mod(Z, Z, M);
fmpz_set_ui_array(X, z, n + 1);
flint_printf("x*y mod 2^N + 1: %s\n", fmpz_equal(X, Z) ? "agrees" : "WRONG");
fmpz_clear(X); fmpz_clear(Y); fmpz_clear(Z); fmpz_clear(M);
sd_fft_mpn_mulmod_2expp1_scratch_clear(S);
sd_fft_mpn_mulmod_2expp1_ctx_clear(C);
flint_free(x);
flint_free(y);
flint_free(z);
flint_cleanup_master();
return 0;
}N = 65536: 1024 digits of 64 bits, 3 primes x*y mod 2^N + 1: agrees
References#
- [CF1994] R. Crandall and B. Fagin. Discrete weighted transforms and large-integer arithmetic. Mathematics of Computation 62 (1994), 305–324.
The FLINT team, “Multiplication modulo 2^N + 1 (fft_small.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/fft_small/mulmod_2expp1.html (generated 2026-11-02).
@misc{flint-doc-fft-small-mulmod-2expp1,
author = {{The FLINT team}},
title = {{Multiplication modulo 2\textasciicircum{}N + 1 (fft\_small.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/fft_small/mulmod_2expp1.html},
note = {Generated 2026-11-02}
}