/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.flink.graph.vertex.centric;

import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.graph.Edge;
import org.apache.flink.graph.Graph;
import org.apache.flink.graph.GraphAlgorithm;
import org.apache.flink.graph.Vertex;
import org.apache.flink.graph.partition.centric.PCGraph;
import org.apache.flink.types.NullValue;

/**
 * This is an implementation of the Single-Source-Shortest Paths algorithm, using a vertex-centric iteration.
 */
@SuppressWarnings("serial")
public class SingleSourceShortestPaths<K> implements GraphAlgorithm<K, Double, Double, DataSet<Vertex<K, Double>>> {

    private final K srcVertexId;
    private final Integer maxIterations;

    /**
     * Creates an instance of the SingleSourceShortestPaths algorithm.
     *
     * @param srcVertexId The ID of the source vertex.
     * @param maxIterations The maximum number of iterations to run.
     */
    public SingleSourceShortestPaths(K srcVertexId, Integer maxIterations) {
        this.srcVertexId = srcVertexId;
        this.maxIterations = maxIterations;
    }

    @Override
    public DataSet<Vertex<K, Double>> run(Graph<K, Double, Double> input) {

        Graph<K, Double, Double> g = input.mapVertices(new InitVerticesMapper<K>(srcVertexId));
        PCGraph<K, Double, Double> pcGraph = new PCGraph<>(g);
        return pcGraph.runVertexCentricIteration(
                new VertexDistanceUpdater<K>(),
                new MinDistanceMessenger<K>(),
                    maxIterations).getVertices();
    }

    public static final class InitVerticesMapper<K>	implements MapFunction<Vertex<K, Double>, Double> {

        private K srcVertexId;

        public InitVerticesMapper(K srcId) {
            this.srcVertexId = srcId;
        }

        public Double map(Vertex<K, Double> value) {
            if (value.f0.equals(srcVertexId)) {
                return 0.0;
            } else {
                return Double.MAX_VALUE;
            }
        }
    }

    /**
     * Function that updates the value of a vertex by picking the minimum
     * distance from all incoming messages.
     *
     * @param <K>
     */
    public static final class VertexDistanceUpdater<K> extends VertexUpdateFunction<K, Double, Double> {

        @Override
        public void updateVertex(Vertex<K, Double> vertex,
                                 MessageIterator<Double> inMessages) {

            Double minDistance = Double.MAX_VALUE;

            for (double msg : inMessages) {
                if (msg < minDistance) {
                    minDistance = msg;
                }
            }

            if (vertex.getValue() > minDistance) {
                setNewVertexValue(minDistance);
            }
        }
    }

    /**
     * Distributes the minimum distance associated with a given vertex among all
     * the target vertices summed up with the edge's value.
     *
     * @param <K>
     */
    public static final class MinDistanceMessenger<K> extends MessagingFunction<K, Double, Double, Double> {

        @Override
        public void sendMessages(Vertex<K, Double> vertex)
                throws Exception {
            for (Edge<K, Double> edge : getEdges()) {
                sendMessageTo(edge.getTarget(), vertex.getValue() + edge.getValue());
            }
        }
    }
}
@@ -848,16 +848,136 @@
 ntric;%0A%0A
+import org.apache.flink.api.common.accumulators.Histogram;%0Aimport org.apache.flink.api.common.accumulators.LongCounter;%0A
 import o
@@ -1586,16 +1586,263 @@
 ble%3E%3E%3E %7B
+%0A    public static final String MESSAGE_SENT_CTR = %22long:message_sent%22;%0A    public static final String MESSAGE_SENT_ITER_CTR = %22histogram:message_sent_iter_ctr%22;%0A    public static final String ACTIVE_VER_ITER_CTR = %22histogram:active_ver_iter_ctr%22;
 %0A%0A    pr
@@ -4288,135 +4288,1140 @@
 oid 
-sendMessages(Vertex%3CK, Double%3E vertex)%0A                throws Exception %7B%0A            for (Edge%3CK, Double%3E edge : getEdges()) %7B
+preSuperstep() throws Exception %7B%0A            runtimeContext.addAccumulator(MESSAGE_SENT_CTR, new LongCounter());%0A            runtimeContext.addAccumulator(MESSAGE_SENT_ITER_CTR, new Histogram());%0A            runtimeContext.addAccumulator(ACTIVE_VER_ITER_CTR, new Histogram());%0A        %7D%0A%0A        @Override%0A        public void sendMessages(Vertex%3CK, Double%3E vertex)%0A                throws Exception %7B%0A%0A            // send current minimum to neighbors%0A            Histogram messageHistogram = runtimeContext.getHistogram(MESSAGE_SENT_ITER_CTR);%0A            LongCounter messageCounter = runtimeContext.getLongCounter(MESSAGE_SENT_CTR);%0A            Histogram vertexHistogram = runtimeContext.getHistogram(ACTIVE_VER_ITER_CTR);%0A%0A            if (vertexHistogram != null) %7B%0A                vertexHistogram.add(getSuperstepNumber());%0A            %7D%0A%0A            for (Edge%3CK, Double%3E edge : getEdges()) %7B%0A                if (messageCounter != null) %7B%0A                    messageCounter.add(1);%0A                %7D%0A                if (messageHistogram != null) %7B%0A                    messageHistogram.add(getSuperstepNumber());%0A                %7D
 %0A   

Add message counters to vertex-centric SSSP