using System; using System.Text; using System.Diagnostics; using Cobilas.Collections; using System.Globalization; using Cobilas.IO.Atlf.Components; namespace Cobilas.Numeric { #pragma warning disable CS1591 // O comentário XML ausente não foi encontrado para o tipo ou membro visível publicamente public static class ParseCalculation { #pragma warning restore CS1591 // O comentário XML ausente não foi encontrado para o tipo ou membro visível publicamente /// All collections of mathematical operations created. public static CalculationsCollection[] Collections { get; private set; } = Array.Empty(); static ParseCalculation() { Collections = new CalculationsCollection[1]; Collections[0] = new BasicCalculation(); Collections[0].Initialization(); foreach (var item in TypeUtilitarian.GetTypes()) { if (item.IsSubclassOf(typeof(CalculationsCollection)) && !item.CompareType() && !item.CompareType()) { CalculationsCollection calc = item.Activator(); calc.Initialization(); Collections = ArrayManipulation.Add(calc, Collections); } } } /// Take a text containing a mathematical formula and perform the calculation. public static double Parse(string text) => GetMathBlock(new CharacterCursor(text.Replace(" ", string.Empty)), GetSignals(), false); /// Take a text containing a mathematical formula and perform the calculation. /// Prints the mathematical formula and its result. /// public static double PrintConsoleCalc(string text) { double res = Parse(text); Console.Write("{0}={1}\r\n", text, res); return res; } /// Take a text containing a mathematical formula and perform the calculation. /// Prints the mathematical formula and its result. /// public static double DebugLogCalc(string text) { double res = Parse(text); Debug.Print("{0}={1}", text, res); return res; } private static MathOperator[] GetSignals() { MathOperator[] s = Array.Empty(); foreach (var item in Collections) ArrayManipulation.Add(item.Calculations, ref s); return s; } private static double GetMathBlock(CharacterCursor txt, MathOperator[] signals, bool isMathBlock) { StringBuilder builder = new StringBuilder(); while (txt.MoveToCharacter()) { if (txt.CharIsEqualToIndex('(')) builder.Append(GetMathBlock(txt, signals, true)); else if (txt.CharIsEqualToIndex(')')) return CalcMathBlock(new CharacterCursor(builder), signals); else if (char.IsWhiteSpace(txt.CurrentCharacter)) throw new FormatException("The expression cannot contain blanks!"); else builder.Append(txt.CurrentCharacter); } if (isMathBlock) throw new FormatException("The parenthesis was not closed!"); return CalcMathBlock(new CharacterCursor(builder), signals); } private static double CalcMathBlock(CharacterCursor txt, MathOperator[] signals) { string[] calc = Array.Empty(); StringBuilder builder = new StringBuilder(); while (txt.MoveToCharacter()) { if (IsSignal(txt, signals, out string sg)) { txt.MoveToCharacter(sg.Length - 1); if (builder.Length != 0) { ArrayManipulation.Add(builder.ToString(), ref calc); builder.Clear(); } ArrayManipulation.Add(sg, ref calc); } else builder.Append(txt.CurrentCharacter); } if (builder.Length != 0) { ArrayManipulation.Add(builder.ToString(), ref calc); builder.Clear(); } byte executionLevel = 0; foreach (var item in signals) if (item.ExecutionLevel > executionLevel) executionLevel = item.ExecutionLevel; for (int I = 0; I <= executionLevel && ArrayManipulation.ArrayLength(calc) > 1; I++) { for (int J = 0; J < ArrayManipulation.ArrayLength(calc); J++) { if (IsSignal(calc[J], signals, out MathOperator math)) if (math.ExecutionLevel == I) switch (math.Orientation) { case SignalOrientation.both: if (J == 0) throw new FormatException($"The signal({calc[J]}) must have a value on the left."); else if (J + 1 >= ArrayManipulation.ArrayLength(calc)) throw new FormatException($"The signal({calc[J]}) must have a value to the right."); if (!IsDigit(calc[J - 1])) throw new FormatException($"The value({calc[J - 1]}{calc[J]}) on the left is not a digit."); else if (!IsDigit(calc[J + 1])) throw new FormatException($"The value({calc[J]}{calc[J + 1]}) on the right is not a digit."); calc[J - 1] = math.Function.DynamicInvoke( double.Parse(calc[J - 1]), double.Parse(calc[J + 1])).ToString(); ArrayManipulation.Remove(J, 2, ref calc); J = -1; break; case SignalOrientation.left: if (J == 0) throw new FormatException($"The signal({calc[J]}) must have a value on the left."); if (!IsDigit(calc[J - 1])) throw new FormatException($"The value({calc[J - 1]}{calc[J]}) on the left is not a digit."); else if (J + 1 < ArrayManipulation.ArrayLength(calc)) { if (!IsSignal(calc[J + 1], signals, out _)) throw new FormatException($"The value({calc[J]}{calc[J + 1]}) on the right is not a signal."); } calc[J - 1] = math.Function.DynamicInvoke( double.Parse(calc[J - 1]), 0).ToString(); ArrayManipulation.Remove(J, 1, ref calc); J = -1; break; case SignalOrientation.right: if (J + 1 >= ArrayManipulation.ArrayLength(calc)) throw new FormatException($"The signal({calc[J]}) must have a value to the right."); if (J != 0) { if (!IsSignal(calc[J - 1], signals, out _)) throw new FormatException($"The value({calc[J - 1]}{calc[J]}) on the left is not a signal."); } else if (!IsDigit(calc[J + 1])) throw new FormatException($"The value({calc[J]}{calc[J + 1]}) on the right is not a digit."); calc[J] = math.Function.DynamicInvoke( 0, double.Parse(calc[J + 1])).ToString(); ArrayManipulation.Remove(J + 1, 1, ref calc); J = -1; break; } } } return double.Parse(calc[0]); } private static bool IsDigit(string txt) => char.IsDigit(txt.ToString(CultureInfo.InvariantCulture), 0); private static bool IsSignal(string signal, MathOperator[] signals, out MathOperator math) { foreach (var item in signals) if (item.Signal == signal) { math = item; return true; } math = default; return false; } private static bool IsSignal(CharacterCursor txt, MathOperator[] signals, out string sg) { foreach (var item in signals) if (txt.CharIsEqualToIndex(item.Signal)) { sg = item.Signal; return true; } sg = string.Empty; return false; } } }