diff --git a/.github/ISSUE_TEMPLATE/help_wanted.md b/.github/ISSUE_TEMPLATE/help_wanted.md index 939dbba0a..75dbb65f7 100644 --- a/.github/ISSUE_TEMPLATE/help_wanted.md +++ b/.github/ISSUE_TEMPLATE/help_wanted.md @@ -14,7 +14,7 @@ labels: "help wanted" - assign: anyone you would like help from --> -## The Code +# The Code diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index eba8a7be2..db6a23664 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,9 +12,9 @@ about: A template PR for code review with a checklist -## Behavior +# Behavior -### Files +## Files - [ ] The file name describes the function's behavior - [ ] There is a module docstring in the function file diff --git a/LICENSE b/LICENSE index 6e628e40e..d04b03511 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -MIT License +# MIT License Copyright (c) 2024 MIT Emerging Talent diff --git a/README.md b/README.md index e69de29bb..7be079914 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,19 @@ +# Git it Done + +## Overview + +Welcome to the **Git it Done** repository! + +This is a collaborative learning project where our team works together to solve +problems and learn new skills. Each teammate will: + +- Solve 2 problems. +- Help resolve 2 problems contributed by others. + +Our team is made up of people with different levels of experience—some of us are +complete beginners, while others have a little experience. But one thing we all +share is a strong eagerness to learn. By the end of this project, we will no longer + be complete beginners. + +We are learning collaboratively, growing through our mistakes, and working as a +team to improve. . diff --git a/collaboration/README.md b/collaboration/README.md index 20889b951..09a13cce6 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -2,4 +2,69 @@ +## Summary of Norms + +Our goal is to create a collaborative environment where everyone feels + comfortable asking questions and working together. We will start with the basics, + progress toward project goals, and remain respectful, helpful, and connected + throughout the journey. + +### 1. Encourage Open and Respectful Communication + +- Ensure all members feel heard and valued in discussions. +- Prioritize clear and constructive communication, encouraging everyone to share + their ideas and perspectives without fear of judgment or interruption. + +### 2. Balance Discussion and Debate + +- Promote productive collaboration through thoughtful discourse. +- Use discussions to generate ideas and debates to critically evaluate them while + maintaining respect and focusing on project goals. + +### 3. Technical Meetings + +- Use **Slack** for technical discussions and project updates. +- Use **WhatsApp** for urgent matters and quick clarifications. + +### 4. Reminders for Code Review + +- To ensure timely code reviews, feel free to send a friendly reminder on Slack + or WhatsApp when necessary. +- Encourage team members to notify the requester once the code review is completed. + +### 5. Handling Delays and Meeting Attendance + +- Meetings will start as scheduled to respect everyone's time. +- If unable to attend, members should share their situation in advance and catch + up via recordings. + +### 6. Maintain a Positive Team Environment + +- Avoid rude or mean behavior. +- If any interaction causes discomfort, address it respectfully to resolve the + issue and foster understanding. + +### 7. Create a Safe Feedback Culture + +- Encourage constructive feedback to improve team performance. +- Focus on solutions rather than blame when giving or receiving feedback. + +### 8. Clear Handoffs When Required + +- If there are delays in completing tasks, inform the team promptly to prevent +further delays. +- Minimize dependency issues by clearly communicating expectations and timelines + when tasks require input or dependencies from another member. + +### 9. Respect for Different Work Styles + +- Foster individuality while ensuring progress. +- Respect that everyone has a unique working style, provided deliverables are + submitted on time and meet agreed standards. + +### 10. Responsiveness and Accountability + +- While everyone has different schedules, acknowledging messages on Slack or +WhatsApp—even with a brief reply—helps keep the team informed and connected. +- This ensures smooth collaboration and demonstrates respect for everyone’s efforts. diff --git a/collaboration/communication.md b/collaboration/communication.md index 484652e0f..3de37dc91 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -1,54 +1,293 @@ -# Communication - -______________________________________________________________________ +# Communication Plan ## Communication Schedule -| Day | How | The topic of discussion | | --- | :-: | ----------------------- | -| | | | +This is the schedule for each week, and it will be updated weekly. + +| Day | How | The topic of discussion | +|-----------|:----:|-------------------------------------------| +| Monday | Zoom | N/A | +| Tuesday | Zoom | N/A | +| Wednesday | Zoom | GitHub Issues and Pull Request Discussion | +| Thursday | Zoom | Discuss documentation and testing | +| Friday | Zoom | Continuous Integration (CI) Checks | +| Saturday | Zoom | No Meeting | +| Sunday | Zoom | Conflicts and CI checks discussion. | + +--- ## Communication Channels -how often will we get in touch on each channel, and what we will discuss there: +All technical discussions will be conducted on Slack. General discussions will +use WhatsApp. -- **Issues**: -- **Pull Requests**: -- **Slack/Discord**: -- **Video Calls**: +- **Issues**: Adding availability to the schedule +- **Pull Requests**: Reviewing each other's code and giving feedback +- **Slack/Discord**: Email, Slack, and WhatsApp +- **Video Calls**: Zoom and Google Meet -______________________________________________________________________ +--- ## Availability -### Availability for calling/messaging +### Availability for Calling/Messaging + +| Day | Mon | Tue | Wed | Thu | Fri | Sat | Sun | +|------------|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| **Madiha** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| **Fatima** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| **Shahroz**| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| **Mojtaba**| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +|**Mudassra**| ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | + +### Hours Available Per Day + +- **Fatima**: _5h_ +- **Madiha**: _5h_ +- **M Shahroz**: _4h_ +- **Mojtaba**: _5h_ +- **Mudassra**: _5h_ + +--- + +## Meeting Agendas and Records + +### Previous Meetings + +#### Meeting 1 + +- **Date:** 20-12-2024 +- **Discussion Topics:** + - Introduction + - Team Norms + - Strategies for tackling the project +- **Decisions Made:** + - Decided on 7 team norms. + - Study Google Documents. + +#### Meeting 2 + +- **Date:** 21-12-2024 +- **Discussion Topics:** + - Finalizing group norms + - Team name +- **Decisions Made:** + - Finalized 10 norms. + - Decided on the team name: "Git it Done." + - First commit made on GitHub. + +#### Meeting 3 + +- **Date:** 22-12-2024 +- **Discussion Topics:** + - Discussion on assigned Google Docs + - Guiding new team members +- **Decisions Made:** + - Official work to start tomorrow. + +#### Meeting 4 + +- **Date:** 23-12-2024 +- **Discussion Topics:** + - Pull requests + - Branches +- **Decisions Made:** + - Tasks assigned to each team member. + - Explore more about pull requests and branches. + - Explore issues on GitHub. + +#### Meeting 5 + + +- **Date:** 25-12-2024 +- **Time:** 10:00 PM PKT +- **Location:** Zoom + + +1. **Discuss GitHub Issues and Pull Requests** + - Topic: How and when to use GitHub Issues and Pull Requests (Important). + - Resource: Watch this [video](https://www.youtube.com/watch?v=TKJ4RdhyB5Y) + to learn about GitHub issues. + - Reference: [communication.md](https://github.com/MIT-Emerging-Talent/ET6-foundations-group-30/blob/main/collaboration/communication.md) + +2. **Discuss and Assign the Chosen Problems** + - Discuss and choose problems to work on. + - Assign specific problems to each team member. + +#### Meeting 6 + + +- **Date:** 25-12-2024 +- **Time:** 10:00 PM PKT +- **Location:** Zoom + + +1. **Discuss documentation and testing** + - Topic: Documentation and testing. + - Resource: Watch this [Repository to study](https://github.com/MIT-Emerging-Talent/ET6-Programming-With-Python/tree/main/3_documenting_and_testing) + +2. **Solve a Simple Problem** + - Collaboratively solve a simple problem. + - Discuss different approaches and finalize the solution. + +3. **Write Unit Tests** + - Write unit tests for the solved problem. + - Discuss the importance of unit testing and share best practices. + +4. **Write Documentation** + - Create documentation for the solved problem and its tests. + - Discuss the format and tools for writing clear and concise documentation. + +#### Meeting 7 + +- **Date:** 27-12-2024 +- **Time:** 10:00 PM PKT +- **Location:** Zoom + + +### 1. Continuous Integration (CI) Checks + +- **Discussion Points:** + - What is Continuous Integration (CI), and why is it important for the project? + - Benefits of CI: + - Early detection of issues. + - Maintaining code quality through automated checks. +- **Resource:** + [Watch this CI Overview Video](https://www.youtube.com/watch?v=mFFXuXjVgkU) + +#### Meeting 8 + +- **Date:** 29-12-2024 +- **Time:** 08:00 PM PKT +- **Location:** Zoom + + + +## Agenda + +In this meeting, we will address the conflicts and bugs occurring in our +codebase. The goal is to identify the root causes, discuss solutions, +and collaboratively resolve the issues during the session. + +### Key Topics + +1. **Identify current conflicts and bugs** + - Discuss the critical issues causing blockers. + - Understand the context behind each issue. +2. **Collaborative code review** + - Share screens to analyze problematic sections of the code. + - Discuss alternative approaches and fixes. +3. **Resolve issues in real-time** + - Apply potential fixes to address conflicts and bugs. + - Validate changes and test functionality. + +--- + +#### Meeting 9 + +- **Date:** 30-12-2024 +- **Time:** 2:00 PM PKT +- **Location:** Zoom + + +### 1. Introduction of New Members + +- **Discussion Points:** + - Brief introductions of new team members. + - Overview of each member's background and expertise. + - Encourage the new members to share their expectations from the team. + +### 2. Progress Update + +- **Discussion Points:** + - Overview of the work completed so far. + - Key milestones and achievements reached. + - Challenges faced and how they were overcome. + +### 3. Current Tasks and Next Steps + +- **Discussion Points:** + - Overview of ongoing tasks and deadlines. + - Team's immediate goals and how to move forward. + - Prioritization of tasks and assignment of responsibilities. + +### 4. Encouraging Inactive Members to Participate + +- **Discussion Points:** + - Address the current inactivity within the team. + - Encourage inactive members to participate and contribute. + - Offer support and guidance to help them integrate into the + team's activities. + +### 5. Offering Support and Assistance + +- **Discussion Points:** + - Identify challenges faced by inactive members. + - Offer help and support to ease their participation. + - Discuss any additional resources or training required to get them up to speed. + +### Upcoming Meetings + +# Discussion: How to Properly Review Code for Pull Requests + +- **Date: 01-01-2025** +- **Time: 10 p.m. PKT** +- **Location: Zoom** + +## Meeting Agenda + +In this meeting, we will discuss best practices and guidelines for reviewing + code submitted through pull requests. This will help us ensure high-quality + contributions, improve collaboration, and maintain a clean codebase. + +### Topics to discuss + +1. **Best practices for reviewing code** + - How to add meaningful comments. + - Consistency, readability, and maintainability. + - Identifying potential bugs and optimization opportunities. +2. **Using GitHub tools effectively** + - Commenting on PRs. + - Requesting changes and approving PRs. +3. **Code review checklist by Evan** + - Discuss and apply the checklist during a live review. + - Replace and refine points in the checklist as necessary. + +### Resources + +- **YouTube Videos**: + - [Link to Video 1](https://www.youtube.com/watch?v=lSnbOtw4izI) + - [Link to Video 2](https://www.youtube.com/watch?v=GCSQZqzm18U) + +- **GitHub Repository**: + - [Repository Link](https://github.com/MIT-Emerging-Talent/ET6-Programming-With-Python/blob/main/3_documenting_and_testing/code_review_exercises.mp4) + +### Deliverables -| Day | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday | | ------- | :----: | :-----: | :-------: | :------: | :----: | :------: | :----: | -| _name_ | | | | | | | | +By the end of the meeting, we aim to: -### How many hours everyone has per day +- Add reviews to a pull request and practice using the code review +checklist by Evan. +- Assign review responsibilities to team members. +- Align on code quality standards. -- name: _5h_; -- name: _6h_; -- name: _5h_; -- name: _4h_; -- name: _3h_; +--- ## Asking for Help -There's a fine line between confidently learning from your mistakes, and -stubbornly getting no where. Here is a general guide for when to ask for help +There's a fine line between confidently learning from your mistakes and +stubbornly getting nowhere. Here is a general guide for when to ask for help based on how long you've been stuck on the same problem: -1. _0 -> 30 min_: Try on your own -1. _30 -> 60 min_: Ask your group for help -1. _60+ min_: Tag your coaches in Slack or GitHub +1. _0 -> 30 min_: Try on your own +2. _30 -> 60 min_: Ask your group for help +3. _60+ min_: Tag your coaches in Slack or GitHub. diff --git a/collaboration/constraints.md b/collaboration/constraints.md index 24079505c..67c413633 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -1,34 +1,75 @@ - - # Constraints -Some boundaries around our project. +## External Constraints + +**These factors outside our control influence how we work on this project.** + +### Platform Requirements + +- Problems must be selected from the workshop's exercises, CodeWars, LeetCode, or +similar coding platforms. +- Solutions must comply with the workshop's requirements (e.g., unit tests, documentation, + and clear variable names). + +### Deadlines + +- Projects must be completed according to the timeline set by the +MIT Emerging Talent program. +- Each member must solve and review problems within the agreed-upon timeframes. + +### Tools and Resources + +- GitHub must be used for version control, communication, and documentation. +- The team must rely on publicly available resources (e.g., online tutorials +and documentation) to enhance learning. + +## Internal Constraints: Voluntary + +These are self-imposed rules or boundaries designed to improve collaboration and +learning outcomes. + +### Documentation Standards + +- All code must include meaningful comments explaining the logic. +- Problem solutions must include documentation with: + - Problem description. + - Approach and thought process. + - Test cases used. + +### Communication Protocols + +- Use GitHub Issues to discuss problems and tasks. +- At least one other member must review pull requests before merging. +- Constructive feedback is required for all reviews. -## External +### Collaborative Learning - +- Each member must share insights or challenges they faced during problem-solving + to help others learn. +- Regular check-ins (via GitHub comments or virtual meetings) to ensure alignment. -## Internal: Involuntary +## New Internal Constraints: Voluntary - +These are new rules designed to improve the workflow and mitigate issues we've +encountered recently. -## Internal: Voluntary +### Pull Request and Branching Practices - +- **Pull Requests**: Each template file must be worked on in a separate branch and +added to the TODO list of the project board. +- **Branch Management**: When creating a new branch, ensure it is synchronized and +updated with the main branch using the following commands: + - `git fetch` to fetch updates. + - `git merge main` to merge the latest main branch changes into the current branch. +- **Selective `git add`**: Only add files on which changes have been made. +**Do not use `git add .`**. This ensures only relevant changes are committed. +- **Template File Changes**: For all template file modifications, work exclusively +in the `update_template_files` branch. +Create an issue for any changes you wish to make, and only submit a pull request +if you plan to merge the changes into the main file. +- **Branch Closure**: Once a new branch is created and used, the previous branches +will not be used further. Always start from the latest main branch or an appropriate +updated branch. +- **Pull Request Review**: Pull requests must be reviewed promptly upon submission +to avoid delays and conflicts. Reviews should be completed as soon as possible to +ensure efficient merging. diff --git a/collaboration/guide/0_repository_setup.md b/collaboration/guide/0_repository_setup.md index aae9cb76e..a8548b977 100644 --- a/collaboration/guide/0_repository_setup.md +++ b/collaboration/guide/0_repository_setup.md @@ -16,6 +16,7 @@ it for collaboration: - Add or remove topics from your repository - Update your main README with your group name and an initial overview of your project. (You can change this as much as you want.) + - Under settings in your repository select: - _Issues_ - _Projects_ diff --git a/collaboration/learning_goals.md b/collaboration/learning_goals.md index 11c583d2b..b86d3017a 100644 --- a/collaboration/learning_goals.md +++ b/collaboration/learning_goals.md @@ -2,4 +2,39 @@ ## Collective +1. **Cultivate Collaboration & Communication** + - Use GitHub tools (issues, pull requests, project boards) for structured teamwork. + - Write actionable, clear issues with helpful resources. + - Continuously update project board to reflect progress and support problem-solving. + - Develop and maintain a clear communication plan for effective collaboration. + - Use branches strategically to organize tasks and streamline development, + for smooth collaboration and progress. + +2. **Manage GitHub Workflows** + - Configure repositories, collaboration features, and best practices for team + workflows. + +3. **Nurture Team Synergy** + - Build cognitive and emotional trust, and repair trust when needed. + - Balance discussion and debate effectively. + - Prioritize idea generation and critical evaluation. + +4. **Enhance Tool Proficiency** + - Improve understanding of VSCode, CLI, Git, and GitHub for development, + debugging, and collaboration. + - Practice making atomic, focused commits with clear commit messages + for better code history and easier code review. + +5. **Improve Python Skills** + - Write clean, modular, and well-documented Python code, adhering to best practices. + - Debug and optimize code for efficiency. + - Write and implement relevant unit test cases using Python’s unittest for + code reliability and maintenance + - Practice Test-Driven Development (TDD). + +6. **Improve Collaboration Through Feedback and Reflection** + - Focus on actionable and constructive feedback for team behaviour. + - Reflect on team experiences and iterate collaboration processes for + continuous improvement. + ## Individual diff --git a/solutions/calculate_max_profit.py b/solutions/calculate_max_profit.py new file mode 100644 index 000000000..783a6a412 --- /dev/null +++ b/solutions/calculate_max_profit.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +A module to calculate the maximum profit from stock trading. + +Module Contents: + + - calculate_max_profit(prices: list) -> int: + Computes the maximum profit that can be made by completing as many transactions as possible + (buy one and sell one share of stock multiple times). + +Created on: 2025-01-05 +Author: Jola-Moses +""" + + +def calculate_max_profit(prices: list) -> int: + """ + Returns the maximum profit from as many stock transactions as possible. + + Parameters: + prices (list): A list of non-negative integers or floats representing the stock prices + on different days. Each price is greater than or equal to zero." + + Returns: + int, float: The maximum profit that can be made from as many transactions as possible. + + Raises: + AssertionError: If `prices` is not a list. + AssertionError: If any element in `prices` is not a numeric type (either int or float). + AssertionError: If any price in `prices` is less than zero. + + Examples: + >>> calculate_max_profit([7, 1, 5, 3, 6, 4]) + 7 + >>> calculate_max_profit([1, 2, 3, 4, 5]) + 4 + >>> calculate_max_profit([7, 6, 4, 3, 1]) + 0 + """ + + assert isinstance(prices, list), "Input should be a list." + assert all(isinstance(price, (int, float)) for price in prices), ( + "Prices should be integers or floats." + ) + assert all(price >= 0 for price in prices), ( + "Prices should be non-negative integers or floats." + ) + + maximum_profit = 0 + + # Compare each day's price with the previous day's, starting from the second day + for i in range(1, len(prices)): + # Add profit if today's price is higher than yesterday's. + if prices[i] > prices[i - 1]: + maximum_profit += prices[i] - prices[i - 1] + + return maximum_profit diff --git a/solutions/count_vowels.py b/solutions/count_vowels.py new file mode 100644 index 000000000..510743f5d --- /dev/null +++ b/solutions/count_vowels.py @@ -0,0 +1,42 @@ +""" +A module for counting vowels in a string. + +Module contents: + - count_vowels: Counts the number of vowels in a given string. + It supports both uppercase and lowercase vowels. + +Created on: 2025-07-06 +@author: Fatima +""" + + +def count_vowels(text: str) -> int: + """Returns the number of vowels (both uppercase and lowercase) in a string. + + Parameters: + text: str, the input string to check + + Returns: + int: Number of vowels in the text. + + Raises: + AssertionError: if the argument is not a string + + >>> count_vowels("hello") + 2 + >>> count_vowels("APPLE") + 2 + >>> count_vowels("why") + 0 + """ + + assert isinstance(text, str), "input must be a string" + + vowels = "aeiouAEIOU" + count = 0 + + for letter in text: + if letter in vowels: + count += 1 + + return count diff --git a/solutions/filter_odd_numbers.py b/solutions/filter_odd_numbers.py new file mode 100644 index 000000000..a00fd6196 --- /dev/null +++ b/solutions/filter_odd_numbers.py @@ -0,0 +1,53 @@ +""" +Module: print_odd_numbers_in_list + +Description: This module provides a function to filter odd numbers from a given list. +The function handles various types of inputs, adheres to constraints, and returns a list +containing only odd integers. + +Created on: 01 01 25 +@author: Muhammad Shahroz +""" + +from typing import List, Union + + +def filter_odd_numbers(input_list: List[Union[int, float, str, None]]) -> List[int]: + """ + Filters and returns a list of odd integers from the given input list. + + Parameters: + input_list (List[Union[int, float, str, None]]): A list containing elements of various types. + + Returns: + List[int]: A list containing only the odd integers from the input. + + Raises: + AssertionError: If the input is not a list. + + Examples: + >>> filter_odd_numbers([1, 2, -3, 4.5, 5]) + [1, -3, 5] + + >>> filter_odd_numbers([1, 2, "three", 4.5, None]) + [1] + + >>> filter_odd_numbers([]) + [] + """ + # Ensure the input is a list + assert isinstance(input_list, list), "Input must be a list." + + # Step 1: Filter only integers + integer_list = [] + for element in input_list: + if isinstance(element, int): + integer_list.append(element) + + # Step 2: Filter only odd numbers from the integers + odd_number_list = [] + for number in integer_list: + if number % 2 != 0: + odd_number_list.append(number) + + return odd_number_list diff --git a/solutions/longest_substring_length.py b/solutions/longest_substring_length.py new file mode 100644 index 000000000..3dca25163 --- /dev/null +++ b/solutions/longest_substring_length.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +A module to find the length of the longest substring in a given string without repeating characters. + +Module Contents: + + - longest_substring_length(s: str) -> int: + Finds the length of the longest substring without repeating characters. + +Created on: 2025-01-03 +Author: Jola-Moses +""" + + +def longest_substring_length(s: str) -> int: + """ + Returns the length of the longest substring without repeating characters. + + Parameters: + s (str): The input string for which to find the longest substring. + An empty string is valid input. + + Returns: + int: A non-negative integer representing the length of the longest substring + without repeating characters. + + Raises: + AssertionError: If `s` is not a string. + + Examples: + >>> longest_substring_length("abcabcbb") + 3 + >>> longest_substring_length("abcdef") + 6 + >>> longest_substring_length("aaaaaa") + 1 + >>> longest_substring_length("") + 0 + """ + + assert isinstance(s, str), "Input should be a string" + + max_length = 0 + current_substring = "" + + for char in s: + if char in current_substring: + # Remove repeating characters + while char in current_substring: + current_substring = current_substring[1:] + + # Add the current character to the substring + current_substring += char + + # Update max_length + max_length = max(max_length, len(current_substring)) + + return max_length diff --git a/solutions/remove_duplicates_from_sorted_list.py b/solutions/remove_duplicates_from_sorted_list.py new file mode 100644 index 000000000..e0a165983 --- /dev/null +++ b/solutions/remove_duplicates_from_sorted_list.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Module: remove_duplicates_from_sorted_list +Description: This module provides a function to remove duplicates from a sorted linked list. +The function ensures each element appears only once while maintaining the sorted order. + +Created on: 01 01 25 +@author: Muhammad Shahroz +""" + +from typing import Optional + + +class ListNode: + """A node in a singly linked list.""" + + def __init__(self, value=0, next_node=None): + self.value = value + self.next = next_node + + +def remove_duplicates_from_sorted_list(head: Optional[ListNode]) -> Optional[ListNode]: + """ + Removes duplicates from a sorted linked list. + + Parameters: + head (Optional[ListNode]): The head node of the sorted linked list. + - The input is expected to be a valid ListNode or None. + - The list is assumed to be sorted in non-decreasing order. + + Returns: + Optional[ListNode]: The head node of the modified linked list with duplicates removed. + + Raises: + AssertionError: If the argument is not of type ListNode or None. + + Examples: + >>> head = ListNode(1, ListNode(1, ListNode(2))) + >>> result = remove_duplicates_from_sorted_list(head) + >>> [result.value, result.next.value, result.next.next] + [1, 2, None] + + >>> head = ListNode(1, ListNode(1, ListNode(2, ListNode(3, ListNode(3))))) + >>> result = remove_duplicates_from_sorted_list(head) + >>> [result.value, result.next.value, result.next.next.value] + [1, 2, 3] + + >>> result = remove_duplicates_from_sorted_list(None) + >>> result is None + True + + Example: + head = ListNode(1, ListNode(1, ListNode(2))) + result = remove_duplicates_from_sorted_list(head) + # result is now a list with values: [1, 2] + """ + # Defensive Assertion + assert head is None or isinstance(head, ListNode), ( + "Input must be a ListNode or None." + ) + + current_node = head + while current_node and current_node.next: + # Traverse the list and remove consecutive duplicates by adjusting pointers. + if current_node.value == current_node.next.value: + current_node.next = current_node.next.next # Skip the duplicate node + else: + current_node = current_node.next # Move to the next unique node + + return head diff --git a/solutions/remove_every_other.py b/solutions/remove_every_other.py new file mode 100644 index 000000000..a953fdb82 --- /dev/null +++ b/solutions/remove_every_other.py @@ -0,0 +1,42 @@ +""" +Module: remove_every_other +Author: Mojtaba Fayyaz +Date: 2024-12-29 + +This module provides a utility function to remove every second element from a list. +""" + + +def remove_every_other(arr: list) -> list: + """ + Removes every second element from the input list. + + Parameters: + arr (list): A list of elements from which every second element will be removed. + + Returns: + list: A new list containing only every other element starting with the first. + + Assumptions: + - The input is always a non-empty list. + + Doctests: + >>> remove_every_other(["Keep", "Remove", "Keep", "Remove", "Keep"]) + ['Keep', 'Keep', 'Keep'] + + >>> remove_every_other([1, 2, 3, 4, 5]) + [1, 3, 5] + + >>> remove_every_other(["A"]) + ['A'] + + Example: + arr = ["Apple", "Banana", "Cherry"] + print(remove_every_other(arr)) + # Output: ['Apple', 'Cherry'] + + Defensive Assertions: + - Ensures the input is a list. + """ + assert isinstance(arr, list), "Input must be a list" + return arr[::2] diff --git a/solutions/remove_triplets.py b/solutions/remove_triplets.py new file mode 100644 index 000000000..3add32047 --- /dev/null +++ b/solutions/remove_triplets.py @@ -0,0 +1,55 @@ +def remove_triplets(text: str) -> str: + """ + Removes triplets from string, a triplet is a sequence of three consecutive identical characters. + + This function processes the input string and ensures no three consecutive characters are the + same by removing the minimum number of characters required. + + Args: + text (str): The input string to process. It can be empty or contain any characters. + + Returns: + str: The processed string where no three consecutive characters are the same. + + Raises: + AssertionError: If the input is not of type string. + + Examples: + >>> remove_triplets("aaabaaaa") + 'aabaa' + + >>> remove_triplets("helllo") + 'hello' + + >>> remove_triplets("leeetcode") + 'leetcode' + + Edge Cases: + - If the input is an empty string, return an empty string. + - If the input has no three consecutive characters, return the input string unchanged. + - If the input contains more than three consecutive identical + characters, the function limits the repetitions to two. + Created on: 02/01/25 + @author: Madiha Malikzada + """ + # Validate input type + assert isinstance(text, str), "Input must be a string type" + + # Handle empty input + if text == "": + return "" + + prev = text[0] + frequency = 1 + ans = text[0] + + for i in range(1, len(text)): + if text[i] == prev: + frequency += 1 + else: + prev = text[i] + frequency = 1 + if frequency < 3: + ans += text[i] + + return ans diff --git a/solutions/reverse_string.py b/solutions/reverse_string.py new file mode 100644 index 000000000..4341ef240 --- /dev/null +++ b/solutions/reverse_string.py @@ -0,0 +1,55 @@ +""" +Module: reverse_string +Author: Mojtaba Fayyaz +Date: 2024-01-02 + +This module provides a utility function to reverse an array of characters in-place. +""" + + +def reverse_string(s: list) -> None: + """ + Reverses the input array of characters in-place. + + Parameters: + s (list): A list of characters to be reversed. + + Returns: + None: The input list is modified in-place. + + Assumptions: + - The input is always a list of characters. + + Doctests: + >>> s = ['h', 'e', 'l', 'l', 'o'] + >>> reverse_string(s) + >>> s + ['o', 'l', 'l', 'e', 'h'] + + >>> s = ['H', 'a', 'n', 'n', 'a', 'h'] + >>> reverse_string(s) + >>> s + ['h', 'a', 'n', 'n', 'a', 'H'] + + >>> s = ['A'] + >>> reverse_string(s) + >>> s + ['A'] + + Example: + s = ['c', 'a', 't'] + reverse_string(s) + print(s) + # Output: ['t', 'a', 'c'] + + Defensive Assertions: + - Ensures the input is a list. + """ + assert isinstance(s, list), "Input must be a list" + left, right = 0, len(s) - 1 + + while left < right: + # Swap the characters at the left and right indices + s[left], s[right] = s[right], s[left] + left += 1 + right -= 1 diff --git a/solutions/tests/test_calculate_max_profit.py b/solutions/tests/test_calculate_max_profit.py new file mode 100644 index 000000000..c5c7e2dcf --- /dev/null +++ b/solutions/tests/test_calculate_max_profit.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +This module contains unit tests to validate the functionality of the +`calculate_max_profit` function. +It calculates the maximum profit from stock prices over several days, +allowing for multiple transactions. + +Test categories: + - Standard cases: Typical lists of stock prices with varying price patterns. + - Edge cases: Empty lists, single price day, and large price differences. + - Defensive tests: Non-list inputs, non-numeric prices, and negative prices + to ensure proper assertion handling. + +Created on: 2025-01-05 +Author: Jola-Moses +""" + +import unittest + +from solutions.calculate_max_profit import calculate_max_profit + + +class CalculateMaxProfit(unittest.TestCase): + """Tests for calculate_max_profit function""" + + def test_basic_case(self): + """Buys at 1 and sells at 5, then buys at 3 and sells at 6, for a total profit of 7.""" + self.assertEqual(calculate_max_profit([7, 1, 5, 3, 6, 4]), 7) + + def test_all_increasing_prices(self): + """Buys at 1 and sells at 5, for a total profit of 4.""" + self.assertEqual(calculate_max_profit([1, 2, 3, 4, 5]), 4) + + def test_decreasing_prices(self): + """Returns 0 when prices only decrease, as no profit can be made.""" + self.assertEqual(calculate_max_profit([7, 6, 4, 3, 1]), 0) + + def test_constant_prices(self): + """Returns 0 for constant prices, as no profit can be made.""" + self.assertEqual(calculate_max_profit([5, 5, 5, 5, 5]), 0) + + def test_single_day_price(self): + """Returns 0 for single day prices, as no profit can be made.""" + self.assertEqual(calculate_max_profit([10]), 0) + + def test_alternating_prices(self): + """Buys at 10 and sells at 20, three times, for a total profit of 30.""" + self.assertEqual(calculate_max_profit([10, 20, 10, 20, 10, 20]), 30) + + def test_float_prices(self): + """Returns a total profit of 35.0.""" + self.assertEqual(calculate_max_profit([5.5, 10.5, 20.5, 10.5, 30.5]), 35.0) + + def test_large_price_difference(self): + """Buys at 1 and sells at 30001 for a profit of 30000.""" + self.assertEqual(calculate_max_profit([1, 30001]), 30000) + + def test_empty_prices(self): + """Returns 0 for an empty list of prices.""" + self.assertEqual(calculate_max_profit([]), 0) + + def test_negative_prices(self): + """Raises an AssertionError for negative price values.""" + with self.assertRaises(AssertionError): + calculate_max_profit([10, -5, 2, 3]) + + def test_non_list_input(self): + """Raises an AssertionError for non_list inputs.""" + with self.assertRaises(AssertionError): + calculate_max_profit(None) + + def test_non_numeric_values(self): + """Raises an AssertionError for non_numeric_values.""" + with self.assertRaises(AssertionError): + calculate_max_profit([1.5, "2", 3]) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_count_vowels.py b/solutions/tests/test_count_vowels.py new file mode 100644 index 000000000..52f765e30 --- /dev/null +++ b/solutions/tests/test_count_vowels.py @@ -0,0 +1,81 @@ +""" +Unit tests for validating the count_vowels function. + +Test Categories: + - Standard Cases: Regular words with varying vowel counts. + - Edge Cases: Empty strings, repeated vowels, and mixed casing. + - Special Cases: Strings with numbers, special characters, and whitespace. + - Defensive Cases: Ensuring proper handling of non-string inputs. + +Created on: 2025-07-06 +@author: Fatima +""" + +import unittest +from solutions.count_vowels import count_vowels + + +class TestCountVowels(unittest.TestCase): + """Test cases for count_vowels function.""" + + # Standard Cases + def test_typical_word(self): + """It should count vowels in a regular word with both vowels and consonants.""" + self.assertEqual(count_vowels("hello"), 2) + + def test_mixed_case_vowels(self): + """It should count both uppercase and lowercase vowels in a string.""" + self.assertEqual(count_vowels("hellOWorld"), 3) + + def test_uppercase_vowels(self): + """It should count uppercase vowels in the string.""" + self.assertEqual(count_vowels("SMALL"), 1) + + def test_lowercase_vowels(self): + """It should count lowercase vowels in the string.""" + self.assertEqual(count_vowels("small"), 1) + + def test_repeated_vowels(self): + """It should count all repeated vowels in a string.""" + self.assertEqual(count_vowels("aaeeiioouuAAEEIIOOUU"), 20) + + # Edge Cases + def test_empty_string(self): + """It should return 0 for an empty string.""" + self.assertEqual(count_vowels(""), 0) + + def test_only_whitespace(self): + """It should return 0 for strings with only whitespace..""" + self.assertEqual(count_vowels(" "), 0) + + def test_no_vowels(self): + """It should return 0 for a string with no vowels.""" + self.assertEqual(count_vowels("fly"), 0) + + def test_long_string(self): + """It should correctly count vowels in a long mixed-case string.""" + text = ("aAeEiIoOuU" * 500) + "bcdfghjklmnpqrstvwxyz" * 100 + self.assertEqual(count_vowels(text), 5000) + + # Special Cases + def test_alphanumeric_string(self): + """It should count vowels in alphanumeric strings, ignoring digits.""" + self.assertEqual(count_vowels("fatima123Malik"), 5) + + def test_special_characters(self): + """It should ignore special characters and only count vowels.""" + self.assertEqual(count_vowels("!@#$%^&*()aeiOu"), 5) + + def test_string_with_whitespace(self): + """It should count vowels even if the string contains spaces.""" + self.assertEqual(count_vowels("hello world"), 3) + + # Defensive Cases + def test_invalid_input(self): + """Test that raises assertion error for non-string input.""" + with self.assertRaises(AssertionError): + count_vowels(None) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_filter_odd_numbers.py b/solutions/tests/test_filter_odd_numbers.py new file mode 100644 index 000000000..42973af67 --- /dev/null +++ b/solutions/tests/test_filter_odd_numbers.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test module for the print_odd_numbers_in_list function. + +Created on: 01 01 25 +@author: Muhammad Shahroz +""" + +import unittest +from ..filter_odd_numbers import filter_odd_numbers + + +class TestFilterOddNumbers(unittest.TestCase): + """ + Unit tests for the filter_odd_numbers function. + """ + + def test_empty_list(self): + """It should return an empty list for an empty input.""" + self.assertEqual(filter_odd_numbers([]), []) + + def test_all_integers(self): + """It should filter odd numbers from a list of integers.""" + self.assertEqual(filter_odd_numbers([1, 2, 3, 4, 5]), [1, 3, 5]) + + def test_negative_integers(self): + """It should handle negative integers correctly.""" + self.assertEqual(filter_odd_numbers([-1, -2, -3, -4, -5]), [-1, -3, -5]) + + def test_mixed_types(self): + """It should ignore non-integer inputs.""" + self.assertEqual(filter_odd_numbers([1, 2, "three", 4.5, None]), [1]) + + def test_all_floats(self): + """It should return an empty list if all elements are floats.""" + self.assertEqual(filter_odd_numbers([1.1, 2.2, 3.3]), []) + + def test_all_strings(self): + """It should return an empty list if all elements are strings.""" + self.assertEqual(filter_odd_numbers(["one", "two", "three"]), []) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_longest_substring_length.py b/solutions/tests/test_longest_substring_length.py new file mode 100644 index 000000000..87d83aac2 --- /dev/null +++ b/solutions/tests/test_longest_substring_length.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +This module contains unit tests to validate the functionality of the +`longest_substring_length` function. +It checks the length of the longest substring without repeating characters in a string. + +Test categories: + - Standard cases: Typical strings with and without repeating characters. + - Edge cases: Empty strings, single-character strings, and strings with special characters. + - Defensive tests: Non-string inputs to ensure proper assertion handling. + +Created on: 2025-01-03 +Author: Jola-Moses +""" + +import unittest +from solutions.longest_substring_length import longest_substring_length + + +class TestLongestSubstringLength(unittest.TestCase): + """Tests for longest_substring_length""" + + def test_basic_case(self): + """It should return 3 for 'abcabcbb'.""" + self.assertEqual(longest_substring_length("abcabcbb"), 3) + + def test_all_unique_characters(self): + """It should return the length of the string with all unique characters.""" + self.assertEqual(longest_substring_length("abcdef"), 6) + + def test_all_identical_characters(self): + """It should return 1 for strings with all identical characters.""" + self.assertEqual(longest_substring_length("aaaaaa"), 1) + + def test_single_character(self): + """lt should return 1 for a string with a single character.""" + self.assertEqual(longest_substring_length("a"), 1) + + def test_alternating_characters(self): + """lt should return 2 for 'abababab'.""" + self.assertEqual(longest_substring_length("abababab"), 2) + + def test_long_repetition(self): + """lt should return 6 for 'abcdeeeeeeeefghij'.""" + self.assertEqual(longest_substring_length("abcdeeeeeeeefghij"), 6) + + def test_mixed_case_sensitivity(self): + """lt should handle characters with different cases uniquely.""" + self.assertEqual(longest_substring_length("AaBbCcDd"), 8) + + def test_mixed_characters(self): + """It should handle strings containing alphanumeric and symbol characters.""" + self.assertEqual(longest_substring_length("a1b#2c$3d4"), 10) + + def test_space(self): + """lt should handle a space as a character.""" + self.assertEqual(longest_substring_length("abc abc"), 4) + + def test_empty_string(self): + """It should return 0 for an empty string.""" + self.assertEqual(longest_substring_length(""), 0) + + def test_long_string(self): + """lt should return 1 for 'a' repeated 1 million times.""" + self.assertEqual(longest_substring_length("a" * 10**6), 1) + + def test_invalid_input_integer(self): + """lt should raise an error for non-string input.""" + with self.assertRaises(AssertionError): + longest_substring_length(30) + + def test_invalid_input_none(self): + """lt should raise an error for non-string input.""" + with self.assertRaises(AssertionError): + longest_substring_length(None) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_remove_duplicates_from_sorted_list.py b/solutions/tests/test_remove_duplicates_from_sorted_list.py new file mode 100644 index 000000000..9dfa16787 --- /dev/null +++ b/solutions/tests/test_remove_duplicates_from_sorted_list.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test module for the remove_duplicates_from_sorted_list function. + +Created on: 01 01 25 +@author: Muhammad Shahroz +""" + +import unittest +from ..remove_duplicates_from_sorted_list import ( + ListNode, + remove_duplicates_from_sorted_list, +) + + +class TestRemoveDuplicatesFromSortedList(unittest.TestCase): + """ + Unit tests for the remove_duplicates_from_sorted_list function. + Ensures that duplicates are removed from a sorted linked list. + """ + + def test_empty_list(self): + """It should evaluate None to None.""" + result = remove_duplicates_from_sorted_list(None) + self.assertIsNone(result) + + def test_single_node_value(self): + """It should evaluate the value of a single-node list.""" + head = ListNode(1) + result = remove_duplicates_from_sorted_list(head) + self.assertEqual(result.value, 1) + + def test_single_node_next(self): + """It should evaluate the next node of a single-node list as None.""" + head = ListNode(1) + result = remove_duplicates_from_sorted_list(head) + self.assertIsNone(result.next) + + def test_duplicate_first_node(self): + """It should remove duplicates at the first node.""" + head = ListNode(1, ListNode(1, ListNode(2))) + result = remove_duplicates_from_sorted_list(head) + self.assertEqual(result.value, 1) + + def test_duplicate_second_node(self): + """It should remove duplicates at the second node.""" + head = ListNode(1, ListNode(1, ListNode(2))) + result = remove_duplicates_from_sorted_list(head) + self.assertEqual(result.next.value, 2) + + def test_duplicate_list_ends(self): + """It should remove duplicates at the end of the list.""" + head = ListNode(1, ListNode(1, ListNode(2, ListNode(3, ListNode(3))))) + result = remove_duplicates_from_sorted_list(head) + self.assertIsNone(result.next.next.next) + + def test_list_without_duplicates_first_node(self): + """It should evaluate the first node of a list without duplicates.""" + head = ListNode(1, ListNode(2, ListNode(3))) + result = remove_duplicates_from_sorted_list(head) + self.assertEqual(result.value, 1) + + def test_list_without_duplicates_second_node(self): + """It should evaluate the second node of a list without duplicates.""" + head = ListNode(1, ListNode(2, ListNode(3))) + result = remove_duplicates_from_sorted_list(head) + self.assertEqual(result.next.value, 2) + + def test_list_without_duplicates_third_node(self): + """It should evaluate the third node of a list without duplicates.""" + head = ListNode(1, ListNode(2, ListNode(3))) + result = remove_duplicates_from_sorted_list(head) + self.assertEqual(result.next.next.value, 3) + + def test_invalid_input(self): + """It should raise an assertion error for invalid input.""" + with self.assertRaises(AssertionError): + remove_duplicates_from_sorted_list("invalid_input") + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_remove_every_other.py b/solutions/tests/test_remove_every_other.py new file mode 100644 index 000000000..2b7c79109 --- /dev/null +++ b/solutions/tests/test_remove_every_other.py @@ -0,0 +1,67 @@ +""" +This module contains unit tests for the `remove_every_other` function. + +The tests cover various scenarios to ensure the function behaves correctly, +including edge cases (e.g., empty lists, single-element lists), +lists with mixed data types, large inputs, and handling of invalid inputs. +""" + +import unittest + +from solutions.remove_every_other import remove_every_other # Updated import + + +class TestRemoveEveryOther(unittest.TestCase): + """ + Unit tests for remove_every_other function. + """ + + def test_removes_every_second_element(self): + """ + Test that it correctly removes every second element. + """ + self.assertEqual( + remove_every_other(["Keep", "Remove", "Keep", "Remove", "Keep"]), + ["Keep", "Keep", "Keep"], + ) + + def test_single_element_list(self): + """ + Test that a single-element list remains unchanged. + """ + self.assertEqual(remove_every_other(["OnlyOne"]), ["OnlyOne"]) + + def test_mixed_type_list(self): + """ + Test that it works with mixed-type lists. + """ + self.assertEqual(remove_every_other([1, "Two", 3, "Four", 5]), [1, 3, 5]) + + def test_large_list(self): + """ + Test with a large list. + """ + self.assertEqual(remove_every_other(list(range(10))), [0, 2, 4, 6, 8]) + + def test_defensive_assertion(self): + """ + Test that the function raises an assertion error for invalid input. + """ + with self.assertRaises(AssertionError): + remove_every_other("Not a list") + + def test_empty_list(self): + """ + Test that an empty list returns an empty list. + """ + self.assertEqual(remove_every_other([]), []) + + def test_list_with_duplicates(self): + """ + Test that the function handles duplicate values correctly. + """ + self.assertEqual(remove_every_other([1, 2, 2, 3, 3, 4]), [1, 2, 3]) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_remove_triplets.py b/solutions/tests/test_remove_triplets.py new file mode 100644 index 000000000..da4b9902a --- /dev/null +++ b/solutions/tests/test_remove_triplets.py @@ -0,0 +1,68 @@ +import unittest +from solutions.remove_triplets import remove_triplets + + +# Test cases for remove_triplets function +class TestRemoveTriplets(unittest.TestCase): + """ + Unit tests for the remove_triplets function. + + This module contains a set of test cases for the remove_triplets function + from the solutions package. The function ensures no three consecutive + characters remain in a string by removing the minimum number of characters + required. + + The tests cover various scenarios, including: + - Empty string input + - Strings without consecutive characters + - Strings with one or more groups of consecutive characters + - Boundary cases such as single characters or long sequences + - Defensive checks for invalid input types + + Test cases are implemented using Python's unittest framework. + + Created on: 02/01/25 + @author: Madiha Malikzada + """ + + def test_empty_input(self): + """Test when input is empty""" + self.assertEqual(remove_triplets(""), "") + + def test_single_character_input(self): + """Test input with a single character""" + self.assertEqual(remove_triplets("a"), "a") + + def test_two_identical_characters(self): + """Test input with two identical characters""" + self.assertEqual(remove_triplets("aa"), "aa") + + def test_three_identical_characters(self): + """Test input with three identical characters""" + self.assertEqual(remove_triplets("aaa"), "aa") + + def test_input_without_consecutive(self): + """Test input without consecutive characters""" + self.assertEqual(remove_triplets("abc"), "abc") + + def test_input_with_one_consecutive(self): + """Test input with one consecutive character""" + self.assertEqual(remove_triplets("helllo"), "hello") + self.assertEqual(remove_triplets("leeetcode"), "leetcode") + + def test_input_with_two_consecutive(self): + """Test input with two consecutive characters""" + self.assertEqual(remove_triplets("aaabaaaa"), "aabaa") + + def test_mixed_triplets(self): + """Test input with multiple groups of triplets""" + self.assertEqual(remove_triplets("aaabbbcccaaa"), "aabbccaa") + + def test_invalid_input_type(self): + """Test invalid input type""" + with self.assertRaises(AssertionError): + remove_triplets(123) # Passing an integer instead of a string + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_reverse_string.py b/solutions/tests/test_reverse_string.py new file mode 100644 index 000000000..6767972b9 --- /dev/null +++ b/solutions/tests/test_reverse_string.py @@ -0,0 +1,71 @@ +""" +Module: test_reverse_string +Author: Mojtaba Fayyaz +Date: 2024-01-02 + +This module contains unit tests for the `reverse_string` function. +The tests verify the function's behavior with various input scenarios, +including edge cases such as empty strings, single-character strings, +palindromes, and mixed characters, as well as invalid input handling. +""" + +import unittest + +from solutions.reverse_string import reverse_string + + +class TestReverseString(unittest.TestCase): + """ + Unit tests for the reverse_string function. + """ + + def test_reverse_simple_string(self): + """ + Test that it correctly reverses a simple string. + """ + s = ["h", "e", "l", "l", "o"] + reverse_string(s) + self.assertEqual(s, ["o", "l", "l", "e", "h"]) + + def test_reverse_palindrome(self): + """ + Test that a palindrome remains unchanged after reversal. + """ + s = ["m", "a", "d", "a", "m"] + reverse_string(s) + self.assertEqual(s, ["m", "a", "d", "a", "m"]) + + def test_single_character_string(self): + """ + Test that a single-character string remains unchanged. + """ + s = ["A"] + reverse_string(s) + self.assertEqual(s, ["A"]) + + def test_empty_string(self): + """ + Test that an empty list remains unchanged. + """ + s = [] + reverse_string(s) + self.assertEqual(s, []) + + def test_mixed_characters_string(self): + """ + Test that it correctly reverses a string with mixed characters. + """ + s = ["1", "2", "!", "@", "a", "b"] + reverse_string(s) + self.assertEqual(s, ["b", "a", "@", "!", "2", "1"]) + + def test_defensive_assertion(self): + """ + Test that the function raises an assertion error for invalid input. + """ + with self.assertRaises(AssertionError): + reverse_string("Not a list") + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_three_sum.py b/solutions/tests/test_three_sum.py new file mode 100644 index 000000000..39e437199 --- /dev/null +++ b/solutions/tests/test_three_sum.py @@ -0,0 +1,53 @@ +import unittest + +from solutions.three_sum import three_sum + + +class TestThreeSum(unittest.TestCase): + """Test cases for the three_sum function.""" + + def test_example_case(self): + """It should find all unique triplets that sum to zero.""" + nums = [-1, 0, 1, 2, -1, -4] + expected = [[-1, -1, 2], [-1, 0, 1]] + self.assertEqual(sorted(three_sum(nums)), sorted(expected)) + + def test_no_triplets(self): + """It should return an empty list when no triplets exist.""" + nums = [0, 1, 1] + expected = [] + self.assertEqual(three_sum(nums), expected) + + def test_all_zeros(self): + """It should handle cases with all elements being zero.""" + nums = [0, 0, 0] + expected = [[0, 0, 0]] + self.assertEqual(three_sum(nums), expected) + + def test_empty_list(self): + """It should return an empty list for an empty input list.""" + nums = [] + expected = [] + self.assertEqual(three_sum(nums), expected) + + def test_fewer_than_three_elements(self): + """It should return an empty list if fewer than three elements are provided.""" + nums = [1, -1] + expected = [] + self.assertEqual(three_sum(nums), expected) + + def test_duplicates_in_triplets(self): + """It should avoid duplicate triplets in the result.""" + nums = [-2, 0, 0, 2, 2] + expected = [[-2, 0, 2]] + self.assertEqual(sorted(three_sum(nums)), sorted(expected)) + + def test_large_negative_and_positive_numbers(self): + """It should handle large positive and negative numbers.""" + nums = [-(10**6), 0, 10**6, -1, 1] + expected = [[-(10**6), 0, 10**6], [-1, 0, 1]] + self.assertEqual(sorted(three_sum(nums)), sorted(expected)) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_who_likes_it.py b/solutions/tests/test_who_likes_it.py new file mode 100644 index 000000000..e32edd4f7 --- /dev/null +++ b/solutions/tests/test_who_likes_it.py @@ -0,0 +1,83 @@ +""" +Tests for the `who_likes_it` module. + +This tests the correctness of the function `who_likes_it`: + - Different numbers of names in the input list. + - Defensive assertions for invalid inputs + - Boundary cases like empty and large lists. + +Author: Madiha Malikzada +Date: 2025-01-06 +""" + +import unittest +from solutions.who_likes_it import who_likes_it + + +class TestWhoLikesIt(unittest.TestCase): + """ + Unit tests for the `who_likes_it` function + """ + + def test_boundary_empty_list(self): + """ + Test boundary case for an empty list. + """ + self.assertEqual(who_likes_it([]), "no one likes this") + + def test_one_like(self): + """ + Test the case when one person likes the post + """ + self.assertEqual(who_likes_it(["Evan"]), "Evan likes this") + + def test_two_likes(self): + """ + Test the case when two people like the post + """ + self.assertEqual(who_likes_it(["Evan", "Madiha"]), "Evan and Madiha like this") + + def test_three_likes(self): + """ + Test the case when three people like the post + """ + self.assertEqual( + who_likes_it(["Evan", "Madiha", "Megan"]), + "Evan, Madiha and Megan like this", + ) + + def test_more_than_three_likes(self): + """ + Test the case when more than three people like the post + """ + self.assertEqual( + who_likes_it(["Evan", "Madiha", "Megan", "Camila"]), + "Evan, Madiha and 2 others like this", + ) + + def test_invalid_input(self): + """ + Test the case when input is not a list type + """ + with self.assertRaises(AssertionError): + who_likes_it("not a list") + + def test_defensive_non_string_elements(self): + """ + Test the case when input list contains non-string elements + """ + with self.assertRaises(AssertionError): + who_likes_it(["Evan", 1, "Megan"]) + + def test_boundary_large_list(self): + """ + Test boundary case for a large list. + """ + names = ["Name" + str(i) for i in range(100)] + self.assertEqual( + who_likes_it(names), f"Name0, Name1 and {len(names) - 2} others like this" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/three_sum.py b/solutions/three_sum.py new file mode 100644 index 000000000..bcdd14dfc --- /dev/null +++ b/solutions/three_sum.py @@ -0,0 +1,45 @@ +def three_sum(nums): + """ + Find all unique triplets in the array that sum up to zero. + + Parameters: + nums (List[int]): The input list of integers. + + Returns: + List[List[int]]: A list of unique triplets that sum to zero. + + Examples: + >>> three_sum([-1, 0, 1, 2, -1, -4]) + [[-1, -1, 2], [-1, 0, 1]] + >>> three_sum([0, 1, 1]) + [] + >>> three_sum([0, 0, 0]) + [[0, 0, 0]] + """ + nums.sort() # Sort the array + result = [] + + for i in range(len(nums) - 2): + # Skip duplicate elements + if i > 0 and nums[i] == nums[i - 1]: + continue + + left, right = i + 1, len(nums) - 1 + while left < right: + total = nums[i] + nums[left] + nums[right] + + if total == 0: + result.append([nums[i], nums[left], nums[right]]) + left += 1 + right -= 1 + # Skip duplicates for left and right pointers + while left < right and nums[left] == nums[left - 1]: + left += 1 + while left < right and nums[right] == nums[right + 1]: + right -= 1 + elif total < 0: + left += 1 + else: + right -= 1 + + return result diff --git a/solutions/who_likes_it.py b/solutions/who_likes_it.py new file mode 100644 index 000000000..95d7a9249 --- /dev/null +++ b/solutions/who_likes_it.py @@ -0,0 +1,66 @@ +""" +This module provides a like display message based on the input list of names. + +Module contents: + Returns a string that describes how many people like a post. + +Author: Madiha Malikzada +Date: 2025-01-06 +""" + +from typing import List + + +# ---- define function ---- +def who_likes_it(names: List[str]) -> str: + """ + Generate a "like display message based on the input list of names + + Args: + names (List[str]): A list of names representing people who liked a an item (post) + + Returns: + str: A display message that describes how many people liked the post + + Raises: + - AssertionError: If any name in the list is not a string + - The list itself is not of type `List` + + Examples: + >>> who_likes_it([]) + 'no one likes this' + >>> who_likes_it(["Evan"]) + 'Evan likes this' + >>> who_likes_it(["Evan", "Madiha"]) + 'Evan and Madiha like this' + >>> who_likes_it(["Evan", "Madiha", "Megan"]) + 'Evan, Madiha and Megan like this' + >>> who_likes_it(["Evan", "Madiha", "Megan", "Camila"]) + 'Evan, Madiha and 2 others like this' + """ + + # Defensive assertions + assert isinstance(names, List), "Input names should be of type List" + assert all(isinstance(name, str) for name in names), ( + "All names should be of type string" + ) + + names_length = len(names) + + # If the list is empty, return 'no one likes this'. + if names_length == 0: + return "no one likes this" + + # If the list has 1 to 3 names, format them directly in the message. + elif names_length == 1: + return f"{names[0]} likes this" + + elif names_length == 2: + return f"{names[0]} and {names[1]} like this" + + elif names_length == 3: + return f"{names[0]}, {names[1]} and {names[2]} like this" + + # For 4 or more names, summarize additional names as 'X others'. + else: + return f"{names[0]}, {names[1]} and {names_length - 2} others like this"