FLINT3.7.0-dev

Multiplication by a fixed scalar

Synopsis#include <flint/nmod_vec.h>
void _nmod_vec_scalar_mul_nmod(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)source
void _nmod_vec_scalar_mul_nmod_generic(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)source
void _nmod_vec_scalar_mul_nmod_shoup(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)source
void _nmod_vec_scalar_mul_nmod_redc(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)source

Sets 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(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)source
void _nmod_vec_scalar_addmul_nmod_generic(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)source
void _nmod_vec_scalar_addmul_nmod_shoup(nn_ptr res, nn_srcptr vec, slong len, ulong c, nmod_t mod)source

Adds 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#

VariantMethodRequires
_genericproduct reduced with the inverse in mod, as in nmod_mulnothing
_shoupShoup multiplication with \lfloor c 2^{\mathtt{FLINT\_BITS}} / n \rfloorn < 2^{\mathtt{FLINT\_BITS} - 1}
_redcMontgomery multiplication [Mon1985] with c in Montgomery formn 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.

AlgorithmMultiplication by a fixed operand (Shoup)Computes ab \bmod n for fixed a and n with two low products, one high product and one comparison, using the precomputed quotient \lfloor aB/n \rfloor.

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_BITS bits, it uses _redc for odd n and length at least 8, and otherwise a generic loop without normalisation shifts.
  • If n has fewer bits, it uses _shoup from length FLINT_MULMOD_SHOUP_THRESHOLD on, and _generic below.

_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#

Examplenmod_vec_scalar.cchecked by make checkdoc
#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;
}
Output
<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
Cite this page

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).