/* 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_MULTI_DIM_COUNTER_H
#define _POLYMAKE_MULTI_DIM_COUNTER_H

#include <Vector.h>

namespace pm {

template <bool left_to_right=true, typename number_type=int>
class MultiDimCounter {
public:
   typedef bidirectional_iterator_tag iterator_category;
   typedef ptrdiff_t difference_type;
   typedef Vector<number_type> value_type;
   typedef const value_type& reference;
   typedef const value_type* pointer;
   typedef MultiDimCounter iterator;
   typedef MultiDimCounter const_iterator;

protected:
   value_type my_start, my_counter, my_limits;
   bool my_at_end;

public:
   MultiDimCounter() { my_at_end = true; }

   template <typename Container>
   explicit MultiDimCounter(const Container& limits)
      : my_start(limits.size()), my_counter(my_start.size()),
	my_limits(my_start.size(), limits.begin()),
	my_at_end(my_start.empty())
   {
#ifdef POLY_DEBUG
      for (int i=0, n=my_start.size(); i<n; ++i)
	 INDEX_CHECK((my_limits[i] <= 0), "MultiDimCounter: limit[" << i << "] <= 0");
#endif
   }

   template <typename Container1, typename Container2>
   MultiDimCounter(const Container1& start, const Container2& limits)
      : my_start(start.size(), start.begin()), my_counter(my_start),
	my_limits(my_start.size(), limits.begin()),
	my_at_end(my_start.empty())
   {
#ifdef POLY_DEBUG
      for (int i=0, n=my_start.size(); i<n; ++i)
	 INDEX_CHECK((my_limits[i] <= my_start[i]), "MultiDimCounter: limit[" << i << "] <= my_start[" << i << "]");
#endif
   }

   MultiDimCounter& operator++ ();
   MultiDimCounter& operator-- ();
   const iterator operator++ (int) { iterator copy=*this; operator++(); return copy; }
   const iterator operator-- (int) { iterator copy=*this; operator--(); return copy; }

   bool operator== (const MultiDimCounter& count) const
   {
      return count->size() == my_counter.size()
	     && std::equal(entire(my_counter), count->begin());
   }

   bool operator!= (const MultiDimCounter& count) const { return !operator==(count); }

   reference operator* () const { return my_counter; }
   pointer operator-> () const { return &my_counter; }

   void rewind()
   {
      my_counter=my_start;
      my_at_end=my_counter.empty();
   }

   template <typename Container>
   void set(const Container& values) {
#ifdef POLY_DEBUG
      DIMENSION_CHECK((values.size() != my_limits.size()), "MultiDimCounter::set - dimension mismatch");

      typename value_type::iterator l=my_limits.begin();
      typename Container::const_iterator v=values.begin();
      for (typename Entire<value_type>::iterator s=entire(my_start);  !s.at_end();  ++s, ++l, ++v)
	 INDEX_CHECK((*v < *s || *v >= *l),
		     "MultiDimCounter::set - value[" << s-my_start.begin() << "] out of range");
#endif

      std::copy(values.begin(), entire(my_counter));
   }

   void set_digit(int digit, typename function_argument<number_type>::type value)
   {
      INDEX_CHECK((digit >= my_limits.size()), "MultiDimCounter::set_digit - index out of range");
      DIMENSION_CHECK((value < my_start[digit] || value >= my_limits[digit]),
		      "MultiDimCounter::set_digit - value out of range");

      my_counter[digit] = value;
   }

   bool at_end() const { return my_at_end; }

   reference limits() const { return my_limits; }
};

template <bool left_to_right, typename number_type>
struct check_iterator_feature<MultiDimCounter<left_to_right, number_type>, end_sensitive> : True { };

template <bool left_to_right, typename number_type>
struct check_iterator_feature<MultiDimCounter<left_to_right, number_type>, rewindable> : True { };

} // end namespace pm

namespace polymake {
   using pm::MultiDimCounter;
}

#include <MultiDimCounter.tcc>

#endif // _POLYMAKE_MULTI_DIM_COUNTER_H

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