package models.terms.meta;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import exceptions.IllegalTypeException;
import exceptions.SubstituteFailedException;
import exceptions.SyntaxException;
import lombok.Getter;
import models.algebra.Expression;
import models.algebra.Symbol;
import models.algebra.Variable;
import models.terms.Dependency;
import models.terms.DependencyTerm;
import models.terms.EvaluatableTerm;
import models.terms.LinearRightNormalizedType;
import models.terms.RDLTerm;
import models.terms.Resource;
import utils.Permutation;
@Getter
public class MetaRDLTerm extends RDLTerm {
protected TermType termType;
protected LinearRightNormalizedType linearRightNormalizedType = LinearRightNormalizedType.UNDEFINED;
protected MetaRDLTerm(Symbol symbol, TermType termType, int size) {
super(symbol, -1, size);
this.termType = termType;
if (isResourceVariable()) {
linearRightNormalizedType = LinearRightNormalizedType.LINEAR_RIGHT_NORMALIZED;
}
}
//dependency
public MetaRDLTerm(MetaRDLTerm dependingTerm, Set<MetaRDLTerm> dependedTerms) {
super(new Symbol(":", 1 + dependedTerms.size()), -1, -1);
int size = dependingTerm.getSize();
addChild(dependingTerm);
for (MetaRDLTerm dependedTerm: new TreeSet<>(dependedTerms)) {
addChild(dependedTerm);
size += dependedTerm.getSize();
}
this.size = size;
this.termType = TermType.META_DEPENDENCY;
}
public MetaRDLTerm(MetaRDLTerm dependingTerm, MetaRDLTerm dependedTerm) {
this(dependingTerm, new TreeSet<>(Set.of(dependedTerm)));
}
//dependency term
public MetaRDLTerm(MetaRDLTerm dependingTerm, List<MetaRDLTerm> terms) {
super(new Symbol(":", terms.size() + 1), -1, -1);
if (! EvaluatableTerm.class.isAssignableFrom(dependingTerm.getTermType().getBaseTermClass())) {
throw new IllegalTypeException();
}
if (terms.size() % 2 != 0) {
throw new SyntaxException("");
}
int size = dependingTerm.getSize();
addChild(dependingTerm);
TreeMap<MetaRDLTerm, MetaRDLTerm> sortedTerms = new TreeMap<>();
for (int i = 0; i < terms.size() / 2; i++) {
MetaRDLTerm dependedTerm = terms.get(2 * i);
MetaRDLTerm argTerm = terms.get(2 * i + 1);
size += dependedTerm.getSize();
size += argTerm.getSize();
if (! EvaluatableTerm.class.isAssignableFrom(dependedTerm.getTermType().getBaseTermClass())) {
throw new IllegalTypeException();
}
if (! EvaluatableTerm.class.isAssignableFrom(argTerm.getTermType().getBaseTermClass())) {
throw new IllegalTypeException();
}
sortedTerms.put(dependedTerm, argTerm);
}
this.size = size;
for (MetaRDLTerm dependedTerm: sortedTerms.keySet()) {
MetaRDLTerm argTerm = sortedTerms.get(dependedTerm);
addChild(dependedTerm);
addChild(argTerm);
}
this.termType = TermType.META_DEPENDENCY_TERM;
}
public MetaRDLTerm(MetaRDLTerm dependingTerm, MetaRDLTerm ...terms) {
this(dependingTerm, Arrays.asList(terms));
}
public RDLTerm substitute(Map<Variable, RDLTerm> binding) {
if (isDependency()) {
RDLTerm dependingTerm = (RDLTerm) getChild(0);
RDLTerm dependedVariable = (RDLTerm) getChild(1);
if (dependingTerm instanceof MetaRDLTerm) {
dependingTerm = ((MetaRDLTerm) dependingTerm).substitute(binding);
}
if (dependedVariable instanceof MetaRDLTerm) {
dependedVariable = ((MetaRDLTerm) dependedVariable).substitute(binding);
}
if (dependedVariable instanceof Resource) {
return new Dependency(dependingTerm, (Resource) dependedVariable);
}
}
else if (isDependencyTerm()) {
RDLTerm dependingTerm = (RDLTerm) getChild(0);
if (dependingTerm instanceof MetaRDLTerm metaDependingTerm) {
dependingTerm = metaDependingTerm.substitute(binding);
}
List<EvaluatableTerm> terms = new ArrayList<>();
for (int i = 1; i < getArity(); i++) {
RDLTerm term = (RDLTerm) getChild(i);
if (term instanceof MetaRDLTerm metaTerm) {
term = metaTerm.substitute(binding);
}
terms.add((EvaluatableTerm) term);
}
return new DependencyTerm((EvaluatableTerm) dependingTerm, terms);
}
throw new SubstituteFailedException();
}
public boolean isVariable() {
return false;
}
public boolean isMatchedBy(RDLTerm another) {
return isMatchedBy(another, new HashMap<>(), new HashMap<>());
}
public boolean isMatchedBy(RDLTerm another, Map<Variable, RDLTerm> binding, Map<Variable, OrderVariableConstraint> orderConstraint) {
if (! another.getClass().isAssignableFrom(this.termType.getBaseTermClass())) {
return false;
}
if (this.getChildren().size() != another.getChildren().size()) {
return false;
}
if (isDependencyTerm() && (! islinearRightNormalizedMatchedBy(another))) {
return false;
}
if (isDependencyTerm() && !isVariable()) {
RDLTerm dependingChild = (RDLTerm) this.getChild(0);
RDLTerm anotherDependingChild = (RDLTerm) another.getChild(0);
if (dependingChild instanceof MetaRDLTerm) {
MetaRDLTerm metaChild = (MetaRDLTerm) dependingChild;
if (! metaChild.isMatchedBy(anotherDependingChild, binding, orderConstraint)) {
return false;
}
} else {
if (!(dependingChild.equals(anotherDependingChild))) {
return false;
}
}
for (List<Integer> perm : Permutation.permutation((getChildren().size() - 1) / 2)) {
Map<Variable, RDLTerm> binding2 = new HashMap<>(binding);
Map<Variable, OrderVariableConstraint> orderConstraint2 = new HashMap<>(orderConstraint);
boolean flg = true;
for (int i = 0; i < (getChildren().size() - 1) / 2; i++) {
int j = perm.get(i);
RDLTerm dependedChild = (RDLTerm) this.getChild(j * 2 + 1);
RDLTerm anotherDependedChild = (RDLTerm) another.getChild(i * 2 + 1);
RDLTerm argChild = (RDLTerm) this.getChild(j * 2 + 2);
RDLTerm anotherArgChild = (RDLTerm) another.getChild(i * 2 + 2);
if (dependedChild instanceof MetaRDLTerm) {
MetaRDLTerm metaChild = (MetaRDLTerm) dependedChild;
if (! metaChild.isMatchedBy(anotherDependedChild, binding2, orderConstraint2)) {
flg = false;
break;
}
} else {
if (!(dependedChild.equals(anotherDependedChild))) {
flg = false;
break;
}
}
if (argChild instanceof MetaRDLTerm) {
MetaRDLTerm metaChild = (MetaRDLTerm) argChild;
if (! metaChild.isMatchedBy(anotherArgChild, binding2, orderConstraint2)) {
flg = false;
break;
}
} else {
if (!(argChild.equals(anotherArgChild))) {
flg = false;
break;
}
}
}
if (flg) {
for (var key: binding2.keySet()) {
binding.put(key, binding2.get(key));
}
for (var key : orderConstraint2.keySet()) {
orderConstraint.put(key, orderConstraint2.get(key));
}
return true;
}
}
return false;
} else if (isDependency() && !isVariable()) {
RDLTerm dependingChild = (RDLTerm) this.getChild(0);
RDLTerm anotherDependingChild = (RDLTerm) another.getChild(0);
if (dependingChild instanceof MetaRDLTerm) {
MetaRDLTerm metaChild = (MetaRDLTerm) dependingChild;
if (! metaChild.isMatchedBy(anotherDependingChild, binding, orderConstraint)) {
return false;
}
} else {
if (!(dependingChild.equals(anotherDependingChild))) {
return false;
}
}
for (List<Integer> perm : Permutation.permutation(getChildren().size() - 1)) {
Map<Variable, RDLTerm> binding2 = new HashMap<>(binding);
Map<Variable, OrderVariableConstraint> orderConstraint2 = new HashMap<>(orderConstraint);
boolean flg = true;
for (int i = 0; i < getChildren().size() - 1; i++) {
int j = perm.get(i);
RDLTerm dependedChild = (RDLTerm) this.getChild(j + 1);
RDLTerm anotherDependedChild = (RDLTerm) another.getChild(i + 1);
if (dependedChild instanceof MetaRDLTerm) {
MetaRDLTerm metaChild = (MetaRDLTerm) dependedChild;
if (! metaChild.isMatchedBy(anotherDependedChild, binding2, orderConstraint2)) {
flg = false;
break;
}
} else {
if (!(dependedChild.equals(anotherDependedChild))) {
flg = false;
break;
}
}
}
if (flg) {
for (var key: binding2.keySet()) {
binding.put(key, binding2.get(key));
}
for (var key : orderConstraint2.keySet()) {
orderConstraint.put(key, orderConstraint2.get(key));
}
return true;
}
}
return false;
} else {
for (int i = 0; i < this.getChildren().size(); i++) {
RDLTerm child = (RDLTerm) this.getChild(i);
RDLTerm anotherChild = (RDLTerm) another.getChild(i);
if (child instanceof MetaRDLTerm) {
MetaRDLTerm metaChild = (MetaRDLTerm) child;
if (! metaChild.isMatchedBy(anotherChild, binding, orderConstraint)) {
return false;
}
} else {
if (!(child.equals(anotherChild))) {
return false;
}
}
}
return true;
}
}
public boolean checkTermType(Class<? extends RDLTerm> clazz) {
return clazz.isAssignableFrom(termType.getBaseTermClass());
}
public boolean isDependency() {
return checkTermType(Dependency.class);
}
public boolean isEvaluatableTerm() {
return checkTermType(EvaluatableTerm.class);
}
public boolean isDependencyTerm() {
return checkTermType(DependencyTerm.class);
}
public boolean isResourceVariable() {
return checkTermType(Resource.class);
}
public void setLinearRightNormalizedType(LinearRightNormalizedType next) {
if (isDependency()) {
return;
}
this.linearRightNormalizedType = next;
}
public Collection<MetaVariable> getAllVariables() {
return getSubTerms(MetaVariable.class).values();
}
protected boolean islinearRightNormalizedMatchedBy(RDLTerm another) {
if (linearRightNormalizedType != LinearRightNormalizedType.UNDEFINED) {
if (another instanceof DependencyTerm) {
if (((DependencyTerm) another).getLinearRightNormalizedType() != linearRightNormalizedType) {
return false;
}
}
else if (another instanceof MetaRDLTerm) {
if (((MetaRDLTerm) another).getLinearRightNormalizedType() != linearRightNormalizedType) {
return false;
}
}
}
return true;
}
@Override
public String toString() {
switch(termType) {
case META_DEPENDENCY:
return "[" + getChild(0).toString() + " : " + getChildren().stream().skip(1).map(Expression::toString).collect(Collectors.joining(",")) + "]";
case META_DEPENDENCY_TERM:
return "[" + getChild(0).toString() + " : " + IntStream.range(0, (getChildren().size() - 1) / 2)
.mapToObj(i -> getChild(i * 2 + 1).toString() + " -> " + getChild(i * 2 + 2)).collect(Collectors.joining(",")) + "]";
default:
return "";
}
}
@Override
public String toStringWithOrder() {
switch(termType) {
case META_DEPENDENCY:
return "[" + ((RDLTerm) getChild(0)).toStringWithOrder() + " : " + ((RDLTerm) getChild(1)).toStringWithOrder() + "]";
case META_DEPENDENCY_LIST:
return "[" + ((RDLTerm) getChild(0)).toStringWithOrder() + "]";
case META_DEPENDENCY_TERM:
return "[" + ((RDLTerm) getChild(0)).toStringWithOrder() + " : "
+ ((RDLTerm) getChild(1)).toStringWithOrder() + " -> " + ((RDLTerm) getChild(2)).toStringWithOrder() + "]";
default:
return "";
}
}
@Override
public boolean equals(Object another) {
if(! (another instanceof MetaRDLTerm)) {
return false;
}
MetaRDLTerm anotherTerm = (MetaRDLTerm) another;
return super.equals(another) && termType == anotherTerm.getTermType();
}
@Override
public int hashCode() {
return (termType.toString() + toStringWithOrder()).hashCode();
}
@Override
public Object clone() {
return null;
}
protected static enum TermType {
META_RDL_TERM(RDLTerm.class),
META_DEPENDENCY(Dependency.class),
META_DEPENDENCY_LIST(Dependency.class),
META_DEPENDENCY_VARIABLE(Dependency.class),
META_DEPENDENCY_TERM(DependencyTerm.class),
META_DEPENDENCY_TERM_VARIABLE(DependencyTerm.class),
META_EVALUATABLE_TERM_VARIABLE(EvaluatableTerm.class),
META_RESOURCE_VARIABLE(Resource.class);
@Getter
private Class<?> baseTermClass;
private TermType(Class<?> clazz) {
this.baseTermClass = clazz;
}
}
}