<commit_before>dagre.layout = (function() {
  function acyclic(g) {
    var onStack = {};
    var visited = {};

    function dfs(u) {
      if (u in visited)
        return;

      visited[u.id()] = true;
      onStack[u.id()] = true;
      u.outEdges().forEach(function(e) {
        var v = e.head();
        if (v.id() in onStack) {
          g.removeEdge(e);
          e.attrs.reverse = true;

          // If this is not a self-loop add the reverse edge to the graph
          if (u.id() !== v.id()) {
            u.addPredecessor(v, e.attrs);
          }
        } else {
          dfs(v);
        }
      });

      delete onStack[u.id()];
    }

    g.nodes().forEach(function(u) {
      dfs(u);
    });
  }

  function reverseAcyclic(g) {
    g.edges().forEach(function(e) {
      if (e.attrs.reverse) {
        g.removeEdge(e);
        g.addEdge(e.head(), e.tail(), e.attrs);
        delete e.attrs.reverse;
      }
    });
  }

  function removeSelfLoops(g) {
    var selfLoops = [];
    g.nodes().forEach(function(u) {
      var es = u.outEdges(u);
      es.forEach(function(e) {
        selfLoops.push(e);
        g.removeEdge(e);
      });
    });
    return selfLoops;
  }

  function addSelfLoops(g, selfLoops) {
    selfLoops.forEach(function(e) {
      g.addEdge(e.head(), e.tail(), e.attrs);
    });
  }

  // Assumes input graph has no self-loops and is otherwise acyclic.
  function addDummyNodes(g) {
    g.edges().forEach(function(e) {
      var prefix = "_dummy-" + e.id() + "-";
      var u = e.tail();
      var sinkRank = e.head().attrs.rank;
      if (u.attrs.rank + 1 < sinkRank) {
        g.removeEdge(e);
        for (var rank = u.attrs.rank + 1; rank < sinkRank; ++rank) {
          var vId = prefix + rank;
          var v = g.addNode(vId, { rank: rank,
                                   dummy: true,
                                   height: 0,
                                   width: 0,
                                   strokeWidth: e.attrs.strokeWidth,
                                   marginX: 0,
                                   marginY: 0 });
          g.addEdge(u, v, e.attrs);
          u = v;
        }
        g.addEdge(u, e.head(), e.attrs);
      }
    });
  }

  function rankToLayering(g) {
    var layering = [];
    g.nodes().forEach(function(u) {
      var rank = u.attrs.rank;
      layering[rank] = layering[rank] || [];
      layering[rank].push(u);
      delete u.attrs.rank;
    });
    return layering;
  }

  return function(g) {
    var selfLoops = removeSelfLoops(g);
    acyclic(g);

    dagre.layout.rank(g);

    addDummyNodes(g);
    var layering = rankToLayering(g);

    dagre.layout.position(g, layering);
<commit_msg>Support layout for an empty graph<commit_after>dagre.layout = (function() {
  function acyclic(g) {
    var onStack = {};
    var visited = {};

    function dfs(u) {
      if (u in visited)
        return;

      visited[u.id()] = true;
      onStack[u.id()] = true;
      u.outEdges().forEach(function(e) {
        var v = e.head();
        if (v.id() in onStack) {
          g.removeEdge(e);
          e.attrs.reverse = true;

          // If this is not a self-loop add the reverse edge to the graph
          if (u.id() !== v.id()) {
            u.addPredecessor(v, e.attrs);
          }
        } else {
          dfs(v);
        }
      });

      delete onStack[u.id()];
    }

    g.nodes().forEach(function(u) {
      dfs(u);
    });
  }

  function reverseAcyclic(g) {
    g.edges().forEach(function(e) {
      if (e.attrs.reverse) {
        g.removeEdge(e);
        g.addEdge(e.head(), e.tail(), e.attrs);
        delete e.attrs.reverse;
      }
    });
  }

  function removeSelfLoops(g) {
    var selfLoops = [];
    g.nodes().forEach(function(u) {
      var es = u.outEdges(u);
      es.forEach(function(e) {
        selfLoops.push(e);
        g.removeEdge(e);
      });
    });
    return selfLoops;
  }

  function addSelfLoops(g, selfLoops) {
    selfLoops.forEach(function(e) {
      g.addEdge(e.head(), e.tail(), e.attrs);
    });
  }

  // Assumes input graph has no self-loops and is otherwise acyclic.
  function addDummyNodes(g) {
    g.edges().forEach(function(e) {
      var prefix = "_dummy-" + e.id() + "-";
      var u = e.tail();
      var sinkRank = e.head().attrs.rank;
      if (u.attrs.rank + 1 < sinkRank) {
        g.removeEdge(e);
        for (var rank = u.attrs.rank + 1; rank < sinkRank; ++rank) {
          var vId = prefix + rank;
          var v = g.addNode(vId, { rank: rank,
                                   dummy: true,
                                   height: 0,
                                   width: 0,
                                   strokeWidth: e.attrs.strokeWidth,
                                   marginX: 0,
                                   marginY: 0 });
          g.addEdge(u, v, e.attrs);
          u = v;
        }
        g.addEdge(u, e.head(), e.attrs);
      }
    });
  }

  function rankToLayering(g) {
    var layering = [];
    g.nodes().forEach(function(u) {
      var rank = u.attrs.rank;
      layering[rank] = layering[rank] || [];
      layering[rank].push(u);
      delete u.attrs.rank;
    });
    return layering;
  }

  return function(g) {
    if (g.nodes().length === 0) {
      // Nothing to do!
      return;
    }

    var selfLoops = removeSelfLoops(g);
    acyclic(g);

    dagre.layout.rank(g);

    addDummyNodes(g);
    var layering = rankToLayering(g);

    dagre.layout.position(g, layering);

{'fraction_non_alphanumeric': 0.15327136868963004, 'fraction_numerical': 0.002369236376890833, 'mean_word_length': 1.5549348230912476, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6221926', 'n_tokens_mistral': 1893, 'n_tokens_neox': 1838, 'n_words': 368}