/**
   * Sorts the linear segments of the given layered graph by finding a topological ordering in the
   * corresponding segment ordering graph.
   *
   * @param layeredGraph layered graph to process
   * @return a sorted array of linear segments
   */
  private LinearSegment[] sortLinearSegments(final LGraph layeredGraph) {
    // set the identifier and input / output priority for all nodes
    List<LinearSegment> segmentList = Lists.newArrayList();
    for (Layer layer : layeredGraph) {
      for (LNode node : layer) {
        node.id = -1;
        int inprio = Integer.MIN_VALUE, outprio = Integer.MIN_VALUE;
        for (LPort port : node.getPorts()) {
          for (LEdge edge : port.getIncomingEdges()) {
            int prio = edge.getProperty(InternalProperties.PRIORITY);
            inprio = Math.max(inprio, prio);
          }
          for (LEdge edge : port.getOutgoingEdges()) {
            int prio = edge.getProperty(InternalProperties.PRIORITY);
            outprio = Math.max(outprio, prio);
          }
        }
        node.setProperty(INPUT_PRIO, inprio);
        node.setProperty(OUTPUT_PRIO, outprio);
      }
    }

    // create linear segments for the layered graph, ignoring odd port side dummies
    int nextLinearSegmentID = 0;
    for (Layer layer : layeredGraph) {
      for (LNode node : layer) {
        // Test for the node ID; calls to fillSegment(...) may have caused the node ID
        // to be != -1.
        if (node.id < 0) {
          LinearSegment segment = new LinearSegment();
          segment.id = nextLinearSegmentID++;
          fillSegment(node, segment);
          segmentList.add(segment);
        }
      }
    }

    // create and initialize segment ordering graph
    List<List<LinearSegment>> outgoingList = Lists.newArrayListWithCapacity(segmentList.size());
    List<Integer> incomingCountList = Lists.newArrayListWithCapacity(segmentList.size());
    for (int i = 0; i < segmentList.size(); i++) {
      outgoingList.add(new ArrayList<LinearSegment>());
      incomingCountList.add(0);
    }

    // create edges for the segment ordering graph
    createDependencyGraphEdges(layeredGraph, segmentList, outgoingList, incomingCountList);

    // turn lists into arrays
    LinearSegment[] segments = segmentList.toArray(new LinearSegment[segmentList.size()]);

    @SuppressWarnings("unchecked")
    List<LinearSegment>[] outgoing = outgoingList.toArray(new List[outgoingList.size()]);

    int[] incomingCount = new int[incomingCountList.size()];
    for (int i = 0; i < incomingCount.length; i++) {
      incomingCount[i] = incomingCountList.get(i);
    }

    // gather the sources of the segment ordering graph
    int nextRank = 0;
    List<LinearSegment> noIncoming = Lists.newArrayList();
    for (int i = 0; i < segments.length; i++) {
      if (incomingCount[i] == 0) {
        noIncoming.add(segments[i]);
      }
    }

    // find a topological ordering of the segment ordering graph
    int[] newRanks = new int[segments.length];
    while (!noIncoming.isEmpty()) {
      LinearSegment segment = noIncoming.remove(0);
      newRanks[segment.id] = nextRank++;

      while (!outgoing[segment.id].isEmpty()) {
        LinearSegment target = outgoing[segment.id].remove(0);
        incomingCount[target.id]--;

        if (incomingCount[target.id] == 0) {
          noIncoming.add(target);
        }
      }
    }

    // apply the new ordering to the array of linear segments
    linearSegments = new LinearSegment[segments.length];
    for (int i = 0; i < segments.length; i++) {
      assert outgoing[i].isEmpty();
      LinearSegment ls = segments[i];
      int rank = newRanks[i];
      linearSegments[rank] = ls;
      ls.id = rank;
      for (LNode node : ls.nodes) {
        node.id = rank;
      }
    }

    return linearSegments;
  }