// class template array -*- C++ -*-

// Copyright (C) 2007 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library 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 2, or (at your option)
// any later version.

// This library 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 library; see the file COPYING.  If not, write to the Free
// Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
// USA.

// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.

/** @file tr1/array
 *  This is a TR1 C++ Library header.
 */
#ifndef _array_hpp_
#define _array_hpp_
#include <algorithm>
#include <stdexcept>
#include <cstddef>
#include <iterator>
#include <cstdlib>
/*
 * This file defines the template array standard container
 * within the std::tr1 and std namespaces (C++11).
 * It also incorporates array subscripting bound checks
 * in case of _GLIBCXX_DEBUG defined.
 *
 * if ARRAY_THROW__ is defined, then it throws an out_of_range
 * exception, else it prints a message in cerr and aborts (unless
 * ARRAY_NOABORT__ is defined)
 */
//--------------------------------------------------------------------------
#ifdef _GLIBCXX_DEBUG
#include <iostream>
#include <string>
#include <sstream>
#include <typeinfo>
#include <cstdio>
#if defined __GNUC__ //&& ( defined __linux__ || defined __MINGW32__ )
#include <cxxabi.h>
#define RETADDR (__builtin_extract_return_addr(__builtin_return_address(0)))
#if defined __linux__
// man program_invocation_name // glibc
extern "C" char* program_invocation_name;
#define PROGRAM_NAME__ program_invocation_name
#elif defined __MINGW32__
extern "C" char** _argv;
#define PROGRAM_NAME__ _argv[0]
#else
extern "C" char* __progname_full;
#define PROGRAM_NAME__ __progname_full
#endif
#else
#define RETADDR (0)
#endif
#ifdef __GNUC__
extern "C" {
	FILE* popen(const char *cmd, const char *mode);
	int pclose(FILE *stream);
}
#endif
#endif
//--------------------------------------------------------------------------
#ifndef NOINLINE__
#if defined __GNUC__ && defined _GLIBCXX_DEBUG && ! defined ARRAY_THROW__
#define NOINLINE__ __attribute__((noinline))
#else
#define NOINLINE__
#endif
#endif
//--------------------------------------------------------------------------
namespace std {
	namespace tr1 {
#ifdef _GLIBCXX_DEBUG
		namespace auxiliar {
			inline std::string demangle__(const char* mangled_name)
			{
				std::string name(mangled_name);
#ifdef __GNUC__
				char* nm = abi::__cxa_demangle(mangled_name, NULL,
											   NULL, NULL);
				if (nm != NULL) {
					name = nm;
					std::free(nm);
				}
#endif
				return name;
			}
			//--------------------------------------------------------------
			inline void __print_filename_line(const void* caller)
			{
#ifdef __GNUC__
				if (caller == 0) {
					std::cerr << "Error: no es posible calcular la direccion de retorno"
							  << " [" << PROGRAM_NAME__ << "]"
							  << std::endl;
				} else {
					const void* return_address = (const char*)caller-1;
					char cmd[1024];
					std::FILE *fp;
					std::snprintf(cmd, 1024, "addr2line -e \"%s\" %p",
								  PROGRAM_NAME__, return_address);
					/*----------------------------*/
					fp = popen(cmd, "r");
					if (fp) {
						char filename[256] = "??";
						int line = 0;
						if (std::fscanf(fp, " %255[^: ]:%d",
										filename, &line) > 0) {
							if ((filename[0] != '?') && (line != 0)) {
								std::cerr << filename << ":" << line
										  << std::endl;
							} else {
								std::cerr << "Error: debe compilar con la opcion -g"
										  << std::endl;
							}
							pclose(fp);
						} else {
							pclose(fp);
							system(cmd);
						}
					} else {
						system(cmd);
					}
				}
#endif
			}
			//--------------------------------------------------------------
#undef PROGRAM_NAME__
		}
#endif
		//------------------------------------------------------------------
		struct ArrayIndexError : public std::out_of_range { 
			explicit ArrayIndexError(const char* w):std::out_of_range(w) {}
		};
		//------------------------------------------------------------------
		/// NB: Requires complete type _Tp.
		template<typename _Tp, std::size_t _Nm>
		struct array {
			//------------------------
			typedef _Tp 	    			      value_type;
			typedef value_type&                   reference;
			typedef const value_type&             const_reference;
			typedef value_type*          		  iterator;
			typedef const value_type*			  const_iterator;
			typedef std::size_t                   size_type;
			typedef std::ptrdiff_t                difference_type;
			typedef std::reverse_iterator<iterator>	      reverse_iterator;
			typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
			//------------------------
			// Support for zero-sized arrays mandatory.
			value_type _M_instance[_Nm ? _Nm : 1];
			//------------------------
			// No explicit construct/copy/destroy for aggregate type.
			//------------------------
#ifdef _GLIBCXX_INCLUDE_AS_CXX0X
			// DR 776.
			void fill(const value_type& __u) {
				std::fill_n(begin(), size(), __u);
			}
#else
			void assign(const value_type& __u) {
				std::fill_n(begin(), size(), __u);
			}
#endif
			void  swap(array& __other) {
				std::swap_ranges(begin(), end(), __other.begin());
			}
			//------------------------
			// Iterators.
			iterator begin() {
				return iterator(&_M_instance[0]);
			}
			const_iterator begin() const {
				return const_iterator(&_M_instance[0]);
			}
			iterator end() {
				return iterator(&_M_instance[_Nm]);
			}
			const_iterator end() const {
				return const_iterator(&_M_instance[_Nm]);
			}
			reverse_iterator rbegin() {
				return reverse_iterator(end());
			}
			const_reverse_iterator rbegin() const {
				return const_reverse_iterator(end());
			}
			reverse_iterator rend() {
				return reverse_iterator(begin());
			}
			const_reverse_iterator rend() const {
				return const_reverse_iterator(begin());
			}
			//------------------------
#ifdef _GLIBCXX_INCLUDE_AS_CXX0X
			const_iterator cbegin() const {
				return const_iterator(&_M_instance[0]);
			}
			const_iterator cend() const {
				return const_iterator(&_M_instance[_Nm]);
			}
			const_reverse_iterator crbegin() const {
				return const_reverse_iterator(end());
			}
			const_reverse_iterator crend() const {
				return const_reverse_iterator(begin());
			}
#endif
			//------------------------
			// Capacity.
			size_type size() const {
				return _Nm;
			}
			size_type max_size() const {
				return _Nm;
			}
			bool empty() const {
				return size() == 0;
			}
			//------------------------
			// Element access.
			NOINLINE__
			reference operator[](size_type __n) {
#ifdef _GLIBCXX_DEBUG
				if (__n >= _Nm) {
#ifdef ARRAY_THROW__
					std::ostringstream msg;
					msg << "std::tr1::array<"
						<<std::tr1::auxiliar::demangle__(typeid(value_type).name())
						<<", "<<_Nm<<">::operator["
						<< long(__n) << "]";
					throw ArrayIndexError(msg.str().c_str());
#else
					std::cerr << "\nError: out-of-bounds array index "
							  << long(__n) << " in std::tr1::array<"
							  <<std::tr1::auxiliar::demangle__(typeid(value_type).name())
							  <<", "<<_Nm<<">::operator[]"<< std::endl;
					std::tr1::auxiliar::__print_filename_line(RETADDR);
#ifndef ARRAY_NOABORT__
					std::abort();
#endif
#endif
				}
#endif
				return _M_instance[__n];
			}
			NOINLINE__
			const_reference operator[](size_type __n) const {
#ifdef _GLIBCXX_DEBUG
				if (__n >= _Nm) {
#ifdef ARRAY_THROW__
					std::ostringstream msg;
					msg << "std::tr1::array<"
						<<std::tr1::auxiliar::demangle__(typeid(value_type).name())
						<<", "<<_Nm<<">::operator["
						<< long(__n) << "]";
					throw ArrayIndexError(msg.str().c_str());
#else
					std::cerr << "\nError: out-of-bounds array index "
							  << long(__n) << " in std::tr1::array<"
							  <<std::tr1::auxiliar::demangle__(typeid(value_type).name())
							  <<", "<<_Nm<<">::operator[]"<< std::endl;
					std::tr1::auxiliar::__print_filename_line(RETADDR);
#ifndef ARRAY_NOABORT__
					std::abort();
#endif
#endif
				}
#endif
				return _M_instance[__n];
			}
			reference at(size_type __n) {
				if (__n >= _Nm) {
					throw std::out_of_range("array::at");
				}
				return _M_instance[__n];
			}
			const_reference at(size_type __n) const {
				if (__n >= _Nm) {
					throw std::out_of_range("array::at");
				}
				return _M_instance[__n];
			}
			//------------------------
			reference front() {
				return *begin();
			}
			const_reference front() const {
				return *begin();
			}
			reference back() {
				return _Nm ? *(end() - 1) : *end();
			}
			const_reference back() const {
				return _Nm ? *(end() - 1) : *end();
			}
			_Tp* data() {
				return &_M_instance[0];
			}
			const _Tp* data() const {
				return &_M_instance[0];
			}
			//------------------------
		};
		//----------------------------
		// Array comparisons.
		template<typename _Tp, std::size_t _Nm>
		inline bool operator==(const array<_Tp, _Nm>& __one,
							   const array<_Tp, _Nm>& __two) {
			return std::equal(__one.begin(), __one.end(), __two.begin());
		}
		template<typename _Tp, std::size_t _Nm>
		inline bool operator!=(const array<_Tp, _Nm>& __one,
							   const array<_Tp, _Nm>& __two) {
			return !(__one == __two);
		}
		template<typename _Tp, std::size_t _Nm>
		inline bool operator<(const array<_Tp, _Nm>& __a,
							  const array<_Tp, _Nm>& __b) { 
			return std::lexicographical_compare(__a.begin(), __a.end(),
												__b.begin(), __b.end()); 
		}
		template<typename _Tp, std::size_t _Nm>
		inline bool operator>(const array<_Tp, _Nm>& __one,
							  const array<_Tp, _Nm>& __two) {
			return __two < __one;
		}
		template<typename _Tp, std::size_t _Nm>
		inline bool operator<=(const array<_Tp, _Nm>& __one,
							   const array<_Tp, _Nm>& __two) {
			return !(__one > __two);
		}
		template<typename _Tp, std::size_t _Nm>
		inline bool operator>=(const array<_Tp, _Nm>& __one,
							   const array<_Tp, _Nm>& __two) {
			return !(__one < __two);
		}
		//----------------------------
		// Specialized algorithms [6.2.2.2].
		template<typename _Tp, std::size_t _Nm>
		inline void swap(array<_Tp, _Nm>& __one, array<_Tp, _Nm>& __two) {
			std::swap_ranges(__one.begin(), __one.end(), __two.begin());
		}
		//----------------------------
		// Tuple interface to class template array [6.2.2.5].
		/// tuple_size
		template<typename _Tp> 
		class tuple_size;
		/// tuple_element
		template<int _Int, typename _Tp> 
		class tuple_element;
		template<typename _Tp, std::size_t _Nm>
		struct tuple_size<array<_Tp, _Nm> > {
			static const int value = _Nm;
		};
		//template<typename _Tp, std::size_t _Nm>
		//const int tuple_size<array<_Tp, _Nm> >::value;
		template<int _Int, typename _Tp, std::size_t _Nm>
		struct tuple_element<_Int, array<_Tp, _Nm> > {
			typedef _Tp type;
		};
		template<int _Int, typename _Tp, std::size_t _Nm>
		inline _Tp& get(array<_Tp, _Nm>& __arr) {
			return __arr[_Int];
		}
		template<int _Int, typename _Tp, std::size_t _Nm>
		inline const _Tp& get(const array<_Tp, _Nm>& __arr) {
			return __arr[_Int];
		}
		//----------------------------
	}
#if __cplusplus < 201103L
	using std::tr1::array;
	using std::tr1::ArrayIndexError;
#endif
}
#endif
