-- 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 LORIA - INRIA - U.H.P. Nancy 1 - FRANCE
--          Dominique COLNET and Suzanne COLLIN - SmartEiffel@loria.fr
--                       http://SmartEiffel.loria.fr
--
class FIXED_ARRAY3[E]
   --
   -- Resizable three dimensional array.
   -- Unlike ARRAY3, the `lower1', `lower2' and `lower3' bounds are
   -- frozen to 0. Thus, one can expect better performances.
   --

inherit COLLECTION3[E]

creation
   make, copy, from_collection3, from_collection, from_model

feature

   upper1, count1, upper2, count2, upper3, count3, count: INTEGER

feature {NONE}

   count2x3: INTEGER
         -- To speed up access, this value is always equal to
         -- `count2' * `count3'

feature {FIXED_ARRAY3}

   storage: NATIVE_ARRAY[E]

   capacity: INTEGER; -- of `storage'.

feature

   lower1: INTEGER is 0

   lower2: INTEGER is 0

   lower3: INTEGER is 0

   make(new_count1, new_count2, new_count3 : INTEGER) is
	 -- Create or reset `Current' with new dimensions.
	 -- All elements are set to the default value of type E.
      require
	 new_count1 > 0
         new_count2 > 0
         new_count3 > 0
      do
	 count1 := new_count1
	 upper1 := new_count1 - 1
	 count2 := new_count2
	 upper2 := new_count2 - 1
         count3 := new_count3
         count2x3 := count2 * count3
         upper3 := new_count3 - 1
         count := count1 * count2x3
	 if capacity < count then
	    capacity := count
	    storage := storage.calloc(capacity)
	 else
	    storage.clear_all(capacity - 1)
	 end
      ensure
	 count1 = new_count1
	 count2 = new_count2
         count3 = new_count3
	 all_default
      end

   from_collection3(model: COLLECTION3[like item]) is
	 -- Uses the `model' to update Current.
      local
         i, j, k: INTEGER
      do
         make(model.upper1+1, model.upper2+1, model.upper3+1)
	 from
	    i := 0
	 until
	    i > upper1
	 loop
	    from
	       j := 0
	    until
	       j > upper2
	    loop
               from
                  k := 0
               until
                  k > upper3
               loop
                  put(model.item(i,j,k),i,j,k)
                  k := k + 1
               end
	       j := j + 1
	    end
	    i := i + 1
	 end
      end

   from_collection(contents: COLLECTION[E]
        new_count1, new_count2, new_count3: INTEGER) is
         --  Reset all bounds using `new_count#i'.
	 --  Copy all elements of `contents', line by line into Current.
      require
         new_count1 >= 0
         new_count2 >= 0
         new_count3 >= 0
         contents.count = new_count1 * new_count2 * new_count3
      local
         i: INTEGER
      do
         make(new_count1, new_count2, new_count3)
	 from
	    i := 0
	 until
	    i >= count
	 loop
	    storage.put(contents.item(contents.lower + i),i)
            i := i + 1
	 end
      ensure
         line_maximum = new_count1 - 1
         column_maximum = new_count2 - 1
         depth_maximum = new_count3 - 1
         count = contents.count
      end

   from_model(model: COLLECTION[COLLECTION[COLLECTION[E]]]) is
         -- The `model' is used to fill line by line the COLLECTION3.
         -- Assume all sub-collections of have the same indexing.
      local
         line, column, depth, n: INTEGER
      do
         make(model.upper - model.lower + 1,
              model.first.upper - model.first.lower + 1,
              model.first.first.upper - model.first.first.lower + 1)
	 from
	    line := model.lower
	 until
	    line > model.upper
	 loop
	    from
	       column := model.first.lower
	    until
	       column > model.first.upper
	    loop
               from
                  depth := model.first.first.lower
               until
                  depth > model.first.first.upper
               loop
                  storage.put(model.item(line).item(column).item(depth),n)
                  n := n + 1
                  depth := depth + 1
               end
	       column := column + 1
	    end
	    line := line + 1
	 end
      ensure
         line_maximum = model.upper - model.lower
         column_maximum = model.first.upper - model.first.lower
         depth_maximum = model.first.first.upper - model.first.first.lower
      end


feature -- Implementation of others feature from COLLECTION3:

   item(line, column, depth: INTEGER): E is
      do
         Result := storage.item(line * count2x3 + column * count3 + depth)
      end

   put(x: like item; line, column, depth: INTEGER) is
      do
         storage.put(x,line * count2x3 + column * count3 + depth)
      end

   force(element: like item; line, column, depth : INTEGER) is
      do
         if not valid_index(line, column, depth) then
            resize(line.max(upper1) + 1,
                   column.max(upper2) + 1,
                   depth.max(upper3) + 1)
	 end
         put(element,line,column,depth)
      end

   copy(other: like Current) is
      do
	 count1 := other.count1
	 upper1 := count1 - 1
	 count2 := other.count2
	 upper2 := count2 - 1
         count3 := other.count3
         count2x3 := count2 * count3
         upper3 := count3 - 1
         count := count1 * count2x3
	 if capacity < count then
	    capacity := count
	    storage := storage.calloc(capacity)
	 end
	 storage.copy_from(other.storage, count - 1)
      end

   is_equal(other: like Current): BOOLEAN is
      do
	 if other = Current then
	    Result := true
	 elseif upper1 /= other.upper1 then
	 elseif upper2 /= other.upper2 then
         elseif upper3 /= other.upper3 then
	 else
	    Result := storage.memcmp(other.storage,count)
	 end
      end

   sub_collection3(line_min, line_max, column_min, column_max,
                   depth_min, depth_max: INTEGER): like Current is
      local
         i, j, k, n: INTEGER
      do
         !!Result.make(line_max - line_min + 1,
                       column_max - column_min + 1,
                       depth_max - depth_min + 1)
	 from
            i := line_min
	    k := 0
	 until
	    i > line_max
	 loop
	    from
               j := column_min
	    until
	       j > column_max
	    loop
               from
                  k := depth_min
               until
                  k > depth_max
               loop
                  Result.storage.put(item(i,j,k),n)
                  n := n + 1
                  k := k + 1
               end
               j := j + 1
	    end
	    i := i + 1
	 end
      ensure
         Result.upper1 = line_max - line_min
         Result.upper2 = column_max - column_min
         Result.upper3 = depth_max - depth_min
      end

feature -- Writing:

   set_all_with(x: E) is
	 --  All element are set with the value x.
      do
	 storage.set_all_with(x,count - 1)
      end

   all_default: BOOLEAN is
      do
	 result := storage.all_default(count - 1)
      end

   slice(l1,up1,l2,up2,l3,up3: INTEGER): like Current is
	 -- Create a new collection initialized with elements of
	 -- range `low'..`up'. Result has the same dynamic type
	 -- as Current collection.
      local
         line, column, depth : INTEGER
      do
	 from
            !!Result.make((up1 - l1) + 1,(up2 - l2) + 1,(up3 - l3) + 1)
            line := l1
	 until
            line > up1
	 loop
	    from
               column := l2
	    until
               column > up2
	    loop
               from
                  depth := l3
               until
                  depth > up3
               loop
                  Result.put(item(line,column,depth),
                             line - l1,
                             column - l2,
                             depth - l3)
                  depth := depth + 1
               end
               column := column + 1
	    end
            line := line + 1
	 end
      end; -- slice

   set_slice(element: like item;  l1,up1,l2,up2,l3,up3: INTEGER) is
         -- Set all the elements in the
         -- range [(l1,up1),(l2,up2),(l3,up3)] of
         -- Current with the element 'element'.
      local
         i,j,k : INTEGER
      do
	 from
            i := l1 * count2x3
         until
            i > up1 * count2x3
         loop
            from
               j := l2 * count3
            until
               j > up2 * count3
            loop
               from
                  k := l3
               until
                  k > up3
               loop
                  storage.put(element,i + j + k)
                  k := k + 1
               end
               j := j + count3
            end
            i := i + count2x3
         end
      end

   swap(line1, column1, depth1, line2, column2, depth2 : INTEGER) is
      local
	 tmp: like item
         c3, c2x3, index1, index2 : INTEGER
      do
	 c3 := count3
         c2x3 := count2x3
         index1 := line1 * c2x3 + column1 * c3 + depth1
         index2 := line2 * c2x3 + column2 * c3 + depth2
	 tmp := storage.item( index1)
         storage.put(storage.item(index2), index1)
	 storage.put(tmp,index2)
      end

feature -- Looking and comparison:

   occurrences(elt: E): INTEGER is
      do
         Result := storage.occurrences(elt,count - 1)
      end

   fast_occurrences(elt: E): INTEGER is
      do
	 Result := storage.fast_occurrences(elt,count - 1)
      end

feature -- Resizing:

   resize(new_count1 , new_count2, new_count3: INTEGER) is
      require
	 new_count1 > 0
	 new_count2 > 0
         new_count3 > 0
      local
	 tmp: like Current
         l, c, d: INTEGER
      do
         !!tmp.make(new_count1, new_count2, new_count3)
         -- It may be possible to avoid this creation when :
	 --    new `capacity' <= old `capacity'
	 from
	    l := line_maximum
	 until
	    l < 0
	 loop
	    from
	       c := column_maximum
	    until
	       c < 0
	    loop
               from
                  d := depth_maximum
               until
                  d < 0
               loop
                  if tmp.valid_index(l,c,d) then
                     tmp.put(item(l,c,d),l,c,d)
                  end
                  d := d - 1
	       end
	       c := c - 1
	    end
	    l := l - 1
	 end
	 standard_copy(tmp)
      ensure
	 upper1 = new_count1 - 1
	 count1 = new_count1
	 upper2 = new_count2 - 1
	 count2 = new_count2
         upper3 = new_count3 - 1
         count3 = new_count3
         count = new_count1 * new_count2 * new_count3
      end

feature -- Looking and Searching:

   has(x: like item): BOOLEAN is
	 -- Look for `x' using `equal' for comparison.
      do
	 Result := storage.index_of(x,count-1) <= (count-1)
      end;  -- has

   fast_has(x: like item): BOOLEAN is
	 -- Same as `has' but use `=' for comparison
      do
	 Result := storage.fast_index_of(x,count - 1) <= (count - 1)
      end; -- fast_has

feature -- Other features:

   replace_all(old_value, new_value: like item) is
      do
	 storage.replace_all(old_value,new_value,count - 1)
      end

   fast_replace_all(old_value, new_value: like item) is
      do
	 storage.fast_replace_all(old_value,new_value,count - 1)
      end

feature {COLLECTION3}

   same_as(other: COLLECTION3[E]): BOOLEAN is
      do
         Result := other.same_as_fixed_array3(Current)
      end

   same_as_array3(other: ARRAY3[E]): BOOLEAN is
      do
	 Result := standard_same_as(other)
      end

   same_as_fixed_array3(other: FIXED_ARRAY3[E]): BOOLEAN is
      do
	 Result := standard_same_as(other)
      end

invariant

   count1 = upper1 + 1

   count2 = upper2 + 1

   count3 = upper3 + 1

   count = count1 * count2 * count3

   count2x3 = count2 * count3

   capacity >= count

end -- FIXED_ARRAY3[E]