/* * Copyright 2017 Google Inc. All Rights Reserved. * * 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.google.errorprone.bugpatterns.argumentselectiondefects; import static com.google.common.collect.ImmutableList.toImmutableList; import blogspot.software_and_algorithms.stern_library.optimization.HungarianAlgorithm; import com.google.common.collect.ImmutableList; import java.util.stream.Stream; /** * Accumulates the various costs of using existing arguments or alternatives. These are modelled as * edge weights in a bipartite graph mapping parameters on to potential arguments. The {@link * #computeAssignments()} function can then be used to find the optimal assignment of parameters to * arguments. * * @author andrewrice@google.com (Andrew Rice) */ class Costs { /** Formal parameters for the method being called. */ private final ImmutableList<Parameter> formals; /** Actual parameters (argments) for the method call. */ private final ImmutableList<Parameter> actuals; /** * The cost matrix of distances: Element (i,j) is the distance between the ith formal parameter * and the name of the jth actual parameter. The next stages assign costs to the elements of this * matrix using Infinity to indicate that this alternative should not be considered. We will refer * to combinations of formal parameter and (potential) actual parameter as pairs. */ private final double[][] costMatrix; Costs(ImmutableList<Parameter> formals, ImmutableList<Parameter> actuals) { this.formals = formals; this.actuals = actuals; this.costMatrix = new double[formals.size()][actuals.size()]; } Changes computeAssignments() { int[] assignments = new HungarianAlgorithm(costMatrix).execute(); ImmutableList<Parameter> formalsWithChange = formals .stream() .filter(f -> assignments[f.index()] != f.index()) .collect(toImmutableList()); if (formalsWithChange.isEmpty()) { return Changes.empty(); } ImmutableList<Double> originalCost = formalsWithChange .stream() .map(f2 -> costMatrix[f2.index()][f2.index()]) .collect(toImmutableList()); ImmutableList<Double> assignmentCost = formalsWithChange .stream() .map(f1 -> costMatrix[f1.index()][assignments[f1.index()]]) .collect(toImmutableList()); ImmutableList<ParameterPair> changes = formalsWithChange .stream() .map(f -> ParameterPair.create(f, actuals.get(assignments[f.index()]))) .collect(toImmutableList()); return Changes.create(originalCost, assignmentCost, changes); } /** * Constructs a stream for every element of formals paired with every element of actuals (cross * product). Each item contains the formal and the actual, which in turn contain their index into * the cost matrix. Elements whose cost is Inf in the cost matrix are filtered out so only viable * pairings remain. */ Stream<ParameterPair> viablePairs() { return formals .stream() .flatMap(f -> actuals.stream().map(a -> ParameterPair.create(f, a))) .filter( p -> costMatrix[p.formal().index()][p.actual().index()] != Double.POSITIVE_INFINITY); } /** Set the cost of all the alternatives for this formal parameter to be Inf. */ void invalidateAllAlternatives(Parameter formal) { for (int actualIndex = 0; actualIndex < costMatrix[formal.index()].length; actualIndex++) { if (actualIndex != formal.index()) { costMatrix[formal.index()][actualIndex] = Double.POSITIVE_INFINITY; } } } /** Update the cost of the given pairing. */ void updatePair(ParameterPair p, double cost) { costMatrix[p.formal().index()][p.actual().index()] = cost; } /** Set the cost of this pairing to be Inf. */ void invalidatePair(ParameterPair p) { updatePair(p, Double.POSITIVE_INFINITY); } }