/*
* Kodkod -- Copyright (c) 2005-2008, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.util.nodes;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import kodkod.ast.BinaryFormula;
import kodkod.ast.Formula;
import kodkod.ast.NaryFormula;
import kodkod.ast.Node;
import kodkod.ast.operator.FormulaOperator;
import kodkod.ast.visitor.AbstractDetector;
import kodkod.ast.visitor.AbstractVoidVisitor;
import kodkod.ast.visitor.VoidVisitor;
import kodkod.util.collections.Containers;
import kodkod.util.collections.IdentityHashSet;
/**
* Provides utility methods for extracting roots (top-level) conjuncts
* of Kodkod formulas
*
* @author Emina Torlak
*/
public final class Nodes {
private Nodes() {}
/**
* Returns the roots of the given formula.
* In other words, breaks up the given formula into its conjunctive
* components, {f0, ..., fk},
* such that, for all 0<=i<=k, f<sub>i</sub> is not a conjunction and
* [[f0 && ... && fk]] <=> [[formula]].
* @return subformulas, {f0, ..., fk}, of the given formula such that, for all 0<=i<=k,
* f<sub>i</sub> is not a conjuction and [[f0 && ... && fk]] <=> [[formula]].
*/
public static Set<Formula> roots(Formula formula) {
final List<Formula> formulas = new LinkedList<Formula>();
formulas.add(formula);
final ListIterator<Formula> itr = formulas.listIterator();
while(itr.hasNext()) {
final Formula f = itr.next();
if (f instanceof BinaryFormula) {
final BinaryFormula bin = (BinaryFormula) f;
if (bin.op()==FormulaOperator.AND) {
itr.remove();
itr.add(bin.left());
itr.add(bin.right());
itr.previous();
itr.previous();
}
} else if (f instanceof NaryFormula) {
final NaryFormula nf = (NaryFormula) f;
if (nf.op()==FormulaOperator.AND) {
itr.remove();
for(Formula child : nf) {
itr.add(child);
}
for(int i = nf.size(); i>0; i--) {
itr.previous();
}
}
}
}
return new LinkedHashSet<Formula>(formulas);
}
/**
* Returns an unmodifiable set consisting of the children of the given formula, if the formula is a binary or an nary conjunction. Otherwise
* returns a singleton set containing the formula itself.
* @return an unmodifiable set consisting of children of the given formula, if the formula is a binary or an nary conjunction. Otherwise
* returns a singleton set containing the formula itself.
*/
public static Set<Formula> conjuncts(Formula formula) {
if (formula instanceof BinaryFormula) {
final BinaryFormula bin = (BinaryFormula) formula;
if (bin.op()==FormulaOperator.AND) {
final Formula left = bin.left(), right = bin.right();
if (left==right) return Collections.singleton(left);
else return new AbstractSet<Formula>() {
@Override
public boolean contains(Object o) { return left==o || right==o; }
@Override
public Iterator<Formula> iterator() { return Containers.iterate(left, right); }
@Override
public int size() { return 2; }
};
}
} else if (formula instanceof NaryFormula) {
final NaryFormula nf = (NaryFormula) formula;
if (nf.op()==FormulaOperator.AND) {
final LinkedHashSet<Formula> children = new LinkedHashSet<Formula>(1+(nf.size()*4)/3);
for(Formula child : nf) { children.add(child); }
return Collections.unmodifiableSet(children);
}
}
return Collections.singleton(formula);
}
/**
* Returns a minimal subset of {@linkplain #roots(Formula) roots} of the given formula such that all nodes in the given collection
* are reachable from those roots. The returned subset is a local minimum in that none of its members can be removed without leaving
* some node in the descendants set unreachable from the remaining roots.
* @requires descendants in formula.*components
* @return { s: Set<Formula> | s.elements in roots(formula) and descendants in s.elements.*components and
* no s': Set<Formula> | s.containsAll(s') and s'.size()<s.size() and descendants in s.elements.*components }
* @throws IllegalArgumentException - descendants !in formula.*components
*/
public static Set<Formula> minRoots(Formula formula, Collection<? extends Node> descendants) {
final Set<Node> desc = new IdentityHashSet<Node>(descendants);
final VoidVisitor visitor = new AbstractVoidVisitor() {
final Set<Node> visited = new IdentityHashSet<Node>();
@Override
protected boolean visited(Node n) {
if (visited.add(n)) {
desc.remove(n);
return false;
}
return true;
}
};
final Set<Formula> roots = new LinkedHashSet<Formula>();
for(Formula root : roots(formula)) {
final int size = desc.size();
root.accept(visitor);
if (desc.size()<size) { roots.add(root); }
if (desc.isEmpty()) { break; }
}
if (!desc.isEmpty())
throw new IllegalArgumentException("descendants !in formula.*components: formula="+formula+" ; descendants="+descendants);
return roots;
}
/**
* Returns all {@linkplain #roots(Formula) roots} of the given formula such that a node in the given collection
* is reachable from that root.
* @return { r: roots(formula) | some r.*components & descendants.elements }
*/
@SuppressWarnings("unchecked")
public static Set<Formula> allRoots(Formula formula, Collection<? extends Node> descendants) {
final Set<Node> desc = new IdentityHashSet<Node>(descendants);
final AbstractDetector detector = new AbstractDetector(Collections.EMPTY_SET) {
protected Boolean lookup(Node n) {
return desc.contains(n) ? Boolean.TRUE : cache.get(n);
}
protected Boolean cache(Node n, boolean val) {
final Boolean ret = Boolean.valueOf(val);
cache.put(n, ret);
return ret;
}
};
final Set<Formula> roots = new LinkedHashSet<Formula>();
for(Formula root : roots(formula)) {
if (root.accept(detector)) {
roots.add(root);
}
}
return roots;
}
}