* procs.h: Doc fix.
[bpt/guile.git] / libguile / sort.c
CommitLineData
54e09076
MD
1/* Copyright (C) 1999 Free Software Foundation, Inc.
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2, or (at your option)
5 * any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this software; see the file COPYING. If not, write to
14 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15 * Boston, MA 02111-1307 USA
16 *
17 * As a special exception, the Free Software Foundation gives permission
18 * for additional uses of the text contained in its release of GUILE.
19 *
20 * The exception is that, if you link the GUILE library with other files
21 * to produce an executable, this does not by itself cause the
22 * resulting executable to be covered by the GNU General Public License.
23 * Your use of that executable is in no way restricted on account of
24 * linking the GUILE library code into it.
25 *
26 * This exception does not however invalidate any other reasons why
27 * the executable file might be covered by the GNU General Public License.
28 *
29 * This exception applies only to the code released by the
30 * Free Software Foundation under the name GUILE. If you copy
31 * code from other Free Software Foundation releases into a copy of
32 * GUILE, as the General Public License permits, the exception does
33 * not apply to the code that you add in this way. To avoid misleading
34 * anyone as to the status of such modified files, you must delete
35 * this exception notice from them.
36 *
37 * If you write modifications of your own for GUILE, it is your choice
38 * whether to permit this exception to apply to your modifications.
39 * If you do not wish that, delete this exception notice. */
40
41/* Written in December 1998 by Roland Orre <orre@nada.kth.se>
42 * This implements the same sort interface as slib/sort.scm
43 * for lists and vectors where slib defines:
44 * sorted?, merge, merge!, sort, sort!
45 * For scsh compatibility are also sort-list and sort-list! defined.
46 * In cases where a stable-sort is required use stable-sort or
47 * stable-sort!. As additional feature is
48 * (restricted-vector-sort! vector less? startpos endpos)
49 * added, where startpos and endpos allows you to sort part of a vector.
50 * Thanks to Aubrey Jaffer for the slib/sort.scm library.
51 * Thanks to Richard A. O'Keefe (based on Prolog code by D.H.D.Warren)
52 * for the merge sort inspiration.
53 * Thanks to Douglas C. Schmidt (schmidt@ics.uci.edu) for the
54 * quicksort code.
55 */
56
57/* We need this to get the definitions for HAVE_ALLOCA_H, etc. */
58#include "scmconfig.h"
59
60/* AIX requires this to be the first thing in the file. The #pragma
61 directive is indented so pre-ANSI compilers will ignore it, rather
62 than choke on it. */
63#ifndef __GNUC__
64# if HAVE_ALLOCA_H
65# include <alloca.h>
66# else
67# ifdef _AIX
68 #pragma alloca
69# else
70# ifndef alloca /* predefined by HP cc +Olibcalls */
71char *alloca ();
72# endif
73# endif
74# endif
75#endif
76
77#include "_scm.h"
78
79#include "eval.h"
80#include "unif.h"
81#include "ramap.h"
82#include "alist.h"
a5cae3f3 83#include "feature.h"
54e09076
MD
84
85#include "sort.h"
86
87/* The routine quicksort was extracted from the GNU C Library qsort.c
88 written by Douglas C. Schmidt (schmidt@ics.uci.edu)
89 and adapted to guile by adding an extra pointer less
90 to quicksort by Roland Orre <orre@nada.kth.se>.
91
92 The reason to do this instead of using the library function qsort
93 was to avoid dependency of the ANSI-C extensions for local functions
94 and also to avoid obscure pool based solutions.
95 */
96
97/* Byte-wise swap two items of size SIZE. */
98#define SWAP(a, b, size) \
99 do \
100 { \
101 register size_t __size = (size); \
102 register char *__a = (a), *__b = (b); \
103 do \
104 { \
105 char __tmp = *__a; \
106 *__a++ = *__b; \
107 *__b++ = __tmp; \
108 } while (--__size > 0); \
109 } while (0)
110
111/* Discontinue quicksort algorithm when partition gets below this size.
112 This particular magic number was chosen to work best on a Sun 4/260. */
113#define MAX_THRESH 4
114
115/* Stack node declarations used to store unfulfilled partition obligations. */
116typedef struct
117 {
118 char *lo;
119 char *hi;
120 }
121stack_node;
122
123/* The next 4 #defines implement a very fast in-line stack abstraction. */
124#define STACK_SIZE (8 * sizeof(unsigned long int))
125#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
126#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
127#define STACK_NOT_EMPTY (stack < top)
128
129
130/* Order size using quicksort. This implementation incorporates
131 four optimizations discussed in Sedgewick:
132
133 1. Non-recursive, using an explicit stack of pointer that store the
134 next array partition to sort. To save time, this maximum amount
135 of space required to store an array of MAX_INT is allocated on the
136 stack. Assuming a 32-bit integer, this needs only 32 *
137 sizeof(stack_node) == 136 bits. Pretty cheap, actually.
138
139 2. Chose the pivot element using a median-of-three decision tree.
140 This reduces the probability of selecting a bad pivot value and
141 eliminates certain extraneous comparisons.
142
143 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
144 insertion sort to order the MAX_THRESH items within each partition.
145 This is a big win, since insertion sort is faster for small, mostly
146 sorted array segments.
147
148 4. The larger of the two sub-partitions is always pushed onto the
149 stack first, with the algorithm then concentrating on the
150 smaller partition. This *guarantees* no more than log (n)
151 stack size is needed (actually O(1) in this case)! */
152
153typedef int (*cmp_fun_t) (SCM less,
154 const void*,
155 const void*);
156
157static void
158quicksort (void *const pbase,
159 size_t total_elems,
160 size_t size,
161 cmp_fun_t cmp,
162 SCM less)
163{
164 register char *base_ptr = (char *) pbase;
165
166 /* Allocating SIZE bytes for a pivot buffer facilitates a better
167 algorithm below since we can do comparisons directly on the pivot. */
168 char *pivot_buffer = (char *) alloca (size);
169 const size_t max_thresh = MAX_THRESH * size;
170
171 if (total_elems == 0)
172 /* Avoid lossage with unsigned arithmetic below. */
173 return;
174
175 if (total_elems > MAX_THRESH)
176 {
177 char *lo = base_ptr;
178 char *hi = &lo[size * (total_elems - 1)];
179 /* Largest size needed for 32-bit int!!! */
180 stack_node stack[STACK_SIZE];
181 stack_node *top = stack + 1;
182
183 while (STACK_NOT_EMPTY)
184 {
185 char *left_ptr;
186 char *right_ptr;
187
188 char *pivot = pivot_buffer;
189
190 /* Select median value from among LO, MID, and HI. Rearrange
191 LO and HI so the three values are sorted. This lowers the
192 probability of picking a pathological pivot value and
193 skips a comparison for both the LEFT_PTR and RIGHT_PTR. */
194
195 char *mid = lo + size * ((hi - lo) / size >> 1);
196
197 if ((*cmp) (less, (void *) mid, (void *) lo))
198 SWAP (mid, lo, size);
199 if ((*cmp) (less, (void *) hi, (void *) mid))
200 SWAP (mid, hi, size);
201 else
202 goto jump_over;
203 if ((*cmp) (less, (void *) mid, (void *) lo))
204 SWAP (mid, lo, size);
205 jump_over:;
206 memcpy (pivot, mid, size);
207 pivot = pivot_buffer;
208
209 left_ptr = lo + size;
210 right_ptr = hi - size;
211
212 /* Here's the famous ``collapse the walls'' section of quicksort.
213 Gotta like those tight inner loops! They are the main reason
214 that this algorithm runs much faster than others. */
215 do
216 {
217 while ((*cmp) (less, (void *) left_ptr, (void *) pivot))
218 left_ptr += size;
219
220 while ((*cmp) (less, (void *) pivot, (void *) right_ptr))
221 right_ptr -= size;
222
223 if (left_ptr < right_ptr)
224 {
225 SWAP (left_ptr, right_ptr, size);
226 left_ptr += size;
227 right_ptr -= size;
228 }
229 else if (left_ptr == right_ptr)
230 {
231 left_ptr += size;
232 right_ptr -= size;
233 break;
234 }
235 }
236 while (left_ptr <= right_ptr);
237
238 /* Set up pointers for next iteration. First determine whether
239 left and right partitions are below the threshold size. If so,
240 ignore one or both. Otherwise, push the larger partition's
241 bounds on the stack and continue sorting the smaller one. */
242
243 if ((size_t) (right_ptr - lo) <= max_thresh)
244 {
245 if ((size_t) (hi - left_ptr) <= max_thresh)
246 /* Ignore both small partitions. */
247 POP (lo, hi);
248 else
249 /* Ignore small left partition. */
250 lo = left_ptr;
251 }
252 else if ((size_t) (hi - left_ptr) <= max_thresh)
253 /* Ignore small right partition. */
254 hi = right_ptr;
255 else if ((right_ptr - lo) > (hi - left_ptr))
256 {
257 /* Push larger left partition indices. */
258 PUSH (lo, right_ptr);
259 lo = left_ptr;
260 }
261 else
262 {
263 /* Push larger right partition indices. */
264 PUSH (left_ptr, hi);
265 hi = right_ptr;
266 }
267 }
268 }
269
270 /* Once the BASE_PTR array is partially sorted by quicksort the rest
271 is completely sorted using insertion sort, since this is efficient
272 for partitions below MAX_THRESH size. BASE_PTR points to the beginning
273 of the array to sort, and END_PTR points at the very last element in
274 the array (*not* one beyond it!). */
275
276 {
277 char *const end_ptr = &base_ptr[size * (total_elems - 1)];
278 char *tmp_ptr = base_ptr;
279 char *thresh = min (end_ptr, base_ptr + max_thresh);
280 register char *run_ptr;
281
282 /* Find smallest element in first threshold and place it at the
283 array's beginning. This is the smallest array element,
284 and the operation speeds up insertion sort's inner loop. */
285
286 for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
287 if ((*cmp) (less, (void *) run_ptr, (void *) tmp_ptr))
288 tmp_ptr = run_ptr;
289
290 if (tmp_ptr != base_ptr)
291 SWAP (tmp_ptr, base_ptr, size);
292
293 /* Insertion sort, running from left-hand-side up to right-hand-side. */
294
295 run_ptr = base_ptr + size;
296 while ((run_ptr += size) <= end_ptr)
297 {
298 tmp_ptr = run_ptr - size;
299 while ((*cmp) (less, (void *) run_ptr, (void *) tmp_ptr))
300 tmp_ptr -= size;
301
302 tmp_ptr += size;
303 if (tmp_ptr != run_ptr)
304 {
305 char *trav;
306
307 trav = run_ptr + size;
308 while (--trav >= run_ptr)
309 {
310 char c = *trav;
311 char *hi, *lo;
312
313 for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
314 *hi = *lo;
315 *hi = c;
316 }
317 }
318 }
319 }
320} /* quicksort */
321
322
323/* comparison routines */
324
325static int
326subr2less (SCM less, const void *a, const void *b)
327{
328 return SCM_NFALSEP (SCM_SUBRF (less) (*(SCM *) a, *(SCM *) b));
329} /* subr2less */
330
331static int
332subr2oless (SCM less, const void *a, const void *b)
333{
334 return SCM_NFALSEP (SCM_SUBRF (less) (*(SCM *) a,
335 *(SCM *) b,
336 SCM_UNDEFINED));
337} /* subr2oless */
338
339static int
340lsubrless (SCM less, const void *a, const void *b)
341{
342 return SCM_NFALSEP (SCM_SUBRF (less)
343 (scm_cons (*(SCM *) a,
344 scm_cons (*(SCM *) b, SCM_EOL))));
345} /* lsubrless */
346
347static int
348closureless (SCM code, const void *a, const void *b)
349{
350 SCM env, next;
351 env = SCM_EXTEND_ENV (SCM_CAR (SCM_CODE (code)),
352 scm_cons (*(SCM *) a,
353 scm_cons (*(SCM *) b, SCM_EOL)),
354 SCM_ENV (code));
355 /* Evaluate the closure body */
356 code = SCM_CDR (SCM_CODE (code));
357 next = code;
358 while (SCM_NNULLP (next = SCM_CDR (next)))
359 {
360 if (SCM_NIMP (SCM_CAR (code)))
361 SCM_XEVAL (SCM_CAR (code), env);
362 code = next;
363 }
364 return SCM_NFALSEP (SCM_XEVALCAR (code, env));
365} /* closureless */
366
367static int
368applyless (SCM less, const void *a, const void *b)
369{
370 return SCM_NFALSEP (scm_apply ((SCM) less,
371 scm_cons (*(SCM *) a,
372 scm_cons (*(SCM *) b, SCM_EOL)),
373 SCM_EOL));
374} /* applyless */
375
376static cmp_fun_t
377scm_cmp_function (SCM p)
378{
379 switch (SCM_TYP7 (p))
380 {
381 case scm_tc7_subr_2:
382 case scm_tc7_rpsubr:
383 case scm_tc7_asubr:
384 return subr2less;
385 case scm_tc7_subr_2o:
386 return subr2oless;
387 case scm_tc7_lsubr:
388 return lsubrless;
389 case scm_tcs_closures:
390 return closureless;
391 default:
392 return applyless;
393 }
394} /* scm_cmp_function */
395
396SCM_PROC (s_restricted_vector_sort_x, "restricted-vector-sort!", 4, 0, 0, scm_restricted_vector_sort_x);
397
398/* Question: Is there any need to make this a more general array sort?
399 It is probably enough to manage the vector type. */
400/* endpos equal as for substring, i.e. endpos is not included. */
401/* More natural wih length? */
402SCM
403scm_restricted_vector_sort_x (SCM vec, SCM less, SCM startpos, SCM endpos)
404{
405 size_t vlen, spos, len, size = sizeof (SCM);
406 SCM *vp;
407
408 SCM_ASSERT (SCM_NIMP (vec), vec, SCM_ARG1, s_restricted_vector_sort_x);
409 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_restricted_vector_sort_x);
410 switch (SCM_TYP7 (vec))
411 {
412 case scm_tc7_vector: /* the only type we manage is vector */
413 break;
414 case scm_tc7_ivect: /* long */
415 case scm_tc7_uvect: /* unsigned */
416 case scm_tc7_fvect: /* float */
417 case scm_tc7_dvect: /* double */
418 default:
419 scm_wta (vec, (char *) SCM_ARG1, s_restricted_vector_sort_x);
420 }
421 vp = SCM_VELTS (vec); /* vector pointer */
422 vlen = SCM_LENGTH (vec);
423
424 SCM_ASSERT (SCM_INUMP(startpos),
425 startpos, SCM_ARG3, s_restricted_vector_sort_x);
426 spos = SCM_INUM (startpos);
427 SCM_ASSERT ((spos >= 0) && (spos <= vlen),
428 startpos, SCM_ARG3, s_restricted_vector_sort_x);
429 SCM_ASSERT ((SCM_INUMP (endpos)) && (SCM_INUM (endpos) <= vlen),
430 endpos, SCM_ARG4, s_restricted_vector_sort_x);
431 len = SCM_INUM (endpos) - spos;
432
433 quicksort (&vp[spos], len, size, scm_cmp_function (less), less);
434 return SCM_UNSPECIFIED;
435 /* return vec; */
436} /* scm_restricted_vector_sort_x */
437
438/* (sorted? sequence less?)
439 * is true when sequence is a list (x0 x1 ... xm) or a vector #(x0 ... xm)
440 * such that for all 1 <= i <= m,
441 * (not (less? (list-ref list i) (list-ref list (- i 1)))). */
442SCM_PROC (s_sorted_p, "sorted?", 2, 0, 0, scm_sorted_p);
443
444SCM
445scm_sorted_p (SCM items, SCM less)
446{
447 long len, j; /* list/vector length, temp j */
448 SCM item, rest; /* rest of items loop variable */
449 SCM *vp;
450 cmp_fun_t cmp = scm_cmp_function (less);
451
452 if (SCM_NULLP (items))
453 return SCM_BOOL_T;
454 SCM_ASSERT (SCM_NIMP (items), items, SCM_ARG1, s_sorted_p);
455 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_sorted_p);
456
457 if (SCM_CONSP (items))
458 {
459 len = scm_ilength (items); /* also checks that it's a pure list */
460 SCM_ASSERT (len >= 0, items, SCM_ARG1, s_sorted_p);
461 if (len <= 1)
462 return SCM_BOOL_T;
463
464 item = SCM_CAR (items);
465 rest = SCM_CDR (items);
466 j = len - 1;
467 while (j > 0)
468 {
469 if ((*cmp) (less, &SCM_CAR(rest), &item))
470 return SCM_BOOL_F;
471 else
472 {
473 item = SCM_CAR (rest);
474 rest = SCM_CDR (rest);
475 j--;
476 }
477 }
478 return SCM_BOOL_T;
479 }
480 else
481 {
482 switch (SCM_TYP7 (items))
483 {
484 case scm_tc7_vector:
485 {
486 vp = SCM_VELTS (items); /* vector pointer */
487 len = SCM_LENGTH (items);
488 j = len - 1;
489 while (j > 0)
490 {
491 if ((*cmp) (less, &vp[1], vp))
492 return SCM_BOOL_F;
493 else
494 {
495 vp++;
496 j--;
497 }
498 }
499 return SCM_BOOL_T;
500 }
501 break;
502 case scm_tc7_ivect: /* long */
503 case scm_tc7_uvect: /* unsigned */
504 case scm_tc7_fvect: /* float */
505 case scm_tc7_dvect: /* double */
506 default:
507 scm_wta (items, (char *) SCM_ARG1, s_sorted_p);
508 }
509 }
510 return SCM_BOOL_F;
511} /* scm_sorted_p */
512
513/* (merge a b less?)
514 takes two lists a and b such that (sorted? a less?) and (sorted? b less?)
515 and returns a new list in which the elements of a and b have been stably
516 interleaved so that (sorted? (merge a b less?) less?).
517 Note: this does _not_ accept vectors. */
518SCM_PROC (s_merge, "merge", 3, 0, 0, scm_merge);
519
520SCM
521scm_merge (SCM alist, SCM blist, SCM less)
522{
523 long alen, blen; /* list lengths */
524 SCM build, last;
525 cmp_fun_t cmp = scm_cmp_function (less);
526 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_merge);
527
528 if (SCM_NULLP (alist))
529 return blist;
530 else if (SCM_NULLP (blist))
531 return alist;
532 else
533 {
534 alen = scm_ilength (alist); /* checks that it's a pure list */
535 blen = scm_ilength (blist); /* checks that it's a pure list */
536 SCM_ASSERT (alen > 0, alist, SCM_ARG1, s_merge);
537 SCM_ASSERT (blen > 0, blist, SCM_ARG2, s_merge);
c56cc3c8 538 if ((*cmp) (less, &SCM_CAR (blist), &SCM_CAR (alist)))
54e09076
MD
539 {
540 build = scm_cons (SCM_CAR (blist), SCM_EOL);
541 blist = SCM_CDR (blist);
542 blen--;
543 }
c56cc3c8
MD
544 else
545 {
546 build = scm_cons (SCM_CAR (alist), SCM_EOL);
547 alist = SCM_CDR (alist);
548 alen--;
549 }
54e09076
MD
550 last = build;
551 while ((alen > 0) && (blen > 0))
552 {
c56cc3c8 553 if ((*cmp) (less, &SCM_CAR (blist), &SCM_CAR (alist)))
54e09076
MD
554 {
555 SCM_SETCDR (last, scm_cons (SCM_CAR (blist), SCM_EOL));
556 blist = SCM_CDR (blist);
557 blen--;
558 }
c56cc3c8
MD
559 else
560 {
561 SCM_SETCDR (last, scm_cons (SCM_CAR (alist), SCM_EOL));
562 alist = SCM_CDR (alist);
563 alen--;
564 }
54e09076
MD
565 last = SCM_CDR (last);
566 }
567 if ((alen > 0) && (blen == 0))
568 SCM_SETCDR (last, alist);
569 else if ((alen == 0) && (blen > 0))
570 SCM_SETCDR (last, blist);
571 }
572 return build;
573} /* scm_merge */
574
575static SCM
576scm_merge_list_x (SCM alist, SCM blist,
577 long alen, long blen,
578 cmp_fun_t cmp, SCM less)
579{
580 SCM build, last;
581
582 if (SCM_NULLP (alist))
583 return blist;
584 else if (SCM_NULLP (blist))
585 return alist;
586 else
587 {
c56cc3c8 588 if ((*cmp) (less, &SCM_CAR (blist), &SCM_CAR (alist)))
54e09076
MD
589 {
590 build = blist;
591 blist = SCM_CDR (blist);
592 blen--;
593 }
c56cc3c8
MD
594 else
595 {
596 build = alist;
597 alist = SCM_CDR (alist);
598 alen--;
599 }
54e09076
MD
600 last = build;
601 while ((alen > 0) && (blen > 0))
602 {
c56cc3c8 603 if ((*cmp) (less, &SCM_CAR (blist), &SCM_CAR (alist)))
54e09076
MD
604 {
605 SCM_SETCDR (last, blist);
606 blist = SCM_CDR (blist);
607 blen--;
608 }
c56cc3c8
MD
609 else
610 {
611 SCM_SETCDR (last, alist);
612 alist = SCM_CDR (alist);
613 alen--;
614 }
54e09076
MD
615 last = SCM_CDR (last);
616 }
617 if ((alen > 0) && (blen == 0))
618 SCM_SETCDR (last, alist);
619 else if ((alen == 0) && (blen > 0))
620 SCM_SETCDR (last, blist);
621 }
622 return build;
623} /* scm_merge_list_x */
624
625SCM_PROC (s_merge_x, "merge!", 3, 0, 0, scm_merge_x);
626
627SCM
628scm_merge_x (SCM alist, SCM blist, SCM less)
629{
630 long alen, blen; /* list lengths */
631
632 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_merge_x);
633 if (SCM_NULLP (alist))
634 return blist;
635 else if (SCM_NULLP (blist))
636 return alist;
637 else
638 {
639 alen = scm_ilength (alist); /* checks that it's a pure list */
640 blen = scm_ilength (blist); /* checks that it's a pure list */
641 SCM_ASSERT (alen >= 0, alist, SCM_ARG1, s_merge);
642 SCM_ASSERT (blen >= 0, blist, SCM_ARG2, s_merge);
643 return scm_merge_list_x (alist, blist,
644 alen, blen,
645 scm_cmp_function (less),
646 less);
647 }
648} /* scm_merge_x */
649
650/* This merge sort algorithm is same as slib's by Richard A. O'Keefe.
651 The algorithm is stable. We also tried to use the algorithm used by
652 scsh's merge-sort but that algorithm showed to not be stable, even
653 though it claimed to be.
654*/
655static SCM
656scm_merge_list_step (SCM * seq,
657 cmp_fun_t cmp,
658 SCM less,
659 int n)
660{
c56cc3c8
MD
661 SCM a, b;
662
54e09076
MD
663 if (n > 2)
664 {
665 long mid = n / 2;
c56cc3c8
MD
666 a = scm_merge_list_step (seq, cmp, less, mid);
667 b = scm_merge_list_step (seq, cmp, less, n - mid);
668 return scm_merge_list_x (a, b, mid, n - mid, cmp, less);
54e09076
MD
669 }
670 else if (n == 2)
671 {
672 SCM p = *seq;
673 SCM rest = SCM_CDR (*seq);
674 SCM x = SCM_CAR (*seq);
675 SCM y = SCM_CAR (SCM_CDR (*seq));
676 *seq = SCM_CDR (rest);
677 SCM_SETCDR (rest, SCM_EOL);
678 if ((*cmp) (less, &y, &x))
679 {
680 SCM_CAR (p) = y;
681 SCM_CAR (rest) = x;
682 }
683 return p;
684 }
685 else if (n == 1)
686 {
687 SCM p = *seq;
688 *seq = SCM_CDR (p);
689 SCM_SETCDR (p, SCM_EOL);
690 return p;
691 }
692 else
693 return SCM_EOL;
694} /* scm_merge_list_step */
695
696
697SCM_PROC (s_sort_x, "sort!", 2, 0, 0, scm_sort_x);
698
699/* scm_sort_x manages lists and vectors, not stable sort */
700SCM
701scm_sort_x (SCM items, SCM less)
702{
703 long len; /* list/vector length */
704 if (SCM_NULLP(items))
705 return SCM_EOL;
706 SCM_ASSERT (SCM_NIMP (items), items, SCM_ARG1, s_sort_x);
707 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_sort_x);
708
709 if (SCM_CONSP (items))
710 {
711 len = scm_ilength (items);
712 SCM_ASSERT (len >= 0, items, SCM_ARG1, s_sort_x);
713 return scm_merge_list_step (&items, scm_cmp_function (less), less, len);
714 }
715 else if (SCM_VECTORP (items))
716 {
717 len = SCM_LENGTH (items);
718 scm_restricted_vector_sort_x (items,
719 less,
720 SCM_MAKINUM (0L),
721 SCM_MAKINUM (len));
722 return items;
723 }
724 else
725 return scm_wta (items, (char *) SCM_ARG1, s_sort_x);
726} /* scm_sort_x */
727
728SCM_PROC (s_sort, "sort", 2, 0, 0, scm_sort);
729
730/* scm_sort manages lists and vectors, not stable sort */
731SCM
732scm_sort (SCM items, SCM less)
733{
734 SCM sortvec; /* the vector we actually sort */
735 long len; /* list/vector length */
736 if (SCM_NULLP(items))
737 return SCM_EOL;
738 SCM_ASSERT (SCM_NIMP (items), items, SCM_ARG1, s_sort);
739 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_sort);
740 if (SCM_CONSP (items))
741 {
742 len = scm_ilength (items);
743 SCM_ASSERT (len >= 0, items, SCM_ARG1, s_sort);
744 items = scm_list_copy (items);
745 return scm_merge_list_step (&items, scm_cmp_function (less), less, len);
746 }
747 else if (SCM_VECTORP (items))
748 {
749 len = SCM_LENGTH (items);
750 sortvec = scm_make_uve (len, scm_array_prototype (items));
751 scm_array_copy_x (items, sortvec);
752 scm_restricted_vector_sort_x (sortvec,
753 less,
754 SCM_MAKINUM (0L),
755 SCM_MAKINUM (len));
756 return sortvec;
757 }
758 else
759 return scm_wta (items, (char *) SCM_ARG1, s_sort_x);
760} /* scm_sort */
761
762static void
763scm_merge_vector_x (void *const vecbase,
764 void *const tempbase,
765 cmp_fun_t cmp,
766 SCM less,
767 long low,
768 long mid,
769 long high)
770{
771 register SCM *vp = (SCM *) vecbase;
772 register SCM *temp = (SCM *) tempbase;
773 long it; /* Index for temp vector */
774 long i1 = low; /* Index for lower vector segment */
775 long i2 = mid + 1; /* Index for upper vector segment */
776
777 /* Copy while both segments contain more characters */
778 for (it = low; (i1 <= mid) && (i2 <= high); ++it)
779 if ((*cmp) (less, &vp[i2], &vp[i1]))
780 temp[it] = vp[i2++];
781 else
782 temp[it] = vp[i1++];
783
784 /* Copy while first segment contains more characters */
785 while (i1 <= mid)
786 temp[it++] = vp[i1++];
787
788 /* Copy while second segment contains more characters */
789 while (i2 <= high)
790 temp[it++] = vp[i2++];
791
792 /* Copy back from temp to vp */
793 for (it = low; it <= high; ++it)
794 vp[it] = temp[it];
795} /* scm_merge_vector_x */
796
797static void
798scm_merge_vector_step (void *const vp,
799 void *const temp,
800 cmp_fun_t cmp,
801 SCM less,
802 long low,
803 long high)
804{
805 if (high > low)
806 {
807 long mid = (low + high) / 2;
808 scm_merge_vector_step (vp, temp, cmp, less, low, mid);
809 scm_merge_vector_step (vp, temp, cmp, less, mid+1, high);
810 scm_merge_vector_x (vp, temp, cmp, less, low, mid, high);
811 }
812} /* scm_merge_vector_step */
813
814
815SCM_PROC (s_stable_sort_x, "stable-sort!", 2, 0, 0, scm_stable_sort_x);
816/* stable-sort! manages lists and vectors */
817
818SCM
819scm_stable_sort_x (SCM items, SCM less)
820{
821 long len; /* list/vector length */
822
823 if (SCM_NULLP (items))
824 return SCM_EOL;
825 SCM_ASSERT (SCM_NIMP (items), items, SCM_ARG1, s_stable_sort_x);
826 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_stable_sort_x);
827 if (SCM_CONSP (items))
828 {
829 len = scm_ilength (items);
830 SCM_ASSERT (len >= 0, items, SCM_ARG1, s_sort_x);
831 return scm_merge_list_step (&items, scm_cmp_function (less), less, len);
832 }
833 else if (SCM_VECTORP (items))
834 {
835 SCM *temp, *vp;
836 len = SCM_LENGTH (items);
837 temp = malloc (len * sizeof(SCM));
838 vp = SCM_VELTS (items);
839 scm_merge_vector_step (vp,
840 temp,
841 scm_cmp_function (less),
842 less,
843 0,
844 len - 1);
845 free(temp);
846 return items;
847 }
848 else
849 return scm_wta (items, (char *) SCM_ARG1, s_stable_sort_x);
850} /* scm_stable_sort_x */
851
852SCM_PROC (s_stable_sort, "stable-sort", 2, 0, 0, scm_stable_sort);
853
854/* stable_sort manages lists and vectors */
855SCM
856scm_stable_sort (SCM items, SCM less)
857{
858 long len; /* list/vector length */
859 if (SCM_NULLP (items))
860 return SCM_EOL;
861 SCM_ASSERT (SCM_NIMP (items), items, SCM_ARG1, s_stable_sort);
862 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_stable_sort);
863 if (SCM_CONSP (items))
864 {
865 len = scm_ilength (items);
866 SCM_ASSERT (len >= 0, items, SCM_ARG1, s_sort);
867 items = scm_list_copy (items);
868 return scm_merge_list_step (&items, scm_cmp_function (less), less, len);
869 }
870 else if (SCM_VECTORP (items))
871 {
872 SCM retvec;
873 SCM *temp, *vp;
874 len = SCM_LENGTH (items);
875 retvec = scm_make_uve (len, scm_array_prototype (items));
876 scm_array_copy_x (items, retvec);
877 temp = malloc (len * sizeof (SCM));
878 vp = SCM_VELTS (retvec);
879 scm_merge_vector_step (vp,
880 temp,
881 scm_cmp_function (less),
882 less,
883 0,
884 len - 1);
885 free (temp);
886 return retvec;
887 }
888 else
889 return scm_wta (items, (char *) SCM_ARG1, s_stable_sort);
890} /* scm_stable_sort */
891
892SCM_PROC (s_sort_list_x, "sort-list!", 2, 0, 0, scm_sort_list_x);
893
894SCM /* stable */
895scm_sort_list_x (SCM items, SCM less)
896{
897 long len = scm_ilength (items);
898 SCM_ASSERT (len >= 0, items, SCM_ARG1, s_sort_list_x);
899 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_sort_list_x);
900 return scm_merge_list_step (&items, scm_cmp_function (less), less, len);
901} /* scm_sort_list_x */
902
903SCM_PROC (s_sort_list, "sort-list", 2, 0, 0, scm_sort_list);
904
905SCM /* stable */
906scm_sort_list (SCM items, SCM less)
907{
908 long len = scm_ilength (items);
909 SCM_ASSERT (len >= 0, items, SCM_ARG1, s_sort_list);
910 SCM_ASSERT (SCM_NIMP (less), less, SCM_ARG2, s_sort_list);
911 items = scm_list_copy (items);
912 return scm_merge_list_step (&items, scm_cmp_function (less), less, len);
913} /* scm_sort_list_x */
914
915void
916scm_init_sort ()
917{
918#include "sort.x"
919
920 scm_add_feature ("sort");
921}