|
25 | 25 |
|
26 | 26 | import java.util.function.LongPredicate;
|
27 | 27 |
|
| 28 | +import static org.neo4j.graphalgo.core.huge.AdjacencyList.DecompressingCursor.NOT_FOUND; |
| 29 | + |
28 | 30 | /**
|
29 | 31 | * An instance of this is not thread-safe; Iteration/Intersection on multiple threads will
|
30 | 32 | * throw misleading {@link NullPointerException}s.
|
@@ -69,78 +71,96 @@ public void intersectAll(long nodeA, IntersectionConsumer consumer) {
|
69 | 71 | // find first neighbour B of A with id > A
|
70 | 72 | long nodeB = neighboursAMain.skipUntil(nodeA);
|
71 | 73 | // if there is no such neighbour -> no triangle (or we already found it)
|
72 |
| - if (nodeA > nodeB) { |
| 74 | + if (nodeB == NOT_FOUND) { |
73 | 75 | return;
|
74 | 76 | }
|
75 | 77 |
|
76 | 78 | // iterates over neighbours of A
|
77 | 79 | AdjacencyList.DecompressingCursor neighboursA = cacheA;
|
78 | 80 | // current neighbour of A
|
79 |
| - long nodeCa; |
| 81 | + long nodeCfromA = NOT_FOUND; |
80 | 82 | // iterates over neighbours of B
|
81 | 83 | AdjacencyList.DecompressingCursor neighboursB = cacheB;
|
82 | 84 | // current neighbour of B
|
83 |
| - long nodeCb; |
| 85 | + long nodeCfromB; |
84 | 86 |
|
85 | 87 | // last node where Ca = Cb
|
86 | 88 | // prevents counting a new triangle for parallel relationships
|
87 | 89 | long triangleC;
|
88 | 90 |
|
89 |
| - // for all neighbours of A |
| 91 | + // for all neighbors of A |
90 | 92 | while (neighboursAMain.hasNextVLong()) {
|
91 | 93 | // we have not yet seen a triangle
|
92 |
| - triangleC = -1; |
| 94 | + triangleC = NOT_FOUND; |
93 | 95 | // check the second node's degree
|
94 | 96 | if (degreeFilter.test(nodeB)) {
|
95 | 97 | neighboursB = cursor(nodeB, neighboursB, offsets, adjacency);
|
96 | 98 | // find first neighbour Cb of B with id > B
|
97 |
| - nodeCb = neighboursB.skipUntil(nodeB); |
| 99 | + nodeCfromB = neighboursB.skipUntil(nodeB); |
98 | 100 |
|
99 |
| - // check the third node's degree |
100 |
| - if (nodeCb > nodeB && degreeFilter.test(nodeCb)) { |
| 101 | + // if B had no neighbors, find a new B |
| 102 | + if (nodeCfromB != NOT_FOUND) { |
101 | 103 | // copy the state of A's cursor
|
102 | 104 | neighboursA.copyFrom(neighboursAMain);
|
103 |
| - // find the first neighbour Ca of A with id >= Cb |
104 |
| - nodeCa = neighboursA.advance(nodeCb); |
105 |
| - |
106 |
| - // if Ca = Cb we have found a triangle |
107 |
| - // we only submit one triangle per parallel relationship |
108 |
| - if (nodeCa == nodeCb && nodeCa > triangleC) { |
109 |
| - consumer.accept(nodeA, nodeB, nodeCa); |
110 |
| - triangleC = nodeCa; |
| 105 | + |
| 106 | + if (degreeFilter.test(nodeCfromB)) { |
| 107 | + // find the first neighbour Ca of A with id >= Cb |
| 108 | + nodeCfromA = neighboursA.advance(nodeCfromB); |
| 109 | + triangleC = checkForAndEmitTriangle(consumer, nodeA, nodeB, nodeCfromA, nodeCfromB, triangleC); |
111 | 110 | }
|
112 | 111 |
|
113 | 112 | // while both A and B have more neighbours
|
114 |
| - while (neighboursB.hasNextVLong() && neighboursA.hasNextVLong()) { |
| 113 | + while (neighboursA.hasNextVLong() && neighboursB.hasNextVLong()) { |
115 | 114 | // take the next neighbour Cb of B
|
116 |
| - nodeCb = neighboursB.nextVLong(); |
117 |
| - if (nodeCb > nodeCa) { |
118 |
| - // if Cb > Ca, take the next neighbour Ca of A with id >= Cb |
119 |
| - nodeCa = neighboursA.advance(nodeCb); |
120 |
| - } |
121 |
| - // check for triangle |
122 |
| - if (nodeCa == nodeCb && nodeCa > triangleC && degreeFilter.test(nodeCa)) { |
123 |
| - consumer.accept(nodeA, nodeB, nodeCa); |
124 |
| - triangleC = nodeCa; |
| 115 | + nodeCfromB = neighboursB.nextVLong(); |
| 116 | + if (degreeFilter.test(nodeCfromB)) { |
| 117 | + if (nodeCfromB > nodeCfromA) { |
| 118 | + // if Cb > Ca, take the next neighbour Ca of A with id >= Cb |
| 119 | + nodeCfromA = neighboursA.advance(nodeCfromB); |
| 120 | + } |
| 121 | + triangleC = checkForAndEmitTriangle( |
| 122 | + consumer, |
| 123 | + nodeA, |
| 124 | + nodeB, |
| 125 | + nodeCfromA, |
| 126 | + nodeCfromB, |
| 127 | + triangleC |
| 128 | + ); |
125 | 129 | }
|
126 | 130 | }
|
127 | 131 |
|
128 | 132 | // it is possible that the last Ca > Cb, but there are no more neighbours Ca of A
|
129 | 133 | // so if there are more neighbours Cb of B
|
130 | 134 | if (neighboursB.hasNextVLong()) {
|
131 | 135 | // we take the next neighbour Cb of B with id >= Ca
|
132 |
| - nodeCb = neighboursB.advance(nodeCa); |
133 |
| - // check for triangle |
134 |
| - if (nodeCa == nodeCb && nodeCa > triangleC && degreeFilter.test(nodeCa)) { |
135 |
| - consumer.accept(nodeA, nodeB, nodeCa); |
| 136 | + nodeCfromB = neighboursB.advance(nodeCfromA); |
| 137 | + if (degreeFilter.test(nodeCfromB)) { |
| 138 | + checkForAndEmitTriangle(consumer, nodeA, nodeB, nodeCfromA, nodeCfromB, triangleC); |
136 | 139 | }
|
137 | 140 | }
|
138 | 141 | }
|
139 | 142 | }
|
140 | 143 |
|
141 | 144 | // skip until the next neighbour B of A with id > (current) B
|
142 |
| - nodeB = skipUntil(neighboursAMain, nodeB); |
| 145 | + nodeB = neighboursAMain.skipUntil(nodeB); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + private long checkForAndEmitTriangle( |
| 150 | + IntersectionConsumer consumer, |
| 151 | + long nodeA, |
| 152 | + long nodeB, |
| 153 | + long nodeCa, |
| 154 | + long nodeCb, |
| 155 | + long triangleC |
| 156 | + ) { |
| 157 | + // if Ca = Cb there exists a triangle |
| 158 | + // if Ca = triangleC we have already counted it |
| 159 | + if (nodeCa == nodeCb && nodeCa > triangleC) { |
| 160 | + consumer.accept(nodeA, nodeB, nodeCa); |
| 161 | + return nodeCa; |
143 | 162 | }
|
| 163 | + return triangleC; |
144 | 164 | }
|
145 | 165 |
|
146 | 166 | private int degree(long node) {
|
|
0 commit comments