diff --git a/README.md b/README.md index 0eb77c0..56ae9aa 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,10 @@ fwd(out, history(another_value)); A [Digital delay line](https://en.wikipedia.org/wiki/Digital_delay_line) can be used to align signals with different delays - after filtering, for example. +### [deque.hh](deque.hh) + +A [ring buffer](https://en.wikipedia.org/wiki/Circular_buffer) based, fixed-size [double-ended queue](https://en.wikipedia.org/wiki/Double-ended_queue). + ### [calculus.hh](calculus.hh) Some [calculus](https://en.wikipedia.org/wiki/Calculus) functions: diff --git a/deque.hh b/deque.hh new file mode 100644 index 0000000..8f08345 --- /dev/null +++ b/deque.hh @@ -0,0 +1,81 @@ +/* +Double-ended queue + +Copyright 2020 Ahmet Inan +*/ + +#pragma once + +#include + +namespace DSP { + +template +class Deque +{ + TYPE buf[SIZE]; + int head, tail, count; +public: + Deque() : head(SIZE-1), tail(0), count(0) + { + } + void push_back(TYPE input) + { + assert(count < SIZE); + ++count; + if (--tail < 0) + tail = SIZE-1; + buf[tail] = input; + } + void push_front(TYPE input) + { + assert(count < SIZE); + ++count; + if (++head >= SIZE) + head = 0; + buf[head] = input; + } + void pop_back() + { + assert(count > 0); + --count; + if (++tail >= SIZE) + tail = 0; + } + void pop_front() + { + assert(count > 0); + --count; + if (--head < 0) + head = SIZE-1; + } + TYPE back() + { + assert(count > 0); + return buf[tail]; + } + TYPE front() + { + assert(count > 0); + return buf[head]; + } + bool empty() + { + return count == 0; + } + bool full() + { + return count == SIZE; + } + int size() + { + return count; + } + int max_size() + { + return SIZE; + } +}; + +} + diff --git a/tests/deque_test.cc b/tests/deque_test.cc new file mode 100644 index 0000000..991c9ca --- /dev/null +++ b/tests/deque_test.cc @@ -0,0 +1,98 @@ +/* +Test for the double-ended queue + +Copyright 2020 Ahmet Inan +*/ + +#include +#include +#include +#include +#include "deque.hh" + +template +void test() +{ + std::deque ref; + DSP::Deque dut; + std::random_device ran; + std::default_random_engine gen(ran()); + typedef std::uniform_int_distribution dis; + for (int loop = 0; loop < 1000000; ++loop) { + assert((int)ref.size() == dut.size()); + int operation = dis(0, 5)(gen); + switch (operation) { + case 0: + assert(ref.empty() == dut.empty()); + if (!dut.empty()) + assert(ref.front() == dut.front()); + break; + case 1: + assert(ref.empty() == dut.empty()); + if (!dut.empty()) + assert(ref.back() == dut.back()); + break; + case 2: + for (int n = dis(0, dut.size())(gen), i = 0; i < n; ++i) { + ref.pop_front(); + dut.pop_front(); + } + break; + case 3: + for (int n = dis(0, dut.size())(gen), i = 0; i < n; ++i) { + ref.pop_back(); + dut.pop_back(); + } + break; + case 4: + for (int n = dis(0, dut.max_size() - dut.size())(gen), i = 0; i < n; ++i) { + int val = gen(); + ref.push_front(val); + dut.push_front(val); + } + break; + case 5: + for (int n = dis(0, dut.max_size() - dut.size())(gen), i = 0; i < n; ++i) { + int val = gen(); + ref.push_back(val); + dut.push_back(val); + } + break; + } + } +} + +int main() +{ + DSP::Deque dut; + assert(dut.empty()); + assert(!dut.full()); + assert(dut.size() == 0); + assert(dut.max_size() == 1); + dut.push_front(42); + assert(dut.full()); + assert(dut.size() == 1); + assert(dut.front() == 42); + assert(dut.back() == 42); + dut.pop_back(); + assert(dut.empty()); + dut.push_back(42); + assert(dut.full()); + assert(dut.size() == 1); + assert(dut.front() == 42); + assert(dut.back() == 42); + dut.pop_front(); + test<1>(); + test<2>(); + test<3>(); + test<4>(); + test<5>(); + test<6>(); + test<7>(); + test<8>(); + test<42>(); + test<123>(); + std::cerr << "Double-ended queue test passed!" << std::endl; + return 0; +} +