
org.xcsp.common.predicates.MatcherInterface Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xcsp3-tools Show documentation
Show all versions of xcsp3-tools Show documentation
Java Tools for parsing XCSP3 instances, compiling JvCSP3 models, and checking solutions. For more information about XCSP3, follow www.xcsp.org
The newest version!
package org.xcsp.common.predicates;
import static org.xcsp.common.Types.TypeExpr.ABS;
import static org.xcsp.common.Types.TypeExpr.ADD;
import static org.xcsp.common.Types.TypeExpr.EQ;
import static org.xcsp.common.Types.TypeExpr.IN;
import static org.xcsp.common.Types.TypeExpr.LONG;
import static org.xcsp.common.Types.TypeExpr.MAX;
import static org.xcsp.common.Types.TypeExpr.MIN;
import static org.xcsp.common.Types.TypeExpr.MUL;
import static org.xcsp.common.Types.TypeExpr.NE;
import static org.xcsp.common.Types.TypeExpr.NEG;
import static org.xcsp.common.Types.TypeExpr.NOT;
import static org.xcsp.common.Types.TypeExpr.NOTIN;
import static org.xcsp.common.Types.TypeExpr.SET;
import static org.xcsp.common.Types.TypeExpr.SPECIAL;
import static org.xcsp.common.Types.TypeExpr.SQR;
import static org.xcsp.common.Types.TypeExpr.SUB;
import static org.xcsp.common.Types.TypeExpr.VAR;
import static org.xcsp.common.predicates.XNode.node;
import static org.xcsp.common.predicates.XNode.specialLeaf;
import java.util.function.BiPredicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.xcsp.common.IVar;
/**
* This interface is used to test if a specified (source) tree matches a predefined target tree. Some kind of abstraction can be used by means of special nodes.
*
* @author Christophe Lecoutre
*
*/
public interface MatcherInterface {
enum AbstractOperation {
ariop, relop, setop, unalop, symop; // logop
}
XNodeLeaf any = specialLeaf("any");
XNodeLeaf anyc = specialLeaf("anyc"); // any under condition
XNodeLeaf var = specialLeaf("var");
XNodeLeaf val = specialLeaf("val");
XNodeLeaf varOrVal = specialLeaf("var-or-val");
XNodeLeaf any_add_val = specialLeaf("any-add-val");
XNodeLeaf var_add_val = specialLeaf("var-add-val");
XNodeLeaf sub = specialLeaf("sub");
XNodeLeaf not = specialLeaf("not");
XNodeLeaf set_vals = specialLeaf("set-vals");
XNodeLeaf min_vars = specialLeaf("min-vars");
XNodeLeaf max_vars = specialLeaf("max-vars");
XNodeLeaf logic_vars = specialLeaf("logic-vars");
XNodeLeaf add_vars = specialLeaf("add-vars");
XNodeLeaf mul_vars = specialLeaf("mul-vars");
XNodeLeaf add_mul_vals = specialLeaf("add-mul-vals");
XNodeLeaf add_mul_vars = specialLeaf("add-mul-vars");
Matcher x_mul_k = new Matcher(node(MUL, var, val));
Matcher x_mul_y = new Matcher(node(MUL, var, var));
Matcher k_mul_x = new Matcher(node(MUL, val, var)); // used in some other contexts (when non canonized forms)
/**
* Returns the target tree, which may possibly involve some form of abstraction by means of special nodes.
*
* @return the target tree
*/
abstract XNode target();
/**
* Returns {@code true} if the specified node (considered at the specified level/depth) is valid with respect to the target tree when assuming that the
* corresponding node in the target tree is a special node.
*
* @param node
* a (source) node
* @param level
* the level/depth associated with the node
* @return {@code true} if the specified source node is valid with respect to a corresponding special node in the target tree
*/
abstract boolean validForSpecialTargetNode(XNode extends IVar> node, int level);
/**
* Returns {@code true} if the specified source tree matches the specified target tree (at the specified level).
*
* @param source
* the source (sub-)tree
* @param target
* the target (sub-)tree
* @param level
* the level/depth for the comparison
* @return {@code true} if the specified source tree matches the specified target tree
*/
default boolean matching(XNode extends IVar> source, XNode target, int level) {
// System.out.println("matching " + source.type + " vs " + target);
if (target == any) // any node (i.e., full abstract node) => everything matches
return true;
if (target == anyc) // any node under condition (the difference with SPECIAL only, is that sons are not considered recursively)
return validForSpecialTargetNode(source, level);
if (target == var) {
// System.out.println("var " + (source.type == VAR));
return source.type == VAR;
}
if (target == val) {
// System.out.println("val " + (source.type == LONG));
return source.type == LONG;
}
if (target == varOrVal)
return source.type == VAR || source.type == LONG;
if (target == any_add_val)
return source.type == ADD && source.sons.length == 2 && source.sons[1].type == LONG;
if (target == var_add_val)
return source.type == ADD && source.sons.length == 2 && source.sons[0].type == VAR && source.sons[1].type == LONG;
if (target == sub)
return source.type == SUB;
if (target == not)
return source.type == NOT;
if (target == set_vals) // abstract set => we control that source is either an empty set or a set built on only longs
return source.type == SET && (source instanceof XNodeLeaf || Stream.of(source.sons).allMatch(s -> s.type == LONG));
if (target == min_vars) // abstract min => we control that source is a min built on only variables
return source.type == MIN && source.sons.length >= 2 && Stream.of(source.sons).allMatch(s -> s.type == VAR);
if (target == max_vars) // abstract max => we control that source is a max built on only variables
return source.type == MAX && source.sons.length >= 2 && Stream.of(source.sons).allMatch(s -> s.type == VAR);
if (target == logic_vars)
return source.type.isLogicalOperator() && source.sons.length >= 2 && Stream.of(source.sons).allMatch(s -> s.type == VAR);
if (target == add_vars)
return source.type == ADD && source.sons.length >= 2 && Stream.of(source.sons).allMatch(s -> s.type == VAR);
if (target == mul_vars)
return source.type == MUL && source.sons.length >= 2 && Stream.of(source.sons).allMatch(s -> s.type == VAR);
if (target == add_mul_vals)
return source.type == ADD && source.sons.length >= 2 && Stream.of(source.sons).allMatch(s -> s.type == VAR || x_mul_k.matches(s));
if (target == add_mul_vars)
return source.type == ADD && source.sons.length >= 2 && Stream.of(source.sons).allMatch(s -> x_mul_y.matches(s));
if (target instanceof XNodeLeaf != source instanceof XNodeLeaf)
return false;
if (target.type != SPECIAL && target.type != source.type)
return false;
if (target.type == SPECIAL) {
if (target instanceof XNodeParentSpecial) {
AbstractOperation ao = AbstractOperation.valueOf(((XNodeParentSpecial>) target).specialName);
if (ao == AbstractOperation.ariop)
if (!source.type.isArithmeticOperator())
return false;
if (ao == AbstractOperation.relop)
if (!source.type.isRelationalOperator())
return false;
if (ao == AbstractOperation.setop)
if (!source.type.oneOf(IN, NOTIN))
return false;
if (ao == AbstractOperation.unalop)
if (!source.type.oneOf(ABS, NEG, SQR, NOT))
return false;
if (ao == AbstractOperation.symop)
if (!source.type.oneOf(EQ, NE))
return false;
} else if (!validForSpecialTargetNode(source, level))
return false;
}
if (target instanceof XNodeLeaf)
return true; // it seems that we have no more control to do
return target.sons.length == source.sons.length
&& IntStream.range(0, target.sons.length).allMatch(i -> matching(source.sons[i], target.sons[i], level + 1));
}
/**
* Returns {@code true} if the predefined target tree matches the specified (source) tree.
*
* @param tree
* a tree
* @return {@code true} if the predefined target tree matches the specified (source) tree
*/
default boolean matches(XNode extends IVar> tree) {
return matching(tree, target(), 0);
}
/**
* This class allows us to perform matching tests between trees.
*/
class Matcher implements MatcherInterface {
private final XNode target;
private final BiPredicate, Integer> p;
@Override
public XNode target() {
return target;
}
/**
* Builds a {@code Matcher} object with the specified target tree.
*
* @param target
* the target tree
* @param p
* a predicate used for special nodes in some occasions
*/
public Matcher(XNode target, BiPredicate, Integer> p) {
this.target = target;
this.p = p;
}
/**
* Builds a {@code Matcher} object with the specified target tree.
*
* @param target
* the target tree
*/
public Matcher(XNode target) {
this(target, null); // (node, level) -> true); // (level == 0 && node.type.isRelationalOperator()) || (level == 1 &&
// node.type.isArithmeticOperator()));
}
@Override
public boolean validForSpecialTargetNode(XNode extends IVar> node, int level) {
return p == null || p.test(node, level);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy