diff --git a/solutions/README.md b/solutions/README.md index 8d01f2910..0fff7bde4 100644 --- a/solutions/README.md +++ b/solutions/README.md @@ -25,7 +25,9 @@ while corresponding test files are maintained in the `tests` folder. | `spiral_traverse.py` | Traverses a 2D matrix in spiral order | Fahed | | `euler_totient.py` | Computes Euler's totient function (ϕ(n))| Fahed | | `is_prime.py` | determine whether a given number is prime. | Fahed| +| `gcd.py` | A function to calculate the greatest common divisor (GCD)| Fahed| | `kronecker_product.py` | Computes the Kronecker ⊗ product of 2 matrices | Fahed| +| `fibonacci.py` | Generates Fibonacci sequences up to n terms | Fahed | | `direction_to_degree.py` | Convert a cardinal direction to degree | Obay | | `check_odd_or_even.py` | Checks if a number is odd or even | Clement | | `grading_system.py`| Assigning letter grade to a numeric score. | Razan | @@ -43,6 +45,8 @@ while corresponding test files are maintained in the `tests` folder. | `password_strength.py` | Checks the strength of a password| Anas | | `decimal_to_binary.py` | Converts decimal to its equivalent binary| Anas | | `count_character.py`| counts occurrences of a character in a string| Mohamed| +| `is_positive.py` | Determines if a given number is positive | Faisal | +| `is_palindrome.py` | Checks string palindrome properties | Faisal | --- diff --git a/solutions/fibonacci.py b/solutions/fibonacci.py new file mode 100644 index 000000000..2ba05dce9 --- /dev/null +++ b/solutions/fibonacci.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +A module for generating Fibonacci sequences. + +Module contents: +- generate_fibonacci: Generates Fibonacci sequence up to n terms. + +The Fibonacci sequence is a series of numbers where each number is the sum of the two +preceding ones, starting with 0 and 1. + +Author: Fahed Daibes +Created: 12-Jan-2025 +""" + + +def generate_fibonacci(n: int) -> list: + """ + Generates the first n terms of the Fibonacci sequence. + + Parameters: + - n (int): The number of terms to generate. + + Returns: + - list: A list containing the first n terms of the Fibonacci sequence. + + Raises: + - AssertionError: If the input is not a positive integer. + + Examples: + >>> generate_fibonacci(0) + [] + + >>> generate_fibonacci(1) + [0] + + >>> generate_fibonacci(5) + [0, 1, 1, 2, 3] + + >>> generate_fibonacci("five") + Traceback (most recent call last): + ... + AssertionError: Input must be a non-negative integer. + """ + # Defensive check + assert isinstance(n, int) and n >= 0, "Input must be a non-negative integer." + + if n == 0: + return [] + if n == 1: + return [0] + + sequence = [0, 1] + for _ in range(2, n): + sequence.append(sequence[-1] + sequence[-2]) + + return sequence diff --git a/solutions/gcd.py b/solutions/gcd.py new file mode 100644 index 000000000..99e2ebafc --- /dev/null +++ b/solutions/gcd.py @@ -0,0 +1,54 @@ +# !/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +gcd.py + +This module provides a function to calculate the greatest common divisor (GCD) +of two integers using the Euclidean algorithm. + +Author: Fahed Daibes +Date: Jan 12 2025 +Group: ET6-foundations-group-16 +""" + + +def gcd(a: int, b: int) -> int: + """ + Calculates the greatest common divisor (GCD) of two integers. + + Parameters: + - a (int): The first integer. + - b (int): The second integer. + + Returns: + - int: The GCD of the two numbers. + + Raises: + - AssertionError: If either input is not an integer. + + Examples: + >>> gcd(48, 18) + 6 + + >>> gcd(101, 103) + 1 + + >>> gcd(0, 25) + 25 + + >>> gcd(0, 0) + 0 + + >>> gcd("eight", 16) + Traceback (most recent call last): + ... + AssertionError: Both inputs must be integers. + """ + # Defensive check + assert isinstance(a, int) and isinstance(b, int), "Both inputs must be integers." + + while b != 0: + a, b = b, a % b + + return abs(a) diff --git a/solutions/is_positive.py b/solutions/is_positive.py new file mode 100644 index 000000000..334b7d779 --- /dev/null +++ b/solutions/is_positive.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +""" +A module for checking if a number is positive. + +Module contents: +- is_positive: Determines if a given number is positive. + +Author: Faisal Minawi +Created: 2025-01-08 +""" + + +def is_positive(n: float) -> bool: + """Determines if a given number is positive. + + A number is considered positive if it is greater than 0. + + Raises: + TypeError: If the input is not a real number (int or float). + + Parameters: + n: float or int, the number to check. + + Returns: + bool: True if the number is greater than 0, False otherwise. + + Examples: + >>> is_positive(5) + True + >>> is_positive(-3) + False + >>> is_positive(0) + False + >>> is_positive(3.14) + True + >>> is_positive(-2.5) + False + >>> is_positive(True) + Traceback (most recent call last): + ... + TypeError: Input must be a real number (int or float) + """ + if isinstance(n, bool): + raise TypeError("Input must be a real number (int or float)") + if not isinstance(n, (int, float)): + raise TypeError("Input must be a real number (int or float)") + return n > 0 diff --git a/solutions/tests/test_fibonacci.py b/solutions/tests/test_fibonacci.py new file mode 100644 index 000000000..7337cabc9 --- /dev/null +++ b/solutions/tests/test_fibonacci.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +test_fibonacci.py + +This module contains unit tests for the `generate_fibonacci` function, which generates +the Fibonacci sequence up to a given number of terms. + +Author: Fahed Daibes +Date: Jan 12 2025 +Group: ET6-foundations-group-16 + +Tests: +- Valid inputs for various sequence lengths. +- Edge cases, such as n=0 and n=1. +- Invalid inputs, such as negative numbers and non-integer types. +""" + +import unittest + +from solutions.fibonacci import generate_fibonacci + + +def assert_fibonacci(expected, n): + """ + Custom assertion function to compare the result of generate_fibonacci with the expected value. + + Parameters: + - expected (list): The expected Fibonacci sequence. + - n (int): The input number of terms. + + Raises: + - AssertionError: If the result of generate_fibonacci does not match the expected value. + """ + result = generate_fibonacci(n) + assert result == expected, f"Expected {expected} for {n}, but got {result}." + + +class TestFibonacci(unittest.TestCase): + """ + This test class contains unit tests for the `generate_fibonacci` function. + + Each test uses only one assertion with the custom `assert_fibonacci` function. + """ + + def test_zero_terms(self): + """Test case for zero terms.""" + assert_fibonacci([], 0) + + def test_one_term(self): + """Test case for one term.""" + assert_fibonacci([0], 1) + + def test_five_terms(self): + """Test case for five terms.""" + assert_fibonacci([0, 1, 1, 2, 3], 5) + + def test_large_sequence(self): + """Test case for a larger sequence.""" + assert_fibonacci([0, 1, 1, 2, 3, 5, 8, 13, 21, 34], 10) + + def test_negative_input(self): + """Test case for negative input.""" + with self.assertRaises(AssertionError): + generate_fibonacci(-3) + + def test_invalid_string(self): + """Test case for an invalid string input.""" + with self.assertRaises(AssertionError): + generate_fibonacci("ten") + + def test_invalid_float(self): + """Test case for an invalid float input.""" + with self.assertRaises(AssertionError): + generate_fibonacci(3.5) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_gcd.py b/solutions/tests/test_gcd.py new file mode 100644 index 000000000..2afe5d502 --- /dev/null +++ b/solutions/tests/test_gcd.py @@ -0,0 +1,85 @@ +# !/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +test_gcd.py + +This module contains unit tests for the `gcd` function, which calculates the +greatest common divisor (GCD) of two integers. + +Author: Fahed Daibes +Date: Jan 12 2025 +Group: ET6-foundations-group-16 + +Tests: +- Valid inputs, including positive, negative, and zero values. +- Invalid inputs, such as non-integer types. +""" + +import unittest + +from solutions.gcd import gcd + + +def assert_gcd(expected, a, b): + """ + Custom assertion function to compare the result of gcd with the expected value. + + Parameters: + - expected (int): The expected GCD. + - a (int): The first input integer. + - b (int): The second input integer. + + Raises: + - AssertionError: If the result of gcd does not match the expected value. + """ + result = gcd(a, b) + assert result == expected, ( + f"Expected {expected} for gcd({a}, {b}), but got {result}." + ) + + +class TestGCD(unittest.TestCase): + """ + This test class contains unit tests for the `gcd` function. + + Each test uses only one assertion with the custom `assert_gcd` function. + """ + + def test_gcd_positive_numbers(self): + """Test case for two positive numbers.""" + assert_gcd(6, 48, 18) + + def test_gcd_coprime_numbers(self): + """Test case for two coprime numbers.""" + assert_gcd(1, 101, 103) + + def test_gcd_one_zero(self): + """Test case for one number being zero.""" + assert_gcd(25, 0, 25) + + def test_gcd_both_zero(self): + """Test case for both numbers being zero.""" + assert_gcd(0, 0, 0) + + def test_gcd_negative_numbers(self): + """Test case for negative numbers.""" + assert_gcd(6, -48, -18) + + def test_gcd_mixed_signs(self): + """Test case for one positive and one negative number.""" + assert_gcd(6, 48, -18) + + def test_invalid_string_input(self): + """Test case for a string input.""" + with self.assertRaises(AssertionError): + gcd("forty-eight", 18) + + def test_invalid_float_input(self): + """Test case for a float input.""" + with self.assertRaises(AssertionError): + gcd(48.5, 18) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_is_positive.py b/solutions/tests/test_is_positive.py new file mode 100644 index 000000000..b60257d6f --- /dev/null +++ b/solutions/tests/test_is_positive.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test module for is_positive function. + +Module contents: +- TestIsPositive: Unit test for the is_positive function. + +Test categories: + - Standard cases: positive numbers, negative numbers, zero + - Edge cases: very large numbers, very small numbers, floating points + - Defensive tests: wrong input types +""" + +import unittest + +from ..is_positive import is_positive + + +class TestIsPositive(unittest.TestCase): + """Test suite for the is_positive function.""" + + # Standard test cases + def test_basic_positive_integer(self): + """It should return True for basic positive integer.""" + self.assertTrue(is_positive(5)) + + def test_basic_negative_integer(self): + """It should return False for basic negative integer.""" + self.assertFalse(is_positive(-5)) + + def test_positive_large_integer(self): + """It should return True for large positive integer.""" + self.assertTrue(is_positive(1000000)) + + def test_negative_large_integer(self): + """It should return False for large negative integer.""" + self.assertFalse(is_positive(-1000000)) + + def test_zero(self): + """It should return False for zero.""" + self.assertFalse(is_positive(0)) + + # Edge cases + def test_very_large_positive_number(self): + """It should handle very large positive numbers correctly.""" + self.assertTrue(is_positive(1e308)) + + def test_very_large_negative_number(self): + """It should handle very large negative numbers correctly.""" + self.assertFalse(is_positive(-1e308)) + + def test_very_small_positive_number(self): + """It should handle very small positive numbers correctly.""" + self.assertTrue(is_positive(1e-308)) + + def test_very_small_negative_number(self): + """It should handle very small negative numbers correctly.""" + self.assertFalse(is_positive(-1e-308)) + + # Floating point tests + def test_positive_float(self): + """It should return True for positive float.""" + self.assertTrue(is_positive(0.1)) + self.assertTrue(is_positive(3.14)) + + def test_negative_float(self): + """It should return False for negative float.""" + self.assertFalse(is_positive(-0.1)) + self.assertFalse(is_positive(-3.14)) + + # Defensive tests + def test_string_input(self): + """It should raise TypeError for string input.""" + with self.assertRaises(TypeError): + is_positive("5") + + def test_none_input(self): + """It should raise TypeError for None input.""" + with self.assertRaises(TypeError): + is_positive(None) + + def test_boolean_true_input(self): + """It should raise TypeError for boolean True input.""" + with self.assertRaises(TypeError): + is_positive(True) + + def test_boolean_false_input(self): + """It should raise TypeError for boolean False input.""" + with self.assertRaises(TypeError): + is_positive(False) + + def test_complex_number_input(self): + """It should raise TypeError for complex number input.""" + with self.assertRaises(TypeError): + is_positive(1 + 2j) + + +if __name__ == "__main__": + unittest.main()