.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8ebd3e3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,68 @@
+
+
+We introduce eFFT, an efficient method for the calculation of the exact Fourier transform of an asynchronous event stream. It is based on keeping the matrices involved in the Radix-2 FFT algorithm in a tree data structure and updating them with the new events, extensively reusing computations, and avoiding unnecessary calculations while preserving exactness. eFFT can operate event-by-event, requiring for each event only a partial recalculation of the tree since most of the stored data are reused. It can also operate with event packets, using the tree structure to detect and avoid unnecessary and repeated calculations when integrating the different events within each packet to further reduce the number of operations.
+
+
+## ⚙️ Installation
+eFFT is provided as a header-only file for easy integration and relies solely on C++ standard and [Eigen3](https://eigen.tuxfamily.org/) libraries.
+
+> [!NOTE]
+> [FFTW3](https://fftw.org/) served as the benchmark for testing and evaluation. To enable it, define `EFFT_USE_FFTW3` during compilation (e.g., `-DEFFT_USE_FFTW3`).
+
+
+## 🖥️ Usage
+Here's a minimal working example:
+```cpp
+eFFT<128> efft; // Instance
+efft.initialize(); // Initialization
+
+Stimulus e(1, 1, true); // Event insertion
+efft.update(e); // Insert event
+
+efft.getFFT(); // Get result as Eigen matrix
+```
+
+And another example handling event packets:
+```cpp
+eFFT<128> efft; // Instance
+efft.initialize(); // Initialization
+
+Stimuli events;
+events.emplace_back(1, 1, true); // Insert event
+events.emplace_back(2, 2, true); // Insert event
+events.emplace_back(3, 3, false); // Extract event
+efft.update(events); // Insert event
+
+efft.getFFT(); // Get result as Eigen matrix
+```
+
+Please refer to the [official documentation](https://raultapia.github.io/efft/) for more details.
+
+## 📜 Citation
+
+If you use this work in an academic context, please cite the following publication:
+
+> R. Tapia, J.R. Martínez-de Dios, A. Ollero
+> **eFFT: An Event-based Method for the Efficient Computation of Exact Fourier Transforms**,
+> IEEE Transactions on Pattern Analysis and Machine Intelligence, 2024.
+
+```bibtex
+@article{tapia2022asap,
+ author={Tapia, R. and Martínez-de Dios, J.R. and Ollero, A.},
+ journal={IEEE Transactions on Pattern Analysis and Machine Intelligence},
+ title={{eFFT}: An Event-based Method for the Efficient Computation of Exact Fourier Transforms},
+ year={2024}
+}
+```
+
+## 📝 License
+
+Distributed under the GPLv3 License. See [`LICENSE`](https://github.com/raultapia/efft/tree/main/LICENSE) for more information.
+
+## 📬 Contact
+
+[Raul Tapia](https://raultapia.com) - raultapia@us.es
diff --git a/include/efft.hpp b/include/efft.hpp
new file mode 100644
index 0000000..7d65533
--- /dev/null
+++ b/include/efft.hpp
@@ -0,0 +1,411 @@
+/**
+ * @file efft.hpp
+ * @brief Event-based Fast Fourier transform
+ * @author Raul Tapia (raultapia.com)
+ * @copyright GNU General Public License v3.0
+ * @see https://github.com/raultapia/efft
+ * @note This is a header-only library
+ */
+#ifndef EFFT_HPP
+#define EFFT_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef EFFT_USE_FFTW3
+#include
+#endif
+
+class Stimulus {
+public:
+ unsigned int row{0};
+ unsigned int col{0};
+ bool state{true};
+ Stimulus() = default;
+ Stimulus(const unsigned int row, const unsigned int col) : row{row}, col{col} {};
+ Stimulus(const unsigned int row, const unsigned int col, const bool state) : row{row}, col{col}, state{state} {};
+ bool operator==(const Stimulus &p) const { return (row == p.row && col == p.col); }
+ bool operator!=(const Stimulus &p) const { return (row != p.row || col != p.col); }
+};
+
+class Stimuli : public std::vector {
+ using std::vector::vector;
+
+public:
+ /**
+ * @brief Filters out stimuli.
+ * @note This method is provided only for convenience.
+ */
+ void filter() {
+ std::sort(begin(), end(), [](const Stimulus &left, const Stimulus &right) {
+ if(left.row != right.row) {
+ return left.row < right.row;
+ }
+ if(left.col != right.col) {
+ return left.col < right.col;
+ }
+ return left.state && !right.state;
+ });
+ resize(std::unique(begin(), end(), [](const Stimulus &left, const Stimulus &right) { return (left.row == right.row && left.col == right.col); }) - begin());
+ }
+
+ /**
+ * @brief Sets the state of all stimuli in the container.
+ * @note This method is provided only for convenience.
+ */
+ void setState(const bool state) {
+ std::transform(begin(), end(), begin(), [state](Stimulus &p) { p.state = state; return p; });
+ }
+};
+
+using cfloat = std::complex;
+using cfloatmat = Eigen::Matrix;
+
+constexpr unsigned int LOG2(const unsigned int n) { return ((n < 2) ? 0 : 1 + LOG2(n >> 1U)); }
+
+#ifdef __has_builtin
+#if __has_builtin(__builtin_ffs)
+#define EFFT_HAS_BUILTIN_FFS
+#endif
+#endif
+#ifdef EFFT_HAS_BUILTIN_FFS
+inline unsigned int log2i(const unsigned int n) { return __builtin_ffs(static_cast(n)) - 1; }
+#else
+inline unsigned int log2i(const unsigned int n) { return static_cast(std::log2f(n)); }
+#endif
+
+template
+class eFFT {
+private:
+ static constexpr unsigned int LOG2_N = LOG2(N);
+ std::array, LOG2_N + 1> tree_;
+ std::array(N) * static_cast(N + 1)> twiddle_;
+#ifdef EFFT_USE_FFTW3
+ std::array(N) * static_cast(N)> fftwInput_;
+ std::array(N) * static_cast(N)> fftwOutput_;
+ fftw_plan plan_{nullptr};
+#endif
+
+public:
+ eFFT() {
+ constexpr float MINUS_TWO_PI = -2 * M_PI;
+ for(unsigned int i = 0; i < N; i++) {
+ for(unsigned int n = 0; n <= N; n++) {
+ twiddle_[i + N * n] = static_cast(std::polar(1.0F, MINUS_TWO_PI * static_cast(i) / static_cast(n)));
+ }
+ }
+ }
+
+ ~eFFT() {
+#ifdef EFFT_USE_FFTW3
+ if(plan_ != nullptr) {
+ fftw_destroy_plan(plan_);
+ }
+#endif
+ }
+
+ eFFT(const eFFT &) = default;
+ eFFT(eFFT &&) noexcept = default;
+ eFFT &operator=(const eFFT &) = default;
+ eFFT &operator=(eFFT &&) noexcept = default;
+
+ /**
+ * @brief Initializes the FFT computation with zero matrix.
+ */
+ void initialize() {
+ cfloatmat f0(cfloatmat::Zero(N, N));
+ initialize(f0);
+ }
+
+ /**
+ * @brief Initializes the FFT computation with the provided matrix.
+ *
+ * @param x Input matrix.
+ * @param offset Offset for the tree structure.
+ */
+ void initialize(cfloatmat &x, const int offset = 0) {
+ const unsigned int n = x.rows();
+ if(n == 1) {
+ tree_[0].emplace_back(x);
+ return;
+ }
+ const unsigned int ndiv2 = n >> 1U;
+ const unsigned int idx = log2i(ndiv2);
+
+ cfloatmat s00(x(Eigen::seq(Eigen::fix<0>, Eigen::last, Eigen::fix<2>), Eigen::seq(Eigen::fix<0>, Eigen::last, Eigen::fix<2>)));
+ cfloatmat s01(x(Eigen::seq(Eigen::fix<0>, Eigen::last, Eigen::fix<2>), Eigen::seq(Eigen::fix<1>, Eigen::last, Eigen::fix<2>)));
+ cfloatmat s10(x(Eigen::seq(Eigen::fix<1>, Eigen::last, Eigen::fix<2>), Eigen::seq(Eigen::fix<0>, Eigen::last, Eigen::fix<2>)));
+ cfloatmat s11(x(Eigen::seq(Eigen::fix<1>, Eigen::last, Eigen::fix<2>), Eigen::seq(Eigen::fix<1>, Eigen::last, Eigen::fix<2>)));
+ initialize(s00, 4 * offset);
+ initialize(s01, 4 * offset + 4);
+ initialize(s10, 4 * offset + 8);
+ initialize(s11, 4 * offset + 12);
+
+ const cfloatmat &x00 = tree_[idx][offset];
+ const cfloatmat &x01 = tree_[idx][offset + 1];
+ const cfloatmat &x10 = tree_[idx][offset + 2];
+ const cfloatmat &x11 = tree_[idx][offset + 3];
+ const unsigned int Nn = N * n;
+ for(unsigned int i = 0; i < ndiv2; i++) {
+ for(unsigned int j = 0; j < ndiv2; j++) {
+ const cfloat tu = twiddle_[j + Nn] * x01(i, j);
+ const cfloat td = twiddle_[(i + j) + Nn] * x11(i, j);
+ const cfloat ts = twiddle_[i + Nn] * x10(i, j);
+
+ const cfloat a = x00(i, j) + tu;
+ const cfloat b = x00(i, j) - tu;
+ const cfloat c = ts + td;
+ const cfloat d = ts - td;
+
+ x(i, j) = a + c;
+ x(i, j + ndiv2) = b + d;
+ x(i + ndiv2, j) = a - c;
+ x(i + ndiv2, j + ndiv2) = b - d;
+ }
+ }
+ tree_[log2i(n)].emplace_back(x);
+ }
+
+ /**
+ * @brief Updates the FFT with a single stimulus.
+ *
+ * @param p The stimulus to update.
+ * @return True if the update changed the FFT state, false otherwise.
+ */
+ bool update(const Stimulus &p) { return update(tree_[LOG2_N][0], p); }
+
+ /**
+ * @brief Updates the FFT with a single stimulus in the specified matrix.
+ *
+ * @param x The matrix to update.
+ * @param p The stimulus to update.
+ * @return True if the update changed the FFT state, false otherwise.
+ */
+ bool update(cfloatmat &x, const Stimulus &p, const unsigned int offset = 0) {
+ const unsigned int n = x.rows();
+ if(n == 1) {
+ return std::exchange(x(0, 0), p.state).real() != static_cast(p.state);
+ }
+ const unsigned int ndiv2 = n >> 1U;
+ const unsigned int nndiv2 = n * ndiv2;
+ const unsigned int idx = log2i(ndiv2);
+
+ bool changed = false;
+ if(static_cast(p.row & 1U)) {
+ if(static_cast(p.col & 1U)) {
+ changed = update(tree_[idx][offset + 3], {p.row >> 1U, p.col >> 1U, p.state}, 4 * offset + 12); // odd-odd
+ } else {
+ changed = update(tree_[idx][offset + 2], {p.row >> 1U, p.col >> 1U, p.state}, 4 * offset + 8); // odd-even
+ }
+ } else {
+ if(static_cast(p.col & 1U)) {
+ changed = update(tree_[idx][offset + 1], {p.row >> 1U, p.col >> 1U, p.state}, 4 * offset + 4); // even-odd
+ } else {
+ changed = update(tree_[idx][offset], {p.row >> 1U, p.col >> 1U, p.state}, 4 * offset); // even-even
+ }
+ }
+
+ if(changed) {
+ const cfloat *x00 = tree_[idx][offset].data();
+ const cfloat *x01 = tree_[idx][offset + 1].data();
+ const cfloat *x10 = tree_[idx][offset + 2].data();
+ const cfloat *x11 = tree_[idx][offset + 3].data();
+ cfloat *xp = x.data();
+ const unsigned int Nn = N * n;
+
+ for(unsigned int j = 0; j < ndiv2; j++) {
+ const unsigned int ndiv2j = ndiv2 * j, nj = n * j;
+ for(unsigned int i = 0; i < ndiv2; i++) {
+ const unsigned int k = i + ndiv2j, k1 = i + nj, k2 = k1 + ndiv2;
+
+ const cfloat tu = twiddle_[j + Nn] * x01[k];
+ const cfloat td = twiddle_[i + j + Nn] * x11[k];
+ const cfloat ts = twiddle_[i + Nn] * x10[k];
+
+ const cfloat x00_k = x00[k];
+ const cfloat a = x00_k + tu;
+ const cfloat b = x00_k - tu;
+ const cfloat c = ts + td;
+ const cfloat d = ts - td;
+
+ xp[k1] = a + c;
+ xp[k1 + nndiv2] = b + d;
+ xp[k2] = a - c;
+ xp[k2 + nndiv2] = b - d;
+ }
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * @brief Updates the FFT with multiple stimuli.
+ *
+ * @param pv The stimuli to update.
+ * @return True if the update changed the FFT state, false otherwise.
+ */
+ bool update(Stimuli &pv) { return update(tree_[LOG2_N][0], pv.begin(), pv.end()); }
+
+ /**
+ * @brief Updates the FFT with multiple stimuli in the specified matrix.
+ *
+ * @param x The matrix to update.
+ * @param b0 Iterator pointing to the begining of the stimuli vector.
+ * @param e0 Iterator pointing to the end of the stimuli vector.
+ * @return True if the update changed the FFT state, false otherwise.
+ */
+ bool update(cfloatmat &x, Stimuli::iterator b0, Stimuli::iterator e0, const unsigned int offset = 0) {
+ const unsigned int n = x.rows();
+ if(n == 1) {
+ if(e0 - b0 == 1) {
+ return std::exchange(x(0, 0), b0->state).real() != static_cast(b0->state);
+ }
+ const bool state = std::any_of(b0, e0, [](const Stimulus &p) { return p.state; });
+ return std::exchange(x(0, 0), state).real() != static_cast(state);
+ }
+ const unsigned int ndiv2 = n >> 1U;
+ const unsigned int nndiv2 = n * ndiv2;
+ const unsigned int idx = log2i(ndiv2);
+
+ Stimuli::iterator e1, e2, e3;
+ e2 = std::partition(b0, e0, [](const Stimulus &p) { return p.row & 1U; });
+ e1 = std::partition(b0, e2, [](const Stimulus &p) { return p.col & 1U; });
+ e3 = std::partition(e2, e0, [](const Stimulus &p) { return p.col & 1U; });
+ std::transform(b0, e1, b0, [](const Stimulus &p) { return Stimulus{p.row >> 1U, p.col >> 1U, p.state}; });
+ std::transform(e1, e2, e1, [](const Stimulus &p) { return Stimulus{p.row >> 1U, p.col >> 1U, p.state}; });
+ std::transform(e2, e3, e2, [](const Stimulus &p) { return Stimulus{p.row >> 1U, p.col >> 1U, p.state}; });
+ std::transform(e3, e0, e3, [](const Stimulus &p) { return Stimulus{p.row >> 1U, p.col >> 1U, p.state}; });
+
+ bool changed = false;
+ if(b0 != e1) {
+ changed = update(tree_[idx][offset + 3], b0, e1, 4 * offset + 12) || changed; // odd-odd
+ }
+ if(e1 != e2) {
+ changed = update(tree_[idx][offset + 2], e1, e2, 4 * offset + 8) || changed; // odd-even
+ }
+ if(e2 != e3) {
+ changed = update(tree_[idx][offset + 1], e2, e3, 4 * offset + 4) || changed; // even-odd
+ }
+ if(e3 != e0) {
+ changed = update(tree_[idx][offset], e3, e0, 4 * offset) || changed; // even-even
+ }
+
+ if(changed) {
+ const cfloat *x00 = tree_[idx][offset].data();
+ const cfloat *x01 = tree_[idx][offset + 1].data();
+ const cfloat *x10 = tree_[idx][offset + 2].data();
+ const cfloat *x11 = tree_[idx][offset + 3].data();
+ cfloat *xp = x.data();
+ const unsigned int Nn = N * n;
+
+ for(unsigned int j = 0; j < ndiv2; j++) {
+ const unsigned int ndiv2j = ndiv2 * j, nj = n * j;
+ for(unsigned int i = 0; i < ndiv2; i++) {
+ const unsigned int k = i + ndiv2j, k1 = i + nj, k2 = k1 + ndiv2;
+
+ const cfloat tu = twiddle_[j + Nn] * x01[k];
+ const cfloat td = twiddle_[i + j + Nn] * x11[k];
+ const cfloat ts = twiddle_[i + Nn] * x10[k];
+
+ const cfloat x00_k = x00[k];
+ const cfloat a = x00_k + tu;
+ const cfloat b = x00_k - tu;
+ const cfloat c = ts + td;
+ const cfloat d = ts - td;
+
+ xp[k1] = a + c;
+ xp[k1 + nndiv2] = b + d;
+ xp[k2] = a - c;
+ xp[k2 + nndiv2] = b - d;
+ }
+ }
+ }
+ return changed;
+ }
+
+#ifdef EFFT_USE_FFTW3
+ /**
+ * @brief Initialize the FFT ground truth (FFTW) using the given image.
+ *
+ * @param image A complex float matrix to initialize the FFT input. Defaults to a zero matrix.
+ */
+ void initializeGroundTruth(const cfloatmat &image = cfloatmat::Zero(N, N)) {
+ plan_ = fftw_plan_dft_2d(N, N, fftwInput_.data(), fftwOutput_.data(), FFTW_FORWARD, FFTW_ESTIMATE | FFTW_UNALIGNED);
+ for(Eigen::Index i = 0; i < image.rows(); i++) {
+ for(Eigen::Index j = 0; j < image.cols(); j++) {
+ fftwInput_[N * i + j][0] = image(i, j).real();
+ fftwInput_[N * i + j][1] = image(i, j).imag();
+ }
+ }
+ fftw_execute(plan_);
+ }
+
+ /**
+ * @brief Update the FFT ground truth (FFTW) with a single stimulus.
+ *
+ * @param p The stimulus to update.
+ */
+ void updateGroundTruth(const Stimulus &p) {
+ fftwInput_[N * p.row + p.col][0] = static_cast(p.state);
+ fftwInput_[N * p.row + p.col][1] = 0;
+ fftw_execute(plan_);
+ }
+
+ /**
+ * @brief Update the FFT ground truth (FFTW) with multiple stimuli.
+ *
+ * @param pv The stimuli to update.
+ */
+ void updateGroundTruth(const Stimuli &pv) {
+ std::set> activated;
+ for(const Stimulus &p : pv) {
+ if(p.state) {
+ activated.insert({p.row, p.col});
+ } else if(activated.find({p.row, p.col}) != activated.end()) {
+ continue;
+ }
+ fftwInput_[N * p.row + p.col][0] = static_cast(p.state);
+ fftwInput_[N * p.row + p.col][1] = 0;
+ }
+ fftw_execute(plan_);
+ }
+#endif
+
+ /**
+ * @brief Get the FFT result as an Eigen matrix of complex floats.
+ *
+ * @return The FFT result.
+ */
+ [[nodiscard]] inline Eigen::Matrix getFFT() const {
+ return tree_[LOG2_N][0];
+ }
+
+#ifdef EFFT_USE_FFTW3
+ /**
+ * @brief Get the FFT ground truth (FFTW) result as an Eigen matrix of complex floats.
+ *
+ * @return The ground truth FFT result.
+ */
+ [[nodiscard]] inline Eigen::Matrix getGroundTruthFFT() const {
+ return Eigen::Map, N, N, Eigen::RowMajor>>(reinterpret_cast *>(fftwOutput_.data())).template cast();
+ }
+
+ /**
+ * @brief Check the difference between the computed FFT and the ground truth FFT (FFTW).
+ *
+ * @return The norm of the difference between the computed FFT and the ground truth FFT.
+ */
+ [[nodiscard]] inline double check() const {
+ return (getFFT() - getGroundTruthFFT()).norm();
+ }
+#endif
+};
+
+#endif // EFFT_HPP
diff --git a/tests/efft.cpp b/tests/efft.cpp
new file mode 100644
index 0000000..3a84050
--- /dev/null
+++ b/tests/efft.cpp
@@ -0,0 +1,208 @@
+#include "efft.hpp"
+#include
+#include
+
+constexpr unsigned int NTEST = 25;
+
+template
+class RandEventGenerator {
+public:
+ RandEventGenerator() : gen(std::random_device{}()), dis(0, N - 1) {}
+ Stimulus next() {
+ return {dis(gen) % N, dis(gen) % N, dis(gen) % 2 == 0};
+ }
+ Stimulus next(const bool state) {
+ return {dis(gen) % N, dis(gen) % N, state};
+ }
+ Stimuli next(unsigned int n) {
+ Stimuli ret;
+ while(static_cast(n--)) {
+ ret.emplace_back(next());
+ }
+ return ret;
+ }
+ Stimuli next(unsigned int n, const bool state) {
+ Stimuli ret;
+ while(static_cast(n--)) {
+ ret.emplace_back(next(state));
+ }
+ return ret;
+ }
+
+private:
+ std::mt19937 gen;
+ std::uniform_int_distribution<> dis;
+};
+
+TEST(StimulusTest, Equality) {
+ const Stimulus s1(123, 456, false);
+ const Stimulus s2(123, 456, false);
+ const Stimulus s3(123, 456, true);
+ const Stimulus s4(123, 654, false);
+
+ ASSERT_EQ(s1, s2);
+ ASSERT_EQ(s1, s3);
+ ASSERT_NE(s1, s4);
+}
+
+TEST(StimuliTest, Filter) {
+ Stimuli ss;
+ ss.emplace_back(23, 45);
+ ss.emplace_back(23, 45);
+ ss.emplace_back(14, 45);
+ ss.emplace_back(23, 33);
+ ss.emplace_back(231, 451, true);
+ ss.emplace_back(231, 451, false);
+ ss.emplace_back(141, 451, true);
+ ss.emplace_back(231, 331, false);
+
+ ASSERT_EQ(ss.size(), 4 + 4);
+ ss.filter();
+ ASSERT_EQ(ss.size(), 3 + 3);
+}
+
+TEST(StimuliTest, State) {
+ Stimuli ss;
+ ss.emplace_back(231, 451, true);
+ ss.emplace_back(231, 451, false);
+ ss.emplace_back(141, 451, true);
+ ss.emplace_back(231, 331, false);
+
+ ss.setState(true);
+ for(const Stimulus s : ss) {
+ ASSERT_EQ(s.state, true);
+ }
+
+ ss.setState(false);
+ for(const Stimulus s : ss) {
+ ASSERT_EQ(s.state, false);
+ }
+}
+
+#ifdef EFFT_USE_FFTW3
+class eFFTTest : public ::testing::TestWithParam {
+};
+
+template
+void FeedWithEvents() {
+ eFFT efft;
+ RandEventGenerator rand;
+
+ Stimulus s;
+ for(unsigned int test = 0; test < NTEST; test++) {
+ if(!test) {
+ efft.initialize();
+ efft.initializeGroundTruth();
+ } else {
+ efft.update(s);
+ efft.updateGroundTruth(s);
+ }
+ ASSERT_LT(efft.check(), 0.001);
+ s = rand.next();
+ }
+}
+TEST(eFFTTest, FeedWithEvents) {
+ FeedWithEvents<4>();
+ FeedWithEvents<8>();
+ FeedWithEvents<16>();
+ FeedWithEvents<32>();
+ FeedWithEvents<64>();
+ FeedWithEvents<128>();
+ FeedWithEvents<256>();
+}
+
+template
+void FeedWithTheSameEvent() {
+ eFFT efft;
+ RandEventGenerator rand;
+ const Stimulus s = rand.next(true);
+
+ for(unsigned int test = 0; test < NTEST; test++) {
+ if(!test) {
+ efft.initialize();
+ efft.initializeGroundTruth();
+ } else {
+ ASSERT_EQ(efft.update(s), test == 1);
+ efft.updateGroundTruth(s);
+ }
+ ASSERT_LT(efft.check(), 0.001);
+ }
+}
+TEST(eFFTTest, FeedWithTheSameEvent) {
+ FeedWithTheSameEvent<4>();
+ FeedWithTheSameEvent<8>();
+ FeedWithTheSameEvent<16>();
+ FeedWithTheSameEvent<32>();
+ FeedWithTheSameEvent<64>();
+ FeedWithTheSameEvent<128>();
+ FeedWithTheSameEvent<256>();
+}
+
+template
+void FeedWithPackets(const unsigned int PACKET_SIZE) {
+ eFFT efft;
+ RandEventGenerator rand;
+
+ Stimuli ss;
+ for(unsigned int test = 0; test < NTEST; test++) {
+ if(!test) {
+ efft.initializeGroundTruth();
+ efft.initialize();
+ } else {
+ efft.updateGroundTruth(ss);
+ efft.update(ss);
+ }
+
+ ASSERT_LT(efft.check(), 0.1);
+ ss = rand.next(PACKET_SIZE);
+ }
+}
+TEST_P(eFFTTest, FeedWithPackets) {
+ const unsigned int p = GetParam();
+ FeedWithPackets<4>(p);
+ FeedWithPackets<8>(p);
+ FeedWithPackets<16>(p);
+ FeedWithPackets<32>(p);
+ FeedWithPackets<64>(p);
+ FeedWithPackets<128>(p);
+ FeedWithPackets<256>(p);
+}
+
+template
+void FeedWithTheSamePacket(const unsigned int PACKET_SIZE) {
+ eFFT efft;
+ RandEventGenerator rand;
+ const Stimuli ss = rand.next(PACKET_SIZE, true);
+
+ for(unsigned int test = 0; test < NTEST; test++) {
+ Stimuli aux;
+ aux.assign(ss.begin(), ss.end());
+ if(!test) {
+ efft.initializeGroundTruth();
+ efft.initialize();
+ } else {
+ efft.updateGroundTruth(aux);
+ ASSERT_EQ(efft.update(aux), test == 1);
+ }
+
+ ASSERT_LT(efft.check(), 0.1);
+ }
+}
+TEST_P(eFFTTest, FeedWithTheSamePacket) {
+ const unsigned int p = GetParam();
+ FeedWithTheSamePacket<4>(p);
+ FeedWithTheSamePacket<8>(p);
+ FeedWithTheSamePacket<16>(p);
+ FeedWithTheSamePacket<32>(p);
+ FeedWithTheSamePacket<64>(p);
+ FeedWithTheSamePacket<128>(p);
+ FeedWithTheSamePacket<256>(p);
+}
+
+INSTANTIATE_TEST_CASE_P(eFFTWithPackets, eFFTTest, ::testing::Values(1, 10, 100, 1000, 10000));
+#endif
+
+int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}