-- This  file  is  free software,  which comes along with SmartEiffel.  This
-- software 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. You can modify it as you want, provided
-- this header is kept unaltered, and a notification of the changes is added.
-- You  are  allowed  to  redistribute it and sell it, alone or as a part of
-- another product.
--
-- Copyright(C) 1994-2002: INRIA - LORIA (INRIA Lorraine) - ESIAL U.H.P.
--			   - University of Nancy 1 - FRANCE
-- Copyright(C) 2003:      INRIA - LORIA (INRIA Lorraine) - I.U.T. Charlemagne
--			   - University of Nancy 2 - FRANCE
--
--		 Dominique COLNET, Suzanne COLLIN, Olivier ZENDRA,
--			   Philippe RIBET, Cyril ADRIAN
--
-- http://SmartEiffel.loria.fr - SmartEiffel@loria.fr
--
deferred class COLLECTION[E]
--
-- Common abstract definition of a sequenceable collection of objects.
-- Such a collection is traversable using a simple INTEGER index from `lower'
-- to `upper'. Items can be added, changed or removed.
--
-- The SmartEiffel standard library provides four implementations of
-- COLLECTION[E]: ARRAY[E], FAST_ARRAY[E], LINKED_LIST[E] and
-- TWO_WAY_LINKED_LIST[E]. All implementations have exactly the same
-- behavior. Switching from one implementation to another only change the
-- memory used and the execution time.
--

inherit SAFE_EQUAL[E] redefine copy, is_equal, fill_tagged_out_memory end

feature -- Indexing:

   lower: INTEGER is
         -- Minimum index.
      deferred
      end

   upper: INTEGER is
         -- Maximum index.
      deferred
      end

   frozen valid_index(index: INTEGER): BOOLEAN is
         -- True when `index' is valid (ie. inside actual
         -- bounds of the collection).
      do
         Result := lower <= index and then index <= upper
      ensure
         Result = (lower <= index and then index <= upper)
      end

feature -- Counting:

   count: INTEGER is
         -- Number of available indices.
      deferred
      ensure
         Result = upper - lower + 1
      end

   is_empty: BOOLEAN is
         -- Is collection empty ?
      deferred
      ensure
	 Result = (count = 0)
      end

feature -- Accessing:

   item, infix "@" (i: INTEGER): E is
         -- Item at the corresponding index `i'.
      require
         valid_index(i)
      deferred
      end

   first: like item is
         -- The very `first' item.
      require
         count >= 1
      deferred
      ensure
         Result = item(lower)
      end

   last: like item is
         -- The `last' item.
      require
         not is_empty
      deferred
      ensure
         Result = item(upper)
      end

feature -- Writing:

   put(element: like item; i: INTEGER) is
         -- Make `element' the item at index `i'.
      require
         valid_index(i)
      deferred
      ensure
         item(i) = element
         count = old count
      end

   swap(i1, i2: INTEGER) is
         -- Swap item at index `i1' with item at index `i2'.
      require
         valid_index(i1)
         valid_index(i2)
      local
         tmp: like item
      do
         tmp := item(i1)
         put(item(i2), i1)
         put(tmp, i2)
      ensure
         item(i1) = old item(i2)
         item(i2) = old item(i1)
         count = old count
      end

   set_all_with(v: like item) is
         -- Set all items with value `v'.
      deferred
      ensure
         count = old count
      end

   set_slice_with(v: like item; lower_index, upper_index: INTEGER) is
         -- Set all items in range [`lower_index' .. `upper_index'] with `v'.
      require
         lower_index <= upper_index
         valid_index(lower_index)
         valid_index(upper_index)
      local
         i: INTEGER
      do
         from
            i := lower_index
         until
            i > upper_index
         loop
            put(v, i)
            i := i + 1
         end
      ensure
         count = old count
      end

   clear_all is
         -- Set every item to its default value.
         -- The `count' is not affected (see also `clear').
      local
         value: like item
      do
         set_all_with(value)
      ensure
	 stable_upper: upper = old upper
	 stable_lower: lower = old lower
         all_default
      end

feature -- Adding:

   add_first(element: like item) is
         -- Add a new item in first position : `count' is increased by
         -- one and all other items are shifted right.
      deferred
      ensure
         first = element
         count = 1 + old count
         lower = old lower
         upper = 1 + old upper
      end

   add_last(element: like item) is
         -- Add a new item at the end : `count' is increased by one.
      deferred
      ensure
         last = element
         count = 1 + old count
         lower = old lower
         upper = 1 + old upper
      end

   add(element: like item; index: INTEGER) is
         -- Add a new `element' at rank `index' : `count' is increased
         -- by one and range [`index' .. `upper'] is shifted right
         -- by one position.
      require
         index.in_range(lower, upper + 1)
      deferred
      ensure
         item(index) = element
         count = 1 + old count
         upper = 1 + old upper
      end

   append_collection(other: COLLECTION[E]) is
         -- Append `other' to Current.
      require
         other /= Void
      local
         i: INTEGER
      do
         from
            i := other.lower
         until
            i > other.upper
         loop
            add_last(other.item(i))
            i := i + 1
         end
      ensure
         count = other.count + old count
      end

feature -- Modification:

   force(element: E; index: INTEGER) is
         -- Make `element' the item at `index', enlarging the collection if
         -- necessary (new bounds except `index' are initialized with
         -- default values).
      require
         index >= lower
      deferred
      ensure
         upper = index.max(old upper)
         item(index) = element
      end

   copy(other: like Current) is
	 -- Reinitialize by copying all the items of `other'.
      deferred
      end

   from_collection(model: COLLECTION[like item]) is
	 -- Initialize the current object with the contents of `model'.
      require
         model /= Void
      deferred
      ensure
         count = model.count
      end

feature -- Removing:

   remove_first is
         -- Remove the `first' element of the collection.
      require
         not is_empty
      deferred
      ensure
         count = (old count) - 1
         (lower = (old lower) + 1) xor (upper = (old upper) - 1)
      end

   remove(index: INTEGER) is
         -- Remove the item at position `index'. Followings items
         -- are shifted left by one position.
      require
         valid_index(index)
      deferred
      ensure
         count = (old count) - 1
         upper = (old upper) - 1
      end

   remove_last is
         -- Remove the `last' item.
      require
         not is_empty
      deferred
      ensure
         count = (old count) - 1
         upper = (old upper) - 1
      end

   clear is
         -- Discard all items in order to make it `is_empty'.
         -- See also `clear_all'.
      deferred
      ensure
         is_empty
      end

feature -- Looking and Searching:

   has(x: like item): BOOLEAN is
         -- Look for `x' using `equal' for comparison.
         -- Also consider `fast_has' to choose the most appropriate.
      do
         Result := valid_index(index_of(x))
      end

   fast_has(x: like item): BOOLEAN is
         -- Look for `x' using basic `=' for comparison.
         -- Also consider `has' to choose the most appropriate.
      do
         Result := valid_index(fast_index_of(x))
      end

   index_of(element: like item): INTEGER is
         -- Give the index of the first occurrence of `element' using
         -- `is_equal' for comparison.
         -- Answer `upper + 1' when `element' is not inside.
         -- Also consider `fast_index_of' to choose the most appropriate.
	 --
	 -- Note: we'll have to mimic what's done in the new ELKS STRING class
	 -- for `index_of' (ie. to add an extra argument). This is in the todo
	 -- list ... let people switch first to ELKS 2001 at time being.
      deferred
      ensure
         lower <= Result
         Result <= upper + 1
         Result <= upper implies safe_equal(element,item(Result))
      end

   fast_index_of(element: like item): INTEGER is
         -- Give the index of the first occurrence of `element' using
         -- basic `=' for comparison.
         -- Answer `upper + 1' when `element' is not inside.
         -- Also consider `index_of' to choose the most appropriate.
      deferred
      ensure
         lower <= Result
         Result <= upper + 1
         Result <= upper implies element = item(Result)
      end

feature -- Looking and comparison:

   is_equal(other: like Current): BOOLEAN is
	 -- Do both collections have the same `lower', `upper', and
	 -- items?
	 -- The basic `=' is used for comparison of items.
	 -- See also `is_equal_map'.
      deferred
      ensure then
	 Result implies (lower = other.lower and upper = other.upper)
      end

   is_equal_map(other: like Current): BOOLEAN is
	 -- Do both collections have the same `lower', `upper', and
	 -- items?
	 -- Feature `is_equal' is used for comparison of items.
	 -- See also `is_equal'.
      deferred
      ensure
	 Result implies (lower = other.lower and upper = other.upper)
      end

   all_default: BOOLEAN is
         -- Do all items have their type's default value?
	 -- Note: for non Void items, the test is performed with the 
	 -- `is_default' predicate.
      deferred
      end

   same_items(other: COLLECTION[E]): BOOLEAN is
	 -- Do both collections have the same items? The basic `=' is used
	 -- for comparison of items and indices are not considered (for
	 -- example this routine may yeld True with `Current' indexed in
	 -- range [1..2] and `other' indexed in range [2..3]).
      require
	 other /= Void
      local
	 i, j: INTEGER
      do
	 if count = other.count then
	    from
	       Result := True
	       i := lower
	       j := other.lower
	    until
	       not Result or else i > upper
	    loop
	       Result := item(i) = other.item(j)
	       i := i + 1
	       j := j + 1
	    end
	 end
      ensure
	 Result implies count = other.count
      end

   occurrences(element: like item): INTEGER is
         -- Number of occurrences of `element' using `equal' for comparison.
         -- Also consider `fast_occurrences' to choose the most appropriate.
      deferred
      ensure
         Result >= 0
      end

   fast_occurrences(element: like item): INTEGER is
         -- Number of occurrences of `element' using basic `=' for comparison.
         -- Also consider `occurrences' to choose the most appropriate.
      deferred
      ensure
         Result >= 0
      end

feature -- Printing:

   frozen fill_tagged_out_memory is
      local
         i: INTEGER; v: like item
      do
         tagged_out_memory.append(once "lower: ")
         lower.append_in(tagged_out_memory)
         tagged_out_memory.append(once " upper: ")
         upper.append_in(tagged_out_memory)
         tagged_out_memory.append(once " [")
         from
            i := lower
         until
            i > upper or else tagged_out_memory.count > 2048
         loop
            v := item(i)
            if v = Void then
               tagged_out_memory.append(once "Void")
            else
               v.out_in_tagged_out_memory
            end
            if i < upper then
               tagged_out_memory.extend(' ')
            end
            i := i + 1
         end
         if i <= upper then
            tagged_out_memory.append(once " ...")
         end
         tagged_out_memory.extend(']')
      end

feature -- Agents based features:

   do_all(action: ROUTINE[ANY,TUPLE[E]]) is
	 -- Apply `action' to every item of `Current'.
      local
	 i: INTEGER
      do
	 from i := lower until i > upper
	 loop
	    action.call([item(i)])
	    i := i + 1
	 end
      end

   for_all(test: PREDICATE[ANY,TUPLE[E]]): BOOLEAN is
	 -- Do all items satisfy `test'?
      local
	 i: INTEGER
      do
	 from
	    Result := True
	    i := lower
	 until
	    not Result or else i > upper
	 loop
	    Result := test.item([item(i)])
	    i := i + 1
	 end
      end

   exists(test: PREDICATE[ANY,TUPLE[E]]): BOOLEAN is
	 -- Does at least one item satisfy `test'?
      local
	 i: INTEGER
      do
	 from
	    i := lower
	 until
	    Result or else i > upper
	 loop
	    Result := test.item([item(i)])
	    i := i + 1
	 end
      end

feature -- Other features:

   get_new_iterator: ITERATOR[E] is
      deferred
      end

   replace_all(old_value, new_value: like item) is
         -- Replace all occurrences of the element `old_value' by `new_value'
         -- using `equal' for comparison.
         -- See also `fast_replace_all' to choose the apropriate one.
      deferred
      ensure
         count = old count
         occurrences(old_value) = 0
      end

   fast_replace_all(old_value, new_value: like item) is
         -- Replace all occurrences of the element `old_value' by `new_value'
         -- using operator `=' for comparison.
         -- See also `replace_all' to choose the apropriate one.
      deferred
      ensure
         count = old count
         fast_occurrences(old_value) = 0
      end

   move(lower_index, upper_index, distance: INTEGER) is
         -- Move range `lower_index' .. `upper_index' by `distance'
         -- positions. Negative distance moves towards lower indices.
         -- Free places get default values.
      require
         lower_index <= upper_index
         valid_index(lower_index)
         valid_index(lower_index + distance)
         valid_index(upper_index)
         valid_index(upper_index + distance)
      local
         default_value: like item
         i: INTEGER
      do
         if distance = 0 then
         elseif distance < 0 then
            from
               i := lower_index
            until
               i > upper_index
            loop
               put(item(i),i + distance)
               put(default_value,i)
               i := i + 1
            end
         else
            from
               i := upper_index
            until
               i < lower_index
            loop
               put(item(i),i + distance)
               put(default_value,i)
               i := i - 1
            end
         end
      ensure
         count = old count
      end

   slice(min, max: INTEGER): like Current is
         -- New collection consisting of items at indexes in [`min'..`max'].
         -- Result has the same dynamic type as `Current'.
	 -- The `lower' index of the `Result' is the same as `lower'.
      require
         lower <= min
         max <= upper
         min <= max + 1
      deferred
      ensure
         same_dynamic_type(Result)
         Result.count = max - min + 1
         Result.lower = lower
      end

   reverse is
	 -- Reverse the order of the elements.
      local
	 i, j: INTEGER
      do
	 from
	    i := lower
	    j := upper
	 until
	    i >= j
	 loop
	    swap(i,j)
	    i := i + 1
	    j := j - 1
	 end
      ensure
	 count = old count
      end

invariant

   valid_bounds: lower <= upper + 1

end -- COLLECTION[E]

