factor/basis/interval-sets/interval-sets.factor

112 lines
2.5 KiB
Factor

! Copyright (C) 2009 Daniel Ehrenberg.
! See http://factorcode.org/license.txt for BSD license.
USING: accessors alien.c-types arrays assocs binary-search
combinators fry grouping kernel locals make math math.order
sequences sequences.private sorting specialized-arrays ;
SPECIALIZED-ARRAY: uint
IN: interval-sets
! Sets of positive integers
TUPLE: interval-set { array uint-array read-only } ;
<PRIVATE
ERROR: not-an-interval-set obj ;
: check-interval-set ( map -- map )
dup interval-set? [ not-an-interval-set ] unless ; inline
PRIVATE>
: in? ( key set -- ? )
check-interval-set array>>
dupd [ <=> ] with search swap [
even? [ >= ] [ 1 - <= ] if
] [ 2drop f ] if* ;
<PRIVATE
: spec>pairs ( sequence -- intervals )
[ dup number? [ dup 2array ] when ] map ;
ALIAS: start first-unsafe
ALIAS: end second-unsafe
: disjoint? ( node1 node2 -- ? )
[ end ] [ start ] bi* < ;
: (delete-redundancies) ( seq -- )
dup length {
{ 0 [ drop ] }
{ 1 [ % ] }
[
drop dup first2 <
[ unclip-slice , ]
[ 2 tail-slice ] if
(delete-redundancies)
]
} case ;
: delete-redundancies ( seq -- seq' )
! If the next element is >= current one, leave out both
[ (delete-redundancies) ] uint-array{ } make ;
: make-intervals ( seq -- interval-set )
uint-array{ } like
delete-redundancies
interval-set boa ;
: >intervals ( seq -- seq' )
[ 1 + ] assoc-map concat ;
PRIVATE>
: <interval-set> ( specification -- interval-set )
spec>pairs sort-keys
>intervals make-intervals ;
<PRIVATE
:: or-step ( set1 set2 -- set1' set2' )
set1 first ,
set1 second set2 first <=
[ set1 0 ] [ set2 2 ] if
[ second , ] [ set2 swap tail-slice ] bi*
set1 2 tail-slice ;
: combine-or ( set1 set2 -- )
{
{ [ over empty? ] [ % drop ] }
{ [ dup empty? ] [ drop % ] }
[
2dup [ first ] bi@ <=
[ swap ] unless
or-step combine-or
]
} cond ;
PRIVATE>
: <interval-or> ( set1 set2 -- set )
[ array>> ] bi@
[ combine-or ] uint-array{ } make
make-intervals ;
<PRIVATE
: prefix-0 ( seq -- 0seq )
0 over ?nth zero? [ rest ] [ 0 prefix ] if ;
: interval-max ( interval-set1 interval-set2 -- n )
[ array>> [ 0 ] [ last ] if-empty ] bi@ max ;
PRIVATE>
: <interval-not> ( set maximum -- set' )
[ array>> prefix-0 ] dip suffix make-intervals ;
: <interval-and> ( set1 set2 -- set )
2dup interval-max
[ '[ _ <interval-not> ] bi@ <interval-or> ] keep
<interval-not> ;