///////////////////////////////////////////////////////////////////////////////
//                             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 := SET[V];


  - copyright   := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag";
  
  - comment := "Definition of a mathematical set of objects.";

  // All common operations on mathematical sets are available.
  // Well knowned implementations are HASHED_SET and AVL_SET.
  
Section Inherit
  
  + parent_traversable:TRAVERSABLE[V] := TRAVERSABLE[V];
  
  + parent_safe_equal:SAFE_EQUAL[V] := SAFE_EQUAL[V];
  
Section Public
  //
  // Counting:
  //
  
  - count:INTEGER <-
  // Cardinality of the set (i.e. actual `count' of stored elements).
  (
    deferred;
    0
  );
	
  - is_empty:BOOLEAN <- count = 0;
  // Is the set empty?
  
  //
  // Adding and removing:
  //
  
  - add e:V <-
  // Add new item `e' to the set. The mathematical definition of adding 
  // in a set is followed, i.e. the element `e' is added only and only 
  // if it is not yet present in the set.
  // As this `add' feature is actually using `is_equal', you may consider 
  // to use `fast_add' for expanded objects as well while trying to get 
  // the very best performances.
  [ ...
    -? {e != NULL}; 
  ]
  (
    deferred;
  )
  [ ...
    "Added." +? {has e};
    "Not in then added." +? {(! Old has e) -> (count = Old count + 1)};
    "In then not added." +? {(Old has e) -> (count = Old count)};
  ];

  - fast_add e:V <-
  // Same job as `add', but uses basic `=' for comparison.
  [ ...
    -? {e != NULL}; 
  ]
  (
    deferred;
  )
  [ ...
    "Added." +? {fast_has e};
    "Not in then added." +? {(! Old fast_has e) -> (count = Old count + 1)};
    "In then not added." +? {(Old fast_has e) -> (count = Old count)};
  ];

  - remove e:V <-
  // Remove item `e' from the set: the mathematical definition of
  // removing from a set is followed.
  [ ...
    -? {e != NULL}; 
  ]
  (
    deferred;
  )
  [ ...
    "Removed." +? {! has e};
    "Not in not removed." +? {(! Old has e) -> (count = Old count)};
    "In then removed." +? {(Old has e) -> (count = Old count - 1)};
  ];

  - fast_remove e:V <-
  // Same job as `remove', but uses basic `=' for comparison.
  [ ...
    -? {e != NULL}; 
  ]
  (
    deferred;
  )
  [ ...
    "Removed." +? {! fast_has e};
    "Not in not removed." +? {(! Old fast_has e) -> (count = Old count)};
    "In then removed." +? {(Old fast_has e) -> (count = Old count - 1)};
  ];
  
  - clear <- clear_count;
  
  - clear_count <-
  // Empty the current set (`is_empty' is True after that call). 
  // If possible, the actual implementation is supposed to keep 
  // its internal storage area in order to refill `Current' in 
  // an efficient way.
  //
  // See also `clear_count_and_capacity' to select the most appropriate.
  (
    deferred;
  )
  [ ...
    "Is empty." +? {count = 0}; 
  ];

  - clear_count_and_capacity <-
  // Empty the current set (`is_empty' is True after that call). 
  // If possible, the actual implementation is supposed to release 
  // its internal storage area for this memory to be used by other objects.
  //
  // See also `clear_count' to select the most appropriate.
  (
    deferred;
  )
  [ ...
    "Is empty." +? {count = 0}; 
  ];
  
  //
  // Looking and searching:
  //
  
  - has e:V :BOOLEAN <-
  // Is element `e' in the set?
  // As this query is actually using `is_equal', you may consider to use 
  // `fast_has' for expanded objects as well while trying to get the very 
  // best performances.
  [ ...
    -? {e != NULL}; 
  ]
  (
    deferred;
    FALSE
  )
  [ ...
    +? {Result -> ! is_empty}; 
  ];
  
  - fast_has e:V :BOOLEAN <-
  // Is element `e' actually stored in the set?
  // Warning: this query is using basic `=' for comparison. 
  //
  // See also `has' when dealing with reference types.
  [ ...
    -? {e != NULL}; 
  ]
  (
    deferred;
  )
  [ ...
    //+? {Result ->> {e = reference_at e}}; // BSBS: Pb with '=='
  ];

  - reference_at e:V :V <-
  // Non Void when `e' is in the set. In such a situation, `Result' is the 
  // object which is actually stored in the `Current' set (see ensure assertion).
  [ ...
    -? {e != NULL};
    -? {! e.is_expanded_type};
  ]
  (
    deferred;
    NULL
  )
  [ ...
    +? {has e ->> {Result == e}}; 
  ];
  
  //
  // To provide iterating facilities:
  //
  
  - lower:INTEGER := 1;

  - upper:INTEGER <-
  ( count )
  [ ...
    +? {Result = count}; 
  ];

  - item i:INTEGER :V <-
  // Item at the corresponding index `i'.
  //
  // See also `lower', `upper', `valid_index'.
  //
  // SETs are intrinsically unordered, so there is no guarantee that 
  // `item'(i) after performing an `add' or `remove' operation is related 
  // in any way to `item'(i) before that operation.
  ( + result:V;
    
    deferred;
    result
  )
  [ ... 
    +? {fast_has Result}; 
  ]; 

  - first:V <- item lower;
  // The very `first' item.
  //
  // See also `last', `item'.
  //
  // SETs are intrinsically unordered, so there is no guarantee that 
  // `first' after performing an `add' or `remove' operation is related 
  // in any way to `first' before that operation.

  - last:V <- item upper;
  // The `last' item.
  //
  // See also `first', `item'.
  //
  // SETs are intrinsically unordered, so there is no guarantee that 
  // `last' after performing an `add' or `remove' operation is related 
  // in any way to `last' before that operation.
  
  //
  // Mathematical operations:
  //
  
  - union other:SELF <-
  // Make the union of the `Current' set with `other'.
  [ ...
    -? {other != NULL}; 
  ]
  ( + i:INTEGER;
    + e:V;
		
    i := 1;
    {i > other.count}.until_do {
      e := other.item i;
      (! has e).if {
	add e;
      };
      i := i + 1;
    };
  )
  [ ...
    +? {count <= Old count + other.count}; 
  ];
  
  - fast_union other:SELF <-
  // Make the union of the `Current' set with `other'.
  [ ...
    -? {other != NULL}; 
  ]
  ( + i:INTEGER;
    + e:V;
		
    i := 1;
    {i > other.count}.until_do {
      e := other.item i;
      (! fast_has e).if {
	fast_add e;
      };
      i := i + 1;
    };
  )
  [ ...
    +? {count <= Old count + other.count}; 
  ];
	
  - '+' other:SELF :SELF <-
  // Return the union of the `Current' set with `other'.
  [ ...
    -? {other != NULL}; 
  ]
  ( + result:SELF;
    
    result := twin;
    result.union other;
    result
  )
  [ ...
    +? {Result.count <= count + other.count};
    +? {(Self.is_subset_of Result) && {other.is_subset_of Result}};
  ];

  - intersection other:SELF <-
  // Make the intersection of the `Current' set with `other'.
  [ ...
    -? {other != NULL}; 
  ]
  ( + i:INTEGER; 
    + e:V;
    
    i := upper;
    {i < lower}.until_do {
      e := item i;
      (! other.has e).if {
	remove e;
      };
      i := i - 1;
    };
  )
  [ ...
    +? {count <= other.count.min (Old count)}; 
  ];
  
  - '^' other:SELF :SELF <-
  // Return the intersection of the `Current' set with `other'.
  [ ...
    -? {other != NULL}; 
  ]
  ( + result:SELF;
    
    result := twin;
    result.intersection other;
    result
  )
  [ ...
    +? {Result.count <= other.count.min count};
    +? {(Result.is_subset_of Self) && {Result.is_subset_of other}};
  ];

  - minus other:SELF <-
  // Make the set `Current' - `other'.
  [ ...
    -? {other != NULL}; 
  ]
  ( + i:INTEGER;
    
    (other = Self).if {
      clear_count;
    } else {
      i := 1;
      {i > other.count}.until_do {
	remove (other.item i);
	i := i + 1;
      };
    };
  )
  [ ...
    +? {count <= Old count}; 
  ];

  - '-' other:SELF :SELF <-
  // Return  the set `Current' - `other'.
  [ ...
    -? {other != NULL}; 
  ]
  ( + result:SELF;
    
    result := twin;
    result.minus other;
    result
  )
  [ ...
    +? {Result.count <= count};
    +? {Result.is_subset_of Self}; 
  ];

  //
  // Comparison:
  //
  
  - is_subset_of other:SELF :BOOLEAN <-
  // Is the `Current' set a subset of `other'?
  [ ...
    -? {other != NULL}; 
  ]
  ( + i:INTEGER;
    + result:BOOLEAN;
    
    (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
  )
  [ ...
    +? {Result -> (count <= other.count)}; 
  ];

  - is_disjoint_from other:SELF :BOOLEAN <-
  // Is the `Current' set disjoint from `other' ?
  [ ...
    -? {other != NULL}; 
  ]
  ( + i:INTEGER;
    + result:BOOLEAN;
    
    (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
  )
  [ ...
    +? {Result = (Self ^ other).is_empty}; 
  ];
  
  - '==' other:SELF :BOOLEAN <-
  // Is the `Current' set equal to `other'?
  ( + i:INTEGER;
    + result:BOOLEAN;
    
    (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
  )
  [ ...
    "Double inclusion." +? {Result = (is_subset_of other) & (other.is_subset_of Self)};
  ];

  //
  // Duplication.
  //
  
  - copy other:SELF <-
  // Copy 'other' into the current set
  ( + i:INTEGER;
    
    // Note: this naive implementation is OK because node
    // recycling is implemented.
    // BSBS: A revoir.
    make;
    i := 1;
    {i > other.count}.until_do {			
      add (other.item i);
      i := i + 1;
    };
  );

  - from_collection model:COLLECTION[V] <-
  // Add all items of `model'.
  [ ...
    -? {model != NULL}; 
  ]
  ( + i, up:INTEGER;
    
    make
    up := model.upper;
    i  := model.lower;
    {i > up}.until_do {
      add (model.item i);
      i := i + 1;
    };
  );

  //
  // Agents based features:
  //
  
  - do_all action:BLOCK <-
  // Apply `action' to every item of `Self'.
  //
  // See also `for_all', `exists'.
  ( + i:INTEGER;
    
    i := lower;
    {i > upper}.until_do {
      action.value (item i);
      i := i + 1;
    };
  );

  - for_all predicate:BLOCK :BOOLEAN <-
  // Do all items satisfy `predicate'?
  //
  // See also `do_all', `exists'.
  ( + i:INTEGER;
    + result:BOOLEAN;
    
    result := TRUE;
    i := lower;
    {(! result) || {i > upper}}.until_do {
      result := predicate.value (item i);
      i := i + 1;
    };
    result
  );

  - exists predicate:BLOCK :BOOLEAN <-
  // Does at least one item satisfy `predicate'?
  //
  // See also `do_all', `for_all'.
  ( + i:INTEGER;
    + result:BOOLEAN;
    
    i := lower;
    {result || {i > upper}}.until_do {
      result := predicate.value (item i);
      i := i + 1;
    };
    result
  );

  //
  // Creation.
  //
  
  - create:SELF <- 
  ( + result:SELF;
    
    result := clone;
    result.make;
    result
  );
  
  - make <-
  // Creation of an empty SET.
  (
    deferred;
  )
  [ ...
    +? {is_empty}; 
  ];

