diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..062da31 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,65 @@ +# **Role:** + +You are to act as a **University Lecturer** in Computer Science. Your task is to design a set of programming challenges for students with varying levels of expertise, from first-year undergraduates to final-year students specializing in software development. Your tone should be encouraging and educational. + +# **Objective:** + +Generate a complete set of 10 programming challenges (5 for Python, 5 for C++) that can be integrated into this repository. + +# **Primary Instructions:** + +You must adhere strictly to the contribution guidelines outlined in this repository's `CONTRIBUTING.md` file and the workflow specifications from `.github/TEMPLATE_ACTION_WORKFLOW.yaml`. + +For each of the 10 challenges, you must generate the complete file structure and content, including: + +1. A **descriptive folder name** (e.g., `palindrome_checker`, `inventory_system`). +2. A detailed **`README.md`** file containing: + * `# Challenge Name` + * `## ๐ŸŽฏ Description`: A clear, concise overview of the problem. + * `## ๐Ÿ” Task`: Specific implementation details, including function/class names, parameters, and return types. + * `## ๐Ÿ“‹ Requirements`: Language versions (Python 3.8+, C++20), standards (PEP 8, Google C++ Style Guide), and any constraints. + * `## ๐Ÿงช Test Cases`: A comprehensive list of test cases, including basic, edge, and error-handling scenarios. +3. **Solution Template Files**: + * For Python: `solution.py` with function signatures and docstrings. + * For C++: `solution.h` (with header guards) and `solution.cpp` with function/class declarations and empty implementations. +4. **Test Files**: + * For Python: `test_main.py` using the `unittest` module. + * For C++: `test.cpp` using the `GoogleTest` framework. +5. A **`CMakeLists.txt`** file for each C++ challenge, configured for `GoogleTest`. +6. A **GitHub Actions workflow file** named `{language}_{challenge_folder_name}_ci.yaml` in the `.github/workflows/` directory. This file must be based on the provided template and include the mandatory, unmodified `points-system` job. + +--- + +### **Challenge Specifications** + +Generate the following challenges, ensuring a mix of difficulties as described. + +#### **Python Challenges (5 total)** + +1. **Beginner - Palindrome Checker:** + * **Task**: Implement a function `is_palindrome(s: str) -> bool` that checks if a string is a palindrome, ignoring case and non-alphanumeric characters. +2. **Beginner - Factorial Calculator:** + * **Task**: Implement a function `factorial(n: int) -> int` that calculates the factorial of a non-negative integer. It should raise a `ValueError` for negative input. +3. **Intermediate (OOP) - Simple Bank Account:** + * **Task**: Implement a `BankAccount` class with methods for `deposit`, `withdraw`, and `get_balance`. + * **Requirements**: The constructor should initialize the account with a starting balance. The `withdraw` method should prevent overdrawing. +4. **Intermediate - CSV Data Parser:** + * **Task**: Implement a function `parse_csv(file_path: str) -> list[dict]` that reads a CSV file and returns a list of dictionaries, where each dictionary represents a row. +5. **Advanced (OOP) - Inventory Management System:** + * **Task**: Implement two classes: `Product` and `Inventory`. The `Product` class will store item details (ID, name, price, quantity). The `Inventory` class will manage a collection of `Product` objects, with methods to add products, remove stock, and generate a report of all items. + +#### **C++ Challenges (5 total)** + +1. **Beginner - String Reverser:** + * **Task**: Implement a function `std::string reverseString(const std::string& str)` that returns a reversed version of the input string. +2. **Intermediate - Vector Statistics:** + * **Task**: Implement a function `std::tuple calculateVectorStats(const std::vector& vec)` that returns the mean, minimum, and maximum values of an integer vector. It should handle empty vectors gracefully. +3. **Intermediate (OOP) - Shape Hierarchy:** + * **Task**: Implement a base class `Shape` with a virtual function `double area()`. Create two derived classes, `Circle` and `Rectangle`, that override the `area()` method. + * **Requirements**: Use modern C++ features and proper class design. +4. **Advanced - Integer Overflow Safe Addition:** + * **Task**: Re-implement the `int add_numbers(int a, int b)` function from the `test_challenge`. + * **Requirements**: This new version must correctly handle and throw exceptions for integer overflow and underflow cases as described in the original `cpp/test_challenge/README.md`. +5. **Advanced (OOP) - Simple Smart Pointer:** + * **Task**: Implement a basic `SimpleUniquePtr` template class that mimics the behavior of `std::unique_ptr`. + * **Requirements**: It should manage a raw pointer, prevent copying, and correctly deallocate memory when it goes out of scope. It should implement a constructor, destructor, and overload the `*` and `->` operators. \ No newline at end of file diff --git a/.github/workflows/cpp_binary_search_tree_ci.yaml b/.github/workflows/cpp_binary_search_tree_ci.yaml new file mode 100644 index 0000000..c72d51b --- /dev/null +++ b/.github/workflows/cpp_binary_search_tree_ci.yaml @@ -0,0 +1,58 @@ +name: C++ Binary Search Tree CI + +on: + push: + paths: + - 'cpp/binary_search_tree/**' + +defaults: + run: + shell: bash + working-directory: ./cpp/binary_search_tree + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build C++ solution + uses: threeal/cmake-action@v2 + with: + source-dir: cpp/binary_search_tree + build-dir: ci-build + + - name: Run C++ tests + working-directory: ci-build/ + run: | + ctest --output-on-failure + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/cpp_prime_checker_ci.yaml b/.github/workflows/cpp_prime_checker_ci.yaml new file mode 100644 index 0000000..8554dcb --- /dev/null +++ b/.github/workflows/cpp_prime_checker_ci.yaml @@ -0,0 +1,58 @@ +name: C++ Prime Checker CI + +on: + push: + paths: + - 'cpp/prime_checker/**' + +defaults: + run: + shell: bash + working-directory: ./cpp/prime_checker + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build C++ solution + uses: threeal/cmake-action@v2 + with: + source-dir: cpp/prime_checker + build-dir: ci-build + + - name: Run C++ tests + working-directory: ci-build/ + run: | + ctest --output-on-failure + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/cpp_smart_array_ci.yaml b/.github/workflows/cpp_smart_array_ci.yaml new file mode 100644 index 0000000..16753bf --- /dev/null +++ b/.github/workflows/cpp_smart_array_ci.yaml @@ -0,0 +1,58 @@ +name: C++ Smart Array CI + +on: + push: + paths: + - 'cpp/smart_array/**' + +defaults: + run: + shell: bash + working-directory: ./cpp/smart_array + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build C++ solution + uses: threeal/cmake-action@v2 + with: + source-dir: cpp/smart_array + build-dir: ci-build + + - name: Run C++ tests + working-directory: ci-build/ + run: | + ctest --output-on-failure + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/cpp_string_reverser_ci.yaml b/.github/workflows/cpp_string_reverser_ci.yaml new file mode 100644 index 0000000..b3b9b91 --- /dev/null +++ b/.github/workflows/cpp_string_reverser_ci.yaml @@ -0,0 +1,58 @@ +name: C++ String Reverser CI + +on: + push: + paths: + - 'cpp/string_reverser/**' + +defaults: + run: + shell: bash + working-directory: ./cpp/string_reverser + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build C++ solution + uses: threeal/cmake-action@v2 + with: + source-dir: cpp/string_reverser + build-dir: ci-build + + - name: Run C++ tests + working-directory: ci-build/ + run: | + ctest --output-on-failure + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/cpp_vector_math_ci.yaml b/.github/workflows/cpp_vector_math_ci.yaml new file mode 100644 index 0000000..24b2787 --- /dev/null +++ b/.github/workflows/cpp_vector_math_ci.yaml @@ -0,0 +1,58 @@ +name: C++ Vector Math CI + +on: + push: + paths: + - 'cpp/vector_math/**' + +defaults: + run: + shell: bash + working-directory: ./cpp/vector_math + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build C++ solution + uses: threeal/cmake-action@v2 + with: + source-dir: cpp/vector_math + build-dir: ci-build + + - name: Run C++ tests + working-directory: ci-build/ + run: | + ctest --output-on-failure + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/python_csv_data_parser_ci.yaml b/.github/workflows/python_csv_data_parser_ci.yaml new file mode 100644 index 0000000..245e892 --- /dev/null +++ b/.github/workflows/python_csv_data_parser_ci.yaml @@ -0,0 +1,55 @@ +name: Python CSV Data Parser CI + +on: + push: + paths: + - 'python/csv_data_parser/**' + +defaults: + run: + shell: bash + working-directory: ./python/csv_data_parser + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Run Python unit tests + run: python -m unittest test_main.py -v + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/python_factorial_calculator_ci.yaml b/.github/workflows/python_factorial_calculator_ci.yaml new file mode 100644 index 0000000..6a229c4 --- /dev/null +++ b/.github/workflows/python_factorial_calculator_ci.yaml @@ -0,0 +1,55 @@ +name: Python Factorial Calculator CI + +on: + push: + paths: + - 'python/factorial_calculator/**' + +defaults: + run: + shell: bash + working-directory: ./python/factorial_calculator + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Run Python unit tests + run: python -m unittest test_main.py -v + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/python_inventory_system_ci.yaml b/.github/workflows/python_inventory_system_ci.yaml new file mode 100644 index 0000000..cb08241 --- /dev/null +++ b/.github/workflows/python_inventory_system_ci.yaml @@ -0,0 +1,55 @@ +name: Python Inventory System CI + +on: + push: + paths: + - 'python/inventory_system/**' + +defaults: + run: + shell: bash + working-directory: ./python/inventory_system + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Run Python unit tests + run: python -m unittest test_main.py -v + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/python_palindrome_checker_ci.yaml b/.github/workflows/python_palindrome_checker_ci.yaml new file mode 100644 index 0000000..d5b5999 --- /dev/null +++ b/.github/workflows/python_palindrome_checker_ci.yaml @@ -0,0 +1,55 @@ +name: Python Palindrome Checker CI + +on: + push: + paths: + - 'python/palindrome_checker/**' + +defaults: + run: + shell: bash + working-directory: ./python/palindrome_checker + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Run Python unit tests + run: python -m unittest test_main.py -v + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/.github/workflows/python_simple_bank_account_ci.yaml b/.github/workflows/python_simple_bank_account_ci.yaml new file mode 100644 index 0000000..394ab05 --- /dev/null +++ b/.github/workflows/python_simple_bank_account_ci.yaml @@ -0,0 +1,55 @@ +name: Python Simple Bank Account CI + +on: + push: + paths: + - 'python/simple_bank_account/**' + +defaults: + run: + shell: bash + working-directory: ./python/simple_bank_account + +permissions: + contents: read + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Run Python unit tests + run: python -m unittest test_main.py -v + + points-system: + name: Award Points + runs-on: ubuntu-latest + needs: test + defaults: + run: + shell: bash + working-directory: . + + steps: + - name: Check secrets and increment points + shell: bash + run: | + if [[ -n "${{ secrets.GKSS_LEADERBOARD_API_URL }}" && -n "${{ secrets.GKSS_LEADERBOARD_API_KEY }}" ]]; then + echo "Incrementing points..." + curl -X POST ${{ secrets.GKSS_LEADERBOARD_API_URL }} \ + -H "x-gkssunisa-api-key: ${{ secrets.GKSS_LEADERBOARD_API_KEY }}" \ + -H "Content-Type: application/json" \ + -d '{"message": "i did it! ${{ github.actor }}"}' + else + echo "Skipping points increment - required secrets not found" + exit 1 + fi diff --git a/cpp/binary_search_tree/CMakeLists.txt b/cpp/binary_search_tree/CMakeLists.txt new file mode 100644 index 0000000..36bf81c --- /dev/null +++ b/cpp/binary_search_tree/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.31) +project(binary_search_tree_challenge VERSION 1.0.0 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +enable_testing() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_executable( + binary_search_tree_test + test.cpp + solution.cpp +) + +target_link_libraries( + binary_search_tree_test + GTest::gtest + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(binary_search_tree_test) diff --git a/cpp/binary_search_tree/README.md b/cpp/binary_search_tree/README.md new file mode 100644 index 0000000..8191941 --- /dev/null +++ b/cpp/binary_search_tree/README.md @@ -0,0 +1,125 @@ +# Binary Search Tree + +## ๐ŸŽฏ Description +This intermediate challenge focuses on implementing a binary search tree (BST) data structure in C++. You'll practice dynamic memory management, recursion, tree traversal algorithms, and proper resource management with modern C++ features. + +## ๐Ÿ” Task +Implement a `BinarySearchTree` class that manages integer values in a binary search tree structure. + +### Class Requirements: + +#### Node Structure (Internal) +You'll need an internal node structure to represent tree nodes: +```cpp +struct Node { + int data; + Node* left; + Node* right; + + Node(int value) : data(value), left(nullptr), right(nullptr) {} +}; +``` + +#### Public Interface + +#### Constructor and Destructor +- `BinarySearchTree()` - Initialize empty tree +- `~BinarySearchTree()` - Clean up all allocated memory + +#### Core Methods +1. `void insert(int value)` - Insert a value into the tree +2. `bool contains(int value) const` - Check if value exists in tree +3. `bool remove(int value)` - Remove a value from tree, return success status +4. `int size() const` - Return number of nodes in tree +5. `bool empty() const` - Check if tree is empty + +#### Traversal Methods +1. `std::vector inorder() const` - Return inorder traversal (sorted order) +2. `std::vector preorder() const` - Return preorder traversal +3. `std::vector postorder() const` - Return postorder traversal + +#### Utility Methods +1. `int findMin() const` - Find minimum value (leftmost node) +2. `int findMax() const` - Find maximum value (rightmost node) +3. `int height() const` - Calculate height of tree + +### BST Properties: +- For each node: left subtree values < node value < right subtree values +- No duplicate values allowed +- Empty tree has height 0, single node has height 1 + +## ๐Ÿ“‹ Requirements +- Class must be implemented in C++20 +- Include proper header guards +- Use proper memory management (no memory leaks) +- Follow RAII principles +- Handle edge cases gracefully +- Use const correctness +- Implement rule of three/five if needed + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Constructor and Basic Operations +- Empty tree should have size 0 and be empty +- Single insertion should work correctly +- Tree should maintain BST property + +### 2. Insertion Tests +- Multiple insertions in various orders +- Duplicate insertions should be ignored +- Tree structure should remain valid + +### 3. Search Operations +- `contains()` should find existing values +- `contains()` should return false for non-existent values +- Edge case searches (empty tree, single node) + +### 4. Removal Operations +- Remove leaf nodes +- Remove nodes with one child +- Remove nodes with two children +- Remove non-existent values should return false + +### 5. Traversal Tests +- Inorder traversal should return values in sorted order +- Preorder and postorder should follow correct patterns +- Empty tree traversals should return empty vectors + +### 6. Utility Function Tests +- `findMin()` and `findMax()` should work correctly +- Height calculation should be accurate +- Size counting should be correct + +### 7. Memory Management +- No memory leaks after destructor +- Proper cleanup of all nodes +- Safe operations on empty trees + +### 8. Edge Cases +- Operations on empty tree +- Single-node tree operations +- Large tree operations + +## ๐Ÿ“ Files +- `include/solution.h` - Declare your class here +- `solution.cpp` - Implement your solution here +- `test.cpp` - Contains the test cases **DO NOT EDIT** +- `CMakeLists.txt` - Build configuration **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `include/solution.h` and `solution.cpp` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- Use recursion for most tree operations +- Remember to handle the case where tree is empty +- For removal, consider three cases: leaf, one child, two children +- When removing node with two children, replace with inorder successor +- Include `` for return types +- Use `nullptr` instead of `NULL` +- Consider implementing helper recursive functions that take Node* parameters +- Don't forget to update size counter in insert/remove operations +- Height of empty tree is typically defined as 0 or -1 (we'll use 0) +- Be careful with memory management - every `new` needs a corresponding `delete` diff --git a/cpp/binary_search_tree/include/solution.h b/cpp/binary_search_tree/include/solution.h new file mode 100644 index 0000000..a01ab00 --- /dev/null +++ b/cpp/binary_search_tree/include/solution.h @@ -0,0 +1,128 @@ +#ifndef SOLUTION_H +#define SOLUTION_H + +#include + +/** + * Binary Search Tree implementation for integer values. + */ +class BinarySearchTree { +private: + struct Node { + int data; + Node* left; + Node* right; + + Node(int value) : data(value), left(nullptr), right(nullptr) {} + }; + + Node* root; + int tree_size; + + // Helper functions for recursive operations + Node* insertHelper(Node* node, int value); + bool containsHelper(Node* node, int value) const; + Node* removeHelper(Node* node, int value, bool& removed); + Node* findMinNode(Node* node) const; + void inorderHelper(Node* node, std::vector& result) const; + void preorderHelper(Node* node, std::vector& result) const; + void postorderHelper(Node* node, std::vector& result) const; + int heightHelper(Node* node) const; + void destroyTree(Node* node); + +public: + /** + * Constructor - Initialize empty tree. + */ + BinarySearchTree(); + + /** + * Destructor - Clean up all allocated memory. + */ + ~BinarySearchTree(); + + /** + * Insert a value into the tree. + * Duplicates are ignored. + * + * @param value The value to insert + */ + void insert(int value); + + /** + * Check if a value exists in the tree. + * + * @param value The value to search for + * @return True if value is found, false otherwise + */ + bool contains(int value) const; + + /** + * Remove a value from the tree. + * + * @param value The value to remove + * @return True if value was removed, false if not found + */ + bool remove(int value); + + /** + * Get the number of nodes in the tree. + * + * @return The size of the tree + */ + int size() const; + + /** + * Check if the tree is empty. + * + * @return True if tree is empty, false otherwise + */ + bool empty() const; + + /** + * Get inorder traversal of the tree (sorted order). + * + * @return Vector of values in inorder + */ + std::vector inorder() const; + + /** + * Get preorder traversal of the tree. + * + * @return Vector of values in preorder + */ + std::vector preorder() const; + + /** + * Get postorder traversal of the tree. + * + * @return Vector of values in postorder + */ + std::vector postorder() const; + + /** + * Find the minimum value in the tree. + * + * @return The minimum value + * @throws std::runtime_error if tree is empty + */ + int findMin() const; + + /** + * Find the maximum value in the tree. + * + * @return The maximum value + * @throws std::runtime_error if tree is empty + */ + int findMax() const; + + /** + * Calculate the height of the tree. + * Empty tree has height 0. + * + * @return The height of the tree + */ + int height() const; +}; + +#endif // SOLUTION_H diff --git a/cpp/binary_search_tree/solution.cpp b/cpp/binary_search_tree/solution.cpp new file mode 100644 index 0000000..452d59f --- /dev/null +++ b/cpp/binary_search_tree/solution.cpp @@ -0,0 +1,123 @@ +#include "solution.h" +#include + +BinarySearchTree::BinarySearchTree() : root(nullptr), tree_size(0) { + // TODO: Constructor is implemented above +} + +BinarySearchTree::~BinarySearchTree() { + // TODO: Implement destructor to clean up all nodes + // Use destroyTree helper function +} + +void BinarySearchTree::insert(int value) { + // TODO: Implement public insert function + // Use insertHelper for recursive implementation +} + +bool BinarySearchTree::contains(int value) const { + // TODO: Implement public contains function + // Use containsHelper for recursive implementation + return false; +} + +bool BinarySearchTree::remove(int value) { + // TODO: Implement public remove function + // Use removeHelper for recursive implementation + return false; +} + +int BinarySearchTree::size() const { + // TODO: Return tree_size + return 0; +} + +bool BinarySearchTree::empty() const { + // TODO: Return true if tree is empty + return true; +} + +std::vector BinarySearchTree::inorder() const { + // TODO: Implement inorder traversal + // Use inorderHelper for recursive implementation + std::vector result; + return result; +} + +std::vector BinarySearchTree::preorder() const { + // TODO: Implement preorder traversal + std::vector result; + return result; +} + +std::vector BinarySearchTree::postorder() const { + // TODO: Implement postorder traversal + std::vector result; + return result; +} + +int BinarySearchTree::findMin() const { + // TODO: Find minimum value (leftmost node) + // Throw std::runtime_error if tree is empty + return 0; +} + +int BinarySearchTree::findMax() const { + // TODO: Find maximum value (rightmost node) + // Throw std::runtime_error if tree is empty + return 0; +} + +int BinarySearchTree::height() const { + // TODO: Calculate height using heightHelper + return 0; +} + +// Private helper functions - implement these for recursive operations + +BinarySearchTree::Node* BinarySearchTree::insertHelper(Node* node, int value) { + // TODO: Recursive insert helper + // Base case: if node is null, create new node + // Recursive case: go left or right based on value + return nullptr; +} + +bool BinarySearchTree::containsHelper(Node* node, int value) const { + // TODO: Recursive contains helper + return false; +} + +BinarySearchTree::Node* BinarySearchTree::removeHelper(Node* node, int value, bool& removed) { + // TODO: Recursive remove helper + // Handle three cases: leaf, one child, two children + // Set removed to true if value was found and removed + return nullptr; +} + +BinarySearchTree::Node* BinarySearchTree::findMinNode(Node* node) const { + // TODO: Find leftmost node starting from given node + return nullptr; +} + +void BinarySearchTree::inorderHelper(Node* node, std::vector& result) const { + // TODO: Recursive inorder traversal: left, root, right +} + +void BinarySearchTree::preorderHelper(Node* node, std::vector& result) const { + // TODO: Recursive preorder traversal: root, left, right +} + +void BinarySearchTree::postorderHelper(Node* node, std::vector& result) const { + // TODO: Recursive postorder traversal: left, right, root +} + +int BinarySearchTree::heightHelper(Node* node) const { + // TODO: Recursive height calculation + // Height is 1 + max(left_height, right_height) + return 0; +} + +void BinarySearchTree::destroyTree(Node* node) { + // TODO: Recursively delete all nodes + // Post-order deletion: delete children first, then parent +} diff --git a/cpp/binary_search_tree/test.cpp b/cpp/binary_search_tree/test.cpp new file mode 100644 index 0000000..8a59ccf --- /dev/null +++ b/cpp/binary_search_tree/test.cpp @@ -0,0 +1,79 @@ +#include "solution.h" +#include + +class BinarySearchTreeTest : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(BinarySearchTreeTest, EmptyTree) { + BinarySearchTree bst; + EXPECT_TRUE(bst.empty()); + EXPECT_EQ(bst.size(), 0); + EXPECT_EQ(bst.height(), 0); +} + +TEST_F(BinarySearchTreeTest, SingleInsertion) { + BinarySearchTree bst; + bst.insert(42); + + EXPECT_FALSE(bst.empty()); + EXPECT_EQ(bst.size(), 1); + EXPECT_TRUE(bst.contains(42)); + EXPECT_FALSE(bst.contains(0)); +} + +TEST_F(BinarySearchTreeTest, MultipleInsertions) { + BinarySearchTree bst; + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + + EXPECT_EQ(bst.size(), 5); + EXPECT_TRUE(bst.contains(50)); + EXPECT_TRUE(bst.contains(30)); + EXPECT_TRUE(bst.contains(70)); + EXPECT_TRUE(bst.contains(20)); + EXPECT_TRUE(bst.contains(40)); +} + +TEST_F(BinarySearchTreeTest, InorderTraversal) { + BinarySearchTree bst; + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(40); + + std::vector inorder = bst.inorder(); + std::vector expected = {20, 30, 40, 50, 70}; + EXPECT_EQ(inorder, expected); +} + +TEST_F(BinarySearchTreeTest, RemoveOperations) { + BinarySearchTree bst; + bst.insert(50); + bst.insert(30); + bst.insert(70); + + EXPECT_TRUE(bst.remove(30)); + EXPECT_FALSE(bst.contains(30)); + EXPECT_EQ(bst.size(), 2); + + EXPECT_FALSE(bst.remove(100)); // Non-existent +} + +TEST_F(BinarySearchTreeTest, FindMinMax) { + BinarySearchTree bst; + bst.insert(50); + bst.insert(30); + bst.insert(70); + bst.insert(20); + bst.insert(80); + + EXPECT_EQ(bst.findMin(), 20); + EXPECT_EQ(bst.findMax(), 80); +} diff --git a/cpp/prime_checker/CMakeLists.txt b/cpp/prime_checker/CMakeLists.txt new file mode 100644 index 0000000..2cd91ff --- /dev/null +++ b/cpp/prime_checker/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.31) +project(prime_checker_challenge VERSION 1.0.0 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +enable_testing() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_executable( + prime_checker_test + test.cpp + solution.cpp +) + +target_link_libraries( + prime_checker_test + GTest::gtest + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(prime_checker_test) diff --git a/cpp/prime_checker/README.md b/cpp/prime_checker/README.md new file mode 100644 index 0000000..7b99800 --- /dev/null +++ b/cpp/prime_checker/README.md @@ -0,0 +1,93 @@ +# Prime Number Checker + +## ๐ŸŽฏ Description +This beginner challenge focuses on implementing mathematical algorithms in C++. You'll create a function that determines whether a given positive integer is a prime number, helping you practice loops, conditionals, and algorithm optimization. + +## ๐Ÿ” Task +Write a function `bool isPrime(int n)` that: +1. Takes a positive integer as input +2. Returns `true` if the number is prime, `false` otherwise +3. Handles edge cases appropriately (numbers โ‰ค 1 are not prime) +4. Uses an efficient algorithm for checking primality + +### Mathematical Background: +A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself. + +### Examples: +- `isPrime(2)` should return `true` (smallest prime) +- `isPrime(17)` should return `true` +- `isPrime(4)` should return `false` (4 = 2 ร— 2) +- `isPrime(1)` should return `false` (by definition) + +## ๐Ÿ“‹ Requirements +- Function must be implemented in C++20 +- Include proper header guards in header file +- Use efficient algorithm (don't check all numbers up to n) +- Handle negative numbers and edge cases appropriately +- Follow Google C++ Style Guide +- Use appropriate data types + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Basic Prime Numbers +- `isPrime(2)` should return `true` (smallest prime) +- `isPrime(3)` should return `true` +- `isPrime(5)` should return `true` +- `isPrime(7)` should return `true` +- `isPrime(11)` should return `true` + +### 2. Basic Composite Numbers +- `isPrime(4)` should return `false` (2 ร— 2) +- `isPrime(6)` should return `false` (2 ร— 3) +- `isPrime(8)` should return `false` (2 ร— 4) +- `isPrime(9)` should return `false` (3 ร— 3) +- `isPrime(10)` should return `false` (2 ร— 5) + +### 3. Edge Cases +- `isPrime(0)` should return `false` +- `isPrime(1)` should return `false` +- `isPrime(-5)` should return `false` +- `isPrime(-1)` should return `false` + +### 4. Larger Prime Numbers +- `isPrime(13)` should return `true` +- `isPrime(17)` should return `true` +- `isPrime(19)` should return `true` +- `isPrime(23)` should return `true` +- `isPrime(29)` should return `true` + +### 5. Larger Composite Numbers +- `isPrime(15)` should return `false` (3 ร— 5) +- `isPrime(21)` should return `false` (3 ร— 7) +- `isPrime(25)` should return `false` (5 ร— 5) +- `isPrime(27)` should return `false` (3 ร— 9) + +### 6. Performance Test Cases +- `isPrime(97)` should return `true` (larger prime) +- `isPrime(100)` should return `false` (2 ร— 50) +- `isPrime(101)` should return `true` (larger prime) + +### 7. Return Type Validation +- Function should return `bool` type +- Should work with different integer inputs + +## ๐Ÿ“ Files +- `include/solution.h` - Declare your function here +- `solution.cpp` - Implement your solution here +- `test.cpp` - Contains the test cases **DO NOT EDIT** +- `CMakeLists.txt` - Build configuration **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `include/solution.h` and `solution.cpp` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- You only need to check divisors up to โˆšn (square root of n) +- You can skip even numbers after checking for divisibility by 2 +- Consider using a loop starting from 3 and incrementing by 2 +- Include `` if you want to use `sqrt()` function +- Remember that 2 is the only even prime number +- For efficiency: `for (int i = 3; i * i <= n; i += 2)` +- Handle the special case of 2 separately from other numbers diff --git a/cpp/prime_checker/include/solution.h b/cpp/prime_checker/include/solution.h new file mode 100644 index 0000000..8004881 --- /dev/null +++ b/cpp/prime_checker/include/solution.h @@ -0,0 +1,12 @@ +#ifndef SOLUTION_H +#define SOLUTION_H + +/** + * Check if a given positive integer is a prime number. + * + * @param n The integer to check for primality + * @return true if n is prime, false otherwise + */ +bool isPrime(int n); + +#endif // SOLUTION_H diff --git a/cpp/prime_checker/solution.cpp b/cpp/prime_checker/solution.cpp new file mode 100644 index 0000000..3e8d562 --- /dev/null +++ b/cpp/prime_checker/solution.cpp @@ -0,0 +1,13 @@ +#include "solution.h" +#include + +bool isPrime(int n) { + // TODO: Implement prime checking algorithm + // Hints: + // - Numbers <= 1 are not prime + // - 2 is the only even prime number + // - Only check odd divisors up to sqrt(n) + // - Use: for (int i = 3; i * i <= n; i += 2) + + return false; // Replace this with your implementation +} diff --git a/cpp/prime_checker/test.cpp b/cpp/prime_checker/test.cpp new file mode 100644 index 0000000..5d0c878 --- /dev/null +++ b/cpp/prime_checker/test.cpp @@ -0,0 +1,73 @@ +#include "solution.h" +#include + +class PrimeCheckerTest : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(PrimeCheckerTest, BasicPrimeNumbers) { + EXPECT_TRUE(isPrime(2)); // Smallest prime + EXPECT_TRUE(isPrime(3)); + EXPECT_TRUE(isPrime(5)); + EXPECT_TRUE(isPrime(7)); + EXPECT_TRUE(isPrime(11)); +} + +TEST_F(PrimeCheckerTest, BasicCompositeNumbers) { + EXPECT_FALSE(isPrime(4)); // 2 ร— 2 + EXPECT_FALSE(isPrime(6)); // 2 ร— 3 + EXPECT_FALSE(isPrime(8)); // 2 ร— 4 + EXPECT_FALSE(isPrime(9)); // 3 ร— 3 + EXPECT_FALSE(isPrime(10)); // 2 ร— 5 +} + +TEST_F(PrimeCheckerTest, EdgeCases) { + EXPECT_FALSE(isPrime(0)); + EXPECT_FALSE(isPrime(1)); + EXPECT_FALSE(isPrime(-5)); + EXPECT_FALSE(isPrime(-1)); +} + +TEST_F(PrimeCheckerTest, LargerPrimeNumbers) { + EXPECT_TRUE(isPrime(13)); + EXPECT_TRUE(isPrime(17)); + EXPECT_TRUE(isPrime(19)); + EXPECT_TRUE(isPrime(23)); + EXPECT_TRUE(isPrime(29)); +} + +TEST_F(PrimeCheckerTest, LargerCompositeNumbers) { + EXPECT_FALSE(isPrime(15)); // 3 ร— 5 + EXPECT_FALSE(isPrime(21)); // 3 ร— 7 + EXPECT_FALSE(isPrime(25)); // 5 ร— 5 + EXPECT_FALSE(isPrime(27)); // 3 ร— 9 +} + +TEST_F(PrimeCheckerTest, PerformanceTestCases) { + EXPECT_TRUE(isPrime(97)); // Larger prime + EXPECT_FALSE(isPrime(100)); // 2 ร— 50 + EXPECT_TRUE(isPrime(101)); // Larger prime +} + +TEST_F(PrimeCheckerTest, ReturnTypeValidation) { + bool result = isPrime(7); + EXPECT_TRUE(std::is_same_v); +} + +TEST_F(PrimeCheckerTest, AdditionalPrimes) { + EXPECT_TRUE(isPrime(31)); + EXPECT_TRUE(isPrime(37)); + EXPECT_TRUE(isPrime(41)); + EXPECT_TRUE(isPrime(43)); + EXPECT_TRUE(isPrime(47)); +} + +TEST_F(PrimeCheckerTest, AdditionalComposites) { + EXPECT_FALSE(isPrime(33)); // 3 ร— 11 + EXPECT_FALSE(isPrime(35)); // 5 ร— 7 + EXPECT_FALSE(isPrime(39)); // 3 ร— 13 + EXPECT_FALSE(isPrime(49)); // 7 ร— 7 + EXPECT_FALSE(isPrime(51)); // 3 ร— 17 +} diff --git a/cpp/smart_array/CMakeLists.txt b/cpp/smart_array/CMakeLists.txt new file mode 100644 index 0000000..20923c9 --- /dev/null +++ b/cpp/smart_array/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.31) +project(smart_array_challenge VERSION 1.0.0 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +enable_testing() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_executable( + smart_array_test + test.cpp + solution.cpp +) + +target_link_libraries( + smart_array_test + GTest::gtest + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(smart_array_test) diff --git a/cpp/smart_array/README.md b/cpp/smart_array/README.md new file mode 100644 index 0000000..edf3613 --- /dev/null +++ b/cpp/smart_array/README.md @@ -0,0 +1,49 @@ +# Smart Pointer Array + +## ๐ŸŽฏ Description +This advanced challenge focuses on implementing a dynamic array class using modern C++ memory management techniques. You'll create a template class that manages memory automatically and provides safe, efficient array operations. + +## ๐Ÿ” Task +Implement a `SmartArray` template class that manages a dynamic array of elements. + +### Class Requirements: + +#### Template Declaration +```cpp +template +class SmartArray +``` + +#### Constructor and Destructor +- `SmartArray(size_t initial_capacity = 10)` - Initialize with capacity +- `SmartArray(const SmartArray& other)` - Copy constructor +- `SmartArray& operator=(const SmartArray& other)` - Copy assignment +- `~SmartArray()` - Destructor + +#### Core Methods +1. `void push_back(const T& element)` - Add element to end +2. `void pop_back()` - Remove last element +3. `T& at(size_t index)` - Access element with bounds checking +4. `const T& at(size_t index) const` - Const version of at() +5. `size_t size() const` - Return number of elements +6. `size_t capacity() const` - Return current capacity +7. `bool empty() const` - Check if array is empty +8. `void clear()` - Remove all elements + +#### Operators +1. `T& operator[](size_t index)` - Array subscript operator +2. `const T& operator[](size_t index) const` - Const version + +## ๐Ÿ“‹ Requirements +- Must be a template class working with any type T +- Automatic memory management (no memory leaks) +- Dynamic resizing when capacity is exceeded +- Exception safety for bounds checking +- Rule of Three implementation +- Modern C++ features (C++20) + +## ๐Ÿ“ Files +- `include/solution.h` - Declare your template class here +- `solution.cpp` - Template implementations (if needed) +- `test.cpp` - Contains the test cases **DO NOT EDIT** +- `CMakeLists.txt` - Build configuration **DO NOT EDIT** diff --git a/cpp/smart_array/include/solution.h b/cpp/smart_array/include/solution.h new file mode 100644 index 0000000..4e56429 --- /dev/null +++ b/cpp/smart_array/include/solution.h @@ -0,0 +1,79 @@ +#ifndef SOLUTION_H +#define SOLUTION_H + +#include +#include + +template +class SmartArray { +private: + std::unique_ptr data; + size_t array_size; + size_t array_capacity; + + void resize() { + // TODO: Implement automatic resizing when capacity is exceeded + } + +public: + SmartArray(size_t initial_capacity = 10) : array_size(0), array_capacity(initial_capacity) { + // TODO: Initialize with given capacity + } + + SmartArray(const SmartArray& other) { + // TODO: Implement copy constructor + } + + SmartArray& operator=(const SmartArray& other) { + // TODO: Implement copy assignment operator + return *this; + } + + ~SmartArray() = default; // unique_ptr handles cleanup automatically + + void push_back(const T& element) { + // TODO: Add element, resize if necessary + } + + void pop_back() { + // TODO: Remove last element + } + + T& at(size_t index) { + // TODO: Access with bounds checking + throw std::out_of_range("Index out of range"); + } + + const T& at(size_t index) const { + // TODO: Const access with bounds checking + throw std::out_of_range("Index out of range"); + } + + T& operator[](size_t index) { + // TODO: Array subscript (no bounds checking) + return data[0]; // Replace with implementation + } + + const T& operator[](size_t index) const { + // TODO: Const array subscript + return data[0]; // Replace with implementation + } + + size_t size() const { + return array_size; + } + + size_t capacity() const { + return array_capacity; + } + + bool empty() const { + return array_size == 0; + } + + void clear() { + array_size = 0; + } +}; + +#endif // SOLUTION_H diff --git a/cpp/smart_array/solution.cpp b/cpp/smart_array/solution.cpp new file mode 100644 index 0000000..579f983 --- /dev/null +++ b/cpp/smart_array/solution.cpp @@ -0,0 +1,2 @@ +// Template implementation is in the header file +// This file is included for CMake compatibility diff --git a/cpp/smart_array/test.cpp b/cpp/smart_array/test.cpp new file mode 100644 index 0000000..e8395bf --- /dev/null +++ b/cpp/smart_array/test.cpp @@ -0,0 +1,67 @@ +#include "solution.h" +#include +#include + +class SmartArrayTest : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(SmartArrayTest, BasicConstruction) { + SmartArray arr; + EXPECT_EQ(arr.size(), 0); + EXPECT_TRUE(arr.empty()); + EXPECT_GE(arr.capacity(), 10); +} + +TEST_F(SmartArrayTest, PushBackAndAccess) { + SmartArray arr; + arr.push_back(42); + arr.push_back(100); + + EXPECT_EQ(arr.size(), 2); + EXPECT_FALSE(arr.empty()); + EXPECT_EQ(arr[0], 42); + EXPECT_EQ(arr[1], 100); + EXPECT_EQ(arr.at(0), 42); + EXPECT_EQ(arr.at(1), 100); +} + +TEST_F(SmartArrayTest, BoundsChecking) { + SmartArray arr; + arr.push_back(42); + + EXPECT_THROW(arr.at(1), std::out_of_range); + EXPECT_THROW(arr.at(100), std::out_of_range); +} + +TEST_F(SmartArrayTest, PopBack) { + SmartArray arr; + arr.push_back(1); + arr.push_back(2); + arr.push_back(3); + + arr.pop_back(); + EXPECT_EQ(arr.size(), 2); + EXPECT_EQ(arr[1], 2); +} + +TEST_F(SmartArrayTest, Clear) { + SmartArray arr; + arr.push_back(1); + arr.push_back(2); + + arr.clear(); + EXPECT_EQ(arr.size(), 0); + EXPECT_TRUE(arr.empty()); +} + +TEST_F(SmartArrayTest, StringType) { + SmartArray arr; + arr.push_back("hello"); + arr.push_back("world"); + + EXPECT_EQ(arr[0], "hello"); + EXPECT_EQ(arr[1], "world"); +} diff --git a/cpp/string_reverser/CMakeLists.txt b/cpp/string_reverser/CMakeLists.txt new file mode 100644 index 0000000..c776953 --- /dev/null +++ b/cpp/string_reverser/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.31) +project(string_reverser_challenge VERSION 1.0.0 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +enable_testing() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_executable( + string_reverser_test + test.cpp + solution.cpp +) + +target_link_libraries( + string_reverser_test + GTest::gtest + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(string_reverser_test) diff --git a/cpp/string_reverser/README.md b/cpp/string_reverser/README.md new file mode 100644 index 0000000..330c0e8 --- /dev/null +++ b/cpp/string_reverser/README.md @@ -0,0 +1,74 @@ +# String Reverser + +## ๐ŸŽฏ Description +This beginner-level challenge focuses on basic string manipulation in C++. You'll implement a function that reverses a string, helping you understand string handling, iterators, and basic algorithms in modern C++. + +## ๐Ÿ” Task +Write a function `std::string reverseString(const std::string& str)` that: +1. Takes a constant reference to a string as input +2. Returns a new string with characters in reverse order +3. Handles empty strings gracefully +4. Does not modify the original string + +### Examples: +- `reverseString("hello")` should return `"olleh"` +- `reverseString("world")` should return `"dlrow"` +- `reverseString("")` should return `""` +- `reverseString("a")` should return `"a"` + +## ๐Ÿ“‹ Requirements +- Function must be implemented in C++20 +- Include proper header guards in header file +- Use `const` correctness +- Follow Google C++ Style Guide +- Use modern C++ features where appropriate +- Handle empty strings without errors + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Basic String Reversal +- `reverseString("hello")` should return `"olleh"` +- `reverseString("world")` should return `"dlrow"` +- `reverseString("C++")` should return `"++C"` + +### 2. Edge Cases +- `reverseString("")` should return `""` (empty string) +- `reverseString("a")` should return `"a"` (single character) +- `reverseString("ab")` should return `"ba"` (two characters) + +### 3. Special Characters and Numbers +- `reverseString("123")` should return `"321"` +- `reverseString("Hello, World!")` should return `"!dlroW ,olleH"` +- `reverseString(" spaces ")` should return `" secaps "` + +### 4. Palindromes +- `reverseString("racecar")` should return `"racecar"` +- `reverseString("level")` should return `"level"` + +### 5. Longer Strings +- `reverseString("The quick brown fox")` should return `"xof nworb kciuq ehT"` + +### 6. Return Type and Const Correctness +- Function should return `std::string` by value +- Input parameter should not be modified +- Function should work with string literals + +## ๐Ÿ“ Files +- `include/solution.h` - Declare your function here +- `solution.cpp` - Implement your solution here +- `test.cpp` - Contains the test cases **DO NOT EDIT** +- `CMakeLists.txt` - Build configuration **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `include/solution.h` and `solution.cpp` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- You can use `std::reverse()` from `` or implement your own reversal logic +- Consider using iterators for a modern C++ approach +- String construction from reverse iterators: `std::string(str.rbegin(), str.rend())` +- Alternative: manually swap characters from both ends +- Don't forget to include necessary headers (``, `` if needed) +- Remember that `const std::string&` prevents modification of the input diff --git a/cpp/string_reverser/include/solution.h b/cpp/string_reverser/include/solution.h new file mode 100644 index 0000000..8253810 --- /dev/null +++ b/cpp/string_reverser/include/solution.h @@ -0,0 +1,14 @@ +#ifndef SOLUTION_H +#define SOLUTION_H + +#include + +/** + * Reverse a string and return the result. + * + * @param str The input string to reverse (passed by const reference) + * @return A new string with characters in reverse order + */ +std::string reverseString(const std::string& str); + +#endif // SOLUTION_H diff --git a/cpp/string_reverser/solution.cpp b/cpp/string_reverser/solution.cpp new file mode 100644 index 0000000..44e3dbd --- /dev/null +++ b/cpp/string_reverser/solution.cpp @@ -0,0 +1,11 @@ +#include "solution.h" +#include + +std::string reverseString(const std::string& str) { + // TODO: Implement string reversal + // Hint: You can use std::reverse or reverse iterators + // Example with reverse iterators: return std::string(str.rbegin(), str.rend()); + // Example with std::reverse: create copy, reverse it, return it + + return ""; // Replace this with your implementation +} diff --git a/cpp/string_reverser/test.cpp b/cpp/string_reverser/test.cpp new file mode 100644 index 0000000..09508be --- /dev/null +++ b/cpp/string_reverser/test.cpp @@ -0,0 +1,52 @@ +#include "solution.h" +#include + +class StringReverserTest : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(StringReverserTest, BasicStringReversal) { + EXPECT_EQ(reverseString("hello"), "olleh"); + EXPECT_EQ(reverseString("world"), "dlrow"); + EXPECT_EQ(reverseString("C++"), "++C"); +} + +TEST_F(StringReverserTest, EdgeCases) { + EXPECT_EQ(reverseString(""), ""); + EXPECT_EQ(reverseString("a"), "a"); + EXPECT_EQ(reverseString("ab"), "ba"); +} + +TEST_F(StringReverserTest, SpecialCharactersAndNumbers) { + EXPECT_EQ(reverseString("123"), "321"); + EXPECT_EQ(reverseString("Hello, World!"), "!dlroW ,olleH"); + EXPECT_EQ(reverseString(" spaces "), " secaps "); +} + +TEST_F(StringReverserTest, Palindromes) { + EXPECT_EQ(reverseString("racecar"), "racecar"); + EXPECT_EQ(reverseString("level"), "level"); +} + +TEST_F(StringReverserTest, LongerStrings) { + EXPECT_EQ(reverseString("The quick brown fox"), "xof nworb kciuq ehT"); + EXPECT_EQ(reverseString("Programming is fun!"), "!nuf si gnimmargorP"); +} + +TEST_F(StringReverserTest, ReturnType) { + std::string result = reverseString("test"); + EXPECT_TRUE(std::is_same_v); +} + +TEST_F(StringReverserTest, ConstCorrectness) { + const std::string input = "constant"; + std::string result = reverseString(input); + EXPECT_EQ(result, "tnatsnoc"); + EXPECT_EQ(input, "constant"); // Original should be unchanged +} + +TEST_F(StringReverserTest, StringLiterals) { + EXPECT_EQ(reverseString("literal"), "laretil"); +} diff --git a/cpp/vector_math/CMakeLists.txt b/cpp/vector_math/CMakeLists.txt new file mode 100644 index 0000000..57cfcf0 --- /dev/null +++ b/cpp/vector_math/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.31) +project(vector_math_challenge VERSION 1.0.0 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +enable_testing() + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +add_executable( + vector_math_test + test.cpp + solution.cpp +) + +target_link_libraries( + vector_math_test + GTest::gtest + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(vector_math_test) diff --git a/cpp/vector_math/README.md b/cpp/vector_math/README.md new file mode 100644 index 0000000..5e0d1b3 --- /dev/null +++ b/cpp/vector_math/README.md @@ -0,0 +1,106 @@ +# Vector Math Library + +## ๐ŸŽฏ Description +This intermediate challenge focuses on implementing a 2D vector mathematics library in C++. You'll practice class design, operator overloading, and mathematical computations while creating a useful utility class for graphics and physics applications. + +## ๐Ÿ” Task +Implement a `Vector2D` class that represents a 2D vector with x and y components, along with common vector operations. + +### Class Requirements: + +#### Constructor +- `Vector2D(double x = 0.0, double y = 0.0)` - Initialize with x and y components + +#### Public Member Variables +- `double x` - X component of the vector +- `double y` - Y component of the vector + +#### Methods +1. `double magnitude() const` - Calculate and return the magnitude (length) of the vector +2. `double distance(const Vector2D& other) const` - Calculate distance to another vector +3. `Vector2D normalize() const` - Return a normalized (unit) vector +4. `double dot(const Vector2D& other) const` - Calculate dot product with another vector + +#### Operator Overloads +1. `Vector2D operator+(const Vector2D& other) const` - Vector addition +2. `Vector2D operator-(const Vector2D& other) const` - Vector subtraction +3. `Vector2D operator*(double scalar) const` - Scalar multiplication +4. `bool operator==(const Vector2D& other) const` - Equality comparison (with tolerance) + +### Mathematical Formulas: +- **Magnitude**: โˆš(xยฒ + yยฒ) +- **Distance**: โˆš((xโ‚-xโ‚‚)ยฒ + (yโ‚-yโ‚‚)ยฒ) +- **Normalize**: (x/magnitude, y/magnitude) +- **Dot Product**: xโ‚ร—xโ‚‚ + yโ‚ร—yโ‚‚ + +## ๐Ÿ“‹ Requirements +- Class must be implemented in C++20 +- Include proper header guards +- Use `const` correctness for methods that don't modify the object +- Handle edge cases (zero-length vectors, normalization) +- Use appropriate floating-point comparison for equality +- Follow Google C++ Style Guide + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Constructor and Basic Properties +- `Vector2D()` should create vector (0, 0) +- `Vector2D(3.0, 4.0)` should create vector (3, 4) +- Access to `x` and `y` components should work + +### 2. Magnitude Calculation +- `Vector2D(3.0, 4.0).magnitude()` should return 5.0 +- `Vector2D(0.0, 0.0).magnitude()` should return 0.0 +- `Vector2D(1.0, 0.0).magnitude()` should return 1.0 + +### 3. Distance Calculation +- Distance between `(0,0)` and `(3,4)` should be 5.0 +- Distance between `(1,1)` and `(4,5)` should be 5.0 +- Distance from vector to itself should be 0.0 + +### 4. Vector Addition +- `(1,2) + (3,4)` should equal `(4,6)` +- `(0,0) + (5,7)` should equal `(5,7)` +- Addition should be commutative + +### 5. Vector Subtraction +- `(5,7) - (1,2)` should equal `(4,5)` +- `(3,4) - (3,4)` should equal `(0,0)` + +### 6. Scalar Multiplication +- `(2,3) * 2.0` should equal `(4,6)` +- `(1,1) * 0.0` should equal `(0,0)` +- `(3,4) * -1.0` should equal `(-3,-4)` + +### 7. Dot Product +- `(1,2).dot((3,4))` should return 11.0 (1ร—3 + 2ร—4) +- `(1,0).dot((0,1))` should return 0.0 (perpendicular vectors) + +### 8. Normalization +- `Vector2D(3,4).normalize()` should have magnitude 1.0 +- Zero vector normalization should handle gracefully + +### 9. Equality Comparison +- `(1.0,2.0) == (1.0,2.0)` should be true +- Floating-point tolerance should be considered + +## ๐Ÿ“ Files +- `include/solution.h` - Declare your class here +- `solution.cpp` - Implement your solution here +- `test.cpp` - Contains the test cases **DO NOT EDIT** +- `CMakeLists.txt` - Build configuration **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `include/solution.h` and `solution.cpp` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- Include `` for sqrt() function +- Use `const double EPSILON = 1e-9;` for floating-point comparisons +- For normalization of zero vectors, return (0, 0) or handle as special case +- Member functions that don't modify the object should be `const` +- Operator overloading should follow C++ conventions +- Consider using initialization lists in constructors +- Test your implementation with various edge cases diff --git a/cpp/vector_math/include/solution.h b/cpp/vector_math/include/solution.h new file mode 100644 index 0000000..40816cf --- /dev/null +++ b/cpp/vector_math/include/solution.h @@ -0,0 +1,83 @@ +#ifndef SOLUTION_H +#define SOLUTION_H + +/** + * A 2D vector class with mathematical operations. + */ +class Vector2D { +public: + double x; + double y; + + /** + * Constructor to initialize vector components. + * + * @param x X component (default: 0.0) + * @param y Y component (default: 0.0) + */ + Vector2D(double x = 0.0, double y = 0.0); + + /** + * Calculate the magnitude (length) of the vector. + * + * @return The magnitude of the vector + */ + double magnitude() const; + + /** + * Calculate the distance to another vector. + * + * @param other The other vector + * @return The distance between this vector and the other + */ + double distance(const Vector2D& other) const; + + /** + * Return a normalized (unit) vector. + * + * @return A vector with magnitude 1 in the same direction + */ + Vector2D normalize() const; + + /** + * Calculate the dot product with another vector. + * + * @param other The other vector + * @return The dot product + */ + double dot(const Vector2D& other) const; + + /** + * Vector addition operator. + * + * @param other The vector to add + * @return The sum of the vectors + */ + Vector2D operator+(const Vector2D& other) const; + + /** + * Vector subtraction operator. + * + * @param other The vector to subtract + * @return The difference of the vectors + */ + Vector2D operator-(const Vector2D& other) const; + + /** + * Scalar multiplication operator. + * + * @param scalar The scalar to multiply by + * @return The scaled vector + */ + Vector2D operator*(double scalar) const; + + /** + * Equality comparison operator. + * + * @param other The vector to compare with + * @return True if vectors are equal (within tolerance) + */ + bool operator==(const Vector2D& other) const; +}; + +#endif // SOLUTION_H diff --git a/cpp/vector_math/solution.cpp b/cpp/vector_math/solution.cpp new file mode 100644 index 0000000..c9020dd --- /dev/null +++ b/cpp/vector_math/solution.cpp @@ -0,0 +1,51 @@ +#include "solution.h" +#include + +const double EPSILON = 1e-9; + +Vector2D::Vector2D(double x, double y) : x(x), y(y) { + // TODO: Constructor is already implemented above using initialization list +} + +double Vector2D::magnitude() const { + // TODO: Calculate and return sqrt(x^2 + y^2) + return 0.0; // Replace with implementation +} + +double Vector2D::distance(const Vector2D& other) const { + // TODO: Calculate distance between this vector and other + // Formula: sqrt((x1-x2)^2 + (y1-y2)^2) + return 0.0; // Replace with implementation +} + +Vector2D Vector2D::normalize() const { + // TODO: Return normalized vector + // Handle zero-magnitude vector case + return Vector2D(0, 0); // Replace with implementation +} + +double Vector2D::dot(const Vector2D& other) const { + // TODO: Calculate dot product: x1*x2 + y1*y2 + return 0.0; // Replace with implementation +} + +Vector2D Vector2D::operator+(const Vector2D& other) const { + // TODO: Implement vector addition + return Vector2D(0, 0); // Replace with implementation +} + +Vector2D Vector2D::operator-(const Vector2D& other) const { + // TODO: Implement vector subtraction + return Vector2D(0, 0); // Replace with implementation +} + +Vector2D Vector2D::operator*(double scalar) const { + // TODO: Implement scalar multiplication + return Vector2D(0, 0); // Replace with implementation +} + +bool Vector2D::operator==(const Vector2D& other) const { + // TODO: Implement equality comparison with floating-point tolerance + // Use EPSILON for comparison: abs(a - b) < EPSILON + return false; // Replace with implementation +} diff --git a/cpp/vector_math/test.cpp b/cpp/vector_math/test.cpp new file mode 100644 index 0000000..f1a312e --- /dev/null +++ b/cpp/vector_math/test.cpp @@ -0,0 +1,188 @@ +#include "solution.h" +#include +#include + +class Vector2DTest : public ::testing::Test { +protected: + void SetUp() override {} + void TearDown() override {} + + // Helper function for floating point comparison + bool isEqual(double a, double b, double epsilon = 1e-9) { + return std::abs(a - b) < epsilon; + } +}; + +TEST_F(Vector2DTest, ConstructorAndBasicProperties) { + Vector2D v1; + EXPECT_DOUBLE_EQ(v1.x, 0.0); + EXPECT_DOUBLE_EQ(v1.y, 0.0); + + Vector2D v2(3.0, 4.0); + EXPECT_DOUBLE_EQ(v2.x, 3.0); + EXPECT_DOUBLE_EQ(v2.y, 4.0); +} + +TEST_F(Vector2DTest, MagnitudeCalculation) { + Vector2D v1(3.0, 4.0); + EXPECT_DOUBLE_EQ(v1.magnitude(), 5.0); + + Vector2D v2(0.0, 0.0); + EXPECT_DOUBLE_EQ(v2.magnitude(), 0.0); + + Vector2D v3(1.0, 0.0); + EXPECT_DOUBLE_EQ(v3.magnitude(), 1.0); + + Vector2D v4(0.0, 1.0); + EXPECT_DOUBLE_EQ(v4.magnitude(), 1.0); +} + +TEST_F(Vector2DTest, DistanceCalculation) { + Vector2D v1(0.0, 0.0); + Vector2D v2(3.0, 4.0); + EXPECT_DOUBLE_EQ(v1.distance(v2), 5.0); + EXPECT_DOUBLE_EQ(v2.distance(v1), 5.0); // Should be symmetric + + Vector2D v3(1.0, 1.0); + Vector2D v4(4.0, 5.0); + EXPECT_DOUBLE_EQ(v3.distance(v4), 5.0); + + // Distance to itself should be 0 + EXPECT_DOUBLE_EQ(v1.distance(v1), 0.0); +} + +TEST_F(Vector2DTest, VectorAddition) { + Vector2D v1(1.0, 2.0); + Vector2D v2(3.0, 4.0); + Vector2D result = v1 + v2; + + EXPECT_DOUBLE_EQ(result.x, 4.0); + EXPECT_DOUBLE_EQ(result.y, 6.0); + + // Addition with zero vector + Vector2D zero(0.0, 0.0); + Vector2D v3(5.0, 7.0); + Vector2D result2 = zero + v3; + + EXPECT_DOUBLE_EQ(result2.x, 5.0); + EXPECT_DOUBLE_EQ(result2.y, 7.0); + + // Commutative property + Vector2D result3 = v3 + zero; + EXPECT_TRUE(result2 == result3); +} + +TEST_F(Vector2DTest, VectorSubtraction) { + Vector2D v1(5.0, 7.0); + Vector2D v2(1.0, 2.0); + Vector2D result = v1 - v2; + + EXPECT_DOUBLE_EQ(result.x, 4.0); + EXPECT_DOUBLE_EQ(result.y, 5.0); + + // Subtracting from itself + Vector2D v3(3.0, 4.0); + Vector2D result2 = v3 - v3; + + EXPECT_DOUBLE_EQ(result2.x, 0.0); + EXPECT_DOUBLE_EQ(result2.y, 0.0); +} + +TEST_F(Vector2DTest, ScalarMultiplication) { + Vector2D v1(2.0, 3.0); + Vector2D result1 = v1 * 2.0; + + EXPECT_DOUBLE_EQ(result1.x, 4.0); + EXPECT_DOUBLE_EQ(result1.y, 6.0); + + // Multiplication by zero + Vector2D v2(1.0, 1.0); + Vector2D result2 = v2 * 0.0; + + EXPECT_DOUBLE_EQ(result2.x, 0.0); + EXPECT_DOUBLE_EQ(result2.y, 0.0); + + // Negative scalar + Vector2D v3(3.0, 4.0); + Vector2D result3 = v3 * -1.0; + + EXPECT_DOUBLE_EQ(result3.x, -3.0); + EXPECT_DOUBLE_EQ(result3.y, -4.0); +} + +TEST_F(Vector2DTest, DotProduct) { + Vector2D v1(1.0, 2.0); + Vector2D v2(3.0, 4.0); + double result = v1.dot(v2); + + EXPECT_DOUBLE_EQ(result, 11.0); // 1*3 + 2*4 = 11 + + // Perpendicular vectors (dot product should be 0) + Vector2D v3(1.0, 0.0); + Vector2D v4(0.0, 1.0); + double result2 = v3.dot(v4); + + EXPECT_DOUBLE_EQ(result2, 0.0); + + // Dot product with itself (magnitude squared) + Vector2D v5(3.0, 4.0); + double result3 = v5.dot(v5); + + EXPECT_DOUBLE_EQ(result3, 25.0); // 3^2 + 4^2 = 25 +} + +TEST_F(Vector2DTest, Normalization) { + Vector2D v1(3.0, 4.0); + Vector2D normalized = v1.normalize(); + + // Normalized vector should have magnitude 1 + EXPECT_TRUE(isEqual(normalized.magnitude(), 1.0)); + + // Check actual values + EXPECT_TRUE(isEqual(normalized.x, 0.6)); // 3/5 + EXPECT_TRUE(isEqual(normalized.y, 0.8)); // 4/5 + + // Already normalized vector + Vector2D v2(1.0, 0.0); + Vector2D normalized2 = v2.normalize(); + EXPECT_DOUBLE_EQ(normalized2.x, 1.0); + EXPECT_DOUBLE_EQ(normalized2.y, 0.0); +} + +TEST_F(Vector2DTest, ZeroVectorNormalization) { + Vector2D zero(0.0, 0.0); + Vector2D normalized = zero.normalize(); + + // Zero vector should remain zero when normalized + EXPECT_DOUBLE_EQ(normalized.x, 0.0); + EXPECT_DOUBLE_EQ(normalized.y, 0.0); +} + +TEST_F(Vector2DTest, EqualityComparison) { + Vector2D v1(1.0, 2.0); + Vector2D v2(1.0, 2.0); + Vector2D v3(1.1, 2.0); + + EXPECT_TRUE(v1 == v2); + EXPECT_FALSE(v1 == v3); + + // Test floating-point tolerance + Vector2D v4(1.0000000001, 2.0); + EXPECT_TRUE(v1 == v4); // Should be equal within tolerance +} + +TEST_F(Vector2DTest, ComplexOperations) { + Vector2D v1(1.0, 2.0); + Vector2D v2(3.0, 4.0); + + // Chain operations: (v1 + v2) * 2.0 + Vector2D result = (v1 + v2) * 2.0; + EXPECT_DOUBLE_EQ(result.x, 8.0); // (1+3)*2 = 8 + EXPECT_DOUBLE_EQ(result.y, 12.0); // (2+4)*2 = 12 + + // Vector operations preserve original vectors + EXPECT_DOUBLE_EQ(v1.x, 1.0); + EXPECT_DOUBLE_EQ(v1.y, 2.0); + EXPECT_DOUBLE_EQ(v2.x, 3.0); + EXPECT_DOUBLE_EQ(v2.y, 4.0); +} diff --git a/python/csv_data_parser/README.md b/python/csv_data_parser/README.md new file mode 100644 index 0000000..0e178c1 --- /dev/null +++ b/python/csv_data_parser/README.md @@ -0,0 +1,88 @@ +# CSV Data Parser + +## ๐ŸŽฏ Description +This challenge focuses on file I/O operations and data parsing in Python. You'll implement a function that reads CSV (Comma-Separated Values) files and converts them into a more usable format for data processing. + +## ๐Ÿ” Task +Write a function `parse_csv(file_path: str) -> list[dict]` that: +1. Takes a file path to a CSV file as input +2. Reads the CSV file and parses its contents +3. Returns a list of dictionaries where: + - Each dictionary represents one row of data + - Dictionary keys are the column headers from the first row + - Dictionary values are the corresponding cell values + +### Expected CSV Format +```csv +name,age,city,salary +John Doe,30,New York,75000 +Jane Smith,25,Los Angeles,65000 +Bob Johnson,35,Chicago,80000 +``` + +Should return: +```python +[ + {'name': 'John Doe', 'age': '30', 'city': 'New York', 'salary': '75000'}, + {'name': 'Jane Smith', 'age': '25', 'city': 'Los Angeles', 'salary': '65000'}, + {'name': 'Bob Johnson', 'age': '35', 'city': 'Chicago', 'salary': '80000'} +] +``` + +## ๐Ÿ“‹ Requirements +- Function must be implemented in Python 3.8+ +- Include proper type hints +- Follow PEP 8 style guidelines +- Handle empty files gracefully +- Handle files with only headers +- Raise appropriate exceptions for file access errors +- Do not use external libraries (only built-in Python modules) +- Strip whitespace from values + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Basic CSV Parsing +- Parse a simple CSV with headers and data rows +- Verify correct dictionary structure +- Verify all rows are included + +### 2. Edge Cases +- Empty CSV file should return empty list +- CSV with only headers should return empty list +- CSV with empty cells should handle gracefully +- CSV with extra commas should handle gracefully + +### 3. Data Integrity +- Whitespace around values should be stripped +- All values should be preserved as strings +- Headers should be used as dictionary keys + +### 4. File Handling +- Non-existent file should raise `FileNotFoundError` +- Invalid file permissions should raise appropriate exception +- File reading errors should be properly handled + +### 5. Format Variations +- Handle CSV with different numbers of columns per row +- Handle CSV with quoted values containing commas +- Handle empty lines in CSV files + +## ๐Ÿ“ Files +- `solution.py` - Implement your solution here +- `test_main.py` - Contains the test cases **DO NOT EDIT** +- Sample CSV files will be created during testing + +## โœ… Submission +1. Implement your solution in `solution.py` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- Use Python's built-in file handling capabilities (`open()`, `with` statement) +- Consider using `str.split(',')` for basic parsing, but be aware of edge cases +- Remember to handle the header row separately from data rows +- Use `str.strip()` to remove whitespace +- Consider what happens with empty rows or malformed data +- Think about how to handle files with inconsistent column counts +- Test your solution with various CSV formats to ensure robustness diff --git a/python/csv_data_parser/solution.py b/python/csv_data_parser/solution.py new file mode 100644 index 0000000..9284436 --- /dev/null +++ b/python/csv_data_parser/solution.py @@ -0,0 +1,27 @@ +def parse_csv(file_path: str) -> list[dict]: + """ + Parse a CSV file and return its contents as a list of dictionaries. + + Args: + file_path (str): Path to the CSV file to parse + + Returns: + list[dict]: List of dictionaries representing CSV rows, + where keys are column headers and values are cell contents + + Raises: + FileNotFoundError: If the specified file doesn't exist + PermissionError: If file cannot be read due to permissions + IOError: If there's an error reading the file + """ + if not isinstance(file_path, str): + raise TypeError("File path must be a string") + + # TODO: Implement CSV parsing logic + # Steps: + # 1. Open and read the file + # 2. Split content into lines + # 3. Parse header row to get column names + # 4. Parse data rows and create dictionaries + # 5. Handle edge cases (empty files, malformed rows, etc.) + pass diff --git a/python/csv_data_parser/test_main.py b/python/csv_data_parser/test_main.py new file mode 100644 index 0000000..70636a2 --- /dev/null +++ b/python/csv_data_parser/test_main.py @@ -0,0 +1,165 @@ +import unittest +import tempfile +import os +from solution import parse_csv + + +class TestCSVDataParser(unittest.TestCase): + + def setUp(self): + """Set up temporary files for testing""" + self.temp_dir = tempfile.mkdtemp() + + def tearDown(self): + """Clean up temporary files""" + import shutil + shutil.rmtree(self.temp_dir, ignore_errors=True) + + def create_temp_csv(self, filename, content): + """Helper method to create temporary CSV files""" + filepath = os.path.join(self.temp_dir, filename) + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + return filepath + + def test_basic_csv_parsing(self): + """Test basic CSV parsing functionality""" + csv_content = """name,age,city,salary +John Doe,30,New York,75000 +Jane Smith,25,Los Angeles,65000 +Bob Johnson,35,Chicago,80000""" + + filepath = self.create_temp_csv('test.csv', csv_content) + result = parse_csv(filepath) + + expected = [ + {'name': 'John Doe', 'age': '30', 'city': 'New York', 'salary': '75000'}, + {'name': 'Jane Smith', 'age': '25', 'city': 'Los Angeles', 'salary': '65000'}, + {'name': 'Bob Johnson', 'age': '35', 'city': 'Chicago', 'salary': '80000'} + ] + + self.assertEqual(result, expected) + self.assertEqual(len(result), 3) + + def test_empty_file(self): + """Test parsing empty CSV file""" + filepath = self.create_temp_csv('empty.csv', '') + result = parse_csv(filepath) + self.assertEqual(result, []) + + def test_headers_only(self): + """Test CSV with only headers""" + csv_content = "name,age,city,salary" + filepath = self.create_temp_csv('headers_only.csv', csv_content) + result = parse_csv(filepath) + self.assertEqual(result, []) + + def test_whitespace_handling(self): + """Test CSV with extra whitespace""" + csv_content = """name, age , city,salary + John Doe , 30 , New York , 75000 +Jane Smith,25,Los Angeles,65000""" + + filepath = self.create_temp_csv('whitespace.csv', csv_content) + result = parse_csv(filepath) + + expected = [ + {'name': 'John Doe', 'age': '30', 'city': 'New York', 'salary': '75000'}, + {'name': 'Jane Smith', 'age': '25', 'city': 'Los Angeles', 'salary': '65000'} + ] + + self.assertEqual(result, expected) + + def test_empty_cells(self): + """Test CSV with empty cells""" + csv_content = """name,age,city,salary +John Doe,,New York,75000 +,25,Los Angeles, +Bob Johnson,35,,80000""" + + filepath = self.create_temp_csv('empty_cells.csv', csv_content) + result = parse_csv(filepath) + + expected = [ + {'name': 'John Doe', 'age': '', 'city': 'New York', 'salary': '75000'}, + {'name': '', 'age': '25', 'city': 'Los Angeles', 'salary': ''}, + {'name': 'Bob Johnson', 'age': '35', 'city': '', 'salary': '80000'} + ] + + self.assertEqual(result, expected) + + def test_inconsistent_columns(self): + """Test CSV with inconsistent column counts""" + csv_content = """name,age,city,salary +John Doe,30,New York +Jane Smith,25,Los Angeles,65000,Extra +Bob Johnson,35,Chicago,80000""" + + filepath = self.create_temp_csv('inconsistent.csv', csv_content) + result = parse_csv(filepath) + + # Should handle missing or extra columns gracefully + self.assertEqual(len(result), 3) + self.assertIn('name', result[0]) + self.assertIn('age', result[0]) + + def test_file_not_found(self): + """Test handling of non-existent file""" + with self.assertRaises(FileNotFoundError): + parse_csv('/nonexistent/file.csv') + + def test_single_row(self): + """Test CSV with single data row""" + csv_content = """name,age,city +John Doe,30,New York""" + + filepath = self.create_temp_csv('single_row.csv', csv_content) + result = parse_csv(filepath) + + expected = [ + {'name': 'John Doe', 'age': '30', 'city': 'New York'} + ] + + self.assertEqual(result, expected) + + def test_return_type(self): + """Test that function returns correct types""" + csv_content = """name,age +John,30""" + + filepath = self.create_temp_csv('types.csv', csv_content) + result = parse_csv(filepath) + + self.assertIsInstance(result, list) + self.assertIsInstance(result[0], dict) + self.assertIsInstance(result[0]['name'], str) + self.assertIsInstance(result[0]['age'], str) + + def test_empty_lines(self): + """Test CSV with empty lines""" + csv_content = """name,age,city + +John Doe,30,New York + +Jane Smith,25,Los Angeles + +""" + + filepath = self.create_temp_csv('empty_lines.csv', csv_content) + result = parse_csv(filepath) + + # Should skip empty lines + self.assertEqual(len(result), 2) + self.assertEqual(result[0]['name'], 'John Doe') + self.assertEqual(result[1]['name'], 'Jane Smith') + + def test_input_validation(self): + """Test input validation""" + with self.assertRaises(TypeError): + parse_csv(123) + with self.assertRaises(TypeError): + parse_csv(None) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/factorial_calculator/README.md b/python/factorial_calculator/README.md new file mode 100644 index 0000000..bcf1158 --- /dev/null +++ b/python/factorial_calculator/README.md @@ -0,0 +1,67 @@ +# Factorial Calculator + +## ๐ŸŽฏ Description +The factorial of a non-negative integer n is the product of all positive integers less than or equal to n. This challenge will help you understand recursive thinking, mathematical computation, and proper error handling in Python. + +## ๐Ÿ” Task +Write a function `factorial(n: int) -> int` that: +1. Takes a non-negative integer parameter `n` +2. Returns the factorial of `n` (n!) +3. Handles the special case where 0! = 1 +4. Raises appropriate exceptions for invalid inputs + +Mathematical definition: +- 0! = 1 +- n! = n ร— (n-1) ร— (n-2) ร— ... ร— 2 ร— 1 for n > 0 + +## ๐Ÿ“‹ Requirements +- Function must be implemented in Python 3.8+ +- Include proper type hints +- Follow PEP 8 style guidelines +- Raise `ValueError` for negative integers +- Raise `TypeError` for non-integer inputs +- Must handle large factorials efficiently + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Basic Factorial Tests +- `factorial(0)` should return `1` +- `factorial(1)` should return `1` +- `factorial(5)` should return `120` +- `factorial(10)` should return `3628800` + +### 2. Edge Cases +- `factorial(2)` should return `2` +- `factorial(3)` should return `6` +- `factorial(4)` should return `24` + +### 3. Large Numbers +- `factorial(20)` should return `2432902008176640000` +- `factorial(15)` should return `1307674368000` + +### 4. Error Handling +- `factorial(-1)` should raise `ValueError` +- `factorial(-5)` should raise `ValueError` +- `factorial(3.5)` should raise `TypeError` +- `factorial("5")` should raise `TypeError` +- `factorial(None)` should raise `TypeError` + +### 5. Return Type Tests +- The function should always return an integer for valid inputs + +## ๐Ÿ“ Files +- `solution.py` - Implement your solution here +- `test_main.py` - Contains the test cases **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `solution.py` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- You can implement this iteratively or recursively +- Remember that 0! = 1 by mathematical convention +- Python's `int` type can handle arbitrarily large numbers +- Consider input validation before computation +- For large numbers, iterative solutions may be more efficient than recursive ones diff --git a/python/factorial_calculator/solution.py b/python/factorial_calculator/solution.py new file mode 100644 index 0000000..d54cf2c --- /dev/null +++ b/python/factorial_calculator/solution.py @@ -0,0 +1,23 @@ +def factorial(n: int) -> int: + """ + Calculate the factorial of a non-negative integer. + + Args: + n (int): A non-negative integer + + Returns: + int: The factorial of n (n!) + + Raises: + TypeError: If input is not an integer + ValueError: If input is negative + """ + if not isinstance(n, int): + raise TypeError("Input must be an integer") + + if n < 0: + raise ValueError("Factorial is not defined for negative numbers") + + # TODO: Implement factorial calculation + # Remember: 0! = 1, and n! = n ร— (n-1) ร— (n-2) ร— ... ร— 1 + pass diff --git a/python/factorial_calculator/test_main.py b/python/factorial_calculator/test_main.py new file mode 100644 index 0000000..86bf5b3 --- /dev/null +++ b/python/factorial_calculator/test_main.py @@ -0,0 +1,60 @@ +import unittest +from solution import factorial + + +class TestFactorialCalculator(unittest.TestCase): + + def test_basic_factorials(self): + """Test basic factorial calculations""" + self.assertEqual(factorial(0), 1) + self.assertEqual(factorial(1), 1) + self.assertEqual(factorial(5), 120) + self.assertEqual(factorial(10), 3628800) + + def test_edge_cases(self): + """Test small number factorials""" + self.assertEqual(factorial(2), 2) + self.assertEqual(factorial(3), 6) + self.assertEqual(factorial(4), 24) + + def test_large_numbers(self): + """Test larger factorial calculations""" + self.assertEqual(factorial(20), 2432902008176640000) + self.assertEqual(factorial(15), 1307674368000) + + def test_error_handling_negative(self): + """Test error handling for negative numbers""" + with self.assertRaises(ValueError): + factorial(-1) + with self.assertRaises(ValueError): + factorial(-5) + with self.assertRaises(ValueError): + factorial(-100) + + def test_error_handling_type(self): + """Test error handling for wrong input types""" + with self.assertRaises(TypeError): + factorial(3.5) + with self.assertRaises(TypeError): + factorial("5") + with self.assertRaises(TypeError): + factorial(None) + with self.assertRaises(TypeError): + factorial([5]) + + def test_return_type(self): + """Test that function returns integers""" + result = factorial(5) + self.assertIsInstance(result, int) + result = factorial(0) + self.assertIsInstance(result, int) + + def test_additional_cases(self): + """Test additional factorial cases""" + self.assertEqual(factorial(6), 720) + self.assertEqual(factorial(7), 5040) + self.assertEqual(factorial(8), 40320) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/inventory_system/README.md b/python/inventory_system/README.md new file mode 100644 index 0000000..1266cf3 --- /dev/null +++ b/python/inventory_system/README.md @@ -0,0 +1,113 @@ +# Inventory Management System + +## ๐ŸŽฏ Description +This advanced challenge combines object-oriented programming with data management to create a comprehensive inventory system. You'll implement multiple classes that work together to manage products and inventory operations. + +## ๐Ÿ” Task +Implement two classes: `Product` and `Inventory` that work together to manage a collection of products. + +### Product Class +Create a `Product` class with the following specifications: + +#### Constructor +- `__init__(self, product_id: int, name: str, price: float, quantity: int)` +- Initialize a product with ID, name, price, and quantity +- Should raise `ValueError` for invalid inputs (negative price/quantity, empty name) + +#### Properties/Methods +- `product_id: int` - Unique product identifier (read-only) +- `name: str` - Product name (read-only) +- `price: float` - Product price (can be updated via setter) +- `quantity: int` - Available quantity (can be updated via setter) +- `total_value() -> float` - Returns price ร— quantity +- `__str__() -> str` - String representation: "ID: {id}, Name: {name}, Price: ${price:.2f}, Qty: {quantity}" + +### Inventory Class +Create an `Inventory` class with the following specifications: + +#### Constructor +- `__init__(self)` - Initialize empty inventory + +#### Methods +1. `add_product(self, product: Product) -> None` + - Add a product to inventory + - If product ID already exists, raise `ValueError` + +2. `remove_product(self, product_id: int) -> bool` + - Remove product from inventory + - Return `True` if removed, `False` if product not found + +3. `update_stock(self, product_id: int, quantity: int) -> bool` + - Update product quantity (add to existing quantity) + - Return `True` if updated, `False` if product not found + - Raise `ValueError` if resulting quantity would be negative + +4. `get_product(self, product_id: int) -> Product | None` + - Return product object by ID, or None if not found + +5. `get_total_value(self) -> float` + - Return total value of all products in inventory + +6. `get_low_stock_products(self, threshold: int = 5) -> list[Product]` + - Return list of products with quantity <= threshold + +7. `generate_report(self) -> str` + - Return formatted inventory report with all products + +## ๐Ÿ“‹ Requirements +- Classes must be implemented in Python 3.8+ +- Include proper type hints +- Follow PEP 8 style guidelines +- Use appropriate encapsulation (private/protected attributes where needed) +- Implement proper validation for all inputs +- Handle edge cases gracefully + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Product Class Tests +- Constructor with valid inputs should create product correctly +- Invalid inputs should raise appropriate exceptions +- Properties should be accessible and updateable where appropriate +- `total_value()` should calculate correctly +- String representation should be formatted correctly + +### 2. Inventory Class Tests +- Empty inventory should be initialized correctly +- Adding products should work for unique IDs +- Adding duplicate product IDs should raise exception +- Removing existing products should return `True` +- Removing non-existent products should return `False` + +### 3. Stock Management Tests +- Updating stock should modify quantity correctly +- Stock updates that would result in negative quantity should raise exception +- Getting products by ID should work correctly +- Getting non-existent products should return `None` + +### 4. Reporting Tests +- Total value calculation should be accurate +- Low stock detection should work with default and custom thresholds +- Inventory report should be properly formatted + +### 5. Edge Cases +- Empty inventory operations should handle gracefully +- Large quantities and prices should be handled correctly +- Multiple operations in sequence should maintain data integrity + +## ๐Ÿ“ Files +- `solution.py` - Implement your solution here +- `test_main.py` - Contains the test cases **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `solution.py` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- Use properties and setters to control access to attributes +- Consider using a dictionary to store products in the inventory (with product_id as key) +- Validate inputs in constructors and methods +- Think about when to raise exceptions vs. return error indicators +- Use list comprehensions for filtering operations like low stock detection +- Format monetary values appropriately in string representations diff --git a/python/inventory_system/solution.py b/python/inventory_system/solution.py new file mode 100644 index 0000000..84a96e7 --- /dev/null +++ b/python/inventory_system/solution.py @@ -0,0 +1,197 @@ +from typing import Optional + + +class Product: + """ + Represents a product in the inventory system. + """ + + def __init__(self, product_id: int, name: str, price: float, quantity: int) -> None: + """ + Initialize a new product. + + Args: + product_id (int): Unique product identifier + name (str): Product name + price (float): Product price (must be non-negative) + quantity (int): Available quantity (must be non-negative) + + Raises: + ValueError: If price or quantity is negative, or name is empty + TypeError: If inputs have wrong types + """ + if not isinstance(product_id, int): + raise TypeError("Product ID must be an integer") + if not isinstance(name, str) or not name.strip(): + raise ValueError("Product name must be a non-empty string") + if not isinstance(price, (int, float)) or price < 0: + raise ValueError("Price must be a non-negative number") + if not isinstance(quantity, int) or quantity < 0: + raise ValueError("Quantity must be a non-negative integer") + + # TODO: Initialize instance variables + pass + + @property + def product_id(self) -> int: + """Get the product ID (read-only).""" + # TODO: Return product ID + pass + + @property + def name(self) -> str: + """Get the product name (read-only).""" + # TODO: Return product name + pass + + @property + def price(self) -> float: + """Get the product price.""" + # TODO: Return product price + pass + + @price.setter + def price(self, value: float) -> None: + """Set the product price.""" + if not isinstance(value, (int, float)) or value < 0: + raise ValueError("Price must be a non-negative number") + # TODO: Set product price + pass + + @property + def quantity(self) -> int: + """Get the product quantity.""" + # TODO: Return product quantity + pass + + @quantity.setter + def quantity(self, value: int) -> None: + """Set the product quantity.""" + if not isinstance(value, int) or value < 0: + raise ValueError("Quantity must be a non-negative integer") + # TODO: Set product quantity + pass + + def total_value(self) -> float: + """ + Calculate the total value of this product (price ร— quantity). + + Returns: + float: Total value of the product + """ + # TODO: Calculate and return total value + pass + + def __str__(self) -> str: + """ + Return string representation of the product. + + Returns: + str: Formatted product information + """ + # TODO: Return formatted string: "ID: {id}, Name: {name}, Price: ${price:.2f}, Qty: {quantity}" + pass + + +class Inventory: + """ + Manages a collection of products. + """ + + def __init__(self) -> None: + """Initialize an empty inventory.""" + # TODO: Initialize inventory data structure + pass + + def add_product(self, product: Product) -> None: + """ + Add a product to the inventory. + + Args: + product (Product): Product to add + + Raises: + ValueError: If product ID already exists + TypeError: If product is not a Product instance + """ + if not isinstance(product, Product): + raise TypeError("Must provide a Product instance") + + # TODO: Check for duplicate ID and add product + pass + + def remove_product(self, product_id: int) -> bool: + """ + Remove a product from inventory. + + Args: + product_id (int): ID of product to remove + + Returns: + bool: True if removed, False if not found + """ + # TODO: Remove product if exists, return success status + pass + + def update_stock(self, product_id: int, quantity: int) -> bool: + """ + Update product stock (add to existing quantity). + + Args: + product_id (int): ID of product to update + quantity (int): Quantity to add (can be negative) + + Returns: + bool: True if updated, False if product not found + + Raises: + ValueError: If resulting quantity would be negative + """ + # TODO: Find product, validate new quantity, and update + pass + + def get_product(self, product_id: int) -> Optional[Product]: + """ + Get a product by its ID. + + Args: + product_id (int): Product ID to search for + + Returns: + Product or None: The product if found, None otherwise + """ + # TODO: Return product if found, None otherwise + pass + + def get_total_value(self) -> float: + """ + Calculate total value of all products in inventory. + + Returns: + float: Total inventory value + """ + # TODO: Sum total values of all products + pass + + def get_low_stock_products(self, threshold: int = 5) -> list[Product]: + """ + Get products with stock below or equal to threshold. + + Args: + threshold (int): Stock threshold (default: 5) + + Returns: + list[Product]: List of low-stock products + """ + # TODO: Filter products by quantity <= threshold + pass + + def generate_report(self) -> str: + """ + Generate a formatted inventory report. + + Returns: + str: Formatted report with all products + """ + # TODO: Create formatted report of all products + pass diff --git a/python/inventory_system/test_main.py b/python/inventory_system/test_main.py new file mode 100644 index 0000000..f3b4717 --- /dev/null +++ b/python/inventory_system/test_main.py @@ -0,0 +1,238 @@ +import unittest +from solution import Product, Inventory + + +class TestProduct(unittest.TestCase): + + def test_product_creation_valid(self): + """Test creating product with valid inputs""" + product = Product(1, "Laptop", 999.99, 10) + self.assertEqual(product.product_id, 1) + self.assertEqual(product.name, "Laptop") + self.assertEqual(product.price, 999.99) + self.assertEqual(product.quantity, 10) + + def test_product_creation_invalid_inputs(self): + """Test product creation with invalid inputs""" + # Invalid product ID + with self.assertRaises(TypeError): + Product("1", "Laptop", 999.99, 10) + + # Empty name + with self.assertRaises(ValueError): + Product(1, "", 999.99, 10) + with self.assertRaises(ValueError): + Product(1, " ", 999.99, 10) + + # Negative price + with self.assertRaises(ValueError): + Product(1, "Laptop", -100, 10) + + # Negative quantity + with self.assertRaises(ValueError): + Product(1, "Laptop", 999.99, -5) + + def test_product_properties_readonly(self): + """Test that ID and name are read-only""" + product = Product(1, "Laptop", 999.99, 10) + + # These should be read-only (no setters) + with self.assertRaises(AttributeError): + product.product_id = 2 + with self.assertRaises(AttributeError): + product.name = "Desktop" + + def test_product_price_setter(self): + """Test price property setter""" + product = Product(1, "Laptop", 999.99, 10) + + # Valid price update + product.price = 1199.99 + self.assertEqual(product.price, 1199.99) + + # Invalid price update + with self.assertRaises(ValueError): + product.price = -100 + + def test_product_quantity_setter(self): + """Test quantity property setter""" + product = Product(1, "Laptop", 999.99, 10) + + # Valid quantity update + product.quantity = 15 + self.assertEqual(product.quantity, 15) + + # Invalid quantity update + with self.assertRaises(ValueError): + product.quantity = -5 + + def test_product_total_value(self): + """Test total value calculation""" + product = Product(1, "Laptop", 999.99, 10) + expected_value = 999.99 * 10 + self.assertAlmostEqual(product.total_value(), expected_value, places=2) + + # Test after updates + product.price = 1200 + product.quantity = 5 + self.assertEqual(product.total_value(), 6000.0) + + def test_product_string_representation(self): + """Test string representation""" + product = Product(1, "Laptop", 999.99, 10) + expected = "ID: 1, Name: Laptop, Price: $999.99, Qty: 10" + self.assertEqual(str(product), expected) + + +class TestInventory(unittest.TestCase): + + def setUp(self): + """Set up test fixtures""" + self.inventory = Inventory() + self.product1 = Product(1, "Laptop", 999.99, 10) + self.product2 = Product(2, "Mouse", 25.50, 50) + self.product3 = Product(3, "Keyboard", 75.00, 3) + + def test_inventory_creation(self): + """Test creating empty inventory""" + inventory = Inventory() + self.assertEqual(inventory.get_total_value(), 0.0) + + def test_add_product_success(self): + """Test adding products to inventory""" + self.inventory.add_product(self.product1) + retrieved = self.inventory.get_product(1) + self.assertEqual(retrieved, self.product1) + + def test_add_product_duplicate_id(self): + """Test adding product with duplicate ID""" + self.inventory.add_product(self.product1) + duplicate = Product(1, "Desktop", 1500, 5) + + with self.assertRaises(ValueError): + self.inventory.add_product(duplicate) + + def test_add_product_invalid_type(self): + """Test adding non-Product object""" + with self.assertRaises(TypeError): + self.inventory.add_product("not a product") + + def test_remove_product_success(self): + """Test removing existing product""" + self.inventory.add_product(self.product1) + result = self.inventory.remove_product(1) + + self.assertTrue(result) + self.assertIsNone(self.inventory.get_product(1)) + + def test_remove_product_not_found(self): + """Test removing non-existent product""" + result = self.inventory.remove_product(999) + self.assertFalse(result) + + def test_update_stock_success(self): + """Test updating stock successfully""" + self.inventory.add_product(self.product1) + result = self.inventory.update_stock(1, 5) + + self.assertTrue(result) + self.assertEqual(self.inventory.get_product(1).quantity, 15) + + def test_update_stock_negative_quantity(self): + """Test updating stock with invalid quantity""" + self.inventory.add_product(self.product1) + + # This should work (reduce stock) + result = self.inventory.update_stock(1, -5) + self.assertTrue(result) + self.assertEqual(self.inventory.get_product(1).quantity, 5) + + # This should fail (would result in negative stock) + with self.assertRaises(ValueError): + self.inventory.update_stock(1, -10) + + def test_update_stock_not_found(self): + """Test updating stock for non-existent product""" + result = self.inventory.update_stock(999, 5) + self.assertFalse(result) + + def test_get_product_exists(self): + """Test getting existing product""" + self.inventory.add_product(self.product1) + retrieved = self.inventory.get_product(1) + self.assertEqual(retrieved, self.product1) + + def test_get_product_not_exists(self): + """Test getting non-existent product""" + result = self.inventory.get_product(999) + self.assertIsNone(result) + + def test_get_total_value(self): + """Test calculating total inventory value""" + self.inventory.add_product(self.product1) # 999.99 * 10 = 9999.90 + self.inventory.add_product(self.product2) # 25.50 * 50 = 1275.00 + + expected_total = (999.99 * 10) + (25.50 * 50) + actual_total = self.inventory.get_total_value() + self.assertAlmostEqual(actual_total, expected_total, places=2) + + def test_get_low_stock_products_default(self): + """Test getting low stock products with default threshold""" + self.inventory.add_product(self.product1) # qty: 10 (not low) + self.inventory.add_product(self.product2) # qty: 50 (not low) + self.inventory.add_product(self.product3) # qty: 3 (low) + + low_stock = self.inventory.get_low_stock_products() + self.assertEqual(len(low_stock), 1) + self.assertEqual(low_stock[0], self.product3) + + def test_get_low_stock_products_custom_threshold(self): + """Test getting low stock products with custom threshold""" + self.inventory.add_product(self.product1) # qty: 10 + self.inventory.add_product(self.product2) # qty: 50 + self.inventory.add_product(self.product3) # qty: 3 + + low_stock = self.inventory.get_low_stock_products(threshold=15) + self.assertEqual(len(low_stock), 2) # product1 and product3 + + def test_generate_report(self): + """Test generating inventory report""" + self.inventory.add_product(self.product1) + self.inventory.add_product(self.product2) + + report = self.inventory.generate_report() + self.assertIsInstance(report, str) + self.assertIn("Laptop", report) + self.assertIn("Mouse", report) + self.assertIn("$999.99", report) + self.assertIn("$25.50", report) + + def test_empty_inventory_operations(self): + """Test operations on empty inventory""" + self.assertEqual(self.inventory.get_total_value(), 0.0) + self.assertEqual(self.inventory.get_low_stock_products(), []) + self.assertFalse(self.inventory.remove_product(1)) + self.assertFalse(self.inventory.update_stock(1, 5)) + + def test_multiple_operations_sequence(self): + """Test sequence of multiple operations""" + # Add products + self.inventory.add_product(self.product1) + self.inventory.add_product(self.product2) + + # Update stock + self.inventory.update_stock(1, -2) # Laptop: 10 -> 8 + self.inventory.update_stock(2, 25) # Mouse: 50 -> 75 + + # Verify changes + self.assertEqual(self.inventory.get_product(1).quantity, 8) + self.assertEqual(self.inventory.get_product(2).quantity, 75) + + # Calculate new total + expected_total = (999.99 * 8) + (25.50 * 75) + actual_total = self.inventory.get_total_value() + self.assertAlmostEqual(actual_total, expected_total, places=2) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/palindrome_checker/README.md b/python/palindrome_checker/README.md new file mode 100644 index 0000000..b0aea83 --- /dev/null +++ b/python/palindrome_checker/README.md @@ -0,0 +1,60 @@ +# Palindrome Checker + +## ๐ŸŽฏ Description +A palindrome is a word, phrase, number, or other sequence of characters that reads the same forward and backward. This challenge will help you practice string manipulation and algorithmic thinking by implementing a function that checks if a string is a palindrome. + +## ๐Ÿ” Task +Write a function `is_palindrome(s: str) -> bool` that: +1. Takes a string parameter `s` +2. Returns `True` if the string is a palindrome, `False` otherwise +3. Ignores case differences (e.g., "Aa" should be considered a palindrome) +4. Ignores non-alphanumeric characters (spaces, punctuation, etc.) + +## ๐Ÿ“‹ Requirements +- Function must be implemented in Python 3.8+ +- Include proper type hints +- Follow PEP 8 style guidelines +- Handle empty strings gracefully +- Only consider alphanumeric characters for palindrome checking + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Basic Palindrome Tests +- `is_palindrome("racecar")` should return `True` +- `is_palindrome("hello")` should return `False` +- `is_palindrome("A")` should return `True` +- `is_palindrome("")` should return `True` + +### 2. Case Insensitive Tests +- `is_palindrome("Racecar")` should return `True` +- `is_palindrome("MadAm")` should return `True` +- `is_palindrome("NoLemon")` should return `False` + +### 3. Alphanumeric Tests +- `is_palindrome("A man a plan a canal Panama")` should return `True` +- `is_palindrome("race a car")` should return `False` +- `is_palindrome("Was it a car or a cat I saw?")` should return `True` + +### 4. Special Cases +- `is_palindrome("12321")` should return `True` +- `is_palindrome("12345")` should return `False` +- `is_palindrome("Madam123321madaM")` should return `True` +- `is_palindrome("!@#$%^&*()")` should return `True` (no alphanumeric characters) + +### 5. Type Validation Tests +- Function should raise `TypeError` for non-string inputs + +## ๐Ÿ“ Files +- `solution.py` - Implement your solution here +- `test_main.py` - Contains the test cases **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `solution.py` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- Consider using Python's built-in string methods like `isalnum()` and `lower()` +- Think about how to efficiently compare characters from both ends of the string +- Remember that empty strings are considered palindromes by convention diff --git a/python/palindrome_checker/solution.py b/python/palindrome_checker/solution.py new file mode 100644 index 0000000..173b4a8 --- /dev/null +++ b/python/palindrome_checker/solution.py @@ -0,0 +1,20 @@ +def is_palindrome(s: str) -> bool: + """ + Check if a string is a palindrome, ignoring case and non-alphanumeric characters. + + Args: + s (str): The string to check + + Returns: + bool: True if the string is a palindrome, False otherwise + + Raises: + TypeError: If input is not a string + """ + if not isinstance(s, str): + raise TypeError("Input must be a string") + + # TODO: Implement palindrome checking logic + # Hint: Consider using string methods to clean the input + # and then compare characters from both ends + pass diff --git a/python/palindrome_checker/test_main.py b/python/palindrome_checker/test_main.py new file mode 100644 index 0000000..e38d23b --- /dev/null +++ b/python/palindrome_checker/test_main.py @@ -0,0 +1,51 @@ +import unittest +from solution import is_palindrome + + +class TestPalindromeChecker(unittest.TestCase): + + def test_basic_palindromes(self): + """Test basic palindrome functionality""" + self.assertTrue(is_palindrome("racecar")) + self.assertFalse(is_palindrome("hello")) + self.assertTrue(is_palindrome("A")) + self.assertTrue(is_palindrome("")) + + def test_case_insensitive(self): + """Test case insensitive palindrome checking""" + self.assertTrue(is_palindrome("Racecar")) + self.assertTrue(is_palindrome("MadAm")) + self.assertFalse(is_palindrome("NoLemon")) + + def test_alphanumeric_only(self): + """Test palindrome checking with spaces and punctuation""" + self.assertTrue(is_palindrome("A man a plan a canal Panama")) + self.assertFalse(is_palindrome("race a car")) + self.assertTrue(is_palindrome("Was it a car or a cat I saw?")) + + def test_special_cases(self): + """Test numeric and mixed palindromes""" + self.assertTrue(is_palindrome("12321")) + self.assertFalse(is_palindrome("12345")) + self.assertTrue(is_palindrome("Madam123321madaM")) + self.assertTrue(is_palindrome("!@#$%^&*()")) # No alphanumeric characters + + def test_type_validation(self): + """Test type validation""" + with self.assertRaises(TypeError): + is_palindrome(123) + with self.assertRaises(TypeError): + is_palindrome(None) + with self.assertRaises(TypeError): + is_palindrome(['a', 'b', 'c']) + + def test_edge_cases(self): + """Test additional edge cases""" + self.assertTrue(is_palindrome("aa")) + self.assertTrue(is_palindrome("aba")) + self.assertFalse(is_palindrome("ab")) + self.assertTrue(is_palindrome("A man, a plan, a canal: Panama!")) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/simple_bank_account/README.md b/python/simple_bank_account/README.md new file mode 100644 index 0000000..3bed25d --- /dev/null +++ b/python/simple_bank_account/README.md @@ -0,0 +1,84 @@ +# Simple Bank Account + +## ๐ŸŽฏ Description +This challenge introduces object-oriented programming concepts by implementing a simple bank account system. You'll learn about encapsulation, instance methods, and proper validation in class design. + +## ๐Ÿ” Task +Implement a `BankAccount` class with the following specifications: + +### Constructor +- `__init__(self, initial_balance: float = 0.0)` +- Initialize account with given balance (defaults to 0.0) +- Should raise `ValueError` if initial_balance is negative + +### Methods +1. `deposit(self, amount: float) -> None` + - Add money to the account + - Should raise `ValueError` if amount is negative or zero + +2. `withdraw(self, amount: float) -> bool` + - Remove money from account if sufficient funds available + - Return `True` if withdrawal successful, `False` if insufficient funds + - Should raise `ValueError` if amount is negative or zero + +3. `get_balance(self) -> float` + - Return current account balance + +4. `__str__(self) -> str` + - Return string representation: "Account balance: $X.XX" + +## ๐Ÿ“‹ Requirements +- Class must be implemented in Python 3.8+ +- Include proper type hints +- Follow PEP 8 style guidelines +- Handle negative amounts appropriately +- Prevent overdrafts (balance cannot go below zero) +- Use appropriate data types for monetary values + +## ๐Ÿงช Test Cases +Your solution must pass the following test cases: + +### 1. Constructor Tests +- `BankAccount()` should create account with balance 0.0 +- `BankAccount(100.0)` should create account with balance 100.0 +- `BankAccount(-50.0)` should raise `ValueError` + +### 2. Deposit Tests +- `deposit(50.0)` on empty account should result in balance 50.0 +- `deposit(25.50)` should handle decimal amounts +- `deposit(-10.0)` should raise `ValueError` +- `deposit(0)` should raise `ValueError` + +### 3. Withdrawal Tests +- `withdraw(30.0)` from account with 100.0 should return `True` and balance 70.0 +- `withdraw(150.0)` from account with 100.0 should return `False` and balance unchanged +- `withdraw(-20.0)` should raise `ValueError` +- `withdraw(0)` should raise `ValueError` + +### 4. Balance Tests +- `get_balance()` should return current balance as float +- Balance should update correctly after deposits and withdrawals + +### 5. String Representation Tests +- `str(account)` should return formatted string with balance + +### 6. Edge Cases +- Multiple deposits and withdrawals should work correctly +- Attempting to withdraw exact balance should succeed +- Floating point precision should be handled appropriately + +## ๐Ÿ“ Files +- `solution.py` - Implement your solution here +- `test_main.py` - Contains the test cases **DO NOT EDIT** + +## โœ… Submission +1. Implement your solution in `solution.py` +2. Push your changes to your repository +3. The automated tests will run and verify your solution + +## ๐Ÿ’ก Tips +- Use instance variables to store account state +- Consider using `round()` for floating point precision issues +- Remember that `withdraw()` should return a boolean indicating success +- Validate inputs before processing transactions +- Consider what should happen when trying to withdraw more than the balance diff --git a/python/simple_bank_account/solution.py b/python/simple_bank_account/solution.py new file mode 100644 index 0000000..8860326 --- /dev/null +++ b/python/simple_bank_account/solution.py @@ -0,0 +1,76 @@ +class BankAccount: + """ + A simple bank account class that supports deposits, withdrawals, and balance inquiries. + """ + + def __init__(self, initial_balance: float = 0.0) -> None: + """ + Initialize a new bank account. + + Args: + initial_balance (float): Starting balance for the account (default: 0.0) + + Raises: + ValueError: If initial_balance is negative + """ + if initial_balance < 0: + raise ValueError("Initial balance cannot be negative") + + # TODO: Initialize the account balance + pass + + def deposit(self, amount: float) -> None: + """ + Deposit money into the account. + + Args: + amount (float): Amount to deposit + + Raises: + ValueError: If amount is negative or zero + """ + if amount <= 0: + raise ValueError("Deposit amount must be positive") + + # TODO: Add amount to account balance + pass + + def withdraw(self, amount: float) -> bool: + """ + Withdraw money from the account if sufficient funds are available. + + Args: + amount (float): Amount to withdraw + + Returns: + bool: True if withdrawal successful, False if insufficient funds + + Raises: + ValueError: If amount is negative or zero + """ + if amount <= 0: + raise ValueError("Withdrawal amount must be positive") + + # TODO: Check if sufficient funds and withdraw if possible + # Return True if successful, False if insufficient funds + pass + + def get_balance(self) -> float: + """ + Get the current account balance. + + Returns: + float: Current account balance + """ + # TODO: Return current balance + pass + + def __str__(self) -> str: + """ + Return string representation of the account. + + Returns: + str: Formatted account balance + """ + # TODO: Return formatted balance string: "Account balance: $X.XX" + pass diff --git a/python/simple_bank_account/test_main.py b/python/simple_bank_account/test_main.py new file mode 100644 index 0000000..0132f96 --- /dev/null +++ b/python/simple_bank_account/test_main.py @@ -0,0 +1,121 @@ +import unittest +from solution import BankAccount + + +class TestSimpleBankAccount(unittest.TestCase): + + def test_constructor_default(self): + """Test default constructor""" + account = BankAccount() + self.assertEqual(account.get_balance(), 0.0) + + def test_constructor_with_balance(self): + """Test constructor with initial balance""" + account = BankAccount(100.0) + self.assertEqual(account.get_balance(), 100.0) + + account = BankAccount(50.75) + self.assertEqual(account.get_balance(), 50.75) + + def test_constructor_negative_balance(self): + """Test constructor with negative balance""" + with self.assertRaises(ValueError): + BankAccount(-50.0) + with self.assertRaises(ValueError): + BankAccount(-0.01) + + def test_deposit_basic(self): + """Test basic deposit functionality""" + account = BankAccount() + account.deposit(50.0) + self.assertEqual(account.get_balance(), 50.0) + + account.deposit(25.50) + self.assertEqual(account.get_balance(), 75.50) + + def test_deposit_invalid_amounts(self): + """Test deposit with invalid amounts""" + account = BankAccount() + + with self.assertRaises(ValueError): + account.deposit(-10.0) + with self.assertRaises(ValueError): + account.deposit(0) + with self.assertRaises(ValueError): + account.deposit(-0.01) + + def test_withdraw_sufficient_funds(self): + """Test withdrawal with sufficient funds""" + account = BankAccount(100.0) + + result = account.withdraw(30.0) + self.assertTrue(result) + self.assertEqual(account.get_balance(), 70.0) + + result = account.withdraw(70.0) # Withdraw exact balance + self.assertTrue(result) + self.assertEqual(account.get_balance(), 0.0) + + def test_withdraw_insufficient_funds(self): + """Test withdrawal with insufficient funds""" + account = BankAccount(100.0) + + result = account.withdraw(150.0) + self.assertFalse(result) + self.assertEqual(account.get_balance(), 100.0) # Balance unchanged + + def test_withdraw_invalid_amounts(self): + """Test withdrawal with invalid amounts""" + account = BankAccount(100.0) + + with self.assertRaises(ValueError): + account.withdraw(-20.0) + with self.assertRaises(ValueError): + account.withdraw(0) + with self.assertRaises(ValueError): + account.withdraw(-0.01) + + def test_get_balance(self): + """Test get_balance method""" + account = BankAccount(42.50) + self.assertIsInstance(account.get_balance(), float) + self.assertEqual(account.get_balance(), 42.50) + + def test_string_representation(self): + """Test string representation""" + account = BankAccount(123.45) + expected = "Account balance: $123.45" + self.assertEqual(str(account), expected) + + account = BankAccount(0.0) + expected = "Account balance: $0.00" + self.assertEqual(str(account), expected) + + def test_multiple_transactions(self): + """Test multiple deposits and withdrawals""" + account = BankAccount(50.0) + + account.deposit(25.0) + self.assertEqual(account.get_balance(), 75.0) + + account.withdraw(20.0) + self.assertEqual(account.get_balance(), 55.0) + + account.deposit(10.50) + self.assertEqual(account.get_balance(), 65.50) + + result = account.withdraw(100.0) # Insufficient funds + self.assertFalse(result) + self.assertEqual(account.get_balance(), 65.50) + + def test_floating_point_precision(self): + """Test floating point operations""" + account = BankAccount(10.10) + account.deposit(20.20) + account.withdraw(5.05) + # Due to floating point precision, we use assertAlmostEqual + self.assertAlmostEqual(account.get_balance(), 25.25, places=2) + + +if __name__ == "__main__": + unittest.main()