#include <boost/bloom.hpp>
#include <cassert>
#include <cmath>
#include <memory>
#include <vector>
#include <iostream>
template<
typename T, std::size_t K,
typename Subfilter = boost::bloom::block<unsigned char,1>,
std::size_t Stride = 0,
typename Hash = boost::hash<T>,
typename Allocator=std::allocator<unsigned char>
>
class rolling_filter
{
public:
rolling_filter(std::size_t w_, std::size_t n, double max_fpr):
w(w_), fs(n)
{
assert(w > 0);
assert(n >= 2);
assert(max_fpr >= 0.0 && max_fpr <= 1.0);
auto sub_fpr = 1.0 - std::pow(1.0 - max_fpr, 1.0 / n);
auto m = filter_type::capacity_for(w, sub_fpr);
for(auto& f: fs) f.reset(m);
}
std::size_t min_size() const
{
return w * (fs.size() - 1);
}
std::size_t max_size() const
{
return w * fs.size();
}
double min_fpr() const
{
double sub_fpr = filter_type::fpr_for(w, fs[0].capacity());
return 1.0 - std::pow(1.0 - sub_fpr, (double)fs.size() - 1.0);
}
double max_fpr() const
{
double sub_fpr = filter_type::fpr_for(w, fs[0].capacity());
return 1.0 - std::pow(1.0 - sub_fpr, (double)fs.size());
}
std::size_t capacity() const
{
return fs[0].capacity() * fs.size();
}
void insert(const T& x)
{
if(++count > w) {
count = 1;
if(++i >= fs.size()) i = 0;
fs[i].clear();
}
fs[i].insert(x);
}
bool may_contain(const T& x) const
{
for(const auto& f: fs) {
if(f.may_contain(x)) return true;
}
return false;
}
private:
using filter_type = boost::bloom::filter<
T, K, Subfilter, Stride, Hash, Allocator
>;
using vector_type = std::vector<
filter_type,
typename std::allocator_traits<Allocator>::
template rebind_alloc<filter_type>
>;
std::size_t w;
vector_type fs;
std::size_t count = 0,
i = 0;
};
int main()
{
const std::size_t window_size = 1000;
const std::size_t num_windows = 10;
const double max_fpr = 0.01;
rolling_filter<std::size_t, 5> rf(window_size, num_windows, max_fpr);
std::cout << "rolling filter capacity: " << rf.capacity() << " bits\n";
const std::size_t num_elements = rf.max_size() * 10 + window_size / 2;
for(std::size_t i = 0 ; i < num_elements; ++i) rf.insert(i);
std::size_t count = 0;
for(std::size_t i = 0 ; i < num_elements - rf.max_size(); ++i) {
count += rf.may_contain(i);
}
std::cout << "measured fpr: "
<< (double)count / (num_elements - rf.max_size())
<< " (should be between " << rf.min_fpr()
<< " and " << rf.max_fpr() << ")\n";
count = 0;
for(std::size_t i = num_elements - rf.max_size() ; i < num_elements; ++i) {
count += rf.may_contain(i);
}
std::cout << "elements found: " << count
<< " (must be between " << rf.min_size()
<< " and " << rf.max_size() << ")\n";
}