/* Copyright (c) 1997-2007
   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_RANDOM_SUBSET_H
#define _POLYMAKE_RANDOM_SUBSET_H "$Project: polymake $$Id: RandomSubset.h 7579 2007-01-22 11:34:46Z gawrilow $"

#include <vector>

#ifndef _POLYMAKE_PLAUSIBLE_CHECKS_H
#  include <plausible_checks.h>
#endif
#ifndef _POLYMAKE_INDEXED_SUBSET_H
#  include <IndexedSubset.h>
#endif
#ifndef _POLYMAKE_RANDOM_GENERATORS_H
#  include <random_generators.h>
#endif
#ifndef _POLYMAKE_INLINES_INT_H
#  include <inlines_int.h>
#endif

namespace pm {

template <typename Iterator, typename RandomSourceIterator>
class random_subset_iterator : public iterator_traits<Iterator>::derivable_type {
   typedef typename iterator_traits<Iterator>::derivable_type super;
public:
   typedef forward_iterator_tag iterator_category;
   typedef random_subset_iterator<typename iterator_traits<Iterator>::iterator, RandomSourceIterator>
      iterator;
   typedef random_subset_iterator<typename iterator_traits<Iterator>::const_iterator, RandomSourceIterator>
      const_iterator;

   random_subset_iterator() { }
   random_subset_iterator(const iterator& it)
      : super(static_cast<const typename iterator::super&>(it)),
	random(it.random), n(it.n), k(it.k) { }

   random_subset_iterator(const Iterator& cur_arg, int n_arg, int k_arg,
			  const RandomSourceIterator& random_arg)
      : super(cur_arg), random(random_arg), n(n_arg), k(k_arg)
   {
      toss(typename iterator_traits<Iterator>::iterator_category(), 0);
   }

   random_subset_iterator(typename alt_constructor<Iterator>::arg_type& cur_arg,
			  int n_arg, int k_arg,
			  const RandomSourceIterator& random_arg)
      : super(cur_arg), random(random_arg), n(n_arg), k(k_arg)
   {
      toss(typename iterator_traits<Iterator>::iterator_category(), 0);
   }

   random_subset_iterator& operator++()
   {
      --k; --n;
      toss(typename iterator_traits<Iterator>::iterator_category(), 1);
      return *this;
   }

   random_subset_iterator operator++ (int) { random_subset_iterator copy=*this; operator++(); return copy; }

   bool at_end() const { return k==0; }
protected:
   void toss(input_iterator_tag, int incr)
   {
      if (incr) super::operator++();
      while ((*random++)*n >= k) {
	 --n; super::operator++();
      }
   }

   void toss(random_access_iterator_tag, int incr)
   {
      while ((*random++)*n >= k) {
	 --n; ++incr;
      }
      super::operator+=(incr);
   }

   RandomSourceIterator random;
   int n, k;

   template <typename,typename> friend class random_subset_iterator;
private:
   // shadow possible super:: operators
   void operator--();
   void operator+=(int);
   void operator-=(int);
   void operator+(int);
   void operator-(int);
   void operator[](int);
};

template <typename Iterator, typename RandomSourceIterator>
struct check_iterator_feature<random_subset_iterator<Iterator,RandomSourceIterator>, end_sensitive> : True { };

template <typename ContainerRef>
class RandomSubset
   : public generic_of_subset<RandomSubset<ContainerRef>, typename deref<ContainerRef>::type> {
public:
   typedef typename deref<ContainerRef>::minus_ref base_type;
   typedef typename container_traits<ContainerRef>::value_type value_type;
   typedef typename container_traits<ContainerRef>::reference reference;
   typedef typename container_traits<ContainerRef>::const_reference const_reference;

   RandomSubset(typename alias<ContainerRef>::arg_type base_arg, int k_arg)
      : base(base_arg), rg(2*log2_ceil(base_arg.size())), k(k_arg)
   {
      INDEX_CHECK((k<0 || k>get_base().size()), "RandomSubset - invalid subset size");
   }

   RandomSubset(typename alias<ContainerRef>::arg_type base_arg, int k_arg, unsigned long seed)
      : base(base_arg), rg(2*log2_ceil(base_arg.size()), seed), k(k_arg)
   {
      INDEX_CHECK((k<0 || k>get_base().size()), "RandomSubset - invalid subset size");
   }

   typedef random_subset_iterator<typename container_traits<ContainerRef>::iterator,
				  UniformRNG<Rational>::iterator>
      iterator;
   typedef random_subset_iterator<typename container_traits<ContainerRef>::const_iterator,
				  UniformRNG<Rational>::iterator>
      const_iterator;

   iterator begin()
   {
      return iterator(get_base().begin(), get_base().size(), k, rg.begin());
   }
   iterator end()
   {
      return iterator(get_base().end(), 0, 0, rg.begin());
   }
   const_iterator begin() const
   {
      return const_iterator(get_base().begin(), get_base().size(), k, rg.begin());
   }
   const_iterator end() const
   {
      return const_iterator(get_base().end(), 0, 0, rg.begin());
   }

   int size() const { return k; }
   bool empty() const { return k==0; }
protected:
   alias<ContainerRef> base;
   mutable UniformRNG<Rational> rg;
   int k;

   typename alias<ContainerRef>::reference get_base() { return base; }
   typename alias<ContainerRef>::const_reference get_base() const { return base; }
};

template <typename ContainerRef>
struct redirect_object_traits< RandomSubset<ContainerRef> >
   : object_traits<typename deref<ContainerRef>::type> {
   static const bool is_temporary=true;
};

template <typename RandomSourceIterator>
class RandomPermutation_iterator {
public:
   typedef forward_iterator_tag iterator_category;
   typedef int value_type;
   typedef const int& reference;
   typedef const int* pointer;
   typedef ptrdiff_t difference_type;
   typedef RandomPermutation_iterator iterator;
   typedef RandomPermutation_iterator const_iterator;

protected:
   void toss() {
      int i=*random % perm_index.size();
      std::swap(perm_index[i], perm_index.back());
   }

   std::vector<int> perm_index;
   RandomSourceIterator random;

public:
   RandomPermutation_iterator() { }

   RandomPermutation_iterator(const sequence& start_arg, const RandomSourceIterator& random_arg)
      : perm_index(start_arg.begin(), start_arg.end()), random(random_arg)
   {
      if (!at_end()) toss();
   }

   reference operator* () const { return perm_index.back(); }

   iterator& operator++()
   {
      perm_index.pop_back();
      if (!at_end()) toss();
      return *this;
   }

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

   bool at_end() const { return perm_index.empty(); }

   bool operator== (const iterator& it) const
   {
      return perm_index.size() == it.perm_index.size()  &&
	     std::equal(perm_index.begin(), perm_index.end(), it.perm_index.begin());
   }

   bool operator!= (const iterator& it) const { return !operator==(it); }
};

template <typename RandomSourceIterator>
struct check_iterator_feature<RandomPermutation_iterator<RandomSourceIterator>, end_sensitive> : True { };

template <typename ContainerRef=sequence,
	  bool _direct=identical_minus_const_ref<ContainerRef, sequence>::value>
class RandomPermutation {
public:
   typedef int value_type;
   typedef const int& reference;
   typedef reference const_reference;

   RandomPermutation(const sequence& base_arg)
      : base(base_arg) {}
   RandomPermutation(const sequence& base_arg, unsigned long seed)
      : base(base_arg), rg(seed) { }

   RandomPermutation(int n)
      : base(0,n) {}
   RandomPermutation(int n, unsigned long seed)
      : base(0,n), rg(seed) {}

   typedef RandomPermutation_iterator<typename UniformRNG<long>::iterator> iterator;
   typedef iterator const_iterator;

   iterator begin() const
   {
      return iterator(base, rg.begin());
   }
   iterator end() const
   {
      return iterator();
   }

   int size() const { return base.size(); }
   bool empty() const { return base.empty(); }
protected:
   sequence base;
   mutable UniformRNG<long> rg;
};

template <typename ContainerRef>
class RandomPermutation<ContainerRef, false>
   : public indexed_subset_impl< RandomPermutation<ContainerRef, false>,
				 list( Container1< ContainerRef >,
				       Container2< RandomPermutation<> > ) > {
   typedef indexed_subset_impl<RandomPermutation> _super;
protected:
   alias<ContainerRef> base;
   RandomPermutation<> perm;
public:
   RandomPermutation(typename alias<ContainerRef>::arg_type base_arg)
      : base(base_arg),
        perm(static_cast<typename alias<ContainerRef>::const_reference>(base).size()) {}
   RandomPermutation(typename alias<ContainerRef>::arg_type base_arg, unsigned long seed)
      : base(base_arg),
	perm(static_cast<typename alias<ContainerRef>::const_reference>(base).size(), seed) {}

   typename _super::container1& get_container1() { return base; }
   const typename _super::container1& get_container1() const { return base; }
   const RandomPermutation<>& get_container2() const { return perm; }
};

template <typename ContainerRef>
struct spec_object_traits< RandomPermutation<ContainerRef> >
   : spec_object_traits<is_container> {
   static const bool is_temporary=true;
};

template <typename Container> inline
RandomSubset<Container&>
select_random_subset(Container& c, int k)
{
   return RandomSubset<Container&>(c,k);
}

template <typename Container> inline
RandomSubset<Container&>
select_random_subset(Container& c, int k, unsigned long seed)
{
   return RandomSubset<Container&>(c,k,seed);
}

template <typename Container> inline
RandomSubset<const Container&>
select_random_subset(const Container& c, int k)
{
   return RandomSubset<const Container&>(c,k);
}

template <typename Container> inline
RandomSubset<const Container&>
select_random_subset(const Container& c, int k, unsigned long seed)
{
   return RandomSubset<const Container&>(c,k,seed);
}

template <typename Container> inline
RandomPermutation<Container&>
random_permutation(Container& c)
{
   return RandomPermutation<Container&>(c);
}

template <typename Container> inline
RandomPermutation<Container&>
random_permutation(Container& c, unsigned long seed)
{
   return RandomPermutation<Container&>(c,seed);
}

template <typename Container> inline
RandomPermutation<const Container&>
random_permutation(const Container& c)
{
   return RandomPermutation<const Container&>(c);
}

template <typename Container> inline
RandomPermutation<const Container&>
random_permutation(const Container& c, unsigned long seed)
{
   return RandomPermutation<const Container&>(c,seed);
}

inline
RandomPermutation<>
random_permutation(int n)
{
   return RandomPermutation<>(n);
}

inline
RandomPermutation<>
random_permutation(int n, unsigned long seed)
{
   return RandomPermutation<>(n,seed);
}

} // end namespace pm

namespace polymake {
   using pm::RandomSubset;
   using pm::select_random_subset;
   using pm::RandomPermutation;
   using pm::random_permutation;
}

#endif // _POLYMAKE_RANDOM_SUBSET_H

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