MWS_2021/Unity Project/Assets/Scripts/LSystem/MathExpression.cs
2021-04-17 22:09:48 +02:00

211 lines
9.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace ConsoleLSystem {
public class MathExpressionComparison{
MathExpression leftSide;
MathExpression rightSide;
Func<float, float,bool> comparisonFunction;
public MathExpressionComparison(MathExpression leftSide, MathExpression rightSide, Func<float, float, bool> comparisonFunction) {
this.leftSide = leftSide;
this.rightSide = rightSide;
this.comparisonFunction = comparisonFunction;
}
public bool eval(float[] variables) {
return comparisonFunction(leftSide.eval(variables), rightSide.eval(variables));
}
}
public class MathExpressionNode {
Func<float[], float[], float> function;
MathExpressionNode[] childNodes;
public MathExpressionNode(Func<float[], float[], float> function, MathExpressionNode[] childNodes) {
this.function = function;
this.childNodes = childNodes;
}
public float eval(float[] variables) {
var x = childNodes.Select(node => node.eval(variables)).ToArray();
return function(x, variables);
}
}
public class MathExpression {
private static Random random = new Random();
private static Dictionary<char, int> expression_hierarchy = new Dictionary<char, int>{
{'^', 4},
{'*', 3},
{'/', 3},
{'+', 1},
{'-', 1},
};
MathExpressionNode rootNode;
public float eval(float [] variables) {
return rootNode.eval(variables);
}
private static float negate(float[] children, float[] variables) {
return -children[0];
}
private static float mulitply(float[] children, float[] variables) {
return children[0] * children[1];
}
private static float power(float[] children, float[] variables) {
return (float)Math.Pow(children[0],children[1]);
}
private static float divide(float[] children, float[] variables) {
return children[0] / children[1];
}
private static float add(float[] children, float[] variables) {
return children[0] + children[1];
}
private static float subtract(float[] children, float[] variables) {
return children[0] - children[1];
}
//private MathExpressionNode parse(Dictionary<string, int> variableNames, string expression_string) {
// var read_characters = 0;
// var node = new MathExpressionNode();
// if (expression_string[0] == '(') {
// var length = LSystemFileParser.countParenthesisEnd(expression_string, '(', ')');
// if (length == expression_string.Length - 2) {
// return parse(variableNames, expression_string.Substring(1, length));
// }
// var left_child_node = parse(variableNames, expression_string.Substring(1, length));
// read_characters += length + 2;
// node.function = getFunction(expression_string[read_characters]);
// read_characters += 1;
// if (expression_string[read_characters] == '(')
// }
//}
private static MathExpressionNode parse(Dictionary<string, int> variableNames, string expression_string) {
expression_string = expression_string.Trim();
var expression_pos = -1;
var current_level = 1000;
for (int i = 0; i < expression_string.Length; i++) {
if (expression_string[i] == '(') {
var length = LSystemFileParser.countParenthesisEnd(expression_string.Substring(i), '(', ')');
if (length == expression_string.Length - 2) {
return parse(variableNames, expression_string.Substring(1, length));
}
//skip parenthesis
// if expression is covered in ( )
if (i == 0 && length == expression_string.Length - 2) {
return parse(variableNames, expression_string.Substring(1, length));
}
else {
i += length + 1;
continue;
}
}
int new_level;
if (expression_hierarchy.TryGetValue(expression_string[i],out new_level)) {
if (i>0 && !(i - 1 == expression_pos && expression_string[i] == '-')) {
if (new_level <= current_level) {
current_level = new_level;
expression_pos = i;
}
}
}
}
if (expression_pos > 0) {
var left_node = parse(variableNames, expression_string.Substring(0, expression_pos));
var right_node = parse(variableNames, expression_string.Substring(expression_pos + 1));
MathExpressionNode[] child_nodes = { left_node, right_node };
switch (expression_string[expression_pos]) {
case '*':
return new MathExpressionNode(mulitply, child_nodes);
break;
case '/':
return new MathExpressionNode(divide, child_nodes);
break;
case '+':
return new MathExpressionNode(add, child_nodes);
break;
case '-':
return new MathExpressionNode(subtract, child_nodes);
break;
case '^':
return new MathExpressionNode(power, child_nodes);
break;
default:
throw new Exception(String.Format("unknown operation {0} in {1}", expression_string[expression_pos], expression_string));
break;
}
}
else if (expression_string[0] == '-') {
MathExpressionNode[] a = { parse(variableNames, expression_string.Substring(1)) };
return new MathExpressionNode(negate, a);
}
else {
int index;
if (expression_string == "RANDOM") {
return new MathExpressionNode((float[] children, float[] arguments) => (float)random.NextDouble(), new MathExpressionNode[0]);
}
if (variableNames.TryGetValue(expression_string.Trim(),out index)){
return new MathExpressionNode((float[] children, float[] arguments) => arguments[index], new MathExpressionNode[0]);
}
else {
try {
float value = float.Parse(expression_string, NumberStyles.Any, CultureInfo.InvariantCulture);
return new MathExpressionNode((float[] children, float[] arguments) => value, new MathExpressionNode[0]);
}
catch (Exception) {
throw new Exception(String.Format("can't parse ``{0}``. It's not a number nor a known variable", expression_string));
}
}
}
}
public MathExpression(Dictionary<string,int> variableNames, string expression_string) {
rootNode = parse(variableNames, expression_string);
}
}
public class MathExpressionComparasionParser {
private static bool le(float a, float b) { return a < b; }
private static bool leq(float a, float b) { return a <= b; }
private static bool eq(float a, float b) { return a == b; }
public static MathExpressionComparison parse(Dictionary<string, int> variableNames, string expression_string) {
//var operations = new string[]{ "<=", ">=", "<", ">"};
if (expression_string.Contains("<=")) {
var parts = expression_string.Split(new string[] { "<=" }, StringSplitOptions.None).Select(s => s.Trim()).ToArray();
return new MathExpressionComparison(new MathExpression(variableNames, parts[0]), new MathExpression(variableNames, parts[1]), leq);
}
if (expression_string.Contains(">=")) {
var parts = expression_string.Split(new string[] { ">=" }, StringSplitOptions.None).Select(s => s.Trim()).ToArray();
return new MathExpressionComparison(new MathExpression(variableNames, parts[1]), new MathExpression(variableNames, parts[0]), leq);
}
if (expression_string.Contains("==")) {
var parts = expression_string.Split(new string[] { "==" }, StringSplitOptions.None).Select(s => s.Trim()).ToArray();
return new MathExpressionComparison(new MathExpression(variableNames, parts[1]), new MathExpression(variableNames, parts[0]), eq);
}
if (expression_string.Contains("<")) {
var parts = expression_string.Split('<').Select(s => s.Trim()).ToArray();
return new MathExpressionComparison(new MathExpression(variableNames, parts[0]), new MathExpression(variableNames, parts[1]), le);
}
if (expression_string.Contains(">")) {
var parts = expression_string.Split('>').Select(s => s.Trim()).ToArray();
return new MathExpressionComparison(new MathExpression(variableNames, parts[1]), new MathExpression(variableNames, parts[0]), le);
}
throw new Exception(String.Format("comparison operation not recognized in {}", expression_string));
}
}
}