#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;
}
}
}
}