Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions DesignNotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 📐 Design Notes: Complex Number Arithmetic Module
**Team:** Group 13
**Target Branch:** `Group_B`

---

## 1. Architectural Approach
* **Modularity:** The feature is logically encapsulated within an independent `ComplexCalculator` class. This ensures clean integration with the base system without accidentally altering the core `Calculator` class .
* **Separation of Concerns:** The module strictly divides responsibilities into three internal domains: string parsing, mathematical execution, and error management.

## 2. Input Parsing Strategy
* **Constraint:** The system strictly requires mathematical inputs to be provided as continuous strings (e.g., `'(3+2j)*(5+3j)'`).
* **Solution:** We implemented Python's `re` (Regular Expressions) module to act as the extraction engine.
* *Pre-processing:* All arbitrary whitespaces are stripped to standardize the user's input.
* *Extraction:* The regex pattern `r'\(([^)]+)\)([\+\-\*\/])\(([^)]+)\)'` dynamically isolates the first operand, the arithmetic operator, and the second operand into exact capture groups.

## 3. Mathematical Implementation
* **Object Casting:** Extracted string operands are safely cast directly to Python's native `complex()` objects, inherently supporting the required $a+bj$ mathematical representation.
***Core Arithmetic:** Standard Python overloaded operators (`+`, `-`, `*`, `/`) handle the required addition, subtraction, multiplication, and division operations.
* **Advanced Computations:** Standard mathematical libraries are utilized for complex plane metrics.
* **Magnitude:** Calculated using the built-in `abs(z)` function to efficiently compute $\sqrt{a^2 + b^2}$.
* **Phase:** Computed using `cmath.phase(z)` to guarantee accurate quadrant mapping and return the angle in radians.
* **Output Formatting:** Raw mathematical results are dynamically reformatted back into the strictly required `a+bj` string notation before being returned to the main process.

## 4. Error Handling & Validation
* **Syntax Validation:** The regex parser acts as the first line of defense. Unmatched patterns or invalid characters raise an immediate `ValueError`.
* **Boundary Conditions:** A programmatic check intercepts mathematical impossibilities, such as dividing by `(0+0j)`, raising a descriptive exception to prevent catastrophic system crashes.
57 changes: 57 additions & 0 deletions FinalReport.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Software Engineering Laboratory: Final Project Report
**Project:** Extensible Calculator System - Complex Number Arithmetic
**Team:** Group 13
**Target Integration Branch:** `Group_B`

---

## 1. Introduction and Objective
The primary objective of this laboratory experiment was to gain practical experience with industry-style software development practices. This was achieved by applying all phases of the Software Development Life Cycle (SDLC) to a single, extensible Python-based calculator system.

Our specific team, Group 13, was tasked with analyzing, designing, implementing, and testing the **Complex Number Arithmetic** extension. The goal was to build a modular feature that integrates seamlessly with the base calculator application while adhering to strict version control and testing protocols.

## 2. Requirement Analysis
During the initial phase of the SDLC, we identified the specific functional and non-functional requirements for the complex number module.

**Functional Requirements:**
* The module must accept user input exclusively as a continuous string (e.g., `'(3+2j)*(5+3j)'`).
* It must support the standard $a+bj$ mathematical representation.
* The system must perform standard arithmetic operations: addition, subtraction, multiplication, and division of complex numbers.
* The system must compute advanced metrics: magnitude and phase.

**Non-Functional Requirements & Constraints:**
* Code must be highly modular and maintainable, isolating complex logic from the core `Calculator` class.
* All errors (like malformed strings or division by zero) must be handled gracefully without crashing the main application.

## 3. System Architecture and Design
To fulfill the requirements, we designed an independent software component to handle all complex domain logic.

* **Object-Oriented Encapsulation:** We designed a standalone `ComplexCalculator` class. This ensures that the base system remains untouched and adheres to the Open/Closed Principle (open for extension, closed for modification).
* **String Parsing Engine:** Because inputs are strict strings, we designed a parsing layer using Python's Regular Expressions (`re` module). We engineered the pattern `r'\(([^)]+)\)([\+\-\*\/])\(([^)]+)\)'` to dynamically isolate the first operand, the arithmetic operator, and the second operand, bypassing arbitrary whitespace issues.
* **Mathematical Delegation:** Rather than manually calculating complex arithmetic, the design leverages Python's native `complex()` casting capabilities and the standard `cmath` library to guarantee mathematical precision.

## 4. Implementation Details
The implementation phase translated our design into functional Python code within the `complex.py` module.

* **Arithmetic Execution:** Once the regex engine extracts the string components, they are cast to `complex` objects. Python's overloaded operators process the calculation, and a custom `_format_result` method reconstructs the output back into the required $a+bj$ string format.
* **Magnitude Computation:** Implemented using the built-in `abs(z)` function, which efficiently processes the underlying $\sqrt{a^2 + b^2}$ calculation.
* **Phase Computation:** Implemented using `cmath.phase(z)`, which safely maps the angle in radians across all four quadrants.
* **Exception Handling:** A robust `try-except` block is implemented to catch `ValueError` instances. If the regex fails to find a match, or if a zero-division occurs during evaluation, the module halts the calculation and returns a descriptive error string.

## 5. Software Testing and Quality Assurance
Test-driven development practices were strictly followed. A comprehensive unit test suite was developed in `test_complex.py` using Python's native `unittest` framework to ensure high code quality and test coverage.

The test suite validates the system against three core paradigms:
* **Normal Cases:** Tests verify standard execution for all operations (e.g., asserting that `(1+2j)+(2+3j)` successfully evaluates to `3.0+5.0j`).
* **Boundary Conditions:** Edge cases were tested, most notably asserting that evaluating `(5+5j)/(0+0j)` correctly raises a `ValueError` rather than triggering a system-level math fault.
* **Invalid Input Handling:** The parser's resilience was tested by injecting malformed strings (e.g., missing parentheses, alphabetic characters, and unsupported operators). The tests confirm that the system correctly identifies and rejects these inputs.

## 6. Version Control and Configuration Management
To support concurrent development across 20 groups without code conflicts, strict Git configuration management protocols were followed.

* **Repository Forking & Cloning:** The main project repository was forked to an isolated environment. As Group 13 falls into the second cohort, we specifically cloned and targeted the `Group_B` branch for our baseline code.
* **Feature Branching:** Development was conducted entirely within an isolated feature branch (`FeatureB_13`) to prevent contamination of the group's integration branch.
* **Integration and Merging:** Following successful local testing, code updates were committed incrementally. The final feature was pushed to the remote fork, and a Pull Request was generated targeting the main repository's `Group_B` branch, ensuring zero merge conflicts with other teams.

## 7. Conclusion
Through this experiment, Group 13 successfully applied the complete Software Development Life Cycle. By conducting thorough requirement analysis, designing a modular regex-driven architecture, implementing robust unit tests, and adhering to strict Git branching strategies, we successfully delivered a highly functional and reliable Complex Number Arithmetic module for the extensible calculator system.
Binary file added __pycache__/calculator.cpython-313.pyc
Binary file not shown.
Binary file added __pycache__/complex.cpython-313.pyc
Binary file not shown.
81 changes: 81 additions & 0 deletions complex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import cmath
import re

class ComplexCalculator:
def evaluate(self, expression: str) -> str:
"""
Parses and evaluates a complex number expression.
Example input: '(3+2j)*(5+3j)'
"""
# Remove all spaces so we don't have to worry about weird spacing
expr = expression.replace(" ", "")

# Regex to match the arithmetic format: (a+bj) operator (c+dj)
# Group 1: First number, Group 2: Operator, Group 3: Second number
pattern = r'\(([^)]+)\)([\+\-\*\/])\(([^)]+)\)'
match = re.match(pattern, expr)

if match:
c1_str = match.group(1)
operator = match.group(2)
c2_str = match.group(3)
return self._perform_arithmetic(c1_str, operator, c2_str)

# Regex to handle Magnitude: e.g., 'mag(3+2j)'
mag_match = re.match(r'mag\(([^)]+)\)', expr)
if mag_match:
return self.magnitude(mag_match.group(1))

# Regex to handle Phase: e.g., 'phase(3+2j)'
phase_match = re.match(r'phase\(([^)]+)\)', expr)
if phase_match:
return self.phase(phase_match.group(1))

raise ValueError("Invalid complex number expression format.")

def _perform_arithmetic(self, c1: str, operator: str, c2: str) -> str:
# Convert strings to actual complex numbers
try:
z1 = complex(c1)
z2 = complex(c2)
except ValueError:
raise ValueError("Invalid format. Use a+bj notation.")

# Perform the actual math
if operator == '+':
result = z1 + z2
elif operator == '-':
result = z1 - z2
elif operator == '*':
result = z1 * z2
elif operator == '/':
if z2 == 0:
raise ValueError("Division by zero")
result = z1 / z2
else:
raise ValueError("Unknown operator")

return self._format_result(result)

def magnitude(self, c_str: str) -> str:
z = complex(c_str)
# Magnitude is calculated as sqrt(a^2 + b^2)
return str(abs(z))

def phase(self, c_str: str) -> str:
z = complex(c_str)
# Phase is calculated in radians
return str(cmath.phase(z))

def _format_result(self, z: complex) -> str:
"""Formats the output back to a readable a+bj string."""
# Ensure the imaginary part always has a + or - sign
sign = '+' if z.imag >= 0 else '-'
return f"{z.real}{sign}{abs(z.imag)}j"

# # Example Usage (You can delete this part in your final file, it's just to show you how it works):
# if __name__ == "__main__":
# calc = ComplexCalculator()
# print(calc.evaluate("(3+2j)*(5+3j)")) # Output: 9.0+19.0j
# print(calc.evaluate("(1+2j)+(2+3j)")) # Output: 3.0+5.0j
# print(calc.evaluate("mag(3+4j)")) # Output: 5.0
66 changes: 66 additions & 0 deletions test_complex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import unittest
import math
from complex import ComplexCalculator

class TestComplexCalculator(unittest.TestCase):

def setUp(self):
# This creates a new calculator object before each test runs
self.calc = ComplexCalculator()

# --- NORMAL CASES ---
def test_addition(self):
# (1+2j) + (2+3j) = 3+5j
result = self.calc.evaluate("(1+2j)+(2+3j)")
self.assertEqual(result, "3.0+5.0j")

def test_subtraction(self):
# (5+5j) - (2+1j) = 3+4j
result = self.calc.evaluate("(5+5j)-(2+1j)")
self.assertEqual(result, "3.0+4.0j")

def test_multiplication(self):
# (1+2j) * (3+4j) = -5+10j
result = self.calc.evaluate("(1+2j)*(3+4j)")
self.assertEqual(result, "-5.0+10.0j")

def test_division(self):
# (10+5j) / (2+0j) = 5+2.5j
result = self.calc.evaluate("(10+5j)/(2+0j)")
self.assertEqual(result, "5.0+2.5j")

def test_magnitude(self):
# Magnitude of 3+4j is exactly 5.0 (Pythagorean triple: 3^2 + 4^2 = 5^2)
result = self.calc.evaluate("mag(3+4j)")
self.assertEqual(result, "5.0")

def test_phase(self):
# Phase of 0+1j is exactly pi/2 (90 degrees)
result = self.calc.evaluate("phase(0+1j)")
# Since float comparison can be tricky, we round it for testing
self.assertEqual(round(float(result), 4), round(math.pi / 2, 4))

# --- BOUNDARY CONDITIONS & INVALID INPUTS ---
def test_divide_by_zero(self):
# The system must catch dividing by zero and raise a ValueError
with self.assertRaises(ValueError) as context:
self.calc.evaluate("(5+5j)/(0+0j)")
self.assertTrue("Division by zero" in str(context.exception))

def test_invalid_string_format(self):
# Testing what happens if the user types complete garbage
with self.assertRaises(ValueError):
self.calc.evaluate("hello_world")

def test_missing_parentheses(self):
# The regex strictly expects parentheses. Missing them should fail.
with self.assertRaises(ValueError):
self.calc.evaluate("3+2j*5+3j")

def test_invalid_operator(self):
# Testing an operator that isn't supported by the regex
with self.assertRaises(ValueError):
self.calc.evaluate("(3+2j)^(5+3j)")

if __name__ == "__main__":
unittest.main()