public SearchArgument.Builder startNot() { ExpressionTree node = new ExpressionTree(ExpressionTree.Operator.NOT); currentTree.Peek().getChildren().Add(node); currentTree.Push(node); return(this); }
/** * Convert an expression so that the top level operator is AND with OR * operators under it. This routine assumes that all of the NOT operators * have been pushed to the leaves via pushdDownNot. * @param root the expression * @return the normalized expression */ internal static ExpressionTree convertToCNF(ExpressionTree root) { if (root.getChildren() != null) { // convert all of the children to CNF int size = root.getChildren().Count; for (int i = 0; i < size; ++i) { root.getChildren()[i] = convertToCNF(root.getChildren()[i]); } if (root.getOperator() == ExpressionTree.Operator.OR) { // a list of leaves that weren't under AND expressions List <ExpressionTree> nonAndList = new List <ExpressionTree>(); // a list of AND expressions that we need to distribute List <ExpressionTree> andList = new List <ExpressionTree>(); foreach (ExpressionTree child in root.getChildren()) { if (child.getOperator() == ExpressionTree.Operator.AND) { andList.Add(child); } else if (child.getOperator() == ExpressionTree.Operator.OR) { // pull apart the kids of the OR expression foreach (ExpressionTree grandkid in child.getChildren()) { nonAndList.Add(grandkid); } } else { nonAndList.Add(child); } } if (andList.Count != 0) { if (checkCombinationsThreshold(andList)) { root = new ExpressionTree(ExpressionTree.Operator.AND); generateAllCombinations(root.getChildren(), andList, nonAndList); } else { root = new ExpressionTree(TruthValue.YES_NO_NULL); } } } } return(root); }
/** * Push the negations all the way to just before the leaves. Also remove * double negatives. * @param root the expression to normalize * @return the normalized expression, which may share some or all of the * nodes of the original expression. */ internal static ExpressionTree pushDownNot(ExpressionTree root) { if (root.getOperator() == ExpressionTree.Operator.NOT) { ExpressionTree child = root.getChildren()[0]; switch (child.getOperator()) { case ExpressionTree.Operator.NOT: return(pushDownNot(child.getChildren()[0])); case ExpressionTree.Operator.CONSTANT: return(new ExpressionTree(child.getConstant().Value.not())); case ExpressionTree.Operator.AND: root = new ExpressionTree(ExpressionTree.Operator.OR); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree(ExpressionTree.Operator.NOT, kid))); } break; case ExpressionTree.Operator.OR: root = new ExpressionTree(ExpressionTree.Operator.AND); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree (ExpressionTree.Operator.NOT, kid))); } break; // for leaf, we don't do anything default: break; } } else if (root.getChildren() != null) { // iterate through children and push down not for each one for (int i = 0; i < root.getChildren().Count; ++i) { root.getChildren()[i] = pushDownNot(root.getChildren()[i]); } } return(root); }
/** * Rewrite expression tree to update the leaves. * @param root the root of the tree to fix * @param leafReorder a map from old leaf ids to new leaf ids * @return the fixed root */ static ExpressionTree rewriteLeaves(ExpressionTree root, int[] leafReorder) { if (root.getOperator() == ExpressionTree.Operator.LEAF) { return(new ExpressionTree(leafReorder[root.getLeaf()])); } else if (root.getChildren() != null) { List <ExpressionTree> children = root.getChildren(); for (int i = 0; i < children.Count; ++i) { children[i] = rewriteLeaves(children[i], leafReorder); } } return(root); }
public SearchArgument.Builder isNull(string column, PredicateLeaf.Type type) { ExpressionTree parent = currentTree.Peek(); if (column == null) { parent.getChildren().Add(new ExpressionTree(TruthValue.YES_NO_NULL)); } else { PredicateLeaf leaf = new PredicateLeafImpl(PredicateLeaf.Operator.IS_NULL, type, column, null, null); parent.getChildren().Add(new ExpressionTree(addLeaf(leaf))); } return(this); }
public SearchArgument.Builder nullSafeEquals(string column, PredicateLeaf.Type type, object literal) { ExpressionTree parent = currentTree.Peek(); if (column == null || literal == null) { parent.getChildren().Add(new ExpressionTree(TruthValue.YES_NO_NULL)); } else { PredicateLeaf leaf = new PredicateLeafImpl(PredicateLeaf.Operator.NULL_SAFE_EQUALS, type, column, literal, null); parent.getChildren().Add(new ExpressionTree(addLeaf(leaf))); } return(this); }
public SearchArgument.Builder end() { ExpressionTree current = currentTree.Pop(); if (current.getChildren().Count == 0) { throw new ArgumentException("Can't create expression " + root + " with no children."); } if (current.getOperator() == ExpressionTree.Operator.NOT && current.getChildren().Count != 1) { throw new ArgumentException("Can't create not expression " + current + " with more than 1 child."); } return(this); }
public ExpressionTree(ExpressionTree other) { this.@operator = other.@operator; if (other.children == null) { this.children = null; } else { this.children = new List<ExpressionTree>(); foreach (ExpressionTree child in other.children) { children.Add(new ExpressionTree(child)); } } this.leaf = other.leaf; this.constant = other.constant; }
public ExpressionTree(ExpressionTree other) { this.@operator = other.@operator; if (other.children == null) { this.children = null; } else { this.children = new List <ExpressionTree>(); foreach (ExpressionTree child in other.children) { children.Add(new ExpressionTree(child)); } } this.leaf = other.leaf; this.constant = other.constant; }
/** * Recursively explore the tree to find the leaves that are still reachable * after optimizations. * @param tree the node to check next * @param next the next available leaf id * @param leafReorder * @return the next available leaf id */ static int compactLeaves(ExpressionTree tree, int next, int[] leafReorder) { if (tree.getOperator() == ExpressionTree.Operator.LEAF) { int oldLeaf = tree.getLeaf(); if (leafReorder[oldLeaf] == -1) { leafReorder[oldLeaf] = next++; } } else if (tree.getChildren() != null) { foreach (ExpressionTree child in tree.getChildren()) { next = compactLeaves(child, next, leafReorder); } } return(next); }
/** * Converts multi-level ands and ors into single level ones. * @param root the expression to flatten * @return the flattened expression, which will always be root with * potentially modified children. */ internal static ExpressionTree flatten(ExpressionTree root) { if (root.getChildren() != null) { // iterate through the index, so that if we add more children, // they don't get re-visited for (int i = 0; i < root.getChildren().Count; ++i) { ExpressionTree child = flatten(root.getChildren()[i]); // do we need to flatten? if (child.getOperator() == root.getOperator() && child.getOperator() != ExpressionTree.Operator.NOT) { bool first = true; foreach (ExpressionTree grandkid in child.getChildren()) { // for the first grandkid replace the original parent if (first) { first = false; root.getChildren()[i] = grandkid; } else { root.getChildren().Insert(++i, grandkid); } } } else { root.getChildren()[i] = child; } } // if we have a singleton AND or OR, just return the child if ((root.getOperator() == ExpressionTree.Operator.OR || root.getOperator() == ExpressionTree.Operator.AND) && root.getChildren().Count == 1) { return(root.getChildren()[0]); } } return(root); }
public SearchArgument.Builder between(string column, PredicateLeaf.Type type, object lower, object upper) { ExpressionTree parent = currentTree.Peek(); if (column == null || lower == null || upper == null) { parent.getChildren().Add(new ExpressionTree(TruthValue.YES_NO_NULL)); } else { List <object> argList = new List <object>(); argList.Add(lower); argList.Add(upper); PredicateLeaf leaf = new PredicateLeafImpl(PredicateLeaf.Operator.BETWEEN, type, column, null, argList); parent.getChildren().Add(new ExpressionTree(addLeaf(leaf))); } return(this); }
/** * Generate all combinations of items on the andList. For each item on the * andList, it generates all combinations of one child from each and * expression. Thus, (and a b) (and c d) will be expanded to: (or a c) * (or a d) (or b c) (or b d). If there are items on the nonAndList, they * are added to each or expression. * @param result a list to put the results onto * @param andList a list of and expressions * @param nonAndList a list of non-and expressions */ private static void generateAllCombinations(List <ExpressionTree> result, List <ExpressionTree> andList, List <ExpressionTree> nonAndList ) { List <ExpressionTree> kids = andList[0].getChildren(); if (result.Count == 0) { foreach (ExpressionTree kid in kids) { ExpressionTree or = new ExpressionTree(ExpressionTree.Operator.OR); result.Add(or); foreach (ExpressionTree node in nonAndList) { or.getChildren().Add(new ExpressionTree(node)); } or.getChildren().Add(kid); } } else { List <ExpressionTree> work = new List <ExpressionTree>(result); result.Clear(); foreach (ExpressionTree kid in kids) { foreach (ExpressionTree or in work) { ExpressionTree copy = new ExpressionTree(or); copy.getChildren().Add(kid); result.Add(copy); } } } if (andList.Count > 1) { generateAllCombinations(result, andList.subList(1, andList.Count), nonAndList); } }
/** * Remove MAYBE values from the expression. If they are in an AND operator, * they are dropped. If they are in an OR operator, they kill their parent. * This assumes that pushDownNot has already been called. * @param expr The expression to clean up * @return The cleaned up expression */ internal static ExpressionTree foldMaybe(ExpressionTree expr) { if (expr.getChildren() != null) { for (int i = 0; i < expr.getChildren().Count; ++i) { ExpressionTree child = foldMaybe(expr.getChildren()[i]); if (child.getConstant() == TruthValue.YES_NO_NULL) { switch (expr.getOperator()) { case ExpressionTree.Operator.AND: expr.getChildren().RemoveAt(i); i -= 1; break; case ExpressionTree.Operator.OR: // a maybe will kill the or condition return(child); default: throw new InvalidOperationException("Got a maybe as child of " + expr); } } else { expr.getChildren()[i] = child; } } if (expr.getChildren().Count == 0) { return(new ExpressionTree(TruthValue.YES_NO_NULL)); } } return(expr); }
public SearchArgument build() { if (currentTree.Count != 1) { throw new ArgumentException("Failed to end " + currentTree.Count + " operations."); } ExpressionTree optimized = pushDownNot(root); optimized = foldMaybe(optimized); optimized = flatten(optimized); optimized = convertToCNF(optimized); optimized = flatten(optimized); int[] leafReorder = new int[leaves.Count]; Arrays.fill(leafReorder, -1); int newLeafCount = compactLeaves(optimized, 0, leafReorder); optimized = rewriteLeaves(optimized, leafReorder); List <PredicateLeaf> leafList = new List <PredicateLeaf>(newLeafCount); // expand list to correct size for (int i = 0; i < newLeafCount; ++i) { leafList.Add(null); } // build the new list foreach (KeyValuePair <PredicateLeaf, int> elem in leaves) { int newLoc = leafReorder[elem.Value]; if (newLoc != -1) { leafList[newLoc] = elem.Key; } } return(new SearchArgumentImpl(optimized, leafList)); }
/** * Recursively explore the tree to find the leaves that are still reachable * after optimizations. * @param tree the node to check next * @param next the next available leaf id * @param leafReorder * @return the next available leaf id */ static int compactLeaves(ExpressionTree tree, int next, int[] leafReorder) { if (tree.getOperator() == ExpressionTree.Operator.LEAF) { int oldLeaf = tree.getLeaf(); if (leafReorder[oldLeaf] == -1) { leafReorder[oldLeaf] = next++; } } else if (tree.getChildren() != null) { foreach (ExpressionTree child in tree.getChildren()) { next = compactLeaves(child, next, leafReorder); } } return next; }
/** * Push the negations all the way to just before the leaves. Also remove * double negatives. * @param root the expression to normalize * @return the normalized expression, which may share some or all of the * nodes of the original expression. */ internal static ExpressionTree pushDownNot(ExpressionTree root) { if (root.getOperator() == ExpressionTree.Operator.NOT) { ExpressionTree child = root.getChildren()[0]; switch (child.getOperator()) { case ExpressionTree.Operator.NOT: return pushDownNot(child.getChildren()[0]); case ExpressionTree.Operator.CONSTANT: return new ExpressionTree(child.getConstant().Value.not()); case ExpressionTree.Operator.AND: root = new ExpressionTree(ExpressionTree.Operator.OR); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree(ExpressionTree.Operator.NOT, kid))); } break; case ExpressionTree.Operator.OR: root = new ExpressionTree(ExpressionTree.Operator.AND); foreach (ExpressionTree kid in child.getChildren()) { root.getChildren().Add(pushDownNot(new ExpressionTree (ExpressionTree.Operator.NOT, kid))); } break; // for leaf, we don't do anything default: break; } } else if (root.getChildren() != null) { // iterate through children and push down not for each one for (int i = 0; i < root.getChildren().Count; ++i) { root.getChildren()[i] = pushDownNot(root.getChildren()[i]); } } return root; }
// Used by kyro SearchArgumentImpl() { leaves = null; expression = null; }
/** * Remove MAYBE values from the expression. If they are in an AND operator, * they are dropped. If they are in an OR operator, they kill their parent. * This assumes that pushDownNot has already been called. * @param expr The expression to clean up * @return The cleaned up expression */ internal static ExpressionTree foldMaybe(ExpressionTree expr) { if (expr.getChildren() != null) { for (int i = 0; i < expr.getChildren().Count; ++i) { ExpressionTree child = foldMaybe(expr.getChildren()[i]); if (child.getConstant() == TruthValue.YES_NO_NULL) { switch (expr.getOperator()) { case ExpressionTree.Operator.AND: expr.getChildren().RemoveAt(i); i -= 1; break; case ExpressionTree.Operator.OR: // a maybe will kill the or condition return child; default: throw new InvalidOperationException("Got a maybe as child of " + expr); } } else { expr.getChildren()[i] = child; } } if (expr.getChildren().Count == 0) { return new ExpressionTree(TruthValue.YES_NO_NULL); } } return expr; }
SearchArgumentImpl(ExpressionTree expression, List<PredicateLeaf> leaves) { this.expression = expression; this.leaves = leaves; }
/** * Converts multi-level ands and ors into single level ones. * @param root the expression to flatten * @return the flattened expression, which will always be root with * potentially modified children. */ internal static ExpressionTree flatten(ExpressionTree root) { if (root.getChildren() != null) { // iterate through the index, so that if we add more children, // they don't get re-visited for (int i = 0; i < root.getChildren().Count; ++i) { ExpressionTree child = flatten(root.getChildren()[i]); // do we need to flatten? if (child.getOperator() == root.getOperator() && child.getOperator() != ExpressionTree.Operator.NOT) { bool first = true; foreach (ExpressionTree grandkid in child.getChildren()) { // for the first grandkid replace the original parent if (first) { first = false; root.getChildren()[i] = grandkid; } else { root.getChildren().Insert(++i, grandkid); } } } else { root.getChildren()[i] = child; } } // if we have a singleton AND or OR, just return the child if ((root.getOperator() == ExpressionTree.Operator.OR || root.getOperator() == ExpressionTree.Operator.AND) && root.getChildren().Count == 1) { return root.getChildren()[0]; } } return root; }
/** * Convert an expression so that the top level operator is AND with OR * operators under it. This routine assumes that all of the NOT operators * have been pushed to the leaves via pushdDownNot. * @param root the expression * @return the normalized expression */ internal static ExpressionTree convertToCNF(ExpressionTree root) { if (root.getChildren() != null) { // convert all of the children to CNF int size = root.getChildren().Count; for (int i = 0; i < size; ++i) { root.getChildren()[i] = convertToCNF(root.getChildren()[i]); } if (root.getOperator() == ExpressionTree.Operator.OR) { // a list of leaves that weren't under AND expressions List<ExpressionTree> nonAndList = new List<ExpressionTree>(); // a list of AND expressions that we need to distribute List<ExpressionTree> andList = new List<ExpressionTree>(); foreach (ExpressionTree child in root.getChildren()) { if (child.getOperator() == ExpressionTree.Operator.AND) { andList.Add(child); } else if (child.getOperator() == ExpressionTree.Operator.OR) { // pull apart the kids of the OR expression foreach (ExpressionTree grandkid in child.getChildren()) { nonAndList.Add(grandkid); } } else { nonAndList.Add(child); } } if (andList.Count != 0) { if (checkCombinationsThreshold(andList)) { root = new ExpressionTree(ExpressionTree.Operator.AND); generateAllCombinations(root.getChildren(), andList, nonAndList); } else { root = new ExpressionTree(TruthValue.YES_NO_NULL); } } } } return root; }
public SearchArgument.Builder startOr() { ExpressionTree node = new ExpressionTree(ExpressionTree.Operator.OR); currentTree.Peek().getChildren().Add(node); currentTree.Push(node); return this; }
private ExpressionTree not(ExpressionTree arg) { return new ExpressionTree(ExpressionTree.Operator.NOT, arg); }
private static void assertNoSharedNodes(ExpressionTree tree, HashSet<ExpressionTree> seen) { if (seen.Contains(tree) && tree.getOperator() != ExpressionTree.Operator.LEAF) { Assert.True(false, "repeated node in expression " + tree); } seen.Add(tree); if (tree.getChildren() != null) { foreach (ExpressionTree child in tree.getChildren()) { assertNoSharedNodes(child, seen); } } }
/** * Generate all combinations of items on the andList. For each item on the * andList, it generates all combinations of one child from each and * expression. Thus, (and a b) (and c d) will be expanded to: (or a c) * (or a d) (or b c) (or b d). If there are items on the nonAndList, they * are added to each or expression. * @param result a list to put the results onto * @param andList a list of and expressions * @param nonAndList a list of non-and expressions */ private static void generateAllCombinations(List<ExpressionTree> result, List<ExpressionTree> andList, List<ExpressionTree> nonAndList ) { List<ExpressionTree> kids = andList[0].getChildren(); if (result.Count == 0) { foreach (ExpressionTree kid in kids) { ExpressionTree or = new ExpressionTree(ExpressionTree.Operator.OR); result.Add(or); foreach (ExpressionTree node in nonAndList) { or.getChildren().Add(new ExpressionTree(node)); } or.getChildren().Add(kid); } } else { List<ExpressionTree> work = new List<ExpressionTree>(result); result.Clear(); foreach (ExpressionTree kid in kids) { foreach (ExpressionTree or in work) { ExpressionTree copy = new ExpressionTree(or); copy.getChildren().Add(kid); result.Add(copy); } } } if (andList.Count > 1) { generateAllCombinations(result, andList.subList(1, andList.Count), nonAndList); } }
/** * Rewrite expression tree to update the leaves. * @param root the root of the tree to fix * @param leafReorder a map from old leaf ids to new leaf ids * @return the fixed root */ static ExpressionTree rewriteLeaves(ExpressionTree root, int[] leafReorder) { if (root.getOperator() == ExpressionTree.Operator.LEAF) { return new ExpressionTree(leafReorder[root.getLeaf()]); } else if (root.getChildren() != null) { List<ExpressionTree> children = root.getChildren(); for (int i = 0; i < children.Count; ++i) { children[i] = rewriteLeaves(children[i], leafReorder); } } return root; }
SearchArgumentImpl(ExpressionTree expression, List <PredicateLeaf> leaves) { this.expression = expression; this.leaves = leaves; }