delaunay_test.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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)
  7. #include <boost/multiprecision/cpp_int.hpp>
  8. #include "arithmetic_backend.hpp"
  9. #include <boost/chrono.hpp>
  10. #include <boost/random/mersenne_twister.hpp>
  11. #include <boost/random/uniform_int_distribution.hpp>
  12. #include <fstream>
  13. #include <iomanip>
  14. template <class Clock>
  15. struct stopwatch
  16. {
  17. typedef typename Clock::duration duration;
  18. stopwatch()
  19. {
  20. m_start = Clock::now();
  21. }
  22. duration elapsed()
  23. {
  24. return Clock::now() - m_start;
  25. }
  26. void reset()
  27. {
  28. m_start = Clock::now();
  29. }
  30. private:
  31. typename Clock::time_point m_start;
  32. };
  33. // Custom 128-bit maths used for exact calculation of the Delaunay test.
  34. // Only the few operators actually needed here are implemented.
  35. struct int128_t
  36. {
  37. int64_t high;
  38. uint64_t low;
  39. int128_t() {}
  40. int128_t(int32_t i) : high(i >> 31), low(static_cast<int64_t>(i)) {}
  41. int128_t(uint32_t i) : high(0), low(i) {}
  42. int128_t(int64_t i) : high(i >> 63), low(i) {}
  43. int128_t(uint64_t i) : high(0), low(i) {}
  44. };
  45. inline int128_t operator<<(int128_t val, int amt)
  46. {
  47. int128_t r;
  48. r.low = val.low << amt;
  49. r.high = val.low >> (64 - amt);
  50. r.high |= val.high << amt;
  51. return r;
  52. }
  53. inline int128_t& operator+=(int128_t& l, int128_t r)
  54. {
  55. l.low += r.low;
  56. bool carry = l.low < r.low;
  57. l.high += r.high;
  58. if (carry)
  59. ++l.high;
  60. return l;
  61. }
  62. inline int128_t operator-(int128_t val)
  63. {
  64. val.low = ~val.low;
  65. val.high = ~val.high;
  66. val.low += 1;
  67. if (val.low == 0)
  68. val.high += 1;
  69. return val;
  70. }
  71. inline int128_t operator+(int128_t l, int128_t r)
  72. {
  73. l += r;
  74. return l;
  75. }
  76. inline bool operator<(int128_t l, int128_t r)
  77. {
  78. if (l.high != r.high)
  79. return l.high < r.high;
  80. return l.low < r.low;
  81. }
  82. inline int128_t mult_64x64_to_128(int64_t a, int64_t b)
  83. {
  84. // Make life simple by dealing only with positive numbers:
  85. bool neg = false;
  86. if (a < 0)
  87. {
  88. neg = !neg;
  89. a = -a;
  90. }
  91. if (b < 0)
  92. {
  93. neg = !neg;
  94. b = -b;
  95. }
  96. // Divide input into 32-bit halves:
  97. uint32_t ah = a >> 32;
  98. uint32_t al = a & 0xffffffff;
  99. uint32_t bh = b >> 32;
  100. uint32_t bl = b & 0xffffffff;
  101. // Long multiplication, with 64-bit temporaries:
  102. // ah al
  103. // * bh bl
  104. // ----------------
  105. // al*bl (t1)
  106. // + ah*bl (t2)
  107. // + al*bh (t3)
  108. // + ah*bh (t4)
  109. // ----------------
  110. uint64_t t1 = static_cast<uint64_t>(al) * bl;
  111. uint64_t t2 = static_cast<uint64_t>(ah) * bl;
  112. uint64_t t3 = static_cast<uint64_t>(al) * bh;
  113. uint64_t t4 = static_cast<uint64_t>(ah) * bh;
  114. int128_t r(t1);
  115. r.high = t4;
  116. r += int128_t(t2) << 32;
  117. r += int128_t(t3) << 32;
  118. if (neg)
  119. r = -r;
  120. return r;
  121. }
  122. template <class R, class T>
  123. BOOST_FORCEINLINE void mul_2n(R& r, const T& a, const T& b)
  124. {
  125. r = a;
  126. r *= b;
  127. }
  128. template <class B, boost::multiprecision::expression_template_option ET, class T>
  129. BOOST_FORCEINLINE void mul_2n(boost::multiprecision::number<B, ET>& r, const T& a, const T& b)
  130. {
  131. multiply(r, a, b);
  132. }
  133. BOOST_FORCEINLINE void mul_2n(int128_t& r, const boost::int64_t& a, const boost::int64_t& b)
  134. {
  135. r = mult_64x64_to_128(a, b);
  136. }
  137. template <class Traits>
  138. inline bool delaunay_test(int32_t ax, int32_t ay, int32_t bx, int32_t by,
  139. int32_t cx, int32_t cy, int32_t dx, int32_t dy)
  140. {
  141. // Test whether the quadrilateral ABCD's diagonal AC should be flipped to BD.
  142. // This is the Cline & Renka method.
  143. // Flip if the sum of the angles ABC and CDA is greater than 180 degrees.
  144. // Equivalently, flip if sin(ABC + CDA) < 0.
  145. // Trig identity: cos(ABC) * sin(CDA) + sin(ABC) * cos(CDA) < 0
  146. // We can use scalar and vector products to find sin and cos, and simplify
  147. // to the following code.
  148. // Numerical robustness is important. This code addresses it by performing
  149. // exact calculations with large integer types.
  150. //
  151. // NOTE: This routine is limited to inputs with up to 30 BIT PRECISION, which
  152. // is to say all inputs must be in the range [INT_MIN/2, INT_MAX/2].
  153. typedef typename Traits::i64_t i64;
  154. typedef typename Traits::i128_t i128;
  155. i64 cos_abc, t;
  156. mul_2n(cos_abc, (ax - bx), (cx - bx)); // subtraction yields 31-bit values, multiplied to give 62-bit values
  157. mul_2n(t, (ay - by), (cy - by));
  158. cos_abc += t; // addition yields 63 bit value, leaving one left for the sign
  159. i64 cos_cda;
  160. mul_2n(cos_cda, (cx - dx), (ax - dx));
  161. mul_2n(t, (cy - dy), (ay - dy));
  162. cos_cda += t;
  163. if (cos_abc >= 0 && cos_cda >= 0)
  164. return false;
  165. if (cos_abc < 0 && cos_cda < 0)
  166. return true;
  167. i64 sin_abc;
  168. mul_2n(sin_abc, (ax - bx), (cy - by));
  169. mul_2n(t, (cx - bx), (ay - by));
  170. sin_abc -= t;
  171. i64 sin_cda;
  172. mul_2n(sin_cda, (cx - dx), (ay - dy));
  173. mul_2n(t, (ax - dx), (cy - dy));
  174. sin_cda -= t;
  175. i128 sin_sum, t128;
  176. mul_2n(sin_sum, sin_abc, cos_cda); // 63-bit inputs multiplied to 126-bit output
  177. mul_2n(t128, cos_abc, sin_cda);
  178. sin_sum += t128; // Addition yields 127 bit result, leaving one bit for the sign
  179. return sin_sum < 0;
  180. }
  181. struct dt_dat
  182. {
  183. int32_t ax, ay, bx, by, cx, cy, dx, dy;
  184. };
  185. typedef std::vector<dt_dat> data_t;
  186. data_t data;
  187. template <class Traits>
  188. void do_calc(const char* name)
  189. {
  190. std::cout << "Running calculations for: " << name << std::endl;
  191. stopwatch<boost::chrono::high_resolution_clock> w;
  192. boost::uint64_t flips = 0;
  193. boost::uint64_t calcs = 0;
  194. for (int j = 0; j < 1000; ++j)
  195. {
  196. for (data_t::const_iterator i = data.begin(); i != data.end(); ++i)
  197. {
  198. const dt_dat& d = *i;
  199. bool flip = delaunay_test<Traits>(d.ax, d.ay, d.bx, d.by, d.cx, d.cy, d.dx, d.dy);
  200. if (flip)
  201. ++flips;
  202. ++calcs;
  203. }
  204. }
  205. double t = boost::chrono::duration_cast<boost::chrono::duration<double> >(w.elapsed()).count();
  206. std::cout << "Number of calculations = " << calcs << std::endl;
  207. std::cout << "Number of flips = " << flips << std::endl;
  208. std::cout << "Total execution time = " << t << std::endl;
  209. std::cout << "Time per calculation = " << t / calcs << std::endl
  210. << std::endl;
  211. }
  212. template <class I64, class I128>
  213. struct test_traits
  214. {
  215. typedef I64 i64_t;
  216. typedef I128 i128_t;
  217. };
  218. dt_dat generate_quadrilateral()
  219. {
  220. static boost::random::mt19937 gen;
  221. static boost::random::uniform_int_distribution<> dist(INT_MIN / 2, INT_MAX / 2);
  222. dt_dat result;
  223. result.ax = dist(gen);
  224. result.ay = dist(gen);
  225. result.bx = boost::random::uniform_int_distribution<>(result.ax, INT_MAX / 2)(gen); // bx is to the right of ax.
  226. result.by = dist(gen);
  227. result.cx = dist(gen);
  228. 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.
  229. result.dx = boost::random::uniform_int_distribution<>(result.cx, INT_MAX / 2)(gen); // dx is to the right of cx.
  230. 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.
  231. return result;
  232. }
  233. static void load_data()
  234. {
  235. for (unsigned i = 0; i < 100000; ++i)
  236. data.push_back(generate_quadrilateral());
  237. }
  238. int main()
  239. {
  240. using namespace boost::multiprecision;
  241. std::cout << "loading data...\n";
  242. load_data();
  243. std::cout << "calculating...\n";
  244. do_calc<test_traits<boost::int64_t, boost::int64_t> >("int64_t, int64_t");
  245. 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>");
  246. do_calc<test_traits<boost::int64_t, number<arithmetic_backend<boost::int64_t>, et_off> > >("int64_t, arithmetic_backend<int64_t>");
  247. 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");
  248. do_calc<test_traits<boost::int64_t, ::int128_t> >("int64_t, int128_t");
  249. do_calc<test_traits<boost::int64_t, boost::multiprecision::int128_t> >("int64_t, boost::multiprecision::int128_t");
  250. 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)");
  251. 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");
  252. do_calc<test_traits<boost::int64_t, cpp_int> >("int64_t, cpp_int");
  253. do_calc<test_traits<boost::int64_t, number<cpp_int_backend<>, et_off> > >("int64_t, cpp_int (no ET's)");
  254. do_calc<test_traits<boost::int64_t, number<cpp_int_backend<128> > > >("int64_t, cpp_int(128-bit cache)");
  255. do_calc<test_traits<boost::int64_t, number<cpp_int_backend<128>, et_off> > >("int64_t, cpp_int (128-bit Cache no ET's)");
  256. return 0;
  257. }