[ci skip] It is *not* the case that a discrete log exists when the base and modulus are coprime. Take 4^x = 2 mod 5 as a counterexample. Change API accordingly.

This commit is contained in:
Nick Thompson
2018-02-10 17:51:59 -06:00
parent 4f4f3eda37
commit faa61cd911
3 changed files with 50 additions and 48 deletions

View File

@ -29,7 +29,7 @@ The algorithms provided by Boost should be acceptable up to roughly 32 bits.
public:
bsgs_discrete_log(Z base, Z p);
Z operator()(Z arg) const;
boost::optional<Z> operator()(Z arg) const;
};
}}
@ -49,7 +49,7 @@ Basic usage is shown below:
}
auto log_2 = bsgs_discrete_log(2, 5);
int log = log_2(3);
int log = log_2(3).value();
std::cout << "log_2(3) mod 5 = " << log << std::endl;
@ -78,10 +78,10 @@ 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,
The baby-step giant-step discrete log will *never* compute a logarithm when the base 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.
For example, since 2[sup 1] = 2 mod 4, log[sub 2](2) = 1.
However, discrete logarithms can exist even when the base and modulus share a common divisor greater than 1.
For example, since 2[super 1] = 2 mod 4, log[sub 2](2) = 1.
Trial multiplication successfully recovers this value, and `bsgs_discrete_log` blows up.

View File

@ -40,7 +40,7 @@ boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z modulus)
if (arg < 1)
{
auto e = boost::format("The argument must be > 0, but is %1%") % arg;
throw std::domain_error(arg);
throw std::domain_error(e.str());
}
if (base >= modulus || arg >= modulus)
{
@ -49,7 +49,7 @@ boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z 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 % modulus;
throw std::domain_error(e.str());
}
if (arg >= p)
if (arg >= modulus)
{
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());
@ -122,15 +122,15 @@ public:
}
Z operator()(Z arg) const
boost::optional<Z> operator()(Z arg) const
{
Z ami = m_inv_base_pow_m;
Z k = arg % m_p;
if(k == 0)
{
throw std::domain_error("Cannot take the logarithm of a number divisible by the modulus.\n");
return {};
}
for (Z i = 0; i < m_root_p; ++i)
for (Z i = 0; i < m_lookup_table.size(); ++i)
{
auto it = m_lookup_table.find(k);
if (it != m_lookup_table.end())
@ -144,10 +144,7 @@ public:
ami = (ami*m_inv_base_pow_m) % m_p;
k = k * ami % m_p;
}
// never should get here . . .
BOOST_ASSERT(false);
// Suppress compiler warnings.
return -1;
return {};
}
private:

View File

@ -17,7 +17,7 @@ using boost::integer::bsgs_discrete_log;
template<class Z>
void test_trial_multiplication_discrete_log()
{
std::cout << "Testing basic trial multiplication discrete logarithm on type " << boost::typeindex::type_id<Z>().pretty_name() << "\n";
boost::optional<Z> x = trial_multiplication_discrete_log<Z>(2, 1, 3);
BOOST_CHECK_EQUAL(0, x.value());
x = trial_multiplication_discrete_log<Z>(2, 2, 3);
@ -59,18 +59,42 @@ void test_trial_multiplication_discrete_log()
template<class Z>
void test_bsgs_discrete_log()
{
std::cout << "Testing basic baby-step giant-step discrete logarithm on type " << boost::typeindex::type_id<Z>().pretty_name() << "\n";
bsgs_discrete_log<Z> dl_7(7, 41);
BOOST_CHECK_EQUAL(dl_7(7), 1);
BOOST_CHECK_EQUAL(dl_7(8), 2);
BOOST_CHECK_EQUAL(dl_7(15), 3);
BOOST_CHECK_EQUAL(dl_7(23), 4);
BOOST_CHECK_EQUAL(dl_7(38), 5);
BOOST_CHECK_EQUAL(dl_7(20), 6);
BOOST_CHECK_EQUAL(dl_7(7).value(), 1);
BOOST_CHECK_EQUAL(dl_7(8).value(), 2);
BOOST_CHECK_EQUAL(dl_7(15).value(), 3);
BOOST_CHECK_EQUAL(dl_7(23).value(), 4);
BOOST_CHECK_EQUAL(dl_7(38).value(), 5);
BOOST_CHECK_EQUAL(dl_7(20).value(), 6);
}
template<class Z>
void test_trial_multiplication_with_prime_base()
void test_trial_multiplication_with_prime_modulus()
{
std::cout << "Testing trial multiplication with prime modulus on type " << boost::typeindex::type_id<Z>().pretty_name() << "\n";
for (Z i = 0; i < boost::math::max_prime; ++i)
{
Z modulus = boost::math::prime(i);
for (Z base = 2; base < modulus; ++base)
{
for (Z arg = 1; arg < modulus; ++arg)
{
boost::optional<Z> dl = trial_multiplication_discrete_log(base, arg, modulus);
if (dl)
{
BOOST_CHECK_EQUAL(arg, boost::multiprecision::powm(base, dl.value(), modulus));
}
}
}
}
}
template<class Z>
void test_bsgs_with_prime_modulus()
{
std::cout << "Testing baby-step, giant-step with prime modulus on type " << boost::typeindex::type_id<Z>().pretty_name() << "\n";
for (Z i = 0; i < boost::math::max_prime; ++i)
{
Z p = boost::math::prime(i);
@ -79,39 +103,20 @@ void test_trial_multiplication_with_prime_base()
bsgs_discrete_log<Z> dl_j(j, p);
for (Z k = 1; k < p; ++k)
{
boost::optional<Z> dl = trial_multiplication_discrete_log(j, k, p);
// It is guaranteed to exist with the modulus is prime:
BOOST_ASSERT(dl);
BOOST_CHECK_EQUAL(k, boost::multiprecision::powm(j, dl.value(), p));
boost::optional<Z> dl = dl_j(k);
if (dl)
{
BOOST_CHECK_EQUAL(k, boost::multiprecision::powm(j, dl.value(), p));
}
}
}
}
}
template<class Z>
void test_bsgs_with_prime_base()
{
for (Z i = 0; i < boost::math::max_prime; ++i)
{
Z p = boost::math::prime(i);
for (Z j = 2; j < p; ++j)
{
bsgs_discrete_log<Z> dl_j(j, p);
for (Z k = 1; k < p; ++k)
{
Z dl = dl_j(k);
BOOST_CHECK_EQUAL(k, boost::multiprecision::powm(j, dl, p));
}
}
}
}
BOOST_AUTO_TEST_CASE(discrete_log_test)
{
test_trial_multiplication_discrete_log<size_t>();
test_bsgs_discrete_log<int>();
test_trial_multiplication_with_prime_base<long long>();
test_bsgs_with_prime_base<long long>();
test_trial_multiplication_with_prime_modulus<long long>();
test_bsgs_with_prime_modulus<long long>();
}