diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index c1ac0268e7..97bb59a4fc 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -33,6 +33,7 @@ baremetal batchmode BDV bfree +BHn bibtex Bies BINDIR @@ -42,6 +43,7 @@ bitwidth bocchino boolt bsd +bst BUFFERALLOCATE BUFFERALLOCATIONFAILED BUFFERGETOUT @@ -237,6 +239,7 @@ fio fle FNDELAY fne +fontcolor FONTPATH foodoodie foodoodiehoo @@ -279,6 +282,7 @@ gpiochip gpioevent gpiohandle gpioline +GPT Graphviz grayscales GROUNDINTERFACERULES @@ -286,9 +290,9 @@ GSE gtags gtest gtimeout +GTK Guire GVCID -GPT HACKSM Handcoded handleflags @@ -321,12 +325,14 @@ INCLUDEDIR ine initstate inkscape +inorder installable intlimits inttypes INVALIDBUFFER INVALIDHEADER INVALIDHEADERHASH +invis invisi ioc ioctl @@ -381,6 +387,7 @@ LOCALSTATEDIR LOGGERRULES LOGPACKET lseek +LTK lvar LVL lxml @@ -544,6 +551,7 @@ RATELIMITERTESTER rawtime RAWTIMETESTER RBF +rbt RCHILD rcvd rdwr @@ -692,8 +700,8 @@ tparam TPP trinomials tts -typedef'ed typedef +typedef'ed uart udiv UDPSOCKET diff --git a/Fw/DataStructures/CMakeLists.txt b/Fw/DataStructures/CMakeLists.txt index 2c408d1ba9..d628b12890 100644 --- a/Fw/DataStructures/CMakeLists.txt +++ b/Fw/DataStructures/CMakeLists.txt @@ -15,14 +15,21 @@ set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalArraySetTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalArrayTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalFifoQueueTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalRedBlackTreeMapTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalRedBlackTreeSetTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/ExternalStackTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/FifoQueueTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/RedBlackTreeMapTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/RedBlackTreeSetOrMapImplTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/RedBlackTreeSetTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/ArraySetOrMapImplTestRules.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/ArraySetOrMapImplTestScenarios.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/FifoQueueTestRules.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/FifoQueueTestScenarios.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/MapTestRules.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/MapTestScenarios.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/SetTestRules.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/SetTestScenarios.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/STest/StackTestRules.cpp" diff --git a/Fw/DataStructures/ExternalRedBlackTreeMap.hpp b/Fw/DataStructures/ExternalRedBlackTreeMap.hpp new file mode 100644 index 0000000000..d866175de1 --- /dev/null +++ b/Fw/DataStructures/ExternalRedBlackTreeMap.hpp @@ -0,0 +1,177 @@ +// ====================================================================== +// \file ExternalRedBlackTreeMap.hpp +// \author bocchino +// \brief A map based on a red-black tree with external storage +// ====================================================================== + +#ifndef Fw_ExternalRedBlackTreeMap_HPP +#define Fw_ExternalRedBlackTreeMap_HPP + +#include "Fw/DataStructures/MapBase.hpp" +#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Fw { + +template +class ExternalRedBlackTreeMap final : public MapBase { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ExternalRedBlackTreeMapTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = MapConstIterator; + + //! The type of a tree node + using Node = typename RedBlackTreeSetOrMapImpl::Node; + + //! The type of a tree node index + using Index = typename RedBlackTreeSetOrMapImpl::Index; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ExternalRedBlackTreeMap() = default; + + //! Constructor providing typed backing storage. + //! nodes must point to at least capacity elements of type Node. + //! freeNodes must point to at least capacity elements of type FwSizeType. + ExternalRedBlackTreeMap(Node* nodes, //!< The nodes + Index* freeNodes, //!< The free nodes + FwSizeType capacity //!< The capacity + ) + : MapBase() { + this->setStorage(nodes, freeNodes, capacity); + } + + //! Constructor providing untyped backing storage. + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + ExternalRedBlackTreeMap(ByteArray data, //!< The data, + FwSizeType capacity //!< The capacity + ) + : MapBase() { + this->setStorage(data, capacity); + } + + //! Copy constructor + ExternalRedBlackTreeMap(const ExternalRedBlackTreeMap& map) : MapBase() { *this = map; } + + //! Destructor + ~ExternalRedBlackTreeMap() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ExternalRedBlackTreeMap& operator=(const ExternalRedBlackTreeMap& map) { + if (&map != this) { + this->m_impl = map.m_impl; + } + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return ConstIterator(this->m_impl.begin()); } + + //! Clear the map + void clear() override { this->m_impl.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return ConstIterator(this->m_impl.end()); } + + //! Find a value associated with a key in the map + //! \return SUCCESS if the item was found + Success find(const K& key, //!< The key + V& value //!< The value + ) const override { + return this->m_impl.find(key, value); + } + + //! Get the capacity of the map (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_impl.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_impl.getSize(); } + + //! Insert a (key, value) pair in the map + //! \return SUCCESS if there is room in the map + Success insert(const K& key, //!< The key + const V& value //!< The value + ) override { + return this->m_impl.insert(key, value); + } + + //! Remove a (key, value) pair from the map + //! \return SUCCESS if the key was there + Success remove(const K& key, //!< The key + V& value //!< The value + ) override { + return this->m_impl.remove(key, value); + } + + //! Set the backing storage (typed data) + //! nodes must point to at least capacity elements of type Node. + //! freeNodes must point to at least capacity elements of type FwSizeType. + void setStorage(Node* nodes, //!< The nodes + Index* freeNodes, //!< The free nodes + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(nodes, freeNodes, capacity); + } + + //! Set the backing storage (untyped data) + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(data, capacity); + } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an RedBlackTreeSetOrMapImpl + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return RedBlackTreeSetOrMapImpl::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalArray of the specified capacity, + //! as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return RedBlackTreeSetOrMapImpl::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The map implementation + RedBlackTreeSetOrMapImpl m_impl = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/ExternalRedBlackTreeSet.hpp b/Fw/DataStructures/ExternalRedBlackTreeSet.hpp new file mode 100644 index 0000000000..b9ab879f7d --- /dev/null +++ b/Fw/DataStructures/ExternalRedBlackTreeSet.hpp @@ -0,0 +1,177 @@ +// ====================================================================== +// \file ExternalRedBlackTreeSet.hpp +// \author bocchino +// \brief An set based on a red-black tree with external storage +// ====================================================================== + +#ifndef Fw_ExternalRedBlackTreeSet_HPP +#define Fw_ExternalRedBlackTreeSet_HPP + +#include "Fw/DataStructures/Nil.hpp" +#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp" +#include "Fw/DataStructures/SetBase.hpp" +#include "Fw/Types/Assert.hpp" + +namespace Fw { + +template +class ExternalRedBlackTreeSet final : public SetBase { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class ExternalRedBlackTreeSetTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = SetConstIterator; + + //! The type of a tree node + using Node = typename RedBlackTreeSetOrMapImpl::Node; + + //! The type of a tree node index + using Index = typename RedBlackTreeSetOrMapImpl::Index; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + ExternalRedBlackTreeSet() = default; + + //! Constructor providing typed backing storage. + //! nodes must point to at least capacity elements of type Node. + //! freeNodes must point to at least capacity elements of type FwSizeType. + ExternalRedBlackTreeSet(Node* nodes, //!< The nodes + Index* freeNodes, //!< The free nodes + FwSizeType capacity //!< The capacity + ) + : SetBase() { + this->setStorage(nodes, freeNodes, capacity); + } + + //! Constructor providing untyped backing storage. + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + ExternalRedBlackTreeSet(ByteArray data, //!< The data, + FwSizeType capacity //!< The capacity + ) + : SetBase() { + this->setStorage(data, capacity); + } + + //! Copy constructor + ExternalRedBlackTreeSet(const ExternalRedBlackTreeSet& set) : SetBase() { *this = set; } + + //! Destructor + ~ExternalRedBlackTreeSet() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + ExternalRedBlackTreeSet& operator=(const ExternalRedBlackTreeSet& set) { + if (&set != this) { + this->m_impl = set.m_impl; + } + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return ConstIterator(this->m_impl.begin()); } + + //! Clear the set + void clear() override { this->m_impl.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return ConstIterator(this->m_impl.end()); } + + //! Find a value associated with an element in the set + //! \return SUCCESS if the item was found + Success find(const T& element //!< The element + ) const override { + Nil nil = {}; + return this->m_impl.find(element, nil); + } + + //! Get the capacity of the set (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_impl.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_impl.getSize(); } + + //! Insert an element in the set + //! \return SUCCESS if there is room in the set + Success insert(const T& element //!< The element + ) override { + return this->m_impl.insert(element, Nil()); + } + + //! Remove an element from the set + //! \return SUCCESS if the element was there + Success remove(const T& element //!< The element + ) override { + Nil nil = {}; + return this->m_impl.remove(element, nil); + } + + //! Set the backing storage (typed data) + //! nodes must point to at least capacity elements of type Node. + //! freeNodes must point to at least capacity elements of type FwSizeType. + void setStorage(Node* nodes, //!< The nodes + Index* freeNodes, //!< The free nodes + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(nodes, freeNodes, capacity); + } + + //! Set the backing storage (untyped data) + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_impl.setStorage(data, capacity); + } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for an RedBlackTreeSetOrMapImpl + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return RedBlackTreeSetOrMapImpl::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalArray of the specified capacity, + //! as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return RedBlackTreeSetOrMapImpl::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The set implementation + RedBlackTreeSetOrMapImpl m_impl = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/MapConstIterator.hpp b/Fw/DataStructures/MapConstIterator.hpp index f9a1cc84b7..68dd09216d 100644 --- a/Fw/DataStructures/MapConstIterator.hpp +++ b/Fw/DataStructures/MapConstIterator.hpp @@ -11,6 +11,7 @@ #include "Fw/DataStructures/ArraySetOrMapImpl.hpp" #include "Fw/DataStructures/MapEntryBase.hpp" +#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp" #include "Fw/FPrimeBasicTypes.hpp" namespace Fw { @@ -24,7 +25,10 @@ class MapConstIterator { //! The type of an array iterator using ArrayIterator = typename ArraySetOrMapImpl::ConstIterator; + //! The type of a map entry base using EntryBase = MapEntryBase; + //! The type of a red-black tree iterator + using RedBlackTreeIterator = typename RedBlackTreeSetOrMapImpl::ConstIterator; private: // ---------------------------------------------------------------------- @@ -40,9 +44,12 @@ class MapConstIterator { Impl() {} //! Array constructor Impl(const ArrayIterator& it) : array(it) {} + //! Red-black tree constructor + Impl(const RedBlackTreeIterator& it) : redBlackTree(it) {} //! An array iterator ArrayIterator array; - // TODO: Add red-black tree implementation + //! A red-black tree iterator + RedBlackTreeIterator redBlackTree; // ! Destructor ~Impl() {} }; @@ -55,6 +62,9 @@ class MapConstIterator { //! Constructor providing an array implementation MapConstIterator(const ArrayIterator& it) : m_impl(it), m_implIterator(&m_impl.array) {} + //! Constructor providing a red-black tree implementation + MapConstIterator(const RedBlackTreeIterator& it) : m_impl(it), m_implIterator(&m_impl.redBlackTree) {} + //! Copy constructor MapConstIterator(const MapConstIterator& it) : m_impl(), m_implIterator() { const auto implKind = it.getImplIterator().implKind(); @@ -63,7 +73,7 @@ class MapConstIterator { this->m_implIterator = new (&this->m_impl.array) ArrayIterator(it.m_impl.array); break; case ImplKind::RED_BLACK_TREE: - // TODO + this->m_implIterator = new (&this->m_impl.redBlackTree) RedBlackTreeIterator(it.m_impl.redBlackTree); break; default: FW_ASSERT(0, static_cast(implKind)); @@ -93,7 +103,7 @@ class MapConstIterator { result = this->m_impl.array.compareEqual(it.m_impl.array); break; case ImplKind::RED_BLACK_TREE: - // TODO + result = this->m_impl.redBlackTree.compareEqual(it.m_impl.redBlackTree); break; default: FW_ASSERT(0, static_cast(implKind1)); diff --git a/Fw/DataStructures/RedBlackTreeMap.hpp b/Fw/DataStructures/RedBlackTreeMap.hpp new file mode 100644 index 0000000000..f4c2c8ae33 --- /dev/null +++ b/Fw/DataStructures/RedBlackTreeMap.hpp @@ -0,0 +1,136 @@ +// ====================================================================== +// \file RedBlackTreeMap.hpp +// \author bocchino +// \brief An map based on a red-black tree with internal storage +// ====================================================================== + +#ifndef Fw_RedBlackTreeMap_HPP +#define Fw_RedBlackTreeMap_HPP + +#include "Fw/DataStructures/ExternalRedBlackTreeMap.hpp" + +namespace Fw { + +template +class RedBlackTreeMap final : public MapBase { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(C > 0, "capacity must be greater than zero"); + + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class RedBlackTreeMapTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = MapConstIterator; + + //! The type of a tree node + using Node = typename RedBlackTreeSetOrMapImpl::Node; + + //! The type of the tree node array + using Nodes = Node[C]; + + //! The type of a tree node index + using Index = typename RedBlackTreeSetOrMapImpl::Index; + + //! The type of the free node array + using FreeNodes = Index[C]; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + RedBlackTreeMap() : MapBase(), m_extMap(m_nodes, m_freeNodes, C) {} + + //! Copy constructor + RedBlackTreeMap(const RedBlackTreeMap& map) : MapBase(), m_extMap(m_nodes, m_freeNodes, C) { + *this = map; + } + + //! Destructor + ~RedBlackTreeMap() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + RedBlackTreeMap& operator=(const RedBlackTreeMap& map) { + this->m_extMap.copyDataFrom(map); + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return this->m_extMap.begin(); } + + //! Clear the map + void clear() override { this->m_extMap.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return this->m_extMap.end(); } + + //! Find a value associated with a key in the map + //! \return SUCCESS if the item was found + Success find(const K& key, //!< The key + V& value //!< The value + ) const override { + return this->m_extMap.find(key, value); + } + + //! Get the capacity of the map (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_extMap.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_extMap.getSize(); } + + //! Insert a (key, value) pair in the map + //! \return SUCCESS if there is room in the map + Success insert(const K& key, //!< The key + const V& value //!< The value + ) override { + return this->m_extMap.insert(key, value); + } + + //! Remove a (key, value) pair from the map + //! \return SUCCESS if the key was there + Success remove(const K& key, //!< The key + V& value //!< The value + ) override { + return this->m_extMap.remove(key, value); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array for storing the tree nodes + Nodes m_nodes = {}; + + //! The array for storing the free node indices + FreeNodes m_freeNodes = {}; + + //! The external map implementation + ExternalRedBlackTreeMap m_extMap = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/RedBlackTreeSet.hpp b/Fw/DataStructures/RedBlackTreeSet.hpp new file mode 100644 index 0000000000..e920d42155 --- /dev/null +++ b/Fw/DataStructures/RedBlackTreeSet.hpp @@ -0,0 +1,131 @@ +// ====================================================================== +// \file RedBlackTreeSet.hpp +// \author bocchino +// \brief An array-based set with internal storage +// ====================================================================== + +#ifndef Fw_RedBlackTreeSet_HPP +#define Fw_RedBlackTreeSet_HPP + +#include "Fw/DataStructures/ExternalRedBlackTreeSet.hpp" + +namespace Fw { + +template +class RedBlackTreeSet final : public SetBase { + // ---------------------------------------------------------------------- + // Static assertions + // ---------------------------------------------------------------------- + + static_assert(C > 0, "capacity must be greater than zero"); + + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class RedBlackTreeSetTester; + + public: + // ---------------------------------------------------------------------- + // Public types + // ---------------------------------------------------------------------- + + //! The type of a const iterator + using ConstIterator = SetConstIterator; + + //! The type of a tree node + using Node = typename RedBlackTreeSetOrMapImpl::Node; + + //! The type of the tree node array + using Nodes = Node[C]; + + //! The type of a tree node index + using Index = typename RedBlackTreeSetOrMapImpl::Index; + + //! The type of the free node array + using FreeNodes = Index[C]; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + RedBlackTreeSet() : SetBase(), m_extSet(m_nodes, m_freeNodes, C) {} + + //! Copy constructor + RedBlackTreeSet(const RedBlackTreeSet& set) : SetBase(), m_extSet(m_nodes, m_freeNodes, C) { *this = set; } + + //! Destructor + ~RedBlackTreeSet() override = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + RedBlackTreeSet& operator=(const RedBlackTreeSet& set) { + this->m_extSet.copyDataFrom(set); + return *this; + } + + //! Get the begin iterator + //! \return The iterator + ConstIterator begin() const override { return this->m_extSet.begin(); } + + //! Clear the set + void clear() override { this->m_extSet.clear(); } + + //! Get the end iterator + //! \return The iterator + ConstIterator end() const override { return this->m_extSet.end(); } + + //! Find an element in the set + //! \return SUCCESS if the element was found + Success find(const T& element //!< The element + ) const override { + return this->m_extSet.find(element); + } + + //! Get the capacity of the set (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const override { return this->m_extSet.getCapacity(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const override { return this->m_extSet.getSize(); } + + //! Insert an element in the set + //! \return SUCCESS if there is room in the set + Success insert(const T& element //!< The element + ) override { + return this->m_extSet.insert(element); + } + + //! Remove an element from the set + //! \return SUCCESS if the key was there + Success remove(const T& element //!< The element + ) override { + return this->m_extSet.remove(element); + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array for storing the tree nodes + Nodes m_nodes = {}; + + //! The array for storing the free node indices + FreeNodes m_freeNodes = {}; + + //! The external set implementation + ExternalRedBlackTreeSet m_extSet = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp b/Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp new file mode 100644 index 0000000000..1cfc02b8bd --- /dev/null +++ b/Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp @@ -0,0 +1,1711 @@ +// +// ====================================================================== +// \title RedBlackTreeSetOrMapImpl +// \author bocchino +// \brief An implementation of a set or map based on a red-black tree +// ====================================================================== + +#ifndef Fw_RedBlackTreeSetOrMapImpl_HPP +#define Fw_RedBlackTreeSetOrMapImpl_HPP + +#include + +#include "Fw/DataStructures/ExternalArray.hpp" +#include "Fw/DataStructures/ExternalStack.hpp" +#include "Fw/DataStructures/SetOrMapImplConstIterator.hpp" +#include "Fw/DataStructures/SetOrMapImplEntry.hpp" +#include "Fw/Types/Assert.hpp" +#include "Fw/Types/SuccessEnumAc.hpp" + +namespace Fw { + +//! This class template implements a red-black tree that can be used as a +//! set or map. A red-black tree is a binary search tree (BST) such that +//! each node is colored red or black. A red-black tree is valid if +//! +//! 1. It satisfies the red child invariant: No red node has a red child. +//! +//! 2. It satisfies the black height invariant (described below). +//! +//! The black height invariant of a red-black tree T is checked with respect to the +//! leaf-augmented tree T'. T' is constructed from T by replacing every "missing" +//! node in T (i.e., every place where there could be a child but is not) with +//! a new black node. The black height invariant says that for every node N in T', +//! every path from N to a leaf in T' goes through the same number of black nodes. +//! +//! A valid red-black tree is balanced, in the sense that the find operation +//! takes O(log n) steps. +template +class RedBlackTreeSetOrMapImpl final { + // ---------------------------------------------------------------------- + // Friend class for testing + // ---------------------------------------------------------------------- + + template + friend class RedBlackTreeSetOrMapImplTester; + + public: + // ---------------------------------------------------------------------- + // The Node type + // ---------------------------------------------------------------------- + + //! Node + class Node { + public: + //! Color + enum class Color : U8 { BLACK, RED }; + + //! Direction + enum class Direction : U8 { LEFT, RIGHT }; + + //! The type of a node index + using Index = FwSizeType; + + //! The type of an entry in the set or map + using Entry = SetOrMapImplEntry; + + public: + //! Constant value representing no node + static constexpr Index NONE = std::numeric_limits::max(); + + public: + //! The index of the parent of this node + Index m_parent = NONE; + + //! The index of the left child of this node + Index m_left = NONE; + + //! The index of the right child of this node + Index m_right = NONE; + + //! The color of this node + Color m_color = Color::BLACK; + + //! The set or map entry stored in this node + Entry m_entry = {}; + + public: + //! Get the child of this node in the specified direction + //! \return The child + Index getChild(Direction direction //!< The direction + ) const { + return (direction == Direction::LEFT) ? this->m_left : this->m_right; + } + + //! Set the child of this node in the specified direction + void setChild(Direction direction, //!< The direction + Index node //!< The node index + ) { + if (direction == Direction::LEFT) { + this->m_left = node; + } else { + this->m_right = node; + } + } + + public: + // Get the opposite direction + static Direction getOppositeDirection(Direction direction //!< The direction + ) { + return (direction == Direction::LEFT) ? Direction::RIGHT : Direction::LEFT; + } + }; + + public: + // ---------------------------------------------------------------------- + // Type aliases + // ---------------------------------------------------------------------- + + //! The color type + using Color = typename Node::Color; + + //! The direction type + using Direction = typename Node::Direction; + + //! The entry type + using Entry = typename Node::Entry; + + //! The node index type + using Index = typename Node::Index; + + //! The type of the array for storing the tree nodes + using Nodes = ExternalArray; + + //! The type of the stack of indices of free nodes + using FreeNodes = ExternalStack; + + public: + // ---------------------------------------------------------------------- + // The ConstIterator type + // ---------------------------------------------------------------------- + + //! Const iterator + class ConstIterator final : public SetOrMapImplConstIterator { + public: + using ImplKind = typename SetOrMapImplConstIterator::ImplKind; + + public: + //! Default constructor + ConstIterator() {} + + //! Constructor providing the implementation + ConstIterator(const RedBlackTreeSetOrMapImpl& impl) + : SetOrMapImplConstIterator(), m_impl(&impl) { + this->m_node = this->m_impl->getOuterNodeUnder(this->m_impl->m_root, Direction::LEFT); + } + + //! Copy constructor + ConstIterator(const ConstIterator& it) + : SetOrMapImplConstIterator(), m_impl(it.m_impl), m_node(it.m_node) {} + + //! Destructor + ~ConstIterator() override = default; + + public: + //! Copy assignment operator + ConstIterator& operator=(const ConstIterator& it) { + this->m_impl = it.m_impl; + this->m_node = it.m_node; + return *this; + } + + //! Equality comparison operator + bool compareEqual(const ConstIterator& it) const { + bool result = false; + if ((this->m_impl == nullptr) && (it.m_impl == nullptr)) { + result = true; + } else if (this->m_impl == it.m_impl) { + result |= (this->m_node == it.m_node); + result |= (!this->isInRange() and !it.isInRange()); + } + return result; + } + + //! Return the impl kind + //! \return The impl kind + ImplKind implKind() const override { return ImplKind::RED_BLACK_TREE; } + + //! Get the set or map impl entry pointed to by this iterator + //! \return The set or map impl entry + const Entry& getEntry() const override { return this->m_impl->m_nodes[this->m_node].m_entry; } + + //! Increment operator + void increment() override { + FW_ASSERT(this->m_impl != nullptr); + const auto& nodes = this->m_impl->m_nodes; + if (this->m_node != Node::NONE) { + const auto rightChild = nodes[this->m_node].getChild(Direction::RIGHT); + if (rightChild != Node::NONE) { + // There is a right child. Go to the leftmost node under that child. + this->m_node = this->m_impl->getOuterNodeUnder(rightChild, Direction::LEFT); + } else { + // There is no right child. Go upwards until we pass through a left child + // or we hit the root. + const auto capacity = this->m_impl->getCapacity(); + bool done = (capacity == 0); + for (FwSizeType i = 0; i < capacity; i++) { + const auto previousNode = this->m_node; + this->m_node = nodes[this->m_node].m_parent; + if ((this->m_node == Node::NONE) or + (nodes[this->m_node].getChild(Direction::LEFT) == previousNode)) { + done = true; + break; + } + } + FW_ASSERT(done == true); + } + } + } + + //! Check whether the iterator is in range + bool isInRange() const override { + FW_ASSERT(this->m_impl != nullptr); + return this->m_node < this->m_impl->getCapacity(); + } + + //! Set the iterator to the end value + void setToEnd() { this->m_node = Node::NONE; } + + private: + //! The implementation over which to iterate + const RedBlackTreeSetOrMapImpl* m_impl = nullptr; + + //! The current node + Index m_node = Node::NONE; + }; + + public: + // ---------------------------------------------------------------------- + // Public constructors and destructors + // ---------------------------------------------------------------------- + + //! Zero-argument constructor + RedBlackTreeSetOrMapImpl() = default; + + //! Constructor providing typed backing storage. + //! nodes must point to at least capacity elements of type Node. + //! freeNodes must point to at least capacity elements of type FwSizeType. + RedBlackTreeSetOrMapImpl(Node* nodes, //!< The nodes + Index* freeNodes, //!< The free nodes + FwSizeType capacity //!< The capacity + ) { + this->setStorage(nodes, freeNodes, capacity); + } + + //! Constructor providing untyped backing storage. + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + RedBlackTreeSetOrMapImpl(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->setStorage(data, capacity); + } + + //! Copy constructor + RedBlackTreeSetOrMapImpl(const RedBlackTreeSetOrMapImpl& impl) { *this = impl; } + + //! Destructor + ~RedBlackTreeSetOrMapImpl() = default; + + public: + // ---------------------------------------------------------------------- + // Public member functions + // ---------------------------------------------------------------------- + + //! operator= + RedBlackTreeSetOrMapImpl& operator=(const RedBlackTreeSetOrMapImpl& impl) { + if (&impl != this) { + this->m_nodes = impl.m_nodes; + this->m_freeNodes = impl.m_freeNodes; + this->m_root = impl.m_root; + } + return *this; + } + + //! Get the begin iterator + ConstIterator begin() const { return ConstIterator(*this); } + + //! Clear the set or map + void clear() { + // Set the root to NONE + this->m_root = Node::NONE; + // Clear the free node stack + this->m_freeNodes.clear(); + // Push all the nodes on the free node stack + const auto capacity = this->getCapacity(); + for (FwSizeType i = 0; i < capacity; i++) { + const auto status = this->m_freeNodes.push(capacity - i - 1); + FW_ASSERT(status == Success::SUCCESS, static_cast(status)); + } + } + + //! Get the end iterator + ConstIterator end() const { + auto it = begin(); + it.setToEnd(); + return it; + } + + //! Find a value associated with a key in the map or an element in a set + //! \return SUCCESS if the item was found + Success find(const KE& keyOrElement, //!< The key or element + VN& valueOrNil //!< The value or Nil + ) const { + auto node = Node::NONE; + auto direction = Direction::LEFT; + const auto status = this->findNode(keyOrElement, node, direction); + if (status == Success::SUCCESS) { + valueOrNil = this->m_nodes[node].m_entry.getValueOrNil(); + } + return status; + } + + //! Get the capacity of the set or map (max number of entries) + //! \return The capacity + FwSizeType getCapacity() const { return this->m_nodes.getSize(); } + + //! Get the size (number of entries) + //! \return The size + FwSizeType getSize() const { + const auto capacity = this->getCapacity(); + const auto freeNodesSize = this->m_freeNodes.getSize(); + FW_ASSERT(freeNodesSize <= capacity, static_cast(freeNodesSize), + static_cast(capacity)); + return capacity - freeNodesSize; + } + + //! Insert an element in the set or a (key, value) pair in the map + //! \return SUCCESS if there is room in the set or map + Success insert(const KE& keyOrElement, //!< The key or element + const VN& valueOrNil //!< The value or Nil + ) { + auto node = Node::NONE; + auto direction = Direction::LEFT; + auto status = Success::FAILURE; + const auto findStatus = this->findNode(keyOrElement, node, direction); + if (findStatus == Success::SUCCESS) { + this->m_nodes[node].m_entry.setValueOrNil(valueOrNil); + status = Success::SUCCESS; + } else { + const auto parent = node; + status = this->m_freeNodes.pop(node); + if (status == Success::SUCCESS) { + this->m_nodes[node] = Node(); + this->m_nodes[node].m_entry.setKeyOrElement(keyOrElement); + this->m_nodes[node].m_entry.setValueOrNil(valueOrNil); + this->insertNode(node, parent, direction); + } + } + return status; + } + + //! Remove an element from the set or a (key, value) pair from the map + //! \return SUCCESS if the key or element was there + Success remove(const KE& keyOrElement, //!< The key or element + VN& valueOrNil //!< The value or Nil + ) { + auto node = Node::NONE; + auto direction = Direction::LEFT; + const auto status = findNode(keyOrElement, node, direction); + if (status == Success::SUCCESS) { + valueOrNil = this->m_nodes[node].m_entry.getValue(); + auto removedNode = Node::NONE; + this->removeNode(node, removedNode); + const auto pushStatus = this->m_freeNodes.push(removedNode); + FW_ASSERT(pushStatus == Success::SUCCESS, static_cast(pushStatus)); + } + return status; + } + + //! Set the backing storage (typed data) + //! nodes must point to at least capacity elements of type Node. + //! freeNodes must point to at least capacity elements of type FwSizeType. + void setStorage(Node* nodes, //!< The nodes + Index* freeNodes, //!< The free nodes + FwSizeType capacity //!< The capacity + ) { + this->m_nodes.setStorage(nodes, capacity); + this->m_freeNodes.setStorage(freeNodes, capacity); + this->clear(); + } + + //! Set the backing storage (untyped data) + //! data must be aligned according to getByteArrayAlignment(). + //! data must contain at least getByteArraySize(capacity) bytes. + void setStorage(ByteArray data, //!< The data + FwSizeType capacity //!< The capacity + ) { + this->m_nodes.setStorage(data, capacity); + const auto nodesSize = Nodes::getByteArraySize(capacity); + // Compute the nearest offset at or after nodesSize that is aligned for FreeNodes + const auto freeNodesAlignment = FreeNodes::getByteArrayAlignment(); + const U8 modulus = nodesSize % freeNodesAlignment; + const FwSizeType freeNodesOffset = (modulus == 0) ? 0 : freeNodesAlignment - modulus; + FW_ASSERT(freeNodesOffset % freeNodesAlignment == 0, static_cast(freeNodesOffset), + static_cast(freeNodesAlignment)); + const auto freeNodesSize = FreeNodes::getByteArraySize(capacity); + // Make sure that data has enough room + FW_ASSERT(freeNodesOffset + freeNodesSize <= data.size, static_cast(freeNodesOffset), + static_cast(freeNodesSize), static_cast(data.size)); + ByteArray freeNodesData(&data.bytes[freeNodesOffset], freeNodesSize); + // Set the storage and clear freeNodes + this->m_freeNodes.setStorage(freeNodesData, capacity); + this->clear(); + } + + public: + // ---------------------------------------------------------------------- + // Public static functions + // ---------------------------------------------------------------------- + + //! Get the alignment of the storage for a RedBlackTreeSetOrMapImpl + //! \return The alignment + static constexpr U8 getByteArrayAlignment() { return ExternalArray::getByteArrayAlignment(); } + + //! Get the size of the storage for an ExternalArray of the specified capacity, + //! as a byte array + //! \return The byte array size + static constexpr FwSizeType getByteArraySize(FwSizeType capacity //!< The capacity + ) { + return Nodes::getByteArraySize(capacity) + FreeNodes::getByteArrayAlignment() + + FreeNodes::getByteArraySize(capacity); + } + + private: + // ---------------------------------------------------------------------- + // Private helper functions + // ---------------------------------------------------------------------- + + //! This function tries to find a node whose key or element ke matches keyOrElement. + //! On return from the function: + //! 1. If such a node exists, then the return value is SUCCESS, + //! and node stores the index of N. + //! 2. Otherwise + //! a. The return value is FAILURE. + //! b. If the tree is empty, then node holds NONE. + //! c. Otherwise node stores the index of the node N containing the NONE + //! child where ke should be inserted, and direction stores the direction + //! of the child in N (left or right). + Success findNode(const KE& keyOrElement, //!< The key or element (input) + Index& node, //!< The node index (output) + Direction& direction //!< The direction (output) + ) const { + auto result = Success::FAILURE; + auto parent = Node::NONE; + auto child = this->m_root; + const auto capacity = this->getCapacity(); + bool done = (capacity == 0); + for (FwSizeType i = 0; i < capacity; i++) { + if (child == Node::NONE) { + node = parent; + done = true; + break; + } + const auto& entryKey = this->m_nodes[child].m_entry.getKeyOrElement(); + if (keyOrElement == entryKey) { + result = Success::SUCCESS; + node = child; + done = true; + break; + } else if (keyOrElement < entryKey) { + direction = Direction::LEFT; + parent = child; + child = this->m_nodes[parent].m_left; + } else { + direction = Direction::RIGHT; + parent = child; + child = this->m_nodes[parent].m_right; + } + } + FW_ASSERT(done); + return result; + } + + //! Get the direction from the parent, i.e., the direction (left or + //! right) to follow from the parent of node to get to node. + //! node must not be NONE. The parent of node must not be NONE. + Direction getDirectionFromParent(Index node //!< The node index + ) const { + const auto parent = this->m_nodes[node].m_parent; + const auto parentRight = m_nodes[parent].m_right; + return (node == parentRight) ? Direction::RIGHT : Direction::LEFT; + } + + //! Get the color of a node + //! \return The color + Color getNodeColor(Index index //!< The node index + ) const { + return (index == Node::NONE) ? Color::BLACK : this->m_nodes[index].m_color; + } + + //! Get the outer node under node in the specified direction. If node has + //! no child in that direction, then the result is node. + Index getOuterNodeUnder(Index node, //!< The node index + Direction direction //!< The direction + ) const { + auto child = (node != Node::NONE) ? this->m_nodes[node].getChild(direction) : Node::NONE; + const auto capacity = this->getCapacity(); + bool done = (capacity == 0); + for (FwSizeType i = 0; i < capacity; i++) { + if (child == Node::NONE) { + done = true; + break; + } + node = child; + child = this->m_nodes[child].getChild(direction); + } + FW_ASSERT(done == true); + return node; + } + + //! This function inserts node into the tree as a left or right child of parent, + //! according to direction. It rebalances the tree as needed to maintain the + //! red-black invariant. + //! + //! It is permissible for parent to be NONE. In this case we are inserting + //! at the root of the tree, and direction is ignored. + //! + //! It is not permissible for node to be NONE. + void insertNode(Index node, //!< The node to insert + Index parent, //!< The new parent + Direction direction //!< The direction under the new parent + ) { + // We assume (1) that the tree is a red-black tree, (2) that parent is NONE or + // the child of parent in the direction `direction` is NONE, and (3) that + // both children of node are NONE. + FW_ASSERT(this->m_nodes[node].getChild(Direction::LEFT) == Node::NONE); + FW_ASSERT(this->m_nodes[node].getChild(Direction::RIGHT) == Node::NONE); + this->m_nodes[node].m_color = Color::RED; + this->m_nodes[node].m_parent = parent; + if (parent == Node::NONE) { + this->m_root = node; + // The tree was empty, and now it consists of a single red node. + } else { + FW_ASSERT(this->m_nodes[parent].getChild(direction) == Node::NONE, + static_cast(this->m_nodes[parent].getChild(direction))); + // Set the parent + this->m_nodes[parent].setChild(direction, node); + const auto capacity = this->getCapacity(); + bool done = (capacity == 0); + for (FwSizeType i = 0; i < capacity; i++) { + // The following invariants hold: (1) node is colored red; (2) + // there may be a red child violation from parent to node; and + // (3) there are no other violations at any nodes. + if (this->getNodeColor(parent) == Color::BLACK) { + // There is no red child violation at parent, because parent is black. + done = true; + break; + } + const auto grandparent = this->m_nodes[parent].m_parent; + if (grandparent == Node::NONE) { + this->m_nodes[parent].m_color = Color::BLACK; + // This step removes the red child violation at parent. + // It preserves all other invariants. + done = true; + break; + } + const auto parentDirection = this->getDirectionFromParent(parent); + const auto parentOppositeDirection = Node::getOppositeDirection(parentDirection); + const auto uncle = this->m_nodes[grandparent].getChild(parentOppositeDirection); + if (this->getNodeColor(uncle) == Color::BLACK) { + if (this->m_nodes[parent].getChild(parentOppositeDirection) == node) { + // The subtree rooted at grandparent has the following + // shape, assuming that parentDirection is RIGHT. + // There is a red child violation from parent to node. + // There are no other violations at any nodes. + // K1, K2, K3, and K4 are arbitrary keys with K1 < K2 < K3 < K4. + /* + // BBBBBBBBBBBBBBBBBBBB + // B B + // B K2 (grandparent) B + // B B + // BBBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRR + // B B R R + // B K1 (uncle) B R K4 (parent) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V V \ + // ------------------ RRRRRRRRRRRRR | + // | | R R | + // | black height n | R K3 (node) R | + // | | R R | + // ------------------ RRRRRRRRRRRRR / + // | | / + // | | / + // V V V + // ---------------------- + // | | + // | black height n + 1 | + // | | + // ---------------------- + */ + this->rotateSubtree(parent, parentDirection); + parent = this->m_nodes[grandparent].getChild(parentDirection); + } + // The subtree rooted at grandparent has the following + // shape, assuming that parentDirection is RIGHT. + // There is a red child violation from parent to K4. + // There are no other violations at any nodes. + /* + // BBBBBBBBBBBBBBBBBBBB + // B B + // B K2 (grandparent) B + // B B + // BBBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRR + // B B R R + // B K1 (uncle) B R K3 (parent) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V | V + // ------------------ | RRRRRRRRRRRRRR + // | | | R R + // | black height n | | R K4 R + // | | | R R + // ------------------ | RRRRRRRRRRRRRR + // \ | | + // \ | | + // V V V + // ---------------------- + // | | + // | black height n + 1 | + // | | + // ---------------------- + */ + this->rotateSubtree(grandparent, parentOppositeDirection); + this->m_nodes[parent].m_color = Color::BLACK; + this->m_nodes[grandparent].m_color = Color::RED; + // The subtree has the following shape. + /* + // BBBBBBBBBBBBBBBBBBB + // B B + // B K3 (parent) B + // B B + // BBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRRRRR RRRRRRRRRRRRRRRR + // R R R R + // R K2 (grandparent) R R K4 R + // R R R R + // RRRRRRRRRRRRRRRRRRRR RRRRRRRRRRRRRRRR + // / \ | | + // / \ | | + // V V V V + // BBBBBBBBBBBBBBBBBB ---------------------------- + // B B | | + // B K1 (uncle) B | black height n + 1 | + // B B | | + // BBBBBBBBBBBBBBBBBB ---------------------------- + // | | + // | | + // V V + // ------------------ + // | | + // | black height n | + // | | + // ------------------ + */ + done = true; + break; + } else { + // The subtree rooted at grandparent has one of four + // shapes, one of which is shown below. Each of the arrows + // to red nodes may point the other way. + // There is a red child violation from parent to K4. + // There are no other violations at any nodes. + /* + // BBBBBBBBBBBBBBBBBBBB + // B B + // B K2 (grandparent) B + // B B + // BBBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // RRRRRRRRRRRRRR RRRRRRRRRRRRRRR + // R R R R + // R K1 (uncle) R R K3 (parent) R + // R R R R + // RRRRRRRRRRRRRR RRRRRRRRRRRRRRR + // \ \ / \ + // \ \ / \ + // \ \ / V + // \ \ / RRRRRRRRRRRRRR + // \ \ / R R + // \ \ / R K4 R + // \ \ | R R + // \ \ | RRRRRRRRRRRRRR + // \ \ | / / + // \ \ | / / + // V V V V V + // ---------------------------------- + // | | + // | black height n | + // | | + // ---------------------------------- + */ + this->m_nodes[parent].m_color = Color::BLACK; + this->m_nodes[uncle].m_color = Color::BLACK; + this->m_nodes[grandparent].m_color = Color::RED; + node = grandparent; + parent = this->m_nodes[node].m_parent; + // The tree shown above now has the following shape. + /* + // ??????????????????? + // ? ? + // ? parent ? + // ? ? + // ??????????????????? + // ^ + // | + // | + // RRRRRRRRRRRRRRRRRRR + // R R + // R K2 (node) R + // R R + // RRRRRRRRRRRRRRRRRRR + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB BBBBBBBBBBBBBBB + // B B B B + // B K1 (uncle) B B K3 B + // B B B B + // BBBBBBBBBBBBBB BBBBBBBBBBBBBBB + // \ \ / \ + // \ \ / \ + // \ \ / V + // \ \ / RRRRRRRRRRRRRR + // \ \ / R R + // \ \ / R K4 R + // \ \ | R R + // \ \ | RRRRRRRRRRRRRR + // \ \ | / / + // \ \ | / / + // V V V V V + // ---------------------------------- + // | | + // | black height n | + // | | + // ---------------------------------- + */ + if (parent == Node::NONE) { + // We have reached the root of the tree. + // Break out of the loop. + done = true; + break; + } + // The invariants at the top of the loop are satisfied. + // Continue to the next iteration. + } + } + FW_ASSERT(done); + } + // The tree is a red-black tree. + } + + //! This function removes a node that is colored black and is a leaf node + //! (i.e., it has no children) and is not the root. node stores the node to + //! remove. It must not be NONE. + void removeBlackLeafNode(Index node //!< The node to remove + ) { + // We assume that the tree is a red-black tree, that node is colored + // black, that node is a leaf node, and that node is not the root. + auto parent = m_nodes[node].m_parent; + // Since node is not the root, parent is not NONE. + auto direction = this->getDirectionFromParent(node); + auto oppositeDirection = Node::getOppositeDirection(direction); + // The leaf-augmented subtree rooted at parent has this shape, assuming + // direction == RIGHT. We use ? to represent the unknown color (red or + // black) of parent. + /* + // ???????????????? + // ? ? + // ? parent ? + // ? ? + // ???????????????? + // / \ + // / \ + // V V + // ------------------ BBBBBBBBBBBB + // | | B B + // | black height 2 | B node B + // | | B B + // ------------------ BBBBBBBBBBBB + // / \ + // / \ + // V V + // BBBBBBBBBB BBBBBBBBBB + // B B B B + // B B B B + // B B B B + // BBBBBBBBBB BBBBBBBBBB + */ + this->m_nodes[parent].setChild(direction, Node::NONE); + // The previous step performs the deletion. + // The remaining steps are for rebalancing. + /* + // The leaf-augmented subtree rooted at parent looks like this, + // assuming direction == RIGHT: + // + // ???????????????? + // ? ? + // ? parent ? + // ? ? + // ???????????????? + // / \ + // / \ + // V V + // ------------------ BBBBBBBBBBBB + // | | B B + // | black height 2 | B B + // | | B B + // ------------------ BBBBBBBBBBBB + // + // The black height invariant is violated, because the left subtree of + // parent has black height 2, and the right subtree has black height 1. + // Therefore there is a black height violation at parent and at every + // node in the path from the root to parent. To restore the black + // height invariant, we must perform rebalancing. + */ + bool done = false; + const auto capacity = this->getCapacity(); + for (FwSizeType i = 0; i < capacity; i++) { + // The red child constraint is satisfied. The black height + // constraint is violated because the leaf-augmented subtree rooted + // at parent has this shape, assuming direction == RIGHT. i is the + // loop index. Note that this diagram agrees with the previous one + // when i = 0. + /* + // ???????????????? + // ? ? + // ? parent ? + // ? ? + // ???????????????? + // / \ + // / \ + // V V + // ---------------------- ---------------------- + // | | | | + // | black height i + 2 | | black height i + 1 | + // | | | | + // ---------------------- ---------------------- + // + // The black height constraint would be satisfied if the child of + // parent in the direction `direction` were replaced with a red-black + // tree of black height i + 2. + */ + auto sibling = this->m_nodes[parent].getChild(oppositeDirection); + auto closeNephew = this->m_nodes[sibling].getChild(direction); + auto distantNephew = this->m_nodes[sibling].getChild(oppositeDirection); + // The leaf-augmented subtree rooted at parent has this shape + // (direction == RIGHT). If distantNephew or closeNephew are leaves + // in the leaf-augmented tree, then K1 and K4 are names for the + // nodes; they are not keys in the original tree. + // There is a black height violation at parent. + /* + // ??????????????????? + // ? ? + // ? K6 (parent) ? + // ? ? + // ??????????????????? + // / \ + // / \ + // V V + // ????????????????? ---------------------- + // ? ? | | + // ? K2 (sibling) ? | black height i + 1 | + // ? ? | | + // ????????????????? ---------------------- + // / \ + // / \ + // V V + // ???????????????????? ???????????????????? + // ? ? ? ? + // ? K1 (distantNephew) ? ? K4 (closeNephew) ? + // ? ? ? ? + // ???????????????????? ???????????????????? + // | | | | + // | | | | + // V V V V + // ------------------------- ------------------------- + // | | | | + // | black height i + 2 - | | black height i + 2 - | + // | number of black nodes | | number of black nodes | + // | in { K1, K2 } | | in { K2, K4 } | + // | | | | + // ------------------------- ------------------------- + */ + if (this->getNodeColor(sibling) == Color::RED) { + // The leaf-augmented subtree rooted at parent has this shape. + // There is a black height violation at parent. + /* + // BBBBBBBBBBBBBBBBBBB + // B B + // B K6 (parent) B + // B B + // BBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRR ---------------------- + // R R | | + // R K2 (sibling) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRR ---------------------- + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // B B B B + // B K1 (distantNephew) B B K4 (closeNephew) B + // B B B B + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i + 1 | | black height i + 1 | + // | | | | + // ------------------------ ------------------------ + */ + this->rotateSubtree(parent, direction); + this->m_nodes[parent].m_color = Color::RED; + this->m_nodes[sibling].m_color = Color::BLACK; + sibling = closeNephew; + closeNephew = this->m_nodes[sibling].getChild(direction); + distantNephew = this->m_nodes[sibling].getChild(oppositeDirection); + // The subtree has this shape: + /* + // BBBBBBBBBBBBBBBB + // B B + // B K2 B + // B B + // BBBBBBBBBBBBBBBB + // / \ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // B B R R + // B K1 B R K6 (parent) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V V V + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // | | B B | | + // | black height i + 1 | B K4 (sibling) B | black height i + 1 | + // | | B B | | + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // / \ + // / \ + // V V + // -------------------------------- -------------------------------- + // | | | | + // | black height i + 1 | | black height i + 1 | + // | with root K3 (distantNephew) | | with root K5 (closeNephew) | + // | | | | + // -------------------------------- -------------------------------- + */ + if (this->getNodeColor(distantNephew) == Color::RED) { + // The subtree has this shape: + /* + // BBBBBBBBBBBBBBBB + // B B + // B K2 B + // B B + // BBBBBBBBBBBBBBBB + // / \ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // B B R R + // B K1 B R K6 (parent) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V V V + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // | | B B | | + // | black height i + 1 | B K4 (sibling) B | black height i + 1 | + // | | B B | | + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRRRRRRRRRRR ------------------------ + // R R | | + // R K3 (distantNephew) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRRRRRRRRRRR ------------------------ + // | | + // | | + // V V + // ------------------------ + // | | + // | black height i + 1 | + // | | + // ------------------------ + */ + this->removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction); + // The subtree has this shape. The entire tree is a valid red-black tree. + /* + // BBBBBBBBBBBBBBBB + // B B + // B K2 B + // B B + // BBBBBBBBBBBBBBBB + // / \ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // B B R R + // B K1 B R K4 (sibling) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V V V + // ------------------------ BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBB + // | | B B B B + // | black height i + 1 | B K3 (distantNephew) B B K6 (parent) B + // | | B B B B + // ------------------------ BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i + 1 | | black height i + 1 | + // | | | | + // ------------------------ ------------------------ + */ + done = true; + break; + } else if (this->getNodeColor(closeNephew) == Color::RED) { + // The subtree has this shape: + /* + // BBBBBBBBBBBBBBBB + // B B + // B K2 B + // B B + // BBBBBBBBBBBBBBBB + // / \ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // B B R R + // B K1 B R K6 (parent) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V V V + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // | | B B | | + // | black height i + 1 | B K4 (sibling) B | black height i + 1 | + // | | B B | | + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // / \ + // / \ + // V V + // ------------------------ RRRRRRRRRRRRRRRRRRRRRRRRRR + // | | R R + // | black height i + 1 | R K5 (closeNephew) R + // | | R R + // ------------------------ RRRRRRRRRRRRRRRRRRRRRRRRRR + // | | + // | | + // V V + // ------------------------ + // | | + // | black height i + 1 | + // | | + // ------------------------ + */ + this->removeBlackLeafNodeHelper1(closeNephew, oppositeDirection, sibling, distantNephew); + // The subtree has this shape: + /* + // BBBBBBBBBBBBBBBB + // B B + // B K2 B + // B B + // BBBBBBBBBBBBBBBB + // / \ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // B B R R + // B K1 B R K6 (parent) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V V V + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // | | B B | | + // | black height i + 1 | B K5 (sibling) B | black height i + 1 | + // | | B B | | + // ------------------------ BBBBBBBBBBBBBBBBBB ------------------------ + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRRRRRRRRRRR ------------------------ + // R R | | + // R K4 (distantNephew) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRRRRRRRRRRR ------------------------ + // | | + // | | + // V V + // ------------------------ + // | | + // | black height i + 1 | + // | | + // ----------------------- + */ + this->removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction); + // The subtree has this shape. The entire tree is a valid red-black tree. + /* + // BBBBBBBBBBBBBBBB + // B B + // B K2 B + // B B + // BBBBBBBBBBBBBBBB + // / \ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRRR + // B B R R + // B K1 B R K5 (sibling) R + // B B R R + // BBBBBBBBBBBBBB RRRRRRRRRRRRRRRRRRRR + // | | / \ + // | | / \ + // V V V V + // ------------------------ BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBB + // | | B B B B + // | black height i + 1 | B K4 (distantNephew) B B K6 (parent) B + // | | B B B B + // ------------------------ BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i + 1 | | black height i + 1 | + // | | | | + // ------------------------ ------------------------ + */ + done = true; + break; + } else { + this->m_nodes[sibling].m_color = Color::RED; + this->m_nodes[parent].m_color = Color::BLACK; + // The subtree has this shape. The entire tree is a valid red-black tree. + /* + // BBBBBBBBBBBBBBBB + // B B + // B K2 B + // B B + // BBBBBBBBBBBBBBBB + // / \ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBB + // B B B B + // B K1 B B K6 (parent) B + // B B B B + // BBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBB + // | | / \ + // | | / \ + // V V V V + // ------------------------ RRRRRRRRRRRRRRRRRR ------------------------ + // | | R R | | + // | black height i + 1 | R K4 (sibling) R | black height i + 1 | + // | | R R | | + // ------------------------ RRRRRRRRRRRRRRRRRR ------------------------ + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBB + // B B B B + // B K3 (distantNephew) B B K5 (closeNephew) B + // B B B B + // BBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i | | black height i | + // | | | | + // ------------------------ ------------------------ + */ + done = true; + break; + } + } else if (this->getNodeColor(distantNephew) == Color::RED) { + // The leaf-augmented subtree rooted at parent has this shape. + // There is a black height violation at parent. + /* + // ??????????????????? + // ? ? + // ? K6 (parent) ? + // ? ? + // ??????????????????? + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBB ---------------------- + // B B | | + // B K2 (sibling) B | black height i + 1 | + // B B | | + // BBBBBBBBBBBBBBBBB ---------------------- + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRRRRR ------------------------ + // R R | | + // R K1 (distantNephew) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRRRRR ------------------------ + // | | + // | | + // V V + // ------------------------ + // | | + // | black height i + 1 | + // | | + // ------------------------ + */ + this->removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction); + // The subtree has this shape. The entire tree is a valid red-black tree. + /* + // BBBBBBBBBBBBBBBBB + // B B + // B K2 (sibling) B + // B B + // BBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB + // B B B B + // B K1 (distantNephew) B B K6 (parent) B + // B B B B + // RRRRRRRRRRRRRRRRRRRR BBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i + 1 | | black height i + 1 | + // | | | | + // ------------------------ ------------------------ + */ + done = true; + break; + } else if (this->getNodeColor(closeNephew) == Color::RED) { + // The leaf-augmented subtree rooted at parent has this shape. + // There is a black height violation at parent. + /* + // ??????????????????? + // ? ? + // ? K6 (parent) ? + // ? ? + // ??????????????????? + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBB ---------------------- + // B B | | + // B K2 (sibling) B | black height i + 1 | + // B B | | + // BBBBBBBBBBBBBBBBB ---------------------- + // / \ + // / \ + // V V + // ------------------------ RRRRRRRRRRRRRRRRRRRR + // | | R R + // | black height i + 1 | R K4 (closeNephew) R + // | | R R + // ------------------------ RRRRRRRRRRRRRRRRRRRR + // | | + // | | + // V V + // ------------------------ + // | | + // | black height i + 1 | + // | | + // ------------------------ + */ + this->removeBlackLeafNodeHelper1(closeNephew, oppositeDirection, sibling, distantNephew); + // The subtree has this shape: + /* + // ??????????????????? + // ? ? + // ? K6 (parent) ? + // ? ? + // ??????????????????? + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBB ---------------------- + // B B | | + // B K4 (sibling) B | black height i + 1 | + // B B | | + // BBBBBBBBBBBBBBBBB ---------------------- + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRRRRR ------------------------ + // R R | | + // R K2 (distantNephew) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRRRRR ------------------------ + // | | + // | | + // V V + // ------------------------ + // | | + // | black height i + 1 | + // | | + // ------------------------ + */ + this->removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction); + // The subtree has this shape. The entire tree is a valid red-black tree. + /* + // BBBBBBBBBBBBBBBBB + // B B + // B K4 (sibling) B + // B B + // BBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBB + // B B B B + // B K2 (distantNephew) B B K6 (parent) B + // B B B B + // RRRRRRRRRRRRRRRRRRRR BBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i + 1 | | black height i + 1 | + // | | | | + // ------------------------ ------------------------ + */ + done = true; + break; + } else if (this->getNodeColor(parent) == Color::RED) { + this->m_nodes[sibling].m_color = Color::RED; + this->m_nodes[parent].m_color = Color::BLACK; + // The leaf-augmented tree rooted at parent has this shape. + // The entire tree is a valid red-black tree. + /* + // BBBBBBBBBBBBBBBBBBB + // B B + // B K6 (parent) B + // B B + // BBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRR ---------------------- + // R R | | + // R K2 (sibling) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRR ---------------------- + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // B B B B + // B K1 (distantNephew) B B K4 (closeNephew) B + // B B B B + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i | | black height i | + // | | | | + // ------------------------ ------------------------ + */ + done = true; + break; + } else { + // The leaf-augmented subtree rooted at parent has this shape. + /* + // BBBBBBBBBBBBBBBBBBB + // B B + // B K6 (parent) B + // B B + // BBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBB ---------------------- + // B B | | + // B K2 (sibling) B | black height i + 1 | + // B B | | + // BBBBBBBBBBBBBBBBB ---------------------- + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // B B B B + // B K1 (distantNephew) B B K4 (closeNephew) B + // B B B B + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i | | black height i | + // | | | | + // ------------------------ ------------------------ + */ + this->m_nodes[sibling].m_color = Color::RED; + node = parent; + parent = this->m_nodes[node].m_parent; + if (parent == Node::NONE) { + // The entire leaf-augmented tree has this shape. + // The entire tree is a valid red-black tree. + /* + // BBBBBBBBBBBBBBBBBBB + // B B + // B K6 (node) B + // B B + // BBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRR ---------------------- + // R R | | + // R K2 (sibling) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRR ---------------------- + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // B B B B + // B K1 (distantNephew) B B K4 (closeNephew) B + // B B B B + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i | | black height i | + // | | | | + // ------------------------ ------------------------ + */ + done = true; + break; + } else { + direction = getDirectionFromParent(node); + oppositeDirection = Node::getOppositeDirection(direction); + // The leaf-augmented subtree rooted at parent has this + // shape, assuming that direction == RIGHT: + /* + // ?????????????? + // ? ? + // ? parent ? + // ? ? + // ?????????????? + // / \ + // / \ + // V V + // ------------------------ BBBBBBBBBBBBBBBBBBB + // | | B B + // | black height i + 3 | B K6 (node) B + // | | B B + // ------------------------ BBBBBBBBBBBBBBBBBBB + // / \ + // / \ + // V V + // RRRRRRRRRRRRRRRRR ---------------------- + // R R | | + // R K2 (sibling) R | black height i + 1 | + // R R | | + // RRRRRRRRRRRRRRRRR ---------------------- + // / \ + // / \ + // V V + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // B B B B + // B K1 (distantNephew) B B K4 (closeNephew) B + // B B B B + // BBBBBBBBBBBBBBBBBBBB BBBBBBBBBBBBBBBBBBBB + // | | | | + // | | | | + // V V V V + // ------------------------ ------------------------ + // | | | | + // | black height i | | black height i | + // | | | | + // ------------------------ ------------------------ + // + // There is a black height violation at parent because the + // left subtree of parent has black height i + 3, and the + // right subtree has black height i + 2. So we need at + // least one more loop iteration. After incrementing i, the + // invariant at the top of the loop is satisfied. + */ + } + } + } + FW_ASSERT(done); + // The tree is a valid red-black tree. + } + + //! This is a helper function for removeBlackLeafNode. sibling and + //! distantNephew are in-out parameters (they are both read and written). + void removeBlackLeafNodeHelper1(Index closeNephew, //!< The close nephew (input) + Direction oppositeDirection, //!< The opposite direction (input) + Index& sibling, //!< The sibling (input and output) + Index& distantNephew //!< The distant nephew (input and output) + ) { + this->rotateSubtree(sibling, oppositeDirection); + this->m_nodes[sibling].m_color = Color::RED; + this->m_nodes[closeNephew].m_color = Color::BLACK; + distantNephew = sibling; + sibling = closeNephew; + } + + //! This is a helper function for removeBlackLeafNode. + void removeBlackLeafNodeHelper2(Index parent, //!< The parent + Index sibling, //!< The sibling + Index distantNephew, //!< The distant nephew + Direction direction //!< The direction + ) { + this->rotateSubtree(parent, direction); + this->m_nodes[sibling].m_color = this->m_nodes[parent].m_color; + this->m_nodes[parent].m_color = Color::BLACK; + this->m_nodes[distantNephew].m_color = Color::BLACK; + } + + //! This function removes a node of the tree. On entry, node stores the key + //! and value to be removed. It must not be NONE. On return, removedNode + //! stores the node that was actually removed. + void removeNode(Index node, //!< The node to remove (input) + Index& removedNode //!< The node actually removed (output) + ) { + if ((this->m_nodes[node].m_left) != Node::NONE && (this->m_nodes[node].m_right != Node::NONE)) { + this->removeNodeWithTwoChildren(node, removedNode); + } else { + this->removeNodeWithAtMostOneChild(node); + removedNode = node; + } + } + + //! This function removes a node of the tree with at most one child. On + //! entry, node stores the node to be removed. It must not be NONE. + void removeNodeWithAtMostOneChild(Index node //!< The node to remove + ) { + if (this->m_nodes[node].m_left != Node::NONE) { + this->removeNodeWithOneChild(node, Direction::LEFT); + } else if (this->m_nodes[node].m_right != Node::NONE) { + this->removeNodeWithOneChild(node, Direction::RIGHT); + } else if (node == this->m_root) { + this->m_root = Node::NONE; + } else if (this->m_nodes[node].m_color == Color::RED) { + this->removeRedLeafNode(node); + } else { + this->removeBlackLeafNode(node); + } + } + + //! This function removes a node of the tree with exactly one child. node + //! stores the node to remove. It must not be NONE. direction stores the + //! direction of the child. + void removeNodeWithOneChild(Index node, //!< The node + Direction direction //!< The direction of the child + ) { + // Since the tree is a valid red-black tree, a node with exactly one + // child must be black, and the child must be red. + FW_ASSERT(this->m_nodes[node].m_color == Color::BLACK, + static_cast(this->m_nodes[node].m_color)); + const auto parent = this->m_nodes[node].m_parent; + const auto child = this->m_nodes[node].getChild(direction); + FW_ASSERT(this->m_nodes[child].m_color == Color::RED, + static_cast(this->m_nodes[node].m_color)); + if (parent == Node::NONE) { + this->m_root = child; + } else { + const auto parentDirection = this->getDirectionFromParent(node); + this->m_nodes[parent].setChild(parentDirection, child); + } + this->m_nodes[child].m_parent = parent; + this->m_nodes[child].m_color = Color::BLACK; + } + + //! This function removes a node of the tree that has two children. On + //! entry, node stores the key and value to be removed. It must not be NONE, + //! and it must have two children. On return, removedNode stores the node + //! that was actually removed. + void removeNodeWithTwoChildren(Index node, //!< The node to remove (input) + Index& removedNode //!< The node actually removed (output) + ) { + const auto rightChild = this->m_nodes[node].m_right; + removedNode = this->getOuterNodeUnder(rightChild, Direction::LEFT); + this->m_nodes[node].m_entry = this->m_nodes[removedNode].m_entry; + this->removeNodeWithAtMostOneChild(removedNode); + } + + //! This function removes a node that is colored red and is a leaf node + //! (i.e., it has no children) and is not the root. node stores the node to + //! remove. It must not be NONE. + void removeRedLeafNode(Index node //!< The node to remove + ) { + FW_ASSERT(node != this->m_root); + const auto& nodeObj = this->m_nodes[node]; + FW_ASSERT(nodeObj.m_color == Color::RED); + FW_ASSERT(nodeObj.m_left == Node::NONE); + FW_ASSERT(nodeObj.m_right == Node::NONE); + const auto parent = nodeObj.m_parent; + const auto direction = this->getDirectionFromParent(node); + this->m_nodes[parent].setChild(direction, Node::NONE); + } + + //! This function performs a left or right rotation on the subtree whose + //! root is node. The following invariants must hold on entry to this + //! function, or an assertion failure will occur: + //! + //! 1. node must not be NONE. + //! + //! 2. The child of node in the direction opposite direction must not be + //! NONE. + void rotateSubtree(Index node, //!< The node index + Direction direction //!< The direction + ) { + // We assume that the tree is a binary search tree (BST). + const auto parent = this->m_nodes[node].m_parent; + const auto oppositeDirection = Node::getOppositeDirection(direction); + const auto newRoot = this->m_nodes[node].getChild(oppositeDirection); + const auto newChild = this->m_nodes[newRoot].getChild(direction); + this->m_nodes[node].setChild(oppositeDirection, newChild); + if (newChild != Node::NONE) { + this->m_nodes[newChild].m_parent = node; + } + this->m_nodes[newRoot].setChild(direction, node); + this->m_nodes[newRoot].m_parent = parent; + if (parent != Node::NONE) { + const auto parentDirection = getDirectionFromParent(node); + this->m_nodes[parent].setChild(parentDirection, newRoot); + } else { + this->m_root = newRoot; + } + this->m_nodes[node].m_parent = newRoot; + // The tree is a BST. + } + + private: + // ---------------------------------------------------------------------- + // Private member variables + // ---------------------------------------------------------------------- + + //! The array for storing the tree nodes + Nodes m_nodes = {}; + + //! The stack of indices of free nodes. The indices point into m_nodes. + FreeNodes m_freeNodes = {}; + + //! The index of the root node + Index m_root = Node::NONE; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/SetConstIterator.hpp b/Fw/DataStructures/SetConstIterator.hpp index 8af5ff73ef..189d58b228 100644 --- a/Fw/DataStructures/SetConstIterator.hpp +++ b/Fw/DataStructures/SetConstIterator.hpp @@ -11,6 +11,7 @@ #include "Fw/DataStructures/ArraySetOrMapImpl.hpp" #include "Fw/DataStructures/Nil.hpp" +#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp" #include "Fw/FPrimeBasicTypes.hpp" namespace Fw { @@ -25,6 +26,9 @@ class SetConstIterator { //! The type of an array iterator using ArrayIterator = typename ArraySetOrMapImpl::ConstIterator; + //! The type of a red-black tree iterator + using RedBlackTreeIterator = typename RedBlackTreeSetOrMapImpl::ConstIterator; + private: // ---------------------------------------------------------------------- // Private types @@ -39,9 +43,12 @@ class SetConstIterator { Impl() {} //! Array constructor Impl(const ArrayIterator& it) : array(it) {} + //! Red-black tree constructor + Impl(const RedBlackTreeIterator& it) : redBlackTree(it) {} //! An array iterator ArrayIterator array; - // TODO: Add red-black tree implementation + //! A red-black tree iterator + RedBlackTreeIterator redBlackTree; // ! Destructor ~Impl() {} }; @@ -54,6 +61,9 @@ class SetConstIterator { //! Constructor providing an array implementation SetConstIterator(const ArrayIterator& it) : m_impl(it), m_implIterator(&m_impl.array) {} + //! Constructor providing a red-black tree implementation + SetConstIterator(const RedBlackTreeIterator& it) : m_impl(it), m_implIterator(&m_impl.redBlackTree) {} + //! Copy constructor SetConstIterator(const SetConstIterator& it) : m_impl(), m_implIterator() { const auto implKind = it.getImplIterator().implKind(); @@ -62,7 +72,7 @@ class SetConstIterator { this->m_implIterator = new (&this->m_impl.array) ArrayIterator(it.m_impl.array); break; case ImplKind::RED_BLACK_TREE: - // TODO + this->m_implIterator = new (&this->m_impl.redBlackTree) RedBlackTreeIterator(it.m_impl.redBlackTree); break; default: FW_ASSERT(0, static_cast(implKind)); @@ -92,7 +102,7 @@ class SetConstIterator { result = this->m_impl.array.compareEqual(it.m_impl.array); break; case ImplKind::RED_BLACK_TREE: - // TODO + result = this->m_impl.redBlackTree.compareEqual(it.m_impl.redBlackTree); break; default: FW_ASSERT(0, static_cast(implKind1)); diff --git a/Fw/DataStructures/docs/ExternalRedBlackTreeMap.md b/Fw/DataStructures/docs/ExternalRedBlackTreeMap.md index 52e6a22c9a..58dad2e276 100644 --- a/Fw/DataStructures/docs/ExternalRedBlackTreeMap.md +++ b/Fw/DataStructures/docs/ExternalRedBlackTreeMap.md @@ -1,4 +1,430 @@ # ExternalRedBlackTreeMap -TODO +`ExternalRedBlackTreeMap` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents an map based on a red-black tree with external storage. +Internally it maintains an +[`RedBlackTreeSetOrMapImpl`](RedBlackTreeSetOrMapImpl.md) +as the map implementation. + +## 1. Template Parameters + +`ExternalRedBlackTreeMap` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`K`|The type of a key in the map| +|`typename`|`V`|The type of a value in the map| + +## 2. Base Class + +`ExternalRedBlackTreeMap` is publicly derived from +[`MapBase`](MapBase.md). + + +## 3. Public Types + +`ExternalRedBlackTreeMap` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`MapConstIterator`](MapConstIterator.md)| +|`Entry`|Alias of [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| + +## 4. Private Member Variables + +`ExternalRedBlackTreeMap` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_impl`|[`RedBlackTreeSetOrMapImpl`](RedBlackTreeSetOrMapImpl.md)|The map implementation|C++ default initialization| + +```mermaid +classDiagram + ExternalRedBlackTreeMap *-- RedBlackTreeSetOrMapImpl +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +ExternalRedBlackTreeMap() +``` + +Initialize each member variable with its default value. + +_Example:_ +```c++ +ExternalRedBlackTreeMap map; +``` + +### 5.2. Constructor Providing Typed Backing Storage + +```c++ +ExternalRedBlackTreeMap(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type [`Entry`](ExternalRedBlackTreeMap.md#Public-Types). + +Call `setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +``` + +### 5.3. Constructor Providing Untyped Backing Storage + +```c++ +ExternalRedBlackTreeMap(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +Call `setStorage(data, capacity)`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Map::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Map::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +Map map(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 5.4. Copy Constructor + +```c++ +ExternalRedBlackTreeMap(const ExternalRedBlackTreeMap& map) +``` + +Set `*this = map`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 3; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map m1(entries, capacity); +// Insert an item +const U16 key = 0; +const U32 value = 42; +const auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Map m2(m1); +ASSERT_EQ(m2.getSize(), 1); +``` + +### 5.5. Destructor + +```c++ +~ExternalRedBlackTreeMap() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +ExternalRedBlackTreeMap& operator=(const ExternalRedBlackTreeMap& map) +``` + +1. If `&map != this` + + 1. Set `m_impl = map.m_impl`. + +1. Return `*this`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 3; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map m1(entries, capacity); +// Insert an item +U16 key = 0; +U32 value = 42; +const auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +ExternalRedBlackTreeMap m2; +ASSERT_EQ(m2.getSize(), 0); +// Call the copy assignment operator +m2 = m1; +ASSERT_EQ(m2.getSize(), 1); +value = 0; +status = m2.find(key, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 42); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_impl.begin()`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map map(entries, capacity); +// Insert an entry in the map +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto it = map.begin(); +// Use the iterator to access the underlying map const entry +const auto key = it->getKey(); +const auto value = it->getValue(); +ASSERT_EQ(key, 0); +ASSERT_EQ(value, 1); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_impl.clear()`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +const auto status = map.insert(0, 3); +ASSERT_EQ(map.getSize(), 1); +map.clear(); +ASSERT_EQ(map.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_impl.end()`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +// Call the constructor providing backing storage +Map map(entries, capacity); +// Insert an entry in the map +auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto iter = map.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, map.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, map.end()); +``` + +### 6.5. find + +```c++ +Success find(const K& key, V& value) override +``` + +Return `m_impl.find(key, value)`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +U32 value = 0; +auto status = map.find(0, value); +ASSERT_EQ(status, Success::FAILURE); +status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +status = map.find(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 1); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_impl.getCapacity()`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +ASSERT_EQ(map.getCapacity(), capacity); +``` + +### 6.7. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_impl.getSize()`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 3); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.8. insert + +```c++ +Success insert(const K& key, const V& value) override +``` + +Return `m_impl.insert(key, value)`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. remove + +```c++ +Success remove(const K& key, V& value) override +``` + +Return `m_impl.remove(key, value)`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map::Entry entries[capacity]; +Map map(entries, capacity); +auto size = map.getSize(); +ASSERT_EQ(size, 0); +auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +// Key does not exist +U32 value = 0; +status = map.remove(10, value); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Key exists +status = map.remove(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +ASSERT_EQ(value, 1); +``` + +### 6.10. setStorage (Typed Data) + +```c++ +void setStorage(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type `Entry`. +The type `Entry` is defined [in this section](ExternalRedBlackTreeMap.md#Public-Types). + +Call `m_impl.setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +Map map; +Map::Entry entries[capacity]; +map.setStorage(entries, capacity); +``` + +### 6.11. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `m_entries.setStorage(data, capacity)`. + +1. Call `clear()`. + +```c++ +using Map = ExternalRedBlackTreeMap; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Map::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Map::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +Map map; +map.setStorage(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +## 7. Public Static Functions + + +### 7.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `RedBlackTreeSetOrMapImpl::getByteArrayAlignment()`. + + +### 7.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +Return `RedBlackTreeSetOrMapImpl::getByteArraySize(capacity)`. diff --git a/Fw/DataStructures/docs/ExternalRedBlackTreeSet.md b/Fw/DataStructures/docs/ExternalRedBlackTreeSet.md index eaf1581452..1740dc1a23 100644 --- a/Fw/DataStructures/docs/ExternalRedBlackTreeSet.md +++ b/Fw/DataStructures/docs/ExternalRedBlackTreeSet.md @@ -1,4 +1,422 @@ -# RedBlackTreeSet +# ExternalRedBlackTreeSet -TODO +`ExternalRedBlackTreeSet` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a set based on a red-black tree with external storage. +Internally it maintains an [`RedBlackTreeSetOrMapImpl`](RedBlackTreeSetOrMapImpl.md) +as the set implementation. + +## 1. Template Parameters + +`ExternalRedBlackTreeSet` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an element in the set| + +## 2. Base Class + +`ExternalRedBlackTreeSet` is publicly derived from +[`SetBase`](SetBase.md). + + +## 3. Public Types + +`ExternalRedBlackTreeSet` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`MapConstIterator`](MapConstIterator.md)| +|`Entry`|Alias of [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| + +The type `Nil` is defined [in this file](Nil.md). + +## 4. Private Member Variables + +`ExternalRedBlackTreeSet` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_impl`|[`RedBlackTreeSetOrMapImpl`](RedBlackTreeSetOrMapImpl.md)|The set implementation|C++ default initialization| + +The type `Nil` is defined [in this file](Nil.md). + +```mermaid +classDiagram + ExternalRedBlackTreeSet *-- RedBlackTreeSetOrMapImpl +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +ExternalRedBlackTreeSet() +``` + +Initialize each member variable with its default value. + +_Example:_ +```c++ +ExternalRedBlackTreeSet set; +``` + +### 5.2. Constructor Providing Typed Backing Storage + +```c++ +ExternalRedBlackTreeSet(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type `Entry`. +The type `Entry` is defined [in this section](ExternalRedBlackTreeSet.md#Public-Types). + +Call `setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +``` + +### 5.3. Constructor Providing Untyped Backing Storage + +```c++ +ExternalRedBlackTreeSet(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +Call `setStorage(data, capacity)`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Set::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Set::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +ExternalRedBlackTreeSet set(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +### 5.4. Copy Constructor + +```c++ +ExternalRedBlackTreeSet(const ExternalRedBlackTreeSet& set) +``` + +Set `*this = set`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 3; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set m1(entries, capacity); +// Insert an item +const auto status = m1.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Set m2(m1); +ASSERT_EQ(m2.getSize(), 1); +``` + +### 5.5. Destructor + +```c++ +~ExternalRedBlackTreeSet() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +ExternalRedBlackTreeSet& operator=(const ExternalRedBlackTreeSet& set) +``` + +1. If `&set != this` + + 1. Set `m_impl = set.m_impl`. + +1. Return `*this`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 3; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set m1(entries, capacity); +// Insert an item +const auto status = m1.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +Set m2; +ASSERT_EQ(m2.getSize(), 0); +// Call the copy assignment operator +m2 = m1; +ASSERT_EQ(m2.getSize(), 1); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_impl.begin()`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set set(entries, capacity); +// Insert an entry in the set +const auto status = set.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto it = set.begin(); +// Use the iterator to access the element +ASSERT_EQ(*it, 42); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_impl.clear()`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +const auto status = set.insert(42); +ASSERT_EQ(set.getSize(), 1); +set.clear(); +ASSERT_EQ(set.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_impl.end()`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +// Call the constructor providing backing storage +Set set(entries, capacity); +// Insert an entry in the set +auto status = set.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto iter = set.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, set.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, set.end()); +``` + +### 6.5. find + +```c++ +Success find(const T& element) override +``` + +1. Set `Nil nil = {}`. + +1. Return `m_impl.find(key, nil)`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto status = set.find(42); +ASSERT_EQ(status, Success::FAILURE); +status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +status = set.find(42); +ASSERT_EQ(status, Success::SUCCESS); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_impl.getCapacity()`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +ASSERT_EQ(set.getCapacity(), capacity); +``` + +### 6.8. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_impl.getSize()`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. insert + +```c++ +Success insert(const T& element) override +``` + +Return `m_impl.insert(key, Nil())`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.10. remove + +```c++ +Success remove(const T& element) override +``` + +1. Set `Nil nil = {}`. + +1. Return `m_impl.remove(key, nil)`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set::Entry entries[capacity]; +Set set(entries, capacity); +auto size = set.getSize(); +ASSERT_EQ(size, 0); +auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +// Element does not exist +status = set.remove(0); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Element exists +status = set.remove(42); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +``` + +### 6.11. setStorage (Typed Data) + +```c++ +void setStorage(Entry* entries, FwSizeType capacity) +``` + +`entries` must point to a primitive array of at least `capacity` +elements of type `Entry`. +The type `Entry` is defined [in this section](ExternalRedBlackTreeSet.md#Public-Types). + +Call `m_impl.setStorage(entries, capacity)`. + +_Example:_ +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +Set set; +Set::Entry entries[capacity]; +set.setStorage(entries, capacity); +``` + +### 6.12. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +1. Call `m_entries.setStorage(data, capacity)`. + +1. Call `clear()`. + +```c++ +using Set = ExternalRedBlackTreeSet; +constexpr FwSizeType capacity = 10; +constexpr U8 alignment = Set::getByteArrayAlignment(); +constexpr FwSizeType byteArraySize = Set::getByteArraySize(capacity); +alignas(alignment) U8 bytes[byteArraySize]; +Set set; +set.setStorage(ByteArray(&bytes[0], sizeof bytes), capacity); +``` + +## 7. Public Static Functions + + +### 7.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `RedBlackTreeSetOrMapImpl::getByteArrayAlignment()`. + + +### 7.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +Return `RedBlackTreeSetOrMapImpl::getByteArraySize(capacity)`. diff --git a/Fw/DataStructures/docs/RedBlackTreeMap.md b/Fw/DataStructures/docs/RedBlackTreeMap.md index 47b9088b3e..6dac2c627e 100644 --- a/Fw/DataStructures/docs/RedBlackTreeMap.md +++ b/Fw/DataStructures/docs/RedBlackTreeMap.md @@ -1,4 +1,304 @@ # RedBlackTreeMap -TODO +`RedBlackTreeMap` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a map based on a red-black tree with internal storage. + +## 1. Template Parameters + +`RedBlackTreeMap` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`K`|The type of a key in the map| +|`typename`|`V`|The type of a value in the map| +|`FwSizeType`|`C`|The capacity, i.e., the maximum number of keys that the map can store| + +`RedBlackTreeMap` statically asserts that `C > 0`. + +## 2. Base Class + +`RedBlackTreeMap` is publicly derived from +[`MapBase`](MapBase.md). + + +## 3. Public Types + +`RedBlackTreeMap` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`MapConstIterator`](MapConstIterator.md)| +|`Node`|Alias of [`RedBlackTreeSetOrMapImpl::Node`](SetOrMapImplEntry.md)| +|`Nodes`|Alias of [`Node[C]`| +|`Index`|Alias of [`RedBlackTreeSetOrMapImpl::Index`](SetOrMapImplEntry.md)| +|`FreeNodes`|Alias of [`Index[C]`| + +## 4. Private Member Variables + +`RedBlackTreeMap` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_extMap`|[`ExternalRedBlackTreeMap`](ExternalRedBlackTreeMap.md)|The external map implementation|C++ default initialization| +|`m_nodes`|`Nodes`|The array for storing the tree nodes| +|`m_freeNodes`|`FreeNodes`|The array for storing the free node indices.| + +```mermaid +classDiagram + RedBlackTreeMap *-- ExternalRedBlackTreeMap +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +RedBlackTreeMap() +``` + +Initialize `m_extMap` with `ExternalRedBlackTreeMap(m_nodes, m_freeNodes, C)`. + +_Example:_ +```c++ +RedBlackTreeMap map; +``` + +### 5.2. Copy Constructor + +```c++ +RedBlackTreeMap(const RedBlackTreeMap& map) +``` + +1. Initialize `m_extMap` with `ExternalRedBlackTreeMap(m_nodes, m_freeNodes, C)`. + +1. Set `*this = map`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map m1; +// Insert an item +const U16 key = 0; +const U32 value = 42; +const auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Map m2(m1); +ASSERT_EQ(m2.getSize(), 1); +``` + +### 5.3. Destructor + +```c++ +~RedBlackTreeMap() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +RedBlackTreeMap& operator=(const RedBlackTreeMap& map) +``` + +Return `m_extMap.copyDataFrom(map)`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map m1; +// Insert an item +U16 key = 0; +U32 value = 42; +auto status = m1.insert(key, value); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +Map m2; +ASSERT_EQ(m2.getSize(), 0); +// Call the copy assignment operator +m2 = m1; +ASSERT_EQ(m2.getSize(), 1); +value = 0; +status = m2.find(key, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 42); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_extMap.begin()`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map map; +// Insert an entry in the map +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto it = map.begin(); +// Use the iterator to access the underlying map const entry +const key = it->getKey(); +const value = it->getValue(); +ASSERT_EQ(key, 0); +ASSERT_EQ(value, 1); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_extMap.clear()`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map map; +const auto status = map.insert(0, 3); +ASSERT_EQ(map.getSize(), 1); +map.clear(); +ASSERT_EQ(map.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_extMap.end()`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +// Call the constructor providing backing storage +Map map; +// Insert an entry in the map +auto status = map.insert(0, 1); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a map const iterator object +auto iter = map.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, map.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, map.end()); +``` + +### 6.5. find + +```c++ +Success find(const K& key, V& value) override +``` + +Return `m_extMap.find(key, value)`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map map; +U32 value = 0; +auto status = map.find(0, value); +ASSERT_EQ(status, Success::FAILURE); +status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +status = map.find(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(value, 1); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_extMap.getCapacity()`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map map; +ASSERT_EQ(map.getCapacity(), 10); +``` + +### 6.8. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_extMap.getSize()`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map map; +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 3); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. insert + +```c++ +Success insert(const K& key, const V& value) override +``` + +Return `m_extMap.insert(key, value)`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map map; +auto size = map.getSize(); +ASSERT_EQ(size, 0); +const auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.10. remove + +```c++ +Success remove(const K& key, V& value) override +``` + +Return `m_extMap.remove(key, value)`. + +_Example:_ +```c++ +using Map = RedBlackTreeMap; +Map map; +auto size = map.getSize(); +ASSERT_EQ(size, 0); +auto status = map.insert(0, 1); +ASSERT_EQ(status, Success::SUCCESS); +size = map.getSize(); +ASSERT_EQ(size, 1); +// Key does not exist +U32 value = 0; +status = map.remove(10, value); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Key exists +status = map.remove(0, value); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +ASSERT_EQ(value, 1); +``` diff --git a/Fw/DataStructures/docs/RedBlackTreeSet.md b/Fw/DataStructures/docs/RedBlackTreeSet.md index e0c1c939f2..c6c538ceed 100644 --- a/Fw/DataStructures/docs/RedBlackTreeSet.md +++ b/Fw/DataStructures/docs/RedBlackTreeSet.md @@ -1,4 +1,292 @@ -# ExternalRedBlackTreeSet +# RedBlackTreeSet -TODO +`RedBlackTreeSet` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a set based on a red-black tree with internal storage. + +## 1. Template Parameters + +`RedBlackTreeSet` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`T`|The type of an element in the set| +|`FwSizeType`|`C`|The capacity, i.e., the maximum number of elements that the set can store| + +`RedBlackTreeSet` statically asserts that `C > 0`. + +## 2. Base Class + +`RedBlackTreeSet` is publicly derived from +[`SetBase`](SetBase.md). + + +## 3. Public Types + +`RedBlackTreeSet` defines the following public types: + +|Name|Definition| +|----|----------| +|`ConstIterator`|Alias of [`SetConstIterator`](SetConstIterator.md)| +|`Entry`|Alias of [`SetOrMapImplEntry`](SetOrMapImplEntry.md)| + +The type `Nil` is defined [in this file](Nil.md). + +## 4. Private Member Variables + +`RedBlackTreeSet` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_extSet`|[`ExternalRedBlackTreeSet`](ExternalRedBlackTreeSet.md)|The external set implementation|C++ default initialization| +|`m_entries`|`Entry[C]`|The array providing the backing memory for `m_extSet`|C++ default initialization| + +The type `Entry` is defined [in this section](RedBlackTreeSet.md#Public-Types). + +```mermaid +classDiagram + RedBlackTreeSet *-- ExternalRedBlackTreeSet +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +RedBlackTreeSet() +``` + +Initialize `m_extSet` with `ExternalRedBlackTreeSet(m_entries, C)`. + +_Example:_ +```c++ +RedBlackTreeSet set; +``` + +### 5.2. Copy Constructor + +```c++ +RedBlackTreeSet(const RedBlackTreeSet& set) +``` + +1. Initialize `m_extSet` with `ExternalRedBlackTreeSet(m_entries, C)`. + +2. Set `*this = set`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set s1; +// Insert an item +const U32 element = 42; +const auto status = s1.insert(element); +ASSERT_EQ(status, Success::SUCCESS); +// Call the copy constructor +Set s2; +ASSERT_EQ(s2.getSize(), 1); +``` + +### 5.3. Destructor + +```c++ +~RedBlackTreeSet() override +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +RedBlackTreeSet& operator=(const RedBlackTreeSet& set) +``` + +Return `m_extSet.copyDataFrom(set)`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set s1; +// Insert an item +U32 element = 42; +const auto status = s1.insert(element); +ASSERT_EQ(status, Success::SUCCESS); +// Call the default constructor +Set s2; +ASSERT_EQ(s2.getSize(), 0); +// Call the copy assignment operator +s2 = s1; +ASSERT_EQ(s2.getSize(), 1); +status = s2.find(element); +ASSERT_EQ(status, Success::SUCCESS); +``` + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `m_extSet.begin()`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set set; +// Insert an element in the set +const auto status = map.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto it = set.begin(); +// Use the iterator to access the underlying map const entry +ASSERT_EQ(*it, 42); +``` + +### 6.3. clear + +```c++ +void clear() override +``` + +Call `m_extSet.clear()`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set set; +const auto status = set.insert(42); +ASSERT_EQ(set.getSize(), 1); +set.clear(); +ASSERT_EQ(set.getSize(), 0); +``` + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +Return `m_extSet.end()`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +// Call the constructor providing backing storage +Set set; +// Insert an element in the set +auto status = set.insert(42); +ASSERT_EQ(status, Fw::Success::SUCCESS); +// Get a set const iterator object +auto iter = set.begin(); +// Check that iter is not at the end +ASSERT_NE(iter, set.end()); +// Increment iter +it++; +// Check that iter is at the end +ASSERT_EQ(iter, set.end()); +``` + +### 6.5. find + +```c++ +Success find(const K& element, V& value) override +``` + +Return `m_extSet.find(element)`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set set; +auto status = set.find(42); +ASSERT_EQ(status, Success::FAILURE); +status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +status = set.find(42); +ASSERT_EQ(status, Success::SUCCESS); +``` + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const override +``` + +Return `m_extSet.getCapacity()`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set set; +ASSERT_EQ(set.getCapacity(), 10); +``` + +### 6.7. getSize + +```c++ +FwSizeType getSize() const override +``` + +Return `m_extSet.getSize()`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set set; +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.8. insert + +```c++ +Success insert(const T& element) override +``` + +Return `m_extSet.insert(element)`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set set; +auto size = set.getSize(); +ASSERT_EQ(size, 0); +const auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +``` + +### 6.9. remove + +```c++ +Success remove(const T& element) override +``` + +Return `m_extSet.remove(element)`. + +_Example:_ +```c++ +using Set = RedBlackTreeSet; +Set set; +auto size = set.getSize(); +ASSERT_EQ(size, 0); +auto status = set.insert(42); +ASSERT_EQ(status, Success::SUCCESS); +size = set.getSize(); +ASSERT_EQ(size, 1); +// Element does not exist +status = set.remove(0); +ASSERT_EQ(status, Success::FAILURE); +ASSERT_EQ(size, 1); +// Element exists +status = set.remove(42); +ASSERT_EQ(status, Success::SUCCESS); +ASSERT_EQ(size, 0); +``` diff --git a/Fw/DataStructures/docs/RedBlackTreeSetOrMapImpl.md b/Fw/DataStructures/docs/RedBlackTreeSetOrMapImpl.md new file mode 100644 index 0000000000..8e020dd74b --- /dev/null +++ b/Fw/DataStructures/docs/RedBlackTreeSetOrMapImpl.md @@ -0,0 +1,1589 @@ +# RedBlackTreeSetOrMapImpl + +`RedBlackTreeSetOrMapImpl` is a `final` class template +defined in [`Fw/DataStructures`](sdd.md). +It represents a set or map implementation based on a red-black tree. +Internally it maintains an [`ExternalArray`](ExternalArray.md) +of tree nodes and an [`ExternalStack`](ExternalStack.md) of +indices pointing into the array. +The implementation uses the algorithm described here: +https://en.wikipedia.org/wiki/Red%E2%80%93black_tree. + +## 1. Red-Black Trees + +A red-black tree is a binary search tree (BST) _T_ together with +a **red-black coloring**, i.e., an assignment of a color (red or +black) to each node of _T_. +A red-black tree _T_ is **valid** if it satisfies certain +validity constraints, which we state below. +The validity constraints ensure that the tree is **balanced**, +i.e., that no path from the root +of the tree to a leaf is too much longer than any of +the other paths. +Therefore the validity constraints ensure that search in the BST is fast +(order log _n_). + +### 1.1. Binary Trees + +A **binary tree** _T_ is a data structure defined as +follows: + +1. _T_ has a set _S_ of **nodes**. + +1. If _S_ is nonempty, then + + 1. There is one node _R_ called the **root** of _T_. + + 1. For each node _N_ + + 1. There may be zero, one, or two nodes designated as + the **children** of _N_. + + 1. Each child of _N_ is designated the **left** or **right** child of _N_. + If N has two children, then there is exactly one left + child and one right child. + + 1. Each node except _R_ is the child of exactly one node. + (Equivalently, each node except _R_ has exactly one parent.) + + 1. _R_ is not the child of any node. + (Equivalently, _R_ has no parent.) + + 1. No path that starts at a node and follows child links + contains a cycle. + +Here is an example of a binary tree: + +
+
+ +
+
+ +In this diagram we adopt the following conventions, which we will use +throughout this document: + +1. The ovals are the nodes of the tree. + +1. A solid arrow denotes a parent-child relationship between nodes. + Each arrow goes from parent to child. + +1. The left child of a node _N_, if it exists, is drawn to the left of and + below _N_. + The right child of _N_, if it exists, is drawn to the right and below. + +Let _T_ be a binary tree, and let _N_ be a node of _T_. +We say that _N_ is a **leaf** node of _T_ if _N_ +has no children. +For example, the tree shown above has a root, two leaves, +and no other nodes. + +### 1.2. Binary Search Trees + +A **binary search tree** (BST) is a binary tree _T_ with the following +additional properties: + +1. Each node _N_ of _T_ stores a **key** _K_. +All the keys are values of the same totally-ordered type, +e.g., integers, strings, etc. + +1. Each node _N_ of _T_ satisfies the following properties: + + 1. If _N_ has a left child _LC_, then any key stored in + _LC_ or in any node reachable by following child links from + _LC_ compares less than the key stored in _N_. + + 1. If _N_ has a right child _RC_, then the key stored in + _RC_ or in any node reachable by following child links from + _RC_ compares greater than the key stored in _N_. + +Here is an example of a BST: + +
+
+ +
+
+ +In this diagram we adopt the following conventions, which we will use +throughout this document: + +1. The labels in the nodes are symbolic names for the keys stored in the nodes. + +1. A lexically prior name stores a prior key. + For example, we could have K1 = 1, K2 = 2, K3 = 3; or K1 = `"a"`, K2 = + `"b"`, K3 = `"c"`; etc. + + +### 1.3. Red-Black Colorings + +A **red-black tree** is a BST _T_ together with a **red-black coloring**, +i.e., an assignment of a color (red or black) to each node of _T_. +For example, here is a red-black tree: + +
+
+ +
+
+ +The color of a node in the diagram is its color in the +red-black coloring. + +Certain red-black trees are considered **valid**. +In the rest of this section, we define the validity constraints. +The standard BST insert and remove operations can violate the +validity constraints. +Therefore, when inserting or removing a node, we must perform +a **rebalancing**, i.e., a transformation that rearranges +links and recolors nodes to maintain +the BST property and to produce a valid coloring. + + +#### 1.3.1. Leaf-Augmented Trees + +In order to state the validity constraints for a red-black tree, +we need to define the concept of a **leaf-augmented tree**. +Let _T_ be a red-black tree. +We define the leaf-augmented tree _T'_ corresponding to _T_ +as follows: + +1. If _T_ is empty, then _T'_ consists of a single black node. + +1. Otherwise, _T'_ is the red-black colored binary tree + that results from (1) adding a new black + leaf node as a left child of every node of _T_ that has no left child + and (2) adding a new black leaf node as a right child of every node of _T_ + that has no right child. + +For example, here is the leaf-augmented tree corresponding to +the tree shown above: + +
+
+ +
+
+ +Notice that a leaf-augmented tree has the following properties: + +1. Every leaf node is black. + +2. Every node either is a leaf node or has exactly two children. + + +#### 1.3.2. Valid Colorings + +A red-black tree _T_ is **valid** if its coloring +satisfies the following constraints: + +1. **RBT1:** No red node of _T_ has a red child. + +1. **RBT2:** Let _T'_ be the leaf-augmented tree constructed from _T_ as + described in the previous section. + For each node _N_ in _T'_, every path that follows child links from _N_ to a + leaf node must pass through the same number of black nodes. + +For example, the first tree shown in the section +Red-Black Colorings +is a valid red-black tree. +Notice that **RBT2** is satisfied at each node. +For example, at the root, each path from the root to a leaf +in the leaf-augmented tree passes through two black nodes. + +This tree is not a valid red-black tree because **RBT2** is not satisfied: + +
+
+ +
+
+ +Here is the corresponding leaf-augmented tree: + +
+
+ +
+
+ +Note that in the leaf-augmented tree, +the path from K2 to either child of K1 goes through two black nodes, +while the path from K2 to its right child goes through one black node. + +#### 1.3.3. Black Height + +When describing the insert and remove algorithms, +it will be useful to have the notion of the **black height** +of a path, a node, a tree, or a forest of trees. + +1. **Paths:** Let _P_ be a path from a node of a red-black tree to a leaf node. + The black height of _P_ is the number of black nodes in _P_. + +1. **Nodes:** Let _N_ be a node of a red-black tree. + If every path _P_ from _N_ to a leaf under _N_ has the same + black height _n_, then the black height of _N_ is defined + and is equal to _n_. + Otherwise the black height of _N_ is not defined. + +1. **Trees:** Let _T_ be a red-black tree. + Let _T'_ be the corresponding leaf-augmented + tree. + If the root of _T'_ has a defined black height _n_, then the black height of + _T_ is defined and is equal to _n_. + Otherwise the black height of _T_ is not defined. + +1. **Forests:** Let _F_ be a forest of red-black trees. + If every tree _T_ in _F_ has a defined black height, + and the black heights all have the same value _n_, + then the black height of _F_ is defined and is equal to _n_. + Otherwise the black height of _F_ is not defined. + +Let _T_ be a red-black tree, and note the following: + +1. _T_ satisfies **RBT2** if and only if it has a defined black height. + +1. _T_ has a defined black height if and + only if each of the child subtrees of the root of _T_ has a + defined black height, and the two black heights are the same. + +#### 1.3.4. Representing Forests + +Let _F_ be a forest of red-black trees with a defined black height. +To represent such a forest, we will write a dotted box, and we may write +a property of the forest in the box. +For example, we may represent the first tree shown in the +section **Red-Black Colorings** as follows: + +
+
+ +
+
+ +Similarly, we may represent the tree shown in the section **Valid Colorings** as follows: + +
+
+ +
+
+ +Then it is easy to see that the first tree has black height 2, +and the second tree has no defined black height. + +An arrow pointing to an empty forest means the same thing as no arrow. + +#### 1.3.5. Local Constraint Violations + +To describe the algorithms, it will be useful to have a precise way to state +where a constraint violation occurs in an invalid red-black tree. +Therefore we make the following definitions for a red-black tree _T_: + +1. If any node _N_ is red and has a red child _C_, then we say that _T_ has a + **red child violation** from _N_ to _C_. + +1. If the subtree rooted at any node _N_ does not have a defined black height, + then we say that _T_ has a **black height violation** at _N_. + +From the definitions, it is clear that _T_ is valid if and only if +it has no violation at any node. + +## 2. Template Parameters + +`RedBlackTreeSetOrMapImpl` has the following template parameters. + +|Kind|Name|Purpose| +|----|----|-------| +|`typename`|`KE`|The type of a key in a map or the element of a set| +|`typename`|`VN`|The type of a value in a map or `Nil` for set| + + +## 3. Types + +### 3.1. Node + +`Node` is a struct defined as a public member of `RedBlackTreeSetOrMapImpl`. +It represents a node of the red-black tree. + +#### 3.1.1. Public Types + +`Node` defines the following public types. + +|Name|Definition|Purpose| +|----|----------|-------| +|`Color`|An enumeration with values `BLACK` and `RED`|A node color| +|`Direction`|An enumeration with values `LEFT` and `RIGHT`|A tree direction| +|`Entry`|An alias for [`SetOrMapImplEntry`](SetOrMapImplEntry.md)|A set or map entry in a tree node.| +|`Index`|`FwSizeType`|An array index representing a tree node| + +#### 3.1.2. Public Constants + +`Node` defines the following constants. + +|Name|Type|Purpose|Value| +|----|----|-------|-------------| +|`NONE`|`Index`|An out-of-bounds index value corresponding to no node|`std::numeric_limits::max()`| + +#### 3.1.3. Public Member Variables + +`Node` has the following public member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`parent`|`Node::Index`|The index of the parent of this node|`Node::NONE`| +|`left`|`Node::Index`|The index of the left child of this node|`Node::NONE`| +|`right`|`Node::Index`|The index of the right child of this node|`Node::NONE`| +|`color`|`Color`|The color of this node|`Color::BLACK`| +|`entry`|`Entry`|The set or map entry stored in this node|C++ default initialization| + +#### 3.1.4. Public Member Functions + +##### 3.1.4.1. getChild + +```c+++ +Node::Index getChild(Direction direction) const +``` + +**Overview:** +Gets the child of `this` in direction `direction`. + +**Algorithm:** +Return `(direction == LEFT) ? left : right`. + +##### 3.1.4.2. setChild + +```c++ +void setChild(Direction direction, Index node) +``` + +**Overview:** +Sets the child of `this` in direction `direction`. + +**Algorithm:** +`(direction == LEFT) ? (this.left = node) : (this.right = node)`. + +#### 3.1.5. Public Static Methods + +##### 3.1.5.1. getOppositeDirection + +```c+++ +static Direction oppositeDirection(Direction direction) +``` + +**Overview:** +Returns the opposite direction. + +**Algorithm:** +Return `(direction == LEFT) ? RIGHT : LEFT`. + +### 3.2. Nodes and FreeNodes + +`RedBlackTreeSetOrMapImpl` defines the following public type aliases. + +|Name|Definition|Purpose| +|----|----------|-------| +|`Nodes`|Alias for [`ExternalArray`](ExternalArray.md)|The type of the array for storing the tree nodes| +|`FreeNodes`|Alias for [`ExternalStack`](ExternalStack.md)|The type of the stack of indices of free nodes.| + +### 3.3. ConstIterator + +`ConstIterator` is a public inner class of `RedBlackTreeSetOrMapImpl`. +It provides non-modifying iteration over the elements of a `RedBlackTreeSetOrMapImpl` +instance. +It is a base class of [`SetOrMapImplConstIterator`](SetOrMapImplConstIterator.md). + +**State:** +`ConstIterator` maintains the following state: + +1. A pointer to a `RedBlackTreeSetOrMapImpl` instance. + +1. A `Node::Index` value. + +**Operations:** +To create the iterator in the `begin` state, we use +[`getOuterNodeUnder`](#getOuterNodeUnder) to traverse the +tree to the leftmost node under the root, and we set the node index to point to +that node. +To set the iterator to the `end` state, we set the node index +to `NONE`. +Incrementing the iterator works as follows: + +1. If the node index is `NONE`, then do nothing. + +1. Otherwise if the current node has a non-null right child, + then use [`getOuterNodeUnder`](#getOuterNodeUnder) to traverse to the leftmost + element under the right child. + Set the node index to point to this node. + +1. Otherwise traverse the tree upwards, stopping when + we have traversed through a left child or the node index is `NONE`. + +## 4. Private Member Variables + +`RedBlackTreeSetOrMapImpl` has the following private member variables. + +|Name|Type|Purpose|Default Value| +|----|----|-------|-------------| +|`m_nodes`|`Nodes`|The array for storing the tree nodes|C++ default initialization| +|`m_freeNodes`|`FreeNodes`|The stack of indices of free nodes. The indices point into `m_nodes`.|C++ default initialization| +|`m_root`|`Node::Index`|The index of the root node|Node::NONE| + +```mermaid +classDiagram + RedBlackTreeSetOrMapImpl *-- ExternalArray + RedBlackTreeSetOrMapImpl *-- ExternalStack + ExternalArray *-- "1..*" Node +``` + +## 5. Public Constructors and Destructors + +### 5.1. Zero-Argument Constructor + +```c++ +RedBlackTreeSetOrMapImpl() +``` + +Initialize each member variable with its default value. + +### 5.2. Constructor Providing Typed Backing Storage + +```c++ +RedBlackTreeSetOrMapImpl(Node* nodes, FwSizeType* freeNodes, FwSizeType capacity) +``` + +Each of `nodes` and `freeNodes` must point to at least `capacity` items. + +Call [`setStorage(nodes, freeNodes, capacity)`](#setStorageTyped). + +### 5.3. Constructor Providing Untyped Backing Storage + +```c++ +RedBlackTreeSetOrMapImpl(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(capacity)`](#getByteArraySize) bytes. + +Call [`setStorage(data, capacity)`](#setStorageUntyped). + +### 5.4. Copy Constructor + +```c++ +RedBlackTreeSetOrMapImpl(const RedBlackTreeSetOrMapImpl& impl) +``` + +Set `*this = impl`. + +### 5.5. Destructor + +```c++ +~RedBlackTreeSetOrMapImpl() +``` + +Defined as `= default`. + +## 6. Public Member Functions + +### 6.1. operator= + +```c++ +RedBlackTreeSetOrMapImpl& operator=(const RedBlackTreeSetOrMapImpl& impl) +``` + +1. If `&impl != this` + + 1. Set `m_nodes = impl.m_nodes`. + + 1. Set `m_freeNodes = impl.m_freeNodes`. + + 1. Set `m_root = impl.m_root`. + +1. Return `*this`. + +### 6.2. begin + +```c++ +ConstIterator begin() const +``` + +Return `ConstIterator(*this)`. + +### 6.3. clear + +```c++ +void clear() +``` + +1. Set `m_root = NONE`. + +1. Call `m_freeNodes.clear()`. + +1. Let `capacity = getCapacity()`. + +1. For each `i` in the range `[0, capacity)` + + 1. Let `status = m_freeNodes.push(capacity - i - 1)`. + + 1. Assert `status == SUCCESS`. + +### 6.4. end + +```c++ +ConstIterator end() const +``` + +1. Set `it = begin()`. + +1. Call `it.setToEnd()`. + +1. Return `it`. + +### 6.5. find + +```c++ +Success find(const KE& keyOrElement, VN& valueOrNil) const +``` + +1. Set `node = NONE`. + +1. Set `direction = LEFT`. + +1. Let `status = findNode(keyOrElement, node, direction)`. + +1. If `status == SUCCESS` + + 1. Set `valueOrNil = m_nodes[node].entry.getValue()`. + +1. Return `status`. + +### 6.6. getCapacity + +```c++ +FwSizeType getCapacity() const +``` + +Return `m_nodes.getSize()`. + +### 6.7. getSize + +```c++ +FwSizeType getSize() const +``` + +1. Let `capacity = getCapacity()`. + +1. Let `freeNodesSize = m_freeNodes.getSize()`. + +1. Assert `freeNodesSize <= capacity`. + +1. Return `capacity - freeNodesSize`. + +### 6.8. insert + +```c++ +Success insert(const KE& keyOrElement, const VN& valueOrNil) +``` + +1. Set `node = NONE`. + +1. Set `direction = LEFT`. + +1. Set `status = FAILURE`. + +1. Let `findStatus = findNode(keyOrElement, node, direction)`. + +1. If `findStatus == SUCCESS` + + 1. Call `m_nodes[node].setValue(valueOrNil)`. + + 1. Set `status = SUCCESS`. + +1. Otherwise + + 1. Let `parent = node`. + + 1. Let `status = m_freeNodes.pop(node)`. + + 1. If `status == SUCCESS` + + 1. Set `m_nodes[node] = Node()`. + + 1. Call `m_nodes[node].entry.setKey(keyOrElement)`. + + 1. Call `m_nodes[node].entry.setValue(valueOrNil)`. + + 1. Call `insertNode(node, parent, direction)`. + +1. Return `status`. + +### 6.9. remove + +```c++ +Success remove(const KE& keyOrElement, VN& valueOrNil) +``` + +1. Set `node = NONE`. + +1. Set `direction = LEFT`. + +1. Let `status = findNode(keyOrElement, node, direction)`. + +1. If `status == SUCCESS` + + 1. Set `valueOrNil = m_nodes[node].entry.getValue()`. + + 1. Set `removedNode = NONE`. + + 1. Call `removeNode(node, removedNode)`. + + 1. Let `pushStatus = m_freeNodes.push(removedNode)`. + + 1. Assert `pushStatus == SUCCESS`. + +1. Return `status`. + + +### 6.10. setStorage (Typed Data) + +```c++ +void setStorage(Node* nodes, FwSizeType* freeNodes, FwSizeType capacity) +``` + +Each of `nodes` and `freeNodes` must point to at least `capacity` items. + +1. Call `m_nodes.setStorage(nodes, capacity)`. + +1. Call `m_freeNodes.setStorage(freeNodes, capacity)`. + +1. Call `this->clear()`. + + +### 6.11. setStorage (Untyped Data) + +```c++ +void setStorage(ByteArray data, FwSizeType capacity) +``` + +`data` must be aligned according to +[`getByteArrayAlignment()`](#getByteArrayAlignment) and must +contain at least [`getByteArraySize(size)`](#getByteArraySize) bytes. + +1. Call `m_nodes.setStorage(data, capacity)`. + +1. Let `nodesSize = Nodes::getByteArraySize()`. + +1. Let `freeNodesOffset` be the smallest integer greater than or equal to `nodesSize` +that is aligned for `FreeNodes::getByteArrayAlignment()`. + +1. Let `freeNodesSize = FreeNodes::getByteArraySize(capacity)`. + +1. Assert that `freeNodesOffset + freeNodesSize <= data.size`. + +1. Let `freeNodesData = ByteArray(&data.bytes[freeNodesOffset], freeNodesSize)`. + +1. Call `m_freeNodes.setStorage(freeNodesData, capacity)`. + +1. Call `this->clear()`. + +## 7. Public Static Functions + + +### 7.1. getByteArrayAlignment + +```c++ +static constexpr U8 getByteArrayAlignment() +``` + +Return `Nodes::getByteArrayAlignment()`. + + +### 7.2. getByteArraySize + +```c++ +static constexpr FwSizeType getByteArraySize(FwSizeType capacity) +``` + +1. Let `nodesSize = Nodes::getByteArraySize(capacity)`. + +1. Let `freeNodesAlignment = FreeNodes::getByteArrayAlignment()`. + +1. Let `freeNodesSize = FreeNodes::getByteArraySize(capacity)`. + +1. Return `nodesSize + freeNodesAlignment + freeNodesSize`. + +## 8. Private Helper Functions + + +### 8.1. findNode + +```c++ +Success findNode(const KE& keyOrElement, Node::Index& node, Direction& direction) const +``` + +**Overview:** +This function tries to find a node whose key or element _ke_ matches +`keyOrElement`. +On return from the function: + +1. If the function found such a node _N_, then the return value is `SUCCESS`, +and `node` stores the index of _N_. + +1. Otherwise + + 1. The return value is `FAILURE`. + + 1. If the tree is empty, then `node` holds `NONE`. + + 1. Otherwise `node` stores the index of the node _N_ containing the `NONE` + child where _ke_ should be inserted, and + `direction` stores the direction of the child in _N_ (left or right). + +**Algorithm:** + +1. Set `result = FAILURE`. + +1. Set `parent = NONE`. + +1. Set `child = m_root`. + +1. Let `capacity = getCapacity()`. + +1. Set `done = (capacity == 0)`. + +1. In a for loop bounded by `capacity`: + + 1. If `child == NONE` then set `node = parent`, set `done = true`, and break + out of the loop. + + 1. Compare `keyOrElement` to the key or element in the entry stored in + `m_nodes[child]`. + + 1. If the comparison result is equality, then set `done = true`, set + `result = SUCCESS`, set `node = child`, and break out of the loop. + + 1. Otherwise if the comparison result is less than set `direction = LEFT`, + set `parent = child`, and set `child = m_nodes[parent].left`. + + 1. Otherwise set `direction = RIGHT`, set `parent = child`, and + set `child = m_nodes[parent].right`. + +1. Assert `done == true`. + +1. Return `result`. + +### 8.2. getDirectionFromParent + +```c++ +Direction getDirectionFromParent(Node::Index node) const +``` + +**Overview:** Get the direction from the parent, i.e., +the direction (left or right) to follow from the parent +of `node` to get to `node`. +`node` must not be `NONE`. +The parent of `node` must not be `NONE`. + +**Algorithm:** + +1. Let `parent = m_nodes[node].parent`. + +1. Let `parentRight = m_nodes[parent].right`. + +1. Return `node == parentRight ? RIGHT : LEFT`. + +### 8.3. getNodeColor + +```c++ +Node::Color getNodeColor(Index index) const +``` + +Return `index == NONE ? BLACK : m_nodes[index].color`. + + +### 8.4. getOuterNodeUnder + +```c++ +Node::Index getOuterNodeUnder(Node::Index node, Direction direction) const +``` + +**Overview:** +Get the outer node under `node` in the specified direction. +If `node` has no child in that direction, then the result is `node`. + +**Algorithm:** + +1. Set `child = (node != NONE) ? m_nodes[node].getChild(direction) : NONE`. + +1. Set `done = (node == NONE)`. + +1. In a for loop bounded by `getCapacity()` + + 1. If `child == NONE` then set `done = true` and break out of the loop. + + 1. Set `node = child`. + + 1. Set `child = m_nodes[child].getChild(direction)`. + +1. Assert `done == true`. + +1. Return `node`. + + +### 8.5. getPredecessorOfNone + +```c++ +Node::Index getPredecessorOfNone(Node::Index node, Direction direction) const +``` + +**Overview:** +This function gets the predecessor of a `NONE` node, specified as +(1) a node, which is either `NONE` itself or a node with a `NONE` child; +and (2) a direction. +The predecessor of a node is the predecessor in the inorder traversal of the +tree. + +If `node` is `NONE`, then the function returns `NONE` and ignores `direction`. +Otherwise, the function returns the predecessor of the +the child of `node` in the direction `direction`, assuming that the +child is `NONE`. + +**Algorithm:** + +1. Set `result = node`. + +1. If `node != NONE` + + 1. Set `parent = m_nodes[node].parent`. + + 1. If `parent == NONE` and `direction == LEFT` then set `result = NONE`. + + 1. If `parent != NONE` and `m_nodes[parent].direction != direction` then set `result = parent`. + +1. Return `result`. + +### 8.6. insertNode + +```c++ +void insertNode(Node::Index node, Node::Index parent, Direction direction) +``` + +**Overview:** +This function inserts `node` into the tree as a left or right +child of `parent`, according to `direction`. +It rebalances the tree as needed to maintain the red-black +invariant. + +It is permissible for `parent` to be `NONE`. +In this case we are inserting at the root of the tree, and `direction` is +ignored. + +It is not permissible for `node` to be `NONE`. + +**Algorithm:** + +1. _We assume (1) that the tree is a red-black tree, (2) that + `parent` is none or the child of `parent` in the direction `direction` is + `NONE`, and (3) that both children of `node` are `NONE`._ + +1. Set `m_nodes[node].color = RED`. + +1. Set `m_nodes[node].parent = parent`. + +1. If `parent == NONE` then set `m_root = node`. + _The tree was empty, and now it consists of a single red node._ + +1. Otherwise + + 1. Call `m_nodes[parent].setChild(direction, node)`. + + 1. Let `capacity = getCapacity()`. + + 1. Set `done = (capacity == 0)`. + + 1. In a for loop bounded by `capacity`: + + 1. _The following invariants hold: (1) `node` is colored red; (2) there + may be a red child violation from `parent` to `node`; and (3) there + are no other violations at any nodes._ + + 1. If `getNodeColor(parent) == BLACK` then set `done = true` and break + out of the loop. + _There is no red child violation at `parent`, because `parent` is black._ + + 1. Set `grandparent = m_nodes[parent].parent`. + + 1. If `grandparent == NONE` then + + 1. Set `m_nodes[parent].color = BLACK`. + _This step removes the red child violation at `parent`, and it preserves + all other invariants._ + + 1. Set `done = true`. + + 1. Otherwise + + 1. Let `parentDirection = getDirectionFromParent(parent)`. + + 1. Let `parentOppositeDirection = Node::getOppositeDirection(parentDirection)`. + + 1. Let `uncle = m_nodes[grandparent].getChild(parentOppositeDirection)`. + + 1. If `getNodeColor(uncle) == BLACK` then + + 1. If `m_nodes[parent].getChild(parentOppositeDirection) == node` + + 1. _The subtree rooted at `grandparent` has the following + shape, assuming that `parentDirection` is `RIGHT`. + There is a red child violation from `parent` to `node`. + There are no other violations at any nodes._ + +
+
+ +
+
+ + 1. Call `rotateSubtree(parent, parentDirection)`. + + 1. Set `parent = m_nodes[grandparent].getChild(parentDirection)`. + + 1. _The subtree rooted at `grandparent` has + the following shape, assuming that `parentDirection` is `RIGHT`. + There is a red child violation from `parent` to K4. + There are no other violations at any nodes._ + + +
+
+ +
+
+ + 1. Call `rotateSubtree(grandparent, parentOppositeDirection)`. + + 1. Set `m_nodes[parent].color = BLACK`. + + 1. Set `m_nodes[grandparent].color = RED`. + + 1. Set `done = true`. + + 1. _The subtree has the following shape:_ + +
+
+ +
+
+ + 1. Otherwise + + 1. _The subtree rooted at `grandparent` has one of four shapes, + one of which is shown below. + Each of the arrows to red nodes may point the other way. + There is a red child violation from `parent` to K4. + There are no other violations at any nodes._ + + +
+
+ +
+
+ + 1. Set `m_nodes[parent].color = BLACK`. + + 1. Set `m_nodes[uncle].color = BLACK`. + + 1. Set `m_nodes[grandparent].color = RED`. + + 1. Set `node = grandparent`. + + 1. Set `parent = m_nodes[node].parent`. + + 1. _Here the tree shown above is transformed into the following shape:_ + +
+
+ +
+
+ + 1. If `parent == NONE` then set `done = true`. + + 1. If `done` then break out of the loop. + + 1. Assert `done`. + +1. _Here the tree is a red-black tree._ + +### 8.7. removeNode + +```c++ +void removeNode(Node::Index node, Node::Index& removedNode) +``` + +**Overview:** +This function removes a node of the tree. +On entry, `node` stores the key and value to be removed. +It must not be `NONE`. +On return, `removedNode` stores the node that was actually removed. + +**Algorithm:** + +1. If `m_nodes[node].left != NONE` and `m_nodes[node].right != NONE` + then call `removeNodeWithTwoChildren(node, removedNode)`. + +1. Otherwise + + 1. Call `removeNodeWithAtMostOneChild(node)`. + + 1. Set `removedNode = node`. + +### 8.8. removeBlackLeafNode + +```c++ +void removeBlackLeafNode(Node::Index node) +``` + +**Overview:** +This function removes a node that is colored black and +is a leaf node (i.e., it has no children) and is not the root. +`node` stores the node to remove. +It must not be `NONE`. + +**Algorithm:** + +1. _We assume that the tree is a red-black tree, that `node` + is colored black, that `node` is a leaf node, and that `node` + is not the root._ + +1. Set `parent = m_nodes[node].parent`. + _Since `node` is not the root, `parent` is not `NONE`._ + +1. Set `direction = getDirectionFromParent(node)`. + +1. Set `oppositeDirection = Node::getOppositeDirection(direction)`. + +1. _The leaf-augmented subtree rooted at `parent` has this shape, assuming `direction == RIGHT`. + We use gray to represent the unknown color (red or black) of `parent`. + The black heights are the black heights of the original tree._ + +
+
+ +
+
+ +1. Call `m_nodes[parent].setChild(direction, NONE)`. + _This step performs the deletion._ + +1. _The leaf-augmented subtree rooted at `parent` + looks like this, assuming `direction == RIGHT`:_ + +
+
+ +
+
+ + _Constraint RBT2 is violated, because the left subtree + of `parent` has black height 2, and the right subtree has black height 1. + Therefore there is a black height violation at `parent` and + at every node in the path from the root to `parent`. + To comply with RBT2, we must perform rebalancing._ + +1. Let `capacity = getCapacity()`. + +1. Set `done = (capacity == 0)`. + +1. For each `i` in the range `[0, capacity)` + + 1. _Constraint RBT1 is satisfied. Constraint RBT2 is violated because + the leaf-augmented subtree rooted at `parent` has this shape, + assuming `direction == RIGHT`. + i is the loop index. + Note that this diagram agrees with the previous one when i = 0._ + +
+
+ +
+
+ + _Constraint RBT2 would be satisfied + if the child of `parent` in the direction `direction` were replaced + with a red-black tree of black height i + 2._ + + 1. Set `sibling = m_nodes[parent].getChild(oppositeDirection)`. + + 1. Set `closeNephew = m_nodes[sibling].getChild(direction)`. + + 1. Set `distantNephew = m_nodes[sibling].getChild(oppositeDirection)`. + + 1. _The leaf-augmented subtree rooted at `parent` has this shape + (direction == `RIGHT`). + If `distantNephew` or `closeNephew` are leaves in the leaf-augmented + tree, then K1 and K4 are names for the nodes; they are not keys in the + original tree._ + +
+
+ +
+
+ + 1. If `m_nodes[sibling].color == RED` + + 1. _The leaf-augmented subtree rooted at `parent` has this shape._ + +
+
+ +
+
+ + 1. Call `rotateSubtree(parent, direction)`. + + 1. Set `m_nodes[parent].color = RED`. + + 1. Set `m_nodes[sibling].color = BLACK`. + + 1. Set `sibling = closeNephew`. + + 1. Set `closeNephew = m_nodes[sibling].getChild(direction)`. + + 1. Set `distantNephew = m_nodes[sibling].getChild(oppositeDirection)`. + + 1. _The subtree has this shape:_ + +
+
+ +
+
+ + 1. If `getNodeColor(distantNephew) == RED` + + 1. _The subtree has this shape:_ + +
+
+ +
+
+ + 1. Call `removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction)`. + + 1. _The subtree has this shape. The entire tree is a valid red-black tree._ + +
+
+ +
+
+ + 1. Set `done = true` and break out of the loop. + + 1. Otherwise if `getNodeColor(closeNephew) == RED` + + 1. _The subtree has this shape:_ + +
+
+ +
+
+ + 1. Call `removeBlackLeafNodeHelper1(closeNephew, oppositeDirection, sibling, distantNephew)`. + + 1. _The subtree has this shape:_ + +
+
+ +
+
+ + 1. Call `removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction)`. + + 1. _The subtree has this shape. The entire tree is a valid red-black tree._ + +
+
+ +
+
+ + 1. Set `done = true` and break out of the loop. + + 1. Otherwise + + 1. Set `m_nodes[sibling].color = RED`. + + 1. Set `m_nodes[parent].color = BLACK`. + + 1. _The subtree has this shape. The entire tree is a valid red-black tree._ + +
+
+ +
+
+ + 1. Set `done = true` and break out of the loop. + + 1. Otherwise if `getNodeColor(distantNephew) == RED` + + 1. _The subtree has this shape:_ + +
+
+ +
+
+ + 1. Call `removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction)`. + + 1. _The subtree has this shape. The entire tree is a valid red-black tree._ + +
+
+ +
+
+ + 1. Set `done = true` and break out of the loop. + + 1. Otherwise if `getNodeColor(closeNephew) == RED` + + 1. _The subtree has this shape:_ + +
+
+ +
+
+ + 1. Call `removeBlackLeafNodeHelper1(closeNephew, oppositeDirection, sibling, distantNephew)`. + + 1. _The subtree has this shape:_ + +
+
+ +
+
+ + 1. Call `removeBlackLeafNodeHelper2(parent, sibling, distantNephew, direction)`. + + 1. _The subtree has this shape. The entire tree is a valid red-black tree._ + +
+
+ +
+
+ + 1. Set `done = true` and break out of the loop. + + 1. Otherwise if `getNodeColor(parent) == RED` + + 1. Set `m_nodes[sibling].color = RED`. + + 1. Set `m_nodes[parent].color = BLACK`. + + 1. _The leaf-augmented subtree rooted at `parent` has this shape. + The entire tree is a valid red-black tree._ + +
+
+ +
+
+ + 1. Set `done = true` and break out of the loop. + + 1. Otherwise + + 1. _The leaf-augmented subtree rooted at `parent` has this shape._ + +
+
+ +
+
+ + 1. Set `m_nodes[sibling].color = RED`. + + 1. Set `node = parent`. + + 1. Set `parent = m_nodes[node].parent`. + + 1. If `parent == NONE` + + 1. _The entire leaf-augmented tree has this shape. + The entire tree is a valid red-black tree._ + +
+
+ +
+
+ + 1. Set `done = true` and break out of the loop. + + 1. Otherwise + + 1. Set `direction = getDirectionFromParent(node)`. + + 1. Set `oppositeDirection = Node::getOppositeDirection(direction)`. + + 1. _The leaf-augmented subtree rooted at `parent` has this shape, + assuming that direction == RIGHT:_ + +
+
+ +
+
+ + _RBT2 is not satisfied because the left subtree of `parent` has + black height i + 3, and the right subtree has black height i + 2. + So we need at least one more loop iteration. + After incrementing i, the invariant at the top of the loop is satisfied._ + +1. Assert `done == true`. + +1. _The tree is a valid red-black tree._ + +### 8.9. removeBlackLeafNodeHelper1 + +```c++ +void removeBlackLeafNodeHelper1( + Node::Index closeNephew, + Direction oppositeDirection, + Node::Index& sibling, + Node::Index& distantNephew +) +``` + +**Overview:** +This is a helper function for `removeBlackLeafNode`. +`sibling` and `distantNephew` are in-out parameters (they are both read and +written). + +**Algorithm:** + +1. Call `rotateSubtree(sibling, oppositeDirection)`. + +1. Set `m_nodes[sibling].color = RED`. + +1. Set `m_nodes[closeNephew].color = BLACK`. + +1. Set `distantNephew = sibling`. + +1. Set `sibling = closeNephew`. + +### 8.10. removeBlackLeafNodeHelper2 + +```c++ +void removeBlackLeafNodeHelper2( + Node::Index parent, + Node::Index sibling, + Node::Index distantNephew, + Direction direction +) +``` + +**Overview:** +This is a helper function for `removeBlackLeafNode`. + +**Algorithm:** + +1. Call `rotateSubtree(parent, direction)`. + +1. Set `m_nodes[sibling].color = m_nodes[parent].color`. + +1. Set `m_nodes[parent].color = Color::BLACK`. + +1. Set `m_nodes[distantNephew].color = Color::BLACK`. + +### 8.11. removeNodeWithAtMostOneChild + +```c++ +void removeNodeWithAtMostOneChild(Node::Index node) +``` + +**Overview:** +This function removes a node of the tree with at most one child. +On entry, `node` stores the node to be removed. +It must not be `NONE`. + +**Algorithm:** + +1. If `m_nodes[node].left != NONE` then + call `removeNodeWithOneChild(node, LEFT)`. + +1. Otherwise if `m_nodes[node].right != NONE` then + call `removeNodeWithOneChild(node, RIGHT)`. + +1. Otherwise if `node == m_root` then set `m_root = NONE`. + +1. Otherwise if `m_nodes[node].color == RED` then call + `removeRedLeafNode(node)`. + +1. Otherwise call `removeBlackLeafNode(node)`. + +### 8.12. removeNodeWithOneChild + +```c++ +void removeNodeWithOneChild(Node::Index node, Direction direction) +``` + +**Overview:** +This function removes a node of the tree with exactly one +child. +`node` stores the node to remove. +It must not be `NONE`. +`direction` stores the direction of the child. + +**Algorithm:** + +1. Assert `m_nodes[node].color == BLACK`. + +1. Let `parent = m_nodes[node].parent`. + +1. Let `child = m_nodes[node].getChild(direction)`. + +1. Assert `m_nodes[child].color == RED`. + +1. If `parent == NONE` then set `m_root = child`. + +1. Otherwise + + 1. Let `direction = getDirectionFromParent(node)`. + + 1. Call `m_nodes[parent].setChild(direction, child)`. + +1. Set `m_nodes[child].parent = parent`. + +1. Set `m_nodes[child].color = BLACK`. + +### 8.13. removeNodeWithTwoChildren + +```c++ +void removeNodeWithTwoChildren(Node::Index node, Node::Index& removedNode) +``` + +**Overview:** +This function removes a node of the tree that has two children. +On entry, `node` stores the key and value to be removed. +It must not be `NONE`, and it must have two children. +On return, `removedNode` stores the node that was actually removed. + +**Algorithm:** + +1. Set `removedNode` to the successor of `node`. + This is the leftmost node under the right child of `node`. + Use [`getOuterNodeUnder`](#getOuterNodeUnder) to compute it. + +1. Set `m_nodes[node].entry = m_nodes[removedNode].entry`. + +1. Call `removeNodeWithAtMostOneChild(removedNode)`. + +### 8.14. removeRedLeafNode + +```c++ +void removeRedLeafNode(Node::Index node) +``` + +**Overview:** +This function removes a node that is colored red and +is a leaf node (i.e., it has no children) and is not the root. +`node` stores the node to remove. +It must not be `NONE`. + +**Algorithm:** + +1. Assert `m_nodes[node].color == RED`. + +1. Assert `node != m_root`. + +1. Let `parent = m_nodes[node].parent`. + +1. Let `direction = getDirectionFromParent(node)`. + +1. Call `m_nodes[parent].setChild(direction, NONE)`. + +### 8.15. rotateSubtree + +```c++ +void rotateSubtree(Node::Index node, Direction direction) +``` + +**Overview:** +This function performs a left or right rotation on the subtree +whose root is `node`. +The following invariants must hold on entry to this function, +or an assertion failure will occur: + +1. `node` must not be `NONE`. + +1. The child of `node` in the direction opposite + `direction` must not be `NONE`. + +**Algorithm:** + +1. _We assume that the tree is a BST._ + +1. Let `parent = m_nodes[node].parent`. + +1. Let `oppositeDirection = Node::getOppositeDirection(direction)`. + +1. Let `newRoot = m_nodes[node].getChild(oppositeDirection))`. + +1. Let `newChild = m_nodes[newRoot].getChild(direction)`. + +1. Call `m_nodes[node].setChild(oppositeDirection, newChild)`. + +1. If `newChild != NONE` then set `m_nodes[newChild].parent = node`. + +1. Call `m_nodes[newRoot].setChild(direction, node)`. + +1. Set `m_nodes[newRoot].parent = parent`. + +1. If `parent != NONE` then + + 1. Let `parentDirection = getDirectionFromParent(node)`. + + 1. Call `m_nodes[parent].setChild(parentDirection, newRoot)`. + +1. Otherwise set `m_root = newRoot`. + +1. Set `m_nodes[node].parent = newRoot`. + +1. _The tree is a BST._ + +**Example:** +Here is an example of applying `rotateSubtree` to do a left rotation. +Before applying the function, the tree looks like this: + +
+
+ +
+
+ +After applying the function, the tree looks like this: + +
+
+ +
+
+ +Notice that the rotation preserves the BST property. diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/black-height-violation-forest.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/black-height-violation-forest.dot new file mode 100644 index 0000000000..1ff6dd8601 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/black-height-violation-forest.dot @@ -0,0 +1,10 @@ +digraph { + + K2 -> BH2 + K2 -> BH1 + + K2 [color="red",fontcolor="red"] + BH1 [shape="box",label="black height 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height 2",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/black-height-violation-forest.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/black-height-violation-forest.svg new file mode 100644 index 0000000000..b0ec02697a --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/black-height-violation-forest.svg @@ -0,0 +1,42 @@ + + + + + + + + + +K2 + +K2 + + + +BH2 + +black height 2 + + + +K2->BH2 + + + + + +BH1 + +black height 1 + + + +K2->BH1 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/bst.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/bst.dot new file mode 100644 index 0000000000..115cb9197e --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/bst.dot @@ -0,0 +1,8 @@ +digraph { + + K4 -> K2 + K4 -> K5 + K2 -> K1 + K2 -> K3 + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/bst.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/bst.svg new file mode 100644 index 0000000000..c1f5475bfc --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/bst.svg @@ -0,0 +1,66 @@ + + + + + + + + + +K4 + +K4 + + + +K2 + +K2 + + + +K4->K2 + + + + + +K5 + +K5 + + + +K4->K5 + + + + + +K1 + +K1 + + + +K2->K1 + + + + + +K3 + +K3 + + + +K2->K3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation-augmented.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation-augmented.dot new file mode 100644 index 0000000000..21c79d2cf5 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation-augmented.dot @@ -0,0 +1,16 @@ +digraph { + + K2 -> K1 + K2 -> K3 [color="invis"] + K1 -> A + K1 -> B + K2 -> C + + K1 + K2 [color="red",fontcolor="red"] + K3 [color="invis",fontcolor="invis"] + A [fontcolor="invis"] + B [fontcolor="invis"] + C [fontcolor="invis"] + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation-augmented.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation-augmented.svg new file mode 100644 index 0000000000..38be7bd76a --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation-augmented.svg @@ -0,0 +1,78 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K3 + +K3 + + + +K2->K3 + + + + + +C + +C + + + +K2->C + + + + + +A + +A + + + +K1->A + + + + + +B + +B + + + +K1->B + + + + + diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation.dot new file mode 100644 index 0000000000..444e180307 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation.dot @@ -0,0 +1,10 @@ +digraph { + + K2 -> K1 + K2 -> K3 [color="invis"] + + K1 + K2 [color="red",fontcolor="red"] + K3 [color="invis",fontcolor="invis"] + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation.svg new file mode 100644 index 0000000000..dcfdc2d7d7 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/rbt2-violation.svg @@ -0,0 +1,42 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K3 + +K3 + + + +K2->K3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/tree.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/tree.dot new file mode 100644 index 0000000000..2ede28cba4 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/tree.dot @@ -0,0 +1,10 @@ +digraph { + + 1 -> 2 + 1 -> 3 + + 1 [fontcolor="white"] + 2 [fontcolor="white"] + 3 [fontcolor="white"] + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/tree.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/tree.svg new file mode 100644 index 0000000000..941d3fd96c --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/tree.svg @@ -0,0 +1,42 @@ + + + + + + + + + +1 + +1 + + + +2 + +2 + + + +1->2 + + + + + +3 + +3 + + + +1->3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-augmented.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-augmented.dot new file mode 100644 index 0000000000..7c00f4070d --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-augmented.dot @@ -0,0 +1,18 @@ +digraph { + + K2 -> K1 + K2 -> K3 + K1 -> A + K1 -> B + K3 -> C + K3 -> D + + K1 [color="red",fontcolor="red"] + K2 + K3 [color="red",fontcolor="red"] + A [fontcolor="invis"] + B [fontcolor="invis"] + C [fontcolor="invis"] + D [fontcolor="invis"] + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-augmented.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-augmented.svg new file mode 100644 index 0000000000..22eed60674 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-augmented.svg @@ -0,0 +1,90 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K3 + +K3 + + + +K2->K3 + + + + + +A + +A + + + +K1->A + + + + + +B + +B + + + +K1->B + + + + + +C + +C + + + +K3->C + + + + + +D + +D + + + +K3->D + + + + + diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-forest.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-forest.dot new file mode 100644 index 0000000000..5c4a1dd9ed --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-forest.dot @@ -0,0 +1,8 @@ +digraph { + + K2 -> BH1 + K2 -> BH1 + + BH1 [shape="box",label="black height 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-forest.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-forest.svg new file mode 100644 index 0000000000..84e42a093b --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid-forest.svg @@ -0,0 +1,36 @@ + + + + + + + + + +K2 + +K2 + + + +BH1 + +black height 1 + + + +K2->BH1 + + + + + +K2->BH1 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid.dot b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid.dot new file mode 100644 index 0000000000..d86c85c4f6 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid.dot @@ -0,0 +1,10 @@ +digraph { + + K2 -> K1 + K2 -> K3 + + K1 [color="red",fontcolor="red"] + K2 + K3 [color="red",fontcolor="red"] + +} diff --git a/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid.svg b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid.svg new file mode 100644 index 0000000000..fd0a5823e7 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/Red-Black-Trees/valid.svg @@ -0,0 +1,42 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K3 + +K3 + + + +K2->K3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-1.dot b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-1.dot new file mode 100644 index 0000000000..b185e5b932 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-1.dot @@ -0,0 +1,25 @@ +digraph { + + K2 -> K1 + K2 -> K4 + K4 -> K3 + K4 -> X [color="invis"] + + K1 -> BHn + K1 -> BHn + + K4 -> BHnPlus1 + K3 -> BHnPlus1 + K3 -> BHnPlus1 + + K1 [label="K1 (uncle)"] + K2 [label="K2 (grandparent)"] + K4 [label="K4 (parent)",color="red",fontcolor="red"] + K3 [label="K3 (node)",color="red",fontcolor="red"] + + X [color="invis",fontcolor="invis"] + + BHn [shape="box",label="black height n",style="dotted",fontname="Italic"] + BHnPlus1 [shape="box",label="black height n + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-1.svg b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-1.svg new file mode 100644 index 0000000000..abf59e248e --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-1.svg @@ -0,0 +1,108 @@ + + + + + + + + + +K2 + +K2 (grandparent) + + + +K1 + +K1 (uncle) + + + +K2->K1 + + + + + +K4 + +K4 (parent) + + + +K2->K4 + + + + + +BHn + +black height n + + + +K1->BHn + + + + + +K1->BHn + + + + + +K3 + +K3 (node) + + + +K4->K3 + + + + + +X + +X + + + +K4->X + + + + + +BHnPlus1 + +black height n + 1 + + + +K4->BHnPlus1 + + + + + +K3->BHnPlus1 + + + + + +K3->BHnPlus1 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-2.dot b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-2.dot new file mode 100644 index 0000000000..610524c39d --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-2.dot @@ -0,0 +1,25 @@ +digraph { + + K2 -> K1 + K2 -> K3 + K3 -> BHnPlus1 + K3 -> X [color="invis"] + K3 -> K4 + + K1 -> BHn + K1 -> BHn + + K4 -> BHnPlus1 + K4 -> BHnPlus1 + + K1 [label="K1 (uncle)"] + K2 [label="K2 (grandparent)"] + K3 [label="K3 (parent)",color="red",fontcolor="red"] + K4 [color="red",fontcolor="red"] + + X [color="invis",fontcolor="invis"] + + BHn [shape="box",label="black height n",style="dotted",fontname="Italic"] + BHnPlus1 [shape="box",label="black height n + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-2.svg b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-2.svg new file mode 100644 index 0000000000..be607dfc16 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-2.svg @@ -0,0 +1,108 @@ + + + + + + + + + +K2 + +K2 (grandparent) + + + +K1 + +K1 (uncle) + + + +K2->K1 + + + + + +K3 + +K3 (parent) + + + +K2->K3 + + + + + +BHn + +black height n + + + +K1->BHn + + + + + +K1->BHn + + + + + +BHnPlus1 + +black height n + 1 + + + +K3->BHnPlus1 + + + + + +X + +X + + + +K3->X + + + + + +K4 + +K4 + + + +K3->K4 + + + + + +K4->BHnPlus1 + + + + + +K4->BHnPlus1 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-3.dot b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-3.dot new file mode 100644 index 0000000000..f5af82e278 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-3.dot @@ -0,0 +1,25 @@ +digraph { + + K3 -> K2 + K2 -> K1 + K2 -> X [color="white"] + K3 -> K4 + + K1 -> BHn + K1 -> BHn + + K2 -> BHnPlus1 + K4 -> BHnPlus1 + K4 -> BHnPlus1 + + K1 [label="K1 (uncle)"] + K2 [label="K2 (grandparent)",color="red",fontcolor="red"] + K3 [label="K3 (parent)"] + K4 [color="red",fontcolor="red"] + + X [color="white",fontcolor="white"] + + BHn [shape="box",label="black height n",style="dotted",fontname="Italic"] + BHnPlus1 [shape="box",label="black height n + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-3.svg b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-3.svg new file mode 100644 index 0000000000..84973c30d5 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/black-uncle-3.svg @@ -0,0 +1,108 @@ + + + + + + + + + +K3 + +K3 (parent) + + + +K2 + +K2 (grandparent) + + + +K3->K2 + + + + + +K4 + +K4 + + + +K3->K4 + + + + + +K1 + +K1 (uncle) + + + +K2->K1 + + + + + +X + +X + + + +K2->X + + + + + +BHnPlus1 + +black height n + 1 + + + +K2->BHnPlus1 + + + + + +BHn + +black height n + + + +K1->BHn + + + + + +K1->BHn + + + + + +K4->BHnPlus1 + + + + + +K4->BHnPlus1 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-1.dot b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-1.dot new file mode 100644 index 0000000000..866e20335e --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-1.dot @@ -0,0 +1,28 @@ +digraph { + + K2 -> K1 + K2 -> K3 + K3 -> BHn + K3 -> X1 [color="invis"] + K3 -> K4 + + K1 [label="K1 (uncle)",color="red",fontcolor="red"] + K2 [label="K2 (grandparent)"] + K3 [label="K3 (parent)",color="red",fontcolor="red"] + K4 [color="red",fontcolor="red"] + + X1 [color="invis",fontcolor="invis"] + X2 [color="invis",fontcolor="invis"] + + BHn [shape="box",label="black height n",style="dotted",fontname="Italic"] + + K1 -> X2 [color="invis"] + X2 -> BHn [color="invis"] + + K1 -> BHn + K1 -> BHn + + K4 -> BHn + K4 -> BHn + +} diff --git a/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-1.svg b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-1.svg new file mode 100644 index 0000000000..af1df1bfcb --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-1.svg @@ -0,0 +1,120 @@ + + + + + + + + + +K2 + +K2 (grandparent) + + + +K1 + +K1 (uncle) + + + +K2->K1 + + + + + +K3 + +K3 (parent) + + + +K2->K3 + + + + + +BHn + +black height n + + + +K1->BHn + + + + + +K1->BHn + + + + + +X2 + +X2 + + + +K1->X2 + + + + + +K3->BHn + + + + + +X1 + +X1 + + + +K3->X1 + + + + + +K4 + +K4 + + + +K3->K4 + + + + + +K4->BHn + + + + + +K4->BHn + + + + + +X2->BHn + + + + + diff --git a/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-2.dot b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-2.dot new file mode 100644 index 0000000000..ba98c92abd --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-2.dot @@ -0,0 +1,29 @@ +digraph { + + P -> K2 [dir="back",style="dotted"] + K2 -> K1 + K2 -> K3 + K3 -> BHn + K3 -> X [color="invis"] + K3 -> K4 + + K1 [label="K1 (uncle)"] + K2 [label="K2 (node)",color="red",fontcolor="red"] + K3 [label="K3"] + K4 [color="red",fontcolor="red"] + + X [color="invis",fontcolor="invis"] + Y [color="invis",fontcolor="invis"] + P [label="parent",color="gray",fontcolor="gray"] + + BHn [shape="box",label="black height n",style="dotted",fontname="Italic"] + + K1 -> BHn + K1 -> Y [color="invis"] + Y -> BHn [color="invis"] + K1 -> BHn + + K4 -> BHn + K4 -> BHn + +} diff --git a/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-2.svg b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-2.svg new file mode 100644 index 0000000000..74c1ce1ed5 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/insertNode/red-uncle-2.svg @@ -0,0 +1,132 @@ + + + + + + + + + +P + +parent + + + +K2 + +K2 (node) + + + +P->K2 + + + + + +K1 + +K1 (uncle) + + + +K2->K1 + + + + + +K3 + +K3 + + + +K2->K3 + + + + + +BHn + +black height n + + + +K1->BHn + + + + + +K1->BHn + + + + + +Y + +Y + + + +K1->Y + + + + + +K3->BHn + + + + + +X + +X + + + +K3->X + + + + + +K4 + +K4 + + + +K3->K4 + + + + + +K4->BHn + + + + + +K4->BHn + + + + + +Y->BHn + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-1.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-1.dot new file mode 100644 index 0000000000..1f4b17216a --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-1.dot @@ -0,0 +1,23 @@ +digraph { + + K6 -> K2 + K6 -> BH1 + K2 -> K1 + K2 -> K4 + + K1 -> BH2 + K1 -> BH2 + + K4 -> BH3 + K4 -> BH3 + + K1 [label="K1 (distantNephew)"] + K2 [label="K2 (sibling)"] + K4 [label="K4 (closeNephew)"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-1.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-1.svg new file mode 100644 index 0000000000..b1b5eb559d --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-1.svg @@ -0,0 +1,102 @@ + + + + + + + + + +K6 + +K6 (parent) + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +K4 + +K4 (closeNephew) + + + +K2->K4 + + + + + +BH2 + +black height i + + + +K1->BH2 + + + + + +K1->BH2 + + + + + +BH3 + +black height i + + + +K4->BH3 + + + + + +K4->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-2.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-2.dot new file mode 100644 index 0000000000..374f849fac --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-2.dot @@ -0,0 +1,23 @@ +digraph { + + K6 -> K2 + K6 -> BH1 + K2 -> K1 + K2 -> K4 + + K1 -> BH2 + K1 -> BH2 + + K4 -> BH3 + K4 -> BH3 + + K1 [label="K1 (distantNephew)"] + K2 [label="K2 (sibling)",color="red",fontcolor="red"] + K4 [label="K4 (closeNephew)"] + K6 [label="K6 (node)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-2.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-2.svg new file mode 100644 index 0000000000..f9821e1dc3 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-2.svg @@ -0,0 +1,102 @@ + + + + + + + + + +K6 + +K6 (node) + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +K4 + +K4 (closeNephew) + + + +K2->K4 + + + + + +BH2 + +black height i + + + +K1->BH2 + + + + + +K1->BH2 + + + + + +BH3 + +black height i + + + +K4->BH3 + + + + + +K4->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-3.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-3.dot new file mode 100644 index 0000000000..6f64abcea3 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-3.dot @@ -0,0 +1,28 @@ +digraph { + + P -> BH1 + P -> K6 + + K6 -> K2 + K6 -> BH2 + K2 -> K1 + K2 -> K4 + + K1 -> BH3 + K1 -> BH3 + + K4 -> BH4 + K4 -> BH4 + + P [label="parent",color="gray",fontcolor="gray"] + K1 [label="K1 (distantNephew)"] + K2 [label="K2 (sibling)",color="red",fontcolor="red"] + K4 [label="K4 (closeNephew)"] + K6 [label="K6 (node)"] + + BH1 [shape="box",label="black height i + 3",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i",style="dotted",fontname="Italic"] + BH4 [shape="box",label="black height i",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-3.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-3.svg new file mode 100644 index 0000000000..b00694caa3 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/all-black-3.svg @@ -0,0 +1,126 @@ + + + + + + + + + +P + +parent + + + +BH1 + +black height i + 3 + + + +P->BH1 + + + + + +K6 + +K6 (node) + + + +P->K6 + + + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH2 + +black height i + 1 + + + +K6->BH2 + + + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +K4 + +K4 (closeNephew) + + + +K2->K4 + + + + + +BH3 + +black height i + + + +K1->BH3 + + + + + +K1->BH3 + + + + + +BH4 + +black height i + + + +K4->BH4 + + + + + +K4->BH4 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/initial.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/initial.dot new file mode 100644 index 0000000000..8a43565bb3 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/initial.dot @@ -0,0 +1,14 @@ +digraph { + + P -> BH2 + P -> N + N -> L1 + N -> L2 + + P [label="parent",color="darkgray",fontcolor="darkgray"] + BH2 [shape="box",label="black height 2",style="dotted",fontname="Italic"] + N [label="node"] + L1 [fontcolor="invis"] + L2 [fontcolor="invis"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/initial.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/initial.svg new file mode 100644 index 0000000000..92dea03169 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/initial.svg @@ -0,0 +1,66 @@ + + + + + + + + + +P + +parent + + + +BH2 + +black height 2 + + + +P->BH2 + + + + + +N + +node + + + +P->N + + + + + +L1 + +L1 + + + +N->L1 + + + + + +L2 + +L2 + + + +N->L2 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-entry.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-entry.dot new file mode 100644 index 0000000000..cd2c35d806 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-entry.dot @@ -0,0 +1,10 @@ +digraph { + + P -> BH2 + P -> L + + P [label="parent",color="darkgray",fontcolor="darkgray"] + BH2 [shape="box",label="black height 2",style="dotted",fontname="Italic"] + L [fontcolor="invis"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-entry.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-entry.svg new file mode 100644 index 0000000000..6627c5beb1 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-entry.svg @@ -0,0 +1,42 @@ + + + + + + + + + +P + +parent + + + +BH2 + +black height 2 + + + +P->BH2 + + + + + +L + +L + + + +P->L + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant-a.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant-a.dot new file mode 100644 index 0000000000..c5cdc76547 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant-a.dot @@ -0,0 +1,23 @@ +digraph { + + K6 -> K2 + K6 -> BH1 + K2 -> K1 + K2 -> K4 + + K1 -> BH2 + K1 -> BH2 + + K4 -> BH3 + K4 -> BH3 + + K1 [label="K1 (distantNephew)",color="darkgray",fontcolor="darkgray"] + K2 [label="K2 (sibling)",color="darkgray",fontcolor="darkgray"] + K4 [label="K4 (closeNephew)",color="darkgray",fontcolor="darkgray"] + K6 [label="K6 (parent)",color="darkgray",fontcolor="darkgray"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 2 - \nnumber of black nodes\nin { K1, K2 }",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 2 - \nnumber of black nodes\nin { K2, K4 }",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant-a.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant-a.svg new file mode 100644 index 0000000000..bf68bbe623 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant-a.svg @@ -0,0 +1,106 @@ + + + + + + + + + +K6 + +K6 (parent) + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +K4 + +K4 (closeNephew) + + + +K2->K4 + + + + + +BH2 + +black height i + 2 - +number of black nodes +in { K1, K2 } + + + +K1->BH2 + + + + + +K1->BH2 + + + + + +BH3 + +black height i + 2 - +number of black nodes +in { K2, K4 } + + + +K4->BH3 + + + + + +K4->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant.dot new file mode 100644 index 0000000000..57fa8bcb47 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant.dot @@ -0,0 +1,10 @@ +digraph { + + P -> BHnPlus2 + P -> BHnPlus1 + + P [label="parent",color="gray",fontcolor="gray"] + BHnPlus1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BHnPlus2 [shape="box",label="black height i + 2",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant.svg new file mode 100644 index 0000000000..1b5db2b846 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/loop-invariant.svg @@ -0,0 +1,42 @@ + + + + + + + + + +P + +parent + + + +BHnPlus2 + +black height i + 2 + + + +P->BHnPlus2 + + + + + +BHnPlus1 + +black height i + 1 + + + +P->BHnPlus1 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-1.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-1.dot new file mode 100644 index 0000000000..2e64335344 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-1.dot @@ -0,0 +1,19 @@ +digraph { + + K6 -> K2 + K6 -> BH1 + K2 -> BH2 + K2 -> K4 + + K4 -> BH3 + K4 -> BH3 + + K2 [label="K2 (sibling)"] + K4 [label="K4 (closeNephew)",color="red",fontcolor="red"] + K6 [label="K6 (parent)",color="gray",fontcolor="gray"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-1.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-1.svg new file mode 100644 index 0000000000..52fad8e4ce --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-1.svg @@ -0,0 +1,84 @@ + + + + + + + + + +K6 + +K6 (parent) + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +BH2 + +black height i + 1 + + + +K2->BH2 + + + + + +K4 + +K4 (closeNephew) + + + +K2->K4 + + + + + +BH3 + +black height i + 1 + + + +K4->BH3 + + + + + +K4->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-2.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-2.dot new file mode 100644 index 0000000000..50acf7c60f --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-2.dot @@ -0,0 +1,20 @@ +digraph { + + K6 -> K4 + K6 -> BH1 + + K4 -> K2 + K4 -> BH2 + + K2 -> BH3 + K2 -> BH3 + + K2 [label="K2 (distantNephew)",color="red",fontcolor="red"] + K4 [label="K4 (sibling)"] + K6 [label="K6 (parent)",color="gray",fontcolor="gray"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-2.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-2.svg new file mode 100644 index 0000000000..310cad6c0a --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-2.svg @@ -0,0 +1,84 @@ + + + + + + + + + +K6 + +K6 (parent) + + + +K4 + +K4 (sibling) + + + +K6->K4 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +K2 + +K2 (distantNephew) + + + +K4->K2 + + + + + +BH2 + +black height i + 1 + + + +K4->BH2 + + + + + +BH3 + +black height i + 1 + + + +K2->BH3 + + + + + +K2->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-3.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-3.dot new file mode 100644 index 0000000000..a24c5a69bf --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-3.dot @@ -0,0 +1,19 @@ +digraph { + + K4 -> K2 + K4 -> K6 + + K2 -> BH1 + K2 -> BH1 + + K6 -> BH2 + K6 -> BH2 + + K2 [label="K2 (distantNephew)"] + K4 [label="K4 (sibling)"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-3.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-3.svg new file mode 100644 index 0000000000..10ecdedb61 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-close-nephew-3.svg @@ -0,0 +1,78 @@ + + + + + + + + + +K4 + +K4 (sibling) + + + +K2 + +K2 (distantNephew) + + + +K4->K2 + + + + + +K6 + +K6 (parent) + + + +K4->K6 + + + + + +BH1 + +black height i + 1 + + + +K2->BH1 + + + + + +K2->BH1 + + + + + +BH2 + +black height i + 1 + + + +K6->BH2 + + + + + +K6->BH2 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-1.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-1.dot new file mode 100644 index 0000000000..d1753266e4 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-1.dot @@ -0,0 +1,19 @@ +digraph { + + K6 -> K2 + K6 -> BH1 + K2 -> K1 + K2 -> BH3 + + K1 -> BH2 + K1 -> BH2 + + K1 [label="K1 (distantNephew)",color="red",fontcolor="red"] + K2 [label="K2 (sibling)"] + K6 [label="K6 (parent)",color="gray",fontcolor="gray"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-1.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-1.svg new file mode 100644 index 0000000000..21cde69d6a --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-1.svg @@ -0,0 +1,84 @@ + + + + + + + + + +K6 + +K6 (parent) + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +BH3 + +black height i + 1 + + + +K2->BH3 + + + + + +BH2 + +black height i + 1 + + + +K1->BH2 + + + + + +K1->BH2 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-2.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-2.dot new file mode 100644 index 0000000000..0dc92b7bb2 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-2.dot @@ -0,0 +1,18 @@ +digraph { + + K2 -> K1 + K2 -> K6 + K1 -> BH1 + K1 -> BH1 + + K6 -> BH2 + K6 -> BH2 + + K1 [label="K1 (distantNephew)"] + K2 [label="K2 (sibling)"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-2.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-2.svg new file mode 100644 index 0000000000..3aa1a0359e --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-distant-nephew-2.svg @@ -0,0 +1,78 @@ + + + + + + + + + +K2 + +K2 (sibling) + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +K6 + +K6 (parent) + + + +K2->K6 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +BH2 + +black height i + 1 + + + +K6->BH2 + + + + + +K6->BH2 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-parent.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-parent.dot new file mode 100644 index 0000000000..cfe2f78861 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-parent.dot @@ -0,0 +1,23 @@ +digraph { + + K6 -> K2 + K6 -> BH1 + K2 -> K1 + K2 -> K4 + + K1 -> BH2 + K1 -> BH2 + + K4 -> BH3 + K4 -> BH3 + + K1 [label="K1 (distantNephew)"] + K2 [label="K2 (sibling)",color="red",fontcolor="red"] + K4 [label="K4 (closeNephew)"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-parent.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-parent.svg new file mode 100644 index 0000000000..8d1dc12bc7 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-parent.svg @@ -0,0 +1,102 @@ + + + + + + + + + +K6 + +K6 (parent) + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +K4 + +K4 (closeNephew) + + + +K2->K4 + + + + + +BH2 + +black height i + + + +K1->BH2 + + + + + +K1->BH2 + + + + + +BH3 + +black height i + + + +K4->BH3 + + + + + +K4->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1.dot new file mode 100644 index 0000000000..ecf12de7be --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1.dot @@ -0,0 +1,23 @@ +digraph { + + K6 -> K2 + K6 -> BH1 + K2 -> K1 + K2 -> K4 + + K1 -> BH2 + K1 -> BH2 + + K4 -> BH3 + K4 -> BH3 + + K1 [label="K1 (distantNephew)"] + K2 [label="K2 (sibling)",color="red",fontcolor="red"] + K4 [label="K4 (closeNephew)"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1.svg new file mode 100644 index 0000000000..815c943857 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1.svg @@ -0,0 +1,102 @@ + + + + + + + + + +K6 + +K6 (parent) + + + +K2 + +K2 (sibling) + + + +K6->K2 + + + + + +BH1 + +black height i + 1 + + + +K6->BH1 + + + + + +K1 + +K1 (distantNephew) + + + +K2->K1 + + + + + +K4 + +K4 (closeNephew) + + + +K2->K4 + + + + + +BH2 + +black height i + 1 + + + +K1->BH2 + + + + + +K1->BH2 + + + + + +BH3 + +black height i + 1 + + + +K4->BH3 + + + + + +K4->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1a.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1a.dot new file mode 100644 index 0000000000..6cc5ddfcf2 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1a.dot @@ -0,0 +1,26 @@ +digraph { + + K2 -> K1 + K2 -> K6 + + K1 -> BH1 + K1 -> BH1 + + K6 -> K4 + K6 -> BH2 + + K4 -> BH3 + K4 -> BH4 + + K1 [label="K1"] + K2 [label="K2"] + K4 [label="K4 (sibling)"] + K6 [label="K6 (parent)",color="red",fontcolor="red"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1\nwith root K3 (distantNephew)",style="dotted",fontname="Italic"] + BH4 [shape="box",label="black height i + 1\nwith root K5 (closeNephew)",style="dotted",fontname="Italic"] + + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1a.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1a.svg new file mode 100644 index 0000000000..4d63f6c38f --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-1a.svg @@ -0,0 +1,110 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K6 + +K6 (parent) + + + +K2->K6 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +K4 + +K4 (sibling) + + + +K6->K4 + + + + + +BH2 + +black height i + 1 + + + +K6->BH2 + + + + + +BH3 + +black height i + 1 +with root K3 (distantNephew) + + + +K4->BH3 + + + + + +BH4 + +black height i + 1 +with root K5 (closeNephew) + + + +K4->BH4 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-2.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-2.dot new file mode 100644 index 0000000000..4794fff3b2 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-2.dot @@ -0,0 +1,30 @@ +digraph { + + K2 -> K1 + K2 -> K6 + + K6 -> K4 + K6 -> BH3 + + K1 -> BH1 + K1 -> BH1 + + K4 -> K3 + K3 -> BH2 + K3 -> BH2 + K4 -> BH4 + + + K1 [label="K1"] + K2 [label="K2"] + K3 [label="K3 (distantNephew)",color="red",fontcolor="red"] + K4 [label="K4 (sibling)"] + K6 [label="K6 (parent)",color="red",fontcolor="red"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH4 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-2.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-2.svg new file mode 100644 index 0000000000..d4a8b4343e --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-2.svg @@ -0,0 +1,126 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K6 + +K6 (parent) + + + +K2->K6 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +K4 + +K4 (sibling) + + + +K6->K4 + + + + + +BH3 + +black height i + 1 + + + +K6->BH3 + + + + + +K3 + +K3 (distantNephew) + + + +K4->K3 + + + + + +BH4 + +black height i + 1 + + + +K4->BH4 + + + + + +BH2 + +black height i + 1 + + + +K3->BH2 + + + + + +K3->BH2 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-3.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-3.dot new file mode 100644 index 0000000000..1c72f185b2 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-3.dot @@ -0,0 +1,27 @@ +digraph { + + K2 -> K1 + K2 -> K4 + + K1 -> BH1 + K1 -> BH1 + + K4 -> K3 + K4 -> K6 + + K3 -> BH2 + K3 -> BH2 + K6 -> BH3 + K6 -> BH3 + + K1 [label="K1"] + K2 [label="K2"] + K3 [label="K3 (distantNephew)"] + K4 [label="K4 (sibling)",color="red",fontcolor="red"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-3.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-3.svg new file mode 100644 index 0000000000..9e1a733bab --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-3.svg @@ -0,0 +1,120 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K4 + +K4 (sibling) + + + +K2->K4 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +K3 + +K3 (distantNephew) + + + +K4->K3 + + + + + +K6 + +K6 (parent) + + + +K4->K6 + + + + + +BH2 + +black height i + 1 + + + +K3->BH2 + + + + + +K3->BH2 + + + + + +BH3 + +black height i + 1 + + + +K6->BH3 + + + + + +K6->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-4.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-4.dot new file mode 100644 index 0000000000..38e6d58137 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-4.dot @@ -0,0 +1,29 @@ +digraph { + + K2 -> K1 + K2 -> K6 + + K6 -> K4 + K6 -> BH3 + + K1 -> BH1 + K1 -> BH1 + + K4 -> BH2 + K4 -> K5 + K5 -> BH4 + K5 -> BH4 + + K1 [label="K1"] + K2 [label="K2"] + K4 [label="K4 (sibling)"] + K5 [label="K5 (closeNephew)",color="red",fontcolor="red"] + K6 [label="K6 (parent)",color="red",fontcolor="red"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH4 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-4.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-4.svg new file mode 100644 index 0000000000..39eb4832c7 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-4.svg @@ -0,0 +1,126 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K6 + +K6 (parent) + + + +K2->K6 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +K4 + +K4 (sibling) + + + +K6->K4 + + + + + +BH3 + +black height i + 1 + + + +K6->BH3 + + + + + +BH2 + +black height i + 1 + + + +K4->BH2 + + + + + +K5 + +K5 (closeNephew) + + + +K4->K5 + + + + + +BH4 + +black height i + 1 + + + +K5->BH4 + + + + + +K5->BH4 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-5.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-5.dot new file mode 100644 index 0000000000..5cea180dd2 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-5.dot @@ -0,0 +1,28 @@ +digraph { + + K2 -> K1 + K2 -> K6 + + K6 -> K5 + K6 -> BH3 + + K1 -> BH1 + K1 -> BH1 + + K5 -> K4 + K5 -> BH4 + K4 -> BH2 + + K1 [label="K1"] + K2 [label="K2"] + K4 [label="K4 (distantNephew)",color="red",fontcolor="red"] + K5 [label="K5 (sibling)"] + K6 [label="K6 (parent)",color="red",fontcolor="red"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH4 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-5.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-5.svg new file mode 100644 index 0000000000..96cbea56f8 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-5.svg @@ -0,0 +1,120 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K6 + +K6 (parent) + + + +K2->K6 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +K5 + +K5 (sibling) + + + +K6->K5 + + + + + +BH3 + +black height i + 1 + + + +K6->BH3 + + + + + +K4 + +K4 (distantNephew) + + + +K5->K4 + + + + + +BH4 + +black height i + 1 + + + +K5->BH4 + + + + + +BH2 + +black height i + 1 + + + +K4->BH2 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-6.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-6.dot new file mode 100644 index 0000000000..e6732721b0 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-6.dot @@ -0,0 +1,27 @@ +digraph { + + K2 -> K1 + K2 -> K5 + + K1 -> BH1 + K1 -> BH1 + + K5 -> K4 + K5 -> K6 + + K4 -> BH2 + K4 -> BH2 + K6 -> BH3 + K6 -> BH3 + + K1 [label="K1"] + K2 [label="K2"] + K4 [label="K4 (distantNephew)"] + K5 [label="K5 (sibling)",color="red",fontcolor="red"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-6.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-6.svg new file mode 100644 index 0000000000..77421229f3 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-6.svg @@ -0,0 +1,120 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K5 + +K5 (sibling) + + + +K2->K5 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +K4 + +K4 (distantNephew) + + + +K5->K4 + + + + + +K6 + +K6 (parent) + + + +K5->K6 + + + + + +BH2 + +black height i + 1 + + + +K4->BH2 + + + + + +K4->BH2 + + + + + +BH3 + +black height i + 1 + + + +K6->BH3 + + + + + +K6->BH3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-7.dot b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-7.dot new file mode 100644 index 0000000000..c8198b2134 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-7.dot @@ -0,0 +1,33 @@ +digraph { + + K2 -> K1 + K2 -> K6 + + K6 -> K4 + K6 -> BH3 + + K1 -> BH1 + K1 -> BH1 + + K4 -> K3 + K3 -> BH2 + K3 -> BH2 + K4 -> K5 + K5 -> BH4 + K5 -> BH4 + + + K1 [label="K1"] + K2 [label="K2"] + K3 [label="K3 (distantNephew)"] + K4 [label="K4 (sibling)",color="red",fontcolor="red"] + K5 [label="K5 (closeNephew)"] + K6 [label="K6 (parent)"] + + BH1 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH2 [shape="box",label="black height i",style="dotted",fontname="Italic"] + BH3 [shape="box",label="black height i + 1",style="dotted",fontname="Italic"] + BH4 [shape="box",label="black height i",style="dotted",fontname="Italic"] + + +} diff --git a/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-7.svg b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-7.svg new file mode 100644 index 0000000000..01a460ccb8 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/removeBlackLeafNode/red-sibling-7.svg @@ -0,0 +1,144 @@ + + + + + + + + + +K2 + +K2 + + + +K1 + +K1 + + + +K2->K1 + + + + + +K6 + +K6 (parent) + + + +K2->K6 + + + + + +BH1 + +black height i + 1 + + + +K1->BH1 + + + + + +K1->BH1 + + + + + +K4 + +K4 (sibling) + + + +K6->K4 + + + + + +BH3 + +black height i + 1 + + + +K6->BH3 + + + + + +K3 + +K3 (distantNephew) + + + +K4->K3 + + + + + +K5 + +K5 (closeNephew) + + + +K4->K5 + + + + + +BH2 + +black height i + + + +K3->BH2 + + + + + +K3->BH2 + + + + + +BH4 + +black height i + + + +K5->BH4 + + + + + +K5->BH4 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/rotateSubtree/final.dot b/Fw/DataStructures/docs/diagrams/rotateSubtree/final.dot new file mode 100644 index 0000000000..1740eabcf2 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/rotateSubtree/final.dot @@ -0,0 +1,19 @@ +digraph { + + K1 -> LTK1 + K1 -> K2 + K3 -> K1 + K3 -> GTK3 + K2 -> LTK2GTK1 + K2 -> GTK2LTK3 + + K1 [label="K1 (node)",color="darkgray",fontcolor="darkgray"] + K2 [label="K2 (newChild)",color="darkgray",fontcolor="darkgray"] + K3 [label="K3 (newRoot)",color="darkgray",fontcolor="darkgray"] + + LTK1 [shape="box",label="K < K1",style="dotted",fontname="Italic"] + GTK3 [shape="box",label="K > K3",style="dotted",fontname="Italic"] + LTK2GTK1 [shape="box",label="K1 < K < K2",style="dotted",fontname="Italic"] + GTK2LTK3 [shape="box",label="K2 < K < K3",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/rotateSubtree/final.svg b/Fw/DataStructures/docs/diagrams/rotateSubtree/final.svg new file mode 100644 index 0000000000..df01ad15fb --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/rotateSubtree/final.svg @@ -0,0 +1,90 @@ + + + + + + + + + +K1 + +K1 (node) + + + +LTK1 + +K < K1 + + + +K1->LTK1 + + + + + +K2 + +K2 (newChild) + + + +K1->K2 + + + + + +LTK2GTK1 + +K1 < K < K2 + + + +K2->LTK2GTK1 + + + + + +GTK2LTK3 + +K2 < K < K3 + + + +K2->GTK2LTK3 + + + + + +K3 + +K3 (newRoot) + + + +K3->K1 + + + + + +GTK3 + +K > K3 + + + +K3->GTK3 + + + + + diff --git a/Fw/DataStructures/docs/diagrams/rotateSubtree/initial.dot b/Fw/DataStructures/docs/diagrams/rotateSubtree/initial.dot new file mode 100644 index 0000000000..06a8db5129 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/rotateSubtree/initial.dot @@ -0,0 +1,19 @@ +digraph { + + K1 -> LTK1 + K1 -> K3 + K3 -> K2 + K3 -> GTK3 + K2 -> LTK2GTK1 + K2 -> GTK2LTK3 + + K1 [label="K1 (node)",color="darkgray",fontcolor="darkgray"] + K3 [label="K3 (newRoot)",color="darkgray",fontcolor="darkgray"] + K2 [label="K2 (newChild)",color="darkgray",fontcolor="darkgray"] + + LTK1 [shape="box",label="K < K1",style="dotted",fontname="Italic"] + GTK3 [shape="box",label="K > K3",style="dotted",fontname="Italic"] + LTK2GTK1 [shape="box",label="K1 < K < K2",style="dotted",fontname="Italic"] + GTK2LTK3 [shape="box",label="K2 < K < K3",style="dotted",fontname="Italic"] + +} diff --git a/Fw/DataStructures/docs/diagrams/rotateSubtree/initial.svg b/Fw/DataStructures/docs/diagrams/rotateSubtree/initial.svg new file mode 100644 index 0000000000..b32de03f46 --- /dev/null +++ b/Fw/DataStructures/docs/diagrams/rotateSubtree/initial.svg @@ -0,0 +1,90 @@ + + + + + + + + + +K1 + +K1 (node) + + + +LTK1 + +K < K1 + + + +K1->LTK1 + + + + + +K3 + +K3 (newRoot) + + + +K1->K3 + + + + + +K2 + +K2 (newChild) + + + +K3->K2 + + + + + +GTK3 + +K > K3 + + + +K3->GTK3 + + + + + +LTK2GTK1 + +K1 < K < K2 + + + +K2->LTK2GTK1 + + + + + +GTK2LTK3 + +K2 < K < K3 + + + +K2->GTK2LTK3 + + + + + diff --git a/Fw/DataStructures/test/ut/ExternalRedBlackTreeMapTest.cpp b/Fw/DataStructures/test/ut/ExternalRedBlackTreeMapTest.cpp new file mode 100644 index 0000000000..d4a1085e80 --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalRedBlackTreeMapTest.cpp @@ -0,0 +1,205 @@ +// ====================================================================== +// \title ExternalRedBlackTreeMapTest.cpp +// \author bocchino +// \brief cpp file for ExternalRedBlackTreeMap tests +// ====================================================================== + +#include "Fw/DataStructures/ExternalRedBlackTreeMap.hpp" +#include "Fw/DataStructures/test/ut/ExternalStackTester.hpp" +#include "Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +template +class ExternalRedBlackTreeMapTester { + public: + ExternalRedBlackTreeMapTester(const ExternalRedBlackTreeMap& map) : m_map(map) {} + + const RedBlackTreeSetOrMapImpl& getImpl() const { return this->m_map.m_impl; } + + private: + const ExternalRedBlackTreeMap& m_map; +}; + +namespace MapTest { + +using Impl = RedBlackTreeSetOrMapImpl; +using ImplTester = RedBlackTreeSetOrMapImplTester; +using Map = ExternalRedBlackTreeMap; +using MapTester = ExternalRedBlackTreeMapTester; +using StackTester = ExternalStackTester; + +TEST(ExternalRedBlackTreeMap, ZeroArgConstructor) { + Map map; + ASSERT_EQ(map.getCapacity(), 0); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(ExternalRedBlackTreeMap, TypedStorageConstructor) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + MapTester mapTester(map); + ImplTester implTester(mapTester.getImpl()); + ASSERT_EQ(implTester.getNodes().getElements(), nodes); + StackTester stackTester(implTester.getFreeNodes()); + ASSERT_EQ(stackTester.getItems().getElements(), freeNodes); + ASSERT_EQ(map.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(ExternalRedBlackTreeMap, UntypedStorageConstructor) { + constexpr auto alignment = Map::getByteArrayAlignment(); + constexpr auto byteArraySize = Map::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + Map map(ByteArray(&bytes[0], sizeof bytes), State::capacity); + MapTester mapTester(map); + ImplTester implTester(mapTester.getImpl()); + ASSERT_EQ(implTester.getNodes().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(map.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(ExternalRedBlackTreeMap, CopyConstructor) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + // Call the constructor providing backing storage + Map map1(nodes, freeNodes, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = map1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Map map2(map1); + MapTester mapTester1(map1); + MapTester mapTester2(map2); + ImplTester implTester1(mapTester1.getImpl()); + ImplTester implTester2(mapTester2.getImpl()); + ASSERT_EQ(implTester2.getNodes().getElements(), nodes); + ASSERT_EQ(implTester2.getNodes().getSize(), FwSizeType(State::capacity)); + StackTester stackTester(implTester2.getFreeNodes()); + ASSERT_EQ(stackTester.getItems().getElements(), freeNodes); + ASSERT_EQ(map2.getSize(), 1); +} + +TEST(ExternalRedBlackTreeMap, CopyAssignmentOperator) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + // Call the constructor providing backing storage + Map map1(nodes, freeNodes, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = map1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Map map2; + ASSERT_EQ(map2.getSize(), 0); + // Call the copy assignment operator + map2 = map1; + ASSERT_EQ(map2.getSize(), 1); +} + +TEST(ExternalRedBlackTreeMap, CopyDataFrom) { + constexpr FwSizeType maxSize = 10; + constexpr FwSizeType smallSize = maxSize / 2; + ImplTester::Node nodes1[maxSize]; + ImplTester::Node nodes2[maxSize]; + ImplTester::Index freeNodes1[maxSize]; + ImplTester::Index freeNodes2[maxSize]; + Map m1(nodes1, freeNodes1, maxSize); + // size1 < capacity2 + { + Map m2(nodes2, freeNodes2, maxSize); + State::testCopyDataFrom(m1, smallSize, m2); + } + // size1 == capacity2 + { + Map m2(nodes2, freeNodes2, maxSize); + State::testCopyDataFrom(m1, maxSize, m2); + } + // size1 > capacity2 + { + Map m2(nodes2, freeNodes2, smallSize); + State::testCopyDataFrom(m1, maxSize, m2); + } +} + +TEST(ExternalRedBlackTreeMapScenarios, Clear) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::clear(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, Find) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::find(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, FindExisting) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::findExisting(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, InsertExisting) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::insertExisting(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, InsertFull) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::insertFull(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, InsertNotFull) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::insertNotFull(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, Remove) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::remove(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, RemoveExisting) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::removeExisting(state); +} + +TEST(ExternalRedBlackTreeMapScenarios, Random) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Map map(nodes, freeNodes, State::capacity); + State state(map); + Scenarios::random(Fw::String("ExternalRedBlackTreeMapRandom"), state, 1000); +} + +} // namespace MapTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ExternalRedBlackTreeSetTest.cpp b/Fw/DataStructures/test/ut/ExternalRedBlackTreeSetTest.cpp new file mode 100644 index 0000000000..59ff21b6ea --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalRedBlackTreeSetTest.cpp @@ -0,0 +1,205 @@ +// ====================================================================== +// \title ExternalRedBlackTreeSetTest.cpp +// \author bocchino +// \brief cpp file for ExternalRedBlackTreeSet tests +// ====================================================================== + +#include "Fw/DataStructures/ExternalRedBlackTreeSet.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/ExternalRedBlackTreeSet.hpp" +#include "Fw/DataStructures/test/ut/ExternalStackTester.hpp" +#include "Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp" + +namespace Fw { + +template +class ExternalRedBlackTreeSetTester { + public: + ExternalRedBlackTreeSetTester(const ExternalRedBlackTreeSet& set) : m_set(set) {} + + const RedBlackTreeSetOrMapImpl& getImpl() const { return this->m_set.m_impl; } + + private: + const ExternalRedBlackTreeSet& m_set; +}; + +namespace SetTest { + +using Impl = RedBlackTreeSetOrMapImpl; +using ImplTester = RedBlackTreeSetOrMapImplTester; +using Set = ExternalRedBlackTreeSet; +using SetTester = ExternalRedBlackTreeSetTester; +using StackTester = ExternalStackTester; + +TEST(ExternalRedBlackTreeSet, ZeroArgConstructor) { + Set set; + ASSERT_EQ(set.getCapacity(), 0); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(ExternalRedBlackTreeSet, TypedStorageConstructor) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + SetTester setTester(set); + ImplTester implTester(setTester.getImpl()); + ASSERT_EQ(implTester.getNodes().getElements(), nodes); + StackTester stackTester(implTester.getFreeNodes()); + ASSERT_EQ(stackTester.getItems().getElements(), freeNodes); + ASSERT_EQ(set.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(ExternalRedBlackTreeSet, UntypedStorageConstructor) { + constexpr auto alignment = Set::getByteArrayAlignment(); + constexpr auto byteArraySize = Set::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + Set set(ByteArray(&bytes[0], sizeof bytes), State::capacity); + SetTester setTester(set); + ImplTester implTester(setTester.getImpl()); + ASSERT_EQ(implTester.getNodes().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(set.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(ExternalRedBlackTreeSet, CopyConstructor) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + // Call the constructor providing backing storage + Set set1(nodes, freeNodes, State::capacity); + // Insert an item + const State::ElementType e = 42; + const auto status = set1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Set set2(set1); + SetTester setTester1(set1); + SetTester setTester2(set2); + ImplTester implTester1(setTester1.getImpl()); + ImplTester implTester2(setTester2.getImpl()); + ASSERT_EQ(implTester2.getNodes().getElements(), nodes); + ASSERT_EQ(implTester2.getNodes().getSize(), FwSizeType(State::capacity)); + StackTester stackTester(implTester2.getFreeNodes()); + ASSERT_EQ(stackTester.getItems().getElements(), freeNodes); + ASSERT_EQ(set2.getSize(), 1); +} + +TEST(ExternalRedBlackTreeSet, CopyAssignmentOperator) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + // Call the constructor providing backing storage + Set set1(nodes, freeNodes, State::capacity); + // Insert an item + const State::ElementType e = 42; + const auto status = set1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Set set2; + ASSERT_EQ(set2.getSize(), 0); + // Call the copy assignment operator + set2 = set1; + ASSERT_EQ(set2.getSize(), 1); +} + +TEST(ExternalRedBlackTreeSet, CopyDataFrom) { + constexpr FwSizeType maxSize = 10; + constexpr FwSizeType smallSize = maxSize / 2; + ImplTester::Node nodes1[maxSize]; + ImplTester::Node nodes2[maxSize]; + ImplTester::Index freeNodes1[maxSize]; + ImplTester::Index freeNodes2[maxSize]; + Set s1(nodes1, freeNodes1, maxSize); + // size1 < capacity2 + { + Set s2(nodes2, freeNodes2, maxSize); + State::testCopyDataFrom(s1, smallSize, s2); + } + // size1 == size2 + { + Set s2(nodes2, freeNodes2, maxSize); + State::testCopyDataFrom(s1, maxSize, s2); + } + // size1 > size2 + { + Set s2(nodes2, freeNodes2, smallSize); + State::testCopyDataFrom(s1, maxSize, s2); + } +} + +TEST(ExternalRedBlackTreeSetScenarios, Clear) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::clear(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, Find) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::find(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, FindExisting) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::findExisting(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, InsertExisting) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::insertExisting(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, InsertFull) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::insertFull(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, InsertNotFull) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::insertNotFull(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, Remove) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::remove(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, RemoveExisting) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::removeExisting(state); +} + +TEST(ExternalRedBlackTreeSetScenarios, Random) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + Set set(nodes, freeNodes, State::capacity); + State state(set); + Scenarios::random(Fw::String("ExternalRedBlackTreeSetRandom"), state, 1000); +} + +} // namespace SetTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/ExternalStackTest.cpp b/Fw/DataStructures/test/ut/ExternalStackTest.cpp index 8666ca92e1..58b383e42d 100644 --- a/Fw/DataStructures/test/ut/ExternalStackTest.cpp +++ b/Fw/DataStructures/test/ut/ExternalStackTest.cpp @@ -5,22 +5,12 @@ // ====================================================================== #include "Fw/DataStructures/ExternalStack.hpp" +#include "Fw/DataStructures/test/ut/ExternalStackTester.hpp" #include "Fw/DataStructures/test/ut/STest/StackTestRules.hpp" #include "Fw/DataStructures/test/ut/STest/StackTestScenarios.hpp" namespace Fw { -template -class ExternalStackTester { - public: - ExternalStackTester(const ExternalStack& stack) : m_stack(stack) {} - - const ExternalArray getItems() const { return this->m_stack.m_items; } - - private: - const ExternalStack& m_stack; -}; - namespace StackTest { using TestStack = ExternalStack; diff --git a/Fw/DataStructures/test/ut/ExternalStackTester.hpp b/Fw/DataStructures/test/ut/ExternalStackTester.hpp new file mode 100644 index 0000000000..3f1688a6b0 --- /dev/null +++ b/Fw/DataStructures/test/ut/ExternalStackTester.hpp @@ -0,0 +1,27 @@ +// ====================================================================== +// \title ExternalStackTester.hpp +// \author bocchino +// \brief Tester class for ExternalStack +// ====================================================================== + +#ifndef Fw_ExternalStackTester_HPP +#define Fw_ExternalStackTester_HPP + +#include "Fw/DataStructures/ExternalStack.hpp" + +namespace Fw { + +template +class ExternalStackTester { + public: + ExternalStackTester(const ExternalStack& stack) : m_stack(stack) {} + + const ExternalArray getItems() const { return this->m_stack.m_items; } + + private: + const ExternalStack& m_stack; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/RedBlackTreeMapTest.cpp b/Fw/DataStructures/test/ut/RedBlackTreeMapTest.cpp new file mode 100644 index 0000000000..2086738fc1 --- /dev/null +++ b/Fw/DataStructures/test/ut/RedBlackTreeMapTest.cpp @@ -0,0 +1,158 @@ +// ====================================================================== +// \title RedBlackTreeMapTest.cpp +// \author bocchino +// \brief cpp file for RedBlackTreeMap tests +// ====================================================================== + +#include "Fw/DataStructures/RedBlackTreeMap.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/RedBlackTreeMap.hpp" +#include "Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/MapTestScenarios.hpp" + +namespace Fw { + +template +class RedBlackTreeMapTester { + public: + using Nodes = typename RedBlackTreeMap::Node; + + using FreeNodes = typename RedBlackTreeMap::FreeNodes; + + RedBlackTreeMapTester(RedBlackTreeMap& map) : m_map(map) {} + + const ExternalRedBlackTreeMap& getExtMap() const { return this->m_map.m_extMap; } + + ExternalRedBlackTreeMap& getExtMap() { return this->m_map.m_extMap; } + + const Nodes& getNodes() const { return this->m_map.m_nodes; } + + const FreeNodes& getFreeNodes() const { return this->m_map.m_freeNodes; } + + private: + RedBlackTreeMap& m_map; +}; + +namespace MapTest { + +using Entry = SetOrMapImplEntry; +using ImplTester = RedBlackTreeSetOrMapImplTester; +using Map = RedBlackTreeMap; +using MapTester = RedBlackTreeMapTester; + +TEST(RedBlackTreeMap, ZeroArgConstructor) { + Map map; + ASSERT_EQ(map.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(map.getSize(), 0); +} + +TEST(RedBlackTreeMap, CopyConstructor) { + Map m1; + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = m1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Map m2(m1); + ASSERT_EQ(m2.getSize(), 1); +} + +TEST(RedBlackTreeMap, CopyAssignmentOperator) { + Map m1; + // Insert an item + const State::KeyType key = 0; + State::ValueType value = 42; + auto status = m1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Map m2; + ASSERT_EQ(m2.getSize(), 0); + // Call the copy assignment operator + m2 = m1; + ASSERT_EQ(m2.getSize(), 1); + value = 0; + status = m2.find(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, 42); +} + +TEST(RedBlackTreeMap, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + Map m1; + // size1 < capacity2 + { + Map m2; + State::testCopyDataFrom(m1, smallSize, m2); + } + // size1 == capacity2 + { + Map m2; + State::testCopyDataFrom(m1, maxSize, m2); + } + // size1 > capacity2 + { + RedBlackTreeMap m2; + State::testCopyDataFrom(m1, maxSize, m2); + } +} + +TEST(RedBlackTreeMapScenarios, Clear) { + Map map; + State state(map); + Scenarios::clear(state); +} + +TEST(RedBlackTreeMapScenarios, Find) { + Map map; + State state(map); + Scenarios::find(state); +} + +TEST(RedBlackTreeMapScenarios, FindExisting) { + Map map; + State state(map); + Scenarios::findExisting(state); +} + +TEST(RedBlackTreeMapScenarios, InsertExisting) { + Map map; + State state(map); + Scenarios::insertExisting(state); +} + +TEST(RedBlackTreeMapScenarios, InsertFull) { + Map map; + State state(map); + Scenarios::insertFull(state); +} + +TEST(RedBlackTreeMapScenarios, InsertNotFull) { + Map map; + State state(map); + Scenarios::insertNotFull(state); +} + +TEST(RedBlackTreeMapScenarios, Remove) { + Map map; + State state(map); + Scenarios::remove(state); +} + +TEST(RedBlackTreeMapScenarios, RemoveExisting) { + Map map; + State state(map); + Scenarios::removeExisting(state); +} + +TEST(RedBlackTreeMapScenarios, Random) { + Map map; + State state(map); + Scenarios::random(Fw::String("RedBlackTreeMapRandom"), state, 1000); +} + +} // namespace MapTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTest.cpp b/Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTest.cpp new file mode 100644 index 0000000000..90af3ab90b --- /dev/null +++ b/Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTest.cpp @@ -0,0 +1,380 @@ +// ====================================================================== +// \title RedBlackTreeSetOrMapImplTest.cpp +// \author bocchino +// \brief cpp file for RedBlackTreeSetOrMapImpl tests +// ====================================================================== + +#include + +#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/test/ut/ExternalStackTester.hpp" +#include "Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.hpp" + +namespace Fw { + +namespace RedBlackTreeSetOrMapImplTest { + +using ImplTester = RedBlackTreeSetOrMapImplTester; + +TEST(RedBlackTreeSetOrMapImpl, ZeroArgConstructor) { + State::Impl impl; + ASSERT_EQ(impl.getCapacity(), 0U); + ASSERT_EQ(impl.getSize(), 0U); +} + +TEST(RedBlackTreeSetOrMapImpl, TypedStorageConstructor) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + ASSERT_EQ(state.tester.getNodes().getElements(), nodes); + ExternalStackTester stackTester(state.tester.getFreeNodes()); + ASSERT_EQ(stackTester.getItems().getElements(), freeNodes); + ASSERT_EQ(impl.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(impl.getSize(), 0U); +} + +TEST(RedBlackTreeSetOrMapImpl, UntypedStorageConstructor) { + constexpr auto alignment = State::Impl::getByteArrayAlignment(); + constexpr auto byteArraySize = State::Impl::getByteArraySize(State::capacity); + alignas(alignment) U8 bytes[byteArraySize]; + State::Impl impl(ByteArray(&bytes[0], sizeof bytes), State::capacity); + State::Tester tester(impl); + ASSERT_EQ(tester.getNodes().getElements(), reinterpret_cast(bytes)); + ASSERT_EQ(impl.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(impl.getSize(), 0U); +} + +TEST(RedBlackTreeSetOrMapImpl, CopyConstructor) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + // Call the constructor providing backing storage + State::Impl impl1(nodes, freeNodes, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = impl1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + State::Impl impl2(impl1); + State::Tester tester1(impl1); + State::Tester tester2(impl2); + ASSERT_EQ(tester2.getNodes().getElements(), nodes); + ASSERT_EQ(tester2.getNodes().getSize(), FwSizeType(State::capacity)); + ExternalStackTester stackTester(tester2.getFreeNodes()); + ASSERT_EQ(stackTester.getItems().getElements(), freeNodes); + ASSERT_EQ(impl2.getSize(), 1U); +} + +TEST(RedBlackTreeSetOrMapImpl, CopyAssignmentOperator) { + ImplTester::Node nodes[State::capacity]; + ImplTester::Index freeNodes[State::capacity]; + // Call the constructor providing backing storage + State::Impl impl1(nodes, freeNodes, State::capacity); + // Insert an item + const State::KeyType key = 0; + const State::ValueType value = 42; + const auto status = impl1.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + State::Impl impl2; + ASSERT_EQ(impl2.getSize(), 0U); + // Call the copy assignment operator + impl2 = impl1; + ASSERT_EQ(impl2.getSize(), 1U); +} + +TEST(ArraySetOrMapImpl, IteratorConstruction) { + State::Impl impl; + State::Impl::ConstIterator it(impl); +} + +TEST(RedBlackTreeSetOrMapImpl, IteratorComparison) { + // Test comparison in default case + State::Impl::ConstIterator it1; + State::Impl::ConstIterator it2; + ASSERT_TRUE(it1.compareEqual(it2)); +} + +TEST(RedBlackTreeSetOrMapImplScenarios, Clear) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); + ASSERT_EQ(state.impl.getSize(), 1U); + Rules::clear.apply(state); + ASSERT_EQ(state.impl.getSize(), 0U); +} + +TEST(RedBlackTreeSetOrMapImplScenarios, Find) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + Rules::find.apply(state); + state.useStoredKey = true; + Rules::insertNotFull.apply(state); + Rules::find.apply(state); +} + +TEST(RedBlackTreeSetOrMapImplScenarios, FindExisting) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); + Rules::findExisting.apply(state); +} + +TEST(RedBlackTreeSetOrMapImplScenarios, InsertExisting) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + Rules::insertNotFull.apply(state); + Rules::insertExisting.apply(state); +} + +TEST(RedBlackTreeSetOrMapImplScenarios, InsertFull) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < State::capacity; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + state.useStoredKey = false; + Rules::insertFull.apply(state); +} + +TEST(RedBlackTreeSetOrMapImplScenarios, InsertNotFull) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + const FwSizeType n = 30; + for (FwSizeType i = 0; i < n; i++) { + Rules::insertNotFull.apply(state); + } + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.tester.printBlackHeight(); +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveRoot) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + Rules::insertNotFull.apply(state); + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveRedLeaf) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + Rules::insertNotFull.apply(state); + state.storedKey = 1; + Rules::insertNotFull.apply(state); + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveNodeWithOneChildAtRoot) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < 2; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.storedKey = 0; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveNodeWithOneChildAwayFromRoot) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < 6; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.storedKey = 4; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveNodeWithTwoChildren) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < 3; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.storedKey = 1; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveBlackLeafWithRedDistantNephew) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < 10; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.storedKey = 6; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveBlackLeafWithRedCloseNephew) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + const State::KeyType keys[] = {10313, 13047, 30597, 41133, 11108, 44775, 25209, 12493, 9954, 37137, + 55775, 43881, 45994, 34609, 58674, 16217, 20766, 37020, 26979, 42589, + 34189, 54346, 32929, 41537, 14284, 54076, 19044, 61246, 63806, 14754}; + + const FwSizeType numKeys = FW_NUM_ARRAY_ELEMENTS(keys); + for (FwSizeType i = 0; i < numKeys; i++) { + state.storedKey = keys[i]; + Rules::insertNotFull.apply(state); + } + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.storedKey = 19044; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveBlackLeafWithRedParent) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < 6; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + state.storedKey = 4; + Rules::remove.apply(state); + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.storedKey = 2; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveBlackLeafWithRedSibling) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + state.useStoredKey = true; + for (FwSizeType i = 0; i < 6; i++) { + state.storedKey = static_cast(i); + Rules::insertNotFull.apply(state); + } + state.storedKey = 4; + Rules::remove.apply(state); + std::cout << "Before:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; + state.storedKey = 0; + Rules::remove.apply(state); + std::cout << "After:\n"; + state.tester.printTree(); + std::cout << "size is " << impl.getSize() << "\n"; +} + +TEST(RedBlackTreeSetOrMapImplScenarios, RemoveExisting) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + const FwSizeType m = 10; + const FwSizeType n = 10; + for (FwSizeType i = 0; i < m; i++) { + for (FwSizeType j = 0; j < n; j++) { + Rules::insertNotFull.apply(state); + } + for (FwSizeType j = 0; j < n / 2; j++) { + Rules::removeExisting.apply(state); + } + } +} + +TEST(RedBlackTreeSetOrMapImplScenarios, Random) { + State::Tester::Node nodes[State::capacity]; + State::Tester::Index freeNodes[State::capacity]; + State::Impl impl(nodes, freeNodes, State::capacity); + State state(impl); + Scenarios::random(Fw::String("RedBlackTreeSetOrMapImplRandom"), state, 1000); +} + +} // namespace RedBlackTreeSetOrMapImplTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp b/Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp new file mode 100644 index 0000000000..746a46ba77 --- /dev/null +++ b/Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp @@ -0,0 +1,245 @@ +// ====================================================================== +// \title RedBlackTreeSetOrMapImplTester.hpp +// \author bocchino +// \brief Class template for access to RedBlackTreeSetOrMapImpl members +// ====================================================================== + +#ifndef Fw_RedBlackTreeSetOrMapImplTester_HPP +#define Fw_RedBlackTreeSetOrMapImplTester_HPP + +#include +#include + +#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +template +class RedBlackTreeSetOrMapImplTester { + public: + using Impl = RedBlackTreeSetOrMapImpl; + + using Color = typename Impl::Color; + + using Direction = typename Impl::Direction; + + using FreeNodes = typename Impl::FreeNodes; + + using Index = typename Impl::Index; + + using Node = typename Impl::Node; + + using Nodes = typename Impl::Nodes; + + RedBlackTreeSetOrMapImplTester(const Impl& impl) : m_impl(impl) { + const auto capacity = this->m_impl.getCapacity(); + this->blackHeights.setStorage(new FwSizeType[capacity], capacity); + } + + ~RedBlackTreeSetOrMapImplTester() { + auto* const elements = this->blackHeights.getElements(); + if (elements != nullptr) { + delete[] elements; + } + } + + const Nodes& getNodes() const { return this->m_impl.m_nodes; } + + const FreeNodes& getFreeNodes() const { return this->m_impl.m_freeNodes; } + + Index getRoot() const { return this->m_impl.m_root; } + + // Check properties of the tree + void checkProperties() { + this->checkBstProperty(); + this->checkRbtProperties(); + } + + // Check the BST property of the tree + void checkBstProperty() const { + const auto capacity = this->m_impl.getCapacity(); + auto it = this->m_impl.begin(); + FwSizeType size = 0; + for (FwSizeType i = 0; i < capacity; i++) { + if (!it.isInRange()) { + break; + } + const KE key1 = it.getEntry().getKey(); + size++; + it.increment(); + if (!it.isInRange()) { + break; + } + const KE key2 = it.getEntry().getKey(); + ASSERT_LT(key1, key2); + } + ASSERT_EQ(size, this->m_impl.getSize()); + } + + // Check the red-black tree properties of the tree. Return the black height. + void checkRbtProperties() { + const auto& nodes = this->m_impl.m_nodes; + auto node = this->m_impl.getOuterNodeUnder(this->m_impl.m_root, Direction::LEFT); + const auto capacity = this->m_impl.getCapacity(); + bool done = (capacity > 0); + for (FwSizeType i = 0; i < capacity; i++) { + if (node == Node::NONE) { + done = true; + break; + } + const auto rightChild = nodes[node].getChild(Direction::RIGHT); + if (rightChild != Node::NONE) { + // There is a right child. Go to the leftmost node under that child. + node = this->m_impl.getOuterNodeUnder(rightChild, Direction::LEFT); + } else { + // There is no right child. Go upwards until we pass through a left child + // or we hit the root. + for (FwSizeType j = 0; j < capacity; j++) { + this->checkForRedViolation(node); + this->updateBlackHeight(node); + const auto previousNode = node; + node = nodes[node].m_parent; + if ((node == Node::NONE) or (nodes[node].getChild(Direction::LEFT) == previousNode)) { + break; + } + } + } + } + ASSERT_TRUE(done); + } + + // Get the black height of a node + FwSizeType getBlackHeight(Index node) const { return (node == Node::NONE) ? 1 : this->blackHeights[node]; } + + // Check for a red violation at a node + void checkForRedViolation(Index node) const { + if (this->m_impl.getNodeColor(node) == Color::RED) { + const auto& nodes = this->m_impl.m_nodes; + const auto leftChild = nodes[node].getChild(Direction::LEFT); + ASSERT_NE(this->m_impl.getNodeColor(leftChild), Color::RED) + << "Red child violation at left child\n" + << " node index is " << node << "\n" + << " node key is " << nodes[node].m_entry.getKeyOrElement() << "\n" + << " left child index is " << leftChild << "\n" + << " left child key is " << nodes[leftChild].m_entry.getKeyOrElement() << "\n"; + const auto rightChild = nodes[node].getChild(Direction::RIGHT); + ASSERT_NE(this->m_impl.getNodeColor(rightChild), Color::RED) + << "Red child violation at right child\n" + << " node index is " << node << "\n" + << " node key is " << nodes[node].m_entry.getKeyOrElement() << "\n" + << " right child index is " << rightChild << "\n" + << " right child key is " << nodes[rightChild].m_entry.getKeyOrElement() << "\n"; + } + } + + // Update the black height of a node, after visiting all its descendants. + // Check for a black height violation. + void updateBlackHeight(Index node) { + const auto& nodes = this->m_impl.m_nodes; + if (node != Node::NONE) { + const auto leftChild = nodes[node].getChild(Direction::LEFT); + const auto leftHeight = getBlackHeight(leftChild); + const auto rightChild = nodes[node].getChild(Direction::RIGHT); + const auto rightHeight = getBlackHeight(rightChild); + ASSERT_EQ(leftHeight, rightHeight) << "Black height violation at node " << node << "\n" + << " left child index is " << leftChild << "\n" + << " right child index is " << rightChild << "\n"; + const FwSizeType nodeHeight = (this->m_impl.getNodeColor(node) == Color::BLACK) ? 1 : 0; + const FwSizeType blackHeight = leftHeight + nodeHeight; + this->blackHeights[node] = blackHeight; + } + } + + // Print the black height + void printBlackHeight() { std::cout << "black height is " << getBlackHeight(this->m_impl.m_root) << "\n"; } + + // Print the tree + void printTree() { + auto node = this->m_impl.m_root; + auto child = Node::NONE; + const auto capacity = this->m_impl.getCapacity(); + I32 indent = 0; + constexpr I32 indentIncrement = 2; + const FwSizeType loopBound = 3 * capacity; + for (FwSizeType i = 0; i < loopBound; i++) { + if (node == Node::NONE) { + break; + } + const auto& nodeObject = this->m_impl.m_nodes[node]; + const auto leftChild = nodeObject.m_left; + const auto rightChild = nodeObject.m_right; + if (child == Node::NONE) { + this->printNode(indent, node); + if (leftChild != Node::NONE) { + indent += indentIncrement; + node = leftChild; + } else if (rightChild != Node::NONE) { + indent += indentIncrement; + node = rightChild; + } else { + indent -= indentIncrement; + child = node; + node = nodeObject.m_parent; + } + } else if ((child == leftChild) && (rightChild != Node::NONE)) { + indent += indentIncrement; + node = rightChild; + child = Node::NONE; + } else { + indent -= indentIncrement; + child = node; + node = nodeObject.m_parent; + } + } + } + + // Print a node + void printNode(I32 indent, Index node) { + for (I32 i = 0; i < indent; i++) { + std::cout << " "; + } + if (node == Node::NONE) { + std::cout << "NONE\n"; + } else { + const auto& nodeObject = this->m_impl.m_nodes[node]; + const auto parent = nodeObject.m_parent; + if (parent != Node::NONE) { + const auto parentDirection = this->m_impl.getDirectionFromParent(node); + std::cout << directionToString(parentDirection) << " "; + } + Fw::String parentStr; + indexToString(parent, parentStr); + std::cout << "Node " << node << " { parent=" << parentStr.toChar() + << ", key=" << nodeObject.m_entry.getKeyOrElement() + << ", value=" << nodeObject.m_entry.getValueOrNil() + << ", color=" << colorToString(nodeObject.m_color) << " }\n"; + } + } + + // Convert an index to a string + void indexToString(Index node, Fw::StringBase& str) { + if (node == Node::NONE) { + str = "NONE"; + } else { + str.format("%" PRI_FwSizeType, node); + } + } + + // Convert a color to a string + static const char* colorToString(Color color) { return (color == Color::RED) ? "RED" : "BLACK"; } + + // Convert a direction to a string + static const char* directionToString(Direction direction) { + return (direction == Direction::LEFT) ? "LEFT" : "RIGHT"; + } + + private: + const Impl& m_impl; + // Array for storing black heights + ExternalArray blackHeights = {}; +}; + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/RedBlackTreeSetTest.cpp b/Fw/DataStructures/test/ut/RedBlackTreeSetTest.cpp new file mode 100644 index 0000000000..4dbe55d574 --- /dev/null +++ b/Fw/DataStructures/test/ut/RedBlackTreeSetTest.cpp @@ -0,0 +1,154 @@ +// ====================================================================== +// \title RedBlackTreeSetTest.cpp +// \author bocchino +// \brief cpp file for RedBlackTreeSet tests +// ====================================================================== + +#include "Fw/DataStructures/RedBlackTreeSet.hpp" +#include "STest/STest/Pick/Pick.hpp" + +#include "Fw/DataStructures/RedBlackTreeSet.hpp" +#include "Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestRules.hpp" +#include "Fw/DataStructures/test/ut/STest/SetTestScenarios.hpp" + +namespace Fw { + +template +class RedBlackTreeSetTester { + public: + using Nodes = typename RedBlackTreeSet::Node; + + using FreeNodes = typename RedBlackTreeSet::FreeNodes; + + RedBlackTreeSetTester(RedBlackTreeSet& map) : m_map(map) {} + + const ExternalRedBlackTreeSet& getExtMap() const { return this->m_map.m_extMap; } + + ExternalRedBlackTreeSet& getExtMap() { return this->m_map.m_extMap; } + + const Nodes& getNodes() const { return this->m_map.m_nodes; } + + const FreeNodes& getFreeNodes() const { return this->m_map.m_freeNodes; } + + private: + RedBlackTreeSet& m_map; +}; + +namespace SetTest { + +using Entry = SetOrMapImplEntry; +using ImplTester = RedBlackTreeSetOrMapImplTester; +using Set = RedBlackTreeSet; +using SetTester = RedBlackTreeSetTester; + +TEST(RedBlackTreeSet, ZeroArgConstructor) { + Set set; + ASSERT_EQ(set.getCapacity(), FwSizeType(State::capacity)); + ASSERT_EQ(set.getSize(), 0); +} + +TEST(RedBlackTreeSet, CopyConstructor) { + Set s1; + // Insert an item + const State::ElementType e = 42; + const auto status = s1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the copy constructor + Set s2(s1); + ASSERT_EQ(s2.getSize(), 1); +} + +TEST(RedBlackTreeSet, CopyAssignmentOperator) { + Set s1; + // Insert an item + const State::ElementType e = 42; + auto status = s1.insert(e); + ASSERT_EQ(status, Success::SUCCESS); + // Call the default constructor + Set s2; + ASSERT_EQ(s2.getSize(), 0); + // Call the copy assignment operator + s2 = s1; + ASSERT_EQ(s2.getSize(), 1); + status = s2.find(e); + ASSERT_EQ(status, Success::SUCCESS); +} + +TEST(RedBlackTreeSet, CopyDataFrom) { + constexpr FwSizeType maxSize = State::capacity; + constexpr FwSizeType smallSize = maxSize / 2; + Set s1; + // size1 < capacity2 + { + Set s2; + State::testCopyDataFrom(s1, smallSize, s2); + } + // size1 == capacity2 + { + Set s2; + State::testCopyDataFrom(s1, maxSize, s2); + } + // size1 > capacity2 + { + RedBlackTreeSet s2; + State::testCopyDataFrom(s1, maxSize, s2); + } +} + +TEST(RedBlackTreeSetScenarios, Clear) { + Set set; + State state(set); + Scenarios::clear(state); +} + +TEST(RedBlackTreeSetScenarios, Find) { + Set set; + State state(set); + Scenarios::find(state); +} + +TEST(RedBlackTreeSetScenarios, FindExisting) { + Set set; + State state(set); + Scenarios::findExisting(state); +} + +TEST(RedBlackTreeSetScenarios, InsertExisting) { + Set set; + State state(set); + Scenarios::insertExisting(state); +} + +TEST(RedBlackTreeSetScenarios, InsertFull) { + Set set; + State state(set); + Scenarios::insertFull(state); +} + +TEST(RedBlackTreeSetScenarios, InsertNotFull) { + Set set; + State state(set); + Scenarios::insertNotFull(state); +} + +TEST(RedBlackTreeSetScenarios, Remove) { + Set set; + State state(set); + Scenarios::remove(state); +} + +TEST(RedBlackTreeSetScenarios, RemoveExisting) { + Set set; + State state(set); + Scenarios::removeExisting(state); +} + +TEST(RedBlackTreeSetScenarios, Random) { + Set set; + State state(set); + Scenarios::random(Fw::String("RedBlackTreeSetRandom"), state, 1000); +} + +} // namespace SetTest +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.cpp b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.cpp new file mode 100644 index 0000000000..72af2c4375 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.cpp @@ -0,0 +1,35 @@ +// ====================================================================== +// \title RedBlackTreeSetOrMapImplTestRules.cpp +// \author Rob Bocchino +// \brief cpp file for RedBlackTreeSetOrMapImpl test rules +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.hpp" + +namespace Fw { + +namespace RedBlackTreeSetOrMapImplTest { + +namespace Rules { + +Clear clear; + +Find find; + +FindExisting findExisting; + +InsertExisting insertExisting; + +InsertFull insertFull; + +InsertNotFull insertNotFull; + +Remove remove; + +RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace RedBlackTreeSetOrMapImplTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.hpp b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.hpp new file mode 100644 index 0000000000..e29ef8d356 --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.hpp @@ -0,0 +1,199 @@ +// ====================================================================== +// \title RedBlackTreeSetOrMapImplTestRules.hpp +// \author bocchino +// \brief hpp file for RedBlackTreeSetOrMapImpl test rules +// ====================================================================== + +#ifndef RedBlackTreeSetOrMapImplTestRules_HPP +#define RedBlackTreeSetOrMapImplTestRules_HPP + +#include + +#include "Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestState.hpp" +#include "STest/STest/Pick/Pick.hpp" +#include "STest/STest/Rule/Rule.hpp" + +namespace Fw { + +namespace RedBlackTreeSetOrMapImplTest { + +using Rule = STest::Rule; + +namespace Rules { + +struct Clear : public Rule { + Clear() : Rule("Clear") {} + bool precondition(const State& state) { return state.impl.getSize() > 0; } + void action(State& state) { + state.impl.clear(); + ASSERT_EQ(state.impl.getSize(), 0U); + state.modelMap.clear(); + } +}; + +struct Find : public Rule { + Find() : Rule("Find") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + const auto key = state.getKey(); + State::ValueType value = 0; + const auto status = state.impl.find(key, value); + if (state.modelMapContains(key)) { + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, state.modelMap[key]); + } else { + ASSERT_EQ(status, Success::FAILURE); + } + } +}; + +struct FindExisting : public Rule { + FindExisting() : Rule("FindExisting") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) > 0; } + void action(State& state) { + const auto size = state.impl.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.impl.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it.increment(); + } + ASSERT_TRUE(it.isInRange()); + const auto key = it.getEntry().getKeyOrElement(); + const auto expectedValue = state.modelMap[key]; + State::ValueType value = 0; + const auto status = state.impl.find(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, expectedValue); + } +}; + +struct InsertExisting : public Rule { + InsertExisting() : Rule("InsertExisting") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) > 0; } + void action(State& state) { + state.tester.checkProperties(); + const auto size = state.impl.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.impl.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it.increment(); + } + ASSERT_TRUE(it.isInRange()); + const auto key = it.getEntry().getKeyOrElement(); + const auto value = state.getValue(); + const auto status = state.impl.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + state.modelMap[key] = value; + ASSERT_EQ(state.impl.getSize(), size); + state.tester.checkProperties(); + } +}; + +struct InsertFull : public Rule { + InsertFull() : Rule("InsertFull") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) >= State::capacity; } + void action(State& state) { + state.tester.checkProperties(); + const auto key = state.getKey(); + const auto value = state.getValue(); + const auto size = state.impl.getSize(); + const auto expectedStatus = state.modelMapContains(key) ? Success::SUCCESS : Success::FAILURE; + const auto status = state.impl.insert(key, value); + ASSERT_EQ(status, expectedStatus); + ASSERT_EQ(state.impl.getSize(), size); + state.tester.checkProperties(); + } +}; + +struct InsertNotFull : public Rule { + InsertNotFull() : Rule("InsertNotFull") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) < State::capacity; } + void action(State& state) { + state.tester.checkProperties(); + const auto key = state.getKey(); + const auto value = state.getValue(); + const auto size = state.impl.getSize(); + const auto expectedSize = state.modelMapContains(key) ? size : size + 1; + const auto status = state.impl.insert(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(state.impl.getSize(), expectedSize); + state.modelMap[key] = value; + state.tester.checkProperties(); + } +}; + +struct Remove : public Rule { + Remove() : Rule("Remove") {} + bool precondition(const State& state) { return true; } + void action(State& state) { + state.tester.checkProperties(); + const auto size = state.impl.getSize(); + ASSERT_EQ(size, state.modelMap.size()); + const auto key = state.getKey(); + State::ValueType value = 0; + const auto status = state.impl.remove(key, value); + if (state.modelMap.count(key) != 0) { + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, state.modelMap[key]); + ASSERT_EQ(state.impl.getSize(), size - 1); + } else { + ASSERT_EQ(status, Success::FAILURE); + ASSERT_EQ(state.impl.getSize(), size); + } + (void)state.modelMap.erase(key); + ASSERT_EQ(state.impl.getSize(), state.modelMap.size()); + state.tester.checkProperties(); + } +}; + +struct RemoveExisting : public Rule { + RemoveExisting() : Rule("RemoveExisting") {} + bool precondition(const State& state) { return static_cast(state.impl.getSize()) > 0; } + void action(State& state) { + state.tester.checkProperties(); + const auto size = state.impl.getSize(); + const auto index = STest::Pick::startLength(0, static_cast(size)); + auto it = state.impl.begin(); + for (FwSizeType i = 0; i < index; i++) { + ASSERT_TRUE(it.isInRange()); + it.increment(); + } + ASSERT_TRUE(it.isInRange()); + const auto key = it.getEntry().getKeyOrElement(); + const auto expectedValue = state.modelMap[key]; + State::ValueType value = 0; + const auto status = state.impl.remove(key, value); + ASSERT_EQ(status, Success::SUCCESS); + ASSERT_EQ(value, expectedValue); + const auto n = state.modelMap.erase(key); + ASSERT_EQ(n, 1U); + ASSERT_EQ(state.impl.getSize(), state.modelMap.size()); + state.tester.checkProperties(); + } +}; + +extern Clear clear; + +extern Find find; + +extern FindExisting findExisting; + +extern InsertExisting insertExisting; + +extern InsertFull insertFull; + +extern InsertNotFull insertNotFull; + +extern Remove remove; + +extern RemoveExisting removeExisting; + +} // namespace Rules + +} // namespace RedBlackTreeSetOrMapImplTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.cpp b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.cpp new file mode 100644 index 0000000000..8b973dcc6c --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.cpp @@ -0,0 +1,33 @@ +// ====================================================================== +// \title RedBlackTreeSetOrMapImplTestScenarios.cpp +// \author Rob Bocchino +// \brief RedBlackTreeSetOrMapImpl test scenarios +// ====================================================================== + +#include "Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.hpp" +#include "Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestRules.hpp" +#include "STest/Scenario/BoundedScenario.hpp" +#include "STest/Scenario/RandomScenario.hpp" + +namespace Fw { + +namespace RedBlackTreeSetOrMapImplTest { + +namespace Scenarios { + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps) { + Rule* rules[] = {//&Rules::clear, + &Rules::find, &Rules::findExisting, &Rules::insertExisting, &Rules::insertFull, + &Rules::insertNotFull, &Rules::remove, &Rules::removeExisting}; + STest::RandomScenario scenario("RandomScenario", rules, + sizeof(rules) / sizeof(STest::RandomScenario*)); + STest::BoundedScenario boundedScenario(name.toChar(), scenario, maxNumSteps); + const U32 numSteps = boundedScenario.run(state); + printf("Ran %u steps.\n", numSteps); +} + +} // namespace Scenarios + +} // namespace RedBlackTreeSetOrMapImplTest + +} // namespace Fw diff --git a/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.hpp b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.hpp new file mode 100644 index 0000000000..a91047026b --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestScenarios.hpp @@ -0,0 +1,26 @@ +// ====================================================================== +// \title RedBlackTreeSetOrMapImplTestScenarios.hpp +// \author Rob Bocchino +// \brief RedBlackTreeSetOrMapImpl test scenarios +// ====================================================================== + +#ifndef RedBlackTreeSetOrMapImplTestScenarios_HPP +#define RedBlackTreeSetOrMapImplTestScenarios_HPP + +#include "Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestState.hpp" + +namespace Fw { + +namespace RedBlackTreeSetOrMapImplTest { + +namespace Scenarios { + +void random(const Fw::StringBase& name, State& state, U32 maxNumSteps); + +} // namespace Scenarios + +} // namespace RedBlackTreeSetOrMapImplTest + +} // namespace Fw + +#endif diff --git a/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestState.hpp b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestState.hpp new file mode 100644 index 0000000000..3af5b78e3e --- /dev/null +++ b/Fw/DataStructures/test/ut/STest/RedBlackTreeSetOrMapImplTestState.hpp @@ -0,0 +1,61 @@ +// ====================================================================== +// \title RedBlackTreeSetOrMapImplTestState.hpp +// \author bocchino +// \brief hpp file for RedBlackTreeSetOrMapImpl test state +// ====================================================================== + +#ifndef RedBlackTreeSetOrMapImplTestState_HPP +#define RedBlackTreeSetOrMapImplTestState_HPP + +#include + +#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp" +#include "Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp" +#include "STest/STest/Pick/Pick.hpp" + +namespace Fw { + +namespace RedBlackTreeSetOrMapImplTest { + +struct State { + //! The key type + using KeyType = U16; + //! The value type + using ValueType = U32; + //! The array set or map capacity + static constexpr FwSizeType capacity = 1024; + //! The Impl type + using Impl = RedBlackTreeSetOrMapImpl; + //! The Tester type + using Tester = RedBlackTreeSetOrMapImplTester; + //! The entry type + using Entry = SetOrMapImplEntry; + //! Constructor + State(Impl& a_impl) : impl(a_impl), tester(a_impl) {} + //! The array set or map under test + Impl& impl; + //! The tester + Tester tester; + //! The map for modeling correct behavior + std::map modelMap; + //! Whether to use the stored key + bool useStoredKey = false; + //! The stored key + KeyType storedKey = 0; + //! Whether to use the stored value + bool useStoredValue = false; + //! The stored value + ValueType storedValue = 0; + //! Get a key + KeyType getKey() const { return useStoredKey ? storedKey : static_cast(STest::Pick::any()); } + //! Get a value + ValueType getValue() const { return useStoredValue ? storedValue : static_cast(STest::Pick::any()); } + //! Check whether the model map contains the specified key + bool modelMapContains(KeyType key) const { return modelMap.count(key) != 0; } +}; + +} // namespace RedBlackTreeSetOrMapImplTest + +} // namespace Fw + +#endif