[med-svn] [salmon] 02/02: fix building
Sascha Steinbiss
satta at debian.org
Wed Sep 7 20:40:08 UTC 2016
This is an automated email from the git hooks/post-receive script.
satta pushed a commit to branch master
in repository salmon.
commit 4b16494e279cc81ab1525c9a6a1f5d6e18a2d126
Author: Sascha Steinbiss <satta at debian.org>
Date: Wed Sep 7 20:39:57 2016 +0000
fix building
---
debian/control | 3 +
...007-Remove-unnecessarily-linked-libraries.patch | 8 +-
debian/patches/0008-Remove-salmon_core-lib.patch | 19 +-
...emove-FIND_PACKAGE-for-liblzma-and-libbz2.patch | 2 +-
debian/patches/cmake-typo-fixes | 2 +-
debian/patches/dependency-fix | 51 +-
debian/patches/use_debian_packaged_rapmap.patch | 28 +
debian/rapmap/BooMap.hpp | 193 ++
debian/rapmap/BooPHF.hpp | 1221 ++++++++
debian/rapmap/HitManager.cpp | 700 +++++
debian/rapmap/HitManager.hpp | 109 +
debian/rapmap/IndexHeader.hpp | 77 +
debian/rapmap/JFRaw.hpp | 30 +
debian/rapmap/RapMapConfig.hpp | 14 +
debian/rapmap/RapMapFileSystem.cpp | 37 +
debian/rapmap/RapMapFileSystem.hpp | 15 +
debian/rapmap/RapMapIndex.hpp | 52 +
debian/rapmap/RapMapSAIndex.cpp | 177 ++
debian/rapmap/RapMapSAIndex.hpp | 63 +
debian/rapmap/RapMapSAIndexer.cpp | 731 +++++
debian/rapmap/RapMapUtils.hpp | 825 +++++
debian/rapmap/SACollector.hpp | 580 ++++
debian/rapmap/SASearcher.hpp | 631 ++++
debian/rapmap/ScopedTimer.hpp | 22 +
debian/rapmap/SpinLock.hpp | 25 +
debian/rapmap/bit_array.c | 3160 ++++++++++++++++++++
debian/rapmap/bit_array.h | 552 ++++
debian/rapmap/bit_macros.h | 205 ++
debian/rapmap/kseq.h | 235 ++
debian/rapmap/macros.h | 59 +
debian/rapmap/rank9b.cpp | 67 +
debian/rapmap/rank9b.h | 42 +
debian/rules | 39 +
33 files changed, 9957 insertions(+), 17 deletions(-)
diff --git a/debian/control b/debian/control
index de6d553..b7e48bd 100644
--- a/debian/control
+++ b/debian/control
@@ -8,6 +8,7 @@ Build-Depends: debhelper (>= 9),
cmake,
libboost-filesystem-dev,
libboost-system-dev,
+ libboost-iostreams-dev,
libboost-thread-dev,
libboost-program-options-dev,
libboost-timer-dev,
@@ -22,9 +23,11 @@ Build-Depends: debhelper (>= 9),
libgff-dev,
libstaden-read-dev,
libspdlog-dev,
+ libtclap-dev,
help2man,
sphinx-doc,
python-sphinx | python3-sphinx,
+ python-sphinx-rtd-theme | python3-sphinx-rtd-theme,
zlib1g-dev,
libeigen3-dev,
rapmap
diff --git a/debian/patches/0007-Remove-unnecessarily-linked-libraries.patch b/debian/patches/0007-Remove-unnecessarily-linked-libraries.patch
index b07b979..1bf1677 100644
--- a/debian/patches/0007-Remove-unnecessarily-linked-libraries.patch
+++ b/debian/patches/0007-Remove-unnecessarily-linked-libraries.patch
@@ -9,9 +9,9 @@ Subject: Remove unnecessarily linked libraries
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
-@@ -134,16 +134,12 @@
- libjellyfish-2.0.so
- /usr/lib/libbwa.a
+@@ -135,16 +135,12 @@
+ jellyfish-2.0
+ bwa
m
- ${LIBLZMA_LIBRARIES}
- ${BZIP2_LIBRARIES}
@@ -34,7 +34,7 @@ Subject: Remove unnecessarily linked libraries
##
set(Boost_ADDITIONAL_VERSIONS "1.53" "1.53.0" "1.54" "1.55" "1.56" "1.57.0" "1.58" "1.59" "1.60" "1.61")
-find_package(Boost 1.61.0 COMPONENTS iostreams filesystem system thread timer chrono program_options serialization)
-+find_package(Boost 1.61.0 COMPONENTS filesystem system thread timer program_options)
++find_package(Boost 1.61.0 COMPONENTS iostreams filesystem system thread timer program_options)
message("BOOST_INCLUDEDIR = ${BOOST_INCLUDEDIR}")
message("BOOST_LIBRARYDIR = ${BOOST_LIBRARYDIR}")
message("Boost_FOUND = ${Boost_FOUND}")
diff --git a/debian/patches/0008-Remove-salmon_core-lib.patch b/debian/patches/0008-Remove-salmon_core-lib.patch
index 508f7ec..1bd87fd 100644
--- a/debian/patches/0008-Remove-salmon_core-lib.patch
+++ b/debian/patches/0008-Remove-salmon_core-lib.patch
@@ -8,7 +8,7 @@ Subject: Remove salmon_core lib
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
-@@ -102,11 +102,8 @@ else()
+@@ -103,13 +103,10 @@
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
endif()
@@ -19,9 +19,12 @@ Subject: Remove salmon_core lib
-add_executable(salmon ${SALMON_MAIN_SRCS} ${SALMON_ALIGN_SRCS})
+add_executable(salmon ${SALMON_LIB_SRCS} ${SALMON_MAIN_SRCS} ${SALMON_ALIGN_SRCS})
- add_executable(unitTests ${UNIT_TESTS_SRCS})
+-add_executable(unitTests ${UNIT_TESTS_SRCS})
++add_executable(unitTests ${SALMON_LIB_SRCS} ${UNIT_TESTS_SRCS})
-@@ -123,7 +120,6 @@ set (SUFFARRAY64_LIB ${GAT_SOURCE_DIR}/e
+ #add_executable(salmon-read ${SALMON_READ_SRCS})
+ #set_target_properties(salmon-read PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_LIBPTHREAD -D_PBGZF_USE -fopenmp"
+@@ -124,7 +121,6 @@
# Link the executable
target_link_libraries(salmon
@@ -29,7 +32,15 @@ Subject: Remove salmon_core lib
gff
${PTHREAD_LIB}
${Boost_LIBRARIES}
-@@ -217,7 +213,7 @@ install(DIRECTORY
+@@ -143,7 +139,6 @@
+
+ # Link the executable
+ target_link_libraries(unitTests
+- salmon_core
+ gff
+ ${PTHREAD_LIB}
+ ${Boost_LIBRARIES}
+@@ -218,7 +213,7 @@
# install(FILES ${Boost_LIBRARIES}
# DESTINATION ${INSTALL_LIB_DIR})
diff --git a/debian/patches/0009-Remove-FIND_PACKAGE-for-liblzma-and-libbz2.patch b/debian/patches/0009-Remove-FIND_PACKAGE-for-liblzma-and-libbz2.patch
index 09ae7fe..a7b815c 100644
--- a/debian/patches/0009-Remove-FIND_PACKAGE-for-liblzma-and-libbz2.patch
+++ b/debian/patches/0009-Remove-FIND_PACKAGE-for-liblzma-and-libbz2.patch
@@ -9,7 +9,7 @@ As these seem not to be required.
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
-@@ -188,53 +188,53 @@ if (NOT ZLIB_FOUND)
+@@ -188,53 +188,53 @@
message (FATAL_ERROR "zlib must be installed before configuration & building can proceed")
endif()
diff --git a/debian/patches/cmake-typo-fixes b/debian/patches/cmake-typo-fixes
index 4c9a96d..782a0df 100644
--- a/debian/patches/cmake-typo-fixes
+++ b/debian/patches/cmake-typo-fixes
@@ -2,7 +2,7 @@ Author: Michael R. Crusoe <crusoe at ucdavis.edu>
Description: fix upstream's typos
--- a/src/SalmonQuantify.cpp
+++ b/src/SalmonQuantify.cpp
-@@ -1985,7 +1985,7 @@ int salmonQuantify(int argc, char* argv[
+@@ -1985,7 +1985,7 @@
(
"maxOcc,m",
po::value<int>(&(memOptions->max_occ))->default_value(200),
diff --git a/debian/patches/dependency-fix b/debian/patches/dependency-fix
index 68a8c5d..2f50a41 100644
--- a/debian/patches/dependency-fix
+++ b/debian/patches/dependency-fix
@@ -484,7 +484,15 @@ Description: Use Debian version of dependencies, don't download them
${ZLIB_INCLUDE_DIR}
${TBB_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
-@@ -107,7 +103,7 @@
+@@ -71,6 +67,7 @@
+ set ( UNIT_TESTS_SRCS
+ ${GAT_SOURCE_DIR}/tests/UnitTests.cpp
+ FragmentLengthDistribution.cpp
++ xxhash.c
+ )
+
+
+@@ -107,7 +104,7 @@
endif()
# Build the Salmon library
@@ -493,19 +501,48 @@ Description: Use Debian version of dependencies, don't download them
# Build the salmon executable
add_executable(salmon ${SALMON_MAIN_SRCS} ${SALMON_ALIGN_SRCS})
-@@ -131,12 +127,12 @@
+@@ -131,12 +128,12 @@
+ gff
+ ${PTHREAD_LIB}
+ ${Boost_LIBRARIES}
+- ${GAT_SOURCE_DIR}/external/install/lib/libstaden-read.a
++ staden-read
+ ${ZLIB_LIBRARY}
+- ${SUFFARRAY_LIB}
+- ${SUFFARRAY64_LIB}
+- ${GAT_SOURCE_DIR}/external/install/lib/libjellyfish-2.0.a
+- ${GAT_SOURCE_DIR}/external/install/lib/libbwa.a
++ divsufsort
++ divsufsort64
++ jellyfish-2.0
++ bwa
+ m
+ ${LIBLZMA_LIBRARIES}
+ ${BZIP2_LIBRARIES}
+@@ -154,12 +151,12 @@
gff
${PTHREAD_LIB}
${Boost_LIBRARIES}
- ${GAT_SOURCE_DIR}/external/install/lib/libstaden-read.a
-+ libstaden-read.so
++ staden-read
${ZLIB_LIBRARY}
- ${SUFFARRAY_LIB}
- ${SUFFARRAY64_LIB}
+- ${SUFFARRAY_LIB}
+- ${SUFFARRAY64_LIB}
- ${GAT_SOURCE_DIR}/external/install/lib/libjellyfish-2.0.a
- ${GAT_SOURCE_DIR}/external/install/lib/libbwa.a
-+ libjellyfish-2.0.so
-+ /usr/lib/libbwa.a
++ divsufsort
++ divsufsort64
++ jellyfish-2.0
++ bwa
m
${LIBLZMA_LIBRARIES}
${BZIP2_LIBRARIES}
+@@ -167,7 +164,7 @@
+ ${LIBSALMON_LINKER_FLAGS}
+ ${NON_APPLECLANG_LIBS}
+ ${FAST_MALLOC_LIB}
+- )
++)
+
+ ### No need for this, I think
+ ## This ensures that the salmon executable should work with or without `make install`
diff --git a/debian/patches/use_debian_packaged_rapmap.patch b/debian/patches/use_debian_packaged_rapmap.patch
index 8c12145..98303f2 100644
--- a/debian/patches/use_debian_packaged_rapmap.patch
+++ b/debian/patches/use_debian_packaged_rapmap.patch
@@ -31,6 +31,34 @@
)
set (SALMON_ALIGN_SRCS
+@@ -64,6 +54,15 @@
+ SGSmooth.cpp
+ )
+
++set (RAPMAP_EMBED_SRCS
++bit_array.c
++HitManager.cpp
++RapMapFileSystem.cpp
++RapMapSAIndex.cpp
++RapMapSAIndexer.cpp
++rank9b.cpp
++)
++
+ set ( UNIT_TESTS_SRCS
+ ${GAT_SOURCE_DIR}/tests/UnitTests.cpp
+ FragmentLengthDistribution.cpp
+@@ -104,9 +103,9 @@
+ endif()
+
+ # Build the salmon executable
+-add_executable(salmon ${SALMON_LIB_SRCS} ${SALMON_MAIN_SRCS} ${SALMON_ALIGN_SRCS})
++add_executable(salmon ${SALMON_LIB_SRCS} ${SALMON_MAIN_SRCS} ${SALMON_ALIGN_SRCS} ${RAPMAP_EMBED_SRCS})
+
+-add_executable(unitTests ${SALMON_LIB_SRCS} ${UNIT_TESTS_SRCS})
++add_executable(unitTests ${SALMON_LIB_SRCS} ${UNIT_TESTS_SRCS} ${RAPMAP_EMBED_SRCS})
+
+ #add_executable(salmon-read ${SALMON_READ_SRCS})
+ #set_target_properties(salmon-read PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_LIBPTHREAD -D_PBGZF_USE -fopenmp"
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -161,7 +161,7 @@
diff --git a/debian/rapmap/BooMap.hpp b/debian/rapmap/BooMap.hpp
new file mode 100644
index 0000000..e2056c6
--- /dev/null
+++ b/debian/rapmap/BooMap.hpp
@@ -0,0 +1,193 @@
+#ifndef __BOO_MAP__
+#define __BOO_MAP__
+
+#include "BooPHF.hpp"
+
+#include "cereal/types/vector.hpp"
+#include "cereal/types/utility.hpp"
+#include "cereal/archives/binary.hpp"
+
+#include <fstream>
+#include <vector>
+#include <iterator>
+#include <type_traits>
+
+#include <sys/stat.h>
+
+// adapted from :
+// http://stackoverflow.com/questions/34875315/implementation-my-own-list-and-iterator-stl-c
+template <typename Iter>
+class KeyIterator {
+public:
+ typedef KeyIterator<Iter> self_type;
+ typedef typename std::iterator_traits<Iter>::value_type::first_type value_type;
+ typedef value_type& reference;
+ typedef value_type* pointer;
+ typedef std::forward_iterator_tag iterator_category;
+ typedef int64_t difference_type;
+
+ KeyIterator(Iter first) : curr_(first) {}
+ KeyIterator operator++() { KeyIterator i = *this; curr_++; return i; }
+ KeyIterator operator++(int) { ++curr_; return *this; }
+ reference operator*() { return curr_->first; }
+ pointer operator->() { return &(curr_->first); }
+ bool operator==(const self_type& rhs) { return curr_ == rhs.curr_; }
+ bool operator!=(const self_type& rhs) { return curr_ != rhs.curr_; }
+ bool operator<(const self_type& rhs) { return curr_ < rhs.curr_; }
+ bool operator<=(const self_type& rhs) { return curr_ <= rhs.curr_; }
+
+private:
+ Iter curr_;
+};
+
+template <typename KeyT, typename ValueT>
+class BooMap {
+public:
+ using HasherT = boomphf::SingleHashFunctor<KeyT>;
+ using BooPHFT = boomphf::mphf<KeyT, HasherT>;
+ using IteratorT = typename std::vector<std::pair<KeyT, ValueT>>::iterator;
+
+ BooMap() : built_(false) {}
+ void add(KeyT&& k, ValueT&& v) {
+ data_.emplace_back(k, v);
+ }
+
+ bool build(int nthreads=1) {
+ size_t numElem = data_.size();
+ KeyIterator<decltype(data_.begin())> kb(data_.begin());
+ KeyIterator<decltype(data_.begin())> ke(data_.end());
+ auto keyIt = boomphf::range(kb, ke);
+ BooPHFT* ph = new BooPHFT(numElem, keyIt, nthreads);
+ boophf_.reset(ph);
+ std::cerr << "reordering keys and values to coincide with phf ... ";
+ std::vector<size_t> inds; inds.reserve(data_.size());
+ for (size_t i = 0; i < data_.size(); ++i) {
+ inds.push_back(ph->lookup(data_[i].first));
+ }
+ reorder_destructive_(inds.begin(), inds.end(), data_.begin());
+ std::cerr << "done\n";
+ built_ = true;
+ return built_;
+ }
+
+ inline IteratorT find(const KeyT& k) {
+ auto ind = boophf_->lookup(k);
+ return (ind < data_.size()) ? (data_[ind].first == k ? data_.begin() + ind : data_.end()) : data_.end();
+ }
+
+ /**
+ * NOTE: This function *assumes* that the key is in the hash.
+ * If it isn't, you'll get back a random element!
+ */
+ inline ValueT& operator[](const KeyT& k) {
+ auto ind = boophf_->lookup(k);
+ return (ind < data_.size() ? data_[ind].second : data_[0].second);
+ }
+
+ inline IteratorT begin() { return data_.begin(); }
+ inline IteratorT end() { return data_.end(); }
+ inline IteratorT cend() const { return data_.cend(); }
+ inline IteratorT cbegin() const { return data_.cbegin(); }
+
+ void save(const std::string& ofileBase) {
+ if (built_) {
+ std::string hashFN = ofileBase + ".bph";
+ // save the perfect hash function
+ {
+ std::ofstream os(hashFN, std::ios::binary);
+ if (!os.is_open()) {
+ std::cerr << "BooM: unable to open output file [" << hashFN << "]; exiting!\n";
+ std::exit(1);
+ }
+ boophf_->save(os);
+ os.close();
+ }
+ // and the values
+ std::string dataFN = ofileBase + ".val";
+ {
+ std::ofstream valStream(dataFN, std::ios::binary);
+ if (!valStream.is_open()) {
+ std::cerr << "BooM: unable to open output file [" << dataFN << "]; exiting!\n";
+ std::exit(1);
+ }
+ {
+ cereal::BinaryOutputArchive outArchive(valStream);
+ outArchive(data_);
+ }
+ valStream.close();
+ }
+ }
+ }
+
+ void load(const std::string& ofileBase) {
+ std::string hashFN = ofileBase + ".bph";
+ std::string dataFN = ofileBase + ".val";
+
+ if ( !FileExists_(hashFN.c_str()) ) {
+ std::cerr << "BooM: Looking for perfect hash function file [" << hashFN << "], which doesn't exist! exiting.\n";
+ std::exit(1);
+ }
+ if ( !FileExists_(dataFN.c_str()) ) {
+ std::cerr << "BooM: Looking for key-value file [" << dataFN << "], which doesn't exist! exiting.\n";
+ std::exit(1);
+ }
+
+ // load the perfect hash function
+ {
+ boophf_.reset(new BooPHFT);
+ std::ifstream is(hashFN, std::ios::binary);
+ boophf_->load(is);
+ is.close();
+ }
+ // and the values
+ {
+ std::ifstream dataStream(dataFN, std::ios::binary);
+ {
+ cereal::BinaryInputArchive inArchive(dataStream);
+ inArchive(data_);
+ }
+ dataStream.close();
+ }
+ built_ = true;
+ }
+
+private:
+ // Taken from http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
+ bool FileExists_(const char *path) {
+ struct stat fileStat;
+ if ( stat(path, &fileStat) ) {
+ return false;
+ }
+ if ( !S_ISREG(fileStat.st_mode) ) {
+ return false;
+ }
+ return true;
+ }
+
+ // From : http://stackoverflow.com/questions/838384/reorder-vector-using-a-vector-of-indices
+ template< typename order_iterator, typename value_iterator >
+ void reorder_destructive_( order_iterator order_begin, order_iterator order_end, value_iterator v ) {
+ using value_t = typename std::iterator_traits< value_iterator >::value_type;
+ using index_t = typename std::iterator_traits< order_iterator >::value_type;
+ using diff_t = typename std::iterator_traits< order_iterator >::difference_type;
+
+ diff_t remaining = order_end - 1 - order_begin;
+ for ( index_t s = index_t(); remaining > 0; ++ s ) {
+ index_t d = order_begin[s];
+ if ( d == (diff_t) -1 ) continue;
+ -- remaining;
+ value_t temp = v[s];
+ for ( index_t d2; d != s; d = d2 ) {
+ std::swap( temp, v[d] );
+ std::swap( order_begin[d], d2 = (diff_t) -1 );
+ -- remaining;
+ }
+ v[s] = temp;
+ }
+ }
+
+ bool built_;
+ std::vector<std::pair<KeyT, ValueT>> data_;
+ std::unique_ptr<BooPHFT> boophf_{nullptr};
+};
+#endif // __BOO_MAP__
diff --git a/debian/rapmap/BooPHF.hpp b/debian/rapmap/BooPHF.hpp
new file mode 100644
index 0000000..64b11c7
--- /dev/null
+++ b/debian/rapmap/BooPHF.hpp
@@ -0,0 +1,1221 @@
+// BooPHF library
+// intended to be a minimal perfect hash function with fast and low memory construction, at the cost of (slightly) higher bits/elem than other state of the art libraries once built.
+// should work with arbitray large number of elements, based on a cascade of "collision-free" bit arrays
+
+#ifndef __BOO_PHF__
+#define __BOO_PHF__
+
+#include <stdio.h>
+#include <climits>
+#include <stdlib.h>
+#include <iostream>
+#include <math.h>
+
+#include <array>
+#include <unordered_map>
+#include <vector>
+#include <assert.h>
+#include <sys/time.h>
+#include <string.h>
+#include <memory> // for make_shared
+
+
+namespace boomphf {
+
+////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark utils
+////////////////////////////////////////////////////////////////
+
+ inline unsigned int popcount_32(unsigned int x)
+ {
+ unsigned int m1 = 0x55555555;
+ unsigned int m2 = 0x33333333;
+ unsigned int m4 = 0x0f0f0f0f;
+ unsigned int h01 = 0x01010101;
+ x -= (x >> 1) & m1; /* put count of each 2 bits into those 2 bits */
+ x = (x & m2) + ((x >> 2) & m2); /* put count of each 4 bits in */
+ x = (x + (x >> 4)) & m4; /* put count of each 8 bits in partie droite 4bit piece*/
+ return (x * h01) >> 24; /* returns left 8 bits of x + (x<<8) + ... */
+ }
+
+
+ inline unsigned int popcount_64(uint64_t x)
+ {
+ unsigned int low = x & 0xffffffff ;
+ unsigned int high = ( x >> 32LL) & 0xffffffff ;
+
+ return (popcount_32(low) + popcount_32(high));
+ }
+
+
+ ///// progress bar
+ class Progress
+ {
+ public:
+ int timer_mode;
+ struct timeval timestamp;
+ double heure_debut, heure_actuelle ;
+ std::string message;
+
+ uint64_t done;
+ uint64_t todo;
+ int subdiv ; // progress printed every 1/subdiv of total to do
+ double partial;
+ int _nthreads;
+ std::vector<double > partial_threaded;
+ std::vector<uint64_t > done_threaded;
+
+ double steps ; //steps = todo/subidv
+
+ void init(uint64_t ntasks, const char * msg,int nthreads =1)
+ {
+ _nthreads = nthreads;
+ message = std::string(msg);
+ gettimeofday(×tamp, NULL);
+ heure_debut = timestamp.tv_sec +(timestamp.tv_usec/1000000.0);
+
+ //fprintf(stderr,"| %-*s |\n",98,msg);
+
+ todo= ntasks;
+ done = 0;
+ partial =0;
+
+ partial_threaded.resize(_nthreads);
+ done_threaded.resize(_nthreads);
+
+ for (int ii=0; ii<_nthreads;ii++) partial_threaded[ii]=0;
+ for (int ii=0; ii<_nthreads;ii++) done_threaded[ii]=0;
+ subdiv= 1000;
+ steps = (double)todo / (double)subdiv;
+
+ if(!timer_mode)
+ {
+ fprintf(stderr,"[");fflush(stderr);
+ }
+ }
+
+ void finish()
+ {
+ set(todo);
+ if(timer_mode)
+ fprintf(stderr,"\n");
+ else
+ fprintf(stderr,"]\n");
+
+ fflush(stderr);
+ todo= 0;
+ done = 0;
+ partial =0;
+
+ }
+ void finish_threaded()// called by only one of the threads
+ {
+ done = 0;
+ double rem = 0;
+ for (int ii=0; ii<_nthreads;ii++) done += (done_threaded[ii] );
+ for (int ii=0; ii<_nthreads;ii++) partial += (partial_threaded[ii] );
+
+ finish();
+
+ }
+ void inc(uint64_t ntasks_done)
+ {
+ done += ntasks_done;
+ partial += ntasks_done;
+
+
+ while(partial >= steps)
+ {
+ if(timer_mode)
+ {
+ gettimeofday(×tamp, NULL);
+ heure_actuelle = timestamp.tv_sec +(timestamp.tv_usec/1000000.0);
+ double elapsed = heure_actuelle - heure_debut;
+ double speed = done / elapsed;
+ double rem = (todo-done) / speed;
+ if(done>todo) rem=0;
+ int min_e = (int)(elapsed / 60) ;
+ elapsed -= min_e*60;
+ int min_r = (int)(rem / 60) ;
+ rem -= min_r*60;
+
+ fprintf(stderr,"%c[%s] %-5.3g%% elapsed: %3i min %-2.0f sec remaining: %3i min %-2.0f sec",13,
+ message.c_str(),
+ 100*(double)done/todo,
+ min_e,elapsed,min_r,rem);
+
+ }
+ else
+ {
+ fprintf(stderr,"-");fflush(stderr);
+ }
+ partial -= steps;
+ }
+
+
+ }
+
+ void inc(uint64_t ntasks_done, int tid) //threads collaborate to this same progress bar
+ {
+ partial_threaded[tid] += ntasks_done;
+ done_threaded[tid] += ntasks_done;
+ while(partial_threaded[tid] >= steps)
+ {
+ if(timer_mode)
+ {
+ struct timeval timet;
+ double now;
+ gettimeofday(&timet, NULL);
+ now = timet.tv_sec +(timet.tv_usec/1000000.0);
+ uint64_t total_done = 0;
+ for (int ii=0; ii<_nthreads;ii++) total_done += (done_threaded[ii] );
+ double elapsed = now - heure_debut;
+ double speed = total_done / elapsed;
+ double rem = (todo-total_done) / speed;
+ if(total_done > todo) rem =0;
+ int min_e = (int)(elapsed / 60) ;
+ elapsed -= min_e*60;
+ int min_r = (int)(rem / 60) ;
+ rem -= min_r*60;
+
+ fprintf(stderr,"%c[%s] %-5.3g%% elapsed: %3i min %-2.0f sec remaining: %3i min %-2.0f sec",13,
+ message.c_str(),
+ 100*(double)total_done/todo,
+ min_e,elapsed,min_r,rem);
+ }
+ else
+ {
+ fprintf(stderr,"-");fflush(stderr);
+ }
+ partial_threaded[tid] -= steps;
+
+ }
+
+ }
+
+ void set(uint64_t ntasks_done)
+ {
+ if(ntasks_done > done)
+ inc(ntasks_done-done);
+ }
+ Progress () : timer_mode(0) {}
+ //include timer, to print ETA ?
+ };
+
+
+
+////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark hasher
+////////////////////////////////////////////////////////////////
+
+ typedef std::array<uint64_t,10> hash_set_t;
+ typedef std::array<uint64_t,2> hash_pair_t;
+
+
+
+ template <typename Item> class HashFunctors
+ {
+ public:
+
+ /** Constructor.
+ * \param[in] nbFct : number of hash functions to be used
+ * \param[in] seed : some initialization code for defining the hash functions. */
+ HashFunctors ()
+ {
+ _nbFct = 7; // use 7 hash func
+ _user_seed = 0;
+ generate_hash_seed ();
+ }
+
+ //return one hash
+ uint64_t operator () (const Item& key, size_t idx) const { return hash64 (key, _seed_tab[idx]); }
+
+ uint64_t hashWithSeed(const Item& key, uint64_t seed) const { return hash64 (key, seed); }
+
+ //this one returns all the 7 hashes
+ //maybe use xorshift instead, for faster hash compute
+ hash_set_t operator () (const Item& key)
+ {
+ hash_set_t hset;
+
+ for(size_t ii=0;ii<10; ii++)
+ {
+ hset[ii] = hash64 (key, _seed_tab[ii]);
+ }
+ return hset;
+ }
+
+ private:
+
+
+ inline static uint64_t hash64 (Item key, uint64_t seed)
+ {
+ uint64_t hash = seed;
+ hash ^= (hash << 7) ^ key * (hash >> 3) ^ (~((hash << 11) + (key ^ (hash >> 5))));
+ hash = (~hash) + (hash << 21);
+ hash = hash ^ (hash >> 24);
+ hash = (hash + (hash << 3)) + (hash << 8);
+ hash = hash ^ (hash >> 14);
+ hash = (hash + (hash << 2)) + (hash << 4);
+ hash = hash ^ (hash >> 28);
+ hash = hash + (hash << 31);
+
+ return hash;
+ }
+
+ /* */
+ void generate_hash_seed ()
+ {
+ static const uint64_t rbase[MAXNBFUNC] =
+ {
+ 0xAAAAAAAA55555555ULL, 0x33333333CCCCCCCCULL, 0x6666666699999999ULL, 0xB5B5B5B54B4B4B4BULL,
+ 0xAA55AA5555335533ULL, 0x33CC33CCCC66CC66ULL, 0x6699669999B599B5ULL, 0xB54BB54B4BAA4BAAULL,
+ 0xAA33AA3355CC55CCULL, 0x33663366CC99CC99ULL
+ };
+
+ for (size_t i=0; i<MAXNBFUNC; ++i) { _seed_tab[i] = rbase[i]; }
+ for (size_t i=0; i<MAXNBFUNC; ++i) { _seed_tab[i] = _seed_tab[i] * _seed_tab[(i+3) % MAXNBFUNC] + _user_seed ; }
+ }
+
+ size_t _nbFct;
+
+ static const size_t MAXNBFUNC = 10;
+ uint64_t _seed_tab[MAXNBFUNC];
+ uint64_t _user_seed;
+ };
+
+/* alternative hash functor based on xorshift, taking a single hash functor as input.
+we need this 2-functors scheme because HashFunctors won't work with unordered_map.
+(rayan)
+*/
+
+ // wrapper around HashFunctors to return only one value instead of 7
+ template <typename Item> class SingleHashFunctor
+ {
+ public:
+ uint64_t operator () (const Item& key, uint64_t seed=0xAAAAAAAA55555555ULL) const { return hashFunctors.hashWithSeed(key, seed); }
+
+ private:
+ HashFunctors<Item> hashFunctors;
+ };
+
+
+
+ template <typename Item, class SingleHasher_t> class XorshiftHashFunctors
+ {
+ /* Xorshift128*
+ Written in 2014 by Sebastiano Vigna (vigna at acm.org)
+
+ To the extent possible under law, the author has dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ See <http://creativecommons.org/publicdomain/zero/1.0/>. */
+ /* This is the fastest generator passing BigCrush without
+ systematic failures, but due to the relatively short period it is
+ acceptable only for applications with a mild amount of parallelism;
+ otherwise, use a xorshift1024* generator.
+
+ The state must be seeded so that it is not everywhere zero. If you have
+ a nonzero 64-bit seed, we suggest to pass it twice through
+ MurmurHash3's avalanching function. */
+
+ // uint64_t s[ 2 ];
+
+ uint64_t next(uint64_t * s) {
+ uint64_t s1 = s[ 0 ];
+ const uint64_t s0 = s[ 1 ];
+ s[ 0 ] = s0;
+ s1 ^= s1 << 23; // a
+ return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0; // b, c
+ }
+
+ public:
+
+
+ uint64_t h0(hash_pair_t & s, const Item& key )
+ {
+ s[0] = singleHasher (key, 0xAAAAAAAA55555555ULL);
+ return s[0];
+ }
+
+ uint64_t h1(hash_pair_t & s, const Item& key )
+ {
+ s[1] = singleHasher (key, 0x33333333CCCCCCCCULL);
+ return s[1];
+ }
+
+
+ //return next hash an update state s
+ uint64_t next(hash_pair_t & s ) {
+ uint64_t s1 = s[ 0 ];
+ const uint64_t s0 = s[ 1 ];
+ s[ 0 ] = s0;
+ s1 ^= s1 << 23; // a
+ return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0; // b, c
+ }
+
+ //this one returns all the hashes
+ hash_set_t operator () (const Item& key)
+ {
+ uint64_t s[ 2 ];
+
+ hash_set_t hset;
+
+ hset[0] = singleHasher (key, 0xAAAAAAAA55555555ULL);
+ hset[1] = singleHasher (key, 0x33333333CCCCCCCCULL);
+
+ s[0] = hset[0];
+ s[1] = hset[1];
+
+ for(size_t ii=2;ii< 10 /* it's much better have a constant here, for inlining; this loop is super performance critical*/; ii++)
+ {
+ hset[ii] = next(s);
+ }
+
+ return hset;
+ }
+ private:
+ SingleHasher_t singleHasher;
+ };
+
+
+////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark iterators
+////////////////////////////////////////////////////////////////
+
+ template <typename Iterator>
+ struct iter_range
+ {
+ iter_range(Iterator b, Iterator e)
+ : m_begin(b)
+ , m_end(e)
+ {}
+
+ Iterator begin() const
+ { return m_begin; }
+
+ Iterator end() const
+ { return m_end; }
+
+ Iterator m_begin, m_end;
+ };
+
+ template <typename Iterator>
+ iter_range<Iterator> range(Iterator begin, Iterator end)
+ {
+ return iter_range<Iterator>(begin, end);
+ }
+
+////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark BitVector
+////////////////////////////////////////////////////////////////
+
+ class bitVector {
+
+ public:
+
+ bitVector() : _size(0)
+ {
+ _bitArray = nullptr;
+ }
+
+ bitVector(uint64_t n) : _size(n)
+ {
+ _nchar = (1ULL+n/64ULL);
+ _bitArray = (uint64_t *) calloc (_nchar,sizeof(uint64_t));
+ }
+
+ ~bitVector()
+ {
+ if(_bitArray != nullptr)
+ free(_bitArray);
+ }
+
+ //copy constructor
+ bitVector(bitVector const &r)
+ {
+ _size = r._size;
+ _nchar = r._nchar;
+ _ranks = r._ranks;
+ _bitArray = (uint64_t *) calloc (_nchar,sizeof(uint64_t));
+ memcpy(_bitArray, r._bitArray, _nchar*sizeof(uint64_t) );
+ }
+
+ // Copy assignment operator
+ bitVector &operator=(bitVector const &r)
+ {
+ if (&r != this)
+ {
+ _size = r._size;
+ _nchar = r._nchar;
+ _ranks = r._ranks;
+ if(_bitArray != nullptr)
+ free(_bitArray);
+ _bitArray = (uint64_t *) calloc (_nchar,sizeof(uint64_t));
+ memcpy(_bitArray, r._bitArray, _nchar*sizeof(uint64_t) );
+ }
+ return *this;
+ }
+
+ // Move assignment operator
+ bitVector &operator=(bitVector &&r)
+ {
+ //printf("bitVector move assignment \n");
+ if (&r != this)
+ {
+ if(_bitArray != nullptr)
+ free(_bitArray);
+
+ _size = std::move (r._size);
+ _nchar = std::move (r._nchar);
+ _ranks = std::move (r._ranks);
+ _bitArray = r._bitArray;
+ r._bitArray = nullptr;
+ }
+ return *this;
+ }
+ // Move constructor
+ bitVector(bitVector &&r) : _bitArray ( nullptr),_size(0)
+ {
+ *this = std::move(r);
+ }
+
+
+ void resize(uint64_t newsize)
+ {
+ //printf("bitvector resize from %llu bits to %llu \n",_size,newsize);
+ _nchar = (1ULL+newsize/64ULL);
+ _bitArray = (uint64_t *) realloc(_bitArray,_nchar*sizeof(uint64_t));
+ _size = newsize;
+ }
+
+ size_t size() const
+ {
+ return _size;
+ }
+
+ uint64_t bitSize() const {return (_nchar*64ULL + _ranks.capacity()*64ULL );}
+
+ //clear whole array
+ void clear()
+ {
+ memset(_bitArray,0,_nchar*sizeof(uint64_t));
+ }
+
+ //clear collisions in interval, only works with start and size multiple of 64
+ void clearCollisions(uint64_t start, size_t size, bitVector * cc)
+ {
+ assert( (start & 63) ==0);
+ assert( (size & 63) ==0);
+ uint64_t ids = (start/64ULL);
+ for(uint64_t ii =0; ii< (size/64ULL); ii++ )
+ {
+ _bitArray[ids+ii] = _bitArray[ids+ii] & (~ (cc->get64(ii)) );
+ }
+
+ cc->clear();
+ }
+
+
+ //clear interval, only works with start and size multiple of 64
+ void clear(uint64_t start, size_t size)
+ {
+ assert( (start & 63) ==0);
+ assert( (size & 63) ==0);
+ memset(_bitArray + (start/64ULL),0,(size/64ULL)*sizeof(uint64_t));
+ }
+
+ //for debug purposes
+ void print() const
+ {
+ printf("bit array of size %llu: \n", _size);
+ for(uint64_t ii = 0; ii< _size; ii++)
+ {
+ if(ii%10==0)
+ printf(" (%llu) ",ii);
+ int val = (_bitArray[ii >> 6] >> (ii & 63 ) ) & 1;
+ printf("%i",val);
+ }
+ printf("\n");
+
+ printf("rank array : size %lu \n",_ranks.size());
+ for (uint64_t ii = 0; ii< _ranks.size(); ii++)
+ {
+ printf("%llu: %llu, ",ii,_ranks[ii]);
+ }
+ printf("\n");
+ }
+
+ //return value at pos
+ uint64_t operator[](uint64_t pos) const
+ {
+ return (_bitArray[pos >> 6ULL] >> (pos & 63 ) ) & 1;
+ }
+
+ //atomically return old val and set to 1
+ uint64_t atomic_test_and_set(uint64_t pos)
+ {
+ uint64_t oldval = __sync_fetch_and_or (_bitArray + (pos >> 6), (uint64_t) (1ULL << (pos & 63)) );
+
+ return ( oldval >> (pos & 63 ) ) & 1;
+ }
+
+
+ uint64_t get(uint64_t pos) const
+ {
+ return (*this)[pos];
+ }
+
+ uint64_t get64(uint64_t cell64) const
+ {
+ return _bitArray[cell64];
+ }
+
+ //set bit pos to 1
+ void set(uint64_t pos)
+ {
+ assert(pos<_size);
+ //_bitArray [pos >> 6] |= (1ULL << (pos & 63) ) ;
+ __sync_fetch_and_or (_bitArray + (pos >> 6ULL), (1ULL << (pos & 63)) );
+ }
+
+ //set bit pos to 0
+ void reset(uint64_t pos)
+ {
+ //_bitArray [pos >> 6] &= ~(1ULL << (pos & 63) ) ;
+ __sync_fetch_and_and (_bitArray + (pos >> 6ULL), ~(1ULL << (pos & 63) ));
+ }
+
+ //return value of last rank
+ // add offset to all ranks computed
+ uint64_t build_ranks(uint64_t offset =0)
+ {
+ _ranks.reserve(2+ _size/_nb_bits_per_rank_sample);
+
+ uint64_t curent_rank = offset;
+ for (size_t ii = 0; ii < _nchar; ii++) {
+ if (((ii*64) % _nb_bits_per_rank_sample) == 0) {
+ _ranks.push_back(curent_rank);
+ }
+ curent_rank += popcount_64(_bitArray[ii]);
+ }
+
+ return curent_rank;
+ }
+
+ uint64_t rank(uint64_t pos) const
+ {
+ uint64_t word_idx = pos / 64ULL;
+ uint64_t word_offset = pos % 64;
+ uint64_t block = pos / _nb_bits_per_rank_sample;
+ uint64_t r = _ranks[block];
+ for (uint64_t w = block * _nb_bits_per_rank_sample / 64; w < word_idx; ++w) {
+ r += popcount_64( _bitArray[w] );
+ }
+ uint64_t mask = (uint64_t(1) << word_offset ) - 1;
+ r += popcount_64( _bitArray[word_idx] & mask);
+
+ return r;
+ }
+
+
+ void save(std::ostream& os) const
+ {
+ os.write(reinterpret_cast<char const*>(&_size), sizeof(_size));
+ os.write(reinterpret_cast<char const*>(&_nchar), sizeof(_nchar));
+ os.write(reinterpret_cast<char const*>(_bitArray), (std::streamsize)(sizeof(uint64_t) * _nchar));
+ size_t sizer = _ranks.size();
+ os.write(reinterpret_cast<char const*>(&sizer), sizeof(size_t));
+ os.write(reinterpret_cast<char const*>(_ranks.data()), (std::streamsize)(sizeof(_ranks[0]) * _ranks.size()));
+ }
+
+ void load(std::istream& is)
+ {
+ is.read(reinterpret_cast<char*>(&_size), sizeof(_size));
+ is.read(reinterpret_cast<char*>(&_nchar), sizeof(_nchar));
+ this->resize(_size);
+ is.read(reinterpret_cast<char *>(_bitArray), (std::streamsize)(sizeof(uint64_t) * _nchar));
+
+ size_t sizer;
+ is.read(reinterpret_cast<char *>(&sizer), sizeof(size_t));
+ _ranks.resize(sizer);
+ is.read(reinterpret_cast<char*>(_ranks.data()), (std::streamsize)(sizeof(_ranks[0]) * _ranks.size()));
+ }
+
+
+ protected:
+ uint64_t* _bitArray;
+ //uint64_t* _bitArray;
+ uint64_t _size;
+ uint64_t _nchar;
+
+ // epsilon = 64 / _nb_bits_per_rank_sample bits
+ // additional size for rank is epsilon * _size
+ static const uint64_t _nb_bits_per_rank_sample = 512; //512 seems ok
+ std::vector<uint64_t> _ranks;
+ };
+
+////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark level
+////////////////////////////////////////////////////////////////
+
+ class level{
+ public:
+ level(){ }
+
+ ~level() {
+ }
+
+ uint64_t get(uint64_t hash_raw)
+ {
+ uint64_t hashi = hash_raw % hash_domain;
+ return bitset.get(hashi);
+ }
+
+ uint64_t idx_begin;
+ uint64_t hash_domain;
+ bitVector bitset;
+ };
+
+
+////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark mphf
+////////////////////////////////////////////////////////////////
+
+
+#define NBBUFF 10000
+
+ template<typename Range,typename Iterator>
+ struct thread_args
+ {
+ void * boophf;
+ Range const * range;
+ std::shared_ptr<void> it_p; /* used to be "Iterator it" but because of fastmode, iterator is polymorphic; TODO: think about whether it should be a unique_ptr actually */
+ std::shared_ptr<void> until_p; /* to cache the "until" variable */
+ int level;
+ };
+
+ //forward declaration
+
+ template <typename elem_t, typename Hasher_t, typename Range, typename it_type>
+ void * thread_processLevel(void * args);
+
+
+ /* Hasher_t returns a single hash when operator()(elem_t key) is called.
+ if used with XorshiftHashFunctors, it must have the following operator: operator()(elem_t key, uint64_t seed) */
+ template <typename elem_t, typename Hasher_t>
+ class mphf {
+
+ /* this mechanisms gets P hashes out of Hasher_t */
+ typedef XorshiftHashFunctors<elem_t,Hasher_t> MultiHasher_t ;
+ // typedef HashFunctors<elem_t> MultiHasher_t; // original code (but only works for int64 keys) (seems to be as fast as the current xorshift)
+ //typedef IndepHashFunctors<elem_t,Hasher_t> MultiHasher_t; //faster than xorshift
+
+ public:
+ mphf() : _built(false)
+ {}
+
+
+ ~mphf()
+ {
+
+ }
+
+
+ // allow perc_elem_loaded elements to be loaded in ram for faster construction (default 3%), set to 0 to desactivate
+ template <typename Range>
+ mphf( size_t n, Range const& input_range,int num_thread = 1, double gamma = 2.0 , bool progress =true, float perc_elem_loaded = 0.03) :
+ _gamma(gamma), _hash_domain(size_t(ceil(double(n) * gamma))), _nelem(n), _num_thread(num_thread), _percent_elem_loaded_for_fastMode (perc_elem_loaded), _withprogress(progress)
+ {
+ if(n ==0) return;
+
+ if(_percent_elem_loaded_for_fastMode > 0.0 )
+ _fastmode =true;
+
+ setup();
+
+ if(_withprogress)
+ {
+ _progressBar.timer_mode=1;
+
+ if(_fastmode)
+ _progressBar.init( _nelem * (_fastModeLevel+1) + ( _nelem * pow(_proba_collision,_fastModeLevel)) * (_nb_levels-(_fastModeLevel+1)) ,"Building BooPHF",num_thread);
+ else
+ _progressBar.init( _nelem * _nb_levels ,"Building BooPHF");
+ }
+
+ uint64_t offset = 0;
+ for(int ii = 0; ii< _nb_levels; ii++)
+ {
+ _tempBitset = new bitVector(_levels[ii].hash_domain); // temp collision bitarray for this level
+
+ processLevel(input_range,ii);
+
+ _levels[ii].bitset.clearCollisions(0 , _levels[ii].hash_domain , _tempBitset);
+
+ offset = _levels[ii].bitset.build_ranks(offset);
+
+ delete _tempBitset;
+ }
+
+ if(_withprogress)
+ _progressBar.finish_threaded();
+
+
+ _lastbitsetrank = offset ;
+
+ //printf("used temp ram for construction : %lli MB \n",setLevelFastmode.capacity()* sizeof(elem_t) /1024ULL/1024ULL);
+
+ std::vector<elem_t>().swap(setLevelFastmode); // clear setLevelFastmode reallocating
+
+
+ pthread_mutex_destroy(&_mutex);
+
+ _built = true;
+ }
+
+
+ uint64_t lookup(elem_t elem)
+ {
+ if(! _built) return ULLONG_MAX;
+
+ //auto hashes = _hasher(elem);
+ uint64_t non_minimal_hp,minimal_hp;
+
+
+ hash_pair_t bbhash; int level;
+ uint64_t level_hash = getLevel(bbhash,elem,&level);
+
+ if( level == (_nb_levels-1))
+ {
+ auto in_final_map = _final_hash.find (elem);
+ if ( in_final_map == _final_hash.end() )
+ {
+ //elem was not in orignal set of keys
+ return ULLONG_MAX; // means elem not in set
+ }
+ else
+ {
+ minimal_hp = in_final_map->second + _lastbitsetrank;
+ return minimal_hp;
+ }
+// minimal_hp = _final_hash[elem] + _lastbitsetrank;
+// return minimal_hp;
+ }
+ else
+ {
+ non_minimal_hp = level_hash % _levels[level].hash_domain; // in fact non minimal hp would be + _levels[level]->idx_begin
+ }
+
+ minimal_hp = _levels[level].bitset.rank(non_minimal_hp );
+
+ return minimal_hp;
+ }
+
+ uint64_t nbKeys() const
+ {
+ return _nelem;
+ }
+
+ uint64_t totalBitSize()
+ {
+
+ uint64_t totalsizeBitset = 0;
+ for(int ii=0; ii<_nb_levels; ii++)
+ {
+ totalsizeBitset += _levels[ii].bitset.bitSize();
+ }
+
+ uint64_t totalsize = totalsizeBitset + _final_hash.size()*42*8 ; // unordered map takes approx 42B per elem [personal test] (42B with uint64_t key, would be larger for other type of elem)
+
+ printf("Bitarray %12llu bits (%.2f %%) (array + ranks )\n",
+ totalsizeBitset, 100*(float)totalsizeBitset/totalsize);
+ printf("final hash %12lu bits (%.2f %%) (nb in final hash %lu)\n",
+ _final_hash.size()*42*8, 100*(float)(_final_hash.size()*42*8)/totalsize,
+ _final_hash.size() );
+ return totalsize;
+ }
+
+ template <typename Iterator> //typename Range,
+ void pthread_processLevel( std::vector<elem_t> & buffer , std::shared_ptr<Iterator> shared_it, std::shared_ptr<Iterator> until_p, int i)
+ {
+ uint64_t nb_done =0;
+ int tid = __sync_fetch_and_add (&_nb_living, 1);
+ auto until = *until_p;
+ uint64_t inbuff =0;
+
+
+
+ for (bool isRunning=true; isRunning ; )
+ {
+
+ //safely copy n items into buffer
+ pthread_mutex_lock(&_mutex);
+ for(; inbuff<NBBUFF && (*shared_it)!=until; ++(*shared_it))
+ {
+ buffer[inbuff]= *(*shared_it); inbuff++;
+ }
+ if((*shared_it)==until) isRunning =false;
+ pthread_mutex_unlock(&_mutex);
+
+
+ //do work on the n elems of the buffer
+ for(uint64_t ii=0; ii<inbuff ; ii++)
+ {
+ elem_t val = buffer[ii];
+
+ //auto hashes = _hasher(val);
+ hash_pair_t bbhash; int level;
+ uint64_t level_hash = getLevel(bbhash,val,&level, i);
+
+ if(level == i) //insert into lvl i
+ {
+ __sync_fetch_and_add(& _cptLevel,1);
+
+ if(i == _fastModeLevel && _fastmode)
+ {
+ uint64_t idxl2 = __sync_fetch_and_add(& _idxLevelsetLevelFastmode,1);
+ //si depasse taille attendue pour setLevelFastmode, fall back sur slow mode mais devrait pas arriver si hash ok et proba avec nous
+ if(idxl2>= setLevelFastmode.size())
+ _fastmode = false;
+ else
+ setLevelFastmode[idxl2] = val; // create set for fast mode
+ }
+
+ //insert to level i+1 : either next level of the cascade or final hash if last level reached
+ if(i == _nb_levels-1) //stop cascade here, insert into exact hash
+ {
+ uint64_t hashidx = __sync_fetch_and_add (& _hashidx, 1);
+
+ pthread_mutex_lock(&_mutex); //see later if possible to avoid this, mais pas bcp item vont la
+ // calc rank de fin precedent level qq part, puis init hashidx avec ce rank, direct minimal, pas besoin inser ds bitset et rank
+ _final_hash[val] = hashidx;
+ pthread_mutex_unlock(&_mutex);
+ }
+ else
+ {
+ //computes next hash
+
+ if ( level == 0)
+ level_hash = _hasher.h0(bbhash,val);
+ else if ( level == 1)
+ level_hash = _hasher.h1(bbhash,val);
+ else
+ {
+ level_hash = _hasher.next(bbhash);
+ }
+ insertIntoLevel(level_hash,i); //should be safe
+ }
+ }
+
+ nb_done++;
+ if((nb_done&1023) ==0 && _withprogress) {_progressBar.inc(nb_done,tid);nb_done=0; }
+
+ }
+
+ inbuff = 0;
+ }
+
+ }
+
+
+ void save(std::ostream& os) const
+ {
+
+ os.write(reinterpret_cast<char const*>(&_gamma), sizeof(_gamma));
+ os.write(reinterpret_cast<char const*>(&_nb_levels), sizeof(_nb_levels));
+ os.write(reinterpret_cast<char const*>(&_lastbitsetrank), sizeof(_lastbitsetrank));
+ os.write(reinterpret_cast<char const*>(&_nelem), sizeof(_nelem));
+ for(int ii=0; ii<_nb_levels; ii++)
+ {
+ _levels[ii].bitset.save(os);
+ }
+
+ //save final hash
+ size_t final_hash_size = _final_hash.size();
+
+ os.write(reinterpret_cast<char const*>(&final_hash_size), sizeof(size_t));
+
+
+ // typename std::unordered_map<elem_t,uint64_t,Hasher_t>::iterator
+ for (auto it = _final_hash.begin(); it != _final_hash.end(); ++it )
+ {
+ os.write(reinterpret_cast<char const*>(&(it->first)), sizeof(elem_t));
+ os.write(reinterpret_cast<char const*>(&(it->second)), sizeof(uint64_t));
+ }
+
+ }
+
+ void load(std::istream& is)
+ {
+
+ is.read(reinterpret_cast<char*>(&_gamma), sizeof(_gamma));
+ is.read(reinterpret_cast<char*>(&_nb_levels), sizeof(_nb_levels));
+ is.read(reinterpret_cast<char*>(&_lastbitsetrank), sizeof(_lastbitsetrank));
+ is.read(reinterpret_cast<char*>(&_nelem), sizeof(_nelem));
+
+ _levels.resize(_nb_levels);
+
+
+ for(int ii=0; ii<_nb_levels; ii++)
+ {
+ //_levels[ii].bitset = new bitVector();
+ _levels[ii].bitset.load(is);
+ }
+
+
+
+ //mini setup, recompute size of each level
+ _proba_collision = 1.0 - pow(((_gamma*(double)_nelem -1 ) / (_gamma*(double)_nelem)),_nelem-1);
+ uint64_t previous_idx =0;
+ _hash_domain = (size_t) (ceil(double(_nelem) * _gamma)) ;
+ for(int ii=0; ii<_nb_levels; ii++)
+ {
+ //_levels[ii] = new level();
+ _levels[ii].idx_begin = previous_idx;
+ _levels[ii].hash_domain = (( (uint64_t) (_hash_domain * pow(_proba_collision,ii)) + 63) / 64 ) * 64;
+ if(_levels[ii].hash_domain == 0 )
+ _levels[ii].hash_domain = 64 ;
+ previous_idx += _levels[ii].hash_domain;
+ }
+
+ //restore final hash
+
+ _final_hash.clear();
+ size_t final_hash_size ;
+
+ is.read(reinterpret_cast<char *>(&final_hash_size), sizeof(size_t));
+
+ for(unsigned int ii=0; ii<final_hash_size; ii++)
+ {
+ elem_t key;
+ uint64_t value;
+
+ is.read(reinterpret_cast<char *>(&key), sizeof(elem_t));
+ is.read(reinterpret_cast<char *>(&value), sizeof(uint64_t));
+
+ _final_hash[key] = value;
+ }
+ _built = true;
+ }
+
+
+ private :
+
+ void setup()
+ {
+ pthread_mutex_init(&_mutex, NULL);
+
+
+ if(_fastmode)
+ setLevelFastmode.resize(_percent_elem_loaded_for_fastMode * (double)_nelem );
+
+ _proba_collision = 1.0 - pow(((_gamma*(double)_nelem -1 ) / (_gamma*(double)_nelem)),_nelem-1);
+
+ double sum_geom =_gamma * ( 1.0 + _proba_collision / (1.0 - _proba_collision));
+ // printf("proba collision %f sum_geom %f \n",_proba_collision,sum_geom);
+
+ _nb_levels = 25;
+ _levels.resize(_nb_levels);
+
+ //build levels
+ uint64_t previous_idx =0;
+ for(int ii=0; ii<_nb_levels; ii++)
+ {
+
+ _levels[ii].idx_begin = previous_idx;
+
+ // round size to nearest superior multiple of 64, makes it easier to clear a level
+ _levels[ii].hash_domain = (( (uint64_t) (_hash_domain * pow(_proba_collision,ii)) + 63) / 64 ) * 64;
+ if(_levels[ii].hash_domain == 0 ) _levels[ii].hash_domain = 64 ;
+ previous_idx += _levels[ii].hash_domain;
+
+ //printf("build level %i bit array : start %12llu, size %12llu ",ii,_levels[ii]->idx_begin,_levels[ii]->hash_domain );
+ //printf(" expected elems : %.2f %% total \n",100.0*pow(_proba_collision,ii));
+
+ }
+
+ for(int ii=0; ii<_nb_levels; ii++)
+ {
+ if(pow(_proba_collision,ii) < _percent_elem_loaded_for_fastMode)
+ {
+ _fastModeLevel = ii;
+ // printf("fast mode level : %i \n",ii);
+ break;
+ }
+ }
+
+
+ }
+
+
+ //compute level and returns hash of last level reached
+ uint64_t getLevel(hash_pair_t & bbhash, elem_t val,int * res_level, int maxlevel = 100)
+ {
+ int level = 0;
+ uint64_t hash_raw=0;
+
+ for (int ii=0; ii<(_nb_levels-1) && ii < maxlevel ; ii++ )
+ {
+
+ //calc le hash suivant
+ if ( ii == 0)
+ hash_raw = _hasher.h0(bbhash,val);
+ else if ( ii == 1)
+ hash_raw = _hasher.h1(bbhash,val);
+ else
+ {
+ hash_raw = _hasher.next(bbhash);
+ }
+
+
+ if( _levels[ii].get(hash_raw) )
+ {
+ break;
+ }
+
+ level++;
+ }
+
+ *res_level = level;
+ return hash_raw;
+ }
+
+
+ //insert into bitarray
+ void insertIntoLevel(uint64_t level_hash, int i)
+ {
+ uint64_t hashl = level_hash % _levels[i].hash_domain;
+
+ if( _levels[i].bitset.atomic_test_and_set(hashl) )
+ {
+ _tempBitset->atomic_test_and_set(hashl);
+ }
+
+ }
+
+
+ //loop to insert into level i
+ template <typename Range>
+ void processLevel(Range const& input_range,int i)
+ {
+ ////alloc the bitset for this level
+ _levels[i].bitset = bitVector(_levels[i].hash_domain); ;
+
+ _cptLevel = 0;
+ _hashidx = 0;
+ _idxLevelsetLevelFastmode =0;
+ _nb_living =0;
+ //create threads
+ pthread_t *tab_threads= new pthread_t [_num_thread];
+ typedef decltype(input_range.begin()) it_type;
+ thread_args<Range, it_type> t_arg; // meme arg pour tous
+ t_arg.boophf = this;
+ t_arg.range = &input_range;
+ t_arg.it_p = std::static_pointer_cast<void>(std::make_shared<it_type>(input_range.begin()));
+ t_arg.until_p = std::static_pointer_cast<void>(std::make_shared<it_type>(input_range.end()));
+
+ t_arg.level = i;
+ if(i >= (_fastModeLevel+1) && _fastmode)
+ {
+ auto data_iterator = boomphf::range(static_cast<const elem_t*>( &setLevelFastmode[0]), static_cast<const elem_t*>( (&setLevelFastmode[0]) +setLevelFastmode.size()));
+ typedef decltype(data_iterator.begin()) fastmode_it_type;
+ t_arg.it_p = std::static_pointer_cast<void>(std::make_shared<fastmode_it_type>(data_iterator.begin()));
+ t_arg.until_p = std::static_pointer_cast<void>(std::make_shared<fastmode_it_type>(data_iterator.end()));
+
+ /* we'd like to do t_arg.it = data_iterator.begin() but types are different;
+ so, casting to (void*) because of that; and we remember the type in the template */
+
+ for(int ii=0;ii<_num_thread;ii++)
+ pthread_create (&tab_threads[ii], NULL, thread_processLevel<elem_t, Hasher_t, Range, fastmode_it_type>, &t_arg); //&t_arg[ii]
+ }
+ else
+ {
+ for(int ii=0;ii<_num_thread;ii++)
+ pthread_create (&tab_threads[ii], NULL, thread_processLevel<elem_t, Hasher_t, Range, decltype(input_range.begin())>, &t_arg); //&t_arg[ii]
+ }
+ //joining
+ for(int ii=0;ii<_num_thread;ii++)
+ {
+ pthread_join(tab_threads[ii], NULL);
+ }
+ // printf("\ngoing to level %i : %llu elems %.2f %% expected : %.2f %% \n",i,_cptLevel,100.0* _cptLevel/(float)_nelem,100.0* pow(_proba_collision,i) );
+
+ if(i == _fastModeLevel) //shrink to actual number of elements in set
+ {
+ //printf("resize setLevelFastmode to %lli \n",_idxLevelsetLevelFastmode);
+ setLevelFastmode.resize(_idxLevelsetLevelFastmode);
+ }
+ delete [] tab_threads;
+ }
+
+ private:
+ //level ** _levels;
+ std::vector<level> _levels;
+ int _nb_levels;
+ MultiHasher_t _hasher;
+ bitVector * _tempBitset;
+
+ double _gamma;
+ uint64_t _hash_domain;
+ uint64_t _nelem;
+ std::unordered_map<elem_t,uint64_t,Hasher_t> _final_hash;
+ Progress _progressBar;
+ int _nb_living;
+ int _num_thread;
+ uint64_t _hashidx;
+ double _proba_collision;
+ uint64_t _lastbitsetrank;
+ uint64_t _idxLevelsetLevelFastmode;
+ uint64_t _cptLevel;
+
+ // fast build mode , requires that _percent_elem_loaded_for_fastMode % elems are loaded in ram
+ float _percent_elem_loaded_for_fastMode ;
+ bool _fastmode;
+ std::vector< elem_t > setLevelFastmode;
+ int _fastModeLevel;
+ bool _withprogress;
+ bool _built;
+ public:
+ pthread_mutex_t _mutex;
+ };
+
+////////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark threading
+////////////////////////////////////////////////////////////////
+
+
+ template <typename elem_t, typename Hasher_t, typename Range, typename it_type>
+ void * thread_processLevel(void * args)
+ {
+ if(args ==NULL) return NULL;
+
+ thread_args<Range,it_type> *targ = (thread_args<Range,it_type>*) args;
+
+ mphf<elem_t, Hasher_t> * obw = (mphf<elem_t, Hasher_t > *) targ->boophf;
+ int level = targ->level;
+ std::vector<elem_t> buffer;
+ buffer.resize(NBBUFF);
+
+ pthread_mutex_t * mutex = & obw->_mutex;
+
+ pthread_mutex_lock(mutex); // from comment above: "//get starting iterator for this thread, must be protected (must not be currently used by other thread to copy elems in buff)"
+ std::shared_ptr<it_type> startit = std::static_pointer_cast<it_type>(targ->it_p);
+ std::shared_ptr<it_type> until_p = std::static_pointer_cast<it_type>(targ->until_p);
+ pthread_mutex_unlock(mutex);
+
+ obw->pthread_processLevel(buffer, startit, until_p, level);
+
+ return NULL;
+ }
+}
+
+#endif //__BOO_PHF__
diff --git a/debian/rapmap/HitManager.cpp b/debian/rapmap/HitManager.cpp
new file mode 100644
index 0000000..b5fc9e7
--- /dev/null
+++ b/debian/rapmap/HitManager.cpp
@@ -0,0 +1,700 @@
+#include "HitManager.hpp"
+#include "BooMap.hpp"
+#include <type_traits>
+
+namespace rapmap {
+ namespace hit_manager {
+ // Return hits from processedHits where position constraints
+ // match maxDist
+ bool collectHitsSimple(std::vector<ProcessedHit>& processedHits,
+ uint32_t readLen,
+ uint32_t maxDist,
+ std::vector<QuasiAlignment>& hits,
+ MateStatus mateStatus){
+ bool foundHit{false};
+ // One processed hit per transcript
+ for (auto& ph : processedHits) {
+ auto tid = ph.tid;
+ std::sort(ph.tqvec.begin(), ph.tqvec.end(),
+ [](const TxpQueryPos& x, const TxpQueryPos& y) -> bool {
+ return x.txpPosInfo.pos() < y.txpPosInfo.pos();
+ });
+ auto& firstHit = ph.tqvec[0];
+ bool hitRC = firstHit.queryRC;
+ bool txpRC = ph.tqvec[0].txpPosInfo.isRC();
+ bool isFwd = (hitRC == txpRC);
+ int32_t hitPos = firstHit.txpPosInfo.pos() - firstHit.queryPos;
+
+ // determine forward
+ hits.emplace_back(tid, hitPos, isFwd, readLen);
+ hits.back().mateStatus = mateStatus;
+ }
+
+ return true;
+ }
+
+
+ // Return hits from processedHits where position constraints
+ // match maxDist
+ bool collectHitsSimpleSA(SAHitMap& processedHits,
+ uint32_t readLen,
+ uint32_t maxDist,
+ std::vector<QuasiAlignment>& hits,
+ MateStatus mateStatus){
+ bool foundHit{false};
+ // One processed hit per transcript
+ auto startOffset = hits.size();
+ for (auto& ph : processedHits) {
+ // If this is an *active* position list
+ if (ph.second.active) {
+ auto tid = ph.first;
+ auto minPosIt = std::min_element(ph.second.tqvec.begin(),
+ ph.second.tqvec.end(),
+ [](const SATxpQueryPos& a, const SATxpQueryPos& b) -> bool {
+ return a.pos < b.pos;
+ });
+ bool hitRC = minPosIt->queryRC;
+ int32_t hitPos = minPosIt->pos - minPosIt->queryPos;
+ bool isFwd = !hitRC;
+ hits.emplace_back(tid, hitPos, isFwd, readLen);
+ hits.back().mateStatus = mateStatus;
+ }
+ }
+ // if SAHitMap is sorted, no need to sort here
+ /*
+ std::sort(hits.begin() + startOffset, hits.end(),
+ [](const QuasiAlignment& a, const QuasiAlignment& b) -> bool {
+ return a.tid < b.tid;
+ });
+ */
+ return true;
+ }
+
+
+ // Return hits from processedHits where position constraints
+ // match maxDist
+ bool collectHitsSimpleSA2(std::vector<ProcessedSAHit>& processedHits,
+ uint32_t readLen,
+ uint32_t maxDist,
+ std::vector<QuasiAlignment>& hits,
+ MateStatus mateStatus){
+ bool foundHit{false};
+
+ // One processed hit per transcript
+ for (auto& ph : processedHits) {
+ // If this is an *active* position list
+ if (ph.active) {
+ auto tid = ph.tid;
+ auto minPosIt =
+ std::min_element(ph.tqvec.begin(),
+ ph.tqvec.end(),
+ [](const SATxpQueryPos& a, const SATxpQueryPos& b) -> bool {
+ return a.pos < b.pos;
+ });
+
+ bool hitRC = minPosIt->queryRC;
+ int32_t hitPos = minPosIt->pos - minPosIt->queryPos;
+ bool isFwd = !hitRC;
+ hits.emplace_back(tid, hitPos, isFwd, readLen);
+ hits.back().mateStatus = mateStatus;
+ }
+ }
+ return true;
+ }
+
+
+
+
+ // Intersects the hit h2 with outHits.
+ // This will modify outHits so that the tqvec field of the
+ // entries in outHits that are labeled by the transcripts in
+ // which h2 appears will have an iterator to the beginning of
+ // the position list for h2.
+ void intersectWithOutput(HitInfo& h2, RapMapIndex& rmi,
+ std::vector<ProcessedHit>& outHits) {
+
+ // Convenient bindings for variables we'll use
+ auto& eqClasses = rmi.eqClassList;
+ auto& eqClassLabels = rmi.eqLabelList;
+ auto& posList = rmi.posList;
+
+ // Iterator to the beginning and end of the output hits
+ auto outHitIt = outHits.begin();
+ auto outHitEnd = outHits.end();
+
+ // Equiv. class for h2
+ auto& eqClassRight = eqClasses[h2.kinfo->eqId];
+
+ // Iterator into, length of and end of the positon list for h2
+ auto rightPosIt = posList.begin() + h2.kinfo->offset;
+ auto rightPosLen = h2.kinfo->count;
+ auto rightPosEnd = rightPosIt + rightPosLen;
+ // Iterator into, length of and end of the transcript list for h2
+ auto rightTxpIt = eqClassLabels.begin() + eqClassRight.txpListStart;
+ auto rightTxpListLen = eqClassRight.txpListLen;
+ auto rightTxpEnd = rightTxpIt + rightTxpListLen;
+
+ auto rightQueryPos = h2.queryPos;
+ auto rightQueryRC = h2.queryRC;
+ PositionListHelper rightPosHelper(rightPosIt, posList.end());
+
+ uint32_t leftTxp, rightTxp;
+ while (outHitIt != outHitEnd and rightTxpIt != rightTxpEnd) {
+ // Get the current transcript ID for the left and right eq class
+ leftTxp = outHitIt->tid;
+ rightTxp = *rightTxpIt;
+ // If we need to advance the left txp, do it
+ if (leftTxp < rightTxp) {
+ // Advance to the next transcript in the
+ // equivalence class label
+ ++outHitIt;
+ } else {
+ // If the transcripts are equal (i.e. leftTxp >= rightTxp and !(rightTxp < leftTxp))
+ // Then see if there are any hits here.
+ if (!(rightTxp < leftTxp)) {
+ // Add the position list iterator and query pos for the
+ // hit from h2 to the back of outHits' tqvec.
+ outHitIt->tqvec.emplace_back(rightPosHelper, rightQueryPos, rightQueryRC);
+ ++outHitIt;
+ }
+ // advance the hit we're intersecting to the next transcript
+ rightPosHelper.advanceToNextTranscript();
+ // Advance the right transcript id regardless of whether
+ // we found a hit or not.
+ ++rightTxpIt;
+ }
+ }
+
+ }
+
+ /** from http://en.cppreference.com/w/cpp/algorithm/lower_bound **/
+ template <typename ForwardIt>
+ ForwardIt binarySearch(
+ ForwardIt first,
+ ForwardIt last,
+ uint32_t value) {
+ ForwardIt it;
+ typename std::iterator_traits<ForwardIt>::difference_type count, step;
+ count = std::distance(first, last);
+
+ while (count > 0) {
+ it = first;
+ step = count / 2;
+ std::advance(it, step);
+ if (*it < value) {
+ first = ++it;
+ count -= step + 1;
+ }
+ else {
+ count = step;
+ }
+ }
+ return first;
+ }
+
+ /** from http://en.cppreference.com/w/cpp/algorithm/find **/
+ template<class InputIt>
+ InputIt linearSearch(InputIt first, InputIt last, uint32_t value) {
+ for (; first != last; ++first) {
+ if (*first == value) {
+ return first;
+ }
+ }
+ return last;
+ }
+
+ /** adapted from https://schani.wordpress.com/2010/04/30/linear-vs-binary-search/ **/
+ uint32_t binarySearchFast(const std::vector<uint32_t>& arr, size_t n, uint32_t key) {
+ uint32_t min = 0, max = n;
+ while (min < max) {
+ int middle = (min + max) >> 1;
+ min = (key > arr[middle]) ? middle+1 : min;
+ max = (key <= arr[middle]) ? middle : max;
+ }
+ return (arr[min] == key) ? min : std::numeric_limits<uint32_t>::max();
+ }
+
+ /** adapted from https://schani.wordpress.com/2010/04/30/linear-vs-binary-search/ **/
+ // ASSUMES SENTINEL VALUE (value in array >= key *MUST* exist)
+ uint32_t linearSearchUnrolled16(const std::vector<uint32_t>& arr, size_t n, uint32_t key) {
+ uint32_t i{0};
+ for (;;) {
+ if ( arr[i + 0] >= key) return i + 0;
+ if ( arr[i + 1] >= key) return i + 1;
+ if ( arr[i + 2] >= key) return i + 2;
+ if ( arr[i + 3] >= key) return i + 3;
+ if ( arr[i + 4] >= key) return i + 4;
+ if ( arr[i + 5] >= key) return i + 5;
+ if ( arr[i + 6] >= key) return i + 6;
+ if ( arr[i + 7] >= key) return i + 7;
+ if ( arr[i + 8] >= key) return i + 8;
+ if ( arr[i + 9] >= key) return i + 9;
+ if ( arr[i + 10] >= key) return i + 10;
+ if ( arr[i + 11] >= key) return i + 11;
+ if ( arr[i + 12] >= key) return i + 12;
+ if ( arr[i + 13] >= key) return i + 13;
+ if ( arr[i + 14] >= key) return i + 14;
+ if ( arr[i + 15] >= key) return i + 15;
+ i += 16;
+ }
+ }
+
+ template <typename RapMapIndexT>
+ void intersectSAIntervalWithOutput2(SAIntervalHit<typename RapMapIndexT::IndexType>& h,
+ RapMapIndexT& rmi,
+ //fbs::eytzinger_array_bfp<uint32_t, uint32_t, true>& outTxps,
+ //std::vector<uint32_t>& outTxps,
+ SAProcessedHitVec& processedHits) {
+ // Convenient bindings for variables we'll use
+ auto& SA = rmi.SA;
+ auto& txpIDs = rmi.positionIDs;
+ auto& txpStarts = rmi.txpOffsets;
+
+ auto& outStructs = processedHits.hits;
+ auto& outTxps = processedHits.txps;
+
+ // Iterator to the beginning and end of the output hits
+ auto txpIt = processedHits.txps.begin();
+ auto txpEnd = processedHits.txps.end();
+
+ uint32_t arraySize = processedHits.txps.size();
+
+ uint32_t rightTxp;
+ uint32_t pos;
+ //decltype(processedHits.txps)::iterator searchIt = txpEnd;
+ uint32_t searchInd{0};
+ for (auto i = h.begin; i < h.end; ++i) {
+ rightTxp = txpIDs[SA[i]];
+ if (arraySize > 64) {
+ searchInd = binarySearchFast(outTxps, arraySize, rightTxp);
+ } else {
+ searchInd = linearSearchUnrolled16(outTxps, arraySize, rightTxp);
+ }
+ // If we found this transcript (make sure it's not the sentinel) then
+ // add it to the list.
+ if ( searchInd < arraySize - 1 ) {
+ //auto offset = std::distance(txpIt, searchIt);
+ pos = static_cast<uint32_t>(SA[i]) - txpStarts[rightTxp];
+ outStructs[searchInd].tqvec.emplace_back(pos, h.queryPos, h.queryRC);
+ }
+ /*
+ auto searchIdx = outTxps.search(rightTxp);
+ if (searchIdx < arraySize) {
+ pos = static_cast<uint32_t>(SA[i]) - txpStarts[rightTxp];
+ outStructs[searchIdx].tqvec.emplace_back(pos, h.queryPos, h.queryRC);
+ }
+ */
+ }
+ }
+
+
+ /*
+ void intersectSAIntervalWithOutput3(SAIntervalHit& h,
+ RapMapSAIndex& rmi,
+ SAProcessedHitVec& outHits) {
+ // Convenient bindings for variables we'll use
+ auto& SA = rmi.SA;
+ auto& txpIDs = rmi.positionIDs;
+ auto& txpStarts = rmi.txpOffsets;
+
+ // Iterator to the beginning and end of the output hits
+ auto outHitIt = outHits.begin();
+ auto outHitEnd = outHits.end();
+
+ // Make a vector of iterators into the right interval
+ std::vector<int*> rightHitIterators;
+ rightHitIterators.reserve(h.span());
+ for (auto i = h.begin; i < h.end; ++i) {
+ rightHitIterators.emplace_back(&SA[i]);
+ }
+ // Sort the iterators by their transcript ID
+ std::sort(rightHitIterators.begin(), rightHitIterators.end(),
+ [&txpIDs](const int* a, const int* b) -> bool {
+ return txpIDs[*a] < txpIDs[*b];
+ });
+ auto rightIntHit = rightHitIterators.begin();
+ auto rightIntHitEnd = rightHitIterators.end();
+
+ uint32_t leftTxp, rightTxp;
+ uint32_t pos;
+ while (outHitIt != outHitEnd and rightIntHit != rightIntHitEnd) {
+ // Get the current transcript ID for the left and right eq class
+ leftTxp = outHitIt->tid;
+ rightTxp = txpIDs[(*(*rightIntHit))];
+ // If we need to advance the left txp, do it
+ if (leftTxp < rightTxp) {
+ // Advance to the next transcript in the
+ // equivalence class label
+ ++outHitIt;
+ } else {
+ // If the transcripts are equal (i.e. leftTxp >= rightTxp and !(rightTxp < leftTxp))
+ // Then see if there are any hits here.
+ if (!(rightTxp < leftTxp)) {
+ // Add the position list iterator and query pos for the
+ // hit from h2 to the back of outHits' tqvec.
+ pos = static_cast<uint32_t>(*(*rightIntHit)) - txpStarts[rightTxp];
+ outHitIt->tqvec.emplace_back(pos, h.queryPos, h.queryRC);
+ //++outHitIt;
+ }
+ ++rightIntHit;
+ }
+ }
+ }
+ */
+
+
+
+ template <typename RapMapIndexT>
+ void intersectSAIntervalWithOutput(SAIntervalHit<typename RapMapIndexT::IndexType>& h,
+ RapMapIndexT& rmi,
+ uint32_t intervalCounter,
+ SAHitMap& outHits) {
+ using OffsetT = typename RapMapIndexT::IndexType;
+ // Convenient bindings for variables we'll use
+ auto& SA = rmi.SA;
+ //auto& txpIDs = rmi.positionIDs;
+ auto& rankDict = rmi.rankDict;
+ auto& txpStarts = rmi.txpOffsets;
+
+ // Walk through every hit in the new interval 'h'
+ for (OffsetT i = h.begin; i != h.end; ++i) {
+ //auto txpID = txpIDs[SA[i]];
+ // auto txpID = rankDict.Rank(SA[i], 1);
+ auto txpID = rmi.transcriptAtPosition(SA[i]);
+ auto txpListIt = outHits.find(txpID);
+ // If we found this transcript
+ // Add this position to the list
+ if (txpListIt != outHits.end()) {
+ txpListIt->second.numActive += (txpListIt->second.numActive == intervalCounter - 1) ? 1 : 0;
+ if (txpListIt->second.numActive == intervalCounter) {
+ auto globalPos = SA[i];
+ auto localPos = globalPos - txpStarts[txpID];
+ txpListIt->second.tqvec.emplace_back(localPos, h.queryPos, h.queryRC);
+ }
+ }
+ }
+ }
+
+
+
+ std::vector<ProcessedHit> intersectHits(
+ std::vector<HitInfo>& inHits,
+ RapMapIndex& rmi
+ ) {
+ // Each inHit is a HitInfo structure that contains
+ // an iterator to the KmerInfo for this k-mer, the k-mer ID,
+ // and the query position where this k-mer appeared.
+ // We want to find the transcripts that appear in *every*
+ // hit. Further, for each transcript, we want to
+ // know the k-mers that appear in this txp.
+
+ // Check this --- we should never call this function
+ // with less than 2 hits.
+ if (inHits.size() < 2) {
+ std::cerr << "intersectHits() called with < 2 k-mer "
+ " hits; this shouldn't happen\n";
+ return {};
+ }
+
+ auto& eqClasses = rmi.eqClassList;
+ auto& eqClassLabels = rmi.eqLabelList;
+ auto& posList = rmi.posList;
+
+ // The HitInfo with the smallest equivalence class
+ // i.e. label with the fewest transcripts.
+ HitInfo* minHit = &inHits[0];
+ for (auto& h : inHits) {
+ if (h.kinfo->count < minHit->kinfo->count) {
+ minHit = &h;
+ }
+ }
+
+ std::vector<ProcessedHit> outHits;
+ outHits.reserve(minHit->kinfo->count);
+ // =========
+ { // Add the info from minHit to outHits
+ // Equiv. class for minHit
+ auto& eqClass = eqClasses[minHit->kinfo->eqId];
+ // Iterator into, length of and end of the positon list
+ auto posIt = posList.begin() + minHit->kinfo->offset;
+ auto posLen = minHit->kinfo->count;
+ auto posEnd = posIt + posLen;
+ // Iterator into, length of and end of the transcript list
+ auto txpIt = eqClassLabels.begin() + eqClass.txpListStart;
+ auto txpListLen = eqClass.txpListLen;
+ auto txpEnd = txpIt + txpListLen;
+ PositionListHelper posHelper(posIt, posList.end());
+
+ while (txpIt != txpEnd) {
+ auto tid = *txpIt;
+ outHits.emplace_back(tid, posHelper, minHit->queryPos, minHit->queryRC);
+ posHelper.advanceToNextTranscript();
+ ++txpIt;
+ }
+ }
+ // =========
+
+ // Now intersect everything in inHits (apart from minHits)
+ // to get the final set of mapping info.
+ for (auto& h : inHits) {
+ if (&h != minHit) { // don't intersect minHit with itself
+ intersectWithOutput(h, rmi, outHits);
+ }
+ }
+
+ size_t requiredNumHits = inHits.size();
+ // do we need stable_partition? --- don't think so.
+ auto newEnd = std::stable_partition(outHits.begin(), outHits.end(),
+ [requiredNumHits] (const ProcessedHit& ph) -> bool {
+ // should never really be greater.
+ return (ph.tqvec.size() >= requiredNumHits);
+ });
+ /*
+ bool didDrop = false;
+ for (auto it = newEnd; it != outHits.end(); ++it) {
+ std::cerr << "Dropped hit for txp " << it->tid << "\n";
+ didDrop = true;
+ }
+ if (didDrop) {
+ auto& eqClass = eqClasses[inHits[0].kinfo->eqId];
+ auto txpIt = eqClassLabels.begin() + eqClass.txpListStart;
+ auto txpListLen = eqClass.txpListLen;
+ auto txpEnd = txpIt + txpListLen;
+ std::cerr << "hits1: {";
+ while (txpIt != txpEnd) {
+ std::cerr << *txpIt << ", ";
+ ++txpIt;
+ }
+ std::cerr << "}\n";
+ auto& eqClass2 = eqClasses[inHits[1].kinfo->eqId];
+ txpIt = eqClassLabels.begin() + eqClass2.txpListStart;
+ txpListLen = eqClass2.txpListLen;
+ txpEnd = txpIt + txpListLen;
+ std::cerr << "hits2: {";
+ while (txpIt != txpEnd) {
+ std::cerr << *txpIt << ", ";
+ ++txpIt;
+ }
+ std::cerr << "}\n";
+ }
+ */
+ // return only the valid hits
+ outHits.resize(std::distance(outHits.begin(), newEnd));
+ return outHits;
+ }
+
+ template <typename RapMapIndexT>
+ std::vector<ProcessedSAHit> intersectSAHits2(
+ std::vector<SAIntervalHit<typename RapMapIndexT::IndexType>>& inHits,
+ RapMapIndexT& rmi
+ ) {
+ using OffsetT = typename RapMapIndexT::IndexType;
+
+ // Each inHit is a SAIntervalHit structure that contains
+ // an SA interval with all hits for a particuar query location
+ // on the read.
+ //
+ // We want to find the transcripts that appear in *every*
+ // interavl. Further, for each transcript, we want to
+ // know the positions within this txp.
+
+ // Check this --- we should never call this function
+ // with less than 2 hits.
+ SAProcessedHitVec outHits;
+ if (inHits.size() < 2) {
+ std::cerr << "intersectHitsSA() called with < 2 k-mer "
+ " hits; this shouldn't happen\n";
+ return outHits.hits;
+ }
+
+ auto& SA = rmi.SA;
+ auto& txpStarts = rmi.txpOffsets;
+ auto& txpIDs = rmi.positionIDs;
+
+ // Start with the smallest interval
+ // i.e. interval with the fewest hits.
+ SAIntervalHit<OffsetT>* minHit = &inHits[0];
+ for (auto& h : inHits) {
+ if (h.span() < minHit->span()) {
+ minHit = &h;
+ }
+ }
+
+ auto& outStructs = outHits.hits;
+ auto& outTxps = outHits.txps;
+ outStructs.reserve(minHit->span());
+ outTxps.reserve(minHit->span());
+ std::map<int, uint32_t> posMap;
+ // =========
+ //{ // Add the info from minHit to outHits
+ for (int i = minHit->begin; i < minHit->end; ++i) {
+ auto globalPos = SA[i];
+ auto tid = txpIDs[globalPos];
+ auto txpPos = globalPos - txpStarts[tid];
+ auto posIt = posMap.find(tid);
+ if (posIt == posMap.end()) {
+ posMap[tid] = outStructs.size();
+ outStructs.emplace_back(tid, txpPos, minHit->queryPos, minHit->queryRC);
+ } else {
+ outStructs[posIt->second].tqvec.emplace_back(txpPos, minHit->queryPos, minHit->queryRC);
+ }
+ }
+ std::sort(outStructs.begin(), outStructs.end(),
+ [] (const ProcessedSAHit& a, const ProcessedSAHit& b) -> bool {
+ return a.tid < b.tid;
+ });
+ for (auto it = outStructs.begin(); it != outStructs.end(); ++it) {
+ outTxps.emplace_back(it->tid);
+ }
+ // Sentinel value for search
+ outTxps.emplace_back(std::numeric_limits<uint32_t>::max());
+ /*
+ fbs::eytzinger_array_bfp<uint32_t, uint32_t, true> searchArray(
+ txpIndices.begin(), txpIndices.size()
+ );
+ */
+ //}
+ // =========
+
+ // Now intersect everything in inHits (apart from minHits)
+ // to get the final set of mapping info.
+ for (auto& h : inHits) {
+ if (&h != minHit) { // don't intersect minHit with itself
+ intersectSAIntervalWithOutput2(h, rmi, outHits);
+ }
+ }
+
+ size_t requiredNumHits = inHits.size();
+ // Mark as active any transcripts with the required number of hits.
+ for (auto it = outStructs.begin(); it != outStructs.end(); ++it) {
+ if (it->tqvec.size() >= requiredNumHits) {
+ it->active = true;
+ }
+ }
+ return outStructs;
+ }
+
+ template <typename RapMapIndexT>
+ SAHitMap intersectSAHits(
+ std::vector<SAIntervalHit<typename RapMapIndexT::IndexType>>& inHits,
+ RapMapIndexT& rmi,
+ bool strictFilter
+ ) {
+ using OffsetT = typename RapMapIndexT::IndexType;
+ // Each inHit is a SAIntervalHit structure that contains
+ // an SA interval with all hits for a particuar query location
+ // on the read.
+ //
+ // We want to find the transcripts that appear in *every*
+ // interavl. Further, for each transcript, we want to
+ // know the positions within this txp.
+
+ // Check this --- we should never call this function
+ // with less than 2 hits.
+ SAHitMap outHits;
+ if (inHits.size() < 2) {
+ std::cerr << "intersectHitsSA() called with < 2 hits "
+ " hits; this shouldn't happen\n";
+ return outHits;
+ }
+
+ auto& SA = rmi.SA;
+ auto& txpStarts = rmi.txpOffsets;
+ //auto& txpIDs = rmi.positionIDs;
+ auto& rankDict = rmi.rankDict;
+
+ // Start with the smallest interval
+ // i.e. interval with the fewest hits.
+ SAIntervalHit<OffsetT>* minHit = &inHits[0];
+ for (auto& h : inHits) {
+ if (h.span() < minHit->span()) {
+ minHit = &h;
+ }
+ }
+
+ //outHits.reserve(minHit->span());
+ // =========
+ { // Add the info from minHit to outHits
+ for (OffsetT i = minHit->begin; i < minHit->end; ++i) {
+ auto globalPos = SA[i];
+ //auto tid = txpIDs[globalPos];
+ auto tid = rmi.transcriptAtPosition(globalPos);
+ auto txpPos = globalPos - txpStarts[tid];
+ outHits[tid].tqvec.emplace_back(txpPos, minHit->queryPos, minHit->queryRC);
+ }
+ }
+ // =========
+
+ // Now intersect everything in inHits (apart from minHits)
+ // to get the final set of mapping info.
+ size_t intervalCounter{2};
+ for (auto& h : inHits) {
+ if (&h != minHit) { // don't intersect minHit with itself
+ intersectSAIntervalWithOutput(h, rmi, intervalCounter, outHits);
+ ++intervalCounter;
+ }
+ }
+
+ size_t requiredNumHits = inHits.size();
+ // Mark as active any transcripts with the required number of hits.
+ for (auto it = outHits.begin(); it != outHits.end(); ++it) {
+ bool enoughHits = (it->second.numActive >= requiredNumHits);
+ it->second.active = (strictFilter) ?
+ (enoughHits and it->second.checkConsistent(requiredNumHits)) :
+ (enoughHits);
+ }
+ return outHits;
+ }
+
+
+ /**
+ * Need to explicitly instantiate the versions we use
+ */
+ using SAIndex32BitDense = RapMapSAIndex<int32_t,google::dense_hash_map<uint64_t, rapmap::utils::SAInterval<int32_t>,
+ rapmap::utils::KmerKeyHasher>>;
+ using SAIndex64BitDense = RapMapSAIndex<int64_t,google::dense_hash_map<uint64_t, rapmap::utils::SAInterval<int64_t>,
+ rapmap::utils::KmerKeyHasher>>;
+ using SAIndex32BitPerfect = RapMapSAIndex<int32_t, BooMap<uint64_t, rapmap::utils::SAInterval<int32_t>>>;
+ using SAIndex64BitPerfect = RapMapSAIndex<int64_t, BooMap<uint64_t, rapmap::utils::SAInterval<int64_t>>>;
+
+ template
+ void intersectSAIntervalWithOutput<SAIndex32BitDense>(SAIntervalHit<int32_t>& h,
+ SAIndex32BitDense& rmi,
+ uint32_t intervalCounter,
+ SAHitMap& outHits);
+
+ template
+ void intersectSAIntervalWithOutput<SAIndex64BitDense>(SAIntervalHit<int64_t>& h,
+ SAIndex64BitDense& rmi,
+ uint32_t intervalCounter,
+ SAHitMap& outHits);
+
+ template
+ SAHitMap intersectSAHits<SAIndex32BitDense>(std::vector<SAIntervalHit<int32_t>>& inHits,
+ SAIndex32BitDense& rmi, bool strictFilter);
+
+ template
+ SAHitMap intersectSAHits<SAIndex64BitDense>(std::vector<SAIntervalHit<int64_t>>& inHits,
+ SAIndex64BitDense& rmi, bool strictFilter);
+
+ template
+ void intersectSAIntervalWithOutput<SAIndex32BitPerfect>(SAIntervalHit<int32_t>& h,
+ SAIndex32BitPerfect& rmi,
+ uint32_t intervalCounter,
+ SAHitMap& outHits);
+
+ template
+ void intersectSAIntervalWithOutput<SAIndex64BitPerfect>(SAIntervalHit<int64_t>& h,
+ SAIndex64BitPerfect& rmi,
+ uint32_t intervalCounter,
+ SAHitMap& outHits);
+
+ template
+ SAHitMap intersectSAHits<SAIndex32BitPerfect>(std::vector<SAIntervalHit<int32_t>>& inHits,
+ SAIndex32BitPerfect& rmi, bool strictFilter);
+
+ template
+ SAHitMap intersectSAHits<SAIndex64BitPerfect>(std::vector<SAIntervalHit<int64_t>>& inHits,
+ SAIndex64BitPerfect& rmi, bool strictFilter);
+ }
+}
diff --git a/debian/rapmap/HitManager.hpp b/debian/rapmap/HitManager.hpp
new file mode 100644
index 0000000..24a288e
--- /dev/null
+++ b/debian/rapmap/HitManager.hpp
@@ -0,0 +1,109 @@
+#ifndef __HIT_MANAGER_HPP__
+#define __HIT_MANAGER_HPP__
+
+#include "RapMapUtils.hpp"
+#include "RapMapIndex.hpp"
+#include "RapMapSAIndex.hpp"
+
+//#include "eytzinger_array.h"
+
+#include <tuple>
+#include <vector>
+#include <algorithm>
+#include <map>
+#include <unordered_map>
+
+namespace rapmap {
+ namespace hit_manager {
+ using HitInfo = rapmap::utils::HitInfo;
+ using ProcessedHit = rapmap::utils::ProcessedHit;
+ using MateStatus = rapmap::utils::MateStatus;
+ using PositionListHelper = rapmap::utils::PositionListHelper;
+ using QuasiAlignment = rapmap::utils::QuasiAlignment;
+ using TxpQueryPos = rapmap::utils::TxpQueryPos;
+ using SATxpQueryPos = rapmap::utils::SATxpQueryPos;
+
+ template <typename T>
+ using SAIntervalHit = rapmap::utils::SAIntervalHit<T>;
+ using SAHitMap = std::map<int, rapmap::utils::ProcessedSAHit>;
+ using ProcessedSAHit = rapmap::utils::ProcessedSAHit;
+
+ class SAProcessedHitVec {
+ public:
+ std::vector<ProcessedSAHit> hits;
+ std::vector<uint32_t> txps;
+ };
+ /*
+ using SAProcessedHitVec = std::tuple<std::vector<ProcessedSAHit>, std::vector<uint32_t>>;
+ */
+
+ // Return hits from processedHits where position constraints
+ // match maxDist
+ bool collectHitsSimple(std::vector<ProcessedHit>& processedHits,
+ uint32_t readLen,
+ uint32_t maxDist,
+ std::vector<QuasiAlignment>& hits,
+ MateStatus mateStatus);
+
+ // Return hits from processedHits where position constraints
+ // match maxDist
+ bool collectHitsSimpleSA(SAHitMap& processedHits,
+ uint32_t readLen,
+ uint32_t maxDist,
+ std::vector<QuasiAlignment>& hits,
+ MateStatus mateStatus);
+
+ // Return hits from processedHits where position constraints
+ // match maxDist
+ bool collectHitsSimpleSA2(std::vector<ProcessedSAHit>& processedHits,
+ uint32_t readLen,
+ uint32_t maxDist,
+ std::vector<QuasiAlignment>& hits,
+ MateStatus mateStatus);
+
+
+ // Intersects the hit h2 with outHits.
+ // This will modify outHits so that the tqvec field of the
+ // entries in outHits that are labeled by the transcripts in
+ // which h2 appears will have an iterator to the beginning of
+ // the position list for h2.
+ void intersectWithOutput(HitInfo& h2, RapMapIndex& rmi,
+ std::vector<ProcessedHit>& outHits);
+
+ template <typename RapMapIndexT>
+ void intersectSAIntervalWithOutput(SAIntervalHit<typename RapMapIndexT::IndexType>& h,
+ RapMapIndexT& rmi,
+ uint32_t intervalCounter,
+ SAHitMap& outHits);
+
+
+ template <typename RapMapIndexT>
+ void intersectSAIntervalWithOutput2(SAIntervalHit<typename RapMapIndexT::IndexType>& h,
+ RapMapIndexT& rmi,
+ SAProcessedHitVec& outStructs);
+
+ /*
+ void intersectSAIntervalWithOutput3(SAIntervalHit& h,
+ RapMapSAIndex& rmi,
+ SAProcessedHitVec& outHits);
+ */
+
+ std::vector<ProcessedHit> intersectHits(
+ std::vector<HitInfo>& inHits,
+ RapMapIndex& rmi);
+
+ template <typename RapMapIndexT>
+ SAHitMap intersectSAHits(
+ std::vector<SAIntervalHit<typename RapMapIndexT::IndexType>>& inHits,
+ RapMapIndexT& rmi,
+ bool strictFilter=false);
+
+ template <typename RapMapIndexT>
+ std::vector<ProcessedSAHit> intersectSAHits2(
+ std::vector<SAIntervalHit<typename RapMapIndexT::IndexType>>& inHits,
+ RapMapIndexT& rmi);
+ }
+}
+
+
+#endif // __HIT_MANAGER_HPP__
diff --git a/debian/rapmap/IndexHeader.hpp b/debian/rapmap/IndexHeader.hpp
new file mode 100644
index 0000000..87eba2d
--- /dev/null
+++ b/debian/rapmap/IndexHeader.hpp
@@ -0,0 +1,77 @@
+#ifndef __INDEX_HEADER_HPP__
+#define __INDEX_HEADER_HPP__
+
+#include "spdlog/spdlog.h"
+#include <cereal/types/string.hpp>
+
+// The different types of indices supported
+enum class IndexType : uint8_t {
+ PSEUDO = 0,
+ QUASI,
+ INVALID
+};
+
+class IndexHeader {
+ public:
+ IndexHeader () : type_(IndexType::INVALID), versionString_("invalid"), usesKmers_(false), kmerLen_(0), perfectHash_(false) {}
+
+ IndexHeader(IndexType typeIn, const std::string& versionStringIn,
+ bool usesKmersIn, uint32_t kmerLenIn, bool bigSA = false, bool perfectHash = false):
+ type_(typeIn), versionString_(versionStringIn),
+ usesKmers_(usesKmersIn), kmerLen_(kmerLenIn), bigSA_(bigSA),
+ perfectHash_(perfectHash) {}
+
+ template <typename Archive>
+ void save(Archive& ar) const {
+ ar( cereal::make_nvp("IndexType", type_) );
+ ar( cereal::make_nvp("IndexVersion", versionString_) );
+ ar( cereal::make_nvp("UsesKmers", usesKmers_) );
+ ar( cereal::make_nvp("KmerLen", kmerLen_) );
+ ar( cereal::make_nvp("BigSA", bigSA_) );
+ ar( cereal::make_nvp("PerfectHash", perfectHash_) );
+ }
+
+ template <typename Archive>
+ void load(Archive& ar) {
+ try {
+ ar( cereal::make_nvp("IndexType", type_) );
+ ar( cereal::make_nvp("IndexVersion", versionString_) );
+ ar( cereal::make_nvp("UsesKmers", usesKmers_) );
+ ar( cereal::make_nvp("KmerLen", kmerLen_) );
+ ar( cereal::make_nvp("BigSA", bigSA_) );
+ ar( cereal::make_nvp("PerfectHash", perfectHash_) );
+ } catch (const cereal::Exception& e) {
+ auto cerrLog = spdlog::get("stderrLog");
+ cerrLog->error("Encountered exception [{}] when loading index.", e.what());
+ cerrLog->error("The index was likely build with an older (and incompatible) "
+ "version of RapMap. Please re-build the index with a compatible version.");
+ cerrLog->flush();
+ std::exit(1);
+ }
+ }
+
+ IndexType indexType() const { return type_; }
+ std::string version() const { return versionString_; }
+ bool usesKmers() const { return usesKmers_; }
+ uint32_t kmerLen() const { return kmerLen_; }
+ bool bigSA() const { return bigSA_; }
+ bool perfectHash() const { return perfectHash_; }
+
+ private:
+ // The type of index we have
+ IndexType type_;
+ // The version string for the index
+ std::string versionString_;
+ // True if this index makes use of k-mers false otherwise
+ // (currently, all supported indices use k-mers in some form)
+ bool usesKmers_;
+ // The length of k-mer used by the index
+ uint32_t kmerLen_;
+ // Do we have a 64-bit suffix array or not
+ bool bigSA_;
+ // Are we using a perfect hash in the index or not?
+ bool perfectHash_;
+};
+
+
+#endif // __INDEX_HEADER_HPP__
diff --git a/debian/rapmap/JFRaw.hpp b/debian/rapmap/JFRaw.hpp
new file mode 100644
index 0000000..4efa052
--- /dev/null
+++ b/debian/rapmap/JFRaw.hpp
@@ -0,0 +1,30 @@
+#ifndef __JF_RAW_H__
+#define __JF_RAW_H__
+
+#include "jellyfish/file_header.hpp"
+// Type for values
+/*
+struct value_type {
+ char foo;
+ int bar;
+ bool baz;
+};
+*/
+
+// Special header type. Just like the jellyfish header type, but save
+// one extra piece of information about the hash array.
+class SpecialHeader : public jellyfish::file_header {
+public:
+ SpecialHeader() = default;
+ SpecialHeader(std::istream& is) : jellyfish::file_header(is) { }
+
+ template<typename storage>
+ void update_from_ary(const storage& ary) {
+ jellyfish::file_header::update_from_ary(ary);
+ root_["size_bytes"] = (Json::UInt64)ary.size_bytes();
+ }
+
+ size_t size_bytes() const { return root_["size_bytes"].asLargestUInt(); }
+};
+
+#endif /* __JF_RAW_H__ */
diff --git a/debian/rapmap/RapMapConfig.hpp b/debian/rapmap/RapMapConfig.hpp
new file mode 100644
index 0000000..df7d935
--- /dev/null
+++ b/debian/rapmap/RapMapConfig.hpp
@@ -0,0 +1,14 @@
+#ifndef __RAPMAP_CONFIG_HPP__
+#define __RAPMAP_CONFIG_HPP__
+
+#include <string>
+
+namespace rapmap {
+ constexpr char majorVersion[] = "0";
+ constexpr char minorVersion[] = "3";
+ constexpr char patchVersion[] = "0";
+ constexpr char version [] = "0.3.0";
+ constexpr uint32_t indexVersion = 2;
+}
+
+#endif //__RAPMAP_CONFIG_HPP__
diff --git a/debian/rapmap/RapMapFileSystem.cpp b/debian/rapmap/RapMapFileSystem.cpp
new file mode 100644
index 0000000..66e246b
--- /dev/null
+++ b/debian/rapmap/RapMapFileSystem.cpp
@@ -0,0 +1,37 @@
+#include "RapMapFileSystem.hpp"
+#include <sys/stat.h>
+
+
+namespace rapmap {
+ namespace fs {
+
+ // Taken from http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
+ bool FileExists(const char *path) {
+ struct stat fileStat;
+ if ( stat(path, &fileStat) ) {
+ return false;
+ }
+ if ( !S_ISREG(fileStat.st_mode) ) {
+ return false;
+ }
+ return true;
+ }
+
+ // Taken from http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
+ bool DirExists(const char *path) {
+ struct stat fileStat;
+ if ( stat(path, &fileStat) ) {
+ return false;
+ }
+ if ( !S_ISDIR(fileStat.st_mode) ) {
+ return false;
+ }
+ return true;
+ }
+
+ void MakeDir(const char* path) {
+ mkdir(path, ACCESSPERMS);
+ }
+
+ }
+}
diff --git a/debian/rapmap/RapMapFileSystem.hpp b/debian/rapmap/RapMapFileSystem.hpp
new file mode 100644
index 0000000..0292128
--- /dev/null
+++ b/debian/rapmap/RapMapFileSystem.hpp
@@ -0,0 +1,15 @@
+#ifndef __RAPMAP_FILESYSTEM_HPP__
+#define __RAPMAP_FILESYSTEM_HPP__
+
+namespace rapmap {
+ namespace fs {
+ // Taken from http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
+ bool FileExists(const char *path);
+ // Taken from http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
+ bool DirExists(const char *path);
+ void MakeDir(const char* path);
+ }
+}
+
+
+#endif //__RAPMAP_FILESYSTEM_HPP__
diff --git a/debian/rapmap/RapMapIndex.hpp b/debian/rapmap/RapMapIndex.hpp
new file mode 100644
index 0000000..3994d8d
--- /dev/null
+++ b/debian/rapmap/RapMapIndex.hpp
@@ -0,0 +1,52 @@
+#ifndef __RAP_MAP_INDEX_HPP__
+#define __RAP_MAP_INDEX_HPP__
+
+#include <fstream>
+#include <memory>
+
+//#include "jellyfish/jellyfish.hpp"
+#include "jellyfish/file_header.hpp"
+#include "jellyfish/binary_dumper.hpp"
+#include "jellyfish/hash_counter.hpp"
+#include "jellyfish/mapped_file.hpp"
+#include "JFRaw.hpp"
+
+#include "spdlog/spdlog.h"
+
+#include <cereal/types/unordered_map.hpp>
+#include <cereal/types/vector.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/archives/binary.hpp>
+
+#include "RapMapUtils.hpp"
+#include "ScopedTimer.hpp"
+
+class RapMapIndex {
+ using PositionList = std::vector<uint32_t>;
+ using KmerInfoList = std::vector<rapmap::utils::KmerInfo>;
+ using EqClassList = std::vector<rapmap::utils::EqClass>;
+ //using MerMapT = jellyfish::cooperative::hash_counter<rapmap::utils::my_mer>;
+ using FileMerArray = jellyfish::large_hash::array_raw<rapmap::utils::my_mer>;
+ using EqClassLabelVec = std::vector<uint32_t>;
+
+ //using KmerIndex = std::unordered_map<uint64_t, TranscriptList, rapmap::utils::KmerKeyHasher>;
+ //using IntervalIndex = std::unordered_map<uint64_t, rapmap::utils::KmerInterval, rapmap::utils::KmerKeyHasher>;
+
+ public:
+ RapMapIndex();
+
+ bool load(std::string& indexPrefix);
+
+ KmerInfoList kmerInfos;
+ std::unique_ptr<char> rawHashMem{nullptr};
+ std::unique_ptr<FileMerArray> merHash{nullptr};
+ EqClassList eqClassList;
+ EqClassLabelVec eqLabelList;
+ PositionList posList;
+ std::vector<std::string> txpNames;
+ std::vector<uint32_t> txpLens;
+ std::vector<uint8_t> fwdJumpTable;
+ std::vector<uint8_t> revJumpTable;
+};
+
+#endif //__RAP_MAP_INDEX_HPP__
diff --git a/debian/rapmap/RapMapSAIndex.cpp b/debian/rapmap/RapMapSAIndex.cpp
new file mode 100644
index 0000000..2e97122
--- /dev/null
+++ b/debian/rapmap/RapMapSAIndex.cpp
@@ -0,0 +1,177 @@
+#include "BooMap.hpp"
+#include "RapMapSAIndex.hpp"
+#include "IndexHeader.hpp"
+#include <cereal/types/unordered_map.hpp>
+#include <cereal/types/vector.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/archives/binary.hpp>
+#include <cereal/archives/json.hpp>
+
+
+#include <future>
+#include <thread>
+
+// These are **free** functions that are used for loading the
+// appropriate type of hash.
+template <typename IndexT>
+bool loadHashFromIndex(const std::string& indexDir,
+ google::dense_hash_map<uint64_t,
+ rapmap::utils::SAInterval<IndexT>,
+ rapmap::utils::KmerKeyHasher>& khash) {
+ khash.set_empty_key(std::numeric_limits<uint64_t>::max());
+ std::ifstream hashStream(indexDir + "hash.bin");
+ khash.unserialize(typename google::dense_hash_map<uint64_t,
+ rapmap::utils::SAInterval<IndexT>,
+ rapmap::utils::KmerKeyHasher>::NopointerSerializer(), &hashStream);
+ return true;
+}
+
+template <typename IndexT>
+bool loadHashFromIndex(const std::string& indexDir,
+ BooMap<uint64_t, rapmap::utils::SAInterval<IndexT>> & h) {
+ std::string hashBase = indexDir + "hash_info";
+ h.load(hashBase);
+ return true;
+}
+
+template <typename IndexT, typename HashT>
+RapMapSAIndex<IndexT, HashT>::RapMapSAIndex() {}
+
+// Given a position, p, in the concatenated text,
+// return the corresponding transcript
+template <typename IndexT, typename HashT>
+IndexT RapMapSAIndex<IndexT, HashT>::transcriptAtPosition(IndexT p) {
+ return rankDict->rank(p);
+}
+
+template <typename IndexT, typename HashT>
+bool RapMapSAIndex<IndexT, HashT>::load(const std::string& indDir) {
+
+ auto logger = spdlog::get("stderrLog");
+ size_t n{0};
+
+ IndexHeader h;
+ std::ifstream indexStream(indDir + "header.json");
+ {
+ cereal::JSONInputArchive ar(indexStream);
+ ar(h);
+ }
+ indexStream.close();
+ uint32_t idxK = h.kmerLen();
+
+ // This part takes the longest, so do it in it's own asynchronous task
+ std::future<bool> loadingHash = std::async(std::launch::async, [this, logger, indDir]() -> bool {
+ if (loadHashFromIndex(indDir, khash)) {
+ logger->info("Successfully loaded position hash");
+ return true;
+ } else {
+ logger->error("Failed to load position hash!");
+ return false;
+ }
+ // If using a google dense hash
+ //this->khash.set_empty_key(std::numeric_limits<uint64_t>::max());
+ //uint32_t k = 31;
+ //std::ifstream hashStream(indDir + "hash.bin");
+ //{
+
+ //logger->info("Loading Position Hash");
+ //khash.load(hashStream);
+ //cereal::BinaryInputArchive hashArchive(hashStream);
+ //hashArchive(k);
+ //khash.unserialize(typename google::dense_hash_map<uint64_t,
+ // rapmap::utils::SAInterval<IndexT>,
+ // rapmap::utils::KmerKeyHasher>::NopointerSerializer(), &hashStream);
+ //hashArchive(khash);
+ //}
+ //hashStream.close();
+ //std::cerr << "had " << khash.size() << " entries\n";
+ //return true;
+ });
+
+ /*
+ std::ifstream intervalStream(indDir + "kintervals.bin");
+ {
+ logger->info("Loading k-mer intervals");
+ cereal::BinaryInputArchive intervalArchive(intervalStream);
+ intervalArchive(kintervals);
+ }
+ intervalStream.close();
+ */
+
+ std::ifstream saStream(indDir + "sa.bin");
+ {
+ logger->info("Loading Suffix Array ");
+ cereal::BinaryInputArchive saArchive(saStream);
+ saArchive(SA);
+ //saArchive(LCP);
+ }
+ saStream.close();
+
+ std::ifstream seqStream(indDir + "txpInfo.bin");
+ {
+ logger->info("Loading Transcript Info ");
+ cereal::BinaryInputArchive seqArchive(seqStream);
+ seqArchive(txpNames);
+ seqArchive(txpOffsets);
+ //seqArchive(positionIDs);
+ seqArchive(seq);
+ }
+ seqStream.close();
+
+ /*
+ std::ifstream rsStream(indDir + "rsdSafe.bin", std::ios::binary);
+ {
+ logger->info("Loading Rank-Select Data");
+ rankDictSafe.Load(rsStream);
+ }
+ rsStream.close();
+ */
+ std::string rsFileName = indDir + "rsd.bin";
+ FILE* rsFile = fopen(rsFileName.c_str(), "r");
+ {
+ logger->info("Loading Rank-Select Bit Array");
+ bitArray.reset(bit_array_create(0));
+ if (!bit_array_load(bitArray.get(), rsFile)) {
+ logger->error("Couldn't load bit array from {}!", rsFileName);
+ std::exit(1);
+ }
+ logger->info("There were {} set bits in the bit array", bit_array_num_bits_set(bitArray.get()));
+ rankDict.reset(new rank9b(bitArray->words, bitArray->num_of_bits));
+ }
+ fclose(rsFile);
+
+ {
+ logger->info("Computing transcript lengths");
+ txpLens.resize(txpOffsets.size());
+ if (txpOffsets.size() > 1) {
+ for(size_t i = 0; i < txpOffsets.size() - 1; ++i) {
+ auto nextOffset = txpOffsets[i+1];
+ auto currentOffset = txpOffsets[i];
+ txpLens[i] = (nextOffset - 1) - currentOffset;
+ }
+ }
+ // The last length is just the length of the suffix array - the last offset
+ txpLens[txpOffsets.size()-1] = (SA.size() - 1) - txpOffsets[txpOffsets.size() - 1];
+ }
+
+ logger->info("Waiting to finish loading hash");
+ loadingHash.wait();
+ auto hashLoadRes = loadingHash.get();
+ if (!hashLoadRes) {
+ logger->error("Failed to load hash!");
+ std::exit(1);
+ }
+ rapmap::utils::my_mer::k(idxK);
+
+ logger->info("Done loading index");
+ return true;
+}
+
+template class RapMapSAIndex<int32_t, google::dense_hash_map<uint64_t,
+ rapmap::utils::SAInterval<int32_t>,
+ rapmap::utils::KmerKeyHasher>>;
+template class RapMapSAIndex<int64_t, google::dense_hash_map<uint64_t,
+ rapmap::utils::SAInterval<int64_t>,
+ rapmap::utils::KmerKeyHasher>>;
+template class RapMapSAIndex<int32_t, BooMap<uint64_t, rapmap::utils::SAInterval<int32_t>>>;
+template class RapMapSAIndex<int64_t, BooMap<uint64_t, rapmap::utils::SAInterval<int64_t>>>;
diff --git a/debian/rapmap/RapMapSAIndex.hpp b/debian/rapmap/RapMapSAIndex.hpp
new file mode 100644
index 0000000..075846e
--- /dev/null
+++ b/debian/rapmap/RapMapSAIndex.hpp
@@ -0,0 +1,63 @@
+#ifndef __RAPMAP_SA_INDEX_HPP__
+#define __RAPMAP_SA_INDEX_HPP__
+
+#include <cereal/types/unordered_map.hpp>
+#include <cereal/types/vector.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/archives/binary.hpp>
+
+#include "spdlog/spdlog.h"
+#include "spdlog/fmt/bundled/format.h"
+
+#include "google/dense_hash_map"
+#include "bit_array.h"
+//#include "bitmap.h"
+//#include "shared.h"
+#include "rank9b.h"
+
+#include <cstdio>
+#include <vector>
+#include <memory>
+
+#include <fstream>
+#include "RapMapUtils.hpp"
+
+template <typename IndexT, typename HashT>
+class RapMapSAIndex {
+ public:
+ using IndexType = IndexT;
+ using HashType = HashT;
+
+ struct BitArrayDeleter {
+ void operator()(BIT_ARRAY* b) {
+ if(b != nullptr) {
+ bit_array_free(b);
+ }
+ }
+ };
+
+ using BitArrayPointer = std::unique_ptr<BIT_ARRAY, BitArrayDeleter>;
+
+ RapMapSAIndex();
+
+ // Given a position, p, in the concatenated text,
+ // return the corresponding transcript
+ IndexT transcriptAtPosition(IndexT p);
+
+ bool load(const std::string& indDir);
+
+ std::vector<IndexT> SA;
+
+ BitArrayPointer bitArray{nullptr};
+ std::unique_ptr<rank9b> rankDict{nullptr};
+
+ std::string seq;
+ std::vector<std::string> txpNames;
+ std::vector<IndexT> txpOffsets;
+ std::vector<IndexT> txpLens;
+ std::vector<IndexT> positionIDs;
+ std::vector<rapmap::utils::SAIntervalWithKey<IndexT>> kintervals;
+ HashT khash;
+};
+
+#endif //__RAPMAP_SA_INDEX_HPP__
diff --git a/debian/rapmap/RapMapSAIndexer.cpp b/debian/rapmap/RapMapSAIndexer.cpp
new file mode 100644
index 0000000..83a1491
--- /dev/null
+++ b/debian/rapmap/RapMapSAIndexer.cpp
@@ -0,0 +1,731 @@
+#include <algorithm>
+#include <cctype>
+#include <cstdio>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <random>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include "tclap/CmdLine.h"
+
+#include <cereal/archives/binary.hpp>
+#include <cereal/archives/json.hpp>
+#include <cereal/types/string.hpp>
+#include <cereal/types/unordered_map.hpp>
+#include <cereal/types/utility.hpp>
+#include <cereal/types/vector.hpp>
+
+#include "BooMap.hpp"
+#include "xxhash.h"
+
+#include "spdlog/spdlog.h"
+
+// Jellyfish 2 include
+#include "jellyfish/mer_dna.hpp"
+#include "jellyfish/stream_manager.hpp"
+#include "jellyfish/whole_sequence_parser.hpp"
+
+#include "divsufsort.h"
+#include "divsufsort64.h"
+
+#include "RapMapFileSystem.hpp"
+#include "RapMapUtils.hpp"
+#include "ScopedTimer.hpp"
+#include "bit_array.h"
+
+#include "JFRaw.hpp"
+#include "jellyfish/binary_dumper.hpp"
+#include "jellyfish/file_header.hpp"
+#include "jellyfish/hash_counter.hpp"
+#include "jellyfish/mer_iterator.hpp"
+#include "jellyfish/mer_overlap_sequence_parser.hpp"
+#include "jellyfish/thread_exec.hpp"
+#include "rank9b.h"
+
+#include "sparsehash/dense_hash_map"
+
+#include "IndexHeader.hpp"
+
+#include <chrono>
+
+using stream_manager =
+ jellyfish::stream_manager<std::vector<std::string>::const_iterator>;
+using single_parser = jellyfish::whole_sequence_parser<stream_manager>;
+using TranscriptID = uint32_t;
+using TranscriptIDVector = std::vector<TranscriptID>;
+using KmerIDMap = std::vector<TranscriptIDVector>;
+using MerMapT = jellyfish::cooperative::hash_counter<rapmap::utils::my_mer>;
+
+bool buildSA(const std::string& outputDir, std::string& concatText, size_t tlen,
+ std::vector<int64_t>& SA) {
+ // IndexT is the signed index type
+ // UIndexT is the unsigned index type
+ using IndexT = int64_t;
+ using UIndexT = uint64_t;
+ bool success{false};
+
+ std::ofstream saStream(outputDir + "sa.bin", std::ios::binary);
+ {
+ ScopedTimer timer;
+ SA.resize(tlen, 0);
+ IndexT textLen = static_cast<IndexT>(tlen);
+ std::cerr << "Building suffix array . . . ";
+ auto ret = divsufsort64(
+ reinterpret_cast<unsigned char*>(const_cast<char*>(concatText.data())),
+ SA.data(), tlen);
+
+ success = (ret == 0);
+ if (success) {
+ std::cerr << "success\n";
+ {
+ ScopedTimer timer2;
+ std::cerr << "saving to disk . . . ";
+ cereal::BinaryOutputArchive saArchive(saStream);
+ saArchive(SA);
+ std::cerr << "done\n";
+ }
+ } else {
+ std::cerr << "FAILURE: return code from libdivsufsort64() was " << ret
+ << "\n";
+ saStream.close();
+ std::exit(1);
+ }
+ std::cerr << "done\n";
+ }
+ saStream.close();
+ return success;
+}
+
+// IndexT is the index type.
+// int32_t for "small" suffix arrays
+// int64_t for "large" ones
+template <typename IndexT>
+bool buildPerfectHash(const std::string& outputDir, std::string& concatText,
+ size_t tlen, uint32_t k, std::vector<IndexT>& SA,
+ uint32_t numHashThreads) {
+ BooMap<uint64_t, rapmap::utils::SAInterval<IndexT>> intervals;
+
+ // The start and stop of the current interval
+ IndexT start = 0, stop = 0;
+ // An iterator to the beginning of the text
+ auto textB = concatText.begin();
+ auto textE = concatText.end();
+ // The current k-mer as a string
+ rapmap::utils::my_mer mer;
+ bool currentValid{false};
+ std::string currentKmer;
+ std::string nextKmer;
+ while (stop < tlen) {
+ // Check if the string starting at the
+ // current position is valid (i.e. doesn't contain $)
+ // and is <= k bases from the end of the string
+ nextKmer = concatText.substr(SA[stop], k);
+ if (nextKmer.length() == k and
+ nextKmer.find_first_of('$') == std::string::npos) {
+ // If this is a new k-mer, then hash the current k-mer
+ if (nextKmer != currentKmer) {
+ if (currentKmer.length() == k and
+ currentKmer.find_first_of('$') == std::string::npos) {
+ mer = rapmap::utils::my_mer(currentKmer);
+ auto bits = mer.get_bits(0, 2 * k);
+ intervals.add(std::move(bits), {start, stop});
+ // push_back(std::make_pair<uint64_t,
+ // rapmap::utils::SAInterval<IndexT>>(std::move(bits), {start,
+ // stop}));
+ }
+ currentKmer = nextKmer;
+ start = stop;
+ }
+ } else {
+ // If this isn't a valid suffix (contains a $)
+ // If the previous interval was valid, put it
+ // in the hash.
+ if (currentKmer.length() == k and
+ currentKmer.find_first_of('$') == std::string::npos) {
+ mer = rapmap::utils::my_mer(currentKmer);
+ auto bits = mer.get_bits(0, 2 * k);
+ // intervals.push_back(std::make_pair<uint64_t,
+ // rapmap::utils::SAInterval<IndexT>>(std::move(bits), {start, stop}));
+ intervals.add(std::move(bits), {start, stop});
+ }
+ // The current interval is invalid and empty
+ currentKmer = nextKmer;
+ start = stop;
+ }
+ if (stop % 1000000 == 0) {
+ std::cerr << "\r\rprocessed " << stop << " positions";
+ }
+ // We always update the end position
+ ++stop;
+ }
+ if (start < tlen) {
+ if (currentKmer.length() == k and
+ currentKmer.find_first_of('$') != std::string::npos) {
+ mer = rapmap::utils::my_mer(currentKmer);
+ auto bits = mer.get_bits(0, 2 * k);
+ // intervals.push_back(std::make_pair<uint64_t,
+ // rapmap::utils::SAInterval<IndexT>>(std::move(bits), {start, stop}));
+ intervals.add(std::move(bits), {start, stop});
+ }
+ }
+
+ // std::cerr << "\nthere are " << intervals.size() << " intervals of the
+ // selected depth\n";
+
+ std::cout << "building perfect hash function\n";
+ intervals.build(numHashThreads);
+ std::cout << "\ndone.\n";
+ std::string outputPrefix = outputDir + "hash_info";
+ std::cout << "saving the perfect hash and SA intervals to disk ... ";
+ intervals.save(outputPrefix);
+ std::cout << "done.\n";
+
+ return true;
+}
+
+bool buildSA(const std::string& outputDir, std::string& concatText, size_t tlen,
+ std::vector<int32_t>& SA) {
+ // IndexT is the signed index type
+ // UIndexT is the unsigned index type
+ using IndexT = int32_t;
+ using UIndexT = uint32_t;
+ bool success{false};
+
+ std::ofstream saStream(outputDir + "sa.bin", std::ios::binary);
+ {
+ ScopedTimer timer;
+ SA.resize(tlen, 0);
+ IndexT textLen = static_cast<IndexT>(tlen);
+ std::cerr << "Building suffix array . . . ";
+ auto ret = divsufsort(
+ reinterpret_cast<unsigned char*>(const_cast<char*>(concatText.data())),
+ SA.data(), tlen);
+
+ success = (ret == 0);
+ if (success) {
+ std::cerr << "success\n";
+ {
+ ScopedTimer timer2;
+ std::cerr << "saving to disk . . . ";
+ cereal::BinaryOutputArchive saArchive(saStream);
+ saArchive(SA);
+ std::cerr << "done\n";
+ }
+ } else {
+ std::cerr << "FAILURE: return code from libdivsufsort() was " << ret
+ << "\n";
+ saStream.close();
+ std::exit(1);
+ }
+ std::cerr << "done\n";
+ }
+ saStream.close();
+ return success;
+}
+
+// IndexT is the index type.
+// int32_t for "small" suffix arrays
+// int64_t for "large" ones
+template <typename IndexT>
+bool buildHash(const std::string& outputDir, std::string& concatText,
+ size_t tlen, uint32_t k, std::vector<IndexT>& SA) {
+ // Now, build the k-mer lookup table
+ google::dense_hash_map<uint64_t, rapmap::utils::SAInterval<IndexT>,
+ rapmap::utils::KmerKeyHasher>
+ khash;
+ khash.set_empty_key(std::numeric_limits<uint64_t>::max());
+
+ // The start and stop of the current interval
+ IndexT start = 0, stop = 0;
+ // An iterator to the beginning of the text
+ auto textB = concatText.begin();
+ auto textE = concatText.end();
+ // The current k-mer as a string
+ rapmap::utils::my_mer mer;
+ bool currentValid{false};
+ std::string currentKmer;
+ std::string nextKmer;
+ while (stop < tlen) {
+ // Check if the string starting at the
+ // current position is valid (i.e. doesn't contain $)
+ // and is <= k bases from the end of the string
+ nextKmer = concatText.substr(SA[stop], k);
+ if (nextKmer.length() == k and
+ nextKmer.find_first_of('$') == std::string::npos) {
+ // If this is a new k-mer, then hash the current k-mer
+ if (nextKmer != currentKmer) {
+ if (currentKmer.length() == k and
+ currentKmer.find_first_of('$') == std::string::npos) {
+ mer = rapmap::utils::my_mer(currentKmer);
+ auto bits = mer.get_bits(0, 2 * k);
+ auto hashIt = khash.find(bits);
+ if (hashIt == khash.end()) {
+ if (start > 1) {
+ if (concatText.substr(SA[start - 1], k) ==
+ concatText.substr(SA[start], k)) {
+ std::cerr << "T[SA[" << start - 1 << "]:" << k
+ << "] = " << concatText.substr(SA[start - 1], k)
+ << " = T[SA[" << start << "]:" << k << "]\n";
+ std::cerr << "start = " << start << ", stop = " << stop << "\n";
+ std::cerr << "[fatal (1)] THIS SHOULD NOT HAPPEN\n";
+ std::exit(1);
+ }
+ }
+ if (start == stop) {
+ std::cerr << "[fatal (2)] Interval is empty! (start = " << start
+ << ") = (stop = " << stop << ")\n";
+ }
+ if (start == stop) {
+ std::cerr << "[fatal (3)] Interval is empty! (start = " << start
+ << ") = (stop = " << stop << ")\n";
+ }
+
+ khash[bits] = {start, stop};
+ } else {
+ std::cerr << "\nERROR (1): trying to add same suffix "
+ << currentKmer << " (len = " << currentKmer.length()
+ << ") multiple times!\n";
+ auto prevInt = hashIt->second;
+ std::cerr << "existing interval is [" << prevInt.begin << ", "
+ << prevInt.end << ")\n";
+ for (auto x = prevInt.begin; x < prevInt.end; ++x) {
+ auto suff = concatText.substr(SA[x], k);
+ for (auto c : suff) {
+ std::cerr << "*" << c << "*";
+ }
+ std::cerr << " (len = " << suff.length() << ")\n";
+ }
+ std::cerr << "new interval is [" << start << ", " << stop << ")\n";
+ for (auto x = start; x < stop; ++x) {
+ auto suff = concatText.substr(SA[x], k);
+ for (auto c : suff) {
+ std::cerr << "*" << c << "*";
+ }
+ std::cerr << "\n";
+ }
+ }
+ }
+ currentKmer = nextKmer;
+ start = stop;
+ }
+ } else {
+ // If this isn't a valid suffix (contains a $)
+
+ // If the previous interval was valid, put it
+ // in the hash.
+ if (currentKmer.length() == k and
+ currentKmer.find_first_of('$') == std::string::npos) {
+ mer = rapmap::utils::my_mer(currentKmer);
+ auto bits = mer.get_bits(0, 2 * k);
+ auto hashIt = khash.find(bits);
+ if (hashIt == khash.end()) {
+ if (start > 2) {
+ if (concatText.substr(SA[start - 1], k) ==
+ concatText.substr(SA[start], k)) {
+ std::cerr << "T[SA[" << start - 1 << "]:" << k
+ << "] = " << concatText.substr(SA[start - 1], k)
+ << " = T[SA[" << start << "]:" << k << "]\n";
+ std::cerr << "start = " << start << ", stop = " << stop << "\n";
+ std::cerr << "[fatal (4)] THIS SHOULD NOT HAPPEN\n";
+ std::exit(1);
+ }
+ }
+ khash[bits] = {start, stop};
+ } else {
+ std::cerr << "\nERROR (2): trying to add same suffix " << currentKmer
+ << "multiple times!\n";
+ auto prevInt = hashIt->second;
+ std::cerr << "existing interval is [" << prevInt.begin << ", "
+ << prevInt.end << ")\n";
+ for (auto x = prevInt.begin; x < prevInt.end; ++x) {
+ std::cerr << concatText.substr(SA[x], k) << "\n";
+ }
+ std::cerr << "new interval is [" << start << ", " << stop << ")\n";
+ for (auto x = start; x < stop; ++x) {
+ std::cerr << concatText.substr(SA[x], k) << "\n";
+ }
+ }
+ }
+ // The current interval is invalid and empty
+ currentKmer = nextKmer;
+ start = stop;
+ }
+ if (stop % 1000000 == 0) {
+ std::cerr << "\r\rprocessed " << stop << " positions";
+ }
+ // We always update the end position
+ ++stop;
+ }
+ if (start < tlen) {
+ if (currentKmer.length() == k and
+ currentKmer.find_first_of('$') != std::string::npos) {
+ mer = rapmap::utils::my_mer(currentKmer);
+ khash[mer.get_bits(0, 2 * k)] = {start, stop};
+ }
+ }
+ std::cerr << "\nkhash had " << khash.size() << " keys\n";
+ std::ofstream hashStream(outputDir + "hash.bin", std::ios::binary);
+ {
+ ScopedTimer timer;
+ std::cerr << "saving hash to disk . . . ";
+ cereal::BinaryOutputArchive hashArchive(hashStream);
+ // hashArchive(k);
+ khash.serialize(typename google::dense_hash_map<
+ uint64_t, rapmap::utils::SAInterval<IndexT>,
+ rapmap::utils::KmerKeyHasher>::NopointerSerializer(),
+ &hashStream);
+ // hashArchive(khash);
+ std::cerr << "done\n";
+ }
+ hashStream.close();
+ return true;
+}
+
+// To use the parser in the following, we get "jobs" until none is
+// available. A job behaves like a pointer to the type
+// jellyfish::sequence_list (see whole_sequence_parser.hpp).
+template <typename ParserT> //, typename CoverageCalculator>
+void indexTranscriptsSA(ParserT* parser, std::string& outputDir,
+ bool noClipPolyA, bool usePerfectHash,
+ uint32_t numHashThreads, std::mutex& iomutex,
+ std::shared_ptr<spdlog::logger> log) {
+ // Seed with a real random value, if available
+ std::random_device rd;
+
+ // Create a random uniform distribution
+ std::default_random_engine eng(rd());
+
+ std::uniform_int_distribution<> dis(0, 3);
+
+ uint32_t n{0};
+ uint32_t k = rapmap::utils::my_mer::k();
+ std::vector<std::string> transcriptNames;
+ std::vector<int64_t> transcriptStarts;
+ // std::vector<uint32_t> positionIDs;
+ constexpr char bases[] = {'A', 'C', 'G', 'T'};
+ uint32_t polyAClipLength{10};
+ uint32_t numPolyAsClipped{0};
+ uint32_t numNucleotidesReplaced{0};
+ std::string polyA(polyAClipLength, 'A');
+
+ using TranscriptList = std::vector<uint32_t>;
+ using eager_iterator = MerMapT::array::eager_iterator;
+ using KmerBinT = uint64_t;
+
+ bool clipPolyA = !noClipPolyA;
+
+ // http://biology.stackexchange.com/questions/21329/whats-the-longest-transcript-known
+ // longest human transcript is Titin (108861), so this gives us a *lot* of
+ // leeway before
+ // we issue any warning.
+ size_t tooLong = 200000;
+ size_t numDistinctKmers{0};
+ size_t numKmers{0};
+ size_t currIndex{0};
+ std::cerr << "\n[Step 1 of 4] : counting k-mers\n";
+
+ // rsdic::RSDicBuilder rsdb;
+ std::vector<uint64_t>
+ onePos; // Positions in the bit array where we should write a '1'
+ fmt::MemoryWriter txpSeqStream;
+ {
+ ScopedTimer timer;
+ while (true) {
+ typename ParserT::job j(*parser);
+ if (j.is_empty())
+ break;
+ for (size_t i = 0; i < j->nb_filled; ++i) { // For each sequence
+ std::string& readStr = j->data[i].seq;
+ readStr.erase(
+ std::remove_if(readStr.begin(), readStr.end(),
+ [](const char a) -> bool { return !(isprint(a)); }),
+ readStr.end());
+
+ uint32_t readLen = readStr.size();
+ // First, replace non ATCG nucleotides
+ for (size_t b = 0; b < readLen; ++b) {
+ readStr[b] = ::toupper(readStr[b]);
+ int c = jellyfish::mer_dna::code(readStr[b]);
+ // Replace non-ACGT bases with pseudo-random bases
+ if (jellyfish::mer_dna::not_dna(c)) {
+ char rbase = bases[dis(eng)];
+ c = jellyfish::mer_dna::code(rbase);
+ readStr[b] = rbase;
+ ++numNucleotidesReplaced;
+ }
+ }
+
+ // Now, do Kallisto-esque clipping of polyA tails
+ if (clipPolyA) {
+ if (readStr.size() > polyAClipLength and
+ readStr.substr(readStr.length() - polyAClipLength) == polyA) {
+
+ auto newEndPos = readStr.find_last_not_of("Aa");
+ // If it was all As
+ if (newEndPos == std::string::npos) {
+ log->warn("Entry with header [{}] appeared to be all A's; it "
+ "will be removed from the index!",
+ j->data[i].header);
+ readStr.resize(0);
+ } else {
+ readStr.resize(newEndPos + 1);
+ }
+ ++numPolyAsClipped;
+ }
+ }
+
+ readLen = readStr.size();
+ // If the transcript was completely removed during clipping, don't
+ // include it in the index.
+ if (readStr.size() >= k) {
+ // If we're suspicious the user has fed in a *genome* rather
+ // than a transcriptome, say so here.
+ if (readStr.size() >= tooLong) {
+ log->warn("Entry with header [{}] was longer than {} nucleotides. "
+ "Are you certain that "
+ "we are indexing a transcriptome and not a genome?",
+ j->data[i].header, tooLong);
+ }
+
+ uint32_t txpIndex = n++;
+
+ // The name of the current transcript
+ auto& recHeader = j->data[i].header;
+ transcriptNames.emplace_back(
+ recHeader.substr(0, recHeader.find_first_of(" \t")));
+
+ // The position at which this transcript starts
+ transcriptStarts.push_back(currIndex);
+
+ txpSeqStream << readStr;
+ txpSeqStream << '$';
+ currIndex += readLen + 1;
+ onePos.push_back(currIndex - 1);
+ } else {
+ log->warn("Discarding entry with header [{}], since it was shorter than "
+ "the k-mer length of {} (perhaps after poly-A clipping)",
+ j->data[i].header, k);
+ }
+ }
+ if (n % 10000 == 0) {
+ std::cerr << "\r\rcounted k-mers for " << n << " transcripts";
+ }
+ }
+ }
+ std::cerr << "\n";
+
+ std::cerr << "Replaced " << numNucleotidesReplaced
+ << " non-ATCG nucleotides\n";
+ std::cerr << "Clipped poly-A tails from " << numPolyAsClipped
+ << " transcripts\n";
+
+ // Put the concatenated text in a string
+ std::string concatText = txpSeqStream.str();
+ // And clear the stream
+ txpSeqStream.clear();
+
+ // Build the suffix array
+ size_t tlen = concatText.length();
+ size_t maxInt = std::numeric_limits<int32_t>::max();
+ bool largeIndex = (tlen + 1 > maxInt);
+
+ // Make our dense bit arrray
+ BIT_ARRAY* bitArray = bit_array_create(concatText.length());
+ for (auto p : onePos) {
+ bit_array_set_bit(bitArray, p);
+ }
+
+ onePos.clear();
+ onePos.shrink_to_fit();
+
+ std::string rsFileName = outputDir + "rsd.bin";
+ FILE* rsFile = fopen(rsFileName.c_str(), "w");
+ {
+ ScopedTimer timer;
+ std::cerr << "Building rank-select dictionary and saving to disk ";
+ bit_array_save(bitArray, rsFile);
+ std::cerr << "done\n";
+ }
+ fclose(rsFile);
+ bit_array_free(bitArray);
+
+ std::ofstream seqStream(outputDir + "txpInfo.bin", std::ios::binary);
+ {
+ ScopedTimer timer;
+ std::cerr << "Writing sequence data to file . . . ";
+ cereal::BinaryOutputArchive seqArchive(seqStream);
+ seqArchive(transcriptNames);
+ if (largeIndex) {
+ seqArchive(transcriptStarts);
+ } else {
+ std::vector<int32_t> txpStarts(transcriptStarts.size(), 0);
+ size_t numTranscriptStarts = transcriptStarts.size();
+ for (size_t i = 0; i < numTranscriptStarts; ++i) {
+ txpStarts[i] = static_cast<int32_t>(transcriptStarts[i]);
+ }
+ transcriptStarts.clear();
+ transcriptStarts.shrink_to_fit();
+ { seqArchive(txpStarts); }
+ }
+ // seqArchive(positionIDs);
+ seqArchive(concatText);
+ std::cerr << "done\n";
+ }
+ seqStream.close();
+
+ // clear stuff we no longer need
+ // positionIDs.clear();
+ // positionIDs.shrink_to_fit();
+ transcriptStarts.clear();
+ transcriptStarts.shrink_to_fit();
+ transcriptNames.clear();
+ transcriptNames.shrink_to_fit();
+ // done clearing
+
+ if (largeIndex) {
+ largeIndex = true;
+ std::cerr << "[info] Building 64-bit suffix array "
+ "(length of generalized text is "
+ << tlen << " )\n";
+ using IndexT = int64_t;
+ std::vector<IndexT> SA;
+ bool success = buildSA(outputDir, concatText, tlen, SA);
+ if (!success) {
+ std::cerr << "[fatal] Could not build the suffix array!\n";
+ std::exit(1);
+ }
+
+ if (usePerfectHash) {
+ success = buildPerfectHash<IndexT>(outputDir, concatText, tlen, k, SA,
+ numHashThreads);
+ } else {
+ success = buildHash<IndexT>(outputDir, concatText, tlen, k, SA);
+ }
+ if (!success) {
+ std::cerr << "[fatal] Could not build the suffix interval hash!\n";
+ std::exit(1);
+ }
+ } else {
+ std::cerr << "[info] Building 32-bit suffix array "
+ "(length of generalized text is "
+ << tlen << ")\n";
+ using IndexT = int32_t;
+ std::vector<IndexT> SA;
+ bool success = buildSA(outputDir, concatText, tlen, SA);
+ if (!success) {
+ std::cerr << "[fatal] Could not build the suffix array!\n";
+ std::exit(1);
+ }
+
+ if (usePerfectHash) {
+ success = buildPerfectHash<IndexT>(outputDir, concatText, tlen, k, SA,
+ numHashThreads);
+ } else {
+ success = buildHash<IndexT>(outputDir, concatText, tlen, k, SA);
+ }
+ if (!success) {
+ std::cerr << "[fatal] Could not build the suffix interval hash!\n";
+ std::exit(1);
+ }
+ }
+
+ std::string indexVersion = "q3";
+ IndexHeader header(IndexType::QUASI, indexVersion, true, k, largeIndex,
+ usePerfectHash);
+ // Finally (since everything presumably succeeded) write the header
+ std::ofstream headerStream(outputDir + "header.json");
+ {
+ cereal::JSONOutputArchive archive(headerStream);
+ archive(header);
+ }
+ headerStream.close();
+}
+
+int rapMapSAIndex(int argc, char* argv[]) {
+ std::cerr << "RapMap Indexer\n";
+
+ TCLAP::CmdLine cmd("RapMap Indexer");
+ TCLAP::ValueArg<std::string> transcripts("t", "transcripts",
+ "The transcript file to be indexed",
+ true, "", "path");
+ TCLAP::ValueArg<std::string> index(
+ "i", "index", "The location where the index should be written", true, "",
+ "path");
+ TCLAP::ValueArg<uint32_t> kval("k", "klen", "The length of k-mer to index",
+ false, 31, "positive integer less than 32");
+ TCLAP::SwitchArg noClip(
+ "n", "noClip",
+ "Don't clip poly-A tails from the ends of target sequences", false);
+ TCLAP::SwitchArg perfectHash(
+ "p", "perfectHash", "Use a perfect hash instead of dense hash --- "
+ "somewhat slows construction, but uses less memory",
+ false);
+ TCLAP::ValueArg<uint32_t> numHashThreads(
+ "x", "numThreads",
+ "Use this many threads to build the perfect hash function", false, 4,
+ "positive integer <= # cores");
+ cmd.add(transcripts);
+ cmd.add(index);
+ cmd.add(kval);
+ cmd.add(noClip);
+ cmd.add(perfectHash);
+ cmd.add(numHashThreads);
+ cmd.parse(argc, argv);
+
+ // stupid parsing for now
+ std::string transcriptFile(transcripts.getValue());
+ std::vector<std::string> transcriptFiles({transcriptFile});
+
+ uint32_t k = kval.getValue();
+ if (k % 2 == 0) {
+ std::cerr << "Error: k must be an odd value, you chose " << k << '\n';
+ std::exit(1);
+ } else if (k > 31) {
+ std::cerr << "Error: k must not be larger than 31, you chose " << k << '\n';
+ std::exit(1);
+ }
+ rapmap::utils::my_mer::k(k);
+
+ std::string indexDir = index.getValue();
+ if (indexDir.back() != '/') {
+ indexDir += '/';
+ }
+ bool dirExists = rapmap::fs::DirExists(indexDir.c_str());
+ bool dirIsFile = rapmap::fs::FileExists(indexDir.c_str());
+ if (dirIsFile) {
+ std::cerr << "The requested index directory already exists as a file.";
+ std::exit(1);
+ }
+ if (!dirExists) {
+ rapmap::fs::MakeDir(indexDir.c_str());
+ }
+
+ std::string logPath = indexDir + "quasi_index.log";
+ auto fileSink = std::make_shared<spdlog::sinks::simple_file_sink_st>(logPath);
+ auto consoleSink = std::make_shared<spdlog::sinks::stderr_sink_st>();
+ auto consoleLog = spdlog::create("stderrLog", {consoleSink});
+ auto fileLog = spdlog::create("fileLog", {fileSink});
+ auto jointLog = spdlog::create("jointLog", {fileSink, consoleSink});
+
+ size_t maxReadGroup{1000}; // Number of reads in each "job"
+ size_t concurrentFile{2}; // Number of files to read simultaneously
+ size_t numThreads{2};
+ stream_manager streams(transcriptFiles.begin(), transcriptFiles.end(),
+ concurrentFile);
+ std::unique_ptr<single_parser> transcriptParserPtr{nullptr};
+ transcriptParserPtr.reset(
+ new single_parser(4 * numThreads, maxReadGroup, concurrentFile, streams));
+
+ bool noClipPolyA = noClip.getValue();
+ bool usePerfectHash = perfectHash.getValue();
+ uint32_t numPerfectHashThreads = numHashThreads.getValue();
+ std::mutex iomutex;
+ indexTranscriptsSA(transcriptParserPtr.get(), indexDir, noClipPolyA,
+ usePerfectHash, numPerfectHashThreads, iomutex, jointLog);
+ return 0;
+}
diff --git a/debian/rapmap/RapMapUtils.hpp b/debian/rapmap/RapMapUtils.hpp
new file mode 100644
index 0000000..cbae039
--- /dev/null
+++ b/debian/rapmap/RapMapUtils.hpp
@@ -0,0 +1,825 @@
+#ifndef __RAP_MAP_UTILS_HPP__
+#define __RAP_MAP_UTILS_HPP__
+
+#include <atomic>
+#include <cmath>
+#include <memory>
+#include "xxhash.h"
+#include <cereal/archives/binary.hpp>
+#include "jellyfish/mer_dna.hpp"
+#include "spdlog/spdlog.h"
+#include "spdlog/fmt/bundled/format.h"
+#include "PairSequenceParser.hpp"
+
+#ifdef RAPMAP_SALMON_SUPPORT
+#include "LibraryFormat.hpp"
+#endif
+
+#ifdef __GNUC__
+#define LIKELY(x) __builtin_expect((x),1)
+#define UNLIKELY(x) __builtin_expect((x),0)
+#else
+#define LIKELY(x) (x)
+#define UNLIKELY(x) (x)
+#endif
+
+// Must be forward-declared
+template <typename IndexT>
+class PairAlignmentFormatter;
+template <typename IndexT>
+class SingleAlignmentFormatter;
+
+// Forward-declare because the C++ compiler is dumb
+class RapMapIndex;
+
+namespace rapmap {
+ namespace utils {
+
+ using my_mer = jellyfish::mer_dna_ns::mer_base_static<uint64_t, 1>;
+
+ constexpr uint32_t newTxpSetMask = 0x80000000;
+ constexpr uint32_t rcSetMask = 0x40000000;
+
+ // Positions are stored in a packed format, where the highest
+ // 2-bits encode if this position refers to a new transcript
+ // and whether or not the k-mer from the hash matches this txp
+ // in the forward or RC direction.
+ void decodePosition(uint32_t p, uint32_t& pout, bool& newTxp, bool& isRC);
+
+ template <typename IndexT>
+ void writeSAMHeader(IndexT& rmi, std::shared_ptr<spdlog::logger> out) {
+ fmt::MemoryWriter hd;
+ hd.write("@HD\tVN:0.1\tSO:unknown\n");
+
+ auto& txpNames = rmi.txpNames;
+ auto& txpLens = rmi.txpLens;
+
+ auto numRef = txpNames.size();
+ for (size_t i = 0; i < numRef; ++i) {
+ hd.write("@SQ\tSN:{}\tLN:{:d}\n", txpNames[i], txpLens[i]);
+ }
+ // Eventuall output a @PG line
+ //hd.format("@PG\t");
+ std::string headerStr(hd.str());
+ // Don't include the last '\n', since the logger will do it for us.
+ headerStr.pop_back();
+ out->info("%s", headerStr);
+ }
+
+ template <typename IndexT>
+ void writeSAMHeader(IndexT& rmi, std::ostream& outStream) {
+ fmt::MemoryWriter hd;
+ hd.write("@HD\tVN:0.1\tSO:unknown\n");
+
+ auto& txpNames = rmi.txpNames;
+ auto& txpLens = rmi.txpLens;
+
+ auto numRef = txpNames.size();
+ for (size_t i = 0; i < numRef; ++i) {
+ hd.write("@SQ\tSN:{}\tLN:{:d}\n", txpNames[i], txpLens[i]);
+ }
+ // Eventuall output a @PG line
+ //hd.format("@PG\t");
+ outStream << hd.str();
+ }
+
+ // from http://stackoverflow.com/questions/9435385/split-a-string-using-c11
+ std::vector<std::string> tokenize(const std::string &s, char delim);
+
+ // from https://github.com/cppformat/cppformat/issues/105
+ class FixedBuffer : public fmt::Buffer<char> {
+ public:
+ FixedBuffer(char *array, std::size_t size)
+ : fmt::Buffer<char>(array, size) {}
+
+ protected:
+ void grow(std::size_t size) {
+ throw std::runtime_error("buffer overflow");
+ }
+ };
+
+ class FixedWriter : public fmt::Writer {
+ private:
+ FixedBuffer buffer_;
+ public:
+ FixedWriter(char *array, std::size_t size)
+ : fmt::Writer(buffer_), buffer_(array, size) {}
+ };
+
+ /**
+ * Stores both the key (k-mer)
+ * and the interval to which it corresponds.
+ * This is useful if the hash itself doesn't validate
+ * the key (e.g. a minimum perfect hash).
+ **/
+ template <typename IndexT>
+ struct SAIntervalWithKey {
+ uint64_t kmer;
+ // SAInterval<IndexT> second;
+ IndexT begin;
+ IndexT end;
+ template <typename Archive>
+ void load(Archive& ar) { ar(kmer, begin, end); }
+
+ template <typename Archive>
+ void save(Archive& ar) const { ar(kmer, begin, end); }
+ };
+
+ template <typename IndexT>
+ struct SAInterval {
+ /*
+ SAInterval(IndexT beginIn, IndexT endIn) : begin(beginIn), end(endIn) {}
+ SAInterval(std::initializer_list<IndexT> il) {
+ auto it = il.begin();
+ begin = *(it);
+ ++it;
+ end = *(il.begin());
+ }
+ */
+
+ IndexT begin;
+ IndexT end;
+ template <typename Archive>
+ void load(Archive& ar) { ar(begin, end); }
+
+ template <typename Archive>
+ void save(Archive& ar) const { ar(begin, end); }
+ };
+
+
+ struct HitCounters {
+ std::atomic<uint64_t> peHits{0};
+ std::atomic<uint64_t> seHits{0};
+ std::atomic<uint64_t> trueHits{0};
+ std::atomic<uint64_t> totHits{0};
+ std::atomic<uint64_t> numReads{0};
+ std::atomic<uint64_t> tooManyHits{0};
+ std::atomic<uint64_t> lastPrint{0};
+ };
+
+ class JFMerKeyHasher{
+ public:
+ size_t operator()(const my_mer& m) const {
+ auto k = rapmap::utils::my_mer::k();
+ auto v = m.get_bits(0, 2*k);
+ return XXH64(static_cast<void*>(&v), 8, 0);
+ }
+ };
+
+ class KmerKeyHasher {
+ public:
+ size_t operator()(const uint64_t& m) const {
+ //auto k = rapmap::utils::my_mer::k();
+ //auto v = m.get_bits(0, 2*k);
+ auto v = m;
+ return XXH64(static_cast<void*>(&v), 8, 0);
+ }
+ };
+
+ struct KmerInterval {
+ uint64_t offset;
+ uint32_t length;
+
+ template <typename Archive>
+ void save(Archive& arch) const {
+ arch(offset, length);
+ }
+
+ template <typename Archive>
+ void load(Archive& arch) {
+ arch(offset, length);
+ }
+ };
+
+ struct KmerInfo {
+ KmerInfo () : eqId(0), offset(0), count(0) {}
+
+
+ KmerInfo(uint32_t eqIdIn, uint32_t offsetIn, uint32_t countIn) :
+ eqId(eqIdIn), offset(offsetIn), count(countIn) {}
+
+ template <typename Archive>
+ void load(Archive& ar) {
+ ar(eqId, offset, count);
+ }
+
+ template <typename Archive>
+ void save(Archive& ar) const {
+ ar(eqId, offset, count);
+ }
+ uint32_t eqId = 0;
+ uint32_t offset = 0;
+ uint32_t count = 0;
+ };
+
+
+ template <class T>
+ inline void hashCombine(std::size_t& seed, const T& v)
+ {
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+ }
+
+ constexpr uint32_t uint32Invalid = std::numeric_limits<uint32_t>::max();
+ using PositionList = std::vector<uint32_t>;
+ using KmerInfoList = std::vector<KmerInfo>;
+
+ enum class MateStatus : uint8_t {
+ SINGLE_END = 0,
+ PAIRED_END_LEFT = 1,
+ PAIRED_END_RIGHT = 2,
+ PAIRED_END_PAIRED = 3 };
+
+ // Wraps the standard iterator of the Position list to provide
+ // some convenient functionality. In the future, maybe this
+ // should be a proper iterator adaptor.
+ struct PositionListHelper{
+ using PLIt = PositionList::iterator;
+
+ PositionListHelper(PLIt itIn, PLIt endIn) :
+ it_(itIn), end_(endIn) {}
+ // The underlying iterator shouldn't be advanced further
+ inline bool done() { return it_ == end_; }
+
+ // The actual postion on the transcript
+ int32_t pos() const { return static_cast<int32_t>((*it_) & 0x3FFFFFFF); }
+
+ // True if the position encoded was on the reverse complement strand
+ // of the reference transcript, false otherwise.
+ bool isRC() const { return (*it_) & 0x40000000; }
+
+ // True if we hit the position list for a new transcript, false otherwise
+ bool isNewTxp() const { return (*it_) & 0x80000000; }
+
+ void advanceToNextTranscript() {
+ if (it_ < end_) {
+ do {
+ ++it_;
+ } while (!isNewTxp() and it_ != end_);
+
+ }
+ }
+
+ PLIt it_; // The underlying iterator
+ PLIt end_; // The end of the container
+ };
+
+
+ struct QuasiAlignment {
+ QuasiAlignment() :
+ tid(std::numeric_limits<uint32_t>::max()),
+ pos(std::numeric_limits<int32_t>::max()),
+ fwd(true),
+ readLen(std::numeric_limits<uint32_t>::max()),
+ fragLen(std::numeric_limits<uint32_t>::max()),
+ isPaired(false)
+#ifdef RAPMAP_SALMON_SUPPORT
+ ,format(LibraryFormat::formatFromID(0))
+#endif // RAPMAP_SALMON_SUPPORT
+ {}
+
+ QuasiAlignment(uint32_t tidIn, int32_t posIn,
+ bool fwdIn, uint32_t readLenIn,
+ uint32_t fragLenIn = 0,
+ bool isPairedIn = false) :
+ tid(tidIn), pos(posIn), fwd(fwdIn),
+ readLen(readLenIn), fragLen(fragLenIn),
+ isPaired(isPairedIn)
+#ifdef RAPMAP_SALMON_SUPPORT
+ ,format(LibraryFormat::formatFromID(0))
+#endif // RAPMAP_SALMON_SUPPORT
+ {}
+ QuasiAlignment(QuasiAlignment&& other) = default;
+ QuasiAlignment& operator=(QuasiAlignment&) = default;
+ QuasiAlignment& operator=(QuasiAlignment&& o) = default;
+ QuasiAlignment(const QuasiAlignment& o) = default;
+ QuasiAlignment(QuasiAlignment& o) = default;
+
+ // Some convenience functions to allow salmon interop
+#ifdef RAPMAP_SALMON_SUPPORT
+ inline uint32_t transcriptID() const { return tid; }
+ inline double score() { return 1.0; }
+ inline uint32_t fragLength() { return fragLen; }
+ inline uint32_t fragLengthPedantic(uint32_t txpLen) const {
+ if (mateStatus != rapmap::utils::MateStatus::PAIRED_END_PAIRED
+ or fwd == mateIsFwd) {
+ return 0;
+ }
+ int32_t p1 = fwd ? pos : matePos;
+ p1 = (p1 < 0) ? 0 : p1;
+ p1 = (p1 > txpLen) ? txpLen : p1;
+ int32_t p2 = fwd ? matePos + mateLen : pos + readLen;
+ p2 = (p2 < 0) ? 0 : p2;
+ p2 = (p2 > txpLen) ? txpLen : p2;
+
+ return (p1 > p2) ? p1 - p2 : p2 - p1;
+ }
+ inline int32_t hitPos() { return std::min(pos, matePos); }
+ double logProb{HUGE_VAL};
+ double logBias{HUGE_VAL};
+ inline LibraryFormat libFormat() { return format; }
+ LibraryFormat format;
+#endif // RAPMAP_SALMON_SUPPORT
+
+ // Only 1 since the mate must have the same tid
+ // we won't call *chimeric* alignments here.
+ uint32_t tid;
+ // Left-most position of the hit
+ int32_t pos;
+ // left-most position of the mate
+ int32_t matePos;
+ // Is the read from the forward strand
+ bool fwd;
+ // Is the mate from the forward strand
+ bool mateIsFwd;
+ // The fragment length (template length)
+ // This is 0 for single-end or orphaned reads.
+ uint32_t fragLen;
+ // The read's length
+ uint32_t readLen;
+ // The mate's length
+ uint32_t mateLen;
+ // Is this a paired *alignment* or not
+ bool isPaired;
+ MateStatus mateStatus;
+ };
+
+ struct HitInfo {
+ HitInfo(KmerInfoList::iterator kit, uint32_t merIDIn,
+ int32_t queryPosIn, bool queryRCIn) :
+ kinfo(kit), merID(merIDIn), queryPos(queryPosIn),
+ queryRC(queryRCIn) {}
+
+ KmerInfoList::iterator kinfo;
+ uint32_t merID;
+ int32_t queryPos;
+ bool queryRC;
+ };
+
+ template <typename OffsetT>
+ struct SAIntervalHit {
+ SAIntervalHit(OffsetT beginIn, OffsetT endIn, uint32_t lenIn, uint32_t queryPosIn, bool queryRCIn) :
+ begin(beginIn), end(endIn), len(lenIn), queryPos(queryPosIn), queryRC(queryRCIn) {}
+
+ OffsetT span() { return end - begin; }
+ OffsetT begin, end;
+ uint32_t len, queryPos;
+ bool queryRC;
+ };
+
+ struct SATxpQueryPos {
+ SATxpQueryPos(uint32_t posIn, uint32_t qposIn, bool queryRCIn, bool activeIn = false) :
+ pos(posIn), queryPos(qposIn), queryRC(queryRCIn), active(activeIn) {}
+ uint32_t pos, queryPos;
+ bool queryRC, active;
+ };
+
+ struct ProcessedSAHit {
+ ProcessedSAHit() : tid(std::numeric_limits<uint32_t>::max()), active(false), numActive(1) {}
+
+ ProcessedSAHit(uint32_t txpIDIn, uint32_t txpPosIn, uint32_t queryPosIn, bool queryRCIn) :
+ tid(txpIDIn), active(false), numActive(1)
+ {
+ tqvec.emplace_back(txpPosIn, queryPosIn, queryRCIn);
+ }
+
+ /**
+ * This enforces a more stringent consistency check on
+ * the hits for this transcript. The hits must be co-linear
+ * with respect to the query and target.
+ *
+ * input: numToCheck --- the number of hits to check in sorted order
+ * hits after the last of these need not be consistent.
+ * return: numToCheck if the first numToCheck hits are consistent;
+ * -1 otherwise
+ **/
+ int32_t checkConsistent(int32_t numToCheck) {
+ auto numHits = tqvec.size();
+
+ // special case for only 1 or two hits (common)
+ if (numHits == 1) {
+ return numToCheck;
+ } else if (numHits == 2) {
+ auto& h1 = (tqvec[0].queryPos < tqvec[1].queryPos) ? tqvec[0] : tqvec[1];
+ auto& h2 = (tqvec[0].queryPos < tqvec[1].queryPos) ? tqvec[1] : tqvec[2];
+ return (h2.pos > h1.pos) ? (numToCheck) : -1;
+ } else {
+ // first, sort by query position
+ std::sort(tqvec.begin(), tqvec.end(),
+ [](const SATxpQueryPos& q1, const SATxpQueryPos& q2) -> bool {
+ return q1.queryPos < q2.queryPos;
+ });
+
+ int32_t lastRefPos{std::numeric_limits<int32_t>::min()};
+ for (size_t i = 0; i < numToCheck; ++i) {
+ int32_t refPos = static_cast<int32_t>(tqvec[i].pos);
+ if (refPos > lastRefPos) {
+ lastRefPos = refPos;
+ } else {
+ return i;
+ }
+ }
+ return numToCheck;
+ }
+ }
+
+ uint32_t tid;
+ std::vector<SATxpQueryPos> tqvec;
+ bool active;
+ uint32_t numActive;
+ };
+
+ struct SAHitInfo {
+ SAHitInfo(uint32_t txpIDIn, uint32_t txpPosIn, uint32_t queryPosIn, bool queryRCIn) :
+ tid(txpIDIn), pos(txpPosIn), queryPos(queryPosIn), queryRC(queryRCIn) {}
+ uint32_t tid;
+ uint32_t pos;
+ uint32_t queryPos;
+ bool queryRC;
+ };
+
+ struct TxpQueryPos {
+ TxpQueryPos(PositionListHelper& ph, int32_t queryPosIn, bool queryRCIn) :
+ txpPosInfo(ph), queryPos(queryPosIn), queryRC(queryRCIn) {}
+ // Iterator for the beginning of the position list
+ // of a given k-mer into a given transcript.
+ PositionListHelper txpPosInfo;
+ // The position of the k-mer on the query.
+ int32_t queryPos;
+ bool queryRC;
+ };
+
+ struct ProcessedHit {
+ ProcessedHit() : tid(std::numeric_limits<uint32_t>::max()) {}
+ ProcessedHit(uint32_t tidIn,
+ PositionListHelper ph, int32_t queryPos, bool queryRC) :
+ tid(tidIn) {
+ tqvec.emplace_back(ph, queryPos, queryRC);
+ }
+
+
+ uint32_t tid; // transcript id
+ // A vector of iterators into the position list
+ // for the k-mers hitting this transcript
+ std::vector<TxpQueryPos> tqvec;
+ };
+
+
+ struct EqClass {
+ EqClass() :
+ txpListStart(uint32Invalid), txpListLen(uint32Invalid) {}
+ EqClass(uint32_t txpListStartIn, uint32_t txpListLenIn) :
+ txpListStart(txpListStartIn), txpListLen(txpListLenIn) {}
+
+ template <typename Archive>
+ void load (Archive& ar) {
+ ar(txpListStart, txpListLen);
+ }
+
+ template <typename Archive>
+ void save (Archive& ar) const {
+ ar(txpListStart, txpListLen);
+ }
+
+ uint32_t txpListStart;
+ uint32_t txpListLen;
+ };
+
+ inline void printMateStatus(rapmap::utils::MateStatus ms) {
+ switch(ms) {
+ case rapmap::utils::MateStatus::SINGLE_END:
+ std::cerr << "SINGLE END";
+ break;
+ case rapmap::utils::MateStatus::PAIRED_END_LEFT:
+ std::cerr << "PAIRED END (LEFT)";
+ break;
+ case rapmap::utils::MateStatus::PAIRED_END_RIGHT:
+ std::cerr << "PAIRED END (RIGHT)";
+ break;
+ case rapmap::utils::MateStatus::PAIRED_END_PAIRED:
+ std::cerr << "PAIRED END (PAIRED)";
+ break;
+ }
+ }
+
+
+ // Declarations for functions dealing with SAM formatting and output
+ //
+ inline void adjustOverhang(int32_t& pos, uint32_t readLen,
+ uint32_t txpLen, FixedWriter& cigarStr) {
+ cigarStr.clear();
+ if (pos + readLen < 0) {
+ cigarStr.write("{}S", readLen);
+ pos = 0;
+ } else if (pos < 0) {
+ int32_t matchLen = readLen + pos;
+ int32_t clipLen = readLen - matchLen;
+ cigarStr.write("{}S{}M", clipLen, matchLen);
+ // Now adjust the mapping position
+ pos = 0;
+ } else if (pos > txpLen) {
+ cigarStr.write("{}S", readLen);
+ } else if (pos + readLen > txpLen) {
+ int32_t matchLen = txpLen - pos;
+ int32_t clipLen = readLen - matchLen;
+ cigarStr.write("{}M{}S", matchLen, clipLen);
+ } else {
+ cigarStr.write("{}M", readLen);
+ }
+ }
+
+ inline void adjustOverhang(QuasiAlignment& qa, uint32_t txpLen,
+ FixedWriter& cigarStr1, FixedWriter& cigarStr2) {
+ if (qa.isPaired) { // both mapped
+ adjustOverhang(qa.pos, qa.readLen, txpLen, cigarStr1);
+ adjustOverhang(qa.matePos, qa.mateLen, txpLen, cigarStr2);
+ } else if (qa.mateStatus == MateStatus::PAIRED_END_LEFT ) {
+ // left read mapped
+ adjustOverhang(qa.pos, qa.readLen, txpLen, cigarStr1);
+ // right read un-mapped will just be read length * S
+ cigarStr2.clear();
+ cigarStr2.write("{}S", qa.mateLen);
+ } else if (qa.mateStatus == MateStatus::PAIRED_END_RIGHT) {
+ // right read mapped
+ adjustOverhang(qa.pos, qa.readLen, txpLen, cigarStr2);
+ // left read un-mapped will just be read length * S
+ cigarStr1.clear();
+ cigarStr1.write("{}S", qa.readLen);
+ }
+ }
+
+
+
+ // get the sam flags for the quasialignment qaln.
+ // peinput is true if the read is paired in *sequencing*; false otherwise
+ // the sam flags for mate 1 are written into flags1 and for mate2 into flags2
+ inline void getSamFlags(const QuasiAlignment& qaln,
+ uint16_t& flags) {
+ constexpr uint16_t pairedInSeq = 0x1;
+ constexpr uint16_t mappedInProperPair = 0x2;
+ constexpr uint16_t unmapped = 0x4;
+ constexpr uint16_t mateUnmapped = 0x8;
+ constexpr uint16_t isRC = 0x10;
+ constexpr uint16_t mateIsRC = 0x20;
+ constexpr uint16_t isRead1 = 0x40;
+ constexpr uint16_t isRead2 = 0x80;
+ constexpr uint16_t isSecondaryAlignment = 0x100;
+ constexpr uint16_t failedQC = 0x200;
+ constexpr uint16_t isPCRDup = 0x400;
+ constexpr uint16_t supplementaryAln = 0x800;
+
+ flags = 0;
+ // Not paired in sequencing
+ // flags1 = (peInput) ? pairedInSeq : 0;
+ // flags |= properlyAligned;
+ // we don't output unmapped yet
+ // flags |= unmapped
+ // flags |= mateUnmapped
+ flags |= (qaln.fwd) ? 0 : isRC;
+ // Mate flag meaningless
+ // flags1 |= (qaln.mateIsFwd) ? 0 : mateIsRC;
+ // flags |= isRead1;
+ //flags2 |= isRead2;
+ }
+
+ // get the sam flags for the quasialignment qaln.
+ // peinput is true if the read is paired in *sequencing*; false otherwise
+ // the sam flags for mate 1 are written into flags1 and for mate2 into flags2
+ inline void getSamFlags(const QuasiAlignment& qaln,
+ bool peInput,
+ uint16_t& flags1,
+ uint16_t& flags2) {
+ constexpr uint16_t pairedInSeq = 0x1;
+ constexpr uint16_t properlyAligned = 0x2;
+ constexpr uint16_t unmapped = 0x4;
+ constexpr uint16_t mateUnmapped = 0x8;
+ constexpr uint16_t isRC = 0x10;
+ constexpr uint16_t mateIsRC = 0x20;
+ constexpr uint16_t isRead1 = 0x40;
+ constexpr uint16_t isRead2 = 0x80;
+ constexpr uint16_t isSecondaryAlignment = 0x100;
+ constexpr uint16_t failedQC = 0x200;
+ constexpr uint16_t isPCRDup = 0x400;
+ constexpr uint16_t supplementaryAln = 0x800;
+
+ flags1 = flags2 = 0;
+ flags1 = (peInput) ? pairedInSeq : 0;
+ flags1 |= (qaln.isPaired) ? properlyAligned : 0;
+ flags2 = flags1;
+ // we don't output unmapped yet
+ bool read1Unaligned = qaln.mateStatus == MateStatus::PAIRED_END_RIGHT;
+ bool read2Unaligned = qaln.mateStatus == MateStatus::PAIRED_END_LEFT;
+ // If read 1 is unaligned, flags1 gets "unmapped" and flags2 gets "mate unmapped"
+ flags1 |= (read1Unaligned) ? unmapped : 0;
+ flags2 |= (read1Unaligned) ? mateUnmapped : 0;
+ // If read 2 is unaligned, flags2 gets "unmapped" and flags1 gets "mate unmapped"
+ flags2 |= (read2Unaligned) ? unmapped : 0;
+ flags1 |= (read2Unaligned) ? mateUnmapped : 0;
+
+ flags1 |= (qaln.fwd) ? 0 : isRC;
+ flags1 |= (qaln.mateIsFwd) ? 0 : mateIsRC;
+ flags2 |= (qaln.mateIsFwd) ? 0 : isRC;
+ flags2 |= (qaln.fwd) ? 0 : mateIsRC;
+ flags1 |= isRead1;
+ flags2 |= isRead2;
+ }
+
+ // Adapted from
+ // https://github.com/mengyao/Complete-Striped-Smith-Waterman-Library/blob/8c9933a1685e0ab50c7d8b7926c9068bc0c9d7d2/src/main.c#L36
+ void reverseRead(std::string& seq,
+ std::string& qual,
+ std::string& readWork,
+ std::string& qualWork);
+
+ template <typename ReadPairT, typename IndexT>
+ uint32_t writeAlignmentsToStream(
+ ReadPairT& r,
+ PairAlignmentFormatter<IndexT>& formatter,
+ HitCounters& hctr,
+ std::vector<QuasiAlignment>& jointHits,
+ fmt::MemoryWriter& sstream);
+
+ template <typename ReadT, typename IndexT>
+ uint32_t writeAlignmentsToStream(
+ ReadT& r,
+ SingleAlignmentFormatter<IndexT>& formatter,
+ HitCounters& hctr,
+ std::vector<QuasiAlignment>& jointHits,
+ fmt::MemoryWriter& sstream);
+
+ inline void mergeLeftRightHitsFuzzy(
+ bool leftMatches,
+ bool rightMatches,
+ std::vector<QuasiAlignment>& leftHits,
+ std::vector<QuasiAlignment>& rightHits,
+ std::vector<QuasiAlignment>& jointHits,
+ uint32_t readLen,
+ uint32_t maxNumHits,
+ bool& tooManyHits,
+ HitCounters& hctr) {
+
+ if (leftHits.empty()) {
+ if (!leftMatches) {
+ if (!rightHits.empty()) {
+ jointHits.insert(jointHits.end(),
+ std::make_move_iterator(rightHits.begin()),
+ std::make_move_iterator(rightHits.end()));
+ hctr.seHits += rightHits.size();
+ }
+ }
+ } else if (rightHits.empty()) {
+ if (!rightMatches) {
+ if (!leftHits.empty()) {
+ jointHits.insert(jointHits.end(),
+ std::make_move_iterator(leftHits.begin()),
+ std::make_move_iterator(leftHits.end()));
+ hctr.seHits += leftHits.size();
+ }
+ }
+ } else {
+ constexpr const int32_t signedZero{0};
+ auto leftIt = leftHits.begin();
+ auto leftEnd = leftHits.end();
+ auto leftLen = std::distance(leftIt, leftEnd);
+ if (rightHits.size() > 0) {
+ auto rightIt = rightHits.begin();
+ auto rightEnd = rightHits.end();
+ auto rightLen = std::distance(rightIt, rightEnd);
+ size_t numHits{0};
+ jointHits.reserve(std::min(leftLen, rightLen));
+ uint32_t leftTxp, rightTxp;
+ while (leftIt != leftEnd && rightIt != rightEnd) {
+ leftTxp = leftIt->tid;
+ rightTxp = rightIt->tid;
+ if (leftTxp < rightTxp) {
+ ++leftIt;
+ } else {
+ if (!(rightTxp < leftTxp)) {
+ int32_t startRead1 = std::max(leftIt->pos, signedZero);
+ int32_t startRead2 = std::max(rightIt->pos, signedZero);
+ bool read1First{(startRead1 < startRead2)};
+ int32_t fragStartPos = read1First ? startRead1 : startRead2;
+ int32_t fragEndPos = read1First ?
+ (startRead2 + rightIt->readLen) : (startRead1 + leftIt->readLen);
+ uint32_t fragLen = fragEndPos - fragStartPos;
+ jointHits.emplace_back(leftTxp,
+ leftIt->pos,
+ leftIt->fwd,
+ leftIt->readLen,
+ fragLen, true);
+ // Fill in the mate info
+ auto& qaln = jointHits.back();
+ qaln.mateLen = rightIt->readLen;
+ qaln.matePos = rightIt->pos;
+ qaln.mateIsFwd = rightIt->fwd;
+ jointHits.back().mateStatus = MateStatus::PAIRED_END_PAIRED;
+
+ ++numHits;
+ if (numHits > maxNumHits) { tooManyHits = true; break; }
+ ++leftIt;
+ }
+ ++rightIt;
+ }
+ }
+ }
+ if (tooManyHits) { jointHits.clear(); ++hctr.tooManyHits; }
+ }
+
+ // If we had proper paired hits
+ if (jointHits.size() > 0) {
+ hctr.peHits += jointHits.size();
+ //orphanStatus = 0;
+ }
+ }
+
+ inline void mergeLeftRightHits(
+ std::vector<QuasiAlignment>& leftHits,
+ std::vector<QuasiAlignment>& rightHits,
+ std::vector<QuasiAlignment>& jointHits,
+ uint32_t readLen,
+ uint32_t maxNumHits,
+ bool& tooManyHits,
+ HitCounters& hctr) {
+ if (leftHits.size() > 0) {
+ constexpr const int32_t signedZero{0};
+ auto leftIt = leftHits.begin();
+ auto leftEnd = leftHits.end();
+ auto leftLen = std::distance(leftIt, leftEnd);
+ if (rightHits.size() > 0) {
+ auto rightIt = rightHits.begin();
+ auto rightEnd = rightHits.end();
+ auto rightLen = std::distance(rightIt, rightEnd);
+ size_t numHits{0};
+ jointHits.reserve(std::min(leftLen, rightLen));
+ uint32_t leftTxp, rightTxp;
+ while (leftIt != leftEnd && rightIt != rightEnd) {
+ leftTxp = leftIt->tid;
+ rightTxp = rightIt->tid;
+ if (leftTxp < rightTxp) {
+ ++leftIt;
+ } else {
+ if (!(rightTxp < leftTxp)) {
+ int32_t startRead1 = std::max(leftIt->pos, signedZero);
+ int32_t startRead2 = std::max(rightIt->pos, signedZero);
+ bool read1First{(startRead1 < startRead2)};
+ int32_t fragStartPos = read1First ? startRead1 : startRead2;
+ int32_t fragEndPos = read1First ?
+ (startRead2 + rightIt->readLen) : (startRead1 + leftIt->readLen);
+ uint32_t fragLen = fragEndPos - fragStartPos;
+ jointHits.emplace_back(leftTxp,
+ startRead1,
+ leftIt->fwd,
+ leftIt->readLen,
+ fragLen, true);
+ // Fill in the mate info
+ auto& qaln = jointHits.back();
+ qaln.mateLen = rightIt->readLen;
+ qaln.matePos = startRead2;
+ qaln.mateIsFwd = rightIt->fwd;
+ jointHits.back().mateStatus = MateStatus::PAIRED_END_PAIRED;
+
+ ++numHits;
+ if (numHits > maxNumHits) { tooManyHits = true; break; }
+ ++leftIt;
+ }
+ ++rightIt;
+ }
+ }
+ }
+ if (tooManyHits) { jointHits.clear(); ++hctr.tooManyHits; }
+ }
+
+ // If we had proper paired hits
+ if (jointHits.size() > 0) {
+ hctr.peHits += jointHits.size();
+ //orphanStatus = 0;
+ } else if (leftHits.size() + rightHits.size() > 0 and !tooManyHits) {
+ // If there weren't proper paired hits, then either
+ // there were too many hits, and we forcibly discarded the read
+ // or we take the single end hits.
+ auto numHits = leftHits.size() + rightHits.size();
+ hctr.seHits += numHits;
+ //orphanStatus = 0;
+ //orphanStatus |= (leftHits.size() > 0) ? 0x1 : 0;
+ //orphanStatus |= (rightHits.size() > 0) ? 0x2 : 0;
+ jointHits.insert(jointHits.end(),
+ std::make_move_iterator(leftHits.begin()),
+ std::make_move_iterator(leftHits.end()));
+ jointHits.insert(jointHits.end(),
+ std::make_move_iterator(rightHits.begin()),
+ std::make_move_iterator(rightHits.end()));
+ }
+ }
+
+ /*
+ template <typename Archive>
+ void save(Archive& archive, const my_mer& mer);
+
+ template <typename Archive>
+ void load(Archive& archive, my_mer& mer);
+ */
+ }
+}
+
+
+#endif // __RAP_MAP_UTILS_HPP__
diff --git a/debian/rapmap/SACollector.hpp b/debian/rapmap/SACollector.hpp
new file mode 100644
index 0000000..261b2ce
--- /dev/null
+++ b/debian/rapmap/SACollector.hpp
@@ -0,0 +1,580 @@
+#ifndef SA_COLLECTOR_HPP
+#define SA_COLLECTOR_HPP
+
+#include "RapMapUtils.hpp"
+#include "RapMapSAIndex.hpp"
+#include "SASearcher.hpp"
+
+#include <iostream>
+#include <algorithm>
+#include <iterator>
+
+template <typename RapMapIndexT>
+class SACollector {
+ public:
+ using OffsetT = typename RapMapIndexT::IndexType;
+
+ SACollector(RapMapIndexT* rmi) : rmi_(rmi) {}
+ bool operator()(std::string& read,
+ std::vector<rapmap::utils::QuasiAlignment>& hits,
+ SASearcher<RapMapIndexT>& saSearcher,
+ rapmap::utils::MateStatus mateStatus,
+ bool strictCheck=false,
+ bool consistentHits=false) {
+
+ using QuasiAlignment = rapmap::utils::QuasiAlignment;
+ using MateStatus = rapmap::utils::MateStatus;
+
+ //auto& posIDs = rmi_->positionIDs;
+ auto& rankDict = rmi_->rankDict;
+ auto& txpStarts = rmi_->txpOffsets;
+ auto& SA = rmi_->SA;
+ auto& khash = rmi_->khash;
+ auto& text = rmi_->seq;
+ uint32_t sampFactor{1};
+ auto salen = SA.size();
+
+ auto readLen = read.length();
+ auto maxDist = 1.5 * readLen;
+ auto k = rapmap::utils::my_mer::k();
+ auto readStartIt = read.begin();
+ auto readEndIt = read.end();
+
+ auto readRevStartIt = read.rbegin();
+ auto readRevEndIt = read.rend();
+
+ auto rb = read.begin();
+ auto re = rb + k;
+ OffsetT lbLeftFwd = 0, ubLeftFwd = 0;
+ OffsetT lbLeftRC = 0, ubLeftRC = 0;
+ OffsetT lbRightFwd = 0, ubRightFwd = 0;
+ OffsetT lbRightRC = 0, ubRightRC = 0;
+ OffsetT matchedLen;
+
+ uint32_t fwdHit{0};
+ uint32_t rcHit{0};
+
+ bool foundHit = false;
+ bool isRev = false;
+ rapmap::utils::my_mer mer;
+ rapmap::utils::my_mer rcMer;
+
+ enum HitStatus { ABSENT = -1, UNTESTED = 0, PRESENT = 1 };
+ // Record if k-mers are hits in the
+ // fwd direction, rc direction or both
+ struct KmerDirScore {
+ KmerDirScore(rapmap::utils::my_mer kmerIn, int32_t kposIn, HitStatus fwdScoreIn, HitStatus rcScoreIn) :
+ kmer(kmerIn), kpos(kposIn), fwdScore(fwdScoreIn), rcScore(rcScoreIn) {}
+ KmerDirScore() : kpos(0), fwdScore(UNTESTED), rcScore(UNTESTED) {}
+ bool operator==(const KmerDirScore& other) const { return kpos == other.kpos; }
+ bool operator<(const KmerDirScore& other) const { return kpos < other.kpos; }
+ void print() {
+ std::cerr << "{ " << kmer.to_str() << ", " << kpos << ", " << ((fwdScore) ? "PRESENT" : "ABSENT") << ", " << ((rcScore) ? "PRESENT" : "ABSENT") << "}\t";
+ }
+ rapmap::utils::my_mer kmer;
+ int32_t kpos;
+ HitStatus fwdScore;
+ HitStatus rcScore;
+ };
+
+ // This allows implementing our heurisic for comparing
+ // forward and reverse-complement strand matches
+ std::vector<KmerDirScore> kmerScores;
+
+ using SAIntervalHit = rapmap::utils::SAIntervalHit<OffsetT>;
+
+ std::vector<SAIntervalHit> fwdSAInts;
+ std::vector<SAIntervalHit> rcSAInts;
+
+ std::vector<uint32_t> leftTxps, leftTxpsRC;
+ std::vector<uint32_t> rightTxps, rightTxpsRC;
+ OffsetT maxInterval{1000};
+
+ // The number of bases that a new query position (to which
+ // we skipped) should overlap the previous extension. A
+ // value of 0 means no overlap (the new search begins at the next
+ // base) while a value of (k - 1) means that k-1 bases (one less than
+ // the k-mer size) must overlap.
+ OffsetT skipOverlap = k-1;
+ // Number of nucleotides to skip when encountering a homopolymer k-mer.
+ OffsetT homoPolymerSkip = k/2;
+
+ // Find a hit within the read
+ // While we haven't fallen off the end
+ while (re < read.end()) {
+
+ // Get the k-mer at the current start position.
+ // And make sure that it's valid (contains no Ns).
+ auto pos = std::distance(readStartIt, rb);
+ auto invalidPos = read.find_first_of("nN", pos);
+ if (invalidPos <= pos + k) {
+ rb = read.begin() + invalidPos + 1;
+ re = rb + k;
+ continue;
+ }
+
+ // If the next k-bases are valid, get the k-mer and
+ // reverse complement k-mer
+ mer = rapmap::utils::my_mer(read.c_str() + pos);
+ if (mer.is_homopolymer()) { rb += homoPolymerSkip; re += homoPolymerSkip; continue; }
+ rcMer = mer.get_reverse_complement();
+
+ // See if we can find this k-mer in the hash
+ auto merIt = khash.find(mer.get_bits(0, 2*k));
+ auto rcMerIt = khash.find(rcMer.get_bits(0, 2*k));
+
+ // If we can find the k-mer in the hash, get its SA interval
+ if (merIt != khash.end()) {
+ OffsetT lb = merIt->second.begin;
+ OffsetT ub = merIt->second.end;
+
+ // lb must be 1 *less* then the current lb
+ auto lbRestart = std::max(static_cast<OffsetT>(0), lb-1);
+ // Extend the SA interval using the read sequence as far as
+ // possible
+ std::tie(lbLeftFwd, ubLeftFwd, matchedLen) =
+ saSearcher.extendSearchNaive(lbRestart, ub, k, rb, readEndIt);
+
+ // If the SA interval is valid, and not too wide, then record
+ // the hit.
+ OffsetT diff = ubLeftFwd - lbLeftFwd;
+ if (ubLeftFwd > lbLeftFwd and diff < maxInterval) {
+ auto queryStart = std::distance(read.begin(), rb);
+ fwdSAInts.emplace_back(lbLeftFwd, ubLeftFwd, matchedLen, queryStart, false);
+ if (strictCheck) {
+ ++fwdHit;
+ // If we also match this k-mer in the rc direction
+ if (rcMerIt != khash.end()) {
+ ++rcHit;
+ kmerScores.emplace_back(mer, pos, PRESENT, PRESENT);
+ } else { // Otherwise it doesn't match in the rc direction
+ kmerScores.emplace_back(mer, pos, PRESENT, ABSENT);
+ }
+
+ // If we didn't end the match b/c we exhausted the query
+ // test the mismatching k-mer in the other strand
+ // TODO: check for 'N'?
+ if (rb + matchedLen < readEndIt){
+ auto kmerPos = std::distance(readStartIt, rb + matchedLen - skipOverlap);
+ mer = rapmap::utils::my_mer(read.c_str() + kmerPos);
+ kmerScores.emplace_back(mer, kmerPos, ABSENT, UNTESTED);
+ }
+ } else { // no strict check
+ ++fwdHit;
+ if (rcMerIt != khash.end()) { ++rcHit; }
+ }
+ }
+ }
+
+ // See if the reverse complement k-mer is in the hash
+ if (rcMerIt != khash.end()) {
+ lbLeftRC = rcMerIt->second.begin;
+ ubLeftRC = rcMerIt->second.end;
+ OffsetT diff = ubLeftRC - lbLeftRC;
+ if (ubLeftRC > lbLeftRC) {
+ // The original k-mer didn't match in the foward direction
+ if (!fwdHit) {
+ ++rcHit;
+ if (strictCheck) {
+ kmerScores.emplace_back(mer, pos, ABSENT, PRESENT);
+ }
+ }
+ }
+ }
+
+ // If we had a hit with either k-mer then we can
+ // break out of this loop to look for the next informative position
+ if (fwdHit + rcHit > 0) {
+ foundHit = true;
+ break;
+ }
+ ++rb; ++re;
+ }
+
+ // If we went the entire length of the read without finding a hit
+ // then we can bail.
+ if (!foundHit) { return false; }
+
+ bool lastSearch{false};
+ // If we had a hit on the forward strand
+ if (fwdHit) {
+
+ // The length of this match
+ auto matchLen = fwdSAInts.front().len;
+ // The iterator to where this match began
+ rb = read.begin() + fwdSAInts.front().queryPos;
+
+ // [lb, ub) is the suffix array interval for the MMP (maximum mappable prefix)
+ // of the k-mer we found. The NIP (next informative position) in the sequence
+ // is the position after the LCE (longest common extension) of
+ // T[SA[lb]:] and T[SA[ub-1]:]
+ auto remainingLength = std::distance(rb + matchLen, readEndIt);
+ auto lce = saSearcher.lce(lbLeftFwd, ubLeftFwd-1, matchLen, remainingLength);
+ auto fwdSkip = std::max(static_cast<OffsetT>(matchLen) - skipOverlap,
+ static_cast<OffsetT>(lce) - skipOverlap);
+
+ size_t nextInformativePosition = std::min(
+ std::max(static_cast<OffsetT>(0),
+ static_cast<OffsetT>(readLen)- static_cast<OffsetT>(k)),
+ static_cast<OffsetT>(std::distance(readStartIt, rb) + fwdSkip)
+ );
+
+ rb = read.begin() + nextInformativePosition;
+ re = rb + k;
+
+ size_t invalidPos{0};
+ while (re <= readEndIt) {
+ // The offset into the string
+ auto pos = std::distance(readStartIt, rb);
+
+ // The position of the first N in the k-mer (if there is one)
+ // If we have already verified there are no Ns in the remainder
+ // of the string (invalidPos is std::string::npos) then we can
+ // skip this test.
+ if (invalidPos != std::string::npos) {
+ invalidPos = read.find_first_of("nN", pos);
+ }
+
+ // If the first N is within k bases, then this k-mer is invalid
+ if (invalidPos < pos + k) {
+ // A valid k-mer can't start until after the 'N'
+ nextInformativePosition = invalidPos + 1;
+ rb = read.begin() + nextInformativePosition;
+ re = rb + k;
+ // Go to the next iteration of the while loop
+ continue;
+ }
+
+ // If the current end position is valid
+ if (re <= readEndIt) {
+
+ mer = rapmap::utils::my_mer(read.c_str() + pos);
+ if (mer.is_homopolymer()) { rb += homoPolymerSkip; re = rb + k; continue; }
+ auto merIt = khash.find(mer.get_bits(0, 2*k));
+
+ if (merIt != khash.end()) {
+ if (strictCheck) {
+ ++fwdHit;
+ kmerScores.emplace_back(mer, pos, PRESENT, UNTESTED);
+ auto rcMer = mer.get_reverse_complement();
+ auto rcMerIt = khash.find(rcMer.get_bits(0, 2*k));
+ if (rcMerIt != khash.end()) {
+ ++rcHit;
+ kmerScores.back().rcScore = PRESENT;
+ }
+ }
+
+ lbRightFwd = merIt->second.begin;
+ ubRightFwd = merIt->second.end;
+
+ // lb must be 1 *less* then the current lb
+ lbRightFwd = std::max(static_cast<OffsetT>(0), lbRightFwd - 1);
+ std::tie(lbRightFwd, ubRightFwd, matchedLen) =
+ saSearcher.extendSearchNaive(lbRightFwd, ubRightFwd,
+ k, rb, readEndIt);
+
+ OffsetT diff = ubRightFwd - lbRightFwd;
+ if (ubRightFwd > lbRightFwd and diff < maxInterval) {
+ auto queryStart = std::distance(read.begin(), rb);
+ fwdSAInts.emplace_back(lbRightFwd, ubRightFwd, matchedLen, queryStart, false);
+ // If we didn't end the match b/c we exhausted the query
+ // test the mismatching k-mer in the other strand
+ // TODO: check for 'N'?
+ if (strictCheck and rb + matchedLen < readEndIt){
+ auto kmerPos = std::distance(readStartIt, rb + matchedLen - skipOverlap);
+ mer = rapmap::utils::my_mer(read.c_str() + kmerPos);
+ // TODO: 04/11/16
+ kmerScores.emplace_back(mer, kmerPos, UNTESTED, UNTESTED);
+ }
+
+ }
+
+ if (lastSearch) { break; }
+ auto mismatchIt = rb + matchedLen;
+ if (mismatchIt < readEndIt) {
+ auto remainingDistance = std::distance(mismatchIt, readEndIt);
+ auto lce = saSearcher.lce(lbRightFwd, ubRightFwd-1, matchedLen, remainingDistance);
+
+ // Where we would jump if we just used the MMP
+ auto skipMatch = mismatchIt - skipOverlap;
+ // Where we would jump if we used the LCE
+ auto skipLCE = rb + lce - skipOverlap;
+ // Pick the larger of the two
+ rb = std::max(skipLCE, skipMatch);
+ if (rb > (readEndIt - k)) {
+ rb = readEndIt - k;
+ lastSearch = true;
+ }
+ re = rb + k;
+ } else {
+ lastSearch = true;
+ rb = readEndIt - k;
+ re = rb + k;
+ }
+
+ } else {
+ rb += sampFactor;
+ re = rb + k;
+ }
+ }
+ }
+ }
+
+ lastSearch = false;
+ if (rcHit >= fwdHit) {
+ size_t pos{read.length() - k};
+
+ auto revReadEndIt = read.rend();
+
+ auto revRB = read.rbegin();
+ auto revRE = revRB + k;
+
+ auto invalidPosIt = revRB;
+ while (revRE <= revReadEndIt){
+
+ revRE = revRB + k;
+ if (revRE > revReadEndIt) { break; }
+
+ // See if this k-mer would contain an N
+ // only check if we don't yet know that there are no remaining
+ // Ns
+ if (invalidPosIt != revReadEndIt) {
+ invalidPosIt = std::find_if(revRB, revRE,
+ [](const char c) -> bool {
+ return c == 'n' or c == 'N';
+ });
+ }
+
+ // If we found an N before the end of the k-mer
+ if (invalidPosIt < revRE) {
+ // Skip to the k-mer starting at the next position
+ // (i.e. right past the N)
+ revRB = invalidPosIt + 1;
+ continue;
+ }
+
+ // The distance from the beginning of the read to the
+ // start of the k-mer
+ pos = std::distance(revRE, revReadEndIt);
+
+ // Get the k-mer and query it in the hash
+ mer = rapmap::utils::my_mer(read.c_str() + pos);
+ if (mer.is_homopolymer()) { revRB += homoPolymerSkip; revRE += homoPolymerSkip; continue; }
+ rcMer = mer.get_reverse_complement();
+ auto rcMerIt = khash.find(rcMer.get_bits(0, 2*k));
+
+ // If we found the k-mer
+ if (rcMerIt != khash.end()) {
+ if (strictCheck) {
+ ++rcHit;
+ kmerScores.emplace_back(mer, pos, UNTESTED, PRESENT);
+ auto merIt = khash.find(mer.get_bits(0, 2*k));
+ if (merIt != khash.end()) {
+ ++fwdHit;
+ kmerScores.back().fwdScore = PRESENT;
+ }
+ }
+
+
+ lbRightRC = rcMerIt->second.begin;
+ ubRightRC = rcMerIt->second.end;
+
+ // lb must be 1 *less* then the current lb
+ // We can't move any further in the reverse complement direction
+ lbRightRC = std::max(static_cast<OffsetT>(0), lbRightRC - 1);
+ std::tie(lbRightRC, ubRightRC, matchedLen) =
+ saSearcher.extendSearchNaive(lbRightRC, ubRightRC, k,
+ revRB, revReadEndIt, true);
+
+ OffsetT diff = ubRightRC - lbRightRC;
+ if (ubRightRC > lbRightRC and diff < maxInterval) {
+ auto queryStart = std::distance(read.rbegin(), revRB);
+ rcSAInts.emplace_back(lbRightRC, ubRightRC, matchedLen, queryStart, true);
+ // If we didn't end the match b/c we exhausted the query
+ // test the mismatching k-mer in the other strand
+ // TODO: check for 'N'?
+ if (strictCheck and revRB + matchedLen < revReadEndIt){
+ auto kmerPos = std::distance(revRB + matchedLen, revReadEndIt);
+ mer = rapmap::utils::my_mer(read.c_str() + kmerPos);
+ // TODO: 04/11/16
+ kmerScores.emplace_back(mer, kmerPos, UNTESTED, UNTESTED);
+ }
+ }
+
+ if (lastSearch) { break; }
+ auto mismatchIt = revRB + matchedLen;
+ if (mismatchIt < revReadEndIt) {
+ auto remainingDistance = std::distance(mismatchIt, revReadEndIt);
+ auto lce = saSearcher.lce(lbRightRC, ubRightRC-1, matchedLen, remainingDistance);
+
+ // Where we would jump if we just used the MMP
+ auto skipMatch = mismatchIt - skipOverlap;
+ // Where we would jump if we used the lce
+ auto skipLCE = revRB + lce - skipOverlap;
+ // Choose the larger of the two
+ revRB = std::max(skipLCE, skipMatch);
+ if (revRB > (revReadEndIt - k)) {
+ revRB = revReadEndIt - k;
+ lastSearch = true;
+ }
+ revRE = revRB + k;
+ } else {
+ lastSearch = true;
+ revRB = revReadEndIt - k;
+ revRE = revRB + k;
+ }
+
+ } else {
+ revRB += sampFactor;
+ revRE = revRB + k;
+ }
+ }
+ }
+
+ if (strictCheck) {
+ // The first two conditions shouldn't happen
+ // but I'm just being paranoid here
+ if (fwdHit > 0 and rcHit == 0) {
+ rcSAInts.clear();
+ } else if (rcHit > 0 and fwdHit == 0) {
+ fwdSAInts.clear();
+ } else {
+ std::sort( kmerScores.begin(), kmerScores.end() );
+ auto e = std::unique(kmerScores.begin(), kmerScores.end());
+ // Compute the score for the k-mers we need to
+ // test in both the forward and rc directions.
+ int32_t fwdScore{0};
+ int32_t rcScore{0};
+ // For every kmer score structure
+ //std::cerr << "[\n";
+ for (auto kmsIt = kmerScores.begin(); kmsIt != e; ++kmsIt) {//: kmerScores) {
+ auto& kms = *kmsIt;
+ // If the forward k-mer is untested, then test it
+ if (kms.fwdScore == UNTESTED) {
+ auto merIt = khash.find(kms.kmer.get_bits(0, 2*k));
+ kms.fwdScore = (merIt != khash.end()) ? PRESENT : ABSENT;
+ }
+ // accumulate the score
+ fwdScore += kms.fwdScore;
+
+ // If the rc k-mer is untested, then test it
+ if (kms.rcScore == UNTESTED) {
+ rcMer = kms.kmer.get_reverse_complement();
+ auto rcMerIt = khash.find(rcMer.get_bits(0, 2*k));
+ kms.rcScore = (rcMerIt != khash.end()) ? PRESENT : ABSENT;
+ }
+ // accumulate the score
+ rcScore += kms.rcScore;
+ //kms.print();
+ //std::cerr << "\n";
+ }
+ //std::cerr << "]\n";
+ // If the forward score is strictly greater
+ // then get rid of the rc hits.
+ if (fwdScore > rcScore) {
+ rcSAInts.clear();
+ } else if (rcScore > fwdScore) {
+ // If the rc score is strictly greater
+ // get rid of the forward hits
+ fwdSAInts.clear();
+ }
+ }
+ }
+
+ auto fwdHitsStart = hits.size();
+ // If we had > 1 forward hit
+ if (fwdSAInts.size() > 1) {
+ auto processedHits = rapmap::hit_manager::intersectSAHits(fwdSAInts, *rmi_, consistentHits);
+ rapmap::hit_manager::collectHitsSimpleSA(processedHits, readLen, maxDist, hits, mateStatus);
+ } else if (fwdSAInts.size() == 1) { // only 1 hit!
+ auto& saIntervalHit = fwdSAInts.front();
+ auto initialSize = hits.size();
+ for (OffsetT i = saIntervalHit.begin; i != saIntervalHit.end; ++i) {
+ auto globalPos = SA[i];
+ auto txpID = rmi_->transcriptAtPosition(globalPos);
+ // the offset into this transcript
+ auto pos = globalPos - txpStarts[txpID];
+ int32_t hitPos = pos - saIntervalHit.queryPos;
+ hits.emplace_back(txpID, hitPos, true, readLen);
+ hits.back().mateStatus = mateStatus;
+ }
+ // Now sort by transcript ID (then position) and eliminate
+ // duplicates
+ auto sortStartIt = hits.begin() + initialSize;
+ auto sortEndIt = hits.end();
+ std::sort(sortStartIt, sortEndIt,
+ [](const QuasiAlignment& a, const QuasiAlignment& b) -> bool {
+ if (a.tid == b.tid) {
+ return a.pos < b.pos;
+ } else {
+ return a.tid < b.tid;
+ }
+ });
+ auto newEnd = std::unique(hits.begin() + initialSize, hits.end(),
+ [] (const QuasiAlignment& a, const QuasiAlignment& b) -> bool {
+ return a.tid == b.tid;
+ });
+ hits.resize(std::distance(hits.begin(), newEnd));
+ }
+ auto fwdHitsEnd = hits.size();
+
+ auto rcHitsStart = fwdHitsEnd;
+ // If we had > 1 rc hit
+ if (rcSAInts.size() > 1) {
+ auto processedHits = rapmap::hit_manager::intersectSAHits(rcSAInts, *rmi_, consistentHits);
+ rapmap::hit_manager::collectHitsSimpleSA(processedHits, readLen, maxDist, hits, mateStatus);
+ } else if (rcSAInts.size() == 1) { // only 1 hit!
+ auto& saIntervalHit = rcSAInts.front();
+ auto initialSize = hits.size();
+ for (OffsetT i = saIntervalHit.begin; i != saIntervalHit.end; ++i) {
+ auto globalPos = SA[i];
+ auto txpID = rmi_->transcriptAtPosition(globalPos);
+ // the offset into this transcript
+ auto pos = globalPos - txpStarts[txpID];
+ int32_t hitPos = pos - saIntervalHit.queryPos;
+ hits.emplace_back(txpID, hitPos, false, readLen);
+ hits.back().mateStatus = mateStatus;
+ }
+ // Now sort by transcript ID (then position) and eliminate
+ // duplicates
+ auto sortStartIt = hits.begin() + rcHitsStart;
+ auto sortEndIt = hits.end();
+ std::sort(sortStartIt, sortEndIt,
+ [](const QuasiAlignment& a, const QuasiAlignment& b) -> bool {
+ if (a.tid == b.tid) {
+ return a.pos < b.pos;
+ } else {
+ return a.tid < b.tid;
+ }
+ });
+ auto newEnd = std::unique(sortStartIt, sortEndIt,
+ [] (const QuasiAlignment& a, const QuasiAlignment& b) -> bool {
+ return a.tid == b.tid;
+ });
+ hits.resize(std::distance(hits.begin(), newEnd));
+ }
+ auto rcHitsEnd = hits.size();
+
+ // If we had both forward and RC hits, then merge them
+ if ((fwdHitsEnd > fwdHitsStart) and (rcHitsEnd > rcHitsStart)) {
+ // Merge the forward and reverse hits
+ std::inplace_merge(hits.begin() + fwdHitsStart, hits.begin() + fwdHitsEnd, hits.begin() + rcHitsEnd,
+ [](const QuasiAlignment& a, const QuasiAlignment& b) -> bool {
+ return a.tid < b.tid;
+ });
+ // And get rid of duplicate transcript IDs
+ auto newEnd = std::unique(hits.begin() + fwdHitsStart, hits.begin() + rcHitsEnd,
+ [] (const QuasiAlignment& a, const QuasiAlignment& b) -> bool {
+ return a.tid == b.tid;
+ });
+ hits.resize(std::distance(hits.begin(), newEnd));
+ }
+ // Return true if we had any valid hits and false otherwise.
+ return foundHit;
+ }
+
+ private:
+ RapMapIndexT* rmi_;
+};
+
+#endif // SA_COLLECTOR_HPP
diff --git a/debian/rapmap/SASearcher.hpp b/debian/rapmap/SASearcher.hpp
new file mode 100644
index 0000000..b36e476
--- /dev/null
+++ b/debian/rapmap/SASearcher.hpp
@@ -0,0 +1,631 @@
+#ifndef SA_SEARCHER_HPP
+#define SA_SEARCHER_HPP
+
+#include <vector>
+#include <algorithm>
+#include <iterator>
+#include "jellyfish/mer_dna.hpp"
+
+#include "RapMapUtils.hpp"
+#include "RapMapSAIndex.hpp"
+
+template <typename RapMapIndexT>
+class SASearcher {
+ public:
+ using OffsetT = typename RapMapIndexT::IndexType;
+
+ SASearcher(RapMapIndexT* rmi) :
+ rmi_(rmi), seq_(&rmi->seq), sa_(&rmi->SA) {}
+
+ int cmp(std::string::iterator abeg,
+ std::string::iterator aend,
+ std::string::iterator bbeg,
+ std::string::iterator bend) {
+ auto ait = abeg;
+ auto bit = bbeg;
+ //size_t la = a.length();
+ //size_t lb = b.length();
+ while (ait < aend and bit < bend) {
+ if (*ait < *bit) {
+ return -1;
+ } else if (*ait > *bit) {
+ return 1;
+ }
+ ++ait;
+ ++bit;
+ }
+ if (bit == bend and ait < aend) {
+ return 1;
+ }
+ return 0;
+ }
+
+ enum class SearchDirection : uint8_t {
+ UP = 0, DOWN
+ };
+
+ template <typename IndexT>
+ struct BoundSearchResult {
+ IndexT maxLen;
+ IndexT bound;
+ SearchDirection dir;
+ };
+
+
+
+ /**
+ * OK! It should be (is) possible to figure out what we need with only two binary
+ * searches. However, that seems to have some tricky corner cases and has been
+ * somewhat illusive so far. This "naive" version performs *3* binary searches.
+ * The first determines the length of the maximum mappable prefix (MMP). The second
+ * finds the lower bound for the query interval and the third finds the upper bound.
+ * The final binary search *is* optimized (it has a lower bound given by the value)
+ * returned by second search. However, this method is likely a bit slower than the
+ * one above (when it can be made to work correctly at all times).
+ */
+ template <typename IteratorT>
+ std::tuple<OffsetT, OffsetT, OffsetT> extendSearchNaive(
+ OffsetT lbIn, // The lower bound for the search
+ OffsetT ubIn, // The upper bound for the search
+ OffsetT startAt, // The offset at which to start looking
+ IteratorT qb, // Iterator to the beginning of the query
+ IteratorT qe, // Iterator to the end of the query
+ bool complementBases=false // True if bases should be complemented
+ // before comparison
+ ) {
+
+ std::vector<OffsetT>& SA = *sa_;
+ std::string& seq = *seq_;
+
+ int64_t m = std::distance(qb, qe);
+ size_t n = seq.length();
+
+ auto sb = seq.begin();
+ auto se = seq.end();
+
+ // If the bounds are already trivial, just figure how long
+ // of a prefix we share and return the interval.
+ if (ubIn - lbIn == 2) {
+ lbIn += 1;
+ auto i = startAt;
+ while (i < m and SA[lbIn] + i < n) {
+ char queryChar = ::toupper(*(qb + i));
+ // If we're reverse complementing
+ if (complementBases) {
+ queryChar = rapmap::utils::my_mer::complement(queryChar);
+ }
+ if ( queryChar < *(sb + SA[lbIn] + i) ) {
+ break;
+ } else if ( queryChar > *(sb + SA[lbIn] + i)) {
+ break;
+ }
+ ++i;
+ }
+ return std::make_tuple(lbIn, ubIn, static_cast<OffsetT>(i));
+ }
+
+ BoundSearchResult<OffsetT> res1, res2;
+
+ char smallest = '#';
+ char largest = '}';
+ char sentinel = smallest;
+
+ // FIX: these have to be large enough to hold the *sum* of the boundaries!
+ int64_t l = lbIn, r = ubIn;
+ int64_t lcpLP = startAt, lcpRP = startAt;
+ int64_t c{0};
+ int64_t i{0};
+
+ int64_t maxI{startAt};
+ int64_t prevI = startAt;
+ int64_t prevILow = startAt;
+ int64_t prevIHigh = startAt;
+ int64_t validBoundLow = ubIn;
+ int64_t validBoundHigh = lbIn;
+ int64_t validBound = 0;
+ bool plt{true};
+ // Reduce the search interval until we hit a border
+ // i.e. until c == r - 1 or c == l + 1
+ while (true) {
+ c = (l + r) / 2;
+ plt = true;
+ i = std::min(lcpLP, lcpRP);
+ while (i < m and SA[c] + i < n) {
+ char queryChar = ::toupper(*(qb + i));
+ // If we're reverse complementing
+ if (complementBases) {
+ queryChar = rapmap::utils::my_mer::complement(queryChar);
+ }
+
+ if ( queryChar < *(sb + SA[c] + i) ) {
+ if (i > prevIHigh) {
+ prevIHigh = i;
+ validBoundHigh = c;
+ } else if (i == prevIHigh) {
+ validBoundHigh = c < validBoundHigh ? c : validBoundHigh;
+ }
+
+ break;
+ } else if ( queryChar > *(sb + SA[c] + i)) {
+ if (i > prevILow) {
+ prevILow = i;
+ validBoundLow = c;
+ } else if (i == prevILow) {
+ validBoundLow = c > validBoundLow ? c : validBoundLow;
+ }
+ plt = false;
+ break;
+ }
+
+ ++i;
+ }
+ if (i == m or SA[c] + i == n) {
+ if (i > prevIHigh) {
+ prevIHigh = i;
+ validBoundHigh = c;
+ } else if (i == prevIHigh) {
+ validBoundHigh = c < validBoundHigh ? c : validBoundHigh;
+ }
+ }
+
+ if (plt) {
+ if (c == l + 1) {
+ auto maxI = std::max(std::max(i, prevILow), prevIHigh);
+ res1.maxLen = maxI;
+ break;
+ }
+ r = c;
+ lcpRP = i;
+ } else {
+ if (c == r - 1) {
+ maxI = std::max(std::max(i, prevILow), prevIHigh);
+ res1.maxLen = maxI;
+ break;
+ }
+ l = c;
+ lcpLP = i;
+ }
+ }
+
+ bool knownValid{true};
+ m = res1.maxLen + 1;
+
+ // first search for the lower bound
+ sentinel = '#';
+ l = lbIn;
+ r = ubIn;
+
+ lcpLP = startAt;
+ lcpRP = startAt;
+ c = 0;
+ plt = true;
+ i = startAt;
+ while (true) {
+ c = (l + r) / 2;
+ plt = true;
+ i = std::min(lcpLP, lcpRP);
+ while (i < m and SA[c] + i < n) {
+ char queryChar = (i < m - 1) ? ::toupper(*(qb + i)) : sentinel;
+ // If we're reverse complementing
+ if (queryChar != sentinel and complementBases) {
+ queryChar = rapmap::utils::my_mer::complement(queryChar);
+ }
+
+ if ( queryChar < *(sb + SA[c] + i) ) {
+ break;
+ } else if ( queryChar > *(sb + SA[c] + i)) {
+ plt = false;
+ break;
+ }
+ ++i;
+ }
+ if (plt) {
+ if (c == l + 1) {
+ res1.bound = c;
+ break;
+ }
+ r = c;
+ lcpRP = i;
+ } else {
+ if (c == r - 1) {
+ res1.bound = r;
+ break;
+ }
+ l = c;
+ lcpLP = i;
+ }
+ }
+
+ // then search for the upper bound
+ sentinel = '{';
+ l = res1.bound - 1;
+ r = ubIn;
+
+ lcpLP = startAt;
+ lcpRP = startAt;
+ c = 0;
+ plt = true;
+ i = startAt;
+ while (true) {
+ c = (l + r) / 2;
+ plt = true;
+ i = std::min(lcpLP, lcpRP);
+ while (i < m and SA[c] + i < n) {
+ char queryChar = (i < m - 1) ? ::toupper(*(qb + i)) : sentinel;
+ // If we're reverse complementing
+ if (queryChar != sentinel and complementBases) {
+ queryChar = rapmap::utils::my_mer::complement(queryChar);
+ }
+
+ if ( queryChar < *(sb + SA[c] + i) ) {
+ break;
+ } else if ( queryChar > *(sb + SA[c] + i)) {
+ plt = false;
+ break;
+ }
+ ++i;
+ }
+ if (plt) {
+ if (c == l + 1) {
+ res2.bound = c;
+ break;
+ }
+ r = c;
+ lcpRP = i;
+ } else {
+ if (c == r - 1) {
+ res2.bound = r;
+ break;
+ }
+ l = c;
+ lcpLP = i;
+ }
+ }
+
+ // Must occur at least once!
+ if (res1.bound == res2.bound) { res2.bound += 1; }
+ return std::make_tuple(static_cast<OffsetT>(res1.bound), static_cast<OffsetT>(res2.bound), static_cast<OffsetT>(res1.maxLen));
+ }
+
+
+ /**
+ * Compute the longest common extension between the suffixes
+ * at T[SA[p1]] and T[SA[p2]]. Start the comparison at `startAt`
+ * positions into the suffix, and only consider an extension
+ * going to at most position `stopAt`.
+ */
+ OffsetT lce(OffsetT p1, OffsetT p2,
+ OffsetT startAt=0,
+ OffsetT stopAt=std::numeric_limits<OffsetT>::max(),
+ bool verbose=false) {
+ std::string& seq = *seq_;
+ std::vector<OffsetT>& SA = *sa_;
+ OffsetT len = static_cast<OffsetT>(startAt);
+ auto o1 = SA[p1] + startAt;
+ auto o2 = SA[p2] + startAt;
+ auto maxIndex = std::max(o1, o2);
+ while (maxIndex + len < textLen_ and seq[o1+len] == seq[o2+len]) {
+ if (seq[o1+len] == '$') { break; }
+ if (len >= stopAt) { break; }
+ ++len;
+ }
+ return len;
+ }
+
+ private:
+ RapMapIndexT* rmi_;
+ std::string* seq_;
+ std::vector<OffsetT>* sa_;
+ OffsetT textLen_;
+};
+
+
+ /*
+ // http://www.cs.jhu.edu/~langmea/resources/lecture_notes/suffix_arrays.pdf
+ std::tuple<int, int> querySimpleAccel(std::string::iterator qb,
+ std::string::iterator qe) {
+ std::vector<int>& SA = *sa_;
+ std::string& seq = *seq_;
+ //ForwardIt it;
+ auto sb = seq.begin();
+ auto se = seq.end();
+
+ size_t n = seq.length();
+ size_t m = std::distance(qb, qe);
+ size_t l = 0, r = n;
+ size_t lcpLP = 0, lcpRP = 0;
+ size_t c{0};
+ size_t i{0};
+ bool plt{true};
+ size_t lower{0};
+ while (true) {
+ c = (l + r) / 2;
+ plt = true;
+ i = std::min(lcpLP, lcpRP);
+ while (i < m and SA[c] + i < n) {
+ if ( *(qb + i) < *(sb + SA[c] + i) ) {
+ break;
+ } else if ( *(qb + i) > *(sb + SA[c] + i)) {
+ plt = false;
+ break;
+ }
+ ++i;
+ }
+ if (plt) {
+ if (c == l + 1) { lower = c; break; }
+ r = c;
+ lcpRP = i;
+ } else {
+ if (c == r - 1) { lower = r; break; }
+ l = c;
+ lcpLP = i;
+ }
+ }
+
+ i = 0;
+ l = 0;
+ r = n;
+ lcpLP = 0;
+ lcpRP = 0;
+ size_t upper{0};
+ while (true) {
+ c = (l + r) / 2;
+ plt = true;
+ i = std::min(lcpLP, lcpRP);
+ while (i < m and SA[c] + i < n) {
+ if ( *(qb + i) < *(sb + SA[c] + i) ) {
+ break;
+ } else if ( *(qb + i) > *(sb + SA[c] + i)) {
+ plt = false;
+ break;
+ }
+ ++i;
+ }
+ if (plt) {
+ if (c == l + 1) { upper = c; break; }
+ r = c;
+ lcpRP = i;
+ } else {
+ if (c == r - 1) { upper = r; break; }
+ l = c;
+ lcpLP = i;
+ }
+ }
+ return std::make_tuple(lower, upper);
+ }
+
+
+ // http://www.cs.jhu.edu/~langmea/resources/lecture_notes/suffix_arrays.pdf
+ // templated on the iterator type so we can use a forward or revers iterator
+ template <typename IteratorT>
+ std::tuple<int, int, int> extendSearch(
+ int lbIn, // The lower bound for the search
+ int ubIn, // The upper bound for the search
+ int startAt, // The offset at which to start looking
+ IteratorT qb, // Iterator to the beginning of the query
+ IteratorT qe, // Iterator to the end of the query
+ bool complementBases=false // True if bases should be complemented
+ // before comparison
+ ) {
+
+ std::vector<int>& SA = *sa_;
+ std::string& seq = *seq_;
+
+ int m = std::distance(qb, qe);
+ size_t n = seq.length();
+
+ auto sb = seq.begin();
+ auto se = seq.end();
+
+ // If the bounds are already trivial, just figure how long
+ // of a prefix we share and return the interval.
+ if (ubIn - lbIn == 2) {
+ lbIn += 1;
+ auto i = startAt;
+ while (i < m and SA[lbIn] + i < n) {
+ char queryChar = ::toupper(*(qb + i));
+ // If we're reverse complementing
+ if (complementBases) {
+ queryChar = rapmap::utils::my_mer::complement(queryChar);
+ }
+ if ( queryChar < *(sb + SA[lbIn] + i) ) {
+ break;
+ } else if ( queryChar > *(sb + SA[lbIn] + i)) {
+ break;
+ }
+ ++i;
+ }
+ return std::make_tuple(lbIn, ubIn, i);
+ }
+
+ BoundSearchResult res1, res2;
+
+ char smallest = '#';
+ char largest = '}';
+ char sentinel = smallest;
+
+ int l = lbIn, r = ubIn;
+ int lcpLP = startAt, lcpRP = startAt;
+ int c{0};
+ int i{0};
+ int maxI{startAt};
+ int prevI = startAt;
+ int prevILow = startAt;
+ int prevIHigh = startAt;
+ int validBoundLow = ubIn;
+ int validBoundHigh = lbIn;
+ int validBound = 0;
+ bool plt{true};
+ bool prevPLT{true};
+ //std::cerr << "lbIn = " << lbIn << ", ubIn = " << ubIn << "\n";
+ // Reduce the search interval until we hit a border
+ // i.e. until c == r - 1 or c == l + 1
+ while (true) {
+ c = (l + r) / 2;
+ //std::cerr << "l = " << l << ", r = " << r << ", c = " << c << '\n';
+ plt = true;
+ i = std::min(lcpLP, lcpRP);
+ while (i < m and SA[c] + i < n) {
+ char queryChar = ::toupper(*(qb + i));
+ // If we're reverse complementing
+ if (complementBases) {
+ queryChar = rapmap::utils::my_mer::complement(queryChar);
+ }
+
+ if ( queryChar < *(sb + SA[c] + i) ) {
+ if (i > prevIHigh) {
+ prevIHigh = i;
+ validBoundHigh = c;
+ } else if (i == prevIHigh) {
+ validBoundHigh = c < validBoundHigh ? c : validBoundHigh;
+ }
+ //std::cerr << "(l = " << l << ", r = " << r << ") pattern < SA[" << c << "]\n";
+ //std::cerr << "(i = " << i << ", m = " << m << ") " << queryChar << " < " << *(sb + SA[c] + i) << "\n";
+
+ break;
+ } else if ( queryChar > *(sb + SA[c] + i)) {
+ if (i > prevILow) {
+ prevILow = i;
+ validBoundLow = c;
+ } else if (i == prevILow) {
+ validBoundLow = c > validBoundLow ? c : validBoundLow;
+ }
+ //std::cerr << "(l = " << l << ", r = " << r << ") pattern > SA[" << c << "]\n";
+ //std::cerr << "(i = " << i << ", m = " << m << ") " << queryChar << " > " << *(sb + SA[c] + i) << "\n";
+ plt = false;
+ break;
+ }
+
+ ++i;
+ }
+ if (i == m or SA[c] + i == n) {
+ if (i > prevIHigh) {
+ prevIHigh = i;
+ validBoundHigh = c;
+ } else if (i == prevIHigh) {
+ validBoundHigh = c < validBoundHigh ? c : validBoundHigh;
+ }
+ }
+
+ if (plt) {
+ if (c == l + 1) {
+ std::cerr << "path 1\n";
+ auto maxI = std::max(std::max(i, prevILow), prevIHigh);
+ res1.maxLen = maxI;
+ if (maxI == m) {
+ res1.dir = SearchDirection::DOWN;
+ res1.bound = c;
+ } else {
+ validBound = (prevILow >= prevIHigh) ? validBoundLow : validBoundHigh;
+ res1.bound = validBound;
+ res1.dir = (res1.bound == validBoundLow) ? SearchDirection::DOWN : SearchDirection::UP;
+ }
+ break;
+ }
+ r = c;
+ lcpRP = i;
+ } else {
+ if (c == r - 1) {
+ std::cerr << "path 2\n";
+ maxI = std::max(std::max(i, prevILow), prevIHigh);
+ res1.maxLen = maxI;
+ validBound = (prevILow >= prevIHigh) ? validBoundLow : validBoundHigh;
+ if (maxI == m) {
+ res1.bound = r;
+ } else {
+ res1.bound = validBound;
+ }
+ res1.dir = (res1.bound == validBoundLow) ? SearchDirection::DOWN : SearchDirection::UP;
+ break;
+ }
+ l = c;
+ lcpLP = i;
+ }
+ }
+
+
+ bool knownValid{true};
+ m = res1.maxLen + 1;
+
+ switch (res1.dir) {
+ case SearchDirection::UP:
+ sentinel = '#';
+ r = res1.bound;
+ l = lbIn;
+ std::cerr << "direction was UP; lb = " << l << ", ub = " << r << "\n";
+ std::cerr << "direction was UP; origLb = " << lbIn << ", origUb = " << ubIn << "\n";
+ break;
+ case SearchDirection::DOWN:
+ sentinel = '{';
+ r = ubIn;
+ l = res1.bound;
+ std::cerr << "direction was DOWN; lb = " << l << ", ub = " << r << "\n";
+ std::cerr << "direction was UP; origLb = " << lbIn << ", origUb = " << ubIn << "\n";
+ break;
+ }
+
+ if (r - l < 2) {
+ if (r == l) { r += 1; }
+ //std::cerr << "early exit!\n";
+ return std::make_tuple(l, r, res1.maxLen);
+ }
+
+
+ lcpLP = startAt;
+ lcpRP = startAt;
+ c = 0;
+ plt = true;
+ prevPLT = true;
+ prevI = 0;
+ prevILow = 0;
+ prevIHigh = 0;
+ i = startAt;
+ validBound = 0;
+ validBoundLow = ubIn;
+ validBoundHigh = lbIn;
+ while (true) {
+ c = (l + r) / 2;
+ plt = true;
+ i = std::min(lcpLP, lcpRP);
+ while (i < m and SA[c] + i < n) {
+ char queryChar = (i < m - 1) ? ::toupper(*(qb + i)) : sentinel;
+ // If we're reverse complementing
+ if (queryChar != sentinel and complementBases) {
+ queryChar = rapmap::utils::my_mer::complement(queryChar);
+ }
+
+ if ( queryChar < *(sb + SA[c] + i) ) {
+ break;
+ } else if ( queryChar > *(sb + SA[c] + i)) {
+ plt = false;
+ break;
+ }
+ ++i;
+ }
+ if (plt) {
+ if (c == l + 1) {
+ res2.dir = SearchDirection::DOWN;
+ res2.bound = c;
+ break;
+ }
+ r = c;
+ lcpRP = i;
+ } else {
+ if (c == r - 1) {
+ res2.bound = r;
+ break;
+ }
+ l = c;
+ lcpLP = i;
+ }
+ }
+
+ auto bound1 = std::min(res1.bound, res2.bound);
+ auto bound2 = std::max(res1.bound, res2.bound);
+ // Must occur at least once!
+ if (bound1 == bound2) { bound2 += 1; }
+ return std::make_tuple(bound1, bound2, res1.maxLen);
+ }
+ */
+
+#endif //SA_SEARCHER_HPP
diff --git a/debian/rapmap/ScopedTimer.hpp b/debian/rapmap/ScopedTimer.hpp
new file mode 100644
index 0000000..de1121c
--- /dev/null
+++ b/debian/rapmap/ScopedTimer.hpp
@@ -0,0 +1,22 @@
+#ifndef __SCOPED_TIMER_HPP__
+#define __SCOPED_TIMER_HPP__
+// from https://gist.github.com/justgord/4482447
+#include <chrono>
+#include <iostream>
+
+struct ScopedTimer
+{
+ std::chrono::high_resolution_clock::time_point t0;
+
+ ScopedTimer()
+ : t0(std::chrono::high_resolution_clock::now())
+ { }
+ ~ScopedTimer(void)
+ {
+ auto t1 = std::chrono::high_resolution_clock::now();
+ std::chrono::duration<double> elapsedSec = t1 - t0;
+ std::cerr << "Elapsed time: " << elapsedSec.count() << "s\n";
+ }
+};
+
+#endif //__SCOPED_TIMER_HPP__
diff --git a/debian/rapmap/SpinLock.hpp b/debian/rapmap/SpinLock.hpp
new file mode 100644
index 0000000..56647fa
--- /dev/null
+++ b/debian/rapmap/SpinLock.hpp
@@ -0,0 +1,25 @@
+#ifndef __SPIN_LOCK_HPP__
+#define __SPIN_LOCK_HPP__
+
+#include <atomic>
+
+// Taken from http://stackoverflow.com/questions/26583433/c11-implementation-of-spinlock-using-atomic
+class SpinLock {
+ std::atomic_flag locked = ATOMIC_FLAG_INIT ;
+public:
+ void lock() {
+ while (locked.test_and_set(std::memory_order_acquire)) { ; }
+ }
+
+ // from http://stackoverflow.com/questions/19742993/implementing-a-spinlock-in-boost-example-neededhttp://stackoverflow.com/questions/19742993/implementing-a-spinlock-in-boost-example-needed
+ // is this legit?
+ bool try_lock() {
+ return !locked.test_and_set(std::memory_order_acquire);
+ }
+
+ void unlock() {
+ locked.clear(std::memory_order_release);
+ }
+};
+
+#endif //__SPIN_LOCK_HPP__
diff --git a/debian/rapmap/bit_array.c b/debian/rapmap/bit_array.c
new file mode 100644
index 0000000..af0bc1a
--- /dev/null
+++ b/debian/rapmap/bit_array.c
@@ -0,0 +1,3160 @@
+/*
+ bit_array.c
+ project: bit array C library
+ url: https://github.com/noporpoise/BitArray/
+ maintainer: Isaac Turner <turner.isaac at gmail.com>
+ license: Public Domain, no warranty
+ date: Aug 2014
+*/
+
+// 64 bit words
+// Array length can be zero
+// Unused top bits must be zero
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <limits.h> // ULONG_MAX
+#include <errno.h>
+#include <signal.h> // needed for abort()
+#include <string.h> // memset()
+#include <assert.h>
+#include <time.h> // needed for seeding rand()
+#include <unistd.h> // need for getpid() for seeding rand number
+#include <ctype.h> // need for tolower()
+#include <errno.h> // perror()
+#include <sys/time.h> // for seeding random
+
+// Windows includes
+#if defined(_WIN32)
+#include <intrin.h>
+#endif
+
+#include "bit_array.h"
+#include "bit_macros.h"
+
+//
+// Tables of constants
+//
+
+// byte reverse look up table
+static const word_t reverse_table[256] =
+{
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
+};
+
+// Morton table for interleaving bytes
+static const word_t morton_table0[256] =
+{
+ 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
+ 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
+ 0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
+ 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
+ 0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
+ 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
+ 0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
+ 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
+ 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
+ 0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
+ 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
+ 0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
+ 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
+ 0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
+ 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
+ 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
+ 0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
+ 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
+ 0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
+ 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
+ 0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
+ 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
+ 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
+ 0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
+ 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
+ 0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
+ 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
+ 0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
+ 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
+ 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
+ 0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
+ 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555,
+};
+
+// Morton table for interleaving bytes, shifted left 1 bit
+static const word_t morton_table1[256] =
+{
+ 0x0000, 0x0002, 0x0008, 0x000A, 0x0020, 0x0022, 0x0028, 0x002A,
+ 0x0080, 0x0082, 0x0088, 0x008A, 0x00A0, 0x00A2, 0x00A8, 0x00AA,
+ 0x0200, 0x0202, 0x0208, 0x020A, 0x0220, 0x0222, 0x0228, 0x022A,
+ 0x0280, 0x0282, 0x0288, 0x028A, 0x02A0, 0x02A2, 0x02A8, 0x02AA,
+ 0x0800, 0x0802, 0x0808, 0x080A, 0x0820, 0x0822, 0x0828, 0x082A,
+ 0x0880, 0x0882, 0x0888, 0x088A, 0x08A0, 0x08A2, 0x08A8, 0x08AA,
+ 0x0A00, 0x0A02, 0x0A08, 0x0A0A, 0x0A20, 0x0A22, 0x0A28, 0x0A2A,
+ 0x0A80, 0x0A82, 0x0A88, 0x0A8A, 0x0AA0, 0x0AA2, 0x0AA8, 0x0AAA,
+ 0x2000, 0x2002, 0x2008, 0x200A, 0x2020, 0x2022, 0x2028, 0x202A,
+ 0x2080, 0x2082, 0x2088, 0x208A, 0x20A0, 0x20A2, 0x20A8, 0x20AA,
+ 0x2200, 0x2202, 0x2208, 0x220A, 0x2220, 0x2222, 0x2228, 0x222A,
+ 0x2280, 0x2282, 0x2288, 0x228A, 0x22A0, 0x22A2, 0x22A8, 0x22AA,
+ 0x2800, 0x2802, 0x2808, 0x280A, 0x2820, 0x2822, 0x2828, 0x282A,
+ 0x2880, 0x2882, 0x2888, 0x288A, 0x28A0, 0x28A2, 0x28A8, 0x28AA,
+ 0x2A00, 0x2A02, 0x2A08, 0x2A0A, 0x2A20, 0x2A22, 0x2A28, 0x2A2A,
+ 0x2A80, 0x2A82, 0x2A88, 0x2A8A, 0x2AA0, 0x2AA2, 0x2AA8, 0x2AAA,
+ 0x8000, 0x8002, 0x8008, 0x800A, 0x8020, 0x8022, 0x8028, 0x802A,
+ 0x8080, 0x8082, 0x8088, 0x808A, 0x80A0, 0x80A2, 0x80A8, 0x80AA,
+ 0x8200, 0x8202, 0x8208, 0x820A, 0x8220, 0x8222, 0x8228, 0x822A,
+ 0x8280, 0x8282, 0x8288, 0x828A, 0x82A0, 0x82A2, 0x82A8, 0x82AA,
+ 0x8800, 0x8802, 0x8808, 0x880A, 0x8820, 0x8822, 0x8828, 0x882A,
+ 0x8880, 0x8882, 0x8888, 0x888A, 0x88A0, 0x88A2, 0x88A8, 0x88AA,
+ 0x8A00, 0x8A02, 0x8A08, 0x8A0A, 0x8A20, 0x8A22, 0x8A28, 0x8A2A,
+ 0x8A80, 0x8A82, 0x8A88, 0x8A8A, 0x8AA0, 0x8AA2, 0x8AA8, 0x8AAA,
+ 0xA000, 0xA002, 0xA008, 0xA00A, 0xA020, 0xA022, 0xA028, 0xA02A,
+ 0xA080, 0xA082, 0xA088, 0xA08A, 0xA0A0, 0xA0A2, 0xA0A8, 0xA0AA,
+ 0xA200, 0xA202, 0xA208, 0xA20A, 0xA220, 0xA222, 0xA228, 0xA22A,
+ 0xA280, 0xA282, 0xA288, 0xA28A, 0xA2A0, 0xA2A2, 0xA2A8, 0xA2AA,
+ 0xA800, 0xA802, 0xA808, 0xA80A, 0xA820, 0xA822, 0xA828, 0xA82A,
+ 0xA880, 0xA882, 0xA888, 0xA88A, 0xA8A0, 0xA8A2, 0xA8A8, 0xA8AA,
+ 0xAA00, 0xAA02, 0xAA08, 0xAA0A, 0xAA20, 0xAA22, 0xAA28, 0xAA2A,
+ 0xAA80, 0xAA82, 0xAA88, 0xAA8A, 0xAAA0, 0xAAA2, 0xAAA8, 0xAAAA,
+};
+
+//
+// Macros
+//
+
+// WORD_SIZE is the number of bits per word
+// sizeof gives size in bytes (8 bits per byte)
+#define WORD_SIZE 64
+// #define WORD_SIZE (sizeof(word_t) * 8)
+
+// POPCOUNT is number of bits set
+
+#if defined(_WIN32)
+
+// See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+static word_t __inline windows_popcount(word_t w)
+{
+ w = w - ((w >> 1) & (word_t)~(word_t)0/3);
+ w = (w & (word_t)~(word_t)0/15*3) + ((w >> 2) & (word_t)~(word_t)0/15*3);
+ w = (w + (w >> 4)) & (word_t)~(word_t)0/255*15;
+ c = (word_t)(w * ((word_t)~(word_t)0/255)) >> (sizeof(word_t) - 1) * 8;
+}
+
+static word_t __inline windows_parity(word_t w)
+{
+ w ^= w >> 1;
+ w ^= w >> 2;
+ w = (w & 0x1111111111111111UL) * 0x1111111111111111UL;
+ return (w >> 60) & 1;
+}
+
+#define POPCOUNT(x) windows_popcountl(x)
+#define PARITY(x) windows_parity(x)
+#else
+#define POPCOUNT(x) (unsigned)__builtin_popcountll(x)
+#define PARITY(x) (unsigned)__builtin_parityll(x)
+#endif
+
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
+
+// Make this a power of two
+#define INIT_CAPACITY_WORDS 2
+
+// word of all 1s
+#define WORD_MAX (~(word_t)0)
+
+#define SET_REGION(arr,start,len) _set_region((arr),(start),(len),FILL_REGION)
+#define CLEAR_REGION(arr,start,len) _set_region((arr),(start),(len),ZERO_REGION)
+#define TOGGLE_REGION(arr,start,len) _set_region((arr),(start),(len),SWAP_REGION)
+
+// Have we initialised with srand() ?
+static char rand_initiated = 0;
+
+static void _seed_rand()
+{
+ if(!rand_initiated)
+ {
+ // Initialise random number generator
+ struct timeval time;
+ gettimeofday(&time, NULL);
+ srand((((time.tv_sec ^ getpid()) * 1000001) + time.tv_usec));
+ rand_initiated = 1;
+ }
+}
+
+//
+// Common internal functions
+//
+
+#define bits_in_top_word(nbits) ((nbits) ? bitset64_idx((nbits) - 1) + 1 : 0)
+
+// Mostly used for debugging
+static inline void _print_word(word_t word, FILE* out)
+{
+ word_offset_t i;
+ for(i = 0; i < WORD_SIZE; i++)
+ {
+ fprintf(out, "%c", ((word >> i) & (word_t)0x1) == 0 ? '0' : '1');
+ }
+}
+
+// prints right to left
+static inline char* _word_to_str(word_t word, char str[WORD_SIZE+1])
+ __attribute__((unused));
+
+static inline char* _word_to_str(word_t word, char str[WORD_SIZE+1])
+{
+ word_offset_t i;
+ for(i = 0; i < WORD_SIZE; i++)
+ {
+ str[WORD_SIZE-i-1] = ((word >> i) & (word_t)0x1) == 0 ? '0' : '1';
+ }
+ str[WORD_SIZE] = '\0';
+ return str;
+}
+
+// Used in debugging
+#ifdef DEBUG
+ #define DEBUG_PRINT(msg,...) printf("[%s:%i] "msg, __FILE__, __LINE__, ##__VA_ARGS__);
+ #define DEBUG_VALIDATE(a) validate_bitarr((a), __FILE__, __LINE__)
+#else
+ #define DEBUG_PRINT(msg,...)
+ #define DEBUG_VALIDATE(a)
+#endif
+
+void validate_bitarr(BIT_ARRAY *arr, const char *file, int lineno)
+{
+ // Check top word is masked
+ word_addr_t tw = arr->num_of_words == 0 ? 0 : arr->num_of_words - 1;
+ bit_index_t top_bits = bits_in_top_word(arr->num_of_bits);
+ int err = 0;
+
+ if(arr->words[tw] > bitmask64(top_bits))
+ {
+ _print_word(arr->words[tw], stderr);
+ fprintf(stderr, "\n[%s:%i] Expected %i bits in top word[%i]\n",
+ file, lineno, (int)top_bits, (int)tw);
+ err = 1;
+ }
+
+ // Check num of words is correct
+ word_addr_t num_words = roundup_bits2words64(arr->num_of_bits);
+ if(num_words != arr->num_of_words)
+ {
+ fprintf(stderr, "\n[%s:%i] num of words wrong "
+ "[bits: %i, word: %i, actual words: %i]\n", file, lineno,
+ (int)arr->num_of_bits, (int)num_words, (int)arr->num_of_words);
+ err = 1;
+ }
+
+ if(err) abort();
+}
+
+// Reverse a word
+static inline word_t _reverse_word(word_t word)
+{
+ word_t reverse = (reverse_table[(word) & 0xff] << 56) |
+ (reverse_table[(word >> 8) & 0xff] << 48) |
+ (reverse_table[(word >> 16) & 0xff] << 40) |
+ (reverse_table[(word >> 24) & 0xff] << 32) |
+ (reverse_table[(word >> 32) & 0xff] << 24) |
+ (reverse_table[(word >> 40) & 0xff] << 16) |
+ (reverse_table[(word >> 48) & 0xff] << 8) |
+ (reverse_table[(word >> 56) & 0xff]);
+
+ return reverse;
+}
+
+static inline void _mask_top_word(BIT_ARRAY* bitarr)
+{
+ // Mask top word
+ word_addr_t num_of_words = MAX(1, bitarr->num_of_words);
+ word_offset_t bits_active = bits_in_top_word(bitarr->num_of_bits);
+ bitarr->words[num_of_words-1] &= bitmask64(bits_active);
+}
+
+//
+// Get and set words (internal use only -- no bounds checking)
+//
+
+static inline word_t _get_word(const BIT_ARRAY* bitarr, bit_index_t start)
+{
+ word_addr_t word_index = bitset64_wrd(start);
+ word_offset_t word_offset = bitset64_idx(start);
+
+ word_t result = bitarr->words[word_index] >> word_offset;
+
+ word_offset_t bits_taken = WORD_SIZE - word_offset;
+
+ // word_offset is now the number of bits we need from the next word
+ // Check the next word has at least some bits
+ if(word_offset > 0 && start + bits_taken < bitarr->num_of_bits)
+ {
+ result |= bitarr->words[word_index+1] << (WORD_SIZE - word_offset);
+ }
+
+ return result;
+}
+
+// Set 64 bits from a particular start position
+// Doesn't extend bit array
+static inline void _set_word(BIT_ARRAY* bitarr, bit_index_t start, word_t word)
+{
+ word_addr_t word_index = bitset64_wrd(start);
+ word_offset_t word_offset = bitset64_idx(start);
+
+ if(word_offset == 0)
+ {
+ bitarr->words[word_index] = word;
+ }
+ else
+ {
+ bitarr->words[word_index]
+ = (word << word_offset) |
+ (bitarr->words[word_index] & bitmask64(word_offset));
+
+ if(word_index+1 < bitarr->num_of_words)
+ {
+ bitarr->words[word_index+1]
+ = (word >> (WORD_SIZE - word_offset)) |
+ (bitarr->words[word_index+1] & (WORD_MAX << word_offset));
+ }
+ }
+
+ // Mask top word
+ _mask_top_word(bitarr);
+ DEBUG_VALIDATE(bitarr);
+}
+
+static inline void _set_byte(BIT_ARRAY *bitarr, bit_index_t start, uint8_t byte)
+{
+ word_t w = _get_word(bitarr, start);
+ _set_word(bitarr, start, (w & ~(word_t)0xff) | byte);
+}
+
+// 4 bits
+static inline void _set_nibble(BIT_ARRAY *bitarr, bit_index_t start,
+ uint8_t nibble)
+{
+ word_t w = _get_word(bitarr, start);
+ _set_word(bitarr, start, (w & ~(word_t)0xf) | nibble);
+}
+
+// Wrap around
+static inline word_t _get_word_cyclic(const BIT_ARRAY* bitarr, bit_index_t start)
+{
+ word_t word = _get_word(bitarr, start);
+
+ bit_index_t bits_taken = bitarr->num_of_bits - start;
+
+ if(bits_taken < WORD_SIZE)
+ {
+ word |= (bitarr->words[0] << bits_taken);
+
+ if(bitarr->num_of_bits < (bit_index_t)WORD_SIZE)
+ {
+ // Mask word to prevent repetition of the same bits
+ word = word & bitmask64(bitarr->num_of_bits);
+ }
+ }
+
+ return word;
+}
+
+// Wrap around
+static inline void _set_word_cyclic(BIT_ARRAY* bitarr,
+ bit_index_t start, word_t word)
+{
+ _set_word(bitarr, start, word);
+
+ bit_index_t bits_set = bitarr->num_of_bits - start;
+
+ if(bits_set < WORD_SIZE && start > 0)
+ {
+ word >>= bits_set;
+
+ // Prevent overwriting the bits we've just set
+ // by setting 'start' as the upper bound for the number of bits to write
+ word_offset_t bits_remaining = MIN(WORD_SIZE - bits_set, start);
+ word_t mask = bitmask64(bits_remaining);
+
+ bitarr->words[0] = bitmask_merge(word, bitarr->words[0], mask);
+ }
+}
+
+//
+// Fill a region (internal use only)
+//
+
+// FillAction is fill with 0 or 1 or toggle
+typedef enum {ZERO_REGION, FILL_REGION, SWAP_REGION} FillAction;
+
+static inline void _set_region(BIT_ARRAY* bitarr, bit_index_t start,
+ bit_index_t length, FillAction action)
+{
+ if(length == 0) return;
+
+ word_addr_t first_word = bitset64_wrd(start);
+ word_addr_t last_word = bitset64_wrd(start+length-1);
+ word_offset_t foffset = bitset64_idx(start);
+ word_offset_t loffset = bitset64_idx(start+length-1);
+
+ if(first_word == last_word)
+ {
+ word_t mask = bitmask64(length) << foffset;
+
+ switch(action)
+ {
+ case ZERO_REGION: bitarr->words[first_word] &= ~mask; break;
+ case FILL_REGION: bitarr->words[first_word] |= mask; break;
+ case SWAP_REGION: bitarr->words[first_word] ^= mask; break;
+ }
+ }
+ else
+ {
+ // Set first word
+ switch(action)
+ {
+ case ZERO_REGION: bitarr->words[first_word] &= bitmask64(foffset); break;
+ case FILL_REGION: bitarr->words[first_word] |= ~bitmask64(foffset); break;
+ case SWAP_REGION: bitarr->words[first_word] ^= ~bitmask64(foffset); break;
+ }
+
+ word_addr_t i;
+
+ // Set whole words
+ switch(action)
+ {
+ case ZERO_REGION:
+ for(i = first_word + 1; i < last_word; i++)
+ bitarr->words[i] = (word_t)0;
+ break;
+ case FILL_REGION:
+ for(i = first_word + 1; i < last_word; i++)
+ bitarr->words[i] = WORD_MAX;
+ break;
+ case SWAP_REGION:
+ for(i = first_word + 1; i < last_word; i++)
+ bitarr->words[i] ^= WORD_MAX;
+ break;
+ }
+
+ // Set last word
+ switch(action)
+ {
+ case ZERO_REGION: bitarr->words[last_word] &= ~bitmask64(loffset+1); break;
+ case FILL_REGION: bitarr->words[last_word] |= bitmask64(loffset+1); break;
+ case SWAP_REGION: bitarr->words[last_word] ^= bitmask64(loffset+1); break;
+ }
+ }
+}
+
+
+
+//
+// Constructor
+//
+
+// If cannot allocate memory, set errno to ENOMEM, return NULL
+BIT_ARRAY* bit_array_alloc(BIT_ARRAY* bitarr, bit_index_t nbits)
+{
+ bitarr->num_of_bits = nbits;
+ bitarr->num_of_words = roundup_bits2words64(nbits);
+ bitarr->capacity_in_words = MAX(8, roundup2pow(bitarr->num_of_words));
+ bitarr->words = (word_t*)calloc(bitarr->capacity_in_words, sizeof(word_t));
+
+ if(bitarr->words == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return bitarr;
+}
+
+void bit_array_dealloc(BIT_ARRAY* bitarr)
+{
+ free(bitarr->words);
+ memset(bitarr, 0, sizeof(BIT_ARRAY));
+}
+
+// If cannot allocate memory, set errno to ENOMEM, return NULL
+BIT_ARRAY* bit_array_create(bit_index_t nbits)
+{
+ BIT_ARRAY* bitarr = (BIT_ARRAY*)malloc(sizeof(BIT_ARRAY));
+
+ // error if could not allocate enough memory
+ if(bitarr == NULL || bit_array_alloc(bitarr, nbits) == NULL)
+ {
+ if(bitarr != NULL) free(bitarr);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ DEBUG_PRINT("Creating BIT_ARRAY (bits: %lu; allocated words: %lu; "
+ "using words: %lu; WORD_SIZE: %i)\n",
+ (unsigned long)nbits, (unsigned long)bitarr->capacity_in_words,
+ (unsigned long)roundup_bits2words64(nbits), (int)WORD_SIZE);
+
+ DEBUG_VALIDATE(bitarr);
+
+ return bitarr;
+}
+
+//
+// Destructor
+//
+void bit_array_free(BIT_ARRAY* bitarr)
+{
+ if(bitarr->words != NULL)
+ free(bitarr->words);
+
+ free(bitarr);
+}
+
+bit_index_t bit_array_length(const BIT_ARRAY* bit_arr)
+{
+ return bit_arr->num_of_bits;
+}
+
+// Change the size of a bit array. Enlarging an array will add zeros
+// to the end of it. Returns 1 on success, 0 on failure (e.g. not enough memory)
+char bit_array_resize(BIT_ARRAY* bitarr, bit_index_t new_num_of_bits)
+{
+ word_addr_t old_num_of_words = bitarr->num_of_words;
+ word_addr_t new_num_of_words = roundup_bits2words64(new_num_of_bits);
+
+ bitarr->num_of_bits = new_num_of_bits;
+ bitarr->num_of_words = new_num_of_words;
+
+ DEBUG_PRINT("Resize: old_num_of_words: %i; new_num_of_words: %i capacity: %i\n",
+ (int)old_num_of_words, (int)new_num_of_words,
+ (int)bitarr->capacity_in_words);
+
+ if(new_num_of_words > bitarr->capacity_in_words)
+ {
+ // Need to change the amount of memory used
+ word_addr_t old_capacity_in_words = bitarr->capacity_in_words;
+ size_t old_capacity_in_bytes = old_capacity_in_words * sizeof(word_t);
+
+ bitarr->capacity_in_words = roundup2pow(new_num_of_words);
+ bitarr->capacity_in_words = MAX(8, bitarr->capacity_in_words);
+
+ size_t new_capacity_in_bytes = bitarr->capacity_in_words * sizeof(word_t);
+ bitarr->words = (word_t*)realloc(bitarr->words, new_capacity_in_bytes);
+
+ if(bitarr->words == NULL)
+ {
+ // error - could not allocate enough memory
+ perror("resize realloc");
+ errno = ENOMEM;
+ return 0;
+ }
+
+ // Need to zero new memory
+ size_t num_bytes_to_zero = new_capacity_in_bytes - old_capacity_in_bytes;
+ memset(bitarr->words + old_capacity_in_words, 0, num_bytes_to_zero);
+
+ DEBUG_PRINT("zeroing from word %i for %i bytes\n", (int)old_capacity_in_words,
+ (int)num_bytes_to_zero);
+ }
+ else if(new_num_of_words < old_num_of_words)
+ {
+ // Shrunk -- need to zero old memory
+ size_t num_bytes_to_zero = (old_num_of_words - new_num_of_words)*sizeof(word_t);
+
+ memset(bitarr->words + new_num_of_words, 0, num_bytes_to_zero);
+ }
+
+ // Mask top word
+ _mask_top_word(bitarr);
+ DEBUG_VALIDATE(bitarr);
+ return 1;
+}
+
+void bit_array_resize_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits)
+{
+ bit_index_t old_num_of_bits = bitarr->num_of_bits;
+
+ if(!bit_array_resize(bitarr, num_of_bits))
+ {
+ fprintf(stderr, "Ran out of memory resizing [%lu -> %lu]",
+ (unsigned long)old_num_of_bits, (unsigned long)num_of_bits);
+ abort();
+ }
+}
+
+// If bitarr length < num_bits, resizes to num_bits
+char bit_array_ensure_size(BIT_ARRAY* bitarr, bit_index_t ensure_num_of_bits)
+{
+ if(bitarr->num_of_bits < ensure_num_of_bits)
+ {
+ return bit_array_resize(bitarr, ensure_num_of_bits);
+ }
+
+ return 1;
+}
+
+void bit_array_ensure_size_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits)
+{
+ if(num_of_bits > bitarr->num_of_bits)
+ {
+ bit_array_resize_critical(bitarr, num_of_bits);
+ }
+}
+
+static inline
+void _bit_array_ensure_nwords(BIT_ARRAY* bitarr, word_addr_t nwords,
+ const char *file, int lineno, const char *func)
+{
+ size_t newmem, oldmem;
+ if(bitarr->capacity_in_words < nwords) {
+ oldmem = bitarr->capacity_in_words * sizeof(word_t);
+ bitarr->capacity_in_words = roundup2pow(nwords);
+ newmem = bitarr->capacity_in_words * sizeof(word_t);
+ bitarr->words = (word_t*)realloc(bitarr->words, newmem);
+
+ if(bitarr->words == NULL) {
+ fprintf(stderr, "[%s:%i:%s()] Ran out of memory resizing [%zu -> %zu]",
+ file, lineno, func, oldmem, newmem);
+ abort();
+ }
+
+ DEBUG_PRINT("Ensure nwords realloc %zu -> %zu\n", oldmem, newmem);
+ }
+}
+
+
+//
+// Get, set, clear, assign and toggle individual bits
+//
+
+// Get the value of a bit (returns 0 or 1)
+char bit_array_get_bit(const BIT_ARRAY* bitarr, bit_index_t b)
+{
+ assert(b < bitarr->num_of_bits);
+ return bit_array_get(bitarr, b);
+}
+
+// set a bit (to 1) at position b
+void bit_array_set_bit(BIT_ARRAY* bitarr, bit_index_t b)
+{
+ assert(b < bitarr->num_of_bits);
+ bit_array_set(bitarr,b);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// clear a bit (to 0) at position b
+void bit_array_clear_bit(BIT_ARRAY* bitarr, bit_index_t b)
+{
+ assert(b < bitarr->num_of_bits);
+ bit_array_clear(bitarr, b);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// If bit is 0 -> 1, if bit is 1 -> 0. AKA 'flip'
+void bit_array_toggle_bit(BIT_ARRAY* bitarr, bit_index_t b)
+{
+ assert(b < bitarr->num_of_bits);
+ bit_array_toggle(bitarr, b);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// If char c != 0, set bit; otherwise clear bit
+void bit_array_assign_bit(BIT_ARRAY* bitarr, bit_index_t b, char c)
+{
+ assert(b < bitarr->num_of_bits);
+ bit_array_assign(bitarr, b, c ? 1 : 0);
+ DEBUG_VALIDATE(bitarr);
+}
+
+//
+// Get, set etc with resize
+//
+
+// Get the value of a bit (returns 0 or 1)
+char bit_array_rget(BIT_ARRAY* bitarr, bit_index_t b)
+{
+ bit_array_ensure_size_critical(bitarr, b+1);
+ return bit_array_get(bitarr, b);
+}
+
+// set a bit (to 1) at position b
+void bit_array_rset(BIT_ARRAY* bitarr, bit_index_t b)
+{
+ bit_array_ensure_size_critical(bitarr, b+1);
+ bit_array_set(bitarr,b);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// clear a bit (to 0) at position b
+void bit_array_rclear(BIT_ARRAY* bitarr, bit_index_t b)
+{
+ bit_array_ensure_size_critical(bitarr, b+1);
+ bit_array_clear(bitarr, b);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// If bit is 0 -> 1, if bit is 1 -> 0. AKA 'flip'
+void bit_array_rtoggle(BIT_ARRAY* bitarr, bit_index_t b)
+{
+ bit_array_ensure_size_critical(bitarr, b+1);
+ bit_array_toggle(bitarr, b);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// If char c != 0, set bit; otherwise clear bit
+void bit_array_rassign(BIT_ARRAY* bitarr, bit_index_t b, char c)
+{
+ bit_array_ensure_size_critical(bitarr, b+1);
+ bit_array_assign(bitarr, b, c ? 1 : 0);
+ DEBUG_VALIDATE(bitarr);
+}
+
+//
+// Set, clear and toggle several bits at once
+//
+
+// Set multiple bits at once.
+// e.g. set bits 1, 20 & 31: bit_array_set_bits(bitarr, 3, 1,20,31);
+void bit_array_set_bits(BIT_ARRAY* bitarr, size_t n, ...)
+{
+ size_t i;
+ va_list argptr;
+ va_start(argptr, n);
+
+ for(i = 0; i < n; i++)
+ {
+ unsigned int bit_index = va_arg(argptr, unsigned int);
+ bit_array_set_bit(bitarr, bit_index);
+ }
+
+ va_end(argptr);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// Clear multiple bits at once.
+// e.g. clear bits 1, 20 & 31: bit_array_clear_bits(bitarr, 3, 1,20,31);
+void bit_array_clear_bits(BIT_ARRAY* bitarr, size_t n, ...)
+{
+ size_t i;
+ va_list argptr;
+ va_start(argptr, n);
+
+ for(i = 0; i < n; i++)
+ {
+ unsigned int bit_index = va_arg(argptr, unsigned int);
+ bit_array_clear_bit(bitarr, bit_index);
+ }
+
+ va_end(argptr);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// Toggle multiple bits at once
+// e.g. toggle bits 1, 20 & 31: bit_array_toggle_bits(bitarr, 3, 1,20,31);
+void bit_array_toggle_bits(BIT_ARRAY* bitarr, size_t n, ...)
+{
+ size_t i;
+ va_list argptr;
+ va_start(argptr, n);
+
+ for(i = 0; i < n; i++)
+ {
+ unsigned int bit_index = va_arg(argptr, unsigned int);
+ bit_array_toggle_bit(bitarr, bit_index);
+ }
+
+ va_end(argptr);
+ DEBUG_VALIDATE(bitarr);
+}
+
+
+//
+// Set, clear and toggle all bits in a region
+//
+
+// Set all the bits in a region
+void bit_array_set_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len)
+{
+ assert(start + len <= bitarr->num_of_bits);
+ SET_REGION(bitarr, start, len);
+ DEBUG_VALIDATE(bitarr);
+}
+
+
+// Clear all the bits in a region
+void bit_array_clear_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len)
+{
+ assert(start + len <= bitarr->num_of_bits);
+ CLEAR_REGION(bitarr, start, len);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// Toggle all the bits in a region
+void bit_array_toggle_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len)
+{
+ assert(start + len <= bitarr->num_of_bits);
+ TOGGLE_REGION(bitarr, start, len);
+ DEBUG_VALIDATE(bitarr);
+}
+
+
+//
+// Set, clear and toggle all bits at once
+//
+
+// set all elements of data to one
+void bit_array_set_all(BIT_ARRAY* bitarr)
+{
+ bit_index_t num_of_bytes = bitarr->num_of_words * sizeof(word_t);
+ memset(bitarr->words, 0xFF, num_of_bytes);
+ _mask_top_word(bitarr);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// set all elements of data to zero
+void bit_array_clear_all(BIT_ARRAY* bitarr)
+{
+ memset(bitarr->words, 0, bitarr->num_of_words * sizeof(word_t));
+ DEBUG_VALIDATE(bitarr);
+}
+
+// Set all 1 bits to 0, and all 0 bits to 1. AKA flip
+void bit_array_toggle_all(BIT_ARRAY* bitarr)
+{
+ word_addr_t i;
+ for(i = 0; i < bitarr->num_of_words; i++)
+ {
+ bitarr->words[i] ^= WORD_MAX;
+ }
+
+ _mask_top_word(bitarr);
+ DEBUG_VALIDATE(bitarr);
+}
+
+//
+// Get a word at a time
+//
+
+uint64_t bit_array_get_word64(const BIT_ARRAY* bitarr, bit_index_t start)
+{
+ assert(start < bitarr->num_of_bits);
+ return (uint64_t)_get_word(bitarr, start);
+}
+
+uint32_t bit_array_get_word32(const BIT_ARRAY* bitarr, bit_index_t start)
+{
+ assert(start < bitarr->num_of_bits);
+ return (uint32_t)_get_word(bitarr, start);
+}
+
+uint16_t bit_array_get_word16(const BIT_ARRAY* bitarr, bit_index_t start)
+{
+ assert(start < bitarr->num_of_bits);
+ return (uint16_t)_get_word(bitarr, start);
+}
+
+uint8_t bit_array_get_word8(const BIT_ARRAY* bitarr, bit_index_t start)
+{
+ assert(start < bitarr->num_of_bits);
+ return (uint8_t)_get_word(bitarr, start);
+}
+
+uint64_t bit_array_get_wordn(const BIT_ARRAY* bitarr, bit_index_t start, int n)
+{
+ assert(start < bitarr->num_of_bits);
+ assert(n <= 64);
+ return (uint64_t)(_get_word(bitarr, start) & bitmask64(n));
+}
+
+//
+// Set a word at a time
+//
+
+void bit_array_set_word64(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word)
+{
+ assert(start < bitarr->num_of_bits);
+ _set_word(bitarr, start, (word_t)word);
+}
+
+void bit_array_set_word32(BIT_ARRAY* bitarr, bit_index_t start, uint32_t word)
+{
+ assert(start < bitarr->num_of_bits);
+ word_t w = _get_word(bitarr, start);
+ _set_word(bitarr, start, (w & ~(word_t)0xffffffff) | word);
+}
+
+void bit_array_set_word16(BIT_ARRAY* bitarr, bit_index_t start, uint16_t word)
+{
+ assert(start < bitarr->num_of_bits);
+ word_t w = _get_word(bitarr, start);
+ _set_word(bitarr, start, (w & ~(word_t)0xffff) | word);
+}
+
+void bit_array_set_word8(BIT_ARRAY* bitarr, bit_index_t start, uint8_t byte)
+{
+ assert(start < bitarr->num_of_bits);
+ _set_byte(bitarr, start, byte);
+}
+
+void bit_array_set_wordn(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word, int n)
+{
+ assert(start < bitarr->num_of_bits);
+ assert(n <= 64);
+ word_t w = _get_word(bitarr, start), m = bitmask64(n);
+ _set_word(bitarr, start, bitmask_merge(word,w,m));
+}
+
+//
+// Number of bits set
+//
+
+// Get the number of bits set (hamming weight)
+bit_index_t bit_array_num_bits_set(const BIT_ARRAY* bitarr)
+{
+ word_addr_t i;
+
+ bit_index_t num_of_bits_set = 0;
+
+ for(i = 0; i < bitarr->num_of_words; i++)
+ {
+ if(bitarr->words[i] > 0)
+ {
+ num_of_bits_set += POPCOUNT(bitarr->words[i]);
+ }
+ }
+
+ return num_of_bits_set;
+}
+
+// Get the number of bits not set (1 - hamming weight)
+bit_index_t bit_array_num_bits_cleared(const BIT_ARRAY* bitarr)
+{
+ return bitarr->num_of_bits - bit_array_num_bits_set(bitarr);
+}
+
+
+// Get the number of bits set in on array and not the other. This is equivalent
+// to hamming weight of the XOR when the two arrays are the same length.
+// e.g. 10101 vs 00111 => hamming distance 2 (XOR is 10010)
+bit_index_t bit_array_hamming_distance(const BIT_ARRAY* arr1,
+ const BIT_ARRAY* arr2)
+{
+ word_addr_t min_words = MIN(arr1->num_of_words, arr2->num_of_words);
+ word_addr_t max_words = MAX(arr1->num_of_words, arr2->num_of_words);
+
+ bit_index_t hamming_distance = 0;
+ word_addr_t i;
+
+ for(i = 0; i < min_words; i++)
+ {
+ hamming_distance += POPCOUNT(arr1->words[i] ^ arr2->words[i]);
+ }
+
+ if(min_words != max_words)
+ {
+ const BIT_ARRAY* long_arr
+ = (arr1->num_of_words > arr2->num_of_words ? arr1 : arr2);
+
+ for(i = min_words; i < max_words; i++)
+ {
+ hamming_distance += POPCOUNT(long_arr->words[i]);
+ }
+ }
+
+ return hamming_distance;
+}
+
+// Parity - returns 1 if odd number of bits set, 0 if even
+char bit_array_parity(const BIT_ARRAY* bitarr)
+{
+ word_addr_t w;
+ unsigned int parity = 0;
+
+ for(w = 0; w < bitarr->num_of_words; w++)
+ {
+ parity ^= PARITY(bitarr->words[w]);
+ }
+
+ return (char)parity;
+}
+
+//
+// Find indices of set/clear bits
+//
+
+// Find the index of the next bit that is set/clear, at or after `offset`
+// Returns 1 if such a bit is found, otherwise 0
+// Index is stored in the integer pointed to by `result`
+// If no such bit is found, value at `result` is not changed
+#define _next_bit_func_def(FUNC,GET) \
+char FUNC(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) \
+{ \
+ assert(offset < bitarr->num_of_bits); \
+ if(bitarr->num_of_bits == 0 || offset >= bitarr->num_of_bits) { return 0; } \
+ \
+ /* Find first word that is greater than zero */ \
+ word_addr_t i = bitset64_wrd(offset); \
+ word_t w = GET(bitarr->words[i]) & ~bitmask64(bitset64_idx(offset)); \
+ \
+ while(1) { \
+ if(w > 0) { \
+ bit_index_t pos = i * WORD_SIZE + trailing_zeros(w); \
+ if(pos < bitarr->num_of_bits) { *result = pos; return 1; } \
+ else { return 0; } \
+ } \
+ i++; \
+ if(i >= bitarr->num_of_words) break; \
+ w = GET(bitarr->words[i]); \
+ } \
+ \
+ return 0; \
+}
+
+// Find the index of the previous bit that is set/clear, before `offset`.
+// Returns 1 if such a bit is found, otherwise 0
+// Index is stored in the integer pointed to by `result`
+// If no such bit is found, value at `result` is not changed
+#define _prev_bit_func_def(FUNC,GET) \
+char FUNC(const BIT_ARRAY* bitarr, bit_index_t offset, bit_index_t* result) \
+{ \
+ assert(offset <= bitarr->num_of_bits); \
+ if(bitarr->num_of_bits == 0 || offset == 0) { return 0; } \
+ \
+ /* Find prev word that is greater than zero */ \
+ word_addr_t i = bitset64_wrd(offset-1); \
+ word_t w = GET(bitarr->words[i]) & bitmask64(bitset64_idx(offset-1)+1); \
+ \
+ if(w > 0) { *result = (i+1) * WORD_SIZE - leading_zeros(w) - 1; return 1; } \
+ \
+ /* i is unsigned so have to use break when i == 0 */ \
+ for(--i; i != BIT_INDEX_MAX; i--) { \
+ w = GET(bitarr->words[i]); \
+ if(w > 0) { \
+ *result = (i+1) * WORD_SIZE - leading_zeros(w) - 1; \
+ return 1; \
+ } \
+ } \
+ \
+ return 0; \
+}
+
+#define GET_WORD(x) (x)
+#define NEG_WORD(x) (~(x))
+_next_bit_func_def(bit_array_find_next_set_bit, GET_WORD);
+_next_bit_func_def(bit_array_find_next_clear_bit,NEG_WORD);
+_prev_bit_func_def(bit_array_find_prev_set_bit, GET_WORD);
+_prev_bit_func_def(bit_array_find_prev_clear_bit,NEG_WORD);
+
+// Find the index of the first bit that is set.
+// Returns 1 if a bit is set, otherwise 0
+// Index of first set bit is stored in the integer pointed to by result
+// If no bits are set, value at `result` is not changed
+char bit_array_find_first_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result)
+{
+ return bit_array_find_next_set_bit(bitarr, 0, result);
+}
+
+// same same
+char bit_array_find_first_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result)
+{
+ return bit_array_find_next_clear_bit(bitarr, 0, result);
+}
+
+// Find the index of the last bit that is set.
+// Returns 1 if a bit is set, otherwise 0
+// Index of last set bit is stored in the integer pointed to by `result`
+// If no bits are set, value at `result` is not changed
+char bit_array_find_last_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result)
+{
+ return bit_array_find_prev_set_bit(bitarr, bitarr->num_of_bits, result);
+}
+
+// same same
+char bit_array_find_last_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result)
+{
+ return bit_array_find_prev_clear_bit(bitarr, bitarr->num_of_bits, result);
+}
+
+//
+// "Sorting" bits
+//
+
+// Put all the 0s before all the 1s
+void bit_array_sort_bits(BIT_ARRAY* bitarr)
+{
+ bit_index_t num_of_bits_set = bit_array_num_bits_set(bitarr);
+ bit_index_t num_of_bits_cleared = bitarr->num_of_bits - num_of_bits_set;
+ bit_array_set_all(bitarr);
+ CLEAR_REGION(bitarr, 0, num_of_bits_cleared);
+ DEBUG_VALIDATE(bitarr);
+}
+
+// Put all the 1s before all the 0s
+void bit_array_sort_bits_rev(BIT_ARRAY* bitarr)
+{
+ bit_index_t num_of_bits_set = bit_array_num_bits_set(bitarr);
+ bit_array_clear_all(bitarr);
+ SET_REGION(bitarr, 0, num_of_bits_set);
+ DEBUG_VALIDATE(bitarr);
+}
+
+
+//
+// Strings and printing
+//
+
+// Construct a BIT_ARRAY from a substring with given on and off characters.
+void bit_array_from_substr(BIT_ARRAY* bitarr, bit_index_t offset,
+ const char *str, size_t len,
+ const char *on, const char *off,
+ char left_to_right)
+{
+ bit_array_ensure_size(bitarr, offset + len);
+ bit_array_clear_region(bitarr, offset, len);
+
+ // BitArray region is now all 0s -- just set the 1s
+ size_t i;
+ bit_index_t j;
+
+ for(i = 0; i < len; i++)
+ {
+ if(strchr(on, str[i]) != NULL)
+ {
+ j = offset + (left_to_right ? i : len - i - 1);
+ bit_array_set(bitarr, j);
+ }
+ else { assert(strchr(off, str[i]) != NULL); }
+ }
+
+ DEBUG_VALIDATE(bitarr);
+}
+
+// From string method
+void bit_array_from_str(BIT_ARRAY* bitarr, const char* str)
+{
+ bit_array_from_substr(bitarr, 0, str, strlen(str), "1", "0", 1);
+}
+
+// Takes a char array to write to. `str` must be bitarr->num_of_bits+1 in length
+// Terminates string with '\0'
+char* bit_array_to_str(const BIT_ARRAY* bitarr, char* str)
+{
+ bit_index_t i;
+
+ for(i = 0; i < bitarr->num_of_bits; i++)
+ {
+ str[i] = bit_array_get(bitarr, i) ? '1' : '0';
+ }
+
+ str[bitarr->num_of_bits] = '\0';
+
+ return str;
+}
+
+char* bit_array_to_str_rev(const BIT_ARRAY* bitarr, char* str)
+{
+ bit_index_t i;
+
+ for(i = 0; i < bitarr->num_of_bits; i++)
+ {
+ str[i] = bit_array_get(bitarr, bitarr->num_of_bits-i-1) ? '1' : '0';
+ }
+
+ str[bitarr->num_of_bits] = '\0';
+
+ return str;
+}
+
+
+// Get a string representations for a given region, using given on/off characters.
+// Note: does not null-terminate
+void bit_array_to_substr(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ char* str, char on, char off,
+ char left_to_right)
+{
+ assert(start + length <= bitarr->num_of_bits);
+
+ bit_index_t i, j;
+ bit_index_t end = start + length - 1;
+
+ for(i = 0; i < length; i++)
+ {
+ j = (left_to_right ? start + i : end - i);
+ str[i] = bit_array_get(bitarr, j) ? on : off;
+ }
+
+// str[length] = '\0';
+}
+
+// Print this array to a file stream. Prints '0's and '1'. Doesn't print newline.
+void bit_array_print(const BIT_ARRAY* bitarr, FILE* fout)
+{
+ bit_index_t i;
+
+ for(i = 0; i < bitarr->num_of_bits; i++)
+ {
+ fprintf(fout, "%c", bit_array_get(bitarr, i) ? '1' : '0');
+ }
+}
+
+// Print a string representations for a given region, using given on/off characters.
+void bit_array_print_substr(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ FILE* fout, char on, char off,
+ char left_to_right)
+{
+ assert(start + length <= bitarr->num_of_bits);
+
+ bit_index_t i, j;
+ bit_index_t end = start + length - 1;
+
+ for(i = 0; i < length; i++)
+ {
+ j = (left_to_right ? start + i : end - i);
+ fprintf(fout, "%c", bit_array_get(bitarr, j) ? on : off);
+ }
+}
+
+//
+// Decimal
+//
+
+// Get bit array as decimal str (e.g. 0b1101 -> "13")
+// len is the length of str char array -- will write at most len-1 chars
+// returns the number of characters needed
+// return is the same as strlen(str)
+size_t bit_array_to_decimal(const BIT_ARRAY *bitarr, char *str, size_t len)
+{
+ size_t i = 0;
+
+ if(bit_array_cmp_uint64(bitarr, 0) == 0)
+ {
+ if(len >= 2)
+ {
+ *str = '0';
+ *(str+1) = '\0';
+ }
+
+ return 1;
+ }
+
+ BIT_ARRAY *tmp = bit_array_clone(bitarr);
+ uint64_t rem;
+
+ str[len-1] = '\0';
+
+ while(bit_array_cmp_uint64(tmp, 0) != 0)
+ {
+ bit_array_div_uint64(tmp, 10, &rem);
+
+ if(i < len-1)
+ {
+ str[len-2-i] = '0' + rem;
+ }
+
+ i++;
+ }
+
+ if(i < len-1)
+ {
+ // Moves null-terminator as well
+ memmove(str, str+len-i-1, i+1);
+ }
+
+ bit_array_free(tmp);
+
+ return i;
+}
+
+// Get bit array from decimal str (e.g. "13" -> 0b1101)
+// Returns number of characters used
+size_t bit_array_from_decimal(BIT_ARRAY *bitarr, const char* decimal)
+{
+ bit_array_clear_all(bitarr);
+ size_t i = 0;
+
+ if(decimal[0] == '\0' || decimal[0] < '0' || decimal[0] > '9')
+ {
+ return 0;
+ }
+
+ bit_array_add_uint64(bitarr, decimal[i] - '0');
+ i++;
+
+ while(decimal[i] != '\0' && decimal[i] >= '0' && decimal[i] <= '9')
+ {
+ bit_array_mul_uint64(bitarr, 10);
+ bit_array_add_uint64(bitarr, decimal[i] - '0');
+ i++;
+ }
+
+ return i;
+}
+
+//
+// Hexidecimal
+//
+
+char bit_array_hex_to_nibble(char c, uint8_t *b)
+{
+ c = tolower(c);
+
+ if(c >= '0' && c <= '9')
+ {
+ *b = c - '0';
+ return 1;
+ }
+ else if(c >= 'a' && c <= 'f')
+ {
+ *b = 0xa + (c - 'a');
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+char bit_array_nibble_to_hex(uint8_t b, char uppercase)
+{
+ if(b <= 9)
+ {
+ return '0' + b;
+ }
+ else
+ {
+ return (uppercase ? 'A' : 'a') + (b - 0xa);
+ }
+}
+
+// Loads array from hex string
+// Returns the number of bits loaded (will be chars rounded up to multiple of 4)
+// (0 on failure)
+bit_index_t bit_array_from_hex(BIT_ARRAY* bitarr, bit_index_t offset,
+ const char* str, size_t len)
+{
+ if(str[0] == '0' && tolower(str[1]) == 'x')
+ {
+ str += 2;
+ len -= 2;
+ }
+
+ size_t i;
+ for(i = 0; i < len; i++, offset += 4)
+ {
+ uint8_t b;
+ if(bit_array_hex_to_nibble(str[i], &b))
+ {
+ bit_array_ensure_size(bitarr, offset + 4);
+ _set_nibble(bitarr, offset, b);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return 4 * i;
+}
+
+// Returns number of characters written
+size_t bit_array_to_hex(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ char* str, char uppercase)
+{
+ assert(start + length <= bitarr->num_of_bits);
+
+ size_t k = 0;
+ bit_index_t offset, end = start + length;
+
+ for(offset = start; offset + WORD_SIZE <= end; offset += WORD_SIZE)
+ {
+ word_t w = _get_word(bitarr, offset);
+
+ word_offset_t j;
+ for(j = 0; j < 64; j += 4)
+ {
+ str[k++] = bit_array_nibble_to_hex((w>>j) & 0xf, uppercase);
+ }
+ }
+
+ if(offset < end)
+ {
+ // Remaining full nibbles (4 bits)
+ word_t w = _get_word(bitarr, offset);
+
+ for(; offset + 4 <= end; offset += 4)
+ {
+ str[k++] = bit_array_nibble_to_hex(w & 0xf, uppercase);
+ w >>= 4;
+ }
+
+ if(offset < end)
+ {
+ // Remaining bits
+ str[k++] = bit_array_nibble_to_hex(w & bitmask64(end - offset), uppercase);
+ }
+ }
+
+ str[k] = '\0';
+
+ // Return number of characters written
+ return k;
+}
+
+// Print bit array as hex
+size_t bit_array_print_hex(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ FILE* fout, char uppercase)
+{
+ assert(start + length <= bitarr->num_of_bits);
+
+ size_t k = 0;
+ bit_index_t offset, end = start + length;
+
+ for(offset = start; offset + WORD_SIZE <= end; offset += WORD_SIZE)
+ {
+ word_t w = _get_word(bitarr, offset);
+
+ word_offset_t j;
+ for(j = 0; j < 64; j += 4)
+ {
+ fprintf(fout, "%c", bit_array_nibble_to_hex((w>>j) & 0xf, uppercase));
+ k++;
+ }
+ }
+
+ if(offset < end)
+ {
+ // Remaining full nibbles (4 bits)
+ word_t w = _get_word(bitarr, offset);
+
+ for(; offset + 4 <= end; offset += 4)
+ {
+ fprintf(fout, "%c", bit_array_nibble_to_hex(w & 0xf, uppercase));
+ w >>= 4;
+ k++;
+ }
+
+ if(offset < end)
+ {
+ // Remaining bits
+ char hex = bit_array_nibble_to_hex(w & bitmask64(end - offset), uppercase);
+ fprintf(fout, "%c", hex);
+ k++;
+ }
+ }
+
+ return k;
+}
+
+//
+// Clone and copy
+//
+
+// Returns NULL if cannot malloc
+BIT_ARRAY* bit_array_clone(const BIT_ARRAY* bitarr)
+{
+ BIT_ARRAY* cpy = bit_array_create(bitarr->num_of_bits);
+
+ if(cpy == NULL)
+ {
+ return NULL;
+ }
+
+ // Copy across bits
+ memcpy(cpy->words, bitarr->words, bitarr->num_of_words * sizeof(word_t));
+
+ DEBUG_VALIDATE(cpy);
+ return cpy;
+}
+
+// destination and source may be the same bit_array
+// and src/dst regions may overlap
+static void _array_copy(BIT_ARRAY* dst, bit_index_t dstindx,
+ const BIT_ARRAY* src, bit_index_t srcindx,
+ bit_index_t length)
+{
+ DEBUG_PRINT("bit_array_copy(dst: %zu, src: %zu, length: %zu)\n",
+ (size_t)dstindx, (size_t)srcindx, (size_t)length);
+
+ // Num of full words to copy
+ word_addr_t num_of_full_words = length / WORD_SIZE;
+ word_addr_t i;
+
+ word_offset_t bits_in_last_word = bits_in_top_word(length);
+
+ if(dst == src && srcindx > dstindx)
+ {
+ // Work left to right
+ DEBUG_PRINT("work left to right\n");
+
+ for(i = 0; i < num_of_full_words; i++)
+ {
+ word_t word = _get_word(src, srcindx+i*WORD_SIZE);
+ _set_word(dst, dstindx+i*WORD_SIZE, word);
+ }
+
+ if(bits_in_last_word > 0)
+ {
+ word_t src_word = _get_word(src, srcindx+i*WORD_SIZE);
+ word_t dst_word = _get_word(dst, dstindx+i*WORD_SIZE);
+
+ word_t mask = bitmask64(bits_in_last_word);
+ word_t word = bitmask_merge(src_word, dst_word, mask);
+
+ _set_word(dst, dstindx+num_of_full_words*WORD_SIZE, word);
+ }
+ }
+ else
+ {
+ // Work right to left
+ DEBUG_PRINT("work right to left\n");
+
+ for(i = 0; i < num_of_full_words; i++)
+ {
+ word_t word = _get_word(src, srcindx+length-(i+1)*WORD_SIZE);
+ _set_word(dst, dstindx+length-(i+1)*WORD_SIZE, word);
+ }
+
+ DEBUG_PRINT("Copy %i,%i to %i\n", (int)srcindx, (int)bits_in_last_word,
+ (int)dstindx);
+
+ if(bits_in_last_word > 0)
+ {
+ word_t src_word = _get_word(src, srcindx);
+ word_t dst_word = _get_word(dst, dstindx);
+
+ word_t mask = bitmask64(bits_in_last_word);
+ word_t word = bitmask_merge(src_word, dst_word, mask);
+ _set_word(dst, dstindx, word);
+ }
+ }
+
+ _mask_top_word(dst);
+}
+
+// destination and source may be the same bit_array
+// and src/dst regions may overlap
+void bit_array_copy(BIT_ARRAY* dst, bit_index_t dstindx,
+ const BIT_ARRAY* src, bit_index_t srcindx,
+ bit_index_t length)
+{
+ assert(srcindx + length <= src->num_of_bits);
+ assert(dstindx <= dst->num_of_bits);
+ _array_copy(dst, dstindx, src, srcindx, length);
+ DEBUG_VALIDATE(dst);
+}
+
+// Clone `src` into `dst`. Resizes `dst`.
+void bit_array_copy_all(BIT_ARRAY* dst, const BIT_ARRAY* src)
+{
+ bit_array_resize_critical(dst, src->num_of_bits);
+ memmove(dst->words, src->words, src->num_of_words * sizeof(word_t));
+ DEBUG_VALIDATE(dst);
+}
+
+
+//
+// Logic operators
+//
+
+// Destination can be the same as one or both of the sources
+void bit_array_and(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2)
+{
+ // Ensure dst array is big enough
+ word_addr_t max_bits = MAX(src1->num_of_bits, src2->num_of_bits);
+ bit_array_ensure_size_critical(dst, max_bits);
+
+ word_addr_t min_words = MIN(src1->num_of_words, src2->num_of_words);
+
+ word_addr_t i;
+
+ for(i = 0; i < min_words; i++)
+ {
+ dst->words[i] = src1->words[i] & src2->words[i];
+ }
+
+ // Set remaining bits to zero
+ for(i = min_words; i < dst->num_of_words; i++)
+ {
+ dst->words[i] = (word_t)0;
+ }
+
+ DEBUG_VALIDATE(dst);
+}
+
+// Destination can be the same as one or both of the sources
+static void _logical_or_xor(BIT_ARRAY* dst,
+ const BIT_ARRAY* src1,
+ const BIT_ARRAY* src2,
+ char use_xor)
+{
+ // Ensure dst array is big enough
+ bit_array_ensure_size_critical(dst, MAX(src1->num_of_bits, src2->num_of_bits));
+
+ word_addr_t min_words = MIN(src1->num_of_words, src2->num_of_words);
+ word_addr_t max_words = MAX(src1->num_of_words, src2->num_of_words);
+
+ word_addr_t i;
+
+ if(use_xor)
+ {
+ for(i = 0; i < min_words; i++)
+ dst->words[i] = src1->words[i] ^ src2->words[i];
+ }
+ else
+ {
+ for(i = 0; i < min_words; i++)
+ dst->words[i] = src1->words[i] | src2->words[i];
+ }
+
+ // Copy remaining bits from longer src array
+ if(min_words != max_words)
+ {
+ const BIT_ARRAY* longer = src1->num_of_words > src2->num_of_words ? src1 : src2;
+
+ for(i = min_words; i < max_words; i++)
+ {
+ dst->words[i] = longer->words[i];
+ }
+ }
+
+ // Set remaining bits to zero
+ size_t size = (dst->num_of_words - max_words) * sizeof(word_t);
+ memset(dst->words + max_words, 0, size);
+
+ DEBUG_VALIDATE(dst);
+}
+
+void bit_array_or(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2)
+{
+ _logical_or_xor(dst, src1, src2, 0);
+}
+
+// Destination can be the same as one or both of the sources
+void bit_array_xor(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2)
+{
+ _logical_or_xor(dst, src1, src2, 1);
+}
+
+// If dst is longer than src, top bits are set to 1
+void bit_array_not(BIT_ARRAY* dst, const BIT_ARRAY* src)
+{
+ bit_array_ensure_size_critical(dst, src->num_of_bits);
+
+ word_addr_t i;
+
+ for(i = 0; i < src->num_of_words; i++)
+ {
+ dst->words[i] = ~(src->words[i]);
+ }
+
+ // Set remaining words to 1s
+ for(i = src->num_of_words; i < dst->num_of_words; i++)
+ {
+ dst->words[i] = WORD_MAX;
+ }
+
+ _mask_top_word(dst);
+
+ DEBUG_VALIDATE(dst);
+}
+
+//
+// Comparisons
+//
+
+// Compare two bit arrays by value stored, with index 0 being the Least
+// Significant Bit (LSB). Arrays do not have to be the same length.
+// Example: ..0101 (5) > ...0011 (3) [index 0 is LSB at right hand side]
+// Sorts on length if all zeros: (0,0) < (0,0,0)
+// returns:
+// >0 iff bitarr1 > bitarr2
+// 0 iff bitarr1 == bitarr2
+// <0 iff bitarr1 < bitarr2
+int bit_array_cmp(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2)
+{
+ word_addr_t i;
+ word_t word1, word2;
+ word_addr_t min_words = bitarr1->num_of_words;
+
+ // i is unsigned so break when i == 0
+ if(bitarr1->num_of_words > bitarr2->num_of_words) {
+ min_words = bitarr2->num_of_words;
+ for(i = bitarr1->num_of_words-1; ; i--) {
+ if(bitarr1->words[i]) return 1;
+ if(i == bitarr2->num_of_words) break;
+ }
+ }
+ else if(bitarr1->num_of_words < bitarr2->num_of_words) {
+ for(i = bitarr2->num_of_words-1; ; i--) {
+ if(bitarr2->words[i]) return 1;
+ if(i == bitarr1->num_of_words) break;
+ }
+ }
+
+ if(min_words == 0) return 0;
+
+ for(i = min_words-1; ; i--)
+ {
+ word1 = bitarr1->words[i];
+ word2 = bitarr2->words[i];
+ if(word1 != word2) return (word1 > word2 ? 1 : -1);
+ if(i == 0) break;
+ }
+
+ if(bitarr1->num_of_bits == bitarr2->num_of_bits) return 0;
+ return bitarr1->num_of_bits > bitarr2->num_of_bits ? 1 : -1;
+}
+
+// Compare two bit arrays by value stored, with index 0 being the Most
+// Significant Bit (MSB). Arrays do not have to be the same length.
+// Example: 10.. > 01.. [index 0 is MSB at left hand side]
+// Sorts on length if all zeros: (0,0) < (0,0,0)
+// returns:
+// >0 iff bitarr1 > bitarr2
+// 0 iff bitarr1 == bitarr2
+// <0 iff bitarr1 < bitarr2
+int bit_array_cmp_big_endian(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2)
+{
+ word_addr_t min_words = MAX(bitarr1->num_of_words, bitarr2->num_of_words);
+
+ word_addr_t i;
+ word_t word1, word2;
+
+ for(i = 0; i < min_words; i++) {
+ word1 = _reverse_word(bitarr1->words[i]);
+ word2 = _reverse_word(bitarr2->words[i]);
+ if(word1 != word2) return (word1 > word2 ? 1 : -1);
+ }
+
+ // Check remaining words. Only one of these loops will execute
+ for(; i < bitarr1->num_of_words; i++)
+ if(bitarr1->words[i]) return 1;
+ for(; i < bitarr2->num_of_words; i++)
+ if(bitarr2->words[i]) return -1;
+
+ if(bitarr1->num_of_bits == bitarr2->num_of_bits) return 0;
+ return bitarr1->num_of_bits > bitarr2->num_of_bits ? 1 : -1;
+}
+
+// compare bitarr with (bitarr2 << pos)
+// bit_array_cmp(bitarr1, bitarr2<<pos)
+// returns:
+// >0 iff bitarr1 > bitarr2
+// 0 iff bitarr1 == bitarr2
+// <0 iff bitarr1 < bitarr2
+int bit_array_cmp_words(const BIT_ARRAY *arr1,
+ bit_index_t pos, const BIT_ARRAY *arr2)
+{
+ if(arr1->num_of_bits == 0 && arr2->num_of_bits == 0)
+ {
+ return 0;
+ }
+
+ bit_index_t top_bit1 = 0, top_bit2 = 0;
+
+ char arr1_zero = !bit_array_find_last_set_bit(arr1, &top_bit1);
+ char arr2_zero = !bit_array_find_last_set_bit(arr2, &top_bit2);
+
+ if(arr1_zero && arr2_zero) return 0;
+ if(arr1_zero) return -1;
+ if(arr2_zero) return 1;
+
+ bit_index_t top_bit2_offset = top_bit2 + pos;
+
+ if(top_bit1 != top_bit2_offset) {
+ return top_bit1 > top_bit2_offset ? 1 : -1;
+ }
+
+ word_addr_t i;
+ word_t word1, word2;
+
+ for(i = top_bit2 / WORD_SIZE; i > 0; i--)
+ {
+ word1 = _get_word(arr1, pos + i * WORD_SIZE);
+ word2 = arr2->words[i];
+
+ if(word1 > word2) return 1;
+ if(word1 < word2) return -1;
+ }
+
+ word1 = _get_word(arr1, pos);
+ word2 = arr2->words[0];
+
+ if(word1 > word2) return 1;
+ if(word1 < word2) return -1;
+
+ // return 1 if arr1[0..pos] != 0, 0 otherwise
+
+ // Whole words
+ word_addr_t num_words = pos / WORD_SIZE;
+
+ for(i = 0; i < num_words; i++)
+ {
+ if(arr1->words[i] > 0)
+ {
+ return 1;
+ }
+ }
+
+ word_offset_t bits_remaining = pos - num_words * WORD_SIZE;
+
+ if(arr1->words[num_words] & bitmask64(bits_remaining))
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+//
+// Reverse -- coords may wrap around
+//
+
+// No bounds checking
+// length cannot be zero
+static void _reverse_region(BIT_ARRAY* bitarr,
+ bit_index_t start,
+ bit_index_t length)
+{
+ bit_index_t left = start;
+ bit_index_t right = (start + length - WORD_SIZE) % bitarr->num_of_bits;
+
+ while(length >= 2 * WORD_SIZE)
+ {
+ // Swap entire words
+ word_t left_word = _get_word_cyclic(bitarr, left);
+ word_t right_word = _get_word_cyclic(bitarr, right);
+
+ // reverse words individually
+ left_word = _reverse_word(left_word);
+ right_word = _reverse_word(right_word);
+
+ // Swap
+ _set_word_cyclic(bitarr, left, right_word);
+ _set_word_cyclic(bitarr, right, left_word);
+
+ // Update
+ left = (left + WORD_SIZE) % bitarr->num_of_bits;
+ right = (right < WORD_SIZE ? right + bitarr->num_of_bits : right) - WORD_SIZE;
+ length -= 2 * WORD_SIZE;
+ }
+
+ word_t word, rev;
+
+ if(length == 0)
+ {
+ return;
+ }
+ else if(length > WORD_SIZE)
+ {
+ // Words overlap
+ word_t left_word = _get_word_cyclic(bitarr, left);
+ word_t right_word = _get_word_cyclic(bitarr, right);
+
+ rev = _reverse_word(left_word);
+ right_word = _reverse_word(right_word);
+
+ // fill left 64 bits with right word rev
+ _set_word_cyclic(bitarr, left, right_word);
+
+ // Now do remaining bits (length is between 1 and 64 bits)
+ left += WORD_SIZE;
+ length -= WORD_SIZE;
+
+ word = _get_word_cyclic(bitarr, left);
+ }
+ else
+ {
+ word = _get_word_cyclic(bitarr, left);
+ rev = _reverse_word(word);
+ }
+
+ rev >>= WORD_SIZE - length;
+ word_t mask = bitmask64(length);
+
+ word = bitmask_merge(rev, word, mask);
+
+ _set_word_cyclic(bitarr, left, word);
+}
+
+void bit_array_reverse_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len)
+{
+ assert(start + len <= bitarr->num_of_bits);
+ if(len > 0) _reverse_region(bitarr, start, len);
+ DEBUG_VALIDATE(bitarr);
+}
+
+void bit_array_reverse(BIT_ARRAY* bitarr)
+{
+ if(bitarr->num_of_bits > 0) _reverse_region(bitarr, 0, bitarr->num_of_bits);
+ DEBUG_VALIDATE(bitarr);
+}
+
+//
+// Shift left / right
+//
+
+// Shift towards MSB / higher index
+void bit_array_shift_left(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill)
+{
+ if(shift_dist >= bitarr->num_of_bits)
+ {
+ fill ? bit_array_set_all(bitarr) : bit_array_clear_all(bitarr);
+ return;
+ }
+ else if(shift_dist == 0)
+ {
+ return;
+ }
+
+ FillAction action = fill ? FILL_REGION : ZERO_REGION;
+
+ bit_index_t cpy_length = bitarr->num_of_bits - shift_dist;
+ _array_copy(bitarr, shift_dist, bitarr, 0, cpy_length);
+ _set_region(bitarr, 0, shift_dist, action);
+}
+
+// shift left extend - don't truncate bits when shifting UP, instead
+// make room for them.
+void bit_array_shift_left_extend(BIT_ARRAY* bitarr, bit_index_t shift_dist,
+ char fill)
+{
+ bit_index_t newlen = bitarr->num_of_bits + shift_dist;
+ bit_index_t cpy_length = bitarr->num_of_bits;
+
+ if(shift_dist == 0)
+ {
+ return;
+ }
+
+ bit_array_resize_critical(bitarr, newlen);
+
+ FillAction action = fill ? FILL_REGION : ZERO_REGION;
+ _array_copy(bitarr, shift_dist, bitarr, 0, cpy_length);
+ _set_region(bitarr, 0, shift_dist, action);
+}
+
+// Shift towards LSB / lower index
+void bit_array_shift_right(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill)
+{
+ if(shift_dist >= bitarr->num_of_bits)
+ {
+ fill ? bit_array_set_all(bitarr) : bit_array_clear_all(bitarr);
+ return;
+ }
+ else if(shift_dist == 0)
+ {
+ return;
+ }
+
+ FillAction action = fill ? FILL_REGION : ZERO_REGION;
+
+ bit_index_t cpy_length = bitarr->num_of_bits - shift_dist;
+ bit_array_copy(bitarr, 0, bitarr, shift_dist, cpy_length);
+
+ _set_region(bitarr, cpy_length, shift_dist, action);
+}
+
+//
+// Cycle
+//
+
+// Cycle towards index 0
+void bit_array_cycle_right(BIT_ARRAY* bitarr, bit_index_t cycle_dist)
+{
+ if(bitarr->num_of_bits == 0)
+ {
+ return;
+ }
+
+ cycle_dist = cycle_dist % bitarr->num_of_bits;
+
+ if(cycle_dist == 0)
+ {
+ return;
+ }
+
+ bit_index_t len1 = cycle_dist;
+ bit_index_t len2 = bitarr->num_of_bits - cycle_dist;
+
+ _reverse_region(bitarr, 0, len1);
+ _reverse_region(bitarr, len1, len2);
+ bit_array_reverse(bitarr);
+}
+
+// Cycle away from index 0
+void bit_array_cycle_left(BIT_ARRAY* bitarr, bit_index_t cycle_dist)
+{
+ if(bitarr->num_of_bits == 0)
+ {
+ return;
+ }
+
+ cycle_dist = cycle_dist % bitarr->num_of_bits;
+
+ if(cycle_dist == 0)
+ {
+ return;
+ }
+
+ bit_index_t len1 = bitarr->num_of_bits - cycle_dist;
+ bit_index_t len2 = cycle_dist;
+
+ _reverse_region(bitarr, 0, len1);
+ _reverse_region(bitarr, len1, len2);
+ bit_array_reverse(bitarr);
+}
+
+//
+// Next permutation
+//
+
+static word_t _next_permutation(word_t v)
+{
+ // From http://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
+ word_t t = v | (v - 1); // t gets v's least significant 0 bits set to 1
+ // Next set to 1 the most significant bit to change,
+ // set to 0 the least significant ones, and add the necessary 1 bits.
+ return (t+1) | (((~t & (t+1)) - 1) >> (trailing_zeros(v) + 1));
+}
+
+// Get the next permutation of an array with a fixed size and given number of
+// bits set. Also known as next lexicographic permutation.
+// Given a bit array find the next lexicographic orginisation of the bits
+// Number of possible combinations given by (size choose bits_set) i.e. nCk
+// 00011 -> 00101 -> 00110 -> 01001 -> 01010 ->
+// 01100 -> 10001 -> 10010 -> 10100 -> 11000 -> 00011 (back to start)
+void bit_array_next_permutation(BIT_ARRAY* bitarr)
+{
+ if(bitarr->num_of_bits == 0)
+ {
+ return;
+ }
+
+ word_addr_t w;
+
+ char carry = 0;
+ word_offset_t top_bits = bitset64_idx(bitarr->num_of_bits);
+
+ for(w = 0; w < bitarr->num_of_words; w++)
+ {
+ word_t mask
+ = (w < bitarr->num_of_words - 1 || top_bits == 0) ? WORD_MAX
+ : bitmask64(top_bits);
+
+ if(bitarr->words[w] > 0 &&
+ (bitarr->words[w] | (bitarr->words[w]-1)) == mask)
+ {
+ // Bits in this word cannot be moved forward
+ carry = 1;
+ }
+ else if(carry)
+ {
+ // 0111 -> 1000, 1000 -> 1001
+ word_t tmp = bitarr->words[w] + 1;
+
+ // Count bits previously set
+ bit_index_t bits_previously_set = POPCOUNT(bitarr->words[w]);
+
+ // set new word
+ bitarr->words[w] = tmp;
+
+ // note: w is unsigned
+ // Zero words while counting bits set
+ while(w > 0)
+ {
+ bits_previously_set += POPCOUNT(bitarr->words[w-1]);
+ bitarr->words[w-1] = 0;
+ w--;
+ }
+
+ // Set bits at the beginning
+ SET_REGION(bitarr, 0, bits_previously_set - POPCOUNT(tmp));
+
+ carry = 0;
+ break;
+ }
+ else if(bitarr->words[w] > 0)
+ {
+ bitarr->words[w] = _next_permutation(bitarr->words[w]);
+ break;
+ }
+ }
+
+ if(carry)
+ {
+ // Loop around
+ bit_index_t num_bits_set = bit_array_num_bits_set(bitarr);
+ bit_array_clear_all(bitarr);
+ SET_REGION(bitarr, 0, num_bits_set);
+ }
+
+ DEBUG_VALIDATE(bitarr);
+}
+
+
+//
+// Interleave
+//
+
+// dst cannot point to the same bit array as src1 or src2
+// src1, src2 may point to the same bit array
+// abcd 1234 -> a1b2c3d4
+// 0011 0000 -> 00001010
+// 1111 0000 -> 10101010
+// 0101 1010 -> 01100110
+void bit_array_interleave(BIT_ARRAY* dst,
+ const BIT_ARRAY* src1,
+ const BIT_ARRAY* src2)
+{
+ // dst cannot be either src1 or src2
+ assert(dst != src1 && dst != src2);
+ // Behaviour undefined when src1 length != src2 length",
+ assert(src1->num_of_bits == src2->num_of_bits);
+
+ // Need at least src1->num_of_words + src2->num_of_words
+ size_t nwords = MIN(src1->num_of_words + src2->num_of_words, 2);
+ _bit_array_ensure_nwords(dst, nwords, __FILE__, __LINE__, __func__);
+ dst->num_of_bits = src1->num_of_bits + src2->num_of_bits;
+ dst->num_of_words = roundup_bits2words64(dst->num_of_bits);
+
+ word_addr_t i, j;
+
+ for(i = 0, j = 0; i < src1->num_of_words; i++)
+ {
+ word_t a = src1->words[i];
+ word_t b = src2->words[i];
+
+ dst->words[j++] = morton_table0[(a ) & 0xff] |
+ morton_table1[(b ) & 0xff] |
+ (morton_table0[(a >> 8) & 0xff] << 16) |
+ (morton_table1[(b >> 8) & 0xff] << 16) |
+ (morton_table0[(a >> 16) & 0xff] << 32) |
+ (morton_table1[(b >> 16) & 0xff] << 32) |
+ (morton_table0[(a >> 24) & 0xff] << 48) |
+ (morton_table1[(b >> 24) & 0xff] << 48);
+
+ dst->words[j++] = morton_table0[(a >> 32) & 0xff] |
+ morton_table1[(b >> 32) & 0xff] |
+ (morton_table0[(a >> 40) & 0xff] << 16) |
+ (morton_table1[(b >> 40) & 0xff] << 16) |
+ (morton_table0[(a >> 48) & 0xff] << 32) |
+ (morton_table1[(b >> 48) & 0xff] << 32) |
+ (morton_table0[(a >> 56) ] << 48) |
+ (morton_table1[(b >> 56) ] << 48);
+ }
+
+ DEBUG_VALIDATE(dst);
+}
+
+//
+// Random
+//
+
+// Set bits randomly with probability prob : 0 <= prob <= 1
+void bit_array_random(BIT_ARRAY* bitarr, float prob)
+{
+ assert(prob >= 0 && prob <= 1);
+
+ if(bitarr->num_of_bits == 0)
+ {
+ return;
+ }
+ else if(prob == 1)
+ {
+ bit_array_set_all(bitarr);
+ return;
+ }
+
+ // rand() generates number between 0 and RAND_MAX inclusive
+ // therefore we want to check if rand() <= p
+ long p = RAND_MAX * prob;
+
+ _seed_rand();
+
+ word_addr_t w;
+ word_offset_t o;
+
+ // Initialise to zero
+ memset(bitarr->words, 0, bitarr->num_of_words * sizeof(word_t));
+
+ for(w = 0; w < bitarr->num_of_words - 1; w++)
+ {
+ for(o = 0; o < WORD_SIZE; o++)
+ {
+ if(rand() <= p)
+ {
+ bitarr->words[w] |= ((word_t)0x1 << o);
+ }
+ }
+ }
+
+ // Top word
+ word_offset_t bits_in_last_word = bits_in_top_word(bitarr->num_of_bits);
+ w = bitarr->num_of_words - 1;
+
+ for(o = 0; o < bits_in_last_word; o++)
+ {
+ if(rand() <= p)
+ {
+ bitarr->words[w] |= ((word_t)0x1 << o);
+ }
+ }
+
+ DEBUG_VALIDATE(bitarr);
+}
+
+// Shuffle the bits in an array randomly
+void bit_array_shuffle(BIT_ARRAY* bitarr)
+{
+ if(bitarr->num_of_bits == 0)
+ return;
+
+ _seed_rand();
+
+ bit_index_t i, j;
+
+ for(i = bitarr->num_of_bits - 1; i > 0; i--)
+ {
+ j = (bit_index_t)rand() % i;
+
+ // Swap i and j
+ char x = (bitarr->words[bitset64_wrd(i)] >> bitset64_idx(i)) & 0x1;
+ char y = (bitarr->words[bitset64_wrd(j)] >> bitset64_idx(j)) & 0x1;
+
+ if(!y)
+ bitarr->words[bitset64_wrd(i)] &= ~((word_t)0x1 << bitset64_idx(i));
+ else
+ bitarr->words[bitset64_wrd(i)] |= (word_t)0x1 << bitset64_idx(i);
+
+ if(!x)
+ bitarr->words[bitset64_wrd(j)] &= ~((word_t)0x1 << bitset64_idx(j));
+ else
+ bitarr->words[bitset64_wrd(j)] |= (word_t)0x1 << bitset64_idx(j);
+ }
+
+ DEBUG_VALIDATE(bitarr);
+}
+
+//
+// Arithmetic
+//
+
+// Returns 1 on sucess, 0 if value in array is too big
+char bit_array_as_num(const BIT_ARRAY* bitarr, uint64_t* result)
+{
+ if(bitarr->num_of_bits == 0)
+ {
+ *result = 0;
+ return 1;
+ }
+
+ word_addr_t w;
+
+ for(w = bitarr->num_of_words-1; w > 0; w--)
+ {
+ if(bitarr->words[w] > 0)
+ {
+ return 0;
+ }
+ }
+
+ *result = bitarr->words[0];
+ return 1;
+}
+
+
+// 1 iff bitarr > value
+// 0 iff bitarr == value
+// -1 iff bitarr < value
+int bit_array_cmp_uint64(const BIT_ARRAY* bitarr, uint64_t value)
+{
+ uint64_t arr_num = 0;
+
+ // If cannot put bitarr in uint64, it is > value
+ if(!bit_array_as_num(bitarr, &arr_num)) return 1;
+
+ if(arr_num > value) return 1;
+ else if(arr_num < value) return -1;
+ else return 0;
+}
+
+// If value is zero, no change is made
+void bit_array_add_uint64(BIT_ARRAY* bitarr, uint64_t value)
+{
+ if(value == 0)
+ {
+ return;
+ }
+ else if(bitarr->num_of_bits == 0)
+ {
+ bit_array_resize_critical(bitarr, WORD_SIZE - leading_zeros(value));
+ bitarr->words[0] = (word_t)value;
+ return;
+ }
+
+ char carry = 0;
+ word_addr_t i;
+
+ for(i = 0; i < bitarr->num_of_words; i++)
+ {
+ if(WORD_MAX - bitarr->words[i] < value)
+ {
+ carry = 1;
+ bitarr->words[i] += value;
+ }
+ else
+ {
+ // Carry is absorbed
+ bitarr->words[i] += value;
+ carry = 0;
+ break;
+ }
+ }
+
+ if(carry)
+ {
+ // Bit array full, need another bit after all words filled
+ bit_array_resize_critical(bitarr, bitarr->num_of_words * WORD_SIZE + 1);
+
+ // Set top word to 1
+ bitarr->words[bitarr->num_of_words-1] = 1;
+ }
+ else
+ {
+ word_t final_word = bitarr->words[bitarr->num_of_words-1];
+ word_offset_t expected_bits = bits_in_top_word(bitarr->num_of_bits);
+ word_offset_t actual_bits = WORD_SIZE - leading_zeros(final_word);
+
+ if(actual_bits > expected_bits)
+ {
+ // num_of_bits has increased -- num_of_words has not
+ bitarr->num_of_bits += (actual_bits - expected_bits);
+ }
+ }
+}
+
+// If value is greater than bitarr, bitarr is not changed and 0 is returned
+// Returns 1 on success, 0 if value > bitarr
+char bit_array_sub_uint64(BIT_ARRAY* bitarr, uint64_t value)
+{
+ if(value == 0)
+ {
+ return 1;
+ }
+ else if(bitarr->words[0] >= value)
+ {
+ bitarr->words[0] -= value;
+ return 1;
+ }
+
+ value -= bitarr->words[0];
+
+ word_addr_t i;
+
+ for(i = 1; i < bitarr->num_of_words; i++)
+ {
+ if(bitarr->words[i] > 0)
+ {
+ // deduct one
+ bitarr->words[i]--;
+
+ for(; i > 0; i--)
+ {
+ bitarr->words[i] = WORD_MAX;
+ }
+
+ // -1 since we've already deducted 1
+ bitarr->words[0] = WORD_MAX - value - 1;
+
+ return 1;
+ }
+ }
+
+ // subtract value is greater than array
+ return 0;
+}
+
+//
+// Arithmetic between bit arrays
+//
+
+// src1, src2 and dst can all be the same BIT_ARRAY
+static void _arithmetic(BIT_ARRAY* dst,
+ const BIT_ARRAY* src1,
+ const BIT_ARRAY* src2,
+ char subtract)
+{
+ word_addr_t max_words = MAX(src1->num_of_words, src2->num_of_words);
+
+ // Adding: dst_words >= max(src1 words, src2 words)
+ // Subtracting: dst_words is >= src1->num_of_words
+
+ char carry = subtract ? 1 : 0;
+
+ word_addr_t i;
+ word_t word1, word2;
+
+ for(i = 0; i < max_words; i++)
+ {
+ word1 = (i < src1->num_of_words ? src1->words[i] : 0);
+ word2 = (i < src2->num_of_words ? src2->words[i] : 0);
+
+ if(subtract)
+ word2 = ~word2;
+
+ dst->words[i] = word1 + word2 + carry;
+ // Update carry
+ carry = WORD_MAX - word1 < word2 || WORD_MAX - word1 - word2 < (word_t)carry;
+ }
+
+ if(subtract)
+ {
+ carry = 0;
+ }
+ else
+ {
+ // Check last word
+ word_offset_t bits_on_last_word = bits_in_top_word(dst->num_of_bits);
+
+ if(bits_on_last_word < WORD_SIZE)
+ {
+ word_t mask = bitmask64(bits_on_last_word);
+
+ if(dst->words[max_words-1] > mask)
+ {
+ // Array has overflowed, increase size
+ dst->num_of_bits++;
+ }
+ }
+ else if(carry)
+ {
+ // Carry onto a new word
+ if(dst->num_of_words == max_words)
+ {
+ // Need to resize for the carry bit
+ bit_array_resize_critical(dst, dst->num_of_bits+1);
+ }
+
+ dst->words[max_words] = (word_t)1;
+ }
+ }
+
+ // Zero the rest of dst array
+ for(i = max_words+carry; i < dst->num_of_words; i++)
+ {
+ dst->words[i] = (word_t)0;
+ }
+
+ DEBUG_VALIDATE(dst);
+}
+
+// src1, src2 and dst can all be the same BIT_ARRAY
+// If dst is shorter than either of src1, src2, it is enlarged
+void bit_array_add(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2)
+{
+ bit_array_ensure_size_critical(dst, MAX(src1->num_of_bits, src2->num_of_bits));
+ _arithmetic(dst, src1, src2, 0);
+}
+
+// dst = src1 - src2
+// src1, src2 and dst can all be the same BIT_ARRAY
+// If dst is shorter than src1, it will be extended to be as long as src1
+// src1 must be greater than or equal to src2 (src1 >= src2)
+void bit_array_subtract(BIT_ARRAY* dst,
+ const BIT_ARRAY* src1, const BIT_ARRAY* src2)
+{
+ // subtraction by method of complements:
+ // a - b = a + ~b + 1 = src1 + ~src2 +1
+
+ assert(bit_array_cmp(src1, src2) >= 0); // Require src1 >= src2
+
+ bit_array_ensure_size_critical(dst, src1->num_of_bits);
+ _arithmetic(dst, src1, src2, 1);
+}
+
+
+// Add `add` to `bitarr` at `pos`
+// Bounds checking not needed as out of bounds is valid
+void bit_array_add_word(BIT_ARRAY *bitarr, bit_index_t pos, uint64_t add)
+{
+ DEBUG_VALIDATE(bitarr);
+
+ if(add == 0)
+ {
+ return;
+ }
+ else if(pos >= bitarr->num_of_bits)
+ {
+ // Resize and add!
+ bit_index_t num_bits_required = pos + (WORD_SIZE - leading_zeros(add));
+ bit_array_resize_critical(bitarr, num_bits_required);
+ _set_word(bitarr, pos, (word_t)add);
+ return;
+ }
+
+ /*
+ char str[1000];
+ printf(" add_word: %s\n", bit_array_to_str_rev(bitarr, str));
+ printf(" word: %s [pos: %i]\n", _word_to_str(add, str), (int)pos);
+ */
+
+ word_t w = _get_word(bitarr, pos);
+ word_t sum = w + add;
+ char carry = WORD_MAX - w < add;
+
+ // Ensure array is big enough
+ bit_index_t num_bits_required = pos + (carry ? WORD_SIZE + 1
+ : (WORD_SIZE - leading_zeros(sum)));
+
+ bit_array_ensure_size(bitarr, num_bits_required);
+
+ _set_word(bitarr, pos, sum);
+ pos += WORD_SIZE;
+
+ if(carry)
+ {
+ word_offset_t offset = pos % WORD_SIZE;
+ word_addr_t addr = bitset64_wrd(pos);
+
+ add = (word_t)0x1 << offset;
+ carry = (WORD_MAX - bitarr->words[addr] < add);
+ sum = bitarr->words[addr] + add;
+
+ num_bits_required = addr * WORD_SIZE +
+ (carry ? WORD_SIZE + 1 : (WORD_SIZE - leading_zeros(sum)));
+
+ bit_array_ensure_size(bitarr, num_bits_required);
+
+ bitarr->words[addr++] = sum;
+
+ if(carry)
+ {
+ while(addr < bitarr->num_of_words && bitarr->words[addr] == WORD_MAX)
+ {
+ bitarr->words[addr++] = 0;
+ }
+
+ if(addr == bitarr->num_of_words)
+ {
+ bit_array_resize_critical(bitarr, addr * WORD_SIZE + 1);
+ }
+ else if(addr == bitarr->num_of_words-1 &&
+ bitarr->words[addr] == bitmask64(bits_in_top_word(bitarr->num_of_bits)))
+ {
+ bit_array_resize_critical(bitarr, bitarr->num_of_bits + 1);
+ }
+
+ bitarr->words[addr]++;
+ }
+ }
+
+ DEBUG_VALIDATE(bitarr);
+}
+
+// Add `add` to `bitarr` at `pos`
+// Bounds checking not needed as out of bounds is valid
+void bit_array_add_words(BIT_ARRAY *bitarr, bit_index_t pos, const BIT_ARRAY *add)
+{
+ assert(bitarr != add); // bitarr and add cannot point to the same bit array
+
+ bit_index_t add_top_bit_set;
+
+ if(!bit_array_find_last_set_bit(add, &add_top_bit_set))
+ {
+ // No bits set in add
+ return;
+ }
+ else if(pos >= bitarr->num_of_bits)
+ {
+ // Just resize and copy!
+ bit_index_t num_bits_required = pos + add_top_bit_set + 1;
+ bit_array_resize_critical(bitarr, num_bits_required);
+ _array_copy(bitarr, pos, add, 0, add->num_of_bits);
+ return;
+ }
+ else if(pos == 0)
+ {
+ bit_array_add(bitarr, bitarr, add);
+ return;
+ }
+
+ /*
+ char str[1000];
+ printf(" add_words1: %s\n", bit_array_to_str_rev(bitarr, str));
+ printf(" add_words2: %s\n", bit_array_to_str_rev(add, str));
+ printf(" [pos: %i]\n", (int)pos);
+ */
+
+ bit_index_t num_bits_required = pos + add_top_bit_set + 1;
+ bit_array_ensure_size(bitarr, num_bits_required);
+
+ word_addr_t first_word = bitset64_wrd(pos);
+ word_offset_t first_offset = bitset64_idx(pos);
+
+ word_t w = add->words[0] << first_offset;
+ unsigned char carry = (WORD_MAX - bitarr->words[first_word] < w);
+
+ bitarr->words[first_word] += w;
+
+ word_addr_t i = first_word + 1;
+ bit_index_t offset = WORD_SIZE - first_offset;
+
+ for(; carry || offset <= add_top_bit_set; i++, offset += WORD_SIZE)
+ {
+ w = offset < add->num_of_bits ? _get_word(add, offset) : (word_t)0;
+
+ if(i >= bitarr->num_of_words)
+ {
+ // Extend by a word
+ bit_array_resize_critical(bitarr, (bit_index_t)(i+1)*WORD_SIZE+1);
+ }
+
+ word_t prev = bitarr->words[i];
+
+ bitarr->words[i] += w + carry;
+
+ carry = (WORD_MAX - prev < w || (carry && prev + w == WORD_MAX)) ? 1 : 0;
+ }
+
+ word_offset_t top_bits
+ = WORD_SIZE - leading_zeros(bitarr->words[bitarr->num_of_words-1]);
+
+ bit_index_t min_bits = (bitarr->num_of_words-1)*WORD_SIZE + top_bits;
+
+ if(bitarr->num_of_bits < min_bits)
+ {
+ // Extend within the last word
+ bitarr->num_of_bits = min_bits;
+ }
+
+ DEBUG_VALIDATE(bitarr);
+}
+
+char bit_array_sub_word(BIT_ARRAY* bitarr, bit_index_t pos, word_t minus)
+{
+ DEBUG_VALIDATE(bitarr);
+
+ if(minus == 0)
+ {
+ return 1;
+ }
+
+ word_t w = _get_word(bitarr, pos);
+
+ if(w >= minus)
+ {
+ _set_word(bitarr, pos, w - minus);
+ DEBUG_VALIDATE(bitarr);
+ return 1;
+ }
+
+ minus -= w;
+
+ bit_index_t offset;
+ for(offset = pos + WORD_SIZE; offset < bitarr->num_of_bits; offset += WORD_SIZE)
+ {
+ w = _get_word(bitarr, offset);
+
+ if(w > 0)
+ {
+ // deduct one
+ _set_word(bitarr, offset, w - 1);
+
+ SET_REGION(bitarr, pos, offset-pos);
+
+ // -1 since we've already deducted 1
+ minus--;
+
+ _set_word(bitarr, pos, WORD_MAX - minus);
+
+ DEBUG_VALIDATE(bitarr);
+ return 1;
+ }
+ }
+
+ DEBUG_VALIDATE(bitarr);
+
+ return 0;
+}
+
+char bit_array_sub_words(BIT_ARRAY* bitarr, bit_index_t pos, BIT_ARRAY* minus)
+{
+ assert(bitarr != minus); // bitarr and minus cannot point to the same bit array
+
+ int cmp = bit_array_cmp_words(bitarr, pos, minus);
+
+ if(cmp == 0)
+ {
+ bit_array_clear_all(bitarr);
+ return 1;
+ }
+ else if(cmp < 0)
+ {
+ return 0;
+ }
+
+ bit_index_t bitarr_length = bitarr->num_of_bits;
+
+ bit_index_t bitarr_top_bit_set;
+ bit_array_find_last_set_bit(bitarr, &bitarr_top_bit_set);
+
+ // subtraction by method of complements:
+ // a - b = a + ~b + 1 = src1 + ~src2 +1
+
+ bit_array_not(minus, minus);
+
+ bit_array_add_words(bitarr, pos, minus);
+ bit_array_add_word(bitarr, pos, (word_t)1);
+
+ bit_array_sub_word(bitarr, pos+minus->num_of_bits, 1);
+ bit_array_resize(bitarr, bitarr_length);
+
+ bit_array_not(minus, minus);
+
+ DEBUG_VALIDATE(bitarr);
+
+ return 1;
+}
+
+void bit_array_mul_uint64(BIT_ARRAY *bitarr, uint64_t multiplier)
+{
+ if(bitarr->num_of_bits == 0 || multiplier == 1)
+ {
+ return;
+ }
+ else if(multiplier == 0)
+ {
+ bit_array_clear_all(bitarr);
+ return;
+ }
+
+ bit_index_t i;
+
+ for(i = bitarr->num_of_bits; i > 0; i--)
+ {
+ if(bit_array_get(bitarr, i-1))
+ {
+ bit_array_clear(bitarr, i-1);
+ bit_array_add_word(bitarr, i-1, multiplier);
+ }
+ }
+
+ DEBUG_VALIDATE(bitarr);
+}
+
+void bit_array_multiply(BIT_ARRAY *dst, BIT_ARRAY *src1, BIT_ARRAY *src2)
+{
+ if(src1->num_of_bits == 0 || src2->num_of_bits == 0)
+ {
+ bit_array_clear_all(dst);
+ return;
+ }
+
+ // Cannot pass the same array as dst, src1 AND src2
+ assert(dst != src1 || dst != src2);
+
+ // Dev: multiplier == 1?
+
+ BIT_ARRAY *read_arr, *add_arr;
+
+ if(src1 == dst)
+ {
+ read_arr = src1;
+ add_arr = src2;
+ }
+ else
+ {
+ read_arr = src2;
+ add_arr = src1;
+ }
+
+ if(dst != src1 && dst != src2)
+ {
+ bit_array_clear_all(dst);
+ }
+
+ bit_index_t i;
+
+ for(i = read_arr->num_of_bits; i > 0; i--)
+ {
+ if(bit_array_get(read_arr, i-1))
+ {
+ bit_array_clear(dst, i-1);
+ bit_array_add_words(dst, i-1, add_arr);
+ }
+ }
+
+ DEBUG_VALIDATE(dst);
+}
+
+// bitarr = round_down(bitarr / divisor)
+// rem = bitarr % divisor
+void bit_array_div_uint64(BIT_ARRAY *bitarr, uint64_t divisor, uint64_t *rem)
+{
+ assert(divisor != 0); // cannot divide by zero
+
+ bit_index_t div_top_bit = 63 - leading_zeros(divisor);
+ bit_index_t bitarr_top_bit;
+
+ if(!bit_array_find_last_set_bit(bitarr, &bitarr_top_bit))
+ {
+ *rem = 0;
+ return;
+ }
+
+ if(bitarr_top_bit < div_top_bit)
+ {
+ *rem = bitarr->words[0];
+ bit_array_clear_all(bitarr);
+ return;
+ }
+
+ // When div is shifted by offset, their top set bits are aligned
+ bit_index_t offset = bitarr_top_bit - div_top_bit;
+
+ uint64_t tmp = _get_word(bitarr, offset);
+ _set_word(bitarr, offset, (word_t)0);
+
+ // Carry if 1 if the top bit was set before left shift
+ char carry = 0;
+
+ // offset unsigned so break when offset == 0
+ while(1)
+ {
+ if(carry)
+ {
+ // (carry:tmp) - divisor = (WORD_MAX+1+tmp)-divisor
+ tmp = WORD_MAX - divisor + tmp + 1;
+ bit_array_set(bitarr, offset);
+ }
+ else if(tmp >= divisor)
+ {
+ tmp -= divisor;
+ bit_array_set(bitarr, offset);
+ }
+ else
+ {
+ bit_array_clear(bitarr, offset);
+ }
+
+ if(offset == 0)
+ break;
+
+ offset--;
+
+ // Is the top bit set (that we're about to shift off)?
+ carry = tmp & 0x8000000000000000;
+
+ tmp <<= 1;
+ tmp |= bit_array_get(bitarr, offset);
+ }
+
+ *rem = tmp;
+}
+
+// Results in:
+// quotient = dividend / divisor
+// dividend = dividend % divisor
+// (dividend is used to return the remainder)
+void bit_array_divide(BIT_ARRAY *dividend, BIT_ARRAY *quotient, BIT_ARRAY *divisor)
+{
+ assert(bit_array_cmp_uint64(divisor, 0) != 0); // Cannot divide by zero
+
+ bit_array_clear_all(quotient);
+
+ int cmp = bit_array_cmp(dividend, divisor);
+
+ if(cmp == 0)
+ {
+ bit_array_ensure_size(quotient, 1);
+ bit_array_set(quotient, 0);
+ bit_array_clear_all(dividend);
+ return;
+ }
+ else if(cmp < 0)
+ {
+ // dividend is < divisor, quotient is zero -- done
+ return;
+ }
+
+ // now we know: dividend > divisor, quotient is zero'd,
+ // dividend != 0, divisor != 0
+ bit_index_t dividend_top_bit = 0, div_top_bit = 0;
+
+ bit_array_find_last_set_bit(dividend, ÷nd_top_bit);
+ bit_array_find_last_set_bit(divisor, &div_top_bit);
+
+ // When divisor is shifted by offset, their top set bits are aligned
+ bit_index_t offset = dividend_top_bit - div_top_bit;
+
+ // offset unsigned so break when offset == 0
+ for(; ; offset--)
+ {
+ if(bit_array_cmp_words(dividend, offset, divisor) >= 0)
+ {
+ bit_array_sub_words(dividend, offset, divisor);
+ bit_array_ensure_size(quotient, offset+1);
+ bit_array_set(quotient, offset);
+ }
+
+ if(offset == 0)
+ break;
+ }
+}
+
+//
+// Read/Write from files
+//
+// file format is [8 bytes: for number of elements in array][data]
+// data is written in little endian order (least sig byte first)
+//
+
+// Saves bit array to a file. Returns the number of bytes written
+// number of bytes returned should be 8+(bitarr->num_of_bits+7)/8
+bit_index_t bit_array_save(const BIT_ARRAY* bitarr, FILE* f)
+{
+ bit_index_t num_of_bytes = roundup_bits2bytes(bitarr->num_of_bits);
+ bit_index_t bytes_written = 0;
+
+ const int endian = 1;
+ if(*(uint8_t*)&endian == 1)
+ {
+ // Little endian machine
+ // Write 8 bytes to store the number of bits in the array
+ bytes_written += fwrite(&bitarr->num_of_bits, 1, 8, f);
+
+ // Write the array
+ bytes_written += fwrite(bitarr->words, 1, num_of_bytes, f);
+ }
+ else
+ {
+ // Big endian machine
+ uint64_t i, w, whole_words = num_of_bytes/sizeof(word_t);
+ uint64_t rem_bytes = num_of_bytes - whole_words*sizeof(word_t);
+ uint64_t n_bits = byteswap64(bitarr->num_of_bits);
+
+ // Write 8 bytes to store the number of bits in the array
+ bytes_written += fwrite(&n_bits, 1, 8, f);
+
+ // Write the array
+ for(i = 0; i < whole_words; i++) {
+ w = byteswap64(bitarr->words[i]);
+ bytes_written += fwrite(&w, 1, 8, f);
+ }
+
+ if(rem_bytes > 0) {
+ w = byteswap64(bitarr->words[whole_words]);
+ bytes_written += fwrite(&w, 1, rem_bytes, f);
+ }
+ }
+
+ return bytes_written;
+}
+
+// Load a uint64 from little endian format.
+// Works for both big and little endian architectures
+static inline uint64_t le64_to_cpu(const uint8_t *x)
+{
+ return (((uint64_t)(x[0])) | ((uint64_t)(x[1]) << 8) |
+ ((uint64_t)(x[2]) << 16) | ((uint64_t)(x[3]) << 24) |
+ ((uint64_t)(x[4]) << 32) | ((uint64_t)(x[5]) << 40) |
+ ((uint64_t)(x[6]) << 48) | ((uint64_t)(x[7]) << 56));
+}
+
+// Reads bit array from a file. bitarr is resized and filled.
+// Returns 1 on success, 0 on failure
+char bit_array_load(BIT_ARRAY* bitarr, FILE* f)
+{
+ // Read in number of bits, return 0 if we can't read in
+ bit_index_t num_bits;
+ if(fread(&num_bits, 1, 8, f) != 8) return 0;
+ num_bits = le64_to_cpu((uint8_t*)&num_bits);
+
+ // Resize
+ bit_array_resize_critical(bitarr, num_bits);
+
+ // Have to calculate how many bytes are needed for the file
+ // (Note: this may be different from num_of_words * sizeof(word_t))
+ bit_index_t num_of_bytes = roundup_bits2bytes(bitarr->num_of_bits);
+ if(fread(bitarr->words, 1, num_of_bytes, f) != num_of_bytes) return 0;
+
+ // Fix endianness
+ word_addr_t i;
+ for(i = 0; i < bitarr->num_of_words; i++)
+ bitarr->words[i] = le64_to_cpu((uint8_t*)&bitarr->words[i]);
+
+ // Mask top word
+ _mask_top_word(bitarr);
+ DEBUG_VALIDATE(bitarr);
+ return 1;
+}
+
+//
+// Hash function
+//
+
+/* From: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/* From: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/* From: lookup3.c, by Bob Jenkins, May 2006, Public Domain. */
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+From: lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+--------------------------------------------------------------------
+hashword2() -- same as hashword(), but take two seeds and return two
+32-bit values. pc and pb must both be nonnull, and *pc and *pb must
+both be initialized with seeds. If you pass in (*pb)==0, the output
+(*pc) will be the same as the return value from hashword().
+--------------------------------------------------------------------
+*/
+static void hashword2 (
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t *pc, /* IN: seed OUT: primary hash value */
+uint32_t *pb) /* IN: more seed OUT: secondary hash value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc;
+ c += *pb;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ *pc=c; *pb=b;
+}
+
+// Pass seed as 0 on first call, pass previous hash value if rehashing due
+// to a collision
+// Using bob jenkins hash lookup3
+uint64_t bit_array_hash(const BIT_ARRAY* bitarr, uint64_t seed)
+{
+ uint32_t seed32[2];
+ memcpy(seed32, &seed, sizeof(uint32_t)*2);
+
+ // Round up length to number 32bit words
+ hashword2((uint32_t*)bitarr->words, (bitarr->num_of_bits + 31) / 32,
+ &seed32[0], &seed32[1]);
+
+ // XOR with array length. This ensures arrays with different length but same
+ // contents have different hash values
+ seed ^= bitarr->num_of_bits;
+
+ return seed;
+}
+
+
+//
+// Generally useful functions
+//
+
+// Generalised 'binary to string' function
+// Adds bits to the string in order of lsb to msb
+// e.g. 0b11010 (26 in decimal) would come out as "01011"
+char* bit_array_word2str(const void *ptr, size_t num_of_bits, char *str)
+{
+ const uint8_t* d = (const uint8_t*)ptr;
+
+ size_t i;
+ for(i = 0; i < num_of_bits; i++)
+ {
+ uint8_t bit = (d[i/8] >> (i % 8)) & 0x1;
+ str[i] = bit ? '1' : '0';
+ }
+ str[num_of_bits] = '\0';
+ return str;
+}
+
+char* bit_array_word2str_rev(const void *ptr, size_t num_of_bits, char *str)
+{
+ const uint8_t* d = (const uint8_t*)ptr;
+
+ size_t i;
+ for(i = 0; i < num_of_bits; i++)
+ {
+ uint8_t bit = (d[i/8] >> (i % 8)) & 0x1;
+ str[num_of_bits-1-i] = bit ? '1' : '0';
+ }
+ str[num_of_bits] = '\0';
+ return str;
+}
diff --git a/debian/rapmap/bit_array.h b/debian/rapmap/bit_array.h
new file mode 100644
index 0000000..70b50ad
--- /dev/null
+++ b/debian/rapmap/bit_array.h
@@ -0,0 +1,552 @@
+/*
+ bit_array.h
+ project: bit array C library
+ url: https://github.com/noporpoise/BitArray/
+ maintainer: Isaac Turner <turner.isaac at gmail.com>
+ license: Public Domain, no warranty
+ date: Sep 2014
+*/
+
+#ifndef BIT_ARRAY_HEADER_SEEN
+#define BIT_ARRAY_HEADER_SEEN
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "bit_macros.h"
+
+typedef struct BIT_ARRAY BIT_ARRAY;
+
+// 64 bit words
+typedef uint64_t word_t, word_addr_t, bit_index_t;
+typedef uint8_t word_offset_t; // Offset within a 64 bit word
+
+#define BIT_INDEX_MIN 0
+#define BIT_INDEX_MAX (~(bit_index_t)0)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Structs
+//
+
+struct BIT_ARRAY
+{
+ word_t* words;
+ bit_index_t num_of_bits;
+ // Number of words used -- this is just round_up(num_of_bits / 64)
+ // if num_of_bits == 0, this is 0
+ word_addr_t num_of_words;
+ // For more efficient allocation we use realloc only to double size --
+ // not for adding every word. Initial size is INIT_CAPACITY_WORDS.
+ word_addr_t capacity_in_words;
+};
+
+//
+// Basics: Constructor, destructor, get length, resize
+//
+
+// Constructor - create a new bit array of length nbits
+BIT_ARRAY* bit_array_create(bit_index_t nbits);
+
+// Destructor - free the memory used for a bit array
+void bit_array_free(BIT_ARRAY* bitarray);
+
+// Allocate using existing struct
+BIT_ARRAY* bit_array_alloc(BIT_ARRAY* bitarr, bit_index_t nbits);
+void bit_array_dealloc(BIT_ARRAY* bitarr);
+
+// Get length of bit array
+bit_index_t bit_array_length(const BIT_ARRAY* bit_arr);
+
+// Change the size of a bit array. Enlarging an array will add zeros
+// to the end of it. Returns 1 on success, 0 on failure (e.g. not enough memory)
+char bit_array_resize(BIT_ARRAY* bitarr, bit_index_t new_num_of_bits);
+
+// If bitarr length < num_bits, resizes to num_bits
+char bit_array_ensure_size(BIT_ARRAY* bitarr, bit_index_t ensure_num_of_bits);
+
+// Same as above but exit with an error message if out of memory
+void bit_array_resize_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits);
+void bit_array_ensure_size_critical(BIT_ARRAY* bitarr, bit_index_t num_of_bits);
+
+
+//
+// Macros
+//
+
+//
+// Get, set, clear, assign and toggle individual bits
+// Macros for fast access -- beware: no bounds checking
+//
+
+#define bit_array_get(arr,i) bitset_get((arr)->words, i)
+#define bit_array_set(arr,i) bitset_set((arr)->words, i)
+#define bit_array_clear(arr,i) bitset_del((arr)->words, i)
+#define bit_array_toggle(arr,i) bitset_tgl((arr)->words, i)
+// c must be 0 or 1
+#define bit_array_assign(arr,i,c) bitset_cpy((arr)->words,i,c)
+
+//
+// Get, set, clear, assign and toggle individual bits
+// "Safe": use assert() to check bounds
+//
+
+// Get the value of a bit (returns 0 or 1)
+char bit_array_get_bit(const BIT_ARRAY* bitarr, bit_index_t b);
+void bit_array_set_bit(BIT_ARRAY* bitarr, bit_index_t b);
+void bit_array_clear_bit(BIT_ARRAY* bitarr, bit_index_t b);
+void bit_array_toggle_bit(BIT_ARRAY* bitarr, bit_index_t b);
+// If char c != 0, set bit; otherwise clear bit
+void bit_array_assign_bit(BIT_ARRAY* bitarr, bit_index_t b, char c);
+
+//
+// "Resizing": enlarge array if needed
+//
+
+char bit_array_rget(BIT_ARRAY* bitarr, bit_index_t b);
+void bit_array_rset(BIT_ARRAY* bitarr, bit_index_t b);
+void bit_array_rclear(BIT_ARRAY* bitarr, bit_index_t b);
+void bit_array_rtoggle(BIT_ARRAY* bitarr, bit_index_t b);
+void bit_array_rassign(BIT_ARRAY* bitarr, bit_index_t b, char c);
+
+//
+// Set, clear and toggle several bits at once
+//
+
+// Set multiple bits at once.
+// e.g. set bits 1, 20 & 31: bit_array_set_bits(bitarr, 3, 1,20,31);
+// Note: variable args are of type unsigned int
+void bit_array_set_bits(BIT_ARRAY* bitarr, size_t n, ...);
+
+// Clear multiple bits at once.
+// e.g. clear bits 1, 20 & 31: bit_array_clear_bits(bitarr, 3, 1,20,31);
+// Note: variable args are of type unsigned int
+void bit_array_clear_bits(BIT_ARRAY* bitarr, size_t n, ...);
+
+// Toggle multiple bits at once
+// e.g. toggle bits 1, 20 & 31: bit_array_toggle_bits(bitarr, 3, 1,20,31);
+// Note: variable args are of type unsigned int
+void bit_array_toggle_bits(BIT_ARRAY* bitarr, size_t n, ...);
+
+//
+// Set, clear and toggle all bits in a region
+//
+
+// Set all the bits in a region
+void bit_array_set_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len);
+
+// Clear all the bits in a region
+void bit_array_clear_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len);
+
+// Toggle all the bits in a region
+void bit_array_toggle_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len);
+
+//
+// Set, clear and toggle all bits at once
+//
+
+// Set all bits in this array to 1
+void bit_array_set_all(BIT_ARRAY* bitarr);
+
+// Set all bits in this array to 0
+void bit_array_clear_all(BIT_ARRAY* bitarr);
+
+// Set all 1 bits to 0, and all 0 bits to 1
+void bit_array_toggle_all(BIT_ARRAY* bitarr);
+
+//
+// Get / set a word of a given size
+//
+
+// First bit is in the least significant bit position
+// start index must be within the range of the bit array (0 <= x < length)
+uint64_t bit_array_get_word64(const BIT_ARRAY* bitarr, bit_index_t start);
+uint32_t bit_array_get_word32(const BIT_ARRAY* bitarr, bit_index_t start);
+uint16_t bit_array_get_word16(const BIT_ARRAY* bitarr, bit_index_t start);
+uint8_t bit_array_get_word8(const BIT_ARRAY* bitarr, bit_index_t start);
+uint64_t bit_array_get_wordn(const BIT_ARRAY* bitarr, bit_index_t start, int n);
+
+// Set 64 bits at once from a particular start position
+void bit_array_set_word64(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word);
+void bit_array_set_word32(BIT_ARRAY* bitarr, bit_index_t start, uint32_t word);
+void bit_array_set_word16(BIT_ARRAY* bitarr, bit_index_t start, uint16_t word);
+void bit_array_set_word8(BIT_ARRAY* bitarr, bit_index_t start, uint8_t byte);
+void bit_array_set_wordn(BIT_ARRAY* bitarr, bit_index_t start, uint64_t word, int n);
+
+//
+// Number of bits set
+//
+
+// Get the number of bits set (hamming weight)
+bit_index_t bit_array_num_bits_set(const BIT_ARRAY* bitarr);
+
+// Get the number of bits not set (length - hamming weight)
+bit_index_t bit_array_num_bits_cleared(const BIT_ARRAY* bitarr);
+
+// Get the number of bits set in on array and not the other. This is equivalent
+// to hamming weight of the XOR when the two arrays are the same length.
+// e.g. 10101 vs 00111 => hamming distance 2 (XOR is 10010)
+bit_index_t bit_array_hamming_distance(const BIT_ARRAY* arr1,
+ const BIT_ARRAY* arr2);
+
+// Parity - returns 1 if odd number of bits set, 0 if even
+char bit_array_parity(const BIT_ARRAY* bitarr);
+
+//
+// Find indices of set/clear bits
+//
+
+// Find the index of the next bit that is set, at or after `offset`
+// Returns 1 if a bit is set, otherwise 0
+// Index of next set bit is stored in the integer pointed to by result
+// If no next bit is set result is not changed
+char bit_array_find_next_set_bit(const BIT_ARRAY* bitarr, bit_index_t offset,
+ bit_index_t* result);
+
+// Find the index of the next bit that is NOT set, at or after `offset`
+// Returns 1 if a bit is NOT set, otherwise 0
+// Index of next zero bit is stored in the integer pointed to by `result`
+// If no next bit is zero, value at `result` is not changed
+char bit_array_find_next_clear_bit(const BIT_ARRAY* bitarr, bit_index_t offset,
+ bit_index_t* result);
+
+// Find the index of the previous bit that is set, before offset.
+// Returns 1 if a bit is set, otherwise 0
+// Index of previous set bit is stored in the integer pointed to by `result`
+// If no previous bit is set result is not changed
+char bit_array_find_prev_set_bit(const BIT_ARRAY* bitarr, bit_index_t offset,
+ bit_index_t* result);
+
+// Find the index of the previous bit that is NOT set, before offset.
+// Returns 1 if a bit is clear, otherwise 0
+// Index of previous zero bit is stored in the integer pointed to by `result`
+// If no previous bit is zero result is not changed
+char bit_array_find_prev_clear_bit(const BIT_ARRAY* bitarr, bit_index_t offset,
+ bit_index_t* result);
+
+// Find the index of the first bit that is set.
+// Returns 1 if a bit is set, otherwise 0
+// Index of first set bit is stored in the integer pointed to by `result`
+// If no bit is set result is not changed
+char bit_array_find_first_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result);
+
+// Find the index of the first bit that is NOT set.
+// Returns 1 if a bit is clear, otherwise 0
+// Index of first zero bit is stored in the integer pointed to by `result`
+// If no bit is zero result is not changed
+char bit_array_find_first_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result);
+
+// Find the index of the last bit that is set.
+// Returns 1 if a bit is set, otherwise 0
+// Index of last set bit is stored in the integer pointed to by `result`
+// If no bit is set result is not changed
+char bit_array_find_last_set_bit(const BIT_ARRAY* bitarr, bit_index_t* result);
+
+// Find the index of the last bit that is NOT set.
+// Returns 1 if a bit is clear, otherwise 0
+// Index of last zero bit is stored in the integer pointed to by `result`
+// If no bit is zero result is not changed
+char bit_array_find_last_clear_bit(const BIT_ARRAY* bitarr, bit_index_t* result);
+
+
+//
+// Sorting
+//
+
+// Put all the 0s before all the 1s
+void bit_array_sort_bits(BIT_ARRAY* bitarr);
+
+// Put all the 1s before all the 0s
+void bit_array_sort_bits_rev(BIT_ARRAY* bitarr);
+
+
+//
+// String and printing methods
+//
+
+// Construct a BIT_ARRAY from a string.
+void bit_array_from_str(BIT_ARRAY* bitarr, const char* bitstr);
+
+// Construct a BIT_ARRAY from a substring with given on and off characters.
+void bit_array_from_substr(BIT_ARRAY* bitarr, bit_index_t offset,
+ const char* str, size_t len,
+ const char *on, const char *off, char left_to_right);
+
+// Takes a char array to write to. `str` must be bitarr->num_of_bits+1 in
+// length. Terminates string with '\0'
+char* bit_array_to_str(const BIT_ARRAY* bitarr, char* str);
+char* bit_array_to_str_rev(const BIT_ARRAY* bitarr, char* str);
+
+// Get a string representations for a given region, using given on/off
+// characters.
+// Note: does not null-terminate
+void bit_array_to_substr(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ char* str, char on, char off, char left_to_right);
+
+// Print this array to a file stream. Prints '0's and '1'. Doesn't print
+// newline.
+void bit_array_print(const BIT_ARRAY* bitarr, FILE* fout);
+
+// Print a string representations for a given region, using given on/off
+// characters. Reverse prints from highest to lowest -- this is useful for
+// printing binary numbers
+void bit_array_print_substr(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ FILE* fout, char on, char off, char left_to_right);
+
+//
+// Decimal
+//
+
+// Get bit array as decimal str (e.g. 0b1101 -> "13")
+size_t bit_array_to_decimal(const BIT_ARRAY *bitarr, char *str, size_t len);
+
+// Return number of characters used
+size_t bit_array_from_decimal(BIT_ARRAY *bitarr, const char* decimal);
+
+//
+// Hexidecimal
+//
+
+// Loads array from hex string
+// Returns the number of bits loaded (will be chars rounded up to multiple of 8)
+// (0 on failure)
+bit_index_t bit_array_from_hex(BIT_ARRAY* bitarr, bit_index_t offset,
+ const char* str, size_t len);
+
+// Returns number of characters written
+size_t bit_array_to_hex(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ char* str, char uppercase);
+
+// Print bit array as hex
+size_t bit_array_print_hex(const BIT_ARRAY* bitarr,
+ bit_index_t start, bit_index_t length,
+ FILE* fout, char uppercase);
+
+//
+// Clone and copy
+//
+
+// Copy a BIT_ARRAY struct and the data it holds - returns pointer to new object
+#define bit_array_dup bit_array_clone
+BIT_ARRAY* bit_array_clone(const BIT_ARRAY* bitarr);
+
+// Copy bits from one array to another
+// Note: use MACRO bit_array_copy
+// Destination and source can be the same bit_array and
+// src/dst regions can overlap
+void bit_array_copy(BIT_ARRAY* dst, bit_index_t dstindx,
+ const BIT_ARRAY* src, bit_index_t srcindx,
+ bit_index_t length);
+
+// copy all of src to dst. dst is resized to match src.
+void bit_array_copy_all(BIT_ARRAY* dst, const BIT_ARRAY* src);
+
+//
+// Logic operators
+//
+
+// BIT_ARRAYs can all be different or the same object
+// dest array will be resized if it is too short
+//
+void bit_array_and(BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2);
+void bit_array_or (BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2);
+void bit_array_xor(BIT_ARRAY* dest, const BIT_ARRAY* src1, const BIT_ARRAY* src2);
+void bit_array_not(BIT_ARRAY* dest, const BIT_ARRAY* src);
+
+//
+// Comparisons
+//
+
+// Note: (bit_array_cmp(a,b) == 0) <=> (bit_array_cmp_big_endian(a,b) == 0)
+
+// comparison functions return:
+// 1 iff bitarr1 > bitarr2
+// 0 iff bitarr1 == bitarr2
+// -1 iff bitarr1 < bitarr2
+
+// Compare two bit arrays by value stored, with index 0 being the Least
+// Significant Bit (LSB). Arrays do not have to be the same length.
+// Example: ..0101 (5) > ...0011 (3) [index 0 is LSB at right hand side]
+int bit_array_cmp(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2);
+
+// Compare two bit arrays by value stored, with index 0 being the Most
+// Significant Bit (MSB). Arrays do not have to be the same length.
+// Example: 10.. > 01.. [index 0 is MSB at left hand side]
+int bit_array_cmp_big_endian(const BIT_ARRAY* bitarr1, const BIT_ARRAY* bitarr2);
+
+// compare bitarr with (bitarr2 << pos)
+int bit_array_cmp_words(const BIT_ARRAY *bitarr,
+ bit_index_t pos, const BIT_ARRAY *bitarr2);
+
+//
+// Shift, interleave, reverse
+//
+
+// Shift array left/right. If fill is zero, filled with 0, otherwise 1
+void bit_array_shift_right(BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill);
+void bit_array_shift_left (BIT_ARRAY* bitarr, bit_index_t shift_dist, char fill);
+
+// shift left without losing any bits. Resizes bitarr.
+void bit_array_shift_left_extend(BIT_ARRAY* bitarr, bit_index_t shift_dist,
+ char fill);
+
+// Cyclic shift
+void bit_array_cycle_right(BIT_ARRAY* bitarr, bit_index_t dist);
+void bit_array_cycle_left (BIT_ARRAY* bitarr, bit_index_t dist);
+
+// Interleave
+// dst cannot point to the same bit array as src1 or src2
+// src1, src2 may point to the same bit array
+// abcd 1234 -> a1b2c3d4
+// 0011 0000 -> 00001010
+// 1111 0000 -> 10101010
+// 0101 1010 -> 01100110
+// Extends dst if it is too short, but does not shrink it if it is too long
+// if dst is longer than length(src1)+length(src2), the end bits are not altered
+void bit_array_interleave(BIT_ARRAY* dst,
+ const BIT_ARRAY* src1,
+ const BIT_ARRAY* src2);
+
+// Reverse the whole array or part of it
+void bit_array_reverse(BIT_ARRAY* bitarr);
+void bit_array_reverse_region(BIT_ARRAY* bitarr, bit_index_t start, bit_index_t len);
+
+//
+// Numeric
+//
+
+// Returns 1 on sucess, 0 if value in array is too big
+char bit_array_as_num(const BIT_ARRAY* bitarr, uint64_t* result);
+
+// 1 iff bitarr > value
+// 0 iff bitarr == value
+// -1 iff bitarr < value
+int bit_array_cmp_uint64(const BIT_ARRAY* bitarr, uint64_t value);
+
+//
+// Arithmetic
+//
+
+// bitarr will be extended if needed
+void bit_array_add_uint64(BIT_ARRAY* bitarr, uint64_t value);
+
+// Add `add` to `bitarr` at `pos` -- same as:
+// bitarr + (add << pos)
+// where pos can be bigger than the length of the array (bitarr will be resized)
+void bit_array_add_word(BIT_ARRAY *bitarr, bit_index_t pos, uint64_t add);
+
+// Add `add` to `bitarr` at `pos`
+void bit_array_add_words(BIT_ARRAY *bitarr, bit_index_t pos, const BIT_ARRAY *add);
+
+// If value is greater than bitarr, bitarr is not changed and 0 is returned
+// Returns 1 on success, 0 if value > bitarr
+char bit_array_sub_uint64(BIT_ARRAY* bitarr, uint64_t value);
+
+// minus `minus` from `bitarr` at `pos` -- same as:
+// bitarr + (minus << pos)
+// Returns 1 on success, 0 if value > bitarr
+char bit_array_sub_word(BIT_ARRAY *bitarr, bit_index_t pos, word_t minus);
+
+// minus `minus` from `bitarr` at `pos`
+// Returns 1 on success, 0 if value > bitarr
+char bit_array_sub_words(BIT_ARRAY* bitarr, bit_index_t pos, BIT_ARRAY* minus);
+
+// Multiply by some value
+void bit_array_mul_uint64(BIT_ARRAY *bitarr, uint64_t multiplier);
+
+// bitarr = round_down(bitarr / divisor)
+// rem = bitarr % divisor
+void bit_array_div_uint64(BIT_ARRAY *bitarr, uint64_t divisor, uint64_t *rem);
+
+//
+// Arithmetic between arrays
+//
+
+// dst = src1 + src2
+// src1, src2 and dst can all be the same BIT_ARRAY
+// If dst is shorter than either of src1, src2, it is enlarged
+void bit_array_add(BIT_ARRAY* dst, const BIT_ARRAY* src1, const BIT_ARRAY* src2);
+
+// dst = src1 - src2
+// src1, src2 and dst can all be the same BIT_ARRAY
+// If dst is shorter than src1, it will be extended to be as long as src1
+// src1 must be greater than or equal to src2 (src1 >= src2)
+void bit_array_subtract(BIT_ARRAY* dst,
+ const BIT_ARRAY* src1, const BIT_ARRAY* src2);
+
+// dst = src1 * src2
+// Pointers cannot all point to the same BIT_ARRAY
+void bit_array_multiply(BIT_ARRAY *dst, BIT_ARRAY *src1, BIT_ARRAY *src2);
+
+// Results in:
+// quotient = dividend / divisor
+// dividend = dividend % divisor
+// (dividend is used to return the remainder)
+void bit_array_divide(BIT_ARRAY *dividend, BIT_ARRAY *quotient, BIT_ARRAY *divisor);
+
+//
+// Read/Write bit_array to a file
+//
+// File format is [8 bytes: for number of elements in array][data]
+// Number of bytes of data is: (int)((num_of_bits + 7) / 8)
+//
+
+// Saves bit array to a file
+// returns the number of bytes written
+bit_index_t bit_array_save(const BIT_ARRAY* bitarr, FILE* f);
+
+// Reads bit array from a file. bitarr is resized and filled.
+// Returns 1 on success, 0 on failure
+char bit_array_load(BIT_ARRAY* bitarr, FILE* f);
+
+
+//
+// Hash function
+//
+
+// Pass seed as 0 on first call, pass previous hash value if rehashing due
+// to a collision
+// Using bob jenkins hash lookup3
+uint64_t bit_array_hash(const BIT_ARRAY* bitarr, uint64_t seed);
+
+//
+// Randomness
+//
+
+// Set bits randomly with probability prob : 0 <= prob <= 1
+void bit_array_random(BIT_ARRAY* bitarr, float prob);
+
+// Shuffle the bits in an array randomly
+void bit_array_shuffle(BIT_ARRAY* bitarr);
+
+// Get the next permutation of an array with a fixed size and given number of
+// bits set. Also known as next lexicographic permutation.
+// Given a bit array find the next lexicographic orginisation of the bits
+// Number of possible combinations given by (size choose bits_set) i.e. nCk
+// 00011 -> 00101 -> 00110 -> 01001 -> 01010 ->
+// 01100 -> 10001 -> 10010 -> 10100 -> 11000 -> 00011 (back to start)
+void bit_array_next_permutation(BIT_ARRAY* bitarr);
+
+//
+// Generally useful functions
+//
+
+// Generalised 'binary to string' function
+// Adds bits to the string in order of lsb to msb
+// e.g. 0b11010 (26 in decimal) would come out as "01011"
+char* bit_array_word2str(const void *ptr, size_t num_of_bits, char *str);
+
+// Same as above but in reverse
+char* bit_array_word2str_rev(const void *ptr, size_t num_of_bits, char *str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/debian/rapmap/bit_macros.h b/debian/rapmap/bit_macros.h
new file mode 100644
index 0000000..7eebfcb
--- /dev/null
+++ b/debian/rapmap/bit_macros.h
@@ -0,0 +1,205 @@
+/*
+ bit_macros.h
+ project: bit array C library
+ url: https://github.com/noporpoise/BitArray/
+ author: Isaac Turner <turner.isaac at gmail.com>
+ license: Public Domain, no warranty
+ date: Dec 2013
+*/
+
+#ifndef BITSET_H_
+#define BITSET_H_
+
+#include <inttypes.h>
+#include <sched.h>
+
+// trailing_zeros is number of least significant zeros
+// leading_zeros is number of most significant zeros
+#if defined(_WIN32)
+ #define trailing_zeros(x) ({ __typeof(x) _r; _BitScanReverse64(&_r, x); _r; })
+ #define leading_zeros(x) ({ __typeof(x) _r; _BitScanForward64(&_r, x); _r; })
+#else
+ #define trailing_zeros(x) ((x) ? (__typeof(x))__builtin_ctzll(x) : (__typeof(x))sizeof(x)*8)
+ #define leading_zeros(x) ((x) ? (__typeof(x))__builtin_clzll(x) : (__typeof(x))sizeof(x)*8)
+#endif
+
+// Get index of top set bit. If x is 0 return nbits
+#define top_set_bit(x) ((x) ? sizeof(x)*8-leading_zeros(x)-1 : sizeof(x)*8)
+
+#define roundup_bits2bytes(bits) (((bits)+7)/8)
+#define roundup_bits2words32(bits) (((bits)+31)/32)
+#define roundup_bits2words64(bits) (((bits)+63)/64)
+
+// Round a number up to the nearest number that is a power of two
+#define roundup2pow(x) (1UL << (64 - leading_zeros(x)))
+
+#define rot32(x,r) (((x)<<(r)) | ((x)>>(32-(r))))
+#define rot64(x,r) (((x)<<(r)) | ((x)>>(64-(r))))
+
+// need to check for length == 0, undefined behaviour if uint64_t >> 64 etc
+#define bitmask(nbits,type) ((nbits) ? ~(type)0 >> (sizeof(type)*8-(nbits)): (type)0)
+#define bitmask32(nbits) bitmask(nbits,uint32_t)
+#define bitmask64(nbits) bitmask(nbits,uint64_t)
+
+// A possibly faster way to combine two words with a mask
+//#define bitmask_merge(a,b,abits) ((a & abits) | (b & ~abits))
+#define bitmask_merge(a,b,abits) (b ^ ((a ^ b) & abits))
+
+// Swap lowest four bits. A nibble is 4 bits (i.e. half a byte)
+#define rev_nibble(x) ((((x)&1)<<3)|(((x)&2)<<1)|(((x)&4)>>1)|(((x)&8)>>3))
+
+//
+// Bit array (bitset)
+//
+// bitsetX_wrd(): get word for a given position
+// bitsetX_idx(): get index within word for a given position
+#define _VOLPTR(x) ((volatile __typeof(x) *)(&(x)))
+#define _VOLVALUE(x) (*_VOLPTR(x))
+
+#define _TYPESHIFT(arr,word,shift) \
+ ((__typeof(*(arr)))((__typeof(*(arr)))(word) << (shift)))
+
+#define bitsetX_wrd(wrdbits,pos) ((pos) / (wrdbits))
+#define bitsetX_idx(wrdbits,pos) ((pos) % (wrdbits))
+
+#define bitset32_wrd(pos) ((pos) >> 5)
+#define bitset32_idx(pos) ((pos) & 31)
+
+#define bitset64_wrd(pos) ((pos) >> 6)
+#define bitset64_idx(pos) ((pos) & 63)
+
+//
+// Bit functions on arrays
+//
+#define bitset2_get(arr,wrd,idx) (((arr)[wrd] >> (idx)) & 0x1)
+#define bitset2_set(arr,wrd,idx) ((arr)[wrd] |= _TYPESHIFT(arr,1,idx))
+#define bitset2_del(arr,wrd,idx) ((arr)[wrd] &=~ _TYPESHIFT(arr,1,idx))
+#define bitset2_tgl(arr,wrd,idx) ((arr)[wrd] ^= _TYPESHIFT(arr,1,idx))
+#define bitset2_or(arr,wrd,idx,bit) ((arr)[wrd] |= _TYPESHIFT(arr,bit,idx))
+#define bitset2_xor(arr,wrd,idx,bit) ((arr)[wrd] = ~((arr)[wrd] ^ (~_TYPESHIFT(arr,bit,idx))))
+#define bitset2_and(arr,wrd,idx,bit) ((arr)[wrd] &= (_TYPESHIFT(arr,bit,idx) | ~_TYPESHIFT(arr,1,idx)))
+#define bitset2_cpy(arr,wrd,idx,bit) ((arr)[wrd] = ((arr)[wrd] &~ _TYPESHIFT(arr,1,idx)) | _TYPESHIFT(arr,bit,idx))
+
+//
+// Thread safe versions
+//
+// They return the value of the bit (0 or 1) before it was updated
+#define bitset2_get_mt(arr,wrd,idx) bitset2_get(_VOLPTR(*(arr)),wrd,idx)
+#define bitset2_set_mt(arr,wrd,idx) ((__sync_fetch_and_or (_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,1,idx)) >> (idx))&1)
+#define bitset2_del_mt(arr,wrd,idx) ((__sync_fetch_and_and(_VOLPTR((arr)[wrd]), ~_TYPESHIFT(arr,1,idx)) >> (idx))&1)
+#define bitset2_tgl_mt(arr,wrd,idx) ((__sync_fetch_and_xor(_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,1,idx)) >> (idx))&1)
+#define bitset2_or_mt(arr,wrd,idx,bit) ((__sync_fetch_and_or (_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,bit,idx)) >> (idx))&1)
+#define bitset2_xor_mt(arr,wrd,idx,bit) ((__sync_fetch_and_xor(_VOLPTR((arr)[wrd]), _TYPESHIFT(arr,bit,idx)) >> (idx))&1)
+#define bitset2_and_mt(arr,wrd,idx,bit) ((__sync_fetch_and_and(_VOLPTR((arr)[wrd]), (_TYPESHIFT(arr,bit,idx) | ~_TYPESHIFT(arr,1,idx))) >> (idx))&1)
+#define bitset2_cpy_mt(arr,wrd,idx,bit) ((bit) ? bitset2_set_mt(arr,wrd,idx) : bitset2_del_mt(arr,wrd,idx))
+
+//
+// Auto detect size of type from pointer
+//
+#define bitset_wrd(arr,pos) bitsetX_wrd(sizeof(*(arr))*8,pos)
+#define bitset_idx(arr,pos) bitsetX_idx(sizeof(*(arr))*8,pos)
+#define bitset_op(func,arr,pos) func(arr, bitset_wrd(arr,pos), bitset_idx(arr,pos))
+#define bitset_op2(func,arr,pos,bit) func(arr, bitset_wrd(arr,pos), bitset_idx(arr,pos), bit)
+
+// Auto-detect type size: bit functions
+#define bitset_get(arr,pos) bitset_op(bitset2_get, arr, pos)
+#define bitset_set(arr,pos) bitset_op(bitset2_set, arr, pos)
+#define bitset_del(arr,pos) bitset_op(bitset2_del, arr, pos)
+#define bitset_tgl(arr,pos) bitset_op(bitset2_tgl, arr, pos)
+#define bitset_or(arr,pos,bit) bitset_op2(bitset2_or, arr, pos, bit)
+#define bitset_xor(arr,pos,bit) bitset_op2(bitset2_xor, arr, pos, bit)
+#define bitset_and(arr,pos,bit) bitset_op2(bitset2_and, arr, pos, bit)
+#define bitset_cpy(arr,pos,bit) bitset_op2(bitset2_cpy, arr, pos, bit)
+
+// Auto-detect type size: thread safe bit functions
+// They return the value of the bit (0 or 1) before it was updated
+#define bitset_get_mt(arr,pos) bitset_op(bitset2_get_mt, arr, pos)
+#define bitset_set_mt(arr,pos) bitset_op(bitset2_set_mt, arr, pos)
+#define bitset_del_mt(arr,pos) bitset_op(bitset2_del_mt, arr, pos)
+#define bitset_tgl_mt(arr,pos) bitset_op(bitset2_tgl_mt, arr, pos)
+#define bitset_or_mt(arr,pos,bit) bitset_op2(bitset2_or_mt, arr, pos, bit)
+#define bitset_xor_mt(arr,pos,bit) bitset_op2(bitset2_xor_mt, arr, pos, bit)
+#define bitset_and_mt(arr,pos,bit) bitset_op2(bitset2_and_mt, arr, pos, bit)
+#define bitset_cpy_mt(arr,pos,bit) bitset_op2(bitset2_cpy_mt, arr, pos, bit)
+
+// Clearing a word does not return a meaningful value
+#define bitset_clear_word(arr,pos) ((arr)[bitset_wrd(arr,pos)] = 0)
+#define bitset_clear_word_mt(arr,pos) (_VOLVALUE((arr)[bitset_wrd(arr,pos)]) = 0)
+
+//
+// Compact bit array of spin locks
+// These are most effecient when arr is of type: volatile char*
+//
+// Acquire a lock
+#define bitlock_acquire_block(arr,pos,wait,abandon) do { \
+ size_t _w = bitset_wrd(arr,pos); \
+ __typeof(*(arr)) _o, _n, _b = _TYPESHIFT(arr, 1, bitset_idx(arr,pos)); \
+ do { \
+ while((_o = _VOLVALUE((arr)[_w])) & _b) { wait } \
+ abandon \
+ _n = _o | _b; \
+ } while(!__sync_bool_compare_and_swap(_VOLPTR((arr)[_w]), _o, _n)); \
+ __sync_synchronize(); /* Must not move commands to before acquiring lock */ \
+} while(0)
+
+// Undefined behaviour if you do not already hold the lock
+#define bitlock_release(arr,pos) do { \
+ size_t _w = bitset_wrd(arr,pos); \
+ __typeof(*(arr)) _mask = ~_TYPESHIFT(arr, 1, bitset_idx(arr,pos)); \
+ __sync_synchronize(); /* Must get the lock before releasing it */ \
+ __sync_and_and_fetch(_VOLPTR((arr)[_w]), _mask); \
+} while(0)
+
+#define bitlock_acquire(arr,pos) bitlock_acquire_block(arr,pos,{},{})
+
+// calls yield if cannot acquire the lock
+#define bitlock_yield_acquire(arr,pos) bitlock_acquire_block(arr,pos,sched_yield();,{})
+
+// Block until we get the lock or someone else does
+// sets the memory pointed to by retptr to 1 if we got the lock, 0 otherwise
+#define bitlock_try_acquire(arr,pos,retptr) do { \
+ *retptr = 1; /* default to success, set to zero if locked */ \
+ bitlock_acquire_block(arr,pos,{*retptr=0;break;},if(!*retptr){break;}); \
+} while(0)
+
+/*
+ * Byteswapping
+ */
+
+/* clang uses these to check for features */
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+/* GCC versions < 4.3 do not have __builtin_bswapX() */
+#if ( defined(__clang__) && !__has_builtin(__builtin_bswap64) ) || \
+ ( !defined(__clang__) && defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+ ( (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)) )
+ #define byteswap64(x) ( (((uint64_t)(x) << 56)) | \
+ (((uint64_t)(x) << 40) & 0xff000000000000ULL) | \
+ (((uint64_t)(x) << 24) & 0xff0000000000ULL) | \
+ (((uint64_t)(x) << 8) & 0xff00000000ULL) | \
+ (((uint64_t)(x) >> 8) & 0xff000000ULL) | \
+ (((uint64_t)(x) >> 24) & 0xff0000ULL) | \
+ (((uint64_t)(x) >> 40) & 0xff00ULL) | \
+ (((uint64_t)(x) >> 56)) )
+
+ #define byteswap32(x) ( (((uint32_t)(x) << 24)) | \
+ (((uint32_t)(x) << 8) & 0xff0000U) | \
+ (((uint32_t)(x) >> 8) & 0xff00U) | \
+ (((uint32_t)(x) >> 24)) )
+
+ /* uint16_t type might be bigger than 2 bytes, so need to mask */
+ #define byteswap16(x) ( (((uint16_t)(x) & 0xff) << 8) | \
+ (((uint16_t)(x) >> 8) & 0xff) )
+#else
+ #define byteswap64(x) __builtin_bswap64(x)
+ #define byteswap32(x) __builtin_bswap64(x)
+ #define byteswap16(x) __builtin_bswap64(x)
+#endif
+
+#endif /* BITLOCK_H_ */
diff --git a/debian/rapmap/kseq.h b/debian/rapmap/kseq.h
new file mode 100644
index 0000000..b2238d1
--- /dev/null
+++ b/debian/rapmap/kseq.h
@@ -0,0 +1,235 @@
+/* The MIT License
+
+ Copyright (c) 2008, 2009, 2011 Attractive Chaos <attractor at live.co.uk>
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+/* Last Modified: 05MAR2012 */
+
+#ifndef AC_KSEQ_H
+#define AC_KSEQ_H
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define KS_SEP_SPACE 0 // isspace(): \t, \n, \v, \f, \r
+#define KS_SEP_TAB 1 // isspace() && !' '
+#define KS_SEP_LINE 2 // line separator: "\n" (Unix) or "\r\n" (Windows)
+#define KS_SEP_MAX 2
+
+#define __KS_TYPE(type_t) \
+ typedef struct __kstream_t { \
+ unsigned char *buf; \
+ int begin, end, is_eof; \
+ type_t f; \
+ } kstream_t;
+
+#define ks_eof(ks) ((ks)->is_eof && (ks)->begin >= (ks)->end)
+#define ks_rewind(ks) ((ks)->is_eof = (ks)->begin = (ks)->end = 0)
+
+#define __KS_BASIC(type_t, __bufsize) \
+ static inline kstream_t *ks_init(type_t f) \
+ { \
+ kstream_t *ks = (kstream_t*)calloc(1, sizeof(kstream_t)); \
+ ks->f = f; \
+ ks->buf = (unsigned char*)malloc(__bufsize); \
+ return ks; \
+ } \
+ static inline void ks_destroy(kstream_t *ks) \
+ { \
+ if (ks) { \
+ free(ks->buf); \
+ free(ks); \
+ } \
+ }
+
+#define __KS_GETC(__read, __bufsize) \
+ static inline int ks_getc(kstream_t *ks) \
+ { \
+ if (ks->is_eof && ks->begin >= ks->end) return -1; \
+ if (ks->begin >= ks->end) { \
+ ks->begin = 0; \
+ ks->end = __read(ks->f, ks->buf, __bufsize); \
+ if (ks->end == 0) { ks->is_eof = 1; return -1;} \
+ } \
+ return (int)ks->buf[ks->begin++]; \
+ }
+
+#ifndef KSTRING_T
+#define KSTRING_T kstring_t
+typedef struct __kstring_t {
+ size_t l, m;
+ char *s;
+} kstring_t;
+#endif
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#define __KS_GETUNTIL(__read, __bufsize) \
+ static int ks_getuntil2(kstream_t *ks, int delimiter, kstring_t *str, int *dret, int append) \
+ { \
+ int gotany = 0; \
+ if (dret) *dret = 0; \
+ str->l = append? str->l : 0; \
+ for (;;) { \
+ int i; \
+ if (ks->begin >= ks->end) { \
+ if (!ks->is_eof) { \
+ ks->begin = 0; \
+ ks->end = __read(ks->f, ks->buf, __bufsize); \
+ if (ks->end == 0) { ks->is_eof = 1; break; } \
+ } else break; \
+ } \
+ if (delimiter == KS_SEP_LINE) { \
+ for (i = ks->begin; i < ks->end; ++i) \
+ if (ks->buf[i] == '\n') break; \
+ } else if (delimiter > KS_SEP_MAX) { \
+ for (i = ks->begin; i < ks->end; ++i) \
+ if (ks->buf[i] == delimiter) break; \
+ } else if (delimiter == KS_SEP_SPACE) { \
+ for (i = ks->begin; i < ks->end; ++i) \
+ if (isspace(ks->buf[i])) break; \
+ } else if (delimiter == KS_SEP_TAB) { \
+ for (i = ks->begin; i < ks->end; ++i) \
+ if (isspace(ks->buf[i]) && ks->buf[i] != ' ') break; \
+ } else i = 0; /* never come to here! */ \
+ if (str->m - str->l < (size_t)(i - ks->begin + 1)) { \
+ str->m = str->l + (i - ks->begin) + 1; \
+ kroundup32(str->m); \
+ str->s = (char*)realloc(str->s, str->m); \
+ } \
+ gotany = 1; \
+ memcpy(str->s + str->l, ks->buf + ks->begin, i - ks->begin); \
+ str->l = str->l + (i - ks->begin); \
+ ks->begin = i + 1; \
+ if (i < ks->end) { \
+ if (dret) *dret = ks->buf[i]; \
+ break; \
+ } \
+ } \
+ if (!gotany && ks_eof(ks)) return -1; \
+ if (str->s == 0) { \
+ str->m = 1; \
+ str->s = (char*)calloc(1, 1); \
+ } else if (delimiter == KS_SEP_LINE && str->l > 1 && str->s[str->l-1] == '\r') --str->l; \
+ str->s[str->l] = '\0'; \
+ return str->l; \
+ } \
+ static inline int ks_getuntil(kstream_t *ks, int delimiter, kstring_t *str, int *dret) \
+ { return ks_getuntil2(ks, delimiter, str, dret, 0); }
+
+#define KSTREAM_INIT(type_t, __read, __bufsize) \
+ __KS_TYPE(type_t) \
+ __KS_BASIC(type_t, __bufsize) \
+ __KS_GETC(__read, __bufsize) \
+ __KS_GETUNTIL(__read, __bufsize)
+
+#define kseq_rewind(ks) ((ks)->last_char = (ks)->f->is_eof = (ks)->f->begin = (ks)->f->end = 0)
+
+#define __KSEQ_BASIC(SCOPE, type_t) \
+ SCOPE kseq_t *kseq_init(type_t fd) \
+ { \
+ kseq_t *s = (kseq_t*)calloc(1, sizeof(kseq_t)); \
+ s->f = ks_init(fd); \
+ return s; \
+ } \
+ SCOPE void kseq_destroy(kseq_t *ks) \
+ { \
+ if (!ks) return; \
+ free(ks->name.s); free(ks->comment.s); free(ks->seq.s); free(ks->qual.s); \
+ ks_destroy(ks->f); \
+ free(ks); \
+ }
+
+/* Return value:
+ >=0 length of the sequence (normal)
+ -1 end-of-file
+ -2 truncated quality string
+ */
+#define __KSEQ_READ(SCOPE) \
+ SCOPE int kseq_read(kseq_t *seq) \
+ { \
+ int c; \
+ kstream_t *ks = seq->f; \
+ if (seq->last_char == 0) { /* then jump to the next header line */ \
+ while ((c = ks_getc(ks)) != -1 && c != '>' && c != '@'); \
+ if (c == -1) return -1; /* end of file */ \
+ seq->last_char = c; \
+ } /* else: the first header char has been read in the previous call */ \
+ seq->comment.l = seq->seq.l = seq->qual.l = 0; /* reset all members */ \
+ if (ks_getuntil(ks, 0, &seq->name, &c) < 0) return -1; /* normal exit: EOF */ \
+ if (c != '\n') ks_getuntil(ks, KS_SEP_LINE, &seq->comment, 0); /* read FASTA/Q comment */ \
+ if (seq->seq.s == 0) { /* we can do this in the loop below, but that is slower */ \
+ seq->seq.m = 256; \
+ seq->seq.s = (char*)malloc(seq->seq.m); \
+ } \
+ while ((c = ks_getc(ks)) != -1 && c != '>' && c != '+' && c != '@') { \
+ if (c == '\n') continue; /* skip empty lines */ \
+ seq->seq.s[seq->seq.l++] = c; /* this is safe: we always have enough space for 1 char */ \
+ ks_getuntil2(ks, KS_SEP_LINE, &seq->seq, 0, 1); /* read the rest of the line */ \
+ } \
+ if (c == '>' || c == '@') seq->last_char = c; /* the first header char has been read */ \
+ if (seq->seq.l + 1 >= seq->seq.m) { /* seq->seq.s[seq->seq.l] below may be out of boundary */ \
+ seq->seq.m = seq->seq.l + 2; \
+ kroundup32(seq->seq.m); /* rounded to the next closest 2^k */ \
+ seq->seq.s = (char*)realloc(seq->seq.s, seq->seq.m); \
+ } \
+ seq->seq.s[seq->seq.l] = 0; /* null terminated string */ \
+ if (c != '+') return seq->seq.l; /* FASTA */ \
+ if (seq->qual.m < seq->seq.m) { /* allocate memory for qual in case insufficient */ \
+ seq->qual.m = seq->seq.m; \
+ seq->qual.s = (char*)realloc(seq->qual.s, seq->qual.m); \
+ } \
+ while ((c = ks_getc(ks)) != -1 && c != '\n'); /* skip the rest of '+' line */ \
+ if (c == -1) return -2; /* error: no quality string */ \
+ while (ks_getuntil2(ks, KS_SEP_LINE, &seq->qual, 0, 1) >= 0 && seq->qual.l < seq->seq.l); \
+ seq->last_char = 0; /* we have not come to the next header line */ \
+ if (seq->seq.l != seq->qual.l) return -2; /* error: qual string is of a different length */ \
+ return seq->seq.l; \
+ }
+
+#define __KSEQ_TYPE(type_t) \
+ typedef struct { \
+ kstring_t name, comment, seq, qual; \
+ int last_char; \
+ kstream_t *f; \
+ } kseq_t;
+
+#define KSEQ_INIT2(SCOPE, type_t, __read) \
+ KSTREAM_INIT(type_t, __read, 16384) \
+ __KSEQ_TYPE(type_t) \
+ __KSEQ_BASIC(SCOPE, type_t) \
+ __KSEQ_READ(SCOPE)
+
+#define KSEQ_INIT(type_t, __read) KSEQ_INIT2(static, type_t, __read)
+
+#define KSEQ_DECLARE(type_t) \
+ __KS_TYPE(type_t) \
+ __KSEQ_TYPE(type_t) \
+ extern kseq_t *kseq_init(type_t fd); \
+ void kseq_destroy(kseq_t *ks); \
+ int kseq_read(kseq_t *seq);
+
+#endif
diff --git a/debian/rapmap/macros.h b/debian/rapmap/macros.h
new file mode 100644
index 0000000..8a0853d
--- /dev/null
+++ b/debian/rapmap/macros.h
@@ -0,0 +1,59 @@
+/*
+ * Sux: Succinct data structures
+ *
+ * Copyright (C) 2007-2013 Sebastiano Vigna
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef ranksel_macros_h
+#define ranksel_macros_h
+
+#define ONES_STEP_4 ( 0x1111111111111111ULL )
+#define ONES_STEP_8 ( 0x0101010101010101ULL )
+#define ONES_STEP_9 ( 1ULL << 0 | 1ULL << 9 | 1ULL << 18 | 1ULL << 27 | 1ULL << 36 | 1ULL << 45 | 1ULL << 54 )
+#define ONES_STEP_16 ( 1ULL << 0 | 1ULL << 16 | 1ULL << 32 | 1ULL << 48 )
+#define MSBS_STEP_4 ( 0x8ULL * ONES_STEP_4 )
+#define MSBS_STEP_8 ( 0x80ULL * ONES_STEP_8 )
+#define MSBS_STEP_9 ( 0x100ULL * ONES_STEP_9 )
+#define MSBS_STEP_16 ( 0x8000ULL * ONES_STEP_16 )
+#define INCR_STEP_8 ( 0x80ULL << 56 | 0x40ULL << 48 | 0x20ULL << 40 | 0x10ULL << 32 | 0x8ULL << 24 | 0x4ULL << 16 | 0x2ULL << 8 | 0x1 )
+
+#define ONES_STEP_32 ( 0x0000000100000001ULL )
+#define MSBS_STEP_32 ( 0x8000000080000000ULL )
+
+#define COMPARE_STEP_8(x,y) ( ( ( ( ( (x) | MSBS_STEP_8 ) - ( (y) & ~MSBS_STEP_8 ) ) ^ (x) ^ ~(y) ) & MSBS_STEP_8 ) >> 7 )
+#define LEQ_STEP_8(x,y) ( ( ( ( ( (y) | MSBS_STEP_8 ) - ( (x) & ~MSBS_STEP_8 ) ) ^ (x) ^ (y) ) & MSBS_STEP_8 ) >> 7 )
+
+#define UCOMPARE_STEP_9(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_9 ) - ( (y) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_9 ) >> 8 )
+#define UCOMPARE_STEP_16(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_16 ) - ( (y) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_16 ) >> 15 )
+#define ULEQ_STEP_9(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_9 ) - ( (x) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_9 ) >> 8 )
+#define ULEQ_STEP_16(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_16 ) - ( (x) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_16 ) >> 15 )
+#define ZCOMPARE_STEP_8(x) ( ( ( x | ( ( x | MSBS_STEP_8 ) - ONES_STEP_8 ) ) & MSBS_STEP_8 ) >> 7 )
+
+#define EASY_LEQ_STEP_8(x,y) ( ( ( ( ( (y) | MSBS_STEP_8 ) - ( x ) ) ) & MSBS_STEP_8 ) >> 7 )
+#define EASY_LEQ_STEP_8_MSBS(x,y) ( ( ( ( (y) | MSBS_STEP_8 ) - ( x ) ) ) & MSBS_STEP_8 )
+
+__inline static int ceil_log2( const uint64_t x ) {
+ return x <= 2 ? x - 1 : 64 - __builtin_clzll( x - 1 );
+}
+
+__inline static int msb( const uint64_t x ) {
+ if ( x == 0 ) return -1;
+ return 63 - __builtin_clzll( x );
+}
+
+
+#endif
diff --git a/debian/rapmap/rank9b.cpp b/debian/rapmap/rank9b.cpp
new file mode 100644
index 0000000..58756a4
--- /dev/null
+++ b/debian/rapmap/rank9b.cpp
@@ -0,0 +1,67 @@
+/*
+ * Sux: Succinct data structures
+ *
+ * Copyright (C) 2007-2013 Sebastiano Vigna
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <cassert>
+#include <cstring>
+#include "rank9b.h"
+
+rank9b::rank9b() {}
+
+rank9b::rank9b( const uint64_t * const bits, const uint64_t num_bits ) {
+ this->bits = bits;
+ num_words = ( num_bits + 63 ) / 64;
+ num_counts = ( ( num_bits + 64 * 8 - 1 ) / ( 64 * 8 ) ) * 2;
+
+ // Init rank structure
+ counts = new uint64_t[ num_counts + 1 ];
+ memset( counts, 0, ( num_counts + 1 ) * sizeof *counts );
+
+ uint64_t c = 0;
+ uint64_t pos = 0;
+ for( uint64_t i = 0; i < num_words; i += 8, pos += 2 ) {
+ counts[ pos ] = c;
+ c += __builtin_popcountll( bits[ i ] );
+ for( int j = 1; j < 8; j++ ) {
+ counts[ pos + 1 ] |= ( c - counts[ pos ] ) << 63 - 9 * j;
+ if ( i + j < num_words ) c += __builtin_popcountll( bits[ i + j ] );
+ }
+ }
+
+ counts[ num_counts ] = c;
+
+ assert( c <= num_bits );
+}
+
+rank9b::~rank9b() {
+ delete [] counts;
+}
+
+
+uint64_t rank9b::rank( const uint64_t k ) {
+ const uint64_t word = k / 64;
+ const uint64_t block = word / 4 & ~1;
+ const int offset = word % 8;
+ return counts[ block ] + ( counts[ block + 1 ] >> ( 63 - offset * 9 ) & 0x1FF ) + __builtin_popcountll( bits[ word ] & ( ( 1ULL << k % 64 ) - 1 ) );
+}
+
+uint64_t rank9b::bit_count() {
+ return num_counts * 64;
+}
+
+void rank9b::print_counts() {}
diff --git a/debian/rapmap/rank9b.h b/debian/rapmap/rank9b.h
new file mode 100644
index 0000000..080d69a
--- /dev/null
+++ b/debian/rapmap/rank9b.h
@@ -0,0 +1,42 @@
+/*
+ * Sux: Succinct data structures
+ *
+ * Copyright (C) 2007-2013 Sebastiano Vigna
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef rank9b_h
+#define rank9b_h
+#include <stdint.h>
+#include "macros.h"
+
+class rank9b {
+private:
+ const uint64_t *bits;
+ uint64_t *counts, *inventory;
+ uint64_t num_words, num_counts, inventory_size, ones_per_inventory, log2_ones_per_inventory, num_ones;
+
+public:
+ rank9b();
+ rank9b( const uint64_t * const bits, const uint64_t num_bits );
+ ~rank9b();
+ uint64_t rank( const uint64_t pos );
+ // Just for analysis purposes
+ void print_counts();
+ uint64_t bit_count();
+};
+
+#endif
diff --git a/debian/rules b/debian/rules
index cf9d3a2..fd283e7 100755
--- a/debian/rules
+++ b/debian/rules
@@ -11,6 +11,28 @@ VERSION := $(shell echo '$(DEBVERS)' | sed -e 's/^[0-9]*://' -e 's/-.*//'
%:
dh $@ --with sphinxdoc --parallel
+override_dh_auto_configure:
+ cp -v debian/rapmap/RapMap* \
+ debian/rapmap/Boo???.hpp \
+ debian/rapmap/IndexHeader.hpp \
+ debian/rapmap/HitManager.hpp \
+ debian/rapmap/JFRaw.hpp \
+ debian/rapmap/SA*.hpp \
+ debian/rapmap/kseq.h \
+ debian/rapmap/SpinLock.hpp \
+ debian/rapmap/ScopedTimer.hpp \
+ debian/rapmap/bit_*.h \
+ debian/rapmap/rank9b.h \
+ debian/rapmap/macros.h \
+ include/
+ cp -v debian/rapmap/bit_array.c \
+ debian/rapmap/rank9b.cpp \
+ debian/rapmap/RapMapSA*.cpp \
+ debian/rapmap/RapMapFileSystem.cpp \
+ debian/rapmap/HitManager.cpp \
+ src/
+ dh_auto_configure
+
override_dh_auto_build:
dh_auto_build
mv doc/source/license.rst doc/ # unused
@@ -26,6 +48,23 @@ override_dh_auto_build:
override_dh_auto_clean:
dh_auto_clean
+ rm -f include/RapMap* \
+ include/Boo???.hpp \
+ include/IndexHeader.hpp \
+ include/HitManager.hpp \
+ include/JFRaw.hpp \
+ include/SA*.hpp \
+ include/kseq.h \
+ include/bit_*.h \
+ include/rank9b.h \
+ include/macros. h \
+ include/SpinLock.hpp \
+ include/ScopedTimer.hpp
+ rm -f src/bit_array.c \
+ src/rank9b.cpp \
+ src/RapMapSA*.cpp \
+ src/RapMapFileSystem.cpp \
+ src/HitManager.cpp
rm -f debian/*.1
rm -Rf sample_data
cd doc && $(MAKE) clean
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/salmon.git
More information about the debian-med-commit
mailing list