2
0

smoothsort.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /*
  2. * This file is part of the Sofia-SIP package
  3. *
  4. * Copyright (C) 2005 Nokia Corporation.
  5. *
  6. * Contact: Pekka Pessi <pekka.pessi@nokia.com>
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public License
  10. * as published by the Free Software Foundation; either version 2.1 of
  11. * the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  21. * 02110-1301 USA
  22. *
  23. */
  24. /**@file smoothsort.c
  25. * @brief Smoothsort implementation
  26. *
  27. * Smoothsort is a in-place sorting algorithm with performance of O(NlogN)
  28. * in worst case and O(n) in best case.
  29. *
  30. * @sa <a href="http://www.enterag.ch/hartwig/order/smoothsort.pdf">
  31. * "Smoothsort, an alternative for sorting in-situ", E.D. Dijkstra, EWD796a</a>,
  32. * &lt;http://www.enterag.ch/hartwig/order/smoothsort.pdf&gt;.
  33. *
  34. * @author Pekka Pessi <Pekka.Pessi@nokia.com>
  35. */
  36. #include <stdlib.h>
  37. #include <sofia-sip/su_config.h>
  38. #include "config.h"
  39. #include <sofia-sip/heap.h>
  40. #include <assert.h>
  41. #include <stdio.h>
  42. #include <sys/types.h>
  43. /** Description of current stretch */
  44. typedef struct {
  45. size_t b, c; /**< Leonardo numbers */
  46. unsigned longlong p; /**< Concatenation codification */
  47. } stretch;
  48. /** Description of array */
  49. typedef struct
  50. {
  51. void *m;
  52. int (*less)(void *m, size_t a, size_t b);
  53. void (*swap)(void *m, size_t a, size_t b);
  54. } array;
  55. static inline size_t stretch_up(stretch s[1])
  56. {
  57. size_t next;
  58. s->p >>= 1;
  59. next = s->b + s->c + 1, s->c = s->b, s->b = next;
  60. return next;
  61. }
  62. static inline size_t stretch_down(stretch s[1], unsigned bit)
  63. {
  64. size_t next;
  65. s->p <<= 1, s->p |= bit;
  66. next = s->c, s->c = s->b - s->c - 1, s->b = next;
  67. return next;
  68. }
  69. #if DEBUG_SMOOTHSORT
  70. static char const *binary(unsigned long long p)
  71. {
  72. static char binary[65];
  73. int i;
  74. if (p == 0)
  75. return "0";
  76. binary[64] = 0;
  77. for (i = 64; p; p >>= 1)
  78. binary[--i] = "01"[p & 1];
  79. return binary + i;
  80. }
  81. #else
  82. #define DEBUG(x) ((void)0)
  83. #endif
  84. /**
  85. * Sift the root of the stretch.
  86. *
  87. * The low values are sifted up (towards index 0) from root.
  88. *
  89. * @param array description of array to sort
  90. * @param r root of the stretch
  91. * @param s description of current stretch
  92. */
  93. static void sift(array const *array, size_t r, stretch s)
  94. {
  95. while (s.b >= 3) {
  96. size_t r2 = r - s.b + s.c;
  97. if (!array->less(array->m, r - 1, r2)) {
  98. r2 = r - 1;
  99. stretch_down(&s, 0);
  100. }
  101. if (array->less(array->m, r2, r))
  102. break;
  103. DEBUG(("\tswap(%p @%zu <=> @%zu)\n", array, r, r2));
  104. array->swap(array->m, r, r2); r = r2;
  105. stretch_down(&s, 0);
  106. }
  107. }
  108. /** Trinkle the roots of the given stretches
  109. *
  110. * @param array description of array to sort
  111. * @param r root of the stretch
  112. * @param s description of stretches to concatenate
  113. */
  114. static void trinkle(array const *array, size_t r, stretch s)
  115. {
  116. DEBUG(("trinkle(%p, %zu, (%u, %s))\n", array, r, s.b, binary(s.p)));
  117. while (s.p != 0) {
  118. size_t r2, r3;
  119. while ((s.p & 1) == 0)
  120. stretch_up(&s);
  121. if (s.p == 1)
  122. break;
  123. r3 = r - s.b;
  124. if (array->less(array->m, r3, r))
  125. break;
  126. s.p--;
  127. if (s.b < 3) {
  128. DEBUG(("\tswap(%p @%zu <=> @%zu b=%u)\n", array, r, r3, s.b));
  129. array->swap(array->m, r, r3); r = r3;
  130. continue;
  131. }
  132. r2 = r - s.b + s.c;
  133. if (array->less(array->m, r2, r - 1)) {
  134. r2 = r - 1;
  135. stretch_down(&s, 0);
  136. }
  137. if (array->less(array->m, r2, r3)) {
  138. DEBUG(("swap(%p [%zu]=[%zu])\n", array, r, r3));
  139. array->swap(array->m, r, r3); r = r3;
  140. continue;
  141. }
  142. DEBUG(("\tswap(%p @%zu <=> @%zu b=%u)\n", array, r, r2, s.b));
  143. array->swap(array->m, r, r2); r = r2;
  144. stretch_down(&s, 0);
  145. break;
  146. }
  147. sift(array, r, s);
  148. }
  149. /** Trinkles the stretches when the adjacent stretches are already trusty.
  150. *
  151. * @param array description of array to sort
  152. * @param r root of the stretch
  153. * @param stretch description of stretches to trinkle
  154. */
  155. static void semitrinkle(array const *array, size_t r, stretch s)
  156. {
  157. size_t r1 = r - s.c;
  158. DEBUG(("semitrinkle(%p, %zu, (%u, %s))\n", array, r, s.b, binary(s.p)));
  159. if (array->less(array->m, r, r1)) {
  160. DEBUG(("\tswap(%p @%zu <=> @%zu b=%u)\n", array, r, r1, s.b));
  161. array->swap(array->m, r, r1);
  162. trinkle(array, r1, s);
  163. }
  164. }
  165. /** Sort array using smoothsort.
  166. *
  167. * Sort @a N elements from array @a base starting with index @a r with smoothsort.
  168. *
  169. * @param base pointer to array
  170. * @param r lowest index to sort
  171. * @param N number of elements to sort
  172. * @param less comparison function returning nonzero if m[a] < m[b]
  173. * @param swap swapper function exchanging elements m[a] and m[b]
  174. */
  175. void su_smoothsort(void *base, size_t r, size_t N,
  176. int (*less)(void *m, size_t a, size_t b),
  177. void (*swap)(void *m, size_t a, size_t b))
  178. {
  179. stretch s = { 1, 1, 1 };
  180. size_t q;
  181. array array_i;
  182. array* const array = &array_i;
  183. array->less = less;
  184. array->swap = swap;
  185. array->m = base;
  186. assert(less && swap);
  187. if (base == NULL || N <= 1 || less == NULL || swap == NULL)
  188. return;
  189. DEBUG(("\nsmoothsort(%p, %zu)\n", array, nmemb));
  190. for (q = 1; q != N; q++, r++, s.p++) {
  191. DEBUG(("loop0 q=%zu, b=%u, p=%s \n", q, s.b, binary(s.p)));
  192. if ((s.p & 7) == 3) {
  193. sift(array, r, s), stretch_up(&s), stretch_up(&s);
  194. }
  195. else /* if ((s.p & 3) == 1) */ { assert((s.p & 3) == 1);
  196. if (q + s.c < N)
  197. sift(array, r, s);
  198. else
  199. trinkle(array, r, s);
  200. while (stretch_down(&s, 0) > 1)
  201. ;
  202. }
  203. }
  204. trinkle(array, r, s);
  205. for (; q > 1; q--) {
  206. s.p--;
  207. DEBUG(("loop1 q=%zu: b=%u p=%s\n", q, s.b, binary(s.p)));
  208. if (s.b <= 1) {
  209. while ((s.p & 1) == 0)
  210. stretch_up(&s);
  211. --r;
  212. }
  213. else /* if b >= 3 */ {
  214. if (s.p) semitrinkle(array, r - (s.b - s.c), s);
  215. stretch_down(&s, 1);
  216. semitrinkle(array, --r, s);
  217. stretch_down(&s, 1);
  218. }
  219. }
  220. }