From 745624377ba76733f671fd032aaf8183608caae4 Mon Sep 17 00:00:00 2001 From: zhangyi51 Date: Tue, 13 Apr 2021 18:48:18 +0800 Subject: [PATCH] extract Record and Records for paths and shortest path Change-Id: I4b79d0987f1eebcfb952bc4627a2f4fc8d2dcaf3 --- .../traversal/algorithm/PathsTraverser.java | 5 +- .../algorithm/ShortestPathTraverser.java | 10 +- .../{ArrayRecord.java => IntArrayRecord.java} | 4 +- .../algorithm/records/IntIntRecord.java | 61 ++++++ .../algorithm/records/MultiPathsRecords.java | 76 +++---- .../algorithm/records/RecordFactory.java | 8 +- .../algorithm/records/RecordType.java | 4 +- .../traversal/algorithm/records/Records.java | 2 - .../algorithm/records/ShortestPathRecord.java | 194 ------------------ .../records/ShortestPathRecords.java | 111 ++++++++++ 10 files changed, 225 insertions(+), 250 deletions(-) rename hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/{ArrayRecord.java => IntArrayRecord.java} (96%) create mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/IntIntRecord.java delete mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecord.java create mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecords.java diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PathsTraverser.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PathsTraverser.java index e7a9b4ffa2..05aededcc1 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PathsTraverser.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PathsTraverser.java @@ -28,6 +28,7 @@ import com.baidu.hugegraph.perf.PerfUtil.Watched; import com.baidu.hugegraph.structure.HugeEdge; import com.baidu.hugegraph.traversal.algorithm.records.MultiPathsRecords; +import com.baidu.hugegraph.traversal.algorithm.records.RecordType; import com.baidu.hugegraph.type.define.Directions; import com.baidu.hugegraph.util.E; import com.google.common.collect.ImmutableList; @@ -93,8 +94,8 @@ private class Traverser { public Traverser(Id sourceV, Id targetV, Id label, long degree, long capacity, long limit) { - this.record = new MultiPathsRecords(sourceV, targetV); - + this.record = new MultiPathsRecords(sourceV, targetV, + RecordType.ARRAY); this.label = label; this.degree = degree; this.capacity = capacity; diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java index 91a7b437cf..151928964b 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java @@ -30,18 +30,16 @@ import com.baidu.hugegraph.backend.id.Id; import com.baidu.hugegraph.perf.PerfUtil.Watched; import com.baidu.hugegraph.structure.HugeEdge; -import com.baidu.hugegraph.traversal.algorithm.records.ShortestPathRecord; +import com.baidu.hugegraph.traversal.algorithm.records.ShortestPathRecords; import com.baidu.hugegraph.traversal.algorithm.steps.EdgeStep; import com.baidu.hugegraph.type.define.Directions; import com.baidu.hugegraph.util.E; -import com.baidu.hugegraph.util.collection.ObjectIntMapping; import com.google.common.collect.ImmutableList; public class ShortestPathTraverser extends HugeTraverser { public ShortestPathTraverser(HugeGraph graph) { super(graph); - this.idMapping = new ObjectIntMapping(); } @Watched @@ -142,7 +140,7 @@ public PathSet allShortestPaths(Id sourceV, Id targetV, Directions dir, private class Traverser { - private ShortestPathRecord record; + private ShortestPathRecords record; private final Directions direction; private final Map labels; private final long degree; @@ -152,7 +150,7 @@ private class Traverser { public Traverser(Id sourceV, Id targetV, Directions dir, Map labels, long degree, long skipDegree, long capacity) { - this.record = new ShortestPathRecord(sourceV, targetV); + this.record = new ShortestPathRecords(sourceV, targetV); this.direction = dir; this.labels = labels; this.degree = degree; @@ -221,7 +219,7 @@ public PathSet backward(boolean all) { Id target = edge.id().otherVertexId(); PathSet paths = this.record.findPath(target, - t -> this.superNode(t, opposite), + t -> !this.superNode(t, opposite), all, false); if (paths.isEmpty()) { diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ArrayRecord.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/IntArrayRecord.java similarity index 96% rename from hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ArrayRecord.java rename to hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/IntArrayRecord.java index f142d7d0d4..a67e4d344b 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ArrayRecord.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/IntArrayRecord.java @@ -23,11 +23,11 @@ import com.baidu.hugegraph.util.collection.Int2IntsMap; -public class ArrayRecord implements Record { +public class IntArrayRecord implements Record { private Int2IntsMap layer; - public ArrayRecord() { + public IntArrayRecord() { this.layer = new Int2IntsMap(); } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/IntIntRecord.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/IntIntRecord.java new file mode 100644 index 0000000000..5f9c06951c --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/IntIntRecord.java @@ -0,0 +1,61 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.traversal.algorithm.records; + +import org.eclipse.collections.api.iterator.IntIterator; +import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; + +public class IntIntRecord implements Record { + + private IntIntHashMap layer; + + public IntIntRecord() { + this.layer = new IntIntHashMap(); + } + + @Override + public IntIterator keys() { + return this.layer.keySet().intIterator(); + } + + @Override + public boolean containsKey(int key) { + return this.layer.containsKey(key); + } + + @Override + public IntIterator get(int key) { + return null; + } + + @Override + public void addPath(int node, int parent) { + this.layer.put(node, parent); + } + + @Override + public int size() { + return this.layer.size(); + } + + public IntIntHashMap layer() { + return this.layer; + } +} diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/MultiPathsRecords.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/MultiPathsRecords.java index b0e82f283d..6763a80181 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/MultiPathsRecords.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/MultiPathsRecords.java @@ -38,58 +38,59 @@ public class MultiPathsRecords implements Records { private final ObjectIntMapping idMapping; + private final RecordType type; - private final Stack sourceLayers; - private final Stack targetLayers; - - private Record currentLayer; - private IntIterator lastLayerKeys; - private int current; - private boolean forward; + protected final Stack sourceRecords; + protected final Stack targetRecords; + protected Record currentRecord; + private IntIterator lastRecordKeys; + protected int current; + protected boolean forward; private int accessed; - public MultiPathsRecords(Id sourceV, Id targetV) { + public MultiPathsRecords(Id sourceV, Id targetV, RecordType type) { this.idMapping = new ObjectIntMapping(); + this.type = type; int sourceCode = this.code(sourceV); int targetCode = this.code(targetV); - Record firstSourceLayer = this.newLayer(); - Record firstTargetLayer = this.newLayer(); - firstSourceLayer.addPath(sourceCode, 0); - firstTargetLayer.addPath(targetCode, 0); - this.sourceLayers = new Stack<>(); - this.targetLayers = new Stack<>(); - this.sourceLayers.push(firstSourceLayer); - this.targetLayers.push(firstTargetLayer); + Record firstSourceRecord = this.newRecord(); + Record firstTargetRecord = this.newRecord(); + firstSourceRecord.addPath(sourceCode, 0); + firstTargetRecord.addPath(targetCode, 0); + this.sourceRecords = new Stack<>(); + this.targetRecords = new Stack<>(); + this.sourceRecords.push(firstSourceRecord); + this.targetRecords.push(firstTargetRecord); this.accessed = 2; } public void startOneLayer(boolean forward) { this.forward = forward; - this.currentLayer = newLayer(); - this.lastLayerKeys = this.forward ? this.sourceLayers.peek().keys() : - this.targetLayers.peek().keys(); + this.currentRecord = this.newRecord(); + this.lastRecordKeys = this.forward ? this.sourceRecords.peek().keys() : + this.targetRecords.peek().keys(); } public void finishOneLayer() { if (this.forward) { - this.sourceLayers.push(this.currentLayer); + this.sourceRecords.push(this.currentRecord); } else { - this.targetLayers.push(this.currentLayer); + this.targetRecords.push(this.currentRecord); } - this.accessed += this.currentLayer.size(); + this.accessed += this.currentRecord.size(); } @Watched public boolean hasNextKey() { - return this.lastLayerKeys.hasNext(); + return this.lastRecordKeys.hasNext(); } @Watched public Id nextKey() { - this.current = this.lastLayerKeys.next(); + this.current = this.lastRecordKeys.next(); return this.id(current); } @@ -113,22 +114,21 @@ public long accessed() { return this.accessed; } - private boolean contains(int node) { + protected boolean contains(int node) { return this.forward ? this.targetContains(node) : this.sourceContains(node); } private boolean sourceContains(int node) { - return this.sourceLayers.peek().containsKey(node); + return this.sourceRecords.peek().containsKey(node); } private boolean targetContains(int node) { - return this.targetLayers.peek().containsKey(node); + return this.targetRecords.peek().containsKey(node); } @Watched private PathSet linkPath(int source, int target, boolean ring) { - PathSet results = new PathSet(); PathSet sources = this.linkSourcePath(source); PathSet targets = this.linkTargetPath(target); @@ -151,13 +151,13 @@ private PathSet linkPath(int source, int target, boolean ring) { } private PathSet linkSourcePath(int source) { - return this.linkPath(this.sourceLayers, source, - this.sourceLayers.size() - 1); + return this.linkPath(this.sourceRecords, source, + this.sourceRecords.size() - 1); } private PathSet linkTargetPath(int target) { - return this.linkPath(this.targetLayers, target, - this.targetLayers.size() - 1); + return this.linkPath(this.targetRecords, target, + this.targetRecords.size() - 1); } private PathSet linkPath(Stack all, int id, int layerIndex) { @@ -189,21 +189,21 @@ private PathSet linkPath(Stack all, int id, int layerIndex) { } @Watched - private void addPath(int current, int parent) { - this.currentLayer.addPath(current, parent); + protected void addPath(int current, int parent) { + this.currentRecord.addPath(current, parent); } @Watched - private int code(Id id) { + protected int code(Id id) { return this.idMapping.object2Code(id); } @Watched - private Id id(int code) { + protected Id id(int code) { return (Id) this.idMapping.code2Object(code); } - public Record newLayer() { - return RecordFactory.newRecord(); + private Record newRecord() { + return RecordFactory.newRecord(this.type); } } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordFactory.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordFactory.java index 1e943ad503..563c820ed3 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordFactory.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordFactory.java @@ -21,16 +21,14 @@ public class RecordFactory { - public static Record newRecord() { - return new ArrayRecord(); - } - public static Record newRecord(RecordType type) { switch (type) { case ARRAY: - return new ArrayRecord(); + return new IntArrayRecord(); case SET: return new IntSetRecord(); + case INT: + return new IntIntRecord(); default: throw new AssertionError("Unsupported record type: " + type); } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordType.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordType.java index 2b8ec8baeb..9a1adbd2f1 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordType.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/RecordType.java @@ -27,7 +27,9 @@ public enum RecordType implements SerialEnum { ARRAY(1, "array"), // Eclipse Collection - SET(2, "set"); + SET(2, "set"), + + INT(3, "int"); private final byte code; private final String name; diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/Records.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/Records.java index 40cd11c820..405ed933eb 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/Records.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/Records.java @@ -26,8 +26,6 @@ public interface Records { - static final int INIT_CAPACITY = 16; - public void startOneLayer(boolean forward); public void finishOneLayer(); diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecord.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecord.java deleted file mode 100644 index fcb9f1bf0b..0000000000 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecord.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2017 HugeGraph Authors - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with this - * work for additional information regarding copyright ownership. The ASF - * licenses this file to You under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package com.baidu.hugegraph.traversal.algorithm.records; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; -import java.util.function.Function; - -import org.eclipse.collections.api.iterator.IntIterator; -import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; -import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; - -import com.baidu.hugegraph.backend.id.Id; -import com.baidu.hugegraph.traversal.algorithm.HugeTraverser.PathSet; -import com.baidu.hugegraph.traversal.algorithm.HugeTraverser.Path; -import com.baidu.hugegraph.util.collection.ObjectIntMapping; - -public class ShortestPathRecord implements Records { - - private final ObjectIntMapping idMapping; - - private final Stack sourceLayers; - private final Stack targetLayers; - - private IntIntHashMap currentLayer; - private IntIterator lastLayerKeys; - private int current; - private boolean forward; - - private final IntHashSet accessed; - private long size; - private boolean foundPath; - - public ShortestPathRecord(Id sourceV, Id targetV) { - - this.idMapping = new ObjectIntMapping(); - - int sourceCode = this.code(sourceV); - int targetCode = this.code(targetV); - IntIntHashMap firstSourceLayer = new IntIntHashMap(); - IntIntHashMap firstTargetLayer = new IntIntHashMap(); - firstSourceLayer.put(sourceCode, 0); - firstTargetLayer.put(targetCode, 0); - this.sourceLayers = new Stack<>(); - this.targetLayers = new Stack<>(); - this.sourceLayers.push(firstSourceLayer); - this.targetLayers.push(firstTargetLayer); - - this.accessed = new IntHashSet(); - this.accessed.add(sourceCode); - this.accessed.add(targetCode); - - this.size = 2L; - } - - public void startOneLayer(boolean forward) { - this.forward = forward; - this.currentLayer = new IntIntHashMap(INIT_CAPACITY); - this.lastLayerKeys = this.forward ? - this.sourceLayers.peek().keySet().intIterator() : - this.targetLayers.peek().keySet().intIterator(); - } - - public void finishOneLayer() { - if (this.forward) { - this.sourceLayers.push(this.currentLayer); - } else { - this.targetLayers.push(this.currentLayer); - } - this.size += this.currentLayer.size(); - } - - public boolean hasNextKey() { - return this.lastLayerKeys.hasNext(); - } - - public Id nextKey() { - this.current = this.lastLayerKeys.next(); - return this.id(current); - } - - public PathSet findPath(Id target, Function filter, - boolean all, boolean ring) { - assert !ring; - PathSet paths = new PathSet(); - int targetCode = this.code(target); - // If cross point exists, shortest path found, concat them - if (this.contains(targetCode)) { - if (!filter.apply(target)) { - return paths; - } - - paths.add(this.forward ? this.linkPath(this.current, targetCode) : - this.linkPath(targetCode, this.current)); - this.foundPath = true; - if (!all) { - return paths; - } - } - - /* - * Not found shortest path yet, node is added to - * newVertices if: - * 1. not in sources and newVertices yet - * 2. path of node doesn't have loop - */ - if (!this.foundPath && this.isNew(targetCode)) { - this.addOneStep(this.current, targetCode); - } - return paths; - } - - public long accessed() { - return this.size; - } - - private boolean contains(int node) { - return this.forward ? this.targetContains(node) : - this.sourceContains(node); - } - - private boolean sourceContains(int node) { - return this.sourceLayers.peek().containsKey(node); - } - - private boolean targetContains(int node) { - return this.targetLayers.peek().containsKey(node); - } - - private boolean isNew(int node) { - return !this.currentLayer.containsKey(node) && - !this.accessed.contains(node); - } - - private void addOneStep(int source, int target) { - this.currentLayer.put(target, source); - } - - private Path linkPath(int source, int target) { - Path sourcePath = this.linkSourcePath(source); - Path targetPath = this.linkTargetPath(target); - sourcePath.reverse(); - List ids = new ArrayList<>(sourcePath.vertices()); - ids.addAll(targetPath.vertices()); - return new Path(ids); - } - - private Path linkSourcePath(int source) { - return this.linkPath(this.sourceLayers, source); - } - - private Path linkTargetPath(int target) { - return this.linkPath(this.targetLayers, target); - } - - private Path linkPath(Stack all, int node) { - int size = all.size(); - List ids = new ArrayList<>(size); - ids.add(this.id(node)); - int value = node; - for (int i = size - 1; i > 0 ; i--) { - IntIntHashMap layer = all.elementAt(i); - value = layer.get(value); - ids.add(this.id(value)); - } - return new Path(ids); - } - - private int code(Id id) { - return this.idMapping.object2Code(id); - } - - private Id id(int code) { - return (Id) this.idMapping.code2Object(code); - } -} diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecords.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecords.java new file mode 100644 index 0000000000..06a51167a7 --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/records/ShortestPathRecords.java @@ -0,0 +1,111 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.traversal.algorithm.records; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; +import java.util.function.Function; + +import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; +import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; + +import com.baidu.hugegraph.backend.id.Id; +import com.baidu.hugegraph.traversal.algorithm.HugeTraverser.PathSet; +import com.baidu.hugegraph.traversal.algorithm.HugeTraverser.Path; + +public class ShortestPathRecords extends MultiPathsRecords { + + private final IntHashSet accessedVertices; + private boolean pathFound; + + public ShortestPathRecords(Id sourceV, Id targetV) { + super(sourceV, targetV, RecordType.INT); + + this.accessedVertices = new IntHashSet(); + this.accessedVertices.add(this.code(sourceV)); + this.accessedVertices.add(this.code(targetV)); + } + + @Override + public PathSet findPath(Id target, Function filter, + boolean all, boolean ring) { + assert !ring; + PathSet paths = new PathSet(); + int targetCode = this.code(target); + // If cross point exists, shortest path found, concat them + if (this.contains(targetCode)) { + if (!filter.apply(target)) { + return paths; + } + paths.add(this.forward ? this.linkPath(this.current, targetCode) : + this.linkPath(targetCode, this.current)); + this.pathFound = true; + if (!all) { + return paths; + } + } + /* + * Not found shortest path yet, node is added to + * newVertices if: + * 1. not in sources and newVertices yet + * 2. path of node doesn't have loop + */ + if (!this.pathFound && this.isNew(targetCode)) { + this.addPath(targetCode, this.current); + } + return paths; + } + + private boolean isNew(int node) { + return !this.currentRecord.containsKey(node) && + !this.accessedVertices.contains(node); + } + + private Path linkPath(int source, int target) { + Path sourcePath = this.linkSourcePath(source); + Path targetPath = this.linkTargetPath(target); + sourcePath.reverse(); + List ids = new ArrayList<>(sourcePath.vertices()); + ids.addAll(targetPath.vertices()); + return new Path(ids); + } + + private Path linkSourcePath(int source) { + return this.linkPath(this.sourceRecords, source); + } + + private Path linkTargetPath(int target) { + return this.linkPath(this.targetRecords, target); + } + + private Path linkPath(Stack all, int node) { + int size = all.size(); + List ids = new ArrayList<>(size); + ids.add(this.id(node)); + int value = node; + for (int i = size - 1; i > 0 ; i--) { + IntIntHashMap layer = ((IntIntRecord) all.elementAt(i)).layer(); + value = layer.get(value); + ids.add(this.id(value)); + } + return new Path(ids); + } +}