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