1 // Copyright Jeremy Murphy 2016.
2 // Use, modification and distribution are subject to the
3 // Boost Software License, Version 1.0. (See accompanying file
4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 # pragma warning (disable : 4224)
10 #include <boost/math/common_factor_rt.hpp>
11 #include <boost/math/special_functions/prime.hpp>
12 #include <boost/multiprecision/cpp_int.hpp>
13 #include <boost/multiprecision/integer.hpp>
14 #include <boost/random.hpp>
15 #include <boost/array.hpp>
21 #include <type_traits>
24 #include "fibonacci.hpp"
25 #include "../../test/table_type.hpp"
26 #include "table_helper.hpp"
27 #include "performance.hpp"
32 boost::multiprecision::cpp_int
total_sum(0);
34 template <typename Func
, class Table
>
35 double exec_timed_test_foo(Func f
, const Table
& data
, double min_elapsed
= 0.5)
39 typename
Table::value_type::first_type sum
{0};
40 stopwatch
<boost::chrono::high_resolution_clock
> w
;
43 for(unsigned count
= 0; count
< repeats
; ++count
)
45 for(typename
Table::size_type n
= 0; n
< data
.size(); ++n
)
46 sum
+= f(data
[n
].first
, data
[n
].second
);
49 t
= boost::chrono::duration_cast
<boost::chrono::duration
<double>>(w
.elapsed()).count();
53 while(t
< min_elapsed
);
60 struct test_function_template
62 vector
<pair
<T
, T
> > const & data
;
63 const char* data_name
;
65 test_function_template(vector
<pair
<T
, T
> > const &data
, const char* name
) : data(data
), data_name(name
) {}
67 template <typename Function
>
68 void operator()(pair
<Function
, string
> const &f
) const
70 auto result
= exec_timed_test_foo(f
.first
, data
);
71 auto table_name
= string("gcd method comparison with ") + compiler_name() + string(" on ") + platform_name();
73 report_execution_time(result
,
76 string(f
.second
) + "\n" + boost_name());
80 boost::random::mt19937 rng
;
81 boost::random::uniform_int_distribution
<> d_0_6(0, 6);
82 boost::random::uniform_int_distribution
<> d_1_20(1, 20);
85 T
get_prime_products()
87 int n_primes
= d_0_6(rng
);
91 // Generate a power of 2:
92 return static_cast<T
>(1u) << d_1_20(rng
);
95 return boost::math::prime(d_1_20(rng
) + 3);
98 for(int i
= 0; i
< n_primes
; ++i
)
99 result
*= boost::math::prime(d_1_20(rng
) + 3) * boost::math::prime(d_1_20(rng
) + 3) * boost::math::prime(d_1_20(rng
) + 3) * boost::math::prime(d_1_20(rng
) + 3) * boost::math::prime(d_1_20(rng
) + 3);
104 T
get_uniform_random()
106 static boost::random::uniform_int_distribution
<T
> minmax((std::numeric_limits
<T
>::min
)(), (std::numeric_limits
<T
>::max
)());
111 inline bool even(T
const& val
)
116 template <class Backend
, boost::multiprecision::expression_template_option ExpressionTemplates
>
117 inline bool even(boost::multiprecision::number
<Backend
, ExpressionTemplates
> const& val
)
119 return !bit_test(val
, 0);
123 T
euclid_textbook(T a
, T b
)
138 T
binary_textbook(T u
, T v
)
142 unsigned shifts
= (std::min
)(boost::multiprecision::lsb(u
), boost::multiprecision::lsb(v
));
150 unsigned bit_index
= boost::multiprecision::lsb(u
);
155 else if(bit_index
= boost::multiprecision::lsb(v
))
172 template <typename Integer
>
173 inline BOOST_CXX14_CONSTEXPR Integer
gcd_default(Integer a
, Integer b
) BOOST_GCD_NOEXCEPT(Integer
)
175 using boost::math::gcd
;
181 void test_type(const char* name
)
183 using namespace boost::math::gcd_detail
;
185 std::vector
<pair
<int_type
, int_type
> > data
;
187 for(unsigned i
= 0; i
< 1000; ++i
)
189 data
.push_back(std::make_pair(get_prime_products
<T
>(), get_prime_products
<T
>()));
191 std::string
row_name("gcd<");
193 row_name
+= "> (random prime number products)";
195 typedef pair
< function
<int_type(int_type
, int_type
)>, string
> f_test
;
196 array
<f_test
, 6> test_functions
{ {
197 { gcd_default
<int_type
>, "gcd" },
198 { Euclid_gcd
<int_type
>, "Euclid_gcd" },
199 { Stein_gcd
<int_type
>, "Stein_gcd" } ,
200 { mixed_binary_gcd
<int_type
>, "mixed_binary_gcd" },
201 { binary_textbook
<int_type
>, "Stein_gcd_textbook" },
202 { euclid_textbook
<int_type
>, "gcd_euclid_textbook" },
204 for_each(begin(test_functions
), end(test_functions
), test_function_template
<int_type
>(data
, row_name
.c_str()));
207 for(unsigned i
= 0; i
< 1000; ++i
)
209 data
.push_back(std::make_pair(get_uniform_random
<T
>(), get_uniform_random
<T
>()));
214 row_name
+= "> (uniform random numbers)";
215 for_each(begin(test_functions
), end(test_functions
), test_function_template
<int_type
>(data
, row_name
.c_str()));
217 // Fibonacci number tests:
221 row_name
+= "> (adjacent Fibonacci numbers)";
222 for_each(begin(test_functions
), end(test_functions
), test_function_template
<int_type
>(fibonacci_numbers_permution_1
<T
>(), row_name
.c_str()));
227 row_name
+= "> (permutations of Fibonacci numbers)";
228 for_each(begin(test_functions
), end(test_functions
), test_function_template
<int_type
>(fibonacci_numbers_permution_2
<T
>(), row_name
.c_str()));
233 row_name
+= "> (Trivial cases)";
234 for_each(begin(test_functions
), end(test_functions
), test_function_template
<int_type
>(trivial_gcd_test_cases
<T
>(), row_name
.c_str()));
237 /*******************************************************************************************************************/
240 T
generate_random(unsigned bits_wanted
)
242 static boost::random::mt19937 gen
;
243 typedef boost::random::mt19937::result_type random_type
;
247 if(std::numeric_limits
<T
>::is_bounded
&& (bits_wanted
== (unsigned)std::numeric_limits
<T
>::digits
))
249 max_val
= (std::numeric_limits
<T
>::max
)();
250 digits
= std::numeric_limits
<T
>::digits
;
254 max_val
= T(1) << bits_wanted
;
255 digits
= bits_wanted
;
258 unsigned bits_per_r_val
= std::numeric_limits
<random_type
>::digits
- 1;
259 while((random_type(1) << bits_per_r_val
) > (gen
.max
)()) --bits_per_r_val
;
261 unsigned terms_needed
= digits
/ bits_per_r_val
+ 1;
264 for(unsigned i
= 0; i
< terms_needed
; ++i
)
273 template <typename N
>
274 N
gcd_stein(N m
, N n
)
276 BOOST_ASSERT(m
>= static_cast<N
>(0));
277 BOOST_ASSERT(n
>= static_cast<N
>(0));
278 if(m
== N(0)) return n
;
279 if(n
== N(0)) return m
;
282 while(even(m
)) { m
>>= 1; d_m
++; }
284 while(even(n
)) { n
>>= 1; d_n
++; }
287 if(n
> m
) swap(n
, m
);
289 do m
>>= 1; while(even(m
));
292 return m
<< (std::min
)(d_m
, d_n
);
296 boost::multiprecision::cpp_int
big_gcd(const boost::multiprecision::cpp_int
& a
, const boost::multiprecision::cpp_int
& b
)
298 return boost::multiprecision::gcd(a
, b
);
301 namespace boost
{ namespace multiprecision
{ namespace backends
{
303 template <unsigned MinBits1
, unsigned MaxBits1
, cpp_integer_type SignType1
, cpp_int_check_type Checked1
, class Allocator1
>
304 inline typename enable_if_c
<!is_trivial_cpp_int
<cpp_int_backend
<MinBits1
, MaxBits1
, SignType1
, Checked1
, Allocator1
> >::value
>::type
306 cpp_int_backend
<MinBits1
, MaxBits1
, SignType1
, Checked1
, Allocator1
>& result
,
307 const cpp_int_backend
<MinBits1
, MaxBits1
, SignType1
, Checked1
, Allocator1
>& a
,
308 const cpp_int_backend
<MinBits1
, MaxBits1
, SignType1
, Checked1
, Allocator1
>& b
)
310 using default_ops::eval_lsb
;
311 using default_ops::eval_is_zero
;
312 using default_ops::eval_get_sign
;
316 eval_gcd(result
, b
, *a
.limbs());
321 eval_gcd(result
, a
, *b
.limbs());
327 cpp_int_backend
<MinBits1
, MaxBits1
, SignType1
, Checked1
, Allocator1
> u(a
), v(b
), mod
;
329 int s
= eval_get_sign(u
);
341 s
= eval_get_sign(v
);
352 /* Let shift := lg K, where K is the greatest power of 2
353 dividing both u and v. */
355 unsigned us
= eval_lsb(u
);
356 unsigned vs
= eval_lsb(v
);
357 shift
= (std::min
)(us
, vs
);
358 eval_right_shift(u
, us
);
359 eval_right_shift(v
, vs
);
361 // From now on access u and v via pointers, that way we have a trivial swap:
362 cpp_int_backend
<MinBits1
, MaxBits1
, SignType1
, Checked1
, Allocator1
>* up(&u
), *vp(&v
), *mp(&mod
);
366 /* Now u and v are both odd, so diff(u, v) is even.
367 Let u = min(u, v), v = diff(u, v)/2. */
368 s
= up
->compare(*vp
);
376 *up
= boost::math::gcd_detail::mixed_binary_gcd(*vp
->limbs(), *up
->limbs());
379 double_limb_type i
, j
;
380 i
= vp
->limbs()[0] | (static_cast<double_limb_type
>(vp
->limbs()[1]) << sizeof(limb_type
) * CHAR_BIT
);
381 j
= (up
->size() == 1) ? *up
->limbs() : up
->limbs()[0] | (static_cast<double_limb_type
>(up
->limbs()[1]) << sizeof(limb_type
) * CHAR_BIT
);
382 u
= boost::math::gcd_detail::mixed_binary_gcd(i
, j
);
386 if(vp
->size() > up
->size() /*eval_msb(*vp) > eval_msb(*up) + 32*/)
388 eval_modulus(*mp
, *vp
, *up
);
390 eval_subtract(*up
, *vp
);
391 if(eval_is_zero(*vp
) == 0)
394 eval_right_shift(*vp
, vs
);
398 if(eval_is_zero(*up
) == 0)
401 eval_right_shift(*up
, vs
);
411 eval_subtract(*vp
, *up
);
413 eval_right_shift(*vp
, vs
);
419 eval_left_shift(result
, shift
);
425 boost::multiprecision::cpp_int
big_gcd_new(const boost::multiprecision::cpp_int
& a
, const boost::multiprecision::cpp_int
& b
)
427 boost::multiprecision::cpp_int result
;
428 boost::multiprecision::backends::eval_gcd_new(result
.backend(), a
.backend(), b
.backend());
433 void test_n_bits(unsigned n
, std::string data_name
, const std::vector
<pair
<boost::multiprecision::cpp_int
, boost::multiprecision::cpp_int
> >* p_data
= 0)
435 using namespace boost::math::detail
;
436 typedef boost::multiprecision::cpp_int int_type
;
437 std::vector
<pair
<int_type
, int_type
> > data
, data2
;
439 for(unsigned i
= 0; i
< 1000; ++i
)
441 data
.push_back(std::make_pair(generate_random
<int_type
>(n
), generate_random
<int_type
>(n
)));
444 typedef pair
< function
<int_type(int_type
, int_type
)>, string
> f_test
;
445 array
<f_test
, 2> test_functions
{ { /*{ Stein_gcd<int_type>, "Stein_gcd" } ,{ Euclid_gcd<int_type>, "Euclid_gcd" },{ binary_textbook<int_type>, "Stein_gcd_textbook" },{ euclid_textbook<int_type>, "gcd_euclid_textbook" },{ mixed_binary_gcd<int_type>, "mixed_binary_gcd" },{ gcd_stein<int_type>, "gcd_stein" },*/{ big_gcd
, "boost::multiprecision::gcd" },{ big_gcd_new
, "big_gcd_new" } } };
446 for_each(begin(test_functions
), end(test_functions
), test_function_template
<int_type
>(p_data
? *p_data
: data
, data_name
.c_str(), true));
452 test_type
<unsigned short>("unsigned short");
453 test_type
<unsigned>("unsigned");
454 test_type
<unsigned long>("unsigned long");
455 test_type
<unsigned long long>("unsigned long long");
457 test_type
<boost::multiprecision::uint256_t
>("boost::multiprecision::uint256_t");
458 test_type
<boost::multiprecision::uint512_t
>("boost::multiprecision::uint512_t");
459 test_type
<boost::multiprecision::uint1024_t
>("boost::multiprecision::uint1024_t");
462 test_n_bits(16, " 16 bit random values");
463 test_n_bits(32, " 32 bit random values");
464 test_n_bits(64, " 64 bit random values");
465 test_n_bits(125, " 125 bit random values");
466 test_n_bits(250, " 250 bit random values");
467 test_n_bits(500, " 500 bit random values");
468 test_n_bits(1000, " 1000 bit random values");
469 test_n_bits(5000, " 5000 bit random values");
470 test_n_bits(10000, "10000 bit random values");
471 //test_n_bits(100000);
472 //test_n_bits(1000000);
474 test_n_bits(0, "consecutive first 1000 fibonacci numbers", &fibonacci_numbers_cpp_int_permution_1());
475 test_n_bits(0, "permutations of first 1000 fibonacci numbers", &fibonacci_numbers_cpp_int_permution_2());