Program Listing for File reportviews.hpp

Return to documentation for file (xnetwork/classes/reportviews.hpp)

#pragma once

//    Copyright (C) 2004-2018 by
//    Wai-Shing Luk <luk036@gmail.com>
//
//
//    All rights reserved.
//    BSD license.
//
// Authors: Wai-Shing Luk (luk036@gmail.com),
//          Pieter Swart (swart@lanl.gov),
//          Dan Schult(dschult@colgate.edu);
// from collections import Mapping, Set, Iterable
// #include <initializer_list>
#include <utility>

namespace xn
{

// static const auto __all__ = {"NodeView",
//                              "NodeDataView",
//                              "EdgeView",
//                              "OutEdgeView",
//                              "InEdgeView",
//                              "EdgeDataView",
//                              "OutEdgeDataView",
//                              "InEdgeDataView",
//                              "MultiEdgeView",
//                              "OutMultiEdgeView",
//                              "InMultiEdgeView",
//                              "MultiEdgeDataView",
//                              "OutMultiEdgeDataView",
//                              "InMultiEdgeDataView",
//                              "DegreeView",
//                              "DiDegreeView",
//                              "InDegreeView",
//                              "OutDegreeView",
//                              "MultiDegreeView",
//                              "DiMultiDegreeView",
//                              "InMultiDegreeView",
//                              "OutMultiDegreeView"};

// NodeViews
// interface: Mapping, Set
template <typename nodeview_t>
class NodeView
{
  private:
    using Self = NodeView<nodeview_t>;
    using Node = typename nodeview_t::value_type;

    nodeview_t& _nodes;

    // auto __getstate__( ) {
    //     return {"_nodes": this->_nodes};
    // }

    // auto __setstate__( state) {
    //     this->_nodes = state["_nodes"];
    // }
  public:
    explicit NodeView(nodeview_t& nodes)
        : _nodes {nodes}
    {
    }

    // Mapping methods
    auto size()
    {
        return this->_nodes.size();
    }

    auto begin()
    {
        return std::begin(this->_nodes);
    }

    auto end()
    {
        return std::end(this->_nodes);
    }

    auto operator[](const Node& n) const -> const auto&
    {
        return this->_nodes[n];
    }

    auto operator[](const Node& n) -> auto&
    {
        return this->_nodes[n];
    }

    // Set methods
    auto contains(const Node& n) -> bool
    {
        return this->_nodes.contains(n);
    }

    // /// @classmethod
    // auto _from_iterable(cls, it) {
    //     return set(it);
    // }

    // // DataView method
    // auto __call__( data=false, default=None) {
    //     if (data == false) {
    //         return (*this);
    //     }
    //     return NodeDataView(this->_nodes, data, default);
    // }

    // auto data( data=true, default=None) {
    //     if (data == false) {
    //         return (*this);
    //     }
    //     return NodeDataView(this->_nodes, data, default);
    // }

    // auto __str__( ) {
    //     return str(list( ));
    // }

    // auto __repr__( ) {
    //     return "%s(%r)" % (this->__class__.__name__, tuple( ));
    // }
};

// class NodeDataView: public Set {
//     /*! A DataView class for nodes of a XNetwork Graph

//     The main use for this class is to iterate through node-data pairs.
//     The data can be the entire data-dictionary for each node, or it
//     can be a specific attribute (with default) for each node.
//     Set operations are enabled with NodeDataView, but don't work in
//     cases where the data is not hashable. Use with caution.
//     Typically, set operations on nodes use NodeView, not NodeDataView.
//     That is, they use `G.nodes` instead of `G.nodes(data="foo")`.

//     Parameters
//     ==========
//     graph : XNetwork graph-like class
//     data : bool or string (default=false);
//     default : object (default=None);
//      */
//     using _Self = NodeDataView;

//     static const auto __slots__ = ("_nodes", "_data", "_default");

//     // auto __getstate__( ) {
//     //     return {"_nodes": this->_nodes,
//     //             "_data": this->_data,
//     //             "_default": this->_default};
//     // }

//     // auto __setstate__( state) {
//     //     this->_nodes = state["_nodes"];
//     //     this->_data = state["_data"];
//     //     this->_default = state["_default"];
//     // }

//     explicit _Self( nodedict, data=false, default=None) {
//         this->_nodes = nodedict;
//         this->_data = data;
//         this->_default = default;
//     }

//     /// @classmethod
//     auto _from_iterable(cls, it) {
//         try {
//             return set(it);
//         } catch (TypeError as err) {
//             if ("unhashable" : str(err) {
//                 const auto msg = " : Could be b/c data=true or your values
//                 are unhashable"; throw TypeError(str(err) + msg);
//             }
//             throw;
//         }
//     }

//     auto __len__( ) {
//         return len(this->_nodes);
//     }

//     auto __iter__( ) {
//         data = this->_data;
//         if (data == false) {
//             return iter(this->_nodes);
//         }
//         if (data == true) {
//             return iter(this->_nodes.items());
//         }
//         return ((n, dd[data] if (data : dd else this->_default)
//                 for (auto n, dd : this->_nodes.items());
//     }

//     bool contains( n) {
//         try {
//             node_in = n : this->_nodes;
//         } catch (TypeError) {
//             n, d = n;
//             return n : this->_nodes and self[n] == d;
//         }
//         if (node_in == true) {
//             return node_in;
//         }
//         try {
//             n, d = n;
//         } catch ((TypeError, ValueError) {
//             return false;
//         }
//         return n : this->_nodes and self[n] == d;
//     }

//     auto operator[]( n) {
//         ddict = this->_nodes[n];
//         data = this->_data;
//         if (data == false or data == true) {
//             return ddict;
//         }
//         return ddict[data] if (data : ddict else this->_default;
//     }

//     auto __str__( ) {
//         return str(list( ));
//     }

//     // auto __repr__( ) {
//     //     if (this->_data == false) {
//     //         return "%s(%r)" % (this->__class__.__name__, tuple( ));
//     //     }
//     //     if (this->_data == true) {
//     //         return "%s(%r)" % (this->__class__.__name__, dict( ));
//     //     }
//     //     return "%s(%r, data=%r)" %
//     //            this->__class__.__name__, dict( ), this->_data);
//     // }
// };

// // DegreeViews
// /*! A View class for degree of nodes : a XNetwork Graph

//     The functionality is like dict.items() with (node, degree) pairs.
//     Additional functionality includes read-only lookup of node degree,
//     and calling with optional features nbunch (for only a subset of nodes);
//     and weight (use edge weights to compute degree).

//     Parameters
//     ==========
//     graph : XNetwork graph-like class
//     nbunch : node, container of nodes, or None meaning all nodes
//     (default=None); weight : bool or string (default=None);

//     Notes
//     -----
//     DegreeView can still lookup any node even if (nbunch is specified.

//     Examples
//     --------
//     >>> G = xn::path_graph(3);
//     >>> DV = G.degree();
//     >>> assert(DV[2] == 1);
//     >>> assert(sum(deg for n, deg : DV) == 4);

//     >>> DVweight = G.degree(weight="span");
//     >>> G.add_edge(1, 2, span=34);
//     >>> DVweight[2];
//     34
//     >>> DVweight[0];  //  default edge weight is 1
//     1
//     >>> sum(span for n, span : DVweight);  // sum weighted degrees
//     70

//     >>> DVnbunch = G.degree(nbunch=(1, 2));
//     >>> assert(len(list(DVnbunch)) == 2);  // iteration over nbunch only
// */
// class DiDegreeView: public object {
//     using _Self = DiDegreeView;

//     explicit _Self( G, nbunch=None, weight=None) {
//         this->_graph = G;
//         this->_succ = G._adj;
//         this->_pred = G._adj;
//         this->_nodes = this->_succ if (nbunch.empty()
//             else list(G.nbunch_iter(nbunch));
//         this->_weight = weight;
//     }

//     auto __call__( nbunch=None, weight=None) {
//         if (nbunch.empty()) {
//             if (weight == this->_weight) {
//                 return (*this);
//             }
//             return this->__class__(this->_graph, None, weight);
//         }
//         try {
//             if (nbunch : this->_nodes) {
//                 if (weight == this->_weight) {
//                     return (*this)[nbunch];
//                 }
//                 return this->__class__(this->_graph, None, weight)[nbunch];
//             }
//         } catch (TypeError) {
//             // pass;
//         }
//         return this->__class__(this->_graph, nbunch, weight);
//     }

//     auto operator[]( n) {
//         weight = this->_weight;
//         succs = this->_succ[n];
//         preds = this->_pred[n];
//         if (weight.empty()) {
//             return len(succs) + len(preds);
//         }
//         return sum(dd.get(weight, 1) for dd : succs.values()) +
//             sum(dd.get(weight, 1) for dd : preds.values());
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 succs = this->_succ[n];
//                 preds = this->_pred[n];
//                 yield (n, len(succs) + len(preds));
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 succs = this->_succ[n];
//                 preds = this->_pred[n];
//                 deg = sum(dd.get(weight, 1) for dd : succs.values())
//                     + sum(dd.get(weight, 1) for dd : preds.values());
//                 yield (n, deg);
//             }
//         }
//     }

//     auto __len__( ) {
//         return len(this->_nodes);
//     }

//     auto __str__( ) {
//         return str(list( ));
//     }

//     // auto __repr__( ) {
//     //     return "%s(%r)" % (this->__class__.__name__, dict( ));
//     // }
// };

// class DegreeView: public DiDegreeView {
//     /*! A DegreeView class to act as G.degree for a XNetwork Graph

//     Typical usage focuses on iteration over `(node, degree)` pairs.
//     The degree is by default the number of edges incident to the node.
//     Optional argument `weight` enables weighted degree using the edge
//     attribute named : the `weight` argument.  Reporting and iteration
//     can also be restricted to a subset of nodes using `nbunch`.

//     Additional functionality include node lookup so that `G.degree[n]`
//     reported the (possibly weighted) degree of node `n`. Calling the
//     view creates a view with different arguments `nbunch` or `weight`.

//     Parameters
//     ==========
//     graph : XNetwork graph-like class
//     nbunch : node, container of nodes, or None meaning all nodes
//     (default=None); weight : string or None (default=None);

//     Notes
//     -----
//     DegreeView can still lookup any node even if (nbunch is specified.

//     Examples
//     --------
//     >>> G = xn::path_graph(3);
//     >>> DV = G.degree();
//     >>> assert(DV[2] == 1);
//     >>> assert(G.degree[2] == 1);
//     >>> assert(sum(deg for n, deg : DV) == 4);

//     >>> DVweight = G.degree(weight="span");
//     >>> G.add_edge(1, 2, span=34);
//     >>> DVweight[2];
//     34
//     >>> DVweight[0];  //  default edge weight is 1
//     1
//     >>> sum(span for n, span : DVweight);  // sum weighted degrees
//     70

//     >>> DVnbunch = G.degree(nbunch=(1, 2));
//     >>> assert(len(list(DVnbunch)) == 2);  // iteration over nbunch only
//      */

//     auto operator[]( n) {
//         weight = this->_weight;
//         nbrs = this->_succ[n];
//         if (weight.empty()) {
//             return len(nbrs) + (n : nbrs);
//         }
//         return sum(dd.get(weight, 1) for dd : nbrs.values()) +
//             (n : nbrs and nbrs[n].get(weight, 1));
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_succ[n];
//                 yield (n, len(nbrs) + (n : nbrs));
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_succ[n];
//                 deg = sum(dd.get(weight, 1) for dd : nbrs.values()) +
//                     (n : nbrs and nbrs[n].get(weight, 1));
//                 yield (n, deg);
//             }
//         }
//     }
// };

// class OutDegreeView: public DiDegreeView {
//     /*! A DegreeView class to report out_degree for a DiGraph; See DegreeView
//     */

//     auto operator[]( n) {
//         weight = this->_weight;
//         nbrs = this->_succ[n];
//         if (this->_weight.empty()) {
//             return len(nbrs);
//         }
//         return sum(dd.get(this->_weight, 1) for dd : nbrs.values());
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 succs = this->_succ[n];
//                 yield (n, len(succs));
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 succs = this->_succ[n];
//                 deg = sum(dd.get(weight, 1) for dd : succs.values());
//                 yield (n, deg);
//             }
//         }
//     }
// };

// class InDegreeView: public DiDegreeView {
//     /*! A DegreeView class to report in_degree for a DiGraph; See DegreeView
//     */

//     auto operator[]( n) {
//         weight = this->_weight;
//         nbrs = this->_pred[n];
//         if (weight.empty()) {
//             return len(nbrs);
//         }
//         return sum(dd.get(weight, 1) for dd : nbrs.values());
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 preds = this->_pred[n];
//                 yield (n, len(preds));
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 preds = this->_pred[n];
//                 deg = sum(dd.get(weight, 1) for dd : preds.values());
//                 yield (n, deg);
//             }
//         }
//     }
// };

// class MultiDegreeView: public DiDegreeView {
//     /*! A DegreeView class for undirected multigraphs; See DegreeView */

//     auto operator[]( n) {
//         weight = this->_weight;
//         nbrs = this->_succ[n];
//         if (weight.empty()) {
//             return sum(len(keys) for keys : nbrs.values()) +
//                 (n : nbrs and len(nbrs[n]));
//         }
//         // edge weighted graph - degree is sum of nbr edge weights
//         deg = sum(d.get(weight, 1) for key_dict : nbrs.values();
//                   for (auto d : key_dict.values());
//         if (n : nbrs) {
//             deg += sum(d.get(weight, 1) for d : nbrs[n].values());
//         }
//         return deg;
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_succ[n];
//                 deg = sum(len(keys) for keys : nbrs.values()) +
//                     (n : nbrs and len(nbrs[n]));
//                 yield (n, deg);
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_succ[n];
//                 deg = sum(d.get(weight, 1) for key_dict : nbrs.values();
//                           for (auto d : key_dict.values());
//                 if (n : nbrs) {
//                     deg += sum(d.get(weight, 1) for d : nbrs[n].values());
//                 }
//                 yield (n, deg);
//             }
//         }
//     }
// };

// class DiMultiDegreeView: public DiDegreeView {
//     /*! A DegreeView class for MultiDiGraph; See DegreeView */

//     auto operator[]( n) {
//         weight = this->_weight;
//         succs = this->_succ[n];
//         preds = this->_pred[n];
//         if (weight.empty()) {
//             return sum(len(keys) for keys : succs.values()) +
//                 sum(len(keys) for keys : preds.values());
//         }
//         // edge weighted graph - degree is sum of nbr edge weights
//         deg = sum(d.get(weight, 1) for key_dict : succs.values();
//                   for (auto d : key_dict.values()) +
//             sum(d.get(weight, 1) for key_dict : preds.values();
//                 for (auto d : key_dict.values());
//         return deg;
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 succs = this->_succ[n];
//                 preds = this->_pred[n];
//                 deg = sum(len(keys) for keys : succs.values()) +
//                     sum(len(keys) for keys : preds.values());
//                 yield (n, deg);
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 succs = this->_succ[n];
//                 preds = this->_pred[n];
//                 deg = sum(d.get(weight, 1) for key_dict : succs.values();
//                           for (auto d : key_dict.values()) +
//                     sum(d.get(weight, 1) for key_dict : preds.values();
//                         for (auto d : key_dict.values());
//                 yield (n, deg);
//             }
//         }
//     }
// };

// class InMultiDegreeView: public DiDegreeView {
//     /*! A DegreeView class for inward degree of MultiDiGraph; See DegreeView
//     */

//     auto operator[]( n) {
//         weight = this->_weight;
//         nbrs = this->_pred[n];
//         if (weight.empty()) {
//             return sum(len(data) for data : nbrs.values());
//         }
//         // edge weighted graph - degree is sum of nbr edge weights
//         return sum(d.get(weight, 1) for key_dict : nbrs.values();
//                    for (auto d : key_dict.values());
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_pred[n];
//                 deg = sum(len(data) for data : nbrs.values());
//                 yield (n, deg);
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_pred[n];
//                 deg = sum(d.get(weight, 1) for key_dict : nbrs.values();
//                           for (auto d : key_dict.values());
//                 yield (n, deg);
//             }
//         }
//     }
// };

// class OutMultiDegreeView: public DiDegreeView {
//     /*! A DegreeView class for outward degree of MultiDiGraph; See DegreeView
//     */

//     auto operator[]( n) {
//         weight = this->_weight;
//         nbrs = this->_succ[n];
//         if (weight.empty()) {
//             return sum(len(data) for data : nbrs.values());
//         }
//         // edge weighted graph - degree is sum of nbr edge weights
//         return sum(d.get(weight, 1) for key_dict : nbrs.values();
//                    for (auto d : key_dict.values());
//     }

//     auto __iter__( ) {
//         weight = this->_weight;
//         if (weight.empty()) {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_succ[n];
//                 deg = sum(len(data) for data : nbrs.values());
//                 yield (n, deg);
//             }
//         } else {
//             for (auto n : this->_nodes) {
//                 nbrs = this->_succ[n];
//                 deg = sum(d.get(weight, 1) for key_dict : nbrs.values();
//                           for (auto d : key_dict.values());
//                 yield (n, deg);
//             }
//         }
//     }
// };

// // EdgeDataViews
// class OutEdgeDataView: public object {
//     /*! EdgeDataView for outward edges of DiGraph; See EdgeDataView */
//     static const auto __slots__ = ("_viewer", "_nbunch", "_data", "_default",
//                  "_adjdict", "_nodes_nbrs", "_report");

//     auto __getstate__( ) {
//         return {"viewer": this->_viewer,
//                 "nbunch": this->_nbunch,
//                 "data": this->_data,;
//                 "default": this->_default};
//     }

//     auto __setstate__( state) {
//         this->__init__(**state);
//     }

//     explicit _Self( viewer, nbunch=None, data=false, default=None) {
//         this->_viewer = viewer;
//         this->_adjdict = viewer._adjdict;
//         if (nbunch.empty()) {
//             this->_nodes_nbrs = this->_adjdict.items;
//         } else {
//             nbunch = list(viewer._graph.nbunch_iter(nbunch));
//             this->_nodes_nbrs = lambda: [(n, this->_adjdict[n]) for n :
//             nbunch];
//         }
//         this->_nbunch = nbunch;
//         this->_data = data;
//         this->_default = default;
//         // Set _report based on data and default
//         if (data == true) {
//             this->_report = lambda n, nbr, dd: (n, nbr, dd);
//         } else if (data == false) {
//             this->_report = lambda n, nbr, dd: (n, nbr);
//         } else { //data is attribute name
//             this->_report = lambda n, nbr, dd:
//                 (n, nbr, dd[data]) if (data : dd else (n, nbr, default);
//         }
//     }

//     auto __len__( ) {
//         return sum(len(nbrs) for n, nbrs : this->_nodes_nbrs());
//     }

//     auto __iter__( ) {
//         return (this->_report(n, nbr, dd) for n, nbrs : this->_nodes_nbrs();
//                 for (auto nbr, dd : nbrs.items());
//     }

//     bool contains( e) {
//         try {
//             auto [u, v] = e[:2];
//             ddict = this->_adjdict[u][v];
//         } catch (KeyError) {
//             return false;
//         }
//         return e == this->_report(u, v, ddict);
//     }

//     auto __str__( ) {
//         return str(list( ));
//     }

//     // auto __repr__( ) {
//     //     return "%s(%r)" % (this->__class__.__name__, list( ));
//     // }
// };

// class EdgeDataView(OutEdgeDataView) {
//     /*! A EdgeDataView class for edges of Graph

//     This view is primarily used to iterate over the edges reporting
//     edges as node-tuples with edge data optionally reported. The
//     argument `nbunch` allows restriction to edges incident to nodes
//     : that container/singleton. The default (nbunch=None);
//     reports all edges. The arguments `data` and `default` control
//     what edge data is reported. The default `data == false` reports
//     only node-tuples for each edge. If `data is true` the entire edge
//     data dict is returned. Otherwise `data` is assumed to hold the name
//     of the edge attribute to report with default `default` if ( that
//     edge attribute is not present.

//     Parameters
//     ----------
//     nbunch : container of nodes, node or None (default None);
//     data : false, true or string (default false);
//     default : default value (default None);

//     Examples
//     --------
//     >>> G = xn::path_graph(3);
//     >>> G.add_edge(1, 2, foo="bar");
//     >>> list(G.edges(data="foo", default="biz"));
//     [(0, 1, "biz"), (1, 2, "bar")];
//     >>> assert((0, 1, "biz"] : G.edges(data="foo", default="biz"));
//      */
//     static const auto __slots__ = ();

//     auto __len__( ) {
//         return sum(1 for e : *this);
//     }

//     auto __iter__( ) {
//         seen = {};
//         for (auto n, nbrs : this->_nodes_nbrs() {
//             for (auto nbr, dd : nbrs.items() {
//                 if (nbr not : seen) {
//                     yield this->_report(n, nbr, dd);
//                 }
//             }
//             seen[n] = 1;
//         }
//         del seen;
//     }

//     bool contains( e) {
//         try {
//             auto [u, v] = e[:2];
//             ddict = this->_adjdict[u][v];
//         } catch (KeyError) {
//             try {
//                 ddict = this->_adjdict[v][u];
//             } catch (KeyError) {
//                 return false;
//             }
//         }
//         return e == this->_report(u, v, ddict);
//     }
// };

// class InEdgeDataView(OutEdgeDataView) {
//     /*! An EdgeDataView class for outward edges of DiGraph; See EdgeDataView
//     */ static const auto __slots__ = ();

//     auto __iter__( ) {
//         return (this->_report(nbr, n, dd) for n, nbrs : this->_nodes_nbrs();
//                 for (auto nbr, dd : nbrs.items());
//     }

//     bool contains( e) {
//         try {
//             auto [u, v] = e[:2];
//             ddict = this->_adjdict[v][u];
//         } catch (KeyError) {
//             return false;
//         }
//         return e == this->_report(u, v, ddict);
//     }
// };

// class OutMultiEdgeDataView(OutEdgeDataView) {
//     /*! An EdgeDataView for outward edges of MultiDiGraph; See EdgeDataView
//     */ static const auto __slots__ = ("keys",);

//     auto __getstate__( ) {
//         return {"viewer": this->_viewer,
//                 "nbunch": this->_nbunch,
//                 "keys": this->keys,
//                 "data": this->_data,
//                 "default": this->_default};
//     }

//     auto __setstate__( state) {
//         this->__init__(**state);
//     }

//     explicit _Self( viewer, nbunch=None,
//                  data=false, keys=false, default=None) {
//         this->_viewer = viewer;
//         this->_adjdict = viewer._adjdict;
//         this->keys = keys;
//         if (nbunch.empty()) {
//             this->_nodes_nbrs = this->_adjdict.items;
//         } else {
//             nbunch = list(viewer._graph.nbunch_iter(nbunch));
//             this->_nodes_nbrs = lambda: [(n, this->_adjdict[n]) for n :
//             nbunch];
//         }
//         this->_nbunch = nbunch;
//         this->_data = data;
//         this->_default = default;
//         // Set _report based on data and default
//         if (data == true) {
//             if (keys == true) {
//                 this->_report = lambda n, nbr, k, dd: (n, nbr, k, dd);
//             } else {
//                 this->_report = lambda n, nbr, k, dd: (n, nbr, dd);
//             }
//         } else if (data == false) {
//             if (keys == true) {
//                 this->_report = lambda n, nbr, k, dd: (n, nbr, k);
//             } else {
//                 this->_report = lambda n, nbr, k, dd: (n, nbr);
//             }
//         } else { //data is attribute name
//             if (keys == true) {
//                 this->_report = lambda n, nbr, k, dd: (n, nbr, k, dd[data])
//                     if (data : dd else (n, nbr, k, default);
//             } else {
//                 this->_report = lambda n, nbr, k, dd: (n, nbr, dd[data])
//                     if (data : dd else (n, nbr, default);
//             }
//         }
//     }

//     auto __len__( ) {
//         return sum(1 for e : *this);
//     }

//     auto __iter__( ) {
//         return (this->_report(n, nbr, k, dd) for n, nbrs :
//         this->_nodes_nbrs();
//                 for (auto nbr, kd : nbrs.items() for k, dd : kd.items());
//     }

//     bool contains( e) {
//         auto [u, v] = e[:2];
//         try {
//             kdict = this->_adjdict[u][v];
//         } catch (KeyError) {
//             return false;
//         }
//         if (this->keys == true) {
//             k = e[2];
//             try {
//                 dd = kdict[k];
//             } catch (KeyError) {
//                 return false;
//             }
//             return e == this->_report(u, v, k, dd);
//         }
//         for (auto k, dd : kdict.items() {
//             if (e == this->_report(u, v, k, dd) {
//                 return true;
//             }
//         }
//         return false;
//     }
// };

// class MultiEdgeDataView(OutMultiEdgeDataView) {
//     /*! An EdgeDataView class for edges of MultiGraph; See EdgeDataView */
//     static const auto __slots__ = ();

//     auto __iter__( ) {
//         seen = {};
//         for (auto n, nbrs : this->_nodes_nbrs() {
//             for (auto nbr, kd : nbrs.items() {
//                 if (nbr not : seen) {
//                     for (auto k, dd : kd.items() {
//                         yield this->_report(n, nbr, k, dd);
//                     }
//                 }
//             }
//             seen[n] = 1;
//         }
//         del seen;
//     }

//     bool contains( e) {
//         auto [u, v] = e[:2];
//         try {
//             kdict = this->_adjdict[u][v];
//         } catch (KeyError) {
//             try {
//                 kdict = this->_adjdict[v][u];
//             } catch (KeyError) {
//                 return false;
//             }
//         }
//         if (this->keys == true) {
//             k = e[2];
//             try {
//                 dd = kdict[k];
//             } catch (KeyError) {
//                 return false;
//             }
//             return e == this->_report(u, v, k, dd);
//         }
//         for (auto k, dd : kdict.items() {
//             if (e == this->_report(u, v, k, dd) {
//                 return true;
//             }
//         }
//         return false;
//     }
// };

// class InMultiEdgeDataView(OutMultiEdgeDataView) {
//     /*! An EdgeDataView for inward edges of MultiDiGraph; See EdgeDataView */
//     static const auto __slots__ = ();

//     auto __iter__( ) {
//         return (this->_report(nbr, n, k, dd) for n, nbrs :
//         this->_nodes_nbrs();
//                 for (auto nbr, kd : nbrs.items() for k, dd : kd.items());
//     }

//     bool contains( e) {
//         auto [u, v] = e[:2];
//         try {
//             kdict = this->_adjdict[v][u];
//         } catch (KeyError) {
//             return false;
//         }
//         if (this->keys == true) {
//             k = e[2];
//             dd = kdict[k];
//             return e == this->_report(u, v, k, dd);
//         }
//         for (auto k, dd : kdict.items() {
//             if (e == this->_report(u, v, k, dd) {
//                 return true;
//             }
//         }
//         return false;
//     }
// };

// EdgeViews    have set operations and no data reported
// Interface: Set, Mapping
// template <typename graph_t>
// class OutEdgeView {
//     // static const auto __slots__ = ("_adjdict", "_graph", "_nodes_nbrs");

//     // auto __getstate__( ) {
//     //     return {"_graph": this->_graph};
//     // }

//     // auto __setstate__( state) {
//     //     this->_graph = G = state["_graph"];
//     //     this->_adjdict = G._succ if (hasattr(G, "succ") else G._adj;
//     //     this->_nodes_nbrs = this->_adjdict.items;
//     // }

//     /// @classmethod
//     // auto _from_iterable(cls, it) {
//     //     return set(it);
//     // }

//     // using dataview = OutEdgeDataView;
//     using Self = OutEdgeView<graph_t>;

//     using node_t = typename graph_t::Node;
//     using edge_t = std::pair<node_t, node_t>;
//     using adjdict_t = typename graph_t::adjlist_outer_dict_factory;

//     graph_t& _graph;
//     adjdict_t& _adjdict;
//     // T  _nodes_nbrs;

//     // using adjiterator = decltype(_graph.adj().begin());
//     // adjiterator curitem; /* ??? */

//     explicit OutEdgeView(graph_t& G) :
//         _graph{G},
//         // _adjdict = G._succ if (hasattr(G, "succ") else G._adj;
//         // _nodes_nbrs{this->_adjdict.items()},
//         _adjdict{G._adj}
//     {}

//     // Set methods
//     // auto __len__( ) {
//     //     return sum(len(nbrs) for n, nbrs : this->_nodes_nbrs());
//     // }

//     // auto __iter__( ) {
//     //     for (auto [n, nbrs] : this->_nodes_nbrs()) {
//     //         for (auto nbr : nbrs) {
//     //             yield (n, nbr);
//     //         }
//     //     }
//     // }

//     bool contains(const edge_t& e) {
//         auto [u, v] = e;
//         return this->_adjdict[u].contains(v);
//     }

//     // Mapping Methods
//     auto operator[](const edge_t& e) {
//         auto [u, v] = e;
//         return this->_adjdict[u][v];
//     }

//     // // EdgeDataView methods
//     // auto __call__( nbunch=None, data=false, default=None) {
//     //     if (nbunch.empty() and data == false) {
//     //         return (*this);
//     //     }
//     //     return this->dataview( nbunch, data, default);
//     // }

//     // auto data( data=true, default=None, nbunch=None) {
//     //     if (nbunch.empty() and data == false) {
//     //         return (*this);
//     //     }
//     //     return this->dataview( nbunch, data, default);
//     // }

//     // // String Methods
//     // auto __str__( ) {
//     //     return str(list( ));
//     // }

//     // auto __repr__( ) {
//     //     return "{0.__class__.__name__}({1!r})".format( list( ));
//     // }
// };

// class EdgeView(OutEdgeView) {
//     /*! A EdgeView class for edges of a Graph

//     This densely packed View allows iteration over edges, data lookup
//     like a dict and set operations on edges represented by node-tuples.
//     In addition, edge data can be controlled by calling this object
//     possibly creating an EdgeDataView. Typically edges are iterated over
//     and reported as `(u, v)` node tuples or `(u, v, key)` node/key tuples
//     for (auto multigraphs. Those edge representations can also be using to
//     lookup the data dict for any edge. Set operations also are available
//     where those tuples are the elements of the set.
//     Calling this object with optional arguments `data`, `default` and `keys`
//     controls the form of the tuple (see EdgeDataView). Optional argument
//     `nbunch` allows restriction to edges only involving certain nodes.

//     If `data == false` (the default) then iterate over 2-tuples `(u, v)`.
//     If `data is true` iterate over 3-tuples `(u, v, datadict)`.
//     Otherwise iterate over `(u, v, datadict.get(data, default))`.
//     For Multigraphs, if (`keys is true`, replace `u, v` with `u, v, key`
//     above.

//     Parameters
//     ==========
//     graph : XNetwork graph-like class
//     nbunch : (default= all nodes : graph) only report edges with these nodes
//     keys : (only for MultiGraph. default=false) report edge key : tuple
//     data : bool or string (default=false) see above
//     default : object (default=None);

//     Examples
//     ========
//     >>> G = xn::path_graph(4);
//     >>> EV = G.edges();
//     >>> (2, 3] : EV
//     true
//     >>> for u, v : EV: print((u, v));
//     (0, 1);
//     (1, 2);
//     (2, 3);
//     >>> assert(EV & {(1, 2), (3, 4)} == {(1, 2)});

//     >>> EVdata = G.edges(data="color", default="aqua");
//     >>> G.add_edge(2, 3, color="blue");
//     >>> assert((2, 3, "blue"] : EVdata);
//     >>> for u, v, c : EVdata: print("({}, {}) has color: {}".format(u, v,
//     c)); (0, 1) has color: aqua (1, 2) has color: aqua (2, 3) has color: blue

//     >>> EVnbunch = G.edges(nbunch=2);
//     >>> assert((2, 3] : EVnbunch);
//     >>> assert((0, 1] : EVnbunch)   //  nbunch is ignored : __contains__
//     >>> for u, v : EVnbunch: assert(u == 2 or v == 2);

//     >>> MG = xn::path_graph(4, create_using=xn::MultiGraph());
//     >>> EVmulti = MG.edges(keys=true);
//     >>> (2, 3, 0] : EVmulti
//     true
//     >>> (2, 3] : EVmulti   // 2-tuples work even when keys is true
//     true
//     >>> key = MG.add_edge(2, 3);
//     >>> for u, v, k : EVmulti: print((u, v, k));
//     (0, 1, 0);
//     (1, 2, 0);
//     (2, 3, 0);
//     (2, 3, 1);
//      */
//     static const auto __slots__ = ();

//     dataview = EdgeDataView;

//     auto __len__( ) {
//         return sum(len(nbrs) + (n : nbrs) for n, nbrs : this->_nodes_nbrs())
//         / 2;
//     }

//     auto __iter__( ) {
//         seen = {};
//         for (auto n, nbrs : this->_nodes_nbrs() {
//             for (auto nbr : nbrs) {
//                 if (nbr not : seen) {
//                     yield (n, nbr);
//                 }
//             }
//             seen[n] = 1;
//         }
//         del seen;
//     }

//     bool contains( e) {
//         try {
//             auto [u, v] = e[:2];
//             return v : this->_adjdict[u] or u : this->_adjdict[v];
//         } catch ((KeyError, ValueError) {
//             return false;
//         }
//     }
// };

// class InEdgeView(OutEdgeView) {
//     /*! A EdgeView class for inward edges of a DiGraph */
//     static const auto __slots__ = ();

//     auto __setstate__( state) {
//         this->_graph = G = state["_graph"];
//         this->_adjdict = G._pred if (hasattr(G, "pred") else G._adj;
//         this->_nodes_nbrs = this->_adjdict.items;
//     }

//     dataview = InEdgeDataView;

//     explicit _Self( G) {
//         this->_graph = G;
//         this->_adjdict = G._pred if (hasattr(G, "pred") else G._adj;
//         this->_nodes_nbrs = this->_adjdict.items;
//     }

//     auto __iter__( ) {
//         for (auto n, nbrs : this->_nodes_nbrs() {
//             for (auto nbr : nbrs) {
//                 yield (nbr, n);
//             }
//         }
//     }

//     bool contains( e) {
//         try {
//             auto [u, v] = e;
//             return u : this->_adjdict[v];
//         } catch (KeyError) {
//             return false;
//         }
//     }

//     auto operator[]( e) {
//         auto [u, v] = e;
//         return this->_adjdict[v][u];
//     }
// };

// class OutMultiEdgeView(OutEdgeView) {
//     /*! A EdgeView class for outward edges of a MultiDiGraph */
//     static const auto __slots__ = ();

//     dataview = OutMultiEdgeDataView;

//     auto __len__( ) {
//         return sum(len(kdict) for n, nbrs : this->_nodes_nbrs();
//                    for (auto nbr, kdict : nbrs.items());
//     }

//     auto __iter__( ) {
//         for (auto n, nbrs : this->_nodes_nbrs() {
//             for (auto nbr, kdict : nbrs.items() {
//                 for (auto key : kdict) {
//                     yield (n, nbr, key);
//                 }
//             }
//         }
//     }

//     bool contains( e) {
//         N = len(e);
//         if (N == 3) {
//             u, v, k = e;
//         } else if (N == 2) {
//             auto [u, v] = e;
//             k = 0.;
//         } else {
//             throw ValueError("MultiEdge must have length 2 or 3");
//         }
//         try {
//             return k : this->_adjdict[u][v];
//         } catch (KeyError) {
//             return false;
//         }
//     }

//     auto operator[]( e) {
//         u, v, k = e;
//         return this->_adjdict[u][v][k];
//     }

//     auto __call__( nbunch=None, data=false, keys=false, default=None) {
//         if (nbunch.empty() and data == false and keys == true) {
//             return (*this);
//         }
//         return this->dataview( nbunch, data, keys, default);
//     }

//     auto data( data=true, keys=false, default=None, nbunch=None) {
//         if (nbunch.empty() and data == false and keys == true) {
//             return (*this);
//         }
//         return this->dataview( nbunch, data, keys, default);
//     }
// };

// class MultiEdgeView(OutMultiEdgeView) {
//     /*! A EdgeView class for edges of a MultiGraph */
//     static const auto __slots__ = ();

//     dataview = MultiEdgeDataView;

//     auto __len__( ) {
//         return sum(1 for e : *this);
//     }

//     auto __iter__( ) {
//         seen = {};
//         for (auto n, nbrs : this->_nodes_nbrs() {
//             for (auto nbr, kd : nbrs.items() {
//                 if (nbr not : seen) {
//                     for (auto k, dd : kd.items() {
//                         yield (n, nbr, k);
//                     }
//                 }
//             }
//             seen[n] = 1;
//         }
//         del seen;
//     }
// };

// class InMultiEdgeView(OutMultiEdgeView) {
//     /*! A EdgeView class for inward edges of a MultiDiGraph */
//     static const auto __slots__ = ();

//     auto __setstate__( state) {
//         this->_graph = G = state["_graph"];
//         this->_adjdict = G._pred if (hasattr(G, "pred") else G._adj;
//         this->_nodes_nbrs = this->_adjdict.items;
//     }

//     dataview = InMultiEdgeDataView;

//     explicit _Self( G) {
//         this->_graph = G;
//         this->_adjdict = G._pred if (hasattr(G, "pred") else G._adj;
//         this->_nodes_nbrs = this->_adjdict.items;
//     }

//     auto __iter__( ) {
//         for (auto n, nbrs : this->_nodes_nbrs() {
//             for (auto nbr, kdict : nbrs.items() {
//                 for (auto key : kdict) {
//                     yield (nbr, n, key);
//                 }
//             }
//         }
//     }

//     bool contains( e) {
//         N = len(e);
//         if (N == 3) {
//             u, v, k = e;
//         } else if (N == 2) {
//             auto [u, v] = e;
//             k = 0.;
//         } else {
//             throw ValueError("MultiEdge must have length 2 or 3");
//         }
//         try {
//             return k : this->_adjdict[v][u];
//         } catch (KeyError) {
//             return false;
//         }
//     }

//     auto operator[]( e) {
//         u, v, k = e;
//         return this->_adjdict[v][u][k];
//     }
// };

} // namespace xn