///////////////////////////////////////////////////////////////////////////////
//                             Lisaac Library                                //
//                                                                           //
//                   LSIIT - ULP - CNRS - INRIA - FRANCE                     //
//                                                                           //
//   This program is free software: you can redistribute it and/or modify    //
//   it under the terms of the GNU General Public License as published by    //
//   the Free Software Foundation, either version 3 of the License, or       //
//   (at your option) any later version.                                     //
//                                                                           //
//   This program is distributed in the hope that it will be useful,         //
//   but WITHOUT ANY WARRANTY; without even the implied warranty of          //
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           //
//   GNU General Public License for more details.                            //
//                                                                           //
//   You should have received a copy of the GNU General Public License       //
//   along with this program.  If not, see <http://www.gnu.org/licenses/>.   //
//                                                                           //
//                     http://isaacproject.u-strasbg.fr/                     //
///////////////////////////////////////////////////////////////////////////////
Section Header
  
  + name      := HASHED_DICTIONARY[V,K];


  - copyright := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag";
  
  - comment   := " Associative memory.\
  \Values of type `V' are stored using Keys of type `K'.";
  
  // Efficient implementation of DICTIONARY using `hash_code' on keys.   
	      
Section Inherit
  
  - parent_simple_dictionary:Expanded SIMPLE_DICTIONARY[V,K];
  
Section Public
// HASHED_DICTIONARY
  
  + buckets:NATIVE_ARRAY[HASHED_DICTIONARY_NODE[V,K]];
  // The `buckets' storage area is the primary hash table of `capacity'
  // elements. To search some key, the first access is done in `buckets'
  // using the remainder of the division of the key `hash_code' by
  // `capacity'. In order to try to avoid clashes, `capacity' is always a
  // prime number (selected using HASH_TABLE_SIZE).
  
Section Public
  
  - default_size:INTEGER := 193;
  // Default size for the storage area in number of items.
  
  // Counting:
  
  + capacity:INTEGER;
  // Of the `buckets' storage area.
  
  + count:INTEGER;
  // Actual `count' of stored elements.
  
  //  
  // Basic access:
  //
  
  - has k:K :BOOLEAN <-
  // Is there a value currently associated with key `k'?
  ( + idx:INTEGER;
    + node:HASHED_DICTIONARY_NODE[V,K];
    
    idx := k.hash_code % capacity;
    node := buckets.item idx;    
    {(node = NULL) || {node.key == k}}.until_do {
      node := node.next;
    };
    node != NULL
  );
  
  - at k:K :V <-
  // Return the value associated to key `k'.
  // (See also `reference_at' if V is a reference type.)
  ( + idx:INTEGER;
    + node:HASHED_DICTIONARY_NODE[V,K];  
       
    idx := k.hash_code % capacity;
    node := buckets.item idx;    
    {node.key == k}.until_do {
      node := node.next;
    };
    node.item
  );
  
  - reference_at k:K :V <-
  // Return NULL or the value associated with key `k'. Actually, this 
  // feature is useful when the type of values (the type E) is a 
  // reference type, to avoid using `has' just followed by `at' to get 
  // the corresponding value.
  ( + idx:INTEGER;
    + node:HASHED_DICTIONARY_NODE[V,K];
    + result:V;
    
    idx := k.hash_code % capacity;
    node := buckets.item idx;
    {(node = NULL) ||{node.key == k}}.until_do {
      node := node.next;
    };
    (node != NULL).if {
      result := node.item;
    };        
    result
  );
  
  - fast_has k:K :BOOLEAN <-
  ( + idx:INTEGER; 
    + node:HASHED_DICTIONARY_NODE[V,K];
    
    idx := k.hash_code % capacity;
    node := buckets.item idx;
    {(node = NULL) || {node.key = k}}.until_do {
      node := node.next;
    };
    node != NULL
  );

  - fast_at k:K :V <-
  ( + idx:INTEGER; 
    + node:HASHED_DICTIONARY_NODE[V,K];
    
    idx := k.hash_code % capacity;
    node := buckets.item idx;
    {node.key = k}.until_do {
      node := node.next;
    };
    node.item
  );
  
  - fast_reference_at k:K :V <-
  // Return NULL or the value associated with key `k'. Actually, this 
  // feature is useful when the type of values (the type V) is a 
  // reference type, to avoid using `has' just followed by `at' to get 
  // the corresponding value.
  ( + idx:INTEGER;
    + node:HASHED_DICTIONARY_NODE[V,K];
    + result:V;
    
    idx := k.hash_code % capacity;
    node := buckets.item idx;
    {(node = NULL) || {node.key = k}}.until_do {
      node := node.next;
    };
    ( node != NULL ).if {
      result := node.item;
    };        
    result
  );
  
Section Public  

  - put v:V to k:K  <-
  // Change some existing entry or `add' the new one. If there is
  // as yet no key `k' in the dictionary, enter it with item `v'.
  // Otherwise overwrite the item associated with key `k'.
  ( + h, idx:INTEGER;
    + node:HASHED_DICTIONARY_NODE[V,K];
        
    h := k.hash_code;
    idx := h % capacity;
    node := buckets.item idx;
        
    {(node = NULL) || {node.key == k}}.until_do {
      node := node.next;
    };
    (node = NULL).if {
      (capacity = count).if {
	increase_capacity;
	idx := h % capacity;
      };
      node := new_node v to k next (buckets.item idx);
      buckets.put node to idx;
      count := count + 1;
      cache_user := -1;
    } else {
      node.set_item v;
    };    
  );

  - fast_put v:V to k:K  <-
  // Change some existing entry or `add' the new one. If there is
  // as yet no key `k' in the dictionary, enter it with item `v'.
  // Otherwise overwrite the item associated with key `k'.
  ( + h, idx:INTEGER;
    + node:HASHED_DICTIONARY_NODE[V,K];
        
    h := k.hash_code;
    idx := h % capacity;
    node := buckets.item idx;
        
    {(node = NULL) || {node.key = k}}.until_do {
      node := node.next;
    };
    (node = NULL).if {
      (capacity = count).if {
	increase_capacity;
	idx := h % capacity;
      };
      node := new_node v to k next (buckets.item idx);
      buckets.put node to idx;
      count := count + 1;
      cache_user := -1;
    } else {
      node.set_item v;
    };    
  );
    
  - add v:V to k:K <-
  // To add a new entry `k' with its associated value `v'. Actually, this
  // is equivalent to call `put', but may run a little bit faster.
  ( + idx:INTEGER;
    + node:HASHED_DICTIONARY_NODE[V,K];
       
    cache_user := -1;
    (capacity = count ).if {
      increase_capacity;
    };
    idx := k.hash_code % capacity;
    node:= new_node v to k next (buckets.item idx);
    buckets.put node to idx;
    count := count + 1;    
  );
  
  //
  // Removing:
  //
  
  - remove k:K <-
  // Remove entry `k' (which may exist or not before this call).
  ( + h, idx:INTEGER;
    + node, previous_node:HASHED_DICTIONARY_NODE[V,K]; 
    
    cache_user := -1;
    h := k.hash_code;
    idx := h % capacity;
    node := buckets.item idx;
    (node != NULL ).if {
      (node.key == k ).if {
	count := count - 1;
	node := node.next;
	buckets.put node to idx;
      } else {
	previous_node := node;
	node := node.next;
	{(node = NULL) || {node.key == k}}.until_do {
	  previous_node := node;
	  node := node.next;
	};
	(node != NULL ).if {
	  count := count - 1;
	  previous_node.set_next (node.next);
	};
      };
    };    
  );
  
  - fast_remove k:K <-
  ( + h, idx:INTEGER; 
    + node, previous_node:HASHED_DICTIONARY_NODE[V,K];
		
    cache_user := -1;
    h := k.hash_code;
    idx := h % capacity;
    node := buckets.item idx;
    (node != NULL).if {
      (node.key = k).if {
	count := count - 1;
	node := dispose_node node;
	buckets.put node to idx;
      } else {					
	previous_node := node;
	node := node.next;	
	{(node = NULL) || {node.key = k}}.until_do {	  
	  previous_node := node;
	  node := node.next;
	};
	(node != NULL).if {
	  count := count - 1;
	  previous_node.set_next (dispose_node node);
	};
      };
    };
  );
  
  - clear <-
  // Discard all items.
  ( 
    cache_user := -1;
    count := 0;    
    buckets.set_all_with NULL until (capacity - 1);
  )
  [   
    +? {capacity = Old capacity};
  ];
  
  //
  // To provide iterating facilities:
  //
    
  - item i:INTEGER :V <-
  (    
    set_cache_user i;
    cache_node.item    
  );
    
  - key index:INTEGER :K <-
  (    
    set_cache_user index;
    cache_node.key    
  );
    
  - key_map_in buffer:COLLECTION[K] <-
  // Append in `buffer', all available keys (this may be useful to
  // speed up the traversal).
  ( + node:HASHED_DICTIONARY_NODE[V,K];
    + idx:INTEGER;

    node := buckets.item idx;
    count.downto 1 do { i:INTEGER;	    
      {node != NULL}.until_do {
	idx := idx + 1;
	? {idx < capacity};
	node := buckets.item idx;
      }; 
      buffer.add_last (node.key);
      node := node.next;
    }; 
  );
    
  - item_map_in buffer:COLLECTION[V]  <-
  // Append in `buffer', all available items (this may be useful to
  // speed up the traversal).
  ( + node:HASHED_DICTIONARY_NODE[V,K];
    + idx:INTEGER;

    node := buckets.item idx;
    count.downto 1 do { i:INTEGER;
      {node != NULL}.until_do {
	idx := idx + 1;
	? {idx < capacity};
	node := buckets.item idx;
      };
      buffer.add_last (node.item);
      node := node.next;
    };         
  );
    
  - copy other:SELF <-
  // Reinitialize by copying all associations of `other'.
  (    
    clear;
    (capacity < other.count).if {
      with_capacity (other.count + 1);
    }.elseif {capacity = 0} then {
      make;
    }; 
    
    1.to (other.count) do { i:INTEGER;
      put (other.item i) to (other.key i);
    }; 
  );
  
  //
  // Other features:
  //
  
  - internal_key k:K :K <-
  // Retrieve the internal key object which correspond to the existing
  // entry `k' (the one memorized into the `self' dictionary).
  ( + node:HASHED_DICTIONARY_NODE[V,K];
    + result:K;
        
    node := buckets.item (k.hash_code % capacity);
    result := node.key;
    {result == k}.until_do {
      node := node.next;
      result := node.key;
    }; 
    result
  );
  
Section Private
  
  - increase_capacity <-
  // There is no more free slots:the dictionary must grow.
  [ 
    -? {capacity = count};
  ]
  ( + idx, new_capacity,i:INTEGER;    
    + old_buckets:NATIVE_ARRAY[HASHED_DICTIONARY_NODE[V,K]];
    + node1, node2:HASHED_DICTIONARY_NODE[V,K];
    
    old_buckets  := buckets;
    new_capacity := HASH_TABLE_SIZE.prime_number_ceiling (capacity + 1);
    
    buckets := NATIVE_ARRAY[HASHED_DICTIONARY_NODE[V,K]].create new_capacity;
    i := capacity -1;
    capacity := new_capacity;
    {i < 0}.until_do {
      node1 := old_buckets.item i;
      {node1 = NULL}.until_do {
	node2 := node1.next;
	idx := node1.key.hash_code % capacity;
	node1.set_next (buckets.item idx);
	buckets.put node1 to idx;
	node1 := node2;
      }; 
      i := i - 1;
    }; 
    cache_user := -1;
  )
  [
    +? { count = Old count };
    +? { capacity > Old capacity };    
  ];
    
  - set_cache_user index:INTEGER <-
  // Set the internal memory cache (`cache_user', `cache_node' and
  // `cache_buckets') to the appropriate valid value.
  [
    -? { valid_index index };
  ]
  (    
    (index = cache_user + 1).if {
      cache_user := index;
      cache_node := cache_node.next;
      {cache_node != NULL}.until_do {
	cache_buckets := cache_buckets + 1;
	cache_node := buckets.item cache_buckets;
      }; 
    }.elseif {index = cache_user} then {
    }.elseif {index = 1} then {
      cache_user := 1;
      cache_buckets := 0;
      cache_node := buckets.item cache_buckets;
      {cache_node != NULL}.until_do {
	cache_buckets := cache_buckets + 1;
	cache_node := buckets.item cache_buckets;
      }; 
    } else {
      set_cache_user 1;
      {cache_user = index}.until_do {
	set_cache_user (cache_user + 1);
      }; 
    }; 
  )
  [ 
    ...
    +? {cache_user = index};
    +? {cache_buckets.in_range 0 to (capacity - 1)};
    +? {cache_node != NULL};
  ];
  
  + cache_user:INTEGER;
  // The last user's external index in range [1 .. `count'] (see `item'
  // and `valid_index' for example) may be saved in `cache_user' otherwise
  // -1 to indicate that the cache is not active. When the cache is
  // active, the corresponding index in `buckets' is save in
  // `cache_buckets' and the corresponding node in `cache_node'.

  + cache_node:HASHED_DICTIONARY_NODE[V, K];
  // Meaningful only when `cache_user' is not -1.

  + cache_buckets:INTEGER;
  // Meaningful only when `cache_user' is not -1.

  - dispose_node node:HASHED_DICTIONARY_NODE[V, K] :HASHED_DICTIONARY_NODE[V, K] <-
  // Add `node' in the `free_nodes' list.
  [
    -? {node != NULL};
  ]
  ( + result:HASHED_DICTIONARY_NODE[V,K];
    result := node.next;
    //node.set_next (free_nodes.item)
    //free_nodes.set_item(node)
    result
  )
  [
    +? {Result = Old node.next};
  ];

  - new_node v:V to k:K next nxt:HASHED_DICTIONARY_NODE[V,K] :HASHED_DICTIONARY_NODE[V, K] <-
  // Create a new one.
  (
    HASHED_DICTIONARY_NODE[V,K].create v to k next nxt
  );
  
Section Public
  
  - create:SELF <-
  ( + result:SELF;
    result := clone;
    result.make;
    result
  );
  
  - make <-
  // Create an empty dictionary. Internal storage `capacity' of the
  // dictionary is initialized using the `Default_size' value. Then,
  // tuning of needed storage `capacity' is performed automatically
  // according to usage. if you are really sure that your dictionary
  // is always really bigger than `Default_size', you may consider to use
  // `with_capacity' to save some execution time.
  (
    with_capacity default_size;
  ) 
  [    
    +? {capacity = default_size};
  ];
    
  - with_capacity medium_size:INTEGER <-
  // May be used to save some execution time if one is sure that
  // storage size will rapidly become really bigger than `Default_size'.
  // When first `remove' occurs, storage size may naturally become
  // smaller than `medium_size'. Afterall, tuning of storage size is
  // done automatically according to usage.
  [
    -? { medium_size > 0 };
  ]
  ( + new_capacity:INTEGER;
       
    new_capacity := HASH_TABLE_SIZE.prime_number_ceiling medium_size;
    buckets := NATIVE_ARRAY[HASHED_DICTIONARY_NODE[V,K]].create new_capacity;
    capacity := new_capacity;
    cache_user := -1;
    count := 0;
  )
  [
    +? { is_empty };
    +? { capacity >= medium_size };
  ];
		
  //
  // Invariant
  //
  
//  [
//    -? {capacity > 0};
//    -? {capacity >= count};
//    -? {cache_user.in_range (-1) to count};
//    -? {(cache_user > 0) ->> {cache_node != NULL}};
//    -? {(cache_user > 0) ->> {cache_buckets.in_range 0 to (capacity - 1)}}
//  ];
  
  
 //------------------- OLD -------------------- 
  
  /*

  + cache_user :INTEGER;
  // The last user's external index in range [1 .. `count'] (see `item'
  // and `valid_index' for example) may be saved in `cache_user' otherwise
  // -1 to indicate that the cache is not active. When the cache is
  // active, the corresponding index in `buckets' is save in
  // `cache_buckets' and the corresponding node in `cache_node'.
  
  + cache_node:HASHED_DICTIONARY_NODE[V,K];
  // Meaningful only when `cache_user' is not -1.
  
  + cache_buckets:INTEGER;
  // Meaningful only when `cache_user' is not -1.
  
  
  
  - is_empty:BOOLEAN <- count = 0;
  // Is it empty ?
  
  - fast_has k:K :BOOLEAN <-
  // Is there a value currently associated with key `k'?
  (
    + idx:INTEGER;
    + node:DICTIONARY_NODE[V,K];
    
    idx := k.hash_code % capacity;
    node := buckets.item idx;
    
    { (node = NULL) || { node.key = k}}.until_do {
      node := node.next;
    };
    node != NULL
  );
  
  - fast_at k:K :V <-
  // Return the value associated to key `k'.
  // (See also `reference_at' if V is a reference type.)
  (
    + idx:INTEGER;
    + node:DICTIONARY_NODE[V,K];  
    
    ? { fast_has k };
    
    idx := k.hash_code % capacity;
    node := buckets.item idx;
    
    { node.key = k}.until_do {
      node := node.next;
    };
    node.item
  );
  
  
  - '@' Left 1 k:K :V <-
  (
    at k
  );
  
    
  // Looking and searching some value:
  
  
  - occurrences v:V :INTEGER <-
  // Number of occurrences using `equal'.
  // See also `fast_occurrences' to chose the apropriate one.
  (
    1.to count do { i:INTEGER;
      (safe_equal (v,(item i)) ).if {
	result := result + 1;
      }; // end if
    }; // end fo
    
    ? { result >= 0 };
    
    result
  );
  
  
  - fast_occurrences v:V :INTEGER <-
  // Number of occurrences using `='.
  // See also `occurrences' to chose the apropriate one.
  (
    + result:INTEGER;
    
    1.to count do { i:INTEGER;
      (v = item i ).if {
	result := result + 1;
      }; // end if
    }; // end do
    
    ? { result >= 0 };
    
    result
  );
  
  
  - key_at v:V :K <-
  // Retrieve the key used for value `v' using `equal' for comparison.
  (
    + i:INTEGER;
    + result:K;
    
    ? { occurrences v = 1};
    i := 1;
    {safe_equal (v,(item i))}.until_do {
      i := i + 1;
    };
    result := cache_node.key;
    
    ? { equal ((at result) ,v) };
    
    result
  );
  
  
  - fast_key_at v:V :K <-
  // Retrieve the key used for value `v' using `=' for comparison.
  (
    + i:INTEGER;         
    + result:K;
    
    ? { fast_occurrences v = 1 };
    i := 1;
    {v = item(i)}.until_do {
      i := i + 1;
    };
    result := cache_node.key;
    
    ? { at result = v };
    
    result
  );
  
  - upper :INTEGER <-
  (
    count
  );
  
  
  - valid_index index:INTEGER :BOOLEAN <-
  (
    + result:BOOLEAN;
    
    result := (1 <= index) && {index <= count};
    
    ? { result =  index.in_range lower to upper };
    
    result
  );
  
  
  - '=='  Right 60 other:SELF :BOOLEAN <-
  // do both dictionaries have the same set of associations?
  // Keys are compared with `==' and values are comnpared
  // with the basic = operator. See also `is_equal_map'.
  (
    + i:INTEGER;
    + result:BOOLEAN;
    
    (Self = other ).if {
      result := TRUE;
    }.elseif { count = other.count } then {
      result := TRUE;
      i := 1;
      {!result || {i > count}}.until_do {
	(other.has (key i) ).if {
	  (other.at (key i) != item i ).if {
	    result := FALSE;
	  } else {
	    i := i + 1;
	  }; // end if
	} else {
	  result := FALSE;
	}; // end if
      }; // end until_do
    }; // end if
    
    ? { result ->> {count = other.count} };
    
    result
  );
  
  
  - is_equal_map other:SELF :BOOLEAN <-
  // Do both dictionaries have the same set of associations?
  // Both keys and values are compared with `=='. See also `=='.
  (
    + i:INTEGER;
    + k:K; 
    + result:BOOLEAN;
    (Self = other ).if {
      result := TRUE;
    }.elseif { count = other.count } then {
      result := TRUE;
      i := 1;
      {! result || {i > count}}.until_do {
	k := key i;
	(other.has k ).if {
	  (! safe_equal ((other.at k),(item i)) ).if {
	    result := FALSE;
	  } else {
	    i := i + 1;
	  }; // end if
	} else {
	  result := FALSE;
	}; // end if
      }; // end until_do
    }; // end if
    
    result
  );
*/  
