Skip to content

Commit

Permalink
Merge pull request #3 from kamendov-maxim/calculator
Browse files Browse the repository at this point in the history
Стековый калькулятор
  • Loading branch information
kamendov-maxim authored Oct 10, 2024
2 parents 0aea769 + c9c462c commit 2ef6d2d
Show file tree
Hide file tree
Showing 9 changed files with 382 additions and 0 deletions.
105 changes: 105 additions & 0 deletions Calculator/Calculator.Src/Calculator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System.Runtime.CompilerServices;
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.FileIO;
using Stack;

namespace CalculatorNamespace;

/// <summary>
/// Класс, содержищий в себе метод для вычисления выражений, записанных в постфиксной записи
/// </summary>
public class Calculator
{
/// <summary>
/// Метод для вычисления выражений, записанных в постфиксной записи
/// </summary>
/// <param name="expression">Выражение в постфиксной записи</param>
/// <param name="stack">Стек, реализующий интерфейс IStack. С помощью него калькулятор будет вычислять значение выражения </param>
/// <returns></returns>
public static Tuple<double, ErrorCode> Evaluate(string expression, IStack stack)
{
if (expression == null || expression == string.Empty)
{
return Tuple.Create(-1.0, ErrorCode.ExpressionIsNull);
}

Array elements = expression.Split(" ");
foreach (string item in elements)
{
if (int.TryParse(item, out int number))
{
stack.Add((double)number);
}
else
{
if (item.Length != 1)
{
return Tuple.Create(-1.0, ErrorCode.WrongOperator);
}
(double number2, bool notEmpty2) = stack.Pop();
if (notEmpty2)
{
(double number1, bool notEmpty1) = stack.Pop();
if (notEmpty1)
{
(double result, ErrorCode ec) = Operation(number1, number2, item[0]);
if (ec != ErrorCode.Ok)
{
return Tuple.Create(-1.0, ec);
}
stack.Add(result);
}

}
}
}
(double answer, bool notEmpty) = stack.Pop();
return Tuple.Create(answer, ErrorCode.Ok);
}

private static Tuple<double, ErrorCode> Operation(double number1, double number2, char op)
{
switch (op)
{
case '+':
{
return Tuple.Create(number1 + number2, ErrorCode.Ok);
}
case '-':
{
return Tuple.Create(number1 - number2, ErrorCode.Ok);
}
case '/':
{
if (Math.Abs(number2) < double.Epsilon)
{
return Tuple.Create(0.0, ErrorCode.DivisionByZero);
}
return Tuple.Create(number1 / number2, ErrorCode.Ok);
}
case '*':
{
return Tuple.Create(number1 * number2, ErrorCode.Ok);
}
default:
return Tuple.Create(-1.0, ErrorCode.WrongOperator);
}
}

private static void ClearStack(IStack stack)
{
while (stack.Size() > 0)
{
stack.Pop();
}
}

public enum ErrorCode
{
Ok,
DivisionByZero,
WrongOperator,
ExpressionIsNull
}

}
10 changes: 10 additions & 0 deletions Calculator/Calculator.Src/Calculator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
26 changes: 26 additions & 0 deletions Calculator/Calculator.Src/InterfaceStack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.VisualBasic;

namespace Stack;

/// <summary>
/// Интерфейс для создания стека, хранящего значения типа double
/// </summary>
public interface IStack
{
/// <summary>
/// Метод для добавления элемента типа double в стек
/// </summary>
/// <param name="element">Элемент, который необходимо добавить</param>
void Add(double element);

/// <summary>
/// Метод для извлечения верхнего элемента из стека
/// </summary>
/// <returns>double - значение верхнего элемента (-1, если его не было) и bool - true, если элемент был в стеке, false, если не было</returns>
Tuple<double, bool> Pop();

/// <summary>
/// Количество элементов в стеке
/// </summary>
int Size();
}
38 changes: 38 additions & 0 deletions Calculator/Calculator.Src/ListStack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Stack;


/// <summary>
/// Реализация IStack с помощью списков
/// </summary>
public class ListStack : IStack
{
public ListStack()
{
this.list = new List<double>();
}

public void Add(double element)
{
this.list.Add(element);
}

public Tuple<double, bool> Pop()
{
double value = -1;
bool notEmpty = false;
if (this.list.Count > 0)
{
notEmpty = true;
value = this.list[this.list.Count - 1];
this.list.RemoveAt(this.list.Count - 1);
}
return Tuple.Create(value, notEmpty);
}

public int Size()
{
return list.Count;
}

private List<double> list;
}
33 changes: 33 additions & 0 deletions Calculator/Calculator.Src/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.ComponentModel.DataAnnotations;
using CalculatorNamespace;
using Stack;

class Program
{

public static void Main()
{
Console.Write("Введите выражение: ");
string? expression = Console.ReadLine();
var listStack = new ListStack();
var stackWithNodes = new StackWithNodes();
(double answer1, Calculator.ErrorCode ec1) = Calculator.Evaluate(expression, listStack);

Check warning on line 14 in Calculator/Calculator.Src/Program.cs

View workflow job for this annotation

GitHub Actions / build-Windows

Possible null reference argument for parameter 'expression' in 'Tuple<double, ErrorCode> Calculator.Evaluate(string expression, IStack stack)'.

Check warning on line 14 in Calculator/Calculator.Src/Program.cs

View workflow job for this annotation

GitHub Actions / build-Windows

Possible null reference argument for parameter 'expression' in 'Tuple<double, ErrorCode> Calculator.Evaluate(string expression, IStack stack)'.

Check warning on line 14 in Calculator/Calculator.Src/Program.cs

View workflow job for this annotation

GitHub Actions / build-Ubuntu_and_MacOs (ubuntu-latest)

Possible null reference argument for parameter 'expression' in 'Tuple<double, ErrorCode> Calculator.Evaluate(string expression, IStack stack)'.

Check warning on line 14 in Calculator/Calculator.Src/Program.cs

View workflow job for this annotation

GitHub Actions / build-Ubuntu_and_MacOs (macos-latest)

Possible null reference argument for parameter 'expression' in 'Tuple<double, ErrorCode> Calculator.Evaluate(string expression, IStack stack)'.
(double answer2, Calculator.ErrorCode ec2) = Calculator.Evaluate(expression, stackWithNodes);
if (ec1 == Calculator.ErrorCode.Ok && ec2 == Calculator.ErrorCode.Ok)
{
Console.WriteLine($"Результат работы при использовании стека на списках: {answer1}\nБез списков: {answer2}");
}
else if (ec1 == Calculator.ErrorCode.DivisionByZero || ec2 == Calculator.ErrorCode.DivisionByZero)
{
Console.WriteLine("Вы не можете делить на ноль");
}
else if (ec1 == Calculator.ErrorCode.WrongOperator || ec2 == Calculator.ErrorCode.WrongOperator)
{
Console.WriteLine("Проверьте правильность ввода");
}
else if (ec1 == Calculator.ErrorCode.ExpressionIsNull || ec2 == Calculator.ErrorCode.ExpressionIsNull)
{
Console.WriteLine("Выражение не может быть пустой строкой");
}
}
}
58 changes: 58 additions & 0 deletions Calculator/Calculator.Src/StackWithNodes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Diagnostics;

namespace Stack;

/// <summary>
/// Реализация IStack с помощью встроенного класса Node
/// </summary>
public class StackWithNodes : IStack
{
private class Node
{
public Node()
{
this.value = 0;
}

public double value;
public Node? next;
}

public StackWithNodes()
{
top = new Node();
}


public void Add(double element)
{
Node newNode = new Node();
newNode.next = this.top;
newNode.value = element;
this.top = newNode;
++this.count;
}

public Tuple<double, bool> Pop()
{
double answer = -1.0;
bool notEmpty = false;
if (this.count > 0)
{
Node next = this.top.next;

Check warning on line 42 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Windows

Converting null literal or possible null value to non-nullable type.

Check warning on line 42 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Windows

Converting null literal or possible null value to non-nullable type.

Check warning on line 42 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Ubuntu_and_MacOs (ubuntu-latest)

Converting null literal or possible null value to non-nullable type.

Check warning on line 42 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Ubuntu_and_MacOs (macos-latest)

Converting null literal or possible null value to non-nullable type.
answer = this.top.value;
this.top = next;

Check warning on line 44 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Windows

Possible null reference assignment.

Check warning on line 44 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Windows

Possible null reference assignment.

Check warning on line 44 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Ubuntu_and_MacOs (ubuntu-latest)

Possible null reference assignment.

Check warning on line 44 in Calculator/Calculator.Src/StackWithNodes.cs

View workflow job for this annotation

GitHub Actions / build-Ubuntu_and_MacOs (macos-latest)

Possible null reference assignment.
notEmpty = true;
--this.count;
}
return Tuple.Create(answer, notEmpty);
}

public int Size()
{
return this.count;
}

public int count { get; private set; }
private Node top;
}
28 changes: 28 additions & 0 deletions Calculator/Calculator.Tests/Calculator.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Calculator.Src\Calculator.csproj" />
</ItemGroup>

</Project>
56 changes: 56 additions & 0 deletions Calculator/Calculator.Tests/TestsForCalculator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
namespace Calculator.Tests;

using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using CalculatorNamespace;
using Stack;

public class Tests
{
public static readonly string[] expressions = ["5 7 +", "5 -7 +", "5 0 +",
"5 25 *", "7 -15 *", "-10 -14 *", "-10 0 *", "6 0 /", "10 2 /", "-5 2 /",
"dfsgfvx", ""];
private static readonly double[] expectedAnswers = [12.0, -2.0, 5.0, 125.0, -105.0,
140.0, 0.0, -1, 5.0, -2.5, -1, -1];
private static readonly Calculator.ErrorCode[] expectedErrorCodes =
[Calculator.ErrorCode.Ok,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.DivisionByZero,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.Ok,
Calculator.ErrorCode.WrongOperator,
Calculator.ErrorCode.ExpressionIsNull];

private static IEnumerable<(string, double, Calculator.ErrorCode, IStack)> InputData
{
get
{
for (int i = 0; i < expressions.Length; ++i)
{
yield return new(expressions[i], expectedAnswers[i], expectedErrorCodes[i], new ListStack());
yield return new(expressions[i], expectedAnswers[i], expectedErrorCodes[i], new StackWithNodes());
}
}
}

[TestCaseSource(nameof(InputData))]
public void CalculationTest((string, double, Calculator.ErrorCode, IStack) inputData)
{
(var expression, var expectedAnswer, var errorCode, var stack) = inputData;
(double answer, Calculator.ErrorCode ec) = Calculator.Evaluate(expression, stack);
Assert.Multiple(() =>
{
Assert.That(ec, Is.EqualTo(errorCode));
if (errorCode == Calculator.ErrorCode.Ok)
{
Assert.That(answer, Is.EqualTo(expectedAnswer).Within(double.Epsilon));
}
});
}
}
28 changes: 28 additions & 0 deletions Calculator/Calculator.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator", "Calculator.Src\Calculator.csproj", "{72690AF6-3603-47BC-8BCC-81F8AA9E2513}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator.Tests", "Calculator.Tests\Calculator.Tests.csproj", "{32426C3E-47BA-4FD3-89F3-F3ECDD3B7773}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{72690AF6-3603-47BC-8BCC-81F8AA9E2513}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72690AF6-3603-47BC-8BCC-81F8AA9E2513}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72690AF6-3603-47BC-8BCC-81F8AA9E2513}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72690AF6-3603-47BC-8BCC-81F8AA9E2513}.Release|Any CPU.Build.0 = Release|Any CPU
{32426C3E-47BA-4FD3-89F3-F3ECDD3B7773}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32426C3E-47BA-4FD3-89F3-F3ECDD3B7773}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32426C3E-47BA-4FD3-89F3-F3ECDD3B7773}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32426C3E-47BA-4FD3-89F3-F3ECDD3B7773}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

0 comments on commit 2ef6d2d

Please sign in to comment.