package context.arch.intelligibility.expression;
import context.arch.intelligibility.expression.Comparison.Relation;
/**
* Expression representing the boolean NOT operation on an expression.
* @author Brian Y. Lim
*
*/
public class Negation implements Expression {
private static final long serialVersionUID = 1296481013575235612L;
/**
* Child expression of the negation.
*/
protected Expression expression;
/**
* Made private, since the more flexible {@link #negate(Expression)} should be used instead,
* especially if the expression argument is already a negation
* @param expression the expression to negate
*/
private Negation(Expression expression) {
this.expression = expression;
}
/**
* Get the child expression of this negation
* @return
*/
public Expression getExpression() {
return expression;
}
@Override
public String toString() {
return "NOT[" + expression + "]";
}
/**
* Negates an expression by wrapping in a Negation expression.
* If the original expression was already negated, then it unwraps it.
* @param expression
* @return
*/
public static Expression negate(Expression expression) {
if (expression instanceof Negation) {
// double negation, so neutralize
return ((Negation)expression).getExpression();
}
return expression = new Negation(expression);
}
/**
* Flip relation through a negation.
* @param relation
* @return
*/
public static Relation negateRelation(Relation relation) {
switch (relation) {
case NO_RELATION: return Relation.NO_RELATION;
case EQUALS: return Relation.NOT_EQUALS;
case NOT_EQUALS: return Relation.EQUALS;
case GREATER_THAN: return Relation.LESS_THAN_OR_EQUAL;
case GREATER_THAN_OR_EQUAL: return Relation.LESS_THAN;
case LESS_THAN: return Relation.GREATER_THAN_OR_EQUAL;
case LESS_THAN_OR_EQUAL: return Relation.GREATER_THAN;
default: return Relation.NO_RELATION;
}
}
/**
* Recursively uses the de Morgan theorem to push NOTs to the leaves.
* Recurses all the way to the leaves and works backwards, i.e. depth-first processing
* @param expression
* @return
*/
public static Expression deMorgan(Expression expression) {
if (expression instanceof Negation) {
return deMorgan((Negation)expression);
}
/*
* For AND and OR, unwrap, apply deMorgan to child expressions, then wrap back
*/
else if (expression instanceof Conjunction) {
Conjunction<Expression> ret = new Conjunction<Expression>();
Conjunction<?> conjunction = (Conjunction<?>)expression;
for (Expression childExp : conjunction) {
ret.add(deMorgan(childExp));
}
return ret;
}
else if (expression instanceof Disjunction) {
Disjunction<Expression> ret = new Disjunction<Expression>();
Disjunction<?> disjunction = (Disjunction<?>)expression;
for (Expression childExp : disjunction) {
ret.add(deMorgan(childExp));
}
return ret;
}
// just return original if any other expression form
return expression;
}
/**
* Apply de Morgan's theorem:
* <ul>
* <li>NOT(a AND b) -> NOT(a) OR NOT(b)</li>
* <li>NOT(a OR b) -> NOT(a) AND NOT(b)</li>
* </ul>
* Recursively does so till the NOT's are at the leaves
*/
protected static Expression deMorgan(Negation negated) {
boolean recurse = true;
Expression expression = negated.getExpression();
if (expression instanceof Conjunction) {
Conjunction<?> conjunction = (Conjunction<?>)expression;
Disjunction<Expression> disjunction = new Disjunction<Expression>();
for (Expression childExp : conjunction) {
childExp = negate(childExp);
if (recurse) { childExp = deMorgan(childExp); }
disjunction.add(childExp);
}
return disjunction;
}
else if (expression instanceof Disjunction) { // similar as for Conjunction, but flips to Disjunction instead
Disjunction<?> disjunction = (Disjunction<?>)expression;
Conjunction<Expression> conjunction = new Conjunction<Expression>();
for (Expression childExp : disjunction) {
childExp = negate(childExp);
if (recurse) { childExp = deMorgan(childExp); }
conjunction.add(childExp);
}
return conjunction;
}
else { // assume terminal but not Value
// combine negated child into a negated expression
Parameter<?> negExpr = Negated.negate((Parameter<?>)negated.getExpression());
return negExpr;
}
}
/**
* Would be the negation of isSatisfiedBy of the childExpression
*/
@Override
public boolean isSatisfiedBy(Expression other) {
return !expression.isSatisfiedBy(other);
}
}