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


  - copyright   := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag";
    
  - author      :="Boutet Jerome (boutet@loria.fr)";
  
  - comment     :="Resizable, fixed lower bound array.\
                  \Unlike ARRAY, the `lower' bound of a FAST_ARRAY is frozen \
                  \to 0. Thus, some memory is saved and looping toward `lower' \
                  \bound (which is 0) run a little bit faster.";
		  		  
  // General purpose resizable FAST_ARRAYs. The only difference with ARRAY is the 
  // fact that the `lower' bound is actually frozen to 0. The `item' access is likely
  // to be more efficient as well as loop going from `upper' to `lower' just because 
  // `lower' is 0. Keep in mind that even if the `lower' is frozen to 0
  // it is really better to use the `lower' attribute, and not 0 directly, just because 
  // you may decide in the future to use another COLLECTION implementation.
  //
  // Like ARRAY, the FAST_ARRAY implementation uses only one chunk of memory, the 
  // `storage' area which is a NATIVE_ARRAY. One must keep in mind that this internal 
  // `storage' area is always kept left align. Thus, you can expect good performances 
  // while using a FAST_ARRAY to modelize a stack behavior with 
  // `add_last' / `last' / `remove_last'. Conversely `add_first' and `remove_first' are 
  // likely to slow down your program if they are too often used. If the fact that 
  // `lower' is stuck to 0 do matter, also consider ARRAY.
		  
Section Inherit
  
  + parent_arrayed_collection:Expanded ARRAYED_COLLECTION[V];
  
Section Public
  
  - lower:INTEGER := 0; // Frozen lower bound.
  
  //
  // Creation and modification:
  //
  
  - create new_count:INTEGER :SELF <-
  // Make array with range [0 .. `new_count' - 1].
  // When `new_count' = 0 the array is empty.
  ( + result:SELF;
    
    result := clone;        
    result.make new_count;
    result
  );
  
  - create_with_capacity new_count:INTEGER :SELF <-
  // Create an empty array with at least `needed_capacity'.
  ( + result:SELF;
    
    result := clone;        
    result.with_capacity new_count;
    result
  );
  
  - create_with_native_array_byte na:NATIVE_ARRAY[UINTEGER_8] size s:INTEGER :SELF <-
  ( + result:SELF;
    
    result := clone;
    result.make_with_native_array_byte na size s;
    result
  );
  
  - make_with_map_object obj:OBJECT <-
  // BSBS: A revoir.
  [ ...
    //-? {obj.object_size!=0};
    -? {element_sizeof = 1};
  ]
  ( 
    storage := CONVERT[OBJECT,NATIVE_ARRAY[V]].on obj;
    capacity := obj.object_size;
    upper := -1;
  )
  [ ...
    +? {storage != NULL};
  ];
  
  - make_with_native_array_byte na:NATIVE_ARRAY[UINTEGER_8] size s:INTEGER <-
  [ ...
    -? {s > 0};
    -? {na != NULL};
  ]
  (    
    storage  := na;
    capacity := s;
    upper := -1;
  )
  [ ...
    +? {storage != NULL};
  ];
  
  - make new_count:INTEGER <-
  // Make array with range [0 .. `new_count' - 1].
  // When `new_count' = 0 the array is empty.
  [ ...
    -? { new_count >= 0};
  ]
  ( 
    ( new_count.to_integer > capacity).if {
      // The new one is bigger:
      storage := NATIVE_ARRAY[V].create new_count;
      capacity := new_count;
    }.elseif { (capacity > 0) && { upper >= 0}} then { 
      // The new one is smaller and just need to be cleared:
      storage.clear 0 to upper;
    }; 
    upper := new_count - 1;
  )
  [ ...
    +? { count = new_count };
    +? { capacity >= Old capacity };
    +? { (upper >= 0) ->> {all_default} }; 
  ]; 
    
  - with_capacity needed_capacity:INTEGER <-
  // Create an empty array with at least `needed_capacity'.
  [ ...
    -? { needed_capacity >= 0 };
  ]
  (    
    (capacity < needed_capacity).if {
      storage := NATIVE_ARRAY[V].create needed_capacity;
      capacity := needed_capacity;
    //}.elseif { capacity > needed_capacity } then {
    //  storage.clear 0 to upper; // BSBS: Ca serre a quoi de le vider !
    };
    upper := -1;
  )
  [ ...
    +? { capacity >= needed_capacity };
    +? { is_empty };    
  ]; 
  
  //
  // Hashable.
  //
  
  - hash_code:INTEGER <-
  ( + result:INTEGER;
    
    (! is_empty).if {
      (last != NULL).if {
	result := last.hash_code;
      }.elseif {first != NULL} then {
	result := first.hash_code;
      };
    };
    result
  );
  
  //
  // Modification:
  //

  - set_capacity new_capacity:INTEGER <-
  // Resize capacity the array, but not count.
  [
    -? {new_capacity >= 0};
  ]
  (    
    (new_capacity > capacity).if {
      (capacity = 0).if {
  	storage := NATIVE_ARRAY[V].create new_capacity;
      } else {
  	storage := storage.realloc capacity with new_capacity;	
      }; 
      capacity := new_capacity;	
    }; 
  )
  [
    +? { capacity >= Old capacity };    
  ];
  
  - fast_resize new_count:INTEGER <-
  [
    ? {new_count <= capacity}; 
  ]
  (
    upper := new_count - 1;
  );
  
  - resize new_count:INTEGER <-
  // Resize the array. When `new_count' is greater than
  // `count', new positions are initialized with appropriate
  // default values.
  [ ...
    -? {new_count >= 0};
  ]
  ( + new_capacity:INTEGER;
           
    ( new_count > count).if {
      (capacity = 0).if {
	storage := NATIVE_ARRAY[V].create new_count;
	capacity := new_count;
      }.elseif { capacity < new_count } then {
	new_capacity := capacity * 2;
	{ new_capacity >= new_count }.until_do {
	  new_capacity := new_capacity * 2;
	}; 
	storage := storage.realloc capacity with new_capacity;
	capacity := new_capacity;	
      }; 
    }.elseif { new_count != count } then {
      storage.clear new_count to (count - 1);
    }; 
    
    upper := new_count - 1;
  ) 
  [ ...
    +? { count = new_count };
    +? { capacity >= Old capacity };    
  ]; 
  
  //
  // Implementation of deferred:
  //
  
  - is_empty:BOOLEAN <- ( upper < 0 ); // end is_empty
    
  - item i:INTEGER :V <- 
  (    
    storage.item i
  ); 
    
  - put element:V to i:INTEGER <-
  (     
    storage.put element to i;    
  ); 
    
  - add_first element:V <-
  ( 
    add_last element;
    (upper = 0).if {
    }.elseif {upper = 1} then {
      swap 0 with 1;
    } else {
      move 0 to (upper - 1) by 1;
      storage.put element to 0;
    }; 
  ); 
    
  - add_last element:V <-
  ( + new_capacity:INTEGER;
        
    (upper + 1 <= capacity - 1 ).if {
      upper := upper + 1;
    }.elseif {capacity = 0} then {
      storage := NATIVE_ARRAY[V].create 2;
      capacity := 2;
      upper := 0
    } else {
      new_capacity := 2 * capacity;
      storage := storage.realloc capacity with new_capacity;
      capacity := new_capacity;
      upper := upper + 1;
    }; 
    storage.put element to upper;
  ); 
  
  - count:INTEGER <- ( upper + 1 ); // end count
    
  - clear <-
  ( 
    upper := -1;
  )
  [ ...
    +? { capacity = Old capacity };    
  ]; 
    
  - copy other:FAST_ARRAY[V] <-
  // Copy `other' onto Current.
  ( + other_upper, new_capacity:INTEGER;
    
    other_upper := other.upper; 
    (other_upper >= 0).if {
      new_capacity := other_upper + 1;
      
      ( capacity < new_capacity ).if {
	storage := NATIVE_ARRAY[V].create new_capacity;
	capacity := new_capacity;
      //}.elseif { capacity > 0 } then {
      //  storage.clear_all (capacity - 1);
      }; 
      storage.copy_from (other.storage) until other_upper;
    //}.elseif { capacity > 0 } then {
    //  storage.clear_all (capacity - 1);
    }; 
    upper := other_upper;
  ); 
  
  - set_all_with v:V <-
  ( 
    storage.set_all_with v until upper;    
  ) ; 
    
  //
  // Sort    
  //
  
  - bubble_sort <-
  // Bubble sort :-( => BSBS: Optmize with Quick sort...
  ( + low,up,idx,max:INTEGER;
    + sw:BOOLEAN;
    
    max := upper-1;
    
    low := 0;
    up  := max;
    {
      sw:=FALSE;
      low.to up do { i:INTEGER;	
	(item i > item (i+1)).if {
	  swap i with (i+1);
	  sw := TRUE;
	};
	
	idx := max - i;
	(item idx > item (idx+1)).if {
	  swap idx with (idx+1);
	  sw := TRUE;
	};		
      };
      up  := up - 1;
      low := low + 1;
    }.do_while {sw};
  );
  
  //
  //
  //
  
  - from_collection model:COLLECTION[V] <-
  ( + i1:INTEGER;
    
    with_capacity model.count;
    upper := model.count - 1;
    i1 := 0;    
    (model.lower).to (model.upper) do { i2:INTEGER;
      storage.put (model.item i2) to i1;
      i1 := i1 + 1;
    }; 
  ); 
    
  - '=='  Right 60 other:COLLECTION[V] :BOOLEAN <-
  ( + result:BOOLEAN;
    + o:SELF;
    
    ( Self = other).if {
      result := TRUE;
    } else {
      o ?= other;
      ((o != NULL) && { upper = o.upper }).if {
	result := storage.fast_memcmp (o.storage) until (upper + 1) ;
      };
    };
    
    result
  ); 
    
  - is_equal_map other:SELF :BOOLEAN <- 
  ( + result:BOOLEAN;
    
    ( Self = other).if {
      result := TRUE;
    }.elseif {upper = other.upper} then {
      result := storage.memcmp (other.storage) until (upper + 1);
    }; 
    
    result
  ); 
    
  - all_default:BOOLEAN <- 
  [
    -? {storage != NULL};
  ]
  (  
    storage.all_default upper
  ); 
    
  - occurrences element:V :INTEGER <- 
  (    
    storage.occurrences element until upper    
  ); 
    
  - fast_occurrences element:V :INTEGER <- 
  (     
    storage.fast_occurrences element until upper    
  ); 
  
  - first_index_of element:V :INTEGER <- 
  ( + result:INTEGER;
    
    (upper >= 0).if {
      result := storage.first_index_of element until upper;
    };
    result
  ); 

  - index_of element:V start start_index:INTEGER :INTEGER <- 
  ( + result:INTEGER;
    
    (upper >= 0).if {
      result := storage.index_of (element,start_index) until upper;
    };    
    result 
  ); 
  
  - reverse_index_of element:V start start_index:INTEGER :INTEGER <-
  (
    storage.reverse_index_of (element, start_index)
  );
    
  - fast_first_index_of element:V :INTEGER <- 
  ( + result:INTEGER;    
    
    (upper >= 0).if {
      result := storage.fast_first_index_of element until upper;
    };    
    result
  ); 

  - fast_index_of element:V start start_index:INTEGER :INTEGER <- 
  ( + result:INTEGER;    
    
    (upper >= 0).if {
      result := storage.fast_index_of (element,start_index) until upper;
    };    
    result
  ); 
  
  - fast_reverse_index_of element:V start start_index:INTEGER :INTEGER <-
  (
    storage.fast_reverse_index_of (element, start_index)
  );
	  
  - subarray min:INTEGER to max:INTEGER :SELF <-  // slice
  ( + result:SELF;
    
    result := SELF.create (max - min + 1);
    result.storage.slice_copy storage to 0 from min to max;
    
    result
  ); 
    
  - force element:V to index:INTEGER <- 
  (     
    ( index <= upper ).if {
      storage.put element to index;
    }.elseif { index = (upper + 1) } then {
      add_last element;
    } else {
      resize (index + 1);
      storage.put element to index;
    }; 
  ); 
    
  - remove_first <- 
  ( + void_storage:NATIVE_ARRAY[V];
    
    ( upper = 0).if {
      storage := void_storage;
      capacity := 0;
      upper := -1;
    } else {
      storage.remove_first upper;
      upper := upper - 1;
    }; 
  )
  [ ...
    +? {lower = Old lower};
  ]; 
    
  - remove_head n:INTEGER <-
  (
    storage.move n to upper by (-n);
    upper := upper - n;
  );
    
  - remove index:INTEGER <- 
  (     
    storage.remove index until upper;
    upper := upper - 1;
  ); 
  
  - remove beg:INTEGER to end:INTEGER <-
  [ ...
    -? {beg <= end};
  ]
  ( + s:INTEGER;
    
    (beg = end).if {
      remove beg;
    } else {
      s := end - beg + 1;
      move (end+1) to upper by (-s);
      upper := upper - s;
    };
  );

  - remove_since beg:INTEGER <-
  [
    -? {beg >= 0};
  ]
  (     
    (beg <= upper).if {      
      upper := beg - 1; 
    };
  );
    
  //
  // Guru Section.
  //
  
  - set_upper new_up:INTEGER <-
  (
    upper := new_up;
  );
