//  (C) Copyright Jeremiah Willcock 2004 
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_FENCED_PRIORITY_QUEUE_HPP
#define BOOST_FENCED_PRIORITY_QUEUE_HPP

#include <vector>
#include <queue>
#include <functional>
#include <boost/pending/queue.hpp>

// Fenced priority queue
// Jeremiah Willcock

// This class implements a fenced priority queue.  This is similar to
// a normal priority queue (sorts its members, and only returns the
// first), except that members cannot be sorted around a "fence" that
// can be placed into the buffer.  This fence is inserted using the
// fence() member function or (possibly) implicitly by the top() and
// pop() methods, and is removed automatically when the elements
// around it are popped.

// The implementation is as follows:  Q is an unsorted queue that
// contains the already-sorted list data, and PQ is a priority queue
// that contains new elements (since the last fence) that have yet to
// be sorted.  New elements are inserted into PQ, and a fence moves
// all elements in PQ into the back of Q in sorted order.  Elements
// are then popped from the front of Q, and if that is empty the front
// of PQ.

namespace boost {

  template<class T, class Compare = std::less<T>, bool implicit_fence = true,
           class Buffer = boost::queue<T> >
  class fenced_priority_queue {
  public:
    typedef T value_type;
    typedef typename Buffer::size_type size_type;

    fenced_priority_queue(const Compare _comp = Compare() ) 
      : PQ(_comp) {} 
    
    void push(const T& data);
    void pop(void);
    T& top(void);
    const T& top(void) const;
    size_type size(void) const;
    bool empty(void) const;
    void fence(void);
    
  private:
    void fence(void) const;

    //let them mutable to allow const version of top and the same 
    //semantics with non-constant version. Rich Lee
    mutable std::priority_queue<T, std::vector<T>, Compare> PQ;
    mutable Buffer Q;
  };
  
  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline void 
  fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  push(const T &t) {
    // Push a new element after the last fence.  This puts it into the
    // priority queue to be sorted with all other elements in its
    // partition.
    PQ.push(t);
  }

  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline void fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  pop(void) {
    // Pop one element from the front of the queue.  Removes from the
    // already-sorted part of the queue if it is non-empty, otherwise
    // removes from the new-element priority queue.  Runs an implicit
    // "fence" operation if the implicit_fence template argument is
    // true.
    if (implicit_fence) fence();
    if ( !Q.empty() )
      Q.pop();
    else
      PQ.pop();
  }

  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline T& fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  top(void) {
    // Get the top element from the queue.  This element comes from Q if
    // possible, otherwise from PQ.  Causes an implicit "fence"
    // operation if the implicit_fence template argument is true.
    if (implicit_fence) fence();
    if ( !Q.empty() )
      return Q.top();
    else
      //std::priority_queue only have const version of top. Rich Lee
      return const_cast<T&>(PQ.top());
  }

  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline const T&
  fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  top(void) const {
    if (implicit_fence) fence();
    if ( !Q.empty() )
      return Q.top();
    else
      return PQ.top();
  }

  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline typename fenced_priority_queue<T, Compare, implicit_fence, Buffer>::size_type 
  fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  size(void) const {
    // Returns the size of the queue (both parts together).
    return Q.size() + PQ.size();
  }

  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline bool 
  fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  empty(void) const {
    // Returns if the queue is empty, i.e. both parts are empty.
    return Q.empty() && PQ.empty();
  }
  
  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline void 
  fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  fence(void) {
    // Perform a fence operation.  Remove elements from PQ in sorted
    // order and insert them in the back of Q.
    while ( !PQ.empty() ) {
      Q.push(PQ.top());
      PQ.pop();
    }
  }
  template<class T, class Compare, bool implicit_fence, class Buffer>
  inline void 
  fenced_priority_queue<T, Compare, implicit_fence, Buffer>::
  fence(void) const {
    // Perform a fence operation.  Remove elements from PQ in sorted
    // order and insert them in the back of Q.
    while ( !PQ.empty() ) {
      Q.push(PQ.top());
      PQ.pop();
    }
  }

}  
#endif /* BOOST_FENCED_PRIORITY_QUEUE_HPP */
