So, it looks like it will help you simplify your expression in the sum of products. Having it in the sum of the forms of the products, you will have a number of different options, each of which will be the only right one if all the values โโthat it contains are true.
One way to deal with this is to use a compound template. We will start with the IExpression interface:
public interface IExpression<T> { int NumberOfPossibilities(); IEnumerable<IEnumerable<ValueExpression<T>>> Possibilities(); IEnumerable<ValueExpression<T>> GetNthPossibility(int n); }
The key points here are the first and last methods. We need to know how many possibilities for this expression, as well as an easy way to get the Nth option. The convention for the latter method is that all values โโmust be true for the nth possibility. For the second method, external enumerations are all expressions that must be ORed together, while each of the internal expressions must be AND together.
There will be three implementations. A ValueExpression , which represents only one value, AndExpression , which represents AND expressions of N together, and OrExpression , which represent ORing N expressions together.
The value is the easiest to implement:
public class ValueExpression<T> : IExpression<T> { public T Value { get; set; } public ValueExpression(T value) { Value = value; } public int NumberOfPossibilities() { return 1; } public IEnumerable<IEnumerable<ValueExpression<T>>> Possibilities() { return new[] { new[] { this } }; } public IEnumerable<ValueExpression<T>> GetNthPossibility(int n) { return new[] { this }; } }
And the expression is a little more. The number of possibilities for AND expression is the product of the number of ANDed possibilities.
To get the Nth option, you can think of it as such:
starts at 0; for 0, the result requires ANDing combining the first possibility of each of the subexpressions. For the first opportunity, we add one to the first subexpression. Each time n grows, we increase the permutation that we get from the first subexpression. When we pass it, it returns to zero, and the next subexpression rises by one. This is basically like a count, but where each digit represents one of the subexpressions and where the basis of this digit is the number of possibilities for this subexpression.
public class AndExpression<T> : IExpression<T> { private IList<IExpression<T>> expressions; public AndExpression(IList<IExpression<T>> expressions) { this.expressions = expressions; } public int NumberOfPossibilities() { return expressions.Aggregate(1, (acc, expression) => acc * expression.NumberOfPossibilities()); } IEnumerable<IEnumerable<ValueExpression<T>>> IExpression<T>.Possibilities() { return Enumerable.Range(0, NumberOfPossibilities()) .Select(n => GetNthPossibility(n)); } public IEnumerable<ValueExpression<T>> GetNthPossibility(int n) { for (int i = 0; i < expressions.Count; i++) { int count = expressions[i].NumberOfPossibilities(); foreach (var value in expressions[i].GetNthPossibility(n % count)) yield return value; n /= count; } } }
For an OR expression, it is similar to, but still different from, the AND version.
The number of possibilities is a sum, not a product of samples of internal expressions.
To get the nth opportunity, we return only elements from one of the capabilities of one of the subexpressions, instead of returning one of them, as AND does.
public class OrExpression<T> : IExpression<T> { private IList<IExpression<T>> expressions; public OrExpression(IList<IExpression<T>> expressions) { this.expressions = expressions; } public int NumberOfPossibilities() { return expressions.Sum(expression => expression.NumberOfPossibilities()); } public IEnumerable<IEnumerable<ValueExpression<T>>> Possibilities() { return Enumerable.Range(0, NumberOfPossibilities()) .Select(n => GetNthPossibility(n)); } public IEnumerable<ValueExpression<T>> GetNthPossibility(int n) { for (int i = 0; i < expressions.Count; i++) { int count = expressions[i].NumberOfPossibilities(); if (n < count) return expressions[i].GetNthPossibility(n); else n -= count; } throw new ArgumentOutOfRangeException(); } }
Here is a simple function to print an expression as a string:
public static void PrintPossibilities<T>(IExpression<T> expression) { Console.WriteLine(string.Join(" + ", expression.Possibilities() .Select(possibility => string.Concat(possibility.Select(value => value.Value))))); }
Note that this does not handle parsing the expression, just processing it after parsing it.
Here is the test of your first example:
var AB = new AndExpression<string>(new[]{ new ValueExpression<string>("A"), new ValueExpression<string>("B")}); var CD = new OrExpression<string>(new[]{ new ValueExpression<string>("C"), new ValueExpression<string>("D")}); var exp = new AndExpression<string>(new IExpression<string>[] { AB, CD }); PrintPossibilities(exp);
What prints:
ABC + ABD
AB changed as an OR expression (your second example):
AC + BC + AD + BD
Your third option may be presented as:
var third = new OrExpression<string>(new[]{ new ValueExpression<string>("A"), new ValueExpression<string>("B"), new ValueExpression<string>("C")});
which when printed leads to:
A + B + C