change graph implementation
This commit is contained in:
23
core/src/main/java/ru/trader/analysis/AnalysisCallBack.java
Normal file
23
core/src/main/java/ru/trader/analysis/AnalysisCallBack.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package ru.trader.analysis;
|
||||
|
||||
public class AnalysisCallBack {
|
||||
|
||||
private volatile boolean cancel = false;
|
||||
|
||||
public String getMessage(String key){return "";}
|
||||
|
||||
public void startStage(String id){}
|
||||
public void setMax(long count){}
|
||||
public void inc(){}
|
||||
public void print(String message){}
|
||||
public void endStage(String id){}
|
||||
|
||||
public final boolean isCancel() {
|
||||
return cancel;
|
||||
}
|
||||
|
||||
public final void cancel(){
|
||||
this.cancel = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.analysis.AnalysisCallBack;
|
||||
import ru.trader.core.Profile;
|
||||
import ru.trader.graph.Connectable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ConnectibleGraph<T extends Connectable<T>> extends Graph<T> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ConnectibleGraph.class);
|
||||
|
||||
private final Profile profile;
|
||||
|
||||
public ConnectibleGraph(Profile profile) {
|
||||
super();
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public ConnectibleGraph(Profile profile, AnalysisCallBack callback) {
|
||||
super(callback);
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
return new ConnectibleGraphBuilder(vertex, set, deep, limit);
|
||||
}
|
||||
|
||||
public void build(T start, Collection<T> set){
|
||||
super.build(start, set, profile.getJumps(), profile.getShip().getTank());
|
||||
}
|
||||
|
||||
private class DistanceFilter implements Predicate<Double> {
|
||||
private final double limit;
|
||||
private final T source;
|
||||
|
||||
private DistanceFilter(double limit, T source) {
|
||||
this.limit = limit;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Double distance) {
|
||||
return distance <= profile.getShip().getJumpRange(limit) || (profile.withRefill() && distance <= profile.getShip().getJumpRange() && source.canRefill());
|
||||
}
|
||||
}
|
||||
|
||||
private class ConnectibleGraphBuilder extends GraphBuilder {
|
||||
private final DistanceFilter distanceFilter;
|
||||
protected boolean refill;
|
||||
|
||||
private ConnectibleGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
super(vertex, set, deep, limit);
|
||||
distanceFilter = new DistanceFilter(limit, vertex.getEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double onConnect(T entry) {
|
||||
double distance = vertex.getEntry().getDistance(entry);
|
||||
if (!distanceFilter.test(distance)){
|
||||
LOG.trace("Vertex {} is far away, {}", entry, distance);
|
||||
return -1;
|
||||
}
|
||||
double costFuel = profile.getShip().getFuelCost(limit, distance);
|
||||
double nextLimit = profile.withRefill() ? limit - costFuel : profile.getShip().getTank();
|
||||
if (nextLimit < 0) {
|
||||
LOG.trace("Refill");
|
||||
refill = true;
|
||||
nextLimit = profile.getShip().getTank() - profile.getShip().getFuelCost(distance);
|
||||
} else {
|
||||
refill = false;
|
||||
}
|
||||
return nextLimit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectibleEdge createEdge(Vertex<T> target) {
|
||||
return new ConnectibleEdge(vertex, target, refill);
|
||||
}
|
||||
}
|
||||
|
||||
protected class ConnectibleEdge extends Edge<T> {
|
||||
private final boolean refill;
|
||||
|
||||
protected ConnectibleEdge(Vertex<T> source, Vertex<T> target, boolean refill) {
|
||||
super(source, target);
|
||||
this.refill = refill;
|
||||
}
|
||||
|
||||
public boolean isRefill() {
|
||||
return refill;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeWeight() {
|
||||
T s = source.getEntry();
|
||||
T t = target.getEntry();
|
||||
return s.getDistance(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return source.getEntry().toString() + " - "+ weight
|
||||
+ (refill ? "R" : "")
|
||||
+" -> " + target.getEntry().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
291
core/src/main/java/ru/trader/analysis/graph/Crawler.java
Normal file
291
core/src/main/java/ru/trader/analysis/graph/Crawler.java
Normal file
@@ -0,0 +1,291 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Crawler<T> {
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
private final static int THRESHOLD = 4;
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Crawler.class);
|
||||
|
||||
private final Graph<T> graph;
|
||||
private final Consumer<List<Edge<T>>> onFoundFunc;
|
||||
|
||||
public Crawler(Graph<T> graph, Consumer<List<Edge<T>>> onFoundFunc) {
|
||||
this.graph = graph;
|
||||
this.onFoundFunc = onFoundFunc;
|
||||
}
|
||||
|
||||
private List<Edge<T>> getCopyList(List<Edge<T>> head, Edge<T> tail){
|
||||
List<Edge<T>> res = new ArrayList<>(20);
|
||||
res.addAll(head);
|
||||
res.add(tail);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void findFast(T target){
|
||||
findFast(target, 1);
|
||||
}
|
||||
|
||||
public void findFast(T target, int count){
|
||||
Vertex<T> t = graph.getVertex(target);
|
||||
int found = 0;
|
||||
if (t != null) {
|
||||
if (count > 1) {
|
||||
found = bfs(new ArrayList<>(), graph.root, target, 0, count);
|
||||
} else {
|
||||
found = dfs(new ArrayList<>(), graph.root, target, t.getLevel() + 1, count);
|
||||
}
|
||||
}
|
||||
LOG.debug("Found {} paths", found);
|
||||
}
|
||||
|
||||
public void findMin(T target){
|
||||
findMin(target, 1);
|
||||
}
|
||||
|
||||
public void findMin(T target, int count){
|
||||
Vertex<T> t = graph.getVertex(target);
|
||||
int found = 0;
|
||||
if (t != null) {
|
||||
found = ucs(new ArrayList<>(), graph.root, target, 0, count);
|
||||
}
|
||||
LOG.debug("Found {} paths", found);
|
||||
}
|
||||
|
||||
private int dfs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("DFS from {} to {}, deep {}, count {}, head {}", source, target, deep, count, head);
|
||||
int found = 0;
|
||||
if (deep == source.getLevel()){
|
||||
Optional<Edge<T>> last = source.getEdges().parallelStream()
|
||||
.filter(next -> next.isConnect(target))
|
||||
.findFirst();
|
||||
if (last.isPresent()){
|
||||
List<Edge<T>> res = getCopyList(head, last.get());
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
}
|
||||
}
|
||||
if (found < count){
|
||||
if (deep < source.getLevel()) {
|
||||
LOG.trace("Search around");
|
||||
for (Edge<T> edge : source.getEdges()) {
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
found += dfs(getCopyList(head, edge), edge.getTarget(), target, deep, count-found);
|
||||
if (found >= count) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private int bfs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("BFS from {} to {}, deep {}, count {}", source, target, deep, count);
|
||||
int found = 0;
|
||||
LinkedList<TraversalEntry> queue = new LinkedList<>();
|
||||
queue.add(new TraversalEntry(head, source));
|
||||
while (!queue.isEmpty() && count > found){
|
||||
TraversalEntry entry = queue.poll();
|
||||
head = entry.head;
|
||||
source = entry.vertex;
|
||||
LOG.trace("Search from {} to {}, head {}", source, target, head);
|
||||
source.sortEdges();
|
||||
for (Edge<T> edge : source.getEdges()) {
|
||||
if (edge.isConnect(target)){
|
||||
List<Edge<T>> res = getCopyList(head, edge);
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
}
|
||||
if (found >= count) break;
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
if (deep < source.getLevel()) {
|
||||
queue.add(new TraversalEntry(getCopyList(head, edge), edge.getTarget()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private int ucs(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("UCS from {} to {}, deep {}, count {}", source, target, deep, count);
|
||||
int found = 0;
|
||||
PriorityQueue<CostTraversalEntry> queue = new PriorityQueue<>();
|
||||
queue.add(new CostTraversalEntry(head, source));
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.poll();
|
||||
LOG.trace("Check path head {}, edge {}, cost {}", entry.head, entry.edge, entry.cost);
|
||||
head = entry.head;
|
||||
Edge<T> edge = entry.edge;
|
||||
if (edge != null) {
|
||||
source = edge.getSource();
|
||||
if (edge.isConnect(target)) {
|
||||
List<Edge<T>> res = getCopyList(head, edge);
|
||||
LOG.debug("Path found {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
if (found >= count) break;
|
||||
}
|
||||
if (edge.getTarget().isSingle() || deep >= source.getLevel()){
|
||||
continue;
|
||||
}
|
||||
head = getCopyList(entry.head, edge);
|
||||
}
|
||||
Iterator<Edge<T>> iterator = entry.iterator;
|
||||
//put only 2 entry for iterate
|
||||
while (iterator.hasNext()){
|
||||
edge = iterator.next();
|
||||
if (deep < source.getLevel() && !edge.getTarget().isSingle() || edge.isConnect(target)) {
|
||||
LOG.trace("Add edge {} to queue", edge);
|
||||
queue.add(new CostTraversalEntry(head, edge, entry.cost));
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
//last edge don't compare
|
||||
private int ucs2(List<Edge<T>> head, Vertex<T> source, T target, int deep, int count) {
|
||||
LOG.trace("UCS2 from {} to {}, deep {}, count {}", source, target, deep, count);
|
||||
int found = 0;
|
||||
PriorityQueue<CostTraversalEntry> queue = new PriorityQueue<>();
|
||||
source.sortEdges();
|
||||
queue.add(new CostTraversalEntry(head, source));
|
||||
while (!queue.isEmpty() && count > found){
|
||||
CostTraversalEntry entry = queue.peek();
|
||||
head = entry.edge != null ? getCopyList(entry.head, entry.edge) : entry.head;
|
||||
Iterator<Edge<T>> iterator = entry.iterator;
|
||||
LOG.trace("Check path head {}, cost {}", head, entry.cost);
|
||||
int i = 0;
|
||||
//put only 2 entry for iterate
|
||||
while (iterator.hasNext() && i < 2){
|
||||
Edge<T> edge = iterator.next();
|
||||
LOG.trace("Check edge {}", edge);
|
||||
if (edge.isConnect(target)) {
|
||||
List<Edge<T>> res = getCopyList(head, edge);
|
||||
LOG.debug("Last edge find, path {}", res);
|
||||
onFoundFunc.accept(res);
|
||||
found++;
|
||||
}
|
||||
if (found >= count) break;
|
||||
if (edge.getTarget().isSingle()) continue;
|
||||
if (deep < source.getLevel()) {
|
||||
edge.getTarget().sortEdges();
|
||||
queue.add(new CostTraversalEntry(head, edge, entry.cost));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (!iterator.hasNext()) queue.poll();
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private class TraversalEntry {
|
||||
private final List<Edge<T>> head;
|
||||
private final Vertex<T> vertex;
|
||||
|
||||
private TraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
|
||||
this.head = head;
|
||||
this.vertex = vertex;
|
||||
}
|
||||
}
|
||||
|
||||
private class CostTraversalEntry implements Comparable<CostTraversalEntry>{
|
||||
private final List<Edge<T>> head;
|
||||
private final Edge<T> edge;
|
||||
private final Iterator<Edge<T>> iterator;
|
||||
private final double cost;
|
||||
|
||||
private CostTraversalEntry(List<Edge<T>> head, Vertex<T> vertex) {
|
||||
this.head = head;
|
||||
this.iterator = vertex.getEdges().iterator();
|
||||
this.edge = null;
|
||||
this.cost = 0;
|
||||
}
|
||||
|
||||
private CostTraversalEntry(List<Edge<T>> head, Edge<T> edge, double cost) {
|
||||
this.head = head;
|
||||
this.edge = edge;
|
||||
this.iterator = edge.getTarget().getEdges().iterator();
|
||||
this.cost = cost + edge.getWeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull CostTraversalEntry other) {
|
||||
int cmp = Double.compare(cost, other.cost);
|
||||
if (cmp != 0) return cmp;
|
||||
return Integer.compare(head.size(), other.head.size());
|
||||
}
|
||||
}
|
||||
/*
|
||||
private class PathFinder extends RecursiveAction {
|
||||
private final TopList<Path<T>> paths;
|
||||
private final Path<T> head;
|
||||
private final Vertex<T> target;
|
||||
|
||||
private PathFinder(TopList<Path<T>> paths, Path<T> head, Vertex<T> target) {
|
||||
this.paths = paths;
|
||||
this.head = head;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
if (target == null || isCancelled()) return;
|
||||
Vertex<T> source = head.getTarget();
|
||||
LOG.trace("Find path to deep from {} to {}, head {}", source, target, head);
|
||||
Edge<T> edge = source.getEdge(target);
|
||||
if (edge != null){
|
||||
Path<T> path = head.connectTo(edge.getTarget(), limit < edge.getLength());
|
||||
path.finish();
|
||||
LOG.trace("Last edge find, add path {}", path);
|
||||
synchronized (paths){
|
||||
if (!paths.add(path)) complete(null);
|
||||
}
|
||||
callback.onFound();
|
||||
}
|
||||
if (!source.isSingle()){
|
||||
LOG.trace("Search around");
|
||||
ArrayList<PathFinder> subTasks = new ArrayList<>(source.getEdges().size());
|
||||
Iterator<Edge<T>> iterator = source.getEdges().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Edge<T> next = iterator.next();
|
||||
if (isDone() || callback.isCancel()) break;
|
||||
// target already added if source consist edge
|
||||
if (next.isConnect(target)) continue;
|
||||
Path<T> path = head.connectTo(next.getTarget(), limit < next.getLength());
|
||||
//Recursive search
|
||||
PathFinder task = new PathFinder(paths, path, target);
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){
|
||||
for (PathFinder subTask : subTasks) {
|
||||
if (isDone() || callback.isCancel()) {
|
||||
subTask.cancel(callback.isCancel());
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
}
|
||||
if (!subTasks.isEmpty()){
|
||||
for (PathFinder subTask : subTasks) {
|
||||
if (isDone() || callback.isCancel()) {
|
||||
subTask.cancel(callback.isCancel());
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
60
core/src/main/java/ru/trader/analysis/graph/Edge.java
Normal file
60
core/src/main/java/ru/trader/analysis/graph/Edge.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class Edge<T> implements Comparable<Edge>{
|
||||
protected Double weight;
|
||||
protected final Vertex<T> target;
|
||||
protected final Vertex<T> source;
|
||||
|
||||
protected Edge(Vertex<T> source, Vertex<T> target) {
|
||||
this.target = target;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
protected abstract double computeWeight();
|
||||
|
||||
public Vertex<T> getTarget(){
|
||||
return target;
|
||||
}
|
||||
|
||||
public Vertex<T> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public double getWeight(){
|
||||
if (weight == null){
|
||||
weight = computeWeight();
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
public boolean isConnect(T other){
|
||||
return target.getEntry().equals(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Edge edge = (Edge) o;
|
||||
return source.equals(edge.source) && target.equals(edge.target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = target.hashCode();
|
||||
result = 31 * result + source.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Edge other) {
|
||||
return Double.compare(getWeight(), other.getWeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return source.getEntry().toString() + " - "+ weight +" -> " + target.getEntry().toString();
|
||||
}
|
||||
}
|
||||
144
core/src/main/java/ru/trader/analysis/graph/Graph.java
Normal file
144
core/src/main/java/ru/trader/analysis/graph/Graph.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.analysis.AnalysisCallBack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
|
||||
public abstract class Graph<T> {
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
private final static int THRESHOLD = 4;
|
||||
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Graph.class);
|
||||
|
||||
protected Vertex<T> root;
|
||||
protected final Map<T, Vertex<T>> vertexes;
|
||||
private final GraphCallBack callback;
|
||||
|
||||
protected int minJumps;
|
||||
|
||||
protected Graph() {
|
||||
this(new AnalysisCallBack());
|
||||
}
|
||||
|
||||
protected Graph(AnalysisCallBack callback) {
|
||||
this.callback = new GraphCallBack(callback);
|
||||
vertexes = new ConcurrentHashMap<>(50, 0.9f, THRESHOLD);
|
||||
}
|
||||
|
||||
protected abstract GraphBuilder createGraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit);
|
||||
|
||||
public void build(T start, Collection<T> set, int maxDeep, double limit) {
|
||||
callback.startBuild(start);
|
||||
root = getInstance(start, maxDeep);
|
||||
POOL.invoke(createGraphBuilder(root, set, maxDeep - 1, limit));
|
||||
if (set.size() > vertexes.size()){
|
||||
minJumps = maxDeep;
|
||||
} else {
|
||||
minJumps = 1;
|
||||
for (Vertex<T> vertex : vertexes.values()) {
|
||||
int jumps = maxDeep - vertex.getLevel();
|
||||
if (jumps > minJumps) minJumps = jumps;
|
||||
}
|
||||
}
|
||||
callback.endBuild();
|
||||
}
|
||||
|
||||
private Vertex<T> getInstance(T entry, int deep){
|
||||
Vertex<T> vertex = new Vertex<>(entry);
|
||||
vertex.setLevel(deep);
|
||||
Vertex<T> old = vertexes.get(entry);
|
||||
if (old == null || old.getLevel() < deep) {
|
||||
LOG.trace("Is top vertex");
|
||||
vertexes.put(entry, vertex);
|
||||
}
|
||||
return vertex;
|
||||
}
|
||||
|
||||
public boolean isAccessible(T entry){
|
||||
return vertexes.containsKey(entry);
|
||||
}
|
||||
|
||||
public Vertex<T> getVertex(T entry){
|
||||
return vertexes.get(entry);
|
||||
}
|
||||
|
||||
public T getRoot() {
|
||||
return root.getEntry();
|
||||
}
|
||||
|
||||
public int getMinJumps() {
|
||||
return minJumps;
|
||||
}
|
||||
|
||||
protected abstract class GraphBuilder extends RecursiveAction {
|
||||
protected final Vertex<T> vertex;
|
||||
protected final Collection<T> set;
|
||||
protected final int deep;
|
||||
protected final double limit;
|
||||
|
||||
protected GraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
this.vertex = vertex;
|
||||
this.set = set;
|
||||
this.deep = deep;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
protected abstract double onConnect(T entry);
|
||||
protected abstract Edge<T> createEdge(Vertex<T> target);
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||
ArrayList<GraphBuilder> subTasks = new ArrayList<>(THRESHOLD);
|
||||
Iterator<T> iterator = set.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (callback.isCancel()) break;
|
||||
T entry = iterator.next();
|
||||
if (entry == vertex.getEntry()) continue;
|
||||
double nextLimit = onConnect(entry);
|
||||
if (nextLimit >= 0) {
|
||||
LOG.trace("Connect {} to {}", vertex, entry);
|
||||
Vertex<T> next = getInstance(entry, vertex.getLevel() - 1);
|
||||
vertex.connect(createEdge(next));
|
||||
if (deep > 0) {
|
||||
//Recursive build
|
||||
GraphBuilder task = createGraphBuilder(next, set, deep - 1, nextLimit);
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Vertex {} is far away", entry);
|
||||
}
|
||||
if (subTasks.size() == THRESHOLD || !iterator.hasNext()){
|
||||
for (GraphBuilder subTask : subTasks) {
|
||||
if (callback.isCancel()){
|
||||
subTask.cancel(true);
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
}
|
||||
if (!subTasks.isEmpty()){
|
||||
for (GraphBuilder subTask : subTasks) {
|
||||
if (callback.isCancel()){
|
||||
subTask.cancel(true);
|
||||
} else {
|
||||
subTask.join();
|
||||
}
|
||||
}
|
||||
subTasks.clear();
|
||||
}
|
||||
LOG.trace("End build graph from {} on deep {}", vertex, deep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import ru.trader.analysis.AnalysisCallBack;
|
||||
|
||||
public class GraphCallBack {
|
||||
private final AnalysisCallBack parent;
|
||||
public final String BUILD_STAGE = "graph.stage.build";
|
||||
|
||||
public GraphCallBack(AnalysisCallBack parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void startBuild(Object entry){
|
||||
parent.startStage(BUILD_STAGE);
|
||||
parent.print(String.format(parent.getMessage(BUILD_STAGE), entry.toString()));
|
||||
}
|
||||
|
||||
public void endBuild(){
|
||||
parent.endStage(BUILD_STAGE);
|
||||
}
|
||||
|
||||
public boolean isCancel(){
|
||||
return parent.isCancel();
|
||||
}
|
||||
}
|
||||
82
core/src/main/java/ru/trader/analysis/graph/Vertex.java
Normal file
82
core/src/main/java/ru/trader/analysis/graph/Vertex.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package ru.trader.analysis.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Vertex<T> {
|
||||
private final ArrayList<Edge<T>> edges = new ArrayList<>();
|
||||
private final T entry;
|
||||
private volatile int level = -1;
|
||||
|
||||
public Vertex(T entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
public T getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public boolean isEntry(T entry){
|
||||
return this.entry.equals(entry);
|
||||
}
|
||||
|
||||
void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void connect(Edge<T> edge){
|
||||
assert this == edge.getSource();
|
||||
synchronized (edges){
|
||||
edges.add(edge);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Edge<T>> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public Optional<Edge<T>> getEdge(Vertex<T> target) {
|
||||
return getEdge(target.entry);
|
||||
}
|
||||
|
||||
public Optional<Edge<T>> getEdge(T target) {
|
||||
return edges.stream().filter((e) -> e.isConnect(target)).findFirst();
|
||||
}
|
||||
|
||||
public void sortEdges(){
|
||||
edges.sort(Comparator.<Edge<T>>naturalOrder());
|
||||
}
|
||||
|
||||
public boolean isConnected(T other){
|
||||
return getEdge(other).isPresent();
|
||||
}
|
||||
|
||||
public boolean isSingle(){
|
||||
return edges.size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Vertex vertex = (Vertex) o;
|
||||
return entry.equals(vertex.entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return entry.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Vertex{" + entry + ", lvl=" + level + '}';
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user