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


  - copyright   := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag";

  - comment     :=" General prurpose, resizable, three dimensional array..";
    
Section Inherit
  
  + parent_collection3:Expanded COLLECTION3[V];
  
Section Public
  
  + lower1:INTEGER;
  + lower2:INTEGER;
  + lower3:INTEGER;
  
  + upper1:INTEGER;
  + upper2:INTEGER;
  + upper3:INTEGER;
  
  + count1:INTEGER;
  + count2:INTEGER;
  + count3:INTEGER;
  
  + count:INTEGER;
  
Section ARRAY3
  
  + storage:NATIVE_ARRAY[V];
  // To store elements line by line.
  
  + capacity:INTEGER;
  // Number of elements in `storage'.
  
Section Public
  
  //
  // Creation / modification:
  // 
  
  - create (line_min, column_min, depth_min:INTEGER) 
  to (line_max, column_max, depth_max:INTEGER) :SELF <-
  // Reset all bounds `line_minimum' / `line_maximum' / `column_minimum'
  // `column_maximum' / `depth_min' and `depth_max' using arguments as
  // new values. All elements are set to the default value of type E.
  ( + result:SELF;
    
    result := clone;
    result.make (line_min, column_min, depth_min) to (line_max, column_max, depth_max);
    result
  );  
  
  - make (line_min, column_min, depth_min:INTEGER) to (line_max, column_max, depth_max:INTEGER) <-
  // Reset all bounds `line_minimum' / `line_maximum' / `column_minimum'
  // `column_maximum' / `depth_min' and `depth_max' using arguments as
  // new values. All elements are set to the default value of type E.
  [ ...
    -? { line_min   <= line_max   };
    -? { column_min <= column_max };
    -? { depth_min  <= depth_max  };
  ]
  (  
    lower1 := line_min;
    upper1 := line_max;
    lower2 := column_min;
    upper2 := column_max;
    lower3 := depth_min;
    upper3 := depth_max;
    count1 := upper1 - lower1 + 1;
    count2 := upper2 - lower2 + 1;
    count3 := upper3 - lower3 + 1;
    count := count1 * count2 * count3;
    (capacity >= count).if {
      storage.clear_all (count - 1);
    } else {
      capacity := count;
      storage := NATIVE_ARRAY[V].calloc count;
    };
  )
  [ ...
    +? { line_minimum = line_min };
    +? { line_maximum = line_max };
    +? { column_minimum = column_min };
    +? { column_maximum = column_max };
    +? { depth_minimum = depth_min };
    +? { depth_maximum = depth_max };
  ];
    
  - from_collection3 model:COLLECTION3[V] <-
  (
    make (model.line_minimum, model.column_minimum, model.depth_minimum) 
    to   (model.line_maximum, model.column_maximum, model.depth_maximum);
    
    line_minimum.to line_maximum do { i:INTEGER;      
      column_minimum.to column_maximum do { j:INTEGER;	 
	depth_minimum.to depth_maximum do { k:INTEGER;
	  put (model.item (i,j,k)) to (i,j,k);
	}; 
      }; 
    }; 
  )
  [ ...
    +? { lower1 = model.lower1 };
    +? { lower2 = model.lower2 };
    +? { lower3 = model.lower3 };
  ];
    
  - from_collection contents:COLLECTION[V] 
  size (line_min,column_min,depth_min:INTEGER) 
  to (line_max,column_max,depth_max:INTEGER) <-
  // Reset all bounds using `line_min', `line_max', `column_min',
  // `column_max', `depth_min', and `depth_max'.
  // Copy all elements of `contents', line by line into Current.
  [ ...
    -? { line_min <= line_max };
    -? { column_min <= column_max };
    -? { depth_min <= depth_max };
    -? { 
      contents.count = (line_max - line_min + 1) * 
      (column_max - column_min + 1) * 
      (depth_max - depth_min + 1) 
    };
  ]
  (    
    make (line_min,column_min,depth_min) to (line_max,column_max,depth_max);
    
    0.to (count - 1) do { i:INTEGER;
      storage.put (contents.item (contents.lower + i)) to i;
    }; 
  )
  [ ...
    +? { line_minimum = line_min };
    +? { line_maximum = line_max };
    +? { column_minimum = column_min };
    +? { column_maximum = column_max };
    +? { depth_minimum = depth_min };
    +? { depth_maximum = depth_max };
    +? { count = contents.count };
  ];
    
  - from_model model:COLLECTION[COLLECTION[COLLECTION[V]]] <-
  //The `model' is used to fill line by line the COLLECTION3.
  //Assume all sub-collections of have the same indexing.
  (
    make (model.lower,model.first.lower,model.first.first.lower) 
    to   (model.upper,model.first.upper,model.first.first.upper);
    
    (model.lower).to (model.upper) do { line:INTEGER;
      (model.first.lower).to (model.first.upper) do { column:INTEGER;
	(model.first.first.lower).to (model.first.first.upper) do { depth:INTEGER;
	  put (model.item line.item column.item depth) to (line,column,depth);
	}; 
      };
    };
  )
  [ ...
    -? { line_minimum = model.lower };
    -? { line_maximum = model.upper };
    -? { column_minimum = model.first.lower };
    -? { column_maximum = model.first.upper };
    -? { depth_minimum = model.first.first.lower };
    -? { depth_maximum = model.first.first.upper };
  ];
  
  //
  // Resizing:
  //
  
  - resize (line_min, column_min, depth_min:INTEGER) to (line_max, column_max, depth_max:INTEGER) <-
  // Resize bounds of the Current array
  [ ...
    -? { line_max   >= line_min   };
    -? { column_max >= column_min };
    -? { depth_max  >= depth_min  };
  ]
  ( + tmp:SELF;
        
    tmp := SELF.clone;
    tmp.make (line_min, column_min, depth_min) to (line_max, column_max, depth_max);
    //BSBS: It may be possible to avoid this creation when :
    //BSBS:   new `capacity' <= old `capacity'
    
    lower1.to line_maximum do { l:INTEGER;
      lower2.to column_maximum do { c:INTEGER;
	lower3.to depth_maximum do { d:INTEGER;
	  (tmp.valid_index (l,c,d)).if {
	    tmp.put (item (l,c,d)) to (l,c,d);
	  }; 
	}; 
      }; 
    }; 
    
    standard_copy tmp;
  )
  [ ...
    +? { line_minimum = line_min };
    +? { line_maximum = line_max };
    +? { column_minimum = column_min };
    +? { column_maximum = column_max };
    +? { depth_minimum = depth_min };
    +? { depth_maximum = depth_max };
  ];
  
  //
  // Implementation of others feature from COLLECTION3:
  //
  
  - item (line, column, depth:INTEGER) :V <-
  (
    storage.item (
      (line - lower1) * count2 * count3 + 
      (column - lower2) * count3 + depth - lower3
    )
  );
    
  - put element:V to (line, column, depth:INTEGER) <-
  (
    storage.put element to (
      (line - lower1) * count2 * count3 + 
      (column - lower2) * count3 + depth - lower3
    );
  );
    
  - force x:V to (line, column, depth:INTEGER) <-
  (
    (! valid_index (line,column,depth)).if {
      resize (line.min lower1,column.min lower2,depth.min lower3) 
      to     (line.max upper1,column.max upper2,depth.max upper3);
    };
    put x to (line,column,depth);
  );
    
  - set_all_with element:V <-
  (
    storage.set_all_with element with (count - 1);
  );
    
  - replace_all old_value:V with new_value:V <-
  (
    storage.replace_all old_value with new_value until (count - 1);
  );
    
  - fast_replace_all old_value:V with new_value:V <-
  (
    storage.fast_replace_all old_value with new_value until (count - 1);
  );
    
  - sub_collection3 (line_min, column_min, depth_min:INTEGER) 
  to (line_max, column_max, depth_max:INTEGER) :SELF <-
  ( + n:INTEGER;
    + result:SELF;
    
    result := create (line_min,column_min,depth_min) to (line_max,column_max,depth_max);
    
    line_min.to line_max do { i:INTEGER;
      column_min.to column_max do { j:INTEGER;
	depth_min.to depth_max do { k:INTEGER;
	  result.storage.put (item (i,j,k)) to n;
	  n := n + 1;
	}; 
      };
    };
    result
  )
  [ ...   
    +? { result.lower1 = line_min   };
    +? { result.lower2 = column_min };
    +? { result.lower3 = depth_min  };
    +? { result.upper1 = line_max   };
    +? { result.upper2 = column_max };
    +? { result.upper3 = depth_max  };    
  ];
  
  //
  // Looking and comparison:
  //
  
  - occurrences elt:V :INTEGER <-
  (
    storage.occurrences elt until (count - 1)
  );
    
  - fast_occurrences elt:V :INTEGER <-
  (
    storage.fast_occurrences elt until (count - 1)
  );
    
  - has x:V :BOOLEAN <-
  //Search if a element x is in the array using `equal'.
  //See also `fast_has' to choose the apropriate one.
  ( + result:BOOLEAN;
    
    (count > 0).if {
      result := storage.index_of x until (count - 1) <= count - 1;
    };
    result
  );
    
  - fast_has x:V :BOOLEAN <-
  // Search if a element x is in the array using `='.
  ( + result:BOOLEAN;
    
    (count > 0).if {
      result := storage.fast_index_of x until (count - 1) <= count -1;
    };
    result
  );
    
  - all_default:BOOLEAN <-
  (
    storage.all_default (count - 1)
  );
    
  - swap (line1, column1, depth1:INTEGER) with (line2, column2, depth2:INTEGER) <-
  ( + tmp:V;
    + index1, index2:INTEGER;
    
    index1 := (line1 - lower1) * count2 * count3 + (column1 - lower2) * count3 + depth1 - lower3;
    index2 := (line2 - lower1) * count2 * count3 + (column2 - lower2) * count3 + depth2 - lower3;
    tmp := storage.item index1;
    storage.put(storage.item index2) to index1;
    storage.put tmp to index2;
  );
    
  - copy other:SELF <-
  (
    lower1 := other.lower1;
    upper1 := other.upper1;
    lower2 := other.lower2;
    upper2 := other.upper2;
    lower3 := other.lower3;
    upper3 := other.upper3;
    count  := other.count;
    count1 := other.count1;
    count2 := other.count2;
    count3 := other.count3;
    (capacity < count).if {
      capacity := count;
      storage := storage.calloc count;
    };
    storage.copy_from (other.storage) until (count - 1);
  );
  
  //
  // Invariant.
  //
  
//  [ ...
//    -? { count1 = upper1 - lower1 + 1 };
//    -? { count2 = upper2 - lower2 + 1 };
//    -? { count3 = upper3 - lower3 + 1 };
//    -? { capacity >= count };
//  ];
  
  
/*  
  - '=='  Right 60 other:SELF :BOOLEAN <-
  (
    + result:BOOLEAN;
    result := FALSE;
    
    ( other = Self ).if {
      result := true;
    }.elseif { lower1 != other.lower1 } then {
    }.elseif { lower2 != other.lower2 } then {
    }.elseif { lower3 != other.lower3 } then {
    }.elseif { upper1 != other.upper1 } then {
    }.elseif { upper2 != other.upper2 } then {
    }.elseif { upper3 != other.upper3 } then {
    } else {
      result := storage.memcmp (other.storage) until count;
    };
    
    result
  );
*/