250 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| /* Copyright 2003-2015 Joaquin M Lopez Munoz.
 | |
|  * 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)
 | |
|  *
 | |
|  * See http://www.boost.org/libs/multi_index for library home page.
 | |
|  */
 | |
| 
 | |
| #ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_MATCHER_HPP
 | |
| #define BOOST_MULTI_INDEX_DETAIL_INDEX_MATCHER_HPP
 | |
| 
 | |
| #if defined(_MSC_VER)
 | |
| #pragma once
 | |
| #endif
 | |
| 
 | |
| #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
 | |
| #include <algorithm>
 | |
| #include <boost/noncopyable.hpp>
 | |
| #include <boost/multi_index/detail/auto_space.hpp>
 | |
| #include <boost/multi_index/detail/raw_ptr.hpp>
 | |
| #include <cstddef>
 | |
| #include <functional>
 | |
| 
 | |
| namespace boost{
 | |
| 
 | |
| namespace multi_index{
 | |
| 
 | |
| namespace detail{
 | |
| 
 | |
| /* index_matcher compares a sequence of elements against a
 | |
|  * base sequence, identifying those elements that belong to the
 | |
|  * longest subsequence which is ordered with respect to the base.
 | |
|  * For instance, if the base sequence is:
 | |
|  *
 | |
|  *   0 1 2 3 4 5 6 7 8 9
 | |
|  *
 | |
|  * and the compared sequence (not necesarilly the same length):
 | |
|  *
 | |
|  *   1 4 2 3 0 7 8 9
 | |
|  *
 | |
|  * the elements of the longest ordered subsequence are:
 | |
|  *
 | |
|  *   1 2 3 7 8 9
 | |
|  * 
 | |
|  * The algorithm for obtaining such a subsequence is called
 | |
|  * Patience Sorting, described in ch. 1 of:
 | |
|  *   Aldous, D., Diaconis, P.: "Longest increasing subsequences: from
 | |
|  *   patience sorting to the Baik-Deift-Johansson Theorem", Bulletin
 | |
|  *   of the American Mathematical Society, vol. 36, no 4, pp. 413-432,
 | |
|  *   July 1999.
 | |
|  *   http://www.ams.org/bull/1999-36-04/S0273-0979-99-00796-X/
 | |
|  *   S0273-0979-99-00796-X.pdf
 | |
|  *
 | |
|  * This implementation is not fully generic since it assumes that
 | |
|  * the sequences given are pointed to by index iterators (having a
 | |
|  * get_node() memfun.)
 | |
|  */
 | |
| 
 | |
| namespace index_matcher{
 | |
| 
 | |
| /* The algorithm stores the nodes of the base sequence and a number
 | |
|  * of "piles" that are dynamically updated during the calculation
 | |
|  * stage. From a logical point of view, nodes form an independent
 | |
|  * sequence from piles. They are stored together so as to minimize
 | |
|  * allocated memory.
 | |
|  */
 | |
| 
 | |
| struct entry
 | |
| {
 | |
|   entry(void* node_,std::size_t pos_=0):node(node_),pos(pos_){}
 | |
| 
 | |
|   /* node stuff */
 | |
| 
 | |
|   void*       node;
 | |
|   std::size_t pos;
 | |
|   entry*      previous;
 | |
|   bool        ordered;
 | |
| 
 | |
|   struct less_by_node
 | |
|   {
 | |
|     bool operator()(
 | |
|       const entry& x,const entry& y)const
 | |
|     {
 | |
|       return std::less<void*>()(x.node,y.node);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /* pile stuff */
 | |
| 
 | |
|   std::size_t pile_top;
 | |
|   entry*      pile_top_entry;
 | |
| 
 | |
|   struct less_by_pile_top
 | |
|   {
 | |
|     bool operator()(
 | |
|       const entry& x,const entry& y)const
 | |
|     {
 | |
|       return x.pile_top<y.pile_top;
 | |
|     }
 | |
|   };
 | |
| };
 | |
| 
 | |
| /* common code operating on void *'s */
 | |
| 
 | |
| template<typename Allocator>
 | |
| class algorithm_base:private noncopyable
 | |
| {
 | |
| protected:
 | |
|   algorithm_base(const Allocator& al,std::size_t size):
 | |
|     spc(al,size),size_(size),n_(0),sorted(false)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   void add(void* node)
 | |
|   {
 | |
|     entries()[n_]=entry(node,n_);
 | |
|     ++n_;
 | |
|   }
 | |
| 
 | |
|   void begin_algorithm()const
 | |
|   {
 | |
|     if(!sorted){
 | |
|       std::sort(entries(),entries()+size_,entry::less_by_node());
 | |
|       sorted=true;
 | |
|     }
 | |
|     num_piles=0;
 | |
|   }
 | |
| 
 | |
|   void add_node_to_algorithm(void* node)const
 | |
|   {
 | |
|     entry* ent=
 | |
|       std::lower_bound(
 | |
|         entries(),entries()+size_,
 | |
|         entry(node),entry::less_by_node()); /* localize entry */
 | |
|     ent->ordered=false;
 | |
|     std::size_t n=ent->pos;                 /* get its position */
 | |
| 
 | |
|     entry dummy(0);
 | |
|     dummy.pile_top=n;
 | |
| 
 | |
|     entry* pile_ent=                        /* find the first available pile */
 | |
|       std::lower_bound(                     /* to stack the entry            */
 | |
|         entries(),entries()+num_piles,
 | |
|         dummy,entry::less_by_pile_top());
 | |
| 
 | |
|     pile_ent->pile_top=n;                   /* stack the entry */
 | |
|     pile_ent->pile_top_entry=ent;        
 | |
| 
 | |
|     /* if not the first pile, link entry to top of the preceding pile */
 | |
|     if(pile_ent>&entries()[0]){ 
 | |
|       ent->previous=(pile_ent-1)->pile_top_entry;
 | |
|     }
 | |
| 
 | |
|     if(pile_ent==&entries()[num_piles]){    /* new pile? */
 | |
|       ++num_piles;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void finish_algorithm()const
 | |
|   {
 | |
|     if(num_piles>0){
 | |
|       /* Mark those elements which are in their correct position, i.e. those
 | |
|        * belonging to the longest increasing subsequence. These are those
 | |
|        * elements linked from the top of the last pile.
 | |
|        */
 | |
| 
 | |
|       entry* ent=entries()[num_piles-1].pile_top_entry;
 | |
|       for(std::size_t n=num_piles;n--;){
 | |
|         ent->ordered=true;
 | |
|         ent=ent->previous;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool is_ordered(void * node)const
 | |
|   {
 | |
|     return std::lower_bound(
 | |
|       entries(),entries()+size_,
 | |
|       entry(node),entry::less_by_node())->ordered;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   entry* entries()const{return raw_ptr<entry*>(spc.data());}
 | |
| 
 | |
|   auto_space<entry,Allocator> spc;
 | |
|   std::size_t                 size_;
 | |
|   std::size_t                 n_;
 | |
|   mutable bool                sorted;
 | |
|   mutable std::size_t         num_piles;
 | |
| };
 | |
| 
 | |
| /* The algorithm has three phases:
 | |
|  *   - Initialization, during which the nodes of the base sequence are added.
 | |
|  *   - Execution.
 | |
|  *   - Results querying, through the is_ordered memfun.
 | |
|  */
 | |
| 
 | |
| template<typename Node,typename Allocator>
 | |
| class algorithm:private algorithm_base<Allocator>
 | |
| {
 | |
|   typedef algorithm_base<Allocator> super;
 | |
| 
 | |
| public:
 | |
|   algorithm(const Allocator& al,std::size_t size):super(al,size){}
 | |
| 
 | |
|   void add(Node* node)
 | |
|   {
 | |
|     super::add(node);
 | |
|   }
 | |
| 
 | |
|   template<typename IndexIterator>
 | |
|   void execute(IndexIterator first,IndexIterator last)const
 | |
|   {
 | |
|     super::begin_algorithm();
 | |
| 
 | |
|     for(IndexIterator it=first;it!=last;++it){
 | |
|       add_node_to_algorithm(get_node(it));
 | |
|     }
 | |
| 
 | |
|     super::finish_algorithm();
 | |
|   }
 | |
| 
 | |
|   bool is_ordered(Node* node)const
 | |
|   {
 | |
|     return super::is_ordered(node);
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   void add_node_to_algorithm(Node* node)const
 | |
|   {
 | |
|     super::add_node_to_algorithm(node);
 | |
|   }
 | |
| 
 | |
|   template<typename IndexIterator>
 | |
|   static Node* get_node(IndexIterator it)
 | |
|   {
 | |
|     return static_cast<Node*>(it.get_node());
 | |
|   }
 | |
| };
 | |
| 
 | |
| } /* namespace multi_index::detail::index_matcher */
 | |
| 
 | |
| } /* namespace multi_index::detail */
 | |
| 
 | |
| } /* namespace multi_index */
 | |
| 
 | |
| } /* namespace boost */
 | |
| 
 | |
| #endif
 | 
