code: ///////////////////////////////////////////////////////////////////////////////
  // For information as to what this class does, see the Javadoc, below.       //
  // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,       //
  // 2007, 2008, 2009, 2010, 2014, 2015 by Peter Spirtes, Richard Scheines, Joseph   //
  // Ramsey, and Clark Glymour.                                                //
  //                                                                           //
  // This program is free software; you can redistribute it and/or modify      //
  // it under the terms of the GNU General Public License as published by      //
  // the Free Software Foundation; either version 2 of the License, or         //
  // (at your option) any later version.                                       //
  //                                                                           //
  // This program is distributed in the hope that it will be useful,           //
  // but WITHOUT ANY WARRANTY; without even the implied warranty of            //
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             //
  // GNU General Public License for more details.                              //
  //                                                                           //
  // You should have received a copy of the GNU General Public License         //
  // along with this program; if not, write to the Free Software               //
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA //
  ///////////////////////////////////////////////////////////////////////////////
  
  package edu.cmu.tetrad.graph;
  
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.List;
  
  import edu.cmu.tetrad.graph.Edge.Property;
  import edu.cmu.tetrad.graph.EdgeTypeProbability.EdgeType;
  
  
  /**
   * This factory class produces edges of the types commonly used for Tetrad-style
   * graphs.  For each method in the class, one supplies a pair of nodes, and an
   * edge is returned, connecting those nodes, of the specified type.  Methods are
   * also included to help determine whether a specified edge falls into one of
   * the types produced by this factory.  It's entirely possible to produce edges
   * of these types other than by using this factory.  For randomUtil, an edge
   * counts as a directed edge just in case it has one null endpoint and one arrow
   * endpoint.  Any edge which has one null endpoint and one arrow endpoint will
   * do, whether or not this factory produced it.  These helper methods provide a
   * uniform way of testing whether an edge is in fact, e.g., a directed edge (or
   * any of the other types).
   *
   * @author Joseph Ramsey
   */
  public final class Edges {
  
      /**
       * Constructs a new bidirected edge from nodeA to nodeB (<->).
       */
      public static Edge bidirectedEdge(Node nodeA, Node nodeB) {
          return new Edge(nodeA, nodeB, Endpoint.ARROW, Endpoint.ARROW);
      }
  
      /**
       * Constructs a new directed edge from nodeA to nodeB (-->).
       */
      public static Edge directedEdge(Node nodeA, Node nodeB) {
          return new Edge(nodeA, nodeB, Endpoint.TAIL, Endpoint.ARROW);
      }
  
      /**
       * Constructs a new partially oriented edge from nodeA to nodeB (o->).
       */
      public static Edge partiallyOrientedEdge(Node nodeA, Node nodeB) {
          return new Edge(nodeA, nodeB, Endpoint.CIRCLE, Endpoint.ARROW);
      }
  
      /**
       * Constructs a new nondirected edge from nodeA to nodeB (o-o).
       */
      public static Edge nondirectedEdge(Node nodeA, Node nodeB) {
          return new Edge(nodeA, nodeB, Endpoint.CIRCLE, Endpoint.CIRCLE);
      }
  
      /**
       * Constructs a new undirected edge from nodeA to nodeB (--).
       */
      public static Edge undirectedEdge(Node nodeA, Node nodeB) {
          return new Edge(nodeA, nodeB, Endpoint.TAIL, Endpoint.TAIL);
      }
  
      /**
       * @return true iff an edge is a bidirected edge (<->).
       */
      public static boolean isBidirectedEdge(Edge edge) {
          return (edge.getEndpoint1() == Endpoint.ARROW) &&
                  (edge.getEndpoint2() == Endpoint.ARROW);
      }
  
      /**
       * @return true iff the given edge is a directed edge (-->).
       */
      public static boolean isDirectedEdge(Edge edge) {
          if (edge.getEndpoint1() == Endpoint.TAIL) {
              if (edge.getEndpoint2() == Endpoint.ARROW) {
                  return true;
              }
          } else if (edge.getEndpoint2() == Endpoint.TAIL) {
              if (edge.getEndpoint1() == Endpoint.ARROW) {
                  return true;
              }
          }
  
          return false;
      }
  
      /**
       * @return true iff the given edge is a partially oriented edge (o->)
       */
      public static boolean isPartiallyOrientedEdge(Edge edge) {
          if (edge.getEndpoint1() == Endpoint.CIRCLE) {
              if (edge.getEndpoint2() == Endpoint.ARROW) {
                  return true;
              }
          } else if (edge.getEndpoint2() == Endpoint.CIRCLE) {
              if (edge.getEndpoint1() == Endpoint.ARROW) {
                  return true;
              }
          }
  
          return false;
      }
  
      /**
       * @return true iff some edge is an nondirected edge (o-o).
       */
      public static boolean isNondirectedEdge(Edge edge) {
          return ((edge.getEndpoint1() == Endpoint.CIRCLE) &&
                  (edge.getEndpoint2() == Endpoint.CIRCLE));
      }
  
      /**
       * @return true iff some edge is an undirected edge (-).
       */
      public static boolean isUndirectedEdge(Edge edge) {
          return ((edge.getEndpoint1() == Endpoint.TAIL) &&
                  (edge.getEndpoint2() == Endpoint.TAIL));
      }
  
      /**
       * @return the node opposite the given node along the given edge.
       */
      public static Node traverse(Node node, Edge edge) {
          if (node == null) {
              return null;
          }
          // changed == to equals.
          if (node.equals(edge.getNode1())) {
              return edge.getNode2();
          } else if (node.equals(edge.getNode2())) {
              return edge.getNode1();
          }
  
          return null;
      }
  
      /**
       * For A -> B, given A, returns B; otherwise returns null.
       */
      public static Node traverseDirected(Node node, Edge edge) {
          if (node == edge.getNode1()) {
              if ((edge.getEndpoint1() == Endpoint.TAIL) &&
                      (edge.getEndpoint2() == Endpoint.ARROW)) {
                  return edge.getNode2();
              }
          } else if (node == edge.getNode2()) {
              if ((edge.getEndpoint2() == Endpoint.TAIL) &&
                      (edge.getEndpoint1() == Endpoint.ARROW)) {
                  return edge.getNode1();
              }
          }
  
          return null;
      }
  
      /**
       * For A -> B, given B, returns A; otherwise returns null.
       */
      public static Node traverseReverseDirected(Node node, Edge edge) {
          if (edge == null) {
              return null;
          }
  
          if (node == edge.getNode1()) {
              if ((edge.getEndpoint1() == Endpoint.ARROW) &&
                      (edge.getEndpoint2() == Endpoint.TAIL)) {
                  return edge.getNode2();
              }
          } else if (node == edge.getNode2()) {
              if ((edge.getEndpoint2() == Endpoint.ARROW) &&
                      (edge.getEndpoint1() == Endpoint.TAIL)) {
                  return edge.getNode1();
              }
          }
  
          return null;
      }
  
      public static Node traverseReverseSemiDirected(Node node, Edge edge) {
          if (edge == null) {
              return null;
          }
  
          if (node == edge.getNode1()) {
              if ((edge.getEndpoint2() == Endpoint.TAIL || edge.getEndpoint2() == Endpoint.CIRCLE)) {
                  return edge.getNode2();
              }
          } else if (node == edge.getNode2()) {
              if ((edge.getEndpoint1() == Endpoint.TAIL || edge.getEndpoint1() == Endpoint.CIRCLE)) {
                  return edge.getNode1();
              }
          }
  
          return null;
      }
  
      /**
       * For A --* B or A o-* B, given A, returns B. For A <-* B, returns null.
       * Added by ekorber, 2004/06/12.
       */
      public static Node traverseSemiDirected(Node node, Edge edge) {
          if (node == edge.getNode1()) {
              if ((edge.getEndpoint1() == Endpoint.TAIL || edge.getEndpoint1() == Endpoint.CIRCLE)) {
                  return edge.getNode2();
              }
          } else if (node == edge.getNode2()) {
              if ((edge.getEndpoint2() == Endpoint.TAIL || edge.getEndpoint2() == Endpoint.CIRCLE)) {
                  return edge.getNode1();
              }
          }
          return null;
      }
  
      public static Node traverseUndirected(Node node, Edge edge) {
          if (node == edge.getNode1()) {
              return edge.getNode2();
          } else if (node == edge.getNode2()) {
              return edge.getNode1();
          } else {
              return null;
          }
      }
  
  
      /**
       * For a directed edge, returns the node adjacent to the arrow endpoint.
       *
       * @throws IllegalArgumentException if the given edge is not a directed
       *                                  edge.
       */
      public static Node getDirectedEdgeHead(Edge edge) {
          if ((edge.getEndpoint1() == Endpoint.ARROW) &&
                  (edge.getEndpoint2() == Endpoint.TAIL)) {
              return edge.getNode1();
          } else if ((edge.getEndpoint2() == Endpoint.ARROW) &&
                  (edge.getEndpoint1() == Endpoint.TAIL)) {
              return edge.getNode2();
          } else {
              throw new IllegalArgumentException("Not a directed edge: " + edge);
          }
      }
  
      /**
       * For a directed edge, returns the node adjacent to the null endpoint.
       *
       * @throws IllegalArgumentException if the given edge is not a directed
       *                                  edge.
       */
      public static Node getDirectedEdgeTail(Edge edge) {
          if ((edge.getEndpoint2() == Endpoint.ARROW) &&
                  (edge.getEndpoint1() == Endpoint.TAIL)) {
              return edge.getNode1();
          } else if ((edge.getEndpoint1() == Endpoint.ARROW) &&
                  (edge.getEndpoint2() == Endpoint.TAIL)) {
              return edge.getNode2();
          } else {
              throw new IllegalArgumentException("Not a directed edge: " + edge);
          }
      }
  
      public static void sortEdges(List<Edge> edges) {
          Collections.sort(edges, new Comparator<Edge>() {
              public int compare(Edge edge1, Edge edge2) {
                  if (edge1 == null || edge2 == null) {
                      return 0;
                  }
  
                  Node left1 = edge1.getNode1();
                  Node right1 = edge1.getNode2();
  
                  Node left2 = edge2.getNode1();
                  Node right2 = edge2.getNode2();
  
                  List<Property> propertiesLeft = edge1.getProperties();
                  List<EdgeTypeProbability> edgeTypePropertiesLeft = edge1.getEdgeTypeProbabilities();
                  
                  List<Property> propertiesRight = edge2.getProperties();
                  List<EdgeTypeProbability> edgeTypePropertiesRight = edge2.getEdgeTypeProbabilities();
                  
                  // Compare edgeTypeProperty first, if exists
                  int compareEdgeTypeProperty = 0;
                  if(!edgeTypePropertiesLeft.isEmpty() && !edgeTypePropertiesRight.isEmpty()) {
                  	// Max probability on the left - excluding [no edge]
                  	double probLeft = 0;
                  	for(EdgeTypeProbability etp : edgeTypePropertiesLeft) {
                  		if(etp.getEdgeType() != EdgeType.nil && etp.getProbability() > probLeft) {
                  			probLeft = etp.getProbability();
                  		}
                  	}
                  	
                  	// Max probability on the right - excluding [no edge]
                  	double probRight = 0;
                  	for(EdgeTypeProbability etp : edgeTypePropertiesRight) {
                  		if(etp.getEdgeType() != EdgeType.nil && etp.getProbability() > probRight) {
                  			probRight = etp.getProbability();
                  		}
                  	}
                  	
                  	if(probLeft - probRight > 0) {
                  		compareEdgeTypeProperty = -1;
                  	}else if(probLeft - probRight < 0) {
                  		compareEdgeTypeProperty = 1;
                  	}            	
                  }
                  if (compareEdgeTypeProperty != 0) {
                  	return compareEdgeTypeProperty;
                  }
                  
                  // Compare edge's properties
                  int compareProperty = 0;
                  int scorePropertyLeft = 0;
                  for(Property property : propertiesLeft) {
                  	if(property == Property.dd || property == Property.nl) {
                  		scorePropertyLeft += 2;
                  	}
                  	if(property == Property.pd || property == Property.pl) {
                  		scorePropertyLeft += 1;
                  	}
                  }
                  int scorePropertyRight = 0;
                  for(Property property : propertiesRight) {
                  	if(property == Property.dd || property == Property.nl) {
                  		scorePropertyRight += 2;
                  	}
                  	if(property == Property.pd || property == Property.pl) {
                  		scorePropertyRight += 1;
                  	}
                  }
                  
                  if(scorePropertyLeft - scorePropertyRight > 0) {
                  	compareProperty = -1;
                  }else if(scorePropertyLeft - scorePropertyRight < 0) {
                  	compareProperty = 1;
                  }
                  if(compareProperty != 0) {	
                  	return compareProperty;
                  }
                  
                  int compareLeft = left1.toString().compareTo(left2.toString());
                  int compareRight = right1.toString().compareTo(right2.toString());
  
                  if (compareLeft != 0) {
                      return compareLeft;
                  } else {
                      return compareRight;
                  }
              }
          });
      }
  }
  
  
  
  
  
  