using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; namespace HeuristicLab.Problems.DataAnalysis { public static class IntervalConstraintsParser { public static IEnumerable Parse(string input) { var options = RegexOptions.Multiline | RegexOptions.IgnoreCase; var matches = Regex.Matches(input, @"^(.*)\bin\b\s*([\[\]])(.*[^\s])(\s*\.\.\s*)([^\s].*)([\[\]])\n*\r*\s*$", options); for (var i = 0; i < matches.Count; ++i) { if (matches[i].Groups.Count == 7) { var intervalConstraint = new IntervalConstraint(); //pattern 1 = defintion var definition = Regex.Replace(matches[i].Groups[1].Value, @"\s *", ""); if (Regex.IsMatch(definition, @"\/")) { var splitted = Regex.Split(definition.Replace(" ", string.Empty), @"\/"); var match = Regex.Match(splitted[0], @"([d∂])([0-9]|[²³])?(.*[^\s*])"); if (match.Success) { intervalConstraint.NumberOfDerivation = match.Groups[2].Success ? ParseDerivationCount(match.Groups[2].Value) : 1; intervalConstraint.Definition = match.Groups[3].Value; intervalConstraint.IsDerivation = true; var formulation = Regex.Match(splitted[1], @"([d∂])(.*[^²³])([²³])?"); if (formulation.Success) { intervalConstraint.Variable = formulation.Groups[2].Success ? formulation.Groups[2].Value : ""; } } else { throw new ArgumentException($"An error occured in the derivation part: {splitted[0]}"); } } else { intervalConstraint.Definition = Regex.Match(definition, @".*[^.\s]*").Value; intervalConstraint.IsDerivation = false; } intervalConstraint.Expression = matches[i].Groups[0].Value.Trim(' ', '\t', '\n', '\r'); intervalConstraint.InclusiveLowerBound = (matches[i].Groups[2].Value == "["); intervalConstraint.InclusiveUpperBound = (matches[i].Groups[6].Value == "]"); intervalConstraint.Interval = new Interval(ParseIntervalBounds(matches[i].Groups[3].Value.Replace(" ", string.Empty)), ParseIntervalBounds(matches[i].Groups[5].Value.Replace(" ", string.Empty))); yield return intervalConstraint; } else { throw new ArgumentException($"The given constraint: {matches[i].Value} is not valid."); } } } public static IEnumerable ParseInput(string input, string target = "", IEnumerable variables = null) { var lines = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); foreach (var line in lines) { var trimmedLine = line.TrimStart(); //Check for target-variable constraint if (trimmedLine.StartsWith("Target:")) { var start = "Target:".Length; var end = trimmedLine.Length; var targetConstraint = trimmedLine.Substring(start, end-start); var match = Regex.Match(targetConstraint, @"([^\s]*)\s*(\bin\b)\s*([\[\]])\s*(\S*)\s*(..)\s*(\S*)\s*([\[\]])"); if (match.Success) { if (match.Groups.Count != 8) { throw new ArgumentException("The given target-constraint is not complete!"); } else { if (target != "") { if (match.Groups[1].Value.Trim() != target) { throw new ArgumentException("The given target variable is not in the given dataset!"); } } var lowerBound = ParseIntervalBounds(match.Groups[4].Value); var upperBound = ParseIntervalBounds(match.Groups[6].Value); var constraint = new IntervalConstraint("Target:" + match.Groups[0].Value); constraint.Expression = "Target:" + match.Groups[0].Value; constraint.Definition = "Target " + match.Groups[1].Value.Trim(); constraint.Variable = match.Groups[1].Value.Trim(); constraint.InclusiveLowerBound = match.Groups[3].Value.Trim() == "["; constraint.InclusiveUpperBound = match.Groups[7].Value.Trim() == "]"; constraint.IsDerivation = false; constraint.NumberOfDerivation = 0; constraint.Interval = new Interval(lowerBound, upperBound); yield return constraint; } } else { throw new ArgumentException("The inserted target constraint is not valid!"); } //Check for derivation } else if (trimmedLine.StartsWith("d") || trimmedLine.StartsWith("\u2202")) { var match = Regex.Match(trimmedLine, @"([d∂])([²³])?\s*(\S*)\s*(\/)([d∂])\s*([^²³]*)([²³])?\s*\bin\b\s*([\[\]])\s*(\S*)\s*(..)\s*(\S*)\s*([\[\]])"); if (match.Success) { if (match.Groups.Count != 13) { throw new ArgumentException("The given derivation-constraint is not complete"); } else { if (target != "") { if (match.Groups[3].Value != target) throw new ArgumentException("The given target variable is not given in the dataset!"); } if (variables != null && variables.Any()) { if (variables.All(v => v != match.Groups[6].Value.Trim())) { throw new ArgumentException("The given variable does not exist in the dataset!"); } } if (match.Groups[2].Value.Trim() != "" || match.Groups[7].Value.Trim() != "") { if (match.Groups[2].Value.Trim() == "" || match.Groups[7].Value.Trim() == "") throw new ArgumentException("Number of derivation has to be written on both sides!"); if (match.Groups[2].Value.Trim() != match.Groups[7].Value.Trim()) throw new ArgumentException("Derivation number is not equal on both sides!"); } var lowerBound = ParseIntervalBounds(match.Groups[9].Value); var upperBound = ParseIntervalBounds(match.Groups[11].Value); var constraint = new IntervalConstraint(match.Groups[0].Value); constraint.Expression = match.Groups[0].Value; constraint.Definition = match.Groups[1].Value + match.Groups[2].Value + match.Groups[3].Value + match.Groups[4].Value + match.Groups[5].Value + match.Groups[6].Value + match.Groups[7].Value; constraint.IsDerivation = true; constraint.InclusiveLowerBound = match.Groups[8].Value.Trim() == "["; constraint.InclusiveUpperBound = match.Groups[12].Value.Trim() == "]"; constraint.Variable = match.Groups[6].Value.Trim(); constraint.NumberOfDerivation = ParseDerivationCount(match.Groups[2].Value.Trim()); constraint.Interval = new Interval(lowerBound, upperBound); yield return constraint; } } else { throw new ArgumentException("The inserted derivation constraint is not valid!"); } //Check for comment } else if (trimmedLine.StartsWith("#") || trimmedLine == "") { //If it is a comment just continue without saving anything continue; } else { throw new ArgumentException("Error at your constraints definition constraints have to start with Target: | d | \u2202 | #"); } } } private static double ParseIntervalBounds(string input) { input = input.ToLower(); switch (input) { case "+inf.": case "inf.": return double.PositiveInfinity; case "-inf.": return double.NegativeInfinity; default: { if (double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) { return value; } else { throw new ArgumentException("The given boundary is not a double value!"); } } } } private static int ParseDerivationCount(string input) { switch (input) { case "": return 1; case "²": return 2; case "³": return 3; default: int.TryParse(input, out var value); return value; } } } }