/******************************************************************************
* Copyright (c) 2009 - 2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
package com.ibm.wala.memsat.util;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import kodkod.ast.BinaryExpression;
import kodkod.ast.BinaryFormula;
import kodkod.ast.BinaryIntExpression;
import kodkod.ast.ComparisonFormula;
import kodkod.ast.ExprToIntCast;
import kodkod.ast.Expression;
import kodkod.ast.Formula;
import kodkod.ast.IfExpression;
import kodkod.ast.IfIntExpression;
import kodkod.ast.IntComparisonFormula;
import kodkod.ast.IntExpression;
import kodkod.ast.IntToExprCast;
import kodkod.ast.NaryExpression;
import kodkod.ast.NaryFormula;
import kodkod.ast.NaryIntExpression;
import kodkod.ast.Node;
import kodkod.ast.NotFormula;
import kodkod.ast.UnaryExpression;
import kodkod.ast.UnaryIntExpression;
import kodkod.ast.operator.ExprCastOperator;
import kodkod.ast.operator.ExprCompOperator;
import kodkod.ast.operator.ExprOperator;
import kodkod.ast.operator.FormulaOperator;
import kodkod.ast.operator.IntCastOperator;
import kodkod.ast.operator.IntCompOperator;
import kodkod.ast.operator.IntOperator;
import kodkod.util.collections.CacheSet;
import kodkod.util.ints.Ints;
/**
* Partially canonicalizes a translation.
* @author Emina Torlak
*/
final class PartialCannonicalizer extends Simplifier {
private final CacheSet<PartialCannonicalizer.Holder<Formula>> formulas;
private final CacheSet<PartialCannonicalizer.Holder<Expression>> exprs;
private final CacheSet<PartialCannonicalizer.Holder<IntExpression>> intExprs;
PartialCannonicalizer() {
exprs = new CacheSet<PartialCannonicalizer.Holder<Expression>>();
formulas = new CacheSet<PartialCannonicalizer.Holder<Formula>>();
intExprs = new CacheSet<PartialCannonicalizer.Holder<IntExpression>>();
}
private static final class Holder<T> {
final T obj; final int hash;
Holder(T obj, int hash) {
this.obj = obj;
this.hash = hash;
}
public int hashCode() { return hash; }
}
/**
* Caches the given replacement for the specified node, if this is
* a caching visitor. Otherwise does nothing. The method returns
* the replacement node.
* @effects this.cache' = this.cache ++ node->replacement,
* @return replacement
*/
protected <N extends Node> N cache(N node, N replacement) {
cache.put(node, replacement);
return replacement;
}
/**
* Returns the super-fast hash of the identity hashcodes of the given objects.
* @return super-fast hash of the identity hashcodes of the given objects.
*/
private static int hash(Object init, Object...rest) {
int hash = Ints.superFastHashIncremental(System.identityHashCode(init), 17);
for(Object obj : rest) {
hash = Ints.superFastHashIncremental(System.identityHashCode(obj), hash);
}
return Ints.superFastHashAvalanche(hash);
}
public Expression visit(IfExpression expr) {
Expression ret = lookup(expr);
if (ret!=null) return ret;
final Formula cond = expr.condition().accept(this);
final Expression thenExpr = expr.thenExpr().accept(this);
final Expression elseExpr = expr.elseExpr().accept(this);
ret = simplify(cond,thenExpr,elseExpr);
if (ret==null) {
final int hash = hash(IfExpression.class, cond, thenExpr, elseExpr);
for(Iterator<PartialCannonicalizer.Holder<Expression>> itr = exprs.get(hash); itr.hasNext(); ) {
final Expression next = itr.next().obj;
if (next.getClass()==IfExpression.class) {
final IfExpression hit = (IfExpression) next;
if (hit.condition()==cond && hit.thenExpr()==thenExpr && hit.elseExpr()==elseExpr) {
return cache(expr, hit);
}
}
}
ret = cond==expr.condition()&&thenExpr==expr.thenExpr()&&elseExpr==expr.elseExpr() ? expr : cond.thenElse(thenExpr, elseExpr);
exprs.add(new PartialCannonicalizer.Holder<Expression>(ret, hash));
}
return cache(expr,ret);
}
public IntExpression visit(IfIntExpression expr) {
IntExpression ret = lookup(expr);
if (ret!=null) return ret;
final Formula cond = expr.condition().accept(this);
final IntExpression thenExpr = expr.thenExpr().accept(this);
final IntExpression elseExpr = expr.elseExpr().accept(this);
ret = simplify(cond,thenExpr,elseExpr);
if (ret==null) {
final int hash = hash(IfIntExpression.class, cond, thenExpr, elseExpr);
for(Iterator<PartialCannonicalizer.Holder<IntExpression>> itr = intExprs.get(hash); itr.hasNext(); ) {
final IntExpression next = itr.next().obj;
if (next.getClass()==IfIntExpression.class) {
final IfIntExpression hit = (IfIntExpression) next;
if (hit.condition()==cond && hit.thenExpr()==thenExpr && hit.elseExpr()==elseExpr) {
return cache(expr, hit);
}
}
}
ret = cond==expr.condition()&&thenExpr==expr.thenExpr()&&elseExpr==expr.elseExpr() ? expr : cond.thenElse(thenExpr, elseExpr);
intExprs.add(new PartialCannonicalizer.Holder<IntExpression>(ret, hash));
}
return cache(expr,ret);
}
public Formula visit(NotFormula not) {
Formula ret = lookup(not);
if (ret!=null) return ret;
final Formula child = not.formula().accept(this);
ret = simplify(child);
if (ret==null) {
final int hash = hash(NotFormula.class, child);
for(Iterator<PartialCannonicalizer.Holder<Formula>> itr = formulas.get(hash); itr.hasNext(); ) {
final Formula next = itr.next().obj;
if (next.getClass()==NotFormula.class) {
if (((NotFormula)next).formula()==child)
return cache(not, next);
}
}
ret = child==not.formula() ? not : child.not();
formulas.add(new PartialCannonicalizer.Holder<Formula>(ret, hash));
}
return cache(not,ret);
}
public Formula visit(ComparisonFormula formula) {
Formula ret = lookup(formula);
if (ret!=null) return ret;
final ExprCompOperator op = formula.op();
final Expression left = formula.left().accept(this);
final Expression right = formula.right().accept(this);
if (left==right) return cache(formula,Formula.TRUE);
final int hash = hash(op, left, right);
for(Iterator<PartialCannonicalizer.Holder<Formula>> itr = formulas.get(hash); itr.hasNext(); ) {
final Formula next = itr.next().obj;
if (next instanceof ComparisonFormula) {
final ComparisonFormula hit = (ComparisonFormula) next;
if (hit.op()==op && hit.left()==left && hit.right()==right) {
return cache(formula, hit);
}
}
}
ret = left==formula.left()&&right==formula.right() ? formula : left.compare(op, right);
formulas.add(new PartialCannonicalizer.Holder<Formula>(ret, hash));
return cache(formula,ret);
}
public Formula visit(IntComparisonFormula formula) {
Formula ret = lookup(formula);
if (ret!=null) return ret;
final IntCompOperator op = formula.op();
final IntExpression left = formula.left().accept(this);
final IntExpression right = formula.right().accept(this);
if (left==right) return cache(formula,Formula.TRUE);
final int hash = hash(op, left, right);
for(Iterator<PartialCannonicalizer.Holder<Formula>> itr = formulas.get(hash); itr.hasNext(); ) {
final Formula next = itr.next().obj;
if (next instanceof IntComparisonFormula) {
final IntComparisonFormula hit = (IntComparisonFormula) next;
if (hit.op()==op && hit.left()==left && hit.right()==right) {
return cache(formula, hit);
}
}
}
ret = left==formula.left()&&right==formula.right() ? formula : left.compare(op, right);
formulas.add(new PartialCannonicalizer.Holder<Formula>(ret, hash));
return cache(formula,ret);
}
public Formula visit(NaryFormula formula) {
Formula ret = lookup(formula);
if (ret!=null) return ret;
final FormulaOperator op = formula.op();
final List<Formula> children = simplify(op, visitAll(op==FormulaOperator.AND ? kodkod.util.nodes.Nodes.roots(formula) : formula));
final int size = children.size();
if (size<2) {
return cache(formula, Formula.compose(op, children));
} else {
final int hash = hash(op, children.toArray());
if (size==2) {
final Formula left = children.get(0), right = children.get(1);
for(Iterator<PartialCannonicalizer.Holder<Formula>> itr = formulas.get(hash); itr.hasNext(); ) {
final Formula next = itr.next().obj;
if (next.getClass() == BinaryFormula.class) {
final BinaryFormula hit = (BinaryFormula) next;
if (hit.op()==op && hit.left()==left && hit.right()==right)
return cache(formula, hit);
}
}
} else {
for(Iterator<PartialCannonicalizer.Holder<Formula>> itr = formulas.get(hash); itr.hasNext(); ) {
final Formula next = itr.next().obj;
if (next.getClass() == NaryFormula.class) {
final NaryFormula hit = (NaryFormula) next;
if (hit.op()==op && hit.size()==size && allSame(hit, children)) {
return cache(formula, hit);
}
}
}
}
ret = formula.size()==size && allSame(formula,children) ? formula : Formula.compose(op, children);
formulas.add(new PartialCannonicalizer.Holder<Formula>(ret, hash));
return cache(formula,ret);
}
}
public Formula visit(BinaryFormula formula) {
Formula ret = lookup(formula);
if (ret!=null) return ret;
final FormulaOperator op = formula.op();
if (op==FormulaOperator.AND) {
final Set<Formula> conjuncts = kodkod.util.nodes.Nodes.roots(formula);
if (conjuncts.size()>2) {
return cache(formula, Formula.and(conjuncts).accept(this));
}
}
final Formula left = formula.left().accept(this);
final Formula right = formula.right().accept(this);
ret = simplify(op, left, right);
if (ret==null) {
final int hash = hash(op, left, right);
for(Iterator<PartialCannonicalizer.Holder<Formula>> itr = formulas.get(hash); itr.hasNext(); ) {
final Formula next = itr.next().obj;
if (next instanceof BinaryFormula) {
final BinaryFormula hit = (BinaryFormula) next;
if (hit.op()==op && hit.left()==left && hit.right()==right) {
return cache(formula, hit);
}
}
}
ret = left==formula.left()&&right==formula.right() ? formula : left.compose(op, right);
formulas.add(new PartialCannonicalizer.Holder<Formula>(ret, hash));
}
return cache(formula,ret);
}
public Expression visit(BinaryExpression expr) {
Expression ret = lookup(expr);
if (ret!=null) return ret;
final ExprOperator op = expr.op();
final Expression left = expr.left().accept(this);
final Expression right = expr.right().accept(this);
ret = simplify(op, left, right);
if (ret==null) {
final int hash = hash(op, left, right);
for(Iterator<PartialCannonicalizer.Holder<Expression>> itr = exprs.get(hash); itr.hasNext(); ) {
final Expression next = itr.next().obj;
if (next instanceof BinaryExpression) {
final BinaryExpression hit = (BinaryExpression) next;
if (hit.op()==op && hit.left()==left && hit.right()==right) {
return cache(expr, hit);
}
}
}
ret = left==expr.left()&&right==expr.right() ? expr : left.compose(op, right);
exprs.add(new PartialCannonicalizer.Holder<Expression>(ret, hash));
}
return cache(expr,ret);
}
public Expression visit(NaryExpression expr) {
Expression ret = lookup(expr);
if (ret!=null) return ret;
final ExprOperator op = expr.op();
final List<Expression> children = simplify(op, visitAll(expr));
final int size = children.size();
switch(size) {
case 0 : return cache(expr, empty(expr.arity()));
case 1 : return cache(expr, children.get(0));
default :
final int hash = hash(op, children.toArray());
if (size==2) {
final Expression left = children.get(0), right = children.get(1);
for(Iterator<PartialCannonicalizer.Holder<Expression>> itr = exprs.get(hash); itr.hasNext(); ) {
final Expression next = itr.next().obj;
if (next.getClass() == BinaryExpression.class) {
final BinaryExpression hit = (BinaryExpression) next;
if (hit.op()==op && hit.left()==left && hit.right()==right) {
return cache(expr, hit);
}
}
}
} else {
for(Iterator<PartialCannonicalizer.Holder<Expression>> itr = exprs.get(hash); itr.hasNext(); ) {
final Expression next = itr.next().obj;
if (next.getClass() == NaryExpression.class) {
final NaryExpression hit = (NaryExpression) next;
if (hit.op()==op && hit.size()==size && allSame(hit,children)) {
return cache(expr, hit);
}
}
}
}
ret = expr.size()==size && allSame(expr,children) ? expr : Expression.compose(op, children);
exprs.add(new PartialCannonicalizer.Holder<Expression>(ret, hash));
return cache(expr,ret);
}
}
public Expression visit(UnaryExpression expr) {
Expression ret = lookup(expr);
if (ret!=null) return ret;
final ExprOperator op = expr.op();
final Expression child = expr.expression().accept(this);
if (isEmpty(child)) return cache(expr, child);
final int hash = hash(op, child);
for(Iterator<PartialCannonicalizer.Holder<Expression>> itr = exprs.get(hash); itr.hasNext(); ) {
final Expression next = itr.next().obj;
if (next.getClass()==UnaryExpression.class) {
if (((UnaryExpression)next).expression()==child)
return cache(expr, next);
}
}
ret = child==expr.expression() ? expr : child.apply(op);
exprs.add(new PartialCannonicalizer.Holder<Expression>(ret, hash));
return cache(expr,ret);
}
public IntExpression visit(UnaryIntExpression expr) {
IntExpression ret = lookup(expr);
if (ret!=null) return ret;
final IntOperator op = expr.op();
final IntExpression child = expr.intExpr().accept(this);
ret = simplify(op, child);
if (ret==null) {
final int hash = hash(op, child);
for(Iterator<PartialCannonicalizer.Holder<IntExpression>> itr = intExprs.get(hash); itr.hasNext(); ) {
final IntExpression next = itr.next().obj;
if (next.getClass()==UnaryIntExpression.class) {
if (((UnaryIntExpression)next).intExpr()==child)
return cache(expr, next);
}
}
ret = child==expr.intExpr() ? expr : child.apply(op);
intExprs.add(new PartialCannonicalizer.Holder<IntExpression>(ret, hash));
}
return cache(expr,ret);
}
public IntExpression visit(ExprToIntCast expr) {
IntExpression ret = lookup(expr);
if (ret!=null) return ret;
final ExprCastOperator op = expr.op();
final Expression child = expr.expression().accept(this);
final int hash = hash(op, child);
for(Iterator<PartialCannonicalizer.Holder<IntExpression>> itr = intExprs.get(hash); itr.hasNext(); ) {
final IntExpression next = itr.next().obj;
if (next.getClass()==ExprToIntCast.class) {
if (((ExprToIntCast)next).expression()==child)
return cache(expr, next);
}
}
ret = child==expr.expression() ? expr : child.apply(op);
intExprs.add(new PartialCannonicalizer.Holder<IntExpression>(ret, hash));
return cache(expr,ret);
}
public Expression visit(IntToExprCast expr) {
Expression ret = lookup(expr);
if (ret!=null) return ret;
final IntCastOperator op = expr.op();
final IntExpression child = expr.intExpr().accept(this);
final int hash = hash(op, child);
for(Iterator<PartialCannonicalizer.Holder<Expression>> itr = exprs.get(hash); itr.hasNext(); ) {
final Expression next = itr.next().obj;
if (next.getClass()==IntToExprCast.class) {
if (((IntToExprCast)next).intExpr()==child)
return cache(expr, next);
}
}
ret = child==expr.intExpr() ? expr : child.cast(op);
exprs.add(new PartialCannonicalizer.Holder<Expression>(ret, hash));
return cache(expr,ret);
}
public IntExpression visit(BinaryIntExpression expr) {
IntExpression ret = lookup(expr);
if (ret!=null) return ret;
final IntOperator op = expr.op();
final IntExpression left = expr.left().accept(this);
final IntExpression right = expr.right().accept(this);
ret = simplify(op, left, right);
if (ret==null) {
final int hash = hash(op, left, right);
for(Iterator<PartialCannonicalizer.Holder<IntExpression>> itr = intExprs.get(hash); itr.hasNext(); ) {
final IntExpression next = itr.next().obj;
if (next instanceof BinaryIntExpression) {
final BinaryIntExpression hit = (BinaryIntExpression) next;
if (hit.op()==op && hit.left()==left && hit.right()==right) {
return cache(expr, hit);
}
}
}
ret = left==expr.left()&&right==expr.right() ? expr : left.compose(op, right);
intExprs.add(new PartialCannonicalizer.Holder<IntExpression>(ret, hash));
}
return cache(expr,ret);
}
public IntExpression visit(NaryIntExpression expr) {
IntExpression ret = lookup(expr);
if (ret!=null) return ret;
final IntOperator op = expr.op();
final List<IntExpression> children = simplify(op, visitAll(expr));
final int size = children.size();
switch(size) {
case 0 : return cache(expr, constant(0));
case 1 : return cache(expr, children.get(0));
default :
final int hash = hash(op, children.toArray());
if (size==2) {
final IntExpression left = children.get(0), right = children.get(1);
for(Iterator<PartialCannonicalizer.Holder<IntExpression>> itr = intExprs.get(hash); itr.hasNext(); ) {
final IntExpression next = itr.next().obj;
if (next.getClass() == BinaryIntExpression.class) {
final BinaryIntExpression hit = (BinaryIntExpression) next;
if (hit.op()==op && hit.left()==left && hit.right()==right)
return cache(expr, hit);
}
}
} else {
for(Iterator<PartialCannonicalizer.Holder<IntExpression>> itr = intExprs.get(hash); itr.hasNext(); ) {
final IntExpression next = itr.next().obj;
if (next.getClass() == NaryIntExpression.class) {
final NaryIntExpression hit = (NaryIntExpression) next;
if (hit.op()==op && hit.size()==size && allSame(hit,children)) {
return cache(expr, hit);
}
}
}
}
ret = expr.size()==size && allSame(expr,children) ? expr : IntExpression.compose(op, children);
intExprs.add(new PartialCannonicalizer.Holder<IntExpression>(ret, hash));
return cache(expr,ret);
}
}
}