[ci skip] Modular exponentiation, modular multiplicative inverse, extended Euclidean algorithm, discrete logarithm.

This commit is contained in:
Nick Thompson
2018-01-28 14:47:14 -06:00
parent 919c5277c1
commit fc4d657201
14 changed files with 768 additions and 39 deletions

View File

@ -15,7 +15,7 @@ programming problems.
namespace boost
{
namespace math
namespace integer
{
template < typename IntegerType >
@ -53,10 +53,10 @@ programming problems.
[section GCD Function Object]
[*Header: ] [@../../../../boost/math/common_factor_rt.hpp <boost/math/common_factor_rt.hpp>]
[*Header: ] [@../../../../boost/integer/common_factor_rt.hpp <boost/integer/common_factor_rt.hpp>]
template < typename IntegerType >
class boost::math::gcd_evaluator
class boost::integer::gcd_evaluator
{
public:
// Types
@ -65,12 +65,12 @@ programming problems.
typedef IntegerType second_argument_type;
// Function object interface
constexpr result_type operator ()(
constexpr result_type operator ()(
first_argument_type const &a,
second_argument_type const &b ) const;
};
The boost::math::gcd_evaluator class template defines a function object
The boost::integer::gcd_evaluator class template defines a function object
class to return the greatest common divisor of two integers.
The template is parameterized by a single type, called IntegerType here.
This type should be a numeric type that represents integers.
@ -82,17 +82,17 @@ the GCD function template. If a numeric type wants to customize evaluations
of its greatest common divisors, then the type should specialize on the
gcd_evaluator class template.
Note that these function objects are `constexpr` in C++14 and later only.
Note that these function objects are `constexpr` in C++14 and later only.
They are also declared `noexcept` when appropriate.
[endsect]
[section LCM Function Object]
[*Header: ] [@../../../../boost/math/common_factor_rt.hpp <boost/math/common_factor_rt.hpp>]
[*Header: ] [@../../../../boost/integer/common_factor_rt.hpp <boost/integer/common_factor_rt.hpp>]
template < typename IntegerType >
class boost::math::lcm_evaluator
class boost::integer::lcm_evaluator
{
public:
// Types
@ -101,12 +101,12 @@ They are also declared `noexcept` when appropriate.
typedef IntegerType second_argument_type;
// Function object interface
constexpr result_type operator ()(
constexpr result_type operator ()(
first_argument_type const &a,
second_argument_type const &b ) const;
};
The boost::math::lcm_evaluator class template defines a function object
The boost::integer::lcm_evaluator class template defines a function object
class to return the least common multiple of two integers. The template
is parameterized by a single type, called IntegerType here. This type
should be a numeric type that represents integers. The result of the
@ -126,13 +126,13 @@ They are also declared `noexcept` when appropriate.
[section:run_time Run-time GCD & LCM Determination]
[*Header: ] [@../../../../boost/math/common_factor_rt.hpp <boost/math/common_factor_rt.hpp>]
[*Header: ] [@../../../../boost/integer/common_factor_rt.hpp <boost/integer/common_factor_rt.hpp>]
template < typename IntegerType >
constexpr IntegerType boost::math::gcd( IntegerType const &a, IntegerType const &b );
constexpr IntegerType boost::integer::gcd( IntegerType const &a, IntegerType const &b );
template < typename IntegerType >
constexpr IntegerType boost::math::lcm( IntegerType const &a, IntegerType const &b );
constexpr IntegerType boost::integer::lcm( IntegerType const &a, IntegerType const &b );
template < typename IntegerType, typename... Args >
constexpr IntegerType gcd( IntegerType const &a, IntegerType const &b, Args const&... );
@ -148,18 +148,18 @@ They are also declared `noexcept` when appropriate.
std::pair<typename std::iterator_traits<I>::value_type, I>
lcm_range(I first, I last);
The boost::math::gcd function template returns the greatest common
The boost::integer::gcd function template returns the greatest common
(nonnegative) divisor of the two integers passed to it.
`boost::math::gcd_range` is the iteration of the above gcd algorithm over a
range, returning the greatest common divisor of all the elements. The algorithm
terminates when the gcd reaches unity or the end of the range. Thus it also
returns the iterator after the last element inspected because this may not be
`boost::integer::gcd_range` is the iteration of the above gcd algorithm over a
range, returning the greatest common divisor of all the elements. The algorithm
terminates when the gcd reaches unity or the end of the range. Thus it also
returns the iterator after the last element inspected because this may not be
equal to the end of the range. The variadic version of `gcd` behaves similarly
but does not indicate which input value caused the gcd to reach unity.
The boost::math::lcm function template returns the least common
The boost::integer::lcm function template returns the least common
(nonnegative) multiple of the two integers passed to it.
As with gcd, there are range and variadic versions of the function for
As with gcd, there are range and variadic versions of the function for
more than 2 arguments.
Note that these functions are constexpr in C++14 and later only.
@ -171,17 +171,17 @@ They are also declared `noexcept` when appropriate.
[note These functions are deprecated in favor of constexpr `gcd` and `lcm` on C++14 capable compilers.]
[*Header: ] [@../../../../boost/math/common_factor_ct.hpp <boost/math/common_factor_ct.hpp>]
[*Header: ] [@../../../../boost/integer/common_factor_ct.hpp <boost/integer/common_factor_ct.hpp>]
typedef ``['unspecified]`` static_gcd_type;
template < static_gcd_type Value1, static_gcd_type Value2 >
struct boost::math::static_gcd : public mpl::integral_c<static_gcd_type, implementation_defined>
struct boost::integer::static_gcd : public mpl::integral_c<static_gcd_type, implementation_defined>
{
};
template < static_gcd_type Value1, static_gcd_type Value2 >
struct boost::math::static_lcm : public mpl::integral_c<static_gcd_type, implementation_defined>
struct boost::integer::static_lcm : public mpl::integral_c<static_gcd_type, implementation_defined>
{
};
@ -190,7 +190,7 @@ for use in integral-constant-expressions by the compiler. Usually this
the same type as `boost::uintmax_t`, but may fall back to being `unsigned long`
for some older compilers.
The boost::math::static_gcd and boost::math::static_lcm class templates
The boost::integer::static_gcd and boost::integer::static_lcm class templates
take two value-based template parameters of the ['static_gcd_type] type
and inherit from the type `boost::mpl::integral_c`.
Inherited from the base class, they have a member /value/
@ -201,7 +201,7 @@ is beyond the range of `static_gcd_type`.
[h3 Example]
#include <boost/math/common_factor.hpp>
#include <boost/integer/common_factor.hpp>
#include <algorithm>
#include <iterator>
#include <iostream>
@ -212,28 +212,28 @@ is beyond the range of `static_gcd_type`.
using std::endl;
cout << "The GCD and LCM of 6 and 15 are "
<< boost::math::gcd(6, 15) << " and "
<< boost::math::lcm(6, 15) << ", respectively."
<< boost::integer::gcd(6, 15) << " and "
<< boost::integer::lcm(6, 15) << ", respectively."
<< endl;
cout << "The GCD and LCM of 8 and 9 are "
<< boost::math::static_gcd<8, 9>::value
<< boost::integer::static_gcd<8, 9>::value
<< " and "
<< boost::math::static_lcm<8, 9>::value
<< boost::integer::static_lcm<8, 9>::value
<< ", respectively." << endl;
int a[] = { 4, 5, 6 }, b[] = { 7, 8, 9 }, c[3];
std::transform( a, a + 3, b, c, boost::math::gcd_evaluator<int>() );
std::transform( a, a + 3, b, c, boost::integer::gcd_evaluator<int>() );
std::copy( c, c + 3, std::ostream_iterator<int>(cout, " ") );
}
[endsect]
[section:gcd_header Header <boost/math/common_factor.hpp>]
[section:gcd_header Header <boost/integer/common_factor.hpp>]
This header simply includes the headers
[@../../../../boost/math/common_factor_ct.hpp <boost/math/common_factor_ct.hpp>]
and [@../../../../boost/math/common_factor_rt.hpp <boost/math/common_factor_rt.hpp>].
[@../../../../boost/integer/common_factor_ct.hpp <boost/integer/common_factor_ct.hpp>]
and [@../../../../boost/integer/common_factor_rt.hpp <boost/integer/common_factor_rt.hpp>].
Note this is a legacy header: it used to contain the actual implementation,
but the compile-time and run-time facilities
@ -243,7 +243,7 @@ were moved to separate headers (since they were independent of each other).
[section:demo Demonstration Program]
The program [@../../../../libs/math/test/common_factor_test.cpp common_factor_test.cpp] is a demonstration of the results from
The program [@../../../../libs/integer/test/common_factor_test.cpp common_factor_test.cpp] is a demonstration of the results from
instantiating various examples of the run-time GCD and LCM function
templates and the compile-time GCD and LCM class templates.
(The run-time GCD and LCM class templates are tested indirectly through
@ -256,13 +256,13 @@ the run-time function templates.)
The greatest common divisor and least common multiple functions are
greatly used in some numeric contexts, including some of the other
Boost libraries. Centralizing these functions to one header improves
code factoring and eases maintainence.
code factoring and eases maintenance.
[endsect]
[section:gcd_history History]
* 24th April 2017 Moved to Jeremy Murphy's improved algorithms, added constexpr and noexcept support,
* 24th April 2017 Moved to Jeremy Murphy's improved algorithms, added constexpr and noexcept support,
added compiler intrinsic support, added variadic and range based versions of the algorithms.
* 13 May 2013 Moved into main Boost.Math Quickbook documentation.
* 17 Dec 2005: Converted documentation to Quickbook Format.
@ -288,5 +288,3 @@ Distributed under 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).
]

View File

@ -0,0 +1,97 @@
[section:discrete_log Discrete Log]
[section Introduction]
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/).
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.
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.
The algorithms provided by Boost should be acceptable up to roughly 64 bits.
[endsect]
[section Synopsis]
#include <boost/integer/discrete_log.hpp>
namespace boost { namespace integer {
template<class Z>
boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z p);
template<class Z>
class baby_step_giant_step_discrete_log
{
public:
baby_step_giant_step_discrete_log(Z base, Z p);
Z operator()(Z arg) const;
};
}}
[endsect]
[section Usage]
Boost provides two algorithms for the discrete log: Trial multiplication and the "baby-step giant step" algorithm.
Basic usage is shown below:
auto logarithm = trial_multiplication_discrete_log(2, 3, 5);
if (logarithm)
{
std::cout << "log_2(3) mod 5 = " << l.value() << std::endl;
}
auto bsgs = baby_step_giant_step_discrete_log(2, 5);
int log = bsgs(3);
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.
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.
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.
boost::optional<int> l = trial_multiplication_discrete_log(2, 3, 4);
if (!l)
{
std::cout << "log_2(3) mod 4 is undefined!\n";
}
The baby-step giant-step algorithm is less polite when the base and the modulus are not coprime:
try
{
auto bsgs = baby_step_giant_step_discrete_log(2, 4);
}
catch(std::exception const & e)
{
// e.what() is gonna tell you 2 and 4 are not coprime:
std::cout << e.what() << std::endl;
}
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.
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.
[endsect]
[section References]
Wagstaff, Samuel S., ['The Joy of Factoring], Vol. 68. American Mathematical Soc., 2013.
[endsect]
[endsect]
[/
Copyright 2018 Nick Thompson.
Distributed under 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).
]

View File

@ -0,0 +1,47 @@
[section:extended_euclidean Extended Euclidean Algorithm]
[section Introduction]
The extended Euclidean algorithm solves the integer relation /mx + ny/ = gcd(/m/, /n/) for /x/ and /y/.
[endsect]
[section Synopsis]
#include <boost/integer/extended_euclidean.hpp>
namespace boost { namespace integer {
template<class Z>
std::tuple<Z, Z, Z> extended_euclidean(Z m, Z n);
}}
[endsect]
[section Usage]
The tuple returned by the extended Euclidean algorithm contains, the greatest common divisor, /x/, and /y/, in that order:
int m = 12;
int n = 15;
auto tup = extended_euclidean(m, n);
int gcd = std::get<0>(tup);
int x = std::get<1>(tup);
int y = std::get<2>(tup);
// mx + ny = gcd(m,n)
[endsect]
[section References]
Wagstaff, Samuel S., ['The Joy of Factoring], Vol. 68. American Mathematical Soc., 2013.
[endsect]
[endsect]
[/
Copyright 2018 Nick Thompson.
Distributed under 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).
]

View File

@ -0,0 +1,51 @@
[section:modular_multiplicative_inverse Modular Multiplicative Inverse]
[section Introduction]
The modular multiplicative inverse of a number /a/ is that number /x/ which satisfied /ax/ = 1 mod /p/.
A fast algorithm for computing modular multiplicative inverses based on the extended Euclidean algorithm exists and is provided by Boost.
[endsect]
[section Synopsis]
#include <boost/integer/modular_multiplicative_inverse.hpp>
namespace boost { namespace integer {
template<class Z>
boost::optional<Z> modular_multiplicative_inverse(Z a, Z p);
}}
[endsect]
[section Usage]
Multiplicative modular inverses exist if and only if /a/ and /p/ are coprime.
So for example
auto x = modular_multiplicative_inverse(2, 5);
if (x)
{
int should_be_three = x.value();
}
auto y = modular_multiplicative_inverse(2, 4);
if (!y)
{
std::cout << "There is no inverse of 2 mod 4\n";
}
[endsect]
[section References]
Wagstaff, Samuel S., ['The Joy of Factoring], Vol. 68. American Mathematical Soc., 2013.
[endsect]
[endsect]
[/
Copyright 2018 Nick Thompson.
Distributed under 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).
]

View File

@ -0,0 +1,140 @@
/*
* (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>
#include <boost/integer/floor_sqrt.hpp>
#include <boost/integer/modular_multiplicative_inverse.hpp>
#include <boost/integer/modular_exponentiation.hpp>
#include <boost/integer/common_factor.hpp>
namespace boost { namespace integer {
// base^^x = a mod p <-> x = log_base(a) mod p
template<class Z>
boost::optional<Z> trial_multiplication_discrete_log(Z base, Z arg, Z p)
{
using std::numeric_limits;
static_assert(numeric_limits<Z>::is_integer,
"The discrete log works on integral types.\n");
if (base <= 1)
{
throw std::logic_error("The base must be > 1.\n");
}
if (p < 3)
{
throw std::logic_error("The modulus must be > 2.\n");
}
if (arg < 1)
{
throw std::logic_error("The argument must be > 0.\n");
}
if (base >= p || arg >= p)
{
throw std::logic_error("Error computing the discrete log: Are your arguments in the wrong order?\n");
}
if (arg == 1)
{
return 0;
}
Z s = 1;
for (Z i = 1; i < p; ++i)
{
s = (s * base) % p;
if (s == arg)
{
return i;
}
}
return {};
}
template<class Z>
class baby_step_giant_step_discrete_log
{
public:
baby_step_giant_step_discrete_log(Z base, Z p) : m_p{p}
{
using std::numeric_limits;
static_assert(numeric_limits<Z>::is_integer,
"The baby_step_giant_step discrete log works on integral types.\n");
if (base <= 1)
{
throw std::logic_error("The base must be > 1.\n");
}
if (p < 3)
{
throw std::logic_error("The modulus must be > 2.\n");
}
if (base >= p)
{
throw std::logic_error("Error computing the discrete log: Are your arguments in the wrong order?\n");
}
m_root_p = floor_sqrt(p);
if (m_root_p*m_root_p != p)
{
m_root_p += 1;
}
auto x = modular_multiplicative_inverse(base, p);
if (!x)
{
throw std::logic_error("The gcd of the b and the modulus is > 1, hence the discrete log is not guaranteed to exist. If you don't require an existence proof, use trial multiplication.\n");
}
m_inv_base_pow_m = modular_exponentiation(x.value(), m_root_p, p);
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);
k = k*base % p;
}
}
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");
}
for (Z i = 0; i < m_root_p; ++i)
{
auto it = m_lookup_table.find(k);
if (it != m_lookup_table.end())
{
return (i*m_root_p + it->second) % m_p;
}
ami = (ami*m_inv_base_pow_m) % m_p;
k = k * ami % m_p;
}
// never should get here . . .
return -1;
}
private:
Z m_p;
Z m_root_p;
Z m_inv_base_pow_m;
std::unordered_map<Z, Z> m_lookup_table;
};
}}
#endif

View File

@ -0,0 +1,67 @@
/*
* (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)
*/
#ifndef BOOST_INTEGER_EXTENDED_EUCLIDEAN_HPP
#define BOOST_INTEGER_EXTENDED_EUCLIDEAN_HPP
#include <tuple>
#include <limits>
namespace boost { namespace integer {
// From "The Joy of Factoring", Algorithm 2.7.
// Should the tuple be a named tuple? Is that possible?
// Solves mx + ny = gcd(m,n). Returns tuple with (gcd(m,n), x, y).
template<class Z>
std::tuple<Z, Z, Z> extended_euclidean(Z m, Z n)
{
using std::numeric_limits;
static_assert(numeric_limits<Z>::is_integer,
"The extended Euclidean algorithm works on integral types.\n");
static_assert(numeric_limits<Z>::is_signed,
"The extended Euclidean algorithm only works on signed integer types.\n");
if (m < 1 || n < 1)
{
throw std::domain_error("Arguments must be strictly positive.\n");
}
bool swapped = false;
if (m < n)
{
swapped = true;
std::swap(m, n);
}
Z u0 = m;
Z u1 = 1;
Z u2 = 0;
Z v0 = n;
Z v1 = 0;
Z v2 = 1;
Z w0;
Z w1;
Z w2;
while(v0 > 0)
{
Z q = u0/v0;
w0 = u0 - q*v0;
w1 = u1 - q*v1;
w2 = u2 - q*v2;
u0 = v0;
u1 = v1;
u2 = v2;
v0 = w0;
v1 = w1;
v2 = w2;
}
if (swapped)
{
std::swap(u1, u2);
}
return std::make_tuple(u0, u1, u2);
}
}}
#endif

View File

@ -0,0 +1,34 @@
/*
* (C) Copyright Nick Thompson 2017.
* 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)
*
* The integer floor_sqrt doesn't lose precision like a cast does.
* Based on Algorithm 5.9 of "The Joy of Factoring".
*/
#ifndef BOOST_INTEGER_FLOOR_SQRT_HPP
#define BOOST_INTEGER_FLOOR_SQRT_HPP
#include <limits>
namespace boost { namespace integer {
template<class Z>
Z floor_sqrt(Z N)
{
static_assert(std::numeric_limits<Z>::is_integer,
"The floor_sqrt function is for taking square roots of integers.\n");
Z x = N;
Z y = x/2 + (x&1);
while (y < x) {
x = y;
y = (x + N / x)/2;
}
return x;
}
}}
#endif

View File

@ -0,0 +1,39 @@
/*
* (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)
*/
#ifndef BOOST_INTEGER_MODULAR_EXPONENTIATION_HPP
#define BOOST_INTEGER_MODULAR_EXPONENTIATION_HPP
#include <limits>
namespace boost { namespace integer {
template<class Z>
Z modular_exponentiation(Z base, Z exponent, Z modulus)
{
using std::numeric_limits;
static_assert(numeric_limits<Z>::is_integer,
"Modular exponentiation works on integral types.\n");
Z result = 1;
if (exponent < 0 || modulus < 0)
{
throw std::domain_error("Both the exponent and the modulus must be > 0.\n");
}
while (exponent > 0)
{
if (exponent & 1)
{
result = (result*base) % modulus;
}
base = (base*base) % modulus;
exponent >>= 1;
}
return result;
}
}}
#endif

View File

@ -0,0 +1,50 @@
/*
* (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)
*/
#ifndef BOOST_INTEGER_MODULAR_MULTIPLICATIVE_INVERSE_HPP
#define BOOST_INTEGER_MODULAR_MULTIPLICATIVE_INVERSE_HPP
#include <limits>
#include <boost/optional.hpp>
#include <boost/integer/extended_euclidean.hpp>
namespace boost { namespace integer {
// From "The Joy of Factoring", Algorithm 2.7.
template<class Z>
boost::optional<Z> modular_multiplicative_inverse(Z a, Z modulus)
{
using std::numeric_limits;
static_assert(numeric_limits<Z>::is_integer,
"The modular multiplicative inverse works on integral types.\n");
if (modulus < 2)
{
throw std::domain_error("Modulus must be > 1.\n");
}
// make sure a < modulus:
a = a % modulus;
if (a == 0)
{
// a doesn't have a modular multiplicative inverse:
return {};
}
auto u = extended_euclidean(a, modulus);
Z gcd = std::get<0>(u);
if (gcd > 1)
{
return {};
}
Z x = std::get<1>(u);
// x might not be in the range 0 < x < m, let's fix that:
x = x % modulus;
while (x <= 0)
{
x += modulus;
}
return x;
}
}}
#endif

View File

@ -10,12 +10,16 @@ obj has_gmpxx : has_gmpxx.cpp ;
explicit has_gmpxx ;
test-suite integer
:
:
[ run integer_traits_test.cpp ]
[ run integer_test.cpp : : : <toolset>gcc:<cxxflags>-Wno-long-long <toolset>darwin:<cxxflags>-Wno-long-long <toolset>sun:<cxxflags>"-Qoption ccfe -tmpldepth=128" ]
[ run integer_mask_test.cpp ]
[ run static_log2_test.cpp ]
[ run static_min_max_test.cpp ]
[ run discrete_log_test.cpp ]
[ run extended_euclidean_test.cpp ]
[ run modular_exponentiation_test.cpp ]
[ run modular_multiplicative_inverse_test.cpp ]
[ compile integer_traits_include_test.cpp ]
[ compile integer_include_test.cpp ]
[ compile integer_mask_include_test.cpp ]

View File

@ -0,0 +1,75 @@
/*
* (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)
*/
#define BOOST_TEST_MODULE discrete_log_test
#include <boost/test/included/unit_test.hpp>
#include <boost/integer/discrete_log.hpp>
using boost::integer::trial_multiplication_discrete_log;
using boost::integer::baby_step_giant_step_discrete_log;
template<class Z>
void test_trial_multiplication_discrete_log()
{
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);
BOOST_CHECK_EQUAL(1, x.value());
x = trial_multiplication_discrete_log<Z>(2, 1, 4);
BOOST_CHECK_EQUAL(0, x.value());
x = trial_multiplication_discrete_log<Z>(2, 2, 4);
BOOST_CHECK_EQUAL(1, x.value());
// No solution to 2^^x mod 4 = 3:
x = trial_multiplication_discrete_log<Z>(2, 3, 4);
BOOST_TEST(!x);
x = trial_multiplication_discrete_log<Z>(7, 7, 41);
BOOST_CHECK_EQUAL(1, x.value());
x = trial_multiplication_discrete_log<Z>(7, 8, 41);
BOOST_CHECK_EQUAL(2, x.value());
x = trial_multiplication_discrete_log<Z>(7, 15, 41);
BOOST_CHECK_EQUAL(3, x.value());
x = trial_multiplication_discrete_log<Z>(7, 23, 41);
BOOST_CHECK_EQUAL(4, x.value());
x = trial_multiplication_discrete_log<Z>(7, 38, 41);
BOOST_CHECK_EQUAL(5, x.value());
x = trial_multiplication_discrete_log<Z>(7, 20, 41);
BOOST_CHECK_EQUAL(6, x.value());
Z k = 1;
for (Z i = 0; i < 40; ++i)
{
x = trial_multiplication_discrete_log<Z>(7, k, 41);
BOOST_CHECK_EQUAL(i, x.value());
k = (7*k) % 41;
}
}
template<class Z>
void test_bsgs_discrete_log()
{
baby_step_giant_step_discrete_log<Z> dl(7, 41);
BOOST_CHECK_EQUAL(dl(7), 1);
BOOST_CHECK_EQUAL(dl(8), 2);
BOOST_CHECK_EQUAL(dl(15), 3);
BOOST_CHECK_EQUAL(dl(23), 4);
BOOST_CHECK_EQUAL(dl(38), 5);
BOOST_CHECK_EQUAL(dl(20), 6);
}
BOOST_AUTO_TEST_CASE(discrete_log_test)
{
test_trial_multiplication_discrete_log<size_t>();
test_bsgs_discrete_log<int>();
}

View File

@ -0,0 +1,41 @@
/*
* (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)
*/
#define BOOST_TEST_MODULE extended_euclidean_test
#include <boost/test/included/unit_test.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/integer/common_factor.hpp>
#include <boost/integer/extended_euclidean.hpp>
using boost::multiprecision::int128_t;
using boost::integer::extended_euclidean;
using boost::integer::gcd;
template<class Z>
void test_extended_euclidean()
{
Z max_arg = 500;
for (Z m = 1; m < max_arg; ++m)
{
for (Z n = 1; n < max_arg; ++n)
{
std::tuple<Z, Z, Z> u = extended_euclidean(m, n);
Z gcdmn = gcd(m, n);
Z x = std::get<1>(u);
Z y = std::get<2>(u);
BOOST_CHECK_EQUAL(std::get<0>(u), gcdmn);
BOOST_CHECK_EQUAL(m*x + n*y, gcdmn);
}
}
}
BOOST_AUTO_TEST_CASE(extended_euclidean_test)
{
test_extended_euclidean<int>();
test_extended_euclidean<long>();
test_extended_euclidean<size_t>();
test_extended_euclidean<int128_t>();
}

View File

@ -0,0 +1,38 @@
/*
* (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)
*
*/
#define BOOST_TEST_MODULE modular_exponentiation_test
#include <boost/test/included/unit_test.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/integer/modular_exponentiation.hpp>
using boost::multiprecision::int128_t;
using boost::integer::modular_exponentiation;
template<class Z>
void test_modular_exponentiation()
{
Z base = 7;
Z modulus = 51;
Z expected = 1;
for (Z exponent = 0; exponent < 10000; ++exponent)
{
Z x = modular_exponentiation<Z>(base, exponent, modulus);
BOOST_CHECK_EQUAL(expected, x);
expected = (expected*base) % modulus;
}
}
BOOST_AUTO_TEST_CASE(modular_exponentiation_test)
{
test_modular_exponentiation<int>();
test_modular_exponentiation<unsigned>();
test_modular_exponentiation<short>();
test_modular_exponentiation<size_t>();
test_modular_exponentiation<int128_t>();
}

View File

@ -0,0 +1,48 @@
/*
* (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)
*/
#define BOOST_TEST_MODULE modular_multiplicative_inverse_test
#include <boost/test/included/unit_test.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/integer/common_factor.hpp>
#include <boost/integer/modular_multiplicative_inverse.hpp>
using boost::multiprecision::int128_t;
using boost::integer::modular_multiplicative_inverse;
using boost::integer::gcd;
template<class Z>
void test_modular_multiplicative_inverse()
{
Z max_arg = 1000;
for (Z modulus = 2; modulus < max_arg; ++modulus)
{
for (Z a = 1; a < max_arg; ++a)
{
Z gcdam = gcd(a, modulus);
boost::optional<Z> inv_a = modular_multiplicative_inverse(a, modulus);
// Should fail if gcd(a, mod) != 1:
if (gcdam > 1)
{
BOOST_CHECK(!inv_a);
}
else
{
BOOST_CHECK(inv_a.value() > 0);
Z outta_be_one = (inv_a.value()*a) % modulus;
BOOST_CHECK_EQUAL(outta_be_one, 1);
}
}
}
}
BOOST_AUTO_TEST_CASE(extended_euclidean_test)
{
test_modular_multiplicative_inverse<int>();
test_modular_multiplicative_inverse<long>();
test_modular_multiplicative_inverse<long long>();
test_modular_multiplicative_inverse<int128_t>();
}