// Copyright 2005-2009 The Trustees of Indiana University.

// Distributed under 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: Jeremiah Willcock
//           Douglas Gregor
//           Andrew Lumsdaine

// Compressed sparse row graph type

#ifndef BOOST_GRAPH_COMPRESSED_SPARSE_ROW_GRAPH_HPP
#define BOOST_GRAPH_COMPRESSED_SPARSE_ROW_GRAPH_HPP

#include <vector>
#include <utility>
#include <algorithm>
#include <climits>
#include <boost/assert.hpp>
#include <iterator>
#if 0
#include <iostream> // For some debugging code below
#endif
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/filtered_graph.hpp> // For keep_all
#include <boost/graph/detail/indexed_properties.hpp>
#include <boost/graph/detail/compressed_sparse_row_struct.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/reverse_iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/integer.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/mpl/if.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/graph/graph_selectors.hpp>
#include <boost/graph/detail/is_distributed_selector.hpp>
#include <boost/graph/properties.hpp>
#include <boost/static_assert.hpp>
#include <boost/functional/hash.hpp>
#include <boost/next_prior.hpp>
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/mpl/print.hpp>

namespace boost {

// A tag type indicating that the graph in question is a compressed
// sparse row graph. This is an internal detail of the BGL.
struct csr_graph_tag;

// A type (edges_are_sorted_t) and a value (edges_are_sorted) used to indicate
// that the edge list passed into the CSR graph is already sorted by source
// vertex.
enum edges_are_sorted_t {edges_are_sorted};

// A type (edges_are_sorted_global_t) and a value (edges_are_sorted_global)
// used to indicate that the edge list passed into the CSR graph is already
// sorted by source vertex.
enum edges_are_sorted_global_t {edges_are_sorted_global};

// A type (edges_are_unsorted_t) and a value (edges_are_unsorted) used to
// indicate that the edge list passed into the CSR graph is not sorted by
// source vertex.  This version caches the edge information in memory, and thus
// requires only a single pass over the input data.
enum edges_are_unsorted_t {edges_are_unsorted};

// A type (edges_are_unsorted_multi_pass_t) and a value
// (edges_are_unsorted_multi_pass) used to indicate that the edge list passed
// into the CSR graph is not sorted by source vertex.  This version uses less
// memory but requires multi-pass capability on the iterators.
enum edges_are_unsorted_multi_pass_t {edges_are_unsorted_multi_pass};

// A type (edges_are_unsorted_multi_pass_global_t) and a value
// (edges_are_unsorted_multi_pass_global) used to indicate that the edge list
// passed into the CSR graph is not sorted by source vertex.  This version uses
// less memory but requires multi-pass capability on the iterators.  The
// global mapping and filtering is done here because it is often faster and it
// greatly simplifies handling of edge properties.
enum edges_are_unsorted_multi_pass_global_t {edges_are_unsorted_multi_pass_global};

// A type (construct_inplace_from_sources_and_targets_t) and a value
// (construct_inplace_from_sources_and_targets) used to indicate that mutable
// vectors of sources and targets (and possibly edge properties) are being used
// to construct the CSR graph.  These vectors are sorted in-place and then the
// targets and properties are swapped into the graph data structure.
enum construct_inplace_from_sources_and_targets_t {construct_inplace_from_sources_and_targets};

// A type (construct_inplace_from_sources_and_targets_global_t) and a value
// (construct_inplace_from_sources_and_targets_global) used to indicate that
// mutable vectors of sources and targets (and possibly edge properties) are
// being used to construct the CSR graph.  These vectors are sorted in-place
// and then the targets and properties are swapped into the graph data
// structure.  It is assumed that global indices (for distributed CSR) are
// used, and a map is required to convert those to local indices.  This
// constructor is intended for internal use by the various CSR graphs
// (sequential and distributed).
enum construct_inplace_from_sources_and_targets_global_t {construct_inplace_from_sources_and_targets_global};

// A type (edges_are_unsorted_global_t) and a value (edges_are_unsorted_global)
// used to indicate that the edge list passed into the CSR graph is not sorted
// by source vertex.  The data is also stored using global vertex indices, and
// must be filtered to choose only local vertices.  This constructor caches the
// edge information in memory, and thus requires only a single pass over the
// input data.  This constructor is intended for internal use by the
// distributed CSR constructors.
enum edges_are_unsorted_global_t {edges_are_unsorted_global};

/****************************************************************************
 * Local helper macros to reduce typing and clutter later on.               *
 ****************************************************************************/
#define BOOST_CSR_GRAPH_TEMPLATE_PARMS                                  \
  typename Directed, typename VertexProperty, typename EdgeProperty,    \
  typename GraphProperty, typename Vertex, typename EdgeIndex
#define BOOST_CSR_GRAPH_TYPE                                            \
   compressed_sparse_row_graph<Directed, VertexProperty, EdgeProperty,  \
                               GraphProperty, Vertex, EdgeIndex>
#define BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS                              \
  typename VertexProperty, typename EdgeProperty,                       \
  typename GraphProperty, typename Vertex, typename EdgeIndex
#define BOOST_DIR_CSR_GRAPH_TYPE                                        \
   compressed_sparse_row_graph<directedS, VertexProperty, EdgeProperty, \
                               GraphProperty, Vertex, EdgeIndex>
#define BOOST_BIDIR_CSR_GRAPH_TEMPLATE_PARMS                            \
  typename VertexProperty, typename EdgeProperty,                       \
  typename GraphProperty, typename Vertex, typename EdgeIndex
#define BOOST_BIDIR_CSR_GRAPH_TYPE                                      \
   compressed_sparse_row_graph<bidirectionalS, VertexProperty, EdgeProperty, \
                               GraphProperty, Vertex, EdgeIndex>

namespace detail {
  template <typename T>
  struct default_construct_iterator: public boost::iterator_facade<default_construct_iterator<T>, T, boost::random_access_traversal_tag, const T&> {
    typedef boost::iterator_facade<default_construct_iterator<T>, T, std::random_access_iterator_tag, const T&> base_type;
    T saved_value;
    const T& dereference() const {return saved_value;}
    bool equal(default_construct_iterator /*i*/) const {return true;}
    void increment() {}
    void decrement() {}
    void advance(typename base_type::difference_type) {}
    typename base_type::difference_type distance_to(default_construct_iterator) const {return 0;}
  };

  template <typename Less>
  struct compare_first {
    Less less;
    compare_first(Less less = Less()): less(less) {}
    template <typename Tuple>
    bool operator()(const Tuple& a, const Tuple& b) const {
      return less(a.template get<0>(), b.template get<0>());
    }
  };

  template <int N, typename Result>
  struct my_tuple_get_class {
    typedef const Result& result_type;
    template <typename Tuple>
    result_type operator()(const Tuple& t) const {
      return t.template get<N>();
    }
  };
}

/** Compressed sparse row graph.
 *
 * Vertex and EdgeIndex should be unsigned integral types and should
 * specialize numeric_limits.
 */
template<typename Directed = directedS,
         typename VertexProperty = no_property,
         typename EdgeProperty = no_property,
         typename GraphProperty = no_property,
         typename Vertex = std::size_t,
         typename EdgeIndex = Vertex>
class compressed_sparse_row_graph; // Not defined

template<typename VertexProperty,
         typename EdgeProperty,
         typename GraphProperty,
         typename Vertex,
         typename EdgeIndex>
class compressed_sparse_row_graph<directedS, VertexProperty, EdgeProperty, GraphProperty, Vertex, EdgeIndex>
   : public detail::indexed_vertex_properties<BOOST_DIR_CSR_GRAPH_TYPE,
                                              VertexProperty, Vertex, typed_identity_property_map<Vertex> >
{
 public:
  typedef detail::indexed_vertex_properties<compressed_sparse_row_graph,
                                            VertexProperty, Vertex, typed_identity_property_map<Vertex> >
    inherited_vertex_properties;

  // Some tests to prevent use of "void" is a property type (as was done in some test cases):
  BOOST_STATIC_ASSERT((!is_same<VertexProperty, void>::value));
  BOOST_STATIC_ASSERT((!is_same<EdgeProperty, void>::value));
  BOOST_STATIC_ASSERT((!is_same<GraphProperty, void>::value));

 public:
  // For Property Graph
  typedef GraphProperty graph_property_type;
  typedef typename lookup_one_property<GraphProperty, graph_bundle_t>::type graph_bundled;

  typedef detail::compressed_sparse_row_structure<EdgeProperty, Vertex, EdgeIndex> forward_type;

 public:
  /* At this time, the compressed sparse row graph can only be used to
   * create directed and bidirectional graphs. In the future,
   * undirected CSR graphs will also be supported.
   */
  // BOOST_STATIC_ASSERT((is_same<Directed, directedS>::value));

  // Concept requirements:
  // For Graph
  typedef Vertex vertex_descriptor;
  typedef detail::csr_edge_descriptor<Vertex, EdgeIndex> edge_descriptor;
  typedef directed_tag directed_category;
  typedef allow_parallel_edge_tag edge_parallel_category;

  class traversal_category: public incidence_graph_tag,
                            public adjacency_graph_tag,
                            public vertex_list_graph_tag,
                            public edge_list_graph_tag {};

  static vertex_descriptor null_vertex() { return vertex_descriptor(-1); }

  // For VertexListGraph
  typedef counting_iterator<Vertex> vertex_iterator;
  typedef Vertex vertices_size_type;

  // For EdgeListGraph
  typedef EdgeIndex edges_size_type;

  // For IncidenceGraph
  typedef detail::csr_out_edge_iterator<compressed_sparse_row_graph> out_edge_iterator;
  typedef EdgeIndex degree_size_type;

  // For AdjacencyGraph
  typedef typename std::vector<Vertex>::const_iterator adjacency_iterator;

  // For EdgeListGraph
  typedef detail::csr_edge_iterator<compressed_sparse_row_graph> edge_iterator;

  // For BidirectionalGraph (not implemented)
  typedef void in_edge_iterator;

  // For internal use
  typedef csr_graph_tag graph_tag;

  typedef typename forward_type::inherited_edge_properties::edge_bundled edge_bundled;
  typedef typename forward_type::inherited_edge_properties::edge_push_back_type edge_push_back_type;
  typedef typename forward_type::inherited_edge_properties::edge_property_type edge_property_type;

  // Constructors

  // Default constructor: an empty graph.
  compressed_sparse_row_graph(): m_property() {}

  //  With numverts vertices
  compressed_sparse_row_graph(vertices_size_type numverts)
    : inherited_vertex_properties(numverts), m_forward(numverts) {}

  //  From number of vertices and unsorted list of edges
  template <typename MultiPassInputIterator>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, numverts, typed_identity_property_map<vertices_size_type>(), keep_all());
  }

  //  From number of vertices and unsorted list of edges, plus edge properties
  template <typename MultiPassInputIterator, typename EdgePropertyIterator>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_forward(), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numverts, typed_identity_property_map<vertices_size_type>(), keep_all());
  }

  //  From number of vertices and unsorted list of edges, with filter and
  //  global-to-local map
  template <typename MultiPassInputIterator, typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_global_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              vertices_size_type numlocalverts,
                              const GlobalToLocal& global_to_local,
                              const SourcePred& source_pred,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_forward(), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, numlocalverts, global_to_local, source_pred);
  }

  //  From number of vertices and unsorted list of edges, plus edge properties,
  //  with filter and global-to-local map
  template <typename MultiPassInputIterator, typename EdgePropertyIterator, typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_global_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numlocalverts,
                              const GlobalToLocal& global_to_local,
                              const SourcePred& source_pred,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_forward(), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numlocalverts, global_to_local, source_pred);
  }

  //  From number of vertices and sorted list of edges (new interface)
  template<typename InputIterator>
  compressed_sparse_row_graph(edges_are_sorted_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              vertices_size_type numverts,
                              edges_size_type numedges = 0,
                              const GraphProperty& prop = GraphProperty())
    : m_property(prop)
  {
    m_forward.assign_from_sorted_edges(edge_begin, edge_end, typed_identity_property_map<vertices_size_type>(), keep_all(), numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  //  From number of vertices and sorted list of edges (new interface)
  template<typename InputIterator, typename EdgePropertyIterator>
  compressed_sparse_row_graph(edges_are_sorted_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numverts,
                              edges_size_type numedges = 0,
                              const GraphProperty& prop = GraphProperty())
    : m_property(prop)
  {
    m_forward.assign_from_sorted_edges(edge_begin, edge_end, ep_iter, typed_identity_property_map<vertices_size_type>(), keep_all(), numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  //  From number of vertices and sorted list of edges, filtered and global (new interface)
  template<typename InputIterator, typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_sorted_global_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              const GlobalToLocal& global_to_local,
                              const SourcePred& source_pred,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : m_property(prop)
  {
    m_forward.assign_from_sorted_edges(edge_begin, edge_end, global_to_local, source_pred, numverts, 0);
    inherited_vertex_properties::resize(numverts);
  }

  //  From number of vertices and sorted list of edges (new interface)
  template<typename InputIterator, typename EdgePropertyIterator, typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_sorted_global_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              const GlobalToLocal& global_to_local,
                              const SourcePred& source_pred,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : m_property(prop)
  {
    m_forward.assign_from_sorted_edges(edge_begin, edge_end, ep_iter, global_to_local, source_pred, numverts, 0);
    inherited_vertex_properties::resize(numverts);
  }

  //  From number of vertices and mutable vectors of sources and targets;
  //  vectors are returned with unspecified contents but are guaranteed not to
  //  share storage with the constructed graph.
  compressed_sparse_row_graph(construct_inplace_from_sources_and_targets_t,
                              std::vector<vertex_descriptor>& sources,
                              std::vector<vertex_descriptor>& targets,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_property(prop)
  {
    m_forward.assign_sources_and_targets_global(sources, targets, numverts, boost::typed_identity_property_map<vertices_size_type>());
  }

  //  From number of vertices and mutable vectors of sources and targets,
  //  expressed with global vertex indices; vectors are returned with
  //  unspecified contents but are guaranteed not to share storage with the
  //  constructed graph.  This constructor should only be used by the
  //  distributed CSR graph.
  template <typename GlobalToLocal>
  compressed_sparse_row_graph(construct_inplace_from_sources_and_targets_global_t,
                              std::vector<vertex_descriptor>& sources,
                              std::vector<vertex_descriptor>& targets,
                              vertices_size_type numlocalverts,
                              GlobalToLocal global_to_local,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_property(prop)
  {
    m_forward.assign_sources_and_targets_global(sources, targets, numlocalverts, global_to_local);
  }

  //  From number of vertices and mutable vectors of sources, targets, and edge
  //  properties; vectors are returned with unspecified contents but are
  //  guaranteed not to share storage with the constructed graph.
  compressed_sparse_row_graph(construct_inplace_from_sources_and_targets_t,
                              std::vector<vertex_descriptor>& sources,
                              std::vector<vertex_descriptor>& targets,
                              std::vector<typename forward_type::inherited_edge_properties::edge_bundled>& edge_props,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_property(prop)
  {
    m_forward.assign_sources_and_targets_global(sources, targets, edge_props, numverts, boost::typed_identity_property_map<vertices_size_type>());
  }

  //  From number of vertices and mutable vectors of sources and targets and
  //  edge properties, expressed with global vertex indices; vectors are
  //  returned with unspecified contents but are guaranteed not to share
  //  storage with the constructed graph.  This constructor should only be used
  //  by the distributed CSR graph.
  template <typename GlobalToLocal>
  compressed_sparse_row_graph(construct_inplace_from_sources_and_targets_global_t,
                              std::vector<vertex_descriptor>& sources,
                              std::vector<vertex_descriptor>& targets,
                              std::vector<typename forward_type::inherited_edge_properties::edge_bundled>& edge_props,
                              vertices_size_type numlocalverts,
                              GlobalToLocal global_to_local,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_property(prop)
  {
    m_forward.assign_sources_and_targets_global(sources, targets, edge_props, numlocalverts, global_to_local);
  }

  //  From number of vertices and single-pass range of unsorted edges.  Data is
  //  cached in coordinate form before creating the actual graph.
  template<typename InputIterator>
  compressed_sparse_row_graph(edges_are_unsorted_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_property(prop)
  {
    std::vector<vertex_descriptor> sources, targets;
    boost::graph::detail::split_into_separate_coords
      (edge_begin, edge_end, sources, targets);
    m_forward.assign_sources_and_targets_global(sources, targets, numverts, boost::typed_identity_property_map<vertices_size_type>());
  }

  //  From number of vertices and single-pass range of unsorted edges and
  //  single-pass range of edge properties.  Data is cached in coordinate form
  //  before creating the actual graph.
  template<typename InputIterator, typename EdgePropertyIterator>
  compressed_sparse_row_graph(edges_are_unsorted_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_property(prop)
  {
    std::vector<vertex_descriptor> sources, targets;
    boost::graph::detail::split_into_separate_coords
      (edge_begin, edge_end, sources, targets);
    size_t numedges = sources.size();
    std::vector<typename forward_type::inherited_edge_properties::edge_bundled> edge_props(numedges);
    for (size_t i = 0; i < numedges; ++i) {
      edge_props[i] = *ep_iter++;
    }
    m_forward.assign_sources_and_targets_global(sources, targets, edge_props, numverts, boost::typed_identity_property_map<vertices_size_type>());
  }

  //  From number of vertices and single-pass range of unsorted edges.  Data is
  //  cached in coordinate form before creating the actual graph.  Edges are
  //  filtered and transformed for use in a distributed graph.
  template<typename InputIterator, typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_unsorted_global_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              vertices_size_type numlocalverts,
                              GlobalToLocal global_to_local,
                              const SourcePred& source_pred,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_property(prop)
  {
    std::vector<vertex_descriptor> sources, targets;
    boost::graph::detail::split_into_separate_coords_filtered
      (edge_begin, edge_end, sources, targets, source_pred);
    m_forward.assign_sources_and_targets_global(sources, targets, numlocalverts, global_to_local);
  }

  //  From number of vertices and single-pass range of unsorted edges and
  //  single-pass range of edge properties.  Data is cached in coordinate form
  //  before creating the actual graph.  Edges are filtered and transformed for
  //  use in a distributed graph.
  template<typename InputIterator, typename EdgePropertyIterator,
           typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_unsorted_global_t,
                              InputIterator edge_begin, InputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numlocalverts,
                              GlobalToLocal global_to_local,
                              const SourcePred& source_pred,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_property(prop)
  {
    std::vector<vertex_descriptor> sources, targets;
    std::vector<edge_bundled> edge_props;
    boost::graph::detail::split_into_separate_coords_filtered
      (edge_begin, edge_end, ep_iter, sources, targets, edge_props, source_pred);
    m_forward.assign_sources_and_targets_global(sources, targets, edge_props, numlocalverts, global_to_local);
  }


  //   Requires IncidenceGraph and a vertex index map
  template<typename Graph, typename VertexIndexMap>
  compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi,
                              vertices_size_type numverts,
                              edges_size_type numedges)
    : m_property()
  {
    assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  //   Requires VertexListGraph and EdgeListGraph
  template<typename Graph, typename VertexIndexMap>
  compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi)
    : m_property()
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    vertices_size_type numverts = num_vertices(g);
    assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  // Requires vertex index map plus requirements of previous constructor
  template<typename Graph>
  explicit compressed_sparse_row_graph(const Graph& g)
    : m_property()
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    assign(g, get(vertex_index, g), num_vertices(g), numedges);
  }

  // From any graph (slow and uses a lot of memory)
  //   Requires IncidenceGraph and a vertex index map
  //   Internal helper function
  //   Note that numedges must be doubled for undirected source graphs
  template<typename Graph, typename VertexIndexMap>
  void
  assign(const Graph& g, const VertexIndexMap& vi,
         vertices_size_type numverts, edges_size_type numedges)
  {
    m_forward.assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  // Requires the above, plus VertexListGraph and EdgeListGraph
  template<typename Graph, typename VertexIndexMap>
  void assign(const Graph& g, const VertexIndexMap& vi)
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    vertices_size_type numverts = num_vertices(g);
    m_forward.assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  // Requires the above, plus a vertex_index map.
  template<typename Graph>
  void assign(const Graph& g)
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    vertices_size_type numverts = num_vertices(g);
    m_forward.assign(g, get(vertex_index, g), numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  // Add edges from a sorted (smallest sources first) range of pairs and edge
  // properties
  template <typename BidirectionalIteratorOrig, typename EPIterOrig,
            typename GlobalToLocal>
  void
  add_edges_sorted_internal(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      EPIterOrig ep_iter_sorted,
      const GlobalToLocal& global_to_local) {
    m_forward.add_edges_sorted_internal(first_sorted, last_sorted, ep_iter_sorted, global_to_local);
  }

  template <typename BidirectionalIteratorOrig, typename EPIterOrig>
  void
  add_edges_sorted_internal(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      EPIterOrig ep_iter_sorted)  {
    m_forward.add_edges_sorted_internal(first_sorted, last_sorted, ep_iter_sorted, typed_identity_property_map<vertices_size_type>());
  }

  // Add edges from a sorted (smallest sources first) range of pairs
  template <typename BidirectionalIteratorOrig>
  void
  add_edges_sorted_internal(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted) {
    m_forward.add_edges_sorted_internal(first_sorted, last_sorted, detail::default_construct_iterator<edge_bundled>());
  }

  template <typename BidirectionalIteratorOrig, typename GlobalToLocal>
  void
  add_edges_sorted_internal_global(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      const GlobalToLocal& global_to_local) {
    m_forward.add_edges_sorted_internal(first_sorted, last_sorted, detail::default_construct_iterator<edge_bundled>(), global_to_local);
  }

  template <typename BidirectionalIteratorOrig, typename EPIterOrig,
            typename GlobalToLocal>
  void
  add_edges_sorted_internal_global(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      EPIterOrig ep_iter_sorted,
      const GlobalToLocal& global_to_local) {
    m_forward.add_edges_sorted_internal(first_sorted, last_sorted, ep_iter_sorted, global_to_local);
  }

  // Add edges from a range of (source, target) pairs that are unsorted
  template <typename InputIterator, typename GlobalToLocal>
  inline void
  add_edges_internal(InputIterator first, InputIterator last,
                     const GlobalToLocal& global_to_local) {
    typedef compressed_sparse_row_graph Graph;
    typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef std::vector<std::pair<vertex_t, vertex_t> > edge_vector_t;
    edge_vector_t new_edges(first, last);
    if (new_edges.empty()) return;
    std::sort(new_edges.begin(), new_edges.end());
    this->add_edges_sorted_internal_global(new_edges.begin(), new_edges.end(), global_to_local);
  }

  template <typename InputIterator>
  inline void
  add_edges_internal(InputIterator first, InputIterator last) {
    this->add_edges_internal(first, last, typed_identity_property_map<vertices_size_type>());
  }

  // Add edges from a range of (source, target) pairs and edge properties that
  // are unsorted
  template <typename InputIterator, typename EPIterator, typename GlobalToLocal>
  inline void
  add_edges_internal(InputIterator first, InputIterator last,
                     EPIterator ep_iter, EPIterator ep_iter_end,
                     const GlobalToLocal& global_to_local) {
    typedef compressed_sparse_row_graph Graph;
    typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef std::pair<vertex_t, vertex_t> vertex_pair;
    typedef std::vector<
              boost::tuple<vertex_pair,
                           edge_bundled> >
      edge_vector_t;
    edge_vector_t new_edges
      (boost::make_zip_iterator(boost::make_tuple(first, ep_iter)),
       boost::make_zip_iterator(boost::make_tuple(last, ep_iter_end)));
    if (new_edges.empty()) return;
    std::sort(new_edges.begin(), new_edges.end(),
              boost::detail::compare_first<
                std::less<vertex_pair> >());
    m_forward.add_edges_sorted_internal
      (boost::make_transform_iterator(
         new_edges.begin(),
         boost::detail::my_tuple_get_class<0, vertex_pair>()),
       boost::make_transform_iterator(
         new_edges.end(),
         boost::detail::my_tuple_get_class<0, vertex_pair>()),
       boost::make_transform_iterator(
         new_edges.begin(),
         boost::detail::my_tuple_get_class
           <1, edge_bundled>()),
       global_to_local);
  }

  // Add edges from a range of (source, target) pairs and edge properties that
  // are unsorted
  template <typename InputIterator, typename EPIterator>
  inline void
  add_edges_internal(InputIterator first, InputIterator last,
                     EPIterator ep_iter, EPIterator ep_iter_end) {
    this->add_edges_internal(first, last, ep_iter, ep_iter_end, typed_identity_property_map<vertices_size_type>());
  }

  using inherited_vertex_properties::operator[];

  // Directly access a edge or edge bundle
  edge_push_back_type& operator[](const edge_descriptor& v)
  { return m_forward.m_edge_properties[get(edge_index, *this, v)]; }

  const edge_push_back_type& operator[](const edge_descriptor& v) const
  { return m_forward.m_edge_properties[get(edge_index, *this, v)]; }

  // Directly access a graph bundle
  graph_bundled& operator[](graph_bundle_t)
  { return get_property(*this); }

  const graph_bundled& operator[](graph_bundle_t) const
  { return get_property(*this); }

  // private: non-portable, requires friend templates
  inherited_vertex_properties&       vertex_properties()       {return *this;}
  const inherited_vertex_properties& vertex_properties() const {return *this;}
  typename forward_type::inherited_edge_properties&       edge_properties()       { return m_forward; }
  const typename forward_type::inherited_edge_properties& edge_properties() const { return m_forward; }

  forward_type m_forward;
  GraphProperty m_property;
};

template<typename VertexProperty,
         typename EdgeProperty,
         typename GraphProperty,
         typename Vertex,
         typename EdgeIndex>
class compressed_sparse_row_graph<bidirectionalS, VertexProperty, EdgeProperty, GraphProperty, Vertex, EdgeIndex>
   : public detail::indexed_vertex_properties<BOOST_BIDIR_CSR_GRAPH_TYPE,
                                              VertexProperty, Vertex, typed_identity_property_map<Vertex> >
{
 public:
  typedef detail::indexed_vertex_properties<compressed_sparse_row_graph,
                                            VertexProperty, Vertex, typed_identity_property_map<Vertex> >
    inherited_vertex_properties;

 public:
  // For Property Graph
  typedef GraphProperty graph_property_type;
  typedef typename lookup_one_property<GraphProperty, graph_bundle_t>::type graph_bundled;
  // typedef GraphProperty graph_property_type;

  typedef detail::compressed_sparse_row_structure<EdgeProperty, Vertex, EdgeIndex> forward_type;
  typedef EdgeIndex /* typename boost::mpl::if_c<boost::is_same<EdgeProperty, boost::no_property>, boost::no_property, EdgeIndex> */ backward_edge_property;
  typedef detail::compressed_sparse_row_structure<backward_edge_property, Vertex, EdgeIndex> backward_type;

 public:
  // Concept requirements:
  // For Graph
  typedef Vertex vertex_descriptor;
  typedef detail::csr_edge_descriptor<Vertex, EdgeIndex> edge_descriptor;
  typedef bidirectional_tag directed_category;
  typedef allow_parallel_edge_tag edge_parallel_category;

  class traversal_category: public bidirectional_graph_tag,
                            public adjacency_graph_tag,
                            public vertex_list_graph_tag,
                            public edge_list_graph_tag {};

  static vertex_descriptor null_vertex() { return vertex_descriptor(-1); }

  // For VertexListGraph
  typedef counting_iterator<Vertex> vertex_iterator;
  typedef Vertex vertices_size_type;

  // For EdgeListGraph
  typedef EdgeIndex edges_size_type;

  // For IncidenceGraph
  typedef detail::csr_out_edge_iterator<compressed_sparse_row_graph> out_edge_iterator;
  typedef EdgeIndex degree_size_type;

  // For AdjacencyGraph
  typedef typename std::vector<Vertex>::const_iterator adjacency_iterator;

  // For EdgeListGraph
  typedef detail::csr_edge_iterator<compressed_sparse_row_graph> edge_iterator;

  // For BidirectionalGraph (not implemented)
  typedef detail::csr_in_edge_iterator<compressed_sparse_row_graph> in_edge_iterator;

  // For internal use
  typedef csr_graph_tag graph_tag;

  typedef typename forward_type::inherited_edge_properties::edge_bundled edge_bundled;
  typedef typename forward_type::inherited_edge_properties::edge_push_back_type edge_push_back_type;
  typedef typename forward_type::inherited_edge_properties::edge_property_type edge_property_type;

  // Constructors

  // Default constructor: an empty graph.
  compressed_sparse_row_graph(): m_property() {}

  //  With numverts vertices
  compressed_sparse_row_graph(vertices_size_type numverts)
    : inherited_vertex_properties(numverts),
      m_forward(numverts), m_backward(numverts) {}

  private:

  void set_up_backward_property_links() {
    std::pair<edge_iterator, edge_iterator> e = edges(*this);
    m_backward.assign_unsorted_multi_pass_edges
      (detail::transpose_edges(
         detail::make_edge_to_index_pair_iter
           (*this, get(vertex_index, *this), e.first)),
       detail::transpose_edges(
         detail::make_edge_to_index_pair_iter
           (*this, get(vertex_index, *this), e.second)),
       boost::counting_iterator<EdgeIndex>(0),
       m_forward.m_rowstart.size() - 1,
       typed_identity_property_map<Vertex>(),
       keep_all());
  }

  public:

  //  From number of vertices and unsorted list of edges
  template <typename MultiPassInputIterator>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, numverts, typed_identity_property_map<Vertex>(), keep_all());
    set_up_backward_property_links();
  }

  //  From number of vertices and unsorted list of edges, plus edge properties
  template <typename MultiPassInputIterator, typename EdgePropertyIterator>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numverts,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numverts), m_forward(), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numverts, typed_identity_property_map<Vertex>(), keep_all());
    set_up_backward_property_links();
  }

  //  From number of vertices and unsorted list of edges, with filter and
  //  global-to-local map
  template <typename MultiPassInputIterator, typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_global_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              vertices_size_type numlocalverts,
                              const GlobalToLocal& global_to_local,
                              const SourcePred& source_pred,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_forward(), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, numlocalverts, global_to_local, source_pred);
    set_up_backward_property_links();
  }

  //  From number of vertices and unsorted list of edges, plus edge properties,
  //  with filter and global-to-local map
  template <typename MultiPassInputIterator, typename EdgePropertyIterator, typename GlobalToLocal, typename SourcePred>
  compressed_sparse_row_graph(edges_are_unsorted_multi_pass_global_t,
                              MultiPassInputIterator edge_begin,
                              MultiPassInputIterator edge_end,
                              EdgePropertyIterator ep_iter,
                              vertices_size_type numlocalverts,
                              const GlobalToLocal& global_to_local,
                              const SourcePred& source_pred,
                              const GraphProperty& prop = GraphProperty())
    : inherited_vertex_properties(numlocalverts), m_forward(), m_property(prop)
  {
    m_forward.assign_unsorted_multi_pass_edges(edge_begin, edge_end, ep_iter, numlocalverts, global_to_local, source_pred);
    set_up_backward_property_links();
  }

  //   Requires IncidenceGraph and a vertex index map
  template<typename Graph, typename VertexIndexMap>
  compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi,
                              vertices_size_type numverts,
                              edges_size_type numedges)
    : m_property()
  {
    assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  //   Requires VertexListGraph and EdgeListGraph
  template<typename Graph, typename VertexIndexMap>
  compressed_sparse_row_graph(const Graph& g, const VertexIndexMap& vi)
    : m_property()
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    vertices_size_type numverts = num_vertices(g);
    assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
  }

  // Requires vertex index map plus requirements of previous constructor
  template<typename Graph>
  explicit compressed_sparse_row_graph(const Graph& g)
    : m_property()
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    assign(g, get(vertex_index, g), num_vertices(g), numedges);
  }

  // From any graph (slow and uses a lot of memory)
  //   Requires IncidenceGraph and a vertex index map
  //   Internal helper function
  //   Note that numedges must be doubled for undirected source graphs
  template<typename Graph, typename VertexIndexMap>
  void
  assign(const Graph& g, const VertexIndexMap& vi,
         vertices_size_type numverts, edges_size_type numedges)
  {
    m_forward.assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
    set_up_backward_property_links();
  }

  // Requires the above, plus VertexListGraph and EdgeListGraph
  template<typename Graph, typename VertexIndexMap>
  void assign(const Graph& g, const VertexIndexMap& vi)
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    vertices_size_type numverts = num_vertices(g);
    m_forward.assign(g, vi, numverts, numedges);
    inherited_vertex_properties::resize(numverts);
    set_up_backward_property_links();
  }

  // Requires the above, plus a vertex_index map.
  template<typename Graph>
  void assign(const Graph& g)
  {
    typename graph_traits<Graph>::edges_size_type numedges = num_edges(g);
    if (is_same<typename graph_traits<Graph>::directed_category, undirectedS>::value) {
      numedges *= 2; // Double each edge (actual doubling done by out_edges function)
    }
    vertices_size_type numverts = num_vertices(g);
    m_forward.assign(g, get(vertex_index, g), numverts, numedges);
    inherited_vertex_properties::resize(numverts);
    set_up_backward_property_links();
  }

  using inherited_vertex_properties::operator[];

  // Directly access a edge or edge bundle
  edge_push_back_type& operator[](const edge_descriptor& v)
  { return m_forward.m_edge_properties[get(edge_index, *this, v)]; }

  const edge_push_back_type& operator[](const edge_descriptor& v) const
  { return m_forward.m_edge_properties[get(edge_index, *this, v)]; }

  // private: non-portable, requires friend templates
  inherited_vertex_properties&       vertex_properties()       {return *this;}
  const inherited_vertex_properties& vertex_properties() const {return *this;}
  typename forward_type::inherited_edge_properties&       edge_properties()       { return m_forward; }
  const typename forward_type::inherited_edge_properties& edge_properties() const { return m_forward; }

  forward_type m_forward;
  backward_type m_backward;
  GraphProperty m_property;
};

// Construction functions
template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
add_vertex(BOOST_CSR_GRAPH_TYPE& g) {
  add_vertex(g, typename BOOST_CSR_GRAPH_TYPE::vertex_bundled());
}

template<BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
add_vertex(BOOST_DIR_CSR_GRAPH_TYPE& g,
           typename BOOST_DIR_CSR_GRAPH_TYPE::vertex_bundled const& p) {
  Vertex old_num_verts_plus_one = g.m_forward.m_rowstart.size();
  g.m_forward.m_rowstart.push_back(g.m_forward.m_rowstart.back());
  g.vertex_properties().push_back(p);
  return old_num_verts_plus_one - 1;
}

template<BOOST_BIDIR_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
add_vertex(BOOST_BIDIR_CSR_GRAPH_TYPE& g,
           typename BOOST_BIDIR_CSR_GRAPH_TYPE::vertex_bundled const& p) {
  Vertex old_num_verts_plus_one = g.m_forward.m_rowstart.size();
  g.m_forward.m_rowstart.push_back(g.m_forward.m_rowstart.back());
  g.m_backward.m_rowstart.push_back(g.m_backward.m_rowstart.back());
  g.vertex_properties().push_back(p);
  return old_num_verts_plus_one - 1;
}

template<BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
add_vertices(typename BOOST_DIR_CSR_GRAPH_TYPE::vertices_size_type count, BOOST_DIR_CSR_GRAPH_TYPE& g) {
  Vertex old_num_verts_plus_one = g.m_forward.m_rowstart.size();
  EdgeIndex numedges = g.m_forward.m_rowstart.back();
  g.m_forward.m_rowstart.resize(old_num_verts_plus_one + count, numedges);
  g.vertex_properties().resize(num_vertices(g));
  return old_num_verts_plus_one - 1;
}

  // Add edges from a sorted (smallest sources first) range of pairs and edge
  // properties
  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS, typename BidirectionalIteratorOrig,
            typename EPIterOrig>
  void
  add_edges_sorted(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      EPIterOrig ep_iter_sorted,
      BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_sorted_internal(first_sorted, last_sorted, ep_iter_sorted);
  }

  // Add edges from a sorted (smallest sources first) range of pairs
  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS, typename BidirectionalIteratorOrig>
  void
  add_edges_sorted(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_sorted_internal(first_sorted, last_sorted);
  }

  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS, typename BidirectionalIteratorOrig,
            typename EPIterOrig, typename GlobalToLocal>
  void
  add_edges_sorted_global(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      EPIterOrig ep_iter_sorted,
      const GlobalToLocal& global_to_local,
      BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_sorted_internal_global(first_sorted, last_sorted, ep_iter_sorted,
                                       global_to_local);
  }

  // Add edges from a sorted (smallest sources first) range of pairs
  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS, typename BidirectionalIteratorOrig,
            typename GlobalToLocal>
  void
  add_edges_sorted_global(
      BidirectionalIteratorOrig first_sorted,
      BidirectionalIteratorOrig last_sorted,
      const GlobalToLocal& global_to_local,
      BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_sorted_internal_global(first_sorted, last_sorted, global_to_local);
  }

  // Add edges from a range of (source, target) pairs that are unsorted
  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS, typename InputIterator,
            typename GlobalToLocal>
  inline void
  add_edges_global(InputIterator first, InputIterator last,
                   const GlobalToLocal& global_to_local, BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_internal(first, last, global_to_local);
  }

  // Add edges from a range of (source, target) pairs that are unsorted
  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS, typename InputIterator>
  inline void
  add_edges(InputIterator first, InputIterator last, BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_internal(first, last);
  }

  // Add edges from a range of (source, target) pairs and edge properties that
  // are unsorted
  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS,
            typename InputIterator, typename EPIterator>
  inline void
  add_edges(InputIterator first, InputIterator last,
            EPIterator ep_iter, EPIterator ep_iter_end,
            BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_internal(first, last, ep_iter, ep_iter_end);
  }

  template <BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS,
            typename InputIterator, typename EPIterator, typename GlobalToLocal>
  inline void
  add_edges_global(InputIterator first, InputIterator last,
            EPIterator ep_iter, EPIterator ep_iter_end,
            const GlobalToLocal& global_to_local,
            BOOST_DIR_CSR_GRAPH_TYPE& g) {
    g.add_edges_internal(first, last, ep_iter, ep_iter_end, global_to_local);
  }

// From VertexListGraph
template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
num_vertices(const BOOST_CSR_GRAPH_TYPE& g) {
  return g.m_forward.m_rowstart.size() - 1;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
std::pair<counting_iterator<Vertex>, counting_iterator<Vertex> >
inline vertices(const BOOST_CSR_GRAPH_TYPE& g) {
  return std::make_pair(counting_iterator<Vertex>(0),
                        counting_iterator<Vertex>(num_vertices(g)));
}

// From IncidenceGraph
template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
source(typename BOOST_CSR_GRAPH_TYPE::edge_descriptor e,
       const BOOST_CSR_GRAPH_TYPE&)
{
  return e.src;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
target(typename BOOST_CSR_GRAPH_TYPE::edge_descriptor e,
       const BOOST_CSR_GRAPH_TYPE& g)
{
  return g.m_forward.m_column[e.idx];
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline std::pair<typename BOOST_CSR_GRAPH_TYPE::out_edge_iterator,
                 typename BOOST_CSR_GRAPH_TYPE::out_edge_iterator>
out_edges(Vertex v, const BOOST_CSR_GRAPH_TYPE& g)
{
  typedef typename BOOST_CSR_GRAPH_TYPE::edge_descriptor ed;
  typedef typename BOOST_CSR_GRAPH_TYPE::out_edge_iterator it;
  EdgeIndex v_row_start = g.m_forward.m_rowstart[v];
  EdgeIndex next_row_start = g.m_forward.m_rowstart[v + 1];
  return std::make_pair(it(ed(v, v_row_start)),
                        it(ed(v, next_row_start)));
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline EdgeIndex
out_degree(Vertex v, const BOOST_CSR_GRAPH_TYPE& g)
{
  EdgeIndex v_row_start = g.m_forward.m_rowstart[v];
  EdgeIndex next_row_start = g.m_forward.m_rowstart[v + 1];
  return next_row_start - v_row_start;
}

template<BOOST_BIDIR_CSR_GRAPH_TEMPLATE_PARMS>
inline std::pair<typename BOOST_BIDIR_CSR_GRAPH_TYPE::in_edge_iterator,
                 typename BOOST_BIDIR_CSR_GRAPH_TYPE::in_edge_iterator>
in_edges(Vertex v, const BOOST_BIDIR_CSR_GRAPH_TYPE& g)
{
  typedef typename BOOST_BIDIR_CSR_GRAPH_TYPE::in_edge_iterator it;
  EdgeIndex v_row_start = g.m_backward.m_rowstart[v];
  EdgeIndex next_row_start = g.m_backward.m_rowstart[v + 1];
  return std::make_pair(it(g, v_row_start),
                        it(g, next_row_start));
}

template<BOOST_BIDIR_CSR_GRAPH_TEMPLATE_PARMS>
inline EdgeIndex
in_degree(Vertex v, const BOOST_BIDIR_CSR_GRAPH_TYPE& g)
{
  EdgeIndex v_row_start = g.m_backward.m_rowstart[v];
  EdgeIndex next_row_start = g.m_backward.m_rowstart[v + 1];
  return next_row_start - v_row_start;
}

// From AdjacencyGraph
template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline std::pair<typename BOOST_CSR_GRAPH_TYPE::adjacency_iterator,
                 typename BOOST_CSR_GRAPH_TYPE::adjacency_iterator>
adjacent_vertices(Vertex v, const BOOST_CSR_GRAPH_TYPE& g)
{
  EdgeIndex v_row_start = g.m_forward.m_rowstart[v];
  EdgeIndex next_row_start = g.m_forward.m_rowstart[v + 1];
  return std::make_pair(g.m_forward.m_column.begin() + v_row_start,
                        g.m_forward.m_column.begin() + next_row_start);
}

// Extra, common functions
template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename graph_traits<BOOST_CSR_GRAPH_TYPE>::vertex_descriptor
vertex(typename graph_traits<BOOST_CSR_GRAPH_TYPE>::vertex_descriptor i,
       const BOOST_CSR_GRAPH_TYPE&)
{
  return i;
}

// edge() can be provided in linear time for the new interface

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline std::pair<typename BOOST_CSR_GRAPH_TYPE::edge_descriptor, bool>
edge(Vertex i, Vertex j, const BOOST_CSR_GRAPH_TYPE& g)
{
  typedef typename BOOST_CSR_GRAPH_TYPE::out_edge_iterator out_edge_iter;
  std::pair<out_edge_iter, out_edge_iter> range = out_edges(i, g);
  for (; range.first != range.second; ++range.first) {
    if (target(*range.first, g) == j)
      return std::make_pair(*range.first, true);
  }
  return std::make_pair(typename BOOST_CSR_GRAPH_TYPE::edge_descriptor(),
                        false);
}

// Find an edge given its index in the graph
template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename BOOST_CSR_GRAPH_TYPE::edge_descriptor
edge_from_index(EdgeIndex idx, const BOOST_CSR_GRAPH_TYPE& g)
{
  typedef typename std::vector<EdgeIndex>::const_iterator row_start_iter;
  BOOST_ASSERT (idx < num_edges(g));
  row_start_iter src_plus_1 =
    std::upper_bound(g.m_forward.m_rowstart.begin(),
                     g.m_forward.m_rowstart.end(),
                     idx);
    // Get last source whose rowstart is at most idx
    // upper_bound returns this position plus 1
  Vertex src = (src_plus_1 - g.m_forward.m_rowstart.begin()) - 1;
  return typename BOOST_CSR_GRAPH_TYPE::edge_descriptor(src, idx);
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline EdgeIndex
num_edges(const BOOST_CSR_GRAPH_TYPE& g)
{
  return g.m_forward.m_column.size();
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
std::pair<typename BOOST_CSR_GRAPH_TYPE::edge_iterator,
          typename BOOST_CSR_GRAPH_TYPE::edge_iterator>
edges(const BOOST_CSR_GRAPH_TYPE& g)
{
  typedef typename BOOST_CSR_GRAPH_TYPE::edge_iterator ei;
  typedef typename BOOST_CSR_GRAPH_TYPE::edge_descriptor edgedesc;
  if (g.m_forward.m_rowstart.size() == 1 || g.m_forward.m_column.empty()) {
    return std::make_pair(ei(), ei());
  } else {
    // Find the first vertex that has outgoing edges
    Vertex src = 0;
    while (g.m_forward.m_rowstart[src + 1] == 0) ++src;
    return std::make_pair(ei(g, edgedesc(src, 0), g.m_forward.m_rowstart[src + 1]),
                          ei(g, edgedesc(num_vertices(g), g.m_forward.m_column.size()), 0));
  }
}

// For Property Graph

// Graph properties
template<BOOST_CSR_GRAPH_TEMPLATE_PARMS, class Tag, class Value>
inline void
set_property(BOOST_CSR_GRAPH_TYPE& g, Tag tag, const Value& value)
{
  get_property_value(g.m_property, tag) = value;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS, class Tag>
inline
typename graph_property<BOOST_CSR_GRAPH_TYPE, Tag>::type&
get_property(BOOST_CSR_GRAPH_TYPE& g, Tag tag)
{
  return get_property_value(g.m_property, tag);
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS, class Tag>
inline
const
typename graph_property<BOOST_CSR_GRAPH_TYPE, Tag>::type&
get_property(const BOOST_CSR_GRAPH_TYPE& g, Tag tag)
{
  return get_property_value(g.m_property, tag);
}

template <typename G, typename Tag, typename Kind>
struct csr_property_map_helper {};
// Kind == void for invalid property tags, so we can use that to SFINAE out

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
struct csr_property_map_helper<BOOST_CSR_GRAPH_TYPE, Tag, vertex_property_tag> {
  typedef vertex_all_t all_tag;
  typedef typename property_traits<typename property_map<BOOST_CSR_GRAPH_TYPE, vertex_all_t>::type>::key_type key_type;
  typedef VertexProperty plist_type;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, vertex_all_t>::type all_type;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, vertex_all_t>::const_type all_const_type;
  typedef transform_value_property_map<detail::lookup_one_property_f<plist_type, Tag>, all_type> type;
  typedef transform_value_property_map<detail::lookup_one_property_f<const plist_type, Tag>, all_const_type> const_type;
};

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
struct csr_property_map_helper<BOOST_CSR_GRAPH_TYPE, Tag, edge_property_tag> {
  typedef edge_all_t all_tag;
  typedef typename property_traits<typename property_map<BOOST_CSR_GRAPH_TYPE, edge_all_t>::type>::key_type key_type;
  typedef EdgeProperty plist_type;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, edge_all_t>::type all_type;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, edge_all_t>::const_type all_const_type;
  typedef transform_value_property_map<detail::lookup_one_property_f<plist_type, Tag>, all_type> type;
  typedef transform_value_property_map<detail::lookup_one_property_f<const plist_type, Tag>, all_const_type> const_type;
};

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
struct csr_property_map_helper<BOOST_CSR_GRAPH_TYPE, Tag, graph_property_tag> {
  typedef graph_all_t all_tag;
  typedef BOOST_CSR_GRAPH_TYPE* key_type;
  typedef GraphProperty plist_type;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, graph_all_t>::type all_type;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, graph_all_t>::const_type all_const_type;
  typedef transform_value_property_map<detail::lookup_one_property_f<plist_type, Tag>, all_type> type;
  typedef transform_value_property_map<detail::lookup_one_property_f<const plist_type, Tag>, all_const_type> const_type;
};

// disable_if isn't truly necessary but required to avoid ambiguity with specializations below
template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
struct property_map<BOOST_CSR_GRAPH_TYPE, Tag, typename disable_if<detail::is_distributed_selector<Vertex> >::type>:
  csr_property_map_helper<
    BOOST_CSR_GRAPH_TYPE,
    Tag,
    typename detail::property_kind_from_graph<BOOST_CSR_GRAPH_TYPE, Tag>
               ::type> {};

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::type
get(Tag tag, BOOST_CSR_GRAPH_TYPE& g) {
  return typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::type(tag, get(typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::all_tag(), g));
}

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::const_type
get(Tag tag, const BOOST_CSR_GRAPH_TYPE& g) {
  return typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::const_type(tag, get(typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::all_tag(), g));
}

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
typename property_traits<typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::type>::reference
get(Tag tag, BOOST_CSR_GRAPH_TYPE& g, typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::key_type k) {
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::all_tag all_tag;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, all_tag>::type outer_pm;
  return lookup_one_property<typename property_traits<outer_pm>::value_type, Tag>::lookup(get(all_tag(), g, k), tag);
}

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
typename property_traits<typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::const_type>::reference
get(Tag tag, const BOOST_CSR_GRAPH_TYPE& g, typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::key_type k) {
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::all_tag all_tag;
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, all_tag>::const_type outer_pm;
  return lookup_one_property<const typename property_traits<outer_pm>::value_type, Tag>::lookup(get(all_tag(), g, k), tag);
}

template <BOOST_CSR_GRAPH_TEMPLATE_PARMS, typename Tag>
void
put(Tag tag,
    BOOST_CSR_GRAPH_TYPE& g,
    typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::key_type k,
    typename lookup_one_property<typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::plist_type, Tag>::type val) {
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::all_tag all_tag;
  lookup_one_property<typename property_map<BOOST_CSR_GRAPH_TYPE, Tag>::plist_type, Tag>::lookup(get(all_tag(), g, k), tag) = val;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
struct property_map<BOOST_CSR_GRAPH_TYPE, vertex_index_t, typename disable_if<detail::is_distributed_selector<Vertex> >::type>
{
  typedef typed_identity_property_map<Vertex> type;
  typedef type const_type;
};

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
struct property_map<BOOST_CSR_GRAPH_TYPE, edge_index_t, typename disable_if<detail::is_distributed_selector<Vertex> >::type>
{
  typedef detail::csr_edge_index_map<Vertex, EdgeIndex> type;
  typedef type const_type;
};

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
struct property_map<BOOST_CSR_GRAPH_TYPE, vertex_all_t, typename disable_if<detail::is_distributed_selector<Vertex> >::type>
{
  typedef typename BOOST_CSR_GRAPH_TYPE::inherited_vertex_properties::vertex_map_type type;
  typedef typename BOOST_CSR_GRAPH_TYPE::inherited_vertex_properties::const_vertex_map_type const_type;
};

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
struct property_map<BOOST_CSR_GRAPH_TYPE, edge_all_t, typename disable_if<detail::is_distributed_selector<Vertex> >::type>
{
  typedef typename BOOST_CSR_GRAPH_TYPE::forward_type::inherited_edge_properties::edge_map_type type;
  typedef typename BOOST_CSR_GRAPH_TYPE::forward_type::inherited_edge_properties::const_edge_map_type const_type;
};

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
struct property_map<BOOST_CSR_GRAPH_TYPE, graph_all_t, typename disable_if<detail::is_distributed_selector<Vertex> >::type>
{
  typedef boost::ref_property_map<BOOST_CSR_GRAPH_TYPE*, typename BOOST_CSR_GRAPH_TYPE::graph_property_type> type;
  typedef boost::ref_property_map<BOOST_CSR_GRAPH_TYPE*, const typename BOOST_CSR_GRAPH_TYPE::graph_property_type> const_type;
};

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typed_identity_property_map<Vertex>
get(vertex_index_t, const BOOST_CSR_GRAPH_TYPE&)
{
  return typed_identity_property_map<Vertex>();
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
get(vertex_index_t,
    const BOOST_CSR_GRAPH_TYPE&, Vertex v)
{
  return v;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typed_identity_property_map<Vertex>
get(vertex_index_t, BOOST_CSR_GRAPH_TYPE&)
{
  return typed_identity_property_map<Vertex>();
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline Vertex
get(vertex_index_t,
    BOOST_CSR_GRAPH_TYPE&, Vertex v)
{
  return v;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, edge_index_t>::const_type
get(edge_index_t, const BOOST_CSR_GRAPH_TYPE&)
{
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, edge_index_t>::const_type
    result_type;
  return result_type();
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline EdgeIndex
get(edge_index_t, const BOOST_CSR_GRAPH_TYPE&,
    typename BOOST_CSR_GRAPH_TYPE::edge_descriptor e)
{
  return e.idx;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, edge_index_t>::const_type
get(edge_index_t, BOOST_CSR_GRAPH_TYPE&)
{
  typedef typename property_map<BOOST_CSR_GRAPH_TYPE, edge_index_t>::const_type
    result_type;
  return result_type();
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline EdgeIndex
get(edge_index_t, BOOST_CSR_GRAPH_TYPE&,
    typename BOOST_CSR_GRAPH_TYPE::edge_descriptor e)
{
  return e.idx;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, vertex_all_t>::type
get(vertex_all_t, BOOST_CSR_GRAPH_TYPE& g)
{
  return g.get_vertex_bundle(get(vertex_index, g));
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, vertex_all_t>::const_type
get(vertex_all_t, const BOOST_CSR_GRAPH_TYPE& g)
{
  return g.get_vertex_bundle(get(vertex_index, g));
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline VertexProperty&
get(vertex_all_t,
    BOOST_CSR_GRAPH_TYPE& g, Vertex v)
{
  return get(vertex_all, g)[v];
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline const VertexProperty&
get(vertex_all_t,
    const BOOST_CSR_GRAPH_TYPE& g, Vertex v)
{
  return get(vertex_all, g)[v];
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline void
put(vertex_all_t,
    BOOST_CSR_GRAPH_TYPE& g,
    Vertex v,
    const VertexProperty& val)
{
  put(get(vertex_all, g), v, val);
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, edge_all_t>::type
get(edge_all_t, BOOST_CSR_GRAPH_TYPE& g)
{
  return g.m_forward.get_edge_bundle(get(edge_index, g));
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, edge_all_t>::const_type
get(edge_all_t, const BOOST_CSR_GRAPH_TYPE& g)
{
  return g.m_forward.get_edge_bundle(get(edge_index, g));
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline EdgeProperty&
get(edge_all_t,
    BOOST_CSR_GRAPH_TYPE& g,
    const typename BOOST_CSR_GRAPH_TYPE::edge_descriptor& e)
{
  return get(edge_all, g)[e];
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline const EdgeProperty&
get(edge_all_t,
    const BOOST_CSR_GRAPH_TYPE& g,
    const typename BOOST_CSR_GRAPH_TYPE::edge_descriptor& e)
{
  return get(edge_all, g)[e];
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline void
put(edge_all_t,
    BOOST_CSR_GRAPH_TYPE& g,
    const typename BOOST_CSR_GRAPH_TYPE::edge_descriptor& e,
    const EdgeProperty& val)
{
  put(get(edge_all, g), e, val);
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, graph_all_t>::type
get(graph_all_t, BOOST_CSR_GRAPH_TYPE& g)
{
  return typename property_map<BOOST_CSR_GRAPH_TYPE, graph_all_t>::type(g.m_property);
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline typename property_map<BOOST_CSR_GRAPH_TYPE, graph_all_t>::const_type
get(graph_all_t, const BOOST_CSR_GRAPH_TYPE& g)
{
  return typename property_map<BOOST_CSR_GRAPH_TYPE, graph_all_t>::const_type(g.m_property);
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline GraphProperty&
get(graph_all_t,
    BOOST_CSR_GRAPH_TYPE& g,
    BOOST_CSR_GRAPH_TYPE*)
{
  return g.m_property;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline const GraphProperty&
get(graph_all_t,
    const BOOST_CSR_GRAPH_TYPE& g,
    BOOST_CSR_GRAPH_TYPE*)
{
  return g.m_property;
}

template<BOOST_CSR_GRAPH_TEMPLATE_PARMS>
inline void
put(graph_all_t,
    BOOST_CSR_GRAPH_TYPE& g,
    BOOST_CSR_GRAPH_TYPE*,
    const GraphProperty& val)
{
  g.m_property = val;
}

#undef BOOST_CSR_GRAPH_TYPE
#undef BOOST_CSR_GRAPH_TEMPLATE_PARMS
#undef BOOST_DIR_CSR_GRAPH_TYPE
#undef BOOST_DIR_CSR_GRAPH_TEMPLATE_PARMS
#undef BOOST_BIDIR_CSR_GRAPH_TYPE
#undef BOOST_BIDIR_CSR_GRAPH_TEMPLATE_PARMS

} // end namespace boost

#endif // BOOST_GRAPH_COMPRESSED_SPARSE_ROW_GRAPH_HPP
