///////////////////////////////////////////////////////////////////////////////
//                             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_SET[V];


  - copyright   := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag";
  
  - comment := "Definition of a mathematical set of hashable objects.";
  
  // All common operations on mathematical sets are available.
    
Section Inherit
  
  + parent_set:Expanded SET[V];
  
Section Public
  
  - default_size:INTEGER := 53;
  // Minimum size for storage in number of items.
  
Section SET
  
  + buckets:NATIVE_ARRAY[HASHED_SET_NODE[V]];
  // The `buckets' storage area is the primary hash table of `capacity'
  // elements. To search some element, 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).
  
  //
  // Internal cache handling:
  //
  
  + 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_SET_NODE[V];
  // Meaningful only when `cache_user' is not -1.
  
  + cache_buckets:INTEGER;
  // Meaningful only when `cache_user' is not -1.
    
Section Public
  
  - create:SELF <-
  // Create an empty set. Internal storage `capacity' of the set is
  // initialized using the `Default_size' value. Then, tuning of needed
  // storage size is done automatically according to usage. If you
  // are really sure that your set is always really bigger than
  // `Default_size', you may use `with_capacity' to save some execution time.
  ( + result:SELF;
    result := clone;
    result.make;
    result
  );
  
  - make <-  
  (    
    with_capacity default_size;
  )
  [ ...
    +? { capacity = default_size };
  ];
  
  - create_with_capacity sz:INTEGER :SELF <-
  // Create an empty set using `medium_size' as an appropriate value
  // to help initialization of `capacity'. Thus, this feature may be used
  // in place of `make' to save some execution time if one is sure that
  // storage size will rapidly become really bigger than `Default_size' (if
  // not sure, simply use `make'). Anyway, the initial `medium_size' value
  // is just an indication and never a limit for the possible
  // `capacity'. Keep in mind that the `capacity' tuning is done
  // automatically according to usage.
  ( + result:SELF;
    
    result := clone;
    result.with_capacity sz; 
    result
  );
  
  - with_capacity medium_size:INTEGER <-
  [ ...
    -? {medium_size > 0};
  ]
  ( + new_capacity:INTEGER;
        
    new_capacity := HASH_TABLE_SIZE.prime_number_ceiling medium_size;
    buckets := NATIVE_ARRAY[HASHED_SET_NODE[V]].create new_capacity;
    capacity := new_capacity;
    cache_user := -1;
    count := 0;
  )
  [ ...
    +? { is_empty };
    +? { capacity >= medium_size };
  ];
  
  //
  // Counting:
  //
  
  + capacity:INTEGER; // Of the `buckets' storage area.
  
  + count:INTEGER;
    
  //
  // Adding and removing:
  //
  
  - add e:V <-
  // Add a new item to the set:the mathematical definition of
  // adding in a set is followed.
  ( + h, idx:INTEGER;
    + node:HASHED_SET_NODE[V];
            
    cache_user := -1;
    h := e.hash_code;
    idx := h % capacity;
    node := buckets.item idx;    
    {(node = NULL) || {node.item == e}}.until_do {
      node := node.next;
    };
    (node = NULL).if {
      (capacity = count).if {
	increase_capacity;
	idx := h % capacity;
      };
      node := HASHED_SET_NODE[V].create e next (buckets.item idx);
      buckets.put node to idx;
      count := count + 1;
    };
  );

  - fast_add e:V <-
  // Add a new item to the set:the mathematical definition of
  // adding in a set is followed.
  ( + h, idx:INTEGER;
    + node:HASHED_SET_NODE[V];
        
    cache_user := -1;
    h := e.hash_code;
    idx := h % capacity;
    node := buckets.item idx;    
    {(node = NULL) || {node.item = e}}.until_do {
      node := node.next;
    };
    (node = NULL).if {
      (capacity = count).if {
	increase_capacity;
	idx := h % capacity;
      };
      node := HASHED_SET_NODE[V].create e next (buckets.item idx);
      buckets.put node to idx;
      count := count + 1;
    };   
  );  
  	 
  - remove e:V <-
  // Remove item `e' from the set:the mathematical definition of
  // removing from a set is followed.
  ( + h, idx:INTEGER;
    + node, previous_node:HASHED_SET_NODE[V];
       
    cache_user := -1;
    h := e.hash_code;
    idx := h % capacity;
    node := buckets.item idx;
    (node != NULL).if {
      ( node.item == e).if {
	count := count - 1;
	node := node.next;
	buckets.put node to idx;
      } else {
	previous_node := node;
	node := node.next;
	{(node = NULL) || {node.item == e}}.until_do {
	  previous_node := node;
	  node := node.next;
	};
	(node != NULL).if {
	  count := count - 1;
	  previous_node.set_next (node.next);
	};
      };
    };    
  );
  
  - fast_remove e:V <-
  // Remove item `e' from the set:the mathematical definition of
  // removing from a set is followed.
  ( + h, idx:INTEGER;
    + node, previous_node:HASHED_SET_NODE[V];
       
    cache_user := -1;
    h := e.hash_code;
    idx := h % capacity;
    node := buckets.item idx;
    (node != NULL).if {
      (node.item = e).if {
	count := count - 1;
	node := node.next;
	buckets.put node to idx;
      } else {
	previous_node := node;
	node := node.next;
	{(node = NULL) || {node.item = e}}.until_do {
	  previous_node := node;
	  node := node.next;
	};
	(node != NULL).if {
	  count := count - 1;
	  previous_node.set_next (node.next);
	};
      };
    };    
  );
  
  - clear <-
  // Empty the current set.
  (
    cache_user := -1;
    count := 0;
    buckets.set_all_with NULL until (capacity - 1);
  )
  [ ...
    +? {capacity = Old capacity};
  ];
  
  //
  // Looking and searching:
  //
  
  - has e:V :BOOLEAN <-
  // Is element `e' in the set?
  ( + idx:INTEGER;
    + node:HASHED_SET_NODE[V];
        
    idx := e.hash_code % capacity;
    node := buckets.item idx;
    {(node = NULL) || {node.item == e}}.until_do {
      node := node.next;
    };
    node != NULL        
  );
  
  - fast_has e:V :BOOLEAN <-
  // Is element `e' in the set?
  ( + idx:INTEGER;
    + node:HASHED_SET_NODE[V];
        
    idx := e.hash_code % capacity;
    node := buckets.item idx;
    {(node = NULL) || {node.item = e}}.until_do {
      node := node.next;
    };
    node != NULL
  );
  
  - reference_at e:V :V <-
  ( + idx:INTEGER; 
    + node:HASHED_SET_NODE[V];
    + result:V;
				
    idx := e.hash_code % capacity;
    node := buckets.item idx;
    {(node = NULL) || {node.item == e}}.until_do {
      node := node.next;
    };
    (node != NULL).if {
      result := node.item;
    };
    result
  );
  
  //
  // To provide iterating facilities:
  //
    
  - item i:INTEGER :V <-
  // Return the item indexed by `index'.
  (         
    set_cache_user i;
    cache_node.item
  );  
  
  //
  // Mathematical operations:
  //
  
  - intersection other:SELF <-
  // Make the intersection of the `self' set with `other'.
  ( + i,c:INTEGER;
    + node1, node2:HASHED_SET_NODE[V];
        
    cache_user := -1;
    i := capacity - 1;
    c := count;
    {c = 0}.until_do {
      node1 := buckets.item i;
      {(node1 = NULL) || {other.has (node1.item)}}.until_do {
	node1 := node1.next;
	c := c - 1;
	buckets.put node1 to i;
	count := count - 1;
      };
      (node1 != NULL).if {
	node2 := node1.next;
	c := c - 1;
	{node2 = NULL}.until_do {
	  (! other.has (node2.item)).if {
	    node1.set_next (node2.next);
	    count := count - 1;
	  } else { 
	    node1 := node2;
	  };	  
	  node2 := node2.next;
	  c := c - 1;
	};
      };
      i := i - 1;
    };    
  );
  
  - copy other:SELF <-
  // Copy 'other' into the current set
  (
    // Note:this is a naive implementation because we should
    // recycle already allocated nodes of `self'.
    ( capacity = 0 ).if {
      with_capacity (other.count + 1);
    } else {
      clear;
    };
    
    lower.to (other.count) do { i:INTEGER;
      add (other.item i);
    };
  );
  
  - from_collection model:COLLECTION[V] <-
  ( + i, up:INTEGER;
				
    with_capacity (model.count.max 1);
    up := model.upper;
    i := model.lower;
    {i > up}.until_do {
      add (model.item i);
      i := i + 1;
    };
  );
  
Section Private  
  
  - increase_capacity <-
  // There is no more free slots:the set must grow.
  ( + i,idx, new_capacity:INTEGER;
    + old_buckets:NATIVE_ARRAY[HASHED_SET_NODE[V]];
    + node1, node2:HASHED_SET_NODE[V];
    
    new_capacity := HASH_TABLE_SIZE.prime_number_ceiling (capacity + 1);
    old_buckets := buckets;
    buckets := buckets.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.item.hash_code % capacity;
	node1.set_next (buckets.item idx);
	buckets.put node1 to idx;
	node1 := node2;
      };
      i := i - 1;
    };
    cache_user := -1;    
  );
  
  - set_cache_user index:INTEGER <-
  (      
    (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};
  );
  
  //
  // 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-----------------  
  
  /*
  
  
  
  - union other:SELF <-
  // Make the union of the `self' set with `other'.
  ( + e:V;
    + old_count:INTEGER;
    
    ? {old_count := count; 
       other != NULL};
    
    lower.to (other.count) do { i:INTEGER;
      e := other.item i;
      add e;
    };
    ? { count <= (old_count + other.count)};
  );

  - fast_union other:SELF <-
  // Make the union of the `self' set with `other'.
  ( + old_count:INTEGER;
    + e:V;
    ? {old_count := count; 
       other != NULL};
    
    lower.to (other.count) do { i:INTEGER;
      e := other.item i;
      fast_add e;
    };
    ? { count <= (old_count + other.count)};
  );
  
  
  - '+' Left 1 other:SELF :SELF <-
  // Return the union of the `self' set with `other'.
  (
    + result:SELF;
    
    ? { other != NULL};
    
    result := twin ;
    result.union other ;
    
    ? { result.count <= count + other.count};
    ? { (Self.is_subset_of result) && (other.is_subset_of result)};
    
    result
  );
  
  
  
  - is_disjoint other:SELF :BOOLEAN <-
  ( + j:INTEGER;
    + result:BOOLEAN;
     
    (other.count<count).if {
      result := other.is_disjoint Self;
    } else {
      j:=upper;
      {(j<lower) || {other.has (item j)}}.until_do {
	j := j-1;
      };
      result := (j=0);
    };
    result
  ); 

  - fast_is_disjoint other:SELF :BOOLEAN <-
  ( + j:INTEGER;
    + result:BOOLEAN;
     
    (other.count<count).if {
      result := other.fast_is_disjoint Self;
    } else {
      j:=upper;
      {(j<lower) || {other.fast_has (item j)}}.until_do {
	j := j-1;
      };
      result := (j=0);
    };
    result
  );
 
  - '^' other:SELF :SELF <-
  // Return the intersection of the `self' set with `other'.
  (
    + result:SELF;
    ? {other != NULL};
    
    result := twin;
    result.intersection(other) ;
    
    ? { result.count <= other.count.min count};
    ? { (result.is_subset_of Self) && { result.is_subset_of other}};
  );
  
  - minus other:SELF <-
  // Make the set `self' - `other'.
  (
    + old_count:INTEGER;
    
    ? { other != NULL};
    
    old_count:=count;
    
    lower.to (other.count) do { i:INTEGER;
      remove (other.item i);
    };
    
    ? { count <= old count};
  );
  
  
  - '-' other:SELF :SELF <-
  // Return  the set `self' - `other'.
  (
    + result:SELF;
    
    ? { other != NULL};
    
    result := twin ;
    result.minus other;
    
    ? { result.count <= count};
    ? { result.is_subset_of(Self)};
    
    result
  );
  
  // Comparison:
  
  - is_subset_of other:SELF :BOOLEAN <-
  // Is the `self' set a subset of `other'?
  (
    + i:INTEGER;
    + result:BOOLEAN;
    
    ? { other != NULL};
    
    result := FALSE;
    (Self = other).if {
      result := TRUE;
    }.elseif { count <= other.count} then {
      result := TRUE;
      i := 1;
      {! result || {i > count}}.until_do {
	result := other.has (item i);
	i := i + 1;
      };
    };
    ? { result ->> {count <= other.count}};
    
    result
  );
  
  
  - is_disjoint_from other:SELF :BOOLEAN <-
  // Is the `self' set disjoint from `other' ?
  (
    + result:BOOLEAN;
    + i:INTEGER;
    
    ? { other != NULL};
    
    result := FALSE;
    
    ( Self != other).if {
      result := TRUE;
      i := 1;
      ( count <= other.count).if {
	{! result || {i > count}}.until_do {
	  result := ! other.has (item i);
	  i := i + 1;
	};
      } else {
	{! result || {i > other.count}}.until_do {
	  result := ! has (other.item i);
	  i := i + 1;
	};
      };
    };
    ? { result = (Self ^ other).is_empty };
    
    result
  );
  
  
  - '=='  Right 60 other:SELF :BOOLEAN <-
  // Is the `self' set equal to `other'?
  (
    + i:INTEGER;
    + result:BOOLEAN;
    
    result := FALSE;
    ( Self = other ).if {
      result := TRUE;
    }.elseif { count = other.count} then {
      result := TRUE;
      i := 1;
      { ! result || {i > count}}.until_do {
	result := other.fast_has (item i);
	i := i + 1;
      };
    };
    //ensure then
    //? { result = ((is_subset_of other) & (other.is_subset_of self))};
    
    result
  );
  
  

  - fast_copy other:SELF <-
  // Copy 'other' into the current set
  (
    // Note:this is a naive implementation because we should
    // recycle already allocated nodes of `self'.
    ( capacity = 0 ).if {
      with_capacity (other.count + 1);
    } else {
      clear;
    };
    
    lower.to (other.count) do { i:INTEGER;
      fast_add (other.item i);
    };
  );
  
  
  // NONE
  
    
  - force e:V <-  
  // Add a new item to the set: the mathematical definition of
  // adding in a set is followed.
  ( + h, idx: INTEGER;
    + node:HASHED_SET_NODE[V];
    ? {! fast_has e};
    h := e.hash_code;
    cache_user := -1;
    (capacity = count).if {
      increase_capacity;
    };
    idx := h % capacity;
    node := HASHED_SET_NODE[V].create e next (buckets.item idx);
    buckets.put node to idx;
    count := count + 1;
    ? {fast_has e};
  );
  
    - search e:V :V <-
  // Is element `e' in the set?
  ( + idx:INTEGER;
    + node:HASHED_SET_NODE[V];
    + result:V;
    ? { e != NULL };
    idx := e.hash_code % capacity;
    node := buckets.item idx;
    { (node = NULL) || {node.item == e}}.until_do {
      node := node.next;
    };
    (node !=NULL).if {
      result := node.item;
    };
    result
  );
  
  - get_if cmp:BLOCK with_hash h:INTEGER :V <-
  // Is element `e' in the set?
  ( + idx:INTEGER;
    + node:HASHED_SET_NODE[V];
    + result:V;

    idx := h % capacity;
    node := buckets.item idx;
    { (node = NULL) || {cmp.value (node.item)}}.until_do {
      node := node.next;
    };
    (node !=NULL).if {
      result := node.item;
    };
    result
  );

  - fast_search e:V :V <-
  // Is element `e' in the set?
  ( + idx:INTEGER;
    + node:HASHED_SET_NODE[V];
    + result:V;
    ? { e != NULL };
    idx := e.hash_code % capacity;
    node := buckets.item idx;
    { (node = NULL) || {node.item = e}}.until_do {
      node := node.next;
    };
    (node !=NULL).if {
      result := node.item;
    };
    result
  );
  
  - fast_index_of elt:V :INTEGER <-
  ( + result:INTEGER;
    
    result := upper;
    {(result<lower) || {item result = elt}}.until_do {
      result := result - 1;
    };
    result
  );
*/