/* * Copyright 2008 by Tommi Rantala * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /* The loser tree is defined in: * * Donald Knuth: The Art of Computer Programming, * Volume III: Sorting and Searching, 1973, * section 5.4.1, page 253 * * It is used to implement multi-way merging. */ /* Example with 8 streams: * * _nodes: * Each node contains an index to the _streams array. The winner (smallest * item) is stored in position 0. Other nodes contain the loser of each * comparison. * * <0> * | * <1> * / \ * / \ * / \ * <2> <3> * / \ / \ * / \ / \ * <4> <5> <6> <7> * * _streams: * 0:(T*,n), 1:(T*,n), ..., 7:(T*,n) * * Both structures contain exactly 2^k items. Empty streams are inserted if * required. */ namespace rantala { static inline unsigned log2(unsigned n) { return 8*sizeof(unsigned)-1-__builtin_clz(n); } template struct loser_tree { typedef struct { T* stream; size_t n; } Stream; unsigned* restrict _nodes; Stream* restrict _streams; unsigned _nonempty_streams; const unsigned _stream_offset; template loser_tree(Iterator begin, Iterator end) : _nodes(0), _streams(0), _nonempty_streams(end-begin), _stream_offset(1 << (log2(_nonempty_streams-1)+1)) { assert(_nonempty_streams>1); void* raw = malloc(_stream_offset*sizeof(unsigned) + _stream_offset*sizeof(Stream)); _nodes = static_cast(raw); _streams = reinterpret_cast( static_cast(raw) + _stream_offset*sizeof(unsigned)); for (unsigned i=0; i < _nonempty_streams; ++i) { _streams[i].stream = begin[i].first; _streams[i].n = begin[i].second; } (void) memset(_streams+_nonempty_streams, 0, (_stream_offset-_nonempty_streams)*sizeof(Stream)); _nodes[0] = init_min(1); //debug()<<*this; } ~loser_tree() { assert(_nodes); assert(_streams); free(static_cast(_nodes)); } Stream& node2stream(unsigned pos) { assert(pos < _stream_offset); assert(_nodes[pos] < _stream_offset); return _streams[_nodes[pos]]; } bool stream_empty(Stream& pos) const { return pos.n == size_t(0); } unsigned init_min(unsigned root) { if (root >= _stream_offset) { return root-_stream_offset; } //debug() << __PRETTY_FUNCTION__ << " root="<> 1; i!=0; i >>= 1) { if (stream_empty(_streams[new_min]) or (not stream_empty(node2stream(i)) and cmp(*node2stream(i).stream, *_streams[new_min].stream) < 0)) { std::swap(new_min, _nodes[i]); } } _nodes[0] = new_min; } T min() { //debug() << __PRETTY_FUNCTION__ << std::endl; assert(_nonempty_streams); assert(not stream_empty(node2stream(0))); T ret = *(node2stream(0).stream++); if (--node2stream(0).n == size_t(0)) { --_nonempty_streams; } update(); //debug() << "\t -> " << ret << std::endl; return ret; } }; #ifndef NDEBUG #include template std::ostream& operator<<(std::ostream& strm, const loser_tree& tree) { strm<<"/-------------------\n"; for(unsigned i=0;i