'use strict';

var _get = require('babel-runtime/helpers/get')['default'];

var _inherits = require('babel-runtime/helpers/inherits')['default'];

var _createClass = require('babel-runtime/helpers/create-class')['default'];

var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];

var _defineProperty = require('babel-runtime/helpers/define-property')['default'];

var _slicedToArray = require('babel-runtime/helpers/sliced-to-array')['default'];

var _regeneratorRuntime = require('babel-runtime/regenerator')['default'];

var _Object$keys = require('babel-runtime/core-js/object/keys')['default'];

var _Object$assign = require('babel-runtime/core-js/object/assign')['default'];

var _Array$from = require('babel-runtime/core-js/array/from')['default'];

var _getIterator = require('babel-runtime/core-js/get-iterator')['default'];

var _Object$getOwnPropertyNames = require('babel-runtime/core-js/object/get-own-property-names')['default'];

var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];

Object.defineProperty(exports, '__esModule', {
  value: true
});
var marked0$0 = [yieldEdges, yieldDegree].map(_regeneratorRuntime.mark);

var _DiGraph2 = require('./DiGraph');

var _DiGraph3 = _interopRequireDefault(_DiGraph2);

var _MultiGraph = require('./MultiGraph');

var _MultiGraph2 = _interopRequireDefault(_MultiGraph);

var _exceptionsJSNetworkXError = require('../exceptions/JSNetworkXError');

var _exceptionsJSNetworkXError2 = _interopRequireDefault(_exceptionsJSNetworkXError);

var _internals = require('../_internals');

/**
 * A directed graph class that can store multiedges.
 *
 * Multiedges are multiple edges between two nodes. Each edge can hold optional
 * data or attributes.
 *
 * A MultiDiGraph holds directed edges. Self loops are allowed. Edges are
 * respresented as links between nodes with optional key/value attributes.
 *
 * ### Example
 *
 * Create an empty graph structure (a "null graph") with no nodes and no edges:
 *
 * ```
 * var G = new jsnx.MultiDiGraph();
 * ```
 *
 * G can be grown in several ways.
 *
 * #### Nodes
 *
 * Add one node at a time:
 *
 * ```
 * G.addNode(1);
 * ```
 *
 * Add the nodes from any iterable:
 *
 * ```
 * G.addNodesFrom([2,3]);
 * var H = new jsnx.Graph();
 * H.addPath([0,1,2,3,4,5]);
 * G.addNodesFrom(H);
 * ```
 *
 * In addition to strings and integers, any object that implements a custom
 * `toString` method can represent a node.
 *
 * #### Edges
 *
 * `G` can also be grown by adding edges. Add one edge,
 *
 * ```
 * G.addEdge(1, 2);
 * ```
 *
 * a list of edges,
 *
 * ```
 * G.addEdgesFrom([[1,2], [1,3]]);
 * ```
 *
 * or a collection of edges
 *
 * ```
 * G.addEdgesFrom(H.edges());
 * ```
 *
 * If some edges connect nodes not yet in the graph, the nodes are added
 * automatically. If an edge already exists, an additional edge is created and
 * stored using a key to identify the edge. By default the key is the lowest
 * unused integer.
 *
 * ```
 * G.addEdgesFrom([[4,5,{route:282}], [4,5,{route:37}]]);
 * G.get(4);
 * // Map {5: {0: {}, 1: {route: 282}, 2: {route: 37}}}
 *
 * #### Attributes
 *
 * Each graph, node and edge can hold key/value attribute pairs in an associated
 * attribute object. By default these are empty, but can be added or changed
 * using `addEdge` or `addNode`.
 *
 * ```
 * G.addNode(1, {time: '5pm'});
 * G.addNodesFrom([3], {time: '2pm'});
 * G.nodes(true);
 * // [[1, {time: '5pm'}], [3, {time: '2pm'}]]
 * ```
 *
 * Add edge attributes using `addEdge` and `addEdgesFrom`:
 *
 * ```
 * G.addEdge(1, 2, {weight: 4.7});
 * G.addEdgesFrom([[3,4], [4,5]], {color: 'red'});
 * G.addEdgesFrom([[1,2,{color: 'blue'}], [2,3,{weight: 8}]]);
 * ```
 */

var MultiDiGraph = (function (_DiGraph) {
  _inherits(MultiDiGraph, _DiGraph);

  /**
   * @param {(Object|Array|Graph)} optData Data to initialize graph.
   *   If no data is passed, an empty graph is created. The data can be an edge
   *   list, or any JSNetworkX graph object.
   * @param {Object=} opt_attr (default= no attributes)
   *       Attributes to add to graph as key=value pairs.
   */

  function MultiDiGraph(optData, optAttr) {
    _classCallCheck(this, MultiDiGraph);

    _get(Object.getPrototypeOf(MultiDiGraph.prototype), 'constructor', this).call(this, optData, optAttr);
  }

  _createClass(MultiDiGraph, [{
    key: 'addEdge',

    /**
     * Add an edge between u and v.
     *
     * The nodes u and v will be automatically added if they are not already in
     * the graph.
     *
     * Edge attributes can be specified by providing an object with key/value
     * pairs.
     *
     * ### Note
     *
     * To replace/update edge data, use the optional key argument to identify a
     * unique edge. Otherwise a new edge will be created.
     *
     * ### Example
     *
     * The following add the edge e=(1,2) to graph G:
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addEdge(1, 2);
     * G.addEdgesFrom([[1,2]]);
     * ```
     *
     * Associate data to edges using keywords:
     *
     * ```
     * G.addEdge(1, 2, {weight: 3});
     * G.addEdge(1, 2, 0, {weight: 4}); // update data for key=0
     * G.addEdge(1, 3, {weight: 7, capacity: 15, length: 342.7});
     * ```
     * @param {Node} u
     * @param {Node} v
     * @param {(string|number)} optKey (default=lowest unused integer) Used to
     *   distinguish multiedges between a pair of nodes.
     * @param {Object} opAttrDict Object of edge attributes. Key/value pairs will
     *   update existing data associated with the edge.
     */
    value: function addEdge(u, v, optKey, optAttrDict) {
      if (optKey && typeof optKey === 'object') {
        optAttrDict = optKey;
        optKey = null;
      }

      if (optAttrDict && !(0, _internals.isPlainObject)(optAttrDict)) {
        throw new _exceptionsJSNetworkXError2['default']('The optAttrDict argument must be a plain object.');
      }

      // add nodes
      var keydict;
      if (!this.succ.has(u)) {
        this.succ.set(u, new _internals.Map());
        this.pred.set(u, new _internals.Map());
        this.node.set(u, {});
      }
      if (!this.succ.has(v)) {
        this.succ.set(v, new _internals.Map());
        this.pred.set(v, new _internals.Map());
        this.node.set(v, {});
      }
      if (this.succ.get(u).has(v)) {
        keydict = this.get(u).get(v);
        if (optKey == null) {
          // find unique integer key
          optKey = _Object$keys(keydict).length;
          while (keydict[optKey]) {
            optKey += 1;
          }
        }
        keydict[optKey] = _Object$assign((0, _internals.getDefault)(keydict[optKey], {}), optAttrDict);
      } else {
        // selfloops work this way without special treatment
        if (optKey == null) {
          optKey = 0;
        }
        keydict = _defineProperty({}, optKey, _Object$assign({}, optAttrDict));
        this.succ.get(u).set(v, keydict);
        this.pred.get(v).set(u, keydict);
      }
    }
  }, {
    key: 'removeEdge',

    /**
     * Remove an edge between u and v.
     *
     * ### Example
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addPath([0,1,2,3]);
     * G.removeEdge(0, 1);
     * ```
     *
     * For multiple edges:
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addEdgesFrom([[1,2], [1,2], [1,2]]);
     * G.removeEdge(1, 2); // remove a single (arbitrary) edge
     * ```
     *
     * For edges with keys:
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addEdge(1, 2, 'first');
     * G.addEdge(1, 2, 'second');
     * G.removeEdge(1, 2, 'second');
     * ```
     * @param {Node} u
     * @param {Node} v
     * @param {(string|number)} optKey Used to distinguish multiple edges between
     *   a pair of nodes. If undefined, remove a single (arbitrary) edge between
     *   u and v.
     */
    value: function removeEdge(u, v, optKey) {
      var keydict;
      var neightborsOfU = this.adj.get(u);
      if (neightborsOfU) {
        keydict = neightborsOfU.get(v);
      }
      if (keydict == null) {
        throw new _exceptionsJSNetworkXError2['default']((0, _internals.sprintf)('The edge %j-%j is not in the graph', u, v));
      }

      // remove the edge with specified data
      if (optKey == null) {
        for (var key in keydict) {
          delete keydict[key];
          break;
        }
      } else {
        if (!keydict[optKey]) {
          throw new _exceptionsJSNetworkXError2['default']((0, _internals.sprintf)('The edge %j-%j with key %j is not in the graph', u, v, optKey));
        }
        delete keydict[optKey];
      }
      if (_Object$keys(keydict).length === 0) {
        // remove the key entries if last edge
        this.succ.get(u)['delete'](v);
        this.pred.get(v)['delete'](u);
      }
    }
  }, {
    key: 'edgesIter',

    /**
     * Return an iterator over the edges.
     *
     * Edges are returned as tuples with optional data and keys in the order
     * `(node, neighbor, key, data)`.
     *
     * ### Note
     *
     * Nodes in `optNbunch` that are not in the graph will be (quietly) ignored.
     * For directed graphs this returns the out-edges.
     *
     * ### Example
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addPath([0,1,2,3]);
     * Array.from(G.edgesIter());
     * // [[0,1], [1,2], [2,3]]
     * Array.from(G.edgesIter(true));
     * // [[0,1,{}], [1,2,{}], [2,3,{}]]
     * Array.from(G.edgesIter([0,2]));
     * // [[0,1], [2,3]]
     * ```
     *
     * @alias outEdgesIter
     *
     * @param {Iterable} optNbunch (default=all nodes) A container of nodes.
     *   The container will be iterated over only once.
     * @param {boolean} optData (default=false) If true, return edge attribute
     *   dictionaries with each edge.
     * @param {boolean} optKeys (default=flase) If true, return edge keys with
     *   each edge.
     * @return {Iterator} An iterator of `(u,v)`, `(u,v,d)` or `(u,v,key,d)` edges
     */
    value: _regeneratorRuntime.mark(function edgesIter(optNbunch) {
      var optData = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
      var optKeys = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
      var nodesNbrs;
      return _regeneratorRuntime.wrap(function edgesIter$(context$2$0) {
        // istanbul ignore next

        var _this = this;

        while (1) switch (context$2$0.prev = context$2$0.next) {
          case 0:
            if (typeof optNbunch === 'boolean') {
              optKeys = optData;
              optData = optNbunch;
              optNbunch = null;
            }

            nodesNbrs = optNbunch == null ? this.adj : (0, _internals.mapIterator)(this.nbunchIter(optNbunch), function (n) {
              return (0, _internals.tuple2)(n, _this.adj.get(n));
            });
            return context$2$0.delegateYield(yieldEdges(nodesNbrs, optData, optKeys, 'out'), 't0', 3);

          case 3:
          case 'end':
            return context$2$0.stop();
        }
      }, edgesIter, this);
    })
  }, {
    key: 'outEdgesIter',

    /**
     * @alias edgesIter
     */
    value: function outEdgesIter(optNbunch, optData, optKeys) {
      return this.edgesIter(optNbunch, optData, optKeys);
    }
  }, {
    key: 'outEdges',

    /**
     * Return a list of the outgoing edges.
     *
     * Edges are returned as tuples with optional data and keys in the order
     * `(node, neighbor, key, data)`.
     *
     * ### Note
     *
     * Nodes in `optNbunch` that are not in the graph will be (quietly) ignored.
     * For directed graphs `edges()` is the same as `outEdges()`.
     *
     * @see inEdges
     *
     * @param {Iterable} optNbunch (default=all nodes) A container of nodes.
     *   The container will be iterated over only once.
     * @param {boolean} optData (default=false) If true, return edge attribute
     *   dictionaries with each edge.
     * @param {boolean} optKeys (default=flase) If true, return edge keys with
     *   each edge.
     * @return {Array} A list of `(u,v)`, `(u,v,d)` or `(u,v,key,d)` tuples of
     *   edges
     */
    value: function outEdges(optNbunch, optData, optKeys) {
      return _Array$from(this.outEdgesIter(optNbunch, optData, optKeys));
    }
  }, {
    key: 'inEdgesIter',

    /**
     * Return an iterator over the incoming edges.
     *
     * Edges are returned as tuples with optional data and keys in the order
     * `(node, neighbor, key, data)`.
     *
     * @see edgesIter
     *
     * @param {Iterable=} optNbunch (default=all nodes) A container of nodes.
     *   The container will be iterated over only once.
     * @param {boolean=} optData (default=false) If true, return edge attribute
     *   dictionaries with each edge.
     * @param {boolean=} optKeys (default=flase) If true, return edge keys with
     *   each edge.
     * @return {Iterator} An iterator of `(u,v)`, `(u,v,d)` or `(u,v,key,d)` edges
     */
    value: _regeneratorRuntime.mark(function inEdgesIter(optNbunch) {
      var optData = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
      var optKeys = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
      var nodesNbrs;
      return _regeneratorRuntime.wrap(function inEdgesIter$(context$2$0) {
        // istanbul ignore next

        var _this2 = this;

        while (1) switch (context$2$0.prev = context$2$0.next) {
          case 0:
            if (typeof optNbunch === 'boolean') {
              optKeys = optData;
              optData = optNbunch;
              optNbunch = null;
            }

            nodesNbrs = optNbunch == null ? this.pred : (0, _internals.mapIterator)(this.nbunchIter(optNbunch), function (n) {
              return (0, _internals.tuple2)(n, _this2.pred.get(n));
            });
            return context$2$0.delegateYield(yieldEdges(nodesNbrs, optData, optKeys, 'in'), 't0', 3);

          case 3:
          case 'end':
            return context$2$0.stop();
        }
      }, inEdgesIter, this);
    })
  }, {
    key: 'inEdges',

    /**
     * Return a list of the incoming edges.
     *
     * @see outEdges
     *
     * @param {Iterable=} optNbunch (default=all nodes) A container of nodes.
     *   The container will be iterated over only once.
     * @param {boolean=} optData (default=false) If true, return edge attribute
     *   dictionaries with each edge.
     * @param {boolean=} optKeys (default=flase) If true, return edge keys with
     *   each edge.
     * @return {Array} A list of `(u,v)`, `(u,v,d)` or `(u,v,key,d)` tuples of
     *   edges
     */
    value: function inEdges(optNbunch, optData, optKeys) {
      return _Array$from(this.inEdgesIter(optNbunch, optData, optKeys));
    }
  }, {
    key: 'degreeIter',

    /**
     * Return an iterator for `(node, degree)`.
     *
     * The node degree is the number of edges adjacent to the node.
     *
     * ### Example
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addPath([0,1,2,3]);
     * Array.from(G.degreeIter([0,1]));
     * // [[0,1], [1,2]]
     * ```
     *
     * @param {Iterable=} optNbunch (default=all nodes) A container of nodes.
     *   The container will be iterated through once.
     * @param {string=} optString (default=null)
     *   The edge attribute that holds the numerical value used as a weight. If
     *   None, then each edge has weight 1.
     *   The degree is the sum of the edge weights.
     * @return {Iterator} The iterator returns two-tuples of `(node, degree)`.
     */
    value: _regeneratorRuntime.mark(function degreeIter(optNbunch, optWeight) {
      var tuple2Succ, tuple2Pred, nodesNbrs, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step$value, _step$value$0, n, succ, _step$value$1, _, pred, keydict, inDegree, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, outDegree, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, _step4$value, _step4$value$0, _step4$value$1;

      return _regeneratorRuntime.wrap(function degreeIter$(context$2$0) {
        // istanbul ignore next

        var _this3 = this;

        while (1) switch (context$2$0.prev = context$2$0.next) {
          case 0:
            tuple2Succ = (0, _internals.createTupleFactory)(2);
            tuple2Pred = (0, _internals.createTupleFactory)(2);
            nodesNbrs = optNbunch == null ? (0, _internals.zipIterator)(this.succ.entries(), this.pred.entries()) : (0, _internals.zipIterator)((0, _internals.mapIterator)(this.nbunchIter(optNbunch), function (n) {
              return tuple2Succ(n, _this3.succ.get(n));
            }), (0, _internals.mapIterator)(this.nbunchIter(optNbunch), function (n) {
              return tuple2Pred(n, _this3.pred.get(n));
            }));

            if (!(optWeight == null)) {
              context$2$0.next = 78;
              break;
            }

            _iteratorNormalCompletion = true;
            _didIteratorError = false;
            _iteratorError = undefined;
            context$2$0.prev = 7;
            _iterator = _getIterator(nodesNbrs);

          case 9:
            if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
              context$2$0.next = 62;
              break;
            }

            _step$value = _slicedToArray(_step.value, 2);
            _step$value$0 = _slicedToArray(_step$value[0], 2);
            n = _step$value$0[0];
            succ = _step$value$0[1];
            _step$value$1 = _slicedToArray(_step$value[1], 2);
            _ = _step$value$1[0];
            pred = _step$value$1[1];
            inDegree = 0;
            _iteratorNormalCompletion2 = true;
            _didIteratorError2 = false;
            _iteratorError2 = undefined;
            context$2$0.prev = 21;

            for (_iterator2 = _getIterator(pred.values()); !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
              keydict = _step2.value;

              inDegree += _Object$keys(keydict).length;
            }
            context$2$0.next = 29;
            break;

          case 25:
            context$2$0.prev = 25;
            context$2$0.t0 = context$2$0['catch'](21);
            _didIteratorError2 = true;
            _iteratorError2 = context$2$0.t0;

          case 29:
            context$2$0.prev = 29;
            context$2$0.prev = 30;

            if (!_iteratorNormalCompletion2 && _iterator2['return']) {
              _iterator2['return']();
            }

          case 32:
            context$2$0.prev = 32;

            if (!_didIteratorError2) {
              context$2$0.next = 35;
              break;
            }

            throw _iteratorError2;

          case 35:
            return context$2$0.finish(32);

          case 36:
            return context$2$0.finish(29);

          case 37:
            outDegree = 0;
            _iteratorNormalCompletion3 = true;
            _didIteratorError3 = false;
            _iteratorError3 = undefined;
            context$2$0.prev = 41;

            for (_iterator3 = _getIterator(succ.values()); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
              keydict = _step3.value;

              inDegree += _Object$keys(keydict).length;
            }
            context$2$0.next = 49;
            break;

          case 45:
            context$2$0.prev = 45;
            context$2$0.t1 = context$2$0['catch'](41);
            _didIteratorError3 = true;
            _iteratorError3 = context$2$0.t1;

          case 49:
            context$2$0.prev = 49;
            context$2$0.prev = 50;

            if (!_iteratorNormalCompletion3 && _iterator3['return']) {
              _iterator3['return']();
            }

          case 52:
            context$2$0.prev = 52;

            if (!_didIteratorError3) {
              context$2$0.next = 55;
              break;
            }

            throw _iteratorError3;

          case 55:
            return context$2$0.finish(52);

          case 56:
            return context$2$0.finish(49);

          case 57:
            context$2$0.next = 59;
            return [n, inDegree + outDegree];

          case 59:
            _iteratorNormalCompletion = true;
            context$2$0.next = 9;
            break;

          case 62:
            context$2$0.next = 68;
            break;

          case 64:
            context$2$0.prev = 64;
            context$2$0.t2 = context$2$0['catch'](7);
            _didIteratorError = true;
            _iteratorError = context$2$0.t2;

          case 68:
            context$2$0.prev = 68;
            context$2$0.prev = 69;

            if (!_iteratorNormalCompletion && _iterator['return']) {
              _iterator['return']();
            }

          case 71:
            context$2$0.prev = 71;

            if (!_didIteratorError) {
              context$2$0.next = 74;
              break;
            }

            throw _iteratorError;

          case 74:
            return context$2$0.finish(71);

          case 75:
            return context$2$0.finish(68);

          case 76:
            context$2$0.next = 110;
            break;

          case 78:
            _iteratorNormalCompletion4 = true;
            _didIteratorError4 = false;
            _iteratorError4 = undefined;
            context$2$0.prev = 81;
            _iterator4 = _getIterator(nodesNbrs);

          case 83:
            if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) {
              context$2$0.next = 96;
              break;
            }

            _step4$value = _slicedToArray(_step4.value, 2);
            _step4$value$0 = _slicedToArray(_step4$value[0], 2);
            n = _step4$value$0[0];
            succ = _step4$value$0[1];
            _step4$value$1 = _slicedToArray(_step4$value[1], 2);
            _ = _step4$value$1[0];
            pred = _step4$value$1[1];
            context$2$0.next = 93;
            return [n, sumEdgeAttribute(pred, optWeight, 1) + sumEdgeAttribute(succ, optWeight, 1)];

          case 93:
            _iteratorNormalCompletion4 = true;
            context$2$0.next = 83;
            break;

          case 96:
            context$2$0.next = 102;
            break;

          case 98:
            context$2$0.prev = 98;
            context$2$0.t3 = context$2$0['catch'](81);
            _didIteratorError4 = true;
            _iteratorError4 = context$2$0.t3;

          case 102:
            context$2$0.prev = 102;
            context$2$0.prev = 103;

            if (!_iteratorNormalCompletion4 && _iterator4['return']) {
              _iterator4['return']();
            }

          case 105:
            context$2$0.prev = 105;

            if (!_didIteratorError4) {
              context$2$0.next = 108;
              break;
            }

            throw _iteratorError4;

          case 108:
            return context$2$0.finish(105);

          case 109:
            return context$2$0.finish(102);

          case 110:
          case 'end':
            return context$2$0.stop();
        }
      }, degreeIter, this, [[7, 64, 68, 76], [21, 25, 29, 37], [30,, 32, 36], [41, 45, 49, 57], [50,, 52, 56], [69,, 71, 75], [81, 98, 102, 110], [103,, 105, 109]]);
    })
  }, {
    key: 'inDegreeIter',

    /**
     * Return an iterator for `(node, in-degree)`.
     *
     * The node in-degree is the number of edges pointing to the node.
     *
     * ### Example
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addPath([0,1,2,3]);
     * Array.from(G.degreeIter([0,1]));
     * // [[0,0], [1,1]]
     * ```
     *
     * @param {Iterable=} optNbunch (default=all nodes) A container of nodes.
     *   The container will be iterated through once.
     * @param {string=} optString (default=null)
     *   The edge attribute that holds the numerical value used as a weight. If
     *   None, then each edge has weight 1.
     *   The degree is the sum of the edge weights.
     * @return {Iterator} The iterator returns two-tuples of `(node, degree)`.
     */
    value: _regeneratorRuntime.mark(function inDegreeIter(optNbunch, optWeight) {
      return _regeneratorRuntime.wrap(function inDegreeIter$(context$2$0) {
        while (1) switch (context$2$0.prev = context$2$0.next) {
          case 0:
            return context$2$0.delegateYield(yieldDegree(this, this.pred, optNbunch, optWeight), 't0', 1);

          case 1:
          case 'end':
            return context$2$0.stop();
        }
      }, inDegreeIter, this);
    })
  }, {
    key: 'outDegreeIter',

    /**
     * Return an iterator for `(node, out-degree)`.
     *
     * The node out-degree is the number of edges pointing out of the node.
     *
     * ### Example
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addPath([0,1,2,3]);
     * Array.from(G.degreeIter([0,1]));
     * // [[0,1], [1,1]]
     * ```
     *
     * @param {Iterable=} optNbunch (default=all nodes) A container of nodes.
     *   The container will be iterated through once.
     * @param {string=} optString (default=null)
     *   The edge attribute that holds the numerical value used as a weight. If
     *   None, then each edge has weight 1.
     *   The degree is the sum of the edge weights.
     * @return {Iterator} The iterator returns two-tuples of `(node, degree)`.
     */
    value: _regeneratorRuntime.mark(function outDegreeIter(optNbunch, optWeight) {
      return _regeneratorRuntime.wrap(function outDegreeIter$(context$2$0) {
        while (1) switch (context$2$0.prev = context$2$0.next) {
          case 0:
            return context$2$0.delegateYield(yieldDegree(this, this.succ, optNbunch, optWeight), 't0', 1);

          case 1:
          case 'end':
            return context$2$0.stop();
        }
      }, outDegreeIter, this);
    })
  }, {
    key: 'isMultigraph',

    /**
     * Return True if graph is a multigraph, False otherwise.
     *
     * @return {boolean} True if graph is a multigraph, False otherwise.
     */
    value: function isMultigraph() {
      return true;
    }
  }, {
    key: 'isDirected',

    /**
     * Return True if graph is directed, False otherwise.
     *
     * @return {boolean}  True if graph is directed, False otherwise.
     */
    value: function isDirected() {
      return true;
    }
  }, {
    key: 'toDirected',

    /**
     * Return a directed copy of the graph.
     *
     * ### Notes
     *
     * This returns a deep copy of the edge, node, and
     * graph attributes which attempts to completely copy
     * all of the data and references.
     *
     * This is in contrast to the similar `var G = new MultiDiGraph(D);`, which
     * returns a shallow copy of the data.
     *
     * @return {MultiDiGraph} A deep copy of the graph.
     */
    value: function toDirected() {
      return (0, _internals.deepcopy)(this);
    }
  }, {
    key: 'toUndirected',

    /**
     * Return an undirected representation of the digraph.
     *
     * ### Notes
     *
     * The result is an undirected graph with the same name, nodes and
     * with edge `(u,v,data)` if either `(u,v,data)` or `(v,u,data)`
     * is in the digraph.  If both edges exist in digraph and
     * their edge data is different, only one edge is created
     * with an arbitrary choice of which edge data to use.
     * You must check and correct for this manually if desired.
     *
     * This returns a deep copy of the edge, node, and
     * graph attributes which attempts to completely copy
     * all of the data and references.
     *
     * This is in contrast to the similar `var G = new MultiGraph(D);`, which
     * returns a shallow copy of the data.
     *
     * @param {boolean=} optReciprocal If true, only keep edges that appear in
     *   both directions in the original digraph.
     * @return {MultiGraph}
     */
    value: function toUndirected(optReciprocal) {
      var H = new _MultiGraph2['default']();
      H.name = this.name;
      H.addNodesFrom(this);
      var _iteratorNormalCompletion5 = true;
      var _didIteratorError5 = false;
      var _iteratorError5 = undefined;

      try {
        for (var _iterator5 = _getIterator(this.adjacencyIter()), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
          var _step5$value = _slicedToArray(_step5.value, 2);

          var u = _step5$value[0];
          var nbrs = _step5$value[1];
          var _iteratorNormalCompletion6 = true;
          var _didIteratorError6 = false;
          var _iteratorError6 = undefined;

          try {
            for (var _iterator6 = _getIterator(nbrs), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
              var _step6$value = _slicedToArray(_step6.value, 2);

              var v = _step6$value[0];
              var keydict = _step6$value[1];

              for (var key in keydict) {
                if (!optReciprocal || this.hasEdge(v, u, key)) {
                  H.addEdge(u, v, key, (0, _internals.deepcopy)(keydict[key]));
                }
              }
            }
          } catch (err) {
            _didIteratorError6 = true;
            _iteratorError6 = err;
          } finally {
            try {
              if (!_iteratorNormalCompletion6 && _iterator6['return']) {
                _iterator6['return']();
              }
            } finally {
              if (_didIteratorError6) {
                throw _iteratorError6;
              }
            }
          }
        }
      } catch (err) {
        _didIteratorError5 = true;
        _iteratorError5 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion5 && _iterator5['return']) {
            _iterator5['return']();
          }
        } finally {
          if (_didIteratorError5) {
            throw _iteratorError5;
          }
        }
      }

      H.graph = (0, _internals.deepcopy)(this.graph);
      H.node = (0, _internals.deepcopy)(this.node);
      return H;
    }
  }, {
    key: 'subgraph',

    /**
     * Return the subgraph induced on nodes in `nbunch`.
     *
     * The induced subgraph of the graph contains the nodes in `optNbunch` and the
     * edges between those nodes.
     *
     * ### Notes
     *
     * The graph, edge or node attributes just point to the original graph.
     * So changes to the node or edge structure will not be reflected in
     * the original graph while changes to the attributes will.
     *
     * To create a subgraph with its own copy of the edge/node attributes use:
     * `jsnx.MultiDiGraph(G.subgraph(nbunch))`.
     *
     * ### Example
     *
     * ```
     * var G = new jsnx.MultiDiGraph();
     * G.addPath([0,1,2,3]);
     * var H = G.subgraph([0,1,2]);
     * H.edges();
     * // [[0,1], [1,2]]
     * ```
     *
     * @param {Iterable} nBunch A container of nodes which will be iterated
     *   through once.
     * @return {MultiDiGraph}
     */
    value: function subgraph(nBunch) {
      var bunch = this.nbunchIter(nBunch);
      // create new graph and copy subgraph into it
      var H = new this.constructor();
      // copy node and attribute dictionaries
      var _iteratorNormalCompletion7 = true;
      var _didIteratorError7 = false;
      var _iteratorError7 = undefined;

      try {
        for (var _iterator7 = _getIterator(bunch), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
          var n = _step7.value;

          H.node.set(n, this.node.get(n));
        }
      } catch (err) {
        _didIteratorError7 = true;
        _iteratorError7 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion7 && _iterator7['return']) {
            _iterator7['return']();
          }
        } finally {
          if (_didIteratorError7) {
            throw _iteratorError7;
          }
        }
      }

      var HSucc = H.succ;
      var HPred = H.pred;
      var thisSucc = this.succ;

      // add nodes
      var _iteratorNormalCompletion8 = true;
      var _didIteratorError8 = false;
      var _iteratorError8 = undefined;

      try {
        for (var _iterator8 = _getIterator(H), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
          var n = _step8.value;

          HSucc.set(n, new _internals.Map());
          HPred.set(n, new _internals.Map());
        }
      } catch (err) {
        _didIteratorError8 = true;
        _iteratorError8 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion8 && _iterator8['return']) {
            _iterator8['return']();
          }
        } finally {
          if (_didIteratorError8) {
            throw _iteratorError8;
          }
        }
      }

      // add edges
      var _iteratorNormalCompletion9 = true;
      var _didIteratorError9 = false;
      var _iteratorError9 = undefined;

      try {
        for (var _iterator9 = _getIterator(HSucc), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
          var _step9$value = _slicedToArray(_step9.value, 2);

          var u = _step9$value[0];
          var HNbrs = _step9$value[1];
          var _iteratorNormalCompletion10 = true;
          var _didIteratorError10 = false;
          var _iteratorError10 = undefined;

          try {
            for (var _iterator10 = _getIterator(thisSucc.get(u)), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
              var _step10$value = _slicedToArray(_step10.value, 2);

              var v = _step10$value[0];
              var keydict = _step10$value[1];

              if (HSucc.has(v)) {
                // add both representations of edge: u-v and v-u
                // they share the same keydict
                var keydictCopy = (0, _internals.clone)(keydict);
                HNbrs.set(v, keydictCopy);
                HPred.get(v).set(u, keydictCopy);
              }
            }
          } catch (err) {
            _didIteratorError10 = true;
            _iteratorError10 = err;
          } finally {
            try {
              if (!_iteratorNormalCompletion10 && _iterator10['return']) {
                _iterator10['return']();
              }
            } finally {
              if (_didIteratorError10) {
                throw _iteratorError10;
              }
            }
          }
        }
      } catch (err) {
        _didIteratorError9 = true;
        _iteratorError9 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion9 && _iterator9['return']) {
            _iterator9['return']();
          }
        } finally {
          if (_didIteratorError9) {
            throw _iteratorError9;
          }
        }
      }

      H.graph = this.graph;
      return H;
    }
  }, {
    key: 'reverse',

    /**
     * Return the reverse of the graph.
     *
     * The reverse is a graph with the same nodes and edges but with the
     * directions of the edges reversed.
     *
     * @param {boolean=} optCopy If true, return a new MultiDiGraph holding the
     *   reversed edges. If false, the reverse graph is created using the original
     *   graph (this changes the original graph).
     * @return {?MultiDiGraph}
     */
    value: function reverse() {
      var optCopy = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];

      var H;
      if (optCopy) {
        H = new this.constructor(null, { name: (0, _internals.sprintf)('Reverse of (%s)', this.name) });

        H.addNodesFrom(this);
        H.addEdgesFrom((0, _internals.mapIterator)(this.edges(true, true), function (_ref) {
          var _ref2 = _slicedToArray(_ref, 4);

          var u = _ref2[0];
          var v = _ref2[1];
          var key = _ref2[2];
          var data = _ref2[3];
          return (0, _internals.tuple4)(v, u, key, (0, _internals.deepcopy)(data));
        }));
        H.graph = (0, _internals.deepcopy)(this.graph);
        H.node = (0, _internals.deepcopy)(this.node);
      } else {
        var _ref3 = [this.succ, this.pred];
        this.pred = _ref3[0];
        this.succ = _ref3[1];

        this.adj = this.succ;
        H = this;
      }
      return H;
    }
  }], [{
    key: '__name__',

    /**
     * Holds the graph type (class) name for information.
     *
     * @type {string}
     */
    get: function get() {
      return 'MultiDiGraph';
    }
  }]);

  return MultiDiGraph;
})(_DiGraph3['default']);

exports['default'] = MultiDiGraph;

// Simulate multiple inheritance by merging prototypes
_Object$getOwnPropertyNames(_MultiGraph2['default'].prototype).forEach(function (prop) {
  if (!MultiDiGraph.prototype.hasOwnProperty(prop)) {
    MultiDiGraph.prototype[prop] = _MultiGraph2['default'].prototype[prop];
  }
});

function yieldEdges(nodesNbrs, data, keys, type) {
  var _iteratorNormalCompletion11, _didIteratorError11, _iteratorError11, _iterator11, _step11, _step11$value, n, nbrs, _iteratorNormalCompletion12, _didIteratorError12, _iteratorError12, _iterator12, _step12, _step12$value, nbr, keydict, key, result;

  return _regeneratorRuntime.wrap(function yieldEdges$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        _iteratorNormalCompletion11 = true;
        _didIteratorError11 = false;
        _iteratorError11 = undefined;
        context$1$0.prev = 3;
        _iterator11 = _getIterator(nodesNbrs);

      case 5:
        if (_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done) {
          context$1$0.next = 48;
          break;
        }

        _step11$value = _slicedToArray(_step11.value, 2);
        n = _step11$value[0];
        nbrs = _step11$value[1];
        _iteratorNormalCompletion12 = true;
        _didIteratorError12 = false;
        _iteratorError12 = undefined;
        context$1$0.prev = 12;
        _iterator12 = _getIterator(nbrs);

      case 14:
        if (_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done) {
          context$1$0.next = 31;
          break;
        }

        _step12$value = _slicedToArray(_step12.value, 2);
        nbr = _step12$value[0];
        keydict = _step12$value[1];
        context$1$0.t0 = _regeneratorRuntime.keys(keydict);

      case 19:
        if ((context$1$0.t1 = context$1$0.t0()).done) {
          context$1$0.next = 28;
          break;
        }

        key = context$1$0.t1.value;
        result = type === 'out' ? [n, nbr] : [nbr, n];

        if (keys) {
          result[2] = isNaN(key) ? key : +key;
        }
        if (data) {
          result.push(keydict[key]);
        }
        context$1$0.next = 26;
        return result;

      case 26:
        context$1$0.next = 19;
        break;

      case 28:
        _iteratorNormalCompletion12 = true;
        context$1$0.next = 14;
        break;

      case 31:
        context$1$0.next = 37;
        break;

      case 33:
        context$1$0.prev = 33;
        context$1$0.t2 = context$1$0['catch'](12);
        _didIteratorError12 = true;
        _iteratorError12 = context$1$0.t2;

      case 37:
        context$1$0.prev = 37;
        context$1$0.prev = 38;

        if (!_iteratorNormalCompletion12 && _iterator12['return']) {
          _iterator12['return']();
        }

      case 40:
        context$1$0.prev = 40;

        if (!_didIteratorError12) {
          context$1$0.next = 43;
          break;
        }

        throw _iteratorError12;

      case 43:
        return context$1$0.finish(40);

      case 44:
        return context$1$0.finish(37);

      case 45:
        _iteratorNormalCompletion11 = true;
        context$1$0.next = 5;
        break;

      case 48:
        context$1$0.next = 54;
        break;

      case 50:
        context$1$0.prev = 50;
        context$1$0.t3 = context$1$0['catch'](3);
        _didIteratorError11 = true;
        _iteratorError11 = context$1$0.t3;

      case 54:
        context$1$0.prev = 54;
        context$1$0.prev = 55;

        if (!_iteratorNormalCompletion11 && _iterator11['return']) {
          _iterator11['return']();
        }

      case 57:
        context$1$0.prev = 57;

        if (!_didIteratorError11) {
          context$1$0.next = 60;
          break;
        }

        throw _iteratorError11;

      case 60:
        return context$1$0.finish(57);

      case 61:
        return context$1$0.finish(54);

      case 62:
      case 'end':
        return context$1$0.stop();
    }
  }, marked0$0[0], this, [[3, 50, 54, 62], [12, 33, 37, 45], [38,, 40, 44], [55,, 57, 61]]);
}

function sumEdgeAttribute(nbrs, attribute, def) {
  var sum = 0;
  var _iteratorNormalCompletion13 = true;
  var _didIteratorError13 = false;
  var _iteratorError13 = undefined;

  try {
    for (var _iterator13 = _getIterator(nbrs.values()), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) {
      var keydict = _step13.value;

      for (var key in keydict) {
        sum += (0, _internals.getDefault)(keydict[key][attribute], def);
      }
    }
  } catch (err) {
    _didIteratorError13 = true;
    _iteratorError13 = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion13 && _iterator13['return']) {
        _iterator13['return']();
      }
    } finally {
      if (_didIteratorError13) {
        throw _iteratorError13;
      }
    }
  }

  return sum;
}

function yieldDegree(graph, edges, nBunch, weight) {
  var nodesNbrs, _iteratorNormalCompletion14, _didIteratorError14, _iteratorError14, _iterator14, _step14, _step14$value, n, nbrs, sum, _iteratorNormalCompletion15, _didIteratorError15, _iteratorError15, _iterator15, _step15, keydict, _iteratorNormalCompletion16, _didIteratorError16, _iteratorError16, _iterator16, _step16, _step16$value;

  return _regeneratorRuntime.wrap(function yieldDegree$(context$1$0) {
    while (1) switch (context$1$0.prev = context$1$0.next) {
      case 0:
        nodesNbrs = nBunch == null ? edges : (0, _internals.mapIterator)(graph.nbunchIter(nBunch), function (n) {
          return (0, _internals.tuple2)(n, edges.get(n));
        });

        if (!(weight == null)) {
          context$1$0.next = 52;
          break;
        }

        _iteratorNormalCompletion14 = true;
        _didIteratorError14 = false;
        _iteratorError14 = undefined;
        context$1$0.prev = 5;
        _iterator14 = _getIterator(nodesNbrs);

      case 7:
        if (_iteratorNormalCompletion14 = (_step14 = _iterator14.next()).done) {
          context$1$0.next = 36;
          break;
        }

        _step14$value = _slicedToArray(_step14.value, 2);
        n = _step14$value[0];
        nbrs = _step14$value[1];
        sum = 0;
        _iteratorNormalCompletion15 = true;
        _didIteratorError15 = false;
        _iteratorError15 = undefined;
        context$1$0.prev = 15;

        for (_iterator15 = _getIterator(nbrs.values()); !(_iteratorNormalCompletion15 = (_step15 = _iterator15.next()).done); _iteratorNormalCompletion15 = true) {
          keydict = _step15.value;

          sum += _Object$keys(keydict).length;
        }
        context$1$0.next = 23;
        break;

      case 19:
        context$1$0.prev = 19;
        context$1$0.t0 = context$1$0['catch'](15);
        _didIteratorError15 = true;
        _iteratorError15 = context$1$0.t0;

      case 23:
        context$1$0.prev = 23;
        context$1$0.prev = 24;

        if (!_iteratorNormalCompletion15 && _iterator15['return']) {
          _iterator15['return']();
        }

      case 26:
        context$1$0.prev = 26;

        if (!_didIteratorError15) {
          context$1$0.next = 29;
          break;
        }

        throw _iteratorError15;

      case 29:
        return context$1$0.finish(26);

      case 30:
        return context$1$0.finish(23);

      case 31:
        context$1$0.next = 33;
        return [n, sum];

      case 33:
        _iteratorNormalCompletion14 = true;
        context$1$0.next = 7;
        break;

      case 36:
        context$1$0.next = 42;
        break;

      case 38:
        context$1$0.prev = 38;
        context$1$0.t1 = context$1$0['catch'](5);
        _didIteratorError14 = true;
        _iteratorError14 = context$1$0.t1;

      case 42:
        context$1$0.prev = 42;
        context$1$0.prev = 43;

        if (!_iteratorNormalCompletion14 && _iterator14['return']) {
          _iterator14['return']();
        }

      case 45:
        context$1$0.prev = 45;

        if (!_didIteratorError14) {
          context$1$0.next = 48;
          break;
        }

        throw _iteratorError14;

      case 48:
        return context$1$0.finish(45);

      case 49:
        return context$1$0.finish(42);

      case 50:
        context$1$0.next = 80;
        break;

      case 52:
        _iteratorNormalCompletion16 = true;
        _didIteratorError16 = false;
        _iteratorError16 = undefined;
        context$1$0.prev = 55;
        _iterator16 = _getIterator(nodesNbrs);

      case 57:
        if (_iteratorNormalCompletion16 = (_step16 = _iterator16.next()).done) {
          context$1$0.next = 66;
          break;
        }

        _step16$value = _slicedToArray(_step16.value, 2);
        n = _step16$value[0];
        nbrs = _step16$value[1];
        context$1$0.next = 63;
        return [n, sumEdgeAttribute(nbrs, weight, 1)];

      case 63:
        _iteratorNormalCompletion16 = true;
        context$1$0.next = 57;
        break;

      case 66:
        context$1$0.next = 72;
        break;

      case 68:
        context$1$0.prev = 68;
        context$1$0.t2 = context$1$0['catch'](55);
        _didIteratorError16 = true;
        _iteratorError16 = context$1$0.t2;

      case 72:
        context$1$0.prev = 72;
        context$1$0.prev = 73;

        if (!_iteratorNormalCompletion16 && _iterator16['return']) {
          _iterator16['return']();
        }

      case 75:
        context$1$0.prev = 75;

        if (!_didIteratorError16) {
          context$1$0.next = 78;
          break;
        }

        throw _iteratorError16;

      case 78:
        return context$1$0.finish(75);

      case 79:
        return context$1$0.finish(72);

      case 80:
      case 'end':
        return context$1$0.stop();
    }
  }, marked0$0[1], this, [[5, 38, 42, 50], [15, 19, 23, 31], [24,, 26, 30], [43,, 45, 49], [55, 68, 72, 80], [73,, 75, 79]]);
}
module.exports = exports['default'];

/* eslint-disable no-unused-vars */

/* eslint-enable no-unused-vars */

/* eslint-disable no-unused-vars */

/* eslint-enable no-unused-vars */