// Copyright 2005 The Trustees of Indiana University.

// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

//  Authors: Alex Breuer
//           Andrew Lumsdaine
#ifndef BOOST_GRAPH_GRAPH_STATS_HPP
#define BOOST_GRAPH_GRAPH_STATS_HPP

#include <map>
#include <list>
#include <boost/graph/iteration_macros.hpp>

namespace boost { namespace graph {

template<typename Graph>
struct  sort_edge_by_origin {
public:
  typedef typename graph_traits<Graph>::edge_descriptor edge_type;

  explicit sort_edge_by_origin( Graph& g ) : g(g) {} 

  inline bool operator()( edge_type a, edge_type b ) {
    return source( a, g ) == source( b, g ) ? target( a, g ) < target( b, g ) :
      source( a, g ) < source( b, g );
  }

private:
  Graph& g;
};

template<typename Graph>
struct  equal_edge {
public:
  typedef typename graph_traits<Graph>::edge_descriptor edge_type;

  explicit equal_edge( Graph& g ) : g(g) {} 

  inline bool operator()( edge_type a, edge_type b ) {
    return source( a, g ) == source( b, g ) &&
      target( a, g ) == target( b, g );
  }

private:
  Graph& g;
};

template<typename Graph>
unsigned long num_dup_edges( Graph& g ) {
  typedef typename graph_traits<Graph>::edge_iterator e_iterator_type;
  typedef typename graph_traits<Graph>::edge_descriptor edge_type;

  std::list<edge_type> all_edges;
  
  BGL_FORALL_EDGES_T( e, g, Graph ) {
    all_edges.push_back( e );
  }
    
  sort_edge_by_origin<Graph> cmp1( g );
  all_edges.sort( cmp1 );
  equal_edge<Graph> cmp2( g );
  all_edges.unique( cmp2 );

  return num_edges( g ) - all_edges.size();
}


template<typename Graph>
std::map<unsigned long, unsigned long> dup_edge_dist( Graph& g ) {
  std::map<unsigned long, unsigned long> dist;
  typedef typename graph_traits<Graph>::adjacency_iterator a_iterator_type;
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_type;

  
  BGL_FORALL_VERTICES_T( v, g, Graph ) {
    std::list<vertex_type> front_neighbors;
    a_iterator_type a_iter, a_end;
    for( tie( a_iter, a_end ) = adjacent_vertices( v, g );
         a_iter != a_end; ++a_iter ) {
      front_neighbors.push_back( *a_iter );
    }
    
    front_neighbors.sort();
    front_neighbors.unique();
    dist[out_degree( v, g ) - front_neighbors.size()] += 1;
  }
  return dist;
}

template<typename Graph>
std::map<unsigned long, unsigned long> degree_dist( Graph& g ) {
  std::map<unsigned long, unsigned long> dist;
  typedef typename graph_traits<Graph>::adjacency_iterator a_iterator_type;
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_type;
  
  BGL_FORALL_VERTICES_T( v, g, Graph ) {
    dist[out_degree( v, g )] += 1;
  }

  return dist;
}

template<typename Graph>
std::map<unsigned long, double> weight_degree_dist( Graph& g ) {
  std::map<unsigned long, double> dist, n;
  typedef typename graph_traits<Graph>::adjacency_iterator a_iterator_type;
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_type;
  typedef typename property_map<Graph, edge_weight_t>::const_type edge_map_type;
  typedef typename property_traits<edge_map_type>::value_type 
    edge_weight_type;

  typename property_map<Graph, edge_weight_t>::type  em = get( edge_weight, g );
  
  BGL_FORALL_VERTICES_T( v, g, Graph ) {
      edge_weight_type tmp = 0;
      BGL_FORALL_OUTEDGES_T( v, e, g, Graph ) {
        tmp += em[e];
      }
      n[out_degree( v, g )] += 1.;
      dist[out_degree( v, g )] += tmp;
  }

  for( std::map<unsigned long, double>::iterator iter = dist.begin();
       iter != dist.end(); ++iter ) {
    assert( n[iter->first] != 0 );
    dist[iter->first] /= n[iter->first];
  }

  return dist;
}

}}

#endif
