/* Copyright (c) 1997-2006
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   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 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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.
*/

#ifndef _POLYMAKE_ARRAY_H
#define _POLYMAKE_ARRAY_H "$Project: polymake $$Id: Array.h 7459 2006-11-09 11:34:48Z gawrilow $"

#ifndef _POLYMAKE_PLAUSIBLE_CHECKS_H
#  include <plausible_checks.h>
#endif
#ifndef _POLYMAKE_SHARED_OBJECT_H
#  include <shared_object.h>
#endif
#ifndef _POLYMAKE_CONVERTERS_H
#  include <converters.h>
#endif
#include <limits>

namespace pm {

template <typename E, typename SharedParams=void>
class Array {
protected:
   /// data array
   shared_array<E, SharedParams> data;

public:
   typedef E value_type;
   typedef E& reference;
   typedef const E& const_reference;

   Array() {}

   explicit Array(int n) : data(n) {}

   Array(int n, const E& init) : data(n, constant(init).begin()) {}

   template <typename Iterator>
   Array(int n, Iterator src, typename enable_if<void*, !std::numeric_limits<Iterator>::is_specialized>::type=0)
      : data(n,src) {}

   template <typename Iterator>
   Array(Iterator first, Iterator last, typename enable_if<void*, !std::numeric_limits<Iterator>::is_specialized>::type=0)
      : data(distance(first,last), first) {}

   template <int _size>
   explicit Array(const E (&a)[_size]) : data(_size, a+0) {}

   template <typename E2, int _size>
   explicit Array(const E2 (&a)[_size]) : data(_size, attach_converter<E>(array2container(a)).begin()) { }

   template <typename Container>
   explicit Array(const Container& src, typename enable_if<void*, identical<typename object_traits<Container>::model,
									    is_container>::value>::type=0)
      : data(src.size(), ensure(src, (dense*)0).begin()) {}

   int size() const { return data.size(); }

   bool empty() const { return size()==0; }

   /// Truncate to zero size
   void clear() { data.clear(); }

   /// Change the size, initialize appended elements with default constructor
   void resize(int n) { data.resize(n); }

   void resize(int n, const E& x)
   {
      data.append(n-size(), constant(x).begin());
   }

   void assign(int n, const E& x)
   {
      data.assign(n, constant(x).begin());
   }

   template <typename Iterator>
   void append(int n, Iterator src)
   {
      data.append(n, src);
   }

   void fill(const E& x)
   {
      data.assign(size(), constant(x).begin());
   }

   template <int n>
   Array& operator= (const E (&a)[n])
   {
      data.assign(n,a+0);
      return *this;
   }

   void swap(Array& a) { data.swap(a.data); }

   friend void relocate(Array* from, Array* to)
   {
      pm::relocate(&from->data, &to->data);
   }

   E& operator[] (int i)
   {
      INDEX_CHECK(i>=size(), "operator[] - array index out of range");
      return (*data)[i];
   }

   const E& operator[] (int i) const
   {
      INDEX_CHECK(i>=size(), "operator[] - array index out of range");
      return (*data)[i];
   }

   typedef E* iterator;
   typedef const E* const_iterator;

   iterator begin() { return *data; }
   iterator end() { return *data+size(); }
   const_iterator begin() const { return *data; }
   const_iterator end() const { return *data+size(); }

   typedef std::reverse_iterator<iterator> reverse_iterator;
   typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

   reverse_iterator rbegin() { return reverse_iterator(end()); }
   reverse_iterator rend() { return reverse_iterator(begin()); }
   const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
   const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }

   reference front() { return (*data)[0]; }
   const_reference front() const { return (*data)[0]; }
   reference back() { return (*data)[size()-1]; }
   const_reference back() const { return (*data)[size()-1]; }
};

template <typename E>
struct spec_object_traits< Array<E> >
   : spec_object_traits<is_container> { };

} // end namespace pm

namespace polymake {
   using pm::Array;
}

namespace std {
   template <typename E> inline
   void swap(pm::Array<E>& a1, pm::Array<E>& a2) { a1.swap(a2); }
}

#endif // _POLYMAKE_ARRAY_H

// Local Variables:
// mode:C++
// End:
