diff --git a/C#/forSpbu/Calculator/Calculator.csproj b/C#/forSpbu/Calculator/Calculator.csproj new file mode 100644 index 0000000..f4dc8b5 --- /dev/null +++ b/C#/forSpbu/Calculator/Calculator.csproj @@ -0,0 +1,12 @@ + + + + WinExe + net7.0-windows + enable + true + enable + true + + + \ No newline at end of file diff --git a/C#/forSpbu/Calculator/Calculator.csproj.user b/C#/forSpbu/Calculator/Calculator.csproj.user new file mode 100644 index 0000000..3a34caa --- /dev/null +++ b/C#/forSpbu/Calculator/Calculator.csproj.user @@ -0,0 +1,8 @@ + + + + + Form + + + \ No newline at end of file diff --git a/C#/forSpbu/Calculator/CalculatorCore.cs b/C#/forSpbu/Calculator/CalculatorCore.cs new file mode 100644 index 0000000..13acf3b --- /dev/null +++ b/C#/forSpbu/Calculator/CalculatorCore.cs @@ -0,0 +1,277 @@ +namespace Calculator; + +public class CalculatorCore +{ + private enum Operation + { + Add, + Sub, + Mul, + Div, + Mod, + Clear, + Equal, + None + } + + private enum State + { + FirstOperandNumber, + FirstOperandComma, + SecondOperandNumber, + SecondOperandComma, + Operation, + Error, + None + } + + private Operation StringGetOperation(string operationString) + { + return operationString switch + { + "+" => Operation.Add, + "-" => Operation.Sub, + "*" => Operation.Mul, + "/" => Operation.Div, + "mod" => Operation.Mod, + "C" => Operation.Clear, + "=" => Operation.Equal, + _ => throw new ArgumentOutOfRangeException(nameof(operationString), operationString, null) + }; + } + + public void TakeNumberToken(string numberString) + { + if (numberString is not ("0" or "1" or "2" or "3" or "4" or "5" or "6" or "7" or "8" or "9" or ",")) + { + throw new ArgumentOutOfRangeException(nameof(numberString), numberString, null); + } + + switch (_state) + { + case State.Error: + if (numberString != ",") + { + _number = numberString; + Expression = ""; + _state = State.FirstOperandNumber; + } + break; + case State.FirstOperandComma: + if (numberString == ",") + { + _state = State.Error; + break; + } + + _number += numberString; + _state = State.FirstOperandNumber; + break; + case State.FirstOperandNumber: + if (_hasComma && numberString == ",") + { + _state = State.Error; + break; + } + + if (numberString == ",") + { + _hasComma = true; + } + _number += numberString; + break; + case State.SecondOperandComma: + if (numberString == ",") + { + _state = State.Error; + break; + } + + _number += numberString; + _state = State.SecondOperandNumber; + break; + case State.SecondOperandNumber: + if (_hasComma && numberString == ",") + { + _state = State.Error; + break; + } + + _hasComma = numberString == ","; + _number += numberString; + break; + case State.Operation: + if (numberString == ",") + { + _state = State.Error; + break; + } + + _number = numberString; + if (_operation == Operation.Equal) + { + _operation = Operation.None; + Expression = ""; + _state = State.FirstOperandNumber; + break; + } + + _state = State.SecondOperandNumber; + break; + case State.None: + if (numberString == ",") + { + _number += numberString; + _state = State.FirstOperandComma; + _hasComma = true; + } + else + { + _number = numberString; + _state = State.FirstOperandNumber; + } + + Expression = ""; + + break; + } + + if (_state == State.Error) + { + Expression = ""; + _number = "Error"; + _hasComma = false; + } + } + + private void ApplyOperation() + { + switch (_operation) + { + case Operation.Add: + _firstOperand += _secondOperand; + break; + case Operation.Sub: + _firstOperand -= _secondOperand; + break; + case Operation.Mul: + _firstOperand *= _secondOperand; + break; + case Operation.Div: + if (_secondOperand is < Delta and > -Delta) + { + _state = State.Error; + break; // Можно как фишку оставить, будет бесконечность + } + _firstOperand /= _secondOperand; + break; + case Operation.Mod: + if (_secondOperand is < Delta and > -Delta) + { + _state = State.Error; + break; + } + _firstOperand %= _secondOperand; + break; + } + } + + public void TakeOperationToken(string operationString) + { + var operation = StringGetOperation(operationString); + if (operation == Operation.Clear) + { + _state = State.None; + _operation = Operation.None; + Expression = ""; + _number = "0"; + _hasComma = false; + return; + } + + switch (_state) + { + case State.Error: + break; + case State.FirstOperandComma: + _state = State.Operation; + _number = _number.Remove(_number.Length - 1); + + if (!double.TryParse(_number, out _firstOperand)) + { + throw new ArgumentOutOfRangeException(nameof(_number), _number, null);; + } + + _operation = operation; + Expression = _firstOperand + " " + operationString; + break; + case State.FirstOperandNumber: + _state = State.Operation; + if (!double.TryParse(_number, out _firstOperand)) + { + throw new ArgumentOutOfRangeException(nameof(_number), _number, null);; + } + + _operation = operation; + Expression = _firstOperand + " " + operationString; + break; + case State.SecondOperandComma: + _state = State.Operation; + _number = _number.Remove(_number.Length - 1); + if (!double.TryParse(_number, out _secondOperand)) + { + throw new ArgumentOutOfRangeException(nameof(_number), _number, null); + } + + ApplyOperation(); + _operation = operation; + + Expression = _firstOperand + " " + operationString; + _hasComma = false; + _number = ""; + + break; + case State.SecondOperandNumber: + _state = State.Operation; + if (!double.TryParse(_number, out _secondOperand)) + { + throw new ArgumentOutOfRangeException(nameof(_number), _number, null); + } + + ApplyOperation(); + _operation = operation; + + Expression = _firstOperand + " " + operationString; + _hasComma = false; + _number = ""; + + break; + case State.Operation: + _operation = operation; + Expression = _firstOperand + " " + operationString; + break; + case State.None: + _state = State.Operation; + _operation = operation; + Expression = "0 " + operationString; + break; + } + + if (_state == State.Error) + { + Expression = ""; + _number = "Error"; + } + } + + private double _firstOperand = 0; + private double _secondOperand = 0; + private Operation _operation = Operation.None; + private State _state = State.None; + private string _number = "0"; + private bool _hasComma = false; + private const double Delta = 0.0001; + + public string Number => _number; + public string Expression { get; private set; } = ""; +} \ No newline at end of file diff --git a/C#/forSpbu/Calculator/CalculatorForm.Designer.cs b/C#/forSpbu/Calculator/CalculatorForm.Designer.cs new file mode 100644 index 0000000..2756b03 --- /dev/null +++ b/C#/forSpbu/Calculator/CalculatorForm.Designer.cs @@ -0,0 +1,182 @@ +namespace Calculator; + +partial class CalculatorForm +{ + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + mainLayout = new TableLayoutPanel(); + + expression = new Label(); + numberLabel = new Label(); + buttonsTable = new TableLayoutPanel(); + + for (int i = 0; i <= 10; i++) + { + numberButtons.Add(new Button()); + } + for (int i = 0; i < 7; i++) + { + operationButtons.Add(new Button()); + } + string[] operationsText = { "C", "+", "-", "mod", "*", "/", "=" }; + + + buttonsTable.SuspendLayout(); + mainLayout.SuspendLayout(); + SuspendLayout(); + + + /* + * Calculator form + */ + Size = new Size(400, 400); + MinimumSize = new Size(300, 400); + Text = "Calculator"; + Controls.Add(mainLayout); + + /* + * Main label + */ + mainLayout.Size = new Size(385, 362); + mainLayout.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; + mainLayout.ColumnCount = 1; + mainLayout.RowCount = 3; + mainLayout.Controls.Add(expression, 0, 0); + mainLayout.Controls.Add(numberLabel, 0, 1); + mainLayout.Controls.Add(buttonsTable, 0, 2); + mainLayout.RowStyles.Add(new ColumnStyle(SizeType.Percent, 10F)); + mainLayout.RowStyles.Add(new ColumnStyle(SizeType.Percent, 15F)); + mainLayout.RowStyles.Add(new ColumnStyle(SizeType.Percent, 75F)); + + /* + * Expression label + */ + expression.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; + expression.TextAlign = ContentAlignment.MiddleRight; + expression.Font = new Font("Segoe UI", 15F, FontStyle.Regular, GraphicsUnit.Pixel); + expression.Text = ""; + + /* + * Number label + */ + numberLabel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; + numberLabel.TextAlign = ContentAlignment.MiddleRight; + numberLabel.Font = new Font("Segoe UI", 30F, FontStyle.Regular, GraphicsUnit.Pixel); + numberLabel.Text = "0"; + + /* + * Buttons table + */ + buttonsTable.BackColor = Color.Black; + buttonsTable.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; + buttonsTable.ColumnCount = 3; + buttonsTable.RowCount = 6; + buttonsTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.ColumnCount)); + buttonsTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.ColumnCount)); + buttonsTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.ColumnCount)); + buttonsTable.RowStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.RowCount)); + buttonsTable.RowStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.RowCount)); + buttonsTable.RowStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.RowCount)); + buttonsTable.RowStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.RowCount)); + buttonsTable.RowStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.RowCount)); + buttonsTable.RowStyles.Add(new ColumnStyle(SizeType.Percent, 100/buttonsTable.RowCount)); + buttonsTable.Controls.Add(numberButtons[0], 1, 5); + buttonsTable.Controls.Add(numberButtons[1], 0, 4); + buttonsTable.Controls.Add(numberButtons[2], 1, 4); + buttonsTable.Controls.Add(numberButtons[3], 2, 4); + buttonsTable.Controls.Add(numberButtons[4], 0, 3); + buttonsTable.Controls.Add(numberButtons[5], 1, 3); + buttonsTable.Controls.Add(numberButtons[6], 2, 3); + buttonsTable.Controls.Add(numberButtons[7], 0, 2); + buttonsTable.Controls.Add(numberButtons[8], 1, 2); + buttonsTable.Controls.Add(numberButtons[9], 2, 2); + buttonsTable.Controls.Add(operationButtons[0], 0, 0); + buttonsTable.Controls.Add(operationButtons[1], 1, 0); + buttonsTable.Controls.Add(operationButtons[2], 2, 0); + buttonsTable.Controls.Add(operationButtons[3], 0, 1); + buttonsTable.Controls.Add(operationButtons[4], 1, 1); + buttonsTable.Controls.Add(operationButtons[5], 2, 1); + buttonsTable.Controls.Add(operationButtons[6], 2, 5); + + buttonsTable.Controls.Add(numberButtons[10], 0, 5); + + /* + * Buttons + */ + for (int i = 0; i <= 9; i++) + { + var button = numberButtons[i]; + button.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; + button.BackColor = Color.Gray; + button.TextAlign = ContentAlignment.MiddleCenter; + button.Text = i.ToString(); + button.ForeColor = Color.Azure; + button.Font = new Font("Segoe UI", 15F, FontStyle.Regular, GraphicsUnit.Pixel); + button.Click += NumberOnClick; + } + var commaButton = numberButtons[10]; + commaButton.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; + commaButton.BackColor = Color.DimGray; + commaButton.TextAlign = ContentAlignment.MiddleCenter; + commaButton.Text = ","; + commaButton.ForeColor = Color.Azure; + commaButton.Font = new Font("Segoe UI", 15F, FontStyle.Regular, GraphicsUnit.Pixel); + commaButton.Click += NumberOnClick; + + /* + * Operations + */ + for (int i = 0; i < 7; i++) + { + var button = operationButtons[i]; + button.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; + button.BackColor = (operationsText[i] != "=") ? Color.DimGray : Color.Aqua; + button.TextAlign = ContentAlignment.MiddleCenter; + button.Text = operationsText[i]; + button.ForeColor = (operationsText[i] != "=") ? Color.Azure : Color.Black; + button.Font = new Font("Segoe UI", 15F, FontStyle.Regular, GraphicsUnit.Pixel); + button.Click += OperationOnClick; + } + + + buttonsTable.ResumeLayout(false); + mainLayout.ResumeLayout(false); + ResumeLayout(false); + } + + #endregion + + private TableLayoutPanel mainLayout; + + private Label expression; + private Label numberLabel; + private TableLayoutPanel buttonsTable; + + private List