text:
package org.elasticsearch.xpack.ql.optimizer;

import org.elasticsearch.xpack.ql.expression.Expression;
import org.elasticsearch.xpack.ql.expression.Expressions;
import org.elasticsearch.xpack.ql.expression.Literal;
import org.elasticsearch.xpack.ql.expression.Order;
import org.elasticsearch.xpack.ql.expression.function.scalar.SurrogateFunction;
import org.elasticsearch.xpack.ql.expression.predicate.BinaryOperator;
import org.elasticsearch.xpack.ql.expression.predicate.BinaryPredicate;
import org.elasticsearch.xpack.ql.expression.predicate.Negatable;
import org.elasticsearch.xpack.ql.expression.predicate.Predicates;
import org.elasticsearch.xpack.ql.expression.predicate.Range;
import org.elasticsearch.xpack.ql.expression.predicate.logical.And;
import org.elasticsearch.xpack.ql.expression.predicate.logical.Not;
import org.elasticsearch.xpack.ql.expression.predicate.logical.Or;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.BinaryComparison;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.Equals;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.GreaterThan;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.GreaterThanOrEqual;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.LessThan;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.LessThanOrEqual;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.NotEquals;
import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.NullEquals;
import org.elasticsearch.xpack.ql.plan.logical.Filter;
import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan;
import org.elasticsearch.xpack.ql.plan.logical.OrderBy;
import org.elasticsearch.xpack.ql.rule.Rule;
import org.elasticsearch.xpack.ql.rule.RuleExecutionException;
import org.elasticsearch.xpack.ql.type.DataTypes;
import org.elasticsearch.xpack.ql.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static org.elasticsearch.xpack.ql.expression.Literal.FALSE;
import static org.elasticsearch.xpack.ql.expression.Literal.TRUE;
import static org.elasticsearch.xpack.ql.expression.predicate.Predicates.combineAnd;
import static org.elasticsearch.xpack.ql.expression.predicate.Predicates.combineOr;
import static org.elasticsearch.xpack.ql.expression.predicate.Predicates.inCommon;
import static org.elasticsearch.xpack.ql.expression.predicate.Predicates.splitAnd;
import static org.elasticsearch.xpack.ql.expression.predicate.Predicates.splitOr;
import static org.elasticsearch.xpack.ql.expression.predicate.Predicates.subtract;
import static org.elasticsearch.xpack.ql.util.CollectionUtils.combine;

public final class OptimizerRules {

    public static final class ConstantFolding extends OptimizerExpressionRule {

        public ConstantFolding() {
            super(TransformDirection.DOWN);
        }

        @Override
        public Expression rule(Expression e) {
            return e.foldable() ? Literal.of(e) : e;
        }
    }

    public static final class BooleanSimplification extends OptimizerExpressionRule {

        public BooleanSimplification() {
            super(TransformDirection.UP);
        }

        @Override
        public Expression rule(Expression e) {
            if (e instanceof And || e instanceof Or) {
                return simplifyAndOr((BinaryPredicate<?, ?, ?, ?>) e);
            }
            if (e instanceof Not) {
                return simplifyNot((Not) e);
            }

            return e;
        }

        private Expression simplifyAndOr(BinaryPredicate<?, ?, ?, ?> bc) {
            Expression l = bc.left();
            Expression r = bc.right();

            if (bc instanceof And) {
                if (TRUE.equals(l)) {
                    return r;
                }
                if (TRUE.equals(r)) {
                    return l;
                }

                if (FALSE.equals(l) || FALSE.equals(r)) {
                    return new Literal(bc.source(), Boolean.FALSE, DataTypes.BOOLEAN);
                }
                if (l.semanticEquals(r)) {
                    return l;
                }

                //
                // common factor extraction -> (a || b) && (a || c) => a || (b && c)
                //
                List<Expression> leftSplit = splitOr(l);
                List<Expression> rightSplit = splitOr(r);

                List<Expression> common = inCommon(leftSplit, rightSplit);
                if (common.isEmpty()) {
                    return bc;
                }
                List<Expression> lDiff = subtract(leftSplit, common);
                List<Expression> rDiff = subtract(rightSplit, common);
                // (a || b || c || ... ) && (a || b) => (a || b)
                if (lDiff.isEmpty() || rDiff.isEmpty()) {
                    return combineOr(common);
                }
                // (a || b || c || ... ) && (a || b || d || ... ) => ((c || ...) && (d || ...)) || a || b
                Expression combineLeft = combineOr(lDiff);
                Expression combineRight = combineOr(rDiff);
                return combineOr(combine(common, new And(combineLeft.source(), combineLeft, combineRight)));
            }

            if (bc instanceof Or) {
                if (TRUE.equals(l) || TRUE.equals(r)) {
                    return new Literal(bc.source(), Boolean.TRUE, DataTypes.BOOLEAN);
                }

                if (FALSE.equals(l)) {
                    return r;
                }
                if (FALSE.equals(r)) {
                    return l;
                }

                if (l.semanticEquals(r)) {
                    return l;
                }

                //
                // common factor extraction -> (a && b) || (a && c) => a && (b || c)
                //
                List<Expression> leftSplit = splitAnd(l);
                List<Expression> rightSplit = splitAnd(r);

                List<Expression> common = inCommon(leftSplit, rightSplit);
                if (common.isEmpty()) {
                    return bc;
                }
                List<Expression> lDiff = subtract(leftSplit, common);
                List<Expression> rDiff = subtract(rightSplit, common);
                // (a || b || c || ... ) && (a || b) => (a || b)
                if (lDiff.isEmpty() || rDiff.isEmpty()) {
                    return combineAnd(common);
                }
                // (a || b || c || ... ) && (a || b || d || ... ) => ((c || ...) && (d || ...)) || a || b
                Expression combineLeft = combineAnd(lDiff);
                Expression combineRight = combineAnd(rDiff);
                return combineAnd(combine(common, new Or(combineLeft.source(), combineLeft, combineRight)));
            }

            // TODO: eliminate conjunction/disjunction
            return bc;
        }

        @SuppressWarnings("rawtypes")
        private Expression simplifyNot(Not n) {
            Expression c = n.field();

            if (TRUE.semanticEquals(c)) {
                return new Literal(n.source(), Boolean.FALSE, DataTypes.BOOLEAN);
            }
            if (FALSE.semanticEquals(c)) {
                return new Literal(n.source(), Boolean.TRUE, DataTypes.BOOLEAN);
            }

            if (c instanceof Negatable) {
                return ((Negatable) c).negate();
            }

            if (c instanceof Not) {
                return ((Not) c).field();
            }

            return n;
        }
    }

    public static final class BooleanLiteralsOnTheRight extends OptimizerExpressionRule {

        public BooleanLiteralsOnTheRight() {
            super(TransformDirection.UP);
        }

        @Override
        public Expression rule(Expression e) {
            return e instanceof BinaryOperator ? literalToTheRight((BinaryOperator<?, ?, ?, ?>) e) : e;
        }

        private Expression literalToTheRight(BinaryOperator<?, ?, ?, ?> be) {
            return be.left() instanceof Literal && !(be.right() instanceof Literal) ? be.swapLeftAndRight() : be;
        }
    }

    /**
     * Propagate Equals to eliminate conjuncted Ranges or BinaryComparisons.
     * When encountering a different Equals, non-containing {@link Range} or {@link BinaryComparison}, the conjunction becomes false.
     * When encountering a containing {@link Range}, {@link BinaryComparison} or {@link NotEquals}, these get eliminated by the equality.
     *
     * Since this rule can eliminate Ranges and BinaryComparisons, it should be applied before {@link CombineBinaryComparisons}.
     *
     * This rule doesn't perform any promotion of {@link BinaryComparison}s, that is handled by
     * {@link CombineBinaryComparisons} on purpose as the resulting Range might be foldable
     * (which is picked by the folding rule on the next run).
     */
    public static final class PropagateEquals extends OptimizerExpressionRule {

        public PropagateEquals() {
            super(TransformDirection.DOWN);
        }

        @Override
        public Expression rule(Expression e) {
            if (e instanceof And) {
                return propagate((And) e);
            } else if (e instanceof Or) {
                return propagate((Or) e);
            }
            return e;
        }

        // combine conjunction
        private Expression propagate(And and) {
            List<Range> ranges = new ArrayList<>();
            // Only equalities, not-equalities and inequalities with a foldable .right are extracted separately;
            // the others go into the general 'exps'.
            List<BinaryComparison> equals = new ArrayList<>();
            List<NotEquals> notEquals = new ArrayList<>();
            List<BinaryComparison> inequalities = new ArrayList<>();
            List<Expression> exps = new ArrayList<>();

            boolean changed = false;

            for (Expression ex : Predicates.splitAnd(and)) {
                if (ex instanceof Range) {
                    ranges.add((Range) ex);
                } else if (ex instanceof Equals || ex instanceof NullEquals) {
                    BinaryComparison otherEq = (BinaryComparison) ex;
                    // equals on different values evaluate to FALSE
                    if (otherEq.right().foldable()) {
                        for (BinaryComparison eq : equals) {
                            if (otherEq.left().semanticEquals(eq.left())) {
                                    Integer comp = BinaryComparison.compare(eq.right().fold(), otherEq.right().fold());
                                    if (comp != null) {
                                        // var cannot be equal to two different values at the same time
                                        if (comp != 0) {
                                        return new Literal(and.source(), Boolean.FALSE, DataTypes.BOOLEAN);
                                        }
                                    }
                                }
                            }
                        equals.add(otherEq);
                    } else {
                        exps.add(otherEq);
                        }
                } else if (ex instanceof GreaterThan || ex instanceof GreaterThanOrEqual ||
                    ex instanceof LessThan || ex instanceof LessThanOrEqual) {
                    BinaryComparison bc = (BinaryComparison) ex;
                    if (bc.right().foldable()) {
                        inequalities.add(bc);
                    } else {
                        exps.add(ex);
                    }
                } else if (ex instanceof NotEquals) {
                    NotEquals otherNotEq = (NotEquals) ex;
                    if (otherNotEq.right().foldable()) {
                        notEquals.add(otherNotEq);
                    } else {
                        exps.add(ex);
                    }
                } else {
                    exps.add(ex);
                }
            }

            // check
            for (BinaryComparison eq : equals) {
                Object eqValue = eq.right().fold();

                for (int i = 0; i < ranges.size(); i++) {
                    Range range = ranges.get(i);

                    if (range.value().semanticEquals(eq.left())) {
                        // if equals is outside the interval, evaluate the whole expression to FALSE
                        if (range.lower().foldable()) {
                            Integer compare = BinaryComparison.compare(range.lower().fold(), eqValue);
                            if (compare != null && (
                                 // eq outside the lower boundary
                                 compare > 0 ||
                                 // eq matches the boundary but should not be included
                                 (compare == 0 && !range.includeLower()))
                            ) {
                                return new Literal(and.source(), Boolean.FALSE, DataTypes.BOOLEAN);
                            }
                        }
                        if (range.upper().foldable()) {
                            Integer compare = BinaryComparison.compare(range.upper().fold(), eqValue);
                            if (compare != null && (
                                 // eq outside the upper boundary
                                 compare < 0 ||
                                 // eq matches the boundary but should not be included
                                 (compare == 0 && !range.includeUpper()))
                            ) {
                                return new Literal(and.source(), Boolean.FALSE, DataTypes.BOOLEAN);
                            }
                        }

                        // it's in the range and thus, remove it
                        ranges.remove(i);
                        changed = true;
                    }
                }

                // evaluate all NotEquals against the Equal
                for (Iterator<NotEquals> iter = notEquals.iterator(); iter.hasNext(); ) {
                    NotEquals neq = iter.next();
                    if (eq.left().semanticEquals(neq.left())) {
                        Integer comp = BinaryComparison.compare(eqValue, neq.right().fold());
                        if (comp != null) {
                            if (comp == 0) { // clashing and conflicting: a = 1 AND a != 1
                                return new Literal(and.source(), Boolean.FALSE, DataTypes.BOOLEAN);
                            } else { // clashing and redundant: a = 1 AND a != 2
                                iter.remove();
                                changed = true;
                            }
                        }
                    }
                }

                // evaluate all inequalities against the Equal
                for (Iterator<BinaryComparison> iter = inequalities.iterator(); iter.hasNext(); ) {
                    BinaryComparison bc = iter.next();
                    if (eq.left().semanticEquals(bc.left())) {
                        Integer compare = BinaryComparison.compare(eqValue, bc.right().fold());
                        if (compare != null) {
                            if (bc instanceof LessThan || bc instanceof LessThanOrEqual) { // a = 2 AND a </<= ?
                                if ((compare == 0 && bc instanceof LessThan) || // a = 2 AND a < 2
                                    0 < compare) { // a = 2 AND a </<= 1
                                    return new Literal(and.source(), Boolean.FALSE, DataTypes.BOOLEAN);
                                }
                            } else if (bc instanceof GreaterThan || bc instanceof GreaterThanOrEqual) { // a = 2 AND a >/>= ?
                                if ((compare == 0 && bc instanceof GreaterThan) || // a = 2 AND a > 2
                                    compare < 0) { // a = 2 AND a >/>= 3
                                    return new Literal(and.source(), Boolean.FALSE, DataTypes.BOOLEAN);
                                }
                            }

                            iter.remove();
                            changed = true;
                        }
                    }
                }
            }

            return changed ? Predicates.combineAnd(CollectionUtils.combine(exps, equals, notEquals, inequalities, ranges)) : and;
        }

        // combine disjunction:
        // a = 2 OR a > 3 -> nop; a = 2 OR a > 1 -> a > 1
        // a = 2 OR a < 3 -> a < 3; a = 2 OR a < 1 -> nop
        // a = 2 OR 3 < a < 5 -> nop; a = 2 OR 1 < a < 3 -> 1 < a < 3; a = 2 OR 0 < a < 1 -> nop
        // a = 2 OR a != 2 -> TRUE; a = 2 OR a = 5 -> nop; a = 2 OR a != 5 -> a != 5
        private Expression propagate(Or or) {
            List<Expression> exps = new ArrayList<>();
            List<Equals> equals = new ArrayList<>(); // foldable right term Equals
            List<NotEquals> notEquals = new ArrayList<>(); // foldable right term NotEquals
            List<Range> ranges = new ArrayList<>();
            List<BinaryComparison> inequalities = new ArrayList<>(); // foldable right term (=limit) BinaryComparision

            // split expressions by type
            for (Expression ex : Predicates.splitOr(or)) {
                if (ex instanceof Equals) {
                    Equals eq = (Equals) ex;
                    if (eq.right().foldable()) {
                        equals.add(eq);
                    } else {
                        exps.add(ex);
                    }
                } else if (ex instanceof NotEquals) {
                    NotEquals neq = (NotEquals) ex;
                    if (neq.right().foldable()) {
                        notEquals.add(neq);
                    } else {
                        exps.add(ex);
                    }
                } else if (ex instanceof Range) {
                    ranges.add((Range) ex);
                } else if (ex instanceof BinaryComparison) {
                    BinaryComparison bc = (BinaryComparison) ex;
                    if (bc.right().foldable()) {
                        inequalities.add(bc);
                    } else {
                        exps.add(ex);
                    }
                } else {
                    exps.add(ex);
                }
            }

            boolean updated = false; // has the expression been modified?

            // evaluate the impact of each Equal over the different types of Expressions
            for (Iterator<Equals> iterEq = equals.iterator(); iterEq.hasNext(); ) {
                Equals eq = iterEq.next();
                Object eqValue = eq.right().fold();
                boolean removeEquals = false;

                // Equals OR NotEquals
                for (NotEquals neq : notEquals) {
                    if (eq.left().semanticEquals(neq.left())) { // a = 2 OR a != ? -> ...
                        Integer comp = BinaryComparison.compare(eqValue, neq.right().fold());
                        if (comp != null) {
                            if (comp == 0) { // a = 2 OR a != 2 -> TRUE
                                return TRUE;
                            } else { // a = 2 OR a != 5 -> a != 5
                                removeEquals = true;
                                break;
                            }
                        }
                    }
                }
                if (removeEquals) {
                    iterEq.remove();
                    updated = true;
                    continue;
                }

                // Equals OR Range
                for (int i = 0; i < ranges.size(); i ++) { // might modify list, so use index loop
                    Range range = ranges.get(i);
                    if (eq.left().semanticEquals(range.value())) {
                        Integer lowerComp = range.lower().foldable() ? BinaryComparison.compare(eqValue, range.lower().fold()) : null;
                        Integer upperComp = range.upper().foldable() ? BinaryComparison.compare(eqValue, range.upper().fold()) : null;

                        if (lowerComp != null && lowerComp == 0) {
                            if (!range.includeLower()) { // a = 2 OR 2 < a < ? -> 2 <= a < ?
                                ranges.set(i, new Range(range.source(), range.value(), range.lower(), true,
                                    range.upper(), range.includeUpper()));
                            } // else : a = 2 OR 2 <= a < ? -> 2 <= a < ?
                            removeEquals = true; // update range with lower equality instead or simply superfluous
                            break;
                        } else if (upperComp != null && upperComp == 0) {
                            if (!range.includeUpper()) { // a = 2 OR ? < a < 2 -> ? < a <= 2
                                ranges.set(i, new Range(range.source(), range.value(), range.lower(), range.includeLower(),
                                    range.upper(), true));
                            } // else : a = 2 OR ? < a <= 2 -> ? < a <= 2
                            removeEquals = true; // update range with upper equality instead
                            break;
                        } else if (lowerComp != null && upperComp != null) {
                            if (0 < lowerComp && upperComp < 0) { // a = 2 OR 1 < a < 3
                                removeEquals = true; // equality is superfluous
                                break;
                            }
                        }
                    }
                }
                if (removeEquals) {
                    iterEq.remove();
                    updated = true;
                    continue;
                }

                // Equals OR Inequality
                for (int i = 0; i < inequalities.size(); i ++) {
                    BinaryComparison bc = inequalities.get(i);
                    if (eq.left().semanticEquals(bc.left())) {
                        Integer comp = BinaryComparison.compare(eqValue, bc.right().fold());
                        if (comp != null) {
                            if (bc instanceof GreaterThan || bc instanceof GreaterThanOrEqual) {
                                if (comp < 0) { // a = 1 OR a > 2 -> nop
                                    continue;
                                } else if (comp == 0 && bc instanceof GreaterThan) { // a = 2 OR a > 2 -> a >= 2
                                    inequalities.set(i, new GreaterThanOrEqual(bc.source(), bc.left(), bc.right()));
                                } // else (0 < comp || bc instanceof GreaterThanOrEqual) :
                                // a = 3 OR a > 2 -> a > 2; a = 2 OR a => 2 -> a => 2

                                removeEquals = true; // update range with equality instead or simply superfluous
                                break;
                            } else if (bc instanceof LessThan || bc instanceof LessThanOrEqual) {
                                if (comp > 0) { // a = 2 OR a < 1 -> nop
                                    continue;
                                }
                                if (comp == 0 && bc instanceof LessThan) { // a = 2 OR a < 2 -> a <= 2
                                    inequalities.set(i, new LessThanOrEqual(bc.source(), bc.left(), bc.right()));
                                } // else (comp < 0 || bc instanceof LessThanOrEqual) : a = 2 OR a < 3 -> a < 3; a = 2 OR a <= 2 -> a <= 2
                                removeEquals = true; // update range with equality instead or simply superfluous
                                break;
                            }
                        }
                    }
                }
                if (removeEquals) {
                    iterEq.remove();
                    updated = true;
                }
            }

            return updated ? Predicates.combineOr(CollectionUtils.combine(exps, equals, notEquals, inequalities, ranges)) : or;
        }
    }

    public static final class CombineBinaryComparisons extends OptimizerExpressionRule {

        public CombineBinaryComparisons() {
            super(TransformDirection.DOWN);
        }

        @Override
        public Expression rule(Expression e) {
            if (e instanceof And) {
                return combine((And) e);
            } else if (e instanceof Or) {
                return combine((Or) e);
            }
            return e;
        }

        // combine conjunction
        private Expression combine(And and) {
            List<Range> ranges = new ArrayList<>();
            List<BinaryComparison> bcs = new ArrayList<>();
            List<Expression> exps = new ArrayList<>();

            boolean changed = false;

            List<Expression> andExps = Predicates.splitAnd(and);
            // Ranges need to show up before BinaryComparisons in list, to allow the latter be optimized away into a Range, if possible.
            // NotEquals need to be last in list, to have a complete set of Ranges (ranges) and BinaryComparisons (bcs) and allow these to
            // optimize the NotEquals away.
            andExps.sort((o1, o2) -> {
                if (o1 instanceof Range && o2 instanceof Range) {
                    return 0; // keep ranges' order
                } else if (o1 instanceof Range || o2 instanceof Range) {
                    return o2 instanceof Range ? 1 : -1; // push Ranges down
                } else if (o1 instanceof NotEquals && o2 instanceof NotEquals) {
                    return 0; // keep NotEquals' order
                } else if (o1 instanceof NotEquals || o2 instanceof NotEquals) {
                    return o1 instanceof NotEquals ? 1 : -1; // push NotEquals up
                } else {
                    return 0; // keep non-Ranges' and non-NotEquals' order
                }
            });
            for (Expression ex : andExps) {
                if (ex instanceof Range) {
                    Range r = (Range) ex;
                    if (findExistingRange(r, ranges, true)) {
                        changed = true;
                    } else {
                        ranges.add(r);
                    }
                } else if (ex instanceof BinaryComparison && !(ex instanceof Equals || ex instanceof NotEquals)) {
                    BinaryComparison bc = (BinaryComparison) ex;

                    if (bc.right().foldable() && (findConjunctiveComparisonInRange(bc, ranges) || findExistingComparison(bc, bcs, true))) {
                        changed = true;
                    } else {
                        bcs.add(bc);
                    }
                } else if (ex instanceof NotEquals) {
                    NotEquals neq = (NotEquals) ex;
                    if (neq.right().foldable() && notEqualsIsRemovableFromConjunction(neq, ranges, bcs)) {
                        // the non-equality can simply be dropped: either superfluous or has been merged with an updated range/inequality
                        changed = true;
                    } else { // not foldable OR not overlapping
                        exps.add(ex);
                    }
                } else {
                    exps.add(ex);
                }
            }

            // finally try combining any left BinaryComparisons into possible Ranges
            // this could be a different rule but it's clearer here wrt the order of comparisons

            for (int i = 0, step = 1; i < bcs.size() - 1; i += step, step = 1) {
                BinaryComparison main = bcs.get(i);

                for (int j = i + 1; j < bcs.size(); j++) {
                    BinaryComparison other = bcs.get(j);

                    if (main.left().semanticEquals(other.left())) {
                        // >/>= AND </<=
                        if ((main instanceof GreaterThan || main instanceof GreaterThanOrEqual)
                            && (other instanceof LessThan || other instanceof LessThanOrEqual)) {
                            bcs.remove(j);
                            bcs.remove(i);

                            ranges.add(new Range(and.source(), main.left(),
                                main.right(), main instanceof GreaterThanOrEqual,
                                other.right(), other instanceof LessThanOrEqual));

                            changed = true;
                            step = 0;
                            break;
                        }
                        // </<= AND >/>=
                        else if ((other instanceof GreaterThan || other instanceof GreaterThanOrEqual)
                            && (main instanceof LessThan || main instanceof LessThanOrEqual)) {
                            bcs.remove(j);
                            bcs.remove(i);

                            ranges.add(new Range(and.source(), main.left(),
                                other.right(), other instanceof GreaterThanOrEqual,
                                main.right(), main instanceof LessThanOrEqual));

                            changed = true;
                            step = 0;
                            break;
                        }
                    }
                }
            }

            return changed ? Predicates.combineAnd(CollectionUtils.combine(exps, bcs, ranges)) : and;
        }

        // combine disjunction
        private Expression combine(Or or) {
            List<BinaryComparison> bcs = new ArrayList<>();
            List<Range> ranges = new ArrayList<>();
            List<Expression> exps = new ArrayList<>();

            boolean changed = false;

            for (Expression ex : Predicates.splitOr(or)) {
                if (ex instanceof Range) {
                    Range r = (Range) ex;
                    if (findExistingRange(r, ranges, false)) {
                        changed = true;
                    } else {
                        ranges.add(r);
                    }
                } else if (ex instanceof BinaryComparison) {
                    BinaryComparison bc = (BinaryComparison) ex;
                    if (bc.right().foldable() && findExistingComparison(bc, bcs, false)) {
                        changed = true;
                    } else {
                        bcs.add(bc);
                    }
                } else {
                    exps.add(ex);
                }
            }

            return changed ? Predicates.combineOr(CollectionUtils.combine(exps, bcs, ranges)) : or;
        }

        private static boolean findExistingRange(Range main, List<Range> ranges, boolean conjunctive) {
            if (!main.lower().foldable() && !main.upper().foldable()) {
                return false;
            }
            // NB: the loop modifies the list (hence why the int is used)
            for (int i = 0; i < ranges.size(); i++) {
                Range other = ranges.get(i);

                if (main.value().semanticEquals(other.value())) {

                    // make sure the comparison was done
                    boolean compared = false;

                    boolean lower = false;
                    boolean upper = false;
                    // boundary equality (useful to differentiate whether a range is included or not)
                    // and thus whether it should be preserved or ignored
                    boolean lowerEq = false;
                    boolean upperEq = false;

                    // evaluate lower
                    if (main.lower().foldable() && other.lower().foldable()) {
                        compared = true;

                        Integer comp = BinaryComparison.compare(main.lower().fold(), other.lower().fold());
                        // values are comparable
                        if (comp != null) {
                            // boundary equality
                            lowerEq = comp == 0 && main.includeLower() == other.includeLower();
                            // AND
                            if (conjunctive) {
                                // (2 < a < 3) AND (1 < a < 3) -> (2 < a < 3)
                                lower = comp > 0 ||
                                // (2 < a < 3) AND (2 <= a < 3) -> (2 < a < 3)
                                        (comp == 0 && !main.includeLower() && other.includeLower());
                            }
                            // OR
                            else {
                                // (1 < a < 3) OR (2 < a < 3) -> (1 < a < 3)
                                lower = comp < 0 ||
                                // (2 <= a < 3) OR (2 < a < 3) -> (2 <= a < 3)
                                        (comp == 0 && main.includeLower() && !other.includeLower()) || lowerEq;
                            }
                        }
                    }
                    // evaluate upper
                    if (main.upper().foldable() && other.upper().foldable()) {
                        compared = true;

                        Integer comp = BinaryComparison.compare(main.upper().fold(), other.upper().fold());
                        // values are comparable
                        if (comp != null) {
                            // boundary equality
                            upperEq = comp == 0 && main.includeUpper() == other.includeUpper();

                            // AND
                            if (conjunctive) {
                                // (1 < a < 2) AND (1 < a < 3) -> (1 < a < 2)
                                upper = comp < 0 ||
                                // (1 < a < 2) AND (1 < a <= 2) -> (1 < a < 2)
                                        (comp == 0 && !main.includeUpper() && other.includeUpper());
                            }
                            // OR
                            else {
                                // (1 < a < 3) OR (1 < a < 2) -> (1 < a < 3)
                                upper = comp > 0 ||
                                // (1 < a <= 3) OR (1 < a < 3) -> (2 < a < 3)
                                        (comp == 0 && main.includeUpper() && !other.includeUpper()) || upperEq;
                            }
                        }
                    }

                    // AND - at least one of lower or upper
                    if (conjunctive) {
                        // can tighten range
                        if (lower || upper) {
                            ranges.remove(i);
                            ranges.add(i,
                                    new Range(main.source(), main.value(),
                                            lower ? main.lower() : other.lower(),
                                            lower ? main.includeLower() : other.includeLower(),
                                            upper ? main.upper() : other.upper(),
                                            upper ? main.includeUpper() : other.includeUpper()));
                        }

                        // range was comparable
                        return compared;
                    }
                    // OR - needs both upper and lower to loosen range
                    else {
                        // can loosen range
                        if (lower && upper) {
                            ranges.remove(i);
                            ranges.add(i,
                                    new Range(main.source(), main.value(),
                                            lower ? main.lower() : other.lower(),
                                            lower ? main.includeLower() : other.includeLower(),
                                            upper ? main.upper() : other.upper(),
                                            upper ? main.includeUpper() : other.includeUpper()));
                            return true;
                        }

                        // if the range in included, no need to add it
                        return compared && (!((lower && !lowerEq) || (upper && !upperEq)));
                    }
                }
            }
            return false;
        }

        private boolean findConjunctiveComparisonInRange(BinaryComparison main, List<Range> ranges) {
            Object value = main.right().fold();

            // NB: the loop modifies the list (hence why the int is used)
            for (int i = 0; i < ranges.size(); i++) {
                Range other = ranges.get(i);

                if (main.left().semanticEquals(other.value())) {

                    if (main instanceof GreaterThan || main instanceof GreaterThanOrEqual) {
                        if (other.lower().foldable()) {
                            Integer comp = BinaryComparison.compare(value, other.lower().fold());
                            if (comp != null) {
                                // 2 < a AND (2 <= a < 3) -> 2 < a < 3
                                boolean lowerEq = comp == 0 && other.includeLower() && main instanceof GreaterThan;
                                // 2 < a AND (1 < a < 3) -> 2 < a < 3
                                boolean lower = comp > 0 || lowerEq;

                                if (lower) {
                                    ranges.remove(i);
                                    ranges.add(i,
                                            new Range(other.source(), other.value(),
                                                    main.right(), lowerEq ? false : main instanceof GreaterThanOrEqual,
                                                    other.upper(), other.includeUpper()));
                                }

                                // found a match
                                return true;
                            }
                        }
                    } else if (main instanceof LessThan || main instanceof LessThanOrEqual) {
                        if (other.upper().foldable()) {
                            Integer comp = BinaryComparison.compare(value, other.upper().fold());
                            if (comp != null) {
                                // a < 2 AND (1 < a <= 2) -> 1 < a < 2
                                boolean upperEq = comp == 0 && other.includeUpper() && main instanceof LessThan;
                                // a < 2 AND (1 < a < 3) -> 1 < a < 2
                                boolean upper = comp < 0 || upperEq;

                                if (upper) {
                                    ranges.remove(i);
                                    ranges.add(i, new Range(other.source(), other.value(),
                                            other.lower(), other.includeLower(),
                                            main.right(), upperEq ? false : main instanceof LessThanOrEqual));
                                }

                                // found a match
                                return true;
                            }
                        }
                    }

                    return false;
                }
            }
            return false;
        }

        /**
         * Find commonalities between the given comparison in the given list.
         * The method can be applied both for conjunctive (AND) or disjunctive purposes (OR).
         */
        private static boolean findExistingComparison(BinaryComparison main, List<BinaryComparison> bcs, boolean conjunctive) {
            Object value = main.right().fold();

            // NB: the loop modifies the list (hence why the int is used)
            for (int i = 0; i < bcs.size(); i++) {
                BinaryComparison other = bcs.get(i);
                // skip if cannot evaluate
                if (!other.right().foldable()) {
                    continue;
                }
                // if bc is a higher/lower value or gte vs gt, use it instead
                if ((other instanceof GreaterThan || other instanceof GreaterThanOrEqual) &&
                    (main instanceof GreaterThan || main instanceof GreaterThanOrEqual)) {

                    if (main.left().semanticEquals(other.left())) {
                        Integer compare = BinaryComparison.compare(value, other.right().fold());

                        if (compare != null) {
                            // AND
                            if ((conjunctive &&
                            // a > 3 AND a > 2 -> a > 3
                                    (compare > 0 ||
                                    // a > 2 AND a >= 2 -> a > 2
                                            (compare == 0 && main instanceof GreaterThan && other instanceof GreaterThanOrEqual)))
                                    ||
                                    // OR
                                    (!conjunctive &&
                                    // a > 2 OR a > 3 -> a > 2
                                            (compare < 0 ||
                                            // a >= 2 OR a > 2 -> a >= 2
                                  (compare == 0 && main instanceof GreaterThanOrEqual && other instanceof GreaterThan)))) {
                                bcs.remove(i);
                                bcs.add(i, main);
                            }
                            // found a match
                            return true;
                        }

                        return false;
                    }
                }
                // if bc is a lower/higher value or lte vs lt, use it instead
                else if ((other instanceof LessThan || other instanceof LessThanOrEqual) &&
                        (main instanceof LessThan || main instanceof LessThanOrEqual)) {

                            if (main.left().semanticEquals(other.left())) {
                                Integer compare = BinaryComparison.compare(value, other.right().fold());

                                if (compare != null) {
                                    // AND
                                    if ((conjunctive &&
                                    // a < 2 AND a < 3 -> a < 2
                                    (compare < 0 ||
                                    // a < 2 AND a <= 2 -> a < 2
                                  (compare == 0 && main instanceof LessThan && other instanceof LessThanOrEqual)))
                                ||
                                    // OR
                                    (!conjunctive &&
                                    // a < 2 OR a < 3 -> a < 3
                                    (compare > 0 ||
                                    // a <= 2 OR a < 2 -> a <= 2
                                    (compare == 0 && main instanceof LessThanOrEqual && other instanceof LessThan)))) {
                                        bcs.remove(i);
                                        bcs.add(i, main);

                                    }
                                    // found a match
                                    return true;
                                }

                                return false;
                            }
                        }
            }

            return false;
        }

        private static boolean notEqualsIsRemovableFromConjunction(NotEquals notEquals, List<Range> ranges, List<BinaryComparison> bcs) {
            Object neqVal = notEquals.right().fold();
            Integer comp;

            // check on "condition-overlapping" ranges:
            // a != 2 AND 3 < a < 5 -> 3 < a < 5; a != 2 AND 0 < a < 1 -> 0 < a < 1 (discard NotEquals)
            // a != 2 AND 2 <= a < 3 -> 2 < a < 3; a != 3 AND 2 < a <= 3 -> 2 < a < 3 (discard NotEquals, plus update Range)
            // a != 2 AND 1 < a < 3 -> nop (do nothing)
            for (int i = 0; i < ranges.size(); i ++) {
                Range range = ranges.get(i);

                if (notEquals.left().semanticEquals(range.value())) {
                    comp = range.lower().foldable() ? BinaryComparison.compare(neqVal, range.lower().fold()) : null;
                    if (comp != null) {
                        if (comp <= 0) {
                            if (comp == 0 && range.includeLower()) { // a != 2 AND 2 <= a < ? -> 2 < a < ?
                                ranges.set(i, new Range(range.source(), range.value(), range.lower(), false, range.upper(),
                                    range.includeUpper()));
                            }
                            // else: !.includeLower() : a != 2 AND 2 < a < 3 -> 2 < a < 3; or:
                            // else: comp < 0 : a != 2 AND 3 < a < ? ->  3 < a < ?

                            return true;
                        } else { // comp > 0 : a != 4 AND 2 < a < ? : can only remove NotEquals if outside the range
                            comp = range.upper().foldable() ? BinaryComparison.compare(neqVal, range.upper().fold()) : null;
                            if (comp != null && comp >= 0) {
                                if (comp == 0 && range.includeUpper()) { // a != 4 AND 2 < a <= 4 -> 2 < a < 4
                                    ranges.set(i, new Range(range.source(), range.value(), range.lower(), range.includeLower(),
                                        range.upper(), false));
                                }
                                // else: !.includeUpper() : a != 4 AND 2 < a < 4 -> 2 < a < 4
                                // else: comp > 0 : a != 4 AND 2 < a < 3 -> 2 < a < 3

                                return true;
                            }
                            // else: comp < 0 : a != 4 AND 2 < a < 5 -> nop; or:
                            // else: comp == null : upper bound not comparable -> nop
                        }
                    } // else: comp == null : lower bound not comparable: evaluate upper bound, in case non-equality value is ">="

                    comp = range.upper().foldable() ? BinaryComparison.compare(neqVal, range.upper().fold()) : null;
                    if (comp != null && comp >= 0) {
                        if (comp == 0 && range.includeUpper()) { // a != 3 AND ?? < a <= 3 -> ?? < a < 3
                            ranges.set(i, new Range(range.source(), range.value(), range.lower(), range.includeLower(), range.upper(),
                                false));
                        }
                        // else: !.includeUpper() : a != 3 AND ?? < a < 3 -> ?? < a < 3
                        // else: comp > 0 : a != 3 and ?? < a < 2 -> ?? < a < 2

                        return true;
                    }
                    // else: comp < 0 : a != 3 AND ?? < a < 4 -> nop, as a decision can't be drawn; or:
                    // else: comp == null : a != 3 AND ?? < a < ?? -> nop
                }
            }

            // check on "condition-overlapping" inequalities:
            // a != 2 AND a > 3 -> a > 3 (discard NotEquals)
            // a != 2 AND a >= 2 -> a > 2 (discard NotEquals plus update inequality)
            // a != 2 AND a > 1 -> nop (do nothing)
            //
            // a != 2 AND a < 3 -> nop
            // a != 2 AND a <= 2 -> a < 2
            // a != 2 AND a < 1 -> a < 1
            for (int i = 0; i < bcs.size(); i ++) {
                BinaryComparison bc = bcs.get(i);

                if (bc instanceof LessThan || bc instanceof LessThanOrEqual) {
                    comp = bc.right().foldable() ? BinaryComparison.compare(neqVal, bc.right().fold()) : null;
                    if (comp != null) {
                        if (comp >= 0) {
                            if (comp == 0 && bc instanceof LessThanOrEqual) { // a != 2 AND a <= 2 -> a < 2
                                bcs.set(i, new LessThan(bc.source(), bc.left(), bc.right()));
                            } // else : comp > 0 (a != 2 AND a </<= 1 -> a </<= 1), or == 0 && bc i.of "<" (a != 2 AND a < 2 -> a < 2)
                            return true;
                        } // else: comp < 0 : a != 2 AND a </<= 3 -> nop
                    } // else: non-comparable, nop
                } else if (bc instanceof GreaterThan || bc instanceof GreaterThanOrEqual) {
                    comp = bc.right().foldable() ? BinaryComparison.compare(neqVal, bc.right().fold()) : null;
                    if (comp != null) {
                        if (comp <= 0) {
                            if (comp == 0 && bc instanceof GreaterThanOrEqual) { // a != 2 AND a >= 2 -> a > 2
                                bcs.set(i, new GreaterThan(bc.source(), bc.left(), bc.right()));
                            } // else: comp < 0 (a != 2 AND a >/>= 3 -> a >/>= 3), or == 0 && bc i.of ">" (a != 2 AND a > 2 -> a > 2)
                            return true;
                        } // else: comp > 0 : a != 2 AND a >/>= 1 -> nop
                    } // else: non-comparable, nop
                } // else: other non-relevant type
            }

            return false;
        }

    }

    public static class ReplaceSurrogateFunction extends OptimizerExpressionRule {

        public ReplaceSurrogateFunction() {
            super(TransformDirection.DOWN);
        }

        @Override
        protected Expression rule(Expression e) {
            if (e instanceof SurrogateFunction) {
                e = ((SurrogateFunction) e).substitute();
            }
            return e;
        }
    }

    public static final class PruneFilters extends OptimizerRule<Filter> {

        @Override
        protected LogicalPlan rule(Filter filter) {
            Expression condition = filter.condition().transformUp(PruneFilters::foldBinaryLogic);

            if (condition instanceof Literal) {
                if (TRUE.equals(condition)) {
                    return filter.child();
                }
                if (FALSE.equals(condition) || Expressions.isNull(condition)) {
                    //TODO: re-visit this branch when it's decided if EQL needs a LocalRelation-like class
                    //return new LocalRelation(filter.source(), new EmptyExecutable(filter.output()));
                    throw new RuleExecutionException("Does not know how to handle a local relation");
                }
            }

            if (!condition.equals(filter.condition())) {
                return new Filter(filter.source(), filter.child(), condition);
            }
            return filter;
        }

        private static Expression foldBinaryLogic(Expression expression) {
            if (expression instanceof Or) {
                Or or = (Or) expression;
                boolean nullLeft = Expressions.isNull(or.left());
                boolean nullRight = Expressions.isNull(or.right());
                if (nullLeft && nullRight) {
                    return new Literal(expression.source(), null, DataTypes.NULL);
                }
                if (nullLeft) {
                    return or.right();
                }
                if (nullRight) {
                    return or.left();
                }
            }
            if (expression instanceof And) {
                And and = (And) expression;
                if (Expressions.isNull(and.left()) || Expressions.isNull(and.right())) {
                    return new Literal(expression.source(), null, DataTypes.NULL);
                }
            }
            return expression;
        }
    }

    public static final class PruneLiteralsInOrderBy extends OptimizerRule<OrderBy> {

        @Override
        protected LogicalPlan rule(OrderBy ob) {
            List<Order> prunedOrders = new ArrayList<>();

            for (Order o : ob.order()) {
                if (o.child().foldable()) {
                    prunedOrders.add(o);
                }
            }

            // everything was eliminated, the order isn't needed anymore
            if (prunedOrders.size() == ob.order().size()) {
                return ob.child();
            }
            if (prunedOrders.size() > 0) {
                List<Order> newOrders = new ArrayList<>(ob.order());
                newOrders.removeAll(prunedOrders);
                return new OrderBy(ob.source(), ob.child(), newOrders);
            }

            return ob;
        }
    }

    public static final class SetAsOptimized extends Rule<LogicalPlan, LogicalPlan> {

        @Override
        public LogicalPlan apply(LogicalPlan plan) {
            plan.forEachUp(this::rule);
            return plan;
        }

        @Override
        protected LogicalPlan rule(LogicalPlan plan) {
            if (!plan.optimized()) {
                plan.setOptimized();
            }
            return plan;
        }
    }

    public abstract static class OptimizerRule<SubPlan extends LogicalPlan> extends Rule<SubPlan, LogicalPlan> {

        private final TransformDirection direction;

        public OptimizerRule() {
            this(TransformDirection.DOWN);
        }

        protected OptimizerRule(TransformDirection direction) {
            this.direction = direction;
        }

        @Override
        public final LogicalPlan apply(LogicalPlan plan) {
            return direction == TransformDirection.DOWN ?
                plan.transformDown(this::rule, typeToken()) : plan.transformUp(this::rule, typeToken());
        }

        @Override
        protected abstract LogicalPlan rule(SubPlan plan);
    }

    public abstract static class OptimizerExpressionRule extends Rule<LogicalPlan, LogicalPlan> {

        private final TransformDirection direction;

        public OptimizerExpressionRule(TransformDirection direction) {
            this.direction = direction;
        }

        @Override
        public final LogicalPlan apply(LogicalPlan plan) {
            return direction == TransformDirection.DOWN ? plan.transformExpressionsDown(this::rule) : plan
                    .transformExpressionsUp(this::rule);
        }

        @Override
        protected LogicalPlan rule(LogicalPlan plan) {
            return plan;
        }

        protected abstract Expression rule(Expression e);
    }

    public enum TransformDirection {
        UP, DOWN
    }
}


text:
smartrpyc.client.exceptions
###########################

.. automodule:: smartrpyc.client.exceptions
    :members:
    :undoc-members:

redpajama_set_name: RedPajamaGithub


text:
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/amba/bus.h>

#include <plat/gpio-nomadik.h>

#include <mach/hardware.h>

#include "devices-common.h"

struct amba_device *
dbx500_add_amba_device(const char *name, resource_size_t base,
		       int irq, void *pdata, unsigned int periphid)
{
	struct amba_device *dev;
	int ret;

	dev = kzalloc(sizeof *dev, GFP_KERNEL);
	if (!dev)
		return ERR_PTR(-ENOMEM);

	dev->dev.init_name = name;

	dev->res.start = base;
	dev->res.end = base + SZ_4K - 1;
	dev->res.flags = IORESOURCE_MEM;

	dev->dma_mask = DMA_BIT_MASK(32);
	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);

	dev->irq[0] = irq;
	dev->irq[1] = NO_IRQ;

	dev->periphid = periphid;

	dev->dev.platform_data = pdata;

	ret = amba_device_register(dev, &iomem_resource);
	if (ret) {
		kfree(dev);
		return ERR_PTR(ret);
	}

	return dev;
}

static struct platform_device *
dbx500_add_platform_device(const char *name, int id, void *pdata,
			   struct resource *res, int resnum)
{
	struct platform_device *dev;
	int ret;

	dev = platform_device_alloc(name, id);
	if (!dev)
		return ERR_PTR(-ENOMEM);

	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
	dev->dev.dma_mask = &dev->dev.coherent_dma_mask;

	ret = platform_device_add_resources(dev, res, resnum);
	if (ret)
		goto out_free;

	dev->dev.platform_data = pdata;

	ret = platform_device_add(dev);
	if (ret)
		goto out_free;

	return dev;

out_free:
	platform_device_put(dev);
	return ERR_PTR(ret);
}

struct platform_device *
dbx500_add_platform_device_4k1irq(const char *name, int id,
				  resource_size_t base,
				  int irq, void *pdata)
{
	struct resource resources[] = {
		[0] = {
			.start	= base,
			.end	= base + SZ_4K - 1,
			.flags	= IORESOURCE_MEM,
		},
		[1] = {
			.start	= irq,
			.end	= irq,
			.flags	= IORESOURCE_IRQ,
		}
	};

	return dbx500_add_platform_device(name, id, pdata, resources,
					  ARRAY_SIZE(resources));
}

static struct platform_device *
dbx500_add_gpio(int id, resource_size_t addr, int irq,
		struct nmk_gpio_platform_data *pdata)
{
	struct resource resources[] = {
		{
			.start	= addr,
			.end	= addr + 127,
			.flags	= IORESOURCE_MEM,
		},
		{
			.start	= irq,
			.end	= irq,
			.flags	= IORESOURCE_IRQ,
		}
	};

	return platform_device_register_resndata(NULL, "gpio", id,
				resources, ARRAY_SIZE(resources),
				pdata, sizeof(*pdata));
}

void dbx500_add_gpios(resource_size_t *base, int num, int irq,
		      struct nmk_gpio_platform_data *pdata)
{
	int first = 0;
	int i;

	for (i = 0; i < num; i++, first += 32, irq++) {
		pdata->first_gpio = first;
		pdata->first_irq = NOMADIK_GPIO_TO_IRQ(first);
		pdata->num_gpio = 32;

		dbx500_add_gpio(i, base[i], irq, pdata);
	}
}


text:
/*-------------------------------------------------------------------------//
//                                                                         //
//      INCLUDES                                                           //
//                                                                         //
//-------------------------------------------------------------------------*/

#ifndef GUCEF_CORE_DVMD5UTILS_H
#include "dvmd5utils.h"
#define GUCEF_CORE_DVMD5UTILS_H
#endif /* GUCEF_CORE_DVMD5UTILS_H ? */

#ifndef GUCEF_CORE_DVFILEUTILS_H
#include "dvfileutils.h"
#define GUCEF_CORE_DVFILEUTILS_H
#endif /* GUCEF_CORE_DVFILEUTILS_H ? */

#ifndef GUCEF_CORE_DVCPPFILEUTILS_H
#include "dvcppfileutils.h"
#define GUCEF_CORE_DVCPPFILEUTILS_H
#endif /* GUCEF_CORE_DVCPPFILEUTILS_H ? */

#ifndef GUCEF_CORE_CLOGMANAGER_H
#include "CLogManager.h"
#define GUCEF_CORE_CLOGMANAGER_H
#endif /* GUCEF_CORE_CLOGMANAGER_H ? */

#ifndef GUCEF_CORE_DVCPPSTRINGUTILS_H
#include "dvcppstringutils.h"
#define GUCEF_CORE_DVCPPSTRINGUTILS_H
#endif /* GUCEF_CORE_DVCPPSTRINGUTILS_H ? */

#ifndef GUCEF_CORE_CFILEACCESS_H
#include "CFileAccess.h"
#define GUCEF_CORE_CFILEACCESS_H
#endif /* GUCEF_CORE_CFILEACCESS_H ? */

#include "gucefPATCHER_CPatchSetFileEngine.h"

/*-------------------------------------------------------------------------//
//                                                                         //
//      NAMESPACE                                                          //
//                                                                         //
//-------------------------------------------------------------------------*/

namespace GUCEF {
namespace PATCHER {

/*-------------------------------------------------------------------------//
//                                                                         //
//      UTILITIES                                                          //
//                                                                         //
//-------------------------------------------------------------------------*/

CPatchSetFileEngine::CPatchSetFileEngine( void )
    : CObservingNotifier()               ,
      CPatchSetFileEngineEvents()        ,
      m_isActive( false )                ,
      m_stopSignalGiven( false )         ,
      m_localRoot()                      ,
      m_tempStorageRoot()                ,
      m_dataRetriever()                  ,
      m_fileAccess( NULL )               ,
      m_fileList()                       ,
      m_curFileLocIndex( 0 )             ,
      m_curFileIndex( 0 )                ,
      m_totalBytesProcessed( 0 )         ,
      m_currentFileReceivedBytes( 0 )    ,
      m_stopOnReplacementFailure( true )
{GUCEF_TRACE;

    SubscribeTo( &m_dataRetriever );
}

/*-------------------------------------------------------------------------*/

CPatchSetFileEngine::CPatchSetFileEngine( const CORE::PulseGeneratorPtr& pulseGenerator )
    : CObservingNotifier()               ,
      CPatchSetFileEngineEvents()        ,
      m_isActive( false )                ,
      m_stopSignalGiven( false )         ,
      m_localRoot()                      ,
      m_tempStorageRoot()                ,
      m_dataRetriever( pulseGenerator )  ,
      m_fileAccess( NULL )               ,
      m_fileList()                       ,
      m_curFileLocIndex( 0 )             ,
      m_curFileIndex( 0 )                ,
      m_totalBytesProcessed( 0 )         ,
      m_currentFileReceivedBytes( 0 )    ,
      m_stopOnReplacementFailure( true )
{GUCEF_TRACE;

    SubscribeTo( &m_dataRetriever );
}

/*-------------------------------------------------------------------------*/

CPatchSetFileEngine::~CPatchSetFileEngine()
{GUCEF_TRACE;

}

/*-------------------------------------------------------------------------*/

CPatchSetFileEngine::TPatchSetFileEngineEventData*
CPatchSetFileEngine::CreateEventStatusObj( void ) const
{GUCEF_TRACE;

    TPatchSetFileEngineEventDataStorage storage;
    if ( m_fileList.size() > m_curFileIndex )
    {
        storage.currentFileEntry = m_fileList[ m_curFileIndex ];
        storage.currentRemoteLocationIndex = m_curFileLocIndex;
        storage.localRoot = m_localRoot;
        storage.tempStorageRoot = m_tempStorageRoot;
        storage.totalBytesProcessed = m_totalBytesProcessed;
    }

    return new TPatchSetFileEngineEventData( storage );
}

/*-------------------------------------------------------------------------*/

bool
CPatchSetFileEngine::Start( const TFileList& fileList            ,
                            const CORE::CString& localRoot       ,
                            const CORE::CString& tempStorageRoot ,
                            const bool stopOnReplacementFailure  )

{GUCEF_TRACE;

    // The user should explicitly stop first if we are already busy
    if ( !IsActive() )
    {
        GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Starting inactive engine" );

        // parameter sanity check
        if ( localRoot.Length() > 0 )
        {
            // Check if we even need to do anything
            if ( fileList.size() > 0 )
            {
                m_isActive = true;
                m_stopSignalGiven = false;
                m_totalBytesProcessed = 0;
                m_currentFileReceivedBytes = 0;
                m_stopOnReplacementFailure = stopOnReplacementFailure;
                m_fileList = fileList;
                m_localRoot = localRoot;
                m_tempStorageRoot = tempStorageRoot;
                m_curFileIndex = 0;
                m_curFileLocIndex = 0;

                NotifyObservers( FileListProcessingStartedEvent, CreateEventStatusObj() );

                // Set things in motion
                return ProcessCurrentFile();
            }
            else
            {
                // There are no files to process,.. so we are done
                NotifyObservers( FileListProcessingStartedEvent, CreateEventStatusObj() );
                NotifyObservers( FileListProcessingCompleteEvent, CreateEventStatusObj() );
                return true;
            }
        }
    }

    return false;
}

/*-------------------------------------------------------------------------*/

bool
CPatchSetFileEngine::ProcessCurrentFile( void )
{GUCEF_TRACE;

    GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Processing the current file" );

    if ( !m_stopSignalGiven                 &&
         m_isActive                         &&
         m_fileList.size() > m_curFileIndex )
    {
        // Get our current file
        const TFileEntry* curFile = &m_fileList[ m_curFileIndex ];

        // Create a path string to the file
        CORE::CString filePath = m_localRoot;
        CORE::AppendToPath( filePath, curFile->name );

        // Check if the file exists
        if ( CORE::FileExists( filePath ) )
        {
            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): There is already a file with the same name as the new file in the destination folder" );

            // The file exists, let's see if it's up to date
            UInt32 fileSize = CORE::Filesize( filePath.C_String() );
            if ( fileSize == curFile->sizeInBytes )
            {
                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): The old file is the same size as the new file so we will have to do a hash check" );

                // The file matches in byte size, it may be up-to-date
                // We will try a hash comparison to make sure
                UInt8 md5Digest[ 16 ];
                CORE::CFileAccess fileAccess( filePath, "rb" );
                fileAccess.Open();
                if ( fileAccess.Opened() && fileAccess.IsValid() )
                {
                    if ( 0 != CORE::md5fromfile( fileAccess.CStyleAccess() ,
                                                 md5Digest                 ) )
                    {
                        GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Finished old file hash generation" );

                        // This may have been a lengthy operation so we re-check the stop signal
                        if ( m_stopSignalGiven || !m_isActive )
                        {
                            m_stopSignalGiven = false;
                            m_isActive = false;
                            return false;
                        }

                        // We have now generated an md5 hash from the file
                        // We will now compare it to the one an up-to-date file should have
                        CORE::CString md5String( CORE::MD5ToString( md5Digest ) );
                        if ( curFile->hash == md5String )
                        {
                            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): The old file has the same hash as the new file so it is already up-to-date" );

                            // Woohoo, this file is already up-to-date
                            m_totalBytesProcessed += curFile->sizeInBytes;
                            NotifyObservers( LocalFileIsOKEvent, CreateEventStatusObj() );

                            // We can now proceed to the next file if there is any
                            if ( m_curFileIndex+1 < m_fileList.size() )
                            {
                                ++m_curFileIndex;
                                return ProcessCurrentFile();
                            }
                            else
                            {
                                // We are finished,.. engine stopping
                                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Depleted list of files to process, we are finished" );
                                NotifyObservers( FileListProcessingCompleteEvent, CreateEventStatusObj() );
                                m_isActive = false;
                            }
                            return true;
                        }
                        else
                        {
                            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): The hash of the remote file does not match that of the local file" );
                            NotifyObservers( LocalFileHashMismatchEvent, CreateEventStatusObj() );
                        }
                    }
                    else
                    {
                        GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Failed to generate hash from the old file so we treat this as a hash mismatch" );
                        NotifyObservers( LocalFileHashMismatchEvent, CreateEventStatusObj() );
                    }
                }
            }
            else
            {
                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): The local file has a different size then the remote file" );
                NotifyObservers( LocalFileSizeMismatchEvent, CreateEventStatusObj() );
            }
        }
        else
        {
            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): The remote file has no local equavalant" );
            NotifyObservers( LocalFileNotFoundEvent, CreateEventStatusObj() );
        }

        GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): The local file did not match the remote file or is missing thus it will be retrieved and updated" );

        // If we get here then the file was found but it is not up-to-date or
        // the file does not even exist locally, either way,.. we have to update it
        CORE::CString tmpFilePath = m_tempStorageRoot;
        CORE::AppendToPath( tmpFilePath, curFile->name );

        // make sure the path exists
        CORE::CreateDirs( CORE::StripFilename( tmpFilePath ) );

        // Try to create the file
        m_fileAccess = new CORE::CFileAccess( tmpFilePath, "wb" );
        m_fileAccess->Open();
        if ( m_fileAccess->Opened() && m_fileAccess->IsValid() )
        {
            // We now have write access to our temp new file.
            // We will now tell our URL buddy to get the file for us
            m_dataRetriever.GetURL().SetURL( curFile->fileLocations[ 0 ].URL );
            m_dataRetriever.SetIOAccess( m_fileAccess );
            while ( m_curFileLocIndex < curFile->fileLocations.size() )
            {
                if ( m_dataRetriever.GetURL().Activate() )
                {
                    // The data retrieval process has now commenced
                    // This is an a-sync operation, we wait for notification
                    GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Started retrieving the file" );
                    NotifyObservers( FileRetrievalStartedEvent, CreateEventStatusObj() );
                    return true;
                }
                else
                {
                    GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Failed to retrieve the file" );
                    NotifyObservers( FileRetrievalErrorEvent, CreateEventStatusObj() );

                    // if we get here then we where unable to activate the given URL
                    // We will try the next one,.. if there is a next one
                    if ( m_curFileLocIndex+1 < curFile->fileLocations.size() )
                    {
                        GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): File processing has been aborted, no alternative download locations remaining" );

                        // we ran out of options,.. the end
                        delete m_fileAccess;
                        m_fileAccess = NULL;
                        NotifyObservers( FileListProcessingAbortedEvent, CreateEventStatusObj() );
                        m_stopSignalGiven = false;
                        m_isActive = false;
                        return false;
                    }
                }
                ++m_curFileLocIndex;
            }
        }
        else
        {
            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Unable to obtain write access to the temporary storage location,.. the end" );

            // We failed to obtain writable access to our temp file storage
            // we ran out of options,.. the end
            delete m_fileAccess;
            m_fileAccess = NULL;
            NotifyObservers( FileStorageErrorEvent, CreateEventStatusObj() );
            m_stopSignalGiven = false;
            m_isActive = false;
            return false;
        }
    }
    else
    {
        if ( m_fileList.size() < m_curFileIndex )
        {
            NotifyObservers( FileListProcessingAbortedEvent, CreateEventStatusObj() );
            m_stopSignalGiven = false;
            m_isActive = false;
        }
        else
        {
            // We are finished,.. engine stopping
            NotifyObservers( FileListProcessingCompleteEvent, CreateEventStatusObj() );
            m_isActive = false;
            m_stopSignalGiven = false;
        }
    }

    return false;
}

/*-------------------------------------------------------------------------*/

void
CPatchSetFileEngine::Stop( void )
{GUCEF_TRACE;

    if ( m_isActive )
    {
        GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Stop called while active" );

        m_stopSignalGiven = true;

        if ( m_dataRetriever.GetURL().IsActive() )
        {
            m_dataRetriever.GetURL().Deactivate();
        }
    }
}

/*-------------------------------------------------------------------------*/

bool
CPatchSetFileEngine::IsActive( void ) const
{GUCEF_TRACE;

    return m_isActive;
}

/*-------------------------------------------------------------------------*/

bool
CPatchSetFileEngine::TryNextFileLocation( void )
{GUCEF_TRACE;

    GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Attempting to retrieve file from an alternate location" );

    m_fileAccess->Close();

    const TFileEntry& curFile = m_fileList[ m_curFileIndex ];
    do
    {
        // if we get here then we where unable to retrieve the file using the given URL
        // We will try the next one,.. if there is a next one
        if ( m_curFileLocIndex+1 < curFile.fileLocations.size() )
        {
            // we ran out of options,.. the end
            m_stopSignalGiven = false;
            m_isActive = false;
            return false;
        }

        ++m_curFileLocIndex;

        m_fileAccess->Open();
        if ( m_dataRetriever.GetURL().Activate() )
        {
            // The data retrieval process has now commenced
            // This is an a-sync operation, we wait for notification
            return true;
        }
    }
    while ( m_curFileLocIndex < curFile.fileLocations.size() );

    return false;
}

/*-------------------------------------------------------------------------*/

bool
CPatchSetFileEngine::ProceedToNextFile( void )
{GUCEF_TRACE;

    GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Proceeding to the next file" );

    // Close the last file if it is still opened
    if ( m_fileAccess != NULL )
    {
        m_fileAccess->Close();
        delete m_fileAccess;
        m_fileAccess = NULL;
    }

    m_curFileLocIndex = 0;
    ++m_curFileIndex;

    return ProcessCurrentFile();
}

/*-------------------------------------------------------------------------*/

void
CPatchSetFileEngine::OnNotify( CORE::CNotifier* notifier                 ,
                               const CORE::CEvent& eventid               ,
                               CORE::CICloneable* eventdata /* = NULL */ )
{GUCEF_TRACE;

    if ( !m_stopSignalGiven && m_isActive )
    {
        // Call base-class version
        CORE::CObservingNotifier::OnNotify( notifier  ,
                                            eventid   ,
                                            eventdata );

        if ( notifier == &m_dataRetriever )
        {
            if ( eventid == CORE::CURLDataRetriever::URLActivateEvent )
            {
                m_currentFileReceivedBytes = 0;
                return;
            }
            else
            if ( eventid == CORE::CURLDataRetriever::URLDataRecievedEvent )
            {
                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Data segment received" );
                m_totalBytesProcessed = ( m_totalBytesProcessed - m_currentFileReceivedBytes ) + m_dataRetriever.GetTotalBytesReceived();
                m_currentFileReceivedBytes = m_dataRetriever.GetTotalBytesReceived();
                NotifyObservers( FileRetrievalProgressEvent, CreateEventStatusObj() );
                return;
            }
            else
            if ( eventid == CORE::CURLDataRetriever::URLAllDataRecievedEvent )
            {
                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): All data for the file has been received" );

                // Check if we stored the file in an alternate location
                if ( m_tempStorageRoot != m_localRoot )
                {
                    GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): The new file is stored in a temporary location" );

                    // safety check,.. just in case
                    assert( m_fileAccess != NULL );
                    if ( m_fileAccess != NULL )
                    {
                        // Create the destination path
                        CORE::CString destPath( m_localRoot );
                        CORE::AppendToPath( destPath, CORE::ExtractFilename( m_fileAccess->GetFilename() ) );

                        // If a file already exists at the target location attempt to delete it
                        if ( CORE::FileExists( destPath ) )
                        {
                            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): A file with the same name already exists at the destination location of the new file" );

                            if ( 0 == CORE::Delete_File( destPath.C_String() ) )
                            {
                                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Failed to delete an old file" );

                                // We failed to delete the old file,..
                                NotifyObservers( LocalFileReplacementFailure, CreateEventStatusObj() );

                                // This is a critical feature for implementing self-replacement of products that use the
                                // patching libraries which will be in use when we patch.
                                if ( m_stopOnReplacementFailure )
                                {
                                    m_stopSignalGiven = false;
                                    m_isActive = false;
                                    return;
                                }
                            }
                            else
                            {
                                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Deleted old file" );
                            }
                        }

                        // Move the new file to the desired final location
                        if ( 0 == CORE::Move_File( destPath.C_String(), m_fileAccess->GetFilename().C_String() ) )
                        {
                            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Failed to move the new file to the location of the old file!" );

                            // Moving the file failed,.. the end
                            NotifyObservers( FileStorageErrorEvent, CreateEventStatusObj() );
                            m_stopSignalGiven = false;
                            m_isActive = false;
                            return;
                        }
                        GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Moved the new file to the location of the old file thus replacing it" );

                        // We successfully replaced/patched a local file
                        NotifyObservers( LocalFileReplacedEvent, CreateEventStatusObj() );
                    }
                }

                ProceedToNextFile();
            }
            else
            if ( eventid == CORE::CURLDataRetriever::URLDataRetrievalErrorEvent )
            {
                GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): Unable to retrieve the file from the current remote file location" );
                TryNextFileLocation();
            }
        }
    }
    else
    {
        if ( m_stopSignalGiven )
        {
            GUCEF_DEBUG_LOG( CORE::LOGLEVEL_NORMAL, "CPatchSetFileEngine(" + CORE::PointerToString( this ) + "): File processing has been aborted" );

            NotifyObservers( FileListProcessingAbortedEvent, CreateEventStatusObj() );
            m_stopSignalGiven = false;
            m_isActive = false;
        }
    }
}

/*-------------------------------------------------------------------------*/

const CString&
CPatchSetFileEngine::GetClassTypeName( void ) const
{GUCEF_TRACE;

    static CString typeName = "GUCEF::PATCHER::CPatchSetFileEngine";
    return typeName;
}

/*-------------------------------------------------------------------------//
//                                                                         //
//      NAMESPACE                                                          //
//                                                                         //
//-------------------------------------------------------------------------*/

}; /* namespace PATCHER */
}; /* namespace GUCEF */

/*-------------------------------------------------------------------------*/


text:
namespace MobileDevice.Tests.Helper
{
    using System;
    using System.IO;

    public static class HelperMethods
    {
        public static void DisplayTaskDescription(string path)
        {
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.WriteLine(ReadTaskDescription(path));
            Console.WriteLine(Constants.TaskDescriptionEndBorder);
            Console.ResetColor();
        }

        private static string ReadTaskDescription(string path)
        {
            string taskDescription = string.Empty;
            try
            {
                taskDescription = File.ReadAllText(path);
            }
            catch (Exception e)
            {
                return e.Message;
            }

            return taskDescription;
        }
    }
}


text:
<?php

namespace Workflow\Workflow;

/**
 *
 * @author nmt
 *
 */
 interface WorkflowFactoryInterface {

     /**
      *
      */
     public function getSubject();

     /**
      *
      * @param unknown $subject
      */
     public function setSubject($subject);

     /**
      *
      */
     public function getWorkFlowList();

}


text:
<?php

namespace Oro\Bundle\EntityExtendBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * @deprecated since 1.4. Will be removed in 2.0
 */
class OptionSetType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('id', 'hidden')
            ->add(
                'priority',
                'hidden',
                [
                    'empty_data' => 9999
                ]
            )
            ->add(
                'label',
                'text',
                [
                    'label' => 'Value',
                    'required' => true,
                ]
            )
            ->add(
                'is_default',
                'radio',
                [
                    'label' => 'Default',
                    'required' => false,
                ]
            );

        $builder->addEventListener(
            FormEvents::POST_SUBMIT,
            array($this, 'postSubmit')
        );
    }

    public function postSubmit(FormEvent $event)
    {
        $data = $event->getData();
        if (is_null($data->getLabel())) {
            $event->getForm()->get('label')->addError(
                new FormError('This value should not be blank.')
            );
        }
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(
            [
                'data_class' => 'Oro\Bundle\EntityConfigBundle\Entity\OptionSet',
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'oro_entity_option_set';
    }
}