/*
* SPDX-FileCopyrightText: 2023 The Refinery Authors
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.store.reasoning.internal;
import tools.refinery.store.query.dnf.Dnf;
import tools.refinery.store.query.dnf.DnfClause;
import tools.refinery.store.query.literal.AbstractCallLiteral;
import tools.refinery.store.query.literal.Literal;
import tools.refinery.store.query.rewriter.AbstractRecursiveRewriter;
import tools.refinery.store.reasoning.interpretation.PartialRelationRewriter;
import tools.refinery.store.reasoning.lifting.DnfLifter;
import tools.refinery.store.reasoning.literal.Concreteness;
import tools.refinery.store.reasoning.literal.ModalConstraint;
import tools.refinery.store.reasoning.literal.Modality;
import tools.refinery.store.reasoning.representation.PartialRelation;
import java.util.*;
class PartialQueryRewriter extends AbstractRecursiveRewriter {
private final DnfLifter lifter;
private final Map relationRewriterMap = new HashMap<>();
PartialQueryRewriter(DnfLifter lifter) {
this.lifter = lifter;
}
public void addRelationRewriter(PartialRelation partialRelation, PartialRelationRewriter interpreter) {
if (relationRewriterMap.put(partialRelation, interpreter) != null) {
throw new IllegalArgumentException("Duplicate partial relation: " + partialRelation);
}
}
@Override
protected Dnf doRewrite(Dnf dnf) {
var builder = Dnf.builderFrom(dnf);
for (var clause : dnf.getClauses()) {
builder.clause(rewriteClause(clause));
}
return builder.build();
}
private List rewriteClause(DnfClause clause) {
var completedLiterals = new ArrayList();
var workList = new ArrayDeque<>(clause.literals());
while (!workList.isEmpty()) {
var literal = workList.removeFirst();
rewrite(literal, completedLiterals, workList);
}
return completedLiterals;
}
private void rewrite(Literal literal, List completedLiterals, Deque workList) {
if (!(literal instanceof AbstractCallLiteral callLiteral)) {
completedLiterals.add(literal);
return;
}
var target = callLiteral.getTarget();
if (target instanceof Dnf dnf) {
rewriteRecursively(callLiteral, dnf, completedLiterals);
} else if (target instanceof ModalConstraint modalConstraint) {
var modality = modalConstraint.modality();
var concreteness = modalConstraint.concreteness();
var constraint = modalConstraint.constraint();
if (constraint instanceof Dnf dnf) {
rewriteRecursively(callLiteral, modality, concreteness, dnf, completedLiterals);
} else if (constraint instanceof PartialRelation partialRelation) {
rewrite(callLiteral, modality, concreteness, partialRelation, workList);
} else {
throw new IllegalArgumentException("Cannot interpret modal constraint: " + modalConstraint);
}
} else {
completedLiterals.add(literal);
}
}
private void rewriteRecursively(AbstractCallLiteral callLiteral, Modality modality, Concreteness concreteness,
Dnf dnf, List completedLiterals) {
var liftedDnf = lifter.lift(modality, concreteness, dnf);
rewriteRecursively(callLiteral, liftedDnf, completedLiterals);
}
private void rewriteRecursively(AbstractCallLiteral callLiteral, Dnf dnf, List completedLiterals) {
var rewrittenDnf = rewrite(dnf);
var rewrittenLiteral = callLiteral.withTarget(rewrittenDnf);
completedLiterals.add(rewrittenLiteral);
}
private void rewrite(AbstractCallLiteral callLiteral, Modality modality, Concreteness concreteness,
PartialRelation partialRelation, Deque workList) {
var rewriter = relationRewriterMap.get(partialRelation);
if (rewriter == null) {
throw new IllegalArgumentException("Do not know how to interpret partial relation: " + partialRelation);
}
var literals = rewriter.rewriteLiteral(callLiteral, modality, concreteness);
int length = literals.size();
for (int i = length - 1; i >= 0; i--) {
workList.addFirst(literals.get(i));
}
}
}