Skip to content

Conversation

@chanderlud
Copy link

@chanderlud chanderlud commented Nov 2, 2025

I worked on this project with @ulises-aguilar

Copilot AI review requested due to automatic review settings November 2, 2025 21:50
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a console-based calculator application with support for basic arithmetic operations (+, -, *, /) using generic types with .NET 9.0. The implementation includes a shunting-yard algorithm to handle operator precedence and supports negative numbers.

Key changes:

  • Created a generic Calculator<TOperand> class with operator precedence support using the shunting-yard algorithm
  • Implemented a Program class with testable WriteLine/ReadLine properties
  • Added comprehensive unit tests for calculator operations and program I/O

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
README.md Updated checklist to mark all requirements as completed
Directory.Build.props Added shared MSBuild properties for .NET 9.0, nullable reference types, and code analysis
Calculate/Program.cs Implemented console program with testable I/O delegates
Calculate/Calculator.cs Created generic calculator with operator precedence and comprehensive expression parsing
Calculate/Calculate.csproj Added project file for console application
Calculate.sln Created solution file with test project reference
Calculate.Tests/ProgramTests.cs Added unit tests for Program I/O redirection
Calculate.Tests/MSTestSettings.cs Configured parallel test execution
Calculate.Tests/CalculatorTests.cs Added comprehensive calculator tests including edge cases
Calculate.Tests/Calculate.Tests.csproj Added test project with MSTest configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

{
WriteLine("Goodbye");
run = false;
} else if (calculator.TryCalculate(equation, out float result))
Copy link

Copilot AI Nov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'else' should be on a new line to maintain consistent formatting with the rest of the codebase. All other closing braces are followed by keywords on new lines.

Suggested change
} else if (calculator.TryCalculate(equation, out float result))
}
else if (calculator.TryCalculate(equation, out float result))

Copilot uses AI. Check for mistakes.
@Hellothereyoko
Copy link

I'm not really seeing much testing that covers program.cs. But technically, this does follow the requirements.

@JosephPotapenko
Copy link

Create a Console project called "Calculate.". ✔
Define a Program Class
Define two init-only setter properties, WriteLine and ReadLine, that contain delegates for writing a line of text and reading a line of text respectively ✔
Write a test that sets these properties at construction time and then invokes the properties and verifies the expected behavior occurs. ✔
Set the default behavior for the WriteLine and ReadLine properties to invoke System.Console versions of the methods and add an empty default constructor. ✔
Define a Calculator class ✔
Define static Add, Subtract, Multiple, and Divide methods that have two parameters and return a third parameter. ✔
Define a read-only property, MathematicalOperations, of type System.Collections.Generics.IReadOnlyDictionary<TKey,TValue> that:
is initialized to a System.Collections.Generics.Dictionary<<TKey,TValue> instance that. ✔
Uses char for the key corresponding to the operators +, -, *, and /. ✔
Has values that correspond with the Add, Subtract, Multiple, and Divide methods. ✔
Implement a TryCalculate method following "TryParse" pattern ✔
Valid calculation expressions include such strings as "3 + 4", "42 - 2", etc. ✔
If there is no whitespace around the operator, you can assume the calculation is invalid and return false. Similarly if the operands are not integers. ✔
Use string.Split(), pattern matching, logical and operators to parse the string in their entirety ✔
Index into the MathematicalOperations method using the operator parsed during pattern matching to find the corresponding implementation and invoke it. ✔
Implement the Program class to instantiate the calculator and invoke it based on user input from the console. ✔
Be sure to use the WriteLine/ReadLine properties on Program for testing the input and output of your program. ✔

Fundamentals
Place all shared project properties into a Directory.Build.props file. ✔
Place all shared project items into a Directory.Build.targets file. (optional)
nullable reference types is enabled ✔
Ensure that you turn on code analysis for all projects(EnableNETAnalyzers) ✔
Set LangVersion and the TargetFramework to the latest released versions available (preview versions optional) ✔
and enabled .NET analyzers for both projects ✔
For this assignment, always use Assert.AreEqual() (the generic version) ✔
All of the above should be unit tested ✔
Choose simplicity over complexity ✔

Extra Credit not done in this repo

@JosephPotapenko
Copy link

The only thing I can imagine can be better is when you're dealing with exit being typed in, but say they accidentally type it in uppercase, and it tells them invalid equation, but it's not technically an equation. You should probably output "invalid input" in to begin with (if other random input is given). If you want, I believe you can ignore the case with:

if (string.Equals(equation, "exit", StringComparison.OrdinalIgnoreCase))

It's kind of messy, but it would be basically the same thing you wrote while ignoring the case of the input. Not exactly necessary, but a little bit more user friendly.

@PigPig-SDK
Copy link

Create a Console project called "Calculate". ✔
Define a Program Class
Define two init-only setter properties, WriteLine and ReadLine, that contain delegates for writing a line of text and reading a line of text respectively ✔
Write a test that sets these properties at construction time and then invokes the properties and verifies the expected behavior occurs. ✔
Set the default behavior for the WriteLine and ReadLine properties to invoke System.Console versions of the methods and add an empty default constructor. ✔
Define a Calculator class ✔
Define static Add, Subtract, Multiply, and Divide methods that have two parameters and return a third parameter. ✔
Define a read-only property, MathematicalOperations, of type System.Collections.Generics.IReadOnlyDictionary<TKey,TValue> that:
is initialized to a System.Collections.Generics.Dictionary<<TKey,TValue> instance that. ✔
Uses char for the key corresponding to the operators +, -, *, and /. ✔
Has values that correspond with the Add, Subtract, Multiply, and Divide methods. ✔
Implement a TryCalculate method following "TryParse" pattern ✔
Valid calculation expressions include such strings as "3 + 4", "42 - 2", etc. ✔
If there is no whitespace around the operator, you can assume the calculation is invalid and return false. Similarly if the operands are not integers. ✔
Use string.Split(), pattern matching, logical and operators to parse the string in their entirety ✔
Index into the MathematicalOperations method using the operator parsed during pattern matching to find the corresponding implementation and invoke it. ✔
Implement the Program class to instantiate the calculator and invoke it based on user input from the console. ✔
Be sure to use the WriteLine/ReadLine properties on Program for testing the input and output of your program. ✔
Fundamentals
Place all shared project properties into a Directory.Build.props file.
Place all shared project items into a Directory.Build.targets file. (optional)
nullable reference types is enabled ✔
Ensure that you turn on code analysis for all projects(EnableNETAnalyzers) ✔
Set LangVersion and the TargetFramework to the latest released versions available (preview versions optional) ✔
and enabled .NET analyzers for both projects ✔
For this assignment, always use Assert.AreEqual() (the generic version) ✔
All of the above should be unit tested ✔
Choose simplicity over complexity ✔

@kellanburns
Copy link

  • Create a Console project called "Calculate". ✔
  • Define a Program Class
    • Define two init-only setter properties, WriteLine and ReadLine, that contain delegates for writing a line of text and reading a line of text respectively ✔
    • Write a test that sets these properties at construction time and then invokes the properties and verifies the expected behavior occurs. ✔
    • Set the default behavior for the WriteLine and ReadLine properties to invoke System.Console versions of the methods and add an empty default constructor. ✔
  • Define a Calculator class ✔
    • Define static Add, Subtract, Multiply, and Divide methods that have two parameters and return a third parameter. ✔
    • Define a read-only property, MathematicalOperations, of type System.Collections.Generics.IReadOnlyDictionary<TKey,TValue> that:
      • is initialized to a System.Collections.Generics.Dictionary<<TKey,TValue> instance that. ✔
        • Uses char for the key corresponding to the operators +, -, *, and /. ✔
        • Has values that correspond with the Add, Subtract, Multiply, and Divide methods. ✔
    • Implement a TryCalculate method following "TryParse" pattern ✔
      • Valid calculation expressions include such strings as "3 + 4", "42 - 2", etc. ✔
      • If there is no whitespace around the operator, you can assume the calculation is invalid and return false. Similarly if the operands are not integers. ✔
      • Use string.Split(), pattern matching, logical and operators to parse the string in their entirety ✔
      • Index into the MathematicalOperations method using the operator parsed during pattern matching to find the corresponding implementation and invoke it. ✔
  • Implement the Program class to instantiate the calculator and invoke it based on user input from the console. ✔
  • Be sure to use the WriteLine/ReadLine properties on Program for testing the input and output of your program. ✔

Extra Credit

Do one of the following two options (or both if you want extra, extra credit) :)

  • Refactor the redirect portion of the Program class into 'ProgramBase` ❌
  • Move ProgramBase into a ConsoleUtilities assembly to be used in other console-based projects ❌
  • Use generics the mathematical operations methods and consider using generic constraints (requires .NET 7.0) ❌

Fundamentals

  • Place all shared project properties into a Directory.Build.props file. ✔
  • Place all shared project items into a Directory.Build.targets file. (optional) ✔
  • nullable reference types is enabled ✔
  • Ensure that you turn on code analysis for all projects(EnableNETAnalyzers) ✔
  • Set LangVersion and the TargetFramework to the latest released versions available (preview versions optional) ✔
  • and enabled .NET analyzers for both projects ✔
  • For this assignment, always use Assert.AreEqual<T>() (the generic version) ✔
  • All of the above should be unit tested ✔
  • Choose simplicity over complexity ✔

@BillMillerCoding
Copy link

Create a Console project called "Calculate.". ✔
Define a Program Class
Define two init-only setter properties, WriteLine and ReadLine, that contain delegates for writing a line of text and reading a line of text respectively ✔
Write a test that sets these properties at construction time and then invokes the properties and verifies the expected behavior occurs. ✔
Set the default behavior for the WriteLine and ReadLine properties to invoke System.Console versions of the methods and add an empty default constructor. ✔
Define a Calculator class ✔
Define static Add, Subtract, Multiple, and Divide methods that have two parameters and return a third parameter. ✔
Define a read-only property, MathematicalOperations, of type System.Collections.Generics.IReadOnlyDictionary<TKey,TValue> that:
is initialized to a System.Collections.Generics.Dictionary<<TKey,TValue> instance that. ✔
Uses char for the key corresponding to the operators +, -, *, and /. ✔
Has values that correspond with the Add, Subtract, Multiple, and Divide methods. ✔
Implement a TryCalculate method following "TryParse" pattern ✔
Valid calculation expressions include such strings as "3 + 4", "42 - 2", etc. ✔
If there is no whitespace around the operator, you can assume the calculation is invalid and return false. Similarly if the operands are not integers. ✔
Use string.Split(), pattern matching, logical and operators to parse the string in their entirety ✔
Index into the MathematicalOperations method using the operator parsed during pattern matching to find the corresponding implementation and invoke it. ✔
Implement the Program class to instantiate the calculator and invoke it based on user input from the console. ✔
Be sure to use the WriteLine/ReadLine properties on Program for testing the input and output of your program. ✔
Extra Credit
Do one of the following two options (or both if you want extra, extra credit) :)

Refactor the redirect portion of the Program class into 'ProgramBase` ✔
Move ProgramBase into a ConsoleUtilities assembly to be used in other console-based projects ✔
Use generics the mathematical operations methods and consider using generic constraints (requires .NET 7.0) ✔
Fundamentals
Place all shared project properties into a Directory.Build.props file. ✔
Place all shared project items into a Directory.Build.targets file. (optional)
nullable reference types is enabled ✔
Ensure that you turn on code analysis for all projects(EnableNETAnalyzers) ✔
Set LangVersion and the TargetFramework to the latest released versions available (preview versions optional) ✔
and enabled .NET analyzers for both projects ✔
For this assignment, always use Assert.AreEqual() (the generic version) ✔
All of the above should be unit tested ✔
Choose simplicity over complexity ✔

@github-actions
Copy link

github-actions bot commented Nov 6, 2025

Summary

Summary
Generated on: 11/06/2025 - 21:58:41
Coverage date: 11/06/2025 - 21:58:39
Parser: MultiReport (2x Cobertura)
Assemblies: 1
Classes: 2
Files: 2
Line coverage: 72.6% (69 of 95)
Covered lines: 69
Uncovered lines: 26
Coverable lines: 95
Total lines: 136
Branch coverage: 75% (30 of 40)
Covered branches: 30
Total branches: 40
Method coverage: Feature is only available for sponsors
Tag: 393_19151082718

Coverage

Calculate - 72.6%
Name Line Branch
Calculate 72.6% 75%
Calculate.Calculator 97.1% 93.7%
Calculate.Program 7.6% 0%

Copy link
Collaborator

@Joshua-Lester3 Joshua-Lester3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instructions

  • Create a Console project called "Calculate". ✔
  • Define a Program Class
    • Define two init-only setter properties, WriteLine and ReadLine, that contain delegates for writing a line of text and reading a line of text respectively ✔
    • Write a test that sets these properties at construction time and then invokes the properties and verifies the expected behavior occurs. ✔
    • Set the default behavior for the WriteLine and ReadLine properties to invoke System.Console versions of the methods and add an empty default constructor. ✔
  • Define a Calculator class ✔
    • Define static Add, Subtract, Multiply, and Divide methods that have two parameters and return a third parameter. ❌ Kind of nitpicky, but this says return a third parameter. So my assumption is there's a third out parameter.
    • Define a read-only property, MathematicalOperations, of type System.Collections.Generics.IReadOnlyDictionary<TKey,TValue> that:
      • is initialized to a System.Collections.Generics.Dictionary<<TKey,TValue> instance that. ✔
        • Uses char for the key corresponding to the operators +, -, *, and /. ✔
        • Has values that correspond with the Add, Subtract, Multiply, and Divide methods. ✔
    • Implement a TryCalculate method following "TryParse" pattern ✔
      • Valid calculation expressions include such strings as "3 + 4", "42 - 2", etc. ✔
      • If there is no whitespace around the operator, you can assume the calculation is invalid and return false. Similarly if the operands are not integers. ✔
      • Use string.Split(), pattern matching, logical and operators to parse the string in their entirety ✔
      • Index into the MathematicalOperations method using the operator parsed during pattern matching to find the corresponding implementation and invoke it. ✔
  • Implement the Program class to instantiate the calculator and invoke it based on user input from the console. ✔
  • Be sure to use the WriteLine/ReadLine properties on Program for testing the input and output of your program. ❌ Program wasn't really tested (at least Main wasn't). I'm assuming these properties were there to help test Program.Main.

Extra Credit

Do one of the following two options (or both if you want extra, extra credit) :)

  • Refactor the redirect portion of the Program class into 'ProgramBase` ❌
    • Move ProgramBase into a ConsoleUtilities assembly to be used in other console-based projects
  • Use generics the mathematical operations methods and consider using generic constraints (requires .NET 7.0) ❌

Fundamentals

  • Place all shared project properties into a Directory.Build.props file.
  • Place all shared project items into a Directory.Build.targets file. (optional)
  • nullable reference types is enabled ✔
  • Ensure that you turn on code analysis for all projects(EnableNETAnalyzers) ✔
  • Set LangVersion and the TargetFramework to the latest released versions available (preview versions optional) ✔
  • and enabled .NET analyzers for both projects ✔
  • For this assignment, always use Assert.AreEqual<T>() (the generic version) ✔
  • All of the above should be unit tested ❌ I'd say it's not fully tested, especially looking at the code coverage report on the PR.
  • Choose simplicity over complexity ✔


<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
Copy link
Collaborator

@Joshua-Lester3 Joshua-Lester3 Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is in the props file, I'd remove it (targetframework) here. If you update props in the future, you'd have to change this as well, since it'd overwrite it.

['/'] = Divide
};

public static int Add(int a, int b) => a + b;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're following requirements very specifically, this would probably look like

public static void Add(int a, int b, out int result) ....


public static int Divide(int a, int b) => a / b;

public bool TryCalculate(string calculation, out int result)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty cool. A little complex and maybe could be refactored a bit because it's a long function. I don't think the requirements asks for this level of complexity (as it only specifies simple examples like "4 + 4", etc.) - though that's not a bad thing.

Comment on lines +14 to +17
Assert.AreEqual<int>(5, ops['+'](2, 3));
Assert.AreEqual<int>(-1, ops['-'](2, 3));
Assert.AreEqual<int>(6, ops['*'](2, 3));
Assert.AreEqual<int>(2, ops['/'](6, 3));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably separate these into multiple tests - there's quite a few ways for this test to fail. It should usually only have one reason to fail.

}

[TestMethod]
public void BasicArithmeticMethods_ValidIntInputs_ExpectedResults()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as above.

Calculator calculator = new();
bool run = true;

while (run)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider that having an infinite loop creates a certain kind of interactive experience like a REPL vs if you did not have the loop the app would exit on each invocation and could be used like in an automated tool call fashion.

int a = evaluationStack.Pop();

// divide by 0 is not allowed
if (token[0] == '/' && b == 0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using the MathematicalOperations division key instead of hard coding this here.

evaluationStack.Push(number);
}
else if (token.Length == 1 && MathematicalOperations.TryGetValue(token[0], out var op))
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the instructions it mentions to "Index into the MathematicalOperations method using the operator parsed during pattern matching to find the corresponding implementation and invoke it."

Copy link

@quattro004 quattro004 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants