1/*
2 * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24package com.sun.hotspot.igv.hierarchicallayout;
25
26import com.sun.hotspot.igv.layout.LayoutGraph;
27import com.sun.hotspot.igv.layout.LayoutManager;
28import com.sun.hotspot.igv.layout.Link;
29import com.sun.hotspot.igv.layout.Vertex;
30import java.awt.Dimension;
31import java.awt.Point;
32import java.util.*;
33
34/**
35 *
36 * @author Thomas Wuerthinger
37 */
38public class HierarchicalLayoutManager implements LayoutManager {
39
40    public static final boolean TRACE = false;
41    public static final boolean CHECK = false;
42    public static final int SWEEP_ITERATIONS = 1;
43    public static final int CROSSING_ITERATIONS = 2;
44    public static final int DUMMY_HEIGHT = 1;
45    public static final int DUMMY_WIDTH = 1;
46    public static final int X_OFFSET = 8;
47    public static final int LAYER_OFFSET = 8;
48    public static final int MAX_LAYER_LENGTH = -1;
49    public static final int MIN_LAYER_DIFFERENCE = 1;
50    public static final int VIP_BONUS = 10;
51
52    public enum Combine {
53
54        NONE,
55        SAME_INPUTS,
56        SAME_OUTPUTS
57    }
58    // Options
59    private Combine combine;
60    private int dummyWidth;
61    private int dummyHeight;
62    private int xOffset;
63    private int layerOffset;
64    private int maxLayerLength;
65    private int minLayerDifference;
66    // Algorithm global datastructures
67    private Set<Link> reversedLinks;
68    private List<LayoutNode> nodes;
69    private HashMap<Vertex, LayoutNode> vertexToLayoutNode;
70    private HashMap<Link, List<Point>> reversedLinkStartPoints;
71    private HashMap<Link, List<Point>> reversedLinkEndPoints;
72    private HashMap<LayoutEdge, LayoutEdge> bottomEdgeHash;
73    private HashMap<Link, List<Point>> splitStartPoints;
74    private HashMap<Link, List<Point>> splitEndPoints;
75    private LayoutGraph graph;
76    private List<LayoutNode>[] layers;
77    private int layerCount;
78    private Set<? extends Link> importantLinks;
79    private Set<Link> linksToFollow;
80
81    private class LayoutNode {
82
83        public int x;
84        public int y;
85        public int width;
86        public int height;
87        public int layer = -1;
88        public int xOffset;
89        public int yOffset;
90        public int bottomYOffset;
91        public Vertex vertex; // Only used for non-dummy nodes, otherwise null
92
93        public List<LayoutEdge> preds = new ArrayList<>();
94        public List<LayoutEdge> succs = new ArrayList<>();
95        public HashMap<Integer, Integer> outOffsets = new HashMap<>();
96        public HashMap<Integer, Integer> inOffsets = new HashMap<>();
97        public int pos = -1; // Position within layer
98
99        public int crossingNumber;
100
101        @Override
102        public String toString() {
103            return "Node " + vertex;
104        }
105    }
106
107    private class LayoutEdge {
108
109        public LayoutNode from;
110        public LayoutNode to;
111        public int relativeFrom;
112        public int relativeTo;
113        public Link link;
114        public boolean vip;
115    }
116
117    private abstract class AlgorithmPart {
118
119        public void start() {
120            if (CHECK) {
121                preCheck();
122            }
123
124            long start = 0;
125            if (TRACE) {
126                System.out.println("##################################################");
127                System.out.println("Starting part " + this.getClass().getName());
128                start = System.currentTimeMillis();
129            }
130            run();
131            if (TRACE) {
132                System.out.println("Timing for " + this.getClass().getName() + " is " + (System.currentTimeMillis() - start));
133                printStatistics();
134            }
135
136            if (CHECK) {
137                postCheck();
138            }
139        }
140
141        protected abstract void run();
142
143        protected void printStatistics() {
144        }
145
146        protected void postCheck() {
147        }
148
149        protected void preCheck() {
150        }
151    }
152
153    public HierarchicalLayoutManager() {
154        this(Combine.NONE);
155    }
156
157    public HierarchicalLayoutManager(Combine b) {
158        this.combine = b;
159        this.dummyWidth = DUMMY_WIDTH;
160        this.dummyHeight = DUMMY_HEIGHT;
161        this.xOffset = X_OFFSET;
162        this.layerOffset = LAYER_OFFSET;
163        this.maxLayerLength = MAX_LAYER_LENGTH;
164        this.minLayerDifference = MIN_LAYER_DIFFERENCE;
165        this.linksToFollow = new HashSet<>();
166    }
167
168    public int getMaxLayerLength() {
169        return maxLayerLength;
170    }
171
172    public void setMaxLayerLength(int v) {
173        maxLayerLength = v;
174    }
175
176    public void setMinLayerDifference(int v) {
177        minLayerDifference = v;
178    }
179
180    @Override
181    public void doLayout(LayoutGraph graph) {
182        doLayout(graph, new HashSet<Link>());
183
184    }
185
186    @Override
187    public void doLayout(LayoutGraph graph, Set<? extends Link> importantLinks) {
188
189        this.importantLinks = importantLinks;
190        this.graph = graph;
191
192        vertexToLayoutNode = new HashMap<>();
193        reversedLinks = new HashSet<>();
194        reversedLinkStartPoints = new HashMap<>();
195        reversedLinkEndPoints = new HashMap<>();
196        bottomEdgeHash = new HashMap<>();
197        nodes = new ArrayList<>();
198        splitStartPoints = new HashMap<>();
199        splitEndPoints = new HashMap<>();
200
201        // #############################################################
202        // Step 1: Build up data structure
203        new BuildDatastructure().start();
204
205        // #############################################################
206        // STEP 2: Reverse edges, handle backedges
207        new ReverseEdges().start();
208
209        for (LayoutNode n : nodes) {
210            ArrayList<LayoutEdge> tmpArr = new ArrayList<>();
211            for (LayoutEdge e : n.succs) {
212                if (importantLinks.contains(e.link)) {
213                    tmpArr.add(e);
214                }
215            }
216
217            for (LayoutEdge e : tmpArr) {
218                e.from.succs.remove(e);
219                e.to.preds.remove(e);
220            }
221        }
222
223        // #############################################################
224        // STEP 3: Assign layers
225        new AssignLayers().start();
226
227        // #############################################################
228        // STEP 4: Create dummy nodes
229        new CreateDummyNodes().start();
230
231        // #############################################################
232        // STEP 5: Crossing Reduction
233        new CrossingReduction().start();
234
235        // #############################################################
236        // STEP 7: Assign X coordinates
237        new AssignXCoordinates().start();
238
239        // #############################################################
240        // STEP 6: Assign Y coordinates
241        new AssignYCoordinates().start();
242
243        // #############################################################
244        // STEP 8: Write back to interface
245        new WriteResult().start();
246    }
247
248    private class WriteResult extends AlgorithmPart {
249
250        private int pointCount;
251
252        @Override
253        protected void run() {
254
255            HashMap<Vertex, Point> vertexPositions = new HashMap<>();
256            HashMap<Link, List<Point>> linkPositions = new HashMap<>();
257            for (Vertex v : graph.getVertices()) {
258                LayoutNode n = vertexToLayoutNode.get(v);
259                assert !vertexPositions.containsKey(v);
260                vertexPositions.put(v, new Point(n.x + n.xOffset, n.y + n.yOffset));
261            }
262
263            for (LayoutNode n : nodes) {
264
265                for (LayoutEdge e : n.preds) {
266                    if (e.link != null) {
267                        ArrayList<Point> points = new ArrayList<>();
268
269                        Point p = new Point(e.to.x + e.relativeTo, e.to.y + e.to.yOffset + e.link.getTo().getRelativePosition().y);
270                        points.add(p);
271                        if (e.to.inOffsets.containsKey(e.relativeTo)) {
272                            points.add(new Point(p.x, p.y + e.to.inOffsets.get(e.relativeTo) + e.link.getTo().getRelativePosition().y));
273                        }
274
275                        LayoutNode cur = e.from;
276                        LayoutNode other = e.to;
277                        LayoutEdge curEdge = e;
278                        while (cur.vertex == null && cur.preds.size() != 0) {
279                            if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
280                                points.remove(points.size() - 1);
281                            }
282                            points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height));
283                            if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
284                                points.remove(points.size() - 1);
285                            }
286                            points.add(new Point(cur.x + cur.width / 2, cur.y));
287                            assert cur.preds.size() == 1;
288                            curEdge = cur.preds.get(0);
289                            cur = curEdge.from;
290                        }
291
292                        p = new Point(cur.x + curEdge.relativeFrom, cur.y + cur.height - cur.bottomYOffset + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y));
293                        if (curEdge.from.outOffsets.containsKey(curEdge.relativeFrom)) {
294                            points.add(new Point(p.x, p.y + curEdge.from.outOffsets.get(curEdge.relativeFrom) + (curEdge.link == null ? 0 : curEdge.link.getFrom().getRelativePosition().y)));
295                        }
296                        points.add(p);
297
298                        Collections.reverse(points);
299
300                        if (cur.vertex == null && cur.preds.size() == 0) {
301
302                            if (reversedLinkEndPoints.containsKey(e.link)) {
303                                for (Point p1 : reversedLinkEndPoints.get(e.link)) {
304                                    points.add(new Point(p1.x + e.to.x, p1.y + e.to.y));
305                                }
306                            }
307
308                            if (splitStartPoints.containsKey(e.link)) {
309                                points.add(0, null);
310                                points.addAll(0, splitStartPoints.get(e.link));
311
312                                //checkPoints(points);
313                                if (reversedLinks.contains(e.link)) {
314                                    Collections.reverse(points);
315                                }
316                                assert !linkPositions.containsKey(e.link);
317                                linkPositions.put(e.link, points);
318                            } else {
319                                splitEndPoints.put(e.link, points);
320                            }
321
322                        } else {
323                            if (reversedLinks.contains(e.link)) {
324                                Collections.reverse(points);
325                            }
326                            if (reversedLinkStartPoints.containsKey(e.link)) {
327                                for (Point p1 : reversedLinkStartPoints.get(e.link)) {
328                                    points.add(new Point(p1.x + cur.x, p1.y + cur.y));
329                                }
330                            }
331
332                            if (reversedLinkEndPoints.containsKey(e.link)) {
333                                for (Point p1 : reversedLinkEndPoints.get(e.link)) {
334                                    points.add(0, new Point(p1.x + other.x, p1.y + other.y));
335                                }
336                            }
337
338                            assert !linkPositions.containsKey(e.link);
339                            linkPositions.put(e.link, points);
340                        }
341                        pointCount += points.size();
342
343                        // No longer needed!
344                        e.link = null;
345                    }
346                }
347
348                for (LayoutEdge e : n.succs) {
349                    if (e.link != null) {
350                        ArrayList<Point> points = new ArrayList<>();
351                        Point p = new Point(e.from.x + e.relativeFrom, e.from.y + e.from.height - e.from.bottomYOffset + e.link.getFrom().getRelativePosition().y);
352                        points.add(p);
353                        if (e.from.outOffsets.containsKey(e.relativeFrom)) {
354                            points.add(new Point(p.x, p.y + e.from.outOffsets.get(e.relativeFrom) + e.link.getFrom().getRelativePosition().y));
355                        }
356
357                        LayoutNode cur = e.to;
358                        LayoutNode other = e.from;
359                        LayoutEdge curEdge = e;
360                        while (cur.vertex == null && !cur.succs.isEmpty()) {
361                            if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
362                                points.remove(points.size() - 1);
363                            }
364                            points.add(new Point(cur.x + cur.width / 2, cur.y));
365                            if (points.size() > 1 && points.get(points.size() - 1).x == cur.x + cur.width / 2 && points.get(points.size() - 2).x == cur.x + cur.width / 2) {
366                                points.remove(points.size() - 1);
367                            }
368                            points.add(new Point(cur.x + cur.width / 2, cur.y + cur.height));
369                            if (cur.succs.isEmpty()) {
370                                break;
371                            }
372                            assert cur.succs.size() == 1;
373                            curEdge = cur.succs.get(0);
374                            cur = curEdge.to;
375                        }
376
377                        p = new Point(cur.x + curEdge.relativeTo, cur.y + cur.yOffset + ((curEdge.link == null) ? 0 : curEdge.link.getTo().getRelativePosition().y));
378                        points.add(p);
379                        if (curEdge.to.inOffsets.containsKey(curEdge.relativeTo)) {
380                            points.add(new Point(p.x, p.y + curEdge.to.inOffsets.get(curEdge.relativeTo) + ((curEdge.link == null) ? 0 : curEdge.link.getTo().getRelativePosition().y)));
381                        }
382
383                        if (cur.succs.isEmpty() && cur.vertex == null) {
384                            if (reversedLinkStartPoints.containsKey(e.link)) {
385                                for (Point p1 : reversedLinkStartPoints.get(e.link)) {
386                                    points.add(0, new Point(p1.x + other.x, p1.y + other.y));
387                                }
388                            }
389
390                            if (splitEndPoints.containsKey(e.link)) {
391                                points.add(null);
392                                points.addAll(splitEndPoints.get(e.link));
393
394                                //checkPoints(points);
395                                if (reversedLinks.contains(e.link)) {
396                                    Collections.reverse(points);
397                                }
398                                assert !linkPositions.containsKey(e.link);
399                                linkPositions.put(e.link, points);
400                            } else {
401                                splitStartPoints.put(e.link, points);
402                            }
403                        } else {
404
405                            if (reversedLinkStartPoints.containsKey(e.link)) {
406                                for (Point p1 : reversedLinkStartPoints.get(e.link)) {
407                                    points.add(0, new Point(p1.x + other.x, p1.y + other.y));
408                                }
409                            }
410                            if (reversedLinkEndPoints.containsKey(e.link)) {
411                                for (Point p1 : reversedLinkEndPoints.get(e.link)) {
412                                    points.add(new Point(p1.x + cur.x, p1.y + cur.y));
413                                }
414                            }
415                            if (reversedLinks.contains(e.link)) {
416                                Collections.reverse(points);
417                            }
418                            //checkPoints(points);
419                            assert !linkPositions.containsKey(e.link);
420                            linkPositions.put(e.link, points);
421                        }
422
423                        pointCount += points.size();
424                        e.link = null;
425                    }
426                }
427            }
428
429            int minX = Integer.MAX_VALUE;
430            int minY = Integer.MAX_VALUE;
431            for (Vertex v : vertexPositions.keySet()) {
432                Point p = vertexPositions.get(v);
433                minX = Math.min(minX, p.x);
434                minY = Math.min(minY, p.y);
435            }
436
437            for (Link l : linkPositions.keySet()) {
438                List<Point> points = linkPositions.get(l);
439                for (Point p : points) {
440                    if (p != null) {
441                        minX = Math.min(minX, p.x);
442                        minY = Math.min(minY, p.y);
443                    }
444                }
445
446            }
447
448            for (Vertex v : vertexPositions.keySet()) {
449                Point p = vertexPositions.get(v);
450                p.x -= minX;
451                p.y -= minY;
452                v.setPosition(p);
453            }
454
455            for (Link l : linkPositions.keySet()) {
456                List<Point> points = linkPositions.get(l);
457                for (Point p : points) {
458                    if (p != null) {
459                        p.x -= minX;
460                        p.y -= minY;
461                    }
462                }
463                l.setControlPoints(points);
464
465            }
466        }
467
468        @Override
469        protected void printStatistics() {
470            System.out.println("Number of nodes: " + nodes.size());
471            int edgeCount = 0;
472            for (LayoutNode n : nodes) {
473                edgeCount += n.succs.size();
474            }
475            System.out.println("Number of edges: " + edgeCount);
476            System.out.println("Number of points: " + pointCount);
477        }
478    }
479
480    private static class Segment {
481
482        public float d;
483        public int orderNumber = -1;
484        public ArrayList<LayoutNode> nodes = new ArrayList<>();
485        public HashSet<Segment> succs = new HashSet<>();
486        public HashSet<Segment> preds = new HashSet<>();
487        public Region region;
488    }
489    private static final Comparator<Segment> segmentComparator = new Comparator<Segment>() {
490
491        @Override
492        public int compare(Segment s1, Segment s2) {
493            return s1.orderNumber - s2.orderNumber;
494        }
495    };
496
497    private static class Region {
498
499        public float d;
500        public int minOrderNumber;
501        public SortedSet<Segment> segments = new TreeSet<>(segmentComparator);
502        public HashSet<Region> succs = new HashSet<>(4);
503        public HashSet<Region> preds = new HashSet<>(4);
504    }
505    private static final Comparator<Region> regionComparator = new Comparator<Region>() {
506
507        @Override
508        public int compare(Region r1, Region r2) {
509            return r1.minOrderNumber - r2.minOrderNumber;
510        }
511    };
512    private static final Comparator<LayoutNode> nodePositionComparator = new Comparator<LayoutNode>() {
513
514        @Override
515        public int compare(LayoutNode n1, LayoutNode n2) {
516            return n1.pos - n2.pos;
517        }
518    };
519    private static final Comparator<LayoutNode> nodeProcessingDownComparator = new Comparator<LayoutNode>() {
520        @Override
521        public int compare(LayoutNode n1, LayoutNode n2) {
522            int n1VIP = 0;
523            for (LayoutEdge e : n1.preds) {
524                if (e.vip) {
525                    n1VIP++;
526                }
527            }
528            int n2VIP = 0;
529            for (LayoutEdge e : n2.preds) {
530                if (e.vip) {
531                    n2VIP++;
532                }
533            }
534            if (n1VIP != n2VIP) {
535                return n2VIP - n1VIP;
536            }
537            if (n1.vertex == null) {
538                if (n2.vertex == null) {
539                    return 0;
540                }
541                return -1;
542            }
543            if (n2.vertex == null) {
544                return 1;
545            }
546            return n1.preds.size() - n2.preds.size();
547        }
548    };
549    private static final Comparator<LayoutNode> nodeProcessingUpComparator = new Comparator<LayoutNode>() {
550
551        @Override
552        public int compare(LayoutNode n1, LayoutNode n2) {
553            int n1VIP = 0;
554            for (LayoutEdge e : n1.succs) {
555                if (e.vip) {
556                    n1VIP++;
557                }
558            }
559            int n2VIP = 0;
560            for (LayoutEdge e : n2.succs) {
561                if (e.vip) {
562                    n2VIP++;
563                }
564            }
565            if (n1VIP != n2VIP) {
566                return n2VIP - n1VIP;
567            }
568            if (n1.vertex == null) {
569                if (n2.vertex == null) {
570                    return 0;
571                }
572                return -1;
573            }
574            if (n2.vertex == null) {
575                return 1;
576            }
577            return n1.succs.size() - n2.succs.size();
578        }
579    };
580
581    private class AssignXCoordinates extends AlgorithmPart {
582
583        private ArrayList<Integer>[] space;
584        private ArrayList<LayoutNode>[] downProcessingOrder;
585        private ArrayList<LayoutNode>[] upProcessingOrder;
586
587        private void initialPositions() {
588            for (LayoutNode n : nodes) {
589                n.x = space[n.layer].get(n.pos);
590            }
591        }
592
593        @SuppressWarnings("unchecked")
594        private void createArrays() {
595            space = new ArrayList[layers.length];
596            downProcessingOrder = new ArrayList[layers.length];
597            upProcessingOrder = new ArrayList[layers.length];
598        }
599
600        @Override
601        protected void run() {
602            createArrays();
603
604            for (int i = 0; i < layers.length; i++) {
605                space[i] = new ArrayList<>();
606                downProcessingOrder[i] = new ArrayList<>();
607                upProcessingOrder[i] = new ArrayList<>();
608
609                int curX = 0;
610                for (LayoutNode n : layers[i]) {
611                    space[i].add(curX);
612                    curX += n.width + xOffset;
613                    downProcessingOrder[i].add(n);
614                    upProcessingOrder[i].add(n);
615                }
616
617                Collections.sort(downProcessingOrder[i], nodeProcessingDownComparator);
618                Collections.sort(upProcessingOrder[i], nodeProcessingUpComparator);
619            }
620
621            initialPositions();
622            for (int i = 0; i < SWEEP_ITERATIONS; i++) {
623                sweepDown();
624                adjustSpace();
625                sweepUp(false);
626                adjustSpace();
627            }
628
629            sweepDown();
630            adjustSpace();
631            sweepUp(true);
632        }
633
634        private void adjustSpace() {
635            for (int i = 0; i < layers.length; i++) {
636                //               space[i] = new ArrayList<>();
637                int curX = 0;
638                for (LayoutNode n : layers[i]) {
639                    space[i].add(n.x);
640//                    curX += n.width + xOffset;
641                }
642            }
643        }
644
645        private int calculateOptimalDown(LayoutNode n) {
646            int size = n.preds.size();
647            if (size == 0) {
648                return n.x;
649            }
650            int vipCount = 0;
651            for (LayoutEdge e : n.preds) {
652                if (e.vip) {
653                    vipCount++;
654                }
655            }
656
657            if (vipCount == 0) {
658                int[] values = new int[size];
659                for (int i = 0; i < size; i++) {
660                    LayoutEdge e = n.preds.get(i);
661                    values[i] = e.from.x + e.relativeFrom - e.relativeTo;
662                }
663                return median(values);
664            } else {
665                int z = 0;
666                int[] values = new int[vipCount];
667                for (int i = 0; i < size; i++) {
668                    LayoutEdge e = n.preds.get(i);
669                    if (e.vip) {
670                        values[z++] = e.from.x + e.relativeFrom - e.relativeTo;
671                    }
672                }
673                return median(values);
674            }
675        }
676
677        private int calculateOptimalBoth(LayoutNode n) {
678            if (n.preds.size() == n.succs.size()) {
679                return n.x;
680            }
681
682            int[] values = new int[n.preds.size() + n.succs.size()];
683            int i = 0;
684
685            for (LayoutEdge e : n.preds) {
686                values[i] = e.from.x + e.relativeFrom - e.relativeTo;
687                i++;
688            }
689
690            for (LayoutEdge e : n.succs) {
691                values[i] = e.to.x + e.relativeTo - e.relativeFrom;
692                i++;
693            }
694
695            return median(values);
696        }
697
698        private int calculateOptimalUp(LayoutNode n) {
699            int size = n.succs.size();
700            if (size == 0) {
701                return n.x;
702            }
703            int[] values = new int[size];
704            for (int i = 0; i < size; i++) {
705                LayoutEdge e = n.succs.get(i);
706                values[i] = e.to.x + e.relativeTo - e.relativeFrom;
707                if (e.vip) {
708                    return values[i];
709                }
710            }
711            return median(values);
712        }
713
714        private int median(int[] values) {
715            Arrays.sort(values);
716            if (values.length % 2 == 0) {
717                return (values[values.length / 2 - 1] + values[values.length / 2]) / 2;
718            } else {
719                return values[values.length / 2];
720            }
721        }
722
723        private void sweepUp(boolean onlyDummies) {
724            for (int i = layers.length - 1; i >= 0; i--) {
725                NodeRow r = new NodeRow(space[i]);
726                for (LayoutNode n : upProcessingOrder[i]) {
727                    int optimal = calculateOptimalUp(n);
728                    r.insert(n, optimal);
729                }
730            }
731        }
732
733        private void doubleSweep() {
734            for (int i = layers.length - 2; i >= 0; i--) {
735                NodeRow r = new NodeRow(space[i]);
736                for (LayoutNode n : upProcessingOrder[i]) {
737                    int optimal = calculateOptimalBoth(n);
738                    r.insert(n, optimal);
739                }
740            }
741        }
742
743        private void sweepDown() {
744            for (int i = 1; i < layers.length; i++) {
745                NodeRow r = new NodeRow(space[i]);
746                for (LayoutNode n : downProcessingOrder[i]) {
747                    int optimal = calculateOptimalDown(n);
748                    r.insert(n, optimal);
749                }
750            }
751        }
752    }
753
754    private static class NodeRow {
755
756        private TreeSet<LayoutNode> treeSet;
757        private ArrayList<Integer> space;
758
759        public NodeRow(ArrayList<Integer> space) {
760            treeSet = new TreeSet<>(nodePositionComparator);
761            this.space = space;
762        }
763
764        public int offset(LayoutNode n1, LayoutNode n2) {
765            int v1 = space.get(n1.pos) + n1.width;
766            int v2 = space.get(n2.pos);
767            return v2 - v1;
768        }
769
770        public void insert(LayoutNode n, int pos) {
771
772            SortedSet<LayoutNode> headSet = treeSet.headSet(n);
773
774            LayoutNode leftNeighbor = null;
775            int minX = Integer.MIN_VALUE;
776            if (!headSet.isEmpty()) {
777                leftNeighbor = headSet.last();
778                minX = leftNeighbor.x + leftNeighbor.width + offset(leftNeighbor, n);
779            }
780
781            if (pos < minX) {
782                n.x = minX;
783            } else {
784
785                LayoutNode rightNeighbor = null;
786                SortedSet<LayoutNode> tailSet = treeSet.tailSet(n);
787                int maxX = Integer.MAX_VALUE;
788                if (!tailSet.isEmpty()) {
789                    rightNeighbor = tailSet.first();
790                    maxX = rightNeighbor.x - offset(n, rightNeighbor) - n.width;
791                }
792
793                if (pos > maxX) {
794                    n.x = maxX;
795                } else {
796                    n.x = pos;
797                }
798
799                assert minX <= maxX : minX + " vs " + maxX;
800            }
801
802            treeSet.add(n);
803        }
804    }
805    private static Comparator<LayoutNode> crossingNodeComparator = new Comparator<LayoutNode>() {
806
807        @Override
808        public int compare(LayoutNode n1, LayoutNode n2) {
809            return n1.crossingNumber - n2.crossingNumber;
810        }
811    };
812
813    private class CrossingReduction extends AlgorithmPart {
814
815        @Override
816        public void preCheck() {
817            for (LayoutNode n : nodes) {
818                assert n.layer < layerCount;
819            }
820        }
821
822        @SuppressWarnings("unchecked")
823        private void createLayers() {
824            layers = new List[layerCount];
825
826            for (int i = 0; i < layerCount; i++) {
827                layers[i] = new ArrayList<>();
828            }
829        }
830
831        @Override
832        protected void run() {
833            createLayers();
834
835            // Generate initial ordering
836            HashSet<LayoutNode> visited = new HashSet<>();
837            for (LayoutNode n : nodes) {
838                if (n.layer == 0) {
839                    layers[0].add(n);
840                    visited.add(n);
841                } else if (n.preds.isEmpty()) {
842                    layers[n.layer].add(n);
843                    visited.add(n);
844                }
845            }
846
847            for (int i = 0; i < layers.length - 1; i++) {
848                for (LayoutNode n : layers[i]) {
849                    for (LayoutEdge e : n.succs) {
850                        if (!visited.contains(e.to)) {
851                            visited.add(e.to);
852                            layers[i + 1].add(e.to);
853                        }
854                    }
855                }
856            }
857
858            updatePositions();
859
860            initX();
861
862            // Optimize
863            for (int i = 0; i < CROSSING_ITERATIONS; i++) {
864                downSweep();
865                upSweep();
866            }
867            downSweep();
868        }
869
870        private void initX() {
871
872            for (int i = 0; i < layers.length; i++) {
873                updateXOfLayer(i);
874            }
875        }
876
877        private void updateXOfLayer(int index) {
878            int x = 0;
879
880            for (LayoutNode n : layers[index]) {
881                n.x = x;
882                x += n.width + X_OFFSET;
883            }
884        }
885
886        private void updatePositions() {
887
888            for (int i = 0; i < layers.length; i++) {
889                int z = 0;
890                for (LayoutNode n : layers[i]) {
891                    n.pos = z;
892                    z++;
893                }
894            }
895        }
896
897        private void downSweep() {
898
899            // Downsweep
900            for (int i = 1; i < layerCount; i++) {
901
902                for (LayoutNode n : layers[i]) {
903                    n.crossingNumber = 0;
904                }
905
906                for (LayoutNode n : layers[i]) {
907
908                    int sum = 0;
909                    int count = 0;
910                    for (LayoutEdge e : n.preds) {
911                        int cur = e.from.x + e.relativeFrom;
912                        int factor = 1;
913                        if (e.vip) {
914                            factor = VIP_BONUS;
915                        }
916                        sum += cur * factor;
917                        count += factor;
918                    }
919
920                    if (count > 0) {
921                        sum /= count;
922                        n.crossingNumber = sum;
923                    }
924                }
925
926                updateCrossingNumbers(i, true);
927                Collections.sort(layers[i], crossingNodeComparator);
928                updateXOfLayer(i);
929
930                int z = 0;
931                for (LayoutNode n : layers[i]) {
932                    n.pos = z;
933                    z++;
934                }
935            }
936        }
937
938        private void updateCrossingNumbers(int index, boolean down) {
939            for (int i = 0; i < layers[index].size(); i++) {
940                LayoutNode n = layers[index].get(i);
941                LayoutNode prev = null;
942                if (i > 0) {
943                    prev = layers[index].get(i - 1);
944                }
945                LayoutNode next = null;
946                if (i < layers[index].size() - 1) {
947                    next = layers[index].get(i + 1);
948                }
949
950                boolean cond = n.succs.isEmpty();
951                if (down) {
952                    cond = n.preds.isEmpty();
953                }
954
955                if (cond) {
956
957                    if (prev != null && next != null) {
958                        n.crossingNumber = (prev.crossingNumber + next.crossingNumber) / 2;
959                    } else if (prev != null) {
960                        n.crossingNumber = prev.crossingNumber;
961                    } else if (next != null) {
962                        n.crossingNumber = next.crossingNumber;
963                    }
964                }
965            }
966        }
967
968        private void upSweep() {
969            // Upsweep
970            for (int i = layerCount - 2; i >= 0; i--) {
971
972                for (LayoutNode n : layers[i]) {
973                    n.crossingNumber = 0;
974                }
975
976                for (LayoutNode n : layers[i]) {
977
978                    int count = 0;
979                    int sum = 0;
980                    for (LayoutEdge e : n.succs) {
981                        int cur = e.to.x + e.relativeTo;
982                        int factor = 1;
983                        if (e.vip) {
984                            factor = VIP_BONUS;
985                        }
986                        sum += cur * factor;
987                        count += factor;
988                    }
989
990                    if (count > 0) {
991                        sum /= count;
992                        n.crossingNumber = sum;
993                    }
994
995                }
996
997                updateCrossingNumbers(i, false);
998                Collections.sort(layers[i], crossingNodeComparator);
999                updateXOfLayer(i);
1000
1001                int z = 0;
1002                for (LayoutNode n : layers[i]) {
1003                    n.pos = z;
1004                    z++;
1005                }
1006            }
1007        }
1008
1009        @Override
1010        public void postCheck() {
1011
1012            HashSet<LayoutNode> visited = new HashSet<>();
1013            for (int i = 0; i < layers.length; i++) {
1014                for (LayoutNode n : layers[i]) {
1015                    assert !visited.contains(n);
1016                    assert n.layer == i;
1017                    visited.add(n);
1018                }
1019            }
1020
1021        }
1022    }
1023
1024    private class AssignYCoordinates extends AlgorithmPart {
1025
1026        @Override
1027        protected void run() {
1028            int curY = 0;
1029
1030            for (int i = 0; i < layers.length; i++) {
1031                int maxHeight = 0;
1032                int baseLine = 0;
1033                int bottomBaseLine = 0;
1034                for (LayoutNode n : layers[i]) {
1035                    maxHeight = Math.max(maxHeight, n.height - n.yOffset - n.bottomYOffset);
1036                    baseLine = Math.max(baseLine, n.yOffset);
1037                    bottomBaseLine = Math.max(bottomBaseLine, n.bottomYOffset);
1038                }
1039
1040                int maxXOffset = 0;
1041                for (LayoutNode n : layers[i]) {
1042                    if (n.vertex == null) {
1043                        // Dummy node
1044                        n.y = curY;
1045                        n.height = maxHeight + baseLine + bottomBaseLine;
1046
1047                    } else {
1048                        n.y = curY + baseLine + (maxHeight - (n.height - n.yOffset - n.bottomYOffset)) / 2 - n.yOffset;
1049                    }
1050
1051                    for (LayoutEdge e : n.succs) {
1052                        int curXOffset = Math.abs(n.x - e.to.x);
1053                        maxXOffset = Math.max(curXOffset, maxXOffset);
1054                    }
1055                }
1056
1057                curY += maxHeight + baseLine + bottomBaseLine;
1058                curY += layerOffset + ((int) (Math.sqrt(maxXOffset) * 1.5));
1059            }
1060        }
1061    }
1062
1063    private class CreateDummyNodes extends AlgorithmPart {
1064
1065        private int oldNodeCount;
1066
1067        @Override
1068        protected void preCheck() {
1069            for (LayoutNode n : nodes) {
1070                for (LayoutEdge e : n.succs) {
1071                    assert e.from != null;
1072                    assert e.from == n;
1073                    assert e.from.layer < e.to.layer;
1074                }
1075
1076                for (LayoutEdge e : n.preds) {
1077                    assert e.to != null;
1078                    assert e.to == n;
1079                }
1080            }
1081        }
1082
1083        @Override
1084        protected void run() {
1085            oldNodeCount = nodes.size();
1086
1087            if (combine == Combine.SAME_OUTPUTS) {
1088
1089                Comparator<LayoutEdge> comparator = new Comparator<LayoutEdge>() {
1090
1091                    @Override
1092                    public int compare(LayoutEdge e1, LayoutEdge e2) {
1093                        return e1.to.layer - e2.to.layer;
1094                    }
1095                };
1096                HashMap<Integer, List<LayoutEdge>> portHash = new HashMap<>();
1097                ArrayList<LayoutNode> currentNodes = new ArrayList<>(nodes);
1098                for (LayoutNode n : currentNodes) {
1099                    portHash.clear();
1100
1101                    ArrayList<LayoutEdge> succs = new ArrayList<>(n.succs);
1102                    HashMap<Integer, LayoutNode> topNodeHash = new HashMap<>();
1103                    HashMap<Integer, HashMap<Integer, LayoutNode>> bottomNodeHash = new HashMap<>();
1104                    for (LayoutEdge e : succs) {
1105                        assert e.from.layer < e.to.layer;
1106                        if (e.from.layer != e.to.layer - 1) {
1107                            if (maxLayerLength != -1 && e.to.layer - e.from.layer > maxLayerLength/* && e.to.preds.size() > 1 && e.from.succs.size() > 1*/) {
1108                                assert maxLayerLength > 2;
1109                                e.to.preds.remove(e);
1110                                e.from.succs.remove(e);
1111
1112                                LayoutEdge topEdge = null;
1113
1114                                if (combine == Combine.SAME_OUTPUTS && topNodeHash.containsKey(e.relativeFrom)) {
1115                                    LayoutNode topNode = topNodeHash.get(e.relativeFrom);
1116                                    topEdge = new LayoutEdge();
1117                                    topEdge.relativeFrom = e.relativeFrom;
1118                                    topEdge.from = e.from;
1119                                    topEdge.relativeTo = topNode.width / 2;
1120                                    topEdge.to = topNode;
1121                                    topEdge.link = e.link;
1122                                    topEdge.vip = e.vip;
1123                                    e.from.succs.add(topEdge);
1124                                    topNode.preds.add(topEdge);
1125                                } else {
1126
1127                                    LayoutNode topNode = new LayoutNode();
1128                                    topNode.layer = e.from.layer + 1;
1129                                    topNode.width = DUMMY_WIDTH;
1130                                    topNode.height = DUMMY_HEIGHT;
1131                                    nodes.add(topNode);
1132                                    topEdge = new LayoutEdge();
1133                                    topEdge.relativeFrom = e.relativeFrom;
1134                                    topEdge.from = e.from;
1135                                    topEdge.relativeTo = topNode.width / 2;
1136                                    topEdge.to = topNode;
1137                                    topEdge.link = e.link;
1138                                    topEdge.vip = e.vip;
1139                                    e.from.succs.add(topEdge);
1140                                    topNode.preds.add(topEdge);
1141                                    topNodeHash.put(e.relativeFrom, topNode);
1142                                    bottomNodeHash.put(e.relativeFrom, new HashMap<Integer, LayoutNode>());
1143                                }
1144
1145                                HashMap<Integer, LayoutNode> hash = bottomNodeHash.get(e.relativeFrom);
1146
1147                                LayoutNode bottomNode = null;
1148                                if (hash.containsKey(e.to.layer)) {
1149                                    bottomNode = hash.get(e.to.layer);
1150                                } else {
1151
1152                                    bottomNode = new LayoutNode();
1153                                    bottomNode.layer = e.to.layer - 1;
1154                                    bottomNode.width = DUMMY_WIDTH;
1155                                    bottomNode.height = DUMMY_HEIGHT;
1156                                    nodes.add(bottomNode);
1157                                    hash.put(e.to.layer, bottomNode);
1158                                }
1159
1160                                LayoutEdge bottomEdge = new LayoutEdge();
1161                                bottomEdge.relativeTo = e.relativeTo;
1162                                bottomEdge.to = e.to;
1163                                bottomEdge.relativeFrom = bottomNode.width / 2;
1164                                bottomEdge.from = bottomNode;
1165                                bottomEdge.link = e.link;
1166                                bottomEdge.vip = e.vip;
1167                                e.to.preds.add(bottomEdge);
1168                                bottomEdgeHash.put(topEdge, bottomEdge);
1169                                bottomNode.succs.add(bottomEdge);
1170
1171                            } else {
1172                                Integer i = e.relativeFrom;
1173                                if (!portHash.containsKey(i)) {
1174                                    portHash.put(i, new ArrayList<LayoutEdge>());
1175                                }
1176                                portHash.get(i).add(e);
1177                            }
1178                        }
1179                    }
1180
1181                    succs = new ArrayList<>(n.succs);
1182                    for (LayoutEdge e : succs) {
1183
1184                        Integer i = e.relativeFrom;
1185                        if (portHash.containsKey(i)) {
1186
1187                            List<LayoutEdge> list = portHash.get(i);
1188                            Collections.sort(list, comparator);
1189
1190                            if (list.size() == 1) {
1191                                processSingleEdge(list.get(0));
1192                            } else {
1193
1194                                int maxLayer = list.get(0).to.layer;
1195                                for (LayoutEdge curEdge : list) {
1196                                    maxLayer = Math.max(maxLayer, curEdge.to.layer);
1197                                }
1198
1199                                int cnt = maxLayer - n.layer - 1;
1200                                LayoutEdge[] edges = new LayoutEdge[cnt];
1201                                LayoutNode[] nodes = new LayoutNode[cnt];
1202                                edges[0] = new LayoutEdge();
1203                                edges[0].from = n;
1204                                edges[0].relativeFrom = i;
1205                                edges[0].vip = e.vip;
1206                                n.succs.add(edges[0]);
1207
1208                                nodes[0] = new LayoutNode();
1209                                nodes[0].width = dummyWidth;
1210                                nodes[0].height = dummyHeight;
1211                                nodes[0].layer = n.layer + 1;
1212                                nodes[0].preds.add(edges[0]);
1213                                edges[0].to = nodes[0];
1214                                edges[0].relativeTo = nodes[0].width / 2;
1215                                for (int j = 1; j < cnt; j++) {
1216                                    edges[j] = new LayoutEdge();
1217                                    edges[j].vip = e.vip;
1218                                    edges[j].from = nodes[j - 1];
1219                                    edges[j].relativeFrom = nodes[j - 1].width / 2;
1220                                    nodes[j - 1].succs.add(edges[j]);
1221                                    nodes[j] = new LayoutNode();
1222                                    nodes[j].width = dummyWidth;
1223                                    nodes[j].height = dummyHeight;
1224                                    nodes[j].layer = n.layer + j + 1;
1225                                    nodes[j].preds.add(edges[j]);
1226                                    edges[j].to = nodes[j];
1227                                    edges[j].relativeTo = nodes[j].width / 2;
1228                                }
1229
1230                                for (LayoutEdge curEdge : list) {
1231                                    assert curEdge.to.layer - n.layer - 2 >= 0;
1232                                    assert curEdge.to.layer - n.layer - 2 < cnt;
1233                                    LayoutNode anchor = nodes[curEdge.to.layer - n.layer - 2];
1234                                    anchor.succs.add(curEdge);
1235                                    curEdge.from = anchor;
1236                                    curEdge.relativeFrom = anchor.width / 2;
1237                                    n.succs.remove(curEdge);
1238                                }
1239
1240                            }
1241
1242                            portHash.remove(i);
1243                        }
1244                    }
1245                }
1246            } else if (combine == Combine.SAME_INPUTS) {
1247                throw new UnsupportedOperationException("Currently not supported");
1248            } else {
1249                ArrayList<LayoutNode> currentNodes = new ArrayList<>(nodes);
1250                for (LayoutNode n : currentNodes) {
1251                    for (LayoutEdge e : n.succs) {
1252                        processSingleEdge(e);
1253                    }
1254                }
1255            }
1256        }
1257
1258        private void processSingleEdge(LayoutEdge e) {
1259            LayoutNode n = e.from;
1260            if (e.to.layer > n.layer + 1) {
1261                LayoutEdge last = e;
1262                for (int i = n.layer + 1; i < last.to.layer; i++) {
1263                    last = addBetween(last, i);
1264                }
1265            }
1266        }
1267
1268        private LayoutEdge addBetween(LayoutEdge e, int layer) {
1269            LayoutNode n = new LayoutNode();
1270            n.width = dummyWidth;
1271            n.height = dummyHeight;
1272            n.layer = layer;
1273            n.preds.add(e);
1274            nodes.add(n);
1275            LayoutEdge result = new LayoutEdge();
1276            result.vip = e.vip;
1277            n.succs.add(result);
1278            result.from = n;
1279            result.relativeFrom = n.width / 2;
1280            result.to = e.to;
1281            result.relativeTo = e.relativeTo;
1282            e.relativeTo = n.width / 2;
1283            e.to.preds.remove(e);
1284            e.to.preds.add(result);
1285            e.to = n;
1286            return result;
1287        }
1288
1289        @Override
1290        public void printStatistics() {
1291            System.out.println("Dummy nodes created: " + (nodes.size() - oldNodeCount));
1292        }
1293
1294        @Override
1295        public void postCheck() {
1296            ArrayList<LayoutNode> currentNodes = new ArrayList<>(nodes);
1297            for (LayoutNode n : currentNodes) {
1298                for (LayoutEdge e : n.succs) {
1299                    assert e.from.layer == e.to.layer - 1;
1300                }
1301            }
1302
1303            for (int i = 0; i < layers.length; i++) {
1304                assert layers[i].size() > 0;
1305                for (LayoutNode n : layers[i]) {
1306                    assert n.layer == i;
1307                }
1308            }
1309        }
1310    }
1311
1312    private class AssignLayers extends AlgorithmPart {
1313
1314        @Override
1315        public void preCheck() {
1316            for (LayoutNode n : nodes) {
1317                assert n.layer == -1;
1318            }
1319        }
1320
1321        @Override
1322        protected void run() {
1323            assignLayerDownwards();
1324            assignLayerUpwards();
1325        }
1326
1327        private void assignLayerDownwards() {
1328            ArrayList<LayoutNode> hull = new ArrayList<>();
1329            for (LayoutNode n : nodes) {
1330                if (n.preds.isEmpty()) {
1331                    hull.add(n);
1332                    n.layer = 0;
1333                }
1334            }
1335
1336            int z = minLayerDifference;
1337            while (!hull.isEmpty()) {
1338                ArrayList<LayoutNode> newSet = new ArrayList<>();
1339                for (LayoutNode n : hull) {
1340                    for (LayoutEdge se : n.succs) {
1341                        LayoutNode s = se.to;
1342                        if (s.layer != -1) {
1343                            // This node was assigned before.
1344                        } else {
1345                            boolean unassignedPred = false;
1346                            for (LayoutEdge pe : s.preds) {
1347                                LayoutNode p = pe.from;
1348                                if (p.layer == -1 || p.layer >= z) {
1349                                    // This now has an unscheduled successor or a successor that was scheduled only in this round.
1350                                    unassignedPred = true;
1351                                    break;
1352                                }
1353                            }
1354
1355                            if (unassignedPred) {
1356                                // This successor node can not yet be assigned.
1357                            } else {
1358                                s.layer = z;
1359                                newSet.add(s);
1360                            }
1361                        }
1362                    }
1363                }
1364
1365                hull = newSet;
1366                z += minLayerDifference;
1367            }
1368
1369            layerCount = z - minLayerDifference;
1370            for (LayoutNode n : nodes) {
1371                n.layer = (layerCount - 1 - n.layer);
1372            }
1373        }
1374
1375        private void assignLayerUpwards() {
1376            ArrayList<LayoutNode> hull = new ArrayList<>();
1377            for (LayoutNode n : nodes) {
1378                if (n.succs.isEmpty()) {
1379                    hull.add(n);
1380                } else {
1381                    n.layer = -1;
1382                }
1383            }
1384
1385            int z = minLayerDifference;
1386            while (!hull.isEmpty()) {
1387                ArrayList<LayoutNode> newSet = new ArrayList<>();
1388                for (LayoutNode n : hull) {
1389                    if (n.layer < z) {
1390                        for (LayoutEdge se : n.preds) {
1391                            LayoutNode s = se.from;
1392                            if (s.layer != -1) {
1393                                // This node was assigned before.
1394                            } else {
1395                                boolean unassignedSucc = false;
1396                                for (LayoutEdge pe : s.succs) {
1397                                    LayoutNode p = pe.to;
1398                                    if (p.layer == -1 || p.layer >= z) {
1399                                        // This now has an unscheduled successor or a successor that was scheduled only in this round.
1400                                        unassignedSucc = true;
1401                                        break;
1402                                    }
1403                                }
1404
1405                                if (unassignedSucc) {
1406                                    // This predecessor node can not yet be assigned.
1407                                } else {
1408                                    s.layer = z;
1409                                    newSet.add(s);
1410                                }
1411                            }
1412                        }
1413                    } else {
1414                        newSet.add(n);
1415                    }
1416                }
1417
1418                hull = newSet;
1419                z += minLayerDifference;
1420            }
1421
1422            layerCount = z - minLayerDifference;
1423
1424            for (LayoutNode n : nodes) {
1425                n.layer = (layerCount - 1 - n.layer);
1426            }
1427        }
1428
1429        @Override
1430        public void postCheck() {
1431            for (LayoutNode n : nodes) {
1432                assert n.layer >= 0;
1433                assert n.layer < layerCount;
1434                for (LayoutEdge e : n.succs) {
1435                    assert e.from.layer < e.to.layer;
1436                }
1437            }
1438        }
1439    }
1440
1441    private class ReverseEdges extends AlgorithmPart {
1442
1443        private HashSet<LayoutNode> visited;
1444        private HashSet<LayoutNode> active;
1445
1446        @Override
1447        protected void run() {
1448
1449            // Remove self-edges
1450            for (LayoutNode node : nodes) {
1451                ArrayList<LayoutEdge> succs = new ArrayList<>(node.succs);
1452                for (LayoutEdge e : succs) {
1453                    assert e.from == node;
1454                    if (e.to == node) {
1455                        node.succs.remove(e);
1456                        node.preds.remove(e);
1457                    }
1458                }
1459            }
1460
1461            // Reverse inputs of roots
1462            for (LayoutNode node : nodes) {
1463                if (node.vertex.isRoot()) {
1464                    boolean ok = true;
1465                    for (LayoutEdge e : node.preds) {
1466                        if (e.from.vertex.isRoot()) {
1467                            ok = false;
1468                            break;
1469                        }
1470                    }
1471                    if (ok) {
1472                        reverseAllInputs(node);
1473                    }
1474                }
1475            }
1476
1477            // Start DFS and reverse back edges
1478            visited = new HashSet<>();
1479            active = new HashSet<>();
1480            for (LayoutNode node : nodes) {
1481                DFS(node);
1482            }
1483
1484            for (LayoutNode node : nodes) {
1485
1486                SortedSet<Integer> reversedDown = new TreeSet<>();
1487
1488                for (LayoutEdge e : node.succs) {
1489                    if (reversedLinks.contains(e.link)) {
1490                        reversedDown.add(e.relativeFrom);
1491                    }
1492                }
1493
1494                SortedSet<Integer> reversedUp = null;
1495                if (reversedDown.size() == 0) {
1496                    reversedUp = new TreeSet<>(Collections.reverseOrder());
1497                } else {
1498                    reversedUp = new TreeSet<>();
1499                }
1500
1501                for (LayoutEdge e : node.preds) {
1502                    if (reversedLinks.contains(e.link)) {
1503                        reversedUp.add(e.relativeTo);
1504                    }
1505                }
1506
1507                final int offset = X_OFFSET + DUMMY_WIDTH;
1508
1509                int curX = 0;
1510                int curWidth = node.width + reversedDown.size() * offset;
1511                for (int pos : reversedDown) {
1512                    ArrayList<LayoutEdge> reversedSuccs = new ArrayList<>();
1513                    for (LayoutEdge e : node.succs) {
1514                        if (e.relativeFrom == pos && reversedLinks.contains(e.link)) {
1515                            reversedSuccs.add(e);
1516                            e.relativeFrom = curWidth;
1517                        }
1518                    }
1519
1520                    ArrayList<Point> startPoints = new ArrayList<>();
1521                    startPoints.add(new Point(curWidth, curX));
1522                    startPoints.add(new Point(pos, curX));
1523                    startPoints.add(new Point(pos, reversedDown.size() * offset));
1524                    for (LayoutEdge e : reversedSuccs) {
1525                        reversedLinkStartPoints.put(e.link, startPoints);
1526                    }
1527
1528                    node.inOffsets.put(pos, -curX);
1529                    curX += offset;
1530                    node.height += offset;
1531                    node.yOffset += offset;
1532                    curWidth -= offset;
1533                }
1534                node.width += reversedDown.size() * offset;
1535
1536                if (reversedDown.size() == 0) {
1537                    curX = offset;
1538                } else {
1539                    curX = -offset;
1540                }
1541
1542                curX = 0;
1543                int minX = 0;
1544                if (reversedDown.size() != 0) {
1545                    minX = -offset * reversedUp.size();
1546                }
1547
1548                int oldNodeHeight = node.height;
1549                for (int pos : reversedUp) {
1550                    ArrayList<LayoutEdge> reversedPreds = new ArrayList<>();
1551                    for (LayoutEdge e : node.preds) {
1552                        if (e.relativeTo == pos && reversedLinks.contains(e.link)) {
1553                            if (reversedDown.size() == 0) {
1554                                e.relativeTo = node.width + offset;
1555                            } else {
1556                                e.relativeTo = curX - offset;
1557                            }
1558
1559                            reversedPreds.add(e);
1560                        }
1561                    }
1562                    node.height += offset;
1563                    ArrayList<Point> endPoints = new ArrayList<>();
1564
1565                    if (reversedDown.size() == 0) {
1566
1567                        curX += offset;
1568                        node.width += offset;
1569                        endPoints.add(new Point(node.width, node.height));
1570
1571                    } else {
1572                        curX -= offset;
1573                        node.width += offset;
1574                        endPoints.add(new Point(curX, node.height));
1575                    }
1576
1577                    node.outOffsets.put(pos - minX, curX);
1578                    curX += offset;
1579                    node.bottomYOffset += offset;
1580
1581                    endPoints.add(new Point(pos, node.height));
1582                    endPoints.add(new Point(pos, oldNodeHeight));
1583                    for (LayoutEdge e : reversedPreds) {
1584                        reversedLinkEndPoints.put(e.link, endPoints);
1585                    }
1586                }
1587
1588                if (minX < 0) {
1589                    for (LayoutEdge e : node.preds) {
1590                        e.relativeTo -= minX;
1591                    }
1592
1593                    for (LayoutEdge e : node.succs) {
1594                        e.relativeFrom -= minX;
1595                    }
1596
1597                    node.xOffset = -minX;
1598                    node.width += -minX;
1599                }
1600            }
1601
1602        }
1603
1604        private void DFS(LayoutNode startNode) {
1605            if (visited.contains(startNode)) {
1606                return;
1607            }
1608
1609            Stack<LayoutNode> stack = new Stack<>();
1610            stack.push(startNode);
1611
1612            while (!stack.empty()) {
1613                LayoutNode node = stack.pop();
1614
1615                if (visited.contains(node)) {
1616                    // Node no longer active
1617                    active.remove(node);
1618                    continue;
1619                }
1620
1621                // Repush immediately to know when no longer active
1622                stack.push(node);
1623                visited.add(node);
1624                active.add(node);
1625
1626                ArrayList<LayoutEdge> succs = new ArrayList<>(node.succs);
1627                for (LayoutEdge e : succs) {
1628                    if (active.contains(e.to)) {
1629                        assert visited.contains(e.to);
1630                        // Encountered back edge
1631                        reverseEdge(e);
1632                    } else if (!visited.contains(e.to) && (linksToFollow.size() == 0 || linksToFollow.contains(e.link))) {
1633                        stack.push(e.to);
1634                    }
1635                }
1636            }
1637        }
1638
1639        private void reverseAllInputs(LayoutNode node) {
1640            for (LayoutEdge e : node.preds) {
1641                assert !reversedLinks.contains(e.link);
1642                reversedLinks.add(e.link);
1643                node.succs.add(e);
1644                e.from.preds.add(e);
1645                e.from.succs.remove(e);
1646                int oldRelativeFrom = e.relativeFrom;
1647                int oldRelativeTo = e.relativeTo;
1648                e.to = e.from;
1649                e.from = node;
1650                e.relativeFrom = oldRelativeTo;
1651                e.relativeTo = oldRelativeFrom;
1652            }
1653            node.preds.clear();
1654        }
1655
1656        private void reverseEdge(LayoutEdge e) {
1657            assert !reversedLinks.contains(e.link);
1658            reversedLinks.add(e.link);
1659
1660            LayoutNode oldFrom = e.from;
1661            LayoutNode oldTo = e.to;
1662            int oldRelativeFrom = e.relativeFrom;
1663            int oldRelativeTo = e.relativeTo;
1664
1665            e.from = oldTo;
1666            e.to = oldFrom;
1667            e.relativeFrom = oldRelativeTo;
1668            e.relativeTo = oldRelativeFrom;
1669
1670            oldFrom.succs.remove(e);
1671            oldFrom.preds.add(e);
1672            oldTo.preds.remove(e);
1673            oldTo.succs.add(e);
1674        }
1675
1676        @Override
1677        public void postCheck() {
1678
1679            for (LayoutNode n : nodes) {
1680
1681                HashSet<LayoutNode> curVisited = new HashSet<>();
1682                Queue<LayoutNode> queue = new LinkedList<>();
1683                for (LayoutEdge e : n.succs) {
1684                    LayoutNode s = e.to;
1685                    queue.add(s);
1686                    curVisited.add(s);
1687                }
1688
1689                while (!queue.isEmpty()) {
1690                    LayoutNode curNode = queue.remove();
1691
1692                    for (LayoutEdge e : curNode.succs) {
1693                        assert e.to != n;
1694                        if (!curVisited.contains(e.to)) {
1695                            queue.add(e.to);
1696                            curVisited.add(e.to);
1697                        }
1698                    }
1699                }
1700            }
1701        }
1702    }
1703    private Comparator<Link> linkComparator = new Comparator<Link>() {
1704
1705        @Override
1706        public int compare(Link l1, Link l2) {
1707            if (l1.isVIP() && !l2.isVIP()) {
1708                return -1;
1709            }
1710
1711            if (!l1.isVIP() && l2.isVIP()) {
1712                return 1;
1713            }
1714
1715            int result = l1.getFrom().getVertex().compareTo(l2.getFrom().getVertex());
1716            if (result != 0) {
1717                return result;
1718            }
1719            result = l1.getTo().getVertex().compareTo(l2.getTo().getVertex());
1720            if (result != 0) {
1721                return result;
1722            }
1723            result = l1.getFrom().getRelativePosition().x - l2.getFrom().getRelativePosition().x;
1724            if (result != 0) {
1725                return result;
1726            }
1727            result = l1.getTo().getRelativePosition().x - l2.getTo().getRelativePosition().x;
1728            return result;
1729        }
1730    };
1731
1732    private class BuildDatastructure extends AlgorithmPart {
1733
1734        @Override
1735        protected void run() {
1736            // Set up nodes
1737            List<Vertex> vertices = new ArrayList<>(graph.getVertices());
1738            Collections.sort(vertices);
1739
1740            for (Vertex v : vertices) {
1741                LayoutNode node = new LayoutNode();
1742                Dimension size = v.getSize();
1743                node.width = (int) size.getWidth();
1744                node.height = (int) size.getHeight();
1745                node.vertex = v;
1746                nodes.add(node);
1747                vertexToLayoutNode.put(v, node);
1748            }
1749
1750            // Set up edges
1751            List<Link> links = new ArrayList<>(graph.getLinks());
1752            Collections.sort(links, linkComparator);
1753            for (Link l : links) {
1754                LayoutEdge edge = new LayoutEdge();
1755                assert vertexToLayoutNode.containsKey(l.getFrom().getVertex());
1756                assert vertexToLayoutNode.containsKey(l.getTo().getVertex());
1757                edge.from = vertexToLayoutNode.get(l.getFrom().getVertex());
1758                edge.to = vertexToLayoutNode.get(l.getTo().getVertex());
1759                edge.relativeFrom = l.getFrom().getRelativePosition().x;
1760                edge.relativeTo = l.getTo().getRelativePosition().x;
1761                edge.link = l;
1762                edge.vip = l.isVIP();
1763                edge.from.succs.add(edge);
1764                edge.to.preds.add(edge);
1765                //assert edge.from != edge.to; // No self-loops allowed
1766            }
1767
1768            for (Link l : importantLinks) {
1769                if (!vertexToLayoutNode.containsKey(l.getFrom().getVertex())
1770                        || vertexToLayoutNode.containsKey(l.getTo().getVertex())) {
1771                    continue;
1772                }
1773                LayoutNode from = vertexToLayoutNode.get(l.getFrom().getVertex());
1774                LayoutNode to = vertexToLayoutNode.get(l.getTo().getVertex());
1775                for (LayoutEdge e : from.succs) {
1776                    if (e.to == to) {
1777                        linksToFollow.add(e.link);
1778                    }
1779                }
1780            }
1781        }
1782
1783        @Override
1784        public void postCheck() {
1785
1786            assert vertexToLayoutNode.keySet().size() == nodes.size();
1787            assert nodes.size() == graph.getVertices().size();
1788
1789            for (Vertex v : graph.getVertices()) {
1790
1791                LayoutNode node = vertexToLayoutNode.get(v);
1792                assert node != null;
1793
1794                for (LayoutEdge e : node.succs) {
1795                    assert e.from == node;
1796                }
1797
1798                for (LayoutEdge e : node.preds) {
1799                    assert e.to == node;
1800                }
1801
1802            }
1803        }
1804    }
1805
1806    @Override
1807    public void doRouting(LayoutGraph graph) {
1808        // Do nothing for now
1809    }
1810}
1811