private double?ParseDataReferenceString(string dRef)
        {
            dRef = GetPrecision(dRef, out int?precision);
            dRef = GetPlayer(dRef, out _);

            dRef = ParseValues(dRef.AsMemory());

            if (string.IsNullOrEmpty(Regex.Replace(dRef, @"[/*+\-()]", string.Empty)))
            {
                return(null);
            }

            // extract the scaling
            string scalingText = GetScalingText(dRef, out _);

            if (!string.IsNullOrEmpty(scalingText))
            {
                dRef = dRef.Replace(scalingText, string.Empty);
            }

            double number = HeroesMathEval.CalculatePathEquation(dRef);

            if (precision.HasValue)
            {
                return(Math.Round(number, precision.Value));
            }
            else
            {
                return(Math.Round(number, 0));
            }
        }
 public void CalculatePathEquationTest()
 {
     Assert.AreEqual(100, HeroesMathEval.CalculatePathEquation("(12 + 6.000000) * (0.1875 + 0.062500) - (12 * 0.1875) / (12 * 0.1875) * 100"));
     Assert.AreEqual(50, HeroesMathEval.CalculatePathEquation("17 / 34 * 100"));
     Assert.AreEqual(70, HeroesMathEval.CalculatePathEquation("(57.8 / 34) * 100 - 100"));
     Assert.AreEqual(40, HeroesMathEval.CalculatePathEquation("-100*(1-1.400000)"));
     Assert.AreEqual(100, HeroesMathEval.CalculatePathEquation("--100"));
     Assert.AreEqual(15, HeroesMathEval.CalculatePathEquation("-100*-0.15"));
     Assert.AreEqual(150, HeroesMathEval.CalculatePathEquation("-100 * (0.225/-0.15)"));
     Assert.AreEqual(40, HeroesMathEval.CalculatePathEquation("(1+(-0.6)*100)"));
     Assert.AreEqual(30, HeroesMathEval.CalculatePathEquation("-(-0.6--0.3)*100"));
     Assert.AreEqual(70, HeroesMathEval.CalculatePathEquation("- (-0.7*100)"));
     Assert.AreEqual(-0.5, HeroesMathEval.CalculatePathEquation("-0.5"));
     Assert.AreEqual(0, HeroesMathEval.CalculatePathEquation("0"));
     Assert.AreEqual(100, HeroesMathEval.CalculatePathEquation("1+0*100"));
     Assert.AreEqual(100, HeroesMathEval.CalculatePathEquation("(1+0*100)"));
     Assert.AreEqual(60, HeroesMathEval.CalculatePathEquation("((5) + (3) / 5 - 1) * 100"));
     Assert.AreEqual(5, HeroesMathEval.CalculatePathEquation("(30/20)-1*10)")); // missing a (left) parenthesis
     Assert.AreEqual(9, HeroesMathEval.CalculatePathEquation("--100*-0.09"));
 }
        private string ParseGameString(string tooltip, string splitter, bool nestedTooltip)
        {
            if (nestedTooltip)
            {
                tooltip = tooltip.Replace("'", "\"");
            }

            string[] parts = Regex.Split(tooltip, splitter);

            for (int i = 0; i < parts.Length; i++)
            {
                if (nestedTooltip)
                {
                    if (!parts[i].Contains("[d ref="))
                    {
                        continue;
                    }
                }
                else
                {
                    if (!parts[i].Contains("<d ref=") && !parts[i].Contains("<d const="))
                    {
                        continue;
                    }
                }

                string pathLookup = parts[i];

                // get and remove precision
                pathLookup = GetPrecision(pathLookup, out int?precision);

                // remove the player
                pathLookup = GetPlayer(pathLookup, out _);

                // perform xml data lookup to find values
                string mathPath = ParseValues(pathLookup.AsMemory());

                if (string.IsNullOrEmpty(Regex.Replace(mathPath, @"[/*+\-()]", string.Empty)))
                {
                    return(string.Empty);
                }

                // extract the scaling and the amount
                string scalingText = GetScalingText(mathPath, out int numOfScalingTexts);

                if (!string.IsNullOrEmpty(scalingText))
                {
                    mathPath = mathPath.Replace(scalingText, string.Empty);
                }

                double number = HeroesMathEval.CalculatePathEquation(mathPath.Trim('/'));

                if (precision.HasValue)
                {
                    parts[i] = Math.Round(number, precision.Value).ToString();
                }
                else
                {
                    parts[i] = Math.Round(number, 0).ToString();
                }

                if (!string.IsNullOrEmpty(scalingText))
                {
                    // only add the scaling in certain cases
                    if (numOfScalingTexts == 1 || (numOfScalingTexts > 1 && !mathPath.Contains('/')))
                    {
                        ReadOnlySpan <char> nextPart = parts[i + 1];
                        nextPart = nextPart.TrimStart();

                        if (nextPart.StartsWith("%"))
                        {
                            parts[i]    += $"%{scalingText}";
                            parts[i + 1] = parts[i + 1].ReplaceFirst("%", string.Empty);
                        }
                        else
                        {
                            parts[i] += $"{scalingText}";
                        }
                    }
                }
            }

            if (nestedTooltip)
            {
                return(string.Join(string.Empty, parts));
            }
            else
            {
                string joinDesc = string.Join(string.Empty, parts);

                foreach (Match match in Regex.Matches(joinDesc, @"[a-z]+""[a-z]+"))
                {
                    joinDesc = joinDesc.Replace(match.Value, match.Value.Replace("\"", "'"));
                }

                return(DescriptionValidator.Validate(joinDesc));
            }
        }