protected void replaceOperator(
      final Operator newOP, final VisualGraph<Operator> parent, final GraphWrapper oldGW) {
    newOP.cloneFrom(this);
    this.replaceWith(newOP);

    final GraphWrapper gw = new GraphWrapperOperator(newOP);
    final GraphBox box = parent.getBoxes().get(oldGW);

    box.initBox(gw);
    box.arrange(Arrange.values()[0]);

    parent.getBoxes().remove(oldGW);
    parent.getBoxes().put(gw, box);

    parent.remove(this.panel);
    parent.add(box.getElement());

    for (final Operator preOp : this.precedingOperators) {
      final GraphWrapper preGW = new GraphWrapperOperator(preOp);
      final GraphBox preBox = parent.getBoxes().get(preGW);

      preBox.setLineAnnotations(preOp.drawAnnotations(parent));
    }

    parent.revalidate();
    parent.repaint();
  }
  /**
   * Method compares the average edge-length and the average node-distance with the optimal
   * Sugiyama-distance.
   *
   * @param graph
   */
  public static String smallestSeparation_Test(OperatorGraph graph) {
    HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes();
    int nodeAmount = boxes.size();
    double scaleConst = 1.0;
    int width = GraphHelper.graphWidth(graph);
    int height = GraphHelper.graphHeight(graph);
    LinkedList<Double> distances = new LinkedList<Double>();
    // compute optimal distance after Sugiyama
    double optiDist = scaleConst * Math.sqrt((width * height) / nodeAmount);

    double ariMiddle = 0.0;
    int edges = 0;
    LinkedList<Double> length = new LinkedList<Double>();

    for (GraphWrapper gw : boxes.keySet()) {
      LinkedList<GraphWrapper> children = gw.getPrecedingElements();
      for (GraphWrapper child : children) {
        double edgeLen = edgeLength(graph, gw, child);
        length.add(edgeLen);
        ariMiddle += edgeLen;
        edges++;
      }
    }

    ariMiddle = ariMiddle / edges;

    double avrDist = 0.0;
    for (GraphWrapper node1 : boxes.keySet()) {
      LinkedList<GraphWrapperIDTuple> childrenTuple = node1.getSucceedingElements();
      LinkedList<GraphWrapper> children = new LinkedList<GraphWrapper>();
      for (GraphWrapperIDTuple child : childrenTuple) {
        children.add(child.getOperator());
      }
      GraphBox box1 = boxes.get(node1);
      for (GraphWrapper node2 : boxes.keySet()) {
        if ((!node2.equals(node1))) {
          GraphBox box2 = boxes.get(node2);
          // compute distance between node1 and node 2
          int x = box2.getX() - box1.getX();
          int y = box2.getY() - box1.getY();
          double distance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
          distances.add(distance);
          avrDist += distance;
        }
      }
    }

    avrDist = avrDist / distances.size();

    String result = "Test: Smallest Separation:\n";
    result += "The optimal distance based on Sugiyama is " + optiDist + ".\n";
    result += "The average distance between not connected nodes is " + avrDist + ".\n";
    result += "The average edge length is " + ariMiddle + ".\n";

    return result;
  }
  /**
   * Method gets the edge-length between the nodes a and b
   *
   * @param graph the graph
   * @param a first node
   * @param b second node
   * @return edge length
   */
  private static double edgeLength(OperatorGraph graph, GraphWrapper a, GraphWrapper b) {
    double length = 0.0;
    HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes();

    GraphBox boxA = boxes.get(a);
    GraphBox boxB = boxes.get(b);
    double x = boxA.getX() - boxB.getX();
    double y = boxA.getY() - boxB.getY();
    length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));

    return length;
  }
  /**
   * Method gets the of edge-crossings, if an edge is vertical
   *
   * @param edge the vertical edge
   * @param edges the rest of edges in the graph
   * @param op the graph
   * @return crossing-amount
   */
  private static int infinitSlope(Edge edge, LinkedList<Edge> edges, OperatorGraph op) {
    int crossings = 0;

    HashMap<GraphWrapper, GraphBox> boxes = op.getBoxes();

    GraphBox source1 = boxes.get(edge.getSource());
    GraphBox target1 = boxes.get(edge.getTarget());
    for (Edge edge2 : edges) {
      GraphBox source2 = boxes.get(edge2.getSource());
      GraphBox target2 = boxes.get(edge2.getTarget());
      if (target2.getX() == source2.getX()) {
        double m =
            ((double) target2.getY() - (double) source2.getY())
                / ((double) target2.getX() - (double) source2.getX()); // slope of line 2
        double b = target2.getY() - (m * target2.getX()); // crossing with y-axis off line 2

        double y = m * source1.getX() + b;
        double minY = Math.min(source1.getY(), target1.getY());
        double maxY = Math.max(source1.getY(), target1.getY());

        if ((y > minY) && (y < maxY)) {
          crossings++;
        }
      }
    }
    return crossings;
  }
  /**
   * Method tests if a graph-layout is symmetric to an axis and shows the result on command-line
   *
   * @param graph
   */
  public static String symmetry_Test(OperatorGraph graph) {
    int width = GraphHelper.graphWidth(graph);
    double symLine = width / 2;

    HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes();
    LinkedList<GraphBox> left = new LinkedList<GraphBox>();
    LinkedList<GraphBox> right = new LinkedList<GraphBox>();

    String result = "Test: Axially Symmetry:\n";
    // get boxes of left and right side of symmetry-line
    for (GraphBox box : boxes.values()) {
      if ((box.getX() + box.width) <= symLine) {
        left.add(box);
      } else if (box.getX() >= symLine) {
        right.add(box);
      } else { // test if node is centered on symmetry-line
        if ((box.getX() + (box.width / 2)) != symLine) {
          result += "Node not in the middle of the symmetry axe!\n";
          result += "Node with x = " + box.getX() + "    y = " + box.getY() + ".\n";
        }
      }
    }

    // test if node-amount on left and right side of symmetry-line is equal
    if (left.size() != right.size()) {
      result += "Different number of nodes on both sides of the symmetry axe:\n";
      result += "Left: " + left.size() + "    Right: " + right.size() + "\n";
    }

    // compute symmetric node-counter-parts
    int symCounter = 0;
    boolean sym = true;
    for (GraphBox box1 : left) {
      double symSpace = symLine - (box1.getX() + box1.width);
      int y = box1.getY();
      for (GraphBox box2 : right) {
        if (y == box2.getY()) {
          if ((symLine + symSpace) == box2.getX()) {
            sym = true;
            symCounter++;
            break;
          } else {
            sym = false;
          }
        }
        sym = false;
      }
    }

    // compute number of symmetric nodes
    final int sumSizes = left.size() + right.size();
    double p = (sumSizes == 0) ? 100 : (2 * symCounter * 100) / ((double) sumSizes);

    if (sym == false) {
      result += "The graph is " + p + "% axially symmetric!\n";
    } else {
      result += "The graph is 100% axially symmetric!\n";
    }
    return result;
  }
  /**
   * Method tests if graph-nodes are uniform distributed over the screen.
   *
   * @param op the graph
   */
  public static String uniformDistribution_Test(OperatorGraph op) {
    HashMap<GraphWrapper, GraphBox> boxes = op.getBoxes();
    double width = GraphHelper.graphWidth(op);
    double height = GraphHelper.graphHeight(op);
    int nodeAmount = boxes.size();
    int fieldNumber = 0;

    // compute number of grid-fields
    fieldNumber = (int) Math.floor(Math.sqrt(nodeAmount));

    int[][] grid = new int[fieldNumber][fieldNumber];

    // compute width and height of grid-fields
    int fieldSpaceX = (int) Math.ceil(width / fieldNumber);
    int fieldSpaceY = (int) Math.ceil(height / fieldNumber);
    int x = fieldSpaceX;
    int y = fieldSpaceY;

    // compute how many nodes are in the grid-fields
    for (int i = 0; i < fieldNumber; i++) {
      for (int j = 0; j < fieldNumber; j++) {
        for (GraphBox box : boxes.values()) {
          if ((box.getX() + box.width < x)
              && (box.getX() >= x - fieldSpaceX)
              && (box.getY() + box.height < y)
              && (box.getY() >= y - fieldSpaceY)) {
            grid[i][j]++;
          }
        }
        y += fieldSpaceY;
      }
      x += fieldSpaceX;
      y = fieldSpaceY;
    }

    // test if nodes are uniformly distributed
    int notUniform = 0;
    boolean equ = true;
    for (int i = 0; i < fieldNumber; i++) {
      for (int j = 0; j < fieldNumber; j++) {
        if ((grid[i][j] > 2) || (grid[i][j] < 1)) {
          equ = false;
          notUniform++;
        }
      }
    }

    String result = "Test: uniform distribution of nodes:\n";
    result += "Size of grid: " + fieldNumber + " * " + fieldNumber + "\n";
    System.out.println("");
    if (equ == false) {
      int fields = fieldNumber * fieldNumber;
      // compute percent of uniform node distribution
      double p = 100 - ((notUniform * 100) / fields);
      result += "The graph is about " + p + "% uniformly filled with nodes...\n";
    } else {
      result += "All grid fields contain 1 or 2 nodes.\n";
      result += "The graph is 100% uniformly filled with nodes...\n";
    }
    return result;
  }
  /**
   * Method computes the amount of edge-crossing in a graph and displays the result on command-line
   */
  public static String minEdgeCrossing_Test(OperatorGraph graph) {
    HashMap<GraphWrapper, GraphBox> boxes = graph.getBoxes();
    LinkedList<Edge> edges = new LinkedList<Edge>();
    int crossCounter = 0;

    // get all edges of the graph
    for (GraphWrapper gw : boxes.keySet()) {
      LinkedList<GraphWrapperIDTuple> children = gw.getSucceedingElements();
      for (GraphWrapperIDTuple child : children) {
        Edge edge = new Edge(gw, child.getOperator(), 0);
        edges.add(edge);
      }
    }
    int edgecount = 0;
    LinkedList<Edge> visited = new LinkedList<Edge>();
    for (int i = 0; i < edges.size(); i++
    /** Edge edge1 : edges* */
    ) {
      Edge edge1 = edges.get(i);
      edgecount++;
      GraphBox sourceE1 = boxes.get(edge1.getSource());
      GraphBox targetE1 = boxes.get(edge1.getTarget());
      // edge 1 is vertical
      if ((targetE1.getX() == sourceE1.getX())) {
        crossCounter = infinitSlope(edge1, edges, graph);
      } else {
        double m1 =
            ((double) targetE1.getY() - (double) sourceE1.getY())
                / ((double) targetE1.getX() - (double) sourceE1.getX()); // slope of line 1
        double b1 = (targetE1.getY()) - (m1 * targetE1.getX()); // slope of line 1
        for (int j = 0; j < edges.size(); j++
        /** Edge edge2 : edges* */
        ) {
          Edge edge2 = edges.get(j);

          if ((!edge2.equals(edge1)) && (!visited.contains(edge2)) && (m1 != 0.0)) {
            GraphBox sourceE2 = boxes.get(edge2.getSource());
            GraphBox targetE2 = boxes.get(edge2.getTarget());
            // edge 2 is vertical
            if (targetE2.getX() == sourceE2.getX()) {
              crossCounter += infinitSlope(edge2, edge1, graph);

            } else {
              double m2 =
                  ((double) targetE2.getY() - (double) sourceE2.getY())
                      / ((double) targetE2.getX() - (double) sourceE2.getX()); // slope of line 2
              double b2 =
                  (targetE2.getY()) - (m2 * targetE2.getX()); // crossing with y-axis of line 2
              double x = ((b2 - b1) / (m1 - m2));
              double y = ((m1 * x) + b1); // cross-point-coordinates of the 2 lines
              double maxXe1 = Math.max(sourceE1.getX(), targetE1.getX());
              double maxXe2 = Math.max(sourceE2.getX(), targetE2.getX());
              double minXe1 = Math.min(sourceE1.getX(), targetE1.getX());
              double minXe2 = Math.min(sourceE2.getX(), targetE2.getX());
              double maxYe1 = Math.max(sourceE1.getY(), targetE1.getY());
              double maxYe2 = Math.max(sourceE2.getY(), targetE2.getY());
              double minYe1 = Math.min(sourceE1.getY(), targetE1.getY());
              double minYe2 = Math.min(sourceE2.getY(), targetE2.getY());

              // test if cross-point is part of edge 1
              if ((x < maxXe1) && (x > minXe1) && (y < maxYe1) && (y > minYe1)) {
                if ((x < maxXe2) && (x > minXe2) && (y < maxYe2) && (y > minYe2)) {
                  crossCounter++;
                }
              }
            }
            // visited.add(edge2);
          }
        }
      }
      visited.add(edge1);
    }

    return "Test: Crossing Edges:\nThe number of edges crossing is "
        + crossCounter
        + " of total "
        + edgecount
        + " edges.\n";
  }