Source code for py3nt.congruence.binomial
"""Congruence of binomial coefficients"""
import numpy as np
from py3nt.congruence.basics import inverse
from py3nt.numbers.integer import Integer
[docs]
def small_binomial_modulo_prime(
n: int, k: int, p: int, factorials: None | np.ndarray = None
) -> int:
r"""
Parameters
----------
n : ``int``
An integer.
k : ``int``
An integer.
p : ``int``
A prime.
factorials : ``Optional[np.ndarray], optional``
Array of precalculated factorials :math:`\pmod{p}`., by default ``None``
Returns
-------
``int``
:math:`\binom{n}{k}\pmod{p}` when :math:`n,k< p`.
Raises
------
``ValueError``
If :math:`n` or :math:`k` is negative.
"""
if n < 0 or k < 0:
raise ValueError("`n` or `k` cannot be negative,")
if not n and not k:
return 1
if not n:
return 0
if not k:
return 1
if n < k:
return 0
if not isinstance(factorials, np.ndarray) and not isinstance(factorials, list):
factorials = np.empty(shape=(n + 1,), dtype=int)
factorials[0] = 1
for i in range(1, n + 1):
factorials[i] = Integer(i).multiply_modular(
other=factorials[i - 1], modulus=p
)
rem = Integer(factorials[n]).multiply_modular(
other=inverse(a=factorials[k], n=p, is_prime=True), modulus=p
)
rem = Integer(rem).multiply_modular(
other=inverse(a=factorials[n - k], n=p, is_prime=True), modulus=p
)
return rem
[docs]
def binomial_modulo_small_prime(n: int, k: int, p: int) -> int:
r"""
Uses Lucas theorem to calculate binomial coefficients modulo primes. If
.. math::
n = (n_{r}\ldots n_{0})_{p}\\
k = (k_{r}\ldots k_{0})_{p}
then
.. math:: \binom{n}{k} \equiv \binom{n_{r}}{k_{r}}\cdots\binom{n_{0}}{k_{0}}\pmod{p}
Parameters
----------
n : ``int``
An integer.
k : ``int``
An integer.
p : ``int``
A prime.
factorials : ``np.ndarray, optional``
Array of precalculated factorials :math:`\pmod{p}`., by default ``None``
Returns
-------
``int``
:math:`\binom{n}{k}\pmod{p}` when :math:`n,k< p`.
"""
factorials = np.empty(shape=(p,), dtype=int)
factorials[0] = 1
for i in range(1, p):
factorials[i] = Integer(i).multiply_modular(other=factorials[i - 1], modulus=p)
rem = 1
while n > 0:
r = n % p
s = k % p
rem = Integer(rem).multiply_modular(
other=small_binomial_modulo_prime(n=r, k=s, p=p, factorials=factorials),
modulus=p,
)
n //= p
k //= p
return rem