Multiplication by a fixed scalar
#include <flint/nmod_vec.h>void _nmod_vec_scalar_mul_nmod_generic(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)sourcevoid _nmod_vec_scalar_mul_nmod_shoup(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)sourcevoid _nmod_vec_scalar_mul_nmod_redc(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)sourceSets res to c times vec. All require c < n. The first function chooses among the others. _shoup requires n < 2^{\mathtt{FLINT\_BITS} - 1} and _redc requires n odd.
_nmod_vec_scalar_mul_nmod requires reduced entries, since it copies or negates vec when c = 1 or c = n - 1. The _generic, _shoup and _redc variants also accept entries of vec that are not reduced.
void _nmod_vec_scalar_addmul_nmod_generic(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)sourcevoid _nmod_vec_scalar_addmul_nmod_shoup(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)sourceAdds c times vec to res. All require c < n. The first function chooses among the others. _shoup requires n < 2^{\mathtt{FLINT\_BITS} - 1}.
The entries of res must always be reduced. _nmod_vec_scalar_addmul_nmod and _nmod_vec_scalar_addmul_nmod_generic also need reduced entries of vec; the former adds or subtracts vec when c = 1 or c = n - 1. _nmod_vec_scalar_addmul_nmod_shoup accepts entries of vec that are not reduced.
When every entry of a vector is multiplied by the same scalar c, data for multiplying by c can be precomputed once and used for every entry. _nmod_vec_scalar_mul_nmod and _nmod_vec_scalar_addmul_nmod each have variants that differ in what they precompute and in their requirements on n, and choose among them from c, n and the length.
The variants#
| Variant | Method | Requires |
|---|---|---|
_generic | product reduced with the inverse in mod, as in nmod_mul | nothing |
_shoup | Shoup multiplication with \lfloor c 2^{\mathtt{FLINT\_BITS}} / n \rfloor | n < 2^{\mathtt{FLINT\_BITS} - 1} |
_redc | Montgomery multiplication [Mon1985] with c in Montgomery form | n odd |
Shoup multiplication needs one high and two low products per entry and no reduction step, against a full product and a two-word reduction for the generic code. It is usually the fastest choice but needs one spare bit in n. Montgomery multiplication handles moduli with the top bit set, where Shoup's method does not apply. Only _nmod_vec_scalar_mul_nmod has a _redc variant.
Choice of variant#
_nmod_vec_scalar_mul_nmod handles c = 0, c = 1 and c = n - 1 by zeroing, copying and negating. Otherwise:
- If n has
FLINT_BITSbits, it uses_redcfor odd n and length at least 8, and otherwise a generic loop without normalisation shifts. - If n has fewer bits, it uses
_shoupfrom lengthFLINT_MULMOD_SHOUP_THRESHOLDon, and_genericbelow.
_nmod_vec_scalar_addmul_nmod handles c = 0, c = 1 and c = n - 1 with no operation, _nmod_vec_add and _nmod_vec_sub. Otherwise it uses _shoup from length FLINT_MULMOD_SHOUP_THRESHOLD on when n has fewer than FLINT_BITS bits, and _generic in all other cases.
The threshold FLINT_MULMOD_SHOUP_THRESHOLD is set in flint-mparam.h for each target; on some recent x86-64 processors it is 0 (always use Shoup). The precomputation for Shoup's method costs one hardware division, which a short vector does not amortise.
Example#
#include <flint/flint.h>
#include <flint/nmod.h>
#include <flint/nmod_vec.h>
int main(void)
{
nmod_t mod;
ulong u[5] = { 1, 2, 3, 4, 5 };
ulong v[5] = { 10, 20, 30, 40, 50 };
ulong w[5], x[5], y[5];
ulong c = 12345;
int i;
nmod_init(&mod, 1000003);
/* w = c u + 3 v - v */
_nmod_vec_scalar_mul_nmod(w, u, 5, c, mod);
_nmod_vec_scalar_addmul_nmod(w, v, 5, 3, mod);
_nmod_vec_sub(w, w, v, 5, mod);
_nmod_vec_print_pretty(w, 5, mod);
/* the variants agree; _shoup also accepts unreduced entries */
_nmod_vec_scalar_mul_nmod_generic(x, v, 5, c, mod);
_nmod_vec_scalar_mul_nmod_redc(y, v, 5, c, mod); /* n is odd */
flint_printf("generic = redc: %d\n", _nmod_vec_equal(x, y, 5));
for (i = 0; i < 5; i++)
y[i] = v[i] + 7 * mod.n; /* not reduced */
_nmod_vec_scalar_mul_nmod_shoup(y, y, 5, c, mod); /* n < 2^(FLINT_BITS - 1) */
flint_printf("generic = shoup: %d\n", _nmod_vec_equal(x, y, 5));
return 0;
}<length-5 integer vector mod 1000003> [ 12365 24730 37095 49460 61825] generic = redc: 1 generic = shoup: 1
References#
- [Mon1985] P. L. Montgomery, "Modular multiplication without trial division", Mathematics of Computation 44 (1985), 519–521
The FLINT team, “Multiplication by a fixed scalar (nmod_vec.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/nmod_vec/scalar.html (generated 2026-10-20).
@misc{flint-doc-nmod-vec-scalar,
author = {{The FLINT team}},
title = {{Multiplication by a fixed scalar (nmod\_vec.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/nmod_vec/scalar.html},
note = {Generated 2026-10-20}
}