From 8bb5b9ffe784458d5209a867c4d5ae2bd7d92eb5 Mon Sep 17 00:00:00 2001 From: Yuchen Liang Date: Wed, 10 Jan 2024 22:19:28 -0500 Subject: [PATCH] feat: add observe remove set stubs for p0 Signed-off-by: Yuchen Liang --- CMakeLists.txt | 10 +- src/include/primer/orset.h | 65 ++++++++ src/include/primer/orset_driver.h | 123 ++++++++++++++ src/primer/.clang-tidy | 10 -- src/primer/CMakeLists.txt | 2 + src/primer/orset.cpp | 50 ++++++ src/primer/orset_driver.cpp | 43 +++++ test/primer/orset_test.cpp | 267 ++++++++++++++++++++++++++++++ 8 files changed, 553 insertions(+), 17 deletions(-) create mode 100644 src/include/primer/orset.h create mode 100644 src/include/primer/orset_driver.h delete mode 100644 src/primer/.clang-tidy create mode 100644 src/primer/orset.cpp create mode 100644 src/primer/orset_driver.cpp create mode 100644 test/primer/orset_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f7c14d749..9bfd0e6bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,14 +250,10 @@ add_custom_target(fix-clang-tidy-diff # hardcode some files to check here for each project. # ########################################################## set(P0_FILES - "src/include/primer/trie_answer.h" - "src/include/primer/trie_store.h" - "src/include/primer/trie.h" - "src/primer/trie_store.cpp" - "src/primer/trie.cpp" - "src/planner/plan_func_call.cpp" - "src/include/execution/expressions/string_expression.h" + "src/include/primer/orset.h" + "src/primer/orset.cpp" ) + add_custom_target(check-clang-tidy-p0 ${BUSTUB_BUILD_SUPPORT_DIR}/run_clang_tidy.py # run LLVM's clang-tidy script -clang-tidy-binary ${CLANG_TIDY_BIN} # using our clang-tidy binary diff --git a/src/include/primer/orset.h b/src/include/primer/orset.h new file mode 100644 index 000000000..f602baa26 --- /dev/null +++ b/src/include/primer/orset.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +namespace bustub { + +/** @brief Unique ID type. */ +using uid_t = int64_t; + +/** @brief The observed remove set datatype. */ +template +class ORSet { + public: + ORSet() = default; + + /** + * @brief Checks if an element is in the set. + * + * @param elem the element to check + * @return true if the element is in the set, and false otherwise. + */ + auto Contains(const T &elem) const -> bool; + + /** + * @brief Adds an element to the set. + * + * @param elem the element to add + * @param uid unique token associated with the add operation. + */ + void Add(const T &elem, uid_t uid); + + /** + * @brief Removes an element from the set if it exists. + * + * @param elem the element to remove. + */ + void Remove(const T &elem); + + /** + * @brief Merge changes from another ORSet. + * + * @param other another ORSet + */ + void Merge(const ORSet &other); + + /** + * @brief Gets all the elements in the set. + * + * @return all the elements in the set. + */ + auto Elements() const -> std::vector; + + /** + * @brief Gets a string representation of the set. + * + * @return a string representation of the set. + */ + auto ToString() const -> std::string; + + private: + // TODO(student): Add your private memeber variables to represent ORSet. +}; + +} // namespace bustub diff --git a/src/include/primer/orset_driver.h b/src/include/primer/orset_driver.h new file mode 100644 index 000000000..61da8f490 --- /dev/null +++ b/src/include/primer/orset_driver.h @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include +#include "primer/orset.h" + +namespace bustub { + +/** @brief Unique ID type. */ +using uid_t = int64_t; + +template +class ORSetDriver; + +template +class ORSetNode { + public: + explicit ORSetNode(ORSetDriver *driver, size_t node_id) : driver_(driver), node_id_(node_id) {} + + /** + * @brief Adds an element to the local ORSet. + * + * @param elem the element to add + */ + inline void Add(const T &elem) { orset_.Add(elem, driver_->GenerateUid()); } + + /** + * @brief Removes an element from the local ORSet. + * + * @param elem the element to remove. + */ + inline void Remove(const T &elem) { orset_.Remove(elem); } + + /** + * @brief Checks if an element is in the local ORSet. + * + * @param elem the element to check + * @return true if the element is in the set, and false otherwise. + */ + inline auto Contains(const T &elem) -> bool { return orset_.Contains(elem); } + + /** + * @brief Merges another ORSet to the local ORSet. + * + * @param to_be_merged the ORSet to be merged. + */ + inline void Merge(const ORSet to_be_merged) { orset_.Merge(to_be_merged); } + + /** + * @brief Saves all local changes to the driver. + */ + void Save(); + + /** + * @brief Loads all the remote changes to the local ORSet. + */ + void Load(); + + /** + * @brief Gets a copy of the local ORSet. + * + * @return the local ORSet. + */ + inline auto GetORSet() -> ORSet { return orset_; } + + private: + /** @brief The local ORSet. */ + ORSet orset_; + + /** @brief ORSet Driver. */ + ORSetDriver *driver_; + + /** @brief node id */ + size_t node_id_; +}; + +/** @brief A driver class for managing ORSets. */ +template +class ORSetDriver { + friend class ORSetNode; + + public: + explicit ORSetDriver(size_t num_orset_node); + + /** + * @brief Gets the ORSetNode at index. + */ + inline auto operator[](size_t index) -> std::unique_ptr> & { return orset_nodes_[index]; } + auto operator[](size_t index) const -> const std::unique_ptr> & { return orset_nodes_[index]; } + + /** + * @brief Gets the ORSet node at index. + * + * @param index index of the ORSet node. + * @return the ORSet node associated with the index. + */ + inline auto At(size_t index) -> std::unique_ptr> & { return orset_nodes_[index]; } + + /** + * @brief Saves changes in all nodes and then load all the changes. + */ + void Sync(); + + private: + /** + * @brief Generates a unique id. + * + * @return a unique id. + */ + inline auto GenerateUid() -> uid_t { return next_uid_++; } + + /** @brief A list of ORSet nodes. */ + std::vector>> orset_nodes_; + + /** @brief List of saved copies of ORSet. */ + std::vector> saved_copies_; + + /** @brief Monotonically increasing unique id for the elements. */ + std::atomic next_uid_ = 0; +}; + +} // namespace bustub diff --git a/src/primer/.clang-tidy b/src/primer/.clang-tidy deleted file mode 100644 index 034363cbe..000000000 --- a/src/primer/.clang-tidy +++ /dev/null @@ -1,10 +0,0 @@ ---- -Checks: ' - -modernize-use-trailing-return-type, - ' -InheritParentConfig: true - -#### Disabled checks and why: ##### -# -# We didn't introduce this rule for project 0, so we're going to disable it solely for project 0. -# For future semesters, developers should remove this `.clang-tidy` file. diff --git a/src/primer/CMakeLists.txt b/src/primer/CMakeLists.txt index 346e874df..a75d46484 100644 --- a/src/primer/CMakeLists.txt +++ b/src/primer/CMakeLists.txt @@ -1,6 +1,8 @@ add_library( bustub_primer OBJECT + orset.cpp + orset_driver.cpp trie.cpp trie_store.cpp) diff --git a/src/primer/orset.cpp b/src/primer/orset.cpp new file mode 100644 index 000000000..a926bef00 --- /dev/null +++ b/src/primer/orset.cpp @@ -0,0 +1,50 @@ +#include "primer/orset.h" +#include +#include +#include +#include "common/exception.h" +#include "fmt/format.h" + +namespace bustub { + +template +auto ORSet::Contains(const T &elem) const -> bool { + // TODO(student): Implement this + throw NotImplementedException("ORSet::Contains is not implemented"); +} + +template +void ORSet::Add(const T &elem, uid_t uid) { + // TODO(student): Implement this + throw NotImplementedException("ORSet::Add is not implemented"); +} + +template +void ORSet::Remove(const T &elem) { + // TODO(student): Implement this + throw NotImplementedException("ORSet::Remove is not implemented"); +} + +template +void ORSet::Merge(const ORSet &other) { + // TODO(student): Implement this + throw NotImplementedException("ORSet::Merge is not implemented"); +} + +template +auto ORSet::Elements() const -> std::vector { + // TODO(student): Implement this + throw NotImplementedException("ORSet::Elements is not implemented"); +} + +template +auto ORSet::ToString() const -> std::string { + auto elements = Elements(); + std::sort(elements.begin(), elements.end()); + return fmt::format("{{{}}}", fmt::join(elements, ", ")); +} + +template class ORSet; +template class ORSet; + +} // namespace bustub diff --git a/src/primer/orset_driver.cpp b/src/primer/orset_driver.cpp new file mode 100644 index 000000000..67afc6521 --- /dev/null +++ b/src/primer/orset_driver.cpp @@ -0,0 +1,43 @@ +#include "primer/orset_driver.h" +#include + +namespace bustub { + +template +ORSetDriver::ORSetDriver(size_t num_orset_node) { + orset_nodes_.reserve(num_orset_node); + for (size_t i = 0; i < num_orset_node; ++i) { + orset_nodes_.emplace_back(std::make_unique>(this, i)); + } + saved_copies_.resize(num_orset_node); +} + +template +void ORSetNode::Load() { + for (size_t i = 0; i < driver_->saved_copies_.size(); ++i) { + if (i == node_id_) { + continue; + } + Merge(driver_->saved_copies_[i]); + } +} + +template +void ORSetNode::Save() { + driver_->saved_copies_[node_id_] = orset_; +} + +template +void ORSetDriver::Sync() { + for (const auto &node : orset_nodes_) { + node->Save(); + } + for (const auto &node : orset_nodes_) { + node->Load(); + } +} + +template class ORSetDriver; +template class ORSetDriver; + +} // namespace bustub diff --git a/test/primer/orset_test.cpp b/test/primer/orset_test.cpp new file mode 100644 index 000000000..b7a290ed1 --- /dev/null +++ b/test/primer/orset_test.cpp @@ -0,0 +1,267 @@ +#include "primer/orset_driver.h" +#include +#include +#include "common/exception.h" +#include "fmt/core.h" +#include "fmt/format.h" +#include "fmt/ranges.h" +#include "gtest/gtest.h" + +namespace bustub { + +TEST(ORSetTest, AddRemoveTest) { + const int n = 10; + auto orset = ORSet(); + + for (int i = 0; i < n; ++i) { + ASSERT_FALSE(orset.Contains(i)); + } + + for (int i = 0; i < n; ++i) { + orset.Add(i, i); + ASSERT_TRUE(orset.Contains(i)); + } + + for (int i = 0; i < n; ++i) { + ASSERT_TRUE(orset.Contains(i)); + orset.Remove(i); + ASSERT_FALSE(orset.Contains(i)); + } +} + +TEST(ORSetTest, MergeTest) { + auto orset_a = ORSet(); + auto orset_b = ORSet(); + orset_a.Add(1, 0); + orset_b.Add(2, 1); + orset_b.Remove(2); + orset_a.Merge(orset_b); + + ASSERT_TRUE(orset_a.Contains(1)); + ASSERT_FALSE(orset_a.Contains(2)); + ASSERT_FALSE(orset_b.Contains(1)); + ASSERT_FALSE(orset_b.Contains(2)); + + orset_b.Merge(orset_a); + ASSERT_TRUE(orset_b.Contains(1)); + + orset_b.Add(2, 2); + ASSERT_TRUE(orset_b.Contains(2)); +} + +TEST(ORSetTest, AddWinsTest) { + auto student_a_courses = ORSet(); + auto student_b_courses = ORSet(); + + student_a_courses.Add("15-445", 0); + student_a_courses.Remove("15-445"); + student_b_courses.Add("15-445", 1); + + auto copy_a = student_a_courses; + auto copy_b = student_b_courses; + + student_a_courses.Merge(copy_b); + student_b_courses.Merge(copy_a); + + ASSERT_TRUE(student_a_courses.Contains("15-445")); + ASSERT_TRUE(student_b_courses.Contains("15-445")); +} + +TEST(ORSetTest, DoubleMergeTest) { + auto student_a_courses = ORSet(); + auto student_b_courses = ORSet(); + + student_a_courses.Add("15-410", 0); + student_a_courses.Remove("15-410"); + student_b_courses.Add("15-410", 1); + student_b_courses.Add("15-721", 2); + + auto copy_a = student_a_courses; + auto copy_b = student_b_courses; + + student_a_courses.Merge(copy_b); + student_b_courses.Merge(copy_a); + + ASSERT_TRUE(student_a_courses.Contains("15-410")); + ASSERT_TRUE(student_a_courses.Contains("15-721")); + ASSERT_TRUE(student_b_courses.Contains("15-410")); + ASSERT_TRUE(student_b_courses.Contains("15-721")); + + student_a_courses.Merge(copy_b); + student_b_courses.Merge(copy_a); + + ASSERT_TRUE(student_a_courses.Contains("15-410")); + ASSERT_TRUE(student_a_courses.Contains("15-721")); + ASSERT_TRUE(student_b_courses.Contains("15-410")); + ASSERT_TRUE(student_b_courses.Contains("15-721")); +} + +TEST(ORSetDriverTest, AddRemoveTest) { + const int n = 10; + auto driver = ORSetDriver(3); + + for (size_t i = 0; i < n; ++i) { + driver[i % 3]->Add(i); + ASSERT_TRUE(driver[i % 3]->Contains(i)); + ASSERT_FALSE(driver[(i + 1) % 3]->Contains(i)); + ASSERT_FALSE(driver[(i + 2) % 3]->Contains(i)); + + driver.Sync(); + + ASSERT_TRUE(driver[0]->Contains(i)); + ASSERT_TRUE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + } + + for (int i = 0; i < n; ++i) { + driver[i % 3]->Remove(i); + ASSERT_FALSE(driver[i % 3]->Contains(i)); + ASSERT_TRUE(driver[(i + 1) % 3]->Contains(i)); + ASSERT_TRUE(driver[(i + 2) % 3]->Contains(i)); + + driver.Sync(); + ASSERT_FALSE(driver[0]->Contains(i)); + ASSERT_FALSE(driver[1]->Contains(i)); + ASSERT_FALSE(driver[2]->Contains(i)); + } +} + +TEST(ORSetDriverTest, MergeTest) { + auto driver = ORSetDriver(2); + driver[0]->Add(1); + driver[1]->Add(1); + driver[0]->Remove(1); + driver.Sync(); + ASSERT_TRUE(driver[0]->Contains(1)); + ASSERT_EQ(driver[0]->Contains(1), driver[1]->Contains(1)); + + driver[1]->Add(2); + driver.Sync(); + driver[0]->Remove(2); + driver[0]->Add(1); + driver.Sync(); + driver[1]->Remove(1); + driver.Sync(); + ASSERT_EQ(driver[0]->Contains(1), driver[1]->Contains(1)); + ASSERT_EQ(driver[0]->Contains(2), driver[1]->Contains(2)); +} + +TEST(ORSetDriverTest, AddBackAgainTest) { + const int n = 10; + auto driver = ORSetDriver(3); + + for (size_t i = 0; i < n; ++i) { + driver[i % 3]->Add(i); + ASSERT_TRUE(driver[i % 3]->Contains(i)); + } + + driver.Sync(); + + for (size_t i = 0; i < n; ++i) { + ASSERT_TRUE(driver[0]->Contains(i)); + ASSERT_TRUE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + + driver[i % 3]->Remove(i); + ASSERT_FALSE(driver[i % 3]->Contains(i)); + } + + driver.Sync(); + + for (size_t i = 0; i < n; ++i) { + ASSERT_FALSE(driver[0]->Contains(i)); + ASSERT_FALSE(driver[1]->Contains(i)); + ASSERT_FALSE(driver[2]->Contains(i)); + } + + for (size_t i = 0; i < n; ++i) { + driver[i % 3]->Add(i); + ASSERT_TRUE(driver[i % 3]->Contains(i)); + } + + driver.Sync(); + + for (size_t i = 0; i < n; ++i) { + ASSERT_TRUE(driver[0]->Contains(i)); + ASSERT_TRUE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + } +} + +TEST(ORSetDriverTest, AddWinsALotTest) { + const int n = 10; + auto driver = ORSetDriver(3); + + for (int i = 0; i < n; ++i) { + driver[0]->Add(i); + driver[1]->Add(i); + driver[2]->Add(i); + } + + for (int i = 0; i < n; ++i) { + ASSERT_TRUE(driver[0]->Contains(i)); + ASSERT_TRUE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + + driver[i % 3]->Remove(i); + ASSERT_FALSE(driver[i % 3]->Contains(i)); + } + + driver.Sync(); + + for (int i = 0; i < n; ++i) { + ASSERT_TRUE(driver[0]->Contains(i)); + ASSERT_TRUE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + } +} + +TEST(ORSetDriverTest, NetworkLostTest) { + const int n = 10; + const int m = 20; + auto driver = ORSetDriver(3); + + for (int i = 0; i < n; ++i) { + driver[i % 2]->Add(i); + } + + for (int i = 0; i < m; ++i) { + driver[2]->Add(i); + } + + driver[0]->Save(); + driver[1]->Save(); + driver[0]->Load(); + driver[1]->Load(); + + for (int i = 0; i < n; ++i) { + ASSERT_TRUE(driver[0]->Contains(i)); + ASSERT_TRUE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + } + + for (int i = n; i < m; ++i) { + ASSERT_FALSE(driver[0]->Contains(i)); + ASSERT_FALSE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + driver[0]->Remove(i); + } + + for (int i = n; i < m; ++i) { + ASSERT_FALSE(driver[0]->Contains(i)); + ASSERT_FALSE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + driver[0]->Add(i); + driver[2]->Remove(i); + } + + driver.Sync(); + + for (int i = 0; i < m; ++i) { + ASSERT_TRUE(driver[0]->Contains(i)); + ASSERT_TRUE(driver[1]->Contains(i)); + ASSERT_TRUE(driver[2]->Contains(i)); + } +} + +} // namespace bustub