Merge branch 'master' of git://factorcode.org/git/factor

db4
Doug Coleman 2008-08-06 11:13:04 -05:00
commit 45d96fdf33
39 changed files with 586 additions and 41 deletions

View File

@ -32,7 +32,7 @@ HELP: assoc>disjoint-set
"4 5 pick equiv? ."
"1 5 pick equiv? ."
"drop"
"t\nt\nf\n"
"t\nt\nf"
}
} ;

View File

@ -1,7 +1,7 @@
IN: io.windows.launcher.nt.tests
USING: io.launcher tools.test calendar accessors
namespaces kernel system arrays io io.files io.encodings.ascii
sequences parser assocs hashtables math continuations ;
sequences parser assocs hashtables math continuations eval ;
[ ] [
<process>

View File

@ -0,0 +1,18 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel assocs ;
IN: persistent.assocs
GENERIC: new-at ( value key assoc -- assoc' )
M: assoc new-at clone [ set-at ] keep ;
GENERIC: pluck-at ( key assoc -- assoc' )
M: assoc pluck-at clone [ delete-at ] keep ;
: changed-at ( key assoc quot -- assoc' )
[ [ at ] dip call ] [ drop new-at ] 3bi ; inline
: conjoined ( key assoc -- assoc' )
dupd new-at ;

View File

@ -0,0 +1 @@
Persistent associative mapping protocol

View File

@ -0,0 +1 @@
Slava Pestov

View File

@ -0,0 +1,8 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: layouts kernel parser math ;
IN: persistent.hashtables.config
: radix-bits ( -- n ) << cell 4 = 4 5 ? parsed >> ; foldable
: radix-mask ( -- n ) radix-bits 2^ 1- ; foldable
: full-bitmap-mask ( -- n ) radix-bits 2^ 2^ 1- ; inline

View File

@ -0,0 +1,110 @@
IN: persistent.hashtables.tests
USING: persistent.hashtables persistent.assocs hashtables assocs
tools.test kernel namespaces random math.ranges sequences fry ;
[ t ] [ PH{ } assoc-empty? ] unit-test
[ PH{ { "A" "B" } } ] [ PH{ } "B" "A" rot new-at ] unit-test
[ "B" ] [ "A" PH{ { "A" "B" } } at ] unit-test
[ f ] [ "X" PH{ { "A" "B" } } at ] unit-test
TUPLE: hash-0-a ;
M: hash-0-a hashcode* 2drop 0 ;
TUPLE: hash-0-b ;
M: hash-0-b hashcode* 2drop 0 ;
[ ] [
PH{ }
"a" T{ hash-0-a } rot new-at
"b" T{ hash-0-b } rot new-at
"ph" set
] unit-test
[
H{
{ T{ hash-0-a } "a" }
{ T{ hash-0-b } "b" }
}
] [ "ph" get >hashtable ] unit-test
[
H{
{ T{ hash-0-b } "b" }
}
] [ "ph" get T{ hash-0-a } swap pluck-at >hashtable ] unit-test
[
H{
{ T{ hash-0-a } "a" }
}
] [ "ph" get T{ hash-0-b } swap pluck-at >hashtable ] unit-test
[
H{
{ T{ hash-0-a } "a" }
{ T{ hash-0-b } "b" }
}
] [ "ph" get "X" swap pluck-at >hashtable ] unit-test
[ ] [
PH{ }
"B" "A" rot new-at
"D" "C" rot new-at
"ph" set
] unit-test
[ H{ { "A" "B" } { "C" "D" } } ] [
"ph" get >hashtable
] unit-test
[ H{ { "C" "D" } } ] [
"ph" get "A" swap pluck-at >hashtable
] unit-test
[ H{ { "A" "B" } { "C" "D" } { "E" "F" } } ] [
"ph" get "F" "E" rot new-at >hashtable
] unit-test
[ H{ { "C" "D" } { "E" "F" } } ] [
"ph" get "F" "E" rot new-at "A" swap pluck-at >hashtable
] unit-test
: random-string ( -- str )
1000000 random ; ! [ CHAR: a CHAR: z [a,b] random ] "" replicate-as ;
: random-assocs ( -- hash phash )
[ random-string ] replicate
[ H{ } clone [ '[ swap , set-at ] each-index ] keep ]
[ PH{ } clone swap [ spin new-at ] each-index ]
bi ;
: ok? ( assoc1 assoc2 -- ? )
[ assoc= ] [ [ assoc-size ] bi@ = ] 2bi and ;
: test-persistent-hashtables-1 ( n -- )
random-assocs ok? ;
[ t ] [ 10 test-persistent-hashtables-1 ] unit-test
[ t ] [ 20 test-persistent-hashtables-1 ] unit-test
[ t ] [ 30 test-persistent-hashtables-1 ] unit-test
[ t ] [ 50 test-persistent-hashtables-1 ] unit-test
[ t ] [ 100 test-persistent-hashtables-1 ] unit-test
[ t ] [ 500 test-persistent-hashtables-1 ] unit-test
[ t ] [ 1000 test-persistent-hashtables-1 ] unit-test
[ t ] [ 5000 test-persistent-hashtables-1 ] unit-test
[ t ] [ 10000 test-persistent-hashtables-1 ] unit-test
[ t ] [ 50000 test-persistent-hashtables-1 ] unit-test
: test-persistent-hashtables-2 ( n -- )
random-assocs
dup keys [
[ nip over delete-at ] [ swap pluck-at nip ] 3bi
2dup ok?
] all? 2nip ;
[ t ] [ 6000 test-persistent-hashtables-2 ] unit-test

View File

@ -0,0 +1,48 @@
! Based on Clojure's PersistentHashMap by Rich Hickey.
USING: kernel math accessors assocs fry combinators parser
prettyprint.backend namespaces
persistent.assocs
persistent.hashtables.nodes
persistent.hashtables.nodes.empty
persistent.hashtables.nodes.leaf
persistent.hashtables.nodes.full
persistent.hashtables.nodes.bitmap
persistent.hashtables.nodes.collision ;
IN: persistent.hashtables
TUPLE: persistent-hash
{ root read-only initial: empty-node }
{ count fixnum read-only } ;
M: persistent-hash assoc-size count>> ;
M: persistent-hash at*
[ dup hashcode >fixnum ] [ root>> ] bi* (entry-at)
dup [ value>> t ] [ f ] if ;
M: persistent-hash new-at ( value key assoc -- assoc' )
[
{ [ 0 ] [ ] [ dup hashcode >fixnum ] [ root>> ] } spread
(new-at) 1 0 ?
] [ count>> ] bi +
persistent-hash boa ;
M: persistent-hash pluck-at
[ [ dup hashcode >fixnum ] [ root>> ] bi* (pluck-at) ] keep
{
{ [ 2dup root>> eq? ] [ nip ] }
{ [ over not ] [ 2drop T{ persistent-hash } ] }
[ count>> 1- persistent-hash boa ]
} cond ;
M: persistent-hash >alist [ root>> >alist% ] { } make ;
: >persistent-hash ( assoc -- phash )
T{ persistent-hash } swap [ spin new-at ] assoc-each ;
: PH{ \ } [ >persistent-hash ] parse-literal ; parsing
M: persistent-hash pprint-delims drop \ PH{ \ } ;
M: persistent-hash >pprint-sequence >alist ;

View File

@ -0,0 +1,86 @@
! Based on Clojure's PersistentHashMap by Rich Hickey.
USING: math math.bit-count arrays kernel accessors locals sequences
sequences.private sequences.lib
persistent.sequences
persistent.hashtables.config
persistent.hashtables.nodes ;
IN: persistent.hashtables.nodes.bitmap
: index ( bit bitmap -- n ) [ 1- ] dip bitand bit-count ; inline
M:: bitmap-node (entry-at) ( key hashcode bitmap-node -- entry )
[let* | shift [ bitmap-node shift>> ]
bit [ hashcode shift bitpos ]
bitmap [ bitmap-node bitmap>> ]
nodes [ bitmap-node nodes>> ] |
bitmap bit bitand 0 eq? [ f ] [
key hashcode
bit bitmap index nodes nth-unsafe
(entry-at)
] if
] ;
M:: bitmap-node (new-at) ( shift value key hashcode bitmap-node -- node' added-leaf )
[let* | shift [ bitmap-node shift>> ]
bit [ hashcode shift bitpos ]
bitmap [ bitmap-node bitmap>> ]
idx [ bit bitmap index ]
nodes [ bitmap-node nodes>> ] |
bitmap bit bitand 0 eq? [
[let | new-leaf [ value key hashcode <leaf-node> ] |
bitmap bit bitor
new-leaf idx nodes insert-nth
shift
<bitmap-node>
new-leaf
]
] [
[let | n [ idx nodes nth ] |
shift radix-bits + value key hashcode n (new-at)
[let | new-leaf [ ] n' [ ] |
n n' eq? [
bitmap-node
] [
bitmap
n' idx nodes new-nth
shift
<bitmap-node>
] if
new-leaf
]
]
] if
] ;
M:: bitmap-node (pluck-at) ( key hashcode bitmap-node -- node' )
[let | bit [ hashcode bitmap-node shift>> bitpos ]
bitmap [ bitmap-node bitmap>> ]
nodes [ bitmap-node nodes>> ]
shift [ bitmap-node shift>> ] |
bit bitmap bitand 0 eq? [ bitmap-node ] [
[let* | idx [ bit bitmap index ]
n [ idx nodes nth-unsafe ]
n' [ key hashcode n (pluck-at) ] |
n n' eq? [
bitmap-node
] [
n' [
bitmap
n' idx nodes new-nth
shift
<bitmap-node>
] [
bitmap bit eq? [ f ] [
bitmap bit bitnot bitand
idx nodes remove-nth
shift
<bitmap-node>
] if
] if
] if
]
] if
] ;
M: bitmap-node >alist% ( node -- ) nodes>> >alist-each% ;

View File

@ -0,0 +1,59 @@
! Based on Clojure's PersistentHashMap by Rich Hickey.
USING: kernel accessors math arrays fry sequences sequences.lib
locals persistent.sequences
persistent.hashtables.config
persistent.hashtables.nodes
persistent.hashtables.nodes.leaf ;
IN: persistent.hashtables.nodes.collision
: find-index ( key hashcode collision-node -- n leaf-node )
leaves>> -rot '[ , , _ matching-key? ] find ; inline
M:: collision-node (entry-at) ( key hashcode collision-node -- leaf-node )
key hashcode collision-node find-index nip ;
M:: collision-node (pluck-at) ( key hashcode collision-node -- leaf-node )
hashcode collision-node hashcode>> eq? [
[let | idx [ key hashcode collision-node find-index drop ] |
idx [
idx collision-node leaves>> smash [
collision-node hashcode>>
<collision-node>
] when
] [ collision-node ] if
]
] [ collision-node ] if ;
M:: collision-node (new-at) ( shift value key hashcode collision-node -- node' added-leaf )
hashcode collision-node hashcode>> eq? [
key hashcode collision-node find-index
[let | leaf-node [ ] idx [ ] |
idx [
value leaf-node value>> = [
collision-node f
] [
hashcode
value key hashcode <leaf-node>
idx
collision-node leaves>>
new-nth
<collision-node>
f
] if
] [
[let | new-leaf-node [ value key hashcode <leaf-node> ] |
hashcode
collision-node leaves>>
new-leaf-node
suffix
<collision-node>
new-leaf-node
]
] if
]
] [
shift collision-node value key hashcode make-bitmap-node
] if ;
M: collision-node >alist% leaves>> >alist-each% ;

View File

@ -0,0 +1,15 @@
! Based on Clojure's PersistentHashMap by Rich Hickey.
USING: accessors kernel locals persistent.hashtables.nodes ;
IN: persistent.hashtables.nodes.empty
M: empty-node (entry-at) 3drop f ;
M: empty-node (pluck-at) 2nip ;
M:: empty-node (new-at) ( shift value key hashcode node -- node' added-leaf )
value key hashcode <leaf-node> dup ;
M: empty-node >alist% drop ;
M: empty-node hashcode>> drop 0 ;

View File

@ -0,0 +1,51 @@
! Based on Clojure's PersistentHashMap by Rich Hickey.
USING: math accessors kernel arrays sequences sequences.private
locals sequences.lib
persistent.sequences
persistent.hashtables.config
persistent.hashtables.nodes ;
IN: persistent.hashtables.nodes.full
M:: full-node (new-at) ( shift value key hashcode full-node -- node' added-leaf )
[let* | nodes [ full-node nodes>> ]
idx [ hashcode full-node shift>> mask ]
n [ idx nodes nth-unsafe ] |
shift radix-bits + value key hashcode n (new-at)
[let | new-leaf [ ] n' [ ] |
n n' eq? [
full-node
] [
n' idx nodes new-nth shift <full-node>
] if
new-leaf
]
] ;
M:: full-node (pluck-at) ( key hashcode full-node -- node' )
[let* | idx [ hashcode full-node shift>> mask ]
n [ idx full-node nodes>> nth ]
n' [ key hashcode n (pluck-at) ] |
n n' eq? [
full-node
] [
n' [
n' idx full-node nodes>> new-nth
full-node shift>>
<full-node>
] [
hashcode full-node shift>> bitpos bitnot full-bitmap-mask bitand
idx full-node nodes>> remove-nth
full-node shift>>
<bitmap-node>
] if
] if
] ;
M:: full-node (entry-at) ( key hashcode full-node -- node' )
key hashcode
hashcode full-node shift>> mask
full-node nodes>> nth-unsafe
(entry-at) ;
M: full-node >alist% nodes>> >alist-each% ;

View File

@ -0,0 +1,28 @@
! Based on Clojure's PersistentHashMap by Rich Hickey.
USING: kernel accessors locals math arrays namespaces
persistent.hashtables.config
persistent.hashtables.nodes ;
IN: persistent.hashtables.nodes.leaf
: matching-key? ( key hashcode leaf-node -- ? )
tuck hashcode>> eq? [ key>> = ] [ 2drop f ] if ; inline
M: leaf-node (entry-at) [ matching-key? ] keep and ;
M: leaf-node (pluck-at) [ matching-key? not ] keep and ;
M:: leaf-node (new-at) ( shift value key hashcode leaf-node -- node' added-leaf )
hashcode leaf-node hashcode>> eq? [
key leaf-node key>> = [
value leaf-node value>> =
[ leaf-node f ] [ value key hashcode <leaf-node> f ] if
] [
[let | new-leaf [ value key hashcode <leaf-node> ] |
hashcode leaf-node new-leaf 2array <collision-node>
new-leaf
]
] if
] [ shift leaf-node value key hashcode make-bitmap-node ] if ;
M: leaf-node >alist% [ key>> ] [ value>> ] bi 2array , ;

View File

@ -0,0 +1,64 @@
! Based on Clojure's PersistentHashMap by Rich Hickey.
USING: math arrays kernel sequences sequences.lib
accessors locals persistent.hashtables.config ;
IN: persistent.hashtables.nodes
SINGLETON: empty-node
TUPLE: leaf-node
{ value read-only }
{ key read-only }
{ hashcode fixnum read-only } ;
C: <leaf-node> leaf-node
TUPLE: collision-node
{ hashcode fixnum read-only }
{ leaves array read-only } ;
C: <collision-node> collision-node
TUPLE: full-node
{ nodes array read-only }
{ shift fixnum read-only }
{ hashcode fixnum read-only } ;
: <full-node> ( nodes shift -- node )
over first hashcode>> full-node boa ;
TUPLE: bitmap-node
{ bitmap fixnum read-only }
{ nodes array read-only }
{ shift fixnum read-only }
{ hashcode fixnum read-only } ;
: <bitmap-node> ( bitmap nodes shift -- node )
pick full-bitmap-mask =
[ <full-node> nip ]
[ over first hashcode>> bitmap-node boa ] if ;
GENERIC: (entry-at) ( key hashcode node -- entry )
GENERIC: (new-at) ( shift value key hashcode node -- node' added-leaf )
GENERIC: (pluck-at) ( key hashcode node -- node' )
GENERIC: >alist% ( node -- )
: >alist-each% ( nodes -- ) [ >alist% ] each ;
: mask ( hash shift -- n ) neg shift radix-mask bitand ; inline
: bitpos ( hash shift -- n ) mask 2^ ; inline
: smash ( idx seq -- seq/elt ? )
dup length 2 = [ [ 1 = ] dip first2 ? f ] [ remove-nth t ] if ; inline
:: make-bitmap-node ( shift branch value key hashcode -- node' added-leaf )
shift value key hashcode
branch hashcode>> shift bitpos
branch 1array
shift
<bitmap-node>
(new-at) ; inline

View File

@ -0,0 +1 @@
Persistent hashtables with O(1) insertion, removal and lookup

View File

@ -1,5 +1,5 @@
USING: help.syntax help.markup kernel arrays assocs ;
IN: persistent-heaps
IN: persistent.heaps
HELP: <persistent-heap>
{ $values { "heap" "a persistent heap" } }

View File

@ -1,5 +1,5 @@
USING: persistent-heaps tools.test ;
IN: persistent-heaps.tests
USING: persistent.heaps tools.test ;
IN: persistent.heaps.tests
: test-input
{ { "hello" 3 } { "goodbye" 2 } { "whatever" 5 }

View File

@ -1,6 +1,6 @@
USING: kernel accessors multi-methods locals combinators math arrays
assocs namespaces sequences ;
IN: persistent-heaps
IN: persistent.heaps
! These are minheaps
<PRIVATE

View File

@ -0,0 +1 @@
collections

View File

@ -0,0 +1 @@
Slava Pestov

View File

@ -0,0 +1,17 @@
IN: persistent.sequences
USING: help.markup help.syntax math sequences kernel ;
HELP: new-nth
{ $values { "val" object } { "i" integer } { "seq" sequence } { "seq'" sequence } }
{ $contract "Persistent analogue of " { $link set-nth } ". Outputs a new sequence with the " { $snippet "i" } "th element replaced by " { $snippet "val" } "." }
{ $notes "This operation runs in " { $snippet "O(log_32 n)" } " time on " { $link "persistent.vectors" } " and " { $snippet "O(n)" } " time on all other sequences." } ;
HELP: ppush
{ $values { "val" object } { "seq" sequence } { "seq'" sequence } }
{ $contract "Persistent analogue of " { $link push } ". Outputs a new sequence with all elements of " { $snippet "seq" } " together with " { $snippet "val" } " added at the end." }
{ $notes "This operation runs in amortized " { $snippet "O(1)" } " time on " { $link "persistent.vectors" } " and " { $snippet "O(n)" } " time on all other sequences." } ;
HELP: ppop
{ $values { "seq" sequence } { "seq'" sequence } }
{ $contract "Persistent analogue of " { $link pop* } ". Outputs a new sequence with all elements of " { $snippet "seq" } " except for the final element." }
{ $notes "This operation runs in amortized " { $snippet "O(1)" } " time on " { $link "persistent.vectors" } " and " { $snippet "O(n)" } " time on all other sequences." } ;

View File

@ -0,0 +1,16 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: sequences kernel ;
IN: persistent.sequences
GENERIC: ppush ( val seq -- seq' )
M: sequence ppush swap suffix ;
GENERIC: ppop ( seq -- seq' )
M: sequence ppop 1 head* ;
GENERIC: new-nth ( val i seq -- seq' )
M: sequence new-nth clone [ set-nth ] keep ;

View File

@ -0,0 +1 @@
Persistent sequence protocol

View File

@ -0,0 +1 @@
collections

View File

@ -0,0 +1 @@
Slava Pestov

View File

@ -0,0 +1 @@
Immutable vectors with O(log_32 n) random access, push, and pop

View File

@ -0,0 +1 @@
collections

View File

@ -1,21 +1,6 @@
USING: help.markup help.syntax kernel math sequences ;
IN: persistent-vectors
HELP: new-nth
{ $values { "val" object } { "i" integer } { "seq" sequence } { "seq'" sequence } }
{ $contract "Persistent analogue of " { $link set-nth } ". Outputs a new sequence with the " { $snippet "i" } "th element replaced by " { $snippet "val" } "." }
{ $notes "This operation runs in " { $snippet "O(log_32 n)" } " time on " { $link persistent-vector } " instances and " { $snippet "O(n)" } " time on all other sequences." } ;
HELP: ppush
{ $values { "val" object } { "seq" sequence } { "seq'" sequence } }
{ $contract "Persistent analogue of " { $link push } ". Outputs a new sequence with all elements of " { $snippet "seq" } " together with " { $snippet "val" } " added at the end." }
{ $notes "This operation runs in amortized " { $snippet "O(1)" } " time on " { $link persistent-vector } " instances and " { $snippet "O(n)" } " time on all other sequences." } ;
HELP: ppop
{ $values { "seq" sequence } { "seq'" sequence } }
{ $contract "Persistent analogue of " { $link pop* } ". Outputs a new sequence with all elements of " { $snippet "seq" } " except for the final element." }
{ $notes "This operation runs in amortized " { $snippet "O(1)" } " time on " { $link persistent-vector } " instances and " { $snippet "O(n)" } " time on all other sequences." } ;
HELP: PV{
{ $syntax "elements... }" }
{ $description "Parses a literal " { $link persistent-vector } "." } ;

View File

@ -1,6 +1,7 @@
IN: persistent-vectors.tests
USING: accessors tools.test persistent-vectors sequences kernel
arrays random namespaces vectors math math.order ;
USING: accessors tools.test persistent.vectors
persistent.sequences sequences kernel arrays random namespaces
vectors math math.order ;
\ new-nth must-infer
\ ppush must-infer

View File

@ -1,8 +1,9 @@
! Based on Clojure's PersistentVector by Rich Hickey.
USING: math accessors kernel sequences.private sequences arrays
combinators combinators.short-circuit parser prettyprint.backend ;
IN: persistent-vectors
combinators combinators.short-circuit parser prettyprint.backend
persistent.sequences ;
IN: persistent.vectors
<PRIVATE
@ -12,18 +13,6 @@ PRIVATE>
ERROR: empty-error pvec ;
GENERIC: ppush ( val seq -- seq' )
M: sequence ppush swap suffix ;
GENERIC: ppop ( seq -- seq' )
M: sequence ppop 1 head* ;
GENERIC: new-nth ( val i seq -- seq' )
M: sequence new-nth clone [ set-nth ] keep ;
TUPLE: persistent-vector
{ count fixnum }
{ root node initial: T{ node f { } 1 } }

View File

@ -212,7 +212,7 @@ HELP: bit?
HELP: log2
{ $values { "x" "a positive integer" } { "n" integer } }
{ $description "Outputs the largest integer " { $snippet "n" } " such that " { $snippet "2^n" } " is less than " { $snippet "x" } "." }
{ $description "Outputs the largest integer " { $snippet "n" } " such that " { $snippet "2^n" } " is less than or equal to " { $snippet "x" } "." }
{ $errors "Throws an error if " { $snippet "x" } " is zero or negative." } ;
HELP: 1+

View File

@ -0,0 +1,38 @@
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel math math.functions quotations words sequences
sequences.private combinators fry ;
IN: math.bit-count
<PRIVATE
DEFER: byte-bit-count
<<
\ byte-bit-count
256 [
0 swap [ [ 1+ ] when ] each-bit
] B{ } map-as '[ HEX: ff bitand , nth-unsafe ] define-inline
>>
GENERIC: (bit-count) ( x -- n )
M: fixnum (bit-count)
{
[ byte-bit-count ]
[ -8 shift byte-bit-count ]
[ -16 shift byte-bit-count ]
[ -24 shift byte-bit-count ]
} cleave + + + ;
M: bignum (bit-count)
dup 0 = [ drop 0 ] [
[ byte-bit-count ] [ -8 shift (bit-count) ] bi +
] if ;
PRIVATE>
: bit-count ( x -- n )
dup 0 >= [ (bit-count) ] [ bitnot (bit-count) ] if ; inline

View File

@ -1 +0,0 @@
Immutable vectors with O(log_32 n) random access and amortized O(1) push/pop

View File

@ -205,8 +205,11 @@ PRIVATE>
: nths ( seq indices -- seq' )
swap [ nth ] curry map ;
: remove-nth ( seq n -- seq' )
cut-slice rest-slice append ;
: remove-nth ( n seq -- seq' )
[ swap head-slice ] [ swap 1+ tail-slice ] 2bi append ;
: insert-nth ( elt n seq -- seq' )
swap cut-slice [ swap 1array ] dip 3append ;
: if-seq ( seq quot1 quot2 -- )
[ f like ] 2dip if* ; inline