about summary refs log tree commit diff
path: root/immer/set.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'immer/set.hpp')
-rw-r--r--immer/set.hpp198
1 files changed, 198 insertions, 0 deletions
diff --git a/immer/set.hpp b/immer/set.hpp
new file mode 100644
index 000000000000..a152ac955334
--- /dev/null
+++ b/immer/set.hpp
@@ -0,0 +1,198 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#pragma once
+
+#include <immer/detail/hamts/champ.hpp>
+#include <immer/detail/hamts/champ_iterator.hpp>
+#include <immer/memory_policy.hpp>
+
+#include <functional>
+
+namespace immer {
+
+template <typename T,
+          typename Hash,
+          typename Equal,
+          typename MemoryPolicy,
+          detail::hamts::bits_t B>
+class set_transient;
+
+/*!
+ * Immutable set representing an unordered bag of values.
+ *
+ * @tparam T    The type of the values to be stored in the container.
+ * @tparam Hash The type of a function object capable of hashing
+ *              values of type `T`.
+ * @tparam Equal The type of a function object capable of comparing
+ *              values of type `T`.
+ * @tparam MemoryPolicy Memory management policy. See @ref
+ *              memory_policy.
+ *
+ * @rst
+ *
+ * This container provides a good trade-off between cache locality,
+ * membership checks, update performance and structural sharing.  It
+ * does so by storing the data in contiguous chunks of :math:`2^{B}`
+ * elements.  When storing big objects, the size of these contiguous
+ * chunks can become too big, damaging performance.  If this is
+ * measured to be problematic for a specific use-case, it can be
+ * solved by using a `immer::box` to wrap the type `T`.
+ *
+ * **Example**
+ *   .. literalinclude:: ../example/set/intro.cpp
+ *      :language: c++
+ *      :start-after: intro/start
+ *      :end-before:  intro/end
+ *
+ * @endrst
+ *
+ */
+template <typename T,
+          typename Hash           = std::hash<T>,
+          typename Equal          = std::equal_to<T>,
+          typename MemoryPolicy   = default_memory_policy,
+          detail::hamts::bits_t B = default_bits>
+class set
+{
+    using impl_t = detail::hamts::champ<T, Hash, Equal, MemoryPolicy, B>;
+
+    struct project_value_ptr
+    {
+        const T* operator()(const T& v) const noexcept { return &v; }
+    };
+
+public:
+    using key_type        = T;
+    using value_type      = T;
+    using size_type       = detail::hamts::size_t;
+    using diference_type  = std::ptrdiff_t;
+    using hasher          = Hash;
+    using key_equal       = Equal;
+    using reference       = const T&;
+    using const_reference = const T&;
+
+    using iterator =
+        detail::hamts::champ_iterator<T, Hash, Equal, MemoryPolicy, B>;
+    using const_iterator = iterator;
+
+    using transient_type = set_transient<T, Hash, Equal, MemoryPolicy, B>;
+
+    /*!
+     * Default constructor.  It creates a set of `size() == 0`.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    set() = default;
+
+    /*!
+     * Returns an iterator pointing at the first element of the
+     * collection. It does not allocate memory and its complexity is
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator begin() const { return {impl_}; }
+
+    /*!
+     * Returns an iterator pointing just after the last element of the
+     * collection. It does not allocate and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD iterator end() const
+    {
+        return {impl_, typename iterator::end_t{}};
+    }
+
+    /*!
+     * Returns the number of elements in the container.  It does
+     * not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type size() const { return impl_.size; }
+
+    /*!
+     * Returns `true` if there are no elements in the container.  It
+     * does not allocate memory and its complexity is @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
+
+    /*!
+     * Returns `1` when `value` is contained in the set or `0`
+     * otherwise. It won't allocate memory and its complexity is
+     * *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD size_type count(const T& value) const
+    {
+        return impl_.template get<detail::constantly<size_type, 1>,
+                                  detail::constantly<size_type, 0>>(value);
+    }
+
+    /*!
+     * Returns a pointer to the value if `value` is contained in the
+     * set, or nullptr otherwise.
+     * It does not allocate memory and its complexity is *effectively*
+     * @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD const T* find(const T& value) const
+    {
+        return impl_.template get<project_value_ptr,
+                                  detail::constantly<const T*, nullptr>>(value);
+    }
+
+    /*!
+     * Returns whether the sets are equal.
+     */
+    IMMER_NODISCARD bool operator==(const set& other) const
+    {
+        return impl_.equals(other.impl_);
+    }
+    IMMER_NODISCARD bool operator!=(const set& other) const
+    {
+        return !(*this == other);
+    }
+
+    /*!
+     * Returns a set containing `value`.  If the `value` is already in
+     * the set, it returns the same set.  It may allocate memory and
+     * its complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD set insert(T value) const
+    {
+        return impl_.add(std::move(value));
+    }
+
+    /*!
+     * Returns a set without `value`.  If the `value` is not in the
+     * set it returns the same set.  It may allocate memory and its
+     * complexity is *effectively* @f$ O(1) @f$.
+     */
+    IMMER_NODISCARD set erase(const T& value) const { return impl_.sub(value); }
+
+    /*!
+     * Returns an @a transient form of this container, a
+     * `immer::set_transient`.
+     */
+    IMMER_NODISCARD transient_type transient() const&
+    {
+        return transient_type{impl_};
+    }
+    IMMER_NODISCARD transient_type transient() &&
+    {
+        return transient_type{std::move(impl_)};
+    }
+
+    // Semi-private
+    const impl_t& impl() const { return impl_; }
+
+private:
+    friend transient_type;
+
+    set(impl_t impl)
+        : impl_(std::move(impl))
+    {}
+
+    impl_t impl_ = impl_t::empty();
+};
+
+} // namespace immer