class ResponseUtils {

    public static Set<NodeIdentifier> findLongResponseTimes(final AsyncClusterResponse response, final double stdDeviationMultiple) {
        final Set<NodeIdentifier> slowResponses = new HashSet<>();

        if (response.isOlderThan(1, TimeUnit.SECONDS)) {
            // If the response is older than 2 seconds, determines if any node took a long time to respond.
            final Set<NodeIdentifier> completedIds = response.getCompletedNodeIdentifiers();

            if (completedIds.size() < 2) {
                return slowResponses;
            }

            long requestMillisSum = 0L;
            int numNodes = 0;
            for (final NodeIdentifier nodeId : completedIds) {
                final long requestMillis = response.getNodeResponse(nodeId).getRequestDuration(TimeUnit.NANOSECONDS);
                if (requestMillis < 0) {
                    continue;
                }

                requestMillisSum += requestMillis;
                numNodes++;
            }

            if (numNodes < 2) {
                return slowResponses;
            }

            final double mean = requestMillisSum / numNodes;
            double differenceSquaredSum = 0D;
            for (final NodeIdentifier nodeId : completedIds) {
                final long requestMillis = response.getNodeResponse(nodeId).getRequestDuration(TimeUnit.NANOSECONDS);
                final double differenceSquared = Math.pow(mean - requestMillis, 2);
                differenceSquaredSum += differenceSquared;
            }

            final double meanOfDifferenceSquared = differenceSquaredSum / numNodes;
            final double stdDev = Math.pow(meanOfDifferenceSquared, 0.5D);
            final double longTimeThreshold = mean + stdDev * stdDeviationMultiple;

            for (final NodeIdentifier nodeId : completedIds) {
                final long requestMillis = response.getNodeResponse(nodeId).getRequestDuration(TimeUnit.NANOSECONDS);
                if (requestMillis > longTimeThreshold) {
                    slowResponses.add(nodeId);
                }
            }
        }

        return slowResponses;
    }

}