/*=========================================================================

  Program:   Visualization Toolkit
  Module:    freerange

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*----------------------------------------------------------------------------
 Copyright (c) Sandia Corporation
 See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
----------------------------------------------------------------------------*/
// .NAME freerange - allocates and deallocates contiguous memory efficiently
//
// .SECTION Description
//
// .SECTION Thanks
// Thanks to David Thompson for implementing this class.

#ifndef freerange_H
#define freerange_H

#include <assert.h>

#include <vtkstd/vector>
#include <vtkstd/deque>
#include <vtkstd/algorithm>
#include <vtkstd/stdexcept>
#include <stdlib.h>
#include <string.h>

/**\brief A free list template class in the spirit of the STL.
 *
 * A free list is a vector of items stored by value. Since items
 * are stored by value, it avoids the overhead of memory allocation
 * and deallocation as items are inserted and removed. When an item
 * is removed, it is not destroyed; a mark is made in a vector of
 * "available" positions. The next time an entry is inserted, the
 * most recently removed item's position in the vector of all items
 * is used to store the newly inserted member.
 */
template< class T, class idx_t, T empty_entry=-1 >
class freerange
{
	public:
		typedef vtkstd::deque<idx_t>                dead_list_entry_t;
		typedef vtkstd::vector< dead_list_entry_t > dead_list_t;
  protected:
    T* array ;
    idx_t array_len ; // max id + 1
    idx_t array_top ; // id of next available entry
    dead_list_t dead ;
		int dead_size;
		idx_t used;
  public:
    freerange()
    {
      array_len = 16 ;
			array_top = 0 ;
			array = new T [ array_len ];
			for ( idx_t i=array_top; i<array_len; ++i )
				array[i] = empty_entry;

			dead_size = 27;
			dead.resize( dead_size+1 ); // dead[0] is unused.

			used = 0;
    }
    ~freerange()
    {
			delete [] array ;
    }

		idx_t size() const { return used; }
		idx_t max_id() const { return array_top - 1; }
		idx_t capacity() const { return array_len; }

    // Access to the array itself.
    T* pointer(idx_t pos) { return array + pos; }

		int max_chunked_grab_size() const { return dead_size; }
		void set_max_chunked_grab_size( int c )
		{
			if ( c == dead_size )
				return;
			if ( c < 1 )
				return;
			dead_size = c;
			// FIXME: the following resize will leak any chunks larger than c!
			dead.resize( dead_size + 1 );
		}

    bool grab_internal( idx_t& v, int num ) 
    {
			bool new_alloc = false;
      if ( (num <= dead_size) && (! dead[num].empty()) ) {
				v = dead[num].front() ;
				dead[num].pop_front() ;
				return (v != 0);
      }
      if ( array_top + num > array_len ) {
				// OK, time to resize the array
				do {
					array_len <<= 1 ;
				} while ( array_top + num > array_len );

				T* array_tmp = new T [ array_len ];
				if ( ! array_tmp )
					throw vtkstd::runtime_error( "freerange memory allocation failed" );
				for ( idx_t i=0; i<array_top; i++ )
					array_tmp[i] = array[i];
				delete [] array;
				array = array_tmp;
				new_alloc = true;
			}
			v = array_top;
			array_top += num;
			used += num;
      return (v != 0) ;
    }

		idx_t grab( int num=1 )
		{
			idx_t v;
			if ( grab_internal( v, num ) )
				for (idx_t i=0; i<num; ++i )
					array[ v + i ] = empty_entry;
			return v;
		}

    idx_t grabAndAssign( const T& src )
    { idx_t entry; grab_internal(entry,1) ; (*(array[entry])) = src ; return entry ; }

		void mark_as_freed( idx_t i, idx_t num )
		{
			for ( idx_t stop=i+num; i < stop; i++ )
				array[ i ] = empty_entry;
		}

    void free( idx_t i, idx_t num=1 )
    {
      if ( i == array_top - 1 - num ) {
				array_top -= num ;
				return ;
			}
			mark_as_freed( i, num );
			// break up large freed blocks into random sizes that all fit in our dead pool
			while ( num >= dead_size ) {
				// Not all OS's have lrand48(), try rand() instead.
				//register int deadpool = lrand48() % dead_size + 1;
				register int deadpool = rand() % dead_size + 1;
				dead[deadpool].push_front( i );
				i += deadpool;
				num -= deadpool;
				used -= deadpool;
			}
			if ( num ) {
				dead[num].push_front( i );
				used -= num;
			}
    }

    T& operator[] ( idx_t i )
    {
      return array[i] ;
    }

    const T& operator[] ( idx_t i ) const
    {
      return array[i] ;
    }

		void resize( idx_t new_size )
		{
			// note that resizing doesn't guarantee the number of contiguous free
			// or even free items in the list... it only guarantees storage for
			// some total number of items assuming that all the holes get filled.
			if ( new_size == array_len ) {
				return;
			} else if ( new_size < array_len ) {
				// time to remove elements AND possibly dead entries
				// until we're trimmed down to size
				array_len = new_size;
				T* array_tmp = new T [ array_len ];
				if ( ! array_tmp )
					throw vtkstd::runtime_error( "freerange memory allocation failed" );
				if ( array_top > array_len ) {
					used -= array_top - array_len;
					array_top = array_len;
				}
				for ( idx_t i=0; i<array_top; i++ )
					array_tmp[i] = array[i];
				delete [] array;
				for ( idx_t i=array_top; i<new_size; ++i )
					array_tmp[i] = empty_entry;
				array = array_tmp;
				for ( idx_t i=1; i<dead_size; i++ ) { // dead[0] is unused
					typename dead_list_entry_t::iterator it = vtkstd::remove_if( dead[i].begin(), dead[i].end(), vtkstd::bind2nd( vtkstd::greater_equal<idx_t>(), new_size ) );
					dead[i].erase( it, dead[i].end() );
				}
			} else { // new_size > array_len
				// allocate more memory
				array_len = new_size;
				T* array_tmp = new T [ array_len ];
				if ( ! array_tmp )
					throw vtkstd::runtime_error( "freerange memory allocation failed" );
				for ( idx_t i=0; i<array_top; i++ )
					array_tmp[i] = array[i];
				for ( idx_t i=array_top; i<new_size; ++i )
					array_tmp[i] = empty_entry;
				delete [] array;
				array = array_tmp;
			}
		}

		// dead[0] is unused!
		typename dead_list_t::iterator dead_begin() { return dead.begin()+1; }
		typename dead_list_t::iterator dead_end() { return dead.end(); }

		// dead[0] is unused!
		typename dead_list_t::const_iterator dead_begin() const { return dead.begin()+1; }
		typename dead_list_t::const_iterator dead_end() const { return dead.end(); }

		void clear()
		{
			array_top = 0;
			used = 0;
			for ( typename dead_list_t::iterator it = dead_begin(); it != dead_end(); ++it )
				it->clear();
		}

		T empty_entry_value() { return empty_entry; }

    class iterator ;
    friend class iterator ;

    iterator begin() ;
    iterator end() ;

    class iterator {
      protected:
				idx_t posn ;
				freerange* fl ;

				void check( idx_t p )
				{
					if ( ! fl ) {
						posn = 0 ;
						return ;
					}

					while ( ((*fl)[p] == empty_entry) && (p < fl->array_top) ) 
						p++ ;

					if ( p >= fl->array_top ) {
						fl = 0 ;
						posn = 0 ;
					} else {
						posn = p ;
					}
				}

			public:
				typedef vtkstd::bidirectional_iterator_tag iterator_category;

				iterator()
				{ fl = 0 ; posn = 0 ; }

				iterator( freerange<T,idx_t,empty_entry>* list, idx_t p )
				{ fl = list ; check(p) ; }

				iterator( const iterator& i )
				{ fl = i.fl ; posn = i.posn ; }

				~iterator() { } ;

				iterator& operator = ( idx_t p )
				{ check( p ) ; }

				idx_t position() const { return posn ; }

				bool operator == ( const iterator& i ) const
				{ return ((i.fl == fl) && (i.posn == posn)) ; }

				bool operator != ( const iterator& i ) const
				{ return ( (fl != i.fl) || (posn != i.posn) ) ; }

				iterator& operator ++ ()
				{ check( ++posn ) ; return *this ; }

				const T& operator * () const
				{ assert(fl) ; return fl->array[posn] ; }

				const T& operator -> () const
				{ assert(fl) ; return fl->array[posn] ; }

				T& operator * ()
				{ assert(fl) ; return fl->array[posn] ; }

				T& operator -> ()
				{ assert(fl) ; return fl->array[posn] ; }
		} ;

} ;


template< class T, class idx_t, T empty_entry >
typename freerange<T,idx_t,empty_entry>::iterator freerange<T,idx_t,empty_entry>::begin()
{
  return iterator( this, 0 ) ;
}

template< class T, class idx_t, T empty_entry >
typename freerange<T,idx_t,empty_entry>::iterator freerange<T,idx_t,empty_entry>::end() 
{
  return iterator( 0, 0 ) ;
}

#endif // freerange_H
