aboutsummaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/graph/Traversals.java
blob: b69d67476e39672fd58eb01251ef4d25120020ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package com.yahoo.graph;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * Provides graph traversal algorithms in an ad-hoc manner.
 *
 * @author jvenstad / jonmv
 */
public class Traversals {

    private Traversals() { }

    /**
     * Returns an {@link Iterable} over the given nodes with a topological iteration order, per the given child mapper.
     *
     * @param nodes An iterable containing all nodes in the graph.
     * @param children A function mapping a node to its children.
     * @param <T> The node type.
     * @return A topologically ordered iterable over the given nodes.
     */
    public static <T> Iterable<T> topologically(Iterable<T> nodes, Function<T, Iterable<T>> children) {
        return () -> new Iterator<T>() {

            Iterator<T> delegate = topological(nodes, children);

            @Override
            public boolean hasNext() {
                return delegate.hasNext();
            }

            @Override
            public T next() {
                T next = delegate.next();
                delegate.remove();
                return next;
            }
        };
    }


    /**
     * Returns an {@link Iterable} over the given nodes with a topological iteration order, per the given child mapper.
     *
     * The {@link Iterator#remove()} method must be called for each returned element in order to proceed to its children.
     *
     * @param nodes An iterable containing all nodes in the graph.
     * @param children A function mapping a node to its children.
     * @param <T> The node type.
     * @return A topologically ordered iterable over the given nodes.
     */
    public static <T> Iterator<T> topological(Iterable<T> nodes, Function<T, Iterable<T>> children) {
        Map<T, Long> indegrees = new HashMap<>();
        for (T node : nodes)
            for (T child : children.apply(node))
                indegrees.merge(child, 1L, Long::sum);

        Deque<T> ready = new ArrayDeque<>();
        for (T node : nodes)
            if ( ! indegrees.containsKey(node))
                ready.add(node);

        return new Iterator<T>() {

            T last = null;

            @Override
            public boolean hasNext() {
                return ! ready.isEmpty();
            }

            @Override
            public T next() {
                return last = ready.remove();
            }

            @Override
            public void remove() {
                if (last == null)
                    throw new IllegalStateException();

                for (T child : children.apply(last))
                    indegrees.compute(child, (__, count) -> {
                        if (--count == 0)
                            ready.add(child);
                        return count;
                    });
            }
        };
    }

    /**
     * Returns an edge set which is the reverse of the given one, restricted to the given node set.
     *
     * @param nodes The node set of the input and output graphs.
     * @param edges The directed edge set for which to find a reverse.
     * @param <T> The node type.
     * @return The given edge set, but with all edges reversed.
     */
    public static <T> Function<T, Iterable<T>> reversed(Iterable<T> nodes, Function<T, Iterable<T>> edges) {
        Map<T, List<T>> reverse = new HashMap<>();
        for (T node : nodes)
            reverse.put(node, new ArrayList<>());

        for (T node : nodes)
            for (T neighbour : edges.apply(node))
                if (reverse.containsKey(neighbour))
                    reverse.get(neighbour).add(node);

        return reverse::get;
    }

}