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 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-to-index <slice-unsafe> ] 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 ;
61 : permutation ( n seq -- seq' )
62 [ permutation-indices ] keep nths-unsafe ;
64 TUPLE: permutations length seq ;
66 : <permutations> ( seq -- permutations )
67 [ length factorial ] keep permutations boa ;
69 M: permutations length length>> ; inline
70 M: permutations nth-unsafe seq>> permutation ;
71 M: permutations hashcode* tuple-hashcode ;
73 INSTANCE: permutations immutable-sequence
75 TUPLE: k-permutations length skip k seq ;
77 :: <k-permutations> ( seq k -- permutations )
81 { [ len k [ zero? ] either? ] [ { } ] }
82 { [ n k = ] [ seq <permutations> ] }
83 [ len n factorial over /i k seq k-permutations boa ]
86 M: k-permutations length length>> ; inline
87 M: k-permutations nth-unsafe
89 [ seq>> [ permutation-indices ] keep ]
90 [ k>> swap [ head ] dip nths-unsafe ] tri ;
91 M: k-permutations hashcode* tuple-hashcode ;
93 INSTANCE: k-permutations immutable-sequence
95 DEFER: next-permutation
99 : <permutation-iota> ( seq -- <iota> )
100 length factorial <iota> ; inline
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-unsafe ] keep [ [ > ] keep swap ] find-last drop nip ; inline
138 : greater-from-last ( n seq -- i )
139 [ nip ] [ nth-unsafe ] 2bi [ > ] curry find-last drop ; inline
141 : reverse-tail! ( n seq -- seq )
142 [ swap 1 + index-to-tail <slice-unsafe> reverse! drop ] keep ; inline
144 : (next-permutation) ( seq -- seq )
146 swap [ greater-from-last ] 2keep
147 [ exchange-unsafe ] [ 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-unsafe ] [ <slice-unsafe> 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 :: nCk-with-replacement ( n k -- nCk )
188 k 1 - n + factorial k factorial / n 1 - factorial / ; inline
190 :: next-combination-with-replacement ( seq n -- seq )
191 seq n 1 - '[ _ = not ] find-last drop :> i
192 seq i tail-slice i seq nth 1 + '[ drop _ ] map! drop
195 :: combinations-with-replacement-quot ( seq k quot -- seq quot' )
197 n k nCk-with-replacement <iota> k 0 <array> seq quot n
198 '[ drop _ [ _ nths-unsafe @ ] keep _ next-combination-with-replacement drop ] ; inline
202 : each-combination-with-replacement ( ... seq k quot: ( ... elt -- ... ) -- ... )
203 combinations-with-replacement-quot each ; inline
205 : map-combinations-with-replacement ( ... seq k quot: ( ... elt -- ... newelt ) -- ... newseq )
206 combinations-with-replacement-quot map ; inline
208 : filter-combinations-with-replacement ( ... seq k quot: ( ... elt -- ... ? ) -- ... newseq )
209 selector [ each-combination-with-replacement ] dip ; inline
211 : map>assoc-combinations-with-replacement ( ... seq k quot: ( ... elt -- ... key value ) exemplar -- ... assoc )
212 [ combinations-with-replacement-quot ] dip map>assoc ; inline
214 : all-combinations-with-replacement ( seq k -- seq' )
215 [ ] map-combinations-with-replacement ;
217 : all-combinations-with-replacement? ( ... seq k quot: ( ... elt -- ... ? ) -- ... ? )
218 combinations-with-replacement-quot all? ; inline
220 : find-combination-with-replacement ( ... seq k quot: ( ... elt -- ... ? ) -- ... elt/f )
221 [ f ] 3dip '[ nip _ keep swap ] combinations-with-replacement-quot find drop swap and ; inline
223 : reduce-combinations-with-replacement ( ... seq k identity quot: ( ... prev elt -- ... next ) -- ... result )
224 -rotd each-combination-with-replacement ; inline
228 ! "Algorithm 515: Generation of a Vector from the Lexicographical Index"
229 ! Buckles, B. P., and Lybanon, M. ACM
230 ! Transactions on Mathematical Software, Vol. 3, No. 2, June 1977.
232 :: combination-indices ( x! p n -- seq )
234 p 0 <array> :> c 0 :> k! 0 :> r!
236 i [ 0 ] [ 1 - c nth-unsafe ] if-zero i c set-nth-unsafe
238 i c [ 1 + ] change-nth-unsafe
239 n i c nth-unsafe - p i 1 + - nCk r!
243 p 2 < [ 0 ] [ p 2 - c nth-unsafe ] if
244 p 1 < [ drop ] [ x + k - p 1 - c set-nth-unsafe ] if
249 : combination ( m seq k -- seq' )
250 swap [ length combination-indices ] [ nths-unsafe ] bi ;
252 TUPLE: combinations seq k length ;
254 : <combinations> ( seq k -- combinations )
255 2dup [ length ] [ nCk ] bi* combinations boa ;
257 M: combinations length length>> ; inline
258 M: combinations nth-unsafe [ seq>> ] [ k>> ] bi combination ;
259 M: combinations hashcode* tuple-hashcode ;
261 INSTANCE: combinations immutable-sequence
265 : find-max-index ( seq n -- i )
266 over length - '[ _ + >= ] find-index drop ; inline
268 : increment-rest ( i seq -- )
269 [ nth-unsafe ] [ swap index-to-tail <slice-unsafe> ] 2bi
270 [ drop 1 + dup ] map! 2drop ; inline
272 : increment-last ( seq -- )
273 [ [ length 1 - ] keep [ 1 + ] change-nth-unsafe ] unless-empty ; inline
275 :: next-combination ( seq n -- seq )
276 seq n find-max-index [
277 1 [-] seq increment-rest
282 :: combinations-quot ( seq k quot -- seq quot' )
284 n k nCk <iota> k <iota> >array seq quot n
285 '[ drop _ [ _ nths-unsafe @ ] keep _ next-combination drop ] ; inline
289 : each-combination ( ... seq k quot: ( ... elt -- ... ) -- ... )
290 combinations-quot each ; inline
292 : map-combinations ( ... seq k quot: ( ... elt -- ... newelt ) -- ... newseq )
293 combinations-quot map ; inline
295 : filter-combinations ( ... seq k quot: ( ... elt -- ... ? ) -- ... newseq )
296 selector [ each-combination ] dip ; inline
298 : map>assoc-combinations ( ... seq k quot: ( ... elt -- ... key value ) exemplar -- ... assoc )
299 [ combinations-quot ] dip map>assoc ; inline
301 : all-combinations ( seq k -- seq' )
302 [ ] map-combinations ;
304 : all-combinations? ( ... seq k quot: ( ... elt -- ... ? ) -- ... ? )
305 combinations-quot all? ; inline
307 : find-combination ( ... seq k quot: ( ... elt -- ... ? ) -- ... elt/f )
308 [ f ] 3dip '[ nip _ keep swap ] combinations-quot find drop swap and ; inline
310 : reduce-combinations ( ... seq k identity quot: ( ... prev elt -- ... next ) -- ... result )
311 -rotd each-combination ; inline
313 : all-subsets ( seq -- subsets )
314 dup length [0..b] [ all-combinations ] with map concat ;
318 :: next-selection ( seq n -- )
320 dup 0 >= [ over 0 = ] [ t ] if
322 [ seq [ + n /mod ] change-nth-unsafe ] keep 1 -
323 ] do until 2drop ; inline
325 :: selections-quot ( seq n quot -- seq quot' )
328 n [ 0 ] [ len swap ^ ] if-zero <iota> [
330 idx seq nths-unsafe quot call
331 idx len next-selection
336 : each-selection ( ... seq n quot: ( ... elt -- ... ) -- ... )
337 selections-quot each ; inline
339 : map-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
340 selections-quot map ; inline
342 : filter-selections ( ... seq n quot: ( ... elt -- ... newelt ) -- ... newseq )
343 selector [ each-selection ] dip ; inline
345 : all-selections ( seq n -- seq' )
348 : all-selections? ( seq n -- ? )
349 selections-quot all? ; inline
351 : find-selection ( ... seq n quot: ( ... elt -- ... ? ) -- ... elt/f )
352 [ f ] 3dip '[ nip _ keep swap ] selections-quot find drop swap and ; inline
354 : reduce-selections ( ... seq n identity quot: ( ... prev elt -- ... next ) -- ... result )
355 -rotd each-selection ; inline