]> gitweb.factorcode.org Git - factor.git/blob - core/sorting/sorting.factor
949c306a67e79e18434170487b87ead97db9141e
[factor.git] / core / sorting / sorting.factor
1 ! Copyright (C) 2005, 2009 Slava Pestov.
2 ! See https://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays assocs growable.private hashtables
4 kernel kernel.private math math.order math.private sequences
5 sequences.private vectors ;
6 IN: sorting
7
8 ! Optimized merge-sort:
9 !
10 ! 1) only allocates 2 temporary arrays
11
12 ! 2) first phase (interchanging pairs x[i], x[i+1] where
13 ! x[i] > x[i+1]) is handled specially
14
15 <PRIVATE
16
17 TUPLE: merge-state
18 { seq    array }
19 { accum  vector }
20 { accum1 vector }
21 { accum2 vector }
22 { from1  array-capacity }
23 { to1    array-capacity }
24 { from2  array-capacity }
25 { to2    array-capacity } ;
26
27 : l-elt ( merge -- elt ) [ from1>> ] [ seq>> ] bi nth-unsafe ; inline
28
29 : r-elt ( merge -- elt ) [ from2>> ] [ seq>> ] bi nth-unsafe ; inline
30
31 : l-done? ( merge -- ? ) [ from1>> ] [ to1>> ] bi eq? ; inline
32
33 : r-done? ( merge -- ? ) [ from2>> ] [ to2>> ] bi eq? ; inline
34
35 : dump-l ( merge -- )
36     [ accum>> ] keep
37     [ [ to1>> ] [ from1>> fixnum-fast ] [ accum>> length integer>fixnum-strict ] tri [ fixnum+fast >>length ] 2keep ]
38     [ seq>> ]
39     [ from1>> roll dupd fixnum+fast ] tri
40     copy-loop drop ; inline
41
42 : dump-r ( merge -- )
43     [ accum>> ] keep
44     [ [ to2>> ] [ from2>> fixnum-fast ] [ accum>> length integer>fixnum-strict ] tri [ fixnum+fast >>length ] 2keep ]
45     [ seq>> ]
46     [ from2>> roll dupd fixnum+fast ] tri
47     copy-loop drop ; inline
48
49 : l-next ( merge -- )
50     [ l-elt ] [ [ 1 + ] change-from1 accum>> ] bi push-unsafe ; inline
51
52 : r-next ( merge -- )
53     [ r-elt ] [ [ 1 + ] change-from2 accum>> ] bi push-unsafe ; inline
54
55 : decide? ( merge quot: ( elt1 elt2 -- <=> ) -- ? )
56     [ [ l-elt ] [ r-elt ] bi ] dip call +gt+ eq? ; inline
57
58 : (merge) ( merge quot: ( elt1 elt2 -- <=> ) -- )
59     over r-done? [ drop dump-l ] [
60         over l-done? [ drop dump-r ] [
61             2dup decide?
62             [ over r-next ] [ over l-next ] if
63             (merge)
64         ] if
65     ] if ; inline recursive
66
67 : flip-accum ( merge -- )
68     dup [ accum>> ] [ accum1>> ] bi eq? [
69         dup accum1>> underlying>> >>seq
70         dup accum2>> >>accum
71     ] [
72         dup accum1>> >>accum
73         dup accum2>> underlying>> >>seq
74     ] if
75     dup accum>> 0 >>length 2drop ; inline
76
77 : <merge> ( seq -- merge )
78     \ merge-state new
79         over >vector >>accum1
80         swap length <vector> >>accum2
81         dup accum1>> underlying>> >>seq
82         dup accum2>> >>accum ; inline
83
84 : compute-midpoint ( merge -- merge )
85     dup [ from1>> ] [ to2>> ] bi + 2/ >>to1 ; inline
86
87 : merging ( from to merge -- )
88     swap >>to2
89     swap >>from1
90     compute-midpoint
91     dup [ to1>> ] [ seq>> length ] bi min >>to1
92     dup [ to2>> ] [ seq>> length ] bi min >>to2
93     dup to1>> >>from2
94     drop ; inline
95
96 : nth-chunk ( n size -- from to ) [ * dup ] keep + ; inline
97
98 : chunks ( length size -- n ) [ align ] keep /i ; inline
99
100 : each-chunk ( length size quot -- )
101     [ [ chunks ] keep ] dip
102     [ nth-chunk ] prepose curry
103     each-integer ; inline
104
105 : merge ( from to merge quot -- )
106     [ [ merging ] keep ] dip (merge) ; inline
107
108 : sort-pass ( merge size quot -- )
109     [
110         over flip-accum
111         over [ seq>> length ] 2dip
112     ] dip
113     [ merge ] 2curry each-chunk ; inline
114
115 : sort-loop ( merge quot -- )
116     [ 2 over seq>> length [ over > ] curry ] dip
117     [ [ 1 shift 2dup ] dip sort-pass ] curry
118     while 2drop ; inline
119
120 : each-pair ( seq quot -- )
121     [ [ length 1 + 2/ ] keep ] dip
122     [ [ 1 shift dup 1 + ] dip ] prepose curry each-integer ; inline
123
124 : (sort-pairs) ( i1 i2 seq quot accum -- )
125     [ 2dup length = ] 2dip rot [
126         [ drop nip nth-unsafe ] dip push-unsafe
127     ] [
128         [
129             [ [ nth-unsafe ] curry bi@ 2dup ] dip call +gt+ eq?
130             [ swap ] when
131         ] dip [ push-unsafe ] curry bi@
132     ] if ; inline
133
134 : sort-pairs ( merge quot -- )
135     [ [ seq>> ] [ accum>> ] bi ] dip swap
136     [ (sort-pairs) ] 2curry each-pair ; inline
137
138 PRIVATE>
139
140 : sort-with ( seq quot: ( obj1 obj2 -- <=> ) -- sortedseq )
141     [ <merge> ] dip
142     [ sort-pairs ] [ sort-loop ] [ drop accum>> underlying>> ] 2tri ; inline
143
144 : inv-sort-with ( seq quot: ( obj1 obj2 -- <=> ) -- sortedseq )
145     '[ @ invert-comparison ] sort-with ; inline
146
147 : sort ( seq -- sortedseq ) [ <=> ] sort-with ;
148
149 : inv-sort ( seq -- sortedseq ) [ >=< ] sort-with ;
150
151 : sort-by ( seq quot: ( elt -- key ) -- sortedseq )
152     [ compare ] curry sort-with ; inline
153
154 : inv-sort-by ( seq quot: ( elt -- key ) -- sortedseq )
155     [ compare invert-comparison ] curry sort-with ; inline
156
157 ALIAS: natural-sort sort ! temporary, deprecated
158
159 <PRIVATE
160
161 : check-bounds ( alist n -- alist )
162     [ swap bounds-check 2drop ] curry dupd each ; inline
163
164 PRIVATE>
165
166 GENERIC: sort-keys ( obj -- sortedseq )
167
168 M: object sort-keys >alist sort-keys ;
169
170 M: sequence sort-keys
171     0 check-bounds [ first-unsafe ] sort-by ;
172
173 M: hashtable sort-keys
174     >alist [ { array } declare first-unsafe ] sort-by ;
175
176 GENERIC: inv-sort-keys ( obj -- sortedseq )
177
178 M: object inv-sort-keys >alist inv-sort-keys ;
179
180 M: sequence inv-sort-keys
181     0 check-bounds [ first-unsafe ] inv-sort-by ;
182
183 M: hashtable inv-sort-keys
184     >alist [ { array } declare first-unsafe ] inv-sort-by ;
185
186 GENERIC: sort-values ( obj -- sortedseq )
187
188 M: object sort-values >alist sort-values ;
189
190 M: sequence sort-values
191     1 check-bounds [ second-unsafe ] sort-by ;
192
193 M: hashtable sort-values
194     >alist [ { array } declare second-unsafe ] sort-by ;
195
196 : sort-pair ( a b -- c d ) 2dup after? [ swap ] when ;
197
198 GENERIC: inv-sort-values ( obj -- sortedseq )
199
200 M: object inv-sort-values >alist inv-sort-values ;
201
202 M: sequence inv-sort-values
203     1 check-bounds [ second-unsafe ] inv-sort-by ;
204
205 M: hashtable inv-sort-values
206     >alist [ { array } declare second-unsafe ] inv-sort-by ;