[ci skip] Fix docs to use less verbose names for modular multiplicative inverse (mod_inverse)

This commit is contained in:
Nick Thompson
2018-02-10 16:07:17 -06:00
parent b3966428c4
commit 4f4f3eda37
4 changed files with 48 additions and 43 deletions

View File

@ -3,13 +3,13 @@
[section Introduction] [section Introduction]
The discrete log is the inverse of modular exponentiation. The discrete log is the inverse of modular exponentiation.
To wit, if /a/[sup /x/] = /b/ mod /p/, then we write /x/ = log[sub a](/b/). To wit, if /a/[super /x/] = /b/ mod /p/, then we write /x/ = log[sub a](/b/).
Fast algorithms for modular exponentiation exists, but currently there are no polynomial time algorithms known for the discrete logarithm, Fast algorithms for modular exponentiation exists, but currently there are no polynomial time algorithms known for the discrete logarithm,
a fact which is the basis for the security of Diffie-Hellman key exchange. a fact which is the basis for the security of Diffie-Hellman key exchange.
Despite having exponential complexity in the number of bits, the algorithms for discrete logarithm provided by Boost are still useful, Despite having exponential complexity in the number of bits, the algorithms for discrete logarithm provided by Boost are still useful,
for there are many uses of the discrete logarithm outside of cryptography which do not require massive inputs. for there are many uses of the discrete logarithm outside of cryptography which do not require massive inputs.
The algorithms provided by Boost should be acceptable up to roughly 64 bits. The algorithms provided by Boost should be acceptable up to roughly 32 bits.
[endsect] [endsect]
@ -24,10 +24,10 @@ The algorithms provided by Boost should be acceptable up to roughly 64 bits.
template<class Z> template<class Z>
class baby_step_giant_step_discrete_log class bsgs_discrete_log
{ {
public: public:
baby_step_giant_step_discrete_log(Z base, Z p); bsgs_discrete_log(Z base, Z p);
Z operator()(Z arg) const; Z operator()(Z arg) const;
@ -39,7 +39,7 @@ The algorithms provided by Boost should be acceptable up to roughly 64 bits.
[section Usage] [section Usage]
Boost provides two algorithms for the discrete log: Trial multiplication and the "baby-step giant step" algorithm. Boost provides two algorithms for the discrete log: Trial multiplication and the "baby-step giant-step" algorithm.
Basic usage is shown below: Basic usage is shown below:
auto logarithm = trial_multiplication_discrete_log(2, 3, 5); auto logarithm = trial_multiplication_discrete_log(2, 3, 5);
@ -48,15 +48,16 @@ Basic usage is shown below:
std::cout << "log_2(3) mod 5 = " << l.value() << std::endl; std::cout << "log_2(3) mod 5 = " << l.value() << std::endl;
} }
auto bsgs = baby_step_giant_step_discrete_log(2, 5); auto log_2 = bsgs_discrete_log(2, 5);
int log = bsgs(3); int log = log_2(3);
std::cout << "log_2(3) mod 5 = " << log << std::endl; std::cout << "log_2(3) mod 5 = " << log << std::endl;
Of these, trial multiplication is more general, requires O(/p/) time and O(1) storage. Of these, trial multiplication is more general, requires [bigo](/p/) time and [bigo](1) storage.
The baby-step giant step algorithm requires O([radic] p) time and O([radic] p) storage, and is slightly less general as the generator must be coprime to the the modulus. The baby-step giant step algorithm requires [bigo]([radic] p) time and [bigo]([radic] p) storage,
and is slightly less general as the base must be coprime to the the modulus.
Let's illustrate this with a few examples: Suppose we wish to compute log[sub 2](3) mod 4. Let's illustrate this with a few examples: Suppose we wish to compute log[sub 2](3) mod 4.
Since 2[sup x] = 3 mod 4 has no solution, the result is undefined. Since 2[super /x/] = 3 mod 4 has no solution, the result is undefined.
boost::optional<int> l = trial_multiplication_discrete_log(2, 3, 4); boost::optional<int> l = trial_multiplication_discrete_log(2, 3, 4);
if (!l) if (!l)
@ -68,7 +69,7 @@ The baby-step giant-step algorithm is less polite when the base and the modulus
try try
{ {
auto bsgs = baby_step_giant_step_discrete_log(2, 4); auto log_2 = bsgs_discrete_log(2, 4);
} }
catch(std::exception const & e) catch(std::exception const & e)
{ {
@ -77,10 +78,11 @@ The baby-step giant-step algorithm is less polite when the base and the modulus
} }
The baby-step giant-step discrete log will *never* compute a logarithm when the generator and modulus are not coprime, because it relies on the existence of modular multiplicative inverses. The baby-step giant-step discrete log will *never* compute a logarithm when the generator and modulus are not coprime,
because it relies on the existence of modular multiplicative inverses.
However, discrete logarithms can exist even when the generator and modulus share a common divisor greater than 1. However, discrete logarithms can exist even when the generator and modulus share a common divisor greater than 1.
For example, since 2[sup 1] = 2 mod 4, log[sub 2](2) = 1. For example, since 2[sup 1] = 2 mod 4, log[sub 2](2) = 1.
Trial multiplication successfully recovers this value, and `baby_step_giant_step_discrete_log` blows up. Trial multiplication successfully recovers this value, and `bsgs_discrete_log` blows up.
[endsect] [endsect]

View File

@ -1,20 +1,20 @@
[section:modular_multiplicative_inverse Modular Multiplicative Inverse] [section:mod_inverse Modular Multiplicative Inverse]
[section Introduction] [section Introduction]
The modular multiplicative inverse of a number /a/ is that number /x/ which satisfied /ax/ = 1 mod /p/. The modular multiplicative inverse of a number /a/ is that number /x/ which satisfies /ax/ = 1 mod /p/.
A fast algorithm for computing modular multiplicative inverses based on the extended Euclidean algorithm exists and is provided by Boost. A fast algorithm for computing modular multiplicative inverses based on the extended Euclidean algorithm exists and is provided by Boost.
[endsect] [endsect]
[section Synopsis] [section Synopsis]
#include <boost/integer/modular_multiplicative_inverse.hpp> #include <boost/integer/mod_inverse.hpp>
namespace boost { namespace integer { namespace boost { namespace integer {
template<class Z> template<class Z>
boost::optional<Z> modular_multiplicative_inverse(Z a, Z p); boost::optional<Z> mod_inverse(Z a, Z p);
}} }}
@ -25,12 +25,12 @@ A fast algorithm for computing modular multiplicative inverses based on the exte
Multiplicative modular inverses exist if and only if /a/ and /p/ are coprime. Multiplicative modular inverses exist if and only if /a/ and /p/ are coprime.
So for example So for example
auto x = modular_multiplicative_inverse(2, 5); auto x = mod_inverse(2, 5);
if (x) if (x)
{ {
int should_be_three = x.value(); int should_be_three = x.value();
} }
auto y = modular_multiplicative_inverse(2, 4); auto y = mod_inverse(2, 4);
if (!y) if (!y)
{ {
std::cout << "There is no inverse of 2 mod 4\n"; std::cout << "There is no inverse of 2 mod 4\n";

View File

@ -21,7 +21,7 @@ namespace boost { namespace integer {
// base^^x = a mod p <-> x = log_base(a) mod p // base^^x = a mod p <-> x = log_base(a) mod p
template<class Z> template<class Z>
boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z p) boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z modulus)
{ {
using std::numeric_limits; using std::numeric_limits;
static_assert(numeric_limits<Z>::is_integer, static_assert(numeric_limits<Z>::is_integer,
@ -29,26 +29,29 @@ boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z p)
if (base <= 1) if (base <= 1)
{ {
throw std::domain_error("The base must be > 1.\n"); auto e = boost::format("The base b is %1%, but must be > 1.\n") % base;
throw std::domain_error(e.str());
} }
if (p < 3) if (modulus < 3)
{ {
throw std::domain_error("The modulus must be > 2.\n"); auto e = boost::format("The modulus must be > 2, but is %1%") % modulus;
throw std::domain_error(e.str());
} }
if (arg < 1) if (arg < 1)
{ {
throw std::domain_error("The argument must be > 0.\n"); auto e = boost::format("The argument must be > 0, but is %1%") % arg;
throw std::domain_error(arg);
} }
if (base >= p || arg >= p) if (base >= modulus || arg >= modulus)
{ {
if (base >= p) if (base >= modulus)
{ {
auto e = boost::format("Error computing the discrete log: The base %1% is greater than the modulus %2%. Are the arguments in the wrong order?") % base % p; auto e = boost::format("Error computing the discrete log: The base %1% is greater than the modulus %2%. Are the arguments in the wrong order?") % base % modulus;
throw std::domain_error(e.str()); throw std::domain_error(e.str());
} }
if (arg >= p) if (arg >= p)
{ {
auto e = boost::format("Error computing the discrete log: The argument %1% is greater than the modulus %2%. Are the arguments in the wrong order?") % arg % p; auto e = boost::format("Error computing the discrete log: The argument %1% is greater than the modulus %2%. Are the arguments in the wrong order?") % arg % modulus;
throw std::domain_error(e.str()); throw std::domain_error(e.str());
} }
} }
@ -58,13 +61,13 @@ boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z p)
return 0; return 0;
} }
Z s = 1; Z s = 1;
for (Z i = 1; i < p; ++i) for (Z i = 1; i < modulus; ++i)
{ {
s = (s * base) % p; s = (s * base) % modulus;
if (s == arg) if (s == arg)
{ {
// Maybe a bit trivial assertion. But still a negligible fraction of the total compute time. // Maybe a bit trivial assertion. But still a negligible fraction of the total compute time.
BOOST_ASSERT(arg == boost::multiprecision::powm(base, i, p)); BOOST_ASSERT(arg == boost::multiprecision::powm(base, i, modulus));
return i; return i;
} }
} }
@ -75,7 +78,7 @@ template<class Z>
class bsgs_discrete_log class bsgs_discrete_log
{ {
public: public:
bsgs_discrete_log(Z base, Z p) : m_p{p}, m_base{base} bsgs_discrete_log(Z base, Z modulus) : m_p{modulus}, m_base{base}
{ {
using std::numeric_limits; using std::numeric_limits;
static_assert(numeric_limits<Z>::is_integer, static_assert(numeric_limits<Z>::is_integer,
@ -85,28 +88,28 @@ public:
{ {
throw std::logic_error("The base must be > 1.\n"); throw std::logic_error("The base must be > 1.\n");
} }
if (p < 3) if (modulus < 3)
{ {
throw std::logic_error("The modulus must be > 2.\n"); throw std::logic_error("The modulus must be > 2.\n");
} }
if (base >= p) if (base >= modulus)
{ {
throw std::logic_error("Error computing the discrete log: Are your arguments in the wrong order?\n"); throw std::logic_error("Error computing the discrete log: Are your arguments in the wrong order?\n");
} }
m_root_p = boost::multiprecision::sqrt(p); m_root_p = boost::multiprecision::sqrt(modulus);
if (m_root_p*m_root_p != p) if (m_root_p*m_root_p != modulus)
{ {
m_root_p += 1; m_root_p += 1;
} }
auto x = mod_inverse(base, p); auto x = mod_inverse(base, modulus);
if (!x) if (!x)
{ {
auto d = boost::integer::gcd(base, p); auto d = boost::integer::gcd(base, modulus);
auto e = boost::format("The gcd of the base %1% and the modulus %2% is %3% != 1, hence the discrete log is not guaranteed to exist, which breaks the baby-step giant step algorithm. If you don't require existence proof for all inputs, use trial multiplication.\n") % base % p % d; auto e = boost::format("The gcd of the base %1% and the modulus %2% is %3% != 1, hence the discrete log is not guaranteed to exist, which breaks the baby-step giant step algorithm. If you don't require existence proof for all inputs, use trial multiplication.\n") % base % modulus % d;
throw std::logic_error(e.str()); throw std::logic_error(e.str());
} }
m_inv_base_pow_m = boost::multiprecision::powm(x.value(), m_root_p, p); m_inv_base_pow_m = boost::multiprecision::powm(x.value(), m_root_p, modulus);
m_lookup_table.reserve(m_root_p); m_lookup_table.reserve(m_root_p);
// Now the expensive part: // Now the expensive part:
@ -114,7 +117,7 @@ public:
for (Z j = 0; j < m_root_p; ++j) for (Z j = 0; j < m_root_p; ++j)
{ {
m_lookup_table.emplace(k, j); m_lookup_table.emplace(k, j);
k = k*base % p; k = k*base % modulus;
} }
} }

View File

@ -4,8 +4,8 @@
* Boost Software License, Version 1.0. (See accompanying file * Boost Software License, Version 1.0. (See accompanying file
* LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/ */
#ifndef BOOST_INTEGER_MODULAR_MULTIPLICATIVE_INVERSE_HPP #ifndef BOOST_INTEGER_MOD_INVERSE_HPP
#define BOOST_INTEGER_MODULAR_MULTIPLICATIVE_INVERSE_HPP #define BOOST_INTEGER_MOD_INVERSE_HPP
#include <limits> #include <limits>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/integer/extended_euclidean.hpp> #include <boost/integer/extended_euclidean.hpp>