#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion 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 ParseInput(string inputText, string target, IEnumerable variables) { if (string.IsNullOrEmpty(inputText)) throw new ArgumentNullException("No input text has been provided."); if (string.IsNullOrEmpty(target)) throw new ArgumentNullException("No target variable has been provided."); if (variables == null) throw new ArgumentNullException("No variables have been provided."); if (!variables.Any()) throw new ArgumentException("Varialbes are empty."); var lines = inputText.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*(\.{2})\s*(\S*)\s*([\[\]])"); if (match.Success) { if (match.Groups.Count != 10) { throw new ArgumentException("The target-constraint is not complete.", line); } else { var targetVariable = match.Groups[1].Value.Trim(); if (match.Groups[1].Value.Trim().StartsWith("'") && match.Groups[1].Value.Trim().EndsWith("'")) { targetVariable = targetVariable.Substring(1, targetVariable.Length - 2); } if (targetVariable != target) { throw new ArgumentException($"The target variable {targetVariable} does not match the provided target {target}.", line); } var lowerBound = ParseIntervalBounds(match.Groups[6].Value); var upperBound = ParseIntervalBounds(match.Groups[8].Value); var expression = "Target:" + match.Groups[0].Value; var parsedTarget = match.Groups[1].Value.Trim(); var variable = targetVariable; var inclLowerBound = match.Groups[5].Value.Trim() == "["; var inclUpperBound = match.Groups[9].Value.Trim() == "]"; var isEnabled = true; var numberOfDerivation = 0; var interval = new Interval(lowerBound, upperBound); var constraint = new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, inclLowerBound, inclUpperBound, isEnabled); yield return constraint; } } else { throw new ArgumentException("The inserted target constraint is not valid.", line); } //Check for derivation } else if (trimmedLine.StartsWith("d") || trimmedLine.StartsWith("\u2202")) { var match = Regex.Match(trimmedLine, @"([d∂])([²³]?)\s*(['](.*)[']|(.*[^\s]))\s*(\/)\s*([d∂])\s*(['](.*)[']|(.*[^\s²³]))\s*([²³]?)\s*\bin\b\s*([\[\]])\s*(\S*)\s*(\.{2})\s*(\S*)\s*([\[\]])"); if (match.Success) { if (match.Groups.Count != 17) { throw new ArgumentException("The given derivation-constraint is not complete.", line); } else { var derivationTarget = match.Groups[3].Value.Trim(); var derivationVariable = match.Groups[8].Value.Trim(); if (match.Groups[3].Value.Trim().StartsWith("'") && match.Groups[3].Value.Trim().EndsWith("'")) { derivationTarget = derivationTarget.Substring(1, derivationTarget.Length - 2); } if (match.Groups[8].Value.Trim().StartsWith("'") && match.Groups[8].Value.Trim().EndsWith("'")) { derivationVariable = derivationVariable.Substring(1, derivationVariable.Length - 2); } if (derivationTarget != target) { throw new ArgumentException($"The target variable {derivationTarget} does not match the provided target {target}.", line); } if (variables.All(v => v != derivationVariable)) { throw new ArgumentException($"The given variable {derivationVariable} does not exist in the dataset.", line); } if (match.Groups[2].Value.Trim() != "" || match.Groups[11].Value.Trim() != "") { if (match.Groups[2].Value.Trim() == "" || match.Groups[11].Value.Trim() == "") throw new ArgumentException("Number of derivation has to be written on both sides.", line); if (match.Groups[2].Value.Trim() != match.Groups[11].Value.Trim()) throw new ArgumentException("Derivation number is not equal on both sides.", line); } var lowerBound = ParseIntervalBounds(match.Groups[13].Value); var upperBound = ParseIntervalBounds(match.Groups[15].Value); var expression = match.Groups[0].Value; var parsedTarget = derivationTarget; var isEnabled = true; var inclLowerBound = match.Groups[12].Value.Trim() == "["; var inclUpperBound = match.Groups[16].Value.Trim() == "]"; var variable = derivationVariable; var numberOfDerivation = ParseDerivationCount(match.Groups[2].Value.Trim()); var interval = new Interval(lowerBound, upperBound); var constraint = new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, inclLowerBound, inclUpperBound, isEnabled); yield return constraint; } } else { throw new ArgumentException("The inserted derivation constraint is not valid.", line); } //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 | #)", line); } } } 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; } } } }