#define FULL_ASSERT_ARRAY_ACCESS    0

#include <stdio.h>
#include <vector>
#include <string.h>

#if FULL_ASSERT_ARRAY_ACCESS
class MemoryInfo
{
public:
	void* address;
	char name[512];
};
static std::vector< MemoryInfo > memoryInfo;
#endif // FULL_ASSERT_ARRAY_ACCESS

template< class C >
class Array
{
	void _assertBounds( long long idx ) const
	{
		if( idx<min || idx>=max )
		{
			fprintf( stderr , "Array index out-of-bounds: %lld <= %lld < %lld\n" , min , idx , max );
			ASSERT( 0 );
			exit( 0 );
		}
	}
protected:
	C *data , *_data;
	long long min , max;
public:
	long long minimum( void ) const { return min; }
	long long maximum( void ) const { return max; }

	inline Array( size_t size , int alignment , bool clear , const char* name=NULL )
	{
		_data = data = (C*)m_aligned_malloc( size * sizeof( C ) , alignment );
		if( clear ) memset( data ,  0 , size * sizeof( C ) );
		else        memset( data , -1 , size * sizeof( C ) );
		min = 0;
		max = (long long)( size );
#if FULL_ASSERT_ARRAY_ACCESS
		{
			IOServer::StdoutLock lock;
			int sz = memoryInfo.size();
			memoryInfo.resize( sz + 1 );
			memoryInfo[sz].address = _data;
			if( name ) strcpy( memoryInfo[sz].name , name );
			else memoryInfo[sz].name[0] = 0;
		}
#endif // FULL_ASSERT_ARRAY_ACCESS
	}
	Array( void )
	{
		data = _data = NULL;
		min = max = 0;
	}
	static Array FromPointer( C* data , long long min , long long max )
	{
		Array a;
		a._data = NULL;
		a.data = data;
		a.min = min;
		a.max = max;
		return a;
	}
	inline C& operator[]( long long idx )
	{
		_assertBounds( idx );
		return data[idx];
	}
	inline const C& operator[]( long long idx ) const
	{
		_assertBounds( idx );
		return data[idx];
	}
	inline Array& operator += (          long long idx ){ min -= idx , max -= idx , data +=idx ; return (*this); }
	inline Array  operator +  (          long long idx ){ Array a(*this) ; a += idx ; return a; }
	inline Array& operator -= (          long long idx ){ return operator += (-idx); }
	inline Array  operator -  (          long long idx ){ return operator +  (-idx); }
	inline Array& operator += (                int idx ){ return operator += ( (long long)idx ); }
	inline Array  operator +  (                int idx ){ return operator +  ( (long long)idx ); }
	inline Array& operator -= (                int idx ){ return operator -= ( (long long)idx ); }
	inline Array  operator -  (                int idx ){ return operator -  ( (long long)idx ); }
	inline Array& operator += ( unsigned long long idx ){ return operator += ( (long long)idx ); }
	inline Array  operator +  ( unsigned long long idx ){ return operator +  ( (long long)idx ); }
	inline Array& operator -= ( unsigned long long idx ){ return operator -= ( (long long)idx ); }
	inline Array  operator -  ( unsigned long long idx ){ return operator -  ( (long long)idx ); }
	inline Array& operator += ( unsigned       int idx ){ return operator += ( (long long)idx ); }
	inline Array  operator +  ( unsigned       int idx ){ return operator +  ( (long long)idx ); }
	inline Array& operator -= ( unsigned       int idx ){ return operator -= ( (long long)idx ); }
	inline Array  operator -  ( unsigned       int idx ){ return operator -  ( (long long)idx ); }

	Array( const Array& a )
	{
		if( !a )
		{
			data = _data =  NULL;
			min = max = 0;
		}
		else
		{
			_data = a._data;
			data = a.data;
			min = a.min;
			max = a.max;
		}
	}
	template< class D >
	Array( const Array< D >& a )
	{
		_data = NULL;
		if( !a )
		{
			data =  NULL;
			min = max = 0;
		}
		else
		{
			long long szC = sizeof( C );
			long long szD = sizeof( D );
			data = (C*)&a[0];
			min = ( a.minimum() * szD ) / szC;
			max = ( a.maximum() * szD ) / szC;
			if( min*szC!=a.minimum()*szD || max*szC!=a.maximum()*szD )
			{
				fprintf( stderr , "Could not convert array [ %lld , %lld ] * %zd => [ %lld , %lld ] * %zd\n" , a.minimum() , a.maximum() , szD , min , max , szC );
				ASSERT( 0 );
				exit( 0 );
			}
		}
	}

	void Free( void )
	{
		if( _data )
		{
			m_aligned_free( _data );
#if FULL_ASSERT_ARRAY_ACCESS
			{
				IOServer::StdoutLock lock;
				int idx;
				for( idx=0 ; idx<memoryInfo.size( ) ; idx++ ) if( memoryInfo[idx].address==_data ) break;
				if( idx==memoryInfo.size() )
				{
					fprintf( stderr , "Could not find memory in address table\n" );
					ASSERT( 0 );
				}
				else
				{
					memoryInfo[idx] = memoryInfo[memoryInfo.size()-1];
					memoryInfo.pop_back( );
				}
			}
#endif // FULL_ASSERT_ARRAY_ACCESS
		}
		(*this) = Array( );
	}
	C* pointer( void ){ return data; }
	const C* pointer( void ) const { return data; }
	bool operator !( void )const { return data==NULL; }
	operator bool( ) const { return data!=NULL; }
};

template< class C >
class ConstArray
{
	void _assertBounds( long long idx ) const
	{
		if( idx<min || idx>=max )
		{
			fprintf( stderr , "ConstArray index out-of-bounds: %lld <= %lld < %lld\n" , min , idx , max );
			ASSERT( 0 );
			exit( 0 );
		}
	}
protected:
	const C *data;
	long long min , max;
public:
	long long minimum( void ) const { return min; }
	long long maximum( void ) const { return max; }

	inline ConstArray( void )
	{
		data = NULL;
		min = max = 0;
	}
	inline ConstArray( const Array< C >& a )
	{
		data = a.pointer( );
		min = a.minimum( );
		max = a.maximum( );
	}
	inline ConstArray( const ConstArray& a )
	{
		data = a.pointer( );
		min = a.minimum( );
		max = a.maximum( );
	}
	template< class D >
	inline ConstArray( const Array< D >& a )
	{
		long long szC = ( long long )( sizeof( C ) ) , szD = ( long long )( sizeof( D ) );
		data = ( const C*)a.pointer( );
		min = ( long long )( ( a.minimum() * szD ) / szC );
		max = ( long long )( ( a.maximum() * szD ) / szC );
		if( min*szC!=a.minimum()*szD || max*szC!=a.maximum()*szD )
		{
			fprintf( stderr , "Could not convert array [ %lld , %lld ] * %zd => [ %lld , %lld ] * %zd\n" , a.minimum() , a.maximum() , szD , min , max , szC );
			ASSERT( 0 );
			exit( 0 );
		}
	}
	template< class D >
	inline ConstArray( const ConstArray< D >& a )
	{
		long long szC = ( long long )( sizeof( C ) ) , szD = ( long long )( sizeof( D ) );
		data = ( const C*)a.pointer( );
		min = ( long long )( ( a.minimum() * szD ) / szC );
		max = ( long long )( ( a.maximum() * szD ) / szC );
		if( min*szC!=a.minimum()*szD || max*szC!=a.maximum()*szD )
		{
			fprintf( stderr , "Could not convert array [ %lld , %lld ] * %zd => [ %lld , %lld ] * %zd\n" , a.minimum() , a.maximum() , szD , min , max , szC );
			ASSERT( 0 );
			exit( 0 );
		}
	}
	inline const C& operator[]( long long idx ) const
	{
		_assertBounds( idx );
		return data[idx];
	}
	inline ConstArray& operator += (          long long idx )       { min -= idx , max -= idx , data += idx ; return (*this); }
	inline ConstArray  operator +  (          long long idx ) const { ConstArray a(*this) ; a += idx ; return a; }
	inline ConstArray& operator -= (          long long idx )       { return operator += (-idx); }
	inline ConstArray  operator -  (          long long idx ) const { return operator +  (-idx); }
	inline ConstArray& operator += ( unsigned long long idx )       { return operator += ( (long long)idx ); }
	inline ConstArray  operator +  ( unsigned long long idx ) const { return operator +  ( (long long)idx ); }
	inline ConstArray& operator -= ( unsigned long long idx )       { return operator -= ( (long long)idx ); }
	inline ConstArray  operator -  ( unsigned long long idx ) const { return operator -  ( (long long)idx ); }
	inline ConstArray& operator += ( unsigned       int idx )       { return operator += ( (long long)idx ); }
	inline ConstArray  operator +  ( unsigned       int idx ) const { return operator +  ( (long long)idx ); }
	inline ConstArray& operator -= ( unsigned       int idx )       { return operator -= ( (long long)idx ); }
	inline ConstArray  operator -  ( unsigned       int idx ) const { return operator -  ( (long long)idx ); }
	inline ConstArray& operator += (                int idx )       { return operator += ( (long long)idx ); }
	inline ConstArray  operator +  (                int idx ) const { return operator +  ( (long long)idx ); }
	inline ConstArray& operator -= (                int idx )       { return operator -= ( (long long)idx ); }
	inline ConstArray  operator -  (                int idx ) const { return operator -  ( (long long)idx ); }

	const C* pointer( void ) const { return data; }
	bool operator !( void ) const { return data==NULL; }
	operator bool( ) const { return data!=NULL; }
};

#if FULL_ASSERT_ARRAY_ACCESS
inline void PrintMemoryInfo( void ){ for( int i=0 ; i<memoryInfo.size() ; i++ ) printf( "%d] %s\n" , i , memoryInfo[i].name ); }
#endif // FULL_ASSERT_ARRAY_ACCESS
template< class C >
Array< C > memcpy( Array< C > destination , const void* source , size_t size )
{
	if( size>destination.maximum()*sizeof(C) )
	{
		fprintf( stderr , "Size of copy exceeds destination maximum: %zd > %zd\n" , size , destination.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	memcpy( &destination[0] , source , size );
	return destination;
}
template< class C , class D >
Array< C > memcpy( Array< C > destination , Array< D > source , size_t size )
{
	if( size>destination.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of copy exceeds destination maximum: %zd > %zd\n" , size , destination.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	if( size>source.maximum()*sizeof( D ) )
	{
		fprintf( stderr , "Size of copy exceeds source maximum: %zd > %zd\n" , size , source.maximum()*sizeof( D ) );
		ASSERT( 0 );
		exit( 0 );
	}
	memcpy( &destination[0] , &source[0] , size );
	return destination;
}
template< class C , class D >
Array< C > memcpy( Array< C > destination , ConstArray< D > source , size_t size )
{
	if( size>destination.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of copy exceeds destination maximum: %zd > %zd\n" , size , destination.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	if( size>source.maximum()*sizeof( D ) )
	{
		fprintf( stderr , "Size of copy exceeds source maximum: %zd > %zd\n" , size , source.maximum()*sizeof( D ) );
		ASSERT( 0 );
		exit( 0 );
	}
	memcpy( &destination[0] , &source[0] , size );
	return destination;
}
template< class D >
void* memcpy( void* destination , Array< D > source , size_t size )
{
	if( size>source.maximum()*sizeof( D ) )
	{
		fprintf( stderr , "Size of copy exceeds source maximum: %zd > %zd\n" , size , source.maximum()*sizeof( D ) );
		ASSERT( 0 );
		exit( 0 );
	}
	memcpy( destination , &source[0] , size );
	return destination;
}
template< class D >
void* memcpy( void* destination , ConstArray< D > source , size_t size )
{
	if( size>source.maximum()*sizeof( D ) )
	{
		fprintf( stderr , "Size of copy exceeds source maximum: %zd > %zd\n" , size , source.maximum()*sizeof( D ) );
		ASSERT( 0 );
		exit( 0 );
	}
	memcpy( destination , &source[0] , size );
	return destination;
}
template< class C >
Array< C > memset( Array< C > destination , int value , size_t size )
{
	if( size>destination.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of copy exceeds destination maximum: %zd > %zd\n" , size , destination.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	memset( &destination[0] , value , size );
	return destination;
}

template< class C >
size_t fread( Array< C > destination , size_t eSize , size_t count , FILE* fp )
{
	if( count*eSize>destination.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of read exceeds source maximum: %zd > %zd\n" , count*eSize , destination.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	return fread( &destination[0] , eSize , count , fp );
}
template< class C >
size_t fwrite( Array< C > source , size_t eSize , size_t count , FILE* fp )
{
	if( count*eSize>source.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of write exceeds source maximum: %zd > %zd\n" , count*eSize , source.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	return fwrite( &source[0] , eSize , count , fp );
}
template< class C >
size_t fwrite( ConstArray< C > source , size_t eSize , size_t count , FILE* fp )
{
	if( count*eSize>source.maximum()*sizeof( C ) )
	{
		fprintf( stderr , "Size of write exceeds source maximum: %zd > %zd\n" , count*eSize , source.maximum()*sizeof( C ) );
		ASSERT( 0 );
		exit( 0 );
	}
	return fwrite( &source[0] , eSize , count , fp );
}
