package edu.stanford.nlp.sempre.tables.lambdadcs;
import java.time.YearMonth;
import java.util.*;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.tables.DenotationTypeInference;
import edu.stanford.nlp.sempre.tables.InfiniteListValue;
import edu.stanford.nlp.sempre.tables.lambdadcs.LambdaDCSException.Type;
import fig.basic.*;
/**
* A unary with infinite number of elements such as (>= 4) and * [= anything]
*
* @author ppasupat
*/
public abstract class InfiniteUnaryDenotation extends UnaryDenotation {
public static class Options {
@Option(gloss = "(!= x) only contains things with the same type as x")
public boolean neqMustTypeCheck = true;
}
public static Options opts = new Options();
// Default implementation: calls |contains| on all elements of |c|
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) return false;
}
return true;
}
@Override
public Iterator<Value> iterator() {
throw new LambdaDCSException(Type.infiniteList, "Cannot iterate over an infinite unary");
}
@Override
public Object[] toArray() {
throw new LambdaDCSException(Type.infiniteList, "Cannot convert an infinite unary to array");
}
@Override public <T> T[] toArray(T[] a) {
throw new LambdaDCSException(Type.infiniteList, "Cannot convert an infinite unary to array");
}
@Override
public int size() {
return Integer.MAX_VALUE;
}
@Override
public UnaryDenotation aggregate(AggregateFormula.Mode mode) {
throw new LambdaDCSException(Type.infiniteList, "Cannot use aggregate mode %s on %s", mode, this);
}
@Override
public UnaryDenotation filter(UnaryDenotation that) {
return merge(that, MergeFormula.Mode.and);
}
// Create an InfiniteUnaryDenotation based on the specification
public static InfiniteUnaryDenotation create(String binary, UnaryDenotation second) {
try {
if (ComparisonUnaryDenotation.COMPARATORS.contains(binary)) {
if (second instanceof EverythingUnaryDenotation) {
return (EverythingUnaryDenotation) second;
} else if (second instanceof GenericDateUnaryDenotation) {
if ("<".equals(binary) || ">=".equals(binary))
return new ComparisonUnaryDenotation(binary, ((GenericDateUnaryDenotation) second).getMin());
if (">".equals(binary) || "<=".equals(binary))
return new ComparisonUnaryDenotation(binary, ((GenericDateUnaryDenotation) second).getMax());
}
return new ComparisonUnaryDenotation(binary, DenotationUtils.getSingleValue(second));
}
} catch (LambdaDCSException e) {
throw e;
} catch (Exception e) { }
throw new LambdaDCSException(Type.invalidFormula,
"Cannot create an InfiniteUnaryDenotation: binary = %s, second = %s", binary, second);
}
// ============================================================
// Everything (*)
// ============================================================
static class EverythingUnaryDenotation extends InfiniteUnaryDenotation {
@Override
public LispTree toLispTree() {
LispTree tree = LispTree.proto.newList();
tree.addChild("unary");
tree.addChild("*");
return tree;
}
@Override
public Value toValue() {
return new InfiniteListValue(Arrays.asList("*"));
}
@Override
public boolean contains(Object o) {
return true;
}
@Override
public UnaryDenotation merge(UnaryDenotation that, MergeFormula.Mode mode) {
switch (mode) {
case and: return that;
case or: return this;
default: throw new LambdaDCSException(Type.invalidFormula, "Unknown merge mode: %s", mode);
}
}
}
public static final InfiniteUnaryDenotation STAR_UNARY = new EverythingUnaryDenotation();
// ============================================================
// Comparison
// ============================================================
public static class ComparisonUnaryDenotation extends InfiniteUnaryDenotation {
public static final List<String> COMPARATORS = Arrays.asList("!=", "<", ">", "<=", ">=");
public final String comparator;
public final Value value;
private final DenotationUtils.TypeProcessor valueProcessor;
public ComparisonUnaryDenotation(String comparator, Value value) {
this.comparator = comparator;
this.value = value;
this.valueProcessor = comparator.equals("!=") ? null : DenotationUtils.getTypeProcessor(value);
}
@Override
public LispTree toLispTree() {
LispTree tree = LispTree.proto.newList();
tree.addChild(comparator);
tree.addChild(value.toLispTree());
return tree;
}
@Override
public Value toValue() {
return new InfiniteListValue(Arrays.asList(comparator, value));
}
@Override
public UnaryDenotation merge(UnaryDenotation that, MergeFormula.Mode mode) {
if (that.size() != Integer.MAX_VALUE) {
if (mode == MergeFormula.Mode.and) {
Set<Value> filtered = new HashSet<>();
for (Value value : that)
if (contains(value))
filtered.add(value);
return new ExplicitUnaryDenotation(filtered);
}
} else if (that instanceof EverythingUnaryDenotation || that instanceof RangeUnaryDenotation) {
return that.merge(this, mode);
} else if (mode == MergeFormula.Mode.and && that instanceof InfiniteUnaryDenotation) {
UnaryDenotation answer = RangeEnds.andMerge(this, (InfiniteUnaryDenotation) that);
if (answer != null) return answer;
}
throw new LambdaDCSException(Type.infiniteList, "Cannot use merge mode %s on %s and %s", mode, this, that);
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Value)) return false;
Value that = ((Value) o);
if (comparator.equals("!=")) {
if (InfiniteUnaryDenotation.opts.neqMustTypeCheck) {
return !that.equals(value) && DenotationTypeInference.typeCheck(that, value);
} else {
return !that.equals(value);
}
}
if (!valueProcessor.isCompatible(that))
throw new LambdaDCSException(Type.typeMismatch, "Cannot compare %s with %s", value, that);
int comparison = valueProcessor.compareValues(that, value);
switch (comparator) {
case "<": return comparison < 0;
case ">": return comparison > 0;
case "<=": return comparison <= 0;
case ">=": return comparison >= 0;
default: throw new LambdaDCSException(Type.invalidFormula, "Unknown comparator: %s", comparator);
}
}
}
// ============================================================
// Range
// ============================================================
static class RangeEnds {
public final String leftComparator, rightComparator;
public final Value leftValue, rightValue;
public RangeEnds(String leftComparator, Value leftValue, String rightComparator, Value rightValue) {
this.leftComparator = leftComparator;
this.leftValue = leftValue;
this.rightComparator = rightComparator;
this.rightValue = rightValue;
}
public static RangeEnds getRangeEnds(InfiniteUnaryDenotation x) {
if (x instanceof RangeUnaryDenotation) {
return ((RangeUnaryDenotation) x).rangeEnds;
} else if (x instanceof ComparisonUnaryDenotation) {
ComparisonUnaryDenotation comparison = (ComparisonUnaryDenotation) x;
String leftComparator = ">", rightComparator = "<";
Value leftValue = null, rightValue = null;
switch (comparison.comparator) {
case "<=": rightComparator = "<=";
case "<": rightValue = comparison.value; break;
case ">=": leftComparator = ">=";
case ">": leftValue = comparison.value; break;
default: return null;
}
return new RangeEnds(leftComparator, leftValue, rightComparator, rightValue);
}
return null;
}
// Helper function for performing AND on ComparisonUnaryDenotation or RangeUnaryDenotation
public static UnaryDenotation andMerge(InfiniteUnaryDenotation xDeno, InfiniteUnaryDenotation yDeno) {
RangeEnds x = getRangeEnds(xDeno), y = getRangeEnds(yDeno);
if (x == null || y == null) return null;
String leftComparator, rightComparator;
Value leftValue, rightValue;
int comparison;
// Left
comparison = (x.leftValue == null) ? -1 : (y.leftValue == null) ? +1 :
DenotationUtils.getTypeProcessor(x.leftValue, y.leftValue).compareValues(x.leftValue, y.leftValue);
if (comparison > 0) {
leftValue = x.leftValue; leftComparator = x.leftComparator;
} else if (comparison < 0) {
leftValue = y.leftValue; leftComparator = y.leftComparator;
} else {
leftValue = x.leftValue; leftComparator = (">".equals(x.leftComparator) || ">".equals(y.leftComparator))? ">" : ">=";
}
// Right
comparison = (x.rightValue == null) ? 1 : (y.rightValue == null) ? -1 :
DenotationUtils.getTypeProcessor(x.rightValue, y.rightValue).compareValues(x.rightValue, y.rightValue);
if (comparison < 0) {
rightValue = x.rightValue; rightComparator = x.rightComparator;
} else if (comparison > 0) {
rightValue = y.rightValue; rightComparator = y.rightComparator;
} else {
rightValue = x.rightValue; rightComparator = ("<".equals(x.rightComparator) || "<".equals(y.rightComparator))? "<" : "<=";
}
// Return answer
if (leftValue == null) {
if (rightValue == null) return null;
return new ComparisonUnaryDenotation(rightComparator, rightValue);
} else if (rightValue == null) {
return new ComparisonUnaryDenotation(leftComparator, leftValue);
} else {
comparison = DenotationUtils.getTypeProcessor(leftValue, rightValue).compareValues(leftValue, rightValue);
if (comparison < 0)
return new RangeUnaryDenotation(leftComparator, leftValue, rightComparator, rightValue);
if (comparison == 0 && ">=".equals(leftComparator) && "<=".equals(rightComparator))
return new ExplicitUnaryDenotation(leftValue);
else
return null;
}
}
}
public static class RangeUnaryDenotation extends InfiniteUnaryDenotation {
public final RangeEnds rangeEnds;
private final DenotationUtils.TypeProcessor valueProcessor;
public RangeUnaryDenotation(String leftComparator, Value leftValue, String rightComparator, Value rightValue) {
this.rangeEnds = new RangeEnds(leftComparator, leftValue, rightComparator, rightValue);
this.valueProcessor = DenotationUtils.getTypeProcessor(leftValue, rightValue);
}
@Override
public LispTree toLispTree() {
LispTree tree = LispTree.proto.newList();
tree.addChild("and");
tree.addChild(LispTree.proto.newList(rangeEnds.leftComparator, rangeEnds.leftValue.toLispTree()));
tree.addChild(LispTree.proto.newList(rangeEnds.rightComparator, rangeEnds.rightValue.toLispTree()));
return tree;
}
@Override
public Value toValue() {
return new InfiniteListValue(Arrays.asList(rangeEnds.leftComparator, rangeEnds.leftValue,
rangeEnds.rightComparator, rangeEnds.rightValue));
}
@Override
public UnaryDenotation merge(UnaryDenotation that, MergeFormula.Mode mode) {
if (that.size() != Integer.MAX_VALUE) {
if (mode == MergeFormula.Mode.and) {
Set<Value> filtered = new HashSet<>();
for (Value value : that)
if (contains(value))
filtered.add(value);
return new ExplicitUnaryDenotation(filtered);
}
} else if (that instanceof EverythingUnaryDenotation) {
return that.merge(this, mode);
} else if (mode == MergeFormula.Mode.and && that instanceof InfiniteUnaryDenotation) {
UnaryDenotation answer = RangeEnds.andMerge(this, (InfiniteUnaryDenotation) that);
if (answer != null) return answer;
}
throw new LambdaDCSException(Type.infiniteList, "Cannot use merge mode %s on %s and %s", mode, this, that);
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Value)) return false;
Value that = ((Value) o);
if (!valueProcessor.isCompatible(that))
throw new LambdaDCSException(Type.typeMismatch, "Cannot compare %s and %s with %s", rangeEnds.leftValue, rangeEnds.rightValue, that);
int comparison = valueProcessor.compareValues(that, rangeEnds.leftValue);
switch (rangeEnds.leftComparator) {
case ">": if (comparison <= 0) return false; break;
case ">=": if (comparison < 0) return false; break;
default: throw new LambdaDCSException(Type.invalidFormula, "Unknown leftComparator: %s", rangeEnds.leftComparator);
}
comparison = valueProcessor.compareValues(that, rangeEnds.rightValue);
switch (rangeEnds.rightComparator) {
case "<": if (comparison >= 0) return false; break;
case "<=": if (comparison > 0) return false; break;
default: throw new LambdaDCSException(Type.invalidFormula, "Unknown rightComparator: %s", rangeEnds.rightComparator);
}
return true;
}
}
// ============================================================
// Generic Date (e.g., (date -1 5 -1) in the formula also matches (date -1 5 12) in knowledge graph)
// ============================================================
public static class GenericDateUnaryDenotation extends InfiniteUnaryDenotation {
DateValue date;
public GenericDateUnaryDenotation(DateValue date) {
if (date.year == -1 && date.month == -1 && date.day == -1)
throw new LambdaDCSException(Type.invalidFormula, "Date cannot be (date -1 -1 -1)");
this.date = date;
}
@Override
public LispTree toLispTree() {
return date.toLispTree();
}
@Override
public ListValue toValue() {
return new ListValue(Collections.singletonList(date));
}
@Override
public UnaryDenotation merge(UnaryDenotation that, MergeFormula.Mode mode) {
if (that.size() != Integer.MAX_VALUE) {
if (mode == MergeFormula.Mode.and) {
Set<Value> filtered = new HashSet<>();
for (Value value : that)
if (contains(value))
filtered.add(value);
return new ExplicitUnaryDenotation(filtered);
}
} else if (that instanceof EverythingUnaryDenotation) {
return that.merge(this, mode);
}
throw new LambdaDCSException(Type.infiniteList, "Cannot use merge mode %s on %s and %s", mode, this, that);
}
public DateValue getMin() {
if (date.day != -1)
return date;
if (date.month != -1)
return new DateValue(date.year, date.month, 1);
if (date.year != -1)
return new DateValue(date.year, 1, 1);
throw new LambdaDCSException(Type.unknown, "Invalid date: (-1 -1 -1).");
}
public DateValue getMax() {
if (date.day != -1)
return date;
if (date.month != -1)
return new DateValue(date.year, date.month,
YearMonth.of(date.year == -1 ? 2000 : date.year, date.month).lengthOfMonth());
if (date.year != -1)
return new DateValue(date.year, 12, 31);
throw new LambdaDCSException(Type.unknown, "Invalid date: (-1 -1 -1).");
}
@Override
public boolean contains(Object o) {
if (!(o instanceof DateValue)) return false;
DateValue that = (DateValue) o;
return (date.year == -1 || date.year == that.year) &&
(date.month == -1 || date.month == that.month) &&
(date.day == -1 || date.day == that.day);
}
/**
* If the provided value is a full date like (date 2015 10 21), return an ExplicitUnaryDenotation object.
* If instead it has a placeholder like (date -1 10 21), return a GenericDateUnaryDenotation object.
*/
public static UnaryDenotation get(DateValue value) {
if (value.year != -1 && value.month != -1 && value.day != -1)
return new ExplicitUnaryDenotation(value);
return new GenericDateUnaryDenotation(value);
}
}
// ============================================================
// Test
// ============================================================
public static void main(String[] args) {
//UnaryDenotation x = new RangeUnaryDenotation(">", new NumberValue(2), "<=", new NumberValue(4));
UnaryDenotation x = new ComparisonUnaryDenotation("<=", new NumberValue(10));
LogInfo.logs("%s", x);
//UnaryDenotation y = new RangeUnaryDenotation(">=", new NumberValue(3), "<=", new NumberValue(5));
UnaryDenotation y = new ComparisonUnaryDenotation("<", new NumberValue(4));
LogInfo.logs("%s", y);
UnaryDenotation z = x.merge(y, MergeFormula.Mode.and);
LogInfo.logs("%s", z);
}
}