fprime/Fw/DataStructures/test/ut/RedBlackTreeSetOrMapImplTester.hpp
Rob Bocchino e9d7f3ab66
Data structure library, phase 2 (#4062)
* Revise RedBlackTree impl docs

* Revise RedBlackTree impl docs

* Revise RedBlackTree impl docs

* Revise RedBlackTree impl docs

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl docs

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Fix typo

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl docs

* Revise RedBlackTree impl docs

* Revise RedBlackTree impl

* Revise RedBlackTreeImpl docs

* Revise Fw/DataStructures

Reformat code

* Revise Fw/DataStructures

Fix compile errors on Linux

* Revise Fw/DataStructures

Fix compile errors on Linux

* Revise Fw/DataStructures

Fix compile errors on Linux

* Revise Fw/DataStructures

Fix compile errors on Linux

* Fix comments

* Revise Fw/DataStructures

Fix compile errors on Linux

* Revise ArrayMap docs

* Remove helper scripts

* Revise tests for Fw/DataStructures

* Fix spelling

* Fix Markdown link

* Fix uninitialized variable in test

* Fix uninitialized variable in test

* Fix "spelling"

Why is the spelling check enforcing arbitrary rules of style?

* Fix comments

* Revise RedBlackTree impl docs

* Start adding RedBlackTree impl

* Revise tests for ArraySetOrMapImpl

* Revise RedBlackTree impl

* Revise comment

* Revise comment

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise ArraySetOrMap impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Add RedBlackTree impl tester

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Fix comments

* Revise RedBlackTree impl

* Fix comments

* Revise comments

* Revise comments

* Revise comments

* Revise statement order for clarity

* Revise unit tests for RedBlackTree impl

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Add build and clean scripts

Working around regressions in the F Prime build system

* Revise build

Fix warnings

* Revise RedBlackTree impl and tests

* Revise tests for RedBlackTree impl

* Revise RedBlackTree impl and tests

* Revise RedBlackTree tests

Remove debug print statements

* Revise RedBlackTree impl tester

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise build script

* Revise RedBlackTree tests

* Revise RedBlackTree tests

* Revise RedBlackTree tests

* Revise RedBlackTree tests

* Revise RedBlackTree tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl test

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise comment

* Revise RedBlackTree impl and tests

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl tests

* Revise RedBlackTree impl

* Revise RedBlackTree impl tests

* Revise formatting

* Format code

* Revise docs

* Revise docs for Fw/DataStructures

* Revise Array and ExternalArray

Add static assertions

* Revise FifoQueue and Stack

Add static assertions

* Revise ArraySet and ArrayMap

Add static assertions

* Revise docs for ExternalRedBlackTreeMap

* Revise docs for ExternalRedBlackTreeSet

* Revise docs for RedBlackTreeMap

* Revise docs for RedBlackTreeSet

* Revise ExternalRedBlackTreeMap tests

* Refactor ExternalStackTester

* Revise ArrayMap tests

* Add RedBlackTreeMap

* Revise RedBlackTreeMap tests

* Revise RedBlackTreeMap

* Add ExternalRedBlackTreeSet

* Add missing file

* Revise SetConstIterator

* Revise ExternalRedBlackTreeSet tests

* Revise ExternalRedBlackTreeSet tests

* Revise ExternalRedBlackTreeSet tests

* Revise ExternalRedBlackTreeSet tests

* Revise ExternalRedBlackTreeSet tests

* Revise ExternalRedBlackTree tests

* Revise ExternalArraySet tests

* Revise ExternalArraySet tests

* Add RedBlackTreeSet

* Revise RedBlackTreeSet tests

* Refactor ArraySetTest

* Revise RedBlackTreeSetOrMapImpl docs

* Revise array initialization

* Revise comments

* Revise Array initialization

* Revise Array design and implementation

* Revert changes to Fw/DataStructures

* Revise Array

* Revise Array

* Revise Array

* Fix formatting

* Add SizedContainer base class

* Revise StackBase

Make it inherit from SizedContainer
Revise stack tests

* Revise MapBase

Make it inherit from SizedContainer
Revise tests

* Revise SetBase

Make it inherit from SizedContainer
Revise tests

* Revise DataStructures design

Add SizedContainer

* Revise SDD for DataStructures

* Revise DataStructures design

* Revise DataStructures design

* Revise DataStructures design

* Revise DataStructures design

* Fix spelling

* Revise zero-arg constructor for Array

* Revise Array interface

Make it consistent with the arrays generated by FPP

* Fix to assertion

* Format code

* Fix spelling

* Fix spelling

* Add -Wno-comment to suppress warnings for now

* Revise comments

* Revise diagrams

* Fix comment warnings on gcc

* Eliminate tabs

* Remove utility scripts

* Revise placement of break statements

* Rename function

getParentDirection --> getDirectionFromParent

* Add comment

* Add svg diagram

* Add svg diagram

* Add svg files

* Revise diagram

* Replace png with svg

* Replace png with svg

* Replace png with svg

* Revise comment

* Revise SDD for red-black tree

* Revise SDD for red-black tree
2025-10-06 13:26:23 -07:00

246 lines
9.3 KiB
C++

// ======================================================================
// \title RedBlackTreeSetOrMapImplTester.hpp
// \author bocchino
// \brief Class template for access to RedBlackTreeSetOrMapImpl members
// ======================================================================
#ifndef Fw_RedBlackTreeSetOrMapImplTester_HPP
#define Fw_RedBlackTreeSetOrMapImplTester_HPP
#include <gtest/gtest.h>
#include <ostream>
#include "Fw/DataStructures/RedBlackTreeSetOrMapImpl.hpp"
#include "STest/STest/Pick/Pick.hpp"
namespace Fw {
template <typename KE, typename VN>
class RedBlackTreeSetOrMapImplTester {
public:
using Impl = RedBlackTreeSetOrMapImpl<KE, VN>;
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<KE, VN>(const Impl& impl) : m_impl(impl) {
const auto capacity = this->m_impl.getCapacity();
this->blackHeights.setStorage(new FwSizeType[capacity], capacity);
}
~RedBlackTreeSetOrMapImplTester<KE, VN>() {
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<FwSizeType> blackHeights = {};
};
} // namespace Fw
#endif