spinsort.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. //----------------------------------------------------------------------------
  2. /// @file spinsort.hpp
  3. /// @brief Spin Sort algorithm
  4. ///
  5. /// @author Copyright (c) 2016 Francisco José Tapia ([email protected] )\n
  6. /// Distributed under the Boost Software License, Version 1.0.\n
  7. /// ( See accompanying file LICENSE_1_0.txt or copy at
  8. /// http://www.boost.org/LICENSE_1_0.txt )
  9. /// @version 0.1
  10. ///
  11. /// @remarks
  12. //-----------------------------------------------------------------------------
  13. #ifndef __BOOST_SORT_PARALLEL_ALGORITHM_SPIN_SORT_HPP
  14. #define __BOOST_SORT_PARALLEL_ALGORITHM_SPIN_SORT_HPP
  15. #include <ciso646>
  16. #include <cstdlib>
  17. #include <functional>
  18. #include <iterator>
  19. #include <memory>
  20. #include <type_traits>
  21. #include <vector>
  22. #include <cstddef>
  23. #include <boost/sort/insert_sort/insert_sort.hpp>
  24. #include <boost/sort/common/util/traits.hpp>
  25. #include <boost/sort/common/util/algorithm.hpp>
  26. #include <boost/sort/common/range.hpp>
  27. #include <boost/sort/common/indirect.hpp>
  28. namespace boost
  29. {
  30. namespace sort
  31. {
  32. namespace spin_detail
  33. {
  34. //----------------------------------------------------------------------------
  35. // USING SENTENCES
  36. //----------------------------------------------------------------------------
  37. namespace bsc = boost::sort::common;
  38. using bsc::range;
  39. using bsc::util::nbits64;
  40. using bsc::util::compare_iter;
  41. using bsc::util::value_iter;
  42. using boost::sort::insert_sort;
  43. //
  44. //############################################################################
  45. // ##
  46. // D E F I N I T I O N S O F F U N C T I O N S ##
  47. // ##
  48. //############################################################################
  49. //
  50. template <class Iter1_t, class Iter2_t, typename Compare>
  51. static void insert_partial_sort (Iter1_t first, Iter1_t mid,
  52. Iter1_t last, Compare comp,
  53. const range<Iter2_t> &rng_aux);
  54. template<class Iter1_t, class Iter2_t, class Compare>
  55. static bool check_stable_sort (const range<Iter1_t> &rng_data,
  56. const range<Iter2_t> &rng_aux, Compare comp);
  57. template<class Iter1_t, class Iter2_t, class Compare>
  58. static void range_sort (const range<Iter1_t> &range1,
  59. const range<Iter2_t> &range2, Compare comp,
  60. uint32_t level);
  61. template<class Iter1_t, class Iter2_t, class Compare>
  62. static void sort_range_sort (const range<Iter1_t> &rng_data,
  63. const range<Iter2_t> &rng_aux, Compare comp);
  64. //
  65. //-----------------------------------------------------------------------------
  66. // function : insert_partial_sort
  67. /// @brief : Insertion sort of elements sorted
  68. /// @param first: iterator to the first element of the range
  69. /// @param mid : last pointer of the sorted data, and first pointer to the
  70. /// elements to insert
  71. /// @param last : iterator to the next element of the last in the range
  72. /// @param comp :
  73. /// @comments : the two ranges are sorted
  74. //-----------------------------------------------------------------------------
  75. template<class Iter1_t, class Iter2_t, typename Compare>
  76. static void insert_partial_sort (Iter1_t first, Iter1_t mid, Iter1_t last,
  77. Compare comp, const range <Iter2_t> &rng_aux)
  78. {
  79. //------------------------------------------------------------------------
  80. // metaprogram
  81. //------------------------------------------------------------------------
  82. typedef value_iter<Iter1_t> value_t;
  83. typedef value_iter<Iter2_t> value2_t;
  84. static_assert (std::is_same<value_t, value2_t>::value,
  85. "Incompatible iterators\n");
  86. //--------------------------------------------------------------------
  87. // program
  88. //--------------------------------------------------------------------
  89. assert(size_t(last - mid) <= rng_aux.size());
  90. if (mid == last) return;
  91. //insertionsort ( mid, last, comp);
  92. if (first == mid) return;
  93. //------------------------------------------------------------------------
  94. // creation of the vector of elements to insert and their position in the
  95. // sorted part
  96. // the data are inserted in rng_aux
  97. //-----------------------------------------------------------------------
  98. std::vector<Iter1_t> viter;
  99. Iter2_t beta = rng_aux.first, data = rng_aux.first;
  100. for (Iter1_t alpha = mid; alpha != last; ++alpha)
  101. *(beta++) = std::move(*alpha);
  102. size_t ndata = last - mid;
  103. Iter1_t linf = first, lsup = mid;
  104. for (uint32_t i = 0; i < ndata; ++i)
  105. {
  106. Iter1_t it1 = std::upper_bound(linf, lsup, *(data + i), comp);
  107. viter.push_back(it1);
  108. linf = it1;
  109. };
  110. // moving the elements
  111. viter.push_back(mid);
  112. for (uint32_t i = viter.size() - 1; i != 0; --i)
  113. {
  114. Iter1_t src = viter[i], limit = viter[i - 1];
  115. Iter1_t dest = src + (i);
  116. while (src != limit) *(--dest) = std::move(*(--src));
  117. *(viter[i - 1] + (i - 1)) = std::move(*(data + (i - 1)));
  118. };
  119. }
  120. ;
  121. //-----------------------------------------------------------------------------
  122. // function : check_stable_sort
  123. /// @brief check if the elements between first and last are osted or reverse
  124. /// sorted. If the number of elements not sorted is small, insert in
  125. /// the sorted part
  126. /// @param range_input : range with the elements to sort
  127. /// @param range_buffer : range with the elements sorted
  128. /// @param comp : object for to compare two elements
  129. /// @param level : when is 1, sort with the insertionsort algorithm
  130. /// if not make a recursive call splitting the ranges
  131. //
  132. /// @comments : if the number of levels is odd, the data are in the first
  133. /// parameter of range_sort, and the results appear in the second parameter
  134. /// If the number of levels is even, the data are in the second
  135. /// parameter of range_sort, and the results are in the same parameter
  136. //-----------------------------------------------------------------------------
  137. template<class Iter1_t, class Iter2_t, class Compare>
  138. static bool check_stable_sort(const range<Iter1_t> &rng_data,
  139. const range<Iter2_t> &rng_aux, Compare comp)
  140. {
  141. //------------------------------------------------------------------------
  142. // metaprogramming
  143. //------------------------------------------------------------------------
  144. typedef value_iter<Iter1_t> value_t;
  145. typedef value_iter<Iter2_t> value2_t;
  146. static_assert (std::is_same<value_t, value2_t>::value,
  147. "Incompatible iterators\n");
  148. //------------------------------------------------------------------------
  149. // program
  150. //------------------------------------------------------------------------
  151. // the maximun number of elements not ordered, for to be inserted in the
  152. // sorted part
  153. //const ptrdiff_t min_insert_partial_sort = 32 ;
  154. const size_t ndata = rng_data.size();
  155. if (ndata < 32)
  156. {
  157. insert_sort(rng_data.first, rng_data.last, comp);
  158. return true;
  159. };
  160. const size_t min_insert_partial_sort =
  161. ((ndata >> 3) < 33) ? 32 : (ndata >> 3);
  162. if (ndata < 2) return true;
  163. // check if sorted
  164. bool sw = true;
  165. Iter1_t it2 = rng_data.first + 1;
  166. for (Iter1_t it1 = rng_data.first;
  167. it2 != rng_data.last and (sw = not comp(*it2, *it1)); it1 =
  168. it2++)
  169. ;
  170. if (sw) return true;
  171. // insert the elements between it1 and last
  172. if (size_t(rng_data.last - it2) < min_insert_partial_sort)
  173. {
  174. sort_range_sort(range<Iter1_t>(it2, rng_data.last), rng_aux, comp);
  175. insert_partial_sort(rng_data.first, it2, rng_data.last, comp, rng_aux);
  176. return true;
  177. };
  178. // check if reverse sorted
  179. if ((it2 != (rng_data.first + 1))) return false;
  180. sw = true;
  181. for (Iter1_t it1 = rng_data.first;
  182. it2 != rng_data.last and (sw = comp(*it2, *it1)); it1 =
  183. it2++)
  184. ;
  185. if (size_t(rng_data.last - it2) >= min_insert_partial_sort) return false;
  186. // reverse the elements between first and it1
  187. size_t nreverse = it2 - rng_data.first;
  188. Iter1_t alpha(rng_data.first), beta(it2 - 1), mid(
  189. rng_data.first + (nreverse >> 1));
  190. while (alpha != mid) {
  191. using std::swap;
  192. swap(*(alpha++), *(beta--));
  193. }
  194. // insert the elements between it1 and last
  195. if (it2 != rng_data.last)
  196. {
  197. sort_range_sort(range<Iter1_t>(it2, rng_data.last), rng_aux, comp);
  198. insert_partial_sort(rng_data.first, it2, rng_data.last, comp, rng_aux);
  199. };
  200. return true;
  201. }
  202. ;
  203. //-----------------------------------------------------------------------------
  204. // function : range_sort
  205. /// @brief this function divide r_input in two parts, sort it,and merge moving
  206. /// the elements to range_buf
  207. /// @param range_input : range with the elements to sort
  208. /// @param range_buffer : range with the elements sorted
  209. /// @param comp : object for to compare two elements
  210. /// @param level : when is 1, sort with the insertionsort algorithm
  211. /// if not make a recursive call splitting the ranges
  212. //
  213. /// @comments : if the number of levels is odd, the data are in the first
  214. /// parameter of range_sort, and the results appear in the second parameter
  215. /// If the number of levels is even, the data are in the second
  216. /// parameter of range_sort, and the results are in the same parameter
  217. /// The two ranges must have the same size
  218. //-----------------------------------------------------------------------------
  219. template<class Iter1_t, class Iter2_t, class Compare>
  220. static void range_sort(const range<Iter1_t> &range1,
  221. const range<Iter2_t> &range2, Compare comp,
  222. uint32_t level)
  223. {
  224. //-----------------------------------------------------------------------
  225. // metaprogram
  226. //-----------------------------------------------------------------------
  227. typedef value_iter<Iter1_t> value_t;
  228. typedef value_iter<Iter2_t> value2_t;
  229. static_assert (std::is_same<value_t, value2_t>::value,
  230. "Incompatible iterators\n");
  231. //-----------------------------------------------------------------------
  232. // program
  233. //-----------------------------------------------------------------------
  234. typedef range<Iter1_t> range_it1;
  235. typedef range<Iter2_t> range_it2;
  236. assert(range1.size() == range2.size() and level != 0);
  237. //------------------- check if sort --------------------------------------
  238. if (range1.size() > 1024)
  239. {
  240. if ((level & 1) == 0)
  241. {
  242. if (check_stable_sort(range2, range1, comp)) return;
  243. }
  244. else
  245. {
  246. if (check_stable_sort(range1, range2, comp))
  247. {
  248. move_forward(range2, range1);
  249. return;
  250. };
  251. };
  252. };
  253. //------------------- normal process -----------------------------------
  254. size_t nelem1 = (range1.size() + 1) >> 1;
  255. range_it1 range_input1(range1.first, range1.first + nelem1),
  256. range_input2(range1.first + nelem1, range1.last);
  257. if (level < 2)
  258. {
  259. insert_sort(range_input1.first, range_input1.last, comp);
  260. insert_sort(range_input2.first, range_input2.last, comp);
  261. }
  262. else
  263. {
  264. range_sort (range_it2(range2.first, range2.first + nelem1),
  265. range_input1, comp, level - 1);
  266. range_sort (range_it2(range2.first + nelem1, range2.last),
  267. range_input2, comp, level - 1);
  268. };
  269. merge(range2, range_input1, range_input2, comp);
  270. }
  271. ;
  272. //-----------------------------------------------------------------------------
  273. // function : sort_range_sort
  274. /// @brief this sort elements using the range_sort function and receiving a
  275. /// buffer of initialized memory
  276. /// @param rng_data : range with the elements to sort
  277. /// @param rng_aux : range of at least the same memory than rng_data used as
  278. /// auxiliary memory in the sorting
  279. /// @param comp : object for to compare two elements
  280. //-----------------------------------------------------------------------------
  281. template<class Iter1_t, class Iter2_t, class Compare>
  282. static void sort_range_sort(const range<Iter1_t> &rng_data,
  283. const range<Iter2_t> &rng_aux, Compare comp)
  284. {
  285. //-----------------------------------------------------------------------
  286. // metaprogram
  287. //-----------------------------------------------------------------------
  288. typedef value_iter<Iter1_t> value_t;
  289. typedef value_iter<Iter2_t> value2_t;
  290. static_assert (std::is_same<value_t, value2_t>::value,
  291. "Incompatible iterators\n");
  292. //------------------------------------------------------------------------
  293. // program
  294. //------------------------------------------------------------------------
  295. // minimal number of element before to jump to insertionsort
  296. static const uint32_t sort_min = 32;
  297. if (rng_data.size() <= sort_min)
  298. {
  299. insert_sort(rng_data.first, rng_data.last, comp);
  300. return;
  301. };
  302. #ifdef __BS_DEBUG
  303. assert (rng_aux.size () >= rng_data.size ());
  304. #endif
  305. range<Iter2_t> rng_buffer(rng_aux.first, rng_aux.first + rng_data.size());
  306. uint32_t nlevel =
  307. nbits64(((rng_data.size() + sort_min - 1) / sort_min) - 1);
  308. //assert (nlevel != 0);
  309. if ((nlevel & 1) == 0)
  310. {
  311. range_sort(rng_buffer, rng_data, comp, nlevel);
  312. }
  313. else
  314. {
  315. range_sort(rng_data, rng_buffer, comp, nlevel);
  316. move_forward(rng_data, rng_buffer);
  317. };
  318. }
  319. ;
  320. //
  321. //############################################################################
  322. // ##
  323. // S T R U C T ##
  324. // ##
  325. // S P I N _ S O R T ##
  326. // ##
  327. //############################################################################
  328. //---------------------------------------------------------------------------
  329. /// @struct spin_sort
  330. /// @brief This class implement s stable sort algorithm with 1 thread, with
  331. /// an auxiliary memory of N/2 elements
  332. //----------------------------------------------------------------------------
  333. template<class Iter_t, typename Compare = compare_iter<Iter_t>>
  334. class spinsort
  335. {
  336. //------------------------------------------------------------------------
  337. // DEFINITIONS AND CONSTANTS
  338. //------------------------------------------------------------------------
  339. typedef value_iter<Iter_t> value_t;
  340. typedef range<Iter_t> range_it;
  341. typedef range<value_t *> range_buf;
  342. // When the number of elements to sort is smaller than Sort_min, are sorted
  343. // by the insertion sort algorithm
  344. static const uint32_t Sort_min = 36;
  345. //------------------------------------------------------------------------
  346. // VARIABLES
  347. //------------------------------------------------------------------------
  348. // Pointer to the auxiliary memory
  349. value_t *ptr;
  350. // Number of elements in the auxiliary memory
  351. size_t nptr;
  352. // construct indicate if the auxiliary memory in initialized, and owner
  353. // indicate if the auxiliary memory had been created inside the object or
  354. // had
  355. // been received as a parameter
  356. bool construct = false, owner = false;
  357. //------------------------------------------------------------------------
  358. // PRIVATE FUNCTIONS
  359. //-------------------------------------------------------------------------
  360. spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux,
  361. size_t naux);
  362. public:
  363. //------------------------------------------------------------------------
  364. // PUBLIC FUNCTIONS
  365. //-------------------------------------------------------------------------
  366. spinsort(Iter_t first, Iter_t last, Compare comp = Compare())
  367. : spinsort(first, last, comp, nullptr, 0) { };
  368. spinsort(Iter_t first, Iter_t last, Compare comp, range_buf range_aux)
  369. : spinsort(first, last, comp, range_aux.first, range_aux.size()) { };
  370. //
  371. //-----------------------------------------------------------------------
  372. // function :~spinsort
  373. /// @brief destructor of the struct. Destroy the elements if construct is
  374. /// true,
  375. /// and return the memory if owner is true
  376. //-----------------------------------------------------------------------
  377. ~spinsort(void)
  378. {
  379. if (construct)
  380. {
  381. destroy(range<value_t *>(ptr, ptr + nptr));
  382. construct = false;
  383. };
  384. if (owner and ptr != nullptr) std::free (ptr);
  385. };
  386. };
  387. //----------------------------------------------------------------------------
  388. // End of class spinsort
  389. //----------------------------------------------------------------------------
  390. //
  391. //-------------------------------------------------------------------------
  392. // function : spinsort
  393. /// @brief constructor of the struct
  394. //
  395. /// @param first : iterator to the first element of the range to sort
  396. /// @param last : iterator after the last element to the range to sort
  397. /// @param comp : object for to compare two elements pointed by Iter_t
  398. /// iterators
  399. /// @param paux : pointer to the auxiliary memory provided. If nullptr, the
  400. /// memory is created inside the class
  401. /// @param naux : number of elements pointed by paux
  402. //------------------------------------------------------------------------
  403. template <class Iter_t, typename Compare>
  404. spinsort <Iter_t, Compare>
  405. ::spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux, size_t naux)
  406. : ptr(paux), nptr(naux), construct(false), owner(false)
  407. {
  408. range<Iter_t> range_input(first, last);
  409. assert(range_input.valid());
  410. size_t nelem = range_input.size();
  411. owner = construct = false;
  412. nptr = (nelem + 1) >> 1;
  413. size_t nelem_1 = nptr;
  414. size_t nelem_2 = nelem - nelem_1;
  415. if (nelem <= (Sort_min << 1))
  416. {
  417. insert_sort(range_input.first, range_input.last, comp);
  418. return;
  419. };
  420. //------------------- check if sort ---------------------------------
  421. bool sw = true;
  422. for (Iter_t it1 = first, it2 = first + 1; it2 != last
  423. and (sw = not comp(*it2, *it1)); it1 = it2++) ;
  424. if (sw) return;
  425. //------------------- check if reverse sort -------------------------
  426. sw = true;
  427. for (Iter_t it1 = first, it2 = first + 1;
  428. it2 != last and (sw = comp(*it2, *it1)); it1 = it2++);
  429. if (sw)
  430. {
  431. using std::swap;
  432. size_t nelem2 = nelem >> 1;
  433. Iter_t it1 = first, it2 = last - 1;
  434. for (size_t i = 0; i < nelem2; ++i)
  435. swap(*(it1++), *(it2--));
  436. return;
  437. };
  438. if (ptr == nullptr)
  439. {
  440. ptr = reinterpret_cast <value_t*>
  441. (std::malloc (nptr * sizeof(value_t)));
  442. if (ptr == nullptr) throw std::bad_alloc();
  443. owner = true;
  444. };
  445. range_buf range_aux(ptr, (ptr + nptr));
  446. //---------------------------------------------------------------------
  447. // Process
  448. //---------------------------------------------------------------------
  449. uint32_t nlevel = nbits64(((nelem + Sort_min - 1) / Sort_min) - 1) - 1;
  450. assert(nlevel != 0);
  451. if ((nlevel & 1) == 1)
  452. {
  453. //----------------------------------------------------------------
  454. // if the number of levels is odd, the data are in the first
  455. // parameter of range_sort, and the results appear in the second
  456. // parameter
  457. //----------------------------------------------------------------
  458. range_it range_1(first, first + nelem_2), range_2(first + nelem_2,
  459. last);
  460. range_aux = move_construct(range_aux, range_2);
  461. construct = true;
  462. range_sort(range_aux, range_2, comp, nlevel);
  463. range_buf rng_bx(range_aux.first, range_aux.first + nelem_2);
  464. range_sort(range_1, rng_bx, comp, nlevel);
  465. merge_half(range_input, rng_bx, range_2, comp);
  466. }
  467. else
  468. {
  469. //----------------------------------------------------------------
  470. // If the number of levels is even, the data are in the second
  471. // parameter of range_sort, and the results are in the same
  472. // parameter
  473. //----------------------------------------------------------------
  474. range_it range_1(first, first + nelem_1), range_2(first + nelem_1,
  475. last);
  476. range_aux = move_construct(range_aux, range_1);
  477. construct = true;
  478. range_sort(range_1, range_aux, comp, nlevel);
  479. range_1.last = range_1.first + range_2.size();
  480. range_sort(range_1, range_2, comp, nlevel);
  481. merge_half(range_input, range_aux, range_2, comp);
  482. };
  483. };
  484. //****************************************************************************
  485. };// End namepspace spin_detail
  486. //****************************************************************************
  487. //
  488. namespace bsc = boost::sort::common;
  489. //-----------------------------------------------------------------------------
  490. // function : spinsort
  491. /// @brief this function implement a single thread stable sort
  492. ///
  493. /// @param first : iterator to the first element of the range to sort
  494. /// @param last : iterator after the last element to the range to sort
  495. /// @param comp : object for to compare two elements pointed by Iter_t
  496. /// iterators
  497. //-----------------------------------------------------------------------------
  498. template <class Iter_t, class Compare = compare_iter<Iter_t>>
  499. inline void spinsort (Iter_t first, Iter_t last, Compare comp = Compare())
  500. {
  501. spin_detail::spinsort <Iter_t, Compare> (first, last, comp);
  502. };
  503. template <class Iter_t, class Compare = compare_iter<Iter_t>>
  504. inline void indirect_spinsort (Iter_t first, Iter_t last,
  505. Compare comp = Compare())
  506. {
  507. typedef typename std::vector<Iter_t>::iterator itx_iter;
  508. typedef common::less_ptr_no_null <Iter_t, Compare> itx_comp;
  509. common::indirect_sort (spinsort<itx_iter, itx_comp>, first, last, comp);
  510. };
  511. //****************************************************************************
  512. };// End namespace sort
  513. };// End namepspace boost
  514. //****************************************************************************
  515. //
  516. #endif