diff --git a/doc/modular_arithmetic/discrete_log.qbk b/doc/modular_arithmetic/discrete_log.qbk index 747e191..d0d21a7 100644 --- a/doc/modular_arithmetic/discrete_log.qbk +++ b/doc/modular_arithmetic/discrete_log.qbk @@ -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 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. diff --git a/include/boost/integer/discrete_log.hpp b/include/boost/integer/discrete_log.hpp index dddd417..b4a7657 100644 --- a/include/boost/integer/discrete_log.hpp +++ b/include/boost/integer/discrete_log.hpp @@ -40,7 +40,7 @@ boost::optional 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 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 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: diff --git a/test/discrete_log_test.cpp b/test/discrete_log_test.cpp index 1c569cc..f4a1881 100644 --- a/test/discrete_log_test.cpp +++ b/test/discrete_log_test.cpp @@ -17,7 +17,7 @@ using boost::integer::bsgs_discrete_log; template void test_trial_multiplication_discrete_log() { - + std::cout << "Testing basic trial multiplication discrete logarithm on type " << boost::typeindex::type_id().pretty_name() << "\n"; boost::optional x = trial_multiplication_discrete_log(2, 1, 3); BOOST_CHECK_EQUAL(0, x.value()); x = trial_multiplication_discrete_log(2, 2, 3); @@ -59,18 +59,42 @@ void test_trial_multiplication_discrete_log() template void test_bsgs_discrete_log() { + std::cout << "Testing basic baby-step giant-step discrete logarithm on type " << boost::typeindex::type_id().pretty_name() << "\n"; bsgs_discrete_log 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 -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().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 dl = trial_multiplication_discrete_log(base, arg, modulus); + if (dl) + { + BOOST_CHECK_EQUAL(arg, boost::multiprecision::powm(base, dl.value(), modulus)); + } + } + } + } +} + + +template +void test_bsgs_with_prime_modulus() +{ + std::cout << "Testing baby-step, giant-step with prime modulus on type " << boost::typeindex::type_id().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 dl_j(j, p); for (Z k = 1; k < p; ++k) { - boost::optional 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 dl = dl_j(k); + if (dl) + { + BOOST_CHECK_EQUAL(k, boost::multiprecision::powm(j, dl.value(), p)); + } } } } } - -template -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 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(); test_bsgs_discrete_log(); - test_trial_multiplication_with_prime_base(); - test_bsgs_with_prime_base(); + test_trial_multiplication_with_prime_modulus(); + test_bsgs_with_prime_modulus(); }