// $Id: combination.h,v 1.1 2011/01/07 03:42:28 samn Exp $ 
// Combination algorithm implementation
//
// Copyright (C) 2004 - 2006, BenBear
//
// More information in http://www.bxmy.org

// This file is an algorithm of the combination. 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, 59 Temple Place - Suite 330, Boston,
// MA 02111-1307, USA.

#ifndef __btb_combination_hpp_def
#define __btb_combination_hpp_def

#include <algorithm>

namespace btb
{
  ////////////////////////////////////////////////////////////////////
  // combination for STL permutation
  // 
  // combination_init    (first, middle, last)
  // combination_adjust  (first, middle, last)
  // combination         (first1, last1, first2, last2)
  // next_combination    (first, middle, last)
  // prev_combination    (first, middle, last)
  ////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////
  // twice_merge: merge [first1, last1) and [first2, last2), then fill
  //              the min element to [first1, last1), left to [first2,
  //              last2)
  ////////////////////////////////////////////////////////////////////
  template <typename Iter>
  void
  twice_merge (Iter first1, Iter last1, Iter first2, Iter last2)
  {
    typedef typename std::iterator_traits<Iter>::value_type value_type;
    typedef typename std::iterator_traits<Iter>::difference_type diff_t;

    if ((first1 == last1) || (first2 == last2))
      return;
    first1 = std::upper_bound(first1, last1, *first2);
    if (first1 == last1)
      return;
    last2 = std::lower_bound(first2, last2, *(last1-1));
    if (first2 == last2)
      return;

    diff_t len1 = std::distance(first1, last1);
    diff_t len2 = std::distance(first2, last2);
    bool min1 = len1 < len2;
    diff_t lent = min1 ? len1 : len2;
    value_type *tmp = new value_type[lent];

    if (min1)
      {
	std::copy(first1, last1, tmp);
	Iter p = first2;
	Iter q = tmp;
	Iter i;
	for (i = first1; i != last1; ++i)
	  if (*p < *q)
	    *i = *p++;
	  else
	    *i = *q++;
	for (i = first2; (i != last2) && (p != last2); ++i)
	  if (*p < *q)
	    *i = *p++;
	  else
	    *i = *q++;
	for (; i != last2; ++i, ++q)
	  *i = *q;
      }
    else
      {
	std::copy(first2, last2, tmp);
	Iter p = last1;
	Iter q = tmp+lent;
	Iter i;
	for (/*Iter*/ i = last2; i != first2;)
	  if (*(p-1) < *(q-1))
	    *--i = *--q;
	  else
	    *--i = *--p;
	for (i = last1; (i != first1) && (p != first1);)
	  if (*(p-1) < *(q-1))
	    *--i = *--q;
	  else
	    *--i = *--p;
	for (; i != first1;)
	  *--i = *--q;
      }

    delete[] tmp;
  }

  ///////////////////////////////////////////////////////////////////
  // __combination_sort: merge sort the [first, last)
  ///////////////////////////////////////////////////////////////////
  template <typename Iter>
  void
  __combination_sort (Iter first, Iter last)
  {
    typedef typename std::iterator_traits<Iter>::difference_type diff_t;
    diff_t len = std::distance(first, last);
    if (len <= 1)
      return;

    if (len == 2)
      {
	if (*first > *--last)
	  std::iter_swap (first, last);
      }
    else
      {
	Iter middle = first;
	std::advance(middle, len / 2);
	__combination_sort (first, middle);
	__combination_sort (middle, last);
	twice_merge (first, middle, middle, last);
      }
  }

  //////////////////////////////////////////////////////////////////////
  // combination_init: init the (first, midle, last) to the min or the
  //                   max combination
  //////////////////////////////////////////////////////////////////////
  template <typename Iter>
  void
  combination_init (Iter first, Iter middle, Iter last, bool min = true)
  {
    __combination_sort (first, middle);
    __combination_sort (middle, last);
    if (min)
      twice_merge (first, middle, middle, last);
    else
      twice_merge (middle, last, first, middle);
  }

  //////////////////////////////////////////////////////////////////////
  // combination_adjust: make the (first, middle, last) to a right
  //                     combination. [first, middle) are the elements
  //                     selected in, [middle, last) are selected out
  //////////////////////////////////////////////////////////////////////
  template <typename Iter>
  void
  combination_adjust (Iter first, Iter middle, Iter last)
  {
    __combination_sort (first, middle);
    __combination_sort (middle, last);
  }

  /////////////////////////////////////////////////////////////////////
  // combination: get next combination.
  //
  // [first1, last1): the elements selected in \\
  // [first2, last2): the elements selected out
  /////////////////////////////////////////////////////////////////////
  template <typename Iter>
  bool
  combination (Iter first1, Iter last1, Iter first2, Iter last2)
  {
    if ((first1 == last1) || (first2 == last2))
      return false;

    Iter qmax = last2;
    --qmax;
    Iter pout1 = std::lower_bound(first1, last1, *qmax);
    bool fin = pout1 == first1;
    Iter left1, left2;
    if (!fin)
      {
	Iter pout = pout1;
	--pout;
	Iter qin = std::upper_bound(first2, last2, *pout);
	std::iter_swap (pout, qin);
	left1 = pout;
	++left1;
	left2 = qin;
	++left2;
      }
    else
      {
	left1 = first1;
	left2 = first2;
      }
    twice_merge (left1, last1, left2, last2);
    return !fin;
  }

  /////////////////////////////////////////////////////////////////////
  // next_combination: get next combination.
  //
  // [first, middle): the elements selected in \\
  // [middle, last): the elements selected out
  /////////////////////////////////////////////////////////////////////
  template <typename Iter>
  inline bool
  next_combination (Iter first, Iter middle, Iter last)
  {
    return combination (first, middle, middle, last);
  }

  /////////////////////////////////////////////////////////////////////
  // prev_combination: get prev combination.
  //
  // [first, middle): the elements selected in \\
  // [middle, last): the elements selected out
  /////////////////////////////////////////////////////////////////////
  template <typename Iter>
  inline bool 
  prev_combination (Iter first, Iter middle, Iter last)
  {
    return combination (first, middle, middle, last);
  }
}

#endif