1 ! Copyright (c) 2007-2010 Slava Pestov, Doug Coleman, Aaron Schaefer, John Benediktsson.
2 ! See http://factorcode.org/license.txt for BSD license.
4 USING: accessors arrays assocs classes.tuple combinators hints
5 kernel kernel.private make math math.functions math.order
6 math.ranges sequences sequences.private sorting strings vectors ;
11 ! Specialized version of nths-unsafe for performance
12 : (nths-unsafe) ( indices seq -- seq' )
13 [ { array } declare ] dip
14 [ [ nth-unsafe ] curry ] keep map-as ; inline
15 GENERIC: nths-unsafe ( indices seq -- seq' )
16 M: string nths-unsafe (nths-unsafe) ;
17 M: array nths-unsafe (nths-unsafe) ;
18 M: vector nths-unsafe (nths-unsafe) ;
19 M: iota nths-unsafe (nths-unsafe) ;
20 M: object nths-unsafe (nths-unsafe) ;
22 : possible? ( n m -- ? )
23 0 rot between? ; inline
25 : twiddle ( n k -- n k )
26 2dup - dupd > [ dupd - ] when ; inline
30 : factorial ( n -- n! )
31 dup 1 > [ [1..b] product ] [ drop 1 ] if ;
34 2dup possible? [ dupd - [a..b) product ] [ 2drop 0 ] if ;
37 twiddle [ nPk ] keep factorial /i ;
40 ! Factoradic-based permutation methodology
44 : factoradic ( n -- factoradic )
45 0 [ over 0 > ] [ 1 + [ /mod ] keep swap ] produce reverse! 2nip ;
47 : bump-indices ( seq n -- )
48 '[ dup _ >= [ 1 + ] when ] map! drop ; inline
50 : (>permutation) ( seq n index -- seq )
51 swap [ dupd head-slice ] dip bump-indices ;
53 : >permutation ( factoradic -- permutation )
54 reverse! dup [ (>permutation) ] each-index reverse! ;
56 : permutation-indices ( n seq -- permutation )
57 length [ factoradic ] dip 0 pad-head >permutation ;
59 : permutation-iota ( seq -- <iota> )
60 length factorial <iota> ; inline
64 : permutation ( n seq -- seq' )
65 [ permutation-indices ] keep nths-unsafe ;
67 TUPLE: permutations length seq ;
69 : <permutations> ( seq -- permutations )
70 [ length factorial ] keep permutations boa ;
72 M: permutations length length>> ; inline
73 M: permutations nth-unsafe seq>> permutation ;
74 M: permutations hashcode* tuple-hashcode ;
76 INSTANCE: permutations immutable-sequence
78 TUPLE: k-permutations length skip k seq ;
80 :: <k-permutations> ( seq k -- permutations )
84 { [ len k [ zero? ] either? ] [ { } ] }
85 { [ n k = ] [ seq <permutations> ] }
86 [ len n factorial over /i k seq k-permutations boa ]
89 M: k-permutations length length>> ; inline
90 M: k-permutations nth-unsafe
92 [ seq>> [ permutation-indices ] keep ]
93 [ k>> swap [ head ] dip nths-unsafe ] tri ;
94 M: k-permutations hashcode* tuple-hashcode ;
96 INSTANCE: k-permutations immutable-sequence
98 DEFER: next-permutation
102 : permutations-quot ( seq quot -- seq quot' )
103 [ [ permutation-iota ] [ length <iota> >array ] [ ] tri ] dip
104 '[ drop _ [ _ nths-unsafe @ ] keep next-permutation drop ] ; inline
108 : each-permutation ( ... seq quot: ( ... elt -- ... ) -- ... )
109 permutations-quot each ; inline
111 : map-permutations ( ... seq quot: ( ... elt -- ... newelt ) -- ... newseq )
112 permutations-quot map ; inline
114 : filter-permutations ( ... seq quot: ( ... elt -- ... ? ) -- ... newseq )
115 selector [ each-permutation ] dip ; inline
117 : all-permutations ( seq -- seq' )
118 [ ] map-permutations ;
120 : all-permutations? ( ... seq quot: ( ... elt -- ... ? ) -- ... ? )
121 permutations-quot all? ; inline
123 : find-permutation ( ... seq quot: ( ... elt -- ... ? ) -- ... elt/f )
124 [ permutations-quot find drop ]
125 [ drop over [ permutation ] [ 2drop f ] if ] 2bi ; inline
127 : reduce-permutations ( ... seq identity quot: ( ... prev elt -- ... next ) -- ... result )
128 swapd each-permutation ; inline
130 : inverse-permutation ( seq -- permutation )
131 <enumerated> sort-values keys ;
135 : cut-point ( seq -- n )
136 [ last ] keep [ [ > ] keep swap ] find-last drop nip ; inline
138 : greater-from-last ( n seq -- i )
139 [ nip ] [ nth ] 2bi [ > ] curry find-last drop ; inline
141 : reverse-tail! ( n seq -- seq )
142 [ swap 1 + tail-slice reverse! drop ] keep ; inline
144 : (next-permutation) ( seq -- seq )
146 swap [ greater-from-last ] 2keep
147 [ exchange ] [ reverse-tail! nip ] 3bi
150 HINTS: (next-permutation) array ;
154 : next-permutation ( seq -- seq )
155 dup empty? [ (next-permutation) ] unless ;
159 : should-swap? ( start curr seq -- ? )
160 [ nipd nth ] [ <slice> member? not ] 3bi ; inline
162 :: unique-permutations ( ... seq i n quot: ( ... elt -- ... ) -- ... )
167 i j seq should-swap? [
168 i j seq exchange-unsafe
169 seq i 1 + n quot unique-permutations
170 i j seq exchange-unsafe
173 ] if ; inline recursive
177 : each-unique-permutation ( ... seq quot: ( ... elt -- ... ) -- ... )
178 [ 0 over length ] dip unique-permutations ; inline
180 : all-unique-permutations ( seq -- seq' )
181 [ [ , ] each-unique-permutation ] { } make ;
183 ! Combinadic-based combination methodology
187 ! "Algorithm 515: Generation of a Vector from the Lexicographical Index"
188 ! Buckles, B. P., and Lybanon, M. ACM
189 ! Transactions on Mathematical Software, Vol. 3, No. 2, June 1977.
191 :: combination-indices ( x! p n -- seq )
193 p 0 <array> :> c 0 :> k! 0 :> r!
195 i [ 0 ] [ 1 - c nth ] if-zero i c set-nth
197 i c [ 1 + ] change-nth
198 n i c nth - p i 1 + - nCk r!
202 p 2 < [ 0 ] [ p 2 - c nth ] if
203 p 1 < [ drop ] [ x + k - p 1 - c set-nth ] if
208 : combination ( m seq k -- seq' )
209 swap [ length combination-indices ] [ nths-unsafe ] bi ;
211 TUPLE: combinations seq k length ;
213 : <combinations> ( seq k -- combinations )
214 2dup [ length ] [ nCk ] bi* combinations boa ;
216 M: combinations length length>> ; inline
217 M: combinations nth-unsafe [ seq>> ] [ k>> ] bi combination ;
218 M: combinations hashcode* tuple-hashcode ;
220 INSTANCE: combinations immutable-sequence
224 : find-max-index ( seq n -- i )
225 over length - '[ _ + >= ] find-index drop ; inline
227 : increment-rest ( i seq -- )
228 [ nth ] [ swap tail-slice ] 2bi
229 [ drop 1 + dup ] map! 2drop ; inline
231 : increment-last ( seq -- )
232 [ [ length 1 - ] keep [ 1 + ] change-nth ] unless-empty ; inline
234 :: next-combination ( seq n -- seq )
235 seq n find-max-index [
236 1 [-] seq increment-rest
241 :: combinations-quot ( seq k quot -- seq quot' )
243 n k nCk <iota> k <iota> >array seq quot n
244 '[ drop _ [ _ nths-unsafe @ ] keep _ next-combination drop ] ; inline
248 : each-combination ( ... seq k quot: ( ... elt -- ... ) -- ... )
249 combinations-quot each ; inline
251 : map-combinations ( ... seq k quot: ( ... elt -- ... newelt ) -- ... newseq )
252 combinations-quot map ; inline
254 : filter-combinations ( ... seq k quot: ( ... elt -- ... ? ) -- ... newseq )
255 selector [ each-combination ] dip ; inline
257 : map>assoc-combinations ( ... seq k quot: ( ... elt -- ... key value ) exemplar -- ... assoc )
258 [ combinations-quot ] dip map>assoc ; inline
260 : all-combinations ( seq k -- seq' )
261 [ ] map-combinations ;
263 : all-combinations? ( ... seq k quot: ( ... elt -- ... ? ) -- ... ? )
264 combinations-quot all? ; inline
266 : find-combination ( ... seq k quot: ( ... elt -- ... ? ) -- ... elt/f )
267 [ f ] 3dip '[ nip _ keep swap ] combinations-quot find drop swap and ; inline
269 : reduce-combinations ( ... seq k identity quot: ( ... prev elt -- ... next ) -- ... result )
270 -rotd each-combination ; inline
272 : all-subsets ( seq -- subsets )
273 dup length [0..b] [ all-combinations ] with map concat ;
277 :: next-selection ( seq n -- )
279 dup 0 >= [ over 0 = ] [ t ] if
281 [ seq [ + n /mod ] change-nth-unsafe ] keep 1 -
282 ] do until 2drop ; inline
284 :: selections-quot ( seq n quot -- seq quot' )
287 n [ 0 ] [ len swap ^ ] if-zero <iota> [
289 idx seq nths-unsafe quot call
290 idx len next-selection
295 : each-selection ( ... seq n quot: ( ... elt -- ... ) -- ... )
296 selections-quot each ; inline
298 : map-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
299 selections-quot map ; inline
301 : filter-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
302 selector [ each-selection ] dip ; inline
304 : all-selections ( seq n -- seq' )
307 : all-selections? ( seq n -- ? )
308 selections-quot all? ; inline
310 : find-selection ( ... seq n quot: ( ... elt -- ... ? ) -- ... elt/f )
311 [ f ] 3dip '[ nip _ keep swap ] selections-quot find drop swap and ; inline
313 : reduce-selections ( ... seq n identity quot: ( ... prev elt -- ... next ) -- ... result )
314 -rotd each-selection ; inline