sort.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /* Copyright (C) 1999, 2000, 2001, 2002, 2004, 2006, 2007, 2008, 2009,
  2. * 2010, 2011, 2012 Free Software Foundation, Inc.
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public License
  6. * as published by the Free Software Foundation; either version 3 of
  7. * the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301 USA
  18. */
  19. /* Written in December 1998 by Roland Orre <orre@nada.kth.se>
  20. * This implements the same sort interface as slib/sort.scm
  21. * for lists and vectors where slib defines:
  22. * sorted?, merge, merge!, sort, sort!
  23. * For scsh compatibility sort-list and sort-list! are also defined.
  24. * In cases where a stable-sort is required use stable-sort or
  25. * stable-sort!. An additional feature is
  26. * (restricted-vector-sort! vector less? startpos endpos)
  27. * which allows you to sort part of a vector.
  28. * Thanks to Aubrey Jaffer for the slib/sort.scm library.
  29. * Thanks to Richard A. O'Keefe (based on Prolog code by D.H.D.Warren)
  30. * for the merge sort inspiration.
  31. * Thanks to Douglas C. Schmidt (schmidt@ics.uci.edu) for the
  32. * quicksort code.
  33. */
  34. #ifdef HAVE_CONFIG_H
  35. # include <config.h>
  36. #endif
  37. #include "libguile/_scm.h"
  38. #include "libguile/eval.h"
  39. #include "libguile/arrays.h"
  40. #include "libguile/array-map.h"
  41. #include "libguile/feature.h"
  42. #include "libguile/vectors.h"
  43. #include "libguile/async.h"
  44. #include "libguile/dynwind.h"
  45. #include "libguile/validate.h"
  46. #include "libguile/sort.h"
  47. /* We have two quicksort variants: one for contigous vectors and one
  48. for vectors with arbitrary increments between elements. Note that
  49. increments can be negative.
  50. */
  51. #define NAME quicksort1
  52. #define INC_PARAM /* empty */
  53. #define INC 1
  54. #include "libguile/quicksort.i.c"
  55. #define NAME quicksort
  56. #define INC_PARAM ssize_t inc,
  57. #define INC inc
  58. #include "libguile/quicksort.i.c"
  59. SCM_DEFINE (scm_restricted_vector_sort_x, "restricted-vector-sort!", 4, 0, 0,
  60. (SCM vec, SCM less, SCM startpos, SCM endpos),
  61. "Sort the vector @var{vec}, using @var{less} for comparing\n"
  62. "the vector elements. @var{startpos} (inclusively) and\n"
  63. "@var{endpos} (exclusively) delimit\n"
  64. "the range of the vector which gets sorted. The return value\n"
  65. "is not specified.")
  66. #define FUNC_NAME s_scm_restricted_vector_sort_x
  67. {
  68. size_t vlen, spos, len;
  69. ssize_t vinc;
  70. scm_t_array_handle handle;
  71. SCM *velts;
  72. velts = scm_vector_writable_elements (vec, &handle, &vlen, &vinc);
  73. spos = scm_to_unsigned_integer (startpos, 0, vlen);
  74. len = scm_to_unsigned_integer (endpos, spos, vlen) - spos;
  75. if (vinc == 1)
  76. quicksort1 (velts + spos*vinc, len, less);
  77. else
  78. quicksort (velts + spos*vinc, len, vinc, less);
  79. scm_array_handle_release (&handle);
  80. return SCM_UNSPECIFIED;
  81. }
  82. #undef FUNC_NAME
  83. /* (sorted? sequence less?)
  84. * is true when sequence is a list (x0 x1 ... xm) or a vector #(x0 ... xm)
  85. * such that for all 1 <= i <= m,
  86. * (not (less? (list-ref list i) (list-ref list (- i 1)))). */
  87. SCM_DEFINE (scm_sorted_p, "sorted?", 2, 0, 0,
  88. (SCM items, SCM less),
  89. "Return @code{#t} iff @var{items} is a list or vector such that, "
  90. "for each element @var{x} and the next element @var{y} of "
  91. "@var{items}, @code{(@var{less} @var{y} @var{x})} returns "
  92. "@code{#f}.")
  93. #define FUNC_NAME s_scm_sorted_p
  94. {
  95. long len, j; /* list/vector length, temp j */
  96. SCM item, rest; /* rest of items loop variable */
  97. if (SCM_NULL_OR_NIL_P (items))
  98. return SCM_BOOL_T;
  99. if (scm_is_pair (items))
  100. {
  101. len = scm_ilength (items); /* also checks that it's a pure list */
  102. SCM_ASSERT_RANGE (1, items, len >= 0);
  103. if (len <= 1)
  104. return SCM_BOOL_T;
  105. item = SCM_CAR (items);
  106. rest = SCM_CDR (items);
  107. j = len - 1;
  108. while (j > 0)
  109. {
  110. if (scm_is_true (scm_call_2 (less, SCM_CAR (rest), item)))
  111. return SCM_BOOL_F;
  112. else
  113. {
  114. item = SCM_CAR (rest);
  115. rest = SCM_CDR (rest);
  116. j--;
  117. }
  118. }
  119. return SCM_BOOL_T;
  120. }
  121. else
  122. {
  123. scm_t_array_handle handle;
  124. size_t i, len;
  125. ssize_t inc;
  126. const SCM *elts;
  127. elts = scm_vector_elements (items, &handle, &len, &inc);
  128. for (i = 1; i < len; i++, elts += inc)
  129. {
  130. if (scm_is_true (scm_call_2 (less, elts[inc], elts[0])))
  131. {
  132. scm_array_handle_release (&handle);
  133. return SCM_BOOL_F;
  134. }
  135. }
  136. scm_array_handle_release (&handle);
  137. return SCM_BOOL_T;
  138. }
  139. }
  140. #undef FUNC_NAME
  141. /* (merge a b less?)
  142. takes two lists a and b such that (sorted? a less?) and (sorted? b less?)
  143. and returns a new list in which the elements of a and b have been stably
  144. interleaved so that (sorted? (merge a b less?) less?).
  145. Note: this does _not_ accept vectors. */
  146. SCM_DEFINE (scm_merge, "merge", 3, 0, 0,
  147. (SCM alist, SCM blist, SCM less),
  148. "Merge two already sorted lists into one.\n"
  149. "Given two lists @var{alist} and @var{blist}, such that\n"
  150. "@code{(sorted? alist less?)} and @code{(sorted? blist less?)},\n"
  151. "return a new list in which the elements of @var{alist} and\n"
  152. "@var{blist} have been stably interleaved so that\n"
  153. "@code{(sorted? (merge alist blist less?) less?)}.\n"
  154. "Note: this does _not_ accept vectors.")
  155. #define FUNC_NAME s_scm_merge
  156. {
  157. SCM build;
  158. if (SCM_NULL_OR_NIL_P (alist))
  159. return blist;
  160. else if (SCM_NULL_OR_NIL_P (blist))
  161. return alist;
  162. else
  163. {
  164. long alen, blen; /* list lengths */
  165. SCM last;
  166. SCM_VALIDATE_NONEMPTYLIST_COPYLEN (1, alist, alen);
  167. SCM_VALIDATE_NONEMPTYLIST_COPYLEN (2, blist, blen);
  168. if (scm_is_true (scm_call_2 (less, SCM_CAR (blist), SCM_CAR (alist))))
  169. {
  170. build = scm_cons (SCM_CAR (blist), SCM_EOL);
  171. blist = SCM_CDR (blist);
  172. blen--;
  173. }
  174. else
  175. {
  176. build = scm_cons (SCM_CAR (alist), SCM_EOL);
  177. alist = SCM_CDR (alist);
  178. alen--;
  179. }
  180. last = build;
  181. while ((alen > 0) && (blen > 0))
  182. {
  183. SCM_TICK;
  184. if (scm_is_true (scm_call_2 (less, SCM_CAR (blist), SCM_CAR (alist))))
  185. {
  186. SCM_SETCDR (last, scm_cons (SCM_CAR (blist), SCM_EOL));
  187. blist = SCM_CDR (blist);
  188. blen--;
  189. }
  190. else
  191. {
  192. SCM_SETCDR (last, scm_cons (SCM_CAR (alist), SCM_EOL));
  193. alist = SCM_CDR (alist);
  194. alen--;
  195. }
  196. last = SCM_CDR (last);
  197. }
  198. if ((alen > 0) && (blen == 0))
  199. SCM_SETCDR (last, alist);
  200. else if ((alen == 0) && (blen > 0))
  201. SCM_SETCDR (last, blist);
  202. }
  203. return build;
  204. }
  205. #undef FUNC_NAME
  206. static SCM
  207. scm_merge_list_x (SCM alist, SCM blist,
  208. long alen, long blen,
  209. SCM less)
  210. {
  211. SCM build, last;
  212. if (SCM_NULL_OR_NIL_P (alist))
  213. return blist;
  214. else if (SCM_NULL_OR_NIL_P (blist))
  215. return alist;
  216. else
  217. {
  218. if (scm_is_true (scm_call_2 (less, SCM_CAR (blist), SCM_CAR (alist))))
  219. {
  220. build = blist;
  221. blist = SCM_CDR (blist);
  222. blen--;
  223. }
  224. else
  225. {
  226. build = alist;
  227. alist = SCM_CDR (alist);
  228. alen--;
  229. }
  230. last = build;
  231. while ((alen > 0) && (blen > 0))
  232. {
  233. SCM_TICK;
  234. if (scm_is_true (scm_call_2 (less, SCM_CAR (blist), SCM_CAR (alist))))
  235. {
  236. SCM_SETCDR (last, blist);
  237. blist = SCM_CDR (blist);
  238. blen--;
  239. }
  240. else
  241. {
  242. SCM_SETCDR (last, alist);
  243. alist = SCM_CDR (alist);
  244. alen--;
  245. }
  246. last = SCM_CDR (last);
  247. }
  248. if ((alen > 0) && (blen == 0))
  249. SCM_SETCDR (last, alist);
  250. else if ((alen == 0) && (blen > 0))
  251. SCM_SETCDR (last, blist);
  252. }
  253. return build;
  254. } /* scm_merge_list_x */
  255. SCM_DEFINE (scm_merge_x, "merge!", 3, 0, 0,
  256. (SCM alist, SCM blist, SCM less),
  257. "Takes two lists @var{alist} and @var{blist} such that\n"
  258. "@code{(sorted? alist less?)} and @code{(sorted? blist less?)} and\n"
  259. "returns a new list in which the elements of @var{alist} and\n"
  260. "@var{blist} have been stably interleaved so that\n"
  261. " @code{(sorted? (merge alist blist less?) less?)}.\n"
  262. "This is the destructive variant of @code{merge}\n"
  263. "Note: this does _not_ accept vectors.")
  264. #define FUNC_NAME s_scm_merge_x
  265. {
  266. if (SCM_NULL_OR_NIL_P (alist))
  267. return blist;
  268. else if (SCM_NULL_OR_NIL_P (blist))
  269. return alist;
  270. else
  271. {
  272. long alen, blen; /* list lengths */
  273. SCM_VALIDATE_NONEMPTYLIST_COPYLEN (1, alist, alen);
  274. SCM_VALIDATE_NONEMPTYLIST_COPYLEN (2, blist, blen);
  275. return scm_merge_list_x (alist, blist, alen, blen, less);
  276. }
  277. }
  278. #undef FUNC_NAME
  279. /* This merge sort algorithm is same as slib's by Richard A. O'Keefe.
  280. The algorithm is stable. We also tried to use the algorithm used by
  281. scsh's merge-sort but that algorithm showed to not be stable, even
  282. though it claimed to be.
  283. */
  284. static SCM
  285. scm_merge_list_step (SCM * seq, SCM less, long n)
  286. {
  287. SCM a, b;
  288. if (n > 2)
  289. {
  290. long mid = n / 2;
  291. SCM_TICK;
  292. a = scm_merge_list_step (seq, less, mid);
  293. b = scm_merge_list_step (seq, less, n - mid);
  294. return scm_merge_list_x (a, b, mid, n - mid, less);
  295. }
  296. else if (n == 2)
  297. {
  298. SCM p = *seq;
  299. SCM rest = SCM_CDR (*seq);
  300. SCM x = SCM_CAR (*seq);
  301. SCM y = SCM_CAR (SCM_CDR (*seq));
  302. *seq = SCM_CDR (rest);
  303. SCM_SETCDR (rest, SCM_EOL);
  304. if (scm_is_true (scm_call_2 (less, y, x)))
  305. {
  306. SCM_SETCAR (p, y);
  307. SCM_SETCAR (rest, x);
  308. }
  309. return p;
  310. }
  311. else if (n == 1)
  312. {
  313. SCM p = *seq;
  314. *seq = SCM_CDR (p);
  315. SCM_SETCDR (p, SCM_EOL);
  316. return p;
  317. }
  318. else
  319. return SCM_EOL;
  320. } /* scm_merge_list_step */
  321. SCM_DEFINE (scm_sort_x, "sort!", 2, 0, 0,
  322. (SCM items, SCM less),
  323. "Sort the sequence @var{items}, which may be a list or a\n"
  324. "vector. @var{less} is used for comparing the sequence\n"
  325. "elements. The sorting is destructive, that means that the\n"
  326. "input sequence is modified to produce the sorted result.\n"
  327. "This is not a stable sort.")
  328. #define FUNC_NAME s_scm_sort_x
  329. {
  330. long len; /* list/vector length */
  331. if (SCM_NULL_OR_NIL_P (items))
  332. return items;
  333. if (scm_is_pair (items))
  334. {
  335. SCM_VALIDATE_LIST_COPYLEN (1, items, len);
  336. return scm_merge_list_step (&items, less, len);
  337. }
  338. else if (scm_is_array (items))
  339. {
  340. scm_restricted_vector_sort_x (items,
  341. less,
  342. scm_from_int (0),
  343. scm_array_length (items));
  344. return items;
  345. }
  346. else
  347. SCM_WRONG_TYPE_ARG (1, items);
  348. }
  349. #undef FUNC_NAME
  350. SCM_DEFINE (scm_sort, "sort", 2, 0, 0,
  351. (SCM items, SCM less),
  352. "Sort the sequence @var{items}, which may be a list or a\n"
  353. "vector. @var{less} is used for comparing the sequence\n"
  354. "elements. This is not a stable sort.")
  355. #define FUNC_NAME s_scm_sort
  356. {
  357. if (SCM_NULL_OR_NIL_P (items))
  358. return items;
  359. if (scm_is_pair (items))
  360. return scm_sort_x (scm_list_copy (items), less);
  361. else if (scm_is_array (items) && 1 == scm_c_array_rank (items))
  362. return scm_sort_x (scm_vector_copy (items), less);
  363. else
  364. SCM_WRONG_TYPE_ARG (1, items);
  365. }
  366. #undef FUNC_NAME
  367. static void
  368. scm_merge_vector_x (SCM *vec,
  369. SCM *temp,
  370. SCM less,
  371. size_t low,
  372. size_t mid,
  373. size_t high,
  374. ssize_t inc)
  375. {
  376. size_t it; /* Index for temp vector */
  377. size_t i1 = low; /* Index for lower vector segment */
  378. size_t i2 = mid + 1; /* Index for upper vector segment */
  379. #define VEC(i) vec[(i)*inc]
  380. /* Copy while both segments contain more characters */
  381. for (it = low; (i1 <= mid) && (i2 <= high); ++it)
  382. {
  383. if (scm_is_true (scm_call_2 (less, VEC(i2), VEC(i1))))
  384. temp[it] = VEC(i2++);
  385. else
  386. temp[it] = VEC(i1++);
  387. }
  388. {
  389. /* Copy while first segment contains more characters */
  390. while (i1 <= mid)
  391. temp[it++] = VEC(i1++);
  392. /* Copy while second segment contains more characters */
  393. while (i2 <= high)
  394. temp[it++] = VEC(i2++);
  395. /* Copy back from temp to vp */
  396. for (it = low; it <= high; it++)
  397. VEC(it) = temp[it];
  398. }
  399. } /* scm_merge_vector_x */
  400. static void
  401. scm_merge_vector_step (SCM *vec,
  402. SCM *temp,
  403. SCM less,
  404. size_t low,
  405. size_t high,
  406. ssize_t inc)
  407. {
  408. if (high > low)
  409. {
  410. size_t mid = (low + high) / 2;
  411. SCM_TICK;
  412. scm_merge_vector_step (vec, temp, less, low, mid, inc);
  413. scm_merge_vector_step (vec, temp, less, mid+1, high, inc);
  414. scm_merge_vector_x (vec, temp, less, low, mid, high, inc);
  415. }
  416. } /* scm_merge_vector_step */
  417. SCM_DEFINE (scm_stable_sort_x, "stable-sort!", 2, 0, 0,
  418. (SCM items, SCM less),
  419. "Sort the sequence @var{items}, which may be a list or a\n"
  420. "vector. @var{less} is used for comparing the sequence elements.\n"
  421. "The sorting is destructive, that means that the input sequence\n"
  422. "is modified to produce the sorted result.\n"
  423. "This is a stable sort.")
  424. #define FUNC_NAME s_scm_stable_sort_x
  425. {
  426. long len; /* list/vector length */
  427. if (SCM_NULL_OR_NIL_P (items))
  428. return items;
  429. if (scm_is_pair (items))
  430. {
  431. SCM_VALIDATE_LIST_COPYLEN (1, items, len);
  432. return scm_merge_list_step (&items, less, len);
  433. }
  434. else
  435. {
  436. scm_t_array_handle temp_handle, vec_handle;
  437. SCM temp, *temp_elts, *vec_elts;
  438. size_t len;
  439. ssize_t inc;
  440. vec_elts = scm_vector_writable_elements (items, &vec_handle,
  441. &len, &inc);
  442. if (len == 0) {
  443. scm_array_handle_release (&vec_handle);
  444. return items;
  445. }
  446. temp = scm_c_make_vector (len, SCM_UNDEFINED);
  447. temp_elts = scm_vector_writable_elements (temp, &temp_handle,
  448. NULL, NULL);
  449. scm_merge_vector_step (vec_elts, temp_elts, less, 0, len-1, inc);
  450. scm_array_handle_release (&temp_handle);
  451. scm_array_handle_release (&vec_handle);
  452. return items;
  453. }
  454. }
  455. #undef FUNC_NAME
  456. SCM_DEFINE (scm_stable_sort, "stable-sort", 2, 0, 0,
  457. (SCM items, SCM less),
  458. "Sort the sequence @var{items}, which may be a list or a\n"
  459. "vector. @var{less} is used for comparing the sequence elements.\n"
  460. "This is a stable sort.")
  461. #define FUNC_NAME s_scm_stable_sort
  462. {
  463. if (SCM_NULL_OR_NIL_P (items))
  464. return SCM_EOL;
  465. if (scm_is_pair (items))
  466. return scm_stable_sort_x (scm_list_copy (items), less);
  467. else
  468. return scm_stable_sort_x (scm_vector_copy (items), less);
  469. }
  470. #undef FUNC_NAME
  471. SCM_DEFINE (scm_sort_list_x, "sort-list!", 2, 0, 0,
  472. (SCM items, SCM less),
  473. "Sort the list @var{items}, using @var{less} for comparing the\n"
  474. "list elements. The sorting is destructive, that means that the\n"
  475. "input list is modified to produce the sorted result.\n"
  476. "This is a stable sort.")
  477. #define FUNC_NAME s_scm_sort_list_x
  478. {
  479. long len;
  480. SCM_VALIDATE_LIST_COPYLEN (1, items, len);
  481. return scm_merge_list_step (&items, less, len);
  482. }
  483. #undef FUNC_NAME
  484. SCM_DEFINE (scm_sort_list, "sort-list", 2, 0, 0,
  485. (SCM items, SCM less),
  486. "Sort the list @var{items}, using @var{less} for comparing the\n"
  487. "list elements. This is a stable sort.")
  488. #define FUNC_NAME s_scm_sort_list
  489. {
  490. long len;
  491. SCM_VALIDATE_LIST_COPYLEN (1, items, len);
  492. items = scm_list_copy (items);
  493. return scm_merge_list_step (&items, less, len);
  494. }
  495. #undef FUNC_NAME
  496. void
  497. scm_init_sort ()
  498. {
  499. #include "libguile/sort.x"
  500. scm_add_feature ("sort");
  501. }
  502. /*
  503. Local Variables:
  504. c-file-style: "gnu"
  505. End:
  506. */