/*
 * KatzIndex.h
 *
 *  Created on: 30.01.2015
 *      Author: Kolja Esders (kolja.esders@student.kit.edu)
 */

#ifndef NETWORKIT_LINKPREDICTION_KATZ_INDEX_HPP_
#define NETWORKIT_LINKPREDICTION_KATZ_INDEX_HPP_

#include <unordered_map>

#include <networkit/linkprediction/LinkPredictor.hpp>

namespace NetworKit {

/**
 * @ingroup linkprediction
 *
 * Implementation of the Katz index.
 * Katz index assigns a pair of nodes a similarity score
 * that is based on the sum of the weighted number of paths of length l
 * where l is smaller than a given limit.
 */
class KatzIndex : public LinkPredictor {
private:
  count maxPathLength; //!< Maximal length of the paths to consider

  double dampingValue; //!< Damping factor in (0,1) used to exponentially damp every addend of the sum

  node lastStartNode; //!< Node at which the algorithm started at the last call

  std::unordered_map<node, double> lastScores; //!< Stores the last generated scores from the lastStartNode

  std::vector<double> dampingFactors; //!< Stores precalculated damping factors to increase performance

  // Helper method used to access the score for a given node-pair. Checks which of the given nodes
  // was used as the starting node and uses the other node to access the last scores generated.
  // Defaults to 0.0 if no score could be found.
  double getScore(node u, node v) const;

  /**
   * Returns the similarity score for the given node-pair based on the Katz index specified during construction.
   * The algorithm considers all paths starting at the node with the smaller degree except the algorithm
   * started at the other node at the last call.
   * @param u First node
   * @param v Second node
   * @return the similarity score of the given node-pair calculated by the specified Katz index
   */
  double runImpl(node u, node v) override;

  /**
   * Calculated the damping factors for every path length smaller or equal to maxPathLength.
   * This can be used to cache damping factors to increase performance through reuse.
   * The results will be stored in dampingFactors and dampingFactors[0] is always 1.
   */
  void calcDampingFactors();

public:
  /**
   *
   * @param maxPathLength Maximal length of the paths to consider
   * @param dampingValue Used to exponentially damp every addend of the sum. Should be in (0, 1]
   */
  explicit KatzIndex(count maxPathLength = 5, double dampingValue = 0.005);

  /**
   *
   * @param G The graph to operate on
   * @param maxPathLength Maximal length of the paths to consider
   * @param dampingValue Used to exponentially damp every addend of the sum. Should be in (0, 1]
   */
  explicit KatzIndex(const Graph& G, count maxPathLength = 5, double dampingValue = 0.005);

  // Overriding this method is necessary as the implementation of the Katz index makes use
  // of caching. This makes run() not thread-safe. To still achieve performance gains
  // we split the nodePairs into subsets and create a new Katz instance for every subset.
  std::vector<LinkPredictor::prediction> runOn(std::vector<std::pair<node, node>> nodePairs) override;
  
};

} // namespace NetworKit

#endif // NETWORKIT_LINKPREDICTION_KATZ_INDEX_HPP_
