From 5adfb90f58c25a31cac93550a66c044775c62799 Mon Sep 17 00:00:00 2001 From: Shokrof Date: Wed, 2 May 2018 11:55:00 +0200 Subject: [PATCH 01/11] CQF is replaced by MQF --- Makefile | 2 +- examples/c++-api/Makefile | 2 +- include/oxli/storage.hh | 111 +- setup.cfg | 2 +- setup.py | 6 +- src/oxli/Makefile | 22 +- src/oxli/storage.cc | 72 +- tests/test_qfstorage.py | 135 +- tests/test_tabletype.py | 10 +- third-party/cqf/gqf.h | 160 --- third-party/mqf/LICENSE | 29 + third-party/mqf/Makefile | 60 + third-party/mqf/README.md | 109 ++ third-party/mqf/gqf.c | 2698 +++++++++++++++++++++++++++++++++++++ third-party/mqf/gqf.h | 257 ++++ 15 files changed, 3388 insertions(+), 287 deletions(-) delete mode 100644 third-party/cqf/gqf.h create mode 100644 third-party/mqf/LICENSE create mode 100644 third-party/mqf/Makefile create mode 100644 third-party/mqf/README.md create mode 100644 third-party/mqf/gqf.c create mode 100644 third-party/mqf/gqf.h diff --git a/Makefile b/Makefile index c7a78807db..0bc0809209 100644 --- a/Makefile +++ b/Makefile @@ -131,7 +131,7 @@ clean: FORCE cd src/oxli && $(MAKE) clean || true cd tests && rm -rf khmertest_* || true rm -f pytests.xml - cd third-party/cqf && make clean || true + cd third-party/mqf && make clean || true rm -f $(EXTENSION_MODULE) rm -f khmer/*.pyc scripts/*.pyc tests/*.pyc oxli/*.pyc \ sandbox/*.pyc khmer/__pycache__/* sandbox/__pycache__/* \ diff --git a/examples/c++-api/Makefile b/examples/c++-api/Makefile index bbed2871ee..576a497c92 100644 --- a/examples/c++-api/Makefile +++ b/examples/c++-api/Makefile @@ -1,7 +1,7 @@ CXXFLAGS=--std=c++11 \ -I ../../include/ \ -I ../../third-party/smhasher \ - -I ../../third-party/cqf \ + -I ../../third-party/mqf \ -I ../../third-party/seqan/core/include/ \ -I ../../third-party/rollinghash diff --git a/include/oxli/storage.hh b/include/oxli/storage.hh index 33fb6f7f73..aeac60e71e 100644 --- a/include/oxli/storage.hh +++ b/include/oxli/storage.hh @@ -410,51 +410,78 @@ public: * * \brief A Quotient Filter storage */ - class QFStorage : public Storage { +class QFStorage : public Storage +{ protected: - QF cf; + QF mf; public: - QFStorage(int size) { - // size is the power of two to specify the number of slots in - // the filter (2**size). Third argument sets the number of bits used - // in the key (current value of size+8 is copied from the CQF example) - // Final argument is the number of bits allocated for the value, which - // we do not use. - qf_init(&cf, (1ULL << size), size+8, 0); - } - - ~QFStorage() { qf_destroy(&cf); } - - BoundedCounterType test_and_set_bits(HashIntoType khash) { - BoundedCounterType x = get_count(khash); - add(khash); - return !x; - } - - // - bool add(HashIntoType khash) { - bool is_new = get_count(khash) == 0; - qf_insert(&cf, khash % cf.range, 0, 1); - return is_new; - } - - // get the count for the given k-mer hash. - const BoundedCounterType get_count(HashIntoType khash) const { - return qf_count_key_value(&cf, khash % cf.range, 0); - } - - // Accessors for protected/private table info members - // xnslots is larger than nslots. It includes some extra slots to deal - // with some details of how the counting is implemented - std::vector get_tablesizes() const { return {cf.xnslots}; } - const size_t n_tables() const { return 1; } - const uint64_t n_unique_kmers() const { return cf.ndistinct_elts; } - const uint64_t n_occupied() const { return cf.noccupied_slots; } - void save(std::string outfilename, WordLength ksize); - void load(std::string infilename, WordLength &ksize); - - Byte **get_raw_tables() { return nullptr; } + QFStorage(int size) + { + // size is the power of two to specify the number of slots in + // the filter (2**size). Third argument sets the number of bits used + // in the key (current value of size+8 is copied from the CQF example) + // Final argument is the number of bits allocated for the value, which + // we do not use. + _supports_bigcount = true; + qf_init(&mf, (1ULL << size), size+8, 0,2,true,"",2038074761); + + + + } + + ~QFStorage() + { + qf_destroy(&mf); + } + + BoundedCounterType test_and_set_bits(HashIntoType khash) + { + BoundedCounterType x = get_count(khash); + add(khash); + return !x; + } + + // + bool add(HashIntoType khash) + { + bool is_new = get_count(khash) == 0; + qf_insert(&mf, khash % mf.metadata->range, 1,false,false); + return is_new; + } + + // get the count for the given k-mer hash. + const BoundedCounterType get_count(HashIntoType khash) const + { + return qf_count_key(&mf, khash % mf.metadata->range); + } + + // Accessors for protected/private table info members + // xnslots is larger than nslots. It includes some extra slots to deal + // with some details of how the counting is implemented + std::vector get_tablesizes() const + { + return {mf.metadata->xnslots}; + } + const size_t n_tables() const + { + return 1; + } + const uint64_t n_unique_kmers() const + { + return mf.metadata->ndistinct_elts; + } + const uint64_t n_occupied() const + { + return mf.metadata->noccupied_slots; + } + void save(std::string outfilename, WordLength ksize); + void load(std::string infilename, WordLength &ksize); + + Byte **get_raw_tables() + { + return nullptr; + } }; diff --git a/setup.cfg b/setup.cfg index 894a4ea9b8..18b56b6859 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ undef = NO_UNIQUE_RC # docker/Dockerfile # libraries = z,bz2 ## if using system libraries -include-dirs = include:third-party/zlib:third-party/bzip2:third-party/seqan/core/include:third-party/smhasher:third-party/cqf:third-party/rollinghash +include-dirs = include:third-party/zlib:third-party/bzip2:third-party/seqan/core/include:third-party/smhasher:third-party/rollinghash:third-party/mqf # include-dirs = lib ## if using system libraries (broken) diff --git a/setup.py b/setup.py index d4049347d3..64be4a7304 100755 --- a/setup.py +++ b/setup.py @@ -330,10 +330,10 @@ def run(self): if sys.platform == 'darwin' and 'gcov' in self.libraries: self.libraries.remove('gcov') - cqfcmd = ['bash', '-c', 'cd third-party/cqf && make'] - spawn(cmd=cqfcmd, dry_run=self.dry_run) + mqfcmd = ['bash', '-c', 'cd third-party/mqf && make'] + spawn(cmd=mqfcmd, dry_run=self.dry_run) for ext in self.extensions: - ext.extra_objects.append(path_join("third-party", "cqf", "gqf.o")) + ext.extra_objects.append(path_join("third-party", "mqf", "gqf.o")) if "z" not in self.libraries: zcmd = ['bash', '-c', 'cd ' + ZLIBDIR + ' && ( test Makefile -nt' diff --git a/src/oxli/Makefile b/src/oxli/Makefile index 9858659a6a..9ce30e156f 100644 --- a/src/oxli/Makefile +++ b/src/oxli/Makefile @@ -69,7 +69,7 @@ PREFIX=/usr/local INCLUDES= -I ../../include/ -I ../../third-party/seqan/core/include/ \ -I ../../third-party/smhasher/ \ - -I ../../third-party/cqf/ \ + -I ../../third-party/mqf/ \ -I ../../third-party/rollinghash ifeq ($(USE_SYSTEM_ZLIB), false) @@ -221,10 +221,10 @@ BZIP2_OBJS_BASE= \ BZIP2_OBJS=$(addprefix $(BZIP2_DIR)/, $(BZIP2_OBJS_BASE)) # Counting bloom filter -CQF_DIR=../../third-party/cqf -CQF_OBJS_BASE= gqf.o +MQF_DIR=../../third-party/mqf +MQF_OBJS_BASE= gqf.o -CQF_OBJS=$(addprefix $(CQF_DIR)/, $(CQF_OBJS_BASE)) +MQF_OBJS=$(addprefix $(MQF_DIR)/, $(MQF_OBJS_BASE)) #### oxli proper below here #### @@ -259,9 +259,9 @@ PRECOMILE_OBJS += $(BZIP2_OBJS) PRECLEAN_TARGS += libbz2clean endif -LIBOXLI_OBJS += $(CQF_OBJS) -PRECOMILE_OBJS += $(CQF_OBJS) -PRECLEAN_TARGS += libcqfclean +LIBOXLI_OBJS += $(MQF_OBJS) +PRECOMILE_OBJS += $(MQF_OBJS) +PRECLEAN_TARGS += libmqfclean HEADERS= \ hashtable.hh \ @@ -290,8 +290,8 @@ zlibclean: (cd $(ZLIB_DIR) && make distclean) libbz2clean: (cd $(BZIP2_DIR) && make -f Makefile-libbz2_so clean) -libcqfclean: - (cd $(CQF_DIR) && make clean) +libmqfclean: + (cd $(MQF_DIR) && make clean) clean: $(PRECLEAN_TARGS) rm -f *.o *.a *.$(SHARED_EXT)* oxli.pc $(TEST_PROGS) @@ -315,8 +315,8 @@ $(ZLIB_OBJS): $(BZIP2_OBJS): (cd $(BZIP2_DIR) && make -f Makefile-libbz2_so $(BZIP2_OBJS_BASE)) -$(CQF_OBJS): - (cd $(CQF_DIR) && make) +$(MQF_OBJS): + (cd $(MQF_DIR) && make) # MurMur3 murmur3.o: ../../third-party/smhasher/MurmurHash3.cc diff --git a/src/oxli/storage.cc b/src/oxli/storage.cc index 923843f451..cf20cfcf9e 100644 --- a/src/oxli/storage.cc +++ b/src/oxli/storage.cc @@ -923,34 +923,14 @@ void QFStorage::save(std::string outfilename, WordLength ksize) unsigned char version = SAVED_FORMAT_VERSION; unsigned char ht_type = SAVED_QFCOUNT; + outfile.write(SAVED_SIGNATURE, 4); outfile.write((const char *) &version, 1); outfile.write((const char *) &ht_type, 1); outfile.write((const char *) &ksize, sizeof(ksize)); - /* just a hack to handle __uint128_t value. Don't know a better to handle it - * right now */ - uint64_t tmp_range; - tmp_range = cf.range; - - outfile.write((const char *) &cf.nslots, sizeof(cf.nslots)); - outfile.write((const char *) &cf.xnslots, sizeof(cf.xnslots)); - outfile.write((const char *) &cf.key_bits, sizeof(cf.key_bits)); - outfile.write((const char *) &cf.value_bits, sizeof(cf.value_bits)); - outfile.write((const char *) &cf.key_remainder_bits, sizeof(cf.key_remainder_bits)); - outfile.write((const char *) &cf.bits_per_slot, sizeof(cf.bits_per_slot)); - outfile.write((const char *) &tmp_range, sizeof(tmp_range)); - outfile.write((const char *) &cf.nblocks, sizeof(cf.nblocks)); - outfile.write((const char *) &cf.nelts, sizeof(cf.nelts)); - outfile.write((const char *) &cf.ndistinct_elts, sizeof(cf.ndistinct_elts)); - outfile.write((const char *) &cf.noccupied_slots, sizeof(cf.noccupied_slots)); - - #if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 || BITS_PER_SLOT == 64 - outfile.write((const char *) cf.blocks, sizeof(qfblock) * cf.nblocks); - #else - outfile.write((const char *) cf.blocks, - (sizeof(qfblock) + SLOTS_PER_BLOCK * cf.bits_per_slot / 8) * cf.nblocks); - #endif + outfile.write((const char *)mf.metadata,sizeof(qfmetadata)); + outfile.write((const char *)mf.blocks,mf.metadata->size); outfile.close(); } @@ -1011,34 +991,22 @@ void QFStorage::load(std::string infilename, WordLength &ksize) infile.read((char *) &save_ksize, sizeof(save_ksize)); ksize = save_ksize; - infile.read((char *) &cf.nslots, sizeof(cf.nslots)); - infile.read((char *) &cf.xnslots, sizeof(cf.xnslots)); - infile.read((char *) &cf.key_bits, sizeof(cf.key_bits)); - infile.read((char *) &cf.value_bits, sizeof(cf.value_bits)); - infile.read((char *) &cf.key_remainder_bits, sizeof(cf.key_remainder_bits)); - infile.read((char *) &cf.bits_per_slot, sizeof(cf.bits_per_slot)); - infile.read((char *) &tmp_range, sizeof(tmp_range)); - - infile.read((char *) &cf.nblocks, sizeof(cf.nblocks)); - infile.read((char *) &cf.nelts, sizeof(cf.nelts)); - infile.read((char *) &cf.ndistinct_elts, sizeof(cf.ndistinct_elts)); - infile.read((char *) &cf.noccupied_slots, sizeof(cf.noccupied_slots)); - /* just a hack to handle __uint128_t value. Don't know a better to handle it - * right now */ - cf.range = tmp_range; - // deallocate previously allocated blocks - free(cf.blocks); - /* allocate the space for the actual qf blocks */ - #if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 || BITS_PER_SLOT == 64 - cf.blocks = (qfblock *)calloc(cf.nblocks, sizeof(qfblock)); - #else - cf.blocks = (qfblock *)calloc(cf.nblocks, sizeof(qfblock) + SLOTS_PER_BLOCK * cf.bits_per_slot / 8); - #endif - #if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 || BITS_PER_SLOT == 64 - infile.read((char *) cf.blocks, sizeof(qfblock) * cf.nblocks); - #else - infile.read((char *) cf.blocks, - (sizeof(qfblock) + SLOTS_PER_BLOCK * cf.bits_per_slot / 8) * cf.nblocks); - #endif + mf.mem = (qfmem *)calloc(sizeof(qfmem), 1); + mf.metadata = (qfmetadata *)calloc(sizeof(qfmetadata), 1); + infile.read((char*)mf.metadata,sizeof(qfmetadata)); + mf.blocks = (qfblock *)calloc(mf.metadata->size, 1); + infile.read((char*)mf.blocks, mf.metadata->size); + + mf.metadata->num_locks = + 10;//should be changed to something realistic like function qf_deserialize + mf.mem->metadata_lock = 0; + /* initialize all the locks to 0 */ + mf.mem->locks = (volatile int *)calloc(mf.metadata->num_locks, + sizeof(volatile int)); + + + + + infile.close(); } diff --git a/tests/test_qfstorage.py b/tests/test_qfstorage.py index d12d058e08..4c1eae80f0 100755 --- a/tests/test_qfstorage.py +++ b/tests/test_qfstorage.py @@ -2,26 +2,137 @@ import random from khmer import QFCounttable +import khmer +from tests import khmer_tst_utils as utils +from khmer import ReadParser -from . import khmer_tst_utils as utils +import random +import pytest -def test_read_write(): - rng = random.Random(1) +MAX_COUNT = 255 +MAX_BIGCOUNT = 65535 + +sketchSize = 1048576 + + +DNA = "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC" + + +def teardown(): + utils.cleanup() + + +@pytest.fixture(params=[khmer.QFCounttable]) +def getSketch(request): + return request.param + + +def test_count_1(getSketch): + print("start") + hi = getSketch(12, sketchSize) + + kmer = 'G' * 12 + hashval = hi.hash('G' * 12) + + assert hi.get(kmer) == 0 + assert hi.get(hashval) == 0 + + hi.count(kmer) + assert hi.get(kmer) == 1 + assert hi.get(hashval) == 1 + + hi.count(kmer) + assert hi.get(kmer) == 2 + assert hi.get(hashval) == 2 + + kmer = 'G' * 11 + + with pytest.raises(ValueError): + hi.hash(kmer) - qf = QFCounttable(20, 1024 * 4) + +def test_count_2(getSketch): + hi = getSketch(12, sketchSize) + print("done") + kmer = 'G' * 12 + hashval = hi.hash('G' * 12) + + assert hi.get(kmer) == 0 + assert hi.get(hashval) == 0 + + hi.count(kmer) + assert hi.get(kmer) == 1 + assert hi.get(hashval) == 1 + + hi.count(hashval) # count hashes same as strings + assert hi.get(kmer) == 2 + assert hi.get(hashval) == 2 + + +def test_read_write(getSketch): + print("Start") + fname = str.encode(utils.get_temp_filename('zzz')) + rng = random.Random(1) + ctm = getSketch(20, sketchSize) kmers = ["".join(rng.choice("ACGT") for _ in range(20)) for n in range(400)] for kmer in kmers: - qf.add(kmer) + ctm.add(kmer) - fname = utils.get_temp_filename('zzz') + ctm.save(fname) - qf.save(fname) + # print("Finish") + # # on purpose choose parameters that are different from sct + ctm2 = getSketch.load(fname) + ctm2.load(fname) + # assert ctm.ksize() == ctm2.ksize() + # for kmer in kmers: + # assert ctm.get(kmer) == ctm2.get(kmer) + # + # - # on purpose choose parameters that are different from sct - qf2 = QFCounttable.load(fname) - assert qf.ksize() == qf2.ksize() - for kmer in kmers: - assert qf.get(kmer) == qf2.get(kmer) + +def test_maxcount_with_bigcount(getSketch): + # hashtable should not saturate, if use_bigcount is set. + kh = getSketch(4, 128) + + last_count = None + for _ in range(0, 10000): + kh.count('AAAA') + c = kh.get('AAAA') + print(c) + if c == last_count: + break + + last_count = c + + assert c == 10000, "should be able to count to 1000: %d" % c + + +def test_get_ksize(getSketch): + kh = getSketch(22, 16) + assert kh.ksize() == 22 + + +# +# def test_read_write(): +# rng = random.Random(1) +# +# qf = QFCounttable(20, 1024 * 4) +# +# kmers = ["".join(rng.choice("ACGT") for _ in range(20)) +# for n in range(400)] +# for kmer in kmers: +# qf.add(kmer) +# +# fname = utils.get_temp_filename('zzz') +# +# qf.save(fname) +# +# # on purpose choose parameters that are different from sct +# qf2 = QFCounttable.load(fname) +# assert qf.ksize() == qf2.ksize() +# for kmer in kmers: +# assert qf.get(kmer) == qf2.get(kmer) diff --git a/tests/test_tabletype.py b/tests/test_tabletype.py index f92058ca51..106a1146eb 100755 --- a/tests/test_tabletype.py +++ b/tests/test_tabletype.py @@ -77,12 +77,14 @@ def test_n_occupied(AnyTabletype): assert tt.n_unique_kmers() == 1 tt.add(kmer) + assert tt.n_occupied() == 1 # the CQF implementation we use can use more than one slot to represent # counts for a single kmer - if not tt.__class__.__name__.startswith("QF"): - assert tt.n_occupied() == 1 - else: - assert tt.n_occupied() == 2 + # if not tt.__class__.__name__.startswith("QF"): + # assert tt.n_occupied() == 1 + # else: + # assert tt.n_occupied() == 2 + assert tt.n_unique_kmers() == 1 diff --git a/third-party/cqf/gqf.h b/third-party/cqf/gqf.h deleted file mode 100644 index ef8b5c4b32..0000000000 --- a/third-party/cqf/gqf.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * ===================================================================================== - * - * Filename: gqf.h - * - * Description: - * - * Version: 1.0 - * Created: 2017-02-04 03:40:58 PM - * Revision: none - * Compiler: gcc - * - * Author: Prashant Pandey (ppandey@cs.stonybrook.edu) - * Rob Johnson (rob@cs.stonybrook.edu) - * Organization: Stony Brook University - * - * ===================================================================================== - */ - -#ifndef QF_H -#define QF_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define BITS_PER_SLOT 8 - -/* Must be >= 6. 6 seems fastest. */ -#define BLOCK_OFFSET_BITS (6) - -#define SLOTS_PER_BLOCK (1ULL << BLOCK_OFFSET_BITS) -#define METADATA_WORDS_PER_BLOCK ((SLOTS_PER_BLOCK + 63) / 64) - - typedef struct __attribute__ ((__packed__)) qfblock { - uint8_t offset; /* Code works with uint16_t, uint32_t, etc, but uint8_t seems just as fast as anything else */ - uint64_t occupieds[METADATA_WORDS_PER_BLOCK]; - uint64_t runends[METADATA_WORDS_PER_BLOCK]; - - #if BITS_PER_SLOT == 8 - uint8_t slots[SLOTS_PER_BLOCK]; - #elif BITS_PER_SLOT == 16 - uint16_t slots[SLOTS_PER_BLOCK]; - #elif BITS_PER_SLOT == 32 - uint32_t slots[SLOTS_PER_BLOCK]; - #elif BITS_PER_SLOT == 64 - uint64_t slots[SLOTS_PER_BLOCK]; - #elif BITS_PER_SLOT != 0 - uint8_t slots[SLOTS_PER_BLOCK * BITS_PER_SLOT / 8]; - #else - uint8_t slots[]; - #endif - } qfblock; - - uint64_t shift_into_b2(uint64_t a, uint64_t b, int bstart, int bend, int amount); - -#ifdef LOG_NUM_SHIFTS -#define len 5000 -int shift_count[len]; -#endif - - typedef struct quotient_filter { - uint64_t nslots; - uint64_t xnslots; - uint64_t key_bits; - uint64_t value_bits; - uint64_t key_remainder_bits; - uint64_t bits_per_slot; - __uint128_t range; - uint64_t nblocks; - uint64_t nelts; - uint64_t ndistinct_elts; - uint64_t noccupied_slots; - qfblock *blocks; - } quotient_filter; - - - typedef quotient_filter QF; - - typedef struct quotient_filter_iterator { - const QF *qf; - uint64_t run; - uint64_t current; - } quotient_filter_iterator; - - typedef quotient_filter_iterator QFi; - - void qf_init(QF *qf, uint64_t nslots, uint64_t key_bits, uint64_t value_bits); - - void qf_destroy(QF *qf); - - /* Increment the counter for this key/value pair by count. */ - void qf_insert(QF *qf, uint64_t key, uint64_t value, uint64_t count); - - /* Remove count instances of this key/value combination. */ - void qf_remove(QF *qf, uint64_t key, uint64_t value, uint64_t count); - - /* Remove all instances of this key/value pair. */ - void qf_delete_key_value(QF *qf, uint64_t key, uint64_t value); - - /* Remove all instances of this key. */ - void qf_delete_key(QF *qf, uint64_t key); - - /* Replace the association (key, oldvalue, count) with the association - (key, newvalue, count). If there is already an association (key, - newvalue, count'), then the two associations will be merged and - their counters will be summed, resulting in association (key, - newvalue, count' + count). */ - void qf_replace(QF *qf, uint64_t key, uint64_t oldvalue, uint64_t newvalue); - - /* Lookup the value associated with key. Returns the count of that - key/value pair in the QF. If it returns 0, then, the key is not - present in the QF. Only returns the first value associated with key - in the QF. If you want to see others, use an iterator. */ - uint64_t qf_query(const QF *qf, uint64_t key, uint64_t *value); - - /* Return the number of times key has been inserted, with any value, - into qf. */ - uint64_t qf_count_key(const QF *qf, uint64_t key); - - /* Return the number of times key has been inserted, with the given - value, into qf. */ - uint64_t qf_count_key_value(const QF *qf, uint64_t key, uint64_t value); - - /* Initialize an iterator */ - void qf_iterator(const QF *qf, QFi *qfi, uint64_t position); - - /* Returns 0 if the iterator is still valid (i.e. has not reached the - end of the QF. */ - int qfi_get(QFi *qfi, uint64_t *key, uint64_t *value, uint64_t *count); - - /* Advance to next entry. Returns whether or not another entry is - found. */ - int qfi_next(QFi *qfi); - - /* Check to see if the if the end of the QF */ - int qfi_end(QFi *qfi); - - /* For debugging */ - void qf_dump(const QF *); - - /* write data structure of to the disk */ - void qf_serialize(const QF *qf, const char *filename); - - /* read data structure off the disk */ - void qf_deserialize(QF *qf, const char *filename); - - /* merge two QFs into the third one. */ - void qf_merge(const QF *qfa, const QF *qfb, QF *qfc); - - /* merge multiple QFs into the final QF one. */ - void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr); - -#ifdef __cplusplus -} -#endif - -#endif /* QF_H */ diff --git a/third-party/mqf/LICENSE b/third-party/mqf/LICENSE new file mode 100644 index 0000000000..09d493bf1f --- /dev/null +++ b/third-party/mqf/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third-party/mqf/Makefile b/third-party/mqf/Makefile new file mode 100644 index 0000000000..1dbc08bebd --- /dev/null +++ b/third-party/mqf/Makefile @@ -0,0 +1,60 @@ +TARGETS=gqf.o + +ifdef D + DEBUG=-g + OPT= +else + DEBUG= + OPT=-Ofast +endif + +# using the SSE4.2 extensions doesn't seem to speed things up by a large +# amount, so disabling it for the time being till we have a good automated +# test for detecting whether or not the CPU supports it +OS := $(shell uname) +ifeq ($(OS), Darwin) + ARCH= +else + #ARCH=-msse4.2 -D__SSE4_2_ + ARCH= +endif + +ifdef P + PROFILE=-pg -no-pie # for bug in gprof. +endif + +CXX = g++ -std=c++11 -fPIC +CC = g++ -std=c++11 -fPIC +LD= g++ -std=c++11 + +CXXFLAGS = -Wall $(DEBUG) $(PROFILE) $(OPT) -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function + +LDFLAGS = $(DEBUG) $(PROFILE) $(OPT) + +# +# declaration of dependencies +# + +all: $(TARGETS) + +# dependencies between programs and .o files + + + +# dependencies between .o files and .cc (or .c) files + +%.o: %.cc +gqf.o: gqf.c gqf.h + +# +# generic build rules +# + +%.o: %.cc + $(CXX) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ + +%.o: %.c + $(CC) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ + +clean: + rm -f *.o $(TARGETS) diff --git a/third-party/mqf/README.md b/third-party/mqf/README.md new file mode 100644 index 0000000000..6a50a694da --- /dev/null +++ b/third-party/mqf/README.md @@ -0,0 +1,109 @@ +# MQF +[![Build Status](https://travis-ci.org/shokrof/MQF.svg?branch=mqfDevelopmenet)](https://travis-ci.org/shokrof/MQF) +[![codecov](https://codecov.io/gh/shokrof/MQF/branch/mqfDevelopmenet/graph/badge.svg)](https://codecov.io/gh/shokrof/MQF) + +MQF, Mixed Quotient Filter, is approximate membership query data structure that supports many useful functions. MQF is a variant of [CQF](https://github.com/splatlab/cqf). MQF has lower bits per element than Bloom filter and Count-min sketches. MQF also has good data locality which makes it efficient when running on secondary storage. Moreover. It supports removing, iterating, merging ,and resizing. + +## Documentation +### Building +MQF onlye requires make and g++ to be installed. +```bash +apt-get install make g++ +make NH=1 +make test NH=1 +./mqf_test +``` +### Initialization +1. qf_init +2. qf_destroy +3. estimate + +### Functions Supported +1. Insert : +Increment the counter for this item by count. + ```c++ + bool qf_insert(QF *qf, uint64_t key, + uint64_t count,bool lock, bool spin); + ``` + + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item to be inserted. + * uint64_t count: Count to be added + * bool lock: For Multithreading, Lock the * slot used by the current thread so that other threads can't change the value + * bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + * returns True if the insertion succeeded. + +2. Count: + Return the number of times key has been inserted, with any value, into qf. + ```c++ + uint64_t qf_count_key(const QF *qf, uint64_t key); + ``` + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item to be counted. + * returns the number of times the item is inserted. +3. Remove: +Decrement the counter for this item by count. +```c++ +bool qf_remove(QF *qf, uint64_t hash, uint64_t count, bool lock, bool spin); +``` + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item to be removed + * uint64_t count: Count to be removed + * bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + * bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + +4. Add/Remove tag to elements +```c++ +uint64_t qf_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock, bool spin); +uint64_t qf_get_tag(const QF *qf, uint64_t key); +uint64_t qf_remove_tag(const QF *qf, uint64_t key, bool lock, bool spin); +``` + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item. + * uint64_t tag: tag for the item. + * bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + * bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + +5. Resize: + resize the filter into a bigger or smaller one + ```c++ + QF* qf_resize(QF* qf, int newQ, const char * originalFilename=NULL, const char * newFilename=NULL); + ``` + * Qf* qf : pointer to the Filter + * uint64_t newQ: new number of slots(Q). the slot size will be recalculated to keep the range constant. + * string originalFilename(optional): dump the current filter to the disk to free space for the new filter. Filename is provided as the content of the string. + * string newFilename(optional): the new filter is created on disk. Filename is provided as the content of the string. + * returns a pointer to the new filter + +6. Merge: merge more than one filter into a final one. +```c++ +void qf_merge(QF *qfa, QF *qfb, QF *qfc); +void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr); +``` + +7. Compare: +check if two filters have the same items, counts and tags. +```c++ +bool qf_equals(QF *qfa, QF *qfb); +``` +8. Intersect +calculate the the intersection between two filters. +```c++ +void qf_intersect(QF *qfa, QF *qfb, QF *qfc); +``` +9. Subtract +subtract the second filter from the first. +```c++ +void qf_subtract(QF *qfa, QF *qfb, QF *qfc); +``` +10. Space: +returns the space percent occupied by the inserted items. +```c++ +int qf_space(QF *qf); +``` + +### Miscellaneous Functions +1. Capacity +2. Copy +3. Serialize/ Deserialize +4. MMap read diff --git a/third-party/mqf/gqf.c b/third-party/mqf/gqf.c new file mode 100644 index 0000000000..d45196723a --- /dev/null +++ b/third-party/mqf/gqf.c @@ -0,0 +1,2698 @@ +#include +#if 0 +# include +#else +# define assert(x) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gqf.h" +#include + +/****************************************************************** + * Code for managing the metadata bits and slots w/o interpreting * + * the content of the slots. + ******************************************************************/ + +/* Must be >= 6. 6 seems fastest. */ +#define BLOCK_OFFSET_BITS (6) + +#define SLOTS_PER_BLOCK (1ULL << BLOCK_OFFSET_BITS) +#define METADATA_WORDS_PER_BLOCK ((SLOTS_PER_BLOCK + 63) / 64) + +#define NUM_SLOTS_TO_LOCK (1ULL<<16) +#define CLUSTER_SIZE (1ULL<<14) + +#define METADATA_WORD(qf,field,slot_index) (get_block((qf), (slot_index) / \ + SLOTS_PER_BLOCK)->field[((slot_index) % SLOTS_PER_BLOCK) / 64]) + +#define BITMASK(nbits) ((nbits) == 64 ? 0xffffffffffffffff : (1ULL << (nbits)) \ + - 1ULL) +#define MAX_VALUE(nbits) ((1ULL << (nbits)) - 1) +#define BILLION 1000000000L + +typedef struct __attribute__ ((__packed__)) qfblock { + /* Code works with uint16_t, uint32_t, etc, but uint8_t seems just as fast as + * anything else */ + uint8_t offset; + uint64_t occupieds[METADATA_WORDS_PER_BLOCK]; + uint64_t runends[METADATA_WORDS_PER_BLOCK]; +#if BITS_PER_SLOT == 8 + uint8_t slots[SLOTS_PER_BLOCK]; +#elif BITS_PER_SLOT == 16 + uint16_t slots[SLOTS_PER_BLOCK]; +#elif BITS_PER_SLOT == 32 + uint32_t slots[SLOTS_PER_BLOCK]; +#elif BITS_PER_SLOT == 64 + uint64_t slots[SLOTS_PER_BLOCK]; +#elif BITS_PER_SLOT != 0 + uint8_t slots[SLOTS_PER_BLOCK * BITS_PER_SLOT / 8]; +#else + uint8_t slots[]; +#endif +} qfblock; + +static __inline__ unsigned long long rdtsc(void) +{ + unsigned hi, lo; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 ); +} + +#ifdef LOG_WAIT_TIME +static inline bool qf_spin_lock(QF *cf, volatile int *lock, uint64_t idx, bool + flag_spin) +{ + struct timespec start, end; + bool ret; + + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + if (!flag_spin) { + ret = !__sync_lock_test_and_set(lock, 1); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + cf->mem->wait_times[idx].locks_acquired_single_attempt++; + cf->mem->wait_times[idx].total_time_single += BILLION * (end.tv_sec - + start.tv_sec) + + end.tv_nsec - start.tv_nsec; + } else { + if (!__sync_lock_test_and_set(lock, 1)) { + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + cf->mem->wait_times[idx].locks_acquired_single_attempt++; + cf->mem->wait_times[idx].total_time_single += BILLION * (end.tv_sec - + start.tv_sec) + + end.tv_nsec - start.tv_nsec; + } else { + while (__sync_lock_test_and_set(lock, 1)) + while (*lock); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + cf->mem->wait_times[idx].total_time_spinning += BILLION * (end.tv_sec - + start.tv_sec) + + end.tv_nsec - start.tv_nsec; + } + ret = true; + } + cf->mem->wait_times[idx].locks_taken++; + + return ret; + + /*start = rdtsc();*/ + /*if (!__sync_lock_test_and_set(lock, 1)) {*/ + /*clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);*/ + /*cf->mem->wait_times[idx].locks_acquired_single_attempt++;*/ + /*cf->mem->wait_times[idx].total_time_single += BILLION * (end.tv_sec - + * start.tv_sec) + end.tv_nsec - start.tv_nsec;*/ + /*} else {*/ + /*while (__sync_lock_test_and_set(lock, 1))*/ + /*while (*lock);*/ + /*clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);*/ + /*cf->mem->wait_times[idx].total_time_spinning += BILLION * (end.tv_sec - + * start.tv_sec) + end.tv_nsec - start.tv_nsec;*/ + /*}*/ + + /*end = rdtsc();*/ + /*cf->mem->wait_times[idx].locks_taken++;*/ + /*return;*/ +} +#else +/** + * Try to acquire a lock once and return even if the lock is busy. + * If spin flag is set, then spin until the lock is available. + */ +static inline bool qf_spin_lock(volatile int *lock, bool flag_spin) +{ + if (!flag_spin) { + return !__sync_lock_test_and_set(lock, 1); + } else { + while (__sync_lock_test_and_set(lock, 1)) + while (*lock); + return true; + } + + return false; +} +#endif + +static inline void qf_spin_unlock(volatile int *lock) +{ + __sync_lock_release(lock); + return; +} + +static bool qf_lock(const QF *cf, uint64_t hash_bucket_index, bool spin, bool flag) +{ + uint64_t hash_bucket_lock_offset = hash_bucket_index % NUM_SLOTS_TO_LOCK; + if (flag) { +#ifdef LOG_WAIT_TIME + if (!qf_spin_lock(cf, &cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK], + hash_bucket_index/NUM_SLOTS_TO_LOCK, spin)) + return false; + if (NUM_SLOTS_TO_LOCK - hash_bucket_lock_offset <= CLUSTER_SIZE) { + if (!qf_spin_lock(cf, &cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK+1], + hash_bucket_index/NUM_SLOTS_TO_LOCK+1, spin)) { + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK]); + return false; + } + } +#else + if (!qf_spin_lock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK], spin)) + return false; + if (NUM_SLOTS_TO_LOCK - hash_bucket_lock_offset <= CLUSTER_SIZE) { + if (!qf_spin_lock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK+1], + spin)) { + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK]); + return false; + } + } +#endif + } else { + /* take the lock for two lock-blocks; the lock-block in which the + * hash_bucket_index falls and the next lock-block */ + +#ifdef LOG_WAIT_TIME + if (hash_bucket_index >= NUM_SLOTS_TO_LOCK && hash_bucket_lock_offset <= + CLUSTER_SIZE) { + if (!qf_spin_lock(cf, &cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK-1], spin)) + return false; + } + if (!qf_spin_lock(cf, &cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK], spin)) { + if (hash_bucket_index >= NUM_SLOTS_TO_LOCK && hash_bucket_lock_offset <= + CLUSTER_SIZE) + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK-1]); + return false; + } + if (!qf_spin_lock(cf, &cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK+1], + spin)) { + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK]); + if (hash_bucket_index >= NUM_SLOTS_TO_LOCK && hash_bucket_lock_offset <= + CLUSTER_SIZE) + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK-1]); + return false; + } +#else + if (hash_bucket_index >= NUM_SLOTS_TO_LOCK && hash_bucket_lock_offset <= + CLUSTER_SIZE) { + if (!qf_spin_lock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK-1], spin)) + return false; + } + if (!qf_spin_lock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK], spin)) { + if (hash_bucket_index >= NUM_SLOTS_TO_LOCK && hash_bucket_lock_offset <= + CLUSTER_SIZE) + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK-1]); + return false; + } + if (!qf_spin_lock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK+1], + spin)) { + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK]); + if (hash_bucket_index >= NUM_SLOTS_TO_LOCK && hash_bucket_lock_offset <= + CLUSTER_SIZE) + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK-1]); + return false; + } +#endif + } + return true; +} + +static void qf_unlock(const QF *cf, uint64_t hash_bucket_index, bool flag) +{ + uint64_t hash_bucket_lock_offset = hash_bucket_index % NUM_SLOTS_TO_LOCK; + if (flag) { + if (NUM_SLOTS_TO_LOCK - hash_bucket_lock_offset <= CLUSTER_SIZE) { + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK+1]); + } + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK]); + } else { + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK+1]); + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK]); + if (hash_bucket_index >= NUM_SLOTS_TO_LOCK && hash_bucket_lock_offset <= + CLUSTER_SIZE) + qf_spin_unlock(&cf->mem->locks[hash_bucket_index/NUM_SLOTS_TO_LOCK-1]); + } +} + +static void modify_metadata(QF *cf, uint64_t *metadata, int cnt) +{ +#ifdef LOG_WAIT_TIME + qf_spin_lock(cf, &cf->mem->metadata_lock,cf->num_locks, true); +#else + qf_spin_lock(&cf->mem->metadata_lock, true); +#endif + *metadata = *metadata + cnt; + qf_spin_unlock(&cf->mem->metadata_lock); + return; +} + +static inline int popcnt(uint64_t val) +{ + asm("popcnt %[val], %[val]" + : [val] "+r" (val) + : + : "cc"); + return val; +} + +static inline int64_t bitscanreverse(uint64_t val) +{ + if (val == 0) { + return -1; + } else { + asm("bsr %[val], %[val]" + : [val] "+r" (val) + : + : "cc"); + return val; + } +} + +static inline int popcntv(const uint64_t val, int ignore) +{ + if (ignore % 64) + return popcnt (val & ~BITMASK(ignore % 64)); + else + return popcnt(val); +} + +// Returns the number of 1s up to (and including) the pos'th bit +// Bits are numbered from 0 +static inline int bitrank(uint64_t val, int pos) { + val = val & ((2ULL << pos) - 1); + asm("popcnt %[val], %[val]" + : [val] "+r" (val) + : + : "cc"); + return val; +} + +/** + * Returns the position of the k-th 1 in the 64-bit word x. + * k is 0-based, so k=0 returns the position of the first 1. + * + * Uses the broadword selection algorithm by Vigna [1], improved by Gog + * and Petri [2] and Vigna [3]. + * + * [1] Sebastiano Vigna. Broadword Implementation of Rank/Select + * Queries. WEA, 2008 + * + * [2] Simon Gog, Matthias Petri. Optimized succinct data + * structures for massive data. Softw. Pract. Exper., 2014 + * + * [3] Sebastiano Vigna. MG4J 5.2.1. http://mg4j.di.unimi.it/ + * The following code is taken from + * https://github.com/facebook/folly/blob/b28186247104f8b90cfbe094d289c91f9e413317/folly/experimental/Select64.h + */ +const uint8_t kSelectInByte[2048] = { + 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, + 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, + 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, + 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, + 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, + 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, + 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, + 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, + 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8, 8, 8, 1, + 8, 2, 2, 1, 8, 3, 3, 1, 3, 2, 2, 1, 8, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, + 2, 1, 8, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, + 4, 3, 3, 1, 3, 2, 2, 1, 8, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1, 6, 4, + 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, + 3, 2, 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 8, 7, 7, 1, 7, 2, + 2, 1, 7, 3, 3, 1, 3, 2, 2, 1, 7, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, + 7, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, + 3, 1, 3, 2, 2, 1, 7, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1, 6, 4, 4, 1, + 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, + 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 8, 8, 8, 8, 8, 8, 8, 2, + 8, 8, 8, 3, 8, 3, 3, 2, 8, 8, 8, 4, 8, 4, 4, 2, 8, 4, 4, 3, 4, 3, 3, 2, 8, 8, + 8, 5, 8, 5, 5, 2, 8, 5, 5, 3, 5, 3, 3, 2, 8, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, + 4, 3, 3, 2, 8, 8, 8, 6, 8, 6, 6, 2, 8, 6, 6, 3, 6, 3, 3, 2, 8, 6, 6, 4, 6, 4, + 4, 2, 6, 4, 4, 3, 4, 3, 3, 2, 8, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2, + 6, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2, 8, 8, 8, 7, 8, 7, 7, 2, 8, 7, + 7, 3, 7, 3, 3, 2, 8, 7, 7, 4, 7, 4, 4, 2, 7, 4, 4, 3, 4, 3, 3, 2, 8, 7, 7, 5, + 7, 5, 5, 2, 7, 5, 5, 3, 5, 3, 3, 2, 7, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, + 3, 2, 8, 7, 7, 6, 7, 6, 6, 2, 7, 6, 6, 3, 6, 3, 3, 2, 7, 6, 6, 4, 6, 4, 4, 2, + 6, 4, 4, 3, 4, 3, 3, 2, 7, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2, 6, 5, + 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 3, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 4, 8, 4, 4, 3, 8, 8, 8, 8, 8, 8, + 8, 5, 8, 8, 8, 5, 8, 5, 5, 3, 8, 8, 8, 5, 8, 5, 5, 4, 8, 5, 5, 4, 5, 4, 4, 3, + 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, 8, 6, 8, 6, 6, 3, 8, 8, 8, 6, 8, 6, 6, 4, 8, 6, + 6, 4, 6, 4, 4, 3, 8, 8, 8, 6, 8, 6, 6, 5, 8, 6, 6, 5, 6, 5, 5, 3, 8, 6, 6, 5, + 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 7, 8, 7, + 7, 3, 8, 8, 8, 7, 8, 7, 7, 4, 8, 7, 7, 4, 7, 4, 4, 3, 8, 8, 8, 7, 8, 7, 7, 5, + 8, 7, 7, 5, 7, 5, 5, 3, 8, 7, 7, 5, 7, 5, 5, 4, 7, 5, 5, 4, 5, 4, 4, 3, 8, 8, + 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, 6, 3, 8, 7, 7, 6, 7, 6, 6, 4, 7, 6, 6, 4, + 6, 4, 4, 3, 8, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 3, 7, 6, 6, 5, 6, 5, + 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 5, 8, 8, 8, 8, 8, 8, 8, 5, 8, 8, 8, 5, 8, 5, 5, 4, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, 8, 6, 8, 6, + 6, 4, 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, 8, 6, 8, 6, 6, 5, 8, 8, 8, 6, 8, 6, 6, 5, + 8, 6, 6, 5, 6, 5, 5, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, + 8, 8, 8, 8, 8, 7, 8, 8, 8, 7, 8, 7, 7, 4, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 7, + 8, 7, 7, 5, 8, 8, 8, 7, 8, 7, 7, 5, 8, 7, 7, 5, 7, 5, 5, 4, 8, 8, 8, 8, 8, 8, + 8, 7, 8, 8, 8, 7, 8, 7, 7, 6, 8, 8, 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, 6, 4, + 8, 8, 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, 6, 5, 8, 7, 7, 6, 7, 6, 6, 5, 7, 6, + 6, 5, 6, 5, 5, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, 8, 8, 8, 8, 8, 6, 8, 8, 8, 6, + 8, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, + 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 7, 8, 7, 7, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 7, 8, 7, 7, 6, 8, 8, 8, 8, + 8, 8, 8, 7, 8, 8, 8, 7, 8, 7, 7, 6, 8, 8, 8, 7, 8, 7, 7, 6, 8, 7, 7, 6, 7, 6, + 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 7, 8, 7, 7, 6, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7 +}; + +static inline uint64_t _select64(uint64_t x, int k) +{ + if (k >= popcnt(x)) { return 64; } + + const uint64_t kOnesStep4 = 0x1111111111111111ULL; + const uint64_t kOnesStep8 = 0x0101010101010101ULL; + const uint64_t kMSBsStep8 = 0x80ULL * kOnesStep8; + + uint64_t s = x; + s = s - ((s & 0xA * kOnesStep4) >> 1); + s = (s & 0x3 * kOnesStep4) + ((s >> 2) & 0x3 * kOnesStep4); + s = (s + (s >> 4)) & 0xF * kOnesStep8; + uint64_t byteSums = s * kOnesStep8; + + uint64_t kStep8 = k * kOnesStep8; + uint64_t geqKStep8 = (((kStep8 | kMSBsStep8) - byteSums) & kMSBsStep8); + uint64_t place = popcnt(geqKStep8) * 8; + uint64_t byteRank = k - (((byteSums << 8) >> place) & (uint64_t)(0xFF)); + return place + kSelectInByte[((x >> place) & 0xFF) | (byteRank << 8)]; +} + +// Returns the position of the rank'th 1. (rank = 0 returns the 1st 1) +// Returns 64 if there are fewer than rank+1 1s. +static inline uint64_t bitselect(uint64_t val, int rank) { +#ifdef __SSE4_2_ + uint64_t i = 1ULL << rank; + asm("pdep %[val], %[mask], %[val]" + : [val] "+r" (val) + : [mask] "r" (i)); + asm("tzcnt %[bit], %[index]" + : [index] "=r" (i) + : [bit] "g" (val) + : "cc"); + return i; +#endif + return _select64(val, rank); +} + +static inline uint64_t bitselectv(const uint64_t val, int ignore, int rank) +{ + return bitselect(val & ~BITMASK(ignore % 64), rank); +} + +#if BITS_PER_SLOT > 0 +static inline qfblock * get_block(const QF *qf, uint64_t block_index) +{ + return &qf->blocks[block_index]; +} +#else +static inline qfblock * get_block(const QF *qf, uint64_t block_index) +{ + // printf("block = %p\n",(void*)(((char *)qf->blocks) + block_index * (sizeof(qfblock) + + // qf->metadata->bits_per_slot * 8 + + // 8*qf->metadata->fixed_counter_size + + // 8*qf->metadata->tag_bits + // )) ); + //printf("blocks start=%p\n",qf->blocks ); + return (qfblock *)(((char *)qf->blocks) + block_index * (sizeof(qfblock) + + qf->metadata->bits_per_slot * 8 + )); +} +#endif + +static inline int is_runend(const QF *qf, uint64_t index) +{ + return (METADATA_WORD(qf, runends, index) >> ((index % SLOTS_PER_BLOCK) % + 64)) & 1ULL; +} + +static inline int is_occupied(const QF *qf, uint64_t index) +{ + return (METADATA_WORD(qf, occupieds, index) >> ((index % SLOTS_PER_BLOCK) % + 64)) & 1ULL; +} + +#if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 || BITS_PER_SLOT == 64 + +static inline uint64_t get_slot(const QF *qf, uint64_t index) +{ + assert(index < qf->metadata->xnslots); + return get_block(qf, index / SLOTS_PER_BLOCK)->slots[index % SLOTS_PER_BLOCK]; +} + +static inline void set_slot(const QF *qf, uint64_t index, uint64_t value) +{ + assert(index < qf->metadata->xnslots); + get_block(qf, index / SLOTS_PER_BLOCK)->slots[index % SLOTS_PER_BLOCK] = + value & BITMASK(qf->metadata->bits_per_slot); +} + +#elif BITS_PER_SLOT > 0 + +/* Little-endian code .... Big-endian is TODO */ + +static inline uint64_t get_slot(const QF *qf, uint64_t index) +{ + /* Should use __uint128_t to support up to 64-bit remainders, but gcc seems + * to generate buggy code. :/ */ + assert(index < qf->metadata->xnslots); + uint64_t *p = (uint64_t *)&get_block(qf, index / + SLOTS_PER_BLOCK)->slots[(index % + SLOTS_PER_BLOCK) + * BITS_PER_SLOT / 8]; + return (uint64_t)(((*p) >> (((index % SLOTS_PER_BLOCK) * BITS_PER_SLOT) % + 8)) & BITMASK(BITS_PER_SLOT)); +} + +static inline void set_slot(const QF *qf, uint64_t index, uint64_t value) +{ + /* Should use __uint128_t to support up to 64-bit remainders, but gcc seems + * to generate buggy code. :/ */ + assert(index < qf->metadata->xnslots); + + uint64_t *p = (uint64_t *)&get_block(qf, index / + SLOTS_PER_BLOCK)->slots[(index % + SLOTS_PER_BLOCK) + * BITS_PER_SLOT / 8]; + uint64_t t = *p; + uint64_t mask = BITMASK(BITS_PER_SLOT); + uint64_t v = value; + int shift = ((index % SLOTS_PER_BLOCK) * BITS_PER_SLOT) % 8; + mask <<= shift; + v <<= shift; + t &= ~mask; + t |= v; + *p = t; +} + +#else + + +/* Little-endian code .... Big-endian is TODO */ + +static inline uint64_t _get_slot(const QF *qf, uint64_t index) +{ + assert(index < qf->metadata->xnslots); + /* Should use __uint128_t to support up to 64-bit remainders, but gcc seems + * to generate buggy code. :/ */ + + uint64_t *p = (uint64_t *)&get_block(qf, index / + SLOTS_PER_BLOCK)->slots[(index % + SLOTS_PER_BLOCK) + * qf->metadata->bits_per_slot / 8]; + return (uint64_t)(((*p) >> (((index % SLOTS_PER_BLOCK) * + qf->metadata->bits_per_slot) % 8)) & + BITMASK(qf->metadata->bits_per_slot)); +} + +static inline void _set_slot(const QF *qf, uint64_t index, uint64_t value) +{ + assert(index < qf->metadata->xnslots); + /* Should use __uint128_t to support up to 64-bit remainders, but gcc seems + * to generate buggy code. :/ */ + //printf("ss %d\n",(index %SLOTS_PER_BLOCK)* qf->metadata->bits_per_slot / 8 ); + uint64_t *p = (uint64_t *)&get_block(qf, index / + SLOTS_PER_BLOCK)->slots[(index % + SLOTS_PER_BLOCK) + * qf->metadata->bits_per_slot / 8]; + + uint64_t t = *p; + uint64_t mask = BITMASK(qf->metadata->bits_per_slot); + uint64_t v = value; + int shift = ((index % SLOTS_PER_BLOCK) * qf->metadata->bits_per_slot) % 8; + mask <<= shift; + v <<= shift; + t &= ~mask; + t |= v; + *p = t; +} + +static inline void set_slot(const QF *qf, uint64_t index, uint64_t value){ + uint64_t original_value=_get_slot(qf,index); + value<<=qf->metadata->fixed_counter_size; + uint64_t mask=BITMASK(qf->metadata->fixed_counter_size); + original_value&=mask; + value|=original_value; + _set_slot(qf,index,value); +} + +static inline uint64_t get_slot(const QF *qf, uint64_t index) +{ + uint64_t mask=BITMASK(qf->metadata->key_remainder_bits); + uint64_t t=_get_slot(qf,index); + t >>= qf->metadata->fixed_counter_size; + + return t&mask; +} + + +#endif + +static inline uint64_t get_fixed_counter(const QF *qf, uint64_t index) +{ + uint64_t t=_get_slot(qf,index); + uint64_t mask=BITMASK(qf->metadata->fixed_counter_size); + return t&mask; + // uint64_t res=0; + // uint64_t base=1; + // uint64_t* p=(uint64_t*)((uint8_t*)get_block(qf, index /SLOTS_PER_BLOCK)->slots+ + // (SLOTS_PER_BLOCK * qf->metadata->bits_per_slot )/ 8); + // + // for(int i=qf->metadata->fixed_counter_size-1;i>=0;i--){ + // res+= base*(( p[i] >> ((index % SLOTS_PER_BLOCK) %64)) & 1ULL); + // base*=2; + // } + // + // return res; +} + inline void set_fixed_counter(const QF *qf, uint64_t index,uint64_t value) +{ + uint64_t mask=BITMASK(qf->metadata->fixed_counter_size); + uint64_t t=value & mask; + uint64_t original_value=_get_slot(qf,index); + original_value&= ~mask; + t|=original_value; + _set_slot(qf,index,t); + // //printf("bits per slot =%lu, fixed counter =%lu, tag_bits=%lu\n", + // //qf->metadata->bits_per_slot,qf->metadata->fixed_counter_size,qf->metadata->tag_bits ); + // //printf("slots start=%p\n",(void*) get_block(qf, index /SLOTS_PER_BLOCK)->slots); + // uint64_t* p=((uint64_t*)&get_block(qf, index /SLOTS_PER_BLOCK)->slots) + + // (qf->metadata->bits_per_slot) ; + // printf("block start=%p\n", (void*)get_block(qf, index /SLOTS_PER_BLOCK)); + // printf("slots start=%p\n", (void*)&get_block(qf, index /SLOTS_PER_BLOCK)->slots); + // //printf("fixed counter=%p index=%lu , value=%lu add=%d\n",(void*)p,index,value,(8 * qf->metadata->bits_per_slot) ); + // //printf("diff=%d\n",(char*)p-(char*) get_block(qf, index /SLOTS_PER_BLOCK) ); + // uint64_t bitmask=1ULL << ((index % SLOTS_PER_BLOCK) %64); + // + // int i= qf->metadata->fixed_counter_size-1 ; + // //printf("fp=%p\n",&p[i]); + // //printf("fp=%p\n",&p[0]); + // for(;i>=0;i--){ + // //printf("fp=%p\n",&p[i]); + // if(value%2){ + // p[i]|= bitmask; + // } + // else{ + // p[i]&= ~(bitmask); + // } + // value=value>>1; + // + // } + // //printf("finish\n" ); + + +} + +static inline uint64_t get_tag(const QF *qf, uint64_t index) +{ + uint64_t mask=BITMASK(qf->metadata->tag_bits); + uint64_t t=_get_slot(qf,index); + t >>= (qf->metadata->fixed_counter_size+qf->metadata->key_remainder_bits); + return t&mask; +} +static inline void set_tag(const QF *qf, uint64_t index,uint64_t value) +{ + uint64_t original_value=_get_slot(qf,index); + value<<=(qf->metadata->fixed_counter_size+qf->metadata->key_remainder_bits); + uint64_t mask=BITMASK(qf->metadata->fixed_counter_size+qf->metadata->key_remainder_bits); + original_value&=mask; + value|=original_value; + _set_slot(qf,index,value); + +} + + + +static inline uint64_t run_end(const QF *qf, uint64_t hash_bucket_index); + +static inline uint64_t block_offset(const QF *qf, uint64_t blockidx) +{ + /* If we have extended counters and a 16-bit (or larger) offset + field, then we can safely ignore the possibility of overflowing + that field. */ + if (sizeof(qf->blocks[0].offset) > 1 || + get_block(qf, blockidx)->offset < BITMASK(8*sizeof(qf->blocks[0].offset))) + return get_block(qf, blockidx)->offset; + + return run_end(qf, SLOTS_PER_BLOCK * blockidx - 1) - SLOTS_PER_BLOCK * + blockidx + 1; +} + +static inline uint64_t run_end(const QF *qf, uint64_t hash_bucket_index) +{ + uint64_t bucket_block_index = hash_bucket_index / SLOTS_PER_BLOCK; + uint64_t bucket_intrablock_offset = hash_bucket_index % SLOTS_PER_BLOCK; + uint64_t bucket_blocks_offset = block_offset(qf, bucket_block_index); + + uint64_t bucket_intrablock_rank = bitrank(get_block(qf, + bucket_block_index)->occupieds[0], + bucket_intrablock_offset); + + if (bucket_intrablock_rank == 0) { + if (bucket_blocks_offset <= bucket_intrablock_offset) + return hash_bucket_index; + else + return SLOTS_PER_BLOCK * bucket_block_index + bucket_blocks_offset - 1; + } + + uint64_t runend_block_index = bucket_block_index + bucket_blocks_offset / + SLOTS_PER_BLOCK; + uint64_t runend_ignore_bits = bucket_blocks_offset % SLOTS_PER_BLOCK; + uint64_t runend_rank = bucket_intrablock_rank - 1; + uint64_t runend_block_offset = bitselectv(get_block(qf, + runend_block_index)->runends[0], + runend_ignore_bits, runend_rank); + if (runend_block_offset == SLOTS_PER_BLOCK) { + if (bucket_blocks_offset == 0 && bucket_intrablock_rank == 0) { + /* The block begins in empty space, and this bucket is in that region of + * empty space */ + return hash_bucket_index; + } else { + do { + runend_rank -= popcntv(get_block(qf, + runend_block_index)->runends[0], + runend_ignore_bits); + runend_block_index++; + runend_ignore_bits = 0; + runend_block_offset = bitselectv(get_block(qf, + runend_block_index)->runends[0], + runend_ignore_bits, runend_rank); + } while (runend_block_offset == SLOTS_PER_BLOCK); + } + } + + uint64_t runend_index = SLOTS_PER_BLOCK * runend_block_index + + runend_block_offset; + if (runend_index < hash_bucket_index) + return hash_bucket_index; + else + return runend_index; +} + +static inline int offset_lower_bound(const QF *qf, uint64_t slot_index) +{ + const qfblock * b = get_block(qf, slot_index / SLOTS_PER_BLOCK); + const uint64_t slot_offset = slot_index % SLOTS_PER_BLOCK; + const uint64_t boffset = b->offset; + const uint64_t occupieds = b->occupieds[0] & BITMASK(slot_offset+1); + assert(SLOTS_PER_BLOCK == 64); + if (boffset <= slot_offset) { + const uint64_t runends = (b->runends[0] & BITMASK(slot_offset)) >> boffset; + return popcnt(occupieds) - popcnt(runends); + } + return boffset - slot_offset + popcnt(occupieds); +} + +static inline int is_empty(const QF *qf, uint64_t slot_index) +{ + return offset_lower_bound(qf, slot_index) == 0; +} + +static inline int might_be_empty(const QF *qf, uint64_t slot_index) +{ + return !is_occupied(qf, slot_index) + && !is_runend(qf, slot_index); +} + +static inline int probably_is_empty(const QF *qf, uint64_t slot_index) +{ + return get_slot(qf, slot_index) == 0 + && !is_occupied(qf, slot_index) + && !is_runend(qf, slot_index); +} + +static inline uint64_t find_first_empty_slot(QF *qf, uint64_t from) +{ + do { + int t = offset_lower_bound(qf, from); + assert(t>=0); + + if (t == 0) + break; + from = from + t; + } while(1); + return from; +} + +static inline uint64_t shift_into_b(const uint64_t a, const uint64_t b, + const int bstart, const int bend, + const int amount) +{ + const uint64_t a_component = bstart == 0 ? (a >> (64 - amount)) : 0; + const uint64_t b_shifted_mask = BITMASK(bend - bstart) << bstart; + const uint64_t b_shifted = ((b_shifted_mask & b) << amount) & b_shifted_mask; + const uint64_t b_mask = ~b_shifted_mask; + return a_component | b_shifted | (b & b_mask); +} + +#if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 || BITS_PER_SLOT == 64 + +static inline void shift_remainders(QF *qf, uint64_t start_index, uint64_t + empty_index) +{ + uint64_t start_block = start_index / SLOTS_PER_BLOCK; + uint64_t start_offset = start_index % SLOTS_PER_BLOCK; + uint64_t empty_block = empty_index / SLOTS_PER_BLOCK; + uint64_t empty_offset = empty_index % SLOTS_PER_BLOCK; + + assert (start_index <= empty_index && empty_index < qf->metadata->xnslots); + + while (start_block < empty_block) { + memmove(&get_block(qf, empty_block)->slots[1], + &get_block(qf, empty_block)->slots[0], + empty_offset * sizeof(qf->blocks[0].slots[0])); + get_block(qf, empty_block)->slots[0] = get_block(qf, + empty_block-1)->slots[SLOTS_PER_BLOCK-1]; + empty_block--; + empty_offset = SLOTS_PER_BLOCK-1; + } + + memmove(&get_block(qf, empty_block)->slots[start_offset+1], + &get_block(qf, empty_block)->slots[start_offset], + (empty_offset - start_offset) * sizeof(qf->blocks[0].slots[0])); +} + +#else + +#define REMAINDER_WORD(qf, i) ((uint64_t *)&(get_block(qf, (i)/qf->metadata->bits_per_slot)->slots[8 * ((i) % qf->metadata->bits_per_slot)])) + +static inline void shift_remainders(QF *qf, const uint64_t start_index, const + uint64_t empty_index) +{ + uint64_t last_word = (empty_index + 1) * qf->metadata->bits_per_slot / 64; + const uint64_t first_word = start_index * qf->metadata->bits_per_slot / 64; + int bend = ((empty_index + 1) * qf->metadata->bits_per_slot) % 64; + const int bstart = (start_index * qf->metadata->bits_per_slot) % 64; + + while (last_word != first_word) { + *REMAINDER_WORD(qf, last_word) = shift_into_b(*REMAINDER_WORD(qf, last_word-1), + *REMAINDER_WORD(qf, last_word), + 0, bend, qf->metadata->bits_per_slot); + last_word--; + bend = 64; + } + *REMAINDER_WORD(qf, last_word) = shift_into_b(0, *REMAINDER_WORD(qf, + last_word), + bstart, bend, + qf->metadata->bits_per_slot); +} + +#endif + +static inline void qf_dump_block(const QF *qf, uint64_t i) +{ + uint64_t j; + printf("#Block %lu \n",i ); + printf("%-192d", get_block(qf, i)->offset); + printf("\n"); + + for (j = 0; j < SLOTS_PER_BLOCK; j++) + printf("%02lx ", j); + printf("\n"); + + for (j = 0; j < SLOTS_PER_BLOCK; j++) + printf(" %d ", (get_block(qf, i)->occupieds[j/64] & (1ULL << (j%64))) ? 1 : 0); + printf("\n"); + + for (j = 0; j < SLOTS_PER_BLOCK; j++) + printf(" %d ", (get_block(qf, i)->runends[j/64] & (1ULL << (j%64))) ? 1 : 0); + printf("\n"); + +#if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 + for (j = 0; j < SLOTS_PER_BLOCK; j++) + printf("%02x ", get_block(qf, i)->slots[j]); +#elif BITS_PER_SLOT == 64 + for (j = 0; j < SLOTS_PER_BLOCK; j++) + printf("%02lx ", get_block(qf, i)->slots[j]); +#else + //for (j = 0; j < SLOTS_PER_BLOCK * qf->metadata->bits_per_slot / 8; j++) + // printf("%02x ", get_block(qf, i)->slots[j]); + +#endif + +for(int j=0;j<64;j++) +{ + printf("%lu ", get_slot(qf,j+64*i)); +} + + printf("\n fixed counter \n"); + + for(int j=0;j<64;j++) + { + printf("%lu ", get_fixed_counter(qf,j+64*i)); + } + + printf("\n tags \n"); + + for(int j=0;j<64;j++) + { + printf("%lu ", get_tag(qf,j+64*i)); + } + + + printf("\n"); + printf("\n"); +} + +void qf_dump(const QF *qf) +{ + uint64_t i; + + printf("%lu %lu %lu\n", + qf->metadata->nblocks, + qf->metadata->ndistinct_elts, + qf->metadata->nelts); + + for (i = 0; i < qf->metadata->nblocks; i++) { + qf_dump_block(qf, i); + } + printf("End\n"); + + + + +} + +static inline void find_next_n_empty_slots(QF *qf, uint64_t from, uint64_t n, + uint64_t *indices) +{ + while (n) { + indices[--n] = find_first_empty_slot(qf, from); + from = indices[n] + 1; + } +} + +static inline void shift_slots(QF *qf, int64_t first, uint64_t last, uint64_t + distance) +{ + int64_t i; + if (distance == 1) + shift_remainders(qf, first, last+1); + else + for (i = last; i >= first; i--) + _set_slot(qf, i + distance, _get_slot(qf, i)); +} + +static inline void shift_runends(QF *qf, int64_t first, uint64_t last, + uint64_t distance) +{ + assert(last < qf->metadata->xnslots && distance < 64); + uint64_t first_word = first / 64; + uint64_t bstart = first % 64; + uint64_t last_word = (last + distance + 1) / 64; + uint64_t bend = (last + distance + 1) % 64; + + if (last_word != first_word) { + METADATA_WORD(qf, runends, 64*last_word) = shift_into_b(METADATA_WORD(qf, runends, 64*(last_word-1)), + METADATA_WORD(qf, runends, 64*last_word), + 0, bend, distance); + bend = 64; + last_word--; + while (last_word != first_word) { + METADATA_WORD(qf, runends, 64*last_word) = shift_into_b(METADATA_WORD(qf, runends, 64*(last_word-1)), + METADATA_WORD(qf, runends, 64*last_word), + 0, bend, distance); + last_word--; + } + } + METADATA_WORD(qf, runends, 64*last_word) = shift_into_b(0, METADATA_WORD(qf, + runends, + 64*last_word), + bstart, bend, distance); + +} + +// static inline void shift_fixed_counters(QF *qf, int64_t first, uint64_t last, +// uint64_t distance) +// { +// assert(last < qf->metadata->xnslots && distance < 64); +// uint64_t first_word = first / 64; +// uint64_t bstart = first % 64; +// uint64_t last_word = (last + distance + 1) / 64; +// uint64_t bend = (last + distance + 1) % 64; +// uint64_t* curr, *prev; +// uint64_t tmp =last_word, tmp_bend=bend; +// for(int i=0;imetadata->fixed_counter_size;i++){ +// last_word=tmp; +// bend=tmp_bend; +// if (last_word != first_word) { +// curr=(uint64_t*)((uint8_t*)get_block(qf, last_word)->slots+ +// (SLOTS_PER_BLOCK * qf->metadata->bits_per_slot) / 8); +// prev=(uint64_t*)((uint8_t*)get_block(qf, last_word-1)->slots+ +// (SLOTS_PER_BLOCK * qf->metadata->bits_per_slot) / 8); +// curr[i] = shift_into_b(prev[i],curr[i],0, bend, distance); +// bend = 64; +// last_word--; +// while (last_word != first_word) { +// curr=(uint64_t*)((uint8_t*)get_block(qf, last_word)->slots+(SLOTS_PER_BLOCK * qf->metadata->bits_per_slot / 8)); +// prev=(uint64_t*)((uint8_t*)get_block(qf, last_word-1)->slots+(SLOTS_PER_BLOCK * qf->metadata->bits_per_slot / 8)); +// curr[i] = shift_into_b(prev[i],curr[i],0, bend, distance); +// last_word--; +// } +// } +// curr=(uint64_t*)((uint8_t*)get_block(qf, last_word)->slots+(SLOTS_PER_BLOCK * qf->metadata->bits_per_slot / 8)); +// curr[i] = shift_into_b(0,curr[i], bstart, bend, distance); +// } +// +// } + +// static inline void shift_tags(QF *qf, int64_t first, uint64_t last, +// uint64_t distance) +// { +// assert(last < qf->metadata->xnslots && distance < 64); +// uint64_t first_word = first / 64; +// uint64_t bstart = first % 64; +// uint64_t last_word = (last + distance + 1) / 64; +// uint64_t bend = (last + distance + 1) % 64; +// uint64_t* curr, *prev; +// uint64_t tmp =last_word, tmp_bend=bend; +// for(int i=0;imetadata->tag_bits;i++){ +// last_word=tmp; +// bend=tmp_bend; +// if (last_word != first_word) { +// curr=(uint64_t*)((uint8_t*)get_block(qf, last_word)->slots+ +// (8 *(qf->metadata->bits_per_slot + qf->metadata->fixed_counter_size ))); +// prev=(uint64_t*)((uint8_t*)get_block(qf, last_word-1)->slots+ +// (8 *(qf->metadata->bits_per_slot + qf->metadata->fixed_counter_size ))); +// curr[i] = shift_into_b(prev[i],curr[i],0, bend, distance); +// bend = 64; +// last_word--; +// while (last_word != first_word) { +// curr=(uint64_t*)((uint8_t*)get_block(qf, last_word)->slots+ +// (8 *(qf->metadata->bits_per_slot + qf->metadata->fixed_counter_size ))); +// prev=(uint64_t*)((uint8_t*)get_block(qf, last_word-1)->slots+ +// (8 *(qf->metadata->bits_per_slot + qf->metadata->fixed_counter_size ))); +// curr[i] = shift_into_b(prev[i],curr[i],0, bend, distance); +// last_word--; +// } +// } +// curr=(uint64_t*)((uint8_t*)get_block(qf, last_word)->slots+ +// (8 *(qf->metadata->bits_per_slot + qf->metadata->fixed_counter_size ))); +// curr[i] = shift_into_b(0,curr[i], bstart, bend, distance); +// } +// +// } + +static inline void insert_replace_slots_and_shift_remainders_and_runends_and_offsets(QF *qf, + int operation, + uint64_t bucket_index, + uint64_t overwrite_index, + const uint64_t *remainders, + const uint64_t *fixed_size_counters, + uint64_t total_remainders, + uint64_t noverwrites) +{ + uint64_t empties[67]; + uint64_t i; + int64_t ninserts = total_remainders - noverwrites; + uint64_t insert_index = overwrite_index + noverwrites; + if(qf->metadata->noccupied_slots+ninserts > qf->metadata->maximum_occupied_slots ) + { + throw std::overflow_error("QF is 95% full, cannot insert more items."); + } + //printf("remainder =%lu ,overwrite_index = %lu , insert_index=%lu , operation=%d, noverwites=%lu total_remainders=%lu nnserts=%lu \n", remainders[0],overwrite_index,insert_index,operation,noverwrites,total_remainders,ninserts); + if (ninserts > 0) { + /* First, shift things to create n empty spaces where we need them. */ + //printf("shift %lu, ninserts=%lu\n",insert_index,ninserts ); + + find_next_n_empty_slots(qf, insert_index, ninserts, empties); + for (i = 0; i < ninserts - 1; i++){ + shift_slots(qf, empties[i+1] + 1, empties[i] - 1, i + 1); + } + shift_slots(qf, insert_index, empties[ninserts - 1] - 1, ninserts); + + + + for (i = 0; i < ninserts - 1; i++) + shift_runends(qf, empties[i+1] + 1, empties[i] - 1, i + 1); + shift_runends(qf, insert_index, empties[ninserts - 1] - 1, ninserts); + + + + // for (i = 0; i < ninserts - 1; i++) + // shift_fixed_counters(qf, empties[i+1] + 1, empties[i] - 1, i + 1); + // shift_fixed_counters(qf, insert_index, empties[ninserts - 1] - 1, ninserts); + // + // + // for (i = 0; i < ninserts - 1; i++) + // shift_tags(qf, empties[i+1] + 1, empties[i] - 1, i + 1); + // shift_tags(qf, insert_index, empties[ninserts - 1] - 1, ninserts); + // + + + + for (i = noverwrites; i < total_remainders - 1; i++) + METADATA_WORD(qf, runends, overwrite_index + i) &= ~(1ULL << + (((overwrite_index + + i) % + SLOTS_PER_BLOCK) + % 64)); + // for (i = noverwrites; i < total_remainders - 1; i++) + // set_fixed_counter(qf,overwrite_index+i,0); + + + switch (operation) { + case 0: /* insert into empty bucket */ + assert (noverwrites == 0); + METADATA_WORD(qf, runends, overwrite_index + total_remainders - 1) |= + 1ULL << (((overwrite_index + total_remainders - 1) % + SLOTS_PER_BLOCK) % 64); + break; + case 1: /* append to bucket */ + METADATA_WORD(qf, runends, overwrite_index + noverwrites - 1) &= + ~(1ULL << (((overwrite_index + noverwrites - 1) % SLOTS_PER_BLOCK) % + 64)); + METADATA_WORD(qf, runends, overwrite_index + total_remainders - 1) |= + 1ULL << (((overwrite_index + total_remainders - 1) % + SLOTS_PER_BLOCK) % 64); + break; + case 2: /* insert into bucket */ + METADATA_WORD(qf, runends, overwrite_index + total_remainders - 1) &= + ~(1ULL << (((overwrite_index + total_remainders - 1) % + SLOTS_PER_BLOCK) % 64)); + break; + default: + fprintf(stderr, "Invalid operation %d\n", operation); + abort(); + } + + uint64_t npreceding_empties = 0; + for (i = bucket_index / SLOTS_PER_BLOCK + 1; i <= empties[0]/SLOTS_PER_BLOCK; i++) { + while (npreceding_empties < ninserts && + empties[ninserts - 1 - npreceding_empties] / SLOTS_PER_BLOCK < i) + npreceding_empties++; + + if (get_block(qf, i)->offset + ninserts - npreceding_empties < BITMASK(8*sizeof(qf->blocks[0].offset))) + get_block(qf, i)->offset += ninserts - npreceding_empties; + else + get_block(qf, i)->offset = (uint8_t) BITMASK(8*sizeof(qf->blocks[0].offset)); + } + } + for (i = 0; i < total_remainders; i++){ + set_slot(qf, overwrite_index + i, remainders[i]); + set_fixed_counter(qf,overwrite_index +i,fixed_size_counters[i]); +// printf("fixed counter = %lu\n",fixed_size_counters[i] ); + } + + modify_metadata(qf, &qf->metadata->noccupied_slots, ninserts); +} + +static inline void remove_replace_slots_and_shift_remainders_and_runends_and_offsets(QF *qf, + int operation, + uint64_t bucket_index, + uint64_t overwrite_index, + const uint64_t *remainders, + const uint64_t *fcounters, + uint64_t total_remainders, + uint64_t old_length) +{ + uint64_t i; + + // Update the slots + for (i = 0; i < total_remainders; i++){ + set_slot(qf, overwrite_index + i, remainders[i]); + set_fixed_counter(qf, overwrite_index + i, fcounters[i]); + if(qf->metadata->tag_bits>0) + set_tag(qf, overwrite_index + i, 0); + } + + + // If this is the last thing in its run, then we may need to set a new runend bit + if (is_runend(qf, overwrite_index + old_length - 1)) { + if (total_remainders > 0) { + // If we're not deleting this entry entirely, then it will still the last entry in this run + METADATA_WORD(qf, runends, overwrite_index + total_remainders - 1) |= 1ULL << ((overwrite_index + total_remainders - 1) % 64); + } else if (overwrite_index > bucket_index && + !is_runend(qf, overwrite_index - 1)) { + // If we're deleting this entry entirely, but it is not the first entry in this run, + // then set the preceding entry to be the runend + METADATA_WORD(qf, runends, overwrite_index - 1) |= 1ULL << ((overwrite_index - 1) % 64); + } + } + + // shift slots back one run at a time + uint64_t original_bucket = bucket_index; + uint64_t current_bucket = bucket_index; + uint64_t current_slot = overwrite_index + total_remainders; + uint64_t current_distance = old_length - total_remainders; + + while (current_distance > 0) { + if (is_runend(qf, current_slot + current_distance - 1)) { + do { + current_bucket++; + } while (current_bucket < current_slot + current_distance && + !is_occupied(qf, current_bucket)); + } + + if (current_bucket <= current_slot) { + set_slot(qf, current_slot, get_slot(qf, current_slot + current_distance)); + set_fixed_counter(qf, current_slot, get_fixed_counter(qf, current_slot + current_distance)); + if(qf->metadata->tag_bits>0) + set_tag(qf, current_slot, get_tag(qf, current_slot + current_distance)); + + if (is_runend(qf, current_slot) != + is_runend(qf, current_slot + current_distance)) + METADATA_WORD(qf, runends, current_slot) ^= 1ULL << (current_slot % 64); + current_slot++; + + } else if (current_bucket <= current_slot + current_distance) { + uint64_t i; + for (i = current_slot; i < current_slot + current_distance; i++) { + set_slot(qf, i, 0); + set_fixed_counter(qf,i,0); + if(qf->metadata->tag_bits>0) + set_tag(qf,i,0); + METADATA_WORD(qf, runends, i) &= ~(1ULL << (i % 64)); + } + + current_distance = current_slot + current_distance - current_bucket; + current_slot = current_bucket; + } else { + current_distance = 0; + } + } + + // reset the occupied bit of the hash bucket index if the hash is the + // only item in the run and is removed completely. + if (operation && !total_remainders) + METADATA_WORD(qf, occupieds, bucket_index) &= ~(1ULL << (bucket_index % 64)); + + // update the offset bits. + // find the number of occupied slots in the original_bucket block. + // Then find the runend slot corresponding to the last run in the + // original_bucket block. + // Update the offset of the block to which it belongs. + uint64_t original_block = original_bucket / SLOTS_PER_BLOCK; + while (1 && old_length > total_remainders) { // we only update offsets if we shift/delete anything + int32_t last_occupieds_bit = bitscanreverse(get_block(qf, original_block)->occupieds[0]); + // there is nothing in the block + if (last_occupieds_bit == -1) { + if (get_block(qf, original_block + 1)->offset == 0) + break; + get_block(qf, original_block + 1)->offset = 0; + } else { + uint64_t last_occupieds_hash_index = SLOTS_PER_BLOCK * original_block + last_occupieds_bit; + uint64_t runend_index = run_end(qf, last_occupieds_hash_index); + // runend spans across the block + // update the offset of the next block + if (runend_index / SLOTS_PER_BLOCK == original_block) { // if the run ends in the same block + if (get_block(qf, original_block + 1)->offset == 0) + break; + get_block(qf, original_block + 1)->offset = 0; + } else if (runend_index / SLOTS_PER_BLOCK == original_block + 1) { // if the last run spans across one block + if (get_block(qf, original_block + 1)->offset == (runend_index % SLOTS_PER_BLOCK) + 1) + break; + get_block(qf, original_block + 1)->offset = (runend_index % SLOTS_PER_BLOCK) + 1; + } else { // if the last run spans across multiple blocks + uint64_t i; + for (i = original_block + 1; i < runend_index / SLOTS_PER_BLOCK - 1; i++) + get_block(qf, i)->offset = SLOTS_PER_BLOCK; + if (get_block(qf, runend_index / SLOTS_PER_BLOCK)->offset == (runend_index % SLOTS_PER_BLOCK) + 1) + break; + get_block(qf, runend_index / SLOTS_PER_BLOCK)->offset = (runend_index % SLOTS_PER_BLOCK) + 1; + } + } + original_block++; + } + + int num_slots_freed = old_length - total_remainders; + modify_metadata(qf, &qf->metadata->noccupied_slots, -num_slots_freed); + /*qf->metadata->noccupied_slots -= (old_length - total_remainders);*/ + if (!total_remainders) { + modify_metadata(qf, &qf->metadata->ndistinct_elts, -1); + /*qf->metadata->ndistinct_elts--;*/ + } +} + +/***************************************************************************** + * Code that uses the above to implement a QF with keys and inline counters. * + *****************************************************************************/ + +/* + Counter format: + 1- count <= 2^(fixed counter size) + Slots : [Remaining] + Fixed counters : [count-1] + + 2- count > 2^(fixed counter size) + Slots : [Remaining] [first digit] [second digit] ... [last digit] + Fixed counters : [Maximum] [Maximum] [Maximum] ... [up to Maximum -1] + + */ +static inline uint64_t *encode_counter(QF *qf, uint64_t remainder, uint64_t + counter, uint64_t *slots, uint64_t *fixed_size_counters) +{ + //printf("inserting %lu repeated %lu\n",remainder,counter); + const uint64_t slots_base = (1ULL << qf->metadata->key_remainder_bits) ; + uint64_t *p = slots; + uint64_t *pf = fixed_size_counters; + const uint64_t fixed_counter_max=(1ULL<metadata->fixed_counter_size)-1; + + if (counter == 0) + return p; + + counter--; + uint64_t fcounter_first=std::min(counter,fixed_counter_max); + counter-=(fcounter_first); + + //printf("first fixed counter =%lu\n", fcounter_first); + if(fcounter_first==fixed_counter_max){ + uint64_t max_count_in_fixed_counter=fixed_counter_max-1;// the fixed size count in the end of the counter should'nt be full + do{ + *--p=counter%slots_base; + *--pf=fixed_counter_max; + //printf("vcount = %lu\n",counter%slots_base ); + counter >>= qf->metadata->key_remainder_bits; + } while(counter>max_count_in_fixed_counter); + *(fixed_size_counters-1)=counter;// set the last counter + //printf("last fixed counter = %lu\n",counter); + } + + *--p = remainder; + *--pf=fcounter_first; + + + return p; +} + +/* Returns the length of the encoding. +REQUIRES: index points to first slot of a counter. */ +static inline uint64_t decode_counter(const QF *qf, uint64_t index, uint64_t + *remainder, uint64_t *count) +{ + + *remainder = get_slot(qf, index); + uint64_t fcount=get_fixed_counter(qf,index); + uint64_t tmp_count= fcount+1; + *count=0; + const uint64_t fixed_count_max=(1ULL << qf->metadata->fixed_counter_size)-1; + //printf("tmp count = %lu\n",tmp_count); + if(fcount == fixed_count_max){ + uint64_t no_digits=0; + do{ + index++; + no_digits++; + *count <<= qf->metadata->key_remainder_bits; + + fcount= get_fixed_counter(qf,index); + // printf("quer slot =%lu fixed count= %lu\n", get_slot(qf, index),fcount); + *count += get_slot(qf, index); + + }while(fcount == fixed_count_max); + *count += fcount<<(no_digits*qf->metadata->key_remainder_bits); + //printf("fixed vcount= %lu\n", fcount<<(no_digits*qf->metadata->bits_per_slot + qf->metadata->fixed_counter_size)); + } + *count += tmp_count; + return index; + +} + +/* return the next slot which corresponds to a + * different element + * */ +static inline uint64_t next_slot(QF *qf, uint64_t current) +{ + uint64_t rem = get_slot(qf, current); + current++; + + while (get_slot(qf, current) == rem && current <= qf->metadata->nslots) { + current++; + } + return current; +} + +// static inline bool insert1(QF *qf, __uint128_t hash, bool lock, bool spin) +// { +// uint64_t hash_remainder = hash & BITMASK(qf->metadata->bits_per_slot); +// uint64_t hash_bucket_index = hash >> qf->metadata->bits_per_slot; +// uint64_t hash_bucket_block_offset = hash_bucket_index % SLOTS_PER_BLOCK; +// +// if (lock) { +// if (!qf_lock(qf, hash_bucket_index, spin, true)) +// return false; +// } +// if (is_empty(qf, hash_bucket_index) /* might_be_empty(qf, hash_bucket_index) && runend_index == hash_bucket_index */) { +// METADATA_WORD(qf, runends, hash_bucket_index) |= 1ULL << +// (hash_bucket_block_offset % 64); +// set_slot(qf, hash_bucket_index, hash_remainder); +// METADATA_WORD(qf, occupieds, hash_bucket_index) |= 1ULL << +// (hash_bucket_block_offset % 64); +// +// /*modify_metadata(qf, &qf->metadata->ndistinct_elts, 1);*/ +// modify_metadata(qf, &qf->metadata->noccupied_slots, 1); +// /*modify_metadata(qf, &qf->metadata->nelts, 1);*/ +// } else { +// uint64_t runend_index = run_end(qf, hash_bucket_index); +// int operation = 0; /* Insert into empty bucket */ +// uint64_t insert_index = runend_index + 1; +// uint64_t new_value = hash_remainder; +// +// /* printf("RUNSTART: %02lx RUNEND: %02lx\n", runstart_index, runend_index); */ +// +// uint64_t runstart_index = hash_bucket_index == 0 ? 0 : run_end(qf, +// hash_bucket_index +// - 1) + 1; +// +// if (is_occupied(qf, hash_bucket_index)) { +// +// /* Find the counter for this remainder if it exists. */ +// uint64_t current_remainder = get_slot(qf, runstart_index); +// uint64_t zero_terminator = runstart_index; +// +// /* The counter for 0 is special. */ +// if (current_remainder == 0) { +// uint64_t t = runstart_index + 1; +// while (t < runend_index && get_slot(qf, t) != 0) +// t++; +// if (t < runend_index && get_slot(qf, t+1) == 0) +// zero_terminator = t+1; /* Three or more 0s */ +// else if (runstart_index < runend_index && get_slot(qf, runstart_index +// + 1) == 0) +// zero_terminator = runstart_index + 1; /* Exactly two 0s */ +// /* Otherwise, exactly one 0 (i.e. zero_terminator == runstart_index) */ +// +// /* May read past end of run, but that's OK because loop below +// can handle that */ +// if (hash_remainder != 0) { +// runstart_index = zero_terminator + 1; +// current_remainder = get_slot(qf, runstart_index); +// } +// } +// +// /* Skip over counters for other remainders. */ +// while (current_remainder < hash_remainder && runstart_index <= +// runend_index) { +// /* If this remainder has an extended counter, skip over it. */ +// if (runstart_index < runend_index && +// get_slot(qf, runstart_index + 1) < current_remainder) { +// runstart_index = runstart_index + 2; +// while (runstart_index < runend_index && +// get_slot(qf, runstart_index) != current_remainder) +// runstart_index++; +// runstart_index++; +// +// /* This remainder has a simple counter. */ +// } else { +// runstart_index++; +// } +// +// /* This may read past the end of the run, but the while loop +// condition will prevent us from using the invalid result in +// that case. */ +// current_remainder = get_slot(qf, runstart_index); +// } +// +// /* If this is the first time we've inserted the new remainder, +// and it is larger than any remainder in the run. */ +// if (runstart_index > runend_index) { +// operation = 1; +// insert_index = runstart_index; +// new_value = hash_remainder; +// /*modify_metadata(qf, &qf->metadata->ndistinct_elts, 1);*/ +// +// /* This is the first time we're inserting this remainder, but +// there are larger remainders already in the run. */ +// } else if (current_remainder != hash_remainder) { +// operation = 2; /* Inserting */ +// insert_index = runstart_index; +// new_value = hash_remainder; +// /*modify_metadata(qf, &qf->metadata->ndistinct_elts, 1);*/ +// +// /* Cases below here: we're incrementing the (simple or +// extended) counter for this remainder. */ +// +// /* If there's exactly one instance of this remainder. */ +// } else if (runstart_index == runend_index || +// (hash_remainder > 0 && get_slot(qf, runstart_index + 1) > +// hash_remainder) || +// (hash_remainder == 0 && zero_terminator == runstart_index)) { +// operation = 2; /* Insert */ +// insert_index = runstart_index; +// new_value = hash_remainder; +// +// /* If there are exactly two instances of this remainder. */ +// } else if ((hash_remainder > 0 && get_slot(qf, runstart_index + 1) == +// hash_remainder) || +// (hash_remainder == 0 && zero_terminator == runstart_index + 1)) { +// operation = 2; /* Insert */ +// insert_index = runstart_index + 1; +// new_value = 0; +// +// /* Special case for three 0s */ +// } else if (hash_remainder == 0 && zero_terminator == runstart_index + 2) { +// operation = 2; /* Insert */ +// insert_index = runstart_index + 1; +// new_value = 1; +// +// /* There is an extended counter for this remainder. */ +// } else { +// +// /* Move to the LSD of the counter. */ +// insert_index = runstart_index + 1; +// while (get_slot(qf, insert_index+1) != hash_remainder) +// insert_index++; +// +// /* Increment the counter. */ +// uint64_t digit, carry; +// do { +// carry = 0; +// digit = get_slot(qf, insert_index); +// // Convert a leading 0 (which is special) to a normal encoded digit +// if (digit == 0) { +// digit++; +// if (digit == current_remainder) +// digit++; +// } +// +// // Increment the digit +// digit = (digit + 1) & BITMASK(qf->metadata->bits_per_slot); +// +// // Ensure digit meets our encoding requirements +// if (digit == 0) { +// digit++; +// carry = 1; +// } +// if (digit == current_remainder) +// digit = (digit + 1) & BITMASK(qf->metadata->bits_per_slot); +// if (digit == 0) { +// digit++; +// carry = 1; +// } +// +// set_slot(qf, insert_index, digit); +// insert_index--; +// } while(insert_index > runstart_index && carry); +// +// /* If the counter needs to be expanded. */ +// if (insert_index == runstart_index && (carry > 0 || (current_remainder +// != 0 && digit >= +// current_remainder))) +// { +// operation = 2; /* insert */ +// insert_index = runstart_index + 1; +// if (!carry) /* To prepend a 0 before the counter if the MSD is greater than the rem */ +// new_value = 0; +// else if (carry) { /* Increment the new value because we don't use 0 to encode counters */ +// new_value = 2; +// /* If the rem is greater than or equal to the new_value then fail*/ +// assert(new_value < current_remainder); +// } +// } else { +// operation = -1; +// } +// } +// } +// +// if (operation >= 0) { +// uint64_t empty_slot_index = find_first_empty_slot(qf, runend_index+1); +// +// shift_remainders(qf, insert_index, empty_slot_index); +// +// set_slot(qf, insert_index, new_value); +// +// shift_runends(qf, insert_index, empty_slot_index-1, 1); +// shift_fixed_counters(qf, insert_index, empty_slot_index-1, 1); +// switch (operation) { +// case 0: +// METADATA_WORD(qf, runends, insert_index) |= 1ULL << ((insert_index +// % +// SLOTS_PER_BLOCK) +// % 64); +// break; +// case 1: +// METADATA_WORD(qf, runends, insert_index-1) &= ~(1ULL << +// (((insert_index-1) % +// SLOTS_PER_BLOCK) % +// 64)); +// METADATA_WORD(qf, runends, insert_index) |= 1ULL << ((insert_index +// % +// SLOTS_PER_BLOCK) +// % 64); +// break; +// case 2: +// METADATA_WORD(qf, runends, insert_index) &= ~(1ULL << +// ((insert_index % +// SLOTS_PER_BLOCK) % +// 64)); +// break; +// default: +// fprintf(stderr, "Invalid operation %d\n", operation); +// abort(); +// } +// /* +// * Increment the offset for each block between the hash bucket index +// * and block of the empty slot +// * */ +// uint64_t i; +// for (i = hash_bucket_index / SLOTS_PER_BLOCK + 1; i <= +// empty_slot_index/SLOTS_PER_BLOCK; i++) { +// if (get_block(qf, i)->offset < BITMASK(8*sizeof(qf->blocks[0].offset))) +// get_block(qf, i)->offset++; +// assert(get_block(qf, i)->offset != 0); +// } +// modify_metadata(qf, &qf->metadata->noccupied_slots, 1); +// } +// /*modify_metadata(qf, &qf->metadata->nelts, 1);*/ +// METADATA_WORD(qf, occupieds, hash_bucket_index) |= 1ULL << +// (hash_bucket_block_offset % 64); +// } +// +// if (lock) { +// qf_unlock(qf, hash_bucket_index, true); +// } +// +// return true; +// } + +static inline bool insert(QF *qf, __uint128_t hash, uint64_t count, bool lock=false, + bool spin=false) +{ + uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); + uint64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; + uint64_t hash_bucket_block_offset = hash_bucket_index % SLOTS_PER_BLOCK; + /*uint64_t hash_bucket_lock_offset = hash_bucket_index % NUM_SLOTS_TO_LOCK;*/ + //printf("index= %lu remainder= %lu count=%lu\n",hash_bucket_index,hash_remainder,count); + if(hash_bucket_index > qf->metadata->xnslots){ + throw std::out_of_range("Insert is called with hash index out of range"); + } + if (lock) { + if (!qf_lock(qf, hash_bucket_index, spin, false)) + return false; + } + + uint64_t runend_index = run_end(qf, hash_bucket_index); + + /* Empty slot */ + if (might_be_empty(qf, hash_bucket_index) && runend_index == + hash_bucket_index) { + METADATA_WORD(qf, runends, hash_bucket_index) |= 1ULL << + (hash_bucket_block_offset % 64); + set_slot(qf, hash_bucket_index, hash_remainder); + set_fixed_counter(qf, hash_bucket_index, 0); + METADATA_WORD(qf, occupieds, hash_bucket_index) |= 1ULL << + (hash_bucket_block_offset % 64); + + modify_metadata(qf, &qf->metadata->ndistinct_elts, 1); + modify_metadata(qf, &qf->metadata->noccupied_slots, 1); + /*modify_metadata(qf, &qf->metadata->nelts, 1);*/ + /* This trick will, I hope, keep the fast case fast. */ + if (count > 1) { + insert(qf, hash, count - 1, false, false); + } + } else { /* Non-empty slot */ + uint64_t new_values[67]; + uint64_t new_fcounters[67]; + uint64_t total_remainders; + int64_t runstart_index = hash_bucket_index == 0 ? 0 : run_end(qf, + hash_bucket_index + - 1) + 1; + + if (!is_occupied(qf, hash_bucket_index)) { /* Empty bucket, but its slot is occupied. */ + uint64_t *p = encode_counter(qf, hash_remainder, count, &new_values[67],&new_fcounters[67]); + total_remainders=&new_values[67] - p; + insert_replace_slots_and_shift_remainders_and_runends_and_offsets(qf, + 0, + hash_bucket_index, + runstart_index, + p, + &new_fcounters[67]-total_remainders, + &new_values[67] - p, + 0); + modify_metadata(qf, &qf->metadata->ndistinct_elts, 1); + } else { /* Non-empty bucket */ + + uint64_t current_remainder, current_count, current_end; + + /* Find the counter for this remainder, if one exists. */ + current_end = decode_counter(qf, runstart_index, ¤t_remainder, + ¤t_count); + while (current_remainder < hash_remainder && !is_runend(qf, current_end)) { + runstart_index = current_end + 1; + current_end = decode_counter(qf, runstart_index, ¤t_remainder, + ¤t_count); + } + + /* If we reached the end of the run w/o finding a counter for this remainder, + then append a counter for this remainder to the run. */ + if (current_remainder < hash_remainder) { + uint64_t *p = encode_counter(qf, hash_remainder, count, &new_values[67],&new_fcounters[67]); + total_remainders=&new_values[67] - p; + insert_replace_slots_and_shift_remainders_and_runends_and_offsets(qf, + 1, /* Append to bucket */ + hash_bucket_index, + current_end + 1, + p, + &new_fcounters[67]-total_remainders, + &new_values[67] - p, + 0); + modify_metadata(qf, &qf->metadata->ndistinct_elts, 1); + /* Found a counter for this remainder. Add in the new count. */ + } else if (current_remainder == hash_remainder) { + uint64_t *p = encode_counter(qf, hash_remainder, current_count + count, &new_values[67],&new_fcounters[67]); + total_remainders=&new_values[67] - p; + insert_replace_slots_and_shift_remainders_and_runends_and_offsets(qf, + is_runend(qf, current_end) ? 1 : 2, + hash_bucket_index, + runstart_index, + p, + &new_fcounters[67]-total_remainders, + &new_values[67] - p, + current_end - runstart_index + 1); + /* No counter for this remainder, but there are larger + remainders, so we're not appending to the bucket. */ + } else { + uint64_t *p = encode_counter(qf, hash_remainder, count, &new_values[67],&new_fcounters[67]); + total_remainders=&new_values[67] - p; + insert_replace_slots_and_shift_remainders_and_runends_and_offsets(qf, + 2, /* Insert to bucket */ + hash_bucket_index, + runstart_index, + p, + &new_fcounters[67]-total_remainders, + &new_values[67] - p, + 0); + modify_metadata(qf, &qf->metadata->ndistinct_elts, 1); + } + } + METADATA_WORD(qf, occupieds, hash_bucket_index) |= 1ULL << (hash_bucket_block_offset % 64); + + /*modify_metadata(qf, &qf->metadata->nelts, count);*/ + } + + if (lock) { + qf_unlock(qf, hash_bucket_index, false); + } + + return true; +} + + bool qf_remove(QF *qf, uint64_t hash, uint64_t count , bool lock, bool spin) +{ + uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); + uint64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; + uint64_t current_remainder, current_count, current_end; + uint64_t new_values[67]; + uint64_t new_fcounters[67]; + + if(hash_bucket_index > qf->metadata->xnslots){ + throw std::out_of_range("Remove function is called with hash index out of range"); + } + + + /* Empty bucket */ + if (!is_occupied(qf, hash_bucket_index)){ + return true; + } + + uint64_t runstart_index = hash_bucket_index == 0 ? 0 : run_end(qf, hash_bucket_index - 1) + 1; + uint64_t original_runstart_index = runstart_index; + int only_item_in_the_run = 0; + + /*Find the counter for this remainder, if one exists.*/ + current_end = decode_counter(qf, runstart_index, ¤t_remainder, ¤t_count); + while (current_remainder < hash_remainder && !is_runend(qf, current_end)) { + runstart_index = current_end + 1; + current_end = decode_counter(qf, runstart_index, ¤t_remainder, ¤t_count); + } + /* remainder not found in the given run */ + if (current_remainder != hash_remainder){ + return true; + } + + if (original_runstart_index == runstart_index && is_runend(qf, current_end)) + only_item_in_the_run = 1; + + + /* endode the new counter */ + uint64_t *p = encode_counter(qf, hash_remainder, + count>current_count? 0 : current_count-count, + &new_values[67],&new_fcounters[67]); + + uint64_t total_reminders=&new_values[67] - p; + // if(fcounter==0 && newcount==0){ + // total_reminders=0; + // p=&new_values[67]; + // } + if (lock) { + if (!qf_lock(qf, hash_bucket_index, spin, false)) + return false; + } + + remove_replace_slots_and_shift_remainders_and_runends_and_offsets(qf, + only_item_in_the_run, + hash_bucket_index, + runstart_index, + p, + &new_fcounters[67]-total_reminders, + total_reminders, + current_end - runstart_index + 1); + + + if (lock) { + qf_unlock(qf, hash_bucket_index, true); + } + + return true; + // update the nelements. + /*modify_metadata(qf, &qf->metadata->nelts, -count);*/ + /*qf->metadata->nelts -= count;*/ +} + +/*********************************************************************** + * Code that uses the above to implement key-value-counter operations. * + ***********************************************************************/ + +void qf_init(QF *qf, uint64_t nslots, uint64_t key_bits, uint64_t tag_bits,uint64_t fixed_counter_size, + bool mem, const char * path, uint32_t seed) +{ + //qf=(QF*)calloc(sizeof(QF),1); + uint64_t num_slots, xnslots, nblocks; + uint64_t key_remainder_bits, bits_per_slot; + uint64_t size; + + + if(popcnt(nslots) != 1){ + throw std::domain_error("nslots must be a power of 2"); + + } + num_slots = nslots; + + xnslots = nslots + 10*sqrt((double)nslots); + nblocks = (xnslots + SLOTS_PER_BLOCK - 1) / SLOTS_PER_BLOCK; + key_remainder_bits = key_bits; + while (nslots > 1) { + //assert(key_remainder_bits > 0); + key_remainder_bits--; + nslots >>= 1; + } + + bits_per_slot = key_remainder_bits+fixed_counter_size+tag_bits ; + //assert (BITS_PER_SLOT == 0 || BITS_PER_SLOT == qf->metadata->bits_per_slot); + //assert(bits_per_slot > 1); +// #if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 || BITS_PER_SLOT == 64 +// size = nblocks * sizeof(qfblock) + (64)*fixed_counter_size; +// #else +// size = nblocks * (sizeof(qfblock) + (SLOTS_PER_BLOCK * bits_per_slot / 8) + +// fixed_counter_size*8 + tag_bits*8 ); +// #endif +//printf("bits per slot =%lu,key remainder bits =%lu, fixed counter =%lu, tag_bits=%lu\n", +//bits_per_slot,key_remainder_bits,fixed_counter_size,tag_bits ); +size = nblocks * (sizeof(qfblock) + (8 * bits_per_slot )) ; + +qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); + + if (mem) { + qf->metadata = (qfmetadata *)calloc(sizeof(qfmetadata), 1); + qf->metadata->mem=mem; + qf->metadata->size = size; + qf->metadata->seed = seed; + qf->metadata->nslots = num_slots; + qf->metadata->xnslots = qf->metadata->nslots + + 10*sqrt((double)qf->metadata->nslots); + qf->metadata->key_bits = key_bits; + qf->metadata->tag_bits = tag_bits; + qf->metadata->fixed_counter_size = fixed_counter_size; + qf->metadata->key_remainder_bits = key_remainder_bits; + qf->metadata->bits_per_slot = bits_per_slot; + + qf->metadata->range = qf->metadata->nslots; + qf->metadata->range <<= qf->metadata->key_remainder_bits; + qf->metadata->nblocks = (qf->metadata->xnslots + SLOTS_PER_BLOCK - 1) / + SLOTS_PER_BLOCK; + qf->metadata->nelts = 0; + qf->metadata->ndistinct_elts = 0; + qf->metadata->noccupied_slots = 0; + qf->metadata->maximum_occupied_slots=(uint64_t)((double)qf->metadata->xnslots *0.95); + qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; + + qf->blocks = (qfblock *)calloc(size, 1); + + + } else { + + qf->mem->fd = open(path, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU); + if (qf->mem->fd < 0) { + perror("Couldn't open file:\n"); + exit(EXIT_FAILURE); + } + + /* prashantpandey: Commenting out fallocate call to preallocate space for + * the file on disk because fallocate is not supported on MAC OS. Revisit + * it later. */ + // int ret; + // ret = fallocate(qf->mem->fd, 0, 0, size+sizeof(qfmetadata)); + // if (ret < 0) { + // perror("Couldn't fallocate file:\n"); + // exit(EXIT_FAILURE); + // } + + // allocate space for mmaped file + std::ofstream outputFile(path); + outputFile.seekp(size+sizeof(qfmetadata)); + outputFile<<0; + outputFile.close(); + + + + qf->metadata = (qfmetadata *)mmap(NULL, size+sizeof(qfmetadata), PROT_READ | + PROT_WRITE, MAP_SHARED, qf->mem->fd, 0); + qf->metadata->mem=mem; + qf->metadata->size = size; + qf->metadata->seed = seed; + qf->metadata->nslots = num_slots; + qf->metadata->xnslots = qf->metadata->nslots + + 10*sqrt((double)qf->metadata->nslots); + qf->metadata->key_bits = key_bits; + qf->metadata->tag_bits = tag_bits; + qf->metadata->fixed_counter_size = fixed_counter_size; + qf->metadata->key_remainder_bits = key_remainder_bits; + qf->metadata->bits_per_slot = bits_per_slot; + + qf->metadata->range = qf->metadata->nslots; + qf->metadata->range <<= qf->metadata->key_remainder_bits; + qf->metadata->nblocks = (qf->metadata->xnslots + SLOTS_PER_BLOCK - 1) / + SLOTS_PER_BLOCK; + qf->metadata->nelts = 0; + qf->metadata->ndistinct_elts = 0; + qf->metadata->noccupied_slots = 0; + qf->metadata->maximum_occupied_slots=(uint64_t)((double)qf->metadata->xnslots *0.95); + qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; + + qf->blocks = (qfblock *)(qf->metadata + 1); + } + + /* initialize all the locks to 0 */ + qf->mem->metadata_lock = 0; + qf->mem->locks = (volatile int *)calloc(qf->metadata->num_locks, + sizeof(volatile int)); + + +#ifdef LOG_WAIT_TIME + qf->mem->wait_times = (wait_time_data* )calloc(qf->metadata->num_locks+1, + sizeof(wait_time_data)); +#endif +} + +/* The caller should call qf_init on the dest QF before calling this function. + */ +void qf_copy(QF *dest, QF *src) +{ + memcpy(dest->mem, src->mem, sizeof(qfmem)); + memcpy(dest->metadata, src->metadata, sizeof(qfmetadata)); + memcpy(dest->blocks, src->blocks, src->metadata->size); +} + +/* free up the memory if the QF is in memory. + * else unmap the mapped memory from pagecache. + * + * It does not delete the file on disk for on-disk QF. + */ +void qf_destroy(QF *qf) +{ + assert(qf->blocks != NULL); + if (qf->metadata->mem) { + free(qf->mem); + free(qf->metadata); + free(qf->blocks); + } else { + msync(qf->metadata, qf->metadata->size + sizeof(qfmetadata),MS_SYNC); + munmap(qf->metadata, qf->metadata->size + sizeof(qfmetadata)); + close(qf->mem->fd); + } + //qf->metadata->noccupied_slots=0; +} + +void qf_close(QF *qf) +{ + assert(qf->blocks != NULL); + munmap(qf->metadata, qf->metadata->size + sizeof(qfmetadata)); + close(qf->mem->fd); +} + +/* + * Will read the on-disk QF using mmap. + * Data won't be copied in memory. + * + */ +void qf_read(QF *qf, const char *path) +{ + struct stat sb; + int ret; + + qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); + qf->mem->fd = open(path, O_RDWR, S_IRWXU); + if (qf->mem->fd < 0) { + perror("Couldn't open file:\n"); + exit(EXIT_FAILURE); + } + + ret = fstat (qf->mem->fd, &sb); + if ( ret < 0) { + perror ("fstat"); + exit(EXIT_FAILURE); + } + + if (!S_ISREG (sb.st_mode)) { + fprintf (stderr, "%s is not a file\n", path); + exit(EXIT_FAILURE); + } + + qf->metadata = (qfmetadata *)mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, + qf->mem->fd, 0); + qf->metadata->mem=false; + qf->blocks = (qfblock *)(qf->metadata + 1); + qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; + qf->mem->metadata_lock = 0; + qf->mem->locks = (volatile int *)calloc(qf->metadata->num_locks, + sizeof(volatile int)); +} + +void qf_reset(QF *qf) +{ + assert(popcnt(nslots) == 1); /* nslots must be a power of 2 */ + + qf->metadata->nelts = 0; + qf->metadata->ndistinct_elts = 0; + qf->metadata->noccupied_slots = 0; + +#ifdef LOG_WAIT_TIME + memset(qf->wait_times, 0, (qf->metadata->num_locks+1)*sizeof(wait_time_data)); +#endif +#if BITS_PER_SLOT == 8 || BITS_PER_SLOT == 16 || BITS_PER_SLOT == 32 || BITS_PER_SLOT == 64 + memset(qf->blocks, 0, qf->metadata->nblocks* sizeof(qfblock)); +#else + memset(qf->blocks, 0, qf->metadata->nblocks*(sizeof(qfblock) + SLOTS_PER_BLOCK * + qf->metadata->bits_per_slot / 8)); +#endif +} + +void qf_serialize(const QF *qf, const char *filename) +{ + FILE *fout; + fout = fopen(filename, "wb+"); + if (fout == NULL) { + perror("Error opening file for serializing\n"); + exit(EXIT_FAILURE); + } + + fwrite(qf->metadata, sizeof(qfmetadata), 1, fout); + + /* we don't serialize the locks */ + fwrite(qf->blocks, qf->metadata->size, 1, fout); + + fclose(fout); +} + +void qf_deserialize(QF *qf, const char *filename) +{ + FILE *fin; + fin = fopen(filename, "rb"); + if (fin == NULL) { + perror("Error opening file for deserializing\n"); + exit(EXIT_FAILURE); + } + + qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); + qf->metadata = (qfmetadata *)calloc(sizeof(qfmetadata), 1); + + fread(qf->metadata, sizeof(qfmetadata), 1, fin); + + /* initlialize the locks in the QF */ + qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; + qf->mem->metadata_lock = 0; + /* initialize all the locks to 0 */ + qf->mem->locks = (volatile int *)calloc(qf->metadata->num_locks, sizeof(volatile int)); + + qf->blocks = (qfblock *)calloc(qf->metadata->size, 1); + fread(qf->blocks, qf->metadata->size, 1, fin); + + fclose(fin); +} +uint64_t qf_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock, bool spin) +{ + __uint128_t hash = key; + uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); + int64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; + + + if (!is_occupied(qf, hash_bucket_index)){ + return 0; + } + + int64_t runstart_index = hash_bucket_index == 0 ? 0 : run_end(qf, + hash_bucket_index-1) + + 1; + + if (runstart_index < hash_bucket_index) + runstart_index = hash_bucket_index; + + /* printf("MC RUNSTART: %02lx RUNEND: %02lx\n", runstart_index, runend_index); */ + + uint64_t current_remainder, current_count, current_end; + do { + current_end = decode_counter(qf, runstart_index, ¤t_remainder, + ¤t_count); + if (current_remainder == hash_remainder){ + if (lock) { + if (!qf_lock(qf, runstart_index, spin, false)) + return 0; + } + + set_tag(qf,runstart_index,tag); + if (lock) { + qf_unlock(qf, runstart_index, true); + } + + + return 1; + } + runstart_index = current_end + 1; + } while (!is_runend(qf, current_end)); + + + return 0; +} + +uint64_t qf_remove_tag(const QF *qf, uint64_t key ,bool lock, bool spin) +{ + __uint128_t hash = key; + uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); + int64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; + if(hash_bucket_index > qf->metadata->xnslots){ + throw std::out_of_range("qf_remove_tag is called with hash index out of range"); + } + + if (!is_occupied(qf, hash_bucket_index)){ + return 0; + } + int64_t runstart_index = hash_bucket_index == 0 ? 0 : run_end(qf, + hash_bucket_index-1) + + 1; + if (runstart_index < hash_bucket_index) + runstart_index = hash_bucket_index; + + /* printf("MC RUNSTART: %02lx RUNEND: %02lx\n", runstart_index, runend_index); */ + + uint64_t current_remainder, current_count, current_end; + do { + current_end = decode_counter(qf, runstart_index, ¤t_remainder, + ¤t_count); + if (current_remainder == hash_remainder){ + if (lock) { + if (!qf_lock(qf, runstart_index, spin, false)) + return false; + } + set_tag(qf,runstart_index,0); + if (lock) + qf_unlock(qf, runstart_index, true); + return 1; + } + runstart_index = current_end + 1; + } while (!is_runend(qf, current_end)); + + + return 0; +} + +uint64_t qf_get_tag(const QF *qf, uint64_t key) +{ + __uint128_t hash = key; + uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); + int64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; + if(hash_bucket_index > qf->metadata->xnslots){ + throw std::out_of_range("qf_get_tag is called with hash index out of range"); + } + if (!is_occupied(qf, hash_bucket_index)) + return 0; + + int64_t runstart_index = hash_bucket_index == 0 ? 0 : run_end(qf, + hash_bucket_index-1) + + 1; + if (runstart_index < hash_bucket_index) + runstart_index = hash_bucket_index; + + /* printf("MC RUNSTART: %02lx RUNEND: %02lx\n", runstart_index, runend_index); */ + + uint64_t current_remainder, current_count, current_end; + do { + current_end = decode_counter(qf, runstart_index, ¤t_remainder, + ¤t_count); + if (current_remainder == hash_remainder){ + return get_tag(qf,runstart_index); + } + runstart_index = current_end + 1; + } while (!is_runend(qf, current_end)); + + return 0; +} + + +bool qf_insert(QF *qf, uint64_t key, uint64_t count, bool + lock, bool spin) +{ + if(count==0) + { + return true; + } + /*uint64_t hash = (key << qf->metadata->tag_bits) | (value & BITMASK(qf->metadata->tag_bits));*/ + if (0 && count == 1) + return insert(qf, key,count, lock, spin); + else + return insert(qf, key, count, lock, spin); +} + +uint64_t qf_count_key(const QF *qf, uint64_t key) +{ + __uint128_t hash = key; + uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); + int64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; + + if (!is_occupied(qf, hash_bucket_index)) + return 0; + + int64_t runstart_index = hash_bucket_index == 0 ? 0 : run_end(qf, + hash_bucket_index-1) + + 1; + if (runstart_index < hash_bucket_index) + runstart_index = hash_bucket_index; + + /* printf("MC RUNSTART: %02lx RUNEND: %02lx\n", runstart_index, runend_index); */ + + uint64_t current_remainder, current_count, current_end; + do { + current_end = decode_counter(qf, runstart_index, ¤t_remainder, + ¤t_count); + if (current_remainder == hash_remainder){ + return current_count; + } + + runstart_index = current_end + 1; + } while (!is_runend(qf, current_end)); + return 0; +} + + + + +/* initialize the iterator at the run corresponding + * to the position index + */ +bool qf_iterator(QF *qf, QFi *qfi, uint64_t position) +{ + if(position > qf->metadata->xnslots){ + throw std::out_of_range("qf_iterator is called with position out of range"); + } + if (!is_occupied(qf, position)) { + uint64_t block_index = position; + uint64_t idx = bitselect(get_block(qf, block_index)->occupieds[0], 0); + if (idx == 64) { + while(idx == 64 && block_index < qf->metadata->nblocks) { + block_index++; + idx = bitselect(get_block(qf, block_index)->occupieds[0], 0); + } + } + position = block_index * SLOTS_PER_BLOCK + idx; + } + + qfi->qf = qf; + qfi->num_clusters = 0; + qfi->run = position; + qfi->current = position == 0 ? 0 : run_end(qfi->qf, position-1) + 1; + if (qfi->current < position) + qfi->current = position; + +#ifdef LOG_CLUSTER_LENGTH + qfi->c_info = (cluster_data* )calloc(qf->metadata->nslots/32, sizeof(cluster_data)); + qfi->cur_start_index = position; + qfi->cur_length = 1; +#endif + + if (qfi->current >= qf->metadata->nslots) + return false; + return true; +} + +int qfi_get(QFi *qfi, uint64_t *key, uint64_t *value, uint64_t *count) +{ + + if(qfi->current > qfi->qf->metadata->xnslots){ + throw std::out_of_range("qfi_get is called with hash index out of range"); + } + uint64_t current_remainder, current_count; + decode_counter(qfi->qf, qfi->current, ¤t_remainder, ¤t_count); + *key = (qfi->run << qfi->qf->metadata->key_remainder_bits) | current_remainder; + *value = get_tag(qfi->qf,qfi->current); // for now we are not using value + *count = current_count; + + qfi->qf->metadata->ndistinct_elts++; + qfi->qf->metadata->nelts += current_count; + + /*qfi->current = end_index;*/ //get should not change the current index + //of the iterator + return 0; +} + +int qfi_next(QFi *qfi) +{ + if (qfi_end(qfi)) + return 1; + else { + /* move to the end of the current counter*/ + uint64_t current_remainder, current_count; + qfi->current = decode_counter(qfi->qf, qfi->current, ¤t_remainder, + ¤t_count); + + if (!is_runend(qfi->qf, qfi->current)) { + qfi->current++; +#ifdef LOG_CLUSTER_LENGTH + qfi->cur_length++; +#endif + if (qfi->current > qfi->qf->metadata->xnslots) + return 1; + return 0; + } + else { +#ifdef LOG_CLUSTER_LENGTH + /* save to check if the new current is the new cluster. */ + uint64_t old_current = qfi->current; +#endif + uint64_t block_index = qfi->run / SLOTS_PER_BLOCK; + uint64_t rank = bitrank(get_block(qfi->qf, block_index)->occupieds[0], + qfi->run % SLOTS_PER_BLOCK); + uint64_t next_run = bitselect(get_block(qfi->qf, + block_index)->occupieds[0], + rank); + if (next_run == 64) { + rank = 0; + while (next_run == 64 && block_index < qfi->qf->metadata->nblocks) { + block_index++; + next_run = bitselect(get_block(qfi->qf, block_index)->occupieds[0], + rank); + } + } + if (block_index == qfi->qf->metadata->nblocks) { + /* set the index values to max. */ + qfi->run = qfi->current = qfi->qf->metadata->xnslots; + return 1; + } + qfi->run = block_index * SLOTS_PER_BLOCK + next_run; + qfi->current++; + if (qfi->current < qfi->run) + qfi->current = qfi->run; +#ifdef LOG_CLUSTER_LENGTH + if (qfi->current > old_current + 1) { /* new cluster. */ + if (qfi->cur_length > 10) { + qfi->c_info[qfi->num_clusters].start_index = qfi->cur_start_index; + qfi->c_info[qfi->num_clusters].length = qfi->cur_length; + qfi->num_clusters++; + } + qfi->cur_start_index = qfi->run; + qfi->cur_length = 1; + } else { + qfi->cur_length++; + } +#endif + return 0; + } + } +} + +inline int qfi_end(QFi *qfi) +{ + if (qfi->current >= qfi->qf->metadata->xnslots /*&& is_runend(qfi->qf, qfi->current)*/) + return 1; + else + return 0; +} + +/* + * Merge qfa and qfb into qfc + */ +/* + * iterate over both qf (qfa and qfb) + * simultaneously + * for each index i + * min(get_value(qfa, ia) < get_value(qfb, ib)) + * insert(min, ic) + * increment either ia or ib, whichever is minimum. + */ +void qf_merge(QF *qfa, QF *qfb, QF *qfc) +{ + QFi qfia, qfib; + if(qfa->metadata->range != qfb->metadata->range || + qfb->metadata->range != qfc->metadata->range ) + { + throw std::logic_error("Merging non compatible filters"); + } + qf_iterator(qfa, &qfia, 0); + qf_iterator(qfb, &qfib, 0); + + uint64_t keya, valuea, counta, keyb, valueb, countb; + qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfib, &keyb, &valueb, &countb); + do { + if (keya < keyb) { + qf_insert(qfc, keya, counta, true, true); + qfi_next(&qfia); + qfi_get(&qfia, &keya, &valuea, &counta); + } + else { + qf_insert(qfc, keyb, countb, true, true); + qfi_next(&qfib); + qfi_get(&qfib, &keyb, &valueb, &countb); + } + } while(!qfi_end(&qfia) && !qfi_end(&qfib)); + + if (!qfi_end(&qfia)) { + do { + qfi_get(&qfia, &keya, &valuea, &counta); + qf_insert(qfc, keya, counta, true, true); + } while(!qfi_next(&qfia)); + } + if (!qfi_end(&qfib)) { + do { + qfi_get(&qfib, &keyb, &valueb, &countb); + qf_insert(qfc, keyb, countb, true, true); + } while(!qfi_next(&qfib)); + } + + return; +} + +bool qf_equals(QF *qfa, QF *qfb) +{ + QFi qfia, qfib; + if(qfa->metadata->range != qfb->metadata->range ) + { + throw std::logic_error("comparing non compatible filters"); + } + qf_iterator(qfa, &qfia, 0); + qf_iterator(qfb, &qfib, 0); + + uint64_t keya, valuea, counta, keyb, valueb, countb; + qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfib, &keyb, &valueb, &countb); + do { + if (keya != keyb) { + return false; + } + else { + if(counta!=countb || valuea != valueb) + { + return false; + } + qfi_next(&qfib); + qfi_next(&qfia); + qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfib, &keyb, &valueb, &countb); + } + } while(!qfi_end(&qfia) && !qfi_end(&qfib)); + + if (!qfi_end(&qfia) || !qfi_end(&qfib)) { + return false; + } + + return true; +} + +void qf_intersect(QF *qfa, QF *qfb, QF *qfc) +{ + QFi qfia, qfib; + if(qfa->metadata->range != qfb->metadata->range || + qfb->metadata->range != qfc->metadata->range ) + { + throw std::logic_error("Calculate intersect for non compatible filters"); + } + qf_iterator(qfa, &qfia, 0); + qf_iterator(qfb, &qfib, 0); + + uint64_t keya, valuea, counta, keyb, valueb, countb; + qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfib, &keyb, &valueb, &countb); + do { + if (keya < keyb) { + qfi_next(&qfia); + qfi_get(&qfia, &keya, &valuea, &counta); + } + else if(keyb < keya) { + qfi_next(&qfib); + qfi_get(&qfib, &keyb, &valueb, &countb); + } + else{ + qf_insert(qfc, keya, std::min(counta,countb), true, true); + qfi_next(&qfia); + qfi_next(&qfib); + qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfib, &keyb, &valueb, &countb); + } + } while(!qfi_end(&qfia) && !qfi_end(&qfib)); + + + + return; +} +void qf_subtract(QF *qfa, QF *qfb, QF *qfc) +{ + QFi qfia, qfib; + if(qfa->metadata->range != qfb->metadata->range || + qfb->metadata->range != qfc->metadata->range ) + { + throw std::logic_error("Calculate subtracte for non compatible filters"); + } + qf_iterator(qfa, &qfia, 0); + qf_iterator(qfb, &qfib, 0); + + uint64_t keya, valuea, counta, keyb, valueb, countb; + qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfib, &keyb, &valueb, &countb); + do { + if (keya < keyb) { + qfi_next(&qfia); + qfi_get(&qfia, &keya, &valuea, &counta); + } + else if(keyb < keya) { + qfi_next(&qfib); + qfi_get(&qfib, &keyb, &valueb, &countb); + } + else{ + qf_insert(qfc, keya, counta>countb? counta-countb : 0, true, true); + qfi_next(&qfia); + qfi_next(&qfib); + qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfib, &keyb, &valueb, &countb); + } + } while(!qfi_end(&qfia) && !qfi_end(&qfib)); + + return; +} + + + +/* + * Merge an array of qfs into the resultant QF + */ +void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr) +{ + int i; + QFi qfi_arr[nqf]; + int flag = 0; + int smallest_i = 0; + uint64_t smallest_key = UINT64_MAX; + + uint64_t range=qf_arr[0]->metadata->range; + for (i=1; imetadata->range!=range) + { + throw std::logic_error("Merging non compatible filters"); + } + } + + for (i=0; imetadata->key_bits-newQ <2) + { + throw std::logic_error("Resize cannot be done. Slot size cannot be less than 2"); + } + + if(originalFilename) + { + qf_serialize(qf,originalFilename); + qf_destroy(qf); + qf_read(qf,originalFilename); + } + QF* newQF=(QF *)calloc(sizeof(QF), 1); + if(newFilename) + { + qf_init(newQF, (1ULL<metadata->key_bits, qf->metadata->tag_bits,qf->metadata->fixed_counter_size, false, newFilename, 2038074761); + } + else{ + qf_init(newQF, (1ULL<metadata->key_bits, qf->metadata->tag_bits,qf->metadata->fixed_counter_size, true, "" , 2038074761); + } + QFi qfi; + qf_iterator(qf, &qfi, 0); + + + uint64_t keya, valuea, counta; + qfi_get(&qfi, &keya, &valuea, &counta); + + do { + qf_insert(newQF, keya, counta); + qf_add_tag(newQF,keya,valuea); + qfi_next(&qfi); + qfi_get(&qfi, &keya, &valuea, &counta); + } while(!qfi_end(&qfi)); + qf_destroy(qf); + return newQF; + + +} + +/* find cosine similarity between two QFs. */ +uint64_t qf_inner_product(QF *qfa, QF *qfb) +{ + uint64_t acc = 0; + QFi qfi; + QF *qf_mem, *qf_disk; + + // create the iterator on the larger QF. + if (qfa->metadata->size > qfb->metadata->size) { + qf_mem = qfb; + qf_disk = qfa; + } else { + qf_mem = qfa; + qf_disk = qfb; + } + + qf_iterator(qf_disk, &qfi, 0); + do { + uint64_t key = 0, value = 0, count = 0; + uint64_t count_mem; + qfi_get(&qfi, &key, &value, &count); + if ((count_mem = qf_count_key(qf_mem, key)) > 0) { + acc += count*count_mem; + } + } while (!qfi_next(&qfi)); + + return acc; +} + +/* find cosine similarity between two QFs. */ +// void qf_intersect(QF *qfa, QF *qfb, QF *qfr) +// { +// QFi qfi; +// QF *qf_mem, *qf_disk; +// +// // create the iterator on the larger QF. +// if (qfa->metadata->size > qfb->metadata->size) { +// qf_mem = qfb; +// qf_disk = qfa; +// } else { +// qf_mem = qfa; +// qf_disk = qfb; +// } +// +// qf_iterator(qf_disk, &qfi, 0); +// do { +// uint64_t key = 0, value = 0, count = 0; +// qfi_get(&qfi, &key, &value, &count); +// if (qf_count_key(qf_mem, key) > 0) +// qf_insert(qfr, key, count, false, false); +// } while (!qfi_next(&qfi)); +// } + +/* magnitude of a QF. */ +uint64_t qf_magnitude(QF *qf) +{ + return sqrt(qf_inner_product(qf, qf)); +} + + +int qf_space(QF *qf) +{ + return (int)(((double)qf->metadata->noccupied_slots/ + (double)qf->metadata->xnslots + )* 100.0); +} + +#ifdef TEST + #include "tests/lowLevelTests.hpp" +#endif diff --git a/third-party/mqf/gqf.h b/third-party/mqf/gqf.h new file mode 100644 index 0000000000..6361381f75 --- /dev/null +++ b/third-party/mqf/gqf.h @@ -0,0 +1,257 @@ +#ifndef QF_H +#define QF_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Can be + 0 (choose size at run-time), + 8, 16, 32, or 64 (for optimized versions), + or other integer <= 56 (for compile-time-optimized bit-shifting-based versions) + */ +#define BITS_PER_SLOT 0 + + struct __attribute__ ((__packed__)) qfblock; + typedef struct qfblock qfblock; + + uint64_t shift_into_b2(uint64_t a, uint64_t b, int bstart, int bend, int amount); + + typedef struct { + uint64_t total_time_single; + uint64_t total_time_spinning; + uint64_t locks_taken; + uint64_t locks_acquired_single_attempt; + } wait_time_data; + + typedef struct quotient_filter_mem { + int fd; + volatile int metadata_lock; + volatile int *locks; + wait_time_data *wait_times; + } quotient_filter_mem; + + typedef quotient_filter_mem qfmem; + + typedef struct quotient_filter_metadata { + uint64_t size; + uint32_t seed; + uint64_t nslots; + uint64_t xnslots; + uint64_t key_bits; + uint64_t tag_bits; + uint64_t fixed_counter_size; + uint64_t key_remainder_bits; + uint64_t bits_per_slot; + __uint128_t range; + uint64_t nblocks; + uint64_t nelts; + uint64_t ndistinct_elts; + uint64_t noccupied_slots; + uint64_t maximum_occupied_slots; + uint64_t num_locks; + bool mem; + } quotient_filter_metadata; + + typedef quotient_filter_metadata qfmetadata; + + typedef struct quotient_filter { + qfmem *mem; + qfmetadata *metadata; + qfblock *blocks; + } quotient_filter; + + typedef quotient_filter QF; + + typedef struct { + uint64_t start_index; + uint16_t length; + } cluster_data; + + typedef struct quotient_filter_iterator { + QF *qf; + uint64_t run; + uint64_t current; + uint64_t cur_start_index; + uint16_t cur_length; + uint32_t num_clusters; + cluster_data *c_info; + } quotient_filter_iterator; + + typedef quotient_filter_iterator QFi; + + /*! + @breif initialize mqf . + + @param Qf* qf : pointer to the Filter. + @param uint64_t nslots : Number of slots in the filter. Maximum number of items to be inserted depends on this number. + @param uint64_t key_bits: Number of bits in the hash values. This number should equal log2(nslots) +r. Accuracy depends on r. + @param uint64_t tag_bits: Number of bits in tag value. + @param uint64_t fixed_counter_size: Fixed counter size. must be > 0. + @param bool mem: Flag to create the filter on memeory. IF false, mmap is used. + @param const char * path: In case of mmap. Path of the file used to pack the filter. + @param uint32_t seed: useless value. To be removed + */ + void qf_init(QF *qf, uint64_t nslots, uint64_t key_bits, uint64_t tag_bits,uint64_t fixed_counter_size, bool mem, const char *path, uint32_t seed); + + void qf_reset(QF *qf); + + void qf_destroy(QF *qf); + + void qf_copy(QF *dest, QF *src); + + /*! + @breif Increment the counter for this item by count. + + @param Qf* qf : pointer to the Filter + @param uint64_t key : hash of the item to be insertedItems + @param uint64_t count: Count to be added + @param bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + @param bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + + @return bool: True if the item is inserted correctly. + */ + bool qf_insert(QF *qf, uint64_t key, uint64_t count, + bool lock=false, bool spin=false); + + + /* Remove all instances of this key/value pair. */ + //void qf_delete_key_value(QF *qf, uint64_t key, uint64_t value); + + /* Remove all instances of this key. */ + //void qf_delete_key(QF *qf, uint64_t key); + + /* Replace the association (key, oldvalue, count) with the association + (key, newvalue, count). If there is already an association (key, + newvalue, count'), then the two associations will be merged and + their counters will be summed, resulting in association (key, + newvalue, count' + count). */ + void qf_replace(QF *qf, uint64_t key, uint64_t oldvalue, uint64_t newvalue); + + /* Lookup the value associated with key. Returns the count of that + key/value pair in the QF. If it returns 0, then, the key is not + present in the QF. Only returns the first value associated with key + in the QF. If you want to see others, use an iterator. */ + uint64_t qf_query(const QF *qf, uint64_t key, uint64_t *value); + + /*! + @breif Return the number of times key has been inserted, with any value, + into qf. + + @param Qf* qf : pointer to the Filter. + @param uint64_t key : hash of the item. + + @return uint64_t the count associated with the input key. + */ + uint64_t qf_count_key(const QF *qf, uint64_t key); + + /*! + @breif Decrement the counter for this item by count. + + @param Qf* qf : pointer to the Filter + @param uint64_t key : hash of the item to be removed + @param uint64_t count: Count to be removed + @param bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + @param bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + + @return bool: Returns true if the item is removed successfully. + */ + bool qf_remove(QF *qf, uint64_t hash, uint64_t count, bool lock=false, bool spin=false); + + + /*! + @breif Add Tag to item. + + @param Qf* qf : pointer to the Filter + @param uint64_t key : hash of the item to be insertedItems + @param uint64_t tag: tag to be added + @param bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + @param bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + + @return bool: True if the item is inserted correctly. + */ + uint64_t qf_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock=false, bool spin=false); + /*! + @breif Return the tag associated with a given item. + + @param Qf* qf : pointer to the Filter. + @param uint64_t key : hash of the item. + + @return uint64_t the tag associated with the input key. + */ + uint64_t qf_get_tag(const QF *qf, uint64_t key); + /*! + @breif delete the tag associated with a given item. + + @param Qf* qf : pointer to the Filter. + @param uint64_t key : hash of the item. + + @return bool: Returns true if the item is removed successfully. + */ + uint64_t qf_remove_tag(const QF *qf, uint64_t key, bool lock=false, bool spin=false); + + /* Initialize an iterator */ + bool qf_iterator(QF *qf, QFi *qfi, uint64_t position); + + /* Returns 0 if the iterator is still valid (i.e. has not reached the + end of the QF. */ + int qfi_get(QFi *qfi, uint64_t *key, uint64_t *value, uint64_t *count); + + /* Advance to next entry. Returns whether or not another entry is + found. */ + int qfi_next(QFi *qfi); + + /* Check to see if the if the end of the QF */ + int qfi_end(QFi *qfi); + + /* For debugging */ + void qf_dump(const QF *); + + /*! write data structure of to the disk */ + void qf_serialize(const QF *qf, const char *filename); + + /* read data structure off the disk */ + void qf_deserialize(QF *qf, const char *filename); + + /* mmap the QF from disk. */ + void qf_read(QF *qf, const char *path); + + /* merge two QFs into the third one. */ + void qf_merge(QF *qfa, QF *qfb, QF *qfc); + + void qf_intersect(QF *qfa, QF *qfb, QF *qfc); + + void qf_subtract(QF *qfa, QF *qfb, QF *qfc); + /* merge multiple QFs into the final QF one. */ + void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr); + + /*! @breif Resize the filter into a bigger or smaller one + + @param Qf* qf : pointer to the Filter + @param uint64_t newQ: new number of slots(Q). the slot size will be recalculated to keep the range constant. + @param string originalFilename(optional): dump the current filter to the disk to free space for the new filter. Filename is provided as the content of the string. + @param string newFilename(optional): the new filter is created on disk. Filename is provided as the content of the string. + + @return QF: New Quotient Filter. + */ + QF* qf_resize(QF* qf, int newQ, const char * originalFilename=NULL, const char * newFilename=NULL); + /* find cosine similarity between two QFs. */ + uint64_t qf_inner_product(QF *qfa, QF *qfb); + + /* magnitude of a QF. */ + uint64_t qf_magnitude(QF *qf); + /* return the filled space(percent) */ + int qf_space(QF *qf); + + bool qf_equals(QF *qfa, QF *qfb); + + +#ifdef __cplusplus +} +#endif + +#endif /* QF_H */ From 6acad74f78129f4cb3458e14064da76528a8ab34 Mon Sep 17 00:00:00 2001 From: Shokrof Date: Mon, 18 Jun 2018 23:50:29 +0200 Subject: [PATCH 02/11] fix building on travis on macosx --- .travis.yml | 2 ++ src/oxli/Makefile | 2 ++ third-party/mqf/Makefile | 12 ++++++------ third-party/mqf/{gqf.c => gqf.cc} | 0 4 files changed, 10 insertions(+), 6 deletions(-) rename third-party/mqf/{gqf.c => gqf.cc} (100%) diff --git a/.travis.yml b/.travis.yml index 693761b7f3..c563b72040 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,6 +70,8 @@ matrix: - TESTATTR="'not linux and not known_failing and not huge'" before_install: - source ci_scripts/install.sh + - CXX="clang++ -stdlib=libc++" + - CXXFLAGS="$CXXFLAGS -nostdinc+" # command to install common dependencies install: diff --git a/src/oxli/Makefile b/src/oxli/Makefile index 9ce30e156f..0b0c60acf7 100644 --- a/src/oxli/Makefile +++ b/src/oxli/Makefile @@ -164,6 +164,8 @@ SONAME = liboxli.$(SHARED_EXT).$(LIB_VERSION) SONAME_FLAGS = -install_name $(PREFIX)/lib/$(SONAME) \ -compatibility_version $(LIB_VERSION) \ -current_version $(LIB_VERSION) +LDFLAGS += -undefined dynamic_lookup -arch x86_64 + else SHARED_EXT = so SONAME = liboxli.$(SHARED_EXT).$(LIB_VERSION) diff --git a/third-party/mqf/Makefile b/third-party/mqf/Makefile index 1dbc08bebd..7710d43308 100644 --- a/third-party/mqf/Makefile +++ b/third-party/mqf/Makefile @@ -23,13 +23,13 @@ ifdef P PROFILE=-pg -no-pie # for bug in gprof. endif -CXX = g++ -std=c++11 -fPIC -CC = g++ -std=c++11 -fPIC -LD= g++ -std=c++11 +#CXX = g++ -std=c++11 -fPIC +#CC = g++ -std=c++11 -fPIC +#LD= g++ -std=c++11 -CXXFLAGS = -Wall $(DEBUG) $(PROFILE) $(OPT) -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function +CXXFLAGS := -std=c++11 -fPIC -Wall $(DEBUG) $(PROFILE) $(OPT) -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function -LDFLAGS = $(DEBUG) $(PROFILE) $(OPT) +LDFLAGS := $(LDFLAGS) $(DEBUG) $(PROFILE) $(OPT) # # declaration of dependencies @@ -44,7 +44,7 @@ all: $(TARGETS) # dependencies between .o files and .cc (or .c) files %.o: %.cc -gqf.o: gqf.c gqf.h +gqf.o: gqf.cc gqf.h # # generic build rules diff --git a/third-party/mqf/gqf.c b/third-party/mqf/gqf.cc similarity index 100% rename from third-party/mqf/gqf.c rename to third-party/mqf/gqf.cc From 3213cb94427608dbbbdbc80fae9fd264455b3575 Mon Sep 17 00:00:00 2001 From: Shokrof Date: Tue, 19 Jun 2018 00:06:36 +0200 Subject: [PATCH 03/11] add -lstdc++ --- examples/c++-api/Makefile | 2 +- src/oxli/Makefile | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/c++-api/Makefile b/examples/c++-api/Makefile index 576a497c92..139ef8d2a7 100644 --- a/examples/c++-api/Makefile +++ b/examples/c++-api/Makefile @@ -8,7 +8,7 @@ CXXFLAGS=--std=c++11 \ TESTS=exact-counting bloom consume %: %.cc ../../src/oxli/liboxli.a - $(CXX) $(CXXFLAGS) $< ../../src/oxli/liboxli.a -o $@ + $(CXX) $(CXXFLAGS) $< ../../src/oxli/liboxli.a -o $@ -lstdc++ ../../src/oxli/liboxli.a: cd ../../src/oxli && make diff --git a/src/oxli/Makefile b/src/oxli/Makefile index 0b0c60acf7..97a5021fc4 100644 --- a/src/oxli/Makefile +++ b/src/oxli/Makefile @@ -164,7 +164,7 @@ SONAME = liboxli.$(SHARED_EXT).$(LIB_VERSION) SONAME_FLAGS = -install_name $(PREFIX)/lib/$(SONAME) \ -compatibility_version $(LIB_VERSION) \ -current_version $(LIB_VERSION) -LDFLAGS += -undefined dynamic_lookup -arch x86_64 +LDFLAGS += -undefined dynamic_lookup -arch x86_64 else SHARED_EXT = so @@ -328,7 +328,7 @@ murmur3.o: ../../third-party/smhasher/MurmurHash3.cc $(CXX) $(CXXFLAGS) $(LDFLAGS) -c -o $@ $< $(LIBOXLISO): $(LIBOXLI_OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(SONAME_FLAGS) -shared -o $@ $^ + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(SONAME_FLAGS) -shared -o $@ $^ -lstdc++ ln -sf $(SONAME) liboxli.$(SHARED_EXT) liboxli.a: $(LIBOXLI_OBJS) From f1141e609988bcb49e886e1a92740e681fef7a1f Mon Sep 17 00:00:00 2001 From: Shokrof Date: Tue, 19 Jun 2018 00:12:20 +0200 Subject: [PATCH 04/11] bug fix --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c563b72040..45720c9cd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -68,10 +68,11 @@ matrix: osx_image: xcode7.3 env: - TESTATTR="'not linux and not known_failing and not huge'" + - CXXFLAGS="$CXXFLAGS -nostdinc+" + - CXX="clang++ -stdlib=libc++" before_install: - source ci_scripts/install.sh - - CXX="clang++ -stdlib=libc++" - - CXXFLAGS="$CXXFLAGS -nostdinc+" + # command to install common dependencies install: From 887b807e9e04cadcc8b776faad297a3df8716fdb Mon Sep 17 00:00:00 2001 From: Shokrof Date: Tue, 19 Jun 2018 01:49:48 +0200 Subject: [PATCH 05/11] upgrading MQF --- setup.py | 1 + src/oxli/Makefile | 4 +- third-party/mqf/Makefile | 55 +++- third-party/mqf/gqf.cc | 631 +++++++++++++++++++++++++++++--------- third-party/mqf/gqf.h | 21 ++ third-party/mqf/utils.cpp | 58 ++++ third-party/mqf/utils.h | 22 ++ 7 files changed, 628 insertions(+), 164 deletions(-) create mode 100644 third-party/mqf/utils.cpp create mode 100644 third-party/mqf/utils.h diff --git a/setup.py b/setup.py index 64be4a7304..9735d9daaa 100755 --- a/setup.py +++ b/setup.py @@ -334,6 +334,7 @@ def run(self): spawn(cmd=mqfcmd, dry_run=self.dry_run) for ext in self.extensions: ext.extra_objects.append(path_join("third-party", "mqf", "gqf.o")) + ext.extra_objects.append(path_join("third-party", "mqf", "utils.o")) if "z" not in self.libraries: zcmd = ['bash', '-c', 'cd ' + ZLIBDIR + ' && ( test Makefile -nt' diff --git a/src/oxli/Makefile b/src/oxli/Makefile index 97a5021fc4..610132b687 100644 --- a/src/oxli/Makefile +++ b/src/oxli/Makefile @@ -224,7 +224,7 @@ BZIP2_OBJS=$(addprefix $(BZIP2_DIR)/, $(BZIP2_OBJS_BASE)) # Counting bloom filter MQF_DIR=../../third-party/mqf -MQF_OBJS_BASE= gqf.o +MQF_OBJS_BASE= gqf.o utils.o MQF_OBJS=$(addprefix $(MQF_DIR)/, $(MQF_OBJS_BASE)) @@ -328,7 +328,7 @@ murmur3.o: ../../third-party/smhasher/MurmurHash3.cc $(CXX) $(CXXFLAGS) $(LDFLAGS) -c -o $@ $< $(LIBOXLISO): $(LIBOXLI_OBJS) - $(CXX) $(CXXFLAGS) $(LDFLAGS) $(SONAME_FLAGS) -shared -o $@ $^ -lstdc++ + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(SONAME_FLAGS) -shared -o $@ $^ -lstdc++ ln -sf $(SONAME) liboxli.$(SHARED_EXT) liboxli.a: $(LIBOXLI_OBJS) diff --git a/third-party/mqf/Makefile b/third-party/mqf/Makefile index 7710d43308..18dba32df2 100644 --- a/third-party/mqf/Makefile +++ b/third-party/mqf/Makefile @@ -1,4 +1,5 @@ -TARGETS=gqf.o +TARGETS=gqf.o utils.o +TESTFILES = tests/CountingTests.o tests/HighLevelFunctionsTests.o tests/IOTests.o tests/tagTests.o ifdef D DEBUG=-g @@ -8,28 +9,23 @@ else OPT=-Ofast endif -# using the SSE4.2 extensions doesn't seem to speed things up by a large -# amount, so disabling it for the time being till we have a good automated -# test for detecting whether or not the CPU supports it -OS := $(shell uname) -ifeq ($(OS), Darwin) +ifdef NH ARCH= else - #ARCH=-msse4.2 -D__SSE4_2_ - ARCH= + ARCH=-msse4.2 -D__SSE4_2_ endif ifdef P PROFILE=-pg -no-pie # for bug in gprof. endif -#CXX = g++ -std=c++11 -fPIC -#CC = g++ -std=c++11 -fPIC -#LD= g++ -std=c++11 +CXX = g++ -std=c++11 +CC = g++ -std=c++11 +LD= g++ -std=c++11 -CXXFLAGS := -std=c++11 -fPIC -Wall $(DEBUG) $(PROFILE) $(OPT) -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function +CXXFLAGS = -fPIC -Wall $(DEBUG) $(PROFILE) $(OPT) $(ARCH) -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function -LDFLAGS := $(LDFLAGS) $(DEBUG) $(PROFILE) $(OPT) +LDFLAGS = $(DEBUG) $(PROFILE) $(OPT) # # declaration of dependencies @@ -37,24 +33,53 @@ LDFLAGS := $(LDFLAGS) $(DEBUG) $(PROFILE) $(OPT) all: $(TARGETS) + + # dependencies between programs and .o files +main: main.o gqf.o utils.o + $(LD) $^ $(LDFLAGS) -o $@ +# dependencies between .o files and .h files + +libgqf.so: gqf.o utils.o + $(LD) $^ $(LDFLAGS) --shared -o $@ + +libgqf.a: gqf.o utils.o + ar rcs $@ $^ + ranlib $@ +test: $(TESTFILES) gqf.cc test.o utils.o + $(LD) $(LDFLAGS) -DTEST -o mqf_test test.o utils.o $(TESTFILES) gqf.c + +main.o: gqf.h + # dependencies between .o files and .cc (or .c) files -%.o: %.cc + gqf.o: gqf.cc gqf.h # # generic build rules # + + + + + + %.o: %.cc $(CXX) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ %.o: %.c $(CC) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ + + + + clean: - rm -f *.o $(TARGETS) + rm -f *.o $(TARGETS) $(TESTS) $(TESTFILES) libgqf.a libgqf.so diff --git a/third-party/mqf/gqf.cc b/third-party/mqf/gqf.cc index d45196723a..befe91ee19 100644 --- a/third-party/mqf/gqf.cc +++ b/third-party/mqf/gqf.cc @@ -18,6 +18,9 @@ #include #include "gqf.h" #include +#include +#include "utils.h" + /****************************************************************** * Code for managing the metadata bits and slots w/o interpreting * @@ -1602,6 +1605,9 @@ static inline uint64_t next_slot(QF *qf, uint64_t current) static inline bool insert(QF *qf, __uint128_t hash, uint64_t count, bool lock=false, bool spin=false) { + if(qf->metadata->maximum_count!=0){ + count=std::min(count,qf->metadata->maximum_count); + } uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); uint64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; uint64_t hash_bucket_block_offset = hash_bucket_index % SLOTS_PER_BLOCK; @@ -1611,6 +1617,8 @@ static inline bool insert(QF *qf, __uint128_t hash, uint64_t count, bool lock=fa throw std::out_of_range("Insert is called with hash index out of range"); } if (lock) { + if(qf->mem->general_lock) + return false; if (!qf_lock(qf, hash_bucket_index, spin, false)) return false; } @@ -1683,7 +1691,11 @@ static inline bool insert(QF *qf, __uint128_t hash, uint64_t count, bool lock=fa modify_metadata(qf, &qf->metadata->ndistinct_elts, 1); /* Found a counter for this remainder. Add in the new count. */ } else if (current_remainder == hash_remainder) { - uint64_t *p = encode_counter(qf, hash_remainder, current_count + count, &new_values[67],&new_fcounters[67]); + uint64_t tmp= current_count + count; + if(qf->metadata->maximum_count!=0){ + tmp=std::min(tmp,qf->metadata->maximum_count); + } + uint64_t *p = encode_counter(qf, hash_remainder, tmp, &new_values[67],&new_fcounters[67]); total_remainders=&new_values[67] - p; insert_replace_slots_and_shift_remainders_and_runends_and_offsets(qf, is_runend(qf, current_end) ? 1 : 2, @@ -1769,6 +1781,8 @@ static inline bool insert(QF *qf, __uint128_t hash, uint64_t count, bool lock=fa // p=&new_values[67]; // } if (lock) { + if(qf->mem->general_lock) + return false; if (!qf_lock(qf, hash_bucket_index, spin, false)) return false; } @@ -1859,7 +1873,8 @@ qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); qf->metadata->noccupied_slots = 0; qf->metadata->maximum_occupied_slots=(uint64_t)((double)qf->metadata->xnslots *0.95); qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; - + qf->metadata->maximum_count = 0; + qf->metadata->tags_map=NULL; qf->blocks = (qfblock *)calloc(size, 1); @@ -1912,12 +1927,14 @@ qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); qf->metadata->noccupied_slots = 0; qf->metadata->maximum_occupied_slots=(uint64_t)((double)qf->metadata->xnslots *0.95); qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; - + qf->metadata->maximum_count = 0; + qf->metadata->tags_map=NULL; qf->blocks = (qfblock *)(qf->metadata + 1); } /* initialize all the locks to 0 */ qf->mem->metadata_lock = 0; + qf->mem->general_lock = 0; qf->mem->locks = (volatile int *)calloc(qf->metadata->num_locks, sizeof(volatile int)); @@ -1935,6 +1952,11 @@ void qf_copy(QF *dest, QF *src) memcpy(dest->mem, src->mem, sizeof(qfmem)); memcpy(dest->metadata, src->metadata, sizeof(qfmetadata)); memcpy(dest->blocks, src->blocks, src->metadata->size); + + if(src->metadata->tags_map!=NULL){ + dest->metadata->tags_map= + new std::map >(*src->metadata->tags_map); + } } /* free up the memory if the QF is in memory. @@ -1945,6 +1967,10 @@ void qf_copy(QF *dest, QF *src) void qf_destroy(QF *qf) { assert(qf->blocks != NULL); + if(qf->metadata->tags_map!=NULL){ + delete qf->metadata->tags_map; + qf->metadata->tags_map=NULL; + } if (qf->metadata->mem) { free(qf->mem); free(qf->metadata); @@ -1954,6 +1980,7 @@ void qf_destroy(QF *qf) munmap(qf->metadata, qf->metadata->size + sizeof(qfmetadata)); close(qf->mem->fd); } + //qf->metadata->noccupied_slots=0; } @@ -1969,37 +1996,43 @@ void qf_close(QF *qf) * Data won't be copied in memory. * */ -void qf_read(QF *qf, const char *path) -{ - struct stat sb; - int ret; + void qf_read(QF *qf, const char *path) + { + struct stat sb; + int ret; - qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); - qf->mem->fd = open(path, O_RDWR, S_IRWXU); - if (qf->mem->fd < 0) { - perror("Couldn't open file:\n"); - exit(EXIT_FAILURE); - } + qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); + qf->mem->fd = open(path, O_RDWR, S_IRWXU); + if (qf->mem->fd < 0) { + perror("Couldn't open file:\n"); + exit(EXIT_FAILURE); + } - ret = fstat (qf->mem->fd, &sb); - if ( ret < 0) { - perror ("fstat"); - exit(EXIT_FAILURE); - } + ret = fstat (qf->mem->fd, &sb); + if ( ret < 0) { + perror ("fstat"); + exit(EXIT_FAILURE); + } - if (!S_ISREG (sb.st_mode)) { - fprintf (stderr, "%s is not a file\n", path); - exit(EXIT_FAILURE); + if (!S_ISREG (sb.st_mode)) { + fprintf (stderr, "%s is not a file\n", path); + exit(EXIT_FAILURE); + } + + qf->metadata = (qfmetadata *)mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, + qf->mem->fd, 0); + qf->metadata->mem=false; + qf->blocks = (qfblock *)(qf->metadata + 1); + qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; + qf->mem->metadata_lock = 0; + qf->mem->locks = (volatile int *)calloc(qf->metadata->num_locks, + sizeof(volatile int)); + + string tagsMapOutName=string(path)+".tags_map"; + if(file_exists(tagsMapOutName)){ + qf->metadata->tags_map=load_tags_map(tagsMapOutName.c_str()); } - qf->metadata = (qfmetadata *)mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, - qf->mem->fd, 0); - qf->metadata->mem=false; - qf->blocks = (qfblock *)(qf->metadata + 1); - qf->metadata->num_locks = (qf->metadata->xnslots/NUM_SLOTS_TO_LOCK)+2; - qf->mem->metadata_lock = 0; - qf->mem->locks = (volatile int *)calloc(qf->metadata->num_locks, - sizeof(volatile int)); } void qf_reset(QF *qf) @@ -2009,7 +2042,8 @@ void qf_reset(QF *qf) qf->metadata->nelts = 0; qf->metadata->ndistinct_elts = 0; qf->metadata->noccupied_slots = 0; - + if(qf->metadata->tags_map!=NULL) + qf->metadata->tags_map->clear(); #ifdef LOG_WAIT_TIME memset(qf->wait_times, 0, (qf->metadata->num_locks+1)*sizeof(wait_time_data)); #endif @@ -2029,15 +2063,20 @@ void qf_serialize(const QF *qf, const char *filename) perror("Error opening file for serializing\n"); exit(EXIT_FAILURE); } - fwrite(qf->metadata, sizeof(qfmetadata), 1, fout); - /* we don't serialize the locks */ fwrite(qf->blocks, qf->metadata->size, 1, fout); - fclose(fout); + + if(qf->metadata->tags_map!=NULL) + { + string tagsMapOutName=string(filename)+".tags_map"; + save_tags_map(qf->metadata->tags_map,tagsMapOutName.c_str()); + } } + + void qf_deserialize(QF *qf, const char *filename) { FILE *fin; @@ -2060,11 +2099,20 @@ void qf_deserialize(QF *qf, const char *filename) qf->blocks = (qfblock *)calloc(qf->metadata->size, 1); fread(qf->blocks, qf->metadata->size, 1, fin); - fclose(fin); + + string tagsMapOutName=string(filename)+".tags_map"; + if(file_exists(tagsMapOutName)){ + qf->metadata->tags_map=load_tags_map(tagsMapOutName.c_str()); + } + + } uint64_t qf_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock, bool spin) { + if(qf->metadata->tag_bits==0){ + return 0; + } __uint128_t hash = key; uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); int64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; @@ -2089,6 +2137,8 @@ uint64_t qf_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock, bool sp ¤t_count); if (current_remainder == hash_remainder){ if (lock) { + if(qf->mem->general_lock) + return false; if (!qf_lock(qf, runstart_index, spin, false)) return 0; } @@ -2110,6 +2160,11 @@ uint64_t qf_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock, bool sp uint64_t qf_remove_tag(const QF *qf, uint64_t key ,bool lock, bool spin) { + + if(qf->metadata->tag_bits==0){ + return 0; + } + __uint128_t hash = key; uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); int64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; @@ -2134,6 +2189,8 @@ uint64_t qf_remove_tag(const QF *qf, uint64_t key ,bool lock, bool spin) ¤t_count); if (current_remainder == hash_remainder){ if (lock) { + if(qf->mem->general_lock) + return false; if (!qf_lock(qf, runstart_index, spin, false)) return false; } @@ -2151,6 +2208,9 @@ uint64_t qf_remove_tag(const QF *qf, uint64_t key ,bool lock, bool spin) uint64_t qf_get_tag(const QF *qf, uint64_t key) { + if(qf->metadata->tag_bits==0){ + return 0; + } __uint128_t hash = key; uint64_t hash_remainder = hash & BITMASK(qf->metadata->key_remainder_bits); int64_t hash_bucket_index = hash >> qf->metadata->key_remainder_bits; @@ -2360,6 +2420,61 @@ inline int qfi_end(QFi *qfi) return 0; } + +void unionFn(uint64_t key_a, uint64_t tag_a,uint64_t count_a, + uint64_t key_b, uint64_t tag_b,uint64_t count_b, + uint64_t *key_c, uint64_t *tag_c,uint64_t *count_c) +{ + if(count_a==0){ + *key_c=key_b; + *tag_c=tag_a; + *count_c=count_b; + } + else if(count_b==0){ + *key_c=key_a; + *tag_c=tag_a; + *count_c=count_a; + } + else{ + *key_c=key_a; + *tag_c=tag_a; + *count_c=count_a+count_b; + } + +} +void intersectFn(uint64_t key_a, uint64_t tag_a,uint64_t count_a, + uint64_t key_b, uint64_t tag_b,uint64_t count_b, + uint64_t *key_c, uint64_t *tag_c,uint64_t *count_c) +{ + *key_c=0; + *tag_c=0; + *count_c=0; + if(count_a!=0 && count_b!=0){ + *key_c=key_a; + *tag_c=tag_a; + *count_c=std::min(count_a,count_b); + } + +} + +void subtractFn(uint64_t key_a, uint64_t tag_a,uint64_t count_a, + uint64_t key_b, uint64_t tag_b,uint64_t count_b, + uint64_t *key_c, uint64_t *tag_c,uint64_t *count_c) +{ + if(count_b==0){ + *key_c=key_a; + *tag_c=tag_a; + *count_c=count_a; + } + else{ + *key_c=key_a; + *tag_c=tag_a; + *count_c=count_ametadata->range != qfb->metadata->range || @@ -2382,37 +2501,74 @@ void qf_merge(QF *qfa, QF *qfb, QF *qfc) qf_iterator(qfa, &qfia, 0); qf_iterator(qfb, &qfib, 0); - uint64_t keya, valuea, counta, keyb, valueb, countb; - qfi_get(&qfia, &keya, &valuea, &counta); - qfi_get(&qfib, &keyb, &valueb, &countb); + uint64_t keya, taga, counta, keyb, tagb, countb; + uint64_t keyc,tagc, countc; + qfi_get(&qfia, &keya, &taga, &counta); + qfi_get(&qfib, &keyb, &tagb, &countb); + do { if (keya < keyb) { - qf_insert(qfc, keya, counta, true, true); + mergeFn(keya,taga,counta,0,0,0,&keyc,&tagc,&countc); qfi_next(&qfia); - qfi_get(&qfia, &keya, &valuea, &counta); + qfi_get(&qfia, &keya, &taga, &counta); } - else { - qf_insert(qfc, keyb, countb, true, true); + else if(keya > keyb) { + mergeFn(0,0,0,keyb,tagb,countb,&keyc,&tagc,&countc); qfi_next(&qfib); - qfi_get(&qfib, &keyb, &valueb, &countb); + qfi_get(&qfib, &keyb, &tagb, &countb); } + else{ + mergeFn(keya,taga,counta,keyb,tagb,countb,&keyc,&tagc,&countc); + qfi_next(&qfia); + qfi_next(&qfib); + qfi_get(&qfia, &keya, &taga, &counta); + qfi_get(&qfib, &keyb, &tagb, &countb); + } + if(countc!=0){ + qf_insert(qfc, keyc, countc, true, true); + qf_add_tag(qfc,keya,tagc); + } + } while(!qfi_end(&qfia) && !qfi_end(&qfib)); if (!qfi_end(&qfia)) { + do { - qfi_get(&qfia, &keya, &valuea, &counta); - qf_insert(qfc, keya, counta, true, true); + qfi_get(&qfia, &keya, &taga, &counta); + mergeFn(keya,taga,counta,0,0,0,&keyc,&tagc,&countc); + if(countc!=0){ + qf_insert(qfc, keyc, countc, true, true); + qf_add_tag(qfc,keyc,tagc); + } } while(!qfi_next(&qfia)); } + if (!qfi_end(&qfib)) { do { - qfi_get(&qfib, &keyb, &valueb, &countb); - qf_insert(qfc, keyb, countb, true, true); + qfi_get(&qfib, &keyb, &tagb, &countb); + mergeFn(0,0,0,keyb,tagb,countb,&keyc,&tagc,&countc); + if(countc!=0){ + qf_insert(qfc, keyc, countc, true, true); + qf_add_tag(qfc,keyc,tagc); + } } while(!qfi_next(&qfib)); } return; } +void qf_merge(QF *qfa, QF *qfb, QF *qfc) +{ + _qf_merge(qfa,qfb,qfc,unionFn); +} + +void qf_intersect(QF *qfa, QF *qfb, QF *qfc) +{ + _qf_merge(qfa,qfb,qfc,intersectFn); +} +void qf_subtract(QF *qfa, QF *qfb, QF *qfc) +{ + _qf_merge(qfa,qfb,qfc,subtractFn); +} bool qf_equals(QF *qfa, QF *qfb) { @@ -2450,140 +2606,300 @@ bool qf_equals(QF *qfa, QF *qfb) return true; } -void qf_intersect(QF *qfa, QF *qfb, QF *qfc) + + +std::map Tags_map; +uint64_t last_index=0; +void union_multi_Fn(uint64_t key_arr[], uint64_t tag_arr[],uint64_t count_arr[] + ,std::map > ** inverted_indexes,int nqf, + uint64_t* key_c, uint64_t* tag_c,uint64_t* count_c) { - QFi qfia, qfib; - if(qfa->metadata->range != qfb->metadata->range || - qfb->metadata->range != qfc->metadata->range ) + + *count_c=0; + for(int i=0;i > ** inverted_indexes,int nqf, + uint64_t* keyc, uint64_t* tag_c,uint64_t* count_c + )) +{ + int i; + uint64_t range=qf_arr[0]->metadata->range; + for (i=1; imetadata->range!=range) + { + throw std::logic_error("Merging non compatible filters"); } - } while(!qfi_end(&qfia) && !qfi_end(&qfib)); + } + QFi *qfi_arr[nqf]; + uint64_t smallest_key=UINT64_MAX,second_smallest_key; + uint64_t keys[nqf]; + uint64_t tags[nqf]; + uint64_t counts[nqf]; + std::map > ** inverted_indexes=new std::map >*[nqf]; - return; -} -void qf_subtract(QF *qfa, QF *qfb, QF *qfc) -{ - QFi qfia, qfib; - if(qfa->metadata->range != qfb->metadata->range || - qfb->metadata->range != qfc->metadata->range ) - { - throw std::logic_error("Calculate subtracte for non compatible filters"); + for (i=0; imetadata->tags_map; } - qf_iterator(qfa, &qfia, 0); - qf_iterator(qfb, &qfib, 0); - uint64_t keya, valuea, counta, keyb, valueb, countb; - qfi_get(&qfia, &keya, &valuea, &counta); - qfi_get(&qfib, &keyb, &valueb, &countb); - do { - if (keya < keyb) { - qfi_next(&qfia); - qfi_get(&qfia, &keya, &valuea, &counta); + uint64_t keys_m[nqf]; + uint64_t tags_m[nqf]; + uint64_t counts_m[nqf]; + + + bool finish=false; + while(!finish) + { + finish=true; + second_smallest_key=UINT64_MAX; + //printf("smallest_key = %llu\n",smallest_key ); + for(i=0;icountb? counta-countb : 0, true, true); - qfi_next(&qfia); - qfi_next(&qfib); - qfi_get(&qfia, &keya, &valuea, &counta); - qfi_get(&qfib, &keyb, &valueb, &countb); + //printf("second_smallest_key=%llu finish=%d\n",second_smallest_key,finish); + uint64_t keyc,tagc, countc; + mergeFn(keys_m,tags_m,counts_m,inverted_indexes,nqf,&keyc,&tagc,&countc); + + if(countc!=0){ + qf_insert(qfr, keyc, countc, true, true); + qf_add_tag(qfr,keyc,tagc); } - } while(!qfi_end(&qfia) && !qfi_end(&qfib)); + smallest_key=second_smallest_key; + } + // cout<<"before delete"<metadata->range; - for (i=1; imetadata->range!=range) +} + +void inverted_union_multi_Fn(uint64_t key_arr[], uint64_t tag_arr[],uint64_t count_arr[], + std::map > ** inverted_indexes ,int nqf, + uint64_t* key_c, uint64_t* tag_c,uint64_t* count_c) +{ + + std::string index_key=""; + *count_c=0; + for(int i=0;ifind(tag_arr[i]); + for(auto k:it->second){ + index_key+=std::to_string(k); + index_key+=';'; + } + } + } } + index_key.pop_back(); + auto it=Tags_map.find(index_key); + if(it==Tags_map.end()) + { - for (i=0; isecond; - while (!flag) { - uint64_t keys[nqf]; - uint64_t values[nqf]; - uint64_t counts[nqf]; - for (i=0; i > ** inverted_indexes, int nqf, + uint64_t* key_c, uint64_t* tag_c,uint64_t* count_c) +{ + std::string index_key=""; + *count_c=0; + for(int i=0;ifind(tag_arr[i]); + for(auto k:it->second){ + index_key+=std::to_string(k); + index_key+=';'; } } - qf_insert(qfr, keys[smallest_i], counts[smallest_i], - true, true); - qfi_next(&qfi_arr[smallest_i]); - qfi_get(&qfi_arr[smallest_i], &keys[smallest_i], &values[smallest_i], - &counts[smallest_i]); - } while(!qfi_end(&qfi_arr[smallest_i])); - - /* remove the qf that is exhausted from the array */ - if (smallest_i < nqf-1) - memmove(&qfi_arr[smallest_i], &qfi_arr[smallest_i+1], - (nqf-smallest_i-1)*sizeof(qfi_arr[0])); - nqf--; - if (nqf == 1) - flag = 1; - } - if (!qfi_end(&qfi_arr[0])) { - do { - uint64_t key, value, count; - qfi_get(&qfi_arr[0], &key, &value, &count); - qf_insert(qfr, key, count, true, true); - } while(!qfi_next(&qfi_arr[0])); + + } } + index_key.pop_back(); + auto it=Tags_map.find(index_key); + if(it==Tags_map.end()) + { + + Tags_map.insert(std::make_pair(index_key,last_index)); + last_index++; + it=Tags_map.find(index_key); + } + *count_c=it->second; + +} + + +void qf_invertable_merge(QF *qf_arr[], int nqf, QF *qfr) +{ + int i; + int last_tag=0; + Tags_map.clear(); + last_index=0; + for(i=0;imetadata->tags_map==NULL){ + qf_arr[i]->metadata->tags_map=new std::map >(); + vector tmp(1); + tmp[0]=last_index; + Tags_map.insert(std::make_pair(std::to_string(i),last_index++)); + qf_arr[i]->metadata->tags_map->insert(make_pair(0,tmp)); + } + else{ + auto it=qf_arr[i]->metadata->tags_map->begin(); + int updated_tags=0; + while(it!=qf_arr[i]->metadata->tags_map->end()){ + for(int j=0;jsecond.size();j++){ + it->second[j]+=last_tag; + auto it2=Tags_map.find(std::to_string(it->second[j])); + if(it2==Tags_map.end()){ + Tags_map.insert(std::make_pair(std::to_string(it->second[j]),it->second[j])); + updated_tags++; + } + } + it++; + } + } + last_tag+=Tags_map.size(); + } + + + + _qf_multi_merge(qf_arr,nqf,qfr,inverted_union_multi_Fn); + qfr->metadata->tags_map=new std::map >(); + auto it=Tags_map.begin(); + while(it!=Tags_map.end()){ + std::vector tmp=key_to_vector_int(it->first); + qfr->metadata->tags_map->insert(std::make_pair(it->second,tmp)); + it++; + + } + + +} + +void qf_invertable_merge_no_count(QF *qf_arr[], int nqf, QF *qfr) +{ + + int i; + int last_tag=0; + Tags_map.clear(); + last_index=0; + for(i=0;imetadata->tags_map==NULL){ + qf_arr[i]->metadata->tags_map=new std::map >(); + vector tmp(1); + tmp[0]=last_index; + Tags_map.insert(std::make_pair(std::to_string(i),last_index++)); + qf_arr[i]->metadata->tags_map->insert(make_pair(0,tmp)); + } + else{ + auto it=qf_arr[i]->metadata->tags_map->begin(); + int updated_tags=0; + while(it!=qf_arr[i]->metadata->tags_map->end()){ + for(int j=0;jsecond.size();j++){ + it->second[j]+=last_tag; + auto it2=Tags_map.find(std::to_string(it->second[j])); + if(it2==Tags_map.end()){ + Tags_map.insert(std::make_pair(std::to_string(it->second[j]),it->second[j])); + updated_tags++; + } + } + it++; + } + } + last_tag+=Tags_map.size(); + } + + + _qf_multi_merge(qf_arr,nqf,qfr,inverted_union_multi_no_count_Fn); + + qfr->metadata->tags_map=new std::map >(); + auto it=Tags_map.begin(); + while(it!=Tags_map.end()){ + std::vector tmp=key_to_vector_int(it->first); + qfr->metadata->tags_map->insert(std::make_pair(it->second,tmp)); + it++; + } + - return; } QF* qf_resize(QF* qf, int newQ, const char * originalFilename, const char * newFilename) @@ -2693,6 +3009,27 @@ int qf_space(QF *qf) )* 100.0); } + +bool qf_general_lock(QF* qf, bool spin){ + if (!qf_spin_lock(&qf->mem->general_lock, spin)) + return false; + return true; +} +void qf_general_unlock(QF* qf){ + qf_spin_unlock(&qf->mem->general_lock); +} +void qf_migrate(QF* source, QF* dest){ + QFi source_i; + if (qf_iterator(source, &source_i, 0)) { + do { + uint64_t key = 0, value = 0, count = 0; + qfi_get(&source_i, &key, &value, &count); + qf_insert(dest, key, count, true, true); + qf_add_tag(dest,key,value); + } while (!qfi_next(&source_i)); + } +} + #ifdef TEST #include "tests/lowLevelTests.hpp" #endif diff --git a/third-party/mqf/gqf.h b/third-party/mqf/gqf.h index 6361381f75..65c4d13b3f 100644 --- a/third-party/mqf/gqf.h +++ b/third-party/mqf/gqf.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #ifdef __cplusplus extern "C" { @@ -30,6 +32,7 @@ extern "C" { typedef struct quotient_filter_mem { int fd; + volatile int general_lock; volatile int metadata_lock; volatile int *locks; wait_time_data *wait_times; @@ -54,7 +57,9 @@ extern "C" { uint64_t noccupied_slots; uint64_t maximum_occupied_slots; uint64_t num_locks; + uint64_t maximum_count; bool mem; + std::map > * tags_map; } quotient_filter_metadata; typedef quotient_filter_metadata qfmetadata; @@ -229,6 +234,18 @@ extern "C" { /* merge multiple QFs into the final QF one. */ void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr); + + /*! @breif Invertiable merge function adds tag for each key and creates index structure. The index is map of an integer and vector of integers where the integer is the value of the tags and vector on integers is the ids of the source filters. + + @param Qf* qf_arr : input array of filters + @param int nqf: number of filters + @param QF* qfr: pointer to the output filter. + @param std::map > *inverted_index_ptr: Pointer to the output index. + */ + void qf_invertable_merge(QF *qf_arr[], int nqf, QF *qfr); + void qf_invertable_merge_no_count(QF *qf_arr[], int nqf, QF *qfr); + + /*! @breif Resize the filter into a bigger or smaller one @param Qf* qf : pointer to the Filter @@ -249,6 +266,10 @@ extern "C" { bool qf_equals(QF *qfa, QF *qfb); + bool qf_general_lock(QF* qf, bool spin); + void qf_general_unlock(QF* qf); + + void qf_migrate(QF* source, QF* destination); #ifdef __cplusplus } diff --git a/third-party/mqf/utils.cpp b/third-party/mqf/utils.cpp new file mode 100644 index 0000000000..0e2e323aae --- /dev/null +++ b/third-party/mqf/utils.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +using namespace std; + +std::vector split(const std::string &text, char sep) { + std::vector tokens; + std::size_t start = 0, end = 0; + while ((end = text.find(sep, start)) != std::string::npos) { + tokens.push_back(text.substr(start, end - start)); + start = end + 1; + } + tokens.push_back(text.substr(start)); + return tokens; +} + +vector key_to_vector_int(const string& key){ + vector tokens=split(key,';'); + vector res; + for(auto f:tokens) + { + res.push_back(atoi(f.c_str())); + } + return res; +} + +void save_tags_map(std::map > * index,const char * fileName) +{ + ofstream out(fileName); + auto it=index->begin(); + while( it != index->end() ) + { + out<first<<" "; + for(auto i:it->second) + { + out< >* load_tags_map(const char * fileName) +{ + std::map >* res=new std::map >(); + ifstream out(fileName); + string key; + uint64_t id; + while(out>>id>>key){ + + res->insert( make_pair(id,key_to_vector_int(key)) ); + } + return res; +} diff --git a/third-party/mqf/utils.h b/third-party/mqf/utils.h new file mode 100644 index 0000000000..d10f9924d6 --- /dev/null +++ b/third-party/mqf/utils.h @@ -0,0 +1,22 @@ +#ifndef _utils_H +#define _utils_H + +#include +#include +#include + +using namespace std; + +std::vector split(const std::string &text, char sep) ; + +vector key_to_vector_int(const string& key); + +void save_tags_map(std::map > * index,const char * fileName); + +std::map >* load_tags_map(const char * fileName); + +inline bool file_exists (const std::string& name) { + ifstream f(name.c_str()); + return f.good(); +} + #endif /* _utils_H */ From aade542fd902fef2023c8d77a80dd61f85a2e2d7 Mon Sep 17 00:00:00 2001 From: Shokrof Date: Tue, 19 Jun 2018 02:24:47 +0200 Subject: [PATCH 06/11] bug fix --- setup.py | 3 ++- third-party/mqf/Makefile | 18 +++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 9735d9daaa..ba1b9cf193 100755 --- a/setup.py +++ b/setup.py @@ -334,7 +334,8 @@ def run(self): spawn(cmd=mqfcmd, dry_run=self.dry_run) for ext in self.extensions: ext.extra_objects.append(path_join("third-party", "mqf", "gqf.o")) - ext.extra_objects.append(path_join("third-party", "mqf", "utils.o")) + ext.extra_objects.append(path_join("third-party", "mqf", + "utils.o")) if "z" not in self.libraries: zcmd = ['bash', '-c', 'cd ' + ZLIBDIR + ' && ( test Makefile -nt' diff --git a/third-party/mqf/Makefile b/third-party/mqf/Makefile index 18dba32df2..06203e1433 100644 --- a/third-party/mqf/Makefile +++ b/third-party/mqf/Makefile @@ -9,23 +9,27 @@ else OPT=-Ofast endif -ifdef NH +OS := $(shell uname) +ifeq ($(OS), Darwin) ARCH= else - ARCH=-msse4.2 -D__SSE4_2_ + #ARCH=-msse4.2 -D__SSE4_2_ + ARCH= endif ifdef P PROFILE=-pg -no-pie # for bug in gprof. endif -CXX = g++ -std=c++11 -CC = g++ -std=c++11 -LD= g++ -std=c++11 +# +# CXX = g++ -std=c++11 +# CC = g++ -std=c++11 +# LD= g++ -std=c++11 + +CXXFLAGS := -std=c++11 -fPIC -Wall $(DEBUG) $(PROFILE) $(OPT) -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function -CXXFLAGS = -fPIC -Wall $(DEBUG) $(PROFILE) $(OPT) $(ARCH) -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function +LDFLAGS := $(LDFLAGS) $(DEBUG) $(PROFILE) $(OPT) -LDFLAGS = $(DEBUG) $(PROFILE) $(OPT) # # declaration of dependencies From 041b7540fcf7b77c5c3d6426075c45887141979c Mon Sep 17 00:00:00 2001 From: Mostafa Shokrof Date: Wed, 26 Sep 2018 20:39:12 +0200 Subject: [PATCH 07/11] updating license --- third-party/mqf/LICENSE | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/third-party/mqf/LICENSE b/third-party/mqf/LICENSE index 09d493bf1f..a2e8301ed8 100644 --- a/third-party/mqf/LICENSE +++ b/third-party/mqf/LICENSE @@ -1,6 +1,7 @@ BSD 3-Clause License -Copyright (c) 2017, +Copyright (c) 2017, Rob Johnson and Prahsant Pandey +Copyright (c) 2017, Mostafa Shokrof and Tamer Mansour All rights reserved. Redistribution and use in source and binary forms, with or without From e7db244b1ffdbf280d099d1d001c86a7936e6e0c Mon Sep 17 00:00:00 2001 From: Shokrof Date: Tue, 6 Nov 2018 17:48:04 +0200 Subject: [PATCH 08/11] use MQF in normailze by median --- include/oxli/hashtable.hh | 4 ++-- include/oxli/storage.hh | 4 ++-- khmer/_oxli/graphs.pxd | 4 ++-- khmer/_oxli/graphs.pyx | 5 +++-- khmer/khmer_args.py | 41 ++++++++++++++++++++++++++++++++++ scripts/normalize-by-median.py | 8 ++++++- third-party/mqf/gqf.cc | 1 + 7 files changed, 58 insertions(+), 9 deletions(-) diff --git a/include/oxli/hashtable.hh b/include/oxli/hashtable.hh index 9e72c9dad3..329158569a 100644 --- a/include/oxli/hashtable.hh +++ b/include/oxli/hashtable.hh @@ -614,8 +614,8 @@ public: class QFCounttable : public oxli::MurmurHashtable { public: - explicit QFCounttable(WordLength ksize, int size) - : MurmurHashtable(ksize, new QFStorage(size)) { } ; + explicit QFCounttable(WordLength ksize, int size,int slotsize) + : MurmurHashtable(ksize, new QFStorage(size,slotsize)) { } ; }; // Hashtable-derived class with BitStorage. diff --git a/include/oxli/storage.hh b/include/oxli/storage.hh index aeac60e71e..2759b88a34 100644 --- a/include/oxli/storage.hh +++ b/include/oxli/storage.hh @@ -416,7 +416,7 @@ protected: QF mf; public: - QFStorage(int size) + QFStorage(int size,int slotSize) { // size is the power of two to specify the number of slots in // the filter (2**size). Third argument sets the number of bits used @@ -424,7 +424,7 @@ public: // Final argument is the number of bits allocated for the value, which // we do not use. _supports_bigcount = true; - qf_init(&mf, (1ULL << size), size+8, 0,2,true,"",2038074761); + qf_init(&mf, (1ULL << size), size+slotSize, 0,2,true,"",2038074761); diff --git a/khmer/_oxli/graphs.pxd b/khmer/_oxli/graphs.pxd index 52d15991af..f20089f9c4 100644 --- a/khmer/_oxli/graphs.pxd +++ b/khmer/_oxli/graphs.pxd @@ -122,7 +122,7 @@ cdef extern from "oxli/hashtable.hh" namespace "oxli" nogil: CpNodetable(WordLength, vector[uint64_t]) cdef cppclass CpQFCounttable "oxli::QFCounttable" (CpHashtable): - CpQFCounttable(WordLength, uint64_t) except +oxli_raise_py_error + CpQFCounttable(WordLength, uint64_t,uint64_t) except +oxli_raise_py_error cdef extern from "oxli/hashgraph.hh" namespace "oxli" nogil: @@ -215,7 +215,7 @@ cdef extern from "oxli/labelhash.hh" namespace "oxli": uint64_t &, CallbackFn, void *) - void consume_seqfile_and_tag_with_labels[SeqIO](const string &, + void _seqfile_and_tag_with_labels[SeqIO](const string &, uint32_t &, uint64_t &) void consume_seqfile_and_tag_with_labels[SeqIO]( diff --git a/khmer/_oxli/graphs.pyx b/khmer/_oxli/graphs.pyx index 62881c9166..022feb5df6 100644 --- a/khmer/_oxli/graphs.pyx +++ b/khmer/_oxli/graphs.pyx @@ -368,9 +368,10 @@ cdef class QFCounttable(Hashtable): Set the number of slots used by the counting quotient filter. This determines the amount of memory used and how many k-mers can be entered into the datastructure. Each slot uses roughly 1.3 bytes. + slot size: integer """ - def __cinit__(self, int k, uint64_t size): + def __cinit__(self, int k, uint64_t size,uint64_t slotsize): # size has to be a power of two power_of_two = ((size & (size - 1) == 0) and (size != 0)) @@ -378,7 +379,7 @@ cdef class QFCounttable(Hashtable): raise ValueError("size has to be a power of two, not" " {}.".format(size)) if type(self) is QFCounttable: - self._qf_this = make_shared[CpQFCounttable](k, log(size, 2)) + self._qf_this = make_shared[CpQFCounttable](k, log(size, 2),slotsize) self._ht_this = self._qf_this diff --git a/khmer/khmer_args.py b/khmer/khmer_args.py index 8f233e4efa..fe67863dd3 100755 --- a/khmer/khmer_args.py +++ b/khmer/khmer_args.py @@ -538,6 +538,47 @@ def create_nodegraph(args, ksize=None, multiplier=1.0, fp_rate=0.01): return khmer.Nodegraph(ksize, tablesize, args.n_tables) +def create_MQFGraph(args, ksize=None, multiplier=1.0, fp_rate=0.1): + if ksize is None: + ksize = args.ksize + if ksize > 31: + print_error("\n** ERROR: khmer only supports k-mer sizes <= 32.\n") + sys.exit(1) + if not args.unique_kmers: + print_error("\n** ERROR: please supply unique number of kmers.\n") + sys.exit(1) + + + if args.unique_kmers: + size=int(math.ceil(math.log2(1.3*args.unique_kmers))) +# if args.max_tablesize: +# size=args.max_tablesize + + if args.fp_rate: + log_info("*** INFO: Overriding default fp {def_fp} with new fp:" + " {new_fp}", def_fp=fp_rate, new_fp=args.fp_rate) + fp_rate = args.fp_rate + + p=int(math.ceil(math.log2(float(args.unique_kmers)/float(fp_rate)))) + slotSize=p-size + + if slotSize<2 : + print_error("\n** ERROR: too small slot size.\n") + sys.exit(1) + + nslots=2**size + nslots+=10*math.sqrt(nslots) + nblocks=int(((nslots+63)/64)) + blockSize=17 + bitsPerSlot=slotSize+2 + totalSize=nblocks*(blockSize+bitsPerSlot*8) + totalSize/=(1000.0 ** 3); + log_info("*** INFO: creating MQF of size {size} and slot {slotsize}. Total Size ={totalSize}G" + , size=size, slotsize=slotSize,totalSize=totalSize) + + return khmer.QFCounttable(ksize,2**size,slotSize) + + def create_countgraph(args, ksize=None, multiplier=1.0, fp_rate=0.1): """Create and return a countgraph.""" args = _check_fp_rate(args, fp_rate) diff --git a/scripts/normalize-by-median.py b/scripts/normalize-by-median.py index 39e387663e..544154d422 100755 --- a/scripts/normalize-by-median.py +++ b/scripts/normalize-by-median.py @@ -51,7 +51,7 @@ import os import khmer import textwrap -from khmer import khmer_args, Countgraph +from khmer import khmer_args, Countgraph, QFCounttable from contextlib import contextmanager from khmer.khmer_args import (build_counting_args, add_loadgraph_args, report_on_config, calculate_graphsize, @@ -295,6 +295,8 @@ def get_parser(): help='Input FAST[AQ] sequence filename.', nargs='+') add_loadgraph_args(parser) add_output_compression_type(parser) + parser.add_argument('--mqf', dest='mqf', default=False, + action='store_true') return parser @@ -338,10 +340,14 @@ def main(): # pylint: disable=too-many-branches,too-many-statements log_info('loading k-mer countgraph from {graph}', graph=args.loadgraph) countgraph = Countgraph.load(args.loadgraph) + elif args.mqf: + countgraph = khmer_args.create_MQFGraph(args) else: log_info('making countgraph') countgraph = khmer_args.create_countgraph(args) + + # create an object to handle diginorm of all files norm = Normalizer(args.cutoff, countgraph) with_diagnostics = WithDiagnostics(norm, report_fp, args.report_frequency) diff --git a/third-party/mqf/gqf.cc b/third-party/mqf/gqf.cc index befe91ee19..c516a925a8 100644 --- a/third-party/mqf/gqf.cc +++ b/third-party/mqf/gqf.cc @@ -1848,6 +1848,7 @@ void qf_init(QF *qf, uint64_t nslots, uint64_t key_bits, uint64_t tag_bits,uint6 //bits_per_slot,key_remainder_bits,fixed_counter_size,tag_bits ); size = nblocks * (sizeof(qfblock) + (8 * bits_per_slot )) ; + qf->mem = (qfmem *)calloc(sizeof(qfmem), 1); if (mem) { From 4e48448db88c65bb8577af9de2636bb3a77b445b Mon Sep 17 00:00:00 2001 From: Shokrof Date: Thu, 8 Nov 2018 22:31:11 +0200 Subject: [PATCH 09/11] First Code --- Makefile | 2 +- include/oxli/storage.hh | 2 +- khmer/_oxli/graphs.pyx | 2 +- setup.cfg | 2 +- setup.py | 6 +- src/oxli/Makefile | 4 +- tests/table_fixtures.py | 2 +- tests/test_qfstorage.py | 10 +- third-party/MQF/.codecov.yml | 4 + third-party/MQF/.gitignore | 19 + third-party/MQF/.travis.yml | 23 + third-party/{mqf/LICENSE => MQF/LICENSE.txt} | 3 +- third-party/MQF/LayeredMQF.cpp | 140 + third-party/MQF/LayeredMQF.h | 136 + third-party/MQF/Makefile | 90 + third-party/MQF/QuotientFilter_MQF.png | Bin 0 -> 46950 bytes third-party/MQF/README.md | 170 + third-party/MQF/ThirdParty/stxxl/.travis.yml | 37 + third-party/MQF/ThirdParty/stxxl/AUTHORS | 15 + third-party/MQF/ThirdParty/stxxl/CHANGELOG | 536 + .../MQF/ThirdParty/stxxl/CMakeLists.txt | 791 + third-party/MQF/ThirdParty/stxxl/INSTALL | 23 + .../MQF/ThirdParty/stxxl/LICENSE_1_0.txt | 23 + third-party/MQF/ThirdParty/stxxl/README | 27 + third-party/MQF/ThirdParty/stxxl/TODO | 116 + .../ThirdParty/stxxl/doc/DoxygenLayout.xml | 186 + .../MQF/ThirdParty/stxxl/doc/coding_style.dox | 79 + .../MQF/ThirdParty/stxxl/doc/common.dox | 530 + .../MQF/ThirdParty/stxxl/doc/design.dox | 1264 ++ .../ThirdParty/stxxl/doc/doxygen-extra.css | 43 + third-party/MQF/ThirdParty/stxxl/doc/faq.dox | 161 + .../ThirdParty/stxxl/doc/images/btree_uml.pdf | Bin 0 -> 37091 bytes .../ThirdParty/stxxl/doc/images/btree_uml.png | Bin 0 -> 59372 bytes .../ThirdParty/stxxl/doc/images/btree_uml.xmi | 6099 ++++++++ .../stxxl/doc/images/btree_uml_small.png | Bin 0 -> 103701 bytes .../images/install_windows_boost_cmake.png | Bin 0 -> 24483 bytes .../images/install_windows_boost_msvc10.png | Bin 0 -> 36613 bytes .../doc/images/install_windows_cmake.png | Bin 0 -> 27592 bytes .../doc/images/install_windows_msvc11.png | Bin 0 -> 24753 bytes .../stxxl/doc/images/layer_diagram.pdf | Bin 0 -> 29116 bytes .../stxxl/doc/images/layer_diagram.png | Bin 0 -> 54489 bytes .../stxxl/doc/images/layer_diagram.svg | 576 + .../stxxl/doc/images/layer_diagram_small.png | Bin 0 -> 49060 bytes .../stxxl/doc/images/overlapping_merging.pdf | Bin 0 -> 8436 bytes .../stxxl/doc/images/overlapping_merging.png | Bin 0 -> 29740 bytes .../doc/images/overlapping_merging_small.png | Bin 0 -> 28882 bytes .../doc/images/overlapping_runformation.pdf | Bin 0 -> 12133 bytes .../doc/images/overlapping_runformation.png | Bin 0 -> 33479 bytes .../images/overlapping_runformation_small.png | Bin 0 -> 24171 bytes .../MQF/ThirdParty/stxxl/doc/images/pdm.png | Bin 0 -> 25906 bytes .../MQF/ThirdParty/stxxl/doc/images/pdm.svg | 581 + .../ThirdParty/stxxl/doc/images/pdm_small.png | Bin 0 -> 27892 bytes .../stxxl/doc/images/pipeline_randomgraph.pdf | Bin 0 -> 6348 bytes .../stxxl/doc/images/pipeline_randomgraph.png | Bin 0 -> 21805 bytes .../doc/images/pipeline_randomgraph_small.png | Bin 0 -> 7068 bytes .../stxxl/doc/images/san00b_pqueue.png | Bin 0 -> 76197 bytes .../stxxl/doc/images/san00b_pqueue.svg | 759 + .../stxxl/doc/images/san00b_pqueue_small.png | Bin 0 -> 39824 bytes .../stxxl/doc/images/simple_logo.png | Bin 0 -> 9309 bytes .../stxxl/doc/images/simple_logo.svg | 118 + .../stxxl/doc/images/vector_architecture.pdf | Bin 0 -> 3223 bytes .../stxxl/doc/images/vector_architecture.png | Bin 0 -> 33965 bytes .../doc/images/vector_architecture_small.png | Bin 0 -> 11572 bytes .../MQF/ThirdParty/stxxl/doc/install.dox | 566 + .../MQF/ThirdParty/stxxl/doc/introduction.dox | 84 + .../MQF/ThirdParty/stxxl/doc/mainpage.dox | 153 + .../MQF/ThirdParty/stxxl/doc/references.bib | 533 + .../MQF/ThirdParty/stxxl/doc/stxxl_tool.dox | 121 + .../MQF/ThirdParty/stxxl/doc/tutorial.dox | 74 + .../ThirdParty/stxxl/doc/tutorial_deque.dox | 120 + .../MQF/ThirdParty/stxxl/doc/tutorial_map.dox | 171 + .../ThirdParty/stxxl/doc/tutorial_matrix.dox | 102 + .../ThirdParty/stxxl/doc/tutorial_pqueue.dox | 128 + .../ThirdParty/stxxl/doc/tutorial_queue.dox | 80 + .../stxxl/doc/tutorial_sequence.dox | 109 + .../ThirdParty/stxxl/doc/tutorial_sorter.dox | 116 + .../ThirdParty/stxxl/doc/tutorial_stack.dox | 94 + .../ThirdParty/stxxl/doc/tutorial_stream.dox | 509 + .../stxxl/doc/tutorial_unordered_map.dox | 59 + .../ThirdParty/stxxl/doc/tutorial_vector.dox | 107 + .../stxxl/doc/tutorial_vector_billing.dox | 114 + .../stxxl/doc/tutorial_vector_buf.dox | 102 + .../ThirdParty/stxxl/examples/CMakeLists.txt | 18 + .../stxxl/examples/algo/CMakeLists.txt | 16 + .../examples/algo/copy_and_sort_file.cpp | 118 + .../stxxl/examples/algo/phonebills.cpp | 173 + .../stxxl/examples/algo/phonebills_genlog.cpp | 123 + .../stxxl/examples/algo/sort_file.cpp | 158 + .../examples/applications/CMakeLists.txt | 26 + .../stxxl/examples/applications/skew3-lcp.cpp | 2909 ++++ .../stxxl/examples/applications/skew3.cpp | 1402 ++ .../stxxl/examples/common/CMakeLists.txt | 15 + .../stxxl/examples/common/cmdline.cpp | 52 + .../stxxl/examples/containers/CMakeLists.txt | 48 + .../stxxl/examples/containers/copy_file.cpp | 118 + .../stxxl/examples/containers/deque1.cpp | 45 + .../stxxl/examples/containers/deque2.cpp | 65 + .../stxxl/examples/containers/map1.cpp | 63 + .../stxxl/examples/containers/matrix1.cpp | 67 + .../stxxl/examples/containers/pqueue1.cpp | 55 + .../stxxl/examples/containers/pqueue2.cpp | 69 + .../stxxl/examples/containers/queue1.cpp | 41 + .../stxxl/examples/containers/queue2.cpp | 51 + .../stxxl/examples/containers/sequence1.cpp | 38 + .../stxxl/examples/containers/sorter1.cpp | 62 + .../stxxl/examples/containers/sorter2.cpp | 88 + .../stxxl/examples/containers/stack1.cpp | 39 + .../stxxl/examples/containers/stack2.cpp | 43 + .../examples/containers/unordered_map1.cpp | 89 + .../stxxl/examples/containers/vector1.cpp | 37 + .../stxxl/examples/containers/vector2.cpp | 50 + .../stxxl/examples/containers/vector_buf.cpp | 140 + .../stxxl/examples/stream/CMakeLists.txt | 15 + .../stxxl/examples/stream/stream1.cpp | 297 + .../MQF/ThirdParty/stxxl/include/stxxl.h | 41 + .../ThirdParty/stxxl/include/stxxl/algorithm | 20 + .../stxxl/include/stxxl/aligned_alloc | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/all | 14 + .../stxxl/include/stxxl/bits/algo/adaptor.h | 246 + .../include/stxxl/bits/algo/async_schedule.h | 78 + .../stxxl/include/stxxl/bits/algo/inmemsort.h | 60 + .../stxxl/include/stxxl/bits/algo/intksort.h | 367 + .../stxxl/include/stxxl/bits/algo/ksort.h | 1083 ++ .../stxxl/include/stxxl/bits/algo/losertree.h | 259 + .../include/stxxl/bits/algo/random_shuffle.h | 385 + .../include/stxxl/bits/algo/run_cursor.h | 126 + .../stxxl/include/stxxl/bits/algo/scan.h | 331 + .../stxxl/include/stxxl/bits/algo/sort.h | 1000 ++ .../stxxl/include/stxxl/bits/algo/sort_base.h | 48 + .../include/stxxl/bits/algo/sort_helper.h | 153 + .../include/stxxl/bits/algo/stable_ksort.h | 467 + .../stxxl/bits/common/addressable_queues.h | 203 + .../include/stxxl/bits/common/aligned_alloc.h | 129 + .../include/stxxl/bits/common/binary_buffer.h | 650 + .../stxxl/include/stxxl/bits/common/cmdline.h | 811 + .../stxxl/bits/common/condition_variable.h | 83 + .../include/stxxl/bits/common/counting_ptr.h | 525 + .../include/stxxl/bits/common/custom_stats.h | 150 + .../stxxl/bits/common/error_handling.h | 159 + .../include/stxxl/bits/common/exceptions.h | 88 + .../include/stxxl/bits/common/exithandler.h | 38 + .../stxxl/bits/common/external_shared_ptr.h | 119 + .../stxxl/include/stxxl/bits/common/is_heap.h | 51 + .../include/stxxl/bits/common/is_sorted.h | 50 + .../stxxl/include/stxxl/bits/common/log.h | 54 + .../stxxl/include/stxxl/bits/common/mutex.h | 203 + .../include/stxxl/bits/common/new_alloc.h | 143 + .../include/stxxl/bits/common/onoff_switch.h | 84 + .../stxxl/include/stxxl/bits/common/rand.h | 292 + .../stxxl/include/stxxl/bits/common/seed.h | 29 + .../include/stxxl/bits/common/semaphore.h | 83 + .../include/stxxl/bits/common/settings.h | 39 + .../include/stxxl/bits/common/simple_vector.h | 181 + .../stxxl/include/stxxl/bits/common/state.h | 67 + .../include/stxxl/bits/common/swap_vector.h | 298 + .../stxxl/include/stxxl/bits/common/timer.h | 280 + .../stxxl/include/stxxl/bits/common/tmeta.h | 163 + .../stxxl/include/stxxl/bits/common/tuple.h | 738 + .../stxxl/include/stxxl/bits/common/types.h | 71 + .../include/stxxl/bits/common/uint_types.h | 323 + .../stxxl/include/stxxl/bits/common/utils.h | 315 + .../include/stxxl/bits/common/winner_tree.h | 366 + .../include/stxxl/bits/compat/hash_map.h | 82 + .../include/stxxl/bits/compat/type_traits.h | 108 + .../include/stxxl/bits/compat/unique_ptr.h | 67 + .../stxxl/include/stxxl/bits/config.h.in | 126 + .../stxxl/bits/containers/btree/btree.h | 1221 ++ .../stxxl/bits/containers/btree/iterator.h | 382 + .../bits/containers/btree/iterator_map.h | 168 + .../stxxl/bits/containers/btree/leaf.h | 699 + .../stxxl/bits/containers/btree/node.h | 761 + .../stxxl/bits/containers/btree/node_cache.h | 624 + .../stxxl/bits/containers/btree/root_node.h | 32 + .../include/stxxl/bits/containers/deque.h | 723 + .../bits/containers/hash_map/block_cache.h | 612 + .../stxxl/bits/containers/hash_map/hash_map.h | 1608 ++ .../stxxl/bits/containers/hash_map/iterator.h | 587 + .../bits/containers/hash_map/iterator_map.h | 279 + .../stxxl/bits/containers/hash_map/tuning.h | 50 + .../stxxl/bits/containers/hash_map/util.h | 577 + .../stxxl/include/stxxl/bits/containers/map.h | 510 + .../include/stxxl/bits/containers/matrix.h | 1423 ++ .../stxxl/bits/containers/matrix_arithmetic.h | 2064 +++ .../stxxl/bits/containers/matrix_low_level.h | 755 + .../include/stxxl/bits/containers/pager.h | 122 + .../bits/containers/parallel_priority_queue.h | 4675 ++++++ .../stxxl/bits/containers/pq_ext_merger.h | 662 + .../stxxl/bits/containers/pq_helpers.h | 349 + .../stxxl/bits/containers/pq_int_merger.h | 396 + .../stxxl/bits/containers/pq_losertree.h | 752 + .../stxxl/bits/containers/pq_mergers.h | 984 ++ .../stxxl/bits/containers/priority_queue.h | 1056 ++ .../include/stxxl/bits/containers/queue.h | 421 + .../include/stxxl/bits/containers/sequence.h | 832 + .../include/stxxl/bits/containers/sorter.h | 282 + .../include/stxxl/bits/containers/stack.h | 1063 ++ .../stxxl/bits/containers/unordered_map.h | 498 + .../include/stxxl/bits/containers/vector.h | 2632 ++++ .../stxxl/include/stxxl/bits/defines.h | 99 + .../stxxl/include/stxxl/bits/deprecated.h | 34 + .../include/stxxl/bits/io/boostfd_file.h | 76 + .../stxxl/bits/io/completion_handler.h | 100 + .../stxxl/include/stxxl/bits/io/create_file.h | 41 + .../include/stxxl/bits/io/disk_queued_file.h | 67 + .../stxxl/include/stxxl/bits/io/disk_queues.h | 128 + .../stxxl/include/stxxl/bits/io/file.h | 230 + .../include/stxxl/bits/io/fileperblock_file.h | 80 + .../stxxl/include/stxxl/bits/io/io.h | 38 + .../stxxl/include/stxxl/bits/io/iostats.h | 647 + .../include/stxxl/bits/io/linuxaio_file.h | 82 + .../include/stxxl/bits/io/linuxaio_queue.h | 100 + .../include/stxxl/bits/io/linuxaio_request.h | 74 + .../stxxl/include/stxxl/bits/io/mem_file.h | 62 + .../stxxl/include/stxxl/bits/io/mmap_file.h | 61 + .../stxxl/include/stxxl/bits/io/request.h | 120 + .../include/stxxl/bits/io/request_interface.h | 86 + .../stxxl/bits/io/request_operations.h | 159 + .../include/stxxl/bits/io/request_queue.h | 43 + .../stxxl/bits/io/request_queue_impl_1q.h | 70 + .../stxxl/bits/io/request_queue_impl_qwqr.h | 74 + .../stxxl/bits/io/request_queue_impl_worker.h | 66 + .../stxxl/bits/io/request_with_state.h | 64 + .../stxxl/bits/io/request_with_waiters.h | 60 + .../include/stxxl/bits/io/serving_request.h | 53 + .../include/stxxl/bits/io/simdisk_file.h | 150 + .../include/stxxl/bits/io/syscall_file.h | 56 + .../include/stxxl/bits/io/ufs_file_base.h | 64 + .../stxxl/include/stxxl/bits/io/wbtl_file.h | 113 + .../include/stxxl/bits/io/wfs_file_base.h | 63 + .../include/stxxl/bits/io/wincall_file.h | 69 + .../stxxl/include/stxxl/bits/libstxxl.h | 25 + .../stxxl/include/stxxl/bits/mng/adaptor.h | 659 + .../stxxl/include/stxxl/bits/mng/bid.h | 162 + .../include/stxxl/bits/mng/block_alloc.h | 266 + .../stxxl/bits/mng/block_alloc_interleaved.h | 169 + .../include/stxxl/bits/mng/block_manager.h | 278 + .../include/stxxl/bits/mng/block_prefetcher.h | 229 + .../include/stxxl/bits/mng/block_scheduler.h | 1844 +++ .../include/stxxl/bits/mng/buf_istream.h | 157 + .../stxxl/bits/mng/buf_istream_reverse.h | 168 + .../include/stxxl/bits/mng/buf_ostream.h | 127 + .../stxxl/include/stxxl/bits/mng/buf_writer.h | 222 + .../stxxl/include/stxxl/bits/mng/config.h | 276 + .../include/stxxl/bits/mng/disk_allocator.h | 251 + .../include/stxxl/bits/mng/prefetch_pool.h | 383 + .../include/stxxl/bits/mng/read_write_pool.h | 224 + .../include/stxxl/bits/mng/typed_block.h | 370 + .../stxxl/include/stxxl/bits/mng/write_pool.h | 289 + .../include/stxxl/bits/msvc_compatibility.h | 39 + .../stxxl/include/stxxl/bits/namespace.h | 20 + .../stxxl/include/stxxl/bits/noncopyable.h | 48 + .../stxxl/include/stxxl/bits/parallel.h | 240 + .../stxxl/include/stxxl/bits/parallel/base.h | 151 + .../bits/parallel/compiletime_settings.h | 63 + .../stxxl/bits/parallel/equally_split.h | 59 + .../include/stxxl/bits/parallel/losertree.h | 1074 ++ .../stxxl/include/stxxl/bits/parallel/merge.h | 239 + .../stxxl/bits/parallel/multiseq_selection.h | 586 + .../stxxl/bits/parallel/multiway_merge.h | 1802 +++ .../stxxl/bits/parallel/multiway_mergesort.h | 388 + .../include/stxxl/bits/parallel/settings.h | 195 + .../stxxl/include/stxxl/bits/parallel/tags.h | 37 + .../include/stxxl/bits/parallel/timing.h | 201 + .../stxxl/include/stxxl/bits/parallel/types.h | 55 + .../stxxl/include/stxxl/bits/singleton.h | 76 + .../stxxl/include/stxxl/bits/stream/choose.h | 311 + .../include/stxxl/bits/stream/sort_stream.h | 1661 ++ .../include/stxxl/bits/stream/sorted_runs.h | 128 + .../stxxl/include/stxxl/bits/stream/stream.h | 1536 ++ .../stxxl/include/stxxl/bits/stream/unique.h | 136 + .../stxxl/include/stxxl/bits/unused.h | 28 + .../stxxl/include/stxxl/bits/utils/malloc.h | 150 + .../include/stxxl/bits/utils/malloc_count.h | 54 + .../include/stxxl/bits/utils/stacktrace.h | 109 + .../stxxl/include/stxxl/bits/verbose.h | 280 + .../stxxl/include/stxxl/bits/version.h | 100 + .../ThirdParty/stxxl/include/stxxl/cmdline | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/deque | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/io | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/ksort | 14 + .../stxxl/include/stxxl/mallocstats | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/map | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/mng | 16 + .../stxxl/include/stxxl/priority_queue | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/queue | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/random | 14 + .../stxxl/include/stxxl/random_shuffle | 14 + .../ThirdParty/stxxl/include/stxxl/request | 15 + .../MQF/ThirdParty/stxxl/include/stxxl/scan | 14 + .../ThirdParty/stxxl/include/stxxl/sequence | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/sort | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/sorter | 14 + .../stxxl/include/stxxl/stable_ksort | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/stack | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/stats | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/stream | 15 + .../MQF/ThirdParty/stxxl/include/stxxl/timer | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/types | 14 + .../stxxl/include/stxxl/unordered_map | 14 + .../MQF/ThirdParty/stxxl/include/stxxl/vector | 14 + .../ThirdParty/stxxl/include/stxxl/version.h | 13 + .../MQF/ThirdParty/stxxl/lib/CMakeLists.txt | 134 + .../stxxl/lib/algo/async_schedule.cpp | 208 + .../ThirdParty/stxxl/lib/common/cmdline.cpp | 356 + .../stxxl/lib/common/exithandler.cpp | 73 + .../MQF/ThirdParty/stxxl/lib/common/log.cpp | 42 + .../MQF/ThirdParty/stxxl/lib/common/rand.cpp | 22 + .../MQF/ThirdParty/stxxl/lib/common/seed.cpp | 79 + .../MQF/ThirdParty/stxxl/lib/common/utils.cpp | 128 + .../ThirdParty/stxxl/lib/common/verbose.cpp | 77 + .../ThirdParty/stxxl/lib/common/version.cpp | 52 + .../ThirdParty/stxxl/lib/io/boostfd_file.cpp | 206 + .../ThirdParty/stxxl/lib/io/create_file.cpp | 240 + .../stxxl/lib/io/disk_queued_file.cpp | 53 + .../MQF/ThirdParty/stxxl/lib/io/file.cpp | 23 + .../stxxl/lib/io/fileperblock_file.cpp | 166 + .../MQF/ThirdParty/stxxl/lib/io/iostats.cpp | 409 + .../ThirdParty/stxxl/lib/io/linuxaio_file.cpp | 66 + .../stxxl/lib/io/linuxaio_queue.cpp | 266 + .../stxxl/lib/io/linuxaio_request.cpp | 129 + .../MQF/ThirdParty/stxxl/lib/io/mem_file.cpp | 92 + .../MQF/ThirdParty/stxxl/lib/io/mmap_file.cpp | 74 + .../MQF/ThirdParty/stxxl/lib/io/request.cpp | 91 + .../stxxl/lib/io/request_queue_impl_1q.cpp | 160 + .../stxxl/lib/io/request_queue_impl_qwqr.cpp | 228 + .../lib/io/request_queue_impl_worker.cpp | 84 + .../stxxl/lib/io/request_with_state.cpp | 95 + .../stxxl/lib/io/request_with_waiters.cpp | 64 + .../stxxl/lib/io/serving_request.cpp | 75 + .../ThirdParty/stxxl/lib/io/simdisk_file.cpp | 230 + .../ThirdParty/stxxl/lib/io/syscall_file.cpp | 120 + .../ThirdParty/stxxl/lib/io/ufs_file_base.cpp | 288 + .../ThirdParty/stxxl/lib/io/ufs_platform.h | 79 + .../MQF/ThirdParty/stxxl/lib/io/wbtl_file.cpp | 355 + .../ThirdParty/stxxl/lib/io/wfs_file_base.cpp | 240 + .../ThirdParty/stxxl/lib/io/wincall_file.cpp | 103 + .../MQF/ThirdParty/stxxl/lib/libstxxl.symbols | 233 + .../stxxl/lib/mng/block_manager.cpp | 123 + .../MQF/ThirdParty/stxxl/lib/mng/config.cpp | 496 + .../stxxl/lib/mng/disk_allocator.cpp | 163 + .../stxxl/lib/utils/malloc_count.cpp | 347 + .../MQF/ThirdParty/stxxl/local/CMakeLists.txt | 24 + .../MQF/ThirdParty/stxxl/local/test1.cpp | 60 + .../MQF/ThirdParty/stxxl/local/test2.cpp | 68 + .../misc/analyze-include-dependencies.pl | 288 + .../ThirdParty/stxxl/misc/analyze-source.pl | 454 + .../stxxl/misc/cmake/TestFileOffsetBits.c | 10 + .../stxxl/misc/cmake/TestLargeFiles.c.cmakein | 24 + .../stxxl/misc/cmake/TestLargeFiles.cmake | 117 + .../stxxl/misc/cmake/TestWindowsFSeek.c | 11 + .../stxxl/misc/cmake/stxxl-config.cmake.in | 20 + .../stxxl/misc/cmake/stxxl-version.cmake.in | 11 + .../MQF/ThirdParty/stxxl/misc/cmake/stxxl.pc | 11 + .../MQF/ThirdParty/stxxl/misc/concat-lines | 35 + .../ThirdParty/stxxl/misc/diskbench-avgdat.sh | 31 + .../MQF/ThirdParty/stxxl/misc/diskbench.mk | 261 + .../MQF/ThirdParty/stxxl/misc/do-release.txt | 88 + .../MQF/ThirdParty/stxxl/misc/fileheader.txt | 14 + .../ThirdParty/stxxl/misc/floating-average | 60 + .../MQF/ThirdParty/stxxl/misc/iostat-plot.mk | 187 + .../ThirdParty/stxxl/misc/record-load-iostat | 76 + .../MQF/ThirdParty/stxxl/misc/remove-unless | 12 + .../MQF/ThirdParty/stxxl/misc/uncrustify.cfg | 1693 ++ .../MQF/ThirdParty/stxxl/misc/valgrind.supp | 8 + .../MQF/ThirdParty/stxxl/tests/CMakeLists.txt | 19 + .../stxxl/tests/algo/CMakeLists.txt | 109 + .../ThirdParty/stxxl/tests/algo/test_asch.cpp | 67 + .../stxxl/tests/algo/test_bad_cmp.cpp | 165 + .../stxxl/tests/algo/test_ksort.cpp | 124 + .../tests/algo/test_ksort_all_parameters.cpp | 168 + .../stxxl/tests/algo/test_parallel_sort.cpp | 255 + .../stxxl/tests/algo/test_random_shuffle.cpp | 101 + .../ThirdParty/stxxl/tests/algo/test_scan.cpp | 104 + .../ThirdParty/stxxl/tests/algo/test_sort.cpp | 131 + .../tests/algo/test_sort_all_parameters.cpp | 164 + .../tests/algo/test_sort_all_parameters.h | 124 + .../stxxl/tests/algo/test_stable_ksort.cpp | 76 + .../algo/test_stable_ksort_all_parameters.cpp | 167 + .../stxxl/tests/common/CMakeLists.txt | 41 + .../stxxl/tests/common/test_binary_buffer.cpp | 90 + .../stxxl/tests/common/test_cmdline.cpp | 52 + .../stxxl/tests/common/test_counting_ptr.cpp | 86 + .../tests/common/test_external_shared_ptr.cpp | 291 + .../stxxl/tests/common/test_globals.cpp | 20 + .../stxxl/tests/common/test_log2.cpp | 111 + .../stxxl/tests/common/test_manyunits.cpp | 13 + .../stxxl/tests/common/test_manyunits2.cpp | 18 + .../stxxl/tests/common/test_random.cpp | 46 + .../stxxl/tests/common/test_swap_vector.cpp | 136 + .../stxxl/tests/common/test_tuple.cpp | 67 + .../stxxl/tests/common/test_uint_types.cpp | 79 + .../stxxl/tests/common/test_winner_tree.cpp | 149 + .../stxxl/tests/containers/CMakeLists.txt | 72 + .../tests/containers/btree/CMakeLists.txt | 27 + .../tests/containers/btree/test_btree.cpp | 299 + .../btree/test_btree_const_scan.cpp | 147 + .../btree/test_btree_insert_erase.cpp | 119 + .../btree/test_btree_insert_find.cpp | 106 + .../btree/test_btree_insert_scan.cpp | 111 + .../tests/containers/hash_map/CMakeLists.txt | 21 + .../containers/hash_map/test_hash_map.cpp | 317 + .../hash_map/test_hash_map_block_cache.cpp | 155 + .../hash_map/test_hash_map_iterators.cpp | 390 + .../hash_map/test_hash_map_reader_writer.cpp | 176 + .../tests/containers/map_test_handlers.h | 117 + .../stxxl/tests/containers/ppq/CMakeLists.txt | 25 + .../tests/containers/ppq/benchmark_pqs.cpp | 1874 +++ .../stxxl/tests/containers/ppq/test_ppq.cpp | 263 + .../ppq/test_ppq_arrays_and_iterator.cpp | 543 + .../stxxl/tests/containers/test_deque.cpp | 114 + .../tests/containers/test_ext_merger.cpp | 102 + .../tests/containers/test_ext_merger2.cpp | 194 + .../stxxl/tests/containers/test_iterators.cpp | 420 + .../tests/containers/test_many_stacks.cpp | 39 + .../stxxl/tests/containers/test_map.cpp | 100 + .../tests/containers/test_map_random.cpp | 338 + .../stxxl/tests/containers/test_matrix.cpp | 411 + .../tests/containers/test_migr_stack.cpp | 66 + .../stxxl/tests/containers/test_pqueue.cpp | 148 + .../stxxl/tests/containers/test_queue.cpp | 173 + .../stxxl/tests/containers/test_queue2.cpp | 77 + .../stxxl/tests/containers/test_sequence.cpp | 102 + .../stxxl/tests/containers/test_sorter.cpp | 195 + .../stxxl/tests/containers/test_stack.cpp | 188 + .../stxxl/tests/containers/test_vector.cpp | 186 + .../tests/containers/test_vector_buf.cpp | 285 + .../tests/containers/test_vector_export.cpp | 50 + .../tests/containers/test_vector_resize.cpp | 42 + .../tests/containers/test_vector_sizes.cpp | 143 + .../ThirdParty/stxxl/tests/io/CMakeLists.txt | 56 + .../ThirdParty/stxxl/tests/io/test_cancel.cpp | 85 + .../MQF/ThirdParty/stxxl/tests/io/test_io.cpp | 95 + .../stxxl/tests/io/test_io_sizes.cpp | 92 + .../ThirdParty/stxxl/tests/io/test_mmap.cpp | 66 + .../stxxl/tests/io/test_sim_disk.cpp | 89 + .../ThirdParty/stxxl/tests/mng/CMakeLists.txt | 45 + .../stxxl/tests/mng/test_aligned.cpp | 75 + .../tests/mng/test_block_alloc_strategy.cpp | 64 + .../stxxl/tests/mng/test_block_manager.cpp | 100 + .../stxxl/tests/mng/test_block_manager1.cpp | 36 + .../stxxl/tests/mng/test_block_manager2.cpp | 61 + .../stxxl/tests/mng/test_block_scheduler.cpp | 375 + .../stxxl/tests/mng/test_bmlayer.cpp | 167 + .../stxxl/tests/mng/test_buf_streams.cpp | 78 + .../stxxl/tests/mng/test_config.cpp | 104 + .../stxxl/tests/mng/test_pool_pair.cpp | 144 + .../stxxl/tests/mng/test_prefetch_pool.cpp | 51 + .../stxxl/tests/mng/test_read_write_pool.cpp | 137 + .../stxxl/tests/mng/test_write_pool.cpp | 44 + .../stxxl/tests/parallel/CMakeLists.txt | 24 + .../tests/parallel/bench_multiway_merge.cpp | 409 + .../tests/parallel/bench_multiway_merge.plot | 79 + .../tests/parallel/test_multiway_merge.cpp | 190 + .../parallel/test_multiway_mergesort.cpp | 74 + .../stxxl/tests/stream/CMakeLists.txt | 34 + .../stxxl/tests/stream/test_loop.cpp | 286 + .../stxxl/tests/stream/test_materialize.cpp | 152 + .../tests/stream/test_naive_transpose.cpp | 162 + .../stxxl/tests/stream/test_push_sort.cpp | 95 + .../stxxl/tests/stream/test_sorted_runs.cpp | 101 + .../stxxl/tests/stream/test_stream.cpp | 231 + .../stxxl/tests/stream/test_stream1.cpp | 100 + .../MQF/ThirdParty/stxxl/tools/CMakeLists.txt | 27 + .../stxxl/tools/benchmark_disks.cpp | 307 + .../stxxl/tools/benchmark_disks_random.cpp | 262 + .../stxxl/tools/benchmark_files.cpp | 461 + .../stxxl/tools/benchmark_pqueue.cpp | 345 + .../ThirdParty/stxxl/tools/benchmark_sort.cpp | 228 + .../stxxl/tools/benchmarks/CMakeLists.txt | 75 + .../ThirdParty/stxxl/tools/benchmarks/README | 19 + .../stxxl/tools/benchmarks/app_config.h | 148 + .../benchmarks/benchmark_naive_matrix.cpp | 102 + .../benchmarks/berkeley_db_benchmark.cpp | 1260 ++ .../tools/benchmarks/matrix_benchmark.cpp | 177 + .../stxxl/tools/benchmarks/monotonic_pq.cpp | 395 + .../stxxl/tools/benchmarks/pq_benchmark.cpp | 260 + .../tools/benchmarks/stack_benchmark.cpp | 228 + .../tools/benchmarks/tpie_stack_benchmark.cpp | 172 + .../ThirdParty/stxxl/tools/create_files.cpp | 271 + .../stxxl/tools/extras/CMakeLists.txt | 15 + .../tools/extras/benchmark_disk_and_flash.cpp | 161 + .../tools/extras/iobench_scatter_in_place.cpp | 178 + .../stxxl/tools/extras/pq_param.cpp | 70 + .../MQF/ThirdParty/stxxl/tools/mallinfo.cpp | 92 + .../MQF/ThirdParty/stxxl/tools/mlock.cpp | 74 + .../MQF/ThirdParty/stxxl/tools/stxxl_tool.cpp | 162 + third-party/MQF/bufferedMQF.cpp | 143 + third-party/MQF/bufferedMQF.h | 123 + third-party/MQF/catch.hpp | 12852 ++++++++++++++++ third-party/MQF/doxygenConfigFile | 2427 +++ third-party/{mqf/gqf.cc => MQF/gqf.c} | 792 +- third-party/{mqf => MQF}/gqf.h | 19 +- third-party/MQF/main.c | 65 + third-party/MQF/onDiskMQF.cpp | 3180 ++++ third-party/MQF/onDiskMQF.h | 520 + third-party/MQF/r_eqn.gif | Bin 0 -> 435 bytes third-party/MQF/stxxlDiskConf.stxxl | 1 + third-party/MQF/test.cpp | 2 + third-party/MQF/tests/CountingTests.cpp | 680 + .../MQF/tests/HighLevelFunctionsTests.cpp | 703 + third-party/MQF/tests/IOTests.cpp | 230 + .../MQF/tests/LayeredCountingTests.cpp | 484 + .../MQF/tests/bufferedCountingTests.cpp | 685 + third-party/MQF/tests/lowLevelTests.hpp | 256 + third-party/MQF/tests/onDiskCountingTests.cpp | 490 + third-party/MQF/tests/tagTests.cpp | 456 + third-party/{mqf => MQF}/utils.cpp | 0 third-party/{mqf => MQF}/utils.h | 0 third-party/mqf/Makefile | 89 - third-party/mqf/README.md | 109 - 510 files changed, 135856 insertions(+), 537 deletions(-) create mode 100644 third-party/MQF/.codecov.yml create mode 100644 third-party/MQF/.gitignore create mode 100644 third-party/MQF/.travis.yml rename third-party/{mqf/LICENSE => MQF/LICENSE.txt} (90%) create mode 100644 third-party/MQF/LayeredMQF.cpp create mode 100644 third-party/MQF/LayeredMQF.h create mode 100644 third-party/MQF/Makefile create mode 100644 third-party/MQF/QuotientFilter_MQF.png create mode 100644 third-party/MQF/README.md create mode 100644 third-party/MQF/ThirdParty/stxxl/.travis.yml create mode 100644 third-party/MQF/ThirdParty/stxxl/AUTHORS create mode 100644 third-party/MQF/ThirdParty/stxxl/CHANGELOG create mode 100644 third-party/MQF/ThirdParty/stxxl/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/INSTALL create mode 100644 third-party/MQF/ThirdParty/stxxl/LICENSE_1_0.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/README create mode 100644 third-party/MQF/ThirdParty/stxxl/TODO create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/DoxygenLayout.xml create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/coding_style.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/common.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/design.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/doxygen-extra.css create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/faq.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.pdf create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.xmi create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_boost_cmake.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_boost_msvc10.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_cmake.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_msvc11.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.pdf create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.svg create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_merging.pdf create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_merging.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_merging_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_runformation.pdf create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_runformation.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_runformation_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/pdm.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/pdm.svg create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/pdm_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/pipeline_randomgraph.pdf create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/pipeline_randomgraph.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/pipeline_randomgraph_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/san00b_pqueue.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/san00b_pqueue.svg create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/san00b_pqueue_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/simple_logo.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/simple_logo.svg create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture.pdf create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture_small.png create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/install.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/introduction.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/mainpage.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/references.bib create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/stxxl_tool.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_deque.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_map.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_matrix.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_pqueue.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_queue.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_sequence.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_sorter.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_stack.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_stream.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_unordered_map.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_billing.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_buf.dox create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/algo/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/algo/copy_and_sort_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills_genlog.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/algo/sort_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/applications/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/applications/skew3-lcp.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/applications/skew3.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/common/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/common/cmdline.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/copy_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/deque1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/deque2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/map1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/matrix1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/queue1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/queue2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/sequence1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/sorter1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/sorter2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/stack1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/stack2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/unordered_map1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/vector1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/vector2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/containers/vector_buf.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/stream/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/examples/stream/stream1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/algorithm create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/aligned_alloc create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/all create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/adaptor.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/async_schedule.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/inmemsort.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/intksort.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/ksort.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/losertree.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/random_shuffle.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/run_cursor.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/scan.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_base.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_helper.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/stable_ksort.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/addressable_queues.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/aligned_alloc.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/binary_buffer.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/cmdline.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/condition_variable.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/counting_ptr.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/custom_stats.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/error_handling.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exceptions.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exithandler.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/external_shared_ptr.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_heap.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_sorted.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/log.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/mutex.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/new_alloc.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/onoff_switch.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/rand.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/seed.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/semaphore.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/settings.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/simple_vector.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/state.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/swap_vector.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/timer.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tmeta.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tuple.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/types.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/uint_types.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/utils.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/winner_tree.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/hash_map.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/type_traits.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/unique_ptr.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/config.h.in create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/btree.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator_map.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/leaf.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node_cache.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/root_node.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/deque.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/block_cache.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/hash_map.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator_map.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/tuning.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/util.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/map.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_arithmetic.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_low_level.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pager.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/parallel_priority_queue.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_ext_merger.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_helpers.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_int_merger.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_losertree.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_mergers.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/priority_queue.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/queue.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sequence.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sorter.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/stack.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/unordered_map.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/vector.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/defines.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/deprecated.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/boostfd_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/completion_handler.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/create_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queued_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queues.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/fileperblock_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/io.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/iostats.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_queue.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_request.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mem_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mmap_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_interface.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_operations.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_1q.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_qwqr.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_worker.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_state.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_waiters.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/serving_request.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/simdisk_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/syscall_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/ufs_file_base.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wbtl_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wfs_file_base.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wincall_file.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/libstxxl.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/adaptor.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/bid.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc_interleaved.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_manager.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_prefetcher.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_scheduler.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream_reverse.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_ostream.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_writer.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/config.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/disk_allocator.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/prefetch_pool.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/read_write_pool.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/typed_block.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/write_pool.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/msvc_compatibility.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/namespace.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/noncopyable.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/base.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/compiletime_settings.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/equally_split.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/losertree.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/merge.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiseq_selection.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiway_merge.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiway_mergesort.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/settings.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/tags.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/timing.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/types.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/singleton.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/stream/choose.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/stream/sort_stream.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/stream/sorted_runs.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/stream/stream.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/stream/unique.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/unused.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/utils/malloc.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/utils/malloc_count.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/utils/stacktrace.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/verbose.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/version.h create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/cmdline create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/deque create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/io create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/ksort create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/mallocstats create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/map create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/mng create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/priority_queue create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/queue create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/random create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/random_shuffle create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/request create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/scan create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/sequence create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/sort create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/sorter create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/stable_ksort create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/stack create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/stats create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/stream create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/timer create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/types create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/unordered_map create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/vector create mode 100644 third-party/MQF/ThirdParty/stxxl/include/stxxl/version.h create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/algo/async_schedule.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/cmdline.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/exithandler.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/log.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/rand.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/seed.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/utils.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/verbose.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/common/version.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/boostfd_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/create_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/disk_queued_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/fileperblock_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/iostats.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/linuxaio_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/linuxaio_queue.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/linuxaio_request.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/mem_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/mmap_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/request.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/request_queue_impl_1q.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/request_queue_impl_qwqr.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/request_queue_impl_worker.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/request_with_state.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/request_with_waiters.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/serving_request.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/simdisk_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/syscall_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/ufs_file_base.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/ufs_platform.h create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/wbtl_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/wfs_file_base.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/io/wincall_file.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/libstxxl.symbols create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/mng/block_manager.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/mng/config.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/mng/disk_allocator.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/lib/utils/malloc_count.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/local/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/local/test1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/local/test2.cpp create mode 100755 third-party/MQF/ThirdParty/stxxl/misc/analyze-include-dependencies.pl create mode 100755 third-party/MQF/ThirdParty/stxxl/misc/analyze-source.pl create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/cmake/TestFileOffsetBits.c create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/cmake/TestLargeFiles.c.cmakein create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/cmake/TestLargeFiles.cmake create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/cmake/TestWindowsFSeek.c create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/cmake/stxxl-config.cmake.in create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/cmake/stxxl-version.cmake.in create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/cmake/stxxl.pc create mode 100755 third-party/MQF/ThirdParty/stxxl/misc/concat-lines create mode 100755 third-party/MQF/ThirdParty/stxxl/misc/diskbench-avgdat.sh create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/diskbench.mk create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/do-release.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/fileheader.txt create mode 100755 third-party/MQF/ThirdParty/stxxl/misc/floating-average create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/iostat-plot.mk create mode 100755 third-party/MQF/ThirdParty/stxxl/misc/record-load-iostat create mode 100755 third-party/MQF/ThirdParty/stxxl/misc/remove-unless create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/uncrustify.cfg create mode 100644 third-party/MQF/ThirdParty/stxxl/misc/valgrind.supp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_asch.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_bad_cmp.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_ksort.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_ksort_all_parameters.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_parallel_sort.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_random_shuffle.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_scan.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_sort.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_sort_all_parameters.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_sort_all_parameters.h create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_stable_ksort.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/algo/test_stable_ksort_all_parameters.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_binary_buffer.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_cmdline.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_counting_ptr.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_external_shared_ptr.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_globals.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_log2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_manyunits.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_manyunits2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_random.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_swap_vector.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_tuple.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_uint_types.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/common/test_winner_tree.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/btree/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/btree/test_btree.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/btree/test_btree_const_scan.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/btree/test_btree_insert_erase.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/btree/test_btree_insert_find.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/btree/test_btree_insert_scan.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/hash_map/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/hash_map/test_hash_map.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/hash_map/test_hash_map_block_cache.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/hash_map/test_hash_map_iterators.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/hash_map/test_hash_map_reader_writer.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/map_test_handlers.h create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/ppq/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/ppq/benchmark_pqs.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/ppq/test_ppq.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/ppq/test_ppq_arrays_and_iterator.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_deque.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_ext_merger.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_ext_merger2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_iterators.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_many_stacks.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_map.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_map_random.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_matrix.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_migr_stack.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_pqueue.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_queue.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_queue2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_sequence.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_sorter.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_stack.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_vector.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_vector_buf.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_vector_export.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_vector_resize.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/containers/test_vector_sizes.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/io/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/io/test_cancel.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/io/test_io.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/io/test_io_sizes.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/io/test_mmap.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/io/test_sim_disk.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_aligned.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_block_alloc_strategy.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_block_manager.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_block_manager1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_block_manager2.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_block_scheduler.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_bmlayer.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_buf_streams.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_config.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_pool_pair.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_prefetch_pool.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_read_write_pool.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/mng/test_write_pool.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/parallel/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/parallel/bench_multiway_merge.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/parallel/bench_multiway_merge.plot create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/parallel/test_multiway_merge.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/parallel/test_multiway_mergesort.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/test_loop.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/test_materialize.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/test_naive_transpose.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/test_push_sort.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/test_sorted_runs.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/test_stream.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tests/stream/test_stream1.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmark_disks.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmark_disks_random.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmark_files.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmark_pqueue.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmark_sort.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/README create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/app_config.h create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/benchmark_naive_matrix.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/berkeley_db_benchmark.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/matrix_benchmark.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/monotonic_pq.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/pq_benchmark.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/stack_benchmark.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/benchmarks/tpie_stack_benchmark.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/create_files.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/extras/CMakeLists.txt create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/extras/benchmark_disk_and_flash.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/extras/iobench_scatter_in_place.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/extras/pq_param.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/mallinfo.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/mlock.cpp create mode 100644 third-party/MQF/ThirdParty/stxxl/tools/stxxl_tool.cpp create mode 100644 third-party/MQF/bufferedMQF.cpp create mode 100644 third-party/MQF/bufferedMQF.h create mode 100644 third-party/MQF/catch.hpp create mode 100644 third-party/MQF/doxygenConfigFile rename third-party/{mqf/gqf.cc => MQF/gqf.c} (84%) rename third-party/{mqf => MQF}/gqf.h (93%) create mode 100644 third-party/MQF/main.c create mode 100644 third-party/MQF/onDiskMQF.cpp create mode 100644 third-party/MQF/onDiskMQF.h create mode 100644 third-party/MQF/r_eqn.gif create mode 100644 third-party/MQF/stxxlDiskConf.stxxl create mode 100644 third-party/MQF/test.cpp create mode 100644 third-party/MQF/tests/CountingTests.cpp create mode 100644 third-party/MQF/tests/HighLevelFunctionsTests.cpp create mode 100644 third-party/MQF/tests/IOTests.cpp create mode 100644 third-party/MQF/tests/LayeredCountingTests.cpp create mode 100644 third-party/MQF/tests/bufferedCountingTests.cpp create mode 100644 third-party/MQF/tests/lowLevelTests.hpp create mode 100644 third-party/MQF/tests/onDiskCountingTests.cpp create mode 100644 third-party/MQF/tests/tagTests.cpp rename third-party/{mqf => MQF}/utils.cpp (100%) rename third-party/{mqf => MQF}/utils.h (100%) delete mode 100644 third-party/mqf/Makefile delete mode 100644 third-party/mqf/README.md diff --git a/Makefile b/Makefile index 0bc0809209..c3096111da 100644 --- a/Makefile +++ b/Makefile @@ -131,7 +131,7 @@ clean: FORCE cd src/oxli && $(MAKE) clean || true cd tests && rm -rf khmertest_* || true rm -f pytests.xml - cd third-party/mqf && make clean || true + cd third-party/MQF && make clean || true rm -f $(EXTENSION_MODULE) rm -f khmer/*.pyc scripts/*.pyc tests/*.pyc oxli/*.pyc \ sandbox/*.pyc khmer/__pycache__/* sandbox/__pycache__/* \ diff --git a/include/oxli/storage.hh b/include/oxli/storage.hh index 2759b88a34..13335c604d 100644 --- a/include/oxli/storage.hh +++ b/include/oxli/storage.hh @@ -424,7 +424,7 @@ public: // Final argument is the number of bits allocated for the value, which // we do not use. _supports_bigcount = true; - qf_init(&mf, (1ULL << size), size+slotSize, 0,2,true,"",2038074761); + qf_init(&mf, (1ULL << size), size+slotSize, 0,2,0,true,"",2038074761); diff --git a/khmer/_oxli/graphs.pyx b/khmer/_oxli/graphs.pyx index 022feb5df6..85a918882c 100644 --- a/khmer/_oxli/graphs.pyx +++ b/khmer/_oxli/graphs.pyx @@ -386,7 +386,7 @@ cdef class QFCounttable(Hashtable): @classmethod def load(cls, file_name): """Load the graph from the specified file.""" - cdef QFCounttable table = cls(1, 1) + cdef QFCounttable table = cls(1, 1,1) deref(table._qf_this).load(_bstring(file_name)) return table diff --git a/setup.cfg b/setup.cfg index 18b56b6859..58e257ba59 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ undef = NO_UNIQUE_RC # docker/Dockerfile # libraries = z,bz2 ## if using system libraries -include-dirs = include:third-party/zlib:third-party/bzip2:third-party/seqan/core/include:third-party/smhasher:third-party/rollinghash:third-party/mqf +include-dirs = include:third-party/zlib:third-party/bzip2:third-party/seqan/core/include:third-party/smhasher:third-party/rollinghash:third-party/MQF # include-dirs = lib ## if using system libraries (broken) diff --git a/setup.py b/setup.py index ba1b9cf193..07ddf2df59 100755 --- a/setup.py +++ b/setup.py @@ -330,12 +330,10 @@ def run(self): if sys.platform == 'darwin' and 'gcov' in self.libraries: self.libraries.remove('gcov') - mqfcmd = ['bash', '-c', 'cd third-party/mqf && make'] + mqfcmd = ['bash', '-c', 'cd third-party/MQF && make all NH=1'] spawn(cmd=mqfcmd, dry_run=self.dry_run) for ext in self.extensions: - ext.extra_objects.append(path_join("third-party", "mqf", "gqf.o")) - ext.extra_objects.append(path_join("third-party", "mqf", - "utils.o")) + ext.extra_objects.append(path_join("third-party", "MQF", "libMQF.a")) if "z" not in self.libraries: zcmd = ['bash', '-c', 'cd ' + ZLIBDIR + ' && ( test Makefile -nt' diff --git a/src/oxli/Makefile b/src/oxli/Makefile index 610132b687..85231c70d3 100644 --- a/src/oxli/Makefile +++ b/src/oxli/Makefile @@ -223,8 +223,8 @@ BZIP2_OBJS_BASE= \ BZIP2_OBJS=$(addprefix $(BZIP2_DIR)/, $(BZIP2_OBJS_BASE)) # Counting bloom filter -MQF_DIR=../../third-party/mqf -MQF_OBJS_BASE= gqf.o utils.o +MQF_DIR=../../third-party/MQF +MQF_OBJS_BASE= libMQF.a MQF_OBJS=$(addprefix $(MQF_DIR)/, $(MQF_OBJS_BASE)) diff --git a/tests/table_fixtures.py b/tests/table_fixtures.py index 1afdd89356..6d26a08f09 100755 --- a/tests/table_fixtures.py +++ b/tests/table_fixtures.py @@ -58,7 +58,7 @@ def build(k, *args): if tabletype is QFCounttable: qf_size = 2**math.ceil(math.log(starting_size, 2)) - return tabletype(k, qf_size) + return tabletype(k, qf_size,8) else: return tabletype(k, starting_size, n_tables) diff --git a/tests/test_qfstorage.py b/tests/test_qfstorage.py index 4c1eae80f0..1e5c2e3468 100755 --- a/tests/test_qfstorage.py +++ b/tests/test_qfstorage.py @@ -30,7 +30,7 @@ def getSketch(request): def test_count_1(getSketch): print("start") - hi = getSketch(12, sketchSize) + hi = getSketch(12, sketchSize,8) kmer = 'G' * 12 hashval = hi.hash('G' * 12) @@ -53,7 +53,7 @@ def test_count_1(getSketch): def test_count_2(getSketch): - hi = getSketch(12, sketchSize) + hi = getSketch(12, sketchSize,8) print("done") kmer = 'G' * 12 hashval = hi.hash('G' * 12) @@ -74,7 +74,7 @@ def test_read_write(getSketch): print("Start") fname = str.encode(utils.get_temp_filename('zzz')) rng = random.Random(1) - ctm = getSketch(20, sketchSize) + ctm = getSketch(20, sketchSize,8) kmers = ["".join(rng.choice("ACGT") for _ in range(20)) for n in range(400)] @@ -96,7 +96,7 @@ def test_read_write(getSketch): def test_maxcount_with_bigcount(getSketch): # hashtable should not saturate, if use_bigcount is set. - kh = getSketch(4, 128) + kh = getSketch(4, 128,8) last_count = None for _ in range(0, 10000): @@ -112,7 +112,7 @@ def test_maxcount_with_bigcount(getSketch): def test_get_ksize(getSketch): - kh = getSketch(22, 16) + kh = getSketch(22, 16,8) assert kh.ksize() == 22 diff --git a/third-party/MQF/.codecov.yml b/third-party/MQF/.codecov.yml new file mode 100644 index 0000000000..9ebb4cbd14 --- /dev/null +++ b/third-party/MQF/.codecov.yml @@ -0,0 +1,4 @@ +ignore: + - main.c + - catch.hpp + - test.h diff --git a/third-party/MQF/.gitignore b/third-party/MQF/.gitignore new file mode 100644 index 0000000000..e098bd2396 --- /dev/null +++ b/third-party/MQF/.gitignore @@ -0,0 +1,19 @@ + +*.gcda + +*.gcno + +mqf_test + +tmp\.ser + +*.o + +main + +docs/ + +libgqf\.so +ThirdParty/stxxl/build/* + +libMQF.a diff --git a/third-party/MQF/.travis.yml b/third-party/MQF/.travis.yml new file mode 100644 index 0000000000..ba7e33108b --- /dev/null +++ b/third-party/MQF/.travis.yml @@ -0,0 +1,23 @@ +language: cpp +sudo: required +dist: trusty +compiler: +- g++ +os: +- linux + + +before_install: + - sudo apt-get -qq update + - sudo apt-get install -y make automake autotools-dev +script: + - make all test NH=1 + - ./mqf_test + - gcov -n -o . gqf.c > /dev/null; +branches: + only: + - mqfDevelopmenet + - master + +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/third-party/mqf/LICENSE b/third-party/MQF/LICENSE.txt similarity index 90% rename from third-party/mqf/LICENSE rename to third-party/MQF/LICENSE.txt index a2e8301ed8..09d493bf1f 100644 --- a/third-party/mqf/LICENSE +++ b/third-party/MQF/LICENSE.txt @@ -1,7 +1,6 @@ BSD 3-Clause License -Copyright (c) 2017, Rob Johnson and Prahsant Pandey -Copyright (c) 2017, Mostafa Shokrof and Tamer Mansour +Copyright (c) 2017, All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/third-party/MQF/LayeredMQF.cpp b/third-party/MQF/LayeredMQF.cpp new file mode 100644 index 0000000000..91abdf3226 --- /dev/null +++ b/third-party/MQF/LayeredMQF.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include "gqf.h" +#include "LayeredMQF.h" +#include + +using namespace std; +void layeredMQF_init(layeredMQF *qf, uint64_t nslots_singletons ,uint64_t nslots + , uint64_t key_bits, uint64_t value_bits,uint64_t fixed_counter_size, bool mem + , const char *path, uint32_t seed){ + + if(qf==NULL) + { + qf= new layeredMQF(); + } + + qf_init(qf->firstLayer_singletons,nslots_singletons,key_bits,value_bits,1,0,mem,path,seed); + qf_init(qf->secondLayer,nslots,key_bits,value_bits,fixed_counter_size,0,mem,path,seed); +} + +void layeredMQF_reset(layeredMQF *qf){ + qf_reset(qf->firstLayer_singletons); + qf_reset(qf->secondLayer); +} + +void layeredMQF_destroy(layeredMQF *qf){ + qf_destroy(qf->firstLayer_singletons); + qf_destroy(qf->secondLayer); +} + +void layeredMQF_copy(layeredMQF *dest, layeredMQF *src){ + qf_copy(dest->firstLayer_singletons,src->firstLayer_singletons); + qf_copy(dest->secondLayer,src->secondLayer); +} + +/* Increment the counter for this key/value pair by count. */ + +bool layeredMQF_insert(layeredMQF *qf, uint64_t key, uint64_t count, + bool lock, bool spin){ + if(count==0) + return true; + bool inSecondLayer=qf_count_key(qf->secondLayer,key)>0; + if(inSecondLayer) + { + return qf_insert(qf->secondLayer,key,count,lock,spin); + } + uint64_t CountinFirstLayer=qf_count_key(qf->firstLayer_singletons,key); + if(CountinFirstLayer>1) + { + cerr<<"First Layer has items > 1"<0) + { + if(qf_remove(qf->firstLayer_singletons,key,CountinFirstLayer,lock,spin)) + return qf_insert(qf->secondLayer,key,count+1,lock,spin); + else{ + return false; + } + } + if(count==1) + return qf_insert(qf->firstLayer_singletons,key,count,lock,spin); + else + return qf_insert(qf->secondLayer,key,count,lock,spin); + + +} + +/* Remove count instances of this key/value combination. */ +bool LayeredMQF_remove(layeredMQF *qf, uint64_t hash, uint64_t count, bool lock, bool spin){ + bool res=false; + res|=qf_remove(qf->firstLayer_singletons,hash,count,lock,spin); + res|=qf_remove(qf->secondLayer,hash,count,lock,spin); + return res; +} + + + + +/* Return the number of times key has been inserted, with any value, + into qf. */ +uint64_t layeredMQF_count_key(const layeredMQF *qf, uint64_t key){ + uint64_t res=qf_count_key(qf->secondLayer,key); + if(res==0) + { + res=qf_count_key(qf->firstLayer_singletons,key); + } + return res; +} + +int layeredMQF_space(layeredMQF *qf){ + return max(qf_space(qf->firstLayer_singletons),qf_space(qf->secondLayer)); +} +// +// /* Initialize an iterator */ +// bool layeredMQF_iterator(layeredMQF *qf, layeredMQFIterator* qfi, uint64_t position){ +// if(qfi ==NULL) +// qfi=new layeredMQFIterator(); +// bool res=false; +// res|=qf_iterator(qf->firstLayer_singletons,qfi->firstLayerIterator,position); +// res|=qf_iterator(qf->secondLayer,qfi->secondLayerIterator,position); +// return res; +// } +// +// /* Returns 0 if the iterator is still valid (i.e. has not reached the +// end of the QF. */ +// int layeredMQF_qfi_get(layeredMQFIterator*qfi, uint64_t *key, uint64_t *value, uint64_t *count){ +// +// } +// +// /* Advance to next entry. Returns whether or not another entry is +// found. */ +// int layeredMQF_qfi_next(layeredMQFIterator*qfi); +// +// /* Check to see if the if the end of the QF */ +// int layeredMQF_qfi_end(layeredMQFIterator*qfi); +// +// /* For debugging */ +// void layeredMQF_dump(const layeredMQF *); +// +// /* write data structure of to the disk */ +// void layeredMQF_serialize(const layeredMQF *qf, const char *filename); +// +// /* read data structure off the disk */ +// void layeredMQF_deserialize(layeredMQF *qf, const char *filename); +// +// /* mmap the QF from disk. */ +// void layeredMQF_read(layeredMQF *qf, const char *path); + +/* merge two QFs into the third one. */ +//void layeredMQF_merge(layeredMQF *layeredMQFa, layeredMQF *layeredMQFb, QF *qfc); + +/* merge multiple QFs into the final QF one. */ +//void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr); + +/* find cosine similarity between two QFs. */ +// uint64_t qf_inner_product(QF *qfa, QF *qfb); + +/* magnitude of a QF. */ +//uint64_t qf_magnitude(QF *qf); diff --git a/third-party/MQF/LayeredMQF.h b/third-party/MQF/LayeredMQF.h new file mode 100644 index 0000000000..40c5c0341a --- /dev/null +++ b/third-party/MQF/LayeredMQF.h @@ -0,0 +1,136 @@ +#ifndef layeredMQF_H +#define layeredMQF_H + +#include +#include +#include +#include "gqf.h" +#ifdef __cplusplus +extern "C" { +#endif + + + typedef class layeredMQF { + public: + QF* firstLayer_singletons; + QF* secondLayer; + layeredMQF(){ + firstLayer_singletons=new QF(); + secondLayer=new QF(); + } + ~layeredMQF() + { + delete firstLayer_singletons; + delete secondLayer; + } + } layeredMQF; + + + + + typedef struct layeredMQFIterator { + QFi firstLayerIterator; + QFi secondLayerIterator; + } layeredMQF_iterator; + + + void layeredMQF_init(layeredMQF *qf, uint64_t nslots_singletons ,uint64_t nslots, uint64_t key_bits, uint64_t value_bits,uint64_t fixed_counter_size, bool mem, const char *path, uint32_t seed); + + void layeredMQF_reset(layeredMQF *qf); + + void layeredMQF_destroy(layeredMQF *qf); + + void layeredMQF_copy(layeredMQF *dest, layeredMQF *src); + + /* Increment the counter for this key/value pair by count. */ + bool layeredMQF_insert(layeredMQF *qf, uint64_t key, uint64_t count, + bool lock, bool spin); + + /* Remove count instances of this key/value combination. */ + bool layeredMQF_remove(QF *qf, uint64_t hash, uint64_t count, bool lock=false, bool spin=false); + + + /*! + @breif Add Tag to item. + + @param Qf* qf : pointer to the Filter + @param uint64_t key : hash of the item to be insertedItems + @param uint64_t tag: tag to be added + @param bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + @param bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + + @return bool: True if the item is inserted correctly. + */ + uint64_t layeredMQF_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock=false, bool spin=false); + /*! + @breif Return the tag associated with a given item. + + @param Qf* qf : pointer to the Filter. + @param uint64_t key : hash of the item. + + @return uint64_t the tag associated with the input key. + */ + uint64_t layeredMQF_get_tag(const QF *qf, uint64_t key); + /*! + @breif delete the tag associated with a given item. + + @param Qf* qf : pointer to the Filter. + @param uint64_t key : hash of the item. + + @return bool: Returns true if the item is removed successfully. + */ + uint64_t layeredMQF_remove_tag(const QF *qf, uint64_t key, bool lock=false, bool spin=false); + + + + /* Return the number of times key has been inserted, with any value, + into qf. */ + uint64_t layeredMQF_count_key(const layeredMQF *qf, uint64_t key); + + + + /* Initialize an iterator */ + bool layeredMQF_qf_iterator(layeredMQF *qf, layeredMQFIterator *qfi, uint64_t position); + + /* Returns 0 if the iterator is still valid (i.e. has not reached the + end of the QF. */ + int layeredMQF_qfi_get(layeredMQFIterator *qfi, uint64_t *key, uint64_t *value, uint64_t *count); + + /* Advance to next entry. Returns whether or not another entry is + found. */ + int layeredMQF_qfi_next(layeredMQFIterator *qfi); + + /* Check to see if the if the end of the QF */ + int layeredMQF_qfi_end(layeredMQFIterator *qfi); + + /* For debugging */ + void layeredMQF_dump(const layeredMQF *); + + /* write data structure of to the disk */ + void layeredMQF_serialize(const layeredMQF *qf, const char *filename); + + /* read data structure off the disk */ + void layeredMQF_deserialize(layeredMQF *qf, const char *filename); + + /* mmap the QF from disk. */ + void layeredMQF_read(layeredMQF *qf, const char *path); + + /* merge two QFs into the third one. */ + //void layeredMQF_merge(layeredMQF *layeredMQFa, layeredMQF *layeredMQFb, QF *qfc); + + /* merge multiple QFs into the final QF one. */ + //void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr); + + /* find cosine similarity between two QFs. */ +// uint64_t qf_inner_product(QF *qfa, QF *qfb); + + /* magnitude of a QF. */ + //uint64_t qf_magnitude(QF *qf); + + int layeredMQF_space(layeredMQF *qf); + +#ifdef __cplusplus +} +#endif + +#endif /* layeredMQF_H */ diff --git a/third-party/MQF/Makefile b/third-party/MQF/Makefile new file mode 100644 index 0000000000..911f73fbcd --- /dev/null +++ b/third-party/MQF/Makefile @@ -0,0 +1,90 @@ +TARGETS=main libMQF.a +TESTFILES = tests/CountingTests.o tests/HighLevelFunctionsTests.o tests/IOTests.o tests/tagTests.o tests/bufferedCountingTests.o tests/onDiskCountingTests.o + +ifdef D + DEBUG=-g + OPT= +else + DEBUG= + OPT=-Ofast +endif + +ifdef NH + ARCH= +else + ARCH=-msse4.2 -D__SSE4_2_ +endif + +ifdef P + PROFILE=-pg -no-pie # for bug in gprof. +endif + +CXX = g++ -std=c++11 +CC = g++ -std=c++11 +LD= g++ -std=c++11 + +INCLUDE= -I ThirdParty/stxxl/include/ -I ThirdParty/stxxl/build/include/ + +CXXFLAGS = -fPIC -Wall $(DEBUG) $(PROFILE) $(OPT) $(ARCH) $(INCLUDE) -fpermissive -fopenmp -m64 -I. -Wno-unused-result -Wno-strict-aliasing -Wno-unused-function -Wno-sign-compare + +#STXXL= -L ThirdParty/stxxl/build/lib/ -llibstxxl +STXXL= ThirdParty/stxxl/build/lib/libstxxl.a + +LDFLAGS = -fopenmp $(DEBUG) $(PROFILE) $(OPT) + +# +# declaration of dependencies +# + +all: $(TARGETS) + +OBJS= gqf.o utils.o bufferedMQF.o onDiskMQF.o + + +# dependencies between programs and .o files + +main: main.o $(STXXL) $(OBJS) + $(LD) $^ $(LDFLAGS) -o $@ $(STXXL) +# dependencies between .o files and .h files + +libgqf.so: $(OBJS) + $(LD) $^ $(LDFLAGS) --shared -o $@ + +test: $(TESTFILES) gqf.c test.o utils.o + $(LD) $(LDFLAGS) -DTEST -o mqf_test test.o LayeredMQF.o bufferedMQF.o onDiskMQF.o utils.o $(TESTFILES) gqf.c $(STXXL) + +main.o: gqf.h + +libMQF.a: $(STXXL) $(OBJS) + ar rcs libMQF.a $(OBJS) $(STXXL) +# dependencies between .o files and .cc (or .c) files + +$(STXXL): + mkdir -p ThirdParty/stxxl/build + cd ThirdParty/stxxl/build && cmake DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./ .. + cd ThirdParty/stxxl/build && make all install + + +gqf.o: gqf.c gqf.h + + +bufferedMQF.o: bufferedMQF.cpp bufferedMQF.h + + + + + +%.o: %.cc + $(CXX) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ + +%.o: %.c %.h + $(CC) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ + +%.o: %.cpp %.hpp + $(CXX) $(CXXFLAGS) $(INCLUDE) $< -c -o $@ + + + + +clean: + rm -f $(OBJS) $(TARGETS) $(TESTS) $(TESTFILES) diff --git a/third-party/MQF/QuotientFilter_MQF.png b/third-party/MQF/QuotientFilter_MQF.png new file mode 100644 index 0000000000000000000000000000000000000000..669ee0e7c5dcdb4184bf30cbd171d1d48603b0cf GIT binary patch literal 46950 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 z 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 !3G?NdMLflClQ29%M@k#Dr_mlX2g^5}ac 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 0. + * bool mem: Flag to create the filter on memory. IF false, mmap is used. + * const char * path: In case of mmap. Path of the file used to pack the filter. + * uint32_t seed: useless value. To be removed +2. qf_destroy +3. estimate + +### Functions Supported +1. Insert : +Increment the counter for this item by count. + ```c++ + bool qf_insert(QF *qf, uint64_t key, + uint64_t count,bool lock, bool spin); + ``` + + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item to be inserted. + * uint64_t count: Count to be added + * bool lock: For Multithreading, Lock the * slot used by the current thread so that other threads can't change the value + * bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + * returns True if the insertion succeeded. + +2. Count: + Return the number of times key has been inserted, with any value, into qf. + ```c++ + uint64_t qf_count_key(const QF *qf, uint64_t key); + ``` + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item to be counted. + * returns the number of times the item is inserted. +3. Remove: +Decrement the counter for this item by count. +```c++ +bool qf_remove(QF *qf, uint64_t hash, uint64_t count, bool lock, bool spin); +``` + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item to be removed + * uint64_t count: Count to be removed + * bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + * bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + +4. Add/Remove tag to elements +```c++ +uint64_t qf_add_tag(const QF *qf, uint64_t key, uint64_t tag, bool lock, bool spin); +uint64_t qf_get_tag(const QF *qf, uint64_t key); +uint64_t qf_remove_tag(const QF *qf, uint64_t key, bool lock, bool spin); +``` + * Qf* qf : pointer to the Filter + * uint64_t key : hash of the item. + * uint64_t tag: tag for the item. + * bool lock: For Multithreading, Lock the slot used by the current thread so that other threads can't change the value + * bool spin: For Multithreading, If there is a lock on the target slot. wait until the lock is freed and insert the count. + +5. Resize: + resize the filter into a bigger or smaller one + ```c++ + QF* qf_resize(QF* qf, int newQ, const char * originalFilename=NULL, const char * newFilename=NULL); + ``` + * Qf* qf : pointer to the Filter + * uint64_t newQ: new number of slots(Q). the slot size will be recalculated to keep the range constant. + * string originalFilename(optional): dump the current filter to the disk to free space for the new filter. Filename is provided as the content of the string. + * string newFilename(optional): the new filter is created on disk. Filename is provided as the content of the string. + * returns a pointer to the new filter + +6. Merge: merge more than one filter into a final one. +```c++ +void qf_merge(QF *qfa, QF *qfb, QF *qfc); +void qf_multi_merge(QF *qf_arr[], int nqf, QF *qfr); +``` +7. Invertible Merge: Invertible merge offers addiotinal functionality to normal merge. Original source filter can be queried for each key. +Invertiable merge function adds tag for each key and creates index structure. The index is map of an integer and vector of integers where the integer is the value of the tags and vector on integers is the ids of the source filters. +```c++ +void qf_invertable_merge(QF *qf_arr[], int nqf, QF *qfr,std::map > *inverted_index_ptr); +``` + * Qf* qf_arr : input array of filters + * int nqf: number of filters + * QF* qfr: pointer to the output filter. + * map (uint64_t,vector(int) ) inverted_index_ptr: Pointer to the output index. + + + + +7. Compare: +check if two filters have the same items, counts and tags. +```c++ +bool qf_equals(QF *qfa, QF *qfb); +``` +8. Intersect +calculate the intersection between two filters. +```c++ +void qf_intersect(QF *qfa, QF *qfb, QF *qfc); +``` +9. Subtract +subtract the second filter from the first. +```c++ +void qf_subtract(QF *qfa, QF *qfb, QF *qfc); +``` +10. Space: +returns the space percent occupied by the inserted items. +```c++ +int qf_space(QF *qf); +``` + +### Miscellaneous Functions +1. Capacity +2. Copy +3. Serialize/ Deserialize +4. MMap read diff --git a/third-party/MQF/ThirdParty/stxxl/.travis.yml b/third-party/MQF/ThirdParty/stxxl/.travis.yml new file mode 100644 index 0000000000..ea0709dbb2 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/.travis.yml @@ -0,0 +1,37 @@ +language: cpp + +env: + global: + # limit parallel threads (default is 32!) + - OMP_NUM_THREADS=4 + matrix: + # gcc-4.8 builds + - CMAKE_CC="gcc-4.8" CMAKE_CXX="g++-4.8" CMAKE_FLAGS="" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug -DUSE_OPENMP=OFF -DNO_CXX11=ON" + - CMAKE_CC="gcc-4.8" CMAKE_CXX="g++-4.8" CMAKE_FLAGS="" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug -DUSE_OPENMP=ON -DUSE_GNU_PARALLEL=OFF" + - CMAKE_CC="gcc-4.8" CMAKE_CXX="g++-4.8" CMAKE_FLAGS="" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug -DUSE_OPENMP=ON -DUSE_GNU_PARALLEL=ON" + - CMAKE_CC="gcc-4.8" CMAKE_CXX="g++-4.8" CMAKE_FLAGS="" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release -DUSE_OPENMP=ON -DUSE_GNU_PARALLEL=OFF" + - CMAKE_CC="gcc-4.8" CMAKE_CXX="g++-4.8" CMAKE_FLAGS="" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release -DUSE_OPENMP=ON -DUSE_GNU_PARALLEL=ON" + # one boost build + - CMAKE_CC="gcc" CMAKE_CXX="g++" CMAKE_FLAGS="" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug -DUSE_GNU_PARALLEL=OFF" USE_BOOST=ON + # one 32-bit build + - CMAKE_CC="gcc" CMAKE_CXX="g++" CMAKE_FLAGS="-m32" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug -DUSE_GNU_PARALLEL=OFF" USE_BOOST=OFF + # clang build + - CMAKE_CC="clang" CMAKE_CXX="clang++" CMAKE_FLAGS="-Wno-sign-conversion" CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release" + +install: + - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + - sudo apt-get -qq update + - if [ "$CMAKE_CXX" == "g++-4.8" ]; then sudo apt-get install g++-4.8; fi + - if [ "$CMAKE_FLAGS" == "-m32" ]; then sudo apt-get install g++-multilib; fi + - if [ "$USE_BOOST" == "ON" ]; then sudo apt-get install libboost-all-dev; fi + +before_script: + - mkdir build + - cd build + - cmake -DCMAKE_C_COMPILER=$CMAKE_CC -DCMAKE_CXX_COMPILER=$CMAKE_CXX + -DBUILD_TESTS=ON -DTRY_COMPILE_HEADERS=ON + -DUSE_BOOST=$USE_BOOST $CMAKE_ARGS ../ + #-DCMAKE_C_FLAGS="-Wconversion $CMAKE_FLAGS" -DCMAKE_CXX_FLAGS="-Wconversion $CMAKE_FLAGS" + +script: + - make -j4 && ./tools/stxxl_tool info && ctest -V diff --git a/third-party/MQF/ThirdParty/stxxl/AUTHORS b/third-party/MQF/ThirdParty/stxxl/AUTHORS new file mode 100644 index 0000000000..19f1bb1375 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/AUTHORS @@ -0,0 +1,15 @@ +Andreas Beckmann +Daniel Feist +Daniel Godas-Lopez +Ilja Andronov +Jaroslaw Fedorowicz +Jens Mehnert +Johannes Singler +Manuel Krings +Markus Westphal +Peter Sanders +Raoul Steffen +Roman Dementiev +Thomas Keh +Thomas Nowak +Timo Bingmann diff --git a/third-party/MQF/ThirdParty/stxxl/CHANGELOG b/third-party/MQF/ThirdParty/stxxl/CHANGELOG new file mode 100644 index 0000000000..e0e4527329 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/CHANGELOG @@ -0,0 +1,536 @@ +Version 1.4-dev (unreleased) + +Version 1.4.1 (29 October 2014) + +* support kernel based asynchronous I/O on Linux (new file type "linuxaio"), + which exploits Native Command Queuing (NCQ) if available. + disable/enable with the define STXXL_FILE_LINUXAIO 0/1 via cmake + +* adding new disk_config entry device_id, which specifies the physical device + id of the "disk" used during prefetching sequence calculations. This used to + be identical with the queue id, however, for linuxaio there is only one + queue; thus the distinction had to be made. In a default config, no changes + are necessary, as the device_id parameter is automatically enumerated. + +* adding stxxl::binary_buffer which can be used for compact serialization and + reading via a stxxl::binary_reader cursor interface. + +* stxxl::unordered_map is a hash map, backed by external memory. It probably + only works well when lots of internal memory is used to buffer access to a + relatively small working set. Then, however, fast direct in-memory item + access can be used. + +* stxxl::external_shared_ptr is a proxy class to allow use of shared_ptr + classes inside stxxl containers. Reference counts are kept in memory, while + data may be swapped out to disk. + +* removing struct default_completion_handler, using a NULL pointer in default + complete handler instead, since otherwise a default_completion_handler + objects is cloned for _each_io_request_! Using a NULL pointer avoids + superfluous new/delete work on the heap. + +* minor changes: + - disable TPIE benchmarks by default, removing a warning. + - compilation and tests work under MinGW 64-bit with special threads. + - fixed compilation on 32-bit systems, everything is -Wconversion safe. + - adding 32-bit and 64-bit cmdline_parser::add_bytes() variants. + - removing all double underscores. + - use atomic_counted_object in class file for request reference counting. + - adding local/test2.cpp containing a stxxl::sorter example. + +Version 1.4.0 (12 December 2013) + +* Reorganized Directory Hierarchy + - separating old component based directories into lib/ (libstxxl source + files), tests/ (for tests cases), examples/ (for tutorial and example code) + and tools/ (for stxxl_tool and more benchmarks). + +* CMake build system for cross-platform compilation + - replaced adhoc Makefiles with CMake build system + - CMake scripts can also output MSVC project files and more. + - all component tests must register with CMake's test system -> run make test + - generates pkg-config and CMake's project config files + +* Greatly Improved Doxygen Documentation + - integrated old LaTeX tutorial + - newly written tutorials for almost all STXXL containers and algorithms + - embedded design documentation from Roman Dementiev's PhD thesis + - new compilation instructions for CMake build system + +* Improved .stxxl Config Options for File I/O Implementations + - looking for .stxxl in more places than just the current directory (see the + docs for which places exactly). + - parse file size with SI and IEC units, like "10GiB" in disk= config lines. + - parse additional file I/O implementation options. + - support for "unlink_on_open" (keep file descriptor but unlink file) and + "delete_at_exit" options. + - support for "direct=off/try/on" to turn off O_DIRECT for tmpfs and similar. + - the default open mode is now direct=try -> prints a warning if O_DIRECT is + unavailable on the system. + +* .stxxl Config Options Represented as disk_config Class + - all disk= config lines are parsed into disk_config objects. + - allow API calls to configure disks via stxxl::config::add_disk() + - delay config file load till config::initialize() is called by block_manager + +* Command line parser stxxl::cmdline_parser + - stand-alone command line parser with ability to parse SI and IEC units for + "bytes sizes". + - automatically output nicely formatted help for all options and flags. + - using the cmdline_parser for all stxxl_tool subtools. + +* Created stxxl_tool from multiple small tools and benchmarks + - stxxl_tool is a collection of subtools, previous scattered around in the + directory hierarchy. + - goal is to have one tool installed in binary distribution together with + libstxxl library and includes. + - primary tool for standardized platform speed tests. + - provide good command line help on these subtools via cmdline_parser. + +* Adding skew3 as "real-world" STXXL application to examples subdirectory. + - enhanced integrated stxxl::tuple construction for this application. + +* Enable use of raw block devices on Linux + - these do provide more stable I/O throughput than even XFS. + - disable growing of these devices + +* Most platform configuration is now done by CMake and saved into the + header file + +* Enhanced stxxl::block_manager to count maximum, current and total number of + bytes allocated in external memory. + +* Support Visual Studio 2012 and 2013 _without_ Boost by using std::thread + - VS 2012 and 2013 support STL threads, mutexes, condition_variables and + more, which are basically modeled after the Boost libraries. Thus we can + simply uses these included libraries instead of Boost. + - Fixed bad runtime deadlock bug occurring when joining std::threads after + main() exits. The bug persists in VS 2012 and 2013. + +* Implementing counting_ptr reference counted objects + - used like boost::intrusive_ptr with a base class. + - reimplementing request_ptr using just counting_ptr. + - (optionally) use atomic operations for thread-safe reference counting + - applying counting_ptr to stream::sorted_runs to avoid extra copying + +* New STXXL container "stxxl::sequence" + - basically a deque container without random access to blocks. + - efficient {push/pop}_{front/back} at both ends. + - access to block sequence via stream and reverse_stream constructions + +* New STXXL container "stxxl::sorter" + - combination of stream::runs_creator and stream::runs_merger into a + convenient sorting container. + +* Added vector_bufreader and vector_bufwriter to overlapping I/O and + computation when accessing a stxxl::vector in scanning mode. + - documented problems of scanning in a vector + - C++11 range loop access support to stxxl::vector via vector_bufreader. + +* Added uint40 and uint48 types for integers with five and six bytes. + +* Adding STXXL_CHECK macros to always check outcomes in tests. + +* Renaming mng/mng.{h,cpp} -> mng/block_manager.{h,cpp}. + +* Reworking version.h + - add integer version like 10400 for 1.4.0 + - add get_version_string() for longer version string with features + - check_library_version() for mismatch between headers and libraries + +* Reworking error_handling.h to throw much more useful exceptions with + errno information and more. + +* Various file renames (which are only possible at major version changes). + +* Fixing bug in shrinking vector with vector::resize(n,true). + +------------------------------------------ +Version 1.3.2 (unreleased) + +* New Features + - Add defines STXXL_VERSION_{MAJOR,MINOR,PATCHLEVEL} and library routines + stxxl::version_{major,minor,patchlevel}() for getting STXXL version + information and an inline function check_library_version() to check at + runtime for mismatching header and library versions. (Bz:#6) + Available via +* Merged matrix branch which allows external memory matrix operations. + - merged dsr::argument_helper for command line processing + - also required an internal memory addressable_priority_queue, a + shared_object_pointer (instrusive pointer) + - adds block_scheduler as submodule to prefetch matrix blocks in a + pre-recorded order + - contains a test and benchmark. +* Fixed bug in stxxl::priority_queue + - added dump_sizes() + - The priority queue may become fragmented after a significant number of + insertions and deletes so that all slots in all groups are being used, but + the last external group is nearly empty (counting elements). In that case + we merge the whole last external group into a temporary ext_merger and swap + this overflow merger and the (now empty) last external merger + afterwards. Increasing the number of groups beyond 4 is not possible + without significant changes elsewhere. +* Fixed bug in stxxl::queue + - added special case when first block has space at the beginning + - added another test N * (push,pop,push), N * (pop, push, pop) +* Using LIKELY and UNLIKEY to prioritize branchs in gcc. +* Platform Support + - add GCC 4.6 + - add clang++ 2.9 + - update ICPC 12.0 (Intel C++ Composer XE 2011 Update 4): drop workarounds + for bugs fixed in Update 4 + - Boost 1.46: Boost Filesystem Version 3 + +------------------------------------------ +Version 1.3.1 (10 March 2011) + +* Possibly breaking changes + - No longer open syscall files with O_SYNC|O_DSYNC|O_RSYNC by default when + doing direct I/O, to avoid a write performance decrease starting with Linux + kernel version 2.6.32. + A stxxl::file::SYNC flag has been added to allow forcing the O_*SYNC flags + when opening files. + - Lock files by default (if implemented for this file type), can by disabled + via stxxl::file::NO_LOCK. + - block_prefetcher now takes a completion_handler instead of a raw function + pointer. Furthermore, the completion_handler is already executed after the + read operation completes, not only when the issuer waits for it to + complete. The I/O thread issues the call to the completion_handler, so for + multiple disks, this may happen concurrently. +* Internal changes, not user-visible + - Change handling of (empty) filler elements in typed_block to fix + mismatching struct size in nested blocks. + - Removed debugmon which was very limited and disabled for a long time. +* Bugfixes + - Allow prefetch schedule computation of vectors that are bound to a file. + - Fix race growing files during concurrent block allocation. + - Allow reading a block that spans over end-of-file, fill remainder with + zeroes. + - Crash at program termination when using global stxxl containers. + - Enable syscall_file to read/write >=2GiB of data in a single I/O operation. +* New public headers: stxxl/stats, stxxl/request +* Parallel mode can be switched on for internal STXXL computation selectively + (i.e. without enabling it for the non-stxxl part of an application), by + setting USE_PARALLEL_MODE=yes in make.settings.local. +* Platform Support + - add ICPC 12.0, works with both MCSTL (needs libstdc++ from GCC 4.2) and + parallel mode (needs libstdc++ from GCC 4.3 (not 4.4/4.5)) + - add CLANG++ 2.8 + - add MSVC 2010/Windows 7 + - allow the library name generated by MSVC to be changed by setting LIBNAME + and redefining STXXL_LIBNAME (defaults to "stxxl") +* Under-the-hood improvements + - code cleanup + - I/O-layer: renaming and reorganization of the request implementation +* Documentation updates +* Several new test programs +* Several test programs improved + +------------------------------------------ +Version 1.3.0 (12 August 2010) + +* New file types + - fileperblock_syscall/fileperblock_mmap/fileperblock_boostfd/fileperblock_wincall: + Use a separate file for each block, which is accessed by means of the + underlying file type. The "filename" given is used as a prefix of the + block file names. The advantage of these file types is that unused + disk space is freed earlier on the file system level. + - wbtl_file: Do library-based write-combining (good for writing small blocks onto SSDs) +* I/O subsystem + - separate the disk number of a file (which queue to put requests in) + from the fact that blocks for this file are allocated via the block + manager (disk number -1 otherwise). + - separate wait time counting for read and write I/Os + - wait times can be logged to a separate log file + (compile with -DSTXXL_WAIT_LOG_ENABLED and set STXXLWAITLOGFILE in the environment) +* Parallel PQ + - priority_queue now supports parallelized operations utilizing the libstdc++ parallel mode +* Other new functionality + - file requests can now be canceled (success not guaranteed), completion handlers are called anyway. + - log file locations are now configurable by the environment variables + STXXLLOGFILE and STXXLERRLOGFILE + - single queue I/O scheduling + - reverse_iterator added to containers types vector, deque and map + - autogrow files (specified in .stxxl by a size of 0), will be deleted on normal program termination + - add infrastructure to build multiple binaries with different CXXFLAGS from a single source + - overwriting deleted memfile regions with uninitialized data is now optional + (STXXL_CLEAR_FREE_MEMFILE_MEM) + - read_write_pool that combines prefetch_pool with write_pool and ensures cache coherency + - add a replaceable exit handler implementation, can be overwritten e.g. for library inclusion +* Many, many bug fixes, in particular concerning + - priority queue + - optimal prefetch schedule + - possible race condition while creating singleton instances + - random_shuffle() +* Platform Support + - add GCC 4.4 (parallel mode features not working in GCC 4.3 can now be used) + - add GCC 4.5 + - initial support for GCC 4.6 + - add ICPC 11.x + - add Boost 1.42 + - add FreeBSD + - drop Solaris +* New benchmarks + - io/benchmark_disks: more command line paramaters for finer testing granularity + - measure hard-disk and flash-disk combined performance and + determine best block size pairs (io/benchmark_disk_and_flash) + - benchmark using regular STXXL configuration (benchmark_configured_disks) +* Possibly breaking changes + - API name changes + vector::touch -> vector::block_externally_updated + - Template parameter changes + stream::sorted_runs, trigger_entry, trigger_entry_cmp, trigger_entry_iterator + - Priority of write changes + - Other name changes (considered internal) + typed_block<>::has_filler -> !typed_block<>::has_only_data + file::delete_region -> file::discard + vector::alloc_strategy -> vector::alloc_strategy_type + - stxxl::sort(..., M) and stxxl::stream::sort(..., M) now adhere to to the memory limit M + more strictly and throw errors instead of overallocating + - Execute completion handler before notifying waiters, so far after. +* Deprecated methods: + - stxxl::stats::reset(), stxxl::stats::_reset_io_wait_time(): + use stxxl::stats_data instead to store snapshots of the counters and compute differences +* Under-the-hood improvements + - I/O layer cleanup + - aligned_alloc +* Doxy now also lists undocumented members +* stop requiring STXXL_ROOT to be a hard coded absolute path set in make.settings.local, + a default of CURDIR works fine +* document #defines in defines.h + - switch on/off file types with STXXL_HAVE_xxx_FILE (to avoid platform compatibility problems) + +------------------------------------------ +Version 1.2.1 (14 August 2008) + +* add support for the libstdc++ parallel_mode (successor of MCSTL), + new make targets: library_g++_pmode, tests_g++_pmode, clean_g++_pmode + (requires g++ 4.3.2 or later) +* new stxxl file type stxxl::mem_file (for debugging purposes only), + helps debugging with valgrind because no memory cell ever + leaves valgrind's control +* properly destroy the singleton objects (block manager, disk queue threads, + logger, stats, config, ...) at program exit +* fixed a bug (recursive locking) in recursive block allocation +* added a test program for recursive block allocation +* sensible dependencies for nmake (MSVC): only rebuild files as necessary +* improve performance of createdisks +* human-readable output for I/O stats +* replace hard coded min/max values by std::numeric_limits<>::min/max in + examples +* fix a case where only half of the available memory was used during + recursive merging +* verify stxxl::set_seed() functionality in tests +* remove stxxl disk files created from default configuration (no .stxxl + file found) and other temporary files at program end +* stop using deprecated functions, switch to the replacements instead +* unify the mutex lock handling by introducing stxxl::scoped_mutex_lock +* unify the I/O wait time counting to work like read/write time counting +* simplify I/O time counting with scoped_{read,write,wait}_timer +* add some more tests +* more code cleanup + reformatting +* move some more code to the library +* reduce some include dependencies +* build system tuning +* propagate region deletion (when blocks are freed) to the file level +* fix problem in sorters where blocks were released too early +* specialize is_sorted() to use const_vector_iterators, no extra writes +* add c++0x style const_iterator cbegin()/cend() to all containers + +------------------------------------------ +Version 1.2.0 (05 July 2008) + +* made the block management layer thread-safe +* made all size_types unsigned +* stxxl::priority_queue + - fixed a rare assertion + - fixed a race condition by using proper block hinting + - insert_queue: replaced std::priority_queue with a special + implementation internal_priority_queue that allows for + fast exporting of all elements + - even more bugs and inefficiencies fixed + - significant speed improvements +* random number generators are now all seedable, + should allow redoing identical program runs for debugging purposes +* stxxl::noncopyable, inspired by boost::noncopyable: inheriting from this + class forbids copying of objects when this is undesirable + - used in all classes that had implemented their own variants previously +* stxxl::vector, all sorting functions: replaced two2one_dim_array_row_adapter + with array_of_sequences_iterator which is much faster, especially + if blocks have padding +* if required, verify that the sentinels satisfy a strict weak ordering +* stxxl::vector: several operations sped up, several more implemented +* fix existing allocation strategies and add experimental support for + distinguishing between regular disks and flash devices +* stxxl::stable_ksort + - add experimental warning, some parts are not yet implemented + - fixed an off-by-one error in memory allocation vs. usage +* btree: fuse last two nodes/leaves if possible, rebalancing can fail +* btree tests: ensure uniqueness of test data if required +* reduce function call overhead of stxxl::debugmon if it's not activated +* add public interface headers: stxxl/types, stxxl/aligned_alloc +* add compatibility wrappers for standard extensions + hash_map, hash_set, auto_ptr +* MCSTL is only supported with g++ 4.2 and icpc 10.x +* lots of small bugfixes +* made the TPIE, BDB and LEDA_SM tests compile again +* general code cleanup + - fixed most compiler warnings + - elimination of duplicate and unused code + - cleaned up and sanitized debugging macros + - no more 'using namespace std' and 'using namespace stxxl' + - fixed ambiguities noted by g++ 4.3 + - unify the #include directives + - add/unify/cleanup copyright headers +* general code reformatting (uncrustify) +* add support for new compiler releases +* portability fixes for different platforms +* implemented file truncation on windows platforms +* build system + - lots of small modifications + - now only requires GNU make 3.78 or later + - check whether STXXL_ROOT has been set correctly and if unset, try + autoconfiguration by creating make.settings.local with defaults + - improved and simplified boost support for posix systems + - Mac OS X support +* changed all tests so that they behave well in their default parameters, + system assumptions and return values and can be run from a script + - use aligned_alloc/aligned_dealloc appropriately +* added some more test programs +* add misc/run_all_tests that runs all tests with sensible parameters, + optionally via valgrind +* checked all tests with valgrind + - fixed use of uninitialized memory when writing to normal files + - (optionally) initialize typed_blocks and payload data in test structs + to suppress (most) uninitialized write errors when writing to disk files + - fix mismatched new/delete in mcstl +* update install and usage instructions +* spell checked sources and documentation + +------------------------------------------ +Version 1.1.0 (31 July 2007) + +* stxxl is now hosted on SourceForge: http://stxxl.sourceforge.net/ +* Restructured the source layout: + - includes moves to include/ + - introduced some public headers: + stxxl.h, stxxl/algorithm, stxxl/bits, stxxl/deque, stxxl/io, stxxl/ksort, stxxl/mallocstats, stxxl/map, stxxl/mng, stxxl/priority_queue, stxxl/queue, stxxl/random, stxxl/scan, stxxl/sort, stxxl/stable_ksort, stxxl/stack, stxxl/stream, stxxl/timer, stxxl/vector + - the header "stxxl" is no longer available, please use "stxxl.h" instead + - the use of any other (internal) header is discouraged, additional public headers can be added as the need arises +* Overhauled the build system: + - merged configuration files, simplified option tuning + - support object files and binaries with and without mcstl support existing in parallel + - the library build creates stxxl.mk which can be included in an applications Makefile to set the correct compiler/linker switches for stxxl + - similarly mcstxxl.mk is created for a stxxl compiled with mcstl support + - add dependency tracking and improve parallelism during build +* compiler support matrix: (for an up-to-date list, please see the doxygen documentation) + compiler | stxxl stxxl + mcstl + --------------+------------------------ + GCC 4.3 | x x + GCC 4.2 | x x + GCC 4.1 | x n/a + GCC 4.0 | x n/a + GCC 3.4 | x n/a + GCC 3.3 | o n/a + GCC 2.95 | - n/a + ICPC 9.1.051 | x x¹ + ICPC 10.0.025 | x x¹ + MSVC 2005 8.0 | x n/a + x = full support o = partial support - = unsupported + n/a = compiler does not support OpenMP which is needed by MCSTL + ¹ = does not work with STL GCC 4.2.0 (ICPC bug), workaround: + the first include in the program must be + "stxxl/bits/common/intel_compatibility.h" +* pipelined stream::sort, stream::runs_creator and stream::runs_merger are parallelized using MCSTL +* obsolete files removed +* fixed include dependencies +* fixed lots of warnings +* fixed lots of small bugs +* add header compilation tests +* stxxl::vector: implemented some more functions +* const char* stxxl::get_version_string(): new function +* comparators inherit from std::binary_function<> +* cleanup, reformat the code to a consistent format (Thanks, uncrustify!) + +------------------------------------------ +Version 1.0e (4 June 2007) + +* Bugfix: for objects with the destructors, memory block were allocated in a non-aligned fashion +* Internal CPU efficiency of Stxxl priority queue is improved (loser trees in external mergers are integrated) +* Fixed bug with conversion of const/non-const iterators, implemented comparison and difference operations on const/non-const iterators (for vector, map, and deque) +* Added operator[] to vector and deque iterators +* stxxl::random_shuffle added +* improved Makefile structure (thanx to Andreas Beckmann) +* Allow parallel build for g++ + +------------------------------------------ +Version 1.0d (16 January 2007) + +* Support of Visual Studio 2005 Express (VC++ 8.0) + +* Algorithms and data structures of Stxxl can now use more than 4GB of main memory on 64-bit processors/compilers + +* Support of error reporting using the C++ exception mechanism + +------------------------------------------- +Version 1.0c (21 September 2006) + +* An implementation of an I/O-efficient deque + +* STXXL uses MCSTL library (optional) to improve the performance of stxxl::sort and pipelined sort on SMP and multicore processors. + +------------------------------------------- +Version 0.99 (22 March 2006) + +* Better compiler support: g++ (versions 3.3.x-4.0.x) and Microsoft Visual C++ 7.1 (.NET) + +* New B+Tree-based implementation of map (compatible with all listed above compilers): I/O-efficient map + +------------------------------------------ +Version 0.9 (9 August 2005) + +* STXXL has been ported to Windows. It now can be run under Windows XP and Windows 2000 + +* STXXL can be compiled now by g++ (versions 3.0.x-3.4.x, 4.0.x) and Microsoft Visual C++ 7.1 (.NET) + +* New data structure: I/O efficient FIFO queue + +------------------------------------------ +Version 0.77 (24 March 2005) + +* An implementation of queue is available + +------------------------------------------ +Version 0.75 (23 March 2005) + +* An implementation of map based on B+tree is available + +------------------------------------------ +Version 0.7 (25 January 2005) + +* The implementation of pipelining is extended and improved + +------------------------------------------ +Version 0.6 (5 September 2004) + +* Tested implementation of the stream package (aka pipelining) is available + +------------------------------------------ +Version 0.5 (21 November 2003) + +* The first implementation of the stream package (aka pipelining) is available + +* Priority queue is available + +------------------------------------------ +Version 0.2 (Summer 2003) + +* The first public release + +* Vectors, stacks, sorting, scanning are available + +------------------------------------------ +# Local Variables: +# mode: text +# mode: flyspell +# End: diff --git a/third-party/MQF/ThirdParty/stxxl/CMakeLists.txt b/third-party/MQF/ThirdParty/stxxl/CMakeLists.txt new file mode 100644 index 0000000000..3f4e5308a4 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/CMakeLists.txt @@ -0,0 +1,791 @@ +############################################################################ +# CMakeLists.txt +# +# Base CMake file for building stxxl with different options. +# +# Part of the STXXL. See http://stxxl.sourceforge.net +# +# Copyright (C) 2013-2014 Timo Bingmann +# +# 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) +############################################################################ + +set(CMAKE_LEGACY_CYGWIN_WIN32 0) # needed to silence warning on cygwin + +# require cmake 2.6.2 (but please use 2.8.x) +cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) + +# the STXXL project +project(stxxl) + +# for additional cmake scripts +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/misc/cmake) + +# prohibit in-source builds +if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") + message(FATAL_ERROR "In-source builds are not allowed, use a separate build directory.") +endif() + +# default to Debug building for single-config generators +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message("Defaulting CMAKE_BUILD_TYPE to Debug") + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# STXXL version string +set(STXXL_VERSION_MAJOR "1") +set(STXXL_VERSION_MINOR "4") +set(STXXL_VERSION_PATCH "99") +set(STXXL_VERSION_STRING "${STXXL_VERSION_MAJOR}.${STXXL_VERSION_MINOR}.${STXXL_VERSION_PATCH}") +set(STXXL_VERSION_PHASE "prerelease/${CMAKE_BUILD_TYPE}") + +# read git directory (if it exists) and find git sha +if(EXISTS ${PROJECT_SOURCE_DIR}/.git) + find_package(Git) + if(GIT_FOUND) + execute_process(COMMAND ${GIT_EXECUTABLE} describe HEAD + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + OUTPUT_VARIABLE "STXXL_VERSION_GIT_REFSPEC" + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + OUTPUT_VARIABLE "STXXL_VERSION_GIT_SHA1" + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Detected git refspec ${STXXL_VERSION_GIT_REFSPEC} sha ${STXXL_VERSION_GIT_SHA1}") + endif() +endif() + +############################################################################### +# compilation options + +# please document all build options in doc/install.doc:install_build_options + +option(BUILD_EXAMPLES "Build all stxxl examples" OFF) + +option(BUILD_TESTS "Build all stxxl test programs" OFF) +if(BUILD_TESTS) + set(BUILD_EXAMPLES ON) +endif() + +option(BUILD_EXTRAS "Build all extra stxxl tests and tool programs" OFF) + +if(MSVC_VERSION LESS 1700) + # require boost for Visual C++ versions older than VC11 = MSVS 2012 + set(USE_BOOST ON) +else() + option(USE_BOOST "Use Boost libraries for threads,config,filesystem,random and date_time" OFF) +endif() + +option(USE_OPENMP "Use OpenMP for multi-core parallelism" ON) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + option(USE_GNU_PARALLEL "Use GNU parallel extensions for multi-core parallelism" ON) + + # GNU parallel mode but NOT OpenMP is impossible. + if(USE_GNU_PARALLEL AND NOT USE_OPENMP) + set(USE_GNU_PARALLEL OFF) + endif() +endif() + +option(TRY_COMPILE_HEADERS "Test stxxl header files for self-sufficiency: try to compile them." OFF) + +if(NOT MSVC) + option(USE_STD_THREADS "Force usage of C++ standard library thread support." OFF) +endif() + +option(NO_CXX11 "Build without C++11 flags" OFF) + +option(USE_VALGRIND "Run tests with valgrind, pre-initialize some memory buffers." OFF) + +option(USE_MALLOC_COUNT "Compile all programs with included malloc_count heap profiler." OFF) + +option(USE_GCOV "Compile and run tests with gcov for coverage analysis." OFF) + +option(STXXL_DEBUG_ASSERTIONS "Enable more costly assertions for internal debugging." OFF) +if(STXXL_DEBUG_ASSERTIONS) + set(STXXL_DEBUG_ASSERTIONS "1") # change from ON/OFF to 1/0 +endif() + +# see tools/benchmarks about older TPIE benchmarks. +option(USE_TPIE "Try to compile extra benchmarks from the 2007 S&PE paper with an old TPIE version." OFF) + +### building shared and/or static libraries + +# by default we currently only build a static library, since we do not aim to +# keep a stable binary interface. +option(BUILD_STATIC_LIBS "Build static library version of libstxxl" ON) +option(BUILD_SHARED_LIBS "Build shared library version of libstxxl" OFF) + +### allow user to specify other installation paths + +set(INSTALL_BIN_DIR "bin" CACHE PATH "Installation directory for executables") +set(INSTALL_LIB_DIR "lib" CACHE PATH "Installation directory for libraries") +set(INSTALL_INCLUDE_DIR "include" CACHE PATH "Installation directory for header files") +set(INSTALL_PKGCONFIG_DIR "lib/pkgconfig" CACHE PATH "Installation directory for pkg-config file") + +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR "CMake") +else() + set(DEF_INSTALL_CMAKE_DIR "lib/cmake/stxxl") +endif() +set(INSTALL_CMAKE_DIR "${DEF_INSTALL_CMAKE_DIR}" CACHE PATH "Installation directory for cmake files") + +############################################################################### +# enable use of "make test" + +enable_testing() + +include(CTest) +set(CTEST_PROJECT_NAME "STXXL") + +if(USE_VALGRIND) + set(STXXL_WITH_VALGRIND 1) + set(VALGRIND_OPTS --leak-check=full --error-exitcode=1 --suppressions=${PROJECT_SOURCE_DIR}/misc/valgrind.supp) +endif() + +############################################################################### +# enable use of "make package" + +# general package settings +set(CPACK_PACKAGE_VENDOR "STXXL Maintainer Team") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Library of external memory algorithms") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE_1_0.txt") +set(CPACK_PACKAGE_VERSION_MAJOR "${STXXL_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${STXXL_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${STXXL_VERSION_PATCH}") + +# source packaging +set(CPACK_SOURCE_GENERATOR "TGZ") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") +set(CPACK_SOURCE_IGNORE_FILES "/\\\\..*$;/build;${CPACK_SOURCE_IGNORE_FILES}") + +# binary packaging +set(CPACK_GENERATOR "TGZ") + +include(CPack) + +############################################################################### +# check platform + +if(WIN32 OR WIN64 OR MINGW) + set(STXXL_WINDOWS "1") +endif() + +if(MSVC) + set(STXXL_MSVC ${MSVC_VERSION}) +endif() + +############################################################################### +# enable warnings + +if(MSVC) + # Force to always compile with W4 + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() + ### disable verbose warnings: + # warning C4127: conditional expression is constant + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4127") + # warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4290") + # warning C4250: '...' : inherits '...' via dominance + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4250") + # warning C4512: assignment operator could not be generated (contains const members) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4512") + # warning C4355: 'this' : used in base member initializer list + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4355") + # disable lots of warnings about "unsecure" C runtime function + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + # disable lots of warnings about deprecated POSIX function names + add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE) + # disable lots of warnings about "unsecure" STL functions + add_definitions(-D_SCL_SECURE_NO_WARNINGS) +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -pedantic -Wno-long-long") + # detect -Wextra + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-Wextra CXX_HAS_FLAGS_WEXTRA) + if(CXX_HAS_FLAGS_WEXTRA) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra") + endif() +endif() + +############################################################################### +# enable C++11 and more compiler features + +# more template depth for some tests and compilers +include(CheckCXXCompilerFlag) +check_cxx_compiler_flag(-ftemplate-depth=1024 CXX_HAS_TEMPLATE_DEPTH) +if(CXX_HAS_TEMPLATE_DEPTH) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth=1024") +endif() + +# enable C++11 +if(MSVC) + # MSVC 11 or greater has C++11 automatically enabled +elseif(CYGWIN) + # C++11 is very restrictive on Cygwin +elseif(NOT NO_CXX11) + # try -std= flags to enable C++11 + check_cxx_compiler_flag(-std=c++11 CXX_HAS_STD_CXX11) + if(CXX_HAS_STD_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + else() + check_cxx_compiler_flag(-std=c++0x CXX_HAS_STD_CXX0X) + if(CXX_HAS_STD_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + endif() + endif() + # on MacOSX with clang we need to use libc++ for C++11 headers + if(APPLE) + if (CMAKE_CXX_COMPILER MATCHES ".*clang[+][+]" + OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + check_cxx_compiler_flag(-stdlib=libc++ CXX_HAS_STDLIB_LIBCXX) + if(CXX_HAS_STDLIB_LIBCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + else() + message(SEND_ERROR "Compilation on MacOSX with clang requires libc++.") + endif() + endif() + endif(APPLE) +endif() + +# check C++ compiler for C++11 features + +include(CheckCXXSourceCompiles) +check_cxx_source_compiles( + "#include + int main() { std::vector v(42); for (auto i : v) { ++i; } return 0; }" + STXXL_HAVE_CXX11_RANGE_FOR_LOOP) + +############################################################################### +# enable gcov coverage analysis with gcc + +if(USE_GCOV) + # find programs + find_program(GENHTML genhtml) + find_program(LCOV lcov) + + if(NOT LCOV OR NOT GENHTML) + message(SEND_ERROR "Coverage analysis requires lcov and genhtml programs.") + endif() + + # add coverage anaylsis compile and link flags + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") + + # add cached variable containing parameters for lcov/genhtml + set(LCOV_FLAGS "" CACHE STRING "parameters for lcov") + set(GENHTML_FLAGS --legend --no-branch-coverage + CACHE STRING "parameters for genhtml") + + # custom target to run before tests + add_custom_target(lcov-reset + COMMAND ${LCOV} -q --directory ${CMAKE_BINARY_DIR} --zerocounters + COMMENT "Resetting code coverage counters") + + # custom lcov target to run tests + add_custom_target(lcov-runtests + COMMAND ${CMAKE_CTEST_COMMAND} \${ARGS} + DEPENDS ${ALL_TESTS} lcov-reset + COMMENT "Running all unit tests") + + # get git version description + execute_process(COMMAND git describe --tags + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_VARIABLE GITDESC + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # command sequence to gather, clean and generate HTML coverage report + add_custom_target(lcov-html + COMMAND ${LCOV} -q --directory . --capture --output-file lcov.info + COMMAND ${LCOV} -q --remove lcov.info '/usr/*' ${LCOV_FLAGS} --output-file lcov-clean.info + COMMAND ${GENHTML} -q -o coverage --title "STXXL ${GITDESC}" --prefix ${PROJECT_SOURCE_DIR} ${GENHTML_FLAGS} lcov-clean.info + COMMENT "Capturing code coverage counters and create HTML coverage report" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + + # top-level target to run tests and generate coverage report + add_custom_target(test-coverage + COMMENT "Generate HTML coverage report " + DEPENDS lcov-runtests lcov-html) + +endif(USE_GCOV) + +############################################################################### +# find thread and random library + +# for testing for c++ system include files +include(CheckIncludeFileCXX) + +check_include_file_cxx(pthread.h HAVE_PTHREAD_H) + +if(MINGW AND NOT HAVE_PTHREAD_H) + set(USE_STD_THREADS ON) +endif() + +if(MSVC OR USE_STD_THREADS) + + # check for std::mutex and std::threads avalability + check_cxx_source_compiles( + "#include + int main() { std::mutex mutex; mutex.lock(); return 0; }" + HAVE_STD_MUTEX) + + check_cxx_source_compiles( + "#include + int main() { std::thread t; return 0; }" + HAVE_STD_THREAD) + + if(HAVE_STD_THREAD AND HAVE_STD_MUTEX) + set(STXXL_STD_THREADS "1") + else() + set(USE_BOOST ON) + message("No std::thread and std::mutex found, trying to use Boost classes instead") + endif() + + # using also requires -pthread on gcc + if(CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + endif() + + # also check for + check_include_file_cxx(random STXXL_STD_RANDOM) + +else() + find_package(Threads REQUIRED) + set(STXXL_POSIX_THREADS "1") +endif() + +############################################################################### +# determine large file support +# note: these flags need not be exported, all file operations must be +# encapsulated within the library! + +include(TestLargeFiles) +test_large_files(HAVE_LARGEFILES) + +if(HAVE_LARGEFILES) + add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES) +else() + message(FATAL_ERROR "Large file support was not detectable.") +endif() + +############################################################################### +# check for O_DIRECT flag + +if(CYGWIN) + #-tb O_DIRECT messes up cygwin + set(STXXL_DIRECT_IO_OFF 1) +elseif(MSVC OR MINGW) + # have FILE_FLAG_NO_BUFFERING on Windows + set(STXXL_DIRECT_IO_OFF 0) +elseif(APPLE) + # we do not have to test anything, all is well. + set(STXXL_DIRECT_IO_OFF 0) +else() + + include(CheckCXXSourceCompiles) + check_cxx_source_compiles(" +#include +#include +int main() { return ((int)O_DIRECT) != 0; } +" STXXL_HAVE_O_DIRECT) + + if(STXXL_HAVE_O_DIRECT) + set(STXXL_DIRECT_IO_OFF 0) + else() + set(STXXL_DIRECT_IO_OFF 1) + endif() + +endif() + +if(STXXL_DIRECT_IO_OFF) + message(WARNING + "The open() flag O_DIRECT was disabled!\n" + "This means that STXXL cannot bypass the system cache on your system, which may degrade performance.") +endif() + +############################################################################### +# check for mmap() function in sys/mman.h + +include(CheckSymbolExists) +check_symbol_exists(mmap "sys/mman.h" STXXL_HAVE_MMAP_FILE) + +############################################################################### +# check for Linux aio syscalls + +include(CheckCXXSourceCompiles) +check_cxx_source_compiles( + "#include + #include + #include + int main() { + aio_context_t context; + long r = syscall(SYS_io_setup, 5, &context); + return (r == 0) ? 0 : -1; + }" + STXXL_HAVE_LINUXAIO_FILE) + +############################################################################### +# check for an atomic add-and-fetch intrinsic for counting_ptr + +include(CheckCXXSourceCompiles) +check_cxx_source_compiles( + "int main() { int x; __sync_add_and_fetch(&x, +1); return 0; }" + STXXL_HAVE_SYNC_ADD_AND_FETCH) + +############################################################################### +# optional Boost libraries + +if(USE_BOOST) + + set(Boost_USE_MULTITHREADED ON) + + if(WIN32) + set(Boost_USE_STATIC_LIBS ON) + endif() + + # first try to find the version + find_package(Boost 1.34.1 REQUIRED) + + if(Boost_VERSION GREATER 104699) + #-tb Boost >= 1.47.0 found, we must also link with libboost_chrono for MSVC to work. + find_package(Boost 1.47.0 REQUIRED COMPONENTS thread date_time chrono iostreams system filesystem) + elseif(Boost_VERSION GREATER 103499) + #-tb Boost >= 1.35.0 found, we must also link with libboost_system for MSVC to work. + find_package(Boost 1.35.0 REQUIRED COMPONENTS thread date_time iostreams system filesystem) + else() + find_package(Boost 1.34.1 REQUIRED COMPONENTS thread date_time iostreams filesystem) + endif() + + # force caching of variables to display them to the user + set(BOOST_ROOT "${BOOST_ROOT}" CACHE PATH "Boost installation root." FORCE) + + if(Boost_FOUND) + # globally add boost include directories + include_directories(${Boost_INCLUDE_DIRS}) + # set defines in + set(STXXL_BOOST_CONFIG "1") + set(STXXL_BOOST_FILESYSTEM "1") + set(STXXL_BOOST_RANDOM "1") + set(STXXL_BOOST_THREADS "1") + set(STXXL_BOOST_TIMESTAMP "1") + elseif(MSVC_VERSION LESS 1700) + message(FATAL_ERROR "Boost libraries are required for MSVC < 2012.") + else() + message(FATAL_ERROR "Boost libraries not found. Try compilation without them.") + endif() + +endif(USE_BOOST) + +############################################################################### +# optional OpenMP parallelization + +if(USE_GNU_PARALLEL AND NOT USE_OPENMP) + message(FATAL_ERROR "Configuration selects GNU parallel mode but not OpenMP." + "This is impossible.") +endif() + +if(USE_OPENMP OR USE_GNU_PARALLEL) + + include(FindOpenMP) + + if(NOT OPENMP_FOUND) + message(STATUS "OpenMP not found. Continuing without parallel algorithm support.") + else() + set(STXXL_PARALLEL 1) + message(STATUS "OpenMP found, enabling built-in parallel algorithms.") + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + endif() + +else() + + message(STATUS "OpenMP disabled in STXXL (no parallelism is used).") + set(STXXL_PARALLEL 0) + +endif(USE_OPENMP OR USE_GNU_PARALLEL) + +############################################################################### +# optional GNU parallel STL mode + +if(USE_GNU_PARALLEL) + + if(NOT OPENMP_FOUND) + message(FATAL_ERROR "OpenMP not found. Try compilation without GNU parallel mode.") + else() + check_include_file_cxx(parallel/algorithm HAVE_PARALLEL_ALGORITHM_H) + + if(NOT HAVE_PARALLEL_ALGORITHM_H) + message(FATAL_ERROR "GNU parallel mode header not found. Try compilation without parallel mode.") + else() + set(STXXL_WITH_GNU_PARALLEL 1) + endif() + endif() + +endif(USE_GNU_PARALLEL) + +############################################################################### +# check if one thread and random library is available + +if(STXXL_STD_THREADS) + message(STATUS "Using std::thread and other C++11 library functions.") + set(STXXL_BOOST_THREADS 0) + set(STXXL_POSIX_THREADS 0) +elseif(STXXL_BOOST_THREADS) + message(STATUS "Using boost::thread library functions.") + set(STXXL_STD_THREADS 0) + set(STXXL_POSIX_THREADS 0) +elseif(STXXL_POSIX_THREADS) + message(STATUS "Using POSIX pthread library functions.") + set(STXXL_STD_THREADS 0) + set(STXXL_BOOST_THREADS 0) +else() + message(SEND_ERROR "Could not detect a thread library. Check the compilation documentation.") +endif() + +############################################################################### +# test for additional includes and features used by some stxxl_tool components + +include(CheckSymbolExists) +check_symbol_exists(mallinfo "malloc.h" STXXL_HAVE_MALLINFO_PROTO) +check_symbol_exists(mlock "sys/mman.h" STXXL_HAVE_MLOCK_PROTO) + +if(USE_MALLOC_COUNT) + # malloc_count requires the linux dl (dynamic linker) library + find_library(DL_LIBRARIES NAMES dl) + set(STXXL_EXTRA_LIBRARIES ${STXXL_EXTRA_LIBRARIES} ${DL_LIBRARIES}) +endif() + +############################################################################### +# configure environment for building + +# create config.h with define switches _inside binary dir_! +configure_file(${PROJECT_SOURCE_DIR}/include/stxxl/bits/config.h.in ${PROJECT_BINARY_DIR}/include/stxxl/bits/config.h) + +# CXX_FLAGS required for compilation +set(STXXL_CXX_FLAGS ${OpenMP_CXX_FLAGS}) + +# globally adds top-level include directories +set(STXXL_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include/ ${PROJECT_BINARY_DIR}/include/) +include_directories(${STXXL_INCLUDE_DIRS}) + +# for targets using stxxl library +set(STXXL_EXTRA_LIBRARIES ${STXXL_EXTRA_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +set(STXXL_LIBRARIES stxxl ${STXXL_EXTRA_LIBRARIES}) + +# export STXXL_INCLUDE_DIRS and STXXL_LIBRARIES to global CACHE +set(STXXL_CXX_FLAGS "${STXXL_CXX_FLAGS}" CACHE STRING "Compiler flags for STXXL") +set(STXXL_INCLUDE_DIRS "${STXXL_INCLUDE_DIRS}" CACHE STRING "Include paths for STXXL") +set(STXXL_LIBRARIES "${STXXL_LIBRARIES}" CACHE STRING "Libraries to link for STXXL") + +# build libstxxl in /lib +add_subdirectory(lib) + +############################################################################### +# macros for building stxxl programs and tests + +# macro for building stxxl programs +macro(stxxl_build_tool PROGNAME) + + add_executable(${PROGNAME} ${PROGNAME}.cpp ${ARGN}) + target_link_libraries(${PROGNAME} ${STXXL_LIBRARIES}) + +endmacro(stxxl_build_tool) + +# macro for building stxxl examples +macro(stxxl_build_example TESTNAME) + + if(BUILD_EXAMPLES) + stxxl_build_tool(${TESTNAME} ${ARGN}) + endif(BUILD_EXAMPLES) + +endmacro(stxxl_build_example) + +# macro for building stxxl tests +macro(stxxl_build_test TESTNAME) + + if(BUILD_TESTS) + stxxl_build_tool(${TESTNAME} ${ARGN}) + endif(BUILD_TESTS) + +endmacro(stxxl_build_test) + +# macro for registering stxxl tests +macro(stxxl_test TESTNAME) + + if(BUILD_TESTS) + set(TESTFULLNAME ${TESTNAME} ${ARGN}) + string(REPLACE ";" "_" TESTFULLNAME "${TESTFULLNAME}") # stringify list + + if(USE_VALGRIND) + # prepend valgrind call + add_test(${TESTFULLNAME} /usr/bin/valgrind ${VALGRIND_OPTS} ./${TESTNAME} ${ARGN}) + set_tests_properties(${TESTFULLNAME} PROPERTIES TIMEOUT 7200) + else() + add_test(${TESTFULLNAME} ${TESTNAME} ${ARGN}) + set_tests_properties(${TESTFULLNAME} PROPERTIES TIMEOUT 3600) + endif() + + endif(BUILD_TESTS) + +endmacro(stxxl_test) + +# macro for building stxxl extra program +macro(stxxl_build_extra_tool) + + if(BUILD_EXTRAS) + stxxl_build_tool(${ARGN}) + endif(BUILD_EXTRAS) + +endmacro(stxxl_build_extra_tool) + +# macro for registering extra stxxl tests +macro(stxxl_extra_test) + + if(BUILD_EXTRAS) + stxxl_test(${ARGN}) + endif(BUILD_EXTRAS) + +endmacro(stxxl_extra_test) + +# macro for setting additional defines for targets +macro(add_define PROGNAME) + + if(TARGET ${PROGNAME}) + set_property(TARGET ${PROGNAME} APPEND PROPERTY COMPILE_DEFINITIONS ${ARGN}) + else() + if(BUILD_TESTS) + message("Ignoring add_define(${PROGNAME} ${ARGN}) for unknown target ${PROGNAME}") + endif(BUILD_TESTS) + endif() + +endmacro(add_define TESTNAME) + +############################################################################### +# cmake script TRY_COMPILE all stxxl header files + +if(TRY_COMPILE_HEADERS) + + include(CheckCXXSourceCompiles) + set(CMAKE_REQUIRED_INCLUDES ${STXXL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + set(CMAKE_REQUIRED_LIBRARIES ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + + file(GLOB_RECURSE header_files FOLLOW_SYMLINKS "include/*") + list(REMOVE_ITEM header_files "${PROJECT_SOURCE_DIR}/include/stxxl/bits/config.h.in") + list(SORT header_files) + + foreach(file ${header_files}) + string(REPLACE "/" "_" compilename "${file}") # replace / to _ to fix warnings + string(REPLACE "." "_" compilename "${compilename}") + + check_cxx_source_compiles( + "#include \"${file}\" + int main() { return 0; }" IsSelfContained${compilename}) + + if(NOT IsSelfContained${compilename}) + message(SEND_ERROR "Compilation FAILED for ${file}\n\nCompiler output:\n${OUTPUT}") + endif() + endforeach() + +endif(TRY_COMPILE_HEADERS) + +############################################################################### +# figure out STXXL_TMPDIR for tests + +if(BUILD_TESTS) + + if(DEFINED ENV{STXXL_TMPDIR}) + set(STXXL_TMPDIR "$ENV{STXXL_TMPDIR}") + elseif(NOT DEFINED STXXL_TMPDIR) + set(STXXL_TMPDIR ".") + endif() + + message(STATUS "Using STXXL_TMPDIR ${STXXL_TMPDIR} for tests") + +endif(BUILD_TESTS) + +############################################################################### +# install header files + +install(FILES include/stxxl.h DESTINATION ${INSTALL_INCLUDE_DIR}) +install(DIRECTORY include/stxxl DESTINATION ${INSTALL_INCLUDE_DIR}) + +# also copy the config file with build options! +install(FILES ${PROJECT_BINARY_DIR}/include/stxxl/bits/config.h + DESTINATION ${INSTALL_INCLUDE_DIR}/stxxl/bits/) + +############################################################################### +# prepare pkg-config file + +configure_file(misc/cmake/stxxl.pc + "${PROJECT_BINARY_DIR}/${STXXL_LIBNAME}.pc" @ONLY) + +# copy the stxxl.pc file into lib/pkgconfig +if(INSTALL_PKGCONFIG_DIR) + install(FILES ${PROJECT_BINARY_DIR}/${STXXL_LIBNAME}.pc + DESTINATION ${INSTALL_PKGCONFIG_DIR}) +endif() + +############################################################################### +# export targets to cmake project config file + +if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.6) + # register package for use from the global CMake-registry + export(PACKAGE stxxl) +endif() + +# add stxxl library targets to the build tree export set +export(TARGETS ${STXXL_EXPORTED_LIBS} FILE "${PROJECT_BINARY_DIR}/stxxl-targets.cmake") + +# calculate absolute install paths +foreach(dir LIB BIN INCLUDE CMAKE) + set(ABS_INSTALL_${dir}_DIR "${INSTALL_${dir}_DIR}") + if(NOT IS_ABSOLUTE "${ABS_INSTALL_${dir}_DIR}") + set(ABS_INSTALL_${dir}_DIR "${CMAKE_INSTALL_PREFIX}/${ABS_INSTALL_${dir}_DIR}") + endif() +endforeach() + +# calculate final installed include path relative to cmake install path +file(RELATIVE_PATH REL_INCLUDE_DIR "${ABS_INSTALL_CMAKE_DIR}" "${ABS_INSTALL_INCLUDE_DIR}") + +# create common stxxl-config.cmake file +configure_file(misc/cmake/stxxl-version.cmake.in + "${PROJECT_BINARY_DIR}/stxxl-version.cmake" @ONLY) + +# create stxxl-version.cmake file for the build tree +set(CONF_INCLUDE_DIRS "${STXXL_INCLUDE_DIRS}") +configure_file(misc/cmake/stxxl-config.cmake.in + "${PROJECT_BINARY_DIR}/stxxl-config.cmake" @ONLY) + +# create stxxl-version.cmake file for the install tree +set(CONF_INCLUDE_DIRS "\${STXXL_CMAKE_DIR}/${REL_INCLUDE_DIR}") +configure_file(misc/cmake/stxxl-config.cmake.in + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/stxxl-config.cmake" @ONLY) + +# install the stxxl-config.cmake and stxxl-version.cmake +install(FILES + "${PROJECT_BINARY_DIR}/stxxl-version.cmake" + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/stxxl-config.cmake" + DESTINATION "${INSTALL_CMAKE_DIR}") + +# Install the export set for use with the install-tree +install(EXPORT stxxl-targets DESTINATION "${INSTALL_CMAKE_DIR}") + +############################################################################### +# build tests and tools + +add_subdirectory(tools) +add_subdirectory(examples) +add_subdirectory(tests) +add_subdirectory(local) diff --git a/third-party/MQF/ThirdParty/stxxl/INSTALL b/third-party/MQF/ThirdParty/stxxl/INSTALL new file mode 100644 index 0000000000..582cdfd928 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/INSTALL @@ -0,0 +1,23 @@ +BASIC INFORMATION + +For detailed installation instructions, including instruction for installation +in a Linux or Windows based environment and application compilation hints +(including an example Makefile) please read the doxygen manual: +http://stxxl.sourceforge.net/ + + +QUICK INSTALLATION INSTRUCTIONS FOR POSIX COMPATIBLE SYSTEMS + +* Extract the tarball and change into the stxxl root directory. + +* Create a separate build directory (in-source building is prohibited): + - mkdir build + - cd build + +* Run cmake to detect the build platform (where ".." is the STXXL tree): + - cmake .. + +* Check cmake's output for errors, then run build the library: + - make + +* See http://stxxl.sourceforge.net/ for more details. diff --git a/third-party/MQF/ThirdParty/stxxl/LICENSE_1_0.txt b/third-party/MQF/ThirdParty/stxxl/LICENSE_1_0.txt new file mode 100644 index 0000000000..36b7cd93cd --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third-party/MQF/ThirdParty/stxxl/README b/third-party/MQF/ThirdParty/stxxl/README new file mode 100644 index 0000000000..7a61d7e06a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/README @@ -0,0 +1,27 @@ +DESCRIPTION + +STXXL is an implementation of the C++ standard template library STL for +external memory (out-of-core) computations, i. e. STXXL implements containers +and algorithms that can process huge volumes of data that only fit on disks. +While the closeness to the STL supports ease of use and compatibility with +existing applications, another design priority is high performance. + + +DOCUMENTATION + +See the Doxygen documentation for installation manual and programmer +documentation: http://stxxl.sourceforge.net + + +LICENSE TERMS + +STXXL is 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) + + +BUGS AND QUESTIONS + +If you find any bugs or have any questions, please visit the forums at + +http://sourceforge.net/projects/stxxl/forums diff --git a/third-party/MQF/ThirdParty/stxxl/TODO b/third-party/MQF/ThirdParty/stxxl/TODO new file mode 100644 index 0000000000..4bb1e13f8b --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/TODO @@ -0,0 +1,116 @@ +* asynchronous pipelining + (currently being developed in branch parallel_pipelining_integration) + +* if the stxxl disk files have been enlarged because more external memory + was requested by the program, resize them afterwards to + max(size_at_program_start, configured_size) + https://sourceforge.net/forum/message.php?msg_id=4925158 + +* allocation strategies: provide a method get_num_disks() + and don't use stxxl::config::get_instance()->disks_number() inappropriately + +* implement recursion in stable_ksort and do not assume random key + distribution, do sampling instead + as a start, at least abort early if the expected size of a bucket is larger + than the memory available to sort it + +* debug stable_ksort in depth, there are still some crashing cases left + +* continue using the new approach for STXXL_VERBOSE: + $(CXX) -DSTXXL_VERBOSE_FOO=STXXL_VERBOSEx + +* check+fix all sorted_runs() calls to not cause write I/Os + +* on disk destruction, check whether all blocks had been deallocated before, + i.e. free_bytes == disk_size + +* implement an allocator which looks at the available free space on disks + when distributing blocks to disks, or does load-balancing depending + on the given speed of the disks + +* abstract away block manager so every container can attach to a file. + +* retry incomplete I/Os for all file types (currently only syscall) + +* iostats: add support for splitting I/Os and truncating reads at EOF + +* do not specify marginal properties such as allocation strategy as part of the template type. + instead, make such properties dynamically configurable using run-time polymorphism, + which would incur only a negligible running time overhead (one virtual function call per block). + +* If we get rid of the sentinels (at least for sorting), we can drop the + min_value()/max_value() requirement on the comparator and therefore + unmodified comparators could be used for stxxl. + +* Traditionally stxxl only supports PODs in external containers, e.g. nothing + that has non-trivial constructors, destructors or copy/assignemnt operators. + That is neccessary to allow fast operations by accessing the underlying + blocks directly without caring about copying/moving/otherwise manipulating + the content or uninitialized elements. + + For some applications it could be helpful to have containers that take + non-POD elements, but you would probably lose the features you gain by having + direct access to the blocks. + + Some discussion regarding using some shared_ptr<> in a stxxl::vector can be + found here: https://sourceforge.net/projects/stxxl/forums/forum/446474/topic/4099030 + +* The following code is not correct: + + file * f = create_file(..., RDONLY); + vector<> v(f); + v[42]; // non-const access, sets dirty flag + + but the error message it produces is very unclear and may come very + asynchronous: + + terminate called after throwing an instance of 'stxxl::io_error' what(): + Error in function virtual void stxxl::mmap_file::serve(const + stxxl::request*): Info: Mapping failed. Page size: 4096 offset modulo page + size 0 Permission denied + + Caused by stxxl::vector<> when swapping out a dirty page. Reproducible with + containers/test_vector_sizes.stxxl.bin + + Possible solution: vector::write_page() should check if + vector-is-bound-to-a-file and file-is-opened-read-only throw "please use only + const access to a read only vector" + +* I've noticed that the destructor for stxxl::vector flushes any dirty data to + disk. Possibly that makes sense for a file-backed vector (I haven't looked at + how those work), but for a vector that uses the disks configured in .stxxl it + is wasted I/O as the data is immediately freed. For the moment I am working + around this by calling thevector.clear() immediately before destroying it, + which seems to prevent the writeback (based on querying the stats). + +* There should be a function like + + bool supports_filetype(const char *) + + that allows to check at runtime whether a file type (given as string) is + available. Useful for tests that take a file type as parameter - they + currently throw an exception "unsupported filetype". + +* Change the constructor + stxxl::vector<>::vector(stxxl::file*) + to + stxxl::vector<>::vector(shared_ptr) + so that ownership of the file can be transferred to the vector and cleanup + (closing etc.) of the file can happen automatically + +* materialize() that takes an empty vector as argument and grows it, including + allocating more blocks of fills the existing part of a vectorand once we came + to it's end automatically expands the vector (including proper resizes). This + approach should be more efficient (due to overlapping) than repeated + push_back() + +* streamop passthrough() useful if one wants to replace some streamop by a + no-op by just redefining one type needs peformance measurements, hopefully + has no impact on speed. + +* stream::discard() should not need to call in.operator*() - are there side + effects to be expected? + -> add a streamop "inspect" or so for this purpose + -> drop *in call from discard + +* add discard(StreamOp, unsigned_irgendwas n): discard at most n elements diff --git a/third-party/MQF/ThirdParty/stxxl/doc/DoxygenLayout.xml b/third-party/MQF/ThirdParty/stxxl/doc/DoxygenLayout.xml new file mode 100644 index 0000000000..9a7391a046 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/DoxygenLayout.xml @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third-party/MQF/ThirdParty/stxxl/doc/coding_style.dox b/third-party/MQF/ThirdParty/stxxl/doc/coding_style.dox new file mode 100644 index 0000000000..9dd3f05e46 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/coding_style.dox @@ -0,0 +1,79 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/coding_style.dox + * + * Coding style guidelines of the STXXL. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page coding_style Coding Style Guidelines + +\author Timo Bingmann (2013) + +STXXL coding style follows the current style of STL and Boost, as STXXL strives to provide STL compatible interfaces. Following these guidelines greatly shortens the maintainers' response time and increases their willingness to incorporate your code into STXXL. + +We cannot provide a full coding style document here, the source code itself must serve as a large example. As the STXXL has grown historically, not all parts of the STXXL library following this coding style. But we do put down the following list of rules: + +- Naming of classes, structs, functions and variables must follow STL naming conventions: no capital letters, use underscores between words. + - The exception are template parameters: use CamelCase for the parameters themselves, and the underscore version for the typedef. + - The smaller the scope of a variable is, the shorter its name should be. + - Member attributes of larger classes should be prefixed with \c m_ ! Attributes of smaller structs can omit \c m_. + +- Tabs should not use used, indentation width is 4 spaces. + +- The following code shows example of the rules above: +\code +//! A class that does something important. +template +class some_class +{ +protected: + //! a class attribute, prefixed with m_ + int m_used; + +public: + //! types are almost always suffixes with _type, + //! with _iterator being an exception. + typedef ValueType value_type; + + //! \name Group of Functions + //! \{ + + //! Return current state of page in cache. + //! \param page please only document parameters when needed. + void get_state(int page) const + { + int ret = 0; + for (size_t i = 0; i < list.size(); ++i) { + if (list[i].page == page) + ret = list[i].state; + } + return ret; + } + + //! \} +}; +\endcode + +- Use of "using namespace" is absolutely prohibited. + +- All public interfaces must be documented using doxygen (see tags in example). + +- All containers and extensions must provide a simple tutorial and example. Design documentation is greatly recommended. + +- All extensions and subsystems must provide tests which sufficiently cover the functions. + +- All preprocessor macros should begin with \c STXXL_ . + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/common.dox b/third-party/MQF/ThirdParty/stxxl/doc/common.dox new file mode 100644 index 0000000000..2ebf0ab047 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/common.dox @@ -0,0 +1,530 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/common.dox + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { + +//////////////////////////////////////////////////////////////////////////////// +/** \page common Common Utilities and Helpers + +\author Timo Bingmann (2013) + +A lots of basic utility classes and helper functions have accumulated in STXXL. Try are usually fundamental enough to be also used in an application program. Before implementing a common software utility, please check the list below; it might already exist in STXXL: + +- \subpage common_io_counter "I/O statistics and performance counter" +- \subpage common_random "random number generators" +- \subpage common_timer "timestamp and timer function" +- \subpage common_simple_vector "a non-growing, non-initializing simple_vector" +- \subpage common_counting_ptr "reference counted (shared) objects via counting_ptr" +- \subpage common_cmdline "command line parser" +- \subpage common_binary_buffer "serialization of variable data structures into blobs" +- \subpage common_thread_sync "synchronization primitives for multi-threading" +- \subpage common_logging "logging macros" +- \subpage common_assert "macros for checking assertions" +- \subpage common_throw "macros for throwing exceptions" +- \subpage common_types "signed and unsigned integer types" +- \subpage common_log2 "calculating log_2(x)" +- \subpage common_misc_macros "miscellaneous macros" +- \subpage common_misc_funcs "miscellaneous functions" + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_random Random Number Generators + +See also file common/rand.h + +Measuring the time consumption of program sections are often of great interest. The Stxxl comes with several build-in pseudo random number generators as shown below: + +\code + +stxxl::random_number32 rand32; // integer values in [0, 2^32) +stxxl::random_number64 rand64; // integer values in [0, 2^64) +stxxl::random_uniform_slow urand_slow; // uniform values in [0.0, 1.0) +stxxl::random_uniform_fast urand_fast; // uniform values in [0.0, 1.0) +stxxl::random_number<> n_rand; // integer values in [0,N) + +unsigned int random32 = rand32(); +stxxl::uint64 random64 = rand64(); +double urandom_slow = urand_slow(); +double urandom_fast = urand_fast(); +unsigned int n_random = n_rand(123456789); + +STXXL_MSG("random 32 bit number: " << random32); +STXXL_MSG("random 64 bit number: " << random64); +STXXL_MSG("random number between [0.0, 1.0) (slow): " << urandom_slow); +STXXL_MSG("random number between [0.0, 1.0) (fast): " << urandom_fast); +STXXL_MSG("random number between [0,123456789): " << n_random); + +\endcode + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_timer Timestamp and Timer Classes + +See also file common/timer.h + +Measuring the time certain parts of an algorithm or the entire algorithm consume will often be of great interest. The STXXL provides build-in time measurement class stxxl::timer which can be used as follows: + +\code +#include // make timer class available + +stxxl::timer Timer; // create Timer object + +Timer.start(); + +// code section which shall be measured + +Timer.stop(); + +// get results: +STXXL_MSG(",easured time: " << (Timer.seconds()) << " (seconds), " << (Timer.mseconds()) << " (milliseconds), " << (Timer.useconds()) << " (microseconds)) + +Timer.reset(); // reset clock to zero which allows to run start() again + +\endcode + +As an alternative, one can also work on the timestamp itself: + +\code +double start = stxxl::timestamp(); + +// code section to be measured + +double stop = stxxl::timestamp(); + +STXXL_MSG("measured time: " << (stop - start) << " seconds."); +\endcode + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_simple_vector A Non-growing, Non-initializing Simpler Vector + +For applications where a std::vector is overkill, or one wishes to allocate an uninitialied POD array, the \ref simple_vector is a good method. + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_counting_ptr Reference Counted (Shared) Objects + +Some objects in STXXL are reference counted. This is usually done for large objects, which should not be copied deeply in assignments. Instead, a reference counting pointer is used, which holds a handle while the pointer exists and deletes the object once all pointers go out of scope. Examples are matrices and \ref stream::sorted_runs. + +The method used is called \ref counting_ptr or intrusive reference counting. This is similar, but not identical to boost or TR1's \c shared_ptr. + +The \ref counting_ptr is accompanied by \ref counted_object, which contains the actual reference counter (a single integer). A reference counted object must derive from \ref counted_object : + +\code +struct something : public stxxl::counted_object +{ +}; +\endcode + +Code that now wishes to use pointers referencing this object, will typedef an \ref counting_ptr, which is used to increment and decrement the included reference counter automatically. + +\code +typedef stxxl::counting_ptr something_ptr +{ + // create new instance of something + something_ptr p1 = new something; + { + // create a new reference to the same instance (no deep copy!) + something_ptr p2 = p1; + // this block end will decrement the reference count, but not delete the object + } + // this block end will delete the object +} +\endcode + +The \ref counting_ptr can generally be used like a usual pointer or \c shared_ptr (see the docs for more). + +There is also \ref const_counting_ptr to return const objects (note that a const \ref counting_ptr will only render the pointer const, not the enclosed object). + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_cmdline Command Line Parser + +STXXL now contains a rather sophisticated command line parser for C++, \ref cmdline_parser, which enables rapid creation of complex command line constructions. Maybe most importantly for application with external memory: the parser will recognize byte sizes with SI/IEC suffixes like '2 GiB' and transform it appropriately. + +\snippet examples/common/cmdline.cpp example + +When running the program above without arguments, it will print: +\verbatim +$ ./cmdline +Missing required argument for parameter 'filename' + +Usage: ./cmdline [options] + +This may some day be a useful program, which solves many serious problems of +the real world and achives global peace. + +Author: Timo Bingmann + +Parameters: + filename A filename to process +Options: + -r, --rounds N Run N rounds of the experiment. + -s, --size Number of bytes to process. +\endverbatim + +Nice output, notice the line wrapping of the description and formatting of parameters and arguments. These too are wrapped if the description is too long. + +We now try to give the program some arguments: +\verbatim +$ ./cmdline -s 2GiB -r 42 /dev/null +Option -s, --size set to 2147483648. +Option -r, --rounds N set to 42. +Parameter filename set to "/dev/null". +Command line parsed okay. +Parameters: + filename (string) "/dev/null" +Options: + -r, --rounds N (unsigned integer) 42 + -s, --size (bytes) 2147483648 +\endverbatim +The output shows pretty much what happens. The command line parser is by default in a verbose mode outputting all arguments and values parsed. The debug summary shows to have values the corresponding variables were set. + +One feature worth naming is that the parser also supports lists of strings, i.e. \c std::vector via \ref cmdline_parser::add_param_stringlist() and similar. + +\example examples/common/cmdline.cpp +This example is documented in \ref common_cmdline tutorial. + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_binary_buffer Serializing Variable Data Structures with binary_buffer + +Some applications of STXXL will require variable data structures. Currently there is not much support for this in STXXL. + +For serializing information into in-memory data blocks, the STXXL provides the helper classes \ref binary_buffer and \ref binary_reader. These provide functions \ref binary_buffer::put<>() to append arbitrary integral data types and \ref binary_reader::get<>() to read these again. Serialization and deserialization of variable data structures are then composed of identical sequences of put()/get(). + +Additionally, the classes already provide methods to serialize variable length strings (together with their lengths), and thereby also sub-block serialization. These functions are called \ref binary_buffer::put_string() and \ref binary_reader::get_string(). + +Furthermore, to squeeze small integers into fewer bytes, they classes also contain "varint" encoding, where each byte contains 7 data bits and one continuation bit. These functions are called \ref binary_buffer::put_varint() and \ref binary_reader::get_varint(). + +The following example fills a binary_buffer with some data elements: +\snippet tests/common/test_binary_buffer.cpp serialize + +And the following binary_reader example deserializes the data elements and check's their content. +\snippet tests/common/test_binary_buffer.cpp deserialize + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_thread_sync Synchronization Primitives for Multi-Threading + +To support multi-threading, some parts of STXXL use synchronization primitives to ensure correct results. The primitives are based either on pthreads or on Boost classes. + +\section mutex Mutexes + +For simple mutual exclusion contexts, stxxl::mutex objects should be used together with scoped locks: + +\code +class Something +{ + stxxl::mutex m_mtx; + void critical_function() + { + stxxl::scoped_mutex_lock lock(m_mtx); + // do something requiring locking + } +}; +\endcode + +\section semaphore Semaphores + +Additionally stxxl::semaphore is available if counting is required. + +\section further Further Primitives: State and OnOff-Switch + +stxxl::state is a synchronized state switching mechanism? + +stxxl::onoff_switch is a two state semaphore thing? + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_logging Logging Macros STXXL_MSG + +All STXXL components should output log or trace messages using the following macros. There are two basic methods for logging using ostream syntax: + +\code +// for plain messages +STXXL_MSG("text " << var) +// for error messages +STXXL_ERRMSG("error message " << reason) +\endcode + +For debugging and tracing the following macros can be used for increasing +levels of verbosity: + +\code +// level 0 (for current debugging) +STXXL_VERBOSE0("text " << var) +// level 1,2 and 3 for more verbose debugging level +STXXL_VERBOSE1("text " << var) +STXXL_VERBOSE2("text " << var) +STXXL_VERBOSE3("text " << var) +\endcode + +A method used by some submodule authors to create their own levels of verbosity is to make their own debugging macros: + +\code +#define STXXL_VERBOSE_VECTOR(msg) STXXL_VERBOSE1("vector[" << static_cast(this) << "]::" << msg) +\endcode + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_assert Macros for Checking Assertions + +There are quite a bunch of macros for testing assertions. You must be careful to pick the right one depending on when and what you want to assert on. + +# Compile-time Assertions: STXXL_STATIC_ASSERT + +To check specific conditions at compile time use \ref STXXL_STATIC_ASSERT. + +\code +struct item { int a,b,c,d; } +STXXL_STATIC_ASSERT(sizeof(item) == 4 * sizeof(int)); +\endcode + +# Assertions in Unit Tests: STXXL_CHECK + +Assertions in unit tests must use the following macros to ensure that the condition is also checked in release builds (where a plain \c "assert()" is void). These \c CHECK function should NOT be used to test return values, since we try to throw exceptions instead of aborting the program. + +\code +// test a condition +STXXL_CHECK( 2+2 == 4 ); +// test a condition and output a more verbose reason on failure +STXXL_CHECK2( 2+2 == 4, "We cannot count!"); +\endcode + +Sometimes one also wants to check that a specific expression \b throws an exception. This checking can be done automatically using a try { } catch {} by using \ref STXXL_CHECK_THROW. + +# Plain Assertions: assert + +For the usual assertions, that should be removed in production code for performance, we use the standard \c "assert()" function. + +However, there is also \ref STXXL_ASSERT(), which can be used as a replacement for \c assert(), when compiler warnings about unused variables or typedefs occur. The issue is that assert() completely removes the code, whereas \ref STXXL_ASSERT() keeps the code encloses it inside \c if(0). + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_throw Macros for Throwing Exception + +The STXXL provides several pre-defined exception macros to detect run-time errors. The basic ones are: + +- STXXL_THROW(exception_type, error_message) +- STXXL_THROW2(exception_type, location, error_message) +- STXXL_THROW_ERRNO(exception_type, error_message) +- STXXL_THROW_INVALID_ARGUMENT(error_message) +- STXXL_THROW_UNREACHABLE(error_message) + +In addition, we also defined \b conditional throw macros, which check the outcome of a function call: + +- STXXL_THROW_IF(expr, exception_type, error_message) +- STXXL_THROW_NE_0(expr, exception_type, error_message) +- STXXL_THROW_EQ_0(expr, exception_type, error_message) +- STXXL_THROW_LT_0(expr, exception_type, error_message) + +For checking system calls which set errno, the following macros are used to also provide strerror information for the user: + +- STXXL_THROW_ERRNO_IF(expr, exception_type, error_message) +- STXXL_THROW_ERRNO_NE_0(expr, exception_type, error_message) +- STXXL_THROW_ERRNO_EQ_0(expr, exception_type, error_message) +- STXXL_THROW_ERRNO_LT_0(expr, exception_type, error_message) + +For checking pthread system calls, a special macro is needed, because these do not set errno. Instead they return the errno value: + +- STXXL_CHECK_PTHREAD_CALL(pthread call) + +And for WINAPI calls there is a special macro to call GetLastError and format it in a nice way: + +- STXXL_THROW_WIN_LASTERROR(exception_type, error_message) + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_log2 Calculating log2(x) for Integers and at Compile-Time + +STXXL provides three methods to calculate log2(x), which is often needed for binary trees, etc. + +The first is during \b compile-time using template meta-programming magic: + +\code +#include + +std::cout << stxxl::LOG2<10000>::floor << std::endl; +std::cout << stxxl::LOG2<10000>::ceil << std::endl; +\endcode + +The second is for calculating log2(x) for \b integer arguments using simple bit shift arithmetic: + +\code +#include + +std::cout << stxxl::ilog2_floor(10000) << std::endl; +std::cout << stxxl::ilog2_ceil(10000) << std::endl; +\endcode + +The third and probably least useful is to use conversion to \b double and \c math.h's facilities: + +\code +#include + +std::cout << stxxl::log2_floor(10000) << std::endl; +std::cout << stxxl::log2_ceil(10000) << std::endl; +\endcode + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_types Signed and Unsigned Integer Types + +STXXL provides two very important types: \ref stxxl::int_type and \ref stxxl::unsigned_type. These should be used for general counting and indexing types, as they are defined to be the size of a register on the machines: on 32-bit machines the two types are 4 bytes size, while on a 64-bit machine the two types are 8 bytes in size! + +The previous types are for general purpose counting. For real 64-bit integers, also on 32-bit machines, STXXL also provides a stxx::uint64 type (independent of other headers). + +See the file common/types.h + +\section common_types_uint uint40 and uint48 Unsigned Integer Types + +When storing file offsets in external memory, one often does not require full 64-bit indexes. Mostly, 40-bit or 48-bit are sufficient, if only < 1 TiB or < 16 TiB of data are processed. If one stores theses integers in five or six bytes, the total I/O volume can be reduced significantly. + +Since this issue occurs commonly in EM algorithms, STXXL provides two types: stxxl::uint40 and stxxl::uint48. + +See the file common/uint_types.h + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_misc_macros Miscellaneous Macros + +\section namespaces STXXL_BEGIN_NAMESPACE + +A long, long time ago, not all compilers supported C++ namespaces, thus STXXL uses the macros \ref STXXL_BEGIN_NAMESPACE and \ref STXXL_END_NAMESPACE, which open and close the \c "namespace stxxl". + +\section unused STXXL_UNUSED + +\ref STXXL_UNUSED is not actually a macro. It is a remedy against "unused variable" warnings, for whatever reason. Usage: + +\code +void function(int x) +{ + STXXL_UNUSED(x); +} +\endcode + +\section likely LIKELY and UNLIKEY + +Some compilers have facilities to specify whether a condition is likely or unlikely to be true. This may have consequences on how to layout the assembler code better. + +\code +if (LIKELY(x > 1)) { ... } +if (UNLIKELY(x > 8)) { ... } +\endcode + +\section deprecated Deprecated Functions + +Some compilers can warn the user about deprecated function by tagging them in the source. In STXXL we use the macro \ref STXXL_DEPRECATED(...) to enclose deprecated functions. + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_misc_funcs Miscellaneous Functions + +\section parse_filesize Parsing Filesizes with K, M, G suffixes + +Since with STXXL one often has to parse large file or disk sizes, there is a function called \ref parse_SI_IEC_size(), which accepts strings like "1 GiB" or "20 TB" as input. + +See the \ref install_config documentation page on the format of accepted file size strings. + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/** \page common_io_counter I/O Performance Counter + +The STXXL library provides various I/O performance counters (stxxl::stats class) which can be used to get an extensive set of I/O statistics. They can be accessed as follows: + +\code + // generate stats instance + stxxl::stats * Stats = stxxl::stats::get_instance(); + + // start measurement here + stxxl::stats_data stats_begin(*Stats); + + // some computation ... + + // substract current stats from stats at the beginning of the measurement + std::cout << (stxxl::stats_data(*Stats) - stats_begin); +\endcode + +The Stats ostream holds various measured I/O data: + +\verbatim +STXXL I/O statistics + total number of reads : 2 + average block size (read) : 2097152 (2.000 MiB) + number of bytes read from disks : 4194304 (4.000 MiB) + time spent in serving all read requests : 0.062768 s @ 63.7268 MiB/s + time spent in reading (parallel read time) : 0.062768 s @ 63.7268 MiB/s + total number of writes : 2 + average block size (write) : 2097152 (2.000 MiB) + number of bytes written to disks : 4194304 (4.000 MiB) + time spent in serving all write requests : 0.0495751 s @ 80.6857 MiB/s + time spent in writing (parallel write time): 0.0495751 s @ 80.6857 MiB/s + time spent in I/O (parallel I/O time) : 0.112343 s @ 71.2104 MiB/s + I/O wait time : 0.104572 s + I/O wait4read time : 0.054934 s + I/O wait4write time : 0.049638 s + Time since the last reset : 0.605008 s +\endverbatim + +We can access individual I/O data in contrast to the whole content of Stats ostream by: + +\code +std::cout << Stats->get_written_volume() << std::endl; // print number of bytes written to the disks +\endcode + +\b Hint: There's another statistical parameter which may be in developer's interest: the maximum number of bytes (the peak) allocated in external memory during program run. This parameter can be accessed by: + +\code +stxxl::block_manager * bm = stxxl::block_manager::get_instance(); +// lots of external work here... +std::cout << "max: " << bm->get_maximum_allocation() << std::endl; // max. number of bytes allocated until now +\endcode + +See \ref stxxl::stats and \ref stxxl::stats_data class reference for all provided individual functions. + +*/ + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/design.dox b/third-party/MQF/ThirdParty/stxxl/doc/design.dox new file mode 100644 index 0000000000..f4063200b2 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/design.dox @@ -0,0 +1,1264 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/design.dox + * + * Design of STXXL, all of this is from Roman's PhD thesis. + * Edited by Timo Bingmann + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Roman Dementiev + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page design Design of STXXL + +\author Roman Dementiev (2006) + +STXXL is a layered library consisting of three layers (see following figure). The lowest layer, the Asynchronous I/O primitives layer (AIO layer), abstracts away the details of how asynchronous I/O is performed on a particular operating system. Other existing external memory algorithm libraries only rely on synchronous I/O APIs \cite CraMeh99 or allow reading ahead sequences stored in a file using the POSIX asynchronous I/O API \cite tpie_manual. These libraries also rely on uncontrolled operating system I/O caching and buffering in order to overlap I/O and computation in some way. However, this approach has significant performance penalties for accesses without locality. Unfortunately, the asynchronous I/O APIs are very different for different operating systems (e.g. POSIX AIO and Win32 Overlapped I/O). Therefore, we have introduced the AIO layer to make porting STXXL easy. Porting the whole library to a different platform requires only reimplementing the AIO layer using native file access methods and/or native multithreading mechanisms. + +\image html layer_diagram.png "The STXXL library structure" + +STXXL already has several implementations of the AIO layer which use different file access methods under POSIX/UNIX and Windows systems. Porting STXXL to Windows took only a few days. The main efforts were spent for writing the AIO layer using the native Windows calls. Rewriting the thread-related code was easy provided the Boost thread library; its interfaces are similar to POSIX threads. There were little header file and compiler-specific incompatibilities; those were solved by conditional compilation using the C++ preprocessor. The POSIX version of STXXL had run immediately on the all listed operating systems after changing some Linux-specific header file includes to more common POSIX headers. + +The Block Management layer (BM layer) provides a programming interface emulating the \b parallel disk model. The BM layer provides an abstraction for a fundamental concept in the external memory algorithm design --- a block of elements. The block manager implements block allocation/deallocation, allowing several block-to-disk assignment strategies: striping, randomized striping, randomized cycling, etc. The block management layer provides an implementation of parallel disk buffered writing \cite HutSanVit01b, optimal prefetching \cite HutSanVit01b, and block caching. The implementations are fully asynchronous and designed to explicitly support overlapping between I/O and computation. + +The top of STXXL consists of two modules. The STL-user layer provides external memory sorting, external memory stack, external memory priority queue, etc. which have (almost) the same interfaces (including syntax and semantics) as their STL counterparts. The Streaming layer provides efficient support for \b pipelining external memory algorithms. Many external memory algorithms, implemented using this layer, can save a factor of 2--3 in I/Os. For example, the algorithms for external memory suffix array construction implemented with this module \cite DKMS05 require only 1/3 of the number of I/Os which must be performed by implementations that use conventional data structures and algorithms (either from STXXL STL-user layer, LEDA-SM, or TPIE). The win is due to an efficient interface that couples the input and the output of the algorithm--components (scans, sorts, etc.). The output from an algorithm is directly fed into another algorithm as input, without needing to store it on the disk in-between. This generic pipelining interface is the first of this kind for external memory algorithms. + +\section aio_layer The Asynchronous I/O primitives Layer + +The purpose of the AIO layer is to provide a unified approach to asynchronous I/O. The layer hides details of native asynchronous I/O interfaces of an operating system. Studying the patterns of I/O accesses of external memory algorithms and data structures, we have identified the following functionality that should be provided by the AIO layer: + +- To issue read and write requests without having to wait for them to be completed. + +- To wait for the completion of a subset of issued I/O requests. + +- To wait for the completion of at least one request from a subset of issued I/O requests. + +- To poll the completion status of any I/O request. + +- To assign a callback function to an I/O request which is called upon I/O completion (asynchronous notification of completion status), with the ability to co-relate callback events with the issued I/O requests. + +The AIO layer exposes two user objects: \ref stxxl::file and \ref stxxl::request_ptr. Together with the I/O waiting functions \ref stxxl::wait_all, \ref stxxl::wait_any, and \ref stxxl::poll_any they provide the functionality mentioned above. Using a \ref stxxl::file object, the user can submit asynchronous read and asynchronous write requests (methods \ref stxxl::file::aread and stxxl::file::awrite). These methods return a \ref stxxl::request_ptr object which is used to track the status of the issued request. The AIO layer functions \ref stxxl::wait_all, \ref stxxl::wait_any, and \ref stxxl::poll_any facilitate tracking a set of \ref stxxl::request_ptr s. The last parameter of the methods \ref stxxl::file::aread and \ref stxxl::file::awrite is a reference to a callback function object (callback functor). The functor's \c operator()(request_ptr) method is called when the I/O request is completed. + +As a part of the AIO layer, the STXXL library provides various I/O performance counters (\ref stxxl::stats class). The class counts the number and the duration of the performed I/O operations as well as the transferred volume. Read and write operations are counted separately. STXXL also measures the time spent by the processing thread(s) waiting for the completions of I/Os (I/O wait time). This metric helps to evaluate the degree and the impact of overlapping between I/O and computation in an application. + + +The following listing shows a simple example of how to use AIO objects to perform asynchronous I/O. All STXXL library objects are defined in the namespace stxxl. For convenience, in line 1 we bring all names from the STXXL namespace to the local scope. In Line 8 a file object \c myfile is constructed. \ref stxxl::syscall_file is an implementation of the STXXL \ref stxxl::file interface which uses UNIX/POSIX \c read and \c write system calls to perform I/O. The file named "storage" in the current directory is opened in read-only mode. In line 9 an asynchronous read of the 1 MB region of the file starting at position 0 is issued. The data will be read into the array \c mybuffer. When the read operation is completed, my_handler::operator() will be called with a pointer to the completed request. The execution stops at line 11 waiting for the completion of the issued read operation. Note that the work done in the function do_something1() is overlapped with reading. When the I/O is finished, one can process the read buffer (line 12) and free it (line 13). + +\code +struct my_handler { // I/O completion handler + void operator () (stxxl::request_ptr ptr) { + std::cout << "Request '" << *ptr << "' completed." << std::endl; + } +}; +char * mybuffer = new char[1024*1024]; // allocate 1MB buffer +stxxl::syscall_file myfile("./storage", stxxl::file::RDONLY); +stxxl::request_ptr myreq = myfile.aread(mybuffer, 0, 1024*1024, my_handler()); +do_something1(); // do_something1() is overlapped with reading +myreq->wait(); // wait for read completion +do_something2(mybuffer);// process the read buffer +delete [] mybuffer; // free the buffer +\endcode + +\subsection aio_impl AIO Layer Implementations + +There are several implementation strategies for the STXXL AIO layer. Some asynchronous I/O related APIs (and underlying libraries implementing them) already exist. The most well known framework is POSIX AIO, which has an implementation on almost every UNIX/POSIX system. Its disadvantage is that it has only limited support for I/O completion event mechanism The Linux AIO kernel side implementation (http://freshmeat.net/projects/linux-aio/) of POSIX AIO does not have this deficit, but is not portable since it works under Linux only. + +The STXXL AIO layer follows a different approach. It does not rely on any asynchronous I/O API. Instead we use synchronous I/O calls running asynchronously in separate threads. For each file there is one read and one write request queue and one thread. The main thread posts requests (invoking \ref stxxl::file::aread() and \ref stxxl::file::awrite() methods) to the file queues. The thread associated with the file executes the requests in FIFO order. This approach is very flexible and it does not suffer from limitations of native asynchronous APIs. + +Our POSIX implementation of the AIO layer is based on POSIX threads and supports several Unix file access methods: the \c syscall method uses \c read and \c write system calls, the \c mmap method uses memory mapping (\c mmap and \c munmap calls), the \c sim_disk method simulates I/O timings of a hard disk provided a big internal memory. To avoid superfluous copying of data between the user and kernel buffer memory, the \c syscall method has the option to use unbuffered file system access. These file access methods can also be used for raw disk I/O, bypassing the file system. In this case, instead of files, raw device handles are open. The read/write calls using direct access (\c O_DIRECT option) have shown the best performance under Linux. The disadvantage of the \c mmap call is that programs using this method have less control over I/O: In most operating systems 4 KBytes data pages of a mmaped file region are brought to the main memory "lazily", only when they are accessed for the first time. This means if one mmaps a 100 KBytes block and touches only the first and the last element of the block then \b two I/Os are issued by the operating system. This will slow down many I/O-efficient algorithms, since for modern disks the seek time is much longer than the reading of 100 KBytes of contiguous data. + +The POSIX implementation does not need to be ported to other UNIX compatible systems, since POSIX threads is the standard threading API on all POSIX-compatible operating systems. + +Our Windows implementation is based on Boost threads, whose interfaces are very similar to POSIX threads. + +AIO file and request implementation classes are derived from the generic \ref stxxl::file and \ref stxxl::request interface classes with C++ pure virtual functions. These functions are specialized for each access method in implementation classes to define the read, write, wait for I/O completion and other operations. The desired access method implementation for a file is chosen dynamically at running time. One can add the support of an additional access method (e.g. for a DAFS distributed filesystem) just providing classes implementing the \ref stxxl::file and \ref stxxl::request interfaces. We have decided to use the virtual function mechanism in the AIO layer because this mechanism is very flexible and will not sacrifice the performance of the library, since the virtual functions of the AIO layer need to be called only once per \b large chunk of data (i.e. \a B bytes). The inefficiencies of C++ virtual functions are well known. Similar to STL, the higher layers of STXXL do not rely on the running time polymorphism with virtual functions to avoid the high per-element penalties. + +\section mng_layer The Block-Management Layer + +The Block-Management (BM) layer provides an implementation of the central concept in I/O efficient algorithms and data structures: a block of elements (\ref stxxl::typed_block object). Besides, it includes a toolbox for allocating, deallocating, buffered writing, prefetching, and caching of blocks. The external memory manager (object \ref stxxl::block_manager) is responsible for allocating and deallocating external memory space on disks. The manager supports four parallel disk allocation strategies: simple striping, fully randomized, simple randomized \cite BarGroVit97, and randomized cycling \cite VitHut01. + +The BM layer also delivers a set of helper classes that efficiently implement frequently used sequential patterns of interaction with the (parallel disk) external memory. The optimal parallel disk queued writing \cite HutSanVit01b is implemented in the \ref stxxl::buffered_writer class. The class operates on blocks. The \ref stxxl::buf_ostream class is build on top of \ref stxxl::buffered_writer and has a high level interface, similar to the interface of STL output iterators. Analogously, the classes \ref stxxl::block_prefetcher and \ref stxxl::buf_istream contain an implementation of an optimal parallel disk \b prefetching algorithm \cite HutSanVit01b. The helper objects of the BM layer support overlapping between I/O and computation, which means that they are able to perform I/O in the background, while the user thread is doing useful computations. + +The BM layer views external memory as a set of large AIO files --- one for each disk. We will refer to these files as \b disks. The other approach would be to map a related subset of blocks (e.g. those belonging to the same data structure) to a separate file. This approach has some performance problems. One of them is that since those (numerous) files are created dynamically, during the run of the program, the file system allocates the disk space on demand, that might in turn introduce severe uncontrolled disk space fragmentation. Therefore we have chosen the "one-large-file-per-disk" approach as our major scheme. However, the design of our library does not forbid data structures to store their content in separate user data files (e.g., as an option, \ref stxxl::vector can be mapped to a user file). + +The external memory manager (object \ref stxxl::block_manager) is responsible for allocating and deallocating external memory space on the disks. The \ref stxxl::block_manager reads information about available disks from the STXXL configuration file. This file contains the location of each disk file, the sizes of the disks, and the file access methods for each disk. When allocating a bunch of blocks, a programmer can specify how the blocks will be assigned to disks, passing an allocation strategy function object. The \ref stxxl::block_manager implements the "first-fit" allocation heuristic \cite BicShaw2003. When an application requests several blocks from a disk, the manager tries to allocate the blocks contiguously. This reduces the bulk access time. + +On allocation requests, the \ref stxxl::block_manager returns \ref stxxl::BID objects -- Block IDentifiers. An object of the type \ref stxxl::BID describes the physical location of an allocated block, including the disk and offset of a region of storage on disk. One can load or store the data that resides at the location given by the \ref stxxl::BID using asynchronous \c read and \c write methods of a \ref stxxl::typed_block object. + +The full signature of the STXXL "block of elements" class is \ref stxxl::typed_block The C++ template parameter RawSize defines the total size of the block in bytes. Since block size is not a single global constant in the STXXL namespace, a programmer can simultaneously operate with several block types having different blocks sizes. Such flexibility is often required for good performance. For example, B+-tree leaves might have a size different from the size of the internal nodes. We have made the block size a template parameter and not a member variable for the sake of efficiency. The values of the template parameters are known to the compiler, therefore for the power of two values (a very common choice) it can replace many arithmetic operations, like divisions and multiplications, by more efficient binary shifts. A critical requirement for many external memory data structures is that a block must be able to store links to other blocks. An STXXL block can store \c NRef objects of type \ref stxxl::BID. Additionally, one can equip a block with a field of the type \c InfoType, that can hold some per-block information. Block elements of type \c T can easily be accessed by the array operator [] and via random access iterators. The maximum number of elements available a block depends on the number of links and the sizes of \c T, \c InfoType and \c BID types. This number is accessible as \ref stxxl::typed_block::size. + +In the following listing, we give an example of how to program block I/O using objects of the BM layer. In line 2 we define the type of block: its size is one megabyte and the type of elements is \c double. The pointer to the only instance of the singleton object \ref stxxl::block_manager is obtained in line 5. Line 6 asks the block manager to allocate 32 blocks in external memory. The new_blocks call writes the allocated BIDs to the output iterator, given by the last parameter. The std::back_inserter iterator adapter will insert the output BIDs at the end of the array \c bids. The manager assigns blocks to disks in a round-robin fashion as the striping() strategy suggests. Line 7 allocates 32 internal memory blocks. The internal memory allocator \ref stxxl::new_alloc\ of STXXL allocates blocks on a virtual memory page boundary, which is a requirement for unbuffered file access. Along lines 8--10 the elements of blocks are filled with some values. Then, the blocks are submitted for writing (lines 11-12). The request objects are stored in an std::vector for the further status tracking. As in the AIO example, I/O is overlapped with computations in the function do_something(). After the completion of all write requests (line 15) we perform some useful processing with the written data (function do_something1()). Finally we free the external memory space occupied by the 32 blocks (line 18). + +\code +typedef stxxl::typed_block<1024*1024,double> block_type; +std::vector bids; //empty array of BIDs +std::vector requests; +stxxl::block_manager * bm = stxxl::block_manager::get_instance (); +bm->new_blocks(32, stxxl::striping(), std::back_inserter(bids)); +std::vector< block_type, new_alloc > blocks(32); +for (int ii = 0; ii < 32; ii++) + for (int jj=0; jj < block_type::size; jj++) + blocks[ii][jj] = some_value(ii,jj); +for (int i = 0; i < 32; i++) + requests.push_back( blocks[i].write(bids[i]) ); +do_something(); // do_something() is overlapped with writing +// wait until all I/Os finish +stxxl::wait_all(requests.begin(), requests.end()); +do_something1(bids.begin(),bids.end()); +// deallocate external memory +bm->delete_blocks(bids.begin(), bids.end()); +\endcode + +# The STL-User Layer + +The documentation of \subpage design_stl "The STL-User Layer" is on a separate subpage. + +# The Algorithm Pipelining Layer + +The \subpage design_pipeline "Streaming layer" provides efficient support for external memory algorithms with mostly \b sequential I/O pattern, i.e. scan, sort, merge, etc. A user algorithm, implemented using this module can save many I/Os. The win is due to an efficient interface, that couples the input and the output of the algorithms-components (scans, sorts, etc.). The output from an algorithm is directly fed into another algorithm as the input, without the need to store it on the disk. + +# Common Helpers and Utilities + +Beyond the layered library, STXXL contains many small helpers commonly used in C++ like random numbers or shared pointers. See \ref common for short descriptions. + +*/ + +/** \page design_stl The STL-User Layer + +\author Roman Dementiev (2006) + +The STL layer of STXXL is composed of two large parts: containers and algorithms. + +- \subpage design_stl_containers "Containers" store elements, usually in external memory, but still follow the same interface as STL containers. See + +- \subpage design_stl_algo "Algorithms" operate, like STL algorithms, on iterators provided by the containers. + +*/ + +/** \page design_stl_containers STXXL Containers + +\author Roman Dementiev (2006) + +\section General Issues Concerning STXXL Containers + +STXXL has the restriction that the data types stored in the containers cannot have C/C++ pointers or references to other elements of external memory containers. The reason is that these pointers and references get invalidated when the blocks containing the elements they point/refer to are written to disk. To get around this problem, the links can be kept in the form of external memory iterators (e.g. \ref stxxl::vector::iterator). The iterators remain valid while storing to and loading from the external memory. When dereferencing an external memory iterator, the pointed object is loaded from external memory by the library on demand (if the object is not in the cache of the data structure already). + +STXXL containers differ from STL containers in treating allocation and distinguishing between uninitialized and initialized memory. STXXL containers assume that the data types they store are plain old data types (POD). The constructors and destructors of the contained data types are not called when a container changes its size. The support of constructors and destructors would imply a significant I/O cost penalty, e.g. on the deallocation of a non-empty container, one has to load all contained objects and call their destructors. This restriction sounds more severe than it is, since external memory data structures cannot cope with custom dynamic memory management anyway, which is the common use of custom constructors/destructors. However, we plan to implement special versions of STXXL containers which will support not only PODs, and handle construction/destruction appropriately. + +# STXXL Containers + +STXXL library was designed to ease the access to external memory algorithms and data structures for a programmer. We decided to equip our implementations of out-of-memory data structure and algorithms with well known generic interfaces of internal memory data structures and algorithms from the Standard Template Library. Currently we have implementation of the following data structures (in STL terminology "containers"): + +- \subpage design_vector "stxxl::vector" +- \subpage design_stack "stxxl::stack" +- \subpage design_queue "stxxl::queue" +- \subpage design_deque "stxxl::deque" +- \subpage design_map "stxxl::map" +- \subpage design_unordered_map "stxxl::unordered_map" + +Beyond these, STXXL also provides a set of containers that are not part of the STL: + +- \subpage design_pqueue "stxxl::priority_queue" +- \subpage design_matrix "stxxl::matrix" +- \subpage design_sorter "stxxl::sorter" +- \subpage design_sequence "stxxl::sequence" + +*/ + +/** \page design_vector Vector + +\author Roman Dementiev (2006) + +The most universal STXXL container is \ref stxxl::vector. Vector is an array whose size can vary dynamically. The implementation of \ref stxxl::vector is similar to the LEDA-SM array \cite CraMeh99. The content of a vector is striped block-wise over the disks, using an assignment strategy given as a template parameter. Some of the blocks are cached in a vector cache of fixed size (also a parameter). The replacement of cache blocks is controlled by a specified page-replacement strategy. STXXL has implementations of LRU and random replacement strategies. The user can provide his/her own strategy as well. The \ref stxxl::vector has STL-compatible Random Access Iterators. + +- One random access costs \f$ \mathcal{O}(1) \f$ I/Os in the worst case. Same for insertion and removal at the end. +- Sequential scanning of the vector costs \f$ \mathcal{O}(1/DB) \f$ amortized I/Os per vector element. + +\section design_vector_architecture The Architecture of stxxl::vector + +The \ref stxxl::vector is organized as a collection of blocks residing on the external storage media (parallel disks). Access to the external blocks is organized through the fully associative \a cache which consist of some fixed amount of in-memory pages (The page is a collection of consecutive blocks. The number of blocks in the page is constant.). The schema of \ref stxxl::vector is depicted in the following figure. When accessing an element the implementation of \ref stxxl::vector access methods ([]operator, \c push_back, etc.) first checks whether the page to which the requested element belongs is in the vector's cache. If it is the case the reference to the element in the cache is returned. Otherwise the page is brought into the cache (If the page of the element has not been touched so far, this step is skipped. To keep an eye on such situations there is a special flag for each page.). If there was no free space in the cache, then some page is to be written out. Vector maintains a \a pager object, that tells which page to kick out. STXXL provides LRU and random paging strategies. The most efficient and default one is LRU. For each page vector maintains the \a dirty flag, which is set when \a non-constant reference to one of the page's elements was returned. The dirty flag is cleared each time when the page is read into the cache. The purpose of the flag is to track whether any element of the page is modified and therefore the page needs to be written to the disk(s) when it has to be evicted from the cache. + +\image html vector_architecture_small.png "The schema of stxxl::vector that consists of ten external memory pages and has a cache with the capacity of four pages. The first cache page is mapped to external page 1, the second page is mapped to external page 8, and the fourth cache page is mapped to page 5. The third page is not assigned to any external memory page." + +In the worst case scenario when vector elements are read/written in the random order each access takes 2 x blocks_per_page I/Os. The factor \a two shows up here because one has to write the replaced from cache page and read the required one). However the scanning of the array costs about \f$ n/B \f$ I/Os using constant vector iterators or const reference to the vector, where \a n is the number of elements to read or write (read-only access). Using non-const vector access methods leads to \f$ 2 \times n/B \f$ I/Os because every page becomes dirty when returning a non const reference. If one needs only to sequentially write elements to the vector in \f$ n/B \f$ I/Os the currently fastest method is \ref stxxl::generate. Sequential writing to an untouched before vector (e.g. when created using stxxl::vector(size_type n) constructor.) or alone adding elements at the end of the vector, using the push_back(const T\&) method, leads also to \f$ n/B \f$ I/Os. + +\code +// Example of use +stxxl::vector V; +V.push_back(3); +assert(V.size() == 1 && V.capacity() >= 1 && V[0] == 3); +\endcode + +\section design_vector_generator stxxl::VECTOR_GENERATOR + +Besides the type of the elements stxxl::vector has many other template parameters (block size, number of blocks per page, pager class, etc.). To make the configuration of the vector type easier STXXL provides special type generator template meta programs for its containers. + +The meta-program for \ref stxxl::vector is called \ref stxxl::VECTOR_GENERATOR. + +\code +// Example of use +typedef stxxl::VECTOR_GENERATOR::result vector_type; +vector_type V; +V.push_back(3); +assert(V.size() == 1 && V.capacity() >= 1 && V[0] == 3); +\endcode + +The \ref stxxl::VECTOR_GENERATOR has the following template parameters: + +\copydetails stxxl::VECTOR_GENERATOR + +### Notes: + +- All blocks of a page are read and written from/to disks together. Therefore to increase the I/O bandwidth, it is recommended to set the PageSize parameter to multiple of \a D. + +- Memory consumption of constructed vector is BlockSize x CachePages x PageSize bytes (see below). + +- The configured vector type is available as \ref VECTOR_GENERATOR<>::result. + +- Since there are defaults for the last five of the parameters, it is not necessary to specify them all. + +- Supported parallel disk assignment strategies: +strategy | identifier +------------------ | ---------- +striping | striping +simple randomized | SR +fully randomized | FR +randomized cycling | RC + +- Supported paging strategies: +strategy | identifier +------------------- | ------ +random | random +least recently used | lru + +### Examples: + +- VECTOR_GENERATOR::result -- external vector of \b double's with four blocks per page, the cache with eight pages, 2 MiB blocks, Random Allocation and lru cache replacement strategy. + +- VECTOR_GENERATOR::result -- external vector of \b double's , with \b eight blocks per page, the cache with eight pages, 2 MiB blocks, Random Allocation and lru cache replacement strategy + +- VECTOR_GENERATOR::result -- external vector of \b double's, with \b eight blocks per page, the cache +with \b two pages, \b 512 KiB blocks, Simple Randomized allocation and lru cache replacement strategy + +\section design_vector_memory Internal Memory Consumption of stxxl::vector + +The cache of \ref stxxl::vector largely dominates in its internal memory consumption. Other members consume very small fraction of \ref stxxl::vector's memory even when the vector size is large. Therefore, the internal memory consumption of \ref stxxl::vector can be estimated as \f$ BlockSize \times CachePages \times PageSize \f$ bytes. + +\section design_vector_notes More Notes + +- In opposite to STL, \ref stxxl::vector's iterators do not get invalidated when the vector is resized or reallocated. + +- Dereferencing a non-const iterator makes the page of the element to which the iterator points to \b dirty. This causes the page to be written back to the disks(s) when the page is to be kicked off from the cache (additional write I/Os). If you do not want this behavior, use const iterators instead. See following example: +\code +vector_type V; +// ... fill the vector here + +vector_type::iterator iter = V.begin(); +// ... advance the iterator +a = *iter; // causes write I/Os, although *iter is not changed + +vector_type::const_iterator citer = V.begin(); +// ... advance the iterator +a = *citer; // read-only access, causes no write I/Os +*citer = b; // does not compile, citer is const +\endcode + +- Non const \c operator[] makes the page of the element \b dirty. This causes the page to be written back to the disks(s) when the page is to be kicked off from the cache (additional write I/Os). If you do not want this behavior, use const \c operator[]. For that you need to access the vector via a const reference to it. Example: +\code +vector_type V; +// ... fill the vector here + +a = V[index]; // causes write I/Os, although V[index] is not changed + +const vector_type & CV = V; // const reference to V +a = CV[index]; // read-only access, can cause no write I/Os +CV[index] = b; // does not compile, CV is const +\endcode +This issue also concerns \c front() and \c back() methods. + +*/ + +/** \page design_stack Stack + +\author Roman Dementiev (2006) + +The I/O efficient stack is perhaps the simplest external memory data structure. Stacks provide only restricted subset of sequence operations: insertion, removal, and inspection of the element at the top of the stack. Stacks are a "last in first out" (LIFO) data structures: the element at the top of a stack is the one that was most recently added. Stacks does not allow iteration through its elements. + +The basic variant of EM stack keeps the top \a k elements in the main memory buffer, where \f$ k \leq 2B \f$. If the buffers get empty on a removal call, one block is brought from the disk to the buffers. Therefore at least \a B removals are required to make one I/O reading a block. Insertions cause no I/Os until the internal buffers get full. In this case to make space the first \a B elements are written to the disk. Thus a block write happens only after at least \a B insertions. If we choose the unit of disk transfer to be a multiple of DB (we denote it as a \a page), set the stack buffer size to 2D pages, and evenly assign the blocks of a page to disks we obtain the following amortized running times of the basic operations of stxxl::stack + +operation | internal work | I/O (amortized) +-------------------- | ---------------------- | ------------------------- +insertion at the end | \f$ \mathcal{O}(1) \f$ | \f$ \mathcal{O}(1/DB) \f$ +removal at the end | \f$ \mathcal{O}(1) \f$ | \f$ \mathcal{O}(1/DB) \f$ + +The STXXL library contains four different variants of stacks, each implementation is specialized for a certain access pattern: + +1. The \ref stxxl::normal_stack is a general purpose implementation which is the best if the access pattern to the stack is an irregular mix of push'es and pop's, i.e. the stack grows and shrinks without a certain rule. + +2. The \ref stxxl::grow_shrink stack is a stack that is optimized for an access pattern where the insertions are (almost) not intermixed with the removals, and/or vice versa, the removals are (almost) not intermixed with the insertions. In short, the stack first grows to its maximal size, then shrinks, then grow again and so on, a pattern which can be described by \f$ (push^{r_i}, push^{r_j})^k \f$ with \f$ 1 \leq j \leq k \f$ and large values for \f$ r_i \f$ and \f$ r_j \f$. + +3. \ref stxxl::grow_shrink2 stack is a "grow-shrink" stack that allows the use of common prefetch and write buffer pools. The pools are shared between several "grow-shrink" stacks. + +4. \ref stxxl::migrating stack is a stack that migrates from internal memory to external memory when its size exceeds a certain threshold (determined by parameter migrating_critical_size) + +To make use of stxxl::stack, one can use the generator template \ref stxxl::STACK_GENERATOR which expects the parameters from left to right as shown in the table below. + +\section design_stack_normal stxxl::normal_stack + +The \ref stxxl::normal_stack is a general purpose implementation of the external memory stack. The stack has two pages, the size of the page in blocks is a configuration constant and can be given as a template parameter. The implementation of the methods follows the description given in the previous section. + +## Internal Memory Consumption of stxxl::normal_stack + +The cache of \ref stxxl::normal_stack largely dominates in its internal memory consumption. Other members consume very small fraction of \ref stxxl::normal_stack's memory even when the stack size is large. Therefore, the internal memory consumption of \ref stxxl::normal_stack can be estimated as \f$ 2 \times BlockSize \times PageSize \f$ bytes, where \f$ BlockSize \f$ is the block size and \f$ PageSize \f$ is the page size in blocks (see \ref design_stack). + +\section design_stack_grow_shrink stxxl::grow_shrink_stack + +The \ref stxxl::grow_shrink_stack specialization is optimized for an access pattern where the insertions are (almost) not intermixed with the removals, and/or vice versa, the removals are (almost) not intermixed with the insertions. In other words the stack first grows to its maximal size, then it shrinks, then it might again grow, then shrink, and so forth, i.e. the pattern is \f$ (push^{i_j}pop^{r_j})^k \f$, where \f$ k \in N \f$, \f$ 1 \leq j \leq k \f$, and \f$ i_j \f$, \f$ r_j \f$ are \a large. The implementation efficiently exploits the knowledge of the access pattern that allows \b prefetching the blocks beforehand while the stack shrinks and buffered writing while the stack grows. Therefore the \b overlapping of I/O and computation is possible. + +## Internal Memory Consumption of stxxl::grow_shrink_stack + +The cache of \ref stxxl::grow_shrink_stack largely dominates in its internal memory +consumption. Other members consume very small fraction of +\ref stxxl::grow_shrink_stack's +memory even when the stack size is large. Therefore, the internal +memory consumption of stxxl::grow_shrink_stack can be estimated as +\f$ 2 \times BlockSize \times PageSize \f$ bytes, where \f$ BlockSize \f$ is the +block size and \f$ PageSize \f$ is the page size in blocks (see \ref design_stack). + +## Members of stxxl::grow_shrink_stack + +The \ref stxxl::grow_shrink_stack has the same set of members as the \ref stxxl::normal_stack. The running times of \ref stxxl::grow_shrink_stack are the same as \ref stxxl::normal_stack except that when the stack switches from growing to shrinking (or from shrinking to growing) \f$ PageSize \f$ I/Os can be spent additionally in the worst case. (This is for the single disk setting, if the page is perfectly striped over parallel disk the number of I/Os is \f$ PageSize \cdot D \f$.) + +\section design_stack_grow_shrink2 stxxl::grow_shrink_stack2 + +The \ref stxxl::grow_shrink_stack2 is optimized for the same kind of access pattern as \ref stxxl::grow_shrink_stack. The difference is that each instance of \ref stxxl::grow_shrink_stack uses an own internal buffer to overlap I/Os and computation, but \ref stxxl::grow_shrink_stack2 is able to share the buffers from the pool used by several stacks. + +## Internal Memory Consumption of stxxl::grow_shrink_stack2 + +Not counting the memory consumption of the shared blocks from the pools, the stack alone consumes about \f$ BlockSize \f$ bytes. It has a cache that consists of only a single block. + +## Members of stxxl::grow_shrink_stack2 + +The \ref stxxl::grow_shrink_stack2 has almost the same set of members as the \ref stxxl::normal_stack, except that it does not have the default constructor. The \ref stxxl::grow_shrink_stack2 requires prefetching and write pool objects (see \ref stxxl::prefetch_pool, \ref stxxl::write_pool and \ref stxxl::read_write_pool) to be specified in the creation time. + +Consequently, the constructor requires a read_write_pool for prefetching and buffered writing. But it also has a second parameter, which tells how many blocks from the prefetching pool are used, this is called "prefetch_aggressiveness". + +\section design_stack_grow_migrating stxxl::migrating_stack + +The \ref stxxl::migrating_stack is a stack that migrates from internal memory to external when its size exceeds a certain threshold (template parameter). The implementation of internal and external memory stacks can be arbitrary and given as a template parameters. + +## Internal Memory Consumption of stxxl::migrating_stack + +The \ref stxxl::migrating_stack memory consumption depends on the memory consumption of the stack implementations given as template parameters. The current state is internal (external), the \ref stxxl::migrating_stack consumes almost exactly the same space as internal (external) memory stack implementation. (The \ref stxxl::migrating_stack needs only few pointers to maintain the switching from internal to external memory implementations.) + +## Members of stxxl::migrating_stack + +The \ref stxxl::migrating_stack extends the member set of \ref stxxl::normal_stack. Additionally, there are \ref stxxl::migrating_stack::internal() and \ref stxxl::migrating_stack::external(), which return true if the currently used implementation is internal or external. + +\section design_stack_generator stxxl::STACK_GENERATOR + +To provide an easy way to choose and configure the stack +implementations, STXXL offers a template meta program called +\ref stxxl::STACK_GENERATOR. + +The \ref stxxl::STACK_GENERATOR has the following template parameters: + +\copydetails stxxl::STACK_GENERATOR + +## Examples: + +- STACK_GENERATOR::result external stack of \c double's +- STACK_GENERATOR::result internal stack of \c double's +- STACK_GENERATOR::result external grow-shrink stack of \c double's +- STACK_GENERATOR::result migrating grow-shrink stack of \c double's, internal implementation is \c std::stack +- STACK_GENERATOR::result migrating grow-shrink stack of \c double's with 1 block per page and block size 512 KiB (total memory occupied = 1 MiB) + +## Example for stxxl::grow_shrink_stack2 + +TODO-df : but use read_write_pool instead of the two pools. + +\code +typedef STACK_GENERATOR::result stack_type; +typedef stack_type::block_type block_type; + +stxxl::prefetch_pool p_pool(10); // 10 read buffers +stxxl::write_pool w_pool(6); // 6 write buffers +stack_type S(p_pool,w_pool,0); // no read buffers used + +for(long long i = 0; i < max_value; ++i) + S.push(i); + +S.set_prefetch_aggressiveness(5); + +/* give a hint that we are going to shrink the stack from now on, +always prefetch 5 buffers beforehand */ + +for(long long i = 0; i < max_value; ++i) + S.pop(); + +S.set_prefetch_aggressiveness(0); +// stop prefetching + +\endcode + +*/ + +/** \page design_queue Queue + +\author Roman Dementiev (2006) + +STXXL also has an implementation of external memory FIFO \ref stxxl::queue. Its design is similar to \ref stxxl::grow_shrink_stack2. The implementation holds the head and the tail blocks in the main memory. Prefetch and write block pools might be used to overlap I/O and computation during \ref stxxl::queue operations. + +*/ + +/** \page design_deque Deque + +\author Roman Dementiev (2006) + +The STXXL implementation of external memory \ref stxxl::deque is an adaptor of an (external memory) vector. This implementation wraps the elements around the end of the vector \b circularly. It provides the pop/push operations from/to the both ends of the \ref stxxl::deque in \f$ \mathcal{O}(1/DB) \f$ amortized I/Os if parameterized with a properly configured \ref stxxl::vector. + +*/ + +/** \page design_pqueue Priority Queue + +\author Roman Dementiev (2006) + +A priority queue is a data structure that provides a restricted subset of container functionality: it provides insertion of elements, and inspection and removal of the top element. It is guaranteed that the top element is the largest element in the priority queue, where the function object Cmp is used for comparisons. Priority queues do not allow iteration through its elements. + +External memory priority queues are the central data structures for many I/O efficient graph algorithms (\cite ZehPhd \cite ChiEtAl95 \cite MSS03). The main technique in these algorithms is time-forward processing (\cite ChiEtAl95 \cite Arg95), easily realizable by an I/O efficient priority queue. I/O efficient priority queues also find application in large-scale discrete event simulation and online sorting. The STXXL implementation of priority queues is based on \cite San00b. An operation of this priority queue, called sequence heap, takes \f$ \mathcal{O}(\frac{1}{B}\log_{M/B}(I/B)) \f$ amortized I/Os, where \f$ I \f$ is the total number of insertions into the priority queue. This queue needs less than a third of I/Os used by other similar cache (I/O) efficient priority queues (e.g. \cite Brengel00 \cite FJKT97). The amortized run time of the STXXL priority queue are +operation | internal work | I/O (amortized) +--------- | --------------------------- | ------------------------ +insertion | \f$ \mathcal{O}(\log I) \f$ | \f$ \mathcal{O}(1/B) \f$ +deletion | \f$ \mathcal{O}(\log I) \f$ | \f$ \mathcal{O}(1/B) \f$ +where \f$ I \f$ is the number of performed operations. + +A sequence heap maintains \a R merge groups \f$ G_1,\ldots, G_R \f$ where \f$ G_i \f$ holds up to \a k sorted sequences of size up to \f$ m k^{i-1} \f$, \f$ m << M \f$, see following figure. When group \f$ G_i \f$ overflows, all its sequences are merged, and the resulting sequence is put into group \f$ G_{i+1} \f$. Each group is equipped with a group buffer of size \a m to allow batched deletion from the sequences. The smallest elements of these buffers are deleted in small batches and stored in the deletion buffer. The elements are first inserted into the insertion priority queue. On deletion, one checks the minimum elements stored in the insertion priority queue and the deletion buffer. + +The difference of our implementation to \cite San00b is that a number of larger merge groups are explicitly kept in external memory. The sorted sequences in those groups only hold their \b first blocks in the main memory. The implementation supports parallel disks and overlaps I/O and computation. As in \cite San00b, the internal merging is based on loser trees \cite Knu98. However, our implementation does not use \b sentinel elements. + +\image html san00b_pqueue_small.png "The structure of STXXL priority queue" + +\section design_pqueue_generator stxxl::PRIORITY_QUEUE_GENERATOR + +Since the \ref stxxl::priority_queue has many setup parameters (internal memory buffer sizes, arity of mergers, number of internal and external memory merger groups, etc.) which are difficult to guess, STXXL provides a helper meta template program that searches for the optimum settings for user demands. This is necessary to limit the amount of main memory occupied by the different buffers of the data structure. The program is called \ref stxxl::PRIORITY_QUEUE_GENERATOR. + +The \ref stxxl::PRIORITY_QUEUE_GENERATOR has the following template parameters: + +\copydetails stxxl::PRIORITY_QUEUE_GENERATOR + +## Notes + +- If \c CompareType(x,y) is true, then x is smaller than y. The element returned by \c Q.top() is the largest element in the priority queue. That is, it has the property that, for every other element \b x in the priority queue, \c CompareType(Q.top(),x) is false. \c CompareType must also provide \c min_value() method, that returns value of type ValueType that is smaller than any element of the queue \b x, i.e. \c CompareType(CompareType.min_value(),x) is always \b true.
+Example: comparison object for priority queue +where \c top() returns the \b smallest contained integer: +\code +struct CmpIntGreater +{ + bool operator () (const int & a, const int & b) const { return a > b; } + int min_value() const { return std::numeric_limits::max(); } +}; +\endcode +Example: comparison object for priority queue where \c top() returns the \b largest contained integer: +\code +struct CmpIntLess +{ + bool operator () (const int & a, const int & b) const { return a < b; } + int min_value() const { return std::numeric_limits::min(); } +}; +\endcode +Note that \c CompareType must define strict weak ordering. (see what it is) + +- stxxl::PRIORITY_QUEUE_GENERATOR is template meta program that searches for \b 7 configuration parameters of \b stxxl::priority_queue that both minimize internal memory consumption of the priority queue to match \c IntMemory and maximize performance of priority queue operations. Actual memory consumption might be larger (use \ref stxxl::priority_queue::mem_cons() method to track it), since the search assumes rather optimistic schedule of push'es and pop'es for the estimation of the maximum memory consumption. To keep actual memory requirements low increase the value of \c MaxItems parameter. + +- For functioning a priority queue object requires two pools of blocks (See constructor of \ref stxxl::priority_queue). To construct STXXL block pools you might need \b block_type that will be used by priority queue. Note that block's size and hence it's type is generated by the \ref stxxl::PRIORITY_QUEUE_GENERATOR in compile type from \c IntMemory, \c MaxItems and \c sizeof(ValueType) and not given directly by user as a template parameter. Block type can be extracted as \ref stxxl::PRIORITY_QUEUE_GENERATOR<>::result::block_type (see \ref tutorial_pqueue). + +- The configured priority queue type is available as \ref stxxl::PRIORITY_QUEUE_GENERATOR<>::result. + +\section design_pqueue_memory Internal Memory Consumption of stxxl::priority_queue + +Internal memory consumption of stxxl::priority_queue is bounded by the IntMemory template parameter in most situations. + +*/ + +/** \page design_map Map (B+-tree) + +\author Roman Dementiev (2006) + +\ref stxxl::map is an STL interface for search trees with unique keys. Our implementation of \ref stxxl::map is a variant of a B+-tree data structure \cite BM72 supporting the operations \c insert, \c erase, \c find, \c lower_bound and \c upper_bound in optimal \f$ \mathcal{O}(\log_{B}(n)) \f$ I/Os. Operations of \ref stxxl::map use \a iterators to refer to the elements stored in the container, e.g. \c find and \c insert return an iterator pointing to the data. Iterators are used for range queries: an iterator pointing to the smallest element in the range is returned by \c lower_bound, the element which is next to the maximum element in the range is returned by \c upper_bound. Scanning through the elements of the query can be done by using \c operator++ or \c operator-- of the obtained iterators in \f$ \mathcal{O}(R/B) \f$ I/Os, where \a R is the number of elements in the result. Our current implementation does not exploit disk parallelism. The flexibility of the iterator-based access has some circumstances for an external memory implementation: iterators must return correct data after reorganizations in the data structure even when the pointed data is moved to a different external memory block. + +The way how iterators are used for accessing a \ref stxxl::map is similar to the use of database cursors \cite BDB99. STXXL is the first C++ template library that provides an I/O-efficient search tree implementation with iterator-based access. + +In the following we briefly describe the architecture of the STXXL B+-tree implementation. A simplified UML class diagram of the implementation is depicted in the figure below. + +Our design allows to use different implementations for leaves and (internal) nodes. For example, leaves could be represented internally as sparse arrays \cite BDIW02 (currently, only the classic sorted array implementation is available). Leaves and nodes can have different external memory block sizes. Each leaf has links to the predecessor and successor leaves to speed up scanning. Our B+-tree is able to prefetch the neighbor leaves when scanning, obtaining a higher bandwidth by overlapping I/O and computation. The root node is always kept in the main memory and implemented as an std::map. To save I/Os, the most frequently used nodes and leaves are cached in corresponding node and leaf \a caches that are implemented in a single template class. An iterator keeps the block identifier (BID) of the external block where the pointed data element is contained, the offset of the data element in the block and a pointer to the B+-tree. In case of reorganizations of the data in external memory blocks (rebalancing, splitting, fusing blocks), all iterators pointing to the moved data have to be updated. For this purpose, the addresses of all instantiated iterators are kept in the iterator map object. The iterator map facilitates fast accounting of iterators, mapping BID and block offsets of iterators to its main memory addresses using an \a internal memory search tree. Therefore, the number of "alive" B+-tree iterators must be kept reasonably small. The parent pointers in leaves and nodes can be useful for finger search (The program can help the search tree finding an element by giving some "position close by" which was determined by an earlier search.) and insertions using a finger, however, that would require to store the whole B-tree path in the iterator data structure. This might make the iterator accounting very slow, therefore we do not maintain the parent links. The implementation can save I/Os when const_iterators are used: no flushing of supposedly changed data is needed (e.g. when scanning or doing other read-only operations). Our implementation of B+-tree supports bulk bottom-up construction from the presorted data given by an iterator range in \f$ \mathcal{O}(n/B) \f$ I/Os. + +\image html btree_uml_small.png "The simplified UML class diagram of the B+-tree implementation." + +*/ + +/** \page design_unordered_map Unordered Map + +There is currently no documentation here, see the tutorial \ref tutorial_unordered_map for some notes. + +The unordered_map/hash map library was created as a student project at the University of Karlsruhe. If you can read German, contact the maintainers for a copy of the student thesis. + +*/ + +/** \page design_matrix Matrix + +Currently no documentation here. + +The matrix library was created as a student project and documented in the following thesis: http://algo2.iti.kit.edu/english/1919.php + +*/ + +/** \page design_sequence Sequence + +The \ref stxxl::sequence container is an external memory deque without facilities for random access. + +One can push and pop on both ends of an \ref stxxl::sequence using the functions push_back(), pop_back, push_front() and pop_front. Only one block at each end is guaranteed to be in memory, but the sequence will automatically prefetch and overlap writes when many equal calls are done. + +There are no operator[] or other random access methods for the sequence. + +The only methods to access all elements are via streams: the sequence can be streamed from front-to-back and from back-to-front (in reverse). The functions \ref sequence::get_stream() and \ref sequence::get_reverse_stream() are convenience access to these streams. + +*/ + +/** \page design_sorter Sorter + +The \ref stxxl::sorter is a "sorting container": a set of items can be inserted in arbitrary order, and after all are inserted, the set can be read as a sorted stream. + +See the \ref tutorial_sorter tutorial for more information. + +*/ + +/** \page design_stl_algo STXXL Algorithms + +\author Roman Dementiev (2006) + +Iterators of \ref stxxl::vector are STL compatible. \ref stxxl::vector::iterator is a model of Random Access Iterator concept from STL. Therefore it is possible to use the \ref stxxl::vector iterator ranges with STL algorithms. However, such use is not I/O efficient if an algorithm accesses the sequence in a random order. For such kind of algorithms STXXL provides I/O efficient implementations described on this page. If an algorithm does only a scan (or a constant number of scans) of a sequence (or sequences) the implementation that calls STL algorithm is nevertheless I/O efficient. However, one can save constant factors in I/O volume and internal work if the the access pattern is known (read-only or write-only scan for example). This knowledge is used in STXXL specialized implementations of STL algorithms. + +Example: STL Algorithms Running on STXXL containers (do not do this, read below!) + +\code +typedef stxxl::VECTOR_GENERATOR::result vector_type; + +// Replace every number in an array with its negative. +const int N = 1000000000; +vector_type A(N); +std::iota(A.begin(), A.end(), 1); +std::transform(A, A+N, A, negate()); + +// Calculate the sum of two vectors, +// storing the result in a third vector. + +const int N = 1000000000; +vector_type V1(N); +vector_type V2(N); +vector_type V3(N); + +std::iota(V1.begin(), V1.end(), 1); +std::fill(V2.begin(), V2.end(), 75); + +assert(V2.size() >= V1.size() && + V3.size() >= V1.size()); +std::transform(V1.begin(), + V1.end(), + V2.begin(), + V3.begin(), + plus()); +\endcode + +The algorithms of the STL can be divided into two groups by their memory access pattern: scanning algorithms and random access algorithms. + +# Scanning Algorithms + +Scanning algorithms work with Input, Output, Forward, and Bidirectional iterators only. Since random access operations are not allowed with these kinds of iterators, the algorithms inherently exhibit a strong spatial locality of reference. STXXL containers and their iterators are STL-compatible, therefore one can directly apply STL scanning algorithms to them, and they will run I/O-efficiently (see the use of \c std::generate and \c std::unique algorithms in the listing above). + +Scanning algorithms are the majority of the STL algorithms (62 out of 71). STXXL also offers specialized implementations of some scanning algorithms, which perform better in terms of constant factors in the I/O volume and internal CPU work. These implementations benefit from accessing lower level interfaces of the BM layer instead of using iterator interfaces, resulting in a smaller CPU overhead. Being aware of the sequential access pattern of the applied algorithm, the STXXL implementations can do prefetching and use queued writing, thereby leading to the overlapping of I/O with computation. + +STXXL provides the following scanning algorithms: +- \subpage design_algo_generate +- \subpage design_algo_foreach +- \subpage design_algo_foreachm +- \subpage design_algo_find + +# Random Access Algorithms + +Random access algorithms require random access iterators, hence may perform (many) random I/Os. For such algorithms, STXXL provides specialized I/O efficient implementations that work with STL-user layer external memory containers. Currently, the library provides two implementations of sorting: +- an std::sort-like sorting routine: \subpage design_algo_sort "stxxl::sort", and +- a sorter that exploits integer keys -- \subpage design_algo_ksort "stxxl::ksort". + +Both sorters are implementations of parallel disk algorithms described in \subpage design_algo_sorting \cite DemSan03. + +*/ + +/** \page design_algo_sorting Parallel Disk Sorting + +\author Roman Dementiev (2006) + +The development of STXXL has been started with sorting, because it is \c the fundamental tool for I/O-efficient processing of large data sets. Therefore, an efficient implementation of sorting largely defines the performance of an external memory software library as a whole. To achieve the best performance our implementation \cite DemSan03 uses parallel disks, has an optimal I/O volume \f$ \mathcal{O}(\frac{N}{DB}\log_{M/B}\frac{N}{B}) \f$ (that matches the lower bound), and guarantees almost perfect overlap between I/O and computation. + +No previous implementation has all these properties, which are needed for a good practical sorting. LEDA-SM \cite CraMeh99 and TPIE \cite APV02 concentrate on single disk implementations. For the overlapping of I/O and computation they rely on prefetching and caching provided by the operating system, which is suboptimal since the system knows little about the application's access pattern. + +Barve and Vitter implemented a parallel disk algorithm \cite BarGroVit97 that can be viewed as the immediate ancestor of our algorithm. Innovations with respect to our sorting are: A different allocation strategy that enables better theoretical I/O bounds \cite HutSanVit01b \cite KalVar01; a prefetching algorithm that optimizes the number of I/O steps and never evicts data previously fetched; overlapping of I/O and computation; a completely asynchronous implementation that reacts flexibly to fluctuations in disk speeds; and an implementation that sorts many GBytes and does not have to limit internal memory size artificially to obtain a nontrivial number of runs. Additionally, our implementation is not a prototype, it has a generic interface and is a part of the software library STXXL. + +Algorithms in \cite Raj98 \cite ChaCor02 \cite ChaCorWis01 have the theoretical advantage of being deterministic. However, they need three passes over data even for not too large inputs. + +Prefetch buffers for disk load balancing and overlapping of I/O and computation have been intensively studied for external memory merge sort (\cite PaiVar92 \cite CaoFelKarLi96 \cite AlbGarLeo98 \cite HutSanVit01b \cite KalVar01 \cite KimKar00). But we have not seen results that guarantee overlapping of I/O and computation during the parallel disk merging of arbitrary runs. + +There are many good practical implementations of sorting (e.g. \cite NBCGL94 \cite Aga96 \cite NKG00 \cite Wyl99) that address parallel disks, overlapping of I/O and computation, and have a low internal overhead. However, we are not aware of fast implementations that give theoretical performance guarantees on achieving asymptotically optimal I/O. Most practical implementations use a form of striping that requires \f$ \Omega(\frac{N}{DB}\log_{\Theta(\frac{M}{DB})}\frac{N}{B}) \f$ I/Os rather than the optimal \f$ \Theta(\frac{N}{DB}\log_{\Theta(M/B)}\frac{N}{B}) \f$. This difference is usually considered insignificant for practical purposes. However, already on our experimental system we have to go somewhat below the block sizes that give the best performance if the input size is 128~GBytes. Another reduction of the block size by a factor of eight (we have eight disks) could increase the run time significantly. We are also not aware of high performance implementations that guarantee overlap of I/O and computation during merging for inputs such as the one described in \ref design_algo_sorting_merging. + +On the other hand, many of the practical merits of our implementation are at +least comparable with the best current implementations: We are close to the +peak performance of our system. + +\section design_algo_sorting_overlapping Multi-way Merge Sort with Overlapped I/Os + +Perhaps the most widely used external memory sorting algorithm is k-way merge sort: During run formation, chunks of \f$ \Theta(M) \f$ elements are read, sorted internally, and written back to the disk as sorted \a runs. The runs are then merged into larger runs until only a single run is left. \f$ k = \mathcal{O}(M/B) \f$ runs can be sorted in a single pass by keeping up to \a B of the smallest elements of each run in internal memory. Using randomization, prediction of the order in which blocks are accessed, a prefetch buffer of \f$ \mathcal{O}(D) \f$ blocks, and an optimal prefetching strategy, it is possible to implement k-way merging using \a D disks in a load balanced way \cite HutSanVit01b. However, the rate at which new blocks are requested is more difficult to predict so that this algorithm does not guarantee overlapping of I/O and computation. In this section, we derive a parallel disk algorithm that compensates these fluctuations in the block request rate by a FIFO buffer of \f$ k+\Theta(D) \f$ blocks. + +\subsection design_algo_sorting_runform Run Formation + +There are many ways to overlap I/O and run formation. We start with a very simple method that treats internal sorting as a black box and therefore can use the fastest available internal sorters. Two threads cooperate to build \a k runs of size \f$ M/2 \f$: + +\verbatim +post a read request for runs 1 and 2 +thread A: | thread B: +for r:=1 to k do | for r:=1 to k-2 do + wait until | wait until + run r is read | run r is written + sort run r | post a read for run r+2 + post a write for run r | +\endverbatim + +\image html overlapping_runformation_small.png "Overlapping I/O and computation during run formation." + +The figure above illustrates how I/O and computation is overlapped by this algorithm. Formalizing this figure, we can prove that using this approach an input of size \a N can be transformed into sorted runs of size \f$ M/2 - \mathcal{O}(DB) \f$ in time \f$ \max(2T_{\mathrm{sort}}(M/2)N/M,\frac{2LN}{DB}) + \mathcal{O}(\frac{LM}{DB}) \f$, where \f$ T_{\mathrm{sort}}(x) \f$ denotes the time for sorting \a x elements internally and where \a L is the time needed for a parallel I/O step. + +In \cite DemSan03 one can find an algorithm which generates longer runs of average length \f$ 2M \f$ and overlaps I/O and computation. + +\subsection design_algo_sorting_multiway Multi-way Merging + +We want to merge \a k sorted sequences comprising \a N' elements stored in \f$ N'/B \f$ blocks (In practical situations, where a single merging phase suffices, we will have \f$ N'=N \f$). In each iteration, the merging thread chooses the smallest remaining element from the \a k sequences and hands it over to the I/O thread. Prediction of read operations is based on the observation that the merging thread does not need to access a block until its smallest element becomes the smallest unread element. We therefore record the \b smallest keys of each block during run formation. By merging the resulting \a k sequences of smallest elements, we can produce a sequence \f$ \sigma \f$ of block identifiers that indicates the exact order in which blocks are logically read by the merging thread. The overhead for producing and storing the prediction data structure is negligible because its size is a factor at least \a B smaller than the input. + +The prediction sequence \f$ \sigma \f$ is used as follows. The merging thread maintains the invariant that it always buffers the \a k first blocks in \f$ \sigma \f$ that contain unselected elements, i.e., initially, the first \a k blocks from \f$ \sigma \f$ are read into these merge buffers. When the last element of a merge buffer block is selected, the now empty buffer frame is returned to the I/O thread and the next block in \f$ \sigma \f$ is read. + +The keys of the smallest elements in each buffer block are kept in a tournament tree data structure \cite Knu98 so that the currently smallest element can be selected in time \f$ \mathcal{O}(\log k) \f$. Hence, the total internal work for merging is \f$ \mathcal{O}(N'\log k) \f$. + +We have now defined multi-way merging from the point of view of the sorting algorithm. Our approach to merging slightly deviates from previous approaches that keep track of the run numbers of the merge blocks and pre-assign each merge block to the corresponding input sequence. In these approaches also the \b last key in the \b previous block decides about the position of a block in \f$ \sigma \f$. The correctness of our approach is shown in \cite DemSan03. With respect to performance, both approaches should be similar. Our approach is somewhat simpler, however --- note that the merging thread does not need to know anything about the \a k input runs and how they are allocated. Its only input is the prediction sequence \f$ \sigma \f$. In a sense, we are merging individual blocks and the order in \f$ \sigma \f$ makes sure that the overall effect is that the input runs are merged. A conceptual advantage is that data \b within a block decides about when a block is read. + +\subsection design_algo_sorting_merging Overlapping I/O and Merging + +\image html overlapping_merging_small.png "Data flow of overlapped parallel disk multi-way merging." + +Although we can predict the order in which blocks are read, we cannot easily predict how much internal work is done between two reads. For example, consider \a k identical runs storing the sequence \f$ \fboxsep0.5mm\framebox{$1^{B-1}2$}\framebox{$3^{B-1}4$}\framebox{$5^{B-1}6$} \cdots \f$. After initializing the merge buffers, the merging thread will consume \f$ k(B-1) \f$ values '1' before it posts another read. Then it will post one read after selecting each of the next \a k values (2). Then there will be a pause of another \f$ k(B-1) \f$ steps and another \a k reads are following each other quickly, etc. We explain how to overlap I/O and computation despite this irregularity using the I/O model of Aggarwal and Vitter \cite AggVit88 that allows access to \a D \b arbitrary blocks within one I/O step. To model overlapping of I/O and computation, we assume that an I/O step takes time \a L and can be done in parallel with internal computations. We maintain an overlap buffer that stores up to \f$ k+3D \f$ blocks in a FIFO manner (see figure above). Whenever the overlap buffer is non-empty, a read can be served from it without blocking. Writing is implemented using a write buffer FIFO with \f$ 2DB \f$ elements capacity. An I/O thread inputs or outputs \a D blocks in time \a L using the following strategy: Whenever no I/O is active and at least \f$ DB \f$ elements are present in the write buffer, an output step is started. When no I/O is active, less than \a D output blocks are available, and at least \a D overlap buffers are unused, then the next \a D blocks from \f$ \sigma \f$ are fetched into the overlap buffer. This strategy guarantees that merging \a k sorted sequences with a total of \a N' elements can be implemented to run in time \f$ \max\left(\frac{2LN'}{DB}, \ell N'\right)+\mathcal{O}(L\lceil\frac{k}{D}\rceil) \f$ where \f$ \ell \f$ is the time needed by the merging thread to produce one element of output and \a L is the time needed to input or output \a D arbitrary blocks \cite DemSan03. + +\subsection design_algo_sorting_scheduling Disk Scheduling + +The I/Os for the run formation and for the output of merging are perfectly balanced over all disks if all sequences are \b striped over the disks, i.e., sequences are stored in blocks of \a B elements each and the blocks numbered \f$ i,\ldots,i+D-1 \f$ in a sequence are stored on different disks for all \a i. In particular, the original input and the final output of sorting can use any kind of striping. + +The merging algorithm presented above is optimal for the unrealistic model of Aggarwal and Vitter \cite AggVit88 which allows to access any \a D blocks in an I/O step. This facilitates good performance for fetching very irregularly placed input blocks. However, this model can be simulated using \a D independent disks using randomized striping allocation \cite VitHut01 and a prefetch buffer of size \f$ m = \Theta(D) \f$ blocks: In almost every input step, \f$ (1-\mathcal{O}(D/m))D \f$ blocks from prefetch sequence \f$ \sigma \f$ can be fetched \cite DemSan03. + +\section design_algo_sorting_impl Implementation Details + +Run Formation. We build runs of a size close to \f$ M/2 \f$ but there are some differences to the simple algorithm from \ref design_algo_sorting_runform. Overlapping of I/O and computation is achieved using the call-back mechanism supported by the I/O layer. Thus, the sorter remains portable over different operating systems with different interfaces to threading. + +We have two implementations with respect to the internal work: \ref stxxl::sort is a comparison based sorting using std::sort from STL to sort the runs internally; \ref stxxl::ksort exploits integer keys and has smaller internal memory bandwidth requirements for large elements with small key fields. After reading elements using DMA (i.e. the STXXL direct access), we extract pairs \f$ (\mathrm{key},\mathrm{pointerToElement}) \f$, sort these pairs, and only then move elements in sorted order to write buffers from where they are output using DMA. + +Furthermore, we exploit random keys. We use two passes of MSD (most significant digit) radix sort of the key-pointer pairs. The first pass uses the \a m most significant bits where \a m is a tuning parameter depending on the size of the processor caches and of the TLB (translation look-aside buffer). This pass consists of a counting phase that determines bucket sizes and a distribution phase that moves pairs. The counting phase is fused into a single loop with pair extraction. The second pass of radix sort uses a number of bits that brings us closest to an expected bucket size of two. This two-pass algorithm is much more cache efficient than a one-pass radix sort. (On our system we get a factor of 3.8 speedup over the one pass radix sort and a factor of 1.6 over STL's sort which in turn is faster than a hand tuned quicksort (for sorting \f$ 2^{21} \f$ pairs storing a random four byte key and a pointer). The remaining buckets are sorted using a comparison based algorithm: Optimal straight line code for \f$ n \leq 4 \f$, insertion sort for \f$ n \in \{ 5..16 \} \f$, and quicksort for \f$ n > 16 \f$. + +Multi-way Merging. We have adapted the tuned multi-way merger from \cite San00b, i.e. a tournament tree stores pointers to the current elements of each merge buffer. + +Overlapping I/O and Computation. We integrate the prefetch buffer and the overlap buffer to a read buffer. We distribute the buffer space between the two purposes of minimizing disk idle time and overlapping I/O and computation indirectly by computing an optimal prefetch sequence for a smaller buffer space. + +Asynchronous I/O. I/O is performed without any synchronization between the disks. The prefetcher computes a sequence \f$ \sigma' \f$ of blocks indicating the order in which blocks should be fetched. As soon as a buffer block becomes available for prefetching, it is used to generate an asynchronous read request for the next block in \f$ \sigma' \f$. The I/O layer of STXXL queues this request at the disk storing the block to be fetched. The thread for this disk serves the queued request in FIFO manner. All I/O is implemented without superfluous copying. STXXL opens files with the option \c O_DIRECT so that blocks are directly moved by DMA (direct memory access) to user memory. A fetched block then travels to the prefetch/overlap buffer and from there to a merge buffer simply by passing a pointer. Similarly, when an element is merged, it is directly moved from the merge buffer to the write buffer and a block of the write buffer is passed to the output queue of a disk simply by passing a pointer to the the I/O layer of STXXL that then uses \c write to output the data using DMA. + +*/ + +/** \page design_algo_sort stxxl::sort -- Sorting Comparison-Based + +\author Roman Dementiev (2006) + +\ref stxxl::sort is an external memory equivalent to STL std::sort. The design and implementation of the algorithm is described in detail in \cite DemSan03. + +# Prototype + +\code +template < typename ExtIterator, + typename StrictWeakOrdering + > +void sort ( ExtIterator first, + ExtIterator last, + StrictWeakOrdering cmp, + unsigned_type M + ) +\endcode + +# Description + +\copydetails stxxl::sort + +# Requirements on Types + +- \c ExtIterator is a model of External Random Access Iterator (In STXXL currently only \ref stxxl::vector provides iterators that are models of External Random Access Iterator.). + +- \c ExtIterator is mutable. + +- \c StrictWeakOrdering is a model of \ref StrictWeakOrdering + +- \c ExtIterator's value type is convertible to \c StrictWeakOrdering's argument type. + +\section StrictWeakOrdering StrictWeakOrdering Comparison Concept + +Model of \b StrictWeakOrdering Comparison concept must: +- provide \b operator(a,b) that returns comparison result of two user types, must define strict weak ordering +- provide \b max_value method that returns a value that is strictly greater than all other objects of user type, +- provide \b min_value method that returns a value that is strictly less than all other objects of user type, +- \b Note: when using unsigned integral types as key in user types, the value 0 cannot be used as a key value of the data to be sorted because it would conflict with the sentinel value returned by \b min_value +- \b Note, that according to the \ref stxxl::sort requirements \c min_value() and \c max_value() can not be present in the input sequence. + +## Examples + +A comparator class for integers: \b my_less_int. +\code +struct my_less_int +{ + bool operator() (int a, int b) const + { + return a < b; + } + int min_value() const { return std::numeric_limits::min(); }; + int max_value() const { return std::numeric_limits::max(); }; +}; +\endcode + +A comparator class \b my_less, that could be instantiated as e.g. \c my_less, \c my_less, etc. +\code +template +struct my_less +{ + typedef ValueType value_type; + bool operator() (const value_type & a, const value_type & b) const + { + return a < b; + } + value_type min_value() const { return std::numeric_limits::min(); }; + value_type max_value() const { return std::numeric_limits::max(); }; +}; +\endcode + +# Preconditions + +[first, last) is a valid range. + +# Complexity + +- Internal work: \f$ \mathcal{O}( N \log N ) \f$, where +\f$ N = (last - first) \cdot \texttt{sizeof(ExtIterator::value\_type)} \f$. + +- I/O complexity: \f$ (2N/DB)(1 + \lceil {\log}_{M/B}(2N/M) \rceil) \f$ I/Os + +stxxl::sort chooses the block size (parameter \a B) equal to the block size of the container, the last and first iterators pointing to (e.g. stxxl::vector's block size). + +The second term in the I/O complexity accounts for the merge phases of the external memory sorting algorithm \cite DemSan03. Avoiding multiple merge phases speeds up the sorting. In practice one should choose the block size \a B$of the container to be sorted such that there is only one merge phase needed: \f$ \lceil {\log}_{M/B}(2N/M) \rceil) = 1 \f$. This is possible for \f$ M > DB \f$ and \f$ N < M^2/2DB \f$. But still this restriction gives a freedom to choose a variety of blocks sizes. The study \cite DemSan03 has shown that optimal \a B for sorting lies in the range \f$ [M^2/(4N),3M^2/(8N)] \f$. With such choice of the parameters the \ref stxxl::sort always performs \f$ 4N/DB \f$ I/Os. + +# Internal Memory Consumption + +The \ref stxxl::sort consumes slightly more than \a M bytes of internal memory. + +# External Memory Consumption + +The \ref stxxl::sort is not in-place. It requires about \a N bytes of external memory to store the sorted runs during the sorting process \cite DemSan03. After the sorting this memory is freed. + +# Example + +\code +struct MyCmp: public std::less // ascending order +{ + static int min_value() const + { return std::numeric_limits::min(); } + static int max_value() const + { return std::numeric_limits::max(); } +}; +typedef stxxl::VECTOR_GENERATOR::result vec_type; + +vec_type V; +// ... fill here the vector with some values + +// Sort in ascending order use 512 MiB of main memory +stxxl::sort(V.begin(), V.end(), MyCmp(), 512*1024*1024); +// sorted +\endcode + +# Sorted Order Checking + +STXXL gives an ability to automatically check the order in the output of STXXL sorters and intermediate results of sorting (the order and a meta information in the sorted runs). The check is switched on if the source codes and the library are compiled with the option -DSTXXL_CHECK_ORDER_IN_SORTS and the option -DNDEBUG is not used. For details see the compiler.make file in the STXXL tar ball. Note, that the checking routines require more internal work as well as additional \f$ N/DB \f$ I/Os to read the sorted runs. Therefore for the final non-debug version of a user application on should switch this option off. + +This checker checks the \ref stxxl::sort, \ref stxxl::ksort, and the \ref design_stream_pipesort "pipelined sorter". + +*/ + +/** \page design_algo_ksort stxxl::ksort -- Sorting Integer Keys + +\author Roman Dementiev (2006) + +\ref stxxl::ksort is a specialization of external memory sorting optimized for records having integer keys. + +# Prototype + +\code +template < typename ExtIterator > +void ksort ( ExtIterator first, + ExtIterator last, + unsigned_type M + ) + +template < typename ExtIterator, typename KeyExtractor > +void ksort ( ExtIterator first, + ExtIterator last, + KeyExtractor keyobj, + unsigned_type M + ) +\endcode + +# Description + +\copydetails stxxl::ksort + +# Requirements on Types + +- \c ExtIterator is a model of External Random Access Iterator. (In STXXL currently only \ref stxxl::vector provides iterators that are models of External Random Access Iterator.) + +- \c ExtIterator is mutable. + +- \c KeyExtractor must implement \c operator() that extracts the key of an element and provide min and max values for the elements in the input, see \ref design_algo_ksort_key_extractor. + +- \c ExtIterator's value type is convertible to \c KeyExtractor's argument type. + +- \c ExtIterator's value type has a typedef \c key_type. + +- For the first version of \ref stxxl::ksort \c ExtIterator's value type must have a \c key() function that returns the key value of the element, and the \c min_value() and \c max\_value() member functions that return minimum and maximum element values respectively.
+Example: +\code +struct MyType +{ + typedef unsigned long long key_type; + key_type m_key; + char m_data[32]; + MyType() {} + MyType(key_type k) : m_key(k) {} + key_type key() { return m_key; } + MyType min_value() const + { return MyType( std::numeric_limits::min() ); } + MyType max_value() const + { return MyType( std::numeric_limits::max() ); } +}; +\endcode + +\section design_algo_ksort_key_extractor Key Extractor Concept + +A model of the Key Extractor concept must: +- define type \b key_type for the type of the keys. +- provide \b operator() that returns key value of an object of user type. +- provide \b max_value method that returns a value that is strictly greater than all other objects of user type in respect to the key obtained by this key extractor, +- provide \b min_value method that returns a value that is strictly less than all other objects of user type in respect to the key obtained by this key extractor, +- operator >, operator <, operator == and operator != on type \b key_type must be defined. +- \b Note: when using unsigned integral types as key, the value 0 cannot be used as a key value because it would conflict with the sentinel value returned by \c min_value. +- \b Note, that according to the stxxl::sort requirements \c min_value and \c max_value can not be present in the input sequence. + +# Examples + +A key extractor object for ordering elements having 64 bit integer keys: +\code +struct MyType +{ + typedef unsigned long long key_type; + key_type m_key; + char m_data[32]; + MyType() {} + MyType(key_type k) : m_key(k) {} +}; +struct GetKey +{ + typedef MyType::key_type key_type; + key_type operator() (const MyType & obj) + { return obj.m_key; } + MyType min_value() const + { return MyType( std::numeric_limits::min() ); } + MyType max_value() const + { return MyType( std::numeric_limits::max() ); } +}; +\endcode + +A key extractor class \b GetWeight, that extracts weight from an \b Edge: +\code +struct Edge +{ + unsigned src, dest, weight; + Edge(unsigned s, unsigned d, unsigned w) : src(s), dest(d), weight(w) {} +}; + +struct GetWeight +{ + typedef unsigned key_type; + key_type operator() (const Edge & e) const + { + return e.weight; + } + Edge min_value() const { return Edge(0, 0, std::numeric_limits::min()); } + Edge max_value() const { return Edge(0, 0, std::numeric_limits::max()); } +}; +\endcode + +# Preconditions + +The same as for \ref design_algo_sort "stxxl::sort". + +# Complexity + +The same as for \ref design_algo_sort "stxxl::sort". + +# Internal Memory Consumption + +The same as for \ref design_algo_sort "stxxl::sort". + +# External Memory Consumption + +The same as for \ref design_algo_sort "stxxl::sort". + +# Example + +\code +struct MyType +{ + typedef unsigned long long key_type; + key_type m_key; + char m_data[32]; + MyType() {} + MyType(key_type k) : m_key(k) {} + key_type key() { return obj.m_key; } + static MyType min_value() const + { return MyType( std::numeric_limits::min() ); } + static MyType max_value() const + { return MyType( std::numeric_limits::max()); } +}; + +typedef stxxl::VECTOR_GENERATOR::result vec_type; + +vec_type V; +// ... fill here the vector with some values + +// Sort in ascending order use 512 MiB of main memory +stxxl::ksort(V.begin(), V.end(), 512*1024*1024); +// sorted +\endcode + +*/ + +/** \page design_algo_generate stxxl::generate + +\author Roman Dementiev (2006) + +The semantics of the algorithm are equivalent to the STL std::generate. + +# Prototype + +\code +template < typename ExtIterator, typename Generator > +void generate ( ExtIterator first, + ExtIterator last, + Generator generator, + int_type nbuffers + ) +\endcode + +# Description + +\copydetails stxxl::generate + +# Requirements on types + +- \c ExtIterator is a model of External Random Access Iterator. +- \c ExtIterator is mutable. +- \c Generator is a model of a STL Generator. +- \c Generator's result type is convertible to \c ExtIterator's value type. + +# Preconditions + +[first, last) is a valid range. + +# Complexity + +- Internal work is linear. +- External work: close to \f$ N/DB \f$ I/Os (write-only). + +# Example + +\code +// Fill a vector with random numbers, using the +// standard C library function rand. +typedef stxxl::VECTOR_GENERATOR::result vector_type; +vector_type V(some_size); +// use 20 buffer blocks +stxxl::generate(V.begin(), V.end(), rand, 20); +\endcode + +*/ + +/** \page design_algo_foreach stxxl::for_each + +\author Roman Dementiev (2006) + +The semantics of the algorithm is equivalent to the STL std::for_each. + +# Prototype + +\code +template < typename ExtIterator, typename UnaryFunction > +UnaryFunction for_each ( ExtIterator first, + ExtIterator last, + UnaryFunction functor, + int_type nbuffers + ) +\endcode + +# Description + +\copydetails stxxl::for_each + +# Requirements on types + +- \c ExtIterator is a model of External Random Access Iterator. +- \c UnaryFunction is a model of STL Unary Function. +- \c UnaryFunction does not apply any non-constant operations through its argument. +- \c ExtIterator's value type is convertible to \c UnaryFunction's argument type. + +# Preconditions + +[first, last) is a valid range. + +# Complexity + +- Internal work is linear. +- External work: close to \f$ N/DB \f$ I/Os (read-only). + +# Example + +\code +template +struct print : public unary_function +{ + print(ostream& out) : os(out), count(0) {} + void operator() (T x) { os << x << ' '; ++count; } + ostream& os; + int count; +}; +typedef stxxl::VECTOR_GENERATOR::result vector_type; +int main() +{ + vector_type A(N); + // fill A with some values + // ... + + print P = stxxl::for_each(A.begin(), A.end(), print(cout)); + cout << endl << P.count << " objects printed." << endl; +} +\endcode + +*/ + +/** \page design_algo_foreachm stxxl::for_each_m (mutating) + +\author Roman Dementiev (2006) + +\ref stxxl::for_each_m is a \b mutating version of \ref stxxl::for_each, i.e. the restriction that Unary Function \c functor can not apply only constant operations through its argument does not exist. + +# Prototype + +\code +template < typename ExtIterator, typename UnaryFunction > +UnaryFunction for_each_m ( ExtIterator first, + ExtIterator last, + UnaryFunction functor, + int nbuffers + ) +\endcode + +# Description + +\copydetails stxxl::for_each_m + +# Requirements on types + +- \c ExtIterator is a model of External Random Access Iterator. +- \c UnaryFunction is a model of STL Unary Function. +- \c ExtIterator's value type is convertible to \c UnaryFunction's argument type. + +# Preconditions + +[first, last) is a valid range. + +# Complexity + +- Internal work is linear. +- External work: close to \f$ 2N/DB \f$ I/Os (read and write). + +# Example + +\code +struct AddX +{ + int x; + AddX(int x_): x(x_) {} + void operator() (int & val) + { val += x; } +}; + +typedef stxxl::VECTOR_GENERATOR::result vector_type; +int main() +{ + vector_type A(N); + // fill A with some values ... + + // Add 5 to each value in the vector + stxxl::for_each_m(A.begin(), A.end(), AddX(5)); +} +\endcode + +*/ + +/** \page design_algo_find stxxl::find + +\author Roman Dementiev (2006) + +The semantics of the algorithm is equivalent to the STL std::find. + +# Prototype + +\code +template < typename ExtIterator, typename EqualityComparable > +ExtIterator find ( ExtIterator first, + ExtIterator last, + const EqualityComparable& value, + int_type nbuffers + ) +\endcode + +# Description + +\copydetails stxxl::find + +# Requirements on types + +- \c EqualityComparable is a model of STL EqualityComparable concept. +- \c ExtIterator is a model of External Random Access Iterator. +- \c Equality is defined between objects of type \c EqualityComparable and objects of \c ExtIterator's value type. + +# Preconditions + +[first, last) is a valid range. + +# Complexity + +- Internal work is linear. +- External work: close to \f$ N/DB \f$ I/Os (read-only). + +# Example + +\code +typedef stxxl::VECTOR_GENERATOR::result vector_type; + +vector_type V; +// fill the vector ... + +// find 7 in V +vector_type::iterator result = find(V.begin(), V.end(), 7); +if(result != V.end()) + std::cout << "Found at position " << (result - V.begin()) << std::endl; +else + std::cout << "Not found" << std::endl; +\endcode + +*/ + +namespace stream { + +/** \page design_pipeline Algorithm Pipelining + +\author Roman Dementiev (2006) + +The pipelined processing technique is very well known in the database world \cite SKS01. This page describes the abstract design of the stream package, see also \ref tutorial_stream. + +Usually, the interface of an external memory algorithm assumes that it reads the input from (an) external memory container(s) and writes output into (an) external memory container(s). The idea of pipelining is to equip the external memory algorithms with a new interface that allows them to feed the output as a data stream directly to the algorithm that consumes the output, rather than writing it to the external memory first. Logically, the input of an external memory algorithm does not have to reside in the external memory, rather, it could be a data stream produced by another external memory algorithm. + +Many external memory algorithms can be viewed as a data flow through a directed acyclic graph \f$ G \f$ with node set \f$ V = F \cup S \cup R \f$ and edge set \f$ E \f$. The file nodes \f$ F \f$ represent physical data sources and data sinks, which are stored on disks (e.g. in the external memory containers of the STL-user layer). A file node writes or/and reads one stream of elements. The streaming nodes \f$ S \f$ read zero, one or several streams and output zero, one or several new streams. Streaming nodes are equivalent to scan operations in non-pipelined external memory algorithms. The difference is that non-pipelined conventional scanning needs a linear number of I/Os, whereas streaming nodes usually do not perform any I/O, unless a node needs to access external memory data structures (stacks, priority queues, etc.). The sorting nodes \f$ R \f$ read a stream and output it in a sorted order. Edges \f$ E \f$ in the graph \f$ G \f$ denote the directions of data flow between nodes. The question "When is a pipelined execution of the computations in a data flow graph \f$ G \f$ possible in an I/O-efficient way?" is analyzed in \cite DKMS05. + +\section design_pipeline_streaming Streaming Layer + +The streaming layer provides a framework for the \b pipelined processing of large sequences. Many external memory algorithms implemented with the STXXL streaming layer save a factor of at least two in I/Os. To the best of our knowledge we are the first who apply the pipelining method systematically in the domain of external memory algorithms. We introduce it in the context of an external memory software library. + +In STXXL, all data flow node implementations have an \c stream interface which is similar to the STL Input iterators (Not be confused with the stream interface of the C++ \c iostream library.). As an input iterator, an \c stream object may be dereferenced to refer to some object and may be incremented to proceed to the next object in the stream. The reference obtained by dereferencing is read-only and must be convertible to the \c value_type of the \c stream. The concept of the \c stream also defines a boolean member function \c empty() which returns \c true iff the end of the stream is reached. + +Now we tabulate the valid expressions and the expression semantics of the \c stream concept in the style of the STL documentation. + +### Notation + +Symbol | Semantics | +--------------------- | --------------------------------------------- | +X, X1,...,Xn | A type that is a model of the stream | +T | The value type of X | +s, s1,...,sn | Object of type X, X1,...,Xn | +t | Object of type T | + +### Valid expressions + +Name | Expression | Type requirements | Return type | +------------------- | ----------------------- | ------------------------------------------------------------- | ------------------------- | +Constructor | X s(s1,...,sn) | s1,....,sn are convertible to X1\&,...,Xn\& | | +Dereference | *s | | Convertible to T | +Member access | s->m | T is a type for which t.m is defined | | +Preincrement | ++s | | X\& | +End of stream check | (*s).empty() | | bool | + +### Expression semantics + +Name | Expression | Precondition | Semantics | Postcondition | +------------- | ----------------------- | ----------------------------------------------------------- | --------------------------------| ------------------------------------------- | +Constructor | X s(s1,...,sn) | s1,...,sn are the \a n input streams of s | | | +Dereference | *s | s is incrementable | | | +Member access | s->m | s is incrementable | Equivalent to (*s).m | | +Preincrement | ++s | s is incrementable | | s is incrementable or past-the-end | + +The binding of a \c stream object to its input streams (incoming edges in a data flow graph \f$ G \f$) happens at compile time, i.e. statically. The other approach would be to allow binding at running time using the C++ virtual function mechanism. However this would result in a severe performance penalty because most C++ compilers are not able to inline virtual functions. To avoid this disadvantage, we follow the static binding approach using C++ templates. For example, assuming that streams s1,...,sn are already constructed, construction of stream \c s with constructor X::X(X1\& s1,..., Xn\& sn) will bind \c s to its inputs s1,...,sn. + +After creating all node objects, the computation starts in a "lazy" fashion, first trying to evaluate the result of the topologically latest node. The node reads its intermediate input nodes, element by element, using the dereference and increment operator of the \c stream interface. The input nodes proceed in the same way, invoking the inputs needed to produce an output element. This process terminates when the result of the topologically latest node is computed. This style of pipelined execution scheduling is I/O efficient, it allows to keep the intermediate results in-memory without needing to store them in external memory. + +The Streaming layer of the STXXL library offers generic classes which implement the functionality of sorting, file, and streaming nodes: + +- File nodes: Function streamify() serves as an adaptor that converts a range of ForwardIterators into a compatible \c stream. Since iterators of \ref stxxl::vector are RandomAccessIterators, streamify() can be used to read external memory. The set of (overloaded) materialize functions implement data sink nodes, they flush the content of a STXXL stream object to an output iterator. + + The library also offers specializations of streamify() and \ref stream::materialize for \ref stxxl::vector, which are more efficient than the generic implementations due to the support of overlapping between I/O and computation. + +- \anchor design_stream_pipesort Sort nodes: The Stream layer sort class is a generic pipelined sorter which has the interface of an \c stream. The input of the sorter may be an object complying to the \c stream interface. As the STL-user layer sorter, the pipelined sorter is an implementation of parallel disk merge sort \cite DemSan03 that overlaps I/O and computation. + + The implementation of \ref stream::sort relies on two classes that encapsulate the two phases of the algorithm: sorted run formation (class runs_creator) and run merging (runs_merger). The separate use of these classes breaks the pipelined data flow: the runs_creator must read the entire input to compute the sorted runs. This facilitates an efficient implementation of loops and recursions: the input for the next iteration or recursion can be the sorted runs stored on disks (\cite JensThesis \cite DKMS05). + + The templated class runs_creator has several specializations which have input interfaces different from the \c stream interface: a specialization where elements to be sorted are push_back'ed into the runs_creator object and a specialization that accepts a set of presorted sequences. All specializations are compatible with the runs_merger. + +- Streaming nodes: In general, most of the implementation effort for algorithms with the streaming layer goes to the streaming nodes. The STXXL library exposes generic classes that help to accelerate coding the streaming node classes. For example \ref stream::transform is similar to the std::transform algorithm: it reads \a n input streams s1,...,sn and returns the result of a user-given n-ary function object functor(*s1,...,*sn) as the next element of the output stream until one of the input streams gets empty. + +As mentioned above, STXXL allows streaming nodes to have more than one output. In this case, only one output of a streaming node can have the \c stream interface (it is an iterator). The other outputs can be passed to other nodes using a "push-item" interface. Such an interface have file nodes (e.g. the method \c push_back of \ref stxxl::vector) and sorting nodes (push_back-specializations). Streaming nodes do not have such methods by definition, however, it is always possible to reimplement all streaming nodes between sorting and/or file nodes as a single streaming node that will \c push_back the output elements to the corresponding sorting/file nodes. + +*/ + +} // namespace stream + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/doxygen-extra.css b/third-party/MQF/ThirdParty/stxxl/doc/doxygen-extra.css new file mode 100644 index 0000000000..2a818d2f0a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/doxygen-extra.css @@ -0,0 +1,43 @@ +/* Customization of doxygen's style for STXXL */ + +/* space out lines slightly */ +body { + line-height: 1.20em; +} + +/* increase size of code snippets */ +div.fragment { + padding: 4px 6px; +} + +div.fragment div.line { + line-height: 135%; +} + +/* make heading smaller and quit justifying */ +h1 { font-size: 16pt; margin: 12pt 0px 8pt 0px; text-align: left; } +h2 { font-size: 15pt; margin: 12pt 0px 8pt 0px; text-align: left; } +h3 { font-size: 14pt; margin: 12pt 0px 8pt 0px; text-align: left; } +h4 { font-size: 14pt; margin: 10pt 0px 6pt 0px; text-align: left; } + +/* limit maximum width of text content */ +div.contents { + max-width: 120ex; + text-align: justify; + hyphens: auto; + moz-hyphens: auto; + o-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; +} + +/* make links more blue */ +a { + color: #3D4A9C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #3D4A9C; +} diff --git a/third-party/MQF/ThirdParty/stxxl/doc/faq.dox b/third-party/MQF/ThirdParty/stxxl/doc/faq.dox new file mode 100644 index 0000000000..603e6a39cc --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/faq.dox @@ -0,0 +1,161 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/faq.dox + * + * Frequently asked and answered questions + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +/** \page faq FAQ - Frequently Asked Questions + +\section faq_latest Latest version of this FAQ + +The most recent version of this FAQ can always be found here. + +\section faq_compilers Supported Compilers and Platforms + +The following compilers have been tested in different \c STXXL configurations. Other compilers might work, too, but we don't have the resources (systems, compilers or time) to test them. Feedback is welcome. + +Please note that from STXXL 1.4.0 on, only 64-bit systems are fully supported. Compilation on 32-bit seems to work, but we cannot support it anymore. + +The compilers marked with '*' are the maintainers' favorite choices and are most thoroughly tested. + +compiler | supported options +----------------------- | -------------------------- +gcc 4.9.1 | stxxl parallel (boost) (c++11) +gcc 4.8.3 * | stxxl parallel (boost) (c++11) +gcc 4.7.3 | stxxl parallel (boost) (c++0x) +gcc 4.6.4 | stxxl parallel (boost) (c++0x) +gcc 4.5.4 | stxxl parallel (boost) (c++0x) +gcc 4.4.7 | stxxl parallel (boost) (c++0x) +gcc 4.3.6 | stxxl (boost) +gcc 4.1.2 | stxxl (boost) +gcc 3.4.6 | stxxl (boost) +gcc 3.3 | unsupported +icpc 2015.0.090 * | stxxl (boost) (c++0x) +icpc 2013.5.192 * | stxxl (boost) (c++0x) +icpc 2011.13.367 | stxxl (boost) (c++0x) +clang++ 3.2, 3.3, 3.4.2 | stxxl (boost) (c++0x) +mingw-w64 gcc 4.8.3 | stxxl parallel (boost) (c++11) +cygwin gcc 4.8.3 | stxxl parallel (boost) (c++11) +msvc 2013 12.0 * | stxxl (boost) (c++11) +msvc 2012 11.0 | stxxl (boost) (c++0x) +msvc 2010 10.0 | stxxl boost required + +- The option "parallel" uses the __gnu_parallel extensions in some parts of STXXL. For all \c gcc versions >= 4.4 the __gnu_parallel extensions are ON by default. Support for MCSTL (predecessor of __gnu_parallel) was removed in STXXL 1.4.0. + +- Boost is optional and not recommended on all systems, except MSVC 2010. It provides no advantages on other platforms. \n + STXXL has been tested with Boost 1.40.0, 1.42.0 and 1.46.1. Other versions may work, too, but older versions will not get support. + +- Support for C++0x and C++11 is integrated and automatically detected. No core parts of STXXL require C++11. + +- All options are automatically detected by CMake. + + +\section faq_credit How can I credit STXXL, and thus foster its development? + +- For all users: + - Sign up at Ohloh and add yourself as an STXXL user / rate STXXL: http://www.ohloh.net/p/stxxl + - Rate STXXL at heise Software-Verzeichnis (German): http://www.heise.de/software/download/stxxl/76072 + - Rate STXXL at SourceForge: https://sourceforge.net/projects/stxxl/ + +- For scientific work: Cite the papers mentioned here: http://stxxl.sourceforge.net/ + +- For industrial users: Tell us the name of your company, so we can use it as a reference. + + +\section faq_nonPODs References to Elements in External Memory Data Structures + +You should not pass or store references to elements in an external memory data structure. When the reference is used, the block that contains the element may be no longer in internal memory.
Use/pass an iterator (reference) instead.
For an \c stxxl::vector with \c n pages and LRU replacement strategy, it can be guaranteed that the last \c n references obtained using \c stxxl::vector::operator[] or dereferencing an iterator are valid. + +However, if \c n is 1, even a single innocent-looking line like +\verbatim +std::cout << v[0] << " " << v[1000000] << std::endl; +\endverbatim +can lead to inconsistent results. + + +\section faq_templateparam Parameterizing STXXL Containers + +STXXL container types like stxxl::vector can be parameterized only with a value type that is a +POD +(i. e. no virtual functions, no user-defined copy assignment/destructor, etc.) +and does not contain references (including pointers) to internal memory. +Usually, "complex" data types do not satisfy this requirements. + +This is why stxxl::vector > and stxxl::vector > are invalid. +If appropriate, use std::vector >, or emulate a two-dimensional array by +doing index calculation. + + +\section faq_threadsafe Thread-Safety + +The I/O and block management layers are thread-safe (since release 1.1.1). +The user layer data structures are not thread-safe.
+I.e. you may access different \c STXXL data structures from concurrent threads without problems, +but you should not share a data structure between threads (without implementing proper locking yourself).
+This is a design choice, having the data structures thread-safe would mean a significant performance loss. + + +\section faq_diskalloc Disk Allocation on Multiple Disks + +Q: I have configured several disks to use with STXXL. Why does STXXL fail complaining about the lack of space? According to my calclulations, the space on the disks should be sufficient. + +A: This may happen if the disks have different size. With the default parameters \c STXXL containers use randomized block-to-disk allocation strategies +that distribute data evenly between the disks but ignore the availability of free space on them. Thus when the smallest disk is full, the program will abort because it cannot grow the file on that disk. + +A2: This round-robin disk allocation is due to the history of STXXL's support for parallel disk algorithms. It would be great if someone would contribute a patch for this issue. This would require adapting stxxl::disk_allocator and stxxl::block_manager to skip full disks when allocating new blocks. + +\section faq_msclr STXXL in a Microsoft CLR Library + +From STXXL user Christian, posted in the forum: + +Precondition: I use STXXL in a Microsoft CLR Library (a special DLL). That means that managed code and native code (e.g. STXXL) have to co-exist in your library. + +Symptom: Application crashes at process exit, when the DLL is unloaded. + +Cause: STXXL's singleton classes use the \c atexit() function to destruct themselves at process exit. The exit handling will cause the process to crash at exit (still unclear if it's a bug or a feature of the MS runtime). + +Solution: + +1.) Compiled STXXL static library with \c STXXL_NON_DEFAULT_EXIT_HANDLER defined. + +2.) For cleanup, \c stxxl::run_exit_handlers() has now to be called manually. To get this done automatically: + +Defined a CLI singleton class "Controller": + +\verbatim +public ref class Controller { +private: + static Controller^ instance = gcnew Controller; + Controller(); +}; +\endverbatim + +Registered my own cleanup function in Controller's constructor which will manage to call \c stxxl::run_exit_handlers(): + +\verbatim +#pragma managed(push, off) +static int myexitfn() +{ + stxxl::run_exit_handlers(); + return 0; +} +#pragma managed(pop) + +Controller::Controller() +{ + onexit(myexitfn); +} +\endverbatim + + +*/ diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.pdf b/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.pdf new file mode 100644 index 0000000000000000000000000000000000000000..661da60ecf63f251607cbbca23a449cd20581e8c GIT binary patch literal 37091 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 DxiHE< literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.png b/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.png new file mode 100644 index 0000000000000000000000000000000000000000..155317a72f8c8f4b70ab3a6bd74949df28c74ea8 GIT binary patch literal 59372 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.xmi b/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.xmi new file mode 100644 index 0000000000..4659184bc1 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml.xmi @@ -0,0 +1,6099 @@ + + + + + umbrello uml modeller http://uml.sf.net + 1.5.1 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ + + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+ + + + diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml_small.png b/third-party/MQF/ThirdParty/stxxl/doc/images/btree_uml_small.png new file mode 100644 index 0000000000000000000000000000000000000000..10ff2efee8c041c4fb85189a27327862166e521e GIT binary patch literal 103701 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®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%0Dv5cg8W0m373kxHc@C3 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
NKM72u$!;OwU-4 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 XGCD9YW;Q5@00000NkvXXu0mjfKI@Ea literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_boost_cmake.png b/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_boost_cmake.png new file mode 100644 index 0000000000000000000000000000000000000000..2c550d36755876de538b4d810830fed936737833 GIT binary patch literal 24483 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 AivR!s literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_boost_msvc10.png b/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_boost_msvc10.png new file mode 100644 index 0000000000000000000000000000000000000000..9689d29568a910908aa3cc479063b4eca85b3ac0 GIT binary patch literal 36613 zcmaI7byQr-(mos<1_(O1Lx3>2yE{PxgS&;`?hq_Nf?IG8?(VL^-3NCG?(mUw?mg$e zzdyd|wT4;Uo87g$tDbtQs&|Nzf)px%0PyC`8&nx-ag{f3pc~)3fwD$~e(m}0xh?hP zO~kN_xQMFT;!!J-2DWufD;^P9$6s z9V@mWpn0RMB6#5K(Az4es#CJCP^+roFJHa{XZllE!aNDh+$NKet@t3p1Kdy3MV(bwcpXp^udPrj1L!KRn((y`J{5EElr z%6y{C%T?9ffIvZ^TU?cI&*!~a%DHbN?{C&`-g~=m!IW4?O9}NPE9{j|MPek|AK7ZK zMN)?MB|Ag!Q|=Vz?iaNmXc|au#K0w%99RSW%ZqhV zmOkvgbg{JjOyh_Bi#SlmiUxJz{<=K|$O=l$D3L&jM%(F;yK%tg_?Vg?0$Z^h4olVJ0b^61NQLk8xK zridCl!gq-hU*mLeZ4>tp=H3Z4MBh≀x759NX#34jAt%lKi55I%-UvWYPoC9$#uT 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

QM5E>UBy=ht84n+nK`E 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_cmake.png b/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_cmake.png new file mode 100644 index 0000000000000000000000000000000000000000..7e31b0a6d87c3804cd689ae9636d97245c365b82 GIT binary patch literal 27592 zcmZs?2|QH)`!+syC0i6BiWd8pOk|l9k|bnbCW*)zjeQL9NhKs(vW<}JBFkWAl(H`) zyTK%ev4^qDU_58^`F?-T^Z)-|&1+=Fobx{Sx!>1)-Pe81%e!~0~?A^73^4w4hV#%hW5^wvwmMw;5$wda$5(q zY4e2i;+f%eXx!dKXbNE`aWH{^vI#Sz7#nl%Ba#T!dbINOoDO$lY{#mF*7Yz$N<|xX zKkJg&K~G5U(ftcV?cG}>0z7+tG>5P^_xa$rNRZ+0?Q9wESTu=gPM7v8Xgx%1@9_*b z+??wO`VJSR6Q-QMH??v70J9E-8%=)P8}1Nj6O$AZrEqg&@6vn>W@-@+FOaV$X^(O3 z>v!}E{NvE5vNlYN*FL%HEuZ4Dq)pkO4y^8d2a6-th1Uym6cV)e2hOjT;i}flu$jr% zleOW6jaS!rg=Z9ry*I*oAIT?fi=3jM^&R9_HKo_H2X1UaSJ!f*0dPk%>lX~y1v=OAT^Fs?nO7~6>8)jB}SL$IsWw>l}t?l%jcs1jo z0Fo27D703jVZ}>LevPu-Hupg6pgKeX>QkQK^EInYU?qW1VO%)Kr92~Vd{Th=kQlR) zNbG=GVxU^(p`X{8d|$0o+n_rwk%g9a>vu_J$&bXi(Cel|eI3M;>r0)Y$^FNnw0EIW zTSc*iSB>+w>qH|$Tq6ZvsZI`fxOsw*(4Sns7magIDi5|BlJ+(U)9JZ8DMaZiJ&=6l zaGFx$juEEHbPTU>!)w-I1P*x5=F99PKkpat>1|@;2oycfRygb#BE8;i+)l|kFzCS& zvuE6M4y-Z$i`tzm9r(GKZ{KS*zN(Bo#2&Qd(Z4DWwyWv*R6PcZX4AO*fi?~J&1jlDr6hTw2fBpZo=%ECh4pZTVk$4$NL;{w!{M7)fKg4_&s(MTN zF7jZ+E6B#P$*+M3Hv_>55X6)=TQa4{F6if*aN6&J+={+h|IrBA7w^uE$9wS@3H|M( z?*9pQU}<@E3?~uA%1VxHdEa!L5k4xp-+M6Rg6a%i z4OmTHj}aR^uhi4tpWb5>L4y_SJ86(xeb>l};WTy1N0Y`!HD5|02VdpNA6{%+Wd-BJ zs*SVE`!%q}vlEg5j zH=<}qQNMrOAJbovyoovmM_$x9yxsesn9$x?MZCd$lb&z5aT3h9M;|siqE)##5&XLI z1Q;cPeP3StVzzH0k zWaf1zL^16169-Mm!J05MwrKiew>?sO%`r$`ux9m}7uWuLOLSB=p+DWPHQs7!OIiO7qmVoLZ$UZqd|747Bf9)vJd<%&qW{dMS6v zbCJS~NWvS)o~9a7dsS?m^9Sf}?zWyHd72_~Ph3-xwXSY`=AUZ|Gl#EL7Yd3~uwTk7*t;|X%TpU7!4@-M{61^PY* zUC9FK5j4w#tw|qI_o%QnUEjiMsl8tN^;d}><&oYK3cPQ(cyo+5i#)Wxa*@Tb^Mi^L zF0gUQtwg*->{(ioDKE7;Uaynj+hO0g1DcJ0AwhnmaWWPkydaNKh@gmyll513hChw@ zkB|t+

&a2YMnc-qdR{;78|HSi_dRI}265OXd~YQtSRKPVDo=o|g#Im7%6EBrlbJ zcJK>#n|`+luo?3`tw4LNTkn1O3h<3P6`|iiRIjJBqGw6zrIo}VUISsC7l^Yqqf}FB zm~PLP{P7Zmo-H2Rak;?S)a&?*gRPaUaIKd;+E3M_z=!z5mjmn(8wezu?2*{C*VXk} zBxoi<3b*@#>}=-6)gO!s%48s~zI3F`8!dhlq4I56g*z}J5%ONR;2*=v^lXBo$4e+# ztcip~Vv2*876SVA72l@s#^NK5G*3ZWFfMz8sr0t?oBrT=;zt!6K|ZAU#r}y=FByf5 zPOW2_mHkZQ%)mNM((2pYJP@gK=6?`z20)o2COB9ftIwr2&*N5$uMAk7tZ?*rl7zXP zYo(17!|JObxAVqB8#%TKJ3CS~w7oF3b~bW$hu$oq9A-<2sIyFeRAV==Jaxf~KYa!V z+?V;);^FOM0a`Q-3)LEPuT6ILBTcVJmtX1Y03l(J3vy)CU_3EMdt5BG#Y`FI0Y1iW9kC0oJUWZ^ugE;;IBpeDMNRl#LtRMtr zs&Ld@UR;3R-1bc;W1lB?4_FSQD~2N`1@aY^YAQh_=yr-+rJ-SU&=?EZEPME;2jdIN z*ay=Z)Z3{^E?j6Xa+*k@W~Cid@SrDAYWrn1zC#u#j2{19KPLkO`t6gsz;La8nI5L~ zKB4+jl?bh+GShs#t5}M%nb*t@{TZ_-+T`qd%HVAW+QyTjZcNOWUhaq*O5sj5YIPA> zUSP6R8xV~i7xDjY9j`V60cover z2tBad4uM+#DS4|5!7Nzx6`;P&mKTeFG<7(Rk(!6Y+@-ICQxT6|{$wKOisr0_Nu8t@ zIXYg{GNGSMQl1`GIh$}PFih}2@34_tPm?Zo1IPG(D=P4UD}RJ2z^7X{S`XiC0mwB& za1;+DUJ3Ybxae7C9Q|Fv|C@N*SV6E(sM|(zsXdbRd3v93PrEGe9DWQJ1wu1k-ApKa zo!aKX7+U>|plYq=Tqd(V@XB-JXl~|-n4(mDh1O8UO3w*hA_M8~fsJ@@y=*(1RpakF z`M-s_1YN59a%>>}C_fYV?_xmIIQP0!|J=zPiFrtj6FRF>wRB#2QMjx-{bk8E^QWVe za`?BSwDXnsLaHjB!fBq(1AY2x3sSbT$^Emwk4K)1BWssyzb!WlI?=201zDnAL^;FQ z{*Ay(8~VI!$9~=Et&3iC?(a~!TAEzG>WXMYPpPZ?f?~f@@2{b6YpQjWG*{Z($;96` zaC*G6W&4rqG(P&@mEVn;WTC-8=*u?cn)j|m>;JK{RkMR+8@4Ka7%u}0>Ee~!JSE4x zdsL6dflvOu8XPkSo~brm4T7uAp}~^!iN2R^BFi0xUxi~lZk?dVoqku|`jM&%&uyyC zENK#5+{S#&iC?x^KTPU|oe$<^SB+4PNEF3G4uf{NzdT__u0-5NOa(Y zhv^Qmj3^Mb>1s;nz<}@k>)*QGNu8>Wu)7?%-s=2Ok50P@e&tU@apuEatm8Tmp~dv< z=rLh>LO$GkE$bv%7l8c$B|b9zCylJWyF zE6tYj(k^lhHCf~MR0bxvAR0V{mMGPe*+K9Z8h47yJKY)UUfEK?3rD&MPq>EFH^#wt zyuw9u5;lCq>};FY+F^I>7jp?ehTZVho)`EkeDznNgJOXz(P3hwDoTqn zhWe~!SO`@8(m4Sga4Dz~DB@2VWC@YZ8j9+gVSE>~yzs8a->l6JHy!=@13Nsqk|@70 zTmOLrR@wMR@54lf`agIfO`nHQwN%L%KP^M+*HaV3prK2WNU)jL@lQ1j@PhNP78gYY zBH$`lXr5YU^7^GlhEtq{??j2_@EV}{7_Xvs!xw{xShn-O!mhl(fDuqD@7En3Ac>w# zk>X3sE_c_UiwtLQ(E?dB4^BFe-+sCYRyTc`(>Z(B)_29e-}T&Lsq!*T@evq@>cZ`> zOk~H0VQ}BxmyrfznuUMV?THRx=x`vgcT}u^>LpR6*uMdHdR$$*rk*iy$i7q3{$)N z*)MN^oPs=yz+La(JN%A&CA`=Tepi{C-xWHhs~z-DNosz@=OrFr60B+Hj0WD``FNW5 z%!gHJxq_U_uRLMo7om)6DN_KvNW8ti%3c##R*DjJ`&o0>xxD|!X~~(ZM+ARfrUM5j z2T4hk9GzcJ*@S#!Mx=9WhOf;C1ND~< zZdt$5HFR93ZYpTI|U3;;`tae(hg9b91~z->3h~+IZq?_P*>0HG)_V z(ntgD5eV)L5t}ru9R4SNQJ=Z1IH`Czgq6N^RsV~sb{QvO3O>35RZp0x4qtdsI^>j`Hi z277EfQNBkH6{l5XV}DPoydZ0^*&_E-RwjN>&d^0I&CPQ+<&_yM3WCAaBr>s z)6M#ubC*HG=){&wh?Amcu5h<;>hNc7!ZOpJlrQgFgqf?BTpl`zd1GFJNPU93q?r3* zzaV1chC=<9&aZHeRdj7ip=ia6Psn(7yL#KJKCKHg*-AuS>6AI6-NDktcdc|($ zrc?GvmjWl`YlZt#bwx41?rD^AjZuFnY)JPjWo}7oG)DD3<3WW)E~^vJ&kG$5Bi+qa zj6xBW4xJqik-|N{ZhCGXqob7vl3U^C5s~KtKhH7#jExFZ@JF@9pP!q&Y@a-Ea+;B* zNlgAC3Qx*B>phi3N;jn!ir4u%F&zU-y&3qnyvL%O5u z79jGs0rB22>-S6je^?8#tYSJ`=!C?{g}Anl-48!(8T6p3mI%R$V+u#ej^xR=TZx80 z+VjS(O#SccGviWwb81F}$2PYlY64}WUE7H6=ku1TmvaJXI7c=ax8c-XH{}-O8k6=-Exdn|e|NR})E)jL| z&H@+EngKMCPJsD#?i1YOv{umeOh6D0yQSgZVSeN%-pfLuLs|)NUo z$V%tPhZW;riu^zA)qipQkPUC+1=JVpF=ae`u2OZ?0QxeJ69s~-KFwa^oR)oR%Du1a zQ;1(1v81Qr{`bijCx7ef*0n!4_7s@>Xwp}ttpxt?&jqhaL8i>Zo)18N0NwXT@@~nU zLn6Icxyc7psa(CMkgH-OG58;cxjKE(y69>CLz)cQ&trg(l0R5F1P6a*(1cwIL}Goj z^H@o{Y*eexLSnu`J13v(eHEOJqe&*QA?%!Y;IcG*?BMj0jH3%bEKi1G*pREa4xY_} zipzp=jW;b;1bZT_v>^wX`YVaWs!*YCjg_}bQOhZdGjYq{j?AmxT#mFA`>PKv=Pc%z z>I}-pRfO-P+73+`I&UiJcRndJPA=Yx`?V=!*>l|4e50iP-U@vc*I9z6XO$HpK;@)r zN!EK>~$&*1M5bW-67fsWxKQ4~focmB;pLVf z%yo1Xx$bn8Hp~=}Yvn$Kl`Y+j{(%};A21PElbn(P$Ilk`?{m7jLzHO>lqda76V zxQ5zHR>-cr(59z1l!3IFUN7TIK&$Z+mJ*4lz?5pyGwj3We>zSfp(BvhkTb92&G{4) zvCU2vnxClQ4%)OKg_rTuX-kxZo@{%X>kZP0fggBeM|Mi9y#>psd7sPi%Z^&7yoR3C zbMalM;%191suf2p*!Vbq~am>m!5!RnAe(98W&5Pi3@N#Ky5j9qsO0m z^?n?Ye21N`5hgxgEc?^k$O)vjWt?rb?yL?nK4P~}fcVIOOe?|9v`6!YBCNd-E9R8> z+*G#kMhmaqo&cir=59%*@^;31g>d544-~F(ltmo=DRd{iA{YL}BUB^I>h5Cw{er!F zD~X#mn;PbCd~<)g{hksQgPS;n$-UcN%RBZs%gDDS?J5z{*i}WIAK01*#)t$^rq}!D z$dw}!w8N6+)_Wm56U9*H)pp){t0q0Pulp#FKZZ=`lb>%(ESOSN{5UcR$`MP@!w& zIF38X#TdR4cdr<@X0Xa+vTtHaenVNsF#}3s^a)RzPrZsXR!ep`j^i2I8~bb4_fEY8 zz?<5UGd!ii5Hie(TIj|sb-zjy4#%V>rC0I{a^oyn$|)Yu`x#Y^!7pk{7QRY`@l6HS z$rnx6hr^oG$gNpx??f85Y<?ph%$1wOJU0zs3-q73v6__j zYieq3_%|DadswJQyTUD$(lAjbT?xxb=@gBhzeATO-7UiQ7lF)Rdn6l|{*Y ze6sJQk>KL7&TigK*#D;+L*7|0p|AxeRzYjWYGpRdgn*n}&e!B~+YPFp<%;MZ9b_S@MK=-Ge zUocj;4mk}Lnd=5_>NMIq!IufQJ>u!?I2O&Y*4}vZVf8rixpVL%rq-F*6ii0KV<<8r zzq|z1^4Ufx%UXYOV|ZAyh8A3Wlq|Ntbcf>V{%FVB4Y^wF5L43!2*s=joW!VtLGDPL z0s(P%!a}Pa5Sx`>cF4Tr_M7#_-0^2^E<-(b+WA^2XX(Mdyj=4WG_hN5dm6I;7^UR_ zY5;}?#8Ls>ua`D5WdcO6j2Jz7Y33suylhSxjaY4ImR*$U_au6XBIEtG2EVwQ=UTbP zgMpho2u;Cr->j9T-$OCvaEP&Fzp!p$I(qp6_9PP82GqkZCDEADjgjQ(w&Ct5U?#ku zT_^P?b_&b4n?G_pcjBuq$sC+K>@^{%(>MyUfU8;P&YtMC2@b4e{Gp(rQ*Fp^5Z!aQd z=V`}*l7gHw{0!Rv^wm-U-^ddlHnZrW6OJ*(Za6J`XsLkB;$%Kq;>yIyF1NaSPJ^GD zger6?_!PuCa!`wa(n#+m0iF{s%3YG09xYafA0=&0?FN_)vxI%^7d%NnJYA&*#5N5d zWNY*2U*dAZVH_m*T4|2yQ1b>j4u_;b$WQT2!D*5WvWaDTRvcLnr}rOa+Bok-#V^`Y z5niv%&`5*upVrx_vB|XPjQC$(Mi(akH^TM5=-K~6xGpnjimfyO)J(r0J>CyA#U~|k z8wn=>(iTY8q5&+N8E2_Z;nkT28jC2sIlHB8_qIbYHHH||&bCZvU+qKze=HbZ(9Gsb zk3yE;srUw79oMtc@McyVAe;KhPaA-O7Z{ux?}roujr*)l6AFmMYb91dD+Sa~2?nzE zcFXkRO<+tsr*rVlTTB7_=W1)G0FZGF8H7*W`akQ@hVn_Z3TX*|V?}RrterGy3PTmFP*4BbD$gzLdg7iRG!4mO#7u|`40{E*>>i7t7igM z12CuT_(jBHZD_YBQSLM4#*KPh+G{pydwK58?FPf`ZxhQC<7Kf9T19&l;fGYZEoEN7 zD=EITPKr#(sFx$RT29Jl@BF5FQpWzZq)>nQQ}E^y*wFM)o1jfDwcq5^>uLSosl6pA z`#3wlXc5#Fm)gSX%*xx8uDHDk>yChqJ}AnJMq6sosqGm(L9Ln#Gm8U^D77NHgH4Mo zDbtLImA#M|KWKwxoF(RddILS>+A5Ve3BfATddJMzvvZqs|QN~JSVlxn9Xb=BXQZ!cgoXG*$>~ZTs7_HMxfm$ z#jFHcV9FXdqJ5vtp>T_|82A zIIx#t^i$(@Yo!|@-gyI)>bwD0=sdBF7PlNfcx6XvGMIfr8i@@{-QcEYw0^1chDmt7;8ACP8Lx+U;@b zd7|+VLU2`?sW>3B{yhctCxp-ow_u~Hr(C*^&lx(d3ohnn4I$cO&_PD~aCC9bx`HXj`?D zv60yxnYYb{?&EyH;)q-2aMLyT3pr-SB+ zF8=CQ9u2e2At|ce{&7?QUj^fNa2NZ6h}VUqpKHJRKt78r4;9C&FGn}bRK%~mEodfa z$I&=9?bRFy0~-1@Yi%bB?$f3N^l@eLX)=#WAlOi-##g}o5y{YXKs{yb@+dScJ9LDJ zEF;zi{@AeHSl*W26CzEW&RIT-L(J!pdNt?RAPAbaE<&S`iJmG{QD4%o5vuTg{UsG- zym)BJ&Cs{TP*&2k^Nho`(`z<@r}I13N5l%)WmlBv(72R8@j3&~d)EP)VWle6~t> z*z)#=v@LrvUBFRdnZ-#9^f6?{xhiorEFZ1@4W#lDYwv_XjJaYXldsQ-=ZVqegpjiAp#w~9=kC*D*5u$=b1ou-ckO;yF1>q-td?e`0g9!<-Q2iv|jjyHU zBfI4d2Nx*a<)nn&>^7o)d9v9wX%9V@apZ78f(-kl8$i|LSZy{Hj6Z_AsEI$PsD0^@ zguZCd!=q%t4{{ok-L!Yf(vD}gH~UL3Bl$Ss(|MxalM-`irr_{Bq#8&ute1?wOtdIa zU7a*OKnSrts|nZdsNWb{u34IB0DB19^5-kuMoBS{FaE$v9GW2NIkrT zk4N+K$26uHsR5xTS{1}(`R{;x;)y6){t?%{V`>Lc;fl}?%&PL}!&7XBSMEB4i`UZz zhMvF-lb4w)sgC`~u|Y++3;B|Ac)J=tkf#Bn$62Ywyd&1;_SBXhu288VHiiXoOJHa> z>UHac79bLDM2kwtemK~=ao>A4c*%-k-x1uYnYOcwZEHEXPd^V3zyBbNnattze#27? z7hV2s_?{1F^PuO&&v@^sEN(-MOP`a9Qt^Xr$zB}yqRCi6+yyfIW zE0?U4r*{2gDT|5DS$err%dw@dn)6Yo-JEGziT;<~Stwkuly%C_w9v5kvR_-j-~}WX*RyuptW4w!>=&CB8G{Ml1m76sWQ_>tcRs5ucW_nbkvVsR z;#1da0<8Km@JtHv+D^(m+lV9W7dH2_3 z+l}wdIX~}w_+yZySrx`KaN7IKH|Je(a-8o!QW_}(q5u!2KX`;haoiHbkFvO3@tvb1 zM#$c--25ijWuIqL_KOQANSzuLXPD_kls=};nZXqlN{_k*viBM=*DK?SiJOy>HFzG?g}w_1cu53r z#V`Z;aUEv;=dZ)d{~Ljn>*c!d;BHbbwaK%}b|{J|h+O6+=Q9Mh)p(ShqT@p(vgrPExVp;o4i+N!+3uF981A zTm0`V-RZSvnNUtakK*B{G+P1>ljQ+Dm2i>_`!rbP$cvwHcu{U>*?5XWr>m;qIN{o*)DY9mXq37<*oEZyu#wZ9t+W-(Mua_ z5A6TOHxXJCpZg}movMAAPv9_v3Gr|Yhy?pIm@~mb#t<;Xlh@FmYM*Nr7Qb!MU@=vl zPdu-?Oi-DwDR(Pd*H}fvH<9=E&>mM0kEZbo;{8-=yrkHYWmupsBn@GTtV&rX<;aH= z;vI*A$`^k-G9$&V;Jl$1I9Wc!nBWW*0`c*VAtCM2(OoK!CuMQP(TtnnNNvfHkIjO} z)M;Bg%{uV5pJ?gT65fsK#qAn9wX-*Y7?yFx(|UJ1A){Fl&m)i2xg6z7gemE3dM* z=4s`Lc=4W5ZZ1{J(xa(^UglEqyK+(cOz~@tO6O7>EM2IdEVNEC7nyKlPG z*{L@dyZou~^;=l+S6`Qq5@{1$9!}t^`GvaG7{+4adA>y~ z+~nculB?~hz4pZR>prX<@C^(5R5i35LGx6a;q`?c0i7ypu4@oXp}88Jw!G*SP~fa_ zrYPEOgQNdELz$*WckJI}&K$L!GD zk#nZ+L5{3Rza^<&557mv6!aw|lzv-d- z?>**kuwOmjs*I@YeypV_4kd%09o{IiFJGj;_05e4^J`iD%bvt5R&qs@{gs*5Em0_n3e2^{ zQoaGTFED};Jz1gHkLyovF)fNHoRl0oVNl(aBZ1X+t5ch6X9!Ro8|Z&|w8Mj??*mM2 zQY$a^vTbSC&eQaxLO)WNg6BO?IWPG4Ony?^2FV#&@N%6QtsI>dj6Ej)jhS%I^vlo8K1m%D=}tIb-5jm~j^`NksAJ=|kkZlZWV}cZ4JdFL)2N z>AdZ}TW_0&@!+uC=sm@m@w)sg@i|@?av9%XC||I<_TqLVzpcyxEA;90#3N)XV2xk^ zqy&{72(*SURtf)EpMsZ63$Vf^TUgFMs*S{YPP_}My$0VJom}<$*jYA{ldC%|pmGZ0 z{c#kSZ3An{F<_wPoq$h3R#`T}!YBK0ep*ts()RY$l0Bn>fp~wbm-D7iuYoEPdkLsA z)`wMQn*p7s!xwKH0;>3jCgX8^%>7z&`OaAtJ%~50=9<=FQHqs0tSnHZ!p2JvzNL^+ z*2OJ%%05I($O*Z<>B@x5eZmwB({2`=rfj(~!Wk!=5_}VLsY^`PlRe)^KEe!c<&E=H z>pLAKUl{=g3W!RCkAylezJV17ngFoZGrHGK_F) z4!dQpRk&!Z5>DV6FurU#0mS-Kw)@?3QB9=x70iuCasAgue(pOpUoFvL>Q`1HPpx;A z$#aM=IM?G`gzMXIOqm<6(B;GL?f^Er^3o%xF#&+@;K%fMt*AI)=pAjH-f11Ia{Nw{lo$}_q zX;xwMYv^QVjZr$Nfsf?LQ!J|Z<D-+0UB?; z0DDy2x@TlpV5p0+4ah`CG^UQ&xZE?hFzWLw5}y6?}vF6(S4$8fGtt*Y+UZN zw2;WNk6a&wjz#}kV4&K~Sj!D7(MFiP65AR%#!44WxtZU_zqr~G%`G$=Ol797MBe?w z<7?d56KE5n`Iu-9%S|>*b(qEce4{Ap8--z?)fXVa1c(1yhjrlB(tP{9Qk{>CTtq0~ zR~+v2kq@c10LoR6fj`O#|=<5S;x^t zYSlpQ_Sf&9fC-6}zv$g~eGR&cV48u9Pc^uH&FkMy7nbIs(2((Ao-VVAIw9lxweA=- z4X{=UjqKjHk)`b9f9Mn%iQ~XsbmkJhS#Pp=@!9gQ&`%4N;3^{~^v*A(uAG3cd++kb zT?=J_GH;Fazp;I(T>JMLKJ^CzFTR|j99YmwFYqN1S})(?O|4dLh1mB6Hz^54jft<7 zO{I})E_IZDMSI%o<&N#uvjSgO&|vSL2bKBGWmo5Q1WrDVignnW_v97@X?`?WdRH(G z2=2NG@J*Mm%rHV|&EIu+7B*u~>9OTMp^*%qYQxNywZSY_g1Ij%8aSRv#hzrd- zRWnm6_626^Cf}T#j;PQ_*81DJ`NP~wB-oZ%`sw8fc)S+i#}&I9D9i$l9-So!MF31U zIHCSId76cbY(h0^-ux`-~W3fhLh^ojcJOEBGsF=2BuTk&gl%!;)sR*W^l)4QX z+>TxHc34}t6`po>p&K&iF1))l{PxaXfLZo6=&+qXrF(1TlSfMEl!jOxa2w#*VGU$Ofc-!o>35=+0__yQm;J1JU{Wsz z#kihfVeg(it1nnWGomSqPE4^C4k{zHa)eq3r;K3SEy#`K{*x-$Uo#r1=shv*D`Rn^ zCM?0wQ7Zj}v#b>!hXpX_D+Bt~-K$>r*Em1r>Xz`U)QpV^n;q}_8DUCxwax{l$S}eG z*fQwrl$=6tH^n$lOjZuFkPT3F&F+lB2BNO&;mwRcg0@i}T2`r=s?$@zq}(XcACs2g zw|v$jTRWJfIBA;CJ=FKd;*mVODkVfwPTexYd#N6uB^5cH(PexoH3&cK;eG7mO1~mnGGa$r|TQqraQBerI-^FPj$*x%h*dL9;E70-^_3fkKT0wQBnP|2CW4>p8 z$%McmV$UMvbiB*@EfmVgjJ-^+!2cXEQtS=saTdT`~(>PG`Z9 z-gShN^NZfDa}Sz*9jIkzhZYmp!n38!0s?0_^*(B(xMg^czK)i{`-mSp+c^12hBw0b z?y54VU&W@Zza2;7OMmZ?rNcdTJ#Agy>Mt3h){@aZBEbS*QVUaW@dh(J$9-d>zM8X` z%^87pQRDiV?d z$X;S*2xRQ|sUD2huu%H@{+XXnk3rPHQJ154{qeHA>eJ5`WkPx(Q06%omFk+L^tuSy zUlkytTIw>}<0s@gSzLe!_sY<`oK#>WWAJLweVqn5=k|9kd>U<+^DamKHSD!FUVgPX zF_W9|)1tNRrFFlrM#VTAt^hp#Ff(6b-MLfi4AR(pXOoBv5dLOU!zsreB>aOqR+Xeh z3zY)1AXkDviSpyp-5_I6OTDfwb6>`|m7oo*g=W&54;?U351S?|W zgx~d^2PSFM+uN_rMJ4THA9SxMpB{ekB{Qa8l$l$YDF!zObR5Uen{4Y{c$T$X_ERg? zftCk2ft<9E0ZacbB#PVcY}Dg>U})$W4uz$Iuxo>CbX0H!jD)J<7Sh4SX}k;5Bc{FirC(3W zWZC$EScTS(IK++nPJ7;3(6`l7DOJ)kL*R4W$U$!@q4>MbL2D|VH3H)*o}JRR0=m0M zWEr>UxY+W6-NdO~f|%>Cyg)YvH-_x8!=Gc%tFEHFff@wj!`)`@7#&yG#|7_(7Y*E>ssv!)wkLkl;X# z^|kY&rz#Zi&V@sp?)jprq~poz*8oxmld4O*_bhygv$Y}P*?bKfxNt^KcrtvrREB{e zi{ksn^r@3-%|*VTThBul$}h4@er|jV`bDTEE_`qmt6Qw+IQzJf%V=Kx-WR2a8&GtT zrq{{F!>eAkTLHMj8!%DGfjtb?e7}%rzW=kzi2_T3-|alj+Qvk71yGI`CC$k&D>EAy zy7PW$omwNnK9W5i)h!(5zVAG{@ik`oa-9-9@DhhtiCa>;C1S{OE$d#S$o-K8=O+dr zNs}5=n904=xU~yWBv_$QqTVg^?kAAE(wy?#`bbtMi~(&}-bf|~g5cU>Od_l}z8rnm zW8oN5i=BGWqxQ`u%4PUT=~v$T@U8+vo9z95KYQ}=K^dDY#&B5Yl5=r=$y><1d#OWf zD446s4ZI2nt_kU-$MnLS@7oOmHx7SaYjiF@2M{MNK^i?N4*=*3GSg8V+N9GH&CCFamx1FTOb2&sGjtPA0;fMK!*7Ur8zyaI$Sy9>Hjx zPN_X#>Y{94t1+)JwzK3z5Xh~9of_2ta%T~v=NfA2B~tFb`wxiVz9J1)&{Q@jvOZ2P z?{E*cywOFK#`X-8i75h;$2=4ID?1me~>`)a%xJzcjy~y&j@^ z#H6ef0pi`{UXULqEOxd^@0o!pk{lGcH&m%I=5|l^2`q%Xx)lkmaAx zW(uvu{N7|_fZh1sC2ThO{Hf!^pCE=#_1XNh_T$r0RRIY$LO(aFxIe5r3pJfDUno5@ z_$MycWa*9Y4_|pddG2{TQ8%Pj9IILTNSP9eNLHlCVz>yNPWum!o2^~yvq5MQ}V_!JGOJ1kRWwz!pXO%vVsIF znq1qXUwc`{I$M8quePzt3QG$veCKP%LfX8SVb>9xZA+ zK$p8azq=voNvTX#NuEi`h+mK4aEwwc*2IGWw`-dE%Jfx+x1%g98YjsFbhc+zj)H4| z=9l$P{x#9qa#G51x`wR7q08z1W!N*S<&+ur?<~?%t&r*jmv6{bpLNnmgNVZ z;zb!NAz7iHu3fG)nB}(Z2za};HI){9lxf_)neN9wtQiapa8UD5c{8zLLpg#FvB6@m z^`mq73N>N*-V!*)P911Q>1r$P8C!6l)|}lA?=f5T*9;>pV$Ltb7lTOueslV-E(&MB z%|S3?NCn0v#YdBjKqP^6{XGvmQUMhF(r*~Q|8e}v>?JYww+am>xDFi?|9(~qX^c%# zZPNQZ!CF4r3u$5!Cm4i~2_x&(m@VD@Dx%SN*9XR(%v=RDKlNUO!U6f zuIRLzoa$LSY&7$u(hFNUxyAG%Fl=fZR-!&TiuL-uTer++D!1``Q61*I>CDALD#dI6 z-W;*ayk|98v1N&clt(s74g1L4e!SaYZ7ET4Ebk;dJAZaFmg9^-Sle+ReCKd7e}=p* zz7meU(iE6m?yUb)Bu_oEXA`~ccZmz3kmwv!z3w(Ws9wp zq;z&5x7a{rYe{*`l~%=u36V#qCc!v1tU13{1o7!!n04*QljeB=Qs<2?uYfa{FjD`V z9n^rFb2t~XEIr~FQBkCpWVVIz zKkDIH@=g~Xh)(i|Kj2zb)Cm~oab>wCH+Ia1G}(~fZ06_6VemX%^6yN%AV|P1t*qo zax$k{u5mBp`D-T-s0RBggqm|`>{eD^ubMd+KK(?u|$(|GB89E zM0?%m^*(_rT`s*8On1Ih(kDbv$HRwX*3i$xV?|!Ga?{v*!fAkVe7QD_qfxa*61`=Z z$;M4HAlDBGwu`^=d!hAL(J|{8rqN^@gopfdL9cEP)Y+}*x%Zq=6v0HQ4uI|z*X#x&4m z{5-wQEa_+WJv_IRN=SC}WLmW=qy!PV_R*t~saRD*5hL+kLk1l@Z1g#$HM!Wx{4}7c zpI*i!Gb4p-pFwI(ctUi=hd6$qq_j_=q2dz;XLAh4X6m`D&h+&16VfYS?*MvkrPnDO zbu82qxxO<(Ns8zO({o_jlP9 za~JI&`uK;8LJ^JC{BL~R$kzu<;(y+aNl*BYt}-{*ETT#7bLjF6ybEYshelVoh%L}K zE3{kT*vLBt)6mYs?2rM=IAl(2e*dsjV7Ly3o7d7wAvsL5NK@UhZ0PGSc`Q-urdKJX z^;O&pK)SKjo;QRlJ&f<ypc2c^R8o(PtRH9qx! z%vR|xZmhWUS4fEYTz?a$=SXn&dZU+Q;I>ddlD6#8p>(-|%f3~8!E4c0|D{>wIfLDfTm910nC(+4?ssuem_TRz}_M6xkR#pQk{d^jCElI zUNerQ;lAthFFdz)FNQ%br~(`dN#K#VeDY{$)GA>>QB%$XP?4SL#0#%Oc=H=@7IG(a zR2r5}8)~LH#%N7ET59W!GsvJ01A24~hXf8nSysLm;52nx2Lt=Ycb9kif#}4^8eZO- z0ZlEFN4yex;YXP$u|N_4`&UQQsa}CpZE9`SH-KfLss3vNHJlJ<-^{V5pB-5^65{BO za91WN*aVKFv32QzJT(~l!DZr#yZ@j>i9LcB253WFuZ{$DC$UWVo0;iFGb=52?~ z0_V2clnBE)X-4K17UZG4U*2lBCd(f_(&{~RPFbu2j)gW08@}na4uYZX4Y%p1nJN7& z(Zeo%G2ndhlP|T?X=NC$N*#BUTp@|(@B4Q=E#q-m7a)zB)_t8E5!i5OkxheYX0RL9 z$+@)6id>lSFGf4%bo}$#e<^IzWi^xT@$ zhETQXis}=jT3=X$)6Qn3DTyRcpq}-{D~)p+jVUXW{8^$Z6Kl_ymef)r&=qEAi~|qC z{zd4nEgOF2*()latN_|v{Knd6DA)BdR#*%>B18aQ&n+!%kp$7S^}a2V8t0N`6#2qA zfz37DlEVUCPqdFrtWIY|?nw$2ikEDtV?U(5!70 zbgigZvxQFhd4&xjq*PH8bD=}jRR7<^`qn?0Ux0znk$n+;6_s!|x9dgxA%gWBtqA+D z(5J2Bp#UQ7#-NK&ThosZBD`p9&EmV5{mbM^`Sc8v3V}-(PV53+qP9uFYzZYFEuy|U!(bH}haL~Kh>{9o$SzN+}U5gp2gfx5+ji z8tBMR$g*g4(rttJoCav7WB5G!h4A7U84t9Mx2j>>!n^t&sV{=b@9^Z#!gG(ixH>vY zl&R?|c}3SRCZ2jPUrH3g|IoxNjA)}^>=&6)80sRKDdZ@z!l}LhPYcYMYjwcjos4!z zCU@A8sindX6U~aw9ddtrde&cBKv4v8^F~6H3`>LLr-icxY3U~Azc&jP1-=RIPYXER zk`vw@q$B=KPce>ZS6Cm#hM7e+R1Lv+NnUA2KY8^hmp0|PNlOwTyyP3q`)-*vpnCzPcqGh1(wT##-DD@CT;&^1kc~!3DRI_97J7iGS?Q;7p|; z{s#a4t<094Q#t&lyD@@D^+TwCov+)-{J00N_Kpd(#i<3SbPmb7M`WbAe6nI6L!wBF z2+!91m_dmXu5rkyYJHVw3qwemTWZ7wW~8~M%2r)lvK#V9JD>+AqDFu63rT-$@`8(e zEA=$anD@}?3u|<0tjrHYtXyMi3vEn-`J0t#Y@S9)V?7)bet|r_D`f4EL7Uovo z?@P`w_neQ1kn6IXI@<4b+Wytj81mQN3V^Ge3cU12&Ss4x+cI1JL|xxpokcmbkm67~ z+qKG*Px4Ok@&bK`KKC$cr{9&Uk_+{(Kpm`s>Qi}}h^1GykKI4bnq7#%-1`s6`e$dSxsgF1IxZ07F!PQ-w(`sEx@Jq}}SH zn>`x1Ka_LpI=Y{$GXm<+|61d_OSw~1Ks^@L81JrbmydTXvx`Py1Hz!oTp4-JZ;`%qR&^X3}B-24gpegI5?i^iDFifIwkBG8_}$aWh} z5@MvF1B#7?<&C$pO`75*;6;A;1hRC~`|Sd0?B_`D7pazZ5!yb0Dinkzm)rN*Dv%Ec zOXek(i<^eq!qprl(7~zVq?bV#qr(^i`9EGcAb?`f%KteKEPkB>=mbzt@Fv=6LMZS* zXeo(_a2|s3if=OFSyZp6J%%h(oYhyjPP$posBpm(Gj$lSDGDdleICMyyv}w><~^I^ zkav}UDTn2lzaDdE^i}dvEIeCx@>hDM?w|by4)Rm!5ALg5XRr27>**P*eT3leL{ZW8 zkHIVzeDj^`bg`H6>>HdES)VgkZH?`E47KY1I?)dmVXM6s>EXg0G&-Fyd(n1i(9%li z*h)9@yMkJm(s=~pHn0YG)=oBMV{7eqma;scb=8^9_q~jTVmIZc*nV#|;Z3~lEAzwm z71W1ErEEGX%<8=cLOpli@x9&}tlJ4He*X}{P-L*shVp4ShiYkG!a3~D+>KG4;oOdA zkGgV3P!|TY4jD&VbQwzyM+&ZHM0;+;)Hqab?4;$V+Q^=NyuQ2{Xjd5{teIN;>1Pes zTcXv+@Griy#0#(t1=6r>Vd6*nP()GS-BEU6p&qKMiVy?TQ_w>o?T_knhpN3)YPyjg zU8WL;BRwsaOm-BE5A`2w=4D%m7GuZF(hk=);g#lY!$!&78Qv!}q?B+=erYvlB|h7V zDE4$%YlUTO6r*Mn3{utPEQ{z>)3JoUM!%)&Lrjz~Cgd%%(g!6U-w&MwBq2fsD%$Z7 z!s^_Rf);Zq4BrJE*H* z`g0Txq(5i7Qzb1@48KzYCCxs+-71~A&R&()6*Z|K1x+mWm6lB!lQi;*%t#SDpM7f3 z>b;*ondF$)ZDo0o3jrKp5B9`=J2%@yM&`DQX@2PVZWV^CyvFo5^VjZPF6&dnEUCh5SaYgeL{ zau@ebviwS95@8>P-{<|=In}A>pba$X>eXFt^9W%ZsdF!SfHax9WG!XRvA#Ux`2N=| zA1xD*3q3?2vgflwLh`-VOfTXZE3&-=N|Y7~iw)pY0mUtR%WDlyz z+*V5PWYpxLGcZlpDd!*5s&1M^>s|4lPWq6Rl30P#5G+G2K*Sur6+F7PQm);#nM97L z&hbW~M(V{M_2126X)s9_B%%ssaO(ssuk!@b+2N7){3O51veh$p%Cej$#Z+{VK~46# z(=2W`gRw}O&3nSI2ufR7xds%x+qpK72w|nv|QrM zh%xtmu5jO`%($vtLT{yYT14E_Ov|1;b-LEjfLhk35J=H@gjq%gq(|_8^xC^u0lI|Vr1w`^#+x^{Yi+wpCq{wQieh+GA1{;&+gki00 zDmg~^=-bI+e0%fFzRU&%b?Neg@&x~E)$yV8j9`P}zO-;%x0s8v@x@&mt^HM%mWGtM zKVlQK+oe1%J;tkte~k&yNM)s8+?bn~y*y^F$%4w>tkPn#v`4u)ub`Z(QkrjtUU@6b zk2c2(IbeWB6^Zwo9^CF_%&pP|$sCM$^U>oZ^*U2AGfh%o)#-Op0-x}zg%STtJz(zs zeg49sucZU_!?`6S`T_=HSj89sFacXE%T&YS`MDkI6u*3fPK1<<@qjX`9j}VS=Cj>j zgLx!_-4B6B1c*=n)4O8I=v^t2DN%TZ5>iGE71ayJ9E3ZThZgqGhp!BggYB;E|5AsX#S2P$PA03ceNjK0ISdv}^B& z$`l^bS~H=q+@U_|MUC)HC2{7v{(T+mNmp+AUyDl*Dl3!&u&oFmDf_yA-iBXd%KG3n zCrub7(+s1-Z^!W(-}%EP7>OEgT!XoY?3OU(hX>Jr@6g8!fgA*vZdI`9>?lKg2nJ~Y zD*S12KOiIQd~`6UqW0vNkqgCbYM%s>>ZvVN;I%*+r0le>s%6Z>GQIzpo`oF(Bm95x z1}N?OLu`OeWxz471ep&U&<>!$|8qyi8n@jf3KA}y;0Yk8B%MC?B)~f@3EwdXPBNV- zaY9z62ehEy?Z>!h-{To|Sr>=07rj&wVzTwm;G)z-UL(M>EW;jVIk?aScaP1io#>W> zS`NEf+VupLaGIpQ=q1Tbve~QFA-3?@LxNseJc+UDAUM z<*W&Muc!wZt5~1(+|O zN@Xs44vzbS3P!}03phc?q5ML?(i^;eMi&pGGZWnUvlFm(10W8>W&b-6{2QZCqaQuF zDA@8!t4=)w*IB{2yH`n=LOQ|~=%b=f74{mS1ruK6VH!m-=Z0QUk@Cjfa`^M^y_7<3BIEV1peT`hh08#`QMDGHx+lB`_a)6It%qOEK_O6 z)O*`^M}(JLj{R9WIBD(amld6-1=FLf)lB*Q9Q&sz2wg>bQ?bOEjLr- z?_VvsCz!XB#}PIU709tJ;kcipGv$HG^geD^p`nrq@6<=U*r{_l-<+g2x}zBAIIYf{ zu`|9aSC1M-2^uwL_0%qQt|k9gtA=Z*6xSf5o9I~@YpD7Rt6N$mT)d$YJ;{ugdb-n|`lO9>V?N}? zjYG7ZJ7ZNY;jK{Kw6XB^eHshuJWEw$%yD}OK9S~rZ>x5n&@}Ro`c{XW*sXJDI*X}% zN2R~$PU7LIeLY4q>tz=Dp-h?n*^_eNGYUs7Wsgs|&!Y!qZA4Pa3J%-8E_M_2BTKR5 zeoP{4-OPnsS^)`8MdE;a1+NTwVc2udf|G*Uwaz+Uk*lDiZ9cErD%2c-4$gIfi%<^U za(2`t^Cc|^KI(zU>)@zhI7G=(ec>r#7yFtY9jw0-ldK0T(fa6j3e&+un}EA+K5WG3 zJtd0KT5AU`&D9uCHeB9VeBTDd>&C;QH==OgMgo_`r!G&EA`fbcO@JGZcFzSjXl_H6 zP1(azRQ7O}X~!4stv8|pUB`9xGqr`}fjI)XjW?wDhkj|SW$scQ!P_dUdGvZ@V3YxK z5iNwpdLHWT1aBqkWI42ZsP~7r`p@^Mq-(N~7oX!IYrJhdcg?AnCAZa0rLUin$Jmk0 zlYECpqXq}6sG~-yy_a2n1K}JfC_ZK08^NF00n!q16L4zDJc-%pa8%-B`|m6O7}$gV zF{aVVWQUx}Z_oJ4uWo}2$sf(s}Xx7=VPLJU}sh7Ni@y^@k8=AW}2 z{H@yYYA@@6lHH6yzq=Bf{iLpZQjkHG4*z9Z8?5W2YXw!3eS^$%{ViWsOM`~N-n~opFa50eykyJ$MCO2dghE`*AQ9( z+G`O(+*BRyt?K@J7em}=#5VtfqAzsY5OE4=vCKA!ge}#|`?lHryW8R3jSEeSYwoeX z?B}R=w@j=-GJg0M|6)UwSad+(%-_SWf14Fb3tpzBe&%zu9LS)098t?#90`J*?k4WchUX*- ztL=_z5fxD2ZIQq7$t16qZrI+s*ShG_Zl<8PGOJ?ps~wUV*->R*yX*w?xikc&7A)5BfqZ-vEHCrq`HK6odd29B0X5b8v2?i>lhW4fpA!qK?A0fn$CKr~n zHw`GLjyhfnb?i4d=vZg~a8sR`^?oJ>pS?V5Djr(lDd*`oGa!!Ysn(fVn`_8atrxbr zTa~&ppMRgH)f?qnS%F)^stw$E7ayhUxy0wt*z{`2|3jrqhSerao2sZ=d>q&qPmtetOM-!?ZBdYAWq4=!6RN$zCD^kE`}^ygtmb zBQp8?EMrF`?&LEt=ykmC{;Uk#yVsu}hsfa=s-LCD>wMz zASo`|pp7bz3=8nl*mMZQ6(COW`A?<=t)8_XWrWT?I)+;<`BpCO>wi(tWAz^dqI&#t~!gsF_Ff^G{GeAx;&1^ zw_i=(sE6jGQU6}FgVBQC%anPb3`pND8fUO)EQjaF5VmgTj_zc>ps!tH;4ct-u~Ps% z%Y6MjVead#>i%CxQ=0Y9ymM>5`ACbuQ913HoHomnp>6yIUqStd7`Yd5;li^mrZ3s@ z>u0~}p)%KP7tSyqSVZw;SV_54V97Y(=d$&ud<5CMJ-oJUK#`pZxVBPDyXGtMwu~9I zKP}eW{SSJ}sB%fid+JGE^>*I+ta1@`sZJ}QSjz&$TD*iU?wJlD*$($tjdd~ba-7+# z-=ohIpq)CHcb9^Boy2Mk-cbbGjz&l9J@*tg>x0l(dR`}qe?AvddhM-lky-#CICTA? zYNFQ(K?S2`8=9hV#WHYl+Oc?#V|1s57f&@9uOMu5L&3%H>j?q#pfAxmew()H@MTp% z>*U<Ozb(4BgM^T&?Uv~4C{H+1m zPCGnAZ@7!X!WT-U6F2w}4>2+Z7Rc5motONgeIyT*Yx?y*Rge=y%m0*Xyq_9}%avAt z_3}mT!Z*Z+?TR_P@6yE2nr|yc%DJTm%b*d~eO@-Ceqg*IeXZ@lTHrC=w%P=uV2cJMdn+I*2VheQ(XY0Ix$G@ncU9;?o1gN;6i z6OcWBV&|C+!m7iiHlZVhj?Plhwe6ECR`+Asmio87uBgSEG|y(}qoLTgOR`1<-&Lqa znnEvmwK-So$b!6fkiB2YHN(Ms;d8M`H*F8*jPV=dr=3;#7D=yn*uwE~NpmfD^xi;y zXLO@%HF8KYR3SuVd)|Lm@4bQFoTWUzuRG=m>#5f(DlzAe!MOr>F035NJ#i+ubS)r~5Q@kTZvjy4sDFLbro4+IGD-K6E_~@4jQxKlaMG z;UBL8ym|g@m*$W2;eiV#ja9X}!0%xISR*Pz0oiDwN=xdRt-*HAObXFe*tMe8)kzc6 zW%4RE!{v>pJQr4{ny>fzD8-?^BW*>Gs8icfkXA*qe_(Vds^t=Jm>DR4n>ldY2ETZHoBl@TZ=1J>Rx9#lCys>;x zhvqS*mwSMR&d)nixRYJ!|A71{BPC!kN-O!I^t4(oEe@q>&U%;ay&5A_*MK}QfBvLw zfF1g3!N_V?VM6~QWLLVikpUJ%5Cb0_(|N|~!p=)$dIv=bmv=;Y|IYJJq;uZa(5eyY z2tW3#{o~k#`%SaH?jl`3*N_O*g#26A_f5sw8S-zjb`zMsQ(E>{6k%aKkXDP~Wh0L; zj3VQ_AkhV`pV9>BF9FQ086WcYhjD3Vt_GxAH_vd+QkfehN+;&;zEiQp9zTY(e=}}x z<{yR+=N{g^@9Apq=dL{pJ-at}8vF_`SZNEB@q2Q>N^e!w#jF`}$te4~EZ zZL==v&!R+Z6gO12k=eOINS1#rA+k@8t54VJ2aIykX2Y=@D9OAWm?0k;O61ovp&fml zlvA#03(7(>Y1Zs%6AzMLgb$~Oyo zHEz$W*yaRF(i+Eh3v5cK){M&Vz3%gdN#ma-&GjJkyd3$cM-E7EHp+Yq?osy_b#UYz zM9-q_;(LOA!+-MSKO_E6F3spB02X3k7#+Y{05~%;Xhwbwp5X{#>oDz>7(i%S0Qo(T zcS}2QfWsP$!y4@jp83RU5YT`=jG*BTAlU&Uw7=*_0ASqW1P-jil>h>`t5?9CLl!H% zTmeY^FE@y~+FPpA*{-^Uvobayn*ZOXyzR`C`Hv@nGdO+QyAAkw+R!FqflIqQ<#@d| d^qcpP-=TLLcHE7?Pjf-88<^==UUh!(e*kZDl^*~A literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_msvc11.png b/third-party/MQF/ThirdParty/stxxl/doc/images/install_windows_msvc11.png new file mode 100644 index 0000000000000000000000000000000000000000..0800f992425c5a1e6bb0ab796f8d36d0f395eba3 GIT binary patch literal 24753 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.pdf b/third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2a2e7b39f5ed8112531c8bc03d2e1c6f66fb63b3 GIT binary patch literal 29116 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 BgOLCL literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.svg b/third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.svg new file mode 100644 index 0000000000..a067784a4a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram.svg @@ -0,0 +1,576 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TXXL + S + + + + + + + + + + + + + files, I/O requests, disk queues, completion handlers + block prefetcher, buffered block writer + + Asynchronous I/O Primitives + Block Management + typed block, block manager, buffered streams, + Containers: + STL Interface + vector, stack, set, map + priority_queue, matrix + sort, for_each, merge + Pipelined sorting, + zero-I/O scanning + Pipelining + Algorithms: + Operating System + Applications + diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram_small.png b/third-party/MQF/ThirdParty/stxxl/doc/images/layer_diagram_small.png new file mode 100644 index 0000000000000000000000000000000000000000..cdd5db06ca14347e59838031f80715884d511b6a GIT binary patch literal 49060 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 AZU6uP literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_merging.pdf b/third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_merging.pdf new file mode 100644 index 0000000000000000000000000000000000000000..331303df1f8f6601e00f277e4fdff5152a35e6fb GIT binary patch literal 8436 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_runformation.png b/third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_runformation.png new file mode 100644 index 0000000000000000000000000000000000000000..a8d24e5214cd22cd3cec8699311d7a29539d2c51 GIT binary patch literal 33479 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_runformation_small.png b/third-party/MQF/ThirdParty/stxxl/doc/images/overlapping_runformation_small.png new file mode 100644 index 0000000000000000000000000000000000000000..2c130ca0f493e0a1cc4911526fee2a2aed349335 GIT binary patch literal 24171 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 UFu!FF=l}o!07*qoM6N<$f~atV6951J literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/pdm.png b/third-party/MQF/ThirdParty/stxxl/doc/images/pdm.png new file mode 100644 index 0000000000000000000000000000000000000000..12c7aa4d1a7d69149b207dc4047dc8d479bcaeb1 GIT binary patch literal 25906 zcma%i2{@GB`}at)(4Arxc8*vi(1WEuOCW$enDVN4~m#V{C5 zOl4ojl)+@3_ZjN@{r#@%{lD+ub#=uv=Q-y-_wqTP`#$$PH8s{_XW?T3fk5on^skzO zK=fb`h_0UL5bz|@ZAlUM@1UR7wHr*pPcYM+C&1s#_w{Z3Kp?^Qw10F*r0N@hhrAG- zn-B{hS4e=9uL~$3AVA99+tbh4>As7UkFQ(CvML`4bf@at)hjn1jxCKdK0H1&@%dLV z#UTWxXC!TO{M^fzrUzK`x!qaL2;6uRZmy$qod4NPQ_~CKTuHYXn?|^}Saozv zpG7$&IcPKApCIS-v>S~WjT^bXA_Zp0r;d+8XRdF2NmEM9O)>x^3RgMEM!xBlT=b1u z5$kPsSey<7dLQy(@q`x=dcb~B&Z}YDFoPkF6?h8ED|=OMsFJpMpQws7fJGredykt` z)~Y-rN?38dIYPBogA7c7YCsd?B|iC*MN;-kKD`50U26e@jQh`pO!Mj1z9R!=Et{VH znP5wWB9q%LI~Y|q)2WfQNaWF^kCXx=^L-Q17tXhwA);NI_Gj8@Hxb7}gYTXp?|<3; zoj@n2Id=dA5_MGB04CrZjFOnq(RKsHIu~;uMgl@~^~HtW)r_qvj+e>d#{QjlrLjhu z-s5_nR2M{WySuSvIqZ*Y(>^g&Iky;U&Y)h}mxwanKVqz)uz(mYc2vWEW2N^~?Pfmm&R9`whsBeG5)!e%?5FK9Rk(i?Vevg8FiP3f9)x0{4(~hn<1|=Dd-K zu`H$cIC#Cr?xM=WVkQp8;2GAVp&uQc(`P!(inzDx;vw&p&koFPgv>C(l(E5T1dL^y z2dSArdQiUD&X!B3#SH>!wy}cq=;Zm@{6#LE{+=4{q`IPaQ|81LyJzrwH}Da;7j()` zjC`Zb8glwNI)3{VV5Svc!2K`6+Fes?Y9;e#AFG#Tb!+2l#$sz#_N*iB@beX&d{G-& zcDqRFE3=)=(a5Q!5BeW2+`SpH3Q>6-CDmR1!k)B9$tj>8Y3e1T86rMRj`JW_w{|oB zSibmOo`hU(JNn13U0-5yC@EqzuN_&JJI~&FS#sO9Vp|Kovm?Ry*r^Y9-+c~gm2m|| zPjlJaABUuQO(z!~HV0tj^r8N$*6#5N)Y+rEsbDgRKl z3=8;=>OmjrwR#u_F*-q*xXT|9u&3JAceV~hUpg-@za}lk2G;)Ls-Vc2gTTJhdC;>8 zO^EJ`-s0|aESo(Ku3($NF(vrbE%rgDvRD@zgf7fW6f*Bl=%J_>^w|V!asLrN-2U{H zLxVT@@RVA0$f1FU=LK<)X?%nW4}Invg@*8XDbDd)rD*@r8(YbLH}lYxiM_c0GL+=b zR#(+1)1$VrX3!=KNrOH~!g*gf#WS&RT~RjLf8*reCG?%XpXYrbZAF@mEXo>1Y@{7y zO`mXVbF{8xc&;-yP$lnYY$W$$vmb`+i840930x{|rC!E;&N9e2K(ievkiM0g`Ti&A zCJI52gGI6j|H>qgG{i`9$>NsEmu}s3cA2BRc=1Bx^BKr!ju#I$^Yr5E!2^$|Gh>0t z{1RI|i&+#xHf7`J;OV~-1#7onCywzr;^p-ZSQ@{-)MK(8E$`r+zNFYwwMKwt%|hjj z?rtJ(MoH}iF(&};kGxhBy7LMKbe6;(QKvlNxJ6#u%0AMH^lS(5?FDRZ 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/pdm.svg b/third-party/MQF/ThirdParty/stxxl/doc/images/pdm.svg new file mode 100644 index 0000000000..34faba5447 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/images/pdm.svg @@ -0,0 +1,581 @@ + + + +image/svg+xml1 +...CPUM2DDB +1 +...M2DDB +3 +L2B +2 +B +1 +CPUL1 +R1R2R3 +... + + + + + + diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/pdm_small.png b/third-party/MQF/ThirdParty/stxxl/doc/images/pdm_small.png new file mode 100644 index 0000000000000000000000000000000000000000..6cc072f15a997c1be731626ae565a2ae6790c812 GIT binary patch literal 27892 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/pipeline_randomgraph.pdf b/third-party/MQF/ThirdParty/stxxl/doc/images/pipeline_randomgraph.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b03854f069122f3ac1c5a69e8289d8b7dc5af4d8 GIT binary patch literal 6348 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 Q5RHk@a8su`&vwE62M6qLh5!Hn literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/pipeline_randomgraph.png b/third-party/MQF/ThirdParty/stxxl/doc/images/pipeline_randomgraph.png new file mode 100644 index 0000000000000000000000000000000000000000..1f15adc8ad3779fa65468ad263c4a8671b67d6f7 GIT binary patch literal 21805 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/san00b_pqueue.png b/third-party/MQF/ThirdParty/stxxl/doc/images/san00b_pqueue.png new file mode 100644 index 0000000000000000000000000000000000000000..aed061903bcf3993a426a4769654dd79e16ca319 GIT binary patch literal 76197 zcmb@uc|4SB_&=^wQVEqc>$D(*BRg3p2V*Zvc136iVeAHRq^xr)l)V~6Nims>t;Ldr z43Yh0Y-0^$$ojpXXE^74KcDaG_t)=_b7JOM?)zHa*LA(G>vs3NsXp5Q{sT-*Ol$_I zKg^k!m{H)r-hF$(CkdivSHVBO`JFSkxNqO3X+7w}{Wnq8eoRa<2gtqZ&ysH24E~F{pQXfUWA1f7Zm7 ztR#oz0h|5#%=THiddvk!aTaK zV83@FvVqOmLv#XH3{}(>v_t7KyX=8B9#4 zZagEz-yd$4lg>teBYGzW&p%x;#t=vMNHD#t(f_PYDYB|aiwHu7o!G2X;=(gAg$kT1 zs25W0|C0IkceUR1C@!X4Pam>+cowBaLnuw3*Y*LV`{&505+6HtF!ho4O5eeYnPMDuP++dY?Al1M;>5$Rd7mGbkmLZ zvS;Gf=i?X-7KwVf`KGr@sjTMEx5@^-HfOtsxuu-1RaKY}F}`84T?e3VzNR9m-A+lG zypH{q_XqT2SM1InM=?tF=-n}#*5t=_&7PH08GO`*dIMKNRwtnTZ z#6pK_yk(>94PLN<^N$Xx%9JbG2Hl!Dj@rc}$$ebwdiiw+LYX}fr?$%(yv_^n@|zDC zU#1+3&X5iXnKS(}8Ntl-J>%dnqHFxiHzY$Yw~dR>9IriO1zA*wRdPE^|(oPXFFu%g5b4OLl@k3=lc? z5nS$>L=3NH2sjyMkuGf`3+rd}I39b$tNay_@w8@KWxDig+|Dn}8PX&p#!Fp3`ADDs zLQSJbU4)EG?2j=8TTCM3Le{?)fjH$tV8xyso)6=c&I8O;p~{N8iSsim--VChnY1?=Kka{= z*Ym-DKD&s8`dm2S#LO`una+r$u$M70ZH<2)*h?L%eg(>^&m_E_iSZ z75@Fhhs(pgDlg6=NkU8NPC(qW<@?092SYSVHr)g$Ma{k3FZU%3f)7qVA;&JsD4U&(nTaOhWR ztV>sFey%Y(+d~^A&agq!QM6#c%Q~EDi7mp6=fpV(xal@pZm?CVaiH5){5#TYpHylb z17h-RG_0K9%!&6H4xHC|?a4btv-=XCK@qUNQv+Oo!v+^FdRw6aA(NcpAmU1r)^z58 z3|#AW^DQ4tYQbEx{BRp(dgXf9sX+k^`dH^L?|tOd;yAOOG01l#ZhxJPqOlaOz0rbx z9g4;{D=757{g_fxw@k;J=K>!hauC>!+}G2P*Y?gIP43*z>oI~L@f{5sNd zUZp_(S|6=YIVPK4W5pTUV+!ue9R|u&E~Cajzab8`dp;2{!zrsAK7UTo(+GY53p!v% z+Qc!eEfp9U)3<%(K@+VL1A^XfigzBjIhHTGSzo^WwFC6^k;d;f+YiS!)+%Ft5{pHF zV-_98&n0+<>p@Oh#DniyUbpSw9U@jkPZ~UiF+X^nkbyRyAIqN$uAPByI0Rma`2YFe zE1dt&XGW%*B}6-E%V(v(R4?sOEU4Zb4^gU|{jAGGi7h2t`3^8vXjD|`FUB-?(3=Ua-58@1`v!$dubUl;X&XL|7zor7-$A); zy45b{Z+tnwNj<1WLi+Lyfp&>lEsT%29x!0f02w*i+qLx3YKhu-__io6pwE5gFs$s6 z0NOa^h;v9=Em(e$Z2hRU{51s1K{zD`*B%o1QX2=w0VD*~-~Amjk6aGSRymVsECt{B z?B2^@;)FE%IzJ6#+$89<1_5gvOJRp5FcHR0dkW!~hJKFsb8fnoOf#$|{{}y;2*;}z z`_Hq%WsravYa`pqbd;W~4suCo!;~gM@vlC*bK*Q$HUb5lnjy<+3E9EJACrDPZ$kTq z=s~NDlx)IcCRRF6_zww6or&$MVOZKE!E9$Ak?Eh}+1r;Egr_=I2_}qtMq&(1fXi*~ zQ}W*z0O3Io?d2frqTSPJ=MOd!i2ompQPXepGquT1Es$0%pJ*UR7O!A2ZGAOCX>s}5;@BY+~{#`B^T$?W#&`FTyO&gaJveV3sadln`1 z?>%5OP#^-rTy6L+LutgzQTOktM@$g_*gsnQztnR%-;4T**V%vUW}g+~nThOB*0F6{ z;MMQ47lxn8Rewylg}!2Q8j`AZ%7~^KO&M*xIvmHBLA?eUOD>-`h0|%()YRQ3d{iK{ zwif+sLix1TbM!A!_0wyn7ot1SInX|;i1R((R=?+>f6(LHC?q%QteC2`8Cqo+$i(CW?fd87Y~$uMK<%T z&p5Mu2HEp(ff^t8j)lkGR+_KTzdk+i#x)%Ev2E;QDR_^DZBkpK65HNMc>2bKN?#)^ zE^MJ~G?&?$)0CwhA7VFJ&O(+6miVA2z zv(hB_5WQu=*^KtbK$554+rG7?!~SysvN(V%5L8SaL(KtC@5@dW^|81Rjj`lUt0A4% zb5rZxf(*G5q1Y_^FA9OTFZ&E=9r+k=Q?G*@I77f#VchLPz6+!Zchd&ks;~ZP0(YGn zwp}rEey^^)WWXBFhi|OQrD&>3SGL?E4uhpr!ilK`Zevzv5vgAYa`F}1J~QKlk+Z=sLiFRNAy+IM)*@@Yu_xA+XhJ+ zl|uwQ2#g4kNE?9yteOucX{)D}T9X|5kyz|*M_OSs5?8_omI$T#_i=SM6o7pL7e#xN zPEzD1@rfGbOXzd=x2}`s{2U!i2l5;8rUqZp-XuHrzx`M$(k4^Sa{lb2UAbaIA42u! zf;nVNGyFbRkCWFi;~MSCNo_xB{FAcBGl8VdY(I13i8CvUZJ9DefT+38ynl!(?uRM` zAZLPvpT*f+rc)BqfZm7ZKvOf4UZ-^-8k(H1z3%<0!Yf$rHo?04ZdrCLgO? zkU&dv@2BrUYo@W76FdElUL=8V07TH2$}99_YE+}EUXb=^{k zTO;j@WA>Ddx|}AsblLK#TTezOu)5?z{&F?m(*F34 zUO^X_tD6!!3dT=!UV@O7W|CPu&~ixoK{ZDXc6Q)~ri?LVJkte^8BJjwE4OWd|m{ z7E@)T9SuN|n`D}~h**V-;@B=J5n)Y>q6fnEm z-^wNh1t}77d8W;oj`Um03#T3f#tGREko@)Hz0|7C6GCq!EbEkk5@A2>S`%tt&W>5^ zFfvU5(KDCJ`-gX8w$YfZne8655B)B@N5cXD8n7AeL0^g{go!sqsF@g2#$9X|p#&i% zFEdMkqV{<;*Pd>FT+KbDg7JvMS;GhhzSzVd{LgbToiF-dO1R!)yJ8}@R+qRz*2z1% zW5xk}AD>1;$)5Jo(1(^%-fio}6fA9q^anJM-lJg;1TYb9{jbr?asp*Ra`{crCE^-Q zV>EivBHYaYyR<-lE{VjiL&NqWd(*KCkNC8P3!BM$ zj0c$`ZBnpKkjq7yGgCvI-^q$(KT_hFm&beGwJ=-#RBe6Qn+wi*wC&~92rr{CJ^(DN zIXfsv{@wQj4S3%VWenZ4xRvX!GnDr=o%k~%kt%YX?;%f`tNDF+v#$p~7%2no18r#I zEEQ2Xrtuvc%er%4c8#v^#Q-ZbD&XYt_^ofa z8EOgqfby9dtXO$nYEq$dxF*Q22^T{&1e+ulSyinJjjNn$3(&b)PZR7@auyz~`#Rp$jd72 zgpiJIas2*@%>^46CV?;<_%k&LVn#($exFzwZWOu7OL6{e-FDC8G zcmb>iLMp{64GBw;iZ`|)y&xw6TfK<0bjjfZF9i0^a}vc;|AHR4Y_`#H97*CxEAEk& z&fpoK{0^?C4wna48%O6WOKT zn-w#l1V&YDmnd3=g4RH=87+~*jJ^;>D^!q4wBve8F$SvxI1#T^Sh!bHNA37aq4KeN zg!hd75rm^@;^-?OTV-+@e%F!9cVWoe1Ej;S_lvkOLMaD8Gy5s0fCPxhIXv`X>x^Eq zgQ|>%C~8Uxggp)>E=Qz(Y(uEi`c+ODqqMw~9Y9$+)4Z9lpu%RAp*!U1qWa7L7(m_u za;13*t$f_sp#NegqWD&a<^aE~T2;SXF%RUdpAmX>-!GUY8JKYaxVDZ~n2R_GGP~UG z-anXyAYBOL>AFq$wZAnLIkk29+)zu#Kl#DEX6y2WHE>9Z234lk9_9x%Z=2*9U z-Yh;x+;5Q}{{CYJ~alo8N)2@i>-1f+9w;owTiw=CO z6Dl8$RRFE1-WEhmUHogcaEDHW^iVJ=2jxt@BPyq~7fQ(YAk)VIpl*Zec8+q&8>ctK z*XOeSP(ciEfUxXAs#B}!S{FEJ zgWEtXeJ3;Y!7RbT?JUN-_o2ml6)v?vH&Cx_0xU)-Cj>v6;jBCM({~TGoS<1;qd{tT z4{3iG$|LLhc7g*#z)*^iX&GRVp5xk@P~(#TMW#dez&r=4XItaFw-T{y0ebyi^Yr)V zn>LatAt*HmrD^lTS2VR0Gyj<{hx%`o09yulAWR}fb`O0hXt@~8sR~+ zI};Jyd(UEB;o7{4XFw+Z91lLUdeAXR!c9Y7p)VC)z^RY6K~yotlsVY6_L0vqQ~#l& z;I;_@1uB|R0U;7%$*|EYL3G}q0m$R{P3nl7v!$>;hSgjjgic@*{y`um1p<#mc+Oze z<@7}40_^zHvX8)-0;n3&+8=l4ZBjL+oGtVHkjqUR&h_oKA@~AVyXJeS30ByVE-&gX z5EtuVbILRS3d|MG3!wPMPH!SBsASbhdd0UfBE7V1Yx3;?0TLcG-zvluvxGms48{2N zPBXIYd7f}9jJ{T5ksMJwhbiW#fv}nox(DuAwQNxJwJp` zDrJGPk45s|w8x6p=JwW|h-!5cRK%85!e{JM$$xR+lOCNQOHnp zxsYn&<(Qxfkdf3OW`uW#sZ7z=9$>(Vx^ghp7Wh!uDUCtUT>V-Ic@4dDnH8YZZCf>;C@&Ol8} zM(4$4BuTC>oH%r}K#v1@EC2*XS_(&QDA~-5!dxYt%G$Wtf9NU0XSBJa2rJtq0F=99 zTmn1-zr=kjK2?1r*lDI00-!Z z`6~|})_lAtk!TbG9#EhpQyCB|S|*T&`7GNL$E#JbmwKxmZAffE`-O-$@@(XQ48lKx z*21+Uj~_~abIh(skQ4sBM675|UT;Za&3=e$D3}*Ny+*kg&QVJm+1k9`IF}yQ@w{2{ z%vQ3|;C$zEBmT_`xbj0$lU02tCjU~lC7K!!M7q;%TE76?Av4=8F$>8TS8k0OShy5iR2j+LNgyuO{L8%OGX2*?JOk@nA1<*)Da7Xuqt8Di}@ugENi` zuPZDR5_WKIGsXaI9%2K6y94gGiz~YReI~%av;KZkU8t8so-(DlMm4f4rQ>>u2=DAMLX`X+0kRTqCdv^7Qj& zT8W%^)v6XK%YvNfe9=n9VvEsQ-VmIbB(k@RE`VgRHSA1&T}%{LWLFK|D|l&340`_~ z|Ilr)JC2H93{pRF+i_zBPm6*ARbPGskIW@D$7)Or+fC!G0GP+3bsWnA&H3%p&A?i}APvAC~pC zmjy9r(H9t&zW@X)xihkbMjTj@{%TBgdR+b8d4}f=>oZ~k1d507E*phQqZ^xxr38h> z6YhHX&fW<|Q*x*@m*lsy(uwUp;q1#Z8$L6~v?@;8-y0}`)ufF}RYzs<4dL?*XYV^d zS*~K?1wIDw;NpzvH#m4032np9C9HuW5CxEUx-+iTK*(Y(t?Y8@901oME<9{4HqN!B zS_-6>S(TdA!w{_IIz)AXwNkyhi!alv-F~bS3Kfv6C54V^S9QL$Vq_*LEs#bLD0I2m zSWSr6ck7CoqzE%C`F%WPHDrD16~nRincq*RCzCWdK=!tU7{Iya=L`t#9u7ht(DraN zHvyE3q98uL;}hn6+*`HevKI&7AQP!}R|GN|z^Gb$(e!byrJdg;t~iqesTz3zgqWrr z8{$x>Xv@t9So;L_YDN18drOi8&cs513cgMusup02o-(M0SL10H0mPx%Sg4;B0ayrGO85MD- zFHTLa22tDtc}1F$PWff8RqVmILH{k|B`0S8&68TE29LTm=*|cFTv~TtH(E#3q-|`a z0PFyLO3(IIr6YL2kjZR*PZq~>1JUUMFCXf5#sSqzzg$j{o2nLAEpUSbq)oYB=@upv zeq^y+oa|MF!?rEW$4p3>#Pb5xjXnu)(+ROTNb+wYfVwMwk^c%*&!=a1%_Vs^J`J5` z>8@*;FMN_cP@-2}4d9z_J3<4nmB8v2g3b}+%^I<5K_}YWtV2ki)(I7aM8t**|09{OGcQ{O?UE2#86><8QBNm<6R%x-HmfZ$*0=IC z3+0K*CnavY|Ieytl<4#Yb}}>i`szf{!y`kd-QhImmXlUWa-!o%_|s;mTxozH=v&F9 zIfsZ@uf1;C*u2|L0!|It&WckiG{Tzi%ZZ0RUiVq|Sa%LnetCDw+5Jnr+w#wX8jrqm zAG3!5^Q9`hb_%W--|7M3Lzib3KJ!wDa%!eSOO0EU2^ICtW3x$Vd(bkWA7Q5;4iPEN zLCdnnhWE)GpxW_noaR)fH_t$=uV*QO)jqtJTasn#{wl7?l95yJN(N!e3K?>l|Gp_n zQ?I8qif{x;k_?@8SiEvDUf!1!3))5-1)Wk$-?@y=#B#4(a03|6tlB%lF+X2Ts(wm| zvl2(egEm5fV-=!W%-?v%Awhjo4#i-7zGsO4B-&W5m?tFC*KTHHQKVjj6oag0UMt?f ziwFE>Gg*|{KkDNdW{CSu7rPo>8WS@`IicFWkTJ}XSX;vj^79=F739mx$Mj_adh)Zs z(|U5_v)58$coT@Wdwi1Whq-aR{HA^8!@ilEBk_CC8N_s0+FrHW(IPKs!4zFSQLiH1 zUHjO%sx9E9*ZMxPpm3vnlGUL2!2LeW3b`dip|pCF7%So}xoMKVc!3+7320x+ChZ1$ zaOu;vFge8)GZEcWHpRlx_{w-KN^=k_DTZA2UYN@xLwX%QW1ft32MZdQAu*_p^KS}f z-nqti@h%lGLjd=@#;gWAOfw=s=?`cepq6%0^yh;C^w`?XJ$*8M?^kn&68P=$=FpW4 z>2xPBQ4`PwAz~QD~%w@ldE!K_1|mG6=l_hGKr2d8S283*AqT zBbsea+d5V%D~^<#Szha*zmBJHA$t#iiZf@qWIBsaW^f`k;2d&NA~I9tPRu|i-x2Wx=DAj8y|PNSnCHdj538La%>vqJN)6&uxRwmo zmR#e#ZcwCY2}sYIZ#@TZdwPINKtzR7`BU%vrY?aoq`n5*6~m004UO{PCAfS9qN!G1 zGMe+c(=)#tj>1ra%1Id~nkCi#DB&#__v;hNDD8YZOH(O5G)3QGo6wB3rn9XCA{(T6 zArDFE6BkLo;C$Km)8-GTj_F*!U;YbevULnKr3iR_P?BKAomd?^LS5F z$2!5RY!CV>J;}fXRDa#V5#3)22qN~3BAc=OY}{O@Gfp#YtU!J4ZjDdag=oV3ndd)~ z)gVjQ<6W{A=Cr=jY*lJxwx4`_V_Wo2>uPGBYr>GEV=cy@r@D7!K6wxFz{oHBO#6-Y zv30%JhG1AM$n=j`buIp0&xv~^dbd!7`}UxgPI8X9~|+AMxH zAv-t>adm4CHKov2ZK3`~&da2a&4=+)A4FA>afzeZ+wrZS|%WL80D9fy#9 zcY!XNveY0Y49AfRxp$D(CG)lGbXo`pcQ0|8b1*FX^Ahm!q?jLe5aliVbSx!@J zKUs3-FkErLesZp9(4FIsfNhl%I!v;6=GzQb2gf4;O!xEEoP+#wUhXykd4soeOJ=I@ zY~Mcwffa7{<7m5rLe{Tq#&=nnCCOZXpIJ^&EvT^jLZn@61Rh=kItQ=a!6-ZV=Q>*vaN zXOpMZj4AIXj=D8!)vxntCX-?jiOUUdgYZ4WX{8a(wa~;K(&iY0TIU+MyWSM!#WA#C ze3q|+GPdFqrnN|UD;nCf?s+zQlrp>3KTx@P*Ddu`<)%djg%yB8e$@2+lCYbfAJVQT z)yfJ#N~)HV*@lt76U|1QDZCX!6Hk2xy*3*zl|=mVJF1C3`*cz^Q=d_MDL{|)a z2V)>CvvluJMHKPc(Ga?b*?A$B2FJ#-lC8|jJ)?>`88zSegg^ong?T5yTkO5AF4Gh1!VA$5%+$?;p(lBC%kZ7jy4g&G%L z#Uv|85fwL=JH>^nR9==eBBVF*`3o?kM%O@@9m3Z?>Lf%&(O%#1)bi0iDAU$>>Hq*9 zGBec#eUtx{QhZI{Fw`#G6|h`NE~LJA))%m$Y5@j2I4N^E2+yj6r&huboi^pcGkJzutJYHEhf;4202pg*{HDW`WGoApq8?&Ukb4y%ZmN3kjwQhTfVt^}v2w{Gbjl>Dh_-5YL^rtnmg!<}^8OC@kdmnsK|U*esqtmKf# zs*|Zicg^4T(-%0%{3_3y@cEN_(Eo$%lw7~J&}*ivR9|UGVv7qECY!eV4Yo9QGy^lW zM4$vl!5Gj^=fmP2G@hc}=Xic^UA&1SyX=hRsr9?_-B4{@hP2rYrY{&JalhtsKy3A{ z`hwBvA=;?cgCKRVC7TL=0X~>F3lVFxSJP+=wfcZ)Srge0YCTU`vAK9Qm*5}$Etb!2 zK~+Lh6yY7*(}nT|N5jbupk~F2vsO0Fa-_T=o{Xo}l<-ojev8R= z(NeG!d6jQFnJRn;aN9i(#tI(EmtFo2nyl}#9Z;JJT}oJ0Rrb?MslOK%&T8phXd6G) zqKIk+9gywTt7kUU_lUHtN}%;_8nu6E8_vDw4K>%G>P@K7*?r5GeyT150x=yznnHI@Nn{`e~ znk>q(Df@NX)^W9OTUzw$tl!oaj*#Uom9W2_Ph1Hd5yE?r*J+7ub(MaVu`ZdNBe@`- z`-q&(6^qoUjjWPR1@Ft4xE2c4AHdhx$U|ThEMCg#Mt*LI3Q*i{DdE3@JkGB6_FWcq z3RQs8F-xz3O~w3(+^bh??9zc$B}CT*P(RSE-s@j~;?z`)RjQM8P{2={7ecp>+=mWXEyG4a$ufE{5D5!Ty14kkOtvR%T6{a zsci|fKGztnb;j^oS;Vy$Ngtt_5oFiJC|r8y#j4~Bor`zp1!P@&cqLJn|1&267=?;# zz)C>O{qmOPFXIG}`A!$KbM|vjb517UiY{55n6->Fdb3=QmvOauql*v7&p%?P1U1@J z(ZM#^%dU z#MfbF>afD^S*2q6jiL~9B+no`CLuCI8&|&KJ9n0mm_jlDduj0>@QUjZ_nJ-vqu-F$ zey&1K1Bt|trq8M<`hu57SRjwCMmSfF>=*u2cgzHNTGP~lFZv7%#I9*K%NB|$e%mlB zj>ia-7nso$7t666GOq%rOX>U zGuWSBT|hYAT%K$%Sit2p5TZL2%F}9=;cG)2?-*8CemNoJ&5PIFPi8K>tQO#W`fdrJ z-@&+8vsz)(1-fVDh+-l2?+MNItmot+f?}gOQAXN~qb(`>(e4AQL7|QGh?7+#J5&x- zk~~NDK1(Q@hSFlDOA$Ma5CmA-DG-0^p@fn|%Fe(>HEQvYjbMG(*&?b{G;;d*y+4~%NF5Xu zWWKf2pzg@^Qt+LAg9f{Cn!1qds}SW>TP*UgRo7+Dy*o=7VXsv1yTuWn z>0~V0Ip0rbx_$ySL+nyb7Oxb$RnLhf)Z%AuJUnfJBxR(9LKO=4L9!m82(Z7Km}&*7 z+;-_rJAop)w0-|Z)bCaS1EC0%hce1rHDK@^Cc<9WB_|N@a~mB^Z4EE_*>mNZ2a%vE z$UVTYHLW91@cYdSs%s#gZ)%(80}=)P0*dNoSA*4Tk-DwR{cy`#sOD$X5;d>CD@eyC z`=y#ZiIuF;Ph75B`6yu^ei8Y)=S7p z?*akH4F8DQ&-%0khyW4_*P`f~cR9zv5C|mwH@x`&wmFL#JZRTVT&q!ZM{L(M1AiCx z<>ClqDlJE+<139(=rBJUh4YY6w7c4tfZVfpYa>!3nPbow&$Of)o5uw zU9~@(-m3Y%g2aFEB+5@i4)k5w-HU!Pn8)*du(@&L*CKNI|1&-SajP&sHe;b#6%f+5 znNY0?Q+Ix@vfiNqttGQ~uu_6+Dm^qbv@8fg@7Q$X}uy=bKpeF zXK;E-W66L;Hfajz!qcbP?9Q&*uprlvcEI7nEccp64j1(-KA44iA8o!mD^mL4%MS&I zJT{0ulFw1t6Hsba^9AlByYJb?#!P6s0kH97Ax>fU(=%f+ z9BI1Ldzit`Zgu02LW{SJpSErMTc^MX z*jD;#9;Z^>wDCBokyq<+0%@iuwm^Q&&yK^}81QC{7Uq^m699PZ(5}VvA8ur=B_#SC zc4AU+8=ko@jSgP@DAX>W1Wv^f=-aO*Y7J^~+Bz9EKA00M z8$1~z%d-RZ3{B`edKZn zxZFQT&a?iB4X{o~ZsS!%xfR@{7U?_vkq^(B5!UZPR2bmq6q}(w=Ci^3xmRo7wxvoJ z4rj;Ga1$Gq67eSPy^YO^AkkWO>IeOJbo|tYGJ|Sk11~yvEPId94QptUB_`;0pxsho zi>h1Dg;pIvID7^ceEdxKfx!LR{t?S>H2rd9tw9GHo4)p@I7euWa43;nOBNwc(&k|rYt?WPE|w}O*6ouL4`9()Yxo(E$IgvexNknf^}rW2BUToam07h^23p*mj@ z<{ENU>QXuJogScz1j(5D0ND;u?u_|6z$3wJ6&v}=$M#_=UJ2vNlN{c69UC3%50^gy zL+G<~2r?tak7e;2g?P=T@QhR_o489}#A!ZQ%?m7!?~yaamDUEnNA`5k+p#r|ihVr7 zK6P>7I~hSRu+mE}whIfof7ymJk;mq(l9Y)EO(B-=MNtomG}6ZeUssvYlN{=z)9Gga zBJrD=`5}I-Ar9rruc_Sli+>rI=!V{oSgWI^;dLvD=Lf90mR2qZ=aimyz{xZV`#9S0 zKc$30!2lh6=DPNaff?l&34N6Gw`VnZKLUtO+3>caFKN}W`7C>Bd6Ig{A!drl(T~6O zrZ8CzoZdxBgMw^XM1KPh;C97-u|=S(2=ULe6Q?L0^Z5gt)ZN1aJ_hS<3ZN2! z7~m+vY2>OFI-E4MIgz3I>Rqx5@Kbcl4SIX!+u@fg%4&ok0qk+fZq}qrz*G+`Tt5SO z(nwp)(wk5V9WRfn?X+nJUVb?4hu@pH`G$vAOHibzxo?Vx;tUx+*Wc~e%q`~WcR~3C zl@U)mwU(>}%7)F6hJzVlH>=1WhUfGev7~P@K9_EQj`{;Qf7k;$Uo&+o{tY_I`H5gw zBbmq;i^}5#!C$<=P3y)_2le&Si;A|xQFdOHZj=jaz(LHFk-b51!aq^q0|vxNXPW!e z$Lb3oMTOsnX<>nkO8fqf8$Zg=nn?)d#HEy%0V65DIRsgjq6nNjEcEo}cwGJAIKCbz zh*oDcpA>~LFlD1^Sd?0o0(t=k(EM=D=RpfSaGcgBA0b+sh((75`gU->H1FlZ&Wi-Mm3ig$rD0kakSZX=w%Iq( ze(sghs~YtdcTL8@!?)yfo|C(stjl69_sRf22N>{%V8$=&4}y>#NWh^ooRw7A2F}15 zRrLfM#=~WF{9(LVz5%r|;*RQQIXN3N zz)|vV-V>bZ(!GMQ-=X!9)|46f*#>1)fKfS~?K@%*ti;7ziB{jjv(aS<7D=|WgQOLO zX>5M+q4vlKAHJ{DZF;irsfXZcRFj*IG9U&6Z*T*nExrOg>|z8mq?cYnWrIz^*4ZdF zO+Eix@XOZ*CL?bjM}V`szrePD-)}Blq`PWP`uX^JEq}%5lMaxpBKXBoepcQP%)D2o zodBnDKY#VDY0?~02Do#Kpoyk+6D*q%}+E7S{kpRfsZjt1l=d1(1SkE>T%V7SqBe8_Y0?`-IBgNF*L8(&CW4VF7;3eI(8 z{WAe?RS`w|&zKaCue?a_hYw07j}GBS*~!i1hk|TectuXAjZHS(3#Q>!A^+nEkSE5c z0mk+@K!9R4au}!6xx_uxM)If3DxxbO^r1EtoH5?(xa|PcmIIrnRGFC!C%$Bmm@P+t zvepP!_-Hm}Y6se!UrV+xy9pNAEDK~hiT{oAa(d}EeUpFPf2lQ4FwDmT$T;ACFk$Zu!;(2ghHL|z{b?Xm(0Yq z1j-gvp4h@;C_MfaLxA4D4y!N=3xF+uSR;M%S8l%4@NK{DvCzq1RbqdZ$Z=DD=XDo|0sMnjMjx$X+eVW)hv9B=JZG^3FM>rS{jwJs*&a#gi(x z{n(Rk7k=r2*_r}l_}R-%^ALVIIT34Arn@}8D9>O3Bo1f#_GET z=tPGG`2_%`hE6GeJ09izPz}X6p!vDH>o;h`x*mN+&Beqx*=UbMu-7cHzQL81cY3YE z5SBd-nrpo?Nk0p~o#*GbDH=R%y0~HTCnu#$L}}$256@s(iP>pbnsQMJP$kZcu3&T# zPY6`m8+%@z|2WUSt)dbrQ`}&tGRwJ7$L+7EPlknS{L3%sH2UhiAKbizw^yN+o>~sg zNPO~4ECC}QmAsUskoBr32x$l0%P(EQD<|YMTY2y!we87?jW$(IUhBUc`fUoP(OXdK zDT;6?=NOc|Cg-bx)7)W5HR(_#$Eq{UW%KQYg2>5Jq4dCenx*m2QyWHo!lb-M-vv2_ ziD#pKNW%ND7la1M(W23`Y(VX9*`2;K70-`POtX#&*FOv%-k`L8cz$ZYuKIL1pW_{l z205SeWm-L=rb%XX-NRdde3aRON0A6JaG^x%^IywgoLu;PHW2NweOyGbl**Ct+=fqV zRj;H@{`K+Hx(`d5;Tif)++0cT6Yb0s&EDwhW!C+qY993kXHx3`DbpR zIIktb_bhJFM3Ya4{xh&fzDDpqrTw&jpk3HqSQ4!}9bK&cLG`f`F}JqcZlKPe+nzOr z@}`!JOxg#YWYUm;JjJ3Ru~=W~BjrS+W?x%M4BBZ@Ua7UP5rNf| zHual}8=R+S&R(4br|t@b3)jZ1{!*=aHzx(fGyZ}7EOiCL-SZ2Y{dJ$;N^as)BKiP> zT?(yAp%KE>!>V%O+)1TFTJWJQm#w)tXNuH(#`pewErQ}$gH~P68|qxJBC5M_D{-!_ zfbw?Q40d)_0WG+j3K~>cQBPBd3RTpqv&NN11fmL7ZiY#CCH$J7mZ=#{vErOHD2p3<*9vHP8#eWQyQQE?JaoEyFnQd93SgkESdJx;6TStgajuq^-w%*3mNU+ zWc31PTEcQ2b-sNiD3jaG)iH}pe|)XvB*UVM7s1&X3;*qk7#Ig&zNzvIIDgr9hfjU7 z2|8#C9np|zF_U-G8&ZLeFvB)pTs;oKKK~}etif>j_A^6ptu6GfLeAlmI043h6VQ?b z1Sn(@JGmhqVhztB)ufhC0}gk%<)DTQBP|8ZBrmCXI6ved zG*mwK4|zJW64dulbTMwot*L(_nUiHo9hKAMu)kJW`S$3_d!e0o9U#YPfi$tm5RlD3 zPOq&njvf8dY%_L02MI$G)rVEcT58x0mCQK49gqRf7c815V>uB>X%Xybln}7( zzHXm`<($?_@1jkzrz|H7OcW2{hmu7#l$Q-XfS6$uuqj4B>F8WL&24D4w^~)$YVnWs zN3GSrukttz0Q!Tlf=pkAPP8yKZ{rAM!ApS;u+%65pfvVKELC;PeRimGC?lbNaH^lq z0Fq-ENZ7v}wAd%$4@&sB=tej;$)Q9{bUGfHH9UBG9%mk*UVW8g=oJhcq_pLJD2#ms zdBxM|coDqM_OZ5@v)$kC&ckMOfUGp&$DlXDhyFc`JPOaONfZxKpE^%_ZM=aEln-VL zioY=qJpXe_A3Avkn=to&hF1WYf4M*hlKjuqBO}A%K-zQ}m_&rM47HlIjh2@RkQXrD z9<^5=no`n)kNp7lthf(M3TVJeeJ%`5r+ol>fIJU;Wb-yT5#6RqE88J%U=e_d_Ap>rnZa`QPFVVY+O(B;E9 ze6`>_#LiJga2*`OF5*&;gF!1g;{Y+RptanGbuou=%-R}=E9R+b{&yfUYr}5=Ohm%llVmQ0z{xe5_D^RSNC%9|LOT*uoM{8hZge`k zW6*nWp!0w=7_;2s@6h`C`|r{+S8z(7!C!SCO!tKSa|i*}h15dFTNos;1zU~lgq90* zf4}n>!#25OdJ0sGIQc8W`pY z#Jx1K=DXhzvkh$BN)Sk1; zcMVM7j!tm~gxs}8S=oFMmXm*xst-vy#UTc!mXyFb07!fwS7uZR=z0BE#@`Vro2Zlq zZT4;LNDnQl4^|gqkSu2O1`2Q4##CgB68lHZUg! zivdE2OBUb=#P%G`wA}gw=oZK`;jvxvXBPBRFqXV|lFfK7LMh)ZF64a2pEFu-!u0@F zLq{eYwh_7fr(HOF?ihCJ6ez>>wVI2`>IyWfZShR>X7t-Ej& zRm)y9ME)d;-A^y_1+;L>cVTyrJp_R$TC{uS^6TLVX$^7}c@Nqmh3A(& zbnv0*C_blYaA9#?20Rz~Yaq<%DQiB)`pebMu`sg_7MY8m{y4ed5w)DT$2nMA@Z#6YB|8JOQG|oY^r7grBj7b)HPS}78V$XO zaQa(2vk&0p9omI49XOW=Q~B^U#RYKxNEhw2`bn!XhB;heU~}?S)Vjmd&3~ITz`?f{ zAAc0U5=c5O0`}kkj%mhhU#0`-OLoC@$X0T7S!CK1cY&41Nb|$HxL?2Lxls2DIMr*UP-BXFAFC;0SULb#`1maJH_evMe-kE~{F7UaO76)zvL< z_O6@jrr+3p|Abxv1RE<(<|{{DNklQ-!uB^jlf-X0@PG7Q4jlIj_aj<*%YS!sFDg#& z?+?kY31KeuDH~D#MP>PBX|#l!?Aw@J6XKbX@rx4VJfpY^zfb!BxcT)i^ADQ4@R#=C zQ*q702xinS^GL4$*FOqqCHyt_-%uwG;!iAbujC$e&n2-z3u3>oX6}(_^HO*ZxOWe9 zE7{J&G(Z0*+9IbsC-DA-T_K%z#{_Dh$t0e@?VWU~eWVur)X$;a?cM#E`na5%WMw)cKEi)e|PQ7 zgg=)r20oES;9lNyDP1wTbom?GQnlPWxkq?|(eF)@2H&{BP8sW zRW0qGC9zB4Y+SBCn~Q5K6xPV6->XNKKYX{cy%^}lx4Uj*9@;pBng|``8NuyMY1S+R zn}^;AD}XS;koEIY&ts-SMM;+^?(2hky`NA!7eLoFbw0R>U{jgy`0df@WsIdX6It-& z&(6nM23o;EnQBHxGuN1?BC9XI2dDG%jiGq9H_U|o0?Fcs{L@#4#=9xW-vaA7^NYm4 zeY?tR{u^cYKN3sXU3lqJ=N_EpVccG-3kR$VSYqHg=?+A^tNUaJebMWT&+2qB?P!My{W|g@yb71T^wbY+hLH+(=O)kpD}|P?&+CpQ3gPy$X)&Q!8b;jE zGA5}W(lwm_C}-PZ{LssKMKeDg!&&Jjdw>;e8)}t1Y^WU>(hZmczgM_EteTGAOE$2f zyc8}8d1mqP03LfyyvO}7{=&SgsHH?>lbt=HVT-!@V5Q+0*3krAQT65^N2%us*vh~D z(|P)Y!q1t|S~ck^$g-bgOI&RQlH6 z#wIBqfln_KwA{aROEKWCUSa{=atfF%`Vy0b1Dy%I`KIz!q{-H{w-WC88@cC;4dce_ zHgSEU!~2U0)TAOwGsOC?W|A(%xj%%`e z<7ObC2$(26ML?yLZf4;G0cjYek|ULr7@-0x0;8o>Ml*VJs0h;FfYG4zNQrGU?>&5; z2mQV8``13O`_vWR>w8`2oO^a)`P;_N#!$6If8j=o3C}eJU_{*zq|BdRV82q})6)$6 zV**YSjSA@UMs?~P;#Q9${J9W;Z;h;z{8Q!33IWLB2bVr|KqN{zYZ!;oFryw<$cb4V%0 zAkhYIH=GH+E7}tme;})WP%}gV5ZRTcH7o1OgE8ejAC5F0ngjMK?E%tIz+o(`Hkt>B zcOVw*N2)78H&{9<{F#x;1RpKE?9+dBDu6up`+N9C&BCE|0UDA(o;uG3Mk|BuQ-$o@ zbWG6~L?fwDWm!z5c;8oT|4izi57#02?4|1IRgZ^)J{p72qWM&BM(%5Fp6L{13v21h6g>EA@-{Ti0Q zj2@-sn$WprJdzPg4hFwql^c>O=2;)n4)rVslnR zJfJ0TT_cOTS2r^Zgmv0VI&?U)TGD#~54@4Acx!?Vm5NGWmo~0>?|BFo%htyKL-Lx{OAnXA9W;oG1?V{dqF&Qz zs{9)ffespv0_cARQdQ;RH5*NldNVZIr)VhThQ2`~JG4(r*1g~ACGIB=Xm04L0dH`f zwyVwviUJRXbF&F+PV|Hv_7A%R#sJ23wQosER(vr()Z^(6IF9Wm7<1`$)-6dbSzl3` zmr64f^kl&NN16R(Av~r<-RNutp+K{2hhoB(AL#jS`heX55SG_Z>W<8qG@vFzPa|N8b{!l#^=(cg<^ znw&~mcg>(WR1PKT`#;ox2K&p(<_!gs^L>Y>`C~^Il~Rcv~+c?9s6@!mN}&ZCJ@4o z6be5nV*YB-z@BUa|ETQ_j`Ft&`* z?tboX3UlE9V)^mDMvwD147vuzcz|fcj+JU}3{Z5Py}kR2h=XEMV&X6KO8$X=4K$h; zv`z!lVm<+lJV%6MYWMcnl5IZD{l}ar;Ou^4S^g{ACA-xLZ;$;#teh=c37*4BQR1)R zFD*`Ie(;y>qCiCpNgc8+3ixq9?Q!;?bxXozvuIT-3Oq!7{xXm9u6x>q@Y$~+MP68lfPU%m25JF1^WIwml zL1Hr^rd;?J%Q|;4f)LnGx3SDonTHOspE^Vj0YOmaAofzg&w>E3qdN5mO{9PaR)!x! z<3E_%UiKR<+p$_>7W4Uz3+OtITbo zM4uM|$BG*Mh)`?4v7oQCSSO5A%d6SNPfTv>uNAdpb6z5jp#+f4PU=>@7en)d{>6q(}vIZw3tZ{zyUr~%t%r3do>76(%^5U6VoGtDZsiKLjr-DAkzZoIb@zjybe zpO|%($zR%1!-8E0qqDUhieCa+#;5&7Gyqoqk7UIlU=AMH=IJ#8)bxPp{0{LI^l|ZD z(p4Q}=x@?MVSJ9>h=$W^M!jZrR)dnZfcm_&tTb;J@(t7C_;D&D}X< z?Bq~>ni>WAc7Z_=sURHG#Od4<1e^OaCd`=vHV~kmFxNI)c^`KKH_pQAfWwIpkK(%R;qe~`}GYckSTKw>zR7{LGM)8A>aA-&PDzTFA; zRi9E~zos!F?o~GV?Ay$G@m~SP{w*@#1Lgu|@J&zGfTa)-_mjwZfew&9(QTJh$xJt_ zyrA^@MfIPmu{%H>cmV#D%BHrKKH2e^G!$_C1vtfi9H~D{M^N)zc z>MD7}U9A-fou2#&t-0X82)qfIGWNT`4GCSL%chqcXLGc#twtqYXdl%jWrg}1zz%X_ zOijE-rDvwg(fh>YM-6Ao8GZ|&Jb-f!z<|D_tK6c0_AB&`<4F5lbEK79>^)$G>-Ke& zoIAG;rz1#a-bVqd=5^=rpJX-e{|0GHE1y%9k;ENBkLJ0<5kS~k5d5O3xHM$YM24C- z1mIU(DjZoDCEJy6Hz@H0tmK1=8Fxe-13s~u3RTsN{51LWu>1C$QP-aAh-gt)E?Vxg z*_Y_!j8m2ZLFu%EM91@;C17 z>e$qJ%0t0|ujEcSfaNbi5ZjE0lUvJnk9<1^eDx@yHGj`mDHsNVc&u9 zh(iY#U`V!vp1gpxu)VsKm-lTC z*=jVA7H5Da^;%jGT{NUkZ~Pm+u~(@ydo>W&&8OjBLZu{Br<=U(@&GkhkQEsya=&NCGyImZDE)afl7?;$=GEy5NO{gM=WVPN;a$z@!@=YgV#{Z8jX*4socJk+ z%nJXc*2KJ?qyMe!xt=*Euc^fn`4@}PD&0hZGQGk7vV{vYztge*fQ>avkNBYtC?{#k z3sy+kcU~YS-$LBKrA)G;270?NapusosG+GdJ=(vnA0}~tsmDMDwM|`A%=J78TtFU{4TgrzzZOiRl7FxlO^&Q{a!Mt#fpZ}ZZe<*dptU*aFrzD zcqIQ3#dk{VY{=0=iN{c(5IW+2P6dR0MTHj&F}1KrydS+u0WcSO3Y0ItSlU=EGI-Jw zpD)hzhfiXUP~xw%`1a@%V`MEJNBFzQKv>0)p@K6RvQ2A6%!f~!F`)oyoKXS`MzcC) z38-B~()_gWeSX^VIJlh}s$#Aj^fnDUV06}2QRgZxM7i6DR1*21& z>m#+yP2m>o519<8{%}M1s^7X#+|L%VO^wlr98|cM-x38J8?5ixGX0$$(XJPe~a~YY|qnG>VAT zS&9iSq`}rIpkbFeM$VD;U$Q`bTV3)>R#?947S*s{)@yxepkoe3aL+T9ArlDoHzNGB z&BHm_tU~{ZAmk6g6__kG4Ngm|E1ez4%CeVMbPzV>IR?E$r9tJjU;Ky$`!+7V$m+dy z)@KKMYyXnltS!#3DK*3Qi%ETIY%GVmLQ{eTlY*+gpF8n|5~ZAdFXc8UD=4X~@dQ+5 z($2tGKO>zum%8d#l3a2f6^9CBUW~j9PMYO3zi_C2W@c!gq(fc8y`-oVQ+7&ljSi`j zTiC|9_IBwt#+4f%{nm}Vd^@33cnY)IT(O)^??4FBIc8|_ON5i3OQ%RmK2l>#PXF7e zp&sugI_UnBnLv+2zQ%kWc&qiM3wnnefx>wC<-cJ>-NF)c!}lXSiKC0Bs8QSf+uK1b z(Crvnlr7?YaZRv-iYN#l+W^91S1Wo-NF?YF{%yBID1>1kFqma4FLAW{79EOb%6I98 z5?@!~dfzoFeA|w{w)q>W7X&w6@NU67$W^C`8sEMnGUD=oWPuG7n&Fa*H3=`zK>Ny{ z5Mej{T>R%Pe2tjrv6+-vO-G5NLAQ>fs1|qGcqpNLEwm2(8M^Rd+#fTz1DQMCAQM21 z1xm_AR9Q`&#JzK7bSU>$b3NWBJ=Q>>@IYcM+FVn>e_hSXF&{~Zw_*voZ3H&XWW945 zIuDh+6&lT07{1?Ok_NDCS63|mXbkKhQZf8DUCkGx8NR77j78Mr{cNR_b z0Qi*3LXEH$B{r*p8XL5@`^}sN+n>fLe`EB!9_1l$U>}3LUU)yZzo1&U)z ztT+*ik~a!S83$EGb4sjq!x5|nwqtbP`ng|8UgBl0KT@z2zjaNo<&Xe`MGBA;Nq~)c zX#2g?R|eQ-jEEtV;rf(W^-lP`;YSqMx=H=MbWe}t{v~-!OzJ|>6YCTHR){AYhgBWy zV+y>8M;8X1bXFuIy_c0SIh=oA;bd?G_%}T`%$A7%>dLd6k_3=^E-lG(sh;-r}?U&;ytrjXQE>KL& zr?Sv{v3srn#W->=g>8Q;G#H4>*_8nNM~EmV(jaIK0VaT2(|15$C~{?>Bj{oVm-y<+bypvCUvD%`*K}OwM=6(q^R)CM9Ip#=b>X_jn41o zz~@xhywn9#GK+K=Plwc*g%iv% z7ymlW6F64Q9WL~YxG-LI#%40_HNoTOz3Ml{QJ3HX9J@^Pneq;B;ei~aj9vU~Hvx)CuATc6Jtiah(g7A*{w=G`D+m zc8O^KHPp5_d^OT>?5^a(A9+I{Csa0Fnx2{oRdJ+|6iJwR!jMWYx-JM|6$hGfOpYFx z2o^%_Jn!>vN-voz2TrNletV zST4=SPi0ooZ)56O@rHgSndsJ8i+xPzGoK5JtuIC5!bB+WK|maPbMCK|97jdVaUDy} z${MEzGw>mGbC1N!tZQTbpf7+Y_K*NA1Bp5%%7DDrdl(*nr#o(7jz1^(n-K#+NBVyr z{EsUCnM=W~{nzPr{=kfXJU{sFax4}~36CL{=5PD2-}rL{AS@jSUJ7iElD|>->zm)H z-|jr_ru^|ks{$qkD|I86cATlOKR=J01^8Kxj@h3rQ)y9B;tfpD{i`RC5rYufm%MNI_jmj>s5+Vll7H3$f6L)4jpiTq(*LKZR;d0b#~=%?;mAMx ztcN~$;4!Rr^PDOINJee(LzV&T@r?3&iICUwcU=vR@;j69-*)kTYoLa5gHUycp7M{z z;6msGeK45eqLvnes{zP=-NNpFRtdODxADot;?f^I{w)q}8dU1v|Dgw=*s`gC^~awk zrwDavo+tkjVu{@@-CHK4E(elVMyop+Ht{?n_OBJvBA57#DI?R#ip_g;AM{7uH2c+} z@I5a6*v0=5FTBAangVX;w~jwSg)i)Q>wUw|CC`TLUn9}Ij!eEdLK!u3Fb+@z4wQ1W z_ODr}kI#aiBh*~r)||Y%-JPPPz~SJ#WfteiRII=;?XZvX{HGMa%mSsq2&CeI79>CO zTYe=dpo)=*?Y*1k_z-(}0Wx?a98PiQPZG%BaoT#M7>PvaNgkufQ$IIMuGiHz`sv%XsFe6 zQNe#`PC;0Q?Tm7OFOod@tu}8m=yCVDOZ(PwL134`4D+pz{KRa5%d4I?()8evug2Gb zcaxe35~=PS=tXey-nG1zZ{sYsD-Hre_ruQI&I1Z^Sb>@~6z2RtjvLRtvtZ~DRX$=N zJD{|0I!clNtRC3j!k@;CPuw>Cl&8UEQz0uxfak`{T+Tsr<+g8bb5LfDB}vzBccxi# zc>M5@2MYDBODNCD%lv4r>X?Z#_2VcvjJV4b@BDzGwCs%OZPPIdV22eBO`$fNdUR|* zl@O`!yyx?+EA(DX;?S?RTen+pN&8e8(^*aUG|e+3%%a2YaM@nJq;am`+;iC%Ipx!m zhQY5tpiT3$uRBpdb8=nR(052(IL8o-l;DjmGR+I1#8$&rOEKq>mvy$gU~()!nTI)- z3`;z_VBQQ!rH|8n2Ekb$&|^us#{v-61YCO&b8KL#Pit<20boz8AuSSGRL)WNa7^h_ z8;hJeqY-^7rpXkjaGTGnxbeO&+_H)7_2b}>+wJ)RNsdhRyABv;UkX$qe8k1}sgCu0 zSr;skjGIqwb>YM}q??9yAlx|mb{1^ie|_-D*vDQD+~Xq<*4Oc-c7oF{OMN8ugRcNa z3zKYs8F3A6MK?CpkWGC8m-`Lfzz57T_$Q5}Ur!meo_?_6EyFs)+=lahVyDCv$6x}- z?054@nY%&Q{*onk+ovkl^LnhHab*DXLJW&NgSNkXOvW%buLgxGAWh5#_ZWf4%a|uX zSZGgm0!E*6aLd?I&=N$RyT0wyyVmoPy)dI#FoFttni9pNAM6MWSBtx7u-66TEo-?A z3&7&9+Fn0OUc5Sn*%nwE_L|!od3cAHm~zPyLV~rR15m8MmW$6j*shpa&y!4T!v%b2 zXQ}XUxW_lF=iRd~?`X(M;OplXzX_{z&TTm5CnkaBXD}o^1SZe{8)iHZ9&Qmg9t(_j z;otUGaE}eF=le!rZAYNo8f!vCWCBnUJ{+&Z z`rhg$S(+}MVh7Fb^R=1ayJ}_3!Kc!6zEfkV7NgTY`0Sk00etP=yh0IkC!oqP!{AY} z36)tII|zPZd!HdTyI%fV^AEhz)S&b35g7gC%jh>kNVg(ASm4fzEr*{NFWRdQR()b{ z3t=J1Np{L|asCC6r}7Im_4`rTcetRkL;MVYs3vk7U}VLQ)td}lnhY}y0ZNP|ORBaf z2VeHiIoqy?0g51{zWr;g+WGMTqBzq0UqfyF%SlpiZ#g!2a|_hvdOn&S^btz zIB2h9y8>%kKCug6qL@SbF)zPl?E7v}3M>@!KD7ob2Q>U@l9lkgp>@99X`mqkxnW)% zhR*(rjd(w79iYR&oO60^iB*FyFywWZ}SpDN2&GHw{I7L3k zQNZuRNYw|8hfqIserxLs=lhw&C!IeiQMH2opG^jUafiN{z~c|bb$FD&zzU2j2)z4g zg$A3E+rW20^2&KKp9h?*pK3kv^Z8>)V5CqN$3+59v4Gq9y&SWr<@bPGCPrc$nYi~<`KUsj+e^XQ*erOab5hfzPc zf6tMf={pkEU2!8L2iV!Lxxw$1CeKSv0Z+3X?J<$Q225CFnEdxBx~dbla{^duCxEk%2?yQOqx_dt+%&9zFRax4;;Z&8ln0Jm4Urfq`00c~s04cC6p z^`?XBO#B%!5R|*;ISo;H2AObXfs!l~@rCHT3+S<}pAuInplc4sOtvdtfLGRG&i{71 z7&FzfJJ9)=BS{W`n`TnGE=1SggeiVgwR8t;&(+jSP>lh;$Qoog1j1Gu>cw+CaRQo` zVa_xB)&`pw4`90yQPezJ5Lmm3v3?bo zJMAKK&)~sDCR8Ou4!U9G0@|3{Yl`AvXlua^CitSwApbW$Xu>!HY6RVo3jhQ!8ITU} zGGsBjfM)jTb2Yvz^vdjhE^!zs9ux)`p7CIKY20`Oa*SP#2KXt1O6-o`8sPD^=myjO z9ekVu1?>C9T|g&I@H$@tkh-$7fLRX^FMoaRB3Zxu?M&rTC4{OkupM&U^0y62-NlD!`j#-00Qil}ZO{S0eQqxR^Xy^?fz4KM=?GcZj6~&D0EwQEk%$bDkM?1~Yd1$% zzEPs6_p3O1_2#zyP-0~rWBwe%)>jyY_pPkw&s9ey&#X~N<30_WT4u{^S!x=v*l^6H zQG{xrXR+_icPBq0=ZM*u_X}ffdYh7MfcGrvVV7s=_?j$y7umL&Y7-u$Cf-M(l5%0HwbP~^lJ^+X8@$dN z@nR>3NC~aS6==TL0U+JLPEd^snltI>=tygK^MtR53OX`Ftye4myCYP3wKSXShwFl_ zEP?vDdBjs2dXs^m{6~+N$PsW$z^~-Zo+mwl>Ky;27wdVitn#zdmr|FJ?+I6PI+8xd zbmv2rSU%!H#CTp7DC^NaW+E2Hq=>zI*-Nw!fT4A>jvg%{CZrlx!`kV7z4D+&Y0PIz zCWTn8Mho0JB@md=VdHv_Cr@`~uDE|RQ!oknQpH#+|JdVe>BOSNbU{2Mh0jRhTJ!|9 zj_i21?uA@U@!nuQ4wR9H)Yt&6jFml176%5Nd);GUn+)g~O7)3R(ccr&dC zm~QZjd2zuL1Ik`zo5bS6sy5WbX`|$ml8ZQ)tIF}w)W8zhOuw2U-8f$kf4sVhtGi`M zcB^lBGEad3@`b$XhRWiZPrWvV;sXOx52m{tUJC?I{_-PS4Q@o+-nH&3%6gl&cG>{f z!jP;iz+~G-c-N22?kJ7sc^(xVFxRDQ9rM%K_e1VO+$?ch1~SKH2-FqXK0? z8>f7Z?_>29CG!jg|7}G*EhZ^8!lY}Z>~)KR9^v;U7QGc(@+sS#MKK?s&`li)Le#@d zn6OgevzsP0l_t4$43of{?);~$uG5JXWct`}h&K&bUr{`pxiq69DX4z!)rHGU_gp(E zH;X8Z^M#%WMCCM7xp-Vu=F-X+La)kBj@pzKf7gk*&VKZAit=f7kja}$Qv}LSOdMw_ z@4;YyU*BUW5kV)GosylmH?cWfr-K~qD-QL!Sg$`>q%0)1I~OMKgE057^XBFb<>oLK zKN`dSserjAH>UM|jm*c(?oEQd&#Y3-{Joc!OU3dqZDviuCdrYo(H~>Xk|dl$ey*aFsHWV+#nnQ3;M8JU^{ywzi4iFvBO5tgA=G#$MJYQ zd$C(Jcs2PXN2+%3^KV;ToeEp~A9n8uUD4PLJSUT#Vw2_LCgFCxXND6j=-|=1p5v?C zoB8En(#h2zWMg(UKf@c8o`LI<3+Br}`IQGLUVbJ0Q<4M2Y_q}>lX_eRV9ev)8jW%f zT0b!X9-i#6AK9`csicp$30l26j#jC))j;A_oy*K!w(!_h zJ3LP>Fu;lQ$!-jU9gh}wx)!E8a_9=h&fEE#@kFEqJKPf}=2|>|2xXq_VW(QqM0V+*#XrIc`a1 z$qHqYmd}G*>K?id5zbR`6$Ugd!^g+A_|o!nrl>0J3WbXj#_l!b8mvauB9gN98~IQ{xs7piIYqK1{5|-U#_vFN%qF*P3A9Bvt>oUyk~bgkSoY-B6UH!- zLX4kg6hx7=0+>a*%!>BLT6kafCA2egUUFh0eYit9cf73VJ{ z;3Zt5^;)I3E~4{C2xUXFX0FKYw7R{o`Nxa*_msD9WXG}#$*SON%7NQ6r;FzBP*JKa(K#R+o-DX-p*?i_Q_c|IYE0>Sa zqv{4Ge%%b!P^N{A?R~{%t530`?Gx}P6%A;j3%xkd`%4!**GqBxit#sIRC|3pQDMT^ zGg$GuBvNRLReXfx6}dR5R!rEJmR7T!PUyUQ_=RV9#~_QI zEyaF#n_?Rt$YQe%{d6^Qnn%XCKrv=^ajb ztb8mEbvEfZ;s`J690tt@GMszrp*-u>f+gkgc}8RYwVIfo`o7 zy4>HIewI6pBQarqJSh|Db(ncP$-W!XgFsIXz5V8WD}}+L>V2M4%euQ@{-D=bnMpvx zi;edy@7z8h5X8wdh$KUYeAAyf1SaLm42@Q0!fstrhI_7J4{qh#_a?yFB~~9!b5LL( z$H0qQZu9I9N zLG!SAdxA5eos|#v+RX1)4O8gSlte{M;uR89JtUs3T(=}ulmbs6N=ifvIr+0^?Js$6 zkK>2)Y+E>;Dt7cLTZM2IHCaZ{AYcppdtuB%z9S`53&sgKe{QTaQTpV5M8v@SO4o)W z8*|J2Y@xsrWS;AGg=@#>@A6=T>Q3dH^}OI`D{Pb1m_+TG;+mc6N~8*Q`Z5Q2S8`F$ zwBmBL|8ikR6D7*bs9oLniC5us#9r-DVfB}H$k@%2+_@_ly(c6Cs{*I#5RqHh`spyg|#ZrdE|6@%J9{hCJzztV#U5m zh5r5`@CKa0LdnEOyKbJ1G6IoI;-8Gt=buD7bY@a``joTuF4sK1OVA?p%62pcMz2<= z(_(q@&ccGKjTqU%XfuPr-G|lh7Oa;mU^4UjeWDHwDyW>A<*t$mS9f&VsHB4Nd&l*( z6h}i#w-0B|+ZetY_)b?TF^22!9v;wfW}b7UXR_U(#5<8rn%4oZ3N%_@y=iFdY=ai2qvAUnNlDoj^jjOV9Y8zIo}vZmHm6b`pPI41R}H%fGUMiIVP(V3jJ5D$XJ zmh;}ktjq7+Gn`j<#_WY2Ti-b0<@7lsRwv2)hc(K-#7<)M2C(-Uy%gfP%|6qnhB;e; zs=l1%FBWz!^t!&LS*>FVPhzhoK23eEC|{3j%^y2J9JLiNmok(?D70_J)V8vU{6iCZQXi&vQ{W92snl@Uf%7dpj z;tKO@O(7~Ky=sD@Y(s-SABgI4t;YDm()GB2YV0Tp*cgau!k0Fr7${W;TeddRaz2_h zxr!Wx`^GzT{TN1OPfbOHM-14$o;&qrKKjuI??*g#W8MBPp$m(w*QU%YMZRa6(8ifB zczj99P$+2QMmEuwnn*$uxNAP`^(Kq(POYgg(smR%6D-?MLJAVEX3CdOHKwOCGNZ8a}ipQz~5qo5u?10TmI<%tmH-a$t$lp+n`2+#U2DU3&(BZ51ef z-Pobup7Q0OXJ)gxFUBPzQS7^B9G6=2qxS5x{I;QUGo!d~=b$RfhIpmhorBw~sD5I; zo7X2MXeUN-tg0>Hb=cyzPe^q}tH}xOqVA-7{@R0#dZrSC$a&TMs|l8~FR8FXg`CJx{g#U$#Ryqe4}b#xmv3p+@*^(WUaY)hnzv^; z;=_8vfd0cy<;5TLC zQt&_l^`&&#!r!H+QOQb8TCt7b=Mr4PQli#)HY+z_XtP$Y<>XQ!mdm*obGJl=2D~_} zRaLj((X0etKBdcsbIp&bh>!B|eBQajSwNa{PSL;E7}`JNHlLGUbDfjx1X3u5 zn}0p|+Y#&_9BexYj}2>Cn4h74p<;m;GI6;-I2LDSDB}2Tm0~x0<(L9O`61dXfHPv{ zMvWbZkCU-VcNi(UX1QE$%L+Ygv(=jtcZod&giLSTsw&V-{=Ns1X-`5bHi)lwazME- z!dct!fLbG>tLbi(-%k}{bB{*fwkP6+lZmL^;+p4_mHV-}WOkHe4-)eIwO$a}Y<_ zyoojQ^r)Q0xRRLKKplSjzSv-zKgMNY*3zB9Ec?hL}iu-O!8y zSPFlsa9`9<#u205D+W@)sejdWB_C@Zxzt@hmy-G1r^Y7Eei?(p94g2aeKY1+?sEdB zg+|o(y{fM}cotZ-vfI~I%``<_iGT z%Y|et3)9u&>P!S}D)vbso+R(qKtsi>FgMTlsxRiIdnk}LWe1)?WSUI!GYtc%P_df} z+g+GgW#LGK&_2~dCui%gGLte=9dIYs$2nWHxJ@}ail_kb{IEM9%;GcgzGawwid^9Z4JZXnteldmqfP zJQ`f7H+x|OgoP_?Cu)(jQV>;xyf(m3n>%!Cj@6a)dQnDO)2 zto;63_Zlj#*0d!#jp$IvXi~H zuJ{n@Ub^b4*rSxRhi67X*t?4rQ-Nolvab09?*gd}U-B*=JME6IUGpx%8xIlCx!&f> zzAd!S-5)p~+xtqbcwM~IPN3@a`>3Nu4vyWr`wl)1}?D!5jKSq zgx!;{?nOi1$7qf8Wd>tUe2CjFk-1QBr#TX3)j2>Mu9v7)f4;oEvDz*n$Fyq_B`ZWz zx7~)Yo25r}M2@o5q8G!zukvcKJlg6F^3+ukf3WOnxlzwgfeJCIVGmzP5VpCk61xe9 z%moS84Q_jGj^a4ZG36&lnRPF#P+>i5U44i4_PD3o+~OgFBHv`ZwbYn0^ueYaALe^4 z&XysTt=Wjfv6qiY-R z;Z-rP{mk|dVj)PZZoFSGA1ca@Pt=%DPMvX>p+ePo?>FK~AHDG1#L@!4gV=h`D)&~% z+eqrHDSFZ4IR569oUkiM*cxNlv*hoNI;_xT4Bt*djF(m$4a!F{p?ODGSw_o z@ml@ZphEjn1C?go`#bdu2xCMT5h;w6xnZ$$tECM?8cj4?uC@h4sQ}cUK5!DmY@dum zvW?bWwK3~4Z2|9~fkU?3n87uQcY&J?-vYu>bl;u135++f-5zUc{s5+lAId^=x7qDwP z_G$YEVb8lv|`jj9&FYggqy{{}Rd$?v|?p(YWc%+}S9cD%S zs<{?2*hak6rY+GTlAu{CXVal@+gw-0IB(v@RyRe?f1N^7W}=owFfr#8l*!fjRHb>e zKO8bBlOuhDH=)NCfEf-ip^qxkA~I%U>H6TiUHb;}+OnNDrMZSkp{0UlYp>P~F8i&= zvnL>c9rBLO$Mu7$i8+{~sH!RbAUjeZkEKwNZ;wM^rD9%w?aIZPon%Sibj1Dda&JH= z$I1SMX$8m}S7yNnM0AH3DS;8Cr~Tu)K+W(H!Z}TbKkFLnEil?h!fEDIUuR7(rt{&0 z6j|h!LH%%qn;EoBYqM)3w+FalNn=~p>;QQq4Tn^gIgSXGk+!_*^GZygJ1mPDKJ{*t zS%@P#UFu$_pE{Y;t9du8p~Y1YmPCt8&J?y#)2<#7HTUMcf!S&-UZjlkFNaafMZiI322_5Q2AxN)ZTen^!}Wzd(T{ z4b#pqr0%+OKwxv9s_z7rh5+YVIrB20M49T?LPx#wKC{+*pZ!Y{*1&n*T&wjj$0)MQ z*4*`(>*hqg1@<}sJYcP@b2V|;q&~B!$fa0kG-j<1rNVZL_Q)5fq)HA|)P&XF^uQJ+ za+)kmUwJd08)K7^&fiyLufYj{MF2O#h5Cf@V=V$Gu(r;ns8$9jhl|vedPiB{m{yR0 zu!>@yX3*?8PAb$K#C27F_eH zV1jc>D-bp&ny1ZrrDDo^X;7FV zO8nPEKNpP-2Cf7e)SgeLTIS6CV&K%U-+B$ZZGj1HFNt&0KoCdIXaLiDk9b0kFOa?ZZf9^|S7yFs&9sH-F#}>~^$9U(&^KjfXJBGql z*Zg`V+5bQgzPBRFsqUKBqfV@{PQR4s)wBr$;eCr(FS+~zVSN|f z&n#Yj|J1bdLP^PSZP(A=`zf=;mw%adif$!U`>pT4J$G8t>c~r%;5zqXl&Hsd4Tpr7 zbUGPPY8{UI!>4i<>5Gq|I$Fc&`R&$7$xQ5 zsWpYSo%;Oz)@8S!p}R#U?~61djNS-79ZHl-ZiRtOPpa%&p>^(hF}F+&)W)iE$TTIt z$V<<^0YsHTEyfwMV-XzCBqI$U4b7To&AdpVf{ash)0Vmt>ZVNf5!oBNZK9|Y0#Ooy*_p`WUV2DhUKGt z?)RQt<+VbXACa7Nd<|GZO z#vVRLk4nzpAN6P(5j;E5Y?(eNe_e9la?a4ojNddY@IC}KK0T*zloSzTls4z*62E*3RlWf^Q`FQv$Guu1a)_6PP`sGxTgg!}d@enBd6uERq(lK8fWhi+&m6;xgj zo$mN!RD46w7FSa%jNAj>X(#uEv?Zg$g^v-boBhG?Ip3!H_Q>9RwqEr1`4t@iJ5NPB z({Fd1tyS^^w-PT(mP>6Ub{czz_-M<5?^FRN@W+PV627tOp$c^?d$wwUArYblHNwoo zyf$El?S0oqcr?lx1x#mMWQ>FP!5wf1N~$ zn*qM`(83Y$>4U5BwsAT*=%cjohh;}(VEDSCAUpe>Rw}INefr6F2SUnJ_+>Zd+d&0Q zGuof|LIr=F@H!T>aVaEH5x@30gy#9r)hGpeR{hL)CS<%pLwu9JQ*`6YkVOwdZ7t*~ zYmUu;*WBFv7~9r@eim2pUT@e>Cb(3VM&qK*+w8uFi^( zdV6#HDY8h9=NI0tr2@4K6hbp^B63O0`86pwMmn{(LvlP<5gCtDpSLWS6qG~8n9B@Y zG@F=ft?{6Qzoy28TZCd8m2L3W38qJI`^uIy)<37&eaG7PIUVcU+3Hc8WW5rqHtSZ+Wr!-nS! zWd~rI%S}{J6;6m-0m-E!A|MaAe?gh@Ef)S@V5NfzdIva-ezW=dviroya1=54sDt8$ z?Ji?rRy}TXW-N;V()AStUrPRthrm|I3(g|-l==ixRPj$yZwfJilYTz_)O@A0lysgC zOSJHrNF-L7Kl?7Hwr&`}?=`Xxw#M9A=P8X`Uti4kLL-tUGvBh5Yqkq0 z*Dc02y1l7cK5GgV9C7yz96~4>f7?=uw1M(7n>ejPmn{c4J;MAXIvshQnUxXTaJ% zH!xgbwP?w+FzRoi1#b9sRllBxao8G5r?fn761!vCm%WX9Uy5_*!#9Zn&>Hf((XRuj zug58nDgcBM8S5Lx3>xbL@A0~#9{=>UmbtSr=`5hXd)7G!Eu@n$f56JD0MlMD;cTnv zd``2&*0JffQMNGU{UB4sO)|ZBR_)zstq4ZX2ETsj?}85RShC*y{tIOEK4n$7(DK0; zprSiexz5#SLVHfiURB?aWMz$g-DiMmIIECio`mc;=udWZWo+KV;iXzPd|GmGqEC~^ zVVTvu`P<3qBF)C;zRIPs))G1AgehG&0a2A7dd&FN-3Mqtbkkn1m}j z=Z@`wmRe5=ij5BOaDR7d3g=Q@s|4H|Czcs|S$*?-;@x;RwQ7+*6DiLnE3^XolfWUqF6%V3*xWkj-n zk3UEB-3LP7L{+o}v!*d@?rw?@R!i}Hw4m8S>W+qkd`44iWnRscIH@YM+obMz-8-Ir z%zfqiXy3L1YOWuH88-L;Q^a$BZKu_@L_p8yPRmvb^D>_UE}A;@2W3=4`tO@QbdME$ zJ5#>nC#FSQZx@SNc!b8NZ#OJo5j~5skVSlUTpk?AJ_A;b>#e+S1qha_#~w$$T}?%& z&glLqQyIR**u3hClfmS~e;B6yc_*A>H|^{bx^{GwjZ!^(?sn`(Ps^(XF_lFSops0% z1hK5DdH+>zBf6=(T!`dUHzHf74V_5T=w5;V0ssk~DH)g#-O++>G@?KLeiF-0?yf>}4=VnT4!;&BXuoA6=%qb;s|d zOVCo;h2&(ItR=G7DK)pq;q2@bDW~?C;QFD`24rq1CLdIjOq1?T$jdVEZT}EiFqau{2059fFj|!qVMKmvni_B>nRsHWz+mEH)j4vjRyWxKC1EdMKPVU*Vjnci z#kG9VTMDQ+j})YLM;wu=n7G?RvldMd62IUMj5>xFH} ze?BDi{&EC4{4BOCQmHXg7h&*csN$uUz<}40HatJut!J`}e_pqthh*P4q2))a4Qnmo z!M6=A&eR02ql<_QtyGQG0kwjp^5fz=7Be|$-tCP>NO6_vFUzhtv9RmgVv^6qy^d~} zr=$$aa)s8fa*c?{!^c-wcIyWq#k53?{OjwawoT_D) z)HXfD^J@Kw6!yktPO4%CODx2>^&{K7?f4f6np_<{ zwa{>VH|sbLHsxOa4!!Lvv9M!A;Vyh@GoOCyE)wf1eS)YnbxiLe6~+B9(y&S|=K(`# z7p+DLouygivm_$|a7PLUs6?`&!xWvW$~q7_38eVH>?=2fDn}=4DjwTC1TsKes;Z^* zZ9**DUCOkJm846`%x0V{f`JXngh9`9AHGP(!I2LeWFsY)UgsM((S3x?9S0#^96mp5 z-(k=0=uojM`R;8#+q3VF!_=|0&D8?Veeun zZw^El`REoa!QWm9(a5}+Se3Ziud%%>d8B}4E?MFr0X9dPEw~)>X%Uvi0m3xap0l<~ z&?d*o1(aN3h-)EFbiHCT_F$Op6QBD?*pXOfi9(X;VRH=2^3i^+Le+|HcCV>hZ~W*C zD(Nj|y&S5QSPo@jI!B3&B@Wj;N<9ZCu=b#@S~;BA;J9-On1RzrvE}d#_iyJe<6vkx0qDCo7{?3> zXzgFL;mR6B;`4D-Mz~dx3=G(7%$2}YGo&7TI0i|TODJW-G*DtDD`d^M+ugYuf2mm(CvwLDSo09);>5duzAsLH)Gosx=xc>*iD@NIDO_w=RmevTdKNDmaC@+OYw4u(MvvCOM-vA+ z?(Ngz#b|2w2xZK=(r?2jtHl)#Tx+f~iEd0$;uX6beYlLjr$~R4$7k}Z|6^V2MDdkVNWBgx63**b`$LW^3*y%qhSr>ab2snkW<2FiMumfz! zU}1Tf@rk-Rl=_#I71A_g{WMC$K4qK6r1IT1)Sub268ugyrqh zO0SQ;vx0_b8p#dw5A_{EN7aevk!G|PGAEz@_VflnyX-Kn1ph&BbI@J;Pa+!fM+Y2H zkuhXexs*-Ox7Fzm)8!m@o>bO3Rwm4(zCa{CnRcc79cl4J;0DTnmlhCkORC6sw2AJ%(0j+8k0V!%D#BnC?2; zZ3N2rZhe1+l_UvWt|4MtMG-FWAmZ+qp&CJedjo$L;6MthzS``2?4&BuhDumtG4nnG zck;Cb-4c$}3oFgylh*iYnz7)G(UdyxZN#bRU7hCu_H;2{eslSDRH{pg(oA?Oc6a8eI)8M9Ze8wErZ5wAW)aVu+XmH*R7ICe9VUsV8BT>Jcbu^sSi`nvg)#I za2{fa`t#ra>F;Bgi0aeVNK@O)2MtJjl>1%)ePi-EYRA-Ub0Bg$?M{sODn@3|QjCdRagWHPE9YApUY!6+^lG)@)|MBD{Q9942uhDTiN@zMG2;5qJqb9{`5NAERva zH8XW%tQO%F55%U96)GDV`goL&e+TTPbZt-N2I=6cgpziPo13C;ULE%U2wjsrYB10N z;@+?IRtxD;3j3)=gbc`Ooq6Y7`D^ms;#&DiA-T#$aGEy%a(tKNu)Xl4<&;&W;F1n$ z@>K6ZZJ3PaH!{Oh%>j)w?hepM4I$>EsS(Z}UUKKQz&55K%!X9-kJjYe3l#EIY=z?w#jH^LBFIy_ZUBYB*TSJO20(h}?ww18G-S=3^XLll(E z*$F_q!idLfF!t%=6;d1bJ18C#8sq>V>w3y2ey;J%QX3une{tQ$(seBRlR`WE<+ml;BclT$z&Y>%o9UUaN8>86 z_FFQh>oGNiNuLZnhhY^2{lSKOfuOSD@ub+j0wNzJj3$=|&2T^7F+AVEQ?G-6RKz(C zSW`|7vY1hyY_S~rSV^1-B$#uZ`w&!K8h=jhN_(ME{lgkC>_dfkF%*hdtJ75dL%?R^ zfCXQnyyo;hneQuX4!JnklTJ z>*gtj!c)3;sE}34p$22w23S@`s%J$am~ziBsOC4(Mn?oYMmxmh4WErEfB&SAI=Qsm z(j3pl#?@Cq-l-8g_3a0>-V$X(*oJj2W%cGE^D&3;p^Rn2H%7t!ML18McHM&2|(5@+ri> z*)e1}X2tzP6gc&0cn;1}({YnH8ScUp&^kWImsF`VAz;k5BOI+Cke2@=e2cmo`usS* z8A8`h31scu00ROqOMDQbpKWQ?C$ZIO7NAkCL{JGsPylXa>?tPjUC!UY?}b4;0TqB* z=ksA926y<21F$9KDh6{8YCAZiS{w3})wU@mKk>OGo4H8uQh`#>v;oxNzB$6rg4Oau z-TgGnn;w3CdA(B51<$W?>z=&ttohBhZLf$n$iUMNzGGjR?O0iN_@K1of=Sdhn*Q4J zp~l$o8D3aFUwYz`x#Xxgd+#BJtAQ690J|Uui(?tQj)q6_r;{*yu)8#@_k89)o4B{} z@32-*Ox8R_0fO2apzlB5UIGMuA76V=wt7^L-2Q5CJfLRWu*H+qK!q@ZTYBkArWH>z zHF8|Zdp`8xIfbojc?&F&-9}jEWQ)=S_GS#3&xoqd7i?wb-(v(0e>L*XUdyTf+od(Y zu8XT&JmJgKkjMIWT}?h75+H<9>r8igJnvKzN-zROxvTlsVxXo-K zW?x5=S*1|k5y5T7zBAXoeUwsOnodB3Fu2IlC!;9Hq-6QTCqY*iE4ceHn1hHe^!a$D zJ-fVmbI^cUvaVx4_BC)ELzUme<4K-cz~Du8I=~#V1h?8tB)q7#UW>0-jmEZshH9kj z{j?g_EhswfKSoS9eBPvLj6J7$Mth+(-c`g33{)($(o}_ilbyH3#hU}kSg2Nsxk^GT zn%+HABDm#A){KqYn2Amq^h|wXIm0(OSug>qD-*Ei9QdQ z41`#W#iqc>*b`cNVfw#QLgao`ZaA4%)@Zlr^8;$Py-d7=+PMNa4x&x$xg!%qJh|?P zL5AOiEQ##?bdxS&dHvca^L78VtMs)cWUNkVxKOmAr|_-mizlT+`6EZ~rfAxG@Wy}u ze#t=YFSQ-p8JA81p&Su|7xjRMxzRSeed3$b+jNs2kBJQq&E$$7e)465!n%KB-F)n; z#+H_MwiW{LhdQ1Jh*XA|rt2PQw`lg%>TB#3C!R3H7#`x}; zKEN}Q9MdGZ4qCzzC;Y#LpAFTlqX*8GGS%6oZ#-7iq@XFitcsW~AeHeJ%#?bB;qu~z z8VJ+qkgJ#917b~`hYm`|o{fWR_W+tky3H}{_?x(c`HR0o7p-(bmU#JgjO~EcxOqJz z;aR2A9bC)aH;GXFJ4d@3Ctu0|IM$K~*ezd2AkgmtU_(8GyUx@iF;$0x8Ps`1 z$q2-9>~{dn;pONfIKc%W5GIik^$OS9d<)OO zoai7eCG}>M+1Qu`_*=qJQM{JN4X-2IQ8r-X8c=*gN9#D|I~QNZ)y1(?+>Sfcz(n@T zC%zq9z-k~g6Nh?luq`^)gd;pG|1f@}y=V;~|3e2;Ag+R_5sc>Bc?ST?zu~dtO$?Zfz;D_Po+ZF@egCG@`?pE0pdoQjnwd-ojAr8jByuPdW;5e0cE_5#%1 zfvy%akFE`NrcUHj4v0F(=xkB-lVaL!E|ycycYO9Oh}dLgsz9+3l%R z4iK$)Ugmqj%GuD`5NaSEQ9huTGZs@k7R7QfR6_-*HR??0NaPK+h-=;LFfYxleqV+q z=(qlnXa*J(1LIel|COVCWeQ9a{O|HCW@XoL2;N86ERCv~E+xoDdd4xvTNJKt`-Tzu+D)$$$&HjrLZ&+>zb|QB^z} zEPQ_zzA>Pzm3Oc%`P3G%nRaO>5Zd3eddUbJ+cWlxRiI|w zQ274LxDT)D?SmsP9w}>?W)Rs}O~;0jX=Y`}K$@K;po}Wi>6Dvqq99T@KlW-Rbu?Ef zI3sesAh3Rsm;DX`19BsU-G*;|{<#N`gC+TyAp3;+ISU<#Dgjok1a zOZmn-yG%}+G0$Tc*Q(vZ&OVRFdA_tq7uIcH)cfn^0jv3y9{em#Ljst#BzhfD49P&! zsH^L*mxQ?FIT2^?0DBK$;S(vYJp`Dxz?U(6w4Rv50+xJ+h{2Gt#uOOc!ggHFagH+* z&q6~^^IMkvmugEu)kALb*<3HftUbQ^y9Y&hNc1xJjyS6+;If`%|6FSX_N818Nh5M# z$xHoalRPSwSq4bCwWtM$=9Cj|pnf`EaVd6GW(n92I3#=-OltT}#8u*Cbs}Y${V3t= zE`YvZb4}C1RsG)Td@4_0b=91)lQQ4Q{oB`V^B2dsRrSg6YRF*kK9<7cf1%@k@F3Zw zDQ2Kg;_)l9F#(I1Mv;dAbsr|@XXlY6L9mGyVA~rg$igb!J(MRzT@qF12De|w z6Zg53s{tfrNM6TB1v?gxe%pTJcI&ZJK5TBGIEo0+IyZpJR-_(gU6ztlph<49scO6) zRV*si9LqmgCj@>0e!k9!h@$*_SX+4-kn-|B<$?SLQo+2C+ePg$aUH5O<|F@lg$^tNPhr;zRk;1(>$6-BJD^-Ua|AQVeUTFH=o>e^2tY}J8v0FQP zS`u47W2^sW@Cg;%Mx9=ydJ5Z}WZ!(kckxf>o(yC1lkx2*=npv}R906gex}WcUSDG) zl}`>Skj+UlW{~IEoyY%+&+9})ZyeA4T_?^Jzj^W0&g_aSG&9ox`7B?U(7z0(eCbws zi1#J|Ag0nwH!zULgz*{$!j9)UcZJqGu-B;%!_#~Yt3Ms)^S=!Kbq95E3tba=olWWH z^_@r8`W0Ov1p{=WbF_z#HxORaxf`%ArlNqAlUA8P)m>c4dUx0Qc>|6e8G=`SDs zpXUCS`KS6{mbz~J|8(!)$Nzr(KfU{ZAOFiu{~hYzj8FfJI#~1iGOqvI;`IRjY5WF? z`=;tqK6TzQlQj8bPB+oi3P-yajMqs#{NI&9WTz&a7`dymgx!>wx~xr753z#H=6SIhqCX$* zs%q$j%?0FZ#(h!`GsV{{NB{~e7U341SG?ObPZ!W2#FH4n9L}3-SKZTf{F}K7;JJr4 z^8%XGM%gX08*8NrwVd+^dpu0_jtbpg$xBwI?{XKH9XF+gR#t_68(W3e!zSHn&7%*t zn-j6RMO6Ztc?P4E7UG)+O=Bu`IUd!eo?9e^MFJbIj&h)u+ir}@?o94p4uYl|2DEEu z)zed*n?LKT3mU8SdvYkCsp3XOg2NZw;PEvSM-k1&-0bN@OAIC4 zTN|NrWJTxl%6i*96p)euLq% z@w>BDB?OA8uQfU+*qaLi#+<@9yg%k7VGVhY^riy6^TZER{Kv4pZ1X^(okR3X2ZZMV zXSDOo?x>3LL{Qu_h8)JSsX=|=?rNKpNp=K~9_>A+=33?Kp?K3)SU^4kxcbJjJg4LI zFf2ANvBzWgmb-}`*86zi&Q9<;VyXF914NvKh>~NIifW9C$SMR)KFKK4Vm0oS)Ojig z?xMDiP(KEv9&%<>K+c+)2Crru2Kz*vSNc46J?GDMnh!2U-FK2EcV@Jk99G0kB7qh>izBBzWf)Lp^4>USX%ZPUgZXJLS*K*!idBCKE1-$~nb9OY z`yEXar=Yx9H*3Qq(?Q@lX8bHo!V6#Fu#s0_FYr<{nZ*FlJY`CPR27en%?}(*)4wd* zQx&KD+Jj$GP0=aMmY!chZ%v7h`*j&BN=UWa6x&&})9#K#ZTc8RgoW`Bhp5nxh`&C` z6VCF62Ds&ssAk>J=}*(MdH%yzC4*kz^BK ztOBun*GD+*AljL`CLo`V6ljA+P8)?LvU5rk=!|M@Y4z&siyvA zy0m66dbHlCV~*&nLX}$I?W?d0K}DgryYO-aI`^%nxR=w4C(^J$EK``q8O#o@=9qY? zsP)W~fC9aHlwjNO*ilvR9mX!;fKXSDG(=j5Htjf~!$Q))HlXNO+-ooxIWTfX)Y{FO zS!H^=l2k_CREmBkM+xGdsGyh9!G@ypI%~zWuRPQBBl$sXY^n#rVVpPf=Y-Max#6~ZO<5&y$q9dTNyvE7L|`Yq-%bn3gTNWfK{{V5=E1 znZ~SZ&ckj8?%RW%c3!-vK<7TZ2d&MY2Ia&wIrr_kWjATOcy0owo=g$<&~eD>J`TRW z9!XhH6AP9DDR!S=wX0?6H)yc2E4U8x>z?YBDtV@TdZ5s;B^yTu{fp&v8DC44M;C_l zr5B{)c|V24V{4mAS=Q6*s&23tCY`;9#qK_p;9e=6Vh$*;igtG+Al7bhd}mgrJmB`DJ~^hi7cWXnh|xk+?oxJo9@RPl%vBNkyy81G8IW z$I&l1-)36-jo-<9#>0sME;dhm*}YDMx1McAnPOYV_FG0gpKQS%4860zCwMR|Az3r9y zYXq~~r}V>qhL%3JAlj+skjyX2e4SABhBp?v>o)lI2UhugMk8(;lNv-;fc-r4FxCC0C!a+_!Oxu?oKutlv^=9jY<6$%rTuk1{N z{T#)6v(YFq^2KPeRiVk-3Tn3HrZOR;Y(56cx?WBEdHayb~b8(1GXz4bo`XXmju*v zPzal$@5~q7>B_e(-;&AC5>5Z+m#`B-p_!ho>*If!B=W3xD$H>y>p~zy)qXz2Ow1m8~wV#%e%#^<>*5B|ezx{z> zBSFKl>SKfEZJ%DxMo!uAWgsJNaiUrknT?g5xsr44$W6r^1d-VAbkFs|nEgldO2s&K zh^;Bm3`HXi);ui!9Cr1>pUGsuAe}0d)TWK+GLg`+MbK)z2BbFgZ8B#yNxk85yDfO* z9Y8C_MLNb@1}#Xec8=?S*IlRH*!X7=_5fEG6pd_|@}$Z@OWm#O$yQ zLOkqYa_{$;iJ?r6`{uv+$aPfv8Wp@>gP86@A*OMC1x*7#=#pHP4>(62?0cND6pCgl z;H`5woa@XF_(ARb4{vYs86+xHBAG6CB+pD_Aw{#$bd+$3r!uG#$hpUS;MCQ$HiOco z$gzJurJLc_^@xWBMWP71sxdUBKyxk5+3@3PFKRKXu`q$BMCM9Do52}dif@*l(iw73 z3T_`yFr&GD`+23vedKk1L7y_v1CgPpw2)|@F=Fi=H8RulV29-&-mVF)Pj3u)ZQYB$ zs$vUg6P+?Pq)yGWuG37DNqaqMUX}G0ax|OK+^>jRepKC_vct=XB59>JPm7{yre;Rf zTkTr4utrkB*vcgy_eCI^NaHf%rtd5W0sg>uFjmdC0E6MN=B_TtX!l!P&&N_2AK%y~c;wKeW42T}LeRY3AzoNy|+Z-b-=<%6XBMAg_(HN%hA9toG~ zQW`+)(x%gN4F#V;T^6H^v;)cWNL2@ybh!#fc3D_a^q@+i$Yo|A)Xoiu655FY2cSV3T!b#g}#;5RG)B zkkaFpA0X!3B=@P%+)eNVrm6#E&N-boemlSz(A;C``M7dlu!Z(lTN2t~v?di!?N}6f zi2q;)(kt`I>ON=KI8k2X#mW}Jam4Bcp-I_O6zF1f315wgR10F7aN9);o5?7$0IDvA zX<-wez9nxYQ&PSP10(@gtjVeV?L+vy(2eb3O05rZO}VyCqEEl!u0N~P$37s+9G1~T z+P20H%kbxZ=Ctwuvj=It$=^vaLo!BwB_)XurY@Q=_{?HQOub;wG?b{&9dW8RLoNr> zvrPWs>i(ve^!_>(Gw*k(+j~GqyP7RH$^KUAdbFC@`sX`UqpWrevxUn>Dz0(7gu573&y(4P^0th7$PsF zubG|HLEkpKrRkmU3ln06;Jk$V2vra~MQhiTlamIh8B_yr!SI;9^0e{=yUFN+#08Am zz8&0FSK zk>;OEY`Kka0@M_e1uy$+%q*)cAmW;kK_DmC+dglFDR0Ixf#AmSxvy$uq!V*{=V09jmNotU=EZmG{?-;(mGNW1UF<< z4t0N3hM#xDeg9qnkVew=-55WhL0CS=lxnb|P~Y*^BY#`mTq-mX9r?AX42Ro4W3+*! zBSmhi`PJW~v~D73Ic>Z7;YR!mEdW z0FCyxi_3nELjaE}3F*Wa9~%knY*nbQoelqnKPrI8cA60B z;C>Z!5HW){;MT~nx-Ml8-k{xOEXs;a3}Lz7zK!Ze12e6<-P_gp>!NnHl_h{>Yv)P$ z!|1u(4Y1AhpIUS)SCi7w3(N-NveIj8(>A7 zy5w4rBCb3c<;eBKa3nqTYUtipA%5O;zHLC|A*1h{HQbYRJjUR`K_*=SPj>e+VO?0e!>5)NVO%Agx$mcjn8JYs38* zwX9&>*YvG3FvdK%R^`Xay6+j!;N6xJl6VPiO{Q8hx%ZXYC2l@thxdbw{SF^GL{E`5 z{K%Kba2%DI73H;?(kyPt1U#{UC_nxsuk^8k)d`GYkyp!KXU<;G3 z$f5KpSw*^CnqRT9jO>R+l@bLBS9J{U;ixNQxWAX{}wVB!j1wF1@+B3nz`KakNV{eI-+gHx5D5m zaYh}TCcbwKps2YQM=yRn`3$;f6A1X(Mq9)|MtzlxvLKCRqht~EOEvx+(-n=& zIw=Hs{J=ILy;lZjH?tH|MW`?d2mFWM$gK1CJ0E)ALS%1}qxWsLe;Tyx?5ZejFVFuh zjhn7FBOJB2B_x%r4;!uz(50JG*l$p!##eM=5_!Wlkc$eP3c@RX0gvs*T_0v9f4vt| zy+E~Nmx_m_&#F&uzE~;BX`i&oQWOIrJeDG2G)wCvG3nr^D5lvI8qEc{UV1wTi35S! zmue1h?bYpm>h>9tj_KFbz6xbHz3qARa411ZtJLrXi2c z@ykv&r^C-6cq;gOd7FlY#a(}j?l~&lcFR-3GjVchv9v zw{(c-UU?1e>Q9iCK8NWoWp6$m?njM7gjayP#MD=}3xH<*wT|Zhxai8NixM~*tb%GJ ziFO=Z-$0IR<1Xt`gf|51WM^+Af5Eq7=tzXFho?A2f87o{Rl)FuPSDY?e15{*_Iw|o z^M@A9Kis#Ei{H22z%y3`Z#b6d@aAPv2;y8lpKi52Dbnx0Z>~K;?^3>PV!gpb z*3a>UN<>nqkMoSV7`an1%UN*t(+oxm_y<-Xjo!le7MetQ2iuYC1keSC!@32Jq>KT0 zTR6rEN7^Q7s7h$S(R9~DTqGkde(BpUKXt7Xls{>swdGcpv00AE4BVoh{wq6b8C`I60Pc*xk{W5+=1JGl>mIw<+6W#0Z}eQp#gw=*0^!Q$d>J zjiFcRL|aVfMGU|M@vWb_VRf3rOIRQqcnb%>_D>6rra~ zD7VHPZd<|NZ`4;`8**s*tv2F=kol!b)wzq!HP1PQq!3p*wnO7?bO0we713`;&U3EC z>k0w)U8{0BjMrCg=%e@Oup%Y*Wt@2rMcqe(+%??B1<&6S9ElM_-a+7W%S9=krgSM^ z`H0l3>B)~2-az1x(QzLiny7IyBC6)v)ptQWg8O1mZi9+GMYweERS;KGXP1;m&5)zn zt?O3VuK3MC@Ts*~3ZBl(7N!I`n_&2P44{P8ky%1;I~g?yFB^(8e5jrMMfJwY2R-Q4 zbi&ZY!6}n4$l996^5VBoO<&Tl6Y1x8F=P5oX?V0N!j=BG>jB?YlWH%BcW;3Kmr4q9 z6d}LBC-%vCFTSoz`TNJ2Kz2g|(I8EWkozEnTa=$vuX1H0DY3~%lxU#6>{tSSyPeR4 zF!gM8>+CN}+K0)`MFcCFq%4MMb)^@y^M(y|R?Y-Ot2_sd54jfA`hEyy39mTORraHU zC;dl5cyH`3vR9%?<cAFu2jY}3E^_(ZBO)5%sBZ@Ug899EakyM z&|{vBRZ#Fuqp)#PG=_Le*SyFT6>6l=a>HQh(^^BA+)uWb-tfMDb@%Z%1g}}n&eFt+ zvvw@SkKpmfaW z_==7;Eoxt)k;R&?kpW<)6hV1g#H1y6!JTRbO~SXmfH*5@2! zWTH6xPIWfmH9STAF{7C!Yo648UaTF0TuI!HU-jc-uoh*>t@cdODW?P>c=dbyDgwLt z6fF4V<21Po3iW0Te!)m6(0JJ*F5%n$dUAFY30PiR^_4=Q19!1vWN}5+(d4{Y=b&Aa zus>fh(ru%x@lf$SVQ|fC(<#HLUfNF2lUjaRymcR&d0Sr-7mBd=zN@c#D}KHvtUgPC zc-N#F-hssh`fbk8(AFd8R+_-p6?H^cLxkVsl$g&n*_?{T0x|kPVde0t)}eJ|spIHUlN3MTU7y{kY&v3gn+zy<&0hZC%vX}J zr`|T3$X!iMuvax{Jd`jU33~x-dkb_i4%#oNqzfARVmOkXv{~AG}dSPbl1Q~v} zS)`5|QXO)}1%(Z9UsA0Z66(5OE+vfd*wT?JT`~8CFZUX{#S(GG_o*5q4;?R#j}}ZBtbl+n5eh>QYaJTMq$A2%7_???b zNP_<3lNIG48dxL)>Tf`9l#xUSdxo}Wx5nP_S66lw+G#zpkevG>867b9(0a!fD|-gg z24S{MYz`~HaIZ6mr3%qHLFNZ`L~~(|gwf&1jV1OfyFZdOPkeUeK}>*SHT}r+J$doI z)Yf<_LFDX;U-esHS$wj|pZY^_T}FFbQ)a-l_60p(I#PNAnZdcyBwzfcCSl(*Qakpt z=_CA)_f*A{ygsD2E}gM-wqa+tY5iz-0^X)#!#Tyx9d@$&+tL3aGb%hQDs1w>01f}# z*Rvx0T;$67?OMMt<=(s_4rW1{!wjD>DZ`vg@(+7hC!21EE!pUjkIjg)qOcBna20H=6n&ruC#>4yK`@clgAo%%6=}W6n$G(zcX=fwS zCZ_@`aBD4oJkkv;sksD)6VvCKCw|Y@{N3PXotMz7w$a23tv)2vhv-9&Xl^Ij++9f* zrOu4PI)A1qZ6?aOm;I6cfvr|ff|?2$=Wy#CW<|hB9j+@(9U_BAeHZv%`Oa8Qpu=0{ zg+Cv~1H7E)Rmv$kys1c-;e+d4g?<|lkPbtjDdn0gi9wa&0hg|h$qug|oA2s?n>W*= zi%iz-h4yXyS`}P^cac312!>Ltt?+9#PStD`PFgONIKeFs-vB_n@_77mWlfa$aeO$X zk7rIV{4}^W8FxGfsS3gM=ljBGCnW9mOo`UXP+`vf53_&8Cwy}z0tfb{2E-2u$S-ka ze`$z@?Cpl#O|O%@J6hEo^ziu{>JJF4ZqGKQw_+JMzy6{Ad#ZDtZ%(5j^LMW9+>;25 zpmGfHW|OO@lg&C{QR?2KQG=A*u1ccwGnL$b4j%lZR=aL`*ETG32zPdvNcsI^w{*Cb z!B^M4!u^Uxz+LyHGzm)(Vi;ebC+N7n_PN}8SFPu@<+SYXS7p;tAI2dyNoaGA;Hi>$ z#aDR5>;9$FY>;zAPTJRaiFBFcohYX0mD!-C8fdpME-%NF>^j34iw8dkS*)eyj9AdgG>UTqh7F75X8n&V8xV&-CNX+Z2tNT%-YsK@=e=DES0#FiUw2Di*f7Mb-CnA}J zaq!HY@Xe9iHQa*|x|zsFcx($2GQcAHO1Fk+TieubpZo4C@V1^@#~JWG#r9{3i;&m2 z{Pq4>`gLKpQE>1Fq28-FTI(61vT#>bS5}m(=lJu$1hW7q3uTKKXN}Rq%>^L?hlkf1 z5chf%p#GxhccESNs|+&EOhk%9R2MVV58lkFIo$Z~7+HbMEJ)Y%&3r*W&&s^Os-4XDY8+teNaEu(AcWI?596 z;QA{=FkZy76!ObIow+QJ+h5AWvid%Qc?MrI_us+sx%(g5MCQfbo-ZQZX>mRsq}xO0 z6jF5sANYR#x*%}W#Rt?v_XusQjK)J(a`RQa2$Eoi2@nEGvHZAp{?&0@n zv%uHa+kOARvH4iDHc55r<|m{fCE8HVUJ(MnYWw`+Zf|tqn>7UxrwceZ^ToVi7n!P9 zh@j$aAM%*!%|z&JJfUU#!$eW# zA*1l4*{4P8Ed)OespMR~+215I6qU3bv!!{9l5?6X&^xUD+mniq{36$|{)01gpI6Ba z^FhKDoeit?^X7sf0YRP31>DW|c;-GNs*XJIM0PG|u>F%YI2}dWyW$IaUPFl;RhbTn z!Bx*SxoZYf0cqqM*d#33o4K~Xy`8g&*Vy;f!-Nd8e0Zx|qVhlO@b^*`Tot~G|7IJK zQrocH2*wItW+2Jmf#~wDtcWBor8Od{;tHBO2^ibC7N1l~OC4%7g+n(RVSbH50oRCRj~oo8o5W(PWbP(~TE4h%v`3u+CgMZT4qcC|UjuaU z>grFQs(pGl-yj^M7eF+`U~Pw#QD#HkRfb58d9P^8De=c2*K(lnhx^?&tGbO@uF>MJ z3sJ$9_x!9249o6nP53#=;-a|Y%@!W%xzEldDC-)y;;Fm!koR0j`8|w-Yj5ngu~=0I zehO0h^JaF3_UG(cta}B4mMVv1KB9kB?vn+vwyK_-EQto ztTPr09bDKR;(ti9Zj#-+OnuFM+u-1y+?Wk=#}&J!&6tQ+%xe6tuA~~K_0%m1nFQJS zK~uTNb(~XDv@V&dIv7r>{QE>N>q?X0Yih-89-J(IbIsrJ{!Xj5EPoM$y#_vEz< zg2+|jkELidmGM(%6wki#RSdLmf2?0^rS|Zt{*4>sDiRqVyYYY*SciL$%O;nWSO@=l z++0I3{}UEfQM_QL$hdi4%$Kzie`kO%HB)Bs(Ls0bAPns+*6PusXcn6D?Ys5s>RVh( zZ#L0fJR@2|MYCK#o?7usv8X`~B9YG)>eZ85G6~y`KRnp^*3A&g*L)&7_DAo%oMMz% zs9kTjqg@JQVciy+lI%ddyiH-3Nl+>MB4dPm}U!n&haAG z`0WEvs(vf+?XXi}<)ONoVAfm)fTTY%5W8=y4cmKKdsClD6oC@~p*RX1Vsx!c@l#?N zh8va!VP9OFTX68a>(S%CQ+7{nh> zGE2UAmf^c7Fl3HQ1P2>^o8k4~s&bX+|Ki7>?SE)?GMxQ7LfMuTRm+Mp{gBd~6<^h< zQk5ZPgU4o(m`|}PG`aETP$mtZO$L&ecFvKe7xNOBkf!0AY=A7B0r+r_@F z+e)Y?uWK%WRs=6 zx3{XvU`H8jUgc!qD1piN-jsys+*O4fgdn9)+HuV&%x%9w*B0mW3p;LVKSW>jUsjH7 zF}8TxeT!dwH27_CXLNvb`k8LYaUR-j_PJf47md5Mqd#9AmzTfJ$KWZ7&7}b2#IQCo z&L9Du$w%65O^;Mb`RS~;+8E8gSjg#;W^WntqB(Ta9_&A>E3^t_xEEan>~(6Bs`9zjz98ST29O|onW@yws&nfsYG zYzJWwUk*}AVtKhxJ9`Ru`4_AsT^XzPZNtUmR@2^{~unIHpB&3=Xqv?eBn3`2J1fQ%GM_potpQ0KSvtendps6?EeS5kR z1ab?gLmX}!Pd0x(2Qb!e`uQnWh}wyr`>cutfn!EQ2r@=3r{KPlawg4fD<`6c!w*_@ z2L(G-KSLS{w@sP44#dR+9uN?ty>`w#M)p`@^4R9Oeh9d=HSt|bP(t5ROLN9Zs>lOy zaM?=IfooTSC|pxe+mEl8bA#<#n&-9Ui3HDvScJJyJ@I4feI+1EFPCrnb7bgPwSAgs zUd0zuv)N?bVPNdH?mJtwXTn8%`=hU3f%+4W(nN7#N%J(z!{dY&o6)YIekhG@uq@QuNi~A&|`eUmalB-2$&l7k`V9|UxKO8w`TY-+EMsm zx9n~)(lVHd0&mGG2Y4zPoDg=sGP9pGJhiL)P<6Fq%YO(Srr8@(d4JuS?bN@g2BUbO zHkVqZ1;N}s12_0Yy_D~Mi(v(Ffh{;50>Sn9bh`Z8wQpE5*2%`5BSV;z>eOVs_T2*o17(W<6>DN#T|Dbl^*BB0*C@g*i?gi#q1TwpJ%FyV|IeQ!OCYkb~Wd+cas|tN6sPRVogisFxJaPY; z@SeG(;Yr2GR*Nb$M6a)&e__W?hWFDr5R~Xczm(rRq+m};zB{Czb)rb6qYvfK#{ci3`^7`s>=82HVol1VV=BrT-1zQ%!*i8E!uL#-WU0Na z$b&V}u$ww`+TB-15Y3C{mmUI_o35YhZ=nk5u?Pphy_l65J_NX;DCEf%*ojh-tYC3C zt*Ukl$_6s7AUB*%56aGZB5Zhtrlx9v5=d6_f!aXmN2MnJPRN6bat4{v-ni5SY$EDKz&uo6Q{-{1n_w3pDWYSW@!(Z&-wMbH&7HHvP z+Gmjs3OM6>(FL1AZRkq+;1XaRG}UdyKgT+y#&)!49*)31z1ivV(u5j}%U9+)S-}*% zJedLYEYu$R7=ls-1&N`L2$Gs4d%|l2-r79(R0duz?om$bHQ8R#my;?Da9$q5JG8=A z#hWbWOSWW8(8m&f!KEFsEFX}tTXU9q_MS@x;MYT-rW4k}u}M0M!ox3}fvU|qOzS^_<$G^T#G4VKrvPjU6vhxwySuB9m>5h z-1-A?(`TdBQe2%qM=}r+AdqC0^~t*88CrJlw?Ag4atO`sKCCIF#_;Ma)@5CW`TAQJ z(oAQ*0`@F+8`jx#Du7f?0f;lO&0QpppCkWtRjVTesN(WSl_n1dyFd;Mp#Je+LF}eb zJxzc?54-oSwd;@}jIzRb_e^WU9wx-zw23!_$oE~T)o`~w|5rar{qr3yed_=z8 zBBK*9@{#O5z?Rp|E3h}xxM6{9a+9pMBav+#?6~Yw-owGUJF#3PC;45C{}Y^pInXANSOiC$PYsnRojKRn=a8tpB)8 zKVrlSz4`_lY-Li|bC=PiSPlARt!(G3?eH{%R)!Hd9NIG@Gikpz1P2)tB&2@zEn%U? zBml7!J%;k8@yNnDk=||*P1SnEoKm^~=$HJ@8{j1bt;&=-8}GfX)M8}fldI^D=b{~! zA6Am<_E_!pwlNbui5@Wvi9Mv4Ro#9!>PKdi-aOI(6F_%Oxvg-mNWrY;Z+tb%Dt>pD z!sL6t7K{%aD9-o|NYA_{lEj^w+R0SHIv#TPP)@pNIuh-ai|=9W8@@Sgn;6Q6!UJZk zaMm?2E_B&8rF08(!DxaPyY8E&Mze)ejEZz26e@k~I!2z)8&+aRe)p%^h@WveT{oOcG0`kd7YiG-_< z*s}Qjq=wOw+~&9q9XP2xhi{kxK8~%22{^NSQ(tw@Sf*Vme=PMCG2#=mZt|Hj+5(oE_tl-~@yf-FL+PU2{f{8^{sTVm^Bixjq>DV8BD;bT?|pO^Oa;&Tu%`=JF$p z8n#PRDaYZq3k@MkneFH;SCp%@CM3?7G-Z0lV%80zQi6_`qc#lSJ_7)lZ z4GpX!;)X35HW|IfApWKA_B;#t#%41DXvRKwZ8t^bl~9kD%vB|FoI9w?G*i3kG1C0- zH3Vx!S}G8w_Ci=|7TBWRibGzU90zyuc-@s1cifZY*>byMO%4=mmEC{Q_lSM+>!gI* zae6nA8L-AL<1MXKnR!4$49*HtU@eCCqiPepWvY6+X%{K4%ZK=dNQjcYMjACL=xyY34@zJ?`@h(+?gA zdEwq_02H2*;^Yt6E`)qXMd8haF*%%jn~Yuq#UxPlq`)|! zy`yBg^lY@^>A>jC>_yj=41Z8ptlLA{_f+|3yB?%{&mfwf3*)|CIu%_KPpH@iUs8^3 z5Etn~vwPoe0Wq;IG!hlCJ-Qrt>2>)Uiy@I$*sjys9MK!|M-i8oZNQL+SDTAqP@l~2 zk~t1RrKvs~>^*|c6mW$%?A13`L|?X0|PN~Wel+>CG6F*UwMVGqt<`? zfit4EqVMR+=?0JprXs`Hm8_4c1!Ux&uwNTC>Usg9aPbIOmh0Y?Z%$|16|BuWOI8C$ zUt^6Fmjmszu=ysF)Wq#rf*q0i*?xETi)Lv++OZyw1QLRpK^QFvexqF7q;)e!nakNX z+cr*OeIB5*aq0;zEaF*FvLsEcLjnZliva%=7R)DKdpoym zQyhabEC5e@eaKxAGzvDZ4jQ@ya~UD0DpS~U%!*&E7m?o_4b{fyG$Fv8^yh%I*1&wg zwwgq;vZUqHzBQR8VjfTSmuv5iW!Q{8xZK`pE7-#X!bfPzSwGI+_&`6JvX^SI;(O`h zHwJ}PuQCL!g4no2J5yt)#C?&tVtnp5|2LxS(~xm6xzv-Nm>bVHzcXDMFL&6tpWze@0IU;?~+=_ zwGJ!qE;44@2+n-Ehx!(pn#bp$Ict3|vaNDmozw|6_Q8LTak%7e7x2P)v911*OpgZ< z8FE?XcqE-=fAf)v$)^Wnp$>`ZH*sGr&R2W*JPYrFFSStW{YE7>K)JVn!2mG(2N8dA ztoStYpWlFg{M+t~3}-X^`Ry-c|D<`((D@&u{2|=m+%i6K{_iMf$@l+GmjeU*OBDR@ ze}KZ1|363hjm*Dnc$RAapT2+6;s1BvGdRv>YY_ThaPWQ37V_`MpXtw_`wPSw#(@uD zs^8iB|LJz+FH!z;{r}RTk@Pn#|DxqTR`VYu{ezbOK<;0%{Yl*aH*)_a+uwHo+ZP!0 z-**4!3jP3w7nJ{9!C8<0Cd^;V{OR#OnidHRj)s#s(+s*lnH(L!sWrZR%0=Dvd;4NE=_vVi`mZL3e;5QqAc{7IjVxAy!jrGHuMdre zu+OD(GOhB{c?7@J9#1+-f1U2r=%DdkaUAqbVeNv#n|h*dVB0zcqA5;`p#@xhT3F(Z zz)`2;dt?nnefbJ&YSzEb2m^eaZTSF>$%Pc_Ov&I0I z{oDATZ-00Y^jDhxp*66J1T6j!|NaBZ*>7jV|L@ZNfe1hEKXCjdf`UU#o{VqF(MjwofR;y%{J@SeYXE}8oy568vXpO;<0++Bx z#zt7y%96^`{F2hr!jcMEhzGhTZkA8SN=KpVBkCafA}S)<4jfAxE7>D}azY8jVZnyr zan>rCn^=g)^TlhbS~`MVeJDfPVrN}!R;7%JSJCsjqS#p>9ShYPL!YCUwjF1pFUhE5 z4Lm9r<-7J#wFCpY75%(lt) z$El)5E3e2!-7YIs3*D~MfxrR$KyYnzs<_ccLRn0I>+$T(A(iNxs7ShaCw61`B7aM< z+gpg)5EKZ-OQ#3VF_ACuHxV=1%9`~by52>O`WDR~Daxo3s}An{MPR26YjmJff*2IrzI>c=&jndH8u;6iu{0K6;*Bwr?b+ zvz1}n>=@x#H0w3Q5G^Wc!{0vks9?`TLQBkID|n(y6(XHC6f~WqCoK2pyA;7 z(epjytxVg{Mc*zCR5(gCx(me}tt6>M7Xm*q5yA?Y9u=77-!3&Pyj`(x`bupp*S5kj z+qTUy%(lQW$+pJvnPa(arelk3q+_vdnqz})yknJZE|1O|Eu;3LJ;NyyTFl$lKEXrX z&S&B>lvUShD*IEhKTUEXNI?i%&FOibJS6?1PB4Wu0mwg-EBZOgHhLCy1(g^LLt&!D zQPt6&s7=%{EghJS?$G6uK5I!SS_W!p=Jj%xT8FMHAJs`~EICRW=~Cw6%;)Uv%)7ZhsfG--MIVB91ur^X6kz`#U*ZoHgKoW?y*yM9 z-2;wyxhRqWk1vt&5zQf zHI&SCF5*|jYMTzUsQieed(z{_9NfdW4YA8v<3opAK$q^uw>?nu(cI1T9F}r$9n_Hi zXy`Uhq}w#!Qar_SMP^|dHNwBUhSqH&+#2QI{RMnk=l1E(PJYerYd*;D+dTMT(CAU+ zK4hbCQLt+q$ zfxfz3b8Dm6v2<2+Xa?mFy?_d#6$A^?m4nOaPMyb`P7Gg+zA$_-eqaCo$bH!TvHKtI z8{fy?H(mX(YP5=3HCg?*I=X6zhNq{elR+l(-Y3SF2|E4yYP_S@8g|1&PT+$o-~u*( z*2-t9>>*DN|8eqmSyO{UKe~E~)qG{JZC6Qt;JO(8F~idPNGRSCNx541^fd91O7T`m ztt$5fkVQe9p6k-*Fug-kWEa-)vO0^~ci{GZn2)sf*%Ml;(w>r!F<#a(I9>3e6dWFqu1;CH#sfS>kffddM@-z(eH*a;kdISO zM6VO2eS^~G-vn&(&XF?R1G0c`qjxxs3`%*^`e?*;#x~@;-T{vhry=SHWQT63O{_j4 zpT9KtR^KxGfKSr`2)VNkOEn9antAC-&YHeU&c(v~)z=aw(9ch88(F-5a#i-c;?tD? znoeCpIKNirJ6p<=RFmFF&#KxKW#uF5!5+azrk(;?;m1Jbh$rVr|DD_-3IrZh+x|m% ztad7KVF4@i>gyA#)=)*KaL?9h;4I_`86qGpRR0W*{z|S$_-49yD)Sri37C&?3(LJc zuBsn^&7UF64~V<~@wv-MO9MQHB83&3;S>o2Xx*2Dhje=+yZLOFW__}NiVbT{Bd?2) zGjcAtO@M0bODwr5pRj#8jf~&meBX7q)e)HVE*S!KnVqB#`DbE&t(FCKe}l?2o;R0P zrDrANBghSs#_vHu!mFYBNpA`At^b*r2W!2rYNhU0?{lYv&SqA_`;Ecs>l}S(loE9r z2Vp+Ig6Zkisa$NIIzU0_6e5CIUt6gh6>0D-F>7DI_P?9CEHG>IXMNmhsr@aU4Hblk z3VWoY`8QC{iK<=!)?J)~MDhkBiE$?ppNGNf%I*cVEioXXNviVA$Z%LDrQ<3u7PaZemT=TFCFA#35UP3Ux zQ|2qm(i=DT?c;)}i=;(Wr?EVZ;?KmY?Q63qk#}98K9!#CRFI)L+!D|LIB|(E|NSK@ zM6)h2#C47-i$61bBtK_j27nTlTi>6yiS~X4V1jJUqoetX(IQ;}z=GBQaycjvawq_~ zvRvo{mVCX&1fYHL!+s%u-R947FKwRjGa|P1Q%scrkH`N8wX+NWMYSGOy#xj3qAH_j41b@xbcz~NLXI?tr6g%cE(ef7QaqTbZ$!^rbv4@ zq-rpMYQcCPa#E8llBhRQfmPr1g>0D%<@T?Ls~vX;d|Pl&u3E5SDom{zHrL``i5SF>@;OD>vVBiygt~&s_Oc37jGJ5nOI)1?%DE{s~TUnl$cI*P= zzizKAfVJdL(MW$q(Vgkndi4m4?0l_DrcL*_>mmK63tvDCw--lmr}j(moLbzeY*~o7 zV^q7yvomT=f(;Q@Wl!7!B$S-qPROmN^i#Yk2q5n@MiL)x0NsfUn3)%K&!R|iF0k== z=YEZx-3O+okVeBe;AK+@sjx0#LD2zWuU4#~sJWH7V2Z+PHkfoCIu04<(^JZ)uJP`Y z6l0&;k5pR2{wa;2tIVbtC_Ck4jId|X4INU+U}CrkZqzy(e{B*i?)8aBQ!hY$@z(fu zr!YdlF1#mx(TXQgcY-?^H#X$0Wz z5=W5086=LB{WVBk!9P-@r^cRyHL=ir#jl6I$?``*=;OW7^vCDFKMwPE#jmKEp5#}K zF!i#`MTh~^*nF=3S&JYIg!yB`x}D}?Ci%h1vbz`)hhe?0!1rp;_U2DORczMrJnxKW z{eYRHY?2U!IX_B>^2HZti4K_f8&h$N7sWd=oW+_-P{{2u&(b0-&2rZ+qn8-njEeV9 zzM%5~5gPwSBOgPqA3J^-PnywC{UAmXRZy&sAv%Izp?NidIR7UkVo$Iu0NmzBrpdaeGCAD;(pG6EhTgl^g`8!{8im&meuC)=sKUOX|k)zfGY{>^iZBte;j!#%V`!fd)*FWuFJ3a6=^)WK8 zPH4r~Q0hZ7&&TR=d6mlRon;x9R*q#1xWfn`=7X^j3|g@(1)$02k*ZLabmkAtAem5` z8empjt!DSYF-GxIXb>~~SD&fqq)(v=KqQ+8m_c2MQ2oFkq58RE)B?Gr@ca7MV$6s! z=2PP?Xw1r1M;Y4Fbs3{hLyL(cT|*KQKmtm9NOfC}(vhwkPgS9UJdKxfJxu$<;1${l zHm*0gPhY9i^4E1Zz+bmSUyf zihCTZ_-mk5prJPkZP1#NN@9% zzd$%x;RSA*mJ}x=(LqD-q;0_a=R56 zW|aOW&MP25g*9uLqrMe7qh7YS2w6x+Mz`8`J0EXmS??6}Hq~Z%cjxL3P#YDkelOJlg$Y!G7}Y zMk9G~0l{z#KAqE7LFg1hL-LCpz@Hw}S8Ir(r%w?SJAzQ;+(407X8_0W$2P9d$ZPf{ zIqwB6Vmm_lr35d>HI&DFjZ2QI)o|>+)AVUXujeO~;=4AdXPWZlXOt0T)BLDh!q7>O zzo%BRN&_=!PVr-dFNcGf$1uW-7_K^W{3FmIa5U%Xol8mBl+;wCQ}dFxB~gWKPl=iA zF}Pxy6up=m57j``RBR$N!K6cPfDR`5DfF0GyF2XrQx*U2t9XrTtyIDls&55M^FFbK ziNshL%f7D`A$Z(k$etu7u9Av=M9RC|RuYgUR^QtrZgjEVYBRaDVCeUW4T$-j}?oVZqzU&AD&JMCu4)NL``@Bj6+&$>@O1izK8~$=tbuQxiN3 zz)6WGoxXzYuc?8GIKH=DO!!`8{rQW4ljS%lFv&v z8S9b(LP}V3oz^t}Jm4eZL3pG1eU=C^nlxblQah>6I|7nLZx-3^Zo4kH+0BiGN5!Pv zm+ihju!>_L!UHsCL=-#y@Iek)(ASc{NJU`0WNzW>5n z&W4g(-ZiY}zVbnznP=YVxv63|GMhI;GNFJo3v@?tp0MO!N|f?Ui%C&>^l?J_f+;lH zroE|I7%s3NAE*+zc7e1g#Njz`XBubZ9#uV9=pD=Vuq1=Hd+d0LpgsgKEye0D6c%nL zr!DYagwTM^p$jUb!pS*2p#K&U8;&^5Q^|9s{znvyAhM{^T z#f{n>1K$#)2PYCQ*hjez-ZrjuaDSc;uNh1}?c?^m^^?;)D*ge-#5c|yJrck?K&k`s z`bg5kluV@$*)dqQ+L3j*sUVN z5VntK!Jj5gQ%4}S_;tLoJ%!`a8r(7ylcx~MBbz9TVVewl`P zlTCNu%r>aVQp3f*T0U>*t-#3_DIngXckATm8@5ZwFAB}c_OlDn)iHNCM?yV|raQb=1J}>ZD``r3;RiR0_7axwBR%{lQnQlyu2BAa&X;E5bd zJqj=O0ItOWD0#*#s~TLooFnHwBc?>`=1sY|w8Nzf`#3fuwi8TX?>i1x-vWW2%L9C% zcLqwS?A=>{zxkOU-yBNiuhD*w(+H%gwP4j`@g>=4dz~-vI>S{D!Z;NTc1gq;(OY!c z00rp_FmqR7f+e@!z*2Qow+SbfY#+Lo^w1L-Qv4}$<|nh;n<&1{s|aem#LmxaT)RRt zI>}DrgSNaD-`i7-qEB1LhAjI&g1%4beA4pTHm2)`v*@RIbwUu{Z1?@ZYFwi(Yf+$H zzK-ME?LXY)9TKrAjNQzT=V8zs`a0Z3!I39A@%7x={_jPJ=G>0X4<$uRd=;It(M_A2 z_t_5*)%TZjO%Ywtz;UTx0H_fg#rXJ=3?fkW_Y_LJ8RkHYTi8(&lf^0B|7%Ua`CyE?H}Ur8eyDz=W*|;KUE9drIB%Gwgt_ zQuQkBlC{Q}dLdisac(zV)b~;EA=8J2W+qX`M-qUh8UDW9$XpOU$AqCDfUp{M}_>q4Z z`BJ&W7NcL8%2}X6JH3`GB;}!4cuvhx<dHivTz}TdveQ%c*n~DJ;m9BPcM-x zn!Ec@&KCBFHZSm`7pgdC^cB$CrkN%|zSMxWKSU&9;*Ig`P%016 ziCNAQ<}*P<(5@xf0+N{q@LEKcC{;hvKCcGl7ZIBQ3D3fjB*3uzadVm$egg|S!P>#W z-9G6qUvIEI;@*=SVO{ z(=El+zH)Vpx!E*0HLWlbOb)mpbT(f#&O8@apz#?tS2eJt2=i}ezRNXIqIc`Bh2$g4 zKRm`$=S=Ow0uN#?LfU935D+xsA;-p*N_Cp#uc=(W0kYqX0yC3HGQ0fh370uv*knxd z56?i@Do+RJQ#pUP!#e?IE5uKFHZ$I!olSrjsWUmj>pHxSJX5T{KkzW&dz>}??;iNZ z|J~!C#{c%fn~nc(czFBa^i{E>c`m)JPglmwu=+g@MVf6(#eNPKt>5(x zXV&|@##gl@cvjc`QQqmJ+##3APJ3npV6#;lSOx%%KD6d0)Zl`ii}`>3=%oS$ZVer7 z4lajG$dD6k2>;U&2?=UsxXBXpu3?W{X;x+GadZ?3 zf81+fKaQGFqmH`~unueLJB2uX|=dvkur{?F$AVy~PVx>geN z=T=~v&k3zxo9+>6ZLm$7%&|GKRi$TWk=M8t180g&W`3G#ce|G0FHQWtw+*SDyQ5^z zZ@QW{^!LA zi&8%dIs5HHX>oUSl_^xEUJGTVfv-IHgIY(bZIzeZ_NQG*jf}A$E~wPGt#t-I1R2XU z57?*Ko)b}#$chb|LcOmjNlQo0l1d-)yQ!;#y9zZ|St+w(udJn;<~6YNoa6u~QB{MK zjScplJdONtzG9@B$1l0|sWDXPwfL`7>zFR9jPo{d}!t$^$Q(o5q@=t*3kkh1Xb zhlssN30Mqr_Rb4TMw=g_gM6pfSA#9;Q}x_=vv4C)CvXiJLl;F=1diuWdGM#UPfV4) ze?woKCWGY{*1J`G({6Mak`VtHvhXs^go4k3{{ds!r7bm{t#~C`662SOx1hNa88=h+ zW5F3-;=n1QPFO*mFo74pi??6FZfF2cxOZzG1y#&Um;G3Ntua)3PJf1$GUmZ4zO3jE z4Pid%RFWAtgdM0R5wyg%nePWT#-y7IQ-%n4`pj{;pHP3k7=dx{d8JfuMPR*_~olXiLVR56FuZICi$75vh z9k!07zFIt*{^*ucnciXXj24xCjv5kNHPO9k;FcFDjRzu=HGF7;AYGMGB)t84hBHtF z>&ABa2wy?ti+yOu^~|39tTY64VUg1p5?M*czwU4uqeJ@sXtfa%VE)AGb2gA>txvFG zXudEll~F(lIUoRvZy*Ozme2iLkp1&Ii-$KX1-{Hn`9q3$In%Wk;BY})Y}>#<>1O&7 z{$2n;r?e~e4ohWBLi#ysNpQ}`OEc;3ajql49Z|gLgZG#Q7gB}D;M+V)rW)M2C)<^1>Bov%YQ2xluCC0R`m2Z_Z5{o@{1eWq zU679+rB}r`=hqDnZLV1WM|=Y!`=%1DSa`!6a@f% zYpkGl7BOy#lU=gs;*%A`9A=uh>m1DQ1S>)9c0V4|{x%z%KklxLaGJkGO(BbJgDTv` z%a24^bYM{6!iol(94)gX|7)Zmg$er;@^%Z2lwj6m7d#6Thl3L_zS8cm$LT!|1aD}X zm@PG$Wl68IbDf@5p;0I5QEeIOII>6YYic$Hc^}L;thcfS8>J$=L!_-wTMp!i8}_g0cHv~RVMo}+3E*VKs-}@?h&2bFr{buBsH)x3m8S@Q9#~} zw^F1F3Mzt43f>pPYhS_r`5B|aguGsa-+K@QD7T1dxHyZ)Gn>d?Em^vTD@KBjpY#rf zFR>;dka7y7t6i~*bT;nQDz*`ZP#}jkgEXbZei8Nvt4&0~^d@=>s=3}vWtJ{JimF8} zWAt@Rn*!&tGOyOXMyKSUh6)c^{Gp2$JrhOa3v~MiArn_=(BeEV=||7* zIu7z})m%eAALBJ28)2(9&WfZS?#fMdD^NR$c1e^*KRcBs~@Y;clJ7+vh)e_`9q zV)86m9lFy_IhalekN{9VeGQQZ`-`z_2AHj?CU-Y?*7QYzhE#CB;tWX?DV#rMlX7#I z*97|P?#(;DLi+TfTHnIb&%<+$o@}!NN~cK_{Ve6tXU8q?J1#mYw)y0g?F`Pf$s|k- z%}2}v==J@B6M1Qi@R6t&<;#~mriDH9BDQ=4_A~#A*-riKZ!F0XZ}`}O)8i<+4S4Tr z{j3652e|v_Gu@tqz2d-i@G;w#o8gn5SHiNOQLY=WrH!|5#N8}>-3XjbputG!=*;+U zzSP#2`($&;*F}M$E9Nwf)oEE~vX^h)GB~M8+#q5=*z=_-RK2+zdyCy*EBor4eV&Xj z*QkAkF;w)Ui}5YpG{wzm^L{Ig)mX0~S#jkrgX6w$heBkyxuU_(kvmb?+Wlt~ONT8? zHI4Vr>LWqdI5^hmJc=>h6$c)BuvTh1O|V3K7Zqv7GLiff_n*0y<5P&4>&7~t#daKvXEFFiH)~8!-p^oNkH_Y2yi;Y@Bz6*lrs`CUx4X1ojQ%Rf z6r)mE)zWvN$<`uvfh!mW5aG2g^S3)0^Gxj~r1!l2hSN7r??qe;oSC{h7xd-baFY1q z$5RQh_;%?&I!6%(;7k>SJ0nZwrVf{@)#W&zCEao-f+{*4nx*%ya=%qQnmYG7KN>gy zdZqfcX*EQXS#rBb>(|Q&Qdd)TxJ^xR_WfnEkOnfi*}g>2^VJ!jG&zjxRhDB*!F*W` z_wL~1(;wMXNMmEB*B#+^Zo*VRp*MR$=Lxl>KJTUJYF;2C(LLh7a#xL2km*X{Ey6VM z^DMou>?6`SEqv}!>k~tuu#0Hw%eSjFD6RG_h%S-{tyUlU?6z)+?6w6?uT6g0y~lBQ zqa&kj=EjWgTHE^Dx9)3!yQ~OWYS_7R2wITqQ>5beMv`|! z`rIpRz~Kp;#-`9Y^qy`JNjvm?TENk)eotBu>O8D+{}+~opA&uV90@;U0yrqR+$i!< z3^=E;;;dk`Vw`|w-3}T4 zJBYbE$aTF{qR%sdkHk`jff@7;cP_Hw2U!M|oTkapS4@Dr=CRrvv zjCLPA{3-XkZ?a_9~8 z_(ZfwC^3o}rVKrQjv9slS3m+x4wr2sM~uH`*YCtCuMa%*ETKPm>Q(yDDj?0RBni3p zicV$Q5*2QwK-u;#t_c#taa&ISV{#aHzc6#o^DpC})Ib%%L8a5>T?a+%o8St|-4%|y z4<_{8QM1EGSlsh11>im6v3y&>?a!%JeDVXc>&Q!&b?S@Cr+@sI6YET^B`IBA358b# z_}!};GyJY_P<8@4+&iw=*30+!cM?ZXn{x0V(f;`Nkl7vm!kW3F5AO60k-H+tr4V98Wg*Ml zuGT31|ry=U&&q+e~BT7@XkE;I~4U;4~oNCEw+jr{z$hHYg6}bDz+r7 z%dFqnnK5r!xszSQG3FO;Gg=xDRxc?Y7#?gExlA~nkriJq2aj)fHKz2cZTh>d-mRq5 z-r&#*1=IKrj@)Id)E24V+Wd0V4;u&y2qiB|w20d^4p}Mxv zb%sLZWXJcOAxEE!ap1Q!L!T<8q_Dctg@9v84ydrg(Wy%ZR5vIeOmVB72@wMPC`(jB zwM0SyEvUkD)?(shK~y9l(1}~%Y@y1~&YsUsQgoXx`_0}7ufX*ce6r}Lii)eK+gO{n91UZDfxv+3 z0bOv~gb3s&e1i#D6dFD3f97er&A-=pxuNe?{N?wSC)}z0m8}zhY`$t0WLfaw*Z%3oHAjq+(Q`}as@70Mwrx@XV#$RDH1#=f04RlJ z{bwnq9G>${nVT8T6#D1(pz>PUGGI4Zc1w_BrWjXXpv zM?e!+UtLJ?bO}Rjv+uv^8Q58{+!9FbW!9M_R0uoz*tsgLZ9JWpAr8Flo9LH;|M(7D ze2TOUbUfYH@VGK~jxFTLk);Ws;{F}o`F($*@+tRbG^J9^u?YMNVq|B0C7%b_YtP7= zO8R)D93tr7ACZ|2F(2Y*%KdmoC;)aqD^t>AzIkLUR!>J50&z&l%Qt##O$$fXZ|%4Xmy z-}Qz!jYRj_yYKJZ2(KkR!}@Rbb%8ABcx?k&oA*zUl`n5Tkz8BQV5y}d#k~@;R0miE zQ2qc{I3bZd`Z7$eVHv7< zj>VtuK=RxRKC^m`h1>{-H1}Q9(EEn?&Cvizy;h*nJ-QXwhlP{@7Y|G_bPB<~BYHad zNczurxBvuU&J=ncKS}A_D;}I1htLGW(I-p#PhXq$gSR;KFBG zP~-M1{lc%_yhxuWmAak&lkmi-!r`=;GDi)7k@*Y-;0quHp$lRey!RihzBPvyoodRu zJdt4Fu@pbvomiSRp z5$6XxCQu64$5Eudhw&vjWaG>-H9@~Tm40?!6<3Mqm&6R-=!g@)6+O>}4Wat4u|~zG z*UO&r-~P&oHMu0?Ti<=y$pVCHLc-3 zV)2e5CgsBLDPDltOnTK}tQk_8n?uvl_75z7otvDQepPJ7BT?2}!+LGYK9f~)S3Q1U z#-UCAs!FF<@kGH?f-p#jl!Tx1OnaT9#*C2R7_Eqz7NySA;A5U$szs^=cmC&dUA$ + + + + + + + + image/svg+xml + + + + + + + + R-merge + + + + k-merge + + + + + + + + + k-merge + + + + + + + + + k-merge + + + + + + + + + k-merge + + + + + + + + + Insert PQ + deletionbuffer + int groupbuffer 1 + int groupbuffer 2 + ext groupbuffer 1 + ext groupbuffer 2 + + + + + ... + + + + + + + + ... + + + + + + + ... + + + + + + B + + + + B + + + + B + + + + + ... + + + + + + B + + + + B + + + + B + + + G + 1 + G + G + 2 + R + + diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/san00b_pqueue_small.png b/third-party/MQF/ThirdParty/stxxl/doc/images/san00b_pqueue_small.png new file mode 100644 index 0000000000000000000000000000000000000000..6d717445e2001c32a4f98c634b085e42f316cf4f GIT binary patch literal 39824 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 Z8RzKKs~WbW%>NCiB(EY@^vu}*{{T}r1Xut7 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/simple_logo.svg b/third-party/MQF/ThirdParty/stxxl/doc/images/simple_logo.svg new file mode 100644 index 0000000000..67486d62d6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/images/simple_logo.svg @@ -0,0 +1,118 @@ + + + + + + + + + + image/svg+xml + + + + + + + + ST + X + L + X + + diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture.pdf b/third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture.pdf new file mode 100644 index 0000000000000000000000000000000000000000..336dedefa285a7c415c3ff17c024ee738a214308 GIT binary patch literal 3223 zcmb_edvp}#6+cCeX-9?V5zE8ECnRS!$L!9`&d$Ev9hziKiey8&SujlqbTa#GcF63` zGBcZOmWSa`#U7*n0eVoV0_Ru+ieOqewbH6o!P9CH+w}M##i&Iz6jVahqPF+jM_zCW ze`NmIZ|`^C_jm98O6vpFR?g<2N{^m-cn2jQ7HO?3D4&n1#fdgEhMYhNG1ZD{VjVHn zs$}8-mbC~LManRBEXAqx)(0PXJ@GDkefPNxHGAWCPFe2ieR;53dNHwg&2^dYhUiy{ z_q8{vtG@p6%J)m^ZkZ*%wMqPJ`OvK5rfpx}`j0(-U%j?xf0t|ayj4>^J$mMgfzP%` z%bUs<-haBJYwAlk^-g>5!#9eGo<9=4ue7Q5WAVe)tq0Oi-YA)?=QMRcF#VN3Zv5Ss z>SoLK)KAO0!oU1izjRyeI%(CZ_gC%})*V`W&qGHK(%(O}xmf-8N0Yo?rgtpu`0Ix2 z{#4X@{kJ`zzr2a>s=HC`nOq^9*mv^&u5T8sKf7b`h6~3J*SeR?7+m;LagDiXTlkT{ z@n1c>Yv#`1+_EedI5vF;MsF_u!_VHjVW@W7?mhqM?@%T!ZkhJg!QI~rpG7s#$u;2z z8~XP5Y3(yN?A~+n(>cKp{?=zbTlAOxGu~Z0`4`JSnsPG!+LmYgzZf{MVqoFk^R@h3 z%iJlS9QgRu)+3MV&rF{D_8oUu_CNmiisv%QsS8ujA8=0HIoP-6gsZrBT~g}4^_{1i zohK)4xuHDz!lPTRjV=4_Y4h1Lkvo`5v}wBcv7+8tuZn*@cyY@5-+ykoSohF1p4T6E z)jQ9V>V56WPtLU*TJz4%k|FQ1CH!rf{XI9!C+MN#C!%Kky_FlEnbtS(;#W1-T=?~4 z!aegJ??3iW=LdDGs_$BNxVj{?cSicZ>Hb}Z4=vwhsT;a>cK0{m4$fTby8YhFdYbu( z<8*BM8%KVAbIF{>p|a!p)7rr1H@DvOgI!O#_ie|0gDu92o}%+pL)+KBhZB)JX5d6{ zM$kom1;Ih(NsbW+hP#rOsh8R?6T}fks?^e`nT2a7>q4Bsw@`4YV}LyD**+VY~@uxMVgN-`x?Yb&V8J0HZR6oDJW z5rNURU1JrDL_sl&>Vt?A#k?gFRK9ydE{ceSM!7+vEZn*&_t;zVH-A|v8JOgS2`h1I zSVLM|N@P7}2$f%D;T)V@6!T#4%}*yKxgDFR z6}Kq~KYi}`7igqJ{Pf*IkPRlQa7_78M#l}Ay0DySmpu`BzDW7zOMoG4O-o5@G&`RR zgbmq0w<=UyJ(n<~z3F%|4sM`yTum6>w4ct3c;QUQ42`lZX1kwm4Avu4)6{mwM0T6U z##uSO97T00jypBI9dR}%4J!3W)EfdsF7|!GIFsz-i z+pWN1HM$a}l(r^}d0;Q=h|)MLmN0BtpKP)gXOu>Uv$0IUPyoWwm87X?2}BG_ty;?T z({?+Loi1Lm;s_^LISxx!7Cx)1mBZLBxn$mn3zm|jJRD;RF2EKKDS(p~x+Rj5vF@j9 z>T6L=Of$@!Y2>y!?KG2*d;%PTYF$2dxX1;M;N%Tz5hdCcfL4WgS&p@`ZY$4)Ifs|^ zcInQ~0U9au$z zRZ^2NN$@e_1$_)D#;9COXm*0Z|DT_DoWNE}27}&!Ca2;st`-EY5)>0y$A~=aW-_I# zS=>it2CJBOWI!o7@G~NNqnaL<0JTywsVcHWz{~*TXwe6kredmC%yw0UYUU2ALb}AO zN{Kclo|%f{Kr>L50THD3kVQN+^s6%jKp+8LUl`ZYV5iLQ4SplVe#o&v6dclkHU+dKK89sjt8h zCU~%nRJx#B(=Auot{_p7GGicc)uk>QUZ80mM=2I@c8Uzt+=rZkzzZmf3N*J9HY@p1 zqCm3??n`K_n}wx1mXCD`m(o}c9vkENc-ZCRXdWIaGM2`%m(o~|3tklC`2gpa&^)7j zF?C5%u?`P^#55=w3^_ndNYmgcLmmo1=b~!rf1qIy9RL6T literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture.png b/third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..d5343ad370e0eecf2f87d67ab0eca48bb7ab0f6b GIT binary patch literal 33965 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 literal 0 HcmV?d00001 diff --git a/third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture_small.png b/third-party/MQF/ThirdParty/stxxl/doc/images/vector_architecture_small.png new file mode 100644 index 0000000000000000000000000000000000000000..1f071d4e75421ab55660605a9a4978e3e155ea76 GIT binary patch literal 11572 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 + * + * 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) + **************************************************************************/ + +/** \page install Compilation and Configuration + +\subpage install_unix + +\subpage install_windows + +\subpage install_windows_boost + +\subpage install_build_options + +\subpage install_config + +*/ + +/** \page install_unix Compiling and Installing STXXL on Linux/Unix Variants + +\author Timo Bingmann (2013) + +Precondition: Make sure GNU \c make, \c CMake >= 2.6.4, \c git and a C++ compiler are installed. See \ref faq_compilers which systems and compilers are currently supported. + +There are three methods to use STXXL: +- The first is recommended for small prototype projects consisting of only a few source files. It uses the \c local/ directory within the STXXL directory tree. +- For larger development projects, the STXXL library can be linked as a library inside a subdirectory. +- For distributing independent sources, the STXXL library can be localed using CMake's \c find_package() mechanism. + +There are quite some \ref install_build_options, which you can use when building STXXL with CMake. + +\section install_unix_local First-Time Compile and Simple Projects in local/ + +1. Clone the STXXL project repository as \b my-project. +\verbatim +$ git clone http://github.com/stxxl/stxxl.git my-project +\endverbatim + +2. Compile the STXXL library in a \c build subdirectory, including the example in \c local/. +\verbatim +$ mkdir my-project/build +$ cd my-project/build +$ cmake .. + +$ make + +\endverbatim + +3. Run the example program \c test1 in \c local/ +\verbatim +(inside my-project/build/) +$ cd local +$ ./test1 + +\endverbatim + +For your own prototype project you can immediately start modifying \c test1.cpp or create a new .cpp in \c local/. The CMake scripts will automatically compile and link all .cpp files in \c local/ correctly with STXXL. + +The CMake file has many build options (see \ref install_build_options). Maybe the most important are \c BUILD_TESTS and \c BUILD_EXAMPLES. By setting them with "-DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON" on the CMake line, additional subprojects are added to the build. + +By default, STXXL compiles in Debug mode and includes many assertions and run-time checks, which typically slow down performance dramatically. To use STXXL at **full speed**, please set the build type to Release by adding -DCMAKE_BUILD_TYPE=Release to the cmake line. + +\section install_unix_subproject Including STXXL as a CMake Subproject + +The second method is for including STXXL in a larger program as a subproject. This is particularly easy with CMake: one can just \c add_directory(stxxl) in a CMakeLists.txt. The following guide shows how to start a simple CMake project and use STXXL in a subdirectory. + +The advantage of this method is that the STXXL is a subproject of your's. Thereby it will always be compiled with the same set of CFLAGS or CMAKE_BUILD_TYPE as your project. This is most convenient for developing projects, as the STXXL has a lot of debug code. When running experiments or going into production, the whole project must be built with \c CMAKE_BUILD_TYPE=Release or similar to remove the debug code. + +1. Create an empty directory \c "my-project" and clone the STXXL inside it. +\verbatim +$ mkdir my-project +$ cd my-project +$ git clone http://github.com/stxxl/stxxl.git +\endverbatim + +2. Create a file named \c CMakeLists.txt inside your \c my-project folder with the following sample content: +\verbatim +# CMakeLists.txt example for STXXL + +project(my-project) +cmake_minimum_required(VERSION 2.8) + +# disallow in-source builds +if("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") + message(SEND_ERROR "In-source builds are not allowed.") +endif("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") + +# enable warnings (always good) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall") + +# include the STXXL library in my-project +add_subdirectory(stxxl) + +# apply STXXL CXXFLAGS to our configuration +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STXXL_CXX_FLAGS}") + +# add STXXL includes path +include_directories(${STXXL_INCLUDE_DIRS}) + +# build a program and link it with STXXL. +add_executable(project main.cpp) +target_link_libraries(project ${STXXL_LIBRARIES}) +\endverbatim + +3. To show how that this build system works, we now copy the \c test1.cpp from STXXL to the new project as \c main.cpp and build it. +\verbatim +$ cp stxxl/local/test1.cpp main.cpp +$ mkdir build +$ cd build +$ cmake .. + +$ make + +\endverbatim + +4. Test the compilation by running \c project +\verbatim +$ ./project +\endverbatim + +\section install_unix_library Including STXXL as a Library + +STXXL compiles into a static (and optionally shared) library plus template include files, which may be included in binary distributions. + +If a binary STXXL package is installed, the following few CMake lines will automatically detect it and configure a program to build with the appropriate CXXFLAGS, include directories and libraries: +\verbatim +# search for stxxl-config.cmake which contains the library's configuration +find_package(STXXL REQUIRED) + +# print some info (this can be removed) +message(STATUS "STXXL_CXX_FLAGS: ${STXXL_CXX_FLAGS}") +message(STATUS "STXXL_INCLUDE_DIRS: ${STXXL_INCLUDE_DIRS}") +message(STATUS "STXXL_LIBRARIES: ${STXXL_LIBRARIES}") + +# apply CXXFLAGS to our configuration +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${STXXL_CXX_FLAGS}") + +# add STXXL include directory +include_directories(${STXXL_INCLUDE_DIRS}) + +# create and executable and linke with STXXL +add_executable(your_program main.cpp) +target_link_libraries(your_program ${STXXL_LIBRARIES}) +\endverbatim + +We have create a simple example project, which contains the lines above and an example program. The source is available via github: +\verbatim +git clone http://github.com/stxxl/myproject.git +\endverbatim +See the README at http://github.com/stxxl/myproject + +# Create a Disk Configuration File + +For STXXL to function beyond very simple examples, you must define the \link install_config disk configuration file \endlink. The simplest method is to create a file named '.stxxl' the same directory as you execute the program. A basic configuration might be: +\verbatim +# file path,maximum capacity of the disk,access method +disk=/tmp/stxxl,1G,syscall unlink +\endverbatim +Please see \ref install_config for further available options. + +# Notes for Linux Distribution Package Maintainers + +Package maintainers should make sure that \b both Debug and Release static libraries are available via the distribution package system. We currently cannot keep a stable binary interface for the library, thus providing versioned shared libraries is not a good idea. + +- The CMake build scripts will generate static and exclude shared libraries when run with "-DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF". + +- Debug libraries are suffixed with "_debug". Due to CMake's system, the library must be compiled once in Debug mode and once in Release mode! (see below) + +- Running "make install" will correctly install the tree into \c CMAKE_INSTALL_PREFIX. + +- The binary package should only contain the \c stxxl_tool (since we do not provide shared libraries). Alternatively, the tool can packaged in an additional \c tools package, thus leaving the binary package empty. The \c test1 binary must not be included. + +- The development package must contain all installed headers, and both "libstxxl.a" and "libstxxl_debug.a". \n + The CMake script will also install CMake project files in \c lib/cmake/stxxl , which must be included in the development package. \n + We also provide a \c pkg-config file, which installs by default into \c lib/pkgconfig/ as \c stxxl.pc and \c stxxl_debug.pc + +- Binary Unix packages should not use Boost. + +A typical build sequence would be +\verbatim +mkdir debug; cd debug +cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_STATIC_LIBS=ON $SRCDIR +make -j4 && make install +cd .. +mkdir release; cd release +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_STATIC_LIBS=ON $SRCDIR +make -j4 && make install +\endverbatim +When building in this order the \b stxxl_tool will be built twice, and the Debug binary will be overwritten with the Release binary. + +*/ + +/** \page install_build_options Options for Build Configuration + +STXXL has some optional features and compile switches that can be changed at build time. + +- Maybe the most important one is switching between \b Debug and \b Release builds. Debug builds are very slow, as STXXL contains many assertions (which are a feature not a bug). With \c cmake the mode is easily defined by using +\verbatim +$ cmake -DCMAKE_BUILD_TYPE=Debug ... + +$ cmake -DCMAKE_BUILD_TYPE=Release ... +\endverbatim +The mode mostly changes CXXFLAGS. + +- Some parts of STXXL have been parallelized using the __gnu_parallel (aka MCSTL) library. Currently, with CMake one can only use the newer __gnu_parallel library by defining +\verbatim +$ cmake -DUSE_GNU_PARALLEL=ON ... +\endverbatim +when building. Parallel is now ON by default for gcc, if it can be detected. The cmake script will check availability of the corresponding header files. + +- Use Boost for file I/O, multi-threading support and more. Boost is required on Windows for older MSVC versions and is CMake tries to automatically find it. On Linux/Unix it is optional and not recommended, but can be activated using +\verbatim +$ cmake -DUSE_BOOST=ON ... +\endverbatim + +- STXXL contains many small example programs, which can be built by defining +\verbatim +$ cmake -DBUILD_EXAMPLES=ON ... +\endverbatim + +- STXXL contains a set of unit tests in \c tests/, which verify most of the libraries functionality. These programs are not built by default, because this makes it easier for STXXL to be used as CMake subproject (\ref install_unix_subproject). The test suite can be built and run using +\verbatim +$ cmake -DBUILD_TESTS=ON ... + +$ make test +\endverbatim +Defining BUILD_TESTS also builds everything in \c examples/. There is also a \c BUILD_EXTRAS configuration flag to build even more, longer running tests. Be advised that the test suite need quite some space on your disks. + +- CMake can be instructed to use other compilers, by setting, for example +\verbatim +$ cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ... +\endverbatim + +- CMake can install the library and all public headers into a prefix by running: +\verbatim +$ cmake -DCMAKE_INSTALL_PREFIX=/some/dir ... + +$ make install +\endverbatim +Additionally, the installation subdirectories can be specified using the following options: +\verbatim +INSTALL_BIN_DIR=bin +INSTALL_LIB_DIR=lib +INSTALL_INCLUDE_DIR=include +INSTALL_PKGCONFIG_DIR=lib/pkgconfig +INSTALL_CMAKE_DIR=lib/cmake/stxxl +\endverbatim + +- The CMake script by default only builds a static library. Shared libraries in Linux incur a small overhead for each library call, which should be insignificant for STXXL. However, keeping a stable binary interface for shared libraries is currently not planned. Nevertheless, you can build static and/or shared library via the following switches: +\verbatim +$ cmake -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON .. +\endverbatim + +- On Unix the pthreads library is used for threading, on Windows the Boost or STL thread library is used. For testing compilation, the STL thread library can optionally also be used on Unix/Linux by setting the \c -DUSE_STD_THREADS=ON switch. + +- For build testing, the CMake script can check that all header files in \c include/ compile by themselves. This is required since each header must be self-sufficient and include other headers appropriately. These test compiles are trigged with \c -DTRY_COMPILE_HEADERS=ON. + +- Again for testing, the CMake script can be instructed to run all tests and examples +\verbatim +# with valgrind by setting +$ cmake -DUSE_VALGRIND=ON ... +# with gcov for test coverage analysis by setting +$ cmake -DUSE_GCOV=ON ... +\endverbatim +The valgrind option also enables a few additional lines of code that clear memory areas that are intentionally uninitialized. +The gcov coverage report can be automatically created using the additional targets \c test-coverage (runs everything) or \c lcov-html to generate the HTML report after running the test suite. + +*/ + +/** \page install_windows Compiling and Installing STXXL with Visual Studio 2012 and newer (without Boost) + +\author Timo Bingmann (2013) + +## Prerequisites: + +- Microsoft Visual Studio >= 2012 (VC11). \n + For older versions you need Boost, see \ref install_windows_boost. +- CMake 2.8, visit http://www.cmake.org/ +- (a git client for development on the bleeding edge) + +## Unpack STXXL source code + +- Download the STXXL archive from http://stxxl.sourceforge.net
+ or use your favorite git client to clone the development version: http://github.com/stxxl/stxxl.git +- Unpack the archive at a convenient location. + +## Use CMake to Generate Visual Studio Projects + +- Open the CMake GUI. +- Fill in the source code field with the place of the STXXL source and pick a build directory. +- Pressing "Configure" brings up a dialog box: select your Visual Studio version (MSVC 11 here).
+ Note that you must select between 32- and 64-bit building here. +- Press "Generate" to run the CMake platform checks and to generate the Visual Studio project files. + +\image html install_windows_cmake.png + +- After generating the project, CMake presents a list of optional configuration switches. Maybe the most important are \c BUILD_TESTS and \c BUILD_EXAMPLES. By selecting them, additional subprojects are generated to build and run the unit tests for STXXL. + +## Use Visual Studio to Build STXXL + +- Afterwards, use Visual Studio to open the stxxl.sln STXXL "Solution" containing the projects. +- Building the ALL_BUILD will first compile the STXXL library sources and then stxxl_tool and the test1 program in \c local/ + +\image html install_windows_msvc11.png + +- You can immediately start working with STXXL by modifying the test1.cpp code in the \c local/ directory. +- Simply switch Visual Studio into Release mode for building without assertions and extra checks. +- To build the examples and test suite open up CMake, enable the check-boxes BUILD_EXAMPLES or BUILD_EXAMPLES and press "Generate" again. This will create many sub-projects in the Visual Studio solution. + +\note For instruction on configuring disks on Windows, see \ref install_config + +## Using STXXL as a Library inside a Project + +TODO. If you are an experienced MSVC user, please contribute! + +*/ + +/** \page install_windows_boost Compiling and Installing STXXL with Visual Studio 2010 and Boost + +\author Timo Bingmann (2013) + +## Prerequisites: + +- Microsoft Visual Studio <= 2010 (VC10). \n + For newer versions you \link install_windows don't need Boost \endlink anymore. +- CMake 2.8, visit http://www.cmake.org/ +- (a git client for development on the bleeding edge) + +## Unpack STXXL source code + +- Download the STXXL archive from http://stxxl.sourceforge.net
+ or use your favorite git client to clone the development version: http://github.com/stxxl/stxxl.git +- Unpack the archive at a convenient location. + +## Download and Install Boost + +- We recommend the official binaries for MSVC. Visit http://sourceforge.net/projects/boost/files/boost-binaries/ + and download the newest package for your compiler, e.g.
boost_1_54_0-msvc-10.0-64.exe +- When installing the package, make sure to install in directory \n + C:\\Boost \n + instead of the default location. This is the most easy way to enable the CMake auto-detection macros to find Boost. +- The pre-compiled Boost libraries are then located at \n + C:\\Boost\\lib64-msvc-10.0 or similar. \n + You must rename this directory to just lib for CMake to automatically find it. + +In summary, CMake only looks for Boost at a few locations: C:\\Boost for headers and C:\\Boost\\lib for library files. While it is possible to instruct CMake to look elsewhere for the necessary files, the easier way is to just rename the directories as required. Otherwise, the Boost location must be specified to CMake by setting the variables \c BOOST_ROOT, \c BOOST_INCLUDEDIR and \c BOOST_LIBRARYDIR in the dialog box each time. + +## Use CMake to Generate Visual Studio Projects + +- Open the CMake GUI. +- Fill in the source code field with the place of the STXXL source and pick a build directory. +- Pressing "Configure" brings up a dialog box: select your Visual Studio version (MSVC 10 here).
+ Note that you must select between 32- and 64-bit building here. +- Press "Generate" to run the CMake platform checks and to generate the Visual Studio project files. + +\image html install_windows_boost_cmake.png + +- If you get any error about CMake not being able to find Boost, refer to the previous section on the directories which CMake considers! + +- After generating the project, CMake presents a list of optional configuration switches. Maybe the most important are \c BUILD_TESTS and \c BUILD_EXAMPLES. By selecting them, additional subprojects are generated to build and run the unit tests for STXXL. + +## Use Visual Studio to Build STXXL + +- Afterwards, use Visual Studio to open the stxxl.sln STXXL "Solution" containing the projects. +- Building the ALL_BUILD will first compile the STXXL library sources and then stxxl_tool and the test1 program in \c local/ + +\image html install_windows_boost_msvc10.png + +- You can immediately start working with STXXL by modifying the test1.cpp code in the \c local/ directory. +- Simply switch Visual Studio into Release mode for building without assertions and extra checks. +- To build the examples and test suite open up CMake, enable the check-boxes BUILD_EXAMPLES or BUILD_EXAMPLES and press "Generate" again. This will create many sub-projects in the Visual Studio solution. + +\note For instruction on configuring disks on Windows, see \ref install_config + +*/ + +/** \page install_config Disk Configuration Files + +\author Timo Bingmann (2013-2014) + +A main feature of the STXXL is to take advantage of parallel access to multiple disks. For this, you must define the disk configuration in a text file, using the syntax described below. If no file is found at the locations below, STXXL will by default create a 1000 MiB file in \c /var/tmp/stxxl on Unix or in the user's temp directory on Windows. + +These are the locations STXXL will look for a disk configuration file on Linux/Unix systems, in order of precedence: +- If the environment variable STXXLCFG specifies a file, this is used. +- Then the current directory of the program is checked: + - first for .stxxl.$HOSTNAME (for host specific configuration), + - then for .stxxl (for general configuration). +- Then the \c $HOME directory of the current user is checked (usual method): + - first for $HOME/.stxxl.$HOSTNAME (for host specific configuration), + - then for $HOME/.stxxl (for general configuration). + +\warning On many Linux distributions the \c $HOSTNAME variable is not exported. For the host specific configuration to work, you must add "export HOSTNAME" to your shell configuration (.bashrc). + +On Windows systems, STXXL looks for a disk configuration file in the following directories: +- If the environment variable STXXLCFG specifies a file, this is used. +- Then the current directory of the program is checked: + - first for .stxxl.\%COMPUTERNAME\%.txt (for host specific configuration), + - then for .stxxl.txt (for general configuration). +- Then the \c \%APPDATA\% directory of the current user is checked (usual method): + - first for \%APPDATA\%/.stxxl.\%COMPUTERNAME\%.txt (for host specific configuration), + - then for \%APPDATA\%/.stxxl.txt (for general configuration). + +\note In a default Windows 7 installation, \%APPDATA\% is C:\\Users\\\\\AppData\\Roaming \n +You can visit your \%APPDATA% directory by simply entering "%APPDATA%" in the Windows Explorer address/location line. + +\section install_config_format Disk Configuration File Format + +Each line of the configuration file describes a disk. Lines starting with '#' are comments. + +A disk description uses the following format: +\verbatim +disk=,, +\endverbatim + +Description of the parameters: + +- \ : full disk filename. + - In order to access disks STXXL uses file-based access methods (see below). Each disk is represented as a file + - If you have a disk that is mounted in Unix to the path /mnt/disk0/, then the correct value for the \c full_disk_filename would be \c /mnt/disk0/some_file_name. \n + - If the string contains "###" (three '#'), then these symbols are replaced by the current process id. + +- \ : maximum capacity of the disk + + - the following size suffixes are recognized: + - \c K, \c M, \c G, \c T, \c P (powers of 10), + - \c Ki, \c Mi, \c Gi, \c Ti, \c Pi (powers of 2). + - if a number has no suffix, \c M (megabyte) is assumed. + + - 0 means autogrow, and the file will be deleted afterwards. + +- \ : \c STXXL has a number of different file access implementations, choose one of them (recommended ones in bold): + + - \c **syscall** : use \c read and \c write system calls which perform disk transfers directly on user memory pages without superfluous copying (currently the fastest method) + + - \c **wincall** : on Windows, use direct calls to the Windows API. + + - \c **linuxaio** : on Linux, use direct syscalls to the native Linux AIO interface. \n + The Linux AIO interface has the advantage of keeping an asynchronous queue inside the kernel. Multiple I/O requests are submitted to the kernel at once, thus the kernel can sort then using its disk schedulers and also forward them to the actual disks as asynchronous operations using NCQ (native command queuing) or TCQ (tagged command queueing). + + - \c memory : keeps all data in RAM, for quicker testing + + - \c mmap : \c use \c mmap and \c munmap system calls + + - \c boostfd : access the file using a Boost file descriptor + + - \c fileperblock_syscall, \c fileperblock_mmap, \c fileperblock_boostfd : same as above, but take a single file per block, using full_disk_filename as file name prefix. Usually provide worse performance than the standard variants, but release freed blocks to the file system immediately. + + - \c simdisk : simulates timings of the IBM IC35L080AVVA07 disk, full_disk_filename must point to a file on a RAM disk partition with sufficient space + + - \c wbtl : library-based write-combining (good for writing small blocks onto SSDs), based on \c syscall + +- \ : additional options for file access implementation. Not all are available for every fileio method. The option order is unimportant. + + - \c **autogrow**, \c noautogrow, \c autogrow=[off/on] : enables automatic growth of the file beyond the specified capacity, enabled by default except if raw_device. + + - \c **direct**, \c nodirect, \c direct=[off/try/on] : disable buffering in system cache by passing O_DIRECT or similar flag to open. \n + This is \a recommended as it improves performance, however, not all filesystems support bypassing cache. With \c direct or \c direct=on, STXXL will fail without direct access. With \c nodirect or \c direct=off it is disabled. The default is \c direct=try , which first attempts to open with O_DIRECT and falls back to opening without if it fails. + + - \c **unlink** (or \c unlink_on_open) : unlink the file from the fs immediately after creation. \n + This is possible on Unix system, as the file descriptor is kept open. This method is \b preferred, because even in the case of a program segfault, the file data is cleaned up by the kernel. + + - \c **delete** (or \c delete_on_exit) : delete file \a after the STXXL program exists \n + This is the more conservative version of unlink, which also works on Windows. However, if the program crashes, the file is not deleted. + + - \c **raw_device** : fail if the opened path is not a raw block device. \n + This flag is not required, raw devices are automatically detected. + + - \c queue=# : assign the disk to a specific I/O request queue and thread. \n + Use this for multiple files that reside on the same physical disk. + + - \c devid=# : assign the disk entry a specific physical device id. \n + Usually you can just omit the devid=# option, since disks are enumerated automatically. In sorting and other prefetched operations, the physical device id is used to schedule block transfers from independent devices. Thus you should label files/disks on the same physical devices with the same devid. + + - \c queue_length=# : specify for linuxaio the desired queue inside the linux kernel using this option. + +Example: +\verbatim +disk=/data01/stxxl,500G,syscall unlink +disk=/data02/stxxl,300G,syscall unlink +\endverbatim + +On Windows, one usually uses different disk drives and \c wincall. +\verbatim +disk=c:\stxxl.tmp,700G,wincall delete +disk=d:\stxxl.tmp,200G,wincall delete +\endverbatim + +On Linux you can try to take advantage of NCQ + Kernel AIO queues: +\verbatim +disk=/data01/stxxl,500G,linuxaio unlink +disk=/data02/stxxl,300G,linuxaio unlink +\endverbatim + +\section install_config_filesystem Recommended: File System XFS or Raw Block Devices + +The library benefits from direct transfers from user memory to disk, which saves superfluous copies. We recommend to use the XFS file system, which gives good read and write performance for large files. Note that file creation speed of \c XFS is a bit slower, so that disk files should be precreated for optimal performance. + +If the filesystems only use is to store one large STXXL disk file, we also recommend to add the following options to the \c mkfs.xfs command to gain maximum performance: +\verbatim +$ mkfs.xfs -d agcount=1 -l size=512b +\endverbatim + +The following filesystems have been reported not to support direct I/O: \c tmpfs , \c glusterfs . By default, STXXL will first try to use direct I/O (\c O_DIRECT open flag). If that fails, it will print a warning and open the file without \c O_DIRECT. + +\note It is also possible to use raw disk devices with \c syscall. \n +Just use \c disk=/dev/sdb1 or similar. This will of course \b overwrite all data on the partitions! The I/O performance of raw disks is generally more stable and slightly higher than with file systems. +\code +disk=/dev/sdb1,0,syscall raw_device +\endcode +The \c raw_device flag is only for verification, STXXL will automatically detect raw block devices and also their size. + +\section install_config_logfiles Log Files + +STXXL produces two kinds of log files, a message and an error log. By setting the environment variables \c STXXLLOGFILE and \c STXXLERRLOGFILE, you can configure the location of these files. The default values are \c stxxl.log and \c stxxl.errlog, respectively. + +\section install_config_precreation Precreating External Memory Files + +In order to get the maximum performance one can precreate disk files described in the configuration file, before running STXXL applications. A precreation utility is included in the set of STXXL utilities in \c stxxl_tool. Run this utility for each disk you have defined in the disk configuration file: +\verbatim +$ stxxl_tool create_files +// for example: +$ stxxl_tool create_files 1GiB /data01/stxxl +\endverbatim + +\section install_config_user User-Supplied disk_config Structures + +With STXXL >= 1.4.0, the library can also be configured via the user application. + +All disk configuration is managed by the stxxl::config class, which contains a list of stxxl::disk_config objects. Each stxxl::disk_config object encapsulates one disk= lines from a config file, or one allocated disk. + +The disk configuration must be supplied to the STXXL library before any other function calls, because the stxxl::config object must be filled before any external memory blocks are allocated by stxxl::block_manager. + +\code +int main() +{ + // get uninitialized config singleton + stxxl::config * cfg = stxxl::config::get_instance(); + + // create a disk_config structure. + stxxl::disk_config disk1("/tmp/stxxl.tmp", 100 * 1024 * 1024, "syscall unlink"); + disk1.direct = stxxl::disk_config::DIRECT_ON; // force O_DIRECT + + // add disk to config + cfg->add_disk(disk1); + + // add another disk + cfg->add_disk( disk_config("disk=/tmp/stxxl-2.tmp, 10 GiB, syscall unlink") ); + + // ... add more disks + + // use STXXL library as usual ... +} +\endcode + +*/ diff --git a/third-party/MQF/ThirdParty/stxxl/doc/introduction.dox b/third-party/MQF/ThirdParty/stxxl/doc/introduction.dox new file mode 100644 index 0000000000..e233febb68 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/introduction.dox @@ -0,0 +1,84 @@ +/*************************************************************************** + * doc/introduction.dox + * + * Most of this is from the old TeX tutorial and papers. + * Edited 2013 by Timo Bingmann + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Roman Dementiev + * + * 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) + **************************************************************************/ + +/** \page introduction Introduction to External Memory + +\author Roman Dementiev, Lutz Kettner, Peter Sanders (2007) + +Massive data sets arise naturally in many domains. Spatial databases of geographic information systems like GoogleEarth and NASA’s World Wind store terabytes of geographically referenced information that includes the whole Earth. In computer graphics one has to visualize highly complex scenes using only a conventional workstation with limited memory \cite Farias2001. Billing systems of telecommunication companies evaluate terabytes of phone call log files \cite BillingLarge. One is interested in analyzing huge network instances like a web graph \cite Donato2006 or a phone call graph. Search engines like Google and Yahoo provide fast text search in their databases indexing billions of web pages. A precise simulation of the Earth’s climate needs to manipulate with petabytes of data \cite Moore2000. These examples are only a sample of numerous applications that have to process vast amounts of data. + +The internal memories of computers can keep only a small fraction of these large data sets. During the processing the applications need to access the external memory (e.g. hard disks) very frequently. One such access can be about 106 times slower than a main memory access. Therefore, the disk accesses (I/Os) become the main bottleneck. + +The data are stored on the magnetic surface of a hard disk that rotates 4200–15,000 times per minute. In order to read or write a designated track of data, the disk controller moves the read/write arm to the position of this track (seek latency). If only a part of the track is needed, there is an additional rotational delay. The total time for such a disk access is an average of 3–10 ms for modern disks. The latency depends on the size and rotational speed of the disk and can hardly be reduced because of the \a mechanical nature of hard disk technology. After placing the read/write arm, the data are streamed at a high speed which is limited only by the surface data density and the MB bandwidth of the I/O interface. This speed is called sustained throughput and achieves up to 80 MB/s nowadays. In order to amortize the high seek latency, one reads or writes the data in blocks. The block size is balanced when the seek latency is a fraction of the sustained transfer time for the block. Good results show blocks containing a full track. For older low-density disks of the early 90s the track capacities were about 16–64 kB. Nowadays, disk tracks have a capacity of several megabytes. + +Operating systems implement the virtual memory mechanism that extends the working space for applications, mapping an external memory file (page/swap file) to virtual addresses. This idea supports the Random Access Machine model \cite Neu45 in which a program has an infinitely large main memory. With virtual memory the application does not know where its data are located: in the main memory or in the swap file. This abstraction does not have large running time penalties for simple sequential access patterns: the operating system is even able to predict them and to load the data in ahead. For more complicated patterns these remedies are not useful and even counterproductive: the swap file is accessed very frequently; the executable code can be swapped out in favor of unnecessary data; the swap file is highly fragmented and thus many random I/O operations are needed even for scanning. + +\section introduction_io_model I/O-efficient Algorithms and Models + +The operating system cannot adapt to complicated access patterns of applications dealing with massive data sets. Therefore, there is a need for explicit handling of external memory accesses. The applications and their underlying algorithms and data structures should care about the pattern and the number of external memory accesses (I/Os) which they cause. + +Several simple models have been introduced for designing I/O-efficient algorithms and data structures (also called external memory algorithms and data structures). The most popular and realistic model is the Parallel disk model (PDM) of Vitter and Shriver \cite VitShr94both. In this model, I/Os are handled explicitly by the application. An I/O operation transfers a block of \a B consecutive elements from/to a disk to amortize the latency. The application tries to transfer \a D blocks between the main memory of size \a M bytes and \a D independent disks in one I/O step to improve bandwidth, see figure below. The input size is \a N bytes which is (much) larger than \a M. The main complexity metrics of an I/O-efficient algorithm in PDM are the number of I/O steps (main metric) and the number of operations executed by the CPU. If not I/O but a slow internal CPU processing is the limiting factor of the performance of an application, we call such behavior CPU-bound. + +The PDM has become the standard theoretical model for designing and analyzing I/O-efficient algorithms. For this model, the following matching upper and lower bounds for I/O complexity are known. Scanning a sequence of N items takes \f$ \mathrm{scan}(N) = \Theta(N / (DB)) \f$ I/Os. Sorting a sequence of \a N items takes \f$ \mathrm{sort}(N) = \Theta(N / (DB) \cdot \log_{M/B} (N/M)) \f$ I/Os. Online search among \a N items takes \f$ \mathrm{search}(N) = \Theta(\log_{DB} (N)) \f$ I/Os. + +\section introduction_memory_hierarchies Memory Hierarchies + +The PDM measures the transfers between the main memory and the hard disks, however, in modern architectures, the CPU does not access the main memory directly. There are a few levels of faster memory caches in-between (figure below): CPU registers, level one (L2), level two (L2) and even level three (L3) caches. The main memory is cheaper and slower than the caches. Cheap dynamic random access memory, used in the majority of computer systems, has an access latency up to 60 ns whereas L1 has a latency of less than a ns. However, for a streamed access a high bandwidth of several GB/s can be achieved. The discrepancy between the speed of CPUs and the latency of the lower hierarchy levels grows very quickly: the speed of processors is improved by about 55% yearly, the hard disk access latency only by 9% \cite Patterson2004. Therefore, the algorithms that are aware of the memory hierarchy will continue to benefit in the future and the development of such algorithms is an important trend in computer science. + +\image html pdm_small.png "Schemes of parallel disk model (left) and memory hierarchy (right)" + +The PDM model only describes a single level in the hierarchy. An algorithm tuned to make a minimum number of I/Os between two particular levels could be I/O-inefficient on other levels. The cache-oblivious model in \cite FLPR99 avoids this problem by not providing the knowledge of the block size \a B and main memory size \a M to the algorithm. The benefit of such an algorithm is that it is I/O-efficient on all levels of the memory hierarchy across many systems without fine tuning for any particular real machine parameters. Many basic algorithms and data structures have been designed for this model (\cite FLPR99, \cite ABDHBM02, \cite BDIW02, \cite BFMZ04). A drawback of cache-oblivious algorithms playing a role in practice is that they are only asymptotically I/O-optimal. The constants hidden in the O-notation of their I/O-complexity are significantly larger than the constants of the corresponding I/O-efficient PDM algorithms (on a particular memory hierarchy level). For instance, a tuned cache-oblivious funnel sort implementation \cite ChristianiThesis is 2.6–4.0 times slower than our I/O-efficient sorter from STXXL (see \ref design_algo_sorting) for out-of-memory inputs \cite Ajwani2007. A similar funnel sort implementation \cite BFV04 is up to two times slower than the I/O-efficient sorter from the TPIE library for large inputs. The reason for this is that these I/O-efficient sorters are highly optimized to minimize the number of transfers between the main memory and the hard disks where the imbalance in the access latency is the largest. Cache-oblivious implementations tend to lose on the inputs, exceeding the main memory size, because they do (a constant factor) more I/Os at the last level of memory hierarchy. In this paper, we concentrate on extremely large out-of-memory inputs, therefore, we will design and implement algorithms and data structures efficient in the PDM. + +\section introduction_algorithm_engineering Algorithm Engineering for Large Data Sets + +Theoretically, I/O-efficient algorithms and data structures have been developed for many problem domains: graph algorithms, string processing, computational geometry, etc. (see the surveys \cite MSS03, \cite Vit01). Some of them have been implemented: sorting, matrix multiplication (\cite TPIEscientific96), search trees (\cite ChiangPHD, \cite Bkdtree03, \cite DynRTrees99, \cite CRBtree03), priority queues (\cite Brengel00), text processing (\cite CraFer02). However, only few of the existing I/O-efficient algorithms have been studied experimentally. As new algorithmic results rely on previous ones, researchers, who would like to engineer practical implementations of their ideas and show the feasibility of external memory computation for the solved problem, need to invest much time in the careful design of unimplemented underlying external algorithms and data structures. Additionally, since I/O-efficient algorithms deal with hard disks, a good knowledge of low-level operating system issues is required when implementing details of I/O accesses and file system management. This delays the transfer of theoretical results into practical applications, which will have a tangible impact for industry. Therefore, one of the primary goals of algorithm engineering for large data sets is to create software frameworks and libraries that handle both the low-level I/O details efficiently and in an abstract way, and provide well-engineered and robust implementations of basic external memory algorithms and data structures. + +\section introduction_stl C++ Standard Template Library + +The Standard Template Library (STL) \cite stepanov94standard is a C++ library which is included in every C++ compiler distribution. It provides basic data structures (called containers) and algorithms. STL containers are generic and can store any built-in or user data type that supports some elementary operations (e.g. copying and assignment). STL algorithms are not bound to a particular container: an algorithm can be applied to any container that supports the operations required for this algorithm (e.g. random access to its elements). This flexibility significantly reduces the complexity of the library. + +STL is based on the C++ template mechanism. The flexibility is supported using compile-time polymorphism rather than the object-oriented run-time polymorphism. The run-time polymorphism is implemented in languages like C++ with the help of virtual functions that usually cannot be inlined by C++ compilers. This results in a high per-element penalty of calling a virtual function. In contrast, modern C++ compilers minimize the abstraction penalty of STL inlining many functions. + +STL containers include: \c std::vector (an unbounded array), \c std::priority queue, \c std::list, \c std::stack, \c std::deque, \c std::set, \c std::multiset (allows duplicate elements), \c std::map (allows mapping from one data item (a key) to another (a value)), \c std::multimap (allows duplicate keys), etc. Containers based on hashing (\c hash_set, \c hash_multiset, \c hash_map and \c hash_multimap) are not yet standardized and distributed as an STL extension. + +Iterators are an important part of the STL library. An iterator is a kind of handle used to access items stored in data structures. Iterators offer the following operations: read/write the value pointed by the iterator, move to the next/previous element in the container, move forward/backward (random access) by some number of elements. + +STL provides a large number of algorithms that perform scanning, searching, and sorting. The implementations accept iterators that possess a certain set of operations described above. Thus, the STL algorithms will work on any container with iterators following the requirements. To achieve flexibility, STL algorithms are parameterized with objects, overloading the function operator (operator()). Such objects are called \a functors. A functor can, for instance, define the sorting order for the STL sorting algorithm or keep the state information in functions passed to other functions. Since the type of the functor is a template parameter of an STL algorithm, the function operator does not need to be virtual and can easily be inlined by the compiler, thus avoiding the function call costs. + +The STL library is well accepted and its generic approach and principles are followed in other famous C++ libraries like Boost \cite karlsson2005beyond and CGAL \cite fabri1998design. + +\section introduction_goals The Goals of STXXL + +Several external memory software library projects (LEDA-SM \cite CraMeh99 and TPIE \cite tpie_manual) were started to reduce the gap between theory and practice in external memory computing. They offer frameworks that aim to speed up the process of implementing I/O-efficient algorithms, abstracting away the details of how I/O is performed. Those projects are excellent proofs of EM paradigm, but have some drawbacks which \b impede their practical use. + +Therefore we started to develop STXXL library, which tries to avoid those obstacles. The objectives of STXXL project (distinguishing it from other libraries): + +- Offer \b transparent support of parallel disks. This feature although announced has not been implemented in any library. + +- Implement \b parallel disk algorithms. LEDA-SM and TPIE libraries offer only implementations of single disk EM algorithms. + +- Make the library able to handle problems of real world size (up to dozens of terabytes). + +- Improved utilization of computer resources. STXXL explicitly supports \b overlapping between I/O and computation. STXXL implementations of external memory algorithms and data structures benefit from the overlapping of I/O and computation. + +- STXXL achieves small constant factors in I/O volume. In particular, \b "pipelining" can save more than \b half the number of I/Os performed by many algorithms. + +- Care about the internal work, improve the in-memory algorithms. Having many disks can hide the latency and increase the I/O bandwidth, s.t. internal work becomes a bottleneck. + +- Care about operating system overheads. Use unbuffered disk access to avoid superfluous copying of data. + +- Short development times due to well-known STL-compatible interfaces for external memory algorithms and data structures. STL algorithms can be directly applied to STXXL containers (code reuse); moreover, the I/O complexity of the algorithms remains optimal in most cases. + +*/ diff --git a/third-party/MQF/ThirdParty/stxxl/doc/mainpage.dox b/third-party/MQF/ThirdParty/stxxl/doc/mainpage.dox new file mode 100644 index 0000000000..33851ed2ba --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/mainpage.dox @@ -0,0 +1,153 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/mainpage.dox + * + * Main page of STXXL doxygen tree. All doc pages should be linked here. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +/** \mainpage Welcome to STXXL + +The core of STXXL is an implementation of the C++ standard template library STL for external memory (out-of-core) computations, i.e., STXXL implements containers and algorithms that can process huge volumes of data that only fit on disks. While the compatibility to the STL supports ease of use and compatibility with existing applications, another design priority is high performance. Here is a selection of STXXL performance features: + +- transparent support of multiple disks +- variable block lengths +- overlapping of I/O and computation +- prevention of OS file buffering overhead +- algorithm pipelining +- utilization of multiple processor cores for internal computation + +See the \subpage introduction "introduction to external memory" for a longer description and our vision. + +# Getting Started: Building and Tutorial + +This section will help you if you are using the STXXL for the first time. + +First you must compile the library. Pick one of the following \subpage install "build instructions", depending on your host system: + +- \ref install_unix +- \ref install_windows + +Once compiled, you can read the following simple tutorials on how to use STXXL containers and algorithms: + +- \ref tutorial_vector +- \ref tutorial_stack +- \ref tutorial_pqueue + +See the corresponding page for a \subpage tutorial "complete list of all Tutorials and Examples". + +# Design and More Information + +We have collected much documentation about the design of STXXL. Even more information is available as academic research papers, technical reports and theses. + +- \subpage design "Design of STXXL concepts, containers and algorithms" + +If you plan to contribute code to STXXL, please read the \subpage coding_style and use the \subpage common. + +# FAQ, Troubleshooting, Bugs and More + +- \subpage faq + +- Questions concerning use and development of the STXXL library should be posted to the FORUMS. Please search the forum before posting, your question may have been answered before. + +- See \ref faq_compilers when compilation fails. + +- Bugs and pull requests can be reported via Github: http://github.com/stxxl/stxxl. + +- Check the \ref changelog for recent changes when switching version. + +- The STXXL source also contains \subpage stxxl_tool "stxxl_tool", a collection of simple tools and benchmarks. + +# License and Authors + +\c STXXL is distributed under the Boost Software License, Version 1.0. \n +You can find a copy of the license in the accompanying file \ref license or at http://www.boost.org/LICENSE_1_0.txt. \n +Many people have contributed to STXXL, see all \ref authors. +\subpage textfiles " " + +*/ + +/** \page textfiles Additional Text Files + +- \subpage readme +- \subpage changelog +- \subpage authors +- \subpage textfiles_install +- \subpage license +- \subpage textfiles_todo + +\page readme README + +\verbinclude README + +\page changelog ChangeLog + +\verbinclude CHANGELOG + +\page authors AUTHORS + +The following list of authors have contributed to STXXL: + +\verbinclude AUTHORS + +\page textfiles_install INSTALL + +\verbinclude INSTALL + +\page license LICENSE_1_0.txt + +\verbinclude LICENSE_1_0.txt + +\page textfiles_todo TODO + +\verbinclude TODO + + */ + +// Module Groups are defined here to fix their order: + +/*! \defgroup stllayer STL-User Layer + Layer which groups STL compatible algorithms and containers +*/ + +/*! \defgroup streampack Stream Package + + Package that enables pipelining of consequent sorts and scans of the external data avoiding the saving the intermediate results on the disk, e.g. the output of a sort can be directly fed into a scan procedure without the need to save it on a disk. All components of the package are contained in the \c stxxl::stream namespace. + + STREAM ALGORITHM CONCEPT (Do not confuse with C++ input/output streams) + +\verbatim + + struct stream_algorithm // stream, pipe, whatever + { + typedef some_type value_type; + + const value_type & operator * () const; // return current element of the stream + stream_algorithm & operator ++ (); // go to next element. precondition: empty() == false + bool empty() const; // return true if end of stream is reached + + }; +\endverbatim +*/ + +/*! \defgroup mnglayer Block Management Layer + + Group of classes which help controlling external memory space, managing disks, and allocating and deallocating blocks of external storage. +*/ + +/*! \defgroup iolayer I/O Primitives Layer + + Group of classes which enable abstraction from operating system calls and support system-independent interfaces for asynchronous I/O. +*/ + +/*! \defgroup support Common Utilities and Support Classes + +Supporting classes also useful for applications, see also \ref common . +*/ diff --git a/third-party/MQF/ThirdParty/stxxl/doc/references.bib b/third-party/MQF/ThirdParty/stxxl/doc/references.bib new file mode 100644 index 0000000000..9b498dc973 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/references.bib @@ -0,0 +1,533 @@ +% -*- fill-column: 1000 -*- + +@InProceedings{APV02, + author = "L. Arge and O. Procopiuc and J. S. Vitter", + title = "{Implementing I/O-efficient Data Structures Using TPIE}", + booktitle = "10th European Symposium on Algorithms (ESA)", + pages = "88--100", + year = 2002, + volume = 2461, + series = "LNCS", + publisher = "Springer" +} + +@InProceedings{CraMeh99, + author = "A. Crauser and K. Mehlhorn", + title = "{LEDA-SM}, extending {LEDA} to Secondary Memory", + booktitle = "3rd International Workshop on Algorithmic Engineering (WAE)", + pages = "228--242", + year = 1999, + volume = 1668, + series = "LNCS" +} + +@Article{VitShr94both, + author = "J. S. Vitter and E. A. M. Shriver", + title = "Algorithms for parallel memory, {I/II}", + journal = "Algorithmica", + year = 1994, + volume = 12, + number = "2/3", + pages = "110--169" +} + +@InProceedings{HutSanVit01b, + author = "D. A. Hutchinson and P. Sanders and J. S. Vitter", + title = "Duality Between Prefetching and Queued Writing with Parallel Disks", + booktitle = "9th European Symposium on Algorithms (ESA)", + year = 2001, + number = 2161, + pages = "62--73", + series = "LNCS", + publisher = "Springer", +} + +@InProceedings{DemSan03, + author = "R. Dementiev and P. Sanders", + title = "Asynchronous Parallel Disk Sorting", + booktitle = "15th ACM Symposium on Parallelism in Algorithms and Architectures", + pages = "138--148", + year = 2003, + address = "San Diego", +} + +@Manual{tpie_manual, + author = "Lars Arge and Rakesh Barve and David Hutchinson and Octavian Procopiuc and Laura Toma and Darren Erik Vengroff and Rajiv Wickeremesinghe", + title = "{TPIE}: User manual and reference", + year = 2003, + month = nov +} + +@InProceedings{DKMS05, + author = "R. Dementiev and J. Mehnert and J. K{\"a}rkk{\"a}inen and P. Sanders", + title = "{Better External Memory Suffix Array Construction}", + booktitle = "Workshop on Algorithm Engineering {\&} Experiments", + year = 2005, + address = "Vancouver", + note = "\url{http://i10www.ira.uka.de/dementiev/files/DKMS05.pdf}" +} + +@InProceedings{VitHut01, + author = "J. S. Vitter and D. A. Hutchinson", + title = "Distribution sort with randomized cycling", + booktitle = "12th ACM-SIAM Symposium on Discrete Algorithms", + pages = "77--86", + year = 2001 +} + +@Article{BarGroVit97, + author = "R. D. Barve and E. F. Grove and J. S. Vitter", + title = "Simple Randomized Mergesort on Parallel Disks", + journal = "Parallel Computing", + year = 1997, + volume = 23, + number = 4, + pages = "601--631", + annote = "auch TR CS-1996-15, Duke" +} + +@Book{BicShaw2003, + author = {L.F. Bic and A.C. Shaw}, + title = {Operating Systems Principles}, + publisher = {Pearson Education}, + year = {2003}, +} + +@PhdThesis{ZehPhd, + author = {Norbert Ralf Zeh}, + title = {I/O Efficient Algorithms for Shortest Path Related Problems}, + school = {Carleton University, Ottawa}, + year = {2002}, + month = apr, +} + +@InProceedings{ChiEtAl95, + author = "Y.-J. Chiang and M. T. Goodrich and E. F. Grove and R. Tamasia and D. E. Vengroff and J. S. Vitter", + title = "External memory graph algorithms", + pages = "139--149", + booktitle = "6th Annual {ACM}-{SIAM} Symposium on Discrete Algorithms", + year = 1995 +} + +@Article{San00b, + author = "Peter Sanders", + title = "Fast Priority Queues for Cached Memory", + journal = "ACM Journal of Experimental Algorithmics", + volume = 5, + year = 2000, +} + +@Book{MSS03, + editor = "U. Meyer and P. Sanders and J. Sibeyn", + title = "Algorithms for Memory Hierarchies", + publisher = "Springer", + year = 2003, + volume = 2625, + series = "LNCS Tutorial" +} + +@InProceedings{Arg95, + author = "L. Arge", + title = "{The Buffer Tree: A New Technique for Optimal I/O-Algorithms}", + number = 955, + series = "LNCS", + pages = "334--345", + booktitle = "4th Workshop on Algorithms and Data Structures", + year = 1995, + publisher = "Springer" +} + +@Book{Knu98, + author = "D. E. Knuth", + title = "The Art of Computer Programming---Sorting and Searching", + publisher = "Addison Wesley", + year = 1998, + edition = "2nd", + volume = 3, + annote = "leftist trees, looser trees" +} + +@Article{Brengel00, + author = {Klaus Brengel and Andreas Crauser and Paolo Ferragina and Ulrich Meyer}, + title = {An Experimental Study of Priority Queues in External Memory}, + journal = {ACM Journal of Experimental Algorithms}, + year = {2000}, + volume = {5}, + number = {17}, +} + +@InProceedings{FJKT97, + author = "R. Fadel and K. V. Jakobsen and J. Katajainen and J. Teuhola", + title = "External heaps combined with effective buffering", + volume = "19-2", + series = "Australian Computer Science Communications", + pages = "72--78", + booktitle = "4th Australasian Theory Symposium", + year = 1997, + publisher = "Springer" +} + +@Article{BM72, + author = {R. Bayer and E. McCreight}, + title = {Organization and maintenance of large ordered indices}, + journal = {Acta Informatica}, + year = {1972}, + pages = {173–189}, +} + +@InProceedings{BDIW02, + author = {M. Bander and Z. Duan and J. Iacono and J. Wu}, + title = {A locality-preserving cache-oblivious dynamic dictionary}, + booktitle = {13th Annual ACM-SIAM Symposium On Descrete Algorithms (SODA'02)}, + year = {2002}, +} + +@InProceedings{BDB99, + author = {Michael A. Olson and Keith Bostic and Margo Seltzer }, + title = "{Berkeley DB}", + booktitle = "{USENIX Annual Technical Conference}", + pages = {183–192}, + year = {1999}, + month = {June}, +} + +@InProceedings{KalVar01, + author = {M. Kallahalla and P. J. Varman}, + title = {Optimal prefetching and caching for parallel {I/O} systems}, + booktitle = "13th Symposium on Parallel Algorithms and Architectures", + pages = "219--228", + year = 2001, +} + +@InProceedings{Raj98, + author = "S. Rajasekaran", + title = "A Framework for Simple Sorting Algorithms on Parallel Disk Systems", + booktitle = "10th {ACM} Symposium on Parallel Algorithms and Architectures", + pages = "88--98", + year = 1998 +} + +@InProceedings{ChaCor02, + author = "G. Chaudhry and T. H. Cormen", + title = "Getting More From Out-of-Core Columnsort", + booktitle = "4th Workshop on Algorithm Engineering and Experiments (ALENEX)", + pages = "143--154", + year = 2002, + number = 2409, + series = "LNCS" +} + +@InProceedings{ChaCorWis01, + author = "G. Chaudhry and T. H. Cormen and L. F. Wisniewski", + title = "Columnsort Lives! An Efficient Out-of-Core Sorting Program", + booktitle = "13th {ACM} Symposium on Parallel Algorithms and Architectures", + year = 2001, + pages = "169--178", +} + +@InProceedings{PaiVar92, + author = "V. S. Pai and P. J. Varman", + title = "Prefetching with Multiple Disks for External Mergesort: Simulation and Analysis", + booktitle = "ICDE", + pages = "273--282", + year = 1992 +} + +@Article{CaoFelKarLi96, + author = "P. Cao and E. W. Felten and A. R. Karlin and K. Li", + title = "Implementation and Performance of Integrated Application-Controlled File Caching, Prefetching and Disk Scheduling", + journal = "ACM Transactions on Computer Systems", + year = 1996, + month = Nov, + volume = "14", + number = 4, + pages = "311--343" +} + +@InProceedings{AlbGarLeo98, + author = "S. Albers and N. Garg and S. Leonardi", + title = "Minimizing Stall Time in Single and Parallel Disk Systems", + pages = "454--462", + ISBN = "0-89791-962-9", + booktitle = "Proceedings of the 30th Annual {ACM} Symposium on Theory of Computing ({STOC}'98)", + month = may # "~23--26", + publisher = "ACM Press", + address = "New York", + year = "1998", +} + +@Article{KimKar00, + author = "Tracy Kimbrel and Anna R. Karlin", + title = "Near-optimal Parallel Prefetching and Caching", + journal = "SIAM Journal on Computing", + year = "2000", + volume = "29", + number = 4, + pages = "1051--1082" +} + +@InProceedings{NBCGL94, + author = "C. Nyberg and T. Barclay and Z. Cvetanovic and J. Gray and D. Lomet", + title = "{AlphaSort}: A {RISC} Machine Sort", + booktitle = "SIGMOD", + pages = "233--242", + year = 1994, +} + +@InProceedings{Aga96, + author = "R. Agarwal", + title = "A super scalar sort algorithm for {RISC} processors", + booktitle = "{ACM SIGMOD} International Conference on Management of Data", + pages = "240--246", + year = 1996, + annote = "msd radix sort mentions TLB and branches" +} + +@Misc{NKG00, + author = "C. Nyberg and C. Koester and J. Gray", + title = "Nsort: A Parallel Sorting Program for {NUMA} and {SMP} Machines", + year = 2000, + note = "\url{http://www.ordinal.com/lit.html}" +} + +@Misc{Wyl99, + author = "J. Wyllie", + title = "{SPsort}: How to Sort a Terabyte Quickly", + year = 1999, + howpublished = {\url{http://research.microsoft.com/barc/SortBenchmark/SPsort.pdf}} +} + +@Article{AggVit88, + author = "A. Aggarwal and J. S. Vitter", + title = "The Input/Output Complexity of Sorting and Related Problems", + journal = "Communications of the ACM", + year = 1988, + volume = 31, + number = 9, + pages = "1116--1127", +} + +@MastersThesis{JensThesis, + author = {Jens Mehnert}, + title = "{External Memory Suffix Array Construction}", + school = {University of Saarland, Germany}, + year = {2004}, + month = {November}, + note = "\url{http://algo2.iti.uka.de/dementiev/esuffix/docu/data/diplom.pdf}", +} + +@Book{SKS01, + author = "A. Silberschatz and H. F. Korth and S. Sudarshan", + title = "Database System Concepts", + publisher = "McGraw-Hill", + year = 2001, + edition = "4th" +} + +@InBook{BillingLarge, + author = {Andrew Hume}, + title = {Handbook of massive data sets}, + chapter = {Billing in the large}, + publisher = {Kluwer Academic Publishers}, + year = {2002}, + pages = {895--909} +} + +@article{Farias2001, + title = {Out-of-core rendering of large, unstructured grids}, + volume = {21}, + issn = {0272-1716}, + doi = {10.1109/38.933523}, + number = {4}, + author = {Farias, R. and Silva, {C.T.}}, + year = {2001}, + pages = {42--50}, +} + +@Misc{Moore2000, + author = {R. W. Moore}, + title = {Enabling Petabyte Computing}, + howpublished = {\url{http://www.nap.edu/html/whitepapers/ch-48.html}}, + year = {2000} +} + +@TechReport{Neu45, + author = "Neumann, J. von", + title = "First Draft of a Report on the {EDVAC}", + institution = "University of Pennsylvania", + year = "1945", + key = "computers-history", + note = "{\url{http://www.histech.rwth-aachen.de/www/quellen/vnedvac.pdf}}", + annote = "{\it ~\\ The report that got von Neumann's name associated with the serial, stored-program, general purpose, digital architecture upon which 99.99\% of all computers today are based.}", +} + +@article{Donato2006, + title = {Algorithms and Experiments for the Webgraph.}, + volume = {10}, + number = {2}, + author = {Donato, Debora and Laura, Luigi and Leonardi, Stefano and Meyer, Ulrich and Millozzi, Stefano and Sibeyn, Jop F.}, + year = {2006}, + pages = {219–-236}, +}, + +@article{Patterson2004, + title = {Latency lags bandwith}, + volume = {47}, + number = {10}, + author = {Patterson, David A.}, + year = {2004}, + pages = {71–-75}, +} + +@InProceedings{FLPR99, + author = "M. Frigo and C. E. Leiserson and H. Prokop and S. Ramachandran", + title = "Cache-Oblivious Algorithms", + booktitle = "40th Symposium on Foundations of Computer Science", + year = 1999, + pages = "285--298" +} + +@inproceedings{ABDHBM02, + address = {New York, {NY}, {USA}}, + series = {{STOC} '02}, + title = {Cache-oblivious priority queue and graph algorithm applications}, + isbn = {1-58113-495-9}, + doi = {10.1145/509907.509950}, + booktitle = {Proceedings of the thiry-fourth annual {ACM} symposium on Theory of computing}, + publisher = {{ACM}}, + author = {Arge, Lars and Bender, Michael A. and Demaine, Erik D. and Holland-Minkley, Bryan and Munro, J. Ian}, + year = {2002}, + pages = {268–276}, +} + +@incollection{BFMZ04, + series = {Lecture Notes in Computer Science}, + title = {Cache-Oblivious Data Structures and Algorithms for Undirected Breadth-First Search and Shortest Paths}, + copyright = {2004 Springer-Verlag Berlin Heidelberg}, + isbn = {978-3-540-22339-9, 978-3-540-27810-8}, + number = {3111}, + booktitle = {Algorithm Theory - {SWAT} 2004}, + publisher = {Springer Berlin Heidelberg}, + author = {Brodal, Gerth Stølting and Fagerberg, Rolf and Meyer, Ulrich and Zeh, Norbert}, + month = jan, + year = {2004}, + pages = {480--492}, +} + +@MastersThesis{ChristianiThesis, + title = {Cache-oblivious Graph Algorithms}, + year = 2005, + author = {Christiani, Frederik Juul}, + journal = {Master's thesis, Department of Mathematics and Computer Science (IMADA), University of Southern Denmark, Odense} +} + +@inproceedings{Ajwani2007, + title = {Improved external memory {BFS} implementation}, + urldate = {2013-02-14}, + booktitle = {Proceedings of the Workshop on Algorithm Engineering and Experiments}, + author = {Ajwani, Deepak and Meyer, Ulrich and Osipov, Vitaly}, + year = {2007}, + pages = {3–12}, + file = {alx07_001ajwanid.pdf:files/3066/alx07_001ajwanid.pdf:application/pdf} +} + +@InProceedings{BFV04, + author = "G. S. Brodal and R. Fagerberg and K. Vinther", + title = "Engineering a Cache-Oblivious Sorting Algorithm", + booktitle = "6th Workshop on Algorithm Engineering and Experiments", + year = 2004 +} + +@Article{Vit01, + author = "J. S. Vitter", + title = "External memory algorithms and data structures: {D}ealing with {MASSIVE} data", + journal = "ACM Computing Surveys", + volume = "33", + number = "2", + pages = "209--271", + year = "2001", +} + +@InProceedings{TPIEscientific96, + author = {D. E. Vengroff and J. S. Vitter}, + title = "{I/O-Efficient Scientific Computation using TPIE}", + booktitle = {Goddard Conference on Mass Storage Systems and Technologies}, + pages = {553--570}, + year = {1996}, + volume = {2}, + note = {published in NASA Conference Publication 3340}, +} + +@PhdThesis{ChiangPHD, + author = {Y.-J. Chiang}, + title = {Dynamic and I/O-Efficient Algorithms for Computational Geometry and Graph Algorithms}, + school = {Brown University}, + year = {1995} +} + +@InProceedings{Bkdtree03, + author = {O. Procopiuc and P. K. Agarwal and L. Arge and J. S. Vitter}, + title = "{Bkd-tree: A Dynamic Scalable KD-Tree}", + booktitle = {Proc. 8th Int'l Symposium on Spatial and Temporal Databases (SSTD '03)}, + pages = {46-65} +} + +@InProceedings{DynRTrees99, + author = {L. Arge and K. H. Hinrichs and J. Vahrenhold and J. S. Vitter}, + title = "{Efficient Bulk Operations on Dynamic R-trees}", + booktitle = {1st Workshop on Algorithm Engineering and Experimentation (ALENEX '99)}, + pages = {328-348}, + year = {1999}, + series = {Lecture Notes in Computer Science}, + publisher = {Springer-Verlag}, +} + +@InProceedings{CRBtree03, + author = {P. K. Agarwal and L. Arge and S. Govindarajan}, + title = "{CRB-Tree: An Efficient Indexing Scheme for Range Aggregate Queries}", + booktitle = {Proc. 9th Int'l Conference on Database Theory (ICDT '03)}, + pages = {143-157}, + year = {2003}, +} + +@Article{CraFer02, + author = "A. Crauser and P. Ferragina", + title = "A Theoretical and Experimental Study on the Construction of Suffix Arrays in External Memory", + journal = "Algorithmica", + year = 2002, + volume = 32, + pages = "1-35", + number = 1 +} + +@techreport{stepanov94standard, + author = "A. A. Stepanov and M. Lee", + title = "{The Standard Template Library}", + number = "X3J16/94-0095, WG21/N0482", + year = "1994", + institution = "Silicon Graphics Inc., Hewlett Packard Laboratories" +} + +@book{karlsson2005beyond, + title = {Beyond the C++ standard library: an introduction to Boost}, + shorttitle = {Beyond the C++ standard library}, + publisher = {Pearson Education}, + author = {Karlsson, Björn}, + year = {2005} +} + +@article{fabri1998design, + title = {On the design of {CGAL}, the computational geometry algorithms library}, + author = {Fabri, Andreas and Giezeman, Geert-Jan and Kettner, Lutz and Schirra, Stefan and Schönherr, Sven}, + year = {1998}, +} + +@InProceedings{Vengroff94, + author = {D. E. Vengroff}, + title = "{A Transparent Parallel I/O Environment}", + booktitle = {Third DAGS Symposium on Parallel Computation}, + pages = {117-134}, + year = {1994}, + address = {Hanover, NH}, + month = {July}, +} diff --git a/third-party/MQF/ThirdParty/stxxl/doc/stxxl_tool.dox b/third-party/MQF/ThirdParty/stxxl/doc/stxxl_tool.dox new file mode 100644 index 0000000000..657bd4b52c --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/stxxl_tool.dox @@ -0,0 +1,121 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/stxxl_tool.dox + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page stxxl_tool stxxl_tool Collection + +\authors Timo Bingmann (2013) + +The \c stxxl_tool accompanies the source code and also binary distributions of STXXL. It contains multiple subprograms that can be used for benchmarking basic functionality of the library. We describe only some of the benchmarks and functions in this document. + +\c stxxl_tool has a verbose command line help and descriptions of all subtools are available there. Try +\verbatim +$ stxxl_tool +(prints all available subtools) +$ stxxl_tool --help +\endverbatim + + +\section create_files + +To pre-create files one can issue a command similar to: +\verbatim +$ stxxl_tool create_files 10gib /data01/stxxl.tmp +\endverbatim + + +\section benchmark_disks Benchmark Disk Bandwidth + +Maybe the most important tool for testing the I/O performance of an experimental platform is stxxl_tool benchmark_disks. + +This subtool will read the disk configuration file (.stxxl) and open all parallel disk files. Then the specified amount of data is written and read on the configured disks using the block manager and file I/O layers. The transfer speed is outputted for every batch for processed blocks. + +An example run looks as follows: +\verbatim +$ stxxl_tool benchmark_disks 10gib -b 4 +Parameter size set to 10737418240. +Option -b, --batch set to 4. +[STXXL-MSG] STXXL v1.4.0 (prerelease) +[STXXL-MSG] Disk '/data01/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall +[STXXL-MSG] Disk '/data02/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall +[STXXL-MSG] In total 2 disks are allocated, space: 457763 MiB +# Batch size: 33554432 (32.000 MiB) (4 blocks of 8388608 (8.000 MiB) ) using randomized cycling striping +Offset 0 MiB: 170.6 MiB/s write, 108.9 MiB/s read +Offset 32 MiB: 164.6 MiB/s write, 108.4 MiB/s read +Offset 64 MiB: 158.6 MiB/s write, 102.0 MiB/s read +Offset 96 MiB: 143.8 MiB/s write, 116.3 MiB/s read +Offset 128 MiB: 156.3 MiB/s write, 130.7 MiB/s read +Offset 160 MiB: 168.3 MiB/s write, 99.3 MiB/s read +Offset 192 MiB: 156.6 MiB/s write, 109.2 MiB/s read +Offset 224 MiB: 159.9 MiB/s write, 107.7 MiB/s read +Offset 256 MiB: 156.0 MiB/s write, 88.7 MiB/s read +Offset 288 MiB: 149.6 MiB/s write, 109.7 MiB/s read +Offset 320 MiB: 138.3 MiB/s write, 113.2 MiB/s read +[...] +\endverbatim + + +\section benchmark_sort Benchmark Sorting Methods + +The second performance metric of an experimental platform is how fast STXXL can sort on it. This is measured by the stxxl_tool benchmark_sort subtool. + +This subtool performs stxxl::sort, stxxl::ksort and stream::sort on uint32 pairs, uint64 pairs and a larger struct of 64 bytes. + +An example run looks as follows: +\verbatim +$ stxxl_tool benchmark_sort 20gib -M 1gib +Parameter size set to 21474836480. +Option -M, --ram set to 1073741824. +[STXXL-MSG] STXXL v1.4.0 (prerelease) + gnu parallel(20120301) +[STXXL-MSG] Disk '/dev/sdb1' is allocated, space: 915527 MiB, I/O implementation: syscall +[STXXL-MSG] Disk '/dev/sdc1' is allocated, space: 915527 MiB, I/O implementation: syscall +[STXXL-MSG] Disk '/dev/sdd1' is allocated, space: 915527 MiB, I/O implementation: syscall +[STXXL-MSG] Disk '/dev/sde1' is allocated, space: 915527 MiB, I/O implementation: syscall +[STXXL-MSG] In total 4 disks are allocated, space: 3662109 MiB +#!!! running sorting test with pair of uint32 = 8 bytes. +# materialize random_stream into vector of size 2684354560 +finished in 46.9271 seconds @ 436.421 MiB/s +# stxxl::sort vector of size 2684354560 +finished in 256.048 seconds @ 79.9849 MiB/s +# stxxl::ksort vector of size 2684354560 +finished in 356.964 seconds @ 57.3727 MiB/s +# stxxl::stream::sort of size 2684354560 +finished in 213.102 seconds @ 96.1041 MiB/s + +#!!! running sorting test with pair of uint64 = 16 bytes. +# materialize random_stream into vector of size 1342177280 +finished in 95.5571 seconds @ 214.322 MiB/s +# stxxl::sort vector of size 1342177280 +finished in 222.727 seconds @ 91.9513 MiB/s +# stxxl::ksort vector of size 1342177280 +finished in 265.456 seconds @ 77.1502 MiB/s +# stxxl::stream::sort of size 1342177280 +finished in 200.357 seconds @ 102.218 MiB/s + +#!!! running sorting test with struct of 64 bytes = 64 bytes. +# materialize random_stream into vector of size 335544320 +finished in 46.9293 seconds @ 436.401 MiB/s +# stxxl::sort vector of size 335544320 +finished in 215.798 seconds @ 94.9035 MiB/s +# stxxl::ksort vector of size 335544320 +finished in 222.5 seconds @ 92.0451 MiB/s +# stxxl::stream::sort of size 335544320 +finished in 112.607 seconds @ 181.871 MiB/s +\endverbatim + +As stxxl::sort and stxxl::ksort perform about 4 read/write steps on the data, the sorting speed is about 1/4 of the scanning speed. On the other hand, stream::sort performs only 2 read/write steps to create a sorted stream from an unsorted one. Thus the stream sorting speed is about 1/2 of scanning speed. + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial.dox new file mode 100644 index 0000000000..ffd747cba6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial.dox @@ -0,0 +1,74 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial Tutorials and Examples + +\authors Timo Bingmann, Daniel Feist (2013) + +This tutorial on the Standard Templete Library for Very Large Datasets (STXXL) is meant as an introduction to all those who use the STXXL for the first time - just basic knowledge in C++ is required. Note that the focus of this tutorial is on the most importatant features and is by no means exhaustive. If necessary, we refer to more advanced functionality which would go however beyond the scope of this introduction. + +Why do i need the STXXL? Imagine a situation where you want to work with a std::vector which does not fit into the internal memory completely. That is precisely the situation where the STXXL container +stxxl::vector solves the issue. The practical part about it: Stxxl containers work in a similar way as their internal counterparts from the Standard Template Library (STL) so that parts of this tutorial may +sound familiar to you. Naturally, the vector container is just an example - the same applies to other data structures. + +Before running the STXXL for the first time you may check \ref install_unix. + +# Available tutorials on STXXL Containers + +This section introduces in all currently available STXXL containers. +The practical part about it: STXXL containers which have an internal counterparts in the Standard Template Library (STL) work similar so that parts of this tutorial may sound familiar to you: + +- \subpage tutorial_vector "stxxl::vector tutorial" + - \subpage tutorial_vector_billing + - \subpage tutorial_vector_buf +- \subpage tutorial_stack "stxxl::stack tutorial" +- \subpage tutorial_queue "stxxl::queue tutorial" +- \subpage tutorial_deque "stxxl::deque tutorial" +- \subpage tutorial_map "stxxl::map tutorial" +- \subpage tutorial_unordered_map "stxxl::unordered_map tutorial" + +Beyond these, STXXL also provides a set of containers that are not part of the STL: + +- \subpage tutorial_pqueue "stxxl::priority_queue tutorial" +- \subpage tutorial_matrix "stxxl::matrix tutorial" +- \subpage tutorial_sorter "stxxl::sorter tutorial" +- \subpage tutorial_sequence "stxxl::sequence tutorial" + +Furthermore, the stream pipeline component of STXXL is described in the following tutorials: + +- \subpage tutorial_stream +- \subpage tutorial_stream_edgesort + +For more information on the structure, the internal design and time / space complexity of the provided operations, see \ref design_stl. + +# More Examples and Real Applications + +The \c examples subdirectory of the STXXL tarballs contains more example code than those of the tutorials above. These are less documented but still contain useful code snippets and usage information. + +- The \c examples/applications directory is a collection of real external memory algorithms computing non-trivial output. We welcome contributions of interesting applications to this collection, currently included are: + + - the DC3/skew3 suffix sorting algorithm \ref examples/applications/skew3.cpp + +- There is a collection of simple tools which copy and sort files containing integers or structs: \ref examples/containers/copy_file.cpp "copy_file.cpp", \ref examples/algo/sort_file.cpp "sort_file.cpp" and \ref examples/algo/copy_and_sort_file.cpp "copy_and_sort_file.cpp". + +\example examples/applications/skew3.cpp +\example examples/algo/sort_file.cpp +\example examples/algo/copy_and_sort_file.cpp + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_deque.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_deque.dox new file mode 100644 index 0000000000..40f05610b3 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_deque.dox @@ -0,0 +1,120 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_deque.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_deque STXXL Deque + +This page introduces into the stxxl::deque container (to learn more about the structure of stxxl::deque, see section \ref design_deque). + +The acronym Deque stands for "double-ended-queue", that means elements can be accessed, inserted and deleted on both ends of the data structure - in contrast to the stxxl::queue (see \ref tutorial_queue), +where that's only possible on one of the two endings. + + +### Creating a STXXL deque + +To create a stxxl::deque object, only the data value type must be specified: + +\code +typedef stxxl::deque deque; +deque my_deque; +\endcode + +See \ref stxxl::deque for additional template parameter details. + +### Insert elements + +Inserting elements is possible at the start (by calling the push_front() function) as well as the end (by calling the push_back() function) of the deque. + +\code +my_deque.push_front(2); +my_deque.push_front(11); + +my_deque.push_back(5); +my_deque.push_back(8); + +// deque now stores: |11|2|5|8| +\endcode + + +### Access elements + +To return a reference to the element at the start of the deque, call front(), to return a reference to the elemtent at the end of the deque, call back() on a deque instance. + +\code +std::cout << "return 'first' element: " << my_deque.front() << std::endl; // prints 11 +std::cout << "return 'last' element: " << my_deque.back() << std::endl; // prints 8 +\endcode + +Accessing elements at random is supported by the STXXL deque with the []-operator like the following. + +\code +std::cout << "random access: " << my_deque[2] << std::endl; // prints 5 +\endcode + +The operations described in this sections are not I/O-efficient as they come with \f$\mathcal{O}(1)\f$ time per I/O-access. To achieve I/O-efficient scanning, the STXXL deque provides different iterators. +The simplest iterator can be used as follows: + +\code +// create forward-iterator (which advances from start to end) +stxxl::deque_iterator deque_iterator = my_deque.begin(); +// access item at current iterator position +std::cout << *deque_iterator << std::endl; +// move up one step by preincrement +++deque_iterator; +\endcode + +### Delete elements + +Deleting elements is possible at both endings of the deque by using pop_front() and pop_back(): + +\code +my_deque.pop_front(); // deque now stores: |2|5|8| +my_deque.pop_back(); // deque now stores: |2|5| +\endcode + + +### Determine size / Check whether the deque is empty + +To determine the size (i.e. the number of elements) of an instance, call size(): + +\code +std::cout << "deque stores: " << my_deque.size() << " elements" << std::endl; +\endcode + +To check if the deque is empty, call empty() which returns true if the deque is empty: + +\code +std::cout << "empty deque? " << my_deque.empty() << std::endl; +\endcode + +### A minimal example on STXXL's deque + +(See \ref examples/containers/deque1.cpp for the sourcecode of the following example). + +\snippet examples/containers/deque1.cpp example + +See \ref examples/containers/deque2.cpp for the sourcecode of a more comprehensive example. + +\example examples/containers/deque1.cpp +This example code is explained in the \ref tutorial_deque section. + +\example examples/containers/deque2.cpp +This example code is explained in the \ref tutorial_deque section. + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_map.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_map.dox new file mode 100644 index 0000000000..2b3be0ad2e --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_map.dox @@ -0,0 +1,171 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_map.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_map STXXL Map (B+-tree) + +This page introduces into the stxxl::map (for further information on the structure you may have a look at \ref design_map). + +stxxl::map is an external associative container that stores elements formed by a combination of a unique key value and a data value, following a specific order. +The map's key values are generally used to sort and uniquely identify the data values, while the data values store the content associated to this key. + +### Creating a STXXL Map + +To create a stxxl::map object, several template parameters are required. The first two parameters KeyType and DataType which is an std::pair in this example are self-explanatory, the third parameter has to be a comparator class which is used to determine whether a key is smaller than another one, the fourth and fifth parameter define the node- and leaf block size. +\code +#define DATA_NODE_BLOCK_SIZE (4096) +#define DATA_LEAF_BLOCK_SIZE (4096) +... +// template parameter +typedef stxxl::map map_type; + +// constructor map(node_cache_size_in_bytes, leaf_cache_size_in_bytes) to create map object named my_map +map_type my_map((map_type::node_block_type::raw_size) * 3, (map_type::leaf_block_type::raw_size) * 3); +\endcode + +The comparator class has to be defined by hand (and before the map definition above) and looks like: +\code +struct ComparatorGreater +{ + bool operator () (const int & a, const int & b) const + { return a > b; } + + static int max_value() + { return std::numeric_limits::min(); } +}; +\endcode + +If CompareGreater()(a,b) is true, then a is greater than b. CompareType must also provide a static max_value method, that returns a value of type KeyType that is larger than any key stored in map, i.e. for all x in map holds CompareType()(x,CompareType::max_value()) + +Naturally, we can define a comparator class which returns true if a is smaller than b as follows: + +\code +struct CompareLess +{ + bool operator () (const int & a, const int & b) const + { return a < b; } + + static int max_value() const + { return std::numeric_limits::max(); } +}; +\endcode + +Note that CompareType must define a strict weak ordering. + +### Insert elements + +Insertion of elements is possible in three different ways: + +1. simple insertion +\code +my_map.insert(std::pair(1, 'a')); +my_map.insert(std::pair(2, 'b')); +my_map.insert(std::pair(3, 'c')); +my_map.insert(std::pair(4, 'd')); +\endcode + +2. insertion with hint +\code +map_type::iterator iter = my_map.begin(); +my_map.insert(iter, std::pair(5, 'w')); +my_map.insert(iter, std::pair(6, 'x')); +my_map.insert(iter, std::pair(7, 'y')); +my_map.insert(iter, std::pair(8, 'z')); +\endcode + +3. range insertion +\code +map_type anothermap((map_type::node_block_type::raw_size) * 3, (map_type::leaf_block_type::raw_size) * 3); +anothermap.insert(my_map.begin(),my_map.find('c')); // stores (1, 'a'), (2, 'b'), (3, 'c') +\endcode + +### Access elements + +Random access is possible by using the []-operator: +\code +std::cout << "my_map[4] is " << my_map[4] << std::endl; // prints 'd' +\endcode + +Scanning a stxxl::map by an iterator works like +\code +// echo every element my_map contains +for (iter = my_map.begin(); iter != my_map.end(); ++iter) +{ + std::cout << iter->first << " => " << iter->second << std::endl; +} +\endcode + +Hint: To enable leaf prefetching during scanning, call my_map.enable_prefetching() before. + +In addition, the operations lower_bound() and upper_bound() are available. The function lower_bound(key) returns an iterator which initially points to the first element in the container whose key is not considered to go before key. upper_bound(key) works similar as it returns an iterator which initially points to the first element in the container whose key is considered to go after key. +\code +map_type::iterator iter_low, iter_up; + +iter_low = my_map.lower_bound(2); // iter_low points to 2 in this case +iter_up = my_map.upper_bound(6); // iter_up points to 5 in this case + +std::cout << "lower bound " << iter_low->second << " upper bound " << iter_up->second << std::endl; +\endcode + +Note: lower_bound() works nearly equal to upper_bound(), except in the case that the map contains an element with a key equivalent lower_bound(x): In this case lower_bound(x) returns an iterator pointing to that element, whereas upper_bound(x) returns an iterator pointing to the next element. + + +### Delete elements + +Removing elements out of the map is possible in three different ways: + +1. Erasing by iterator +\code +map_type::iter iter = my_map.find(7); +my_map.erase(iter); +\endcode + +2. Erasing by key +\code +my_map.erase(8); +\endcode + +3. Erasing by range +\code +iter = my_map.find(3); +my_map.erase(iter, my_map.end()); +\endcode + +### Determine size / Check whether the map is empty + +To determine the size (i.e. the number of elements) of an instance, call size(): +\code +std::cout << "number of elements in my_map: " << my_map.size() << std::endl; +\endcode + +To check if the priority queue is empty, call empty() which returns true in case: +\code +std::cout << "is my_map empty? " << my_map.empty() << std::endl; +\endcode + +### A minimal working example on STXXL Map + +(See \ref examples/containers/map1.cpp for the sourcecode of the following example). + +\snippet examples/containers/map1.cpp example + +\example examples/containers/map1.cpp +This example code is explained in the \ref tutorial_map section + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_matrix.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_matrix.dox new file mode 100644 index 0000000000..5d8167fafb --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_matrix.dox @@ -0,0 +1,102 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_matrix.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ +namespace stxxl { + +/** \page tutorial_matrix STXXL Matrix + +This section introduces into the STXXL matrix container (to learn more about the structure of stxxl::matrix, see section \ref design_matrix). + +### Create a STXXL Matrix + +Before using a STXXL matrix, we initially have to define and then to instantiate a matrix object. Two template parameters are required to define a stxxl::matrix container. ValueType defines the type of the contained objects (must be a POD with no references to internal memory) and BlockSizeLength specifies the side length of the square submatrices (blocks). BlockSizeLength is given in bits and must be a multiple +of k assuming a valueType of k bits. The block schedular is used for swapping of blocks and provide blocks for temporary storage and expects the type of swappable_blocks to manage as a parameter. +Can be some specialized subclass. We used matrix_swappable_block as a subclass which holds the same template parameters as the aforementioned stxxl::matrix container. + +\code +// Define integer matrix + +// template paramter +typedef stxxl::block_scheduler > block_schedular_type; + +// template paramter +typedef stxxl::matrix matrix_type; + +// construct block schedular object which uses 64 MiB internal memory for caching +block_schedular_type my_bs(64*1024*1024); +\endcode + +Instanciate three new height x width (3 x 3 in this example) matrices A, B and C using a block schedular. \n +Note that all values are initially set to zero. + +\code +int height = 3; +int width = 3; + +matrix_type A(my_bs, height, width); // creates a new matrix A of given dimensions. Elements' values are set to zero. +matrix_type B(my_bs, height, width); // B +matrix_type C(my_bs, height, width); // C +\endcode + +### Insert / Access elements + +To insert and access elements, the STXXL matrix container intends different iterators. Iterate for example row-by-row beginning with the top row can be done with the row_major_iterator. +The operator * accesses a single element the iterator points to just now. +\code +typedef matrix_type::row_major_iterator row_iterator; + +// let it_A point to the first element of matrix A and advance till the very last element of matrix A is reached +for (row_iterator it_A = A.begin(); it_A != A.end(); ++it_A) +{ + *it_A = 1; // set current matrix element to 1 +} +\endcode + + +### Determine size of matrix + +To detect the height and width of a given matrix C, we can call: + +\code +std::cout << "height: " << C.get_height() << " - " << "width: " << C.get_width() << std::endl; +\endcode + + +### Matrix Operations + +The STXXL Matrix container provides the following arithmetic operations: + +- Addition, Substraction and Multiplication of two matrices A and B. + \code + C = A + B; + C = A - B; + C = A * B; + \endcode +- Transposition and Zeroing of a matrix C. + \code + C.transpose(); + C.set_zero(); + \endcode + +### A minimal working example of STXXL's Matrix + +(See \ref examples/containers/matrix1.cpp for the sourcecode of the following example). + +\snippet examples/containers/matrix1.cpp example + +\example examples/containers/matrix1.cpp +This example code is explained in the \ref tutorial_matrix section +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_pqueue.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_pqueue.dox new file mode 100644 index 0000000000..a5d5f7fca6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_pqueue.dox @@ -0,0 +1,128 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_pqueue.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_pqueue STXXL Priority Queue + +This page introduces into the stxxl::priority_queue container (to learn more about the structure of stxxl::priority_queue, see section \ref design_pqueue). + +Basically, the priority queue provides insertion of new elements as well as access and deletion of the element on top. +The invariant guarantees that the top element is the largest (or smallest if desired) of all inserted elements identified by comparison realized by the customizable comparator class. + +### Creating a STXXL priority queue + +To manage the configuration of the priority queue type, we use the generator template stxxl::PRIORITY_QUEUE_GENERATOR. This generator template expects a value type (which is an integer in our example), +a class which we name Comparator(a,b) to compare two given elements a and b, a internal memory limit in bytes and the number of elements to be stored (in 1024 units). See section \ref design_pqueue_generator for additional configuration parameters and information. + +Thus the definition may look as follows: + +\code +// template parameter +typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pqueue_type; +\endcode + +The ComparatorGreater(a,b) class is needed to compare two given elements a and b and has to be defined by hand (and before the priority queue definition above): + +\code +struct ComparatorGreater +{ + bool operator () (const int &a, const int &b) const + { return (a > b); } + + int min_value() const + { return std::numeric_limits::max(); } +}; +\endcode + +The compare-operator () of two elements a and b returns true, if a is larger than b, otherwise false. Consequently, this priority queue serves it's smallest element on top . The additional min_value() function ensures that Comparator(min_value(),x) is true for each and every x. + +iLikewise the minimum-on-top Comparator, we can easily define a largest element on top Comparator which stores the the largest contained integer on top as well: + +\code +struct ComparatorLess +{ + bool operator () (const int & a, const int & b) const + { return a::min(); } +}; +\endcode + +Note that CompareType must define a strict weak ordering. These and some other details are available in the Notes part of \ref design_pqueue_generator + +To create a STXXL priority queue instance, a resizable buffered writing and prefetched reading pool (to overlap I/O and computation) is needed: + +\code + typedef pqueue_type::block_type block_type; + const unsigned int mem_for_pools = 16 * 1024 * 1024; // restricts memory consumption of the pools + + stxxl::read_write_pool pool((mem_for_pools / 2) / block_type::raw_size, (mem_for_pools / 2) / block_type::raw_size); + pqueue_type my_pqueue(pool); // creates priority queue object with read-write-pool +\endcode + + +### Insert / Access / Delete elements + +To insert a new element into the priority queue, call push(): + +\code +my_pqueue.push(5); +\endcode + +The priority queue only allows to access the top element, which is the smallest or largest element (depending on the used comparator class) of all inserted elements. +Calling top() on an instance returns this element: + +\code +int x; +x = my_pqueue.top(); +\endcode + +Erasing elements is only possible on the top of the priority queue by calling pop(). +Note that after removing the element on top, the priority queue still holds the above mentioned property. + +\code +my_pqueue.pop(); +\endcode +### Determine size / Check whether the priority queue is empty + +To determine the size (i.e. the number of elements) of an instance, call size(): +\code +std::cout << "priority queue stores: " << my_pqueue.size() << " elements" << std::endl; +\endcode + +To check if the priority queue is empty, call empty() which returns true in case: +\code +std::cout << "empty priority queue? " << my_pqueue.empty() << std::endl; +\endcode + +### A minimal working example of STXXL's priority queue + +(See \ref examples/containers/pqueue1.cpp for the sourcecode of the following example). + +\snippet examples/containers/pqueue1.cpp example + +See \ref examples/containers/pqueue2.cpp for the sourcecode of another small example. + +\example examples/containers/pqueue1.cpp +This example code is explained in the \ref tutorial_pqueue section + +\example examples/containers/pqueue2.cpp +This example code is explained in the \ref tutorial_pqueue section + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_queue.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_queue.dox new file mode 100644 index 0000000000..c5344af43c --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_queue.dox @@ -0,0 +1,80 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_queue.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ +namespace stxxl { + +/** \page tutorial_queue STXXL Queue + +This page introduces into the stxxl::queue Container (to learn more about the structure of stxxl::stack, see section \ref design_queue). + +### Creating a STXXL queue + +Before using a STXXL queue, we initially have to define and then to instantiate a queue object. The implementation holds the head and the tail blocks in the main memory. Prefetch and write block pools might be used to overlap I/O and computation during queue operations. A minimal configuration is shown below - the value_type (integer in our example case) is the only stricly neccessary parameter. +The default configuration initializes a write_pool and a prefetch_pool of size 1. + +\code +typedef stxxl::queue queue; +// create queue object with default parameters: +// write_pool size = ?, prefetch_pool size = 1, blocks2prefetch = number of blocks in the prefetch pool (i.e. 1) +queue my_queue; +\endcode + +The STXXL queue implementation provides three different types of constructors to customize your individual caching. See \ref stxxl::queue more details. Additional optional template parameters are block_size, allocation_strategy, size_type, see \ref stxxl::queue for further details. + +### Insert / Access / Delete elements + +To insert a new value at the beginning of the queue, call push(). +Accessing elements are possible on both endings of the queue, back() returns the value at the beginning, front() returns the value at the end. Deleting a value by pop() erases the first inserted element. + +\code +my_queue.push(5); // queue now stores: |5| +my_queue.push(9); // queue now stores: |9|5| +my_queue.push(1); // queue now stores: |1|9|5| +x = my_queue.back(); // x = 1 +y = my_queue.front(); // y = 5 +my_queue.pop(); // queue now stores: |1|9| +\endcode + +### Determine size / Check whether queue is empty + +To determine the number of elements a queue currently stores, call size(): + +\code +std::cout << "size of queue: " << my_queue.size() << std::endl; +\endcode + +To check if the queue is empty, call empty() which returns true in that case: + +\code +std::cout << "queue empty? " << my_queue.empty() << std::endl; +\endcode + +### A minimal working example of STXXL's queue + +(See \ref examples/containers/queue1.cpp for the sourcecode of the following example). + +\snippet examples/containers/queue1.cpp example + +See \ref examples/containers/queue2.cpp for the sourcecode of a more comprehensive example. + +\example examples/containers/queue1.cpp +This example code is explained in the \ref tutorial_queue section. + +\example examples/containers/queue2.cpp +This example code is explained in the \ref tutorial_queue section. + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_sequence.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_sequence.dox new file mode 100644 index 0000000000..ceab0dab56 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_sequence.dox @@ -0,0 +1,109 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_sequence.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ +namespace stxxl { +/** \page tutorial_sequence STXXL Sequence + +This page introduces into the stxxl::sequence container (to learn more about the structure of stxxl::sequence, see section \ref stxxl::sequence). + +In reality, the STXXL sequence container is a STXXL deque container (see \ref design_deque) without random access. Deque stands for "double-ended-queue", that means elements can be accessed, inserted and deleted on both ends of the data structure. Consequently both containers are quite similar - however, the usage varies. + +### Create a STXXL sequence +To create an empty stxxl::sequence object with own write and prefetch block pool, only the data value type must be specified: +\code + typedef stxxl::sequence sequence_type; + sequence_type my_sequence; +\endcode + +### Insert elements +Inserting elements is possible at the start (by calling the push_front() function) as well as the end (by calling the push_back() function) of the deque (equal to \ref tutorial_deque) + +\code +my_sequence.push_front(2); +my_sequence.push_front(11); +my_sequence.push_back(5); +my_sequence.push_back(8); +// sequence now stores: |11|2|5|8| +\endcode + +### Access elements +To return a reference to the element at the start of the sequence, call front(), to return a reference to the elemtent at the end of the sequence, call back() on a deque instance. +\code +std::cout << "return 'first' element: " << my_sequence.front() << std::endl; // prints 11 +std::cout << "return 'last' element: " << my_sequence.back() << std::endl; // prints 8 +\endcode + +Due to the fact that the sequence container does \b not support random access, the sequence can only be accessed in an I/O-efficient way by iterating using streams: either from front to back or in reverse. For this purpose, the sequence provides the public member functions get_stream() and get_reverse_stream(). + +The preincrement operator ++ let the stream point to the next element in the sequence (depending on the stream direction). +Accessing an element the iterator points to is possible by using the prefixed * operator. +To check if the end of the sequence container is reached by the stream, the empty() function returns true in such a case. Note that the stream is immutable and therefore read-only, so you can't modify it's members. The subsequent examples illustrate the usage. + +The forward iterator moves from back to front and may be used as follows: +\code +// create stream which points to the front element of the sequence +sequence_type::stream forward_stream = my_sequence.get_stream(); + +// advance from front to back of sequence +while (!forward_stream.empty()) +{ + std::cout << *forward_stream << " "; + ++forward_stream; +} +\endcode + +The reverse iterator moves from back to front and may be used as follows: +\code +// create stream which points to the back element of the sequence +sequence_type::reverse_stream reverse_stream = my_sequence.get_reverse_stream(); + +// advance from back to front of sequence +while (!reverse_stream.empty()) +{ + std::cout << *reverse_stream << " "; + ++reverse_stream; +} +\endcode + + +### Delete elements +Removing elements is possible at both endings of the sequence by using pop_front() and pop_back(): +\code +my_deque.pop_front(); // deque now stores: |2|5|8| +my_deque.pop_back(); // deque now stores: |2|5| +\endcode + +### Determine size / Check whether the sequence is empty +To determine the size (i.e. the number of elements) of an instance, call size(): +\code +std::cout << "sequence stores: " << my_sequence.size() << " elements" << std::endl; +\endcode + +To check if the sequence is empty, call empty() which returns true if the sequence is empty: +\code +std::cout << "empty sequence? " << my_sequence.empty() << std::endl; +\endcode + + +### A minimal working example on STXXL's sequence +(See \ref examples/containers/sequence1.cpp for the sourcecode of the following example). + +\snippet examples/containers/sequence1.cpp example + +\example examples/containers/sequence1.cpp +This example code is explained in the \ref tutorial_sequence section. + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_sorter.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_sorter.dox new file mode 100644 index 0000000000..f8f58748da --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_sorter.dox @@ -0,0 +1,116 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_sorter.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { +/** \page tutorial_sorter STXXL Sorter + +This section introduces into the STXXL sorter container (to learn more about the structure of stxxl::sorter, see section \ref stxxl::sorter). + +The STXXL sorter container combines the two functions of runs_creator and runs_merger from the stream packages into a two-phase container. As a result, the STXXL sorter implements a external memory k-way merge sort to sort large amounts of data. + +### Create a STXXL sorter + +Before using a STXXL sorter, we initially have to define and then to instantiate a sorter object. Two template parameters are required to define a stxxl::sorter. ValueType defines the type of the contained objects (must be a POD with no references to internal memory) and CompareType is the type of comparison object used for sorting the runs (see example below). +BlockSize and AllocStr are optional (see \ref stxxl::sorter for additional information). The more straightforward of the two sorter constructors expects the comparator object and memory_to_use (bytes) in ram for sorted runs as parameters. + +\code +// template parameter +typedef stxxl::sorter sorter_type; + +// create sorter object (CompareType(), MemoryToUse) +sorter_type int_sorter(my_comparator(), 64*1024*1024); +\endcode + + +The comparator class may look as follows. The operator() is needed to compare two given elements a and b. CompareType must also provide a min_value() method, that returns the value of type ValueType that is smaller than any element of the queue x, i.e. CompareType(CompareType.min_value(),x) is always true as well as a max_value() method that works equivalent: +\code +// achieve an ascending order of sorting +struct my_comparator +{ + bool operator()(const int &a, const int &b) const + { + return a < b; + } + + int min_value() const { + return std::numeric_limits::min(); + } + + int max_value() const { + return std::numeric_limits::max(); + } +}; +\endcode + +Note that CompareType must define strict weak ordering. \n +\n +The sorter container know two different kind of states - the input state and a output state. Insertion of elements are only allowed when the sorter is in the input state. After sorting is called, the container enters the output state and inserting elements is disallowed. + +### Insert elements + +Inserting elements is possible into the sorter container by calling the push() function: +\code +int_sorter.push(5); +int_sorter.push(10); +int_sorter.push(3); +\endcode + +### Sorting all elements + +Sorting all elements a sorter container is holding, call sort(): +\code +int_sorter.sort(); +\endcode + + +### Access sorted elements + +After calling sort, the items ca be read in sorted order using the operator*(), using operator++() to advance to the next item and empty() to check for the end: +\code +while (!int_sorter.empty()) +{ + std::cout << *int_sorter << " "; + ++int_sorter; +} +\endcode + + +### Determine size / Check whether the map is empty + +To determine the size (i.e. the number of elements) of a sorter container, call size(): +\code +std::cout << "number of elements in int_sorter: " << int_sorter.size() << std::endl; +\endcode + +To check if the sorter is empty, call empty() which returns true in case: +\code +std::cout << "is int_sorter empty? " << int_sorter.empty() << std::endl; +\endcode + +### A minimal working example of STXXL's sorter + +(See \ref examples/containers/sorter1.cpp for the sourcecode of the following example). + +\snippet examples/containers/sorter1.cpp example + +See \ref examples/containers/sorter2.cpp for the sourcecode of a more comprehensive example. + +\example examples/containers/sorter1.cpp +This example code is explained in the \ref tutorial_sorter section. + +\example examples/containers/sorter2.cpp +This example code is explained in the \ref tutorial_sorter section. +*/ +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_stack.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_stack.dox new file mode 100644 index 0000000000..4ab7c75c06 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_stack.dox @@ -0,0 +1,94 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_stack.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_stack STXXL Stack + +This section introduces into the STXXL stack container (to learn more about the structure of stxxl::stack, see section \ref design_stack). + +STXXL stacks are last in first out (LIFO), i.e. inserting and extracting elements are only allowed from one end of the container and the element on top is the element added most recently. + +### Creating a STXXL stack + +Before using a STXXL stack, we initially have to define and then to instantiate a stack object. To manage the configuration of the stack type we used the generator template. A minimal configuration is shown below - as one can see the value_type (integer in our case) is the only stricly neccessary parameter. See \ref design_stack_generator for additional configuration parameters and information. + +\code +typedef stxxl::STACK_GENERATOR::result stack; +stack my_stack; // create empty stack object +\endcode + +Hint: STXXL stack provides specialized implementations for a certain access pattern. If the access pattern is known before, such a customization might gain a significant speedup. +The default stack is stxxl::normal_stack which is the best for a random sequence of push'es and pop's. See \ref design_stack section for more details. + +### Insert / Access / Delete elements + +To insert an element on top of the stack call push(): + +\code +my_stack.push(7); +my_stack.push(2); +my_stack.push(5); +// stack from bottom to top: |7|2|5| +\endcode + +To access the top element call top(): + +\code +std::cout << "element on top of stack is: " << my_stack.top << std::endl; // prints out 5 +\endcode + +To remove the top element call pop(). + +\code +my_stack.pop(); // removes element 5 +\endcode + +### Determine size / Check whether stack is empty + +To determine the size (i.e. the number of elements) of stack instance, call size(): + +\code +std::cout << "size of stack: " << my_stack.size() << std::endl; +\endcode + +To check if the stack is empty, call empty() which returns true in case of emptyness: + +\code +// loop till stack is empty +while (!my_stack.empty()) +{ + // do something +} +\endcode + +### A minimal working example of STXXL's stack + +(See \ref examples/containers/stack1.cpp for the sourcecode of the following example). + +\snippet examples/containers/stack1.cpp example + +See \ref examples/containers/stack2.cpp for the sourcecode of a more comprehensive example. + +\example examples/containers/stack1.cpp +This example code is explained in the \ref tutorial_stack section + +\example examples/containers/stack2.cpp +This example code is explained in the \ref tutorial_stack section + +*/ + +} // namespace diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_stream.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_stream.dox new file mode 100644 index 0000000000..4ce38a33ab --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_stream.dox @@ -0,0 +1,509 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_stream.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { +namespace stream { + +/** \page tutorial_stream Tutorial for the Stream Package + +\author Timo Bingmann (2012-06-11) + +This page gives a short introduction into the stream package. First the main abstractions are discussed and then some examples on how to utilize the existing algorithms are developed. + +All example code can be found in \ref examples/stream/stream1.cpp + +In \ref tutorial_stream_edgesort another example is given, where an existing algorithm is "pipelined". + +\section stream1 Abstraction, Interface and a Simple Example + +The stream package is built around the abstract notion of an object being able to produce a sequence of output values. Only three simple operations are necessary: +- Retrieval of the current value: prefix \c * operator +- Advance to the next value in the sequence: prefix \c ++ operator +- Indication of the sequence's end: \c empty() function + +The most common place object that fits easily into this abstraction is the random generator. Actually, a random generator only requires two operations: it can be queried for its current value and be instructed to calculate/advance to new value. Of course the random sequence should be unbounded, so an \c empty() function would always be false. Nevertheless, this common-place example illustrates the purpose of the stream interface pretty well. + +All stream objects must support the three operations above, they form the stream algorithm concept. In C++ a class conforms to this concept if it implements the following interface: + +\code +struct stream_object +{ + // Type of the values in the output sequence. + typedef output_type value_type; + + // Retrieval prefix * operator (like dereferencing a pointer or iterator). + const value_type& operator* () const; + + // Prefix increment ++ operator, which advances the stream to the next value. + stream_object& operator++ (); + + // Empty indicator. True if the last ++ operation could not fetch a value. + bool empty() const; +}; +\endcode + +A very simple stream object that produces the sequence 1,2,3,4,....,1000 is shown in the following snippet: + +\code +struct counter_object +{ + // This stream produces a sequence of integers. + typedef int value_type; + +private: + // A class attribute to save the current value. + int m_current_value; + +public: + // A constructor to set the initial value to 1. + counter_object() + : m_current_value(1) + { + } + + // The retrieve operator returning the current value. + const value_type& operator* () const + { + return m_current_value; + } + + // Increment operator advancing to the next integer. + counter_object& operator++ () + { + ++m_current_value; + return *this; + } + + // Empty indicator, which in this case can check the current value. + bool empty() const + { + return (m_current_value > 1000); + } +}; +\endcode + +After this verbose interface definition, the actual iteration over a stream object can be done as follows: + +\code +counter_object counter; + +while (!counter.empty()) +{ + std::cout << *counter << " "; + ++counter; +} +std::cout << std::endl; +\endcode + +For those who like to shorten everything into fewer lines, the above can also be expressed as a for loop: + +\code +for (counter_object cnt; !cnt.empty(); ++cnt) +{ + std::cout << *cnt << " "; +} +std::cout << std::endl; +\endcode + +Both loops will print the following output: +\verbatim +1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [...] 995 996 997 998 999 1000 +\endverbatim + +\section stream2 Pipelining: Plugging Stream Objects Together + +The stream interface is so very useful for external memory algorithms because it represents the concept of sequential access to a stream of individual values. While the simple example above only works with integers, the \c value_type of streams will more often contain complex tuple structs with multiple components. + +A stream algorithm can then be constructed from multiple stream objects that pass data from one to another. This notion of "plugging together" stream objects is used in the following example to calculate the square of each value of an integer sequence: + +\code +template +struct squaring_object +{ + // This stream produces a sequence of integers. + typedef int value_type; + +private: + // A reference to another stream of integers, which are our input. + InputStream& m_input_stream; + + // A temporary value buffer to hold the current square for retrieval. + value_type m_current_value; + +public: + // A constructor taking another stream of integers as input. + squaring_object(InputStream& input_stream) + : m_input_stream(input_stream) + { + if (!m_input_stream.empty()) + { + m_current_value = *m_input_stream; + m_current_value = m_current_value * m_current_value; + } + } + + // The retrieve operator returning the square of the input stream. + const value_type& operator* () const + { + return m_current_value; + } + + // Increment operator: handled by incrementing the input stream. + squaring_object& operator++ () + { + ++m_input_stream; + if (!m_input_stream.empty()) + { + m_current_value = *m_input_stream; + m_current_value = m_current_value * m_current_value; + } + return *this; + } + + // Empty indicator: this stream is empty when the input stream is. + bool empty() const + { + return m_input_stream.empty(); + } +}; +\endcode + +For a beginner in stream object programming, the squaring example contains multiple unexpected, verbose complications. + +- We wish to allow many different integer sequences as input streams to the squaring class. For this we use template meta-programming and define squaring to take any class as \c InputStream template parameter. As yet, in C++ we cannot syntactically define which concepts the template parameters must fulfill, in this case one would require \c InputStream to implement the stream interface. + +- After defining the input stream class, one will usually need an instantiated object of that class inside the new stream class. Most common practice is to define references to other streams as class attributes, and have the actual objects be passed to the constructor of the new stream object.
In the case of the squaring class, any \c InputStream object is accepted by the constructor and a reference is saved into \c m_input_stream. + +- As second attribute, the squaring class contains m_current_value. The additional temporary value is required in this case because \c operator*() must return a const-reference, so the square must actually be stored in a variable after it is calculated. Now note that the squaring operation in this version is implemented at two places: in the constructor and the \c operator++().
This is necessary, because the stream concept requires that the first value be immediately available after construction! Therefore it must be calculated in the constructor, and this code is usually a duplicate to the action done in \c operator++(). A real implementation would probably combine the calculation code into a \c process() function and also do additional allocation work in the constructor. + +An instance of the \c counter_object can be plugged into a \c squaring_object as done in the following example: + +\code +counter_object counter; +squaring_object squares(counter); + +while (!squares.empty()) +{ + std::cout << *squares << " "; + ++squares; +} +std::cout << std::endl; +\endcode + +The example outputs: + +\verbatim +1 4 9 16 25 36 49 64 81 100 121 144 169 [...] 986049 988036 990025 992016 994009 996004 998001 1000000 +\endverbatim + +\section stream3 Miscellaneous Utilities Provided by the Stream Package + +The above examples are pure C++ interface manipulations and do not even require STXXL. However, when writing stream algorithms you can take advantage of the utilities provided by the stream package to create complex algorithms. Probably the most useful is the pair of sorting classes, which will be discussed after a few preliminaries. + +More complex algorithms will most often use tuples as values passed from one stream to another. These tuples wrap all information fields of a specific piece of data. Simple tuples can be created using \c std::pair, tuples with larger number of components can use Boost.Tuple or just plain structs with multiple fields. (In the tuple case, the temporary value inside the stream struct can mostly be avoided.) + +The stream package contains utilities to plug stream classes together to form complex algorithms. The following few examples are very basic algorithms: + +Very often the input to a sequence of stream classes comes from an array or other container. In this case one requires an input stream object, which iterates through the container and outputs each element once. STXXL provides iterator2stream for this common purpose: +\code +std::vector intvector; +// (fill intvector) + +// define stream class iterating over an integer vector +typedef stxxl::stream::iterator2stream< std::vector::const_iterator > intstream_type; + +// instantiate the stream object, iterate from begin to end of intvector. +intstream_type intstream (intvector.begin(), intvector.end()); + +// plug in squaring object after vector iterator stream. +squaring_object squares(intstream); +\endcode + +Most important: if the input container is a stxxl::vector, then one should use vector_iterator2stream, because this class will prefetch additional blocks from the vector while processing the stream. +\code +stxxl::vector intvector; +// (fill intvector) + +// define stream class iterating over an integer STXXL vector +typedef stxxl::stream::vector_iterator2stream< stxxl::vector::const_iterator > intstream_type; + +// instantiate the stream object, iterate from begin to end of intvector using prefetching +intstream_type intstream (intvector.begin(), intvector.end()); + +// plug in squaring object after vector iterator stream. +squaring_object squares(intstream); +\endcode + +The opposite to iterator2stream is to collect the output of a sequence of stream objects into a container or stxxl::vector. This operation is called \c materialize and also comes in the general version and a special version for the STXXL-vector, which uses asynchronous writes. + +This example shows how to materialize a stream into a usual STL vector. +\code +// construct the squared counter stream +counter_object counter; +squaring_object squares(counter); + +// allocate vector of 100 integers +std::vector intvector (100); + +// materialize 100 integers from stream and put into vector +stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); +\endcode + +And the only modification needed to support larger data sets is to materialize to an STXXL vector: +\code +// construct the squared counter stream +counter_object counter; +squaring_object squares(counter); + +// allocate STXXL vector of 100 integers +stxxl::vector intvector (100); + +// materialize 100 integers from stream and put into STXXL vector +stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); +\endcode + +\section stream4 Sorting As Provided by the Stream Package + +Maybe the most important set of tools in the stream package is the pairs of sorter classes runs_creator and runs_merger. The general way to sort a sequential input stream is to first consolidate a large number of input items in an internal memory buffer. Then when the buffer is full, it can be sorted in internal memory and subsequently written out to disk. This sorted sequence is then called a run. When the input stream is finished and the sorted output must be produced, theses sorted sequences can efficiently be merged using a tournament tree or similar multi-way comparison structure. (see \ref design_algo_sorting.) + +STXXL implements this using two stream classes: runs_creator and runs_merger. + +The following examples shows how to sort the integer sequence 1,2,...,1000 first by the right-most decimal digit, then by its absolute value (yes a somewhat constructed example, but it serves its purpose well.) For all sorters a comparator object is required which tells the sorter which of two objects is the smaller one. This is similar to the requirements of the usual STL, however, the STXXL sorters need to additional functions: \c min_value() and \c max_value() which are used as padding sentinels. These functions return the smallest and highest possible values of the given data type. +\code +// define comparator class: compare right-most decimal and then absolute value +struct CompareMod10 +{ + // comparison operator() returning true if (a < b) + inline bool operator() (int a, int b) const + { + if ((a % 10) == (b % 10)) + return a < b; + else + return (a % 10) < (b % 10); + } + + // smallest possible integer value + int min_value() const { return INT_MIN; } + // largest possible integer value + int max_value() const { return INT_MAX; } +}; +\endcode + +All sorters steps require an internal memory buffer. This size can be fixed using a parameter to runs_creator and runs_merger. The following example code instantiates a counter object, plugs this into a runs_creator which is followed by a runs_merger. + +\code +static const int ram_use = 10*1024*1024; // amount of memory to use in runs creation + +counter_object counter; // the counter stream from first examples + +// define a runs sorter for the counter stream which order by CompareMod10 object. +typedef stxxl::stream::runs_creator rc_counter_type; + +// instance of CompareMod10 comparator class +CompareMod10 comparemod10; + +// instance of runs_creator which reads the counter stream. +rc_counter_type rc_counter (counter, comparemod10, ram_use); + +// define a runs merger for the sorted runs from rc_counter. +typedef stxxl::stream::runs_merger rm_counter_type; + +// instance of runs_merger which merges sorted runs from rc_counter. +rm_counter_type rm_counter (rc_counter.result(), comparemod10, ram_use); + +// read sorted stream: runs_merger also conforms to the stream interface. +while (!rm_counter.empty()) +{ + std::cout << *rm_counter << " "; + ++rm_counter; +} +std::cout << std::endl; +\endcode +The output of the code above is: +\verbatim +10 20 30 40 50 60 70 80 [...] 990 1000 1 11 21 31 41 51 61 [...] 909 919 929 939 949 959 969 979 989 999 +\endverbatim + +Note that in the above example the input of the runs_creator is itself a stream. If however the data is not naturally available as a stream, one can use a variant of runs_creator which accepts input via a \c push() function. This is more useful when using an imperative programming style. Note that the runs_merger does not change. +\code +static const int ram_use = 10*1024*1024; // amount of memory to use in runs creation + +// define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. +typedef stxxl::stream::runs_creator, CompareMod10> rc_counter_type; + +// instance of CompareMod10 comparator class. +CompareMod10 comparemod10; + +// instance of runs_creator which waits for input. +rc_counter_type rc_counter (comparemod10, ram_use); + +// write sequence of integers into runs +for (int i = 1; i <= 1000; ++i) + rc_counter.push(i); + +// define a runs merger for the sorted runs from rc_counter. +typedef stxxl::stream::runs_merger rm_counter_type; + +// instance of runs_merger which merges sorted runs from rc_counter. +rm_counter_type rm_counter (rc_counter.result(), comparemod10, ram_use); + +// read sorted stream: runs_merger also conforms to the stream interface. +while (!rm_counter.empty()) +{ + std::cout << *rm_counter << " "; + ++rm_counter; +} +std::cout << std::endl; +\endcode + +And as the last example in this tutorial we show how to use stxxl::sorter, which combines runs_creator and runs_merger into one object. The sorter has two states: input and output. During input, new elements can be sorted using \c push(). Then to switch to output state, the function \c sort() is called, after which the sorter can be queried using the usual stream interface. +\code +static const int ram_use = 10*1024*1024; // amount of memory to use in runs creation + +// define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. +typedef stxxl::sorter sr_counter_type; + +// instance of CompareMod10 comparator class. +CompareMod10 comparemod10; + +// instance of sorter which waits for input. +sr_counter_type sr_counter (comparemod10, ram_use); + +// write sequence of integers into sorter, which creates sorted runs +for (int i = 1; i <= 1000; ++i) + sr_counter.push(i); + +// signal sorter that the input stream is finished and switch to output mode. +sr_counter.sort(); + +// read sorted stream: sorter also conforms to the stream interface. +while (!sr_counter.empty()) +{ + std::cout << *sr_counter << " "; + ++sr_counter; +} +std::cout << std::endl; +\endcode + +All three examples have the same output. + +\example examples/stream/stream1.cpp +This example code is explain in the \ref tutorial_stream. + +*/ + +/** \page tutorial_stream_edgesort Generating Random Graphs using Streams + +\author Roman Dementiev (2007) + +This page gives an example of how an application using "traditional" containers and algorithms can be converted into using pipelines streaming. + +The purpose of our example is to generate a huge random directed graph in a sorted edge array representation, i.e. the edges in the edge array must be sorted lexicographically. The definitions of the classes \c edge, \c random_edge and \c edge_cmp are in the following code listing. + +\code +struct edge { // edge class + int src, dst; // nodes + edge() {} + edge(int src_, int dst_): src(src_), dst(dst_) {} + bool operator == (const edge & b) const { + return src == b.src && dst == b.dst; + } +}; +struct random_edge { // random edge generator functor + edge operator () () const { + edge Edge(random() - 1, random() - 1); + while(Edge.dst == Edge.src) + Edge.dst = random() - 1 ; // no self-loops + return Edge; + } +}; +struct edge_cmp { // edge comparison functor + edge min_value() const { + return edge(std::numeric_limits::min(),0); }; + edge max_value() const { + return edge(std::numeric_limits::max(),0); }; + bool operator () (const edge & a, + const edge & b) const { + return a.src < b.src || (a.src == b.src && a.dst < b.dst); + } +}; +\endcode + +A straightforward procedure to generate the graph is to: 1) generate a sequence of random edges, 2) sort the sequence, 3) remove duplicate edges from it. If we ignore definitions of helper classes the STL/STXXL code of the algorithm implementation is only five lines long: + +\code +// create vector +stxxl::vector ExtEdgeVec(10000000000ULL); +// generate random edges +stxxl::generate(ExtEdgeVec.begin(), ExtEdgeVec.end(), random_edge()); +// sort edges by source vertex +stxxl::sort(ExtEdgeVec.begin(), ExtEdgeVec.end(), edge_cmp(), 512*1024*1024); +// unify equal edges +stxxl::vector::iterator NewEnd = std::unique(ExtEdgeVec.begin(), ExtEdgeVec.end()); +ExtEdgeVec.resize(NewEnd - ExtEdgeVec.begin()); +\endcode + +Line 2 creates an STXXL external memory vector with 10 billion edges. Line 4 fills the vector with random edges (stxxl::generate from the STL is used). In the next line the STXXL external memory sorter sorts randomly generated edges using 512 megabytes of internal memory. The lexicographical order is defined by functor \c my_cmp, stxxl::sort also requires the comparison functor to provide upper and lower bounds for the elements being sorted. Line 8 deletes duplicate edges in the external memory vector with the help of the STL \c std::unique algorithm. The \c NewEnd vector iterator points to the right boundary of the range without duplicates. Finally (in the last line), we chop the vector at the \c NewEnd boundary. + +Now we count the number of I/Os performed by this example: external vector construction takes no I/Os; filling with random values requires a scan --- \f$ N/DB \f$ I/Os; sorting will take \f$ 4N/DB \f$ I/Os; duplicate removal needs no more than \f$ 2N/DB \f$ I/Os; chopping a vector is I/O-free. The total number of I/Os is \f$ 7N/DB \f$. + +# Pipelined random graph generation + +Now we "pipeline" the random graph generation example shown in the previous chapter. The data flow graph of the algorithm is presented in the following figure: + +\image html pipeline_randomgraph_small.png "Pipeline of Random Graph Generator" + +The following code listing shows the pipelined code of the algorithm, the definitions of \c edge, \c random_edge, and \c edge_cmp are assumed to be available from the listing in the previous section. + +\code +class random_edge_stream { + stxxl::int64 counter; + edge current; + random_edge_stream(); +public: + typedef edge value_type; + random_edge_stream(stxxl::int64 elements) + : counter(elements), current(random_edge()()) + { } + const edge & operator * () const { return current; } + const edge * operator ->() const { return ¤t; } + random_edge_stream & operator ++ () + { + --counter; + current = random_edge()(); + return *this; + } + bool empty() const { return counter == 0; } +}; + +random_edge_stream RandomStream(10000000000ULL); +typedef stxxl::stream::sort sorted_stream; +sorted_stream SortedStream(RandomStream, edge_cmp(), 512*1024*1024); +typedef stxxl::stream::unique unique_stream_type; +unique_stream_type UniqueStream(SortedStream); +stxxl::vector ExtEdgeVec(10000000000ULL); +stxxl::vector::iterator NewEnd = + stxxl::stream::materialize(UniqueStream, ExtEdgeVec.begin()); +ExtEdgeVec.resize(NewEnd - ExtEdgeVec.begin()); +\endcode + +Since the sorter of the streaming layer accepts an \c stream input, we do not need to output the random edges. Rather, we generate them on the fly. The \c random_edge_stream object (model of \c stream) supplies the sorter with a stream of random edges. We define the type of the sorter node as \c sorted_stream; it is parameterized by the type of the input stream and the type of the comparison function object. Then a \c SortedStream object is created and its input is attached to the \c RandomStream object's output. The internal memory consumption of the sorter stream object is limited to 512 MB. The \c UniqueStream object filters the duplicates in its input edge stream. The generic \c stream::unique stream class stems from the STXXL library. The stream::materialize function records the content of the \c UniqueStream into the external memory vector. As in the previous non-pipelined version, we cut the vector at the \c NewEnd boundary. + +Let us count the number of I/Os the program performs: random edge generation by \c RandomStream costs no I/O; sorting in \c SortedStream needs to store the sorted runs and read them again to merge --- \f$ 2N/DB \f$ I/Os; \c UniqueStream deletes duplicates on the fly, it does not need any I/O; and materializing the final output can cost up to \f$ N/DB \f$ I/Os. All in all, the program only incurs \f$ 3N/DB \f$ I/Os, compared to \f$ 7N/DB \f$ for the non-pipelined code. + +*/ + +} // namespace stream +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_unordered_map.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_unordered_map.dox new file mode 100644 index 0000000000..af0b86d358 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_unordered_map.dox @@ -0,0 +1,59 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_unordered_map.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_unordered_map STXXL Unordered Map (Hash Map) + +This page introduces the **EXPERIMENTAL** stxxl::unordered_map which can be used in-lieu of std::unordered_map (for further information on the interface, refer to the API \ref stxxl::unordered_map). + +stxxl::unordered_map is an external memory hash map that stores elements formed by a combination of a unique key value and a data value, without any specific order. The main problem is that a hash map **ITSELF IS NOT VERY EFFICIENT** in external memory, since access to an element requires a random access to disk. **PLEASE CHECK** whether an ordered sequence, as provided by stxxl::map, may not be the better replacement for your application. However, if you are willing to provide **a lot of buffer memory** then the hash map can cache many items in internal memory, and direct hash-based access will be very fast. Also, with SSDs one may be able to reduce the block size. + +The implementation of the unordered hash_map is experimental, and help for improving, fixing bugs and writing documentation in it is very welcome. If you have an application, please consider **THROUGHLY TESTING** the implementation and patching problems. + +### Creating a STXXL Unordered Map + +To create a stxxl::unordered_map object, several template parameters are required. The first two parameters KeyType and MappedType, which are combined into a std::pair in this example, are self-explanatory, the third parameter is a *hasher class* and the fourth has to be a *comparator class* which is used to determine whether a key is smaller than another one, the fifth and sixth parameters define the subblock- and block size (in subblock items). +\snippet examples/containers/unordered_map1.cpp construction + +The hash function follows the standard std::hash signature, and returns a size_t: +\snippet examples/containers/unordered_map1.cpp hash + +Instead of the **equality comparator** as required by the C++ standard, we require a **less comparator**, because the unordered_map **sorts** bulk insertions by hash value. A simple comparator looks like: +\snippet examples/containers/unordered_map1.cpp comparator + +After construction, the standard operations of an unordered map are available as one would think, see below for a short example of some function. + +### Additional Implementation Notes + + * The implementation contains some TODO items very relevant to performance. A potential heavy user should consider fixing these. + + * As the btree, the unordered_map must keep an iterator map for updating items when they are swapped out to disk. + +TODO: write more information. + +### A minimal working example on STXXL Unordered Map + +(See \ref examples/containers/unordered_map1.cpp for the sourcecode of the following example). + +\snippet examples/containers/unordered_map1.cpp example + +\example examples/containers/unordered_map1.cpp +This example code is explained in the \ref tutorial_unordered_map section + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector.dox new file mode 100644 index 0000000000..3fe791fe6a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector.dox @@ -0,0 +1,107 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_vector.dox + * + * Usage Tutorial for STXXL + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_vector STXXL Vector + +This section introduces into the STXXL vector container (to learn more about the structure of stxxl::vector, see section \ref design_vector). + +### Creating a STXXL vector + +Before we can use a STXXL vector, we first have to define and then to instantiate a vector object. +To manage the configuration of the vector type we use the generator template. A minimal configuration is shown below - as one can see the value_type (integer in our case) is the only only stricly neccessary +parameter. See \ref design_vector_generator for additional configuration parameters and information. + +\code +typedef stxxl::VECTOR_GENERATOR::result vector; +vector my_vector; // creates empty vector object +\endcode + +### Insert elements + +We can fill the vector by calling push_back() which appends a new value at the end: + +\code +for (int i = 0; i < 1024*1024; i++) +{ + my_vector.push_back(i); +} +// my_vector stores: 0 1 2 3 4 5 6 [...] 1024*1024 +\endcode + +### Access elements + +To read and/or modify the values of a STXXL vector, simply use the []-operator: + +\code +std::cout << "element at position 99: " << my_vector[99] << std::endl; +my_vector[99] = 0; // zeroing element at position 99 +\endcode + +In addition, the STXXL vector provides different iterators to advance the vector which can be used as follows: + +\code +// create iterator which starts at the beginning of my_vector +vector::iterator iter = my_vector.begin(); +// access an element +std::cout << "first element: " << *iter << std::endl; +// go to next element +iter++; +\endcode + +Alongside with the many advantages of iterators, there are several things which need to bear in mind when using them. Details are described in \ref design_vector_notes. + +### Delete elements + +The operation pop_back() removes the last element of the vector (without returning it). The following code snippet is emptying my_vector: + +\code +// empty() returns true, if the vector is empty +while (!my_vector.empty()) { + my_vector.pop_back(); +} +\endcode + +### Determine size / Check whether vector is empty + +To determine the number of elements a vector currently stores, call size(): +\code +std::cout << "size of vector: " << my_vector.size() << std::endl; +\endcode + +To check if the vector is empty, call the empty() function which returns true in that case: +\code +std::cout << "vector empty? " << my_vector.empty() << std::endl; +\endcode + +### A minimal working example of STXXL's vector + +(See \ref examples/containers/vector1.cpp for the sourcecode of the following example). + +\snippet examples/containers/vector1.cpp example + +See \ref examples/containers/vector2.cpp for the sourcecode of a more comprehensive example. + +\example examples/containers/vector1.cpp +This example code is explained in the \ref tutorial_vector section. + +\example examples/containers/vector2.cpp +This example code is explained in the \ref tutorial_vector section. + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_billing.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_billing.dox new file mode 100644 index 0000000000..09d8b5af42 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_billing.dox @@ -0,0 +1,114 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_vector_billing.dox + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_vector_billing A Billing System for Phone Calls (stxxl::vector and stxxl::sort) + +\author Roman Dementiev (2006) + +The intended audience of this tutorial are developers or researchers who develop applications or implement algorithms processing large data sets which do not fit into the main memory of a computer. They must have basic knowledge in the theory of external memory computing and have working knowledge of C++ and an experience with programming using STL. Familiarity with key concepts of generic programming and C++ template mechanism is assumed. + +Let us start with a toy but pretty relevant problem: the phone call billing problem. You are given a sequence of event records. Each record has a time stamp (time when the event had happened), type of event ('call begin' or 'call end'), the callers number, and the destination number. The event sequence is time-ordered. Your task is to generate a bill for each subscriber that includes cost of all her calls. The solution is uncomplicated: sort the records by the callers number. Since the sort brings all records of a subscriber together, we \a scan the sorted result computing and summing up the costs of all calls of a particular subscriber. The phone companies record up to 300 million transactions per day. AT&T billing system Gecko \cite BillingLarge has to process databases with about 60 billion records, occupying 2.6 terabytes. Certainly this volume can not be sorted in the main memory of a single computer (Except may be in the main memory of an expensive supercomputer.) Therefore we need to sort those huge data sets out-of-memory. Now we show how STXXL can be useful here, since it can handle large volumes I/O efficiently. + +# STL Code + +If you are familiar with STL your the main function of bill +generation program will probably look like this: + +\code +int main(int argc, char * argv[]) +{ + if(argc < 4) // check if all parameters are given + { // in the command line + print_usage(argv[0]); + return 0; + } + // open file with the event log + std::fstream in(argv[1], std::ios::in); + // create a vector of log entries to read in + std::vector v; + // read the input file and push the records + // into the vector + std::copy(std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(v)); + // sort records by callers number + std::sort(v.begin(), v.end(), SortByCaller()); + // open bill file for output + std::fstream out(argv[3], std::ios::out); + // scan the vector and output bills + std::for_each(v.begin(), v.end(), ProduceBill(out)); + return 0; +} +\endcode + +To complete the code we need to define the log entry data type \c LogEntry, input operator \c >> for \c LogEntry, comparison functor \c SortByCaller, unary functor \c ProduceBills used for computing bills, and the \c print_usage function. + +\snippet examples/algo/phonebills.cpp prolog + +# Going Large -- Use STXXL + +In order to make the program I/O efficient we will replace the STL internal memory data structures and algorithms by their STXXL counterparts. The changes are marked with \c //! + +\code +#include //! include STXXL headers +// the rest of the code remains the same +int main(int argc, char * argv[]) +{ + if(argc < 4) // check if all parameters are given + { // in the command line + print_usage(argv[0]); + return 0; + } + // open file with the event log + std::fstream in(argv[1], std::ios::in); + // create a vector of log entries to read in + stxxl::vector v; //! use stxxl::vector instead of std::vector + // read the input file and push the records + // into the vector + std::copy(std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(v)); + // bound the main memory consumption by M + // during sorting + const unsigned M = atol(argv[2])*1024*1024; //! calculated memory limit M + // sort records by callers number + stxxl::sort(v.begin(), v.end(), SortByCaller(), M); //! use stxxl::sort instead of std::sort + // open bill file for output + std::fstream out(argv[3], std::ios::out); + // scan the vector and output bills + // the last parameter tells how many buffers + // to use for overlapping I/O and computation + stxxl::for_each(v.begin(), v.end(), ProduceBill(out), 2); //! use stxxl::for_each instead of std::for_each + return 0; +} +\endcode + +As you note the changes are minimal. Only the namespaces and some memory specific parameters had to be changed. + +See \ref examples/algo/phonebills.cpp for the full source code. The example program is automatically compiled when building STXXL, refer to \ref install on how to build programs with STXXL. + +The program \ref examples/algo/phonebills_genlog.cpp can be used to generate logs for processing with the phonebills example. + +Do not forget to configure you external memory space in file .stxxl. See \ref config. + +\example examples/algo/phonebills.cpp +This example code is explain in \ref tutorial_vector_billing + +\example examples/algo/phonebills_genlog.cpp +This example code is explain in \ref tutorial_vector_billing + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_buf.dox b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_buf.dox new file mode 100644 index 0000000000..2d278fa472 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/doc/tutorial_vector_buf.dox @@ -0,0 +1,102 @@ +// -*- mode: c++; mode: visual-line; mode: flyspell; fill-column: 100000 -*- +/*************************************************************************** + * doc/tutorial_vector_buf.dox + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +namespace stxxl { + +/** \page tutorial_vector_buf Efficient Sequential Reading and Writing to Vectors + +\author Timo Bingmann (2013) + +The stxxl::vector is a very versatile container and it allows sequential access loops like the following: + +\snippet examples/containers/vector_buf.cpp element + +However, these sequential loops using element access are not very efficient (see the experimental results below). Each \c operator[] is processed by the vector paging mechanism, and returns a writeable reference to the element. Because the reference might be modified, the vector must assume that the accessed page is dirty. Thus the read loop will actually rewrite the whole vector. + +This effect can be avoided using a const vector& or vector::const_iterator as shown in the following: + +\snippet examples/containers/vector_buf.cpp iterator + +This method is already pretty good, but one can achieve even better performance. The problem with iterators is that all accesses still go through the vector's paging algorithms, possibly updating the internal paging algorithm's state. More importantly, the access operators do not use prefetching. For this purpose STXXL provides buffered reading and writing to vector ranges. These utilized asynchronous I/O and will thus will overlap I/O with computation. + +The two basic classes to efficiently read and write vector are vector_bufreader and vector_bufwriter. Their interface is a combination of the \c stream interface and iostreams. The two classes are more conveniently accessible via vector::bufreader_type and vector::bufwriter_type. + +\snippet examples/containers/vector_buf.cpp buffered + +When using vector_bufwriter the vector's size \a should be allocated in advance. However, this is not required: when reaching the end of the vector, the buffered writer will automatically double the vector's size. Thus writing will not produce segfaults; however, doubling may go wrong for huge vectors. + +Note that the same efficiency can be achieved using stream functions: stream::vector_iterator2stream and stream::materialize also use overlapping I/O. + +As last method, which is currently supported by STXXL, one can iterate over the vector using C++11's \c auto \c for loop construct: + +\snippet examples/containers/vector_buf.cpp cxx11 + +Note that we must construct a buffered reader explicitly, because just stating "vec" would amount to using the usual iterators (with pager). Support for C++11 is still experimental. + +All source code from this example is available in \ref examples/containers/vector_buf.cpp. The program also check the sum results and measures the time. + +The following experimental results are from a machine with four disks: +\verbatim +$ vector_buf 64 +[STXXL-MSG] STXXL v1.4.0 (prerelease) +[STXXL-MSG] Disk '/data01/stxxl' is allocated, space: 162124 MiB, I/O implementation: syscall_unlink +[STXXL-MSG] Disk '/data02/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall_unlink +[STXXL-MSG] Disk '/data03/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall_unlink +[STXXL-MSG] Disk '/data04/stxxl' is allocated, space: 228881 MiB, I/O implementation: syscall_unlink +[STXXL-MSG] In total 4 disks are allocated, space: 848770 MiB +[STXXL-MSG] Starting vector element access +sum: 4393751543808 +[STXXL-MSG] Finished vector element access after 848.695 seconds. Processed 128.000 GiB @ 154.439 MiB/s +[STXXL-MSG] Starting vector iterator access +sum: 4393751543808 +[STXXL-MSG] Finished vector iterator access after 540.938 seconds. Processed 128.000 GiB @ 242.305 MiB/s +[STXXL-MSG] Starting vector buffered access +sum: 4393751543808 +[STXXL-MSG] Finished vector buffered access after 441.26 seconds. Processed 128.000 GiB @ 297.040 MiB/s +[STXXL-MSG] Starting vector C++11 loop access +sum: 4393751543808 +[STXXL-MSG] Finished vector C++11 loop access after 440.977 seconds. Processed 128.000 GiB @ 297.231 MiB/s +\endverbatim + +Obviously, buffered access to stxxl::vector most efficient, where using const_iterators is only 18\% slower. Just using element access via operator[] is not a good idea. + +The difference between the methods grows smaller then using only one disk, because the I/O bandwidth decreases: +\verbatim +$ vector_buf 64 +[STXXL-MSG] STXXL v1.4.0 (prerelease) +[STXXL-MSG] Disk '/data01/stxxl' is allocated, space: 162124 MiB, I/O implementation: syscall_unlink +[STXXL-MSG] Starting vector element access +sum: 4393751543808 +[STXXL-MSG] Finished vector element access after 2793.85 seconds. Processed 128.000 GiB @ 46.915 MiB/s +[STXXL-MSG] Starting vector iterator access +sum: 4393751543808 +[STXXL-MSG] Finished vector iterator access after 1770.75 seconds. Processed 128.000 GiB @ 74.020 MiB/s +[STXXL-MSG] Starting vector buffered access +sum: 4393751543808 +[STXXL-MSG] Finished vector buffered access after 1670.13 seconds. Processed 128.000 GiB @ 78.480 MiB/s +[STXXL-MSG] Starting vector C++11 loop access +sum: 4393751543808 +[STXXL-MSG] Finished vector C++11 loop access after 1671.53 seconds. Processed 128.000 GiB @ 78.415 MiB/s +\endverbatim + +As a last note: there is also vector_bufreader_reverse and vector::bufreader_reverse_type for buffered reading in reverse. + +\example examples/containers/vector_buf.cpp +This example code is explained in the \ref tutorial_vector_buf section. + +\example examples/containers/copy_file.cpp +This is an example of how to copy a file to another using STXXL's asynchronous I/O features. + +*/ + +} // namespace stxxl diff --git a/third-party/MQF/ThirdParty/stxxl/examples/CMakeLists.txt b/third-party/MQF/ThirdParty/stxxl/examples/CMakeLists.txt new file mode 100644 index 0000000000..ba9cfcf38f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/CMakeLists.txt @@ -0,0 +1,18 @@ +############################################################################ +# examples/CMakeLists.txt +# +# Part of the STXXL. See http://stxxl.sourceforge.net +# +# Copyright (C) 2013 Timo Bingmann +# +# 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) +############################################################################ + +add_subdirectory(algo) +add_subdirectory(applications) +add_subdirectory(common) +add_subdirectory(containers) +add_subdirectory(stream) + diff --git a/third-party/MQF/ThirdParty/stxxl/examples/algo/CMakeLists.txt b/third-party/MQF/ThirdParty/stxxl/examples/algo/CMakeLists.txt new file mode 100644 index 0000000000..dc2196aba0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/algo/CMakeLists.txt @@ -0,0 +1,16 @@ +############################################################################ +# examples/algo/CMakeLists.txt +# +# Part of the STXXL. See http://stxxl.sourceforge.net +# +# Copyright (C) 2013 Timo Bingmann +# +# 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) +############################################################################ + +stxxl_build_example(copy_and_sort_file) +stxxl_build_example(phonebills) +stxxl_build_example(phonebills_genlog) +stxxl_build_example(sort_file) diff --git a/third-party/MQF/ThirdParty/stxxl/examples/algo/copy_and_sort_file.cpp b/third-party/MQF/ThirdParty/stxxl/examples/algo/copy_and_sort_file.cpp new file mode 100644 index 0000000000..13d45da74f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/algo/copy_and_sort_file.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * examples/algo/copy_and_sort_file.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010 Andreas Beckmann + * + * 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) + **************************************************************************/ + +//! \example algo/copy_and_sort_file.cpp +//! This example imports a file into an \c stxxl::vector without copying its +//! content and then sorts it using \c stxxl::stream::sort while writing the +//! sorted output to a different file. + +#include +#include +#include +#include +#include +#include +#include + +struct my_type +{ + typedef unsigned key_type; + + key_type m_key; + char m_data[128 - sizeof(key_type)]; + + my_type() { } + my_type(key_type k) : m_key(k) { } + + static my_type min_value() + { + return my_type(std::numeric_limits::min()); + } + static my_type max_value() + { + return my_type(std::numeric_limits::max()); + } +}; + +inline bool operator < (const my_type& a, const my_type& b) +{ + return a.m_key < b.m_key; +} + +inline bool operator == (const my_type& a, const my_type& b) +{ + return a.m_key == b.m_key; +} + +struct Cmp +{ + typedef my_type first_argument_type; + typedef my_type second_argument_type; + typedef bool result_type; + bool operator () (const my_type& a, const my_type& b) const + { + return a < b; + } + static my_type min_value() + { + return my_type::min_value(); + } + static my_type max_value() + { + return my_type::max_value(); + } +}; + +std::ostream& operator << (std::ostream& o, const my_type& obj) +{ + o << obj.m_key; + return o; +} + +int main(int argc, char** argv) +{ + if (argc < 3) + { + std::cout << "Usage: " << argv[0] << " infile outfile" << std::endl; + return -1; + } + + const stxxl::internal_size_type memory_to_use = 512 * 1024 * 1024; + const stxxl::internal_size_type block_size = sizeof(my_type) * 4096; + + typedef stxxl::vector, block_size> vector_type; + + stxxl::syscall_file in_file(argv[1], stxxl::file::DIRECT | stxxl::file::RDONLY); + stxxl::syscall_file out_file(argv[2], stxxl::file::DIRECT | stxxl::file::RDWR | stxxl::file::CREAT); + vector_type input(&in_file); + vector_type output(&out_file); + output.resize(input.size()); + + typedef stxxl::stream::streamify_traits::stream_type input_stream_type; + input_stream_type input_stream = stxxl::stream::streamify(input.begin(), input.end()); + + typedef Cmp comparator_type; + typedef stxxl::stream::sort sort_stream_type; + sort_stream_type sort_stream(input_stream, comparator_type(), memory_to_use); + + vector_type::iterator o = stxxl::stream::materialize(sort_stream, output.begin(), output.end()); + STXXL_ASSERT(o == output.end()); + + if (1) { + STXXL_MSG("Checking order..."); + STXXL_MSG((stxxl::is_sorted(output.begin(), output.end(), comparator_type()) ? "OK" : "WRONG")); + } + + return 0; +} + +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills.cpp b/third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills.cpp new file mode 100644 index 0000000000..8921aee5fa --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + * examples/algo/phonebills.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2004 Roman Dementiev + * + * 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) + **************************************************************************/ + +#define USE_STXXL + +//! [prolog] +#include // STXXL header +#include // for STL std::sort +#include // for STL std::vector +#include // for std::fstream +#include // for time_t type + +#define CT_PER_MIN 2 // subscribers pay 2 cent per minute + +struct LogEntry // the event log data structure +{ + long long int from; // callers number (64 bit integer) + long long int to; // destination number (64 bit int) + time_t timestamp; // time of event + int event; // event type 1 - call started + // 2 - call ended +}; + +// input operator used for reading from the file +std::istream& operator >> (std::istream& i, LogEntry& entry) +{ + i >> entry.from; + i >> entry.to; + i >> entry.timestamp; + i >> entry.event; + return i; +} + +// output operator used for writing to file +std::ostream& operator << (std::ostream& i, const LogEntry& entry) +{ + i << entry.from << " "; + i << entry.to << " "; + i << entry.timestamp << " "; + i << entry.event; + return i; +} + +// unary function used for producing the bills +struct ProduceBill +{ + std::ostream& out; // stream for outputting + // the bills + unsigned sum; // current subscribers debit + LogEntry last; // the last record + + ProduceBill(std::ostream& o_) : out(o_), sum(0) { last.from = -1; } + + void operator () (const LogEntry& e) + { + if (last.from == e.from) + { + // either the last event was 'call started' and current event + // is 'call ended' or the last event was 'call ended' and + // current event is 'call started' + assert((last.event == 1 && e.event == 2) || + (last.event == 2 && e.event == 1)); + + if (e.event == 2) // call ended + sum += CT_PER_MIN * (unsigned int)(e.timestamp - last.timestamp) / 60; + } + else if (last.from != -1) + { + assert(last.event == 2); // must be 'call ended' + assert(e.event == 1); // must be 'call started' + + // output the total sum + out << last.from << "; " << (sum / 100) << " EUR " << (sum % 100) << + " ct" << std::endl; + + sum = 0; // reset the sum + } + + last = e; + } +}; + +struct SortByCaller +{ + // comparison function + bool operator () (const LogEntry& a, const LogEntry& b) const + { + return a.from < b.from || + (a.from == b.from && a.timestamp < b.timestamp) || + (a.from == b.from && a.timestamp == b.timestamp && + a.event < b.event); + } + // min sentinel = value which is strictly smaller that any input element + static LogEntry min_value() + { + LogEntry dummy; + dummy.from = std::numeric_limits::min(); + dummy.timestamp = std::numeric_limits::min(); + dummy.event = std::numeric_limits::min(); + return dummy; + } + // max sentinel = value which is strictly larger that any input element + static LogEntry max_value() + { + LogEntry dummy; + dummy.from = std::numeric_limits::max(); + dummy.timestamp = std::numeric_limits::max(); + dummy.event = std::numeric_limits::max(); + return dummy; + } +}; + +void print_usage(const char* program) +{ + std::cout << "Usage: " << program << " logfile main billfile" << std::endl; + std::cout << " logfile - file name of the input" << std::endl; + std::cout << " main - memory to use (in MiB)" << std::endl; + std::cout << " billfile - file name of the output" << std::endl; +} +//! [prolog] + +#ifdef USE_STXXL +typedef stxxl::vector vector_type; +#else +typedef std::vector vector_type; +#endif + +int main(int argc, char* argv[]) +{ + if (argc < 4) + { + print_usage(argv[0]); + return 0; + } + + // read from the file + std::fstream in(argv[1], std::ios::in); + vector_type v; + + std::copy(std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(v)); + + // sort by callers +#ifndef USE_STXXL + + std::sort(v.begin(), v.end(), SortByCaller()); + std::fstream out(argv[3], std::ios::out); + // output bills + std::for_each(v.begin(), v.end(), ProduceBill(out)); + +#else + const stxxl::internal_size_type M = atol(argv[2]) * 1024 * 1024; + + stxxl::sort(v.begin(), v.end(), SortByCaller(), M); + std::fstream out(argv[3], std::ios::out); + // output bills + stxxl::for_each(v.begin(), v.end(), ProduceBill(out), M / vector_type::block_type::raw_size); + +#endif + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills_genlog.cpp b/third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills_genlog.cpp new file mode 100644 index 0000000000..996ac1c3da --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/algo/phonebills_genlog.cpp @@ -0,0 +1,123 @@ +/*************************************************************************** + * examples/algo/phonebills_genlog.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2004 Roman Dementiev + * + * 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) + **************************************************************************/ + +#include + +#include +#include + +struct LogEntry +{ + long long unsigned from; // callers number + long long unsigned to; // destination number + time_t timestamp; // time of event + int event; // event type 1 - call started + // 2 - call ended +}; + +struct cmp +{ + bool operator () (const LogEntry& a, const LogEntry& b) const + { + return a.timestamp < b.timestamp; + } + static LogEntry min_value() + { + LogEntry e; + e.timestamp = std::numeric_limits::min(); + return e; + } + static LogEntry max_value() + { + LogEntry e; + e.timestamp = std::numeric_limits::max(); + return e; + } +}; + +typedef stxxl::VECTOR_GENERATOR::result vector_type; + +std::ostream& operator << (std::ostream& i, const LogEntry& entry) +{ + i << entry.from << " "; + i << entry.to << " "; + i << entry.timestamp << " "; + i << entry.event << "\n"; + return i; +} + +int main(int argc, char* argv[]) +{ + if (argc < 5) + { + STXXL_MSG("Usage: " << argv[0] << " ncalls avcalls main logfile"); + STXXL_MSG(" ncalls - number of calls"); + STXXL_MSG(" avcalls - average number of calls per client"); + STXXL_MSG(" main - memory to use (in MiB)"); + STXXL_MSG(" logfile - file name of the output"); + + return 0; + } + stxxl::internal_size_type M = atol(argv[3]) * 1024 * 1024; + const stxxl::uint64 ncalls = stxxl::atouint64(argv[1]); + const long av_calls = atol(argv[2]); + const stxxl::uint64 nclients = ncalls / av_calls; + stxxl::uint64 calls_made = 0; + + time_t now = time(NULL); + + vector_type log; + log.reserve(2 * ncalls); + + stxxl::random_number64 rnd; + + for (stxxl::uint64 number = 0; + number < nclients && calls_made < ncalls; + ++number) + { + long long int serv = std::min(rnd(av_calls * 2), (ncalls - calls_made)); + LogEntry e; + e.from = number; + + time_t cur = now; + + while (serv-- > 0) + { + cur += (time_t)(1 + rnd(3600 * 24)); + + e.to = rnd(nclients); + e.timestamp = cur; + + ++calls_made; + e.event = 1; + log.push_back(e); + + cur += (time_t)(1 + rnd(1800)); + e.timestamp = cur; + e.event = 2; + + log.push_back(e); + } + } + + // sort events in order of their appereance + stxxl::sort(log.begin(), log.end(), cmp(), M); + + std::fstream out(argv[4], std::ios::out); + + std::copy(log.begin(), log.end(), std::ostream_iterator(out)); + + STXXL_MSG("\n" << calls_made << " calls made."); + STXXL_MSG("The log is written to '" << argv[4] << "'."); + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/algo/sort_file.cpp b/third-party/MQF/ThirdParty/stxxl/examples/algo/sort_file.cpp new file mode 100644 index 0000000000..35614620d6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/algo/sort_file.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * examples/algo/sort_file.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2003 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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) + **************************************************************************/ + +//! \example algo/sort_file.cpp +//! This example imports a file into an \c stxxl::vector without copying its +//! content and then sorts it using stxxl::sort / stxxl::ksort / ... + +#include +#include +#include +#include +#include +#include + +struct my_type +{ + typedef unsigned key_type; + + key_type m_key; + char m_data[128 - sizeof(key_type)]; + + key_type key() const + { + return m_key; + } + + my_type() { } + my_type(key_type k) : m_key(k) { } + + static my_type min_value() + { + return my_type(std::numeric_limits::min()); + } + static my_type max_value() + { + return my_type(std::numeric_limits::max()); + } +}; + +inline bool operator < (const my_type& a, const my_type& b) +{ + return a.key() < b.key(); +} + +inline bool operator == (const my_type& a, const my_type& b) +{ + return a.key() == b.key(); +} + +struct Cmp +{ + typedef my_type first_argument_type; + typedef my_type second_argument_type; + typedef bool result_type; + bool operator () (const my_type& a, const my_type& b) const + { + return a < b; + } + static my_type min_value() + { + return my_type::min_value(); + } + static my_type max_value() + { + return my_type::max_value(); + } +}; + +std::ostream& operator << (std::ostream& o, const my_type& obj) +{ + o << obj.key(); + return o; +} + +int main(int argc, char** argv) +{ + if (argc < 3) + { + std::cout << "Usage: " << argv[0] << " action file" << std::endl; + std::cout << " where action is one of generate, sort, ksort, stable_sort, stable_ksort" << std::endl; + return -1; + } + + const stxxl::unsigned_type block_size = sizeof(my_type) * 4096; + + if (strcmp(argv[1], "generate") == 0) { + const my_type::key_type num_elements = 1 * 1024 * 1024; + const stxxl::unsigned_type records_in_block = block_size / sizeof(my_type); + stxxl::syscall_file f(argv[2], stxxl::file::CREAT | stxxl::file::RDWR); + my_type* array = (my_type*)stxxl::aligned_alloc(block_size); + memset(array, 0, block_size); + + my_type::key_type cur_key = num_elements; + for (unsigned i = 0; i < num_elements / records_in_block; i++) + { + for (unsigned j = 0; j < records_in_block; j++) + array[j].m_key = cur_key--; + + stxxl::request_ptr req = f.awrite((void*)array, stxxl::int64(i) * block_size, block_size); + req->wait(); + } + stxxl::aligned_dealloc(array); + } + else { +#if STXXL_PARALLEL_MULTIWAY_MERGE + STXXL_MSG("STXXL_PARALLEL_MULTIWAY_MERGE"); +#endif + stxxl::syscall_file f(argv[2], stxxl::file::DIRECT | stxxl::file::RDWR); + unsigned memory_to_use = 50 * 1024 * 1024; + typedef stxxl::vector, block_size> vector_type; + vector_type v(&f); + + /* + STXXL_MSG("Printing..."); + for(stxxl::int64 i=0; i < v.size(); i++) + STXXL_MSG(v[i].key()); + */ + + STXXL_MSG("Checking order..."); + STXXL_MSG((stxxl::is_sorted(v.begin(), v.end()) ? "OK" : "WRONG")); + + STXXL_MSG("Sorting..."); + if (strcmp(argv[1], "sort") == 0) { + stxxl::sort(v.begin(), v.end(), Cmp(), memory_to_use); +#if 0 // stable_sort is not yet implemented + } + else if (strcmp(argv[1], "stable_sort") == 0) { + stxxl::stable_sort(v.begin(), v.end(), memory_to_use); +#endif + } + else if (strcmp(argv[1], "ksort") == 0) { + stxxl::ksort(v.begin(), v.end(), memory_to_use); + } + else if (strcmp(argv[1], "stable_ksort") == 0) { + stxxl::stable_ksort(v.begin(), v.end(), memory_to_use); + } + else { + STXXL_MSG("Not implemented: " << argv[1]); + } + + STXXL_MSG("Checking order..."); + STXXL_MSG((stxxl::is_sorted(v.begin(), v.end()) ? "OK" : "WRONG")); + } + + return 0; +} + +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/examples/applications/CMakeLists.txt b/third-party/MQF/ThirdParty/stxxl/examples/applications/CMakeLists.txt new file mode 100644 index 0000000000..f178cf2bdd --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/applications/CMakeLists.txt @@ -0,0 +1,26 @@ +############################################################################ +# examples/applications/CMakeLists.txt +# +# Part of the STXXL. See http://stxxl.sourceforge.net +# +# Copyright (C) 2013 Timo Bingmann +# +# 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) +############################################################################ + +if(NOT CYGWIN AND NOT MINGW) #-tb too big to build on cygwin + + stxxl_build_example(skew3) + stxxl_build_example(skew3-lcp) + + if(MSVC AND BUILD_TESTS) + # requires /bigobj flag to build + set_target_properties(skew3 PROPERTIES COMPILE_FLAGS /bigobj) + endif() + + stxxl_test(skew3 --size 1mib random --check) + stxxl_test(skew3-lcp --size 1mib random --check) + +endif() \ No newline at end of file diff --git a/third-party/MQF/ThirdParty/stxxl/examples/applications/skew3-lcp.cpp b/third-party/MQF/ThirdParty/stxxl/examples/applications/skew3-lcp.cpp new file mode 100644 index 0000000000..74a11bede4 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/applications/skew3-lcp.cpp @@ -0,0 +1,2909 @@ +/*************************************************************************** + * examples/applications/skew3-lcp.cpp + * + * Implementation of the DC3-LCP aka skew3-lcp algorithm in external memory, + * based on the internal memory version described in Juha Kaerkkaeinen, + * Peter Sanders and Stefan Burkhardt. "Linear work suffix array construction". + * Journal of the ACM, Volume 53 Issue 6, November 2006, Pages 918-936. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2004 Jens Mehnert + * Copyright (C) 2012-2015 Timo Bingmann + * Copyright (C) 2012-2015 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using stxxl::internal_size_type; +using stxxl::external_size_type; + +/// Global variables, types and helpers. + +// limits max ram used by external data structures to 1.0 GiB the conservative +// way +internal_size_type ram_use = 0.8 * 1024 * 1024 * 1024; + +// alphabet data type +typedef unsigned char alphabet_type; + +// calculation data type +typedef external_size_type size_type; + +/// Suffix array checker for correctness verification. + +/** + * Algorithm to check whether the suffix array is correct. Loosely based on the + * ideas of Kaerkkaeinen und Burghardt, originally implemented in STXXL by Jens + * Mehnert (2004), reimplemented using triples by Timo Bingmann (2012). + * + * \param InputT is the original text, from which the suffix array was build. + * \param InputSA is the suffix array from InputT. + * \return true iff InputSA is the correct suffix array of InputT, else false. + * + * Note: ISA := The inverse of SA. + */ +template +bool sacheck(InputT& inputT, InputSA& inputSA) +{ + typedef typename InputSA::value_type offset_type; + typedef stxxl::tuple pair_type; + typedef stxxl::tuple triple_type; + + /// Pipeline Declaration + + // build tuples with index: (SA[i]) -> (i, SA[i]) + typedef stxxl::stream::counter index_counter_type; + index_counter_type index_counter; + + typedef stxxl::stream::make_tuple tuple_index_sa_type; + tuple_index_sa_type tuple_index_sa(index_counter, inputSA); + + // take (i, SA[i]) and sort to (ISA[i], i) + typedef stxxl::tuple_less2nd pair_less_type; + typedef typename stxxl::stream::sort build_isa_type; + + build_isa_type build_isa(tuple_index_sa, pair_less_type(), ram_use / 3); + + // build (ISA[i], T[i], ISA[i+1]) and sort to (i, T[SA[i]], ISA[SA[i]+1]) + typedef stxxl::tuple_less1st triple_less_type; // comparison relation + + typedef typename stxxl::stream::use_push triple_push_type; // indicator use push() + typedef typename stxxl::stream::runs_creator triple_rc_type; + typedef typename stxxl::stream::runs_merger triple_rm_type; + + triple_rc_type triple_rc(triple_less_type(), ram_use / 3); + + /// Process + + // loop 1: read ISA and check for a permutation. + // simultaneously create runs of triples by iterating ISA and T. + size_type totalSize; + { + offset_type prev_isa = (*build_isa).first; + offset_type counter = 0; + while (!build_isa.empty()) { + if ((*build_isa).second != counter) { + std::cout << "Error: suffix array is not a permutation of 0..n-1." << std::endl; + return false; + } + + ++counter; + ++build_isa; // ISA is one in front of T + + if (!build_isa.empty()) { + triple_rc.push(triple_type(prev_isa, *inputT, (*build_isa).first)); + prev_isa = (*build_isa).first; + } + ++inputT; + } + + totalSize = counter; + } + + if (totalSize == 1) return true; + + // loop 2: read triples (i,T[SA[i]],ISA[SA[i]+1]) and check for correct ordering. + triple_rm_type triple_rm(triple_rc.result(), triple_less_type(), ram_use / 3); + + { + triple_type prev_triple = *triple_rm; + size_type counter = 0; + + ++triple_rm; + + while (!triple_rm.empty()) { + const triple_type& this_triple = *triple_rm; + + if (prev_triple.second > this_triple.second) + { + // simple check of first character of suffix + std::cout << "Error: suffix array position " + << counter << " ordered incorrectly." << std::endl; + return false; + } + else if (prev_triple.second == this_triple.second) { + if (this_triple.third == (offset_type)totalSize) { + // last suffix of string must be first among those with same first character + std::cout << "Error: suffix array position " + << counter << " ordered incorrectly." << std::endl; + return false; + } + if (prev_triple.third != (offset_type)totalSize && + prev_triple.third > this_triple.third) { + // positions SA[i] and SA[i-1] has same first character but their suffixes are ordered incorrectly: + // the suffix position of SA[i] is given by ISA[SA[i]] + std::cout << "Error: suffix array position " + << counter << " ordered incorrectly." << std::endl; + return false; + } + } + + prev_triple = this_triple; + + ++triple_rm; + ++counter; + } + } + return true; +} + +/// Wrapper for suffix array checker class sachecker. + +/** + * Wrapper function for sachecker. + * + * \param InputT is the original text, from which the suffix array was built. + * \param InputSA is the suffix array from InputT. + * \return true iff InputSA is the correct suffix array of InputT, else false. + */ +template +bool sacheck_vectors(InputT& inputT, InputSA& inputSA) +{ + typename stxxl::stream::streamify_traits::stream_type streamT + = stxxl::stream::streamify(inputT.begin(), inputT.end()); + + typename stxxl::stream::streamify_traits::stream_type streamSA + = stxxl::stream::streamify(inputSA.begin(), inputSA.end()); + + return sacheck(streamT, streamSA); +} + +/// Kasai's semi external lcp array calculator. + +template +struct sa_index_stream_type +{ + typedef typename InputStream::value_type offset_type; + typedef stxxl::tuple value_type; + +private: + size_type m_counter; + + InputStream& m_input; + + value_type m_curr; + +public: + sa_index_stream_type(InputStream& input) : m_counter(0), m_input(input) + { + if (!m_input.empty()) + m_curr = value_type(*m_input, m_counter++, 0); + } + + const value_type& operator * () const + { + return m_curr; + } + + sa_index_stream_type& operator ++ () + { + ++m_input; + if (!m_input.empty()) + m_curr = value_type(*m_input, m_counter++, m_curr.first); + return *this; + } + + bool empty() const + { + return m_input.empty(); + } +}; + +/** + * Algorithm to calculate the LCP array from a string and suffix array in linear + * time. Based on the ideas of Kasai et al. (2001), implemented by Timo + * Bingmann (2012). + * + * \param &string is a reference to the input sequence container. + * \param &SA is a reference to the suffix array container. + * \param &lcp is a reference to the container which stores the lcp array + * calculated by the algorithm. + */ +template +void lcparray_stxxl_kasai(const StringContainer& string, + const SAContainer& SA, LCPContainer& lcp) +{ + assert(string.size() == SA.size()); + + /// Generate ISA. + + typedef typename StringContainer::value_type alphabet_type; + typedef typename SAContainer::value_type offset_type; + typedef typename SAContainer::size_type size_type; + + static const std::size_t block_size = sizeof(offset_type) * 1024 * 1024 / 2; + + typedef stxxl::tuple offset_pair_type; + typedef stxxl::tuple offset_triple_type; + + // define stream iterating over a STXXL vector + typedef stxxl::stream::iterator2stream sa_stream_type; + sa_stream_type sa_stream(SA.begin(), SA.end()); + sa_index_stream_type sa_index_stream(sa_stream); + + // comparator for ISA sort: 1st component ascending + typedef stxxl::tuple_less1st offset_triple_less1st_type; + offset_triple_less1st_type offset_triple_less1st; + + // runs creator for ISA stream + typedef stxxl::stream::sort, offset_triple_less1st_type, block_size> isa_sort_type; + isa_sort_type isa_sort(sa_index_stream, offset_triple_less1st, ram_use / 8, ram_use / 8); + + /// Output sorter. + + typedef stxxl::tuple_less1st_less2nd offset_pair_less1st_type; + offset_pair_less1st_type offset_pair_less1st; + + typedef stxxl::sorter lcp_sorter_type; + lcp_sorter_type lcp_sorter(offset_pair_less1st, ram_use / 8); + + /// Kasai algorithm: iterate over ISA. + + { + size_type h = 0; // current height + size_type i = 0; // ISA index counter; + + lcp_sorter.push(offset_pair_type(0, 0)); + + std::vector stringRAM(string.begin(), string.end()); + + while (!isa_sort.empty()) { + offset_type k = isa_sort->second; // k = ISA[i] + + if (k > offset_type(0)) { + size_type j = isa_sort->third; // j = SA[k-1]; + + while (i + h < stringRAM.size() && j + h < stringRAM.size() && + stringRAM[i + h] == stringRAM[j + h]) + h++; + + lcp_sorter.push(offset_pair_type(k, h)); + } + if (h > 0) h--; + + ++isa_sort, ++i; + } + } + + /// Collect output. + + stxxl::stream::choose pair_2nd_stream(lcp_sorter); + + lcp.resize(string.size()); + lcp_sorter.sort(); + + stxxl::stream::materialize(pair_2nd_stream, lcp.begin(), lcp.end()); +} + +namespace algo { + +/* + * Class which implements the DC3-LCP aka skew3-lcp algorithm. + * T := input string. The recursion works as follows: + * Step 1: a) convert T into stream of quadruples (i,T[i],T[i+1],T[i+2],T[i+3]) (-> make_quads class) + * b) pick all mod1/mod2 triples T[i],T[i+1],T[i+2] at position i mod 3 != 0 (-> extract_mod12 class) + * b) sort mod1/mod2 triples lexicographically (-> build_sa class) + * c) give mod1/mod2 triples lexicographical ascending names and + * save overlap of neighbored triples in LCPn (-> naming class) + * d) check lexicographical names for uniqueness (-> naming class) + * If yes: proceed with Step 2, If no: set T' := "lexicographical names" and recurse + (i.e. run Step 1 again) which returns SA12 := SA(T') and LCP12 := LCPn(T') + * Step 2: a) by sorting the lexicographical names n we receive ranks r which represent ISA. + * b) construct mod0-quints, mod1-quads and mod2-quints (-> build_sa class) + * c) prepare for merging by: + * sort mod0-quints by 2 components, sort mod1-quads / mod2-quints by one component (-> build_sa class) + * c) merge {mod0-quints, mod1-quads, mod2-quints} and + * compare first characters of tuples, + * perform an RMQ on LCP12, + * perform an RMQ on LCPn (-> merge_sa_lcp class) + * Step 3: a) return Suffix and LCP array of T + * + * \param offset_type later suffix array data type + */ +template +class skew +{ +public: + /// Types and comparators needed by the skew algorithm. + + struct intPair + { + offset_type id, i, j; + + intPair() { } + + intPair(offset_type _id, offset_type _i, offset_type _j) + : id(_id), i(_i), j(_j) { } + }; + + struct leftRmq + { + offset_type id, i, j, k; + + leftRmq() { } + + leftRmq(offset_type _id, offset_type _i, offset_type _j, offset_type _k) + : id(_id), i(_i), j(_j), k(_k) { } + }; + + struct leftRmqComparator + { + bool operator () (const leftRmq& a, const leftRmq& b) const + { + return a.k < b.k; + } + + leftRmq min_value() const + { + return leftRmq(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + leftRmq max_value() const + { + return leftRmq(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } + }; + + struct rightRmq + { + offset_type id, i, j, k, tmpK; + + rightRmq() { } + + rightRmq(offset_type _id, offset_type _i, offset_type _j, offset_type _k, offset_type _tmpK) + : id(_id), i(_i), j(_j), k(_k), tmpK(_tmpK) { } + }; + + struct rightRmqComparator + { + bool operator () (const rightRmq& a, const rightRmq& b) const + { + return a.k < b.k; + } + + rightRmq min_value() const + { + return rightRmq(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + rightRmq max_value() const + { + return rightRmq(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } + }; + + struct finalPair + { + offset_type id, min; + + finalPair() { } + + finalPair(offset_type _id, offset_type _min) + : id(_id), min(_min) { } + }; + + struct finalPairComparator + { + bool operator () (const finalPair& a, const finalPair& b) const + { + return a.id < b.id; + } + + finalPair min_value() const + { + return finalPair(std::numeric_limits::min(), + std::numeric_limits::min()); + } + + finalPair max_value() const + { + return finalPair(std::numeric_limits::max(), + std::numeric_limits::max()); + } + }; + + struct two_tuple + { + offset_type first, second; + + two_tuple() { } + + two_tuple(offset_type _first, offset_type _second) + : first(_first), second(_second) { } + }; + + struct l3Tuple + { + offset_type first; + bool second; + offset_type third, fourth; + + l3Tuple() { } + + l3Tuple(offset_type _first, bool _second, offset_type _third, offset_type _fourth) + : first(_first), second(_second), third(_third), fourth(_fourth) { } + }; + + struct l3TupleComparator + { + bool operator () (const l3Tuple& a, const l3Tuple& b) const + { + return a.third < b.third; + } + + l3Tuple min_value() const + { + return l3Tuple(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + l3Tuple max_value() const + { + return l3Tuple(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } + }; + + struct innerTuple + { + offset_type first; + bool second; + offset_type third; + + innerTuple() { } + + innerTuple(offset_type _first, bool _second, offset_type _third) + : first(_first), second(_second), third(_third) { } + }; + + struct innerTupleComparator + { + bool operator () (const innerTuple& x, const innerTuple& y) const + { + return x.third < y.third; + } + + innerTuple min_value() const + { + return innerTuple(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + innerTuple max_value() const + { + return innerTuple(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } + }; + + struct pos_pair + { + offset_type first; + bool second; + offset_type third; + + pos_pair() { } + + pos_pair(offset_type _first, bool _second, offset_type _third) + : first(_first), second(_second), third(_third) { } + }; + + struct PairComparator + { + bool operator () (const pos_pair& x, const pos_pair& y) const + { + return x.first < y.first; + } + + pos_pair min_value() const + { + return pos_pair(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + pos_pair max_value() const + { + return pos_pair(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } + }; + + // 2-tuple, 3-tuple, 4-tuple (=quads), 5-tuple(=quints) definition + typedef stxxl::tuple skew_pair_type; + typedef stxxl::tuple skew_triple_type; + typedef stxxl::tuple skew_quad_type; + typedef stxxl::tuple skew_quint_type; + + static const std::size_t block_size = sizeof(offset_type) * 1024 * 1024 / 2; + + typedef typename stxxl::VECTOR_GENERATOR::result offset_array_type; + typedef stxxl::stream::vector_iterator2stream offset_array_it_rg; + + typedef stxxl::sequence simpleDeq; + typedef stxxl::sequence pairDeq; + typedef stxxl::sequence twoDeq; + + /** + * Comparison function for the mod0 tuples. + */ + struct less_mod0 + { + typedef skew_quint_type value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if (a.second == b.second) + return a.fourth < b.fourth; + else + return a.second < b.second; + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + /** + * Counter for creating tuple indexes for example. + */ + template + struct counter + { + typedef ValueType value_type; + + value_type cnt; + + counter() : cnt(0) { } + + const value_type& operator * () const + { + return cnt; + } + + counter& operator ++ () + { + ++cnt; + return *this; + } + + bool empty() const + { + return false; + } + }; + + /** + * Compares quadruples with respect to the first component. + */ + struct less_quad_2nd + { + typedef skew_quad_type value_type; + + bool operator () (const skew_quad_type& a, const skew_quad_type& b) const + { + return a.second < b.second; + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + /** + * Compares five-tuples with respect to the second component. + */ + struct less_quint_2nd + { + typedef skew_quint_type value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + return a.second < b.second; + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + /** + * Put the (0 mod 2) [which are the 1,2 mod 3 tuples] tuples at the begin. + */ + struct less_skew + { + typedef skew_pair_type value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if ((a.first & 1) == (b.first & 1)) + return a.first < b.first; + else + return (a.first & 1) < (b.first & 1); + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + /** + * Compare two pairs by their first component. + */ + struct mod12Comparator + { + typedef skew_pair_type value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + return a.first < b.first; + } + + value_type min_value() const + { + return value_type(std::numeric_limits::min(), std::numeric_limits::min()); + } + + value_type max_value() const + { + return value_type(std::numeric_limits::max(), std::numeric_limits::max()); + } + }; + + /** + * Concatenates two streams as streamA '+' streamB containing pairs. + */ + template + class concat + { + public: + typedef StreamType value_type; + + private: + StreamA& A; + StreamB& B; + + public: + concat(StreamA& A_, StreamB& B_) : A(A_), B(B_) + { + assert(!A.empty()); + assert(!B.empty()); + } + + const value_type& operator * () const + { + if (empty()) + throw std::runtime_error("operator()* error"); + if (!A.empty()) + return *A; + return *B; + } + + concat& operator ++ () + { + assert(!empty()); + + if (!A.empty()) + ++A; + else if (!B.empty()) + ++B; + + return *this; + } + + bool empty() const + { + return ((A.empty()) && (B.empty())); + } + }; + + /** + * Sort skew_quad datatype. + */ + template + struct less_quad + { + typedef typename stxxl::tuple value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if (a.second == b.second) { + if (a.third == b.third) + return a.fourth < b.fourth; + else + return a.third < b.third; + } + else { + return a.second < b.second; + } + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + /// Skew naming. + + /** + * Check, if last three components of two quads are equal. + */ + template + static inline bool quad_eq(const quad_type& a, const quad_type& b) + { + return (a.second == b.second) && (a.third == b.third) && (a.fourth == b.fourth); + } + + /** + * Check overlapping of two triples in case of non-equality. + */ + template + static inline char quad_neq(const quad_type& a, const quad_type& b) + { + if ((a.second == b.second) && (a.third == b.third)) + return 2; + else if (a.second == b.second) + return 1; + else + return 0; + } + + /** + * Naming pipe for the conventional skew algorithm without discarding. + */ + template + class naming + { + public: + typedef typename Input::value_type quad_type; + typedef skew_pair_type value_type; + + private: + Input& A; + bool& unique; + offset_type lexname; + quad_type prev; + skew_pair_type result; + + simpleDeq* LCPn; + + public: + naming(Input& A_, bool& unique_, simpleDeq* lcp_names) : A(A_), unique(unique_), lexname(0) + { + assert(!A.empty()); + unique = true; + + LCPn = lcp_names; + prev = *A; + LCPn->push_back(0); // insert zero sentinel to hold LCPn[0] = 0 + + result.first = prev.first; + result.second = lexname; + } + + const value_type& operator * () const + { + return result; + } + + naming& operator ++ () + { + assert(!A.empty()); + + ++A; + if (A.empty()) return *this; + + quad_type curr = *A; + + if (!quad_eq(prev, curr)) { + ++lexname; + LCPn->push_back(quad_neq(prev, curr)); + } + else { + if (!A.empty() && curr.second != offset_type(0)) { + LCPn->push_back(3); + unique = false; + } + } + + result.first = curr.first; + result.second = lexname; + + prev = curr; + return *this; + } + + bool empty() const + { + return A.empty(); + } + }; + + /// Pipelining classes. + + /** + * Create tuples until one of the input streams are empty. + */ + template + class make_pairs + { + public: + typedef skew_pair_type value_type; + + private: + InputA& A; + InputB& B; + value_type result; + + public: + make_pairs(InputA& a, InputB& b) + : A(a), B(b) + { + assert(!A.empty()); + assert(!B.empty()); + if (!empty()) + result = value_type(*A, *B + add_alphabet); + } + + const value_type& operator * () const + { + return result; + } + + make_pairs& operator ++ () + { + assert(!A.empty()); + assert(!B.empty()); + + ++A; + ++B; + + if (!A.empty() && !B.empty()) + result = value_type(*A, *B + add_alphabet); + + return *this; + } + + bool empty() const + { + return (A.empty() || B.empty()); + } + }; + + /** + * Collect three characters t_i, t_{i+1}, t_{i+2} beginning at the index i. + * Since we need at least one unique endcaracter, we free the first characters, + * i.e. we map (t_i) -> (i,t_i,t_{i+1},t_{i+2}). + * + * \param Input holds all characters t_i from input string t. + * \param alphabet_type + * \param add_alphabet + */ + template + class make_quads + { + public: + typedef stxxl::tuple value_type; + + private: + Input& A; + value_type current; + offset_type counter; + unsigned int z3z; // = counter mod 3, ("+",Z/3Z) is cheaper than '%' + bool finished; + + offset_array_type& backup; + + public: + make_quads(Input& data_in_, offset_array_type& backup_) + : A(data_in_), + current(0, 0, 0, 0), + counter(0), + z3z(0), + finished(false), + backup(backup_) + { + assert(!A.empty()); + + current.first = counter; + current.second = (*A).second + add_alphabet; + ++A; + + if (!A.empty()) { + current.third = (*A).second + add_alphabet; + ++A; + } + else { + current.third = 0; + current.fourth = 0; + } + + if (!A.empty()) + current.fourth = (*A).second + add_alphabet; + else + current.fourth = 0; + } + + const value_type& operator * () const + { + return current; + } + + make_quads& operator ++ () + { + assert(!A.empty() || !finished); + + if (current.second != offset_type(0)) + backup.push_back(current.second); + + if (++z3z == 3) z3z = 0; + + current.first = ++counter; + current.second = current.third; + current.third = current.fourth; + + if (!A.empty()) + ++A; + + if (!A.empty()) + current.fourth = (*A).second + add_alphabet; + else + current.fourth = 0; + + if ((current.second == offset_type(0)) && (z3z != 1)) + finished = true; + + return *this; + } + + bool empty() const + { + return (A.empty() && finished); + } + }; + + /** + * Drop 1/3 of the input, more exactly the offsets at positions (0 mod 3). + * Index begins with 0. + */ + template + class extract_mod12 + { + public: + typedef typename Input::value_type value_type; + + private: + Input& A; + offset_type counter; + offset_type output_counter; + value_type result; + + public: + extract_mod12(Input& A_) : A(A_), counter(0), output_counter(0) + { + assert(!A.empty()); + ++A, ++counter; // skip 0 = mod0 offset + if (!A.empty()) { + result = *A; + result.first = output_counter; + } + } + + const value_type& operator * () const + { + return result; + } + + extract_mod12& operator ++ () + { + assert(!A.empty()); + + ++A, ++counter, ++output_counter; + + if (!A.empty() && (counter % 3) == 0) + ++A, ++counter; // skip mod0 offsets + + if (!A.empty()) { + result = *A; + result.first = output_counter; + } + + return *this; + } + + bool empty() const + { + return A.empty(); + } + }; + + /// Basis for (Batched) Range Minimum Queries (RMQ) construction. + + /** + * Sparse Table construction based on "Bender, M. A., and Farach-Colton, M. + * The LCA problem revisited (2000)", that creates a table of size n log n for + * every minimum in 'power-of-two'-ranges and allows to answer an RMQ(i,j) + * in O(1) by simple table lookups afterwards. + */ + class sparseTable + { + private: + std::vector > QTable; + + public: + sparseTable(std::vector& fieldVector) + { + std::size_t dimI = fieldVector.size(); // y-Dimension of QTable + std::size_t dimK = floor(log2(dimI)) + 1; // x-Dimension of QTable + createQTable(fieldVector, dimI, dimK); + } + + void createQTable(std::vector& field, std::size_t dimI, std::size_t dimK) + { + assert(dimI > 0); + assert(dimK > 0); + + std::size_t iLimit = dimI - 1; + std::size_t kLimit = dimK - 1; + std::size_t magicCounter = 0; + + QTable.push_back(std::vector()); + std::vector& tempVector = QTable[QTable.size() - 1]; + tempVector.reserve(iLimit); + + for (std::size_t i = 0; i <= iLimit; ++i) + tempVector.push_back(field[i]); + + for (std::size_t k = 0; k < kLimit; ++k) { + QTable.push_back(std::vector()); + std::vector& tempVector = QTable[QTable.size() - 1]; + tempVector.reserve(dimI - (0 + (1 << k))); + + magicCounter += (1 << k); + for (std::size_t i = 0; i + (1 << k) < dimI; ++i) { + if (QTable[k][i] <= QTable[k][i + (1 << k)]) + tempVector.push_back(QTable[k][i]); + else + tempVector.push_back(QTable[k][i + (1 << k)]); + + if (i == (iLimit - magicCounter)) + break; + } + } + } + + offset_type query(offset_type I, offset_type J) + { + std::size_t i = I; + std::size_t j = J; + std::size_t k = floor(log2(j - i + 1)); + offset_type r = QTable[k][i]; + offset_type s = QTable[k][j - (1 << k) + 1]; + + if (r <= s) return r; + else return s; + } + + void clear() + { + QTable.clear(); + } + }; + + /** + * This class uses the sparseTable construction to answer batched RMQs. + * First: calculate number_of_pages and page borders. + * Second: split up sequence of RMQs (id, i, j) into two parts based + * on page borders and sort splitted RMQs. + * Third: build up minBorderArray. + * Fourth: sort and answer RMQs with respect to their id. + * + * \param Stream1 holds a reference to a sequence of values the RMQs are performed on. + * \param Stream2 holds a reference to a sequence of RMQs. + */ + template + class sparseTableAlgo + { + typedef leftRmqComparator left_cmp; + typedef rightRmqComparator right_cmp; + typedef finalPairComparator final_cmp; + + typedef stxxl::sorter left_sorter_type; + typedef stxxl::sorter right_sorter_type; + typedef stxxl::sorter final_sorter_type; + + left_sorter_type left_sorter; + right_sorter_type right_sorter; + final_sorter_type collective_sorter; + + private: + std::vector minBorderArray; + offset_type span, end_part, numberOfPages; + + // Compare min of page k with last minBorderArray entries + offset_type getArrayMin(offset_type pageMin, offset_type k, offset_type tmpK) + { + offset_type min = pageMin; + offset_type lastPages = k - 1 - tmpK; + + if (lastPages > offset_type(0)) { + for (offset_type p = 1; p <= lastPages; ++p) { + if (min > minBorderArray[k - p]) + min = minBorderArray[k - p]; + } + } + return min; + } + + public: + sparseTableAlgo() + : left_sorter(left_cmp(), ram_use / 6), + right_sorter(right_cmp(), ram_use / 6), + collective_sorter(final_cmp(), ram_use / 6) { } + + // compute numberOfPages and their page-size (span) + void initialize(offset_type lengthOfVector) + { + offset_type M = (offset_type(ram_use / 2) / sizeof(offset_type)); + span = end_part = 0; + + assert(lengthOfVector > offset_type(0)); + + if (lengthOfVector >= offset_type((ram_use / 2) / sizeof(offset_type))) + span = 10000; + + do { + // space inequation: M - c >= v + v*log_2(v) + n/v + if ((M - (span + 1) - ((span + 1) * log2(span + 1)) - (lengthOfVector / (span + 1))) > 0) + ++span; + else + break; + } while (span < lengthOfVector); + + assert(span > offset_type(0)); + + offset_type tmp = (offset_type)lengthOfVector / span; + if ((lengthOfVector % span) != 0) { + end_part = lengthOfVector - (tmp * span); + numberOfPages = tmp + 1; + } + else { + numberOfPages = tmp; + } + } + + // split up RMQs (id, i, j)) into two parts + void splitAndSortRMQ(Stream2& pair_stream) + { + intPair temp; + offset_type left, leftBorder, right, rightBorder, id, tmpK; + bool finishedI; + + while (!pair_stream.empty()) { + temp = *pair_stream; + id = temp.id; + left = temp.i; + right = temp.j; + + finishedI = 0; + tmpK = 0; + leftBorder = 0; + rightBorder = span - 1; + + // i := left, j := right, | := left or right border, *|* := currently considered border + + for (offset_type k = 0; k < numberOfPages; ++k) { + // find beginning of RMQ (id, i, j) + if (left >= leftBorder && left <= rightBorder && finishedI == 0) { + // (trivial) case: ...*|*..i..j..*|*... + // => push leftRMQ(id, relative_i, relative_j, k) into leftRMQSorter + if (right >= leftBorder && right <= rightBorder) { + left_sorter.push(leftRmq(id, (left - leftBorder), (right - leftBorder), k)); + finishedI = 1; + break; + } + else { // case: ...|..i..*|*... => push leftRMQ(id, relative_i, relative_j, k) into leftRMQSorter + left_sorter.push(leftRmq(id, (left - leftBorder), (rightBorder - leftBorder), k)); + finishedI = 1; + tmpK = k; + + assert(numberOfPages > offset_type(1)); + + leftBorder = (rightBorder + 1); + if ((k == offset_type(numberOfPages - 2)) && (end_part > offset_type(0))) + rightBorder += end_part; + else + rightBorder += span; + + continue; + } + } + + // case: *|*.....*|*..j..|... + // => "empty" page, do nothing and keep on walking + + // case: *|*..j..|.....|.....| + // => push rightRMQ(id, relative_i, relative_j, k, tmpK) into rightRMQSorter + if (right >= leftBorder && right <= rightBorder && finishedI == 1) { + right_sorter.push(rightRmq(id, 0, (right - leftBorder), k, tmpK)); + break; + } + + assert(numberOfPages > offset_type(1)); + + leftBorder = (rightBorder + 1); + if ((k == offset_type(numberOfPages - 2)) && (end_part > offset_type(0))) + rightBorder += end_part; + else + rightBorder += span; + } + ++pair_stream; + } + left_sorter.sort(); + right_sorter.sort(); + } + + void answerTuple(Stream1& int_stream) + { + offset_type temp, min, left_min, right_min, leftBorder, rightBorder, page_min; + std::vector tempVector; + + leftBorder = 0; + rightBorder = span - 1; + + // build up minBorderArray + for (offset_type n = 0; n < numberOfPages; ++n) { + min = std::numeric_limits::max(); + + for (offset_type i = leftBorder; i <= rightBorder; ++i) { + assert(!int_stream.empty()); + + temp = *int_stream; + tempVector.push_back(temp); + + if (temp < min) + min = temp; + + if (i <= rightBorder) { + assert(!int_stream.empty()); + ++int_stream; + } + } + + minBorderArray.push_back(min); + + // create sparseoffset_typeable of tempVector + sparseTable myTable(tempVector); + + while (!left_sorter.empty() && left_sorter->k <= n) { + // getMin(i, j, k, tmpK, vector) + left_min = myTable.query(left_sorter->i, left_sorter->j); + + collective_sorter.push(finalPair(left_sorter->id, left_min)); + ++left_sorter; + } + + while (!right_sorter.empty() && right_sorter->k <= n) { + // getMin(i, j, k, tmpK, vector) + page_min = myTable.query(right_sorter->i, right_sorter->j); + + // mind previous minima in minBorderArray + right_min = getArrayMin(page_min, right_sorter->k, right_sorter->tmpK); + + collective_sorter.push(finalPair(right_sorter->id, right_min)); + ++right_sorter; + } + + tempVector.clear(); + myTable.clear(); + + leftBorder = (rightBorder + 1); + if ((n == offset_type(numberOfPages - 2)) && (end_part > offset_type(0))) + rightBorder += end_part; + else + rightBorder += span; + } + left_sorter.finish_clear(); + right_sorter.finish_clear(); + } + + void answerRMQ(simpleDeq& resultDeq) + { + finalPair tmp; + offset_type id, min; + + collective_sorter.sort(); // sort by their id's + + while (!collective_sorter.empty()) { + id = collective_sorter->id; + min = collective_sorter->min; + + if (collective_sorter.size() > 1) + ++collective_sorter; + + if (id == collective_sorter->id) { // equal id's + if (min >= collective_sorter->min) + resultDeq.push_back(collective_sorter->min); + else + resultDeq.push_back(min); + + if (collective_sorter.size() >= 1) + ++collective_sorter; + } + else { // unequal id's + resultDeq.push_back(min); + } + } + collective_sorter.finish_clear(); + minBorderArray.clear(); + } + }; + + /** + * Construct the lcp array by: + * \param lcpn defined as the overlapping of two lexicographical adjacent triples. + * \param lcp12 defined as the lcp array of the last recursive step of dc3-lcp. + * \param ISA1('old') defined as the inverse suffix array of the i%3==1 triples of the last recursive step of dc3. + * \param ISA2('old') defined as the inverse suffix array of the i%3==2 triples of the last recursive step of dc3. + * \param SA12('old') defined as the suffix array of the last recursive step of dc3. + */ + class build_lcp + { + private: + typedef l3Tuple l3_type; + typedef innerTuple inner_type; + typedef two_tuple two_tuple_type; + typedef pos_pair pos_pair_type; + + typedef l3TupleComparator l3Tuple_cmp; + typedef innerTupleComparator innerTuple_cmp; + typedef PairComparator pair_cmp; + + typedef stxxl::sorter l3_tuple_type; + typedef stxxl::sorter inner_tuple_type; + typedef stxxl::sorter pair_type; + + typedef typename simpleDeq::stream simpleDeqStream; + typedef typename pairDeq::stream pairDeqStream; + typedef typename twoDeq::stream twoDeqStream; + + //typedef single_concat s_concatenation; + typedef concat s_concatenation; + + simpleDeq l1Deq, l2Deq, l3Deq; + simpleDeq* lcpn_ptr, * isa1_ptr, * isa2_ptr, * sa12_ptr; + simpleDeqStream* l1_ptr, * l2_ptr, * l3_ptr; + + pairDeq l2RMQ; + twoDeq l3TempDeq; + + build_lcp* lcp12_ptr; + + bool finished_l2, blank; + + offset_type countl2; + offset_type result; + + public: + build_lcp(simpleDeq* lcpn, build_lcp* lcp12, simpleDeq* ISA1, simpleDeq* ISA2, simpleDeq* SA_12) + : finished_l2(false), blank(false), countl2(0) + { + lcpn_ptr = lcpn; + lcp12_ptr = lcp12; + isa1_ptr = ISA1; + isa2_ptr = ISA2; + sa12_ptr = SA_12; + + l1_ptr = NULL; + } + + void saveL1(char l) + { + l1Deq.push_back(l); + } + + void saveRMQ(offset_type r_i, offset_type r_j) + { + if (lcp12_ptr != NULL) { + if (r_j > offset_type(0)) + r_j = r_j - offset_type(1); + + assert(lcpn_ptr->size() >= r_i); + assert(lcpn_ptr->size() >= r_j); + + if (r_i <= r_j) + l2RMQ.push_back(intPair(countl2, r_i, r_j)); + else + l2RMQ.push_back(intPair(countl2, r_j, r_i)); + + ++countl2; + } + } + + void finalize() + { + if (l1_ptr != NULL) return; + + l1_ptr = new simpleDeqStream(l1Deq); + + if (lcp12_ptr != NULL) { + pairDeqStream rmqStream = l2RMQ.get_stream(); + + sparseTableAlgo sp_algo; + sp_algo.initialize(lcpn_ptr->size()); // |lcpn| == |lcp12| + sp_algo.splitAndSortRMQ(rmqStream); + sp_algo.answerTuple(*lcp12_ptr); + sp_algo.answerRMQ(l2Deq); + } + delete lcp12_ptr; + lcp12_ptr = NULL; + + l2_ptr = new simpleDeqStream(l2Deq); + + finished_l2 = true; + answerL3(); + } + + void preprocessL3(offset_type r_i, offset_type r_j) + { + l3TempDeq.push_back(two_tuple(r_i, r_j)); + } + + void answerL3() + { + assert(finished_l2); + + pairDeq rmqDeq; + + if (l2Deq.size() == 0) { + offset_type id = 0; + twoDeqStream l3Stream = l3TempDeq.get_stream(); + + while (!l3Stream.empty()) { + two_tuple tmp = *l3Stream; + offset_type& r_i = tmp.first; + offset_type& r_j = tmp.second; + + if ((r_i > offset_type(0)) && (r_j > offset_type(0))) { + r_j = r_j - offset_type(1); + if (r_i >= offset_type(lcpn_ptr->size()) || r_j >= offset_type(lcpn_ptr->size())) + rmqDeq.push_back(intPair(id, 0, 0)); + else if (r_i <= r_j) + rmqDeq.push_back(intPair(id, r_i, r_j)); + else + rmqDeq.push_back(intPair(id, r_j, r_i)); + } + else { + rmqDeq.push_back(intPair(id, 0, 0)); + } + ++l3Stream; + ++id; + } + + pairDeqStream rmqStream = rmqDeq.get_stream(); + simpleDeqStream lcpnStream = lcpn_ptr->get_stream(); + + sparseTableAlgo sp_algo; + sp_algo.initialize(lcpn_ptr->size()); // |lcpn| == |lcp12| + sp_algo.splitAndSortRMQ(rmqStream); + sp_algo.answerTuple(lcpnStream); + sp_algo.answerRMQ(l3Deq); + } + else { + simpleDeqStream l2Stream = l2Deq.get_stream(); + twoDeqStream l3Stream = l3TempDeq.get_stream(); + + l3_tuple_type l3_tuple_sorter(l3Tuple_cmp(), ram_use, ram_use / 2); + + offset_type id = 0; + + while (!l3Stream.empty()) { + two_tuple tmp = *l3Stream; + + offset_type& r_i = tmp.first; + offset_type& r_j = tmp.second; + + if (r_i > offset_type(0) && r_j > offset_type(0)) { + l3_tuple_sorter.push(l3_type(id, 0, r_i - 1, *l2Stream)); // (id, k, r_i - 1, l2) + l3_tuple_sorter.push(l3_type(id, 1, r_j - 1, *l2Stream)); // (id, k, r_j - 1, l2) + } + else { + rmqDeq.push_back(intPair(id, 0, 0)); // trivial RMQ + } + ++l2Stream; + ++l3Stream; + ++id; + } + + l3_tuple_sorter.sort(); + inner_tuple_type inner_tuple_sorter(innerTuple_cmp(), ram_use / 2, ram_use / 2); + + /// Compute SA12[...] part. + + { + simpleDeqStream sa12Stream = sa12_ptr->get_stream(); + offset_type flag = 0; + + while (!l3_tuple_sorter.empty()) { + l3_type tmp = *l3_tuple_sorter; // tmp = (id, k, r, l) + + while (flag < tmp.third) { + if (!sa12Stream.empty()) + ++sa12Stream; + + ++flag; + } + + inner_tuple_sorter.push(inner_type(tmp.first, tmp.second, (*sa12Stream + tmp.fourth))); + ++l3_tuple_sorter; + } + + l3_tuple_sorter.finish_clear(); + inner_tuple_sorter.sort(); + } + + /// Compute ISA[...] part. + + simpleDeqStream isa1Stream = isa1_ptr->get_stream(); + simpleDeqStream isa2Stream = isa2_ptr->get_stream(); + + s_concatenation isa12Stream(isa1Stream, isa2Stream); + pair_type isa_sorter(pair_cmp(), ram_use / 2, ram_use / 2); + + offset_type add = 0; + + while (!inner_tuple_sorter.empty()) { + inner_type tmp = *inner_tuple_sorter; // tmp (id, k, r) + + while (add < tmp.third) { + if (!isa12Stream.empty()) + ++isa12Stream; + + ++add; + } + + if (tmp.second == 1) { + if (tmp.third <= offset_type(sa12_ptr->size() - 1)) + isa_sorter.push(pos_pair_type(tmp.first, tmp.second, (*isa12Stream - 1))); + else + isa_sorter.push(pos_pair_type(tmp.first, tmp.second, 0)); + } + else { + if (tmp.third <= offset_type(sa12_ptr->size() - 1)) + isa_sorter.push(pos_pair_type(tmp.first, tmp.second, *isa12Stream)); + else + isa_sorter.push(pos_pair_type(tmp.first, tmp.second, 0)); + } + + ++inner_tuple_sorter; + } + + inner_tuple_sorter.finish_clear(); + isa_sorter.sort(); + + delete sa12_ptr; + + while (!isa_sorter.empty()) { + pos_pair_type tmp1 = *isa_sorter; // id, r_i, r_j + ++isa_sorter; + pos_pair_type tmp2 = *isa_sorter; + + if (tmp1.third <= tmp2.third) + rmqDeq.push_back(intPair(tmp1.first, tmp1.third, tmp2.third)); + else + rmqDeq.push_back(intPair(tmp1.first, tmp2.third, tmp1.third)); + + ++isa_sorter; + } + + isa_sorter.finish_clear(); + + pairDeqStream rmqStream = rmqDeq.get_stream(); + simpleDeqStream lcpnStream = lcpn_ptr->get_stream(); + + sparseTableAlgo sp_algo; + sp_algo.initialize(lcpn_ptr->size()); + sp_algo.splitAndSortRMQ(rmqStream); + sp_algo.answerTuple(lcpnStream); + sp_algo.answerRMQ(l3Deq); + } // else + + delete lcpn_ptr; + + l3_ptr = new simpleDeqStream(l3Deq); + + if (!l2_ptr->empty()) + result = *(*l1_ptr) + (3 * *(*l2_ptr)) + *(*l3_ptr); + else + result = *(*l1_ptr) + *(*l3_ptr); + } //answerL3() + + const offset_type& operator * () const + { + return result; + } + + build_lcp& operator ++ () + { + assert(!empty()); + assert(!l1_ptr->empty()); + assert(!l3_ptr->empty()); + + if (!l2_ptr->empty()) + ++(*l2_ptr); + + ++(*l1_ptr); + ++(*l3_ptr); + if (!l1_ptr->empty() && !l3_ptr->empty()) { + if (!l2_ptr->empty()) + result = *(*l1_ptr) + (3 * *(*l2_ptr)) + *(*l3_ptr); + else + result = *(*l1_ptr) + *(*l3_ptr); + } + else { + blank = true; + + assert(l1_ptr != NULL && l2_ptr != NULL && l3_ptr != NULL); + delete l1_ptr; + delete l2_ptr; + delete l3_ptr; + } + + return *this; + } + + bool empty() const + { + return blank; + } + }; //build_lcp() + + /** + * Create the suffix array and the lcp array from the current sub problem + * by simple comparison-based merging. More precisely: compare characters(out of + * text t) and ranks(out of ISA12) of the following parameter constellation: + * + * \param Mod0 5-tuple (quint): + * \param Mod1 4-tuple (quad): + * \param Mod2 5-tuple (quint): + */ + template + class merge_sa_lcp + { + public: + typedef offset_type value_type; + + private: + Mod0& A; + Mod1& B; + Mod2& C; + + skew_quint_type s0; + skew_quad_type s1; + skew_quint_type s2; + + int selected; + + bool exists[3]; + + offset_type index; + offset_type merge_result; + + offset_type last1_winner; + offset_type last2_winner; + + skew_quint_type last_s0; + skew_quad_type last_s1; + skew_quint_type last_s2; + + offset_type last_type; + offset_type bound; + + build_lcp* b_lcp; + + bool cmp_mod12() + { + return s1.second < s2.second; + } + + bool cmp_mod02() + { + if (s0.second == s2.third) { + if (s0.third == s2.fourth) + return s0.fifth < s2.fifth; + else + return s0.third < s2.fourth; + } + else { + return s0.second < s2.third; + } + } + + bool cmp_mod01() + { + if (s0.second == s1.third) + return s0.fourth < s1.fourth; + else + return s0.second < s1.third; + } + + /// Comparison part of merge. + + char l12_construction0(offset_type c1, offset_type c2) + { + if (last_type == offset_type(0)) { + if (last1_winner == c1) { + if (last2_winner == c2) { + b_lcp->saveRMQ(last_s0.fifth, s0.fifth); + b_lcp->preprocessL3(last_s0.fifth, s0.fifth); + return 2; + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 1; + } + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 0; + } + } + else if (last_type == offset_type(1)) { + if (last1_winner == c1) { + b_lcp->saveRMQ(last_s1.fourth, s0.fourth); + b_lcp->preprocessL3(last_s1.fourth, s0.fourth); + return 1; + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 0; + } + } + else { + if (last1_winner == c1) { + if (last2_winner == c2) { + b_lcp->saveRMQ(last_s2.fifth, s0.fifth); + b_lcp->preprocessL3(last_s2.fifth, s0.fifth); + return 2; + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 1; + } + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 0; + } + } + } + + bool l12_construction1(offset_type c1) + { + if (last_type == offset_type(0)) { + if (last1_winner == c1) { + b_lcp->saveRMQ(last_s0.fourth, s1.fourth); + b_lcp->preprocessL3(last_s0.fourth, s1.fourth); + return 1; + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 0; + } + } + else { // i.e. S1-S1 or S2-S1 + if (last_type == offset_type(1)) { + b_lcp->saveRMQ(s1.second - 1, s1.second); + b_lcp->preprocessL3(last_s1.second, s1.second); + } + else { + b_lcp->saveRMQ(s1.second - 1, s1.second); + b_lcp->preprocessL3(last_s2.second, s1.second); + } + return 0; + } + } + + char l12_construction2(offset_type c1, offset_type c2) + { + if (last_type == offset_type(0)) { + if (last1_winner == c1) { + if (last2_winner == c2) { + b_lcp->saveRMQ(last_s0.fifth, s2.fifth); + b_lcp->preprocessL3(last_s0.fifth, s2.fifth); + return 2; + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 1; + } + } + else { + b_lcp->saveRMQ(0, 0); + b_lcp->preprocessL3(0, 0); + return 0; + } + } + else { // i.e. S1-S2 or S2-S2 + if (last_type == offset_type(1)) { + b_lcp->saveRMQ(s2.second - 1, s2.second); + b_lcp->preprocessL3(last_s1.second, s2.second); + } + else { + b_lcp->saveRMQ(s2.second - 1, s2.second); + b_lcp->preprocessL3(last_s2.second, s2.second); + } + return 0; + } + } + + void get012() + { + if (cmp_mod01()) { + if (cmp_mod02()) { + selected = 0; + merge_result = s0.first; + + b_lcp->saveL1(l12_construction0(s0.second, s0.third)); + last1_winner = s0.second; + last2_winner = s0.third; + + last_type = 0; + last_s0 = s0; + } + else { + selected = 2; + merge_result = s2.first; + + b_lcp->saveL1(l12_construction2(s2.third, s2.fourth)); + last1_winner = s2.third; + last2_winner = s2.fourth; + + last_type = 2; + last_s2 = s2; + } + } + else { + if (cmp_mod12()) { + selected = 1; + merge_result = s1.first; + + b_lcp->saveL1(l12_construction1(s1.third)); + last1_winner = s1.third; + + last_type = 1; + last_s1 = s1; + } + else { + selected = 2; + merge_result = s2.first; + + b_lcp->saveL1(l12_construction2(s2.third, s2.fourth)); + last1_winner = s2.third; + last2_winner = s2.fourth; + + last_type = 2; + last_s2 = s2; + } + } + } + + void get01() + { + if (cmp_mod01()) { + selected = 0; + merge_result = s0.first; + + b_lcp->saveL1(l12_construction0(s0.second, s0.third)); + last1_winner = s0.second; + last2_winner = s0.third; + + last_type = 0; + last_s0 = s0; + } + else { + selected = 1; + merge_result = s1.first; + + b_lcp->saveL1(l12_construction1(s1.third)); + last1_winner = s1.third; + + last_type = 1; + last_s1 = s1; + } + } + + void get12() + { + if (cmp_mod12()) { + selected = 1; + merge_result = s1.first; + + b_lcp->saveL1(l12_construction1(s1.third)); + last1_winner = s1.third; + + last_type = 1; + last_s1 = s1; + } + else { + selected = 2; + merge_result = s2.first; + + b_lcp->saveL1(l12_construction2(s2.third, s2.fourth)); + last1_winner = s2.third; + last2_winner = s2.fourth; + + last_type = 2; + last_s2 = s2; + } + } + + void get02() + { + if (cmp_mod02()) { + selected = 0; + merge_result = s0.first; + + b_lcp->saveL1(l12_construction0(s0.second, s0.third)); + last1_winner = s0.second; + last2_winner = s0.third; + + last_type = 0; + last_s0 = s0; + } + else { + selected = 2; + merge_result = s2.first; + + b_lcp->saveL1(l12_construction2(s2.third, s2.fourth)); + last1_winner = s2.third; + last2_winner = s2.fourth; + + last_type = 2; + last_s2 = s2; + } + } + + void solve() + { + if (exists[0]) { + if (exists[1]) { + if (exists[2]) { + get012(); + } + else { + get01(); + } + } + else { + if (exists[2]) { + get02(); + } + else { + selected = 0; + merge_result = s0.first; + + b_lcp->saveL1(l12_construction0(s0.second, s0.third)); + last1_winner = s0.second; + last2_winner = s0.third; + + last_type = 0; + last_s0 = s0; + } + } + } + else { + if (exists[1]) { + if (exists[2]) { + get12(); + } + else { + selected = 1; + merge_result = s1.first; + + b_lcp->saveL1(l12_construction1(s1.third)); + last1_winner = s1.third; + + last_type = 1; + last_s1 = s1; + } + } + else { + if (exists[2]) { + selected = 2; + merge_result = s2.first; + + b_lcp->saveL1(l12_construction2(s2.third, s2.fourth)); + last1_winner = s2.third; + last2_winner = s2.fourth; + + last_type = 2; + last_s2 = s2; + } + else { + assert(false); + } + } + } + } + + public: + bool empty() const + { + return (A.empty() && B.empty() && C.empty()); + } + + merge_sa_lcp(Mod0& x1, Mod1& x2, Mod2& x3, build_lcp* lcp_ptr) + : A(x1), B(x2), C(x3), selected(-1), index(0) + { + assert(!A.empty()); + assert(!B.empty()); + assert(!C.empty()); + exists[0] = true; + exists[1] = true; + exists[2] = true; + s0 = *A; + s1 = *B; + s2 = *C; + + last1_winner = std::numeric_limits::max(); + last2_winner = std::numeric_limits::max(); + + last_type = 3; // last_type \in [0,2] + + last_s0 = s0; + last_s1 = s1; + last_s2 = s2; + + b_lcp = lcp_ptr; + + solve(); + } + + const value_type& operator * () const + { + return merge_result; + } + + merge_sa_lcp& operator ++ () + { + if (selected == 0) { + assert(!A.empty()); + ++A; + if (!A.empty()) + s0 = *A; + else + exists[0] = false; + } + else if (selected == 1) { + assert(!B.empty()); + ++B; + if (!B.empty()) + s1 = *B; + else + exists[1] = false; + } + else { + assert(!C.empty()); + assert(selected == 2); + ++C; + if (!C.empty()) + s2 = *C; + else + exists[2] = false; + } + + ++index; + if (!empty()) + solve(); + + return *this; + } + }; + + /** + * Helper function for computing the size of the 2/3 subproblem. + */ + static inline offset_type subp_size(size_type n) + { + return offset_type((n / 3) * 2 + ((n % 3) == 2)); + } + + /** + * Sort mod0-quints / mod1-quads / mod2-quints and + * run merge_sa_lcp class to merge them together. + * \param S input string pipe type. + * \param Mod1 mod1 tuples input pipe type. + * \param Mod2 mod2 tuples input pipe type. + */ + template + class build_sa + { + public: + typedef offset_type value_type; + static const unsigned int add_rank = 1; // free first rank to mark ranks beyond end of input + + private: + // Mod1 types + typedef typename stxxl::stream::use_push mod1_push_type; + typedef typename stxxl::stream::runs_creator mod1_runs_type; + typedef typename mod1_runs_type::sorted_runs_type sorted_mod1_runs_type; + typedef typename stxxl::stream::runs_merger mod1_rm_type; + + // Mod2 types + typedef typename stxxl::stream::use_push mod2_push_type; + typedef typename stxxl::stream::runs_creator mod2_runs_type; + typedef typename mod2_runs_type::sorted_runs_type sorted_mod2_runs_type; + typedef typename stxxl::stream::runs_merger mod2_rm_type; + + // Mod0 types + typedef typename stxxl::stream::use_push mod0_push_type; + typedef typename stxxl::stream::runs_creator mod0_runs_type; + typedef typename mod0_runs_type::sorted_runs_type sorted_mod0_runs_type; + typedef typename stxxl::stream::runs_merger mod0_rm_type; + + // Merge type + typedef merge_sa_lcp merge_sa_lcp_type; + + // Functions + less_mod0 c0; + less_quad_2nd c1; + less_quint_2nd c2; + + // Runs merger + mod1_rm_type* mod1_result; + mod2_rm_type* mod2_result; + mod0_rm_type* mod0_result; + + // Merger + merge_sa_lcp_type* vmerge_sa_lcp; + + // Input + S& source; + Mod1& mod_1; + Mod2& mod_2; + + // Tmp variables + offset_type t[3]; + offset_type old_t2; + offset_type old_mod2; + bool exists[3]; + offset_type mod_one; + offset_type mod_two; + + offset_type index; + + // Empty_flag + bool ready; + + // Result + value_type result; + + // lcp part + simpleDeq ISA1; + simpleDeq ISA2; + build_lcp* b_lcp; + + public: + build_sa(S& source_, Mod1& mod_1_, Mod2& mod_2_, size_type a_size, std::size_t memsize, + simpleDeq* lcp_names, build_lcp* lcp12, simpleDeq* SA_12) + : source(source_), mod_1(mod_1_), mod_2(mod_2_), index(0), ready(false) + { + assert(!source_.empty()); + + // Runs storage + + // input: ISA_1,2 from previous level + mod0_runs_type mod0_runs(c0, memsize / 4); + mod1_runs_type mod1_runs(c1, memsize / 4); + mod2_runs_type mod2_runs(c2, memsize / 4); + + while (!source.empty()) + { + exists[0] = false; + exists[1] = false; + exists[2] = false; + + if (!source.empty()) { + t[0] = *source; + ++source; + exists[0] = true; + } + + if (!source.empty()) { + assert(!mod_1.empty()); + t[1] = *source; + ++source; + mod_one = *mod_1 + add_rank; + ISA1.push_back(mod_one); + ++mod_1; // isa1 + exists[1] = true; + } + + if (!source.empty()) { + assert(!mod_2.empty()); + t[2] = *source; + ++source; + mod_two = *mod_2 + add_rank; + ISA2.push_back(mod_two); + ++mod_2; // isa2 + exists[2] = true; + } + + // Check special cases in the middle of "source" + // Cases are cx|xc cxx|cxx and cxxc|xxc + + assert(t[0] != offset_type(0)); + assert(t[1] != offset_type(0)); + assert(t[2] != offset_type(0)); + + // modX = isaX + // Mod 0 : (index0,char0,char1,mod1,mod2) + // Mod 1 : (index1,mod1,char1,mod2) + // Mod 2 : (index2,mod2) + + if (exists[2]) { // Nothing is missed + mod0_runs.push(skew_quint_type(index, t[0], t[1], mod_one, mod_two)); + mod1_runs.push(skew_quad_type(index + 1, mod_one, t[1], mod_two)); + + if (index != offset_type(0)) + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], mod_one)); + } + else if (exists[1]) { // Last element missed + mod0_runs.push(skew_quint_type(index, t[0], t[1], mod_one, 0)); + mod1_runs.push(skew_quad_type(index + 1, mod_one, t[1], 0)); + + if (index != offset_type(0)) + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], mod_one)); + } + else { // Only one element left + assert(exists[0]); + mod0_runs.push(skew_quint_type(index, t[0], 0, 0, 0)); + + if (index != offset_type(0)) + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], 0)); + } + + old_mod2 = mod_two; + old_t2 = t[2]; + index += 3; + } + + if ((a_size % 3) == 0) { // changed + if (index != offset_type(0)) + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, 0, 0)); + } + + if ((a_size % 3) == 1) { + mod_one = *mod_1 + add_rank; + ISA1.push_back(mod_one); + } + + mod0_runs.deallocate(); + mod1_runs.deallocate(); + mod2_runs.deallocate(); + + // Prepare for merging + mod0_result = new mod0_rm_type(mod0_runs.result(), less_mod0(), memsize / 5); + mod1_result = new mod1_rm_type(mod1_runs.result(), less_quad_2nd(), memsize / 5); + mod2_result = new mod2_rm_type(mod2_runs.result(), less_quint_2nd(), memsize / 5); + + // output: ISA_1,2 for next level + b_lcp = new build_lcp(lcp_names, lcp12, &ISA1, &ISA2, SA_12); + vmerge_sa_lcp = new merge_sa_lcp_type(*mod0_result, *mod1_result, *mod2_result, b_lcp); // S_0, S_1, S_2, b_lcp + + result = *(*vmerge_sa_lcp); + } + + const value_type& operator * () const + { + return result; + } + + build_sa& operator ++ () + { + assert(vmerge_sa_lcp != 0 && !vmerge_sa_lcp->empty()); + + ++(*vmerge_sa_lcp); + if (!vmerge_sa_lcp->empty()) { + result = *(*vmerge_sa_lcp); + } + else { + assert(vmerge_sa_lcp->empty()); + ready = true; + + assert(vmerge_sa_lcp != NULL); + delete vmerge_sa_lcp; + vmerge_sa_lcp = NULL; + + assert(mod0_result != NULL && mod1_result != NULL && mod2_result != NULL); + delete mod0_result; + mod0_result = NULL; + delete mod1_result; + mod1_result = NULL; + delete mod2_result; + mod2_result = NULL; + } + + return *this; + } + + bool empty() const + { + return ready; + } + + build_lcp * finalize_lcp() + { + assert(ready); // merge *must* be finished here + b_lcp->finalize(); + + return b_lcp; + } + }; + + /// The core of DC3-LCP aka skew3-lcp algorithm. + + /** + * The algorithm class calls all sub-parts of DC3-LCP from this section and collect their returns. + * + * \param Input type of the input pipe. + * \param block_size block size of an external sorter we use. + */ + template + class algorithm + { + public: + typedef offset_type value_type; + typedef typename Input::value_type alphabet_type; + + private: + typedef counter counter_stream_type; + // (t_i) -> (i,t_i) + typedef make_pairs make_pairs_input_type; + + bool finished; // used for empty() check + bool unique; // is the current quad array unique? + unsigned int rec_depth; // current recusion depth of the algorithm + + typedef mod12Comparator mod12cmp; + typedef stxxl::sorter, mod12cmp, block_size> mod12_sorter_type; + + typedef stxxl::stream::choose isa_second_type; + typedef build_sa buildSA_type; + typedef make_pairs precompute_isa_type; + + buildSA_type* out_sa; // points at final constructed suffix array + + private: + /// Recursion of dc3-lcp. + + template + buildSA_type * dc3_lcp(InputType1& p_Input) + { + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) + typedef make_quads make_quads_input_type; + + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) with i = 1,2 mod 3 + typedef extract_mod12 mod12_quads_input_type; + + // sort (i,t_i,t_{i+1},t_{i+2}) by (t_i,t_{i+1},t_{i+2}) + typedef typename stxxl::stream::sort, block_size> sort_mod12_input_type; + + // name (i,t_i,t_{i+1},t_{i+2}) -> (i,n_i) + typedef naming naming_input_type; + + unique = false; + offset_type sticking_length = 0; // holds length of current s^12 + + mod12_sorter_type m1_sorter(mod12cmp(), ram_use / 6); + mod12_sorter_type m2_sorter(mod12cmp(), ram_use / 6); + + // sorted mod1 runs -concat- sorted mod2 runs + typedef concat, mod12_sorter_type, mod12_sorter_type> concatenation; + + offset_array_type text; + + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) + make_quads_input_type quads_input(p_Input, text); + + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) with i = 1,2 mod 3 + mod12_quads_input_type mod12_quads_input(quads_input); + + // sort (i,t_i,t_{i+1},t_{i+2}) by (t_i,t_i+1},t_{i+2}) + sort_mod12_input_type sort_mod12_input(mod12_quads_input, less_quad(), ram_use / 6); + + // lcpn sequence + simpleDeq* lcp_names = new simpleDeq; + + // name (i,t_i,t_{i+1},t_{i+2}) -> (i,"n_i") + naming_input_type names_input(sort_mod12_input, unique, lcp_names); // fill lcp_names while lexnaming step + + // create (i, s^12[i]) + while (!names_input.empty()) { + const skew_pair_type& tmp = *names_input; + if (tmp.first & 1) + m2_sorter.push(tmp); + else + m1_sorter.push(tmp); + + ++names_input; + sticking_length = sticking_length + 1; + //sticking_length++; //old + } + + m1_sorter.sort(); + m2_sorter.sort(); + + if (!unique) { + ++rec_depth; + (std::cout << "lexicographical names are not unique -> recusion depth=" << rec_depth << std::endl).flush(); + + // compute s^12 := lexname[S[1 mod 3]] '+' lexname[S[2 mod 3]], (also known as reduced recursion string 'R') + concatenation stick_mod1mod2(m1_sorter, m2_sorter); + + buildSA_type* recType = dc3_lcp(stick_mod1mod2); + + // deallocate runs_creator and runs_creator + m1_sorter.finish_clear(); + m2_sorter.finish_clear(); + + --rec_depth; + (std::cout << "recusion depth=" << rec_depth << std::endl).flush(); + + counter_stream_type sa_loop_index; + precompute_isa_type sa_pairs(*recType, sa_loop_index); // => (SA12, i) + + // store beginning of mod2-tuples of s^12 in mod2_pos + offset_type special = (sticking_length != subp_size(text.size())); + offset_type mod2_pos = (subp_size(text.size()) >> 1) + (subp_size(text.size()) & 1) + special; + + mod12_sorter_type isa1_type(mod12cmp(), ram_use / 6); + mod12_sorter_type isa2_type(mod12cmp(), ram_use / 6); + + simpleDeq* SA12 = new simpleDeq; + + while (!sa_pairs.empty()) { + const skew_pair_type& tmp = *sa_pairs; // tmp.first is exactly the SA12 array + SA12->push_back(tmp.first); // save current SA^12 vector + if (tmp.first < mod2_pos) + isa1_type.push(tmp); + else + isa2_type.push(tmp); + + ++sa_pairs; + } + + isa1_type.finish(); + isa2_type.finish(); + + build_lcp* lcp12 = recType->finalize_lcp(); + + delete recType; + + offset_array_it_rg input(text.begin(), text.end()); + + // => (i, ISA) + isa1_type.sort(ram_use / 8); + isa2_type.sort(ram_use / 8); + + // pick ISA of (i, ISA) + isa_second_type isa1_(isa1_type); + isa_second_type isa2_(isa2_type); + + return new buildSA_type(input, isa1_, isa2_, text.size(), ram_use, lcp_names, lcp12, SA12); + } + // else (if unique) + (std::cout << "unique lexicographical names! " << std::endl).flush(); + + isa_second_type isa1(m1_sorter); + isa_second_type isa2(m2_sorter); + + offset_array_it_rg source(text.begin(), text.end()); + + // build lcp12[i] = 0 forall i -> use NULL + return new buildSA_type(source, isa1, isa2, text.size(), ram_use, lcp_names, NULL, NULL); + } //dc3-lcp() + + public: + algorithm(Input& data_in) : finished(false), rec_depth(0) + { + // (t_i) -> (i,t_i) + counter_stream_type dummy; + make_pairs_input_type pairs_input(dummy, data_in); + + this->out_sa = dc3_lcp(pairs_input); + } + + ~algorithm() + { + delete out_sa; + out_sa = NULL; + } + + const value_type& operator * () const + { + return *(*out_sa); + } + + algorithm& operator ++ () + { + assert(!out_sa->empty()); + + ++(*out_sa); + + if ((out_sa != NULL) && (out_sa->empty())) + finished = true; + + return *this; + } + + bool empty() const + { + return finished; + } + + build_lcp * finish_lcp() + { + return out_sa->finalize_lcp(); + } + }; // algorithm class +}; // skew class + +} // namespace algo + +// Helper to print out readable characters. +template +static inline std::string dumpC(alphabet_type c) +{ + std::ostringstream oss; + if (isalnum(c)) oss << '\'' << (char)c << '\''; + else oss << (int)c; + return oss.str(); +} + +/** + * Helper class which cuts input off an input stream at a specified length. + */ +template +class cut_stream +{ +public: + // same value type as input stream + typedef typename InputType::value_type value_type; + +protected: + // instance of input stream + InputType& m_input; + + // counter after which the stream ends + size_type m_count; + +public: + cut_stream(InputType& input, size_type count) + : m_input(input), m_count(count) { } + + const value_type& operator * () const + { + assert(m_count > 0); + return *m_input; + } + + cut_stream& operator ++ () + { + assert(!empty()); + --m_count; + ++m_input; + return *this; + } + + bool empty() const + { + return (m_count == 0) || m_input.empty(); + } +}; + +alphabet_type unary_generator() +{ + return 'a'; +} + +template +int process(const std::string& input_filename, const std::string& output_filename, + size_type sizelimit, bool text_output_flag, bool check_flag, bool input_verbatim) +{ + static const std::size_t block_size = sizeof(offset_type) * 1024 * 1024 / 2; + + typedef typename stxxl::VECTOR_GENERATOR::result alphabet_vector_type; + typedef typename stxxl::VECTOR_GENERATOR::result offset_vector_type; + + // input and output files (if supplied via command line) + stxxl::syscall_file* input_file = NULL, * output_file = NULL; + + // input and output vectors for suffix array construction + alphabet_vector_type input_vector; + + offset_vector_type output_vector; + offset_vector_type lcparray; + + // to verify lcparray with kasai's semi external algorithm + offset_vector_type checker_lcp; + + using stxxl::file; + + if (input_verbatim) { + // copy input verbatim into vector + input_vector.resize(input_filename.size()); + std::copy(input_filename.begin(), input_filename.end(), input_vector.begin()); + } + else if (input_filename == "random") { + if (sizelimit == std::numeric_limits::max()) { + std::cout << "You must provide -s for generated inputs." << std::endl; + return 1; + } + + // fill input vector with random bytes + input_vector.resize(sizelimit); + stxxl::random_number8_r rand8; + stxxl::generate(input_vector.begin(), input_vector.end(), rand8); + } + else if (input_filename == "unary") { + if (sizelimit == std::numeric_limits::max()) { + std::cout << "You must provide -s for generated inputs." << std::endl; + return 1; + } + + // fill input vector with a's + input_vector.resize(sizelimit); + stxxl::generate(input_vector.begin(), input_vector.end(), unary_generator); + } + else { + // define input file object and map input_vector to input_file (no copying) + input_file = new stxxl::syscall_file(input_filename, file::RDONLY | file::DIRECT); + alphabet_vector_type file_input_vector(input_file); + input_vector.swap(file_input_vector); + } + + if (output_filename.size()) { + // define output file object and map output_vector to output_file + output_file = new stxxl::syscall_file(output_filename, file::RDWR | file::CREAT | file::DIRECT); + offset_vector_type file_output_vector(output_file); + output_vector.swap(file_output_vector); + } + + // initialize and start I/O measurement + stxxl::stats* Stats = stxxl::stats::get_instance(); + stxxl::stats_data stats_begin(*Stats); + + // construct skew class with bufreader input type + typedef alphabet_vector_type::bufreader_type input_type; + typedef cut_stream cut_input_type; + typedef typename algo::skew::template algorithm skew_type; + + size_type size = input_vector.size(); + if (size > sizelimit) size = sizelimit; + + std::cout << "input size = " << size << std::endl; + + if (size + 3 >= std::numeric_limits::max()) { + std::cout << "error: input is too long for selected word size!" << std::endl; + return -1; + } + + input_type input(input_vector); + cut_input_type cut_input(input, size); + skew_type skew(cut_input); + + // make sure output vector has the right size + output_vector.resize(size); + lcparray.resize(size); + checker_lcp.resize(size); + + // write suffix array stream into output vector + stxxl::stream::materialize(skew, output_vector.begin(), output_vector.end()); + + typename algo::skew::build_lcp * lcp = skew.finish_lcp(); + stxxl::stream::materialize(*lcp, lcparray.begin(), lcparray.end()); + + std::cout << "output size = " << output_vector.size() << std::endl; + std::cout << (stxxl::stats_data(*Stats) - stats_begin); // print i/o statistics + + if (text_output_flag) { + std::cout << std::endl << "resulting suffix array:" << std::endl; + + for (size_type i = 0; i < output_vector.size(); i++) { + std::cout << i << " : " << output_vector[i] << " : "; + + // We need a const reference because operator[] would write data + const alphabet_vector_type& cinput = input_vector; + + for (size_type j = 0; output_vector[i] + j < cinput.size(); j++) + std::cout << dumpC(cinput[output_vector[i] + j]) << " "; + + std::cout << std::endl; + } + + std::cout << "resulting lcp array: \n"; + + for (size_type k = 0; k < lcparray.size(); ++k) + std::cout << k << " : " << dumpC(lcparray[k]) << std::endl; + } + + int ret = 0; + + if (check_flag) { + (std::cout << "checking suffix array... ").flush(); + + if (!sacheck_vectors(input_vector, output_vector)) { + std::cout << "failed!" << std::endl; + ret = -1; + } + else + std::cout << "ok." << std::endl; + + (std::cout << "checking lcp array... ").flush(); + + lcparray_stxxl_kasai(input_vector, output_vector, checker_lcp); + bool correct_lcp = true; + + for (unsigned int k = 0; k < lcparray.size(); ++k) { + if (checker_lcp[k] != lcparray[k]) { + std::cout << "failed!" << std::endl; + std::cout << k << ", " << checker_lcp[k] << " - " << lcparray[k] << std::endl; + correct_lcp = false; + ret = -2; + break; + } + } + + if (correct_lcp) + std::cout << "ok." << std::endl; + } + + // close file, but have to deallocate vector first! + + if (input_file) { + input_vector = alphabet_vector_type(); + delete input_file; + } + if (output_file) { + output_vector = offset_vector_type(); + delete output_file; + } + + return ret; +} + +int main(int argc, char* argv[]) +{ + stxxl::cmdline_parser cp; + + cp.set_description( + "DC3-LCP aka skew3-lcp algorithm for external memory suffix array and LCP array construction."); + cp.set_author( + "Jens Mehnert , \n" + "Timo Bingmann , \n" + "Daniel Feist "); + + std::string input_filename, output_filename; + size_type sizelimit = std::numeric_limits::max(); + bool text_output_flag = false; + bool check_flag = false; + bool input_verbatim = false; + unsigned wordsize = 32; + + cp.add_param_string("input", input_filename, + "Path to input file (or verbatim text).\n" + " The special inputs 'random' and 'unary' generate " + "such text on-the-fly."); + cp.add_flag('c', "check", check_flag, + "Check suffix array for correctness."); + cp.add_flag('t', "text", text_output_flag, + "Print out suffix array in readable text."); + cp.add_string('o', "output", output_filename, + "Output suffix array to given path."); + cp.add_flag('v', "verbatim", input_verbatim, + "Consider \"input\" as verbatim text to construct " + "suffix array on."); + cp.add_bytes('s', "size", sizelimit, + "Cut input text to given size," + "units are [b,kb,mb,gb], e.g. 100kb."); + cp.add_bytes('M', "memuse", ram_use, + "Amount of RAM to use, default: 1 GiB."); + cp.add_uint('w', "wordsize", wordsize, + "Set word size of suffix array to 32, 40 or 64 bit," + "default: 32-bit."); + + // process command line + if (!cp.process(argc, argv)) + return -1; + + if (wordsize == 32) + return process( + input_filename, output_filename, sizelimit, + text_output_flag, check_flag, input_verbatim); + else if (wordsize == 40) + return process( + input_filename, output_filename, sizelimit, + text_output_flag, check_flag, input_verbatim); + else if (wordsize == 64) + return process( + input_filename, output_filename, sizelimit, + text_output_flag, check_flag, input_verbatim); + else + std::cerr << "Invalid wordsize for suffix array: 32, 40 or 64 are allowed." << std::endl; + + return -1; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/applications/skew3.cpp b/third-party/MQF/ThirdParty/stxxl/examples/applications/skew3.cpp new file mode 100644 index 0000000000..3174201222 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/applications/skew3.cpp @@ -0,0 +1,1402 @@ +/*************************************************************************** + * examples/applications/skew3.cpp + * + * Implementation of the external memory suffix sorting algorithm DC3 aka + * skew3 as described in Roman Dementiev, Juha Kaerkkaeinen, Jens Mehnert and + * Peter Sanders. "Better External Memory Suffix Array Construction". Journal + * of Experimental Algorithmics (JEA), volume 12, 2008. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2004 Jens Mehnert + * Copyright (C) 2012-2013 Timo Bingmann + * Copyright (C) 2012-2013 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using stxxl::uint64; +using stxxl::internal_size_type; +using stxxl::external_size_type; +namespace stream = stxxl::stream; + +// 1 GiB ram used by external data structures / 1 MiB block size +internal_size_type ram_use = 1024 * 1024 * 1024; + +// alphabet data type +typedef unsigned char alphabet_type; + +// calculation data type +typedef external_size_type size_type; + +/// Suffix Array checker for correctness verification + +/** + * Algorithm to check whether the suffix array is correct. Loosely based on the + * ideas of Kaerkkaeinen und Burghardt, originally implemented in STXXL by Jens + * Mehnert (2004), reimplemented using triples by Timo Bingmann (2012). + * + * \param InputT is the original text, from which the suffix array was build + * \param InputSA is the suffix array from InputT + * + * Note: ISA := The inverse of SA + */ +template +bool sacheck(InputT& inputT, InputSA& inputSA) +{ + typedef typename InputSA::value_type offset_type; + typedef stxxl::tuple pair_type; + typedef stxxl::tuple triple_type; + + // *** Pipeline Declaration *** + + // Build tuples with index: (SA[i]) -> (i, SA[i]) + typedef stxxl::stream::counter index_counter_type; + index_counter_type index_counter; + + typedef stream::make_tuple tuple_index_sa_type; + tuple_index_sa_type tuple_index_sa(index_counter, inputSA); + + // take (i, SA[i]) and sort to (ISA[i], i) + typedef stxxl::tuple_less2nd pair_less_type; + typedef typename stream::sort build_isa_type; + + build_isa_type build_isa(tuple_index_sa, pair_less_type(), ram_use / 3); + + // build (ISA[i], T[i], ISA[i+1]) and sort to (i, T[SA[i]], ISA[SA[i]+1]) + typedef stxxl::tuple_less1st triple_less_type; // comparison relation + + typedef typename stream::use_push triple_push_type; // indicator use push() + typedef typename stream::runs_creator triple_rc_type; + typedef typename stream::runs_merger triple_rm_type; + + triple_rc_type triple_rc(triple_less_type(), ram_use / 3); + + // ************************* Process ****************************** + // loop 1: read ISA and check for a permutation. Simultaneously create runs + // of triples by iterating ISA and T. + + size_type totalSize; + { + offset_type prev_isa = (*build_isa).first; + offset_type counter = 0; + while (!build_isa.empty()) + { + if ((*build_isa).second != counter) { + std::cout << "Error: suffix array is not a permutation of 0..n-1." << std::endl; + return false; + } + + ++counter; + ++build_isa; // ISA is one in front of T + + if (!build_isa.empty()) { + triple_rc.push(triple_type(prev_isa, *inputT, (*build_isa).first)); + prev_isa = (*build_isa).first; + } + ++inputT; + } + + totalSize = counter; + } + + if (totalSize == 1) return true; + + // ************************************************************************ + // loop 2: read triples (i,T[SA[i]],ISA[SA[i]+1]) and check for correct + // ordering. + + triple_rm_type triple_rm(triple_rc.result(), triple_less_type(), ram_use / 3); + + { + triple_type prev_triple = *triple_rm; + size_type counter = 0; + + ++triple_rm; + + while (!triple_rm.empty()) + { + const triple_type& this_triple = *triple_rm; + + if (prev_triple.second > this_triple.second) + { + // simple check of first character of suffix + std::cout << "Error: suffix array position " << counter << " ordered incorrectly." << std::endl; + return false; + } + else if (prev_triple.second == this_triple.second) + { + if (this_triple.third == (offset_type)totalSize) { + // last suffix of string must be first among those with same + // first character + std::cout << "Error: suffix array position " << counter << " ordered incorrectly." << std::endl; + return false; + } + if (prev_triple.third != (offset_type)totalSize && prev_triple.third > this_triple.third) { + // positions SA[i] and SA[i-1] has same first character but + // their suffixes are ordered incorrectly: the suffix + // position of SA[i] is given by ISA[SA[i]] + std::cout << "Error: suffix array position " << counter << " ordered incorrectly." << std::endl; + return false; + } + } + + prev_triple = this_triple; + + ++triple_rm; + ++counter; + } + } + return true; +} + +template +bool sacheck_vectors(InputT& inputT, InputSA& inputSA) +{ + typename stream::streamify_traits::stream_type streamT + = stream::streamify(inputT.begin(), inputT.end()); + + typename stream::streamify_traits::stream_type streamSA + = stream::streamify(inputSA.begin(), inputSA.end()); + + return sacheck(streamT, streamSA); +} + +/// DC3 aka skew algorithm + +/* + * DC3 aka skew algorithm a short description. T := input string + * The recursion works as follows: + * Step 1: a) pick all mod1/mod2 triples (i.e. triples T[i,i+2] at position i mod 3 != 0) (-> extract_mod12 class) + * b) sort mod1/mod2 triples lexicographically (-> build_sa class) + * c) give mod1/mod2 triples lexicographical ascending names n (-> naming class) + * d) check lexicographical names for uniqueness (-> naming class) + * If yes: proceed to next Step, If no: set T := lexicographical names and run Step 1 again + * Step 2: a) by sorting the lexicographical names n we receive ranks r + * b) construct mod0-quints, mod1-quads and mod2-quints (-> build_sa class) + * c) prepare for merging by: + * sort mod0-quints by 2 components, sort mod1-quads / mod2-quints by one component (-> build_sa class) + * c) merge mod0-quints, mod1-quads and mod2-quints (-> merge_sa class) + * Step 3: a) return Suffix Array of T + * + * \param offset_type later suffix array data type + */ +template +class skew +{ +public: + // 2-tuple, 3-tuple, 4-tuple (=quads), 5-tuple(=quints) definition + typedef stxxl::tuple skew_pair_type; + typedef stxxl::tuple skew_triple_type; + typedef stxxl::tuple skew_quad_type; + typedef stxxl::tuple skew_quint_type; + + typedef typename stxxl::VECTOR_GENERATOR::result offset_array_type; + typedef stream::vector_iterator2stream offset_array_it_rg; + + /** Comparison function for the mod0 tuples. */ + struct less_mod0 + { + typedef skew_quint_type value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if (a.second == b.second) + return a.fourth < b.fourth; + else + return a.second < b.second; + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + typedef stxxl::tuple_less2nd less_mod1; + typedef stxxl::tuple_less2nd less_mod2; + + /** Put the (0 mod 2) [which are the 1,2 mod 3 tuples] tuples at the begin. */ + struct less_skew + { + typedef skew_pair_type value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if ((a.first & 1) == (b.first & 1)) + return a.first < b.first; + else + return (a.first & 1) < (b.first & 1); + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + /** Sort skew_quad datatype. */ + template + struct less_quad + { + typedef stxxl::tuple value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if (a.second == b.second) { + if (a.third == b.third) + return a.fourth < b.fourth; + else + return a.third < b.third; + } + else + return a.second < b.second; + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } + }; + + /** Check, if last two components of tree quads are equal. */ + template + static inline bool quad_eq(const quad_type& a, const quad_type& b) + { + return (a.second == b.second) && (a.third == b.third) && (a.fourth == b.fourth); + } + + /** Naming pipe for the conventional skew algorithm without discarding. */ + template + class naming + { + public: + typedef typename Input::value_type quad_type; + + typedef skew_pair_type value_type; + + private: + Input& A; + + bool& unique; + offset_type lexname; + quad_type prev; + skew_pair_type result; + + public: + naming(Input& A_, bool& unique_) + : A(A_), unique(unique_), lexname(0) + { + assert(!A.empty()); + unique = true; + + prev = *A; + result.first = prev.first; + result.second = lexname; + } + + const value_type& operator * () const + { + return result; + } + + naming& operator ++ () + { + assert(!A.empty()); + + ++A; + if (A.empty()) + return *this; + + quad_type curr = *A; + if (!quad_eq(prev, curr)) { + ++lexname; + } + else { + if (!A.empty() && curr.second != offset_type(0)) { + unique = false; + } + } + + result.first = curr.first; + result.second = lexname; + + prev = curr; + return *this; + } + + bool empty() const + { + return A.empty(); + } + }; + + /** Create tuples of 2 components until one of the input streams are empty. */ + template + class make_pairs + { + public: + typedef stxxl::tuple value_type; + + private: + InputA& A; + InputB& B; + value_type result; + + public: + make_pairs(InputA& a, InputB& b) + : A(a), B(b) + { + assert(!A.empty()); + assert(!B.empty()); + if (!empty()) { + result = value_type(*A, *B + add_alphabet); + } + } + + const value_type& operator * () const + { return result; } + + make_pairs& operator ++ () + { + assert(!A.empty()); + assert(!B.empty()); + + ++A; + ++B; + + if (!A.empty() && !B.empty()) { + result = value_type(*A, *B + add_alphabet); + } + + return *this; + } + + bool empty() const + { return (A.empty() || B.empty()); } + }; + + /** + * Collect three characters t_i, t_{i+1}, t_{i+2} beginning at the index + * i. Since we need at least one unique endcaracter, we free the first + * characters i.e. we map (t_i) -> (i,t_i,t_{i+1},t_{i+2}) + * + * \param Input holds all characters t_i from input string t + * \param alphabet_type + * \param add_alphabet + */ + template + class make_quads + { + public: + typedef stxxl::tuple value_type; + + private: + Input& A; + value_type current; + offset_type counter; + unsigned int z3z; // = counter mod 3, ("+",Z/3Z) is cheaper than % + bool finished; + + offset_array_type& text; + + public: + make_quads(Input& data_in_, offset_array_type& text_) + : A(data_in_), + current(0, 0, 0, 0), + counter(0), + z3z(0), + finished(false), + text(text_) + { + assert(!A.empty()); + + current.first = counter; + current.second = (*A).second + add_alphabet; + ++A; + + if (!A.empty()) { + current.third = (*A).second + add_alphabet; + ++A; + } + else { + current.third = 0; + current.fourth = 0; + } + + if (!A.empty()) { + current.fourth = (*A).second + add_alphabet; + } + else { + current.fourth = 0; + } + } + + const value_type& operator * () const + { return current; } + + make_quads& operator ++ () + { + assert(!A.empty() || !finished); + + if (current.second != offset_type(0)) { + text.push_back(current.second); + } + + // Calculate module + if (++z3z == 3) z3z = 0; + + current.first = ++counter; + current.second = current.third; + current.third = current.fourth; + + if (!A.empty()) + ++A; + + if (!A.empty()) { + current.fourth = (*A).second + add_alphabet; + } + else { + current.fourth = 0; + } + + // Inserts a dummy tuple for input sizes of n%3==1 + if ((current.second == offset_type(0)) && (z3z != 1)) { + finished = true; + } + + return *this; + } + + bool empty() const + { return (A.empty() && finished); } + }; + + /** Drop 1/3 of the input. More exactly the offsets at positions (0 mod + * 3). Index begins with 0. */ + template + class extract_mod12 + { + public: + typedef typename Input::value_type value_type; + + private: + Input& A; + offset_type counter; + offset_type output_counter; + value_type result; + + public: + extract_mod12(Input& A_) + : A(A_), + counter(0), + output_counter(0) + { + assert(!A.empty()); + ++A, ++counter; // skip 0 = mod0 offset + if (!A.empty()) { + result = *A; + result.first = output_counter; + } + } + + const value_type& operator * () const + { return result; } + + extract_mod12& operator ++ () + { + assert(!A.empty()); + + ++A, ++counter, ++output_counter; + + if (!A.empty() && (counter % 3) == 0) { + // skip mod0 offsets + ++A, ++counter; + } + if (!A.empty()) { + result = *A; + result.first = output_counter; + } + + return *this; + } + + bool empty() const + { return A.empty(); } + }; + + /** Create the suffix array from the current sub problem by simple + * comparison-based merging. More precisely: compare characters(out of + * text t) and ranks(out of ISA12) of the following constellation: + * Input constellation: + * \param Mod0 5-tuple (quint): + * \param Mod1 4-tuple (quad): + * \param Mod2 5-tuple (quint): + */ + template + class merge_sa + { + public: + typedef offset_type value_type; + + private: + Mod0& A; + Mod1& B; + Mod2& C; + + skew_quint_type s0; + skew_quad_type s1; + skew_quint_type s2; + + int selected; + bool done[3]; + + offset_type index; + offset_type merge_result; + + bool cmp_mod1_less_mod2() + { + assert(!done[1] && !done[2]); + + return s1.second < s2.second; + } + + bool cmp_mod0_less_mod2() + { + assert(!done[0] && !done[2]); + + if (s0.second == s2.third) { + if (s0.third == s2.fourth) + return s0.fifth < s2.fifth; + else + return s0.third < s2.fourth; + } + else + return s0.second < s2.third; + } + + bool cmp_mod0_less_mod1() + { + assert(!done[0] && !done[1]); + + if (s0.second == s1.third) + return s0.fourth < s1.fourth; + else + return s0.second < s1.third; + } + + void merge() + { + assert(!done[0] || !done[1] || !done[2]); + + if (done[0]) + { + if (done[2] || (!done[1] && cmp_mod1_less_mod2())) + { + selected = 1; + merge_result = s1.first; + } + else + { + selected = 2; + merge_result = s2.first; + } + } + else if (done[1] || cmp_mod0_less_mod1()) + { + if (done[2] || cmp_mod0_less_mod2()) + { + selected = 0; + merge_result = s0.first; + } + else + { + selected = 2; + merge_result = s2.first; + } + } + else + { + if (done[2] || cmp_mod1_less_mod2()) + { + selected = 1; + merge_result = s1.first; + } + else + { + selected = 2; + merge_result = s2.first; + } + } + + assert(!done[selected]); + } + + public: + bool empty() const + { + return (A.empty() && B.empty() && C.empty()); + } + + merge_sa(Mod0& x1, Mod1& x2, Mod2& x3) + : A(x1), B(x2), C(x3), selected(-1), index(0) + { + assert(!A.empty()); + assert(!B.empty()); + assert(!C.empty()); + done[0] = false; + done[1] = false; + done[2] = false; + s0 = *A; + s1 = *B; + s2 = *C; + + merge(); + } + + const value_type& operator * () const + { + return merge_result; + } + + merge_sa& operator ++ () + { + if (selected == 0) { + assert(!A.empty()); + ++A; + if (!A.empty()) + s0 = *A; + else + done[0] = true; + } + else if (selected == 1) { + assert(!B.empty()); + ++B; + if (!B.empty()) + s1 = *B; + else + done[1] = true; + } + else { + assert(!C.empty()); + assert(selected == 2); + ++C; + if (!C.empty()) + s2 = *C; + else + done[2] = true; + } + + ++index; + if (!empty()) + merge(); + + return *this; + } + }; + + /** Helper function for computing the size of the 2/3 subproblem. */ + static inline size_type subp_size(size_type n) + { + return (n / 3) * 2 + ((n % 3) == 2); + } + + /** + * Sort mod0-quints / mod1-quads / mod2-quints and run merge_sa class to + * merge them together. + * \param S input string pipe type. + * \param Mod1 mod1 tuples input pipe type. + * \param Mod2 mod2 tuples input pipe type. + */ + template + class build_sa + { + public: + typedef offset_type value_type; + + static const unsigned int add_rank = 1; // free first rank to mark ranks beyond end of input + + private: + // mod1 types + typedef typename stream::use_push mod1_push_type; + typedef typename stream::runs_creator mod1_runs_type; + typedef typename mod1_runs_type::sorted_runs_type sorted_mod1_runs_type; + typedef typename stream::runs_merger mod1_rm_type; + + // mod2 types + typedef typename stream::use_push mod2_push_type; + typedef typename stream::runs_creator mod2_runs_type; + typedef typename mod2_runs_type::sorted_runs_type sorted_mod2_runs_type; + typedef typename stream::runs_merger mod2_rm_type; + + // mod0 types + typedef typename stream::use_push mod0_push_type; + typedef typename stream::runs_creator mod0_runs_type; + typedef typename mod0_runs_type::sorted_runs_type sorted_mod0_runs_type; + typedef typename stream::runs_merger mod0_rm_type; + + // Merge type + typedef merge_sa merge_sa_type; + + // Functions + less_mod0 c0; + less_mod1 c1; + less_mod2 c2; + + // Runs merger + mod1_rm_type* mod1_result; + mod2_rm_type* mod2_result; + mod0_rm_type* mod0_result; + + // Merger + merge_sa_type* vmerge_sa; + + // Input + S& source; + Mod1& mod_1; + Mod2& mod_2; + + // Tmp variables + offset_type t[3]; + offset_type old_t2; + offset_type old_mod2; + bool exists[3]; + offset_type mod_one; + offset_type mod_two; + + offset_type index; + + // Empty_flag + bool ready; + + // Result + value_type result; + + public: + build_sa(S& source_, Mod1& mod_1_, Mod2& mod_2_, size_type a_size, size_t memsize) + : source(source_), mod_1(mod_1_), mod_2(mod_2_), index(0), ready(false) + { + assert(!source_.empty()); + + // Runs storage + + // input: ISA_1,2 from previous level + mod0_runs_type mod0_runs(c0, memsize / 4); + mod1_runs_type mod1_runs(c1, memsize / 4); + mod2_runs_type mod2_runs(c2, memsize / 4); + + while (!source.empty()) + { + exists[0] = false; + exists[1] = false; + exists[2] = false; + + if (!source.empty()) { + t[0] = *source; + ++source; + exists[0] = true; + } + + if (!source.empty()) { + assert(!mod_1.empty()); + t[1] = *source; + ++source; + mod_one = *mod_1 + add_rank; + ++mod_1; + exists[1] = true; + } + + if (!source.empty()) { + assert(!mod_2.empty()); + t[2] = *source; + ++source; + mod_two = *mod_2 + add_rank; + ++mod_2; + exists[2] = true; + } + + // Check special cases in the middle of "source" + // Cases are cx|xc cxx|cxx and cxxc|xxc + + assert(t[0] != offset_type(0)); + assert(t[1] != offset_type(0)); + assert(t[2] != offset_type(0)); + + // Mod 0 : (index0,char0,char1,mod1,mod2) + // Mod 1 : (index1,mod1,char1,mod2) + // Mod 2 : (index2,mod2) + + if (exists[2]) { // Nothing is missed + mod0_runs.push(skew_quint_type(index, t[0], t[1], mod_one, mod_two)); + mod1_runs.push(skew_quad_type(index + 1, mod_one, t[1], mod_two)); + + if (index != offset_type(0)) { + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], mod_one)); + } + } + else if (exists[1]) { // Last element missed + mod0_runs.push(skew_quint_type(index, t[0], t[1], mod_one, 0)); + mod1_runs.push(skew_quad_type(index + 1, mod_one, t[1], 0)); + + if (index != offset_type(0)) { + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], mod_one)); + } + } + else { // Only one element left + assert(exists[0]); + mod0_runs.push(skew_quint_type(index, t[0], 0, 0, 0)); + + if (index != offset_type(0)) { + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, t[0], 0)); + } + } + + old_mod2 = mod_two; + old_t2 = t[2]; + index += 3; + } + + if ((a_size % 3) == 0) { // changed + if (index != offset_type(0)) { + mod2_runs.push(skew_quint_type((index - 1), old_mod2, old_t2, 0, 0)); + } + } + + mod0_runs.deallocate(); + mod1_runs.deallocate(); + mod2_runs.deallocate(); + + std::cout << "merging S0 = " << mod0_runs.size() << ", S1 = " << mod1_runs.size() + << ", S2 = " << mod2_runs.size() << " tuples" << std::endl; + + // Prepare for merging + + mod0_result = new mod0_rm_type(mod0_runs.result(), less_mod0(), memsize / 5); + mod1_result = new mod1_rm_type(mod1_runs.result(), less_mod1(), memsize / 5); + mod2_result = new mod2_rm_type(mod2_runs.result(), less_mod2(), memsize / 5); + + // output: ISA_1,2 for next level + vmerge_sa = new merge_sa_type(*mod0_result, *mod1_result, *mod2_result); + + // read first suffix + result = *(*vmerge_sa); + } + + const value_type& operator * () const + { + return result; + } + + build_sa& operator ++ () + { + assert(vmerge_sa != 0 && !vmerge_sa->empty()); + + ++(*vmerge_sa); + if (!vmerge_sa->empty()) { + result = *(*vmerge_sa); + } + else { // cleaning up + assert(vmerge_sa->empty()); + ready = true; + + assert(vmerge_sa != NULL); + delete vmerge_sa, vmerge_sa = NULL; + + assert(mod0_result != NULL && mod1_result != NULL && mod2_result != NULL); + delete mod0_result, mod0_result = NULL; + delete mod1_result, mod1_result = NULL; + delete mod2_result, mod2_result = NULL; + } + + return *this; + } + + ~build_sa() + { + if (vmerge_sa) delete vmerge_sa; + + if (mod0_result) delete mod0_result; + if (mod1_result) delete mod1_result; + if (mod2_result) delete mod2_result; + } + + bool empty() const + { + return ready; + } + }; + + /** The skew algorithm. + * \param Input type of the input pipe. */ + template + class algorithm + { + public: + typedef offset_type value_type; + typedef typename Input::value_type alphabet_type; + + protected: + // finished reading final suffix array + bool finished; + + // current recursion depth + unsigned int rec_depth; + + protected: + // generate (i) sequence + typedef stxxl::stream::counter counter_stream_type; + + // Sorter + typedef stxxl::tuple_less1st mod12cmp; + typedef stxxl::sorter mod12_sorter_type; + + // Additional streaming items + typedef stream::choose isa_second_type; + typedef build_sa buildSA_type; + typedef make_pairs precompute_isa_type; + + // Real recursive skew3 implementation + // This part is the core of the skew algorithm and runs all class objects in their respective order + template + buildSA_type * skew3(RecInputType& p_Input) + { + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) + typedef make_quads make_quads_input_type; + + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) with i = 1,2 mod 3 + typedef extract_mod12 mod12_quads_input_type; + + // sort (i,t_i,t_{i+1},t_{i+2}) by (t_i,t_{i+1},t_{i+2}) + typedef typename stream::sort > sort_mod12_input_type; + + // name (i,t_i,t_{i+1},t_{i+2}) -> (i,n_i) + typedef naming naming_input_type; + + mod12_sorter_type m1_sorter(mod12cmp(), ram_use / 5); + mod12_sorter_type m2_sorter(mod12cmp(), ram_use / 5); + + // sorted mod1 runs -concatenate- sorted mod2 runs + typedef stxxl::stream::concatenate concatenation_type; + + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) + offset_array_type text; + make_quads_input_type quads_input(p_Input, text); + + // (t_i) -> (i,t_i,t_{i+1},t_{i+2}) with i = 1,2 mod 3 + mod12_quads_input_type mod12_quads_input(quads_input); + + // sort (i,t_i,t_{i+1},t_{i+2}) by (t_i,t_i+1},t_{i+2}) + sort_mod12_input_type sort_mod12_input(mod12_quads_input, less_quad(), ram_use / 5); + + // name (i,t_i,t_{i+1},t_{i+2}) -> (i,"n_i") + bool unique = false; // is the current quad array unique? + naming_input_type names_input(sort_mod12_input, unique); + + // create (i, s^12[i]) + size_type concat_length = 0; // holds length of current S_12 + while (!names_input.empty()) { + const skew_pair_type& tmp = *names_input; + if (tmp.first & 1) { + m2_sorter.push(tmp); // sorter #2 + } + else { + m1_sorter.push(tmp); // sorter #1 + } + ++names_input; + concat_length++; + } + + std::cout << "recursion string length = " << concat_length << std::endl; + + m1_sorter.sort(); + m2_sorter.sort(); + + if (!unique) + { + std::cout << "not unique -> next recursion level = " << ++rec_depth << std::endl; + + // compute s^12 := lexname[S[1 mod 3]] . lexname[S[2 mod 3]], (also known as reduced recursion string 'R') + concatenation_type concat_mod1mod2(m1_sorter, m2_sorter); + + buildSA_type* recType = skew3(concat_mod1mod2); // recursion with recursion string T' = concat_mod1mod2 lexnames + + std::cout << "exit recursion level = " << --rec_depth << std::endl; + + counter_stream_type isa_loop_index; + precompute_isa_type isa_pairs(*recType, isa_loop_index); // add index as component => (SA12, i) + + // store beginning of mod2-tuples of s^12 in mod2_pos + offset_type special = (concat_length != subp_size(text.size())); + offset_type mod2_pos = offset_type((subp_size(text.size()) >> 1) + (subp_size(text.size()) & 1) + special); + + mod12_sorter_type isa1_pair(mod12cmp(), ram_use / 5); + mod12_sorter_type isa2_pair(mod12cmp(), ram_use / 5); + + while (!isa_pairs.empty()) { + const skew_pair_type& tmp = *isa_pairs; + if (tmp.first < mod2_pos) { + if (tmp.first + special < mod2_pos) // else: special sentinel tuple is dropped + isa1_pair.push(tmp); // sorter #1 + } + else { + isa2_pair.push(tmp); // sorter #2 + } + ++isa_pairs; + } + + delete recType; + + isa1_pair.finish(); + isa2_pair.finish(); + + offset_array_it_rg input(text.begin(), text.end()); + + // => (i, ISA) + isa1_pair.sort(ram_use / 8); + isa2_pair.sort(ram_use / 8); + + // pick ISA of (i, ISA) + isa_second_type isa1(isa1_pair); + isa_second_type isa2(isa2_pair); + + // prepare and run merger + return new buildSA_type(input, isa1, isa2, text.size(), ram_use); + } + else // unique + { + std::cout << "unique names!" << std::endl; + + isa_second_type isa1(m1_sorter); + isa_second_type isa2(m2_sorter); + + offset_array_it_rg source(text.begin(), text.end()); + + // prepare and run merger + return new buildSA_type(source, isa1, isa2, text.size(), ram_use); + } + } // end of skew3() + + protected: + // Adapt (t_i) -> (i,t_i) for input to fit to recursive call + typedef make_pairs make_pairs_input_type; + + // points to final constructed suffix array generator + buildSA_type* out_sa; + + public: + algorithm(Input& data_in) + : finished(false), rec_depth(0) + { + // (t_i) -> (i,t_i) + counter_stream_type dummy; + make_pairs_input_type pairs_input(dummy, data_in); + + out_sa = skew3(pairs_input); + } + + const value_type& operator * () const + { + return *(*out_sa); + } + + algorithm& operator ++ () + { + assert(out_sa); + assert(!out_sa->empty()); + + ++(*out_sa); + + if (out_sa->empty()) { + finished = true; + delete out_sa; + out_sa = NULL; + } + return *this; + } + + ~algorithm() + { + if (out_sa) delete out_sa; + } + + bool empty() const + { + return finished; + } + }; // algorithm class +}; // skew class + +//! helper to print out readable characters. +template +static inline std::string dumpC(alphabet_type c) +{ + std::ostringstream oss; + if (isalnum(c)) oss << '\'' << (char)c << '\''; + else oss << (int)c; + return oss.str(); +} + +//! helper stream to cut input off a specified length. +template +class cut_stream +{ +public: + //! same value type as input stream + typedef typename InputType::value_type value_type; + +protected: + //! instance of input stream + InputType& m_input; + + //! counter after which the stream ends + size_type m_count; + +public: + cut_stream(InputType& input, size_type count) + : m_input(input), m_count(count) + { } + + const value_type& operator * () const + { + assert(m_count > 0); + return *m_input; + } + + cut_stream& operator ++ () + { + assert(!empty()); + --m_count; + ++m_input; + return *this; + } + + bool empty() const + { + return (m_count == 0) || m_input.empty(); + } +}; + +alphabet_type unary_generator() +{ + return 'a'; +} + +template +int process(const std::string& input_filename, const std::string& output_filename, + size_type sizelimit, + bool text_output_flag, bool check_flag, bool input_verbatim) +{ + static const size_t block_size = sizeof(offset_type) * 1024 * 1024 / 2; + + typedef typename stxxl::VECTOR_GENERATOR::result alphabet_vector_type; + typedef typename stxxl::VECTOR_GENERATOR::result offset_vector_type; + + // input and output files (if supplied via command line) + stxxl::syscall_file* input_file = NULL, * output_file = NULL; + + // input and output vectors for suffix array construction + alphabet_vector_type input_vector; + offset_vector_type output_vector; + + using stxxl::file; + + if (input_verbatim) + { + // copy input verbatim into vector + input_vector.resize(input_filename.size()); + std::copy(input_filename.begin(), input_filename.end(), input_vector.begin()); + } + else if (input_filename == "random") + { + if (sizelimit == std::numeric_limits::max()) { + std::cout << "You must provide -s for generated inputs." << std::endl; + return 1; + } + + // fill input vector with random bytes + input_vector.resize(sizelimit); + stxxl::random_number8_r rand8; + stxxl::generate(input_vector.begin(), input_vector.end(), rand8); + } + else if (input_filename == "unary") + { + if (sizelimit == std::numeric_limits::max()) { + std::cout << "You must provide -s for generated inputs." << std::endl; + return 1; + } + + // fill input vector with random bytes + input_vector.resize(sizelimit); + stxxl::generate(input_vector.begin(), input_vector.end(), unary_generator); + } + else + { + // define input file object and map input_vector to input_file (no copying) + input_file = new stxxl::syscall_file(input_filename, file::RDONLY | file::DIRECT); + alphabet_vector_type file_input_vector(input_file); + input_vector.swap(file_input_vector); + } + + if (output_filename.size()) + { + // define output file object and map output_vector to output_file + output_file = new stxxl::syscall_file(output_filename, file::RDWR | file::CREAT | file::DIRECT); + offset_vector_type file_output_vector(output_file); + output_vector.swap(file_output_vector); + } + + // I/O measurement + stxxl::stats* Stats = stxxl::stats::get_instance(); + stxxl::stats_data stats_begin(*Stats); + + // construct skew class with bufreader input type + typedef alphabet_vector_type::bufreader_type input_type; + typedef cut_stream cut_input_type; + typedef typename skew::template algorithm skew_type; + + size_type size = input_vector.size(); + if (size > sizelimit) size = sizelimit; + + std::cout << "input size = " << size << std::endl; + + if (size + 3 >= std::numeric_limits::max()) { + std::cout << "error: input is too long for selected word size!" << std::endl; + return -1; + } + + input_type input(input_vector); + cut_input_type cut_input(input, size); + skew_type skew(cut_input); + + // make sure output vector has the right size + output_vector.resize(size); + + // write suffix array stream into output vector + stream::materialize(skew, output_vector.begin(), output_vector.end()); + + std::cout << "output size = " << output_vector.size() << std::endl; + std::cout << (stxxl::stats_data(*Stats) - stats_begin); // print i/o statistics + + if (text_output_flag) + { + std::cout << std::endl << "resulting suffix array:" << std::endl; + + for (unsigned int i = 0; i < output_vector.size(); i++) { + std::cout << i << " : " << output_vector[i] << " : "; + + // We need a const reference because operator[] would write data + const alphabet_vector_type& cinput = input_vector; + + for (unsigned int j = 0; output_vector[i] + j < cinput.size(); j++) { + std::cout << dumpC(cinput[output_vector[i] + j]) << " "; + } + + std::cout << std::endl; + } + } + + int ret = 0; + + if (check_flag) + { + (std::cout << "checking suffix array... ").flush(); + + if (!sacheck_vectors(input_vector, output_vector)) { + std::cout << "failed!" << std::endl; + ret = -1; + } + else + std::cout << "ok." << std::endl; + } + + // close file, but have to deallocate vector first! + + if (input_file) { + input_vector = alphabet_vector_type(); + delete input_file; + } + if (output_file) { + output_vector = offset_vector_type(); + delete output_file; + } + + return ret; +} + +int main(int argc, char* argv[]) +{ + stxxl::cmdline_parser cp; + + cp.set_description( + "DC3 aka skew3 algorithm for external memory suffix array construction." + ); + cp.set_author( + "Jens Mehnert , " + "Timo Bingmann , " + "Daniel Feist "); + + std::string input_filename, output_filename; + size_type sizelimit = std::numeric_limits::max(); + bool text_output_flag = false; + bool check_flag = false; + bool input_verbatim = false; + unsigned wordsize = 32; + + cp.add_param_string("input", input_filename, + "Path to input file (or verbatim text).\n" + " The special inputs 'random' and 'unary' generate " + "such text on-the-fly."); + cp.add_flag('c', "check", check_flag, + "Check suffix array for correctness."); + cp.add_flag('t', "text", text_output_flag, + "Print out suffix array in readable text."); + cp.add_string('o', "output", output_filename, + "Output suffix array to given path."); + cp.add_flag('v', "verbatim", input_verbatim, + "Consider \"input\" as verbatim text to construct " + "suffix array on."); + cp.add_bytes('s', "size", sizelimit, + "Cut input text to given size, e.g. 2 GiB."); + cp.add_bytes('M', "memuse", ram_use, + "Amount of RAM to use, default: 1 GiB."); + cp.add_uint('w', "wordsize", wordsize, + "Set word size of suffix array to 32, 40 or 64 bit, " + "default: 32-bit."); + + // process command line + if (!cp.process(argc, argv)) + return -1; + + if (wordsize == 32) + return process( + input_filename, output_filename, sizelimit, + text_output_flag, check_flag, input_verbatim); + else if (wordsize == 40) + return process( + input_filename, output_filename, sizelimit, + text_output_flag, check_flag, input_verbatim); + else if (wordsize == 64) + return process( + input_filename, output_filename, sizelimit, + text_output_flag, check_flag, input_verbatim); + else + std::cerr << "Invalid wordsize for suffix array: 32, 40 or 64 are allowed." << std::endl; + + return -1; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/common/CMakeLists.txt b/third-party/MQF/ThirdParty/stxxl/examples/common/CMakeLists.txt new file mode 100644 index 0000000000..9f6823d1a2 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/common/CMakeLists.txt @@ -0,0 +1,15 @@ +############################################################################ +# examples/common/CMakeLists.txt +# +# Part of the STXXL. See http://stxxl.sourceforge.net +# +# Copyright (C) 2013 Timo Bingmann +# +# 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) +############################################################################ + +stxxl_build_example(cmdline) + +stxxl_test(cmdline test) \ No newline at end of file diff --git a/third-party/MQF/ThirdParty/stxxl/examples/common/cmdline.cpp b/third-party/MQF/ThirdParty/stxxl/examples/common/cmdline.cpp new file mode 100644 index 0000000000..3ef831547a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/common/cmdline.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + * examples/common/cmdline.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +// [example] +#include + +int main(int argc, char* argv[]) +{ + stxxl::cmdline_parser cp; + + // add description and author + cp.set_description("This may some day be a useful program, which solves " + "many serious problems of the real world and achives " + "global peace."); + cp.set_author("Timo Bingmann "); + + // add an unsigned integer option --rounds + unsigned int rounds = 0; + cp.add_uint('r', "rounds", "N", rounds, + "Run N rounds of the experiment."); + + // add a byte size argument which the user can enter like '1gi' + stxxl::uint64 a_size = 0; + cp.add_bytes('s', "size", a_size, + "Number of bytes to process."); + + // add a required parameter + std::string a_filename; + cp.add_param_string("filename", a_filename, + "A filename to process"); + + // process command line + if (!cp.process(argc, argv)) + return -1; // some error occurred and help was always written to user. + + std::cout << "Command line parsed okay." << std::endl; + + // output for debugging + cp.print_result(); + + // do something useful +} +// [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/CMakeLists.txt b/third-party/MQF/ThirdParty/stxxl/examples/containers/CMakeLists.txt new file mode 100644 index 0000000000..12fd3d07c3 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/CMakeLists.txt @@ -0,0 +1,48 @@ +############################################################################ +# examples/containers/CMakeLists.txt +# +# Part of the STXXL. See http://stxxl.sourceforge.net +# +# Copyright (C) 2013-2014 Timo Bingmann +# +# 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) +############################################################################ + +stxxl_build_example(copy_file) +stxxl_build_example(deque1) +stxxl_build_example(deque2) +stxxl_build_example(map1) +stxxl_build_example(matrix1) +stxxl_build_example(pqueue1) +stxxl_build_example(pqueue2) +stxxl_build_example(queue1) +stxxl_build_example(queue2) +stxxl_build_example(sequence1) +stxxl_build_example(sorter1) +stxxl_build_example(sorter2) +stxxl_build_example(stack1) +stxxl_build_example(stack2) +stxxl_build_example(unordered_map1) +stxxl_build_example(vector1) +stxxl_build_example(vector2) +stxxl_build_example(vector_buf) + +stxxl_test(deque1) +stxxl_test(deque2) +stxxl_test(map1) +stxxl_test(matrix1) +stxxl_test(pqueue1) +stxxl_test(pqueue2) +stxxl_test(queue1) +stxxl_test(queue2) +stxxl_test(sequence1) +stxxl_test(sorter1) +stxxl_test(sorter2) +stxxl_test(stack1) +stxxl_test(stack2) +stxxl_test(unordered_map1) +stxxl_test(vector1) +stxxl_test(vector2) +stxxl_test(vector_buf) diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/copy_file.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/copy_file.cpp new file mode 100644 index 0000000000..e5ed9fb1fe --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/copy_file.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * examples/containers/copy_file.cpp + * + * This example shows three methods to copy a file to another file using + * stxxl:vectors. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2004-2006 Roman Dementiev + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +#include +#include +#include + +void copy_file(const char* input_path, const char* output_path, unsigned int method) +{ + using stxxl::file; + + file::unlink(output_path); // delete output file + + stxxl::timer tm(true); // start a timer + + // input file object + stxxl::syscall_file InputFile(input_path, file::RDONLY | file::DIRECT); + // output file object + stxxl::syscall_file OutputFile(output_path, file::RDWR | file::CREAT | file::DIRECT); + + typedef stxxl::vector vector_type; + + std::cout << "Copying file " << input_path << " to " << output_path << std::endl; + + // InputVector is mapped to InputFile + vector_type InputVector(&InputFile); + vector_type OutputVector(&OutputFile); // OutputVector is mapped to OutputFile + + std::cout << "File " << input_path << " has size " << InputVector.size() << " bytes." << std::endl; + + if (method == 1) + { + // First method: copy vector elements. This is rather slow, because no + // prefetching is used and the vector's pager does lots of internal + // work! + + std::cout << "Using first method: copying vector elements." << std::endl; + + for (vector_type::const_iterator it = InputVector.begin(); // iterate through InputVector + it != InputVector.end(); ++it) + { + OutputVector.push_back(*it); // add the value pointed by 'it' to OutputVector + } + } + else if (method == 2) + { + // Second method: use a vector_iterator2stream to prefetch blocks in + // the input vector and a vector_bufwriter for buffered writing to the + // output. + + std::cout << "Using second method: vector_iterator2stream and vector_bufwriter." << std::endl; + + // prepare output vector's size + OutputVector.resize(InputVector.size()); + + // construct prefetching input stream + stxxl::stream::vector_iterator2stream + input(InputVector.begin(), InputVector.end()); + + // construct buffered output writer + vector_type::bufwriter_type writer(OutputVector.begin()); + + while (!input.empty()) // iterate through InputVector + { + writer << *input; + ++input; + } + writer.finish(); // flush buffers + } + else if (method == 3) + { + // Third method: use a vector_iterator2stream to prefetch blocks in the + // input vector and materialize to write the stream to the output. + + std::cout << "Using third method: vector_iterator2stream and materialize." << std::endl; + + // prepare output vector's size + OutputVector.resize(InputVector.size()); + + // construct prefetching input stream + stxxl::stream::vector_iterator2stream + input(InputVector.begin(), InputVector.end()); + + // materilize intput directly into output vector + stxxl::stream::materialize(input, OutputVector.begin(), OutputVector.end()); + } + + std::cout << "Copied in " << tm.seconds() << " at " + << (double)InputVector.size() / tm.seconds() / 1024 / 1024 << " MiB/s" << std::endl; +} + +int main(int argc, char* argv[]) +{ + if (argc < 3) + { + std::cout << "Usage: " << argv[0] << " input_file output_file [method 1-3]" << std::endl; + return -1; + } + + int method = (argc >= 4) ? atoi(argv[3]) : 3; + + copy_file(argv[1], argv[2], method); + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/deque1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/deque1.cpp new file mode 100644 index 0000000000..638f739ab8 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/deque1.cpp @@ -0,0 +1,45 @@ +/*************************************************************************** + * examples/containers/deque1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include + +int main() +{ + typedef stxxl::deque deque; + deque my_deque; + + my_deque.push_front(2); + my_deque.push_front(11); + my_deque.push_back(5); + my_deque.push_back(8); + // deque now stores: |11|2|5|8| + + std::cout << "return 'first' element: " << my_deque.front() << std::endl; // prints 11 + std::cout << "return 'last' element: " << my_deque.back() << std::endl; // prints 8 + std::cout << "random access: " << my_deque[2] << std::endl; // prints 5 + + // generate forward iterator + stxxl::deque_iterator deque_iterator = my_deque.begin(); + + // iterate over my_deque, access values and delete them afterwards + while (!my_deque.empty()) + { + std::cout << *deque_iterator << " "; + ++deque_iterator; + my_deque.pop_front(); + } + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/deque2.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/deque2.cpp new file mode 100644 index 0000000000..3ffc9887a1 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/deque2.cpp @@ -0,0 +1,65 @@ +/*************************************************************************** + * examples/containers/deque2.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include + +int main() +{ + typedef stxxl::deque deque; + deque my_deque; + + unsigned int random, p, x; + unsigned int smaller_left = 0; + unsigned int smaller_right = 0; + stxxl::random_number32 rand32; + stxxl::uint64 number_of_elements = 8 * 1024 * 1024; + + // fill deque with random integer values + for (stxxl::uint64 i = 0; i < number_of_elements; i++) + { + random = rand32(); // produce random integer from intervall [0,2^32) + my_deque.push_front(random); + } + + stxxl::deque_iterator deque_iterator = my_deque.begin(); + + // Access random element x at position p(x) in the deque + p = (unsigned int)(rand32() % number_of_elements); + x = my_deque[p]; + + // Count number of smaller elements from the front to p(x) - 1 + for (stxxl::uint64 j = 0; j < p; j++) + { + if (*deque_iterator < x) + { + smaller_left += 1; + } + ++deque_iterator; + } + + ++deque_iterator; + + // Count number of smaller elements from p(x) + 1 to the end + for (stxxl::uint64 k = p + 1; k < number_of_elements - 1; k++) + { + if (*deque_iterator < x) + { + smaller_right += 1; + } + ++deque_iterator; + } + + STXXL_MSG("smaller left: " << smaller_left << ", smaller right: " << smaller_right); + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/map1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/map1.cpp new file mode 100644 index 0000000000..d8c23efe50 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/map1.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * examples/containers/map1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include + +#define DATA_NODE_BLOCK_SIZE (4096) +#define DATA_LEAF_BLOCK_SIZE (4096) + +//! [comparator] +struct CompareGreater +{ + bool operator () (const int& a, const int& b) const + { return a > b; } + + static int max_value() + { return std::numeric_limits::min(); } +}; +//! [comparator] + +int main() +{ + // template parameter + typedef stxxl::map map_type; + + // Constructor map(node_cache_size_in_bytes, leaf_cache_size_in_bytes) + map_type my_map((map_type::node_block_type::raw_size)*3, (map_type::leaf_block_type::raw_size)*3); + + my_map.insert(std::pair(1, 'a')); + my_map.insert(std::pair(2, 'b')); + my_map.insert(std::pair(3, 'c')); + my_map.insert(std::pair(4, 'd')); + + my_map.erase(3); + + map_type::iterator iter; + + std::cout << "my_map contains:\n"; + for (iter = my_map.begin(); iter != my_map.end(); ++iter) + { + std::cout << iter->first << " => " << iter->second << std::endl; + } + + map_type::iterator iter_low, iter_up; + + iter_low = my_map.lower_bound(1); // iter_low points to (1,a) in this case + iter_up = my_map.upper_bound(3); // iter_up points to (2,b) in this case + + std::cout << "lower bound " << iter_low->second << ", upper bound " << iter_up->second << std::endl; + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/matrix1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/matrix1.cpp new file mode 100644 index 0000000000..67f9a82852 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/matrix1.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * examples/containers/matrix1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include + +int main() +{ + // Matrix dimensions + int height = 3; + int width = 3; + + int internal_memory = 64 * 1024 * 1024; + const int small_block_order = 32; // must be multiple of matrix valueType in bits + + typedef stxxl::block_scheduler > block_schedular_type; + typedef stxxl::matrix matrix_type; + + block_schedular_type my_bs(internal_memory); + + // Create 3 matrices with given dimensions + matrix_type A(my_bs, height, width); + matrix_type B(my_bs, height, width); + matrix_type C(my_bs, height, width); + + typedef matrix_type::row_major_iterator row_iterator; + + int i = 0; + + // Fill matrix A with values 0,1,2,3,... + for (row_iterator it_A = A.begin(); it_A != A.end(); ++it_A, ++i) + { + *it_A = i; + } + + i = 0; + + // Fill matrix B with values 0,2,4,8,... + for (row_iterator it_B = B.begin(); it_B != B.end(); ++it_B, ++i) + { + *it_B = i * 2; + } + + // Multiply matrix A and B and store result in matrix C + C = A * B; + + C.transpose(); + + // Print out matrix C + for (row_iterator it_C = C.begin(); it_C != C.end(); ++it_C) + { + std::cout << *it_C << " "; + } + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue1.cpp new file mode 100644 index 0000000000..351c95ab00 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue1.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + * examples/containers/pqueue1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include +#include + +// comparison struct for priority queue where top() returns the smallest contained value: +struct ComparatorGreater +{ + bool operator () (const int& a, const int& b) const + { return (a > b); } + + int min_value() const + { return std::numeric_limits::max(); } +}; + +int main() +{ + typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pqueue_type; + typedef pqueue_type::block_type block_type; + + // block_type::raw_size = 262144 bytes + // use 64 block read and write pools each to enable overlapping between I/O and computation + const unsigned int mem_for_pools = 32 * 1024 * 1024; + stxxl::read_write_pool pool((mem_for_pools / 2) / block_type::raw_size, (mem_for_pools / 2) / block_type::raw_size); + pqueue_type my_pqueue(pool); // creates stxxl priority queue instance with read-write-pool + + my_pqueue.push(5); + my_pqueue.push(4); + my_pqueue.push(19); + my_pqueue.push(1); + assert(my_pqueue.size() == 4); + + assert(my_pqueue.top() == 1); + STXXL_MSG("Smallest inserted value in my: " << my_pqueue.top()); + + my_pqueue.pop(); // pop the 1 on top + + assert(my_pqueue.top() == 4); + STXXL_MSG("Smallest value after 1 pop(): " << my_pqueue.top()); + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue2.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue2.cpp new file mode 100644 index 0000000000..724bcb077e --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/pqueue2.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * examples/containers/pqueue2.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include + +// comparison struct for priority queue where top() returns the biggest contained value: +struct Cmp +{ + bool operator () (const int& a, const int& b) const + { return a < b; } + + int min_value() const + { return std::numeric_limits::min(); } +}; + +int main() +{ + // use 64 GiB on main memory and 1 billion items at most + typedef stxxl::PRIORITY_QUEUE_GENERATOR::result pq_type; + typedef pq_type::block_type block_type; + + // block_type::raw_size = 262144 bytes + // use 64 block read and write pools each to enable overlapping between I/O and computation + const unsigned int mem_for_pools = 32 * 1024 * 1024; + stxxl::read_write_pool pool((mem_for_pools / 2) / block_type::raw_size, (mem_for_pools / 2) / block_type::raw_size); + + pq_type Q(pool); + + Q.push(1); + Q.push(4); + Q.push(2); + Q.push(8); + Q.push(5); + Q.push(7); + + assert(Q.size() == 6); + + assert(Q.top() == 8); + Q.pop(); + + assert(Q.top() == 7); + Q.pop(); + + assert(Q.top() == 5); + Q.pop(); + + assert(Q.top() == 4); + Q.pop(); + + assert(Q.top() == 2); + Q.pop(); + + assert(Q.top() == 1); + Q.pop(); + + assert(Q.empty()); + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/queue1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/queue1.cpp new file mode 100644 index 0000000000..ce19211ee2 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/queue1.cpp @@ -0,0 +1,41 @@ +/*************************************************************************** + * examples/containers/queue1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include + +int main() +{ + typedef stxxl::queue queue; + queue my_queue; + + my_queue.push(5); + my_queue.push(11); + my_queue.push(3); + my_queue.push(7); + // my_queue now stores: |7|3|11|5| + + assert(my_queue.size() == 4); + + std::cout << "back element " << my_queue.back() << std::endl; // prints out 7 (last inserted element) + assert(my_queue.back() == 7); + std::cout << "front element " << my_queue.front() << std::endl; // prints out 5 (first inserted element) + assert(my_queue.front() == 5); + my_queue.pop(); // deletes element 5, queue now stores: |7|3|11| + + std::cout << "front element " << my_queue.front() << std::endl; // prints out 11 + assert(my_queue.front() == 11); + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/queue2.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/queue2.cpp new file mode 100644 index 0000000000..80269ca6b3 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/queue2.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + * examples/containers/queue2.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include + +int main() +{ + // template parameter + typedef stxxl::queue a_queue; + + // construct queue with default parameters + a_queue my_queue; + + unsigned int random; + stxxl::random_number32 rand32; // define random number generator + stxxl::uint64 number_of_elements = 64 * 1024 * 1024; + + // push random values in the queue + for (stxxl::uint64 i = 0; i < number_of_elements; i++) + { + random = rand32(); // generate random integers from interval [0,2^32) + my_queue.push(random); + } + + unsigned int last_inserted = my_queue.back(); + STXXL_MSG("last element inserted: " << last_inserted); + + // identify smaller element than first_inserted, search in growth-direction (front->back) + while (!my_queue.empty()) + { + if (last_inserted > my_queue.front()) + { + STXXL_MSG("found smaller element: " << my_queue.front() << " than last inserted element"); + break; + } + std::cout << my_queue.front() << " " << std::endl; + my_queue.pop(); + } + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/sequence1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/sequence1.cpp new file mode 100644 index 0000000000..919cfce80a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/sequence1.cpp @@ -0,0 +1,38 @@ +/*************************************************************************** + * examples/containers/sequence1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include + +int main() +{ + typedef stxxl::sequence sequence_type; + sequence_type my_sequence; + + for (int i = 0; i < 100; ++i) + { + my_sequence.push_back(i * i); + } + + sequence_type::stream forward_stream = my_sequence.get_stream(); + + while (!forward_stream.empty()) + { + std::cout << *forward_stream << " "; + my_sequence.pop_back(); + ++forward_stream; + } + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/sorter1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/sorter1.cpp new file mode 100644 index 0000000000..03a52624e0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/sorter1.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * examples/containers/sorter1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include +#include +#include + +struct my_comparator +{ + bool operator () (const int& a, const int& b) const + { + return a < b; + } + + int min_value() const + { + return std::numeric_limits::min(); + } + + int max_value() const + { + return std::numeric_limits::max(); + } +}; + +int main() +{ + // template parameter + typedef stxxl::sorter sorter_type; + + // create sorter object (CompareType(), MainMemoryLimit) + sorter_type int_sorter(my_comparator(), 64 * 1024 * 1024); + + // fill sorter with elements order in descending order + for (int i = 1000; i > 0; i--) + { + int_sorter.push(i); + } + + int_sorter.sort(); // sort elements (in ascending order) + + // walk through sorted values and print them out + while (!int_sorter.empty()) + { + std::cout << *int_sorter << " "; + ++int_sorter; + } + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/sorter2.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/sorter2.cpp new file mode 100644 index 0000000000..e98376949d --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/sorter2.cpp @@ -0,0 +1,88 @@ +/*************************************************************************** + * examples/containers/sorter2.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include +#include +#include +#include + +struct TwoInteger +{ + int i, j; + + TwoInteger() + { } + + TwoInteger(int _i, int _j) + : i(_i), j(_j) + { } +}; + +struct TwoIntegerComparator +{ + bool operator () (const TwoInteger& a, const TwoInteger& b) const + { + return a.i < b.i; + } + + TwoInteger min_value() const + { + return TwoInteger(std::numeric_limits::min(), std::numeric_limits::min()); + } + + TwoInteger max_value() const + { + return TwoInteger(std::numeric_limits::max(), std::numeric_limits::max()); + } +}; + +int main() +{ + // template parameter + typedef stxxl::sorter sorter_type; + + // create sorter object (CompareType(), MainMemoryLimit) + sorter_type int_sorter(TwoIntegerComparator(), 64 * 1024 * 1024); + + stxxl::random_number32 rand32; + + stxxl::timer Timer1; + Timer1.start(); + + // insert random numbers from [0,100000) + for (size_t i = 0; i < 1000; ++i) + { + int_sorter.push(TwoInteger(rand32() % 100000, (int)i)); // fill sorter container + } + + Timer1.stop(); + + STXXL_MSG("push time: " << (Timer1.mseconds() / 1000)); + + stxxl::timer Timer2; + + Timer2.start(); + int_sorter.sort(); // switch to output state and sort + Timer2.stop(); + + STXXL_MSG("sort time: " << (Timer2.mseconds() / 1000)); + + // echo sorted elements + while (!int_sorter.empty()) + { + std::cout << int_sorter->i << " "; // access value + ++int_sorter; + } + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/stack1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/stack1.cpp new file mode 100644 index 0000000000..4140c7c675 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/stack1.cpp @@ -0,0 +1,39 @@ +/*************************************************************************** + * examples/containers/stack1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include + +int main() +{ + typedef stxxl::STACK_GENERATOR::result stack_type; + stack_type my_stack; + + my_stack.push(8); + my_stack.push(7); + my_stack.push(4); + assert(my_stack.size() == 3); + + assert(my_stack.top() == 4); + my_stack.pop(); + + assert(my_stack.top() == 7); + my_stack.pop(); + + assert(my_stack.top() == 8); + my_stack.pop(); + + assert(my_stack.empty()); + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/stack2.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/stack2.cpp new file mode 100644 index 0000000000..b2d236232e --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/stack2.cpp @@ -0,0 +1,43 @@ +/*************************************************************************** + * examples/containers/stack2.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include + +int main() +{ + // template parameter + typedef stxxl::STACK_GENERATOR::result simple_stack; + + // create stack instance + simple_stack a_stack; + + stxxl::random_number<> random; + stxxl::uint64 number_of_elements = 16 * 1024 * 1024; + + // routine: 1) push random values on stack and 2) pop all except the lowest value and start again + for (int k = 0; k < 5; k++) { + STXXL_MSG("push..."); + for (stxxl::uint64 i = 0; i < number_of_elements; i++) + { + a_stack.push(random(123456789)); + } + + STXXL_MSG("pop..."); + for (stxxl::uint64 j = 0; j < number_of_elements - 1; j++) + { + a_stack.pop(); + } + } + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/unordered_map1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/unordered_map1.cpp new file mode 100644 index 0000000000..961c3af71c --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/unordered_map1.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * examples/containers/unordered_map1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * Copyright (C) 2014 Timo Bingmann + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include + +//! [hash] +struct HashFunctor +{ + size_t operator () (int key) const + { + // a simple integer hash function + return (size_t)(key * 2654435761u); + } +}; +//! [hash] + +//! [comparator] +struct CompareLess +{ + bool operator () (const int& a, const int& b) const + { return a < b; } + + static int min_value() { return std::numeric_limits::min(); } + static int max_value() { return std::numeric_limits::max(); } +}; +//! [comparator] + +int main() +{ +//! [construction] +#define SUB_BLOCK_SIZE 8192 +#define SUB_BLOCKS_PER_BLOCK 256 + + // template parameter + typedef stxxl::unordered_map< + int, char, HashFunctor, CompareLess, SUB_BLOCK_SIZE, SUB_BLOCKS_PER_BLOCK + > unordered_map_type; + + // constructor: use defaults for all parameters + unordered_map_type my_map; +//! [construction] + + // insert some items and delete one + my_map.insert(std::make_pair(1, 'a')); + my_map.insert(std::make_pair(2, 'b')); + my_map.insert(std::make_pair(3, 'c')); + my_map.insert(std::make_pair(4, 'd')); + + my_map.erase(3); + + // iterate over all items in the unordered_map + unordered_map_type::iterator iter; + + std::cout << "my_map contains:\n"; + for (iter = my_map.begin(); iter != my_map.end(); ++iter) + { + std::cout << iter->first << " => " << iter->second << std::endl; + } + + // direct operator[] access to items + std::cout << "my_map[2] = " << my_map[2] << std::endl; + + // efficient bulk-insert into hash map by sorting by hash keys + std::vector value_array; + + for (int i = 0; i < 128; ++i) + value_array.push_back(std::make_pair(i, (char)i)); + + my_map.insert(value_array.begin(), value_array.end(), 8 * 1024 * 1024); + + // check results of insertion + std::cout << "my_map[42] = " << my_map[42] << std::endl; + std::cout << "my_map.size() = " << my_map.size() << std::endl; + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/vector1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/vector1.cpp new file mode 100644 index 0000000000..afd7b06056 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/vector1.cpp @@ -0,0 +1,37 @@ +/*************************************************************************** + * examples/containers/vector1.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +//! [example] +#include +#include + +int main() +{ + typedef stxxl::VECTOR_GENERATOR::result vector; + vector my_vector; + + for (int i = 0; i < 1024 * 1024; i++) + { + my_vector.push_back(i + 2); + } + + std::cout << my_vector[99] << std::endl; + my_vector[100] = 0; + + while (!my_vector.empty()) + { + my_vector.pop_back(); + } + + return 0; +} +//! [example] diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/vector2.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/vector2.cpp new file mode 100644 index 0000000000..ccb9f04075 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/vector2.cpp @@ -0,0 +1,50 @@ +/*************************************************************************** + * examples/containers/vector2.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Daniel Feist + * + * 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) + **************************************************************************/ + +#include +#include + +int main() +{ + // template parameter + typedef stxxl::VECTOR_GENERATOR::result vector_type; + + vector_type my_vector; + unsigned int counter = 0; + unsigned int tmp; + stxxl::random_number<> rand; + stxxl::uint64 number_of_elements = 32 * 1024 * 1024; + + // fill vector with random integers + for (stxxl::uint64 i = 0; i < number_of_elements; ++i) + { + tmp = rand(123456789); // generate random number from the interval [0,123456789) + my_vector.push_back(tmp); + } + + // construct iterator + vector_type::const_iterator iter = my_vector.begin(); + + // use iterator to advance my_vector and calculate number of even elements + for (stxxl::uint64 j = 0; j < my_vector.size(); j++) + { + //std::cout << *iter << " "; + if (*iter % 2 == 0) // is my_vector's current element even? + { + ++counter; + } + iter++; + } + + STXXL_MSG("found " << counter << " even numbers in V"); + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/containers/vector_buf.cpp b/third-party/MQF/ThirdParty/stxxl/examples/containers/vector_buf.cpp new file mode 100644 index 0000000000..16e28b5871 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/containers/vector_buf.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** + * examples/containers/vector_buf.cpp + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +#include +#include +#include + +using stxxl::uint64; + +void test_vector_element(uint64 size) +{ + stxxl::scoped_print_timer tm("vector element access", 2 * size * sizeof(uint64)); + +//! [element] + typedef stxxl::VECTOR_GENERATOR::result vector_type; + + vector_type vec(size); + + for (uint64 i = 0; i < vec.size(); ++i) + vec[i] = (i % 1024); + + uint64 sum = 0; + for (uint64 i = 0; i < vec.size(); ++i) + sum += vec[i]; +//! [element] + + std::cout << "sum: " << sum << std::endl; + STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); +} + +void test_vector_iterator(uint64 size) +{ + stxxl::scoped_print_timer tm("vector iterator access", 2 * size * sizeof(uint64)); + +//! [iterator] + typedef stxxl::VECTOR_GENERATOR::result vector_type; + + vector_type vec(size); + + uint64 i = 0; + for (vector_type::iterator it = vec.begin(); it != vec.end(); ++it, ++i) + *it = (i % 1024); + + uint64 sum = 0; + for (vector_type::const_iterator it = vec.begin(); it != vec.end(); ++it) + sum += *it; +//! [iterator] + + std::cout << "sum: " << sum << std::endl; + STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); +} + +void test_vector_buffered(uint64 size) +{ + stxxl::scoped_print_timer tm("vector buffered access", 2 * size * sizeof(uint64)); + +//! [buffered] + typedef stxxl::VECTOR_GENERATOR::result vector_type; + + vector_type vec(size); + + // write using vector_bufwriter + vector_type::bufwriter_type writer(vec); + + for (uint64 i = 0; i < vec.size(); ++i) + writer << (i % 1024); + + // required to flush out the last block (or destruct the bufwriter) + writer.finish(); + + // now read using vector_bufreader + uint64 sum = 0; + + for (vector_type::bufreader_type reader(vec); !reader.empty(); ++reader) + { + sum += *reader; + } +//! [buffered] + + std::cout << "sum: " << sum << std::endl; + STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); +} + +#if STXXL_HAVE_CXX11_RANGE_FOR_LOOP +void test_vector_cxx11(uint64 size) +{ + stxxl::scoped_print_timer tm("vector C++11 loop access", 2 * size * sizeof(uint64)); + + typedef stxxl::VECTOR_GENERATOR::result vector_type; + + vector_type vec(size); + + { + vector_type::bufwriter_type writer(vec); + + for (uint64 i = 0; i < vec.size(); ++i) + writer << (i % 1024); + } + +//! [cxx11] + // now using vector_bufreader adaptor to C++11 for loop + uint64 sum = 0; + + for (auto it : vector_type::bufreader_type(vec)) + { + sum += it; + } +//! [cxx11] + + std::cout << "sum: " << sum << std::endl; + STXXL_CHECK(sum == size / 1024 * (1024 * 1023 / 2)); +} +#endif + +int main(int argc, char* argv[]) +{ + int multi = (argc >= 2 ? atoi(argv[1]) : 64); + const uint64 size = multi * 1024 * uint64(1024) / sizeof(uint64); + + stxxl::block_manager::get_instance(); + + test_vector_element(size); + test_vector_iterator(size); + test_vector_buffered(size); + +#if STXXL_HAVE_CXX11_RANGE_FOR_LOOP + test_vector_cxx11(size); +#endif + + return 0; +} diff --git a/third-party/MQF/ThirdParty/stxxl/examples/stream/CMakeLists.txt b/third-party/MQF/ThirdParty/stxxl/examples/stream/CMakeLists.txt new file mode 100644 index 0000000000..876d523148 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/stream/CMakeLists.txt @@ -0,0 +1,15 @@ +############################################################################ +# examples/stream/CMakeLists.txt +# +# Part of the STXXL. See http://stxxl.sourceforge.net +# +# Copyright (C) 2013 Timo Bingmann +# +# 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) +############################################################################ + +stxxl_build_example(stream1) + +stxxl_test(stream1) diff --git a/third-party/MQF/ThirdParty/stxxl/examples/stream/stream1.cpp b/third-party/MQF/ThirdParty/stxxl/examples/stream/stream1.cpp new file mode 100644 index 0000000000..495c79f3f9 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/examples/stream/stream1.cpp @@ -0,0 +1,297 @@ +/*************************************************************************** + * examples/stream/stream1.cpp + * + * This file contains the example snippets from the stream tutorial. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2012-2013 Timo Bingmann + * + * 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) + **************************************************************************/ + +#include +#include +#include + +#include +#include + +struct counter_object +{ + // This stream produces a sequence of integers. + typedef int value_type; + +private: + // A class attribute to save the current value. + int m_current_value; + +public: + // A constructor to set the initial value to 1. + counter_object() + : m_current_value(1) + { } + + // The retrieve operator returning the current value. + const value_type& operator * () const + { + return m_current_value; + } + + // Increment operator advancing to the next integer. + counter_object& operator ++ () + { + ++m_current_value; + return *this; + } + + // Empty indicator, which in this case can check the current value. + bool empty() const + { + return (m_current_value > 1000); + } +}; + +template +struct squaring_object +{ + // This stream produces a sequence of integers. + typedef int value_type; + +private: + // A reference to another stream of integers, which are our input. + InputStream& m_input_stream; + + // A temporary value buffer to hold the current square in for retrieval. + value_type m_current_value; + +public: + // A constructor taking another stream of integers as input. + squaring_object(InputStream& input_stream) + : m_input_stream(input_stream) + { + if (!m_input_stream.empty()) + { + m_current_value = *m_input_stream; + m_current_value = m_current_value * m_current_value; + } + } + + // The retrieve operator returning the square of the input stream. + const value_type& operator * () const + { + return m_current_value; + } + + // Increment operator: handled by incrementing the input stream. + squaring_object& operator ++ () + { + ++m_input_stream; + if (!m_input_stream.empty()) + { + m_current_value = *m_input_stream; + m_current_value = m_current_value * m_current_value; + } + return *this; + } + + // Empty indicator: this stream is empty when the input stream is. + bool empty() const + { + return m_input_stream.empty(); + } +}; + +// define comparator class: compare right-most decimal and then absolute value +struct CompareMod10 +{ + // comparison operator() returning true if (a < b) + inline bool operator () (int a, int b) const + { + if ((a % 10) == (b % 10)) + return a < b; + else + return (a % 10) < (b % 10); + } + + // smallest possible integer value + int min_value() const { return INT_MIN; } + // largest possible integer value + int max_value() const { return INT_MAX; } +}; + +int main() +{ + { + counter_object counter; + + while (!counter.empty()) + { + std::cout << *counter << " "; + ++counter; + } + std::cout << std::endl; + } + + { + for (counter_object cnt; !cnt.empty(); ++cnt) + { + std::cout << *cnt << " "; + } + std::cout << std::endl; + } + + { + counter_object counter; + squaring_object squares(counter); + + while (!squares.empty()) + { + std::cout << *squares << " "; + ++squares; + } + std::cout << std::endl; + } + + { + std::vector intvector; + // (fill intvector) + + // define stream class iterating over an integer vector + typedef stxxl::stream::iterator2stream::const_iterator> intstream_type; + + // instantiate the stream object, iterate from begin to end of intvector. + intstream_type intstream(intvector.begin(), intvector.end()); + + // plug in squaring object after vector iterator stream. + squaring_object squares(intstream); + } + + { + stxxl::vector intvector; + // (fill intvector) + + // define stream class iterating over an integer vector + typedef stxxl::stream::vector_iterator2stream::const_iterator> intstream_type; + + // instantiate the stream object, iterate from begin to end of intvector. + intstream_type intstream(intvector.begin(), intvector.end()); + + // plug in squaring object after vector iterator stream. + squaring_object squares(intstream); + } + + { + // construct the squared counter stream + counter_object counter; + squaring_object squares(counter); + + // allocate vector of 100 integers + std::vector intvector(100); + + // materialize 100 integers from stream and put into vector + stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); + } + + { + // construct the squared counter stream + counter_object counter; + squaring_object squares(counter); + + // allocate STXXL vector of 100 integers + stxxl::vector intvector(100); + + // materialize 100 integers from stream and put into STXXL vector + stxxl::stream::materialize(squares, intvector.begin(), intvector.end()); + } + + { + static const int ram_use = 10 * 1024 * 1024; // amount of memory to use in runs creation + + counter_object counter; // the counter stream from first examples + + // define a runs sorter for the counter stream which order by CompareMod10 object. + typedef stxxl::stream::runs_creator rc_counter_type; + + // instance of CompareMod10 comparator class + CompareMod10 comparemod10; + + // instance of runs_creator which reads the counter stream. + rc_counter_type rc_counter(counter, comparemod10, ram_use); + + // define a runs merger for the sorted runs from rc_counter. + typedef stxxl::stream::runs_merger rm_counter_type; + + // instance of runs_merger which merges sorted runs from rc_counter. + rm_counter_type rm_counter(rc_counter.result(), comparemod10, ram_use); + + // read sorted stream: runs_merger also conforms to the stream interface. + while (!rm_counter.empty()) + { + std::cout << *rm_counter << " "; + ++rm_counter; + } + std::cout << std::endl; + } + + { + static const int ram_use = 10 * 1024 * 1024; // amount of memory to use in runs creation + + // define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. + typedef stxxl::stream::runs_creator, CompareMod10> rc_counter_type; + + // instance of CompareMod10 comparator class. + CompareMod10 comparemod10; + + // instance of runs_creator which waits for input. + rc_counter_type rc_counter(comparemod10, ram_use); + + // write sequence of integers into runs + for (int i = 1; i <= 1000; ++i) + rc_counter.push(i); + + // define a runs merger for the sorted runs from rc_counter. + typedef stxxl::stream::runs_merger rm_counter_type; + + // instance of runs_merger which merges sorted runs from rc_counter. + rm_counter_type rm_counter(rc_counter.result(), comparemod10, ram_use); + + // read sorted stream: runs_merger also conforms to the stream interface. + while (!rm_counter.empty()) + { + std::cout << *rm_counter << " "; + ++rm_counter; + } + std::cout << std::endl; + } + + { + static const int ram_use = 10 * 1024 * 1024; // amount of memory to use in runs creation + + // define a runs sorter which accepts imperative push()s and orders by CompareMod10 object. + typedef stxxl::sorter sr_counter_type; + + // instance of CompareMod10 comparator class. + CompareMod10 comparemod10; + + // instance of sorter which waits for input. + sr_counter_type sr_counter(comparemod10, ram_use); + + // write sequence of integers into sorter, which creates sorted runs + for (int i = 1; i <= 1000; ++i) + sr_counter.push(i); + + // signal sorter that the input stream is finished and switch to output mode. + sr_counter.sort(); + + // read sorted stream: sorter also conforms to the stream interface. + while (!sr_counter.empty()) + { + std::cout << *sr_counter << " "; + ++sr_counter; + } + std::cout << std::endl; + } +} diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl.h new file mode 100644 index 0000000000..790891b766 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * include/stxxl.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2007 Roman Dementiev + * + * 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 STXXL_MAIN_HEADER +#define STXXL_MAIN_HEADER + +#include + +#include + +#include + +#include +#include +#include +#if ! defined(__GNUG__) || ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 30400) +// map does not work with g++ 3.3 +#include +#endif +#include +#include +#include + +#include + +#include + +#include + +#include + +#endif // STXXL_MAIN_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/algorithm b/third-party/MQF/ThirdParty/stxxl/include/stxxl/algorithm new file mode 100644 index 0000000000..09a30c77b3 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/algorithm @@ -0,0 +1,20 @@ +// -*- mode: c++ -*- +/*************************************************************************** + * include/stxxl/algorithm + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Andreas Beckmann + * + * 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) + **************************************************************************/ + +#include + +#include +#include +//#include + +#include diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/aligned_alloc b/third-party/MQF/ThirdParty/stxxl/include/stxxl/aligned_alloc new file mode 100644 index 0000000000..a3c572c21b --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/aligned_alloc @@ -0,0 +1,14 @@ +// -*- mode: c++ -*- +/*************************************************************************** + * include/stxxl/aligned_alloc + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008 Andreas Beckmann + * + * 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) + **************************************************************************/ + +#include diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/all b/third-party/MQF/ThirdParty/stxxl/include/stxxl/all new file mode 100644 index 0000000000..9aaa264f96 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/all @@ -0,0 +1,14 @@ +// -*- mode: c++ -*- +/*************************************************************************** + * include/stxxl/all + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Andreas Beckmann + * + * 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) + **************************************************************************/ + +#include diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/adaptor.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/adaptor.h new file mode 100644 index 0000000000..6e494b0099 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/adaptor.h @@ -0,0 +1,246 @@ +/*************************************************************************** + * include/stxxl/bits/algo/adaptor.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2010 Andreas Beckmann + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_ALGO_ADAPTOR_HEADER +#define STXXL_ALGO_ADAPTOR_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +struct runs2bid_array_adaptor : public two2one_dim_array_adapter_base, PosType> +{ + typedef runs2bid_array_adaptor self_type; + typedef BID data_type; + + enum { + block_size = BlockSize + }; + + unsigned_type dim_size; + + typedef two2one_dim_array_adapter_base, PosType> parent_type; + using parent_type::array; + using parent_type::pos; + + runs2bid_array_adaptor(RunType** a, PosType p, unsigned_type d) + : two2one_dim_array_adapter_base, PosType>(a, p), dim_size(d) + { } + runs2bid_array_adaptor(const self_type& a) + : two2one_dim_array_adapter_base, PosType>(a), dim_size(a.dim_size) + { } + + const self_type& operator = (const self_type& a) + { + array = a.array; + pos = a.pos; + dim_size = a.dim_size; + return *this; + } + + data_type& operator * () + { + CHECK_RUN_BOUNDS(pos); + return (BID&)((*(array[(pos) % dim_size]))[(pos) / dim_size].bid); + } + + const data_type* operator -> () const + { + CHECK_RUN_BOUNDS(pos); + return &((*(array[(pos) % dim_size])[(pos) / dim_size].bid)); + } + + data_type& operator [] (PosType n) const + { + n += pos; + CHECK_RUN_BOUNDS(n); + return (BID&)((*(array[(n) % dim_size]))[(n) / dim_size].bid); + } +}; + +BLOCK_ADAPTOR_OPERATORS(runs2bid_array_adaptor) + +template +struct runs2bid_array_adaptor2 + : public two2one_dim_array_adapter_base, PosType> +{ + typedef runs2bid_array_adaptor2 self_type; + typedef BID data_type; + + typedef two2one_dim_array_adapter_base, PosType> base_type; + + using base_type::pos; + using base_type::array; + + enum { + block_size = BlockSize + }; + + PosType w, h, K; + + runs2bid_array_adaptor2(RunType** a, PosType p, int_type _w, int_type _h) + : two2one_dim_array_adapter_base, PosType>(a, p), + w(_w), h(_h), K(_w * _h) + { } + + runs2bid_array_adaptor2(const self_type& a) + : two2one_dim_array_adapter_base, PosType>(a), + w(a.w), h(a.h), K(a.K) + { } + + const self_type& operator = (const self_type& a) + { + array = a.array; + pos = a.pos; + w = a.w; + h = a.h; + K = a.K; + return *this; + } + + data_type& operator * () + { + PosType i = pos - K; + if (i < 0) + return (BID&)((*(array[(pos) % w]))[(pos) / w].bid); + + PosType _w = w; + _w--; + return (BID&)((*(array[(i) % _w]))[h + (i / _w)].bid); + } + + const data_type* operator -> () const + { + PosType i = pos - K; + if (i < 0) + return &((*(array[(pos) % w])[(pos) / w].bid)); + + PosType _w = w; + _w--; + return &((*(array[(i) % _w])[h + (i / _w)].bid)); + } + + data_type& operator [] (PosType n) const + { + n += pos; + PosType i = n - K; + if (i < 0) + return (BID&)((*(array[(n) % w]))[(n) / w].bid); + + PosType _w = w; + _w--; + return (BID&)((*(array[(i) % _w]))[h + (i / _w)].bid); + } +}; + +BLOCK_ADAPTOR_OPERATORS(runs2bid_array_adaptor2) + +template +struct trigger_entry_iterator +{ + typedef trigger_entry_iterator self_type; + typedef typename std::iterator_traits::value_type::bid_type bid_type; + + // STL typedefs + typedef bid_type value_type; + typedef std::random_access_iterator_tag iterator_category; + typedef int_type difference_type; + typedef value_type* pointer; + typedef value_type& reference; + + trigger_iterator_type value; + + trigger_entry_iterator(const self_type& a) : value(a.value) { } + trigger_entry_iterator(trigger_iterator_type v) : value(v) { } + + bid_type& operator * () + { + return value->bid; + } + bid_type* operator -> () const + { + return &(value->bid); + } + const bid_type& operator [] (int_type n) const + { + return (value + n)->bid; + } + bid_type& operator [] (int_type n) + { + return (value + n)->bid; + } + + self_type& operator ++ () + { + value++; + return *this; + } + self_type operator ++ (int) + { + self_type tmp = *this; + value++; + return tmp; + } + self_type& operator -- () + { + value--; + return *this; + } + self_type operator -- (int) + { + self_type tmp = *this; + value--; + return tmp; + } + bool operator == (const self_type& a) const + { + return value == a.value; + } + bool operator != (const self_type& a) const + { + return value != a.value; + } + self_type operator += (int_type n) + { + value += n; + return *this; + } + self_type operator -= (int_type n) + { + value -= n; + return *this; + } + int_type operator - (const self_type& a) const + { + return value - a.value; + } + int_type operator + (const self_type& a) const + { + return value + a.value; + } +}; + +template +inline +trigger_entry_iterator +make_bid_iterator(Iterator iter) +{ + return trigger_entry_iterator(iter); +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_ADAPTOR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/async_schedule.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/async_schedule.h new file mode 100644 index 0000000000..ed15f05654 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/async_schedule.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * include/stxxl/bits/algo/async_schedule.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_ALGO_ASYNC_SCHEDULE_HEADER +#define STXXL_ALGO_ASYNC_SCHEDULE_HEADER + +// Implements the "prudent prefetching" as described in +// D. Hutchinson, P. Sanders, J. S. Vitter: Duality between prefetching +// and queued writing on parallel disks, 2005 +// DOI: 10.1137/S0097539703431573 + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +void compute_prefetch_schedule( + const int_type* first, + const int_type* last, + int_type* out_first, + int_type m, + int_type D); + +inline void compute_prefetch_schedule( + int_type* first, + int_type* last, + int_type* out_first, + int_type m, + int_type D) +{ + compute_prefetch_schedule(static_cast(first), last, out_first, m, D); +} + +template +void compute_prefetch_schedule( + const RunType& input, + int_type* out_first, + int_type m, + int_type D) +{ + const int_type L = input.size(); + simple_vector disks(L); + for (int_type i = 0; i < L; ++i) + disks[i] = input[i].bid.storage->get_device_id(); + compute_prefetch_schedule(disks.begin(), disks.end(), out_first, m, D); +} + +template +void compute_prefetch_schedule( + BidIteratorType input_begin, + BidIteratorType input_end, + int_type* out_first, + int_type m, + int_type D) +{ + const int_type L = input_end - input_begin; + simple_vector disks(L); + int_type i = 0; + for (BidIteratorType it = input_begin; it != input_end; ++it, ++i) + disks[i] = it->storage->get_device_id(); + compute_prefetch_schedule(disks.begin(), disks.end(), out_first, m, D); +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_ASYNC_SCHEDULE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/inmemsort.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/inmemsort.h new file mode 100644 index 0000000000..3f260c8c76 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/inmemsort.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * include/stxxl/bits/algo/inmemsort.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003 Roman Dementiev + * Copyright (C) 2010 Andreas Beckmann + * + * 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 STXXL_ALGO_INMEMSORT_HEADER +#define STXXL_ALGO_INMEMSORT_HEADER + +#include +#include +#include +#include +#include +#include + +#include + +STXXL_BEGIN_NAMESPACE + +template +void stl_in_memory_sort(ExtIterator first, ExtIterator last, StrictWeakOrdering cmp) +{ + typedef typename ExtIterator::block_type block_type; + + STXXL_VERBOSE("stl_in_memory_sort, range: " << (last - first)); + first.flush(); + unsigned_type nblocks = last.bid() - first.bid() + (last.block_offset() ? 1 : 0); + simple_vector blocks(nblocks); + simple_vector reqs(nblocks); + unsigned_type i; + + for (i = 0; i < nblocks; ++i) + reqs[i] = blocks[i].read(*(first.bid() + i)); + + wait_all(reqs.begin(), nblocks); + + unsigned_type last_block_correction = last.block_offset() ? (block_type::size - last.block_offset()) : 0; + check_sort_settings(); + potentially_parallel:: + sort(make_element_iterator(blocks.begin(), first.block_offset()), + make_element_iterator(blocks.begin(), nblocks * block_type::size - last_block_correction), + cmp); + + for (i = 0; i < nblocks; ++i) + reqs[i] = blocks[i].write(*(first.bid() + i)); + + wait_all(reqs.begin(), nblocks); +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_INMEMSORT_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/intksort.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/intksort.h new file mode 100644 index 0000000000..7e264b2104 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/intksort.h @@ -0,0 +1,367 @@ +/*************************************************************************** + * include/stxxl/bits/algo/intksort.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Peter Sanders + * + * 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 STXXL_ALGO_INTKSORT_HEADER +#define STXXL_ALGO_INTKSORT_HEADER + +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +static void +count(TypeKey* a, TypeKey* aEnd, int_type* bucket, int_type K, + typename TypeKey::key_type offset, unsigned shift) +{ + // reset buckets + std::fill(bucket, bucket + K, 0); + + // count occupancies + for (TypeKey* p = a; p < aEnd; p++) + { + int_type i = (int_type)((p->key - offset) >> shift); + /* + if (!(i < K && i >= 0)) + { + STXXL_ERRMSG("i: " << i); + abort(); + } + */ + bucket[i]++; + } +} + +static inline void +exclusive_prefix_sum(int_type* bucket, int_type K) +{ + int_type sum = 0; + for (int_type i = 0; i < K; i++) + { + int_type current = bucket[i]; + bucket[i] = sum; + sum += current; + } +} + +// distribute input a to output b using bucket for the starting indices +template +static void +classify(TypeKey* a, TypeKey* aEnd, TypeKey* b, int_type* bucket, + typename TypeKey::key_type offset, unsigned shift) +{ + for (TypeKey* p = a; p < aEnd; p++) + { + int_type i = (int_type)((p->key - offset) >> shift); + int_type bi = bucket[i]; + b[bi] = *p; + bucket[i] = bi + 1; + } +} + +template +inline void +sort2(Type& a, Type& b) +{ + if (b < a) + std::swap(a, b); +} + +template +inline void +sort3(Type& a, Type& b, Type& c) +{ + Type temp; + if (b < a) + { + if (c < a) + { // b , c < a + if (b < c) + { // b < c < a + temp = a; + a = b; + b = c; + c = temp; + } + else + { // c <=b < a + std::swap(c, a); + } + } + else + { // b < a <=c + std::swap(a, b); + } + } + else + { // a <=b + if (c < a) + { // c < a <=b + temp = a; + a = c; + c = b; + b = temp; + } + else + { // a <=b , c + if (c < b) + { // a <=c < b + std::swap(b, c); + } + } + } + // Assert1 (!(b < a) && !(c < b)); +} + +template +inline void +sort4(Type& a, Type& b, Type& c, Type& d) +{ + sort2(a, b); + sort2(c, d); // a < b ; c < d + if (c < a) + { // c minimal, a < b + if (d < a) + { // c < d < a < b + std::swap(a, c); + std::swap(b, d); + } + else + { // c < a < {db} + if (d < b) + { // c < a < d < b + Type temp = a; + a = c; + c = d; + d = b; + b = temp; + } + else + { // c < a < b < d + Type temp = a; + a = c; + c = b; + b = temp; + } + } + } + else + { // a minimal ; c < d + if (c < b) + { // c < (bd) + if (d < b) + { // c < d < b + Type temp = b; + b = c; + c = d; + d = temp; + } + else + { // a < c < b < d + std::swap(b, c); + } + } // else sorted + } + //Assert1 (!(b < a) && !(c < b) & !(d < c)); +} + +template +inline void +sort5(Type& a, Type& b, Type& c, Type& d, Type& e) +{ + sort2(a, b); + sort2(d, e); + if (d < a) + { + std::swap(a, d); + std::swap(b, e); + } // a < d < e, a < b + if (d < c) + { + std::swap(c, d); // a minimal, c < {de} + sort2(d, e); + } + else + { // a +inline void +insertion_sort(Type* a, Type* aEnd) +{ + Type* pp; + for (Type* p = a + 1; p < aEnd; p++) + { + // Invariant a..p-1 is sorted; + Type t = *p; + if (t < *a) + { // new minimum + // move stuff to the right + for (pp = p; pp != a; pp--) + { + *pp = *(pp - 1); + } + *pp = t; + } + else + { + // now we can use *a as a sentinel + for (pp = p; t < *(pp - 1); pp--) + { + *pp = *(pp - 1); + } + *pp = t; + } + } +} + +// sort each bucket +// bucket[i] is an index one off to the right from +// the end of the i-th bucket +template +static void +cleanup(Type* b, int_type* bucket, int_type K) +{ + Type* c = b; + for (int_type i = 0; i < K; i++) + { + Type* cEnd = b + bucket[i]; + switch (cEnd - c) + { + case 0: + break; + case 1: + break; + case 2: + sort2(c[0], c[1]); + break; + case 3: + sort3(c[0], c[1], c[2]); + break; + case 4: + sort4(c[0], c[1], c[2], c[3]); + break; + case 5: +#if 0 + sort5(c[0], c[1], c[2], c[3], c[4]); + break; +#endif + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + insertion_sort(c, cEnd); + break; + default: + check_sort_settings(); + potentially_parallel:: + sort(c, cEnd); + } + c = cEnd; + } +} + +// do a single level MDS radix sort +// using bucket[0..K-1] as a counter array +// and using (key(x) - offset) >> shift to index buckets. +// and using (key(x) - offset) >> shift to index buckets. +// the input comes from a..aEnd-1 +// the output goes to b +template +void +l1sort(TypeKey* a, + TypeKey* aEnd, + TypeKey* b, int_type* bucket, int_type K, + typename TypeKey::key_type offset, int shift) +{ + count(a, aEnd, bucket, K, offset, shift); + exclusive_prefix_sum(bucket, K); + classify(a, aEnd, b, bucket, offset, shift); + cleanup(b, bucket, K); +} + +template +void classify_block(Type* begin, Type* end, TypeKey*& out, + int_type* bucket, typename KeyExtractor::key_type offset, unsigned shift, KeyExtractor keyobj) +{ + assert(shift < (sizeof(typename KeyExtractor::key_type) * 8 + 1)); + for (Type* p = begin; p < end; p++, out++) // count & create references + { + out->ptr = p; + typename KeyExtractor::key_type key = keyobj(*p); + int_type ibucket = (int_type)((key - offset) >> shift); + out->key = key; + bucket[ibucket]++; + } +} +template +void classify_block(Type* begin, Type* end, TypeKey*& out, int_type* bucket, typename Type::key_type offset, unsigned shift, + const int_type K, KeyExtractor keyobj) +{ + assert(shift < (sizeof(typename Type::key_type) * 8 + 1)); + for (Type* p = begin; p < end; p++, out++) // count & create references + { + out->ptr = p; + typename Type::key_type key = keyobj(*p); + int_type ibucket = (key - offset) >> shift; + /* + if (!(ibucket < K && ibucket >= 0)) + { + STXXL_ERRMSG("ibucket: " << ibucket << " K:" << K); + abort(); + } + */ + out->key = key; + bucket[ibucket]++; + } + STXXL_UNUSED(K); +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_INTKSORT_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/ksort.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/ksort.h new file mode 100644 index 0000000000..33ec8cffe2 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/ksort.h @@ -0,0 +1,1083 @@ +/*************************************************************************** + * include/stxxl/bits/algo/ksort.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008-2011 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_ALGO_KSORT_HEADER +#define STXXL_ALGO_KSORT_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define INTERLEAVED_ALLOC + +#define OPT_MERGING + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stllayer + +//! \defgroup stlalgo Algorithms +//! \ingroup stllayer +//! Algorithms with STL-compatible interface +//! \{ + +/*! \internal + */ +namespace ksort_local { + +template +struct trigger_entry +{ + typedef BIDType bid_type; + typedef KeyType key_type; + + bid_type bid; + key_type key; + + operator bid_type () + { + return bid; + } +}; + +template +inline bool operator < (const trigger_entry& a, + const trigger_entry& b) +{ + return (a.key < b.key); +} + +template +inline bool operator > (const trigger_entry& a, + const trigger_entry& b) +{ + return (a.key > b.key); +} + +template +struct type_key +{ + typedef KeyType key_type; + key_type key; + Type* ptr; + + type_key() { } + type_key(key_type k, Type* p) : key(k), ptr(p) + { } +}; + +template +bool operator < (const type_key& a, const type_key& b) +{ + return a.key < b.key; +} + +template +bool operator > (const type_key& a, const type_key& b) +{ + return a.key > b.key; +} + +template +struct write_completion_handler +{ + BlockType* block; + BidType bid; + request_ptr* req; + void operator () (request* /*completed_req*/) + { + * req = block->read(bid); + } +}; + +template +inline void write_out( + TypeKey* begin, + TypeKey* end, + BlockType*& cur_blk, + const BlockType* end_blk, + int_type& out_block, + int_type& out_pos, + RunType& run, + write_completion_handler*& next_read, + typename BlockType::bid_type*& bids, + request_ptr* write_reqs, + request_ptr* read_reqs, + InputBidIterator& it, + KeyExtractor keyobj) +{ + typedef typename BlockType::type type; + + type* elem = cur_blk->elem; + for (TypeKey* p = begin; p < end; p++) + { + elem[out_pos++] = *(p->ptr); + + if (out_pos >= BlockType::size) + { + run[out_block].key = keyobj(*(cur_blk->elem)); + + if (cur_blk < end_blk) + { + next_read->block = cur_blk; + next_read->req = read_reqs + out_block; + read_reqs[out_block] = NULL; + bids[out_block] = next_read->bid = *(it++); + + write_reqs[out_block] = cur_blk->write( + run[out_block].bid, + // postpone read of block from next run + // after write of block from this run + *(next_read++)); + } + else + { + write_reqs[out_block] = cur_blk->write(run[out_block].bid); + } + + cur_blk++; + elem = cur_blk->elem; + out_block++; + out_pos = 0; + } + } +} + +template < + typename BlockType, + typename RunType, + typename InputBidIterator, + typename KeyExtractor> +void +create_runs( + InputBidIterator it, + RunType** runs, + const unsigned_type nruns, + const unsigned_type m2, + KeyExtractor keyobj) +{ + typedef typename BlockType::value_type type; + typedef typename BlockType::bid_type bid_type; + typedef typename KeyExtractor::key_type key_type; + typedef type_key type_key_; + + block_manager* bm = block_manager::get_instance(); + BlockType* Blocks1 = new BlockType[m2]; + BlockType* Blocks2 = new BlockType[m2]; + bid_type* bids = new bid_type[m2]; + type_key_* refs1 = new type_key_[m2 * Blocks1->size]; + type_key_* refs2 = new type_key_[m2 * Blocks1->size]; + request_ptr* read_reqs = new request_ptr[m2]; + request_ptr* write_reqs = new request_ptr[m2]; + write_completion_handler* next_run_reads = + new write_completion_handler[m2]; + + RunType* run; + run = *runs; + int_type run_size = (*runs)->size(); + key_type offset = 0; + const int log_k1 = ilog2_ceil((m2 * BlockType::size * sizeof(type_key_) / STXXL_L2_SIZE) ? + (m2 * BlockType::size * sizeof(type_key_) / STXXL_L2_SIZE) : 2); + const int log_k2 = ilog2_floor(m2 * Blocks1->size) - log_k1 - 1; + STXXL_VERBOSE("log_k1: " << log_k1 << " log_k2:" << log_k2); + const int_type k1 = int_type(1) << log_k1; + const int_type k2 = int_type(1) << log_k2; + int_type* bucket1 = new int_type[k1]; + int_type* bucket2 = new int_type[k2]; + int_type i; + + disk_queues::get_instance()->set_priority_op(request_queue::WRITE); + + for (i = 0; i < run_size; i++) + { + bids[i] = *(it++); + read_reqs[i] = Blocks1[i].read(bids[i]); + } + + unsigned_type k = 0; + const int shift1 = (int)(sizeof(key_type) * 8 - log_k1); + const int shift2 = shift1 - log_k2; + STXXL_VERBOSE("shift1: " << shift1 << " shift2:" << shift2); + + for ( ; k < nruns; k++) + { + run = runs[k]; + run_size = run->size(); + + std::fill(bucket1, bucket1 + k1, 0); + + type_key_* ref_ptr = refs1; + for (i = 0; i < run_size; i++) + { + if (k) + write_reqs[i]->wait(); + + read_reqs[i]->wait(); + bm->delete_block(bids[i]); + + classify_block(Blocks1[i].begin(), Blocks1[i].end(), ref_ptr, bucket1, offset, shift1, keyobj); + } + + exclusive_prefix_sum(bucket1, k1); + classify(refs1, refs1 + run_size * Blocks1->size, refs2, bucket1, + offset, shift1); + + int_type out_block = 0; + int_type out_pos = 0; + unsigned_type next_run_size = (k < nruns - 1) ? (runs[k + 1]->size()) : 0; + + // recurse on each bucket + type_key_* c = refs2; + type_key_* d = refs1; + BlockType* cur_blk = Blocks2; + BlockType* end_blk = Blocks2 + next_run_size; + write_completion_handler* next_read = next_run_reads; + + for (i = 0; i < k1; i++) + { + type_key_* cEnd = refs2 + bucket1[i]; + type_key_* dEnd = refs1 + bucket1[i]; + + l1sort(c, cEnd, d, bucket2, k2, + offset + (key_type(1) << key_type(shift1)) * key_type(i), shift2); // key_type,key_type,... paranoia + + write_out( + d, dEnd, cur_blk, end_blk, + out_block, out_pos, *run, next_read, bids, + write_reqs, read_reqs, it, keyobj); + + c = cEnd; + d = dEnd; + } + + std::swap(Blocks1, Blocks2); + } + + wait_all(write_reqs, m2); + + delete[] bucket1; + delete[] bucket2; + delete[] refs1; + delete[] refs2; + delete[] Blocks1; + delete[] Blocks2; + delete[] bids; + delete[] next_run_reads; + delete[] read_reqs; + delete[] write_reqs; +} + +template +struct run_cursor2_cmp : public std::binary_function< + run_cursor2, + run_cursor2, + bool + > +{ + typedef run_cursor2 cursor_type; + KeyExtractor keyobj; + run_cursor2_cmp(KeyExtractor _keyobj) + : keyobj(_keyobj) + { } + inline bool operator () (const cursor_type& a, const cursor_type& b) const + { + if (UNLIKELY(b.empty())) + return true; + // sentinel emulation + if (UNLIKELY(a.empty())) + return false; + //sentinel emulation + + return (keyobj(a.current()) < keyobj(b.current())); + } + +private: + run_cursor2_cmp() { } +}; + +template +class key_comparison : public std::binary_function +{ + KeyExtractor ke; + +public: + key_comparison() { } + key_comparison(KeyExtractor ke_) : ke(ke_) { } + bool operator () (const RecordType& a, const RecordType& b) const + { + return ke(a) < ke(b); + } +}; + +template +bool check_ksorted_runs(RunType** runs, + unsigned_type nruns, + unsigned_type m, + KeyExtractor keyext) +{ + typedef BlockType block_type; + typedef typename BlockType::value_type value_type; + + STXXL_MSG("check_ksorted_runs Runs: " << nruns); + unsigned_type irun = 0; + for (irun = 0; irun < nruns; ++irun) + { + const unsigned_type nblocks_per_run = runs[irun]->size(); + unsigned_type blocks_left = nblocks_per_run; + block_type* blocks = new block_type[m]; + request_ptr* reqs = new request_ptr[m]; + value_type last = keyext.min_value(); + + for (unsigned_type off = 0; off < nblocks_per_run; off += m) + { + const unsigned_type nblocks = STXXL_MIN(blocks_left, m); + const unsigned_type nelements = nblocks * block_type::size; + blocks_left -= nblocks; + + for (unsigned_type j = 0; j < nblocks; ++j) + { + reqs[j] = blocks[j].read((*runs[irun])[off + j].bid); + } + wait_all(reqs, reqs + nblocks); + + if (off && (keyext(blocks[0][0]) < keyext(last))) + { + STXXL_MSG("check_sorted_runs wrong first value in the run " << irun); + STXXL_MSG(" first value: " << blocks[0][0] << " with key" << keyext(blocks[0][0])); + STXXL_MSG(" last value: " << last << " with key" << keyext(last)); + for (unsigned_type k = 0; k < block_type::size; ++k) + STXXL_MSG("Element " << k << " in the block is :" << blocks[0][k] << " key: " << keyext(blocks[0][k])); + + delete[] reqs; + delete[] blocks; + return false; + } + + for (unsigned_type j = 0; j < nblocks; ++j) + { + if (keyext(blocks[j][0]) != (*runs[irun])[off + j].key) + { + STXXL_MSG("check_sorted_runs wrong trigger in the run " << irun << " block " << (off + j)); + STXXL_MSG(" trigger value: " << (*runs[irun])[off + j].key); + STXXL_MSG("Data in the block:"); + for (unsigned_type k = 0; k < block_type::size; ++k) + STXXL_MSG("Element " << k << " in the block is :" << blocks[j][k] << " with key: " << keyext(blocks[j][k])); + + STXXL_MSG("BIDS:"); + for (unsigned_type k = 0; k < nblocks; ++k) + { + if (k == j) + STXXL_MSG("Bad one comes next."); + STXXL_MSG("BID " << (off + k) << " is: " << ((*runs[irun])[off + k].bid)); + } + + delete[] reqs; + delete[] blocks; + return false; + } + } + if (!stxxl::is_sorted(make_element_iterator(blocks, 0), + make_element_iterator(blocks, nelements), + key_comparison())) + { + STXXL_MSG("check_sorted_runs wrong order in the run " << irun); + STXXL_MSG("Data in blocks:"); + for (unsigned_type j = 0; j < nblocks; ++j) + { + for (unsigned_type k = 0; k < block_type::size; ++k) + STXXL_MSG(" Element " << k << " in block " << (off + j) << " is :" << blocks[j][k] << " with key: " << keyext(blocks[j][k])); + } + STXXL_MSG("BIDS:"); + for (unsigned_type k = 0; k < nblocks; ++k) + { + STXXL_MSG("BID " << (k + off) << " is: " << ((*runs[irun])[k + off].bid)); + } + + delete[] reqs; + delete[] blocks; + return false; + } + last = blocks[nblocks - 1][block_type::size - 1]; + } + + assert(blocks_left == 0); + delete[] reqs; + delete[] blocks; + } + + return true; +} + +template +void merge_runs(RunType** in_runs, unsigned_type nruns, RunType* out_run, unsigned_type _m, KeyExtractor keyobj) +{ + typedef BlockType block_type; + typedef block_prefetcher prefetcher_type; + typedef run_cursor2 run_cursor_type; + + unsigned_type i; + RunType consume_seq(out_run->size()); + + int_type* prefetch_seq = new int_type[out_run->size()]; + + typename RunType::iterator copy_start = consume_seq.begin(); + for (i = 0; i < nruns; i++) + { + // TODO: try to avoid copy + copy_start = std::copy( + in_runs[i]->begin(), + in_runs[i]->end(), + copy_start); + } + std::stable_sort(consume_seq.begin(), consume_seq.end() _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL); + + size_t disks_number = config::get_instance()->disks_number(); + +#ifdef PLAY_WITH_OPT_PREF + const int_type n_write_buffers = 4 * disks_number; +#else + const int_type n_prefetch_buffers = STXXL_MAX(int_type(2 * disks_number), (3 * (int_type(_m) - int_type(nruns)) / 4)); + STXXL_VERBOSE("Prefetch buffers " << n_prefetch_buffers); + const int_type n_write_buffers = STXXL_MAX(int_type(2 * disks_number), int_type(_m) - int_type(nruns) - int_type(n_prefetch_buffers)); + STXXL_VERBOSE("Write buffers " << n_write_buffers); + // heuristic + const int_type n_opt_prefetch_buffers = 2 * int_type(disks_number) + (3 * (int_type(n_prefetch_buffers) - int_type(2 * disks_number))) / 10; + STXXL_VERBOSE("Prefetch buffers " << n_opt_prefetch_buffers); +#endif + +#if STXXL_SORT_OPTIMAL_PREFETCHING + compute_prefetch_schedule( + consume_seq, + prefetch_seq, + n_opt_prefetch_buffers, + config::get_instance()->get_max_device_id()); +#else + for (i = 0; i < out_run->size(); i++) + prefetch_seq[i] = i; + +#endif + + prefetcher_type prefetcher(consume_seq.begin(), + consume_seq.end(), + prefetch_seq, + nruns + n_prefetch_buffers); + + buffered_writer writer(n_write_buffers, n_write_buffers / 2); + + unsigned_type out_run_size = out_run->size(); + + run_cursor2_cmp cmp(keyobj); + loser_tree< + run_cursor_type, + run_cursor2_cmp > + losers(&prefetcher, nruns, cmp); + + block_type* out_buffer = writer.get_free_block(); + + for (i = 0; i < out_run_size; i++) + { + losers.multi_merge(out_buffer->elem, out_buffer->elem + block_type::size); + (*out_run)[i].key = keyobj(out_buffer->elem[0]); + out_buffer = writer.write(out_buffer, (*out_run)[i].bid); + } + + delete[] prefetch_seq; + + block_manager* bm = block_manager::get_instance(); + for (i = 0; i < nruns; i++) + { + unsigned_type sz = in_runs[i]->size(); + for (unsigned_type j = 0; j < sz; j++) + bm->delete_block((*in_runs[i])[j].bid); + + delete in_runs[i]; + } +} + +template +simple_vector< + trigger_entry + >* +ksort_blocks(InputBidIterator input_bids, unsigned_type _n, + unsigned_type _m, KeyExtractor keyobj) +{ + typedef BlockType block_type; + typedef typename BlockType::value_type type; + typedef typename KeyExtractor::key_type key_type; + typedef typename BlockType::bid_type bid_type; + typedef trigger_entry trigger_entry_type; + typedef simple_vector run_type; + typedef typename interleaved_alloc_traits::strategy interleaved_alloc_strategy; + + unsigned_type m2 = div_ceil(_m, 2); + const unsigned_type m2_rf = m2 * block_type::raw_size / + (block_type::raw_size + block_type::size * sizeof(type_key)); + STXXL_VERBOSE("Reducing number of blocks in a run from " << m2 << " to " << + m2_rf << " due to key size: " << sizeof(typename KeyExtractor::key_type) << " bytes"); + m2 = m2_rf; + unsigned_type full_runs = _n / m2; + unsigned_type partial_runs = ((_n % m2) ? 1 : 0); + unsigned_type nruns = full_runs + partial_runs; + unsigned_type i; + + block_manager* mng = block_manager::get_instance(); + + STXXL_VERBOSE("n=" << _n << " nruns=" << nruns << "=" << full_runs << "+" << partial_runs); + + double begin = timestamp(), after_runs_creation, end; + + run_type** runs = new run_type*[nruns]; + + for (i = 0; i < full_runs; i++) + runs[i] = new run_type(m2); + +#ifdef INTERLEAVED_ALLOC + if (partial_runs) + { + unsigned_type last_run_size = _n - full_runs * m2; + runs[i] = new run_type(last_run_size); + + mng->new_blocks(interleaved_alloc_strategy(nruns, AllocStrategy()), + runs2bid_array_adaptor2 + (runs, 0, nruns, last_run_size), + runs2bid_array_adaptor2 + (runs, _n, nruns, last_run_size)); + } + else + mng->new_blocks(interleaved_alloc_strategy(nruns, AllocStrategy()), + runs2bid_array_adaptor + (runs, 0, nruns), + runs2bid_array_adaptor + (runs, _n, nruns)); + +#else + if (partial_runs) + runs[i] = new run_type(_n - full_runs * m2); + + for (i = 0; i < nruns; i++) + { + mng->new_blocks(AllocStrategy(), make_bid_iterator(runs[i]->begin()), make_bid_iterator(runs[i]->end())); + } +#endif + + create_runs( + input_bids, runs, nruns, m2, keyobj); + + after_runs_creation = timestamp(); + + double io_wait_after_rf = stats::get_instance()->get_io_wait_time(); + + disk_queues::get_instance()->set_priority_op(request_queue::WRITE); + + const int_type merge_factor = optimal_merge_factor(nruns, _m); + run_type** new_runs; + + while (nruns > 1) + { + int_type new_nruns = div_ceil(nruns, merge_factor); + STXXL_VERBOSE("Starting new merge phase: nruns: " << nruns << + " opt_merge_factor: " << merge_factor << " m:" << _m << " new_nruns: " << new_nruns); + + new_runs = new run_type*[new_nruns]; + + int_type runs_left = nruns; + int_type cur_out_run = 0; + int_type blocks_in_new_run = 0; + + while (runs_left > 0) + { + int_type runs2merge = STXXL_MIN(runs_left, merge_factor); + blocks_in_new_run = 0; + for (unsigned_type i = nruns - runs_left; i < (nruns - runs_left + runs2merge); i++) + blocks_in_new_run += runs[i]->size(); + + // allocate run + new_runs[cur_out_run++] = new run_type(blocks_in_new_run); + runs_left -= runs2merge; + } + // allocate blocks in the new runs + if (cur_out_run == 1 && blocks_in_new_run == int_type(_n) && !input_bids->is_managed()) + { + // if we sort a file we can reuse the input bids for the output + InputBidIterator cur = input_bids; + for (int_type i = 0; cur != (input_bids + _n); ++cur) + { + (*new_runs[0])[i++].bid = *cur; + } + + bid_type& firstBID = (*new_runs[0])[0].bid; + if (firstBID.is_managed()) + { + // the first block does not belong to the file + // need to reallocate it + mng->new_block(FR(), firstBID); + } + bid_type& lastBID = (*new_runs[0])[_n - 1].bid; + if (lastBID.is_managed()) + { + // the first block does not belong to the file + // need to reallocate it + mng->new_block(FR(), lastBID); + } + } + else + { + mng->new_blocks(interleaved_alloc_strategy(new_nruns, AllocStrategy()), + runs2bid_array_adaptor2(new_runs, 0, new_nruns, blocks_in_new_run), + runs2bid_array_adaptor2(new_runs, _n, new_nruns, blocks_in_new_run)); + } + + // merge all + runs_left = nruns; + cur_out_run = 0; + while (runs_left > 0) + { + int_type runs2merge = STXXL_MIN(runs_left, merge_factor); +#if STXXL_CHECK_ORDER_IN_SORTS + assert((check_ksorted_runs(runs + nruns - runs_left, runs2merge, m2, keyobj))); +#endif + STXXL_VERBOSE("Merging " << runs2merge << " runs"); + merge_runs(runs + nruns - runs_left, + runs2merge, *(new_runs + (cur_out_run++)), _m, keyobj); + runs_left -= runs2merge; + } + + nruns = new_nruns; + delete[] runs; + runs = new_runs; + } + + run_type* result = *runs; + delete[] runs; + + end = timestamp(); + + STXXL_VERBOSE("Elapsed time : " << end - begin << " s. Run creation time: " << + after_runs_creation - begin << " s"); + STXXL_VERBOSE("Time in I/O wait(rf): " << io_wait_after_rf << " s"); + STXXL_VERBOSE(*stats::get_instance()); + + return result; +} + +} // namespace ksort_local + +/*! + * Sort records with integer keys, see \ref design_algo_ksort. + * + * stxxl::ksort sorts the elements in [first, last) into ascending order, + * meaning that if \c i and \c j are any two valid iterators in [first, last) + * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as + * std::sort and stxxl::sort, stxxl::ksort is not guaranteed to be stable. That + * is, suppose that \c *i and \c *j are equivalent: neither one is less than + * the other. It is not guaranteed that the relative order of these two + * elements will be preserved by stxxl::ksort. + * + * The two versions of stxxl::ksort differ in how they define whether one + * element is less than another. The first version assumes that the elements + * have \c key() member function that returns an integral key (32 or 64 bit), + * as well as the minimum and the maximum element values. The second version + * compares objects extracting the keys using \c keyobj object, that is in turn + * provides min and max element values. + * + * The sorter's internal memory consumption is bounded by \c M bytes. + * + * \param first object of model of \c ext_random_access_iterator concept + * \param last object of model of \c ext_random_access_iterator concept + * \param keyobj \link design_algo_ksort_key_extractor key extractor \endlink object + * \param M amount of memory for internal use (in bytes) + */ +template +void ksort(ExtIterator first, ExtIterator last, KeyExtractor keyobj, unsigned_type M) +{ + typedef simple_vector< + ksort_local::trigger_entry< + typename ExtIterator::bid_type, typename KeyExtractor::key_type + > + > run_type; + typedef typename ExtIterator::vector_type::value_type value_type; + typedef typename ExtIterator::bid_type bid_type; + typedef typename ExtIterator::block_type block_type; + typedef typename ExtIterator::vector_type::alloc_strategy_type alloc_strategy_type; + typedef typename ExtIterator::bids_container_iterator bids_container_iterator; + + unsigned_type n = 0; + block_manager* mng = block_manager::get_instance(); + + first.flush(); + + if ((last - first) * sizeof(value_type) < M) + { + stl_in_memory_sort(first, last, + ksort_local::key_comparison(keyobj)); + } + else + { + assert(2 * block_type::raw_size <= M); + + if (first.block_offset()) + { + if (last.block_offset()) // first and last element reside + // not in the beginning of the block + { + block_type* first_block = new block_type; + block_type* last_block = new block_type; + bid_type first_bid, last_bid; + request_ptr req; + + req = first_block->read(*first.bid()); + mng->new_block(FR(), first_bid); // try to overlap + mng->new_block(FR(), last_bid); + req->wait(); + + req = last_block->read(*last.bid()); + + unsigned_type i = 0; + for ( ; i < first.block_offset(); i++) + { + first_block->elem[i] = keyobj.min_value(); + } + + req->wait(); + + req = first_block->write(first_bid); + for (i = last.block_offset(); i < block_type::size; i++) + { + last_block->elem[i] = keyobj.max_value(); + } + + req->wait(); + + req = last_block->write(last_bid); + + n = last.bid() - first.bid() + 1; + + std::swap(first_bid, *first.bid()); + std::swap(last_bid, *last.bid()); + + req->wait(); + + delete first_block; + delete last_block; + + run_type* out = + ksort_local::ksort_blocks< + block_type, alloc_strategy_type, + bids_container_iterator, KeyExtractor + >(first.bid(), n, M / block_type::raw_size, keyobj); + + first_block = new block_type; + last_block = new block_type; + block_type* sorted_first_block = new block_type; + block_type* sorted_last_block = new block_type; + request_ptr* reqs = new request_ptr[2]; + + reqs[0] = first_block->read(first_bid); + reqs[1] = sorted_first_block->read((*(out->begin())).bid); + wait_all(reqs, 2); + + reqs[0] = last_block->read(last_bid); + reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); + + for (i = first.block_offset(); i < block_type::size; i++) + { + first_block->elem[i] = sorted_first_block->elem[i]; + } + wait_all(reqs, 2); + + req = first_block->write(first_bid); + + for (i = 0; i < last.block_offset(); i++) + { + last_block->elem[i] = sorted_last_block->elem[i]; + } + + req->wait(); + + req = last_block->write(last_bid); + + mng->delete_block(out->begin()->bid); + mng->delete_block((*out)[out->size() - 1].bid); + + *first.bid() = first_bid; + *last.bid() = last_bid; + + typename run_type::iterator it = out->begin(); + it++; + bids_container_iterator cur_bid = first.bid(); + cur_bid++; + + for ( ; cur_bid != last.bid(); cur_bid++, it++) + { + *cur_bid = (*it).bid; + } + + delete first_block; + delete sorted_first_block; + delete sorted_last_block; + delete[] reqs; + delete out; + + req->wait(); + + delete last_block; + } + else + { + // first element resides + // not in the beginning of the block + + block_type* first_block = new block_type; + bid_type first_bid; + request_ptr req; + + req = first_block->read(*first.bid()); + mng->new_block(FR(), first_bid); // try to overlap + req->wait(); + + unsigned_type i = 0; + for ( ; i < first.block_offset(); i++) + { + first_block->elem[i] = keyobj.min_value(); + } + + req = first_block->write(first_bid); + + n = last.bid() - first.bid(); + + std::swap(first_bid, *first.bid()); + + req->wait(); + + delete first_block; + + run_type* out = + ksort_local::ksort_blocks< + block_type, alloc_strategy_type, + bids_container_iterator, KeyExtractor + >(first.bid(), n, M / block_type::raw_size, keyobj); + + first_block = new block_type; + + block_type* sorted_first_block = new block_type; + + request_ptr* reqs = new request_ptr[2]; + + reqs[0] = first_block->read(first_bid); + reqs[1] = sorted_first_block->read((*(out->begin())).bid); + wait_all(reqs, 2); + + for (i = first.block_offset(); i < block_type::size; i++) + { + first_block->elem[i] = sorted_first_block->elem[i]; + } + + req = first_block->write(first_bid); + + mng->delete_block(out->begin()->bid); + + *first.bid() = first_bid; + + typename run_type::iterator it = out->begin(); + it++; + bids_container_iterator cur_bid = first.bid(); + cur_bid++; + + for ( ; cur_bid != last.bid(); cur_bid++, it++) + { + *cur_bid = (*it).bid; + } + + *cur_bid = (*it).bid; + + delete sorted_first_block; + delete[] reqs; + delete out; + + req->wait(); + + delete first_block; + } + } + else + { + if (last.block_offset()) // last element resides + // not in the beginning of the block + { + block_type* last_block = new block_type; + bid_type last_bid; + request_ptr req; + unsigned_type i; + + req = last_block->read(*last.bid()); + mng->new_block(FR(), last_bid); + req->wait(); + + for (i = last.block_offset(); i < block_type::size; i++) + { + last_block->elem[i] = keyobj.max_value(); + } + + req = last_block->write(last_bid); + + n = last.bid() - first.bid() + 1; + + std::swap(last_bid, *last.bid()); + + req->wait(); + + delete last_block; + + run_type* out = + ksort_local::ksort_blocks< + block_type, alloc_strategy_type, + bids_container_iterator, KeyExtractor + >(first.bid(), n, M / block_type::raw_size, keyobj); + + last_block = new block_type; + block_type* sorted_last_block = new block_type; + request_ptr* reqs = new request_ptr[2]; + + reqs[0] = last_block->read(last_bid); + reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); + wait_all(reqs, 2); + + for (i = 0; i < last.block_offset(); i++) + { + last_block->elem[i] = sorted_last_block->elem[i]; + } + + req = last_block->write(last_bid); + + mng->delete_block((*out)[out->size() - 1].bid); + + *last.bid() = last_bid; + + typename run_type::iterator it = out->begin(); + bids_container_iterator cur_bid = first.bid(); + + for ( ; cur_bid != last.bid(); cur_bid++, it++) + { + *cur_bid = (*it).bid; + } + + delete sorted_last_block; + delete[] reqs; + delete out; + + req->wait(); + + delete last_block; + } + else + { + // first and last element reside in the beginning of blocks + n = last.bid() - first.bid(); + + run_type* out = + ksort_local::ksort_blocks< + block_type, alloc_strategy_type, + bids_container_iterator, KeyExtractor + >(first.bid(), n, M / block_type::raw_size, keyobj); + + typename run_type::iterator it = out->begin(); + bids_container_iterator cur_bid = first.bid(); + + for ( ; cur_bid != last.bid(); cur_bid++, it++) + { + *cur_bid = (*it).bid; + } + + delete out; + } + } + } + +#if STXXL_CHECK_ORDER_IN_SORTS + typedef typename ExtIterator::const_iterator const_iterator; + STXXL_ASSERT(stxxl::is_sorted(const_iterator(first), const_iterator(last), + ksort_local::key_comparison())); +#endif +} + +template +struct ksort_defaultkey +{ + typedef typename RecordType::key_type key_type; + key_type operator () (const RecordType& obj) const + { + return obj.key(); + } + RecordType max_value() const + { + return RecordType::max_value(); + } + RecordType min_value() const + { + return RecordType::min_value(); + } +}; + +/*! + * Sort records with integer keys, see \ref design_algo_ksort. + * + * stxxl::ksort sorts the elements in [first, last) into ascending order, + * meaning that if \c i and \c j are any two valid iterators in [first, last) + * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as + * std::sort and stxxl::sort, stxxl::ksort is not guaranteed to be stable. That + * is, suppose that \c *i and \c *j are equivalent: neither one is less than + * the other. It is not guaranteed that the relative order of these two + * elements will be preserved by stxxl::ksort. + * + * \param first object of model of \c ext_random_access_iterator concept + * \param last object of model of \c ext_random_access_iterator concept + * \param M amount of buffers for internal use + * \remark Order in the result is non-stable + */ +template +void ksort(ExtIterator first, ExtIterator last, unsigned_type M) +{ + ksort(first, last, + ksort_defaultkey(), M); +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_KSORT_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/losertree.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/losertree.h new file mode 100644 index 0000000000..5820ccc7bf --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/losertree.h @@ -0,0 +1,259 @@ +/*************************************************************************** + * include/stxxl/bits/algo/losertree.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 1999 Peter Sanders + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_ALGO_LOSERTREE_HEADER +#define STXXL_ALGO_LOSERTREE_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +class loser_tree : private noncopyable +{ + int logK; + int_type k; + int_type* entry; + RunCursorType* current; + RunCursorCmpType cmp; + + int_type init_winner(int_type root) + { + if (root >= k) + { + return root - k; + } + else + { + int_type left = init_winner(2 * root); + int_type right = init_winner(2 * root + 1); + if (cmp(current[left], current[right])) + { + entry[root] = right; + return left; + } + else + { + entry[root] = left; + return right; + } + } + } + +public: + typedef typename RunCursorType::prefetcher_type prefetcher_type; + typedef typename RunCursorType::value_type value_type; + + loser_tree( + prefetcher_type* p, + int_type nruns, + RunCursorCmpType c) + : cmp(c) + { + int_type i; + logK = ilog2_ceil(nruns); + int_type kReg = k = (int_type(1) << logK); + + STXXL_VERBOSE2("loser_tree: logK=" << logK << " nruns=" << nruns << " K=" << kReg); + +#ifdef STXXL_SORT_SINGLE_PREFETCHER + current = new RunCursorType[kReg]; + RunCursorType::set_prefetcher(p); +#else + current = new RunCursorType[kReg]; + for (i = 0; i < kReg; ++i) + current[i].prefetcher() = p; +#endif + entry = new int_type[(kReg << 1)]; + // init cursors + for (i = 0; i < nruns; ++i) + { + current[i].buffer = p->pull_block(); + //current[i].pos = 0; // done in constructor + entry[kReg + i] = i; + } + + for (i = nruns; i < kReg; ++i) + { + current[i].make_inf(); + entry[kReg + i] = i; + } + + entry[0] = init_winner(1); + } + ~loser_tree() + { + delete[] current; + delete[] entry; + } + + void swap(loser_tree& obj) + { + std::swap(logK, obj.logK); + std::swap(k, obj.k); + std::swap(entry, obj.entry); + std::swap(current, obj.current); + std::swap(cmp, obj.cmp); + } + +private: + template + void multi_merge_unrolled(value_type* out_first, value_type* out_last) + { + RunCursorType* currentE, * winnerE; + int_type* regEntry = entry; + int_type winnerIndex = regEntry[0]; + + while (LIKELY(out_first != out_last)) + { + winnerE = current + winnerIndex; + *(out_first) = winnerE->current(); + ++out_first; + + ++(*winnerE); + +#define TreeStep(L) \ + if (LogK >= L) \ + { \ + currentE = current + \ + regEntry[(winnerIndex + (1 << LogK)) >> (((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0)]; \ + if (cmp(*currentE, *winnerE)) \ + { \ + std::swap(regEntry[(winnerIndex + (1 << LogK)) \ + >> (((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0)], winnerIndex); \ + winnerE = currentE; \ + } \ + } + + TreeStep(10); + TreeStep(9); + TreeStep(8); + TreeStep(7); + TreeStep(6); + TreeStep(5); + TreeStep(4); + TreeStep(3); + TreeStep(2); + TreeStep(1); + +#undef TreeStep + } + + regEntry[0] = winnerIndex; + } + + void multi_merge_unrolled_0(value_type* out_first, value_type* out_last) + { + while (LIKELY(out_first != out_last)) + { + *out_first = current->current(); + ++out_first; + ++(*current); + } + } + + void multi_merge_k(value_type* out_first, value_type* out_last) + { + RunCursorType* currentE, * winnerE; + int_type kReg = k; + int_type winnerIndex = entry[0]; + + while (LIKELY(out_first != out_last)) + { + winnerE = current + winnerIndex; + *(out_first) = winnerE->current(); + ++out_first; + + ++(*winnerE); + + for (int_type i = (winnerIndex + kReg) >> 1; i > 0; i >>= 1) + { + currentE = current + entry[i]; + + if (cmp(*currentE, *winnerE)) + { + std::swap(entry[i], winnerIndex); + winnerE = currentE; + } + } + } + + entry[0] = winnerIndex; + } + +public: + void multi_merge(value_type* out_first, value_type* out_last) + { + switch (logK) + { + case 0: + multi_merge_unrolled_0(out_first, out_last); + break; + case 1: + multi_merge_unrolled<1>(out_first, out_last); + break; + case 2: + multi_merge_unrolled<2>(out_first, out_last); + break; + case 3: + multi_merge_unrolled<3>(out_first, out_last); + break; + case 4: + multi_merge_unrolled<4>(out_first, out_last); + break; + case 5: + multi_merge_unrolled<5>(out_first, out_last); + break; + case 6: + multi_merge_unrolled<6>(out_first, out_last); + break; + case 7: + multi_merge_unrolled<7>(out_first, out_last); + break; + case 8: + multi_merge_unrolled<8>(out_first, out_last); + break; + case 9: + multi_merge_unrolled<9>(out_first, out_last); + break; + case 10: + multi_merge_unrolled<10>(out_first, out_last); + break; + default: + multi_merge_k(out_first, out_last); + break; + } + } +}; + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::loser_tree& a, + stxxl::loser_tree& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_ALGO_LOSERTREE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/random_shuffle.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/random_shuffle.h new file mode 100644 index 0000000000..531f6ce844 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/random_shuffle.h @@ -0,0 +1,385 @@ +/*************************************************************************** + * include/stxxl/bits/algo/random_shuffle.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Manuel Krings + * Copyright (C) 2007 Markus Westphal + * Copyright (C) 2009, 2010 Andreas Beckmann + * + * 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 STXXL_ALGO_RANDOM_SHUFFLE_HEADER +#define STXXL_ALGO_RANDOM_SHUFFLE_HEADER + +// TODO: improve main memory consumption in recursion +// (free stacks buffers) +// TODO: shuffle small input in internal memory + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlalgo +//! \{ + +//! External equivalent of std::random_shuffle +//! \param first begin of the range to shuffle +//! \param last end of the range to shuffle +//! \param rand random number generator object (functor) +//! \param M number of bytes for internal use +//! \param AS parallel disk allocation strategy +//! +//! - BlockSize size of the block to use for external memory data structures +//! - PageSize page size in blocks to use for external memory data structures +template +void random_shuffle(ExtIterator first, + ExtIterator last, + RandomNumberGenerator& rand, + unsigned_type M, + AllocStrategy AS = STXXL_DEFAULT_ALLOC_STRATEGY()) +{ + STXXL_UNUSED(AS); // FIXME: Why is this not being used? + typedef typename ExtIterator::value_type value_type; + typedef typename STACK_GENERATOR< + value_type, external, grow_shrink2, PageSize, + BlockSize, void, 0, AllocStrategy + >::result stack_type; + typedef typename stack_type::block_type block_type; + + STXXL_VERBOSE1("random_shuffle: Plain Version"); + STXXL_STATIC_ASSERT(int(BlockSize) < 0 && "This implementation was never tested. Please report to the stxxl developers if you have an ExtIterator that works with this implementation."); + + int64 n = last - first; // the number of input elements + + // make sure we have at least 6 blocks + 1 page + if (M < 6 * BlockSize + PageSize * BlockSize) { + STXXL_ERRMSG("random_shuffle: insufficient memory, " << M << " bytes supplied,"); + M = 6 * BlockSize + PageSize * BlockSize; + STXXL_ERRMSG("random_shuffle: increasing to " << M << " bytes (6 blocks + 1 page)"); + } + + int_type k = M / (3 * BlockSize); // number of buckets + + int64 i, j, size = 0; + + value_type* temp_array; + typedef typename VECTOR_GENERATOR< + value_type, PageSize, 4, BlockSize, AllocStrategy + >::result temp_vector_type; + temp_vector_type* temp_vector; + + STXXL_VERBOSE1("random_shuffle: " << M / BlockSize - k << " write buffers for " << k << " buckets"); + read_write_pool pool(0, M / BlockSize - k); // no read buffers and M/B-k write buffers + + stack_type** buckets; + + // create and put buckets into container + buckets = new stack_type*[k]; + for (j = 0; j < k; j++) + buckets[j] = new stack_type(pool, 0); + + ///// Reading input ///////////////////// + typedef typename stream::streamify_traits::stream_type input_stream; + input_stream in = stream::streamify(first, last); + + // distribute input into random buckets + int_type random_bucket = 0; + for (i = 0; i < n; ++i) { + random_bucket = rand(k); + buckets[random_bucket]->push(*in); // reading the current input element + ++in; // go to the next input element + } + + ///// Processing ////////////////////// + // resize buffers + pool.resize_write(0); + pool.resize_prefetch(PageSize); + + unsigned_type space_left = M - k * BlockSize - + PageSize * BlockSize; // remaining int space + ExtIterator Writer = first; + ExtIterator it = first; + + for (i = 0; i < k; i++) { + STXXL_VERBOSE1("random_shuffle: bucket no " << i << " contains " << buckets[i]->size() << " elements"); + } + + // shuffle each bucket + for (i = 0; i < k; i++) { + buckets[i]->set_prefetch_aggr(PageSize); + size = buckets[i]->size(); + + // does the bucket fit into memory? + if (size * sizeof(value_type) < space_left) { + STXXL_VERBOSE1("random_shuffle: no recursion"); + + // copy bucket into temp. array + temp_array = new value_type[size]; + for (j = 0; j < size; j++) { + temp_array[j] = buckets[i]->top(); + buckets[i]->pop(); + } + + // shuffle + potentially_parallel:: + random_shuffle(temp_array, temp_array + size, rand); + + // write back + for (j = 0; j < size; j++) { + *Writer = temp_array[j]; + ++Writer; + } + + // free memory + delete[] temp_array; + } + else { + STXXL_VERBOSE1("random_shuffle: recursion"); + + // copy bucket into temp. stxxl::vector + temp_vector = new temp_vector_type(size); + + for (j = 0; j < size; j++) { + (*temp_vector)[j] = buckets[i]->top(); + buckets[i]->pop(); + } + + pool.resize_prefetch(0); + space_left += PageSize * BlockSize; + STXXL_VERBOSE1("random_shuffle: Space left: " << space_left); + + // recursive shuffle + stxxl::random_shuffle(temp_vector->begin(), + temp_vector->end(), rand, space_left); + + pool.resize_prefetch(PageSize); + + // write back + for (j = 0; j < size; j++) { + *Writer = (*temp_vector)[j]; + ++Writer; + } + + // free memory + delete temp_vector; + } + + // free bucket + delete buckets[i]; + space_left += BlockSize; + } + + delete[] buckets; +} + +//! External equivalent of std::random_shuffle (specialization for stxxl::vector) +//! \param first begin of the range to shuffle +//! \param last end of the range to shuffle +//! \param rand random number generator object (functor) +//! \param M number of bytes for internal use +template +void random_shuffle( + stxxl::vector_iterator first, + stxxl::vector_iterator last, + RandomNumberGenerator& rand, + unsigned_type M) +{ + typedef stxxl::vector_iterator ExtIterator; + typedef typename ExtIterator::value_type value_type; + typedef typename ExtIterator::bids_container_iterator bids_container_iterator; + typedef typename stxxl::STACK_GENERATOR::result stack_type; + typedef typename stack_type::block_type block_type; + + STXXL_VERBOSE1("random_shuffle: Vector Version"); + + // make sure we have at least 6 blocks + 1 page + if (M < 6 * BlockSize + PageSize * BlockSize) { + STXXL_ERRMSG("random_shuffle: insufficient memory, " << M << " bytes supplied,"); + M = 6 * BlockSize + PageSize * BlockSize; + STXXL_ERRMSG("random_shuffle: increasing to " << M << " bytes (6 blocks + 1 page)"); + } + + stxxl::int64 n = last - first; // the number of input elements + int_type k = M / (3 * BlockSize); // number of buckets + + stxxl::int64 i, j, size = 0; + + value_type* temp_array; + typedef typename stxxl::VECTOR_GENERATOR< + value_type, PageSize, 4, BlockSize, AllocStrategy + >::result temp_vector_type; + temp_vector_type* temp_vector; + + // no read buffers and M/B-k write buffers + stxxl::read_write_pool pool(0, M / BlockSize - k); + + stack_type** buckets; + + // create and put buckets into container + buckets = new stack_type*[k]; + for (j = 0; j < k; j++) + buckets[j] = new stack_type(pool, 0); + + typedef buf_istream buf_istream_type; + typedef buf_ostream buf_ostream_type; + + first.flush(); // flush container + + // create prefetching stream, + buf_istream_type in(first.bid(), last.bid() + ((last.block_offset()) ? 1 : 0), 2); + // create buffered write stream for blocks + buf_ostream_type out(first.bid(), 2); + + ExtIterator _cur = first - first.block_offset(); + + // leave part of the block before _begin untouched (e.g. copy) + for ( ; _cur != first; ++_cur) + { + typename ExtIterator::value_type tmp; + in >> tmp; + out << tmp; + } + + ///// Reading input ///////////////////// + + // distribute input into random buckets + int_type random_bucket = 0; + for (i = 0; i < n; ++i, ++_cur) { + random_bucket = rand((unsigned)k); + typename ExtIterator::value_type tmp; + in >> tmp; + buckets[random_bucket]->push(tmp); // reading the current input element + } + + ///// Processing ////////////////////// + // resize buffers + pool.resize_write(0); + pool.resize_prefetch(PageSize); + + // remaining int space + unsigned_type space_left = M - k * BlockSize - PageSize * BlockSize; + + for (i = 0; i < k; i++) { + STXXL_VERBOSE1("random_shuffle: bucket no " << i << " contains " << buckets[i]->size() << " elements"); + } + + // shuffle each bucket + for (i = 0; i < k; i++) { + buckets[i]->set_prefetch_aggr(PageSize); + size = buckets[i]->size(); + + // does the bucket fit into memory? + if (size * sizeof(value_type) < space_left) { + STXXL_VERBOSE1("random_shuffle: no recursion"); + + // copy bucket into temp. array + temp_array = new value_type[(size_t)size]; + for (j = 0; j < size; j++) { + temp_array[j] = buckets[i]->top(); + buckets[i]->pop(); + } + + // shuffle + potentially_parallel:: + random_shuffle(temp_array, temp_array + size, rand); + + // write back + for (j = 0; j < size; j++) { + typename ExtIterator::value_type tmp; + tmp = temp_array[j]; + out << tmp; + } + + // free memory + delete[] temp_array; + } + else { + STXXL_VERBOSE1("random_shuffle: recursion"); + // copy bucket into temp. stxxl::vector + temp_vector = new temp_vector_type(size); + + for (j = 0; j < size; j++) { + (*temp_vector)[j] = buckets[i]->top(); + buckets[i]->pop(); + } + + pool.resize_prefetch(0); + space_left += PageSize * BlockSize; + + STXXL_VERBOSE1("random_shuffle: Space left: " << space_left); + + // recursive shuffle + stxxl::random_shuffle(temp_vector->begin(), + temp_vector->end(), rand, space_left); + + pool.resize_prefetch(PageSize); + + // write back + for (j = 0; j < size; j++) { + typename ExtIterator::value_type tmp; + tmp = (*temp_vector)[j]; + out << tmp; + } + + // free memory + delete temp_vector; + } + + // free bucket + delete buckets[i]; + space_left += BlockSize; + } + + delete[] buckets; + + // leave part of the block after _end untouched + if (last.block_offset()) + { + ExtIterator last_block_end = last + (block_type::size - last.block_offset()); + for ( ; _cur != last_block_end; ++_cur) + { + typename ExtIterator::value_type tmp; + in >> tmp; + out << tmp; + } + } +} + +//! External equivalent of std::random_shuffle (specialization for stxxl::vector) +//! \param first begin of the range to shuffle +//! \param last end of the range to shuffle +//! \param M number of bytes for internal use +template +inline +void random_shuffle( + stxxl::vector_iterator first, + stxxl::vector_iterator last, + unsigned_type M) +{ + stxxl::random_number<> rand; + stxxl::random_shuffle(first, last, rand, M); +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_RANDOM_SHUFFLE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/run_cursor.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/run_cursor.h new file mode 100644 index 0000000000..7948b33dab --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/run_cursor.h @@ -0,0 +1,126 @@ +/*************************************************************************** + * include/stxxl/bits/algo/run_cursor.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_ALGO_RUN_CURSOR_HEADER +#define STXXL_ALGO_RUN_CURSOR_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +struct run_cursor +{ + unsigned_type pos; + BlockType* buffer; + + run_cursor() : pos(0), buffer(NULL) { } + + inline typename BlockType::const_reference current() const + { + return (*buffer)[pos]; + } + inline void operator ++ () + { + ++pos; + } +}; + +#ifdef STXXL_SORT_SINGLE_PREFETCHER + +template +struct have_prefetcher +{ + static void* untyped_prefetcher; +}; + +#endif + +template +struct run_cursor2 : public run_cursor +#ifdef STXXL_SORT_SINGLE_PREFETCHER + , public have_prefetcher<> +#endif +{ + typedef BlockType block_type; + typedef PrefetcherType prefetcher_type; + typedef run_cursor2 _Self; + typedef typename block_type::value_type value_type; + + using run_cursor::pos; + using run_cursor::buffer; + +#ifdef STXXL_SORT_SINGLE_PREFETCHER + static prefetcher_type* const prefetcher() // sorry, a hack + { + return reinterpret_cast(untyped_prefetcher); + } + static void set_prefetcher(prefetcher_type* pfptr) + { + untyped_prefetcher = pfptr; + } + run_cursor2() { } +#else + prefetcher_type* prefetcher_; + prefetcher_type* & prefetcher() // sorry, a hack + { + return prefetcher_; + } + + run_cursor2(prefetcher_type* p = NULL) : prefetcher_(p) { } +#endif + + inline bool empty() const + { + return (pos >= block_type::size); + } + inline void operator ++ () + { + assert(!empty()); + ++pos; + if (UNLIKELY(pos >= block_type::size)) + { + if (prefetcher()->block_consumed(buffer)) + pos = 0; + } + } + inline void make_inf() + { + pos = block_type::size; + } +}; + +#ifdef STXXL_SORT_SINGLE_PREFETCHER +template +void* have_prefetcher::untyped_prefetcher = NULL; +#endif + +#if 0 +template +struct run_cursor_cmp +{ + typedef run_cursor cursor_type; + + inline bool operator () (const cursor_type& a, const cursor_type& b) // greater or equal + { + return !((*a.buffer)[a.pos] < (*b.buffer)[b.pos]); + } +}; +#endif + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_RUN_CURSOR_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/scan.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/scan.h new file mode 100644 index 0000000000..d769860ecd --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/scan.h @@ -0,0 +1,331 @@ +/*************************************************************************** + * include/stxxl/bits/algo/scan.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * Copyright (C) 2008, 2009 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_ALGO_SCAN_HEADER +#define STXXL_ALGO_SCAN_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlalgo +//! \{ + +/*! + * External equivalent of std::for_each, see \ref design_algo_foreach. + * + * stxxl::for_each applies the function object \c functor to each element in + * the range [first, last); \c functor's return value, if any, is + * ignored. Applications are performed in forward order, i.e. from first to + * last. stxxl::for_each returns the function object after it has been applied + * to each element. To overlap I/O and computation \c nbuffers used (a value + * at least \a D is recommended). The size of the buffers is derived from the + * container that is pointed by the iterators. + * + * \remark The implementation exploits STXXL buffered streams (computation and I/O overlapped). + * + * \param begin object of model of \c ext_random_access_iterator concept + * \param end object of model of \c ext_random_access_iterator concept + * \param functor function object of model of \c std::UnaryFunction concept + * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D ) + * \return function object \c functor after it has been applied to the each element of the given range + * + * \warning nested stxxl::for_each are not supported + */ +template +UnaryFunction for_each(ExtIterator begin, ExtIterator end, + UnaryFunction functor, int_type nbuffers = 0) +{ + if (begin == end) + return functor; + + typedef typename ExtIterator::value_type value_type; + + typedef buf_istream< + typename ExtIterator::block_type, + typename ExtIterator::bids_container_iterator + > buf_istream_type; + + begin.flush(); // flush container + + if (nbuffers == 0) + nbuffers = 2 * config::get_instance()->disks_number(); + + // create prefetching stream, + buf_istream_type in(begin.bid(), end.bid() + ((end.block_offset()) ? 1 : 0), nbuffers); + + ExtIterator cur = begin - begin.block_offset(); + + // leave part of the block before begin untouched (e.g. copy) + for ( ; cur != begin; ++cur) + { + value_type tmp; + in >> tmp; + } + + // apply functor to the range [begin,end) + for ( ; cur != end; ++cur) + { + value_type tmp; + in >> tmp; + functor(tmp); + } + + // leave part of the block after end untouched + if (end.block_offset()) + { + ExtIterator last_block_end = end - end.block_offset() + ExtIterator::block_type::size; + for ( ; cur != last_block_end; ++cur) + { + value_type tmp; + in >> tmp; + } + } + + return functor; +} + +/*! + * External equivalent of std::for_each (mutating), see \ref design_algo_foreachm + * + * stxxl::for_each_m applies the function object \c functor to each element in + * the range [first, last); \c functor's return value, if any, is + * ignored. Applications are performed in forward order, i.e. from first to + * last. stxxl::for_each_m returns the function object after it has been + * applied to each element. To overlap I/O and computation \c nbuffers are used + * (a value at least \a 2D is recommended). The size of the buffers is derived + * from the container that is pointed by the iterators. + * + * \remark The implementation exploits STXXL buffered streams (computation and + * I/O overlapped) + * + * \param begin object of model of \c ext_random_access_iterator concept + * \param end object of model of \c ext_random_access_iterator concept + * \param functor object of model of \c std::UnaryFunction concept + * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D ) + * \return function object \c functor after it has been applied to the each element of the given range + * + * \warning nested stxxl::for_each_m are not supported + */ +template +UnaryFunction for_each_m(ExtIterator begin, ExtIterator end, + UnaryFunction functor, int_type nbuffers = 0) +{ + if (begin == end) + return functor; + + typedef typename ExtIterator::value_type value_type; + + typedef buf_istream< + typename ExtIterator::block_type, + typename ExtIterator::bids_container_iterator + > buf_istream_type; + + typedef buf_ostream< + typename ExtIterator::block_type, + typename ExtIterator::bids_container_iterator + > buf_ostream_type; + + begin.flush(); // flush container + + if (nbuffers == 0) + nbuffers = 2 * config::get_instance()->disks_number(); + + // create prefetching stream, + buf_istream_type in(begin.bid(), end.bid() + ((end.block_offset()) ? 1 : 0), nbuffers / 2); + // create buffered write stream for blocks + buf_ostream_type out(begin.bid(), nbuffers / 2); + // REMARK: these two streams do I/O while + // functor is being computed (overlapping for free) + + ExtIterator cur = begin - begin.block_offset(); + + // leave part of the block before begin untouched (e.g. copy) + for ( ; cur != begin; ++cur) + { + value_type tmp; + in >> tmp; + out << tmp; + } + + // apply functor to the range [begin,end) + for ( ; cur != end; ++cur) + { + value_type tmp; + in >> tmp; + functor(tmp); + out << tmp; + } + + // leave part of the block after end untouched + if (end.block_offset()) + { + ExtIterator _last_block_end = end - end.block_offset() + ExtIterator::block_type::size; + for ( ; cur != _last_block_end; ++cur) + { + value_type tmp; + in >> tmp; + out << tmp; + } + } + + return functor; +} + +/*! + * External equivalent of std::generate, see \ref design_algo_generate. + * + * Generate assigns the result of invoking \c generator, a function object that + * takes no arguments, to each element in the range [first, last). To overlap + * I/O and computation \c nbuffers are used (a value at least \a D is + * recommended). The size of the buffers is derived from the container that is + * pointed by the iterators. + * + * \remark The implementation exploits STXXL buffered streams (computation and + * I/O overlapped). + * + * \param begin object of model of \c ext_random_access_iterator concept + * \param end object of model of \c ext_random_access_iterator concept + * \param generator function object of model of \c std::generator concept + * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D, or zero for automaticl 2*D) + */ +template +void generate(ExtIterator begin, ExtIterator end, + Generator generator, int_type nbuffers = 0) +{ + typedef typename ExtIterator::block_type block_type; + + typedef buf_ostream< + block_type, typename ExtIterator::bids_container_iterator + > buf_ostream_type; + + while (begin.block_offset()) // go to the beginning of the block + // of the external vector + { + if (begin == end) + return; + + *begin = generator(); + ++begin; + } + + begin.flush(); // flush container + + if (nbuffers == 0) + nbuffers = 2 * config::get_instance()->disks_number(); + + // create buffered write stream for blocks + buf_ostream_type outstream(begin.bid(), nbuffers); + + assert(begin.block_offset() == 0); + + // delay calling block_externally_updated() until the block is + // completely filled (and written out) in outstream + typename ExtIterator::const_iterator prev_block = begin; + + while (end != begin) + { + if (begin.block_offset() == 0) { + if (prev_block != begin) { + prev_block.block_externally_updated(); + prev_block = begin; + } + } + + *outstream = generator(); + ++begin; + ++outstream; + } + + typename ExtIterator::const_iterator out = begin; + + while (out.block_offset()) // filling the rest of the block + { + *outstream = *out; + ++out; + ++outstream; + } + + if (prev_block != out) + prev_block.block_externally_updated(); + + begin.flush(); +} + +/*! + * External equivalent of std::find, see \ref design_algo_find. + * + * Returns the first iterator \a i in the range [first, last) such that *i + * == value. Returns last if no such iterator exists. To overlap I/O and + * computation \c nbuffers are used (a value at least \a D is recommended). The + * size of the buffers is derived from the container that is pointed by the + * iterators. + * + * \remark The implementation exploits STXXL buffered streams (computation and + * I/O overlapped). + * + * \param begin object of model of \c ext_random_access_iterator concept + * \param end object of model of \c ext_random_access_iterator concept + * \param value value that is equality comparable to the ExtIterator's value type + * \param nbuffers number of buffers (blocks) for internal use (should be at least 2*D) + * \return first iterator \c i in the range [begin,end) such that *( \c i ) == \c value, if no + * such exists then \c end + */ +template +ExtIterator find(ExtIterator begin, ExtIterator end, + const EqualityComparable& value, int_type nbuffers = 0) +{ + if (begin == end) + return end; + + typedef buf_istream< + typename ExtIterator::block_type, + typename ExtIterator::bids_container_iterator + > buf_istream_type; + + begin.flush(); // flush container + + if (nbuffers == 0) + nbuffers = 2 * config::get_instance()->disks_number(); + + // create prefetching stream, + buf_istream_type in(begin.bid(), end.bid() + ((end.block_offset()) ? 1 : 0), nbuffers); + + ExtIterator cur = begin - begin.block_offset(); + + // skip part of the block before begin untouched + for ( ; cur != begin; ++cur) + ++in; + + // search in the the range [begin,end) + for ( ; cur != end; ++cur) + { + typename ExtIterator::value_type tmp; + in >> tmp; + if (tmp == value) + return cur; + } + + return cur; +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_SCAN_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort.h new file mode 100644 index 0000000000..2a14fb4d07 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort.h @@ -0,0 +1,1000 @@ +/*************************************************************************** + * include/stxxl/bits/algo/sort.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2003 Roman Dementiev + * Copyright (C) 2006 Johannes Singler + * Copyright (C) 2008-2011 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_ALGO_SORT_HEADER +#define STXXL_ALGO_SORT_HEADER + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlalgo +//! \{ + +/*! \internal + */ +namespace sort_local { + +template +struct read_next_after_write_completed +{ + typedef BlockType block_type; + block_type* block; + BidType bid; + request_ptr* req; + void operator () (request* /*completed_req*/) + { + * req = block->read(bid); + } +}; + +template < + typename BlockType, + typename RunType, + typename InputBidIterator, + typename ValueCmp> +void +create_runs( + InputBidIterator it, + RunType** runs, + int_type nruns, + int_type _m, + ValueCmp cmp) +{ + typedef BlockType block_type; + typedef RunType run_type; + + typedef typename block_type::bid_type bid_type; + STXXL_VERBOSE1("stxxl::create_runs nruns=" << nruns << " m=" << _m); + + int_type m2 = _m / 2; + block_manager* bm = block_manager::get_instance(); + block_type* Blocks1 = new block_type[m2]; + block_type* Blocks2 = new block_type[m2]; + bid_type* bids1 = new bid_type[m2]; + bid_type* bids2 = new bid_type[m2]; + request_ptr* read_reqs1 = new request_ptr[m2]; + request_ptr* read_reqs2 = new request_ptr[m2]; + request_ptr* write_reqs = new request_ptr[m2]; + read_next_after_write_completed* next_run_reads = + new read_next_after_write_completed[m2]; + + disk_queues::get_instance()->set_priority_op(request_queue::WRITE); + + int_type i; + int_type run_size = 0; + + assert(nruns >= 2); + + run_size = runs[0]->size(); + assert(run_size == m2); + + for (i = 0; i < run_size; ++i) + { + STXXL_VERBOSE1("stxxl::create_runs posting read " << Blocks1[i].elem); + bids1[i] = *(it++); + read_reqs1[i] = Blocks1[i].read(bids1[i]); + } + + run_size = runs[1]->size(); + + for (i = 0; i < run_size; ++i) + { + STXXL_VERBOSE1("stxxl::create_runs posting read " << Blocks2[i].elem); + bids2[i] = *(it++); + read_reqs2[i] = Blocks2[i].read(bids2[i]); + } + + for (int_type k = 0; k < nruns - 1; ++k) + { + run_type* run = runs[k]; + run_size = run->size(); + assert(run_size == m2); + int_type next_run_size = runs[k + 1]->size(); + STXXL_ASSERT((next_run_size == m2) || (next_run_size <= m2 && k == nruns - 2)); + + STXXL_VERBOSE1("stxxl::create_runs start waiting read_reqs1"); + wait_all(read_reqs1, run_size); + STXXL_VERBOSE1("stxxl::create_runs finish waiting read_reqs1"); + for (i = 0; i < run_size; ++i) + bm->delete_block(bids1[i]); + + check_sort_settings(); + potentially_parallel:: + sort(make_element_iterator(Blocks1, 0), + make_element_iterator(Blocks1, run_size * block_type::size), + cmp); + + STXXL_VERBOSE1("stxxl::create_runs start waiting write_reqs"); + if (k > 0) + wait_all(write_reqs, m2); + STXXL_VERBOSE1("stxxl::create_runs finish waiting write_reqs"); + + int_type runplus2size = (k < nruns - 2) ? runs[k + 2]->size() : 0; + for (i = 0; i < m2; ++i) + { + STXXL_VERBOSE1("stxxl::create_runs posting write " << Blocks1[i].elem); + (*run)[i].value = Blocks1[i][0]; + if (i >= runplus2size) { + write_reqs[i] = Blocks1[i].write((*run)[i].bid); + } + else + { + next_run_reads[i].block = Blocks1 + i; + next_run_reads[i].req = read_reqs1 + i; + bids1[i] = next_run_reads[i].bid = *(it++); + write_reqs[i] = Blocks1[i].write((*run)[i].bid, next_run_reads[i]); + } + } + std::swap(Blocks1, Blocks2); + std::swap(bids1, bids2); + std::swap(read_reqs1, read_reqs2); + } + + run_type* run = runs[nruns - 1]; + run_size = run->size(); + STXXL_VERBOSE1("stxxl::create_runs start waiting read_reqs1"); + wait_all(read_reqs1, run_size); + STXXL_VERBOSE1("stxxl::create_runs finish waiting read_reqs1"); + for (i = 0; i < run_size; ++i) + bm->delete_block(bids1[i]); + + check_sort_settings(); + potentially_parallel:: + sort(make_element_iterator(Blocks1, 0), + make_element_iterator(Blocks1, run_size * block_type::size), + cmp); + + STXXL_VERBOSE1("stxxl::create_runs start waiting write_reqs"); + wait_all(write_reqs, m2); + STXXL_VERBOSE1("stxxl::create_runs finish waiting write_reqs"); + + for (i = 0; i < run_size; ++i) + { + STXXL_VERBOSE1("stxxl::create_runs posting write " << Blocks1[i].elem); + (*run)[i].value = Blocks1[i][0]; + write_reqs[i] = Blocks1[i].write((*run)[i].bid); + } + + STXXL_VERBOSE1("stxxl::create_runs start waiting write_reqs"); + wait_all(write_reqs, run_size); + STXXL_VERBOSE1("stxxl::create_runs finish waiting write_reqs"); + + delete[] Blocks1; + delete[] Blocks2; + delete[] bids1; + delete[] bids2; + delete[] read_reqs1; + delete[] read_reqs2; + delete[] write_reqs; + delete[] next_run_reads; +} + +template +bool check_sorted_runs(RunType** runs, + unsigned_type nruns, + unsigned_type m, + ValueCmp cmp) +{ + typedef BlockType block_type; + typedef typename block_type::value_type value_type; + + STXXL_MSG("check_sorted_runs Runs: " << nruns); + unsigned_type irun = 0; + for (irun = 0; irun < nruns; ++irun) + { + const unsigned_type nblocks_per_run = runs[irun]->size(); + unsigned_type blocks_left = nblocks_per_run; + block_type* blocks = new block_type[m]; + request_ptr* reqs = new request_ptr[m]; + value_type last = cmp.min_value(); + + for (unsigned_type off = 0; off < nblocks_per_run; off += m) + { + const unsigned_type nblocks = STXXL_MIN(blocks_left, m); + const unsigned_type nelements = nblocks * block_type::size; + blocks_left -= nblocks; + + for (unsigned_type j = 0; j < nblocks; ++j) + { + reqs[j] = blocks[j].read((*runs[irun])[j + off].bid); + } + wait_all(reqs, reqs + nblocks); + + if (off && cmp(blocks[0][0], last)) + { + STXXL_MSG("check_sorted_runs wrong first value in the run " << irun); + STXXL_MSG(" first value: " << blocks[0][0]); + STXXL_MSG(" last value: " << last); + for (unsigned_type k = 0; k < block_type::size; ++k) + STXXL_MSG("Element " << k << " in the block is :" << blocks[0][k]); + + delete[] reqs; + delete[] blocks; + return false; + } + + for (unsigned_type j = 0; j < nblocks; ++j) + { + if (!(blocks[j][0] == (*runs[irun])[j + off].value)) + { + STXXL_MSG("check_sorted_runs wrong trigger in the run " << irun << " block " << (j + off)); + STXXL_MSG(" trigger value: " << (*runs[irun])[j + off].value); + STXXL_MSG("Data in the block:"); + for (unsigned_type k = 0; k < block_type::size; ++k) + STXXL_MSG("Element " << k << " in the block is :" << blocks[j][k]); + + STXXL_MSG("BIDS:"); + for (unsigned_type k = 0; k < nblocks; ++k) + { + if (k == j) + STXXL_MSG("Bad one comes next."); + STXXL_MSG("BID " << (k + off) << " is: " << ((*runs[irun])[k + off].bid)); + } + + delete[] reqs; + delete[] blocks; + return false; + } + } + if (!stxxl::is_sorted(make_element_iterator(blocks, 0), + make_element_iterator(blocks, nelements), + cmp)) + { + STXXL_MSG("check_sorted_runs wrong order in the run " << irun); + STXXL_MSG("Data in blocks:"); + for (unsigned_type j = 0; j < nblocks; ++j) + { + for (unsigned_type k = 0; k < block_type::size; ++k) + STXXL_MSG(" Element " << k << " in block " << (j + off) << " is :" << blocks[j][k]); + } + STXXL_MSG("BIDS:"); + for (unsigned_type k = 0; k < nblocks; ++k) + { + STXXL_MSG("BID " << (k + off) << " is: " << ((*runs[irun])[k + off].bid)); + } + + delete[] reqs; + delete[] blocks; + return false; + } + + last = blocks[nblocks - 1][block_type::size - 1]; + } + + assert(blocks_left == 0); + delete[] reqs; + delete[] blocks; + } + + return true; +} + +template +void merge_runs(RunType** in_runs, int_type nruns, + RunType* out_run, unsigned_type _m, ValueCmp cmp) +{ + typedef BlockType block_type; + typedef RunType run_type; + typedef ValueCmp value_cmp; + typedef typename run_type::value_type trigger_entry_type; + typedef block_prefetcher prefetcher_type; + typedef run_cursor2 run_cursor_type; + typedef sort_helper::run_cursor2_cmp run_cursor2_cmp_type; + + run_type consume_seq(out_run->size()); + + int_type* prefetch_seq = new int_type[out_run->size()]; + + typename run_type::iterator copy_start = consume_seq.begin(); + for (int_type i = 0; i < nruns; i++) + { + // \todo: try to avoid copy + copy_start = std::copy( + in_runs[i]->begin(), + in_runs[i]->end(), + copy_start); + } + + std::stable_sort(consume_seq.begin(), consume_seq.end(), + sort_helper::trigger_entry_cmp(cmp) _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL); + + int_type disks_number = config::get_instance()->disks_number(); + +#ifdef PLAY_WITH_OPT_PREF + const int_type n_write_buffers = 4 * disks_number; +#else + const int_type n_prefetch_buffers = STXXL_MAX(2 * disks_number, (3 * (int_type(_m) - nruns) / 4)); + const int_type n_write_buffers = STXXL_MAX(2 * disks_number, int_type(_m) - nruns - n_prefetch_buffers); + #if STXXL_SORT_OPTIMAL_PREFETCHING + // heuristic + const int_type n_opt_prefetch_buffers = 2 * disks_number + (3 * (n_prefetch_buffers - 2 * disks_number)) / 10; + #endif +#endif + +#if STXXL_SORT_OPTIMAL_PREFETCHING + compute_prefetch_schedule( + consume_seq, + prefetch_seq, + n_opt_prefetch_buffers, + config::get_instance()->get_max_device_id()); +#else + for (unsigned_type i = 0; i < out_run->size(); i++) + prefetch_seq[i] = i; + +#endif + + prefetcher_type prefetcher(consume_seq.begin(), + consume_seq.end(), + prefetch_seq, + nruns + n_prefetch_buffers); + + buffered_writer writer(n_write_buffers, n_write_buffers / 2); + + int_type out_run_size = out_run->size(); + + block_type* out_buffer = writer.get_free_block(); + +//If parallelism is activated, one can still fall back to the +//native merge routine by setting stxxl::SETTINGS::native_merge= true, //otherwise, it is used anyway. + + if (do_parallel_merge()) + { +#if STXXL_PARALLEL_MULTIWAY_MERGE + +// begin of STL-style merging + + typedef stxxl::int64 diff_type; + typedef std::pair sequence; + std::vector seqs(nruns); + std::vector buffers(nruns); + + for (int_type i = 0; i < nruns; i++) // initialize sequences + { + buffers[i] = prefetcher.pull_block(); // get first block of each run + seqs[i] = std::make_pair(buffers[i]->begin(), buffers[i]->end()); + // this memory location stays the same, only the data is exchanged + } + + #if STXXL_CHECK_ORDER_IN_SORTS + value_type last_elem = cmp.min_value(); + #endif + diff_type num_currently_mergeable = 0; + + for (int_type j = 0; j < out_run_size; ++j) // for the whole output run, out_run_size is in blocks + { + diff_type rest = block_type::size; // elements still to merge for this output block + + STXXL_VERBOSE1("output block " << j); + do { + if (num_currently_mergeable < rest) + { + if (prefetcher.empty()) + { + // anything remaining is already in memory + num_currently_mergeable = (out_run_size - j) * block_type::size + - (block_type::size - rest); + } + else + { + num_currently_mergeable = sort_helper::count_elements_less_equal( + seqs, consume_seq[prefetcher.pos()].value, cmp); + } + } + + diff_type output_size = STXXL_MIN(num_currently_mergeable, rest); // at most rest elements + + STXXL_VERBOSE1("before merge " << output_size); + + parallel::multiway_merge( + seqs.begin(), seqs.end(), + out_buffer->end() - rest, output_size, cmp); + // sequence iterators are progressed appropriately + + rest -= output_size; + num_currently_mergeable -= output_size; + + STXXL_VERBOSE1("after merge"); + + sort_helper::refill_or_remove_empty_sequences(seqs, buffers, prefetcher); + } while (rest > 0 && seqs.size() > 0); + + #if STXXL_CHECK_ORDER_IN_SORTS + if (!stxxl::is_sorted(out_buffer->begin(), out_buffer->end(), cmp)) + { + for (value_type* i = out_buffer->begin() + 1; i != out_buffer->end(); i++) + if (cmp(*i, *(i - 1))) + { + STXXL_VERBOSE1("Error at position " << (i - out_buffer->begin())); + } + assert(false); + } + + if (j > 0) // do not check in first iteration + assert(cmp((*out_buffer)[0], last_elem) == false); + + last_elem = (*out_buffer)[block_type::size - 1]; + #endif + + (*out_run)[j].value = (*out_buffer)[0]; // save smallest value + + out_buffer = writer.write(out_buffer, (*out_run)[j].bid); + } + +// end of STL-style merging + +#else + STXXL_THROW_UNREACHABLE(); +#endif + } + else + { +// begin of native merging procedure + + loser_tree + losers(&prefetcher, nruns, run_cursor2_cmp_type(cmp)); + +#if STXXL_CHECK_ORDER_IN_SORTS + value_type last_elem = cmp.min_value(); +#endif + + for (int_type i = 0; i < out_run_size; ++i) + { + losers.multi_merge(out_buffer->elem, out_buffer->elem + block_type::size); + (*out_run)[i].value = *(out_buffer->elem); + +#if STXXL_CHECK_ORDER_IN_SORTS + assert(stxxl::is_sorted( + out_buffer->begin(), + out_buffer->end(), + cmp)); + + if (i) + assert(cmp(*(out_buffer->elem), last_elem) == false); + + last_elem = (*out_buffer).elem[block_type::size - 1]; +#endif + + out_buffer = writer.write(out_buffer, (*out_run)[i].bid); + } + +// end of native merging procedure + } + + delete[] prefetch_seq; + + block_manager* bm = block_manager::get_instance(); + for (int_type i = 0; i < nruns; ++i) + { + unsigned_type sz = in_runs[i]->size(); + for (unsigned_type j = 0; j < sz; ++j) + bm->delete_block((*in_runs[i])[j].bid); + + delete in_runs[i]; + } +} + +template +simple_vector >* +sort_blocks(InputBidIterator input_bids, + unsigned_type _n, + unsigned_type _m, + ValueCmp cmp) +{ + typedef BlockType block_type; + typedef AllocStrategy alloc_strategy; + typedef InputBidIterator input_bid_iterator; + typedef ValueCmp value_cmp; + typedef typename block_type::bid_type bid_type; + typedef sort_helper::trigger_entry trigger_entry_type; + typedef simple_vector run_type; + typedef typename interleaved_alloc_traits::strategy interleaved_alloc_strategy; + + unsigned_type m2 = _m / 2; + unsigned_type full_runs = _n / m2; + unsigned_type partial_runs = ((_n % m2) ? 1 : 0); + unsigned_type nruns = full_runs + partial_runs; + unsigned_type i; + + block_manager* mng = block_manager::get_instance(); + + //STXXL_VERBOSE ("n=" << _n << " nruns=" << nruns << "=" << full_runs << "+" << partial_runs); + + double begin = timestamp(), after_runs_creation, end; + + run_type** runs = new run_type*[nruns]; + + for (i = 0; i < full_runs; i++) + runs[i] = new run_type(m2); + + if (partial_runs) + runs[i] = new run_type(_n - full_runs * m2); + + for (i = 0; i < nruns; ++i) + mng->new_blocks(alloc_strategy(), make_bid_iterator(runs[i]->begin()), make_bid_iterator(runs[i]->end())); + + sort_local::create_runs(input_bids, runs, nruns, _m, cmp); + + after_runs_creation = timestamp(); + + double io_wait_after_rf = stats::get_instance()->get_io_wait_time(); + + disk_queues::get_instance()->set_priority_op(request_queue::WRITE); + + const int_type merge_factor = optimal_merge_factor(nruns, _m); + run_type** new_runs; + + while (nruns > 1) + { + int_type new_nruns = div_ceil(nruns, merge_factor); + STXXL_VERBOSE("Starting new merge phase: nruns: " << nruns << + " opt_merge_factor: " << merge_factor << " m:" << _m << " new_nruns: " << new_nruns); + + new_runs = new run_type*[new_nruns]; + + int_type runs_left = nruns; + int_type cur_out_run = 0; + int_type blocks_in_new_run = 0; + + while (runs_left > 0) + { + int_type runs2merge = STXXL_MIN(runs_left, merge_factor); + blocks_in_new_run = 0; + for (unsigned_type i = nruns - runs_left; i < (nruns - runs_left + runs2merge); i++) + blocks_in_new_run += runs[i]->size(); + + // allocate run + new_runs[cur_out_run++] = new run_type(blocks_in_new_run); + runs_left -= runs2merge; + } + // allocate blocks for the new runs + if (cur_out_run == 1 && blocks_in_new_run == int_type(_n) && !input_bids->is_managed()) + { + // if we sort a file we can reuse the input bids for the output + input_bid_iterator cur = input_bids; + for (int_type i = 0; cur != (input_bids + _n); ++cur) + { + (*new_runs[0])[i++].bid = *cur; + } + + bid_type& firstBID = (*new_runs[0])[0].bid; + if (firstBID.is_managed()) + { + // the first block does not belong to the file + // need to reallocate it + mng->new_block(FR(), firstBID); + } + bid_type& lastBID = (*new_runs[0])[_n - 1].bid; + if (lastBID.is_managed()) + { + // the first block does not belong to the file + // need to reallocate it + mng->new_block(FR(), lastBID); + } + } + else + { + mng->new_blocks(interleaved_alloc_strategy(new_nruns, alloc_strategy()), + runs2bid_array_adaptor2(new_runs, 0, new_nruns, blocks_in_new_run), + runs2bid_array_adaptor2(new_runs, _n, new_nruns, blocks_in_new_run)); + } + // merge all + runs_left = nruns; + cur_out_run = 0; + while (runs_left > 0) + { + int_type runs2merge = STXXL_MIN(runs_left, merge_factor); +#if STXXL_CHECK_ORDER_IN_SORTS + assert((check_sorted_runs(runs + nruns - runs_left, runs2merge, m2, cmp))); +#endif + STXXL_VERBOSE("Merging " << runs2merge << " runs"); + merge_runs(runs + nruns - runs_left, + runs2merge, *(new_runs + (cur_out_run++)), _m, cmp + ); + runs_left -= runs2merge; + } + + nruns = new_nruns; + delete[] runs; + runs = new_runs; + } + + run_type* result = *runs; + delete[] runs; + + end = timestamp(); + + STXXL_VERBOSE("Elapsed time : " << end - begin << " s. Run creation time: " << + after_runs_creation - begin << " s"); + STXXL_VERBOSE("Time in I/O wait(rf): " << io_wait_after_rf << " s"); + STXXL_VERBOSE(*stats::get_instance()); + + return result; +} + +} // namespace sort_local + +/*! + * Sort records comparison-based, see \ref design_algo_sort. + * + * stxxl::sort sorts the elements in [first, last) into ascending order, + * meaning that if \c i and \c j are any two valid iterators in [first, last) + * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as + * std::sort, stxxl::sort is not guaranteed to be stable. That is, suppose that + * \c *i and \c *j are equivalent: neither one is less than the other. It is + * not guaranteed that the relative order of these two elements will be + * preserved by stxxl::sort. + * + * The order is defined by the \c cmp parameter. The sorter's internal memory + * consumption is bounded by \a M bytes. + * + * \param first object of model of \c ext_random_access_iterator concept + * \param last object of model of \c ext_random_access_iterator concept + * \param cmp comparison object of \ref StrictWeakOrdering + * \param M amount of memory for internal use (in bytes) + */ +template +void sort(ExtIterator first, ExtIterator last, StrictWeakOrdering cmp, unsigned_type M) +{ + sort_helper::verify_sentinel_strict_weak_ordering(cmp); + + typedef typename ExtIterator::vector_type::value_type value_type; + typedef typename ExtIterator::block_type block_type; + typedef typename ExtIterator::bid_type bid_type; + typedef typename ExtIterator::vector_type::alloc_strategy_type alloc_strategy_type; + typedef typename ExtIterator::bids_container_iterator bids_container_iterator; + + typedef simple_vector > run_type; + + unsigned_type n = 0; + + block_manager* mng = block_manager::get_instance(); + + first.flush(); + + if ((last - first) * sizeof(value_type) * sort_memory_usage_factor() < M) + { + stl_in_memory_sort(first, last, cmp); + } + else + { + if (!(2 * block_type::raw_size * (unsigned_type)sort_memory_usage_factor() <= M)) { + throw bad_parameter("stxxl::sort(): INSUFFICIENT MEMORY provided, please increase parameter 'M'"); + } + + if (first.block_offset()) + { + if (last.block_offset()) // first and last element are + // not the first elements of their block + { + block_type* first_block = new block_type; + block_type* last_block = new block_type; + typename ExtIterator::bid_type first_bid, last_bid; + request_ptr req; + + req = first_block->read(*first.bid()); + mng->new_block(FR(), first_bid); // try to overlap + mng->new_block(FR(), last_bid); + req->wait(); + + req = last_block->read(*last.bid()); + + unsigned_type i = 0; + for ( ; i < first.block_offset(); ++i) + { + first_block->elem[i] = cmp.min_value(); + } + + req->wait(); + + req = first_block->write(first_bid); + for (i = last.block_offset(); i < block_type::size; ++i) + { + last_block->elem[i] = cmp.max_value(); + } + + req->wait(); + + req = last_block->write(last_bid); + + n = last.bid() - first.bid() + 1; + + std::swap(first_bid, *first.bid()); + std::swap(last_bid, *last.bid()); + + req->wait(); + + delete first_block; + delete last_block; + + run_type* out = + sort_local::sort_blocks< + block_type, alloc_strategy_type, bids_container_iterator + >(first.bid(), n, + M / sort_memory_usage_factor() / block_type::raw_size, cmp); + + first_block = new block_type; + last_block = new block_type; + block_type* sorted_first_block = new block_type; + block_type* sorted_last_block = new block_type; + request_ptr* reqs = new request_ptr[2]; + + reqs[0] = first_block->read(first_bid); + reqs[1] = sorted_first_block->read((*(out->begin())).bid); + + reqs[0]->wait(); + reqs[1]->wait(); + + reqs[0] = last_block->read(last_bid); + reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); + + for (i = first.block_offset(); i < block_type::size; i++) + { + first_block->elem[i] = sorted_first_block->elem[i]; + } + + reqs[0]->wait(); + reqs[1]->wait(); + + req = first_block->write(first_bid); + + for (i = 0; i < last.block_offset(); ++i) + { + last_block->elem[i] = sorted_last_block->elem[i]; + } + + req->wait(); + + req = last_block->write(last_bid); + + mng->delete_block(out->begin()->bid); + mng->delete_block((*out)[out->size() - 1].bid); + + *first.bid() = first_bid; + *last.bid() = last_bid; + + typename run_type::iterator it = out->begin(); + ++it; + bids_container_iterator cur_bid = first.bid(); + ++cur_bid; + + for ( ; cur_bid != last.bid(); ++cur_bid, ++it) + { + *cur_bid = (*it).bid; + } + + delete first_block; + delete sorted_first_block; + delete sorted_last_block; + delete[] reqs; + delete out; + + req->wait(); + + delete last_block; + } + else + { + // first element is + // not the first element of its block + block_type* first_block = new block_type; + bid_type first_bid; + request_ptr req; + + req = first_block->read(*first.bid()); + mng->new_block(FR(), first_bid); // try to overlap + req->wait(); + + unsigned_type i = 0; + for ( ; i < first.block_offset(); ++i) + { + first_block->elem[i] = cmp.min_value(); + } + + req = first_block->write(first_bid); + + n = last.bid() - first.bid(); + + std::swap(first_bid, *first.bid()); + + req->wait(); + + delete first_block; + + run_type* out = + sort_local::sort_blocks< + block_type, alloc_strategy_type, bids_container_iterator + >(first.bid(), n, + M / sort_memory_usage_factor() / block_type::raw_size, cmp); + + first_block = new block_type; + + block_type* sorted_first_block = new block_type; + + request_ptr* reqs = new request_ptr[2]; + + reqs[0] = first_block->read(first_bid); + reqs[1] = sorted_first_block->read((*(out->begin())).bid); + + reqs[0]->wait(); + reqs[1]->wait(); + + for (i = first.block_offset(); i < block_type::size; ++i) + { + first_block->elem[i] = sorted_first_block->elem[i]; + } + + req = first_block->write(first_bid); + + mng->delete_block(out->begin()->bid); + + *first.bid() = first_bid; + + typename run_type::iterator it = out->begin(); + ++it; + bids_container_iterator cur_bid = first.bid(); + ++cur_bid; + + for ( ; cur_bid != last.bid(); ++cur_bid, ++it) + { + *cur_bid = (*it).bid; + } + + *cur_bid = (*it).bid; + + delete sorted_first_block; + delete[] reqs; + delete out; + + req->wait(); + + delete first_block; + } + } + else + { + if (last.block_offset()) // last is + // not the first element of its block + { + block_type* last_block = new block_type; + bid_type last_bid; + request_ptr req; + unsigned_type i; + + req = last_block->read(*last.bid()); + mng->new_block(FR(), last_bid); + req->wait(); + + for (i = last.block_offset(); i < block_type::size; ++i) + { + last_block->elem[i] = cmp.max_value(); + } + + req = last_block->write(last_bid); + + n = last.bid() - first.bid() + 1; + + std::swap(last_bid, *last.bid()); + + req->wait(); + + delete last_block; + + run_type* out = + sort_local::sort_blocks< + block_type, alloc_strategy_type, bids_container_iterator + >(first.bid(), n, + M / sort_memory_usage_factor() / block_type::raw_size, cmp); + + last_block = new block_type; + block_type* sorted_last_block = new block_type; + request_ptr* reqs = new request_ptr[2]; + + reqs[0] = last_block->read(last_bid); + reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid); + + reqs[0]->wait(); + reqs[1]->wait(); + + for (i = 0; i < last.block_offset(); ++i) + { + last_block->elem[i] = sorted_last_block->elem[i]; + } + + req = last_block->write(last_bid); + + mng->delete_block((*out)[out->size() - 1].bid); + + *last.bid() = last_bid; + + typename run_type::iterator it = out->begin(); + bids_container_iterator cur_bid = first.bid(); + + for ( ; cur_bid != last.bid(); ++cur_bid, ++it) + { + *cur_bid = (*it).bid; + } + + delete sorted_last_block; + delete[] reqs; + delete out; + + req->wait(); + + delete last_block; + } + else + { + // first and last element are first elements of their of blocks + n = last.bid() - first.bid(); + + run_type* out = + sort_local::sort_blocks< + block_type, alloc_strategy_type, bids_container_iterator + >(first.bid(), n, + M / sort_memory_usage_factor() / block_type::raw_size, cmp); + + typename run_type::iterator it = out->begin(); + bids_container_iterator cur_bid = first.bid(); + + for ( ; cur_bid != last.bid(); ++cur_bid, ++it) + { + *cur_bid = (*it).bid; + } + + delete out; + } + } + } + +#if STXXL_CHECK_ORDER_IN_SORTS + typedef typename ExtIterator::const_iterator const_iterator; + assert(stxxl::is_sorted(const_iterator(first), const_iterator(last), cmp)); +#endif +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_SORT_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_base.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_base.h new file mode 100644 index 0000000000..c05920746f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_base.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * include/stxxl/bits/algo/sort_base.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_ALGO_SORT_BASE_HEADER +#define STXXL_ALGO_SORT_BASE_HEADER + +#include +#include + +#ifndef STXXL_NO_WARN_RECURSIVE_SORT +#define STXXL_WARNMSG_RECURSIVE_SORT STXXL_ERRMSG +#else +#define STXXL_WARNMSG_RECURSIVE_SORT STXXL_VERBOSE +#endif + +#ifndef STXXL_SORT_OPTIMAL_PREFETCHING +#define STXXL_SORT_OPTIMAL_PREFETCHING 1 +#endif + +#ifndef STXXL_CHECK_ORDER_IN_SORTS +#define STXXL_CHECK_ORDER_IN_SORTS 0 +#endif + +#ifndef STXXL_L2_SIZE +#define STXXL_L2_SIZE (512 * 1024) +#endif + +STXXL_BEGIN_NAMESPACE + +// Optimal merging: merge r = pow(nruns,1/ceil(log(nruns)/log(m))) runs at once +inline unsigned_type optimal_merge_factor(unsigned_type num_runs, unsigned_type max_concurrent_runs) +{ + return unsigned_type(ceil(pow(double(num_runs), 1. / ceil(log(double(num_runs)) / log(double(max_concurrent_runs)))))); +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_SORT_BASE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_helper.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_helper.h new file mode 100644 index 0000000000..524e40dc12 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/sort_helper.h @@ -0,0 +1,153 @@ +/*************************************************************************** + * include/stxxl/bits/algo/sort_helper.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2003 Roman Dementiev + * Copyright (C) 2009, 2010 Andreas Beckmann + * + * 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 STXXL_ALGO_SORT_HELPER_HEADER +#define STXXL_ALGO_SORT_HELPER_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \internal +namespace sort_helper { + +template +inline void verify_sentinel_strict_weak_ordering(StrictWeakOrdering cmp) +{ + STXXL_ASSERT(!cmp(cmp.min_value(), cmp.min_value())); + STXXL_ASSERT(cmp(cmp.min_value(), cmp.max_value())); + STXXL_ASSERT(!cmp(cmp.max_value(), cmp.min_value())); + STXXL_ASSERT(!cmp(cmp.max_value(), cmp.max_value())); +} + +template +struct trigger_entry +{ + typedef BlockType block_type; + typedef typename block_type::bid_type bid_type; + typedef ValueType value_type; + + bid_type bid; + value_type value; + + operator bid_type () + { + return bid; + } +}; + +template +struct trigger_entry_cmp + : public std::binary_function +{ + typedef TriggerEntryType trigger_entry_type; + ValueCmp cmp; + trigger_entry_cmp(ValueCmp c) : cmp(c) { } + trigger_entry_cmp(const trigger_entry_cmp& a) : cmp(a.cmp) { } + bool operator () (const trigger_entry_type& a, const trigger_entry_type& b) const + { + return cmp(a.value, b.value); + } +}; + +template +struct run_cursor2_cmp + : public std::binary_function< + run_cursor2, + run_cursor2, + bool + > +{ + typedef BlockType block_type; + typedef PrefetcherType prefetcher_type; + typedef ValueCmp value_cmp; + + typedef run_cursor2 cursor_type; + value_cmp cmp; + + run_cursor2_cmp(value_cmp c) : cmp(c) { } + run_cursor2_cmp(const run_cursor2_cmp& a) : cmp(a.cmp) { } + inline bool operator () (const cursor_type& a, const cursor_type& b) const + { + if (UNLIKELY(b.empty())) + return true; + // sentinel emulation + if (UNLIKELY(a.empty())) + return false; + // sentinel emulation + + return (cmp(a.current(), b.current())); + } +}; + +// this function is used by parallel mergers +template +inline unsigned_type +count_elements_less_equal(const SequenceVector& seqs, + const ValueType& bound, Comparator cmp) +{ + typedef typename SequenceVector::size_type seqs_size_type; + typedef typename SequenceVector::value_type::first_type iterator; + unsigned_type count = 0; + + for (seqs_size_type i = 0; i < seqs.size(); ++i) + { + iterator position = std::upper_bound(seqs[i].first, seqs[i].second, bound, cmp); + STXXL_VERBOSE1("less equal than " << position - seqs[i].first); + count += position - seqs[i].first; + } + STXXL_VERBOSE1("finished loop"); + return count; +} + +// this function is used by parallel mergers +template +inline void +refill_or_remove_empty_sequences(SequenceVector& seqs, + BufferPtrVector& buffers, + Prefetcher& prefetcher) +{ + typedef typename SequenceVector::size_type seqs_size_type; + + for (seqs_size_type i = 0; i < seqs.size(); ++i) + { + if (seqs[i].first == seqs[i].second) // run empty + { + if (prefetcher.block_consumed(buffers[i])) + { + seqs[i].first = buffers[i]->begin(); // reset iterator + seqs[i].second = buffers[i]->end(); + STXXL_VERBOSE1("block ran empty " << i); + } + else + { + seqs.erase(seqs.begin() + i); // remove this sequence + buffers.erase(buffers.begin() + i); + STXXL_VERBOSE1("seq removed " << i); + --i; // don't skip the next sequence + } + } + } +} + +} // namespace sort_helper + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_SORT_HELPER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/stable_ksort.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/stable_ksort.h new file mode 100644 index 0000000000..bd05e8a05f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/algo/stable_ksort.h @@ -0,0 +1,467 @@ +/*************************************************************************** + * include/stxxl/bits/algo/stable_ksort.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_ALGO_STABLE_KSORT_HEADER +#define STXXL_ALGO_STABLE_KSORT_HEADER + +// it is a first try: distribution sort without sampling +// I rework the stable_ksort when I would have a time + +#include +#include +#include +#include +#include +#include +#include + +#ifndef STXXL_VERBOSE_STABLE_KSORT +#define STXXL_VERBOSE_STABLE_KSORT STXXL_VERBOSE1 +#endif + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlalgo +//! \{ + +/*! \internal + */ +namespace stable_ksort_local { + +template +void classify_block(Type* begin, Type* end, TypeKey*& out, + int_type* bucket, typename Type::key_type offset, + unsigned shift) +{ + for (Type* p = begin; p < end; p++, out++) // count & create references + { + out->ptr = p; + typename Type::key_type key = p->key(); + int_type ibucket = (int_type)((key - offset) >> shift); + out->key = key; + bucket[ibucket]++; + } +} + +template +struct type_key +{ + typedef typename Type::key_type key_type; + key_type key; + Type* ptr; + + type_key() { } + type_key(key_type k, Type* p) : key(k), ptr(p) + { } +}; + +template +bool operator < (const type_key& a, const type_key& b) +{ + return a.key < b.key; +} + +template +bool operator > (const type_key& a, const type_key& b) +{ + return a.key > b.key; +} + +template +class bid_sequence +{ +public: + typedef BIDType bid_type; + typedef bid_type& reference; + typedef AllocStrategy alloc_strategy; + typedef typename simple_vector::size_type size_type; + typedef typename simple_vector::iterator iterator; + +protected: + simple_vector* bids; + alloc_strategy alloc_strategy_; + +public: + bid_sequence() : bids(NULL) { } + bid_sequence(size_type size_) + { + bids = new simple_vector(size_); + block_manager* mng = block_manager::get_instance(); + mng->new_blocks(alloc_strategy_, bids->begin(), bids->end()); + } + void init(size_type size_) + { + bids = new simple_vector(size_); + block_manager* mng = block_manager::get_instance(); + mng->new_blocks(alloc_strategy_, bids->begin(), bids->end()); + } + reference operator [] (size_type i) + { + size_type size_ = size(); // cache size in a register + if (i < size_) + return *(bids->begin() + i); + + block_manager* mng = block_manager::get_instance(); + simple_vector* larger_bids = new simple_vector((i + 1) * 2); + std::copy(bids->begin(), bids->end(), larger_bids->begin()); + mng->new_blocks(alloc_strategy_, larger_bids->begin() + size_, larger_bids->end()); + delete bids; + bids = larger_bids; + return *(larger_bids->begin() + i); + } + size_type size() { return bids->size(); } + iterator begin() { return bids->begin(); } + ~bid_sequence() + { + block_manager::get_instance()->delete_blocks(bids->begin(), bids->end()); + delete bids; + } +}; + +template +void distribute( + bid_sequence* bucket_bids, + int64* bucket_sizes, + const int_type nbuckets, + const int_type lognbuckets, + ExtIterator first, + ExtIterator last, + const int_type nread_buffers, + const int_type nwrite_buffers) +{ + typedef typename ExtIterator::vector_type::value_type value_type; + typedef typename value_type::key_type key_type; + typedef typename ExtIterator::block_type block_type; + typedef typename ExtIterator::bids_container_iterator bids_container_iterator; + + typedef buf_istream buf_istream_type; + + int_type i = 0; + + buf_istream_type in(first.bid(), last.bid() + ((first.block_offset()) ? 1 : 0), + nread_buffers); + + buffered_writer out( + nbuckets + nwrite_buffers, + nwrite_buffers); + + unsigned_type* bucket_block_offsets = new unsigned_type[nbuckets]; + unsigned_type* bucket_iblock = new unsigned_type[nbuckets]; + block_type** bucket_blocks = new block_type*[nbuckets]; + + std::fill(bucket_sizes, bucket_sizes + nbuckets, 0); + std::fill(bucket_iblock, bucket_iblock + nbuckets, 0); + std::fill(bucket_block_offsets, bucket_block_offsets + nbuckets, 0); + + for (i = 0; i < nbuckets; i++) + bucket_blocks[i] = out.get_free_block(); + + ExtIterator cur = first - first.block_offset(); + + // skip part of the block before first untouched + for ( ; cur != first; cur++) + ++in; + + const int_type shift = sizeof(key_type) * 8 - lognbuckets; + // search in the the range [_begin,_end) + STXXL_VERBOSE_STABLE_KSORT("Shift by: " << shift << " bits, lognbuckets: " << lognbuckets); + for ( ; cur != last; cur++) + { + key_type cur_key = in.current().key(); + int_type ibucket = (int_type)(cur_key >> shift); + + int_type block_offset = bucket_block_offsets[ibucket]; + in >> (bucket_blocks[ibucket]->elem[block_offset++]); + if (block_offset == block_type::size) + { + block_offset = 0; + int_type iblock = bucket_iblock[ibucket]++; + bucket_blocks[ibucket] = out.write(bucket_blocks[ibucket], bucket_bids[ibucket][iblock]); + } + bucket_block_offsets[ibucket] = block_offset; + } + for (i = 0; i < nbuckets; i++) + { + if (bucket_block_offsets[i]) + { + out.write(bucket_blocks[i], bucket_bids[i][bucket_iblock[i]]); + } + bucket_sizes[i] = int64(block_type::size) * bucket_iblock[i] + + bucket_block_offsets[i]; + STXXL_VERBOSE_STABLE_KSORT("Bucket " << i << " has size " << bucket_sizes[i] << + ", estimated size: " << ((last - first) / int64(nbuckets))); + } + + delete[] bucket_blocks; + delete[] bucket_block_offsets; + delete[] bucket_iblock; +} + +} // namespace stable_ksort_local + +//! Sort records with integer keys +//! \param first object of model of \c ext_random_access_iterator concept +//! \param last object of model of \c ext_random_access_iterator concept +//! \param M amount of memory for internal use (in bytes) +//! \remark Elements must provide a method key() which returns the integer key. +//! \remark Not yet fully implemented, it assumes that the keys are uniformly +//! distributed between [0,std::numeric_limits::max(). +template +void stable_ksort(ExtIterator first, ExtIterator last, unsigned_type M) +{ + STXXL_MSG("Warning: stable_ksort is not yet fully implemented, it assumes that the keys are uniformly distributed between [0,std::numeric_limits::max()]"); + typedef typename ExtIterator::vector_type::value_type value_type; + typedef typename value_type::key_type key_type; + typedef typename ExtIterator::block_type block_type; + typedef typename ExtIterator::bids_container_iterator bids_container_iterator; + typedef typename block_type::bid_type bid_type; + typedef typename ExtIterator::vector_type::alloc_strategy_type alloc_strategy; + typedef stable_ksort_local::bid_sequence bucket_bids_type; + typedef stable_ksort_local::type_key type_key_; + + first.flush(); // flush container + + double begin = timestamp(); + + unsigned_type i = 0; + config* cfg = config::get_instance(); + const unsigned_type m = M / block_type::raw_size; + assert(2 * block_type::raw_size <= M); + const unsigned_type write_buffers_multiple = 2; + const unsigned_type read_buffers_multiple = 2; + const unsigned_type ndisks = cfg->disks_number(); + const unsigned_type min_num_read_write_buffers = (write_buffers_multiple + read_buffers_multiple) * ndisks; + const unsigned_type nmaxbuckets = m - min_num_read_write_buffers; + const unsigned int lognbuckets = ilog2_floor(nmaxbuckets); + const unsigned_type nbuckets = unsigned_type(1) << lognbuckets; + const unsigned_type est_bucket_size = (unsigned_type)div_ceil((last - first) / nbuckets, block_type::size); //in blocks + + if (m < min_num_read_write_buffers + 2 || nbuckets < 2) { + STXXL_ERRMSG("stxxl::stable_ksort: Not enough memory. Blocks available: " << m << + ", required for r/w buffers: " << min_num_read_write_buffers << + ", required for buckets: 2, nbuckets: " << nbuckets); + throw bad_parameter("stxxl::stable_ksort(): INSUFFICIENT MEMORY provided, please increase parameter 'M'"); + } + STXXL_VERBOSE_STABLE_KSORT("Elements to sort: " << (last - first)); + STXXL_VERBOSE_STABLE_KSORT("Number of buckets has to be reduced from " << nmaxbuckets << " to " << nbuckets); + const unsigned_type nread_buffers = (m - nbuckets) * read_buffers_multiple / (read_buffers_multiple + write_buffers_multiple); + const unsigned_type nwrite_buffers = (m - nbuckets) * write_buffers_multiple / (read_buffers_multiple + write_buffers_multiple); + + STXXL_VERBOSE_STABLE_KSORT("Read buffers in distribution phase: " << nread_buffers); + STXXL_VERBOSE_STABLE_KSORT("Write buffers in distribution phase: " << nwrite_buffers); + + bucket_bids_type* bucket_bids = new bucket_bids_type[nbuckets]; + for (i = 0; i < nbuckets; ++i) + bucket_bids[i].init(est_bucket_size); + + int64* bucket_sizes = new int64[nbuckets]; + + disk_queues::get_instance()->set_priority_op(request_queue::WRITE); + + stable_ksort_local::distribute( + bucket_bids, + bucket_sizes, + nbuckets, + lognbuckets, + first, + last, + nread_buffers, + nwrite_buffers); + + double dist_end = timestamp(), end; + double io_wait_after_d = stats::get_instance()->get_io_wait_time(); + + { + // sort buckets + unsigned_type write_buffers_multiple_bs = 2; + unsigned_type max_bucket_size_bl = (m - write_buffers_multiple_bs * ndisks) / 2; // in number of blocks + int64 max_bucket_size_rec = int64(max_bucket_size_bl) * block_type::size; // in number of records + int64 max_bucket_size_act = 0; // actual max bucket size + // establish output stream + + for (i = 0; i < nbuckets; i++) + { + max_bucket_size_act = STXXL_MAX(bucket_sizes[i], max_bucket_size_act); + if (bucket_sizes[i] > max_bucket_size_rec) + { + STXXL_ERRMSG("Bucket " << i << " is too large: " << bucket_sizes[i] << + " records, maximum: " << max_bucket_size_rec); + STXXL_ERRMSG("Recursion on buckets is not yet implemented, aborting."); + abort(); + } + } + // here we can increase write_buffers_multiple_b knowing max(bucket_sizes[i]) + // ... and decrease max_bucket_size_bl + const int_type max_bucket_size_act_bl = (int_type)div_ceil(max_bucket_size_act, block_type::size); + STXXL_VERBOSE_STABLE_KSORT("Reducing required number of required blocks per bucket from " << + max_bucket_size_bl << " to " << max_bucket_size_act_bl); + max_bucket_size_rec = max_bucket_size_act; + max_bucket_size_bl = max_bucket_size_act_bl; + const unsigned_type nwrite_buffers_bs = m - 2 * max_bucket_size_bl; + STXXL_VERBOSE_STABLE_KSORT("Write buffers in bucket sorting phase: " << nwrite_buffers_bs); + + typedef buf_ostream buf_ostream_type; + buf_ostream_type out(first.bid(), nwrite_buffers_bs); + + disk_queues::get_instance()->set_priority_op(request_queue::READ); + + if (first.block_offset()) + { + // has to skip part of the first block + block_type* block = new block_type; + request_ptr req; + req = block->read(*first.bid()); + req->wait(); + + for (i = 0; i < first.block_offset(); i++) + { + out << block->elem[i]; + } + delete block; + } + block_type* blocks1 = new block_type[max_bucket_size_bl]; + block_type* blocks2 = new block_type[max_bucket_size_bl]; + request_ptr* reqs1 = new request_ptr[max_bucket_size_bl]; + request_ptr* reqs2 = new request_ptr[max_bucket_size_bl]; + type_key_* refs1 = new type_key_[(size_t)max_bucket_size_rec]; + type_key_* refs2 = new type_key_[(size_t)max_bucket_size_rec]; + + // submit reading first 2 buckets (Peter's scheme) + unsigned_type nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[0], block_type::size); + for (i = 0; i < nbucket_blocks; i++) + reqs1[i] = blocks1[i].read(bucket_bids[0][i]); + + nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[1], block_type::size); + for (i = 0; i < nbucket_blocks; i++) + reqs2[i] = blocks2[i].read(bucket_bids[1][i]); + + key_type offset = 0; + const unsigned log_k1 = STXXL_MAX(ilog2_ceil(max_bucket_size_rec * sizeof(type_key_) / STXXL_L2_SIZE), 1); + unsigned_type k1 = unsigned_type(1) << log_k1; + int_type* bucket1 = new int_type[k1]; + + const unsigned int shift = (unsigned int)(sizeof(key_type) * 8 - lognbuckets); + const unsigned int shift1 = shift - log_k1; + + STXXL_VERBOSE_STABLE_KSORT("Classifying " << nbuckets << " buckets, max size:" << max_bucket_size_rec << + " block size:" << block_type::size << " log_k1:" << log_k1); + + for (unsigned_type k = 0; k < nbuckets; k++) + { + nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[k], block_type::size); + const unsigned log_k1_k = STXXL_MAX(ilog2_ceil(bucket_sizes[k] * sizeof(type_key_) / STXXL_L2_SIZE), 1); + assert(log_k1_k <= log_k1); + k1 = (unsigned_type)(1) << log_k1_k; + std::fill(bucket1, bucket1 + k1, 0); + + STXXL_VERBOSE_STABLE_KSORT("Classifying bucket " << k << " size:" << bucket_sizes[k] << + " blocks:" << nbucket_blocks << " log_k1:" << log_k1_k); + // classify first nbucket_blocks-1 blocks, they are full + type_key_* ref_ptr = refs1; + key_type offset1 = offset + (key_type(1) << key_type(shift)) * key_type(k); + for (i = 0; i < nbucket_blocks - 1; i++) + { + reqs1[i]->wait(); + stable_ksort_local::classify_block(blocks1[i].begin(), blocks1[i].end(), + ref_ptr, bucket1, offset1, shift1 /*,k1*/); + } + // last block might be non-full + const unsigned_type last_block_size = + (unsigned_type)(bucket_sizes[k] - (nbucket_blocks - 1) * block_type::size); + reqs1[i]->wait(); + + //STXXL_MSG("block_type::size: "<ptr)); + + delete[] bucket2; + c = cEnd; + d = dEnd; + } + // submit next read + const unsigned_type bucket2submit = k + 2; + if (bucket2submit < nbuckets) + { + nbucket_blocks = (unsigned_type)div_ceil(bucket_sizes[bucket2submit], block_type::size); + for (i = 0; i < nbucket_blocks; i++) + reqs1[i] = blocks1[i].read(bucket_bids[bucket2submit][i]); + } + + std::swap(blocks1, blocks2); + std::swap(reqs1, reqs2); + } + + delete[] bucket1; + delete[] refs1; + delete[] refs2; + delete[] blocks1; + delete[] blocks2; + delete[] reqs1; + delete[] reqs2; + delete[] bucket_bids; + delete[] bucket_sizes; + + if (last.block_offset()) + { + // has to skip part of the first block + block_type* block = new block_type; + request_ptr req = block->read(*last.bid()); + req->wait(); + + for (i = last.block_offset(); i < block_type::size; i++) + { + out << block->elem[i]; + } + delete block; + } + + end = timestamp(); + } + + STXXL_VERBOSE("Elapsed time : " << end - begin << " s. Distribution time: " << + dist_end - begin << " s"); + STXXL_VERBOSE("Time in I/O wait(ds): " << io_wait_after_d << " s"); + STXXL_VERBOSE(*stats::get_instance()); +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_ALGO_STABLE_KSORT_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/addressable_queues.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/addressable_queues.h new file mode 100644 index 0000000000..733f10d328 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/addressable_queues.h @@ -0,0 +1,203 @@ +/*************************************************************************** + * include/stxxl/bits/common/addressable_queues.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010-2011 Raoul Steffen + * + * 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 STXXL_COMMON_ADDRESSABLE_QUEUES_HEADER +#define STXXL_COMMON_ADDRESSABLE_QUEUES_HEADER + +#include +#include +#include + +#include + +STXXL_BEGIN_NAMESPACE + +//! An internal fifo queue that allows removing elements addressed with (a copy +//! of) themselves. +//! \tparam KeyType Type of contained elements. +template +class addressable_fifo_queue +{ + typedef std::list container_type; + typedef typename container_type::iterator container_iterator; + typedef std::map meta_type; + typedef typename meta_type::iterator meta_iterator; + + container_type vals; + meta_type meta; + +public: + //! Type of handle to an entry. For use with insert and remove. + typedef meta_iterator handle; + + //! Create an empty queue. + addressable_fifo_queue() { } + ~addressable_fifo_queue() { } + + //! Check if queue is empty. + //! \return If queue is empty. + bool empty() const + { return vals.empty(); } + + //! Insert new element. If the element is already in, it is moved to the + //! back. + //! \param e Element to insert. + //! \return pair Iterator to element; if element was newly + //! inserted. + std::pair insert(const KeyType& e) + { + container_iterator ei = vals.insert(vals.end(), e); + std::pair r = meta.insert(std::make_pair(e, ei)); + if (! r.second) + { + // element was already in + vals.erase(r.first->second); + r.first->second = ei; + } + return r; + } + + //! Erase element from the queue. + //! \param e Element to remove. + //! \return If element was in. + bool erase(const KeyType& e) + { + handle mi = meta.find(e); + if (mi == meta.end()) + return false; + vals.erase(mi->second); + meta.erase(mi); + return true; + } + + //! Erase element from the queue. + //! \param i Iterator to element to remove. + void erase(handle i) + { + vals.erase(i->second); + meta.erase(i); + } + + //! Access top element in the queue. + //! \return Const reference to top element. + const KeyType & top() const + { return vals.front(); } + + //! Remove top element from the queue. + //! \return Top element. + KeyType pop() + { + assert(! empty()); + const KeyType e = top(); + meta.erase(e); + vals.pop_front(); + return e; + } +}; + +//! An internal priority queue that allows removing elements addressed with (a +//! copy of) themselves. +//! \tparam KeyType Type of contained elements. +//! \tparam PriorityType Type of Priority. +template > +class addressable_priority_queue +{ + struct cmp // like < for pair, but uses Cmp for < on first + { + bool operator () (const std::pair& left, + const std::pair& right) const + { + Cmp c; + return c(left.first, right.first) || + ((! c(right.first, left.first)) && left.second < right.second); + } + }; + + typedef std::set, cmp> container_type; + typedef typename container_type::iterator container_iterator; + typedef std::map meta_type; + typedef typename meta_type::iterator meta_iterator; + + container_type vals; + meta_type meta; + +public: + //! Type of handle to an entry. For use with insert and remove. + typedef meta_iterator handle; + + //! Create an empty queue. + addressable_priority_queue() { } + ~addressable_priority_queue() { } + + //! Check if queue is empty. + //! \return If queue is empty. + bool empty() const + { return vals.empty(); } + + //! Insert new element. If the element is already in, it's priority is updated. + //! \param e Element to insert. + //! \param o Priority of element. + //! \return pair Iterator to element; if element was newly inserted. + std::pair insert(const KeyType& e, const PriorityType o) + { + std::pair s = vals.insert(std::make_pair(o, e)); + std::pair r = meta.insert(std::make_pair(e, s.first)); + if (! r.second && s.second) + { + // was already in with different priority + vals.erase(r.first->second); + r.first->second = s.first; + } + return r; + } + + //! Erase element from the queue. + //! \param e Element to remove. + //! \return If element was in. + bool erase(const KeyType& e) + { + handle mi = meta.find(e); + if (mi == meta.end()) + return false; + vals.erase(mi->second); + meta.erase(mi); + return true; + } + + //! Erase element from the queue. + //! \param i Iterator to element to remove. + void erase(handle i) + { + vals.erase(i->second); + meta.erase(i); + } + + //! Access top (= min) element in the queue. + //! \return Const reference to top element. + const KeyType & top() const + { return vals.begin()->second; } + + //! Remove top (= min) element from the queue. + //! \return Top element. + KeyType pop() + { + assert(! empty()); + const KeyType e = top(); + meta.erase(e); + vals.erase(vals.begin()); + return e; + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_ADDRESSABLE_QUEUES_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/aligned_alloc.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/aligned_alloc.h new file mode 100644 index 0000000000..8de0875149 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/aligned_alloc.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * include/stxxl/bits/common/aligned_alloc.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_COMMON_ALIGNED_ALLOC_HEADER +#define STXXL_COMMON_ALIGNED_ALLOC_HEADER + +#include +#include +#include +#include + +#ifndef STXXL_VERBOSE_ALIGNED_ALLOC +#define STXXL_VERBOSE_ALIGNED_ALLOC STXXL_VERBOSE2 +#endif + +STXXL_BEGIN_NAMESPACE + +template +struct aligned_alloc_settings { + static bool may_use_realloc; +}; + +template +bool aligned_alloc_settings::may_use_realloc = true; + +// meta_info_size > 0 is needed for array allocations that have overhead +// +// meta_info +// aligned begin of data unallocated behind data +// v v v +// ----===============#MMMM========================------ +// ^ ^^ ^ +// buffer result result+m_i_size+size +// pointer to buffer +// (---) unallocated, (===) allocated memory + +template +inline void * aligned_alloc(size_t size, size_t meta_info_size = 0) +{ + STXXL_VERBOSE2("stxxl::aligned_alloc<" << Alignment << ">(), size = " << size << ", meta info size = " << meta_info_size); +#if !defined(STXXL_WASTE_MORE_MEMORY_FOR_IMPROVED_ACCESS_AFTER_ALLOCATED_MEMORY_CHECKS) + // malloc()/realloc() variant that frees the unused amount of memory + // after the data area of size 'size'. realloc() from valgrind does not + // preserve the old memory area when shrinking, so out-of-bounds + // accesses can't be detected easily. + // Overhead: about Alignment bytes. + size_t alloc_size = Alignment + sizeof(char*) + meta_info_size + size; + char* buffer = (char*)std::malloc(alloc_size); +#else + // More space consuming and memory fragmenting variant using + // posix_memalign() instead of malloc()/realloc(). Ensures that the end + // of the data area (of size 'size') will match the end of the allocated + // block, so no corrections are neccessary and + // access-behind-allocated-memory problems can be easily detected by + // valgrind. Usually produces an extra memory fragment of about + // Alignment bytes. + // Overhead: about 2 * Alignment bytes. + size_t alloc_size = Alignment * div_ceil(sizeof(char*) + meta_info_size, Alignment) + size; + char* buffer; + if (posix_memalign((void**)&buffer, Alignment, alloc_size) != 0) + throw std::bad_alloc(); +#endif + if (buffer == NULL) + throw std::bad_alloc(); + #ifdef STXXL_ALIGNED_CALLOC + memset(buffer, 0, alloc_size); + #endif + char* reserve_buffer = buffer + sizeof(char*) + meta_info_size; + char* result = reserve_buffer + Alignment - + (((unsigned_type)reserve_buffer) % (Alignment)) - meta_info_size; + STXXL_VERBOSE2("stxxl::aligned_alloc<" << Alignment << ">() address " << (void*)result << " lost " << (result - buffer) << " bytes"); + //-tb: check that there is space for one char* before the "result" pointer + // delivered to the user. this char* is set below to the beginning of the + // allocated area. + assert(long(result - buffer) >= long(sizeof(char*))); + + // free unused memory behind the data area + // so access behind the requested size can be recognized + size_t realloc_size = (result - buffer) + meta_info_size + size; + if (realloc_size < alloc_size && aligned_alloc_settings::may_use_realloc) { + char* realloced = (char*)std::realloc(buffer, realloc_size); + if (buffer != realloced) { + // hmm, realloc does move the memory block around while shrinking, + // might run under valgrind, so disable realloc and retry + STXXL_ERRMSG("stxxl::aligned_alloc: disabling realloc()"); + std::free(realloced); + aligned_alloc_settings::may_use_realloc = false; + return aligned_alloc(size, meta_info_size); + } + assert(result + size <= buffer + realloc_size); + } + + *(((char**)result) - 1) = buffer; + STXXL_VERBOSE2( + "stxxl::aligned_alloc<" << Alignment << ">(), allocated at " << + (void*)buffer << " returning " << (void*)result); + STXXL_VERBOSE_ALIGNED_ALLOC( + "stxxl::aligned_alloc<" << Alignment << + ">(size = " << size << ", meta info size = " << meta_info_size << + ") => buffer = " << (void*)buffer << ", ptr = " << (void*)result); + + return result; +} + +template +inline void +aligned_dealloc(void* ptr) +{ + if (!ptr) + return; + char* buffer = *(((char**)ptr) - 1); + STXXL_VERBOSE_ALIGNED_ALLOC("stxxl::aligned_dealloc<" << Alignment << ">(), ptr = " << ptr << ", buffer = " << (void*)buffer); + std::free(buffer); +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_ALIGNED_ALLOC_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/binary_buffer.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/binary_buffer.h new file mode 100644 index 0000000000..9cc22898b5 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/binary_buffer.h @@ -0,0 +1,650 @@ +/*************************************************************************** + * include/stxxl/bits/common/binary_buffer.h + * + * Classes binary_buffer and binary_reader to construct data blocks with + * variable length content. Programs construct blocks using + * binary_buffer::put() and read them using + * binary_reader::get(). The operation sequences should match. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013-2014 Timo Bingmann + * + * 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 STXXL_COMMON_BINARY_BUFFER_HEADER +#define STXXL_COMMON_BINARY_BUFFER_HEADER + +#include +#include +#include +#include +#include +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup support +//! \{ + +/*! + * binary_buffer represents a dynamically growable area of memory, which can be + * modified by appending integral data types via put() and other basic + * operations. + */ +class binary_buffer +{ +protected: + //! Allocated buffer pointer. + char* m_data; + + //! Size of valid data. + size_t m_size; + + //! Total capacity of buffer. + size_t m_capacity; + +public: + //! Create a new empty object + inline binary_buffer() + : m_data(NULL), m_size(0), m_capacity(0) + { } + + //! Copy-Constructor, duplicates memory content. + inline binary_buffer(const binary_buffer& other) + : m_data(NULL), m_size(0), m_capacity(0) + { + assign(other); + } + + //! Constructor, copy memory area. + inline binary_buffer(const void* data, size_t n) + : m_data(NULL), m_size(0), m_capacity(0) + { + assign(data, n); + } + + //! Constructor, create object with n bytes pre-allocated. + inline binary_buffer(size_t n) + : m_data(NULL), m_size(0), m_capacity(0) + { + alloc(n); + } + + //! Constructor from std::string, copies string content. + inline binary_buffer(const std::string& str) + : m_data(NULL), m_size(0), m_capacity(0) + { + assign(str.data(), str.size()); + } + + //! Destroys the memory space. + inline ~binary_buffer() + { + dealloc(); + } + + //! Return a pointer to the currently kept memory area. + inline const char * data() const + { + return m_data; + } + + //! Return a writeable pointer to the currently kept memory area. + inline char * data() + { + return m_data; + } + + //! Return the currently used length in bytes. + inline size_t size() const + { + return m_size; + } + + //! Return the currently allocated buffer capacity. + inline size_t capacity() const + { + return m_capacity; + } + + //! Explicit conversion to std::string (copies memory of course). + inline std::string str() const + { + return std::string(reinterpret_cast(m_data), m_size); + } + + //! Set the valid bytes in the buffer, use if the buffer is filled + //! directly. + inline binary_buffer & set_size(size_t n) + { + assert(n <= m_capacity); + m_size = n; + + return *this; + } + + //! Make sure that at least n bytes are allocated. + inline binary_buffer & alloc(size_t n) + { + if (m_capacity < n) + { + m_capacity = n; + m_data = static_cast(realloc(m_data, m_capacity)); + } + + return *this; + } + + //! Deallocates the kept memory space (we use dealloc() instead of free() + //! as a name, because sometimes "free" is replaced by the preprocessor) + inline binary_buffer & dealloc() + { + if (m_data) free(m_data); + m_data = NULL; + m_size = m_capacity = 0; + + return *this; + } + + //! Detach the memory from the object, returns the memory pointer. + inline const char * detach() + { + const char* data = m_data; + m_data = NULL; + m_size = m_capacity = 0; + return data; + } + + //! Clears the memory contents, does not deallocate the memory. + inline binary_buffer & clear() + { + m_size = 0; + return *this; + } + + //! Copy a memory range into the buffer, overwrites all current + //! data. Roughly equivalent to clear() followed by append(). + inline binary_buffer & assign(const void* data, size_t len) + { + if (len > m_capacity) alloc(len); + + memcpy(m_data, data, len); + m_size = len; + + return *this; + } + + //! Copy the contents of another buffer object into this buffer, overwrites + //! all current data. Roughly equivalent to clear() followed by append(). + inline binary_buffer & assign(const binary_buffer& other) + { + if (&other != this) + assign(other.data(), other.size()); + + return *this; + } + + //! Assignment operator: copy other's memory range into buffer. + inline binary_buffer& operator = (const binary_buffer& other) + { + if (&other != this) + assign(other.data(), other.size()); + + return *this; + } + + //! Align the size of the buffer to a multiple of n. Fills up with 0s. + inline binary_buffer & align(size_t n) + { + assert(n > 0); + size_t rem = m_size % n; + if (rem != 0) + { + size_t add = n - rem; + if (m_size + add > m_capacity) dynalloc(m_size + add); + memset(m_data + m_size, 0, add); + m_size += add; + } + assert((m_size % n) == 0); + + return *this; + } + + //! Dynamically allocate more memory. At least n bytes will be available, + //! probably more to compensate future growth. + inline binary_buffer & dynalloc(size_t n) + { + if (m_capacity < n) + { + // place to adapt the buffer growing algorithm as need. + size_t newsize = m_capacity; + + while (newsize < n) { + if (newsize < 256) newsize = 512; + else if (newsize < 1024 * 1024) newsize = 2 * newsize; + else newsize += 1024 * 1024; + } + + alloc(newsize); + } + + return *this; + } + + // *** Appending Write Functions *** + + //! Append a memory range to the buffer + inline binary_buffer & append(const void* data, size_t len) + { + if (m_size + len > m_capacity) dynalloc(m_size + len); + + memcpy(m_data + m_size, data, len); + m_size += len; + + return *this; + } + + //! Append the contents of a different buffer object to this one. + inline binary_buffer & append(const class binary_buffer& bb) + { + return append(bb.data(), bb.size()); + } + + //! Append to contents of a std::string, excluding the null (which isn't + //! contained in the string size anyway). + inline binary_buffer & append(const std::string& s) + { + return append(s.data(), s.size()); + } + + //! Put (append) a single item of the template type T to the buffer. Be + //! careful with implicit type conversions! + template + inline binary_buffer & put(const Type item) + { + if (m_size + sizeof(Type) > m_capacity) dynalloc(m_size + sizeof(Type)); + + *reinterpret_cast(m_data + m_size) = item; + m_size += sizeof(Type); + + return *this; + } + + //! Append a varint to the buffer. + inline binary_buffer & put_varint(uint32 v) + { + if (v < 128) { + put(uint8(v)); + } + else if (v < 128 * 128) { + put((uint8)(((v >> 0) & 0x7F) | 0x80)); + put((uint8)((v >> 7) & 0x7F)); + } + else if (v < 128 * 128 * 128) { + put((uint8)(((v >> 0) & 0x7F) | 0x80)); + put((uint8)(((v >> 7) & 0x7F) | 0x80)); + put((uint8)((v >> 14) & 0x7F)); + } + else if (v < 128 * 128 * 128 * 128) { + put((uint8)(((v >> 0) & 0x7F) | 0x80)); + put((uint8)(((v >> 7) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)((v >> 21) & 0x7F)); + } + else { + put((uint8)(((v >> 0) & 0x7F) | 0x80)); + put((uint8)(((v >> 7) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)(((v >> 21) & 0x7F) | 0x80)); + put((uint8)((v >> 28) & 0x7F)); + } + + return *this; + } + + //! Append a varint to the buffer. + inline binary_buffer & put_varint(int v) + { + return put_varint((uint32)v); + } + + //! Append a varint to the buffer. + inline binary_buffer & put_varint(uint64 v) + { + if (v < 128) { + put(uint8(v)); + } + else if (v < 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)((v >> 07) & 0x7F)); + } + else if (v < 128 * 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)((v >> 14) & 0x7F)); + } + else if (v < 128 * 128 * 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)((v >> 21) & 0x7F)); + } + else if (v < ((uint64)128) * 128 * 128 * 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)(((v >> 21) & 0x7F) | 0x80)); + put((uint8)((v >> 28) & 0x7F)); + } + else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)(((v >> 21) & 0x7F) | 0x80)); + put((uint8)(((v >> 28) & 0x7F) | 0x80)); + put((uint8)((v >> 35) & 0x7F)); + } + else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)(((v >> 21) & 0x7F) | 0x80)); + put((uint8)(((v >> 28) & 0x7F) | 0x80)); + put((uint8)(((v >> 35) & 0x7F) | 0x80)); + put((uint8)((v >> 42) & 0x7F)); + } + else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128 * 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)(((v >> 21) & 0x7F) | 0x80)); + put((uint8)(((v >> 28) & 0x7F) | 0x80)); + put((uint8)(((v >> 35) & 0x7F) | 0x80)); + put((uint8)(((v >> 42) & 0x7F) | 0x80)); + put((uint8)((v >> 49) & 0x7F)); + } + else if (v < ((uint64)128) * 128 * 128 * 128 * 128 * 128 * 128 * 128 * 128) { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)(((v >> 21) & 0x7F) | 0x80)); + put((uint8)(((v >> 28) & 0x7F) | 0x80)); + put((uint8)(((v >> 35) & 0x7F) | 0x80)); + put((uint8)(((v >> 42) & 0x7F) | 0x80)); + put((uint8)(((v >> 49) & 0x7F) | 0x80)); + put((uint8)((v >> 56) & 0x7F)); + } + else { + put((uint8)(((v >> 00) & 0x7F) | 0x80)); + put((uint8)(((v >> 07) & 0x7F) | 0x80)); + put((uint8)(((v >> 14) & 0x7F) | 0x80)); + put((uint8)(((v >> 21) & 0x7F) | 0x80)); + put((uint8)(((v >> 28) & 0x7F) | 0x80)); + put((uint8)(((v >> 35) & 0x7F) | 0x80)); + put((uint8)(((v >> 42) & 0x7F) | 0x80)); + put((uint8)(((v >> 49) & 0x7F) | 0x80)); + put((uint8)(((v >> 56) & 0x7F) | 0x80)); + put((uint8)((v >> 63) & 0x7F)); + } + + return *this; + } + + //! Put a string by saving it's length followed by the data itself. + inline binary_buffer & put_string(const char* data, size_t len) + { + return put_varint((uint32)len).append(data, len); + } + + //! Put a string by saving it's length followed by the data itself. + inline binary_buffer & put_string(const std::string& str) + { + return put_string(str.data(), str.size()); + } + + //! Put a binary_buffer by saving it's length followed by the data itself. + inline binary_buffer & put_string(const binary_buffer& bb) + { + return put_string(bb.data(), bb.size()); + } +}; + +/*! + * binary_buffer_ref represents a memory area as pointer and valid length. It + * is not deallocated or otherwise managed. This class can be used to pass + * around references to binary_buffer objects. + */ +class binary_buffer_ref +{ +protected: + //! Allocated buffer pointer. + const char* m_data; + + //! Size of valid data. + size_t m_size; + +public: + //! Constructor, assign memory area from binary_buffer. + binary_buffer_ref(const binary_buffer& bb) + : m_data(bb.data()), m_size(bb.size()) + { } + + //! Constructor, assign memory area from pointer and length. + binary_buffer_ref(const void* data, size_t n) + : m_data(reinterpret_cast(data)), m_size(n) + { } + + //! Constructor, assign memory area from string, does NOT copy. + inline binary_buffer_ref(const std::string& str) + : m_data(str.data()), m_size(str.size()) + { } + + //! Return a pointer to the currently kept memory area. + const void * data() const + { return m_data; } + + //! Return the currently valid length in bytes. + size_t size() const + { return m_size; } + + //! Explicit conversion to std::string (copies memory of course). + inline std::string str() const + { return std::string(reinterpret_cast(m_data), m_size); } + + //! Compare contents of two binary_buffer_refs. + bool operator == (const binary_buffer_ref& br) const + { + if (m_size != br.m_size) return false; + return memcmp(m_data, br.m_data, m_size) == 0; + } + + //! Compare contents of two binary_buffer_refs. + bool operator != (const binary_buffer_ref& br) const + { + if (m_size != br.m_size) return true; + return memcmp(m_data, br.m_data, m_size) != 0; + } +}; + +/*! + * binary_reader represents a binary_buffer_ref with an additional cursor with which + * the memory can be read incrementally. + */ +class binary_reader : public binary_buffer_ref +{ +protected: + //! Current read cursor + size_t m_curr; + +public: + //! Constructor, assign memory area from binary_buffer. + inline binary_reader(const binary_buffer_ref& br) + : binary_buffer_ref(br), m_curr(0) + { } + + //! Constructor, assign memory area from pointer and length. + inline binary_reader(const void* data, size_t n) + : binary_buffer_ref(data, n), m_curr(0) + { } + + //! Constructor, assign memory area from string, does NOT copy. + inline binary_reader(const std::string& str) + : binary_buffer_ref(str), m_curr(0) + { } + + //! Return the current read cursor. + inline size_t curr() const + { + return m_curr; + } + + //! Reset the read cursor. + inline binary_reader & rewind() + { + m_curr = 0; + return *this; + } + + //! Check that n bytes are available at the cursor. + inline bool cursor_available(size_t n) const + { + return (m_curr + n <= m_size); + } + + //! Throws a std::underflow_error unless n bytes are available at the + //! cursor. + inline void check_available(size_t n) const + { + if (!cursor_available(n)) + throw (std::underflow_error("binary_reader underrun")); + } + + //! Return true if the cursor is at the end of the buffer. + inline bool empty() const + { + return (m_curr == m_size); + } + + //! Advance the cursor given number of bytes without reading them. + inline binary_reader & skip(size_t n) + { + check_available(n); + m_curr += n; + + return *this; + } + + //! Fetch a number of unstructured bytes from the buffer, advancing the + //! cursor. + inline binary_reader & read(void* outdata, size_t datalen) + { + check_available(datalen); + memcpy(outdata, m_data + m_curr, datalen); + m_curr += datalen; + + return *this; + } + + //! Fetch a number of unstructured bytes from the buffer as std::string, + //! advancing the cursor. + inline std::string read(size_t datalen) + { + check_available(datalen); + std::string out(m_data + m_curr, datalen); + m_curr += datalen; + return out; + } + + //! Fetch a single item of the template type Type from the buffer, + //! advancing the cursor. Be careful with implicit type conversions! + template + inline Type get() + { + check_available(sizeof(Type)); + + Type ret = *reinterpret_cast(m_data + m_curr); + m_curr += sizeof(Type); + + return ret; + } + + //! Fetch a varint with up to 32-bit from the buffer at the cursor. + inline uint32 get_varint() + { + uint32 u, v = get(); + if (!(v & 0x80)) return v; + v &= 0x7F; + u = get(), v |= (u & 0x7F) << 7; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 14; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 21; + if (!(u & 0x80)) return v; + u = get(); + if (u & 0xF0) + throw (std::overflow_error("Overflow during varint decoding.")); + v |= (u & 0x7F) << 28; + return v; + } + + //! Fetch a 64-bit varint from the buffer at the cursor. + inline uint64 get_varint64() + { + uint64 u, v = get(); + if (!(v & 0x80)) return v; + v &= 0x7F; + u = get(), v |= (u & 0x7F) << 7; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 14; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 21; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 28; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 35; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 42; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 49; + if (!(u & 0x80)) return v; + u = get(), v |= (u & 0x7F) << 56; + if (!(u & 0x80)) return v; + u = get(); + if (u & 0xFE) + throw (std::overflow_error("Overflow during varint64 decoding.")); + v |= (u & 0x7F) << 63; + return v; + } + + //! Fetch a string which was put via put_string(). + inline std::string get_string() + { + uint32 len = get_varint(); + return read(len); + } + + //! Fetch a binary_buffer_ref to a binary string or blob which was put via + //! put_string(). Does NOT copy the data. + inline binary_buffer_ref get_binary_buffer_ref() + { + uint32 len = get_varint(); + // save object + binary_buffer_ref br(m_data + m_curr, len); + // skip over sub block data + skip(len); + return br; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_BINARY_BUFFER_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/cmdline.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/cmdline.h new file mode 100644 index 0000000000..04ffdd1b2e --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/cmdline.h @@ -0,0 +1,811 @@ +/*************************************************************************** + * include/stxxl/bits/common/cmdline.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_CMDLINE_HEADER +#define STXXL_COMMON_CMDLINE_HEADER + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup support +//! \{ + +/** + * Command line parser which automatically fills variables and prints nice + * usage messages. + * + * This is a straightforward command line parser in C++, which will recognize + * short options -s, long options --long and parameters, both required and + * optional. It will automatically parse integers and byte sizes with + * SI/IEC suffixes (e.g. 1 GiB). It also works with lists of strings, + * e.g. multiple filenames. + * + * Maybe most important it will nicely format the options and parameters + * description using word wrapping. + */ +class cmdline_parser : private noncopyable +{ +protected: + //! base class of all options and parameters + struct argument + { + //! single letter short option, or 0 is none + char m_key; + //! long option key or name for parameters + std::string m_longkey; + //! option type description, e.g. "<#>" to indicate numbers + std::string m_keytype; + //! longer description, which will be wrapped + std::string m_desc; + //! required, process() fails if the option/parameter is not found. + bool m_required; + //! found during processing of command line + bool m_found; + //! repeated argument, i.e. std::vector + bool m_repeated; + + //! contructor filling most attributes + argument(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required) + : m_key(key), m_longkey(longkey), m_keytype(keytype), + m_desc(desc), m_required(required), m_found(false), + m_repeated(false) + { } + + //! empty virtual destructor + virtual ~argument() { } + + //! return formatted type name to user + virtual const char * type_name() const = 0; + + //! process one item from command line for this argument + virtual bool process(int& argc, const char* const*& argv) = 0; + + //! format value to ostream + virtual void print_value(std::ostream& os) const = 0; + + //! return 'longkey [keytype]' + std::string param_text() const + { + std::string s = m_longkey; + if (m_keytype.size()) { + s += ' ' + m_keytype; + } + return s; + } + + //! return '-s, --longkey [keytype]' + std::string option_text() const + { + std::string s; + if (m_key) { + s += '-'; + s += m_key; + s += ", "; + } + s += "--"; + s += m_longkey; + if (m_keytype.size()) { + s += ' ' + m_keytype; + } + return s; + } + }; + + //! specialization of argument for boolean flags (can only be set to true). + struct argument_flag : public argument + { + //! reference to boolean to set to true + bool& m_dest; + + //! contructor filling most attributes + argument_flag(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, bool& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { } + + virtual const char * type_name() const + { return "flag"; } + + //! "process" argument: just set to true, no argument is used. + virtual bool process(int&, const char* const*&) + { + m_dest = true; + return true; + } + + virtual void print_value(std::ostream& os) const + { os << (m_dest ? "true" : "false"); } + }; + + //! specialization of argument for integer options or parameters + struct argument_int : public argument + { + int& m_dest; + + //! contructor filling most attributes + argument_int(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, int& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { } + + virtual const char * type_name() const + { return "integer"; } + + //! parse signed integer using sscanf. + virtual bool process(int& argc, const char* const*& argv) + { + if (argc == 0) return false; + if (sscanf(argv[0], "%d", &m_dest) == 1) { + --argc, ++argv; + return true; + } + else { + return false; + } + } + + virtual void print_value(std::ostream& os) const + { os << m_dest; } + }; + + //! specialization of argument for unsigned integer options or parameters + struct argument_uint : public argument + { + unsigned int& m_dest; + + //! contructor filling most attributes + argument_uint(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, + unsigned int& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { } + + virtual const char * type_name() const + { return "unsigned integer"; } + + //! parse unsigned integer using sscanf. + virtual bool process(int& argc, const char* const*& argv) + { + if (argc == 0) return false; + if (sscanf(argv[0], "%u", &m_dest) == 1) { + --argc, ++argv; + return true; + } + else { + return false; + } + } + + virtual void print_value(std::ostream& os) const + { os << m_dest; } + }; + + //! specialization of argument for double options or parameters + struct argument_double : public argument + { + double& m_dest; + + //! contructor filling most attributes + argument_double(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, + double& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { } + + virtual const char * type_name() const + { return "double"; } + + //! parse unsigned integer using sscanf. + virtual bool process(int& argc, const char* const*& argv) + { + if (argc == 0) return false; + if (sscanf(argv[0], "%lf", &m_dest) == 1) { + --argc, ++argv; + return true; + } + else { + return false; + } + } + + virtual void print_value(std::ostream& os) const + { os << m_dest; } + }; + + //! specialization of argument for SI/IEC suffixes byte size options or + //! parameters + struct argument_bytes32 : public argument + { + uint32& m_dest; + + //! contructor filling most attributes + argument_bytes32(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, uint32& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { } + + virtual const char * type_name() const + { return "bytes"; } + + //! parse byte size using SI/IEC parser from stxxl. + virtual bool process(int& argc, const char* const*& argv) + { + if (argc == 0) return false; + uint64 dest; + if (parse_SI_IEC_size(argv[0], dest) && + (uint64)(m_dest = (uint32)dest) == dest) { + --argc, ++argv; + return true; + } + else { + return false; + } + } + + virtual void print_value(std::ostream& os) const + { os << m_dest; } + }; + + //! specialization of argument for SI/IEC suffixes byte size options or + //! parameters + struct argument_bytes64 : public argument + { + uint64& m_dest; + + //! contructor filling most attributes + argument_bytes64(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, uint64& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { } + + virtual const char * type_name() const + { return "bytes"; } + + //! parse byte size using SI/IEC parser from stxxl. + virtual bool process(int& argc, const char* const*& argv) + { + if (argc == 0) return false; + if (parse_SI_IEC_size(argv[0], m_dest)) { + --argc, ++argv; + return true; + } + else { + return false; + } + } + + virtual void print_value(std::ostream& os) const + { os << m_dest; } + }; + + //! specialization of argument for string options or parameters + struct argument_string : public argument + { + std::string& m_dest; + + //! contructor filling most attributes + argument_string(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, + std::string& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { } + + virtual const char * type_name() const + { return "string"; } + + //! "process" string argument just by storing it. + virtual bool process(int& argc, const char* const*& argv) + { + if (argc == 0) return false; + m_dest = argv[0]; + --argc, ++argv; + return true; + } + + virtual void print_value(std::ostream& os) const + { os << '"' << m_dest << '"'; } + }; + + //! specialization of argument for multiple string options or parameters + struct argument_stringlist : public argument + { + std::vector& m_dest; + + //! contructor filling most attributes + argument_stringlist(char key, const std::string& longkey, + const std::string& keytype, + const std::string& desc, bool required, + std::vector& dest) + : argument(key, longkey, keytype, desc, required), + m_dest(dest) + { + m_repeated = true; + } + + virtual const char * type_name() const + { return "string list"; } + + //! "process" string argument just by storing it in vector. + virtual bool process(int& argc, const char* const*& argv) + { + if (argc == 0) return false; + m_dest.push_back(argv[0]); + --argc, ++argv; + return true; + } + + virtual void print_value(std::ostream& os) const + { + os << '['; + for (size_t i = 0; i < m_dest.size(); ++i) + { + if (i != 0) os << ','; + os << '"' << m_dest[i] << '"'; + } + os << ']'; + } + }; + +protected: + //! option and parameter list type + typedef std::vector arglist_type; + + //! list of options available + arglist_type m_optlist; + //! list of parameters, both required and optional + arglist_type m_paramlist; + + //! formatting width for options, '-s, --switch <#>' + int m_opt_maxlong; + //! formatting width for parameters, 'param <#>' + int m_param_maxlong; + + //! argv[0] for usage. + const char* m_progname; + + //! verbose processing of arguments + bool m_verbose_process; + + //! user set description of program, will be wrapped + std::string m_description; + //! user set author of program, will be wrapped + std::string m_author; + + //! set line wrap length + unsigned int m_linewrap; + + //! maximum length of a type_name() result + static const int m_maxtypename = 16; + +private: + //! update maximum formatting width for new option + void calc_opt_max(const argument* arg) + { + m_opt_maxlong = STXXL_MAX((int)arg->option_text().size() + 2, + m_opt_maxlong); + } + + //! update maximum formatting width for new parameter + void calc_param_max(const argument* arg) + { + m_param_maxlong = STXXL_MAX((int)arg->param_text().size() + 2, + m_param_maxlong); + } + +public: + //! Wrap a long string at spaces into lines. Prefix is added + //! unconditionally to each line. Lines are wrapped after wraplen + //! characters if possible. + static void + output_wrap(std::ostream& os, const std::string& text, size_t wraplen, + size_t indent_first = 0, size_t indent_rest = 0, + size_t current = 0, size_t indent_newline = 0); + +public: + //! Construct new command line parser + cmdline_parser() + : m_opt_maxlong(8), + m_param_maxlong(8), + m_progname(NULL), + m_verbose_process(true), + m_linewrap(80) + { } + + //! Delete all added arguments + ~cmdline_parser() + { + for (size_t i = 0; i < m_optlist.size(); ++i) + delete m_optlist[i]; + m_optlist.clear(); + + for (size_t i = 0; i < m_paramlist.size(); ++i) + delete m_paramlist[i]; + m_paramlist.clear(); + } + + //! Set description of program, text will be wrapped + void set_description(const std::string& description) + { + m_description = description; + } + + //! Set author of program, will be wrapped. + void set_author(const std::string& author) + { + m_author = author; + } + + //! Set verbose processing of command line arguments + void set_verbose_process(bool verbose_process) + { + m_verbose_process = verbose_process; + } + + // ************************************************************************ + + //! add boolean option flag -key, --longkey [keytype] with description and + //! store to dest + void add_flag(char key, const std::string& longkey, + const std::string& keytype, bool& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_flag(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add signed integer option -key, --longkey [keytype] with description + //! and store to dest + void add_int(char key, const std::string& longkey, + const std::string& keytype, int& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_int(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add unsigned integer option -key, --longkey [keytype] with description + //! and store to dest + void add_uint(char key, const std::string& longkey, + const std::string& keytype, unsigned int& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_uint(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add double option -key, --longkey [keytype] with description and store + //! to dest + void add_double(char key, const std::string& longkey, + const std::string& keytype, double& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_double(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and + //! store to 64-bit dest + void add_bytes(char key, const std::string& longkey, + const std::string& keytype, stxxl::uint32& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_bytes32(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and + //! store to 64-bit dest + void add_bytes(char key, const std::string& longkey, + const std::string& keytype, stxxl::uint64& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_bytes64(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add string option -key, --longkey [keytype] and store to dest + void add_string(char key, const std::string& longkey, + const std::string& keytype, std::string& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_string(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add string list option -key, --longkey [keytype] and store to dest + void add_stringlist(char key, const std::string& longkey, + const std::string& keytype, + std::vector& dest, + const std::string& desc) + { + m_optlist.push_back( + new argument_stringlist(key, longkey, keytype, desc, false, dest) + ); + calc_opt_max(m_optlist.back()); + } + + //! add boolean option flag -key, --longkey with description and store to + //! dest + void add_flag(char key, const std::string& longkey, bool& dest, + const std::string& desc) + { return add_flag(key, longkey, "", dest, desc); } + + //! add signed integer option -key, --longkey with description and store to + //! dest + void add_int(char key, const std::string& longkey, int& dest, + const std::string& desc) + { return add_int(key, longkey, "", dest, desc); } + + //! add unsigned integer option -key, --longkey [keytype] with description + //! and store to dest + void add_uint(char key, const std::string& longkey, unsigned int& dest, + const std::string& desc) + { return add_uint(key, longkey, "", dest, desc); } + + //! add double option -key, --longkey [keytype] with description and store + //! to dest + void add_double(char key, const std::string& longkey, double& dest, + const std::string& desc) + { return add_double(key, longkey, "", dest, desc); } + + //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and + //! store to 32-bit dest + void add_bytes(char key, const std::string& longkey, stxxl::uint32& dest, + const std::string& desc) + { return add_bytes(key, longkey, "", dest, desc); } + + //! add SI/IEC suffixes byte size option -key, --longkey [keytype] and + //! store to 64-bit dest + void add_bytes(char key, const std::string& longkey, stxxl::uint64& dest, + const std::string& desc) + { return add_bytes(key, longkey, "", dest, desc); } + + //! add string option -key, --longkey [keytype] and store to dest + void add_string(char key, const std::string& longkey, std::string& dest, + const std::string& desc) + { return add_string(key, longkey, "", dest, desc); } + + //! add string list option -key, --longkey [keytype] and store to dest + void add_stringlist(char key, const std::string& longkey, + std::vector& dest, const std::string& desc) + { return add_stringlist(key, longkey, "", dest, desc); } + + // ************************************************************************ + + //! add signed integer parameter [name] with description and store to dest + void add_param_int(const std::string& name, int& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_int(0, name, "", desc, true, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add unsigned integer parameter [name] with description and store to dest + void add_param_uint(const std::string& name, unsigned int& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_uint(0, name, "", desc, true, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add double parameter [name] with description and store to dest + void add_param_double(const std::string& name, double& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_double(0, name, "", desc, true, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add SI/IEC suffixes byte size parameter [name] with description and + //! store to dest + void add_param_bytes(const std::string& name, uint32& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_bytes32(0, name, "", desc, true, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add SI/IEC suffixes byte size parameter [name] with description and + //! store to dest + void add_param_bytes(const std::string& name, uint64& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_bytes64(0, name, "", desc, true, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add string parameter [name] with description and store to dest + void add_param_string(const std::string& name, std::string& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_string(0, name, "", desc, true, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add string list parameter [name] with description and store to dest. + //! \warning this parameter must be last, as it will gobble all non-option + //! arguments! + void add_param_stringlist(const std::string& name, + std::vector& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_stringlist(0, name, "", desc, true, dest) + ); + calc_param_max(m_paramlist.back()); + } + + // ************************************************************************ + + //! add optional signed integer parameter [name] with description and store + //! to dest + void add_opt_param_int(const std::string& name, int& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_int(0, name, "", desc, false, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add optional unsigned integer parameter [name] with description and + //! store to dest + void add_opt_param_uint(const std::string& name, unsigned int& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_uint(0, name, "", desc, false, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add optional double parameter [name] with description and store to dest + void add_opt_param_double(const std::string& name, double& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_double(0, name, "", desc, false, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add optional SI/IEC suffixes byte size parameter [name] with + //! description and store to dest + void add_opt_param_bytes(const std::string& name, uint32& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_bytes32(0, name, "", desc, false, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add optional SI/IEC suffixes byte size parameter [name] with + //! description and store to dest + void add_opt_param_bytes(const std::string& name, uint64& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_bytes64(0, name, "", desc, false, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add optional string parameter [name] with description and store to dest + void add_opt_param_string(const std::string& name, std::string& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_string(0, name, "", desc, false, dest) + ); + calc_param_max(m_paramlist.back()); + } + + //! add optional string parameter [name] with description and store to dest + //! \warning this parameter must be last, as it will gobble all non-option + //! arguments! + void add_opt_param_stringlist(const std::string& name, + std::vector& dest, + const std::string& desc) + { + m_paramlist.push_back( + new argument_stringlist(0, name, "", desc, false, dest) + ); + calc_param_max(m_paramlist.back()); + } + + // ************************************************************************ + + //! output nicely formatted usage information including description of all + //! parameters and options. + void print_usage(std::ostream& os = std::cout); + +private: + //! print error about option. + void print_option_error(int argc, const char* const* argv, + const argument* arg, + std::ostream& os); + + //! print error about parameter. + void print_param_error(int argc, const char* const* argv, + const argument* arg, + std::ostream& os); + +public: + //! parse command line options as specified by the options and parameters + //! added. + //! \return true if command line is okay and all required parameters are + //! present. + bool process(int argc, const char* const* argv, + std::ostream& os = std::cout); + + //! print nicely formatted result of processing + void print_result(std::ostream& os = std::cout); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_CMDLINE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/condition_variable.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/condition_variable.h new file mode 100644 index 0000000000..6433b417a6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/condition_variable.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * include/stxxl/bits/common/condition_variable.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_CONDITION_VARIABLE_HEADER +#define STXXL_COMMON_CONDITION_VARIABLE_HEADER + +#include +#include +#include + +#if STXXL_STD_THREADS + #include +#elif STXXL_BOOST_THREADS + #include +#elif STXXL_POSIX_THREADS + #include + #include + + #include + #include +#else + #error "Thread implementation not detected." +#endif + +STXXL_BEGIN_NAMESPACE + +#if STXXL_STD_THREADS + +typedef std::condition_variable condition_variable; + +#elif STXXL_BOOST_THREADS + +typedef boost::condition condition_variable; + +#elif STXXL_POSIX_THREADS + +class condition_variable : private noncopyable +{ + //! pthread handle to condition + pthread_cond_t cond; + +public: + //! initialize condition variable + condition_variable() + { + STXXL_CHECK_PTHREAD_CALL(pthread_cond_init(&cond, NULL)); + } + //! destroy condition variable + ~condition_variable() noexcept(false) + { + STXXL_CHECK_PTHREAD_CALL(pthread_cond_destroy(&cond)); + } + //! notify one waiting thread + void notify_one() + { + STXXL_CHECK_PTHREAD_CALL(pthread_cond_signal(&cond)); + } + //! notify all waiting threads + void notify_all() + { + STXXL_CHECK_PTHREAD_CALL(pthread_cond_broadcast(&cond)); + } + //! wait for a signal on the condition variable + void wait(scoped_mutex_lock& lock) + { + STXXL_CHECK_PTHREAD_CALL(pthread_cond_wait(&cond, &lock.native_handle())); + } +}; + +#endif + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_CONDITION_VARIABLE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/counting_ptr.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/counting_ptr.h new file mode 100644 index 0000000000..6eab3fdff0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/counting_ptr.h @@ -0,0 +1,525 @@ +/*************************************************************************** + * include/stxxl/bits/common/counting_ptr.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010-2011 Raoul Steffen + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_COUNTING_PTR_HEADER +#define STXXL_COMMON_COUNTING_PTR_HEADER + +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup support +//! \{ + +/*! + * High-performance smart pointer used as a wrapping reference counting + * pointer. + * + * This smart pointer class requires two functions in the templated type: void + * inc_reference() and void dec_reference(). These must increment and decrement + * a reference counter inside the templated object. When initialized, the type + * must have reference count zero. It is _not_ immediately called with + * add_reference(). Each new object referencing the data calls add_reference() + * and each destroying holder calls del_reference(). When the data object + * determines that it's internal counter is zero, then it must destroy itself. + * + * Accompanying the counting_ptr is a const_counting_ptr and a class + * counted_object, from which reference counted classes must be derive + * from. The class counted_object implement all methods required for reference + * counting. + * + * The whole method is more similar to boost' instrusive_ptr, but also yields + * something resembling shared_ptr. + */ +template +class counting_ptr +{ +public: + //! contained type. + typedef Type element_type; + +private: + //! the pointer to the currently referenced object. + Type* m_ptr; + +protected: + //! increment reference counter for current object. + void inc_reference() + { inc_reference(m_ptr); } + + //! increment reference counter of other object. + void inc_reference(Type* o) + { if (o) o->inc_reference(); } + + //! decrement reference counter of current object and maybe delete it. + void dec_reference() + { if (m_ptr && m_ptr->dec_reference()) delete m_ptr; } + +public: + //! default constructor: contains a NULL pointer. + counting_ptr() : m_ptr(NULL) + { } + + //! constructor with pointer: initializes new reference to ptr. + counting_ptr(Type* ptr) : m_ptr(ptr) + { inc_reference(); } + + //! copy-constructor: also initializes new reference to ptr. + counting_ptr(const counting_ptr& other_ptr) : m_ptr(other_ptr) + { inc_reference(); } + + //! assignment operator: dereference current object and acquire reference on new one. + counting_ptr& operator = (const counting_ptr& other_ptr) + { return operator = (other_ptr.m_ptr); } + + //! assignment to pointer: dereference current and acquire reference to new ptr. + counting_ptr& operator = (Type* ptr) + { + inc_reference(ptr); + dec_reference(); + m_ptr = ptr; + return *this; + } + + //! destructor: decrements reference counter in ptr. + ~counting_ptr() + { dec_reference(); } + + //! return the enclosed object as reference. + Type& operator * () const + { + assert(m_ptr); + return *m_ptr; + } + + //! return the enclosed pointer. + Type* operator -> () const + { + assert(m_ptr); + return m_ptr; + } + + //! implicit cast to the enclosed pointer. + operator Type* () const + { return m_ptr; } + + //! return the enclosed pointer. + Type * get() const + { return m_ptr; } + + //! test equality of only the pointer values. + bool operator == (const counting_ptr& other_ptr) const + { return m_ptr == other_ptr.m_ptr; } + + //! test inequality of only the pointer values. + bool operator != (const counting_ptr& other_ptr) const + { return m_ptr != other_ptr.m_ptr; } + + //! cast to bool check for a NULL pointer + operator bool () const + { return valid(); } + + //! test for a non-NULL pointer + bool valid() const + { return (m_ptr != NULL); } + + //! test for a NULL pointer + bool empty() const + { return (m_ptr == NULL); } + + //! if the object is referred by this counting_ptr only + bool unique() const + { return m_ptr && m_ptr->unique(); } + + //! make and refer a copy if the original object was shared. + void unify() + { + if (m_ptr && ! m_ptr->unique()) + operator = (new Type(*m_ptr)); + } + + //! swap enclosed object with another counting pointer (no reference counts need change) + void swap(counting_ptr& b) + { + std::swap(m_ptr, b.m_ptr); + } +}; + +//! swap enclosed object with another counting pointer (no reference counts need change) +template +void swap(counting_ptr& a1, counting_ptr& a2) +{ + a1.swap(a2); +} + +/*! + * High-performance smart pointer used as a wrapping reference counting + * pointer. + * + * This smart pointer class requires two functions in the templated type: void + * inc_reference() and void dec_reference(). These must increment and decrement + * a reference counter inside the templated object. When initialized, the type + * must have reference count zero. It is _not_ immediately called with + * add_reference(). Each new object referencing the data calls add_reference() + * and each destroying holder calls del_reference(). When the data object + * determines that it's internal counter is zero, then it must destroy itself. + * + * Accompanying the counting_ptr is a const_counting_ptr and a class + * counted_object, from which reference counted classes must be derive + * from. The class counted_object implement all methods required for reference + * counting. + * + * The whole method is more similar to boost' instrusive_ptr, but also yields + * something resembling shared_ptr. + */ +template +class const_counting_ptr +{ +public: + //! contained type. + typedef Type element_type; + +private: + //! the pointer to the currently referenced object. + const Type* m_ptr; + +protected: + //! increment reference counter for current object. + void inc_reference() + { inc_reference(m_ptr); } + + //! increment reference counter of other object. + void inc_reference(const Type* o) + { if (o) o->inc_reference(); } + + //! decrement reference counter of current object and maybe delete it. + void dec_reference() + { if (m_ptr && m_ptr->dec_reference()) delete m_ptr; } + +public: + //! default constructor: contains a NULL pointer. + const_counting_ptr() : m_ptr(NULL) + { } + + //! constructor with pointer: initializes new reference to ptr. + const_counting_ptr(const Type* ptr) : m_ptr(ptr) + { inc_reference(); } + + //! copy-constructor: also initializes new reference to ptr. + const_counting_ptr(const const_counting_ptr& other_ptr) : m_ptr(other_ptr) + { inc_reference(); } + + //! constructor from non-const: also initializes new reference to ptr. + const_counting_ptr(const counting_ptr& other_ptr) : m_ptr(other_ptr.get()) + { inc_reference(); } + + //! assignment operator: dereference current object and acquire reference on new one. + const_counting_ptr& operator = (const const_counting_ptr& other_ptr) + { return operator = (other_ptr.m_ptr); } + + //! assignment operator: dereference current object and acquire reference on new one. + const_counting_ptr& operator = (const counting_ptr& other_ptr) + { return operator = (other_ptr.get()); } + + //! assignment to pointer: dereference current and acquire reference to new ptr. + const_counting_ptr& operator = (const Type* ptr) + { + inc_reference(ptr); + dec_reference(); + m_ptr = ptr; + return *this; + } + + //! destructor: decrements reference counter in ptr. + ~const_counting_ptr() + { dec_reference(); } + + //! return the enclosed object as reference. + const Type& operator * () const + { + assert(m_ptr); + return *m_ptr; + } + + //! return the enclosed pointer. + const Type* operator -> () const + { + assert(m_ptr); + return m_ptr; + } + + //! implicit cast to the enclosed pointer. + operator const Type* () const + { return m_ptr; } + + //! return the enclosed pointer. + const Type * get() const + { return m_ptr; } + + //! test equality of only the pointer values. + bool operator == (const const_counting_ptr& other_ptr) const + { return m_ptr == other_ptr.m_ptr; } + + //! test inequality of only the pointer values. + bool operator != (const const_counting_ptr& other_ptr) const + { return m_ptr != other_ptr.m_ptr; } + + //! test equality of only the pointer values. + bool operator == (const counting_ptr& other_ptr) const + { return m_ptr == other_ptr.get(); } + + //! test inequality of only the pointer values. + bool operator != (const counting_ptr& other_ptr) const + { return m_ptr != other_ptr.get(); } + + //! cast to bool check for a NULL pointer + operator bool () const + { return m_ptr; } + + //! test for a non-NULL pointer + bool valid() const + { return m_ptr; } + + //! test for a NULL pointer + bool empty() const + { return !m_ptr; } + + //! if the object is referred by this const_counting_ptr only + bool unique() const + { return m_ptr && m_ptr->unique(); } + + //! swap enclosed object with another const_counting pointer (no reference counts need change) + void swap(const_counting_ptr& b) + { + std::swap(m_ptr, b.m_ptr); + } +}; + +//! swap enclosed object with another const_counting pointer (no reference counts need change) +template +void swap(const_counting_ptr& a1, const_counting_ptr& a2) +{ + a1.swap(a2); +} + +/*! + * Provides reference counting abilities for use with counting_ptr. + * + * Use as superclass of the actual object, this adds a reference_count + * value. Then either use counting_ptr as pointer to manage references and + * deletion, or just do normal new and delete. + * + * For thread-safe functions, use atomic_counted_object instead of this class! + */ +class counted_object +{ +private: + //! the reference count is kept mutable to all const_counting_ptr() to + //! change the reference count. + mutable unsigned_type m_reference_count; + +public: + //! new objects have zero reference count + counted_object() + : m_reference_count(0) { } + + //! coping still creates a new object with zero reference count + counted_object(const counted_object&) + : m_reference_count(0) { } + + //! assignment operator, leaves pointers unchanged + counted_object& operator = (const counted_object&) + { return *this; } // changing the contents leaves pointers unchanged + + ~counted_object() + { assert(m_reference_count == 0); } + +public: + //! Call whenever setting a pointer to the object + void inc_reference() const + { ++m_reference_count; } + + //! Call whenever resetting (i.e. overwriting) a pointer to the object. + //! IMPORTANT: In case of self-assignment, call AFTER inc_reference(). + //! \return if the object has to be deleted (i.e. if it's reference count dropped to zero) + bool dec_reference() const + { return (! --m_reference_count); } + + //! Test if the counted_object is referenced by only one counting_ptr. + bool unique() const + { return (m_reference_count == 1); } + + //! Return the number of references to this object (for debugging) + unsigned_type get_reference_count() const + { return m_reference_count; } +}; + +#if STXXL_HAVE_SYNC_ADD_AND_FETCH || STXXL_MSVC + +/*! + * Provides reference counting abilities for use with counting_ptr with atomics + * operations. + * + * Use as superclass of the actual object, this adds a reference_count + * value. Then either use counting_ptr as pointer to manage references and + * deletion, or just do normal new and delete. + * + * This class does thread-safe increment and decrement using atomic operations + * on an integral type. + */ +class atomic_counted_object +{ +private: + //! the reference count is kept mutable to all const_counting_ptr() to + //! change the reference count. +#if STXXL_MSVC + mutable long m_reference_count; +#else + mutable unsigned_type m_reference_count; +#endif + +public: + //! new objects have zero reference count + atomic_counted_object() + : m_reference_count(0) { } + + //! coping still creates a new object with zero reference count + atomic_counted_object(const atomic_counted_object&) + : m_reference_count(0) { } + + //! assignment operator, leaves pointers unchanged + atomic_counted_object& operator = (const atomic_counted_object&) + { return *this; } // changing the contents leaves pointers unchanged + + ~atomic_counted_object() + { assert(m_reference_count == 0); } + +public: + //! Call whenever setting a pointer to the object + void inc_reference() const + { +#if STXXL_MSVC + _InterlockedIncrement(&m_reference_count); +#else + __sync_add_and_fetch(&m_reference_count, +1); +#endif + } + + //! Call whenever resetting (i.e. overwriting) a pointer to the object. + //! IMPORTANT: In case of self-assignment, call AFTER inc_reference(). + //! \return if the object has to be deleted (i.e. if it's reference count dropped to zero) + bool dec_reference() const + { +#if STXXL_MSVC + return (_InterlockedDecrement(&m_reference_count) == 0); +#else + return (__sync_add_and_fetch(&m_reference_count, -1) == 0); +#endif + } + + //! Test if the counted_object is referenced by only one counting_ptr. + bool unique() const + { + return (m_reference_count == 1); + } + + //! Return the number of references to this object (for debugging) + unsigned_type get_reference_count() const + { + return m_reference_count; + } +}; + +#else // no atomic intrinsics found, use mutexes (slow) + +/*! + * Provides reference counting abilities for use with counting_ptr with mutex + * locking. + * + * Use as superclass of the actual object, this adds a reference_count + * value. Then either use counting_ptr as pointer to manage references and + * deletion, or just do normal new and delete. + * + * This class does thread-safe increment and decrement using scoped locks. A + * faster version of this class is available using atomic operations. + */ +class atomic_counted_object +{ +private: + //! the reference count is kept mutable to all const_counting_ptr() to + //! change the reference count. + mutable unsigned_type m_reference_count; + + //! the mutex used to synchronize access to the reference counter. + mutable mutex m_reference_count_mutex; + +public: + //! new objects have zero reference count + atomic_counted_object() + : m_reference_count(0) { } + + //! coping still creates a new object with zero reference count + atomic_counted_object(const atomic_counted_object&) + : m_reference_count(0) { } + + //! assignment operator, leaves pointers unchanged + atomic_counted_object& operator = (const atomic_counted_object&) + { return *this; } // changing the contents leaves pointers unchanged + + ~atomic_counted_object() + { assert(m_reference_count == 0); } + +public: + //! Call whenever setting a pointer to the object + void inc_reference() const + { + scoped_mutex_lock lock(m_reference_count_mutex); + ++m_reference_count; + } + + //! Call whenever resetting (i.e. overwriting) a pointer to the object. + //! IMPORTANT: In case of self-assignment, call AFTER inc_reference(). + //! \return if the object has to be deleted (i.e. if it's reference count dropped to zero) + bool dec_reference() const + { + scoped_mutex_lock lock(m_reference_count_mutex); + return (--m_reference_count == 0); + } + + //! Test if the counted_object is referenced by only one counting_ptr. + bool unique() const + { + scoped_mutex_lock lock(m_reference_count_mutex); + return (m_reference_count == 1); + } + + //! Return the number of references to this object (for debugging) + unsigned_type get_reference_count() const + { + scoped_mutex_lock lock(m_reference_count_mutex); + return m_reference_count; + } +}; + +#endif + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_COUNTING_PTR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/custom_stats.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/custom_stats.h new file mode 100644 index 0000000000..111753ad30 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/custom_stats.h @@ -0,0 +1,150 @@ +/*************************************************************************** + * include/stxxl/bits/common/custom_stats.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2014 Thomas Keh + * + * 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 STXXL_COMMON_CUSTOM_STATS_HEADER +#define STXXL_COMMON_CUSTOM_STATS_HEADER + +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +/*! + * This class provides a statistical counter that can easily be deactivated + * using a typedef to dummy_custom_stats_counter. It's basically a wrapper for + * a unsigned long long value. + * + * \see dummy_custom_stats_counter + */ +template +class custom_stats_counter +{ +public: + //! The counter's value type + typedef ValueType value_type; + +protected: + //! The counter's value + value_type m_value; + +public: + //! The constructor. Initializes the counter to 0. + custom_stats_counter() + : m_value(0) + { } + + //! Increases the counter by right. + //! \param right The corresponding integer value + custom_stats_counter& operator += (const value_type& right) + { + m_value += right; + return *this; + } + //! Increases the counter by 1 (prefix). + custom_stats_counter& operator ++ () + { + ++m_value; + return *this; + } + //! Increases the counter by 1 (postfix). + custom_stats_counter operator ++ (int) + { + custom_stats_counter copy = *this; + ++m_value; + return copy; + } + //! Assignment operator + //! \param other The corresponding integer value + custom_stats_counter& operator = (const value_type& other) + { + m_value = other; + return *this; + } + /*! + * Set the counter to other if other is larger than the current counter + * value. + * + * \param other The corresponding integer value + */ + void set_max(const value_type& other) + { + m_value = std::max(m_value, other); + } + /*! + * Return the counter value interpreted as a memory amount in IEC units as + * a string. For that purpose the counter value is multiplied with the + * byte_per_element argument. + * + * \param byte_per_element The memory amount per "counter element". + */ + std::string as_memory_amount(const value_type& byte_per_element) const + { + return format_IEC_size(m_value * byte_per_element) + "B"; + } + /*! + * Cast to counter_type: Returns the counter's value as a regular integer + * value. This can be used as a getter as well as for printing with + * std::out. + */ + operator value_type () const + { + return m_value; + } +}; + +/*! + * Dummy class for custom_stats_counter. The methods do nothing. The compiler + * should optimize out the code. + * + * \see custom_stats_counter + */ +template +class dummy_custom_stats_counter +{ +public: + typedef ValueType value_type; + +public: + dummy_custom_stats_counter() { } + dummy_custom_stats_counter& operator += (const value_type&) + { + return *this; + } + dummy_custom_stats_counter& operator ++ () + { + return *this; + } + dummy_custom_stats_counter& operator ++ (int) + { + return *this; + } + dummy_custom_stats_counter& operator = (const value_type&) + { + return *this; + } + void set_max(value_type) { } + std::string as_memory_amount(const value_type&) const + { + return ""; + } + operator value_type () const + { + return value_type(); + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_CUSTOM_STATS_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/error_handling.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/error_handling.h new file mode 100644 index 0000000000..534834f1e9 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/error_handling.h @@ -0,0 +1,159 @@ +/*************************************************************************** + * include/stxxl/bits/common/error_handling.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007-2010 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_ERROR_HANDLING_HEADER +#define STXXL_COMMON_ERROR_HANDLING_HEADER + +/** \file error_handling.h + * Macros for convenient error checking and reporting via exception. + */ + +#include +#include +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +#if STXXL_MSVC + #define STXXL_PRETTY_FUNCTION_NAME __FUNCTION__ +#else + #define STXXL_PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__ +#endif + +//////////////////////////////////////////////////////////////////////////// + +//! Throws exception_type with "Error in [location] : [error_message]" +#define STXXL_THROW2(exception_type, location, error_message) \ + do { \ + std::ostringstream msg; \ + msg << "Error in " << location << " : " << error_message; \ + throw exception_type(msg.str()); \ + } while (false) + +//! Throws exception_type with "Error in [function] : [error_message]" +#define STXXL_THROW(exception_type, error_message) \ + STXXL_THROW2(exception_type, \ + STXXL_PRETTY_FUNCTION_NAME, \ + error_message) + +//! Throws exception_type with "Error in [function] : [error_message] : [errno_value message]" +#define STXXL_THROW_ERRNO2(exception_type, error_message, errno_value) \ + STXXL_THROW2(exception_type, \ + STXXL_PRETTY_FUNCTION_NAME, \ + error_message << " : " << strerror(errno_value)) + +//! Throws exception_type with "Error in [function] : [error_message] : [errno message]" +#define STXXL_THROW_ERRNO(exception_type, error_message) \ + STXXL_THROW_ERRNO2(exception_type, error_message, errno) + +//! Throws std::invalid_argument with "Error in [function] : [error_message]" +#define STXXL_THROW_INVALID_ARGUMENT(error_message) \ + STXXL_THROW2(std::invalid_argument, \ + STXXL_PRETTY_FUNCTION_NAME, \ + error_message) + +//! Throws stxxl::unreachable with "Error in file [file], line [line] : this code should never be reachable" +#define STXXL_THROW_UNREACHABLE() \ + STXXL_THROW2(stxxl::unreachable, \ + "file " << __FILE__ << ", line " << __LINE__, \ + "this code should never be reachable") + +//////////////////////////////////////////////////////////////////////////// + +//! Throws exception_type if (expr) with "Error in [function] : [error_message]" +#define STXXL_THROW_IF(expr, exception_type, error_message) \ + do { \ + if (expr) { \ + STXXL_THROW(exception_type, error_message); \ + } \ + } while (false) + +//! Throws exception_type if (expr != 0) with "Error in [function] : [error_message]" +#define STXXL_THROW_NE_0(expr, exception_type, error_message) \ + STXXL_THROW_IF((expr) != 0, exception_type, error_message) + +//! Throws exception_type if (expr == 0) with "Error in [function] : [error_message]" +#define STXXL_THROW_EQ_0(expr, exception_type, error_message) \ + STXXL_THROW_IF((expr) == 0, exception_type, error_message) + +//! Throws exception_type if (expr < 0) with "Error in [function] : [error_message]" +#define STXXL_THROW_LT_0(expr, exception_type, error_message) \ + STXXL_THROW_IF((expr) < 0, exception_type, error_message) + +//////////////////////////////////////////////////////////////////////////// + +//! Throws exception_type if (expr) with "Error in [function] : [error_message] : [errno message]" +#define STXXL_THROW_ERRNO_IF(expr, exception_type, error_message) \ + do { \ + if (expr) { \ + STXXL_THROW_ERRNO(exception_type, error_message); \ + } \ + } while (false) + +//! Throws exception_type if (expr != 0) with "Error in [function] : [error_message] : [errno message]" +#define STXXL_THROW_ERRNO_NE_0(expr, exception_type, error_message) \ + STXXL_THROW_ERRNO_IF((expr) != 0, exception_type, error_message) + +//! Throws exception_type if (expr == 0) with "Error in [function] : [error_message] : [errno message]" +#define STXXL_THROW_ERRNO_EQ_0(expr, exception_type, error_message) \ + STXXL_THROW_ERRNO_IF((expr) == 0, exception_type, error_message) + +//! Throws exception_type if (expr < 0) with "Error in [function] : [error_message] : [errno message]" +#define STXXL_THROW_ERRNO_LT_0(expr, exception_type, error_message) \ + STXXL_THROW_ERRNO_IF((expr) < 0, exception_type, error_message) + +//////////////////////////////////////////////////////////////////////////// + +//! Checks pthread call, if return != 0, throws stxxl::resource_error with "Error in [function] : [pthread_expr] : [errno message] +#define STXXL_CHECK_PTHREAD_CALL(expr) \ + do { \ + int res = (expr); \ + if (res != 0) { \ + STXXL_THROW_ERRNO2(stxxl::resource_error, #expr, res); \ + } \ + } while (false) + +//////////////////////////////////////////////////////////////////////////// + +#if STXXL_WINDOWS || defined(__MINGW32__) + +//! Throws exception_type with "Error in [function] : [error_message] : [formatted GetLastError()]" +#define STXXL_THROW_WIN_LASTERROR(exception_type, error_message) \ + do { \ + LPVOID lpMsgBuf; \ + DWORD dw = GetLastError(); \ + FormatMessage( \ + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, \ + NULL, dw, \ + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \ + (LPTSTR)&lpMsgBuf, \ + 0, NULL); \ + std::ostringstream msg; \ + msg << "Error in " << STXXL_PRETTY_FUNCTION_NAME \ + << " : " << error_message \ + << " : error code " << dw << " : " << ((char*)lpMsgBuf); \ + LocalFree(lpMsgBuf); \ + throw exception_type(msg.str()); \ + } while (false) + +#endif + +//////////////////////////////////////////////////////////////////////////// + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_ERROR_HANDLING_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exceptions.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exceptions.h new file mode 100644 index 0000000000..72d8f0c18a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exceptions.h @@ -0,0 +1,88 @@ +/*************************************************************************** + * include/stxxl/bits/common/exceptions.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_COMMON_EXCEPTIONS_HEADER +#define STXXL_COMMON_EXCEPTIONS_HEADER + +#include +#include +#include + +#include + +STXXL_BEGIN_NAMESPACE + +class io_error : public std::ios_base::failure +{ +public: + io_error() throw () + : std::ios_base::failure("") + { } + + io_error(const std::string& message) throw () + : std::ios_base::failure(message) + { } +}; + +class resource_error : public std::runtime_error +{ +public: + resource_error() throw () + : std::runtime_error("") + { } + + resource_error(const std::string& message) throw () + : std::runtime_error(message) + { } +}; + +class bad_ext_alloc : public std::runtime_error +{ +public: + bad_ext_alloc() throw () + : std::runtime_error("") + { } + + bad_ext_alloc(const std::string& message) throw () + : std::runtime_error(message) + { } +}; + +class bad_parameter : public std::runtime_error +{ +public: + bad_parameter() throw () + : std::runtime_error("") + { } + + bad_parameter(const std::string& message) throw () + : std::runtime_error(message) + { } +}; + +class unreachable : public std::runtime_error +{ +public: + unreachable() throw () + : std::runtime_error("") + { } + + unreachable(const std::string& message) throw () + : std::runtime_error(message) + { } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_EXCEPTIONS_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exithandler.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exithandler.h new file mode 100644 index 0000000000..d85f2eb8ee --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/exithandler.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * include/stxxl/bits/common/exithandler.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_COMMON_EXITHANDLER_HEADER +#define STXXL_COMMON_EXITHANDLER_HEADER + +#include + +STXXL_BEGIN_NAMESPACE + +// There are several possibilities for the exit handlers. To use the default +// implementation (which uses atexit()), nothing special has to be done. +// +// To work around problems with atexit() being used in a dll you may #define +// STXXL_NON_DEFAULT_EXIT_HANDLER at library compilation time. In this case +// the library/application should call stxxl::run_exit_handlers() during +// shutdown. +// +// To provide your own exit handler implementation, #define +// STXXL_EXTERNAL_EXIT_HANDLER and implement stxxl::register_exit_handler(void +// (*)(void)) and stxxl::run_exit_handlers() in your application. + +int register_exit_handler(void (* function)(void)); +void run_exit_handlers(); + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_EXITHANDLER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/external_shared_ptr.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/external_shared_ptr.h new file mode 100644 index 0000000000..d8e6594fee --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/external_shared_ptr.h @@ -0,0 +1,119 @@ +/*************************************************************************** + * include/stxxl/bits/common/external_shared_ptr.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2011 Daniel Godas-Lopez + * + * 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 STXXL_COMMON_EXTERNAL_SHARED_PTR_HEADER +#define STXXL_COMMON_EXTERNAL_SHARED_PTR_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup support +//! \{ + +/*! + * This class takes a shared pointer, increments its reference count and wraps + * it in a way that the resulting object can be copied, dumped to disk, and + * destroyed without affecting the refcount. When the object is retrieved from + * disk and recreated on internal memory, it will still hold a reference to the + * same memory block and can be used right away by calling the "get" method or + * unwrapped with the "unwrap" method to decrement the refcount. + * + * In the context of this template, a shared pointer is an object of a class P + * that fulfills the following requirements: + * + * - Can be copy-constructed + * - Has an assignment operator (so that the get method can be used) + * - Contains a pointer to a reference count stored outside the class + * - Increments the reference count on copy-construction + * - Decrements the reference count on destruction + * + * Both the Boost and c++0x implementations of shared_ptr fulfill these + * requirements. At the moment of writing the author is not aware of any + * implementations of shared pointers that can't be used with this wrapper. + */ +template +class external_shared_ptr +{ +private: + /*! + * We store the pointer like this so that the refcount does not get + * incremented when the wrapper is copy-constructed, or decremented when + * the wrapper is destroyed. + * + * The whole external_shared_ptr object will be aligned by the compiler to + * a multiple of its size. The size of the object is sizeof(P) as the + * buffer is its only member. The buffer is placed in the class at offset 0 + * so the alignment of the stored P should be alright without any + * additional hints. + */ + char data[sizeof(P)]; + +public: + /*! + * This constructor needs to be defined so that the [] operator in maps and + * hash tables works. If unwrap() or get() are called for an object + * constructed this way the behavior is undefined. + */ + external_shared_ptr() + { } + + /*! + * Copy the pointer to internal storage and increment the refcount (the + * destructor never gets called). + */ + external_shared_ptr(P ptr) + { + new (data)P(ptr); + } + + /*! + * Call the destructor to decrement the refcount. If this is called more + * than once the results are undefined. + */ + void unwrap() + { + P* p = reinterpret_cast((void*)data); + p->~P(); + } + + /*! + * If this is called after unwrap() the behaviour is undefined. + */ + P get() const + { + P* p = reinterpret_cast((void*)data); + return *p; + } + + bool operator == (const external_shared_ptr& x) const + { + P* p1 = reinterpret_cast((void*)data); + P* p2 = reinterpret_cast((void*)x.data); + + return *p1 == *p2; + } + + //! Output contained data items + friend std::ostream& + operator << (std::ostream& os, const external_shared_ptr& p) + { + return os << p.get(); + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_EXTERNAL_SHARED_PTR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_heap.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_heap.h new file mode 100644 index 0000000000..5d0f183f61 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_heap.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * include/stxxl/bits/common/is_heap.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2015 Timo Bingmann + * + * 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 STXXL_COMMON_IS_HEAP_HEADER +#define STXXL_COMMON_IS_HEAP_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +bool is_heap( + RandomAccessIterator first, RandomAccessIterator last, + StrictWeakOrdering comp = + std::less + ::value_type>()) +{ + typedef typename std::iterator_traits + ::difference_type diff_type; + + if (first == last) return true; + + RandomAccessIterator parent = first; + + diff_type num = 1; + for (RandomAccessIterator child = first + 1; child != last; ++child, ++num) + { + if (comp(*parent, *child)) // parent < child -> max-heap violated + return false; + + if ((num & 1) == 0) + ++parent; + } + + return true; +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_IS_HEAP_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_sorted.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_sorted.h new file mode 100644 index 0000000000..c8bc78905e --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/is_sorted.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * include/stxxl/bits/common/is_sorted.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_COMMON_IS_SORTED_HEADER +#define STXXL_COMMON_IS_SORTED_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +bool is_sorted(ForwardIterator first, ForwardIterator last, + StrictWeakOrdering comp) +{ + if (first == last) + return true; + + ForwardIterator next = first; + for (++next; next != last; ++first, ++next) { + if (comp(*next, *first)) + return false; + } + + return true; +} + +template +bool is_sorted(ForwardIterator first, ForwardIterator last) +{ + return stxxl::is_sorted( + first, last, + std::less + ::value_type>()); +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_IS_SORTED_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/log.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/log.h new file mode 100644 index 0000000000..c57e46d615 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/log.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * include/stxxl/bits/common/log.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2004-2005 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_COMMON_LOG_HEADER +#define STXXL_COMMON_LOG_HEADER + +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +class logger : public singleton +{ + friend class singleton; + + std::ofstream log_stream_; + std::ofstream errlog_stream_; + std::ofstream* waitlog_stream_; + + logger(); + ~logger(); + +public: + inline std::ofstream & log_stream() + { + return log_stream_; + } + + inline std::ofstream & errlog_stream() + { + return errlog_stream_; + } + + inline std::ofstream * waitlog_stream() + { + return waitlog_stream_; + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_LOG_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/mutex.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/mutex.h new file mode 100644 index 0000000000..615f7756a5 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/mutex.h @@ -0,0 +1,203 @@ +/*************************************************************************** + * include/stxxl/bits/common/mutex.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_MUTEX_HEADER +#define STXXL_COMMON_MUTEX_HEADER + +#include +#include + +#if STXXL_STD_THREADS && STXXL_WINDOWS && STXXL_MSVC >= 1700 +#include +#endif + +#if STXXL_STD_THREADS + #include +#elif STXXL_BOOST_THREADS + #include +#elif STXXL_POSIX_THREADS + #include + + #include + #include +#else + #error "Thread implementation not detected." +#endif + +STXXL_BEGIN_NAMESPACE + +#if STXXL_STD_THREADS + +typedef std::mutex mutex; + +#elif STXXL_BOOST_THREADS + +typedef boost::mutex mutex; + +#elif STXXL_POSIX_THREADS + +class mutex : private noncopyable +{ + //! mutex handle + pthread_mutex_t m_mutex; + +public: + //! construct unlocked mutex + mutex() + { + STXXL_CHECK_PTHREAD_CALL(pthread_mutex_init(&m_mutex, NULL)); + } + //! destroy mutex handle + ~mutex() noexcept(false) + { + // try simple delete first + int res = pthread_mutex_destroy(&m_mutex); + if (res == 0) return; + + // try to lock and unlock mutex + res = pthread_mutex_trylock(&m_mutex); + + if (res == 0 || res == EBUSY) { + STXXL_CHECK_PTHREAD_CALL(pthread_mutex_unlock(&m_mutex)); + } + else { + STXXL_THROW_ERRNO2(resource_error, "pthread_mutex_trylock() failed", res); + } + + STXXL_CHECK_PTHREAD_CALL(pthread_mutex_destroy(&m_mutex)); + } + //! lock mutex, may block + void lock() + { + STXXL_CHECK_PTHREAD_CALL(pthread_mutex_lock(&m_mutex)); + } + //! unlock mutex + void unlock() + { + STXXL_CHECK_PTHREAD_CALL(pthread_mutex_unlock(&m_mutex)); + } + //! return platform specific handle + pthread_mutex_t & native_handle() + { + return m_mutex; + } +}; + +#endif // STXXL_POSIX_THREADS + +#if STXXL_STD_THREADS && STXXL_WINDOWS && STXXL_MSVC >= 1700 + +class spin_lock; +typedef spin_lock fastmutex; + +#else + +typedef mutex fastmutex; + +#endif + +#if STXXL_STD_THREADS + +typedef std::unique_lock scoped_mutex_lock; +typedef std::unique_lock scoped_fast_mutex_lock; + +#elif STXXL_BOOST_THREADS + +typedef boost::mutex::scoped_lock scoped_mutex_lock; +typedef boost::mutex::scoped_lock scoped_fast_mutex_lock; + +#else + +//! Aquire a lock that's valid until the end of scope. +class scoped_mutex_lock +{ + //! mutex reference + mutex& m_mutex; + + //! marker if already unlocked by this thread (needs no synchronization) + bool is_locked; + +public: + //! lock mutex + scoped_mutex_lock(mutex& m) + : m_mutex(m), is_locked(true) + { + m_mutex.lock(); + } + //! unlock mutex hold when object goes out of scope. + ~scoped_mutex_lock() + { + unlock(); + } + //! unlock mutex hold prematurely + void unlock() + { + if (is_locked) { + is_locked = false; + m_mutex.unlock(); + } + } + //! return platform specific handle + pthread_mutex_t & native_handle() + { + return m_mutex.native_handle(); + } +}; + +typedef scoped_mutex_lock scoped_fast_mutex_lock; + +#endif + +#if STXXL_STD_THREADS && STXXL_WINDOWS && STXXL_MSVC >= 1700 + +class spin_lock +{ +public: +#if STXXL_MSVC < 1800 + spin_lock() + { + lck.clear(std::memory_order_release); + } +#else + spin_lock() + { } +#endif + + void lock() + { + while (lck.test_and_set(std::memory_order_acquire)) + { } + } + + void unlock() + { + lck.clear(std::memory_order_release); + } + +private: +#if STXXL_MSVC >= 1800 + std::atomic_flag lck = ATOMIC_FLAG_INIT; + spin_lock(const spin_lock&) = delete; + spin_lock& operator = (const spin_lock&) = delete; +#else + std::atomic_flag lck; + spin_lock(const spin_lock&); + spin_lock& operator = (const spin_lock&); +#endif +}; + +#endif +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_MUTEX_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/new_alloc.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/new_alloc.h new file mode 100644 index 0000000000..2d2df7f504 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/new_alloc.h @@ -0,0 +1,143 @@ +/*************************************************************************** + * include/stxxl/bits/common/new_alloc.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2006 Roman Dementiev + * Copyright (C) 2007, 2008, 2010 Andreas Beckmann + * + * 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 STXXL_COMMON_NEW_ALLOC_HEADER +#define STXXL_COMMON_NEW_ALLOC_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +class new_alloc; + +template +struct new_alloc_rebind; + +template +struct new_alloc_rebind{ + typedef new_alloc other; +}; + +template +struct new_alloc_rebind { + typedef std::allocator other; +}; + +// designed for typed_block (to use with std::vector) +template +class new_alloc +{ +public: + // type definitions + typedef Type value_type; + typedef Type* pointer; + typedef const Type* const_pointer; + typedef Type& reference; + typedef const Type& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + // rebind allocator to type Rebind, use new_alloc only if Rebind == Type + template + struct rebind { + typedef typename new_alloc_rebind::other other; + }; + + // return address of values + pointer address(reference value) const + { + return &value; + } + const_pointer address(const_reference value) const + { + return &value; + } + + new_alloc() throw () { } + new_alloc(const new_alloc&) throw () { } + template + new_alloc(const new_alloc&) throw () { } + ~new_alloc() throw () { } + + template + operator std::allocator() + { + static std::allocator helper_allocator; + return helper_allocator; + } + + // return maximum number of elements that can be allocated + size_type max_size() const throw () + { + return std::numeric_limits::max() / sizeof(Type); + } + + // allocate but don't initialize num elements of type Type + pointer allocate(size_type num, const void* = 0) + { + return static_cast(Type::operator new (num * sizeof(Type))); + } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 402. wrong new expression in [some_] allocator::construct + // initialize elements of allocated storage p with value value + void construct(pointer p, const Type& value) + { + // initialize memory with placement new + ::new ((void*)p)Type(value); + } + +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + template + void construct(pointer p, Args&& ... args) + { + ::new ((void*)p)Type(std::forward(args) ...); + } +#endif + + // destroy elements of initialized storage p + void destroy(pointer p) + { + // destroy objects by calling their destructor + p->~Type(); + } + + // deallocate storage p of deleted elements + void deallocate(pointer p, size_type /*num*/) + { + Type::operator delete (p); + } +}; + +// return that all specializations of this allocator are interchangeable +template +inline bool operator == (const new_alloc&, + const new_alloc&) throw () +{ + return true; +} + +template +inline bool operator != (const new_alloc&, + const new_alloc&) throw () +{ + return false; +} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_NEW_ALLOC_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/onoff_switch.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/onoff_switch.h new file mode 100644 index 0000000000..2d87fa357c --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/onoff_switch.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * include/stxxl/bits/common/onoff_switch.h + * + * Kind of binary semaphore: initially OFF, then multiple waiters can attach + * to the switch, which get notified one-by-one when switched ON. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_ONOFF_SWITCH_HEADER +#define STXXL_COMMON_ONOFF_SWITCH_HEADER + +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +class onoff_switch : private noncopyable +{ + //! mutex for condition variable + mutex m_mutex; + + //! condition variable + condition_variable m_cond; + + //! the switch's state + bool m_on; + +public: + //! construct switch + onoff_switch(bool flag = false) + : m_on(flag) + { } + //! turn switch ON and notify one waiter + void on() + { + scoped_mutex_lock lock(m_mutex); + m_on = true; + lock.unlock(); + m_cond.notify_one(); + } + //! turn switch OFF and notify one waiter + void off() + { + scoped_mutex_lock lock(m_mutex); + m_on = false; + lock.unlock(); + m_cond.notify_one(); + } + //! wait for switch to turn ON + void wait_for_on() + { + scoped_mutex_lock lock(m_mutex); + if (!m_on) + m_cond.wait(lock); + } + //! wait for switch to turn OFF + void wait_for_off() + { + scoped_mutex_lock lock(m_mutex); + if (m_on) + m_cond.wait(lock); + } + //! return true if switch is ON + bool is_on() + { + scoped_mutex_lock lock(m_mutex); + return m_on; + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_ONOFF_SWITCH_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/rand.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/rand.h new file mode 100644 index 0000000000..ca3d1ba0bd --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/rand.h @@ -0,0 +1,292 @@ +/*************************************************************************** + * include/stxxl/bits/common/rand.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002, 2003, 2005 Roman Dementiev + * Copyright (C) 2007 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_RAND_HEADER +#define STXXL_COMMON_RAND_HEADER + +#include + +#include +#include +#include +#include + +#if STXXL_STD_RANDOM + #include +#elif STXXL_BOOST_RANDOM + #include +#endif + +// Recommended seeding procedure: +// by default, the global seed is initialized from a high resolution timer and the process id +// 1. stxxl::set_seed(seed); // optionally, do this if you wan't to us a specific seed to replay a certain program run +// 2. seed = stxxl::get_next_seed(); // store/print/... this value can be used for step 1 to replay the program with a specific seed +// 3. stxxl::srandom_number32(); // seed the global state of stxxl::random_number32 +// 4. create all the other prngs used. + +STXXL_BEGIN_NAMESPACE + +extern unsigned ran32State; + +//! \addtogroup support +//! \{ + +//! Fast uniform [0, 2^32) pseudo-random generator with period 2^32, random +//! bits: 32. +//! \warning Uses a global state and is not reentrant or thread-safe! +struct random_number32 +{ + typedef unsigned value_type; + + //! Returns a random number from [0, 2^32) + inline value_type operator () () const + { + return (ran32State = 1664525 * ran32State + 1013904223); + } + + //! Returns a random number from [0, N) + inline value_type operator () (const value_type& N) const + { + return operator () () % N; + } +}; + +//! Set a seed value for \c random_number32. +inline void srandom_number32(unsigned seed = get_next_seed()) +{ + ran32State = seed; +} + +//! Fast uniform [0, 2^32) pseudo-random generator with period 2^32, random +//! bits: 32. +//! Reentrant variant of random_number32 that keeps it's private state. +struct random_number32_r +{ + typedef unsigned value_type; + mutable unsigned state; + + random_number32_r(unsigned seed = get_next_seed()) + { + state = seed; + } + + //! Change the current seed + void set_seed(unsigned seed) + { + state = seed; + } + + //! Returns a random number from [0, 2^32) + inline value_type operator () () const + { + return (state = 1664525 * state + 1013904223); + } +}; + +//! Fast uniform [0, 255] pseudo-random generator with period 2^8, random bits: +//! 8 (one byte). +class random_number8_r +{ + random_number32_r m_rnd32; + uint32 m_value; + unsigned int m_pos; + +public: + typedef uint8 value_type; + + random_number8_r(unsigned seed = get_next_seed()) + : m_rnd32(seed), m_pos(4) + { } + + //! Returns a random byte from [0, 255] + inline value_type operator () () + { + if (++m_pos >= 4) { + m_value = m_rnd32(); + m_pos = 0; + } + return ((uint8*)&m_value)[m_pos]; + } +}; + +//! Fast uniform [0.0, 1.0) pseudo-random generator +//! \warning Uses a global state and is not reentrant or thread-safe! +struct random_uniform_fast +{ + typedef double value_type; + random_number32 rnd32; + + random_uniform_fast(unsigned /*seed*/ = get_next_seed()) + { } + + //! Returns a random number from [0.0, 1.0) + inline value_type operator () () const + { + return (double(rnd32()) * (0.5 / 0x80000000)); + } +}; + +#if STXXL_MSVC +#pragma warning(push) +#pragma warning(disable:4512) // assignment operator could not be generated +#endif + +//! Slow and precise uniform [0.0, 1.0) pseudo-random generator +//! period: at least 2^48, random bits: at least 31 +//! +//! \warning Seed is not the same as in the fast generator \c random_uniform_fast +struct random_uniform_slow +{ + typedef double value_type; +#if STXXL_STD_RANDOM + typedef std::default_random_engine gen_type; + mutable gen_type gen; + typedef std::uniform_real_distribution<> uni_type; + mutable uni_type uni; + + random_uniform_slow(unsigned seed = get_next_seed()) + : gen(seed), uni(0.0, 1.0) + { } +#elif STXXL_BOOST_RANDOM + typedef boost::minstd_rand base_generator_type; + base_generator_type generator; + boost::uniform_real<> uni_dist; + mutable boost::variate_generator > uni; + + random_uniform_slow(unsigned seed = get_next_seed()) + : uni(generator, uni_dist) + { + uni.engine().seed(seed); + } +#else + mutable unsigned short state48[3]; +/* + * embedded erand48.c + * + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + static void + _dorand48(unsigned short xseed[3]) + { + unsigned long accu; + unsigned short temp[2]; + + static const unsigned short _mult[3] = { 0xe66d, 0xdeec, 0x0005 }; + static const unsigned short _add = 0x000b; + + accu = (unsigned long)_mult[0] * (unsigned long)xseed[0] + + (unsigned long)_add; + temp[0] = (unsigned short)accu; /* lower 16 bits */ + accu >>= sizeof(unsigned short) * 8; + accu += (unsigned long)_mult[0] * (unsigned long)xseed[1] + + (unsigned long)_mult[1] * (unsigned long)xseed[0]; + temp[1] = (unsigned short)accu; /* middle 16 bits */ + accu >>= sizeof(unsigned short) * 8; + accu += _mult[0] * xseed[2] + _mult[1] * xseed[1] + _mult[2] * xseed[0]; + xseed[0] = temp[0]; + xseed[1] = temp[1]; + xseed[2] = (unsigned short)accu; + } + + static double + _erand48(unsigned short xseed[3]) + { + _dorand48(xseed); + return ldexp((double)xseed[0], -48) + + ldexp((double)xseed[1], -32) + + ldexp((double)xseed[2], -16); + } +/* end erand48.c */ + + random_uniform_slow(unsigned seed = get_next_seed()) + { + state48[0] = (unsigned short)(seed & 0xffff); + state48[1] = (unsigned short)(seed >> 16); + state48[2] = 42; + _dorand48(state48); + } +#endif + + //! Returns a random number from [0.0, 1.0) + inline value_type operator () () const + { +#if STXXL_STD_RANDOM + return uni(gen); +#elif STXXL_BOOST_RANDOM + return uni(); +#else + return _erand48(state48); +#endif + } +}; + +//! Uniform [0, N) pseudo-random generator +template +struct random_number +{ + typedef unsigned value_type; + UniformRGen uniform; + + random_number(unsigned seed = get_next_seed()) + : uniform(seed) + { } + + //! Returns a random number from [0, N) + inline value_type operator () (value_type N) const + { + return static_cast(uniform() * double(N)); + } +}; + +//! Slow and precise uniform [0, 2^64) pseudo-random generator +struct random_number64 +{ + typedef stxxl::uint64 value_type; + random_uniform_slow uniform; + + random_number64(unsigned seed = get_next_seed()) + : uniform(seed) + { } + + //! Returns a random number from [0, 2^64) + inline value_type operator () () const + { + return static_cast(uniform() * (18446744073709551616.)); + } + + //! Returns a random number from [0, N) + inline value_type operator () (value_type N) const + { + return static_cast(uniform() * double(N)); + } +}; + +#if STXXL_MSVC +#pragma warning(pop) // assignment operator could not be generated +#endif + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_RAND_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/seed.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/seed.h new file mode 100644 index 0000000000..48a6f52ceb --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/seed.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * include/stxxl/bits/common/seed.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Andreas Beckmann + * + * 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 STXXL_COMMON_SEED_HEADER +#define STXXL_COMMON_SEED_HEADER + +#include + +STXXL_BEGIN_NAMESPACE + +//! set the global stxxl seed value +void set_seed(unsigned seed); + +//! get a seed value for prng initialization, subsequent calls return a +//! sequence of different values +unsigned get_next_seed(); + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_SEED_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/semaphore.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/semaphore.h new file mode 100644 index 0000000000..4010c41c6f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/semaphore.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * include/stxxl/bits/common/semaphore.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_SEMAPHORE_HEADER +#define STXXL_COMMON_SEMAPHORE_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +class semaphore : private noncopyable +{ + //! value of the semaphore + int v; + + //! mutex for condition variable + mutex m_mutex; + + //! condition variable + condition_variable m_cond; + +public: + //! construct semaphore + semaphore(int init_value = 1) + : v(init_value) + { } + //! function increments the semaphore and signals any threads that are + //! blocked waiting a change in the semaphore + int operator ++ (int) + { + scoped_mutex_lock lock(m_mutex); + int res = ++v; + lock.unlock(); + m_cond.notify_one(); + return res; + } + //! function decrements the semaphore and blocks if the semaphore is <= 0 + //! until another thread signals a change + int operator -- (int) + { + scoped_mutex_lock lock(m_mutex); + while (v <= 0) + m_cond.wait(lock); + + return --v; + } + //! function does NOT block but simply decrements the semaphore should not + //! be used instead of down -- only for programs where multiple threads + //! must up on a semaphore before another thread can go down, i.e., allows + //! programmer to set the semaphore to a negative value prior to using it + //! for synchronization. + int decrement() + { + scoped_mutex_lock lock(m_mutex); + return --v; + } +#if 0 + //! function returns the value of the semaphore at the time the + //! critical section is accessed. obviously the value is not guaranteed + //! after the function unlocks the critical section. + int get_value() + { + scoped_mutex_lock lock(m_mutex); + return v; + } +#endif +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_SEMAPHORE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/settings.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/settings.h new file mode 100644 index 0000000000..24eebc1ea1 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/settings.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * include/stxxl/bits/common/settings.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * + * 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 STXXL_COMMON_SETTINGS_HEADER +#define STXXL_COMMON_SETTINGS_HEADER + +/*! + * @file stxxl/bits/common/settings.h + * Provides a static class to store runtime tuning parameters. + */ + +#include + +STXXL_BEGIN_NAMESPACE + +template +class settings +{ +public: + static bool native_merge; +}; + +template +bool settings::native_merge = false; + +typedef settings<> SETTINGS; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_SETTINGS_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/simple_vector.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/simple_vector.h new file mode 100644 index 0000000000..c3ec901246 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/simple_vector.h @@ -0,0 +1,181 @@ +/*************************************************************************** + * include/stxxl/bits/common/simple_vector.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008, 2011 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_SIMPLE_VECTOR_HEADER +#define STXXL_COMMON_SIMPLE_VECTOR_HEADER + +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup support +//! \{ + +/*! + * Simpler non-growing vector without initialization. + * + * simple_vector can be used a replacement for std::vector when only a + * non-growing array is needed. The advantages of simple_vector are that it + * does not initilize memory for POD types (faster), allows simpler inlines and + * is less error prone to copying and other problems.. + */ +template +class simple_vector : private noncopyable +{ +public: + typedef ValueType value_type; + typedef size_t size_type; + +protected: + //! size of allocated memory + size_type m_size; + + //! pointer to allocated memory area + value_type* m_array; + +public: + // *** simple pointer iterators + + typedef value_type* iterator; + typedef const value_type* const_iterator; + typedef value_type& reference; + typedef const value_type& const_reference; + +public: + //! allocate empty simple vector + simple_vector() + : m_size(0), m_array(NULL) + { } + //! allocate vector's memory + simple_vector(size_type sz) + : m_size(sz), m_array(NULL) + { + if (m_size > 0) + m_array = new value_type[m_size]; + } + //! swap vector with another one + void swap(simple_vector& obj) + { + std::swap(m_size, obj.m_size); + std::swap(m_array, obj.m_array); + } + //! delete vector + ~simple_vector() + { + delete[] m_array; + } + //! return iterator to beginning of vector + iterator data() + { + return m_array; + } + //! return iterator to beginning of vector + const_iterator data() const + { + return m_array; + } + //! return mutable iterator to first element + iterator begin() + { + return m_array; + } + //! return constant iterator to first element + const_iterator begin() const + { + return m_array; + } + //! return constant iterator to first element + const_iterator cbegin() const + { + return begin(); + } + //! return mutable iterator beyond last element + iterator end() + { + return m_array + m_size; + } + //! return constant iterator beyond last element + const_iterator end() const + { + return m_array + m_size; + } + //! return constant iterator beyond last element + const_iterator cend() const + { + return end(); + } + //! return number of items in vector + size_type size() const + { + return m_size; + } + //! return the i-th position of the vector + reference operator [] (size_type i) + { + assert(i < m_size); + return *(begin() + i); + } + //! return constant reference to the i-th position of the vector + const_reference operator [] (size_type i) const + { + assert(i < m_size); + return *(begin() + i); + } + //! resize the array to contain exactly newsize items + void resize(size_type newsize) + { + if (m_array) + { + STXXL_MSG("Warning: resizing non-empty simple_vector"); + value_type* tmp = m_array; + m_array = new value_type[newsize]; + memcpy((void*)m_array, (void*)tmp, + sizeof(value_type) * STXXL_MIN(m_size, newsize)); + delete[] tmp; + m_size = newsize; + } + else + { + m_array = new value_type[newsize]; + m_size = newsize; + } + } + //! Zero the whole array content. + void memzero() + { + memset(m_array, 0, m_size * sizeof(value_type)); + } +}; + +// \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::simple_vector& a, + stxxl::simple_vector& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_COMMON_SIMPLE_VECTOR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/state.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/state.h new file mode 100644 index 0000000000..a8195161ab --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/state.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * include/stxxl/bits/common/state.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_STATE_HEADER +#define STXXL_COMMON_STATE_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +class state : private noncopyable +{ + typedef ValueType value_type; + + //! mutex for condition variable + mutex m_mutex; + + //! condition variable + condition_variable m_cond; + + //! current state + value_type m_state; + +public: + state(const value_type& s) + : m_state(s) + { } + + void set_to(const value_type& new_state) + { + scoped_mutex_lock lock(m_mutex); + m_state = new_state; + lock.unlock(); + m_cond.notify_all(); + } + + void wait_for(const value_type& needed_state) + { + scoped_mutex_lock lock(m_mutex); + while (needed_state != m_state) + m_cond.wait(lock); + } + + value_type operator () () + { + scoped_mutex_lock lock(m_mutex); + return m_state; + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_STATE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/swap_vector.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/swap_vector.h new file mode 100644 index 0000000000..7bf7e033a6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/swap_vector.h @@ -0,0 +1,298 @@ +/*************************************************************************** + * include/stxxl/bits/common/swap_vector.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2014 Thomas Keh + * + * 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 STXXL_COMMON_SWAP_VECTOR_HEADER +#define STXXL_COMMON_SWAP_VECTOR_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup support +//! \{ + +/*! + * Vector that avoids copying of ValueType objects in push_back() (here: + * swap_back()) and resize() operations. Values are swapped with + * default-constructed instances instead. Important: A template spezialization + * for std::swap(ValueType&,ValueType&) must be provided. Make shure the swap + * implementation is located above these lines. + */ +template +class swap_vector : private noncopyable +{ +public: + typedef ValueType value_type; + typedef size_t size_type; + +protected: + //! size of vector + size_type m_size; + + //! size of allocated memory + size_type m_capacity; + + //! pointer to allocated memory area + value_type* m_array; + +public: + // *** simple pointer iterators + + typedef value_type* iterator; + typedef const value_type* const_iterator; + typedef value_type& reference; + typedef const value_type& const_reference; + +public: + //! Create an empty vector. + swap_vector() + : m_size(0), m_capacity(0), m_array(NULL) + { } + //! Create a vector with the spezified size. + swap_vector(size_type size) + : m_size(size), m_capacity(size), m_array(NULL) + { + if (m_size > 0) + m_array = new value_type[m_size]; + } + //! Create a vector with the spezified size and reserve (possibly more) + //! space. + swap_vector(size_type size, size_type capacity) + : m_size(size), m_capacity(std::max(size, capacity)), m_array(NULL) + { + if (m_capacity > 0) + m_array = new value_type[m_capacity]; + } + //! Swap the vector with another one. + void swap(swap_vector& obj) + { + using std::swap; + swap(m_size, obj.m_size); + swap(m_capacity, obj.m_capacity); + swap(m_array, obj.m_array); + } + //! Delete the vector. + ~swap_vector() + { + delete[] m_array; + } + //! Return the vector size. + size_type size() const + { + return m_size; + } + //! Return the vector size. + bool empty() const + { + return (m_size == 0); + } + //! Return the size of the underlaying array. + size_type capacity() const + { + return m_capacity; + } + //! Return iterator to the beginning of vector. + iterator data() + { + return m_array; + } + //! Return iterator to the beginning of vector. + const_iterator data() const + { + return m_array; + } + //! Return mutable iterator to the first element. + iterator begin() + { + return m_array; + } + //! Return constant iterator to the first element. + const_iterator begin() const + { + return m_array; + } + //! Return constant iterator to the first element. + const_iterator cbegin() const + { + return begin(); + } + //! Return mutable iterator beyond the last element. + iterator end() + { + return m_array + m_size; + } + //! Return constant iterator beyond the last element. + const_iterator end() const + { + return m_array + m_size; + } + //! Return constant iterator beyond the last element. + const_iterator cend() const + { + return end(); + } + //! Return the i-th position of the vector. + reference operator [] (size_type i) + { + assert(i < m_size); + return *(begin() + i); + } + //! Return constant reference to the i-th position of the vector. + const_reference operator [] (size_type i) const + { + assert(i < m_size); + return *(begin() + i); + } + //! Return reference to first element. + reference front() + { + assert(m_size > 0); + return *(m_array); + } + //! Return constant reference to first element. + const_reference front() const + { + assert(m_size > 0); + return *(m_array); + } + //! Return reference to last element. + reference back() + { + assert(m_size > 0); + return *(m_array + m_size - 1); + } + //! Return reference to last element. + const_reference back() const + { + assert(m_size > 0); + return *(m_array + m_size - 1); + } + //! Resize the underlaying array to contain at least newsize items. + void reserve(size_type newsize) + { + if (newsize > m_capacity) + { + if (m_array) + { + value_type* tmp = m_array; + m_array = new value_type[newsize]; + for (unsigned i = 0; i < m_size; ++i) + { + using std::swap; + swap(tmp[i], m_array[i]); + } + delete[] tmp; + } + else + { + m_array = new value_type[newsize]; + } + m_capacity = newsize; + } + } + //! Resize the vector to contain at least newsize items. + void resize(size_type newsize) + { + reserve(newsize); + m_size = newsize; + } + //! Create a new value_type object at the end of the vector and then swap + //! it with val. + void swap_back(reference val) + { + if (m_size + 1 > m_capacity) + { + reserve(std::max((size_type)2, 2 * m_capacity)); + } + using std::swap; + swap(m_array[m_size], val); + ++m_size; + } + //! Clear the vector. The capacity doesn't change. + void clear() + { + m_size = 0; + } + //! Erase the element at the given position by swapping it to the and and + //! then reducing the vector size. + iterator erase(iterator position) + { + return erase(position, position + 1); + } + //! Erase the elements at in the range [begin,last) by swapping them to the + //! and and then reducing the vector size. + iterator erase(iterator first, iterator last) + { + assert(first >= begin()); + assert(last <= end()); + if (last < m_array + m_size - 1) + { + // iteratively swap forward the elements behind last + iterator f = first; + iterator l = last; + while (l < end()) + { + using std::swap; + swap(*f, *l); + ++f; + ++l; + } + } + m_size -= (last - first); + return first; + } +}; + +/*! + * Transforms the range [first,last) into a range with all the elements for + * which pred returns true removed, and returns an iterator to the new end of + * that range. + * + * The function is compatible to std::remove_if, but uses std::swap instead of + * copy-assignment (resp. move-assignment in C++11). + */ +template +ForwardIterator swap_remove_if(ForwardIterator first, ForwardIterator last, UnaryPredicate pred) +{ + ForwardIterator result = first; + while (first != last) + { + if (!pred(*first)) + { + using std::swap; + swap(*first, *result); + ++result; + } + ++first; + } + return result; +} + +// \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::swap_vector& a, + stxxl::swap_vector& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_COMMON_SWAP_VECTOR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/timer.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/timer.h new file mode 100644 index 0000000000..4d04929ea0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/timer.h @@ -0,0 +1,280 @@ +/*************************************************************************** + * include/stxxl/bits/common/timer.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002, 2005 Roman Dementiev + * Copyright (C) 2007-2009 Andreas Beckmann + * Copyright (C) 2008 Johannes Singler + * Copyright (C) 2013-2014 Timo Bingmann + * + * 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 STXXL_COMMON_TIMER_HEADER +#define STXXL_COMMON_TIMER_HEADER + +#include +#include +#include +#include + +#if STXXL_BOOST_TIMESTAMP + #include + #include +#elif STXXL_WINDOWS + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include +#else + #include + #include +#endif + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup support +//! \{ + +//! Returns number of seconds since the epoch, high resolution. +inline double +timestamp() +{ +#if STXXL_BOOST_TIMESTAMP + boost::posix_time::ptime MyTime = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration Duration = + MyTime - boost::posix_time::time_from_string("1970-01-01 00:00:00.000"); + double sec = double(Duration.hours()) * 3600. + + double(Duration.minutes()) * 60. + + double(Duration.seconds()) + + double(Duration.fractional_seconds()) / (pow(10., Duration.num_fractional_digits())); + return sec; +#elif STXXL_WINDOWS + return GetTickCount() / 1000.0; +#else + struct timeval tp; + gettimeofday(&tp, NULL); + return double(tp.tv_sec) + double(tp.tv_usec) / 1000000.; +#endif +} + +/*! + * Class timer is a simple stop watch timer. It uses the timestamp() function + * to get the current time when start() is called. Then, after some processing, + * the function stop() functions can be called, or seconds() and other + * accessors can be called directly. + */ +class timer +{ + //! boolean whether the stopwatch timer is currently running + bool running; + + //! total accumulated time in seconds. + double accumulated; + + //! last start time of the stopwatch + double last_clock; + + //! return current timestamp + static inline double timestamp() + { + return stxxl::timestamp(); + } + +public: + //! boolean indicating that this class does real timing + static const bool is_real = true; + + //! initialize and optionally immediately start the timer + inline timer(bool start_immediately = false) + : running(false), accumulated(0), last_clock(0) + { + if (start_immediately) start(); + } + + //! start timer + inline void start() + { + running = true; + last_clock = timestamp(); + } + + //! stop timer + inline void stop() + { + running = false; + accumulated += timestamp() - last_clock; + } + + //! return accumulated time + inline void reset() + { + accumulated = 0.; + last_clock = timestamp(); + } + + //! return currently accumulated time in milliseconds + inline double mseconds() const + { + if (running) + return (accumulated + timestamp() - last_clock) * 1000.; + + return (accumulated * 1000.); + } + + //! return currently accumulated time in microseconds + inline double useconds() const + { + if (running) + return (accumulated + timestamp() - last_clock) * 1000000.; + + return (accumulated * 1000000.); + } + + //! return currently accumulated time in seconds (as double) + inline double seconds() const + { + if (running) + return (accumulated + timestamp() - last_clock); + + return (accumulated); + } + + //! accumulate elapsed time from another timer + inline timer& operator += (const timer& tm) + { +#if STXXL_PARALLEL +#pragma omp atomic +#endif + accumulated += tm.seconds(); + return *this; + } + + //! direct <<-operator for ostream. Can be used for printing with std::cout. + friend std::ostream& operator << (std::ostream& os, const timer& t) + { + return os << t.seconds() << 's'; + } +}; + +/*! + * Class fake_timer is a drop-in replacement for timer, which does + * nothing. Using the fake class, timers can quickly be disabled in release + * builds, but still be available for debugging session. + * + * \see timer + */ +class fake_timer +{ +public: + //! boolean indicating that this class does NOT do real timing + static const bool is_real = false; + + //! initialize and optionally immediately start the timer + fake_timer(bool = false) + { } + + //! start timer + void start() + { } + + //! stop timer + void stop() + { } + + //! return accumulated time + void reset() + { } + + //! return currently accumulated time in milliseconds + double mseconds() const + { + return std::numeric_limits::quiet_NaN(); + } + + //! return currently accumulated time in microseconds + double useconds() const + { + return std::numeric_limits::quiet_NaN(); + } + + //! return currently accumulated time in seconds (as double) + double seconds() const + { + return std::numeric_limits::quiet_NaN(); + } + + //! accumulate elapsed time from another timer + inline fake_timer& operator += (const fake_timer&) + { + return *this; + } + + //! direct <<-operator for ostream. Can be used for printing with std::cout. + friend std::ostream& operator << (std::ostream& os, const fake_timer& t) + { + return os << t.seconds() << 's'; + } +}; + +/*! + * Simple scoped timer, which takes a text message and prints the duration + * until the scope is destroyed. + */ +class scoped_print_timer +{ +protected: + //! message + std::string m_message; + + //! bytes processed + uint64 m_bytes; + + //! timer + stxxl::timer m_timer; + +public: + //! save message and start timer + scoped_print_timer(const std::string& message, const uint64 bytes = 0) + : m_message(message), + m_bytes(bytes), + m_timer(true) + { + STXXL_MSG("Starting " << message); + } + + //! on destruction: tell the time + ~scoped_print_timer() + { + if (m_bytes == 0) { + STXXL_MSG("Finished " + << m_message + << " after " << m_timer.seconds() << " seconds"); + } + else { + double bps = (double)m_bytes / m_timer.seconds(); + + STXXL_MSG("Finished " + << m_message + << " after " << m_timer.seconds() << " seconds. " + << "Processed " << format_IEC_size(m_bytes) << "B" + << " @ " << format_IEC_size((uint64)bps) << "B/s"); + } + } + + //! constant access to enclosed timer + const stxxl::timer & timer() const + { + return m_timer; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_TIMER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tmeta.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tmeta.h new file mode 100644 index 0000000000..81a6d32f05 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tmeta.h @@ -0,0 +1,163 @@ +/*************************************************************************** + * include/stxxl/bits/common/tmeta.h + * + * Template Metaprogramming Tools + * (from the Generative Programming book Krysztof Czarnecki, Ulrich Eisenecker) + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_COMMON_TMETA_HEADER +#define STXXL_COMMON_TMETA_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! IF template metaprogramming statement. +//! +//! If \c Flag is \c true then \c IF<>::result is of type Type1 +//! otherwise of \c IF<>::result is of type Type2 +template +struct IF +{ + typedef Type1 result; +}; + +template +struct IF +{ + typedef Type2 result; +}; + +//! If \c Flag is \c true then \c IF<>::result is Num1 +//! otherwise of \c IF<>::result is Num2 +template +struct IF_N +{ + enum + { + result = Num1 + }; +}; + +template +struct IF_N +{ + enum + { + result = Num2 + }; +}; + +const int DEFAULT = ~(~0u >> 1); // initialize with the smallest int + +struct NilCase { }; + +template +struct CASE +{ + enum { tag = tag_ }; + typedef Type_ Type; + typedef Next_ Next; +}; + +template +class SWITCH +{ + typedef typename Case::Next NextCase; + enum + { + caseTag = Case::tag, + found = (caseTag == tag || caseTag == DEFAULT) + }; + +public: + typedef typename IF::result + >::result result; +}; + +template +class SWITCH +{ +public: + typedef NilCase result; +}; + +//! \internal, use LOG2 instead +template +class LOG2_floor +{ +public: + enum + { + value = LOG2_floor::value + 1 + }; +}; + +template <> +class LOG2_floor<1> +{ +public: + enum + { + value = 0 + }; +}; + +template <> +class LOG2_floor<0> +{ +public: + enum + { + value = 0 + }; +}; + +template +class LOG2 +{ +public: + enum + { + floor = LOG2_floor::value, + ceil = LOG2_floor::value + 1 + }; +}; + +template <> +class LOG2<1> +{ +public: + enum + { + floor = 0, + ceil = 0 + }; +}; + +template <> +class LOG2<0> +{ +public: + enum + { + floor = 0, + ceil = 0 + }; +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_TMETA_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tuple.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tuple.h new file mode 100644 index 0000000000..721a45b5af --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/tuple.h @@ -0,0 +1,738 @@ +/*************************************************************************** + * include/stxxl/bits/common/tuple.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003 Roman Dementiev + * + * 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 STXXL_COMMON_TUPLE_HEADER +#define STXXL_COMMON_TUPLE_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +struct Plug { }; + +template +struct tuple_base +{ + typedef T1 first_type; + typedef T2 second_type; + typedef T3 third_type; + typedef T4 fourth_type; + typedef T5 fifth_type; + typedef T6 sixth_type; + + template + struct item_type + { +/* + typedef typename SWITCH > > > > > > >::result result; +*/ + }; +}; + +//! k-Tuple data type +//! +//! (defined for k < 7) +template +struct tuple +{ + //! First tuple component type + typedef T1 first_type; + //! Second tuple component type + typedef T2 second_type; + //! Third tuple component type + typedef T3 third_type; + //! Fourth tuple component type + typedef T4 fourth_type; + //! Fifth tuple component type + typedef T5 fifth_type; + //! Sixth tuple component type + typedef T6 sixth_type; + + template + struct item_type + { + typedef typename SWITCH > > > > > > >::result result; + }; + + //! First tuple component + first_type first; + //! Second tuple component + second_type second; + //! Third tuple component + third_type third; + //! Fourth tuple component + fourth_type fourth; + //! Fifth tuple component + fifth_type fifth; + //! Sixth tuple component + sixth_type sixth; + + //! Empty constructor + tuple() { } + + //! Construct tuple from components + tuple(first_type _first, + second_type _second, + third_type _third, + fourth_type _fourth, + fifth_type _fifth, + sixth_type _sixth + ) + : first(_first), + second(_second), + third(_third), + fourth(_fourth), + fifth(_fifth), + sixth(_sixth) + { } + + //! Equality comparison + bool operator == (const tuple& t) const + { + return first == t.first && second == t.second && third == t.third + && fourth == t.fourth && fifth == t.fifth && sixth == t.sixth; + } + + //! Inequality comparison + bool operator != (const tuple& t) const + { + return !(first == t.first && second == t.second && third == t.third + && fourth == t.fourth && fifth == t.fifth && sixth == t.sixth); + } + + //! Make tuple ostream-able + friend std::ostream& operator << (std::ostream& os, const tuple& t) + { + return os << '(' << t.first << ',' << t.second << ',' << t.third + << ',' << t.fourth << ',' << t.fifth << ',' << t.sixth + << ')'; + } + + //! Return minimum value of tuple using numeric_limits + static tuple min_value() + { + return tuple(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + //! Return maximum value of tuple using numeric_limits + static tuple max_value() + { + return tuple(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } +}; + +//! Partial specialization for 1- \c tuple +template +struct tuple +{ + //! First tuple component type + typedef T1 first_type; + + //! First tuple component + first_type first; + + template + struct item_type + { + typedef typename IF::result result; + }; + + //! Empty constructor + tuple() { } + + //! Initializing constructor + tuple(first_type first_) + : first(first_) + { } + + //! Equality comparison + bool operator == (const tuple& t) const + { + return first == t.first; + } + + //! Inequality comparison + bool operator != (const tuple& t) const + { + return !(first == t.first); + } + + //! Make tuple ostream-able + friend std::ostream& operator << (std::ostream& os, const tuple& t) + { + return os << '(' << t.first << ')'; + } + + //! Return minimum value of tuple using numeric_limits + static tuple min_value() + { + return tuple(std::numeric_limits::min()); + } + + //! Return maximum value of tuple using numeric_limits + static tuple max_value() + { + return tuple(std::numeric_limits::max()); + } +}; + +//! Partial specialization for 2- \c tuple (equivalent to std::pair) +template +struct tuple +{ + //! First tuple component type + typedef T1 first_type; + //! Second tuple component type + typedef T2 second_type; + + template + struct item_type + { + typedef typename SWITCH + > > >::result result; + }; + + //! First tuple component + first_type first; + //! Second tuple component + second_type second; + + //! Empty constructor + tuple() { } + + //! Initializing constructor + tuple(first_type first_, + second_type second_) + : first(first_), + second(second_) + { } + + //! Equality comparison + bool operator == (const tuple& t) const + { + return first == t.first && second == t.second; + } + + //! Inequality comparison + bool operator != (const tuple& t) const + { + return !(first == t.first && second == t.second); + } + + //! Make tuple ostream-able + friend std::ostream& operator << (std::ostream& os, const tuple& t) + { + return os << '(' << t.first << ',' << t.second << ')'; + } + + //! Return minimum value of tuple using numeric_limits + static tuple min_value() + { + return tuple(std::numeric_limits::min(), + std::numeric_limits::min()); + } + + //! Return maximum value of tuple using numeric_limits + static tuple max_value() + { + return tuple(std::numeric_limits::max(), + std::numeric_limits::max()); + } +}; + +//! Partial specialization for 3- \c tuple (triple) +template +struct tuple +{ + //! First tuple component type + typedef T1 first_type; + //! Second tuple component type + typedef T2 second_type; + //! Third tuple component type + typedef T3 third_type; + + template + struct item_type + { + typedef typename SWITCH + > > > >::result result; + }; + + //! First tuple component + first_type first; + //! Second tuple component + second_type second; + //! Third tuple component + third_type third; + + //! Empty constructor + tuple() { } + + //! Construct tuple from components + tuple(first_type _first, + second_type _second, + third_type _third) + : first(_first), + second(_second), + third(_third) + { } + + //! Equality comparison + bool operator == (const tuple& t) const + { + return first == t.first && second == t.second && third == t.third; + } + + //! Inequality comparison + bool operator != (const tuple& t) const + { + return !(first == t.first && second == t.second && third == t.third); + } + + //! Make tuple ostream-able + friend std::ostream& operator << (std::ostream& os, const tuple& t) + { + return os << '(' << t.first << ',' << t.second << ',' << t.third + << ')'; + } + + //! Return minimum value of tuple using numeric_limits + static tuple min_value() + { + return tuple(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + //! Return maximum value of tuple using numeric_limits + static tuple max_value() + { + return tuple(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } +}; + +//! Partial specialization for 4- \c tuple +template +struct tuple +{ + //! First tuple component type + typedef T1 first_type; + //! Second tuple component type + typedef T2 second_type; + //! Third tuple component type + typedef T3 third_type; + //! Fourth tuple component type + typedef T4 fourth_type; + + template + struct item_type + { + typedef typename SWITCH > > > > >::result result; + }; + + //! First tuple component + first_type first; + //! Second tuple component + second_type second; + //! Third tuple component + third_type third; + //! Fourth tuple component + fourth_type fourth; + + //! Empty constructor + tuple() { } + + //! Construct tuple from components + tuple(first_type _first, + second_type _second, + third_type _third, + fourth_type _fourth) + : first(_first), + second(_second), + third(_third), + fourth(_fourth) + { } + + //! Equality comparison + bool operator == (const tuple& t) const + { + return first == t.first && second == t.second && third == t.third + && fourth == t.fourth; + } + + //! Inequality comparison + bool operator != (const tuple& t) const + { + return !(first == t.first && second == t.second && third == t.third + && fourth == t.fourth); + } + + //! Make tuple ostream-able + friend std::ostream& operator << (std::ostream& os, const tuple& t) + { + return os << '(' << t.first << ',' << t.second << ',' << t.third + << ',' << t.fourth << ')'; + } + + //! Return minimum value of tuple using numeric_limits + static tuple min_value() + { + return tuple(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + //! Return maximum value of tuple using numeric_limits + static tuple max_value() + { + return tuple(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } +}; + +//! Partial specialization for 5- \c tuple +template +struct tuple +{ + //! First tuple component type + typedef T1 first_type; + //! Second tuple component type + typedef T2 second_type; + //! Third tuple component type + typedef T3 third_type; + //! Fourth tuple component type + typedef T4 fourth_type; + //! Fifth tuple component type + typedef T5 fifth_type; + + template + struct item_type + { + typedef typename SWITCH > > > > > >::result result; + }; + + //! First tuple component + first_type first; + //! Second tuple component + second_type second; + //! Third tuple component + third_type third; + //! Fourth tuple component + fourth_type fourth; + //! Fifth tuple component + fifth_type fifth; + + //! Empty constructor + tuple() { } + + //! Construct tuple from components + tuple(first_type _first, + second_type _second, + third_type _third, + fourth_type _fourth, + fifth_type _fifth) + : first(_first), + second(_second), + third(_third), + fourth(_fourth), + fifth(_fifth) + { } + + //! Equality comparison + bool operator == (const tuple& t) const + { + return first == t.first && second == t.second && third == t.third + && fourth == t.fourth && fifth == t.fifth; + } + + //! Inequality comparison + bool operator != (const tuple& t) const + { + return !(first == t.first && second == t.second && third == t.third + && fourth == t.fourth && fifth == t.fifth); + } + + //! Make tuple ostream-able + friend std::ostream& operator << (std::ostream& os, const tuple& t) + { + return os << '(' << t.first << ',' << t.second << ',' << t.third + << ',' << t.fourth << ',' << t.fifth << ')'; + } + + //! Return minimum value of tuple using numeric_limits + static tuple min_value() + { + return tuple(std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()); + } + + //! Return maximum value of tuple using numeric_limits + static tuple max_value() + { + return tuple(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + } +}; + +/* + template + typename tuple_type::item_type::result get(const tuple_type & t) + { + return NULL; + } +*/ + +template +struct tuple_less1st +{ + typedef TupleType value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + return (a.first < b.first); + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } +}; + +template +struct tuple_greater1st +{ + typedef TupleType value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + return (a.first > b.first); + } + + static value_type min_value() { return value_type::max_value(); } + static value_type max_value() { return value_type::min_value(); } +}; + +template +struct tuple_less1st_less2nd +{ + typedef TupleType value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if (a.first == b.first) + return (a.second < b.second); + return (a.first < b.first); + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } +}; + +template +struct tuple_less2nd +{ + typedef TupleType value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + return (a.second < b.second); + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } +}; + +template +struct tuple_less2nd_less1st +{ + typedef TupleType value_type; + + bool operator () (const value_type& a, const value_type& b) const + { + if (a.second == b.second) + return (a.first < b.first); + return (a.second < b.second); + } + + static value_type min_value() { return value_type::min_value(); } + static value_type max_value() { return value_type::max_value(); } +}; + +namespace stream { + +/** + * Counter for creating tuple indexes for example. + */ +template +struct counter +{ +public: + typedef ValueType value_type; + +protected: + value_type m_count; + +public: + counter(const value_type& start = 0) + : m_count(start) + { } + + const value_type& operator * () const + { + return m_count; + } + + counter& operator ++ () + { + ++m_count; + return *this; + } + + bool empty() const + { + return false; + } +}; + +/** + * Concatenates two tuple streams as streamA . streamB + */ +template +class concatenate +{ +public: + typedef typename StreamA::value_type value_type; + +private: + StreamA& A; + StreamB& B; + +public: + concatenate(StreamA& A_, StreamB& B_) : A(A_), B(B_) + { + assert(!A.empty()); + assert(!B.empty()); + } + + const value_type& operator * () const + { + assert(!empty()); + + if (!A.empty()) { + return *A; + } + else { + return *B; + } + } + + concatenate& operator ++ () + { + assert(!empty()); + + if (!A.empty()) { + ++A; + } + else if (!B.empty()) { + ++B; + } + + return *this; + } + + bool empty() const + { + return (A.empty() && B.empty()); + } +}; + +} // namespace stream + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_TUPLE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/types.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/types.h new file mode 100644 index 0000000000..7fe461c26b --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/types.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * include/stxxl/bits/common/types.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Roman Dementiev + * Copyright (C) 2010 Andreas Beckmann + * + * 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 STXXL_COMMON_TYPES_HEADER +#define STXXL_COMMON_TYPES_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +#if STXXL_MSVC +typedef __int8 int8; +typedef unsigned __int8 uint8; +typedef __int16 int16; +typedef unsigned __int16 uint16; +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +typedef char int8; +typedef unsigned char uint8; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +typedef long long int int64; +typedef unsigned long long int uint64; +#endif + +// integer types declarations +enum { my_pointer_size = sizeof(void*) }; + +template +struct choose_int_types +{ }; + +template <> +struct choose_int_types<4> // for 32-bit processors/compilers +{ + typedef int32 int_type; + typedef uint32 unsigned_type; +}; + +template <> +struct choose_int_types<8> // for 64-bit processors/compilers +{ + typedef int64 int_type; + typedef uint64 unsigned_type; +}; + +typedef choose_int_types::int_type int_type; +typedef choose_int_types::unsigned_type unsigned_type; + +typedef unsigned_type internal_size_type; // fits in internal memory +typedef uint64 external_size_type; // may require external memory + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_TYPES_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/uint_types.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/uint_types.h new file mode 100644 index 0000000000..a1d94e967b --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/uint_types.h @@ -0,0 +1,323 @@ +/*************************************************************************** + * include/stxxl/bits/common/uint_types.h + * + * Class representing a 40-bit or 48-bit unsigned integer encoded in five or + * six bytes. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_UINT_TYPES_HEADER +#define STXXL_COMMON_UINT_TYPES_HEADER + +#include +#include +#include +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +/*! + * Construct an 40-bit or 48-bit unsigned integer stored in five or six bytes. + * + * The purpose of this class is to provide integers with smaller data storage + * footprints when more than 32-bit, but less than 64-bit indexes are + * needed. This is commonly the case for storing file offsets and indexes. Here + * smaller types currently suffice for files < 1 TiB or < 16 TiB. + * + * The class combines a 32-bit integer with a HighType (either 8-bit or 16-bit) + * to get a larger type. Only unsigned values are supported, which fits the + * general application of file offsets. + * + * Calculation in uint_pair are generally done by transforming everything to + * 64-bit data type, so that 64-bit register arithmetic can be used. The + * exception here is \b increment and \b decrement, which is done directly on + * the lower/higher part. Not all arithmetic operations are supported, patches + * welcome if you really need the operations. + */ +#if STXXL_MSVC +#pragma pack(push, 1) +#endif +template +class uint_pair +{ +public: + //! lower part type, always 32-bit + typedef uint32 low_type; + //! higher part type, currently either 8-bit or 16-bit + typedef HighType high_type; + +private: + //! member containing lower significant integer value + low_type low; + //! member containing higher significant integer value + high_type high; + + //! return highest value storable in lower part, also used as a mask. + static unsigned_type low_max() + { + return std::numeric_limits::max(); + } + + //! number of bits in the lower integer part, used a bit shift value. + static const size_t low_bits = 8 * sizeof(low_type); + + //! return highest value storable in higher part, also used as a mask. + static unsigned_type high_max() + { + return std::numeric_limits::max(); + } + + //! number of bits in the higher integer part, used a bit shift value. + static const size_t high_bits = 8 * sizeof(high_type); + +public: + //! number of binary digits (bits) in uint_pair + static const size_t digits = low_bits + high_bits; + + //! number of bytes in uint_pair + static const size_t bytes = sizeof(low_type) + sizeof(high_type); + + //! empty constructor, does not even initialize to zero! + inline uint_pair() + { + // compile-time assertions about size of low_type + STXXL_STATIC_ASSERT(8 * sizeof(low_type) == 32); + // compile-time assertions about size of our data structure, this tests + // packing of structures by the compiler + STXXL_STATIC_ASSERT(sizeof(uint_pair) == bytes); + STXXL_STATIC_ASSERT(sizeof(uint_pair) == digits / 8); + STXXL_STATIC_ASSERT(digits / 8 == bytes); + } + + //! construct unit pair from lower and higher parts. + inline uint_pair(const low_type& l, const high_type& h) + : low(l), high(h) + { } + + //! copy constructor + inline uint_pair(const uint_pair& a) + : low(a.low), high(a.high) + { } + + //! const from a simple 32-bit unsigned integer + inline uint_pair(const uint32& a) + : low(a), high(0) + { } + + //! const from a simple 32-bit signed integer + inline uint_pair(const int32& a) + : low(a), high(0) + { + if (a >= 0) + low = a; + else + low = a, high = (high_type)high_max(); + } + + //! construct from an uint64 (unsigned long long) + inline uint_pair(const uint64& a) + : low((low_type)(a & low_max())), + high((high_type)((a >> low_bits) & high_max())) + { + // check for overflow + assert((a >> (low_bits + high_bits)) == 0); + } + + //! return the number as an uint64 (unsigned long long) + inline uint64 ull() const + { + return ((uint64)high) << low_bits | (uint64)low; + } + + //! implicit cast to an unsigned long long + inline operator uint64 () const + { + return ull(); + } + + //! return the number as a uint64 + inline uint64 u64() const + { + return ((uint64)high) << low_bits | (uint64)low; + } + + //! prefix increment operator (directly manipulates the integer parts) + inline uint_pair& operator ++ () + { + if (UNLIKELY(low == low_max())) + ++high, low = 0; + else + ++low; + return *this; + } + + //! prefix decrement operator (directly manipulates the integer parts) + inline uint_pair& operator -- () + { + if (UNLIKELY(low == 0)) + --high, low = (low_type)low_max(); + else + --low; + return *this; + } + + //! addition operator (uses 64-bit arithmetic) + inline uint_pair& operator += (const uint_pair& b) + { + uint64 add = (uint64)low + b.low; + low = (low_type)(add & low_max()); + high = (high_type)(high + b.high + ((add >> low_bits) & high_max())); + return *this; + } + + //! equality checking operator + inline bool operator == (const uint_pair& b) const + { + return (low == b.low) && (high == b.high); + } + + //! inequality checking operator + inline bool operator != (const uint_pair& b) const + { + return (low != b.low) || (high != b.high); + } + + //! less-than comparison operator + inline bool operator < (const uint_pair& b) const + { + return (high < b.high) || (high == b.high && low < b.low); + } + + //! less-or-equal comparison operator + inline bool operator <= (const uint_pair& b) const + { + return (high < b.high) || (high == b.high && low <= b.low); + } + + //! greater comparison operator + inline bool operator > (const uint_pair& b) const + { + return (high > b.high) || (high == b.high && low > b.low); + } + + //! greater-or-equal comparison operator + inline bool operator >= (const uint_pair& b) const + { + return (high > b.high) || (high == b.high && low >= b.low); + } + + //! make a uint_pair outputtable via iostreams, using unsigned long long. + friend std::ostream& operator << (std::ostream& os, const uint_pair& a) + { + return os << a.ull(); + } + + //! return an uint_pair instance containing the smallest value possible + static uint_pair min() + { + return uint_pair(std::numeric_limits::min(), + std::numeric_limits::min()); + } + + //! return an uint_pair instance containing the largest value possible + static uint_pair max() + { + return uint_pair(std::numeric_limits::max(), + std::numeric_limits::max()); + } +} +#if STXXL_MSVC +; +#pragma pack(pop) +#else +__attribute__ ((packed)); +#endif + +//! \addtogroup support +//! \{ + +//! Construct a 40-bit unsigned integer stored in five bytes. +typedef uint_pair uint40; + +//! Construct a 48-bit unsigned integer stored in six bytes. +typedef uint_pair uint48; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +//! template class providing some numeric_limits fields for uint_pair types. +template +class numeric_limits > +{ +public: + //! yes we have information about uint_pair + static const bool is_specialized = true; + + //! return an uint_pair instance containing the smallest value possible + static stxxl::uint_pair min() + { return stxxl::uint_pair::min(); } + + //! return an uint_pair instance containing the largest value possible + static stxxl::uint_pair max() + { return stxxl::uint_pair::max(); } + + //! return an uint_pair instance containing the smallest value possible + static stxxl::uint_pair lowest() + { return min(); } + + //! unit_pair types are unsigned + static const bool is_signed = false; + + //! uint_pair types are integers + static const bool is_integer = true; + + //! unit_pair types contain exact integers + static const bool is_exact = true; + + //! unit_pair radix is binary + static const int radix = 2; + + //! number of binary digits (bits) in uint_pair + static const int digits = stxxl::uint_pair::digits; + + //! epsilon is zero + static const stxxl::uint_pair epsilon() + { return stxxl::uint_pair(0, 0); } + + //! rounding error is zero + static const stxxl::uint_pair round_error() + { return stxxl::uint_pair(0, 0); } + + //! no exponent + static const int min_exponent = 0; + + //! no exponent + static const int min_exponent10 = 0; + + //! no exponent + static const int max_exponent = 0; + + //! no exponent + static const int max_exponent10 = 0; + + //! no infinity + static const bool has_infinity = false; +}; + +} // namespace std + +#endif // !STXXL_COMMON_UINT_TYPES_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/utils.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/utils.h new file mode 100644 index 0000000000..3db2e59901 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/utils.h @@ -0,0 +1,315 @@ +/*************************************************************************** + * include/stxxl/bits/common/utils.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2006 Roman Dementiev + * Copyright (C) 2007-2009 Andreas Beckmann + * Copyright (C) 2008 Johannes Singler + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_COMMON_UTILS_HEADER +#define STXXL_COMMON_UTILS_HEADER + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//////////////////////////////////////////////////////////////////////////// + +#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))) +# define STXXL_ATTRIBUTE_UNUSED __attribute__ ((unused)) +#else +# define STXXL_ATTRIBUTE_UNUSED +#endif + +//////////////////////////////////////////////////////////////////////////// + +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#define STXXL_STATIC_ASSERT(x) static_assert(x, #x) +#else +#define STXXL_STATIC_ASSERT(x) { typedef int static_assert_dummy_type[(x) ? 1 : -1] STXXL_ATTRIBUTE_UNUSED; } +#endif + +//////////////////////////////////////////////////////////////////////////// + +//! Split a string by given separator string. Returns a vector of strings with +//! at least min_fields and at most limit_fields +static inline std::vector +split(const std::string& str, const std::string& sep, + unsigned int min_fields = 0, + unsigned int limit_fields = std::numeric_limits::max()) +{ + std::vector result; + if (str.empty()) { + result.resize(min_fields); + return result; + } + + std::string::size_type CurPos(0), LastPos(0); + while (1) + { + if (result.size() + 1 == limit_fields) + break; + + CurPos = str.find(sep, LastPos); + if (CurPos == std::string::npos) + break; + + result.push_back( + str.substr(LastPos, + std::string::size_type(CurPos - LastPos)) + ); + + LastPos = CurPos + sep.size(); + } + + std::string sub = str.substr(LastPos); + result.push_back(sub); + + if (result.size() < min_fields) + result.resize(min_fields); + + return result; +} + +//////////////////////////////////////////////////////////////////////////// + +//! Format any ostream-able type into a string +template +std::string to_str(const Type& t) +{ + std::ostringstream oss; + oss << t; + return oss.str(); +} + +//////////////////////////////////////////////////////////////////////////// + +//! Parse a string like "343KB" or "44 GiB" into the corresponding size in +//! bytes. Returns the number of bytes and sets ok = true if the string could +//! be parsed correctly. If no units indicator is given, use def_unit in +//! k/m/g/t/p (powers of ten) or in K/M/G/T/P (power of two). +bool parse_SI_IEC_size(const std::string& str, uint64& size, char def_unit = 0); + +//! Format a byte size using SI (K, M, G, T) suffixes (powers of ten). Returns +//! "123 M" or similar. +std::string format_SI_size(uint64 number); + +//! Format a byte size using IEC (Ki, Mi, Gi, Ti) suffixes (powers of +//! two). Returns "123 Ki" or similar. +std::string format_IEC_size(uint64 number); + +//////////////////////////////////////////////////////////////////////////// + +inline stxxl::int64 atoi64(const char* s) +{ +#if STXXL_MSVC + return _atoi64(s); +#else + return atoll(s); +#endif +} + +//////////////////////////////////////////////////////////////////////////// + +inline stxxl::uint64 atouint64(const char* s) +{ +#if STXXL_MSVC + return _strtoui64(s, NULL, 10); +#else + return strtoull(s, NULL, 10); +#endif +} + +//////////////////////////////////////////////////////////////////////////// + +template +inline const Type& +STXXL_MIN(const Type& a, const Type& b) +{ + return std::min(a, b); +} + +template +inline const Type& +STXXL_MAX(const Type& a, const Type& b) +{ + return std::max(a, b); +} + +//////////////////////////////////////////////////////////////////////////// + +//! calculate the log2 floor of an integral type using math.h +template +inline Integral log2_ceil(Integral i) +{ + return Integral(ceil(log2(i))); +} + +//! calculate the log2 ceiling of an integral type using math.h +template +inline Integral log2_floor(Integral i) +{ + return Integral(log2(i)); +} + +//////////////////////////////////////////////////////////////////////////// + +//! calculate the log2 floor of an integer type (by repeated bit shifts) +template +unsigned int ilog2_floor(IntegerType i) +{ + unsigned int p = 0; + while (i >= 256) i >>= 8, p += 8; + while (i >>= 1) ++p; + return p; +} + +//! calculate the log2 ceiling of an integer type (by repeated bit shifts) +template +unsigned int ilog2_ceil(const IntegerType& i) +{ + if (i <= 1) return 0; + return ilog2_floor(i - 1) + 1; +} + +//////////////////////////////////////////////////////////////////////////// + +template +inline +typename compat::remove_const::type +div_ceil(Integral n, Integral2 d) +{ +#if 0 // ambiguous overload for std::div(unsigned_anything, unsigned_anything) + typedef __typeof__ (std::div (n, d)) div_type; + div_type result = std::div(n, d); + return result.quot + (result.rem != 0); +#else + return n / d + ((n % d) != 0); +#endif +} + +//////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUC__ +#define HAVE_BUILTIN_EXPECT +#endif + +#ifdef HAVE_BUILTIN_EXPECT + #define LIKELY(c) __builtin_expect((c), 1) +#else + #define LIKELY(c) c +#endif + +#ifdef HAVE_BUILTIN_EXPECT + #define UNLIKELY(c) __builtin_expect((c), 0) +#else + #define UNLIKELY(c) c +#endif + +//////////////////////////////////////////////////////////////////////////// + +inline size_t longhash1(uint64 key_) +{ + key_ += ~(key_ << 32); + key_ ^= (key_ >> 22); + key_ += ~(key_ << 13); + key_ ^= (key_ >> 8); + key_ += (key_ << 3); + key_ ^= (key_ >> 15); + key_ += ~(key_ << 27); + key_ ^= (key_ >> 31); + return (size_t)key_; +} + +//////////////////////////////////////////////////////////////////////////// + +template +inline void swap_1D_arrays(Type* a, Type* b, unsigned_type size) +{ + for (unsigned_type i = 0; i < size; ++i) + std::swap(a[i], b[i]); +} + +//////////////////////////////////////////////////////////////////////////// + +template +static inline Integral round_up_to_power_of_two(Integral n) +{ + --n; + for (int k = 1; k != 8 * sizeof(n); k <<= 1) + n |= n >> k; + ++n; + return n; +} + +//! round n up to next larger multiple of 2^power. example: (48,4) = 64, (48,3) = 48. +template +inline Integral round_up_to_power_of_two(Integral n, unsigned_type power) +{ + Integral pot = Integral(1) << power, // = 0..0 1 0^power + mask = pot - 1; // = 0..0 0 1^power + if (n & mask) // n not divisible by pot + return (n & ~mask) + pot; + else + return n; +} + +//////////////////////////////////////////////////////////////////////////// + +template +inline typename Container::value_type pop(Container& c) +{ + typename Container::value_type r = c.top(); + c.pop(); + return r; +} + +template +inline typename Container::value_type pop_front(Container& c) +{ + typename Container::value_type r = c.front(); + c.pop_front(); + return r; +} + +template +inline typename Container::value_type pop_back(Container& c) +{ + typename Container::value_type r = c.back(); + c.pop_back(); + return r; +} + +template +inline typename Container::value_type pop_begin(Container& c) +{ + typename Container::value_type r = *c.begin(); + c.erase(c.begin()); + return r; +} + +//////////////////////////////////////////////////////////////////////////// + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_UTILS_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/winner_tree.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/winner_tree.h new file mode 100644 index 0000000000..598cd1eee5 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/common/winner_tree.h @@ -0,0 +1,366 @@ +/*************************************************************************** + * include/stxxl/bits/common/winner_tree.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2014 Thomas Keh + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_COMMON_WINNER_TREE_HEADER +#define STXXL_COMMON_WINNER_TREE_HEADER + +#include +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +/*! + * The class winner_tree is a binary tournament tree. There are n=2^k so + * called players which compete for the winning position. The winner is the + * smallest one regarding the comparator m_less. Each player is identified with + * it's index. The comparator should use this index in order to compare the + * actual values. + * + * A change of one players value results in O(log n) operations. The winner + * can be determined in O(1) operations. + * + * There is a number of fixed players which remain at the same index forever, + * even if they were deactivated in between. Additionally any number of players + * can be added dynamically. They are assigned to a free slot and if one gets + * removed the slot will be marked free again. + */ +template +class winner_tree +{ +protected: + //! the binary tree of size 2^(k+1)-1 + std::vector m_tree; + + //! the comparator object of this tree + Comparator& m_less; + + //! number of slots for the players (2^k) + unsigned_type m_num_slots; + + //! Defines if statistics are gathered: fake_timer or timer + typedef fake_timer stats_timer; + + //! Collection of stats from the winner_tree + struct stats_type + { + stats_timer replay_time; + stats_timer double_num_slots_time; + stats_timer remove_player_time; + + friend std::ostream& operator << (std::ostream& os, const stats_type& o) + { + return os << "replay_time=" << o.replay_time << std::endl + << "double_num_slots_time=" << o.double_num_slots_time << std::endl + << "remove_player_time=" << o.remove_player_time << std::endl; + } + }; + + //! Collection of stats from the winner_tree + stats_type m_stats; + + const unsigned_type invalid_key = std::numeric_limits::max(); + +public: + /** + * Constructor. Reserves space, registers free slots. No games are played + * here! All players and slots are deactivated in the beginning. + * + * \param num_players Number of fixed players. Fixed players remain at the + * same index forever, even if they were deactivated in between. + * + * \param less The comparator. It should use two given indices, compare the + * corresponding values and return the index of one with the smaller value. + */ + winner_tree(unsigned_type num_players, Comparator& less) + : m_less(less) + { + STXXL_ASSERT(num_players > 0 && num_players <= invalid_key); + + m_num_slots = (1 << ilog2_ceil(num_players)); + unsigned_type treesize = (m_num_slots << 1) - 1; + m_tree.resize(treesize, invalid_key); + } + + //! activate one of the static players or add a new one and replay + inline void activate_player(unsigned_type index) + { + STXXL_ASSERT(index != invalid_key); + while (index >= m_num_slots) + double_num_slots(); + replay_on_change(index); + } + + //! activate a player and resize if necessary + inline void activate_without_replay(unsigned_type index) + { + STXXL_ASSERT(index != invalid_key); + while (index >= m_num_slots) + double_num_slots(); + m_tree[((m_tree.size() / 2) + index)] = index; + } + + //! deactivate a player + inline void deactivate_without_replay(unsigned_type index) + { + STXXL_ASSERT(index != invalid_key); + STXXL_ASSERT(index < m_num_slots); + m_tree[((m_tree.size() / 2) + index)] = invalid_key; + } + + //! deactivate a player and replay. + inline void deactivate_player(unsigned_type index) + { + STXXL_ASSERT(index != invalid_key); + STXXL_ASSERT(index < m_num_slots); + replay_on_change(index, true); + } + + /** + * deactivate one player in a batch of deactivations run + * replay_on_deactivation() afterwards. If there are multiple consecutive + * removes you should run deactivate_player_step() for all of them first and + * afterwards run replay_on_deactivation() for each one of them. + */ + inline void deactivate_player_step(unsigned_type index) + { + STXXL_ASSERT(index != invalid_key); + STXXL_ASSERT(index < m_num_slots); + + m_stats.remove_player_time.start(); + unsigned_type p = (m_tree.size() / 2) + index; + + // Needed for following deactivations... + while (p > 0 && m_tree[p] == index) { + m_tree[p] = invalid_key; + p -= (p + 1) % 2; // move to left sibling if necessary + p /= 2; // move to parent + } + + if (m_tree[0] == index) + m_tree[0] = invalid_key; + + m_stats.remove_player_time.stop(); + } + + //! Replay after the player at index has been deactivated. + inline void replay_on_deactivations(unsigned_type index) + { + STXXL_ASSERT(index != invalid_key); + STXXL_ASSERT(index < m_num_slots); + replay_on_change(index, true); + } + + //! Notify that the value of the player at index has changed. + inline void notify_change(unsigned_type index) + { + STXXL_ASSERT(index != invalid_key); + replay_on_change(index); + } + + //! Returns the winner. + //! Remember running replay_on_pop() if the value of the winner changes. + inline unsigned_type top() const + { + return m_tree[0]; + } + + //! Returns if all players are deactivated. + inline bool empty() const + { + return (m_tree[0] == invalid_key); + } + + //! Return the current number of slots + size_t num_slots() const + { + return m_num_slots; + } + + //! Replay after the value of the winning player has changed. + inline void replay_on_pop() + { + STXXL_ASSERT(!empty()); + replay_on_change(m_tree[0]); + } + + /** + * Replays all games the player with the given index is involved in. This + * corresponds to the path from leaf [index] to root. If only the value of + * player [index] has changed the result is a valid winner tree. + * + * \param index The player whose value has changed. + * + * \param done Set done to true if the player has been deactivated + * or removed. All games will be lost then. + */ + inline void replay_on_change(unsigned_type index, bool done = false) + { + STXXL_ASSERT(index != invalid_key); + m_stats.replay_time.start(); + + unsigned_type top; + unsigned_type p = (m_tree.size() / 2) + index; + + if (!done) + top = index; + else + top = invalid_key; + + while (p > 0) { + m_tree[p] = top; + p -= (p + 1) % 2; // round down to left node position + + if (m_tree[p] == invalid_key) + top = m_tree[p + 1]; + else if (m_tree[p + 1] == invalid_key) + top = m_tree[p]; + else if (m_less(m_tree[p], m_tree[p + 1])) + top = m_tree[p]; + else + top = m_tree[p + 1]; + + p /= 2; + } + + m_tree[p] = top; + m_stats.replay_time.stop(); + } + + //! Doubles the number of slots and adapts the tree so it's a valid winner + //! tree again. + inline void double_num_slots() + { + m_stats.double_num_slots_time.start(); + + m_num_slots = m_num_slots << 1; + size_t old_tree_size = m_tree.size(); + size_t tree_size = (m_num_slots << 1) - 1; + m_tree.resize(tree_size, invalid_key); + + for (unsigned_type i = old_tree_size - 1; i >= 0; --i) { + size_t old_index = i; + size_t old_level = ilog2_floor(old_index + 1); + size_t new_index = old_index + (1 << old_level); + m_tree[new_index] = m_tree[old_index]; + } + + size_t step_size = (1 << ilog2_floor(old_tree_size)); + size_t index = tree_size - 1; + + while (step_size > 0) { + for (size_t i = 0; i < step_size; ++i) { + m_tree[index] = invalid_key; + --index; + } + index -= step_size; + step_size /= 2; + } + + m_stats.double_num_slots_time.stop(); + } + + //! Deactivate all players + inline void clear() + { + std::fill(m_tree.begin(), m_tree.end(), invalid_key); + } + + inline void resize(unsigned_type num_players) + { + m_num_slots = (1 << ilog2_ceil(num_players)); + unsigned_type treesize = (m_num_slots << 1) - 1; + m_tree.resize(treesize, invalid_key); + } + + //! Deactivate all players and resize the tree. + inline void resize_and_clear(unsigned_type num_players) + { + resize(num_players); + clear(); + } + + inline void resize_and_rebuild(unsigned_type num_players) + { + //resize(num_players); + STXXL_ASSERT(num_players > 0); + resize_and_clear(num_players); + for (unsigned_type i = 0; i < num_players; ++i) + activate_without_replay(i); + //for (unsigned_type i=num_players; i index OK + const unsigned_type rc = i * 2 + 2; + unsigned_type winner; + + if (m_tree[lc] == invalid_key) + winner = m_tree[rc]; + else if (m_tree[rc] == invalid_key) + winner = m_tree[lc]; + else if (m_less(m_tree[lc], m_tree[rc])) + winner = m_tree[lc]; + else + winner = m_tree[rc]; + + m_tree[i] = winner; + } while (i > 0); + } + + //! Returns a readable representation of the winner tree as string. + std::string to_string() const + { + std::ostringstream ss; + unsigned_type levelsize = 1; + unsigned_type j = 0; + for (unsigned_type i = 0; i < m_tree.size(); ++i) { + if (i >= j + levelsize) { + ss << "\n"; + j = i; + levelsize *= 2; + } + if (m_tree[i] != invalid_key) + ss << m_tree[i] << " "; + else + ss << "~ "; + } + ss << "\n"; + return ss.str(); + } + + //! Print statistical data. + void print_stats() const + { + STXXL_VARDUMP(m_num_slots); + STXXL_MSG(m_stats); + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMMON_WINNER_TREE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/hash_map.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/hash_map.h new file mode 100644 index 0000000000..c17e1624f7 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/hash_map.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * include/stxxl/bits/compat/hash_map.h + * + * compatibility interface to C++ standard extension hash_map + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008, 2010, 2011 Andreas Beckmann + * Copyright (C) 2009, 2010 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_COMPAT_HASH_MAP_HEADER +#define STXXL_COMPAT_HASH_MAP_HEADER + +#include +#include + +#if __cplusplus >= 201103L || (STXXL_MSVC && _MSC_VER >= 1900) + #include +#elif STXXL_MSVC + #include +#elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ + (!defined(__ICC) || (__ICC > 1110)) + #include +#else + #include +#endif + +STXXL_BEGIN_NAMESPACE + +template +struct compat_hash { +#if __cplusplus >= 201103L || (STXXL_MSVC && _MSC_VER >= 1900) + typedef std::hash result; +#elif STXXL_MSVC + typedef stdext::hash_compare result; +#elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ + (!defined(__ICC) || (__ICC > 1110)) + typedef std::tr1::hash result; +#else + typedef __gnu_cxx::hash result; +#endif +}; + +template ::result> +struct compat_hash_map { +#if __cplusplus >= 201103L || (STXXL_MSVC && _MSC_VER >= 1900) + typedef std::unordered_map result; +#elif STXXL_MSVC + typedef stdext::hash_map result; +#elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ + (!defined(__ICC) || (__ICC > 1110)) + typedef std::tr1::unordered_map result; +#else + typedef __gnu_cxx::hash_map result; +#endif +}; + +template ::result> +struct compat_hash_multimap { +#if __cplusplus >= 201103L || (STXXL_MSVC && _MSC_VER >= 1900) + typedef std::unordered_multimap result; +#elif STXXL_MSVC + typedef stdext::hash_multimap result; +#elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40200) && \ + (!defined(__ICC) || (__ICC > 1110)) + typedef std::tr1::unordered_multimap result; +#else + typedef __gnu_cxx::hash_multimap result; +#endif +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMPAT_HASH_MAP_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/type_traits.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/type_traits.h new file mode 100644 index 0000000000..5dfa8a7b3f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/type_traits.h @@ -0,0 +1,108 @@ +/*************************************************************************** + * include/stxxl/bits/compat/type_traits.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2009-2011 Andreas Beckmann + * + * 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 STXXL_COMPAT_TYPE_TRAITS_HEADER +#define STXXL_COMPAT_TYPE_TRAITS_HEADER + +#include +#include + +#if __cplusplus >= 201103L +#include +#elif defined(__GNUG__) && (__GNUC__ >= 4) +#include +#elif STXXL_BOOST_CONFIG +#include +#endif + +STXXL_BEGIN_NAMESPACE + +namespace compat { + +#if __cplusplus >= 201103L +using std::remove_const; +#elif defined(__GNUG__) && (__GNUC__ >= 4) +using std::tr1::remove_const; +#elif STXXL_BOOST_CONFIG +using boost::remove_const; +#else +template +struct remove_const +{ + typedef Type type; +}; + +template +struct remove_const +{ + typedef Type type; +}; +#endif + +#if defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40300) +// That is a small subset of what GCC 4.3 does: + +// Utility for finding the signed versions of unsigned integral types. +template +struct _make_signed +{ + typedef Type type; +}; + +template <> +struct _make_signed +{ + typedef signed char type; +}; + +template <> +struct _make_signed +{ + typedef signed char type; +}; + +template <> +struct _make_signed +{ + typedef signed short type; +}; + +template <> +struct _make_signed +{ + typedef signed int type; +}; + +template <> +struct _make_signed +{ + typedef signed long type; +}; + +template <> +struct _make_signed +{ + typedef signed long long type; +}; + +template +struct make_signed +{ + typedef typename _make_signed::type type; +}; +#endif + +} // namespace compat + +STXXL_END_NAMESPACE + +#endif // !STXXL_COMPAT_TYPE_TRAITS_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/unique_ptr.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/unique_ptr.h new file mode 100644 index 0000000000..9df12ffe07 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/compat/unique_ptr.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * include/stxxl/bits/compat/unique_ptr.h + * + * compatibility interface to unique_ptr (C++0x), previously auto_ptr + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_COMPAT_UNIQUE_PTR_HEADER +#define STXXL_COMPAT_UNIQUE_PTR_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +struct compat_unique_ptr { +#if __cplusplus >= 201103L && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40400) + typedef std::unique_ptr result; +#else + // auto_ptr is inherently broken and is deprecated by unique_ptr in c++0x + typedef std::auto_ptr result; +#endif +}; + +STXXL_END_NAMESPACE + +#if defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) == 30400) + +namespace workaround_gcc_3_4 { + +// std::swap in gcc 3.4 is broken, tmp is declared const there +template +inline void +swap(Type& a, Type& b) +{ + // concept requirements + __glibcxx_function_requires(_SGIAssignableConcept) + + Type tmp = a; + a = b; + b = tmp; +} + +} // namespace workaround_gcc_3_4 + +namespace std { + +// overload broken std::swap to call a working swap() +template +inline void swap(std::auto_ptr& a, std::auto_ptr& b) +{ + workaround_gcc_3_4::swap(a, b); +} + +} // namespace std + +#endif + +#endif // !STXXL_COMPAT_UNIQUE_PTR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/config.h.in b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/config.h.in new file mode 100644 index 0000000000..89159901ed --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/config.h.in @@ -0,0 +1,126 @@ +// -*- mode: c++ -*- +/*************************************************************************** + * include/stxxl/bits/config.h.in + * + * Template file processed by cmake to set all define switches for this build + * according to the cmake build options. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2012-2013 Timo Bingmann + * + * 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 STXXL_CONFIG_HEADER +#define STXXL_CONFIG_HEADER + +// the STXXL library version variables +#define STXXL_VERSION_MAJOR ${STXXL_VERSION_MAJOR} +#define STXXL_VERSION_MINOR ${STXXL_VERSION_MINOR} +#define STXXL_VERSION_PATCH ${STXXL_VERSION_PATCH} +#define STXXL_VERSION_STRING "${STXXL_VERSION_STRING}" +#define STXXL_VERSION_PHASE "${STXXL_VERSION_PHASE}" + +// if this is a git repository, add the refspec and commit sha +#cmakedefine STXXL_VERSION_GIT_REFSPEC "${STXXL_VERSION_GIT_REFSPEC}" +#cmakedefine STXXL_VERSION_GIT_SHA1 "${STXXL_VERSION_GIT_SHA1}" + +#ifndef STXXL_DEBUG_ASSERTIONS +#cmakedefine STXXL_DEBUG_ASSERTIONS ${STXXL_DEBUG_ASSERTIONS} +#endif +// default: off +// cmake: option +// effect: enable more costly assertions and checks for debugging the library + +#cmakedefine STXXL_DIRECT_IO_OFF ${STXXL_DIRECT_IO_OFF} +// default: 0/1 (platform dependent) +// cmake: detection of platform and flag +// effect: disables use of O_DIRECT flag on unsupported platforms + +#cmakedefine STXXL_HAVE_MMAP_FILE ${STXXL_HAVE_MMAP_FILE} +// default: 0/1 (platform dependent) +// used in: io/mmap_file.h/cpp +// effect: enables/disables memory mapped file implementation + +#cmakedefine STXXL_HAVE_LINUXAIO_FILE ${STXXL_HAVE_LINUXAIO_FILE} +// default: 0/1 (platform dependent) +// used in: io/linuxaio_file.h/cpp +// effect: enables/disables Linux AIO file implementation + +#cmakedefine STXXL_POSIX_THREADS ${STXXL_POSIX_THREADS} +// default: off +// cmake: detection of pthreads by cmake +// effect: uses POSIX thread and mutexes on linux + +#cmakedefine STXXL_STD_THREADS ${STXXL_STD_THREADS} +// default: off +// cmake: detection of and +// effect: uses std thread and mutex on windows or (forced on linux) + +#cmakedefine STXXL_WINDOWS ${STXXL_WINDOWS} +// default: off +// cmake: detection of ms windows platform (32- or 64-bit) +// effect: enables windows-specific api calls (mingw or msvc) + +#cmakedefine STXXL_MSVC ${STXXL_MSVC} +// default: off +// cmake: detection of ms visual c++ via CMake (contains version number) +// effect: enables msvc-specific headers and macros + +#cmakedefine STXXL_HAVE_CXX11_RANGE_FOR_LOOP ${STXXL_HAVE_CXX11_RANGE_FOR_LOOP} +// default: off +// run-time: detection C++11 support for "for (auto i : obj) { }" +// effect: enables some C++11 construct (currently only allowed in examples) + +#cmakedefine STXXL_HAVE_SYNC_ADD_AND_FETCH ${STXXL_HAVE_SYNC_ADD_AND_FETCH} +// default: off +// cmake: detection of __sync_add_and_fetch() intrinsic +// effect: enables use of atomics in counting_ptr + +#cmakedefine STXXL_PARALLEL ${STXXL_PARALLEL} +// default: on/off (depends on compiler and platform) +// cmake: -DUSE_PARALLEL=ON +// effect: enable parallelized algorithms using OpenMP like multiway_merge and sort + +#cmakedefine STXXL_WITH_GNU_PARALLEL ${STXXL_WITH_GNU_PARALLEL} +// default: off +// cmake: -DUSE_GNU_PARALLEL=ON +// effect: explicitly enables use of __gnu_parallel algorithms + +#cmakedefine STXXL_BOOST_CONFIG ${STXXL_BOOST_CONFIG} +#cmakedefine STXXL_BOOST_FILESYSTEM ${STXXL_BOOST_FILESYSTEM} +#cmakedefine STXXL_BOOST_RANDOM ${STXXL_BOOST_RANDOM} +#cmakedefine STXXL_BOOST_THREADS ${STXXL_BOOST_THREADS} +#cmakedefine STXXL_BOOST_TIMESTAMP ${STXXL_BOOST_TIMESTAMP} +// default: off +// cmake: -DUSE_BOOST=ON +// effect: enables use of boost libraries in different parts of STXXL. + +#if STXXL_BOOST_CONFIG + #include +#endif + +#cmakedefine STXXL_STD_RANDOM ${STXXL_STD_RANDOM} +// default: off +// cmake: detection of +// effect: uses std random generator on windows or (forced on linux) + +#cmakedefine STXXL_HAVE_MALLINFO_PROTO ${STXXL_HAVE_MALLINFO_PROTO} +// default: off +// cmake: detection of mallinfo() function in +// effect: used by stxxl_tool/mallinfo for malloc stats + +#cmakedefine STXXL_HAVE_MLOCK_PROTO ${STXXL_HAVE_MLOCK_PROTO} +// default: off +// cmake: detection of mlock() function in +// effect: used by stxxl_tool/mlock for locking physical pages + +#cmakedefine STXXL_WITH_VALGRIND ${STXXL_WITH_VALGRIND} +// default: off +// cmake: option USE_VALGRIND=ON +// effect: run all tests with valgrind and pre-initialize some memory buffers + +#endif // !STXXL_CONFIG_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/btree.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/btree.h new file mode 100644 index 0000000000..fb1449706a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/btree.h @@ -0,0 +1,1221 @@ +/*************************************************************************** + * include/stxxl/bits/containers/btree/btree.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006, 2008 Roman Dementiev + * + * 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 STXXL_CONTAINERS_BTREE_BTREE_HEADER +#define STXXL_CONTAINERS_BTREE_BTREE_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace btree { + +template +class btree : private noncopyable +{ +public: + typedef KeyType key_type; + typedef DataType data_type; + typedef CompareType key_compare; + + typedef btree self_type; + + typedef PDAllocStrategy alloc_strategy_type; + + typedef stxxl::uint64 size_type; + typedef stxxl::int64 difference_type; + typedef std::pair value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef value_type const* const_pointer; + + // leaf type declarations + typedef normal_leaf leaf_type; + friend class normal_leaf; + typedef typename leaf_type::block_type leaf_block_type; + typedef typename leaf_type::bid_type leaf_bid_type; + typedef node_cache leaf_cache_type; + friend class node_cache; + // iterator types + typedef btree_iterator iterator; + typedef btree_const_iterator const_iterator; + friend class btree_iterator_base; + // iterator map type + typedef iterator_map iterator_map_type; + // node type declarations + typedef normal_node node_type; + typedef typename node_type::block_type node_block_type; + friend class normal_node; + typedef typename node_type::bid_type node_bid_type; + typedef node_cache node_cache_type; + friend class node_cache; + + typedef typename leaf_type::value_compare value_compare; + + enum { + min_node_size = node_type::min_size, + max_node_size = node_type::max_size, + min_leaf_size = leaf_type::min_size, + max_leaf_size = leaf_type::max_size + }; + +private: + key_compare m_key_compare; + mutable node_cache_type m_node_cache; + mutable leaf_cache_type m_leaf_cache; + iterator_map_type m_iterator_map; + size_type m_size; + unsigned int m_height; + bool m_prefetching_enabled; + block_manager* m_bm; + alloc_strategy_type m_alloc_strategy; + + typedef std::map root_node_type; + typedef typename root_node_type::iterator root_node_iterator_type; + typedef typename root_node_type::const_iterator root_node_const_iterator_type; + typedef std::pair root_node_pair_type; + + root_node_type m_root_node; + iterator m_end_iterator; + + void insert_into_root(const std::pair& splitter) + { + std::pair result = + m_root_node.insert(splitter); + STXXL_ASSERT(result.second == true); + + if (m_root_node.size() > max_node_size) // root overflow + { + STXXL_VERBOSE1("btree::insert_into_root, overflow happened, splitting"); + + node_bid_type left_bid; + node_type* left_node = m_node_cache.get_new_node(left_bid); + assert(left_node); + node_bid_type right_bid; + node_type* right_node = m_node_cache.get_new_node(right_bid); + assert(right_node); + + const unsigned_type old_size = m_root_node.size(); + const unsigned_type half = m_root_node.size() / 2; + unsigned_type i = 0; + root_node_iterator_type it = m_root_node.begin(); + typename node_block_type::iterator block_it = left_node->block().begin(); + while (i < half) // copy smaller part + { + *block_it = *it; + ++i; + ++block_it; + ++it; + } + left_node->block().info.cur_size = (unsigned int)half; + key_type left_key = (left_node->block()[half - 1]).first; + + block_it = right_node->block().begin(); + while (i < old_size) // copy larger part + { + *block_it = *it; + ++i; + ++block_it; + ++it; + } + unsigned_type right_size = right_node->block().info.cur_size = (unsigned int)(old_size - half); + key_type right_key = (right_node->block()[right_size - 1]).first; + + assert(old_size == right_node->size() + left_node->size()); + + // create new root node + m_root_node.clear(); + m_root_node.insert(root_node_pair_type(left_key, left_bid)); + m_root_node.insert(root_node_pair_type(right_key, right_bid)); + + ++m_height; + STXXL_VERBOSE1("btree Increasing height to " << m_height); + if (m_node_cache.size() < (m_height - 1)) + { + STXXL_THROW2(std::runtime_error, "btree::bulk_construction", + "The height of the tree (" << m_height << ") has exceeded the required capacity (" << (m_node_cache.size() + 1) << ") of the node cache. Increase the node cache size."); + } + } + } + + template + void fuse_or_balance(root_node_iterator_type uit, CacheType& cache) + { + typedef typename CacheType::node_type local_node_type; + typedef typename local_node_type::bid_type local_bid_type; + + root_node_iterator_type left_it, right_it; + if (uit->first == key_compare::max_value()) + { + // uit is the last entry in the root + assert(uit != m_root_node.begin()); + right_it = uit; + left_it = --uit; + } + else + { + left_it = uit; + right_it = ++uit; + assert(right_it != m_root_node.end()); + } + + // now fuse or balance nodes pointed by leftIt and rightIt + local_bid_type left_bid = (local_bid_type)left_it->second; + local_bid_type right_bid = (local_bid_type)right_it->second; + local_node_type* left_node = cache.get_node(left_bid, true); + local_node_type* right_node = cache.get_node(right_bid, true); + + const unsigned_type total_size = left_node->size() + right_node->size(); + if (total_size <= right_node->max_nelements()) + { + // --- fuse --- + + // add the content of left_node to right_node + right_node->fuse(*left_node); + + cache.unfix_node(right_bid); + // 'delete_node' unfixes left_bid also + cache.delete_node(left_bid); + + // delete left BID from the root + m_root_node.erase(left_it); + } + else + { + // --- balance --- + + key_type new_splitter = right_node->balance(*left_node); + + // delete left BID from the root + m_root_node.erase(left_it); + + // reinsert with the new key + m_root_node.insert(root_node_pair_type(new_splitter, (node_bid_type)left_bid)); + + cache.unfix_node(left_bid); + cache.unfix_node(right_bid); + } + } + + void create_empty_leaf() + { + leaf_bid_type new_bid; + leaf_type* new_leaf = m_leaf_cache.get_new_node(new_bid); + assert(new_leaf); + m_end_iterator = new_leaf->end(); // initialize end() iterator + m_root_node.insert( + root_node_pair_type(key_compare::max_value(), (node_bid_type)new_bid) + ); + } + + void deallocate_children() + { + if (m_height == 2) + { + // we have children leaves here + for (root_node_const_iterator_type it = m_root_node.begin(); + it != m_root_node.end(); ++it) + { + // delete from leaf cache and deallocate bid + m_leaf_cache.delete_node((leaf_bid_type)it->second); + } + } + else + { + for (root_node_const_iterator_type it = m_root_node.begin(); + it != m_root_node.end(); ++it) + { + node_type* node = m_node_cache.get_node((node_bid_type)it->second); + assert(node); + node->deallocate_children(m_height - 1); + // delete from node cache and deallocate bid + m_node_cache.delete_node((node_bid_type)it->second); + } + } + } + + template + void bulk_construction(InputIterator begin, InputIterator end, + double node_fill_factor, double leaf_fill_factor) + { + assert(node_fill_factor >= 0.5); + assert(leaf_fill_factor >= 0.5); + key_type last_key = key_compare::max_value(); + + typedef std::pair key_bid_pair; + typedef typename stxxl::VECTOR_GENERATOR< + key_bid_pair, 1, 1, node_block_type::raw_size + >::result key_bid_vector_type; + + key_bid_vector_type bids; + + leaf_bid_type new_bid; + leaf_type* leaf = m_leaf_cache.get_new_node(new_bid); + const unsigned_type max_leaf_elements = unsigned_type( + (double)leaf->max_nelements() * leaf_fill_factor + ); + + while (begin != end) + { + // write data in leaves + + // if *b not equal to the last element + if (m_key_compare(begin->first, last_key) || m_key_compare(last_key, begin->first)) + { + ++m_size; + if (leaf->size() == max_leaf_elements) + { + // overflow, need a new block + bids.push_back(key_bid_pair(leaf->back().first, (node_bid_type)new_bid)); + + leaf_type* new_leaf = m_leaf_cache.get_new_node(new_bid); + assert(new_leaf); + // Setting links + leaf->succ() = new_leaf->my_bid(); + new_leaf->pred() = leaf->my_bid(); + + leaf = new_leaf; + } + leaf->push_back(*begin); + last_key = begin->first; + } + ++begin; + } + + // rebalance the last leaf + if (leaf->underflows() && !bids.empty()) + { + leaf_type* left_leaf = m_leaf_cache.get_node((leaf_bid_type)(bids.back().second)); + assert(left_leaf); + if (left_leaf->size() + leaf->size() <= leaf->max_nelements()) + { + // can fuse + leaf->fuse(*left_leaf); + m_leaf_cache.delete_node((leaf_bid_type)(bids.back().second)); + bids.pop_back(); + assert(!leaf->overflows() && !leaf->underflows()); + } + else + { + // need to rebalance + const key_type new_splitter = leaf->balance(*left_leaf); + bids.back().first = new_splitter; + assert(!left_leaf->overflows() && !left_leaf->underflows()); + } + } + + assert(!leaf->overflows() && (!leaf->underflows() || m_size <= max_leaf_size)); + + m_end_iterator = leaf->end(); // initialize end() iterator + + bids.push_back(key_bid_pair(key_compare::max_value(), (node_bid_type)new_bid)); + + const unsigned_type max_node_elements = unsigned_type( + double(max_node_size) * node_fill_factor + ); + + //-tb fixes bug with only one child remaining in m_root_node + while (bids.size() > node_type::max_nelements()) + { + key_bid_vector_type parent_bids; + + stxxl::uint64 nparents = div_ceil(bids.size(), max_node_elements); + assert(nparents >= 2); + STXXL_VERBOSE1("btree bulk construct" + << " bids.size=" << bids.size() + << " nparents=" << nparents + << " max_node_elements=" << max_node_elements + << " node_type::max_nelements=" << node_type::max_nelements()); + + for (typename key_bid_vector_type::const_iterator it = bids.begin(); + it != bids.end(); ) + { + node_bid_type new_bid; + node_type* node = m_node_cache.get_new_node(new_bid); + assert(node); + + for (unsigned_type cnt = 0; + cnt < max_node_elements && it != bids.end(); ++cnt, ++it) + { + node->push_back(*it); + } + + STXXL_VERBOSE1("btree bulk construct node size : " << node->size() << " limits: " << node->min_nelements() << " " << node->max_nelements() << " max_node_elements: " << max_node_elements); + + if (node->underflows()) + { + // this can happen only at the end + assert(it == bids.end()); + assert(!parent_bids.empty()); + + node_type* left_node = m_node_cache.get_node(parent_bids.back().second); + assert(left_node); + if (left_node->size() + node->size() <= node->max_nelements()) + { + // can fuse + STXXL_VERBOSE1("btree bulk construct fuse last nodes:" + << " left_node.size=" << left_node->size() + << " node.size=" << node->size()); + + node->fuse(*left_node); + m_node_cache.delete_node(parent_bids.back().second); + parent_bids.pop_back(); + } + else + { + // need to rebalance + STXXL_VERBOSE1("btree bulk construct rebalance last nodes:" + << " left_node.size=" << left_node->size() + << " node.size=" << node->size()); + + const key_type new_splitter = node->balance(*left_node, false); + parent_bids.back().first = new_splitter; + + STXXL_VERBOSE1("btree bulk construct after rebalance:" + << " left_node.size=" << left_node->size() + << " node.size=" << node->size()); + + assert(!left_node->overflows() && !left_node->underflows()); + } + } + assert(!node->overflows() && !node->underflows()); + + parent_bids.push_back(key_bid_pair(node->back().first, new_bid)); + } + + STXXL_VERBOSE1("btree parent_bids.size()=" << parent_bids.size() + << " bids.size()=" << bids.size()); + + std::swap(parent_bids, bids); + + assert(nparents == bids.size() || (nparents - 1) == bids.size()); + + ++m_height; + STXXL_VERBOSE1("Increasing height to " << m_height); + if (m_node_cache.size() < (m_height - 1)) + { + STXXL_THROW2(std::runtime_error, "btree::bulk_construction", + "The height of the tree (" << m_height << ") has exceeded the required capacity (" << (m_node_cache.size() + 1) << ") of the node cache. Increase the node cache size."); + } + } + + m_root_node.insert(bids.begin(), bids.end()); + + STXXL_VERBOSE1("btree bulk root_node_.size()=" << m_root_node.size()); + } + +public: + btree(unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes) + : m_node_cache(node_cache_size_in_bytes, this, m_key_compare), + m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), + m_iterator_map(this), + m_size(0), + m_height(2), + m_prefetching_enabled(true), + m_bm(block_manager::get_instance()) + { + STXXL_VERBOSE1("Creating a btree, addr=" << this); + STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); + STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); + STXXL_VERBOSE1(" elements in a node: " << node_block_type::size); + STXXL_VERBOSE1(" elements in a leaf: " << leaf_block_type::size); + STXXL_VERBOSE1(" size of a node element: " << sizeof(typename node_block_type::value_type)); + STXXL_VERBOSE1(" size of a leaf element: " << sizeof(typename leaf_block_type::value_type)); + + create_empty_leaf(); + } + + btree(const key_compare& key_compare, + unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes) + : m_key_compare(key_compare), + m_node_cache(node_cache_size_in_bytes, this, m_key_compare), + m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), + m_iterator_map(this), + m_size(0), + m_height(2), + m_prefetching_enabled(true), + m_bm(block_manager::get_instance()) + { + STXXL_VERBOSE1("Creating a btree, addr=" << this); + STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); + STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); + + create_empty_leaf(); + } + + virtual ~btree() + { + try + { + deallocate_children(); + } + catch (...) + { + // no exceptions in destructor + } + } + + size_type size() const + { + return m_size; + } + + size_type max_size() const + { + return std::numeric_limits::max(); + } + + bool empty() const + { + return !m_size; + } + + std::pair insert(const value_type& x) + { + root_node_iterator_type it = m_root_node.lower_bound(x.first); + assert(!m_root_node.empty()); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Inserting new value into a leaf"); + leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); + assert(leaf); + std::pair splitter; + std::pair result = leaf->insert(x, splitter); + if (result.second) + ++m_size; + + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + //if(key_compare::max_value() == Splitter.first) + if (!(m_key_compare(key_compare::max_value(), splitter.first) || + m_key_compare(splitter.first, key_compare::max_value()))) + return result; + // no overflow/splitting happened + + STXXL_VERBOSE1("Inserting new value into root node"); + + insert_into_root(std::make_pair(splitter.first, node_bid_type(splitter.second))); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Inserting new value into a node"); + node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); + assert(node); + std::pair splitter; + std::pair result = node->insert(x, m_height - 1, splitter); + if (result.second) + ++m_size; + + m_node_cache.unfix_node((node_bid_type)it->second); + //if(key_compare::max_value() == Splitter.first) + if (!(m_key_compare(key_compare::max_value(), splitter.first) || + m_key_compare(splitter.first, key_compare::max_value()))) + return result; + // no overflow/splitting happened + + STXXL_VERBOSE1("Inserting new value into root node"); + + insert_into_root(splitter); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + + return result; + } + + iterator begin() + { + root_node_iterator_type it = m_root_node.begin(); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("btree: retrieving begin() from the first leaf"); + leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second); + assert(leaf); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return leaf->begin(); + } + + // 'it' points to a node + STXXL_VERBOSE1("btree: retrieving begin() from the first node"); + node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); + assert(node); + iterator result = node->begin(m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + + return result; + } + + const_iterator begin() const + { + root_node_const_iterator_type it = m_root_node.begin(); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("btree: retrieving begin() from the first leaf"); + const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second); + assert(leaf); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return leaf->begin(); + } + + // 'it' points to a node + STXXL_VERBOSE1("btree: retrieving begin() from the first node"); + const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); + assert(node); + const_iterator result = node->begin(m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + iterator end() + { + return m_end_iterator; + } + + const_iterator end() const + { + return m_end_iterator; + } + + data_type& operator [] (const key_type& k) + { + return (*((insert(value_type(k, data_type()))).first)).second; + } + + iterator find(const key_type& k) + { + root_node_iterator_type it = m_root_node.lower_bound(k); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Searching in a leaf"); + leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); + assert(leaf); + iterator result = leaf->find(k); + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + assert(result == end() || result->first == k); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Searching in a node"); + node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); + assert(node); + iterator result = node->find(k, m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + + assert(result == end() || result->first == k); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + const_iterator find(const key_type& k) const + { + root_node_const_iterator_type it = m_root_node.lower_bound(k); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Searching in a leaf"); + const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second, true); + assert(leaf); + const_iterator result = leaf->find(k); + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + assert(result == end() || result->first == k); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Searching in a node"); + const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); + assert(node); + const_iterator result = node->find(k, m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + + assert(result == end() || result->first == k); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + iterator lower_bound(const key_type& k) + { + root_node_iterator_type it = m_root_node.lower_bound(k); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Searching lower bound in a leaf"); + leaf_type* leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); + assert(leaf); + iterator result = leaf->lower_bound(k); + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Searching lower bound in a node"); + node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); + assert(node); + iterator result = node->lower_bound(k, m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + const_iterator lower_bound(const key_type& k) const + { + root_node_const_iterator_type it = m_root_node.lower_bound(k); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Searching lower bound in a leaf"); + const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second, true); + assert(leaf); + const_iterator result = leaf->lower_bound(k); + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Searching lower bound in a node"); + const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); + assert(node); + const_iterator result = node->lower_bound(k, m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + iterator upper_bound(const key_type& k) + { + root_node_iterator_type it = m_root_node.upper_bound(k); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Searching upper bound in a leaf"); + leaf_type* Leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); + assert(Leaf); + iterator result = Leaf->upper_bound(k); + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Searching upper bound in a node"); + node_type* Node = m_node_cache.get_node((node_bid_type)it->second, true); + assert(Node); + iterator result = Node->upper_bound(k, m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + const_iterator upper_bound(const key_type& k) const + { + root_node_const_iterator_type it = m_root_node.upper_bound(k); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Searching upper bound in a leaf"); + const leaf_type* leaf = m_leaf_cache.get_const_node((leaf_bid_type)it->second, true); + assert(leaf); + const_iterator result = leaf->upper_bound(k); + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Searching upper bound in a node"); + const node_type* node = m_node_cache.get_const_node((node_bid_type)it->second, true); + assert(node); + const_iterator result = node->upper_bound(k, m_height - 1); + m_node_cache.unfix_node((node_bid_type)it->second); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return result; + } + + std::pair equal_range(const key_type& k) + { + // l->first >= k + iterator l = lower_bound(k); + + // if (k < l->first) + if (l == end() || m_key_compare(k, l->first)) + // then upper_bound == lower_bound + return std::pair(l, l); + + iterator u = l; + // only one element ==k can exist + ++u; + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + + // then upper_bound == (lower_bound+1) + return std::pair(l, u); + } + + std::pair equal_range(const key_type& k) const + { + // l->first >= k + const_iterator l = lower_bound(k); + + // if (k < l->first) + if (l == end() || m_key_compare(k, l->first)) + // then upper_bound == lower_bound + return std::pair(l, l); + + const_iterator u = l; + // only one element ==k can exist + ++u; + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + // then upper_bound == (lower_bound+1) + return std::pair(l, u); + } + + size_type erase(const key_type& k) + { + root_node_iterator_type it = m_root_node.lower_bound(k); + assert(it != m_root_node.end()); + + if (m_height == 2) // 'it' points to a leaf + { + STXXL_VERBOSE1("Deleting key from a leaf"); + leaf_type* Leaf = m_leaf_cache.get_node((leaf_bid_type)it->second, true); + assert(Leaf); + size_type result = Leaf->erase(k); + m_size -= result; + m_leaf_cache.unfix_node((leaf_bid_type)it->second); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + + if ((!Leaf->underflows()) || m_root_node.size() == 1) + return result; + // no underflow or root has a special degree 1 (too few elements) + + STXXL_VERBOSE1("btree: Fusing or rebalancing a leaf"); + fuse_or_balance(it, m_leaf_cache); + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + + return result; + } + + // 'it' points to a node + STXXL_VERBOSE1("Deleting key from a node"); + assert(m_root_node.size() >= 2); + node_type* node = m_node_cache.get_node((node_bid_type)it->second, true); + assert(node); + size_type result = node->erase(k, m_height - 1); + m_size -= result; + m_node_cache.unfix_node((node_bid_type)it->second); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + if (!node->underflows()) + return result; + // no underflow happened + + STXXL_VERBOSE1("Fusing or rebalancing a node"); + fuse_or_balance(it, m_node_cache); + + if (m_root_node.size() == 1) + { + STXXL_VERBOSE1("btree Root has size 1 and height > 2"); + STXXL_VERBOSE1("btree Deallocate root and decrease height"); + it = m_root_node.begin(); + node_bid_type root_bid = it->second; + assert(it->first == key_compare::max_value()); + node_type* root_node = m_node_cache.get_node(root_bid); + assert(root_node); + assert(root_node->back().first == key_compare::max_value()); + m_root_node.clear(); + m_root_node.insert(root_node->block().begin(), + root_node->block().begin() + root_node->size()); + + m_node_cache.delete_node(root_bid); + --m_height; + STXXL_VERBOSE1("btree Decreasing height to " << m_height); + } + + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + + return result; + } + + size_type count(const key_type& k) + { + if (find(k) == end()) + return 0; + + return 1; + } + + void erase(iterator pos) + { + assert(pos != end()); +#ifndef NDEBUG + size_type old_size = size(); +#endif + + erase(pos->first); + + assert(size() == old_size - 1); + } + + iterator insert(iterator /*pos*/, const value_type& x) + { + // pos ignored in the current version + return insert(x).first; + } + + void clear() + { + deallocate_children(); + + m_root_node.clear(); + + m_size = 0; + m_height = 2, + + create_empty_leaf(); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + } + + template + void insert(InputIterator b, InputIterator e) + { + while (b != e) + { + insert(*(b++)); + } + } + + template + btree(InputIterator begin, + InputIterator end, + const key_compare& c_, + unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes, + bool range_sorted = false, + double node_fill_factor = 0.75, + double leaf_fill_factor = 0.6) + : m_key_compare(c_), + m_node_cache(node_cache_size_in_bytes, this, m_key_compare), + m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), + m_iterator_map(this), + m_size(0), + m_height(2), + m_prefetching_enabled(true), + m_bm(block_manager::get_instance()) + { + STXXL_VERBOSE1("Creating a btree, addr=" << this); + STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); + STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); + + if (range_sorted == false) + { + create_empty_leaf(); + insert(begin, end); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return; + } + + bulk_construction(begin, end, node_fill_factor, leaf_fill_factor); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + } + + template + btree(InputIterator begin, + InputIterator end, + unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes, + bool range_sorted = false, + double node_fill_factor = 0.75, + double leaf_fill_factor = 0.6) + : m_node_cache(node_cache_size_in_bytes, this, m_key_compare), + m_leaf_cache(leaf_cache_size_in_bytes, this, m_key_compare), + m_iterator_map(this), + m_size(0), + m_height(2), + m_prefetching_enabled(true), + m_bm(block_manager::get_instance()) + { + STXXL_VERBOSE1("Creating a btree, addr=" << this); + STXXL_VERBOSE1(" bytes in a node: " << node_bid_type::size); + STXXL_VERBOSE1(" bytes in a leaf: " << leaf_bid_type::size); + + if (range_sorted == false) + { + create_empty_leaf(); + insert(begin, end); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + return; + } + + bulk_construction(begin, end, node_fill_factor, leaf_fill_factor); + assert(m_leaf_cache.nfixed() == 0); + assert(m_node_cache.nfixed() == 0); + } + + void erase(iterator first, iterator last) + { + if (first == begin() && last == end()) + clear(); + else + while (first != last) + erase(first++); + } + + key_compare key_comp() const + { + return m_key_compare; + } + value_compare value_comp() const + { + return value_compare(m_key_compare); + } + + void swap(btree& obj) + { + std::swap(m_key_compare, obj.m_key_compare); // OK + + std::swap(m_node_cache, obj.m_node_cache); // OK + std::swap(m_leaf_cache, obj.m_leaf_cache); // OK + + std::swap(m_iterator_map, obj.m_iterator_map); // must update all iterators + + std::swap(m_end_iterator, obj.m_end_iterator); + std::swap(m_size, obj.m_size); + std::swap(m_height, obj.m_height); + std::swap(m_alloc_strategy, obj.m_alloc_strategy); + std::swap(m_root_node, obj.m_root_node); + } + + void enable_prefetching() + { + m_prefetching_enabled = true; + } + void disable_prefetching() + { + m_prefetching_enabled = false; + } + bool prefetching_enabled() + { + return m_prefetching_enabled; + } + + void print_statistics(std::ostream& o) const + { + o << "Node cache statistics:" << std::endl; + m_node_cache.print_statistics(o); + o << "Leaf cache statistics:" << std::endl; + m_leaf_cache.print_statistics(o); + } + void reset_statistics() + { + m_node_cache.reset_statistics(); + m_leaf_cache.reset_statistics(); + } +}; + +template +inline bool operator == + (const btree& a, + const btree& b) +{ + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); +} + +template +inline bool operator != + (const btree& a, + const btree& b) +{ + return !(a == b); +} + +template +inline bool operator < + (const btree& a, + const btree& b) +{ + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +} + +template +inline bool operator > + (const btree& a, + const btree& b) +{ + return b < a; +} + +template +inline bool operator <= + (const btree& a, + const btree& b) +{ + return !(b < a); +} + +template +inline bool operator >= + (const btree& a, + const btree& b) +{ + return !(a < b); +} + +} // namespace btree + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::btree::btree& a, + stxxl::btree::btree& b) +{ + if (&a != &b) + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_BTREE_BTREE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator.h new file mode 100644 index 0000000000..5854d92a88 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator.h @@ -0,0 +1,382 @@ +/*************************************************************************** + * include/stxxl/bits/containers/btree/iterator.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * + * 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 STXXL_CONTAINERS_BTREE_ITERATOR_HEADER +#define STXXL_CONTAINERS_BTREE_ITERATOR_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace btree { + +template +class iterator_map; +template +class btree_iterator; +template +class btree_const_iterator; +template +class normal_leaf; + +template +class btree_iterator_base +{ +public: + typedef BTreeType btree_type; + typedef typename btree_type::leaf_bid_type bid_type; + typedef typename btree_type::value_type value_type; + typedef typename btree_type::reference reference; + typedef typename btree_type::const_reference const_reference; + typedef std::bidirectional_iterator_tag iterator_category; + typedef typename btree_type::difference_type difference_type; + + typedef typename btree_type::leaf_type leaf_type; + + friend class iterator_map; + template + friend class normal_leaf; + + template + friend bool operator == (const btree_iterator& a, + const btree_const_iterator& b); + template + friend bool operator != (const btree_iterator& a, + const btree_const_iterator& b); + +protected: + btree_type* btree; + bid_type bid; + unsigned_type pos; + + btree_iterator_base() + { + STXXL_VERBOSE3("btree_iterator_base def construct addr=" << this); + make_invalid(); + } + + btree_iterator_base(btree_type* _btree, + const bid_type& _bid, + unsigned_type _pos) + : btree(_btree), bid(_bid), pos(_pos) + { + STXXL_VERBOSE3("btree_iterator_base parameter construct addr=" << this); + btree->m_iterator_map.register_iterator(*this); + } + + void make_invalid() + { + btree = NULL; + pos = 0; + } + + btree_iterator_base(const btree_iterator_base& obj) + { + STXXL_VERBOSE3("btree_iterator_base constr from" << (&obj) << " to " << this); + btree = obj.btree; + bid = obj.bid; + pos = obj.pos; + if (btree) + btree->m_iterator_map.register_iterator(*this); + } + + btree_iterator_base& operator = (const btree_iterator_base& obj) + { + STXXL_VERBOSE3("btree_iterator_base copy from" << (&obj) << " to " << this); + if (&obj != this) + { + if (btree) + btree->m_iterator_map.unregister_iterator(*this); + + btree = obj.btree; + bid = obj.bid; + pos = obj.pos; + if (btree) + btree->m_iterator_map.register_iterator(*this); + } + return *this; + } + + reference non_const_access() + { + assert(btree); + leaf_type* leaf = btree->m_leaf_cache.get_node(bid); + assert(leaf); + return (reference)((*leaf)[pos]); + } + + const_reference const_access() const + { + assert(btree); + leaf_type const* leaf = btree->m_leaf_cache.get_const_node(bid); + assert(leaf); + return (reference)((*leaf)[pos]); + } + + bool operator == (const btree_iterator_base& obj) const + { + return bid == obj.bid && pos == obj.pos && btree == obj.btree; + } + + bool operator != (const btree_iterator_base& obj) const + { + return bid != obj.bid || pos != obj.pos || btree != obj.btree; + } + + btree_iterator_base & increment() + { + assert(btree); + bid_type cur_bid = bid; + const leaf_type* leaf = btree->m_leaf_cache.get_const_node(bid, true); + assert(leaf); + leaf->increment_iterator(*this); + btree->m_leaf_cache.unfix_node(cur_bid); + return *this; + } + + btree_iterator_base & decrement() + { + assert(btree); + bid_type cur_bid = bid; + const leaf_type* leaf = btree->m_leaf_cache.get_const_node(bid, true); + assert(leaf); + leaf->decrement_iterator(*this); + btree->m_leaf_cache.unfix_node(cur_bid); + return *this; + } + +public: + virtual ~btree_iterator_base() + { + STXXL_VERBOSE3("btree_iterator_base deconst " << this); + if (btree) + btree->m_iterator_map.unregister_iterator(*this); + } +}; + +template +class btree_iterator : public btree_iterator_base +{ +public: + typedef BTreeType btree_type; + typedef typename btree_type::leaf_bid_type bid_type; + typedef typename btree_type::value_type value_type; + typedef typename btree_type::reference reference; + typedef typename btree_type::const_reference const_reference; + typedef typename btree_type::pointer pointer; + + typedef btree_iterator_base base_type; + + template + friend class normal_leaf; + + using base_type::non_const_access; + + btree_iterator() + : base_type() + { } + + btree_iterator(const btree_iterator& obj) + : base_type(obj) + { } + + btree_iterator& operator = (const btree_iterator& obj) + { + base_type::operator = (obj); + return *this; + } + + reference operator * () + { + return non_const_access(); + } + + pointer operator -> () + { + return &(non_const_access()); + } + + bool operator == (const btree_iterator& obj) const + { + return base_type::operator == (obj); + } + + bool operator != (const btree_iterator& obj) const + { + return base_type::operator != (obj); + } + + btree_iterator& operator ++ () + { + assert(*this != base_type::btree->end()); + base_type::increment(); + return *this; + } + + btree_iterator& operator -- () + { + base_type::decrement(); + return *this; + } + + btree_iterator operator ++ (int) + { + assert(*this != base_type::btree->end()); + btree_iterator result(*this); + base_type::increment(); + return result; + } + + btree_iterator operator -- (int) + { + btree_iterator result(*this); + base_type::decrement(); + return result; + } + +private: + btree_iterator(btree_type* _btree, + const bid_type& _bid, + unsigned_type _pos) + : base_type(_btree, _bid, _pos) + { } +}; + +template +class btree_const_iterator : public btree_iterator_base +{ +public: + typedef btree_iterator iterator; + + typedef BTreeType btree_type; + typedef typename btree_type::leaf_bid_type bid_type; + typedef typename btree_type::value_type value_type; + typedef typename btree_type::const_reference reference; + typedef typename btree_type::const_pointer pointer; + + typedef btree_iterator_base base_type; + + template + friend class normal_leaf; + + using base_type::const_access; + + btree_const_iterator() + : base_type() + { } + + btree_const_iterator(const btree_const_iterator& obj) + : base_type(obj) + { } + + btree_const_iterator(const iterator& obj) + : base_type(obj) + { } + + btree_const_iterator& operator = (const btree_const_iterator& obj) + { + base_type::operator = (obj); + return *this; + } + + reference operator * () + { + return const_access(); + } + + pointer operator -> () + { + return &(const_access()); + } + + bool operator == (const iterator& obj) const + { + return base_type::operator == (obj); + } + + bool operator != (const iterator& obj) const + { + return base_type::operator != (obj); + } + + bool operator == (const btree_const_iterator& obj) const + { + return base_type::operator == (obj); + } + + bool operator != (const btree_const_iterator& obj) const + { + return base_type::operator != (obj); + } + + btree_const_iterator& operator ++ () + { + assert(*this != base_type::btree->end()); + base_type::increment(); + return *this; + } + + btree_const_iterator& operator -- () + { + base_type::decrement(); + return *this; + } + + btree_const_iterator operator ++ (int) + { + assert(*this != base_type::btree->end()); + btree_const_iterator result(*this); + base_type::increment(); + return result; + } + + btree_const_iterator operator -- (int) + { + btree_const_iterator result(*this); + base_type::decrement(); + return result; + } + +private: + btree_const_iterator(btree_type* _btree, + const bid_type& _bid, + unsigned_type _pos) + : base_type(_btree, _bid, _pos) + { } +}; + +template +inline bool operator == (const btree_iterator& a, + const btree_const_iterator& b) +{ + return a.btree_iterator_base::operator == (b); +} + +template +inline bool operator != (const btree_iterator& a, + const btree_const_iterator& b) +{ + return a.btree_iterator_base::operator != (b); +} + +} // namespace btree + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_BTREE_ITERATOR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator_map.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator_map.h new file mode 100644 index 0000000000..68991e5abf --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/iterator_map.h @@ -0,0 +1,168 @@ +/*************************************************************************** + * include/stxxl/bits/containers/btree/iterator_map.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * + * 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 STXXL_CONTAINERS_BTREE_ITERATOR_MAP_HEADER +#define STXXL_CONTAINERS_BTREE_ITERATOR_MAP_HEADER + +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace btree { + +template +class iterator_map : private noncopyable +{ +public: + typedef BTreeType btree_type; + typedef typename btree_type::leaf_bid_type bid_type; + typedef btree_iterator_base iterator_base; + +private: + struct Key + { + bid_type bid; + unsigned_type pos; + Key() { } + Key(const bid_type& b, unsigned_type p) + : bid(b), pos(p) { } + }; + + struct bid_comp + { + bool operator () (const bid_type& a, const bid_type& b) const + { + return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset); + } + }; + struct KeyCmp + { + bid_comp BIDComp; + bool operator () (const Key& a, const Key& b) const + { + return BIDComp(a.bid, b.bid) || (a.bid == b.bid && a.pos < b.pos); + } + }; + + typedef std::multimap multimap_type; + + multimap_type m_it2addr; + btree_type* m_btree; + + typedef typename multimap_type::value_type pair_type; + typedef typename multimap_type::iterator mmiterator_type; + typedef typename multimap_type::const_iterator mmconst_iterator_type; + + //! changes btree pointer in all contained iterators + void change_btree_pointers(btree_type* b) + { + for (mmconst_iterator_type it = m_it2addr.begin(); + it != m_it2addr.end(); ++it) + { + (it->second)->btree = b; + } + } + +public: + iterator_map(btree_type* b) + : m_btree(b) + { } + + void register_iterator(iterator_base& it) + { + STXXL_VERBOSE2("btree::iterator_map register_iterator addr=" << &it << + " BID: " << it.bid << " POS: " << it.pos); + m_it2addr.insert(pair_type(Key(it.bid, it.pos), &it)); + } + void unregister_iterator(iterator_base& it) + { + STXXL_VERBOSE2("btree::iterator_map unregister_iterator addr=" << &it << + " BID: " << it.bid << " POS: " << it.pos); + assert(!m_it2addr.empty()); + Key key(it.bid, it.pos); + std::pair range = + m_it2addr.equal_range(key); + + assert(range.first != range.second); + + mmiterator_type i = range.first; + for ( ; i != range.second; ++i) + { + assert(it.bid == i->first.bid); + assert(it.pos == i->first.pos); + + if (i->second == &it) + { + // found it + m_it2addr.erase(i); + return; + } + } + + STXXL_THROW2(std::runtime_error, "btree::iterator_map::unregister_iterator", "Panic in btree::iterator_map, can not find and unregister iterator"); + } + template + void find(const bid_type& bid, + unsigned_type first_pos, + unsigned_type last_pos, + OutputContainer& out) + { + Key firstkey(bid, first_pos); + Key lastkey(bid, last_pos); + mmconst_iterator_type begin = m_it2addr.lower_bound(firstkey); + mmconst_iterator_type end = m_it2addr.upper_bound(lastkey); + + for (mmconst_iterator_type i = begin; + i != end; ++i) + { + assert(bid == i->first.bid); + out.push_back(i->second); + } + } + + virtual ~iterator_map() + { + for (mmconst_iterator_type it = m_it2addr.begin(); + it != m_it2addr.end(); ++it) + { + it->second->make_invalid(); + } + } + + void swap(iterator_map& obj) + { + std::swap(m_it2addr, obj.m_it2addr); + change_btree_pointers(m_btree); + obj.change_btree_pointers(obj.m_btree); + } +}; + +} // namespace btree + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::btree::iterator_map& a, + stxxl::btree::iterator_map& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_BTREE_ITERATOR_MAP_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/leaf.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/leaf.h new file mode 100644 index 0000000000..d7c619fc98 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/leaf.h @@ -0,0 +1,699 @@ +/*************************************************************************** + * include/stxxl/bits/containers/btree/leaf.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * + * 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 STXXL_CONTAINERS_BTREE_LEAF_HEADER +#define STXXL_CONTAINERS_BTREE_LEAF_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace btree { + +template +class node_cache; + +template +class normal_leaf : private noncopyable +{ +public: + typedef normal_leaf self_type; + + friend class node_cache; + + typedef KeyType key_type; + typedef DataType data_type; + typedef KeyCmp key_compare; + typedef std::pair value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + + enum { + raw_size = RawSize + }; + + typedef BID bid_type; + + struct metainfo_type + { + bid_type me, pred, succ; + unsigned cur_size; + }; + + typedef typed_block block_type; + enum { + nelements = block_type::size - 1, + max_size = nelements, + min_size = nelements / 2 + }; + + typedef BTreeType btree_type; + typedef typename btree_type::size_type size_type; + typedef btree_iterator_base iterator_base; + typedef btree_iterator iterator; + typedef btree_const_iterator const_iterator; + + typedef node_cache leaf_cache_type; + +public: + struct value_compare : public std::binary_function + { + key_compare comp; + + value_compare(key_compare c) : comp(c) { } + + bool operator () (const value_type& x, const value_type& y) const + { + return comp(x.first, y.first); + } + }; + +private: + block_type* m_block; + btree_type* m_btree; + + key_compare m_cmp; + value_compare m_vcmp; + + void split(std::pair& splitter) + { + bid_type new_bid; + m_btree->m_leaf_cache.get_new_node(new_bid); // new (left) leaf + normal_leaf* new_leaf = m_btree->m_leaf_cache.get_node(new_bid, true); + assert(new_leaf); + + // update links + new_leaf->succ() = my_bid(); + normal_leaf* pred_leaf = NULL; + if (pred().valid()) + { + new_leaf->pred() = pred(); + pred_leaf = m_btree->m_leaf_cache.get_node(pred()); + assert(pred_leaf); + assert(m_vcmp(pred_leaf->back(), front())); + pred_leaf->succ() = new_bid; + } + pred() = new_bid; + + typedef std::vector iterators2fix_type; + iterators2fix_type iterators2fix; + m_btree->m_iterator_map.find(my_bid(), 0, size(), iterators2fix); + + const unsigned end_of_smaller_part = size() / 2; + + splitter.first = ((*m_block)[end_of_smaller_part - 1]).first; + splitter.second = new_bid; + + const unsigned old_size = size(); + // copy the smaller part + std::copy(m_block->begin(), m_block->begin() + end_of_smaller_part, + new_leaf->m_block->begin()); + new_leaf->m_block->info.cur_size = end_of_smaller_part; + // copy the larger part + std::copy(m_block->begin() + end_of_smaller_part, + m_block->begin() + old_size, m_block->begin()); + m_block->info.cur_size = old_size - end_of_smaller_part; + assert(size() + new_leaf->size() == old_size); + + // fix iterators + for (typename iterators2fix_type::iterator it2fix = iterators2fix.begin(); + it2fix != iterators2fix.end(); ++it2fix) + { + m_btree->m_iterator_map.unregister_iterator(**it2fix); + + if ((*it2fix)->pos < end_of_smaller_part) // belongs to the smaller part + (*it2fix)->bid = new_bid; + + else + (*it2fix)->pos -= end_of_smaller_part; + + m_btree->m_iterator_map.register_iterator(**it2fix); + } + + STXXL_VERBOSE1("btree::normal_leaf split leaf " << this + << " splitter: " << splitter.first); + +#if STXXL_VERBOSE_LEVEL >= 1 + if (pred_leaf) + { + STXXL_VERBOSE1("btree::normal_leaf pred_part.smallest = " << pred_leaf->front().first); + STXXL_VERBOSE1("btree::normal_leaf pred_part.largest = " << pred_leaf->back().first); + } +#endif + STXXL_VERBOSE1("btree::normal_leaf smaller_part.smallest = " << new_leaf->front().first); + STXXL_VERBOSE1("btree::normal_leaf smaller_part.largest = " << new_leaf->back().first); + STXXL_VERBOSE1("btree::normal_leaf larger_part.smallest = " << front().first); + STXXL_VERBOSE1("btree::normal_leaf larger_part.largest = " << back().first); + + m_btree->m_leaf_cache.unfix_node(new_bid); + } + +public: + virtual ~normal_leaf() + { + delete m_block; + } + + normal_leaf(btree_type* btree, + key_compare cmp) + : m_block(new block_type), + m_btree(btree), + m_cmp(cmp), + m_vcmp(cmp) + { + assert(min_nelements() >= 2); + assert(2 * min_nelements() - 1 <= max_nelements()); + assert(max_nelements() <= nelements); + assert(unsigned(block_type::size) >= nelements + 1); // extra space for an overflow + } + + bool overflows() const { return m_block->info.cur_size > max_nelements(); } + bool underflows() const { return m_block->info.cur_size < min_nelements(); } + + static unsigned max_nelements() { return max_size; } + static unsigned min_nelements() { return min_size; } + + bid_type & succ() + { + return m_block->info.succ; + } + bid_type & pred() + { + return m_block->info.pred; + } + + const bid_type & succ() const + { + return m_block->info.succ; + } + const bid_type & pred() const + { + return m_block->info.pred; + } + + /* + template + normal_leaf(InputIterator begin_, InputIterator end_, + btree_type * btree, + key_compare cmp): + m_block(new block_type), + m_btree(btree), + m_cmp(cmp), + m_vcmp(cmp) + { + assert(min_nelements() >=2); + assert(2*min_nelements() - 1 <= max_nelements()); + assert(max_nelements() <= nelements); + assert(unsigned(block_type::size) >= nelements +1); // extra space for an overflow element + + unsigned new_size = end_ - begin_; + assert(new_size <= max_nelements()); + assert(new_size >= min_nelements()); + + std::copy(begin_,end_,m_block->begin()); + assert(stxxl::is_sorted(m_block->begin(),m_block->begin() + new_size, m_vcmp)); + m_block->info.cur_size = new_size; + }*/ + + unsigned size() const + { + return m_block->info.cur_size; + } + + const bid_type & my_bid() const + { + return m_block->info.me; + } + + void save() + { + request_ptr req = m_block->write(my_bid()); + req->wait(); + } + + request_ptr load(const bid_type& bid) + { + request_ptr req = m_block->read(bid); + req->wait(); + assert(bid == my_bid()); + return req; + } + + request_ptr prefetch(const bid_type& bid) + { + return m_block->read(bid); + } + + void init(const bid_type& my_bid_) + { + m_block->info.me = my_bid_; + m_block->info.succ = bid_type(); + m_block->info.pred = bid_type(); + m_block->info.cur_size = 0; + } + + reference operator [] (unsigned_type i) + { + return (*m_block)[i]; + } + + const_reference operator [] (unsigned_type i) const + { + return (*m_block)[i]; + } + + reference back() + { + return (*m_block)[size() - 1]; + } + + reference front() + { + return *(m_block->begin()); + } + + const_reference back() const + { + return (*m_block)[size() - 1]; + } + + const_reference front() const + { + return *(m_block->begin()); + } + + void dump() + { + STXXL_VERBOSE2("Dump of leaf " << this); + for (unsigned i = 0; i < size(); ++i) + STXXL_VERBOSE2((*this)[i].first << " " << (*this)[i].second); + } + + std::pair insert( + const value_type& x, + std::pair& splitter) + { + assert(size() <= max_nelements()); + splitter.first = key_compare::max_value(); + + typename block_type::iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), x, m_vcmp); + + if (!(m_vcmp(*it, x) || m_vcmp(x, *it)) && it != (m_block->begin() + size())) // *it == x + { + // already exists + return std::pair( + iterator(m_btree, my_bid(), unsigned(it - m_block->begin())), + false); + } + + typename block_type::iterator cur = m_block->begin() + size() - 1; + + for ( ; cur >= it; --cur) + *(cur + 1) = *cur; + // move elements to make space for the new element + + *it = x; + + std::vector iterators2fix; + m_btree->m_iterator_map.find(my_bid(), unsigned(it - m_block->begin()), size(), iterators2fix); + typename std::vector::iterator it2fix = iterators2fix.begin(); + for ( ; it2fix != iterators2fix.end(); ++it2fix) + { + m_btree->m_iterator_map.unregister_iterator(**it2fix); + ++((*it2fix)->pos); // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + + ++(m_block->info.cur_size); + + std::pair result(iterator(m_btree, my_bid(), unsigned(it - m_block->begin())), true); + + if (size() <= max_nelements()) + { + // no overflow + dump(); + return result; + } + + // overflow + + split(splitter); + + return result; + } + + iterator begin() + { + return iterator(m_btree, my_bid(), 0); + } + + const_iterator begin() const + { + return const_iterator(m_btree, my_bid(), 0); + } + + iterator end() + { + return iterator(m_btree, my_bid(), size()); + } + + void increment_iterator(iterator_base& it) const + { + assert(it.bid == my_bid()); + assert(it.pos != size()); + + m_btree->m_iterator_map.unregister_iterator(it); + + ++(it.pos); + if (it.pos == size() && succ().valid()) + { + // run to the end of the leaf + STXXL_VERBOSE1("btree::normal_leaf jumping to the next block"); + it.pos = 0; + it.bid = succ(); + } + // increment of pos from 0 to 1 + else if (it.pos == 1 && m_btree->m_prefetching_enabled) + { + // prefetch the succ leaf + if (succ().valid()) + m_btree->m_leaf_cache.prefetch_node(succ()); + } + m_btree->m_iterator_map.register_iterator(it); + } + + void decrement_iterator(iterator_base& it) const + { + assert(it.bid == my_bid()); + + m_btree->m_iterator_map.unregister_iterator(it); + + if (it.pos == 0) + { + assert(pred().valid()); + + it.bid = pred(); + normal_leaf const* pred_leaf = m_btree->m_leaf_cache.get_const_node(pred(), true); + assert(pred_leaf); + it.pos = pred_leaf->size() - 1; + + // prefetch the pred leaf of pred_leaf + if (m_btree->m_prefetching_enabled && pred_leaf->pred().valid()) + m_btree->m_leaf_cache.prefetch_node(pred_leaf->pred()); + + m_btree->m_leaf_cache.unfix_node(pred()); + } + else + --it.pos; + + m_btree->m_iterator_map.register_iterator(it); + } + + iterator find(const key_type& k) + { + value_type search_val(k, data_type()); + typename block_type::iterator lb = + std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); + if (lb == m_block->begin() + size() || lb->first != k) + return m_btree->end(); + + return iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); + } + + const_iterator find(const key_type& k) const + { + value_type search_val(k, data_type()); + typename block_type::iterator lb = + std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); + if (lb == m_block->begin() + size() || lb->first != k) + return m_btree->end(); + + return const_iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); + } + + iterator lower_bound(const key_type& k) + { + value_type search_val(k, data_type()); + + typename block_type::iterator lb = + std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); + + // lower_bound is in the succ block + if (lb == m_block->begin() + size() && succ().valid()) + { + return iterator(m_btree, succ(), 0); + } + + return iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); + } + + const_iterator lower_bound(const key_type& k) const + { + value_type search_val(k, data_type()); + typename block_type::iterator lb = + std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); + + // lower_bound is in the succ block + if (lb == m_block->begin() + size() && succ().valid()) + { + return iterator(m_btree, succ(), 0); + } + + return const_iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); + } + + iterator upper_bound(const key_type& k) + { + value_type search_val(k, data_type()); + typename block_type::iterator lb = + std::upper_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); + + // upper_bound is in the succ block + if (lb == m_block->begin() + size() && succ().valid()) + { + return iterator(m_btree, succ(), 0); + } + + return iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); + } + + const_iterator upper_bound(const key_type& k) const + { + value_type search_val(k, data_type()); + typename block_type::iterator lb = + std::upper_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); + + // upper_bound is in the succ block + if (lb == m_block->begin() + size() && succ().valid()) + { + return const_iterator(m_btree, succ(), 0); + } + + return const_iterator(m_btree, my_bid(), unsigned(lb - m_block->begin())); + } + + size_type erase(const key_type& k) + { + value_type search_val(k, data_type()); + typename block_type::iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), search_val, m_vcmp); + + if (it == m_block->begin() + size() || it->first != k) + return 0; + // no such element + + // move elements one position left + std::copy(it + 1, m_block->begin() + size(), it); + + std::vector iterators2fix; + m_btree->m_iterator_map.find(my_bid(), unsigned(it + 1 - m_block->begin()), size(), iterators2fix); + typename std::vector::iterator it2fix = iterators2fix.begin(); + for ( ; it2fix != iterators2fix.end(); ++it2fix) + { + STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << " (pos--)"); + m_btree->m_iterator_map.unregister_iterator(**it2fix); + --((*it2fix)->pos); // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + + --(m_block->info.cur_size); + + return 1; + } + + void fuse(const normal_leaf& src) + { + STXXL_VERBOSE1("btree::normal_leaf Fusing"); + assert(m_vcmp(src.back(), front())); + const unsigned src_size = src.size(); + + typename block_type::iterator cur = m_block->begin() + size() - 1; + typename block_type::const_iterator begin = m_block->begin(); + + for ( ; cur >= begin; --cur) + *(cur + src_size) = *cur; + // move elements to make space for Src elements + + // copy Src to *this leaf + std::copy(src.m_block->begin(), src.m_block->begin() + src_size, m_block->begin()); + + std::vector iterators2fix; + m_btree->m_iterator_map.find(my_bid(), 0, size(), iterators2fix); + typename std::vector::iterator it2fix = iterators2fix.begin(); + for ( ; it2fix != iterators2fix.end(); ++it2fix) + { + STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << + " (pos+" << src_size << ")"); + m_btree->m_iterator_map.unregister_iterator(**it2fix); + ((*it2fix)->pos) += src_size; // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + + iterators2fix.clear(); + m_btree->m_iterator_map.find(src.my_bid(), 0, src_size, iterators2fix); + for (it2fix = iterators2fix.begin(); it2fix != iterators2fix.end(); ++it2fix) + { + STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << + " (bid=" << my_bid() << ")"); + m_btree->m_iterator_map.unregister_iterator(**it2fix); + ((*it2fix)->bid) = my_bid(); // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + + m_block->info.cur_size += src_size; + + // update links + pred() = src.pred(); + if (pred().valid()) + { // update successor link + normal_leaf* new_pred = m_btree->m_leaf_cache.get_node(pred()); + assert(new_pred); + new_pred->succ() = my_bid(); + } + } + + key_type balance(normal_leaf& left) + { + STXXL_VERBOSE1("btree::normal_leaf Balancing leaves with bids " << + left.my_bid() << " and " << my_bid()); + const unsigned total_size = left.size() + size(); + unsigned new_left_size = total_size / 2; + assert(new_left_size <= left.max_nelements()); + assert(new_left_size >= left.min_nelements()); + unsigned new_right_size = total_size - new_left_size; + assert(new_right_size <= max_nelements()); + assert(new_right_size >= min_nelements()); + + assert(m_vcmp(left.back(), front()) || size() == 0); + + if (new_left_size < left.size()) + { + // #elements to move from left to *this + const unsigned nEl2Move = left.size() - new_left_size; + + typename block_type::iterator cur = m_block->begin() + size() - 1; + typename block_type::const_iterator begin = m_block->begin(); + + for ( ; cur >= begin; --cur) + *(cur + nEl2Move) = *cur; + // move elements to make space for Src elements + + // copy Left to *this leaf + std::copy(left.m_block->begin() + new_left_size, + left.m_block->begin() + left.size(), m_block->begin()); + + std::vector iterators2fix1; + std::vector iterators2fix2; + m_btree->m_iterator_map.find(my_bid(), 0, size(), iterators2fix1); + m_btree->m_iterator_map.find(left.my_bid(), new_left_size, left.size(), iterators2fix2); + + typename std::vector::iterator it2fix = iterators2fix1.begin(); + for ( ; it2fix != iterators2fix1.end(); ++it2fix) + { + STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << + " (pos+" << nEl2Move << ")"); + m_btree->m_iterator_map.unregister_iterator(**it2fix); + ((*it2fix)->pos) += nEl2Move; // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + + it2fix = iterators2fix2.begin(); + for ( ; it2fix != iterators2fix2.end(); ++it2fix) + { + STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << + " (pos-" << new_left_size << " bid=" << my_bid() << ")"); + m_btree->m_iterator_map.unregister_iterator(**it2fix); + ((*it2fix)->bid) = my_bid(); // fixing iterators + ((*it2fix)->pos) -= new_left_size; // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + } + else + { + assert(new_right_size < size()); + + const unsigned nEl2Move = size() - new_right_size; // #elements to move from *this to Left + + // copy *this to Left + std::copy(m_block->begin(), + m_block->begin() + nEl2Move, left.m_block->begin() + left.size()); + // move elements in *this + std::copy(m_block->begin() + nEl2Move, + m_block->begin() + size(), m_block->begin()); + + std::vector iterators2fix1; + std::vector iterators2fix2; + m_btree->m_iterator_map.find(my_bid(), nEl2Move, size(), iterators2fix1); + m_btree->m_iterator_map.find(my_bid(), 0, nEl2Move - 1, iterators2fix2); + + typename std::vector::iterator it2fix = iterators2fix1.begin(); + for ( ; it2fix != iterators2fix1.end(); ++it2fix) + { + STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << + " (pos-" << nEl2Move << ")"); + m_btree->m_iterator_map.unregister_iterator(**it2fix); + ((*it2fix)->pos) -= nEl2Move; // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + + it2fix = iterators2fix2.begin(); + for ( ; it2fix != iterators2fix2.end(); ++it2fix) + { + STXXL_VERBOSE2("btree::normal_leaf updating iterator " << (*it2fix) << + " (pos+" << left.size() << " bid=" << left.my_bid() << ")"); + m_btree->m_iterator_map.unregister_iterator(**it2fix); + ((*it2fix)->bid) = left.my_bid(); // fixing iterators + ((*it2fix)->pos) += left.size(); // fixing iterators + m_btree->m_iterator_map.register_iterator(**it2fix); + } + } + + m_block->info.cur_size = new_right_size; // update size + left.m_block->info.cur_size = new_left_size; // update size + + return left.back().first; + } + + void push_back(const value_type& x) + { + (*this)[size()] = x; + ++(m_block->info.cur_size); + } +}; + +} // namespace btree + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_BTREE_LEAF_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node.h new file mode 100644 index 0000000000..13dec7bf96 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node.h @@ -0,0 +1,761 @@ +/*************************************************************************** + * include/stxxl/bits/containers/btree/node.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * + * 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 STXXL_CONTAINERS_BTREE_NODE_HEADER +#define STXXL_CONTAINERS_BTREE_NODE_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace btree { + +template +class node_cache; + +template +class normal_node : private noncopyable +{ +public: + typedef normal_node self_type; + + friend class node_cache; + + typedef KeyType key_type; + typedef KeyCmp key_compare; + + enum { + raw_size = RawSize + }; + typedef BID bid_type; + typedef bid_type node_bid_type; + typedef self_type node_type; + typedef std::pair value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + + struct metainfo_type + { + bid_type me; + unsigned cur_size; + }; + typedef typed_block block_type; + + enum { + nelements = block_type::size - 1, + max_size = nelements, + min_size = nelements / 2 + }; + typedef typename block_type::iterator block_iterator; + typedef typename block_type::const_iterator block_const_iterator; + + typedef BTreeType btree_type; + typedef typename btree_type::size_type size_type; + typedef typename btree_type::iterator iterator; + typedef typename btree_type::const_iterator const_iterator; + + typedef typename btree_type::value_type btree_value_type; + typedef typename btree_type::leaf_bid_type leaf_bid_type; + typedef typename btree_type::leaf_type leaf_type; + + typedef node_cache node_cache_type; + +private: + struct value_compare : public std::binary_function + { + key_compare comp; + + value_compare(key_compare c) : comp(c) { } + + bool operator () (const value_type& x, const value_type& y) const + { + return comp(x.first, y.first); + } + }; + + block_type* m_block; + btree_type* m_btree; + key_compare m_cmp; + value_compare m_vcmp; + + std::pair insert(const std::pair& splitter, + const block_iterator& place2insert) + { + std::pair result(key_compare::max_value(), bid_type()); + + // splitter != *place2insert + assert(m_vcmp(*place2insert, splitter) || m_vcmp(splitter, *place2insert)); + + block_iterator cur = m_block->begin() + size() - 1; + for ( ; cur >= place2insert; --cur) + *(cur + 1) = *cur; + // copy elements to make space for the new element + + *place2insert = splitter; // insert + + ++(m_block->info.cur_size); + + if (size() > max_nelements()) // overflow! need to split + { + STXXL_VERBOSE1("btree::normal_node::insert overflow happened, splitting"); + + bid_type new_bid; + m_btree->m_node_cache.get_new_node(new_bid); // new (left) node + normal_node* new_node = m_btree->m_node_cache.get_node(new_bid, true); + assert(new_node); + + const unsigned end_of_smaller_part = size() / 2; + + result.first = ((*m_block)[end_of_smaller_part - 1]).first; + result.second = new_bid; + + const unsigned old_size = size(); + // copy the smaller part + std::copy(m_block->begin(), m_block->begin() + end_of_smaller_part, new_node->m_block->begin()); + new_node->m_block->info.cur_size = end_of_smaller_part; + // copy the larger part + std::copy(m_block->begin() + end_of_smaller_part, + m_block->begin() + old_size, m_block->begin()); + m_block->info.cur_size = old_size - end_of_smaller_part; + assert(size() + new_node->size() == old_size); + + m_btree->m_node_cache.unfix_node(new_bid); + + STXXL_VERBOSE1("btree::normal_node split leaf " << this + << " splitter: " << result.first); + } + + return result; + } + + template + void fuse_or_balance(block_iterator UIt, CacheType& cache) + { + typedef typename CacheType::node_type local_node_type; + typedef typename local_node_type::bid_type local_bid_type; + + block_iterator leftIt, rightIt; + if (UIt == (m_block->begin() + size() - 1)) // UIt is the last entry in the root + { + assert(UIt != m_block->begin()); + rightIt = UIt; + leftIt = --UIt; + } + else + { + leftIt = UIt; + rightIt = ++UIt; + assert(rightIt != (m_block->begin() + size())); + } + + // now fuse or balance nodes pointed by leftIt and rightIt + local_bid_type left_bid = (local_bid_type)leftIt->second; + local_bid_type right_bid = (local_bid_type)rightIt->second; + local_node_type* left_node = cache.get_node(left_bid, true); + local_node_type* right_node = cache.get_node(right_bid, true); + + const unsigned total_size = left_node->size() + right_node->size(); + if (total_size <= right_node->max_nelements()) + { + // --- fuse --- + + // add the content of left_node to right_node + right_node->fuse(*left_node); + + cache.unfix_node(right_bid); + // 'delete_node' unfixes left-bid also + cache.delete_node(left_bid); + + // delete left BID from the root + std::copy(leftIt + 1, m_block->begin() + size(), leftIt); + --(m_block->info.cur_size); + } + else + { + // --- balance --- + + key_type new_splitter = right_node->balance(*left_node); + + // change key + leftIt->first = new_splitter; + assert(m_vcmp(*leftIt, *rightIt)); + + cache.unfix_node(left_bid); + cache.unfix_node(right_bid); + } + } + +public: + virtual ~normal_node() + { + delete m_block; + } + + normal_node(btree_type* btree, + key_compare cmp) + : m_block(new block_type), + m_btree(btree), + m_cmp(cmp), + m_vcmp(cmp) + { + assert(min_nelements() >= 2); + assert(2 * min_nelements() - 1 <= max_nelements()); + assert(max_nelements() <= nelements); + // extra space for an overflow + assert(unsigned(block_type::size) >= nelements + 1); + } + + block_type & block() + { + return *m_block; + } + + bool overflows() const { return m_block->info.cur_size > max_nelements(); } + bool underflows() const { return m_block->info.cur_size < min_nelements(); } + + static unsigned max_nelements() { return max_size; } + static unsigned min_nelements() { return min_size; } + + /* + template + normal_node(InputIterator begin_, InputIterator end_, + btree_type * btree, + key_compare cmp): + m_block(new block_type), + m_btree(btree), + m_cmp(cmp), + m_vcmp(cmp) + { + assert(min_nelements() >=2); + assert(2*min_nelements() - 1 <= max_nelements()); + assert(max_nelements() <= nelements); + assert(unsigned(block_type::size) >= nelements +1); // extra space for an overflow + + unsigned new_size = end_ - begin_; + assert(new_size <= max_nelements()); + assert(new_size >= min_nelements()); + + std::copy(begin_,end_,m_block->begin()); + assert(stxxl::is_sorted(m_block->begin(),m_block->begin() + new_size, m_vcmp)); + m_block->info.cur_size = new_size; + }*/ + + unsigned size() const + { + return m_block->info.cur_size; + } + + bid_type my_bid() const + { + return m_block->info.me; + } + + void save() + { + request_ptr req = m_block->write(my_bid()); + req->wait(); + } + + request_ptr load(const bid_type& bid) + { + request_ptr req = m_block->read(bid); + req->wait(); + assert(bid == my_bid()); + return req; + } + + request_ptr prefetch(const bid_type& bid) + { + return m_block->read(bid); + } + + void init(const bid_type& my_bid_) + { + m_block->info.me = my_bid_; + m_block->info.cur_size = 0; + } + + reference operator [] (int i) + { + return (*m_block)[i]; + } + + const_reference operator [] (int i) const + { + return (*m_block)[i]; + } + + reference back() + { + return (*m_block)[size() - 1]; + } + + reference front() + { + return *(m_block->begin()); + } + + const_reference back() const + { + return (*m_block)[size() - 1]; + } + + const_reference front() const + { + return *(m_block->begin()); + } + + std::pair + insert(const btree_value_type& x, unsigned height, + std::pair& splitter) + { + assert(size() <= max_nelements()); + splitter.first = key_compare::max_value(); + + value_type key2search(x.first, bid_type()); + block_iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + //bid_type found_bid = it->second; + + if (height == 2) // found_bid points to a leaf + { + STXXL_VERBOSE1("btree::normal_node Inserting new value into a leaf"); + leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)it->second, true); + assert(leaf); + std::pair bot_splitter; + std::pair result = leaf->insert(x, bot_splitter); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)it->second); + //if(key_compare::max_value() == BotSplitter.first) + if (!(m_cmp(key_compare::max_value(), bot_splitter.first) || + m_cmp(bot_splitter.first, key_compare::max_value()))) + return result; + // no overflow/splitting happened + + STXXL_VERBOSE1("btree::normal_node Inserting new value in *this"); + + splitter = insert(std::make_pair(bot_splitter.first, bid_type(bot_splitter.second)), it); + + return result; + } + else + { // found_bid points to a node + STXXL_VERBOSE1("btree::normal_node Inserting new value into a node"); + node_type* node = m_btree->m_node_cache.get_node((node_bid_type)it->second, true); + assert(node); + std::pair bot_splitter; + std::pair result = node->insert(x, height - 1, bot_splitter); + m_btree->m_node_cache.unfix_node((node_bid_type)it->second); + //if(key_compare::max_value() == BotSplitter.first) + if (!(m_cmp(key_compare::max_value(), bot_splitter.first) || + m_cmp(bot_splitter.first, key_compare::max_value()))) + return result; + // no overflow/splitting happened + + STXXL_VERBOSE1("btree::normal_node Inserting new value in *this"); + + splitter = insert(bot_splitter, it); + + return result; + } + } + + iterator begin(unsigned height) + { + bid_type first_bid = m_block->begin()->second; + if (height == 2) // FirstBid points to a leaf + { + assert(size() > 1); + STXXL_VERBOSE1("btree::node retrieveing begin() from the first leaf"); + leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)first_bid); + assert(leaf); + return leaf->begin(); + } + else + { // FirstBid points to a node + STXXL_VERBOSE1("btree: retrieveing begin() from the first node"); + node_type* node = m_btree->m_node_cache.get_node((node_bid_type)first_bid, true); + assert(node); + iterator result = node->begin(height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)first_bid); + return result; + } + } + + const_iterator begin(unsigned height) const + { + bid_type FirstBid = m_block->begin()->second; + if (height == 2) // FirstBid points to a leaf + { + assert(size() > 1); + STXXL_VERBOSE1("btree::node retrieveing begin() from the first leaf"); + const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)FirstBid); + assert(leaf); + return leaf->begin(); + } + else + { // FirstBid points to a node + STXXL_VERBOSE1("btree: retrieveing begin() from the first node"); + const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)FirstBid, true); + assert(node); + const_iterator result = node->begin(height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)FirstBid); + return result; + } + } + + iterator find(const key_type& k, unsigned height) + { + value_type key2search(k, bid_type()); + + block_iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + bid_type found_bid = it->second; + + if (height == 2) // found_bid points to a leaf + { + STXXL_VERBOSE1("Searching in a leaf"); + leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); + assert(leaf); + iterator result = leaf->find(k); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); + + return result; + } + + // found_bid points to a node + STXXL_VERBOSE1("Searching in a node"); + node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); + assert(node); + iterator result = node->find(k, height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); + + return result; + } + + const_iterator find(const key_type& k, unsigned height) const + { + value_type key2search(k, bid_type()); + + block_iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + bid_type found_bid = it->second; + + if (height == 2) // found_bid points to a leaf + { + STXXL_VERBOSE1("Searching in a leaf"); + const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)found_bid, true); + assert(leaf); + const_iterator result = leaf->find(k); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); + + return result; + } + + // found_bid points to a node + STXXL_VERBOSE1("Searching in a node"); + const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)found_bid, true); + assert(node); + const_iterator result = node->find(k, height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); + + return result; + } + + iterator lower_bound(const key_type& k, unsigned height) + { + value_type key2search(k, bid_type()); + assert(!m_vcmp(back(), key2search)); + block_iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + bid_type found_bid = it->second; + + if (height == 2) // found_bid points to a leaf + { + STXXL_VERBOSE1("Searching lower bound in a leaf"); + leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); + assert(leaf); + iterator result = leaf->lower_bound(k); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); + + return result; + } + + // found_bid points to a node + STXXL_VERBOSE1("Searching lower bound in a node"); + node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); + assert(node); + iterator result = node->lower_bound(k, height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); + + return result; + } + + const_iterator lower_bound(const key_type& k, unsigned height) const + { + value_type key2search(k, bid_type()); + assert(!m_vcmp(back(), key2search)); + block_iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + bid_type found_bid = it->second; + + if (height == 2) // found_bid points to a leaf + { + STXXL_VERBOSE1("Searching lower bound in a leaf"); + const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)found_bid, true); + assert(leaf); + const_iterator result = leaf->lower_bound(k); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); + + return result; + } + + // found_bid points to a node + STXXL_VERBOSE1("Searching lower bound in a node"); + const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)found_bid, true); + assert(node); + const_iterator result = node->lower_bound(k, height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); + + return result; + } + + iterator upper_bound(const key_type& k, unsigned height) + { + value_type key2search(k, bid_type()); + assert(m_vcmp(key2search, back())); + block_iterator it = + std::upper_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + bid_type found_bid = it->second; + + if (height == 2) // found_bid points to a leaf + { + STXXL_VERBOSE1("Searching upper bound in a leaf"); + leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); + assert(leaf); + iterator result = leaf->upper_bound(k); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); + + return result; + } + + // found_bid points to a node + STXXL_VERBOSE1("Searching upper bound in a node"); + node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); + assert(node); + iterator result = node->upper_bound(k, height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); + + return result; + } + + const_iterator upper_bound(const key_type& k, unsigned height) const + { + value_type key2search(k, bid_type()); + assert(m_vcmp(key2search, back())); + block_iterator it = + std::upper_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + bid_type found_bid = it->second; + + if (height == 2) // found_bid points to a leaf + { + STXXL_VERBOSE1("Searching upper bound in a leaf"); + const leaf_type* leaf = m_btree->m_leaf_cache.get_const_node((leaf_bid_type)found_bid, true); + assert(leaf); + const_iterator result = leaf->upper_bound(k); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)found_bid); + + return result; + } + + // found_bid points to a node + STXXL_VERBOSE1("Searching upper bound in a node"); + const node_type* node = m_btree->m_node_cache.get_const_node((node_bid_type)found_bid, true); + assert(node); + const_iterator result = node->upper_bound(k, height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); + + return result; + } + + void fuse(const normal_node& src) + { + assert(m_vcmp(src.back(), front())); + const unsigned src_size = src.size(); + + block_iterator cur = m_block->begin() + size() - 1; + block_const_iterator begin = m_block->begin(); + + for ( ; cur >= begin; --cur) + *(cur + src_size) = *cur; + // move elements to make space for Src elements + + // copy Src to *this leaf + std::copy(src.m_block->begin(), src.m_block->begin() + src_size, m_block->begin()); + + m_block->info.cur_size += src_size; + } + + key_type balance(normal_node& left, bool check_constraints = true) + { + const unsigned total_size = left.size() + size(); + unsigned new_left_size = total_size / 2; + STXXL_ASSERT(!check_constraints || new_left_size <= left.max_nelements()); + STXXL_ASSERT(!check_constraints || new_left_size >= left.min_nelements()); + unsigned new_right_size = total_size - new_left_size; + STXXL_ASSERT(!check_constraints || new_right_size <= max_nelements()); + STXXL_ASSERT(!check_constraints || new_right_size >= min_nelements()); + + assert(m_vcmp(left.back(), front()) || size() == 0); + + if (new_left_size < left.size()) + { + // #elements to move from left to *this + const unsigned nEl2Move = left.size() - new_left_size; + + block_iterator cur = m_block->begin() + size() - 1; + block_const_iterator begin = m_block->begin(); + + for ( ; cur >= begin; --cur) + *(cur + nEl2Move) = *cur; + // move elements to make space for Src elements + + // copy left to *this leaf + std::copy(left.m_block->begin() + new_left_size, + left.m_block->begin() + left.size(), m_block->begin()); + } + else + { + assert(new_right_size < size()); + + // #elements to move from *this to left + const unsigned nEl2Move = size() - new_right_size; + + // copy *this to left + std::copy(m_block->begin(), + m_block->begin() + nEl2Move, left.m_block->begin() + left.size()); + // move elements in *this + std::copy(m_block->begin() + nEl2Move, + m_block->begin() + size(), m_block->begin()); + } + + m_block->info.cur_size = new_right_size; // update size + left.m_block->info.cur_size = new_left_size; // update size + + return left.back().first; + } + + size_type erase(const key_type& k, unsigned height) + { + value_type key2search(k, bid_type()); + + block_iterator it = + std::lower_bound(m_block->begin(), m_block->begin() + size(), key2search, m_vcmp); + + assert(it != (m_block->begin() + size())); + + bid_type found_bid = it->second; + + assert(size() >= 2); + + if (height == 2) // 'found_bid' points to a leaf + { + STXXL_VERBOSE1("btree::normal_node Deleting key from a leaf"); + leaf_type* leaf = m_btree->m_leaf_cache.get_node((leaf_bid_type)found_bid, true); + assert(leaf); + size_type result = leaf->erase(k); + m_btree->m_leaf_cache.unfix_node((leaf_bid_type)it->second); + if (!leaf->underflows()) + return result; + // no underflow or root has a special degree 1 (too few elements) + + STXXL_VERBOSE1("btree::normal_node Fusing or rebalancing a leaf"); + fuse_or_balance(it, m_btree->m_leaf_cache); + + return result; + } + + // 'found_bid' points to a node + STXXL_VERBOSE1("btree::normal_node Deleting key from a node"); + node_type* node = m_btree->m_node_cache.get_node((node_bid_type)found_bid, true); + assert(node); + size_type result = node->erase(k, height - 1); + m_btree->m_node_cache.unfix_node((node_bid_type)found_bid); + if (!node->underflows()) + return result; + // no underflow happened + + STXXL_VERBOSE1("btree::normal_node Fusing or rebalancing a node"); + fuse_or_balance(it, m_btree->m_node_cache); + + return result; + } + + void deallocate_children(unsigned height) + { + if (height == 2) + { + // we have children leaves here + for (block_const_iterator it = block().begin(); + it != block().begin() + size(); ++it) + { + // delete from leaf cache and deallocate bid + m_btree->m_leaf_cache.delete_node((leaf_bid_type)it->second); + } + } + else + { + for (block_const_iterator it = block().begin(); + it != block().begin() + size(); ++it) + { + node_type* node = m_btree->m_node_cache.get_node((node_bid_type)it->second); + assert(node); + node->deallocate_children(height - 1); + // delete from node cache and deallocate bid + m_btree->m_node_cache.delete_node((node_bid_type)it->second); + } + } + } + + void push_back(const value_type& x) + { + (*this)[size()] = x; + ++(m_block->info.cur_size); + } +}; + +} // namespace btree + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_BTREE_NODE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node_cache.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node_cache.h new file mode 100644 index 0000000000..69cbfd365d --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/node_cache.h @@ -0,0 +1,624 @@ +/*************************************************************************** + * include/stxxl/bits/containers/btree/node_cache.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * + * 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 STXXL_CONTAINERS_BTREE_NODE_CACHE_HEADER +#define STXXL_CONTAINERS_BTREE_NODE_CACHE_HEADER + +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +#define STXXL_BTREE_CACHE_VERBOSE STXXL_VERBOSE2 + +// TODO: speedup BID2node_ access using search result iterator in the methods + +namespace btree { + +template +class node_cache : private noncopyable +{ +public: + typedef BTreeType btree_type; + typedef NodeType node_type; + typedef typename node_type::block_type block_type; + typedef typename node_type::bid_type bid_type; + typedef typename btree_type::key_compare key_compare; + + typedef typename btree_type::alloc_strategy_type alloc_strategy_type; + typedef stxxl::lru_pager<> pager_type; + +private: + btree_type* m_btree; + key_compare m_cmp; + +/* + struct bid_comp + { + bool operator () (const bid_type & a, const bid_type & b) const + { + return (a.storage < b.storage) || ( a.storage == b.storage && a.offset < b.offset); + } + }; +*/ + + struct bid_hash + { + size_t operator () (const bid_type& bid) const + { + size_t result = + longhash1(bid.offset + reinterpret_cast(bid.storage)); + return result; + } +#if STXXL_MSVC + bool operator () (const bid_type& a, const bid_type& b) const + { + return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset); + } + enum + { // parameters for hash table + bucket_size = 4, // 0 < bucket_size + min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N + }; +#endif + }; + + std::vector m_nodes; + std::vector m_reqs; + std::vector m_fixed; + std::vector m_dirty; + std::vector m_free_nodes; + typedef typename compat_hash_map::result hash_map_type; + + //typedef std::map BID2node_type; + typedef hash_map_type bid2node_type; + + bid2node_type m_bid2node; + pager_type m_pager; + block_manager* m_bm; + alloc_strategy_type m_alloc_strategy; + + int64 n_found; + int64 n_not_found; + int64 n_created; + int64 n_deleted; + int64 n_read; + int64 n_written; + int64 n_clean_forced; + + // changes btree pointer in all contained iterators + void change_btree_pointers(btree_type* b) + { + for (typename std::vector::const_iterator it = m_nodes.begin(); + it != m_nodes.end(); ++it) + { + (*it)->m_btree = b; + } + } + +public: + node_cache(unsigned_type cache_size_in_bytes, + btree_type* btree, + key_compare cmp) + : m_btree(btree), + m_cmp(cmp), + m_bm(block_manager::get_instance()), + n_found(0), + n_not_found(0), + n_created(0), + n_deleted(0), + n_read(0), + n_written(0), + n_clean_forced(0) + { + const unsigned_type nnodes = cache_size_in_bytes / block_type::raw_size; + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache constructor nodes=" << nnodes); + if (nnodes < 3) + { + STXXL_THROW2(std::runtime_error, "btree::node_cache::node_cache", "Too few memory for a node cache (<3)"); + } + m_nodes.reserve(nnodes); + m_reqs.resize(nnodes); + m_free_nodes.reserve(nnodes); + m_fixed.resize(nnodes, false); + m_dirty.resize(nnodes, true); + for (unsigned_type i = 0; i < nnodes; ++i) + { + m_nodes.push_back(new node_type(m_btree, m_cmp)); + m_free_nodes.push_back(i); + } + + pager_type tmp_pager(nnodes); + std::swap(m_pager, tmp_pager); + } + + unsigned_type size() const + { + return m_nodes.size(); + } + + // returns the number of fixed pages + unsigned_type nfixed() const + { + typename bid2node_type::const_iterator i = m_bid2node.begin(); + typename bid2node_type::const_iterator end = m_bid2node.end(); + unsigned_type cnt = 0; + for ( ; i != end; ++i) + { + if (m_fixed[(*i).second]) + ++cnt; + } + return cnt; + } + + ~node_cache() + { + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache destructor addr=" << this); + typename bid2node_type::const_iterator i = m_bid2node.begin(); + typename bid2node_type::const_iterator end = m_bid2node.end(); + for ( ; i != end; ++i) + { + const unsigned_type p = (*i).second; + if (m_reqs[p].valid()) + m_reqs[p]->wait(); + + if (m_dirty[p]) + m_nodes[p]->save(); + } + for (unsigned_type i = 0; i < size(); ++i) + delete m_nodes[i]; + } + + node_type * get_new_node(bid_type& new_bid) + { + ++n_created; + + if (m_free_nodes.empty()) + { + // need to kick a node + int_type node2kick; + unsigned_type i = 0; + const unsigned_type max_tries = size() + 1; + do + { + ++i; + node2kick = m_pager.kick(); + if (i == max_tries) + { + STXXL_ERRMSG( + "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); + STXXL_ERRMSG("Returning NULL node."); + return NULL; + } + m_pager.hit(node2kick); + } while (m_fixed[node2kick]); + + if (m_reqs[node2kick].valid()) + m_reqs[node2kick]->wait(); + + node_type& node = *(m_nodes[node2kick]); + + if (m_dirty[node2kick]) + { + node.save(); + ++n_written; + } + else + ++n_clean_forced; + + //reqs_[node2kick] = request_ptr(); // reset request + + assert(m_bid2node.find(node.my_bid()) != m_bid2node.end()); + m_bid2node.erase(node.my_bid()); + m_bm->new_block(m_alloc_strategy, new_bid); + + m_bid2node[new_bid] = node2kick; + + node.init(new_bid); + + m_dirty[node2kick] = true; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_new_node, need to kick node " << node2kick); + + return &node; + } + + int_type free_node = m_free_nodes.back(); + m_free_nodes.pop_back(); + assert(m_fixed[free_node] == false); + + m_bm->new_block(m_alloc_strategy, new_bid); + m_bid2node[new_bid] = free_node; + node_type& node = *(m_nodes[free_node]); + node.init(new_bid); + + // assert(!(reqs_[free_node].valid())); + + m_pager.hit(free_node); + + m_dirty[free_node] = true; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_new_node, free node " << free_node << "available"); + + return &node; + } + + node_type * get_node(const bid_type& bid, bool fix = false) + { + typename bid2node_type::const_iterator it = m_bid2node.find(bid); + ++n_read; + + if (it != m_bid2node.end()) + { + // the node is in cache + const int_type nodeindex = it->second; + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, the node " << nodeindex << "is in cache , fix=" << fix); + m_fixed[nodeindex] = fix; + m_pager.hit(nodeindex); + m_dirty[nodeindex] = true; + + if (m_reqs[nodeindex].valid() && !m_reqs[nodeindex]->poll()) + m_reqs[nodeindex]->wait(); + + ++n_found; + return m_nodes[nodeindex]; + } + + ++n_not_found; + + // the node is not in cache + if (m_free_nodes.empty()) + { + // need to kick a node + int_type node2kick; + unsigned_type i = 0; + const unsigned_type max_tries = size() + 1; + do + { + ++i; + node2kick = m_pager.kick(); + if (i == max_tries) + { + STXXL_ERRMSG( + "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); + STXXL_ERRMSG("Returning NULL node."); + return NULL; + } + m_pager.hit(node2kick); + } while (m_fixed[node2kick]); + + if (m_reqs[node2kick].valid()) + m_reqs[node2kick]->wait(); + + node_type& node = *(m_nodes[node2kick]); + + if (m_dirty[node2kick]) + { + node.save(); + ++n_written; + } + else + ++n_clean_forced; + + m_bid2node.erase(node.my_bid()); + + m_reqs[node2kick] = node.load(bid); + m_bid2node[bid] = node2kick; + + m_fixed[node2kick] = fix; + + m_dirty[node2kick] = true; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, need to kick node" << node2kick << " fix=" << fix); + + return &node; + } + + int_type free_node = m_free_nodes.back(); + m_free_nodes.pop_back(); + assert(m_fixed[free_node] == false); + + node_type& node = *(m_nodes[free_node]); + m_reqs[free_node] = node.load(bid); + m_bid2node[bid] = free_node; + + m_pager.hit(free_node); + + m_fixed[free_node] = fix; + + m_dirty[free_node] = true; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, free node " << free_node << "available, fix=" << fix); + + return &node; + } + + node_type const * get_const_node(const bid_type& bid, bool fix = false) + { + typename bid2node_type::const_iterator it = m_bid2node.find(bid); + ++n_read; + + if (it != m_bid2node.end()) + { + // the node is in cache + const int_type nodeindex = it->second; + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, the node " << nodeindex << "is in cache , fix=" << fix); + m_fixed[nodeindex] = fix; + m_pager.hit(nodeindex); + + if (m_reqs[nodeindex].valid() && !m_reqs[nodeindex]->poll()) + m_reqs[nodeindex]->wait(); + + ++n_found; + return m_nodes[nodeindex]; + } + + ++n_not_found; + + // the node is not in cache + if (m_free_nodes.empty()) + { + // need to kick a node + int_type node2kick; + unsigned_type i = 0; + const unsigned_type max_tries = size() + 1; + do + { + ++i; + node2kick = m_pager.kick(); + if (i == max_tries) + { + STXXL_ERRMSG( + "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); + STXXL_ERRMSG("Returning NULL node."); + return NULL; + } + m_pager.hit(node2kick); + } while (m_fixed[node2kick]); + + if (m_reqs[node2kick].valid()) + m_reqs[node2kick]->wait(); + + node_type& node = *(m_nodes[node2kick]); + if (m_dirty[node2kick]) + { + node.save(); + ++n_written; + } + else + ++n_clean_forced; + + m_bid2node.erase(node.my_bid()); + + m_reqs[node2kick] = node.load(bid); + m_bid2node[bid] = node2kick; + + m_fixed[node2kick] = fix; + + m_dirty[node2kick] = false; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, need to kick node" << node2kick << " fix=" << fix); + + return &node; + } + + int_type free_node = m_free_nodes.back(); + m_free_nodes.pop_back(); + assert(m_fixed[free_node] == false); + + node_type& node = *(m_nodes[free_node]); + m_reqs[free_node] = node.load(bid); + m_bid2node[bid] = free_node; + + m_pager.hit(free_node); + + m_fixed[free_node] = fix; + + m_dirty[free_node] = false; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache get_node, free node " << free_node << "available, fix=" << fix); + + return &node; + } + + void delete_node(const bid_type& bid) + { + typename bid2node_type::const_iterator it = m_bid2node.find(bid); + try + { + if (it != m_bid2node.end()) + { + // the node is in the cache + const int_type nodeindex = it->second; + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache delete_node " << nodeindex << " from cache."); + if (m_reqs[nodeindex].valid()) + m_reqs[nodeindex]->wait(); + + //reqs_[nodeindex] = request_ptr(); // reset request + m_free_nodes.push_back(nodeindex); + m_bid2node.erase(bid); + m_fixed[nodeindex] = false; + } + ++n_deleted; + } catch (const io_error& ex) + { + m_bm->delete_block(bid); + throw io_error(ex.what()); + } + m_bm->delete_block(bid); + } + + void prefetch_node(const bid_type& bid) + { + if (m_bid2node.find(bid) != m_bid2node.end()) + return; + + // the node is not in cache + if (m_free_nodes.empty()) + { + // need to kick a node + int_type node2kick; + unsigned_type i = 0; + const unsigned_type max_tries = size() + 1; + do + { + ++i; + node2kick = m_pager.kick(); + if (i == max_tries) + { + STXXL_ERRMSG( + "The node cache is too small, no node can be kicked out (all nodes are fixed) !"); + STXXL_ERRMSG("Returning NULL node."); + return; + } + m_pager.hit(node2kick); + } while (m_fixed[node2kick]); + + if (m_reqs[node2kick].valid()) + m_reqs[node2kick]->wait(); + + node_type& node = *(m_nodes[node2kick]); + + if (m_dirty[node2kick]) + { + node.save(); + ++n_written; + } + else + ++n_clean_forced; + + m_bid2node.erase(node.my_bid()); + + m_reqs[node2kick] = node.prefetch(bid); + m_bid2node[bid] = node2kick; + + m_fixed[node2kick] = false; + + m_dirty[node2kick] = false; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache prefetch_node, need to kick node" << node2kick << " "); + + return; + } + + int_type free_node = m_free_nodes.back(); + m_free_nodes.pop_back(); + assert(m_fixed[free_node] == false); + + node_type& node = *(m_nodes[free_node]); + m_reqs[free_node] = node.prefetch(bid); + m_bid2node[bid] = free_node; + + m_pager.hit(free_node); + + m_fixed[free_node] = false; + + m_dirty[free_node] = false; + + assert(size() == m_bid2node.size() + m_free_nodes.size()); + + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache prefetch_node, free node " << free_node << "available"); + + return; + } + + void unfix_node(const bid_type& bid) + { + assert(m_bid2node.find(bid) != m_bid2node.end()); + m_fixed[m_bid2node[bid]] = false; + STXXL_BTREE_CACHE_VERBOSE("btree::node_cache unfix_node, node " << m_bid2node[bid]); + } + + void swap(node_cache& obj) + { + std::swap(m_cmp, obj.m_cmp); + std::swap(m_nodes, obj.m_nodes); + std::swap(m_reqs, obj.m_reqs); + change_btree_pointers(m_btree); + obj.change_btree_pointers(obj.m_btree); + std::swap(m_fixed, obj.m_fixed); + std::swap(m_free_nodes, obj.m_free_nodes); + std::swap(m_bid2node, obj.m_bid2node); + std::swap(m_pager, obj.m_pager); + std::swap(m_alloc_strategy, obj.m_alloc_strategy); + std::swap(n_found, obj.n_found); + std::swap(n_not_found, obj.n_found); + std::swap(n_created, obj.n_created); + std::swap(n_deleted, obj.n_deleted); + std::swap(n_read, obj.n_read); + std::swap(n_written, obj.n_written); + std::swap(n_clean_forced, obj.n_clean_forced); + } + + void print_statistics(std::ostream& o) const + { + if (n_read) + o << "Found blocks : " << n_found << " (" << + 100. * double(n_found) / double(n_read) << "%)" << std::endl; + else + o << "Found blocks : " << n_found << " (" << + 100 << "%)" << std::endl; + + o << "Not found blocks : " << n_not_found << std::endl; + o << "Created in the cache blocks : " << n_created << std::endl; + o << "Deleted blocks : " << n_deleted << std::endl; + o << "Read blocks : " << n_read << std::endl; + o << "Written blocks : " << n_written << std::endl; + o << "Clean blocks forced from the cache: " << n_clean_forced << std::endl; + } + void reset_statistics() + { + n_found = 0; + n_not_found = 0; + n_created = 0; + n_deleted = 0; + n_read = 0; + n_written = 0; + n_clean_forced = 0; + } +}; + +} // namespace btree + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::btree::node_cache& a, + stxxl::btree::node_cache& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_BTREE_NODE_CACHE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/root_node.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/root_node.h new file mode 100644 index 0000000000..a6dfca0c90 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/btree/root_node.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * include/stxxl/bits/containers/btree/root_node.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * + * 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 STXXL_CONTAINERS_BTREE_ROOT_NODE_HEADER +#define STXXL_CONTAINERS_BTREE_ROOT_NODE_HEADER + +#include + +STXXL_BEGIN_NAMESPACE + +namespace btree { + +template +class root_node +{ + // a place for a future custom root node tree implementation +}; + +} // namespace btree + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_BTREE_ROOT_NODE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/deque.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/deque.h new file mode 100644 index 0000000000..93dc6546a1 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/deque.h @@ -0,0 +1,723 @@ +/*************************************************************************** + * include/stxxl/bits/containers/deque.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * Copyright (C) 2008, 2009 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_CONTAINERS_DEQUE_HEADER +#define STXXL_CONTAINERS_DEQUE_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +template +class deque; + +template +class const_deque_iterator; + +template +class deque_iterator +{ +public: + typedef DequeType deque_type; + typedef typename deque_type::vector_type vector_type; + + typedef typename deque_type::value_type value_type; + typedef typename deque_type::pointer pointer; + typedef typename deque_type::const_pointer const_pointer; + typedef typename deque_type::reference reference; + typedef typename deque_type::const_reference const_reference; + typedef typename deque_type::size_type size_type; + typedef typename deque_type::difference_type difference_type; + typedef deque_iterator iterator; + typedef const_deque_iterator const_iterator; + + typedef std::random_access_iterator_tag iterator_category; + + friend class const_deque_iterator; + friend class deque; + +protected: + typedef deque_iterator self_type; + + deque_type* m_deque; + size_type m_offset; + + deque_iterator(deque_type* deque, size_type offset) + : m_deque(deque), m_offset(offset) + { } + +public: + deque_iterator() : m_deque(NULL), m_offset(0) { } + + difference_type operator - (const self_type& a) const + { + size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? + m_offset : (m_deque->m_vector.size() + m_offset); + size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? + a.m_offset : (m_deque->m_vector.size() + a.m_offset); + + return SelfAbsOffset - aAbsOffset; + } + + difference_type operator - (const const_iterator& a) const + { + size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? + m_offset : (m_deque->m_vector.size() + m_offset); + size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? + a.m_offset : (m_deque->m_vector.size() + a.m_offset); + + return SelfAbsOffset - aAbsOffset; + } + + self_type operator - (size_type op) const + { + return self_type(m_deque, (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size()); + } + + self_type operator + (size_type op) const + { + return self_type(m_deque, (m_offset + op) % m_deque->m_vector.size()); + } + + self_type& operator -= (size_type op) + { + m_offset = (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size(); + return *this; + } + + self_type& operator += (size_type op) + { + m_offset = (m_offset + op) % m_deque->m_vector.size(); + return *this; + } + + reference operator * () + { + return m_deque->m_vector[m_offset]; + } + + pointer operator -> () + { + return &(m_deque->m_vector[m_offset]); + } + + const_reference operator * () const + { + return m_deque->m_vector[m_offset]; + } + + const_pointer operator -> () const + { + return &(m_deque->m_vector[m_offset]); + } + + reference operator [] (size_type op) + { + return m_deque->m_vector[(m_offset + op) % m_deque->m_vector.size()]; + } + + const_reference operator [] (size_type op) const + { + return m_deque->m_vector[(m_offset + op) % m_deque->m_vector.size()]; + } + + self_type& operator ++ () + { + m_offset = (m_offset + 1) % m_deque->m_vector.size(); + return *this; + } + self_type operator ++ (int) + { + self_type tmp = *this; + m_offset = (m_offset + 1) % m_deque->m_vector.size(); + return tmp; + } + self_type& operator -- () + { + m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); + return *this; + } + self_type operator -- (int) + { + self_type tmp = *this; + m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); + return tmp; + } + bool operator == (const self_type& a) const + { + assert(m_deque == a.m_deque); + return m_offset == a.m_offset; + } + bool operator != (const self_type& a) const + { + assert(m_deque == a.m_deque); + return m_offset != a.m_offset; + } + + bool operator < (const self_type& a) const + { + assert(m_deque == a.m_deque); + return (a - (*this)) > 0; + } + + bool operator > (const self_type& a) const + { + return a < (*this); + } + + bool operator <= (const self_type& a) const + { + return !((*this) > a); + } + + bool operator >= (const self_type& a) const + { + return !((*this) < a); + } + + bool operator == (const const_iterator& a) const + { + assert(m_deque == a.m_deque); + return m_offset == a.m_offset; + } + bool operator != (const const_iterator& a) const + { + assert(m_deque == a.m_deque); + return m_offset != a.m_offset; + } + + bool operator < (const const_iterator& a) const + { + assert(m_deque == a.m_deque); + return (a - (*this)) > 0; + } + + bool operator > (const const_iterator& a) const + { + return a < (*this); + } + + bool operator <= (const const_iterator& a) const + { + return !((*this) > a); + } + + bool operator >= (const const_iterator& a) const + { + return !((*this) < a); + } +}; + +template +class const_deque_iterator +{ +public: + typedef DequeType deque_type; + typedef typename deque_type::vector_type vector_type; + + typedef typename deque_type::value_type value_type; + typedef typename deque_type::const_pointer pointer; + typedef typename deque_type::const_pointer const_pointer; + typedef typename deque_type::const_reference reference; + typedef typename deque_type::const_reference const_reference; + typedef typename deque_type::size_type size_type; + typedef typename deque_type::difference_type difference_type; + + typedef deque_iterator iterator; + typedef const_deque_iterator const_iterator; + + typedef std::random_access_iterator_tag iterator_category; + + friend class deque_iterator; + friend class deque; + +protected: + typedef const_deque_iterator self_type; + + const deque_type* m_deque; + size_type m_offset; + + const_deque_iterator(const deque_type* deque, size_type offset) + : m_deque(deque), m_offset(offset) + { } + +public: + const_deque_iterator() : m_deque(NULL), m_offset(0) { } + + const_deque_iterator(const deque_iterator& it) + : m_deque(it.m_deque), m_offset(it.m_offset) + { } + + difference_type operator - (const self_type& a) const + { + size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? + m_offset : (m_deque->m_vector.size() + m_offset); + size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? + a.m_offset : (m_deque->m_vector.size() + a.m_offset); + + return SelfAbsOffset - aAbsOffset; + } + + difference_type operator - (const iterator& a) const + { + size_type SelfAbsOffset = (m_offset >= m_deque->m_begin) ? + m_offset : (m_deque->m_vector.size() + m_offset); + size_type aAbsOffset = (a.m_offset >= m_deque->m_begin) ? + a.m_offset : (m_deque->m_vector.size() + a.m_offset); + + return SelfAbsOffset - aAbsOffset; + } + + self_type operator - (size_type op) const + { + return self_type(m_deque, (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size()); + } + + self_type operator + (size_type op) const + { + return self_type(m_deque, (m_offset + op) % m_deque->m_vector.size()); + } + + self_type& operator -= (size_type op) + { + m_offset = (m_offset + m_deque->m_vector.size() - op) % m_deque->m_vector.size(); + return *this; + } + + self_type& operator += (size_type op) + { + m_offset = (m_offset + op) % m_deque->m_vector.size(); + return *this; + } + + const_reference operator * () const + { + return m_deque->m_vector[m_offset]; + } + + const_pointer operator -> () const + { + return &(m_deque->m_vector[m_offset]); + } + + const_reference operator [] (size_type op) const + { + return m_deque->m_vector[(m_offset + op) % m_deque->m_vector.size()]; + } + + self_type& operator ++ () + { + m_offset = (m_offset + 1) % m_deque->m_vector.size(); + return *this; + } + self_type operator ++ (int) + { + self_type tmp = *this; + m_offset = (m_offset + 1) % m_deque->m_vector.size(); + return tmp; + } + self_type& operator -- () + { + m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); + return *this; + } + self_type operator -- (int) + { + self_type tmp = *this; + m_offset = (m_offset + m_deque->m_vector.size() - 1) % m_deque->m_vector.size(); + return tmp; + } + bool operator == (const self_type& a) const + { + assert(m_deque == a.m_deque); + return m_offset == a.m_offset; + } + bool operator != (const self_type& a) const + { + assert(m_deque == a.m_deque); + return m_offset != a.m_offset; + } + + bool operator < (const self_type& a) const + { + assert(m_deque == a.m_deque); + return (a - (*this)) > 0; + } + + bool operator > (const self_type& a) const + { + return a < (*this); + } + + bool operator <= (const self_type& a) const + { + return !((*this) > a); + } + + bool operator >= (const self_type& a) const + { + return !((*this) < a); + } + + bool operator == (const iterator& a) const + { + assert(m_deque == a.m_deque); + return m_offset == a.m_offset; + } + bool operator != (const iterator& a) const + { + assert(m_deque == a.m_deque); + return m_offset != a.m_offset; + } + + bool operator < (const iterator& a) const + { + assert(m_deque == a.m_deque); + return (a - (*this)) > 0; + } + + bool operator > (const iterator& a) const + { + return a < (*this); + } + + bool operator <= (const iterator& a) const + { + return !((*this) > a); + } + + bool operator >= (const iterator& a) const + { + return !((*this) < a); + } +}; + +//! \addtogroup stlcont +//! \{ + +//! A deque container. \n +//! Introduction to deque container: see \ref tutorial_deque tutorial. \n +//! Design and Internals of deque container: see \ref design_deque +//! +//! It is an adaptor of the \c VectorType. +//! The implementation wraps the elements around +//! the end of the \c VectorType circularly. +//! \tparam ValueType type of the contained objects (POD with no references to internal memory) +//! \tparam VectorType the type of the underlying vector container, +//! the default is \c stxxl::vector +template > +class deque : private noncopyable +{ + typedef deque self_type; + +public: + typedef typename VectorType::size_type size_type; + typedef typename VectorType::difference_type difference_type; + typedef VectorType vector_type; + typedef ValueType value_type; + typedef ValueType* pointer; + typedef const value_type* const_pointer; + typedef ValueType& reference; + typedef const ValueType& const_reference; + typedef deque_iterator iterator; + typedef const_deque_iterator const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + friend class deque_iterator; + friend class const_deque_iterator; + +private: + vector_type m_vector; + size_type m_begin, m_end, m_size; + + void double_array() + { + const size_type old_size = m_vector.size(); + m_vector.resize(2 * old_size); + if (m_begin > m_end) + { // copy data to the new end of the vector + const size_type new_begin = old_size + m_begin; + std::copy(m_vector.begin() + m_begin, + m_vector.begin() + old_size, + m_vector.begin() + new_begin); + m_begin = new_begin; + } + } + +public: + //! \name Constructors/Destructors + //! \{ + + deque() + : m_vector((STXXL_DEFAULT_BLOCK_SIZE(T)) / sizeof(value_type)), + m_begin(0), m_end(0), m_size(0) + { } + + deque(size_type n) + : m_vector(STXXL_MAX(STXXL_DEFAULT_BLOCK_SIZE(ValueType) / sizeof(value_type), 2 * n)), + m_begin(0), m_end(n), m_size(n) + { } + + ~deque() // empty so far + { } + + //! \} + + //! \name Iterators + //! \{ + + iterator begin() + { + return iterator(this, m_begin); + } + iterator end() + { + return iterator(this, m_end); + } + const_iterator begin() const + { + return const_iterator(this, m_begin); + } + const_iterator cbegin() const + { + return begin(); + } + const_iterator end() const + { + return const_iterator(this, m_end); + } + const_iterator cend() const + { + return end(); + } + + reverse_iterator rbegin() + { + return reverse_iterator(end()); + } + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(end()); + } + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(end()); + } + reverse_iterator rend() + { + return reverse_iterator(begin()); + } + const_reverse_iterator rend() const + { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crend() const + { + return const_reverse_iterator(begin()); + } + + //! \} + + //! \name Capacity + //! \{ + + size_type size() const + { + return m_size; + } + + size_type max_size() const + { + return std::numeric_limits::max() / 2 - 1; + } + + bool empty() const + { + return m_size == 0; + } + + //! \} + + //! \name Operators + //! \{ + + reference operator [] (size_type n) + { + assert(n < size()); + return m_vector[(m_begin + n) % m_vector.size()]; + } + + const_reference operator [] (size_type n) const + { + assert(n < size()); + return m_vector[(m_begin + n) % m_vector.size()]; + } + + reference front() + { + assert(!empty()); + return m_vector[m_begin]; + } + + const_reference front() const + { + assert(!empty()); + return m_vector[m_begin]; + } + + reference back() + { + assert(!empty()); + return m_vector[(m_end + m_vector.size() - 1) % m_vector.size()]; + } + + const_reference back() const + { + assert(!empty()); + return m_vector[(m_end + m_vector.size() - 1) % m_vector.size()]; + } + + //! \} + + //! \name Modifiers + //! \{ + + void push_front(const value_type& el) + { + if ((m_begin + m_vector.size() - 1) % m_vector.size() == m_end) + { + // an overflow will occur: resize the array + double_array(); + } + + m_begin = (m_begin + m_vector.size() - 1) % m_vector.size(); + m_vector[m_begin] = el; + ++m_size; + } + + void push_back(const value_type& el) + { + if ((m_end + 1) % m_vector.size() == m_begin) + { + // an overflow will occur: resize the array + double_array(); + } + m_vector[m_end] = el; + m_end = (m_end + 1) % m_vector.size(); + ++m_size; + } + + void pop_front() + { + assert(!empty()); + m_begin = (m_begin + 1) % m_vector.size(); + --m_size; + } + + void pop_back() + { + assert(!empty()); + m_end = (m_end + m_vector.size() - 1) % m_vector.size(); + --m_size; + } + + //! \} + + //! \name Modifiers + //! \{ + + void swap(deque& obj) + { + std::swap(m_vector, obj.m_vector); + std::swap(m_begin, obj.m_begin); + std::swap(m_end, obj.m_end); + std::swap(m_size, obj.m_size); + } + + void clear() + { + m_vector.clear(); + m_vector.resize((STXXL_DEFAULT_BLOCK_SIZE(T)) / sizeof(value_type)); + m_begin = 0; + m_end = 0; + m_size = 0; + } + + //! \} + + //! \name Capacity + //! \{ + + void resize(size_type n) + { + if (n < size()) + { + do + { + pop_back(); + } while (n < size()); + } + else + { + if (n + 1 > m_vector.size()) + { // need to resize + const size_type old_size = m_vector.size(); + m_vector.resize(2 * n); + if (m_begin > m_end) + { // copy data to the new end of the vector + const size_type new_begin = m_vector.size() - old_size + m_begin; + std::copy(m_vector.begin() + m_begin, + m_vector.begin() + old_size, + m_vector.begin() + new_begin); + m_begin = new_begin; + } + } + m_end = (m_end + n - size()) % m_vector.size(); + m_size = n; + } + } + + //! \} +}; + +template +bool operator == (const deque& a, const deque& b) +{ + return std::equal(a.begin(), a.end(), b.begin()); +} + +template +bool operator < (const deque& a, const deque& b) +{ + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +} + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::deque& a, + stxxl::deque& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_DEQUE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/block_cache.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/block_cache.h new file mode 100644 index 0000000000..03663d9f88 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/block_cache.h @@ -0,0 +1,612 @@ +/*************************************************************************** + * include/stxxl/bits/containers/hash_map/block_cache.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Markus Westphal + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_CONTAINERS_HASH_MAP_BLOCK_CACHE_HEADER +#define STXXL_CONTAINERS_HASH_MAP_BLOCK_CACHE_HEADER + +#ifdef STXXL_BOOST_CONFIG + #include +#endif + +#include +#include +#include +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace hash_map { + +//! Used inside block_cache for buffering write requests of cached blocks. +template +class block_cache_write_buffer : private noncopyable +{ +public: + typedef BlockType block_type; + typedef typename block_type::bid_type bid_type; + +protected: + std::vector blocks_; + std::vector reqs_; + std::vector free_blocks_; + std::list busy_blocks_; // TODO make that a circular-buffer + +public: + block_cache_write_buffer(unsigned_type size) + { + blocks_.reserve(size); + free_blocks_.reserve(size); + reqs_.resize(size); + + for (unsigned_type i = 0; i < size; i++) { + blocks_.push_back(new block_type()); + free_blocks_.push_back(i); + } + } + + //! Writes the given block back to disk; + //! callers have to exchange the passed block with the returned one! + block_type * write(block_type* write_block, const bid_type& bid) + { + if (free_blocks_.empty()) { + unsigned_type i_buffer = busy_blocks_.front(); + busy_blocks_.pop_front(); + + if (reqs_[i_buffer].valid()) + reqs_[i_buffer]->wait(); + + free_blocks_.push_back(i_buffer); + } + + unsigned_type i_buffer = free_blocks_.back(); + free_blocks_.pop_back(); + block_type* buffer = blocks_[i_buffer]; + + blocks_[i_buffer] = write_block; + reqs_[i_buffer] = blocks_[i_buffer]->write(bid); + busy_blocks_.push_back(i_buffer); + + return buffer; + } + + void flush() + { + while (!busy_blocks_.empty()) { + unsigned_type i_buffer = busy_blocks_.front(); + busy_blocks_.pop_front(); + if (reqs_[i_buffer].valid()) + reqs_[i_buffer]->wait(); + } + busy_blocks_.clear(); + free_blocks_.clear(); + for (unsigned_type i = 0; i < blocks_.size(); i++) + free_blocks_.push_back(i); + } + + void swap(block_cache_write_buffer& obj) + { + std::swap(blocks_, obj.blocks_); + std::swap(reqs_, obj.reqs_); + std::swap(free_blocks_, obj.free_blocks_); + std::swap(busy_blocks_, obj.busy_blocks_); + } + + ~block_cache_write_buffer() + { + flush(); + for (unsigned_type i = 0; i < blocks_.size(); i++) + delete blocks_[i]; + } +}; + +//! Cache of blocks contained in an external memory hash map. Uses the +//! stxxl::lru_pager as eviction algorithm. +template +class block_cache : private noncopyable +{ +public: + typedef BlockType block_type; + typedef typename block_type::bid_type bid_type; + typedef typename block_type::value_type subblock_type; + typedef typename subblock_type::bid_type subblock_bid_type; + +protected: + struct bid_eq + { + bool operator () (const bid_type& a, const bid_type& b) const + { + return (a.storage == b.storage && a.offset == b.offset); + } + }; + + struct bid_hash + { + size_t operator () (const bid_type& bid) const + { + return longhash1(bid.offset + reinterpret_cast(bid.storage)); + } +#ifdef STXXL_MSVC + bool operator () (const bid_type& a, const bid_type& b) const + { + return (a.storage < b.storage) || + (a.storage == b.storage && a.offset < b.offset); + } + enum + { // parameters for hash table + bucket_size = 4, // 0 < bucket_size + min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N + }; +#endif + }; + + typedef stxxl::lru_pager<> pager_type; + typedef block_cache_write_buffer write_buffer_type; + + typedef typename compat_hash_map::result bid_map_type; + + enum { valid_all = block_type::size }; + + write_buffer_type write_buffer_; + + //! cached blocks + std::vector blocks_; + //! bids of cached blocks + std::vector bids_; + std::vector retain_count_; + + //! true iff block has been altered while in cache + std::vector dirty_; + + //! valid_all or the actually loaded subblock's index + std::vector valid_subblock_; + + //! free blocks as indices to blocks_-vector + std::vector free_blocks_; + std::vector reqs_; + + bid_map_type bid_map_; + pager_type pager_; + + /* statistics */ + int64 n_found; + int64 n_not_found; + int64 n_read; + int64 n_written; + int64 n_clean_forced; + int64 n_wrong_subblock; + +public: + //! Construct a new block-cache. + //! \param cache_size cache-size in number of blocks + block_cache(unsigned_type cache_size) + : write_buffer_(config::get_instance()->disks_number() * 2), + blocks_(cache_size), + bids_(cache_size), + retain_count_(cache_size), + dirty_(cache_size, false), + valid_subblock_(cache_size), + free_blocks_(cache_size), + reqs_(cache_size), + pager_(cache_size), + n_found(0), + n_not_found(0), + n_read(0), + n_written(0), + n_clean_forced(0), + n_wrong_subblock(0) + { + for (unsigned_type i = 0; i < cache_size; i++) + { + blocks_[i] = new block_type(); + free_blocks_[i] = i; + } + } + + //! Return cache-size + unsigned_type size() const + { + return blocks_.size(); + } + + ~block_cache() + { + STXXL_VERBOSE1("hash_map::block_cache destructor addr=" << this); + + for (typename bid_map_type::const_iterator i = bid_map_.begin(); + i != bid_map_.end(); ++i) + { + const unsigned_type i_block = (*i).second; + + if (reqs_[i_block].valid()) + reqs_[i_block]->wait(); + + if (dirty_[i_block]) { + blocks_[i_block] = + write_buffer_.write(blocks_[i_block], bids_[i_block]); + } + } + write_buffer_.flush(); + + for (unsigned_type i = 0; i < size(); ++i) + delete blocks_[i]; + } + +protected: + //! Force a block from the cache; write back to disk if dirty + void kick_block() + { + unsigned_type i_block2kick; + + unsigned_type max_tries = size() + 1; + unsigned_type i = 0; + do + { + ++i; + i_block2kick = pager_.kick(); + if (i == max_tries) + { + throw std::runtime_error( + "The block cache is too small," + "no block can be kicked out (all blocks are retained)!" + ); + } + pager_.hit(i_block2kick); + } while (retain_count_[i_block2kick] > 0); + + if (valid_subblock_[i_block2kick] == valid_all && + reqs_[i_block2kick].valid()) + { + reqs_[i_block2kick]->wait(); + } + + if (dirty_[i_block2kick]) + { + blocks_[i_block2kick] = + write_buffer_.write(blocks_[i_block2kick], bids_[i_block2kick]); + ++n_written; + } + else + ++n_clean_forced; + + bid_map_.erase(bids_[i_block2kick]); + free_blocks_.push_back(i_block2kick); + } + +public: + //! Retain a block in cache. Blocks, that are retained by at least one + //! client, won't get kicked. Make sure to release all retained blocks + //! again. + //! + //! \param bid block, whose retain-count is to be increased + //! \return true if block was cached, false otherwise + bool retain_block(const bid_type& bid) + { + typename bid_map_type::const_iterator it = bid_map_.find(bid); + if (it == bid_map_.end()) + return false; + + unsigned_type i_block = (*it).second; + retain_count_[i_block]++; + return true; + } + + //! Release a block (decrement retain-count). If the retain-count reaches + //! 0, a block may be kicked again. + //! + //! \param bid block, whose retain-count is to be decremented + //! \return true if operation was successfull (block cached and + //! retain-count > 0), false otherwise + bool release_block(const bid_type& bid) + { + typename bid_map_type::const_iterator it = bid_map_.find(bid); + if (it == bid_map_.end()) + return false; + + unsigned_type i_block = (*it).second; + if (retain_count_[i_block] == 0) + return false; + + retain_count_[i_block]--; + return true; + } + + //! Set given block's dirty-flag. Note: If the given block was only + //! partially loaded, it will be completely reloaded. + //! + //! \return true if block cached, false otherwise + bool make_dirty(const bid_type& bid) + { + typename bid_map_type::const_iterator it = bid_map_.find(bid); + if (it == bid_map_.end()) + return false; + + unsigned_type i_block = (*it).second; + + // only complete blocks can be marked as dirty + if (valid_subblock_[i_block] != valid_all) + { + reqs_[i_block] = blocks_[i_block]->read(bid); + valid_subblock_[i_block] = valid_all; + } + + if (reqs_[i_block].valid()) { + if (reqs_[i_block]->poll() == false) + reqs_[i_block]->wait(); + } + + dirty_[i_block] = true; + return true; + } + + //! Retrieve a subblock from the cache. If not yet cached, only the + //! subblock will be loaded. + //! + //! \param bid block, to which the requested subblock belongs + //! \param i_subblock index of requested subblock + //! \return pointer to subblock + subblock_type * get_subblock(const bid_type& bid, unsigned_type i_subblock) + { + block_type* block; + unsigned_type i_block; + n_read++; + + // block (partly) cached? + typename bid_map_type::const_iterator it = bid_map_.find(bid); + if (it != bid_map_.end()) + { + i_block = (*it).second; + block = blocks_[i_block]; + + // complete block or wanted subblock is in the cache + if (valid_subblock_[i_block] == valid_all || + valid_subblock_[i_block] == i_subblock) + { + ++n_found; + + if (valid_subblock_[i_block] == valid_all && + reqs_[i_block].valid()) + { + // request not yet completed? + if (reqs_[i_block]->poll() == false) + reqs_[i_block]->wait(); + } + + return &((*block)[i_subblock]); + } + // wrong subblock in cache + else + { + ++n_not_found; + ++n_wrong_subblock; + // actually loading the subblock will be done below + + // note: if a client still holds a reference to the "old" + // subblock, it will find its data to be still valid. + } + } + // block not cached + else + { + n_not_found++; + + if (free_blocks_.empty()) + kick_block(); + + i_block = free_blocks_.back(), free_blocks_.pop_back(); + block = blocks_[i_block]; + + bid_map_[bid] = i_block; + bids_[i_block] = bid; + dirty_[i_block] = false; + retain_count_[i_block] = 0; + } + + // now actually load the wanted subblock and store it within *block + subblock_bid_type subblock_bid( + bid.storage, bid.offset + i_subblock * subblock_type::raw_size + ); + request_ptr req = ((*block)[i_subblock]).read(subblock_bid); + req->wait(); + + valid_subblock_[i_block] = i_subblock; + pager_.hit(i_block); + + return &((*block)[i_subblock]); + } + + //! Load a block in advance. + //! \param bid Identifier of the block to load + void prefetch_block(const bid_type& bid) + { + unsigned_type i_block; + + // cached + typename bid_map_type::const_iterator it = bid_map_.find(bid); + if (it != bid_map_.end()) + { + i_block = (*it).second; + + // complete block cached; we can finish here + if (valid_subblock_[i_block] == valid_all) { + pager_.hit(i_block); + return; + } + + // only a single subblock is cached; we have to load the + // complete block (see below) + } + // not even a subblock cached + else { + if (free_blocks_.empty()) + kick_block(); + + i_block = free_blocks_.back(), free_blocks_.pop_back(); + + bid_map_[bid] = i_block; + bids_[i_block] = bid; + retain_count_[i_block] = 0; + dirty_[i_block] = false; + } + + // now actually load the block + reqs_[i_block] = blocks_[i_block]->read(bid); + valid_subblock_[i_block] = valid_all; + pager_.hit(i_block); + } + + //! Write all dirty blocks back to disk + void flush() + { + for (typename bid_map_type::const_iterator i = bid_map_.begin(); + i != bid_map_.end(); ++i) + { + const unsigned_type i_block = (*i).second; + if (dirty_[i_block]) + { + blocks_[i_block] = + write_buffer_.write(blocks_[i_block], bids_[i_block]); + + dirty_[i_block] = false; + } + } + write_buffer_.flush(); + } + + //! Empty cache; don't write back dirty blocks + void clear() + { + free_blocks_.clear(); + for (unsigned_type i = 0; i < size(); i++) + { + if (reqs_[i].valid()) { + reqs_[i]->cancel(); + reqs_[i]->wait(); + } + + free_blocks_.push_back(i); + } + bid_map_.clear(); + } + + //! Print statistics: Number of hits/misses, blocks forced from cache or + //! written back. + void print_statistics(std::ostream& o = std::cout) const + { + o << "Blocks found : " << n_found << " (" << 100. * double(n_found) / double(n_read) << "%)" << std::endl; + o << "Blocks not found : " << n_not_found << std::endl; + o << "Blocks read : " << n_read << std::endl; + o << "Blocks written : " << n_written << std::endl; + o << "Clean blocks forced from the cache: " << n_clean_forced << std::endl; + o << "Wrong subblock cached : " << n_wrong_subblock << std::endl; + } + + //! Reset all counters to zero + void reset_statistics() + { + n_found = 0; + n_not_found = 0; + n_read = 0; + n_written = 0; + n_clean_forced = 0; + n_wrong_subblock = 0; + } + + //! Exchange contents of two caches + //! \param obj cache to swap contents with + void swap(block_cache& obj) + { + write_buffer_.swap(obj.write_buffer_); + std::swap(blocks_, obj.blocks_); + std::swap(bids_, obj.bids_); + std::swap(retain_count_, obj.retain_count_); + + std::swap(dirty_, obj.dirty_); + std::swap(valid_subblock_, obj.valid_subblock_); + + std::swap(free_blocks_, obj.free_blocks_); + std::swap(reqs_, obj.reqs_); + + std::swap(bid_map_, obj.bid_map_); + std::swap(pager_, obj.pager_); + + std::swap(n_found, obj.n_found); + std::swap(n_not_found, obj.n_found); + std::swap(n_read, obj.n_read); + std::swap(n_written, obj.n_written); + std::swap(n_clean_forced, obj.n_clean_forced); + std::swap(n_wrong_subblock, obj.n_wrong_subblock); + } + +#if 0 // for debugging, requires data items to be ostream-able. + + //! Show currently cached blocks + void dump_cache(std::ostream& os) const + { + for (size_t i = 0; i < blocks_.size(); i++) + { + bid_type bid = bids_[i]; + if (bid_map_.count(bid) == 0) { + os << "Block " << i << ": empty\n"; + continue; + } + + os << "Block " << i << ": bid=" << bids_[i] + << " dirty=" << dirty_[i] + << " retain_count=" << retain_count_[i] + << " valid_subblock=" << valid_subblock_[i] << "\n"; + + for (size_t k = 0; k < block_type::size; k++) { + os << " Subbblock " << k << ": "; + if (valid_subblock_[i] != valid_all && valid_subblock_[i] != k) + { + os << "not valid\n"; + continue; + } + for (size_t l = 0; l < block_type::value_type::size; l++) { + os << "(" << (*blocks_[i])[k][l].first + << ", " << (*blocks_[i])[k][l].second << ") "; + } + os << std::endl; + } + } + } +#endif +}; + +} // namespace hash_map + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::hash_map::block_cache_write_buffer& a, + stxxl::hash_map::block_cache_write_buffer& b) +{ + a.swap(b); +} + +template +void swap(stxxl::hash_map::block_cache& a, + stxxl::hash_map::block_cache& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_HASH_MAP_BLOCK_CACHE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/hash_map.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/hash_map.h new file mode 100644 index 0000000000..0759bd220e --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/hash_map.h @@ -0,0 +1,1608 @@ +/*************************************************************************** + * include/stxxl/bits/containers/hash_map/hash_map.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Markus Westphal + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_CONTAINERS_HASH_MAP_HASH_MAP_HEADER +#define STXXL_CONTAINERS_HASH_MAP_HASH_MAP_HEADER + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +#define STXXL_VERBOSE_HASH_MAP(m) \ + STXXL_VERBOSE1("hash_map[" << static_cast(this) << "]::" << m) + +//! External memory hash-map +namespace hash_map { + +/*! + * Main implementation of external memory hash map. + * + * \tparam KeyType the key type + * \tparam MappedType the mapped type associated with a key + * \tparam HashType a hash functional + * \tparam CompareType a less comparison relation for KeyType + * \tparam SubBlockSize the raw size of a subblock (caching granularity) + * (default: 8192) + * \tparam SubBlocksPerBlock the number of subblocks per external block + * (default: 256 -> 2MB blocks) + * \tparam AllocType allocator for internal-memory buffer + */ +template > + > +class hash_map : private noncopyable +{ +protected: + typedef hash_map self_type; + +public: + //! type of the keys being used + typedef KeyType key_type; + //! type of the data to be stored + typedef MappedType mapped_type; + //! actually store (key-data)-pairs + typedef std::pair value_type; + //! type for value-references + typedef value_type& reference; + //! type for constant value-references + typedef const value_type& const_reference; + //! pointer to type of keys + typedef value_type* pointer; + //! const pointer to type of keys + typedef value_type const* const_pointer; + + typedef stxxl::external_size_type external_size_type; + typedef stxxl::internal_size_type internal_size_type; + typedef stxxl::int64 difference_type; + + //! type of (mother) hash-function + typedef HashType hasher; + //! functor that imposes a ordering on keys (but see _lt()) + typedef KeyCompareType key_compare; + //! allocator template type + typedef AllocatorType allocator_type; + + typedef hash_map_iterator iterator; + typedef hash_map_const_iterator const_iterator; + + //! subblock- and block-size in bytes + enum { + block_raw_size = SubBlocksPerBlock * SubBlockSize, + subblock_raw_size = SubBlockSize + }; + + //! Subblock-size as number of elements, block-size as number of subblocks + enum { + subblocks_per_block = SubBlocksPerBlock, + subblock_size = SubBlockSize / sizeof(value_type) + }; + + //! a subblock consists of subblock_size values + typedef typed_block subblock_type; + //! a block consists of block_size subblocks + typedef typed_block block_type; + + //! block-identifier for subblocks + typedef typename subblock_type::bid_type subblock_bid_type; + //! block-identifier for blocks + typedef typename block_type::bid_type bid_type; + //! container for block-bids + typedef std::vector bid_container_type; + //! iterator for block-bids + typedef typename bid_container_type::iterator bid_iterator_type; + + enum source_type { src_internal, src_external, src_unknown }; + + //! nodes for internal-memory buffer + typedef node node_type; + //! buckets + typedef bucket bucket_type; + + typedef std::vector buckets_container_type; + + //! for tracking active iterators + typedef iterator_map iterator_map_type; + + typedef block_cache block_cache_type; + + typedef buffered_reader reader_type; + + typedef typename allocator_type::template rebind::other node_allocator_type; + +protected: + //! user supplied mother hash-function + hasher hash_; + //! user supplied strict-weak-ordering for keys + key_compare cmp_; + //! array of bucket + buckets_container_type buckets_; + //! blocks-ids of allocated blocks + bid_container_type bids_; + //! size of internal-memory buffer in number of entries + internal_size_type buffer_size_; + //! maximum size for internal-memory buffer + internal_size_type max_buffer_size_; + //! keeps track of all active iterators + iterator_map_type iterator_map_; + + mutable block_cache_type block_cache_; + //! used to allocate new nodes for internal buffer + node_allocator_type node_allocator_; + //! false if the total-number of values is correct (false) or true if + //! estimated (true); see *oblivious_-methods + mutable bool oblivious_; + //! (estimated) number of values + mutable external_size_type num_total_; + //! desired load factor after rehashing + float opt_load_factor_; + +public: + /*! + * Construct a new hash-map + * \param n initial number of buckets + * \param hf hash-function + * \param cmp comparator-object + * \param buffer_size size of internal-memory buffer in bytes + * \param a allocation-strategory for internal-memory buffer + */ + hash_map(internal_size_type n = 0, + const hasher& hf = hasher(), + const key_compare& cmp = key_compare(), + internal_size_type buffer_size = 128*1024*1024, + const allocator_type& a = allocator_type()) + : hash_(hf), + cmp_(cmp), + buckets_(n), + bids_(0), + buffer_size_(0), + iterator_map_(this), + block_cache_(tuning::get_instance()->blockcache_size), + node_allocator_(a), + oblivious_(false), + num_total_(0), + opt_load_factor_(0.875) + { + max_buffer_size_ = buffer_size / sizeof(node_type); + } + + /*! + * Construct a new hash-map and insert all values in the range [f,l) + * + * \param begin beginning of the range + * \param end end of the range + * \param mem_to_sort internal memory that may be used for bulk-construction (not + * to be confused with the buffer-memory) + * \param n initial number of buckets + * \param hf hash-function + * \param cmp comparator-object + * \param buffer_size size of internal-memory buffer in bytes + * \param a allocation-strategory for internal-memory buffer + */ + template + hash_map(InputIterator begin, InputIterator end, + internal_size_type mem_to_sort = 256*1024*1024, + internal_size_type n = 0, + const hasher& hf = hasher(), + const key_compare& cmp = key_compare(), + internal_size_type buffer_size = 128*1024*1024, + const allocator_type& a = allocator_type()) + : hash_(hf), + cmp_(cmp), + buckets_(n), // insert will determine a good size + bids_(0), + buffer_size_(0), + iterator_map_(this), + block_cache_(tuning::get_instance()->blockcache_size), + node_allocator_(a), + oblivious_(false), + num_total_(0), + opt_load_factor_(0.875) + { + max_buffer_size_ = buffer_size / sizeof(node_type); + insert(begin, end, mem_to_sort); + } + + ~hash_map() + { + clear(); + } + +public: + //! Hash-function used by this hash-map + hasher hash_function() const + { return hash_; } + + //! Strict-weak-ordering used by this hash-map + key_compare key_cmp() const + { return cmp_; } + + //! Get node memory allocator + allocator_type get_allocator() const + { return node_allocator_; } + +protected: + /*! + * After using *oblivious_-methods only an estimate for the total number of + * elements can be given. This method accesses external memory to + * calculate the exact number. + */ + void _make_conscious() + { /* const */ //! TODO: make const again + if (!oblivious_) + return; + + typedef HashedValuesStream values_stream_type; + + // this will start prefetching automatically + reader_type reader(bids_.begin(), bids_.end(), block_cache_); + values_stream_type values(buckets_.begin(), buckets_.end(), + reader, bids_.begin(), *this); + + num_total_ = 0; + while (!values.empty()) + { + ++num_total_; + ++values; + } + oblivious_ = false; + } + +public: + //! Number of values currently stored. Note: If the correct number is + //! currently unknown (because *_oblivous-methods were used), external + //! memory will be scanned. + external_size_type size() const + { + if (oblivious_) + ((self_type*)this)->_make_conscious(); + return num_total_; + } + + //! The hash-map may store up to this number of values + external_size_type max_size() const + { + return std::numeric_limits::max(); + } + + //! Check if container is empty. + bool empty() const + { + return size() == 0; + } + + /*! + * Insert a new value if no value with the same key is already present; + * external memory must therefore be accessed + * + * \param value what to insert + * \return a tuple whose second part is true iff the value was actually + * added (no value with the same key present); the first part is an + * iterator pointing to the newly inserted or already stored value + */ + std::pair insert(const value_type& value) + { + if (buckets_.size() == 0) + _rebuild_buckets(128); + + internal_size_type i_bucket = _bkt_num(value.first); + bucket_type& bucket = buckets_[i_bucket]; + node_type* node = _find_key_internal(bucket, value.first); + + // found value in internal memory + if (node && _eq(node->value_.first, value.first)) + { + bool old_deleted = node->deleted(); + if (old_deleted) + { + node->set_deleted(false); + node->value_ = value; + ++num_total_; + } + return std::pair( + iterator(this, i_bucket, node, + 0, src_internal, false, value.first), old_deleted); + } + // search external memory ... + else + { + tuple result + = _find_key_external(bucket, value.first); + + external_size_type i_external = result.first; + value_type ext_value = result.second; + + // ... if found, return iterator pointing to external position ... + if (i_external < bucket.n_external_ && _eq(ext_value.first, value.first)) + { + return std::pair( + iterator(this, i_bucket, node, + i_external, src_external, true, value.first), false); + } + // ... otherwise create a new buffer-node to add the value + else + { + ++num_total_; + node_type* new_node = + node + ? node->set_next(_new_node(value, node->next(), false)) + : (bucket.list_ = _new_node(value, bucket.list_, false)); + + iterator it(this, i_bucket, new_node, + 0, src_internal, false, value.first); + + ++buffer_size_; + if (buffer_size_ >= max_buffer_size_) + _rebuild_buckets(); // will fix it as well + + return std::pair(it, true); + } + } + } + + //! Insert a value; external memory is not accessed so that another value + //! with the same key may be overwritten + //! \param value what to insert + //! \return iterator pointing to the inserted value + iterator insert_oblivious(const value_type& value) + { + internal_size_type i_bucket = _bkt_num(value.first); + bucket_type& bucket = buckets_[i_bucket]; + node_type* node = _find_key_internal(bucket, value.first); + + // found value in internal memory + if (node && _eq(node->value_.first, value.first)) + { + if (node->deleted()) + ++num_total_; + + node->set_deleted(false); + node->value_ = value; + return iterator(this, i_bucket, node, + 0, src_internal, false, value.first); + } + // not found; ignore external memory and add a new node to the + // internal-memory buffer + else + { + oblivious_ = true; + ++num_total_; + node_type* new_node = + node + ? node->set_next(_new_node(value, node->next(), false)) + : (bucket.list_ = _new_node(value, bucket.list_, false)); + + // there may be some iterators that reference the newly inserted + // value in external memory these need to be fixed (make them point + // to new_node) + iterator_map_.fix_iterators_2int(i_bucket, value.first, new_node); + + iterator it(this, i_bucket, new_node, + 0, src_internal, false, value.first); + + ++buffer_size_; + if (buffer_size_ >= max_buffer_size_) + _rebuild_buckets(); + + return it; + } + } + + //! Erase value by iterator + //! \param it iterator pointing to the value to erase + void erase(const_iterator it) + { + --num_total_; + bucket_type& bucket = buckets_[it.i_bucket_]; + + if (it.source_ == src_internal) + { + it.node_->set_deleted(true); + iterator_map_.fix_iterators_2end(it.i_bucket_, it.key_); + } + else { + // find biggest value < iterator's value + node_type* node = _find_key_internal(bucket, it.key_); + assert(!node || !_eq(node->value_.first, it.key_)); + + // add delete-node to buffer + if (node) + node->set_next(_new_node(value_type(it.key_, mapped_type()), node->next(), true)); + else + bucket.list_ = _new_node(value_type(it.key_, mapped_type()), bucket.list_, true); + + iterator_map_.fix_iterators_2end(it.i_bucket_, it.key_); + + ++buffer_size_; + if (buffer_size_ >= max_buffer_size_) + _rebuild_buckets(); + } + } + + //! Erase value by key; check external memory + //! \param key key of value to erase + //! \return number of values actually erased (0 or 1) + external_size_type erase(const key_type& key) + { + internal_size_type i_bucket = _bkt_num(key); + bucket_type& bucket = buckets_[i_bucket]; + node_type* node = _find_key_internal(bucket, key); + + // found in internal memory + if (node && _eq(node->value_.first, key)) + { + if (!node->deleted()) + { + node->set_deleted(true); + --num_total_; + iterator_map_.fix_iterators_2end(i_bucket, key); + return 1; + } + else + return 0; // already deleted + } + // check external memory + else + { + tuple result + = _find_key_external(bucket, key); + + external_size_type i_external = result.first; + value_type ext_value = result.second; + + // found in external memory; add delete-node + if (i_external < bucket.n_external_ && _eq(ext_value.first, key)) + { + --num_total_; + + if (node) + node->set_next(_new_node(value_type(key, mapped_type()), node->next(), true)); + else + bucket.list_ = _new_node(value_type(key, mapped_type()), bucket.list_, true); + + iterator_map_.fix_iterators_2end(i_bucket, key); + + ++buffer_size_; + if (buffer_size_ >= max_buffer_size_) + _rebuild_buckets(); + + return 1; + } + // no value with given key + else + return 0; + } + } + + //! Erase value by key but without looking at external memory + //! \param key key for value to release + void erase_oblivious(const key_type& key) + { + internal_size_type i_bucket = _bkt_num(key); + bucket_type& bucket = buckets_[i_bucket]; + node_type* node = _find_key_internal(bucket, key); + + // found value in internal-memory + if (node && _eq(node->value_.first, key)) + { + if (!node->deleted()) + { + --num_total_; + node->set_deleted(true); + iterator_map_.fix_iterators_2end(i_bucket, key); + } + } + // not found; ignore external memory and add delete-node + else + { + oblivious_ = true; + --num_total_; + + if (node) + node->set_next(_new_node(value_type(key, mapped_type()), node->next(), true)); + else + bucket.list_ = _new_node(value_type(key, mapped_type()), bucket.list_, true); + + iterator_map_.fix_iterators_2end(i_bucket, key); + + ++buffer_size_; + if (buffer_size_ >= max_buffer_size_) + _rebuild_buckets(); + } + } + + //! Reset hash-map: erase all values, invalidate all iterators + void clear() + { + STXXL_VERBOSE_HASH_MAP("clear()"); + + iterator_map_.fix_iterators_all2end(); + block_cache_.flush(); + block_cache_.clear(); + + // reset buckets and release buffer-memory + for (internal_size_type i_bucket = 0; + i_bucket < buckets_.size(); i_bucket++) + { + _erase_nodes(buckets_[i_bucket].list_, NULL); + buckets_[i_bucket] = bucket_type(); + } + oblivious_ = false; + num_total_ = 0; + buffer_size_ = 0; + + // free external memory + block_manager* bm = block_manager::get_instance(); + bm->delete_blocks(bids_.begin(), bids_.end()); + bids_.clear(); + } + + //! Exchange stored values with another hash-map + //! \param obj hash-map to swap values with + void swap(self_type& obj) + { + std::swap(buckets_, obj.buckets_); + std::swap(bids_, obj.bids_); + + std::swap(oblivious_, obj.oblivious_); + std::swap(num_total_, obj.num_total_); + + std::swap(node_allocator_, obj.node_allocator_); + + std::swap(hash_, obj.hash_); + std::swap(cmp_, obj.cmp_); + + std::swap(buffer_size_, obj.buffer_size_); + std::swap(max_buffer_size_, obj.max_buffer_size_); + + std::swap(opt_load_factor_, obj.opt_load_factor_); + + std::swap(iterator_map_, obj.iterator_map_); + + std::swap(block_cache_, obj.block_cache_); + } + +protected: + // find statistics + mutable external_size_type n_subblocks_loaded; + mutable external_size_type n_found_internal; + mutable external_size_type n_found_external; + mutable external_size_type n_not_found; + +public: + //! Reset hash-map statistics + void reset_statistics() + { + block_cache_.reset_statistics(); + n_subblocks_loaded = n_found_external = n_found_internal = n_not_found = 0; + } + + //! Print short general statistics to output stream + void print_statistics(std::ostream& o = std::cout) const + { + o << "Find-statistics:" << std::endl; + o << " Found internal : " << n_found_internal << std::endl; + o << " Found external : " << n_found_external << std::endl; + o << " Not found : " << n_not_found << std::endl; + o << " Subblocks searched : " << n_subblocks_loaded << std::endl; + + iterator_map_.print_statistics(o); + block_cache_.print_statistics(o); + } + + //! Look up value by key. Non-const access. + //! \param key key for value to look up + iterator find(const key_type& key) + { + if (buffer_size_ + 1 >= max_buffer_size_) // (*) + _rebuild_buckets(); + + internal_size_type i_bucket = _bkt_num(key); + bucket_type& bucket = buckets_[i_bucket]; + node_type* node = _find_key_internal(bucket, key); + + // found in internal-memory buffer + if (node && _eq(node->value_.first, key)) { + n_found_internal++; + if (node->deleted()) + return this->_end(); + else + return iterator(this, i_bucket, node, 0, src_internal, false, key); + } + // search external elements + else { + tuple result + = _find_key_external(bucket, key); + + external_size_type i_external = result.first; + value_type value = result.second; + + // found in external memory + if (i_external < bucket.n_external_ && _eq(value.first, key)) { + n_found_external++; + + // we ultimately expect the user to de-reference the returned + // iterator to change its value (non-const!). to prevent an + // additional disk-access, we create a new node in the + // internal-memory buffer overwriting the external value. + // note: by checking and rebuilding (if neccessary) in (*) we + // made sure that the new node will fit into the buffer and no + // rebuild is neccessary here. + node_type* new_node = + node + ? node->set_next(_new_node(value, node->next(), false)) + : (bucket.list_ = _new_node(value, bucket.list_, false)); + + ++buffer_size_; + + iterator_map_.fix_iterators_2int(i_bucket, value.first, new_node); + + return iterator(this, i_bucket, new_node, i_external + 1, src_internal, true, key); + } + // not found in external memory + else { + n_not_found++; + return this->_end(); + } + } + } + + //! Look up value by key. Const access. + //! \param key key for value to look up + const_iterator find(const key_type& key) const + { + internal_size_type i_bucket = _bkt_num(key); + const bucket_type& bucket = buckets_[i_bucket]; + node_type* node = _find_key_internal(bucket, key); + + // found in internal-memory buffer + if (node && _eq(node->value_.first, key)) { + n_found_internal++; + if (node->deleted()) + return this->_end(); + else + return const_iterator((self_type*)this, i_bucket, node, 0, src_internal, false, key); + } + // search external elements + else { + tuple result + = _find_key_external(bucket, key); + + external_size_type i_external = result.first; + value_type value = result.second; + + // found in external memory + if (i_external < bucket.n_external_ && _eq(value.first, key)) { + n_found_external++; + return const_iterator((self_type*)this, i_bucket, node, i_external, src_external, true, key); + } + // not found in external memory + else { + n_not_found++; + return this->_end(); + } + } + } + + //! Number of values with given key + //! \param k key for value to look up + //! \return 0 or 1 depending on the presence of a value with the given key + external_size_type count(const key_type& k) const + { + const_iterator cit = find(k); + return (cit == end()) ? 0 : 1; + } + + //! Finds a range containing all values with given key. Non-const access + //! \param key key to look for# + //! \return range may be empty or contains exactly one value + std::pair equal_range(const key_type& key) + { + iterator it = find(key); + return std::pair(it, it); + } + + //! Finds a range containing all values with given key. Const access + //! \param key key to look for# + //! \return range may be empty or contains exactly one value + std::pair equal_range(const key_type& key) const + { + const_iterator cit = find(key); + return std::pair(cit, cit); + } + + //! Convenience operator to quickly insert or find values. Use with caution + //! since using this operator will check external-memory. + mapped_type& operator [] (const key_type& key) + { + if (buffer_size_ + 1 >= max_buffer_size_) // (*) + _rebuild_buckets(); + + internal_size_type i_bucket = _bkt_num(key); + bucket_type& bucket = buckets_[i_bucket]; + node_type* node = _find_key_internal(bucket, key); + + // found in internal-memory buffer + if (node && _eq(node->value_.first, key)) { + if (node->deleted()) { + node->set_deleted(false); + node->value_.second = mapped_type(); + ++num_total_; + } + return node->value_.second; + } + // search external elements + else { + tuple result + = _find_key_external(bucket, key); + + external_size_type i_external = result.first; + value_type found_value = result.second; + + value_type buffer_value = + (i_external < bucket.n_external_ && _eq(found_value.first, key)) + ? found_value + : value_type(key, mapped_type()); + + // add a new node to the buffer. this new node's value overwrites + // the external value if it was found and otherwise is set to (key, + // mapped_type()) + node_type* new_node = + node + ? node->set_next(_new_node(buffer_value, node->next(), false)) + : (bucket.list_ = _new_node(buffer_value, bucket.list_, false)); + + ++buffer_size_; + // note that we already checked the buffer-size in (*) + + return new_node->value_.second; + } + } + + //! Number of buckets + internal_size_type bucket_count() const + { return buckets_.size(); } + + //! Maximum number of buckets + internal_size_type max_bucket_count() const + { return (internal_size_type)(max_size() / subblock_size); } + + //! Bucket-index for values with given key. + internal_size_type bucket_index(const key_type& k) const + { return _bkt_num(k); } + +public: + //! Average number of (sub)blocks occupied by a bucket. + float load_factor() const + { return (float)num_total_ / ((float)subblock_size * (float)buckets_.size()); } + + //! Get desired load-factor + float opt_load_factor() const { return opt_load_factor_; } + + //! Set desired load-factor + void opt_load_factor(float z) + { + opt_load_factor_ = z; + if (load_factor() > opt_load_factor_) + _rebuild_buckets(); + } + + //! Rehash with (at least) n buckets + void rehash(internal_size_type n = 0) + { + _rebuild_buckets(n); + } + + //! Number of bytes occupied by buffer + internal_size_type buffer_size() const + { + // buffer-size internally stored as number of nodes + return buffer_size_ * sizeof(node_type); + } + + //! Maximum buffer size in byte + internal_size_type max_buffer_size() const + { + return max_buffer_size_ * sizeof(node_type); + } + + //! Set maximum buffer size + //! \param buffer_size new size in byte + void max_buffer_size(internal_size_type buffer_size) + { + max_buffer_size_ = buffer_size / sizeof(node_type); + if (buffer_size_ >= max_buffer_size_) + _rebuild_buckets(); + } + +protected: + //! iterator pointing to the beginnning of the hash-map + template + Iterator _begin() const + { + self_type* non_const_this = (self_type*)this; + + if (buckets_.size() == 0) + return _end(); + + // correct key will be set by find_next() + Iterator it(non_const_this, 0, buckets_[0].list_, + 0, src_unknown, true, key_type()); + it.find_next(); + + return it; + } + + //! iterator pointing to the end of the hash-map (iterator-type as + //! template-parameter) + template + Iterator _end() const + { + self_type* non_const_this = (self_type*)this; + return Iterator(non_const_this); + } + +public: + //! Returns an iterator pointing to the beginning of the hash-map + iterator begin() { return _begin(); } + + //! Returns a const_interator pointing to the beginning of the hash-map + const_iterator begin() const { return _begin(); } + + //! Returns an iterator pointing to the end of the hash-map + iterator end() { return _end(); } + + //! Returns a const_iterator pointing to the end of the hash-map + const_iterator end() const { return _end(); } + +protected: + //! Allocate a new buffer-node + node_type * _get_node() + { + return node_allocator_.allocate(1); + } + + //! Free given node + void _put_node(node_type* node) + { + node_allocator_.deallocate(node, 1); + } + + //! Allocate a new buffer-node and initialize with given value, node and + //! deleted-flag + node_type * _new_node(const value_type& value, node_type* nxt, bool del) + { + node_type* node = _get_node(); + node->value_ = value; + node->set_next(nxt); + node->set_deleted(del); + return node; + } + + //! Free nodes in range [first, last). If last is NULL all nodes will be + //! freed. + void _erase_nodes(node_type* first, node_type* last) + { + node_type* curr = first; + while (curr != last) + { + node_type* next = curr->next(); + _put_node(curr); + curr = next; + } + } + + //! Bucket-index for values with given key + internal_size_type _bkt_num(const key_type& key) const + { + return _bkt_num(key, buckets_.size()); + } + + /*! + * Bucket-index for values with given key. The total number of buckets has + * to be specified as well. The bucket number is determined by \f$ + * bucket_num = (hash/max_hash)*n_buckets \f$ max_hash is in fact 2^63-1 + * (internal_size_type=uint64 (or uint32)) but we rather divide by 2^64, so + * we can use plain integer arithmetic easily (there should be only a small + * difference): this way we must only calculate the upper 64 bits of the + * product hash*n_buckets and we're done. See + * http://www.cs.uaf.edu/~cs301/notes/Chapter5/node5.html + */ + internal_size_type _bkt_num(const key_type& key, internal_size_type n) const + { + //! TODO maybe specialize double arithmetic to integer. the old code + //! was faulty -tb. + return (internal_size_type)( + (double)n * ((double)hash_(key) / (double)std::numeric_limits::max()) + ); + } + + /*! + * Locate the given key in the internal-memory chained list. If the key is + * not present, the node with the biggest key smaller than the given key is + * returned. Note that the returned value may be zero: either because the + * chained list is empty or because the given key is smaller than all other + * keys in the chained list. + */ + node_type* + _find_key_internal(const bucket_type& bucket, const key_type& key) const + { + node_type* old = NULL; + for (node_type* curr = bucket.list_; + curr && _leq(curr->value_.first, key); + curr = curr->next()) + { + old = curr; + } + return old; + } + + /*! + * Search for key in external part of bucket. Return value is (i_external, + * value), where i_ext = bucket._num_external if key could not be found. + */ + tuple + _find_key_external(const bucket_type& bucket, const key_type& key) const + { + subblock_type* subblock; + + // number of subblocks occupied by bucket + internal_size_type n_subblocks = (internal_size_type)( + bucket.n_external_ / subblock_size + ); + if (bucket.n_external_ % subblock_size != 0) + n_subblocks++; + + for (internal_size_type i_subblock = 0; + i_subblock < n_subblocks; i_subblock++) + { + subblock = _load_subblock(bucket, i_subblock); + // number of values in i-th subblock + internal_size_type n_values = + (i_subblock + 1 < n_subblocks) + ? (internal_size_type)subblock_size + : (internal_size_type)( + bucket.n_external_ - i_subblock * subblock_size + ); + + //! TODO: replace with bucket.n_external_ % subblock_size + + // biggest key in current subblock still too small => next subblock + if (_lt((*subblock)[n_values - 1].first, key)) + continue; + + // binary search in current subblock + internal_size_type i_lower = 0, i_upper = n_values; + while (i_lower + 1 != i_upper) + { + internal_size_type i_middle = (i_lower + i_upper) / 2; + if (_leq((*subblock)[i_middle].first, key)) + i_lower = i_middle; + else + i_upper = i_middle; + } + + value_type value = (*subblock)[i_lower]; + + if (_eq(value.first, key)) + return tuple + (i_subblock * subblock_size + i_lower, value); + else + return tuple + (bucket.n_external_, value_type()); + } + + return tuple + (bucket.n_external_, value_type()); + } + + /*! + * Load the given bucket's i-th subblock. + * Since a bucket may be spread over several blocks, we must + * 1. determine in which block the requested subblock is located + * 2. at which position within the obove-mentioned block the questioned subblock is located + */ + subblock_type* + _load_subblock(const bucket_type& bucket, internal_size_type which_subblock) const + { + n_subblocks_loaded++; + + // index of the requested subblock counted from the very beginning of + // the bucket's first block + external_size_type i_abs_subblock = bucket.i_subblock_ + which_subblock; + + /* 1. */ + bid_type bid = bids_[bucket.i_block_ + (internal_size_type)(i_abs_subblock / subblocks_per_block)]; + /* 2. */ + internal_size_type i_subblock_within = (internal_size_type)(i_abs_subblock % subblocks_per_block); + + return block_cache_.get_subblock(bid, i_subblock_within); + } + + typedef HashedValue hashed_value_type; + + //! Functor to extracts the actual value from a HashedValue-struct + struct HashedValueExtractor + { + value_type& operator () (hashed_value_type& hvalue) + { return hvalue.value_; } + }; + + /*! + * Will return from its input-stream all values that are to be stored in + * the given bucket. Those values must appear in consecutive order + * beginning with the input-stream's current value. + */ + template + struct HashingStream + { + typedef typename InputStream::value_type value_type; + + self_type* map_; + InputStream& input_; + internal_size_type i_bucket_; + external_size_type bucket_size_; + value_type value_; + bool empty_; + ValueExtractor vextract_; + + HashingStream(InputStream& input, internal_size_type i_bucket, + ValueExtractor vextract, self_type* map) + : map_(map), + input_(input), + i_bucket_(i_bucket), + bucket_size_(0), + vextract_(vextract) + { + empty_ = find_next(); + } + + const value_type& operator * () { return value_; } + + bool empty() const { return empty_; } + + void operator ++ () + { + ++input_; + empty_ = find_next(); + } + + bool find_next() + { + if (input_.empty()) + return true; + value_ = *input_; + if (map_->_bkt_num(vextract_(value_).first) != i_bucket_) + return true; + + ++bucket_size_; + return false; + } + }; + + /* Rebuild hash-map. The desired number of buckets may be supplied. */ + void _rebuild_buckets(internal_size_type n_desired = 0) + { + STXXL_VERBOSE_HASH_MAP("_rebuild_buckets()"); + + typedef buffered_writer writer_type; + typedef HashedValuesStream values_stream_type; + typedef HashingStream hashing_stream_type; + + const int_type write_buffer_size = config::get_instance()->disks_number() * 4; + + // determine new number of buckets from desired load_factor ... + internal_size_type n_new; + n_new = (internal_size_type)ceil((double)num_total_ / ((double)subblock_size * (double)opt_load_factor())); + + // ... but give the user the chance to request even more buckets + if (n_desired > n_new) + n_new = std::min(n_desired, max_bucket_count()); + + // allocate new buckets and bids + buckets_container_type old_buckets(n_new); + std::swap(buckets_, old_buckets); + + bid_container_type old_bids; + std::swap(bids_, old_bids); + + // read stored values in consecutive order + + // use new to control point of destruction (see below) + reader_type* reader + = new reader_type(old_bids.begin(), old_bids.end(), block_cache_); + + values_stream_type values_stream(old_buckets.begin(), old_buckets.end(), + *reader, old_bids.begin(), *this); + + writer_type writer(&bids_, write_buffer_size, write_buffer_size / 2); + + // re-distribute values among new buckets. + + // this makes use of the fact that if value1 preceeds value2 before + // resizing, value1 will preceed value2 after resizing as well (uniform + // rehashing) + num_total_ = 0; + for (internal_size_type i_bucket = 0; + i_bucket < buckets_.size(); i_bucket++) + { + buckets_[i_bucket] = bucket_type(); + buckets_[i_bucket].i_block_ = writer.i_block(); + buckets_[i_bucket].i_subblock_ = writer.i_subblock(); + + hashing_stream_type hasher(values_stream, i_bucket, HashedValueExtractor(), this); + external_size_type i_ext = 0; + while (!hasher.empty()) + { + const hashed_value_type& hvalue = *hasher; + iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, i_ext); + + writer.append(hvalue.value_); + ++hasher; + ++i_ext; + } + + writer.finish_subblock(); + buckets_[i_bucket].n_external_ = hasher.bucket_size_; + num_total_ += hasher.bucket_size_; + } + writer.flush(); + // reader must be deleted before deleting old_bids because its + // destructor will dereference the bid-iterator + delete reader; + block_cache_.clear(); + + // get rid of old blocks and buckets + block_manager* bm = stxxl::block_manager::get_instance(); + bm->delete_blocks(old_bids.begin(), old_bids.end()); + + for (internal_size_type i_bucket = 0; + i_bucket < old_buckets.size(); i_bucket++) + { + _erase_nodes(old_buckets[i_bucket].list_, NULL); + old_buckets[i_bucket] = bucket_type(); + } + + buffer_size_ = 0; + oblivious_ = false; + } + + /*! + * Stream for filtering duplicates. Used to eliminate duplicated values + * when bulk-inserting Note: input has to be sorted, so that duplicates + * will occure in row + */ + template + struct UniqueValueStream + { + typedef typename InputStream::value_type value_type; + self_type& map_; + InputStream& in_; + + UniqueValueStream(InputStream& input, self_type& map) + : map_(map), in_(input) + { } + + bool empty() const { return in_.empty(); } + + const value_type& operator * () { return *in_; } + + void operator ++ () + { + value_type v_old = *in_; + ++in_; + while (!in_.empty() && v_old.first == (*in_).first) + ++in_; + } + }; + + template + struct AddHashStream + { + //! (hash,value) + typedef std::pair value_type; + self_type& map_; + InputStream& in_; + + AddHashStream(InputStream& input, self_type& map) + : map_(map), in_(input) + { } + + bool empty() const { return in_.empty(); } + + value_type operator * () + { return value_type(map_.hash_((*in_).first), *in_); } + + void operator ++ () { ++in_; } + }; + + /*! + * Extracts the value-part (ignoring the hashvalue); required by + * HashingStream (see above) + */ + struct StripHashFunctor + { + const value_type& operator () (std::pair& v) + { return v.second; } + }; + + /*! + * Comparator object for values as required by stxxl::sort. Sorting is done + * lexicographically by Note: the hash-value has already + * been computed. + */ + struct Cmp : public std::binary_function< + std::pair, + std::pair, bool + > + { + self_type& map_; + Cmp(self_type& map) : map_(map) { } + + bool operator () (const std::pair& a, + const std::pair& b) const + { + return (a.first < b.first) || + ((a.first == b.first) && map_.cmp_(a.second.first, b.second.first)); + } + std::pair min_value() const + { + return std::pair( + std::numeric_limits::min(), + value_type(map_.cmp_.min_value(), mapped_type()) + ); + } + std::pair max_value() const + { + return std::pair( + std::numeric_limits::max(), + value_type(map_.cmp_.max_value(), mapped_type()) + ); + } + }; + +public: + //! Bulk-insert of values in the range [f, l) + //! \param f beginning of the range + //! \param l end of the range + //! \param mem internal memory that may be used (note: this memory will be used additionally to the buffer). The more the better + template + void insert(InputIterator f, InputIterator l, internal_size_type mem) + { + //! values already stored in the hashtable ("old values") + typedef HashedValuesStream old_values_stream; + //! old values, that are to be stored in a certain (new) bucket + typedef HashingStream old_hashing_stream; + + //! values to insert ("new values") + typedef typename stxxl::stream::streamify_traits::stream_type input_stream; + + //! new values with added hash: (hash, (key, mapped)) + typedef AddHashStream new_values_stream; + //! new values sorted by + typedef stxxl::stream::sort new_sorted_values_stream; + //! new values sorted by with duplicates eliminated + typedef UniqueValueStream new_unique_values_stream; + //! new values, that are to be stored in a certain bucket + typedef HashingStream new_hashing_stream; + + typedef buffered_writer writer_type; + + int_type write_buffer_size = config::get_instance()->disks_number() * 2; + + // calculate new number of buckets + external_size_type num_total_new = num_total_ + (l - f); // estimated number of elements + external_size_type n_buckets_new = (external_size_type)ceil((double)num_total_new / ((double)subblock_size * (double)opt_load_factor())); + if (n_buckets_new > max_bucket_count()) + n_buckets_new = max_bucket_count(); + + STXXL_VERBOSE_HASH_MAP("insert() items=" << (l - f) << " buckets_new=" << n_buckets_new); + + // prepare new buckets and bids + buckets_container_type old_buckets((internal_size_type)n_buckets_new); + std::swap(buckets_, old_buckets); + // writer will allocate new blocks as necessary + bid_container_type old_bids; + std::swap(bids_, old_bids); + + // already stored values ("old values") + reader_type* reader = new reader_type(old_bids.begin(), old_bids.end(), + block_cache_); + old_values_stream old_values(old_buckets.begin(), old_buckets.end(), + *reader, old_bids.begin(), *this); + + // values to insert ("new values") + input_stream input = stxxl::stream::streamify(f, l); + new_values_stream new_values(input, *this); + new_sorted_values_stream new_sorted_values(new_values, Cmp(*this), mem); + new_unique_values_stream new_unique_values(new_sorted_values, *this); + + writer_type writer(&bids_, write_buffer_size, write_buffer_size / 2); + + num_total_ = 0; + for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) + { + buckets_[i_bucket] = bucket_type(); + buckets_[i_bucket].i_block_ = writer.i_block(); + buckets_[i_bucket].i_subblock_ = writer.i_subblock(); + + old_hashing_stream old_hasher(old_values, i_bucket, HashedValueExtractor(), this); + new_hashing_stream new_hasher(new_unique_values, i_bucket, StripHashFunctor(), this); + internal_size_type bucket_size = 0; + + // more old and new values for the current bucket => choose smallest + while (!old_hasher.empty() && !new_hasher.empty()) + { + internal_size_type old_hash = hash_((*old_hasher).value_.first); + internal_size_type new_hash = (*new_hasher).first; + key_type old_key = (*old_hasher).value_.first; + key_type new_key = (*new_hasher).second.first; + + // old value wins + if ((old_hash < new_hash) || (old_hash == new_hash && cmp_(old_key, new_key))) // (_lt((*old_hasher)._value.first, (*new_hasher).second.first)) + { + const hashed_value_type& hvalue = *old_hasher; + iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, bucket_size); + writer.append(hvalue.value_); + ++old_hasher; + } + // new value smaller or equal => new value wins + else + { + if (_eq(old_key, new_key)) + { + const hashed_value_type& hvalue = *old_hasher; + iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, bucket_size); + ++old_hasher; + } + writer.append((*new_hasher).second); + ++new_hasher; + } + ++bucket_size; + } + // no more new values for the current bucket + while (!old_hasher.empty()) + { + const hashed_value_type& hvalue = *old_hasher; + iterator_map_.fix_iterators_2ext(hvalue.i_bucket_, hvalue.value_.first, i_bucket, bucket_size); + writer.append(hvalue.value_); + ++old_hasher; + ++bucket_size; + } + // no more old values for the current bucket + while (!new_hasher.empty()) + { + writer.append((*new_hasher).second); + ++new_hasher; + ++bucket_size; + } + + writer.finish_subblock(); + buckets_[i_bucket].n_external_ = bucket_size; + num_total_ += bucket_size; + } + writer.flush(); + delete reader; + block_cache_.clear(); + + // release old blocks + block_manager* bm = stxxl::block_manager::get_instance(); + bm->delete_blocks(old_bids.begin(), old_bids.end()); + + // free nodes in old bucket lists + for (internal_size_type i_bucket = 0; + i_bucket < old_buckets.size(); i_bucket++) + { + _erase_nodes(old_buckets[i_bucket].list_, NULL); + old_buckets[i_bucket] = bucket_type(); + } + + buffer_size_ = 0; + oblivious_ = false; + } + +protected: + /* 1 iff a < b + The comparison is done lexicographically by (hash-value, key) + */ + bool _lt(const key_type& a, const key_type& b) const + { + internal_size_type hash_a = hash_(a); + internal_size_type hash_b = hash_(b); + + return (hash_a < hash_b) || + ((hash_a == hash_b) && cmp_(a, b)); + } + + //! true iff a > b + bool _gt(const key_type& a, const key_type& b) const { return _lt(b, a); } + //! true iff a <= b + bool _leq(const key_type& a, const key_type& b) const { return !_gt(a, b); } + //! true iff a >= b + bool _geq(const key_type& a, const key_type& b) const { return !_lt(a, b); } + + //! true iff a == b. note: it is mandatory that equal keys yield equal + //! hash-values => hashing not neccessary for equality-testing. + bool _eq(const key_type& a, const key_type& b) const + { return !cmp_(a, b) && !cmp_(b, a); } + + friend class hash_map_iterator_base; + friend class hash_map_iterator; + friend class hash_map_const_iterator; + friend class iterator_map; + friend class block_cache; + friend struct HashedValuesStream; + +#if 1 + void _dump_external() + { + reader_type reader(bids_.begin(), bids_.end(), &block_cache_); + + for (internal_size_type i_block = 0; i_block < bids_.size(); i_block++) { + std::cout << "block " << i_block << ":\n"; + + for (internal_size_type i_subblock = 0; i_subblock < subblocks_per_block; i_subblock++) { + std::cout << " subblock " << i_subblock << ":\n "; + + for (external_size_type i_element = 0; i_element < subblocks_per_block; i_element++) { + std::cout << reader.const_value().first << ", "; + ++reader; + } + std::cout << std::endl; + } + } + } + + void _dump_buckets() + { + reader_type reader(bids_.begin(), bids_.end(), &block_cache_); + + std::cout << "number of buckets: " << buckets_.size() << std::endl; + for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { + const bucket_type& bucket = buckets_[i_bucket]; + reader.skip_to(bids_.begin() + bucket.i_block_, bucket.i_subblock_); + + std::cout << " bucket " << i_bucket << ": block=" << bucket.i_block_ << ", subblock=" << bucket.i_subblock_ << ", external=" << bucket.n_external_ << std::endl; + + node_type* node = bucket.list_; + std::cout << " internal_list="; + while (node) { + std::cout << node->value_.first << " (del=" << node->deleted() << "), "; + node = node->next(); + } + std::cout << std::endl; + + std::cout << " external="; + for (external_size_type i_element = 0; i_element < bucket.n_external_; i_element++) { + std::cout << reader.const_value().first << ", "; + ++reader; + } + std::cout << std::endl; + } + } + + void _dump_bucket_statistics() + { + std::cout << "number of buckets: " << buckets_.size() << std::endl; + for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) { + const bucket_type& bucket = buckets_[i_bucket]; + std::cout << " bucket " << i_bucket << ": block=" << bucket.i_block_ << ", subblock=" << bucket.i_subblock_ << ", external=" << bucket.n_external_ << ", list=" << bucket.list_ << std::endl; + } + } +#endif + +public: + //! Construct an equality predicate from the comparison operator + struct equal_to : public std::binary_function + { + //! reference to hash_map + const self_type& m_map; + + //! constructor requires reference to hash_map + equal_to(const self_type& map) : m_map(map) { } + + //! return whether the arguments compare equal (x==y). + bool operator () (const key_type& x, const key_type& y) const + { + return m_map._eq(x, y); + } + + //! C++11 required type + typedef key_type first_argument_type; + //! C++11 required type + typedef key_type second_argument_type; + //! C++11 required type + typedef bool result_type; + }; + + //! Type of constructed equality predicate + typedef equal_to key_equal; + + //! Constructed equality predicate used by this hash-map + key_equal key_eq() const + { + return equal_to(*this); + } + +public: + //! Even more statistics: Number of buckets, number of values, buffer-size, + //! values per bucket + void print_load_statistics(std::ostream& o = std::cout) const + { + external_size_type sum_external = 0; + external_size_type square_sum_external = 0; + external_size_type max_external = 0; + + for (internal_size_type i_bucket = 0; i_bucket < buckets_.size(); i_bucket++) + { + const bucket_type& b = buckets_[i_bucket]; + + sum_external += b.n_external_; + square_sum_external += b.n_external_ * b.n_external_; + if (b.n_external_ > max_external) + max_external = b.n_external_; + } + + double avg_external = (double)sum_external / (double)buckets_.size(); + double std_external = sqrt(((double)square_sum_external / (double)buckets_.size()) - (avg_external * avg_external)); + + o << "Bucket count : " << buckets_.size() << std::endl; + o << "Values total : " << num_total_ << std::endl; + o << "Values buffered : " << buffer_size_ << std::endl; + o << "Max Buffer-Size : " << max_buffer_size_ << std::endl; + o << "Max external/bucket : " << max_external << std::endl; + o << "Avg external/bucket : " << avg_external << std::endl; + o << "Std external/bucket : " << std_external << std::endl; + o << "Load-factor : " << load_factor() << std::endl; + o << "Blocks allocated : " << bids_.size() << " => " << (bids_.size() * block_type::raw_size) << " bytes" << std::endl; + o << "Bytes per value : " << ((double)(bids_.size() * block_type::raw_size) / (double)num_total_) << std::endl; + } +}; /* end of class hash_map */ + +} // namespace hash_map + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::hash_map::hash_map& a, + stxxl::hash_map::hash_map& b) +{ + if (&a != &b) + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_HASH_MAP_HASH_MAP_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator.h new file mode 100644 index 0000000000..b8f1760ab7 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator.h @@ -0,0 +1,587 @@ +/*************************************************************************** + * include/stxxl/bits/containers/hash_map/iterator.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Markus Westphal + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_CONTAINERS_HASH_MAP_ITERATOR_HEADER +#define STXXL_CONTAINERS_HASH_MAP_ITERATOR_HEADER + +#include +#include + +#include + +STXXL_BEGIN_NAMESPACE + +namespace hash_map { + +template +class iterator_map; +template +class hash_map_iterator; +template +class hash_map_const_iterator; +template +class block_cache; + +template +class hash_map_iterator_base +{ +public: + friend class iterator_map; + friend void HashMap::erase(hash_map_const_iterator it); + + typedef HashMap hash_map_type; + typedef typename hash_map_type::internal_size_type internal_size_type; + typedef typename hash_map_type::external_size_type external_size_type; + typedef typename hash_map_type::value_type value_type; + typedef typename hash_map_type::key_type key_type; + typedef typename hash_map_type::reference reference; + typedef typename hash_map_type::const_reference const_reference; + typedef typename hash_map_type::node_type node_type; + typedef typename hash_map_type::bucket_type bucket_type; + typedef typename hash_map_type::bid_iterator_type bid_iterator_type; + typedef typename hash_map_type::source_type source_type; + + typedef buffered_reader reader_type; + + typedef std::forward_iterator_tag iterator_category; + +protected: + HashMap* map_; + reader_type* reader_; + //! true if prefetching enabled; false by default, will be set to true when + //! incrementing (see find_next()) + bool prefetch_; + //! index of current bucket + internal_size_type i_bucket_; + //! source of current value: external or internal + source_type source_; + //! current (source=internal) or old (src=external) internal node + node_type* node_; + //! position of current (source=external) or next (source=internal) + //! external value (see _ext_valid) + external_size_type i_external_; + //! key of current value + key_type key_; + /*! true if i_external points to the current or next external value + + example: iterator was created by hash_map::find() and the value was found + in internal memory + + => iterator pointing to internal node is created and location of next + external value is unknown (_ext_valid == false) + + => when incrementing the iterator the external values will be scanned + from the beginning of the bucket to find the valid external index + */ + bool ext_valid_; + //! true if iterator equals end() + bool end_; + +public: + //! Construct a new iterator + hash_map_iterator_base(HashMap* map, internal_size_type i_bucket, node_type* node, + external_size_type i_external, source_type source, + bool ext_valid, key_type key) + : map_(map), + reader_(NULL), + prefetch_(false), + i_bucket_(i_bucket), + source_(source), + node_(node), + i_external_(i_external), + key_(key), + ext_valid_(ext_valid), + end_(false) + { + STXXL_VERBOSE3("hash_map_iterator_base parameter construct addr=" << this); + map_->iterator_map_.register_iterator(*this); + } + + //! Construct a new iterator pointing to the end of the given hash-map. + hash_map_iterator_base(hash_map_type* map) + : map_(map), + reader_(NULL), + prefetch_(false), + i_bucket_(0), + source_(hash_map_type::src_unknown), + node_(NULL), + i_external_(0), + ext_valid_(false), + end_(true) + { } + + //! Construct a new iterator from an existing one + hash_map_iterator_base(const hash_map_iterator_base& obj) + : map_(obj.map_), + reader_(NULL), + prefetch_(obj.prefetch_), + i_bucket_(obj.i_bucket_), + source_(obj.source_), + node_(obj.node_), + i_external_(obj.i_external_), + key_(obj.key_), + ext_valid_(obj.ext_valid_), + end_(obj.end_) + { + STXXL_VERBOSE3("hash_map_iterator_base constr from" << (&obj) << " to " << this); + + if (!end_ && map_) + map_->iterator_map_.register_iterator(*this); + } + + //! Assignment operator + hash_map_iterator_base& operator = (const hash_map_iterator_base& obj) + { + STXXL_VERBOSE3("hash_map_iterator_base copy from" << (&obj) << " to " << this); + + if (&obj != this) + { + if (map_ && !end_) + map_->iterator_map_.unregister_iterator(*this); + + reset_reader(); + + map_ = obj.map_; + i_bucket_ = obj.i_bucket_; + node_ = obj.node_; + source_ = obj.source_; + i_external_ = obj.i_external_; + ext_valid_ = obj.ext_valid_; + prefetch_ = obj.prefetch_; + end_ = obj.end_; + key_ = obj.key_; + + if (map_ && !end_) + map_->iterator_map_.register_iterator(*this); + } + return *this; + } + + //! Two iterators are equal if the point to the same value in the same map + bool operator == (const hash_map_iterator_base& obj) const + { + if (end_ && obj.end_) + return true; + + if (map_ != obj.map_ || + i_bucket_ != obj.i_bucket_ || + source_ != obj.source_) + return false; + + if (source_ == hash_map_type::src_internal) + return node_ == obj.node_; + else + return i_external_ == obj.i_external_; + } + + bool operator != (const hash_map_iterator_base& obj) const + { + return ! operator == (obj); + } + +protected: + //! Initialize reader object to scan external values + void init_reader() + { + const bucket_type& bucket = map_->buckets_[i_bucket_]; + + bid_iterator_type begin = map_->bids_.begin() + bucket.i_block_; + bid_iterator_type end = map_->bids_.end(); + + reader_ = new reader_type(begin, end, map_->block_cache_, + bucket.i_subblock_, prefetch_); + + // external value's index already known + if (ext_valid_) + { + // TODO: speed this up (go directly to i_external_ + for (external_size_type i = 0; i < i_external_; i++) + ++(*reader_); + } + // otw lookup external value. + // case I: no internal value => first external value is the desired one + else if (node_ == NULL) + { + i_external_ = 0; + ext_valid_ = true; + } + // case II: search for smallest external value > internal value + else + { + i_external_ = 0; + while (i_external_ < bucket.n_external_) + { + if (map_->_gt(reader_->const_value().first, node_->value_.first)) + break; + + ++(*reader_); + ++i_external_; + } + // note: i_external==num_external just means that there was no + // external value > internal value (which is perfectly OK) + ext_valid_ = true; + } + } + + //! Reset reader-object + void reset_reader() + { + if (reader_) { + delete reader_; + reader_ = NULL; + } + } + +public: + //! Advance iterator to the next value + //! The next value is determined in the following way + //! - if there are remaining internal or external values in the current + //! bucket, choose the smallest among them, that is not marked as deleted + //! - otherwise continue with the next bucket + void find_next(bool start_prefetching = false) + { + // invariant: current external value is always > current internal value + assert(!end_); + + internal_size_type i_bucket_old = i_bucket_; + bucket_type bucket = map_->buckets_[i_bucket_]; + + if (reader_ == NULL) + init_reader(); + + // when incremented once, more increments are likely to follow; + // therefore start prefetching + if (start_prefetching && !prefetch_) + { + reader_->enable_prefetching(); + prefetch_ = true; + } + + // determine starting-points for comparision, which are given by: + // - tmp_node: smallest internal value > old value (tmp_node may be NULL) + // - reader_: smallest external value > old value (external value may not exists) + node_type* tmp_node = (node_) ? node_ : bucket.list_; + if (source_ == hash_map_type::src_external) + { + while (tmp_node && map_->_leq(tmp_node->value_.first, key_)) + tmp_node = tmp_node->next(); + + ++i_external_; + ++(*reader_); + } + else if (source_ == hash_map_type::src_internal) + tmp_node = node_->next(); + // else (source unknown): tmp_node and reader_ already point to the + // correct values + + while (true) { + // internal and external values available + while (tmp_node && i_external_ < bucket.n_external_) + { + // internal value less or equal external value => internal wins + if (map_->_leq(tmp_node->value_.first, reader_->const_value().first)) + { + node_ = tmp_node; + if (map_->_eq(node_->value_.first, reader_->const_value().first)) + { + ++i_external_; + ++(*reader_); + } + + if (!node_->deleted()) + { + key_ = node_->value_.first; + source_ = hash_map_type::src_internal; + goto end_search; // just this once - I promise... + } + else + // continue search if internal value flaged as deleted + tmp_node = tmp_node->next(); + } + // otherwise external wins + else + { + key_ = reader_->const_value().first; + source_ = hash_map_type::src_external; + goto end_search; + } + } + // only external values left + if (i_external_ < bucket.n_external_) + { + key_ = reader_->const_value().first; + source_ = hash_map_type::src_external; + goto end_search; + } + // only internal values left + while (tmp_node) + { + node_ = tmp_node; + if (!node_->deleted()) + { + key_ = node_->value_.first; + source_ = hash_map_type::src_internal; + goto end_search; + } + else + tmp_node = tmp_node->next(); // continue search + } + + // at this point there are obviously no more values in the current + // bucket let's try the next one (outer while-loop!) + i_bucket_++; + if (i_bucket_ == map_->buckets_.size()) + { + end_ = true; + reset_reader(); + goto end_search; + } + else + { + bucket = map_->buckets_[i_bucket_]; + i_external_ = 0; + tmp_node = bucket.list_; + node_ = NULL; + reader_->skip_to(map_->bids_.begin() + bucket.i_block_, bucket.i_subblock_); + } + } + +end_search: + if (end_) + { + this->map_->iterator_map_.unregister_iterator(*this, i_bucket_old); + } + else if (i_bucket_old != i_bucket_) + { + this->map_->iterator_map_.unregister_iterator(*this, i_bucket_old); + this->map_->iterator_map_.register_iterator(*this, i_bucket_); + } + } + + virtual ~hash_map_iterator_base() + { + STXXL_VERBOSE3("hash_map_iterator_base deconst " << this); + + if (map_ && !end_) + map_->iterator_map_.unregister_iterator(*this); + reset_reader(); + } +}; + +template +class hash_map_iterator : public hash_map_iterator_base +{ +public: + typedef HashMap hash_map_type; + typedef typename hash_map_type::internal_size_type internal_size_type; + typedef typename hash_map_type::external_size_type external_size_type; + typedef typename hash_map_type::value_type value_type; + typedef typename hash_map_type::key_type key_type; + typedef typename hash_map_type::reference reference; + typedef typename hash_map_type::const_reference const_reference; + typedef typename hash_map_type::pointer pointer; + typedef typename hash_map_type::const_pointer const_pointer; + typedef typename hash_map_type::node_type node_type; + typedef typename hash_map_type::bucket_type bucket_type; + typedef typename hash_map_type::bid_iterator_type bid_iterator_type; + typedef typename hash_map_type::source_type source_type; + + typedef buffered_reader reader_type; + + typedef std::forward_iterator_tag iterator_category; + + typedef stxxl::hash_map::hash_map_iterator_base base_type; + typedef stxxl::hash_map::hash_map_const_iterator hash_map_const_iterator; + +public: + hash_map_iterator(hash_map_type* map, internal_size_type i_bucket, + node_type* node, external_size_type i_external, + source_type source, bool ext_valid, key_type key) + : base_type(map, i_bucket, node, i_external, source, ext_valid, key) + { } + + hash_map_iterator() + : base_type(NULL) + { } + + hash_map_iterator(hash_map_type* map) + : base_type(map) + { } + + hash_map_iterator(const hash_map_iterator& obj) + : base_type(obj) + { } + + hash_map_iterator& operator = (const hash_map_iterator& obj) + { + base_type::operator = (obj); + return *this; + } + + bool operator == (const hash_map_iterator& obj) const + { + return base_type::operator == (obj); + } + + bool operator == (const hash_map_const_iterator& obj) const + { + return base_type::operator == (obj); + } + + bool operator != (const hash_map_iterator& obj) const + { + return base_type::operator != (obj); + } + + bool operator != (const hash_map_const_iterator& obj) const + { + return base_type::operator != (obj); + } + + //! Return reference to current value. If source is external, mark the + //! value's block as dirty + reference operator * () + { + if (this->source_ == hash_map_type::src_internal) + { + return this->node_->value_; + } + else + { + if (this->reader_ == NULL) + base_type::init_reader(); + + return this->reader_->value(); + } + } + + //! Return reference to current value. If source is external, mark the + //! value's block as dirty + pointer operator -> () + { + return &operator * (); + } + + //! Increment iterator + hash_map_iterator& operator ++ () + { + base_type::find_next(true); + return *this; + } +}; + +template +class hash_map_const_iterator : public hash_map_iterator_base +{ +public: + typedef HashMap hash_map_type; + typedef typename hash_map_type::internal_size_type internal_size_type; + typedef typename hash_map_type::external_size_type external_size_type; + typedef typename hash_map_type::value_type value_type; + typedef typename hash_map_type::key_type key_type; + typedef typename hash_map_type::reference reference; + typedef typename hash_map_type::const_reference const_reference; + typedef typename hash_map_type::pointer pointer; + typedef typename hash_map_type::const_pointer const_pointer; + typedef typename hash_map_type::node_type node_type; + typedef typename hash_map_type::bucket_type bucket_type; + typedef typename hash_map_type::bid_iterator_type bid_iterator_type; + typedef typename hash_map_type::source_type source_type; + + typedef buffered_reader reader_type; + + typedef std::forward_iterator_tag iterator_category; + + typedef stxxl::hash_map::hash_map_iterator_base base_type; + typedef stxxl::hash_map::hash_map_iterator hash_map_iterator; + +public: + hash_map_const_iterator(hash_map_type* map, internal_size_type i_bucket, + node_type* node, external_size_type i_external, + source_type source, bool ext_valid, key_type key) + : base_type(map, i_bucket, node, i_external, source, ext_valid, key) + { } + + hash_map_const_iterator() + : base_type(NULL) + { } + + hash_map_const_iterator(hash_map_type* map) + : base_type(map) + { } + + hash_map_const_iterator(const hash_map_iterator& obj) + : base_type(obj) + { } + + hash_map_const_iterator(const hash_map_const_iterator& obj) + : base_type(obj) + { } + + hash_map_const_iterator& operator = (const hash_map_const_iterator& obj) + { + base_type::operator = (obj); + return *this; + } + + bool operator == (const hash_map_const_iterator& obj) const + { + return base_type::operator == (obj); + } + + bool operator == (const hash_map_iterator& obj) const + { + return base_type::operator == (obj); + } + + bool operator != (const hash_map_const_iterator& obj) const + { + return base_type::operator != (obj); + } + + bool operator != (const hash_map_iterator& obj) const + { + return base_type::operator != (obj); + } + + //! Return const-reference to current value + const_reference operator * () + { + if (this->source_ == hash_map_type::src_internal) + { + return this->node_->value_; + } + else + { + if (this->reader_ == NULL) + base_type::init_reader(); + + return this->reader_->const_value(); + } + } + + //! Increment iterator + hash_map_const_iterator& operator ++ () + { + base_type::find_next(true); + return *this; + } +}; + +} // namespace hash_map + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_HASH_MAP_ITERATOR_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator_map.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator_map.h new file mode 100644 index 0000000000..57555b3afe --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/iterator_map.h @@ -0,0 +1,279 @@ +/*************************************************************************** + * include/stxxl/bits/containers/hash_map/iterator_map.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Markus Westphal + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_CONTAINERS_HASH_MAP_ITERATOR_MAP_HEADER +#define STXXL_CONTAINERS_HASH_MAP_ITERATOR_MAP_HEADER + +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace hash_map { + +template +class iterator_map : private noncopyable +{ +public: + typedef HashMap hash_map_type; + typedef typename hash_map_type::node_type node_type; + typedef typename hash_map_type::source_type source_type; + typedef typename hash_map_type::key_type key_type; + + typedef typename hash_map_type::internal_size_type internal_size_type; + typedef typename hash_map_type::external_size_type external_size_type; + + typedef hash_map_iterator_base iterator_base; + +private: +#if 0 + typedef std::multimap multimap_type; +#else + struct hasher + { + size_t operator () (const internal_size_type& key) const + { + return longhash1(key); + } +#if STXXL_MSVC + bool operator () (const internal_size_type& a, const internal_size_type& b) const + { + return (a < b); + } + enum + { // parameters for hash table + bucket_size = 4, // 0 < bucket_size + min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N + }; +#endif + }; + // store iterators by bucket-index + typedef typename compat_hash_multimap< + internal_size_type, iterator_base*, hasher + >::result multimap_type; +#endif + + //! bucket-index and pointer to iterator_base + typedef typename multimap_type::value_type pair_type; + typedef typename multimap_type::iterator mmiterator_type; + typedef typename multimap_type::const_iterator const_mmiterator_type; + + hash_map_type* map_; + multimap_type it_map_; + +public: + iterator_map(hash_map_type* map) + : map_(map) + { } + + ~iterator_map() + { + it_map_.clear(); + } + + void register_iterator(iterator_base& it) + { + register_iterator(it, it.i_bucket_); + } + + void register_iterator(iterator_base& it, internal_size_type i_bucket) + { + STXXL_VERBOSE2("hash_map::iterator_map register_iterator addr=" << &it << " bucket=" << i_bucket); + it_map_.insert(pair_type(i_bucket, &it)); + } + + void unregister_iterator(iterator_base& it) + { + unregister_iterator(it, it.i_bucket_); + } + + void unregister_iterator(iterator_base& it, internal_size_type i_bucket) + { + STXXL_VERBOSE2("hash_map::iterator_map unregister_iterator addr=" << &it << " bucket=" << i_bucket); + + std::pair range + = it_map_.equal_range(i_bucket); + + assert(range.first != range.second); + + for (mmiterator_type i = range.first; i != range.second; ++i) + { + if (i->second == &it) + { + it_map_.erase(i); + return; + } + } + + throw std::runtime_error("unregister_iterator Panic in hash_map::iterator_map, can not find and unregister iterator"); + } + + //! Update iterators with given key and bucket and make them point to the + //! specified location in external memory (will be called during + //! re-hashing) + void fix_iterators_2ext(internal_size_type i_bucket_old, const key_type& key, + internal_size_type i_bucket_new, external_size_type i_ext) + { + STXXL_VERBOSE2("hash_map::iterator_map fix_iterators_2ext i_bucket=" << i_bucket_old << " new_i_ext=" << i_ext); + + std::vector its2fix; + _find(i_bucket_old, its2fix); + + for (typename std::vector::iterator + it2fix = its2fix.begin(); it2fix != its2fix.end(); ++it2fix) + { + if (!map_->_eq(key, (**it2fix).key_)) + continue; + + if (i_bucket_old != i_bucket_new) + { + unregister_iterator(**it2fix); + register_iterator(**it2fix, i_bucket_new); + } + + (**it2fix).i_bucket_ = i_bucket_new; + (**it2fix).node_ = NULL; + (**it2fix).i_external_ = i_ext; + (**it2fix).source_ = hash_map_type::src_external; + // external position is now known (i_ext) and therefore valid + (**it2fix).ext_valid_ = true; + (**it2fix).reset_reader(); + (**it2fix).reader_ = NULL; + } + } + + //! Update iterators with given key and bucket and make them point to the + //! specified node in internal memory (will be called by insert_oblivious) + void fix_iterators_2int(internal_size_type i_bucket, const key_type& key, node_type* node) + { + STXXL_VERBOSE2("hash_map::iterator_map fix_iterators_2int i_bucket=" << i_bucket << " node=" << node); + + std::vector its2fix; + _find(i_bucket, its2fix); + + for (typename std::vector::iterator + it2fix = its2fix.begin(); it2fix != its2fix.end(); ++it2fix) + { + if (!map_->_eq((**it2fix).key_, key)) + continue; + + assert((**it2fix).source_ == hash_map_type::src_external); + + (**it2fix).source_ = hash_map_type::src_internal; + (**it2fix).node_ = node; + (**it2fix).i_external_++; + if ((**it2fix).reader_) + (**it2fix).reader_->operator ++ (); + } + } + + //! Update iterators with given key and bucket and make them point to the + //! end of the hash-map (called by erase and erase_oblivious) + void fix_iterators_2end(internal_size_type i_bucket, const key_type& key) + { + STXXL_VERBOSE2("hash_map::iterator_map fix_iterators_2end i_bucket=" << i_bucket); + + std::vector its2fix; + _find(i_bucket, its2fix); + + for (typename std::vector::iterator + it2fix = its2fix.begin(); it2fix != its2fix.end(); ++it2fix) + { + if (!map_->_eq(key, (**it2fix).key_)) + continue; + + (**it2fix).end_ = true; + (**it2fix).reset_reader(); + unregister_iterator(**it2fix); + } + } + + //! Update all iterators and make them point to the end of the hash-map + //! (used by clear()) + void fix_iterators_all2end() + { + for (mmiterator_type it2fix = it_map_.begin(); + it2fix != it_map_.end(); ++it2fix) + { + (*it2fix).second->end_ = true; + (*it2fix).second->reset_reader(); + } + it_map_.clear(); + } + +private: + //! Find all iterators registered with given bucket and add them to outc + template + void _find(internal_size_type i_bucket, OutputContainer& outc) + { + std::pair range + = it_map_.equal_range(i_bucket); + + for (mmiterator_type i = range.first; i != range.second; ++i) + outc.push_back((*i).second); + } + + // changes hash_map pointer in all contained iterators + void change_hash_map_pointers(hash_map_type* map) + { + for (mmiterator_type it = it_map_.begin(); it != it_map_.end(); ++it) + ((*it).second)->map_ = map; + } + +public: + void swap(iterator_map& obj) + { + std::swap(it_map_, obj.it_map_); + std::swap(map_, obj.map_); + + change_hash_map_pointers(map_); + obj.change_hash_map_pointers(obj.map_); + } + + void print_statistics(std::ostream& o = std::cout) const + { + o << "Registered iterators: " << it_map_.size() << "\n"; + + for (const_mmiterator_type i = it_map_.begin(); i != it_map_.end(); ++i) + { + o << " Address=" << i->second + << ", Bucket=" << i->second->i_bucket_ + << ", Node=" << i->second->node_ + << ", i_ext=" << i->second->i_external_ + << ", " + << ((i->second->source_ == hash_map_type::src_external) + ? "external" : "internal") << std::endl; + } + } +}; + +} // namespace hash_map + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::hash_map::iterator_map& a, + stxxl::hash_map::iterator_map& b) +{ + if (&a != &b) + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_HASH_MAP_ITERATOR_MAP_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/tuning.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/tuning.h new file mode 100644 index 0000000000..525eb26b3f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/tuning.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * include/stxxl/bits/containers/hash_map/tuning.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Markus Westphal + * + * 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 STXXL_CONTAINERS_HASH_MAP_TUNING_HEADER +#define STXXL_CONTAINERS_HASH_MAP_TUNING_HEADER +#define _STXXL_TUNING_H_ + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace hash_map { + +//! Tuning parameters for external memory hash map. +class tuning : public singleton +{ + friend class singleton; + +public: + //! see buffered_reader + size_t prefetch_page_size; + //! see buffered_reader + size_t prefetch_pages; + //! see block_cache and hash_map + size_t blockcache_size; + +private: + /*! set reasonable default values for tuning params */ + tuning() + : prefetch_page_size(config::get_instance()->disks_number() * 2), + prefetch_pages(2), + blockcache_size(config::get_instance()->disks_number() * 12) + { } +}; + +} // namespace hash_map + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_HASH_MAP_TUNING_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/util.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/util.h new file mode 100644 index 0000000000..ff31eb6322 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/hash_map/util.h @@ -0,0 +1,577 @@ +/*************************************************************************** + * include/stxxl/bits/containers/hash_map/util.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Markus Westphal + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_CONTAINERS_HASH_MAP_UTIL_HEADER +#define STXXL_CONTAINERS_HASH_MAP_UTIL_HEADER +#define STXXL_CONTAINERS_HASHMAP__UTIL_H + +#include +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace hash_map { + +// For internal memory chaining: struct to compose next-pointer and delete-flag +// share the same memory: the lowest bit is occupied by the del-flag. +template +struct node +{ + node* next_and_del_; + ValueType value_; + + //! check if the next node is deleted. + bool deleted() + { + return ((int_type)next_and_del_ & 0x01) == 1; + } + //! change deleted flag on the next node + bool set_deleted(bool d) + { + next_and_del_ = (node*)(((int_type)next_and_del_ & ~0x01) | (int_type)d); + return d; + } + + //! return the next node, without the "next" flag. + node * next() + { + return (node*)((int_type)next_and_del_ & ~0x01); + } + //! change the "next" value of next node pointer + node * set_next(node* n) + { + next_and_del_ = (node*)(((int_type)next_and_del_ & 0x01) | (int_type)n); + return n; + } +}; + +template +struct bucket +{ + //! entry point to the chain in internal memory + NodeType* list_; + + //! number of elements in external memory + external_size_type n_external_; + + //! index of first block's bid (to be used as index for hash_map's + //! bids_-array + internal_size_type i_block_; + + //! index of first subblock + internal_size_type i_subblock_; + + bucket() + : list_(NULL), + n_external_(0), + i_block_(0), + i_subblock_(0) + { } + + bucket(NodeType* list, external_size_type n_external, + internal_size_type i_block, internal_size_type i_subblock) + : list_(list), + n_external_(n_external), + i_block_(i_block), + i_subblock_(i_subblock) + { } +}; + +//! Used to scan external memory with prefetching. +template +class buffered_reader : private noncopyable +{ +public: + typedef CacheType cache_type; + typedef BidIterator bid_iterator; + + typedef typename cache_type::block_type block_type; + typedef typename block_type::value_type subblock_type; + typedef typename subblock_type::value_type value_type; + + typedef typename bid_iterator::value_type bid_type; + + enum { block_size = block_type::size, subblock_size = subblock_type::size }; + +private: + //! index within current block + unsigned_type i_value_; + //! points to the beginning of the block-sequence + bid_iterator begin_bid_; + //! points to the current block + bid_iterator curr_bid_; + //! points to the end of the block-sequence + bid_iterator end_bid_; + //! points to the next block to prefetch + bid_iterator pref_bid_; + + //! shared block-cache + cache_type& cache_; + + //! true if prefetching enabled + bool prefetch_; + //! pages, which are read at once from disk, consist of this many blocks + unsigned_type page_size_; + //! number of pages to prefetch + unsigned_type prefetch_pages_; + + //! current block dirty ? + bool dirty_; + //! current subblock + subblock_type* subblock_; + +public: + //! Create a new buffered reader to read the blocks in [seq_begin, seq_end) + //! \param seq_begin First block's bid + //! \param seq_end Last block's bid + //! \param cache Block-cache used for prefetching + //! \param i_subblock Start reading from this subblock + //! \param prefetch Enable/Disable prefetching + buffered_reader(bid_iterator seq_begin, bid_iterator seq_end, + cache_type& cache, + internal_size_type i_subblock = 0, bool prefetch = true) + : i_value_(0), + begin_bid_(seq_begin), + curr_bid_(seq_begin), + end_bid_(seq_end), + cache_(cache), + prefetch_(false), + page_size_(tuning::get_instance()->prefetch_page_size), + prefetch_pages_(tuning::get_instance()->prefetch_pages), + dirty_(false), + subblock_(NULL) + { + if (seq_begin == seq_end) + return; + + if (prefetch) + enable_prefetching(); + + // will (amongst other things) set subblock_ and retain current block + skip_to(seq_begin, i_subblock); + } + + ~buffered_reader() + { + if (curr_bid_ != end_bid_) + cache_.release_block(*curr_bid_); + } + + void enable_prefetching() + { + if (prefetch_) + return; + + prefetch_ = true; + pref_bid_ = curr_bid_; + // start prefetching page_size*prefetch_pages blocks beginning with current one + for (unsigned_type i = 0; i < page_size_ * prefetch_pages_; i++) + { + if (pref_bid_ == end_bid_) + break; + + cache_.prefetch_block(*pref_bid_); + ++pref_bid_; + } + } + + //! Get const-reference to current value. + const value_type & const_value() + { + return (*subblock_)[i_value_ % subblock_size]; + } + + //! Get reference to current value. The current value's block's dirty flag + //! will be set. + value_type & value() + { + if (!dirty_) { + cache_.make_dirty(*curr_bid_); + dirty_ = true; + } + + return (*subblock_)[i_value_ % subblock_size]; + } + + //! Advance to the next value + //! \return false if last value has been reached, otherwise true. + bool operator ++ () + { + if (curr_bid_ == end_bid_) + return false; + + // same block + if (i_value_ + 1 < block_size * subblock_size) + { + i_value_++; + } + // entered new block + else + { + cache_.release_block(*curr_bid_); + + i_value_ = 0; + dirty_ = false; + ++curr_bid_; + + if (curr_bid_ == end_bid_) + return false; + + cache_.retain_block(*curr_bid_); + + // if a complete page has been consumed, prefetch the next one + if (prefetch_ && (curr_bid_ - begin_bid_) % page_size_ == 0) + { + for (unsigned i = 0; i < page_size_; i++) + { + if (pref_bid_ == end_bid_) + break; + cache_.prefetch_block(*pref_bid_); + ++pref_bid_; + } + } + } + + // entered new subblock + if (i_value_ % subblock_size == 0) + { + subblock_ = cache_.get_subblock(*curr_bid_, i_value_ / subblock_size); + } + + return true; + } + + //! Skip remaining values of the current subblock. + void next_subblock() + { + i_value_ = (i_value_ / subblock_size + 1) * subblock_size - 1; + operator ++ (); // takes care of prefetching etc + } + + //! Continue reading at given block and subblock. + void skip_to(bid_iterator bid, internal_size_type i_subblock) + { + if (curr_bid_ == end_bid_) + return; + + if (bid != curr_bid_) + dirty_ = false; + + cache_.release_block(*curr_bid_); + + if (bid == end_bid_) + return; + + // skip to block + while (curr_bid_ != bid) { + ++curr_bid_; + + if (prefetch_ && (curr_bid_ - begin_bid_) % page_size_ == 0) + { + for (unsigned i = 0; i < page_size_; i++) + { + if (pref_bid_ == end_bid_) + break; + cache_.prefetch_block(*pref_bid_); + ++pref_bid_; + } + } + } + // skip to subblock + i_value_ = i_subblock * subblock_size; + subblock_ = cache_.get_subblock(*curr_bid_, i_subblock); + cache_.retain_block(*curr_bid_); + } +}; + +//! Buffered writing of values. New Blocks are allocated as needed. +template +class buffered_writer : private noncopyable +{ +public: + typedef BlockType block_type; + typedef BidContainer bid_container_type; + + typedef typename block_type::value_type subblock_type; + typedef typename subblock_type::value_type value_type; + + typedef stxxl::buffered_writer writer_type; + + enum { + block_size = block_type::size, + subblock_size = subblock_type::size + }; + +private: + //! buffered writer + writer_type writer_; + //! current buffer-block + block_type* block_; + + //! sequence of allocated blocks (to be expanded as needed) + bid_container_type* bids_; + + //! current block's index + unsigned_type i_block_; + //! current value's index in the range of [0..\#values per block[ + unsigned_type i_value_; + //! number of blocks to allocate in a row + unsigned_type increase_; + +public: + //! Create a new buffered writer. + //! \param c write values to these blocks (c holds the bids) + //! \param buffer_size Number of write-buffers to use + //! \param batch_size bulk buffered writing + buffered_writer(bid_container_type* c, + int_type buffer_size, int_type batch_size) + : writer_(buffer_size, batch_size), + bids_(c), + i_block_(0), + i_value_(0), + increase_(config::get_instance()->disks_number() * 3) + { + block_ = writer_.get_free_block(); + } + + ~buffered_writer() + { + flush(); + } + + //! Write all values from given stream. + template + void append_from_stream(StreamType& stream) + { + while (!stream.empty()) + { + append(*stream); + ++stream; + } + } + + //! Write given value. + void append(const value_type& value) + { + internal_size_type i_subblock = (i_value_ / subblock_size); + (*block_)[i_subblock][i_value_ % subblock_size] = value; + + if (i_value_ + 1 < block_size * subblock_size) + i_value_++; + // reached end of a block + else + { + i_value_ = 0; + + // allocate new blocks if neccessary ... + if (i_block_ == bids_->size()) + { + bids_->resize(bids_->size() + increase_); + block_manager* bm = stxxl::block_manager::get_instance(); + bm->new_blocks(striping(), bids_->end() - increase_, bids_->end()); + } + // ... and write current block + block_ = writer_.write(block_, (*bids_)[i_block_]); + + i_block_++; + } + } + + //! Continue writing at the beginning of the next subblock. TODO more + //! efficient + void finish_subblock() + { + i_value_ = (i_value_ / subblock_size + 1) * subblock_size - 1; + append(value_type()); // writing and allocating blocks etc + } + + //! Flushes not yet written blocks. + void flush() + { + i_value_ = 0; + if (i_block_ == bids_->size()) + { + bids_->resize(bids_->size() + increase_); + block_manager* bm = stxxl::block_manager::get_instance(); + bm->new_blocks(striping(), bids_->end() - increase_, bids_->end()); + } + block_ = writer_.write(block_, (*bids_)[i_block_]); + i_block_++; + + writer_.flush(); + } + + //! Index of current block. + internal_size_type i_block() { return i_block_; } + + //! Index of current subblock. + internal_size_type i_subblock() { return i_value_ / subblock_size; } +}; + +/*! + * Additional information about a stored value: + * - the bucket in which it can be found + * - where it is currently stored (intern or extern) + * - the buffer-node + * - the position in external memory + */ +template +struct HashedValue +{ + typedef HashMap hash_map_type; + typedef typename hash_map_type::value_type value_type; + typedef typename hash_map_type::source_type source_type; + typedef typename hash_map_type::node_type node_type; + + typedef typename hash_map_type::internal_size_type internal_size_type; + typedef typename hash_map_type::external_size_type external_size_type; + + value_type value_; + internal_size_type i_bucket_; + source_type source_; + node_type* node_; + external_size_type i_external_; + + HashedValue() + : i_bucket_(internal_size_type(-1)) + { } + + HashedValue(const value_type& value, internal_size_type i_bucket, + source_type src, node_type* node, external_size_type i_external) + : value_(value), + i_bucket_(i_bucket), + source_(src), + node_(node), + i_external_(i_external) + { } +}; + +/*! + * Stream interface for all value-pairs currently stored in the map. Returned + * values are HashedValue-objects (actual value enriched with information on + * where the value can be found (bucket-number, internal, external)). Values, + * marked as deleted in internal-memory, are not returned; for modified values + * only the one in internal memory is returned. +*/ +template +struct HashedValuesStream +{ + typedef HashMap hash_map_type; + typedef HashedValue value_type; + + typedef typename hash_map_type::node_type node_type; + typedef typename hash_map_type::bid_container_type::iterator bid_iterator; + typedef typename hash_map_type::buckets_container_type::iterator bucket_iterator; + + typedef typename hash_map_type::internal_size_type internal_size_type; + typedef typename hash_map_type::external_size_type external_size_type; + + hash_map_type& map_; + Reader& reader_; + bucket_iterator curr_bucket_; + bucket_iterator end_bucket_; + bid_iterator begin_bid_; + internal_size_type i_bucket_; + node_type* node_; + external_size_type i_external_; + value_type value_; + + HashedValuesStream(bucket_iterator begin_bucket, bucket_iterator end_bucket, + Reader& reader, bid_iterator begin_bid, + hash_map_type& map) + : map_(map), + reader_(reader), + curr_bucket_(begin_bucket), + end_bucket_(end_bucket), + begin_bid_(begin_bid), + i_bucket_(0), + node_(curr_bucket_ != end_bucket_ ? curr_bucket_->list_ : NULL), + i_external_(0) + { + if (!empty()) + value_ = find_next(); + } + + const value_type& operator * () { return value_; } + + bool empty() const { return curr_bucket_ == end_bucket_; } + + void operator ++ () + { + if (value_.source_ == hash_map_type::src_internal) + node_ = node_->next(); + else + { + ++reader_; + ++i_external_; + } + value_ = find_next(); + } + + value_type find_next() + { + while (true) + { + // internal and external elements available + while (node_ && i_external_ < curr_bucket_->n_external_) + { + if (map_._leq(node_->value_.first, reader_.const_value().first)) + { + if (map_._eq(node_->value_.first, reader_.const_value().first)) + { + ++reader_; + ++i_external_; + } + + if (!node_->deleted()) + return value_type(node_->value_, i_bucket_, hash_map_type::src_internal, node_, i_external_); + else + node_ = node_->next(); + } + else + return value_type(reader_.const_value(), i_bucket_, hash_map_type::src_external, node_, i_external_); + } + // only internal elements left + while (node_) + { + if (!node_->deleted()) + return value_type(node_->value_, i_bucket_, hash_map_type::src_internal, node_, i_external_); + else + node_ = node_->next(); + } + // only external elements left + while (i_external_ < curr_bucket_->n_external_) + return value_type(reader_.const_value(), i_bucket_, hash_map_type::src_external, node_, i_external_); + + // if we made it to this point there are obviously no more values in the current bucket + // let's try the next one (outer while-loop!) + ++curr_bucket_; + ++i_bucket_; + if (curr_bucket_ == end_bucket_) + return value_type(); + + node_ = curr_bucket_->list_; + i_external_ = 0; + reader_.skip_to(begin_bid_ + curr_bucket_->i_block_, curr_bucket_->i_subblock_); + } + } +}; + +} // namespace hash_map + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_HASH_MAP_UTIL_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/map.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/map.h new file mode 100644 index 0000000000..28311403f5 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/map.h @@ -0,0 +1,510 @@ +/*************************************************************************** + * include/stxxl/bits/containers/map.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * Copyright (C) 2008, 2009 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_MAP_HEADER +#define STXXL_CONTAINERS_MAP_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace btree { + +template +class btree; + +} // namespace btree + +//! \addtogroup stlcont +//! \{ + +//! External associative container (map). \n +//! Introduction to map container: see \ref tutorial_map tutorial. \n +//! Design and Internals of map container: see \ref design_map +//! +//! \tparam KeyType key type (POD with no references to internal memory) +//! \tparam DataType data type (POD with no references to internal memory) +//! \tparam CompareType comparison type used to determine +//! whether a key is smaller than another one. +//! If CompareType()(x,y) is true, then x is smaller than y. +//! CompareType must also provide a static \c max_value method, that returns +//! a value of type KeyType that is +//! larger than any key stored in map : i.e. for all \b x in map holds +//! CompareType()(x,CompareType::max_value()) +//! +//!
+//! Example: : +//! \verbatim +//! struct CmpIntGreater +//! { +//! bool operator () (const int & a, const int & b) const { return a>b; } +//! static int max_value() { return std::numeric_limits::min(); } +//! }; +//! \endverbatim +//! Another example: +//! \verbatim +//! struct CmpIntLess +//! { +//! bool operator () (const int & a, const int & b) const { return a::max(); } +//! }; +//! \endverbatim +//! Note that CompareType must define a strict weak ordering. +//! (
see what it is) +//! \tparam RawNodeSize size of internal nodes of map in bytes (btree implementation). +//! \tparam RawLeafSize size of leaves of map in bytes (btree implementation). +//! \tparam PDAllocStrategy parallel disk allocation strategy (\c stxxl::SR is recommended and default) +//! +template +class map : private noncopyable +{ + typedef btree::btree impl_type; + + impl_type impl; + +public: + typedef typename impl_type::node_block_type node_block_type; + typedef typename impl_type::leaf_block_type leaf_block_type; + + typedef typename impl_type::key_type key_type; + typedef typename impl_type::data_type data_type; + typedef typename impl_type::data_type mapped_type; + typedef typename impl_type::value_type value_type; + typedef typename impl_type::key_compare key_compare; + typedef typename impl_type::value_compare value_compare; + typedef typename impl_type::pointer pointer; + typedef typename impl_type::const_pointer const_pointer; + typedef typename impl_type::reference reference; + typedef typename impl_type::const_reference const_reference; + typedef typename impl_type::size_type size_type; + typedef typename impl_type::difference_type difference_type; + typedef typename impl_type::iterator iterator; + typedef typename impl_type::const_iterator const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + //! \name Iterators + //! \{ + + iterator begin() { return impl.begin(); } + iterator end() { return impl.end(); } + const_iterator begin() const { return impl.begin(); } + const_iterator end() const { return impl.end(); } + const_iterator cbegin() const { return begin(); } + const_iterator cend() const { return end(); } + + reverse_iterator rbegin() + { + return reverse_iterator(end()); + } + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(end()); + } + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(end()); + } + reverse_iterator rend() + { + return reverse_iterator(begin()); + } + const_reverse_iterator rend() const + { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crend() const + { + return const_reverse_iterator(begin()); + } + + //! \} + + //! \name Capacity + //! \{ + + size_type size() const { return impl.size(); } + size_type max_size() const { return impl.max_size(); } + bool empty() const { return impl.empty(); } + + //! \} + + //! \name Observers + //! \{ + + key_compare key_comp() const { return impl.key_comp(); } + value_compare value_comp() const { return impl.value_comp(); } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + //! A constructor + //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) + //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) + map(unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes + ) : impl(node_cache_size_in_bytes, leaf_cache_size_in_bytes) + { } + + //! A constructor + //! \param c_ comparator object + //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) + //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) + map(const key_compare& c_, + unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes + ) : impl(c_, node_cache_size_in_bytes, leaf_cache_size_in_bytes) + { } + + //! Constructs a map from a given input range + //! \param b beginning of the range + //! \param e end of the range + //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) + //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) + //! \param range_sorted if \c true than the constructor assumes that the range is sorted + //! and performs a fast bottom-up bulk construction of the map (btree implementation) + //! \param node_fill_factor node fill factor in [0,1] for bulk construction + //! \param leaf_fill_factor leaf fill factor in [0,1] for bulk construction + template + map(InputIterator b, + InputIterator e, + unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes, + bool range_sorted = false, + double node_fill_factor = 0.75, + double leaf_fill_factor = 0.6 + ) : impl(b, e, node_cache_size_in_bytes, leaf_cache_size_in_bytes, + range_sorted, node_fill_factor, leaf_fill_factor) + { } + + //! Constructs a map from a given input range + //! \param b beginning of the range + //! \param e end of the range + //! \param c_ comparator object + //! \param node_cache_size_in_bytes size of node cache in bytes (btree implementation) + //! \param leaf_cache_size_in_bytes size of leaf cache in bytes (btree implementation) + //! \param range_sorted if \c true than the constructor assumes that the range is sorted + //! and performs a fast bottom-up bulk construction of the map (btree implementation) + //! \param node_fill_factor node fill factor in [0,1] for bulk construction + //! \param leaf_fill_factor leaf fill factor in [0,1] for bulk construction + template + map(InputIterator b, + InputIterator e, + const key_compare& c_, + unsigned_type node_cache_size_in_bytes, + unsigned_type leaf_cache_size_in_bytes, + bool range_sorted = false, + double node_fill_factor = 0.75, + double leaf_fill_factor = 0.6 + ) : impl(b, e, c_, node_cache_size_in_bytes, leaf_cache_size_in_bytes, + range_sorted, node_fill_factor, leaf_fill_factor) + { } + + //! \} + + //! \name Modifiers + //! \{ + + void swap(map& obj) { std::swap(impl, obj.impl); } + std::pair insert(const value_type& x) + { + return impl.insert(x); + } + iterator insert(iterator pos, const value_type& x) + { + return impl.insert(pos, x); + } + template + void insert(InputIterator b, InputIterator e) + { + impl.insert(b, e); + } + void erase(iterator pos) + { + impl.erase(pos); + } + size_type erase(const key_type& k) + { + return impl.erase(k); + } + void erase(iterator first, iterator last) + { + impl.erase(first, last); + } + void clear() + { + impl.clear(); + } + + //! \} + + //! \name Operations + //! \{ + + iterator find(const key_type& k) + { + return impl.find(k); + } + const_iterator find(const key_type& k) const + { + return impl.find(k); + } + size_type count(const key_type& k) + { + return impl.count(k); + } + iterator lower_bound(const key_type& k) + { + return impl.lower_bound(k); + } + const_iterator lower_bound(const key_type& k) const + { + return impl.lower_bound(k); + } + iterator upper_bound(const key_type& k) + { + return impl.upper_bound(k); + } + const_iterator upper_bound(const key_type& k) const + { + return impl.upper_bound(k); + } + std::pair equal_range(const key_type& k) + { + return impl.equal_range(k); + } + std::pair equal_range(const key_type& k) const + { + return impl.equal_range(k); + } + + //! \} + + //! \name Operators + //! \{ + + data_type& operator [] (const key_type& k) + { + return impl[k]; + } + + //! \} + + //! \name Miscellaneous + //! \{ + + //! Enables leaf prefetching during scanning + void enable_prefetching() + { + impl.enable_prefetching(); + } + + //! Disables leaf prefetching during scanning + void disable_prefetching() + { + impl.disable_prefetching(); + } + + //! Returns the status of leaf prefetching during scanning + bool prefetching_enabled() + { + return impl.prefetching_enabled(); + } + + //! Prints cache statistics + void print_statistics(std::ostream& o) const + { + impl.print_statistics(o); + } + + //! Resets cache statistics + void reset_statistics() + { + impl.reset_statistics(); + } + + //! \} + + ////////////////////////////////////////////////// + template + friend bool operator == (const map& a, + const map& b); + ////////////////////////////////////////////////// + template + friend bool operator < (const map& a, + const map& b); + ////////////////////////////////////////////////// + template + friend bool operator > (const map& a, + const map& b); + ////////////////////////////////////////////////// + template + friend bool operator != (const map& a, + const map& b); + ////////////////////////////////////////////////// + template + friend bool operator <= (const map& a, + const map& b); + ////////////////////////////////////////////////// + template + friend bool operator >= (const map& a, + const map& b); + ////////////////////////////////////////////////// +}; + +template +inline bool operator == (const map& a, + const map& b) +{ + return a.impl == b.impl; +} + +template +inline bool operator < (const map& a, + const map& b) +{ + return a.impl < b.impl; +} + +template +inline bool operator > (const map& a, + const map& b) +{ + return a.impl > b.impl; +} + +template +inline bool operator != (const map& a, + const map& b) +{ + return a.impl != b.impl; +} + +template +inline bool operator <= (const map& a, + const map& b) +{ + return a.impl <= b.impl; +} + +template +inline bool operator >= (const map& a, + const map& b) +{ + return a.impl >= b.impl; +} + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::map& a, + stxxl::map& b + ) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_MAP_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix.h new file mode 100644 index 0000000000..ae6ae1b7ac --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix.h @@ -0,0 +1,1423 @@ +/*************************************************************************** + * include/stxxl/bits/containers/matrix.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010-2011 Raoul Steffen + * + * 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 STXXL_CONTAINERS_MATRIX_HEADER +#define STXXL_CONTAINERS_MATRIX_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \defgroup matrix matrix +//! Efficient external memory matrix operations +//! \ingroup stlcont +//! \{ + +/* index-variable naming convention: + * [MODIFIER_][UNIT_]DIMENSION[_in_[MODIFIER_]ENVIRONMENT] + * + * e.g.: + * block_row = number of row measured in rows consisting of blocks + * element_row_in_block = number of row measured in rows consisting of elements in the (row of) block(s) + * + * size-variable naming convention: + * [MODIFIER_][ENVIRONMENT_]DIMENSION[_in_UNITs] + * + * e.g. + * height_in_blocks + */ + +// forward declaration +template +class matrix; + +//! external column-vector container for matrix multiplication +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +template +class column_vector : public vector +{ +public: + typedef vector vector_type; + typedef typename vector_type::size_type size_type; + + using vector_type::size; + + //! \param n number of elements + column_vector(size_type n = 0) + : vector_type(n) { } + + column_vector operator + (const column_vector& right) const + { + assert(size() == right.size()); + column_vector res(size()); + for (size_type i = 0; i < size(); ++i) + res[i] = (*this)[i] + right[i]; + return res; + } + + column_vector operator - (const column_vector& right) const + { + assert(size() == right.size()); + column_vector res(size()); + for (size_type i = 0; i < size(); ++i) + res[i] = (*this)[i] - right[i]; + return res; + } + + column_vector operator * (const ValueType scalar) const + { + column_vector res(size()); + for (size_type i = 0; i < size(); ++i) + res[i] = (*this)[i] * scalar; + return res; + } + + column_vector& operator += (const column_vector& right) + { + assert(size() == right.size()); + for (size_type i = 0; i < size(); ++i) + (*this)[i] += right[i]; + return *this; + } + + column_vector& operator -= (const column_vector& right) + { + assert(size() == right.size()); + for (size_type i = 0; i < size(); ++i) + (*this)[i] -= right[i]; + return *this; + } + + column_vector& operator *= (const ValueType scalar) + { + for (size_type i = 0; i < size(); ++i) + (*this)[i] *= scalar; + return *this; + } + + void set_zero() + { + for (typename vector_type::iterator it = vector_type::begin(); it != vector_type::end(); ++it) + *it = 0; + } +}; + +//! external row-vector container for matrix multiplication +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +template +class row_vector : public vector +{ +public: + typedef vector vector_type; + typedef typename vector_type::size_type size_type; + + using vector_type::size; + + //! \param n number of elements + row_vector(size_type n = 0) + : vector_type(n) { } + + row_vector operator + (const row_vector& right) const + { + assert(size() == right.size()); + row_vector res(size()); + for (size_type i = 0; i < size(); ++i) + res[i] = (*this)[i] + right[i]; + return res; + } + + row_vector operator - (const row_vector& right) const + { + assert(size() == right.size()); + row_vector res(size()); + for (size_type i = 0; i < size(); ++i) + res[i] = (*this)[i] - right[i]; + return res; + } + + row_vector operator * (const ValueType scalar) const + { + row_vector res(size()); + for (size_type i = 0; i < size(); ++i) + res[i] = (*this)[i] * scalar; + return res; + } + + template + row_vector operator * (const matrix& right) const + { return right.multiply_from_left(*this); } + + ValueType operator * (const column_vector& right) const + { + ValueType res = 0; + for (size_type i = 0; i < size(); ++i) + res += (*this)[i] * right[i]; + return res; + } + + row_vector& operator += (const row_vector& right) + { + assert(size() == right.size()); + for (size_type i = 0; i < size(); ++i) + (*this)[i] += right[i]; + return *this; + } + + row_vector& operator -= (const row_vector& right) + { + assert(size() == right.size()); + for (size_type i = 0; i < size(); ++i) + (*this)[i] -= right[i]; + return *this; + } + + row_vector& operator *= (const ValueType scalar) + { + for (size_type i = 0; i < size(); ++i) + (*this)[i] *= scalar; + return *this; + } + + void set_zero() + { + for (typename vector_type::iterator it = vector_type::begin(); it != vector_type::end(); ++it) + *it = 0; + } +}; + +//! Specialized swappable_block that interprets uninitialized as containing zeros. +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +//! +//! When initializing, all values are set to zero. +template +class matrix_swappable_block : public swappable_block +{ +public: + typedef typename swappable_block::internal_block_type internal_block_type; + + using swappable_block::get_internal_block; + + void fill_default() + { + // get_internal_block checks acquired + internal_block_type& data = get_internal_block(); + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type row = 0; row < int_type(BlockSideLength); ++row) + for (int_type col = 0; col < int_type(BlockSideLength); ++col) + data[row * BlockSideLength + col] = 0; + } +}; + +//! External container for a (sub)matrix. Not intended for direct use. +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +//! +//! Stores blocks only, so all measures (height, width, row, col) are in blocks. +template +class swappable_block_matrix : public atomic_counted_object +{ +public: + typedef int_type size_type; + typedef int_type elem_size_type; + typedef block_scheduler > block_scheduler_type; + typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; + typedef std::vector blocks_type; + typedef matrix_local::matrix_operations Ops; + + block_scheduler_type& bs; + +private: + // assigning is not allowed + swappable_block_matrix& operator = (const swappable_block_matrix& other); + +protected: + //! height of the matrix in blocks + size_type height, + //! width of the matrix in blocks + width, + //! height copied from supermatrix in blocks + height_from_supermatrix, + //! width copied from supermatrix in blocks + width_from_supermatrix; + //! the matrice's blocks in row-major + blocks_type blocks; + //! if the elements in each block are in col-major instead of row-major + bool elements_in_blocks_transposed; + + //! get identifier of the block at (row, col) + swappable_block_identifier_type & bl(const size_type row, const size_type col) + { return blocks[row * width + col]; } + +public: + //! Create an empty swappable_block_matrix of given dimensions. + swappable_block_matrix(block_scheduler_type& bs, const size_type height_in_blocks, const size_type width_in_blocks, const bool transposed = false) + : bs(bs), + height(height_in_blocks), + width(width_in_blocks), + height_from_supermatrix(0), + width_from_supermatrix(0), + blocks(height * width), + elements_in_blocks_transposed(transposed) + { + for (size_type row = 0; row < height; ++row) + for (size_type col = 0; col < width; ++col) + bl(row, col) = bs.allocate_swappable_block(); + } + + //! Create swappable_block_matrix of given dimensions that + //! represents the submatrix of supermatrix starting at (from_row_in_blocks, from_col_in_blocks). + //! + //! If supermatrix is not large enough, the submatrix is padded with empty blocks. + //! The supermatrix must not be destructed or transposed before the submatrix is destructed. + swappable_block_matrix(const swappable_block_matrix& supermatrix, + const size_type height_in_blocks, const size_type width_in_blocks, + const size_type from_row_in_blocks, const size_type from_col_in_blocks) + : bs(supermatrix.bs), + height(height_in_blocks), + width(width_in_blocks), + height_from_supermatrix(std::min(supermatrix.height - from_row_in_blocks, height)), + width_from_supermatrix(std::min(supermatrix.width - from_col_in_blocks, width)), + blocks(height * width), + elements_in_blocks_transposed(supermatrix.elements_in_blocks_transposed) + { + for (size_type row = 0; row < height_from_supermatrix; ++row) + { + for (size_type col = 0; col < width_from_supermatrix; ++col) + bl(row, col) = supermatrix.block(row + from_row_in_blocks, col + from_col_in_blocks); + for (size_type col = width_from_supermatrix; col < width; ++col) + bl(row, col) = bs.allocate_swappable_block(); + } + for (size_type row = height_from_supermatrix; row < height; ++row) + for (size_type col = 0; col < width; ++col) + bl(row, col) = bs.allocate_swappable_block(); + } + + //! Create swappable_block_matrix that represents the combination matrix ul ur dl dr. + //! + //! The submatrices are assumed to be of fitting dimensions and equal transposition. + //! The submatrices must not be destructed or transposed before the matrix is destructed. + swappable_block_matrix(const swappable_block_matrix& ul, const swappable_block_matrix& ur, + const swappable_block_matrix& dl, const swappable_block_matrix& dr) + : bs(ul.bs), + height(ul.height + dl.height), + width(ul.width + ur.width), + height_from_supermatrix(height), + width_from_supermatrix(width), + blocks(height * width), + elements_in_blocks_transposed(ul.elements_in_blocks_transposed) + { + for (size_type row = 0; row < ul.height; ++row) + { + for (size_type col = 0; col < ul.width; ++col) + bl(row, col) = ul.block(row, col); + for (size_type col = ul.width; col < width; ++col) + bl(row, col) = ur.block(row, col - ul.width); + } + for (size_type row = ul.height; row < height; ++row) + { + for (size_type col = 0; col < ul.width; ++col) + bl(row, col) = dl.block(row - ul.height, col); + for (size_type col = ul.width; col < width; ++col) + bl(row, col) = dr.block(row - ul.height, col - ul.width); + } + } + + swappable_block_matrix(const swappable_block_matrix& other) + : atomic_counted_object(other), + bs(other.bs), + height(other.height), + width(other.width), + height_from_supermatrix(0), + width_from_supermatrix(0), + blocks(height * width), + elements_in_blocks_transposed(false) + { + for (size_type row = 0; row < height; ++row) + for (size_type col = 0; col < width; ++col) + bl(row, col) = bs.allocate_swappable_block(); + // 0 + other is copying + Ops::element_op(*this, other, typename Ops::addition()); + } + + ~swappable_block_matrix() + { + for (size_type row = 0; row < height_from_supermatrix; ++row) + { + for (size_type col = width_from_supermatrix; col < width; ++col) + bs.free_swappable_block(bl(row, col)); + } + for (size_type row = height_from_supermatrix; row < height; ++row) + for (size_type col = 0; col < width; ++col) + bs.free_swappable_block(bl(row, col)); + } + + static size_type block_index_from_elem(elem_size_type index) + { return index / BlockSideLength; } + + static int_type elem_index_in_block_from_elem(elem_size_type index) + { return index % BlockSideLength; } + + // regards transposed + int_type elem_index_in_block_from_elem(elem_size_type row, elem_size_type col) const + { + return (is_transposed()) + ? row % BlockSideLength + col % BlockSideLength * BlockSideLength + : row % BlockSideLength * BlockSideLength + col % BlockSideLength; + } + + //! get identifier of the block at (row, col) + const swappable_block_identifier_type & block(const size_type row, const size_type col) const + { return blocks[row * width + col]; } + + //! get identifier of the block at (row, col) + const swappable_block_identifier_type& operator () (const size_type row, const size_type col) const + { return block(row, col); } + + const size_type & get_height() const + { return height; } + + const size_type & get_width() const + { return width; } + + //! if the elements inside the blocks are in transposed order i.e. column-major + const bool & is_transposed() const + { return elements_in_blocks_transposed; } + + void transpose() + { + // transpose matrix of blocks + blocks_type bn(blocks.size()); + for (size_type row = 0; row < height; ++row) + for (size_type col = 0; col < width; ++col) + bn[col * height + row] = bl(row, col); + bn.swap(blocks); + // swap dimensions + std::swap(height, width); + std::swap(height_from_supermatrix, width_from_supermatrix); + elements_in_blocks_transposed = ! elements_in_blocks_transposed; + } + + void set_zero() + { + for (typename blocks_type::iterator it = blocks.begin(); it != blocks.end(); ++it) + bs.deinitialize(*it); + } +}; + +//! general iterator type that points to single elements inside a matrix +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +template +class matrix_iterator +{ +protected: + typedef matrix matrix_type; + typedef typename matrix_type::swappable_block_matrix_type swappable_block_matrix_type; + typedef typename matrix_type::block_scheduler_type block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename matrix_type::elem_size_type elem_size_type; + typedef typename matrix_type::block_size_type block_size_type; + + template + friend class matrix; + + template + friend class const_matrix_iterator; + + matrix_type* m; + elem_size_type current_row, // \ both indices == -1 <=> empty iterator + current_col; // / + block_size_type current_block_row, + current_block_col; + internal_block_type* current_iblock; // NULL if block is not acquired + + void acquire_current_iblock() + { + if (! current_iblock) + current_iblock = &m->data->bs.acquire(m->data->block(current_block_row, current_block_col)); + } + + void release_current_iblock() + { + if (current_iblock) + { + m->data->bs.release(m->data->block(current_block_row, current_block_col), true); + current_iblock = 0; + } + } + + //! create iterator pointing to given row and col + matrix_iterator(matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) + : m(&matrix), + current_row(start_row), + current_col(start_col), + current_block_row(m->data->block_index_from_elem(start_row)), + current_block_col(m->data->block_index_from_elem(start_col)), + current_iblock(0) { } + + //! create empty iterator + matrix_iterator(matrix_type& matrix) + : m(&matrix), + current_row(-1), // empty iterator + current_col(-1), + current_block_row(-1), + current_block_col(-1), + current_iblock(0) { } + + void set_empty() + { + release_current_iblock(); + current_row = -1; + current_col = -1; + current_block_row = -1; + current_block_col = -1; + } + +public: + matrix_iterator(const matrix_iterator& other) + : m(other.m), + current_row(other.current_row), + current_col(other.current_col), + current_block_row(other.current_block_row), + current_block_col(other.current_block_col), + current_iblock(0) + { + if (other.current_iblock) + acquire_current_iblock(); + } + + matrix_iterator& operator = (const matrix_iterator& other) + { + set_pos(other.current_row, other.current_col); + m = other.m; + if (other.current_iblock) + acquire_current_iblock(); + return *this; + } + + ~matrix_iterator() + { release_current_iblock(); } + + void set_row(const elem_size_type new_row) + { + const block_size_type new_block_row = m->data->block_index_from_elem(new_row); + if (new_block_row != current_block_row) + { + release_current_iblock(); + current_block_row = new_block_row; + } + current_row = new_row; + } + + void set_col(const elem_size_type new_col) + { + const block_size_type new_block_col = m->data->block_index_from_elem(new_col); + if (new_block_col != current_block_col) + { + release_current_iblock(); + current_block_col = new_block_col; + } + current_col = new_col; + } + + void set_pos(const elem_size_type new_row, const elem_size_type new_col) + { + const block_size_type new_block_row = m->data->block_index_from_elem(new_row), + new_block_col = m->data->block_index_from_elem(new_col); + if (new_block_col != current_block_col || new_block_row != current_block_row) + { + release_current_iblock(); + current_block_row = new_block_row; + current_block_col = new_block_col; + } + current_row = new_row; + current_col = new_col; + } + + void set_pos(const std::pair new_pos) + { set_pos(new_pos.first, new_pos.second); } + + const elem_size_type & get_row() const + { return current_row; } + + const elem_size_type & get_col() const + { return current_col; } + + std::pair get_pos() const + { return std::make_pair(current_row, current_col); } + + bool empty() const + { return current_row == -1 && current_col == -1; } + + operator bool () const + { return ! empty(); } + + bool operator == (const matrix_iterator& other) const + { + return current_row == other.current_row && current_col == other.current_col && m == other.m; + } + + //! Returns reference access to the element referenced by the iterator. + //! The reference is only valid so long as the iterator is not moved. + ValueType& operator * () + { + acquire_current_iblock(); + return (*current_iblock)[m->data->elem_index_in_block_from_elem(current_row, current_col)]; + } +}; + +//! row-major iterator that points to single elements inside a matrix +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +template +class matrix_row_major_iterator : public matrix_iterator +{ +protected: + typedef matrix_iterator matrix_iterator_type; + typedef typename matrix_iterator_type::matrix_type matrix_type; + typedef typename matrix_iterator_type::elem_size_type elem_size_type; + + template + friend class matrix; + + using matrix_iterator_type::m; + using matrix_iterator_type::set_empty; + + //! create iterator pointing to given row and col + matrix_row_major_iterator(matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) + : matrix_iterator_type(matrix, start_row, start_col) { } + + //! create empty iterator + matrix_row_major_iterator(matrix_type& matrix) + : matrix_iterator_type(matrix) { } + +public: + //! convert from matrix_iterator + matrix_row_major_iterator(const matrix_iterator_type& matrix_iterator) + : matrix_iterator_type(matrix_iterator) { } + + // Has to be not empty, else behavior is undefined. + matrix_row_major_iterator& operator ++ () + { + if (get_col() + 1 < m->get_width()) + // => not matrix_row_major_iterator the end of row, move right + set_col(get_col() + 1); + else if (get_row() + 1 < m->get_height()) + // => at end of row but not last row, move to beginning of next row + set_pos(get_row() + 1, 0); + else + // => at end of matrix, set to empty-state + set_empty(); + return *this; + } + + // Has to be not empty, else behavior is undefined. + matrix_row_major_iterator& operator -- () + { + if (get_col() - 1 >= 0) + // => not at the beginning of row, move left + set_col(get_col() - 1); + else if (get_row() - 1 >= 0) + // => at beginning of row but not first row, move to end of previous row + set_pos(get_row() - 1, m->get_width() - 1); + else + // => at beginning of matrix, set to empty-state + set_empty(); + return *this; + } + + using matrix_iterator_type::get_row; + using matrix_iterator_type::get_col; + using matrix_iterator_type::set_col; + using matrix_iterator_type::set_pos; +}; + +//! column-major iterator that points to single elements inside a matrix +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +template +class matrix_col_major_iterator : public matrix_iterator +{ +protected: + typedef matrix_iterator matrix_iterator_type; + typedef typename matrix_iterator_type::matrix_type matrix_type; + typedef typename matrix_iterator_type::elem_size_type elem_size_type; + + template + friend class matrix; + + using matrix_iterator_type::m; + using matrix_iterator_type::set_empty; + + //! create iterator pointing to given row and col + matrix_col_major_iterator(matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) + : matrix_iterator_type(matrix, start_row, start_col) { } + + //! create empty iterator + matrix_col_major_iterator(matrix_type& matrix) + : matrix_iterator_type(matrix) { } + +public: + //! convert from matrix_iterator + matrix_col_major_iterator(const matrix_iterator_type& matrix_iterator) + : matrix_iterator_type(matrix_iterator) { } + + // Has to be not empty, else behavior is undefined. + matrix_col_major_iterator& operator ++ () + { + if (get_row() + 1 < m->get_height()) + // => not at the end of col, move down + set_row(get_row() + 1); + else if (get_col() + 1 < m->get_width()) + // => at end of col but not last col, move to beginning of next col + set_pos(0, get_col() + 1); + else + // => at end of matrix, set to empty-state + set_empty(); + return *this; + } + + // Has to be not empty, else behavior is undefined. + matrix_col_major_iterator& operator -- () + { + if (get_row() - 1 >= 0) + // => not at the beginning of col, move up + set_row(get_row() - 1); + else if (get_col() - 1 >= 0) + // => at beginning of col but not first col, move to end of previous col + set_pos(m->get_height() - 1, get_col() - 1); + else + // => at beginning of matrix, set to empty-state + set_empty(); + return *this; + } + + using matrix_iterator_type::get_row; + using matrix_iterator_type::get_col; + using matrix_iterator_type::set_row; + using matrix_iterator_type::set_pos; +}; + +//! general const_iterator type that points to single elements inside a matrix +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +template +class const_matrix_iterator +{ +protected: + typedef matrix matrix_type; + typedef typename matrix_type::swappable_block_matrix_type swappable_block_matrix_type; + typedef typename matrix_type::block_scheduler_type block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename matrix_type::elem_size_type elem_size_type; + typedef typename matrix_type::block_size_type block_size_type; + + template + friend class matrix; + + const matrix_type* m; + elem_size_type current_row, // \ both indices == -1 <=> empty iterator + current_col; // / + block_size_type current_block_row, + current_block_col; + internal_block_type* current_iblock; // NULL if block is not acquired + + void acquire_current_iblock() + { + if (! current_iblock) + current_iblock = &m->data->bs.acquire(m->data->block(current_block_row, current_block_col)); + } + + void release_current_iblock() + { + if (current_iblock) + { + m->data->bs.release(m->data->block(current_block_row, current_block_col), false); + current_iblock = 0; + } + } + + //! create iterator pointing to given row and col + const_matrix_iterator(const matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) + : m(&matrix), + current_row(start_row), + current_col(start_col), + current_block_row(m->data->block_index_from_elem(start_row)), + current_block_col(m->data->block_index_from_elem(start_col)), + current_iblock(0) { } + + //! create empty iterator + const_matrix_iterator(const matrix_type& matrix) + : m(&matrix), + current_row(-1), // empty iterator + current_col(-1), + current_block_row(-1), + current_block_col(-1), + current_iblock(0) { } + + void set_empty() + { + release_current_iblock(); + current_row = -1; + current_col = -1; + current_block_row = -1; + current_block_col = -1; + } + +public: + const_matrix_iterator(const matrix_iterator& other) + : m(other.m), + current_row(other.current_row), + current_col(other.current_col), + current_block_row(other.current_block_row), + current_block_col(other.current_block_col), + current_iblock(0) + { + if (other.current_iblock) + acquire_current_iblock(); + } + + const_matrix_iterator(const const_matrix_iterator& other) + : m(other.m), + current_row(other.current_row), + current_col(other.current_col), + current_block_row(other.current_block_row), + current_block_col(other.current_block_col), + current_iblock(0) + { + if (other.current_iblock) + acquire_current_iblock(); + } + + const_matrix_iterator& operator = (const const_matrix_iterator& other) + { + set_pos(other.current_row, other.current_col); + m = other.m; + if (other.current_iblock) + acquire_current_iblock(); + return *this; + } + + ~const_matrix_iterator() + { release_current_iblock(); } + + void set_row(const elem_size_type new_row) + { + const block_size_type new_block_row = m->data->block_index_from_elem(new_row); + if (new_block_row != current_block_row) + { + release_current_iblock(); + current_block_row = new_block_row; + } + current_row = new_row; + } + + void set_col(const elem_size_type new_col) + { + const block_size_type new_block_col = m->data->block_index_from_elem(new_col); + if (new_block_col != current_block_col) + { + release_current_iblock(); + current_block_col = new_block_col; + } + current_col = new_col; + } + + void set_pos(const elem_size_type new_row, const elem_size_type new_col) + { + const block_size_type new_block_row = m->data->block_index_from_elem(new_row), + new_block_col = m->data->block_index_from_elem(new_col); + if (new_block_col != current_block_col || new_block_row != current_block_row) + { + release_current_iblock(); + current_block_row = new_block_row; + current_block_col = new_block_col; + } + current_row = new_row; + current_col = new_col; + } + + void set_pos(const std::pair new_pos) + { set_pos(new_pos.first, new_pos.second); } + + const elem_size_type & get_row() const + { return current_row; } + + const elem_size_type & get_col() const + { return current_col; } + + std::pair get_pos() const + { return std::make_pair(current_row, current_col); } + + bool empty() const + { return current_row == -1 && current_col == -1; } + + operator bool () const + { return ! empty(); } + + bool operator == (const const_matrix_iterator& other) const + { + return current_row == other.current_row && current_col == other.current_col && m == other.m; + } + + //! Returns reference access to the element referenced by the iterator. + //! The reference is only valid so long as the iterator is not moved. + const ValueType& operator * () + { + acquire_current_iblock(); + return (*current_iblock)[m->data->elem_index_in_block_from_elem(current_row, current_col)]; + } +}; + +//! row-major const_iterator that points to single elements inside a matrix +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +template +class const_matrix_row_major_iterator : public const_matrix_iterator +{ +protected: + typedef const_matrix_iterator const_matrix_iterator_type; + typedef typename const_matrix_iterator_type::matrix_type matrix_type; + typedef typename const_matrix_iterator_type::elem_size_type elem_size_type; + + template + friend class matrix; + + using const_matrix_iterator_type::m; + using const_matrix_iterator_type::set_empty; + + //! create iterator pointing to given row and col + const_matrix_row_major_iterator(const matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) + : const_matrix_iterator_type(matrix, start_row, start_col) { } + + //! create empty iterator + const_matrix_row_major_iterator(const matrix_type& matrix) + : const_matrix_iterator_type(matrix) { } + +public: + //! convert from matrix_iterator + const_matrix_row_major_iterator(const const_matrix_row_major_iterator& matrix_iterator) + : const_matrix_iterator_type(matrix_iterator) { } + + //! convert from matrix_iterator + const_matrix_row_major_iterator(const const_matrix_iterator_type& matrix_iterator) + : const_matrix_iterator_type(matrix_iterator) { } + + // Has to be not empty, else behavior is undefined. + const_matrix_row_major_iterator& operator ++ () + { + if (get_col() + 1 < m->get_width()) + // => not matrix_row_major_iterator the end of row, move right + set_col(get_col() + 1); + else if (get_row() + 1 < m->get_height()) + // => at end of row but not last row, move to beginning of next row + set_pos(get_row() + 1, 0); + else + // => at end of matrix, set to empty-state + set_empty(); + return *this; + } + + // Has to be not empty, else behavior is undefined. + const_matrix_row_major_iterator& operator -- () + { + if (get_col() - 1 >= 0) + // => not at the beginning of row, move left + set_col(get_col() - 1); + else if (get_row() - 1 >= 0) + // => at beginning of row but not first row, move to end of previous row + set_pos(get_row() - 1, m->get_width() - 1); + else + // => at beginning of matrix, set to empty-state + set_empty(); + return *this; + } + + using const_matrix_iterator_type::get_row; + using const_matrix_iterator_type::get_col; + using const_matrix_iterator_type::set_col; + using const_matrix_iterator_type::set_pos; +}; + +//! column-major const_iterator that points to single elements inside a matrix +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +template +class const_matrix_col_major_iterator : public const_matrix_iterator +{ +protected: + typedef const_matrix_iterator const_matrix_iterator_type; + typedef typename const_matrix_iterator_type::matrix_type matrix_type; + typedef typename const_matrix_iterator_type::elem_size_type elem_size_type; + + template + friend class matrix; + + using const_matrix_iterator_type::m; + using const_matrix_iterator_type::set_empty; + + //! create iterator pointing to given row and col + const_matrix_col_major_iterator(const matrix_type& matrix, const elem_size_type start_row, const elem_size_type start_col) + : const_matrix_iterator_type(matrix, start_row, start_col) { } + + //! create empty iterator + const_matrix_col_major_iterator(const matrix_type& matrix) + : const_matrix_iterator_type(matrix) { } + +public: + //! convert from matrix_iterator + const_matrix_col_major_iterator(const matrix_iterator& matrix_iterator) + : const_matrix_iterator_type(matrix_iterator) { } + + //! convert from matrix_iterator + const_matrix_col_major_iterator(const const_matrix_iterator_type& matrix_iterator) + : const_matrix_iterator_type(matrix_iterator) { } + + // Has to be not empty, else behavior is undefined. + const_matrix_col_major_iterator& operator ++ () + { + if (get_row() + 1 < m->get_height()) + // => not at the end of col, move down + set_row(get_row() + 1); + else if (get_col() + 1 < m->get_width()) + // => at end of col but not last col, move to beginning of next col + set_pos(0, get_col() + 1); + else + // => at end of matrix, set to empty-state + set_empty(); + return *this; + } + + // Has to be not empty, else behavior is undefined. + const_matrix_col_major_iterator& operator -- () + { + if (get_row() - 1 >= 0) + // => not at the beginning of col, move up + set_row(get_row() - 1); + else if (get_col() - 1 >= 0) + // => at beginning of col but not first col, move to end of previous col + set_pos(m->get_height() - 1, get_col() - 1); + else + // => at beginning of matrix, set to empty-state + set_empty(); + return *this; + } + + using const_matrix_iterator_type::get_row; + using const_matrix_iterator_type::get_col; + using const_matrix_iterator_type::set_row; + using const_matrix_iterator_type::set_pos; +}; + +//! External matrix container. \n +//! Introduction to matrix container: see \ref tutorial_matrix tutorial. \n +//! Design and Internals of matrix container: see \ref design_matrix. +//! +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam BlockSideLength side length of a matrix block +//! +//! Divides the matrix in square submatrices (blocks). +//! Blocks can be swapped individually to and from external memory. +//! They are only swapped if necessary to minimize I/O. +template +class matrix +{ +protected: + typedef matrix matrix_type; + typedef swappable_block_matrix swappable_block_matrix_type; + typedef counting_ptr swappable_block_matrix_pointer_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename swappable_block_matrix_type::size_type block_size_type; + typedef typename swappable_block_matrix_type::elem_size_type elem_size_type; + typedef matrix_local::matrix_operations Ops; + typedef matrix_swappable_block swappable_block_type; + +public: + typedef matrix_iterator iterator; + typedef const_matrix_iterator const_iterator; + typedef matrix_row_major_iterator row_major_iterator; + typedef matrix_col_major_iterator col_major_iterator; + typedef const_matrix_row_major_iterator const_row_major_iterator; + typedef const_matrix_col_major_iterator const_col_major_iterator; + typedef column_vector column_vector_type; + typedef row_vector row_vector_type; + +protected: + template + friend class matrix_iterator; + + template + friend class const_matrix_iterator; + + elem_size_type height, width; + + swappable_block_matrix_pointer_type data; + +public: + //! \name Constructors/Destructors + //! \{ + + //! Creates a new matrix of given dimensions. Elements' values are set to zero. + //! \param bs block scheduler used + //! \param height height of the created matrix + //! \param width width of the created matrix + matrix(block_scheduler_type& bs, + const elem_size_type height, const elem_size_type width) + : height(height), + width(width), + data( + new swappable_block_matrix_type( + bs, + div_ceil(height, BlockSideLength), + div_ceil(width, BlockSideLength)) + ) + { } + + matrix(block_scheduler_type& bs, + const column_vector_type& left, const row_vector_type& right) + : height((elem_size_type)left.size()), + width((elem_size_type)right.size()), + data( + new swappable_block_matrix_type( + bs, + div_ceil(height, BlockSideLength), + div_ceil(width, BlockSideLength)) + ) + { Ops::recursive_matrix_from_vectors(*data, left, right); } + + ~matrix() { } + //! \} + + //! \name Capacity + //! \{ + const elem_size_type & get_height() const + { return height; } + + const elem_size_type & get_width() const + { return width; } + //! \} + + //! \name Iterators + //! \{ + iterator begin() + { + data.unify(); + return iterator(*this, 0, 0); + } + const_iterator begin() const + { return const_iterator(*this, 0, 0); } + const_iterator cbegin() const + { return const_iterator(*this, 0, 0); } + iterator end() + { + data.unify(); + return iterator(*this); + } + const_iterator end() const + { return const_iterator(*this); } + const_iterator cend() const + { return const_iterator(*this); } + const_iterator operator () (const elem_size_type row, const elem_size_type col) const + { return const_iterator(*this, row, col); } + + iterator operator () (const elem_size_type row, const elem_size_type col) + { + data.unify(); + return iterator(*this, row, col); + } + //! \} + + //! \name Modifiers + //! \{ + void transpose() + { + data.unify(); + data->transpose(); + std::swap(height, width); + } + + void set_zero() + { + if (data.unique()) + data->set_zero(); + else + data = new swappable_block_matrix_type + (data->bs, div_ceil(height, BlockSideLength), div_ceil(width, BlockSideLength)); + } + //! \} + + //! \name Operations + //! \{ + matrix_type operator + (const matrix_type& right) const + { + assert(height == right.height && width == right.width); + matrix_type res(data->bs, height, width); + Ops::element_op(*res.data, *data, *right.data, typename Ops::addition()); // more efficient than copying this and then adding right + return res; + } + + matrix_type operator - (const matrix_type& right) const + { + assert(height == right.height && width == right.width); + matrix_type res(data->bs, height, width); + Ops::element_op(*res.data, *data, *right.data, typename Ops::subtraction()); // more efficient than copying this and then subtracting right + return res; + } + + matrix_type operator * (const matrix_type& right) const + { return multiply(right); } + + matrix_type operator * (const ValueType scalar) const + { + matrix_type res(data->bs, height, width); + Ops::element_op(*res.data, *data, typename Ops::scalar_multiplication(scalar)); + return res; + } + + matrix_type& operator += (const matrix_type& right) + { + assert(height == right.height && width == right.width); + data.unify(); + Ops::element_op(*data, *right.data, typename Ops::addition()); + return *this; + } + + matrix_type& operator -= (const matrix_type& right) + { + assert(height == right.height && width == right.width); + data.unify(); + Ops::element_op(*data, *right.data, typename Ops::subtraction()); + return *this; + } + + matrix_type& operator *= (const matrix_type& right) + { return *this = operator * (right); } // implicitly unifies by constructing a result-matrix + + matrix_type& operator *= (const ValueType scalar) + { + data.unify(); + Ops::element_op(*data, typename Ops::scalar_multiplication(scalar)); + return *this; + } + + column_vector_type operator * (const column_vector_type& right) const + { + assert(elem_size_type(right.size()) == width); + column_vector_type res(height); + res.set_zero(); + Ops::recursive_matrix_col_vector_multiply_and_add(*data, right, res); + return res; + } + + row_vector_type multiply_from_left(const row_vector_type& left) const + { + assert(elem_size_type(left.size()) == height); + row_vector_type res(width); + res.set_zero(); + Ops::recursive_matrix_row_vector_multiply_and_add(left, *data, res); + return res; + } + + //! multiply with another matrix + //! \param right matrix to multiply with + //! \param multiplication_algorithm allows to choose the applied algorithm + //! \param scheduling_algorithm allows to choose the applied algorithm + //! + //! Available algorithms are: \n + //! 0: naive_multiply_and_add (I/O inefficient, slow) \n + //! 1: recursive_multiply_and_add (recommended, default, stable time and I/O complexity) \n + //! 2: strassen_winograd_multiply_and_add (sometimes fast but unstable time and I/O complexity) \n + //! 3: multi_level_strassen_winograd_multiply_and_add (sometimes fast but unstable time and I/O complexity) \n + //! 4: strassen_winograd_multiply, optimized pre- and postadditions (sometimes fast but unstable time and I/O complexity) \n + //! 5: strassen_winograd_multiply_and_add_interleaved, optimized preadditions (sometimes fast but unstable time and I/O complexity) \n + //! 6: multi_level_strassen_winograd_multiply_and_add_block_grained (sometimes fast but unstable time and I/O complexity) + matrix_type multiply(const matrix_type& right, const int_type multiplication_algorithm = 1, const int_type scheduling_algorithm = 2) const + { + assert(width == right.height); + assert(&data->bs == &right.data->bs); + matrix_type res(data->bs, height, right.width); + + if (scheduling_algorithm > 0) + { + // all offline algos need a simulation-run + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_simulation(data->bs) + ); + switch (multiplication_algorithm) + { + case 0: + Ops::naive_multiply_and_add(*data, *right.data, *res.data); + break; + case 1: + Ops::recursive_multiply_and_add(*data, *right.data, *res.data); + break; + case 2: + Ops::strassen_winograd_multiply_and_add(*data, *right.data, *res.data); + break; + case 3: + Ops::multi_level_strassen_winograd_multiply_and_add(*data, *right.data, *res.data); + break; + case 4: + Ops::strassen_winograd_multiply(*data, *right.data, *res.data); + break; + case 5: + Ops::strassen_winograd_multiply_and_add_interleaved(*data, *right.data, *res.data); + break; + case 6: + Ops::multi_level_strassen_winograd_multiply_and_add_block_grained(*data, *right.data, *res.data); + break; + default: + STXXL_ERRMSG("invalid multiplication-algorithm number"); + break; + } + } + switch (scheduling_algorithm) + { + case 0: + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_online_lru(data->bs) + ); + break; + case 1: + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_offline_lfd(data->bs) + ); + break; + case 2: + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_offline_lru_prefetching(data->bs) + ); + break; + default: + STXXL_ERRMSG("invalid scheduling-algorithm number"); + } + switch (multiplication_algorithm) + { + case 0: + Ops::naive_multiply_and_add(*data, *right.data, *res.data); + break; + case 1: + Ops::recursive_multiply_and_add(*data, *right.data, *res.data); + break; + case 2: + Ops::strassen_winograd_multiply_and_add(*data, *right.data, *res.data); + break; + case 3: + Ops::multi_level_strassen_winograd_multiply_and_add(*data, *right.data, *res.data); + break; + case 4: + Ops::strassen_winograd_multiply(*data, *right.data, *res.data); + break; + case 5: + Ops::strassen_winograd_multiply_and_add_interleaved(*data, *right.data, *res.data); + break; + case 6: + Ops::multi_level_strassen_winograd_multiply_and_add_block_grained(*data, *right.data, *res.data); + break; + default: + STXXL_ERRMSG("invalid multiplication-algorithm number"); + break; + } + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_online_lru(data->bs) + ); + return res; + } + + //! Use internal memory multiplication. Designated for testing. May exceed memory limitations. + matrix_type multiply_internal(const matrix_type& right, const int_type scheduling_algorithm = 2) const + { + assert(width == right.height); + assert(&data->bs == &right.data->bs); + matrix_type res(data->bs, height, right.width); + + if (scheduling_algorithm > 0) + { + // all offline algos need a simulation-run + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_simulation(data->bs) + ); + multiply_internal(right, res); + } + switch (scheduling_algorithm) + { + case 0: + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_online_lru(data->bs) + ); + break; + case 1: + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_offline_lfd(data->bs) + ); + break; + case 2: + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_offline_lru_prefetching(data->bs) + ); + break; + default: + STXXL_ERRMSG("invalid scheduling-algorithm number"); + } + multiply_internal(right, res); + delete data->bs.switch_algorithm_to( + new block_scheduler_algorithm_online_lru(data->bs) + ); + return res; + } + //! \} + +protected: + void multiply_internal(const matrix_type& right, matrix_type& res) const + { + ValueType* A = new ValueType[height * width]; + ValueType* B = new ValueType[right.height * right.width]; + ValueType* C = new ValueType[res.height * res.width]; + ValueType* vit; + vit = A; + for (const_row_major_iterator mit = cbegin(); mit != cend(); ++mit, ++vit) + *vit = *mit; + vit = B; + for (const_row_major_iterator mit = right.cbegin(); mit != right.cend(); ++mit, ++vit) + *vit = *mit; + if (! res.data->bs.is_simulating()) + { +#if STXXL_BLAS + gemm_wrapper(height, width, res.width, + ValueType(1), false, A, + false, B, + ValueType(0), false, C); +#else + assert(false /* internal multiplication is only available for testing with blas */); +#endif + } + vit = C; + for (row_major_iterator mit = res.begin(); mit != res.end(); ++mit, ++vit) + *mit = *vit; + delete[] A; + delete[] B; + delete[] C; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_MATRIX_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_arithmetic.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_arithmetic.h new file mode 100644 index 0000000000..0ae6ffca26 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_arithmetic.h @@ -0,0 +1,2064 @@ +/*************************************************************************** + * include/stxxl/bits/containers/matrix_arithmetic.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010-2011 Raoul Steffen + * + * 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 STXXL_CONTAINERS_MATRIX_ARITHMETIC_HEADER +#define STXXL_CONTAINERS_MATRIX_ARITHMETIC_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +#ifndef STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS +#define STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS 3 +#endif +#ifndef STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE +#define STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE 2 +#endif + +template +class column_vector; + +template +class row_vector; + +template +class swappable_block_matrix; + +//! \addtogroup matrix +//! \{ + +struct matrix_operation_statistic_dataset +{ + int_type block_multiplication_calls, + block_multiplications_saved_through_zero, + block_addition_calls, + block_additions_saved_through_zero; + + matrix_operation_statistic_dataset() + : block_multiplication_calls(0), + block_multiplications_saved_through_zero(0), + block_addition_calls(0), + block_additions_saved_through_zero(0) { } + + matrix_operation_statistic_dataset operator + (const matrix_operation_statistic_dataset& stat) + { + matrix_operation_statistic_dataset res(*this); + res.block_multiplication_calls += stat.block_multiplication_calls; + res.block_multiplications_saved_through_zero += stat.block_multiplications_saved_through_zero; + res.block_addition_calls += stat.block_addition_calls; + res.block_additions_saved_through_zero += stat.block_additions_saved_through_zero; + return res; + } + + matrix_operation_statistic_dataset operator - (const matrix_operation_statistic_dataset& stat) + { + matrix_operation_statistic_dataset res(*this); + res.block_multiplication_calls -= stat.block_multiplication_calls; + res.block_multiplications_saved_through_zero -= stat.block_multiplications_saved_through_zero; + res.block_addition_calls -= stat.block_addition_calls; + res.block_additions_saved_through_zero -= stat.block_additions_saved_through_zero; + return res; + } +}; + +struct matrix_operation_statistic + : public singleton, public matrix_operation_statistic_dataset +{ }; + +struct matrix_operation_statistic_data : public matrix_operation_statistic_dataset +{ + matrix_operation_statistic_data(const matrix_operation_statistic& stat = * matrix_operation_statistic::get_instance()) + : matrix_operation_statistic_dataset(stat) { } + + matrix_operation_statistic_data(const matrix_operation_statistic_dataset& stat) + : matrix_operation_statistic_dataset(stat) { } + + matrix_operation_statistic_data& operator = (const matrix_operation_statistic& stat) + { + return *this = matrix_operation_statistic_data(stat); + } + + void set() + { operator = (*matrix_operation_statistic::get_instance()); } + + matrix_operation_statistic_data operator + (const matrix_operation_statistic_data& stat) + { return matrix_operation_statistic_data(matrix_operation_statistic_dataset(*this) + matrix_operation_statistic_dataset(stat)); } + + matrix_operation_statistic_data operator - (const matrix_operation_statistic_data& stat) + { return matrix_operation_statistic_data(matrix_operation_statistic_dataset(*this) - matrix_operation_statistic_dataset(stat)); } +}; + +std::ostream& operator << (std::ostream& o, const matrix_operation_statistic_data& statsd) +{ + o << "matrix operation statistics" << std::endl; + o << "block multiplication calls : " + << statsd.block_multiplication_calls << std::endl; + o << "block multiplications saved through zero blocks: " + << statsd.block_multiplications_saved_through_zero << std::endl; + o << "block multiplications performed : " + << statsd.block_multiplication_calls - statsd.block_multiplications_saved_through_zero << std::endl; + o << "block addition calls : " + << statsd.block_addition_calls << std::endl; + o << "block additions saved through zero blocks : " + << statsd.block_additions_saved_through_zero << std::endl; + o << "block additions performed : " + << statsd.block_addition_calls - statsd.block_additions_saved_through_zero << std::endl; + return o; +} + +//! \} + +//! matrix low-level operations and tools +namespace matrix_local { + +//! A static_quadtree holds 4^Level elements arranged in a quad tree. +//! +//! Static quad trees are useful for recursive algorithms with fixed depth +//! that partition the in- and output and perform pre- and postcalculations on the partitions. +//! The four children of one node are denoted as ul (up left), ur (up right), dl (down left), and dr (down right). +template +struct static_quadtree +{ + typedef static_quadtree smaller_static_quadtree; + + smaller_static_quadtree ul, ur, dl, dr; + + static_quadtree(smaller_static_quadtree ul, smaller_static_quadtree ur, + smaller_static_quadtree dl, smaller_static_quadtree dr) + : ul(ul), ur(ur), dl(dl), dr(dr) { } + + static_quadtree() { } + + static_quadtree& operator &= (const static_quadtree& right) + { + ul &= right.ul, ur &= right.ur; + dl &= right.dl, dr &= right.dr; + return *this; + } + + static_quadtree& operator += (const static_quadtree& right) + { + ul += right.ul, ur += right.ur; + dl += right.dl, dr += right.dr; + return *this; + } + + static_quadtree& operator -= (const static_quadtree& right) + { + ul -= right.ul, ur -= right.ur; + dl -= right.dl, dr -= right.dr; + return *this; + } + + static_quadtree operator & (const static_quadtree& right) const + { return static_quadtree(ul & right.ul, ur & right.ur, dl & right.dl, dr & right.dr); } + + static_quadtree operator + (const static_quadtree& right) const + { return static_quadtree(ul + right.ul, ur + right.ur, dl + right.dl, dr + right.dr); } + + static_quadtree operator - (const static_quadtree& right) const + { return static_quadtree(ul - right.ul, ur - right.ur, dl - right.dl, dr - right.dr); } +}; + +template +struct static_quadtree +{ + ValueType val; + + static_quadtree(const ValueType& v) + : val(v) { } + + static_quadtree() { } + + operator const ValueType& () const + { return val; } + + operator ValueType& () + { return val; } + + static_quadtree& operator &= (const static_quadtree& right) + { + val &= right.val; + return *this; + } + + static_quadtree& operator += (const static_quadtree& right) + { + val += right.val; + return *this; + } + + static_quadtree& operator -= (const static_quadtree& right) + { + val -= right.val; + return *this; + } + + static_quadtree operator ! () const + { return static_quadtree(! val); } + + static_quadtree operator & (const static_quadtree& right) const + { return val & right.val; } + + static_quadtree operator + (const static_quadtree& right) const + { return val + right.val; } + + static_quadtree operator - (const static_quadtree& right) const + { return val - right.val; } +}; + +template +struct feedable_strassen_winograd +{ + typedef static_quadtree zbt; // true <=> is a zero-block + typedef static_quadtree vt; + + typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_ab; + typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_a; + typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_b; + typedef feedable_strassen_winograd smaller_feedable_strassen_winograd_n; + + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename swappable_block_matrix_type::size_type size_type; + + const size_type n, m, l; + smaller_feedable_strassen_winograd_ab p1, p2; + smaller_feedable_strassen_winograd_n p3, p4, p5; + smaller_feedable_strassen_winograd_b p6; + smaller_feedable_strassen_winograd_a p7; + + feedable_strassen_winograd( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : n(n), m(m), l(l), + p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), + p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), + p3( bs_c, n/2, m/2, l/2), + p4( bs_c, n/2, m/2, l/2), + p5( bs_c, n/2, m/2, l/2), + p6( bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), + p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} + + feedable_strassen_winograd( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : n(n), m(m), l(l), + p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2), + p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2), + p3( bs_c, n/2, m/2, l/2), + p4( bs_c, n/2, m/2, l/2), + p5( bs_c, n/2, m/2, l/2), + p6( bs_c, n/2, m/2, l/2), + p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} + + feedable_strassen_winograd( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : n(n), m(m), l(l), + p1(bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), + p2(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), + p3(bs_c, n/2, m/2, l/2), + p4(bs_c, n/2, m/2, l/2), + p5(bs_c, n/2, m/2, l/2), + p6(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), + p7(bs_c, n/2, m/2, l/2) {} + + feedable_strassen_winograd( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : n(n), m(m), l(l), + p1(bs_c, n / 2, m / 2, l / 2), + p2(bs_c, n / 2, m / 2, l / 2), + p3(bs_c, n / 2, m / 2, l / 2), + p4(bs_c, n / 2, m / 2, l / 2), + p5(bs_c, n / 2, m / 2, l / 2), + p6(bs_c, n / 2, m / 2, l / 2), + p7(bs_c, n / 2, m / 2, l / 2) { } + + void begin_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + typename zbt::smaller_static_quadtree + s1 = zb.dl & zb.dr, + s2 = s1 & zb.ul, + s3 = zb.ul & zb.dl, + s4 = zb.ur & s2; + p1.begin_feeding_a_block(block_row, block_col, zb.ul); + p2.begin_feeding_a_block(block_row, block_col, zb.ur); + p3.begin_feeding_a_block(block_row, block_col, s1); + p4.begin_feeding_a_block(block_row, block_col, s2); + p5.begin_feeding_a_block(block_row, block_col, s3); + p6.begin_feeding_a_block(block_row, block_col, s4); + p7.begin_feeding_a_block(block_row, block_col, zb.dr); + } + + void feed_a_element(const int_type element_num, const vt v) + { + typename vt::smaller_static_quadtree + s1 = v.dl + v.dr, + s2 = s1 - v.ul, + s3 = v.ul - v.dl, + s4 = v.ur - s2; + p1.feed_a_element(element_num, v.ul); + p2.feed_a_element(element_num, v.ur); + p3.feed_a_element(element_num, s1); + p4.feed_a_element(element_num, s2); + p5.feed_a_element(element_num, s3); + p6.feed_a_element(element_num, s4); + p7.feed_a_element(element_num, v.dr); + } + + void end_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + typename zbt::smaller_static_quadtree + s1 = zb.dl & zb.dr, + s2 = s1 & zb.ul, + s3 = zb.ul & zb.dl, + s4 = zb.ur & s2; + p1.end_feeding_a_block(block_row, block_col, zb.ul); + p2.end_feeding_a_block(block_row, block_col, zb.ur); + p3.end_feeding_a_block(block_row, block_col, s1); + p4.end_feeding_a_block(block_row, block_col, s2); + p5.end_feeding_a_block(block_row, block_col, s3); + p6.end_feeding_a_block(block_row, block_col, s4); + p7.end_feeding_a_block(block_row, block_col, zb.dr); + } + + void begin_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + typename zbt::smaller_static_quadtree + t1 = zb.ur & zb.ul, + t2 = zb.dr & t1, + t3 = zb.dr & zb.ur, + t4 = zb.dl & t2; + p1.begin_feeding_b_block(block_row, block_col, zb.ul); + p2.begin_feeding_b_block(block_row, block_col, zb.dl); + p3.begin_feeding_b_block(block_row, block_col, t1); + p4.begin_feeding_b_block(block_row, block_col, t2); + p5.begin_feeding_b_block(block_row, block_col, t3); + p6.begin_feeding_b_block(block_row, block_col, zb.dr); + p7.begin_feeding_b_block(block_row, block_col, t4); + } + + void feed_b_element(const int_type element_num, const vt v) + { + typename vt::smaller_static_quadtree + t1 = v.ur - v.ul, + t2 = v.dr - t1, + t3 = v.dr - v.ur, + t4 = v.dl - t2; + p1.feed_b_element(element_num, v.ul); + p2.feed_b_element(element_num, v.dl); + p3.feed_b_element(element_num, t1); + p4.feed_b_element(element_num, t2); + p5.feed_b_element(element_num, t3); + p6.feed_b_element(element_num, v.dr); + p7.feed_b_element(element_num, t4); + } + + void end_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + typename zbt::smaller_static_quadtree + t1 = zb.ur & zb.ul, + t2 = zb.dr & t1, + t3 = zb.dr & zb.ur, + t4 = zb.dl & t2; + p1.end_feeding_b_block(block_row, block_col, zb.ul); + p2.end_feeding_b_block(block_row, block_col, zb.dl); + p3.end_feeding_b_block(block_row, block_col, t1); + p4.end_feeding_b_block(block_row, block_col, t2); + p5.end_feeding_b_block(block_row, block_col, t3); + p6.end_feeding_b_block(block_row, block_col, zb.dr); + p7.end_feeding_b_block(block_row, block_col, t4); + } + + void multiply() + { + p1.multiply(); + p2.multiply(); + p3.multiply(); + p4.multiply(); + p5.multiply(); + p6.multiply(); + p7.multiply(); + } + + zbt begin_reading_block(const size_type& block_row, const size_type& block_col) + { + zbt r; + r.ur = r.ul = p1.begin_reading_block(block_row, block_col); + r.ul &= p2.begin_reading_block(block_row, block_col); + r.ur &= p4.begin_reading_block(block_row, block_col); + r.dr = r.dl = p5.begin_reading_block(block_row, block_col); + r.dl &= r.ur; + r.dl &= p7.begin_reading_block(block_row, block_col); + r.ur &= p3.begin_reading_block(block_row, block_col); + r.dr &= r.ur; + r.ur &= p6.begin_reading_block(block_row, block_col); + return r; + } + + vt read_element(int_type element_num) + { + vt r; + r.ur = r.ul = p1.read_element(element_num); + r.ul += p2.read_element(element_num); + r.ur += p4.read_element(element_num); + r.dr = r.dl = p5.read_element(element_num); + r.dl += r.ur; + r.dl += p7.read_element(element_num); + r.ur += p3.read_element(element_num); + r.dr += r.ur; + r.ur += p6.read_element(element_num); + return r; + } + + zbt end_reading_block(const size_type& block_row, const size_type& block_col) + { + zbt r; + r.ur = r.ul = p1.end_reading_block(block_row, block_col); + r.ul &= p2.end_reading_block(block_row, block_col); + r.ur &= p4.end_reading_block(block_row, block_col); + r.dr = r.dl = p5.end_reading_block(block_row, block_col); + r.dl &= r.ur; + r.dl &= p7.end_reading_block(block_row, block_col); + r.ur &= p3.end_reading_block(block_row, block_col); + r.dr &= r.ur; + r.ur &= p6.end_reading_block(block_row, block_col); + return r; + } +}; + +template +struct feedable_strassen_winograd +{ + typedef static_quadtree zbt; // true <=> is a zero-block + typedef static_quadtree vt; + + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename swappable_block_matrix_type::size_type size_type; + + swappable_block_matrix_type a, b, c; + const size_type n, m, l; + internal_block_type* iblock; + + feedable_strassen_winograd( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : a(existing_a, n, l, a_from_row, a_from_col), + b(existing_b, n, l, b_from_row, b_from_col), + c(bs_c, n, m), + n(n), m(m), l(l), + iblock(0) { } + + feedable_strassen_winograd( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : a(existing_a, n, l, a_from_row, a_from_col), + b(bs_c, n, l), + c(bs_c, n, m), + n(n), m(m), l(l), + iblock(0) { } + + feedable_strassen_winograd( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : a(bs_c, n, l), + b(existing_b, n, l, b_from_row, b_from_col), + c(bs_c, n, m), + n(n), m(m), l(l), + iblock(0) { } + + feedable_strassen_winograd( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : a(bs_c, n, l), + b(bs_c, n, l), + c(bs_c, n, m), + n(n), m(m), l(l), + iblock(0) { } + + void begin_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt) + { + if (! AExists) + iblock = &a.bs.acquire(a(block_row, block_col), true); + } + + void feed_a_element(const int_type element_num, const vt v) + { + if (! AExists) + (*iblock)[element_num] = v; + } + + void end_feeding_a_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + if (! AExists) + { + a.bs.release(a(block_row, block_col), ! zb); + iblock = 0; + } + } + + void begin_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt) + { + if (! BExists) + iblock = &b.bs.acquire(b(block_row, block_col), true); + } + + void feed_b_element(const int_type element_num, const vt v) + { + if (! BExists) + (*iblock)[element_num] = v; + } + + void end_feeding_b_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + if (! BExists) + { + b.bs.release(b(block_row, block_col), ! zb); + iblock = 0; + } + } + + void multiply() + { matrix_operations::choose_level_for_feedable_sw(a, b, c); } + + zbt begin_reading_block(const size_type& block_row, const size_type& block_col) + { + bool zb = ! c.bs.is_initialized(c(block_row, block_col)); + iblock = &c.bs.acquire(c(block_row, block_col)); + return zb; + } + + vt read_element(const int_type element_num) + { return (*iblock)[element_num]; } + + zbt end_reading_block(const size_type& block_row, const size_type& block_col) + { + c.bs.release(c(block_row, block_col), false); + iblock = 0; + return ! c.bs.is_initialized(c(block_row, block_col)); + } +}; + +template +struct matrix_to_quadtree +{ + typedef static_quadtree zbt; // true <=> is a zero-block + typedef static_quadtree vt; + + typedef matrix_to_quadtree smaller_matrix_to_quadtree; + + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename swappable_block_matrix_type::size_type size_type; + + smaller_matrix_to_quadtree ul, ur, dl, dr; + + matrix_to_quadtree(const swappable_block_matrix_type & matrix) + : ul(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, 0), + ur(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, matrix.get_width()/2), + dl(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, 0), + dr(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, matrix.get_width()/2) + { assert(! (matrix.get_height() % 2 | matrix.get_width() % 2)); } + + matrix_to_quadtree(const swappable_block_matrix_type & matrix, + const size_type height, const size_type width, const size_type from_row, const size_type from_col) + : ul(matrix, height/2, width/2, from_row, from_col), + ur(matrix, height/2, width/2, from_row, from_col + width/2), + dl(matrix, height/2, width/2, from_row + height/2, from_col), + dr(matrix, height/2, width/2, from_row + height/2, from_col + width/2) + { assert(! (height % 2 | width % 2)); } + + void begin_feeding_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + ul.begin_feeding_block(block_row, block_col, zb.ul); + ur.begin_feeding_block(block_row, block_col, zb.ur); + dl.begin_feeding_block(block_row, block_col, zb.dl); + dr.begin_feeding_block(block_row, block_col, zb.dr); + } + + void feed_element(const int_type element_num, const vt v) + { + ul.feed_element(element_num, v.ul); + ur.feed_element(element_num, v.ur); + dl.feed_element(element_num, v.dl); + dr.feed_element(element_num, v.dr); + } + + void feed_and_add_element(const int_type element_num, const vt v) + { + ul.feed_and_add_element(element_num, v.ul); + ur.feed_and_add_element(element_num, v.ur); + dl.feed_and_add_element(element_num, v.dl); + dr.feed_and_add_element(element_num, v.dr); + } + + void end_feeding_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + ul.end_feeding_block(block_row, block_col, zb.ul); + ur.end_feeding_block(block_row, block_col, zb.ur); + dl.end_feeding_block(block_row, block_col, zb.dl); + dr.end_feeding_block(block_row, block_col, zb.dr); + } + + zbt begin_reading_block(const size_type& block_row, const size_type& block_col) + { + zbt zb; + zb.ul = ul.begin_reading_block(block_row, block_col); + zb.ur = ur.begin_reading_block(block_row, block_col); + zb.dl = dl.begin_reading_block(block_row, block_col); + zb.dr = dr.begin_reading_block(block_row, block_col); + return zb; + } + + vt read_element(const int_type element_num) + { + vt v; + v.ul = ul.read_element(element_num); + v.ur = ur.read_element(element_num); + v.dl = dl.read_element(element_num); + v.dr = dr.read_element(element_num); + return v; + } + + zbt end_reading_block(const size_type& block_row, const size_type& block_col) + { + zbt zb; + zb.ul = ul.end_reading_block(block_row, block_col); + zb.ur = ur.end_reading_block(block_row, block_col); + zb.dl = dl.end_reading_block(block_row, block_col); + zb.dr = dr.end_reading_block(block_row, block_col); + return zb; + } + + const size_type & get_height_in_blocks() + { return ul.get_height_in_blocks(); } + + const size_type & get_width_in_blocks() + { return ul.get_width_in_blocks(); } +}; + +template +struct matrix_to_quadtree +{ + typedef static_quadtree zbt; // true <=> is a zero-block + typedef static_quadtree vt; + + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename swappable_block_matrix_type::size_type size_type; + + swappable_block_matrix_type m; + internal_block_type* iblock; + + matrix_to_quadtree(const swappable_block_matrix_type& matrix) + : m(matrix, matrix.get_height(), matrix.get_width(), 0, 0), + iblock(0) { } + + matrix_to_quadtree(const swappable_block_matrix_type& matrix, + const size_type height, const size_type width, const size_type from_row, const size_type from_col) + : m(matrix, height, width, from_row, from_col), + iblock(0) { } + + void begin_feeding_block(const size_type& block_row, const size_type& block_col, const zbt) + { iblock = &m.bs.acquire(m(block_row, block_col)); } + + void feed_element(const int_type element_num, const vt v) + { (*iblock)[element_num] = v; } + + void feed_and_add_element(const int_type element_num, const vt v) + { (*iblock)[element_num] += v; } + + void end_feeding_block(const size_type& block_row, const size_type& block_col, const zbt zb) + { + m.bs.release(m(block_row, block_col), ! zb); + iblock = 0; + } + + zbt begin_reading_block(const size_type& block_row, const size_type& block_col) + { + zbt zb = ! m.bs.is_initialized(m(block_row, block_col)); + iblock = &m.bs.acquire(m(block_row, block_col)); + return zb; + } + + vt read_element(const int_type element_num) + { return (*iblock)[element_num]; } + + zbt end_reading_block(const size_type& block_row, const size_type& block_col) + { + m.bs.release(m(block_row, block_col), false); + iblock = 0; + return ! m.bs.is_initialized(m(block_row, block_col)); + } + + const size_type & get_height_in_blocks() + { return m.get_height(); } + + const size_type & get_width_in_blocks() + { return m.get_width(); } +}; + +template +struct feedable_strassen_winograd_block_grained +{ + typedef static_quadtree zbt; // true <=> is a zero-block + typedef static_quadtree vt; + + typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_ab; + typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_a; + typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_b; + typedef feedable_strassen_winograd_block_grained smaller_feedable_strassen_winograd_n; + + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename swappable_block_matrix_type::size_type size_type; + typedef matrix_operations Ops; + + const size_type n, m, l; + smaller_feedable_strassen_winograd_ab p1, p2; + smaller_feedable_strassen_winograd_n p3, p4, p5; + smaller_feedable_strassen_winograd_b p6; + smaller_feedable_strassen_winograd_a p7; + + inline feedable_strassen_winograd_block_grained( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : n(n), m(m), l(l), + p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), + p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), + p3( bs_c, n/2, m/2, l/2), + p4( bs_c, n/2, m/2, l/2), + p5( bs_c, n/2, m/2, l/2), + p6( bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), + p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} + + inline feedable_strassen_winograd_block_grained( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : n(n), m(m), l(l), + p1(existing_a, a_from_row, a_from_col, bs_c, n/2, m/2, l/2), + p2(existing_a, a_from_row, a_from_col + l/2, bs_c, n/2, m/2, l/2), + p3( bs_c, n/2, m/2, l/2), + p4( bs_c, n/2, m/2, l/2), + p5( bs_c, n/2, m/2, l/2), + p6( bs_c, n/2, m/2, l/2), + p7(existing_a, a_from_row + n/2, a_from_col + l/2, bs_c, n/2, m/2, l/2) {} + + inline feedable_strassen_winograd_block_grained( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : n(n), m(m), l(l), + p1(bs_c, n/2, m/2, l/2, existing_b, b_from_row, b_from_col), + p2(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col), + p3(bs_c, n/2, m/2, l/2), + p4(bs_c, n/2, m/2, l/2), + p5(bs_c, n/2, m/2, l/2), + p6(bs_c, n/2, m/2, l/2, existing_b, b_from_row + l/2, b_from_col + m/2), + p7(bs_c, n/2, m/2, l/2) {} + + inline feedable_strassen_winograd_block_grained( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : n(n), m(m), l(l), + p1(bs_c, n / 2, m / 2, l / 2), + p2(bs_c, n / 2, m / 2, l / 2), + p3(bs_c, n / 2, m / 2, l / 2), + p4(bs_c, n / 2, m / 2, l / 2), + p5(bs_c, n / 2, m / 2, l / 2), + p6(bs_c, n / 2, m / 2, l / 2), + p7(bs_c, n / 2, m / 2, l / 2) { } + + inline void feed_a(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) + { + // partition bl + typename Ops::swappable_block_matrix_quarterer qbl(bl); + // preadditions + swappable_block_matrix_type + s1(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), + s2(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), + s3(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), + s4(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()); + Ops::strassen_winograd_preaddition_a(qbl.ul, qbl.ur, qbl.dl, qbl.dr, s1, s2, s3, s4); + // feed recursive + p1.feed_a(row, col, qbl.ul); + p2.feed_a(row, col, qbl.ur); + p3.feed_a(row, col, s1); + p4.feed_a(row, col, s2); + p5.feed_a(row, col, s3); + p6.feed_a(row, col, s4); + p7.feed_a(row, col, qbl.dr); + } + + inline void feed_b(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) + { + // partition bl + typename Ops::swappable_block_matrix_quarterer qbl(bl); + // preadditions + swappable_block_matrix_type + t1(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), + t2(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), + t3(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()), + t4(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()); + Ops::strassen_winograd_preaddition_b(qbl.ul, qbl.ur, qbl.dl, qbl.dr, t1, t2, t3, t4); + // feed recursive + p1.feed_b(row, col, qbl.ul); + p2.feed_b(row, col, qbl.dl); + p3.feed_b(row, col, t1); + p4.feed_b(row, col, t2); + p5.feed_b(row, col, t3); + p6.feed_b(row, col, qbl.dr); + p7.feed_b(row, col, t4); + } + + inline void multiply() + { + p1.multiply(); + p2.multiply(); + p3.multiply(); + p4.multiply(); + p5.multiply(); + p6.multiply(); + p7.multiply(); + } + + inline void read_and_add(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) + { + // partition bl + typename Ops::swappable_block_matrix_quarterer qbl(bl); + // postadditions + swappable_block_matrix_type px(bl.bs, qbl.ul.get_height(), qbl.ul.get_width(), qbl.ul.is_transposed()); + p2.read_and_add(row, col, qbl.ul); + p1.read_and_add(row, col, px); + Ops::element_op(qbl.ul, px, typename Ops::addition()); + p4.read_and_add(row, col, px); + Ops::element_op(qbl.ur, px, typename Ops::addition()); + p5.read_and_add(row, col, px); + Ops::element_op_twice_nontransposed(qbl.dl, qbl.dr, px, typename Ops::addition()); + px.set_zero(); + p7.read_and_add(row, col, qbl.dl); + p3.read_and_add(row, col, px); + Ops::element_op_twice_nontransposed(qbl.dr, qbl.ur, px, typename Ops::addition()); + p6.read_and_add(row, col, qbl.ur); + } + + inline static unsigned_type get_num_temp_grains() + { return smaller_feedable_strassen_winograd_ab::get_num_temp_grains() + (4 ^ Level) * 2; } +}; + +template +struct feedable_strassen_winograd_block_grained +{ + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename swappable_block_matrix_type::swappable_block_identifier_type swappable_block_identifier_type; + typedef typename swappable_block_matrix_type::size_type size_type; + typedef matrix_operations Ops; + + typedef static_quadtree bt; + + swappable_block_matrix_type a, b, c; + + inline feedable_strassen_winograd_block_grained( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : a(existing_a, n, l, a_from_row, a_from_col), + b(existing_b, n, l, b_from_row, b_from_col), + c(bs_c, n, m) { } + + inline feedable_strassen_winograd_block_grained( + const swappable_block_matrix_type& existing_a, const size_type a_from_row, const size_type a_from_col, + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : a(existing_a, n, l, a_from_row, a_from_col), + b(bs_c, n, l), + c(bs_c, n, m) { } + + inline feedable_strassen_winograd_block_grained( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l, + const swappable_block_matrix_type& existing_b, const size_type b_from_row, const size_type b_from_col) + : a(bs_c, n, l), + b(existing_b, n, l, b_from_row, b_from_col), + c(bs_c, n, m) { } + + inline feedable_strassen_winograd_block_grained( + block_scheduler_type& bs_c, const size_type n, const size_type m, const size_type l) + : a(bs_c, n, l), + b(bs_c, n, l), + c(bs_c, n, m) { } + + inline void feed_a(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) + { + if (! AExists) + { + // copy bl to a from (row, col) (assuming a from (row, col) == 0) + swappable_block_matrix_type at(a, bl.get_height(), bl.get_width(), row, col); + Ops::element_op(at, bl, typename Ops::addition()); + } + } + + inline void feed_b(const size_type& row, const size_type& col, const swappable_block_matrix_type& bl) + { + if (! BExists) + { + // copy bl(0,0) to b(row, col) (assuming b from (row, col) == 0) + swappable_block_matrix_type bt(b, bl.get_height(), bl.get_width(), row, col); + Ops::element_op(bt, bl, typename Ops::addition()); + } + } + + inline void multiply() + { + matrix_operations:: + multi_level_strassen_winograd_multiply_and_add_block_grained(a, b, c); + if (! AExists) + a.set_zero(); + if (! BExists) + b.set_zero(); + } + + + inline void read_and_add(const size_type& row, const size_type& col, swappable_block_matrix_type& bl) + { + // add c from (row, col) to bl + swappable_block_matrix_type ct(c, bl.get_height(), bl.get_width(), row, col); + Ops::element_op(bl, ct, typename Ops::addition()); + ct.set_zero(); + } + + inline static unsigned_type get_num_temp_grains() + { return 0; } +}; + +template +struct matrix_to_quadtree_block_grained +{ + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::size_type size_type; + + typedef matrix_to_quadtree_block_grained smaller_matrix_to_quadtree_block_grained; + + smaller_matrix_to_quadtree_block_grained ul, ur, dl, dr; + + inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type & matrix) + : ul(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, 0), + ur(matrix, matrix.get_height()/2, matrix.get_width()/2, 0, matrix.get_width()/2), + dl(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, 0), + dr(matrix, matrix.get_height()/2, matrix.get_width()/2, matrix.get_height()/2, matrix.get_width()/2) + { assert(! (matrix.get_height() % 2 | matrix.get_width() % 2)); } + + inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type & matrix, + const size_type height, const size_type width, const size_type from_row, const size_type from_col) + : ul(matrix, height/2, width/2, from_row, from_col), + ur(matrix, height/2, width/2, from_row, from_col + width/2), + dl(matrix, height/2, width/2, from_row + height/2, from_col), + dr(matrix, height/2, width/2, from_row + height/2, from_col + width/2) + { assert(! (height % 2 | width % 2)); } + + inline swappable_block_matrix_type operator () (const size_type& row, const size_type& col) + { + return swappable_block_matrix_type(ul(row, col), ur(row, col), dl(row, col), dr(row, col)); + } + + inline const size_type get_height() + { return ul.get_height(); } + + inline const size_type get_width() + { return ul.get_width(); } +}; + +template +struct matrix_to_quadtree_block_grained +{ + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::size_type size_type; + + swappable_block_matrix_type m; + + inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type& matrix) + : m(matrix, matrix.get_height(), matrix.get_width(), 0, 0) + { assert(! (matrix.get_height() % Granularity | matrix.get_width() % Granularity)); } + + inline matrix_to_quadtree_block_grained(const swappable_block_matrix_type& matrix, + const size_type height, const size_type width, const size_type from_row, const size_type from_col) + : m(matrix, height, width, from_row, from_col) + { assert(! (matrix.get_height() % Granularity | matrix.get_width() % Granularity)); } + + inline swappable_block_matrix_type operator () (const size_type& row, const size_type& col) + { + return swappable_block_matrix_type(m, Granularity, Granularity, row * Granularity, col * Granularity); + } + + inline const size_type get_height() + { return m.get_height() / Granularity; } + + inline const size_type get_width() + { return m.get_width() / Granularity; } +}; + +template +struct matrix_operations +{ + // tuning-parameter: Only matrices larger than this (in blocks) are processed by Strassen-Winograd. + // you have to adapt choose_level_for_feedable_sw, too + static const int_type strassen_winograd_base_case_size; + + typedef swappable_block_matrix swappable_block_matrix_type; + typedef typename swappable_block_matrix_type::block_scheduler_type block_scheduler_type; + typedef typename swappable_block_matrix_type::swappable_block_identifier_type swappable_block_identifier_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename swappable_block_matrix_type::size_type size_type; + typedef column_vector column_vector_type; + typedef row_vector row_vector_type; + typedef typename column_vector_type::size_type vector_size_type; + + // +-+-+-+ addition +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + struct addition + { + /* op(c,a,b) means c = a b e.g. assign sum + * op(c,a) means c = a e.g. add up + * op(a) means a e.g. sign + * + * it should hold: + * op(c,0,0) equivalent c = 0 + * op(c=0,a) equivalent c = op(a) + * op(c,0) equivalent {} + */ + + inline ValueType& operator () (ValueType& c, const ValueType& a, const ValueType& b) { return c = a + b; } + inline ValueType& operator () (ValueType& c, const ValueType& a) { return c += a; } + inline ValueType operator () (const ValueType& a) { return +a; } + }; + + struct subtraction + { + inline ValueType& operator () (ValueType& c, const ValueType& a, const ValueType& b) { return c = a - b; } + inline ValueType& operator () (ValueType& c, const ValueType& a) { return c -= a; } + inline ValueType operator () (const ValueType& a) { return -a; } + }; + + struct scalar_multiplication + { + inline scalar_multiplication(const ValueType scalar = 1) : s(scalar) { } + inline ValueType& operator () (ValueType& c, const ValueType& a) { return c = a * s; } + inline ValueType operator () (const ValueType& a) { return a * s; } + inline operator const ValueType& () { return s; } + const ValueType s; + }; + + // element_op(C,A,B) calculates C = A B + template + static swappable_block_matrix_type& + element_op(swappable_block_matrix_type& C, + const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, Op op = Op()) + { + for (size_type row = 0; row < C.get_height(); ++row) + for (size_type col = 0; col < C.get_width(); ++col) + element_op_swappable_block( + C(row, col), C.is_transposed(), C.bs, + A(row, col), A.is_transposed(), A.bs, + B(row, col), B.is_transposed(), B.bs, op); + return C; + } + + // element_op(C,A) calculates C = A + template + static swappable_block_matrix_type& + element_op(swappable_block_matrix_type& C, + const swappable_block_matrix_type& A, Op op = Op()) + { + for (size_type row = 0; row < C.get_height(); ++row) + for (size_type col = 0; col < C.get_width(); ++col) + element_op_swappable_block( + C(row, col), C.is_transposed(), C.bs, + A(row, col), A.is_transposed(), A.bs, op); + return C; + } + + // element_op(C) calculates C = C + template + static swappable_block_matrix_type& + element_op(swappable_block_matrix_type& C, Op op = Op()) + { + for (size_type row = 0; row < C.get_height(); ++row) + for (size_type col = 0; col < C.get_width(); ++col) + element_op_swappable_block( + C(row, col), C.bs, op); + return C; + } + + // calculates c = a b + template + static void + element_op_swappable_block( + const swappable_block_identifier_type c, const bool c_is_transposed, block_scheduler_type& bs_c, + const swappable_block_identifier_type a, bool a_is_transposed, block_scheduler_type& bs_a, + const swappable_block_identifier_type b, bool b_is_transposed, block_scheduler_type& bs_b, Op op = Op()) + { + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_addition_calls; + // check if zero-block (== ! initialized) + if (! bs_a.is_initialized(a) && ! bs_b.is_initialized(b)) + { + // => a and b are zero -> set c zero + bs_c.deinitialize(c); + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_additions_saved_through_zero; + return; + } + a_is_transposed = a_is_transposed != c_is_transposed; + b_is_transposed = b_is_transposed != c_is_transposed; + if (! bs_a.is_initialized(a)) + { + // a is zero -> copy b + internal_block_type& ic = bs_c.acquire(c, true), + & ib = bs_b.acquire(b); + if (! bs_c.is_simulating()) + { + if (b_is_transposed) + low_level_matrix_binary_ass_op(&ic[0], 0, &ib[0], op); + else + low_level_matrix_binary_ass_op(&ic[0], 0, &ib[0], op); + } + bs_b.release(b, false); + bs_c.release(c, true); + } + else if (! bs_b.is_initialized(b)) + { + // b is zero -> copy a + internal_block_type& ic = bs_c.acquire(c, true), + & ia = bs_a.acquire(a); + if (! bs_c.is_simulating()) + { + if (a_is_transposed) + low_level_matrix_binary_ass_op(&ic[0], &ia[0], 0, op); + else + low_level_matrix_binary_ass_op(&ic[0], &ia[0], 0, op); + } + bs_a.release(a, false); + bs_c.release(c, true); + } + else + { + internal_block_type& ic = bs_c.acquire(c, true), + & ia = bs_a.acquire(a), + & ib = bs_b.acquire(b); + if (! bs_c.is_simulating()) + { + if (a_is_transposed) + { + if (b_is_transposed) + low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); + else + low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); + } + else + { + if (b_is_transposed) + low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); + else + low_level_matrix_binary_ass_op(&ic[0], &ia[0], &ib[0], op); + } + } + bs_a.release(a, false); + bs_b.release(b, false); + bs_c.release(c, true); + } + } + + // calculates c = a + template + static void + element_op_swappable_block( + const swappable_block_identifier_type c, const bool c_is_transposed, block_scheduler_type& bs_c, + const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, Op op = Op()) + { + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_addition_calls; + // check if zero-block (== ! initialized) + if (! bs_a.is_initialized(a)) + { + // => b is zero => nothing to do + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_additions_saved_through_zero; + return; + } + const bool c_is_zero = ! bs_c.is_initialized(c); + // acquire + internal_block_type& ic = bs_c.acquire(c, c_is_zero), + & ia = bs_a.acquire(a); + // add + if (! bs_c.is_simulating()) + { + if (c_is_zero) { + if (c_is_transposed == a_is_transposed) + low_level_matrix_unary_op(&ic[0], &ia[0], op); + else + low_level_matrix_unary_op(&ic[0], &ia[0], op); + } + else { + if (c_is_transposed == a_is_transposed) + low_level_matrix_unary_ass_op(&ic[0], &ia[0], op); + else + low_level_matrix_unary_ass_op(&ic[0], &ia[0], op); + } + } + // release + bs_c.release(c, true); + bs_a.release(a, false); + } + + // calculates c = c + template + static void + element_op_swappable_block( + const swappable_block_identifier_type c, block_scheduler_type& bs_c, Op op = Op()) + { + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_addition_calls; + // check if zero-block (== ! initialized) + if (! bs_c.is_initialized(c)) + { + // => c is zero => nothing to do + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_additions_saved_through_zero; + return; + } + // acquire + internal_block_type& ic = bs_c.acquire(c); + // add + if (! bs_c.is_simulating()) + low_level_matrix_unary_op(&ic[0], &ic[0], op); + // release + bs_c.release(c, true); + } + + // additions for strassen-winograd + + inline static void + strassen_winograd_preaddition_a(swappable_block_matrix_type& a11, + swappable_block_matrix_type& a12, + swappable_block_matrix_type& a21, + swappable_block_matrix_type& a22, + swappable_block_matrix_type& s1, + swappable_block_matrix_type& s2, + swappable_block_matrix_type& s3, + swappable_block_matrix_type& s4) + { + for (size_type row = 0; row < a11.get_height(); ++row) + for (size_type col = 0; col < a11.get_width(); ++col) + { + op_swappable_block_nontransposed(s3, a11, subtraction(), a21, row, col); + op_swappable_block_nontransposed(s1, a21, addition(), a22, row, col); + op_swappable_block_nontransposed(s2, s1, subtraction(), a11, row, col); + op_swappable_block_nontransposed(s4, a12, subtraction(), s2, row, col); + } + } + + inline static void + strassen_winograd_preaddition_b(swappable_block_matrix_type& b11, + swappable_block_matrix_type& b12, + swappable_block_matrix_type& b21, + swappable_block_matrix_type& b22, + swappable_block_matrix_type& t1, + swappable_block_matrix_type& t2, + swappable_block_matrix_type& t3, + swappable_block_matrix_type& t4) + { + for (size_type row = 0; row < b11.get_height(); ++row) + for (size_type col = 0; col < b11.get_width(); ++col) + { + op_swappable_block_nontransposed(t3, b22, subtraction(), b12, row, col); + op_swappable_block_nontransposed(t1, b12, subtraction(), b11, row, col); + op_swappable_block_nontransposed(t2, b22, subtraction(), t1, row, col); + op_swappable_block_nontransposed(t4, b21, subtraction(), t2, row, col); + } + } + + inline static void + strassen_winograd_postaddition(swappable_block_matrix_type& c11, // = p2 + swappable_block_matrix_type& c12, // = p6 + swappable_block_matrix_type& c21, // = p7 + swappable_block_matrix_type& c22, // = p4 + swappable_block_matrix_type& p1, + swappable_block_matrix_type& p3, + swappable_block_matrix_type& p5) + { + for (size_type row = 0; row < c11.get_height(); ++row) + for (size_type col = 0; col < c11.get_width(); ++col) + { + op_swappable_block_nontransposed(c11, addition(), p1, row, col); // (u1) + op_swappable_block_nontransposed( p1, addition(), c22, row, col); // (u2) + op_swappable_block_nontransposed( p5, addition(), p1, row, col); // (u3) + op_swappable_block_nontransposed(c21, addition(), p5, row, col); // (u4) + op_swappable_block_nontransposed(c22, p5, addition(), p3, row, col); // (u5) + op_swappable_block_nontransposed( p1, addition(), p3, row, col); // (u6) + op_swappable_block_nontransposed(c12, addition(), p1, row, col); // (u7) + } + } + + // calculates c1 += a; c2 += a + template + inline static void + element_op_twice_nontransposed(swappable_block_matrix_type& c1, + swappable_block_matrix_type& c2, + const swappable_block_matrix_type& a, Op op = Op()) + { + for (size_type row = 0; row < a.get_height(); ++row) + for (size_type col = 0; col < a.get_width(); ++col) + { + element_op_swappable_block( + c1(row, col), false, c1.bs, + a(row, col), false, a.bs, op); + element_op_swappable_block( + c2(row, col), false, c2.bs, + a(row, col), false, a.bs, op); + } + } + + template + inline static void + op_swappable_block_nontransposed(swappable_block_matrix_type& c, + swappable_block_matrix_type& a, Op op, swappable_block_matrix_type& b, + size_type& row, size_type& col) + { + element_op_swappable_block( + c(row, col), false, c.bs, + a(row, col), false, a.bs, + b(row, col), false, b.bs, op); + } + + template + inline static void + op_swappable_block_nontransposed(swappable_block_matrix_type& c, Op op, swappable_block_matrix_type& a, + size_type& row, size_type& col) + { + element_op_swappable_block( + c(row, col), false, c.bs, + a(row, col), false, a.bs, op); + } + + // +-+ end addition +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // +-+-+-+ matrix multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + /* n, m and l denote the three dimensions of a matrix multiplication, according to the following ascii-art diagram: + * + * +--m--+ + * +----l-----+ | | +--m--+ + * | | | | | | + * n A | • l B | = n C | + * | | | | | | + * +----------+ | | +-----+ + * +-----+ + * + * The index-variables are called i, j, k for dimension + * n, m, l . + */ + + // requires height and width divisible by 2 + struct swappable_block_matrix_quarterer + { + swappable_block_matrix_type upleft, upright, + downleft, downright, + & ul, & ur, & dl, & dr; + + swappable_block_matrix_quarterer(const swappable_block_matrix_type & whole) + : upleft (whole, whole.get_height()/2, whole.get_width()/2, 0, 0), + upright (whole, whole.get_height()/2, whole.get_width()/2, 0, whole.get_width()/2), + downleft (whole, whole.get_height()/2, whole.get_width()/2, whole.get_height()/2, 0), + downright(whole, whole.get_height()/2, whole.get_width()/2, whole.get_height()/2, whole.get_width()/2), + ul(upleft), ur(upright), dl(downleft), dr(downright) + { assert(! (whole.get_height() % 2 | whole.get_width() % 2)); } + }; + + struct swappable_block_matrix_padding_quarterer + { + swappable_block_matrix_type upleft, upright, + downleft, downright, + & ul, & ur, & dl, & dr; + + swappable_block_matrix_padding_quarterer(const swappable_block_matrix_type & whole) + : upleft (whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), 0, 0), + upright (whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), 0, div_ceil(whole.get_width(),2)), + downleft (whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), div_ceil(whole.get_height(),2), 0), + downright(whole, div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2), div_ceil(whole.get_height(),2), div_ceil(whole.get_width(),2)), + ul(upleft), ur(upright), dl(downleft), dr(downright) {} + }; + + struct swappable_block_matrix_approximative_quarterer + { + swappable_block_matrix_type upleft, upright, + downleft, downright, + & ul, & ur, & dl, & dr; + + swappable_block_matrix_approximative_quarterer(const swappable_block_matrix_type & whole) + : upleft (whole, whole.get_height()/2, whole.get_width()/2, 0, 0), + upright (whole, whole.get_height()/2, whole.get_width() - whole.get_width()/2, 0, whole.get_width()/2), + downleft (whole, whole.get_height() - whole.get_height()/2, whole.get_width()/2, whole.get_height()/2, 0), + downright(whole, whole.get_height() - whole.get_height()/2, whole.get_width() - whole.get_width()/2, whole.get_height()/2, whole.get_width()/2), + ul(upleft), ur(upright), dl(downleft), dr(downright) {} + }; + + //! calculates C = A * B + C + // requires fitting dimensions + static swappable_block_matrix_type& + multi_level_strassen_winograd_multiply_and_add_block_grained(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + int_type num_levels = ilog2_ceil(std::min(A.get_width(), std::min(C.get_width(), C.get_height()))); + if (num_levels > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) + { + if (num_levels > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS) + num_levels = STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS; + swappable_block_matrix_type padded_a(A, round_up_to_power_of_two(A.get_height(), num_levels), + round_up_to_power_of_two(A.get_width(), num_levels), 0, 0), + padded_b(B, round_up_to_power_of_two(B.get_height(), num_levels), + round_up_to_power_of_two(B.get_width(), num_levels), 0, 0), + padded_c(C, round_up_to_power_of_two(C.get_height(), num_levels), + round_up_to_power_of_two(C.get_width(), num_levels), 0, 0); + switch (num_levels) + { + #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 5 && 5 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) + case 5: + use_feedable_sw_block_grained<5>(padded_a, padded_a, padded_c); + break; + #endif + #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 4 && 4 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) + case 4: + use_feedable_sw_block_grained<4>(padded_a, padded_a, padded_c); + break; + #endif + #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 3 && 3 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) + case 3: + use_feedable_sw_block_grained<3>(padded_a, padded_a, padded_c); + break; + #endif + #if (STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_MAX_NUM_LEVELS >= 2 && 2 > STXXL_MATRIX_MULTI_LEVEL_STRASSEN_WINOGRAD_BASE_CASE) + case 2: + use_feedable_sw_block_grained<2>(padded_a, padded_a, padded_c); + break; + #endif + default: // only here in case of wrong bounds + strassen_winograd_multiply_and_add_interleaved(A, B, C); + break; + } + } + else + // base case + strassen_winograd_multiply_and_add_interleaved(A, B, C); + return C; + } + + // input matrices have to be padded + template + static void use_feedable_sw_block_grained(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + const unsigned granularity = 1; + + feedable_strassen_winograd_block_grained + fsw(A, 0, 0, C.bs, C.get_height(), C.get_width(), A.get_width(), B, 0, 0); + // preadditions for A + { + matrix_to_quadtree_block_grained + mtq_a(A); + for (size_type row = 0; row < mtq_a.get_height(); ++row) + for (size_type col = 0; col < mtq_a.get_width(); ++col) + fsw.feed_a(row, col, mtq_a(row, col)); + } + // preadditions for B + { + matrix_to_quadtree_block_grained + mtq_b(B); + for (size_type row = 0; row < mtq_b.get_height(); ++row) + for (size_type col = 0; col < mtq_b.get_width(); ++col) + fsw.feed_b(row, col, mtq_b(row, col)); + } + // recursive multiplications + fsw.multiply(); + // postadditions + { + matrix_to_quadtree_block_grained + mtq_c(C); + for (size_type row = 0; row < mtq_c.get_height(); ++row) + for (size_type col = 0; col < mtq_c.get_width(); ++col) + fsw.read_and_add(row, col, mtq_c(row, col)); + } + } + + //! calculates C = A * B + C + // requires fitting dimensions + static swappable_block_matrix_type& + multi_level_strassen_winograd_multiply_and_add(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + int_type p = ilog2_ceil(std::min(A.get_width(), std::min(C.get_width(), C.get_height()))); + + swappable_block_matrix_type padded_a(A, round_up_to_power_of_two(A.get_height(), p), + round_up_to_power_of_two(A.get_width(), p), 0, 0), + padded_b(B, round_up_to_power_of_two(B.get_height(), p), + round_up_to_power_of_two(B.get_width(), p), 0, 0), + padded_c(C, round_up_to_power_of_two(C.get_height(), p), + round_up_to_power_of_two(C.get_width(), p), 0, 0); + choose_level_for_feedable_sw(padded_a, padded_b, padded_c); + return C; + } + + // input matrices have to be padded + static void choose_level_for_feedable_sw(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + switch (ilog2_ceil(std::min(A.get_width(), std::min(C.get_width(), C.get_height())))) + { + default: + /* + use_feedable_sw<4>(A, B, C); + break; + case 3: + use_feedable_sw<3>(A, B, C); + break; + case 2:*/ + use_feedable_sw<2>(A, B, C); + break; + case 1: + /*use_feedable_sw<1>(A, B, C); + break;*/ + case 0: + // base case + recursive_multiply_and_add(A, B, C); + break; + } + } + + // input matrices have to be padded + template + static void use_feedable_sw(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + feedable_strassen_winograd + fsw(A, 0, 0, C.bs, C.get_height(), C.get_width(), A.get_width(), B, 0, 0); + // preadditions for A + matrix_to_quadtree + mtq_a(A); + for (size_type block_row = 0; block_row < mtq_a.get_height_in_blocks(); ++block_row) + for (size_type block_col = 0; block_col < mtq_a.get_width_in_blocks(); ++block_col) + { + fsw.begin_feeding_a_block(block_row, block_col, + mtq_a.begin_reading_block(block_row, block_col)); + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type element_row_in_block = 0; element_row_in_block < int_type(BlockSideLength); ++element_row_in_block) + for (int_type element_col_in_block = 0; element_col_in_block < int_type(BlockSideLength); ++element_col_in_block) + fsw.feed_a_element(element_row_in_block * BlockSideLength + element_col_in_block, + mtq_a.read_element(element_row_in_block * BlockSideLength + element_col_in_block)); + fsw.end_feeding_a_block(block_row, block_col, + mtq_a.end_reading_block(block_row, block_col)); + } + // preadditions for B + matrix_to_quadtree + mtq_b(B); + for (size_type block_row = 0; block_row < mtq_b.get_height_in_blocks(); ++block_row) + for (size_type block_col = 0; block_col < mtq_b.get_width_in_blocks(); ++block_col) + { + fsw.begin_feeding_b_block(block_row, block_col, + mtq_b.begin_reading_block(block_row, block_col)); + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type element_row_in_block = 0; element_row_in_block < int_type(BlockSideLength); ++element_row_in_block) + for (int_type element_col_in_block = 0; element_col_in_block < int_type(BlockSideLength); ++element_col_in_block) + fsw.feed_b_element(element_row_in_block * BlockSideLength + element_col_in_block, + mtq_b.read_element(element_row_in_block * BlockSideLength + element_col_in_block)); + fsw.end_feeding_b_block(block_row, block_col, + mtq_b.end_reading_block(block_row, block_col)); + } + // recursive multiplications + fsw.multiply(); + // postadditions + matrix_to_quadtree + mtq_c(C); + for (size_type block_row = 0; block_row < mtq_c.get_height_in_blocks(); ++block_row) + for (size_type block_col = 0; block_col < mtq_c.get_width_in_blocks(); ++block_col) + { + mtq_c.begin_feeding_block(block_row, block_col, + fsw.begin_reading_block(block_row, block_col)); + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type element_row_in_block = 0; element_row_in_block < int_type(BlockSideLength); ++element_row_in_block) + for (int_type element_col_in_block = 0; element_col_in_block < int_type(BlockSideLength); ++element_col_in_block) + mtq_c.feed_and_add_element(element_row_in_block * BlockSideLength + element_col_in_block, + fsw.read_element(element_row_in_block * BlockSideLength + element_col_in_block)); + mtq_c.end_feeding_block(block_row, block_col, + fsw.end_reading_block(block_row, block_col)); + } + } + + //! calculates C = A * B + // assumes fitting dimensions + static swappable_block_matrix_type& + strassen_winograd_multiply(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + // base case + if (C.get_height() <= strassen_winograd_base_case_size + || C.get_width() <= strassen_winograd_base_case_size + || A.get_width() <= strassen_winograd_base_case_size) + { + C.set_zero(); + return recursive_multiply_and_add(A, B, C); + } + + // partition matrix + swappable_block_matrix_padding_quarterer qa(A), qb(B), qc(C); + // preadditions + swappable_block_matrix_type s1(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + s2(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + s3(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + s4(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + t1(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), + t2(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), + t3(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), + t4(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()); + strassen_winograd_preaddition_a(qa.ul, qa.ur, qa.dl, qa.dr, s1, s2, s3, s4); + strassen_winograd_preaddition_b(qb.ul, qb.ur, qb.dl, qb.dr, t1, t2, t3, t4); + // recursive multiplications + swappable_block_matrix_type p1(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()), + // p2 stored in qc.ul + p3(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()), + // p4 stored in qc.dr + p5(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()); + // p6 stored in qc.ur + // p7 stored in qc.dl + strassen_winograd_multiply(qa.ul, qb.ul, p1); + strassen_winograd_multiply(qa.ur, qb.dl, qc.ul); + strassen_winograd_multiply( s1, t1, p3); + strassen_winograd_multiply( s2, t2, qc.dr); + strassen_winograd_multiply( s3, t3, p5); + strassen_winograd_multiply( s4, qb.dr, qc.ur); + strassen_winograd_multiply(qa.dr, t4, qc.dl); + // postadditions + strassen_winograd_postaddition(qc.ul, qc.ur, qc.dl, qc.dr, p1, p3, p5); + return C; + } + + //! calculates C = A * B + C + // assumes fitting dimensions + static swappable_block_matrix_type& + strassen_winograd_multiply_and_add_interleaved(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + // base case + if (C.get_height() <= strassen_winograd_base_case_size + || C.get_width() <= strassen_winograd_base_case_size + || A.get_width() <= strassen_winograd_base_case_size) + return recursive_multiply_and_add(A, B, C); + + // partition matrix + swappable_block_matrix_padding_quarterer qa(A), qb(B), qc(C); + // preadditions + swappable_block_matrix_type s1(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + s2(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + s3(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + s4(C.bs, qa.ul.get_height(), qa.ul.get_width(), qa.ul.is_transposed()), + t1(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), + t2(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), + t3(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()), + t4(C.bs, qb.ul.get_height(), qb.ul.get_width(), qb.ul.is_transposed()); + strassen_winograd_preaddition_a(qa.ul, qa.ur, qa.dl, qa.dr, s1, s2, s3, s4); + strassen_winograd_preaddition_b(qb.ul, qb.ur, qb.dl, qb.dr, t1, t2, t3, t4); + // recursive multiplications and postadditions + swappable_block_matrix_type px(C.bs, qc.ul.get_height(), qc.ul.get_width(), qc.ul.is_transposed()); + strassen_winograd_multiply_and_add_interleaved(qa.ur, qb.dl, qc.ul); // p2 + strassen_winograd_multiply_and_add_interleaved(qa.ul, qb.ul, px); // p1 + element_op(qc.ul, px); + strassen_winograd_multiply_and_add_interleaved(s2, t2, px); // p4 + s2.set_zero(); + t2.set_zero(); + element_op(qc.ur, px); + strassen_winograd_multiply_and_add_interleaved(s3, t3, px); // p5 + s3.set_zero(); + t3.set_zero(); + element_op_twice_nontransposed(qc.dl, qc.dr, px); + px.set_zero(); + strassen_winograd_multiply_and_add_interleaved(qa.dr, t4, qc.dl); // p7 + t4.set_zero(); + strassen_winograd_multiply_and_add_interleaved(s1, t1, px); // p3 + s1.set_zero(); + t1.set_zero(); + element_op_twice_nontransposed(qc.dr, qc.ur, px); + px.set_zero(); + strassen_winograd_multiply_and_add_interleaved(s4, qb.dr, qc.ur); // p6 + return C; + } + + //! calculates C = A * B + C + // assumes fitting dimensions + static swappable_block_matrix_type& + strassen_winograd_multiply_and_add(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + // base case + if (C.get_height() <= strassen_winograd_base_case_size + || C.get_width() <= strassen_winograd_base_case_size + || A.get_width() <= strassen_winograd_base_case_size) + return recursive_multiply_and_add(A, B, C); + + // partition matrix + swappable_block_matrix_padding_quarterer qa(A), qb(B), qc(C); + // preadditions + swappable_block_matrix_type s1(C.bs, qa.ul.get_height(), qa.ul.get_width()), + s2(C.bs, qa.ul.get_height(), qa.ul.get_width()), + s3(C.bs, qa.ul.get_height(), qa.ul.get_width()), + s4(C.bs, qa.ul.get_height(), qa.ul.get_width()), + t1(C.bs, qb.ul.get_height(), qb.ul.get_width()), + t2(C.bs, qb.ul.get_height(), qb.ul.get_width()), + t3(C.bs, qb.ul.get_height(), qb.ul.get_width()), + t4(C.bs, qb.ul.get_height(), qb.ul.get_width()); + element_op(s3, qa.ul, qa.dl); + element_op(s1, qa.dl, qa.dr); + element_op(s2, s1, qa.ul); + element_op(s4, qa.ur, s2); + element_op(t3, qb.dr, qb.ur); + element_op(t1, qb.ur, qb.ul); + element_op(t2, qb.dr, t1); + element_op(t4, qb.dl, t2); + // recursive multiplications and postadditions + swappable_block_matrix_type px(C.bs, qc.ul.get_height(), qc.ul.get_width()); + strassen_winograd_multiply_and_add(qa.ur, qb.dl, qc.ul); // p2 + strassen_winograd_multiply_and_add(qa.ul, qb.ul, px); // p1 + element_op(qc.ul, px); + strassen_winograd_multiply_and_add(s2, t2, px); // p4 + element_op(qc.ur, px); + strassen_winograd_multiply_and_add(s3, t3, px); // p5 + element_op(qc.dl, px); + element_op(qc.dr, px); + px.set_zero(); + strassen_winograd_multiply_and_add(qa.dr, t4, qc.dl); // p7 + strassen_winograd_multiply_and_add(s1, t1, px); // p3 + element_op(qc.dr, px); + element_op(qc.ur, px); + strassen_winograd_multiply_and_add(s4, qb.dr, qc.ur); // p6 + return C; + } + + //! calculates C = A * B + C + // assumes fitting dimensions + static swappable_block_matrix_type& + recursive_multiply_and_add(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + // catch empty intervals + if (C.get_height() * C.get_width() * A.get_width() == 0) + return C; + // base case + if ((C.get_height() == 1) + (C.get_width() == 1) + (A.get_width() == 1) >= 2) + return naive_multiply_and_add(A, B, C); + + // partition matrix + swappable_block_matrix_approximative_quarterer qa(A), qb(B), qc(C); + // recursive multiplication + // The order of recursive calls is optimized to enhance locality. C has priority because it has to be read and written. + recursive_multiply_and_add(qa.ul, qb.ul, qc.ul); + recursive_multiply_and_add(qa.ur, qb.dl, qc.ul); + recursive_multiply_and_add(qa.ur, qb.dr, qc.ur); + recursive_multiply_and_add(qa.ul, qb.ur, qc.ur); + recursive_multiply_and_add(qa.dl, qb.ur, qc.dr); + recursive_multiply_and_add(qa.dr, qb.dr, qc.dr); + recursive_multiply_and_add(qa.dr, qb.dl, qc.dl); + recursive_multiply_and_add(qa.dl, qb.ul, qc.dl); + + return C; + } + + //! calculates C = A * B + C + // requires fitting dimensions + static swappable_block_matrix_type& + naive_multiply_and_add(const swappable_block_matrix_type& A, + const swappable_block_matrix_type& B, + swappable_block_matrix_type& C) + { + const size_type& n = C.get_height(), + & m = C.get_width(), + & l = A.get_width(); + for (size_type i = 0; i < n; ++i) + for (size_type j = 0; j < m; ++j) + for (size_type k = 0; k < l; ++k) + multiply_and_add_swappable_block(A(i, k), A.is_transposed(), A.bs, + B(k, j), B.is_transposed(), B.bs, + C(i, j), C.is_transposed(), C.bs); + return C; + } + + static void multiply_and_add_swappable_block( + const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, + const swappable_block_identifier_type b, const bool b_is_transposed, block_scheduler_type& bs_b, + const swappable_block_identifier_type c, const bool c_is_transposed, block_scheduler_type& bs_c) + { + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_multiplication_calls; + // check if zero-block (== ! initialized) + if (! bs_a.is_initialized(a) || ! bs_b.is_initialized(b)) + { + // => one factor is zero => product is zero + if (! bs_c.is_simulating()) + ++matrix_operation_statistic::get_instance()->block_multiplications_saved_through_zero; + return; + } + // acquire + ValueType* ap = bs_a.acquire(a).begin(), + * bp = bs_b.acquire(b).begin(), + * cp = bs_c.acquire(c).begin(); + // multiply + if (! bs_c.is_simulating()) + low_level_matrix_multiply_and_add + (ap, a_is_transposed, bp, b_is_transposed, cp, c_is_transposed); + // release + bs_a.release(a, false); + bs_b.release(b, false); + bs_c.release(c, true); + } + + // +-+ end matrix multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // +-+-+-+ matrix-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + //! calculates z = A * x + static column_vector_type& + recursive_matrix_col_vector_multiply_and_add(const swappable_block_matrix_type& A, + const column_vector_type& x, column_vector_type& z, + const vector_size_type offset_x = 0, const vector_size_type offset_z = 0) + { + // catch empty intervals + if (A.get_height() * A.get_width() == 0) + return z; + // base case + if (A.get_height() == 1 || A.get_width() == 1) + return naive_matrix_col_vector_multiply_and_add(A, x, z, offset_x, offset_z); + + // partition matrix + swappable_block_matrix_approximative_quarterer qa(A); + // recursive multiplication + // The order of recursive calls is optimized to enhance locality. + recursive_matrix_col_vector_multiply_and_add(qa.ul, x, z, offset_x, offset_z ); + recursive_matrix_col_vector_multiply_and_add(qa.ur, x, z, offset_x + qa.ul.get_width(), offset_z ); + recursive_matrix_col_vector_multiply_and_add(qa.dr, x, z, offset_x + qa.ul.get_width(), offset_z + qa.ul.get_height()); + recursive_matrix_col_vector_multiply_and_add(qa.dl, x, z, offset_x, offset_z + qa.ul.get_height()); + + return z; + } + + static column_vector_type& + naive_matrix_col_vector_multiply_and_add(const swappable_block_matrix_type& A, + const column_vector_type& x, column_vector_type& z, + const vector_size_type offset_x = 0, const vector_size_type offset_z = 0) + { + for (size_type row = 0; row < A.get_height(); ++row) + for (size_type col = 0; col < A.get_width(); ++col) + matrix_col_vector_multiply_and_add_swappable_block(A(row, col), A.is_transposed(), A.bs, + x, z, (offset_x + col) * BlockSideLength, (offset_z + row) * BlockSideLength); + return z; + } + + static void matrix_col_vector_multiply_and_add_swappable_block( + const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, + const column_vector_type& x, column_vector_type& z, + const vector_size_type offset_x = 0, const vector_size_type offset_z = 0) + { + // check if zero-block (== ! initialized) + if (! bs_a.is_initialized(a)) + { + // => matrix is zero => product is zero + return; + } + // acquire + internal_block_type& ia = bs_a.acquire(a); + // multiply + if (! bs_a.is_simulating()) + { + int_type row_limit = std::min(BlockSideLength, unsigned(z.size() - offset_z)), + col_limit = std::min(BlockSideLength, unsigned(x.size() - offset_x)); + if (a_is_transposed) + for (int_type col = 0; col < col_limit; ++col) + for (int_type row = 0; row < row_limit; ++row) + z[offset_z + row] += x[offset_x + col] * ia[row + col * BlockSideLength]; + else + for (int_type row = 0; row < row_limit; ++row) + for (int_type col = 0; col < col_limit; ++col) + z[offset_z + row] += x[offset_x + col] * ia[row * BlockSideLength + col]; + } + // release + bs_a.release(a, false); + } + + //! calculates z = y * A + static row_vector_type& + recursive_matrix_row_vector_multiply_and_add(const row_vector_type& y, + const swappable_block_matrix_type& A, row_vector_type& z, + const vector_size_type offset_y = 0, const vector_size_type offset_z = 0) + { + // catch empty intervals + if (A.get_height() * A.get_width() == 0) + return z; + // base case + if (A.get_height() == 1 || A.get_width() == 1) + return naive_matrix_row_vector_multiply_and_add(y, A, z, offset_y, offset_z); + + // partition matrix + swappable_block_matrix_approximative_quarterer qa(A); + // recursive multiplication + // The order of recursive calls is optimized to enhance locality. + recursive_matrix_row_vector_multiply_and_add(y, qa.ul, z, offset_y, offset_z ); + recursive_matrix_row_vector_multiply_and_add(y, qa.dl, z, offset_y + qa.ul.get_height(), offset_z ); + recursive_matrix_row_vector_multiply_and_add(y, qa.dr, z, offset_y + qa.ul.get_height(), offset_z + qa.ul.get_width()); + recursive_matrix_row_vector_multiply_and_add(y, qa.ur, z, offset_y, offset_z + qa.ul.get_width()); + + return z; + } + + static row_vector_type& + naive_matrix_row_vector_multiply_and_add(const row_vector_type& y, const swappable_block_matrix_type& A, + row_vector_type& z, + const vector_size_type offset_y = 0, const vector_size_type offset_z = 0) + { + for (size_type row = 0; row < A.get_height(); ++row) + for (size_type col = 0; col < A.get_width(); ++col) + matrix_row_vector_multiply_and_add_swappable_block(y, A(row, col), A.is_transposed(), A.bs, + z, (offset_y + row) * BlockSideLength, (offset_z + col) * BlockSideLength); + return z; + } + + static void matrix_row_vector_multiply_and_add_swappable_block(const row_vector_type& y, + const swappable_block_identifier_type a, const bool a_is_transposed, block_scheduler_type& bs_a, + row_vector_type& z, + const vector_size_type offset_y = 0, const vector_size_type offset_z = 0) + { + // check if zero-block (== ! initialized) + if (! bs_a.is_initialized(a)) + { + // => matrix is zero => product is zero + return; + } + // acquire + internal_block_type& ia = bs_a.acquire(a); + // multiply + if (! bs_a.is_simulating()) + { + int_type row_limit = std::min(BlockSideLength, unsigned(y.size() - offset_y)), + col_limit = std::min(BlockSideLength, unsigned(z.size() - offset_z)); + if (a_is_transposed) + for (int_type col = 0; col < col_limit; ++col) + for (int_type row = 0; row < row_limit; ++row) + z[offset_z + col] += ia[row + col * BlockSideLength] * y[offset_y + row]; + else + for (int_type row = 0; row < row_limit; ++row) + for (int_type col = 0; col < col_limit; ++col) + z[offset_z + col] += ia[row * BlockSideLength + col] * y[offset_y + row]; + } + // release + bs_a.release(a, false); + } + + // +-+ end matrix-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + // +-+-+-+ vector-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + static void recursive_matrix_from_vectors(swappable_block_matrix_type A, const column_vector_type& l, + const row_vector_type& r, vector_size_type offset_l = 0, vector_size_type offset_r = 0) + { + // catch empty intervals + if (A.get_height() * A.get_width() == 0) + return; + // base case + if (A.get_height() == 1 || A.get_width() == 1) + { + naive_matrix_from_vectors(A, l, r, offset_l, offset_r); + return; + } + + // partition matrix + swappable_block_matrix_approximative_quarterer qa(A); + // recursive creation + // The order of recursive calls is optimized to enhance locality. + recursive_matrix_from_vectors(qa.ul, l, r, offset_l, offset_r ); + recursive_matrix_from_vectors(qa.ur, l, r, offset_l, offset_r + qa.ul.get_width()); + recursive_matrix_from_vectors(qa.dr, l, r, offset_l + qa.ul.get_height(), offset_r + qa.ul.get_width()); + recursive_matrix_from_vectors(qa.dl, l, r, offset_l + qa.ul.get_height(), offset_r ); + } + + static void naive_matrix_from_vectors(swappable_block_matrix_type A, const column_vector_type& l, + const row_vector_type& r, vector_size_type offset_l = 0, vector_size_type offset_r = 0) + { + for (size_type row = 0; row < A.get_height(); ++row) + for (size_type col = 0; col < A.get_width(); ++col) + matrix_from_vectors_swappable_block(A(row, col), A.is_transposed(), A.bs, + l, r, (offset_l + row) * BlockSideLength, (offset_r + col) * BlockSideLength); + } + + static void matrix_from_vectors_swappable_block(swappable_block_identifier_type a, + const bool a_is_transposed, block_scheduler_type& bs_a, + const column_vector_type& l, const row_vector_type& r, + vector_size_type offset_l, vector_size_type offset_r) + { + // acquire + internal_block_type& ia = bs_a.acquire(a, true); + // multiply + if (! bs_a.is_simulating()) + { + int_type row_limit = std::min(BlockSideLength, unsigned(l.size() - offset_l)), + col_limit = std::min(BlockSideLength, unsigned(r.size() - offset_r)); + if (a_is_transposed) + for (int_type col = 0; col < col_limit; ++col) + for (int_type row = 0; row < row_limit; ++row) + ia[row + col * BlockSideLength] = l[row + offset_l] * r[col + offset_r]; + else + for (int_type row = 0; row < row_limit; ++row) + for (int_type col = 0; col < col_limit; ++col) + ia[row * BlockSideLength + col] = l[row + offset_l] * r[col + offset_r]; + } + // release + bs_a.release(a, true); + } + + // +-+ end vector-vector multiplication +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +}; + +// Adjust choose_level_for_feedable_sw, too! +template +const int_type matrix_operations::strassen_winograd_base_case_size = 3; + +} // namespace matrix_local + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_MATRIX_ARITHMETIC_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_low_level.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_low_level.h new file mode 100644 index 0000000000..550b77c500 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/matrix_low_level.h @@ -0,0 +1,755 @@ +/*************************************************************************** + * include/stxxl/bits/containers/matrix_low_level.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010-2011 Raoul Steffen + * + * 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 STXXL_CONTAINERS_MATRIX_LOW_LEVEL_HEADER +#define STXXL_CONTAINERS_MATRIX_LOW_LEVEL_HEADER + +#ifndef STXXL_BLAS +#define STXXL_BLAS 0 +#endif + +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup matrix +//! \{ + +namespace matrix_local { + +// forward declaration +template +struct matrix_operations; + +// generic declaration +template +struct switch_major_index; + +// row-major specialization +template +struct switch_major_index +{ + inline switch_major_index(const int_type row, const int_type col) : i(row * BlockSideLength + col) { } + inline operator int_type& () { return i; } + +private: + int_type i; +}; + +//column-major specialization +template +struct switch_major_index +{ + inline switch_major_index(const int_type row, const int_type col) : i(row + col * BlockSideLength) { } + inline operator int_type& () { return i; } + +private: + int_type i; +}; + +//! c = a [op] b; for arbitrary entries +template +struct low_level_matrix_binary_ass_op +{ + low_level_matrix_binary_ass_op(ValueType* c, const ValueType* a, const ValueType* b, Op op = Op()) + { + if (a) + if (b) + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type row = 0; row < int_type(BlockSideLength); ++row) + for (int_type col = 0; col < int_type(BlockSideLength); ++col) + op(c[switch_major_index < BlockSideLength, false > (row, col)], + a[switch_major_index < BlockSideLength, a_transposed > (row, col)], + b[switch_major_index < BlockSideLength, b_transposed > (row, col)]); + else + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type row = 0; row < int_type(BlockSideLength); ++row) + for (int_type col = 0; col < int_type(BlockSideLength); ++col) + op(c[switch_major_index < BlockSideLength, false > (row, col)], + a[switch_major_index < BlockSideLength, a_transposed > (row, col)], 0); + else + { + assert(b /* do not add nothing to nothing */); + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type row = 0; row < int_type(BlockSideLength); ++row) + for (int_type col = 0; col < int_type(BlockSideLength); ++col) + op(c[switch_major_index < BlockSideLength, false > (row, col)], + 0, b[switch_major_index < BlockSideLength, b_transposed > (row, col)]); + } + } +}; + +//! c [op]= a; for arbitrary entries +template +struct low_level_matrix_unary_ass_op +{ + low_level_matrix_unary_ass_op(ValueType* c, const ValueType* a, Op op = Op()) + { + if (a) + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type row = 0; row < int_type(BlockSideLength); ++row) + for (int_type col = 0; col < int_type(BlockSideLength); ++col) + op(c[switch_major_index < BlockSideLength, false > (row, col)], + a[switch_major_index < BlockSideLength, a_transposed > (row, col)]); + } +}; + +//! c =[op] a; for arbitrary entries +template +struct low_level_matrix_unary_op +{ + low_level_matrix_unary_op(ValueType* c, const ValueType* a, Op op = Op()) + { + assert(a); + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type row = 0; row < int_type(BlockSideLength); ++row) + for (int_type col = 0; col < int_type(BlockSideLength); ++col) + c[switch_major_index < BlockSideLength, false > (row, col)] = + op(a[switch_major_index < BlockSideLength, a_transposed > (row, col)]); + } +}; + +//! multiplies matrices A and B, adds result to C, for arbitrary entries +//! param pointer to blocks of A,B,C; elements in blocks have to be in row-major +/* designated usage as: + * void + * low_level_matrix_multiply_and_add(const double * a, bool a_in_col_major, + const double * b, bool b_in_col_major, + double * c, const bool c_in_col_major) */ +template +struct low_level_matrix_multiply_and_add +{ + low_level_matrix_multiply_and_add(const ValueType* a, bool a_in_col_major, + const ValueType* b, bool b_in_col_major, + ValueType* c, const bool c_in_col_major) + { + if (c_in_col_major) + { + std::swap(a, b); + bool a_cm = ! b_in_col_major; + b_in_col_major = ! a_in_col_major; + a_in_col_major = a_cm; + } + if (! a_in_col_major) + { + if (! b_in_col_major) + { // => both row-major + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables + for (unsigned_type k = 0; k < BlockSideLength; ++k) + for (unsigned_type j = 0; j < BlockSideLength; ++j) + c[i * BlockSideLength + j] += a[i * BlockSideLength + k] * b[k * BlockSideLength + j]; + } + else + { // => a row-major, b col-major + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables + for (unsigned_type j = 0; j < BlockSideLength; ++j) + for (unsigned_type k = 0; k < BlockSideLength; ++k) + c[i * BlockSideLength + j] += a[i * BlockSideLength + k] * b[k + j * BlockSideLength]; + } + } + else + { + if (! b_in_col_major) + { // => a col-major, b row-major + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables + for (unsigned_type k = 0; k < BlockSideLength; ++k) + for (unsigned_type j = 0; j < BlockSideLength; ++j) + c[i * BlockSideLength + j] += a[i + k * BlockSideLength] * b[k * BlockSideLength + j]; + } + else + { // => both col-major + #if STXXL_PARALLEL + #pragma omp parallel for + #endif + for (int_type i = 0; i < int_type(BlockSideLength); ++i) //OpenMP does not like unsigned iteration variables + for (unsigned_type k = 0; k < BlockSideLength; ++k) + for (unsigned_type j = 0; j < BlockSideLength; ++j) + c[i * BlockSideLength + j] += a[i + k * BlockSideLength] * b[k + j * BlockSideLength]; + } + } + } +}; + +#if STXXL_BLAS +typedef int_type blas_int; +typedef std::complex blas_double_complex; +typedef std::complex blas_single_complex; + +// --- vector add (used as matrix-add) ----------------- + +extern "C" void daxpy_(const blas_int* n, const double* alpha, const double* x, const blas_int* incx, double* y, const blas_int* incy); +extern "C" void saxpy_(const blas_int* n, const float* alpha, const float* x, const blas_int* incx, float* y, const blas_int* incy); +extern "C" void zaxpy_(const blas_int* n, const blas_double_complex* alpha, const blas_double_complex* x, const blas_int* incx, blas_double_complex* y, const blas_int* incy); +extern "C" void caxpy_(const blas_int* n, const blas_single_complex* alpha, const blas_single_complex* x, const blas_int* incx, blas_single_complex* y, const blas_int* incy); +extern "C" void dcopy_(const blas_int* n, const double* x, const blas_int* incx, double* y, const blas_int* incy); +extern "C" void scopy_(const blas_int* n, const float* x, const blas_int* incx, float* y, const blas_int* incy); +extern "C" void zcopy_(const blas_int* n, const blas_double_complex* x, const blas_int* incx, blas_double_complex* y, const blas_int* incy); +extern "C" void ccopy_(const blas_int* n, const blas_single_complex* x, const blas_int* incx, blas_single_complex* y, const blas_int* incy); + +//! c = a + b; for double entries +template +struct low_level_matrix_binary_ass_op::addition> +{ + low_level_matrix_binary_ass_op(double* c, const double* a, const double* b, typename matrix_operations::addition = typename matrix_operations::addition()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::addition> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::addition> + (c, b); + } + } +}; +//! c = a - b; for double entries +template +struct low_level_matrix_binary_ass_op::subtraction> +{ + low_level_matrix_binary_ass_op(double* c, const double* a, const double* b, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::subtraction> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::subtraction> + (c, b); + } + } +}; +//! c += a; for double entries +template +struct low_level_matrix_unary_ass_op::addition> +{ + low_level_matrix_unary_ass_op(double* c, const double* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const double one = 1.0; + if (a) + daxpy_(&size, &one, a, &int_one, c, &int_one); + } +}; +//! c -= a; for double entries +template +struct low_level_matrix_unary_ass_op::subtraction> +{ + low_level_matrix_unary_ass_op(double* c, const double* a, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const double minusone = -1.0; + if (a) + daxpy_(&size, &minusone, a, &int_one, c, &int_one); + } +}; +//! c = a; for double entries +template +struct low_level_matrix_unary_op::addition> +{ + low_level_matrix_unary_op(double* c, const double* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + dcopy_(&size, a, &int_one, c, &int_one); + } +}; + +//! c = a + b; for float entries +template +struct low_level_matrix_binary_ass_op::addition> +{ + low_level_matrix_binary_ass_op(float* c, const float* a, const float* b, typename matrix_operations::addition = typename matrix_operations::addition()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::addition> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::addition> + (c, b); + } + } +}; +//! c = a - b; for float entries +template +struct low_level_matrix_binary_ass_op::subtraction> +{ + low_level_matrix_binary_ass_op(float* c, const float* a, const float* b, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::subtraction> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::subtraction> + (c, b); + } + } +}; +//! c += a; for float entries +template +struct low_level_matrix_unary_ass_op::addition> +{ + low_level_matrix_unary_ass_op(float* c, const float* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const float one = 1.0; + if (a) + saxpy_(&size, &one, a, &int_one, c, &int_one); + } +}; +//! c -= a; for float entries +template +struct low_level_matrix_unary_ass_op::subtraction> +{ + low_level_matrix_unary_ass_op(float* c, const float* a, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const float minusone = -1.0; + if (a) + saxpy_(&size, &minusone, a, &int_one, c, &int_one); + } +}; +//! c = a; for float entries +template +struct low_level_matrix_unary_op::addition> +{ + low_level_matrix_unary_op(float* c, const float* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + scopy_(&size, a, &int_one, c, &int_one); + } +}; + +//! c = a + b; for blas_double_complex entries +template +struct low_level_matrix_binary_ass_op::addition> +{ + low_level_matrix_binary_ass_op(blas_double_complex* c, const blas_double_complex* a, const blas_double_complex* b, typename matrix_operations::addition = typename matrix_operations::addition()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::addition> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::addition> + (c, b); + } + } +}; +//! c = a - b; for blas_double_complex entries +template +struct low_level_matrix_binary_ass_op::subtraction> +{ + low_level_matrix_binary_ass_op(blas_double_complex* c, const blas_double_complex* a, const blas_double_complex* b, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::subtraction> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::subtraction> + (c, b); + } + } +}; +//! c += a; for blas_double_complex entries +template +struct low_level_matrix_unary_ass_op::addition> +{ + low_level_matrix_unary_ass_op(blas_double_complex* c, const blas_double_complex* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const blas_double_complex one = 1.0; + if (a) + zaxpy_(&size, &one, a, &int_one, c, &int_one); + } +}; +//! c -= a; for blas_double_complex entries +template +struct low_level_matrix_unary_ass_op::subtraction> +{ + low_level_matrix_unary_ass_op(blas_double_complex* c, const blas_double_complex* a, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const blas_double_complex minusone = -1.0; + if (a) + zaxpy_(&size, &minusone, a, &int_one, c, &int_one); + } +}; +//! c = a; for blas_double_complex entries +template +struct low_level_matrix_unary_op::addition> +{ + low_level_matrix_unary_op(blas_double_complex* c, const blas_double_complex* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + zcopy_(&size, a, &int_one, c, &int_one); + } +}; + +//! c = a + b; for blas_single_complex entries +template +struct low_level_matrix_binary_ass_op::addition> +{ + low_level_matrix_binary_ass_op(blas_single_complex* c, const blas_single_complex* a, const blas_single_complex* b, typename matrix_operations::addition = typename matrix_operations::addition()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::addition> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::addition> + (c, b); + } + } +}; +//! c = a - b; for blas_single_complex entries +template +struct low_level_matrix_binary_ass_op::subtraction> +{ + low_level_matrix_binary_ass_op(blas_single_complex* c, const blas_single_complex* a, const blas_single_complex* b, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + if (a) + if (b) + { + low_level_matrix_unary_op::addition> + (c, a); + low_level_matrix_unary_ass_op::subtraction> + (c, b); + } + else + low_level_matrix_unary_op::addition> + (c, a); + else + { + assert(b /* do not add nothing to nothing */); + low_level_matrix_unary_op::subtraction> + (c, b); + } + } +}; +//! c += a; for blas_single_complex entries +template +struct low_level_matrix_unary_ass_op::addition> +{ + low_level_matrix_unary_ass_op(blas_single_complex* c, const blas_single_complex* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const blas_single_complex one = 1.0; + if (a) + caxpy_(&size, &one, a, &int_one, c, &int_one); + } +}; +//! c -= a; for blas_single_complex entries +template +struct low_level_matrix_unary_ass_op::subtraction> +{ + low_level_matrix_unary_ass_op(blas_single_complex* c, const blas_single_complex* a, + typename matrix_operations::subtraction = typename matrix_operations::subtraction()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + const blas_single_complex minusone = -1.0; + if (a) + caxpy_(&size, &minusone, a, &int_one, c, &int_one); + } +}; +//! c = a; for blas_single_complex entries +template +struct low_level_matrix_unary_op::addition> +{ + low_level_matrix_unary_op(blas_single_complex* c, const blas_single_complex* a, + typename matrix_operations::addition = typename matrix_operations::addition()) + { + const blas_int size = BlockSideLength * BlockSideLength; + const blas_int int_one = 1; + ccopy_(&size, a, &int_one, c, &int_one); + } +}; + +// --- matrix-matrix multiplication --------------- + +extern "C" void dgemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const double* alpha, const double* a, const blas_int* lda, + const double* b, const blas_int* ldb, + const double* beta, double* c, const blas_int* ldc); + +extern "C" void sgemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const float* alpha, const float* a, const blas_int* lda, + const float* b, const blas_int* ldb, + const float* beta, float* c, const blas_int* ldc); + +extern "C" void zgemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const blas_double_complex* alpha, const blas_double_complex* a, const blas_int* lda, + const blas_double_complex* b, const blas_int* ldb, + const blas_double_complex* beta, blas_double_complex* c, const blas_int* ldc); + +extern "C" void cgemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const blas_single_complex* alpha, const blas_single_complex* a, const blas_int* lda, + const blas_single_complex* b, const blas_int* ldb, + const blas_single_complex* beta, blas_single_complex* c, const blas_int* ldc); + +template +void gemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const ValueType* alpha, const ValueType* a, const blas_int* lda, + const ValueType* b, const blas_int* ldb, + const ValueType* beta, ValueType* c, const blas_int* ldc); + +//! calculates c = alpha * a * b + beta * c +//! \tparam ValueType type of elements +//! \param n height of a and c +//! \param l width of a and height of b +//! \param m width of b and c +//! \param a_in_col_major if a is stored in column-major rather than row-major +//! \param b_in_col_major if b is stored in column-major rather than row-major +//! \param c_in_col_major if c is stored in column-major rather than row-major +template +void gemm_wrapper(const blas_int n, const blas_int l, const blas_int m, + const ValueType alpha, const bool a_in_col_major, const ValueType* a, + const bool b_in_col_major, const ValueType* b, + const ValueType beta, const bool c_in_col_major, ValueType* c) +{ + const blas_int& stride_in_a = a_in_col_major ? n : l; + const blas_int& stride_in_b = b_in_col_major ? l : m; + const blas_int& stride_in_c = c_in_col_major ? n : m; + const char transa = a_in_col_major xor c_in_col_major ? 'T' : 'N'; + const char transb = b_in_col_major xor c_in_col_major ? 'T' : 'N'; + if (c_in_col_major) + // blas expects matrices in column-major unless specified via transa rsp. transb + gemm_(&transa, &transb, &n, &m, &l, &alpha, a, &stride_in_a, b, &stride_in_b, &beta, c, &stride_in_c); + else + // blas expects matrices in column-major, so we calculate c^T = alpha * b^T * a^T + beta * c^T + gemm_(&transb, &transa, &m, &n, &l, &alpha, b, &stride_in_b, a, &stride_in_a, &beta, c, &stride_in_c); +} + +template <> +void gemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const double* alpha, const double* a, const blas_int* lda, + const double* b, const blas_int* ldb, + const double* beta, double* c, const blas_int* ldc) +{ + dgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); +} + +template <> +void gemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const float* alpha, const float* a, const blas_int* lda, + const float* b, const blas_int* ldb, + const float* beta, float* c, const blas_int* ldc) +{ + sgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); +} + +template <> +void gemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const blas_double_complex* alpha, const blas_double_complex* a, const blas_int* lda, + const blas_double_complex* b, const blas_int* ldb, + const blas_double_complex* beta, blas_double_complex* c, const blas_int* ldc) +{ + zgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); +} + +template <> +void gemm_(const char* transa, const char* transb, + const blas_int* m, const blas_int* n, const blas_int* k, + const blas_single_complex* alpha, const blas_single_complex* a, const blas_int* lda, + const blas_single_complex* b, const blas_int* ldb, + const blas_single_complex* beta, blas_single_complex* c, const blas_int* ldc) +{ + cgemm_(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc); +} + +//! multiplies matrices A and B, adds result to C, for double entries +template +struct low_level_matrix_multiply_and_add +{ + low_level_matrix_multiply_and_add(const double* a, bool a_in_col_major, + const double* b, bool b_in_col_major, + double* c, const bool c_in_col_major) + { + gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, + 1.0, a_in_col_major, a, + /**/ b_in_col_major, b, + 1.0, c_in_col_major, c); + } +}; + +//! multiplies matrices A and B, adds result to C, for float entries +template +struct low_level_matrix_multiply_and_add +{ + low_level_matrix_multiply_and_add(const float* a, bool a_in_col_major, + const float* b, bool b_in_col_major, + float* c, const bool c_in_col_major) + { + gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, + 1.0, a_in_col_major, a, + /**/ b_in_col_major, b, + 1.0, c_in_col_major, c); + } +}; + +//! multiplies matrices A and B, adds result to C, for complex entries +template +struct low_level_matrix_multiply_and_add +{ + low_level_matrix_multiply_and_add(const blas_single_complex* a, bool a_in_col_major, + const blas_single_complex* b, bool b_in_col_major, + blas_single_complex* c, const bool c_in_col_major) + { + gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, + 1.0, a_in_col_major, a, + /**/ b_in_col_major, b, + 1.0, c_in_col_major, c); + } +}; + +//! multiplies matrices A and B, adds result to C, for complex entries +template +struct low_level_matrix_multiply_and_add +{ + low_level_matrix_multiply_and_add(const blas_double_complex* a, bool a_in_col_major, + const blas_double_complex* b, bool b_in_col_major, + blas_double_complex* c, const bool c_in_col_major) + { + gemm_wrapper(BlockSideLength, BlockSideLength, BlockSideLength, + 1.0, a_in_col_major, a, + /**/ b_in_col_major, b, + 1.0, c_in_col_major, c); + } +}; +#endif + +} // namespace matrix_local + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_MATRIX_LOW_LEVEL_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pager.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pager.h new file mode 100644 index 0000000000..4d6ccb4583 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pager.h @@ -0,0 +1,122 @@ +/*************************************************************************** + * include/stxxl/bits/containers/pager.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002, 2003, 2006 Roman Dementiev + * Copyright (C) 2011 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_PAGER_HEADER +#define STXXL_CONTAINERS_PAGER_HEADER + +#include +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlcont_vector +//! \{ + +enum pager_type +{ + random, + lru +}; + +//! Pager with \b random replacement strategy +template +class random_pager +{ + enum { n_pages = npages_ }; + + typedef unsigned_type size_type; + + size_type num_pages; + random_number rnd; + +public: + random_pager(size_type num_pages = n_pages) : num_pages(num_pages) { } + size_type kick() + { + return rnd(size()); + } + + void hit(size_type ipage) + { + STXXL_ASSERT(ipage < size()); + } + + size_type size() const + { + return num_pages; + } +}; + +//! Pager with \b LRU replacement strategy +template +class lru_pager : private noncopyable +{ + enum { n_pages = npages_ }; + + typedef unsigned_type size_type; + typedef std::list list_type; + + list_type history; + simple_vector history_entry; + +public: + lru_pager(size_type num_pages = n_pages) : history_entry(num_pages) + { + for (size_type i = 0; i < size(); ++i) + history_entry[i] = history.insert(history.end(), i); + } + + size_type kick() + { + return history.back(); + } + + void hit(size_type ipage) + { + assert(ipage < size()); + history.splice(history.begin(), history, history_entry[ipage]); + } + + void swap(lru_pager& obj) + { + history.swap(obj.history); + history_entry.swap(obj.history_entry); + } + + size_type size() const + { + return history_entry.size(); + } +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::lru_pager& a, + stxxl::lru_pager& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_PAGER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/parallel_priority_queue.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/parallel_priority_queue.h new file mode 100644 index 0000000000..558396f2bd --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/parallel_priority_queue.h @@ -0,0 +1,4675 @@ +/*************************************************************************** + * include/stxxl/bits/containers/parallel_priority_queue.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2014-2015 Thomas Keh + * Copyright (C) 2014-2015 Timo Bingmann + * + * 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 STXXL_CONTAINERS_PARALLEL_PRIORITY_QUEUE_HEADER +#define STXXL_CONTAINERS_PARALLEL_PRIORITY_QUEUE_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if STXXL_PARALLEL + #include +#endif + +#if __cplusplus >= 201103L +#define STXXL_MOVE(T) std::move(T) +#else +#define STXXL_MOVE(T) T +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace ppq_local { + +/*! + * A random-access iterator class for block oriented data. The iterator is + * intended to be provided by the internal_array and external_array classes + * and to be used by the multiway_merge algorithm as input iterators. + * + * \tparam ValueType the value type + */ +template +class ppq_iterator +{ +public: + typedef ValueType value_type; + typedef value_type& reference; + typedef value_type* pointer; + typedef ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + typedef std::vector > block_pointers_type; + +protected: + typedef ppq_iterator self_type; + + //! pointer to a vector of begin/end pointer pairs + //! They allow access to the data blocks. + const block_pointers_type* m_block_pointers; + + //! pointer to the current element + pointer m_current; + + //! index of the current element + size_t m_index; + + //! index of the current element's block + size_t m_block_index; + + //! size of each data block + size_t m_block_items; + +public: + //! default constructor (should not be used directly) + ppq_iterator() + : m_block_pointers(NULL) + { } + + //! constructor + //! + //! \param block_pointers A reference to the properly initialized vector of begin and end pointers. + //! One pair for each block. The pointers should be valid for all blocks that + //! are expected to be accessed with this iterator. + //! \param block_items The size of a single block. If there is only one block (e.g. if the iterator + //! belongs to an internal_array), use the total size here. + //! \param index The index of the current element (global - index 0 belongs to the first element + //! in the first block, no matter if the values are still valid) + ppq_iterator(const block_pointers_type* block_pointers, size_t block_items, + size_t index) + : m_block_pointers(block_pointers), + m_index(index), + m_block_items(block_items) + { + update(); + } + + //! returns the value's index in the internal or external array + size_t get_index() const + { + return m_index; + } + + reference operator * () const + { + assert(m_current); + return *m_current; + } + + pointer operator -> () const + { + return &(operator * ()); + } + + reference operator [] (difference_type relative_index) const + { + const difference_type index = m_index + relative_index; + + const size_t block_index = index / m_block_items; + const size_t local_index = index % m_block_items; + + assert(block_index < m_block_pointers->size()); + assert((*m_block_pointers)[block_index].first + local_index + < (*m_block_pointers)[block_index].second); + + return *((*m_block_pointers)[block_index].first + local_index); + } + + //! prefix-increment operator + self_type& operator ++ () + { + ++m_index; + ++m_current; + + if (UNLIKELY(m_current == (*m_block_pointers)[m_block_index].second)) { + if (m_block_index + 1 < m_block_pointers->size()) { + m_current = (*m_block_pointers)[++m_block_index].first; + } + else { + // global end + assert(m_block_index + 1 == m_block_pointers->size()); + m_current = (*m_block_pointers)[m_block_index++].second; + } + } + + return *this; + } + //! prefix-decrement operator + self_type& operator -- () + { + assert(m_index > 0); + --m_index; + + if (m_block_index >= m_block_pointers->size() + || m_current == (*m_block_pointers)[m_block_index].first) { + // begin of current block or global end + assert(m_block_index > 0); + assert(m_block_index <= m_block_pointers->size()); + m_current = (*m_block_pointers)[--m_block_index].second - 1; + } + else { + --m_current; + } + + return *this; + } + + self_type operator + (difference_type addend) const + { + return self_type(m_block_pointers, m_block_items, m_index + addend); + } + self_type& operator += (difference_type addend) + { + m_index += addend; + update(); + return *this; + } + self_type operator - (difference_type subtrahend) const + { + return self_type(m_block_pointers, m_block_items, m_index - subtrahend); + } + difference_type operator - (const self_type& o) const + { + return (m_index - o.m_index); + } + self_type& operator -= (difference_type subtrahend) + { + m_index -= subtrahend; + update(); + return *this; + } + bool operator == (const self_type& o) const + { + return m_index == o.m_index; + } + bool operator != (const self_type& o) const + { + return m_index != o.m_index; + } + bool operator < (const self_type& o) const + { + return m_index < o.m_index; + } + bool operator <= (const self_type& o) const + { + return m_index <= o.m_index; + } + bool operator > (const self_type& o) const + { + return m_index > o.m_index; + } + bool operator >= (const self_type& o) const + { + return m_index >= o.m_index; + } + + friend std::ostream& operator << (std::ostream& os, const ppq_iterator& i) + { + return os << "[" << i.m_index << "]"; + } + +private: + //! updates m_block_index and m_current based on m_index + inline void update() + { + m_block_index = m_index / m_block_items; + const size_t local_index = m_index % m_block_items; + + if (m_block_index < m_block_pointers->size()) { + m_current = (*m_block_pointers)[m_block_index].first + local_index; + assert(m_current <= (*m_block_pointers)[m_block_index].second); + } + else { + // global end if end is beyond the last real block + assert(m_block_index == m_block_pointers->size()); + assert(local_index == 0); + //-tb old: m_current = (*m_block_pointers)[m_block_index - 1].second; + m_current = NULL; + } + } +}; + +/*! + * Internal arrays store a sorted sequence of values in RAM, which will be + * merged together into the deletion buffer when it needs to be + * refilled. Internal arrays are constructed from the insertions heaps when + * they overflow. + */ +template +class internal_array : private noncopyable +{ +public: + typedef ValueType value_type; + typedef ppq_iterator iterator; + +protected: + typedef typename iterator::block_pointers_type block_pointers_type; + + //! Contains the items of the sorted sequence. + std::vector m_values; + + //! Index of the current head + unsigned_type m_min_index; + + //! Level of internal array (Sander's PQ: group number) + unsigned_type m_level; + + //! Begin and end pointers of the array + //! This is used by the iterator + block_pointers_type m_block_pointers; + +public: + //! Default constructor. Don't use this directy. Needed for regrowing in + //! surrounding vector. + internal_array() : m_min_index(0) { } + + //! Constructor which takes a value vector. The value vector is empty + //! afterwards. + internal_array(std::vector& values, + unsigned_type min_index = 0, + unsigned_type level = 0) + : m_values(), m_min_index(min_index), m_level(level), + m_block_pointers(1) + { + std::swap(m_values, values); + STXXL_ASSERT(values.size() > 0); + m_block_pointers[0] = std::make_pair(&(*m_values.begin()), &(*m_values.begin()) + m_values.size()); + } + + //! Swap internal_array with another one. + void swap(internal_array& o) + { + using std::swap; + + swap(m_values, o.m_values); + swap(m_min_index, o.m_min_index); + swap(m_level, o.m_level); + swap(m_block_pointers, o.m_block_pointers); + } + + //! Swap internal_array with another one. + friend void swap(internal_array& a, internal_array& b) + { + a.swap(b); + } + + //! Random access operator + inline value_type& operator [] (size_t i) + { + return m_values[i]; + } + + //! Use inc_min(diff) if multiple values have been extracted. + inline void inc_min(size_t diff = 1) + { + m_min_index += diff; + } + + //! The currently smallest element in the array. + inline const value_type & get_min() const + { + return m_values[m_min_index]; + } + + //! The index of the currently smallest element in the array. + inline size_t get_min_index() const + { + return m_min_index; + } + + //! The index of the largest element in the array. + inline size_t get_max_index() const + { + return (m_values.size() - 1); + } + + //! Returns if the array has run empty. + inline bool empty() const + { + return (m_min_index >= m_values.size()); + } + + //! Make this array empty. + inline void make_empty() + { + m_min_index = m_values.size(); + } + + //! Returns the current size of the array. + inline size_t size() const + { + return (m_values.size() - m_min_index); + } + + //! Returns the initial size of the array. + inline size_t capacity() const + { + return m_values.size(); + } + + //! Returns the level (group number) of the array. + inline unsigned_type level() const + { + return m_level; + } + + //! Return the amount of internal memory used by an array with the capacity + //! in number of items. + static size_t int_memory(size_t capacity) + { + return sizeof(internal_array) + capacity * sizeof(value_type); + } + + //! Return the amount of internal memory used by the array + inline size_t int_memory() const + { + return int_memory(m_values.capacity()); + } + + //! Begin iterator + inline iterator begin() const + { + // not const, unfortunately. + return iterator(&m_block_pointers, capacity(), m_min_index); + } + + //! End iterator + inline iterator end() const + { + // not const, unfortunately. + return iterator(&m_block_pointers, capacity(), capacity()); + } +}; + +template +class external_array_writer; + +/*! + * External array stores a sorted sequence of values on the hard disk and + * allows access to the first block (containing the smallest values). The + * class uses buffering and prefetching in order to improve the performance. + * + * \tparam ValueType Type of the contained objects (POD with no references to + * internal memory). + * + * \tparam BlockSize External block size. Default = + * STXXL_DEFAULT_BLOCK_SIZE(ValueType). + * + * \tparam AllocStrategy Allocation strategy for the external memory. Default = + * STXXL_DEFAULT_ALLOC_STRATEGY. + */ +template < + class ValueType, + unsigned_type BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), + class AllocStrategy = STXXL_DEFAULT_ALLOC_STRATEGY + > +class external_array : private noncopyable +{ +public: + typedef ValueType value_type; + typedef ppq_iterator iterator; + + typedef external_array self_type; + typedef typed_block block_type; + typedef read_write_pool pool_type; + typedef std::vector > bid_vector; + typedef typename bid_vector::iterator bid_iterator; + typedef std::vector block_vector; + typedef std::vector request_vector; + typedef std::vector minima_vector; + typedef typename iterator::block_pointers_type block_pointers_type; + typedef external_array_writer writer_type; + + //! The number of elements fitting into one block + enum { + block_size = BlockSize, + block_items = BlockSize / sizeof(value_type) + }; + + static const bool debug = false; + +protected: + //! The total size of the external array in items. Cannot be changed + //! after construction. + external_size_type m_capacity; + + //! Number of blocks, again: calculated at construction time. + unsigned_type m_num_blocks; + + //! Level of external array (Sander's PQ: group number) + unsigned_type m_level; + + //! Common prefetch and write buffer pool + pool_type* m_pool; + + //! The IDs of each block in external memory. + bid_vector m_bids; + + //! A vector of size m_num_blocks with block_type pointers, some of them + //! will be filled while writing, but most are NULL. + block_vector m_blocks; + + //! Begin and end pointers for each block, used for merging with + //! ppq_iterator. + block_pointers_type m_block_pointers; + + //! The read request pointers are used to wait until the block has been + //! completely fetched. + request_vector m_requests; + + //! stores the minimum value of each block + minima_vector m_minima; + + //! Is array in write phase? True = write phase, false = read phase. + bool m_write_phase; + + //! The total number of elements minus the number of extracted values + external_size_type m_size; + + //! The read position in the array. + external_size_type m_index; + + //! The index behind the last element that is located in RAM (or is at + //! least requested to be so) + external_size_type m_end_index; + + //! The first unhinted block index. + unsigned_type m_unhinted_block; + + //! The first unhinted block index as it was before the + //! prepare_rebuilding_hints() call. Used for removal of hints which aren't + //! needed anymore. + unsigned_type m_old_unhinted_block; + + //! allow writer to access to all variables + friend class external_array_writer; + +public: + /*! + * Constructs an external array + * + * \param size The total number of elements. Cannot be changed after + * construction. + * + * \param num_prefetch_blocks Number of blocks to prefetch from hard disk + * + * \param num_write_buffer_blocks Size of the write buffer in number of + * blocks + */ + external_array(external_size_type size, pool_type* pool, unsigned_type level = 0) + : // constants + m_capacity(size), + m_num_blocks((size_t)div_ceil(m_capacity, block_items)), + m_level(level), + m_pool(pool), + + // vectors + m_bids(m_num_blocks), + m_blocks(m_num_blocks, reinterpret_cast(1)), + m_block_pointers(m_num_blocks), + m_requests(m_num_blocks, NULL), + m_minima(m_num_blocks), + + // state + m_write_phase(true), + + // indices + m_size(0), + m_index(0), + m_end_index(0), + m_unhinted_block(0), + m_old_unhinted_block(0) + { + assert(m_capacity > 0); + // allocate blocks in EM. + block_manager* bm = block_manager::get_instance(); + bm->new_blocks(AllocStrategy(), m_bids.begin(), m_bids.end()); + } + + //! Default constructor. Don't use this directy. Needed for regrowing in + //! surrounding vector. + external_array() + : // constants + m_capacity(0), + m_num_blocks(0), + m_level(0), + m_pool(NULL), + + // vectors + m_bids(0), + m_blocks(0), + m_block_pointers(0), + m_requests(0), + m_minima(0), + + // state + m_write_phase(false), + + // indices + m_size(0), + m_index(0), + m_end_index(0), + m_unhinted_block(0), + m_old_unhinted_block(0) + { } + + //! Swap external_array with another one. + void swap(external_array& o) + { + using std::swap; + + // constants + swap(m_capacity, o.m_capacity); + swap(m_num_blocks, o.m_num_blocks); + swap(m_level, o.m_level); + swap(m_pool, o.m_pool); + + // vectors + swap(m_bids, o.m_bids); + swap(m_requests, o.m_requests); + swap(m_blocks, o.m_blocks); + swap(m_block_pointers, o.m_block_pointers); + swap(m_minima, o.m_minima); + + // state + swap(m_write_phase, o.m_write_phase); + + // indices + swap(m_size, o.m_size); + swap(m_index, o.m_index); + swap(m_end_index, o.m_end_index); + swap(m_unhinted_block, o.m_unhinted_block); + swap(m_old_unhinted_block, o.m_old_unhinted_block); + } + + //! Swap external_array with another one. + friend void swap(external_array& a, external_array& b) + { + a.swap(b); + } + + //! Destructor + ~external_array() + { + if (m_size == 0) return; + + // not all data has been read! this only happen when the PPQ is + // destroyed while containing data. + + const unsigned_type block_index = m_index / block_items; + const unsigned_type end_block_index = get_end_block_index(); + + // released blocks currently held in RAM + for (size_t i = block_index; i < end_block_index; ++i) { + m_pool->add_prefetch(m_blocks[i]); + // cannot report the number of freed blocks to PPQ. + } + + // cancel currently hinted blocks + for (size_t i = end_block_index; i < m_unhinted_block; ++i) { + STXXL_DEBUG("ea[" << this << "]: discarding prefetch hint on" + " block " << i); + + m_requests[i]->cancel(); + m_requests[i]->wait(); + // put block back into pool + m_pool->add_prefetch(m_blocks[i]); + // invalidate block entry + m_blocks[i] = NULL; + m_requests[i] = request_ptr(); + } + + // figure out first block that is still allocated in EM. + bid_iterator i_begin = m_bids.begin() + block_index; + block_manager::get_instance()->delete_blocks(i_begin, m_bids.end()); + + // check that all is empty + for (size_t i = block_index; i < end_block_index; ++i) + assert(m_blocks[i] == NULL); + } + + //! Returns the capacity in items. + size_t capacity() const + { + return m_capacity; + } + + //! Returns the current size in items. + size_t size() const + { + return m_size; + } + + //! Returns true if the array is empty. + bool empty() const + { + return (m_size == 0); + } + + //! Returns the level (group number) of the array. + inline unsigned_type level() const + { + return m_level; + } + + //! Return the number of blocks. + size_t num_blocks() const + { + return m_num_blocks; + } + + //! Returns memory usage of EA with given capacity, excluding blocks loaded + //! in RAM. Blocks belong to prefetch pool. + static size_t int_memory(size_t capacity) + { + size_t num_blocks = div_ceil(capacity, block_items); + + return sizeof(external_array) + + num_blocks * sizeof(typename bid_vector::value_type) + + num_blocks * sizeof(typename block_vector::value_type) + + num_blocks * sizeof(typename block_pointers_type::value_type) + + num_blocks * sizeof(typename request_vector::value_type) + + num_blocks * sizeof(typename minima_vector::value_type); + } + + //! Return the amount of internal memory used by the EA. + inline size_t int_memory() const + { + return int_memory(m_capacity); + } + + //! Returns the number elements available in internal memory + size_t buffer_size() const + { + return (m_end_index - m_index); + } + + //! Returns the block beyond the block in which *(m_end_index-1) is located. + unsigned_type get_end_block_index() const + { + unsigned_type end_block_index = m_end_index / block_items; + + // increase block index if inside the block + if (m_end_index % block_items != 0) ++end_block_index; + assert(end_block_index <= m_num_blocks); + + return end_block_index; + } + + //! Returns the block in which m_index is located. + inline unsigned_type get_current_block_index() const + { + return (m_index / block_items); + } + + //! Returns a random-access iterator to the begin of the data + //! in internal memory. + iterator begin() const + { + //-TODO?: assert(block_valid(m_index / block_items) || m_index == m_capacity); + return iterator(&m_block_pointers, block_items, m_index); + } + + //! Returns a random-access iterator 1 behind the end of the data + //! in internal memory. + iterator end() const + { + //-TODO? assert(!block_valid(m_end_index / block_items) || m_end_index == m_capacity); + return iterator(&m_block_pointers, block_items, m_end_index); + } + + //! Returns the smallest element in the array + const value_type & get_min() + { + return *begin(); + } + + //! Returns if there is data in EM, that's not randomly accessible. + bool has_em_data() const + { + return (get_end_block_index() < m_num_blocks); + } + + //! Returns the smallest element of the first block NOT in internal memory + //! (or at least requested to be in internal memory) + const value_type & get_next_block_min() const + { + assert(get_end_block_index() < m_num_blocks); + return m_minima[get_end_block_index()]; + } + + //! Returns if the data requested to be in internal memory is + //! completely fetched. True if wait() has been called before. + bool valid() const + { + bool result = true; + const unsigned_type block_index = m_index / block_items; + const unsigned_type end_block_index = get_end_block_index(); + for (unsigned_type i = block_index; i < end_block_index; ++i) { + result = result && block_valid(i); + } + return result; + } + + //! Random access operator for data in internal memory + //! You should call wait() once after fetching data from EM. + value_type& operator [] (size_t i) const + { + assert(i < m_capacity); + const size_t block_index = i / block_items; + const size_t local_index = i % block_items; + assert(i < m_capacity); + assert(block_valid(block_index)); + return m_blocks[block_index]->elem[local_index]; + } + +public: + //! prepare the pool for writing external arrays with given number of + //! threads + static void prepare_write_pool(pool_type& pool, unsigned_type num_threads) + { + unsigned_type write_blocks = num_threads; + // need at least one + if (write_blocks == 0) write_blocks = 1; + // for holding boundary blocks + write_blocks *= 2; + // more disks than threads? + if (write_blocks < config::get_instance()->disks_number()) + write_blocks = config::get_instance()->disks_number(); +#if STXXL_DEBUG_ASSERTIONS + // required for re-reading the external array + write_blocks = 2 * write_blocks; +#endif + if (pool.size_write() < write_blocks) { + STXXL_ERRMSG("WARNING: enlarging PPQ write pool to " << + write_blocks << " blocks = " << + write_blocks * block_size / 1024 / 1024 << " MiB"); + pool.resize_write(write_blocks); + } + } + +protected: + //! prepare the external_array for writing using multiway_merge() with + //! num_threads. this method is called by the external_array_writer's + //! constructor. + void prepare_write(unsigned_type num_threads) + { + prepare_write_pool(*m_pool, num_threads); + } + + //! finish the writing phase after multiway_merge() filled the vector. this + //! method is called by the external_array_writer's destructor.. + void finish_write() + { + // check that all blocks where written + for (unsigned_type i = 0; i < m_num_blocks; ++i) + assert(m_blocks[i] == NULL); + + // compatibility to the block write interface + m_size = m_capacity; + m_index = 0; + m_end_index = 0; + m_unhinted_block = 0; + + m_write_phase = false; + } + + //! Called by the external_array_writer to read a block from disk into + //! m_blocks[]. If the block is marked as uninitialized, then no read is + //! performed. This is the usual case, and in theory, no block ever has be + //! re-read from disk, since all can be written fully. However, we do + //! support re-reading blocks for debugging purposes inside + //! multiway_merge(), in a full performance build re-reading never occurs. + void read_block(size_t block_index) + { + assert(block_index < m_num_blocks); + assert(m_blocks[block_index] == NULL || + m_blocks[block_index] == reinterpret_cast(1)); + + if (m_blocks[block_index] == reinterpret_cast(1)) + { + // special marker: this block is uninitialized -> no need to read + // from disk. + m_blocks[block_index] = m_pool->steal(); + } + else + { + // block was already written, have to read from EM. + STXXL_DEBUG("ea[" << this << "]: " + "read_block needs to re-read block index=" << block_index); + + static bool s_warned = false; + if (!s_warned) + { + s_warned = true; + STXXL_ERRMSG("ppq::external_array[" << this << "] " + "writer requested to re-read block from EM."); + STXXL_ERRMSG("This should never occur in full-performance mode, " + "verify that you run in debug mode."); + } + + // this re-reading is not necessary for full performance builds, so + // we immediately wait for the I/O to be completed. + m_blocks[block_index] = m_pool->steal(); + request_ptr req = m_pool->read(m_blocks[block_index], m_bids[block_index]); + req->wait(); + assert(req->poll()); + assert(m_blocks[block_index]); + } + } + + //! Called by the external_array_writer to write a block from m_blocks[] to + //! disk. Prior to writing and releasing the memory, extra information is + //! preserved. + void write_block(size_t block_index) + { + assert(block_index < m_num_blocks); + assert(m_blocks[block_index] != NULL && + m_blocks[block_index] != reinterpret_cast(1)); + + // calculate minimum and maximum values + const internal_size_type this_block_items = + std::min(block_items, m_capacity - block_index * (external_size_type)block_items); + + STXXL_DEBUG("ea[" << this << "]: write_block index=" << block_index << + " this_block_items=" << this_block_items); + + assert(this_block_items > 0); + block_type& this_block = *m_blocks[block_index]; + + m_minima[block_index] = this_block[0]; + + // write out block (in background) + m_pool->write(m_blocks[block_index], m_bids[block_index]); + + m_blocks[block_index] = NULL; + } + +public: + //! \name Prefetching Hints + //! \{ + + //! Prefetch the next unhinted block, requires one free read block from the + //! global pool. + void hint_next_block() + { + assert(m_unhinted_block < m_num_blocks); + + // will read (prefetch) block i + size_t i = m_unhinted_block++; + + STXXL_DEBUG("ea[" << this << "]: prefetching block_index=" << i); + + assert(m_pool->size_write() > 0); + assert(m_blocks[i] == NULL); + + // steal block from pool, but also perform read via pool, since this + // checks the associated write_pool. + m_blocks[i] = m_pool->steal_prefetch(); + m_requests[i] = m_pool->read(m_blocks[i], m_bids[i]); + } + + //! Returns if there is data in EM, that's not already hinted + //! to the prefetcher. + bool has_unhinted_em_data() const + { + return (m_unhinted_block < m_num_blocks); + } + + //! Returns the smallest element of the next hint candidate (the block + //! after the last hinted one). + const value_type & get_next_hintable_min() const + { + assert(m_unhinted_block < m_num_blocks); + return m_minima[m_unhinted_block]; + } + + //! Returns the number of hinted blocks. + size_t num_hinted_blocks() const + { + assert(get_end_block_index() <= m_unhinted_block); + return m_unhinted_block - get_end_block_index(); + } + + //! This method prepares rebuilding the hints (this is done after creating + //! a new EA in order to always have globally the n blocks hinted which + //! will be fetched first). Resets m_unhinted_block to the first block not + //! in RAM. Thereafter prehint_next_block() is used to advance this index. + //! finish_rebuilding_hints() should be called after placing all hints in + //! order to clean up the prefetch pool. + void rebuild_hints_prepare() + { + m_old_unhinted_block = m_unhinted_block; + m_unhinted_block = get_end_block_index(); + assert(get_end_block_index() <= m_old_unhinted_block); + } + + //! Advance m_unhinted_block index without actually prefetching. + void rebuild_hints_prehint_next_block() + { + assert(m_unhinted_block < m_num_blocks); + + // will read (prefetch) block after cancellations. + + STXXL_DEBUG("ea[" << this << "]: pre-hint of" << + " block_index=" << m_unhinted_block); + + ++m_unhinted_block; + } + + //! Cancel hints which aren't needed anymore from the prefetcher and fixes + //! it's size. prepare_rebuilding_hints() must be called before! + void rebuild_hints_cancel() + { + for (size_t i = m_unhinted_block; i < m_old_unhinted_block; ++i) { + STXXL_DEBUG("ea[" << this << "]: discarding prefetch hint on" + " block " << i); + m_requests[i]->cancel(); + m_requests[i]->wait(); + // put block back into pool + m_pool->add_prefetch(m_blocks[i]); + // invalidate block entry + m_blocks[i] = NULL; + m_requests[i] = request_ptr(); + } + } + + //! Perform real-hinting of pre-hinted blocks, since now canceled blocks + //! are available. + void rebuild_hints_finish() + { + for (size_t i = m_old_unhinted_block; i < m_unhinted_block; ++i) + { + STXXL_DEBUG("ea[" << this << "]: perform real-hinting of" + " block " << i); + + assert(m_pool->size_write() > 0); + assert(m_blocks[i] == NULL); + m_blocks[i] = m_pool->steal_prefetch(); + m_requests[i] = m_pool->read(m_blocks[i], m_bids[i]); + } + } + + //! \} + +public: + //! \name Waiting and Removal + //! \{ + + //! Waits until the next prefetched block is read into RAM, then polls for + //! any further blocks that are done as well. Returns how many blocks were + //! successfully read. + unsigned_type wait_next_blocks() + { + size_t begin = get_end_block_index(), i = begin; + + STXXL_DEBUG("ea[" << this << "]: waiting for" << + " block index=" << i << + " end_index=" << m_end_index); + + assert(has_em_data()); + + assert(i < m_unhinted_block); + assert(m_bids[i].valid()); + assert(m_requests[i].valid()); + + // wait for prefetched request to finish. + m_requests[i]->wait(); + assert(m_requests[i]->poll()); + assert(m_blocks[i]); + + update_block_pointers(i); + ++i; + + // poll further hinted blocks if already done + while (i < m_unhinted_block && m_requests[i]->poll()) + { + STXXL_DEBUG("ea[" << this << "]: poll-ok for" << + " block index=" << i << + " end_index=" << m_end_index); + m_requests[i]->wait(); + assert(m_requests[i]->poll()); + assert(m_blocks[i]); + + update_block_pointers(i); + ++i; + } + + m_end_index = std::min(m_capacity, i * (external_size_type)block_items); + + return i - begin; + } + + //! Waits until all hinted blocks are read into RAM. Returns how many + //! blocks were successfully read. + unsigned_type wait_all_hinted_blocks() + { + size_t begin = get_end_block_index(), i = begin; + while (i < m_unhinted_block) + { + STXXL_DEBUG("wait_all_hinted_blocks(): ea[" << this << "]: waiting for" << + " block index=" << i << + " end_index=" << m_end_index); + m_requests[i]->wait(); + assert(m_requests[i]->poll()); + assert(m_blocks[i]); + update_block_pointers(i); + ++i; + } + m_end_index = std::min(m_capacity, i * (external_size_type)block_items); + return i - begin; + } + + //! Returns the number of blocks loaded in RAM. + size_t num_used_blocks() const + { + return get_end_block_index() - (m_index / block_items); + } + + //! Removes the first n elements from the array. Returns the number of + //! blocks released into the block pool. + unsigned_type remove_items(size_t n) + { + assert(m_index + n <= m_capacity); + assert(m_index + n <= m_end_index); + assert(m_size >= n); + + STXXL_DEBUG("ea[" << this << "]: remove " << n << " items"); + + if (n == 0) + return 0; + + const size_t block_index = m_index / block_items; + + const size_t index_after = m_index + n; + size_t block_index_after = index_after / block_items; + size_t local_index_after = index_after % block_items; + + if (m_size == n && local_index_after != 0) // end of EA + ++block_index_after; + + assert(block_index_after <= m_num_blocks); + + bid_iterator i_begin = m_bids.begin() + block_index; + bid_iterator i_end = m_bids.begin() + block_index_after; + assert(i_begin <= i_end); + block_manager::get_instance()->delete_blocks(i_begin, i_end); + + for (size_t i = block_index; i < block_index_after; ++i) { + assert(block_valid(i)); + // return block to pool + m_pool->add_prefetch(m_blocks[i]); + } + + m_index = index_after; + m_size -= n; + + unsigned_type blocks_freed = block_index_after - block_index; + + STXXL_DEBUG("ea[" << this << "]: after remove:" << + " index_after=" << index_after << + " block_index_after=" << block_index_after << + " local_index_after=" << local_index_after << + " blocks_freed=" << blocks_freed << + " num_blocks=" << m_num_blocks << + " capacity=" << m_capacity); + + assert(block_index_after <= m_num_blocks); + // at most one block outside of the currently loaded range + assert(block_index_after <= get_end_block_index()); + + return blocks_freed; + } + + //! \} + +protected: + //! Returns if the block with the given index is completely fetched. + bool block_valid(size_t block_index) const + { + if (!m_write_phase) { + if (block_index >= m_num_blocks) return false; + return (m_requests[block_index] && m_requests[block_index]->poll()); + } + else { + return (bool)m_blocks[block_index]; + } + } + + //! Updates the m_block_pointers vector. + //! Should be called after any steal() or read() operation. + //! This is necessary for the iterators to work properly. + inline void update_block_pointers(size_t block_index) + { + STXXL_DEBUG("ea[" << this << "]: updating block pointers for " << block_index); + + m_block_pointers[block_index].first = m_blocks[block_index]->begin(); + if (block_index + 1 != m_num_blocks) + m_block_pointers[block_index].second = m_blocks[block_index]->end(); + else + m_block_pointers[block_index].second = + m_block_pointers[block_index].first + + (m_capacity - block_index * block_items); + + assert(m_block_pointers[block_index].first != NULL); + assert(m_block_pointers[block_index].second != NULL); + } + + inline size_t last_block_items() + { + size_t mod = m_capacity % block_items; + return (mod > 0) ? mod : (size_t)block_items; + } +}; + +/** + * An external_array can only be written using an external_array_writer + * object. The writer objects provides iterators which are designed to be used + * by stxxl::parallel::multiway_merge() to write the external memory blocks in + * parallel. Thus in the writer we coordinate thread-safe access to the blocks + * using reference counting. + * + * An external_array_writer::iterator has two states: normal and "live". In + * normal mode, the iterator only has a valid index into the external array's + * items. In normal mode, only index calculations are possible. Once + * operator*() is called, the iterators goes into "live" mode by requesting + * access to the corresponding block. Using reference counting the blocks is + * written once all iterators are finished with the corresponding block. Since + * with operator*() we cannot know if the value is going to be written or read, + * when going to live mode, the block must be read from EM. This read overhead, + * however, is optimized by marking blocks as uninitialized in external_array, + * and skipping reads for then. In a full performance build, no block needs to + * be read from disk. Reads only occur in debug mode, when the results are + * verify. + * + * The iterator's normal/live mode only stays active for the individual + * iterator object. When an iterator is copied/assigned/calculated with the + * mode is NOT inherited! The exception is prefix operator ++, which is used by + * multiway_merge() to fill an array. Thus the implementation of the iterator + * heavily depends on the behavior of multiway_merge() and is optimized for it. + */ +template +class external_array_writer : public noncopyable +{ +public: + typedef ExternalArrayType ea_type; + + typedef external_array_writer self_type; + + typedef typename ea_type::value_type value_type; + typedef typename ea_type::block_type block_type; + + //! prototype declaration of nested class. + class iterator; + + //! scope based debug variable + static const bool debug = false; + +protected: + //! reference to the external array to be written + ea_type& m_ea; + +#ifndef NDEBUG + //! total number of iterators referencing this writer + unsigned int m_ref_total; +#endif + + //! reference counters for the number of live iterators on the + //! corresponding block in external_array. + std::vector m_ref_count; + + //! mutex for reference counting array (this is actually nicer than + //! openmp's critical) + mutex m_mutex; + + //! optimization: hold live iterators for the expected boundary blocks of + //! multiway_merge(). + std::vector m_live_boundary; + +protected: + //! read block into memory and increase reference count (called when an + //! iterator goes live on the block). + block_type * get_block_ref(size_t block_index) + { + scoped_mutex_lock lock(m_mutex); + + assert(block_index < m_ea.num_blocks()); + unsigned int ref = m_ref_count[block_index]++; +#ifndef NDEBUG + ++m_ref_total; +#endif + + if (ref == 0) { + STXXL_DEBUG("get_block_ref block_index=" << block_index << + " ref=" << ref << " reading."); + m_ea.read_block(block_index); + } + else { + STXXL_DEBUG("get_block_ref block_index=" << block_index << + " ref=" << ref); + } + + return m_ea.m_blocks[block_index]; + } + + //! decrease reference count on the block, and possibly write it to disk + //! (called when an iterator releases live mode). + void free_block_ref(size_t block_index) + { + scoped_mutex_lock lock(m_mutex); + + assert(block_index < m_ea.num_blocks()); +#ifndef NDEBUG + assert(m_ref_total > 0); + --m_ref_total; +#endif + unsigned int ref = --m_ref_count[block_index]; + + if (ref == 0) { + STXXL_DEBUG("free_block_ref block_index=" << block_index << + " ref=" << ref << " written."); + m_ea.write_block(block_index); + } + else { + STXXL_DEBUG("free_block_ref block_index=" << block_index << + " ref=" << ref); + } + } + + //! allow access to the block_ref functions + friend class iterator; + +public: + /** + * An iterator which can be used to write (and read) an external_array via + * an external_array_writer. See the documentation of external_array_writer. + */ + class iterator + { + public: + typedef external_array_writer writer_type; + typedef ExternalArrayType ea_type; + + typedef typename ea_type::value_type value_type; + typedef value_type& reference; + typedef value_type* pointer; + typedef ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + + typedef iterator self_type; + + static const size_t block_items = ea_type::block_items; + + //! scope based debug variable + static const bool debug = false; + + protected: + //! pointer to the external array containing the elements + writer_type* m_writer; + + //! when operator* or operator-> are called, then the iterator goes + //! live and allocates a reference to the block's data (possibly + //! reading it from EM). + bool m_live; + + //! index of the current element, absolute in the external array + external_size_type m_index; + + //! index of the current element's block in the external array's block + //! list. undefined while m_live is false. + internal_size_type m_block_index; + + //! pointer to the referenced block. undefined while m_live is false. + block_type* m_block; + + //! pointer to the current element inside the referenced block. + //! undefined while m_live is false. + internal_size_type m_current; + + public: + //! default constructor (should not be used directly) + iterator() + : m_writer(NULL), m_live(false), m_index(0) + { } + + //! construct a new iterator + iterator(writer_type* writer, external_size_type index) + : m_writer(writer), + m_live(false), + m_index(index) + { + STXXL_DEBUG("Construct iterator for index " << m_index); + } + + //! copy an iterator, the new iterator is _not_ automatically live! + iterator(const iterator& other) + : m_writer(other.m_writer), + m_live(false), + m_index(other.m_index) + { + STXXL_DEBUG("Copy-Construct iterator for index " << m_index); + } + + //! assign an iterator, the assigned iterator is not automatically live! + iterator& operator = (const iterator& other) + { + if (&other != this) + { + STXXL_DEBUG("Assign iterator to index " << other.m_index); + + if (m_live) + m_writer->free_block_ref(m_block_index); + + m_writer = other.m_writer; + m_live = false; + m_index = other.m_index; + } + + return *this; + } + + ~iterator() + { + if (!m_live) return; // no need for cleanup + + m_writer->free_block_ref(m_block_index); + + STXXL_DEBUG("Destruction of iterator for index " << m_index << + " in block " << m_index / block_items); + } + + //! return the current absolute index inside the external array. + external_size_type get_index() const + { + return m_index; + } + + //! allocates a reference to the block's data (possibly reading it from + //! EM). + void make_live() + { + assert(!m_live); + + // calculate block and index inside + m_block_index = m_index / block_items; + m_current = m_index % block_items; + + STXXL_DEBUG("operator*() live request for index=" << m_index << + " block_index=" << m_block_index << + " m_current=" << m_current); + + // get block reference + m_block = m_writer->get_block_ref(m_block_index); + m_live = true; + } + + //! access the current item + reference operator * () + { + if (UNLIKELY(!m_live)) + make_live(); + + return (*m_block)[m_current]; + } + + //! access the current item + pointer operator -> () + { + return &(operator * ()); + } + + //! prefix-increment operator + self_type& operator ++ () + { + ++m_index; + if (UNLIKELY(!m_live)) return *this; + + // if index stays in the same block, everything is fine + ++m_current; + if (LIKELY(m_current != block_items)) return *this; + + // release current block + m_writer->free_block_ref(m_block_index); + m_live = false; + + return *this; + } + + self_type operator + (difference_type addend) const + { + return self_type(m_writer, m_index + addend); + } + self_type operator - (difference_type subtrahend) const + { + return self_type(m_writer, m_index - subtrahend); + } + difference_type operator - (const self_type& o) const + { + return (m_index - o.m_index); + } + + bool operator == (const self_type& o) const + { + return m_index == o.m_index; + } + bool operator != (const self_type& o) const + { + return m_index != o.m_index; + } + bool operator < (const self_type& o) const + { + return m_index < o.m_index; + } + bool operator <= (const self_type& o) const + { + return m_index <= o.m_index; + } + bool operator > (const self_type& o) const + { + return m_index > o.m_index; + } + bool operator >= (const self_type& o) const + { + return m_index >= o.m_index; + } + }; + +public: + external_array_writer(ea_type& ea, unsigned int num_threads = 0) + : m_ea(ea), + m_ref_count(ea.num_blocks(), 0) + { +#ifndef NDEBUG + m_ref_total = 0; +#endif + +#if STXXL_PARALLEL + if (num_threads == 0) + num_threads = omp_get_max_threads(); +#else + if (num_threads == 0) + num_threads = 1; +#endif + + m_ea.prepare_write(num_threads); + + // optimization: hold live iterators for the boundary blocks which two + // threads write to. this prohibits the blocks to be written to disk + // and read again. + + double step = (double)m_ea.capacity() / (double)num_threads; + m_live_boundary.resize(num_threads - 1); + + for (unsigned int i = 0; i < num_threads - 1; ++i) + { + external_size_type index = (external_size_type)((i + 1) * step); + STXXL_DEBUG("hold index " << index << + " in block " << index / ea_type::block_items); + m_live_boundary[i] = iterator(this, index); + m_live_boundary[i].make_live(); + } + } + + ~external_array_writer() + { + m_live_boundary.clear(); // release block boundaries +#ifndef NDEBUG + STXXL_ASSERT(m_ref_total == 0); +#endif + m_ea.finish_write(); + } + + iterator begin() + { + return iterator(this, 0); + } + + iterator end() + { + return iterator(this, m_ea.capacity()); + } +}; + +/*! + * The minima_tree contains minima from all sources inside the PPQ. It contains + * four substructures: winner trees for insertion heaps, internal and external + * arrays, each containing the minima from all currently allocated + * structures. These three sources, plus the deletion buffer are combined using + * a "head" inner tree containing only up to four item. + */ +template +class minima_tree +{ +public: + typedef ParentType parent_type; + typedef minima_tree self_type; + + typedef typename parent_type::inv_compare_type compare_type; + typedef typename parent_type::value_type value_type; + typedef typename parent_type::proc_vector_type proc_vector_type; + typedef typename parent_type::internal_arrays_type ias_type; + typedef typename parent_type::external_arrays_type eas_type; + + static const unsigned initial_ia_size = 2; + static const unsigned initial_ea_size = 2; + +protected: + //! WinnerTree-Comparator for the head winner tree. It accesses all + //! relevant data structures from the priority queue. + struct head_comp + { + self_type& m_parent; + proc_vector_type& m_proc; + ias_type& m_ias; + const compare_type& m_compare; + + head_comp(self_type& parent, proc_vector_type& proc, + ias_type& ias, const compare_type& compare) + : m_parent(parent), + m_proc(proc), + m_ias(ias), + m_compare(compare) + { } + + const value_type & get_value(int input) const + { + switch (input) { + case HEAP: + return m_proc[m_parent.m_heaps.top()]->insertion_heap[0]; + case IA: + return m_ias[m_parent.m_ia.top()].get_min(); + case EB: + return m_parent.m_parent.m_extract_buffer[ + m_parent.m_parent.m_extract_buffer_index + ]; + default: + abort(); + } + } + + bool operator () (const int a, const int b) const + { + return m_compare(get_value(a), get_value(b)); + } + }; + + //! Comparator for the insertion heaps winner tree. + struct heaps_comp + { + proc_vector_type& m_proc; + const compare_type& m_compare; + + heaps_comp(proc_vector_type& proc, const compare_type& compare) + : m_proc(proc), m_compare(compare) + { } + + const value_type & get_value(int index) const + { + return m_proc[index]->insertion_heap[0]; + } + + bool operator () (const int a, const int b) const + { + return m_compare(get_value(a), get_value(b)); + } + }; + + //! Comparator for the internal arrays winner tree. + struct ia_comp + { + ias_type& m_ias; + const compare_type& m_compare; + + ia_comp(ias_type& ias, const compare_type& compare) + : m_ias(ias), m_compare(compare) + { } + + bool operator () (const int a, const int b) const + { + return m_compare(m_ias[a].get_min(), m_ias[b].get_min()); + } + }; + +protected: + //! The priority queue + parent_type& m_parent; + + //! value_type comparator + const compare_type& m_compare; + + //! Comperator instances + head_comp m_head_comp; + heaps_comp m_heaps_comp; + ia_comp m_ia_comp; + + //! The winner trees + winner_tree m_head; + winner_tree m_heaps; + winner_tree m_ia; + +public: + //! Entries in the head winner tree. + enum Types { + HEAP = 0, + IA = 1, + EB = 2, + TYPE_ERROR = 3 + }; + + //! Construct the tree of minima sources. + minima_tree(parent_type& parent) + : m_parent(parent), + m_compare(parent.m_inv_compare), + // construct comparators + m_head_comp(*this, parent.m_proc, + parent.m_internal_arrays, m_compare), + m_heaps_comp(parent.m_proc, m_compare), + m_ia_comp(parent.m_internal_arrays, m_compare), + // construct header winner tree + m_head(3, m_head_comp), + m_heaps(m_parent.m_num_insertion_heaps, m_heaps_comp), + m_ia(initial_ia_size, m_ia_comp) + { } + + //! Return smallest items of head winner tree. + std::pair top() + { + unsigned type = m_head.top(); + switch (type) + { + case HEAP: + return std::make_pair(HEAP, m_heaps.top()); + case IA: + return std::make_pair(IA, m_ia.top()); + case EB: + return std::make_pair(EB, 0); + default: + return std::make_pair(TYPE_ERROR, 0); + } + } + + //! Update minima tree after an item from the heap index was removed. + void update_heap(int_type index) + { + m_heaps.notify_change(index); + m_head.notify_change(HEAP); + } + + //! Update minima tree after an item of the extract buffer was removed. + void update_extract_buffer() + { + m_head.notify_change(EB); + } + + //! Update minima tree after an item from an internal array was removed. + void update_internal_array(unsigned index) + { + m_ia.notify_change(index); + m_head.notify_change(IA); + } + + //! Add a newly created internal array to the minima tree. + void add_internal_array(unsigned index) + { + m_ia.activate_player(index); + m_head.notify_change(IA); + } + + //! Remove an insertion heap from the minima tree. + void deactivate_heap(unsigned index) + { + m_heaps.deactivate_player(index); + if (!m_heaps.empty()) + m_head.notify_change(HEAP); + else + m_head.deactivate_player(HEAP); + } + + //! Remove the extract buffer from the minima tree. + void deactivate_extract_buffer() + { + m_head.deactivate_player(EB); + } + + //! Remove an internal array from the minima tree. + void deactivate_internal_array(unsigned index) + { + m_ia.deactivate_player(index); + if (!m_ia.empty()) + m_head.notify_change(IA); + else + m_head.deactivate_player(IA); + } + + //! Remove all insertion heaps from the minima tree. + void clear_heaps() + { + m_heaps.clear(); + m_head.deactivate_player(HEAP); + } + + //! Remove all internal arrays from the minima tree. + void clear_internal_arrays() + { + m_ia.resize_and_clear(initial_ia_size); + m_head.deactivate_player(IA); + } + + void rebuild_internal_arrays() + { + if (!m_parent.m_internal_arrays.empty()) + { + m_ia.resize_and_rebuild(m_parent.m_internal_arrays.size()); + m_head.notify_change(IA); + } + else + { + m_head.deactivate_player(IA); + } + } + + //! Return size of internal arrays minima tree + size_t ia_slots() const + { + return m_ia.num_slots(); + } + + //! Returns a readable representation of the winner tree as string. + std::string to_string() const + { + std::ostringstream ss; + ss << "Head:" << std::endl << m_head.to_string() << std::endl; + ss << "Heaps:" << std::endl << m_heaps.to_string() << std::endl; + ss << "IA:" << std::endl << m_ia.to_string() << std::endl; + return ss.str(); + } + + //! Prints statistical data. + void print_stats() const + { + STXXL_MSG("Head winner tree stats:"); + m_head.print_stats(); + STXXL_MSG("Heaps winner tree stats:"); + m_heaps.print_stats(); + STXXL_MSG("IA winner tree stats:"); + m_ia.print_stats(); + } +}; + +} // namespace ppq_local + +/*! + * Parallelized External Memory Priority Queue. + * + * \tparam ValueType Type of the contained objects (POD with no references to + * internal memory). + * + * \tparam CompareType The comparator type used to determine whether one + * element is smaller than another element. + * + * \tparam DefaultMemSize Maximum memory consumption by the queue. Can be + * overwritten by the constructor. Default = 1 GiB. + * + * \tparam MaxItems Maximum number of elements the queue contains at one + * time. Default = 0 = unlimited. This is no hard limit and only used for + * optimization. Can be overwritten by the constructor. + * + * \tparam BlockSize External block size. Default = + * STXXL_DEFAULT_BLOCK_SIZE(ValueType). + * + * \tparam AllocStrategy Allocation strategy for the external memory. Default = + * STXXL_DEFAULT_ALLOC_STRATEGY. + */ +template < + class ValueType, + class CompareType = std::less, + class AllocStrategy = STXXL_DEFAULT_ALLOC_STRATEGY, + uint64 BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), + uint64 DefaultMemSize = 1* 1024L* 1024L* 1024L, + uint64 MaxItems = 0 + > +class parallel_priority_queue : private noncopyable +{ + //! \name Types + //! \{ + +public: + typedef ValueType value_type; + typedef CompareType compare_type; + typedef AllocStrategy alloc_strategy; + static const uint64 block_size = BlockSize; + typedef uint64 size_type; + + typedef typed_block block_type; + typedef std::vector > bid_vector; + typedef bid_vector bids_container_type; + typedef read_write_pool pool_type; + typedef ppq_local::internal_array internal_array_type; + typedef ppq_local::external_array external_array_type; + typedef typename external_array_type::writer_type external_array_writer_type; + typedef typename std::vector::iterator value_iterator; + typedef typename internal_array_type::iterator iterator; + typedef std::pair iterator_pair_type; + + static const bool debug = false; + + //! currently global public tuning parameter: + unsigned_type c_max_internal_level_size; + + //! currently global public tuning parameter: + unsigned_type c_max_external_level_size; + +protected: + //! type of insertion heap itself + typedef std::vector heap_type; + + //! type of internal arrays vector + typedef typename stxxl::swap_vector internal_arrays_type; + //! type of external arrays vector + typedef typename stxxl::swap_vector external_arrays_type; + //! type of minima tree combining the structures + typedef ppq_local::minima_tree< + parallel_priority_queue > minima_type; + //! allow minima tree access to internal data structures + friend class ppq_local::minima_tree< + parallel_priority_queue >; + + //! Inverse comparison functor + struct inv_compare_type + { + const compare_type& compare; + + inv_compare_type(const compare_type& c) + : compare(c) + { } + + bool operator () (const value_type& x, const value_type& y) const + { + return compare(y, x); + } + }; + + //! <-Comparator for value_type + compare_type m_compare; + + //! >-Comparator for value_type + inv_compare_type m_inv_compare; + + //! Defines if statistics are gathered: dummy_custom_stats_counter or + //! custom_stats_counter + typedef dummy_custom_stats_counter stats_counter; + + //! Defines if statistics are gathered: fake_timer or timer + typedef fake_timer stats_timer; + + //! \} + + //! \name Compile-Time Parameters + //! \{ + + //! Merge sorted heaps when flushing into an internal array. + //! Pro: Reduces the risk of a large winner tree + //! Con: Flush insertion heaps becomes slower. + static const bool c_merge_sorted_heaps = true; + + //! Default number of write buffer block for a new external array being + //! filled. + static const unsigned c_num_write_buffer_blocks = 14; + + //! Defines for how much external arrays memory should be reserved in the + //! constructor. + static const unsigned c_num_reserved_external_arrays = 10; + + //! Size of a single insertion heap in Byte, if not defined otherwise in + //! the constructor. Default: 1 MiB + static const size_type c_default_single_heap_ram = 1L * 1024L * 1024L; + + //! Default limit of the extract buffer ram consumption as share of total + //! ram + // C++11: constexpr static double c_default_extract_buffer_ram_part = 0.05; + // C++98 does not allow static const double initialization here. + // It's located in global scope instead. + static const double c_default_extract_buffer_ram_part; + + /*! + * Limit the size of the extract buffer to an absolute value. + * + * The actual size can be set using the extract_buffer_ram parameter of the + * constructor. If this parameter is not set, the value is calculated by + * (total_ram*c_default_extract_buffer_ram_part) + * + * If c_limit_extract_buffer==false, the memory consumption of the extract + * buffer is only limited by the number of external and internal + * arrays. This is considered in memory management using the + * ram_per_external_array and ram_per_internal_array values. Attention: + * Each internal array reserves space for the extract buffer in the size of + * all heaps together. + */ + static const bool c_limit_extract_buffer = true; + + //! For bulks of size up to c_single_insert_limit sequential single insert + //! is faster than bulk_push. + static const unsigned c_single_insert_limit = 100; + + //! \} + + //! \name Parameters and Sizes for Memory Allocation Policy + + //! Number of insertion heaps. Usually equal to the number of CPUs. + const long m_num_insertion_heaps; + + //! Capacity of one inserion heap + const unsigned m_insertion_heap_capacity; + + //! Return size of insertion heap reservation in bytes + size_type insertion_heap_int_memory() const + { + return m_insertion_heap_capacity * sizeof(value_type); + } + + //! Total amount of internal memory + const size_type m_mem_total; + + //! Maximum size of extract buffer in number of elements + //! Only relevant if c_limit_extract_buffer==true + size_type m_extract_buffer_limit; + + //! Size of all insertion heaps together in bytes + const size_type m_mem_for_heaps; + + //! Number of read/prefetch blocks per external array. + const float m_num_read_blocks_per_ea; + + //! Total number of read/prefetch buffer blocks + unsigned_type m_num_read_blocks; + //! number of currently hinted prefetch blocks + unsigned_type m_num_hinted_blocks; + //! number of currently loaded blocks + unsigned_type m_num_used_read_blocks; + + //! Free memory in bytes + size_type m_mem_left; + + //! \} + + //! Flag if inside a bulk_push sequence. + bool m_in_bulk_push; + + //! If the bulk currently being inserted is very large, this boolean is set + //! and bulk_push just accumulate the elements for eventual sorting. + bool m_is_very_large_bulk; + + //! First index in m_external_arrays that was not re-hinted during a + //! bulk_push sequence. + unsigned_type m_bulk_first_delayed_external_array; + + //! Index of the currently smallest element in the extract buffer + size_type m_extract_buffer_index; + + //! \name Number of elements currently in the data structures + //! \{ + + //! Number of elements int the insertion heaps + size_type m_heaps_size; + + //! Number of elements in the extract buffer + size_type m_extract_buffer_size; + + //! Number of elements in the internal arrays + size_type m_internal_size; + + //! Number of elements in the external arrays + size_type m_external_size; + + //! \} + + //! \name Data Holding Structures + //! \{ + + //! A struct containing the local insertion heap and other information + //! _local_ to a processor. + struct ProcessorData + { + //! The heaps where new elements are usually inserted into + heap_type insertion_heap; + + //! The number of items inserted into the insheap during bulk parallel + //! access. + size_type heap_add_size; + }; + + typedef std::vector proc_vector_type; + + //! Array of processor local data structures, including the insertion heaps. + proc_vector_type m_proc; + + //! Prefetch and write buffer pool for external arrays (has to be in front + //! of m_external_arrays) + pool_type m_pool; + + //! The extract buffer where external (and internal) arrays are merged into + //! for extracting + std::vector m_extract_buffer; + + //! The sorted arrays in internal memory + internal_arrays_type m_internal_arrays; + + //! The sorted arrays in external memory + external_arrays_type m_external_arrays; + + //! The aggregated pushes. They cannot be extracted yet. + std::vector m_aggregated_pushes; + + //! The maximum number of internal array levels. + static const unsigned_type c_max_internal_levels = 8; + + //! The number of internal arrays on each level, we use plain array. + unsigned_type m_internal_levels[c_max_internal_levels]; + + //! The maximum number of external array levels. + static const unsigned_type c_max_external_levels = 8; + + //! The number of external arrays on each level, we use plain array. + unsigned_type m_external_levels[c_max_external_levels]; + + //! The winner tree containing the smallest values of all sources + //! where the globally smallest element could come from. + minima_type m_minima; + + //! Compares the largest accessible value of two external arrays. + struct external_min_comparator { + const external_arrays_type& m_eas; + const inv_compare_type& m_compare; + + external_min_comparator(const external_arrays_type& eas, + const inv_compare_type& compare) + : m_eas(eas), m_compare(compare) { } + + bool operator () (const size_t& a, const size_t& b) const + { + return m_compare(m_eas[a].get_next_block_min(), + m_eas[b].get_next_block_min()); + } + } m_external_min_comparator; + + //! Tracks the largest accessible values of the external arrays if there + //! is unaccessible data in EM. The winning array is the first one that + //! needs to fetch further data from EM. Used in calculate_merge_sequences. + winner_tree m_external_min_tree; + + //! Compares the largest value of the block hinted the latest of two + //! external arrays. + struct hint_comparator { + const external_arrays_type& m_eas; + const inv_compare_type& m_compare; + + hint_comparator(const external_arrays_type& eas, + const inv_compare_type& compare) + : m_eas(eas), m_compare(compare) { } + + bool operator () (const size_t& a, const size_t& b) const + { + return m_compare(m_eas[a].get_next_hintable_min(), + m_eas[b].get_next_hintable_min()); + } + } m_hint_comparator; + + //! Tracks the largest values of the block hinted the latest of the + //! external arrays if there is unaccessible data in EM. The winning + //! array is the first one that needs to fetch further data from EM. + //! Used for prefetch hints. + winner_tree m_hint_tree; + + //! Random number generator for randomly selecting a heap in sequential + //! push() + random_number32_r m_rng; + + //! \} + + /* + * Helper function to remove empty internal/external arrays. + */ + + //! Unary operator which returns true if the external array has run empty. + struct empty_external_array_eraser { + bool operator () (external_array_type& a) const + { return a.empty(); } + }; + + //! Unary operator which returns true if the internal array has run empty. + struct empty_internal_array_eraser { + bool operator () (internal_array_type& a) const + { return a.empty(); } + }; + + //! Clean up empty internal arrays, free their memory and capacity + void cleanup_internal_arrays() + { + typename internal_arrays_type::iterator swap_end = + stxxl::swap_remove_if(m_internal_arrays.begin(), + m_internal_arrays.end(), + empty_internal_array_eraser()); + + for (typename internal_arrays_type::iterator ia = swap_end; + ia != m_internal_arrays.end(); ++ia) + { + m_mem_left += ia->int_memory(); + --m_internal_levels[ia->level()]; + } + + if (swap_end != m_internal_arrays.end()) + STXXL_DEBUG0("cleanup_internal_arrays" << + " cleaned=" << m_internal_arrays.end() - swap_end); + + m_internal_arrays.erase(swap_end, m_internal_arrays.end()); + m_minima.rebuild_internal_arrays(); + } + + //! Clean up empty external arrays, free their memory and capacity + void cleanup_external_arrays() + { + typedef typename external_arrays_type::iterator ea_iterator; + empty_external_array_eraser pred; + + // The following is a modified implementation of swap_remove_if(). + // Updates m_external_min_tree accordingly. + + ea_iterator first = m_external_arrays.begin(); + ea_iterator last = m_external_arrays.end(); + ea_iterator swap_end = first; + size_t size = m_external_arrays.end() - m_external_arrays.begin(); + size_t first_removed = size; + while (first != last) + { + if (!pred(*first)) + { + using std::swap; + swap(*first, *swap_end); + ++swap_end; + } + else if (first_removed >= size) + { + first_removed = first - m_external_arrays.begin(); + } + ++first; + } + + // subtract memory of EAs, which will be freed + for (ea_iterator ea = swap_end; ea != last; ++ea) { + m_mem_left += ea->int_memory(); + --m_external_levels[ea->level()]; + } + + size_t swap_end_index = swap_end - m_external_arrays.begin(); + + // Deactivating all affected players first. + // Otherwise there might be outdated comparisons. + for (size_t i = size; i != first_removed; ) { + --i; + m_external_min_tree.deactivate_player_step(i); + // TODO delay if (m_in_bulk_push)? + m_hint_tree.deactivate_player_step(i); + } + + // Replay moved arrays. + for (size_t i = first_removed; i < swap_end_index; ++i) { + update_external_min_tree(i); + // TODO delay if (m_in_bulk_push)? + update_hint_tree(i); + } + + STXXL_DEBUG("Removed " << m_external_arrays.end() - swap_end << + " empty external arrays."); + + m_external_arrays.erase(swap_end, m_external_arrays.end()); + + resize_read_pool(); // shrinks read/prefetch pool + } + + /*! + * SiftUp a new element from the last position in the heap, reestablishing + * the heap invariant. This is identical to std::push_heap, except that it + * returns the last element modified by siftUp. Thus we can identify if the + * minimum may have changed. + */ + template + static inline unsigned_type + push_heap(RandomAccessIterator first, RandomAccessIterator last, + HeapCompareType comp) + { + typedef typename std::iterator_traits::value_type + value_type; + + value_type value = STXXL_MOVE(*(last - 1)); + + unsigned_type index = (last - first) - 1; + unsigned_type parent = (index - 1) / 2; + + while (index > 0 && comp(*(first + parent), value)) + { + *(first + index) = STXXL_MOVE(*(first + parent)); + index = parent; + parent = (index - 1) / 2; + } + *(first + index) = STXXL_MOVE(value); + + return index; + } + +public: + //! \name Initialization + //! \{ + + /*! + * Constructor. + * + * \param compare Comparator for priority queue, which is a Max-PQ. + * + * \param total_ram Maximum RAM usage. 0 = Default = Use the template + * value DefaultMemSize. + * + * \param num_read_blocks_per_ea Number of read blocks per external + * array. Default = 1.5f + * + * \param num_write_buffer_blocks Number of write buffer blocks for a new + * external array being filled. 0 = Default = c_num_write_buffer_blocks + * + * \param num_insertion_heaps Number of insertion heaps. 0 = Default = + * Determine by omp_get_max_threads(). + * + * \param single_heap_ram Memory usage for a single insertion heap. + * Default = c_single_heap_ram. + * + * \param extract_buffer_ram Memory usage for the extract buffer. Only + * relevant if c_limit_extract_buffer==true. 0 = Default = total_ram * + * c_default_extract_buffer_ram_part. + */ + parallel_priority_queue( + const compare_type& compare = compare_type(), + size_type total_ram = DefaultMemSize, + float num_read_blocks_per_ea = 1.5f, + unsigned_type num_write_buffer_blocks = c_num_write_buffer_blocks, + unsigned_type num_insertion_heaps = 0, + size_type single_heap_ram = c_default_single_heap_ram, + size_type extract_buffer_ram = 0) + : c_max_internal_level_size(64), + c_max_external_level_size(64), + m_compare(compare), + m_inv_compare(m_compare), + // Parameters and Sizes for Memory Allocation Policy +#if STXXL_PARALLEL + m_num_insertion_heaps(num_insertion_heaps > 0 ? num_insertion_heaps : omp_get_max_threads()), +#else + m_num_insertion_heaps(num_insertion_heaps > 0 ? num_insertion_heaps : 1), +#endif + m_insertion_heap_capacity(single_heap_ram / sizeof(value_type)), + m_mem_total(total_ram), + m_mem_for_heaps(m_num_insertion_heaps * single_heap_ram), + m_num_read_blocks_per_ea(num_read_blocks_per_ea), + m_num_read_blocks(0), + m_num_hinted_blocks(0), + m_num_used_read_blocks(0), + // (unnamed) + m_in_bulk_push(false), + m_is_very_large_bulk(false), + m_extract_buffer_index(0), + // Number of elements currently in the data structures + m_heaps_size(0), + m_extract_buffer_size(0), + m_internal_size(0), + m_external_size(0), + // Data Holding Structures + m_proc(m_num_insertion_heaps), + m_pool(0, num_write_buffer_blocks), + m_external_arrays(), + m_minima(*this), + m_external_min_comparator(m_external_arrays, m_inv_compare), + m_external_min_tree(4, m_external_min_comparator), + m_hint_comparator(m_external_arrays, m_inv_compare), + m_hint_tree(4, m_hint_comparator), + // flags + m_limit_extract(false) + { +#if STXXL_PARALLEL + if (!omp_get_nested()) { + omp_set_nested(1); + if (!omp_get_nested()) { + STXXL_ERRMSG("Could not enable OpenMP's nested parallelism, " + "however, the PPQ requires this OpenMP feature."); + abort(); + } + } +#else + STXXL_ERRMSG("You are using stxxl::parallel_priority_queue without " + "support for OpenMP parallelism."); + STXXL_ERRMSG("This is probably not what you want, so check the " + "compilation settings."); +#endif + + if (c_limit_extract_buffer) { + m_extract_buffer_limit = (extract_buffer_ram > 0) + ? extract_buffer_ram / sizeof(value_type) + : static_cast(((double)(m_mem_total) * c_default_extract_buffer_ram_part / sizeof(value_type))); + } + + for (unsigned_type i = 0; i < c_max_internal_levels; ++i) + m_internal_levels[i] = 0; + + for (unsigned_type i = 0; i < c_max_external_levels; ++i) + m_external_levels[i] = 0; + + // TODO: Do we still need this line? Insertion heap memory is + // registered below. And merge buffer is equal to the new IA... + // total_ram - ram for the heaps - ram for the heap merger + m_mem_left = m_mem_total - 2 * m_mem_for_heaps; + + // reverse insertion heap memory on processor-local memory +#if STXXL_PARALLEL +#pragma omp parallel for +#endif + for (long p = 0; p < m_num_insertion_heaps; ++p) + { + m_proc[p] = new ProcessorData; + m_proc[p]->insertion_heap.reserve(m_insertion_heap_capacity); + assert(m_proc[p]->insertion_heap.capacity() * sizeof(value_type) + == insertion_heap_int_memory()); + } + + m_mem_left -= m_num_insertion_heaps * insertion_heap_int_memory(); + + // prepare prefetch buffer pool (already done in initializer), + // initially zero. + + // prepare write buffer pool: calculate size and subtract from mem_left + external_array_type::prepare_write_pool(m_pool, m_num_insertion_heaps); + m_mem_left -= m_pool.size_write() * block_size; + + // prepare internal arrays + if (c_merge_sorted_heaps) { + m_internal_arrays.reserve(m_mem_total / m_mem_for_heaps); + } + else { + m_internal_arrays.reserve(m_mem_total * m_num_insertion_heaps / m_mem_for_heaps); + } + + // prepare external arrays + m_external_arrays.reserve(c_num_reserved_external_arrays); + + if (m_mem_total < m_mem_left) // checks if unsigned type wrapped. + { + STXXL_ERRMSG("Minimum memory requirement insufficient, " + "increase PPQ's memory limit or decrease buffers."); + abort(); + } + + check_invariants(); + } + + //! Destructor. + ~parallel_priority_queue() + { + // clean up data structures + + for (size_t p = 0; p < m_num_insertion_heaps; ++p) + { + delete m_proc[p]; + } + } + +protected: + //! Assert many invariants of the data structures. + void check_invariants() const + { +#ifdef NDEBUG + // disable in Release builds + return; +#endif + + size_type mem_used = 0; + + mem_used += 2 * m_mem_for_heaps + + m_pool.size_write() * block_size + + m_pool.free_size_prefetch() * block_size + + m_num_hinted_blocks * block_size + + m_num_used_read_blocks * block_size; + + // count number of blocks hinted in prefetcher + + size_t num_hinted = 0, num_used_read = 0; + for (size_t i = 0; i < m_external_arrays.size(); ++i) { + num_hinted += m_external_arrays[i].num_hinted_blocks(); + num_used_read += m_external_arrays[i].num_used_blocks(); + } + + STXXL_CHECK(num_hinted == m_num_hinted_blocks); + STXXL_CHECK(num_used_read == m_num_used_read_blocks); + + STXXL_CHECK_EQUAL(m_num_used_read_blocks, + m_num_read_blocks + - m_pool.free_size_prefetch() + - m_num_hinted_blocks); + + // test the processor local data structures + + size_type heaps_size = 0; + + for (int_type p = 0; p < m_num_insertion_heaps; ++p) + { + // check that each insertion heap is a heap + + // TODO: remove soon, because this is very expensive + STXXL_CHECK(1 || stxxl::is_heap(m_proc[p]->insertion_heap.begin(), + m_proc[p]->insertion_heap.end(), + m_compare)); + + STXXL_CHECK(m_proc[p]->insertion_heap.capacity() <= m_insertion_heap_capacity); + + heaps_size += m_proc[p]->insertion_heap.size(); + mem_used += m_proc[p]->insertion_heap.capacity() * sizeof(value_type); + } + + if (!m_in_bulk_push) + STXXL_CHECK_EQUAL(m_heaps_size, heaps_size); + + // count number of items and memory size of internal arrays + + size_type ia_size = 0; + size_type ia_memory = 0; + std::vector ia_levels(c_max_internal_levels, 0); + + for (typename internal_arrays_type::const_iterator ia = + m_internal_arrays.begin(); ia != m_internal_arrays.end(); ++ia) + { + ia_size += ia->size(); + ia_memory += ia->int_memory(); + ++ia_levels[ia->level()]; + } + + STXXL_CHECK_EQUAL(m_internal_size, ia_size); + mem_used += ia_memory; + + for (unsigned_type i = 0; i < c_max_internal_levels; ++i) + STXXL_CHECK_EQUAL(m_internal_levels[i], ia_levels[i]); + + // count number of items in external arrays + + size_type ea_size = 0; + size_type ea_memory = 0; + std::vector ea_levels(c_max_external_levels, 0); + + for (typename external_arrays_type::const_iterator ea = + m_external_arrays.begin(); ea != m_external_arrays.end(); ++ea) + { + ea_size += ea->size(); + ea_memory += ea->int_memory(); + ++ea_levels[ea->level()]; + } + + STXXL_CHECK_EQUAL(m_external_size, ea_size); + mem_used += ea_memory; + + for (unsigned_type i = 0; i < c_max_external_levels; ++i) + STXXL_CHECK_EQUAL(m_external_levels[i], ea_levels[i]); + + // calculate mem_used so that == mem_total - mem_left + + STXXL_CHECK_EQUAL(memory_consumption(), mem_used); + } + + //! \} + + //! \name Properties + //! \{ + +public: + //! The number of elements in the queue. + inline size_type size() const + { + return m_heaps_size + m_internal_size + m_external_size + m_extract_buffer_size; + } + + //! Returns if the queue is empty. + inline bool empty() const + { + return (size() == 0); + } + + //! The memory consumption in Bytes. + inline size_type memory_consumption() const + { + assert(m_mem_total >= m_mem_left); + return (m_mem_total - m_mem_left); + } + +protected: + //! Returns if the extract buffer is empty. + inline bool extract_buffer_empty() const + { + return (m_extract_buffer_size == 0); + } + + //! \} + +public: + //! \name Bulk Operations + //! \{ + + /*! + * Start a sequence of push operations. + * \param bulk_size Exact number of elements to push before the next pop. + */ + void bulk_push_begin(size_type bulk_size) + { + assert(!m_in_bulk_push); + m_in_bulk_push = true; + m_bulk_first_delayed_external_array = m_external_arrays.size(); + + size_type heap_capacity = m_num_insertion_heaps * m_insertion_heap_capacity; + + // if bulk_size is large: use simple aggregation instead of keeping the + // heap property and sort everything afterwards. + if (bulk_size > heap_capacity && 0) { + m_is_very_large_bulk = true; + } + else { + m_is_very_large_bulk = false; + + if (bulk_size + m_heaps_size > heap_capacity) { + if (m_heaps_size > 0) { + //flush_insertion_heaps(); + } + } + } + + // zero bulk insertion counters + for (int_type p = 0; p < m_num_insertion_heaps; ++p) + m_proc[p]->heap_add_size = 0; + } + + /*! + * Push an element inside a sequence of pushes. + * Run bulk_push_begin() before using this method. + * + * \param element The element to push. + * \param p The id of the insertion heap to use (usually the thread id). + */ + void bulk_push(const value_type& element, const unsigned_type p) + { + assert(m_in_bulk_push); + + heap_type& insheap = m_proc[p]->insertion_heap; + + if (!m_is_very_large_bulk && 0) + { + // if small bulk: if heap is full -> sort locally and put into + // internal array list. insert items and keep heap invariant. + if (UNLIKELY(insheap.size() >= m_insertion_heap_capacity)) { +#if STXXL_PARALLEL +#pragma omp atomic +#endif + m_heaps_size += m_proc[p]->heap_add_size; + + m_proc[p]->heap_add_size = 0; + flush_insertion_heap(p); + } + + assert(insheap.size() < insheap.capacity()); + + // put item onto heap and siftUp + insheap.push_back(element); + std::push_heap(insheap.begin(), insheap.end(), m_compare); + } + else if (!m_is_very_large_bulk && 1) + { + // if small bulk: if heap is full -> sort locally and put into + // internal array list. insert items but DO NOT keep heap + // invariant. + if (UNLIKELY(insheap.size() >= m_insertion_heap_capacity)) { +#if STXXL_PARALLEL +#pragma omp atomic +#endif + m_heaps_size += m_proc[p]->heap_add_size; + + m_proc[p]->heap_add_size = 0; + flush_insertion_heap(p); + } + + assert(insheap.size() < insheap.capacity()); + + // put item onto heap and DO NOT siftUp + insheap.push_back(element); + } + else // m_is_very_large_bulk + { + if (UNLIKELY(insheap.size() >= 2 * 1024 * 1024)) { +#if STXXL_PARALLEL +#pragma omp atomic +#endif + m_heaps_size += m_proc[p]->heap_add_size; + + m_proc[p]->heap_add_size = 0; + flush_insertion_heap(p); + } + + assert(insheap.size() < insheap.capacity()); + + // put onto insertion heap but do not keep heap property + insheap.push_back(element); + } + + m_proc[p]->heap_add_size++; + } + + /*! + * Push an element inside a bulk sequence of pushes. Run bulk_push_begin() + * before using this method. This function uses the insertion heap id = + * omp_get_thread_num(). + * + * \param element The element to push. + */ + void bulk_push(const value_type& element) + { +#if STXXL_PARALLEL + return bulk_push(element, (unsigned_type)omp_get_thread_num()); +#else + unsigned_type id = m_rng() % m_num_insertion_heaps; + return bulk_push(element, id); +#endif + } + + /*! + * Ends a sequence of push operations. Run bulk_push_begin() and some + * bulk_push() before this. + */ + void bulk_push_end() + { + assert(m_in_bulk_push); + m_in_bulk_push = false; + + if (!m_is_very_large_bulk && 0) + { + for (int_type p = 0; p < m_num_insertion_heaps; ++p) + { + m_heaps_size += m_proc[p]->heap_add_size; + + if (!m_proc[p]->insertion_heap.empty()) + m_minima.update_heap(p); + } + } + else if (!m_is_very_large_bulk && 1) + { +#if STXXL_PARALLEL +#pragma omp parallel for +#endif + for (int_type p = 0; p < m_num_insertion_heaps; ++p) + { + // reestablish heap property: siftUp only those items pushed + for (unsigned_type index = m_proc[p]->heap_add_size; index != 0; ) { + std::push_heap(m_proc[p]->insertion_heap.begin(), + m_proc[p]->insertion_heap.end() - (--index), + m_compare); + } + +#if STXXL_PARALLEL +#pragma omp atomic +#endif + m_heaps_size += m_proc[p]->heap_add_size; + } + + for (int_type p = 0; p < m_num_insertion_heaps; ++p) + { + if (!m_proc[p]->insertion_heap.empty()) + m_minima.update_heap(p); + } + } + else // m_is_very_large_bulk + { +#if STXXL_PARALLEL +#pragma omp parallel for +#endif + for (int_type p = 0; p < m_num_insertion_heaps; ++p) + { + if (m_proc[p]->insertion_heap.size() >= m_insertion_heap_capacity) { + // flush out overfull insertion heap arrays +#if STXXL_PARALLEL +#pragma omp atomic +#endif + m_heaps_size += m_proc[p]->heap_add_size; + + m_proc[p]->heap_add_size = 0; + flush_insertion_heap(p); + } + else { + // reestablish heap property: siftUp only those items pushed + for (unsigned_type index = m_proc[p]->heap_add_size; index != 0; ) { + std::push_heap(m_proc[p]->insertion_heap.begin(), + m_proc[p]->insertion_heap.end() - (--index), + m_compare); + } + +#if STXXL_PARALLEL +#pragma omp atomic +#endif + m_heaps_size += m_proc[p]->heap_add_size; + m_proc[p]->heap_add_size = 0; + } + } + + for (int_type p = 0; p < m_num_insertion_heaps; ++p) + { + if (!m_proc[p]->insertion_heap.empty()) + m_minima.update_heap(p); + } + } + + if (m_bulk_first_delayed_external_array != m_external_arrays.size()) { + STXXL_DEBUG("bulk_push_end: run delayed re-hinting of EAs"); + rebuild_hint_tree(); + } + + check_invariants(); + } + + //! Extract up to max_size values at once. + void bulk_pop(std::vector& out, size_t max_size) + { + STXXL_DEBUG("bulk_pop_size with max_size=" << max_size); + + const size_t n_elements = std::min(max_size, size()); + assert(n_elements < m_extract_buffer_limit); + + if (m_heaps_size > 0) + flush_insertion_heaps(); + + convert_eb_into_ia(); + + refill_extract_buffer(n_elements, n_elements); + + out.resize(0); + using std::swap; + swap(m_extract_buffer, out); + m_extract_buffer_index = 0; + m_extract_buffer_size = 0; + m_minima.deactivate_extract_buffer(); + + check_invariants(); + } + + //! Extracts all elements which are greater or equal to a given limit. + //! \param out result vector + //! \param limit limit value + //! \param max_size maximum number of items to extract + //! \return true if the buffer contains all items < limit, false it was too + //! small. + bool bulk_pop_limit(std::vector& out, const value_type& limit, + size_t max_size = std::numeric_limits::max()) + { + STXXL_DEBUG("bulk_pop_limit with limit=" << limit); + + convert_eb_into_ia(); + + if (m_heaps_size > 0) { + if (0) + flush_insertion_heaps(); + else if (1) + flush_insertion_heaps_with_limit(limit); + } + + size_type ias = m_internal_arrays.size(); + size_type eas = m_external_arrays.size(); + std::vector sizes(eas + ias); + std::vector sequences(eas + ias); + size_type output_size = 0; + + int limiting_ea_index = m_external_min_tree.top(); + + // pop limit may have to change due to memory limit + value_type this_limit = limit; + bool has_full_range = true; + + // get all relevant blocks + while (limiting_ea_index > -1) + { + const value_type& ea_limit = + m_external_arrays[limiting_ea_index].get_next_block_min(); + + if (m_compare(ea_limit, this_limit)) { + // No more EM data smaller or equal to limit + break; + } + + if (m_external_arrays[limiting_ea_index].num_hinted_blocks() == 0) { + // No more read/prefetch blocks available for EA + this_limit = ea_limit; + has_full_range = false; + break; + } + + wait_next_ea_blocks(limiting_ea_index); + // consider next limiting EA + limiting_ea_index = m_external_min_tree.top(); + STXXL_ASSERT(limiting_ea_index < (int)eas); + } + + // build sequences + for (size_type i = 0; i < eas + ias; ++i) { + iterator begin, end; + + if (i < eas) { + assert(!m_external_arrays[i].empty()); + assert(m_external_arrays[i].valid()); + begin = m_external_arrays[i].begin(); + end = m_external_arrays[i].end(); + } + else { + size_type j = i - eas; + assert(!(m_internal_arrays[j].empty())); + begin = m_internal_arrays[j].begin(); + end = m_internal_arrays[j].end(); + } + + end = std::lower_bound(begin, end, this_limit, m_inv_compare); + + sizes[i] = std::distance(begin, end); + sequences[i] = std::make_pair(begin, end); + } + + output_size = std::accumulate(sizes.begin(), sizes.end(), 0); + if (output_size > max_size) { + output_size = max_size; + has_full_range = false; + } + out.resize(output_size); + + STXXL_DEBUG("bulk_pop_limit with" << + " sequences=" << sequences.size() << + " output_size=" << output_size << + " has_full_range=" << has_full_range); + + potentially_parallel::multiway_merge( + sequences.begin(), sequences.end(), + out.begin(), output_size, m_inv_compare); + + advance_arrays(sequences, sizes, eas, ias); + + check_invariants(); + + return has_full_range; + } + +#if TODO_MAYBE_FIXUP_LATER + /*! + * Insert a vector of elements at one time. + * \param elements Vector containing the elements to push. + * Attention: elements vector may be owned by the PQ afterwards. + */ + void bulk_push_vector(std::vector& elements) + { + size_type heap_capacity = m_num_insertion_heaps * m_insertion_heap_capacity; + if (elements.size() > heap_capacity / 2) { + flush_array(elements); + return; + } + + bulk_push_begin(elements.size()); +#if STXXL_PARALLEL + #pragma omp parallel + { + const unsigned thread_num = omp_get_thread_num(); + #pragma omp parallel for + for (size_type i = 0; i < elements.size(); ++i) { + bulk_push(elements[i], thread_num); + } + } +#else + const unsigned thread_num = m_rng() % m_num_insertion_heaps; + for (size_type i = 0; i < elements.size(); ++i) { + bulk_push(elements[i], thread_num); + } +#endif + bulk_push_end(); + } +#endif + + //! \} + + //! \name Aggregation Operations + //! \{ + + /*! + * Aggregate pushes. Use flush_aggregated_pushes() to finally push + * them. extract_min is allowed is allowed in between the aggregation of + * pushes if you can assure, that the extracted value is smaller than all + * of the aggregated values. + * \param element The element to push. + */ + void aggregate_push(const value_type& element) + { + m_aggregated_pushes.push_back(element); + } + +#if TODO_MAYBE_FIXUP_LATER + /*! + * Insert the aggregated values into the queue using push(), bulk insert, + * or sorting, depending on the number of aggregated values. + */ + void flush_aggregated_pushes() + { + size_type size = m_aggregated_pushes.size(); + size_type ram_internal = 2 * size * sizeof(value_type); // ram for the sorted array + part of the ram for the merge buffer + size_type heap_capacity = m_num_insertion_heaps * m_insertion_heap_capacity; + + if (ram_internal > m_mem_for_heaps / 2) { + flush_array(m_aggregated_pushes); + } + else if ((m_aggregated_pushes.size() > c_single_insert_limit) && (m_aggregated_pushes.size() < heap_capacity)) { + bulk_push_vector(m_aggregated_pushes); + } + else { + for (value_iterator i = m_aggregated_pushes.begin(); i != m_aggregated_pushes.end(); ++i) { + push(*i); + } + } + + m_aggregated_pushes.clear(); + } +#endif + //! \} + + //! \name std::priority_queue compliant operations + //! \{ + + /*! + * Insert new element + * \param element the element to insert. + * \param p number of insertion heap to insert item into + */ + void push(const value_type& element, unsigned_type p = 0) + { + assert(!m_in_bulk_push && !m_limit_extract); + + heap_type& insheap = m_proc[p]->insertion_heap; + + if (insheap.size() >= m_insertion_heap_capacity) { + flush_insertion_heap(p); + } + + // push item to end of heap and siftUp + insheap.push_back(element); + unsigned_type index = push_heap(insheap.begin(), insheap.end(), + m_compare); + ++m_heaps_size; + + if (insheap.size() == 1 || index == 0) + m_minima.update_heap(p); + } + + //! Access the minimum element. + const value_type & top() + { + assert(!m_in_bulk_push && !m_limit_extract); + assert(!empty()); + + if (extract_buffer_empty()) { + refill_extract_buffer(std::min(m_extract_buffer_limit, + m_internal_size + m_external_size)); + } + + static const bool debug = false; + + std::pair type_and_index = m_minima.top(); + const unsigned& type = type_and_index.first; + const unsigned& index = type_and_index.second; + + assert(type < 4); + + switch (type) { + case minima_type::HEAP: + STXXL_DEBUG("heap " << index << + ": " << m_proc[index]->insertion_heap[0]); + return m_proc[index]->insertion_heap[0]; + case minima_type::IA: + STXXL_DEBUG("ia " << index << + ": " << m_internal_arrays[index].get_min()); + return m_internal_arrays[index].get_min(); + case minima_type::EB: + STXXL_DEBUG("eb " << m_extract_buffer_index << + ": " << m_extract_buffer[m_extract_buffer_index]); + return m_extract_buffer[m_extract_buffer_index]; + default: + STXXL_ERRMSG("Unknown extract type: " << type); + abort(); + } + } + + //! Remove the minimum element. + void pop() + { + assert(!m_in_bulk_push && !m_limit_extract); + + m_stats.num_extracts++; + + if (extract_buffer_empty()) { + refill_extract_buffer(std::min(m_extract_buffer_limit, + m_internal_size + m_external_size)); + } + + m_stats.extract_min_time.start(); + + std::pair type_and_index = m_minima.top(); + unsigned type = type_and_index.first; + unsigned index = type_and_index.second; + + assert(type < 4); + + switch (type) { + case minima_type::HEAP: + { + heap_type& insheap = m_proc[index]->insertion_heap; + + m_stats.pop_heap_time.start(); + std::pop_heap(insheap.begin(), insheap.end(), m_compare); + insheap.pop_back(); + m_stats.pop_heap_time.stop(); + + m_heaps_size--; + + if (!insheap.empty()) + m_minima.update_heap(index); + else + m_minima.deactivate_heap(index); + + break; + } + case minima_type::IA: + { + m_internal_arrays[index].inc_min(); + m_internal_size--; + + if (!(m_internal_arrays[index].empty())) + m_minima.update_internal_array(index); + else + // internal array has run empty + m_minima.deactivate_internal_array(index); + + break; + } + case minima_type::EB: + { + ++m_extract_buffer_index; + assert(m_extract_buffer_size > 0); + --m_extract_buffer_size; + + if (!extract_buffer_empty()) + m_minima.update_extract_buffer(); + else + m_minima.deactivate_extract_buffer(); + + break; + } + default: + STXXL_ERRMSG("Unknown extract type: " << type); + abort(); + } + + m_stats.extract_min_time.stop(); + + check_invariants(); + } + + //! \} + + //! \name Bulk-Limit Operations + //! \{ + +protected: + //! current limit element + value_type m_limit_element; + + //! flag if inside a bulk limit extract session + bool m_limit_extract; + + //! flag if the extract buffer contains the full limit range + bool m_limit_has_full_range; + +public: + //! Begin bulk-limit extraction session with limit element. + void limit_begin(const value_type& limit, size_type bulk_size) + { + m_limit_extract = true; + m_limit_element = limit; + + std::vector new_extract_buffer; + m_limit_has_full_range = + bulk_pop_limit(new_extract_buffer, limit, m_extract_buffer_limit); + std::swap(new_extract_buffer, m_extract_buffer); + + m_extract_buffer_index = 0; + m_extract_buffer_size = m_extract_buffer.size(); + if (m_extract_buffer_size) + m_minima.update_extract_buffer(); + else + m_minima.deactivate_extract_buffer(); + + bulk_push_begin(bulk_size); + } + + //! Push new item >= bulk-limit element into insertion heap p. + void limit_push(const value_type& element, const unsigned_type p = 0) + { + assert(m_limit_extract); + assert(!m_compare(m_limit_element, element)); + + return bulk_push(element, p); + } + + //! Access the minimum element, which can only be in the extract buffer. + const value_type & limit_top() + { + assert(m_limit_extract); + + // if buffer is empty and we extracted the full range last time, return + // limit items as sentinel. + if (m_extract_buffer_size == 0 && m_limit_has_full_range) + return m_limit_element; + + if (extract_buffer_empty()) + { + // extract more items + std::vector new_extract_buffer; + m_limit_has_full_range = + bulk_pop_limit(new_extract_buffer, m_limit_element, + m_extract_buffer_limit); + + std::swap(new_extract_buffer, m_extract_buffer); + + m_extract_buffer_index = 0; + m_extract_buffer_size = m_extract_buffer.size(); + if (m_extract_buffer_size) + m_minima.update_extract_buffer(); + else + m_minima.deactivate_extract_buffer(); + } + + return m_extract_buffer[m_extract_buffer_index]; + } + + //! Remove the minimum element, only works correctly while elements < L. + void limit_pop() + { + assert(m_limit_extract); + + ++m_extract_buffer_index; + assert(m_extract_buffer_size > 0); + --m_extract_buffer_size; + + if (extract_buffer_empty() && !m_limit_has_full_range) + { + // extract more items + std::vector new_extract_buffer; + m_limit_has_full_range = + bulk_pop_limit(new_extract_buffer, m_limit_element, + m_extract_buffer_limit); + + std::swap(new_extract_buffer, m_extract_buffer); + + m_extract_buffer_index = 0; + m_extract_buffer_size = m_extract_buffer.size(); + if (m_extract_buffer_size) + m_minima.update_extract_buffer(); + else + m_minima.deactivate_extract_buffer(); + } + } + + //! Finish bulk-limit extraction session. + void limit_end() + { + assert(m_limit_extract); + + bulk_push_end(); + + m_limit_extract = false; + } + + //! \} + +protected: + //! Flushes all elements of the insertion heaps which are greater + //! or equal to a given limit. + //! \param limit limit value + void flush_insertion_heaps_with_limit(const value_type& limit) + { + // perform extract for all items < L into back of insertion_heap + std::vector back_size(m_num_insertion_heaps); + +//#if STXXL_PARALLEL +//#pragma omp parallel for +//#endif + for (size_t p = 0; p < m_num_insertion_heaps; ++p) + { + heap_type& insheap = m_proc[p]->insertion_heap; + + typename heap_type::iterator back = insheap.end(); + + while (back != insheap.begin() && + m_compare(limit, insheap[0])) + { + // while top < L, perform pop_heap: put top to back and + // siftDown new items (shortens heap by one) + std::pop_heap(insheap.begin(), back, m_compare); + --back; + } + + // range insheap.begin() + back to insheap.end() is < L, rest >= L. + + for (typename heap_type::const_iterator it = insheap.begin(); + it != insheap.end(); ++it) + { + if (it < back) + assert(!m_compare(limit, *it)); + else + assert(m_compare(limit, *it)); + } + + back_size[p] = insheap.end() - back; + } + + // put items from insertion heaps into an internal array + unsigned_type back_sum = std::accumulate( + back_size.begin(), back_size.end(), unsigned_type(0)); + + STXXL_DEBUG("flush_insertion_heaps_with_limit(): back_sum = " << back_sum); + + if (back_sum) + { + // test that enough RAM is available for remaining items + flush_ia_ea_until_memory_free(back_sum * sizeof(value_type)); + + std::vector values(back_sum); + + // copy items into values vector + typename std::vector::iterator vi = values.begin(); + for (size_t p = 0; p < m_num_insertion_heaps; ++p) + { + heap_type& insheap = m_proc[p]->insertion_heap; + + std::copy(insheap.end() - back_size[p], insheap.end(), vi); + vi += back_size[p]; + insheap.resize(insheap.size() - back_size[p]); + + if (insheap.empty()) + m_minima.deactivate_heap(p); + else + m_minima.update_heap(p); + } + + potentially_parallel::sort(values.begin(), values.end(), m_inv_compare); + + add_as_internal_array(values); + m_heaps_size -= back_sum; + } + } + +public: + /*! + * Merges all external arrays and all internal arrays into one external array. + * Public for benchmark purposes. + */ + void merge_external_arrays() + { + STXXL_ERRMSG("Merging external arrays. This should not happen." + << " You should adjust memory assignment and/or external array level size."); + check_external_level(0, true); + STXXL_DEBUG("Merging all external arrays done."); + + resize_read_pool(); + + // Rebuild hint tree completely as the hint sequence may have changed. + if (!m_in_bulk_push) + rebuild_hint_tree(); + else + assert(m_external_arrays.size() - 1 >= m_bulk_first_delayed_external_array); + + check_invariants(); + } + + //! Free up memory by flushing internal arrays and combining external + //! arrays until enough bytes are free. + void flush_ia_ea_until_memory_free(internal_size_type mem_free) + { + if (m_mem_left >= mem_free) return; + + if (m_internal_size > 0) { + flush_internal_arrays(); + } + else { + merge_external_arrays(); + } + + assert(m_mem_left >= mem_free); + } + + //! Automatically resize the read/prefetch buffer pool depending on number + //! of external arrays. + void resize_read_pool() + { + unsigned_type new_num_read_blocks = + m_num_read_blocks_per_ea * m_external_arrays.size(); + + STXXL_DEBUG("resize_read_pool:" << + " m_num_read_blocks=" << m_num_read_blocks << + " ea_size=" << m_external_arrays.size() << + " m_num_read_blocks_per_ea=" << m_num_read_blocks_per_ea << + " new_num_read_blocks=" << new_num_read_blocks << + " free_size_prefetch=" << m_pool.free_size_prefetch() << + " m_num_hinted_blocks=" << m_num_hinted_blocks << + " m_num_used_read_blocks=" << m_num_used_read_blocks); + + // add new blocks + if (new_num_read_blocks > m_num_read_blocks) + { + unsigned_type mem_needed = + (new_num_read_blocks - m_num_read_blocks) * block_size; + + // -tb: this may recursively call this function! + //flush_ia_ea_until_memory_free(mem_needed); + STXXL_ASSERT(m_mem_left >= mem_needed); + + while (new_num_read_blocks > m_num_read_blocks) { + block_type* new_block = new block_type(); + m_pool.add_prefetch(new_block); + ++m_num_read_blocks; + } + + m_mem_left -= mem_needed; + } + + // steal extra blocks (as many as possible) + if (new_num_read_blocks < m_num_read_blocks) + { + while (new_num_read_blocks < m_num_read_blocks && + m_pool.free_size_prefetch() > 0) + { + block_type* del_block = m_pool.steal_prefetch(); + delete del_block; + --m_num_read_blocks; + m_mem_left += block_size; + } + + if (new_num_read_blocks < m_num_read_blocks) + STXXL_ERRMSG("WARNING: could not immediately reduce read/prefetch pool!"); + } + } + + //! Rebuild hint tree completely as the hint sequence may have changed, and + //! re-hint the correct block sequence. + void rebuild_hint_tree() + { + m_stats.hint_time.start(); + + // prepare rehinting sequence: reset hint begin pointer + for (size_t i = 0; i < m_external_arrays.size(); ++i) + m_external_arrays[i].rebuild_hints_prepare(); + + // rebuild hint tree with first elements + for (size_t i = 0; i < m_external_arrays.size(); ++i) + { + if (m_external_arrays[i].has_unhinted_em_data()) { + m_hint_tree.activate_without_replay(i); + } + else { + m_hint_tree.deactivate_without_replay(i); + } + } + m_hint_tree.rebuild(); + + // virtually release all hints + unsigned_type free_prefetch_blocks = + m_pool.free_size_prefetch() + m_num_hinted_blocks; + m_num_hinted_blocks = 0; + + int gmin_index; + while (free_prefetch_blocks > 0 && + (gmin_index = m_hint_tree.top()) >= 0) + { + assert((size_t)gmin_index < m_external_arrays.size()); + + STXXL_DEBUG("Give pre-hint in EA[" << gmin_index << "] min " << + m_external_arrays[gmin_index].get_next_hintable_min()); + + m_external_arrays[gmin_index].rebuild_hints_prehint_next_block(); + --free_prefetch_blocks; + ++m_num_hinted_blocks; + + if (m_external_arrays[gmin_index].has_unhinted_em_data()) { + m_hint_tree.replay_on_change(gmin_index); + } + else { + m_hint_tree.deactivate_player(gmin_index); + } + } + + // invalidate all hinted blocks no longer needed + for (size_t i = 0; i < m_external_arrays.size(); ++i) + m_external_arrays[i].rebuild_hints_cancel(); + + // perform real hinting on pre-hinted blocks + for (size_t i = 0; i < m_external_arrays.size(); ++i) + m_external_arrays[i].rebuild_hints_finish(); + + assert(free_prefetch_blocks == m_pool.free_size_prefetch()); + + m_stats.hint_time.stop(); + } + + //! Updates the prefetch prediction tree afer a remove_items(), which frees + //! up blocks. + //! \param ea_index index of the external array in question + inline void update_hint_tree(size_t ea_index) + { + m_stats.hint_time.start(); + if (m_external_arrays[ea_index].has_unhinted_em_data()) { + m_hint_tree.replay_on_change(ea_index); + } + else { + m_hint_tree.deactivate_player(ea_index); + } + m_stats.hint_time.stop(); + } + + //! Updates the external min tree afer a remove() or a + //! wait_next_blocks() call. + //! \param ea_index index of the external array in question + inline void update_external_min_tree(size_t ea_index) + { + if (m_external_arrays[ea_index].has_em_data()) { + m_external_min_tree.replay_on_change(ea_index); + } + else { + m_external_min_tree.deactivate_player(ea_index); + } + } + + //! Hints EA blocks which will be needed soon. Hints at most + //! m_num_prefetchers blocks globally. + inline void hint_external_arrays() + { + m_stats.hint_time.start(); + + STXXL_DEBUG("hint_external_arrays()" + " for free_size_prefetch=" << m_pool.free_size_prefetch()); + + int gmin_index; + while (m_pool.free_size_prefetch() > 0 && + (gmin_index = m_hint_tree.top()) >= 0) + { + assert((size_t)gmin_index < m_external_arrays.size()); + + STXXL_DEBUG("Give hint in EA[" << gmin_index << "]"); + m_external_arrays[gmin_index].hint_next_block(); + ++m_num_hinted_blocks; + + if (m_external_arrays[gmin_index].has_unhinted_em_data()) { + m_hint_tree.replay_on_change(gmin_index); + } + else { + m_hint_tree.deactivate_player(gmin_index); + } + } + + m_stats.hint_time.stop(); + } + + //! Print statistics. + void print_stats() const + { + STXXL_VARDUMP(c_merge_sorted_heaps); + STXXL_VARDUMP(c_limit_extract_buffer); + STXXL_VARDUMP(c_single_insert_limit); + + if (c_limit_extract_buffer) { + STXXL_VARDUMP(m_extract_buffer_limit); + STXXL_MEMDUMP(m_extract_buffer_limit * sizeof(value_type)); + } + +#if STXXL_PARALLEL + STXXL_VARDUMP(omp_get_max_threads()); +#endif + + STXXL_MEMDUMP(m_mem_for_heaps); + STXXL_MEMDUMP(m_mem_left); + + //if (num_extract_buffer_refills > 0) { + // STXXL_VARDUMP(total_extract_buffer_size / num_extract_buffer_refills); + // STXXL_MEMDUMP(total_extract_buffer_size / num_extract_buffer_refills * sizeof(value_type)); + //} + + STXXL_MSG(m_stats); + m_minima.print_stats(); + } + +protected: + //! Calculates the sequences vector needed by the multiway merger, + //! considering inaccessible data from external arrays. + //! The sizes vector stores the size of each sequence. + //! \param reuse_previous_lower_bounds Reuse upper bounds from previous runs. + //! sequences[i].second must be valid upper bound iterator from a previous run! + //! \returns the index of the external array which is limiting factor + //! or m_external_arrays.size() if not limited. + size_t calculate_merge_sequences(std::vector& sizes, + std::vector& sequences, + bool reuse_previous_lower_bounds = false) + { + STXXL_DEBUG("calculate merge sequences"); + + static const bool debug = false; + + const size_type eas = m_external_arrays.size(); + const size_type ias = m_internal_arrays.size(); + + assert(sizes.size() == eas + ias); + assert(sequences.size() == eas + ias); + + /* + * determine minimum of each first block + */ + + int gmin_index = m_external_min_tree.top(); + bool needs_limit = (gmin_index >= 0) ? true : false; + +// test correctness of external block min tree +#ifdef STXXL_DEBUG_ASSERTIONS + + bool test_needs_limit = false; + int test_gmin_index = 0; + value_type test_gmin_value; + + m_stats.refill_minmax_time.start(); + for (size_type i = 0; i < eas; ++i) { + if (m_external_arrays[i].has_em_data()) { + const value_type& min_value = + m_external_arrays[i].get_next_block_min(); + + if (!test_needs_limit) { + test_needs_limit = true; + test_gmin_value = min_value; + test_gmin_index = i; + } + else { + STXXL_DEBUG("min[" << i << "]: " << min_value << + " test: " << test_gmin_value << + ": " << m_inv_compare(min_value, test_gmin_value)); + if (m_inv_compare(min_value, test_gmin_value)) { + test_gmin_value = min_value; + test_gmin_index = i; + } + } + } + } + m_stats.refill_minmax_time.stop(); + + STXXL_ASSERT(needs_limit == test_needs_limit); + STXXL_ASSERT(!needs_limit || gmin_index == test_gmin_index); + +#endif + + /* + * calculate size and create sequences to merge + */ + +#if STXXL_PARALLEL +// #pragma omp parallel for if(eas + ias > m_num_insertion_heaps) +#endif + for (size_type i = 0; i < eas + ias; ++i) { + iterator begin, end; + + if (i < eas) { + begin = m_external_arrays[i].begin(); + end = m_external_arrays[i].end(); + } + else { + size_type j = i - eas; + begin = m_internal_arrays[j].begin(); + end = m_internal_arrays[j].end(); + } + + if (needs_limit) { + const value_type& gmin_value = + m_external_arrays[gmin_index].get_next_block_min(); + + // remove timer if parallel + //stats.refill_lower_bound_time.start(); + if (reuse_previous_lower_bounds) { + // Be careful that sequences[i].second is really valid and + // set by a previous calculate_merge_sequences() run! + end = std::lower_bound(sequences[i].second, end, + gmin_value, m_inv_compare); + } + else + { + end = std::lower_bound(begin, end, + gmin_value, m_inv_compare); + } + //stats.refill_lower_bound_time.stop(); + } + + sizes[i] = std::distance(begin, end); + sequences[i] = std::make_pair(begin, end); + + STXXL_DEBUG("sequence[" << i << "] " << (i < eas ? "ea " : "ia ") << + begin << " - " << end << + " size " << sizes[i] << + (needs_limit ? " with ub limit" : "")); + } + + if (needs_limit) { + STXXL_DEBUG("return with needs_limit: gmin_index=" << gmin_index); + return gmin_index; + } + else { + STXXL_DEBUG("return with needs_limit: eas=" << eas); + return eas; + } + } + +protected: + //! Convert extract buffer into a new internal array. + void convert_eb_into_ia(bool do_not_flush = false) + { + if (m_extract_buffer_size == 0) return; + + STXXL_DEBUG("convert_eb_into_ia"); + + // tb: if in limit sequence and the EB gets flushed out to EM, then we + // have to re-merge items into the EB instead of returning the + // sentinel. + m_limit_has_full_range = false; + + // TODO: memory is NOT allocated, but extract buffer is currently not + // counted + if (!do_not_flush) + flush_ia_ea_until_memory_free( + internal_array_type::int_memory(m_extract_buffer.size()) + ); + + if (m_extract_buffer_size == 0) return; + + // first deactivate extract buffer to replay tree for new IA. + m_minima.deactivate_extract_buffer(); + + // add eb as internal array with current index + add_as_internal_array(m_extract_buffer, m_extract_buffer_index); + + m_extract_buffer_index = 0; + m_extract_buffer_size = 0; + } + + //! Refills the extract buffer from the external arrays. + //! \param minimum_size requested minimum size of the resulting extract buffer. + //! Prints a warning if there is not enough data to reach this size. + //! \param maximum_size maximum size of the extract buffer. Using + //! m_extract_buffer_limit if set to 0. + inline void refill_extract_buffer(size_t minimum_size = 0, + size_t maximum_size = 0) + { + STXXL_DEBUG("refilling extract buffer" << + " ia_size=" << m_internal_arrays.size() << + " ea_size=" << m_external_arrays.size()); + + if (maximum_size == 0) + maximum_size = m_extract_buffer_limit; + + check_invariants(); + + assert(extract_buffer_empty()); + m_extract_buffer_index = 0; + + cleanup_external_arrays(); + + size_type ias, eas = m_external_arrays.size(); + + m_minima.clear_internal_arrays(); + cleanup_internal_arrays(); + ias = m_internal_arrays.size(); + + if (eas == 0 && ias == 0) { + m_extract_buffer.resize(0); + m_minima.deactivate_extract_buffer(); + return; + } + + m_stats.num_extract_buffer_refills++; + m_stats.refill_extract_buffer_time.start(); + m_stats.refill_time_before_merge.start(); + + std::vector sizes(eas + ias); + std::vector sequences(eas + ias); + size_type output_size = 0; + + if (minimum_size > 0) { + size_t limiting_ea_index = eas + 1; + bool reuse_lower_bounds = false; + while (output_size < minimum_size) + { + STXXL_DEBUG("refill: request more data," << + " output_size=" << output_size << + " minimum_size=" << minimum_size << + " limiting_ea_index=" << limiting_ea_index); + + if (limiting_ea_index < eas) { + if (m_external_arrays[limiting_ea_index].num_hinted_blocks() == 0) + break; + + wait_next_ea_blocks(limiting_ea_index); + reuse_lower_bounds = true; + } + else if (limiting_ea_index == eas) { + // no more unaccessible EM data + STXXL_MSG("Warning: refill_extract_buffer(n): " + "minimum_size > # mergeable elements!"); + break; + } + + limiting_ea_index = calculate_merge_sequences( + sizes, sequences, reuse_lower_bounds); + + output_size = std::accumulate(sizes.begin(), sizes.end(), 0); + } + } + else { + calculate_merge_sequences(sizes, sequences); + output_size = std::accumulate(sizes.begin(), sizes.end(), 0); + } + + if (c_limit_extract_buffer) { + output_size = std::min(output_size, maximum_size); + } + + m_stats.max_extract_buffer_size.set_max(output_size); + m_stats.total_extract_buffer_size += output_size; + + assert(output_size > 0); + m_extract_buffer.resize(output_size); + m_extract_buffer_size = output_size; + + m_stats.refill_time_before_merge.stop(); + m_stats.refill_merge_time.start(); + + potentially_parallel::multiway_merge( + sequences.begin(), sequences.end(), + m_extract_buffer.begin(), output_size, m_inv_compare); + + m_stats.refill_merge_time.stop(); + m_stats.refill_time_after_merge.start(); + + advance_arrays(sequences, sizes, eas, ias); + + m_minima.update_extract_buffer(); + + m_stats.refill_time_after_merge.stop(); + m_stats.refill_extract_buffer_time.stop(); + + check_invariants(); + } + + //! Requests more EM data from a given EA and updates + //! the winner trees and hints accordingly. + inline void wait_next_ea_blocks(unsigned_type ea_index) + { + unsigned_type used_blocks = + m_external_arrays[ea_index].wait_next_blocks(); + + m_num_hinted_blocks -= used_blocks; + m_num_used_read_blocks += used_blocks; + + update_external_min_tree(ea_index); + } + + // Removes empty arrays and updates the winner trees accordingly + inline void advance_arrays(std::vector& sequences, + std::vector& sizes, + size_t eas, size_t ias) + { + unsigned_type total_freed_blocks = 0; + + for (size_type i = 0; i < eas + ias; ++i) { + // dist represents the number of elements that haven't been merged + size_type dist = std::distance(sequences[i].first, + sequences[i].second); + const size_t diff = sizes[i] - dist; + if (diff == 0) continue; + + if (i < eas) { + // remove items and free blocks in RAM. + unsigned_type freed_blocks = + m_external_arrays[i].remove_items(diff); + + m_num_used_read_blocks -= freed_blocks; + total_freed_blocks += freed_blocks; + + // correct item count. + assert(m_external_size >= diff); + m_external_size -= diff; + } + else { + size_type j = i - eas; + m_internal_arrays[j].inc_min(diff); + assert(m_internal_size >= diff); + m_internal_size -= diff; + } + } + + // remove empty arrays - important for the next round (may also reduce + // number of prefetch buffers, so must be before hinting). + cleanup_external_arrays(); + + // prefetch new blocks from EAs using freed blocks + if (total_freed_blocks) + hint_external_arrays(); + + m_stats.num_new_external_arrays = 0; + cleanup_internal_arrays(); + } + + //! Flushes the insertions heap p into an internal array. + inline void flush_insertion_heap(unsigned_type p) + { + assert(m_proc[p]->insertion_heap.size() != 0); + + heap_type& insheap = m_proc[p]->insertion_heap; + size_t size = insheap.size(); + + STXXL_DEBUG0( + "Flushing insertion heap array p=" << p << + " size=" << insheap.size() << + " capacity=" << insheap.capacity() << + " int_memory=" << internal_array_type::int_memory(insheap.size()) << + " mem_left=" << m_mem_left); + + m_stats.num_insertion_heap_flushes++; + stats_timer flush_time(true); // separate timer due to parallel sorting + + // sort locally, independent of others + std::sort(insheap.begin(), insheap.end(), m_inv_compare); + +#if STXXL_PARALLEL +#pragma omp critical (stxxl_flush_insertion_heap) +#endif + { + // test that enough RAM is available for merged internal array: + // otherwise flush the existing internal arrays out to disk. + flush_ia_ea_until_memory_free( + internal_array_type::int_memory(insheap.size())); + + // invalidate player in minima tree (before adding the IA to tree) + m_minima.deactivate_heap(p); + + // insheap is empty afterwards, as vector was swapped into new_array + add_as_internal_array(insheap); + + // reserve new insertion heap + insheap.reserve(m_insertion_heap_capacity); + assert(insheap.capacity() * sizeof(value_type) + == insertion_heap_int_memory()); + + // update item counts +#if STXXL_PARALLEL +#pragma omp atomic +#endif + m_heaps_size -= size; + } + + m_stats.insertion_heap_flush_time += flush_time; + } + + //! Flushes all insertions heaps into an internal array. + inline void flush_insertion_heaps() + { + size_type max_mem_needed; + + if (c_merge_sorted_heaps) { + max_mem_needed = m_mem_for_heaps; + } + else { + max_mem_needed = insertion_heap_int_memory(); + } + + // test that enough RAM is available for merged internal array: + // otherwise flush the existing internal arrays out to disk. + flush_ia_ea_until_memory_free(max_mem_needed); + + m_stats.num_insertion_heap_flushes++; + m_stats.insertion_heap_flush_time.start(); + + size_type size = m_heaps_size; + size_type int_memory = 0; + assert(size > 0); + std::vector > sequences(m_num_insertion_heaps); + +#if STXXL_PARALLEL + #pragma omp parallel for +#endif + for (long i = 0; i < m_num_insertion_heaps; ++i) + { + heap_type& insheap = m_proc[i]->insertion_heap; + + std::sort(insheap.begin(), insheap.end(), m_inv_compare); + + if (c_merge_sorted_heaps) + sequences[i] = std::make_pair(insheap.begin(), insheap.end()); + + int_memory += insheap.capacity(); + } + + if (c_merge_sorted_heaps) + { + m_stats.merge_sorted_heaps_time.start(); + std::vector merged_array(size); + + potentially_parallel::multiway_merge( + sequences.begin(), sequences.end(), + merged_array.begin(), size, m_inv_compare); + + m_stats.merge_sorted_heaps_time.stop(); + + add_as_internal_array(merged_array); + + for (int_type i = 0; i < m_num_insertion_heaps; ++i) + { + m_proc[i]->insertion_heap.clear(); + m_proc[i]->insertion_heap.reserve(m_insertion_heap_capacity); + } + m_minima.clear_heaps(); + } + else + { + for (unsigned i = 0; i < m_num_insertion_heaps; ++i) + { + heap_type& insheap = m_proc[i]->insertion_heap; + + if (insheap.size() == 0) continue; + + add_as_internal_array(insheap); + + // reserve new insertion heap + insheap.reserve(m_insertion_heap_capacity); + } + + m_minima.clear_heaps(); + } + + m_heaps_size = 0; + + m_stats.insertion_heap_flush_time.stop(); + + check_invariants(); + } + + //! Flushes the internal arrays into an external array. + void flush_internal_arrays() + { + STXXL_DEBUG("Flushing internal arrays" << + " num_arrays=" << m_internal_arrays.size()); + + m_stats.num_internal_array_flushes++; + m_stats.internal_array_flush_time.start(); + + m_minima.clear_internal_arrays(); + + // also flush extract buffer items out to disk. + convert_eb_into_ia(true); + + // clean up internal arrays that have been deleted in extract_min! + cleanup_internal_arrays(); + + size_type num_arrays = m_internal_arrays.size(); + size_type size = m_internal_size; + size_type int_memory = 0; + std::vector sequences(num_arrays); + + for (unsigned i = 0; i < num_arrays; ++i) + { + sequences[i] = std::make_pair(m_internal_arrays[i].begin(), + m_internal_arrays[i].end()); + + int_memory += m_internal_arrays[i].int_memory(); + } + + // must release more RAM in IAs than the EA takes, otherwise: merge + // external and internal arrays! + if (int_memory < external_array_type::int_memory(size) + + ceil(m_num_read_blocks_per_ea) * block_size) + { + return merge_external_arrays(); + } + + // construct new external array + + external_array_type ea(size, &m_pool, 0); + + m_stats.max_merge_buffer_size.set_max(size); + + { + external_array_writer_type external_array_writer(ea); + + potentially_parallel::multiway_merge( + sequences.begin(), sequences.end(), + external_array_writer.begin(), size, m_inv_compare); + } + + STXXL_DEBUG("Merge done of new ea " << &ea); + + m_external_arrays.swap_back(ea); + + m_internal_size = 0; + m_external_size += size; + + // register EA in min tree + // important for check_external_level()! + m_external_min_tree.activate_without_replay(m_external_arrays.size() - 1); + update_external_min_tree(m_external_arrays.size() - 1); + + // register EA in hint tree + m_hint_tree.activate_without_replay(m_external_arrays.size() - 1); + if (!m_in_bulk_push) + update_hint_tree(m_external_arrays.size() - 1); + // else: done in bulk_push_end() -> rebuild_hint_tree() + + m_internal_arrays.clear(); + m_stats.num_new_internal_arrays = 0; + cleanup_internal_arrays(); + + // TODO: is this necessary? See cleanup_internal_arrays(). + for (size_t i = 0; i < c_max_internal_levels; ++i) + m_internal_levels[i] = 0; + + m_mem_left += int_memory; + m_mem_left -= m_external_arrays.back().int_memory(); + + m_stats.max_num_external_arrays.set_max(m_external_arrays.size()); + m_stats.internal_array_flush_time.stop(); + + // update EA level and potentially merge + ++m_external_levels[0]; + check_external_level(0); + + resize_read_pool(); + // Rebuild hint tree completely as the hint sequence may have changed. + if (!m_in_bulk_push) + rebuild_hint_tree(); + else + assert(m_external_arrays.size() - 1 >= m_bulk_first_delayed_external_array); + + check_invariants(); + } + + // Compares the largest accessible value of two external arrays. + struct s_min_tree_comparator { + const external_arrays_type& m_eas; + const std::vector& m_indices; + const inv_compare_type& m_compare; + + s_min_tree_comparator(const external_arrays_type& eas, + const inv_compare_type& compare, + const std::vector& indices) + : m_eas(eas), m_indices(indices), m_compare(compare) { } + + bool operator () (const size_t& a, const size_t& b) const + { + return m_compare(m_eas[m_indices[a]].get_next_hintable_min(), + m_eas[m_indices[b]].get_next_hintable_min()); + } + }; + + //! Merges external arrays if there are too many external arrays on + //! the same level. + void check_external_level(unsigned_type level, bool force_merge_all = false) + { + if (!force_merge_all) + STXXL_DEBUG("Checking external level " << level); + + // return if EA level is not full + if (m_external_levels[level] < c_max_external_level_size && !force_merge_all) + return; + + unsigned_type level_size = 0; + size_type int_memory = 0; + std::vector ea_index; + + for (unsigned_type i = 0; i < m_external_arrays.size(); ++i) + { + if (m_external_arrays[i].level() != level && !force_merge_all) continue; + if (m_external_arrays[i].empty()) continue; + + level_size += m_external_arrays[i].size(); + int_memory += m_external_arrays[i].int_memory(); + ea_index.push_back(i); + } + + // return if there is not enough RAM for the new array. + // TODO: force_merge_all==true is for freeing memory. Breaking here is not + // helpful in this case. But one should maybe reserve some space in advance. + if (m_mem_left < external_array_type::int_memory(level_size) && !force_merge_all) + return; + m_mem_left -= external_array_type::int_memory(level_size); + + STXXL_ASSERT(force_merge_all || c_max_external_level_size == ea_index.size()); + unsigned_type num_arrays_to_merge = ea_index.size(); + + STXXL_DEBUG("merging external arrays" << + " level=" << level << + " level_size=" << level_size << + " sequences=" << num_arrays_to_merge << + " force_merge_all=" << force_merge_all); + + // if force_merge_all: create array in highest level to avoid merging + // of such a large EA. + unsigned_type new_level = force_merge_all ? c_max_external_levels - 1 : level + 1; + + // construct new external array + external_array_type ea(level_size, &m_pool, new_level); + { + external_array_writer_type external_array_writer(ea); + typename external_array_writer_type::iterator out_iter + = external_array_writer.begin(); + + // === build minima_tree over the level's arrays === + + s_min_tree_comparator min_tree_comparator(m_external_arrays, + m_inv_compare, ea_index); + + winner_tree min_tree(num_arrays_to_merge, + min_tree_comparator); + + // ================================================= + + int_type num_arrays_done = 0; + + while (num_arrays_to_merge != num_arrays_done) + { + STXXL_DEBUG("num_arrays_done = " << num_arrays_done); + + // === build hints === + + for (int_type i = 0; i < num_arrays_to_merge; ++i) { + if (m_external_arrays[ea_index[i]].has_unhinted_em_data()) { + min_tree.activate_without_replay(i); + } + else { + min_tree.deactivate_without_replay(i); + } + } + + min_tree.rebuild(); + + // === fill available memory with read blocks === + while (m_mem_left >= block_size) { + block_type* new_block = new block_type(); + m_pool.add_prefetch(new_block); + ++m_num_read_blocks; + m_mem_left -= block_size; + } + // ============================================== + + // cleanup hints (all arrays, not only the ones to merge) + for (unsigned_type i = 0; i < m_external_arrays.size(); ++i) { + m_external_arrays[i].rebuild_hints_prepare(); + } + + // virtually release all hints + unsigned_type free_prefetch_blocks = + m_pool.free_size_prefetch() + m_num_hinted_blocks; + m_num_hinted_blocks = 0; + + int gmin_index_index; // index in ea_index + while (free_prefetch_blocks > 0 && + (gmin_index_index = min_tree.top()) >= 0) + { + const unsigned_type gmin_index = ea_index[gmin_index_index]; + assert(gmin_index < m_external_arrays.size()); + + STXXL_DEBUG0("check_external_level():Give pre-hint in EA[" << gmin_index << "] min " << + m_external_arrays[gmin_index].get_next_hintable_min()); + + m_external_arrays[gmin_index].rebuild_hints_prehint_next_block(); + --free_prefetch_blocks; + ++m_num_hinted_blocks; + + if (m_external_arrays[gmin_index].has_unhinted_em_data()) { + min_tree.replay_on_change(gmin_index_index); + } + else { + min_tree.deactivate_player(gmin_index_index); + } + } + + // invalidate all hinted blocks no longer needed + // (all arrays, not only the ones to merge) + for (size_t i = 0; i < m_external_arrays.size(); ++i) + m_external_arrays[i].rebuild_hints_cancel(); + + // perform real hinting on pre-hinted blocks + // (all arrays, not only the ones to merge) + for (size_t i = 0; i < m_external_arrays.size(); ++i) + m_external_arrays[i].rebuild_hints_finish(); + + assert(free_prefetch_blocks == m_pool.free_size_prefetch()); + + // ================================ end build hints ====== + + // === wait for data === + for (size_type i = 0; i < num_arrays_to_merge; ++i) { + const unsigned_type index = ea_index[i]; + + unsigned_type used_blocks = + m_external_arrays[index].wait_all_hinted_blocks(); + + m_num_hinted_blocks -= used_blocks; + m_num_used_read_blocks += used_blocks; + } + // ===================== + + // === build sequences === + std::vector sequences(num_arrays_to_merge); + std::vector sizes(num_arrays_to_merge); + + gmin_index_index = min_tree.top(); + bool needs_limit = (gmin_index_index >= 0) ? true : false; + + for (size_type i = 0; i < num_arrays_to_merge; ++i) { + const unsigned_type index = ea_index[i]; + iterator begin = m_external_arrays[index].begin(); + iterator end = m_external_arrays[index].end(); + + if (needs_limit) { + const unsigned_type gmin_index = ea_index[gmin_index_index]; + const value_type& gmin_value = + m_external_arrays[gmin_index].get_next_block_min(); + + end = std::lower_bound(begin, end, + gmin_value, m_inv_compare); + } + + sizes[i] = std::distance(begin, end); + sequences[i] = std::make_pair(begin, end); + + STXXL_DEBUG("sequence[" << i << "] ea " << + begin << " - " << end << + " size " << sizes[i] << + (needs_limit ? " with ub limit" : "")); + } + // ========================================== + + // === merge === + + size_type output_size = std::accumulate(sizes.begin(), sizes.end(), 0); + + out_iter = potentially_parallel::multiway_merge( + sequences.begin(), sequences.end(), + out_iter, output_size, m_inv_compare); + + for (unsigned_type i = 0; i < num_arrays_to_merge; ++i) { + const unsigned_type index = ea_index[i]; + + if (!m_external_arrays[index].empty()) { + // remove items and free blocks in RAM. + unsigned_type freed_blocks = + m_external_arrays[index].remove_items(sizes[i]); + + m_num_used_read_blocks -= freed_blocks; + + if (m_external_arrays[index].empty()) + ++num_arrays_done; + } + } + + // reset read buffer + resize_read_pool(); + + // cannot call clear_external_arrays() here, since it + // corrupts ea_index. + } + + if (m_in_bulk_push) + m_bulk_first_delayed_external_array = 0; // TODO: workaround + } // destroy external_array_writer + + // clean up now empty arrays + cleanup_external_arrays(); + + m_external_arrays.swap_back(ea); + ++m_external_levels[new_level]; + + // register EA in min tree + m_external_min_tree.activate_without_replay(m_external_arrays.size() - 1); + update_external_min_tree(m_external_arrays.size() - 1); + + // register EA in hint tree + m_hint_tree.activate_without_replay(m_external_arrays.size() - 1); + if (!m_in_bulk_push) + update_hint_tree(m_external_arrays.size() - 1); + // else: done in bulk_push_end() -> rebuild_hint_tree() + + STXXL_DEBUG("Merge done of new ea " << &ea); + + if (!force_merge_all) + check_external_level(level + 1); + + check_invariants(); + } + + //! Add new internal array, which requires that values are sorted! + //! automatically decreases m_mem_left! also merges internal arrays if + //! there are too many internal arrays on the same level. + void add_as_internal_array(std::vector& values, + unsigned_type used = 0, + unsigned_type level = 0) + { + const size_t size = values.size(); + const size_t capacity = values.capacity(); + assert(size > used); // at least one element + + internal_array_type new_array(values, used, level); + STXXL_ASSERT(new_array.int_memory() == + internal_array_type::int_memory(capacity)); + m_internal_arrays.swap_back(new_array); + + if (!extract_buffer_empty()) { + m_stats.num_new_internal_arrays++; + m_stats.max_num_new_internal_arrays.set_max( + m_stats.num_new_internal_arrays); + m_minima.add_internal_array( + static_cast(m_internal_arrays.size()) - 1 + ); + } + + m_internal_size += size - used; + m_mem_left -= internal_array_type::int_memory(capacity); + + STXXL_CHECK(level < c_max_internal_levels && + "Internal array level is larger than anything possible " + "in this universe. Increase the size of m_internal_levels"); + + ++m_internal_levels[level]; + + m_stats.max_num_internal_arrays.set_max(m_internal_arrays.size()); + + // if IA level is too large ... + if (m_internal_levels[level] < c_max_internal_level_size) return; + + unsigned_type level_size = 0; + size_type int_memory = 0; + std::vector sequences; + std::vector ia_index; + + for (unsigned_type i = 0; i < m_internal_arrays.size(); ++i) + { + if (m_internal_arrays[i].level() != level) continue; + if (m_internal_arrays[i].empty()) continue; + + level_size += m_internal_arrays[i].size(); + int_memory += m_internal_arrays[i].int_memory(); + sequences.push_back(std::make_pair(m_internal_arrays[i].begin(), + m_internal_arrays[i].end())); + ia_index.push_back(i); + } + + // AND there is enough RAM to merge it (without flushing out to EA). + if (m_mem_left < internal_array_type::int_memory(level_size)) return; + + // must free up more memory than the new array needs. + STXXL_ASSERT(int_memory >= internal_array_type::int_memory(level_size)); + + STXXL_DEBUG("merging internal arrays" << + " level=" << level << + " level_size=" << level_size << + " sequences=" << sequences.size()); + + std::vector merged_array(level_size); + + potentially_parallel::multiway_merge( + sequences.begin(), sequences.end(), + merged_array.begin(), level_size, m_inv_compare); + + // release memory of old internal arrays immediately + for (unsigned_type i = 0; i < ia_index.size(); ++i) + { + unsigned_type ia = ia_index[i]; + m_internal_arrays[ia].make_empty(); + // this is done in cleanup_internal_arrays()... + //if (ia < m_minima.ia_slots()) + // m_minima.deactivate_internal_array(ia); + } + + cleanup_internal_arrays(); + + // in add_as_internal_array the level_size is re-added! + m_internal_size -= level_size; + + // add as new internal array at next level (and maybe recursively merge) + add_as_internal_array(merged_array, 0, level + 1); + } + + /*! + * Sorts the values from values and writes them into an internal array. + * Don't use the value vector afterwards! + * + * \param values the vector to sort and store + */ + void flush_array_internal(std::vector& values) + { + potentially_parallel::sort(values.begin(), values.end(), m_inv_compare); + + // flush until enough memory for new array + flush_ia_ea_until_memory_free( + internal_array_type::int_memory(values.size()) + ); + + add_as_internal_array(values); + } + + //! Struct of all statistical counters and timers. Turn on/off statistics + //! using the stats_counter and stats_timer typedefs. + struct stats_type + { + //! Largest number of elements in the extract buffer at the same time + stats_counter max_extract_buffer_size; + + //! Sum of the sizes of each extract buffer refill. Used for average + //! size. + stats_counter total_extract_buffer_size; + + //! Largest number of elements in the merge buffer when running + //! flush_internal_arrays() + stats_counter max_merge_buffer_size; + + //! Total number of extracts + stats_counter num_extracts; + + //! Number of refill_extract_buffer() calls + stats_counter num_extract_buffer_refills; + + //! Number of flush_insertion_heaps() calls + stats_counter num_insertion_heap_flushes; + + //! Number of flush_directly_to_hd() calls + stats_counter num_direct_flushes; + + //! Number of flush_internal_arrays() calls + stats_counter num_internal_array_flushes; + + //! Number of merge_external_arrays() calls + stats_counter num_external_array_merges; + + //! Largest number of internal arrays at the same time + stats_counter max_num_internal_arrays; + + //! Largest number of external arrays at the same time + stats_counter max_num_external_arrays; + + //! Temporary number of new external arrays at the same time (which + //! were created while the extract buffer hadn't been empty) + stats_counter num_new_external_arrays; + + //! Largest number of new external arrays at the same time (which were + //! created while the extract buffer hadn't been empty) + stats_counter max_num_new_external_arrays; + + //! Temporary number of new internal arrays at the same time (which + //! were created while the extract buffer hadn't been empty) + stats_counter num_new_internal_arrays; + + //! Largest number of new internal arrays at the same time (which were + //! created while the extract buffer hadn't been empty) + stats_counter max_num_new_internal_arrays; + + //! Total time for flush_insertion_heaps() + stats_timer insertion_heap_flush_time; + + //! Total time for flush_directly_to_hd() + stats_timer direct_flush_time; + + //! Total time for flush_internal_arrays() + stats_timer internal_array_flush_time; + + //! Total time for merge_external_arrays() + stats_timer external_array_merge_time; + + //! Total time for extract_min() + stats_timer extract_min_time; + + //! Total time for refill_extract_buffer() + stats_timer refill_extract_buffer_time; + + //! Total time for the merging in refill_extract_buffer() + //! Part of refill_extract_buffer_time. + stats_timer refill_merge_time; + + //! Total time for all things before merging in refill_extract_buffer() + //! Part of refill_extract_buffer_time. + stats_timer refill_time_before_merge; + + //! Total time for all things after merging in refill_extract_buffer() + //! Part of refill_extract_buffer_time. + stats_timer refill_time_after_merge; + + //! Total time of wait() calls in first part of + //! refill_extract_buffer(). Part of refill_time_before_merge and + //! refill_extract_buffer_time. + stats_timer refill_wait_time; + + //! Total time for pop_heap() in extract_min(). + //! Part of extract_min_time. + stats_timer pop_heap_time; + + //! Total time for merging the sorted heaps. + //! Part of flush_insertion_heaps. + stats_timer merge_sorted_heaps_time; + + //! Total time for std::lower_bound calls in refill_extract_buffer() + //! Part of refill_extract_buffer_time and refill_time_before_merge. + // stats_timer refill_lower_bound_time; + + //! Total time for std::accumulate calls in refill_extract_buffer() + //! Part of refill_extract_buffer_time and refill_time_before_merge. + stats_timer refill_accumulate_time; + + //! Total time for determining the smallest max value in refill_extract_buffer() + //! Part of refill_extract_buffer_time and refill_time_before_merge. + stats_timer refill_minmax_time; + + stats_timer hint_time; + + friend std::ostream& operator << (std::ostream& os, const stats_type& o) + { + return os << "max_extract_buffer_size=" << o.max_extract_buffer_size.as_memory_amount(sizeof(value_type)) << std::endl + << "total_extract_buffer_size=" << o.total_extract_buffer_size.as_memory_amount(sizeof(value_type)) << std::endl + << "max_merge_buffer_size=" << o.max_merge_buffer_size.as_memory_amount(sizeof(value_type)) << std::endl + << "num_extracts=" << o.num_extracts << std::endl + << "num_extract_buffer_refills=" << o.num_extract_buffer_refills << std::endl + << "num_insertion_heap_flushes=" << o.num_insertion_heap_flushes << std::endl + << "num_direct_flushes=" << o.num_direct_flushes << std::endl + << "num_internal_array_flushes=" << o.num_internal_array_flushes << std::endl + << "num_external_array_merges=" << o.num_external_array_merges << std::endl + << "max_num_internal_arrays=" << o.max_num_internal_arrays << std::endl + << "max_num_external_arrays=" << o.max_num_external_arrays << std::endl + << "num_new_external_arrays=" << o.num_new_external_arrays << std::endl + << "max_num_new_external_arrays=" << o.max_num_new_external_arrays << std::endl + << "num_new_internal_arrays=" << o.num_new_internal_arrays << std::endl + << "max_num_new_internal_arrays=" << o.max_num_new_internal_arrays << std::endl + << "insertion_heap_flush_time=" << o.insertion_heap_flush_time << std::endl + << "direct_flush_time=" << o.direct_flush_time << std::endl + << "internal_array_flush_time=" << o.internal_array_flush_time << std::endl + << "external_array_merge_time=" << o.external_array_merge_time << std::endl + << "extract_min_time=" << o.extract_min_time << std::endl + << "refill_extract_buffer_time=" << o.refill_extract_buffer_time << std::endl + << "refill_merge_time=" << o.refill_merge_time << std::endl + << "refill_time_before_merge=" << o.refill_time_before_merge << std::endl + << "refill_time_after_merge=" << o.refill_time_after_merge << std::endl + << "refill_wait_time=" << o.refill_wait_time << std::endl + << "pop_heap_time=" << o.pop_heap_time << std::endl + << "merge_sorted_heaps_time=" << o.merge_sorted_heaps_time << std::endl + // << "refill_lower_bound_time=" << o.refill_lower_bound_time << std::endl + << "refill_accumulate_time=" << o.refill_accumulate_time << std::endl + << "refill_minmax_time=" << o.refill_minmax_time << std::endl + << "hint_time=" << o.hint_time << std::endl; + } + }; + + stats_type m_stats; +}; + +// For C++98 compatibility: +template < + class ValueType, + class CompareType, + class AllocStrategy, + uint64 BlockSize, + uint64 DefaultMemSize, + uint64 MaxItems + > +const double parallel_priority_queue::c_default_extract_buffer_ram_part = 0.05; + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_PARALLEL_PRIORITY_QUEUE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_ext_merger.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_ext_merger.h new file mode 100644 index 0000000000..1b3ce5ef10 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_ext_merger.h @@ -0,0 +1,662 @@ +/*************************************************************************** + * include/stxxl/bits/containers/pq_ext_merger.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 1999 Peter Sanders + * Copyright (C) 2003, 2004, 2007 Roman Dementiev + * Copyright (C) 2007-2009 Johannes Singler + * Copyright (C) 2007-2010 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER +#define STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlcontinternals +//! +//! \{ + +/*! \internal + */ +namespace priority_queue_local { + +/*! + * External merger, based on the loser tree data structure. + * \param Arity maximum arity of merger, does not need to be a power of 2 + */ +template +class ext_merger : private noncopyable +{ +public: + //! class is parameterized by the block of the external arrays + typedef BlockType block_type; + typedef CompareType compare_type; + + // max_arity / 2 < arity <= max_arity + enum { arity = Arity, max_arity = 1UL << (LOG2::ceil) }; + + typedef AllocStr alloc_strategy; + + typedef typename block_type::bid_type bid_type; + typedef typename block_type::value_type value_type; + + typedef typename std::deque bid_container_type; + + typedef read_write_pool pool_type; + + //! our type + typedef ext_merger self_type; + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL + //! type of embedded adapter to parallel multiway_merge + typedef parallel_merger_adapter tree_type; +#else + //! type of embedded loser tree + typedef loser_tree tree_type; +#endif + + //! size type of total number of item in merger + typedef external_size_type size_type; + +public: + struct sequence_state : private noncopyable + { + block_type* block; //!< current block + unsigned_type current; //!< current index in current block + bid_container_type bids; //!< list of blocks forming this sequence + compare_type cmp; + ext_merger* merger; + bool allocated; + + //! \returns current element + const value_type& operator * () const + { + return (*block)[current]; + } + + sequence_state() + : block(NULL), current(0), + merger(NULL), + allocated(false) + { } + + ~sequence_state() + { + STXXL_VERBOSE2("ext_merger sequence_state::~sequence_state()"); + + block_manager* bm = block_manager::get_instance(); + bm->delete_blocks(bids.begin(), bids.end()); + } + + void make_inf() + { + current = 0; + (*block)[0] = cmp.min_value(); + } + + bool is_sentinel(const value_type& a) const + { + return !(cmp(cmp.min_value(), a)); + } + + bool not_sentinel(const value_type& a) const + { + return cmp(cmp.min_value(), a); + } + + void swap(sequence_state& obj) + { + if (&obj != this) + { + std::swap(current, obj.current); + std::swap(block, obj.block); + std::swap(bids, obj.bids); + assert(merger == obj.merger); + std::swap(allocated, obj.allocated); + } + } + + sequence_state& operator ++ () + { + assert(not_sentinel((*block)[current])); + assert(current < block->size); + + ++current; + + if (current == block->size) + { + STXXL_VERBOSE2("ext_merger sequence_state operator++ crossing block border "); + // go to the next block + if (bids.empty()) // if there is no next block + { + STXXL_VERBOSE2("ext_merger sequence_state operator++ it was the last block in the sequence "); + // swap memory area and delete other object. + bid_container_type to_delete; + std::swap(to_delete, bids); + make_inf(); + } + else + { + STXXL_VERBOSE2("ext_merger sequence_state operator++ there is another block "); + bid_type bid = bids.front(); + bids.pop_front(); + merger->pool->hint(bid); + if (!(bids.empty())) + { + STXXL_VERBOSE2("ext_merger sequence_state operator++ more blocks exist in a sequence, hinting the next"); + merger->pool->hint(bids.front()); + } + merger->pool->read(block, bid)->wait(); + STXXL_VERBOSE2("first element of read block " << bid << " " << *(block->begin()) << " cached in " << block); + if (!(bids.empty())) + merger->pool->hint(bids.front()); // re-hint, reading might have made a block free + block_manager::get_instance()->delete_block(bid); + current = 0; + } + } + return *this; + } + }; + + //! type of sequences in which the values are stored: external arrays + typedef sequence_state sequence_type; + +protected: + //! loser tree instance + tree_type tree; + + //! sequence including current position, dereference gives current element + sequence_state states[max_arity]; + + //! read and writer block pool + pool_type* pool; + + //! a memory block filled with sentinel values + block_type* sentinel_block; + + //! total number of elements stored + size_type m_size; + +public: + ext_merger(const compare_type& c = compare_type()) // TODO: pass pool as parameter + : tree(c, *this), + pool(NULL), + m_size(0) + { + init(); + + tree.initialize(); + } + + virtual ~ext_merger() + { + STXXL_VERBOSE1("ext_merger::~ext_merger()"); + for (unsigned_type i = 0; i < arity; ++i) + { + delete states[i].block; + } + delete sentinel_block; + } + + void set_pool(pool_type* pool_) + { + pool = pool_; + } + +public: + //! \name Interface for loser_tree + //! \{ + + //! is this segment empty ? + bool is_array_empty(unsigned_type slot) const + { + return is_sentinel(*(states[slot])); + } + + //! Is this segment allocated? Otherwise it's empty, already on the stack + //! of free segment indices and can be reused. + bool is_array_allocated(unsigned_type slot) const + { + return states[slot].allocated; + } + + //! Return the item sequence of the given slot + sequence_type & get_array(unsigned_type slot) + { + return states[slot]; + } + + //! Swap contents of arrays a and b + void swap_arrays(unsigned_type a, unsigned_type b) + { + states[a].swap(states[b]); + } + + //! Set a usually empty array to the sentinel + void make_array_sentinel(unsigned_type a) + { + states[a].make_inf(); + } + + //! free an empty segment. + void free_array(unsigned_type slot) + { + STXXL_VERBOSE1("ext_merger::free_array() deleting array " << slot << " allocated=" << int(is_array_allocated(slot))); + assert(is_array_allocated(slot)); + states[slot].allocated = false; + states[slot].make_inf(); + + // free player in loser tree + tree.free_player(slot); + } + + //! Hint (prefetch) first non-internal (actually second) block of each + //! sequence. + void prefetch_arrays() + { + for (unsigned_type i = 0; i < tree.k; ++i) + { + if (!states[i].bids.empty()) + pool->hint(states[i].bids.front()); + } + } + + //! \} + +protected: + void init() + { + STXXL_VERBOSE2("ext_merger::init()"); + + sentinel_block = NULL; + if (arity < max_arity) + { + sentinel_block = new block_type; + for (unsigned_type i = 0; i < block_type::size; ++i) + (*sentinel_block)[i] = tree.cmp.min_value(); + if (arity + 1 == max_arity) { + // same memory consumption, but smaller merge width, better use arity = max_arity + STXXL_ERRMSG("inefficient PQ parameters for ext_merger: arity + 1 == max_arity"); + } + } + + for (unsigned_type i = 0; i < max_arity; ++i) + { + states[i].merger = this; + if (i < arity) + states[i].block = new block_type; + else + states[i].block = sentinel_block; + + states[i].make_inf(); + } + } + +#if 0 + void swap(ext_merger& obj) + { + std::swap(cmp, obj.cmp); + std::swap(k, obj.k); + std::swap(logK, obj.logK); + std::swap(m_size, obj.m_size); + swap_1D_arrays(states, obj.states, max_arity); + + // std::swap(pool,obj.pool); + } +#endif + +public: + unsigned_type mem_cons() const // only rough estimation + { + return (STXXL_MIN(arity + 1, max_arity) * block_type::raw_size); + } + + //! Whether there is still space for new array + bool is_space_available() const + { + return tree.is_space_available(); + } + + //! True if a is the sentinel value + bool is_sentinel(const value_type& a) const + { + return tree.is_sentinel(a); + } + + //! Return the number of items in the arrays + size_type size() const + { + return m_size; + } + + /*! + \param bidlist list of blocks to insert + \param first_block the first block of the sequence, before bidlist + \param first_size number of elements in the first block + \param slot slot to insert into + */ + void insert_segment(bid_container_type& bidlist, block_type* first_block, + unsigned_type first_size, unsigned_type slot) + { + STXXL_VERBOSE1("ext_merger::insert_segment(bidlist,...) " << this << " " << bidlist.size() << " " << slot); + assert(!is_array_allocated(slot)); + assert(first_size > 0); + + sequence_state& new_sequence = states[slot]; + new_sequence.current = block_type::size - first_size; + std::swap(new_sequence.block, first_block); + delete first_block; + std::swap(new_sequence.bids, bidlist); + new_sequence.allocated = true; + assert(is_array_allocated(slot)); + } + + //! Merge all items from another merger and insert the resulting external + //! array into the merger. Requires: is_space_available() == 1 + template + void append_merger(Merger& another_merger, size_type segment_size) + { + STXXL_VERBOSE1("ext_merger::append_merger(merger,...)" << this); + + if (segment_size == 0) + { + // deallocate memory ? + STXXL_VERBOSE1("Merged segment with zero size."); + return; + } + + // allocate a new player slot + unsigned_type index = tree.new_player(); + + // construct new sorted array from merger + assert(segment_size); + unsigned_type nblocks = (unsigned_type)(segment_size / block_type::size); + //assert(nblocks); // at least one block + STXXL_VERBOSE1("ext_merger::insert_segment nblocks=" << nblocks); + if (nblocks == 0) + { + STXXL_VERBOSE1("ext_merger::insert_segment(merger,...) WARNING: inserting a segment with " << + nblocks << " blocks"); + STXXL_VERBOSE1("THIS IS INEFFICIENT: TRY TO CHANGE PRIORITY QUEUE PARAMETERS"); + } + unsigned_type first_size = (unsigned_type)(segment_size % block_type::size); + if (first_size == 0) + { + first_size = block_type::size; + --nblocks; + } + + // allocate blocks + block_manager* bm = block_manager::get_instance(); + bid_container_type bids(nblocks); + bm->new_blocks(alloc_strategy(), bids.begin(), bids.end()); + block_type* first_block = new block_type; + + another_merger.multi_merge( + first_block->begin() + (block_type::size - first_size), + first_block->end()); + + STXXL_VERBOSE1("last element of first block " << *(first_block->end() - 1)); + assert(!tree.cmp(*(first_block->begin() + (block_type::size - first_size)), *(first_block->end() - 1))); + + assert(pool->size_write() > 0); + + for (typename bid_container_type::iterator curbid = bids.begin(); curbid != bids.end(); ++curbid) + { + block_type* b = pool->steal(); + another_merger.multi_merge(b->begin(), b->end()); + STXXL_VERBOSE1("first element of following block " << *curbid << " " << *(b->begin())); + STXXL_VERBOSE1("last element of following block " << *curbid << " " << *(b->end() - 1)); + assert(!tree.cmp(*(b->begin()), *(b->end() - 1))); + pool->write(b, *curbid); + STXXL_VERBOSE1("written to block " << *curbid << " cached in " << b); + } + + insert_segment(bids, first_block, first_size, index); + + m_size += segment_size; + + // propagate new information up the tree + tree.update_on_insert((index + tree.k) >> 1, *(states[index]), index); + } + + // delete the (length = end-begin) smallest elements and write them to [begin..end) + // empty segments are deallocated + // requires: + // - there are at least length elements + // - segments are ended by sentinels + template + void multi_merge(OutputIterator begin, OutputIterator end) + { + assert((size_type)(end - begin) <= m_size); + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL + multi_merge_parallel(begin, end); +#else // STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL + tree.multi_merge(begin, end); + m_size -= end - begin; +#endif + } + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL + +protected: + //! extract the (length = end - begin) smallest elements using parallel + //! multiway_merge. + + template + void multi_merge_parallel(OutputIterator begin, OutputIterator end) + { + const unsigned_type& k = tree.k; + + if (begin == end) + return; + + typedef stxxl::int64 diff_type; + + typedef std::pair sequence; + + std::vector seqs; + std::vector orig_seq_index; + + invert_order inv_cmp(tree.cmp); + + for (unsigned_type i = 0; i < k; ++i) //initialize sequences + { + if (states[i].current == states[i].block->size || is_sentinel(*states[i])) + continue; + + seqs.push_back(std::make_pair(states[i].block->begin() + states[i].current, states[i].block->end())); + orig_seq_index.push_back(i); + +#if STXXL_CHECK_ORDER_IN_SORTS + if (!is_sentinel(*seqs.back().first) && !stxxl::is_sorted(seqs.back().first, seqs.back().second, inv_cmp)) + { + STXXL_VERBOSE1("length " << i << " " << (seqs.back().second - seqs.back().first)); + for (value_type* v = seqs.back().first + 1; v < seqs.back().second; ++v) + { + if (inv_cmp(*v, *(v - 1))) + { + STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs.back().first - 1) << "/" << (v - seqs.back().first) << " " << *(v - 1) << " " << *v); + } + if (is_sentinel(*v)) + { + STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs.back().first)); + } + } + assert(false); + } +#endif + + // Hint first non-internal (actually second) block of this sequence. + if (!states[i].bids.empty()) + pool->hint(states[i].bids.front()); + } + + assert(seqs.size() > 0); + +#if STXXL_CHECK_ORDER_IN_SORTS + value_type last_elem; +#endif + + // elements still to merge for this output block + diff_type rest = end - begin; + + while (rest > 0) + { + // minimum of the sequences' last elements + value_type min_last = tree.cmp.min_value(); + + diff_type total_size = 0; + + for (unsigned_type i = 0; i < seqs.size(); ++i) + { + diff_type seq_i_size = seqs[i].second - seqs[i].first; + if (seq_i_size > 0) + { + total_size += seq_i_size; + if (inv_cmp(*(seqs[i].second - 1), min_last)) + min_last = *(seqs[i].second - 1); + + STXXL_VERBOSE1("front block of seq " << i << ": front=" << *(seqs[i].first) << " back=" << *(seqs[i].second - 1) << " len=" << seq_i_size); + } + else { + STXXL_VERBOSE1("front block of seq " << i << ": empty"); + } + } + + assert(total_size > 0); + assert(!is_sentinel(min_last)); + + STXXL_VERBOSE1("min_last " << min_last << " total size " << total_size << " num_seq " << seqs.size()); + + diff_type less_equal_than_min_last = 0; + //locate this element in all sequences + for (unsigned_type i = 0; i < seqs.size(); ++i) + { + //assert(seqs[i].first < seqs[i].second); + + typename block_type::iterator position = + std::upper_bound(seqs[i].first, seqs[i].second, min_last, inv_cmp); + + //no element larger than min_last is merged + + STXXL_VERBOSE1("seq " << i << ": " << (position - seqs[i].first) << " greater equal than " << min_last); + + less_equal_than_min_last += (position - seqs[i].first); + } + + // at most rest elements + diff_type output_size = STXXL_MIN(less_equal_than_min_last, rest); + + STXXL_VERBOSE1("output_size=" << output_size << " = min(leq_t_ml=" << less_equal_than_min_last << ", rest=" << rest << ")"); + + assert(output_size > 0); + + //main call + + // sequence iterators are progressed appropriately: + begin = parallel::multiway_merge( + seqs.begin(), seqs.end(), begin, output_size, inv_cmp); + + rest -= output_size; + m_size -= output_size; + + for (unsigned_type i = 0; i < seqs.size(); ++i) + { + sequence_state& state = states[orig_seq_index[i]]; + + state.current = seqs[i].first - state.block->begin(); + + assert(seqs[i].first <= seqs[i].second); + + if (seqs[i].first == seqs[i].second) + { + // has run empty? + + assert(state.current == state.block->size); + if (state.bids.empty()) + { + // if there is no next block + STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) it was the last block in the sequence "); + state.make_inf(); + } + else + { +#if STXXL_CHECK_ORDER_IN_SORTS + last_elem = *(seqs[i].second - 1); +#endif + STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) there is another block "); + bid_type bid = state.bids.front(); + state.bids.pop_front(); + pool->hint(bid); + if (!(state.bids.empty())) + { + STXXL_VERBOSE2("seq " << i << ": ext_merger::multi_merge(...) more blocks exist, hinting the next"); + pool->hint(state.bids.front()); + } + pool->read(state.block, bid)->wait(); + STXXL_VERBOSE1("seq " << i << ": first element of read block " << bid << " " << *(state.block->begin()) << " cached in " << state.block); + if (!(state.bids.empty())) + pool->hint(state.bids.front()); // re-hint, reading might have made a block free + state.current = 0; + seqs[i] = std::make_pair(state.block->begin() + state.current, state.block->end()); + block_manager::get_instance()->delete_block(bid); + +#if STXXL_CHECK_ORDER_IN_SORTS + STXXL_VERBOSE1("before " << last_elem << " after " << *seqs[i].first << " newly loaded block " << bid); + if (!stxxl::is_sorted(seqs[i].first, seqs[i].second, inv_cmp)) + { + STXXL_VERBOSE1("length " << i << " " << (seqs[i].second - seqs[i].first)); + for (value_type* v = seqs[i].first + 1; v < seqs[i].second; ++v) + { + if (inv_cmp(*v, *(v - 1))) + { + STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs[i].first - 1) << "/" << (v - seqs[i].first) << " " << *(v - 1) << " " << *v); + } + if (is_sentinel(*v)) + { + STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs[i].first)); + } + } + assert(false); + } +#endif + } + } + } + } //while (rest > 1) + + for (unsigned_type i = 0; i < seqs.size(); ++i) + { + unsigned_type seg = orig_seq_index[i]; + if (is_array_empty(seg)) + { + STXXL_VERBOSE1("deallocated " << seg); + free_array(seg); + } + } + + tree.maybe_compact(); + } +#endif // STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL +}; // class ext_merger + +} // namespace priority_queue_local + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_helpers.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_helpers.h new file mode 100644 index 0000000000..552d15b557 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_helpers.h @@ -0,0 +1,349 @@ +/*************************************************************************** + * include/stxxl/bits/containers/pq_helpers.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 1999 Peter Sanders + * Copyright (C) 2003, 2004, 2007 Roman Dementiev + * Copyright (C) 2007, 2009 Johannes Singler + * Copyright (C) 2007, 2008 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_PQ_HELPERS_HEADER +#define STXXL_CONTAINERS_PQ_HELPERS_HEADER + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if STXXL_PARALLEL + +#if defined(STXXL_PARALLEL_MODE) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40400) +#undef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL +#undef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL +#undef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER +#define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL 0 +#define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL 0 +#define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER 0 +#endif + +// enable/disable parallel merging for certain cases, for performance tuning +#ifndef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL +#define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL 1 +#endif +#ifndef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL +#define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL 1 +#endif +#ifndef STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER +#define STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER 1 +#endif + +#endif //STXXL_PARALLEL + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL +#define STXXL_PQ_EXTERNAL_LOSER_TREE 0 // no loser tree for the external sequences +#else +#define STXXL_PQ_EXTERNAL_LOSER_TREE 1 +#endif + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL +#define STXXL_PQ_INTERNAL_LOSER_TREE 0 // no loser tree for the internal sequences +#else +#define STXXL_PQ_INTERNAL_LOSER_TREE 1 +#endif + +#define STXXL_VERBOSE_PQ(msg) STXXL_VERBOSE2_THIS("priority_queue::" << msg) + +STXXL_BEGIN_NAMESPACE + +//! \defgroup stlcontinternals internals +//! \ingroup stlcont +//! Supporting internal classes +//! \{ + +/*! \internal + */ +namespace priority_queue_local { + +/*! + * Similar to std::priority_queue, with the following differences: + * - Maximum size is fixed at construction time, so an array can be used. + * - Provides access to underlying heap, so (parallel) sorting in place is possible. + * - Can be cleared "at once", without reallocation. + */ +template , + typename CompareType = std::less > +class internal_priority_queue +{ +public: + typedef ValueType value_type; + typedef ContainerType container_type; + typedef CompareType compare_type; + typedef typename container_type::reference reference; + typedef typename container_type::const_reference const_reference; + typedef typename container_type::size_type size_type; + +protected: + // See queue::heap for notes on these names. + container_type heap; + CompareType comp; + size_type current_size; + +public: + //! Default constructor creates no elements. + explicit + internal_priority_queue(size_type capacity) + : heap(capacity), current_size(0) + { } + + //! Returns true if the %queue is empty. + bool + empty() const + { return current_size == 0; } + + //! Returns the number of elements in the %queue. + size_type + size() const + { return current_size; } + + /*! + * Returns a read-only (constant) reference to the data at the first + * element of the %queue. + */ + const_reference + top() const + { + return heap.front(); + } + + /*! + * Add data to the %queue. + * \param x Data to be added. + * + * This is a typical %queue operation. + * The time complexity of the operation depends on the underlying + * container. + */ + void + push(const value_type& x) + { + heap[current_size] = x; + ++current_size; + std::push_heap(heap.begin(), heap.begin() + current_size, comp); + } + + /*! + * Removes first element. + * + * This is a typical %queue operation. It shrinks the %queue + * by one. The time complexity of the operation depends on the + * underlying container. + * + * Note that no data is returned, and if the first element's + * data is needed, it should be retrieved before pop() is + * called. + */ + void + pop() + { + std::pop_heap(heap.begin(), heap.begin() + current_size, comp); + --current_size; + } + + //! Sort all contained elements, write result to \c target. + void sort_to(value_type* target) + { + check_sort_settings(); + potentially_parallel:: + sort(heap.begin(), heap.begin() + current_size, comp); + std::reverse_copy(heap.begin(), heap.begin() + current_size, target); + } + + //! Remove all contained elements. + void clear() + { + current_size = 0; + } +}; + +//! Inverts the order of a comparison functor by swapping its arguments. +template +class invert_order +{ +protected: + Predicate pred; + +public: + explicit + invert_order(const Predicate& _pred) : pred(_pred) { } + + bool operator () (const FirstType& x, const SecondType& y) const + { + return pred(y, x); + } +}; + +/*! + * Similar to std::stack, with the following differences: + * - Maximum size is fixed at compilation time, so an array can be used. + * - Can be cleared "at once", without reallocation. + */ +template +class internal_bounded_stack +{ + typedef ValueType value_type; + typedef unsigned_type size_type; + enum { max_size = MaxSize }; + + size_type m_size; + value_type m_array[max_size]; + +public: + internal_bounded_stack() : m_size(0) { } + + void push(const value_type& x) + { + assert(m_size < max_size); + m_array[m_size++] = x; + } + + const value_type & top() const + { + assert(m_size > 0); + return m_array[m_size - 1]; + } + + void pop() + { + assert(m_size > 0); + --m_size; + } + + void clear() + { + m_size = 0; + } + + size_type size() const + { + return m_size; + } + + bool empty() const + { + return m_size == 0; + } +}; + +template +class short_sequence : public std::pair +{ + typedef std::pair pair; + +public: + typedef Iterator iterator; + typedef const iterator const_iterator; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::difference_type size_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef unsigned_type origin_type; + +private: + origin_type m_origin; + +public: + short_sequence(Iterator first, Iterator last, origin_type origin) + : pair(first, last), m_origin(origin) + { } + + iterator begin() + { + return this->first; + } + + const_iterator begin() const + { + return this->first; + } + + const_iterator cbegin() const + { + return begin(); + } + + iterator end() + { + return this->second; + } + + const_iterator end() const + { + return this->second; + } + + const_iterator cend() const + { + return end(); + } + + reference front() + { + return *begin(); + } + + const_reference front() const + { + return *begin(); + } + + reference back() + { + return *(end() - 1); + } + + const_reference back() const + { + return *(end() - 1); + } + + size_type size() const + { + return end() - begin(); + } + + bool empty() const + { + return size() == 0; + } + + origin_type origin() const + { + return m_origin; + } +}; + +} // namespace priority_queue_local + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_PQ_HELPERS_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_int_merger.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_int_merger.h new file mode 100644 index 0000000000..28b276c441 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_int_merger.h @@ -0,0 +1,396 @@ +/*************************************************************************** + * include/stxxl/bits/containers/pq_int_merger.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 1999 Peter Sanders + * Copyright (C) 2003, 2004, 2007 Roman Dementiev + * Copyright (C) 2007-2009 Johannes Singler + * Copyright (C) 2007, 2008 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_PQ_INT_MERGER_HEADER +#define STXXL_CONTAINERS_PQ_INT_MERGER_HEADER + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlcontinternals +//! +//! \{ + +/*! \internal + */ +namespace priority_queue_local { + +template +class int_merger : private noncopyable +{ +public: + //! type of values in merger + typedef ValueType value_type; + //! comparator object type + typedef CompareType compare_type; + + enum { max_arity = MaxArity }; + + //! our type + typedef int_merger self_type; + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + //! type of embedded adapter to parallel multiway_merge + typedef parallel_merger_adapter tree_type; +#else + //! type of embedded loser tree + typedef loser_tree tree_type; +#endif + + //! type of sequences in which the values are stored: memory arrays + typedef value_type* sequence_type; + + //! size type of total number of item in merger + typedef internal_size_type size_type; + +protected: + //! loser tree instance + tree_type tree; + + //! target of free segment pointers + value_type sentinel; + + // leaf information. note that Knuth uses indices k..k-1, while we use + // 0..k-1 + + //! pointer to current element + value_type* current[MaxArity]; + //! pointer to end of block for current element + value_type* current_end[MaxArity]; + //! start of Segments + value_type* segment[MaxArity]; + //! just to count the internal memory consumption, in bytes + unsigned_type segment_size[MaxArity]; + + unsigned_type mem_cons_; + + //! total number of elements stored + size_type m_size; + +public: + //! \name Interface for loser_tree + //! \{ + + //! is this array invalid? here: empty and prefixed with sentinel? + bool is_array_empty(unsigned_type slot) const + { + return tree.is_sentinel(*(current[slot])); + } + + //! is this array's backing memory still allocated or does the current + //! point to sentinel? + bool is_array_allocated(unsigned_type slot) const + { + return current[slot] != &sentinel; + } + + //! Return the item sequence of the given slot + sequence_type & get_array(unsigned_type slot) + { + return current[slot]; + } + + //! Swap contents of arrays a and b + void swap_arrays(unsigned_type a, unsigned_type b) + { + std::swap(current[a], current[b]); + std::swap(current_end[a], current_end[b]); + std::swap(segment[a], segment[b]); + std::swap(segment_size[a], segment_size[b]); + } + + //! Set a usually empty array to the sentinel + void make_array_sentinel(unsigned_type slot) + { + current[slot] = &sentinel; + current_end[slot] = &sentinel; + segment[slot] = NULL; + } + + //! free an empty segment . + void free_array(unsigned_type slot) + { + // reroute current pointer to some empty sentinel segment + // with a sentinel key + STXXL_VERBOSE2("int_merger::free_array() deleting array " << + slot << " address: " << segment[slot] << " size: " << (segment_size[slot] / sizeof(value_type)) - 1); + current[slot] = &sentinel; + current_end[slot] = &sentinel; + + // free memory + delete[] segment[slot]; + segment[slot] = NULL; + mem_cons_ -= segment_size[slot]; + + // free player in loser tree + tree.free_player(slot); + } + + //! Hint (prefetch) first non-internal (actually second) block of each + //! sequence. No-operation for internal arrays. + void prefetch_arrays() + { } + + //! \} + +public: + int_merger(const compare_type& c = compare_type()) + : tree(c, *this), + sentinel(c.min_value()), + mem_cons_(0), + m_size(0) + { + segment[0] = NULL; + current[0] = &sentinel; + current_end[0] = &sentinel; + + // entry and sentinel are initialized by init since they need the value + // of supremum + tree.initialize(); + } + + ~int_merger() + { + STXXL_VERBOSE1("int_merger::~int_merger()"); + for (unsigned_type i = 0; i < tree.k; ++i) + { + if (segment[i]) + { + STXXL_VERBOSE2("int_merger::~int_merger() deleting segment " << i); + delete[] segment[i]; + mem_cons_ -= segment_size[i]; + } + } + // check whether we have not lost any memory + assert(mem_cons_ == 0); + } + + void swap(int_merger& obj) + { + std::swap(sentinel, obj.sentinel); + swap_1D_arrays(current, obj.current, MaxArity); + swap_1D_arrays(current_end, obj.current_end, MaxArity); + swap_1D_arrays(segment, obj.segment, MaxArity); + swap_1D_arrays(segment_size, obj.segment_size, MaxArity); + std::swap(mem_cons_, obj.mem_cons_); + } + + unsigned_type mem_cons() const { return mem_cons_; } + + //! Whether there is still space for new array + bool is_space_available() const + { + return tree.is_space_available(); + } + + //! True if a is the sentinel value + bool is_sentinel(const value_type& a) const + { + return tree.is_sentinel(a); + } + + //! append array to merger, takes ownership of the array. + //! requires: is_space_available() == 1 + void append_array(value_type* target, unsigned_type length) + { + STXXL_VERBOSE2("int_merger::insert_segment(" << target << "," << length << ")"); + //std::copy(target,target + length,std::ostream_iterator(std::cout, "\n")); + + if (length == 0) + { + // immediately deallocate this is not only an optimization but also + // needed to keep free segments from clogging up the tree + delete[] target; + return; + } + + assert(!tree.is_sentinel(target[0])); + assert(!tree.is_sentinel(target[length - 1])); + assert(tree.is_sentinel(target[length])); + + // allocate a new player slot + unsigned_type index = tree.new_player(); + + assert(current[index] == &sentinel); + + // link new segment + current[index] = segment[index] = target; + current_end[index] = target + length; + segment_size[index] = (length + 1) * sizeof(value_type); + mem_cons_ += (length + 1) * sizeof(value_type); + m_size += length; + + // propagate new information up the tree + tree.update_on_insert((index + tree.k) >> 1, *target, index); + } + + //! Return the number of items in the arrays + size_type size() const + { + return m_size; + } + + //! extract the (length = end - begin) smallest elements and write them to + //! [begin..end) empty segments are deallocated. Requires: there are at + //! least length elements and segments are ended by sentinels. + void multi_merge(value_type* begin, value_type* end) + { + assert(begin + m_size >= end); + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + multi_merge_parallel(begin, end - begin); +#else + tree.multi_merge(begin, end); +#endif + + m_size -= end - begin; + } + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + +protected: + //! extract the (length = end - begin) smallest elements using parallel + //! multiway_merge. + void multi_merge_parallel(value_type* target, unsigned_type length) + { + const unsigned_type& k = tree.k; + const unsigned_type& logK = tree.logK; + compare_type& cmp = tree.cmp; + + STXXL_VERBOSE3("int_merger::multi_merge_parallel(target=" << target << ", len=" << length << ") k=" << k); + + if (length == 0) + return; + + assert(k > 0); + + //This is the place to make statistics about internal multi_merge calls. + + invert_order inv_cmp(cmp); + switch (logK) { + case 0: { + assert(k == 1); + + memcpy(target, current[0], length * sizeof(value_type)); + current[0] += length; + + if (is_array_empty(0) && is_array_allocated(0)) + free_array(0); + + break; + } + case 1: { + assert(k == 2); + + std::pair seqs[2] = + { + std::make_pair(current[0], current_end[0]), + std::make_pair(current[1], current_end[1]) + }; + + parallel::multiway_merge_sentinels( + seqs, seqs + 2, target, length, inv_cmp); + + current[0] = seqs[0].first; + current[1] = seqs[1].first; + + if (is_array_empty(0) && is_array_allocated(0)) + free_array(0); + + if (is_array_empty(1) && is_array_allocated(1)) + free_array(1); + + break; + } + case 2: { + assert(k == 4); + + std::pair seqs[4] = + { + std::make_pair(current[0], current_end[0]), + std::make_pair(current[1], current_end[1]), + std::make_pair(current[2], current_end[2]), + std::make_pair(current[3], current_end[3]) + }; + + parallel::multiway_merge_sentinels( + seqs, seqs + 4, target, length, inv_cmp); + + current[0] = seqs[0].first; + current[1] = seqs[1].first; + current[2] = seqs[2].first; + current[3] = seqs[3].first; + + if (is_array_empty(0) && is_array_allocated(0)) + free_array(0); + + if (is_array_empty(1) && is_array_allocated(1)) + free_array(1); + + if (is_array_empty(2) && is_array_allocated(2)) + free_array(2); + + if (is_array_empty(3) && is_array_allocated(3)) + free_array(3); + + break; + } + default: { + std::vector > seqs; + std::vector orig_seq_index; + for (unsigned int i = 0; i < k; ++i) + { + if (current[i] != current_end[i] && !is_sentinel(*current[i])) + { + seqs.push_back(std::make_pair(current[i], current_end[i])); + orig_seq_index.push_back(i); + } + } + + parallel::multiway_merge_sentinels( + seqs.begin(), seqs.end(), target, length, inv_cmp); + + for (unsigned int i = 0; i < seqs.size(); ++i) + { + int_type seg = orig_seq_index[i]; + current[seg] = seqs[i].first; + } + + for (unsigned int i = 0; i < k; ++i) + { + if (is_array_empty(i) && is_array_allocated(i)) + { + STXXL_VERBOSE3("deallocated " << i); + free_array(i); + } + } + break; + } + } + + tree.maybe_compact(); + } +#endif // STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL +}; + +} // namespace priority_queue_local + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_PQ_INT_MERGER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_losertree.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_losertree.h new file mode 100644 index 0000000000..2b599fd894 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_losertree.h @@ -0,0 +1,752 @@ +/*************************************************************************** + * include/stxxl/bits/containers/pq_losertree.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 1999 Peter Sanders + * Copyright (C) 2003, 2004, 2007 Roman Dementiev + * Copyright (C) 2007-2009 Johannes Singler + * Copyright (C) 2007, 2008 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_PQ_LOSERTREE_HEADER +#define STXXL_CONTAINERS_PQ_LOSERTREE_HEADER + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlcontinternals +//! +//! \{ + +/*! \internal + */ +namespace priority_queue_local { + +////////////////////////////////////////////////////////////////////// +// The data structure from Knuth, "Sorting and Searching", Section 5.4.1 +/*! + * Loser tree from Knuth, "Sorting and Searching", Section 5.4.1 + * \param MaxArity maximum arity of loser tree, has to be a power of two + */ +template +class loser_tree : private noncopyable +{ +public: + typedef ValueType value_type; + typedef CompareType comparator_type; + typedef value_type Element; + enum { max_arity = MaxArity }; + +private: +#if STXXL_PQ_INTERNAL_LOSER_TREE + struct Entry + { + value_type key; // Key of Loser element (winner for 0) + unsigned_type index; // number of losing segment + }; +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + + comparator_type cmp; + // stack of free segment indices + internal_bounded_stack free_slots; + + unsigned_type size_; // total number of elements stored + unsigned_type logK; // log of current tree size + unsigned_type k; // invariant (k == 1 << logK), always a power of two + + Element sentinel; // target of free segment pointers + +#if STXXL_PQ_INTERNAL_LOSER_TREE + // upper levels of loser trees + // entry[0] contains the winner info + Entry entry[MaxArity]; +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + + // leaf information + // note that Knuth uses indices k..k-1 + // while we use 0..k-1 + Element* current[MaxArity]; // pointer to current element + Element* current_end[MaxArity]; // pointer to end of block for current element + Element* segment[MaxArity]; // start of Segments + unsigned_type segment_size[MaxArity]; // just to count the internal memory consumption, in bytes + + unsigned_type mem_cons_; + + // private member functions + unsigned_type initWinner(unsigned_type root); + void update_on_insert(unsigned_type node, const Element& newKey, unsigned_type newIndex, + Element* winnerKey, unsigned_type* winnerIndex, unsigned_type* mask); + void deallocate_segment(unsigned_type slot); + void doubleK(); + void compactTree(); + void rebuildLoserTree(); + bool is_segment_empty(unsigned_type slot); + void multi_merge_k(Element* target, unsigned_type length); + +#if STXXL_PQ_INTERNAL_LOSER_TREE + template + void multi_merge_f(Element* target, unsigned_type length) + { + //Entry *currentPos; + //Element currentKey; + //int currentIndex; // leaf pointed to by current entry + Element* done = target + length; + Entry* regEntry = entry; + Element** regStates = current; + unsigned_type winnerIndex = regEntry[0].index; + Element winnerKey = regEntry[0].key; + Element* winnerPos; + //Element sup = sentinel; // supremum + + assert(logK >= LogK); + while (target != done) + { + winnerPos = regStates[winnerIndex]; + + // write result + *target = winnerKey; + + // advance winner segment + ++winnerPos; + regStates[winnerIndex] = winnerPos; + winnerKey = *winnerPos; + + // remove winner segment if empty now + if (is_sentinel(winnerKey)) + { + deallocate_segment(winnerIndex); + } + ++target; + + // update loser tree +#define TreeStep(L) \ + if (1 << LogK >= 1 << L) { \ + Entry* pos ## L = regEntry + ((winnerIndex + (1 << LogK)) >> ((LogK - L + 1 >= 0) ? (LogK - L + 1) : 0)); \ + Element key ## L = pos ## L->key; \ + if (cmp(winnerKey, key ## L)) { \ + unsigned_type index ## L = pos ## L->index; \ + pos ## L->key = winnerKey; \ + pos ## L->index = winnerIndex; \ + winnerKey = key ## L; \ + winnerIndex = index ## L; \ + } \ + } + TreeStep(10); + TreeStep(9); + TreeStep(8); + TreeStep(7); + TreeStep(6); + TreeStep(5); + TreeStep(4); + TreeStep(3); + TreeStep(2); + TreeStep(1); +#undef TreeStep + } + regEntry[0].index = winnerIndex; + regEntry[0].key = winnerKey; + } +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + +public: + bool is_sentinel(const Element& a) + { + return !(cmp(cmp.min_value(), a)); + } + bool not_sentinel(const Element& a) + { + return cmp(cmp.min_value(), a); + } + +public: + loser_tree(); + ~loser_tree(); + void init(); + + void swap(loser_tree& obj) + { + std::swap(cmp, obj.cmp); + std::swap(free_slots, obj.free_slots); + std::swap(size_, obj.size_); + std::swap(logK, obj.logK); + std::swap(k, obj.k); + std::swap(sentinel, obj.sentinel); +#if STXXL_PQ_INTERNAL_LOSER_TREE + swap_1D_arrays(entry, obj.entry, MaxArity); +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + swap_1D_arrays(current, obj.current, MaxArity); + swap_1D_arrays(current_end, obj.current_end, MaxArity); + swap_1D_arrays(segment, obj.segment, MaxArity); + swap_1D_arrays(segment_size, obj.segment_size, MaxArity); + std::swap(mem_cons_, obj.mem_cons_); + } + + void multi_merge(Element* begin, Element* end) + { + multi_merge(begin, end - begin); + } + void multi_merge(Element*, unsigned_type length); + + unsigned_type mem_cons() const { return mem_cons_; } + + bool is_space_available() const // for new segment + { + return (k < MaxArity) || !free_slots.empty(); + } + + //! insert segment beginning at target + void insert_segment(Element * target, unsigned_type length); + + unsigned_type size() const { return size_; } +}; + +///////////////////////// LoserTree /////////////////////////////////// +template +loser_tree::loser_tree() + : size_(0), logK(0), k(1), mem_cons_(0) +{ + free_slots.push(0); + segment[0] = NULL; + current[0] = &sentinel; + current_end[0] = &sentinel; + // entry and sentinel are initialized by init + // since they need the value of supremum + init(); +} + +template +void loser_tree::init() +{ + assert(!cmp(cmp.min_value(), cmp.min_value())); // verify strict weak ordering + sentinel = cmp.min_value(); + rebuildLoserTree(); +#if STXXL_PQ_INTERNAL_LOSER_TREE + assert(current[entry[0].index] == &sentinel); +#endif //STXXL_PQ_INTERNAL_LOSER_TREE +} + +// rebuild loser tree information from the values in current +template +void loser_tree::rebuildLoserTree() +{ +#if STXXL_PQ_INTERNAL_LOSER_TREE + // MaxArity needs to be a power of two + assert(LOG2::floor == LOG2::ceil); + unsigned_type winner = initWinner(1); + entry[0].index = winner; + entry[0].key = *(current[winner]); +#endif //STXXL_PQ_INTERNAL_LOSER_TREE +} + +#if STXXL_PQ_INTERNAL_LOSER_TREE +// given any values in the leaves this +// routing recomputes upper levels of the tree +// from scratch in linear time +// initialize entry[root].index and the subtree rooted there +// return winner index +template +unsigned_type loser_tree::initWinner(unsigned_type root) +{ + if (root >= k) { // leaf reached + return root - k; + } + else { + unsigned_type left = initWinner(2 * root); + unsigned_type right = initWinner(2 * root + 1); + Element lk = *(current[left]); + Element rk = *(current[right]); + if (!(cmp(lk, rk))) { // right subtree loses + entry[root].index = right; + entry[root].key = rk; + return left; + } + else { + entry[root].index = left; + entry[root].key = lk; + return right; + } + } +} + +// first go up the tree all the way to the root +// hand down old winner for the respective subtree +// based on new value, and old winner and loser +// update each node on the path to the root top down. +// This is implemented recursively +template +void loser_tree::update_on_insert( + unsigned_type node, + const Element& newKey, + unsigned_type newIndex, + Element* winnerKey, + unsigned_type* winnerIndex, // old winner + unsigned_type* mask) // 1 << (ceil(log KNK) - dist-from-root) +{ + if (node == 0) { // winner part of root + *mask = (unsigned_type)(1) << (logK - 1); + *winnerKey = entry[0].key; + *winnerIndex = entry[0].index; + if (cmp(entry[node].key, newKey)) + { + entry[node].key = newKey; + entry[node].index = newIndex; + } + } + else { + update_on_insert(node >> 1, newKey, newIndex, winnerKey, winnerIndex, mask); + Element loserKey = entry[node].key; + unsigned_type loserIndex = entry[node].index; + if ((*winnerIndex & *mask) != (newIndex & *mask)) { // different subtrees + if (cmp(loserKey, newKey)) { // newKey will have influence here + if (cmp(*winnerKey, newKey)) { // old winner loses here + entry[node].key = *winnerKey; + entry[node].index = *winnerIndex; + } + else { // new entry loses here + entry[node].key = newKey; + entry[node].index = newIndex; + } + } + *winnerKey = loserKey; + *winnerIndex = loserIndex; + } + // note that nothing needs to be done if + // the winner came from the same subtree + // a) newKey <= winnerKey => even more reason for the other tree to lose + // b) newKey > winnerKey => the old winner will beat the new + // entry further down the tree + // also the same old winner is handed down the tree + + *mask >>= 1; // next level + } +} +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + +// make the tree two times as wide +template +void loser_tree::doubleK() +{ + STXXL_VERBOSE3("loser_tree::doubleK (before) k=" << k << " logK=" << logK << " MaxArity=" << MaxArity << " #free=" << free_slots.size()); + assert(k > 0); + assert(k < MaxArity); + assert(free_slots.empty()); // stack was free (probably not needed) + + // make all new entries free + // and push them on the free stack + for (unsigned_type i = 2 * k - 1; i >= k; i--) // backwards + { + current[i] = &sentinel; + current_end[i] = &sentinel; + segment[i] = NULL; + free_slots.push(i); + } + + // double the size + k *= 2; + logK++; + + STXXL_VERBOSE3("loser_tree::doubleK (after) k=" << k << " logK=" << logK << " MaxArity=" << MaxArity << " #free=" << free_slots.size()); + assert(!free_slots.empty()); + + // recompute loser tree information + rebuildLoserTree(); +} + +// compact nonempty segments in the left half of the tree +template +void loser_tree::compactTree() +{ + STXXL_VERBOSE3("loser_tree::compactTree (before) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); + assert(logK > 0); + + // compact all nonempty segments to the left + unsigned_type pos = 0; + unsigned_type last_empty = 0; + for ( ; pos < k; pos++) + { + if (not_sentinel(*(current[pos]))) + { + segment_size[last_empty] = segment_size[pos]; + current[last_empty] = current[pos]; + current_end[last_empty] = current_end[pos]; + segment[last_empty] = segment[pos]; + last_empty++; + } /* + else + { + if(segment[pos]) + { + STXXL_VERBOSE2("loser_tree::compactTree() deleting segment "< 1) && ((k / 2) >= last_empty)) + { + k /= 2; + logK--; + } + + // overwrite garbage and compact the stack of free segment indices + free_slots.clear(); // none free + for ( ; last_empty < k; last_empty++) + { + current[last_empty] = &sentinel; + current_end[last_empty] = &sentinel; + free_slots.push(last_empty); + } + + STXXL_VERBOSE3("loser_tree::compactTree (after) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); + + // recompute loser tree information + rebuildLoserTree(); +} + +// insert segment beginning at target +// require: is_space_available() == 1 +template +void loser_tree:: +insert_segment(Element* target, unsigned_type length) +{ + STXXL_VERBOSE2("loser_tree::insert_segment(" << target << "," << length << ")"); + //std::copy(target,target + length,std::ostream_iterator(std::cout, "\n")); + + if (length > 0) + { + assert(not_sentinel(target[0])); + assert(not_sentinel(target[length - 1])); + assert(is_sentinel(target[length])); + + // get a free slot + if (free_slots.empty()) + { // tree is too small + doubleK(); + } + assert(!free_slots.empty()); + unsigned_type index = free_slots.top(); + free_slots.pop(); + + // link new segment + current[index] = segment[index] = target; + current_end[index] = target + length; + segment_size[index] = (length + 1) * sizeof(value_type); + mem_cons_ += (length + 1) * sizeof(value_type); + size_ += length; + +#if STXXL_PQ_INTERNAL_LOSER_TREE + // propagate new information up the tree + Element dummyKey; + unsigned_type dummyIndex; + unsigned_type dummyMask; + update_on_insert((index + k) >> 1, *target, index, + &dummyKey, &dummyIndex, &dummyMask); +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + } + else { + // immediately deallocate + // this is not only an optimization + // but also needed to keep free segments from + // clogging up the tree + delete[] target; + } +} + +template +loser_tree::~loser_tree() +{ + STXXL_VERBOSE1("loser_tree::~loser_tree()"); + for (unsigned_type i = 0; i < k; ++i) + { + if (segment[i]) + { + STXXL_VERBOSE2("loser_tree::~loser_tree() deleting segment " << i); + delete[] segment[i]; + mem_cons_ -= segment_size[i]; + } + } + // check whether we have not lost any memory + assert(mem_cons_ == 0); +} + +// free an empty segment . +template +void loser_tree:: +deallocate_segment(unsigned_type slot) +{ + // reroute current pointer to some empty sentinel segment + // with a sentinel key + STXXL_VERBOSE2("loser_tree::deallocate_segment() deleting segment " << + slot << " address: " << segment[slot] << " size: " << (segment_size[slot] / sizeof(value_type)) - 1); + current[slot] = &sentinel; + current_end[slot] = &sentinel; + + // free memory + delete[] segment[slot]; + segment[slot] = NULL; + mem_cons_ -= segment_size[slot]; + + // push on the stack of free segment indices + free_slots.push(slot); +} + +// delete the length smallest elements and write them to target +// empty segments are deallocated +// require: +// - there are at least length elements +// - segments are ended by sentinels +template +void loser_tree:: +multi_merge(Element* target, unsigned_type length) +{ + STXXL_VERBOSE3("loser_tree::multi_merge(target=" << target << ", len=" << length << ") k=" << k); + + if (length == 0) + return; + + assert(k > 0); + assert(length <= size_); + + //This is the place to make statistics about internal multi_merge calls. + +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + priority_queue_local::invert_order inv_cmp(cmp); +#endif + switch (logK) { + case 0: + assert(k == 1); +#if STXXL_PQ_INTERNAL_LOSER_TREE + assert(entry[0].index == 0); +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + assert(free_slots.empty()); + memcpy(target, current[0], length * sizeof(Element)); + //std::copy(current[0], current[0] + length, target); + current[0] += length; +#if STXXL_PQ_INTERNAL_LOSER_TREE + entry[0].key = **current; +#endif //STXXL_PQ_INTERNAL_LOSER_TREE + if (is_segment_empty(0)) + deallocate_segment(0); + + break; + case 1: + assert(k == 2); +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + { + std::pair seqs[2] = + { + std::make_pair(current[0], current_end[0]), + std::make_pair(current[1], current_end[1]) + }; + parallel::multiway_merge_sentinels( + seqs, seqs + 2, target, length, inv_cmp); + current[0] = seqs[0].first; + current[1] = seqs[1].first; + } +#else + merge_iterator(current[0], current[1], target, length, cmp); + rebuildLoserTree(); +#endif + if (is_segment_empty(0)) + deallocate_segment(0); + + if (is_segment_empty(1)) + deallocate_segment(1); + + break; + case 2: + assert(k == 4); +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + { + std::pair seqs[4] = + { + std::make_pair(current[0], current_end[0]), + std::make_pair(current[1], current_end[1]), + std::make_pair(current[2], current_end[2]), + std::make_pair(current[3], current_end[3]) + }; + parallel::multiway_merge_sentinels( + seqs, seqs + 4, target, length, inv_cmp); + current[0] = seqs[0].first; + current[1] = seqs[1].first; + current[2] = seqs[2].first; + current[3] = seqs[3].first; + } +#else + if (is_segment_empty(3)) + merge3_iterator(current[0], current[1], current[2], target, length, cmp); + else + merge4_iterator(current[0], current[1], current[2], current[3], target, length, cmp); + + rebuildLoserTree(); +#endif + if (is_segment_empty(0)) + deallocate_segment(0); + + if (is_segment_empty(1)) + deallocate_segment(1); + + if (is_segment_empty(2)) + deallocate_segment(2); + + if (is_segment_empty(3)) + deallocate_segment(3); + + break; +#if !(STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL) + case 3: multi_merge_f<3>(target, length); + break; + case 4: multi_merge_f<4>(target, length); + break; + case 5: multi_merge_f<5>(target, length); + break; + case 6: multi_merge_f<6>(target, length); + break; + case 7: multi_merge_f<7>(target, length); + break; + case 8: multi_merge_f<8>(target, length); + break; + case 9: multi_merge_f<9>(target, length); + break; + case 10: multi_merge_f<10>(target, length); + break; +#endif + default: +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL + { + std::vector > seqs; + std::vector orig_seq_index; + for (unsigned int i = 0; i < k; ++i) + { + if (current[i] != current_end[i] && !is_sentinel(*current[i])) + { + seqs.push_back(std::make_pair(current[i], current_end[i])); + orig_seq_index.push_back(i); + } + } + + parallel::multiway_merge_sentinels( + seqs.begin(), seqs.end(), target, length, inv_cmp); + + for (unsigned int i = 0; i < seqs.size(); ++i) + { + int_type seg = orig_seq_index[i]; + current[seg] = seqs[i].first; + } + + for (unsigned int i = 0; i < k; ++i) + if (is_segment_empty(i)) + { + STXXL_VERBOSE3("deallocated " << i); + deallocate_segment(i); + } + } +#else + multi_merge_k(target, length); +#endif + break; + } + + size_ -= length; + + // compact tree if it got considerably smaller + { + const unsigned_type num_segments_used = k - free_slots.size(); + const unsigned_type num_segments_trigger = k - (3 * k / 5); + // using k/2 would be worst case inefficient (for large k) + // for k \in {2, 4, 8} the trigger is k/2 which is good + // because we have special mergers for k \in {1, 2, 4} + // there is also a special 3-way-merger, that will be + // triggered if k == 4 && is_segment_empty(3) + STXXL_VERBOSE3("loser_tree compact? k=" << k << " #used=" << num_segments_used + << " <= #trigger=" << num_segments_trigger << " ==> " + << ((k > 1 && num_segments_used <= num_segments_trigger) ? "yes" : "no ") + << " || " + << ((k == 4 && !free_slots.empty() && !is_segment_empty(3)) ? "yes" : "no ") + << " #free=" << free_slots.size()); + if (k > 1 && ((num_segments_used <= num_segments_trigger) || + (k == 4 && !free_slots.empty() && !is_segment_empty(3)))) + { + compactTree(); + } + } + //std::copy(target,target + length,std::ostream_iterator(std::cout, "\n")); +} + +// is this segment empty and does not point to sentinel yet? +template +inline bool loser_tree:: +is_segment_empty(unsigned_type slot) +{ + return (is_sentinel(*(current[slot])) && (current[slot] != &sentinel)); +} + +#if STXXL_PQ_INTERNAL_LOSER_TREE +// multi-merge for arbitrary K +template +void loser_tree:: +multi_merge_k(Element* target, unsigned_type length) +{ + Entry* currentPos; + Element currentKey; + unsigned_type currentIndex; // leaf pointed to by current entry + unsigned_type kReg = k; + Element* done = target + length; + unsigned_type winnerIndex = entry[0].index; + Element winnerKey = entry[0].key; + Element* winnerPos; + + while (target != done) + { + winnerPos = current[winnerIndex]; + + // write result + *target = winnerKey; + + // advance winner segment + ++winnerPos; + current[winnerIndex] = winnerPos; + winnerKey = *winnerPos; + + // remove winner segment if empty now + if (is_sentinel(winnerKey)) // + deallocate_segment(winnerIndex); + + // go up the entry-tree + for (unsigned_type i = (winnerIndex + kReg) >> 1; i > 0; i >>= 1) { + currentPos = entry + i; + currentKey = currentPos->key; + if (cmp(winnerKey, currentKey)) { + currentIndex = currentPos->index; + currentPos->key = winnerKey; + currentPos->index = winnerIndex; + winnerKey = currentKey; + winnerIndex = currentIndex; + } + } + + ++target; + } + entry[0].index = winnerIndex; + entry[0].key = winnerKey; +} +#endif // STXXL_PQ_INTERNAL_LOSER_TREE + +} // namespace priority_queue_local + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_PQ_LOSERTREE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_mergers.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_mergers.h new file mode 100644 index 0000000000..69d42b683f --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/pq_mergers.h @@ -0,0 +1,984 @@ +/*************************************************************************** + * include/stxxl/bits/containers/pq_mergers.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 1999 Peter Sanders + * Copyright (C) 2003, 2004, 2007 Roman Dementiev + * Copyright (C) 2007-2009 Johannes Singler + * Copyright (C) 2007, 2008 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_PQ_MERGERS_HEADER +#define STXXL_CONTAINERS_PQ_MERGERS_HEADER + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup stlcontinternals +//! +//! \{ + +/*! \internal + */ +namespace priority_queue_local { + +//////////////////////////////////////////////////////////////////////////////// +// auxiliary functions + +// merge length elements from the two sentinel terminated input +// sequences source0 and source1 to target +// advance source0 and source1 accordingly +// require: at least length nonsentinel elements available in source0, source1 +// require: target may overwrite one of the sources as long as +// *(sourcex + length) is before the end of sourcex +template +void merge2_iterator( + InputIterator& source0, + InputIterator& source1, + OutputIterator target, OutputIterator end, + CompareType& cmp) +{ + while (target != end) + { + if (cmp(*source0, *source1)) + { + *target = *source1; + ++source1; + } + else + { + *target = *source0; + ++source0; + } + ++target; + } +} + +// merge length elements from the three sentinel terminated input +// sequences source0, source1 and source2 to target +// advance source0, source1 and source2 accordingly +// require: at least length nonsentinel elements available in source0, source1 and source2 +// require: target may overwrite one of the sources as long as +// *(sourcex + length) is before the end of sourcex +template +void merge3_iterator( + InputIterator& source0, + InputIterator& source1, + InputIterator& source2, + OutputIterator target, OutputIterator end, + CompareType& cmp) +{ + if (cmp(*source1, *source0)) { + if (cmp(*source2, *source1)) { + goto s012; + } + else { + if (cmp(*source0, *source2)) { + goto s201; + } + else { + goto s021; + } + } + } + else { + if (cmp(*source2, *source1)) { + if (cmp(*source2, *source0)) { + goto s102; + } + else { + goto s120; + } + } + else { + goto s210; + } + } + +#define Merge3Case(a, b, c) \ + s ## a ## b ## c: \ + if (target == end) \ + return; \ + *target = *source ## a; \ + ++target; \ + ++source ## a; \ + if (cmp(*source ## b, *source ## a)) \ + goto s ## a ## b ## c; \ + if (cmp(*source ## c, *source ## a)) \ + goto s ## b ## a ## c; \ + goto s ## b ## c ## a; + + // the order is chosen in such a way that + // four of the trailing gotos can be eliminated by the optimizer + Merge3Case(0, 1, 2); + Merge3Case(1, 2, 0); + Merge3Case(2, 0, 1); + Merge3Case(1, 0, 2); + Merge3Case(0, 2, 1); + Merge3Case(2, 1, 0); + +#undef Merge3Case +} + +// merge length elements from the four sentinel terminated input +// sequences source0, source1, source2 and source3 to target +// advance source0, source1, source2 and source3 accordingly +// require: at least length nonsentinel elements available in source0, source1, source2 and source3 +// require: target may overwrite one of the sources as long as +// *(sourcex + length) is before the end of sourcex +template +void merge4_iterator( + InputIterator& source0, + InputIterator& source1, + InputIterator& source2, + InputIterator& source3, + OutputIterator target, OutputIterator end, + CompareType& cmp) +{ +#define StartMerge4(a, b, c, d) \ + if ((!cmp(*source ## a, *source ## b)) && \ + (!cmp(*source ## b, *source ## c)) && \ + (!cmp(*source ## c, *source ## d))) \ + goto s ## a ## b ## c ## d; + + // b>a c>b d>c + // ab) !(b>c) !(c>d) + + StartMerge4(0, 1, 2, 3); + StartMerge4(1, 2, 3, 0); + StartMerge4(2, 3, 0, 1); + StartMerge4(3, 0, 1, 2); + + StartMerge4(0, 3, 1, 2); + StartMerge4(3, 1, 2, 0); + StartMerge4(1, 2, 0, 3); + StartMerge4(2, 0, 3, 1); + + StartMerge4(0, 2, 3, 1); + StartMerge4(2, 3, 1, 0); + StartMerge4(3, 1, 0, 2); + StartMerge4(1, 0, 2, 3); + + StartMerge4(2, 0, 1, 3); + StartMerge4(0, 1, 3, 2); + StartMerge4(1, 3, 2, 0); + StartMerge4(3, 2, 0, 1); + + StartMerge4(3, 0, 2, 1); + StartMerge4(0, 2, 1, 3); + StartMerge4(2, 1, 3, 0); + StartMerge4(1, 3, 0, 2); + + StartMerge4(1, 0, 3, 2); + StartMerge4(0, 3, 2, 1); + StartMerge4(3, 2, 1, 0); + StartMerge4(2, 1, 0, 3); + +#define Merge4Case(a, b, c, d) \ + s ## a ## b ## c ## d: \ + if (target == end) \ + return; \ + *target = *source ## a; \ + ++target; \ + ++source ## a; \ + if (cmp(*source ## c, *source ## a)) \ + { \ + if (cmp(*source ## b, *source ## a)) \ + goto s ## a ## b ## c ## d; \ + else \ + goto s ## b ## a ## c ## d; \ + } \ + else \ + { \ + if (cmp(*source ## d, *source ## a)) \ + goto s ## b ## c ## a ## d; \ + else \ + goto s ## b ## c ## d ## a; \ + } + + Merge4Case(0, 1, 2, 3); + Merge4Case(1, 2, 3, 0); + Merge4Case(2, 3, 0, 1); + Merge4Case(3, 0, 1, 2); + + Merge4Case(0, 3, 1, 2); + Merge4Case(3, 1, 2, 0); + Merge4Case(1, 2, 0, 3); + Merge4Case(2, 0, 3, 1); + + Merge4Case(0, 2, 3, 1); + Merge4Case(2, 3, 1, 0); + Merge4Case(3, 1, 0, 2); + Merge4Case(1, 0, 2, 3); + + Merge4Case(2, 0, 1, 3); + Merge4Case(0, 1, 3, 2); + Merge4Case(1, 3, 2, 0); + Merge4Case(3, 2, 0, 1); + + Merge4Case(3, 0, 2, 1); + Merge4Case(0, 2, 1, 3); + Merge4Case(2, 1, 3, 0); + Merge4Case(1, 3, 0, 2); + + Merge4Case(1, 0, 3, 2); + Merge4Case(0, 3, 2, 1); + Merge4Case(3, 2, 1, 0); + Merge4Case(2, 1, 0, 3); + +#undef StartMerge4 +#undef Merge4Case +} + +//////////////////////////////////////////////////////////////////////////////// +// Loser tree data structure from Knuth, "Sorting and Searching", Section 5.4.1 + +/*! + * Loser tree from Knuth, "Sorting and Searching", Section 5.4.1 + * \tparam Arity maximum arity of merger, does not need to be a power of 2 + */ +template +class loser_tree : private noncopyable +{ +public: + //! type of arrays container linked with loser tree + typedef ArraysType arrays_type; + //! comparator object type + typedef CompareType compare_type; + + // arity_bound / 2 < arity <= arity_bound + enum { arity = Arity, max_arity = 1UL << (LOG2::ceil) }; + + //! type of values stored in the arrays container + typedef typename arrays_type::value_type value_type; + //! type of the ordered sequences in the arrays container + typedef typename arrays_type::sequence_type sequence_type; + +public: + //! the comparator object + compare_type cmp; + + //! current tree size, invariant (k == 1 << logK), always a power of two + unsigned_type k; + //! log of current tree size + unsigned_type logK; + + // only entries 0 .. arity-1 may hold actual sequences, the other + // entries arity .. max_arity-1 are sentinels to make the size of the tree + // a power of 2 always + +protected: + //! reference to the linked arrays + arrays_type& arrays; + + //! type of nodes in the loser tree + struct Entry + { + value_type key; //!< Key of Loser element (winner for 0) + unsigned_type index; //!< number of losing segment + }; + + //! levels of loser tree: entry[0] contains the winner info + struct Entry entry[max_arity]; + + //! stack of free player indices + internal_bounded_stack free_slots; + +public: + loser_tree(const compare_type& c, arrays_type& a) + : cmp(c), k(1), logK(0), arrays(a) + { + // verify strict weak ordering + assert(!cmp(cmp.min_value(), cmp.min_value())); + } + + void initialize() + { + // initial state: one empty player slot + free_slots.push(0); + + rebuild_loser_tree(); + + assert(arrays.is_array_empty(0) && !arrays.is_array_allocated(0)); + } + + //! True if a is the sentinel value + bool is_sentinel(const value_type& a) const + { + return !(cmp(cmp.min_value(), a)); // a <= cmp.min_value() + } + + //! Allocate a free slot for a new player. + unsigned_type new_player() + { + // get a free slot + if (free_slots.empty()) { + // tree is too small, attempt to enlarge + double_k(); + } + + assert(!free_slots.empty()); + unsigned_type index = free_slots.top(); + free_slots.pop(); + + return index; + } + + //! Free a finished player's slot + void free_player(unsigned_type slot) + { + // push on the stack of free segment indices + free_slots.push(slot); + } + + //! Whether there is still space for new array + bool is_space_available() const + { + return (k < arity) || !free_slots.empty(); + } + + //! rebuild loser tree information from the values in current + void rebuild_loser_tree() + { + unsigned_type winner = init_winner(1); + entry[0].index = winner; + entry[0].key = *arrays.get_array(winner); + } + + // given any values in the leaves this + // routing recomputes upper levels of the tree + // from scratch in linear time + // initialize entry[root].index and the subtree rooted there + // return winner index + unsigned_type init_winner(unsigned_type root) + { + if (root >= k || root >= max_arity) + { // leaf reached + return root - k; + } + else + { + unsigned_type left = init_winner(2 * root); + unsigned_type right = init_winner(2 * root + 1); + value_type lk = *arrays.get_array(left); + value_type rk = *arrays.get_array(right); + assert(root < max_arity); + + if (!(cmp(lk, rk))) + { + // right subtree looses + entry[root].index = right; + entry[root].key = rk; + return left; + } + else + { + entry[root].index = left; + entry[root].key = lk; + return right; + } + } + } + + /*! + * Update loser tree on insert or decrement of player index first go up the + * tree all the way to the root hand down old winner for the respective + * subtree based on new value, and old winner and loser update each node on + * the path to the root top down. This is implemented recursively + */ + void update_on_insert(unsigned_type node, + const value_type& newKey, unsigned_type newIndex, + value_type* winner_key, + unsigned_type* winner_index, // old winner + unsigned_type* mask) // 1 << (ceil(log KNK) - dist-from-root) + { + if (node == 0) + { + // winner part of root + *mask = (unsigned_type)(1) << (logK - 1); + *winner_key = entry[0].key; + *winner_index = entry[0].index; + if (cmp(entry[node].key, newKey)) + { + entry[node].key = newKey; + entry[node].index = newIndex; + } + } + else + { + update_on_insert(node >> 1, newKey, newIndex, winner_key, winner_index, mask); + value_type loserKey = entry[node].key; + unsigned_type loserIndex = entry[node].index; + if ((*winner_index & *mask) != (newIndex & *mask)) { // different subtrees + // newKey will have influence here + if (cmp(loserKey, newKey)) { + if (cmp(*winner_key, newKey)) { + // old winner loses here + entry[node].key = *winner_key; + entry[node].index = *winner_index; + } + else { + // new entry loses here + entry[node].key = newKey; + entry[node].index = newIndex; + } + } + *winner_key = loserKey; + *winner_index = loserIndex; + } + // note that nothing needs to be done if the winner came from the + // same subtree + // a) newKey <= winner_key => even more reason for the other tree to lose + // b) newKey > winner_key => the old winner will beat the new + // entry further down the tree + // also the same old winner is handed down the tree + + *mask >>= 1; // next level + } + } + + //! Initial call to recursive update_on_insert + void update_on_insert(unsigned_type node, + const value_type& newKey, unsigned_type newIndex) + { + value_type dummyKey; + unsigned_type dummyIndex, dummyMask; + update_on_insert(node, newKey, newIndex, + &dummyKey, &dummyIndex, &dummyMask); + } + + //! make the tree twice as wide + void double_k() + { + STXXL_VERBOSE1("double_k (before) k=" << k << " logK=" << logK << " arity=" << arity << " max_arity=" << max_arity << " #free=" << free_slots.size()); + assert(k > 0); + assert(k < arity); + assert(free_slots.empty()); // stack was free (probably not needed) + + // make all new entries free and push them on the free stack + for (unsigned_type i = 2 * k - 1; i >= k; i--) //backwards + { + arrays.make_array_sentinel(i); + if (i < arity) + free_slots.push(i); + } + + // double the size + k *= 2; + logK++; + + STXXL_VERBOSE1("double_k (after) k=" << k << " logK=" << logK << " arity=" << arity << " max_arity=" << max_arity << " #free=" << free_slots.size()); + assert(!free_slots.empty()); + assert(k <= max_arity); + + // recompute loser tree information + rebuild_loser_tree(); + } + + //! compact nonempty segments in the left half of the tree + void compact_tree() + { + STXXL_VERBOSE3("compact_tree (before) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); + assert(logK > 0); + + // compact all nonempty segments to the left + unsigned_type last_empty = 0; + for (unsigned_type pos = 0; pos < k; pos++) + { + if (!arrays.is_array_empty(pos)) + { + assert(arrays.is_array_allocated(pos)); + if (pos != last_empty) + { + assert(!arrays.is_array_allocated(last_empty)); + arrays.swap_arrays(last_empty, pos); + } + ++last_empty; + } + /* + else + { + if(segment[pos]) + { + STXXL_VERBOSE2("int_arrays::compact_tree() deleting segment "< 1) && last_empty <= (k / 2)) + { + k /= 2; + logK--; + } + + // overwrite garbage and compact the stack of free segment indices + free_slots.clear(); // none free + for ( ; last_empty < k; last_empty++) + { + assert(!arrays.is_array_allocated(last_empty)); + arrays.make_array_sentinel(last_empty); + if (last_empty < arity) + free_slots.push(last_empty); + } + + STXXL_VERBOSE3("compact_tree (after) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); + + // recompute loser tree information + rebuild_loser_tree(); + } + + //! compact tree if it got considerably smaller + void maybe_compact() + { + const unsigned_type num_segments_used = k - free_slots.size(); + const unsigned_type num_segments_trigger = k - (3 * k / 5); + // using k/2 would be worst case inefficient (for large k) + // for k \in {2, 4, 8} the trigger is k/2 which is good + // because we have special mergers for k \in {1, 2, 4} + // there is also a special 3-way-merger, that will be + // triggered if k == 4 && is_array_atsentinel(3) + STXXL_VERBOSE3("int_merger compact? k=" << k << " #used=" << num_segments_used + << " <= #trigger=" << num_segments_trigger << " ==> " + << ((k > 1 && num_segments_used <= num_segments_trigger) ? "yes" : "no ") + << " || " + << ((k == 4 && !free_slots.empty() && !arrays.is_array_empty(3)) ? "yes" : "no ") + << " #free=" << free_slots.size()); + if (k > 1 && + ((num_segments_used <= num_segments_trigger) || + (k == 4 && !free_slots.empty() && !arrays.is_array_empty(3)))) + { + compact_tree(); + } + } + + //! multi-merge for arbitrary K + template + void multi_merge_k(OutputIterator begin, OutputIterator end) + { + Entry* current_pos; + value_type current_key; + unsigned_type current_index; // leaf pointed to by current entry + unsigned_type winner_index = entry[0].index; + value_type winner_key = entry[0].key; + + while (begin != end) + { + // write result + *begin++ = *arrays.get_array(winner_index); + + // advance winner segment + ++(arrays.get_array(winner_index)); + + winner_key = *arrays.get_array(winner_index); + + // remove winner segment if empty now + if (is_sentinel(winner_key)) + arrays.free_array(winner_index); + + // go up the entry-tree + for (unsigned_type i = (winner_index + k) >> 1; i > 0; i >>= 1) + { + current_pos = entry + i; + current_key = current_pos->key; + if (cmp(winner_key, current_key)) + { + current_index = current_pos->index; + current_pos->key = winner_key; + current_pos->index = winner_index; + winner_key = current_key; + winner_index = current_index; + } + } + } + entry[0].index = winner_index; + entry[0].key = winner_key; + } + + template + void multi_merge_f(OutputIterator begin, OutputIterator end) + { + unsigned_type winner_index = entry[0].index; + value_type winner_key = entry[0].key; + + // TODO: reinsert assert(log_k >= LogK); + while (begin != end) + { + // write result + *begin++ = *arrays.get_array(winner_index); + + // advance winner segment + ++(arrays.get_array(winner_index)); + + winner_key = *arrays.get_array(winner_index); + + // remove winner segment if empty now + if (is_sentinel(winner_key)) + arrays.free_array(winner_index); + + // update loser tree +#define TreeStep(L) \ + if (1 << LogK >= 1 << L) { \ + int pos_shift = ((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0; \ + Entry* pos = entry + ((winner_index + (1 << LogK)) >> pos_shift); \ + value_type key = pos->key; \ + if (cmp(winner_key, key)) { \ + unsigned_type index = pos->index; \ + pos->key = winner_key; \ + pos->index = winner_index; \ + winner_key = key; \ + winner_index = index; \ + } \ + } + TreeStep(10); + TreeStep(9); + TreeStep(8); + TreeStep(7); + TreeStep(6); + TreeStep(5); + TreeStep(4); + TreeStep(3); + TreeStep(2); + TreeStep(1); +#undef TreeStep + } + entry[0].index = winner_index; + entry[0].key = winner_key; + } + + //! extract the (length = end - begin) smallest elements and write them to + //! [begin..end) empty segments are deallocated. Requires: there are at + //! least length elements and segments are ended by sentinels. + template + void multi_merge(OutputIterator begin, OutputIterator end) + { + int_type length = end - begin; + + STXXL_VERBOSE3("multi_merge(length=" << length << ") from sequences k=" << k); + + if (begin == end) + return; + + assert(k > 0); + + // This is the place to make statistics about external multi_merge calls. + + arrays.prefetch_arrays(); + + switch (logK) { + case 0: { + assert(k == 1); + assert(entry[0].index == 0); + assert(free_slots.empty()); + + // in int_merger: + // memcpy(target, states[0], length * sizeof(value_type)); + + sequence_type& seq = arrays.get_array(0); + for (int_type i = 0; i < length; ++i, ++seq, ++begin) + *begin = *seq; + entry[0].key = *seq; + + if (arrays.is_array_empty(0) && arrays.is_array_allocated(0)) + arrays.free_array(0); + + break; + } + case 1: + assert(k == 2); + merge2_iterator(arrays.get_array(0), arrays.get_array(1), + begin, end, cmp); + rebuild_loser_tree(); + + if (arrays.is_array_empty(0) && arrays.is_array_allocated(0)) + arrays.free_array(0); + + if (arrays.is_array_empty(1) && arrays.is_array_allocated(1)) + arrays.free_array(1); + + break; + case 2: + assert(k == 4); + if (arrays.is_array_empty(3)) + merge3_iterator(arrays.get_array(0), arrays.get_array(1), + arrays.get_array(2), + begin, end, cmp); + else + merge4_iterator(arrays.get_array(0), arrays.get_array(1), + arrays.get_array(2), arrays.get_array(3), + begin, end, cmp); + + rebuild_loser_tree(); + + if (arrays.is_array_empty(0) && arrays.is_array_allocated(0)) + arrays.free_array(0); + + if (arrays.is_array_empty(1) && arrays.is_array_allocated(1)) + arrays.free_array(1); + + if (arrays.is_array_empty(2) && arrays.is_array_allocated(2)) + arrays.free_array(2); + + if (arrays.is_array_empty(3) && arrays.is_array_allocated(3)) + arrays.free_array(3); + + break; + case 3: multi_merge_f<3>(begin, end); + break; + case 4: multi_merge_f<4>(begin, end); + break; + case 5: multi_merge_f<5>(begin, end); + break; + case 6: multi_merge_f<6>(begin, end); + break; + case 7: multi_merge_f<7>(begin, end); + break; + case 8: multi_merge_f<8>(begin, end); + break; + case 9: multi_merge_f<9>(begin, end); + break; + case 10: multi_merge_f<10>(begin, end); + break; + default: multi_merge_k(begin, end); + break; + } + + maybe_compact(); + + //std::copy(target,target + length,std::ostream_iterator(std::cout, "\n")); + } + + void swap(loser_tree& obj) + { + std::swap(free_slots, obj.free_slots); + swap_1D_arrays(entry, obj.entry, max_arity); + } +}; + +#if STXXL_PARALLEL && (STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL || STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL) +/*! + * Adapter from loser_tree to parallel merger + * __gnu_parallel::multiway_merge. This class holds most attributes of + * loser_tree, except for the tree itself: it thus basically only manages an + * array of sequence. + * \tparam Arity maximum arity of merger, does not need to be a power of 2 + */ +template +class parallel_merger_adapter : private noncopyable +{ +public: + //! type of arrays container linked with loser tree + typedef ArraysType arrays_type; + //! comparator object type + typedef CompareType compare_type; + + // arity_bound / 2 < arity <= arity_bound + enum { arity = Arity, max_arity = 1UL << (LOG2::ceil) }; + + //! type of values stored in the arrays container + typedef typename arrays_type::value_type value_type; + //! type of the ordered sequences in the arrays container + typedef typename arrays_type::sequence_type sequence_type; + +public: + //! the comparator object + compare_type cmp; + + //! current tree size, invariant (k == 1 << logK), always a power of two + unsigned_type k; + //! log of current tree size + unsigned_type logK; + +protected: + //! reference to the linked arrays + arrays_type& arrays; + + //! stack of free player indices + internal_bounded_stack free_slots; + +public: + parallel_merger_adapter(const compare_type& c, arrays_type& a) + : cmp(c), k(1), logK(0), arrays(a) + { + // verify strict weak ordering + assert(!cmp(cmp.min_value(), cmp.min_value())); + } + + void initialize() + { + // initial state: one empty player slot + free_slots.push(0); + } + + //! True if a is the sentinel value + bool is_sentinel(const value_type& a) const + { + return !(cmp(cmp.min_value(), a)); // a <= cmp.min_value() + } + + //! Allocate a free slot for a new player. + unsigned_type new_player() + { + // get a free slot + if (free_slots.empty()) { + // tree is too small, attempt to enlarge + double_k(); + } + + assert(!free_slots.empty()); + unsigned_type index = free_slots.top(); + free_slots.pop(); + + return index; + } + + //! Free a finished player's slot + void free_player(unsigned_type slot) + { + free_slots.push(slot); + } + + //! Whether there is still space for new array + bool is_space_available() const + { + return (k < arity) || !free_slots.empty(); + } + + //! Initial call to recursive update_on_insert + void update_on_insert(unsigned_type /* node */, + const value_type& /* newKey */, + unsigned_type /* newIndex */) + { } + + //! make the tree twice as wide + void double_k() + { + STXXL_VERBOSE1("double_k (before) k=" << k << " logK=" << logK << " arity=" << arity << " max_arity=" << max_arity << " #free=" << free_slots.size()); + assert(k > 0); + assert(k < arity); + assert(free_slots.empty()); // stack was free (probably not needed) + + // make all new entries free and push them on the free stack + for (unsigned_type i = 2 * k - 1; i >= k; i--) //backwards + { + arrays.make_array_sentinel(i); + if (i < arity) + free_slots.push(i); + } + + // double the size + k *= 2; + logK++; + + STXXL_VERBOSE1("double_k (after) k=" << k << " logK=" << logK << " arity=" << arity << " max_arity=" << max_arity << " #free=" << free_slots.size()); + assert(!free_slots.empty()); + assert(k <= max_arity); + } + + //! compact nonempty segments in the left half of the tree + void compact_tree() + { + STXXL_VERBOSE3("compact_tree (before) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); + assert(logK > 0); + + // compact all nonempty segments to the left + unsigned_type last_empty = 0; + for (unsigned_type pos = 0; pos < k; pos++) + { + if (!arrays.is_array_empty(pos)) + { + assert(arrays.is_array_allocated(pos)); + if (pos != last_empty) + { + assert(!arrays.is_array_allocated(last_empty)); + arrays.swap_arrays(last_empty, pos); + } + ++last_empty; + } + /* + else + { + if(segment[pos]) + { + STXXL_VERBOSE2("int_arrays::compact_tree() deleting segment "< 1) && last_empty <= (k / 2)) + { + k /= 2; + logK--; + } + + // overwrite garbage and compact the stack of free segment indices + free_slots.clear(); // none free + for ( ; last_empty < k; last_empty++) + { + assert(!arrays.is_array_allocated(last_empty)); + arrays.make_array_sentinel(last_empty); + if (last_empty < arity) + free_slots.push(last_empty); + } + + STXXL_VERBOSE3("compact_tree (after) k=" << k << " logK=" << logK << " #free=" << free_slots.size()); + } + + //! compact tree if it got considerably smaller + void maybe_compact() + { + const unsigned_type num_segments_used = k - free_slots.size(); + const unsigned_type num_segments_trigger = k - (3 * k / 5); + // using k/2 would be worst case inefficient (for large k) + // for k \in {2, 4, 8} the trigger is k/2 which is good + // because we have special mergers for k \in {1, 2, 4} + // there is also a special 3-way-merger, that will be + // triggered if k == 4 && is_array_atsentinel(3) + STXXL_VERBOSE3("int_merger compact? k=" << k << " #used=" << num_segments_used + << " <= #trigger=" << num_segments_trigger << " ==> " + << ((k > 1 && num_segments_used <= num_segments_trigger) ? "yes" : "no ") + << " || " + << ((k == 4 && !free_slots.empty() && !arrays.is_array_empty(3)) ? "yes" : "no ") + << " #free=" << free_slots.size()); + if (k > 1 && + ((num_segments_used <= num_segments_trigger) || + (k == 4 && !free_slots.empty() && !arrays.is_array_empty(3)))) + { + compact_tree(); + } + } + + void swap(parallel_merger_adapter& obj) + { + std::swap(free_slots, obj.free_slots); + } +}; +#endif // STXXL_PARALLEL && (STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL || STXXL_PARALLEL_PQ_MULTIWAY_MERGE_INTERNAL) + +} // namespace priority_queue_local + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_PQ_MERGERS_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/priority_queue.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/priority_queue.h new file mode 100644 index 0000000000..9bead59410 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/priority_queue.h @@ -0,0 +1,1056 @@ +/*************************************************************************** + * include/stxxl/bits/containers/priority_queue.h + * + * Implements a data structure from "Peter Sanders. Fast Priority Queues for + * Cached Memory. ALENEX'99" for external memory. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 1999 Peter Sanders + * Copyright (C) 2003, 2004, 2007 Roman Dementiev + * Copyright (C) 2007-2009 Johannes Singler + * Copyright (C) 2007-2010 Andreas Beckmann + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_CONTAINERS_PRIORITY_QUEUE_HEADER +#define STXXL_CONTAINERS_PRIORITY_QUEUE_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +/* + KNBufferSize1 = 32; + KNN = 512; // length of group 1 sequences + KNKMAX = 64; // maximal arity + LogKNKMAX = 6; // ceil(log KNKMAX) + KNLevels = 4; // overall capacity >= KNN*KNKMAX^KNLevels + */ + +// internal memory consumption >= N_*(KMAX_^IntLevels_) + ext + +template < + class ValueType, + class CompareType, + unsigned BufferSize1_ = 32, // equalize procedure call overheads etc. + unsigned N_ = 512, // length of group 1 sequences + unsigned IntKMAX_ = 64, // maximal arity for internal mergers + unsigned IntLevels_ = 4, // number of internal groups + unsigned BlockSize_ = (2* 1024* 1024), // external block size + unsigned ExtKMAX_ = 64, // maximal arity for external mergers + unsigned ExtLevels_ = 2, // number of external groups + class AllocStr_ = STXXL_DEFAULT_ALLOC_STRATEGY + > +struct priority_queue_config +{ + typedef ValueType value_type; + typedef CompareType comparator_type; + typedef AllocStr_ alloc_strategy_type; + enum + { + delete_buffer_size = BufferSize1_, + N = N_, + IntKMAX = IntKMAX_, + num_int_groups = IntLevels_, + num_ext_groups = ExtLevels_, + BlockSize = BlockSize_, + ExtKMAX = ExtKMAX_, + element_size = sizeof(ValueType) + }; +}; + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::priority_queue_local::ext_merger& a, + stxxl::priority_queue_local::ext_merger& b) +{ + a.swap(b); +} +template +void swap(stxxl::priority_queue_local::int_merger& a, + stxxl::priority_queue_local::int_merger& b) +{ + a.swap(b); +} + +} // namespace std + +STXXL_BEGIN_NAMESPACE + +//! External priority queue data structure \n +//! Introduction to priority queue container: see \ref tutorial_pqueue tutorial. \n +//! Design and Internals of priority queue container: see \ref design_pqueue. +template +class priority_queue : private noncopyable +{ +public: + typedef ConfigType Config; + enum + { + delete_buffer_size = Config::delete_buffer_size, + N = Config::N, + IntKMAX = Config::IntKMAX, + num_int_groups = Config::num_int_groups, + num_ext_groups = Config::num_ext_groups, + total_num_groups = Config::num_int_groups + Config::num_ext_groups, + BlockSize = Config::BlockSize, + ExtKMAX = Config::ExtKMAX + }; + + //! The type of object stored in the priority_queue. + typedef typename Config::value_type value_type; + //! Comparison object. + typedef typename Config::comparator_type comparator_type; + typedef typename Config::alloc_strategy_type alloc_strategy_type; + //! An unsigned integral type (64 bit). + typedef stxxl::uint64 size_type; + //! Type of the block used in disk-memory transfers + typedef typed_block block_type; + typedef read_write_pool pool_type; + +protected: + typedef priority_queue_local::internal_priority_queue, comparator_type> + insert_heap_type; + + typedef priority_queue_local::int_merger< + value_type, + comparator_type, + IntKMAX> int_merger_type; + + typedef priority_queue_local::ext_merger< + block_type, + comparator_type, + ExtKMAX, + alloc_strategy_type> ext_merger_type; + + int_merger_type int_mergers[num_int_groups]; + pool_type* pool; + bool pool_owned; + ext_merger_type** ext_mergers; + + // one delete buffer for each tree => group buffer + value_type group_buffers[total_num_groups][N + 1]; // tree->group_buffers->delete_buffer (extra space for sentinel) + value_type* group_buffer_current_mins[total_num_groups]; // group_buffer_current_mins[i] is current start of group_buffers[i], end is group_buffers[i] + N + + // overall delete buffer + value_type delete_buffer[delete_buffer_size + 1]; + value_type* delete_buffer_current_min; // current start of delete_buffer + value_type* delete_buffer_end; // end of delete_buffer + + comparator_type cmp; + + // insert buffer + insert_heap_type insert_heap; + + // how many groups are active + unsigned_type num_active_groups; + + // total size not counting insert_heap and delete_buffer + size_type size_; + +private: + void init(); + + void refill_delete_buffer(); + size_type refill_group_buffer(unsigned_type k); + + unsigned_type make_space_available(unsigned_type level); + void empty_insert_heap(); + + value_type get_supremum() const { return cmp.min_value(); } //{ return group_buffers[0][KNN].key; } + unsigned_type current_delete_buffer_size() const { return delete_buffer_end - delete_buffer_current_min; } + unsigned_type current_group_buffer_size(unsigned_type i) const { return &(group_buffers[i][N]) - group_buffer_current_mins[i]; } + +public: + //! \name Constructors/Destructors + //! \{ + + //! Constructs external priority queue object. + //! \param pool_ pool of blocks that will be used + //! for data writing and prefetching for the disk<->memory transfers + //! happening in the priority queue. Larger pool size + //! helps to speed up operations. + priority_queue(pool_type& pool_); + + //! Constructs external priority queue object. + //! \param p_pool_ pool of blocks that will be used + //! for data prefetching for the disk<->memory transfers + //! happening in the priority queue. Larger pool size + //! helps to speed up operations. + //! \param w_pool_ pool of blocks that will be used + //! for writing data for the memory<->disk transfers + //! happening in the priority queue. Larger pool size + //! helps to speed up operations. + STXXL_DEPRECATED( + priority_queue(prefetch_pool& p_pool_, write_pool& w_pool_) + ); + + //! Constructs external priority queue object. + //! \param p_pool_mem memory (in bytes) for prefetch pool that will be used + //! for data prefetching for the disk<->memory transfers + //! happening in the priority queue. Larger pool size + //! helps to speed up operations. + //! \param w_pool_mem memory (in bytes) for buffered write pool that will be used + //! for writing data for the memory<->disk transfers + //! happening in the priority queue. Larger pool size + //! helps to speed up operations. + priority_queue(unsigned_type p_pool_mem, unsigned_type w_pool_mem); + + virtual ~priority_queue(); + + //! \} + +#if 0 + + //! swap this priority queue with another one. + //! Implementation correctness is questionable. + void swap(priority_queue& obj) + { + //swap_1D_arrays(int_mergers,obj.int_mergers,num_int_groups); // does not work in g++ 3.4.3 :( bug? + for (unsigned_type i = 0; i < num_int_groups; ++i) + std::swap(int_mergers[i], obj.int_mergers[i]); + + //std::swap(pool,obj.pool); + //std::swap(pool_owned, obj.pool_owned); + std::swap(ext_mergers, obj.ext_mergers); + for (unsigned_type i1 = 0; i1 < total_num_groups; ++i1) + for (unsigned_type i2 = 0; i2 < (N + 1); ++i2) + std::swap(group_buffers[i1][i2], obj.group_buffers[i1][i2]); + + STXXL_STATIC_ASSERT(false); + // Shoot yourself in the foot: group_buffer_current_mins contains pointers into group_buffers ... + // either recompute them or add/subtract (&this->group_buffers[0][0] - &obj->group_buffers[0][0]) + swap_1D_arrays(group_buffer_current_mins, obj.group_buffer_current_mins, total_num_groups); + swap_1D_arrays(delete_buffer, obj.delete_buffer, delete_buffer_size + 1); + std::swap(delete_buffer_current_min, obj.delete_buffer_current_min); + std::swap(delete_buffer_end, obj.delete_buffer_end); + std::swap(cmp, obj.cmp); + std::swap(insert_heap, obj.insert_heap); + std::swap(num_active_groups, obj.num_active_groups); + std::swap(size_, obj.size_); + } +#endif + + //! \name Capacity + //! \{ + + //! Returns number of elements contained. + //! \return number of elements contained + size_type size() const; + + //! Returns true if queue has no elements. + //! \return \b true if queue has no elements, \b false otherwise + bool empty() const { return (size() == 0); } + + //! \} + + //! \name Operators + //! \{ + + //! Returns "largest" element. + //! + //! Returns a const reference to the element at the top of the + //! priority_queue. The element at the top is guaranteed to be the largest + //! element in the \b priority queue, as determined by the comparison + //! function \b comparator_type (the same as the second parameter of + //! PRIORITY_QUEUE_GENERATOR utility class). That is, for every other + //! element \b x in the priority_queue, \b comparator_type(Q.top(), x) is + //! false. Precondition: \c empty() is false. + const value_type & top() const; + + //! \} + + //! \name Modifiers + //! \{ + + //! Removes the element at the top. + //! + //! Removes the element at the top of the priority_queue, that is, the + //! largest element in the \b priority_queue. Precondition: \c empty() is + //! \b false. Postcondition: \c size() will be decremented by 1. + void pop(); + + //! Inserts x into the priority_queue. + //! + //! Inserts x into the priority_queue. Postcondition: \c size() will be + //! incremented by 1. + void push(const value_type& obj); + + //! \} + + //! \name Miscellaneous + //! \{ + + //! Number of bytes consumed by the \b priority_queue from the internal + //! memory not including pools (see the constructor) + unsigned_type mem_cons() const + { + unsigned_type dynam_alloc_mem = 0; + //dynam_alloc_mem += w_pool.mem_cons(); + //dynam_alloc_mem += p_pool.mem_cons(); + for (int i = 0; i < num_int_groups; ++i) + dynam_alloc_mem += int_mergers[i].mem_cons(); + + for (int i = 0; i < num_ext_groups; ++i) + dynam_alloc_mem += ext_mergers[i]->mem_cons(); + + return (sizeof(*this) + + sizeof(ext_merger_type) * num_ext_groups + + dynam_alloc_mem); + } + + void dump_sizes() const; + void dump_params() const; + + //! \} +}; + +template +inline typename priority_queue::size_type +priority_queue::size() const +{ + return size_ + + insert_heap.size() - 1 + + (delete_buffer_end - delete_buffer_current_min); +} + +template +inline const typename priority_queue::value_type & +priority_queue::top() const +{ + assert(!insert_heap.empty()); + + const typename priority_queue::value_type& t = insert_heap.top(); + if (/*(!insert_heap.empty()) && */ cmp(*delete_buffer_current_min, t)) + return t; + else + return *delete_buffer_current_min; +} + +template +inline void priority_queue::pop() +{ + //STXXL_VERBOSE1("priority_queue::pop()"); + assert(!insert_heap.empty()); + + if (/*(!insert_heap.empty()) && */ cmp(*delete_buffer_current_min, insert_heap.top())) + insert_heap.pop(); + else + { + assert(delete_buffer_current_min < delete_buffer_end); + ++delete_buffer_current_min; + if (delete_buffer_current_min == delete_buffer_end) + refill_delete_buffer(); + } +} + +template +inline void priority_queue::push(const value_type& obj) +{ + //STXXL_VERBOSE3("priority_queue::push("<< obj <<")"); + assert(!int_mergers->is_sentinel(obj)); + if (insert_heap.size() == N + 1) + empty_insert_heap(); + + assert(!insert_heap.empty()); + + insert_heap.push(obj); +} + +//////////////////////////////////////////////////////////////// + +template +priority_queue::priority_queue(pool_type& pool_) + : pool(&pool_), + pool_owned(false), + delete_buffer_end(delete_buffer + delete_buffer_size), + insert_heap(N + 2), + num_active_groups(0), size_(0) +{ + STXXL_VERBOSE_PQ("priority_queue(pool)"); + init(); +} + +// DEPRECATED +template +priority_queue::priority_queue(prefetch_pool& p_pool_, write_pool& w_pool_) + : pool(new pool_type(p_pool_, w_pool_)), + pool_owned(true), + delete_buffer_end(delete_buffer + delete_buffer_size), + insert_heap(N + 2), + num_active_groups(0), size_(0) +{ + STXXL_VERBOSE_PQ("priority_queue(p_pool, w_pool)"); + init(); +} + +template +priority_queue::priority_queue(unsigned_type p_pool_mem, unsigned_type w_pool_mem) + : pool(new pool_type(p_pool_mem / BlockSize, w_pool_mem / BlockSize)), + pool_owned(true), + delete_buffer_end(delete_buffer + delete_buffer_size), + insert_heap(N + 2), + num_active_groups(0), size_(0) +{ + STXXL_VERBOSE_PQ("priority_queue(pool sizes)"); + init(); +} + +template +void priority_queue::init() +{ + assert(!cmp(cmp.min_value(), cmp.min_value())); // verify strict weak ordering + + ext_mergers = new ext_merger_type*[num_ext_groups]; + for (unsigned_type j = 0; j < num_ext_groups; ++j) { + ext_mergers[j] = new ext_merger_type; + ext_mergers[j]->set_pool(pool); + } + + value_type sentinel = cmp.min_value(); + insert_heap.push(sentinel); // always keep the sentinel + delete_buffer[delete_buffer_size] = sentinel; // sentinel + delete_buffer_current_min = delete_buffer_end; // empty + for (unsigned_type i = 0; i < total_num_groups; i++) + { + group_buffers[i][N] = sentinel; // sentinel + group_buffer_current_mins[i] = &(group_buffers[i][N]); // empty + } +} + +template +priority_queue::~priority_queue() +{ + STXXL_VERBOSE_PQ("~priority_queue()"); + if (pool_owned) + delete pool; + + for (unsigned_type j = 0; j < num_ext_groups; ++j) + delete ext_mergers[j]; + delete[] ext_mergers; +} + +//--------------------- Buffer refilling ------------------------------- + +// refill group_buffers[j] and return number of elements found +template +typename priority_queue::size_type +priority_queue::refill_group_buffer(unsigned_type group) +{ + STXXL_VERBOSE_PQ("refill_group_buffer(" << group << ")"); + + value_type* target; + size_type length; + size_type group_size = (group < num_int_groups) ? + int_mergers[group].size() : + ext_mergers[group - num_int_groups]->size(); // elements left in segments + unsigned_type left_elements = group_buffers[group] + N - group_buffer_current_mins[group]; //elements left in target buffer + if (group_size + left_elements >= size_type(N)) + { // buffer will be filled completely + target = group_buffers[group]; + length = N - left_elements; + } + else + { + target = group_buffers[group] + N - group_size - left_elements; + length = group_size; + } + + if (length > 0) + { + // shift remaininig elements to front + memmove(target, group_buffer_current_mins[group], left_elements * sizeof(value_type)); + group_buffer_current_mins[group] = target; + + // fill remaining space from group + if (group < num_int_groups) + int_mergers[group].multi_merge(target + left_elements, + target + left_elements + length); + else + ext_mergers[group - num_int_groups]->multi_merge( + target + left_elements, + target + left_elements + length); + } + + //STXXL_MSG(length + left_elements); + //std::copy(target,target + length + left_elements,std::ostream_iterator(std::cout, "\n")); +#if STXXL_CHECK_ORDER_IN_SORTS + priority_queue_local::invert_order inv_cmp(cmp); + if (!stxxl::is_sorted(group_buffer_current_mins[group], group_buffers[group] + N, inv_cmp)) + { + STXXL_VERBOSE_PQ("refill_grp... length: " << length << " left_elements: " << left_elements); + for (value_type* v = group_buffer_current_mins[group] + 1; v < group_buffer_current_mins[group] + left_elements; ++v) + { + if (inv_cmp(*v, *(v - 1))) + { + STXXL_MSG("Error in buffer " << group << " at position " << (v - group_buffer_current_mins[group] - 1) << "/" << (v - group_buffer_current_mins[group]) << " " << *(v - 2) << " " << *(v - 1) << " " << *v << " " << *(v + 1)); + } + } + assert(false); + } +#endif + + return length + left_elements; +} + +template +void priority_queue::refill_delete_buffer() +{ + STXXL_VERBOSE_PQ("refill_delete_buffer()"); + + size_type total_group_size = 0; + //num_active_groups is <= 4 + for (unsigned_type i = num_active_groups; i > 0; ) + { + --i; + if ((group_buffers[i] + N) - group_buffer_current_mins[i] < delete_buffer_size) + { + size_type length = refill_group_buffer(i); + // max active level dry now? + if (length == 0 && unsigned(i) == num_active_groups - 1) + --num_active_groups; + + total_group_size += length; + } + else + total_group_size += delete_buffer_size; // actually only a sufficient lower bound + } + + size_type length; + if (total_group_size >= delete_buffer_size) // buffer can be filled completely + { + length = delete_buffer_size; // amount to be copied + size_ -= size_type(delete_buffer_size); // amount left in group_buffers + } + else + { + length = total_group_size; + assert(size_ == length); // trees and group_buffers get empty + size_ = 0; + } + + priority_queue_local::invert_order inv_cmp(cmp); + + // now call simplified refill routines + // which can make the assumption that + // they find all they are asked in the buffers + delete_buffer_current_min = delete_buffer_end - length; + STXXL_VERBOSE_PQ("refill_del... Active groups = " << num_active_groups); + switch (num_active_groups) + { + case 0: + break; + case 1: + std::copy(group_buffer_current_mins[0], group_buffer_current_mins[0] + length, delete_buffer_current_min); + group_buffer_current_mins[0] += length; + break; + case 2: +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER + { + std::pair seqs[2] = + { + std::make_pair(group_buffer_current_mins[0], group_buffers[0] + N), + std::make_pair(group_buffer_current_mins[1], group_buffers[1] + N) + }; + + parallel::multiway_merge_sentinels( + seqs, seqs + 2, delete_buffer_current_min, length, inv_cmp); + // sequence iterators are progressed appropriately + + group_buffer_current_mins[0] = seqs[0].first; + group_buffer_current_mins[1] = seqs[1].first; + } +#else + priority_queue_local::merge2_iterator( + group_buffer_current_mins[0], group_buffer_current_mins[1], + delete_buffer_current_min, delete_buffer_current_min + length, cmp); +#endif + break; + case 3: +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER + { + std::pair seqs[3] = + { + std::make_pair(group_buffer_current_mins[0], group_buffers[0] + N), + std::make_pair(group_buffer_current_mins[1], group_buffers[1] + N), + std::make_pair(group_buffer_current_mins[2], group_buffers[2] + N) + }; + + parallel::multiway_merge_sentinels( + seqs, seqs + 3, delete_buffer_current_min, length, inv_cmp); + // sequence iterators are progressed appropriately + + group_buffer_current_mins[0] = seqs[0].first; + group_buffer_current_mins[1] = seqs[1].first; + group_buffer_current_mins[2] = seqs[2].first; + } +#else + priority_queue_local::merge3_iterator( + group_buffer_current_mins[0], + group_buffer_current_mins[1], + group_buffer_current_mins[2], + delete_buffer_current_min, delete_buffer_current_min + length, cmp); +#endif + break; + case 4: +#if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_DELETE_BUFFER + { + std::pair seqs[4] = + { + std::make_pair(group_buffer_current_mins[0], group_buffers[0] + N), + std::make_pair(group_buffer_current_mins[1], group_buffers[1] + N), + std::make_pair(group_buffer_current_mins[2], group_buffers[2] + N), + std::make_pair(group_buffer_current_mins[3], group_buffers[3] + N) + }; + + parallel::multiway_merge_sentinels( + seqs, seqs + 4, delete_buffer_current_min, length, inv_cmp); + // sequence iterators are progressed appropriately + + group_buffer_current_mins[0] = seqs[0].first; + group_buffer_current_mins[1] = seqs[1].first; + group_buffer_current_mins[2] = seqs[2].first; + group_buffer_current_mins[3] = seqs[3].first; + } +#else + priority_queue_local::merge4_iterator( + group_buffer_current_mins[0], + group_buffer_current_mins[1], + group_buffer_current_mins[2], + group_buffer_current_mins[3], + delete_buffer_current_min, delete_buffer_current_min + length, cmp); + // side effect free +#endif + break; + default: + STXXL_THROW2(std::runtime_error, "priority_queue<...>::refill_delete_buffer()", + "Overflow! The number of buffers on 2nd level in stxxl::priority_queue is currently limited to 4"); + } + +#if STXXL_CHECK_ORDER_IN_SORTS + if (!stxxl::is_sorted(delete_buffer_current_min, delete_buffer_end, inv_cmp)) + { + for (value_type* v = delete_buffer_current_min + 1; v < delete_buffer_end; ++v) + { + if (inv_cmp(*v, *(v - 1))) + { + STXXL_MSG("Error at position " << (v - delete_buffer_current_min - 1) << "/" << (v - delete_buffer_current_min) << " " << *(v - 1) << " " << *v); + } + } + assert(false); + } +#endif + //std::copy(delete_buffer_current_min,delete_buffer_current_min + length,std::ostream_iterator(std::cout, "\n")); +} + +//-------------------------------------------------------------------- + +// check if space is available on level k and +// empty this level if necessary leading to a recursive call. +// return the level where space was finally available +template +unsigned_type priority_queue::make_space_available(unsigned_type level) +{ + STXXL_VERBOSE_PQ("make_space_available(" << level << ")"); + unsigned_type finalLevel; + assert(level < total_num_groups); + assert(level <= num_active_groups); + + if (level == num_active_groups) + ++num_active_groups; + + const bool spaceIsAvailable_ = + (level < num_int_groups) ? int_mergers[level].is_space_available() + : (ext_mergers[level - num_int_groups]->is_space_available()); + + if (spaceIsAvailable_) + { + finalLevel = level; + } + else if (level == total_num_groups - 1) + { + size_type capacity = N; + for (int i = 0; i < num_int_groups; ++i) + capacity *= IntKMAX; + for (int i = 0; i < num_ext_groups; ++i) + capacity *= ExtKMAX; + STXXL_ERRMSG("priority_queue OVERFLOW - all groups full, size=" << size() << + ", capacity(last externel group (" << num_int_groups + num_ext_groups - 1 << "))=" << capacity); + dump_sizes(); + + unsigned_type extLevel = level - num_int_groups; + const size_type segmentSize = ext_mergers[extLevel]->size(); + STXXL_VERBOSE1("Inserting segment into last level external: " << level << " " << segmentSize); + ext_merger_type* overflow_merger = new ext_merger_type; + overflow_merger->set_pool(pool); + overflow_merger->append_merger(*ext_mergers[extLevel], segmentSize); + std::swap(ext_mergers[extLevel], overflow_merger); + delete overflow_merger; + finalLevel = level; + } + else + { + finalLevel = make_space_available(level + 1); + + if (level < num_int_groups - 1) // from internal to internal tree + { + unsigned_type segmentSize = int_mergers[level].size(); + value_type* newSegment = new value_type[segmentSize + 1]; + int_mergers[level].multi_merge(newSegment, newSegment + segmentSize); // empty this level + + newSegment[segmentSize] = delete_buffer[delete_buffer_size]; // sentinel + // for queues where size << #inserts + // it might make sense to stay in this level if + // segmentSize < alpha * KNN * k^level for some alpha < 1 + int_mergers[level + 1].append_array(newSegment, segmentSize); + } + else + { + if (level == num_int_groups - 1) // from internal to external tree + { + const unsigned_type segmentSize = int_mergers[num_int_groups - 1].size(); + STXXL_VERBOSE_PQ("make_space... Inserting segment into first level external: " << level << " " << segmentSize); + ext_mergers[0]->append_merger(int_mergers[num_int_groups - 1], segmentSize); + } + else // from external to external tree + { + const size_type segmentSize = ext_mergers[level - num_int_groups]->size(); + STXXL_VERBOSE_PQ("make_space... Inserting segment into second level external: " << level << " " << segmentSize); + ext_mergers[level - num_int_groups + 1]->append_merger(*ext_mergers[level - num_int_groups], segmentSize); + } + } + } + return finalLevel; +} + +// empty the insert heap into the main data structure +template +void priority_queue::empty_insert_heap() +{ + STXXL_VERBOSE_PQ("empty_insert_heap()"); + assert(insert_heap.size() == (N + 1)); + + const value_type sup = get_supremum(); + + // build new segment + value_type* newSegment = new value_type[N + 1]; + value_type* newPos = newSegment; + + // put the new data there for now + //insert_heap.sortTo(newSegment); + value_type* SortTo = newSegment; + + insert_heap.sort_to(SortTo); + + SortTo = newSegment + N; + insert_heap.clear(); + insert_heap.push(*SortTo); + + assert(insert_heap.size() == 1); + + newSegment[N] = sup; // sentinel + + // copy the delete_buffer and group_buffers[0] to temporary storage + // (the temporary can be eliminated using some dirty tricks) + const unsigned_type tempSize = N + delete_buffer_size; + value_type temp[tempSize + 1]; + unsigned_type sz1 = current_delete_buffer_size(); + unsigned_type sz2 = current_group_buffer_size(0); + value_type* pos = temp + tempSize - sz1 - sz2; + std::copy(delete_buffer_current_min, delete_buffer_current_min + sz1, pos); + std::copy(group_buffer_current_mins[0], group_buffer_current_mins[0] + sz2, pos + sz1); + temp[tempSize] = sup; // sentinel + + // refill delete_buffer + // (using more complicated code it could be made somewhat fuller + // in certain circumstances) + priority_queue_local::merge2_iterator( + pos, newPos, + delete_buffer_current_min, delete_buffer_current_min + sz1, cmp); + + // refill group_buffers[0] + // (as above we might want to take the opportunity + // to make group_buffers[0] fuller) + priority_queue_local::merge2_iterator( + pos, newPos, + group_buffer_current_mins[0], group_buffer_current_mins[0] + sz2, cmp); + + // merge the rest to the new segment + // note that merge exactly trips into the footsteps + // of itself + priority_queue_local::merge2_iterator(pos, newPos, + newSegment, newSegment + N, cmp); + + // and insert it + unsigned_type freeLevel = make_space_available(0); + assert(freeLevel == 0 || int_mergers[0].size() == 0); + int_mergers[0].append_array(newSegment, N); + + // get rid of invalid level 2 buffers + // by inserting them into tree 0 (which is almost empty in this case) + if (freeLevel > 0) + { + for (int_type i = freeLevel; i >= 0; i--) + { + // reverse order not needed + // but would allow immediate refill + + newSegment = new value_type[current_group_buffer_size(i) + 1]; // with sentinel + std::copy(group_buffer_current_mins[i], group_buffer_current_mins[i] + current_group_buffer_size(i) + 1, newSegment); + int_mergers[0].append_array(newSegment, current_group_buffer_size(i)); + group_buffer_current_mins[i] = group_buffers[i] + N; // empty + } + } + + // update size + size_ += size_type(N); + + // special case if the tree was empty before + if (delete_buffer_current_min == delete_buffer_end) + refill_delete_buffer(); +} + +template +void priority_queue::dump_sizes() const +{ + unsigned_type capacity = N; + STXXL_MSG("pq::size()\t= " << size()); + STXXL_MSG(" insert_heap\t= " << insert_heap.size() - 1 << "/" << capacity); + STXXL_MSG(" delete_buffer\t= " << (delete_buffer_end - delete_buffer_current_min) << "/" << delete_buffer_size); + for (int i = 0; i < num_int_groups; ++i) { + capacity *= IntKMAX; + STXXL_MSG(" grp " << i << " int" << + " grpbuf=" << current_group_buffer_size(i) << + " size=" << int_mergers[i].size() << "/" << capacity << + " (" << (int)((double)int_mergers[i].size() * 100.0 / (double)capacity) << "%)" << + " space=" << int_mergers[i].is_space_available()); + } + for (int i = 0; i < num_ext_groups; ++i) { + capacity *= ExtKMAX; + STXXL_MSG(" grp " << i + num_int_groups << " ext" << + " grpbuf=" << current_group_buffer_size(i + num_int_groups) << + " size=" << ext_mergers[i]->size() << "/" << capacity << + " (" << (int)((double)ext_mergers[i]->size() * 100.0 / (double)capacity) << "%)" << + " space=" << ext_mergers[i]->is_space_available()); + } + dump_params(); +} + +template +void priority_queue::dump_params() const +{ + STXXL_MSG("params: delete_buffer_size=" << delete_buffer_size << " N=" << N << " IntKMAX=" << IntKMAX << " num_int_groups=" << num_int_groups << " ExtKMAX=" << ExtKMAX << " num_ext_groups=" << num_ext_groups << " BlockSize=" << BlockSize); +} + +namespace priority_queue_local { + +struct Parameters_for_priority_queue_not_found_Increase_IntMem +{ + enum { fits = false }; + typedef Parameters_for_priority_queue_not_found_Increase_IntMem result; +}; + +struct dummy +{ + enum { fits = false }; + typedef dummy result; +}; + +template +struct find_B_m +{ + typedef find_B_m self_type; + + //! element size + static const internal_size_type element_size = ElementSize; + //! internal memory size of PQ + static const internal_size_type intmem = IntMem; + //! block size (iterates from 8 MiB downwards) + static const internal_size_type B = BlockSize; + + //! number of blocks that fit into internal memory (M) + static const internal_size_type k = IntMem / BlockSize; + //! number of blocks fitting into buffers of mergers (arity of both + //! mergers), increased from 1 to 2048 ?-tb + static const internal_size_type m = m_; + //! remaining blocks, (freely moving, not necessarily unused) ?-tb + static const int_type c = k - m_; + + // memory occupied by block must be at least 10 times larger than size of ext sequence + + //! calculated boolean whether the configuration fits into internal memory. + static const external_size_type fits = + // need some temporary constant-size internal blocks + (c > 10) && + // satisfy items requirement + (((k - m) * m * (m * B / (ElementSize * 4 * 1024))) >= MaxItems) && + // if we have two ext mergers their degree must be at least 64=m/2 + ((MaxItems < ((k - m) * m / (2 * ElementSize)) * 1024) || m >= 128); + + static const unsigned_type step = 1; + + //! if not fits, recurse into configuration with +step more internal buffers + typedef typename find_B_m= k)>::result candidate1; + //! if not fits, recurse into configuration with block size halved. + typedef typename find_B_m::result candidate2; + + //! return a fitting configuration. + typedef typename IF::result>::result result; +}; + +// specialization for the case when no valid parameters are found +template +struct find_B_m +{ + enum { fits = false }; + typedef Parameters_for_priority_queue_not_found_Increase_IntMem result; +}; + +// to speedup search +template +struct find_B_m +{ + enum { fits = false }; + typedef dummy result; +}; + +// start search +template +struct find_settings +{ + // start from block size (8*1024*1024) bytes + typedef typename find_B_m::result result; +}; + +struct Parameters_not_found_Try_to_change_the_Tune_parameter +{ + typedef Parameters_not_found_Try_to_change_the_Tune_parameter result; +}; + +template +struct compute_N +{ + typedef compute_N Self; + + static const unsigned_type X = X_; + static const unsigned_type AI = AI_; + static const unsigned_type N = X / (AI * AI); // two stage internal + + typedef typename IF<(N >= CriticalSize), Self, typename compute_N::result>::result result; +}; + +template +struct compute_N<1, X_, CriticalSize_> +{ + typedef Parameters_not_found_Try_to_change_the_Tune_parameter result; +}; + +} // namespace priority_queue_local + +//! \} + +//! \addtogroup stlcont +//! \{ + +//! Priority queue type generator. \n +//! Introduction to priority queue container: see \ref tutorial_pqueue tutorial. \n +//! Design and Internals of priority queue container: see \ref design_pqueue. +//! +//! \tparam ValueType type of the contained objects (POD with no references to internal memory) +//! +//! \tparam CompareType the comparator type used to determine whether one element is +//! smaller than another element. +//! +//! \tparam IntMemory upper limit for internal memory consumption in bytes. +//! +//! \tparam MaxItems upper limit for number of elements contained in the priority queue (in 1024 units).
+//! Example: if you are sure that priority queue contains no more than +//! one million elements in a time, then the right parameter is (1000000 / 1024) = 976. +//! +//! \tparam Tune tuning parameter for meta-program search.
+//! Try to play with it if the code does not compile (larger than default +//! values might help). Code does not compile if no suitable internal +//! parameters were found for given IntMemory and MaxItems. It might also +//! happen that given IntMemory is too small for given MaxItems, try larger +//! values. +template +class PRIORITY_QUEUE_GENERATOR +{ +public: + // actual calculation of B, m, k and element_size + typedef typename priority_queue_local::find_settings::result settings; + enum { + B = settings::B, + m = settings::m, + X = B * (settings::k - m) / settings::element_size, // interpretation of result + Buffer1Size = 32 // fixed + }; + // derivation of N, AI, AE + typedef typename priority_queue_local::compute_N<(1 << Tune), X, 4* Buffer1Size>::result ComputeN; + enum + { + N = ComputeN::N, + AI = ComputeN::AI, + AE = (m / 2 < 2) ? 2 : (m / 2) // at least 2 + }; + + // Estimation of maximum internal memory consumption (in bytes) + static const unsigned_type EConsumption = X * settings::element_size + settings::B * AE + ((MaxItems / X) / AE) * settings::B * 1024; + + /* + unsigned BufferSize1_ = 32, // equalize procedure call overheads etc. + unsigned N_ = 512, // bandwidth + unsigned IntKMAX_ = 64, // maximal arity for internal mergers + unsigned IntLevels_ = 4, + unsigned BlockSize = (2*1024*1024), + unsigned ExtKMAX_ = 64, // maximal arity for external mergers + unsigned ExtLevels_ = 2, + */ + typedef priority_queue > result; +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::priority_queue& a, + stxxl::priority_queue& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_PRIORITY_QUEUE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/queue.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/queue.h new file mode 100644 index 0000000000..c164c6b33c --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/queue.h @@ -0,0 +1,421 @@ +/*************************************************************************** + * include/stxxl/bits/containers/queue.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2005 Roman Dementiev + * Copyright (C) 2009, 2010 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_QUEUE_HEADER +#define STXXL_CONTAINERS_QUEUE_HEADER + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +#ifndef STXXL_VERBOSE_QUEUE +#define STXXL_VERBOSE_QUEUE STXXL_VERBOSE2 +#endif + +//! \addtogroup stlcont +//! \{ + +//! External FIFO queue container. \n +//! Introduction to queue container: see \ref tutorial_queue tutorial\n +//! Design and Internals of queue container: see \ref design_queue. +//! +//! \tparam ValueType type of the contained objects (POD with no references to internal memory) +//! \tparam BlockSize size of the external memory block in bytes, default is \c STXXL_DEFAULT_BLOCK_SIZE(ValueType) +//! \tparam AllocStr parallel disk allocation strategy, default is \c STXXL_DEFAULT_ALLOC_STRATEGY +//! \tparam SizeType size data type, default is \c stxxl::uint64 +template +class queue : private noncopyable +{ +public: + typedef ValueType value_type; + typedef AllocStr alloc_strategy_type; + typedef SizeType size_type; + enum { + block_size = BlockSize + }; + + typedef typed_block block_type; + typedef BID bid_type; + +private: + typedef read_write_pool pool_type; + + size_type m_size; + bool delete_pool; + pool_type* pool; + block_type* front_block; + block_type* back_block; + value_type* front_element; + value_type* back_element; + alloc_strategy_type alloc_strategy; + unsigned_type alloc_count; + std::deque bids; + block_manager* bm; + unsigned_type blocks2prefetch; + +public: + //! \name Constructors/Destructors + //! \{ + + //! Constructs empty queue with own write and prefetch block pool. + //! + //! \param D number of parallel disks, defaulting to the configured number of scratch disks, + //! memory consumption will be 2 * D + 2 blocks + //! (first and last block, D blocks as write cache, D block for prefetching) + explicit queue(int_type D = -1) + : m_size(0), + delete_pool(true), + alloc_count(0), + bm(block_manager::get_instance()) + { + if (D < 1) + D = config::get_instance()->disks_number(); + STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(D)"); + pool = new pool_type(D, D + 2); + init(); + } + + //! Constructs empty queue with own write and prefetch block pool. + //! + //! \param w_pool_size number of blocks in the write pool, must be at least 2, recommended at least 3 + //! \param p_pool_size number of blocks in the prefetch pool, recommended at least 1 + //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), + //! default is number of block in the prefetch pool + explicit queue(unsigned_type w_pool_size, unsigned_type p_pool_size, int blocks2prefetch_ = -1) + : m_size(0), + delete_pool(true), + alloc_count(0), + bm(block_manager::get_instance()) + { + STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(sizes)"); + pool = new pool_type(p_pool_size, w_pool_size); + init(blocks2prefetch_); + } + + //! Constructs empty queue. + //! + //! \param w_pool write pool + //! \param p_pool prefetch pool + //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), + //! default is number of blocks in the prefetch pool + //! \warning Number of blocks in the write pool must be at least 2, recommended at least 3 + //! \warning Number of blocks in the prefetch pool recommended at least 1 + STXXL_DEPRECATED( + queue(write_pool& w_pool, prefetch_pool& p_pool, int blocks2prefetch_ = -1)) + : m_size(0), + delete_pool(true), + alloc_count(0), + bm(block_manager::get_instance()) + { + STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(pools)"); + pool = new pool_type(p_pool, w_pool); + init(blocks2prefetch_); + } + + //! Constructs empty queue. + //! + //! \param pool_ block write/prefetch pool + //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), + //! default is number of blocks in the prefetch pool + //! \warning Number of blocks in the write pool must be at least 2, recommended at least 3 + //! \warning Number of blocks in the prefetch pool recommended at least 1 + queue(pool_type& pool_, int blocks2prefetch_ = -1) + : m_size(0), + delete_pool(false), + pool(&pool_), + alloc_count(0), + bm(block_manager::get_instance()) + { + STXXL_VERBOSE_QUEUE("queue[" << this << "]::queue(pool)"); + init(blocks2prefetch_); + } + + //! \} + + //! \name Modifiers + //! \{ + + void swap(queue& obj) + { + std::swap(m_size, obj.m_size); + std::swap(delete_pool, obj.delete_pool); + std::swap(pool, obj.pool); + std::swap(front_block, obj.front_block); + std::swap(back_block, obj.back_block); + std::swap(front_element, obj.front_element); + std::swap(back_element, obj.back_element); + std::swap(alloc_strategy, obj.alloc_strategy); + std::swap(alloc_count, obj.alloc_count); + std::swap(bids, obj.bids); + std::swap(bm, obj.bm); + std::swap(blocks2prefetch, obj.blocks2prefetch); + } + + //! \} + +private: + void init(int blocks2prefetch_ = -1) + { + if (pool->size_write() < 2) { + STXXL_ERRMSG("queue: invalid configuration, not enough blocks (" << pool->size_write() << + ") in write pool, at least 2 are needed, resizing to 3"); + pool->resize_write(3); + } + + if (pool->size_write() < 3) { + STXXL_MSG("queue: inefficient configuration, no blocks for buffered writing available"); + } + + if (pool->size_prefetch() < 1) { + STXXL_MSG("queue: inefficient configuration, no blocks for prefetching available"); + } + + front_block = back_block = pool->steal(); + back_element = back_block->begin() - 1; + front_element = back_block->begin(); + set_prefetch_aggr(blocks2prefetch_); + } + +public: + //! \name Miscellaneous + //! \{ + + //! Defines the number of blocks to prefetch (\c front side). + //! This method should be called whenever the prefetch pool is resized + //! \param blocks2prefetch_ defines the number of blocks to prefetch (\c front side), + //! a negative value means to use the number of blocks in the prefetch pool + void set_prefetch_aggr(int_type blocks2prefetch_) + { + if (blocks2prefetch_ < 0) + blocks2prefetch = pool->size_prefetch(); + else + blocks2prefetch = blocks2prefetch_; + } + + //! Returns the number of blocks prefetched from the \c front side. + unsigned_type get_prefetch_aggr() const + { + return blocks2prefetch; + } + //! \} + + //! \name Modifiers + //! \{ + + //! Adds an element in the queue. + void push(const value_type& val) + { + if (UNLIKELY(back_element == back_block->begin() + (block_type::size - 1))) + { + // back block is filled + if (front_block == back_block) + { // can not write the back block because it + // is the same as the front block, must keep it memory + STXXL_VERBOSE1("queue::push Case 1"); + } + else if (size() < 2 * block_type::size) + { + STXXL_VERBOSE1("queue::push Case 1.5"); + // only two blocks with a gap in the beginning, move elements within memory + assert(bids.empty()); + size_t gap = front_element - front_block->begin(); + assert(gap > 0); + std::copy(front_element, front_block->end(), front_block->begin()); + std::copy(back_block->begin(), back_block->begin() + gap, front_block->begin() + (block_type::size - gap)); + std::copy(back_block->begin() + gap, back_block->end(), back_block->begin()); + front_element -= gap; + back_element -= gap; + + ++back_element; + *back_element = val; + ++m_size; + return; + } + else + { + STXXL_VERBOSE1("queue::push Case 2"); + // write the back block + // need to allocate new block + bid_type newbid; + + bm->new_block(alloc_strategy, newbid, alloc_count++); + + STXXL_VERBOSE_QUEUE("queue[" << this << "]: push block " << back_block << " @ " << FMT_BID(newbid)); + bids.push_back(newbid); + pool->write(back_block, newbid); + if (bids.size() <= blocks2prefetch) { + STXXL_VERBOSE1("queue::push Case Hints"); + pool->hint(newbid); + } + } + back_block = pool->steal(); + + back_element = back_block->begin(); + *back_element = val; + ++m_size; + return; + } + ++back_element; + *back_element = val; + ++m_size; + } + + //! Removes element from the queue. + void pop() + { + assert(!empty()); + + if (UNLIKELY(front_element == front_block->begin() + (block_type::size - 1))) + { + // if there is only one block, it implies ... + if (back_block == front_block) + { + STXXL_VERBOSE1("queue::pop Case 3"); + assert(size() == 1); + assert(back_element == front_element); + assert(bids.empty()); + // reset everything + back_element = back_block->begin() - 1; + front_element = back_block->begin(); + m_size = 0; + return; + } + + --m_size; + if (m_size <= block_type::size) + { + STXXL_VERBOSE1("queue::pop Case 4"); + assert(bids.empty()); + // the back_block is the next block + pool->add(front_block); + front_block = back_block; + front_element = back_block->begin(); + return; + } + STXXL_VERBOSE1("queue::pop Case 5"); + + assert(!bids.empty()); + request_ptr req = pool->read(front_block, bids.front()); + STXXL_VERBOSE_QUEUE("queue[" << this << "]: pop block " << front_block << " @ " << FMT_BID(bids.front())); + + // give prefetching hints + for (unsigned_type i = 0; i < blocks2prefetch && i < bids.size() - 1; ++i) + { + STXXL_VERBOSE1("queue::pop Case Hints"); + pool->hint(bids[i + 1]); + } + + front_element = front_block->begin(); + req->wait(); + + bm->delete_block(bids.front()); + bids.pop_front(); + return; + } + + ++front_element; + --m_size; + } + //! \} + + //! \name Operators + //! \{ + + //! Returns a mutable reference at the back of the queue. + value_type & back() + { + assert(!empty()); + return *back_element; + } + + //! Returns a const reference at the back of the queue. + const value_type & back() const + { + assert(!empty()); + return *back_element; + } + + //! Returns a mutable reference at the front of the queue. + value_type & front() + { + assert(!empty()); + return *front_element; + } + + //! Returns a const reference at the front of the queue. + const value_type & front() const + { + assert(!empty()); + return *front_element; + } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + ~queue() + { + if (front_block != back_block) + pool->add(back_block); + pool->add(front_block); + + if (delete_pool) + { + delete pool; + } + + if (!bids.empty()) + bm->delete_blocks(bids.begin(), bids.end()); + } + + //! \} + + //! \name Capacity + //! \{ + + //! Returns the size of the queue. + size_type size() const + { + return m_size; + } + + //! Returns \c true if queue is empty. + bool empty() const + { + return (m_size == 0); + } + + //! \} +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_QUEUE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sequence.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sequence.h new file mode 100644 index 0000000000..2d1717fb90 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sequence.h @@ -0,0 +1,832 @@ +/*************************************************************************** + * include/stxxl/bits/containers/sequence.h + * + * based on include/stxxl/bits/containers/queue.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2012-2013 Timo Bingmann + * + * 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 STXXL_CONTAINERS_SEQUENCE_HEADER +#define STXXL_CONTAINERS_SEQUENCE_HEADER + +#include + +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +#ifndef STXXL_VERBOSE_SEQUENCE +#define STXXL_VERBOSE_SEQUENCE STXXL_VERBOSE2 +#endif + +//! \addtogroup stlcont +//! \{ + +//! External sequence or deque container without random access. \n +//! Introduction to sequence container: see \ref tutorial_sequence tutorial. \n +//! Design and Internals of sequence container: see \ref design_queue + +/** + * Sequence is a primitive container consisting of only a sequence of blocks in + * external memory. The sequence provides appending methods similar to a deque: + * push_back and push_front; and also the corresponding pop functions. However, + * different from stxxl::deque (which is a vector in disguise), the sequence + * does not allow random access. Instead, the sequence can only be iterated + * using streams: either from front to back or in reverse. + * + * As with queue and stack, sequences of pushes and pops are made efficient + * using overlapping or read-ahead via block pools. The stream access likewise + * uses overlapped I/O, just like stream::vector_iterator2stream. + * + * \tparam ValueType type of the contained objects (POD with no references to internal memory) + * \tparam BlockSize size of the external memory block in bytes, default is \c STXXL_DEFAULT_BLOCK_SIZE(ValTp) + * \tparam AllocStr parallel disk allocation strategy, default is \c STXXL_DEFAULT_ALLOC_STRATEGY + * \tparam SizeType size data type, default is \c stxxl::uint64 + */ +template +class sequence : private noncopyable +{ +public: + typedef ValueType value_type; + typedef AllocStr alloc_strategy_type; + typedef SizeType size_type; + enum { + block_size = BlockSize + }; + + typedef typed_block block_type; + typedef BID bid_type; + + typedef std::deque bid_deque_type; + +private: + typedef read_write_pool pool_type; + + /// current number of items in the sequence + size_type m_size; + + /// whether the m_pool object is own and should be deleted. + bool m_owns_pool; + + /// read_write_pool of blocks + pool_type* m_pool; + + /// current front block of sequence + block_type* m_front_block; + + /// current back block of sequence + block_type* m_back_block; + + /// pointer to current front element in m_front_block + value_type* m_front_element; + + /// pointer to current back element in m_back_block + value_type* m_back_element; + + /// block allocation strategy + alloc_strategy_type m_alloc_strategy; + + /// block allocation counter + unsigned_type m_alloc_count; + + /// allocated block identifiers + bid_deque_type m_bids; + + /// block manager used + block_manager* m_bm; + + /// number of blocks to prefetch + unsigned_type m_blocks2prefetch; + +public: + //! \name Constructors/Destructors + //! \{ + + //! Constructs empty sequence with own write and prefetch block pool + //! + //! \param D number of parallel disks, defaulting to the configured number of scratch disks, + //! memory consumption will be 2 * D + 2 blocks + //! (first and last block, D blocks as write cache, D block for prefetching) + explicit sequence(int_type D = -1) + : m_size(0), + m_owns_pool(true), + m_alloc_count(0), + m_bm(block_manager::get_instance()) + { + if (D < 1) D = config::get_instance()->disks_number(); + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::sequence(D)"); + m_pool = new pool_type(D, D + 2); + init(); + } + + //! Constructs empty sequence with own write and prefetch block pool + //! + //! \param w_pool_size number of blocks in the write pool, must be at least 2, recommended at least 3 + //! \param p_pool_size number of blocks in the prefetch pool, recommended at least 1 + //! \param blocks2prefetch defines the number of blocks to prefetch (\c front side), + //! default is number of block in the prefetch pool + explicit sequence(unsigned_type w_pool_size, unsigned_type p_pool_size, int blocks2prefetch = -1) + : m_size(0), + m_owns_pool(true), + m_alloc_count(0), + m_bm(block_manager::get_instance()) + { + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::sequence(sizes)"); + m_pool = new pool_type(p_pool_size, w_pool_size); + init(blocks2prefetch); + } + + //! Constructs empty sequence + //! + //! \param pool block write/prefetch pool + //! \param blocks2prefetch defines the number of blocks to prefetch (\c front side), default is number of blocks in the prefetch pool + //! \warning Number of blocks in the write pool must be at least 2, recommended at least 3 + //! \warning Number of blocks in the prefetch pool recommended at least 1 + sequence(pool_type& pool, int blocks2prefetch = -1) + : m_size(0), + m_owns_pool(false), + m_pool(&pool), + m_alloc_count(0), + m_bm(block_manager::get_instance()) + { + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::sequence(pool)"); + init(blocks2prefetch); + } + + //! \} + + //! \name Modifiers + //! \{ + + void swap(sequence& obj) + { + std::swap(m_size, obj.m_size); + std::swap(m_owns_pool, obj.m_owns_pool); + std::swap(m_pool, obj.m_pool); + std::swap(m_front_block, obj.m_front_block); + std::swap(m_back_block, obj.m_back_block); + std::swap(m_front_element, obj.m_front_element); + std::swap(m_back_element, obj.m_back_element); + std::swap(m_alloc_strategy, obj.m_alloc_strategy); + std::swap(m_alloc_count, obj.m_alloc_count); + std::swap(m_bids, obj.m_bids); + std::swap(m_bm, obj.m_bm); + std::swap(m_blocks2prefetch, obj.m_blocks2prefetch); + } + + //! \} + +private: + void init(int blocks2prefetch = -1) + { + if (m_pool->size_write() < 2) { + STXXL_ERRMSG("sequence: invalid configuration, not enough blocks (" << m_pool->size_write() << + ") in write pool, at least 2 are needed, resizing to 3"); + m_pool->resize_write(3); + } + + if (m_pool->size_write() < 3) { + STXXL_MSG("sequence: inefficient configuration, no blocks for buffered writing available"); + } + + if (m_pool->size_prefetch() < 1) { + STXXL_MSG("sequence: inefficient configuration, no blocks for prefetching available"); + } + + /// initialize empty sequence + m_front_block = m_back_block = m_pool->steal(); + m_back_element = m_back_block->begin() - 1; + m_front_element = m_back_block->begin(); + set_prefetch_aggr(blocks2prefetch); + } + +public: + //! \name Miscellaneous + //! \{ + + //! Defines the number of blocks to prefetch (\c front side). + //! This method should be called whenever the prefetch pool is resized + //! \param blocks2prefetch defines the number of blocks to prefetch (\c front side), + //! a negative value means to use the number of blocks in the prefetch pool + void set_prefetch_aggr(int_type blocks2prefetch) + { + if (blocks2prefetch < 0) + m_blocks2prefetch = m_pool->size_prefetch(); + else + m_blocks2prefetch = blocks2prefetch; + } + + //! Returns the number of blocks prefetched from the \c front side + unsigned_type get_prefetch_aggr() const + { + return m_blocks2prefetch; + } + + //! \} + + //! \name Modifiers + //! \{ + + //! Adds an element to the front of the sequence + void push_front(const value_type& val) + { + if (UNLIKELY(m_front_element == m_front_block->begin())) + { + if (m_size == 0) + { + STXXL_VERBOSE1("sequence::push_front Case 0"); + assert(m_front_block == m_back_block); + m_front_element = m_back_element = m_front_block->end() - 1; + *m_front_element = val; + ++m_size; + return; + } + // front block is completely filled + else if (m_front_block == m_back_block) + { + // can not write the front block because it + // is the same as the back block, must keep it memory + STXXL_VERBOSE1("sequence::push_front Case 1"); + } + else if (size() < 2 * block_type::size) + { + STXXL_VERBOSE1("sequence::push_front Case 1.5"); + // only two blocks with a gap at the end, move elements within memory + assert(m_bids.empty()); + size_t gap = m_back_block->end() - (m_back_element + 1); + assert(gap > 0); + std::copy_backward(m_back_block->begin(), m_back_element + 1, m_back_block->end()); + std::copy_backward(m_front_block->end() - gap, m_front_block->end(), m_back_block->begin() + gap); + std::copy_backward(m_front_block->begin(), m_front_block->end() - gap, m_front_block->end()); + m_front_element += gap; + m_back_element += gap; + + --m_front_element; + *m_front_element = val; + ++m_size; + return; + } + else + { + STXXL_VERBOSE1("sequence::push_front Case 2"); + // write the front block + // need to allocate new block + bid_type newbid; + + m_bm->new_block(m_alloc_strategy, newbid, m_alloc_count++); + + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: push_front block " << m_front_block << " @ " << FMT_BID(newbid)); + m_bids.push_front(newbid); + m_pool->write(m_front_block, newbid); + if (m_bids.size() <= m_blocks2prefetch) { + STXXL_VERBOSE1("sequence::push Case Hints"); + m_pool->hint(newbid); + } + } + + m_front_block = m_pool->steal(); + m_front_element = m_front_block->end() - 1; + *m_front_element = val; + ++m_size; + } + else // not at beginning of a block + { + --m_front_element; + *m_front_element = val; + ++m_size; + } + } + + //! Adds an element to the end of the sequence + void push_back(const value_type& val) + { + if (UNLIKELY(m_back_element == m_back_block->begin() + (block_type::size - 1))) + { + // back block is completely filled + if (m_front_block == m_back_block) + { + // can not write the back block because it + // is the same as the front block, must keep it memory + STXXL_VERBOSE1("sequence::push_back Case 1"); + } + else if (size() < 2 * block_type::size) + { + STXXL_VERBOSE1("sequence::push_back Case 1.5"); + // only two blocks with a gap in the beginning, move elements within memory + assert(m_bids.empty()); + size_t gap = m_front_element - m_front_block->begin(); + assert(gap > 0); + std::copy(m_front_element, m_front_block->end(), m_front_block->begin()); + std::copy(m_back_block->begin(), m_back_block->begin() + gap, m_front_block->begin() + (block_type::size - gap)); + std::copy(m_back_block->begin() + gap, m_back_block->end(), m_back_block->begin()); + m_front_element -= gap; + m_back_element -= gap; + + ++m_back_element; + *m_back_element = val; + ++m_size; + return; + } + else + { + STXXL_VERBOSE1("sequence::push_back Case 2"); + // write the back block + // need to allocate new block + bid_type newbid; + + m_bm->new_block(m_alloc_strategy, newbid, m_alloc_count++); + + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: push_back block " << m_back_block << " @ " << FMT_BID(newbid)); + m_bids.push_back(newbid); + m_pool->write(m_back_block, newbid); + if (m_bids.size() <= m_blocks2prefetch) { + STXXL_VERBOSE1("sequence::push_back Case Hints"); + m_pool->hint(newbid); + } + } + m_back_block = m_pool->steal(); + + m_back_element = m_back_block->begin(); + *m_back_element = val; + ++m_size; + } + else // not at end of a block + { + ++m_back_element; + *m_back_element = val; + ++m_size; + } + } + + //! Removes element from the front of the sequence + void pop_front() + { + assert(!empty()); + + if (UNLIKELY(m_front_element == m_front_block->begin() + (block_type::size - 1))) + { + // if there is only one block, it implies ... + if (m_back_block == m_front_block) + { + STXXL_VERBOSE1("sequence::pop_front Case 1"); + assert(size() == 1); + assert(m_back_element == m_front_element); + assert(m_bids.empty()); + // reset everything + m_back_element = m_back_block->begin() - 1; + m_front_element = m_back_block->begin(); + m_size = 0; + return; + } + + --m_size; + if (m_size <= block_type::size) + { + STXXL_VERBOSE1("sequence::pop_front Case 2"); + assert(m_bids.empty()); + // the m_back_block is the next block + m_pool->add(m_front_block); + m_front_block = m_back_block; + m_front_element = m_back_block->begin(); + return; + } + STXXL_VERBOSE1("sequence::pop_front Case 3"); + + assert(!m_bids.empty()); + request_ptr req = m_pool->read(m_front_block, m_bids.front()); + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: pop_front block " << m_front_block << " @ " << FMT_BID(m_bids.front())); + + // give prefetching hints + for (unsigned_type i = 0; i < m_blocks2prefetch && i < m_bids.size() - 1; ++i) + { + STXXL_VERBOSE1("sequence::pop_front Case Hints"); + m_pool->hint(m_bids[i + 1]); + } + + m_front_element = m_front_block->begin(); + req->wait(); + + m_bm->delete_block(m_bids.front()); + m_bids.pop_front(); + } + else + { + ++m_front_element; + --m_size; + } + } + + //! Removes element from the back of the sequence + void pop_back() + { + assert(!empty()); + + if (UNLIKELY(m_back_element == m_back_block->begin())) + { + // if there is only one block, it implies ... + if (m_back_block == m_front_block) + { + STXXL_VERBOSE1("sequence::pop_back Case 1"); + assert(size() == 1); + assert(m_back_element == m_front_element); + assert(m_bids.empty()); + // reset everything + m_back_element = m_back_block->begin() - 1; + m_front_element = m_back_block->begin(); + m_size = 0; + return; + } + + --m_size; + if (m_size <= block_type::size) + { + STXXL_VERBOSE1("sequence::pop_back Case 2"); + assert(m_bids.empty()); + // the m_front_block is the next block + m_pool->add(m_back_block); + m_back_block = m_front_block; + m_back_element = m_back_block->end() - 1; + return; + } + + STXXL_VERBOSE1("sequence::pop_back Case 3"); + + assert(!m_bids.empty()); + request_ptr req = m_pool->read(m_back_block, m_bids.back()); + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]: pop_back block " << m_back_block << " @ " << FMT_BID(m_bids.back())); + + // give prefetching hints + for (unsigned_type i = 1; i < m_blocks2prefetch && i < m_bids.size() - 1; ++i) + { + STXXL_VERBOSE1("sequence::pop_front Case Hints"); + m_pool->hint(m_bids[m_bids.size() - 1 - i]); + } + + m_back_element = m_back_block->end() - 1; + req->wait(); + + m_bm->delete_block(m_bids.back()); + m_bids.pop_back(); + } + else + { + --m_back_element; + --m_size; + } + } + + //! \} + + //! \name Capacity + //! \{ + + //! Returns the size of the sequence + size_type size() const + { + return m_size; + } + + //! Returns \c true if sequence is empty + bool empty() const + { + return (m_size == 0); + } + + //! \} + + //! \name Operators + //! \{ + + //! Returns a mutable reference at the back of the sequence + value_type & back() + { + assert(!empty()); + return *m_back_element; + } + + //! Returns a const reference at the back of the sequence + const value_type & back() const + { + assert(!empty()); + return *m_back_element; + } + + //! Returns a mutable reference at the front of the sequence + value_type & front() + { + assert(!empty()); + return *m_front_element; + } + + //! Returns a const reference at the front of the sequence + const value_type & front() const + { + assert(!empty()); + return *m_front_element; + } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + ~sequence() + { + if (m_front_block != m_back_block) + m_pool->add(m_back_block); + + m_pool->add(m_front_block); + + if (m_owns_pool) + delete m_pool; + + if (!m_bids.empty()) + m_bm->delete_blocks(m_bids.begin(), m_bids.end()); + } + + //! \} + + /**************************************************************************/ + + class stream + { + public: + typedef typename sequence::value_type value_type; + + typedef typename bid_deque_type::const_iterator bid_iter_type; + + protected: + const sequence& m_sequence; + + size_type m_size; + + value_type* m_current_element; + + block_type* m_current_block; + + bid_iter_type m_next_bid; + + public: + stream(const sequence& sequence) + : m_sequence(sequence), + m_size(sequence.size()) + { + m_current_block = sequence.m_front_block; + m_current_element = sequence.m_front_element; + m_next_bid = sequence.m_bids.begin(); + } + + ~stream() + { + if (m_current_block != m_sequence.m_front_block && + m_current_block != m_sequence.m_back_block) // give m_current_block back to pool + m_sequence.m_pool->add(m_current_block); + } + + //! return number of element left till end-of-stream. + size_type size() const + { + return m_size; + } + + //! standard stream method + bool empty() const + { + return (m_size == 0); + } + + //! standard stream method + const value_type& operator * () const + { + assert(!empty()); + return *m_current_element; + } + + //! standard stream method + stream& operator ++ () + { + assert(!empty()); + + if (UNLIKELY(m_current_element == m_current_block->begin() + (block_type::size - 1))) + { + // next item position is beyond end of current block, find next block + --m_size; + + if (m_size == 0) + { + STXXL_VERBOSE1("sequence::stream::operator++ last block finished clean at block end"); + assert(m_next_bid == m_sequence.m_bids.end()); + assert(m_current_block == m_sequence.m_back_block); + // nothing to give back to sequence pool + m_current_element = NULL; + return *this; + } + else if (m_size <= block_type::size) // still items left in last partial block + { + STXXL_VERBOSE1("sequence::stream::operator++ reached last block"); + assert(m_next_bid == m_sequence.m_bids.end()); + // the m_back_block is the next block + if (m_current_block != m_sequence.m_front_block) // give current_block back to pool + m_sequence.m_pool->add(m_current_block); + m_current_block = m_sequence.m_back_block; + m_current_element = m_current_block->begin(); + return *this; + } + else if (m_current_block == m_sequence.m_front_block) + { + STXXL_VERBOSE1("sequence::stream::operator++ first own-block case: steal block from sequence's pool"); + m_current_block = m_sequence.m_pool->steal(); + } + + STXXL_VERBOSE1("sequence::stream::operator++ default case: fetch next block"); + + assert(m_next_bid != m_sequence.m_bids.end()); + request_ptr req = m_sequence.m_pool->read(m_current_block, *m_next_bid); + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::stream::operator++ read block " << m_current_block << " @ " << FMT_BID(*m_next_bid)); + + // give prefetching hints + bid_iter_type bid = m_next_bid + 1; + for (unsigned_type i = 0; i < m_sequence.m_blocks2prefetch && bid != m_sequence.m_bids.end(); ++i, ++bid) + { + STXXL_VERBOSE1("sequence::stream::operator++ giving prefetch hints"); + m_sequence.m_pool->hint(*bid); + } + + m_current_element = m_current_block->begin(); + req->wait(); + + ++m_next_bid; + } + else + { + --m_size; + ++m_current_element; + } + return *this; + } + }; + + //! \name Miscellaneous + //! \{ + + //! construct a forward stream from this sequence + stream get_stream() + { + return stream(*this); + } + + //! \} + + /**************************************************************************/ + + class reverse_stream + { + public: + typedef typename sequence::value_type value_type; + + typedef typename bid_deque_type::const_reverse_iterator bid_iter_type; + + protected: + const sequence& m_sequence; + + size_type m_size; + + value_type* m_current_element; + + block_type* m_current_block; + + bid_iter_type m_next_bid; + + public: + reverse_stream(const sequence& sequence) + : m_sequence(sequence), + m_size(sequence.size()) + { + m_current_block = sequence.m_back_block; + m_current_element = sequence.m_back_element; + m_next_bid = sequence.m_bids.rbegin(); + } + + ~reverse_stream() + { + if (m_current_block != m_sequence.m_front_block && + m_current_block != m_sequence.m_back_block) // give m_current_block back to pool + m_sequence.m_pool->add(m_current_block); + } + + //! return number of element left till end-of-stream. + size_type size() const + { + return m_size; + } + + //! standard stream method + bool empty() const + { + return (m_size == 0); + } + + //! standard stream method + const value_type& operator * () const + { + assert(!empty()); + return *m_current_element; + } + + //! standard stream method + reverse_stream& operator ++ () + { + assert(!empty()); + + if (UNLIKELY(m_current_element == m_current_block->begin())) + { + // next item position is beyond begin of current block, find next block + --m_size; + + if (m_size == 0) + { + STXXL_VERBOSE1("sequence::reverse_stream::operator++ last block finished clean at block begin"); + assert(m_next_bid == m_sequence.m_bids.rend()); + assert(m_current_block == m_sequence.m_front_block); + // nothing to give back to sequence pool + m_current_element = NULL; + return *this; + } + else if (m_size <= block_type::size) + { + STXXL_VERBOSE1("sequence::reverse_stream::operator++ reached first block"); + assert(m_next_bid == m_sequence.m_bids.rend()); + // the m_back_block is the next block + if (m_current_block != m_sequence.m_back_block) // give current_block back to pool + m_sequence.m_pool->add(m_current_block); + m_current_block = m_sequence.m_front_block; + m_current_element = m_current_block->begin() + (block_type::size - 1); + return *this; + } + else if (m_current_block == m_sequence.m_back_block) + { + STXXL_VERBOSE1("sequence::reverse_stream::operator++ first own-block case: steal block from sequence's pool"); + m_current_block = m_sequence.m_pool->steal(); + } + + STXXL_VERBOSE1("sequence::reverse_stream::operator++ default case: fetch previous block"); + + assert(m_next_bid != m_sequence.m_bids.rend()); + request_ptr req = m_sequence.m_pool->read(m_current_block, *m_next_bid); + STXXL_VERBOSE_SEQUENCE("sequence[" << this << "]::reverse_stream::operator++ read block " << m_current_block << " @ " << FMT_BID(*m_next_bid)); + + // give prefetching hints + bid_iter_type bid = m_next_bid + 1; + for (unsigned_type i = 0; i < m_sequence.m_blocks2prefetch && bid != m_sequence.m_bids.rend(); ++i, ++bid) + { + STXXL_VERBOSE1("sequence::reverse_stream::operator++ giving prefetch hints"); + m_sequence.m_pool->hint(*bid); + } + + m_current_element = m_current_block->begin() + (block_type::size - 1); + req->wait(); + + ++m_next_bid; + } + else + { + --m_size; + --m_current_element; + } + return *this; + } + }; + + //! \name Miscellaneous + //! \{ + + //! construct a reverse stream from this sequence + reverse_stream get_reverse_stream() + { + return reverse_stream(*this); + } + + //! \} +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_SEQUENCE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sorter.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sorter.h new file mode 100644 index 0000000000..013ac5fafa --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/sorter.h @@ -0,0 +1,282 @@ +/*************************************************************************** + * include/stxxl/bits/containers/sorter.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2012 Timo Bingmann + * + * 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 STXXL_CONTAINERS_SORTER_HEADER +#define STXXL_CONTAINERS_SORTER_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +#ifndef STXXL_VERBOSE_SORTER +#define STXXL_VERBOSE_SORTER STXXL_VERBOSE2 +#endif + +//! \addtogroup stlcont +//! \{ + +//! External sorter container. \n +//! Introduction to sorter container: see \ref tutorial_sorter tutorial. \n +//! Design and Internals of sorter container: see \ref design_sorter + +/** + * External Sorter: use stream package objects to keep a sorted container. + * + * This sorter container combines the two functions of runs_creator and + * runs_merger from the stream packages into a two-phase container. + * + * In the first phase the container is filled with unordered items via push(), + * which are presorted internally into runs of size M. When the internal memory + * overflows a runs is written to external memory in blocks of block_size. + * + * When sort() is called the container enters the output phase and push() is + * disallowed. After calling sort() the items can be read in sorted order using + * operator*() to get the top item, operator++() to advance to the next one and + * empty() to check for end of stream. This is exactly the stream interface. + * + * In the output phase the sorter can be returned to the beginning of the + * stream using rewind() and everything is read again in sorted order. + * + * Using clear() the object can be reset into input state and all items are + * destroyed. + * + * Added in STXXL 1.4 + * + * \tparam ValueType type of the contained objects (POD with no references to internal memory) + * \tparam CompareType type of comparison object used for sorting the runs + * \tparam BlockSize size of the external memory block in bytes, default is \c STXXL_DEFAULT_BLOCK_SIZE(ValTp) + * \tparam AllocStr parallel disk allocation strategy, default is \c STXXL_DEFAULT_ALLOC_STRATEGY + */ +template +class sorter : private noncopyable +{ +public: + // *** Template Parameters + + typedef ValueType value_type; + typedef CompareType cmp_type; + enum { + block_size = BlockSize + }; + typedef AllocStrategy alloc_strategy_type; + + // *** Constructed Types + + //! runs creator type with push() method + typedef stream::runs_creator, cmp_type, + block_size, alloc_strategy_type> runs_creator_type; + + //! corresponding runs merger type + typedef stream::runs_merger runs_merger_type; + + //! size type + typedef typename runs_merger_type::size_type size_type; + +protected: + // *** Object Attributes + + //! current state of sorter + enum { STATE_INPUT, STATE_OUTPUT } m_state; + + //! runs creator object holding all items + runs_creator_type m_runs_creator; + + //! runs merger reading items when in STATE_OUTPUT + runs_merger_type m_runs_merger; + +public: + //! \name Constructors + //! \{ + + //! Constructor allocation memory_to_use bytes in ram for sorted runs. + sorter(const cmp_type& cmp, unsigned_type memory_to_use) + : m_state(STATE_INPUT), + m_runs_creator(cmp, memory_to_use), + m_runs_merger(cmp, memory_to_use) + { } + + //! Constructor variant with differently sizes runs_creator and runs_merger + sorter(const cmp_type& cmp, unsigned_type creator_memory_to_use, unsigned_type merger_memory_to_use) + : m_state(STATE_INPUT), + m_runs_creator(cmp, creator_memory_to_use), + m_runs_merger(cmp, merger_memory_to_use) + + { } + + //! \} + + //! \name Modifiers + //! \{ + + //! Remove all items and return to input state. + void clear() + { + if (m_state == STATE_OUTPUT) + m_runs_merger.deallocate(); + + m_runs_creator.allocate(); + m_state = STATE_INPUT; + } + + //! Push another item (only callable during input state). + void push(const value_type& val) + { + assert(m_state == STATE_INPUT); + m_runs_creator.push(val); + } + + //! \} + + //! \name Modus + //! \{ + + //! Finish push input state and deallocate input buffer. + void finish() + { + if (m_state == STATE_OUTPUT) + { + m_runs_merger.deallocate(); + } + + m_runs_creator.deallocate(); + } + + //! Deallocate buffers and clear result. + void finish_clear() + { + if (m_state == STATE_OUTPUT) + { + m_runs_merger.deallocate(); + m_runs_creator.result()->clear(); + } + + m_runs_creator.deallocate(); + } + + //! \} + + //! \name Modifiers + //! \{ + + //! Switch to output state, rewind() in case the output was already sorted. + void sort() + { + if (m_state == STATE_OUTPUT) + { + m_runs_merger.deallocate(); + } + + m_runs_creator.deallocate(); + m_runs_merger.initialize(m_runs_creator.result()); + m_state = STATE_OUTPUT; + } + + //! Switch to output state, rewind() in case the output was already sorted. + void sort(unsigned_type merger_memory_to_use) + { + m_runs_merger.set_memory_to_use(merger_memory_to_use); + sort(); + } + + //! \} + + //! \name Modus + //! \{ + + //! Switch to output state, rewind() in case the output was already sorted. + void sort_reuse() + { + assert(m_state == STATE_INPUT); + + m_runs_merger.initialize(m_runs_creator.result()); + m_state = STATE_OUTPUT; + } + + //! Rewind output stream to beginning. + void rewind() + { + assert(m_state == STATE_OUTPUT); + + m_runs_merger.deallocate(); + + m_state = STATE_INPUT; + return sort(); + } + + //! \} + + //! Change runs_merger memory usage + void set_merger_memory_to_use(unsigned_type merger_memory_to_use) + { + m_runs_merger.set_memory_to_use(merger_memory_to_use); + } + + //! \} + + //! \name Capacity + //! \{ + + //! Number of items pushed or items remaining to be read. + size_type size() const + { + if (m_state == STATE_INPUT) + return m_runs_creator.size(); + else + return m_runs_merger.size(); + } + //! Standard stream method + bool empty() const + { + assert(m_state == STATE_OUTPUT); + return m_runs_merger.empty(); + } + + //! \} + + //! \name Operators + //! \{ + + //! Standard stream method + const value_type& operator * () const + { + assert(m_state == STATE_OUTPUT); + return *m_runs_merger; + } + + //! Standard stream method + const value_type* operator -> () const + { + return &(operator * ()); + } + + //! Standard stream method (preincrement operator) + sorter& operator ++ () + { + assert(m_state == STATE_OUTPUT); + ++m_runs_merger; + return *this; + } + + //! \} +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_CONTAINERS_SORTER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/stack.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/stack.h new file mode 100644 index 0000000000..76091fa596 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/stack.h @@ -0,0 +1,1063 @@ +/*************************************************************************** + * include/stxxl/bits/containers/stack.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003-2004 Roman Dementiev + * Copyright (C) 2009, 2010 Andreas Beckmann + * + * 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 STXXL_CONTAINERS_STACK_HEADER +#define STXXL_CONTAINERS_STACK_HEADER + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \defgroup stlcont_stack stack +//! \ingroup stlcont +//! External stack implementations +//! \{ + +template +struct stack_config_generator +{ + typedef ValueType value_type; + enum { blocks_per_page = BlocksPerPage }; + typedef AllocStr alloc_strategy; + enum { block_size = BlockSize }; + typedef SizeType size_type; +}; + +//! External stack container. +//! Introduction to stack container: see \ref tutorial_stack tutorial. \n +//! Design and Internals of stack container: see \ref design_stack +//! Conservative implementation. Fits best if your access pattern consists of irregularly mixed +//! push'es and pop's. +//! For semantics of the methods see documentation of the STL \c std::stack.
+//! To gain full bandwidth of disks \c StackConfig::BlocksPerPage must >= number of disks
+//! \internal +template +class normal_stack : private noncopyable +{ +public: + typedef StackConfig cfg; + //! type of the elements stored in the stack + typedef typename cfg::value_type value_type; + typedef typename cfg::alloc_strategy alloc_strategy_type; + //! type for sizes (64-bit) + typedef typename cfg::size_type size_type; + enum { + blocks_per_page = cfg::blocks_per_page, + block_size = cfg::block_size + }; + + //! type of block used in disk-memory transfers + typedef typed_block block_type; + typedef BID bid_type; + +private: + size_type m_size; + unsigned_type cache_offset; + value_type* current_element; + simple_vector cache; + typename simple_vector::iterator front_page; + typename simple_vector::iterator back_page; + std::vector bids; + alloc_strategy_type alloc_strategy; + +public: + //! \name Constructors/Destructors + //! \{ + + //! Default constructor: creates empty stack. + normal_stack() + : m_size(0), + cache_offset(0), + current_element(NULL), + cache(blocks_per_page * 2), + front_page(cache.begin() + blocks_per_page), + back_page(cache.begin()), + bids(0) + { + bids.reserve(blocks_per_page); + } + + //! \name Accessor Functions + //! \{ + + void swap(normal_stack& obj) + { + std::swap(m_size, obj.m_size); + std::swap(cache_offset, obj.cache_offset); + std::swap(current_element, obj.current_element); + std::swap(cache, obj.cache); + std::swap(front_page, obj.front_page); + std::swap(back_page, obj.back_page); + std::swap(bids, obj.bids); + std::swap(alloc_strategy, obj.alloc_strategy); + } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + //! Copy-construction from a another stack of any type. + //! \param stack_ stack object (could be external or internal, important is that it must + //! have a copy constructor, \c top() and \c pop() methods ) + template + normal_stack(const StackType& stack_) + : m_size(0), + cache_offset(0), + current_element(NULL), + cache(blocks_per_page * 2), + front_page(cache.begin() + blocks_per_page), + back_page(cache.begin()), + bids(0) + { + bids.reserve(blocks_per_page); + + StackType stack_copy = stack_; + size_t sz = stack_copy.size(); + + std::vector tmp(sz); + + for (size_t i = 0; i < sz; ++i) { + tmp[sz - i - 1] = stack_copy.top(); + stack_copy.pop(); + } + + for (size_t i = 0; i < sz; ++i) + push(tmp[i]); + } + + virtual ~normal_stack() + { + STXXL_VERBOSE(STXXL_PRETTY_FUNCTION_NAME); + block_manager::get_instance()->delete_blocks(bids.begin(), bids.end()); + } + + //! \} + + //! \name Capacity + //! \{ + + //! Returns the number of elements contained in the stack + size_type size() const + { + return m_size; + } + + //! Returns true if the stack is empty. + bool empty() const + { + return (!m_size); + } + + //! \} + + //! \name Accessor Functions + //! \{ + + //! Return mutable reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + value_type & top() + { + assert(m_size > 0); + return (*current_element); + } + + //! Return constant reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + const value_type & top() const + { + assert(m_size > 0); + return (*current_element); + } + + //! Inserts an element at the top of the stack. Postconditions: size() is + //! incremented by 1, and top() is the inserted element. + void push(const value_type& val) + { + assert(cache_offset <= 2 * blocks_per_page * block_type::size); + //assert(cache_offset >= 0); + + if (UNLIKELY(cache_offset == 2 * blocks_per_page * block_type::size)) // cache overflow + { + STXXL_VERBOSE2("growing, size: " << m_size); + + bids.resize(bids.size() + blocks_per_page); + typename std::vector::iterator cur_bid = bids.end() - blocks_per_page; + block_manager::get_instance()->new_blocks(alloc_strategy, cur_bid, bids.end(), cur_bid - bids.begin()); + + simple_vector requests(blocks_per_page); + + for (int i = 0; i < blocks_per_page; ++i, ++cur_bid) + { + requests[i] = (back_page + i)->write(*cur_bid); + } + + std::swap(back_page, front_page); + + bids.reserve(bids.size() + blocks_per_page); + + cache_offset = blocks_per_page * block_type::size + 1; + current_element = &((*front_page)[0]); + ++m_size; + + wait_all(requests.begin(), blocks_per_page); + + *current_element = val; + + return; + } + + current_element = element(cache_offset); + *current_element = val; + ++m_size; + ++cache_offset; + } + + //! Removes the element at the top of the stack. Precondition: stack is not + //! empty(). Postcondition: size() is decremented. + void pop() + { + assert(cache_offset <= 2 * blocks_per_page * block_type::size); + assert(cache_offset > 0); + assert(m_size > 0); + + if (UNLIKELY(cache_offset == 1 && bids.size() >= blocks_per_page)) + { + STXXL_VERBOSE2("shrinking, size: " << m_size); + + simple_vector requests(blocks_per_page); + + { + typename std::vector::const_iterator cur_bid = bids.end(); + for (int i = blocks_per_page - 1; i >= 0; --i) + { + requests[i] = (front_page + i)->read(*(--cur_bid)); + } + } + + std::swap(front_page, back_page); + + cache_offset = blocks_per_page * block_type::size; + --m_size; + current_element = &((*(back_page + (blocks_per_page - 1)))[block_type::size - 1]); + + wait_all(requests.begin(), blocks_per_page); + + block_manager::get_instance()->delete_blocks(bids.end() - blocks_per_page, bids.end()); + bids.resize(bids.size() - blocks_per_page); + + return; + } + + --m_size; + + current_element = element((--cache_offset) - 1); + } + + //! \} + +private: + value_type * element(unsigned_type offset) + { + if (offset < blocks_per_page * block_type::size) + return &((*(back_page + offset / block_type::size))[offset % block_type::size]); + + unsigned_type unbiased_offset = offset - blocks_per_page * block_type::size; + return &((*(front_page + unbiased_offset / block_type::size))[unbiased_offset % block_type::size]); + } +}; + +//! Efficient implementation that uses prefetching and overlapping using internal buffers. +//! +//! Use it if your access pattern consists of many repeated push'es and pop's +//! For semantics of the methods see documentation of the STL \c std::stack. +//! \warning The amortized complexity of operation is not O(1/DB), rather O(DB) +template +class grow_shrink_stack : private noncopyable +{ +public: + typedef StackConfig cfg; + //! type of the elements stored in the stack + typedef typename cfg::value_type value_type; + typedef typename cfg::alloc_strategy alloc_strategy_type; + //! type for sizes (64-bit) + typedef typename cfg::size_type size_type; + enum { + blocks_per_page = cfg::blocks_per_page, + block_size = cfg::block_size + }; + + //! type of block used in disk-memory transfers + typedef typed_block block_type; + typedef BID bid_type; + +private: + size_type m_size; + unsigned_type cache_offset; + value_type* current_element; + simple_vector cache; + typename simple_vector::iterator cache_buffers; + typename simple_vector::iterator overlap_buffers; + simple_vector requests; + std::vector bids; + alloc_strategy_type alloc_strategy; + +public: + //! \name Constructors/Destructor + //! \{ + + //! Default constructor: creates empty stack. + grow_shrink_stack() + : m_size(0), + cache_offset(0), + current_element(NULL), + cache(blocks_per_page * 2), + cache_buffers(cache.begin()), + overlap_buffers(cache.begin() + blocks_per_page), + requests(blocks_per_page), + bids(0) + { + bids.reserve(blocks_per_page); + } + + //! \} + + //! \name Accessor Functions + //! \{ + + void swap(grow_shrink_stack& obj) + { + std::swap(m_size, obj.m_size); + std::swap(cache_offset, obj.cache_offset); + std::swap(current_element, obj.current_element); + std::swap(cache, obj.cache); + std::swap(cache_buffers, obj.cache_buffers); + std::swap(overlap_buffers, obj.overlap_buffers); + std::swap(requests, obj.requests); + std::swap(bids, obj.bids); + std::swap(alloc_strategy, obj.alloc_strategy); + } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + //! Copy-construction from a another stack of any type. + //! \param stack_ stack object (could be external or internal, important is that it must + //! have a copy constructor, \c top() and \c pop() methods ) + template + grow_shrink_stack(const StackType& stack_) + : m_size(0), + cache_offset(0), + current_element(NULL), + cache(blocks_per_page * 2), + cache_buffers(cache.begin()), + overlap_buffers(cache.begin() + blocks_per_page), + requests(blocks_per_page), + bids(0) + { + bids.reserve(blocks_per_page); + + StackType stack_copy = stack_; + size_t sz = stack_copy.size(); + + std::vector tmp(sz); + + for (size_t i = 0; i < sz; ++i) + { + tmp[sz - i - 1] = stack_copy.top(); + stack_copy.pop(); + } + + for (size_t i = 0; i < sz; ++i) + push(tmp[i]); + } + virtual ~grow_shrink_stack() + { + STXXL_VERBOSE(STXXL_PRETTY_FUNCTION_NAME); + try + { + if (requests[0].get()) + wait_all(requests.begin(), blocks_per_page); + } + catch (const io_error&) + { } + block_manager::get_instance()->delete_blocks(bids.begin(), bids.end()); + } + + //! \} + + //! \name Capacity + //! \{ + + //! Returns the number of elements contained in the stack + size_type size() const + { + return m_size; + } + //! Returns true if the stack is empty. + bool empty() const + { + return (!m_size); + } + + //! \} + + //! \name Accessor Functions + //! \{ + + //! Return mutable reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + value_type & top() + { + assert(m_size > 0); + return (*current_element); + } + + //! Return constant reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + const value_type & top() const + { + assert(m_size > 0); + return (*current_element); + } + + //! Inserts an element at the top of the stack. Postconditions: size() is + //! incremented by 1, and top() is the inserted element. + void push(const value_type& val) + { + assert(cache_offset <= blocks_per_page * block_type::size); + //assert(cache_offset >= 0); + + if (UNLIKELY(cache_offset == blocks_per_page * block_type::size)) // cache overflow + { + STXXL_VERBOSE2("growing, size: " << m_size); + + bids.resize(bids.size() + blocks_per_page); + typename std::vector::iterator cur_bid = bids.end() - blocks_per_page; + block_manager::get_instance()->new_blocks(alloc_strategy, cur_bid, bids.end(), cur_bid - bids.begin()); + + for (int i = 0; i < blocks_per_page; ++i, ++cur_bid) + { + if (requests[i].get()) + requests[i]->wait(); + + requests[i] = (cache_buffers + i)->write(*cur_bid); + } + + std::swap(cache_buffers, overlap_buffers); + + bids.reserve(bids.size() + blocks_per_page); + + cache_offset = 1; + current_element = &((*cache_buffers)[0]); + ++m_size; + + *current_element = val; + + return; + } + + current_element = &((*(cache_buffers + cache_offset / block_type::size))[cache_offset % block_type::size]); + *current_element = val; + ++m_size; + ++cache_offset; + } + + //! Removes the element at the top of the stack. Precondition: stack is not + //! empty(). Postcondition: size() is decremented. + void pop() + { + assert(cache_offset <= blocks_per_page * block_type::size); + assert(cache_offset > 0); + assert(m_size > 0); + + if (UNLIKELY(cache_offset == 1 && bids.size() >= blocks_per_page)) + { + STXXL_VERBOSE2("shrinking, size: " << m_size); + + if (requests[0].get()) + wait_all(requests.begin(), blocks_per_page); + + std::swap(cache_buffers, overlap_buffers); + + if (bids.size() > blocks_per_page) + { + STXXL_VERBOSE2("prefetching, size: " << m_size); + typename std::vector::const_iterator cur_bid = bids.end() - blocks_per_page; + for (int i = blocks_per_page - 1; i >= 0; --i) + requests[i] = (overlap_buffers + i)->read(*(--cur_bid)); + } + + block_manager::get_instance()->delete_blocks(bids.end() - blocks_per_page, bids.end()); + bids.resize(bids.size() - blocks_per_page); + + cache_offset = blocks_per_page * block_type::size; + --m_size; + current_element = &((*(cache_buffers + (blocks_per_page - 1)))[block_type::size - 1]); + + return; + } + + --m_size; + unsigned_type cur_offset = (--cache_offset) - 1; + current_element = &((*(cache_buffers + cur_offset / block_type::size))[cur_offset % block_type::size]); + } + + //! \} +}; + +//! Efficient implementation that uses prefetching and overlapping using (shared) buffers pools. +//! \warning This is a single buffer stack! Each direction change (push() followed by pop() or vice versa) may cause one I/O. +template +class grow_shrink_stack2 : private noncopyable +{ +public: + typedef StackConfig cfg; + //! type of the elements stored in the stack + typedef typename cfg::value_type value_type; + typedef typename cfg::alloc_strategy alloc_strategy_type; + //! type for sizes (64-bit) + typedef typename cfg::size_type size_type; + enum { + blocks_per_page = cfg::blocks_per_page, // stack of this type has only one page + block_size = cfg::block_size + }; + + //! type of block used in disk-memory transfers + typedef typed_block block_type; + typedef BID bid_type; + +private: + typedef read_write_pool pool_type; + + size_type m_size; + unsigned_type cache_offset; + block_type* cache; + std::vector bids; + alloc_strategy_type alloc_strategy; + unsigned_type pref_aggr; + pool_type* owned_pool; + pool_type* pool; + +public: + //! \name Constructors/Destructors + //! \{ + + //! Default constructor: creates empty stack. The stack will use the + //! read_write_pool for prefetching and buffered writing. + //! \param pool_ block write/prefetch pool + //! \param prefetch_aggressiveness number of blocks that will be used from prefetch pool + grow_shrink_stack2(pool_type& pool_, + unsigned_type prefetch_aggressiveness = 0) + : m_size(0), + cache_offset(0), + cache(new block_type), + pref_aggr(prefetch_aggressiveness), + owned_pool(NULL), + pool(&pool_) + { + STXXL_VERBOSE2("grow_shrink_stack2::grow_shrink_stack2(...)"); + } + + //! Default constructor: creates empty stack. The stack will use the pair + //! of prefetch_pool and write_pool for prefetching and buffered writing. + //! This constructor is deprecated in favor of the read_write_pool + //! constructor. + //! + //! \param p_pool_ prefetch pool, that will be used for block prefetching + //! \param w_pool_ write pool, that will be used for block writing + //! \param prefetch_aggressiveness number of blocks that will be used from prefetch pool + STXXL_DEPRECATED( + grow_shrink_stack2(prefetch_pool& p_pool_, + write_pool& w_pool_, + unsigned_type prefetch_aggressiveness = 0) + ) + : m_size(0), + cache_offset(0), + cache(new block_type), + pref_aggr(prefetch_aggressiveness), + owned_pool(new pool_type(p_pool_, w_pool_)), + pool(owned_pool) + { + STXXL_VERBOSE2("grow_shrink_stack2::grow_shrink_stack2(...)"); + } + + //! \} + + //! \name Accessor Functions + //! \{ + + void swap(grow_shrink_stack2& obj) + { + std::swap(m_size, obj.m_size); + std::swap(cache_offset, obj.cache_offset); + std::swap(cache, obj.cache); + std::swap(bids, obj.bids); + std::swap(alloc_strategy, obj.alloc_strategy); + std::swap(pref_aggr, obj.pref_aggr); + std::swap(owned_pool, obj.owned_pool); + std::swap(pool, obj.pool); + } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + virtual ~grow_shrink_stack2() + { + try + { + STXXL_VERBOSE2("grow_shrink_stack2::~grow_shrink_stack2()"); + const int_type bids_size = bids.size(); + const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr), (int_type)0); + int_type i; + for (i = bids_size - 1; i >= last_pref; --i) + { + // clean the prefetch buffer + pool->invalidate(bids[i]); + } + typename std::vector::iterator cur = bids.begin(); + typename std::vector::const_iterator end = bids.end(); + for ( ; cur != end; ++cur) + { + // FIXME: read_write_pool needs something like cancel_write(bid) + block_type* b = NULL; // w_pool.steal(*cur); + if (b) + { + pool->add(cache); // return buffer + cache = b; + } + } + delete cache; + } + catch (const io_error&) + { } + block_manager::get_instance()->delete_blocks(bids.begin(), bids.end()); + delete owned_pool; + } + + //! \} + + //! \name Capacity + //! \{ + + //! Returns the number of elements contained in the stack + size_type size() const + { + return m_size; + } + //! Returns true if the stack is empty. + bool empty() const + { + return (!m_size); + } + + //! \} + + //! \name Accessor Functions + //! \{ + + //! Inserts an element at the top of the stack. Postconditions: size() is + //! incremented by 1, and top() is the inserted element. + void push(const value_type& val) + { + STXXL_VERBOSE3("grow_shrink_stack2::push(" << val << ")"); + assert(cache_offset <= block_type::size); + + if (UNLIKELY(cache_offset == block_type::size)) + { + STXXL_VERBOSE2("grow_shrink_stack2::push(" << val << ") growing, size: " << m_size); + + bids.resize(bids.size() + 1); + typename std::vector::iterator cur_bid = bids.end() - 1; + block_manager::get_instance()->new_blocks(alloc_strategy, cur_bid, bids.end(), cur_bid - bids.begin()); + pool->write(cache, bids.back()); + cache = pool->steal(); + const int_type bids_size = bids.size(); + const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr) - 1, (int_type)0); + for (int_type i = bids_size - 2; i >= last_pref; --i) + { + // clean prefetch buffers + pool->invalidate(bids[i]); + } + cache_offset = 0; + } + (*cache)[cache_offset] = val; + ++m_size; + ++cache_offset; + + assert(cache_offset > 0); + assert(cache_offset <= block_type::size); + } + + //! Return mutable reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + value_type & top() + { + assert(m_size > 0); + assert(cache_offset > 0); + assert(cache_offset <= block_type::size); + return (*cache)[cache_offset - 1]; + } + + //! Return constant reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + const value_type & top() const + { + assert(m_size > 0); + assert(cache_offset > 0); + assert(cache_offset <= block_type::size); + return (*cache)[cache_offset - 1]; + } + + //! Removes the element at the top of the stack. Precondition: stack is not + //! empty(). Postcondition: size() is decremented. + void pop() + { + STXXL_VERBOSE3("grow_shrink_stack2::pop()"); + assert(m_size > 0); + assert(cache_offset > 0); + assert(cache_offset <= block_type::size); + if (UNLIKELY(cache_offset == 1 && (!bids.empty()))) + { + STXXL_VERBOSE2("grow_shrink_stack2::pop() shrinking, size = " << m_size); + + bid_type last_block = bids.back(); + bids.pop_back(); + pool->read(cache, last_block)->wait(); + block_manager::get_instance()->delete_block(last_block); + rehint(); + cache_offset = block_type::size + 1; + } + + --cache_offset; + --m_size; + } + + //! \} + + //! \name Miscellaneous + //! \{ + + //! Sets level of prefetch aggressiveness (number of blocks from the + //! prefetch pool used for prefetching). + //! \param new_p new value for the prefetch aggressiveness + void set_prefetch_aggr(unsigned_type new_p) + { + if (pref_aggr > new_p) + { + const int_type bids_size = bids.size(); + const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr), (int_type)0); + for (int_type i = bids_size - new_p - 1; i >= last_pref; --i) + { + // clean prefetch buffers + pool->invalidate(bids[i]); + } + } + pref_aggr = new_p; + rehint(); + } + + //! Returns number of blocks used for prefetching. + unsigned_type get_prefetch_aggr() const + { + return pref_aggr; + } + + //! \} + +private: + //! hint the last pref_aggr external blocks. + void rehint() + { + const int_type bids_size = bids.size(); + const int_type last_pref = STXXL_MAX(int_type(bids_size) - int_type(pref_aggr), (int_type)0); + for (int_type i = bids_size - 1; i >= last_pref; --i) + { + pool->hint(bids[i]); // prefetch + } + } +}; + +//! A stack that migrates from internal memory to external when its size exceeds a certain threshold. +//! +//! For semantics of the methods see documentation of the STL \c std::stack. +template +class migrating_stack : private noncopyable +{ +public: + typedef typename ExternalStack::cfg cfg; + //! type of the elements stored in the stack + typedef typename cfg::value_type value_type; + //! type for sizes (64-bit) + typedef typename cfg::size_type size_type; + enum { + blocks_per_page = cfg::blocks_per_page, + block_size = cfg::block_size + }; + + typedef InternalStack int_stack_type; + typedef ExternalStack ext_stack_type; + +private: + enum { critical_size = CritSize }; + + int_stack_type* int_impl; + ext_stack_type* ext_impl; + + //! Copy-construction from a another stack of any type. + //! \warning not implemented yet! + template + migrating_stack(const StackType& stack_); + +public: + //! \name Constructors/Destructors + //! \{ + + //! Default constructor: creates empty stack. + migrating_stack() + : int_impl(new int_stack_type()), ext_impl(NULL) + { } + + virtual ~migrating_stack() + { + delete int_impl; + delete ext_impl; + } + + //! \} + + //! \name Accessor Functions + //! \{ + + void swap(migrating_stack& obj) + { + std::swap(int_impl, obj.int_impl); + std::swap(ext_impl, obj.ext_impl); + } + + //! \} + + //! \name Miscellaneous + //! \{ + + //! Returns true if current implementation is internal, otherwise false. + bool internal() const + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + return (int_impl != NULL); + } + //! Returns true if current implementation is external, otherwise false. + bool external() const + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + return (ext_impl != NULL); + } + + //! \} + + //! \name Capacity + //! \{ + + //! Returns true if the stack is empty. + bool empty() const + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + return (int_impl) ? int_impl->empty() : ext_impl->empty(); + } + //! Returns the number of elements contained in the stack + size_type size() const + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + return (int_impl) ? size_type(int_impl->size()) : ext_impl->size(); + } + + //! \} + + //! \name Accessor Functions + //! \{ + + //! Return mutable reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + value_type & top() + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + return (int_impl) ? int_impl->top() : ext_impl->top(); + } + + //! Return constant reference to the element at the top of the + //! stack. Precondition: stack is not empty(). + const value_type & top() const + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + return (int_impl) ? int_impl->top() : ext_impl->top(); + } + + //! Inserts an element at the top of the stack. Postconditions: size() is + //! incremented by 1, and top() is the inserted element. + void push(const value_type& val) + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + + if (int_impl) + { + int_impl->push(val); + if (UNLIKELY(int_impl->size() == critical_size)) + { + // migrate to external stack + ext_impl = new ext_stack_type(*int_impl); + delete int_impl; + int_impl = NULL; + } + } + else + ext_impl->push(val); + } + + //! Removes the element at the top of the stack. Precondition: stack is not + //! empty(). Postcondition: size() is decremented. + void pop() + { + assert((int_impl && !ext_impl) || (!int_impl && ext_impl)); + + if (int_impl) + int_impl->pop(); + else + ext_impl->pop(); + } + + //! \} +}; + +enum stack_externality { external, migrating, internal }; +enum stack_behaviour { normal, grow_shrink, grow_shrink2 }; + +//! Stack type generator \n +//! Introduction to stack container: see \ref tutorial_stack tutorial. \n +//! Design and Internals of stack container: see \ref design_stack. +//! +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! +//! \tparam Externality selects stack implementation, default: \b external. One of +//! - \c external, external container, implementation is chosen according to \c Behaviour parameter. +//! - \c migrating, migrates from internal implementation given by \c IntStackType parameter +//! to external implementation given by \c Behaviour parameter when size exceeds \c MigrCritSize +//! - \c internal, choses \c IntStackType implementation +//! +//! \tparam Behaviour chooses \b external implementation, default: \b stxxl::normal_stack. One of: +//! - \c normal, conservative version, implemented in \c stxxl::normal_stack +//! - \c grow_shrink, efficient version, implemented in \c stxxl::grow_shrink_stack +//! - \c grow_shrink2, efficient version, implemented in \c stxxl::grow_shrink_stack2 +//! +//! \tparam BlocksPerPage defines how many blocks has one page of internal cache of an +//! \b external implementation, default is \b 4. All \b external implementations have +//! \b two pages. +//! +//! \tparam BlockSize external block size in bytes, default is 2 MiB. +//! +//! \tparam IntStackType type of internal stack used for some implementations, default: \b std::stack. +//! +//! \tparam MigrCritSize threshold value for number of elements when +//! stxxl::migrating_stack migrates to the external memory, default: 2 x BlocksPerPage x BlockSize. +//! +//! \tparam AllocStr one of allocation strategies: striping, RC, SR, or FR. Default is \b RC. +//! +//! \tparam SizeType size type, default is \b stxxl::uint64. +//! +//! The configured stack type is available as STACK_GENERATOR<>::result. +//! +template < + class ValueType, + stack_externality Externality = external, + stack_behaviour Behaviour = normal, + unsigned BlocksPerPage = 4, + unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), + + class IntStackType = std::stack, + unsigned_type MigrCritSize = (2* BlocksPerPage* BlockSize), + + class AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY, + class SizeType = stxxl::uint64 + > +class STACK_GENERATOR +{ + typedef stack_config_generator cfg; + + typedef typename IF, + grow_shrink_stack2 >::result GrShrTp; + typedef typename IF, GrShrTp>::result ExtStackType; + typedef typename IF, ExtStackType>::result MigrOrNotStackType; + +public: + typedef typename IF::result result; +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::normal_stack& a, + stxxl::normal_stack& b) +{ + a.swap(b); +} + +template +void swap(stxxl::grow_shrink_stack& a, + stxxl::grow_shrink_stack& b) +{ + a.swap(b); +} + +template +void swap(stxxl::grow_shrink_stack2& a, + stxxl::grow_shrink_stack2& b) +{ + a.swap(b); +} + +template +void swap(stxxl::migrating_stack& a, + stxxl::migrating_stack& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_STACK_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/unordered_map.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/unordered_map.h new file mode 100644 index 0000000000..41d5e25242 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/unordered_map.h @@ -0,0 +1,498 @@ +/*************************************************************************** + * include/stxxl/bits/containers/unordered_map.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008 Markus Westphal + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_CONTAINERS_UNORDERED_MAP_HEADER +#define STXXL_CONTAINERS_UNORDERED_MAP_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace hash_map { + +template < + class KeyType, + class DataType, + class HashType, + class CompareType, + unsigned SubBlockSize, + unsigned SubBlocksPerBlock, + class Alloc + > +class hash_map; + +} // namespace hash_map + +//! \addtogroup stlcont +//! \{ + +/*! + * An external memory implementation of the STL unordered_map container, which + * is based on an external memory hash map. For more information see \ref + * tutorial_unordered_map. + * + * \tparam KeyType the key type + * \tparam MappedType the mapped type associated with a key + * \tparam HashType a hash functional + * \tparam CompareType a less comparison relation for KeyType + * \tparam SubBlockSize the raw size of a subblock (caching granularity) + * (default: 8192) + * \tparam SubBlocksPerBlock the number of subblocks per external block + * (default: 256 -> 2MB blocks) + * \tparam AllocType allocator for internal-memory buffer + */ +template < + class KeyType, + class MappedType, + class HashType, + class CompareType, + unsigned SubBlockSize = 8* 1024, + unsigned SubBlocksPerBlock = 256, + class AllocType = std::allocator > + > +class unordered_map : private noncopyable +{ + typedef hash_map::hash_map impl_type; + + impl_type impl; + +public: + //! \name Types + //! \{ + + //! the first template parameter (Key) + typedef typename impl_type::key_type key_type; + //! the second template parameter (T) + typedef typename impl_type::mapped_type mapped_type; + //! pair + typedef typename impl_type::value_type value_type; + //! the third template parameter (HashType) + typedef typename impl_type::hasher hasher; + //! the fourth template parameter (CompareType) (!!! not: equality compare) + typedef typename impl_type::key_compare key_compare; + //! the fifth template parameter (AllocType) + typedef AllocType allocator_type; + + typedef typename impl_type::reference reference; + typedef typename impl_type::const_reference const_reference; + typedef typename impl_type::pointer pointer; + typedef typename impl_type::const_pointer const_pointer; + + typedef typename impl_type::external_size_type size_type; + typedef typename impl_type::difference_type difference_type; + + typedef typename impl_type::external_size_type external_size_type; + typedef typename impl_type::internal_size_type internal_size_type; + + typedef typename impl_type::iterator iterator; + typedef typename impl_type::const_iterator const_iterator; + + //! constructed equality predicate for key + typedef typename impl_type::key_equal key_equal; + + //! \} + + //! \name Constructors + //! \{ + + /*! + * Construct a new hash-map + * + * \param n initial number of buckets + * \param hf hash-function + * \param cmp comparator-object + * \param buffer_size size of internal-memory buffer in bytes + * \param a allocation-strategory for internal-memory buffer + */ + unordered_map(internal_size_type n = 0, + const hasher& hf = hasher(), + const key_compare& cmp = key_compare(), + internal_size_type buffer_size = 100*1024*1024, + const allocator_type& a = allocator_type()) + : impl(n, hf, cmp, buffer_size, a) + { } + + /*! + * Construct a new hash-map and insert all values in the range [begin,end) + * + * \param begin beginning of the range + * \param end end of the range + * \param mem_to_sort internal memory that may be used for + * bulk-construction (not to be confused with the buffer-memory) + * \param n initial number of buckets + * \param hf hash-function + * \param cmp comparator-object + * \param buffer_size size of internal-memory buffer in bytes + * \param a allocation-strategory for internal-memory buffer + */ + template + unordered_map(InputIterator begin, InputIterator end, + internal_size_type mem_to_sort = 256*1024*1024, + internal_size_type n = 0, + const hasher& hf = hasher(), + const key_compare& cmp = key_compare(), + internal_size_type buffer_size = 100*1024*1024, + const allocator_type& a = allocator_type()) + : impl(begin, end, mem_to_sort, n, hf, cmp, buffer_size, a) + { } + + //! \} + + //! \name Size and Capacity + //! \{ + + //! Number of values currently stored. Note: If the correct number is + //! currently unknown (because **oblivous-methods** were used), external + //! memory will be scanned. + external_size_type size() const + { + return impl.size(); + } + + //! The hash-map may store up to this number of values + external_size_type max_size() const + { + return impl.max_size(); + } + + //! Check if container is empty, see size() about oblivious-methods. + bool empty() const + { + return impl.empty(); + } + + //! \} + + //! \name Iterators + //! \{ + + //! iterator pointing to the beginnning of the hash-map + iterator begin() + { + return impl.begin(); + } + + //! iterator pointing to the end of the hash-map (iterator-type as + //! template-parameter) + iterator end() + { + return impl.end(); + } + + //! iterator pointing to the beginnning of the hash-map + const_iterator begin() const + { + return impl.begin(); + } + + //! iterator pointing to the end of the hash-map (iterator-type as + //! template-parameter) + const_iterator end() const + { + return impl.end(); + } + + //! \} + + //! \name Lookup and Element Access + //! \{ + + //! Convenience operator to quickly insert or find values. Use with caution + //! since using this operator will check external-memory. + mapped_type& operator [] (const key_type& key) + { + return impl[key]; + } + + //! Look up value by key. Non-const access. + //! \param key key for value to look up + iterator find(const key_type& key) + { + return impl.find(key); + } + + //! Look up value by key. Const access. + //! \param key key for value to look up + const_iterator find(const key_type& key) const + { + return impl.find(key); + } + + //! Number of values with given key + //! \param key key for value to look up + //! \return 0 or 1 depending on the presence of a value with the given key + external_size_type count(const key_type& key) const + { + return impl.count(key); + } + + //! Finds a range containing all values with given key. Non-const access + //! \param key key to look for# + //! \return range may be empty or contains exactly one value + std::pair + equal_range(const key_type& key) + { + return impl.equal_range(key); + } + + //! Finds a range containing all values with given key. Const access + //! \param key key to look for# + //! \return range may be empty or contains exactly one value + std::pair + equal_range(const key_type& key) const + { + return impl.equal_range(key); + } + + //! \} + + //! \name Modifiers: Insert + //! \{ + + /*! + * Insert a new value if no value with the same key is already present; + * external memory must therefore be accessed + * + * \param value what to insert + * \return a tuple whose second part is true iff the value was actually + * added (no value with the same key present); the first part is an + * iterator pointing to the newly inserted or already stored value + */ + std::pair insert(const value_type& value) + { + return impl.insert(value); + } + + //! Insert a value; external memory is not accessed so that another value + //! with the same key may be overwritten + //! \param value what to insert + //! \return iterator pointing to the inserted value + iterator insert_oblivious(const value_type& value) + { + return impl.insert_oblivious(value); + } + + //! Bulk-insert of values in the range [f, l) + //! \param first beginning of the range + //! \param last end of the range + //! \param mem internal memory that may be used (note: this memory will be + //! used additionally to the buffer). The more the better + template + void insert(InputIterator first, InputIterator last, internal_size_type mem) + { + impl.insert(first, last, mem); + } + + //! \} + + //! \name Modifiers: Erase + //! \{ + + //! Erase value by iterator + //! \param it iterator pointing to the value to erase + void erase(const_iterator it) + { + impl.erase(it); + } + + //! Erase value by key; check external memory + //! \param key key of value to erase + //! \return number of values actually erased (0 or 1) + external_size_type erase(const key_type& key) + { + return impl.erase(key); + } + + //! Erase value by key but without looking at external memory + //! \param key key for value to release + void erase_oblivious(const key_type& key) + { + impl.erase_oblivious(key); + } + + //! Reset hash-map: erase all values, invalidate all iterators + void clear() + { + impl.clear(); + } + + //! Exchange stored values with another hash-map + //! \param obj hash-map to swap values with + void swap(unordered_map& obj) + { + std::swap(impl, obj.impl); + } + + //! \} + + //! \name Bucket Interface + //! \{ + + //! Number of buckets + internal_size_type bucket_count() const + { + return impl.bucket_count(); + } + + //! Maximum number of buckets + internal_size_type max_bucket_count() const + { + return impl.max_bucket_count(); + } + + // bucket_size()? + + //! Bucket-index for values with given key. + internal_size_type bucket(const key_type& k) const + { + return impl.bucket_index(k); + } + + //! \} + + //! \name Hash Policy + //! \{ + + //! Average number of (sub)blocks occupied by a bucket. + float load_factor() const + { + return impl.load_factor(); + } + + //! Set desired load-factor + float opt_load_factor() const + { + return impl.opt_load_factor(); + } + + //! Set desired load-factor + void opt_load_factor(float z) + { + impl.opt_load_factor(z); + } + + //! Rehash with (at least) n buckets + void rehash(internal_size_type n) + { + impl.rehash(n); + } + + //! \} + + //! \name Observers + //! \{ + + //! Hash-function used by this hash-map + hasher hash_function() const + { + return impl.hash_function(); + } + + //! Strict-weak-ordering used by this hash-map + key_compare key_comp() const + { + return impl.key_cmp(); + } + + //! Constructed equality predicate used by this hash-map + key_equal key_eq() const + { + return impl.key_eq(); + } + + //! Get node memory allocator + allocator_type get_allocator() const + { + return impl.get_allocator(); + } + + //! \} + + //! \name Internal Memory Buffer Policy + //! \{ + + //! Number of bytes occupied by buffer + internal_size_type buffer_size() const + { + return impl.buffer_size(); + } + + //! Maximum buffer size in byte + internal_size_type max_buffer_size() const + { + return impl.max_buffer_size(); + } + + //! Set maximum buffer size + //! \param buffer_size new size in byte + void max_buffer_size(internal_size_type buffer_size) + { + impl.max_buffer_size(buffer_size); + } + + //! \} + + //! \name Statistics + //! \{ + + //! Reset hash-map statistics + void reset_statistics() + { + impl.reset_statistics(); + } + + //! Print short general statistics to output stream + void print_statistics(std::ostream& o = std::cout) const + { + impl.print_statistics(o); + } + + //! Even more statistics: Number of buckets, number of values, buffer-size, + //! values per bucket + void print_load_statistics(std::ostream& o = std::cout) const + { + impl.print_load_statistics(o); + } + + // \} +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template < + class KeyType, + class MappedType, + class HashType, + class CompareType, + unsigned SubBlockSize, + unsigned SubBlocksPerBlock, + class AllocType + > +void swap(stxxl::unordered_map& a, + stxxl::unordered_map& b + ) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_UNORDERED_MAP_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/vector.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/vector.h new file mode 100644 index 0000000000..475dd644df --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/containers/vector.h @@ -0,0 +1,2632 @@ +/*************************************************************************** + * include/stxxl/bits/containers/vector.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2008 Roman Dementiev + * Copyright (C) 2007-2009 Johannes Singler + * Copyright (C) 2008-2010 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_CONTAINERS_VECTOR_HEADER +#define STXXL_CONTAINERS_VECTOR_HEADER + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +#define STXXL_VERBOSE_VECTOR(msg) STXXL_VERBOSE1("vector[" << static_cast(this) << "]::" << msg) + +//! \defgroup stlcont Containers +//! \ingroup stllayer +//! Containers with STL-compatible interface + +//! \defgroup stlcont_vector vector +//! \ingroup stlcont +//! Vector and support classes +//! \{ + +template +class double_blocked_index +{ + typedef SizeType size_type; + + static const size_type modulo12 = modulo1 * modulo2; + + size_type pos; + unsigned_type block1, block2, offset; + + //! \invariant block2 * modulo12 + block1 * modulo1 + offset == pos && 0 <= block1 < modulo2 && 0 <= offset < modulo1 + + void set(size_type pos) + { + this->pos = pos; + block2 = (int_type)(pos / modulo12); + pos -= block2 * modulo12; + block1 = (int_type)(pos / modulo1); + offset = (int_type)(pos - block1 * modulo1); + + assert(block2 * modulo12 + block1 * modulo1 + offset == this->pos); + assert(/* 0 <= block1 && */ block1 < modulo2); + assert(/* 0 <= offset && */ offset < modulo1); + } + +public: + double_blocked_index() + { + set(0); + } + + double_blocked_index(size_type pos) + { + set(pos); + } + + double_blocked_index(unsigned_type block2, unsigned_type block1, unsigned_type offset) + { + assert(/* 0 <= block1 && */ block1 < modulo2); + assert(/* 0 <= offset && */ offset < modulo1); + + this->block2 = block2; + this->block1 = block1; + this->offset = offset; + pos = block2 * modulo12 + block1 * modulo1 + offset; + } + + double_blocked_index& operator = (size_type pos) + { + set(pos); + return *this; + } + + //pre-increment operator + double_blocked_index& operator ++ () + { + ++pos; + ++offset; + if (offset == modulo1) + { + offset = 0; + ++block1; + if (block1 == modulo2) + { + block1 = 0; + ++block2; + } + } + + assert(block2 * modulo12 + block1 * modulo1 + offset == this->pos); + assert(/* 0 <= block1 && */ block1 < modulo2); + assert(/* 0 <= offset && */ offset < modulo1); + + return *this; + } + + //post-increment operator + double_blocked_index operator ++ (int) + { + double_blocked_index former(*this); + operator ++ (); + return former; + } + + //pre-increment operator + double_blocked_index& operator -- () + { + --pos; + if (offset == 0) + { + offset = modulo1; + if (block1 == 0) + { + block1 = modulo2; + --block2; + } + --block1; + } + --offset; + + assert(block2 * modulo12 + block1 * modulo1 + offset == this->pos); + assert(/*0 <= block1 &&*/ block1 < modulo2); + assert(/*0 <= offset &&*/ offset < modulo1); + + return *this; + } + + //post-increment operator + double_blocked_index operator -- (int) + { + double_blocked_index former(*this); + operator -- (); + return former; + } + + double_blocked_index operator + (size_type addend) const + { + return double_blocked_index(pos + addend); + } + + double_blocked_index& operator += (size_type addend) + { + set(pos + addend); + return *this; + } + + double_blocked_index operator - (size_type addend) const + { + return double_blocked_index(pos - addend); + } + + size_type operator - (const double_blocked_index& dbi2) const + { + return pos - dbi2.pos; + } + + double_blocked_index& operator -= (size_type subtrahend) + { + set(pos - subtrahend); + return *this; + } + + bool operator == (const double_blocked_index& dbi2) const + { + return pos == dbi2.pos; + } + + bool operator != (const double_blocked_index& dbi2) const + { + return pos != dbi2.pos; + } + + bool operator < (const double_blocked_index& dbi2) const + { + return pos < dbi2.pos; + } + + bool operator <= (const double_blocked_index& dbi2) const + { + return pos <= dbi2.pos; + } + + bool operator > (const double_blocked_index& dbi2) const + { + return pos > dbi2.pos; + } + + bool operator >= (const double_blocked_index& dbi2) const + { + return pos >= dbi2.pos; + } + + double_blocked_index& operator >>= (size_type shift) + { + set(pos >> shift); + return *this; + } + + size_type get_pos() const + { + return pos; + } + + const unsigned_type & get_block2() const + { + return block2; + } + + const unsigned_type & get_block1() const + { + return block1; + } + + const unsigned_type & get_offset() const + { + return offset; + } +}; + +//////////////////////////////////////////////////////////////////////////// + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +class vector; + +template +class const_vector_iterator; + +template +class vector_bufreader; + +template +class vector_bufreader_reverse; + +template +class vector_bufwriter; + +//////////////////////////////////////////////////////////////////////////// + +//! External vector iterator, model of \c ext_random_access_iterator concept. +template +class vector_iterator +{ + typedef vector_iterator self_type; + + typedef const_vector_iterator const_self_type; + + friend class const_vector_iterator; + +public: + //! \name Types + //! \{ + + typedef self_type iterator; + typedef const_self_type const_iterator; + + typedef unsigned block_offset_type; + typedef vector vector_type; + friend class vector; + typedef typename vector_type::bids_container_type bids_container_type; + typedef typename bids_container_type::iterator bids_container_iterator; + typedef typename bids_container_type::bid_type bid_type; + typedef typename vector_type::block_type block_type; + typedef typename vector_type::blocked_index_type blocked_index_type; + + typedef std::random_access_iterator_tag iterator_category; + typedef typename vector_type::size_type size_type; + typedef typename vector_type::difference_type difference_type; + typedef typename vector_type::value_type value_type; + typedef typename vector_type::reference reference; + typedef typename vector_type::const_reference const_reference; + typedef typename vector_type::pointer pointer; + typedef typename vector_type::const_pointer const_pointer; + + //! \} + +protected: + blocked_index_type offset; + vector_type* p_vector; + +private: + //! private constructor for initializing other iterators + vector_iterator(vector_type* v, size_type o) + : offset(o), p_vector(v) + { } + +public: + //! constructs invalid iterator + vector_iterator() + : offset(0), p_vector(NULL) + { } + //! copy-constructor + vector_iterator(const self_type& a) + : offset(a.offset), + p_vector(a.p_vector) + { } + + //! \name Iterator Properties + //! \{ + + //! return pointer to vector containing iterator + vector_type * parent_vector() const + { + return p_vector; + } + //! return block offset of current element + block_offset_type block_offset() const + { + return static_cast(offset.get_offset()); + } + //! return iterator to BID containg current element + bids_container_iterator bid() const + { + return p_vector->bid(offset); + } + + //! \} + + //! \name Access Operators + //! \{ + + //! return current element + reference operator * () + { + return p_vector->element(offset); + } + //! return pointer to current element + pointer operator -> () + { + return &(p_vector->element(offset)); + } + //! return const reference to current element + const_reference operator * () const + { + return p_vector->const_element(offset); + } + //! return const pointer to current element + const_pointer operator -> () const + { + return &(p_vector->const_element(offset)); + } + //! return mutable reference to element +i after the current element + reference operator [] (size_type i) + { + return p_vector->element(offset.get_pos() + i); + } + +#ifdef _LIBCPP_VERSION + //-tb 2013-11: libc++ defines std::reverse_iterator::operator[] in such a + // way that it expects vector_iterator::operator[] to return a (mutable) + // reference. Thus to remove confusion about the compiler error, we remove + // the operator[] const for libc++. The const_reference actually violates + // some version of the STL standard, but works well in gcc's libstdc++. +#else + //! return const reference to element +i after the current element + const_reference operator [] (size_type i) const + { + return p_vector->const_element(offset.get_pos() + i); + } +#endif + //! \} + + //! \name Relative Calculation of Iterators + //! \{ + + //! calculate different between two iterator + difference_type operator - (const self_type& a) const + { + return offset - a.offset; + } + //! calculate different between two iterator + difference_type operator - (const const_self_type& a) const + { + return offset - a.offset; + } + //! return iterator advanced -i positions in the vector + self_type operator - (size_type i) const + { + return self_type(p_vector, offset.get_pos() - i); + } + //! return iterator advanced +i positions in the vector + self_type operator + (size_type i) const + { + return self_type(p_vector, offset.get_pos() + i); + } + //! advance this iterator -i positions in the vector + self_type& operator -= (size_type i) + { + offset -= i; + return *this; + } + //! advance this iterator +i positions in the vector + self_type& operator += (size_type i) + { + offset += i; + return *this; + } + //! advance this iterator to next position in the vector + self_type& operator ++ () + { + offset++; + return *this; + } + //! advance this iterator to next position in the vector + self_type operator ++ (int) + { + self_type tmp = *this; + offset++; + return tmp; + } + //! advance this iterator to preceding position in the vector + self_type& operator -- () + { + offset--; + return *this; + } + //! advance this iterator to preceding position in the vector + self_type operator -- (int) + { + self_type tmp = *this; + offset--; + return tmp; + } + + //! \} + + //! \name Comparison Operators + //! \{ + + bool operator == (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset == a.offset; + } + bool operator != (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset != a.offset; + } + bool operator < (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset < a.offset; + } + bool operator <= (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset <= a.offset; + } + bool operator > (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset > a.offset; + } + bool operator >= (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset >= a.offset; + } + + bool operator == (const const_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset == a.offset; + } + bool operator != (const const_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset != a.offset; + } + bool operator < (const const_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset < a.offset; + } + bool operator <= (const const_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset <= a.offset; + } + bool operator > (const const_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset > a.offset; + } + bool operator >= (const const_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset >= a.offset; + } + + //! \} + + //! \name Flushing Operation + //! \{ + + void block_externally_updated() + { + p_vector->block_externally_updated(offset); + } + + void flush() + { + p_vector->flush(); + } + + //! \} +}; + +//////////////////////////////////////////////////////////////////////////// + +//! Const external vector iterator, model of \c ext_random_access_iterator concept. +template +class const_vector_iterator +{ + typedef const_vector_iterator self_type; + + typedef vector_iterator mutable_self_type; + + friend class vector_iterator; + +public: + //! \name Types + //! \{ + + typedef self_type const_iterator; + typedef mutable_self_type iterator; + + typedef unsigned block_offset_type; + typedef vector vector_type; + friend class vector; + typedef typename vector_type::bids_container_type bids_container_type; + typedef typename bids_container_type::iterator bids_container_iterator; + typedef typename bids_container_type::bid_type bid_type; + typedef typename vector_type::block_type block_type; + typedef typename vector_type::blocked_index_type blocked_index_type; + + typedef std::random_access_iterator_tag iterator_category; + typedef typename vector_type::size_type size_type; + typedef typename vector_type::difference_type difference_type; + typedef typename vector_type::value_type value_type; + typedef typename vector_type::const_reference reference; + typedef typename vector_type::const_reference const_reference; + typedef typename vector_type::const_pointer pointer; + typedef typename vector_type::const_pointer const_pointer; + + //! \} + +protected: + blocked_index_type offset; + const vector_type* p_vector; + +private: + //! private constructor for initializing other iterators + const_vector_iterator(const vector_type* v, size_type o) + : offset(o), p_vector(v) + { } + +public: + //! constructs invalid iterator + const_vector_iterator() + : offset(0), p_vector(NULL) + { } + //! copy-constructor + const_vector_iterator(const self_type& a) + : offset(a.offset), p_vector(a.p_vector) + { } + //! copy-constructor from mutable iterator + const_vector_iterator(const mutable_self_type& a) + : offset(a.offset), p_vector(a.p_vector) + { } + + //! \name Iterator Properties + //! \{ + + //! return pointer to vector containing iterator + const vector_type * parent_vector() const + { + return p_vector; + } + //! return block offset of current element + block_offset_type block_offset() const + { + return static_cast(offset.get_offset()); + } + //! return iterator to BID containg current element + bids_container_iterator bid() const + { + return ((vector_type*)p_vector)->bid(offset); + } + + //! \} + + //! \name Access Operators + //! \{ + + //! return current element + const_reference operator * () const + { + return p_vector->const_element(offset); + } + //! return pointer to current element + const_pointer operator -> () const + { + return &(p_vector->const_element(offset)); + } + //! return const reference to element +i after the current element + const_reference operator [] (size_type i) const + { + return p_vector->const_element(offset.get_pos() + i); + } + + //! \} + + //! \name Relative Calculation of Iterators + //! \{ + + //! calculate different between two iterator + difference_type operator - (const self_type& a) const + { + return offset - a.offset; + } + //! calculate different between two iterator + difference_type operator - (const mutable_self_type& a) const + { + return offset - a.offset; + } + //! return iterator advanced -i positions in the vector + self_type operator - (size_type i) const + { + return self_type(p_vector, offset.get_pos() - i); + } + //! return iterator advanced +i positions in the vector + self_type operator + (size_type i) const + { + return self_type(p_vector, offset.get_pos() + i); + } + //! advance this iterator -i positions in the vector + self_type& operator -= (size_type i) + { + offset -= i; + return *this; + } + //! advance this iterator +i positions in the vector + self_type& operator += (size_type i) + { + offset += i; + return *this; + } + //! advance this iterator to next position in the vector + self_type& operator ++ () + { + offset++; + return *this; + } + //! advance this iterator to next position in the vector + self_type operator ++ (int) + { + self_type tmp_ = *this; + offset++; + return tmp_; + } + //! advance this iterator to preceding position in the vector + self_type& operator -- () + { + offset--; + return *this; + } + //! advance this iterator to preceding position in the vector + self_type operator -- (int) + { + self_type tmp = *this; + offset--; + return tmp; + } + + //! \} + + //! \name Comparison Operators + //! \{ + + bool operator == (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset == a.offset; + } + bool operator != (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset != a.offset; + } + bool operator < (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset < a.offset; + } + bool operator <= (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset <= a.offset; + } + bool operator > (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset > a.offset; + } + bool operator >= (const self_type& a) const + { + assert(p_vector == a.p_vector); + return offset >= a.offset; + } + + bool operator == (const mutable_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset == a.offset; + } + bool operator != (const mutable_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset != a.offset; + } + bool operator < (const mutable_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset < a.offset; + } + bool operator <= (const mutable_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset <= a.offset; + } + bool operator > (const mutable_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset > a.offset; + } + bool operator >= (const mutable_self_type& a) const + { + assert(p_vector == a.p_vector); + return offset >= a.offset; + } + + //! \} + + //! \name Flushing Operation + //! \{ + + void block_externally_updated() + { + p_vector->block_externally_updated(offset); + } + + void flush() + { + p_vector->flush(); + } + + //! \} +}; + +//////////////////////////////////////////////////////////////////////////// + +//! External vector container. \n +//! Introduction to vector container: see \ref tutorial_vector tutorial. \n +//! Design and Internals of vector container: see \ref design_vector +//! +//! For semantics of the methods see documentation of the STL std::vector +//! \tparam ValueType type of contained objects (POD with no references to internal memory) +//! \tparam PageSize number of blocks in a page +//! \tparam PagerType pager type, \c random_pager or \c lru_pager, where x is the default number of pages, +//! default is \c lru_pager<8> +//! \tparam BlockSize external block size in bytes, default is 2 MiB +//! \tparam AllocStr one of allocation strategies: \c striping , \c RC , \c SR , or \c FR +//! default is RC +//! +//! Memory consumption: BlockSize*x*PageSize bytes +//! \warning Do not store references to the elements of an external vector. Such references +//! might be invalidated during any following access to elements of the vector +template < + typename ValueType, + unsigned PageSize = 4, + typename PagerType = lru_pager<8>, + unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), + typename AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY, + typename SizeType = stxxl::uint64 // will be deprecated soon + > +class vector +{ +public: + //! \name Standard Types + //! \{ + + //! The type of elements stored in the vector. + typedef ValueType value_type; + //! reference to value_type + typedef value_type& reference; + //! constant reference to value_type + typedef const value_type& const_reference; + //! pointer to value_type + typedef value_type* pointer; + //! constant pointer to value_type + typedef const value_type* const_pointer; + //! an unsigned 64-bit integral type + typedef SizeType size_type; + typedef stxxl::int64 difference_type; + + typedef PagerType pager_type; + typedef AllocStr alloc_strategy_type; + + enum constants { + block_size = BlockSize, + page_size = PageSize, + on_disk = -1 + }; + + //! iterator used to iterate through a vector, see \ref design_vector_notes. + typedef vector_iterator iterator; + friend class vector_iterator; + + //! constant iterator used to iterate through a vector, see \ref design_vector_notes. + typedef const_vector_iterator const_iterator; + friend class const_vector_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + //! \} + + //! \name Extra Types + //! \{ + + //! vector_bufwriter compatible with this vector + typedef vector_bufwriter bufwriter_type; + + //! vector_bufreader compatible with this vector + typedef vector_bufreader bufreader_type; + + //! vector_bufreader compatible with this vector + typedef vector_bufreader_reverse bufreader_reverse_type; + + //! \internal + class bid_vector : public std::vector > + { + public: + typedef std::vector > super_type; + typedef typename super_type::size_type size_type; + typedef typename super_type::value_type bid_type; + + bid_vector(size_type sz) : super_type(sz) + { } + }; + + typedef bid_vector bids_container_type; + typedef typename bids_container_type::iterator bids_container_iterator; + typedef typename bids_container_type::const_iterator const_bids_container_iterator; + + //! type of the block used in disk-memory transfers + typedef typed_block block_type; + //! double-index type to reference individual elements in a block + typedef double_blocked_index blocked_index_type; + + //! \} + +private: + alloc_strategy_type m_alloc_strategy; + size_type m_size; + bids_container_type m_bids; + mutable pager_type m_pager; + + // enum specifying status of a page of the vector + enum { valid_on_disk = 0, uninitialized = 1, dirty = 2 }; + //! status of each page (valid_on_disk, uninitialized or dirty) + mutable std::vector m_page_status; + mutable std::vector m_page_to_slot; + mutable simple_vector m_slot_to_page; + mutable std::queue m_free_slots; + mutable simple_vector* m_cache; + file* m_from; + block_manager* m_bm; + bool m_exported; + + size_type size_from_file_length(stxxl::uint64 file_length) const + { + stxxl::uint64 blocks_fit = file_length / stxxl::uint64(block_type::raw_size); + size_type cur_size = blocks_fit * stxxl::uint64(block_type::size); + stxxl::uint64 rest = file_length - blocks_fit * stxxl::uint64(block_type::raw_size); + return (cur_size + rest / stxxl::uint64(sizeof(value_type))); + } + + stxxl::uint64 file_length() const + { + typedef stxxl::uint64 file_size_type; + size_type cur_size = size(); + size_type num_full_blocks = cur_size / block_type::size; + if (cur_size % block_type::size != 0) + { + size_type rest = cur_size - num_full_blocks * block_type::size; + return file_size_type(num_full_blocks) * block_type::raw_size + rest * sizeof(value_type); + } + return file_size_type(num_full_blocks) * block_type::raw_size; + } + +public: + //! \name Constructors/Destructors + //! \{ + + //! Constructs external vector with n elements. + //! + //! \param n Number of elements. + //! \param npages Number of cached pages. + vector(size_type n = 0, unsigned_type npages = pager_type().size()) + : m_size(n), + m_bids((size_t)div_ceil(n, block_type::size)), + m_pager(npages), + m_page_status(div_ceil(m_bids.size(), page_size)), + m_page_to_slot(div_ceil(m_bids.size(), page_size)), + m_slot_to_page(npages), + m_cache(NULL), + m_from(NULL), + m_exported(false) + { + m_bm = block_manager::get_instance(); + + allocate_page_cache(); + + for (size_t i = 0; i < m_page_status.size(); ++i) + { + m_page_status[i] = uninitialized; + m_page_to_slot[i] = on_disk; + } + + for (unsigned_type i = 0; i < numpages(); ++i) + m_free_slots.push(i); + + m_bm->new_blocks(m_alloc_strategy, m_bids.begin(), m_bids.end(), 0); + } + + //! \} + + //! \name Modifier + //! \{ + + //! swap content + void swap(vector& obj) + { + std::swap(m_alloc_strategy, obj.m_alloc_strategy); + std::swap(m_size, obj.m_size); + std::swap(m_bids, obj.m_bids); + std::swap(m_pager, obj.m_pager); + std::swap(m_page_status, obj.m_page_status); + std::swap(m_page_to_slot, obj.m_page_to_slot); + std::swap(m_slot_to_page, obj.m_slot_to_page); + std::swap(m_free_slots, obj.m_free_slots); + std::swap(m_cache, obj.m_cache); + std::swap(m_from, obj.m_from); + std::swap(m_exported, obj.m_exported); + } + + //! \} + + //! \name Miscellaneous + //! \{ + + //! Allocate page cache, must be called to allow access to elements. + void allocate_page_cache() const + { + // numpages() might be zero + if (!m_cache && numpages() > 0) + m_cache = new simple_vector(numpages() * page_size); + } + + //! allows to free the cache, but you may not access any element until call + //! allocate_page_cache() again + void deallocate_page_cache() const + { + flush(); + delete m_cache; + m_cache = NULL; + } + + //! \name Size and Capacity + //! \{ + + //! return the size of the vector. + size_type size() const + { + return m_size; + } + //! true if the vector's size is zero. + bool empty() const + { + return (!m_size); + } + + //! Return the number of elelemtsn for which \a external memory has been + //! allocated. capacity() is always greator than or equal to size(). + size_type capacity() const + { + return size_type(m_bids.size()) * block_type::size; + } + //! Returns the number of bytes that the vector has allocated on disks. + size_type raw_capacity() const + { + return size_type(m_bids.size()) * block_type::raw_size; + } + + /*! Reserves at least n elements in external memory. + * + * If n is less than or equal to capacity(), this call has no + * effect. Otherwise, it is a request for allocation of additional \b + * external memory. If the request is successful, then capacity() is + * greater than or equal to n; otherwise capacity() is unchanged. In either + * case, size() is unchanged. + */ + void reserve(size_type n) + { + if (n <= capacity()) + return; + + unsigned_type old_bids_size = m_bids.size(); + unsigned_type new_bids_size = (unsigned_type)div_ceil(n, block_type::size); + unsigned_type new_pages = div_ceil(new_bids_size, page_size); + m_page_status.resize(new_pages, uninitialized); + m_page_to_slot.resize(new_pages, on_disk); + + m_bids.resize(new_bids_size); + if (m_from == NULL) + { + m_bm->new_blocks(m_alloc_strategy, + m_bids.begin() + old_bids_size, m_bids.end(), + old_bids_size); + } + else + { + size_type offset = size_type(old_bids_size) * size_type(block_type::raw_size); + for (bids_container_iterator it = m_bids.begin() + old_bids_size; + it != m_bids.end(); ++it, offset += size_type(block_type::raw_size)) + { + (*it).storage = m_from; + (*it).offset = offset; + } + STXXL_VERBOSE_VECTOR("reserve(): Changing size of file " << + ((void*)m_from) << " to " << offset); + m_from->set_size(offset); + } + } + + //! Resize vector contents to n items. + //! \warning this will not call the constructor of objects in external memory! + void resize(size_type n) + { + _resize(n); + } + + //! Resize vector contents to n items, and allow the allocated external + //! memory to shrink. Internal memory allocation remains unchanged. + //! \warning this will not call the constructor of objects in external memory! + void resize(size_type n, bool shrink_capacity) + { + if (shrink_capacity) + _resize_shrink_capacity(n); + else + _resize(n); + } + + //! \} + +private: + //! Resize vector, only allow capacity growth. + void _resize(size_type n) + { + reserve(n); + if (n < m_size) { + // mark excess pages as uninitialized and evict them from cache + unsigned_type first_page_to_evict = (unsigned_type)div_ceil(n, block_type::size * page_size); + for (size_t i = first_page_to_evict; i < m_page_status.size(); ++i) { + if (m_page_to_slot[i] != on_disk) { + m_free_slots.push(m_page_to_slot[i]); + m_page_to_slot[i] = on_disk; + } + m_page_status[i] = uninitialized; + } + } + m_size = n; + } + + //! Resize vector, also allow reduction of external memory capacity. + void _resize_shrink_capacity(size_type n) + { + unsigned_type old_bids_size = m_bids.size(); + unsigned_type new_bids_size = (unsigned_type)div_ceil(n, block_type::size); + + if (new_bids_size > old_bids_size) + { + reserve(n); + } + else if (new_bids_size < old_bids_size) + { + unsigned_type new_pages_size = div_ceil(new_bids_size, page_size); + + STXXL_VERBOSE_VECTOR("shrinking from " << old_bids_size << " to " << + new_bids_size << " blocks = from " << + m_page_status.size() << " to " << + new_pages_size << " pages"); + + // release blocks + if (m_from != NULL) + m_from->set_size(new_bids_size * block_type::raw_size); + else + m_bm->delete_blocks(m_bids.begin() + old_bids_size, m_bids.end()); + + m_bids.resize(new_bids_size); + + // don't resize m_page_to_slot or m_page_status, because it is + // still needed to check page status and match the mapping + // m_slot_to_page + + // clear dirty flag, so these pages will be never written + std::fill(m_page_status.begin() + new_pages_size, + m_page_status.end(), (unsigned char)valid_on_disk); + } + + m_size = n; + } + +public: + //! \name Modifiers + //! \{ + + //! Erases all of the elements and deallocates all external memory that is + //! occupied. + void clear() + { + m_size = 0; + if (m_from == NULL) + m_bm->delete_blocks(m_bids.begin(), m_bids.end()); + + m_bids.clear(); + m_page_status.clear(); + m_page_to_slot.clear(); + while (!m_free_slots.empty()) + m_free_slots.pop(); + + for (unsigned_type i = 0; i < numpages(); ++i) + m_free_slots.push(i); + } + + //! \name Front and Back Access + //! \{ + + //! Append a new element at the end. + void push_back(const_reference obj) + { + size_type old_size = m_size; + resize(old_size + 1); + element(old_size) = obj; + } + //! Removes the last element (without returning it, see back()). + void pop_back() + { + resize(m_size - 1); + } + + //! \} + + //! \name Operators + //! \{ + + //! Returns a reference to the last element, see \ref design_vector_notes. + reference back() + { + return element(m_size - 1); + } + //! Returns a reference to the first element, see \ref design_vector_notes. + reference front() + { + return element(0); + } + //! Returns a constant reference to the last element, see \ref design_vector_notes. + const_reference back() const + { + return const_element(m_size - 1); + } + //! Returns a constant reference to the first element, see \ref design_vector_notes. + const_reference front() const + { + return const_element(0); + } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + //! Construct vector from a file. + //! \param from file to be constructed from + //! \param size Number of elements. + //! \param npages Number of cached pages. + //! \warning Only one \c vector can be assigned to a particular (physical) file. + //! The block size of the vector must be a multiple of the element size + //! \c sizeof(ValueType) and the page size (4096). + vector(file* from, size_type size = size_type(-1), unsigned_type npages = pager_type().size()) + : m_size((size == size_type(-1)) ? size_from_file_length(from->size()) : size), + m_bids((size_t)div_ceil(m_size, size_type(block_type::size))), + m_pager(npages), + m_page_status(div_ceil(m_bids.size(), page_size)), + m_page_to_slot(div_ceil(m_bids.size(), page_size)), + m_slot_to_page(npages), + m_cache(NULL), + m_from(from), + m_exported(false) + { + // initialize from file + if (!block_type::has_only_data) + { + std::ostringstream str; + str << "The block size for a vector that is mapped to a file must be a multiple of the element size (" << + sizeof(value_type) << ") and the page size (4096)."; + throw std::runtime_error(str.str()); + } + + m_bm = block_manager::get_instance(); + + allocate_page_cache(); + + for (size_t i = 0; i < m_page_status.size(); ++i) + { + m_page_status[i] = valid_on_disk; + m_page_to_slot[i] = on_disk; + } + + for (unsigned_type i = 0; i < numpages(); ++i) + m_free_slots.push(i); + + // allocate blocks equidistantly and in-order + size_type offset = 0; + for (bids_container_iterator it = m_bids.begin(); + it != m_bids.end(); ++it, offset += size_type(block_type::raw_size)) + { + (*it).storage = from; + (*it).offset = offset; + } + from->set_size(offset); + } + + //! copy-constructor + vector(const vector& obj) + : m_size(obj.size()), + m_bids((size_t)div_ceil(obj.size(), block_type::size)), + m_pager(obj.numpages()), + m_page_status(div_ceil(m_bids.size(), page_size)), + m_page_to_slot(div_ceil(m_bids.size(), page_size)), + m_slot_to_page(obj.numpages()), + m_cache(NULL), + m_from(NULL), + m_exported(false) + { + assert(!obj.m_exported); + m_bm = block_manager::get_instance(); + + allocate_page_cache(); + + for (size_t i = 0; i < m_page_status.size(); ++i) + { + m_page_status[i] = uninitialized; + m_page_to_slot[i] = on_disk; + } + + for (unsigned_type i = 0; i < numpages(); ++i) + m_free_slots.push(i); + + m_bm->new_blocks(m_alloc_strategy, m_bids.begin(), m_bids.end(), 0); + + const_iterator inbegin = obj.begin(); + const_iterator inend = obj.end(); + std::copy(inbegin, inend, begin()); + } + + //! \} + + //! \name Operators + //! \{ + + //! assignment operator + vector& operator = (const vector& obj) + { + if (&obj != this) + { + vector tmp(obj); + this->swap(tmp); + } + return *this; + } + + //! \} + + //! \name Iterator Construction + //! \{ + + //! returns an iterator pointing to the beginning of the vector, see \ref design_vector_notes. + iterator begin() + { + return iterator(this, 0); + } + //! returns a const_iterator pointing to the beginning of the vector, see \ref design_vector_notes. + const_iterator begin() const + { + return const_iterator(this, 0); + } + //! returns a const_iterator pointing to the beginning of the vector, see \ref design_vector_notes. + const_iterator cbegin() const + { + return begin(); + } + //! returns an iterator pointing beyond the end of the vector, see \ref design_vector_notes. + iterator end() + { + return iterator(this, m_size); + } + //! returns a const_iterator pointing beyond the end of the vector, see \ref design_vector_notes. + const_iterator end() const + { + return const_iterator(this, m_size); + } + //! returns a const_iterator pointing beyond the end of the vector, see \ref design_vector_notes. + const_iterator cend() const + { + return end(); + } + + //! returns a reverse_iterator pointing to the end of the vector. + reverse_iterator rbegin() + { + return reverse_iterator(end()); + } + //! returns a reverse_iterator pointing to the end of the vector. + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(end()); + } + //! returns a reverse_iterator pointing to the end of the vector. + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(end()); + } + //! returns a reverse_iterator pointing beyond the beginning of the vector. + reverse_iterator rend() + { + return reverse_iterator(begin()); + } + //! returns a reverse_iterator pointing beyond the beginning of the vector. + const_reverse_iterator rend() const + { + return const_reverse_iterator(begin()); + } + //! returns a reverse_iterator pointing beyond the beginning of the vector. + const_reverse_iterator crend() const + { + return const_reverse_iterator(begin()); + } + + //! \} + + //! \name Direct Element Access + //! \{ + + //! access the element at the given vector's offset + reference operator [] (size_type offset) + { + return element(offset); + } + //! access the element at the given vector's offset + const_reference operator [] (size_type offset) const + { + return const_element(offset); + } + + //! access the element at the given vector's offset + reference at(size_type offset) + { + assert(offset < (size_type)size()); + return element(offset); + } + //! access the element at the given vector's offset + const_reference at(size_type offset) const + { + assert(offset < (size_type)size()); + return const_element(offset); + } + + //! return true if the given vector offset is in cache + bool is_element_cached(size_type offset) const + { + return is_page_cached(blocked_index_type(offset)); + } + + //! \} + + //! \name Modifiers + //! \{ + + //! Flushes the cache pages to the external memory. + void flush() const + { + simple_vector non_free_slots(numpages()); + + for (unsigned_type i = 0; i < numpages(); i++) + non_free_slots[i] = true; + + while (!m_free_slots.empty()) + { + non_free_slots[m_free_slots.front()] = false; + m_free_slots.pop(); + } + + for (unsigned_type i = 0; i < numpages(); i++) + { + m_free_slots.push(i); + int_type page_no = m_slot_to_page[i]; + if (non_free_slots[i]) + { + STXXL_VERBOSE_VECTOR("flush(): flushing page " << i << " at address " << + (int64(page_no) * int64(block_type::size) * int64(page_size))); + write_page(page_no, i); + + m_page_to_slot[page_no] = on_disk; + } + } + } + + //! \} + + //! \name Constructors/Destructors + //! \{ + + ~vector() + { + STXXL_VERBOSE_VECTOR("~vector()"); + try + { + flush(); + } + catch (io_error e) + { + STXXL_ERRMSG("io_error thrown in ~vector(): " << e.what()); + } + catch (...) + { + STXXL_ERRMSG("Exception thrown in ~vector()"); + } + + if (!m_exported) + { + if (m_from == NULL) { + m_bm->delete_blocks(m_bids.begin(), m_bids.end()); + } + else // file must be truncated + { + STXXL_VERBOSE_VECTOR("~vector(): Changing size of file " << + ((void*)m_from) << " to " << file_length()); + STXXL_VERBOSE_VECTOR("~vector(): size of the vector is " << size()); + try + { + m_from->set_size(file_length()); + } + catch (...) + { + STXXL_ERRMSG("Exception thrown in ~vector()...set_size()"); + } + } + } + delete m_cache; + } + + //! \} + + //! \name Miscellaneous + //! \{ + + //! Export data such that it is persistent on the file system. Resulting + //! files will be numbered ascending. + void export_files(std::string filename_prefix) + { + int64 no = 0; + for (bids_container_iterator i = m_bids.begin(); i != m_bids.end(); ++i) { + std::ostringstream number; + number << std::setw(9) << std::setfill('0') << no; + size_type current_block_size = + ((i + 1) == m_bids.end() && m_size % block_type::size > 0) ? + (m_size % block_type::size) * sizeof(value_type) : + block_type::size * sizeof(value_type); + (*i).storage->export_files((*i).offset, current_block_size, filename_prefix + number.str()); + ++no; + } + m_exported = true; + } + + //! Get the file associated with this vector, or NULL. + file * get_file() const + { + return m_from; + } + + //! \} + + //! \name Capacity + //! \{ + + //! Set the blocks and the size of this container explicitly. + //! The vector must be completely empty before. + template + void set_content(const ForwardIterator& bid_begin, const ForwardIterator& bid_end, size_type n) + { + unsigned_type new_bids_size = div_ceil(n, block_type::size); + m_bids.resize(new_bids_size); + std::copy(bid_begin, bid_end, m_bids.begin()); + unsigned_type new_pages = div_ceil(new_bids_size, page_size); + m_page_status.resize(new_pages, valid_on_disk); + m_page_to_slot.resize(new_pages, on_disk); + m_size = n; + } + + //! Number of pages used by the pager. + inline unsigned_type numpages() const + { + return m_pager.size(); + } + + //! \} + +private: + bids_container_iterator bid(const size_type& offset) + { + return (m_bids.begin() + + static_cast + (offset / block_type::size)); + } + bids_container_iterator bid(const blocked_index_type& offset) + { + return (m_bids.begin() + + static_cast + (offset.get_block2() * PageSize + offset.get_block1())); + } + const_bids_container_iterator bid(const size_type& offset) const + { + return (m_bids.begin() + + static_cast + (offset / block_type::size)); + } + const_bids_container_iterator bid(const blocked_index_type& offset) const + { + return (m_bids.begin() + + static_cast + (offset.get_block2() * PageSize + offset.get_block1())); + } + + void read_page(int_type page_no, int_type cache_slot) const + { + assert(page_no < (int_type)m_page_status.size()); + if (m_page_status[page_no] == uninitialized) + return; + STXXL_VERBOSE_VECTOR("read_page(): page_no=" << page_no << " cache_slot=" << cache_slot); + request_ptr* reqs = new request_ptr[page_size]; + int_type block_no = page_no * page_size; + int_type last_block = STXXL_MIN(block_no + page_size, int_type(m_bids.size())); + int_type i = cache_slot * page_size, j = 0; + for ( ; block_no < last_block; ++block_no, ++i, ++j) + { + reqs[j] = (*m_cache)[i].read(m_bids[block_no]); + } + assert(last_block - page_no * page_size > 0); + wait_all(reqs, last_block - page_no * page_size); + delete[] reqs; + } + void write_page(int_type page_no, int_type cache_slot) const + { + assert(page_no < (int_type)m_page_status.size()); + if (!(m_page_status[page_no] & dirty)) + return; + STXXL_VERBOSE_VECTOR("write_page(): page_no=" << page_no << " cache_slot=" << cache_slot); + request_ptr* reqs = new request_ptr[page_size]; + int_type block_no = page_no * page_size; + int_type last_block = STXXL_MIN(block_no + page_size, int_type(m_bids.size())); + assert(block_no < last_block); + int_type i = cache_slot * page_size, j = 0; + for ( ; block_no < last_block; ++block_no, ++i, ++j) + { + reqs[j] = (*m_cache)[i].write(m_bids[block_no]); + } + m_page_status[page_no] = valid_on_disk; + assert(last_block - page_no * page_size > 0); + wait_all(reqs, last_block - page_no * page_size); + delete[] reqs; + } + + reference element(size_type offset) + { +#ifdef STXXL_RANGE_CHECK + assert(offset < (size_type)size()); +#endif + return element(blocked_index_type(offset)); + } + + reference element(const blocked_index_type& offset) + { +#ifdef STXXL_RANGE_CHECK + assert(offset.get_pos() < size()); +#endif + unsigned_type page_no = offset.get_block2(); + assert(page_no < m_page_to_slot.size()); // fails if offset is too large, out of bound access + int_type cache_slot = m_page_to_slot[page_no]; + if (cache_slot < 0) // == on_disk + { + if (m_free_slots.empty()) // has to kick + { + int_type kicked_slot = m_pager.kick(); + m_pager.hit(kicked_slot); + int_type old_page_no = m_slot_to_page[kicked_slot]; + m_page_to_slot[page_no] = kicked_slot; + m_page_to_slot[old_page_no] = on_disk; + m_slot_to_page[kicked_slot] = page_no; + + write_page(old_page_no, kicked_slot); + read_page(page_no, kicked_slot); + + m_page_status[page_no] = dirty; + + return (*m_cache)[kicked_slot * page_size + offset.get_block1()][offset.get_offset()]; + } + else + { + int_type free_slot = m_free_slots.front(); + m_free_slots.pop(); + m_pager.hit(free_slot); + m_page_to_slot[page_no] = free_slot; + m_slot_to_page[free_slot] = page_no; + + read_page(page_no, free_slot); + + m_page_status[page_no] = dirty; + + return (*m_cache)[free_slot * page_size + offset.get_block1()][offset.get_offset()]; + } + } + else + { + m_page_status[page_no] = dirty; + m_pager.hit(cache_slot); + return (*m_cache)[cache_slot * page_size + offset.get_block1()][offset.get_offset()]; + } + } + + // don't forget to first flush() the vector's cache before updating pages externally + void page_externally_updated(unsigned_type page_no) const + { + // fails if offset is too large, out of bound access + assert(page_no < m_page_status.size()); + // "A dirty page has been marked as newly initialized. The page content will be lost." + assert(!(m_page_status[page_no] & dirty)); + if (m_page_to_slot[page_no] != on_disk) { + // remove page from cache + m_free_slots.push(m_page_to_slot[page_no]); + m_page_to_slot[page_no] = on_disk; + STXXL_VERBOSE_VECTOR("page_externally_updated(): page_no=" << page_no << " flushed from cache."); + } + else { + STXXL_VERBOSE_VECTOR("page_externally_updated(): page_no=" << page_no << " no need to flush."); + } + m_page_status[page_no] = valid_on_disk; + } + + void block_externally_updated(size_type offset) const + { + page_externally_updated( + (unsigned_type)(offset / (block_type::size * page_size)) + ); + } + + void block_externally_updated(const blocked_index_type& offset) const + { + page_externally_updated(offset.get_block2()); + } + + const_reference const_element(size_type offset) const + { + return const_element(blocked_index_type(offset)); + } + + const_reference const_element(const blocked_index_type& offset) const + { + unsigned_type page_no = offset.get_block2(); + assert(page_no < m_page_to_slot.size()); // fails if offset is too large, out of bound access + int_type cache_slot = m_page_to_slot[page_no]; + if (cache_slot < 0) // == on_disk + { + if (m_free_slots.empty()) // has to kick + { + int_type kicked_slot = m_pager.kick(); + m_pager.hit(kicked_slot); + int_type old_page_no = m_slot_to_page[kicked_slot]; + m_page_to_slot[page_no] = kicked_slot; + m_page_to_slot[old_page_no] = on_disk; + m_slot_to_page[kicked_slot] = page_no; + + write_page(old_page_no, kicked_slot); + read_page(page_no, kicked_slot); + + return (*m_cache)[kicked_slot * page_size + offset.get_block1()][offset.get_offset()]; + } + else + { + int_type free_slot = m_free_slots.front(); + m_free_slots.pop(); + m_pager.hit(free_slot); + m_page_to_slot[page_no] = free_slot; + m_slot_to_page[free_slot] = page_no; + + read_page(page_no, free_slot); + + return (*m_cache)[free_slot * page_size + offset.get_block1()][offset.get_offset()]; + } + } + else + { + m_pager.hit(cache_slot); + return (*m_cache)[cache_slot * page_size + offset.get_block1()][offset.get_offset()]; + } + } + + bool is_page_cached(const blocked_index_type& offset) const + { + unsigned_type page_no = offset.get_block2(); + assert(page_no < m_page_to_slot.size()); // fails if offset is too large, out of bound access + int_type cache_slot = m_page_to_slot[page_no]; + return (cache_slot >= 0); // on_disk == -1 + } +}; + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +inline bool operator == (stxxl::vector& a, + stxxl::vector& b) +{ + return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); +} + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +inline bool operator != (stxxl::vector& a, + stxxl::vector& b) +{ + return !(a == b); +} + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +inline bool operator < (stxxl::vector& a, + stxxl::vector& b) +{ + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +} + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +inline bool operator > (stxxl::vector& a, + stxxl::vector& b) +{ + return b < a; +} + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +inline bool operator <= (stxxl::vector& a, + stxxl::vector& b) +{ + return !(b < a); +} + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +inline bool operator >= (stxxl::vector& a, + stxxl::vector& b) +{ + return !(a < b); +} + +//////////////////////////////////////////////////////////////////////////// + +// specialization for stxxl::vector, to use only const_iterators +template +bool is_sorted( + stxxl::vector_iterator first, + stxxl::vector_iterator last) +{ + return stxxl::is_sorted( + stxxl::const_vector_iterator(first), + stxxl::const_vector_iterator(last)); +} + +template +bool is_sorted( + stxxl::vector_iterator first, + stxxl::vector_iterator last, + StrictWeakOrdering comp) +{ + return stxxl::is_sorted( + stxxl::const_vector_iterator(first), + stxxl::const_vector_iterator(last), + comp); +} + +//////////////////////////////////////////////////////////////////////////// + +template +class vector_bufreader_iterator; + +/*! + * Buffered sequential reader from a vector using overlapped I/O. + * + * This buffered reader can be used to read a large sequential region of a + * vector using overlapped I/O. The object is created from an iterator range, + * which can then be read to using operator<<(), or with operator*() and + * operator++(). + * + * The interface also fulfills all requirements of a stream. Actually most of + * the code is identical to stream::vector_iterator2stream. + * + * Note that this buffered reader is inefficient for reading small ranges. This + * is intentional, as one can just use operator[] on the vector for that. + * + * See \ref tutorial_vector_buf + */ +template +class vector_bufreader : public noncopyable +{ +public: + //! template parameter: the vector iterator type + typedef VectorIterator vector_iterator; + + //! value type of the output vector + typedef typename vector_iterator::value_type value_type; + + //! block type used in the vector + typedef typename vector_iterator::block_type block_type; + + //! type of the input vector + typedef typename vector_iterator::vector_type vector_type; + + //! block identifier iterator of the vector + typedef typename vector_iterator::bids_container_iterator bids_container_iterator; + + //! construct output buffered stream used for overlapped reading + typedef buf_istream buf_istream_type; + + //! construct an iterator for vector_bufreader (for C++11 range-based for loop) + typedef vector_bufreader_iterator bufreader_iterator; + + //! size of remaining data + typedef typename vector_type::size_type size_type; + +protected: + //! iterator to the beginning of the range. + vector_iterator m_begin; + + //! internal "current" iterator into the vector. + vector_iterator m_iter; + + //! iterator to the end of the range. + vector_iterator m_end; + + //! buffered input stream used to overlapped I/O. + buf_istream_type* m_bufin; + + //! number of blocks to use as buffers. + unsigned_type m_nbuffers; + + //! allow vector_bufreader_iterator to check m_iter against its current value + friend class vector_bufreader_iterator; + +public: + //! Create overlapped reader for the given iterator range. + //! \param begin iterator to position were to start reading in vector + //! \param end iterator to position were to end reading in vector + //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) + vector_bufreader(vector_iterator begin, vector_iterator end, + unsigned_type nbuffers = 0) + : m_begin(begin), m_end(end), + m_bufin(NULL), + m_nbuffers(nbuffers) + { + m_begin.flush(); // flush container + + if (m_nbuffers == 0) + m_nbuffers = 2 * config::get_instance()->disks_number(); + + rewind(); + } + + //! Create overlapped reader for the whole vector's content. + //! \param vec vector to read + //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) + vector_bufreader(const vector_type& vec, unsigned_type nbuffers = 0) + : m_begin(vec.begin()), m_end(vec.end()), + m_bufin(NULL), + m_nbuffers(nbuffers) + { + m_begin.flush(); // flush container + + if (m_nbuffers == 0) + m_nbuffers = 2 * config::get_instance()->disks_number(); + + rewind(); + } + + //! Rewind stream back to begin. Note that this recreates the buffered + //! reader and is thus not cheap. + void rewind() + { + m_iter = m_begin; + if (empty()) return; + + if (m_bufin) delete m_bufin; + + // find last bid to read + bids_container_iterator end_bid = m_end.bid() + (m_end.block_offset() ? 1 : 0); + + // construct buffered istream for range + m_bufin = new buf_istream_type(m_begin.bid(), end_bid, m_nbuffers); + + // skip the beginning of the block, up to real beginning + vector_iterator curr = m_begin - m_begin.block_offset(); + + for ( ; curr != m_begin; ++curr) + ++(*m_bufin); + } + + //! Finish reading and free buffered reader. + ~vector_bufreader() + { + if (m_bufin) delete m_bufin; + } + + //! Return constant reference to current item + const value_type& operator * () const + { + return *(*m_bufin); + } + + //! Return constant pointer to current item + const value_type* operator -> () const + { + return &(*(*m_bufin)); + } + + //! Advance to next item (asserts if !empty()). + vector_bufreader& operator ++ () + { + assert(!empty()); + ++m_iter; + ++(*m_bufin); + + if (UNLIKELY(empty())) { + delete m_bufin; + m_bufin = NULL; + } + + return *this; + } + + //! Read current item into variable and advance to next one. + vector_bufreader& operator >> (value_type& v) + { + v = operator * (); + operator ++ (); + + return *this; + } + + //! Return remaining size. + size_type size() const + { + assert(m_begin <= m_iter && m_iter <= m_end); + return (size_type)(m_end - m_iter); + } + + //! Returns true once the whole range has been read. + bool empty() const + { + return (m_iter == m_end); + } + + //! Return vector_bufreader_iterator for C++11 range-based for loop + bufreader_iterator begin() + { + return bufreader_iterator(*this, m_begin); + } + + //! Return vector_bufreader_iterator for C++11 range-based for loop + bufreader_iterator end() + { + return bufreader_iterator(*this, m_end); + } +}; + +//////////////////////////////////////////////////////////////////////////// + +/*! + * Adapter for vector_bufreader to match iterator requirements of C++11 + * range-based loop construct. + * + * Since vector_bufreader itself points to only one specific item, this + * iterator is merely a counter facade. The functions operator*() and + * operator++() must only be called when it is in _sync_ with the bufreader + * object. This is generally only the case for an iterator constructed with + * begin() and then advanced with operator++(). The class checks this using + * asserts(), the operators will fail if used wrong. + * + * See \ref tutorial_vector_buf + */ +template +class vector_bufreader_iterator +{ +public: + //! The underlying buffered reader type + typedef VectorBufReaderType vector_bufreader_type; + + //! Value type of vector + typedef typename vector_bufreader_type::value_type value_type; + + //! Use vector_iterator to reference a point in the vector. + typedef typename vector_bufreader_type::vector_iterator vector_iterator; + +protected: + //! Buffered reader used to access elements in vector + vector_bufreader_type& m_bufreader; + + //! Use vector_iterator to reference a point in the vector. + vector_iterator m_iter; + +public: + //! Construct iterator using vector_iterator + vector_bufreader_iterator(vector_bufreader_type& bufreader, const vector_iterator& iter) + : m_bufreader(bufreader), m_iter(iter) + { } + + //! Return constant reference to current item + const value_type& operator * () const + { + assert(m_bufreader.m_iter == m_iter); + return m_bufreader.operator * (); + } + + //! Return constant pointer to current item + const value_type* operator -> () const + { + assert(m_bufreader.m_iter == m_iter); + return m_bufreader.operator -> (); + } + + //! Make bufreader advance to next item (asserts if !empty() or if iterator + //! does not point to current). + vector_bufreader_iterator& operator ++ () + { + assert(m_bufreader.m_iter == m_iter); + m_bufreader.operator ++ (); + m_iter++; + return *this; + } + + //! Equality comparison operator + bool operator == (const vector_bufreader_iterator& vbi) const + { + assert(&m_bufreader == &vbi.m_bufreader); + return (m_iter == vbi.m_iter); + } + + //! Inequality comparison operator + bool operator != (const vector_bufreader_iterator& vbi) const + { + assert(&m_bufreader == &vbi.m_bufreader); + return (m_iter != vbi.m_iter); + } +}; + +//////////////////////////////////////////////////////////////////////////// + +/*! + * Buffered sequential reverse reader from a vector using overlapped I/O. + * + * This buffered reader can be used to read a large sequential region of a + * vector _in_reverse_ using overlapped I/O. The object is created from an + * iterator range, which can then be read to using operator<<(), or with + * operator*() and operator++(), where ++ actually goes to the preceding + * element. + * + * The interface also fulfills all requirements of a stream. Actually most of + * the code is identical to stream::vector_iterator2stream. + * + * Note that this buffered reader is inefficient for reading small ranges. This + * is intentional, as one can just use operator[] on the vector for that. + * + * See \ref tutorial_vector_buf + */ +template +class vector_bufreader_reverse : public noncopyable +{ +public: + //! template parameter: the vector iterator type + typedef VectorIterator vector_iterator; + + //! value type of the output vector + typedef typename vector_iterator::value_type value_type; + + //! block type used in the vector + typedef typename vector_iterator::block_type block_type; + + //! type of the input vector + typedef typename vector_iterator::vector_type vector_type; + + //! block identifier iterator of the vector + typedef typename vector_iterator::bids_container_iterator bids_container_iterator; + + //! construct output buffered stream used for overlapped reading + typedef buf_istream_reverse buf_istream_type; + + //! size of remaining data + typedef typename vector_type::size_type size_type; + +protected: + //! iterator to the beginning of the range. + vector_iterator m_begin; + + //! internal "current" iterator into the vector. + vector_iterator m_iter; + + //! iterator to the end of the range. + vector_iterator m_end; + + //! buffered input stream used to overlapped I/O. + buf_istream_type* m_bufin; + + //! number of blocks to use as buffers. + unsigned_type m_nbuffers; + +public: + //! Create overlapped reader for the given iterator range. + //! \param begin iterator to position were to start reading in vector + //! \param end iterator to position were to end reading in vector + //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) + vector_bufreader_reverse(vector_iterator begin, vector_iterator end, + unsigned_type nbuffers = 0) + : m_begin(begin), m_end(end), + m_bufin(NULL), + m_nbuffers(nbuffers) + { + m_begin.flush(); // flush container + + if (m_nbuffers == 0) + m_nbuffers = 2 * config::get_instance()->disks_number(); + + rewind(); + } + + //! Create overlapped reader for the whole vector's content. + //! \param vec vector to read + //! \param nbuffers number of buffers used for overlapped I/O (>= 2*D recommended) + vector_bufreader_reverse(const vector_type& vec, unsigned_type nbuffers = 0) + : m_begin(vec.begin()), m_end(vec.end()), + m_bufin(NULL), + m_nbuffers(nbuffers) + { + m_begin.flush(); // flush container + + if (m_nbuffers == 0) + m_nbuffers = 2 * config::get_instance()->disks_number(); + + rewind(); + } + + //! Rewind stream back to begin. Note that this recreates the buffered + //! reader and is thus not cheap. + void rewind() + { + m_iter = m_end; + if (empty()) return; + + if (m_bufin) delete m_bufin; + + // find last bid to read + bids_container_iterator end_bid = m_end.bid() + (m_end.block_offset() ? 1 : 0); + + // construct buffered istream_reverse for range + m_bufin = new buf_istream_type(m_begin.bid(), end_bid, m_nbuffers); + + // skip to beginning of reverse sequence. + stxxl::int_type endoff = m_end.block_offset(); + if (endoff == 0) { + // nothing to skip + } + else { + // else, let ifstream_reverse skip last elements at end of block, + // up to real end + for ( ; endoff != block_type::size; endoff++) + ++(*m_bufin); + } + } + + //! Finish reading and free buffered reader. + ~vector_bufreader_reverse() + { + if (m_bufin) delete m_bufin; + } + + //! Return constant reference to current item + const value_type& operator * () const + { + return *(*m_bufin); + } + + //! Return constant pointer to current item + const value_type* operator -> () const + { + return &(*(*m_bufin)); + } + + //! Advance to next item (asserts if !empty()). + vector_bufreader_reverse& operator ++ () + { + assert(!empty()); + --m_iter; + ++(*m_bufin); + + if (UNLIKELY(empty())) { + delete m_bufin; + m_bufin = NULL; + } + + return *this; + } + + //! Read current item into variable and advance to next one. + vector_bufreader_reverse& operator >> (value_type& v) + { + v = operator * (); + operator ++ (); + + return *this; + } + + //! Return remaining size. + size_type size() const + { + assert(m_begin <= m_iter && m_iter <= m_end); + return (size_type)(m_iter - m_begin); + } + + //! Returns true once the whole range has been read. + bool empty() const + { + return (m_iter == m_begin); + } +}; + +//////////////////////////////////////////////////////////////////////////// + +/*! + * Buffered sequential writer to a vector using overlapped I/O. + * + * This buffered writer can be used to write a large sequential region of a + * vector using overlapped I/O. The object is created from an iterator range, + * which can then be written to using operator << (), or with operator * () and + * operator ++ (). + * + * The buffered writer is given one iterator in the constructor. When writing, + * this iterator advances in the vector and will \b enlarge the vector once it + * reaches the end(). The vector size is doubled each time; nevertheless, it is + * better to preinitialize the vector's size using stxxl::vector::resize(). + * + * See \ref tutorial_vector_buf + */ +template +class vector_bufwriter : public noncopyable +{ +public: + //! template parameter: the vector iterator type + typedef VectorIterator iterator; + + //! type of the output vector + typedef typename iterator::vector_type vector_type; + + //! value type of the output vector + typedef typename iterator::value_type value_type; + + //! block type used in the vector + typedef typename iterator::block_type block_type; + + //! block identifier iterator of the vector + typedef typename iterator::bids_container_iterator bids_container_iterator; + + //! iterator type of vector + typedef typename iterator::iterator vector_iterator; + typedef typename iterator::const_iterator vector_const_iterator; + + //! construct output buffered stream used for overlapped writing + typedef buf_ostream buf_ostream_type; + +protected: + //! internal iterator into the vector. + vector_iterator m_iter; + + //! iterator to the current end of the vector. + vector_const_iterator m_end; + + //! boolean whether the vector was grown, will shorten at finish(). + bool m_grown; + + //! iterator into vector of the last block accessed (used to issue updates + //! when the block is switched). + vector_const_iterator m_prevblk; + + //! buffered output stream used to overlapped I/O. + buf_ostream_type* m_bufout; + + //! number of blocks to use as buffers. + unsigned_type m_nbuffers; + +public: + //! Create overlapped writer beginning at the given iterator. + //! \param begin iterator to position were to start writing in vector + //! \param nbuffers number of buffers used for overlapped I/O (>= 2D recommended) + vector_bufwriter(vector_iterator begin, + unsigned_type nbuffers = 0) + : m_iter(begin), + m_end(m_iter.parent_vector()->end()), + m_grown(false), + m_bufout(NULL), + m_nbuffers(nbuffers) + { + if (m_nbuffers == 0) + m_nbuffers = 2 * config::get_instance()->disks_number(); + + assert(m_iter <= m_end); + } + + //! Create overlapped writer for the vector's beginning + //! \param vec vector to write + //! \param nbuffers number of buffers used for overlapped I/O (>= 2D recommended) + vector_bufwriter(vector_type& vec, + unsigned_type nbuffers = 0) + : m_iter(vec.begin()), + m_end(m_iter.parent_vector()->end()), + m_grown(false), + m_bufout(NULL), + m_nbuffers(nbuffers) + { + if (m_nbuffers == 0) + m_nbuffers = 2 * config::get_instance()->disks_number(); + + assert(m_iter <= m_end); + } + + //! Finish writing and flush output back to vector. + ~vector_bufwriter() + { + finish(); + } + + //! Return mutable reference to item at the position of the internal + //! iterator. + value_type& operator * () + { + if (UNLIKELY(m_iter == m_end)) + { + // iterator points to end of vector -> double vector's size + + if (m_bufout) { + // fixes issue with buf_ostream writing invalid blocks: when + // buf_ostream::current_elem advances to next block, flush() + // will write to block beyond bid().end. + if (m_iter.block_offset() != 0) + m_bufout->flush(); // flushes overlap buffers + + delete m_bufout; + m_bufout = NULL; + + if (m_iter.block_offset() != 0) + m_iter.block_externally_updated(); + } + + vector_type& v = *m_iter.parent_vector(); + if (v.size() < 2 * block_type::size) { + v.resize(2 * block_type::size); + } + else { + v.resize(2 * v.size()); + } + m_end = v.end(); + m_grown = true; + } + + assert(m_iter < m_end); + + if (UNLIKELY(m_bufout == NULL)) + { + if (m_iter.block_offset() != 0) + { + // output position is not at the start of the block, we + // continue to use the iterator initially passed to the + // constructor. + return *m_iter; + } + else + { + // output position is start of block: create buffered writer + + m_iter.flush(); // flush container + + // create buffered write stream for blocks + m_bufout = new buf_ostream_type(m_iter.bid(), m_nbuffers); + m_prevblk = m_iter; + + // drop through to normal output into buffered writer + } + } + + // if the pointer has finished a block, then we inform the vector that + // this block has been updated. + if (UNLIKELY(m_iter.block_offset() == 0)) { + if (m_prevblk != m_iter) { + m_prevblk.block_externally_updated(); + m_prevblk = m_iter; + } + } + + return m_bufout->operator * (); + } + + //! Advance internal iterator. + vector_bufwriter& operator ++ () + { + // always advance internal iterator + ++m_iter; + + // if buf_ostream active, advance that too + if (LIKELY(m_bufout != NULL)) m_bufout->operator ++ (); + + return *this; + } + + //! Write value to the current position and advance the internal iterator. + vector_bufwriter& operator << (const value_type& v) + { + operator * () = v; + operator ++ (); + + return *this; + } + + //! Finish writing and flush output back to vector. + void finish() + { + if (m_bufout) + { + // must finish the block started in the buffered writer: fill it with + // the data in the vector + vector_const_iterator const_out = m_iter; + + while (const_out.block_offset() != 0) + { + m_bufout->operator * () = *const_out; + m_bufout->operator ++ (); + ++const_out; + } + + // inform the vector that the block has been updated. + if (m_prevblk != m_iter) { + m_prevblk.block_externally_updated(); + m_prevblk = m_iter; + } + + delete m_bufout; + m_bufout = NULL; + } + + if (m_grown) + { + vector_type& v = *m_iter.parent_vector(); + v.resize(m_iter - v.begin()); + + m_grown = false; + } + } +}; + +//////////////////////////////////////////////////////////////////////////// + +//! External vector type generator. +//! +//! \tparam ValueType element type of contained objects (POD with no references to internal memory) +//! \tparam PageSize number of blocks in a page, default: \b 4 (recommended >= D) +//! \tparam CachePages number of pages in cache, default: \b 8 (recommended >= 2) +//! \tparam BlockSize external block size \a B in bytes, default: 2 MiB +//! \tparam AllocStr parallel disk allocation strategies: \c striping, RC, SR, or FR. default: \b RC. +//! \tparam Pager pager type: \c random or \c lru, default: \b lru. +//! +//! \warning Do not store references to the elements of an external vector. Such references +//! might be invalidated during any following access to elements of the vector +template < + typename ValueType, + unsigned PageSize = 4, + unsigned CachePages = 8, + unsigned BlockSize = STXXL_DEFAULT_BLOCK_SIZE(ValueType), + typename AllocStr = STXXL_DEFAULT_ALLOC_STRATEGY, + pager_type Pager = lru + > +struct VECTOR_GENERATOR +{ + typedef typename IF, random_pager >::result PagerType; + + typedef vector result; +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template < + typename ValueType, + unsigned PageSize, + typename PagerType, + unsigned BlockSize, + typename AllocStr, + typename SizeType> +void swap(stxxl::vector& a, + stxxl::vector& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_CONTAINERS_VECTOR_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/defines.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/defines.h new file mode 100644 index 0000000000..b12d571379 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/defines.h @@ -0,0 +1,99 @@ +/*************************************************************************** + * include/stxxl/bits/defines.h + * + * Document all defines that may change the behavior of stxxl. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008-2010 Andreas Beckmann + * + * 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 STXXL_DEFINES_HEADER +#define STXXL_DEFINES_HEADER + +//#define STXXL_HAVE_MMAP_FILE 0/1 +//#define STXXL_HAVE_SIMDISK_FILE 0/1 +//#define STXXL_HAVE_BOOSTFD_FILE 0/1 +//#define STXXL_HAVE_WINCALL_FILE 0/1 +//#define STXXL_HAVE_WBTL_FILE 0/1 +//#define STXXL_HAVE_LINUXAIO_FILE 0/1 +// default: 0/1 (platform and type dependent) +// used in: io/*_file.h, io/*_file.cpp, mng/mng.cpp +// affects: library +// effect: enables/disables some file implementations + +//#define STXXL_CHECK_BLOCK_ALIGNING +// default: not defined +// used in: io/*_file.cpp +// effect: call request::check_alignment() from request::request(...) + +//#define STXXL_CHECK_FOR_PENDING_REQUESTS_ON_SUBMISSION 0/1 +// default: 1 +// used in: io/*_queue*.cpp +// affects: library +// effect: check (and warn) for multiple concurrently pending I/O requests +// for the same block, usually causing coherency problems on +// out-of-order execution + +//#define STXXL_DO_NOT_COUNT_WAIT_TIME +// default: not defined +// used in: io/iostats.{h,cpp} +// effect: makes calls to wait time counting functions no-ops + +//#define STXXL_WAIT_LOG_ENABLED +// default: not defined +// used in: common/log.cpp, io/iostats.cpp +// effect: writes wait timing information to the file given via environment +// variable STXXLWAITLOGFILE, does nothing if this is not defined + +//#define STXXL_PRINT_TIMESTAMP_ALWAYS +// default: not defined +// used in: common/verbose.cpp +// affects: library +// effect: prefix all MSG/ERRMSG/VERBOSE with elapsed time since program start + +//#define STXXL_SORT_OPTIMAL_PREFETCHING 0/1 +// default: 1 +// used in: algo/*sort.h, stream/sort_stream.h +// effect if defined to 0: does not reorder prefetch requests to a disk +// optimal schedule (Hutchinson, Sanders, Vitter: Duality between +// prefetching and queued writing on parallel disks, 2005) + +//#define STXXL_CHECK_ORDER_IN_SORTS 0/1 +// default: 0 +// used in: algo/*sort.h, stream/sort_stream.h, containers/priority_queue.h +// effect if set to 1: perform additional checking of sorted results + +//#define STXXL_NO_WARN_RECURSIVE_SORT +// default: not defined +// used in: algo/sort_base.h +// affects: programs +// effect if defined: does not print error messages about possibly inefficient +// recursive merging + +//#define STXXL_HACK_SINGLE_IO_THREAD +// default: not defined +// used in: io/disk_queues.h +// affects: programs +// effect if defined: uses only a single I/O thread instead of one per disk +// used e.g. by EcoSort which puts input file, output file and +// scratch on a single disk (RAID0) + +//#define STXXL_MNG_COUNT_ALLOCATION 0/1 +// default: 1 +// used in: mng/block_manager.h +// effect if defined: counts current, total and maximum allocation of bytes in +// block manager. The numbers are exported via block_manager's get_ +// functions. This can be used to determine the maximum disk space required by +// an application. + +//#define STXXL_NO_DEPRECATED 0/1 +// default: 0 +// used in deprecated.h +// turns off deprecated warnings for some forced template instantiations + +#endif // !STXXL_DEFINES_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/deprecated.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/deprecated.h new file mode 100644 index 0000000000..1fed534834 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/deprecated.h @@ -0,0 +1,34 @@ +/*************************************************************************** + * include/stxxl/bits/deprecated.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008-2009 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_DEPRECATED_HEADER +#define STXXL_DEPRECATED_HEADER + +#include + +// deprecated functions + +#if STXXL_NO_DEPRECATED +// dont issue deprecated warnings for forced instantiation tests -tb + #define STXXL_DEPRECATED(x) x +#elif STXXL_MSVC + #define STXXL_DEPRECATED(x) __declspec(deprecated) x +#elif defined(__GNUG__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 30400) +// no __attribute__ ((__deprecated__)) in GCC 3.3 + #define STXXL_DEPRECATED(x) x +#else + #define STXXL_DEPRECATED(x) x __attribute__ ((__deprecated__)) +#endif + +#endif // !STXXL_DEPRECATED_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/boostfd_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/boostfd_file.h new file mode 100644 index 0000000000..1fa8bb3476 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/boostfd_file.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * include/stxxl/bits/io/boostfd_file.h + * + * File implementation based on boost::iostreams::file_decriptor + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2006 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_BOOSTFD_FILE_HEADER +#define STXXL_IO_BOOSTFD_FILE_HEADER + +#include + +#ifndef STXXL_HAVE_BOOSTFD_FILE +#if STXXL_BOOST_CONFIG // if boost is available + #define STXXL_HAVE_BOOSTFD_FILE 1 +#else + #define STXXL_HAVE_BOOSTFD_FILE 0 +#endif +#endif + +#if STXXL_HAVE_BOOSTFD_FILE + +#include +#include + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Implementation based on boost::iostreams::file_decriptor. +class boostfd_file : public disk_queued_file +{ + typedef boost::iostreams::file_descriptor fd_type; + +protected: + //! sequentialize function calls involving m_file_des + mutex m_fd_mutex; + fd_type m_file_des; + int m_mode; + offset_type _size(); + +public: + boostfd_file( + const std::string& filename, int mode, + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID); + ~boostfd_file(); + offset_type size(); + void set_size(offset_type newsize); + void lock(); + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_BOOSTFD_FILE + +#endif // !STXXL_IO_BOOSTFD_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/completion_handler.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/completion_handler.h new file mode 100644 index 0000000000..453905b129 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/completion_handler.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * include/stxxl/bits/io/completion_handler.h + * + * Loki-style completion handler (functors) + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_IO_COMPLETION_HANDLER_HEADER +#define STXXL_IO_COMPLETION_HANDLER_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +class request; + +class completion_handler_impl +{ +public: + virtual void operator () (request*) = 0; + virtual completion_handler_impl * clone() const = 0; + virtual ~completion_handler_impl() { } +}; + +template +class completion_handler1 : public completion_handler_impl +{ +private: + HandlerType m_handler; + +public: + completion_handler1(const HandlerType& handler) + : m_handler(handler) + { } + completion_handler1 * clone() const + { + return new completion_handler1(*this); + } + void operator () (request* req) + { + m_handler(req); + } +}; + +//! Completion handler class (Loki-style). +//! +//! In some situations one needs to execute some actions after completion of an +//! I/O request. In these cases one can use an I/O completion handler - a +//! function object that can be passed as a parameter to asynchronous I/O calls +//! \c stxxl::file::aread and \c stxxl::file::awrite . +class completion_handler +{ + compat_unique_ptr::result m_ptr; + +public: + //! Construct default, no operation completion handler. + completion_handler() + : m_ptr(static_cast(NULL)) + { } + + //! Copy constructor. + completion_handler(const completion_handler& obj) + : m_ptr(obj.m_ptr.get() ? obj.m_ptr.get()->clone() : NULL) + { } + + //! Construct a completion handler which calls some function. + template + completion_handler(const HandlerType& handler) + : m_ptr(new completion_handler1(handler)) + { } + + //! Assignment operator + completion_handler& operator = (const completion_handler& obj) + { + m_ptr.reset(obj.m_ptr.get() ? obj.m_ptr.get()->clone() : NULL); + return *this; + } + + //! Call the enclosed completion handler. + void operator () (request* req) + { + if (m_ptr.get()) + (*m_ptr)(req); + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_COMPLETION_HANDLER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/create_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/create_file.h new file mode 100644 index 0000000000..15706759fc --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/create_file.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * include/stxxl/bits/io/create_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_IO_CREATE_FILE_HEADER +#define STXXL_IO_CREATE_FILE_HEADER + +#include +#include + +#include + +STXXL_BEGIN_NAMESPACE + +//! create fileio object from io_impl string and a few parameters +file * create_file(const std::string& io_impl, + const std::string& filename, + int options, + int physical_device_id = file::DEFAULT_QUEUE, + int disk_allocator_id = file::NO_ALLOCATOR); + +// prototype +class disk_config; + +//! create fileio object from disk_config parameter +file * create_file(disk_config& config, int mode, + int disk_allocator_id = file::NO_ALLOCATOR); + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_CREATE_FILE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queued_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queued_file.h new file mode 100644 index 0000000000..21b94f6fe1 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queued_file.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * include/stxxl/bits/io/disk_queued_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_DISK_QUEUED_FILE_HEADER +#define STXXL_IO_DISK_QUEUED_FILE_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +class completion_handler; + +//! Implementation of some file methods based on serving_request. +class disk_queued_file : public virtual file +{ + int m_queue_id, m_allocator_id; + +public: + disk_queued_file(int queue_id, int allocator_id) + : m_queue_id(queue_id), m_allocator_id(allocator_id) + { } + + request_ptr aread( + void* buffer, + offset_type pos, + size_type bytes, + const completion_handler& on_cmpl = completion_handler()); + + request_ptr awrite( + void* buffer, + offset_type pos, + size_type bytes, + const completion_handler& on_cmpl = completion_handler()); + + virtual int get_queue_id() const + { + return m_queue_id; + } + + virtual int get_allocator_id() const + { + return m_allocator_id; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_DISK_QUEUED_FILE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queues.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queues.h new file mode 100644 index 0000000000..9aa231276a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/disk_queues.h @@ -0,0 +1,128 @@ +/*************************************************************************** + * include/stxxl/bits/io/disk_queues.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008-2010 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_DISK_QUEUES_HEADER +#define STXXL_IO_DISK_QUEUES_HEADER + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Encapsulates disk queues. +//! \remark is a singleton +class disk_queues : public singleton +{ + friend class singleton; + + typedef stxxl::int64 DISKID; + typedef std::map request_queue_map; + +protected: + request_queue_map queues; + disk_queues() + { + stxxl::stats::get_instance(); // initialize stats before ourselves + } + +public: + void add_request(request_ptr& req, DISKID disk) + { +#ifdef STXXL_HACK_SINGLE_IO_THREAD + disk = 42; +#endif + request_queue_map::iterator qi = queues.find(disk); + request_queue* q; + if (qi == queues.end()) + { + // create new request queue +#if STXXL_HAVE_LINUXAIO_FILE + if (dynamic_cast(req.get())) + q = queues[disk] = new linuxaio_queue( + dynamic_cast(req->get_file())->get_desired_queue_length() + ); + else +#endif + q = queues[disk] = new request_queue_impl_qwqr(); + } + else + q = qi->second; + + q->add_request(req); + } + + //! Cancel a request. + //! The specified request is canceled unless already being processed. + //! However, cancelation cannot be guaranteed. + //! Cancelled requests must still be waited for in order to ensure correct + //! operation. + //! \param req request to cancel + //! \param disk disk number for disk that \c req was scheduled on + //! \return \c true iff the request was canceled successfully + bool cancel_request(request_ptr& req, DISKID disk) + { +#ifdef STXXL_HACK_SINGLE_IO_THREAD + disk = 42; +#endif + if (queues.find(disk) != queues.end()) + return queues[disk]->cancel_request(req); + else + return false; + } + + request_queue * get_queue(DISKID disk) + { + if (queues.find(disk) != queues.end()) + return queues[disk]; + else + return NULL; + } + + ~disk_queues() + { + // deallocate all queues + for (request_queue_map::iterator i = queues.begin(); i != queues.end(); i++) + delete (*i).second; + } + + //! Changes requests priorities. + //! \param op one of: + //! - READ, read requests are served before write requests within a disk queue + //! - WRITE, write requests are served before read requests within a disk queue + //! - NONE, read and write requests are served by turns, alternately + void set_priority_op(request_queue::priority_op op) + { + for (request_queue_map::iterator i = queues.begin(); i != queues.end(); i++) + i->second->set_priority_op(op); + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_DISK_QUEUES_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/file.h new file mode 100644 index 0000000000..1615b132c0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/file.h @@ -0,0 +1,230 @@ +/*************************************************************************** + * include/stxxl/bits/io/file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008, 2010 Andreas Beckmann + * Copyright (C) 2008, 2009 Johannes Singler + * Copyright (C) 2013-2014 Timo Bingmann + * + * 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 STXXL_IO_FILE_HEADER +#define STXXL_IO_FILE_HEADER + +#include + +#if defined (__linux__) + #define STXXL_CHECK_BLOCK_ALIGNING +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup iolayer +//! \{ + +//! \defgroup fileimpl File I/O Implementations +//! Implementations of \c stxxl::file for various file access methods and +//! operating systems. +//! \{ + +class completion_handler; + +//! Defines interface of file. +//! +//! It is a base class for different implementations that might +//! base on various file systems or even remote storage interfaces +class file : private noncopyable +{ +public: + //! the offset of a request, also the size of the file + typedef request::offset_type offset_type; + //! the size of a request + typedef request::size_type size_type; + + //! Definition of acceptable file open modes. + //! + //! Various open modes in a file system must be + //! converted to this set of acceptable modes + enum open_mode + { + RDONLY = 1, //!< only reading of the file is allowed + WRONLY = 2, //!< only writing of the file is allowed + RDWR = 4, //!< read and write of the file are allowed + CREAT = 8, //!< in case file does not exist no error occurs and file is newly created + DIRECT = 16, //!< I/Os proceed bypassing file system buffers, i.e. unbuffered I/O. + //!< Tries to open with appropriate flags, if fails print warning and open normally. + TRUNC = 32, //!< once file is opened its length becomes zero + SYNC = 64, //!< open the file with O_SYNC | O_DSYNC | O_RSYNC flags set + NO_LOCK = 128, //!< do not acquire an exclusive lock by default + REQUIRE_DIRECT = 256 //!< implies DIRECT, fail if opening with DIRECT flag does not work. + }; + + static const int DEFAULT_QUEUE = -1; + static const int DEFAULT_LINUXAIO_QUEUE = -2; + static const int NO_ALLOCATOR = -1; + static const unsigned int DEFAULT_DEVICE_ID = (unsigned int)(-1); + + //! Construct a new file, usually called by a subclass. + file(unsigned int device_id = DEFAULT_DEVICE_ID) + : m_device_id(device_id) + { } + + //! Schedules an asynchronous read request to the file. + //! \param buffer pointer to memory buffer to read into + //! \param pos file position to start read from + //! \param bytes number of bytes to transfer + //! \param on_cmpl I/O completion handler + //! \return \c request_ptr request object, which can be used to track the + //! status of the operation + + virtual request_ptr aread(void* buffer, offset_type pos, size_type bytes, + const completion_handler& on_cmpl = completion_handler()) = 0; + + //! Schedules an asynchronous write request to the file. + //! \param buffer pointer to memory buffer to write from + //! \param pos starting file position to write + //! \param bytes number of bytes to transfer + //! \param on_cmpl I/O completion handler + //! \return \c request_ptr request object, which can be used to track the + //! status of the operation + virtual request_ptr awrite(void* buffer, offset_type pos, size_type bytes, + const completion_handler& on_cmpl = completion_handler()) = 0; + + virtual void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type) = 0; + + //! Changes the size of the file. + //! \param newsize new file size + virtual void set_size(offset_type newsize) = 0; + + //! Returns size of the file. + //! \return file size in bytes + virtual offset_type size() = 0; + + //! Returns the identifier of the file's queue number. + //! \remark Files allocated on the same physical device usually share the + //! same queue, unless there is a common queue (e.g. with linuxaio). + virtual int get_queue_id() const = 0; + + //! Returns the file's disk allocator number + virtual int get_allocator_id() const = 0; + + //! Locks file for reading and writing (acquires a lock in the file system). + virtual void lock() = 0; + + //! Discard a region of the file (mark it unused). + //! Some specialized file types may need to know freed regions + virtual void discard(offset_type offset, offset_type size) + { + STXXL_UNUSED(offset); + STXXL_UNUSED(size); + } + + virtual void export_files(offset_type offset, offset_type length, + std::string prefix) + { + STXXL_UNUSED(offset); + STXXL_UNUSED(length); + STXXL_UNUSED(prefix); + } + + //! close and remove file + virtual void close_remove() { } + + virtual ~file() noexcept(false) + { + unsigned_type nr = get_request_nref(); + if (nr != 0) + STXXL_ERRMSG("stxxl::file is being deleted while there are " + "still " << nr << " (unfinished) requests " + "referencing it"); + } + + //! Identifies the type of I/O implementation. + //! \return pointer to null terminated string of characters, containing the + //! name of I/O implementation + virtual const char * io_type() const = 0; + +protected: + //! The file's physical device id (e.g. used for prefetching sequence + //! calculation) + unsigned int m_device_id; + +public: + //! Returns the file's physical device id + unsigned int get_device_id() const + { + return m_device_id; + } + +protected: + //! count the number of requests referencing this file + atomic_counted_object m_request_ref; + +public: + //! increment referenced requests + void add_request_ref() + { + m_request_ref.inc_reference(); + } + + //! decrement referenced requests + void delete_request_ref() + { + m_request_ref.dec_reference(); + } + + //! return number of referenced requests + unsigned_type get_request_nref() + { + return m_request_ref.get_reference_count(); + } + +public: + //! \name Static Functions for Platform Abstraction + //! \{ + + //! unlink path from filesystem + static int unlink(const char* path); + + //! truncate a path to given length. Use this only if you dont have a + //! fileio-specific object, which provides truncate(). + static int truncate(const char* path, external_size_type length); + + //! \} +}; + +//! \} + +//! \defgroup reqlayer I/O Requests and Queues +//! Encapsulation of an I/O request, queues for requests and threads to process +//! them. +//! \{ +//! \} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_FILE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/fileperblock_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/fileperblock_file.h new file mode 100644 index 0000000000..4dd1768355 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/fileperblock_file.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * include/stxxl/bits/io/fileperblock_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008, 2009 Johannes Singler + * + * 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 STXXL_IO_FILEPERBLOCK_FILE_HEADER +#define STXXL_IO_FILEPERBLOCK_FILE_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Implementation of file based on other files, dynamically allocate one file per block. +//! Allows for dynamic disk space consumption. +template +class fileperblock_file : public disk_queued_file +{ +private: + std::string filename_prefix; + int mode; + offset_type current_size; + bool lock_file_created; + base_file_type lock_file; + +protected: + //! Constructs a file name for a given block. + std::string filename_for_block(offset_type offset); + +public: + //! Constructs file object. + //! param filename_prefix filename prefix, numbering will be appended to it + //! param mode open mode, see \c file::open_modes + fileperblock_file( + const std::string& filename_prefix, + int mode, + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID); + + virtual ~fileperblock_file(); + + virtual void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + + //! Changes the size of the file. + //! \param new_size value of the new file size + virtual void set_size(offset_type new_size) { current_size = new_size; } + + //! Returns size of the file. + //! \return file size in length + virtual offset_type size() { return current_size; } + + virtual void lock(); + + //! Frees the specified region. + //! Actually deletes the corresponding file if the whole thing is deleted. + virtual void discard(offset_type offset, offset_type length); + + //! Rename the file corresponding to the offset such that it is out of reach for deleting. + virtual void export_files(offset_type offset, offset_type length, std::string filename); + + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_FILEPERBLOCK_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/io.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/io.h new file mode 100644 index 0000000000..5c066f9fcc --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/io.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * include/stxxl/bits/io/io.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_IO_IO_HEADER +#define STXXL_IO_IO_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//! \c STXXL library namespace +STXXL_BEGIN_NAMESPACE + + STXXL_END_NAMESPACE + +#endif // !STXXL_IO_IO_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/iostats.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/iostats.h new file mode 100644 index 0000000000..d9ea649a4b --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/iostats.h @@ -0,0 +1,647 @@ +/*************************************************************************** + * include/stxxl/bits/io/iostats.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * Copyright (C) 2008-2010 Andreas Beckmann + * Copyright (C) 2009, 2010 Johannes Singler + * + * 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 STXXL_IO_IOSTATS_HEADER +#define STXXL_IO_IOSTATS_HEADER + +#ifndef STXXL_IO_STATS + #define STXXL_IO_STATS 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup iolayer +//! +//! \{ + +//! Collects various I/O statistics. +//! \remarks is a singleton +class stats : public singleton +{ + friend class singleton; + + unsigned reads, writes; // number of operations + int64 volume_read, volume_written; // number of bytes read/written + unsigned c_reads, c_writes; // number of cached operations + int64 c_volume_read, c_volume_written; // number of bytes read/written from/to cache + double t_reads, t_writes; // seconds spent in operations + double p_reads, p_writes; // seconds spent in parallel operations + double p_begin_read, p_begin_write; // start time of parallel operation + double p_ios; // seconds spent in all parallel I/O operations (read and write) + double p_begin_io; + double t_waits, p_waits; // seconds spent waiting for completion of I/O operations + double p_begin_wait; + double t_wait_read, p_wait_read; + double p_begin_wait_read; + double t_wait_write, p_wait_write; + double p_begin_wait_write; + int acc_reads, acc_writes; // number of requests, participating in parallel operation + int acc_ios; + int acc_waits; + int acc_wait_read, acc_wait_write; + double last_reset; + mutex read_mutex, write_mutex, io_mutex, wait_mutex; + + stats(); + +public: + enum wait_op_type { + WAIT_OP_ANY, + WAIT_OP_READ, + WAIT_OP_WRITE + }; + + class scoped_read_write_timer + { + typedef unsigned_type size_type; + + bool is_write; +#if STXXL_IO_STATS + bool running; +#endif + + public: + scoped_read_write_timer(size_type size, bool is_write = false) + : is_write(is_write) +#if STXXL_IO_STATS + , running(false) +#endif + { + start(size); + } + + ~scoped_read_write_timer() + { + stop(); + } + + void start(size_type size) + { +#if STXXL_IO_STATS + if (!running) { + running = true; + if (is_write) + stats::get_instance()->write_started(size); + else + stats::get_instance()->read_started(size); + } +#else + STXXL_UNUSED(size); +#endif + } + + void stop() + { +#if STXXL_IO_STATS + if (running) { + if (is_write) + stats::get_instance()->write_finished(); + else + stats::get_instance()->read_finished(); + running = false; + } +#endif + } + }; + + class scoped_write_timer + { + typedef unsigned_type size_type; + +#if STXXL_IO_STATS + bool running; +#endif + + public: + scoped_write_timer(size_type size) +#if STXXL_IO_STATS + : running(false) +#endif + { + start(size); + } + + ~scoped_write_timer() + { + stop(); + } + + void start(size_type size) + { +#if STXXL_IO_STATS + if (!running) { + running = true; + stats::get_instance()->write_started(size); + } +#else + STXXL_UNUSED(size); +#endif + } + + void stop() + { +#if STXXL_IO_STATS + if (running) { + stats::get_instance()->write_finished(); + running = false; + } +#endif + } + }; + + class scoped_read_timer + { + typedef unsigned_type size_type; + +#if STXXL_IO_STATS + bool running; +#endif + + public: + scoped_read_timer(size_type size) +#if STXXL_IO_STATS + : running(false) +#endif + { + start(size); + } + + ~scoped_read_timer() + { + stop(); + } + + void start(size_type size) + { +#if STXXL_IO_STATS + if (!running) { + running = true; + stats::get_instance()->read_started(size); + } +#else + STXXL_UNUSED(size); +#endif + } + + void stop() + { +#if STXXL_IO_STATS + if (running) { + stats::get_instance()->read_finished(); + running = false; + } +#endif + } + }; + + class scoped_wait_timer + { +#ifndef STXXL_DO_NOT_COUNT_WAIT_TIME + bool running; + wait_op_type wait_op; +#endif + + public: + scoped_wait_timer(wait_op_type wait_op, bool measure_time = true) +#ifndef STXXL_DO_NOT_COUNT_WAIT_TIME + : running(false), wait_op(wait_op) +#endif + { + if (measure_time) + start(); + } + + ~scoped_wait_timer() + { + stop(); + } + + void start() + { +#ifndef STXXL_DO_NOT_COUNT_WAIT_TIME + if (!running) { + running = true; + stats::get_instance()->wait_started(wait_op); + } +#endif + } + + void stop() + { +#ifndef STXXL_DO_NOT_COUNT_WAIT_TIME + if (running) { + stats::get_instance()->wait_finished(wait_op); + running = false; + } +#endif + } + }; + +public: + //! Returns total number of reads. + //! \return total number of reads + unsigned get_reads() const + { + return reads; + } + + //! Returns total number of writes. + //! \return total number of writes + unsigned get_writes() const + { + return writes; + } + + //! Returns number of bytes read from disks. + //! \return number of bytes read + int64 get_read_volume() const + { + return volume_read; + } + + //! Returns number of bytes written to the disks. + //! \return number of bytes written + int64 get_written_volume() const + { + return volume_written; + } + + //! Returns total number of reads served from cache. + //! \return total number of cached reads + unsigned get_cached_reads() const + { + return c_reads; + } + + //! Returns total number of cached writes. + //! \return total number of cached writes + unsigned get_cached_writes() const + { + return c_writes; + } + + //! Returns number of bytes read from cache. + //! \return number of bytes read from cache + int64 get_cached_read_volume() const + { + return c_volume_read; + } + + //! Returns number of bytes written to the cache. + //! \return number of bytes written to cache + int64 get_cached_written_volume() const + { + return c_volume_written; + } + + //! Time that would be spent in read syscalls if all parallel reads were serialized. + //! \return seconds spent in reading + double get_read_time() const + { + return t_reads; + } + + //! Time that would be spent in write syscalls if all parallel writes were serialized. + //! \return seconds spent in writing + double get_write_time() const + { + return t_writes; + } + + //! Period of time when at least one I/O thread was executing a read. + //! \return seconds spent in reading + double get_pread_time() const + { + return p_reads; + } + + //! Period of time when at least one I/O thread was executing a write. + //! \return seconds spent in writing + double get_pwrite_time() const + { + return p_writes; + } + + //! Period of time when at least one I/O thread was executing a read or a write. + //! \return seconds spent in I/O + double get_pio_time() const + { + return p_ios; + } + + //! I/O wait time counter. + //! \return number of seconds spent in I/O waiting functions \link + //! request::wait request::wait \endlink, \c wait_any and \c wait_all + double get_io_wait_time() const + { + return t_waits; + } + + double get_wait_read_time() const + { + return t_wait_read; + } + + double get_wait_write_time() const + { + return t_wait_write; + } + + //! Return time of the last reset. + //! \return seconds passed from the last reset() + double get_last_reset_time() const + { + return last_reset; + } + +#ifndef STXXL_IO_STATS_RESET_FORBIDDEN + //! Resets I/O time counters (including I/O wait counter). + STXXL_DEPRECATED(void reset()); +#endif + + //! Resets I/O wait time counter. + STXXL_DEPRECATED(void _reset_io_wait_time()); + + // for library use + void write_started(unsigned_type size_, double now = 0.0); + void write_canceled(unsigned_type size_); + void write_finished(); + void write_cached(unsigned_type size_); + void read_started(unsigned_type size_, double now = 0.0); + void read_canceled(unsigned_type size_); + void read_finished(); + void read_cached(unsigned_type size_); + void wait_started(wait_op_type wait_op); + void wait_finished(wait_op_type wait_op); +}; + +#if !STXXL_IO_STATS +inline void stats::write_started(unsigned_type size_, double now) +{ + STXXL_UNUSED(size_); + STXXL_UNUSED(now); +} +inline void stats::write_cached(unsigned_type size_) +{ + STXXL_UNUSED(size_); +} +inline void stats::write_finished() { } +inline void stats::read_started(unsigned_type size_, double now) +{ + STXXL_UNUSED(size_); + STXXL_UNUSED(now); +} +inline void stats::read_cached(unsigned_type size_) +{ + STXXL_UNUSED(size_); +} +inline void stats::read_finished() { } +#endif +#ifdef STXXL_DO_NOT_COUNT_WAIT_TIME +inline void stats::wait_started(wait_op_type) { } +inline void stats::wait_finished(wait_op_type) { } +#endif + +class stats_data +{ + //! number of operations + unsigned reads, writes; + //! number of bytes read/written + int64 volume_read, volume_written; + //! number of cached operations + unsigned c_reads, c_writes; + //! number of bytes read/written from/to cache + int64 c_volume_read, c_volume_written; + //! seconds spent in operations + double t_reads, t_writes; + //! seconds spent in parallel operations + double p_reads, p_writes; + //! seconds spent in all parallel I/O operations (read and write) + double p_ios; + //! seconds spent waiting for completion of I/O operations + double t_wait; + double t_wait_read, t_wait_write; + double elapsed; + +public: + stats_data() + : reads(0), + writes(0), + volume_read(0), + volume_written(0), + c_reads(0), + c_writes(0), + c_volume_read(0), + c_volume_written(0), + t_reads(0.0), + t_writes(0.0), + p_reads(0.0), + p_writes(0.0), + p_ios(0.0), + t_wait(0.0), + t_wait_read(0.0), + t_wait_write(0.0), + elapsed(0.0) + { } + + stats_data(const stats& s) + : reads(s.get_reads()), + writes(s.get_writes()), + volume_read(s.get_read_volume()), + volume_written(s.get_written_volume()), + c_reads(s.get_cached_reads()), + c_writes(s.get_cached_writes()), + c_volume_read(s.get_cached_read_volume()), + c_volume_written(s.get_cached_written_volume()), + t_reads(s.get_read_time()), + t_writes(s.get_write_time()), + p_reads(s.get_pread_time()), + p_writes(s.get_pwrite_time()), + p_ios(s.get_pio_time()), + t_wait(s.get_io_wait_time()), + t_wait_read(s.get_wait_read_time()), + t_wait_write(s.get_wait_write_time()), + elapsed(timestamp() - s.get_last_reset_time()) + { } + + stats_data operator + (const stats_data& a) const + { + stats_data s; + s.reads = reads + a.reads; + s.writes = writes + a.writes; + s.volume_read = volume_read + a.volume_read; + s.volume_written = volume_written + a.volume_written; + s.c_reads = c_reads + a.c_reads; + s.c_writes = c_writes + a.c_writes; + s.c_volume_read = c_volume_read + a.c_volume_read; + s.c_volume_written = c_volume_written + a.c_volume_written; + s.t_reads = t_reads + a.t_reads; + s.t_writes = t_writes + a.t_writes; + s.p_reads = p_reads + a.p_reads; + s.p_writes = p_writes + a.p_writes; + s.p_ios = p_ios + a.p_ios; + s.t_wait = t_wait + a.t_wait; + s.t_wait_read = t_wait_read + a.t_wait_read; + s.t_wait_write = t_wait_write + a.t_wait_write; + s.elapsed = elapsed + a.elapsed; + return s; + } + + stats_data operator - (const stats_data& a) const + { + stats_data s; + s.reads = reads - a.reads; + s.writes = writes - a.writes; + s.volume_read = volume_read - a.volume_read; + s.volume_written = volume_written - a.volume_written; + s.c_reads = c_reads - a.c_reads; + s.c_writes = c_writes - a.c_writes; + s.c_volume_read = c_volume_read - a.c_volume_read; + s.c_volume_written = c_volume_written - a.c_volume_written; + s.t_reads = t_reads - a.t_reads; + s.t_writes = t_writes - a.t_writes; + s.p_reads = p_reads - a.p_reads; + s.p_writes = p_writes - a.p_writes; + s.p_ios = p_ios - a.p_ios; + s.t_wait = t_wait - a.t_wait; + s.t_wait_read = t_wait_read - a.t_wait_read; + s.t_wait_write = t_wait_write - a.t_wait_write; + s.elapsed = elapsed - a.elapsed; + return s; + } + + unsigned get_reads() const + { + return reads; + } + + unsigned get_writes() const + { + return writes; + } + + int64 get_read_volume() const + { + return volume_read; + } + + int64 get_written_volume() const + { + return volume_written; + } + + unsigned get_cached_reads() const + { + return c_reads; + } + + unsigned get_cached_writes() const + { + return c_writes; + } + + int64 get_cached_read_volume() const + { + return c_volume_read; + } + + int64 get_cached_written_volume() const + { + return c_volume_written; + } + + double get_read_time() const + { + return t_reads; + } + + double get_write_time() const + { + return t_writes; + } + + double get_pread_time() const + { + return p_reads; + } + + double get_pwrite_time() const + { + return p_writes; + } + + double get_pio_time() const + { + return p_ios; + } + + double get_elapsed_time() const + { + return elapsed; + } + + double get_io_wait_time() const + { + return t_wait; + } + + double get_wait_read_time() const + { + return t_wait_read; + } + + double get_wait_write_time() const + { + return t_wait_write; + } +}; + +std::ostream& operator << (std::ostream& o, const stats_data& s); + +inline std::ostream& operator << (std::ostream& o, const stats& s) +{ + o << stxxl::stats_data(s); + return o; +} + +std::string format_with_SI_IEC_unit_multiplier(uint64 number, const char* unit = "", int multiplier = 1000); + +inline std::string add_IEC_binary_multiplier(uint64 number, const char* unit = "") +{ + return format_with_SI_IEC_unit_multiplier(number, unit, 1024); +} + +inline std::string add_SI_multiplier(uint64 number, const char* unit = "") +{ + return format_with_SI_IEC_unit_multiplier(number, unit, 1000); +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_IOSTATS_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_file.h new file mode 100644 index 0000000000..1aa1bfc4cb --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_file.h @@ -0,0 +1,82 @@ +/*************************************************************************** + * include/stxxl/bits/io/linuxaio_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2011 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_LINUXAIO_FILE_HEADER +#define STXXL_IO_LINUXAIO_FILE_HEADER + +#include + +#if STXXL_HAVE_LINUXAIO_FILE + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +class linuxaio_queue; + +//! \addtogroup fileimpl +//! \{ + +//! Implementation of \c file based on the Linux kernel interface for +//! asynchronous I/O +class linuxaio_file : public ufs_file_base, public disk_queued_file +{ + friend class linuxaio_request; + +private: + int desired_queue_length; + +public: + //! Constructs file object + //! \param filename path of file + //! \param mode open mode, see \c stxxl::file::open_modes + //! \param queue_id disk queue identifier + //! \param allocator_id linked disk_allocator + //! \param device_id physical device identifier + //! \param desired_queue_length queue length requested from kernel + linuxaio_file( + const std::string& filename, int mode, + int queue_id = DEFAULT_LINUXAIO_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID, + int desired_queue_length = 0) + : file(device_id), + ufs_file_base(filename, mode), + disk_queued_file(queue_id, allocator_id), + desired_queue_length(desired_queue_length) + { } + + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + request_ptr aread(void* buffer, offset_type pos, size_type bytes, + const completion_handler& on_cmpl = completion_handler()); + request_ptr awrite(void* buffer, offset_type pos, size_type bytes, + const completion_handler& on_cmpl = completion_handler()); + const char * io_type() const; + + int get_desired_queue_length() const + { + return desired_queue_length; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_LINUXAIO_FILE + +#endif // !STXXL_IO_LINUXAIO_FILE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_queue.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_queue.h new file mode 100644 index 0000000000..7f250903c6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_queue.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * include/stxxl/bits/io/linuxaio_queue.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2011 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_LINUXAIO_QUEUE_HEADER +#define STXXL_IO_LINUXAIO_QUEUE_HEADER + +#include + +#if STXXL_HAVE_LINUXAIO_FILE + +#include +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Queue for linuxaio_file(s) +//! +//! Only one queue exists in a program, i.e. it is a singleton. +class linuxaio_queue : public request_queue_impl_worker +{ + friend class linuxaio_request; + + typedef linuxaio_queue self_type; + +private: + //! OS context + aio_context_t context; + + //! storing linuxaio_request* would drop ownership + typedef std::list queue_type; + + // "waiting" request have submitted to this queue, but not yet to the OS, + // those are "posted" + mutex waiting_mtx, posted_mtx; + queue_type waiting_requests; + + //! max number of OS requests + int max_events; + //! number of requests in waitings_requests + semaphore num_waiting_requests, num_free_events, num_posted_requests; + + // two threads, one for posting, one for waiting + thread_type post_thread, wait_thread; + state post_thread_state, wait_thread_state; + + // Why do we need two threads, one for posting, and one for waiting? Is + // one not enough? + // 1. User call cannot io_submit directly, since this tends to take + // considerable time sometimes + // 2. A single thread cannot wait for the user program to post requests + // and the OS to produce I/O completion events at the same time + // (IOCB_CMD_NOOP does not seem to help here either) + + static const priority_op _priority_op = WRITE; + + static void * post_async(void* arg); // thread start callback + static void * wait_async(void* arg); // thread start callback + void post_requests(); + void handle_events(io_event* events, long num_events, bool canceled); + void wait_requests(); + void suspend(); + + // needed by linuxaio_request + aio_context_t get_io_context() { return context; } + +public: + //! Construct queue. Requests max number of requests simultaneously + //! submitted to disk, 0 means as many as possible + linuxaio_queue(int desired_queue_length = 0); + + void add_request(request_ptr& req); + bool cancel_request(request_ptr& req); + void complete_request(request_ptr& req); + ~linuxaio_queue(); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_LINUXAIO_FILE + +#endif // !STXXL_IO_LINUXAIO_QUEUE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_request.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_request.h new file mode 100644 index 0000000000..6ee20e5444 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/linuxaio_request.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * include/stxxl/bits/io/linuxaio_request.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2011 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_LINUXAIO_REQUEST_HEADER +#define STXXL_IO_LINUXAIO_REQUEST_HEADER + +#include + +#if STXXL_HAVE_LINUXAIO_FILE + +#include +#include + +#define STXXL_VERBOSE_LINUXAIO(msg) STXXL_VERBOSE2(msg) + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Request for an linuxaio_file. +class linuxaio_request : public request_with_state +{ + template + friend class fileperblock_file; + + //! control block of async request + iocb cb; + + void fill_control_block(); + +public: + linuxaio_request( + const completion_handler& on_cmpl, + file* file, + void* buffer, + offset_type offset, + size_type bytes, + request_type type) + : request_with_state(on_cmpl, file, buffer, offset, bytes, type) + { + assert(dynamic_cast(file)); + STXXL_VERBOSE_LINUXAIO("linuxaio_request[" << this << "]" << + " linuxaio_request" << + "(file=" << file << " buffer=" << buffer << + " offset=" << offset << " bytes=" << bytes << + " type=" << type << ")"); + } + + bool post(); + bool cancel(); + bool cancel_aio(); + void completed(bool posted, bool canceled); + void completed(bool canceled) { completed(true, canceled); } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_LINUXAIO_FILE + +#endif // !STXXL_IO_LINUXAIO_REQUEST_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mem_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mem_file.h new file mode 100644 index 0000000000..95a72dc829 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mem_file.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * include/stxxl/bits/io/mem_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_MEM_FILE_HEADER +#define STXXL_IO_MEM_FILE_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Implementation of file based on new[] and memcpy. +class mem_file : public disk_queued_file +{ + //! pointer to memory area of "file" + char* m_ptr; + + //! size of memory area + offset_type m_size; + + //! sequentialize function calls + mutex m_mutex; + +public: + //! constructs file object. + mem_file( + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID) + : file(device_id), + disk_queued_file(queue_id, allocator_id), + m_ptr(NULL), m_size(0) + { } + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + ~mem_file(); + offset_type size(); + void set_size(offset_type newsize); + void lock(); + void discard(offset_type offset, offset_type size); + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_MEM_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mmap_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mmap_file.h new file mode 100644 index 0000000000..04bdfdaceb --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/mmap_file.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * include/stxxl/bits/io/mmap_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * + * 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 STXXL_IO_MMAP_FILE_HEADER +#define STXXL_IO_MMAP_FILE_HEADER + +#include + +#if STXXL_HAVE_MMAP_FILE + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Implementation of memory mapped access file. +class mmap_file : public ufs_file_base, public disk_queued_file +{ +public: + //! Constructs file object. + //! \param filename path of file + //! \param mode open mode, see \c stxxl::file::open_modes + //! \param queue_id disk queue identifier + //! \param allocator_id linked disk_allocator + //! \param device_id physical device identifier + inline mmap_file( + const std::string& filename, + int mode, + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID) + : file(device_id), + ufs_file_base(filename, mode), + disk_queued_file(queue_id, allocator_id) + { } + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_MMAP_FILE + +#endif // !STXXL_IO_MMAP_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request.h new file mode 100644 index 0000000000..44ac8e88b3 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request.h @@ -0,0 +1,120 @@ +/*************************************************************************** + * include/stxxl/bits/io/request.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2013-2014 Timo Bingmann + * + * 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 STXXL_IO_REQUEST_HEADER +#define STXXL_IO_REQUEST_HEADER + +#include + +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +#define STXXL_BLOCK_ALIGN 4096 + +class file; + +//! Request object encapsulating basic properties like file and offset. +class request : virtual public request_interface, public atomic_counted_object +{ + friend class linuxaio_queue; + +protected: + completion_handler m_on_complete; + compat_unique_ptr::result m_error; + +protected: + file* m_file; + void* m_buffer; + offset_type m_offset; + size_type m_bytes; + request_type m_type; + +public: + request(const completion_handler& on_compl, + file* file, + void* buffer, + offset_type offset, + size_type bytes, + request_type type); + + virtual ~request() noexcept(false); + + file * get_file() const { return m_file; } + void * get_buffer() const { return m_buffer; } + offset_type get_offset() const { return m_offset; } + size_type get_size() const { return m_bytes; } + request_type get_type() const { return m_type; } + + void check_alignment() const; + + std::ostream & print(std::ostream& out) const; + + //! Inform the request object that an error occurred during the I/O + //! execution. + void error_occured(const char* msg) + { + m_error.reset(new stxxl::io_error(msg)); + } + + //! Inform the request object that an error occurred during the I/O + //! execution. + void error_occured(const std::string& msg) + { + m_error.reset(new stxxl::io_error(msg)); + } + + //! Rises an exception if there were error with the I/O. + void check_errors() + { + if (m_error.get()) + throw *(m_error.get()); + } + + virtual const char * io_type() const; + +protected: + void check_nref(bool after = false) + { + if (get_reference_count() < 2) + check_nref_failed(after); + } + +private: + void check_nref_failed(bool after); +}; + +inline std::ostream& operator << (std::ostream& out, const request& req) +{ + return req.print(out); +} + +//! A reference counting pointer for \c request. +typedef counting_ptr request_ptr; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_interface.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_interface.h new file mode 100644 index 0000000000..7bd47f0b06 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_interface.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_interface.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008, 2009, 2011 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * + * 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 STXXL_IO_REQUEST_INTERFACE_HEADER +#define STXXL_IO_REQUEST_INTERFACE_HEADER + +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +class onoff_switch; + +//! Functional interface of a request. +//! +//! Since all library I/O operations are asynchronous, +//! one needs to keep track of their status: +//! e.g. whether an I/O operation completed or not. +class request_interface : private noncopyable +{ +public: + typedef stxxl::external_size_type offset_type; + typedef stxxl::internal_size_type size_type; + enum request_type { READ, WRITE }; + +public: + virtual bool add_waiter(onoff_switch* sw) = 0; + virtual void delete_waiter(onoff_switch* sw) = 0; + +protected: + virtual void notify_waiters() = 0; + +protected: + virtual void completed(bool canceled) = 0; + +public: + //! Suspends calling thread until completion of the request. + virtual void wait(bool measure_time = true) = 0; + + //! Cancel a request. + //! + //! The request is canceled unless already being processed. + //! However, cancelation cannot be guaranteed. + //! Canceled requests must still be waited for in order to ensure correct operation. + //! If the request was canceled successfully, the completion handler will not be called. + //! \return \c true iff the request was canceled successfully + virtual bool cancel() = 0; + + //! Polls the status of the request. + //! \return \c true if request is completed, otherwise \c false + virtual bool poll() = 0; + + //! Identifies the type of I/O implementation. + //! \return pointer to null terminated string of characters, containing the name of I/O implementation + virtual const char * io_type() const = 0; + + //! Dumps properties of a request. + virtual std::ostream & print(std::ostream& out) const = 0; + + virtual ~request_interface() noexcept(false) + { } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_INTERFACE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_operations.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_operations.h new file mode 100644 index 0000000000..4d0482815a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_operations.h @@ -0,0 +1,159 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_operations.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008, 2009 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * + * 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 STXXL_IO_REQUEST_OPERATIONS_HEADER +#define STXXL_IO_REQUEST_OPERATIONS_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Collection of functions to track statuses of a number of requests. + +//! Suspends calling thread until \b all given requests are completed. +//! \param reqs_begin begin of request sequence to wait for +//! \param reqs_end end of request sequence to wait for +template +void wait_all(RequestIterator reqs_begin, RequestIterator reqs_end) +{ + for ( ; reqs_begin != reqs_end; ++reqs_begin) + (request_ptr(*reqs_begin))->wait(); +} + +//! Suspends calling thread until \b all given requests are completed. +//! \param req_array array of request_ptr objects +//! \param count size of req_array +inline void wait_all(request_ptr req_array[], size_t count) +{ + wait_all(req_array, req_array + count); +} + +//! Cancel requests. +//! The specified requests are canceled unless already being processed. +//! However, cancelation cannot be guaranteed. +//! Cancelled requests must still be waited for in order to ensure correct +//! operation. +//! \param reqs_begin begin of request sequence +//! \param reqs_end end of request sequence +//! \return number of request canceled +template +typename std::iterator_traits::difference_type +cancel_all(RequestIterator reqs_begin, RequestIterator reqs_end) +{ + typename std::iterator_traits::difference_type num_canceled = 0; + while (reqs_begin != reqs_end) + { + if ((request_ptr(*reqs_begin))->cancel()) + ++num_canceled; + ++reqs_begin; + } + return num_canceled; +} + +//! Polls requests. +//! \param reqs_begin begin of request sequence to poll +//! \param reqs_end end of request sequence to poll +//! \return \c true if any of requests is completed, then index contains valid value, otherwise \c false +template +RequestIterator poll_any(RequestIterator reqs_begin, RequestIterator reqs_end) +{ + while (reqs_begin != reqs_end) + { + if ((request_ptr(*reqs_begin))->poll()) + return reqs_begin; + + ++reqs_begin; + } + return reqs_end; +} + +//! Polls requests. +//! \param req_array array of request_ptr objects +//! \param count size of req_array +//! \param index contains index of the \b first completed request if any +//! \return \c true if any of requests is completed, then index contains valid value, otherwise \c false +inline bool poll_any(request_ptr req_array[], size_t count, size_t& index) +{ + request_ptr* res = poll_any(req_array, req_array + count); + index = res - req_array; + return res != (req_array + count); +} + +//! Suspends calling thread until \b any of requests is completed. +//! \param reqs_begin begin of request sequence to wait for +//! \param reqs_end end of request sequence to wait for +//! \return index in req_array pointing to the \b first completed request +template +RequestIterator wait_any(RequestIterator reqs_begin, RequestIterator reqs_end) +{ + stats::scoped_wait_timer wait_timer(stats::WAIT_OP_ANY); + + onoff_switch sw; + + RequestIterator cur = reqs_begin, result = reqs_end; + + for ( ; cur != reqs_end; cur++) + { + if ((request_ptr(*cur))->add_waiter(&sw)) + { + // request is already done, no waiter was added to the request + result = cur; + + if (cur != reqs_begin) + { + while (--cur != reqs_begin) + (request_ptr(*cur))->delete_waiter(&sw); + + (request_ptr(*cur))->delete_waiter(&sw); + } + + (request_ptr(*result))->check_errors(); + + return result; + } + } + + sw.wait_for_on(); + + for (cur = reqs_begin; cur != reqs_end; cur++) + { + (request_ptr(*cur))->delete_waiter(&sw); + if (result == reqs_end && (request_ptr(*cur))->poll()) + result = cur; + } + + return result; +} + +//! Suspends calling thread until \b any of requests is completed. +//! \param req_array array of \c request_ptr objects +//! \param count size of req_array +//! \return index in req_array pointing to the \b first completed request +inline size_t wait_any(request_ptr req_array[], size_t count) +{ + return wait_any(req_array, req_array + count) - req_array; +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_OPERATIONS_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue.h new file mode 100644 index 0000000000..e23b9f05cb --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_queue.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2009 Andreas Beckmann + * Copyright (C) 2011 Johannes Singler + * + * 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 STXXL_IO_REQUEST_QUEUE_HEADER +#define STXXL_IO_REQUEST_QUEUE_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Interface of a request_queue to which requests can be added and canceled. +class request_queue : private noncopyable +{ +public: + enum priority_op { READ, WRITE, NONE }; + +public: + virtual void add_request(request_ptr& req) = 0; + virtual bool cancel_request(request_ptr& req) = 0; + virtual ~request_queue() noexcept(false) { } + virtual void set_priority_op(priority_op p) { STXXL_UNUSED(p); } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_QUEUE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_1q.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_1q.h new file mode 100644 index 0000000000..f1854a4dd9 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_1q.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_queue_impl_1q.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008-2009 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * + * 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 STXXL_IO_REQUEST_QUEUE_IMPL_1Q_HEADER +#define STXXL_IO_REQUEST_QUEUE_IMPL_1Q_HEADER + +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Implementation of a local request queue having only one queue for both read +//! and write requests, thus having only one thread. +class request_queue_impl_1q : public request_queue_impl_worker +{ +private: + typedef request_queue_impl_1q self; + typedef std::list queue_type; + + mutex m_queue_mutex; + queue_type m_queue; + + state m_thread_state; + thread_type m_thread; + semaphore m_sem; + + static const priority_op m_priority_op = WRITE; + + static void * worker(void* arg); + +public: + // \param n max number of requests simultaneously submitted to disk + request_queue_impl_1q(int n = 1); + + // in a multi-threaded setup this does not work as intended + // also there were race conditions possible + // and actually an old value was never restored once a new one was set ... + // so just disable it and all it's nice implications + void set_priority_op(priority_op op) + { + //_priority_op = op; + STXXL_UNUSED(op); + } + void add_request(request_ptr& req); + bool cancel_request(request_ptr& req); + ~request_queue_impl_1q(); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_QUEUE_IMPL_1Q_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_qwqr.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_qwqr.h new file mode 100644 index 0000000000..2ab9cd9102 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_qwqr.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_queue_impl_qwqr.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008, 2009 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_IO_REQUEST_QUEUE_IMPL_QWQR_HEADER +#define STXXL_IO_REQUEST_QUEUE_IMPL_QWQR_HEADER + +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Implementation of a local request queue having two queues, one for read and +//! one for write requests, thus having two threads. This is the default +//! implementation. +class request_queue_impl_qwqr : public request_queue_impl_worker +{ +private: + typedef request_queue_impl_qwqr self; + typedef std::list queue_type; + + mutex m_write_mutex; + mutex m_read_mutex; + queue_type m_write_queue; + queue_type m_read_queue; + + state m_thread_state; + thread_type m_thread; + semaphore m_sem; + + static const priority_op m_priority_op = WRITE; + + static void * worker(void* arg); + +public: + // \param n max number of requests simultaneously submitted to disk + request_queue_impl_qwqr(int n = 1); + + // in a multi-threaded setup this does not work as intended + // also there were race conditions possible + // and actually an old value was never restored once a new one was set ... + // so just disable it and all it's nice implications + void set_priority_op(priority_op op) + { + //_priority_op = op; + STXXL_UNUSED(op); + } + void add_request(request_ptr& req); + bool cancel_request(request_ptr& req); + ~request_queue_impl_qwqr(); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_QUEUE_IMPL_QWQR_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_worker.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_worker.h new file mode 100644 index 0000000000..9c3fe5f884 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_queue_impl_worker.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_queue_impl_worker.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008, 2009 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_IO_REQUEST_QUEUE_IMPL_WORKER_HEADER +#define STXXL_IO_REQUEST_QUEUE_IMPL_WORKER_HEADER + +#include + +#if STXXL_STD_THREADS + #include +#elif STXXL_BOOST_THREADS + #include +#elif STXXL_POSIX_THREADS + #include +#else + #error "Thread implementation not detected." +#endif + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Implementation of request queue worker threads. Worker threads can be +//! started by start_thread and stopped with stop_thread. The queue state is +//! checked before termination and updated afterwards. +class request_queue_impl_worker : public request_queue +{ +protected: + enum thread_state { NOT_RUNNING, RUNNING, TERMINATING, TERMINATED }; + +#if STXXL_STD_THREADS + typedef std::thread* thread_type; +#elif STXXL_BOOST_THREADS + typedef boost::thread* thread_type; +#else + typedef pthread_t thread_type; +#endif + +protected: + void start_thread(void* (*worker)(void*), void* arg, thread_type& t, state& s); + void stop_thread(thread_type& t, state& s, semaphore& sem); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_QUEUE_IMPL_WORKER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_state.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_state.h new file mode 100644 index 0000000000..0b6f78f1f3 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_state.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_with_state.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * + * 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 STXXL_IO_REQUEST_WITH_STATE_HEADER +#define STXXL_IO_REQUEST_WITH_STATE_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Request with completion state. +class request_with_state : public request_with_waiters +{ +protected: + //! states of request + //! OP - operating, DONE - request served, READY2DIE - can be destroyed + enum request_state { OP = 0, DONE = 1, READY2DIE = 2 }; + + state m_state; + +protected: + request_with_state( + const completion_handler& on_cmpl, + file* f, + void* buf, + offset_type off, + size_type b, + request_type t) + : request_with_waiters(on_cmpl, f, buf, off, b, t), + m_state(OP) + { } + +public: + virtual ~request_with_state(); + void wait(bool measure_time = true); + bool poll(); + bool cancel(); + +protected: + void completed(bool canceled); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_WITH_STATE_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_waiters.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_waiters.h new file mode 100644 index 0000000000..56f284e516 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/request_with_waiters.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * include/stxxl/bits/io/request_with_waiters.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_IO_REQUEST_WITH_WAITERS_HEADER +#define STXXL_IO_REQUEST_WITH_WAITERS_HEADER + +#include + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Request that is aware of threads waiting for it to complete. +class request_with_waiters : public request +{ + mutex m_waiters_mutex; + std::set m_waiters; + +protected: + bool add_waiter(onoff_switch* sw); + void delete_waiter(onoff_switch* sw); + void notify_waiters(); + + //! returns number of waiters + size_t num_waiters(); + +public: + request_with_waiters( + const completion_handler& on_cmpl, + file* f, + void* buf, + offset_type off, + size_type b, + request_type t) + : request(on_cmpl, f, buf, off, b, t) + { } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_REQUEST_WITH_WAITERS_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/serving_request.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/serving_request.h new file mode 100644 index 0000000000..f0bfcc9013 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/serving_request.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * include/stxxl/bits/io/serving_request.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * + * 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 STXXL_IO_SERVING_REQUEST_HEADER +#define STXXL_IO_SERVING_REQUEST_HEADER + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup reqlayer +//! \{ + +//! Request which serves an I/O by calling the synchronous routine of the file. +class serving_request : public request_with_state +{ + template + friend class fileperblock_file; + friend class request_queue_impl_qwqr; + friend class request_queue_impl_1q; + +public: + serving_request( + const completion_handler& on_cmpl, + file* f, + void* buf, + offset_type off, + size_type b, + request_type t); + +protected: + virtual void serve(); + +public: + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_SERVING_REQUEST_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/simdisk_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/simdisk_file.h new file mode 100644 index 0000000000..b9ddc506d4 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/simdisk_file.h @@ -0,0 +1,150 @@ +/*************************************************************************** + * include/stxxl/bits/io/simdisk_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2003 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * + * 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 STXXL_IO_SIMDISK_FILE_HEADER +#define STXXL_IO_SIMDISK_FILE_HEADER + +#include + +#ifndef STXXL_HAVE_SIMDISK_FILE +// use mmap call +#define STXXL_HAVE_SIMDISK_FILE STXXL_HAVE_MMAP_FILE +#endif + +#if STXXL_HAVE_SIMDISK_FILE + +#include +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \weakgroup fileimpl +//! \{ + +class simdisk_geometry : private noncopyable +{ + struct Zone + { + // manufactured data +#if 0 + int last_cyl; + int sect_per_track; +#endif + // derived data + int first_sector; + int sectors; + double sustained_data_rate; // in MiB/s + inline Zone(int _first_sector) : first_sector(_first_sector) + { } // constructor for zone search + + inline Zone( +#if 0 + int _last_cyl, + int _sect_per_track, +#endif + int _first_sector, + int _sectors, + double _rate) + : +#if 0 + last_cyl(_last_cyl), + sect_per_track(_sect_per_track), +#endif + first_sector(_first_sector), + sectors(_sectors), + sustained_data_rate(_rate) + { } + }; + struct ZoneCmp + { + inline bool operator () (const Zone& a, const Zone& b) const + { + return a.first_sector < b.first_sector; + } + }; + +protected: + int nsurfaces; + int bytes_per_sector; + double cmd_ovh; // in s + double seek_time; // in s + double rot_latency; // in s + double head_switch_time; // in s + double cyl_switch_time; // in s + double revolution_time; // in s + double interface_speed; // in byte/s + std::set zones; + + void add_zone(int& first_cyl, int last_cyl, + int sec_per_track, int& first_sect); + +public: + inline simdisk_geometry() + { } + double get_delay(file::offset_type offset, file::size_type size); // returns delay in s + + inline ~simdisk_geometry() + { } + + static const double s_average_speed; +}; + +class IC35L080AVVA07 : public simdisk_geometry // IBM series 120GXP +{ +public: + IC35L080AVVA07(); +}; + +//! Implementation of disk emulation. +//! \remark It is emulation of IBM IC35L080AVVA07 disk's timings +class sim_disk_file : public ufs_file_base, public disk_queued_file, public IC35L080AVVA07 +{ +public: + //! Constructs file object. + //! \param filename path of file + //! \attention filename must be resided at memory disk partition + //! \param mode open mode, see \c stxxl::file::open_modes + //! \param queue_id disk queue identifier + //! \param allocator_id linked disk_allocator + //! \param device_id physical device identifier + inline sim_disk_file( + const std::string& filename, + int mode, + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID) + : file(device_id), + ufs_file_base(filename, mode), + disk_queued_file(queue_id, allocator_id) + { + std::cout << "Please, make sure that '" << filename << + "' is resided on swap memory partition!" << + std::endl; + } + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + void set_size(offset_type newsize); + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_SIMDISK_FILE + +#endif // !STXXL_IO_SIMDISK_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/syscall_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/syscall_file.h new file mode 100644 index 0000000000..9dd9c8b8c0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/syscall_file.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * include/stxxl/bits/io/syscall_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_IO_SYSCALL_FILE_HEADER +#define STXXL_IO_SYSCALL_FILE_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Implementation of file based on UNIX syscalls. +class syscall_file : public ufs_file_base, public disk_queued_file +{ +public: + //! Constructs file object. + //! \param filename path of file + //! \param mode open mode, see \c stxxl::file::open_modes + //! \param queue_id disk queue identifier + //! \param allocator_id linked disk_allocator + //! \param device_id physical device identifier + syscall_file( + const std::string& filename, + int mode, + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID) + : file(device_id), + ufs_file_base(filename, mode), + disk_queued_file(queue_id, allocator_id) + { } + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_SYSCALL_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/ufs_file_base.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/ufs_file_base.h new file mode 100644 index 0000000000..962d5dd41a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/ufs_file_base.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * include/stxxl/bits/io/ufs_file_base.h + * + * UNIX file system file base + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_IO_UFS_FILE_BASE_HEADER +#define STXXL_IO_UFS_FILE_BASE_HEADER + +#include +#include +#include + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Base for UNIX file system implementations. +class ufs_file_base : public virtual file +{ +protected: + mutex fd_mutex; // sequentialize function calls involving file_des + int file_des; // file descriptor + int m_mode; // open mode + const std::string filename; + bool m_is_device; //!< is special device node + ufs_file_base(const std::string& filename, int mode); + void _after_open(); + offset_type _size(); + void _set_size(offset_type newsize); + void close(); + +public: + ~ufs_file_base(); + offset_type size(); + void set_size(offset_type newsize); + void lock(); + const char * io_type() const; + void close_remove(); + //! unlink file without closing it. + void unlink(); + //! return true if file is special device node + bool is_device() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_IO_UFS_FILE_BASE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wbtl_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wbtl_file.h new file mode 100644 index 0000000000..933a21630d --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wbtl_file.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * include/stxxl/bits/io/wbtl_file.h + * + * a write-buffered-translation-layer pseudo file + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008-2009 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * + * 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 STXXL_IO_WBTL_FILE_HEADER +#define STXXL_IO_WBTL_FILE_HEADER + +#ifndef STXXL_HAVE_WBTL_FILE +#define STXXL_HAVE_WBTL_FILE 1 +#endif + +#if STXXL_HAVE_WBTL_FILE + +#include + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Implementation of file based on buffered writes and block remapping via a +//! translation layer. +class wbtl_file : public disk_queued_file +{ + typedef std::pair place; + typedef std::map sortseq; + typedef std::map place_map; + + // the physical disk used as backend + file* storage; + offset_type sz; + size_type write_block_size; + + mutex mapping_mutex; + // logical to physical address translation + sortseq address_mapping; + // physical to (logical address, size) translation + place_map reverse_mapping; + // list of free (physical) regions + sortseq free_space; + offset_type free_bytes; + + // the write buffers: + // write_buffer[curbuf] is the current write buffer + // write_buffer[1-curbuf] is the previous write buffer + // buffer_address if the start offset on the backend file + // curpos is the next writing position in write_buffer[curbuf] + mutex buffer_mutex; + char* write_buffer[2]; + offset_type buffer_address[2]; + int curbuf; + size_type curpos; + request_ptr backend_request; + + struct FirstFit : public std::binary_function + { + bool operator () ( + const place& entry, + const offset_type size) const + { + return (entry.second >= size); + } + }; + +public: + //! Constructs file object. + //! param backend_file file object used as storage backend, will be deleted in ~wbtl_file() + wbtl_file( + file* backend_file, + size_type write_buffer_size, + int write_buffers = 2, + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR); + ~wbtl_file(); + offset_type size(); + void set_size(offset_type newsize); + void lock(); + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + void discard(offset_type offset, offset_type size); + const char * io_type() const; + +private: + void _add_free_region(offset_type offset, offset_type size); + +protected: + void sread(void* buffer, offset_type offset, size_type bytes); + void swrite(void* buffer, offset_type offset, size_type bytes); + offset_type get_next_write_block(); + void check_corruption(offset_type region_pos, offset_type region_size, + sortseq::iterator pred, sortseq::iterator succ); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_WBTL_FILE + +#endif // !STXXL_IO_WBTL_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wfs_file_base.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wfs_file_base.h new file mode 100644 index 0000000000..483e640590 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wfs_file_base.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * include/stxxl/bits/io/wfs_file_base.h + * + * Windows file system file base + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2005 Roman Dementiev + * Copyright (C) 2008, 2010 Andreas Beckmann + * Copyright (C) 2009, 2010 Johannes Singler + * + * 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 STXXL_IO_WFS_FILE_BASE_HEADER +#define STXXL_IO_WFS_FILE_BASE_HEADER + +#include + +#if STXXL_WINDOWS + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Base for Windows file system implementations. +class wfs_file_base : public virtual file +{ +protected: + typedef void* HANDLE; + + mutex fd_mutex; // sequentialize function calls involving file_des + HANDLE file_des; // file descriptor + int mode_; // open mode + const std::string filename; + offset_type bytes_per_sector; + bool locked; + wfs_file_base(const std::string& filename, int mode); + offset_type _size(); + void close(); + +public: + ~wfs_file_base(); + offset_type size(); + void set_size(offset_type newsize); + void lock(); + const char * io_type() const; + void close_remove(); +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // STXXL_WINDOWS + +#endif // !STXXL_IO_WFS_FILE_BASE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wincall_file.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wincall_file.h new file mode 100644 index 0000000000..e2097af3e9 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/io/wincall_file.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * include/stxxl/bits/io/wincall_file.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2005-2006 Roman Dementiev + * Copyright (C) 2008 Andreas Beckmann + * Copyright (C) 2009-2010 Johannes Singler + * + * 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 STXXL_IO_WINCALL_FILE_HEADER +#define STXXL_IO_WINCALL_FILE_HEADER + +#include + +#ifndef STXXL_HAVE_WINCALL_FILE +#if STXXL_WINDOWS + #define STXXL_HAVE_WINCALL_FILE 1 +#else + #define STXXL_HAVE_WINCALL_FILE 0 +#endif +#endif + +#if STXXL_HAVE_WINCALL_FILE + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup fileimpl +//! \{ + +//! Implementation of file based on Windows native I/O calls. +class wincall_file : public wfs_file_base, public disk_queued_file +{ +public: + //! Constructs file object. + //! \param filename path of file + //! \param mode open mode, see \c stxxl::file::open_modes + //! \param queue_id disk queue identifier + //! \param allocator_id linked disk_allocator + //! \param device_id physical device identifier + wincall_file( + const std::string& filename, + int mode, + int queue_id = DEFAULT_QUEUE, + int allocator_id = NO_ALLOCATOR, + unsigned int device_id = DEFAULT_DEVICE_ID) + : file(device_id), + wfs_file_base(filename, mode), + disk_queued_file(queue_id, allocator_id) + { } + void serve(void* buffer, offset_type offset, size_type bytes, + request::request_type type); + const char * io_type() const; +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // #if STXXL_HAVE_WINCALL_FILE + +#endif // !STXXL_IO_WINCALL_FILE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/libstxxl.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/libstxxl.h new file mode 100644 index 0000000000..5a5d2b2b62 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/libstxxl.h @@ -0,0 +1,25 @@ +/*************************************************************************** + * include/stxxl/bits/libstxxl.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2011 Andreas Beckmann + * + * 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 STXXL_LIBSTXXL_HEADER +#define STXXL_LIBSTXXL_HEADER + +#include + +#if STXXL_MSVC + #ifndef STXXL_LIBNAME + #define STXXL_LIBNAME "stxxl" + #endif +//-tb #pragma comment (lib, "lib" STXXL_LIBNAME ".lib") +#endif + +#endif // !STXXL_LIBSTXXL_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/adaptor.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/adaptor.h new file mode 100644 index 0000000000..a7834be6ab --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/adaptor.h @@ -0,0 +1,659 @@ +/*************************************************************************** + * include/stxxl/bits/mng/adaptor.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2003 Roman Dementiev + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2009-2010 Andreas Beckmann + * + * 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 STXXL_MNG_ADAPTOR_HEADER +#define STXXL_MNG_ADAPTOR_HEADER + +#include + +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup mnglayer +//! +//! \{ + +template +class blocked_index +{ + unsigned_type pos; + unsigned_type block; + unsigned_type offset; + + //! \invariant block * modulo + offset = pos + + void set(unsigned_type pos) + { + this->pos = pos; + block = pos / modulo; + offset = pos % modulo; + } + +public: + blocked_index() + { + set(0); + } + + blocked_index(unsigned_type pos) + { + set(pos); + } + + blocked_index(unsigned_type block, unsigned_type offset) + { + this->block = block; + this->offset = offset; + pos = block * modulo + offset; + } + + void operator = (unsigned_type pos) + { + set(pos); + } + + //pre-increment operator + blocked_index& operator ++ () + { + ++pos; + ++offset; + if (offset == modulo) + { + offset = 0; + ++block; + } + return *this; + } + + //post-increment operator + blocked_index operator ++ (int) + { + blocked_index former(*this); + operator ++ (); + return former; + } + + //pre-increment operator + blocked_index& operator -- () + { + --pos; + if (offset == 0) + { + offset = modulo; + --block; + } + --offset; + return *this; + } + + //post-increment operator + blocked_index operator -- (int) + { + blocked_index former(*this); + operator -- (); + return former; + } + + blocked_index& operator += (unsigned_type addend) + { + set(pos + addend); + return *this; + } + + blocked_index& operator >>= (unsigned_type shift) + { + set(pos >> shift); + return *this; + } + + operator unsigned_type () const + { + return pos; + } + + const unsigned_type & get_block() const + { + return block; + } + + const unsigned_type & get_offset() const + { + return offset; + } +}; + +#define STXXL_ADAPTOR_ARITHMETICS(pos) \ + bool operator == (const self_type &a) const \ + { \ + return (a.pos == pos); \ + } \ + bool operator != (const self_type& a) const \ + { \ + return (a.pos != pos); \ + } \ + bool operator < (const self_type& a) const \ + { \ + return (pos < a.pos); \ + } \ + bool operator > (const self_type& a) const \ + { \ + return (pos > a.pos); \ + } \ + bool operator <= (const self_type& a) const \ + { \ + return (pos <= a.pos); \ + } \ + bool operator >= (const self_type& a) const \ + { \ + return (pos >= a.pos); \ + } \ + self_type operator + (pos_type off) const \ + { \ + return self_type(array, pos + off); \ + } \ + self_type operator - (pos_type off) const \ + { \ + return self_type(array, pos - off); \ + } \ + self_type& operator ++ () \ + { \ + pos++; \ + return *this; \ + } \ + self_type operator ++ (int) \ + { \ + self_type tmp = *this; \ + pos++; \ + return tmp; \ + } \ + self_type& operator -- () \ + { \ + pos--; \ + return *this; \ + } \ + self_type operator -- (int) \ + { \ + self_type tmp = *this; \ + pos--; \ + return tmp; \ + } \ + pos_type operator - (const self_type& a) const \ + { \ + return pos - a.pos; \ + } \ + self_type& operator -= (pos_type off) \ + { \ + pos -= off; \ + return *this; \ + } \ + self_type& operator += (pos_type off) \ + { \ + pos += off; \ + return *this; \ + } + +template +struct two2one_dim_array_adapter_base + : public std::iterator +{ + typedef OneDimArrayType one_dim_array_type; + typedef DataType data_type; + typedef PosType pos_type; + + typedef two2one_dim_array_adapter_base self_type; + + one_dim_array_type* array; + pos_type pos; + + two2one_dim_array_adapter_base() + { } + + two2one_dim_array_adapter_base(one_dim_array_type* a, pos_type p) + : array(a), pos(p) + { } + two2one_dim_array_adapter_base(const two2one_dim_array_adapter_base& a) + : array(a.array), pos(a.pos) + { } + + STXXL_ADAPTOR_ARITHMETICS(pos) +}; + +////////////////////////////// + +#define BLOCK_ADAPTOR_OPERATORS(two_to_one_dim_array_adaptor_base) \ + \ + template \ + inline two_to_one_dim_array_adaptor_base& operator ++ ( \ + two_to_one_dim_array_adaptor_base& a) \ + { \ + a.pos++; \ + return a; \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base operator ++ ( \ + two_to_one_dim_array_adaptor_base& a, int) \ + { \ + two_to_one_dim_array_adaptor_base tmp = a; \ + a.pos++; \ + return tmp; \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base& operator -- ( \ + two_to_one_dim_array_adaptor_base& a) \ + { \ + a.pos--; \ + return a; \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base operator -- ( \ + two_to_one_dim_array_adaptor_base& a, int) \ + { \ + two_to_one_dim_array_adaptor_base tmp = a; \ + a.pos--; \ + return tmp; \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base& operator -= ( \ + two_to_one_dim_array_adaptor_base& a, \ + typename two_to_one_dim_array_adaptor_base::_pos_type off) \ + { \ + a.pos -= off; \ + return a; \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base& operator += ( \ + two_to_one_dim_array_adaptor_base& a, \ + typename two_to_one_dim_array_adaptor_base::_pos_type off) \ + { \ + a.pos += off; \ + return a; \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base operator + ( \ + const two_to_one_dim_array_adaptor_base& a, \ + typename two_to_one_dim_array_adaptor_base::_pos_type off) \ + { \ + return two_to_one_dim_array_adaptor_base(a.array, a.pos + off); \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base operator + ( \ + typename two_to_one_dim_array_adaptor_base::_pos_type off, \ + const two_to_one_dim_array_adaptor_base& a) \ + { \ + return two_to_one_dim_array_adaptor_base(a.array, a.pos + off); \ + } \ + \ + template \ + inline two_to_one_dim_array_adaptor_base operator - ( \ + const two_to_one_dim_array_adaptor_base& a, \ + typename two_to_one_dim_array_adaptor_base::_pos_type off) \ + { \ + return two_to_one_dim_array_adaptor_base(a.array, a.pos - off); \ + } + +#if 0 +////////////////////////// +template > +struct two2one_dim_array_row_adapter + : public two2one_dim_array_adapter_base +{ + typedef OneDimArrayType one_dim_array_type; + typedef DataType data_type; + typedef DimSize dim_type; + typedef PosType pos_type; + + typedef two2one_dim_array_row_adapter self_type; + + typedef two2one_dim_array_adapter_base base_type; + using base_type::array; + using base_type::pos; + + two2one_dim_array_row_adapter() + { } + two2one_dim_array_row_adapter(one_dim_array_type* a, pos_type p) + : two2one_dim_array_adapter_base(a, p) + { } + two2one_dim_array_row_adapter(const two2one_dim_array_row_adapter& a) + : two2one_dim_array_adapter_base(a) + { } + + data_type& operator * () + { + return array[(pos).get_block()][(pos).get_offset()]; + } + + data_type* operator -> () const + { + return &(array[(pos).get_block()][(pos).get_offset()]); + } + + data_type& operator [] (pos_type n) + { + n += pos; + return array[(n) / dim_size][(n) % dim_size]; + } + + const data_type& operator [] (pos_type n) const + { + n += pos; + return array[(n) / dim_size][(n) % dim_size]; + } + STXXL_ADAPTOR_ARITHMETICS(pos) +}; + +template > +struct two2one_dim_array_column_adapter + : public two2one_dim_array_adapter_base +{ + typedef two2one_dim_array_column_adapter self_type; + + using two2one_dim_array_adapter_base::pos; + using two2one_dim_array_adapter_base::array; + + two2one_dim_array_column_adapter(one_dim_array_type* a, pos_type p) + : two2one_dim_array_adapter_base(a, p) + { } + two2one_dim_array_column_adapter(const self_type& a) + : two2one_dim_array_adapter_base(a) + { } + + data_type& operator * () + { + return array[(pos).get_offset()][(pos).get_block()]; + } + + data_type* operator -> () const + { + return &(array[(pos).get_offset()][(pos).get_block()]); + } + + const data_type& operator [] (pos_type n) const + { + n += pos; + return array[(n) % dim_size][(n) / dim_size]; + } + + data_type& operator [] (pos_type n) + { + n += pos; + return array[(n) % dim_size][(n) / dim_size]; + } + STXXL_ADAPTOR_ARITHMETICS(pos) +}; +#endif + +template +class array_of_sequences_iterator + : public std::iterator +{ +public: + typedef ArrayType array_type; + typedef ValueType value_type; + +protected: + unsigned_type pos; + unsigned_type offset; + array_type* arrays; + array_type* base; + value_type* base_element; + + //! \invariant block * modulo + offset = pos + + void set(unsigned_type pos) + { + this->pos = pos; + offset = pos % modulo; + base = arrays + pos / modulo; + base_element = base->elem; + } + +public: + array_of_sequences_iterator() + { + this->arrays = NULL; + set(0); + } + + array_of_sequences_iterator(array_type* arrays) + { + this->arrays = arrays; + set(0); + } + + array_of_sequences_iterator(array_type* arrays, unsigned_type pos) + { + this->arrays = arrays; + set(pos); + } + + void operator = (unsigned_type pos) + { + set(pos); + } + + //pre-increment operator + array_of_sequences_iterator& operator ++ () + { + ++pos; + ++offset; + if (offset == modulo) + { + offset = 0; + ++base; + base_element = base->elem; + } + return *this; + } + + //post-increment operator + array_of_sequences_iterator operator ++ (int) + { + array_of_sequences_iterator former(*this); + operator ++ (); + return former; + } + + //pre-increment operator + array_of_sequences_iterator& operator -- () + { + --pos; + if (offset == 0) + { + offset = modulo; + --base; + base_element = base->elem; + } + --offset; + return *this; + } + + //post-increment operator + array_of_sequences_iterator operator -- (int) + { + array_of_sequences_iterator former(*this); + operator -- (); + return former; + } + + array_of_sequences_iterator& operator += (unsigned_type addend) + { + set(pos + addend); + return *this; + } + + array_of_sequences_iterator& operator -= (unsigned_type addend) + { + set(pos - addend); + return *this; + } + + array_of_sequences_iterator operator + (unsigned_type addend) const + { + return array_of_sequences_iterator(arrays, pos + addend); + } + + array_of_sequences_iterator operator - (unsigned_type subtrahend) const + { + return array_of_sequences_iterator(arrays, pos - subtrahend); + } + + unsigned_type operator - (const array_of_sequences_iterator& subtrahend) const + { + return pos - subtrahend.pos; + } + + bool operator == (const array_of_sequences_iterator& aoai) const + { + return pos == aoai.pos; + } + + bool operator != (const array_of_sequences_iterator& aoai) const + { + return pos != aoai.pos; + } + + bool operator < (const array_of_sequences_iterator& aoai) const + { + return pos < aoai.pos; + } + + bool operator <= (const array_of_sequences_iterator& aoai) const + { + return pos <= aoai.pos; + } + + bool operator > (const array_of_sequences_iterator& aoai) const + { + return pos > aoai.pos; + } + + bool operator >= (const array_of_sequences_iterator& aoai) const + { + return pos >= aoai.pos; + } + + const value_type& operator * () const + { + return base_element[offset]; + } + + value_type& operator * () + { + return base_element[offset]; + } + + const value_type& operator -> () const + { + return &(base_element[offset]); + } + + value_type& operator -> () + { + return &(base_element[offset]); + } + + const value_type& operator [] (unsigned_type index) const + { + return arrays[index / modulo][index % modulo]; + } + + value_type& operator [] (unsigned_type index) + { + return arrays[index / modulo][index % modulo]; + } +}; + +namespace helper { + +template +class element_iterator_generator +{ }; + +// default case for blocks with fillers or other data: use array_of_sequences_iterator +template +class element_iterator_generator +{ + typedef BlockType block_type; + typedef typename block_type::value_type value_type; + + typedef SizeType size_type; + +public: + typedef array_of_sequences_iterator iterator; + + iterator operator () (block_type* blocks, SizeType offset) const + { + return iterator(blocks, offset); + } +}; + +// special case for completely filled blocks: use trivial pointers +template +class element_iterator_generator +{ + typedef BlockType block_type; + typedef typename block_type::value_type value_type; + + typedef SizeType size_type; + +public: + typedef value_type* iterator; + + iterator operator () (block_type* blocks, SizeType offset) const + { + return blocks[0].elem + offset; + } +}; + +} // namespace helper + +template +struct element_iterator_traits +{ + typedef typename helper::element_iterator_generator< + BlockType, SizeType, BlockType::has_only_data + >::iterator element_iterator; +}; + +template +inline +typename element_iterator_traits::element_iterator +make_element_iterator(BlockType* blocks, SizeType offset) +{ + helper::element_iterator_generator< + BlockType, SizeType, BlockType::has_only_data + > iter_gen; + return iter_gen(blocks, offset); +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_ADAPTOR_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/bid.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/bid.h new file mode 100644 index 0000000000..838e731cf6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/bid.h @@ -0,0 +1,162 @@ +/*************************************************************************** + * include/stxxl/bits/mng/bid.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * Copyright (C) 2009, 2010 Andreas Beckmann + * Copyright (C) 2009 Johannes Singler + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_MNG_BID_HEADER +#define STXXL_MNG_BID_HEADER + +#include +#include +#include + +#include +#include +#include + +#ifndef STXXL_VERBOSE_BLOCK_LIFE_CYCLE +#define STXXL_VERBOSE_BLOCK_LIFE_CYCLE STXXL_VERBOSE2 +#endif +#define FMT_BID(_bid_) "[" << (_bid_).storage->get_allocator_id() << "]0x" << std::hex << std::setfill('0') << std::setw(8) << (_bid_).offset << "/0x" << std::setw(8) << (_bid_).size + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup mnglayer +//! \{ + +//! Block identifier class. +//! +//! Stores block identity, given by file and offset within the file +template +struct BID +{ + enum + { + size = Size, //!< Block size + t_size = Size //!< Blocks size, given by the parameter + }; + + file* storage; //!< pointer to the file of the block + stxxl::int64 offset; //!< offset within the file of the block + + BID() : storage(NULL), offset(0) + { } + + bool valid() const + { + return storage != NULL; + } + + BID(file* s, stxxl::int64 o) : storage(s), offset(o) + { } + + BID(const BID& obj) : storage(obj.storage), offset(obj.offset) + { } + + template + explicit BID(const BID& obj) + : storage(obj.storage), offset(obj.offset) + { } + + template + BID& operator = (const BID& obj) + { + storage = obj.storage; + offset = obj.offset; + return *this; + } + + bool is_managed() const + { + return storage->get_allocator_id() != file::NO_ALLOCATOR; + } +}; + +//! Specialization of block identifier class (BID) for variable size block size. +//! +//! Stores block identity, given by file, offset within the file, and size of the block +template <> +struct BID<0> +{ + file* storage; //!< pointer to the file of the block + stxxl::int64 offset; //!< offset within the file of the block + unsigned size; //!< size of the block in bytes + + enum + { + t_size = 0 //!< Blocks size, given by the parameter + }; + + BID() : storage(NULL), offset(0), size(0) + { } + + BID(file* f, stxxl::int64 o, unsigned s) : storage(f), offset(o), size(s) + { } + + bool valid() const + { + return (storage != NULL); + } +}; + +template +bool operator == (const BID& a, const BID& b) +{ + return (a.storage == b.storage) && (a.offset == b.offset) && (a.size == b.size); +} + +template +bool operator != (const BID& a, const BID& b) +{ + return (a.storage != b.storage) || (a.offset != b.offset) || (a.size != b.size); +} + +template +std::ostream& operator << (std::ostream& s, const BID& bid) +{ + // [0x12345678|0]0x00100000/0x00010000 + // [file ptr|file id]offset/size + + std::ios state(NULL); + state.copyfmt(s); + + s << "[" << bid.storage << "|"; + if (bid.storage) + s << bid.storage->get_allocator_id(); + else + s << "?"; + s << "]0x" << std::hex << std::setfill('0') << std::setw(8) << bid.offset << "/0x" << std::setw(8) << bid.size << std::dec; + + s.copyfmt(state); + return s; +} + +template +class BIDArray : public simple_vector > +{ +public: + BIDArray() + : simple_vector >() + { } + + BIDArray(unsigned_type size) + : simple_vector >(size) + { } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BID_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc.h new file mode 100644 index 0000000000..19ade8bb89 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc.h @@ -0,0 +1,266 @@ +/*************************************************************************** + * include/stxxl/bits/mng/block_alloc.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2007 Roman Dementiev + * Copyright (C) 2007-2009 Andreas Beckmann + * + * 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 STXXL_MNG_BLOCK_ALLOC_HEADER +#define STXXL_MNG_BLOCK_ALLOC_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \defgroup alloc Allocation Functors +//! \ingroup mnglayer +//! Standard allocation strategies encapsulated in functors. +//! \{ + +//! Example disk allocation scheme functor. +//! \remarks model of \b allocation_strategy concept +struct basic_allocation_strategy +{ + basic_allocation_strategy(int disks_begin, int disks_end); + basic_allocation_strategy(); + int operator () (int i) const; + static const char * name(); +}; + +//! Striping disk allocation scheme functor. +//! \remarks model of \b allocation_strategy concept +struct striping +{ + unsigned_type begin, diff; + +public: + striping(unsigned_type b, unsigned_type e) : begin(b), diff(e - b) + { } + + striping() : begin(0) + { + diff = config::get_instance()->disks_number(); + } + + unsigned_type operator () (unsigned_type i) const + { + return begin + i % diff; + } + + static const char * name() + { + return "striping"; + } +}; + +//! Fully randomized disk allocation scheme functor. +//! \remarks model of \b allocation_strategy concept +struct FR : public striping +{ +private: + typedef random_number rnd_type; + rnd_type rnd; + +public: + FR(unsigned_type b, unsigned_type e) : striping(b, e) + { } + + FR() : striping() + { } + + unsigned_type operator () (unsigned_type /*i*/) const + { + return begin + rnd(rnd_type::value_type(diff)); + } + + static const char * name() + { + return "fully randomized striping"; + } +}; + +//! Simple randomized disk allocation scheme functor. +//! \remarks model of \b allocation_strategy concept +struct SR : public striping +{ +private: + unsigned_type offset; + + typedef random_number rnd_type; + + void init() + { + rnd_type rnd; + offset = rnd(rnd_type::value_type(diff)); + } + +public: + SR(unsigned_type b, unsigned_type e) : striping(b, e) + { + init(); + } + + SR() : striping() + { + init(); + } + + unsigned_type operator () (unsigned_type i) const + { + return begin + (i + offset) % diff; + } + + static const char * name() + { + return "simple randomized striping"; + } +}; + +//! Randomized cycling disk allocation scheme functor. +//! \remarks model of \b allocation_strategy concept +struct RC : public striping +{ +private: + std::vector perm; + + void init() + { + for (unsigned_type i = 0; i < diff; i++) + perm[i] = i; + + stxxl::random_number rnd; + std::random_shuffle(perm.begin(), perm.end(), rnd _STXXL_FORCE_SEQUENTIAL); + } + +public: + RC(unsigned_type b, unsigned_type e) : striping(b, e), perm(diff) + { + init(); + } + + RC() : striping(), perm(diff) + { + init(); + } + + unsigned_type operator () (unsigned_type i) const + { + return begin + perm[i % diff]; + } + + static const char * name() + { + return "randomized cycling striping"; + } +}; + +struct RC_disk : public RC +{ + RC_disk(unsigned_type b, unsigned_type e) : RC(b, e) + { } + + RC_disk() : RC(config::get_instance()->regular_disk_range().first, config::get_instance()->regular_disk_range().second) + { } + + static const char * name() + { + return "Randomized cycling striping on regular disks"; + } +}; + +struct RC_flash : public RC +{ + RC_flash(unsigned_type b, unsigned_type e) : RC(b, e) + { } + + RC_flash() : RC(config::get_instance()->flash_range().first, config::get_instance()->flash_range().second) + { } + + static const char * name() + { + return "Randomized cycling striping on flash devices"; + } +}; + +//! 'Single disk' disk allocation scheme functor. +//! \remarks model of \b allocation_strategy concept +struct single_disk +{ + unsigned_type disk; + single_disk(unsigned_type d, unsigned_type = 0) : disk(d) + { } + + single_disk() : disk(0) + { } + + unsigned_type operator () (unsigned_type /*i*/) const + { + return disk; + } + + static const char * name() + { + return "single disk"; + } +}; + +//! Allocator functor adaptor. +//! +//! Gives offset to disk number sequence defined in constructor +template +struct offset_allocator +{ + BaseAllocator base; + int_type offset; + + //! Creates functor based on instance of \c BaseAllocator functor + //! with offset \c offset_. + //! \param offset_ offset + //! \param base_ used to create a copy + offset_allocator(int_type offset_, const BaseAllocator& base_) : base(base_), offset(offset_) + { } + + //! Creates functor based on instance of \c BaseAllocator functor. + //! \param base_ used to create a copy + offset_allocator(const BaseAllocator& base_) : base(base_), offset(0) + { } + + //! Creates functor based on default \c BaseAllocator functor. + offset_allocator() : offset(0) + { } + + unsigned_type operator () (unsigned_type i) const + { + return base(offset + i); + } + + int_type get_offset() const + { + return offset; + } + + void set_offset(int_type i) + { + offset = i; + } +}; + +#ifndef STXXL_DEFAULT_ALLOC_STRATEGY + #define STXXL_DEFAULT_ALLOC_STRATEGY stxxl::RC +#endif + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BLOCK_ALLOC_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc_interleaved.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc_interleaved.h new file mode 100644 index 0000000000..a82ee34155 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_alloc_interleaved.h @@ -0,0 +1,169 @@ +/*************************************************************************** + * include/stxxl/bits/mng/block_alloc_interleaved.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002, 2003 Roman Dementiev + * Copyright (C) 2007-2009, 2011 Andreas Beckmann + * + * 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 STXXL_MNG_BLOCK_ALLOC_INTERLEAVED_HEADER +#define STXXL_MNG_BLOCK_ALLOC_INTERLEAVED_HEADER + +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +#define CHECK_RUN_BOUNDS(pos) + +struct interleaved_striping +{ +protected: + int_type nruns; + unsigned_type begindisk; + unsigned_type diff; + + interleaved_striping(int_type nruns, unsigned_type begindisk, unsigned_type diff) + : nruns(nruns), begindisk(begindisk), diff(diff) + { } + +public: + interleaved_striping(int_type _nruns, const striping& strategy) + : nruns(_nruns), begindisk(strategy.begin), diff(strategy.diff) + { } + + unsigned_type operator () (unsigned_type i) const + { + return begindisk + (i / nruns) % diff; + } +}; + +struct interleaved_FR : public interleaved_striping +{ + typedef random_number rnd_type; + rnd_type rnd; + + interleaved_FR(int_type _nruns, const FR& strategy) + : interleaved_striping(_nruns, strategy.begin, strategy.diff) + { } + + unsigned_type operator () (unsigned_type /*i*/) const + { + return begindisk + rnd(rnd_type::value_type(diff)); + } +}; + +struct interleaved_SR : public interleaved_striping +{ + typedef random_number rnd_type; + std::vector offsets; + + interleaved_SR(int_type _nruns, const SR& strategy) + : interleaved_striping(_nruns, strategy.begin, strategy.diff) + { + rnd_type rnd; + for (int_type i = 0; i < nruns; i++) + offsets.push_back(rnd(rnd_type::value_type(diff))); + } + + unsigned_type operator () (unsigned_type i) const + { + return begindisk + (i / nruns + offsets[i % nruns]) % diff; + } +}; + +struct interleaved_RC : public interleaved_striping +{ + std::vector > perms; + + interleaved_RC(int_type _nruns, const RC& strategy) + : interleaved_striping(_nruns, strategy.begin, strategy.diff), + perms(nruns, std::vector(diff)) + { + for (int_type i = 0; i < nruns; i++) + { + for (unsigned_type j = 0; j < diff; j++) + perms[i][j] = j; + + random_number rnd; + std::random_shuffle(perms[i].begin(), perms[i].end(), rnd _STXXL_FORCE_SEQUENTIAL); + } + } + + unsigned_type operator () (unsigned_type i) const + { + return begindisk + perms[i % nruns][(i / nruns) % diff]; + } +}; + +struct first_disk_only : public interleaved_striping +{ + first_disk_only(int_type _nruns, const single_disk& strategy) + : interleaved_striping(_nruns, strategy.disk, 1) + { } + + unsigned_type operator () (unsigned_type) const + { + return begindisk; + } +}; + +template +struct interleaved_alloc_traits +{ }; + +template <> +struct interleaved_alloc_traits +{ + typedef interleaved_striping strategy; +}; + +template <> +struct interleaved_alloc_traits +{ + typedef interleaved_FR strategy; +}; + +template <> +struct interleaved_alloc_traits +{ + typedef interleaved_SR strategy; +}; + +template <> +struct interleaved_alloc_traits +{ + typedef interleaved_RC strategy; +}; + +template <> +struct interleaved_alloc_traits +{ + // FIXME! HACK! + typedef interleaved_RC strategy; +}; + +template <> +struct interleaved_alloc_traits +{ + // FIXME! HACK! + typedef interleaved_RC strategy; +}; + +template <> +struct interleaved_alloc_traits +{ + typedef first_disk_only strategy; +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BLOCK_ALLOC_INTERLEAVED_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_manager.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_manager.h new file mode 100644 index 0000000000..4e1514a7b2 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_manager.h @@ -0,0 +1,278 @@ +/*************************************************************************** + * include/stxxl/bits/mng/block_manager.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2007 Roman Dementiev + * Copyright (C) 2007, 2009 Johannes Singler + * Copyright (C) 2008-2010 Andreas Beckmann + * + * 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 STXXL_MNG_BLOCK_MANAGER_HEADER +#define STXXL_MNG_BLOCK_MANAGER_HEADER + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if STXXL_MSVC +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +#ifndef STXXL_MNG_COUNT_ALLOCATION +#define STXXL_MNG_COUNT_ALLOCATION 1 +#endif // STXXL_MNG_COUNT_ALLOCATION + +//! \addtogroup mnglayer +//! \{ + +//! Block manager class. +//! +//! Manages allocation and deallocation of blocks in multiple/single disk setting +//! \remarks is a singleton +class block_manager : public singleton +{ + friend class singleton; + + disk_allocator** disk_allocators; + file** disk_files; + + size_t ndisks; + block_manager(); + +#if STXXL_MNG_COUNT_ALLOCATION + //! total requested allocation in bytes + uint64 m_total_allocation; + + //! currently allocated bytes + uint64 m_current_allocation; + + //! maximum number of bytes allocated during program run. + uint64 m_maximum_allocation; +#endif // STXXL_MNG_COUNT_ALLOCATION + +protected: + template + void new_blocks_int( + const unsigned_type nblocks, + const DiskAssignFunctor& functor, + unsigned_type offset, + BIDIteratorClass out); + +public: + //! return total number of bytes available in all disks + uint64 get_total_bytes() const; + + //! Return total number of free disk allocations + uint64 get_free_bytes() const; + + //! Allocates new blocks. + //! + //! Allocates new blocks according to the strategy + //! given by \b functor and stores block identifiers + //! to the range [ \b bidbegin, \b bidend) + //! Allocation will be lined up with previous partial allocations + //! of \b offset blocks. + //! \param functor object of model of \b allocation_strategy concept + //! \param bidbegin bidirectional BID iterator object + //! \param bidend bidirectional BID iterator object + //! \param offset advance for \b functor to line up partial allocations + template + void new_blocks( + const DiskAssignFunctor& functor, + BIDIteratorClass bidbegin, + BIDIteratorClass bidend, + unsigned_type offset = 0) + { + typedef typename std::iterator_traits::value_type bid_type; + new_blocks_int(std::distance(bidbegin, bidend), functor, offset, bidbegin); + } + + //! Allocates new blocks according to the strategy + //! given by \b functor and stores block identifiers + //! to the output iterator \b out + //! Allocation will be lined up with previous partial allocations + //! of \b offset blocks. + //! \param nblocks the number of blocks to allocate + //! \param functor object of model of \b allocation_strategy concept + //! \param out iterator object of OutputIterator concept + //! \param offset advance for \b functor to line up partial allocations + //! + //! The \c BlockType template parameter defines the type of block to allocate + template + void new_blocks( + const unsigned_type nblocks, + const DiskAssignFunctor& functor, + BIDIteratorClass out, + unsigned_type offset = 0) + { + typedef typename BlockType::bid_type bid_type; + new_blocks_int(nblocks, functor, offset, out); + } + + //! Allocates a new block according to the strategy + //! given by \b functor and stores the block identifier + //! to bid. + //! Allocation will be lined up with previous partial allocations + //! of \b offset blocks. + //! \param functor object of model of \b allocation_strategy concept + //! \param bid BID to store the block identifier + //! \param offset advance for \b functor to line up partial allocations + template + void new_block(const DiskAssignFunctor& functor, BID& bid, unsigned_type offset = 0) + { + new_blocks_int >(1, functor, offset, &bid); + } + + //! Deallocates blocks. + //! + //! Deallocates blocks in the range [ \b bidbegin, \b bidend) + //! \param bidbegin iterator object of \b bid_iterator concept + //! \param bidend iterator object of \b bid_iterator concept + template + void delete_blocks(const BIDIteratorClass& bidbegin, const BIDIteratorClass& bidend); + + //! Deallocates a block. + //! \param bid block identifier + template + void delete_block(const BID& bid); + + ~block_manager(); + +#if STXXL_MNG_COUNT_ALLOCATION + //! return total requested allocation in bytes + uint64 get_total_allocation() const + { return m_total_allocation; } + + //! return currently allocated bytes + uint64 get_current_allocation() const + { return m_current_allocation; } + + //! return maximum number of bytes allocated during program run. + uint64 get_maximum_allocation() const + { return m_maximum_allocation; } +#endif // STXXL_MNG_COUNT_ALLOCATION +}; + +template +void block_manager::new_blocks_int( + const unsigned_type nblocks, + const DiskAssignFunctor& functor, + unsigned_type offset, + OutputIterator out) +{ + typedef BIDType bid_type; + typedef BIDArray bid_array_type; + + simple_vector bl(ndisks); + simple_vector disk_bids(ndisks); + simple_vector disk_ptrs(nblocks); + + // choose disks by calling DiskAssignFunctor + + bl.memzero(); + for (unsigned_type i = 0; i < nblocks; ++i) + { + unsigned_type disk = functor(offset + i); + disk_ptrs[i] = disk_files[disk]; + bl[disk]++; + } + + // allocate blocks on disks + + for (unsigned_type i = 0; i < ndisks; ++i) + { + if (bl[i]) + { + disk_bids[i].resize(bl[i]); + disk_allocators[i]->new_blocks(disk_bids[i]); + } + } + + bl.memzero(); + + OutputIterator it = out; + for (unsigned_type i = 0; i != nblocks; ++it, ++i) + { + const int disk = disk_ptrs[i]->get_allocator_id(); + bid_type bid(disk_ptrs[i], disk_bids[disk][bl[disk]++].offset); + *it = bid; + STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:new " << FMT_BID(bid)); + } + +#if STXXL_MNG_COUNT_ALLOCATION + m_total_allocation += nblocks * BIDType::size; + m_current_allocation += nblocks * BIDType::size; + m_maximum_allocation = STXXL_MAX(m_maximum_allocation, m_current_allocation); +#endif // STXXL_MNG_COUNT_ALLOCATION +} + +template +void block_manager::delete_block(const BID& bid) +{ + if (!bid.valid()) { + //STXXL_MSG("Warning: invalid block to be deleted."); + return; + } + if (!bid.is_managed()) + return; // self managed disk + STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:delete " << FMT_BID(bid)); + assert(bid.storage->get_allocator_id() >= 0); + disk_allocators[bid.storage->get_allocator_id()]->delete_block(bid); + disk_files[bid.storage->get_allocator_id()]->discard(bid.offset, bid.size); + +#if STXXL_MNG_COUNT_ALLOCATION + m_current_allocation -= BlockSize; +#endif // STXXL_MNG_COUNT_ALLOCATION +} + +template +void block_manager::delete_blocks( + const BIDIteratorClass& bidbegin, + const BIDIteratorClass& bidend) +{ + for (BIDIteratorClass it = bidbegin; it != bidend; it++) + { + delete_block(*it); + } +} + +// in bytes +#ifndef STXXL_DEFAULT_BLOCK_SIZE + #define STXXL_DEFAULT_BLOCK_SIZE(type) (2 * 1024 * 1024) // use traits +#endif + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BLOCK_MANAGER_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_prefetcher.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_prefetcher.h new file mode 100644 index 0000000000..8fe69b399c --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_prefetcher.h @@ -0,0 +1,229 @@ +/*************************************************************************** + * include/stxxl/bits/mng/block_prefetcher.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * Copyright (C) 2009, 2010 Johannes Singler + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_MNG_BLOCK_PREFETCHER_HEADER +#define STXXL_MNG_BLOCK_PREFETCHER_HEADER + +#include +#include + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup schedlayer +//! \{ + +class set_switch_handler +{ + onoff_switch& switch_; + completion_handler on_compl; + +public: + set_switch_handler(onoff_switch& _switch, const completion_handler& on_compl) + : switch_(_switch), on_compl(on_compl) + { } + + void operator () (request* req) + { + // call before setting switch to on, otherwise, user has no way to wait + // for the completion handler to be executed + on_compl(req); + switch_.on(); + } +}; + +//! Encapsulates asynchronous prefetching engine. +//! +//! \c block_prefetcher overlaps I/Os with consumption of read data. +//! Utilizes optimal asynchronous prefetch scheduling (by Peter Sanders et.al.) +template +class block_prefetcher : private noncopyable +{ +public: + typedef BlockType block_type; + typedef BidIteratorType bid_iterator_type; + + typedef typename block_type::bid_type bid_type; + +protected: + bid_iterator_type consume_seq_begin; + bid_iterator_type consume_seq_end; + unsigned_type seq_length; + + int_type* prefetch_seq; + + unsigned_type nextread; + unsigned_type nextconsume; + + const int_type nreadblocks; + + block_type* read_buffers; + request_ptr* read_reqs; + bid_type* read_bids; + + onoff_switch* completed; + int_type* pref_buffer; + + completion_handler do_after_fetch; + + block_type * wait(int_type iblock) + { + STXXL_VERBOSE1("block_prefetcher: waiting block " << iblock); + { + stats::scoped_wait_timer wait_timer(stats::WAIT_OP_READ); + + completed[iblock].wait_for_on(); + } + STXXL_VERBOSE1("block_prefetcher: finished waiting block " << iblock); + int_type ibuffer = pref_buffer[iblock]; + STXXL_VERBOSE1("block_prefetcher: returning buffer " << ibuffer); + assert(ibuffer >= 0 && ibuffer < nreadblocks); + return (read_buffers + ibuffer); + } + +public: + //! Constructs an object and immediately starts prefetching. + //! \param _cons_begin \c bid_iterator pointing to the \c bid of the first block to be consumed + //! \param _cons_end \c bid_iterator pointing to the \c bid of the ( \b last + 1 ) block of consumption sequence + //! \param _pref_seq gives the prefetch order, is a pointer to the integer array that contains + //! the indices of the blocks in the consumption sequence + //! \param _prefetch_buf_size amount of prefetch buffers to use + //! \param do_after_fetch unknown + block_prefetcher( + bid_iterator_type _cons_begin, + bid_iterator_type _cons_end, + int_type* _pref_seq, + int_type _prefetch_buf_size, + completion_handler do_after_fetch = completion_handler()) + : consume_seq_begin(_cons_begin), + consume_seq_end(_cons_end), + seq_length(_cons_end - _cons_begin), + prefetch_seq(_pref_seq), + nextread(STXXL_MIN(unsigned_type(_prefetch_buf_size), seq_length)), + nextconsume(0), + nreadblocks(nextread), + do_after_fetch(do_after_fetch) + { + STXXL_VERBOSE1("block_prefetcher: seq_length=" << seq_length); + STXXL_VERBOSE1("block_prefetcher: _prefetch_buf_size=" << _prefetch_buf_size); + assert(seq_length > 0); + assert(_prefetch_buf_size > 0); + int_type i; + read_buffers = new block_type[nreadblocks]; + read_reqs = new request_ptr[nreadblocks]; + read_bids = new bid_type[nreadblocks]; + pref_buffer = new int_type[seq_length]; + + std::fill(pref_buffer, pref_buffer + seq_length, -1); + + completed = new onoff_switch[seq_length]; + + for (i = 0; i < nreadblocks; ++i) + { + assert(prefetch_seq[i] < int_type(seq_length)); + assert(prefetch_seq[i] >= 0); + read_bids[i] = *(consume_seq_begin + prefetch_seq[i]); + STXXL_VERBOSE1("block_prefetcher: reading block " << i << + " prefetch_seq[" << i << "]=" << prefetch_seq[i] << + " @ " << &read_buffers[i] << + " @ " << read_bids[i]); + read_reqs[i] = read_buffers[i].read( + read_bids[i], + set_switch_handler(*(completed + prefetch_seq[i]), do_after_fetch)); + pref_buffer[prefetch_seq[i]] = i; + } + } + //! Pulls next unconsumed block from the consumption sequence. + //! \return Pointer to the already prefetched block from the internal buffer pool + block_type * pull_block() + { + STXXL_VERBOSE1("block_prefetcher: pulling a block"); + return wait(nextconsume++); + } + //! Exchanges buffers between prefetcher and application. + //! \param buffer pointer to the consumed buffer. After call if return value is true \c buffer + //! contains valid pointer to the next unconsumed prefetched buffer. + //! \remark parameter \c buffer must be value returned by \c pull_block() or \c block_consumed() methods + //! \return \c false if there are no blocks to prefetch left, \c true if consumption sequence is not emptied + bool block_consumed(block_type*& buffer) + { + int_type ibuffer = buffer - read_buffers; + STXXL_VERBOSE1("block_prefetcher: buffer " << ibuffer << " consumed"); + if (read_reqs[ibuffer].valid()) + read_reqs[ibuffer]->wait(); + + read_reqs[ibuffer] = NULL; + + if (nextread < seq_length) + { + assert(ibuffer >= 0 && ibuffer < nreadblocks); + int_type next_2_prefetch = prefetch_seq[nextread++]; + STXXL_VERBOSE1("block_prefetcher: prefetching block " << next_2_prefetch); + + assert((next_2_prefetch < int_type(seq_length)) && (next_2_prefetch >= 0)); + assert(!completed[next_2_prefetch].is_on()); + + pref_buffer[next_2_prefetch] = ibuffer; + read_bids[ibuffer] = *(consume_seq_begin + next_2_prefetch); + read_reqs[ibuffer] = read_buffers[ibuffer].read( + read_bids[ibuffer], + set_switch_handler(*(completed + next_2_prefetch), do_after_fetch) + ); + } + + if (nextconsume >= seq_length) + return false; + + buffer = wait(nextconsume++); + + return true; + } + + //! No more consumable blocks available, but can't delete the prefetcher, + //! because not all blocks may have been returned, yet. + bool empty() const + { + return nextconsume >= seq_length; + } + + //! Index of the next element in the consume sequence. + unsigned_type pos() const + { + return nextconsume; + } + + //! Frees used memory. + ~block_prefetcher() + { + for (int_type i = 0; i < nreadblocks; ++i) + if (read_reqs[i].valid()) + read_reqs[i]->wait(); + + delete[] read_reqs; + delete[] read_bids; + delete[] completed; + delete[] pref_buffer; + delete[] read_buffers; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BLOCK_PREFETCHER_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_scheduler.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_scheduler.h new file mode 100644 index 0000000000..091d9d83b5 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/block_scheduler.h @@ -0,0 +1,1844 @@ +/*************************************************************************** + * include/stxxl/bits/mng/block_scheduler.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2010-2011 Raoul Steffen + * + * 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 STXXL_MNG_BLOCK_SCHEDULER_HEADER +#define STXXL_MNG_BLOCK_SCHEDULER_HEADER + +#include +#include +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! Virtualization of a block of data. +//! Holds information for allocating and swapping. To use in cooperation with block_scheduler. +//! +//! A swappable_block can be uninitialized, i.e. it holds no data. +//! When access is required, is has to be acquired first, and released afterwards, so it can be swapped in and out as required. +//! If the stored data is no longer needed, it can get uninitialized, freeing both internal and external memory. +//! \tparam ValueType type of contained objects (POD with no references to internal memory). +//! \tparam BlockSize Number of objects in one block. +//! BlockSize*sizeof(ValueType) must be divisible by 4096. +template +class swappable_block +{ +protected: + static const unsigned_type raw_block_size = BlockSize * sizeof(ValueType); + +public: + typedef typed_block internal_block_type; + typedef typename internal_block_type::bid_type external_block_type; + +protected: + external_block_type external_data; //!external_data.valid if no associated space on disk + internal_block_type* internal_data; //NULL if there is no internal memory reserved + bool dirty; + int_type reference_count; + + static unsigned_type disk_allocation_offset; + + void get_external_block() + { block_manager::get_instance()->new_block(striping(), external_data, ++disk_allocation_offset); } + + void free_external_block() + { + block_manager::get_instance()->delete_block(external_data); + external_data = external_block_type(); // make invalid + } + +public: + //! Create in uninitialized state. + swappable_block() + : external_data() /*!valid*/, internal_data(0), dirty(false), reference_count(0) { } + + ~swappable_block() { } + + //! If it has an internal_block. The internal_block implicitly holds valid data. + bool is_internal() const + { return (internal_data != NULL); } + + //! If the external_block does not hold valid data. + bool is_dirty() const + { return dirty; } + + //! If it has an external_block. + bool has_external_block() const + { return external_data.valid(); } + + //! If it has an external_block that holds valid data. + bool is_external() const + { return has_external_block() && ! is_dirty(); } + + //! If it is acquired. + bool is_acquired() const + { return reference_count > 0; } + + //! If it holds an internal_block but does not need it. + bool is_evictable() const + { return ! is_acquired() && is_internal(); } + + //! If it has some valid data (in- or external). + bool is_initialized() const + { return is_internal() || is_external(); } + + //! Invalidate external data if true. + //! \return is_dirty() + bool make_dirty_if(const bool make_dirty) + { + assert(is_acquired()); + return dirty = make_dirty || dirty; + } + + //! Acquire the block, i.e. add a reference. Has to be internal. + //! \return A reference to the data-block. + internal_block_type & acquire() + { + assert(is_internal()); + ++reference_count; + return *internal_data; + } + + //! Release the block, i.e. subduct a reference. Has to be acquired. + void release() + { + assert(is_acquired()); + --reference_count; + } + + //! Get a reference to the data-block. Has to be acquired. + const internal_block_type & get_internal_block() const + { + assert(is_acquired()); + return *internal_data; + } + + //! Get a reference to the data-block. Has to be acquired. + internal_block_type & get_internal_block() + { + assert(is_acquired()); + return *internal_data; + } + + //! Fill block with default data, is supposed to be overwritten by subclass. Has to be internal. + void fill_default() { } + + //! Read asyncronusly from external_block to internal_block. Has to be internal and have an external_block. + //! \return A request pointer to the I/O. + request_ptr read_async(completion_handler on_cmpl = completion_handler()) + { + assert(is_internal()); + assert(has_external_block()); + #ifdef RW_VERBOSE + STXXL_MSG("reading block"); + #endif + dirty = false; + return internal_data->read(external_data, on_cmpl); + } + + //! Read synchronously from external_block to internal_block. Has to be internal and have an external_block. + void read_sync() + { read_async()->wait(); } + + //! Write asyncronusly from internal_block to external_block if necessary. + //! \return A request pointer to the I/O, an invalid request pointer if not necessary. + request_ptr clean_async(completion_handler on_cmpl = completion_handler()) + { + if (! is_dirty()) + return request_ptr(); + if (! has_external_block()) + get_external_block(); + #ifdef RW_VERBOSE + STXXL_MSG("writing block"); + #endif + dirty = false; + return internal_data->write(external_data, on_cmpl); + } + + //! Write synchronously from internal_block to external_block if necessary. + void clean_sync() + { + request_ptr rp = clean_async(); + if (rp.valid()) + rp->wait(); + } + + //! Attach an internal_block, making the block internal. Has to be not internal. + void attach_internal_block(internal_block_type* iblock) + { + assert(! is_internal()); + internal_data = iblock; + } + + //! Detach the internal_block. Writes to external_block if necessary. Has to be evictable. + //! \return A pointer to the internal_block. + internal_block_type * detach_internal_block() + { + assert(is_evictable()); + clean_sync(); + internal_block_type* iblock = internal_data; + internal_data = 0; + return iblock; + } + + //! Bring the block in uninitialized state, freeing external and internal memory. + //! Returns a pointer to the internal_block, NULL if it had none. + //! \return A pointer to the freed internal_block, NULL if it had none. + internal_block_type * deinitialize() + { + assert(! is_acquired()); + dirty = false; + // free external_block (so that it becomes invalid and the disk-space can be used again) + if (has_external_block()) + free_external_block(); + // free internal_block + internal_block_type* iblock = internal_data; + internal_data = 0; + return iblock; + } + + //! Set the external_block that holds the swappable_block's data. The block gets initialized with it. + //! \param eblock The external_block holding initial data. + void initialize(external_block_type eblock) + { + assert(! is_initialized()); + external_data = eblock; + } + + //! Extract the swappable_blocks data in an external_block. The block gets uninitialized. + //! \return The external_block that holds the swappable_block's data. + external_block_type extract_external_block() + { + assert(! is_internal()); + external_block_type eblock = external_data; + external_data = external_block_type(); + return eblock; + } +}; + +template +unsigned_type swappable_block::disk_allocation_offset = 0; + +template +class block_scheduler_algorithm; + +template +class block_scheduler_algorithm_online_lru; + +//! Schedules swapping of blocks and provides blocks for temporary storage. +//! +//! In simple mode, it tries to save I/Os through caching only. +//! In simulation mode, it records access patterns into a prediction sequence. +//! The prediction sequence can then be used for prefetching in the (offline) execute mode. +//! This will only work for algorithms with deterministic, data oblivious access patterns. +//! In simulation mode, no I/O is performed; the data provided is accessible but undefined. +//! In execute mode, it does caching, prefetching, and possibly other optimizations. +//! \tparam SwappableBlockType Type of swappable_blocks to manage. Can be some specialized subclass. +template +class block_scheduler : private noncopyable +{ +protected: + // tuning-parameter: To acquire blocks, internal memory has to be allocated. + // This constant limits the number of internal_blocks allocated at once. + static const int_type max_internal_blocks_alloc_at_once; + + typedef int_type time_type; + +public: + typedef typename SwappableBlockType::internal_block_type internal_block_type; + typedef typename SwappableBlockType::external_block_type external_block_type; + typedef typename std::vector::size_type swappable_block_identifier_type; + + /*/! Mode the block scheduler currently works in + enum mode + { + online, //serve requests immediately, without any prediction, LRU caching + simulation, //record prediction sequence only, do not serve requests, (returned blocks must not be accessed) + offline_lfd, //serve requests based on prediction sequence, using longest-forward-distance caching + offline_lfd_prefetch //serve requests based on prediction sequence, using longest-forward-distance caching, and prefetching + };*/ + +public: + // -------- prediction_sequence ------- + enum block_scheduler_operation + { + op_acquire, + op_acquire_uninitialized, + op_release, + op_release_dirty, + op_deinitialize, + op_initialize, + op_extract_external_block + }; + + struct prediction_sequence_element + { + block_scheduler_operation op; + swappable_block_identifier_type id; + time_type time; + + prediction_sequence_element(block_scheduler_operation op, + swappable_block_identifier_type id, int_type time) + : op(op), id(id), time(time) { } + }; + + typedef std::list prediction_sequence_type; + // ---- end prediction_sequence ------- + +protected: + template + friend class block_scheduler_algorithm; + + const int_type max_internal_blocks; + int_type remaining_internal_blocks; + //! Stores pointers to arrays of internal_blocks. Used to deallocate them only. + std::stack internal_blocks_blocks; + //! holds free internal_blocks with attributes reset. + std::stack free_internal_blocks; + //! temporary blocks that will not be needed after algorithm termination. + mutable std::vector swappable_blocks; + //! holds indices of free swappable_blocks with attributes reset. + std::priority_queue, + std::greater > free_swappable_blocks; + block_manager* bm; + block_scheduler_algorithm* algo; + + //! Get an internal_block from the freelist or a newly allocated one if available. + //! \return Pointer to the internal_block. NULL if none available. + internal_block_type * get_free_internal_block() + { + if (! free_internal_blocks.empty()) + { + // => there are internal_blocks in the free-list + internal_block_type* iblock = free_internal_blocks.top(); + free_internal_blocks.pop(); + return iblock; + } + else if (remaining_internal_blocks > 0) + { + // => more internal_blocks can be allocated + int_type num_blocks = std::min(max_internal_blocks_alloc_at_once, remaining_internal_blocks); + remaining_internal_blocks -= num_blocks; + internal_block_type* iblocks = new internal_block_type[num_blocks]; + internal_blocks_blocks.push(iblocks); + for (int_type i = num_blocks - 1; i > 0; --i) + free_internal_blocks.push(iblocks + i); + return iblocks; + } + else + { + // => no internal_block available + return 0; + } + } + + //! Return an internal_block to the freelist. + void return_free_internal_block(internal_block_type* iblock) + { free_internal_blocks.push(iblock); } + +public: + //! Create a block_scheduler with empty prediction sequence in simple mode. + //! \param max_internal_memory Amount of internal memory (in bytes) the scheduler is allowed to use for acquiring, prefetching and caching. + explicit block_scheduler(const int_type max_internal_memory) + : max_internal_blocks(div_ceil(max_internal_memory, sizeof(internal_block_type))), + remaining_internal_blocks(max_internal_blocks), + bm(block_manager::get_instance()), + algo(0) + { + algo = new block_scheduler_algorithm_online_lru(*this); + } + + ~block_scheduler() + { + delete algo; + int_type num_freed_internal_blocks = 0; + if (free_swappable_blocks.size() != swappable_blocks.size()) + { + // => not all swappable_blocks are free, at least deinitialize them + STXXL_ERRMSG("not all swappable_blocks are free, those not acquired will be deinitialized"); + // evictable_blocks would suffice + for (typename std::vector::iterator it = swappable_blocks.begin(); + it != swappable_blocks.end(); ++it) + { + if (! it->is_acquired() && it->deinitialize()) + // count internal_blocks that get freed + num_freed_internal_blocks++; + } + } + if (int_type nlost = (max_internal_blocks - remaining_internal_blocks) + - (free_internal_blocks.size() + num_freed_internal_blocks)) { + STXXL_ERRMSG(nlost << " internal_blocks are lost. They will get deallocated."); + } + while (! internal_blocks_blocks.empty()) + { + delete[] internal_blocks_blocks.top(); + internal_blocks_blocks.pop(); + } + } + + //! Acquire the given block. + //! Has to be in pairs with release. Pairs may be nested and interleaved. + //! \return Reference to the block's data. + //! param sbid Swappable block to acquire. + internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) + { return algo->acquire(sbid, uninitialized); } + + //! Release the given block. + //! Has to be in pairs with acquire. Pairs may be nested and interleaved. + //! \param sbid Swappable block to release. + //! \param dirty If the data has been changed, invalidating possible data in external storage. + void release(const swappable_block_identifier_type sbid, const bool dirty) + { algo->release(sbid, dirty); } + + //! Drop all data in the given block, freeing in- and external memory. + void deinitialize(const swappable_block_identifier_type sbid) + { algo->deinitialize(sbid); } + + //! Initialize the swappable_block with the given external_block. + //! + //! It will use the the external_block for swapping and take care about + //! it's deallocation. Has to be uninitialized. + //! \param sbid identifier to the swappable_block + //! \param eblock external_block a.k.a. bid + void initialize(const swappable_block_identifier_type sbid, external_block_type eblock) + { algo->initialize(sbid, eblock); } + + //! Deinitialize the swappable_block and return it's contents in an external_block. + //! + //! \param sbid identifier to the swappable_block + //! \return external_block a.k.a. bid + external_block_type extract_external_block(const swappable_block_identifier_type sbid) + { return algo->extract_external_block(sbid); } + + //! check if the swappable_block is initialized. + //! \param sbid identifier to the swappable_block + //! \return if the swappable_block is initialized + bool is_initialized(const swappable_block_identifier_type sbid) const + { return algo->is_initialized(sbid); } + + //! Record a timestep in the prediction sequence to seperate consecutive + //! acquire rsp. release-operations. Has an effect only in simulation mode. + void explicit_timestep() + { algo->explicit_timestep(); } + + //! Get a const reference to given block's data. Block has to be already acquired. + //! \param sbid Swappable block to access. + internal_block_type & get_internal_block(const swappable_block_identifier_type sbid) const + { return swappable_blocks[sbid].get_internal_block(); } + + //! Allocate an uninitialized swappable_block. + //! \return An identifier of the block. + swappable_block_identifier_type allocate_swappable_block() + { + swappable_block_identifier_type sbid; + if (free_swappable_blocks.empty()) + { + // create new swappable_block + sbid = swappable_blocks.size(); + swappable_blocks.resize(sbid + 1); + algo->swappable_blocks_resize(sbid + 1); + } + else + { + // take swappable_block from freelist + sbid = free_swappable_blocks.top(); + free_swappable_blocks.pop(); + } + return sbid; + } + + //! Free given no longer used temporary swappable_block. + //! \param sbid Temporary swappable_block to free. + void free_swappable_block(const swappable_block_identifier_type sbid) + { + deinitialize(sbid); + free_swappable_blocks.push(sbid); + } + + //! Returns if simulation mode is on, i.e. if a prediction sequence is being recorded. + //! \return If simulation mode is on. + bool is_simulating() const + { return algo->is_simulating(); } + + //! Switch the used algorithm, e.g. to simulation etc.. + //! \param new_algo Pointer to the new algorithm object. Has to be instantiated to the block scheduler (or the old algorithm object). + //! \return Pointer to the old algorithm object. + block_scheduler_algorithm * switch_algorithm_to(block_scheduler_algorithm* new_algo) + { + assert(&new_algo->bs == this); + block_scheduler_algorithm* old_algo = algo; + algo = new_algo; + return old_algo; + } + + //! Return the current algorithm. + block_scheduler_algorithm * get_current_algorithm() const + { + return algo; + } + + //! Get the prediction_sequence. + //! \return reference to the prediction_sequence + const prediction_sequence_type & get_prediction_sequence() const + { return algo->get_prediction_sequence(); } + + void flush() + { + std::vector requests; + while (! algo->evictable_blocks_empty()) + { + swappable_block_identifier_type sbid = algo->evictable_blocks_pop(); + request_ptr rq = swappable_blocks[sbid].clean_async(); + if (rq.valid()) + requests.push_back(rq); + return_free_internal_block(swappable_blocks[sbid].detach_internal_block()); + } + for (typename std::vector::reverse_iterator it = requests.rbegin(); + it != requests.rend(); ++it) + { + (*it)->wait(); + } + } +}; + +template +const int_type block_scheduler::max_internal_blocks_alloc_at_once = 128; + +//! Interface of a block scheduling algorithm. +template +class block_scheduler_algorithm : private noncopyable +{ +protected: + typedef block_scheduler block_scheduler_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename block_scheduler_type::external_block_type external_block_type; + typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; + typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; + typedef typename block_scheduler_type::time_type time_type; + +public: + block_scheduler_type& bs; + +protected: + std::vector& swappable_blocks; + prediction_sequence_type prediction_sequence; + + block_scheduler_algorithm * get_algorithm_from_block_scheduler() + { return bs.algo; } + + //! Get an internal_block from the block_scheduler. + //! \return Pointer to the internal_block. NULL if none available. + internal_block_type * get_free_internal_block_from_block_scheduler() + { return bs.get_free_internal_block(); } + + //! Return an internal_block to the block_scheduler. + void return_free_internal_block_to_block_scheduler(internal_block_type* iblock) + { bs.return_free_internal_block(iblock); } + +public: + block_scheduler_algorithm(block_scheduler_type& bs) + : bs(bs), + swappable_blocks(bs.swappable_blocks) + { } + + block_scheduler_algorithm(block_scheduler_algorithm* old) + : bs(old->bs), + swappable_blocks(bs.swappable_blocks) + { } + + virtual ~block_scheduler_algorithm() { } + + virtual bool evictable_blocks_empty() = 0; + virtual swappable_block_identifier_type evictable_blocks_pop() = 0; + virtual void swappable_blocks_resize(swappable_block_identifier_type /*size*/) { } + + virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) = 0; + virtual void release(swappable_block_identifier_type sbid, const bool dirty) = 0; + virtual void deinitialize(swappable_block_identifier_type sbid) = 0; + virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) = 0; + virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) = 0; + + virtual bool is_initialized(const swappable_block_identifier_type sbid) const + { return swappable_blocks[sbid].is_initialized(); } + + virtual void explicit_timestep() { } + virtual bool is_simulating() const + { return false; } + virtual const prediction_sequence_type & get_prediction_sequence() const + { return prediction_sequence; } +}; + +//! Block scheduling algorithm caching via the least recently used policy (online). +template +class block_scheduler_algorithm_online_lru : public block_scheduler_algorithm +{ +protected: + typedef block_scheduler block_scheduler_type; + typedef block_scheduler_algorithm block_scheduler_algorithm_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename block_scheduler_type::external_block_type external_block_type; + typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; + + using block_scheduler_algorithm_type::bs; + using block_scheduler_algorithm_type::swappable_blocks; + using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; + using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; + using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; + + //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. + addressable_fifo_queue evictable_blocks; + + internal_block_type * get_free_internal_block() + { + // try to get a free internal_block + if (internal_block_type* iblock = get_free_internal_block_from_block_scheduler()) + return iblock; + // evict block + assert(! evictable_blocks.empty()); // fails it there is not enough memory available + return swappable_blocks[evictable_blocks.pop()].detach_internal_block(); + } + + void return_free_internal_block(internal_block_type* iblock) + { return_free_internal_block_to_block_scheduler(iblock); } + + void init() + { + if (get_algorithm_from_block_scheduler()) + while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) + evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); + } + +public: + block_scheduler_algorithm_online_lru(block_scheduler_type& bs) + : block_scheduler_algorithm_type(bs) + { init(); } + + block_scheduler_algorithm_online_lru(block_scheduler_algorithm_type* old) + : block_scheduler_algorithm_type(old) + { init(); } + + virtual ~block_scheduler_algorithm_online_lru() + { + if (! evictable_blocks.empty()) + STXXL_ERRMSG("Destructing block_scheduler_algorithm_online that still holds evictable blocks. They get deinitialized."); + while (! evictable_blocks.empty()) + { + SwappableBlockType& sblock = swappable_blocks[evictable_blocks.pop()]; + if (internal_block_type* iblock = sblock.deinitialize()) + return_free_internal_block(iblock); + } + } + + virtual bool evictable_blocks_empty() + { return evictable_blocks.empty(); } + + virtual swappable_block_identifier_type evictable_blocks_pop() + { return evictable_blocks.pop(); } + + virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + /* acquired => internal -> increase reference count + internal but not acquired -> remove from evictable_blocks, increase reference count + not internal => uninitialized or external -> get internal_block, increase reference count + uninitialized -> fill with default value + external -> read */ + if (sblock.is_internal()) + { + if (! sblock.is_acquired()) + // not acquired yet -> remove from evictable_blocks + evictable_blocks.erase(sbid); + sblock.acquire(); + } + else if (sblock.is_initialized()) + { + // => external but not internal + //get internal_block + sblock.attach_internal_block(get_free_internal_block()); + if (! uninitialized) + //load block synchronously + sblock.read_sync(); + sblock.acquire(); + } + else + { + // => ! sblock.is_initialized() + //get internal_block + sblock.attach_internal_block(get_free_internal_block()); + sblock.acquire(); + //initialize new block + if (! uninitialized) + sblock.fill_default(); + } + return sblock.get_internal_block(); + } + + virtual void release(swappable_block_identifier_type sbid, const bool dirty) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + sblock.make_dirty_if(dirty); + sblock.release(); + if (! sblock.is_acquired()) + { + if (sblock.is_dirty() || sblock.is_external()) + // => evictable, put in pq + evictable_blocks.insert(sbid); + else + // => uninitialized, release internal block and put it in freelist + return_free_internal_block(sblock.detach_internal_block()); + } + } + + virtual void deinitialize(swappable_block_identifier_type sbid) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + if (sblock.is_evictable()) + evictable_blocks.erase(sbid); + if (internal_block_type* iblock = sblock.deinitialize()) + return_free_internal_block(iblock); + } + + virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + sblock.initialize(eblock); + } + + virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + if (sblock.is_evictable()) + evictable_blocks.erase(sbid); + if (sblock.is_internal()) + return_free_internal_block(sblock.detach_internal_block()); + return sblock.extract_external_block(); + } +}; + +//! Pseudo block scheduling algorithm only recording the request sequence. +template +class block_scheduler_algorithm_simulation : public block_scheduler_algorithm +{ +protected: + typedef block_scheduler block_scheduler_type; + typedef block_scheduler_algorithm block_scheduler_algorithm_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename block_scheduler_type::external_block_type external_block_type; + typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; + typedef typename block_scheduler_type::prediction_sequence_element prediction_sequence_element_type; + typedef typename block_scheduler_algorithm_type::time_type time_type; + + using block_scheduler_algorithm_type::bs; + using block_scheduler_algorithm_type::prediction_sequence; + using block_scheduler_algorithm_type::swappable_blocks; + using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; + using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; + using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; + + //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. + std::stack evictable_blocks; + time_type time_count; + bool last_op_release; + std::vector reference_counts; + internal_block_type dummy_block; + + void return_free_internal_block(internal_block_type* iblock) + { return_free_internal_block_to_block_scheduler(iblock); } + + void init() + { + if (get_algorithm_from_block_scheduler()) + while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) + evictable_blocks.push(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); + for (swappable_block_identifier_type i = 0; i < reference_counts.size(); ++i) + reference_counts[i] = swappable_blocks[i].is_initialized(); + } + +public: + block_scheduler_algorithm_simulation(block_scheduler_type& bs) + : block_scheduler_algorithm_type(bs), + time_count(0), + last_op_release(false), + reference_counts(swappable_blocks.size()) + { init(); } + + block_scheduler_algorithm_simulation(block_scheduler_algorithm_type* old) + : block_scheduler_algorithm_type(old), + time_count(0), + last_op_release(false), + reference_counts(swappable_blocks.size()) + { init(); } + + virtual ~block_scheduler_algorithm_simulation() + { + if (! evictable_blocks.empty()) + STXXL_ERRMSG("Destructing block_scheduler_algorithm_record_prediction_sequence that still holds evictable blocks. They get deinitialized."); + while (! evictable_blocks.empty()) + { + SwappableBlockType& sblock = swappable_blocks[evictable_blocks.top()]; + if (internal_block_type* iblock = sblock.deinitialize()) + return_free_internal_block(iblock); + evictable_blocks.pop(); + } + } + + virtual bool evictable_blocks_empty() + { return evictable_blocks.empty(); } + + virtual swappable_block_identifier_type evictable_blocks_pop() + { + swappable_block_identifier_type sbid = evictable_blocks.top(); + evictable_blocks.pop(); + return sbid; + } + + virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) + { + ++reference_counts[sbid]; + last_op_release = false; + if (uninitialized) + prediction_sequence.push_back( + prediction_sequence_element_type(block_scheduler_type::op_acquire_uninitialized, sbid, time_count) + ); + else + prediction_sequence.push_back( + prediction_sequence_element_type(block_scheduler_type::op_acquire, sbid, time_count) + ); + return dummy_block; + } + + virtual void release(swappable_block_identifier_type sbid, const bool dirty) + { + --reference_counts[sbid] += dirty; + time_count += ! last_op_release; + last_op_release = true; + if (dirty) + prediction_sequence.push_back( + prediction_sequence_element_type(block_scheduler_type::op_release_dirty, sbid, time_count) + ); + else + prediction_sequence.push_back( + prediction_sequence_element_type(block_scheduler_type::op_release, sbid, time_count) + ); + } + + virtual void deinitialize(swappable_block_identifier_type sbid) + { + reference_counts[sbid] = false; + prediction_sequence.push_back( + prediction_sequence_element_type(block_scheduler_type::op_deinitialize, sbid, time_count) + ); + } + + virtual void initialize(swappable_block_identifier_type sbid, external_block_type) + { + reference_counts[sbid] = true; + prediction_sequence.push_back( + prediction_sequence_element_type(block_scheduler_type::op_initialize, sbid, time_count) + ); + } + + virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) + { + reference_counts[sbid] = false; + prediction_sequence.push_back( + prediction_sequence_element_type(block_scheduler_type::op_extract_external_block, sbid, time_count) + ); + return external_block_type(); + } + + virtual void swappable_blocks_resize(swappable_block_identifier_type size) + { + reference_counts.resize(size, 0); + } + + virtual bool is_initialized(const swappable_block_identifier_type sbid) const + { + return reference_counts[sbid] > 0; + } + + virtual void explicit_timestep() + { ++time_count; } + + virtual bool is_simulating() const + { return true; } +}; + +//! Block scheduling algorithm caching via the longest forward distance policy (offline). +template +class block_scheduler_algorithm_offline_lfd : public block_scheduler_algorithm +{ +protected: + typedef block_scheduler block_scheduler_type; + typedef block_scheduler_algorithm block_scheduler_algorithm_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename block_scheduler_type::external_block_type external_block_type; + typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; + typedef typename block_scheduler_algorithm_type::time_type time_type; + typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; + + using block_scheduler_algorithm_type::bs; + using block_scheduler_algorithm_type::swappable_blocks; + using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; + using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; + using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; + + class priority + { + unsigned_type p; + + public: + priority(const SwappableBlockType& sblock, const std::pair& t) + { + // p larger => evict earlier + if (t.first) + { + // most significant: next use + p = unsigned_type(t.second) << 2; + // less significant: not dirty + p |= unsigned_type(! sblock.is_dirty()) << 1; + // less significant: has external_block + p |= unsigned_type(sblock.has_external_block()) << 0; + } + else + { + // most significant: next use + p = std::numeric_limits::max() << 2; + // less significant: next operation: extract > accessed no more > deinitialize + p |= unsigned_type(t.second) << 0; + } + } + + // less => evict earlier + bool operator < (const priority& right) const + { return p > right.p; } + }; + + //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. + addressable_priority_queue evictable_blocks; + /*! + * Stores for the sequence of releases extracted from the prediction_sequence: + * (true, timestamp of the blocks next acquire) if it is acquired next + * (false, 0) if it is deinitialized next + * (false, 1) if it is not accessed any more + * (false, 2) if it is extracted next + * (false, 3) if it is initialized next + */ + std::deque > next_use; + + internal_block_type * get_free_internal_block() + { + // try to get a free internal_block + if (internal_block_type* iblock = get_free_internal_block_from_block_scheduler()) + return iblock; + // evict block + assert(! evictable_blocks.empty()); // fails it there is not enough memory available + return swappable_blocks[evictable_blocks.pop()].detach_internal_block(); + } + + void return_free_internal_block(internal_block_type* iblock) + { return_free_internal_block_to_block_scheduler(iblock); } + + void init(block_scheduler_algorithm_type* old_algo) + { + std::vector > + blocks_next_acquire(swappable_blocks.size(), std::make_pair(false, 1)); + if (old_algo) + { + // precomputations for priorities: init next_acquires + const prediction_sequence_type& ps = old_algo->get_prediction_sequence(); + for (typename prediction_sequence_type::const_reverse_iterator it = ps.rbegin(); it != ps.rend(); ++it) + { + switch (it->op) + { + case (block_scheduler_type::op_acquire): + case (block_scheduler_type::op_acquire_uninitialized): + blocks_next_acquire[it->id] = std::make_pair(true, it->time); + break; + case (block_scheduler_type::op_release): + case (block_scheduler_type::op_release_dirty): + next_use.push_front(blocks_next_acquire[it->id]); + break; + case (block_scheduler_type::op_deinitialize): + blocks_next_acquire[it->id] = std::make_pair(false, 0); + break; + case (block_scheduler_type::op_initialize): + blocks_next_acquire[it->id] = std::make_pair(false, 3); + break; + case (block_scheduler_type::op_extract_external_block): + blocks_next_acquire[it->id] = std::make_pair(false, 2); + break; + } + } + } + if (get_algorithm_from_block_scheduler()) + { + while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) + { + // insert already evictable blocks with the right priority + const swappable_block_identifier_type sbid = get_algorithm_from_block_scheduler()->evictable_blocks_pop(); + evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], blocks_next_acquire[sbid])); + } + } + } + +public: + block_scheduler_algorithm_offline_lfd(block_scheduler_type& bs) + : block_scheduler_algorithm_type(bs) + { init(get_algorithm_from_block_scheduler()); } + + // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence + block_scheduler_algorithm_offline_lfd(block_scheduler_algorithm_type* old) + : block_scheduler_algorithm_type(old) + { init(old); } + + virtual ~block_scheduler_algorithm_offline_lfd() + { + if (! evictable_blocks.empty()) + STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lfd that still holds evictable blocks. They get deinitialized."); + while (! evictable_blocks.empty()) + { + SwappableBlockType& sblock = swappable_blocks[evictable_blocks.pop()]; + if (internal_block_type* iblock = sblock.deinitialize()) + return_free_internal_block(iblock); + } + } + + virtual bool evictable_blocks_empty() + { return evictable_blocks.empty(); } + + virtual swappable_block_identifier_type evictable_blocks_pop() + { return evictable_blocks.pop(); } + + virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + /* acquired => internal -> increase reference count + internal but not acquired -> remove from evictable_blocks, increase reference count + not intern => uninitialized or external -> get internal_block, increase reference count + uninitialized -> fill with default value + external -> read */ + if (sblock.is_internal()) + { + if (! sblock.is_acquired()) + // not acquired yet -> remove from evictable_blocks + evictable_blocks.erase(sbid); + sblock.acquire(); + } + else if (sblock.is_initialized()) + { + // => external but not internal + //get internal_block + sblock.attach_internal_block(get_free_internal_block()); + if (! uninitialized) + //load block synchronously + sblock.read_sync(); + sblock.acquire(); + } + else + { + // => ! sblock.is_initialized() + //get internal_block + sblock.attach_internal_block(get_free_internal_block()); + sblock.acquire(); + //initialize new block + if (! uninitialized) + sblock.fill_default(); + } + return sblock.get_internal_block(); + } + + virtual void release(swappable_block_identifier_type sbid, const bool dirty) + { + if (next_use.empty()) + { + STXXL_ERRMSG("block_scheduler_algorithm_offline_lfd got release-request but prediction sequence ended. Switching to block_scheduler_algorithm_online."); + // switch algorithm + block_scheduler_algorithm_type* new_algo, * old_algo; + new_algo = new block_scheduler_algorithm_online_lru(bs); + old_algo = bs.switch_algorithm_to(new_algo); + // redirect call + new_algo->release(sbid, dirty); + // delete self + delete old_algo; + return; + } + SwappableBlockType& sblock = swappable_blocks[sbid]; + sblock.make_dirty_if(dirty); + sblock.release(); + if (! sblock.is_acquired()) + { + if (sblock.is_dirty() || sblock.is_external()) + // => evictable, put in pq + evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], next_use.front())); + else + // => uninitialized, release internal block and put it in freelist + return_free_internal_block(sblock.detach_internal_block()); + } + next_use.pop_front(); + } + + virtual void deinitialize(swappable_block_identifier_type sbid) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + if (sblock.is_evictable()) + evictable_blocks.erase(sbid); + if (internal_block_type* iblock = sblock.deinitialize()) + return_free_internal_block(iblock); + } + + virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + sblock.initialize(eblock); + } + + virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + if (sblock.is_evictable()) + evictable_blocks.erase(sbid); + if (sblock.is_internal()) + return_free_internal_block(sblock.detach_internal_block()); + return sblock.extract_external_block(); + } +}; + +//! Block scheduling algorithm caching via the least recently used policy +//! (offline), and prefetching in addition. +template +class block_scheduler_algorithm_offline_lru_prefetching : public block_scheduler_algorithm +{ +protected: + struct scheduled_block_meta; + struct write_read_request; + + typedef block_scheduler block_scheduler_type; + typedef block_scheduler_algorithm block_scheduler_algorithm_type; + typedef typename block_scheduler_type::internal_block_type internal_block_type; + typedef typename block_scheduler_type::external_block_type external_block_type; + typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; + typedef typename block_scheduler_algorithm_type::time_type time_type; + typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; + typedef typename block_scheduler_type::block_scheduler_operation block_scheduler_operation; + typedef typename std::vector::iterator swappable_blocks_iterator; + + typedef std::map scheduled_blocks_type; + typedef typename scheduled_blocks_type::iterator scheduled_blocks_iterator; + typedef typename scheduled_blocks_type::reference scheduled_blocks_reference; + typedef std::map write_scheduled_blocks_type; + typedef typename write_scheduled_blocks_type::iterator write_scheduled_blocks_iterator; + typedef typename write_scheduled_blocks_type::reference write_scheduled_blocks_reference; + + using block_scheduler_algorithm_type::bs; + using block_scheduler_algorithm_type::swappable_blocks; + using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; + using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; + using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; + using block_scheduler_algorithm_type::prediction_sequence; + + struct write_read_request + { + bool write_done_soon; // set by read_after_write, checked by schedule_read() + bool shall_read; // checked by read_after_write, set by schedule_read() + swappable_blocks_iterator block_to_start_read; // used by read_after_write, set by schedule_read() + scheduled_blocks_iterator taker; // read_req set by read_after_write + request_ptr write_req; // completes with read_after_write + + write_read_request() + : write_done_soon(false), shall_read(false), block_to_start_read(), taker(), write_req(0) { } + }; + + struct read_after_write + { + write_read_request* wrr; + + read_after_write(write_read_request* write_read_req) + : wrr(write_read_req) { } + + void operator () (request*) + { + wrr->write_done_soon = true; + if (wrr->shall_read) + wrr->taker->second.read_req = wrr->block_to_start_read->read_async(); + } + }; + + struct scheduled_block_meta + { + internal_block_type* reserved_iblock; + std::pair giver; + request_ptr read_req; + std::deque operations; // invariant: not empty; front: last scheduled operation, back: upcoming operation + + scheduled_block_meta(block_scheduler_operation op) + : reserved_iblock(0), + giver(false, 0), + read_req(0), + operations() + { operations.push_front(op); } + }; + + //! Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. + std::set free_evictable_blocks; + std::set scheduled_evictable_blocks; + + //! Holds not internal swappable_blocks, whose next access has already been scheduled. + scheduled_blocks_type scheduled_blocks; + + //! Holds swappable_blocks, whose internal block has been taken away but the clean did not finish yet. + write_scheduled_blocks_type write_scheduled_blocks; + typename prediction_sequence_type::iterator next_op_to_schedule; + + //! Schedule an internal, possibly dirty swappable_block to write. + //! + //! The block becomes not dirty. if it was dirty, an entry in write_scheduled_blocks is made referencing the write_read_request. + //! \param sbid block to write + //! \return pointer to the write_read_request + write_read_request * schedule_write(const swappable_block_identifier_type sbid) + { + SwappableBlockType& sblock = swappable_blocks[sbid]; + write_read_request* wrr = new write_read_request; + wrr->write_req = sblock.clean_async(read_after_write(wrr)); + if (wrr->write_req.valid()) + { + bool t = write_scheduled_blocks.insert(std::make_pair(sbid, wrr)).second; + STXXL_ASSERT(t); + return wrr; + } + else + { + delete wrr; + return 0; + } + } + + //! Try to interrupt a read scheduled in a write_read_request. + //! + //! side-effect: possibly erases entry from write_scheduled_blocks, so the iterator writing_block may become invalid + //! \return if successful + bool try_interrupt_read(const write_scheduled_blocks_iterator& writing_block) + { + // stop read + writing_block->second->shall_read = false; + // check if stopped + if (! writing_block->second->write_done_soon) + return true; + // => possibly to late + scheduled_blocks_reference taker = *writing_block->second->taker; + // wait + wait_on_write(writing_block); + // check if read started + if (taker.second.read_req.valid()) + // => read started, to late + return false; + else + // => just in time + return true; + } + + //! Schedule an internal and external block to read. + //! + //! If the giver is still writing, schedule read via its write_read_request. + void schedule_read(scheduled_blocks_iterator block_to_read) + { + // first check if block_to_read is still writing. do not read before write finished + // wait_on_write(block_to_read->first); + + write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->first); + if (it != write_scheduled_blocks.end()) + { + scheduled_blocks_iterator other_block_to_read = it->second->taker; + // check if scheduled to read + if (it->second->shall_read) + { + if (try_interrupt_read(it)) + { + // => interrupted, swap internal_blocks + std::swap(other_block_to_read->second.giver, block_to_read->second.giver); + if (other_block_to_read->second.giver.first) + { + write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second); + if (it != write_scheduled_blocks.end()) + it->second->taker = other_block_to_read; + else + other_block_to_read->second.giver.first = false; + } + if (block_to_read->second.giver.first) + { + write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second); + if (it != write_scheduled_blocks.end()) + it->second->taker = block_to_read; + else + block_to_read->second.giver.first = false; + } + + internal_block_type* tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block(); + swappable_blocks[block_to_read->first].attach_internal_block( + swappable_blocks[other_block_to_read->first].detach_internal_block()); + swappable_blocks[other_block_to_read->first].attach_internal_block(tmp_iblock); + // => this block has its internal_block back, no need to read + // reschedule other + schedule_read(other_block_to_read); + return; + } + // else => read already started, but write done -> read this + } + else + { + // => no read scheduled, swap internal_blocks + std::swap(other_block_to_read->second.giver, block_to_read->second.giver); + if (other_block_to_read->second.giver.first) + { + write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second); + if (it != write_scheduled_blocks.end()) + it->second->taker = other_block_to_read; + else + other_block_to_read->second.giver.first = false; + } + if (block_to_read->second.giver.first) + { + write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second); + if (it != write_scheduled_blocks.end()) + it->second->taker = block_to_read; + else + block_to_read->second.giver.first = false; + } + + internal_block_type* tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block(); + swappable_blocks[block_to_read->first].attach_internal_block( + other_block_to_read->second.reserved_iblock); + other_block_to_read->second.reserved_iblock = tmp_iblock; + // => this block has its internal_block back, no need to read + return; + } + } + + // schedule block_to_read to read + if (block_to_read->second.giver.first) + { + write_scheduled_blocks_iterator writing_block = write_scheduled_blocks.find(block_to_read->second.giver.second); + if (writing_block != write_scheduled_blocks.end()) + { + // => there is a write scheduled + // tell the completion handler that we want a read + writing_block->second->block_to_start_read = swappable_blocks.begin() + block_to_read->first; + writing_block->second->taker = block_to_read; + writing_block->second->shall_read = true; + // and check if it is not to late + if (writing_block->second->write_done_soon) + { + // => the completion handler may have missed our wish to read + // so wait for it to finish and check + wait_on_write(writing_block); + block_to_read->second.giver.first = false; + if (block_to_read->second.read_req.valid()) + // read scheduled + return; + } + else + // read scheduled + return; + } + else + block_to_read->second.giver.first = false; + } + // => read could not be scheduled through the completion handler + block_to_read->second.read_req = swappable_blocks[block_to_read->first].read_async(); + } + + //! Wait for the write to finish. + //! + //! side-effect: erases entry from write_scheduled_blocks + void wait_on_write(const write_scheduled_blocks_iterator& writing_block) + { + writing_block->second->write_req->wait(); + delete writing_block->second; + write_scheduled_blocks.erase(writing_block); + } + + //! Wait for the write to finish. + //! + //! side-effect: erases entry from write_scheduled_blocks + void wait_on_write(const swappable_block_identifier_type& writing_block) + { + write_scheduled_blocks_iterator it = write_scheduled_blocks.find(writing_block); + if (it != write_scheduled_blocks.end()) + wait_on_write(it); + } + + //! Wait for the write of the giver to finish. + //! + //! side-effect: erases entry from write_scheduled_blocks + void wait_on_write(const scheduled_blocks_iterator& schedule_meta) + { + if (schedule_meta->second.giver.first) + { + wait_on_write(schedule_meta->second.giver.second); + schedule_meta->second.giver.first = false; + } + } + + //! Wait for the read to finish. + //! + //! side-effect: erases entry for the write of the giver from write_scheduled_blocks + void wait_on_read(const scheduled_blocks_iterator& schedule_meta) + { + wait_on_write(schedule_meta); + if (schedule_meta->second.read_req.valid()) + { + schedule_meta->second.read_req->wait(); + schedule_meta->second.read_req = 0; + } + } + + //! Wait for the write of the giver to finish and return reserved internal_block. + //! + //! side-effect: erases entry for the write of the giver from write_scheduled_blocks + internal_block_type * get_ready_block(const scheduled_blocks_iterator& schedule_meta) + { + wait_on_write(schedule_meta); + internal_block_type* r = schedule_meta->second.reserved_iblock; + schedule_meta->second.reserved_iblock = 0; + return r; + } + + bool shall_keep_internal_block(const scheduled_blocks_iterator& schedule_meta, const bool ignore_first = true) const + { + // returns true iif there is an acquire or acquire_uninitialized scheduled or there is a deinitialize scheduled and the block is dirty + for (typename std::deque::reverse_iterator + rit = schedule_meta->second.operations.rbegin() + ignore_first; + rit != schedule_meta->second.operations.rend(); ++rit) + { + switch (*rit) + { + case block_scheduler_type::op_acquire: + case block_scheduler_type::op_acquire_uninitialized: + return true; + break; + case block_scheduler_type::op_release: + case block_scheduler_type::op_release_dirty: + break; + case block_scheduler_type::op_deinitialize: + if (swappable_blocks[schedule_meta->first].is_dirty()) return true; break; + case block_scheduler_type::op_initialize: + case block_scheduler_type::op_extract_external_block: + break; + } + } + return false; + } + + // assumes the current operation to be still in operations + bool shall_be_cleaned(const scheduled_blocks_iterator& schedule_meta) const + { + // returns true iif there is an extract_external_block scheduled and no release_dirty, deinitialize or initialize before + for (typename std::deque::reverse_iterator + rit = schedule_meta->second.operations.rbegin() + 1; + rit != schedule_meta->second.operations.rend(); ++rit) + { + switch (*rit) + { + case block_scheduler_type::op_acquire: + case block_scheduler_type::op_acquire_uninitialized: + case block_scheduler_type::op_release: + break; + case block_scheduler_type::op_release_dirty: + case block_scheduler_type::op_deinitialize: + case block_scheduler_type::op_initialize: + return false; + break; + case block_scheduler_type::op_extract_external_block: + return true; + break; + } + } + return false; + } + + bool shall_be_read(const scheduled_blocks_iterator& schedule_meta, const bool ignore_first = true) const + { + // returns true iif there is an acquire scheduled next and the block is initialized + return swappable_blocks[schedule_meta->first].is_initialized() + && schedule_meta->second.operations.rbegin() + ignore_first != schedule_meta->second.operations.rend() + && *(schedule_meta->second.operations.rbegin() + ignore_first) == block_scheduler_type::op_acquire; + } + + void operation_done(scheduled_blocks_iterator& schedule_meta) + { + schedule_meta->second.operations.pop_back(); + if (schedule_meta->second.operations.empty()) + { + assert(! schedule_meta->second.giver.first); + scheduled_blocks.erase(schedule_meta); + } + } + + block_scheduler_algorithm_type * give_up(std::string err_msg = "detected some error in the prediction sequence") + { + STXXL_ERRMSG("block_scheduler_algorithm_offline_lru_prefetching: " << err_msg << ". Switching to block_scheduler_algorithm_online."); + // switch algorithm + block_scheduler_algorithm_type* new_algo + = new block_scheduler_algorithm_online_lru(bs); + // and delete self + delete bs.switch_algorithm_to(new_algo); + return new_algo; + } + + void return_free_internal_block(internal_block_type* iblock) + { return_free_internal_block_to_block_scheduler(iblock); } + + void schedule_next_operations() + { + while (next_op_to_schedule != prediction_sequence.end()) + { + // list operation in scheduled_blocks + std::pair ins_res = scheduled_blocks.insert( + std::make_pair(next_op_to_schedule->id, next_op_to_schedule->op)); + scheduled_blocks_iterator schedule_meta = ins_res.first; + if (! ins_res.second) + schedule_meta->second.operations.push_front(next_op_to_schedule->op); + SwappableBlockType& sblock = swappable_blocks[next_op_to_schedule->id]; + + // do appropriate preparations + if (next_op_to_schedule->op == block_scheduler_type::op_acquire + || next_op_to_schedule->op == block_scheduler_type::op_acquire_uninitialized) + { + if (sblock.is_internal()) + { + if (free_evictable_blocks.erase(next_op_to_schedule->id)) + scheduled_evictable_blocks.insert(next_op_to_schedule->id); + } + else + { + if (! schedule_meta->second.reserved_iblock) + { + // => needs internal_block -> try to get one + // -> try to get one from block_scheduler + schedule_meta->second.reserved_iblock = get_free_internal_block_from_block_scheduler(); + if (! schedule_meta->second.reserved_iblock) + { + // -> try to get one by evicting + if (free_evictable_blocks.empty()) + { + // => can not schedule acquire + // remove operation from scheduled_blocks + if (ins_res.second) + scheduled_blocks.erase(ins_res.first); + else + schedule_meta->second.operations.pop_front(); + // stop scheduling + return; + } + swappable_block_identifier_type giver = pop_begin(free_evictable_blocks); + { + assert(scheduled_blocks.find(giver) == scheduled_blocks.end() || + !shall_keep_internal_block(scheduled_blocks.find(giver), false)); + } + write_read_request* wrr = schedule_write(giver); + schedule_meta->second.giver.first = (wrr != NULL); + schedule_meta->second.giver.second = giver; + schedule_meta->second.reserved_iblock = swappable_blocks[giver].detach_internal_block(); + if (wrr) + wrr->taker = schedule_meta; + } + // read if desired + if (shall_be_read(schedule_meta, false)) + { + // => there is no operation scheduled for this block before this acquire and it is initialized + // -> start prefetching now + sblock.attach_internal_block(schedule_meta->second.reserved_iblock); + schedule_meta->second.reserved_iblock = 0; + scheduled_evictable_blocks.insert(next_op_to_schedule->id); + schedule_read(schedule_meta); + } + } + } + } + else if (next_op_to_schedule->op == block_scheduler_type::op_deinitialize) + { + if (sblock.is_dirty()) + if (free_evictable_blocks.erase(next_op_to_schedule->id)) + scheduled_evictable_blocks.insert(next_op_to_schedule->id); + } + + ++next_op_to_schedule; + } + for (typename std::set::iterator it = free_evictable_blocks.begin(); + it != free_evictable_blocks.end(); ++it) + { + if (! write_scheduled_blocks.count(*it)) + schedule_write(*it); + } + } + + void init(block_scheduler_algorithm_type* old_algo) + { + if (old_algo) + // copy prediction sequence + prediction_sequence = old_algo->get_prediction_sequence(); + next_op_to_schedule = prediction_sequence.begin(); + if (get_algorithm_from_block_scheduler()) + while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) + free_evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); + schedule_next_operations(); + } + + void deinit() + { + // TODO remove + if (! scheduled_blocks.empty()) + STXXL_MSG("deinit while scheduled_blocks not empty"); + if (! scheduled_evictable_blocks.empty()) + STXXL_MSG("deinit while scheduled_evictable_blocks not empty"); + + // empty scheduled_blocks + free_evictable_blocks.insert(scheduled_evictable_blocks.begin(), scheduled_evictable_blocks.end()); + //for (typename std::set::iterator it = scheduled_evictable_blocks.begin(); + // it != scheduled_evictable_blocks.end(); ++it) + // free_evictable_blocks.insert(*it); + scheduled_evictable_blocks.clear(); + while (! scheduled_blocks.empty()) + { + scheduled_blocks_iterator it = scheduled_blocks.begin(); + wait_on_read(it); + if (it->second.reserved_iblock) + return_free_internal_block(it->second.reserved_iblock); + scheduled_blocks.erase(it); + } + while (! write_scheduled_blocks.empty()) + { + write_scheduled_blocks_iterator it = write_scheduled_blocks.begin(); + wait_on_write(it); + } + } + +public: + block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_type& bs) + : block_scheduler_algorithm_type(bs) + { init(get_algorithm_from_block_scheduler()); } + + // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence + block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_algorithm_type* old) + : block_scheduler_algorithm_type(old) + { init(old); } + + virtual ~block_scheduler_algorithm_offline_lru_prefetching() + { + deinit(); + if (! free_evictable_blocks.empty()) + STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lru_prefetching that still holds evictable blocks. They get deinitialized."); + while (! free_evictable_blocks.empty()) + { + SwappableBlockType& sblock = swappable_blocks[pop_begin(free_evictable_blocks)]; + if (internal_block_type* iblock = sblock.deinitialize()) + return_free_internal_block(iblock); + } + } + + virtual bool evictable_blocks_empty() + { + deinit(); + return free_evictable_blocks.empty(); + } + + virtual swappable_block_identifier_type evictable_blocks_pop() + { return pop_begin(free_evictable_blocks); } + + virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) + { + assert(! prediction_sequence.empty()); + assert(prediction_sequence.front().op == + ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire)); + assert(prediction_sequence.front().id == sbid); + prediction_sequence.pop_front(); + scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); + assert(schedule_meta != scheduled_blocks.end()); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory) + assert(schedule_meta->second.operations.back() == + ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire)); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory) + + SwappableBlockType& sblock = swappable_blocks[sbid]; + /* acquired => internal -> increase reference count + internal but not acquired -> remove from scheduled_evictable_blocks, increase reference count + not internal => uninitialized or external -> get internal_block, increase reference count + uninitialized -> fill with default value + external -> read */ + if (sblock.is_internal()) + { + if (! sblock.is_acquired()) + { + // not acquired yet -> remove from scheduled_evictable_blocks + size_t t = scheduled_evictable_blocks.erase(sbid); + STXXL_ASSERT(t != 0); + wait_on_read(schedule_meta); + } + sblock.acquire(); + } + else + { + assert(uninitialized || ! sblock.is_initialized()); // initialized blocks should be scheduled to read and thus internal + //get internal_block + sblock.attach_internal_block(get_ready_block(schedule_meta)); + sblock.acquire(); + //initialize new block + if (! uninitialized) + sblock.fill_default(); + } + + operation_done(schedule_meta); + return sblock.get_internal_block(); + } + + virtual void release(swappable_block_identifier_type sbid, const bool dirty) + { + assert(! prediction_sequence.empty()); + assert(prediction_sequence.front().op == + ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release)); + assert(prediction_sequence.front().id == sbid); + prediction_sequence.pop_front(); + scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); + assert(schedule_meta != scheduled_blocks.end()); + assert(schedule_meta->second.operations.back() == + ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release)); + + SwappableBlockType& sblock = swappable_blocks[sbid]; + sblock.make_dirty_if(dirty); + sblock.release(); + if (! sblock.is_acquired()) + { + if (sblock.is_dirty() || sblock.is_external()) + { + // => evictable + if (shall_keep_internal_block(schedule_meta)) + { + // => swappable_block shall keep its internal_block + scheduled_evictable_blocks.insert(sbid); + if (shall_be_cleaned(schedule_meta)) + schedule_write(sbid); + } + else + { + // give block to scheduler + free_evictable_blocks.insert(sbid); + if (next_op_to_schedule != prediction_sequence.end()) + schedule_next_operations(); + else { + if (! write_scheduled_blocks.count(sbid)) + schedule_write(sbid); + } + } + } + else + { + // => uninitialized + if (shall_keep_internal_block(schedule_meta)) + // => swappable_block shall keep its internal_block + schedule_meta->second.reserved_iblock = sblock.detach_internal_block(); + else + { + // release internal block and give it to prefetcher + return_free_internal_block(sblock.detach_internal_block()); + if (next_op_to_schedule != prediction_sequence.end()) + schedule_next_operations(); + } + } + } + operation_done(schedule_meta); + } + + virtual void deinitialize(swappable_block_identifier_type sbid) + { + assert(! prediction_sequence.empty()); + assert(prediction_sequence.front().op == block_scheduler_type::op_deinitialize); + assert(prediction_sequence.front().id == sbid); + prediction_sequence.pop_front(); + scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); + assert(schedule_meta != scheduled_blocks.end()); + assert(schedule_meta->second.operations.back() == block_scheduler_type::op_deinitialize); + + SwappableBlockType& sblock = swappable_blocks[sbid]; + if (sblock.is_evictable()) + { + size_t t; + if (shall_keep_internal_block(schedule_meta, false)) + { + t = scheduled_evictable_blocks.erase(sbid); + if (t == 0) { + STXXL_ERRMSG("dirty block not scheduled on deinitialize"); + t = free_evictable_blocks.erase(sbid); + } + } + else + t = free_evictable_blocks.erase(sbid); + assert(t != 0); + } + if (internal_block_type* iblock = sblock.deinitialize()) + { + if (shall_keep_internal_block(schedule_meta)) + // => swappable_block shall keep its internal_block + schedule_meta->second.reserved_iblock = iblock; + else + { + // release internal block and give it to prefetcher + return_free_internal_block(iblock); + if (next_op_to_schedule != prediction_sequence.end()) + schedule_next_operations(); + } + } + operation_done(schedule_meta); + } + + virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) + { + assert(! prediction_sequence.empty()); + assert(prediction_sequence.front().op == block_scheduler_type::op_initialize); + assert(prediction_sequence.front().id == sbid); + prediction_sequence.pop_front(); + scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); + assert(schedule_meta != scheduled_blocks.end()); + assert(schedule_meta->second.operations.back() == block_scheduler_type::op_initialize); + + SwappableBlockType& sblock = swappable_blocks[sbid]; + sblock.initialize(eblock); + if (shall_be_read(schedule_meta)) + { + sblock.attach_internal_block(schedule_meta->second.reserved_iblock); + schedule_meta->second.reserved_iblock = 0; + scheduled_evictable_blocks.insert(sbid); + schedule_read(schedule_meta); + } + operation_done(schedule_meta); + } + + virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) + { + assert(! prediction_sequence.empty()); + assert(prediction_sequence.front().op == block_scheduler_type::op_extract_external_block); + assert(prediction_sequence.front().id == sbid); + prediction_sequence.pop_front(); + scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); + assert(schedule_meta != scheduled_blocks.end()); + assert(schedule_meta->second.operations.back() == block_scheduler_type::op_extract_external_block); + + SwappableBlockType& sblock = swappable_blocks[sbid]; + wait_on_write(sbid); + operation_done(schedule_meta); + return sblock.extract_external_block(); + } +}; + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BLOCK_SCHEDULER_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream.h new file mode 100644 index 0000000000..92d18d351d --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream.h @@ -0,0 +1,157 @@ +/*************************************************************************** + * include/stxxl/bits/mng/buf_istream.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * + * 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 STXXL_MNG_BUF_ISTREAM_HEADER +#define STXXL_MNG_BUF_ISTREAM_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup schedlayer +//! \{ + +// a paranoid check +#define BUF_ISTREAM_CHECK_END + +//! Buffered input stream. +//! +//! Reads data records from the stream of blocks. +//! \remark Reading performed in the background, i.e. with overlapping of I/O and computation +template +class buf_istream : private noncopyable +{ +public: + typedef BlockType block_type; + typedef BidIteratorType bid_iterator_type; + +private: + buf_istream() { } + +protected: + typedef block_prefetcher prefetcher_type; + prefetcher_type* prefetcher; + int_type current_elem; + block_type* current_blk; + int_type* prefetch_seq; +#ifdef BUF_ISTREAM_CHECK_END + bool not_finished; +#endif + +public: + typedef typename block_type::reference reference; + typedef buf_istream self_type; + + //! Constructs input stream object. + //! \param begin \c bid_iterator pointing to the first block of the stream + //! \param end \c bid_iterator pointing to the ( \b last + 1 ) block of the stream + //! \param nbuffers number of buffers for internal use + buf_istream(bid_iterator_type begin, bid_iterator_type end, unsigned_type nbuffers) + : current_elem(0) +#ifdef BUF_ISTREAM_CHECK_END + , not_finished(true) +#endif + { + const unsigned_type ndisks = config::get_instance()->disks_number(); + const unsigned_type mdevid = config::get_instance()->get_max_device_id(); + const int_type seq_length = end - begin; + prefetch_seq = new int_type[seq_length]; + + // obvious schedule + //for(int_type i = 0; i < seq_length; ++i) + // prefetch_seq[i] = i; + + // optimal schedule + nbuffers = STXXL_MAX(2 * ndisks, unsigned_type(nbuffers - 1)); + compute_prefetch_schedule(begin, end, prefetch_seq, + nbuffers, mdevid); + + prefetcher = new prefetcher_type(begin, end, prefetch_seq, nbuffers); + + current_blk = prefetcher->pull_block(); + } + + //! Input stream operator, reads in \c record. + //! \param record reference to the block record type, + //! contains value of the next record in the stream after the call of the operator + //! \return reference to itself (stream object) + self_type& operator >> (reference record) + { +#ifdef BUF_ISTREAM_CHECK_END + assert(not_finished); +#endif + + record = current_blk->elem[current_elem++]; + + if (UNLIKELY(current_elem >= block_type::size)) + { + current_elem = 0; +#ifdef BUF_ISTREAM_CHECK_END + not_finished = prefetcher->block_consumed(current_blk); +#else + prefetcher->block_consumed(current_blk); +#endif + } + + return (*this); + } + + //! Returns reference to the current record in the stream. + reference current() /* const */ + { + return current_blk->elem[current_elem]; + } + + //! Returns reference to the current record in the stream. + reference operator * () /* const */ + { + return current_blk->elem[current_elem]; + } + + //! Moves to the next record in the stream. + //! \return reference to itself after the advance + self_type& operator ++ () + { +#ifdef BUF_ISTREAM_CHECK_END + assert(not_finished); +#endif + + current_elem++; + + if (UNLIKELY(current_elem >= block_type::size)) + { + current_elem = 0; +#ifdef BUF_ISTREAM_CHECK_END + not_finished = prefetcher->block_consumed(current_blk); +#else + prefetcher->block_consumed(current_blk); +#endif + } + return *this; + } + + //! Frees used internal objects. + ~buf_istream() + { + delete prefetcher; + delete[] prefetch_seq; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BUF_ISTREAM_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream_reverse.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream_reverse.h new file mode 100644 index 0000000000..433dacc6f8 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_istream_reverse.h @@ -0,0 +1,168 @@ +/*************************************************************************** + * include/stxxl/bits/mng/buf_istream_reverse.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_MNG_BUF_ISTREAM_REVERSE_HEADER +#define STXXL_MNG_BUF_ISTREAM_REVERSE_HEADER + +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup schedlayer +//! \{ + +// a paranoid check +#define BUF_ISTREAM_CHECK_END + +//! Buffered input stream, reading the items in the blocks in reverse order. +//! +//! Reads data records from the stream of blocks in reverse order. +//! \remark Reading performed in the background, i.e. with overlapping of I/O and computation +template +class buf_istream_reverse : private noncopyable +{ +public: + typedef BlockType block_type; + typedef BidIteratorType bid_iterator_type; + + //-tb note that we redefine the BID type here, because there is no way to + //-derive it from BidIteratorType (which is usually just a POD pointer). + typedef BIDArray bid_vector_type; + +private: + buf_istream_reverse() { } + +protected: + typedef block_prefetcher prefetcher_type; + prefetcher_type* prefetcher; + int_type current_elem; + block_type* current_blk; + int_type* prefetch_seq; +#ifdef BUF_ISTREAM_CHECK_END + bool not_finished; +#endif + bid_vector_type bids_; + +public: + typedef typename block_type::reference reference; + typedef buf_istream_reverse self_type; + + //! Constructs input stream object, reading [first,last) blocks in reverse. + //! \param begin \c bid_iterator pointing to the first block of the stream + //! \param end \c bid_iterator pointing to the ( \b last + 1 ) block of the stream + //! \param nbuffers number of buffers for internal use + buf_istream_reverse(bid_iterator_type begin, bid_iterator_type end, int_type nbuffers) + : current_elem(0), +#ifdef BUF_ISTREAM_CHECK_END + not_finished(true), +#endif + bids_(end - begin) + { + // copy list of bids in reverse + std::reverse_copy(begin, end, bids_.begin()); + + // calculate prefetch sequence + const unsigned_type ndisks = config::get_instance()->disks_number(); + const unsigned_type mdevid = config::get_instance()->get_max_device_id(); + + prefetch_seq = new int_type[bids_.size()]; + + // optimal schedule + nbuffers = STXXL_MAX(2 * ndisks, unsigned_type(nbuffers - 1)); + compute_prefetch_schedule(bids_.begin(), bids_.end(), prefetch_seq, + nbuffers, mdevid); + + // create stream prefetcher + prefetcher = new prefetcher_type(bids_.begin(), bids_.end(), prefetch_seq, nbuffers); + + // fetch block: last in sequence + current_blk = prefetcher->pull_block(); + current_elem = block_type::size - 1; + } + + //! Input stream operator, reads in \c record. + //! \param record reference to the block record type, + //! contains value of the next record in the stream after the call of the operator + //! \return reference to itself (stream object) + self_type& operator >> (reference record) + { +#ifdef BUF_ISTREAM_CHECK_END + assert(not_finished); +#endif + + record = current_blk->elem[current_elem--]; + + if (UNLIKELY(current_elem < 0)) + { + current_elem = block_type::size - 1; +#ifdef BUF_ISTREAM_CHECK_END + not_finished = prefetcher->block_consumed(current_blk); +#else + prefetcher->block_consumed(current_blk); +#endif + } + + return (*this); + } + + //! Returns reference to the current record in the stream. + reference current() /* const */ + { + return current_blk->elem[current_elem]; + } + + //! Returns reference to the current record in the stream. + reference operator * () /* const */ + { + return current_blk->elem[current_elem]; + } + + //! Moves to the _previous_ record in the stream. + //! \return reference to itself after the advance + self_type& operator ++ () + { +#ifdef BUF_ISTREAM_CHECK_END + assert(not_finished); +#endif + + current_elem--; + + if (UNLIKELY(current_elem < 0)) + { + current_elem = block_type::size - 1; +#ifdef BUF_ISTREAM_CHECK_END + not_finished = prefetcher->block_consumed(current_blk); +#else + prefetcher->block_consumed(current_blk); +#endif + } + return *this; + } + + //! Frees used internal objects. + ~buf_istream_reverse() + { + delete prefetcher; + delete[] prefetch_seq; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BUF_ISTREAM_REVERSE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_ostream.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_ostream.h new file mode 100644 index 0000000000..048116ffaf --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_ostream.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * include/stxxl/bits/mng/buf_ostream.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * + * 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 STXXL_MNG_BUF_OSTREAM_HEADER +#define STXXL_MNG_BUF_OSTREAM_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup schedlayer +//! \{ + +//! Buffered output stream. +//! +//! Writes data records to the stream of blocks. +//! \remark Writing performed in the background, i.e. with overlapping of I/O and computation +template +class buf_ostream : private noncopyable +{ +public: + typedef BlockType block_type; + typedef BidIteratorType bid_iterator_type; + +protected: + buffered_writer writer; + bid_iterator_type current_bid; + int_type current_elem; + block_type* current_blk; + +public: + typedef typename block_type::const_reference const_reference; + typedef typename block_type::reference reference; + typedef buf_ostream self_type; + + //! Constructs output stream object. + //! \param first_bid \c bid_iterator pointing to the first block of the stream + //! \param nbuffers number of buffers for internal use + buf_ostream(bid_iterator_type first_bid, int_type nbuffers) + : writer(nbuffers, nbuffers / 2), current_bid(first_bid), + current_elem(0) + { + current_blk = writer.get_free_block(); + } + + //! Output stream operator, writes out \c record. + //! \param record const reference to block record type, containing a value of record to write to the stream + //! \return reference to itself (stream object) + self_type& operator << (const_reference record) + { + current_blk->elem[current_elem++] = record; + if (UNLIKELY(current_elem >= block_type::size)) + { + current_elem = 0; + current_blk = writer.write(current_blk, *(current_bid++)); + } + return *this; + } + + //! Returns reference to the current record. + //! \return reference to the current record + reference current() + { + return current_blk->elem[current_elem]; + } + + //! Returns reference to the current record. + //! \return reference to the current record + reference operator * () + { + return current_blk->elem[current_elem]; + } + + //! Moves to the next record in the stream. + //! \return reference to itself after the advance + self_type& operator ++ () + { + ++current_elem; + if (UNLIKELY(current_elem >= block_type::size)) + { + current_elem = 0; + current_blk = writer.write(current_blk, *(current_bid++)); + } + return *this; + } + + //! Fill current block with padding and flush + self_type & fill(const_reference record) + { + while (current_elem != 0) + { + operator << (record); + } + return *this; + } + + //! Force flush of current block, for finishing writing within a block. + //! \warning Use with caution as the block may contain uninitialized data + self_type & flush() + { + current_elem = 0; + current_blk = writer.write(current_blk, *(current_bid++)); + return *this; + } + + //! Deallocates internal objects. + ~buf_ostream() + { + assert(current_elem == 0); + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BUF_OSTREAM_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_writer.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_writer.h new file mode 100644 index 0000000000..2aa81b38f6 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/buf_writer.h @@ -0,0 +1,222 @@ +/*************************************************************************** + * include/stxxl/bits/mng/buf_writer.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * + * 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 STXXL_MNG_BUF_WRITER_HEADER +#define STXXL_MNG_BUF_WRITER_HEADER + +#include +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \defgroup schedlayer Block Scheduling Sublayer +//! \ingroup mnglayer +//! Group of classes which help in scheduling +//! sequences of read and write requests +//! via prefetching and buffered writing +//! \{ + +//! Encapsulates asynchronous buffered block writing engine. +//! +//! \c buffered_writer overlaps I/Os with filling of output buffer. +template +class buffered_writer : private noncopyable +{ + typedef BlockType block_type; + typedef typename block_type::bid_type bid_type; + +protected: + const unsigned_type nwriteblocks; + block_type* write_buffers; + bid_type* write_bids; + request_ptr* write_reqs; + const unsigned_type writebatchsize; + + std::vector free_write_blocks; // contains free write blocks + std::vector busy_write_blocks; // blocks that are in writing, notice that if block is not in free_ + // an not in busy then block is not yet filled + + struct batch_entry + { + stxxl::int64 offset; + int_type ibuffer; + batch_entry(stxxl::int64 o, int_type b) : offset(o), ibuffer(b) { } + }; + struct batch_entry_cmp + { + bool operator () (const batch_entry& a, const batch_entry& b) const + { + return (a.offset > b.offset); + } + }; + + typedef std::priority_queue, batch_entry_cmp> batch_type; + batch_type batch_write_blocks; // sorted sequence of blocks to write + +public: + //! Constructs an object. + //! \param write_buf_size number of write buffers to use + //! \param write_batch_size number of blocks to accumulate in + //! order to flush write requests (bulk buffered writing) + buffered_writer(unsigned_type write_buf_size, unsigned_type write_batch_size) + : nwriteblocks((write_buf_size > 2) ? write_buf_size : 2), + writebatchsize(write_batch_size ? write_batch_size : 1) + { + write_buffers = new block_type[nwriteblocks]; + write_reqs = new request_ptr[nwriteblocks]; + + write_bids = new bid_type[nwriteblocks]; + + for (unsigned_type i = 0; i < nwriteblocks; i++) + free_write_blocks.push_back(i); + + disk_queues::get_instance()->set_priority_op(request_queue::WRITE); + } + //! Returns free block from the internal buffer pool. + //! \return pointer to the block from the internal buffer pool + block_type * get_free_block() + { + int_type ibuffer; + for (std::vector::iterator it = busy_write_blocks.begin(); + it != busy_write_blocks.end(); ++it) + { + if (write_reqs[ibuffer = (*it)]->poll()) + { + busy_write_blocks.erase(it); + free_write_blocks.push_back(ibuffer); + + break; + } + } + if (UNLIKELY(free_write_blocks.empty())) + { + int_type size = busy_write_blocks.size(); + request_ptr* reqs = new request_ptr[size]; + int_type i = 0; + for ( ; i < size; ++i) + { + reqs[i] = write_reqs[busy_write_blocks[i]]; + } + int_type completed = wait_any(reqs, size); + int_type completed_global = busy_write_blocks[completed]; + delete[] reqs; + busy_write_blocks.erase(busy_write_blocks.begin() + completed); + + return (write_buffers + completed_global); + } + ibuffer = free_write_blocks.back(); + free_write_blocks.pop_back(); + + return (write_buffers + ibuffer); + } + //! Submits block for writing. + //! \param filled_block pointer to the block + //! \remark parameter \c filled_block must be value returned by \c get_free_block() or \c write() methods + //! \param bid block identifier, a place to write data of the \c filled_block + //! \return pointer to the new free block from the pool + block_type * write(block_type* filled_block, const bid_type& bid) // writes filled_block and returns a new block + { + if (batch_write_blocks.size() >= writebatchsize) + { + // flush batch + while (!batch_write_blocks.empty()) + { + int_type ibuffer = batch_write_blocks.top().ibuffer; + batch_write_blocks.pop(); + + if (write_reqs[ibuffer].valid()) + write_reqs[ibuffer]->wait(); + + write_reqs[ibuffer] = write_buffers[ibuffer].write(write_bids[ibuffer]); + + busy_write_blocks.push_back(ibuffer); + } + } + // STXXL_MSG("Adding write request to batch"); + + int_type ibuffer = filled_block - write_buffers; + write_bids[ibuffer] = bid; + batch_write_blocks.push(batch_entry(bid.offset, ibuffer)); + + return get_free_block(); + } + //! Flushes not yet written buffers. + void flush() + { + int_type ibuffer; + while (!batch_write_blocks.empty()) + { + ibuffer = batch_write_blocks.top().ibuffer; + batch_write_blocks.pop(); + + if (write_reqs[ibuffer].valid()) + write_reqs[ibuffer]->wait(); + + write_reqs[ibuffer] = write_buffers[ibuffer].write(write_bids[ibuffer]); + + busy_write_blocks.push_back(ibuffer); + } + for (std::vector::const_iterator it = + busy_write_blocks.begin(); + it != busy_write_blocks.end(); it++) + { + ibuffer = *it; + write_reqs[ibuffer]->wait(); + } + + assert(batch_write_blocks.empty()); + free_write_blocks.clear(); + busy_write_blocks.clear(); + + for (unsigned_type i = 0; i < nwriteblocks; i++) + free_write_blocks.push_back(i); + } + + //! Flushes not yet written buffers and frees used memory. + ~buffered_writer() + { + int_type ibuffer; + while (!batch_write_blocks.empty()) + { + ibuffer = batch_write_blocks.top().ibuffer; + batch_write_blocks.pop(); + + if (write_reqs[ibuffer].valid()) + write_reqs[ibuffer]->wait(); + + write_reqs[ibuffer] = write_buffers[ibuffer].write(write_bids[ibuffer]); + + busy_write_blocks.push_back(ibuffer); + } + for (std::vector::const_iterator it = + busy_write_blocks.begin(); + it != busy_write_blocks.end(); it++) + { + ibuffer = *it; + write_reqs[ibuffer]->wait(); + } + + delete[] write_reqs; + delete[] write_buffers; + delete[] write_bids; + } +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_BUF_WRITER_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/config.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/config.h new file mode 100644 index 0000000000..ea6a1a0184 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/config.h @@ -0,0 +1,276 @@ +/*************************************************************************** + * include/stxxl/bits/mng/config.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2005 Roman Dementiev + * Copyright (C) 2008, 2009 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_MNG_CONFIG_HEADER +#define STXXL_MNG_CONFIG_HEADER + +#include +#include +#include +#include + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup mnglayer +//! \{ + +//! Encapsulate the configuration of one "disk". The disk is actually a file +//! I/O object which block_manager uses to read/write blocks. +class disk_config +{ +public: + //! \name Basic Disk Configuration Parameters + //! \{ + + //! the file path used by the io implementation + std::string path; + + //! file size to initially allocate + uint64 size; + + //! io implementation to access file + std::string io_impl; + + //! \} + +public: + //! default constructor + disk_config(); + + //! initializing constructor, also parses fileio parameter + disk_config(const std::string& path, uint64 size, const std::string& fileio); + + //! initializing constructor, parse full line as in config files + disk_config(const std::string& line); + + //! parse a disk=\,\,\ options line into disk_config, + //! throws std::runtime_error on parse errors. + void parse_line(const std::string& line); + + //! parse the "io_impl" parameter into the optional parameter fields. + void parse_fileio(); + + //! return formatted fileio name and optional configuration parameters + std::string fileio_string() const; + +public: + //! \name Optional Disk / File I/O Implementation Parameters + //! \{ + + //! autogrow file if more disk space is needed, automatically set if size == 0. + bool autogrow; + + //! delete file on program exit (default for autoconfigurated files) + bool delete_on_exit; + + //! tristate variable: direct=0 -> force direct OFF, direct=1 -> try direct + //! ON, if fails print warning and open without direct, direct=2 -> force + //! direct ON, fail if unavailable. + enum direct_type { DIRECT_OFF = 0, DIRECT_TRY = 1, DIRECT_ON = 2 } direct; + + //! marks flash drives (configuration entries with flash= instead of disk=) + bool flash; + + //! select request queue for disk. Use different queues for files on + //! different disks. queue=-1 -> default queue (one for each disk). + int queue; + + //! the selected physical device id (e.g. for calculating prefetching + //! sequences). If -1 then the device id is chosen automatically. + unsigned int device_id; + + //! turned on by syscall fileio when the path points to a raw block device + bool raw_device; + + //! unlink file immediately after opening (available on most Unix) + bool unlink_on_open; + + //! desired queue length for linuxaio_file and linuxaio_queue + int queue_length; + + //! \} +}; + +//! Access point to disks properties. Since 1.4.0: no config files are read +//! automatically! +//! \remarks is a singleton +class config : public singleton +{ + friend class singleton; + + //! typedef of list of configured disks + typedef std::vector disk_list_type; + + //! list of configured disks + disk_list_type disks_list; + + //! In disks_list, flash devices come after all regular disks + unsigned first_flash; + + //! Finished initializing config + bool is_initialized; + + //! Constructor: this must be inlined to print the header version + //! string. + inline config() + : is_initialized(false) + { + logger::get_instance(); + STXXL_MSG(get_version_string_long()); + print_library_version_mismatch(); + } + + //! deletes autogrow files + ~config(); + + //! Search several places for a config file. + void find_config(); + + //! If disk list is empty, then search different locations for a disk + //! configuration file, or load a default config if everything fails. + void initialize(); + +public: + //! \name Initialization Functions + //! \{ + + //! Check that initialize() was called. + //! \note This function need not be called by the user, block_manager will + //! always call it. + void check_initialized() + { + if (!is_initialized) initialize(); + } + + //! Load disk configuration file. + void load_config_file(const std::string& config_path); + + //! Load default configuration. + void load_default_config(); + + //! Add a disk to the configuration list. + //! + //! \warning This function should only be used during initialization, as it + //! has no effect after construction of block_manager. + inline config & add_disk(const disk_config& cfg) + { + disks_list.push_back(cfg); + return *this; + } + + //! \} + +protected: + //! \name Automatic Disk Enumeration Functions + //! \{ + + //! static counter for automatic physical device enumeration + unsigned int m_max_device_id; + +public: + //! Returns automatic physical device id counter + unsigned int get_max_device_id(); + + //! Returns next automatic physical device id counter + unsigned int get_next_device_id(); + + //! Update the automatic physical device id counter + void update_max_device_id(unsigned int devid); + + //! \} + +public: + //! \name Query Functions + //! \{ + + //! Returns number of disks available to user. + //! \return number of disks + inline size_t disks_number() + { + check_initialized(); + return disks_list.size(); + } + + //! Returns contiguous range of regular disks w/o flash devices in the array of all disks. + //! \return range [begin, end) of regular disk indices + inline std::pair regular_disk_range() const + { + assert(is_initialized); + return std::pair(0, first_flash); + } + + //! Returns contiguous range of flash devices in the array of all disks. + //! \return range [begin, end) of flash device indices + inline std::pair flash_range() const + { + assert(is_initialized); + return std::pair(first_flash, (unsigned)disks_list.size()); + } + + //! Returns mutable disk_config structure for additional disk parameters + inline disk_config & disk(size_t disk) + { + check_initialized(); + return disks_list[disk]; + } + + //! Returns constant disk_config structure for additional disk parameters + inline const disk_config & disk(size_t disk) const + { + assert(is_initialized); + return disks_list[disk]; + } + + //! Returns path of disks. + //! \param disk disk's identifier + //! \return string that contains the disk's path name + inline const std::string & disk_path(size_t disk) const + { + assert(is_initialized); + return disks_list[disk].path; + } + + //! Returns disk size. + //! \param disk disk's identifier + //! \return disk size in bytes + inline stxxl::uint64 disk_size(size_t disk) const + { + assert(is_initialized); + return disks_list[disk].size; + } + + //! Returns name of I/O implementation of particular disk. + //! \param disk disk's identifier + inline const std::string & disk_io_impl(size_t disk) const + { + assert(is_initialized); + return disks_list[disk].io_impl; + } + + //! Returns the total size over all disks + uint64 total_size() const; + + //! \} +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_CONFIG_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/disk_allocator.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/disk_allocator.h new file mode 100644 index 0000000000..59ad0857f0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/disk_allocator.h @@ -0,0 +1,251 @@ +/*************************************************************************** + * include/stxxl/bits/mng/disk_allocator.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2009, 2010 Andreas Beckmann + * Copyright (C) 2013-2015 Timo Bingmann + * + * 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 STXXL_MNG_DISK_ALLOCATOR_HEADER +#define STXXL_MNG_DISK_ALLOCATOR_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \ingroup mnglayer +//! \{ + +class disk_allocator : private noncopyable +{ + typedef std::pair place; + + struct first_fit : public std::binary_function + { + bool operator () ( + const place& entry, + const stxxl::int64 size) const + { + return (entry.second >= size); + } + }; + + typedef std::map sortseq; + + stxxl::mutex mutex; + sortseq free_space; + stxxl::int64 free_bytes; + stxxl::int64 disk_bytes; + stxxl::int64 cfg_bytes; + stxxl::file* storage; + bool autogrow; + + void dump() const; + + void deallocation_error( + stxxl::int64 block_pos, stxxl::int64 block_size, + const sortseq::iterator& pred, const sortseq::iterator& succ) const; + + // expects the mutex to be locked to prevent concurrent access + void add_free_region(stxxl::int64 block_pos, stxxl::int64 block_size); + + // expects the mutex to be locked to prevent concurrent access + void grow_file(stxxl::int64 extend_bytes) + { + if (!extend_bytes) + return; + + storage->set_size(disk_bytes + extend_bytes); + add_free_region(disk_bytes, extend_bytes); + disk_bytes += extend_bytes; + } + +public: + disk_allocator(stxxl::file* storage, const disk_config& cfg) + : free_bytes(0), + disk_bytes(0), + cfg_bytes(cfg.size), + storage(storage), + autogrow(cfg.autogrow) + { + // initial growth to configured file size + grow_file(cfg.size); + } + + ~disk_allocator() + { + if (disk_bytes > cfg_bytes) { // reduce to original size + storage->set_size(cfg_bytes); + } + } + + inline int64 get_free_bytes() const + { + return free_bytes; + } + + inline int64 get_used_bytes() const + { + return disk_bytes - free_bytes; + } + + inline int64 get_total_bytes() const + { + return disk_bytes; + } + + template + void new_blocks(BIDArray& bids) + { + new_blocks(bids.begin(), bids.end()); + } + + template + void new_blocks(BID* begin, BID* end); + +#if 0 + template + void delete_blocks(const BIDArray& bids) + { + for (unsigned i = 0; i < bids.size(); ++i) + delete_block(bids[i]); + } +#endif + + template + void delete_block(const BID& bid) + { + scoped_mutex_lock lock(mutex); + + STXXL_VERBOSE2("disk_allocator::delete_block<" << BlockSize << + ">(pos=" << bid.offset << ", size=" << bid.size << + "), free:" << free_bytes << " total:" << disk_bytes); + + add_free_region(bid.offset, bid.size); + } +}; + +template +void disk_allocator::new_blocks(BID* begin, BID* end) +{ + stxxl::int64 requested_size = 0; + + for (typename BIDArray::iterator cur = begin; cur != end; ++cur) + { + STXXL_VERBOSE2("Asking for a block with size: " << (cur->size)); + requested_size += cur->size; + } + + scoped_mutex_lock lock(mutex); + + STXXL_VERBOSE2("disk_allocator::new_blocks, BlockSize = " << BlockSize << + ", free:" << free_bytes << " total:" << disk_bytes << + ", blocks: " << (end - begin) << + " begin: " << static_cast(begin) << + " end: " << static_cast(end) << + ", requested_size=" << requested_size); + + if (free_bytes < requested_size) + { + if (!autogrow) { + STXXL_THROW(bad_ext_alloc, + "Out of external memory error: " << requested_size << + " requested, " << free_bytes << " bytes free. " + "Maybe enable autogrow flags?"); + } + + STXXL_ERRMSG("External memory block allocation error: " << requested_size << + " bytes requested, " << free_bytes << + " bytes free. Trying to extend the external memory space..."); + + grow_file(requested_size); + } + + // dump(); + + sortseq::iterator space; + space = std::find_if(free_space.begin(), free_space.end(), + bind2nd(first_fit(), requested_size) _STXXL_FORCE_SEQUENTIAL); + + if (space == free_space.end() && requested_size == BlockSize) + { + assert(end - begin == 1); + + if (!autogrow) { + STXXL_ERRMSG("Warning: Severe external memory space fragmentation!"); + dump(); + + STXXL_ERRMSG("External memory block allocation error: " << requested_size << + " bytes requested, " << free_bytes << + " bytes free. Trying to extend the external memory space..."); + } + + grow_file(BlockSize); + + space = std::find_if(free_space.begin(), free_space.end(), + bind2nd(first_fit(), requested_size) _STXXL_FORCE_SEQUENTIAL); + } + + if (space != free_space.end()) + { + stxxl::int64 region_pos = (*space).first; + stxxl::int64 region_size = (*space).second; + free_space.erase(space); + if (region_size > requested_size) + free_space[region_pos + requested_size] = region_size - requested_size; + + for (stxxl::int64 pos = region_pos; begin != end; ++begin) + { + begin->offset = pos; + pos += begin->size; + } + free_bytes -= requested_size; + //dump(); + + return; + } + + // no contiguous region found + STXXL_VERBOSE1("Warning, when allocating an external memory space, no contiguous region found"); + STXXL_VERBOSE1("It might harm the performance"); + + assert(requested_size > BlockSize); + assert(end - begin > 1); + + lock.unlock(); + + BID* middle = begin + ((end - begin) / 2); + new_blocks(begin, middle); + new_blocks(middle, end); +} + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_DISK_ALLOCATOR_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/prefetch_pool.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/prefetch_pool.h new file mode 100644 index 0000000000..a95750829c --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/prefetch_pool.h @@ -0,0 +1,383 @@ +/*************************************************************************** + * include/stxxl/bits/mng/prefetch_pool.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003-2004 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_MNG_PREFETCH_POOL_HEADER +#define STXXL_MNG_PREFETCH_POOL_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup schedlayer +//! \{ + +//! Implements dynamically resizable prefetching pool. +template +class prefetch_pool : private noncopyable +{ +public: + typedef BlockType block_type; + typedef typename block_type::bid_type bid_type; + +protected: + struct bid_hash + { + size_t operator () (const bid_type& bid) const + { + size_t result = size_t(bid.storage) + + size_t(bid.offset & 0xffffffff) + + size_t(bid.offset >> 32); + return result; + } +#if STXXL_MSVC + bool operator () (const bid_type& a, const bid_type& b) const + { + return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset); + } + enum + { // parameters for hash table + bucket_size = 4, // 0 < bucket_size + min_buckets = 8 // min_buckets = 2 ^^ N, 0 < N + }; +#endif + }; + typedef std::pair busy_entry; + typedef typename compat_hash_map::result hash_map_type; + typedef typename std::list::iterator free_blocks_iterator; + typedef typename hash_map_type::iterator busy_blocks_iterator; + + //! contains free prefetch blocks + std::list free_blocks; + + //! blocks that are in reading or already read but not retrieved by user + hash_map_type busy_blocks; + + //! count number of free blocks, since traversing the std::list is slow. + unsigned_type free_blocks_size; + +public: + //! Constructs pool. + //! \param init_size initial number of blocks in the pool + explicit prefetch_pool(unsigned_type init_size = 1) + : free_blocks_size(init_size) + { + unsigned_type i = 0; + for ( ; i < init_size; ++i) + free_blocks.push_back(new block_type); + } + + void swap(prefetch_pool& obj) + { + std::swap(free_blocks, obj.free_blocks); + std::swap(busy_blocks, obj.busy_blocks); + std::swap(free_blocks_size, obj.free_blocks_size); + } + + //! Waits for completion of all ongoing read requests and frees memory. + virtual ~prefetch_pool() + { + while (!free_blocks.empty()) + { + delete free_blocks.back(); + free_blocks.pop_back(); + } + + try + { + busy_blocks_iterator i2 = busy_blocks.begin(); + for ( ; i2 != busy_blocks.end(); ++i2) + { + i2->second.second->wait(); + delete i2->second.first; + } + } + catch (...) + { } + } + + //! Returns number of owned blocks. + unsigned_type size() const + { + return free_blocks_size + busy_blocks.size(); + } + + //! Returns the number of free prefetching blocks. + unsigned_type free_size() const + { + return free_blocks_size; + } + + //! Returns the number of busy prefetching blocks. + unsigned_type busy_size() const + { + return busy_blocks.size(); + } + + //! Add a new block to prefetch pool, enlarges size of pool. + void add(block_type*& block) + { + free_blocks.push_back(block); + ++free_blocks_size; + block = NULL; // prevent caller from using the block any further + } + + //! Take out a block from the pool, one unhinted free block must be + //! available. + //! \return pointer to the block. Ownership of the block goes to the caller. + block_type * steal() + { + STXXL_CHECK(!free_blocks.empty()); + + block_type* p = free_blocks.back(); + free_blocks.pop_back(); + --free_blocks_size; + return p; + } + + /*! + * Gives a hint for prefetching a block, the block may or may not be read + * into a prefetch buffer. + * + * \param bid address of a block to be prefetched + * \return \c true if there was a free block to do prefetch and + * prefetching was scheduled, \c false otherwise + * + * \note If there are no free blocks available (all blocks are already in + * reading or read but not retrieved by user calling \c read method) + * calling \c hint function has no effect + */ + bool hint(bid_type bid) + { + // if block is already hinted, no need to hint it again + if (in_prefetching(bid)) { + STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " was already cached"); + return true; + } + + if (free_blocks_size) // only if we have a free block + { + --free_blocks_size; + block_type* block = free_blocks.back(); + free_blocks.pop_back(); + STXXL_VERBOSE2("prefetch_pool::hint bid=" << bid << " => prefetching"); + request_ptr req = block->read(bid); + busy_blocks[bid] = busy_entry(block, req); + return true; + } + STXXL_VERBOSE2("prefetch_pool::hint bid=" << bid << " => no free blocks for prefetching"); + return false; + } + + /*! + * Gives a hint for prefetching a block, the block may or may not be read + * into a prefetch buffer. This variant checks if the write pool is + * currently writing said block. + * + * \param bid address of a block to be prefetched + * \return \c true if there was a free block to do prefetch and + * prefetching was scheduled, \c false otherwise + * + * \note If there are no free blocks available (all blocks are already in + * reading or read but not retrieved by user calling \c read method) + * calling \c hint function has no effect + */ + bool hint(bid_type bid, write_pool& w_pool) + { + // if block is already hinted, no need to hint it again + if (in_prefetching(bid)) { + STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " was already cached"); + return true; + } + + if (free_blocks_size) // only if we have a free block + { + --free_blocks_size; + block_type* block = free_blocks.back(); + free_blocks.pop_back(); + if (w_pool.has_request(bid)) + { + busy_entry wp_request = w_pool.steal_request(bid); + STXXL_VERBOSE1("prefetch_pool::hint2 bid=" << bid << " was in write cache at " << wp_request.first); + assert(wp_request.first != 0); + w_pool.add(block); //in exchange + busy_blocks[bid] = wp_request; + return true; + } + STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " => prefetching"); + request_ptr req = block->read(bid); + busy_blocks[bid] = busy_entry(block, req); + return true; + } + STXXL_VERBOSE2("prefetch_pool::hint2 bid=" << bid << " => no free blocks for prefetching"); + return false; + } + + //! Cancel a hint request in case the block is no longer desired. + bool invalidate(bid_type bid) + { + busy_blocks_iterator cache_el = busy_blocks.find(bid); + if (cache_el == busy_blocks.end()) + return false; + + // cancel request if it is a read request, there might be + // write requests 'stolen' from a write_pool that may not be canceled + if (cache_el->second.second->get_type() == request::READ) + cache_el->second.second->cancel(); + // finish the request + cache_el->second.second->wait(); + ++free_blocks_size; + free_blocks.push_back(cache_el->second.first); + busy_blocks.erase(cache_el); + return true; + } + + //! Checks if a block is in the hinted block set. + bool in_prefetching(bid_type bid) + { + return (busy_blocks.find(bid) != busy_blocks.end()); + } + + //! Returns the request pointer for a hinted block, or an invalid NULL + //! request in case it was not requested due to lack of prefetch buffers. + request_ptr find(bid_type bid) + { + busy_blocks_iterator cache_el = busy_blocks.find(bid); + + if (cache_el == busy_blocks.end()) + return request_ptr(); // invalid pointer + else + return cache_el->second.second; + } + + //! Returns true if the blocks was hinted and the request is finished. + bool poll(bid_type bid) + { + request_ptr req = find(bid); + return req.valid() ? req->poll() : false; + } + + /*! + * Reads block. If this block is cached block is not read but passed from + * the cache. + * + * \param block block object, where data to be read to. If block was cached + * \c block 's ownership goes to the pool and block from cache is returned + * in \c block value. + * + * \param bid address of the block + * + * \warning \c block parameter must be allocated dynamically using \c new . + * + * \return request pointer object of read operation + */ + request_ptr read(block_type*& block, bid_type bid) + { + busy_blocks_iterator cache_el = busy_blocks.find(bid); + if (cache_el == busy_blocks.end()) + { + // not cached + STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => no copy in cache, retrieving to " << block); + return block->read(bid); + } + + // cached + STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => copy in cache exists"); + ++free_blocks_size; + free_blocks.push_back(block); + block = cache_el->second.first; + request_ptr result = cache_el->second.second; + busy_blocks.erase(cache_el); + return result; + } + + request_ptr read(block_type*& block, bid_type bid, write_pool& w_pool) + { + // try cache + busy_blocks_iterator cache_el = busy_blocks.find(bid); + if (cache_el != busy_blocks.end()) + { + // cached + STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => copy in cache exists"); + ++free_blocks_size; + free_blocks.push_back(block); + block = cache_el->second.first; + request_ptr result = cache_el->second.second; + busy_blocks.erase(cache_el); + return result; + } + + // try w_pool cache + if (w_pool.has_request(bid)) + { + busy_entry wp_request = w_pool.steal_request(bid); + STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " was in write cache at " << wp_request.first); + assert(wp_request.first != 0); + w_pool.add(block); //in exchange + block = wp_request.first; + return wp_request.second; + } + + // not cached + STXXL_VERBOSE1("prefetch_pool::read bid=" << bid << " => no copy in cache, retrieving to " << block); + return block->read(bid); + } + + //! Resizes size of the pool. + //! \param new_size desired size of the pool. If some + //! blocks are used for prefetching, these blocks can't be freed. + //! Only free blocks (not in prefetching) can be freed by reducing + //! the size of the pool calling this method. + //! \return new size of the pool + unsigned_type resize(unsigned_type new_size) + { + int_type diff = int_type(new_size) - int_type(size()); + if (diff > 0) + { + free_blocks_size += diff; + while (--diff >= 0) + free_blocks.push_back(new block_type); + + return size(); + } + + while (diff < 0 && free_blocks_size > 0) + { + ++diff; + --free_blocks_size; + delete free_blocks.back(); + free_blocks.pop_back(); + } + return size(); + } +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::prefetch_pool& a, + stxxl::prefetch_pool& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_MNG_PREFETCH_POOL_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/read_write_pool.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/read_write_pool.h new file mode 100644 index 0000000000..a0e435d8d1 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/read_write_pool.h @@ -0,0 +1,224 @@ +/*************************************************************************** + * include/stxxl/bits/mng/read_write_pool.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_MNG_READ_WRITE_POOL_HEADER +#define STXXL_MNG_READ_WRITE_POOL_HEADER + +#include +#include + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup schedlayer +//! \{ + +//! Implements dynamically resizable buffered writing and prefetched reading pool. +template +class read_write_pool : private noncopyable +{ +public: + typedef BlockType block_type; + typedef typename block_type::bid_type bid_type; + typedef unsigned_type size_type; + +protected: + typedef write_pool write_pool_type; + typedef prefetch_pool prefetch_pool_type; + + write_pool_type* w_pool; + prefetch_pool_type* p_pool; + bool delete_pools; + +public: + //! Constructs pool. + //! \param init_size_prefetch initial number of blocks in the prefetch pool + //! \param init_size_write initial number of blocks in the write pool + explicit read_write_pool(size_type init_size_prefetch = 1, size_type init_size_write = 1) + : delete_pools(true) + { + w_pool = new write_pool_type(init_size_write); + p_pool = new prefetch_pool_type(init_size_prefetch); + } + + STXXL_DEPRECATED(read_write_pool(prefetch_pool_type& p_pool, write_pool_type& w_pool)) + : w_pool(&w_pool), p_pool(&p_pool), delete_pools(false) + { } + + void swap(read_write_pool& obj) + { + std::swap(w_pool, obj.w_pool); + std::swap(p_pool, obj.p_pool); + std::swap(delete_pools, obj.delete_pools); + } + + //! Waits for completion of all ongoing requests and frees memory. + ~read_write_pool() + { + if (delete_pools) { + delete w_pool; + delete p_pool; + } + } + + //! Returns number of blocks owned by the write_pool. + size_type size_write() const { return w_pool->size(); } + + //! Returns number of blocks owned by the prefetch_pool. + size_type size_prefetch() const { return p_pool->size(); } + + //! Resizes size of the pool. + //! \param new_size new size of the pool after the call + void resize_write(size_type new_size) + { + w_pool->resize(new_size); + } + + //! Resizes size of the pool. + //! \param new_size new size of the pool after the call + void resize_prefetch(size_type new_size) + { + p_pool->resize(new_size); + } + + // WRITE POOL METHODS + + //! Passes a block to the pool for writing. + //! \param block block to write. Ownership of the block goes to the pool. + //! \c block must be allocated dynamically with using \c new . + //! \param bid location, where to write + //! \warning \c block must be allocated dynamically with using \c new . + //! \return request object of the write operation + request_ptr write(block_type*& block, bid_type bid) + { + request_ptr result = w_pool->write(block, bid); + + // if there is a copy of this block in the prefetch pool, + // it is now a stale copy, so invalidate it and re-hint the block + if (p_pool->invalidate(bid)) + p_pool->hint(bid, *w_pool); + + return result; + } + + //! Take out a block from the pool. + //! \return pointer to the block. Ownership of the block goes to the caller. + block_type * steal() + { + return w_pool->steal(); + } + + //! Add block to write pool + void add(block_type*& block) + { + w_pool->add(block); + } + + // PREFETCH POOL METHODS + + //! Gives a hint for prefetching a block. + //! \param bid address of a block to be prefetched + //! \return \c true if there was a free block to do prefetch and prefetching + //! was scheduled, \c false otherwise + //! \note If there are no free blocks available (all blocks + //! are already in reading or read but not retrieved by user calling \c read + //! method) calling \c hint function has no effect + bool hint(bid_type bid) + { + return p_pool->hint(bid, *w_pool); + } + + //! Cancel a hint request in case the block is no longer desired. + bool invalidate(bid_type bid) + { + return p_pool->invalidate(bid); + } + + /*! + * Reads block. If this block is cached block is not read but passed from + * the cache. + * + * \param block block object, where data to be read to. If block was cached + * \c block 's ownership goes to the pool and block from cache is returned + * in \c block value. + * + * \param bid address of the block + * \warning \c block parameter must be allocated dynamically using \c new . + * \return request pointer object of read operation + */ + request_ptr read(block_type*& block, bid_type bid) + { + return p_pool->read(block, bid, *w_pool); + } + + //! Returns the request pointer for a hinted block, or an invalid NULL + //! request in case it was not requested due to lack of prefetch buffers. + request_ptr find_hint(bid_type bid) + { + return p_pool->find(bid); + } + + //! Returns true if the blocks was hinted and the request is finished. + bool poll_hint(bid_type bid) + { + return p_pool->poll(bid); + } + + //! Add block to prefetch pool + void add_prefetch(block_type*& block) + { + p_pool->add(block); + } + + //! Take out a block from the prefetch pool, one unhinted free block must + //! be available. + //! \return pointer to the block. Ownership of the block goes to the caller. + block_type * steal_prefetch() + { + return p_pool->steal(); + } + + //! Checks if a block is in the hinted block set. + bool in_prefetching(bid_type bid) + { + return p_pool->in_prefetching(bid); + } + + //! Returns the number of free prefetching blocks. + unsigned_type free_size_prefetch() const + { + return p_pool->free_size(); + } + + //! Returns the number of busy prefetching blocks. + unsigned_type busy_size_prefetch() const + { + return p_pool->busy_size(); + } +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::read_write_pool& a, + stxxl::read_write_pool& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_MNG_READ_WRITE_POOL_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/typed_block.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/typed_block.h new file mode 100644 index 0000000000..a134941df0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/typed_block.h @@ -0,0 +1,370 @@ +/*************************************************************************** + * include/stxxl/bits/mng/typed_block.h + * + * Constructs a typed_block object containing as many elements elements plus + * some metadata as fits into the given block size. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2002-2004 Roman Dementiev + * Copyright (C) 2008-2010 Andreas Beckmann + * Copyright (C) 2013 Timo Bingmann + * + * 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 STXXL_MNG_TYPED_BLOCK_HEADER +#define STXXL_MNG_TYPED_BLOCK_HEADER + +#include +#include +#include +#include + +#ifndef STXXL_VERBOSE_TYPED_BLOCK +#define STXXL_VERBOSE_TYPED_BLOCK STXXL_VERBOSE2 +#endif + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup mnglayer +//! \{ + +//! Block Manager Internals \internal +namespace mng_local { + +//! \defgroup mnglayer_internals Internals +//! \ingroup mnglayer +//! Internals and support classes +//! \{ + +template +class filler_struct +{ + typedef unsigned char byte_type; + byte_type filler_array[Bytes]; + +public: + filler_struct() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] filler_struct is constructed"); } +}; + +template <> +class filler_struct<0> +{ + typedef unsigned char byte_type; + +public: + filler_struct() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] filler_struct<> is constructed"); } +}; + +//! Contains data elements for \c stxxl::typed_block , not intended for direct use. +template +class element_block +{ +public: + typedef Type type; + typedef Type value_type; + typedef Type& reference; + typedef const Type& const_reference; + typedef type* pointer; + typedef pointer iterator; + typedef const type* const_iterator; + + enum + { + size = Size //!< number of elements in the block + }; + + //! Array of elements of type Type + value_type elem[size]; + + element_block() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] element_block is constructed"); } + + //! An operator to access elements in the block + reference operator [] (size_t i) + { + return elem[i]; + } + + //! Returns \c iterator pointing to the first element. + iterator begin() + { + return elem; + } + + //! Returns \c const_iterator pointing to the first element. + const_iterator begin() const + { + return elem; + } + + //! Returns \c const_iterator pointing to the first element. + const_iterator cbegin() const + { + return begin(); + } + + //! Returns \c iterator pointing to the end element. + iterator end() + { + return elem + size; + } + + //! Returns \c const_iterator pointing to the end element. + const_iterator end() const + { + return elem + size; + } + + //! Returns \c const_iterator pointing to the end element. + const_iterator cend() const + { + return end(); + } +}; + +//! Contains BID references for \c stxxl::typed_block , not intended for direct use. +template +class block_w_bids : public element_block +{ +public: + enum + { + raw_size = RawSize, + nbids = NBids + }; + + typedef BID bid_type; + + //! Array of BID references + bid_type ref[nbids]; + + //! An operator to access bid references + bid_type& operator () (size_t i) + { + return ref[i]; + } + + block_w_bids() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_bids is constructed"); } +}; + +template +class block_w_bids + : public element_block +{ +public: + enum + { + raw_size = RawSize, + nbids = 0 + }; + + typedef BID bid_type; + + block_w_bids() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_bids<> is constructed"); } +}; + +//! Contains per block information for \c stxxl::typed_block , not intended for direct use. +template +class block_w_info + : public block_w_bids)* NBids - sizeof(MetaInfoType)) / sizeof(Type)), RawSize, NBids> +{ +public: + //! Type of per block information element. + typedef MetaInfoType info_type; + + //! Per block information element. + info_type info; + + block_w_info() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_info is constructed"); } +}; + +template +class block_w_info + : public block_w_bids)* NBids) / sizeof(Type)), RawSize, NBids> +{ +public: + typedef void info_type; + + block_w_info() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] block_w_info<> is constructed"); } +}; + +//! Contains per block filler for \c stxxl::typed_block , not intended for direct use. +template +class add_filler : public BaseType +{ +private: + //! Per block filler element. + filler_struct filler; + +public: + add_filler() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] add_filler is constructed"); } +}; + +template +class add_filler + : public BaseType +{ +public: + add_filler() { STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] add_filler<> is constructed"); } +}; + +//! Helper to compute the size of the filler , not intended for direct use. +template +class expand_struct : public add_filler +{ }; + +//! \} + +} // namespace mng_local + +//! Block containing elements of fixed length. +//! +//! \tparam RawSize size of block in bytes +//! \tparam Type type of block's records +//! \tparam NRef number of block references (BIDs) that can be stored in the block (default is 0) +//! \tparam MetaInfoType type of per block information (default is no information - void) +//! +//! The data array of type Type is contained in the parent class \c stxxl::element_block, see related information there. +//! The BID array of references is contained in the parent class \c stxxl::block_w_bids, see related information there. +//! The "per block information" is contained in the parent class \c stxxl::block_w_info, see related information there. +//! \warning If \c RawSize > 2MB object(s) of this type can not be allocated on the stack (as a +//! function variable for example), because Linux POSIX library limits the stack size for the +//! main thread to (2MB - system page size) +template +class typed_block + : public mng_local::expand_struct, RawSize> +{ + typedef mng_local::expand_struct, RawSize> Base; + +public: + typedef Type value_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef pointer iterator; + typedef const value_type* const_pointer; + typedef const_pointer const_iterator; + + enum constants + { + raw_size = RawSize, //!< size of block in bytes + size = Base::size, //!< number of elements in block + has_only_data = (raw_size == (size * sizeof(value_type))) //!< no meta info, bids or (non-empty) fillers included in the block, allows value_type array addressing across block boundaries + }; + + typedef BID bid_type; + + typed_block() + { + STXXL_STATIC_ASSERT(sizeof(typed_block) == raw_size); + STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] typed_block is constructed"); +#if 0 + assert(((long)this) % STXXL_BLOCK_ALIGN == 0); +#endif + } + +#if 0 + typed_block(const typed_block& tb) + { + STXXL_STATIC_ASSERT(sizeof(typed_block) == raw_size); + STXXL_MSG("[" << (void*)this << "] typed_block is copy constructed from [" << (void*)&tb << "]"); + STXXL_UNUSED(tb); + } +#endif + + /*! Writes block to the disk(s). + *! \param bid block identifier, points the file(disk) and position + *! \param on_cmpl completion handler + *! \return \c pointer_ptr object to track status I/O operation after the call + */ + request_ptr write(const bid_type& bid, + completion_handler on_cmpl = completion_handler()) + { + STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:write " << FMT_BID(bid)); + return bid.storage->awrite(this, bid.offset, raw_size, on_cmpl); + } + + /*! Reads block from the disk(s). + *! \param bid block identifier, points the file(disk) and position + *! \param on_cmpl completion handler + *! \return \c pointer_ptr object to track status I/O operation after the call + */ + request_ptr read(const bid_type& bid, + completion_handler on_cmpl = completion_handler()) + { + STXXL_VERBOSE_BLOCK_LIFE_CYCLE("BLC:read " << FMT_BID(bid)); + return bid.storage->aread(this, bid.offset, raw_size, on_cmpl); + } + + static void* operator new (size_t bytes) + { + unsigned_type meta_info_size = bytes % raw_size; + STXXL_VERBOSE_TYPED_BLOCK("typed::block operator new[]: bytes=" << bytes << ", meta_info_size=" << meta_info_size); + + void* result = aligned_alloc( + bytes - meta_info_size, meta_info_size); + +#if STXXL_WITH_VALGRIND || STXXL_TYPED_BLOCK_INITIALIZE_ZERO + memset(result, 0, bytes); +#endif + return result; + } + + static void* operator new[] (size_t bytes) + { + unsigned_type meta_info_size = bytes % raw_size; + STXXL_VERBOSE_TYPED_BLOCK("typed::block operator new[]: bytes=" << bytes << ", meta_info_size=" << meta_info_size); + + void* result = aligned_alloc( + bytes - meta_info_size, meta_info_size); + +#if STXXL_WITH_VALGRIND || STXXL_TYPED_BLOCK_INITIALIZE_ZERO + memset(result, 0, bytes); +#endif + return result; + } + + static void* operator new (size_t /*bytes*/, void* ptr) // construct object in existing memory + { + return ptr; + } + + static void operator delete (void* ptr) + { + aligned_dealloc(ptr); + } + + static void operator delete[] (void* ptr) + { + aligned_dealloc(ptr); + } + + static void operator delete (void*, void*) + { } + +#if 1 + // STRANGE: implementing destructor makes g++ allocate + // additional 4 bytes in the beginning of every array + // of this type !? makes aligning to 4K boundaries difficult + // + // http://www.cc.gatech.edu/grads/j/Seung.Won.Jun/tips/pl/node4.html : + // "One interesting thing is the array allocator requires more memory + // than the array size multiplied by the size of an element, by a + // difference of delta for metadata a compiler needs. It happens to + // be 8 bytes long in g++." + ~typed_block() + { + STXXL_VERBOSE_TYPED_BLOCK("[" << (void*)this << "] typed_block is destructed"); + } +#endif +}; + +//! \} + +STXXL_END_NAMESPACE + +#endif // !STXXL_MNG_TYPED_BLOCK_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/write_pool.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/write_pool.h new file mode 100644 index 0000000000..18c518b3eb --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/mng/write_pool.h @@ -0,0 +1,289 @@ +/*************************************************************************** + * include/stxxl/bits/mng/write_pool.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2003-2004 Roman Dementiev + * Copyright (C) 2009 Andreas Beckmann + * + * 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 STXXL_MNG_WRITE_POOL_HEADER +#define STXXL_MNG_WRITE_POOL_HEADER + +#include +#include +#include +#include +#include + +#define STXXL_VERBOSE_WPOOL(msg) STXXL_VERBOSE1("write_pool[" << static_cast(this) << "]" << msg) + +STXXL_BEGIN_NAMESPACE + +//! \addtogroup schedlayer +//! \{ + +//! Implements dynamically resizable buffered writing pool. +template +class write_pool : private noncopyable +{ +public: + typedef BlockType block_type; + typedef typename block_type::bid_type bid_type; + + // a hack to make wait_any work with busy_entry type + struct busy_entry + { + block_type* block; + request_ptr req; + bid_type bid; + + busy_entry() : block(NULL) { } + busy_entry(const busy_entry& a) : block(a.block), req(a.req), bid(a.bid) { } + busy_entry(block_type*& bl, request_ptr& r, bid_type& bi) + : block(bl), req(r), bid(bi) { } + + operator request_ptr () { return req; } + }; + typedef typename std::list::iterator free_blocks_iterator; + typedef typename std::list::iterator busy_blocks_iterator; + +protected: + // contains free write blocks + std::list free_blocks; + // blocks that are in writing + std::list busy_blocks; + +public: + //! Constructs pool. + //! \param init_size initial number of blocks in the pool + explicit write_pool(unsigned_type init_size = 1) + { + for (unsigned_type i = 0; i < init_size; ++i) + { + free_blocks.push_back(new block_type); + STXXL_VERBOSE_WPOOL(" create block=" << free_blocks.back()); + } + } + + void swap(write_pool& obj) + { + std::swap(free_blocks, obj.free_blocks); + std::swap(busy_blocks, obj.busy_blocks); + } + + //! Waits for completion of all ongoing write requests and frees memory. + ~write_pool() + { + STXXL_VERBOSE_WPOOL("::~write_pool free_blocks.size()=" << free_blocks.size() << + " busy_blocks.size()=" << busy_blocks.size()); + while (!free_blocks.empty()) + { + STXXL_VERBOSE_WPOOL(" delete free block=" << free_blocks.back()); + delete free_blocks.back(); + free_blocks.pop_back(); + } + + try + { + for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) + { + i2->req->wait(); + if (free_blocks.empty()) + STXXL_VERBOSE_WPOOL(" delete busy block=(empty)"); + else + STXXL_VERBOSE_WPOOL(" delete busy block=" << free_blocks.back()); + delete i2->block; + } + } + catch (...) + { } + } + + //! Returns number of owned blocks. + unsigned_type size() const { return free_blocks.size() + busy_blocks.size(); } + + //! Passes a block to the pool for writing. + //! \param block block to write. Ownership of the block goes to the pool. + //! \c block must be allocated dynamically with using \c new . + //! \param bid location, where to write + //! \warning \c block must be allocated dynamically with using \c new . + //! \return request object of the write operation + request_ptr write(block_type*& block, bid_type bid) + { + STXXL_VERBOSE_WPOOL("::write: " << block << " @ " << bid); + for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) + { + if (i2->bid == bid) { + assert(i2->block != block); + STXXL_VERBOSE_WPOOL("WAW dependency"); + // try to cancel the obsolete request + i2->req->cancel(); + // invalidate the bid of the stale write request, + // prevents prefetch_pool from stealing a stale block + i2->bid.storage = 0; + } + } + request_ptr result = block->write(bid); + busy_blocks.push_back(busy_entry(block, result, bid)); + block = NULL; // prevent caller from using the block any further + return result; + } + + //! Take out a block from the pool. + //! \return pointer to the block. Ownership of the block goes to the caller. + block_type * steal() + { + STXXL_ASSERT(size() > 0); + if (!free_blocks.empty()) + { + block_type* p = free_blocks.back(); + STXXL_VERBOSE_WPOOL("::steal : " << free_blocks.size() << " free blocks available, serve block=" << p); + free_blocks.pop_back(); + return p; + } + STXXL_VERBOSE_WPOOL("::steal : all " << busy_blocks.size() << " are busy"); + busy_blocks_iterator completed = wait_any(busy_blocks.begin(), busy_blocks.end()); + assert(completed != busy_blocks.end()); // we got something reasonable from wait_any + assert(completed->req->poll()); // and it is *really* completed + block_type* p = completed->block; + busy_blocks.erase(completed); + check_all_busy(); // for debug + STXXL_VERBOSE_WPOOL(" serve block=" << p); + return p; + } + + // deprecated name for the steal() + STXXL_DEPRECATED(block_type * get()) + { + return steal(); + } + + //! Resizes size of the pool. + //! \param new_size new size of the pool after the call + void resize(unsigned_type new_size) + { + int_type diff = int_type(new_size) - int_type(size()); + if (diff > 0) + { + while (--diff >= 0) + { + free_blocks.push_back(new block_type); + STXXL_VERBOSE_WPOOL(" create block=" << free_blocks.back()); + } + + return; + } + + while (++diff <= 0) + delete steal(); + } + + STXXL_DEPRECATED(request_ptr get_request(bid_type bid)) + { + busy_blocks_iterator i2 = busy_blocks.begin(); + for ( ; i2 != busy_blocks.end(); ++i2) + { + if (i2->bid == bid) + return i2->req; + } + return request_ptr(); + } + + bool has_request(bid_type bid) + { + for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) + { + if (i2->bid == bid) + return true; + } + return false; + } + + STXXL_DEPRECATED(block_type * steal(bid_type bid)) + { + busy_blocks_iterator i2 = busy_blocks.begin(); + for ( ; i2 != busy_blocks.end(); ++i2) + { + if (i2->bid == bid) + { + block_type* p = i2->block; + i2->req->wait(); + busy_blocks.erase(i2); + return p; + } + } + return NULL; + } + + // returns a block and a (potentially unfinished) I/O request associated with it + std::pair steal_request(bid_type bid) + { + for (busy_blocks_iterator i2 = busy_blocks.begin(); i2 != busy_blocks.end(); ++i2) + { + if (i2->bid == bid) + { + // remove busy block from list, request has not yet been waited for! + block_type* blk = i2->block; + request_ptr req = i2->req; + busy_blocks.erase(i2); + + STXXL_VERBOSE_WPOOL("::steal_request block=" << blk); + // hand over block and (unfinished) request to caller + return std::pair(blk, req); + } + } + STXXL_VERBOSE_WPOOL("::steal_request NOT FOUND"); + // not matching request found, return a dummy + return std::pair((block_type*)NULL, request_ptr()); + } + + void add(block_type*& block) + { + STXXL_VERBOSE_WPOOL("::add " << block); + free_blocks.push_back(block); + block = NULL; // prevent caller from using the block any further + } + +protected: + void check_all_busy() + { + busy_blocks_iterator cur = busy_blocks.begin(); + int_type cnt = 0; + while (cur != busy_blocks.end()) + { + if (cur->req->poll()) + { + free_blocks.push_back(cur->block); + cur = busy_blocks.erase(cur); + ++cnt; + continue; + } + ++cur; + } + STXXL_VERBOSE_WPOOL("::check_all_busy : " << cnt << + " are completed out of " << busy_blocks.size() + cnt << " busy blocks"); + } +}; + +//! \} + +STXXL_END_NAMESPACE + +namespace std { + +template +void swap(stxxl::write_pool& a, + stxxl::write_pool& b) +{ + a.swap(b); +} + +} // namespace std + +#endif // !STXXL_MNG_WRITE_POOL_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/msvc_compatibility.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/msvc_compatibility.h new file mode 100644 index 0000000000..c2c937d5cc --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/msvc_compatibility.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * include/stxxl/bits/msvc_compatibility.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2009, 2011 Andreas Beckmann + * + * 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 STXXL_MSVC_COMPATIBILITY_HEADER +#define STXXL_MSVC_COMPATIBILITY_HEADER + +#include + +#if STXXL_MSVC + +#include + +inline double log2(double x) +{ + return (log(x) / log(2.)); +} + +// http://msdn.microsoft.com/en-us/library/2ts7cx93.aspx +#define snprintf _snprintf + +// http://msdn.microsoft.com/en-us/library/h80404d3.aspx +#define strtoll _strtoi64 + +// http://msdn.microsoft.com/en-us/library/85zk715d.aspx +#define strtoull _strtoui64 + +#endif // STXXL_MSVC + +#endif // !STXXL_MSVC_COMPATIBILITY_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/namespace.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/namespace.h new file mode 100644 index 0000000000..7299ab7ee4 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/namespace.h @@ -0,0 +1,20 @@ +/*************************************************************************** + * include/stxxl/bits/namespace.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Roman Dementiev + * + * 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 STXXL_NAMESPACE_HEADER +#define STXXL_NAMESPACE_HEADER + +#define STXXL_BEGIN_NAMESPACE namespace stxxl { +#define STXXL_END_NAMESPACE \ + } + +#endif // !STXXL_NAMESPACE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/noncopyable.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/noncopyable.h new file mode 100644 index 0000000000..ae75f25cfe --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/noncopyable.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * include/stxxl/bits/noncopyable.h + * + * Inspired by boost::noncopyable. + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Andreas Beckmann + * + * 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 STXXL_NONCOPYABLE_HEADER +#define STXXL_NONCOPYABLE_HEADER + +#include +#include + +#if STXXL_BOOST_CONFIG + #include +#endif + +STXXL_BEGIN_NAMESPACE + +#if STXXL_BOOST_CONFIG + +typedef boost::noncopyable noncopyable; + +#else + +class noncopyable +{ +protected: + noncopyable() { } + +private: + // copying and assignment is not allowed + noncopyable(const noncopyable&); + const noncopyable& operator = (const noncopyable&); +}; + +#endif + +STXXL_END_NAMESPACE + +#endif // !STXXL_NONCOPYABLE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel.h new file mode 100644 index 0000000000..d973861473 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel.h @@ -0,0 +1,240 @@ +/*************************************************************************** + * include/stxxl/bits/parallel.h + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2008-2010 Andreas Beckmann + * Copyright (C) 2011 Johannes Singler + * Copyright (C) 2015 Timo Bingmann + * + * 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 STXXL_PARALLEL_HEADER +#define STXXL_PARALLEL_HEADER + +#include + +#include + +#if STXXL_PARALLEL + #include +#endif + +#include +#include +#include + +#if defined(_GLIBCXX_PARALLEL) +//use _STXXL_FORCE_SEQUENTIAL to tag calls which are not worthwhile parallelizing +#define _STXXL_FORCE_SEQUENTIAL , __gnu_parallel::sequential_tag() +#else +#define _STXXL_FORCE_SEQUENTIAL +#endif + +#if 0 +// sorting triggers is done sequentially +#define _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL _STXXL_FORCE_SEQUENTIAL +#else +// sorting triggers may be parallelized +#define _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL +#endif + +#if !STXXL_PARALLEL +#undef STXXL_PARALLEL_MULTIWAY_MERGE +#define STXXL_PARALLEL_MULTIWAY_MERGE 0 +#endif + +#if defined(STXXL_PARALLEL_MODE) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) < 40400) +#undef STXXL_PARALLEL_MULTIWAY_MERGE +#define STXXL_PARALLEL_MULTIWAY_MERGE 0 +#endif + +#if !defined(STXXL_PARALLEL_MULTIWAY_MERGE) +#define STXXL_PARALLEL_MULTIWAY_MERGE 1 +#endif + +#if !defined(STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD) +#define STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD 0 +#endif + +#if STXXL_WITH_GNU_PARALLEL +#include +#else +#include +#endif + +#include + +STXXL_BEGIN_NAMESPACE + +inline unsigned sort_memory_usage_factor() +{ +#if STXXL_PARALLEL && !STXXL_NOT_CONSIDER_SORT_MEMORY_OVERHEAD && defined(STXXL_PARALLEL_MODE) + return (__gnu_parallel::_Settings::get().sort_algorithm == __gnu_parallel::MWMS && omp_get_max_threads() > 1) ? 2 : 1; //memory overhead for multiway mergesort +#else + return 1; //no overhead +#endif +} + +inline void check_sort_settings() +{ +#if STXXL_PARALLEL && defined(STXXL_PARALLEL_MODE) && !defined(STXXL_NO_WARN_OMP_NESTED) + static bool did_warn = false; + if (!did_warn) { + if (__gnu_parallel::_Settings::get().sort_algorithm != __gnu_parallel::MWMS) { + if (omp_get_max_threads() <= 2) { + did_warn = true; // no problem with at most 2 threads, no need to check again + } + else if (!omp_get_nested()) { + STXXL_ERRMSG("Inefficient settings detected. To get full potential from your CPU it is recommended to set OMP_NESTED=TRUE in the environment."); + did_warn = true; + } + } + } +#else + // nothing to check +#endif +} + +inline bool do_parallel_merge() +{ +#if STXXL_PARALLEL_MULTIWAY_MERGE && defined(STXXL_PARALLEL_MODE) + return !stxxl::SETTINGS::native_merge && omp_get_max_threads() >= 1; +#else + return false; +#endif +} + +//! this namespace provides parallel or sequential algorithms depending on the +//! compilation settings. it should be used by all components, where +//! parallelism is optional. +namespace potentially_parallel { + +#if STXXL_WITH_GNU_PARALLEL + +using __gnu_parallel::sort; +using __gnu_parallel::random_shuffle; + +#elif STXXL_PARALLEL + +using std::sort; +using std::random_shuffle; + +#else + +using std::sort; +using std::random_shuffle; + +#endif + +/*! Multi-way merging dispatcher. + * \param seqs_begin Begin iterator of iterator pair input sequence. + * \param seqs_end End iterator of iterator pair input sequence. + * \param target Begin iterator out output sequence. + * \param comp Comparator. + * \param length Maximum length to merge. + * \return End iterator of output sequence. + */ +template +RandomAccessIterator3 +multiway_merge(RandomAccessIteratorPairIterator seqs_begin, + RandomAccessIteratorPairIterator seqs_end, + RandomAccessIterator3 target, DiffType length, + Comparator comp) +{ +#if STXXL_PARALLEL + return stxxl::parallel::multiway_merge( + seqs_begin, seqs_end, target, length, comp); +#else + return stxxl::parallel::sequential_multiway_merge( + seqs_begin, seqs_end, target, length, comp); +#endif +} + +/*! Multi-way merging dispatcher. + * \param seqs_begin Begin iterator of iterator pair input sequence. + * \param seqs_end End iterator of iterator pair input sequence. + * \param target Begin iterator out output sequence. + * \param comp Comparator. + * \param length Maximum length to merge. + * \return End iterator of output sequence. + */ +template +RandomAccessIterator3 +multiway_merge_stable(RandomAccessIteratorPairIterator seqs_begin, + RandomAccessIteratorPairIterator seqs_end, + RandomAccessIterator3 target, DiffType length, + Comparator comp) +{ +#if STXXL_PARALLEL + return stxxl::parallel::multiway_merge_stable( + seqs_begin, seqs_end, target, length, comp); +#else + return stxxl::parallel::sequential_multiway_merge( + seqs_begin, seqs_end, target, length, comp); +#endif +} + +/*! Multi-way merging front-end. + * \param seqs_begin Begin iterator of iterator pair input sequence. + * \param seqs_end End iterator of iterator pair input sequence. + * \param target Begin iterator out output sequence. + * \param comp Comparator. + * \param length Maximum length to merge. + * \return End iterator of output sequence. + * \pre For each \c i, \c seqs_begin[i].second must be the end marker of the sequence, but also reference the one more sentinel element. + */ +template +RandomAccessIterator3 +multiway_merge_sentinels(RandomAccessIteratorPairIterator seqs_begin, + RandomAccessIteratorPairIterator seqs_end, + RandomAccessIterator3 target, DiffType length, + Comparator comp) +{ +#if STXXL_PARALLEL + return stxxl::parallel::multiway_merge_sentinels( + seqs_begin, seqs_end, target, length, comp); +#else + return stxxl::parallel::sequential_multiway_merge( + seqs_begin, seqs_end, target, length, comp); +#endif +} + +/*! Multi-way merging front-end. + * \param seqs_begin Begin iterator of iterator pair input sequence. + * \param seqs_end End iterator of iterator pair input sequence. + * \param target Begin iterator out output sequence. + * \param comp Comparator. + * \param length Maximum length to merge. + * \return End iterator of output sequence. + * \pre For each \c i, \c seqs_begin[i].second must be the end marker of the sequence, but also reference the one more sentinel element. + */ +template +RandomAccessIterator3 +multiway_merge_stable_sentinels(RandomAccessIteratorPairIterator seqs_begin, + RandomAccessIteratorPairIterator seqs_end, + RandomAccessIterator3 target, DiffType length, + Comparator comp) +{ +#if STXXL_PARALLEL + return stxxl::parallel::multiway_merge_stable_sentinels( + seqs_begin, seqs_end, target, length, comp); +#else + return stxxl::parallel::sequential_multiway_merge( + seqs_begin, seqs_end, target, length, comp); +#endif +} + +} // namespace potentially_parallel + +STXXL_END_NAMESPACE + +#endif // !STXXL_PARALLEL_HEADER +// vim: et:ts=4:sw=4 diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/base.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/base.h new file mode 100644 index 0000000000..141d51594e --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/base.h @@ -0,0 +1,151 @@ +/*************************************************************************** + * include/stxxl/bits/parallel/base.h + * + * Sequential helper functions. + * Extracted from MCSTL - http://algo2.iti.uni-karlsruhe.de/singler/mcstl/ + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_PARALLEL_BASE_HEADER +#define STXXL_PARALLEL_BASE_HEADER + +#include +#include + +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace parallel { + +/*! + * Alternative to std::not2, typedefs first_argument_type and + * second_argument_type not needed. + */ +template +class binary_negate + : public std::binary_function +{ +protected: + Predicate pred; + +public: + explicit + binary_negate(const Predicate& _pred) : pred(_pred) { } + + bool operator () (const first_argument_type& x, + const second_argument_type& y) const + { + return !pred(x, y); + } +}; + +/*! + * Encode two integers into one mcstl::lcas_t. + * + * \param a First integer, to be encoded in the most-significant \c lcas_t_bits/2 bits. + * \param b Second integer, to be encoded in the least-significant \c lcas_t_bits/2 bits. + * \return mcstl::lcas_t value encoding \c a and \c b. + * \see decode2 + */ +static inline lcas_t encode2(int a, int b) // must all be non-negative, actually +{ + return (((lcas_t)a) << (lcas_t_bits / 2)) | (((lcas_t)b) << 0); +} + +/*! + * Decode two integers from one mcstl::lcas_t. + * + * \param x mcstl::lcas_t to decode integers from. + * \param a First integer, to be decoded from the most-significant \c lcas_t_bits/2 bits of \c x. + * \param b Second integer, to be encoded in the least-significant \c lcas_t_bits/2 bits of \c x. + * \see encode2 + */ +static inline void decode2(lcas_t x, int& a, int& b) +{ + a = (int)((x >> (lcas_t_bits / 2)) & lcas_t_mask); + b = (int)((x >> 0) & lcas_t_mask); +} + +/*! + * Constructs predicate for equality from strict weak ordering predicate + */ +template +class equal_from_less : public std::binary_function +{ +private: + Comparator& comp; + +public: + equal_from_less(Comparator& _comp) : comp(_comp) { } + + bool operator () (const T1& a, const T2& b) + { + //FIXME: wrong in general (T1 != T2) + return !comp(a, b) && !comp(b, a); + } +}; + +/*! + * Compute the median of three referenced elements, according to \c comp. + * + * \param a First iterator. + * \param b Second iterator. + * \param c Third iterator. + * \param comp Comparator. + */ +template +RandomAccessIterator +median_of_three_iterators(RandomAccessIterator a, RandomAccessIterator b, + RandomAccessIterator c, Comparator& comp) +{ + if (comp(*a, *b)) + if (comp(*b, *c)) + return b; + else if (comp(*a, *c)) + return c; + else + return a; + else //just swap a and b + if (comp(*a, *c)) + return a; + else if (comp(*b, *c)) + return c; + else + return b; +} + +/** Similar to std::equal_to, but allows two different types. */ +template +struct equal_to : std::binary_function +{ + bool operator () (const T1& t1, const T2& t2) const + { + return t1 == t2; + } +}; + +/** Similar to std::less, but allows two different types. */ +template +struct less : std::binary_function +{ + bool operator () (const T1& t1, const T2& t2) const + { + return t1 < t2; + } +}; + +} // namespace parallel + +STXXL_END_NAMESPACE + +#endif // !STXXL_PARALLEL_BASE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/compiletime_settings.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/compiletime_settings.h new file mode 100644 index 0000000000..c423ab80e2 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/compiletime_settings.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * include/stxxl/bits/parallel/compiletime_settings.h + * + * Defines on options concerning debugging and performance, at compile-time. + * Extracted from MCSTL - http://algo2.iti.uni-karlsruhe.de/singler/mcstl/ + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_PARALLEL_COMPILETIME_SETTINGS_HEADER +#define STXXL_PARALLEL_COMPILETIME_SETTINGS_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace parallel { + +/** STXXL_PARALLEL_PCALL Macro to produce log message when entering a + * function. + * \param n Input size. + * \see STXXL_VERBOSE_LEVEL */ +#if (STXXL_VERBOSE_LEVEL <= 0) +#define STXXL_PARALLEL_PCALL(n) +#endif + +#if (STXXL_VERBOSE_LEVEL >= 1) +#if STXXL_PARALLEL +#define STXXL_PARALLEL_PCALL(n) \ + STXXL_MSG(" " << __FUNCTION__ << ":\n" \ + "iam = " << omp_get_thread_num() << ", " \ + "n = " << (n) << ", " \ + "num_threads = " << SETTINGS::num_threads); +#else +#define STXXL_PARALLEL_PCALL(n) \ + STXXL_MSG(" " << __FUNCTION__ << ":\n" \ + "iam = single-threaded, " \ + "n = " << (n) << ", " \ + "num_threads = " << SETTINGS::num_threads); +#endif +#endif + +/** First copy the data, sort it locally, and merge it back (0); or copy it + * back after everyting is done (1). + * + * Recommendation: 0 */ +#define STXXL_MULTIWAY_MERGESORT_COPY_LAST 0 + +} // namespace parallel + +STXXL_END_NAMESPACE + +#endif // !STXXL_PARALLEL_COMPILETIME_SETTINGS_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/equally_split.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/equally_split.h new file mode 100644 index 0000000000..77de8e4e49 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/equally_split.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * include/stxxl/bits/parallel/equally_split.h + * + * Function to split a sequence into parts of almost equal size. + * Extracted from MCSTL - http://algo2.iti.uni-karlsruhe.de/singler/mcstl/ + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_PARALLEL_EQUALLY_SPLIT_HEADER +#define STXXL_PARALLEL_EQUALLY_SPLIT_HEADER + +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace parallel { + +/*! + * Split a sequence into parts of almost equal size. + * + * The resulting sequence s of length p+1 contains the splitting positions when + * splitting the range [0,n) into parts of almost equal size (plus minus 1). + * The first entry is 0, the last one n. There may result empty parts. + * + * \param n Number of elements + * \param p Number of parts + * \param s Splitters + * \returns End of splitter sequence, i. e. \c s+p+1 + */ +template +DiffTypeOutputIterator equally_split(DiffType n, thread_index_t p, + DiffTypeOutputIterator s) +{ + DiffType chunk_length = n / p, split = n % p, start = 0; + for (thread_index_t i = 0; i < p; i++) + { + *s++ = start; + start += ((DiffType)i < split) ? (chunk_length + 1) : chunk_length; + } + *s++ = n; + + return s; +} + +} // namespace parallel + +STXXL_END_NAMESPACE + +#endif // !STXXL_PARALLEL_EQUALLY_SPLIT_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/losertree.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/losertree.h new file mode 100644 index 0000000000..218f8dda0d --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/losertree.h @@ -0,0 +1,1074 @@ +/*************************************************************************** + * include/stxxl/bits/parallel/losertree.h + * + * Many generic loser tree variants. + * Extracted from MCSTL - http://algo2.iti.uni-karlsruhe.de/singler/mcstl/ + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2014-2015 Timo Bingmann + * + * 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 STXXL_PARALLEL_LOSERTREE_HEADER +#define STXXL_PARALLEL_LOSERTREE_HEADER + +#include +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace parallel { + +/** + * Guarded loser tree/tournament tree, either copying the whole element into + * the tree structure, or looking up the element via the index. + * + * This is a base class for the LoserTreeCopy\ and \ classes. + * + * Guarding is done explicitly through one flag sup per element, inf is not + * needed due to a better initialization routine. This is a well-performing + * variant. + * + * \tparam ValueType the element type + * \tparam Comparator comparator to use for binary comparisons. + */ +template > +class LoserTreeCopyBase +{ +public: + //! size of counters and array indexes + typedef unsigned int size_type; + //! type of the source field + typedef int source_type; + +protected: + //! Internal representation of a loser tree player/node + struct Loser + { + //! flag, true iff is a virtual maximum sentinel + bool sup; + //! index of source + source_type source; + //! copy of key value of the element in this node + ValueType key; + }; + + //! number of nodes + const size_type ik; + //! log_2(ik) next greater power of 2 + const size_type k; + //! array containing loser tree nodes + Loser* losers; + //! the comparator object + Comparator comp; + //! still have to construct keys + bool first_insert; + +public: + LoserTreeCopyBase(size_type _k, + Comparator _comp = std::less()) + : ik(_k), + k(round_up_to_power_of_two(ik)), + comp(_comp), + first_insert(true) + { + // avoid default-constructing losers[].key + losers = static_cast(operator new (2 * k * sizeof(Loser))); + + for (size_type i = ik - 1; i < k; ++i) + { + losers[i + k].sup = true; + losers[i + k].source = (source_type)(-1); + } + } + + ~LoserTreeCopyBase() + { + for (size_type i = 0; i < (2 * k); ++i) + losers[i].~Loser(); + + delete losers; + } + + void print(std::ostream& os) + { + for (size_type i = 0; i < (k * 2); i++) + os << i << " " << losers[i].key << " from " << losers[i].source << ", " << losers[i].sup << "\n"; + } + + //! return the index of the player with the smallest element. + source_type get_min_source() + { + return losers[0].source; + } + + /** + * Initializes the player source with the element key. + * + * \param key the element to insert + * \param source index of the player + * \param sup flag that determines whether the value to insert is an + * explicit supremum sentinel. + */ + void insert_start(const ValueType& key, source_type source, bool sup) + { + size_type pos = k + source; + + losers[pos].sup = sup; + losers[pos].source = source; + + if (UNLIKELY(first_insert)) + { + // copy construct all keys from this first key + for (size_type i = 0; i < (2 * k); ++i) + new (&(losers[i].key))ValueType(key); + first_insert = false; + } + else + losers[pos].key = key; + } + + /** + * Computes the winner of the competition at player root. Called + * recursively (starting at 0) to build the initial tree. + * + * \param root index of the game to start. + */ + size_type init_winner(size_type root) + { + if (root >= k) + { + return root; + } + else + { + size_type left = init_winner(2 * root); + size_type right = init_winner(2 * root + 1); + if (losers[right].sup || + (!losers[left].sup && !comp(losers[right].key, losers[left].key))) + { //left one is less or equal + losers[root] = losers[right]; + return left; + } + else + { //right one is less + losers[root] = losers[left]; + return right; + } + } + } + + void init() + { + losers[0] = losers[init_winner(1)]; + } +}; + +/** + * Guarded loser tree/tournament tree, either copying the whole element into + * the tree structure, or looking up the element via the index. + * + * Unstable specialization of LoserTreeCopyBase. + * + * Guarding is done explicitly through one flag sup per element, inf is not + * needed due to a better initialization routine. This is a well-performing + * variant. + * + * \tparam ValueType the element type + * \tparam Comparator comparator to use for binary comparisons. + */ +template > +class LoserTreeCopy : public LoserTreeCopyBase +{ +public: + typedef LoserTreeCopyBase base_type; + + typedef typename base_type::size_type size_type; + typedef typename base_type::source_type source_type; + using base_type::k; + using base_type::losers; + using base_type::comp; + + LoserTreeCopy(size_type _k, Comparator _comp = std::less()) + : base_type(_k, _comp) + { } + + // do not pass const reference since key will be used as local variable + void delete_min_insert(ValueType key, bool sup) + { + using std::swap; + + source_type source = losers[0].source; + for (size_type pos = (k + source) / 2; pos > 0; pos /= 2) + { + // the smaller one gets promoted + if (sup || + (!losers[pos].sup && comp(losers[pos].key, key))) + { + // the other one is smaller + swap(losers[pos].sup, sup); + swap(losers[pos].source, source); + swap(losers[pos].key, key); + } + } + + losers[0].sup = sup; + losers[0].source = source; + losers[0].key = key; + } +}; + +/** + * Guarded loser tree/tournament tree, either copying the whole element into + * the tree structure, or looking up the element via the index. + * + * Stable specialization of LoserTreeCopyBase. + * + * Guarding is done explicitly through one flag sup per element, inf is not + * needed due to a better initialization routine. This is a well-performing + * variant. + * + * \tparam ValueType the element type + * \tparam Comparator comparator to use for binary comparisons. + */ +template +class LoserTreeCopy + : public LoserTreeCopyBase +{ +public: + typedef LoserTreeCopyBase base_type; + + typedef typename base_type::size_type size_type; + typedef typename base_type::source_type source_type; + using base_type::k; + using base_type::losers; + using base_type::comp; + + LoserTreeCopy(size_type _k, Comparator _comp = std::less()) + : base_type(_k, _comp) + { } + + // do not pass const reference since key will be used as local variable + void delete_min_insert(ValueType key, bool sup) + { + using std::swap; + + source_type source = losers[0].source; + for (size_type pos = (k + source) / 2; pos > 0; pos /= 2) + { + if ((sup && (!losers[pos].sup || losers[pos].source < source)) || + (!sup && !losers[pos].sup && + ((comp(losers[pos].key, key)) || + (!comp(key, losers[pos].key) && losers[pos].source < source)))) + { + // the other one is smaller + swap(losers[pos].sup, sup); + swap(losers[pos].source, source); + swap(losers[pos].key, key); + } + } + + losers[0].sup = sup; + losers[0].source = source; + losers[0].key = key; + } +}; + +/** Guarded loser tree, either copying the whole element into the tree structure, or looking up the element via the index. + * + * Guarding is done explicitly through one flag sup per element, inf is not needed due to a better initialization routine. + * This is a well-performing variant. + */ +template > +class LoserTreeReference +{ +#undef COPY +#ifdef COPY + #define KEY(i) losers[i].key + #define KEY_SOURCE(i) key +#else + #define KEY(i) keys[losers[i].source] + #define KEY_SOURCE(i) keys[i] +#endif + +private: + struct Loser + { + bool sup; + int source; +#ifdef COPY + T key; +#endif + }; + + unsigned int ik, k; + Loser* losers; +#ifndef COPY + T* keys; +#endif + Comparator comp; + +public: + LoserTreeReference(unsigned int _k, Comparator _comp = std::less()) : comp(_comp) + { + ik = _k; + k = round_up_to_power_of_two(ik); + losers = new Loser[k * 2]; +#ifndef COPY + keys = new T[ik]; +#endif + for (unsigned int i = ik - 1; i < k; i++) + losers[i + k].sup = true; + } + + ~LoserTreeReference() + { + delete[] losers; +#ifndef COPY + delete[] keys; +#endif + } + + void print(std::ostream& os) + { + for (unsigned int i = 0; i < (k * 2); i++) + os << i << " " << KEY(i) << " from " << losers[i].source << ", " << losers[i].sup << "\n"; + } + + int get_min_source() + { + return losers[0].source; + } + + void insert_start(T key, int source, bool sup) + { + unsigned int pos = k + source; + + losers[pos].sup = sup; + losers[pos].source = source; + KEY(pos) = key; + } + + unsigned int init_winner(unsigned int root) + { + if (root >= k) + { + return root; + } + else + { + unsigned int left = init_winner(2 * root); + unsigned int right = init_winner(2 * root + 1); + if (losers[right].sup || + (!losers[left].sup && !comp(KEY(right), KEY(left)))) + { //left one is less or equal + losers[root] = losers[right]; + return left; + } + else + { //right one is less + losers[root] = losers[left]; + return right; + } + } + } + + void init() + { + losers[0] = losers[init_winner(1)]; + } + + void delete_min_insert(T /* key */, bool sup) + { + using std::swap; + + int source = losers[0].source; + for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2) + { + //the smaller one gets promoted + if (sup || + (!losers[pos].sup && comp(KEY(pos), KEY_SOURCE(source)))) + { //the other one is smaller + swap(losers[pos].sup, sup); + swap(losers[pos].source, source); +#ifdef COPY + swap(KEY(pos), KEY_SOURCE(source)); +#endif + } + } + + losers[0].sup = sup; + losers[0].source = source; +#ifdef COPY + KEY(0) = KEY_SOURCE(source); +#endif + } + + void insert_start_stable(T key, int source, bool sup) + { + return insert_start(key, source, sup); + } + + unsigned int init_winner_stable(unsigned int root) + { + if (root >= k) + { + return root; + } + else + { + unsigned int left = init_winner(2 * root); + unsigned int right = init_winner(2 * root + 1); + if (losers[right].sup || + (!losers[left].sup && !comp(KEY(right), KEY(left)))) + { //left one is less or equal + losers[root] = losers[right]; + return left; + } + else + { //right one is less + losers[root] = losers[left]; + return right; + } + } + } + + void init_stable() + { + losers[0] = losers[init_winner_stable(1)]; + } + + void delete_min_insert_stable(T /* key */, bool sup) + { + using std::swap; + + int source = losers[0].source; + for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2) + { + //the smaller one gets promoted, ties are broken by source + if ((sup && (!losers[pos].sup || losers[pos].source < source)) || + (!sup && !losers[pos].sup && + ((comp(KEY(pos), KEY_SOURCE(source))) || + (!comp(KEY_SOURCE(source), KEY(pos)) && losers[pos].source < source)))) + { //the other one is smaller + swap(losers[pos].sup, sup); + swap(losers[pos].source, source); +#ifdef COPY + swap(KEY(pos), KEY_SOURCE(source)); +#endif + } + } + + losers[0].sup = sup; + losers[0].source = source; +#ifdef COPY + KEY(0) = KEY_SOURCE(source); +#endif + } +}; +#undef KEY +#undef KEY_SOURCE + +/** + * Guarded loser tree, using pointers to the elements instead of copying them + * into the tree nodes. + * + * This is a base class for the LoserTreePointer\ and \ classes. + * + * Guarding is done explicitly through one flag sup per element, inf is not + * needed due to a better initialization routine. This is a well-performing + * variant. + */ +template > +class LoserTreePointerBase +{ +public: + //! size of counters and array indexes + typedef typename LoserTreeCopyBase + ::size_type size_type; + //! type of the source field + typedef typename LoserTreeCopyBase + ::source_type source_type; + +protected: + //! Internal representation of a loser tree player/node + struct Loser + { + //! flag, true iff is a virtual maximum sentinel + bool sup; + //! index of source + source_type source; + //! pointer to key value of the element in this node + const ValueType* keyp; + }; + + //! number of nodes + const size_type ik; + //! log_2(ik) next greater power of 2 + const size_type k; + //! array containing loser tree nodes + Loser* losers; + //! the comparator object + Comparator comp; + +public: + LoserTreePointerBase(size_type _k, + Comparator _comp = std::less()) + : ik(_k), + k(round_up_to_power_of_two(ik)), + losers(new Loser[k * 2]), + comp(_comp) + { + for (size_type i = ik - 1; i < k; i++) + { + losers[i + k].sup = true; + losers[i + k].source = (source_type)(-1); + } + } + + ~LoserTreePointerBase() + { + delete[] losers; + } + + void print(std::ostream& os) + { + for (size_type i = 0; i < (k * 2); i++) + os << i << " " << losers[i].keyp << " from " << losers[i].source << ", " << losers[i].sup << "\n"; + } + + //! return the index of the player with the smallest element. + source_type get_min_source() + { + return losers[0].source; + } + + /** + * Initializes the player source with the element key. + * + * \param key the element to insert + * \param source index of the player + * \param sup flag that determines whether the value to insert is an + * explicit supremum sentinel. + */ + void insert_start(const ValueType& key, source_type source, bool sup) + { + size_type pos = k + source; + + losers[pos].sup = sup; + losers[pos].source = source; + losers[pos].keyp = &key; + } + + /** + * Computes the winner of the competition at player root. Called + * recursively (starting at 0) to build the initial tree. + * + * \param root index of the game to start. + */ + size_type init_winner(size_type root) + { + if (root >= k) + { + return root; + } + else + { + size_type left = init_winner(2 * root); + size_type right = init_winner(2 * root + 1); + if (losers[right].sup || + (!losers[left].sup && !comp(*losers[right].keyp, *losers[left].keyp))) + { //left one is less or equal + losers[root] = losers[right]; + return left; + } + else + { //right one is less + losers[root] = losers[left]; + return right; + } + } + } + + void init() + { + losers[0] = losers[init_winner(1)]; + } +}; + +/** + * Guarded loser tree, using pointers to the elements instead of copying them + * into the tree nodes. + * + * Unstable specialization of LoserTreeCopyBase. + * + * Guarding is done explicitly through one flag sup per element, inf is not + * needed due to a better initialization routine. This is a well-performing + * variant. + */ +template > +class LoserTreePointer : public LoserTreePointerBase +{ +public: + typedef LoserTreePointerBase base_type; + + typedef typename base_type::size_type size_type; + typedef typename base_type::source_type source_type; + using base_type::k; + using base_type::losers; + using base_type::comp; + + LoserTreePointer(size_type _k, Comparator _comp = std::less()) + : base_type(_k, _comp) + { } + + void delete_min_insert(const ValueType& key, bool sup) + { + using std::swap; + + const ValueType* keyp = &key; + source_type source = losers[0].source; + for (size_type pos = (k + source) / 2; pos > 0; pos /= 2) + { + //the smaller one gets promoted + if (sup || + (!losers[pos].sup && comp(*losers[pos].keyp, *keyp))) + { //the other one is smaller + swap(losers[pos].sup, sup); + swap(losers[pos].source, source); + swap(losers[pos].keyp, keyp); + } + } + + losers[0].sup = sup; + losers[0].source = source; + losers[0].keyp = keyp; + } +}; + +/** + * Guarded loser tree, using pointers to the elements instead of copying them + * into the tree nodes. + * + * Unstable specialization of LoserTreeCopyBase. + * + * Guarding is done explicitly through one flag sup per element, inf is not + * needed due to a better initialization routine. This is a well-performing + * variant. + */ +template +class LoserTreePointer + : public LoserTreePointerBase +{ +public: + typedef LoserTreePointerBase base_type; + + typedef typename base_type::size_type size_type; + typedef typename base_type::source_type source_type; + using base_type::k; + using base_type::losers; + using base_type::comp; + + LoserTreePointer(size_type _k, Comparator _comp = std::less()) + : base_type(_k, _comp) + { } + + void delete_min_insert(const ValueType& key, bool sup) + { + using std::swap; + + const ValueType* keyp = &key; + source_type source = losers[0].source; + for (size_type pos = (k + source) / 2; pos > 0; pos /= 2) + { + //the smaller one gets promoted, ties are broken by source + if ((sup && (!losers[pos].sup || losers[pos].source < source)) || + (!sup && !losers[pos].sup && + ((comp(*losers[pos].keyp, *keyp)) || + (!comp(*keyp, *losers[pos].keyp) && losers[pos].source < source)))) + { //the other one is smaller + swap(losers[pos].sup, sup); + swap(losers[pos].source, source); + swap(losers[pos].keyp, keyp); + } + } + + losers[0].sup = sup; + losers[0].source = source; + losers[0].keyp = keyp; + } +}; + +/** + * Unguarded loser tree, copying the whole element into the tree structure. + * + * This is a base class for the LoserTreeCopyUnguarded\ and \ + * classes. + * + * No guarding is done, therefore not a single input sequence must run empty. + * This is a very fast variant. + */ +template > +class LoserTreeCopyUnguardedBase : private noncopyable +{ +protected: + //! Internal representation of a loser tree player/node + struct Loser + { + //! index of source + int source; + //! copy of key value of the element in this node + ValueType key; + }; + + //! number of nodes + unsigned int ik; + //! log_2(ik) next greater power of 2 + unsigned int k; + //! array containing loser tree nodes + Loser* losers; + //! the comparator object + Comparator comp; + +public: + LoserTreeCopyUnguardedBase(unsigned int _k, const ValueType& _sentinel, + Comparator _comp = std::less()) + : ik(_k), + k(round_up_to_power_of_two(ik)), + losers(new Loser[k * 2]), + comp(_comp) + { + for (unsigned int i = 0; i < 2 * k; i++) + { + losers[i].source = -1; + losers[i].key = _sentinel; + } + } + + ~LoserTreeCopyUnguardedBase() + { + delete[] losers; + } + + void print(std::ostream& os) + { + for (unsigned int i = 0; i < k + ik; i++) + os << i << " " << losers[i].key << " from " << losers[i].source << "\n"; + } + + //! return the index of the player with the smallest element. + int get_min_source() + { + assert(losers[0].source != -1 && "Data underrun in unguarded merging."); + return losers[0].source; + } + + void insert_start(const ValueType& key, int source) + { + unsigned int pos = k + source; + + losers[pos].source = source; + losers[pos].key = key; + } + + unsigned int init_winner(unsigned int root) + { + if (root >= k) + { + return root; + } + else + { + unsigned int left = init_winner(2 * root); + unsigned int right = init_winner(2 * root + 1); + if (!comp(losers[right].key, losers[left].key)) + { //left one is less or equal + losers[root] = losers[right]; + return left; + } + else + { //right one is less + losers[root] = losers[left]; + return right; + } + } + } + + void init() + { + losers[0] = losers[init_winner(1)]; + } +}; + +template > +class LoserTreeCopyUnguarded : public LoserTreeCopyUnguardedBase +{ +protected: + typedef LoserTreeCopyUnguardedBase base_type; + + using base_type::k; + using base_type::losers; + using base_type::comp; + +public: + LoserTreeCopyUnguarded(unsigned int _k, const ValueType& _sentinel, + Comparator _comp = std::less()) + : base_type(_k, _sentinel, _comp) + { } + + // do not pass const reference since key will be used as local variable + void delete_min_insert(ValueType key) + { + using std::swap; + + int source = losers[0].source; + for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2) + { + // the smaller one gets promoted + if (comp(losers[pos].key, key)) + { + // the other one is smaller + swap(losers[pos].source, source); + swap(losers[pos].key, key); + } + } + + losers[0].source = source; + losers[0].key = key; + } +}; + +template +class LoserTreeCopyUnguarded + : public LoserTreeCopyUnguardedBase +{ +protected: + typedef LoserTreeCopyUnguardedBase base_type; + + using base_type::k; + using base_type::losers; + using base_type::comp; + +public: + LoserTreeCopyUnguarded(unsigned int _k, const ValueType& _sentinel, + Comparator _comp = std::less()) + : base_type(_k, _sentinel, _comp) + { } + + // do not pass const reference since key will be used as local variable + void delete_min_insert(ValueType key) + { + using std::swap; + + int source = losers[0].source; + for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2) + { + if (!comp(key, losers[pos].key) && losers[pos].source < source) + { + // the other one is smaller + swap(losers[pos].source, source); + swap(losers[pos].key, key); + } + } + + losers[0].source = source; + losers[0].key = key; + } +}; + +/** + * Unguarded loser tree, keeping only pointers to the elements in the tree + * structure. + * + * This is a base class for the LoserTreePointerUnguarded\ and \ + * classes. + * + * No guarding is done, therefore not a single input sequence must run empty. + * This is a very fast variant. + */ +template > +class LoserTreePointerUnguardedBase : private noncopyable +{ +protected: + //! Internal representation of a loser tree player/node + struct Loser + { + //! index of source + int source; + //! copy of key value of the element in this node + const ValueType* keyp; + }; + + //! number of nodes + unsigned int ik; + //! log_2(ik) next greater power of 2 + unsigned int k; + //! array containing loser tree nodes + Loser* losers; + //! the comparator object + Comparator comp; + +public: + LoserTreePointerUnguardedBase(unsigned int _k, const ValueType& _sentinel, + Comparator _comp = std::less()) + : ik(_k), + k(round_up_to_power_of_two(ik)), + losers(new Loser[k * 2]), + comp(_comp) + { + for (unsigned int i = ik - 1; i < k; i++) + { + losers[i + k].source = -1; + losers[i + k].keyp = &_sentinel; + } + } + + ~LoserTreePointerUnguardedBase() + { + delete[] losers; + } + + void print(std::ostream& os) + { + for (unsigned int i = 0; i < k + ik; i++) + os << i << " " << *losers[i].keyp << " from " << losers[i].source << "\n"; + } + + int get_min_source() + { + return losers[0].source; + } + + void insert_start(const ValueType& key, int source) + { + unsigned int pos = k + source; + + losers[pos].source = source; + losers[pos].keyp = &key; + } + + unsigned int init_winner(unsigned int root) + { + if (root >= k) + { + return root; + } + else + { + unsigned int left = init_winner(2 * root); + unsigned int right = init_winner(2 * root + 1); + if (!comp(*losers[right].keyp, *losers[left].keyp)) + { //left one is less or equal + losers[root] = losers[right]; + return left; + } + else + { //right one is less + losers[root] = losers[left]; + return right; + } + } + } + + void init() + { + losers[0] = losers[init_winner(1)]; + } +}; + +template > +class LoserTreePointerUnguarded + : public LoserTreePointerUnguardedBase +{ +protected: + typedef LoserTreePointerUnguardedBase base_type; + + using base_type::k; + using base_type::losers; + using base_type::comp; + +public: + LoserTreePointerUnguarded(unsigned int _k, const ValueType& _sentinel, + Comparator _comp = std::less()) + : base_type(_k, _sentinel, _comp) + { } + + void delete_min_insert(const ValueType& key) + { + using std::swap; + + const ValueType* keyp = &key; + int source = losers[0].source; + for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2) + { + //the smaller one gets promoted + if (comp(*losers[pos].keyp, *keyp)) + { + //the other one is smaller + swap(losers[pos].source, source); + swap(losers[pos].keyp, keyp); + } + } + + losers[0].source = source; + losers[0].keyp = keyp; + } +}; + +template +class LoserTreePointerUnguarded + : public LoserTreePointerUnguardedBase +{ +protected: + typedef LoserTreePointerUnguardedBase base_type; + + using base_type::k; + using base_type::losers; + using base_type::comp; + +public: + LoserTreePointerUnguarded(unsigned int _k, const ValueType& _sentinel, + Comparator _comp = std::less()) + : base_type(_k, _sentinel, _comp) + { } + + void delete_min_insert(const ValueType& key) + { + using std::swap; + + const ValueType* keyp = &key; + int source = losers[0].source; + for (unsigned int pos = (k + source) / 2; pos > 0; pos /= 2) + { + //the smaller one gets promoted, ties are broken by source + if (comp(*losers[pos].keyp, *keyp) || + (!comp(*keyp, *losers[pos].keyp) && losers[pos].source < source)) + { + //the other one is smaller + swap(losers[pos].source, source); + swap(losers[pos].keyp, keyp); + } + } + + losers[0].source = source; + losers[0].keyp = keyp; + } +}; + +} // namespace parallel + +STXXL_END_NAMESPACE + +#endif // !STXXL_PARALLEL_LOSERTREE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/merge.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/merge.h new file mode 100644 index 0000000000..b4c80e386a --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/merge.h @@ -0,0 +1,239 @@ +/*************************************************************************** + * include/stxxl/bits/parallel/merge.h + * + * Parallel implementation of std::merge(). + * Extracted from MCSTL - http://algo2.iti.uni-karlsruhe.de/singler/mcstl/ + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_PARALLEL_MERGE_HEADER +#define STXXL_PARALLEL_MERGE_HEADER + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace parallel { + +/*! + * Merge routine being able to merge only the \c max_length smallest elements. + * + * The \c begin iterators are advanced accordingly, they might not reach \c + * end, in contrast to the usual variant. + * + * \param begin1 Begin iterator of first sequence. + * \param end1 End iterator of first sequence. + * \param begin2 Begin iterator of second sequence. + * \param end2 End iterator of second sequence. + * \param target Target begin iterator. + * \param max_length Maximum number of elements to merge. + * \param comp Comparator. + * \return Output end iterator. + */ +template +OutputIterator +merge_advance_usual(RandomAccessIterator1& begin1, RandomAccessIterator1 end1, + RandomAccessIterator2& begin2, RandomAccessIterator2 end2, + OutputIterator target, DiffType max_length, + Comparator comp) +{ + while (begin1 != end1 && begin2 != end2 && max_length > 0) + { + // array1[i1] < array0[i0] + if (comp(*begin2, *begin1)) + *target++ = *begin2++; + else + *target++ = *begin1++; + --max_length; + } + + if (begin1 != end1) + { + target = std::copy(begin1, begin1 + max_length, target); + begin1 += max_length; + } + else + { + target = std::copy(begin2, begin2 + max_length, target); + begin2 += max_length; + } + return target; +} + +/*! + * Merge routine being able to merge only the \c max_length smallest elements. + * + * The \c begin iterators are advanced accordingly, they might not reach \c + * end, in contrast to the usual variant. Specially designed code should allow + * the compiler to generate conditional moves instead of branches. + * + * \param begin1 Begin iterator of first sequence. + * \param end1 End iterator of first sequence. + * \param begin2 Begin iterator of second sequence. + * \param end2 End iterator of second sequence. + * \param target Target begin iterator. + * \param max_length Maximum number of elements to merge. + * \param comp Comparator. + * \return Output end iterator. + */ +template +OutputIterator +merge_advance_movc(RandomAccessIterator1& begin1, RandomAccessIterator1 end1, + RandomAccessIterator2& begin2, RandomAccessIterator2 end2, + OutputIterator target, + DiffType max_length, Comparator comp) +{ + typedef typename std::iterator_traits::value_type ValueType1; + typedef typename std::iterator_traits::value_type ValueType2; + + while (begin1 != end1 && begin2 != end2 && max_length > 0) + { + RandomAccessIterator1 next1 = begin1 + 1; + RandomAccessIterator2 next2 = begin2 + 1; + ValueType1 element1 = *begin1; + ValueType2 element2 = *begin2; + + if (comp(element2, element1)) + { + element1 = element2; + begin2 = next2; + } + else + { + begin1 = next1; + } + + *target = element1; + + ++target; + --max_length; + } + + if (begin1 != end1) + { + target = std::copy(begin1, begin1 + max_length, target); + begin1 += max_length; + } + else + { + target = std::copy(begin2, begin2 + max_length, target); + begin2 += max_length; + } + + return target; +} + +/*! + * Merge routine being able to merge only the \c max_length smallest elements. + * + * The \c begin iterators are advanced accordingly, they might not reach \c + * end, in contrast to the usual variant. Static switch on whether to use the + * conditional-move variant. + * + * \param begin1 Begin iterator of first sequence. + * \param end1 End iterator of first sequence. + * \param begin2 Begin iterator of second sequence. + * \param end2 End iterator of second sequence. + * \param target Target begin iterator. + * \param max_length Maximum number of elements to merge. + * \param comp Comparator. + * \return Output end iterator. + */ +template +OutputIterator +merge_advance(RandomAccessIterator1& begin1, RandomAccessIterator1 end1, + RandomAccessIterator2& begin2, RandomAccessIterator2 end2, + OutputIterator target, + DiffType max_length, Comparator comp) +{ + STXXL_PARALLEL_PCALL(max_length); + + return merge_advance_movc(begin1, end1, begin2, end2, target, max_length, comp); +} + +/*! + * Merge routine fallback to sequential in case the iterators of the two input + * sequences are of different type. + * + * \param begin1 Begin iterator of first sequence. + * \param end1 End iterator of first sequence. + * \param begin2 Begin iterator of second sequence. + * \param end2 End iterator of second sequence. + * \param target Target begin iterator. + * \param max_length Maximum number of elements to merge. + * \param comp Comparator. + * \return Output end iterator. + */ +template +RandomAccessIterator3 +parallel_merge_advance( + RandomAccessIterator1& begin1, RandomAccessIterator1 end1, + RandomAccessIterator2& begin2, RandomAccessIterator2 end2, + // different iterators, parallel implementation not available + RandomAccessIterator3 target, + typename std::iterator_traits::difference_type max_length, + Comparator comp) +{ + return merge_advance(begin1, end1, begin2, end2, target, max_length, comp); +} + +/*! + * Parallel merge routine being able to merge only the \c max_length smallest + * elements. + * + * The \c begin iterators are advanced accordingly, they might not reach \c + * end, in contrast to the usual variant. The functionality is projected onto + * parallel_multiway_merge. + * + * \param begin1 Begin iterator of first sequence. + * \param end1 End iterator of first sequence. + * \param begin2 Begin iterator of second sequence. + * \param end2 End iterator of second sequence. + * \param target Target begin iterator. + * \param max_length Maximum number of elements to merge. + * \param comp Comparator. + * \return Output end iterator. + */ +template +RandomAccessIterator3 +parallel_merge_advance( + RandomAccessIterator1& begin1, RandomAccessIterator1 end1, + RandomAccessIterator1& begin2, RandomAccessIterator1 end2, + RandomAccessIterator3 target, + typename std::iterator_traits::difference_type max_length, + Comparator comp) +{ + std::pair seqs[2] = { + std::make_pair(begin1, end1), std::make_pair(begin2, end2) + }; + RandomAccessIterator3 target_end = parallel_multiway_merge( + seqs, seqs + 2, target, comp, max_length, true, false + ); + + return target_end; +} + +} // namespace parallel + +STXXL_END_NAMESPACE + +#endif // !STXXL_PARALLEL_MERGE_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiseq_selection.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiseq_selection.h new file mode 100644 index 0000000000..57e7599cc0 --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiseq_selection.h @@ -0,0 +1,586 @@ +/*************************************************************************** + * include/stxxl/bits/parallel/multiseq_selection.h + * + * Functions to find elements of a certain global rank in multiple sorted + * sequences. Also serves for splitting such sequence sets. + * + * Extracted from MCSTL - http://algo2.iti.uni-karlsruhe.de/singler/mcstl/ + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_PARALLEL_MULTISEQ_SELECTION_HEADER +#define STXXL_PARALLEL_MULTISEQ_SELECTION_HEADER + +#include +#include +#include + +#include +#include +#include +#include + +STXXL_BEGIN_NAMESPACE + +namespace parallel { + +//! Compare a pair of types lexcigraphically, ascending. +template +class lexicographic + : public std::binary_function, std::pair, bool> +{ +protected: + Comparator& m_comp; + +public: + lexicographic(Comparator& comp) : m_comp(comp) { } + + inline bool operator () (const std::pair& p1, + const std::pair& p2) + { + if (m_comp(p1.first, p2.first)) + return true; + + if (m_comp(p2.first, p1.first)) + return false; + + // firsts are equal + return p1.second < p2.second; + } +}; + +//! Compare a pair of types lexcigraphically, descending. +template +class lexicographic_rev + : public std::binary_function, std::pair, bool> +{ +protected: + Comparator& m_comp; + +public: + lexicographic_rev(Comparator& comp) : m_comp(comp) { } + + inline bool operator () (const std::pair& p1, + const std::pair& p2) + { + if (m_comp(p2.first, p1.first)) + return true; + + if (m_comp(p1.first, p2.first)) + return false; + + // firsts are equal + return p2.second < p1.second; + } +}; + +/*! + * Splits several sorted sequences at a certain global rank, resulting in a + * splitting point for each sequence. The sequences are passed via a sequence + * of random-access iterator pairs, none of the sequences may be empty. If + * there are several equal elements across the split, the ones on the left side + * will be chosen from sequences with smaller number. + * + * \param begin_seqs Begin of the sequence of iterator pairs. + * \param end_seqs End of the sequence of iterator pairs. + * \param rank The global rank to partition at. + * \param begin_offsets A random-access sequence begin where the result will be + * stored in. Each element of the sequence is an iterator that points to the + * first element on the greater part of the respective sequence. + * \param comp The ordering functor, defaults to std::less. + */ +template +void multiseq_partition( + const RanSeqs& begin_seqs, const RanSeqs& end_seqs, + const RankType& rank, + RankIterator begin_offsets, + Comparator comp = std::less< + typename std::iterator_traits + ::value_type::first_type>::value_type + >()) //std::less +{ + STXXL_PARALLEL_PCALL(end_seqs - begin_seqs); + + typedef typename std::iterator_traits + ::value_type::first_type iterator; + typedef typename std::iterator_traits + ::difference_type diff_type; + typedef typename std::iterator_traits + ::value_type value_type; + + typedef std::pair sample_pair; + // comparators for sample_pair + lexicographic lcomp(comp); + lexicographic_rev lrcomp(comp); + + // number of sequences, number of elements in total (possibly including padding) + const diff_type m = std::distance(begin_seqs, end_seqs); + diff_type nmax, n; + RankType N = 0; + + for (diff_type i = 0; i < m; ++i) + { + N += std::distance(begin_seqs[i].first, begin_seqs[i].second); + assert(std::distance(begin_seqs[i].first, begin_seqs[i].second) > 0); + } + + if (rank == N) + { + for (diff_type i = 0; i < m; ++i) + begin_offsets[i] = begin_seqs[i].second; // very end + return; + } + + assert(m != 0 && N != 0 && rank < N); + + diff_type* seqlen = new diff_type[m]; + + seqlen[0] = std::distance(begin_seqs[0].first, begin_seqs[0].second); + nmax = seqlen[0]; + for (diff_type i = 1; i < m; ++i) + { + seqlen[i] = std::distance(begin_seqs[i].first, begin_seqs[i].second); + nmax = std::max(nmax, seqlen[i]); + } + + // pad all lists to this length, at least as long as any ns[i], equality + // iff nmax = 2^k - 1 + diff_type l = round_up_to_power_of_two(nmax + 1) - 1; + + diff_type* a = new diff_type[m], * b = new diff_type[m]; + + for (diff_type i = 0; i < m; ++i) + a[i] = 0, b[i] = l; + + n = l / 2; + + // invariants: + // 0 <= a[i] <= seqlen[i], 0 <= b[i] <= l + +#define S(i) (begin_seqs[i].first) + + // initial partition + + std::vector sample; + + for (diff_type i = 0; i < m; ++i) { + if (n < seqlen[i]) { + // sequence long enough + sample.push_back(sample_pair(S(i)[n], i)); + } + } + + std::sort(sample.begin(), sample.end(), lcomp); + + for (diff_type i = 0; i < m; ++i) { + // conceptual infinity + if (n >= seqlen[i]) { + // sequence too short, conceptual infinity + sample.push_back(sample_pair(S(i)[0] /*dummy element*/, i)); + } + } + + diff_type localrank = rank / l; + + diff_type j; + for (j = 0; j < localrank && ((n + 1) <= seqlen[sample[j].second]); ++j) + a[sample[j].second] += n + 1; + for ( ; j < m; ++j) + b[sample[j].second] -= n + 1; + + // further refinement + + while (n > 0) + { + n /= 2; + + const value_type* lmax = NULL; // impossible to avoid the warning? + for (diff_type i = 0; i < m; ++i) + { + if (a[i] > 0) + { + if (!lmax) + lmax = &(S(i)[a[i] - 1]); + else + { + // max, favor rear sequences + if (!comp(S(i)[a[i] - 1], *lmax)) + lmax = &(S(i)[a[i] - 1]); + } + } + } + + for (diff_type i = 0; i < m; ++i) + { + diff_type middle = (b[i] + a[i]) / 2; + if (lmax && middle < seqlen[i] && comp(S(i)[middle], *lmax)) + a[i] = std::min(a[i] + n + 1, seqlen[i]); + else + b[i] -= n + 1; + } + + diff_type leftsize = 0; + for (diff_type i = 0; i < m; ++i) + leftsize += a[i] / (n + 1); + + diff_type skew = rank / (n + 1) - leftsize; + + if (skew > 0) + { + // move to the left, find smallest + std::priority_queue, + lexicographic_rev > + pq(lrcomp); + + for (diff_type i = 0; i < m; ++i) { + if (b[i] < seqlen[i]) + pq.push(sample_pair(S(i)[b[i]], i)); + } + + for ( ; skew != 0 && !pq.empty(); --skew) + { + diff_type source = pq.top().second; + pq.pop(); + + a[source] = std::min(a[source] + n + 1, seqlen[source]); + b[source] += n + 1; + + if (b[source] < seqlen[source]) + pq.push(sample_pair(S(source)[b[source]], source)); + } + } + else if (skew < 0) + { + // move to the right, find greatest + std::priority_queue, + lexicographic > + pq(lcomp); + + for (diff_type i = 0; i < m; ++i) { + if (a[i] > 0) + pq.push(sample_pair(S(i)[a[i] - 1], i)); + } + + for ( ; skew != 0; ++skew) + { + diff_type source = pq.top().second; + pq.pop(); + + a[source] -= n + 1; + b[source] -= n + 1; + + if (a[source] > 0) + pq.push(sample_pair(S(source)[a[source] - 1], source)); + } + } + } + + // postconditions: a[i] == b[i] in most cases, except when a[i] has been + // clamped because of having reached the boundary + + // now return the result, calculate the offset, compare the keys on both + // edges of the border + + // maximum of left edge, minimum of right edge + value_type* maxleft = NULL, * minright = NULL; + for (diff_type i = 0; i < m; ++i) + { + if (a[i] > 0) + { + if (!maxleft) + { + maxleft = &(S(i)[a[i] - 1]); + } + else + { + // max, favor rear sequences + if (!comp(S(i)[a[i] - 1], *maxleft)) + maxleft = &(S(i)[a[i] - 1]); + } + } + if (b[i] < seqlen[i]) + { + if (!minright) + { + minright = &(S(i)[b[i]]); + } + else + { + // min, favor fore sequences + if (comp(S(i)[b[i]], *minright)) + minright = &(S(i)[b[i]]); + } + } + } + + for (diff_type i = 0; i < m; ++i) + begin_offsets[i] = S(i) + a[i]; + + delete[] seqlen; + delete[] a; + delete[] b; +} + +/*! + * Selects the element at a certain global rank from several sorted sequences. + * + * The sequences are passed via a sequence of random-access iterator pairs, + * none of the sequences may be empty. + * + * \param begin_seqs Begin of the sequence of iterator pairs. + * \param end_seqs End of the sequence of iterator pairs. + * \param rank The global rank to partition at. + * \param offset The rank of the selected element in the global subsequence of + * elements equal to the selected element. If the selected element is unique, + * this number is 0. + * \param comp The ordering functor, defaults to std::less. */ +template +ValueType multiseq_selection(const RanSeqs& begin_seqs, const RanSeqs& end_seqs, + const RankType& rank, + RankType& offset, + Comparator comp = std::less()) +{ + STXXL_PARALLEL_PCALL(end_seqs - begin_seqs); + + typedef typename std::iterator_traits + ::value_type::first_type iterator; + typedef typename std::iterator_traits + ::difference_type diff_type; + + typedef std::pair sample_pair; + // comparators for sample_pair + lexicographic lcomp(comp); + lexicographic_rev lrcomp(comp); + + // number of sequences, number of elements in total (possibly including padding) + const diff_type m = std::distance(begin_seqs, end_seqs); + diff_type nmax, n; + RankType N = 0; + + for (diff_type i = 0; i < m; ++i) + N += std::distance(begin_seqs[i].first, begin_seqs[i].second); + + if (m == 0 || N == 0 || rank < 0 || rank >= N) + // result undefined when there is no data or rank is outside bounds + throw std::exception(); + + diff_type* seqlen = new diff_type[m]; + + seqlen[0] = std::distance(begin_seqs[0].first, begin_seqs[0].second); + nmax = seqlen[0]; + for (diff_type i = 1; i < m; ++i) + { + seqlen[i] = std::distance(begin_seqs[i].first, begin_seqs[i].second); + nmax = std::max(nmax, seqlen[i]); + } + + // pad all lists to this length, at least as long as any ns[i], equliaty iff + // nmax = 2^k - 1 + diff_type l = round_up_to_power_of_two(nmax + 1) - 1; + + diff_type* a = new diff_type[m], * b = new diff_type[m]; + + for (diff_type i = 0; i < m; ++i) + a[i] = 0, b[i] = l; + + n = l / 2; + + // invariants: + // 0 <= a[i] <= seqlen[i], 0 <= b[i] <= l + +#define S(i) (begin_seqs[i].first) + + //initial partition + + std::vector sample; + + for (diff_type i = 0; i < m; ++i) { + if (n < seqlen[i]) + sample.push_back(sample_pair(S(i)[n], i)); + } + + std::sort(sample.begin(), sample.end(), lcomp); + + for (diff_type i = 0; i < m; ++i) { + //conceptual infinity + if (n >= seqlen[i]) + sample.push_back(sample_pair(S(i)[0] /*dummy element*/, i)); + } + + diff_type localrank = rank / l; + + diff_type j; + for (j = 0; j < localrank && ((n + 1) <= seqlen[sample[j].second]); ++j) + a[sample[j].second] += n + 1; + for ( ; j < m; ++j) + b[sample[j].second] -= n + 1; + + // further refinement + + while (n > 0) + { + n /= 2; + + const ValueType* lmax = NULL; + for (diff_type i = 0; i < m; ++i) + { + if (a[i] > 0) + { + if (!lmax) + { + lmax = &(S(i)[a[i] - 1]); + } + else + { + // max + if (comp(*lmax, S(i)[a[i] - 1])) + lmax = &(S(i)[a[i] - 1]); + } + } + } + + for (diff_type i = 0; i < m; ++i) + { + diff_type middle = (b[i] + a[i]) / 2; + if (lmax && middle < seqlen[i] && comp(S(i)[middle], *lmax)) + a[i] = std::min(a[i] + n + 1, seqlen[i]); + else + b[i] -= n + 1; + } + + diff_type leftsize = 0; + for (diff_type i = 0; i < m; ++i) + leftsize += a[i] / (n + 1); + + diff_type skew = rank / (n + 1) - leftsize; + + if (skew > 0) + { + // move to the left, find smallest + std::priority_queue, + lexicographic_rev > + pq(lrcomp); + + for (diff_type i = 0; i < m; ++i) { + if (b[i] < seqlen[i]) + pq.push(sample_pair(S(i)[b[i]], i)); + } + + for ( ; skew != 0 && !pq.empty(); --skew) + { + diff_type source = pq.top().second; + pq.pop(); + + a[source] = std::min(a[source] + n + 1, seqlen[source]); + b[source] += n + 1; + + if (b[source] < seqlen[source]) + pq.push(sample_pair(S(source)[b[source]], source)); + } + } + else if (skew < 0) + { + // move to the right, find greatest + std::priority_queue, + lexicographic > + pq(lcomp); + + for (diff_type i = 0; i < m; ++i) { + if (a[i] > 0) + pq.push(sample_pair(S(i)[a[i] - 1], i)); + } + + for ( ; skew != 0; ++skew) + { + diff_type source = pq.top().second; + pq.pop(); + + a[source] -= n + 1; + b[source] -= n + 1; + + if (a[source] > 0) + pq.push(sample_pair(S(source)[a[source] - 1], source)); + } + } + } + + // postconditions: a[i] == b[i] in most cases, except when a[i] has been + // clamped because of having reached the boundary + + // now return the result, calculate the offset, compare the keys on both + // edges of the border + + // maximum of left edge, minimum of right edge + ValueType* maxleft = NULL, * minright = NULL; + for (diff_type i = 0; i < m; ++i) + { + if (a[i] > 0) + { + if (!maxleft) + { + maxleft = &(S(i)[a[i] - 1]); + } + else + { + // max + if (comp(*maxleft, S(i)[a[i] - 1])) + maxleft = &(S(i)[a[i] - 1]); + } + } + if (b[i] < seqlen[i]) + { + if (!minright) + { + minright = &(S(i)[b[i]]); + } + else + { + // min + if (comp(S(i)[b[i]], *minright)) + minright = &(S(i)[b[i]]); + } + } + } + + // minright is the splitter, in any case + + if (!maxleft || comp(*minright, *maxleft)) + { + // good luck, everything is split unambigiously + offset = 0; + } + else + { + // we have to calculate an offset + offset = 0; + + for (diff_type i = 0; i < m; ++i) + { + diff_type lb = std::lower_bound(S(i), S(i) + seqlen[i], *minright, comp) - S(i); + offset += a[i] - lb; + } + } + + delete[] seqlen; + delete[] a; + delete[] b; + + return *minright; +} + +#undef S + +} // namespace parallel + +STXXL_END_NAMESPACE + +#endif // !STXXL_PARALLEL_MULTISEQ_SELECTION_HEADER diff --git a/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiway_merge.h b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiway_merge.h new file mode 100644 index 0000000000..0608554aae --- /dev/null +++ b/third-party/MQF/ThirdParty/stxxl/include/stxxl/bits/parallel/multiway_merge.h @@ -0,0 +1,1802 @@ +/*************************************************************************** + * include/stxxl/bits/parallel/multiway_merge.h + * + * Implementation of sequential and parallel multiway merge. + * Extracted from MCSTL - http://algo2.iti.uni-karlsruhe.de/singler/mcstl/ + * + * Part of the STXXL. See http://stxxl.sourceforge.net + * + * Copyright (C) 2007 Johannes Singler + * Copyright (C) 2014 Timo Bingmann + * + * 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 STXXL_PARALLEL_MULTIWAY_MERGE_HEADER +#define STXXL_PARALLEL_MULTIWAY_MERGE_HEADER + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && STXXL_DEBUG_ASSERTIONS +#include +typedef SSIZE_T ssize_t; +#endif + +STXXL_BEGIN_NAMESPACE + +namespace parallel { + +//! Length of a sequence described by a pair of iterators. +template +typename std::iterator_traits< + typename RandomAccessIteratorPair::first_type + >::difference_type +iterpair_size(const RandomAccessIteratorPair& p) +{ + return p.second - p.first; +} + +/*! + * Iterator wrapper supporting an implicit supremum at the end of the sequence, + * dominating all comparisons. Deriving from RandomAccessIterator is not + * possible since RandomAccessIterator need not be a class. +*/ +template +class guarded_iterator +{ +public: + //! Our own type + typedef guarded_iterator self_type; + + //! Value type of the iterator + typedef typename std::iterator_traits::value_type value_type; + +protected: + //! Current iterator position. + RandomAccessIterator current; + //! End iterator of the sequence. + RandomAccessIterator end; + //! Comparator. + Comparator& comp; + +public: + /*! + * Constructor. Sets iterator to beginning of sequence. + * \param begin Begin iterator of sequence. + * \param end End iterator of sequence. + * \param comp Comparator provided for associated overloaded compare + * operators. + */ + guarded_iterator(RandomAccessIterator begin, RandomAccessIterator end, + Comparator& comp) + : current(begin), end(end), comp(comp) + { } + + /*! + * Pre-increment operator. + * \return This. + */ + self_type& operator ++ () + { + ++current; + return *this; + } + + /*! + * Dereference operator. + * \return Referenced element. + */ + value_type& operator * () + { + return *current; + } + + /*! + * Convert to wrapped iterator. + * \return Wrapped iterator. + */ + RandomAccessIterator & iterator() + { + return current; + } + + /*! + * Compare two elements referenced by guarded iterators. + * \param bi1 First iterator. + * \param bi2 Second iterator. + * \return \c True if less. + */ + friend bool operator < (self_type& bi1, self_type& bi2) + { + if (bi1.current == bi1.end) // bi1 is sup + return bi2.current == bi2.end; // bi2 is not sup + if (bi2.current == bi2.end) // bi2 is sup + return true; + return bi1.comp(*bi1, *bi2); // normal compare + } + + /*! + * Compare two elements referenced by guarded iterators. + * \param bi1 First iterator. + * \param bi2 Second iterator. + * \return \c True if less equal. + */ + friend bool operator <= (self_type& bi1, self_type& bi2) + { + if (bi2.current == bi2.end) //bi1 is sup + return bi1.current != bi1.end; //bi2 is not sup + if (bi1.current == bi1.end) //bi2 is sup + return false; + return !bi1.comp(*bi2, *bi1); //normal compare + } +}; + +template +class unguarded_iterator +{ +public: + //! Our own type + typedef unguarded_iterator self_type; + + //! Value type of the iterator + typedef typename std::iterator_traits::value_type value_type; + +protected: + //! Current iterator position. + RandomAccessIterator current; + //! Comparator. + Comparator& comp; + +public: + /*! + * Constructor. Sets iterator to beginning of sequence. + * \param begin Begin iterator of sequence. + * param end Unused, only for compatibility. + * \param comp Unused, only for compatibility. + */ + unguarded_iterator(RandomAccessIterator begin, + RandomAccessIterator /* end */, + Comparator& comp) + : current(begin), comp(comp) + { } + + /*! + * Pre-increment operator. + * \return This. + */ + self_type& operator ++ () + { + ++current; + return *this; + } + + /*! + * Dereference operator. + * \return Referenced element. + */ + value_type& operator * () + { + return *current; + } + + /*! + * Convert to wrapped iterator. + * \return Wrapped iterator. + */ + RandomAccessIterator & iterator() + { + return current; + } + + /*! + * Compare two elements referenced by unguarded iterators. + * \param bi1 First iterator. + * \param bi2 Second iterator. + * \return \c True if less. + */ + friend bool operator < (self_type& bi1, self_type& bi2) + { + return bi1.comp(*bi1, *bi2); // normal compare, unguarded + } + + /*! + * Compare two elements referenced by unguarded iterators. + * \param bi1 First iterator. + * \param bi2 Second iterator. + * \return \c True if less equal. + */ + friend bool operator <= (self_type& bi1, self_type& bi2) + { + return !bi1.comp(*bi2, *bi1); // normal compare, unguarded + } +}; + +/*! + * Prepare a set of sequences to be merged without a (end) guard + * + * \param seqs_begin + * \param seqs_end + * \param comp + * \param min_sequence + * \tparam Stable + * \pre (seqs_end - seqs_begin > 0) + */ +template +typename std::iterator_traits< + typename std::iterator_traits::value_type::first_type + >::difference_type +prepare_unguarded(RandomAccessIteratorIterator seqs_begin, + RandomAccessIteratorIterator seqs_end, + Comparator comp, + int& min_sequence) +{ + STXXL_PARALLEL_PCALL(seqs_end - seqs_begin); + + typedef typename std::iterator_traits + ::value_type::first_type RandomAccessIterator; + typedef typename std::iterator_traits + ::value_type value_type; + typedef typename std::iterator_traits + ::difference_type diff_type; + + if ((*seqs_begin).first == (*seqs_begin).second) + { + // empty sequence found, it's the first one + min_sequence = 0; + return -1; + } + + // last element in sequence + value_type min = *((*seqs_begin).second - 1); + min_sequence = 0; + for (RandomAccessIteratorIterator s = seqs_begin + 1; s != seqs_end; ++s) + { + if ((*s).first == (*s).second) + { + // empty sequence found + min_sequence = static_cast(s - seqs_begin); + return -1; + } + const value_type& v = *((*s).second - 1); + if (comp(v, min)) + { + // last element in sequence is strictly smaller + min = v; + min_sequence = static_cast(s - seqs_begin); + } + } + + diff_type overhang_size = 0; + + int s = 0; + for (s = 0; s <= min_sequence; ++s) + { + RandomAccessIterator split; + if (Stable) + split = std::upper_bound(seqs_begin[s].first, seqs_begin[s].second, + min, comp); + else + split = std::lower_bound(seqs_begin[s].first, seqs_begin[s].second, + min, comp); + + overhang_size += seqs_begin[s].second - split; + } + + for ( ; s < (seqs_end - seqs_begin); ++s) + { + RandomAccessIterator split = + std::lower_bound(seqs_begin[s].first, seqs_begin[s].second, + min, comp); + overhang_size += seqs_begin[s].second - split; + } + + return overhang_size; // so many elements will be left over afterwards +} + +/*! + * Prepare a set of sequences to be merged with a (end) guard (sentinel) + * \param seqs_begin + * \param seqs_end + * \param comp + */ +template +typename std::iterator_traits< + typename std::iterator_traits::value_type::first_type + >::difference_type +prepare_unguarded_sentinel(RandomAccessIteratorIterator seqs_begin, + RandomAccessIteratorIterator seqs_end, + Comparator comp) +{ + STXXL_PARALLEL_PCALL(seqs_end - seqs_begin); + + typedef typename std::iterator_traits + ::value_type::first_type RandomAccessIterator; + typedef typename std::iterator_traits + ::value_type value_type; + typedef typename std::iterator_traits + ::difference_type diff_type; + + value_type* max_value = NULL; // last element in sequence + for (RandomAccessIteratorIterator s = seqs_begin; s != seqs_end; ++s) + { + if ((*s).first == (*s).second) + continue; + value_type& v = *((*s).second - 1); //last element in sequence + if (!max_value || comp(*max_value, v)) //strictly greater + max_value = &v; + } + + diff_type overhang_size = 0; + + for (RandomAccessIteratorIterator s = seqs_begin; s != seqs_end; ++s) + { + RandomAccessIterator split = std::lower_bound((*s).first, (*s).second, *max_value, comp); + overhang_size += (*s).second - split; + *((*s).second) = *max_value; //set sentinel + } + + return overhang_size; // so many elements will be left over afterwards +} + +/*! + * Highly efficient 3-way merging procedure. + * + * Merging is done with the algorithm implementation described by Peter + * Sanders. Basically, the idea is to minimize the number of necessary + * comparison after merging an element. The implementation trick that makes + * this fast is that the order of the sequences is stored in the instruction + * pointer (translated into labels in C++). + * + * This works well for merging up to 4 sequences. + * + * Note that making the merging stable does \a not come at a performance hit. + * + * Whether the merging is done guarded or unguarded is selected by the used + * iterator class. + * + * \param seqs_begin Begin iterator of iterator pair input sequence. + * \param seqs_end End iterator of iterator pair input sequence. + * \param target Begin iterator out output sequence. + * \param length Maximum length to merge. + * \param comp Comparator. + * \return End iterator of output sequence. + */ +template