diff --git a/__pycache__/calculator.cpython-313.pyc b/__pycache__/calculator.cpython-313.pyc new file mode 100644 index 0000000..969ff6b Binary files /dev/null and b/__pycache__/calculator.cpython-313.pyc differ diff --git a/__pycache__/complex.cpython-313.pyc b/__pycache__/complex.cpython-313.pyc new file mode 100644 index 0000000..6195042 Binary files /dev/null and b/__pycache__/complex.cpython-313.pyc differ diff --git a/complex.py b/complex.py new file mode 100644 index 0000000..ccb1593 --- /dev/null +++ b/complex.py @@ -0,0 +1,115 @@ +# complex.py +from calculator import Calculator +import re +import cmath + +def _format_complex(c): + """ + Helper function to format a Python complex object back into a + clean 'a+bj' string without parentheses to match lab requirements. + """ + res = str(c).replace(' ', '') + if res.startswith('(') and res.endswith(')'): + res = res[1:-1] + return res + +def parse_complex_string(expression): + """ + Parses an input string like '(3+2j) * (5+3j)'. + Extracts the two complex numbers and the mathematical operator. + """ + # Regex to capture everything inside the parentheses and the operator between them + pattern = r'\s*\(([^)]+)\)\s*([+\-*/])\s*\(([^)]+)\)\s*' + match = re.match(pattern, expression) + + if not match: + raise ValueError("Invalid complex expression format. Expected: '(a+bj) op (c+dj)'") + + c1_str, operator, c2_str = match.groups() + + # Remove any internal spaces (e.g., '3 + 2j' becomes '3+2j') + return c1_str.replace(' ', ''), operator, c2_str.replace(' ', '') + +def add_complex(c1, c2): + """ + Member 2: Handles the addition of two complex numbers[cite: 120]. + """ + pass + +def subtract_complex(c1, c2): + """ + Member 2: Handles the subtraction of c2 from c1[cite: 120]. + """ + pass + +def multiply_complex(c1, c2): + """ + Member 3: Handles the multiplication of two complex numbers[cite: 120]. + """ + + calc = Calculator() + + # 1. Convert the incoming strings to Python complex objects + c1 = complex(c1_str) + c2 = complex(c2_str) + + a, b = c1.real, c1.imag + c, d = c2.real, c2.imag + + # (a+bi)*(c+di) = (ac - bd) + (ad + bc)i + real_part = calc.subtract(calc.multiply(a, c), calc.multiply(b, d)) + imag_part = calc.add(calc.multiply(a, d), calc.multiply(b, c)) + + # 4. Reconstruct into a complex object and format back to a string + result = complex(real_part, imag_part) + return _format_complex(result) + +def divide_complex(c1, c2): + """ + Member 3: Handles the division of c1 by c2[cite: 120]. + Must include error handling for division by zero. + """ + # c1 and c2 are tuples: (real, imag) + calc = Calculator() + + # 1. Convert the incoming strings to Python complex objects + c1 = complex(c1_str) + c2 = complex(c2_str) + + a, b = c1.real, c1.imag + c, d = c2.real, c2.imag + + # denominator = c^2 + d^2 + denominator = calc.add(calc.multiply(c, c), calc.multiply(d, d)) + if denominator == 0: + raise ValueError("Division by zero in complex division") + # real part: (ac + bd) / (c^2 + d^2) + real_num = calc.add(calc.multiply(a, c), calc.multiply(b, d)) + real_part = calc.divide(real_num, denominator) + # imag part: (bc - ad) / (c^2 + d^2) + imag_num = calc.subtract(calc.multiply(b, c), calc.multiply(a, d)) + imag_part = calc.divide(imag_num, denominator) + + result = complex(real_part, imag_part) + return _format_complex(result) + +def compute_magnitude(c): + """ + Member 4: Calculates the magnitude of a single complex number[cite: 122]. + """ + pass + +def compute_phase(c): + """ + Member 4: Calculates the phase (angle) of a single complex number[cite: 122]. + """ + pass + +def evaluate_complex_expression(expression): + """ + Member 5 (Integrator): The main runner function. + Takes the raw string, passes it to parse_complex_string, + routes the parsed data to the correct math function above, + and returns the final string result. + """ + \ No newline at end of file diff --git a/test_complex.py b/test_complex.py new file mode 100644 index 0000000..e244b6a --- /dev/null +++ b/test_complex.py @@ -0,0 +1,100 @@ +# test_complex.py +import unittest +from complex import ( + parse_complex_string, add_complex, subtract_complex, + multiply_complex, divide_complex, compute_magnitude, + compute_phase, evaluate_complex_expression +) + +class TestComplexArithmetic(unittest.TestCase): + + # --- Parser Tests --- + def test_parse_complex_string_valid(self): + c1, op, c2 = parse_complex_string('(3 + 2j) * (5+ 3j)') + self.assertEqual(c1, '3+2j') + self.assertEqual(op, '*') + self.assertEqual(c2, '5+3j') + + def test_parse_complex_string_invalid(self): + # Boundary Condition: Missing brackets should raise a ValueError + with self.assertRaises(ValueError): + parse_complex_string('3+2j * 5+3j') + + # --- Member 2 Tests --- + def test_add_complex(self): + # Test normal addition and negative addition + pass + + def test_subtract_complex(self): + # Test normal subtraction + pass + + # --- Member 3 Tests --- + + def test_multiply_complex(self): + # Normal cases + self.assertEqual(multiply_complex((3, 2), (5, 3)), (9, 19)) # (3+2j)*(5+3j) = (3*5-2*3, 3*3+2*5) = (15-6, 9+10) = (9, 19) + self.assertEqual(multiply_complex((0, 0), (5, 3)), (0, 0)) # Zero times anything + self.assertEqual(multiply_complex((1, 0), (0, 1)), (0, 1)) # (1+0j)*(0+1j) = (0, 1) + self.assertEqual(multiply_complex((2, -3), (-1, 4)), (10, 11)) # (2-3j)*(-1+4j) = (2*-1-(-3)*4, 2*4+(-3)*-1) = (-2+12, 8+3) = (10, 11) + + # Boundary cases + self.assertEqual(multiply_complex((0, 0), (0, 0)), (0, 0)) # Both zero + self.assertEqual(multiply_complex((999999, 0), (0, 999999)), (0, 999999*999999)) + self.assertEqual(multiply_complex((-1, -1), (-1, -1)), (0, 2)) + + # Invalid input handling + with self.assertRaises(TypeError): + multiply_complex((1,), (2, 3)) # Too few elements + with self.assertRaises(TypeError): + multiply_complex((1, 2, 3), (2, 3)) # Too many elements + with self.assertRaises(TypeError): + multiply_complex((1, 'a'), (2, 3)) # Non-numeric + with self.assertRaises(TypeError): + multiply_complex('not a tuple', (2, 3)) + + + def test_divide_complex(self): + # Normal cases + self.assertEqual(divide_complex((3, 2), (5, 3)), (0.6341463414634146, 0.04878048780487805)) + self.assertEqual(divide_complex((0, 0), (5, 3)), (0.0, 0.0)) + self.assertEqual(divide_complex((1, 0), (1, 0)), (1.0, 0.0)) + self.assertEqual(divide_complex((2, -3), (-1, 4)), (-0.6470588235294118, -0.5294117647058824)) + + # Boundary cases + self.assertEqual(divide_complex((0, 0), (0, 1)), (0.0, 0.0)) + self.assertEqual(divide_complex((999999, 0), (0, 999999)), (0.0, -1.0)) + self.assertEqual(divide_complex((-1, -1), (-1, -1)), (1.0, 0.0)) + + # Invalid input handling + with self.assertRaises(TypeError): + divide_complex((1,), (2, 3)) # Too few elements + with self.assertRaises(TypeError): + divide_complex((1, 2, 3), (2, 3)) # Too many elements + with self.assertRaises(TypeError): + divide_complex((1, 'a'), (2, 3)) # Non-numeric + with self.assertRaises(TypeError): + divide_complex('not a tuple', (2, 3)) + + + def test_divide_complex_by_zero(self): + # Test division by zero exception handling + with self.assertRaises(ValueError): + divide_complex((1, 2), (0, 0)) + + # --- Member 4 Tests --- + def test_compute_magnitude(self): + # Test magnitude calculations + pass + + def test_compute_phase(self): + # Test phase calculations (check against known angles) + pass + + # --- Member 5 Tests (Integration) --- + def test_evaluate_complex_expression(self): + # Test the full pipeline from raw string to final output + pass + +if __name__ == "__main__": + unittest.main() \ No newline at end of file