1 //---------------------------------------------------------------------
2 // <copyright file="BoolExpr.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
11 using System.Collections.Generic;
13 using System.Globalization;
14 using System.Collections.ObjectModel;
15 using System.Diagnostics;
18 namespace System.Data.Common.Utils.Boolean
21 /// Base type for Boolean expressions. Boolean expressions are immutable,
22 /// and value-comparable using Equals. Services include local simplification
23 /// and normalization to Conjunctive and Disjunctive Normal Forms.
26 /// Comments use the following notation convention:
28 /// "A . B" means "A and B"
29 /// "A + B" means "A or B"
30 /// "!A" means "not A"
32 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
33 internal abstract partial class BoolExpr<T_Identifier> : IEquatable<BoolExpr<T_Identifier>>
36 /// Gets an enumeration value indicating the type of the expression node.
38 internal abstract ExprType ExprType { get; }
41 /// Standard accept method invoking the appropriate method overload
42 /// in the given visitor.
44 /// <typeparam name="T_Return">T_Return is the return type for the visitor.</typeparam>
45 /// <param name="visitor">Visitor implementation.</param>
46 /// <returns>Value computed for this node.</returns>
47 internal abstract T_Return Accept<T_Return>(Visitor<T_Identifier, T_Return> visitor);
50 /// Invokes the Simplifier visitor on this expression tree.
51 /// Simplifications are purely local (see Simplifier class
54 internal BoolExpr<T_Identifier> Simplify()
56 return IdentifierService<T_Identifier>.Instance.LocalSimplify(this);
60 /// Expensive simplification that considers various permutations of the
61 /// expression (including Decision Diagram, DNF, and CNF translations)
63 internal BoolExpr<T_Identifier> ExpensiveSimplify(out Converter<T_Identifier> converter)
65 var context = IdentifierService<T_Identifier>.Instance.CreateConversionContext();
66 converter = new Converter<T_Identifier>(this, context);
68 // Check for valid/unsat constraints
69 if (converter.Vertex.IsOne())
71 return TrueExpr<T_Identifier>.Value;
73 if (converter.Vertex.IsZero())
75 return FalseExpr<T_Identifier>.Value;
78 // Pick solution from the (unmodified) expression, its CNF and its DNF
79 return ChooseCandidate(this, converter.Cnf.Expr, converter.Dnf.Expr);
82 private static BoolExpr<T_Identifier> ChooseCandidate(params BoolExpr<T_Identifier>[] candidates)
84 Debug.Assert(null != candidates && 1 < candidates.Length, "must be at least one to pick");
86 int resultUniqueTermCount = default(int);
87 int resultTermCount = default(int);
88 BoolExpr<T_Identifier> result = null;
90 foreach (var candidate in candidates)
92 // first do basic simplification
93 var simplifiedCandidate = candidate.Simplify();
95 // determine "interesting" properties of the expression
96 int candidateUniqueTermCount = simplifiedCandidate.GetTerms().Distinct().Count();
97 int candidateTermCount = simplifiedCandidate.CountTerms();
99 // see if it's better than the current result best result
100 if (null == result || // bootstrap
101 candidateUniqueTermCount < resultUniqueTermCount || // check if the candidate improves on # of terms
102 (candidateUniqueTermCount == resultUniqueTermCount && // in case of tie, choose based on total
103 candidateTermCount < resultTermCount))
105 result = simplifiedCandidate;
106 resultUniqueTermCount = candidateUniqueTermCount;
107 resultTermCount = candidateTermCount;
115 /// Returns all term expressions below this node.
117 internal List<TermExpr<T_Identifier>> GetTerms()
119 return LeafVisitor<T_Identifier>.GetTerms(this);
123 /// Counts terms in this expression.
125 internal int CountTerms()
127 return TermCounter<T_Identifier>.CountTerms(this);
131 /// Implicit cast from a value of type T to a TermExpr where
132 /// TermExpr.Value is set to the given value.
134 /// <param name="value">Value to wrap in term expression</param>
135 /// <returns>Term expression</returns>
136 public static implicit operator BoolExpr<T_Identifier>(T_Identifier value)
138 return new TermExpr<T_Identifier>(value);
142 /// Creates the negation of the current element.
144 internal virtual BoolExpr<T_Identifier> MakeNegated()
146 return new NotExpr<T_Identifier>(this);
149 public override string ToString()
151 return ExprType.ToString();
154 public bool Equals(BoolExpr<T_Identifier> other)
156 return null != other && ExprType == other.ExprType &&
157 EquivalentTypeEquals(other);
160 protected abstract bool EquivalentTypeEquals(BoolExpr<T_Identifier> other);
164 /// Boolean expression that evaluates to true.
166 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
167 internal sealed class TrueExpr<T_Identifier> : BoolExpr<T_Identifier>
169 private static readonly TrueExpr<T_Identifier> s_value = new TrueExpr<T_Identifier>();
171 // private constructor so that we control existence of True instance
178 /// Gets the one instance of TrueExpr
180 internal static TrueExpr<T_Identifier> Value { get { return s_value; } }
182 internal override ExprType ExprType { get { return ExprType.True; } }
184 internal override T_Return Accept<T_Return>(Visitor<T_Identifier, T_Return> visitor)
186 return visitor.VisitTrue(this);
189 internal override BoolExpr<T_Identifier> MakeNegated()
191 return FalseExpr<T_Identifier>.Value;
194 protected override bool EquivalentTypeEquals(BoolExpr<T_Identifier> other)
196 return object.ReferenceEquals(this, other);
201 /// Boolean expression that evaluates to false.
203 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
204 internal sealed class FalseExpr<T_Identifier> : BoolExpr<T_Identifier>
206 private static readonly FalseExpr<T_Identifier> s_value = new FalseExpr<T_Identifier>();
208 // private constructor so that we control existence of False instance
215 /// Gets the one instance of FalseExpr
217 internal static FalseExpr<T_Identifier> Value { get { return s_value; } }
219 internal override ExprType ExprType { get { return ExprType.False; } }
221 internal override T_Return Accept<T_Return>(Visitor<T_Identifier, T_Return> visitor)
223 return visitor.VisitFalse(this);
226 internal override BoolExpr<T_Identifier> MakeNegated()
228 return TrueExpr<T_Identifier>.Value;
231 protected override bool EquivalentTypeEquals(BoolExpr<T_Identifier> other)
233 return object.ReferenceEquals(this, other);
238 /// A term is a leaf node in a Boolean expression. Its value (T/F) is undefined.
240 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
241 internal sealed class TermExpr<T_Identifier> : BoolExpr<T_Identifier>, IEquatable<TermExpr<T_Identifier>>
243 private readonly T_Identifier _identifier;
244 private readonly IEqualityComparer<T_Identifier> _comparer;
247 /// Construct a term.
249 /// <param name="comparer">Value comparer to use when comparing two
250 /// term expressions.</param>
251 /// <param name="identifier">Identifier/tag for this term.</param>
252 internal TermExpr(IEqualityComparer<T_Identifier> comparer, T_Identifier identifier)
255 Debug.Assert(null != (object)identifier);
256 _identifier = identifier;
257 if (null == comparer) { _comparer = EqualityComparer<T_Identifier>.Default; }
258 else { _comparer = comparer; }
260 internal TermExpr(T_Identifier identifier) : this(null, identifier) { }
263 /// Gets identifier for this term. This value is used to determine whether
264 /// two terms as equivalent.
266 internal T_Identifier Identifier { get { return _identifier; } }
268 internal override ExprType ExprType { get { return ExprType.Term; } }
270 public override bool Equals(object obj)
272 Debug.Fail("use only typed equals");
273 return this.Equals(obj as TermExpr<T_Identifier>);
276 public bool Equals(TermExpr<T_Identifier> other)
278 return _comparer.Equals(_identifier, other._identifier);
281 protected override bool EquivalentTypeEquals(BoolExpr<T_Identifier> other)
283 return _comparer.Equals(_identifier, ((TermExpr<T_Identifier>)other)._identifier);
286 public override int GetHashCode()
288 return _comparer.GetHashCode(_identifier);
291 public override string ToString()
293 return StringUtil.FormatInvariant("{0}", _identifier);
296 internal override T_Return Accept<T_Return>(Visitor<T_Identifier, T_Return> visitor)
298 return visitor.VisitTerm(this);
301 internal override BoolExpr<T_Identifier> MakeNegated()
303 Literal<T_Identifier> literal = new Literal<T_Identifier>(this, true);
304 // leverage normalization code if it exists
305 Literal<T_Identifier> negatedLiteral = literal.MakeNegated();
306 if (negatedLiteral.IsTermPositive)
308 return negatedLiteral.Term;
312 return new NotExpr<T_Identifier>(negatedLiteral.Term);
318 /// Abstract base class for tree expressions (unary as in Not, n-ary
319 /// as in And or Or). Duplicate elements are trimmed at construction
320 /// time (algorithms applied to these trees rely on the assumption
321 /// of uniform children).
323 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
324 internal abstract class TreeExpr<T_Identifier> : BoolExpr<T_Identifier>
326 private readonly Set<BoolExpr<T_Identifier>> _children;
327 private readonly int _hashCode;
330 /// Initialize a new tree expression with the given children.
332 /// <param name="children">Child expressions</param>
333 protected TreeExpr(IEnumerable<BoolExpr<T_Identifier>> children)
336 Debug.Assert(null != children);
337 _children = new Set<BoolExpr<T_Identifier>>(children);
338 _children.MakeReadOnly();
339 _hashCode = _children.GetElementsHashCode();
343 /// Gets the children of this expression node.
345 internal Set<BoolExpr<T_Identifier>> Children { get { return _children; } }
347 public override bool Equals(object obj)
349 Debug.Fail("use only typed Equals");
350 return base.Equals(obj as BoolExpr<T_Identifier>);
353 public override int GetHashCode()
358 public override string ToString()
360 return StringUtil.FormatInvariant("{0}({1})", ExprType, _children);
363 protected override bool EquivalentTypeEquals(BoolExpr<T_Identifier> other)
365 return ((TreeExpr<T_Identifier>)other).Children.SetEquals(Children);
370 /// A tree expression that evaluates to true iff. none of its children
371 /// evaluate to false.
374 /// An And expression with no children is equivalent to True (this is an
375 /// operational convenience because we assume an implicit True is along
376 /// for the ride in every And expression)
380 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
381 internal class AndExpr<T_Identifier> : TreeExpr<T_Identifier>
384 /// Initialize a new And expression with the given children.
386 /// <param name="children">Child expressions</param>
387 internal AndExpr(params BoolExpr<T_Identifier>[] children)
388 : this((IEnumerable<BoolExpr<T_Identifier>>)children)
393 /// Initialize a new And expression with the given children.
395 /// <param name="children">Child expressions</param>
396 internal AndExpr(IEnumerable<BoolExpr<T_Identifier>> children)
401 internal override ExprType ExprType { get { return ExprType.And; } }
403 internal override T_Return Accept<T_Return>(Visitor<T_Identifier, T_Return> visitor)
405 return visitor.VisitAnd(this);
410 /// A tree expression that evaluates to true iff. any of its children
411 /// evaluates to true.
414 /// An Or expression with no children is equivalent to False (this is an
415 /// operational convenience because we assume an implicit False is along
416 /// for the ride in every Or expression)
420 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
421 internal class OrExpr<T_Identifier> : TreeExpr<T_Identifier>
424 /// Initialize a new Or expression with the given children.
426 /// <param name="children">Child expressions</param>
427 internal OrExpr(params BoolExpr<T_Identifier>[] children)
428 : this((IEnumerable<BoolExpr<T_Identifier>>)children)
433 /// Initialize a new Or expression with the given children.
435 /// <param name="children">Child expressions</param>
436 internal OrExpr(IEnumerable<BoolExpr<T_Identifier>> children)
441 internal override ExprType ExprType { get { return ExprType.Or; } }
443 internal override T_Return Accept<T_Return>(Visitor<T_Identifier, T_Return> visitor)
445 return visitor.VisitOr(this);
450 /// A tree expression that evaluates to true iff. its (single) child evaluates to false.
452 /// <typeparam name="T_Identifier">The type of leaf term identifiers in this expression.</typeparam>
453 internal sealed class NotExpr<T_Identifier> : TreeExpr<T_Identifier>
456 /// Initialize a new Not expression with the given child.
458 /// <param name="child"></param>
459 internal NotExpr(BoolExpr<T_Identifier> child)
460 : base(new BoolExpr<T_Identifier>[] { child })
464 internal override ExprType ExprType { get { return ExprType.Not; } }
466 internal BoolExpr<T_Identifier> Child { get { return Children.First(); } }
468 internal override T_Return Accept<T_Return>(Visitor<T_Identifier, T_Return> visitor)
470 return visitor.VisitNot(this);
473 public override string ToString()
475 return String.Format(CultureInfo.InvariantCulture, "!{0}", Child);
478 internal override BoolExpr<T_Identifier> MakeNegated()
485 /// Enumeration of Boolean expression node types.
487 internal enum ExprType
489 And, Not, Or, Term, True, False,