Skip to content
Closed
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
29 changes: 29 additions & 0 deletions Calculator.Console/AskUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Calculator.Console;
using Console = System.Console;

public static class AskUser
{
public static T Ask<T>(string[] optionTexts) where T : IParsable<T>
{
bool isValidOption = true;
T? result;
do
{
if (!isValidOption)
{
Console.WriteLine("Choose a correct option");
}

foreach (var optionText in optionTexts)
{
Console.WriteLine(optionText);
}

isValidOption = T.TryParse(Console.ReadLine(), null, out result);
} while (!isValidOption);

// isValidOption is true when result get correctly parsed so != null
return result!;
}

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

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

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

</Project>
64 changes: 64 additions & 0 deletions Calculator.Console/Logger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
namespace Calculator.Console;
using System.Diagnostics;

public class Logger : IDisposable
{
private StreamWriter? _logFile;
private TextWriterTraceListener? _listener;
private readonly string _filePath = "Calculator.";

public Logger(string fileName)
{
_filePath += fileName;
InitLogger();
}

private void InitLogger()
{
_logFile = File.CreateText(_filePath);
_listener = new TextWriterTraceListener(_logFile);
Trace.Listeners.Add(_listener);
Trace.AutoFlush = true;
Intro(_filePath);
}

public void Dispose()
{
Trace.WriteLine($"Closing log at {DateTime.Now}");
if (_listener != null)
{
Trace.Listeners.Remove(_listener);
_listener.Dispose();
_listener = null;
}
if (_logFile != null)
{
_logFile.Dispose();
_logFile = null;
}
}

public void Result(double n1, double n2, Library.Operator op, double result)
{
if (_logFile == null)
{
InitLogger();
}
Trace.WriteLine($"{(op.AmountInput() > 1 ? n1 + " " : "")}{op.GetSymbol()} {n2} = {result}");
}

public void DeleteLogs()
{
Dispose();
if (File.Exists(_filePath))
{
File.Delete(_filePath);
}
}

private void Intro(string fileName)
{
Trace.WriteLine($"Starting {fileName} Log");
Trace.WriteLine(String.Format($"Started {DateTime.Now}"));
}
}
9 changes: 9 additions & 0 deletions Calculator.Console/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Calculator.Console;

class Program
{
static void Main(string[] args)
{
UserInterface.Start();
}
}
91 changes: 91 additions & 0 deletions Calculator.Console/UserInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using Calculator.Library;

namespace Calculator.Console;
using Console = System.Console;

public static class UserInterface
{
private static double _num1;
private static double _num2;

// Could add timestamps in the name for persistent logs
// Here we would rather override the file each time
private const string LogName = "calculator.log";
private static readonly Logger Logs = new Logger(LogName);
private static readonly JsonLogger JsonLogs = new JsonLogger();

public static void Start()
{
bool endApp = false;
Intro();

while (!endApp)
{
_num1 = AskUserANumber();
Library.Operator op = AskUserOperand();
// For some operation, only one number is needed, take the value of num1 for easier print
_num2 = (op.AmountInput() > 1) ? AskUserANumber("second") : _num1;

try
{
PrintResult(op);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
endApp = AskCloseApp();
}

Logs.Dispose();
JsonLogs.Dispose();
}

private static void Intro()
{
// Display title as the C# console calculator app.
Console.WriteLine("Console Calculator in C#\r");
Console.WriteLine("------------------------\n");
}

private static double AskUserANumber(string amount = "first")
{
return AskUser.Ask<double>(
[$"Type the {amount} number, and press ENTER"]);
}

private static Library.Operator AskUserOperand()
{
return AskUser.Ask<Library.Operator>(
["Choose an option from the following list",
"\t+ - Add",
"\t- - Subtract",
"\t* - Multiply",
"\t/ - Divide",
"\tsqrt - Square root",
"\t^ - Power",
"\tcos - Cosinus",
"\tsin - Sinus"]);
}

private static void PrintResult(Library.Operator op)
{
double result = op.Result(_num1, _num2);
Console.WriteLine($"Your result: {(op.AmountInput() > 1 ? _num1 : "")} {op.GetSymbol()} {_num2} = {result}");
Logs.Result(_num1, _num2, op, result);
JsonLogs.LogResult(_num1, _num2, op, result);
}

private static bool AskCloseApp()
{
Console.WriteLine("Press 'n' and Enter to close the app, or press 'd' and Enter to delete the logs or press any other key and Enter to continue: ");
string? input = Console.ReadLine();
if (input == "d")
{
Logs.DeleteLogs();
JsonLogs.DeleteLogs();
return AskCloseApp();
}
return (input == "n");
}
}
13 changes: 13 additions & 0 deletions Calculator.Library/Calculator.Library.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="14.0.1-beta2" />
</ItemGroup>

</Project>
66 changes: 66 additions & 0 deletions Calculator.Library/JsonLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Newtonsoft.Json;

namespace Calculator.Library;

public class JsonLogger : IDisposable
{
private JsonWriter? _writer;
private const string FilePath = "CalculatorLog.json";

public JsonLogger()
{
InitLogger();
}

private void InitLogger()
{
var logFile = File.CreateText(FilePath);
logFile.AutoFlush = true;
_writer = new JsonTextWriter(logFile);
_writer.Formatting = Formatting.Indented;
_writer.WriteStartObject();
_writer.WritePropertyName("Operations");
_writer.WriteStartArray();
}

public void LogResult(double n1, double n2, Operator op, double result)
{
if (_writer == null)
{
InitLogger();
}
_writer!.WriteStartObject();
_writer.WritePropertyName("Operand1");
_writer.WriteValue(n1);
if (op.AmountInput() > 1)
{
_writer.WritePropertyName("Operand2");
_writer.WriteValue(n2);
}
_writer.WritePropertyName("Operation");
_writer.WriteValue(op.GetSymbol());
_writer.WritePropertyName("Result");
_writer.WriteValue(result);
_writer.WriteEndObject();
}

public void DeleteLogs()
{
Dispose();
if (File.Exists(FilePath))
{
File.Delete(FilePath);
}
}

public void Dispose()
{
if (_writer == null) return;

_writer.WriteEndArray();
_writer.WriteEndObject();
_writer.Close();
((IDisposable)_writer).Dispose();
_writer = null;
}
}
96 changes: 96 additions & 0 deletions Calculator.Library/Operand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
namespace Calculator.Library;

using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;


public enum Operand
{
Addition,
Subtraction,
Multiplication,
Division,
SquareRoot,
Power,
Cos,
Sin
}

public class Operator : IParsable<Operator>
{
private readonly Operand? _operand;

public Operator(string? input)
{
_operand = input?.ToUpper().Trim() switch
{
"+" => Operand.Addition,
"-" => Operand.Subtraction,
"*" => Operand.Multiplication,
"/" => Operand.Division,
"SQRT" => Operand.SquareRoot,
"^" => Operand.Power,
"COS" => Operand.Cos,
"SIN" => Operand.Sin,
_ => null
};
}

public double Result(double a, double b) => _operand switch
{
Operand.Addition => a + b,
Operand.Subtraction => a! - b,
Operand.Multiplication => a * b,
Operand.Division => b != 0 ? a / b : throw new DivideByZeroException("Can't divide by zero"),
Operand.SquareRoot => b >= 0 ? Math.Sqrt(b) : throw new Exception("Square root of negative number isn't a real number"),
Operand.Power => (b % 0 ==0 || a >= 0) ? Math.Pow(a, b) : throw new Exception("Power of negative number with fractional exponent isn't a real number"),
Operand.Cos => Math.Cos(b),
Operand.Sin => Math.Sin(b),
_ => throw new InvalidEnumArgumentException("Operand not recognized")
};

public string GetSymbol() => _operand switch
{
Operand.Addition => "+",
Operand.Subtraction => "-",
Operand.Multiplication => "*",
Operand.Division => "/",
Operand.SquareRoot => "SQRT",
Operand.Power => "^",
Operand.Cos => "cos",
Operand.Sin => "sin",
_ => throw new InvalidEnumArgumentException("Operand not recognized")
};

public int AmountInput()
{
switch (_operand)
{
case Operand.SquareRoot:
case Operand.Cos:
case Operand.Sin:
return 1;
default:
return 2;
}
}

public static Operator Parse(string s, IFormatProvider? provider)
{
return TryParse(s, provider, out var result) ? result : throw new FormatException("Not a valid input Operator");
}

public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Operator result)
{
var op = new Operator(s);

if (op._operand == null)
{
result = null;
return false;
}

result = op;
return true;
}
}
4 changes: 4 additions & 0 deletions Calculator.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<Solution>
<Project Path="Calculator.Console/Calculator.Console.csproj" />
<Project Path="Calculator.Library/Calculator.Library.csproj" />
</Solution>