public static <N, E> void replaceDirectedNetworkNodes(
      MutableNetwork<N, E> network, Function<N, N> function) {
    checkArgument(network.isDirected(), "Only directed networks are supported, given %s", network);
    checkArgument(
        !network.allowsSelfLoops(),
        "Only networks without self loops are supported, given %s",
        network);

    // A map from the existing node to the replacement node
    Map<N, N> oldNodesToNewNodes = new HashMap<>(network.nodes().size());
    for (N currentNode : network.nodes()) {
      N newNode = function.apply(currentNode);
      // Skip updating the network if the old node is equivalent to the new node
      if (!currentNode.equals(newNode)) {
        oldNodesToNewNodes.put(currentNode, newNode);
      }
    }

    // For each replacement, connect up the existing predecessors and successors to the new node
    // and then remove the old node.
    for (Map.Entry<N, N> entry : oldNodesToNewNodes.entrySet()) {
      N oldNode = entry.getKey();
      N newNode = entry.getValue();
      network.addNode(newNode);
      for (N predecessor : ImmutableSet.copyOf(network.predecessors(oldNode))) {
        for (E edge : ImmutableSet.copyOf(network.edgesConnecting(predecessor, oldNode))) {
          network.removeEdge(edge);
          network.addEdge(predecessor, newNode, edge);
        }
      }
      for (N successor : ImmutableSet.copyOf(network.successors(oldNode))) {
        for (E edge : ImmutableSet.copyOf(network.edgesConnecting(oldNode, successor))) {
          network.removeEdge(edge);
          network.addEdge(newNode, successor, edge);
        }
      }
      network.removeNode(oldNode);
    }
  }
@Test
  public void testNodeReplacement() {
    Function<String, String> function =
        new Function<String, String>() {
          @Override
          public String apply(String input) {
            return "E".equals(input) || "J".equals(input) || "M".equals(input) || "O".equals(input)
                ? input.toLowerCase()
                : input;
          }
        };

    Function<String, String> spiedFunction = spy(function);
    MutableNetwork<String, String> network = createNetwork();
    Networks.replaceDirectedNetworkNodes(network, spiedFunction);
    MutableNetwork<String, String> originalNetwork = createNetwork();
    for (String node : originalNetwork.nodes()) {
      assertEquals(
          originalNetwork.successors(node).stream()
              .map(function)
              .collect(Collectors.toCollection(HashSet::new)),
          network.successors(function.apply(node)));
      // Ensure that the transform function was only called once
      verify(spiedFunction, times(1)).apply(node);
    }
    assertEquals(
        network.nodes(),
        originalNetwork.nodes().stream()
            .map(function)
            .collect(Collectors.toCollection(HashSet::new)));
  }