public boolean equals(Object obj) {
      ReachabilityEdge edge = (ReachabilityEdge) obj;

      if (!(edge.getFrom().equals(this.getFrom()))) {
        return false;
      }

      return edge.getTo().equals(this.getTo());
    }
  /**
   * Returns the set of nodes reachable from the given set of initial nodes in the given graph
   * according to the criteria in the given legal pairs object.
   *
   * <p>A variable V is reachable from initialNodes iff for some variable X in initialNodes thers is
   * a path U [X, Y1, ..., V] such that legalPairs.isLegalFirstNode(X, Y1) and for each [H1, H2, H3]
   * as subpaths of U, legalPairs.isLegalPairs(H1, H2, H3).
   *
   * <p>The algorithm used is a variant of Algorithm 1 from Geiger, Verma, & Pearl (1990).
   *
   * @param initialNodes The nodes that reachability paths start from.
   * @param legalPairs Specifies initial edges (given initial nodes) and legal edge pairs.
   * @param c a set of vertices (intuitively, the set of variables to be conditioned on.
   * @param d a set of vertices (intuitively to be used in tests of legality, for example, the set
   *     of ancestors of c).
   * @param graph the graph with respect to which reachability is determined.
   */
  public static Set<Node> getReachableNodes(
      List<Node> initialNodes, LegalPairs legalPairs, List<Node> c, List<Node> d, Graph graph) {
    HashSet<Node> reachable = new HashSet<Node>();
    MultiKeyMap visited = new MultiKeyMap();
    List<ReachabilityEdge> nextEdges = new LinkedList<ReachabilityEdge>();

    for (Node x : initialNodes) {
      List<Node> adjX = graph.getAdjacentNodes(x);

      for (Node y : adjX) {
        if (legalPairs.isLegalFirstEdge(x, y)) {
          reachable.add(y);
          nextEdges.add(new ReachabilityEdge(x, y));
          visited.put(x, y, Boolean.TRUE);
        }
      }
    }

    while (nextEdges.size() > 0) {
      List<ReachabilityEdge> currEdges = nextEdges;
      nextEdges = new LinkedList<ReachabilityEdge>();

      for (ReachabilityEdge edge : currEdges) {
        Node x = edge.getFrom();
        Node y = edge.getTo();
        List<Node> adjY = graph.getAdjacentNodes(y);

        for (Node z : adjY) {
          if ((visited.get(y, z)) == Boolean.TRUE) {
            continue;
          }

          if (legalPairs.isLegalPair(x, y, z, c, d)) {
            reachable.add(z);
            nextEdges.add(new ReachabilityEdge(y, z));
            visited.put(y, z, Boolean.TRUE);
          }
        }
      }
    }

    return reachable;
  }