-- This file is  free  software, which  comes  along  with  SmallEiffel. 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-98 LORIA - UHP - CRIN - INRIA - FRANCE
--            Dominique COLNET and Suzanne COLLIN - colnet@loria.fr 
--                       http://SmallEiffel.loria.fr
--
class ARRAY[E]
   -- 
   -- General purpose resizable ARRAYs.
   -- 

inherit ARRAYED_COLLECTION[E];

creation make, with_capacity, from_collection

feature
   
   lower: INTEGER;     
         -- Lower index bound.
   
feature -- Creation and Modification :
   
   make(minindex, maxindex: INTEGER) is
         -- Make array with range [`minindex' .. `maxindex']. 
         -- When `maxindex' = `minindex' - 1, the array is empty.
      require
         minindex -1 <= maxindex
      local
         needed: INTEGER;
      do
         lower := minindex;
         upper := maxindex;
         needed := maxindex - minindex + 1;
         if needed > 0 then
            if capacity < needed then
               storage := storage.calloc(needed);
               capacity := needed;
            else
               clear_all;
            end;
         end;
      ensure
         lower = minindex;
         upper = maxindex;
         all_cleared
      end;

   with_capacity(needed_capacity, low: INTEGER) is
         -- Create an empty array with `capacity' initialized
         -- at least to `needed_capacity' and `lower' set to `low'.
      require
         needed_capacity >= 0
      do
         if capacity < needed_capacity then
            storage := storage.calloc(needed_capacity);
            capacity := needed_capacity;
         end;
         lower := low;
         upper := low - 1;
      ensure
         empty;
         needed_capacity <= capacity;
         lower = low
      end;

feature -- Modification :
   
   resize(minindex, maxindex: INTEGER) is
         -- Resize the array. No elements will be lost in the 
         -- intersection of [old `lower' .. old `upper'] and 
         -- [`minindex' .. `maxindex'].
         -- New positions are initialized with appropriate 
         -- default values.
      require
         minindex -1 <= maxindex
      local
         needed, offset, intersize: INTEGER;
      do
         needed := maxindex - minindex + 1;
         if needed > 0 then
            if needed > capacity then
               if capacity = 0 then
                  storage := storage.calloc(needed);
                  capacity := needed;
               else
                  storage := storage.realloc(capacity, needed);
                  capacity := needed;
               end;
            end;
            offset := lower - minindex;
            intersize := maxindex.min(upper) - minindex.max(lower) + 1;
            if intersize > 0 then
               if offset = 0 then
                  if intersize < needed then
                     storage.clear(intersize, needed - 1);
                  end;
               elseif offset < 0 then
                  storage.move(-offset, intersize - offset - 1, offset);
                  if intersize < needed then
                     storage.clear(intersize, needed - 1);
                  end;
               else
                  storage.move(0, intersize - 1, offset);
                  storage.clear(0, offset - 1);
                  if intersize + offset < needed then
                     storage.clear(intersize + offset, needed - 1);
                  end;
               end;
            else
               storage.clear(0, needed - 1);
            end;
         end;
         lower := minindex;
         upper := maxindex;
      end;

feature 

   sub_array(low, up: INTEGER): like Current is
      local
         i: INTEGER;
      do
         from
            !!Result.with_capacity(up - low + 1,low);
            Result.set_upper(up);
            i := low;
         until
            i > up
         loop
            Result.put(item(i),i);
            i := i + 1;
         end;
      ensure then
         Result.lower = low;
      end;

feature -- Implementation of deferred :

   count: INTEGER is
      do
         Result := upper - lower + 1;
      end;

   item, infix "@" (index: INTEGER): E is
      do
         Result := storage.item(index - lower);
      end;
   
   put(element: like item; index: INTEGER) is
      do
         storage.put(element,index - lower);
      end;

   force(element: like item; index: INTEGER) is
         -- Put `element' at position `index'. Current is
         -- resized if `index' is not inside current bounds.
         -- New bounds are initialized with default values.
      require else
         true
      do
         if upper < index then
            resize(lower,index);
         elseif index < lower then
            resize(index,upper);
         end;
         put(element,index);
      end;

   copy(other: like Current) is
      local
         needed_capacity: INTEGER;
      do
         lower := other.lower;
         upper := other.upper;
         needed_capacity := upper - lower + 1;
         if capacity < needed_capacity then
            capacity := needed_capacity;
            storage := storage.calloc(capacity);
         end;
         if needed_capacity > 0 then
            storage.copy_from(other.storage,needed_capacity - 1);
         end;
      end;
   
   set_all_with(v: like item) is
      do
         storage.set_all_with(v,upper - lower);
      end;

   remove_first is
      do
         storage.remove_first(upper - lower);
         lower := lower + 1;
      ensure then
         upper = old upper;
      end;
   
   remove(index: INTEGER) is
      do
         storage.remove(index - lower,upper - lower);
         upper := upper - 1;
      end;
   
   clear is
      do
         upper := lower - 1;
      ensure then
         capacity = old capacity
      end;

   add_first(element: like item) is
      do
         if upper < lower then
            add_last(element);
         else
            add_last(element);
            move(lower,upper - 1,1);
            put(element,lower);
         end;
      end;

   add_last(element: like item) is
      local
         new_capacity: INTEGER;
      do
         if capacity < count + 1 then
            if capacity = 0 then
               capacity := 16;
               storage := storage.calloc(capacity);
            else
               new_capacity := 2 * capacity;
               storage := storage.realloc(capacity,new_capacity);
               capacity := new_capacity;
            end;
         end;
         upper := upper + 1;
         put(element,upper);
      end;

   from_collection(model: COLLECTION[like item]) is
      local
         i, up: INTEGER;
      do
         from
            with_capacity(model.count,model.lower);
            i := model.lower;
            up := model.upper;
            upper := up;
         until
            i > up
         loop
            put(model.item(i),i);
            i := i + 1;
         end;
      ensure then
         lower = model.lower;
         upper = model.upper
      end;

   all_cleared: BOOLEAN is
      do
         Result := storage.all_cleared(upper - lower);
      end;

   nb_occurrences(element: like item): INTEGER is
      do
         Result := storage.nb_occurrences(element,upper - lower);
      end;
      
   fast_nb_occurrences(element: like item): INTEGER is
      do
         Result := storage.fast_nb_occurrences(element,upper - lower);
      end;
      
   index_of(element: like item): INTEGER is
      do
         Result := lower + storage.index_of(element,upper - lower);
      end;

   fast_index_of(element: like item): INTEGER is
      do
         Result := lower + storage.fast_index_of(element,upper - lower);
      end;

   is_equal(other: like Current): BOOLEAN is
      do
         if Current = other then
            Result := true;
         elseif lower = other.lower and then upper = other.upper then
            Result := storage.memcmp(other.storage,count);
         end;
      end;

   slice(low, up: INTEGER): like Current is
      local
         i: INTEGER;
      do
         from
            i := low;
            !!Result.with_capacity(up - low + 1,lower);
         until
            i > up
         loop
            Result.add_last(item(i));
            i := i + 1;
         end;
      end;

end -- ARRAY[E]

