/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.facebook.presto.sql.planner.assertions;

import com.facebook.presto.Session;
import com.facebook.presto.metadata.FunctionManager;
import com.facebook.presto.metadata.Metadata;
import com.facebook.presto.sql.planner.PlannerUtils;
import com.facebook.presto.sql.planner.Symbol;
import com.facebook.presto.sql.planner.plan.AggregationNode;
import com.facebook.presto.sql.planner.plan.AggregationNode.Aggregation;
import com.facebook.presto.sql.planner.plan.PlanNode;
import com.facebook.presto.sql.tree.FunctionCall;

import java.util.Map;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

public class AggregationFunctionMatcher
        implements RvalueMatcher
{
    private final ExpectedValueProvider<FunctionCall> callMaker;

    public AggregationFunctionMatcher(ExpectedValueProvider<FunctionCall> callMaker)
    {
        this.callMaker = requireNonNull(callMaker, "functionCall is null");
    }

    @Override
    public Optional<Symbol> getAssignedSymbol(PlanNode node, Session session, Metadata metadata, SymbolAliases symbolAliases)
    {
        Optional<Symbol> result = Optional.empty();
        if (!(node instanceof AggregationNode)) {
            return result;
        }

        AggregationNode aggregationNode = (AggregationNode) node;

        FunctionCall expectedCall = callMaker.getExpectedValue(symbolAliases);
        for (Map.Entry<Symbol, Aggregation> assignment : aggregationNode.getAggregations().entrySet()) {
            if (compareAggregation(metadata.getFunctionManager(), assignment.getValue(), expectedCall)) {
                checkState(!result.isPresent(), "Ambiguous function calls in %s", aggregationNode);
                result = Optional.of(assignment.getKey());
            }
        }

        return result;
    }

    private boolean compareAggregation(FunctionManager functionManager, Aggregation aggregation, FunctionCall expectedCall)
    {
        return expectedCall.getName().getSuffix().equalsIgnoreCase(functionManager.getFunctionMetadata(aggregation.getFunctionHandle()).getName()) &&
                expectedCall.getArguments().equals(aggregation.getArguments()) &&
                expectedCall.getFilter().equals(aggregation.getFilter()) &&
                expectedCall.isDistinct() == aggregation.isDistinct() &&
                expectedCall.getOrderBy().map(PlannerUtils::toOrderingScheme).equals(aggregation.getOrderBy());
    }

    @Override
    public String toString()
    {
        return callMaker.toString();
    }
}

@@ -782,28 +782,30 @@
 planner.
-PlannerUtils
+OrderingScheme
 ;%0Aimport
@@ -1082,16 +1082,61 @@
 ionCall;
+%0Aimport com.facebook.presto.sql.tree.OrderBy;
 %0A%0Aimport
@@ -1179,16 +1179,88 @@
 ional;%0A%0A
+import static com.facebook.presto.sql.planner.PlannerUtils.toSortOrder;%0A
 import s
@@ -2231,23 +2231,22 @@
     if (
-compare
+verify
 Aggregat
@@ -2550,16 +2550,23 @@
 ate 
+static 
 boolean 
 comp
@@ -2565,15 +2565,14 @@
 ean 
-compare
+verify
 Aggr
@@ -3071,102 +3071,1220 @@
     
-expectedCall.getOrderBy().map(PlannerUtils::toOrderingScheme).equals(aggregation.getOrderBy())
+verifyAggregationOrderBy(aggregation.getOrderBy(), expectedCall.getOrderBy());%0A    %7D%0A%0A    private static boolean verifyAggregationOrderBy(Optional%3COrderingScheme%3E orderingScheme, Optional%3COrderBy%3E expectedSortOrder)%0A    %7B%0A        if (orderingScheme.isPresent() && expectedSortOrder.isPresent()) %7B%0A            return verifyAggregationOrderBy(orderingScheme.get(), expectedSortOrder.get());%0A        %7D%0A        return orderingScheme.isPresent() == expectedSortOrder.isPresent();%0A    %7D%0A%0A    private static boolean verifyAggregationOrderBy(OrderingScheme orderingScheme, OrderBy expectedSortOrder)%0A    %7B%0A        if (orderingScheme.getOrderBy().size() != expectedSortOrder.getSortItems().size()) %7B%0A            return false;%0A        %7D%0A        for (int i = 0; i %3C expectedSortOrder.getSortItems().size(); i++) %7B%0A            Symbol orderingSymbol = orderingScheme.getOrderBy().get(i);%0A            if (expectedSortOrder.getSortItems().get(i).getSortKey().equals(orderingSymbol.toSymbolReference()) &&%0A                    toSortOrder(expectedSortOrder.getSortItems().get(i)).equals(orderingScheme.getOrdering(orderingSymbol))) %7B%0A                continue;%0A            %7D%0A            return false;%0A        %7D%0A        return true
 ;%0A  

Add utility to compare OrderingScheme with OrderBy