From db268235324880efd0534ab4230f05f1af41be1b Mon Sep 17 00:00:00 2001 From: Xingyu Chen Date: Mon, 9 Mar 2026 16:59:53 -0400 Subject: [PATCH 1/4] implement autograder --- ci_cd/.gitlab-ci.yml | 11 +++ ci_cd/problem2.yml | 52 ++++++++++++ ci_cd/problem3.yml | 47 +++++++++++ tests/Makefile | 39 +++++++++ tests/include/Bloom.h | 71 +++++++++++++++++ tests/testBloom.cpp | 179 ++++++++++++++++++++++++++++++++++++++++++ tests/testHash.cpp | 75 ++++++++++++++++++ 7 files changed, 474 insertions(+) create mode 100644 ci_cd/.gitlab-ci.yml create mode 100644 ci_cd/problem2.yml create mode 100644 ci_cd/problem3.yml create mode 100644 tests/Makefile create mode 100644 tests/include/Bloom.h create mode 100644 tests/testBloom.cpp create mode 100644 tests/testHash.cpp diff --git a/ci_cd/.gitlab-ci.yml b/ci_cd/.gitlab-ci.yml new file mode 100644 index 0000000..24774c7 --- /dev/null +++ b/ci_cd/.gitlab-ci.yml @@ -0,0 +1,11 @@ +stages: + - prebuild + - compile + - test + +include: + - local: 'ci_cd/problem2.yml' + - local: 'ci_cd/problem3.yml' + +default: + timeout: 5m diff --git a/ci_cd/problem2.yml b/ci_cd/problem2.yml new file mode 100644 index 0000000..e7f8e5f --- /dev/null +++ b/ci_cd/problem2.yml @@ -0,0 +1,52 @@ +prebuild_problem_2: + stage: prebuild + script: + - | + # Check if source files exist + if [ ! -f "impl/myBloom.h" ]; then + echo "myBloom.h does not exist under include directory"; + exit 1; + fi + if [ ! -f "impl/myBloom.cpp" ]; then + echo "myBloom.cpp does not exist under impl directory"; + exit 1; + fi + - git clone https://agile.bu.edu/gitlab/configs/ec330/homeworks/homeworkFour.git hw4 + artifacts: + paths: + - hw4/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem2"' + tags: [c++-17] + +compile_problem_2: + stage: compile + needs: + - job: prebuild_problem_2 + artifacts: true + script: + - cp include/myBloom.h hw4/tests/include/ + - cp impl/myBloom.cpp hw4/tests/impl/ + - cd hw4/tests + - make problem2 + artifacts: + paths: + - hw4/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem2"' + tags: [c++-17] + +exec_problem_2: + stage: test + needs: + - job: compile_problem_2 + artifacts: true + script: + - cd hw4/tests + - ./problem2 + artifacts: + paths: + - hw4/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem2"' + tags: [c++-17] \ No newline at end of file diff --git a/ci_cd/problem3.yml b/ci_cd/problem3.yml new file mode 100644 index 0000000..3549f7a --- /dev/null +++ b/ci_cd/problem3.yml @@ -0,0 +1,47 @@ +prebuild_problem_3: + stage: prebuild + script: + - | + # Check if source files exist + if [ ! -f "impl/miningHash.cpp" ]; then + echo "miningHash.cpp does not exist under impl directory"; + exit 1; + fi + - git clone https://agile.bu.edu/gitlab/configs/ec330/homeworks/homeworkFour.git hw4 + artifacts: + paths: + - hw4/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3"' + tags: [c++-17] + +compile_problem_3: + stage: compile + needs: + - job: prebuild_problem_3 + artifacts: true + script: + - cp impl/miningHash.cpp hw4/tests/impl/ + - cd hw4/tests + - make problem3 + artifacts: + paths: + - hw4/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3"' + tags: [c++-17] + +exec_problem_3: + stage: test + needs: + - job: compile_problem_3 + artifacts: true + script: + - cd hw4/tests + - ./problem3 + artifacts: + paths: + - hw4/ + rules: + - if: '$CI_COMMIT_REF_NAME == "problem3"' + tags: [c++-17] \ No newline at end of file diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..c0d367b --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,39 @@ +# Makefile, generated with support of ChatGPT + +# Compiler and flags +CXX = g++ -O2 +CXXFLAGS = -Wall -std=c++17 + +# Source files +PROBLEM2_SRCS = testBloom.cpp +PROBLEM3_SRCS = testHash.cpp +SRCS = $(PROBLEM2_SRCS) $(PROBLEM3_SRCS) + +# Object files +PROBLEM2_OBJS = testBloom.o +PROBLEM3_OBJS = testHash.o +OBJS = $(PROBLEM2_OBJS) $(PROBLEM3_OBJS) + +# Executable names +PROBLEM2_EXEC = problem2 +PROBLEM3_EXEC = problem3 +EXECS = $(PROBLEM2_EXEC) $(PROBLEM3_EXEC) + +# Default target to build the executable +all: $(PROBLEM2_EXEC) $(PROBLEM3_EXEC) + +# Rule to build the executable from object files +$(PROBLEM2_EXEC): $(PROBLEM2_OBJS) Makefile + $(CXX) $(CXXFLAGS) -o $(PROBLEM2_EXEC) $(PROBLEM2_OBJS) + +$(PROBLEM3_EXEC): $(PROBLEM3_OBJS) Makefile + $(CXX) $(CXXFLAGS) -o $(PROBLEM3_EXEC) $(PROBLEM3_OBJS) + +# Rules to build object files from source files, with dependency on the Common.h header +%.o: %.cpp Makefile + $(CXX) $(CXXFLAGS) -c $< -o $@ + +# Clean target to remove compiled files +clean: + rm -f $(OBJS) $(EXEC) + diff --git a/tests/include/Bloom.h b/tests/include/Bloom.h new file mode 100644 index 0000000..6b55bbc --- /dev/null +++ b/tests/include/Bloom.h @@ -0,0 +1,71 @@ +// +// Created by Ari Trachtenberg on 3/15/17. +// + +#ifndef BLOOM_OBJECT_H +#define BLOOM_OBJECT_H + +#include +#include +#include + +using namespace std; + +/** + * Represents a Bloom object corresponding to a character-based Bloom filter. + * \tparam length The length (in characters) of the Bloom filter. + */ +template +class Bloom { +public: + /** + * Instantiate an empty Bloom filter object + */ + Bloom () = default; + + /** + * @return A pointer to an empty Bloom filter. + */ + virtual unique_ptr< Bloom > makeBloom() = 0; + + /** + * Instantiates a Bloom filter object from a given \param theFilter string + * \param theFilter The object is created to represent this \param theFilter + * \pre \param theFilter must have been produced by the \ref getFilter() + * method of some BloomFilter object of the same length, or else the + * behavior is unspecified. + * \return A pointer to a Bloom filter corresponding to \param theFilter + */ + virtual unique_ptr< Bloom > makeBloom(const array& theFilter) = 0; + + /** + * inserts \param item into the Bloom object. + */ + virtual void insert(string item)=0; + + /** + * Checks whether \param item is in the Bloom filter object. + * @return true if \param item may be in the Bloom filter object + * false if \param item is definitely not in the Bloom filter object + */ + virtual bool exists(string item)=0; + + /** + * \return A filter string representing the Bloom object + */ + const array getFilter() const { return filter; } + + /** + * Destructor for the Bloom object + */ + virtual ~Bloom() = default; + +protected: + /** + * The filter itself. + */ + array filter; +}; + + +#endif //BLOOM_OBJECT_H diff --git a/tests/testBloom.cpp b/tests/testBloom.cpp new file mode 100644 index 0000000..3d599d4 --- /dev/null +++ b/tests/testBloom.cpp @@ -0,0 +1,179 @@ +// +// Created by Kevin on 3/9/2026. +// + +#include +#include +#include +#include + +#include "include/myBloom.h" + +using namespace std; + +// helper function: consistent failure printing +bool failExample(const char* testName, + const std::string& msg, + const std::string& expected, + const std::string& got) { + std::cerr << "[" << testName << " FAILED] " << msg + << ": expected " << expected << ", got " << got << "\n"; + return false; +} + +// helper: compare two filters +template +static bool sameFilter(const Bloom& a, const Bloom& b) { + return a.getFilter() == b.getFilter(); +} + +// -------------------- Empty bloom should not contain a fresh item -------------------- +bool test_0() { + const char* T = "Empty bloom should not contain an arbitrary fresh item"; + + myBloom<16> bloom; + + // For an empty Bloom filter, exists should definitely be false + // because no bits have been set yet. + if (bloom.exists("apple")) { + return failExample( + T, + "empty bloom reported an item as present", + "false", + "true" + ); + } + + return true; +} + +// -------------------- Inserted item must exist -------------------- +bool test_1() { + const char* T = "Inserted items should always exist"; + + myBloom<16> bloom; + + bloom.insert("apple"); + bloom.insert("banana"); + bloom.insert("carrot"); + + if (!bloom.exists("apple")) { + return failExample(T, "inserted item apple not found", "true", "false"); + } + + if (!bloom.exists("banana")) { + return failExample(T, "inserted item banana not found", "true", "false"); + } + + if (!bloom.exists("carrot")) { + return failExample(T, "inserted item carrot not found", "true", "false"); + } + + return true; +} + +// -------------------- makeBloom(filter) should reconstruct equivalent filter -------------------- +bool test_2() { + const char* T = "Bloom reconstructed from filter should preserve membership"; + + myBloom<16> original; + original.insert("dog"); + original.insert("cat"); + original.insert("mouse"); + + array saved = original.getFilter(); + + unique_ptr< Bloom<16> > rebuilt = original.makeBloom(saved); + + if (!rebuilt->exists("dog")) { + return failExample(T, "rebuilt bloom lost inserted item dog", "true", "false"); + } + + if (!rebuilt->exists("cat")) { + return failExample(T, "rebuilt bloom lost inserted item cat", "true", "false"); + } + + if (!rebuilt->exists("mouse")) { + return failExample(T, "rebuilt bloom lost inserted item mouse", "true", "false"); + } + + if (!sameFilter<16>(original, *rebuilt)) { + return failExample( + T, + "rebuilt bloom filter differs from original filter", + "same filter", + "different filter" + ); + } + + return true; +} + +// -------------------- makeBloom() should create a fresh empty bloom -------------------- +bool test_3() { + const char* T = "makeBloom() should create an empty bloom"; + + myBloom<16> original; + original.insert("apple"); + + unique_ptr< Bloom<16> > fresh = original.makeBloom(); + + if (fresh->exists("apple")) { + return failExample( + T, + "fresh bloom unexpectedly contains inserted item from another object", + "false", + "true" + ); + } + + return true; +} + +// -------------------- Multiple inserted items should remain detectable after more insertions -------------------- +bool test_4() { + const char* T = "Earlier inserted items should still exist after later insertions"; + + myBloom<32> bloom; + + bloom.insert("alpha"); + bloom.insert("beta"); + bloom.insert("gamma"); + bloom.insert("delta"); + bloom.insert("epsilon"); + bloom.insert("zeta"); + bloom.insert("eta"); + bloom.insert("theta"); + + const std::array inserted = { + "alpha", "beta", "gamma", "delta", + "epsilon", "zeta", "eta", "theta" + }; + + for (const auto& s : inserted) { + if (!bloom.exists(s)) { + return failExample( + T, + "an inserted item was later lost", + "true", + "false" + ); + } + } + + return true; +} + +int main() { + bool results[] = { test_0(), test_1(), test_2(), test_3(), test_4() }; + + bool allPassed = true; + for (size_t ii = 0; ii < std::size(results); ii++) { + cout << "Test of problem " << to_string(ii) << ": " + << (results[ii] ? "passed" : "failed") << endl; + allPassed &= results[ii]; + } + + if (allPassed) exit(0); + else exit(-1); +} \ No newline at end of file diff --git a/tests/testHash.cpp b/tests/testHash.cpp new file mode 100644 index 0000000..ebc04f3 --- /dev/null +++ b/tests/testHash.cpp @@ -0,0 +1,75 @@ +// +// Created by Kevin on 3/9/2026. +// + +#include +#include +#include +#include +#include + +using namespace std; + +#include "impl/miningHash.cpp" + +// helper function: consistent failure printing +bool failExample(const char* testName, + const std::string& msg, + const std::string& expected, + const std::string& got) { + std::cerr << "[" << testName << " FAILED] " << msg + << ": expected " << expected << ", got " << got << "\n"; + return false; +} + +// -------------------- Provided example 1 -------------------- +bool test_0() { + const char* T = "Provided example with nonce 2"; + + string got = mining_hash("The quick brown fox jumps over the lazy dog.", 2ULL); + string expected = "114129918868003668674860640176"; + + if (got != expected) { + return failExample( + T, + "hash output does not match provided example", + expected, + got + ); + } + + return true; +} + +// -------------------- Provided example 2 -------------------- +bool test_1() { + const char* T = "Provided example with nonce 155"; + + string got = mining_hash("The quick brown fox jumps over the lazy dog.", 155ULL); + string expected = "224347652901828190347249710002"; + + if (got != expected) { + return failExample( + T, + "hash output does not match provided example", + expected, + got + ); + } + + return true; +} + +int main() { + bool results[] = { test_0(), test_1() }; + + bool allPassed = true; + for (size_t ii = 0; ii < std::size(results); ii++) { + cout << "Test of problem " << to_string(ii) << ": " + << (results[ii] ? "passed" : "failed") << endl; + allPassed &= results[ii]; + } + + if (allPassed) exit(0); + else exit(-1); +} \ No newline at end of file -- GitLab From 122574fc88fa9532c018bfe56e3888c9e6f8fec9 Mon Sep 17 00:00:00 2001 From: Xingyu Chen Date: Tue, 10 Mar 2026 17:08:43 -0400 Subject: [PATCH 2/4] update to template version --- tests/testHash.cpp | 4 ++-- tests/test_hash.exe | Bin 0 -> 74278 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 tests/test_hash.exe diff --git a/tests/testHash.cpp b/tests/testHash.cpp index ebc04f3..571d94c 100644 --- a/tests/testHash.cpp +++ b/tests/testHash.cpp @@ -26,7 +26,7 @@ bool failExample(const char* testName, bool test_0() { const char* T = "Provided example with nonce 2"; - string got = mining_hash("The quick brown fox jumps over the lazy dog.", 2ULL); + string got = mining_hash("The quick brown fox jumps over the lazy dog.", 2ULL); string expected = "114129918868003668674860640176"; if (got != expected) { @@ -45,7 +45,7 @@ bool test_0() { bool test_1() { const char* T = "Provided example with nonce 155"; - string got = mining_hash("The quick brown fox jumps over the lazy dog.", 155ULL); + string got = mining_hash("The quick brown fox jumps over the lazy dog.", 155ULL); string expected = "224347652901828190347249710002"; if (got != expected) { diff --git a/tests/test_hash.exe b/tests/test_hash.exe new file mode 100644 index 0000000000000000000000000000000000000000..93d89e8d10d210ee864ee8d99ba0bdd6ccc26e26 GIT binary patch literal 74278 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 Date: Tue, 10 Mar 2026 17:09:00 -0400 Subject: [PATCH 3/4] update to template version --- tests/test_hash.exe | Bin 74278 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/test_hash.exe diff --git a/tests/test_hash.exe b/tests/test_hash.exe deleted file mode 100644 index 93d89e8d10d210ee864ee8d99ba0bdd6ccc26e26..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74278 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 Date: Wed, 11 Mar 2026 15:40:15 -0400 Subject: [PATCH 4/4] address comments --- ci_cd/problem2.yml | 12 ++++----- ci_cd/problem3.yml | 6 ++--- tests/testBloom.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++-- tests/testHash.cpp | 2 +- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/ci_cd/problem2.yml b/ci_cd/problem2.yml index e7f8e5f..62d4ca9 100644 --- a/ci_cd/problem2.yml +++ b/ci_cd/problem2.yml @@ -3,12 +3,12 @@ prebuild_problem_2: script: - | # Check if source files exist - if [ ! -f "impl/myBloom.h" ]; then - echo "myBloom.h does not exist under include directory"; + if [ ! -f "impl/MyBloom.h" ]; then + echo "MyBloom.h does not exist under include directory"; exit 1; fi - if [ ! -f "impl/myBloom.cpp" ]; then - echo "myBloom.cpp does not exist under impl directory"; + if [ ! -f "impl/MyBloom.cpp" ]; then + echo "MyBloom.cpp does not exist under impl directory"; exit 1; fi - git clone https://agile.bu.edu/gitlab/configs/ec330/homeworks/homeworkFour.git hw4 @@ -25,8 +25,8 @@ compile_problem_2: - job: prebuild_problem_2 artifacts: true script: - - cp include/myBloom.h hw4/tests/include/ - - cp impl/myBloom.cpp hw4/tests/impl/ + - cp include/MyBloom.h hw4/tests/include/ + - cp impl/MyBloom.cpp hw4/tests/impl/ - cd hw4/tests - make problem2 artifacts: diff --git a/ci_cd/problem3.yml b/ci_cd/problem3.yml index 3549f7a..152030e 100644 --- a/ci_cd/problem3.yml +++ b/ci_cd/problem3.yml @@ -3,8 +3,8 @@ prebuild_problem_3: script: - | # Check if source files exist - if [ ! -f "impl/miningHash.cpp" ]; then - echo "miningHash.cpp does not exist under impl directory"; + if [ ! -f "impl/MiningHash.cpp" ]; then + echo "MiningHash.cpp does not exist under impl directory"; exit 1; fi - git clone https://agile.bu.edu/gitlab/configs/ec330/homeworks/homeworkFour.git hw4 @@ -21,7 +21,7 @@ compile_problem_3: - job: prebuild_problem_3 artifacts: true script: - - cp impl/miningHash.cpp hw4/tests/impl/ + - cp impl/MiningHash.cpp hw4/tests/impl/ - cd hw4/tests - make problem3 artifacts: diff --git a/tests/testBloom.cpp b/tests/testBloom.cpp index 3d599d4..85f6b3a 100644 --- a/tests/testBloom.cpp +++ b/tests/testBloom.cpp @@ -7,7 +7,7 @@ #include #include -#include "include/myBloom.h" +#include "include/MyBloom.h" using namespace std; @@ -164,8 +164,67 @@ bool test_4() { return true; } +// -------------------- No false negatives -------------------- + +bool test_5() { + + const char* T = "Bloom filter should have no false negatives"; + + myBloom<32> bf; + + // elements inserted + std::vector inserted = { + "apple", + "banana", + "carrot", + "dog", + "elephant" + }; + + // elements to check + std::vector check = { + "apple", + "banana", + "carrot", + "dog", + "elephant", + "fish", + "grape", + "house", + "island", + "jacket" + }; + + // insert elements + for (const auto& s : inserted) + bf.insert(s); + + // verify Bloom filter property + for (const auto& s : check) { + + bool exists = bf.exists(s); + + if (!exists) { + + // if Bloom says "not present", + // it must truly not have been inserted + if (std::find(inserted.begin(), inserted.end(), s) != inserted.end()) { + + return failExample( + T, + "Bloom filter produced a false negative", + "element should exist", + s + ); + } + } + } + + return true; +} + int main() { - bool results[] = { test_0(), test_1(), test_2(), test_3(), test_4() }; + bool results[] = { test_0(), test_1(), test_2(), test_3(), test_4(), test_5() }; bool allPassed = true; for (size_t ii = 0; ii < std::size(results); ii++) { diff --git a/tests/testHash.cpp b/tests/testHash.cpp index 571d94c..4158e81 100644 --- a/tests/testHash.cpp +++ b/tests/testHash.cpp @@ -10,7 +10,7 @@ using namespace std; -#include "impl/miningHash.cpp" +#include "impl/MiningHash.cpp" // helper function: consistent failure printing bool failExample(const char* testName, -- GitLab