2018-01-28 14:47:14 -06:00
/*
* ( C ) Copyright Nick Thompson 2018.
* Use , modification and distribution are subject to the
* Boost Software License , Version 1.0 . ( See accompanying file
* LICENSE_1_0 . txt or copy at http : //www.boost.org/LICENSE_1_0.txt)
*
* Two methods of computing the discrete logarithm over the multiplicative group of integers mod p .
*/
# ifndef BOOST_INTEGER_DISCRETE_LOG_HPP
# define BOOST_INTEGER_DISCRETE_LOG_HPP
# include <limits>
# include <unordered_map>
# include <boost/optional.hpp>
2018-02-09 17:19:26 -06:00
# include <boost/format.hpp>
# include <boost/multiprecision/integer.hpp>
# include <boost/integer/common_factor_rt.hpp>
# include <boost/integer/mod_inverse.hpp>
2018-01-28 14:47:14 -06:00
namespace boost { namespace integer {
// base^^x = a mod p <-> x = log_base(a) mod p
template < class Z >
2018-02-10 16:07:17 -06:00
boost : : optional < Z > trial_multiplication_discrete_log ( Z base , Z arg , Z modulus )
2018-01-28 14:47:14 -06:00
{
using std : : numeric_limits ;
static_assert ( numeric_limits < Z > : : is_integer ,
" The discrete log works on integral types. \n " ) ;
if ( base < = 1 )
{
2018-02-10 16:07:17 -06:00
auto e = boost : : format ( " The base b is %1%, but must be > 1. \n " ) % base ;
throw std : : domain_error ( e . str ( ) ) ;
2018-01-28 14:47:14 -06:00
}
2018-02-10 16:07:17 -06:00
if ( modulus < 3 )
2018-01-28 14:47:14 -06:00
{
2018-02-10 16:07:17 -06:00
auto e = boost : : format ( " The modulus must be > 2, but is %1% " ) % modulus ;
throw std : : domain_error ( e . str ( ) ) ;
2018-01-28 14:47:14 -06:00
}
if ( arg < 1 )
{
2018-02-10 16:07:17 -06:00
auto e = boost : : format ( " The argument must be > 0, but is %1% " ) % arg ;
2018-02-10 17:51:59 -06:00
throw std : : domain_error ( e . str ( ) ) ;
2018-01-28 14:47:14 -06:00
}
2018-02-10 16:07:17 -06:00
if ( base > = modulus | | arg > = modulus )
2018-01-28 14:47:14 -06:00
{
2018-02-10 16:07:17 -06:00
if ( base > = modulus )
2018-02-09 17:19:26 -06:00
{
2018-02-10 16:07:17 -06:00
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 ;
2018-02-09 17:19:26 -06:00
throw std : : domain_error ( e . str ( ) ) ;
}
2018-02-10 17:51:59 -06:00
if ( arg > = modulus )
2018-02-09 17:19:26 -06:00
{
2018-02-10 16:07:17 -06:00
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 ;
2018-02-09 17:19:26 -06:00
throw std : : domain_error ( e . str ( ) ) ;
}
2018-01-28 14:47:14 -06:00
}
if ( arg = = 1 )
{
return 0 ;
}
Z s = 1 ;
2018-02-10 16:07:17 -06:00
for ( Z i = 1 ; i < modulus ; + + i )
2018-01-28 14:47:14 -06:00
{
2018-02-10 16:07:17 -06:00
s = ( s * base ) % modulus ;
2018-01-28 14:47:14 -06:00
if ( s = = arg )
{
2018-02-09 17:19:26 -06:00
// Maybe a bit trivial assertion. But still a negligible fraction of the total compute time.
2018-02-10 16:07:17 -06:00
BOOST_ASSERT ( arg = = boost : : multiprecision : : powm ( base , i , modulus ) ) ;
2018-01-28 14:47:14 -06:00
return i ;
}
}
return { } ;
}
template < class Z >
2018-02-09 17:19:26 -06:00
class bsgs_discrete_log
2018-01-28 14:47:14 -06:00
{
public :
2018-02-10 16:07:17 -06:00
bsgs_discrete_log ( Z base , Z modulus ) : m_p { modulus } , m_base { base }
2018-01-28 14:47:14 -06:00
{
using std : : numeric_limits ;
static_assert ( numeric_limits < Z > : : is_integer ,
2018-02-09 17:19:26 -06:00
" The baby-step, giant-step discrete log works on integral types. \n " ) ;
2018-01-28 14:47:14 -06:00
if ( base < = 1 )
{
throw std : : logic_error ( " The base must be > 1. \n " ) ;
}
2018-02-10 16:07:17 -06:00
if ( modulus < 3 )
2018-01-28 14:47:14 -06:00
{
throw std : : logic_error ( " The modulus must be > 2. \n " ) ;
}
2018-02-10 16:07:17 -06:00
if ( base > = modulus )
2018-01-28 14:47:14 -06:00
{
throw std : : logic_error ( " Error computing the discrete log: Are your arguments in the wrong order? \n " ) ;
}
2018-02-10 16:07:17 -06:00
m_root_p = boost : : multiprecision : : sqrt ( modulus ) ;
if ( m_root_p * m_root_p ! = modulus )
2018-01-28 14:47:14 -06:00
{
m_root_p + = 1 ;
}
2018-02-10 16:07:17 -06:00
auto x = mod_inverse ( base , modulus ) ;
2018-01-28 14:47:14 -06:00
if ( ! x )
{
2018-02-10 16:07:17 -06:00
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 % modulus % d ;
2018-02-09 17:19:26 -06:00
throw std : : logic_error ( e . str ( ) ) ;
2018-01-28 14:47:14 -06:00
}
2018-02-10 16:07:17 -06:00
m_inv_base_pow_m = boost : : multiprecision : : powm ( x . value ( ) , m_root_p , modulus ) ;
2018-01-28 14:47:14 -06:00
m_lookup_table . reserve ( m_root_p ) ;
// Now the expensive part:
Z k = 1 ;
for ( Z j = 0 ; j < m_root_p ; + + j )
{
m_lookup_table . emplace ( k , j ) ;
2018-02-10 16:07:17 -06:00
k = k * base % modulus ;
2018-01-28 14:47:14 -06:00
}
}
2018-02-10 17:51:59 -06:00
boost : : optional < Z > operator ( ) ( Z arg ) const
2018-01-28 14:47:14 -06:00
{
Z ami = m_inv_base_pow_m ;
Z k = arg % m_p ;
if ( k = = 0 )
{
2018-02-10 17:51:59 -06:00
return { } ;
2018-01-28 14:47:14 -06:00
}
2018-02-10 17:51:59 -06:00
for ( Z i = 0 ; i < m_lookup_table . size ( ) ; + + i )
2018-01-28 14:47:14 -06:00
{
auto it = m_lookup_table . find ( k ) ;
if ( it ! = m_lookup_table . end ( ) )
{
2018-02-09 17:19:26 -06:00
Z log_b_arg = ( i * m_root_p + it - > second ) % m_p ;
// This computation of the modular exponentiation is laughably quick relative to computing the discrete log.
// Why not put an assert here for our peace of mind?
BOOST_ASSERT ( arg = = boost : : multiprecision : : powm ( m_base , log_b_arg , m_p ) ) ;
return log_b_arg ;
2018-01-28 14:47:14 -06:00
}
ami = ( ami * m_inv_base_pow_m ) % m_p ;
k = k * ami % m_p ;
}
2018-02-10 17:51:59 -06:00
return { } ;
2018-01-28 14:47:14 -06:00
}
private :
Z m_p ;
2018-02-09 17:19:26 -06:00
Z m_base ;
2018-01-28 14:47:14 -06:00
Z m_root_p ;
Z m_inv_base_pow_m ;
std : : unordered_map < Z , Z > m_lookup_table ;
} ;
} }
# endif