/**
   * Performs step C of the algorithm, as indicated on page xxx of CPS, with the modification that
   * X--W--Y is oriented as X-->W<--Y if W is *determined by* the sepset of (X, Y), rather than W
   * just being *in* the sepset of (X, Y).
   */
  public static void pcdOrientC(
      SepsetMap set, IndependenceTest test, Knowledge knowledge, Graph graph) {
    TetradLogger.getInstance().log("info", "Staring Collider Orientation:");

    List<Node> nodes = graph.getNodes();

    for (Node y : nodes) {
      List<Node> adjacentNodes = graph.getAdjacentNodes(y);

      if (adjacentNodes.size() < 2) {
        continue;
      }

      ChoiceGenerator cg = new ChoiceGenerator(adjacentNodes.size(), 2);
      int[] combination;

      while ((combination = cg.next()) != null) {
        Node x = adjacentNodes.get(combination[0]);
        Node z = adjacentNodes.get(combination[1]);

        // Skip triples that are shielded.
        if (graph.isAdjacentTo(x, z)) {
          continue;
        }

        List<Node> sepset = set.get(x, z);

        if (sepset == null) {
          continue;
        }

        List<Node> augmentedSet = new LinkedList<Node>(sepset);
        augmentedSet.add(y);

        if (test.determines(sepset, y)) {
          continue;
        }
        //
        if (!test.splitDetermines(sepset, x, z) && test.splitDetermines(augmentedSet, x, z)) {
          continue;
        }

        if (!isArrowpointAllowed(x, y, knowledge) || !isArrowpointAllowed(z, y, knowledge)) {
          continue;
        }

        graph.setEndpoint(x, y, Endpoint.ARROW);
        graph.setEndpoint(z, y, Endpoint.ARROW);

        TetradLogger.getInstance()
            .log("colliderOriented", SearchLogUtils.colliderOrientedMsg(x, y, z));
      }
    }

    TetradLogger.getInstance().log("info", "Finishing Collider Orientation.");
  }
  public static Graph bestGuessCycleOrientation(Graph graph, IndependenceTest test) {
    while (true) {
      List<Node> cycle = GraphUtils.directedCycle(graph);

      if (cycle == null) {
        break;
      }

      LinkedList<Node> _cycle = new LinkedList<Node>(cycle);

      Node first = _cycle.getFirst();
      Node last = _cycle.getLast();

      _cycle.addFirst(last);
      _cycle.addLast(first);

      int _j = -1;
      double minP = Double.POSITIVE_INFINITY;

      for (int j = 1; j < _cycle.size() - 1; j++) {
        int i = j - 1;
        int k = j + 1;

        Node x = test.getVariable(_cycle.get(i).getName());
        Node y = test.getVariable(_cycle.get(j).getName());
        Node z = test.getVariable(_cycle.get(k).getName());

        test.isIndependent(x, z, Collections.singletonList(y));

        System.out.println("Testing " + x + " _||_ " + z + " | " + y);

        double p = test.getPValue();

        System.out.println("p = " + p);

        if (p < minP) {
          _j = j;
          minP = p;
        }
      }

      Node x = _cycle.get(_j - 1);
      Node y = _cycle.get(_j);
      Node z = _cycle.get(_j + 1);

      graph.removeEdge(x, y);
      graph.removeEdge(z, y);
      graph.addDirectedEdge(x, y);
      graph.addDirectedEdge(z, y);
    }

    return graph;
  }
  public static boolean existsLocalSepsetWithoutDet(
      Node x, Node y, Node z, IndependenceTest test, Graph graph, int depth) {
    Set<Node> __nodes = new HashSet<Node>(graph.getAdjacentNodes(x));
    __nodes.addAll(graph.getAdjacentNodes(z));
    __nodes.remove(x);
    __nodes.remove(z);
    List<Node> _nodes = new LinkedList<Node>(__nodes);
    TetradLogger.getInstance()
        .log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

    int _depth = depth;
    if (_depth == -1) {
      _depth = 1000;
    }
    _depth = Math.min(_depth, _nodes.size());

    for (int d = 0; d <= _depth; d++) {
      if (_nodes.size() >= d) {
        ChoiceGenerator cg2 = new ChoiceGenerator(_nodes.size(), d);
        int[] choice;

        while ((choice = cg2.next()) != null) {
          List<Node> condSet = asList(choice, _nodes);

          if (condSet.contains(y)) {
            continue;
          }

          if (test.determines(condSet, y)) {
            continue;
          }

          //        LogUtils.getInstance().finest("Trying " + condSet);

          if (test.isIndependent(x, z, condSet)) {
            return true;
          }
        }
      }
    }

    return false;
  }
  private Node getVariableForName(String targetVariableName) {
    Node target = null;

    for (Node V : test.getVariables()) {
      if (V.getName().equals(targetVariableName)) {
        target = V;
        break;
      }
    }

    if (target == null) {
      throw new IllegalArgumentException("Target variable not in dataset: " + targetVariableName);
    }

    return target;
  }
  public static CpcTripleType getCpcTripleType(
      Node x, Node y, Node z, IndependenceTest test, int depth, Graph graph) {
    //    	System.out.println("getCpcTripleType 1");

    boolean existsSepsetContainingY = false;
    boolean existsSepsetNotContainingY = false;

    Set<Node> __nodes = new HashSet<Node>(graph.getAdjacentNodes(x));
    __nodes.remove(z);

    //    	System.out.println("getCpcTripleType 2");

    List<Node> _nodes = new LinkedList<Node>(__nodes);
    TetradLogger.getInstance()
        .log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

    //        System.out.println("getCpcTripleType 3");

    int _depth = depth;
    if (_depth == -1) {
      _depth = 1000;
    }
    _depth = Math.min(_depth, _nodes.size());

    //    	System.out.println("getCpcTripleType 4");

    for (int d = 0; d <= _depth; d++) {
      //        	System.out.println("getCpcTripleType 5");

      ChoiceGenerator cg = new ChoiceGenerator(_nodes.size(), d);
      int[] choice;

      while ((choice = cg.next()) != null) {
        //            	System.out.println("getCpcTripleType 6");

        List<Node> condSet = GraphUtils.asList(choice, _nodes);

        //            	System.out.println("getCpcTripleType 7");

        if (test.isIndependent(x, z, condSet)) {
          if (condSet.contains(y)) {
            existsSepsetContainingY = true;
          } else {
            existsSepsetNotContainingY = true;
          }
        }
      }
    }

    //    	System.out.println("getCpcTripleType 8");

    __nodes = new HashSet<Node>(graph.getAdjacentNodes(z));
    __nodes.remove(x);

    _nodes = new LinkedList<Node>(__nodes);
    TetradLogger.getInstance()
        .log("adjacencies", "Adjacents for " + x + "--" + y + "--" + z + " = " + _nodes);

    //    	System.out.println("getCpcTripleType 9");

    _depth = depth;
    if (_depth == -1) {
      _depth = 1000;
    }
    _depth = Math.min(_depth, _nodes.size());

    //    	System.out.println("getCpcTripleType 10");

    for (int d = 0; d <= _depth; d++) {
      //        	System.out.println("getCpcTripleType 11");

      ChoiceGenerator cg = new ChoiceGenerator(_nodes.size(), d);
      int[] choice;

      while ((choice = cg.next()) != null) {
        List<Node> condSet = GraphUtils.asList(choice, _nodes);

        if (test.isIndependent(x, z, condSet)) {
          if (condSet.contains(y)) {
            existsSepsetContainingY = true;
          } else {
            existsSepsetNotContainingY = true;
          }
        }
      }
    }

    //    	System.out.println("getCpcTripleType 12");

    if (existsSepsetContainingY == existsSepsetNotContainingY) {
      return CpcTripleType.AMBIGUOUS;
    } else if (!existsSepsetNotContainingY) {
      return CpcTripleType.NONCOLLIDER;
    } else {
      return CpcTripleType.COLLIDER;
    }
  }