#include <array>
#include <boost/bloom/filter.hpp>
#include <boost/bloom/fast_multiblock32.hpp>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string_view>
template<std::size_t K>
struct k_mer
{
static_assert(
K >= 0 &&
2 * K <= sizeof(std::uint64_t) * CHAR_BIT);
static constexpr std::size_t size()
{
return K;
}
void reset()
{
data = 0;
}
k_mer& operator+=(char n)
{
static constexpr std::uint64_t mask=
(((std::uint64_t)1) << (2 * size())) - 1;
data <<= 2;
data &= mask;
data |= table[(unsigned char)n];
return *this;
}
std::uint64_t data = 0;
using table_type=std::array<unsigned char, UCHAR_MAX>;
static constexpr table_type table = [] {
table_type table{};
table['A'] = table['a'] = 0;
table['C'] = table['c'] = 1;
table['G'] = table['g'] = 2;
table['T'] = table['t'] = 3;
return table;
}();
};
template<std::size_t N>
std::size_t hash_value(const k_mer<N>& km)
{
if constexpr (sizeof(std::size_t) >= sizeof(std::uint64_t)) {
return (std::size_t)km.data;
}
else{
return (std::size_t)(km.data ^ (km.data >> 32));
}
}
using genome_filter = boost::bloom::filter<
k_mer<20>,
1, boost::bloom::fast_multiblock32<8> >;
genome_filter make_genome_filter(const char* filename)
{
using k_mer = genome_filter::value_type;
std::ifstream in(filename, std::ios::ate);
if(!in) throw std::runtime_error("can't open file");
genome_filter f((std::size_t)in.tellg(), 0.01);
in.seekg(0);
std::string line;
std::size_t width = 0;
k_mer km;
while(std::getline(in, line)) {
if(line.empty()) continue;
if(line[0] == '>') {
width = 0;
km.reset();
continue;
}
std::size_t i = 0;
for(; width< km.size() - 1 && i < line.size(); ++i) {
km += line[i];
++width;
}
for(; i < line.size(); ++i) {
km += line[i];
f.insert(km);
}
}
return f;
}
bool may_contain(const genome_filter& f, std::string_view seq)
{
using k_mer = genome_filter::value_type;
assert(seq.size() >= k_mer::size());
k_mer km;
auto first = seq.begin(), last = seq.end();
for(std::size_t i = 0; i < km.size() - 1; ++i) km += *first++;
do{
km += *first++;
if(!f.may_contain(km)) return false;
}while(first != last);
return true;
}
int main()
{
try{
auto f=make_genome_filter(
"GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna");
const char* seqs[] = {
"ataaataagattgCGACTCAAAATTAAgcaataacac",
"attatagggagaaatatgatcgcgtatgcgagagtagtgccaacatattgtgctc",
"agaATTTACTAAGTACTTCTATGAATGGAATTATTATTGGAAACTCTACAA",
"ATTTACTAAGTACTTCTATCTGCAAATTAACAATTTATCAAACAACTG",
"ataaataagattgCGACTCAAAAGTAAgcaat"
};
int i = 0;
for(auto seq: seqs){
std::cout << "check sequence " << i++ << ": "
<< may_contain(f, seq) << "\n";
}
}
catch(const std::exception& e) {
std::cerr << e.what() << "\n";
return EXIT_FAILURE;
}
}