diff --git a/.idea/.idea.calculator/.idea/git_toolbox_prj.xml b/.idea/.idea.calculator/.idea/git_toolbox_prj.xml new file mode 100644 index 0000000..02b915b --- /dev/null +++ b/.idea/.idea.calculator/.idea/git_toolbox_prj.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/Commands/CalculateCommand.cs b/Commands/CalculateCommand.cs deleted file mode 100644 index 782282f..0000000 --- a/Commands/CalculateCommand.cs +++ /dev/null @@ -1,38 +0,0 @@ -using calculator.Common; -using calculator.Model; -using calculator.Services; - -namespace calculator.Commands; - -public class CalculateCommand : ICommand -{ - private readonly ICaculator _caculator; - private readonly CaculatorData _data; - private readonly double? _b; - private readonly Operation _operation; - private double? _previousValue; - - public CalculateCommand(ICaculator caculator, CaculatorData data) - { - _caculator = caculator; - _data = data; - _operation = data.Operation.Value; - - if (double.TryParse(data.Input, out var b)) - _b = b; - } - - public void Execute() - { - if (_data.Value is null) - return; - _previousValue = _data.Value; - _data.Value = _caculator.Calculate(_operation, _data.Value.Value, _b); - _data.Input = null; - } - - public void Undo() - { - _data.Value = _previousValue; - } -} \ No newline at end of file diff --git a/Common/CalculatorExtensions.cs b/Common/CalculatorExtensions.cs new file mode 100644 index 0000000..d3d9765 --- /dev/null +++ b/Common/CalculatorExtensions.cs @@ -0,0 +1,54 @@ +namespace calculator.Common; + +public static class CalculatorExtensions +{ + public static int Precedence(this Operation op) + { + switch (op) + { + case Operation.Add: + case Operation.Sub: + return 1; + case Operation.Mul: + case Operation.Div: + case Operation.Factorial: + return 2; + case Operation.PowY: + case Operation.SqrtY: + return 3; + case Operation.Cube: + case Operation.Pow: + case Operation.Sqrt: + case Operation.CubeRoot: + case Operation.Square: + case Operation.Log: + case Operation.Ln: + case Operation.Inv: + case Operation.OneDiv: + case Operation.Mod: + case Operation.Neg: + case Operation.Percent: + case Operation.Sinh: + case Operation.Sin: + case Operation.Cosh: + case Operation.Cos: + case Operation.Tanh: + case Operation.Tan: + return 4; + default: + case Operation.OpenBracket: + case Operation.CloseBracket: + return 0; + } + } + + public static bool IsBigger(this Operation a, Operation b) + { + return Precedence(a) > Precedence(b); + } + + public static bool IsLowerOrEqual(this Operation a, Operation b) + { + return Precedence(a) <= Precedence(b); + } +} \ No newline at end of file diff --git a/Common/Operation.cs b/Common/Operation.cs index b969da4..37a490e 100644 --- a/Common/Operation.cs +++ b/Common/Operation.cs @@ -18,7 +18,6 @@ public enum Operation Ln, Exp, Inv, - Pi, OneDiv, Mod, Neg, @@ -28,5 +27,16 @@ public enum Operation Cosh, Cos, Tanh, - Tan + Tan, + OpenBracket, + CloseBracket, + Int, + Dms, + FE, + PowX +} +public enum Constants +{ + Pi, + Exp } \ No newline at end of file diff --git a/Controller/CaculatorController.cs b/Controller/CaculatorController.cs index 612b414..ed35880 100644 --- a/Controller/CaculatorController.cs +++ b/Controller/CaculatorController.cs @@ -1,8 +1,8 @@ -using calculator.Commands; -using calculator.Common; +using calculator.Common; using calculator.Model; using calculator.Services; using calculator.View; +using System.Runtime.InteropServices; namespace calculator.Controller; @@ -12,7 +12,6 @@ public class CaculatorController private readonly IInputService _inputService; private readonly ICaculatorView _view; private readonly CaculatorData _data; - private ICommand? _currentCommand; public CaculatorController(ICaculator caculator, IInputService inputService, ICaculatorView view, CaculatorData data) @@ -27,14 +26,24 @@ public class CaculatorController _view.ClearPressed += OnClearPressed; _view.CalculatePressed += OnCalculatePressed; _view.SingleOperatorPressed += OnSingleOperationPressed; + _view.Constants += OnConstantsPressed; OnClearPressed(true); } private void OnSingleOperationPressed(Operation operation) { - OnOperatorPressed(operation); - _currentCommand = new CalculateCommand(_caculator, _data); - OnCalculatePressed(); + if (double.TryParse(_data.Input, out var value) == false) + { + if (_data.Values.Count > 0) + { + value = _data.Values.Pop()!.Value; + } + else { return; } + } + + _data.Values.Push(_caculator.Calculate(operation, value)); + UpdateView(_data.Values.Peek()!.Value); + _data.Input = null; } private void OnClearPressed(bool full) @@ -42,60 +51,104 @@ public class CaculatorController Clear(full: full); } + private void Calculate(bool fullAnswer = true) + { + if (_data.Operations.Count == 0) + return; + + if (_data.Values.Count == 0) + return; + + if (TryGetValueFromInput() == false) + return; + + PerformCalculation(fullAnswer); + + UpdateView(_data.Values.Peek()!.Value); + } + private void OnCalculatePressed() { - if (_data.Operation is null) - return; + Calculate(); + } - if (_data.Value is null) - return; - - if (_data is not { Caculated: true, Input: null } || _currentCommand == null) + private void PerformCalculation(bool fullAnswer = true) + { + while (true) { - _currentCommand = new CalculateCommand(_caculator, _data); + while (_data.Operations.Count > 0 && _data.Operations.Peek() != Operation.OpenBracket) + { + var b = _data.Values.Pop(); + var a = _data.Values.Pop(); + _data.Values.Push(_caculator.Calculate(_data.Operations.Pop(), a!.Value, b!.Value)); + } + + if (_data.Operations.Count > 0 && _data.Operations.Peek() == Operation.OpenBracket && fullAnswer) + { + _data.Operations.Pop(); + } + + if (_data.Operations.Count > 0 && fullAnswer) + { + continue; + } + + break; } - - _currentCommand?.Execute(); - _data.Caculated = true; - UpdateView(_data.Value.Value); } private void Clear(string value = "0", bool full = true) { if (full) { - _data.Value = null; - _data.Operation = null; + _data.Values.Clear(); + _data.Operations.Clear(); } - - _data.Caculated = false; + _data.Input = null; _view.UpdateView(value); } private void OnOperatorPressed(Operation operation) { - if (_data.Input == null && _data.Value == null) - return; - - if (_data is { Caculated: false, Input: not null, Value: not null }) + if (_data.Operations.Count > 0) { - OnCalculatePressed(); + if (operation == Operation.CloseBracket) + { + Calculate(false); + return; + } + + if (operation != Operation.OpenBracket && operation.IsLowerOrEqual(_data.Operations.Peek())) + { + Calculate(false); + } } - _data.Operation = operation; + _data.Operations.Push(operation); - if (_data.Input != null) + TryGetValueFromInput(); + } + + private bool TryGetValueFromInput() + { + if (_data.Input is not null) { - _data.Value = double.Parse(_data.Input); + if (double.TryParse(_data.Input, out var value) == false) + { + return false; + } + + _data.Values.Push(value); } _data.Input = null; + return true; } private void OnOperandPressed(char obj) { - if (_data.Caculated) + if (_data.Operations.Count == 0 && _data.Input == null) { Clear(full: true); } @@ -113,4 +166,26 @@ public class CaculatorController { _view.UpdateView(input); } -} \ No newline at end of file + + private void OnConstantsPressed(Constants constants) + { + if (_data.Operations.Count == 0 && _data.Input == null) + { + Clear(full: true); + } + + double output = 0; + switch (constants) + { + case Constants.Pi: + output = Math.PI; + break; + case Constants.Exp: + output = Math.E; + break; + } + _data.Input = output.ToString("0.#####"); + UpdateView(output); + } +} + diff --git a/Form1.Designer.cs b/Form1.Designer.cs index 53bd2c3..5af0b11 100644 --- a/Form1.Designer.cs +++ b/Form1.Designer.cs @@ -29,7 +29,7 @@ private void InitializeComponent() { panel1 = new Panel(); - button49 = new Button(); + PowX = new Button(); Log_button = new Button(); Mod_button = new Button(); Exp_button = new Button(); @@ -97,7 +97,7 @@ // panel1 // panel1.BackColor = Color.MediumSlateBlue; - panel1.Controls.Add(button49); + panel1.Controls.Add(PowX); panel1.Controls.Add(Log_button); panel1.Controls.Add(Mod_button); panel1.Controls.Add(Exp_button); @@ -157,16 +157,17 @@ panel1.Size = new Size(573, 338); panel1.TabIndex = 0; // - // button49 + // PowX // - button49.FlatStyle = FlatStyle.Flat; - button49.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); - button49.Location = new Point(232, 296); - button49.Name = "button49"; - button49.Size = new Size(51, 37); - button49.TabIndex = 55; - button49.Text = "10ᵜ"; - button49.UseVisualStyleBackColor = true; + PowX.FlatStyle = FlatStyle.Flat; + PowX.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + PowX.Location = new Point(232, 296); + PowX.Name = "PowX"; + PowX.Size = new Size(51, 37); + PowX.TabIndex = 55; + PowX.Text = "10ᵜ"; + PowX.UseVisualStyleBackColor = true; + PowX.Click += OnPowXButtonClick; // // Log_button // @@ -206,8 +207,11 @@ // // button53 // + button53.Cursor = Cursors.No; + button53.Enabled = false; button53.FlatStyle = FlatStyle.Flat; button53.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + button53.ForeColor = Color.Tomato; button53.Location = new Point(4, 296); button53.Name = "button53"; button53.Size = new Size(51, 37); @@ -325,8 +329,12 @@ // // dms_button // + dms_button.BackgroundImageLayout = ImageLayout.None; + dms_button.Cursor = Cursors.No; + dms_button.Enabled = false; dms_button.FlatStyle = FlatStyle.Flat; dms_button.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + dms_button.ForeColor = Color.Tomato; dms_button.Location = new Point(4, 210); dms_button.Name = "dms_button"; dms_button.Size = new Size(51, 37); @@ -392,6 +400,7 @@ Int_button.TabIndex = 36; Int_button.Text = "Int"; Int_button.UseVisualStyleBackColor = true; + Int_button.Click += OnIntButtonClick; // // button23 // @@ -403,6 +412,7 @@ button23.TabIndex = 35; button23.Text = ")"; button23.UseVisualStyleBackColor = true; + button23.Click += OnCloseBracketButtonClick; // // button30 // @@ -414,6 +424,7 @@ button30.TabIndex = 34; button30.Text = "("; button30.UseVisualStyleBackColor = true; + button30.Click += OnOpenBracketButtonClick; // // Ln_button // @@ -729,8 +740,11 @@ // // button5 // + button5.Cursor = Cursors.No; + button5.Enabled = false; button5.FlatStyle = FlatStyle.Flat; button5.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + button5.ForeColor = Color.Tomato; button5.Location = new Point(517, 81); button5.Name = "button5"; button5.Size = new Size(51, 37); @@ -740,8 +754,11 @@ // // button4 // + button4.Cursor = Cursors.No; + button4.Enabled = false; button4.FlatStyle = FlatStyle.Flat; button4.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + button4.ForeColor = Color.Tomato; button4.Location = new Point(460, 81); button4.Name = "button4"; button4.Size = new Size(51, 37); @@ -751,8 +768,11 @@ // // button3 // + button3.Cursor = Cursors.No; + button3.Enabled = false; button3.FlatStyle = FlatStyle.Flat; button3.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + button3.ForeColor = Color.Tomato; button3.Location = new Point(403, 81); button3.Name = "button3"; button3.Size = new Size(51, 37); @@ -762,8 +782,11 @@ // // button2 // + button2.Cursor = Cursors.No; + button2.Enabled = false; button2.FlatStyle = FlatStyle.Flat; button2.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + button2.ForeColor = Color.Tomato; button2.Location = new Point(346, 81); button2.Name = "button2"; button2.Size = new Size(51, 37); @@ -773,8 +796,11 @@ // // button1 // + button1.Cursor = Cursors.No; + button1.Enabled = false; button1.FlatStyle = FlatStyle.Flat; button1.Font = new Font("Segoe UI", 9.75F, FontStyle.Bold, GraphicsUnit.Point); + button1.ForeColor = Color.Tomato; button1.Location = new Point(289, 81); button1.Name = "button1"; button1.Size = new Size(51, 37); @@ -796,8 +822,11 @@ // radioButton3 // radioButton3.AutoSize = true; + radioButton3.BackgroundImageLayout = ImageLayout.None; + radioButton3.Cursor = Cursors.No; + radioButton3.Enabled = false; radioButton3.Font = new Font("Microsoft Sans Serif", 11.25F, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Point); - radioButton3.ForeColor = Color.FromArgb(255, 192, 255); + radioButton3.ForeColor = Color.Tomato; radioButton3.Location = new Point(197, 13); radioButton3.Name = "radioButton3"; radioButton3.Size = new Size(76, 22); @@ -809,8 +838,11 @@ // radioButton2 // radioButton2.AutoSize = true; + radioButton2.BackgroundImageLayout = ImageLayout.None; + radioButton2.Cursor = Cursors.No; + radioButton2.Enabled = false; radioButton2.Font = new Font("Microsoft Sans Serif", 11.25F, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Point); - radioButton2.ForeColor = Color.FromArgb(255, 192, 255); + radioButton2.ForeColor = Color.Tomato; radioButton2.Location = new Point(98, 13); radioButton2.Name = "radioButton2"; radioButton2.Size = new Size(95, 22); @@ -822,8 +854,11 @@ // radioButton1 // radioButton1.AutoSize = true; + radioButton1.BackgroundImageLayout = ImageLayout.None; + radioButton1.Cursor = Cursors.No; + radioButton1.Enabled = false; radioButton1.Font = new Font("Microsoft Sans Serif", 11.25F, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Point); - radioButton1.ForeColor = Color.FromArgb(255, 192, 255); + radioButton1.ForeColor = Color.Tomato; radioButton1.Location = new Point(6, 13); radioButton1.Name = "radioButton1"; radioButton1.Size = new Size(93, 22); @@ -848,6 +883,7 @@ // linkLabel1 // linkLabel1.AutoSize = true; + linkLabel1.Enabled = false; linkLabel1.LinkColor = Color.FromArgb(255, 192, 255); linkLabel1.Location = new Point(16, 6); linkLabel1.Name = "linkLabel1"; @@ -859,6 +895,7 @@ // linkLabel2 // linkLabel2.AutoSize = true; + linkLabel2.Enabled = false; linkLabel2.LinkColor = Color.FromArgb(255, 192, 255); linkLabel2.Location = new Point(49, 6); linkLabel2.Name = "linkLabel2"; @@ -870,6 +907,7 @@ // linkLabel3 // linkLabel3.AutoSize = true; + linkLabel3.Enabled = false; linkLabel3.LinkColor = Color.FromArgb(255, 192, 255); linkLabel3.Location = new Point(102, 6); linkLabel3.Name = "linkLabel3"; @@ -914,7 +952,7 @@ private LinkLabel linkLabel1; private LinkLabel linkLabel2; private LinkLabel linkLabel3; - private Button button49; + private Button PowX; private Button Log_button; private Button Mod_button; private Button Exp_button; diff --git a/Form1.cs b/Form1.cs index 074e7e4..a718b22 100644 --- a/Form1.cs +++ b/Form1.cs @@ -15,6 +15,7 @@ namespace calculator public event Action? SingleOperatorPressed; public event Action? ClearPressed; public event Action? CalculatePressed; + public event Action? Constants; public void UpdateView(string input) { @@ -83,7 +84,7 @@ namespace calculator private void OnDotButtonClick(object sender, EventArgs e) { - OperandPressed?.Invoke('.'); + OperandPressed?.Invoke(','); } private void OnNegativeButtonClick(object sender, EventArgs e) @@ -198,7 +199,7 @@ namespace calculator private void OnLogButtonClick(object sender, EventArgs e) { - SingleOperatorPressed?.Invoke(Operation.Log); + OperatorPressed?.Invoke(Operation.Log); } private void OnInvButtonClick(object sender, EventArgs e) @@ -213,13 +214,32 @@ namespace calculator private void OnExpButtonClick(object sender, EventArgs e) { - SingleOperatorPressed?.Invoke(Operation.Exp); + Constants?.Invoke(Common.Constants.Exp); } private void OnPiButtonClick(object sender, EventArgs e) { - SingleOperatorPressed?.Invoke(Operation.Pi); + Constants?.Invoke(Common.Constants.Pi); } + private void OnOpenBracketButtonClick(object? sender, EventArgs e) + { + OperatorPressed?.Invoke(Operation.OpenBracket); + } + + private void OnCloseBracketButtonClick(object? sender, EventArgs e) + { + OperatorPressed?.Invoke(Operation.CloseBracket); + } + + private void OnIntButtonClick(object sender, EventArgs e) + { + SingleOperatorPressed?.Invoke(Operation.Int); + } + + private void OnPowXButtonClick(object sender, EventArgs e) + { + SingleOperatorPressed?.Invoke(Operation.PowX); + } } } \ No newline at end of file diff --git a/Form1.resx b/Form1.resx index 2a5f517..5254a5f 100644 --- a/Form1.resx +++ b/Form1.resx @@ -120,78 +120,18 @@ True - - True - - - True - - - True - - + True True - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - True True - - True - - - True - True diff --git a/Model/CaculatorData.cs b/Model/CaculatorData.cs index ddcd271..87e12c5 100644 --- a/Model/CaculatorData.cs +++ b/Model/CaculatorData.cs @@ -5,7 +5,6 @@ namespace calculator.Model; public class CaculatorData { public string? Input { get; set; } - public double? Value { get; set; } - public Operation? Operation { get; set; } - public bool Caculated { get; set; } + public Stack Values { get; set; } = new(); + public Stack Operations { get; set; } = new(); } \ No newline at end of file diff --git a/Services/Caculyator.cs b/Services/Caculyator.cs index ea95d7f..b1c896d 100644 --- a/Services/Caculyator.cs +++ b/Services/Caculyator.cs @@ -31,6 +31,8 @@ namespace calculator.Services return Math.Pow(a, b); case Operation.Mod: return a % b; + case Operation.Log: + return Math.Log(a, b); default: throw new ArgumentException($"Invalid operator: {token}"); } @@ -75,16 +77,14 @@ namespace calculator.Services return Math.Pow(a, 3); case Operation.CubeRoot: return Math.Pow(a, 1 / 3); - case Operation.Log: - return Math.Log10(a); case Operation.Ln: return Math.Log(a); - case Operation.Exp: - return Math.Exp(a); case Operation.Inv: return 1 / a; - case Operation.Pi: - return Math.PI; + case Operation.Int: + return Math.Round(a); + case Operation.PowX: + return Math.Pow(10, a); default: throw new ArgumentException($"Invalid operator: {token}"); } diff --git a/View/ICaculatorView.cs b/View/ICaculatorView.cs index 6044b09..7d9ea53 100644 --- a/View/ICaculatorView.cs +++ b/View/ICaculatorView.cs @@ -9,5 +9,6 @@ public interface ICaculatorView public event Action ClearPressed; public event Action CalculatePressed; public event Action SingleOperatorPressed; + public event Action Constants; public void UpdateView(string input); } \ No newline at end of file