From 2a3bd92b9524c30a883e193e5188e9841cbd7389 Mon Sep 17 00:00:00 2001 From: Arseny Date: Thu, 3 Sep 2026 19:16:12 +0300 Subject: [PATCH] Add calculator project --- Calculator.sln | 22 ++ CalculatorLibrary/Calculator.cs | 84 ++++++ CalculatorLibrary/CalculatorLibrary.csproj | 13 + CalculatorLibrary/Models/Calculation.cs | 9 + .../Models/CalculationHistory.cs | 6 + TCSA.Calculator/Calculator.csproj | 18 ++ TCSA.Calculator/Program.cs | 260 ++++++++++++++++++ 7 files changed, 412 insertions(+) create mode 100644 Calculator.sln create mode 100644 CalculatorLibrary/Calculator.cs create mode 100644 CalculatorLibrary/CalculatorLibrary.csproj create mode 100644 CalculatorLibrary/Models/Calculation.cs create mode 100644 CalculatorLibrary/Models/CalculationHistory.cs create mode 100644 TCSA.Calculator/Calculator.csproj create mode 100644 TCSA.Calculator/Program.cs diff --git a/Calculator.sln b/Calculator.sln new file mode 100644 index 00000000..238e6ffb --- /dev/null +++ b/Calculator.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Calculator", "TCSA.Calculator\Calculator.csproj", "{8235EA08-A7BE-4654-99D9-F96EB4884C06}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CalculatorLibrary", "CalculatorLibrary\CalculatorLibrary.csproj", "{4639BC5A-00B5-4014-90BE-A04CEF4D0211}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8235EA08-A7BE-4654-99D9-F96EB4884C06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8235EA08-A7BE-4654-99D9-F96EB4884C06}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8235EA08-A7BE-4654-99D9-F96EB4884C06}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8235EA08-A7BE-4654-99D9-F96EB4884C06}.Release|Any CPU.Build.0 = Release|Any CPU + {4639BC5A-00B5-4014-90BE-A04CEF4D0211}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4639BC5A-00B5-4014-90BE-A04CEF4D0211}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4639BC5A-00B5-4014-90BE-A04CEF4D0211}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4639BC5A-00B5-4014-90BE-A04CEF4D0211}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/CalculatorLibrary/Calculator.cs b/CalculatorLibrary/Calculator.cs new file mode 100644 index 00000000..e5d64b7b --- /dev/null +++ b/CalculatorLibrary/Calculator.cs @@ -0,0 +1,84 @@ +using CalculatorLibrary.Models; +using Newtonsoft.Json; + +namespace CalculatorLibrary; + +public class Calculator +{ + public double DoOperation(string op, double num1, double num2 = 0) + { + double + result = double.NaN; // Default value is "not-a-number" if an operation, such as division, could result in an error. + + // Use a switch statement to do the math. + switch (op) + { + case "a": + result = num1 + num2; + break; + case "s": + result = num1 - num2; + break; + case "m": + result = num1 * num2; + break; + case "d": + if (num2 != 0) + { + result = num1 / num2; + } + break; + case "p": + result = Math.Pow(num1, num2); + break; + case "r": + result = Math.Sqrt(num1); + break; + case "x": + result = num1 * 10; + break; + case "sin": + double radians = num1 * Math.PI / 180; + result = Math.Sin(radians); + break; + case "cos": + radians = num1 * Math.PI / 180; + result = Math.Cos(radians); + break; + case "tan": + radians = num1 * Math.PI / 180; + result = Math.Tan(radians); + break; + } + + Calculation calculation = new Calculation(); + calculation.Operand1 = num1; + calculation.Operand2 = num2; + calculation.Operation = op; + calculation.Result = result; + + var history = GetHistory(); + history.Operations.Add(calculation); + string json = JsonConvert.SerializeObject(history); + File.WriteAllText("calculationlog.json", json); + + return result; + } + + public CalculationHistory GetHistory() + { + var historyObject = new CalculationHistory(); + if (File.Exists("calculationlog.json")) + { + using StreamReader streamReader = new StreamReader("calculationlog.json"); + var history = streamReader.ReadToEnd(); + historyObject = JsonConvert.DeserializeObject(history); + } + return historyObject; + } + + public void ClearHistory() + { + File.Delete("calculationlog.json"); + } +} \ No newline at end of file diff --git a/CalculatorLibrary/CalculatorLibrary.csproj b/CalculatorLibrary/CalculatorLibrary.csproj new file mode 100644 index 00000000..b05421ed --- /dev/null +++ b/CalculatorLibrary/CalculatorLibrary.csproj @@ -0,0 +1,13 @@ + + + + net10.0 + enable + enable + + + + + + + diff --git a/CalculatorLibrary/Models/Calculation.cs b/CalculatorLibrary/Models/Calculation.cs new file mode 100644 index 00000000..12db5ead --- /dev/null +++ b/CalculatorLibrary/Models/Calculation.cs @@ -0,0 +1,9 @@ +namespace CalculatorLibrary.Models; + +public class Calculation +{ + public double Operand1 { get; set; } + public double Operand2 { get; set; } + public string Operation { get; set; } + public double Result { get; set; } +} \ No newline at end of file diff --git a/CalculatorLibrary/Models/CalculationHistory.cs b/CalculatorLibrary/Models/CalculationHistory.cs new file mode 100644 index 00000000..3c03093d --- /dev/null +++ b/CalculatorLibrary/Models/CalculationHistory.cs @@ -0,0 +1,6 @@ +namespace CalculatorLibrary.Models; + +public class CalculationHistory +{ + public List Operations { get; set; } = new List(); +} \ No newline at end of file diff --git a/TCSA.Calculator/Calculator.csproj b/TCSA.Calculator/Calculator.csproj new file mode 100644 index 00000000..13dd0a80 --- /dev/null +++ b/TCSA.Calculator/Calculator.csproj @@ -0,0 +1,18 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + diff --git a/TCSA.Calculator/Program.cs b/TCSA.Calculator/Program.cs new file mode 100644 index 00000000..cd70d49c --- /dev/null +++ b/TCSA.Calculator/Program.cs @@ -0,0 +1,260 @@ +using CalculatorLibrary; +using System.Text.RegularExpressions; +using CalculatorLibrary.Models; +using Spectre.Console; + +namespace CalculatorProgram; + +class Program +{ + static void Main(string[] args) + { + Calculator calculator = new Calculator(); + Menu(calculator); + } + + + static void Menu(Calculator calculator) + { + int calculationCount = 0; + bool endApp = false; + while (!endApp) + { + AnsiConsole.Clear(); + + string menuOp = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("[yellow]Choose an option:[/]") + .AddChoices( + "Calculate", + "View history", + "Clear history", + "Exit")); + + switch (menuOp) + { + case "Calculate": + AnsiConsole.Clear(); + + bool isCalculated = Calculate(calculator); + + if (isCalculated) calculationCount++; + + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("[grey]Press Enter to return to the menu...[/]"); + Console.ReadLine(); + break; + + case "View history": + AnsiConsole.Clear(); + + PrintHistory(calculator.GetHistory()); + + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("[grey]Press Enter to return to the menu...[/]"); + Console.ReadLine(); + break; + + case "Clear history": + calculator.ClearHistory(); + + AnsiConsole.Clear(); + AnsiConsole.MarkupLine("[green]History cleared.[/]"); + + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("[grey]Press Enter to return to the menu...[/]"); + Console.ReadLine(); + break; + + case "Exit": + endApp = true; + break; + } + } + + AnsiConsole.Clear(); + AnsiConsole.MarkupLine( + $"[green]Calculator uses this session: {calculationCount}[/]"); + } + + public static bool Calculate(Calculator calculator) + { + bool isCalculated = false; + double result; + + // Ask the user to choose an operator. + string operation = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("[yellow]Choose an operation:[/]") + .AddChoices( + "Add", + "Subtract", + "Multiply", + "Divide", + "Power", + "Sqrt", + "x10", + "Sin", + "Cos", + "Tan")); + + string op = operation switch + { + "Add" => "a", + "Subtract" => "s", + "Multiply" => "m", + "Divide" => "d", + "Power" => "p", + "Sqrt" => "r", + "x10" => "x", + "Sin" => "sin", + "Cos" => "cos", + "Tan" => "tan", + _ => throw new InvalidOperationException("Unknown operation.") + }; + + + // Ask the user to type the first number. + double cleanNum1 = GetNumberFromUser(calculator, "First number"); + double cleanNum2 = 0; + + if (!Regex.IsMatch(op, "^(r|x|sin|cos|tan)$")) + { + // Ask the user to type the second number. + cleanNum2 = GetNumberFromUser(calculator, "Second number"); + } + + try + { + result = calculator.DoOperation(op, cleanNum1, cleanNum2); + isCalculated = true; + if (double.IsNaN(result)) + { + AnsiConsole.MarkupLine("[red]This operation will result in a mathematical error.[/]"); + } + else + { + AnsiConsole.MarkupLine($"[green]Your result: {result:0.##}[/]"); + } + } + catch (Exception e) + { + AnsiConsole.MarkupLine($"[red]Oh no! An exception occurred trying to do the math.[/]"); + AnsiConsole.MarkupLine($"[grey]Details: {e.Message}[/]"); + } + + return isCalculated; + } + + public static double GetNumberFromUser(Calculator calculator, string input) + { + while (true) + { + string inputChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title($"[yellow]{input}[/]") + .AddChoices( + "Enter a new number", + "Use result from history")); + + switch (inputChoice) + { + case "Enter a new number": + return AnsiConsole.Prompt( + new TextPrompt("Type a number:")); + + case "Use result from history": + var history = calculator.GetHistory(); + + if (history.Operations.Count == 0) + { + AnsiConsole.MarkupLine("[red]No operations found.[/]"); + continue; + } + + var historyChoices = new List(); + + foreach (var calculation in history.Operations) + { + historyChoices.Add(FormatCalculation(calculation)); + } + + string selectedCalculation = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("[yellow]Choose a calculation:[/]") + .AddChoices(historyChoices)); + + int selectedIndex = historyChoices.IndexOf(selectedCalculation); + + return history.Operations[selectedIndex].Result; + } + } + } + + public static void PrintHistory(CalculationHistory history) + { + if (history.Operations.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No operations found.[/]"); + return; + } + + var table = new Table(); + + table.AddColumn("#"); + table.AddColumn("Calculation"); + + for (int i = 0; i < history.Operations.Count; i++) + { + table.AddRow( + (i + 1).ToString(), + FormatCalculation(history.Operations[i])); + } + + AnsiConsole.Write(table); + } + + public static string FormatCalculation(Calculation calculation) + { + string formattedCalculation; + + switch (calculation.Operation) + { + case "a": + formattedCalculation = ($"{calculation.Operand1} + {calculation.Operand2} = {calculation.Result}"); + break; + case "s": + formattedCalculation = ($"{calculation.Operand1} - {calculation.Operand2} = {calculation.Result}"); + break; + case "m": + formattedCalculation = ($"{calculation.Operand1} * {calculation.Operand2} = {calculation.Result}"); + break; + case "d": + formattedCalculation = ($"{calculation.Operand1} / {calculation.Operand2} = {calculation.Result}"); + break; + case "p": + formattedCalculation = ($"{calculation.Operand1} ^ {calculation.Operand2} = {calculation.Result}"); + break; + case "r": + formattedCalculation = ($"√{calculation.Operand1} = {calculation.Result}"); + break; + case "x": + formattedCalculation = ($"{calculation.Operand1} × 10 = {calculation.Result}"); + break; + case "sin": + formattedCalculation = ($"sin({calculation.Operand1}) = {calculation.Result}"); + break; + case "cos": + formattedCalculation = ($"cos({calculation.Operand1}) = {calculation.Result}"); + break; + case "tan": + formattedCalculation = ($"tan({calculation.Operand1}) = {calculation.Result}"); + break; + default: + formattedCalculation = "Invalid option."; + break; + } + + return formattedCalculation; + } +} \ No newline at end of file