1
/* Copyright (C) 1999,2000,2001,2002, 2004, 2006 Free Software Foundation, Inc.
2
* This library is free software; you can redistribute it and/or
3
* modify it under the terms of the GNU Lesser General Public
4
* License as published by the Free Software Foundation; either
5
* version 2.1 of the License, or (at your option) any later version.
7
* This library is distributed in the hope that it will be useful,
8
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
* Lesser General Public License for more details.
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
14
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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
35
#include "libguile/_scm.h"
36
#include "libguile/eval.h"
37
#include "libguile/unif.h"
38
#include "libguile/ramap.h"
39
#include "libguile/feature.h"
40
#include "libguile/vectors.h"
41
#include "libguile/lang.h"
42
#include "libguile/async.h"
43
#include "libguile/dynwind.h"
45
#include "libguile/validate.h"
46
#include "libguile/sort.h"
48
/* We have two quicksort variants: one for contigous vectors and one
49
for vectors with arbitrary increments between elements. Note that
50
increments can be negative.
53
#define NAME quicksort1
54
#define INC_PARAM /* empty */
56
#include "libguile/quicksort.i.c"
58
#define NAME quicksort
59
#define INC_PARAM ssize_t inc,
61
#include "libguile/quicksort.i.c"
63
static scm_t_trampoline_2
64
compare_function (SCM less, unsigned int arg_nr, const char* fname)
66
const scm_t_trampoline_2 cmp = scm_trampoline_2 (less);
67
SCM_ASSERT_TYPE (cmp != NULL, less, arg_nr, fname, "less predicate");
72
SCM_DEFINE (scm_restricted_vector_sort_x, "restricted-vector-sort!", 4, 0, 0,
73
(SCM vec, SCM less, SCM startpos, SCM endpos),
74
"Sort the vector @var{vec}, using @var{less} for comparing\n"
75
"the vector elements. @var{startpos} (inclusively) and\n"
76
"@var{endpos} (exclusively) delimit\n"
77
"the range of the vector which gets sorted. The return value\n"
79
#define FUNC_NAME s_scm_restricted_vector_sort_x
81
const scm_t_trampoline_2 cmp = compare_function (less, 2, FUNC_NAME);
82
size_t vlen, spos, len;
84
scm_t_array_handle handle;
87
velts = scm_vector_writable_elements (vec, &handle, &vlen, &vinc);
88
spos = scm_to_unsigned_integer (startpos, 0, vlen);
89
len = scm_to_unsigned_integer (endpos, spos, vlen) - spos;
92
quicksort1 (velts + spos*vinc, len, cmp, less);
94
quicksort (velts + spos*vinc, len, vinc, cmp, less);
96
scm_array_handle_release (&handle);
98
return SCM_UNSPECIFIED;
103
/* (sorted? sequence less?)
104
* is true when sequence is a list (x0 x1 ... xm) or a vector #(x0 ... xm)
105
* such that for all 1 <= i <= m,
106
* (not (less? (list-ref list i) (list-ref list (- i 1)))). */
107
SCM_DEFINE (scm_sorted_p, "sorted?", 2, 0, 0,
108
(SCM items, SCM less),
109
"Return @code{#t} iff @var{items} is a list or a vector such that\n"
110
"for all 1 <= i <= m, the predicate @var{less} returns true when\n"
111
"applied to all elements i - 1 and i")
112
#define FUNC_NAME s_scm_sorted_p
114
const scm_t_trampoline_2 cmp = compare_function (less, 2, FUNC_NAME);
115
long len, j; /* list/vector length, temp j */
116
SCM item, rest; /* rest of items loop variable */
118
if (SCM_NULL_OR_NIL_P (items))
121
if (scm_is_pair (items))
123
len = scm_ilength (items); /* also checks that it's a pure list */
124
SCM_ASSERT_RANGE (1, items, len >= 0);
128
item = SCM_CAR (items);
129
rest = SCM_CDR (items);
133
if (scm_is_true ((*cmp) (less, SCM_CAR (rest), item)))
137
item = SCM_CAR (rest);
138
rest = SCM_CDR (rest);
146
scm_t_array_handle handle;
150
SCM result = SCM_BOOL_T;
152
elts = scm_vector_elements (items, &handle, &len, &inc);
154
for (i = 1; i < len; i++, elts += inc)
156
if (scm_is_true ((*cmp) (less, elts[inc], elts[0])))
163
scm_array_handle_release (&handle);
174
takes two lists a and b such that (sorted? a less?) and (sorted? b less?)
175
and returns a new list in which the elements of a and b have been stably
176
interleaved so that (sorted? (merge a b less?) less?).
177
Note: this does _not_ accept vectors. */
178
SCM_DEFINE (scm_merge, "merge", 3, 0, 0,
179
(SCM alist, SCM blist, SCM less),
180
"Merge two already sorted lists into one.\n"
181
"Given two lists @var{alist} and @var{blist}, such that\n"
182
"@code{(sorted? alist less?)} and @code{(sorted? blist less?)},\n"
183
"return a new list in which the elements of @var{alist} and\n"
184
"@var{blist} have been stably interleaved so that\n"
185
"@code{(sorted? (merge alist blist less?) less?)}.\n"
186
"Note: this does _not_ accept vectors.")
187
#define FUNC_NAME s_scm_merge
191
if (SCM_NULL_OR_NIL_P (alist))
193
else if (SCM_NULL_OR_NIL_P (blist))
197
const scm_t_trampoline_2 cmp = compare_function (less, 3, FUNC_NAME);
198
long alen, blen; /* list lengths */
201
SCM_VALIDATE_NONEMPTYLIST_COPYLEN (1, alist, alen);
202
SCM_VALIDATE_NONEMPTYLIST_COPYLEN (2, blist, blen);
203
if (scm_is_true ((*cmp) (less, SCM_CAR (blist), SCM_CAR (alist))))
205
build = scm_cons (SCM_CAR (blist), SCM_EOL);
206
blist = SCM_CDR (blist);
211
build = scm_cons (SCM_CAR (alist), SCM_EOL);
212
alist = SCM_CDR (alist);
216
while ((alen > 0) && (blen > 0))
219
if (scm_is_true ((*cmp) (less, SCM_CAR (blist), SCM_CAR (alist))))
221
SCM_SETCDR (last, scm_cons (SCM_CAR (blist), SCM_EOL));
222
blist = SCM_CDR (blist);
227
SCM_SETCDR (last, scm_cons (SCM_CAR (alist), SCM_EOL));
228
alist = SCM_CDR (alist);
231
last = SCM_CDR (last);
233
if ((alen > 0) && (blen == 0))
234
SCM_SETCDR (last, alist);
235
else if ((alen == 0) && (blen > 0))
236
SCM_SETCDR (last, blist);
244
scm_merge_list_x (SCM alist, SCM blist,
245
long alen, long blen,
246
scm_t_trampoline_2 cmp, SCM less)
250
if (SCM_NULL_OR_NIL_P (alist))
252
else if (SCM_NULL_OR_NIL_P (blist))
256
if (scm_is_true ((*cmp) (less, SCM_CAR (blist), SCM_CAR (alist))))
259
blist = SCM_CDR (blist);
265
alist = SCM_CDR (alist);
269
while ((alen > 0) && (blen > 0))
272
if (scm_is_true ((*cmp) (less, SCM_CAR (blist), SCM_CAR (alist))))
274
SCM_SETCDR (last, blist);
275
blist = SCM_CDR (blist);
280
SCM_SETCDR (last, alist);
281
alist = SCM_CDR (alist);
284
last = SCM_CDR (last);
286
if ((alen > 0) && (blen == 0))
287
SCM_SETCDR (last, alist);
288
else if ((alen == 0) && (blen > 0))
289
SCM_SETCDR (last, blist);
292
} /* scm_merge_list_x */
295
SCM_DEFINE (scm_merge_x, "merge!", 3, 0, 0,
296
(SCM alist, SCM blist, SCM less),
297
"Takes two lists @var{alist} and @var{blist} such that\n"
298
"@code{(sorted? alist less?)} and @code{(sorted? blist less?)} and\n"
299
"returns a new list in which the elements of @var{alist} and\n"
300
"@var{blist} have been stably interleaved so that\n"
301
" @code{(sorted? (merge alist blist less?) less?)}.\n"
302
"This is the destructive variant of @code{merge}\n"
303
"Note: this does _not_ accept vectors.")
304
#define FUNC_NAME s_scm_merge_x
306
if (SCM_NULL_OR_NIL_P (alist))
308
else if (SCM_NULL_OR_NIL_P (blist))
312
const scm_t_trampoline_2 cmp = compare_function (less, 3, FUNC_NAME);
313
long alen, blen; /* list lengths */
314
SCM_VALIDATE_NONEMPTYLIST_COPYLEN (1, alist, alen);
315
SCM_VALIDATE_NONEMPTYLIST_COPYLEN (2, blist, blen);
316
return scm_merge_list_x (alist, blist, alen, blen, cmp, less);
322
/* This merge sort algorithm is same as slib's by Richard A. O'Keefe.
323
The algorithm is stable. We also tried to use the algorithm used by
324
scsh's merge-sort but that algorithm showed to not be stable, even
325
though it claimed to be.
328
scm_merge_list_step (SCM * seq, scm_t_trampoline_2 cmp, SCM less, long n)
336
a = scm_merge_list_step (seq, cmp, less, mid);
337
b = scm_merge_list_step (seq, cmp, less, n - mid);
338
return scm_merge_list_x (a, b, mid, n - mid, cmp, less);
343
SCM rest = SCM_CDR (*seq);
344
SCM x = SCM_CAR (*seq);
345
SCM y = SCM_CAR (SCM_CDR (*seq));
346
*seq = SCM_CDR (rest);
347
SCM_SETCDR (rest, SCM_EOL);
348
if (scm_is_true ((*cmp) (less, y, x)))
351
SCM_SETCAR (rest, x);
359
SCM_SETCDR (p, SCM_EOL);
364
} /* scm_merge_list_step */
367
SCM_DEFINE (scm_sort_x, "sort!", 2, 0, 0,
368
(SCM items, SCM less),
369
"Sort the sequence @var{items}, which may be a list or a\n"
370
"vector. @var{less} is used for comparing the sequence\n"
371
"elements. The sorting is destructive, that means that the\n"
372
"input sequence is modified to produce the sorted result.\n"
373
"This is not a stable sort.")
374
#define FUNC_NAME s_scm_sort_x
376
long len; /* list/vector length */
377
if (SCM_NULL_OR_NIL_P (items))
380
if (scm_is_pair (items))
382
const scm_t_trampoline_2 cmp = compare_function (less, 2, FUNC_NAME);
383
SCM_VALIDATE_LIST_COPYLEN (1, items, len);
384
return scm_merge_list_step (&items, cmp, less, len);
386
else if (scm_is_vector (items))
388
scm_restricted_vector_sort_x (items,
391
scm_vector_length (items));
395
SCM_WRONG_TYPE_ARG (1, items);
400
SCM_DEFINE (scm_sort, "sort", 2, 0, 0,
401
(SCM items, SCM less),
402
"Sort the sequence @var{items}, which may be a list or a\n"
403
"vector. @var{less} is used for comparing the sequence\n"
404
"elements. This is not a stable sort.")
405
#define FUNC_NAME s_scm_sort
407
if (SCM_NULL_OR_NIL_P (items))
410
if (scm_is_pair (items))
411
return scm_sort_x (scm_list_copy (items), less);
412
else if (scm_is_vector (items))
413
return scm_sort_x (scm_vector_copy (items), less);
415
SCM_WRONG_TYPE_ARG (1, items);
421
scm_merge_vector_x (SCM *vec,
423
scm_t_trampoline_2 cmp,
430
size_t it; /* Index for temp vector */
431
size_t i1 = low; /* Index for lower vector segment */
432
size_t i2 = mid + 1; /* Index for upper vector segment */
434
#define VEC(i) vec[(i)*inc]
436
/* Copy while both segments contain more characters */
437
for (it = low; (i1 <= mid) && (i2 <= high); ++it)
439
if (scm_is_true ((*cmp) (less, VEC(i2), VEC(i1))))
440
temp[it] = VEC(i2++);
442
temp[it] = VEC(i1++);
446
/* Copy while first segment contains more characters */
448
temp[it++] = VEC(i1++);
450
/* Copy while second segment contains more characters */
452
temp[it++] = VEC(i2++);
454
/* Copy back from temp to vp */
455
for (it = low; it <= high; it++)
458
} /* scm_merge_vector_x */
462
scm_merge_vector_step (SCM *vec,
464
scm_t_trampoline_2 cmp,
472
size_t mid = (low + high) / 2;
474
scm_merge_vector_step (vec, temp, cmp, less, low, mid, inc);
475
scm_merge_vector_step (vec, temp, cmp, less, mid+1, high, inc);
476
scm_merge_vector_x (vec, temp, cmp, less, low, mid, high, inc);
478
} /* scm_merge_vector_step */
481
SCM_DEFINE (scm_stable_sort_x, "stable-sort!", 2, 0, 0,
482
(SCM items, SCM less),
483
"Sort the sequence @var{items}, which may be a list or a\n"
484
"vector. @var{less} is used for comparing the sequence elements.\n"
485
"The sorting is destructive, that means that the input sequence\n"
486
"is modified to produce the sorted result.\n"
487
"This is a stable sort.")
488
#define FUNC_NAME s_scm_stable_sort_x
490
const scm_t_trampoline_2 cmp = compare_function (less, 2, FUNC_NAME);
491
long len; /* list/vector length */
493
if (SCM_NULL_OR_NIL_P (items))
496
if (scm_is_pair (items))
498
SCM_VALIDATE_LIST_COPYLEN (1, items, len);
499
return scm_merge_list_step (&items, cmp, less, len);
501
else if (scm_is_vector (items))
503
scm_t_array_handle temp_handle, vec_handle;
504
SCM temp, *temp_elts, *vec_elts;
508
vec_elts = scm_vector_writable_elements (items, &vec_handle,
510
temp = scm_c_make_vector (len, SCM_UNDEFINED);
511
temp_elts = scm_vector_writable_elements (temp, &temp_handle,
514
scm_merge_vector_step (vec_elts, temp_elts, cmp, less, 0, len-1, inc);
516
scm_array_handle_release (&temp_handle);
517
scm_array_handle_release (&vec_handle);
522
SCM_WRONG_TYPE_ARG (1, items);
527
SCM_DEFINE (scm_stable_sort, "stable-sort", 2, 0, 0,
528
(SCM items, SCM less),
529
"Sort the sequence @var{items}, which may be a list or a\n"
530
"vector. @var{less} is used for comparing the sequence elements.\n"
531
"This is a stable sort.")
532
#define FUNC_NAME s_scm_stable_sort
534
if (scm_is_pair (items))
535
return scm_stable_sort_x (scm_list_copy (items), less);
536
else if (scm_is_vector (items))
537
return scm_stable_sort_x (scm_vector_copy (items), less);
539
SCM_WRONG_TYPE_ARG (1, items);
544
SCM_DEFINE (scm_sort_list_x, "sort-list!", 2, 0, 0,
545
(SCM items, SCM less),
546
"Sort the list @var{items}, using @var{less} for comparing the\n"
547
"list elements. The sorting is destructive, that means that the\n"
548
"input list is modified to produce the sorted result.\n"
549
"This is a stable sort.")
550
#define FUNC_NAME s_scm_sort_list_x
552
const scm_t_trampoline_2 cmp = compare_function (less, 2, FUNC_NAME);
555
SCM_VALIDATE_LIST_COPYLEN (1, items, len);
556
return scm_merge_list_step (&items, cmp, less, len);
561
SCM_DEFINE (scm_sort_list, "sort-list", 2, 0, 0,
562
(SCM items, SCM less),
563
"Sort the list @var{items}, using @var{less} for comparing the\n"
564
"list elements. This is a stable sort.")
565
#define FUNC_NAME s_scm_sort_list
567
const scm_t_trampoline_2 cmp = compare_function (less, 2, FUNC_NAME);
570
SCM_VALIDATE_LIST_COPYLEN (1, items, len);
571
items = scm_list_copy (items);
572
return scm_merge_list_step (&items, cmp, less, len);
580
#include "libguile/sort.x"
582
scm_add_feature ("sort");