spinsort.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. //----------------------------------------------------------------------------
  2. /// @file spinsort.hpp
  3. /// @brief Spin Sort algorithm
  4. ///
  5. /// @author Copyright (c) 2016 Francisco José Tapia (fjtapia@gmail.com )\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 <boost/sort/spinsort/util/indirect.hpp>
  16. #include <boost/sort/insert_sort/insert_sort.hpp>
  17. #include <boost/sort/common/util/traits.hpp>
  18. #include <boost/sort/common/util/algorithm.hpp>
  19. #include <boost/sort/common/range.hpp>
  20. #include <boost/sort/common/indirect.hpp>
  21. #include <cstdlib>
  22. #include <functional>
  23. #include <iterator>
  24. #include <memory>
  25. #include <type_traits>
  26. #include <vector>
  27. #include <cstddef>
  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. std::swap(*(alpha++), *(beta--));
  192. // insert the elements between it1 and last
  193. if (it2 != rng_data.last)
  194. {
  195. sort_range_sort(range<Iter1_t>(it2, rng_data.last), rng_aux, comp);
  196. insert_partial_sort(rng_data.first, it2, rng_data.last, comp, rng_aux);
  197. };
  198. return true;
  199. }
  200. ;
  201. //-----------------------------------------------------------------------------
  202. // function : range_sort
  203. /// @brief this function divide r_input in two parts, sort it,and merge moving
  204. /// the elements to range_buf
  205. /// @param range_input : range with the elements to sort
  206. /// @param range_buffer : range with the elements sorted
  207. /// @param comp : object for to compare two elements
  208. /// @param level : when is 1, sort with the insertionsort algorithm
  209. /// if not make a recursive call splitting the ranges
  210. //
  211. /// @comments : if the number of levels is odd, the data are in the first
  212. /// parameter of range_sort, and the results appear in the second parameter
  213. /// If the number of levels is even, the data are in the second
  214. /// parameter of range_sort, and the results are in the same parameter
  215. /// The two ranges must have the same size
  216. //-----------------------------------------------------------------------------
  217. template<class Iter1_t, class Iter2_t, class Compare>
  218. static void range_sort(const range<Iter1_t> &range1,
  219. const range<Iter2_t> &range2, Compare comp,
  220. uint32_t level)
  221. {
  222. //-----------------------------------------------------------------------
  223. // metaprogram
  224. //-----------------------------------------------------------------------
  225. typedef value_iter<Iter1_t> value_t;
  226. typedef value_iter<Iter2_t> value2_t;
  227. static_assert (std::is_same<value_t, value2_t>::value,
  228. "Incompatible iterators\n");
  229. //-----------------------------------------------------------------------
  230. // program
  231. //-----------------------------------------------------------------------
  232. typedef range<Iter1_t> range_it1;
  233. typedef range<Iter2_t> range_it2;
  234. assert(range1.size() == range2.size() and level != 0);
  235. //------------------- check if sort --------------------------------------
  236. if (range1.size() > 1024)
  237. {
  238. if ((level & 1) == 0)
  239. {
  240. if (check_stable_sort(range2, range1, comp)) return;
  241. }
  242. else
  243. {
  244. if (check_stable_sort(range1, range2, comp))
  245. {
  246. move_forward(range2, range1);
  247. return;
  248. };
  249. };
  250. };
  251. //------------------- normal process -----------------------------------
  252. size_t nelem1 = (range1.size() + 1) >> 1;
  253. range_it1 range_input1(range1.first, range1.first + nelem1),
  254. range_input2(range1.first + nelem1, range1.last);
  255. if (level < 2)
  256. {
  257. insert_sort(range_input1.first, range_input1.last, comp);
  258. insert_sort(range_input2.first, range_input2.last, comp);
  259. }
  260. else
  261. {
  262. range_sort (range_it2(range2.first, range2.first + nelem1),
  263. range_input1, comp, level - 1);
  264. range_sort (range_it2(range2.first + nelem1, range2.last),
  265. range_input2, comp, level - 1);
  266. };
  267. merge(range2, range_input1, range_input2, comp);
  268. }
  269. ;
  270. //-----------------------------------------------------------------------------
  271. // function : sort_range_sort
  272. /// @brief this sort elements using the range_sort function and receiving a
  273. /// buffer of initialized memory
  274. /// @param rng_data : range with the elements to sort
  275. /// @param rng_aux : range of at least the same memory than rng_data used as
  276. /// auxiliary memory in the sorting
  277. /// @param comp : object for to compare two elements
  278. //-----------------------------------------------------------------------------
  279. template<class Iter1_t, class Iter2_t, class Compare>
  280. static void sort_range_sort(const range<Iter1_t> &rng_data,
  281. const range<Iter2_t> &rng_aux, Compare comp)
  282. {
  283. //-----------------------------------------------------------------------
  284. // metaprogram
  285. //-----------------------------------------------------------------------
  286. typedef value_iter<Iter1_t> value_t;
  287. typedef value_iter<Iter2_t> value2_t;
  288. static_assert (std::is_same<value_t, value2_t>::value,
  289. "Incompatible iterators\n");
  290. //------------------------------------------------------------------------
  291. // program
  292. //------------------------------------------------------------------------
  293. // minimal number of element before to jump to insertionsort
  294. static const uint32_t sort_min = 32;
  295. if (rng_data.size() <= sort_min)
  296. {
  297. insert_sort(rng_data.first, rng_data.last, comp);
  298. return;
  299. };
  300. #ifdef __BS_DEBUG
  301. assert (rng_aux.size () >= rng_data.size ());
  302. #endif
  303. range<Iter2_t> rng_buffer(rng_aux.first, rng_aux.first + rng_data.size());
  304. uint32_t nlevel =
  305. nbits64(((rng_data.size() + sort_min - 1) / sort_min) - 1);
  306. //assert (nlevel != 0);
  307. if ((nlevel & 1) == 0)
  308. {
  309. range_sort(rng_buffer, rng_data, comp, nlevel);
  310. }
  311. else
  312. {
  313. range_sort(rng_data, rng_buffer, comp, nlevel);
  314. move_forward(rng_data, rng_buffer);
  315. };
  316. }
  317. ;
  318. //
  319. //############################################################################
  320. // ##
  321. // S T R U C T ##
  322. // ##
  323. // S P I N _ S O R T ##
  324. // ##
  325. //############################################################################
  326. //---------------------------------------------------------------------------
  327. /// @struct spin_sort
  328. /// @brief This class implement s stable sort algorithm with 1 thread, with
  329. /// an auxiliary memory of N/2 elements
  330. //----------------------------------------------------------------------------
  331. template<class Iter_t, typename Compare = compare_iter<Iter_t>>
  332. class spinsort
  333. {
  334. //------------------------------------------------------------------------
  335. // DEFINITIONS AND CONSTANTS
  336. //------------------------------------------------------------------------
  337. typedef value_iter<Iter_t> value_t;
  338. typedef range<Iter_t> range_it;
  339. typedef range<value_t *> range_buf;
  340. // When the number of elements to sort is smaller than Sort_min, are sorted
  341. // by the insertion sort algorithm
  342. static const uint32_t Sort_min = 36;
  343. //------------------------------------------------------------------------
  344. // VARIABLES
  345. //------------------------------------------------------------------------
  346. // Pointer to the auxiliary memory
  347. value_t *ptr;
  348. // Number of elements in the auxiliary memory
  349. size_t nptr;
  350. // construct indicate if the auxiliary memory in initialized, and owner
  351. // indicate if the auxiliary memory had been created inside the object or
  352. // had
  353. // been received as a parameter
  354. bool construct = false, owner = false;
  355. //------------------------------------------------------------------------
  356. // PRIVATE FUNCTIONS
  357. //-------------------------------------------------------------------------
  358. spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux,
  359. size_t naux);
  360. public:
  361. //------------------------------------------------------------------------
  362. // PUBLIC FUNCTIONS
  363. //-------------------------------------------------------------------------
  364. spinsort(Iter_t first, Iter_t last, Compare comp = Compare())
  365. : spinsort(first, last, comp, nullptr, 0) { };
  366. spinsort(Iter_t first, Iter_t last, Compare comp, range_buf range_aux)
  367. : spinsort(first, last, comp, range_aux.first, range_aux.size()) { };
  368. //
  369. //-----------------------------------------------------------------------
  370. // function :~spinsort
  371. /// @brief destructor of the struct. Destroy the elements if construct is
  372. /// true,
  373. /// and return the memory if owner is true
  374. //-----------------------------------------------------------------------
  375. ~spinsort(void)
  376. {
  377. if (construct)
  378. {
  379. destroy(range<value_t *>(ptr, ptr + nptr));
  380. construct = false;
  381. };
  382. if (owner and ptr != nullptr) std::return_temporary_buffer(ptr);
  383. };
  384. };
  385. //----------------------------------------------------------------------------
  386. // End of class spinsort
  387. //----------------------------------------------------------------------------
  388. //
  389. //-------------------------------------------------------------------------
  390. // function : spinsort
  391. /// @brief constructor of the struct
  392. //
  393. /// @param first : iterator to the first element of the range to sort
  394. /// @param last : iterator after the last element to the range to sort
  395. /// @param comp : object for to compare two elements pointed by Iter_t
  396. /// iterators
  397. /// @param paux : pointer to the auxiliary memory provided. If nullptr, the
  398. /// memory is created inside the class
  399. /// @param naux : number of elements pointed by paux
  400. //------------------------------------------------------------------------
  401. template <class Iter_t, typename Compare>
  402. spinsort <Iter_t, Compare>
  403. ::spinsort (Iter_t first, Iter_t last, Compare comp, value_t *paux, size_t naux)
  404. : ptr(paux), nptr(naux), construct(false), owner(false)
  405. {
  406. range<Iter_t> range_input(first, last);
  407. assert(range_input.valid());
  408. size_t nelem = range_input.size();
  409. owner = construct = false;
  410. nptr = (nelem + 1) >> 1;
  411. size_t nelem_1 = nptr;
  412. size_t nelem_2 = nelem - nelem_1;
  413. if (nelem <= (Sort_min << 1))
  414. {
  415. insert_sort(range_input.first, range_input.last, comp);
  416. return;
  417. };
  418. //------------------- check if sort ---------------------------------
  419. bool sw = true;
  420. for (Iter_t it1 = first, it2 = first + 1; it2 != last
  421. and (sw = not comp(*it2, *it1)); it1 = it2++) ;
  422. if (sw) return;
  423. //------------------- check if reverse sort -------------------------
  424. sw = true;
  425. for (Iter_t it1 = first, it2 = first + 1;
  426. it2 != last and (sw = comp(*it2, *it1)); it1 = it2++);
  427. if (sw)
  428. {
  429. size_t nelem2 = nelem >> 1;
  430. Iter_t it1 = first, it2 = last - 1;
  431. for (size_t i = 0; i < nelem2; ++i)
  432. std::swap(*(it1++), *(it2--));
  433. return;
  434. };
  435. if (ptr == nullptr)
  436. {
  437. ptr = std::get_temporary_buffer<value_t>(nptr).first;
  438. if (ptr == nullptr) throw std::bad_alloc();
  439. owner = true;
  440. };
  441. range_buf range_aux(ptr, (ptr + nptr));
  442. //---------------------------------------------------------------------
  443. // Process
  444. //---------------------------------------------------------------------
  445. uint32_t nlevel = nbits64(((nelem + Sort_min - 1) / Sort_min) - 1) - 1;
  446. assert(nlevel != 0);
  447. if ((nlevel & 1) == 1)
  448. {
  449. //----------------------------------------------------------------
  450. // if the number of levels is odd, the data are in the first
  451. // parameter of range_sort, and the results appear in the second
  452. // parameter
  453. //----------------------------------------------------------------
  454. range_it range_1(first, first + nelem_2), range_2(first + nelem_2,
  455. last);
  456. range_aux = move_construct(range_aux, range_2);
  457. construct = true;
  458. range_sort(range_aux, range_2, comp, nlevel);
  459. range_buf rng_bx(range_aux.first, range_aux.first + nelem_2);
  460. range_sort(range_1, rng_bx, comp, nlevel);
  461. merge_half(range_input, rng_bx, range_2, comp);
  462. }
  463. else
  464. {
  465. //----------------------------------------------------------------
  466. // If the number of levels is even, the data are in the second
  467. // parameter of range_sort, and the results are in the same
  468. // parameter
  469. //----------------------------------------------------------------
  470. range_it range_1(first, first + nelem_1), range_2(first + nelem_1,
  471. last);
  472. range_aux = move_construct(range_aux, range_1);
  473. construct = true;
  474. range_sort(range_1, range_aux, comp, nlevel);
  475. range_1.last = range_1.first + range_2.size();
  476. range_sort(range_1, range_2, comp, nlevel);
  477. merge_half(range_input, range_aux, range_2, comp);
  478. };
  479. };
  480. //****************************************************************************
  481. };// End namepspace spin_detail
  482. //****************************************************************************
  483. //
  484. namespace bsc = boost::sort::common;
  485. //-----------------------------------------------------------------------------
  486. // function : spinsort
  487. /// @brief this function implement a single thread stable sort
  488. ///
  489. /// @param first : iterator to the first element of the range to sort
  490. /// @param last : iterator after the last element to the range to sort
  491. /// @param comp : object for to compare two elements pointed by Iter_t
  492. /// iterators
  493. //-----------------------------------------------------------------------------
  494. template <class Iter_t, class Compare = compare_iter<Iter_t>>
  495. inline void spinsort (Iter_t first, Iter_t last, Compare comp = Compare())
  496. {
  497. spin_detail::spinsort <Iter_t, Compare> (first, last, comp);
  498. };
  499. template <class Iter_t, class Compare = compare_iter<Iter_t>>
  500. inline void indirect_spinsort (Iter_t first, Iter_t last,
  501. Compare comp = Compare())
  502. {
  503. typedef typename std::vector<Iter_t>::iterator itx_iter;
  504. typedef common::less_ptr_no_null <Iter_t, Compare> itx_comp;
  505. common::indirect_sort (spinsort<itx_iter, itx_comp>, first, last, comp);
  506. };
  507. //****************************************************************************
  508. };// End namespace sort
  509. };// End namepspace boost
  510. //****************************************************************************
  511. //
  512. #endif