1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright 2012 John Maddock.
3 // Copyright 2012 Phil Endecott
4 // Distributed under the Boost
5 // Software License, Version 1.0. (See accompanying file
6 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 #include <boost/multiprecision/cpp_int.hpp>
9 #include "arithmetic_backend.hpp"
10 #include <boost/chrono.hpp>
11 #include <boost/random/mersenne_twister.hpp>
12 #include <boost/random/uniform_int_distribution.hpp>
17 template <class Clock
>
20 typedef typename
Clock::duration duration
;
23 m_start
= Clock::now();
27 return Clock::now() - m_start
;
31 m_start
= Clock::now();
35 typename
Clock::time_point m_start
;
38 // Custom 128-bit maths used for exact calculation of the Delaunay test.
39 // Only the few operators actually needed here are implemented.
46 int128_t(int32_t i
): high(i
>>31), low(static_cast<int64_t>(i
)) {}
47 int128_t(uint32_t i
): high(0), low(i
) {}
48 int128_t(int64_t i
): high(i
>>63), low(i
) {}
49 int128_t(uint64_t i
): high(0), low(i
) {}
52 inline int128_t
operator<<(int128_t val
, int amt
)
55 r
.low
= val
.low
<< amt
;
56 r
.high
= val
.low
>> (64-amt
);
57 r
.high
|= val
.high
<< amt
;
61 inline int128_t
& operator+=(int128_t
& l
, int128_t r
)
64 bool carry
= l
.low
< r
.low
;
70 inline int128_t
operator-(int128_t val
)
75 if (val
.low
== 0) val
.high
+= 1;
79 inline int128_t
operator+(int128_t l
, int128_t r
)
85 inline bool operator<(int128_t l
, int128_t r
)
87 if (l
.high
!= r
.high
) return l
.high
< r
.high
;
92 inline int128_t
mult_64x64_to_128(int64_t a
, int64_t b
)
94 // Make life simple by dealing only with positive numbers:
96 if (a
<0) { neg
= !neg
; a
= -a
; }
97 if (b
<0) { neg
= !neg
; b
= -b
; }
99 // Divide input into 32-bit halves:
100 uint32_t ah
= a
>> 32;
101 uint32_t al
= a
& 0xffffffff;
102 uint32_t bh
= b
>> 32;
103 uint32_t bl
= b
& 0xffffffff;
105 // Long multiplication, with 64-bit temporaries:
116 uint64_t t1
= static_cast<uint64_t>(al
)*bl
;
117 uint64_t t2
= static_cast<uint64_t>(ah
)*bl
;
118 uint64_t t3
= static_cast<uint64_t>(al
)*bh
;
119 uint64_t t4
= static_cast<uint64_t>(ah
)*bh
;
123 r
+= int128_t(t2
) << 32;
124 r
+= int128_t(t3
) << 32;
131 template <class R
, class T
>
132 BOOST_FORCEINLINE
void mul_2n(R
& r
, const T
& a
, const T
& b
)
138 template <class B
, boost::multiprecision::expression_template_option ET
, class T
>
139 BOOST_FORCEINLINE
void mul_2n(boost::multiprecision::number
<B
, ET
>& r
, const T
& a
, const T
& b
)
144 BOOST_FORCEINLINE
void mul_2n(int128_t
& r
, const boost::int64_t& a
, const boost::int64_t& b
)
146 r
= mult_64x64_to_128(a
, b
);
149 template <class Traits
>
150 inline bool delaunay_test(int32_t ax
, int32_t ay
, int32_t bx
, int32_t by
,
151 int32_t cx
, int32_t cy
, int32_t dx
, int32_t dy
)
153 // Test whether the quadrilateral ABCD's diagonal AC should be flipped to BD.
154 // This is the Cline & Renka method.
155 // Flip if the sum of the angles ABC and CDA is greater than 180 degrees.
156 // Equivalently, flip if sin(ABC + CDA) < 0.
157 // Trig identity: cos(ABC) * sin(CDA) + sin(ABC) * cos(CDA) < 0
158 // We can use scalar and vector products to find sin and cos, and simplify
159 // to the following code.
160 // Numerical robustness is important. This code addresses it by performing
161 // exact calculations with large integer types.
163 // NOTE: This routine is limited to inputs with up to 30 BIT PRECISION, which
164 // is to say all inputs must be in the range [INT_MIN/2, INT_MAX/2].
166 typedef typename
Traits::i64_t i64
;
167 typedef typename
Traits::i128_t i128
;
170 mul_2n(cos_abc
, (ax
-bx
), (cx
-bx
)); // subtraction yields 31-bit values, multiplied to give 62-bit values
171 mul_2n(t
, (ay
-by
), (cy
-by
));
172 cos_abc
+= t
; // addition yields 63 bit value, leaving one left for the sign
175 mul_2n(cos_cda
, (cx
-dx
), (ax
-dx
));
176 mul_2n(t
, (cy
-dy
), (ay
-dy
));
179 if (cos_abc
>= 0 && cos_cda
>= 0) return false;
180 if (cos_abc
< 0 && cos_cda
< 0) return true;
183 mul_2n(sin_abc
, (ax
-bx
), (cy
-by
));
184 mul_2n(t
, (cx
-bx
), (ay
-by
));
188 mul_2n(sin_cda
, (cx
-dx
), (ay
-dy
));
189 mul_2n(t
, (ax
-dx
), (cy
-dy
));
193 mul_2n(sin_sum
, sin_abc
, cos_cda
); // 63-bit inputs multiplied to 126-bit output
194 mul_2n(t128
, cos_abc
, sin_cda
);
195 sin_sum
+= t128
; // Addition yields 127 bit result, leaving one bit for the sign
202 int32_t ax
, ay
, bx
, by
, cx
, cy
, dx
, dy
;
205 typedef std::vector
<dt_dat
> data_t
;
208 template <class Traits
>
209 void do_calc(const char* name
)
211 std::cout
<< "Running calculations for: " << name
<< std::endl
;
213 stopwatch
<boost::chrono::high_resolution_clock
> w
;
215 boost::uint64_t flips
= 0;
216 boost::uint64_t calcs
= 0;
218 for(int j
= 0; j
< 1000; ++j
)
220 for(data_t::const_iterator i
= data
.begin(); i
!= data
.end(); ++i
)
222 const dt_dat
& d
= *i
;
223 bool flip
= delaunay_test
<Traits
>(d
.ax
,d
.ay
, d
.bx
,d
.by
, d
.cx
,d
.cy
, d
.dx
,d
.dy
);
228 double t
= boost::chrono::duration_cast
<boost::chrono::duration
<double> >(w
.elapsed()).count();
230 std::cout
<< "Number of calculations = " << calcs
<< std::endl
;
231 std::cout
<< "Number of flips = " << flips
<< std::endl
;
232 std::cout
<< "Total execution time = " << t
<< std::endl
;
233 std::cout
<< "Time per calculation = " << t
/ calcs
<< std::endl
<< std::endl
;
236 template <class I64
, class I128
>
244 dt_dat
generate_quadrilateral()
246 static boost::random::mt19937 gen
;
247 static boost::random::uniform_int_distribution
<> dist(INT_MIN
/2, INT_MAX
/2);
251 result
.ax
= dist(gen
);
252 result
.ay
= dist(gen
);
253 result
.bx
= boost::random::uniform_int_distribution
<>(result
.ax
, INT_MAX
/2)(gen
); // bx is to the right of ax.
254 result
.by
= dist(gen
);
255 result
.cx
= dist(gen
);
256 result
.cy
= boost::random::uniform_int_distribution
<>(result
.cx
> result
.bx
? result
.by
: result
.ay
, INT_MAX
/2)(gen
); // cy is below at least one of ay and by.
257 result
.dx
= boost::random::uniform_int_distribution
<>(result
.cx
, INT_MAX
/2)(gen
); // dx is to the right of cx.
258 result
.dy
= boost::random::uniform_int_distribution
<>(result
.cx
> result
.bx
? result
.by
: result
.ay
, INT_MAX
/2)(gen
); // cy is below at least one of ay and by.
263 static void load_data()
265 for(unsigned i
= 0; i
< 100000; ++i
)
266 data
.push_back(generate_quadrilateral());
272 using namespace boost::multiprecision
;
273 std::cout
<< "loading data...\n";
276 std::cout
<< "calculating...\n";
278 do_calc
<test_traits
<boost::int64_t, boost::int64_t> >("int64_t, int64_t");
279 do_calc
<test_traits
<number
<arithmetic_backend
<boost::int64_t>, et_off
>, number
<arithmetic_backend
<boost::int64_t>, et_off
> > >("arithmetic_backend<int64_t>, arithmetic_backend<int64_t>");
280 do_calc
<test_traits
<boost::int64_t, number
<arithmetic_backend
<boost::int64_t>, et_off
> > >("int64_t, arithmetic_backend<int64_t>");
281 do_calc
<test_traits
<number
<cpp_int_backend
<64, 64, boost::multiprecision::signed_magnitude
, boost::multiprecision::unchecked
, void>, et_off
>, number
<cpp_int_backend
<64, 64, boost::multiprecision::signed_magnitude
, boost::multiprecision::unchecked
, void>, et_off
> > >("multiprecision::int64_t, multiprecision::int64_t");
283 do_calc
<test_traits
<boost::int64_t, ::int128_t
> >("int64_t, int128_t");
284 do_calc
<test_traits
<boost::int64_t, boost::multiprecision::int128_t
> >("int64_t, boost::multiprecision::int128_t");
285 do_calc
<test_traits
<boost::int64_t, number
<cpp_int_backend
<128, 128, boost::multiprecision::signed_magnitude
, boost::multiprecision::unchecked
, void>, et_on
> > >("int64_t, int128_t (ET)");
286 do_calc
<test_traits
<number
<cpp_int_backend
<64, 64, boost::multiprecision::signed_magnitude
, boost::multiprecision::unchecked
, void>, et_off
>, boost::multiprecision::int128_t
> >("multiprecision::int64_t, multiprecision::int128_t");
288 do_calc
<test_traits
<boost::int64_t, cpp_int
> >("int64_t, cpp_int");
289 do_calc
<test_traits
<boost::int64_t, number
<cpp_int_backend
<>, et_off
> > >("int64_t, cpp_int (no ET's)");
290 do_calc
<test_traits
<boost::int64_t, number
<cpp_int_backend
<128> > > >("int64_t, cpp_int(128-bit cache)");
291 do_calc
<test_traits
<boost::int64_t, number
<cpp_int_backend
<128>, et_off
> > >("int64_t, cpp_int (128-bit Cache no ET's)");