   
6.9 DICTIONARY 
==============



   The data abstraction `dictionary' or `associative array' is ideally 
   suited for implementing  a one--one relation  between two sets. The 
   Eiffel/S class  DICTIONARY is implemented  with a  hash table using 
   closed addressing and is  in fact very similar  to the class HASH_ 
   TABLE.  It  is  not,  however,  a  container  class  in  the sense 
   described  earlier (it  does  not inherit  from  TABLE) and  has a 
   syntax and semantics more like that of ordinary arrays. 


   The class DICTIONARY does inherit from TRAVERSABLE and thus one can 
   have iterators  to traverse all  the elements in  a dictionary. For 
   this reason DICTIONARY has the functions iterator, inside, item, 
   and key  that all  tables have. This  means, though,  that the name 
   item is not available for the  function that accesses an element at 
   `index k' as with  arrays. For this reason  that function is called 
   `at' here. 

 class DICTIONARY [ G, K -> HASHABLE ] 

 make 
   -- The creation procedure 

 at (k  : K) :  G 
   --  Return the item  stored at  key `k'. 
   require 
      key_is_there : has (k) 

 count : INTEGER 

 has (k : K) : BOOLEAN 
   -- Is there an item currently associated 
   -- key 'k'? 

 inside (it : ITERATOR)  : BOOLEAN 
   -- Is  the given iterator still inside the container? 
   require 
      not_void : it /= void 

 item (it : ITERATOR) : G 
   --  The element at the position on which the
   -- iterator `it' is currently standing. 
   require 
      not_void : it /= void is_inside : inside (it) 


 iterator : ITERATOR 
   -- Returns an iterator object which is prepared to
   -- traverse the current  dictionary. The `first'  routine 
   -- of the iterator  has already been  called and  does not 
   --  need to be called explicitly unless one wants to 
   -- restart the traversal. 

 key (it : ITERATOR) :  K 
   -- The key at  the position on which the 
   -- iterator  `it' is currently  standing. 
   require 
      not_void : it /= void 
      is_inside : inside (it) 

 protected : BOOLEAN 
   -- True if and  only if there is at least one 
   -- iterator traversing the dictionary at the moment. 

 put (x :  G, k : K) 
   -- If there is  as yet no key  `k' in the 
   -- dictionary, enter it with item `x'. 
   -- Otherwise overwrite the item associated 
   -- with key `k'. 

 remove (k : K)
   -- If there is  an entry for key `k' 
   -- remove it. Otherwise the call has no effect. 

 set_default_size  (new_size  :  INTEGER) 
   --  Change  the default number of  buckets. 
   --  This procedure  will normally  be called 
   --  before putting any elements into the 
   -- dictionary. If it is called later, this  
   -- will cause  an automatic rehashing.  
   -- The default size is 32. 

 set_load_factor   (new_factor  :  REAL)
   -- The  "load  factor" determines the point at  
   -- which a resize  and rehash takes place.  This 
   -- happens  when `count' >  (load factor) *  (no. buckets).
   -- The default load_factor is 2.0. 

 set_shrinkable  (on_or_off :  BOOLEAN) 
   -- Should  the  number of buckets shrink again 
   -- when count gets small? Default is `off'. 

   The time complexity for DICTIONARY  is the same as for HASH_TABLE. 
   Here, however, we have control  over the load factor . The reader 
   may be  concerned that the  resizing and rehashing  that occur when 
   threatens to exceed its limit could adversely affect the complexity 
   calculation. The  usual arguments with  amortization show, however, 
   that at least asymptotically the cost of a put remains O(1) . 


   put : O(1) 
   remove : O(1) 
   has : O(1) 
   at : O(1) 


   The times given here are based  on the assumption of simple uniform 
   hashing.  For a  discussion  of the  worst  case (all  keys  in one 
   `bucket') see the section on HASH_TABLE. 


   The space complexity is as follows. 


   Space: O(count + slots) 


   The space complexity for an iterator is the following. 


   Space: O(1) 




6.10 CATALOG*
============= 


   The class DICTIONARY is useful for implementing a one--one relation 
   between two  sets of  objects. If  one needs  a many--one relation, 
   then CATALOG  is the  right choice  (strictly speaking:  one of its 
   effective   descendents).  A  CATALOG  has  a  similar  syntax  and 
   semantics to those of a  DICTIONARY; however, at (key) returns not 
   just a single item but rather an entire list of items. 


   In  fact CATALOG  is an  abstract  or deferred  class. Each  of its 
   descendents provides a different kind of list to be associated with 
   each key. The available choices are: LIST, SORTED_LIST, SHORT_ 
   LIST and SHORT_SORTED_LIST. 

 deferred class CATALOG [ G, K -> HASHABLE ] 
   
 make -- The creation procedure 
 
 add (x :  G, k : K) 
   -- If there is  as yet no key  `k' in the 
   -- catalog, enter it  and create a list
   -- with the  one item `x'.
   -- Otherwise add the item `x' to the list. 

 at (k : K) :  COLLECTION [ G ]
   --  Return the items stored at key `k'. 
   require 
      key_is_there : has (k) 

 count : INTEGER 
   -- The number of keys in the catalog. 

 has (k : K) : BOOLEAN 
   -- Are there items currently associated 
   -- key 'k'? 

 inside (it : ITERATOR)  : BOOLEAN 
   -- Is  the given iterator still inside the container? 
   require 
      not_void : it /= void 

 item (it :  ITERATOR) : COLLECTION [  G ]
   --  The elements at the position  on  which the  
   --  iterator `it'  is  currently standing. 
   require 
      not_void : it /= void 
      is_inside : inside (it) 

 iterator : ITERATOR 
   -- Returns an iterator object which is prepared to 
   -- traverse the  current catalog. The `first'  routine 
   -- of the iterator has already been called and  does not 
   -- need to be called explicitly unless one wants to
   -- restart the traversal. 

 key (it : ITERATOR) :  K 
   -- The key at  the position on which the 
   -- iterator `it' is currently standing. 
   require 
      not_void : it /= void 
      is_inside : inside (it) 

 protected : BOOLEAN 
   -- True if and  only if there is at least one 
   -- iterator traversing the dictionary at the moment. 

 remove (k : K) 
   -- If there is  an entry for key `k' and `x'
   -- is in its list, remove it. Otherwise the
   -- call has no effect. 

 set_default_size  (new_size  :  INTEGER)
   --  Change  the default number of  buckets.
   --  This procedure  will normally  be called 
   --  before putting any  elements into the 
   -- catalog. If  it is called later, this
   -- will cause  an automatic rehashing. 
   -- The default size is 32. 

 set_load_factor (new_factor :  REAL)
   --  The  "load  factor" determines the point at
   -- which a resize  and rehash takes place. This 
   -- happens  when `count' >  (load factor) *  (no. buckets).
   -- `new_factor' should be < 1.0; factors >= 1.0
   -- are simply ignored. 
   -- The default load_factor 0.75. 

Complexity:

   Space: O(n + slots) ,  where n is the total  number of items in the 
   catalog (remember that for catalogs count  is the number of keys in 
   the catalog). 


6.10.1 LIST_CATALOG 
===================

   Time: 
     O(n) amort. O(1) 
     remove : O(n) 
     has : O(1) 
     at : O(1) 


   Here n is  the number of  elements in the  list associated with the 
   given key. 


6.10.2 SORTED_CATALOG 
=====================

   Time: 
     add : O(lg (n)) 
     remove : O(lg (n)) 
     has : O(1) 
     at : O(1) 

   Here n is  the number of  elements in the  list associated with the 
   given key. 


6.10.3 SHORT_CATALOG 
====================

   Time: 
     add : O(n) amort. O(1) 
     remove : O(n) 
     has : O(1) 
     at : O(1) 

   Here n is  the number of  elements in the  list associated with the 
   given key. 


6.10.4 SHORT_SORTED_CATALOG 
===========================

   Time: 
     add : O(n) 
     remove : O(n) 
     has : O(1) 
     at : O(1) 


   Here n is  the number of  elements in the  list associated with the 
   given key. 


   

6.11 STACK 
==========

   This  is  the  first  of  the  container  classes  with  restricted 
   semantics as explained at the beginning of this chapter. 

   Stacks are  implemented using dynamic  arrays. For  a discussion of 
   the properties  of dynamic arrays  see the discussion  of the class 
   LIST. In  particular in  considering the  costs of  add and remove 
   below one should pay attention to the amortized time rather than to 
   the worst case time. 

 class STACK [ G ] 

 make 

 add (x : G) 
   -- Push the element `x' onto the top of the stack. 

 count : INTEGER 
   -- The number of elements on the stack. 

 empty : BOOLEAN 
   -- This is a convenience function. 
 
 item : G 
   -- The element at the top of the stack. 
   require 
      not_empty : not empty 

 remove 
   -- Remove the element at the top of the stack. 
   require
      not_empty : not empty 

Complexity:

  Space: O(count) 

  Time: 
    add : O(count) amort. O(1) 
    item : O(1) 
    remove : O(count) amort. O(1) 



   
6.12 QUEUE 
==========

   This  is  the  second  of  the  container  classes  with restricted 
   semantics explained at the beginning of this chapter. 

   Queues are  implemented using dynamic  arrays. For  a discussion of 
   the properties  of dynamic arrays  see the discussion  of the class 
   LIST. In  particular in  considering the  costs of  add and remove 
   below one should pay attention to the amortized time rather than to 
   the worst case time. 

 class QUEUE [ G ] 

 make 

 add (x : G) 
   -- Append the element `x' to the end of the queue. 

 count : INTEGER 
   -- The number of elements in the queue. 
 
 empty : BOOLEAN 
   -- This is a convenience function. 
 
 item : G 
   -- The element at the front of the queue. 
   require 
      not_empty : not empty 

 remove 
   -- Remove the element at the front of the queue. 
   require 
      not_empty : not empty 

   Space: O(count) 
     Time: 

Complexity

   Space: O(count)
   
   Time:
     add : O((count) amort. O(1) 
     item : O(1) 
     remove : O(count) amort. O(1) 


   
6.13 PRIORITY_QUEUE 
===================

   This is one of the two kinds of priority queue. The elements in the 
   queue  are  `ordered'  according to  the  order  they  inherit from 
   COMPARABLE. 


   Priority  queues are  implemented using  dynamic arrays  and binary 
   heaps. For a discussion of the properties of dynamic arrays see the 
   discussion of  the class  LIST. In  particular in  considering the 
   costs  of add  and remove  below  one should  pay attention  to the 
   amortized time rather than to the worst case time. 

 class PRIORITY_QUEUE [ G -> COMPARABLE ] 

 make 

 add (x  : G)  
   -- Insert  `x' at  the appropriate  position in the queue. 

 count : INTEGER 
   -- The number of elements in the queue. 

 empty : BOOLEAN 
   -- This is a convenience function. 

 item : G  
   -- The element at  the front of the  queue. 
   -- No other element has a higher priority. 
   require
      not_empty : not empty 

 remove 
   -- Remove the element at the front of the queue. 
   require
      not_empty : not empty 


Complexity:

   Space: O(count) 

   Time: 
     add : O(count) amort. O(lg (count)) 
     item : O(1) 
     remove : O(count) amort. O(lg (count)) 


   
6.14 KEY_PRIORITY_QUEUE 
=======================

   This is the other of the  two kinds of priority queue. The elements 
   are inserted into the  queue together with a  key or priority which 
   inherits an order from COMPARABLE.  Their position in the queue is 
   determined by this key. 


   For a discussion of the  implementation see the previous section on 
   PRIORITY_QUEUE. 

 class KEY_PRIORITY_QUEUE [ K -> COMPARABLE, G ] 

 make 

 add (k : K, x : G)
   -- Insert the pair `k', `x' at the appropriate 
   -- position in the queue (determined by `k'). 

 count : INTEGER
   -- The number of elements in the queue. 

 empty : BOOLEAN 
   -- This is a convenience function. 

 item : G 
    -- The element at  the front of the  queue.
    -- No other element in the queue has a higher priority. 
    require 
       not_empty : not empty 

 key : K 
    -- The key at the  front of the queue. No other key
    -- in the queue is larger. 
    require
       not_empty : not empty 

 remove
    -- Remove the pair at the front of the queue. 
    require
       not_empty : not empty 

Complexity
   
    Space: O(count) 
    Time: 
      add : O(count) amort. O(lg (count)) 
      item : O(1) 
      remove : O(count) amort. O(lg (count)) 


   
6.15 COLLECTION* 
================

   COLLECTION is an abstract class  providing all features needed by a 
   general   collection.   All  implementations   of  the  abstraction 
   collection  should inherit  from COLLECTION  and make  the deferred 
   features effective. 


 class COLLECTION [ G ] 

 make (only_once  : BOOLEAN)
   --  If `only_once' is  true, then the new collection  will
   --  not allow  duplicates. Otherwise duplicate entries 
   -- are allowed. 

   add (x : G)
   -- If  the collection was created in non-unique mode, then
   -- the element `x' is unconditionally added to the
   -- contents of the collection. If  the list was created
   -- in unique mode, `x'is only added if  it was not yet in
   -- the collection. Otherwise a call `add (x)' is
   -- without effect. 
   require 
      clash : not protected 

 count : INTEGER 
   -- That's how many there are 

 empty : BOOLEAN 

 found  : BOOLEAN
    -- True  if and  only  if the  last call  to
    -- `search' was successful. 

 found_item : G
    -- The item  found by the  last call to `search'. The value
    -- is valid only if `found' = true. 

 inside (it : ITERATOR)  : BOOLEAN
    -- Is  the given iterator still inside the container? 
    require
       not_void : it /= void 

 item (it :  ITERATOR) : G
    --  The element at  the position on which the iterator 
    -- `it' is currently standing. 
    require
       not_void : it /= void
       is_inside : inside (it) 

 iterator  :  ITERATOR
    -- Returns  an  iterator  object  which is prepared to
    -- traverse the  current container. The `first' routine 
    -- of the iterator has already been  called and does not
    -- need to be called explicitly unless one wants to
    -- restart the traversal. 

 protected : BOOLEAN
    -- True if and  only if there is at least one 
    -- iterator traversing the container at the moment. 

 remove (x : G)
    -- If the element `x' is present in the list, then one
    -- reference to `x' is removed. 
    require 
       clash : not protected 

 search (x : G) 
   -- Look for  the element `x' in the collection. If at
   -- least one reference to `x'  is found there, set `found'
   -- to true and `found_item'  to the item found;
   -- otherwise set `found' to false. 




   
6.16 TABLE* 
===========


   TABLE  is an  abstract  class providing  all  features needed  by a 
   general table. All implementations  of the abstraction table should 
   inherit from TABLE and make the deferred features effective. 


 class TABLE [ K, G ] 

 make (only_once : BOOLEAN)
   -- If `only_once' is true, then the
   -- new table  will not  allow duplicate  keys.
   --  Otherwise duplicate entries are allowed. 

 add (x : G, k : K)
   -- If the table was created in non-unique
   --  mode then  the pair  `x', `k'  is unconditionally
   -- added  to the contents of the table. If the  table
   -- was created in unique mode, then `x', `k' is
   -- only added if  `k' was not yet in the table.
   -- Otherwise a call  `add (x, k)'  is without effect. 
   require 
      clash : not protected 

 count : INTEGER
   -- that's how many there are 

 empty : BOOLEAN 
 
 found  : BOOLEAN
   -- True  if and  only  if the  last call
   -- to `search' was successful. 

 found_item : G
   -- The item found by the last call to `search'.
   -- The value is valid only if `found' = true. 

 found_key : K
   -- The key found by the  last call to `search'.
   -- The value is valid only if `found' = true. 

 inside (it : ITERATOR)  : BOOLEAN
   -- Is  the given iterator still inside the container?
   require 
      not_void : it /= void 

 item (it :  ITERATOR) : G
   -- The element at  the position on which the
   -- iterator  `it' is currently standing.
   require
      not_void : it /= void
      is_inside : inside (it) 

 iterator  :  ITERATOR
    -- Returns  an  iterator  object  which is prepared to
    -- traverse the  current container. The `first' routine 
    -- of the iterator has already been  called and does not
    -- need to be called explicitly unless one wants to
    -- restart the traversal. 

 key (it : ITERATOR) :  K
    -- The key at  the position on which the 
    -- iterator  `it' is currently  standing.
    require
       not_void  : it /= void
       is_inside : inside (it) 

 protected : BOOLEAN
    -- True if and  only if there is at least one 
    -- iterator traversing the container at the moment. 

 remove (k : K) 
    -- If the  key `k' is present in the container,
    -- then one reference to `k' is removed.
    require
       clash : not protected 

 replace (x : G, k  : K)
    -- If an  entry for key k exists, replace item with x;
    -- otherwise make an  entry for k with item x.
    require 
       clash : not protected 

 search (k : K)
    -- Look for the  key `k' in the table. If at least one
    -- reference to `k'  is found  there, set `found'  to true;
    -- otherwise set `found' to false.
    -- If `found' = true, `found_item' and `found_key' are
    -- set correspondingly. 



6.17 SORTED_COLLECTION* 
=======================


   SORTED_COLLECTION  is an  abstract  class providing  all features 
   needed  by a  sorted collection.  It is  derived from  the abstract 
   classes   TWOWAY_TRAVERSABLE  and  COLLECTION  and  adds  no  new 
   features. All implementations of  the abstraction sorted collection 
   should  inherit  from  SORTED_COLLECTION  and  make  the deferred 
   features effective. 


6.18 SORT_TABLE* 
================

   SORT_TABLE is an abstract class  providing all features needed by 
   a sorted  table. It is  derived from the  abstract classes TWOWAY_ 
   TRAVERSABLE and TABLE and adds no new features. All implementations 
   of the  abstraction sorted  table should  inherit from SORT_TABLE 
   and make the deferred features effective. 




6.19 TRAVERSABLE* 
=================

   This is an abstract (deferred) class describing classes that can be 
   traversed by iterators. It has very few public features. 

 class TRAVERSABLE 

 inside (it : ITERATOR)  : BOOLEAN
   -- Is  the given iterator still inside the container? 
   require
      not_void : it /= void 

 iterator  :  ITERATOR
   -- Returns  an  iterator  object  which is prepared to
   -- traverse the  current container. The `first' routine 
   -- of the iterator has already been  called and does not
   -- need to be called explicitly unless one wants to 
   -- restart the traversal. 

 protected : BOOLEAN 
   -- True if and  only if there is at least one 
   -- iterator traversing the container at the moment. 




6.20 TWOWAY_TRAVERSABLE* 
========================

   This  too is  an abstract  (deferred)  class derived  directly from 
   TRAVERSABLE  and  providing  the  additional  machinery  needed  by 
   two--way iterators.  It has  no public features  of its  own but it 
   does redefine iterator to be of type TWOWAY_ITER. 



   
6.21 ITERATOR 
=============

   This  class  describes  the  most  general  sort  of  iterator  for 
   traversing any object conforming to TRAVERSABLE. It encapsulates a 
   sort of memory or recollection for keeping track of where it was in 
   the course of a traversal. We  shall use the term `cursor' to refer 
   to the  current position of  the iterator without  meaning to imply 
   that an iterator necessarily has an attribute cursor. 


 class ITERATOR 

 finished : BOOLEAN 

 first
   -- Go to first element of container. 
   ensure 
      finish_invariant : not inside implies finished 

 forth
   -- Go to next element of container. 
   require 
      still_inside : inside 
   ensure 
      finish_invariant : not inside implies finished 

 stop -- Make finished true. 



   To illustrate the use of iterators  we show how one would typically 
   implement a linear search through a TABLE. 


Example

   search (t : TABLE [ K, G ] , k : K) is 
     local
       it : ITERATOR 
     do
       from 
         it :=  t.iterator
       until  
         it.finished
       loop
         if k.is_equal (t.key (it)) then
           it.stop 
         else
           it.forth
         end
       end
 
       found := t.inside (it) 
       if found then
         found_item := t.item (it)
         found_key := t.key (it) 
       end
     end -- procedure


   Please  not  that it  is  important  to call  stop  when  ending an 
   iteration prematurely. Otherwise  the container remains `protected' 
   and later calls to add or remove will fail. 




6.22 TWOWAY_ITER 
================

   This  class  is derived  from  ITERATOR and  realizes  the two--way 
   iterators by implementing the routines last and back which a normal 
   iterator does not have. 

 class TWOWAY_ITER 

 back
   -- Go to previous element of traversable object. 
   require
     still_inside : inside 
   ensure
     finish_invariant : finished = not inside 

 last
   -- Go to last element of traversable object. 
   ensure
      finish_invariant : finished = not inside 




6.23 Creating new container classes 
===================================

   This section describes  how to make new  container classes that fit 
   smoothly into  the general  pattern of  the Eiffel/S  classes. This 
   discussion applies only to classes that fit one of the abstractions 
   collection, sorted  collection, table  or sorted  table. By letting 
   the new class inherit from one of the abstract classes COLLECTION, 
   SORTED_COLLECTION,  TABLE   or  SORT_TABLE   one   can  save 
   considerable work and guarantee that the new class behaves like the 
   existing ones. 


   One should follow the steps given below. 

1. Decide whether your  class is to be  an unsorted collection, sorted 
   collection etc.  and let it  inherit from  the appropriate abstract 
   class out of the list of four classes above. 

2. Decide how you  want to implement your  data structure: with linked 
   nodes of some kind, with dynamic  arrays, with some kind of tree or 
   some completely new kind of structure. 

3. Now you must provide effective implementations of the features that 
   are still deferred in the parent class. Begin with the two routines 
   add  and  remove.  Note  that  you  do  not  have  to  repeat  the 
   precondition not protected; it is automatically inherited from the 
   parent class. You  may also want  to provide a  redefinition of the 
   routine search, since  the default version  provided in the parent 
   class is not very  efficient. It is also  likely that you will need 
   to   redefine  the  creation  procedure  make  to  initialize  your 
   structures appropriately. 

4. The next step is  to decide what information  an iterator will have 
   to  store in  order  to remember  where  it is  in  your structure. 
   Typically  an iterator  for your  new container  class will  have a 
   attribute cursor of an appropriate  type. For example, if your data 
   structure uses dynamic arrays then the cursor can be an integer (an 
   index in the  array) and you  can use the  class INT_ITER already 
   available  in the  cluster CONTAINER  (resp. TWI_ITER  for sorted 
   containers).  If you  use linked  lists with  nodes, then  you will 
   probably need an  iterator type with  a cursor of  the type of your 
   nodes. 

   If a new kind of iterator  is needed, create the appropriate class, 
   letting it inherit from ITERATOR  or TWOWAY_ITER and providing it 
   with features cursor and  set_cursor. These features should only 
   be exported to  the class TRAVERSABLE. You  can use INT_ITER and 
   TWI_ITER as patterns for your new iterator class. 

5. If your  iterator class is  called MY_ITER  redefine the function 
   iterator in your container class to look as follows: 

   iterator : MY_ITER is
     do
       !!result.make (current)
     end 


6. Now you can implement the features 

   inside (it : ITERATOR) : BOOLEAN

   item (it : ITERATOR) : G 

   for your new class. They will typically have to extract the desired 
   information from it.cursor.  If you are building  a table you will 
   also need a function 

   key (it : ITERATOR) : K 

   that extracts the key from it.cursor. 

7. The  next  step  is to  provide  effective  implementations  of the 
   procedures 

   first (it : ITERATOR) 

   next (it : ITERATOR) 


   and for sorted containers also 

   last (it : ITERATOR)

   previous (it : ITERATOR) 


   that  use it.set_cursor  to move  the  cursor to  the appropriate 
   position. These routines are only exported to the class ITERATOR. 

8. Finally you will almost certainly  want to redefine the routines is 
   _equal and copy inherited  from GENERAL to do  the right thing for 
   your new class. It is recommended that you use the classes provided 
   in the Eiffel/S library as models for this step. 

     If you  are writing a  new implementation  for collections please 
   remember that collections guarantee that the iterator always visits 
   the elements in  the order they  were added: the  oldest first, the 
   youngest last. 


   A good warming up  exercise would be to  reimplement the class LIST 
   using  a singly  or  doubly linked  list.  Then you  can  run races 
   between your new class and the  Eiffel/S class LIST to see which is 
   faster. 

