From a392ca1aa99735783dcda8154e9611ddf6508572 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Mon, 21 Sep 2020 14:33:52 +1000 Subject: [PATCH] set: add a simple static-alloc, dynamic-resize, set --- CMakeLists.txt | 3 + set/dset.cpp | 9 ++ set/dset.hpp | 207 ++++++++++++++++++++++++++++++++++++++++++++++ test/set/dset.cpp | 30 +++++++ 4 files changed, 249 insertions(+) create mode 100644 set/dset.cpp create mode 100644 set/dset.hpp create mode 100644 test/set/dset.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 205283e2..a4a85f8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -505,6 +505,8 @@ list ( registrar.hpp roots/bisection.hpp scoped.hpp + set/dset.cpp + set/dset.hpp signal.cpp signal.hpp singleton.hpp @@ -725,6 +727,7 @@ if (TESTS) registrar roots/bisection scoped + set/dset signal singleton stream diff --git a/set/dset.cpp b/set/dset.cpp new file mode 100644 index 00000000..89fdedbc --- /dev/null +++ b/set/dset.cpp @@ -0,0 +1,9 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2020, Danny Robson + */ + +#include "./dset.hpp" \ No newline at end of file diff --git a/set/dset.hpp b/set/dset.hpp new file mode 100644 index 00000000..fea61085 --- /dev/null +++ b/set/dset.hpp @@ -0,0 +1,207 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2020, Danny Robson + */ + +#pragma once + +#include +#include +#include + +#include + + +namespace cruft::set { + /// A multiset container whose storage is a constant capacity fixed at + /// compile time. + template < + typename ValueT, + std::size_t MaxV + > + struct dset { + public: + using value_type = ValueT; + using pointer = value_type*; + using const_pointer = value_type const*; + using reference = value_type&; + using const_reference = value_type const&; + + using iterator = pointer; + using const_iterator = const_pointer; + + using size_type = std::size_t; + + dset () = default; + dset (std::initializer_list _init) + { + std::copy (_init.begin (), _init.end (), m_data.begin ()); + m_size = _init.size (); + } + + decltype(auto) begin (void) const& { return m_data.data (); } + decltype(auto) end (void) const& { return begin () + m_size; } + + void clear (void); + + /// A list of items to add to this set. + /// + /// Items must be in sorted order. + void add (std::span); + + /// Add a number of copies of a specified item to the set. + void add (std::size_t count, ValueT const &val) + { + if (capacity () - size () < count) + throw std::bad_alloc (); + + auto pos = std::lower_bound (m_data.begin (), m_data.begin () + m_size, val); + std::copy_backward (pos, m_data.begin () + m_size, m_data.begin () + m_size + count); + std::fill_n (pos, count, val); + m_size += count; + } + + /// A list of items to remove from this store. + /// + /// Items must be in sorted order, and must be a subset of this + /// container. + void erase (std::span); + + /// Tests if the list is a subset of this container. + /// + /// The list must be sorted. + bool contains (std::span rhs) const + { + return std::includes (begin (), end (), rhs.begin (), rhs.end ()); + } + + /// Tests if this store contains at least the items in the + /// supplied store. + bool contains (dset const &rhs) const + { + return contains (rhs.m_data); + } + + std::size_t count (ValueT const &val) const + { + auto pos = std::find (begin (), end (), val); + + std::size_t tally = 0; + for ( ; pos != end () && *pos == val; ++pos) + ++tally; + + return tally; + } + + dset operator+ (std::span rhs) const + { + if (remain () < rhs.size ()) + throw std::bad_alloc (); + + dset res; + auto const pos = std::merge ( + begin (), + end (), + rhs.begin (), + rhs.end (), + res.m_data.begin () + ); + res.m_size = std::distance (res.m_data.begin (), pos); + return res; + } + + dset operator- (std::span rhs) const + { + dset res; + auto const pos = std::set_difference ( + begin (), + end (), + rhs.begin (), + rhs.end (), + res.m_data.begin () + ); + + res.m_size = std::distance (res.m_data.begin (), pos); + return res; + } + + dset& operator+= (std::span) &; + dset& operator-= (std::span) &; + + dset operator+ (dset const &rhs) const + { + return *this + std::span (rhs.m_data.data (), rhs.m_size); + } + + dset operator- (dset const &rhs) const + { + return *this - std::span (rhs.m_data.data (), rhs.m_size); + } + + + dset& operator+= (dset const &rhs) & + { + // TODO: Optimise me. + return *this = *this + std::span (rhs.m_data.data (), rhs.m_size); + } + + dset& operator-= (dset const &rhs) & + { + // TODO: Optimise me. + return *this = *this - rhs; + } + + + dset + operator& (dset const &rhs) const + { + dset res; + + auto const pos = std::set_intersection ( + begin (), + end (), + rhs.begin (), + rhs.end (), + res.m_data.begin () + ); + + res.m_size = std::distance (res.m_data.begin (), pos); + + return res; + } + + dset& operator&= (dset const &rhs) &; + + bool operator== (dset const &rhs) const + { + return std::equal (begin (), end (), rhs.begin (), rhs.end ()); + } + + bool operator!= (dset const &rhs) const + { + return !(*this == rhs); + } + + bool empty (void) const + { + return m_size == 0; + } + + std::size_t size (void) const { return m_size; } + + std::size_t + capacity (void) const + { + return MaxV; + } + + std::size_t remain (void) const { return capacity () - size (); } + + private: + std::size_t m_size = 0; + std::array m_data; + }; +} \ No newline at end of file diff --git a/test/set/dset.cpp b/test/set/dset.cpp new file mode 100644 index 00000000..fe17d8e2 --- /dev/null +++ b/test/set/dset.cpp @@ -0,0 +1,30 @@ +#include "set/dset.hpp" +#include "tap.hpp" + + +using cruft::set::dset; + + +int main () +{ + + cruft::TAP::logger tap; + + { + dset const a { 1, 2, 3, 5, }; + dset const b { 2, 4, 5, }; + dset const c { 2, 5, }; + + tap.expect_eq (a & b, c, "set intersection"); + } + + { + dset val { 3, 4, 5 }; + val.add (2, 4); + dset const expected { 3, 4, 4, 4, 5 }; + + tap.expect_eq (val, expected, "set add_n"); + } + + return tap.status (); +} \ No newline at end of file