/utilset.txt
\ Hash based lists (ala Smalltalk Set's)                             vandys

\ Arbitrary cell values can be inserted in a hashed list, and their
\  presence tested for efficiently using a hashing of the value.
\  The list will grow as needed to accomodate its contents
\  It is benign to add something more than once; it will exist
\  in the list only once.
\ Instance variables hold a list of values, the overall size of the list,
\  and a count of the number of values in the list (0 is not a legal value).
\ A member in the Set
\  is located at its hash modulo current count, with collisions handled
\  by simply advancing by cells until a null entry is found.  It is
\  guaranteed that there will always be a null cell for this search.

only extensions definitions










\ Set--class, instance creation, and initialization                  vandys

Collection -> subclass: Set
   ivars:   intcell list   intcell count   intcell elems   endivars
Set -> class -> :method newsize ( size self -- set )
   super-> new { size set }   size set Set>count !
   size cells bkzalloc set Set>list !   set method;
Set -> class -> :method new ( self -- set )
   (#INITHASH) swap -> newsize method;
Set -> :method empty! { self -- }
   self Set>list @   self Set>count @ cells   erase
   self Set>elems off   method;
Set -> :method free ( self -- )   dup Set>list @ bkfree
   super-> free   method;











\ Accessing members of Set                                           vandys

: set>hash ( set key -- u )   (>hash)   swap Set>count @ mod ; Local
Set -> :method in? { key self -- ? }   self key set>hash   dup { start }
   cells self Set>list @ +   self Set>count @   start do ( 'cell )
      @+   dup key = if   unloop 2drop true exit   then
      0= if   unloop drop false exit   then   loop drop
   self Set>list @   start 0 do ( 'cell )
      @+   dup key = if   unloop 2drop true exit   then
      0= if   unloop drop false exit   then   loop   1 abort" List full"
   method;

Set -> :method @ ( idx self -- val )   dup Set>list @   swap Set>count @
   0 do   ( idx 'cell )
      @+ if   swap ?dup 0= if   cell- @ unloop exit   then   1- swap then
   loop   1 abort" Out of bounds" method;









\ Hash based lists--storage, rehash                                  vandys

: setAdd ( new elem -- )   swap -> add ; scrLocal
Set -> :method swap { other self -- }
   self Set>list @   self Set>count @   self Set>elems @ { list count elems }
   other Set>list @   self Set>list !   other Set>count @   self Set>count !
    other Set>elems @   self Set>elems !
   list other Set>list !   count other Set>count !   elems other Set>elems !
   method;
Set -> :method grow { self -- }
   self Set>count @ (>newhash) Set -> newsize { new }
   new ['] setAdd self -> do   new self -> swap   new -> free method;













\ Hash based lists--storage, rehash                                  vandys

: upsize { set -- }   set Set>elems inc
   set Set>elems @   set Set>count @ = if   set -> grow   then ; scrLocal
Set -> :method add { key self -- }   key dup 0= abort" Zero key"
   self swap set>hash   dup { start }
   cells self Set>list @ +   self Set>count @   start do ( 'cell )
      @+   dup key = if   unloop 2drop exit   then
      0= if   cell- key swap !   unloop self upsize exit   then   loop drop
   self Set>list @   start 0 do
      @+   dup key = if   unloop 2drop exit   then
      0= if   cell- key swap !   unloop self upsize exit   then   loop
   1 abort" List full" method;












\ Hash based lists--removal                                          vandys

: fixups { set start -- }
   start cells set Set>list @ +   set Set>count @ start do
      dup @ ?dup 0= if   unloop drop exit   then
      ( 'cell key ) over off   set -> ! loop drop
   set Set>list @   start 0 do
      dup @ ?dup 0= if   unloop drop exit   then
      ( 'cell key ) over off   set -> ! loop ; scrLocal
Set -> :method remove { key self -- }   self key set>hash   dup { start }
   cells self Set>list @ +   self Set>count @ start do
      @+ dup key = if   drop cell- off  self i unloop fixups   exit then
      0= abort" Not in Set" loop drop
   self Set>list @   start 0 do
      @+ dup key = if   drop cell- off  self i unloop fixups   exit then
      0= abort" Not in Set" loop drop method;









\ Hash based lists--enumeration, display                             vandys

Set -> :method do { arg 'fn self -- }   self Set>elems @   self Set>list @
   ( count 'cell ) begin over while
      @+ ?dup if   arg swap 'fn execute   swap 1- swap   then
   repeat   2drop method;

Set -> :method size ( set -- u )   Set>elems @ method;

















\ Hash based lists--testing                                          vandys
false [if] \ testing
Set -> new constant s
500 constant #ITERS
create vals   #ITERS cells allot

: vrfy   #ITERS 0 do   vals i cells + @ ?dup if
      s -> in? not abort" Missing"   then   loop ;
: init   randstate off   vals #ITERS cells erase ;
: duped? ( n -- ? )   #ITERS 0 do   vals i cells + @ over = if
      unloop drop true exit   then loop   drop false ;
: nextrand ( -- n )
   begin   random dup duped?   while   drop   repeat ;
: test1   init   #ITERS 0 do
      nextrand   dup s -> !   vals i cells + !   loop   vrfy ;
: test2   #ITERS 0 do   vals i cells + @   s -> !   loop   vrfy ;
: test3   #ITERS 5 / 0 do   random #ITERS mod cells vals +
   dup @ s -> remove   off   loop   vrfy ;
: (test4) ( magic val -- )   s -> in? not abort" bad iter"
   1234 - abort" bad magic" ;
: test4   1234 ['] (test4) s -> do ;
[then] \ testing



\ Hash based lists--duplication                                      vandys

Set -> :method copy { self -- new }
   self Set>count @ Set -> newsize { new } \ TBD: skip the bkzalloc of list
   self Set>list @   new Set>list @   self Set>count @ cells move
   self Set>elems @   new Set>elems !   new method;

Set -> :method ephem { self -- }   self Set>list @ ephem drop
   self super-> ephem method;