Project Overview
This capstone project challenges you to build a fully-functional scientific calculator using C++. You will work with the Kaggle Mathematical Expression Dataset containing 500 test expressions covering basic arithmetic, algebraic operations, trigonometric functions, logarithms, and complex nested expressions with varying operator precedence. The dataset includes expressions with expected results for validation, edge cases for error handling, and performance benchmarks. Your calculator must parse and evaluate these expressions correctly, demonstrating mastery of C++ fundamentals including OOP, data structures (stacks, queues), operator overloading, and exception handling.
Expression Parser
Parse mathematical expressions with operator precedence
Scientific Ops
Trigonometry, logarithms, powers, and roots
Memory System
Store, recall, add, and clear memory values
History
Track calculations with undo/redo support
Learning Objectives
Technical Skills
- Implement expression parsing with stacks
- Apply operator overloading for custom types
- Use inheritance and polymorphism for operations
- Handle exceptions for mathematical errors
- Implement the command pattern for history
Software Design Skills
- Design modular, extensible architecture
- Separate concerns (parsing, evaluation, UI)
- Write comprehensive unit tests
- Document code with clear comments
- Apply SOLID principles in class design
Project Scenario
TechCalc Software Inc.
You have been hired as a Software Developer at TechCalc Software, a company specializing in educational and scientific software tools. The company wants to create a flagship calculator application that can be used by students, engineers, and scientists. The calculator must handle complex mathematical expressions and provide a robust, user-friendly experience.
"We need a calculator that goes beyond basic arithmetic. It should handle complex expressions like 'sin(45) + 2^3 * (5 - 2)' correctly, remember previous calculations, and provide helpful error messages. Can you build something that both students and professionals would love to use?"
Functional Requirements
- Addition, subtraction, multiplication, division
- Proper operator precedence (PEMDAS/BODMAS)
- Parentheses support for grouping
- Negative numbers and decimal handling
- Trigonometric: sin, cos, tan, asin, acos, atan
- Logarithmic: log (base 10), ln (natural log)
- Exponential: power (^), square root (sqrt)
- Constants: π (pi), e (Euler's number)
- MC (Memory Clear) - Clear stored value
- MR (Memory Recall) - Retrieve stored value
- M+ (Memory Add) - Add to stored value
- M- (Memory Subtract) - Subtract from stored
- Calculation history with scroll/navigation
- Undo last operation functionality
- Expression validation and error messages
- Unit conversions (degrees/radians)
Test Cases Dataset
Use the comprehensive test case dataset to validate your calculator implementation. The CSV file contains expressions with expected results for automated testing:
Dataset Download
Download the test cases dataset and sample mathematical constants file. Use these to validate your calculator's accuracy and handle edge cases.
Original Data Source
This project uses test cases inspired by the Mathematical Expression Evaluation Dataset from Kaggle - containing diverse mathematical expressions for testing calculator implementations. The dataset covers 500 expressions across 6 difficulty levels with expected outputs.
Dataset Schema
| Column | Type | Description |
|---|---|---|
test_id | Integer | Unique test case identifier (1-500) |
expression | String | Mathematical expression to evaluate |
expected_result | Float | Expected output (or "ERROR" for invalid) |
category | String | basic, intermediate, advanced, scientific, edge_case |
difficulty | Integer | Difficulty level (1-5) |
description | String | Description of what the test validates |
| Column | Type | Description |
|---|---|---|
constant_name | String | Name of constant (pi, e, phi, etc.) |
symbol | String | Symbol used in expressions (π, e, φ) |
value | Float | Numeric value (high precision) |
description | String | Mathematical definition |
| Column | Type | Description |
|---|---|---|
from_unit | String | Source unit (degrees, radians, etc.) |
to_unit | String | Target unit |
conversion_factor | Float | Multiplication factor |
category | String | angle, length, temperature, etc. |
Sample Test Cases Preview
Here are sample test cases from the dataset showing different difficulty levels:
| ID | Expression | Expected | Category | Difficulty |
|---|---|---|---|---|
| 1 | 2 + 3 | 5 | basic | 1 |
| 45 | 3 + 4 * 2 | 11 | intermediate | 2 |
| 150 | (5 + 3) * 2 - 4 / 2 | 14 | advanced | 3 |
| 320 | sin(30) + cos(60) | 1.0 | scientific | 4 |
| 450 | 1 / 0 | ERROR | edge_case | 5 |
Project Requirements
Your project must include all of the following components. Structure your code with clear separation of concerns and comprehensive documentation.
Expression Parser
Parsing Requirements:
- Tokenize input expressions into operators, numbers, and functions
- Implement Shunting-yard algorithm OR recursive descent parser
- Handle operator precedence: () > ^ > * / > + -
- Support both infix notation and function calls
class ExpressionParser {
std::vector<Token> tokenize(const std::string& expr);
double evaluate(const std::string& expr);
bool validate(const std::string& expr);
private:
std::stack<double> values;
std::stack<char> operators;
int getPrecedence(char op);
double applyOp(double a, double b, char op);
};
Mathematical Operations
Operation Classes:
- Create an abstract
Operationbase class - Implement concrete classes for each operation type
- Use polymorphism to handle different operations uniformly
- Support extensibility for adding new operations
Required Operations:
- Arithmetic: +, -, *, /, % (modulo), ^ (power)
- Trigonometric: sin, cos, tan, asin, acos, atan
- Logarithmic: log, ln, log2
- Other: sqrt, abs, floor, ceil, round
Memory System
Memory Functions:
MC- Memory Clear: Reset memory to 0MR- Memory Recall: Return stored valueM+- Memory Add: Add current result to memoryM-- Memory Subtract: Subtract from memoryMS- Memory Store: Store current result
class MemoryManager {
double memory = 0.0;
public:
void clear() { memory = 0.0; }
double recall() const { return memory; }
void add(double value) { memory += value; }
void subtract(double value) { memory -= value; }
void store(double value) { memory = value; }
};
History System
History Features:
- Store all calculations with expression and result
- Navigate through history (up/down arrows)
- Reuse previous expressions
- Implement undo functionality
- Persist history to file (optional)
struct HistoryEntry {
std::string expression;
double result;
std::chrono::time_point<std::chrono::system_clock> timestamp;
};
class HistoryManager {
std::vector<HistoryEntry> history;
int currentIndex = -1;
public:
void add(const std::string& expr, double result);
HistoryEntry* getPrevious();
HistoryEntry* getNext();
void clear();
void saveToFile(const std::string& filename);
};
User Interface
Console Interface:
- Clear, intuitive command prompt
- Help command showing available operations
- Error messages with helpful suggestions
- Mode switching (degrees/radians)
Display Requirements:
- Show current memory status
- Display calculation history
- Format numbers appropriately (precision)
- Color coding for errors (optional)
Feature Specifications
Implement all of the following features. Each feature should be well-tested and handle edge cases appropriately.
| Operator | Example | Result |
|---|---|---|
| Addition (+) | 5 + 3 | 8 |
| Subtraction (-) | 10 - 4 | 6 |
| Multiplication (*) | 6 * 7 | 42 |
| Division (/) | 15 / 3 | 5 |
| Modulo (%) | 17 % 5 | 2 |
| Power (^) | 2 ^ 8 | 256 |
| Function | Example | Result |
|---|---|---|
| sin(x) | sin(30) | 0.5 |
| cos(x) | cos(60) | 0.5 |
| tan(x) | tan(45) | 1.0 |
| sqrt(x) | sqrt(144) | 12 |
| log(x) | log(100) | 2 |
| ln(x) | ln(e) | 1 |
- Division by zero: Return clear error message
- Invalid syntax: Identify position of error
- Mismatched parentheses: Indicate which is missing
- Unknown function: Suggest similar functions
- Domain errors: sqrt(-1), log(-5), etc.
- Overflow/Underflow: Handle large/small numbers
- Constants: pi (π), e, phi (φ)
- Ans: Use previous result in expression
- Variables: Store named values (bonus)
- Degree/Radian mode: Toggle for trig functions
- Precision control: Set decimal places
- Expression history: Arrow key navigation
Sample Implementation
// Operator precedence handling
int getPrecedence(char op) {
switch(op) {
case '+': case '-': return 1;
case '*': case '/': case '%': return 2;
case '^': return 3;
default: return 0;
}
}
// Right associativity for power operator
bool isRightAssociative(char op) {
return op == '^';
}
// Scientific function evaluation
double evalFunction(const std::string& func, double arg) {
if (func == "sin") return std::sin(toRadians(arg));
if (func == "cos") return std::cos(toRadians(arg));
if (func == "tan") return std::tan(toRadians(arg));
if (func == "sqrt") return std::sqrt(arg);
if (func == "log") return std::log10(arg);
if (func == "ln") return std::log(arg);
throw std::runtime_error("Unknown function: " + func);
}
Recommended Code Structure
Organize your code following this modular structure. Each class should have a single responsibility and be well-documented.
calculator-app/
├── include/
│ ├── Calculator.h # Main calculator class
│ ├── Parser.h # Expression tokenizer and parser
│ ├── Evaluator.h # Expression evaluator
│ ├── Operations.h # Operation base class and implementations
│ ├── MemoryManager.h # Memory functions (MC, MR, M+, M-)
│ ├── HistoryManager.h # Calculation history
│ ├── Token.h # Token structure for parsing
│ └── Exceptions.h # Custom exception classes
├── src/
│ ├── main.cpp # Entry point and UI loop
│ ├── Calculator.cpp # Calculator implementation
│ ├── Parser.cpp # Parser implementation
│ ├── Evaluator.cpp # Evaluator implementation
│ ├── Operations.cpp # Operation implementations
│ ├── MemoryManager.cpp # Memory implementation
│ └── HistoryManager.cpp # History implementation
├── tests/
│ ├── test_parser.cpp # Parser unit tests
│ ├── test_evaluator.cpp # Evaluator unit tests
│ ├── test_operations.cpp # Operation unit tests
│ └── test_integration.cpp # Integration tests with CSV data
├── data/
│ └── test_cases.csv # Test cases from dataset
├── Makefile # Build configuration
└── README.md # Project documentation
Class Diagram
Main controller class
Tokenize & parse
Compute results
Store values
Track calculations
Submission Requirements
Create a public GitHub repository with the exact name shown below:
Required Repository Name
cpp-calculator-app
Required Project Structure
cpp-calculator-app/
├── include/ # Header files
│ ├── Calculator.h
│ ├── Parser.h
│ ├── Evaluator.h
│ ├── Operations.h
│ ├── MemoryManager.h
│ └── HistoryManager.h
├── src/ # Source files
│ ├── main.cpp
│ ├── Calculator.cpp
│ ├── Parser.cpp
│ └── ... (other implementations)
├── tests/ # Unit tests
│ └── test_*.cpp
├── data/ # Test data files
│ └── test_cases.csv
├── screenshots/ # Application screenshots
│ ├── basic_operations.png
│ ├── scientific_functions.png
│ └── error_handling.png
├── Makefile # Build configuration
└── README.md # Project documentation
README.md Required Sections
1. Project Header
- Project title and description
- Your full name and submission date
- Course and project number
2. Features
- List of all supported operations
- Scientific functions available
- Memory and history features
3. Building & Running
- Compilation instructions
- Required dependencies
- How to run the application
4. Usage Examples
- Sample expressions and outputs
- Screenshots of the application
- Command reference
5. Architecture
- Class diagram or description
- Design patterns used
- Key algorithms explained
6. Testing
- How to run tests
- Test coverage summary
- Edge cases handled
Do Include
- All source and header files
- Makefile or CMakeLists.txt
- Unit tests for core functionality
- Test data CSV file
- Screenshots of working application
- Clear code comments and documentation
Do Not Include
- Compiled binaries or object files
- IDE-specific project files
- External library source code
- Temporary or backup files
- Personal information in code
Enter your GitHub username - we will verify your repository automatically
Grading Rubric
Your project will be graded on the following criteria. Total: 400 points.
| Criteria | Points | Description |
|---|---|---|
| Expression Parser | 80 | Correct tokenization, operator precedence, parentheses handling |
| Basic Operations | 60 | All arithmetic operations work correctly with proper precision |
| Scientific Functions | 60 | Trig, log, power, sqrt functions with degree/radian modes |
| Memory System | 40 | MC, MR, M+, M-, MS all work correctly |
| History System | 40 | Storage, navigation, undo functionality |
| Error Handling | 40 | Graceful handling of all edge cases with clear messages |
| Code Quality | 40 | Clean code, OOP principles, documentation, tests |
| Documentation | 40 | README quality, code comments, screenshots |
| Total | 400 |
Grading Levels
Excellent
All features work perfectly with bonus features
Good
All required features work correctly
Satisfactory
Core features work with minor issues
Needs Work
Missing key features or major bugs
Ready to Submit?
Make sure you have completed all requirements and reviewed the grading rubric above.
Submit Your ProjectPre-Submission Checklist
Use this checklist to verify you have completed all requirements before submitting your project.