remove old classes
This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
public class Edge<T extends Connectable<T>> {
|
||||
protected double length;
|
||||
protected final Vertex<T> target;
|
||||
protected final Vertex<T> source;
|
||||
|
||||
public Edge(Vertex<T> source, T target) {
|
||||
this(source, new Vertex<>(target));
|
||||
}
|
||||
|
||||
public Edge(Vertex<T> source, Vertex<T> target) {
|
||||
this.target = target;
|
||||
this.source = source;
|
||||
this.length = source.getEntry().getDistance(target.getEntry());
|
||||
}
|
||||
|
||||
public double getLength(){
|
||||
return length;
|
||||
}
|
||||
|
||||
public boolean isConnect(T other){
|
||||
return target.getEntry().equals(other);
|
||||
}
|
||||
|
||||
public boolean isConnect(Vertex<T> target){
|
||||
return getTarget().equals(target);
|
||||
}
|
||||
|
||||
public Vertex<T> getTarget(){
|
||||
return target;
|
||||
}
|
||||
|
||||
public Vertex<T> getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@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 String toString() {
|
||||
return source.toString() + " -> " + target.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class Graph<T extends Connectable<T>> {
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
private final static int THRESHOLD = 4;
|
||||
private final static int DEFAULT_COUNT = 200;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PathConstructor<E extends Connectable<E>> {
|
||||
Path<E> build(Vertex<E> source);
|
||||
}
|
||||
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Graph.class);
|
||||
|
||||
protected final Vertex<T> root;
|
||||
protected final Map<T,Vertex<T>> vertexes;
|
||||
private final GraphCallBack<T> callback;
|
||||
|
||||
protected final double stock;
|
||||
protected final double maxDistance;
|
||||
protected final boolean withRefill;
|
||||
private final PathConstructor<T> pathFabric;
|
||||
protected int minJumps;
|
||||
|
||||
|
||||
public Graph(T start, Collection<T> set, double stock, int maxDeep) {
|
||||
this(start, set, stock, stock, false, maxDeep, Path::new);
|
||||
}
|
||||
|
||||
public Graph(T start, Collection<T> set, double stock, double maxDistance, int maxDeep) {
|
||||
this(start, set, stock, maxDistance, true, maxDeep, Path::new);
|
||||
}
|
||||
|
||||
public Graph(T start, Collection<T> set, double stock, boolean withRefill, int maxDeep) {
|
||||
this(start, set, stock, stock, withRefill, maxDeep, Path::new);
|
||||
}
|
||||
|
||||
public Graph(T start, Collection<T> set, double stock, boolean withRefill, int maxDeep, PathConstructor<T> pathFabric) {
|
||||
this(start, set, stock, stock, withRefill, maxDeep, pathFabric);
|
||||
}
|
||||
|
||||
public Graph(T start, Collection<T> set, double stock, double maxDistance, boolean withRefill, int maxDeep, PathConstructor<T> pathFabric) {
|
||||
this(start, set, stock, maxDistance, withRefill, maxDeep, pathFabric, new GraphCallBack<>());
|
||||
}
|
||||
|
||||
public Graph(T start, Collection<T> set, double stock, double maxDistance, boolean withRefill, int maxDeep, PathConstructor<T> pathFabric, GraphCallBack<T> callback) {
|
||||
this.maxDistance = maxDistance;
|
||||
this.stock = stock;
|
||||
this.withRefill = withRefill;
|
||||
this.pathFabric = pathFabric;
|
||||
this.callback = callback;
|
||||
root = new Vertex<>(start);
|
||||
root.setLevel(maxDeep);
|
||||
vertexes = new ConcurrentHashMap<>(50, 0.9f, THRESHOLD);
|
||||
vertexes.put(root.getEntry(), root);
|
||||
callback.onStartBuild(start);
|
||||
build(root, set, maxDeep, stock);
|
||||
callback.onEndBuild();
|
||||
}
|
||||
|
||||
private void build(Vertex<T> root, Collection<T> set, int maxDeep, double stock) {
|
||||
POOL.invoke(new GraphBuilder(root, set, maxDeep - 1, stock));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAccessible(T entry){
|
||||
return vertexes.containsKey(entry);
|
||||
}
|
||||
|
||||
public Vertex<T> getVertex(T entry){
|
||||
return vertexes.get(entry);
|
||||
}
|
||||
|
||||
private void findPathsTo(Vertex<T> target, TopList<Path<T>> res, int deep){
|
||||
callback.onStartFind(root, target);
|
||||
POOL.invoke(new PathFinder(res, pathFabric.build(root), target, deep-1, stock));
|
||||
callback.onEndFind();
|
||||
}
|
||||
|
||||
public List<Path<T>> getPathsTo(T entry){
|
||||
return getPathsTo(entry, DEFAULT_COUNT);
|
||||
}
|
||||
|
||||
public List<Path<T>> getPathsTo(T entry, int max){
|
||||
return getPathsTo(entry, max, root.getLevel()).getList();
|
||||
}
|
||||
|
||||
public TopList<Path<T>> getPathsTo(T entry, int max, int deep){
|
||||
Vertex<T> target = getVertex(entry);
|
||||
TopList<Path<T>> paths = newTopList(max);
|
||||
callback.setMax(1);
|
||||
findPathsTo(target, paths, deep);
|
||||
callback.inc();
|
||||
paths.finish();
|
||||
return paths;
|
||||
}
|
||||
|
||||
public List<Path<T>> getPaths(int count){
|
||||
return getPaths(count, root.getLevel()).getList();
|
||||
}
|
||||
|
||||
public TopList<Path<T>> getPaths(int count, int deep){
|
||||
TopList<Path<T>> paths = newTopList(count);
|
||||
callback.setMax(vertexes.size());
|
||||
for (Vertex<T> target : vertexes.values()) {
|
||||
if (callback.isCancel()) break;
|
||||
TopList<Path<T>> p = newTopList(minJumps);
|
||||
findPathsTo(target, p, deep);
|
||||
for (Path<T> path : p.getList()) {
|
||||
paths.add(path);
|
||||
}
|
||||
callback.inc();
|
||||
}
|
||||
paths.finish();
|
||||
return paths;
|
||||
}
|
||||
|
||||
protected TopList<Path<T>> newTopList(int count){
|
||||
return new TopList<>(count);
|
||||
}
|
||||
|
||||
public Path<T> getFastPathTo(T entry){
|
||||
Vertex<T> target = getVertex(entry);
|
||||
if (target == null) return null;
|
||||
return findFastPath(pathFabric.build(root), target, target.getLevel()+1, stock);
|
||||
}
|
||||
|
||||
private Path<T> findFastPath(Path<T> head, Vertex<T> target, int deep, double limit) {
|
||||
Vertex<T> source = head.getTarget();
|
||||
LOG.trace("Find fast path from {} to {}, deep {}, limit {}, head {}", source, target, deep, limit, head);
|
||||
DistanceFilter distanceFilter = new DistanceFilter(limit, source.getEntry());
|
||||
if (deep == source.getLevel()){
|
||||
Optional<Edge<T>> last = source.getEdges().parallelStream()
|
||||
.filter(next -> next.isConnect(target) && distanceFilter.test(next.getLength()) && !head.isConnect(next.getTarget()))
|
||||
.findFirst();
|
||||
if (last.isPresent()){
|
||||
Path<T> path = head.connectTo(last.get().getTarget(), limit < last.get().getLength());
|
||||
path.finish();
|
||||
LOG.trace("Last edge find, path {}", path);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
if (deep < source.getLevel()){
|
||||
LOG.trace("Search around");
|
||||
Optional<Path<T>> res = source.getEdges().parallelStream()
|
||||
.filter(next -> next.getTarget().getLevel() < source.getLevel() && distanceFilter.test(next.getLength()))
|
||||
.map((next) -> {
|
||||
Path<T> path = head.connectTo(next.getTarget(), limit < next.getLength());
|
||||
double nextLimit = withRefill ? limit - next.getLength(): stock;
|
||||
// refill
|
||||
if (nextLimit < 0 ) nextLimit = stock - next.getLength();
|
||||
return findFastPath(path, target, deep, nextLimit);
|
||||
})
|
||||
.filter(path -> path != null)
|
||||
.findFirst();
|
||||
if (res.isPresent()) return res.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public T getRoot() {
|
||||
return root.getEntry();
|
||||
}
|
||||
|
||||
public int getMinJumps() {
|
||||
return minJumps;
|
||||
}
|
||||
|
||||
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 <= Math.min(limit, maxDistance) || (withRefill && distance <= maxDistance && source.canRefill());
|
||||
}
|
||||
}
|
||||
|
||||
private class GraphBuilder extends RecursiveAction {
|
||||
private final Vertex<T> vertex;
|
||||
private final Collection<T> set;
|
||||
private final int deep;
|
||||
private final double limit;
|
||||
private final DistanceFilter distanceFilter;
|
||||
|
||||
private GraphBuilder(Vertex<T> vertex, Collection<T> set, int deep, double limit) {
|
||||
this.vertex = vertex;
|
||||
this.set = set;
|
||||
this.deep = deep;
|
||||
this.limit = limit;
|
||||
distanceFilter = new DistanceFilter(limit, vertex.getEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
LOG.trace("Build graph from {}, limit {}, deep {}", vertex, limit, deep);
|
||||
ArrayList<GraphBuilder> subTasks = new ArrayList<>(set.size());
|
||||
Iterator<T> iterator = set.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (callback.isCancel()) break;
|
||||
T entry = iterator.next();
|
||||
if (entry == vertex.getEntry()) continue;
|
||||
double distance = vertex.getEntry().getDistance(entry);
|
||||
if (distanceFilter.test(distance)) {
|
||||
Vertex<T> next = vertexes.get(entry);
|
||||
if (next == null) {
|
||||
LOG.trace("Is new vertex");
|
||||
next = new Vertex<>(entry);
|
||||
vertexes.put(entry, next);
|
||||
}
|
||||
LOG.trace("Add edge from {} to {}", vertex, next);
|
||||
vertex.addEdge(new Edge<>(vertex, next));
|
||||
// If level >= deep when vertex already added on upper deep
|
||||
if (next.getLevel() < deep) {
|
||||
next.setLevel(vertex.getLevel() - 1);
|
||||
if (deep > 0) {
|
||||
double nextLimit = withRefill ? limit - distance : stock;
|
||||
if (nextLimit < 0) {
|
||||
LOG.trace("Refill");
|
||||
nextLimit = stock - distance;
|
||||
}
|
||||
//Recursive build
|
||||
GraphBuilder task = new GraphBuilder(next, set, deep - 1, nextLimit);
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Vertex {} is far away, {}", entry, distance);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private class PathFinder extends RecursiveAction {
|
||||
private final TopList<Path<T>> paths;
|
||||
private final Path<T> head;
|
||||
private final Vertex<T> target;
|
||||
private final int deep;
|
||||
private final double limit;
|
||||
private final DistanceFilter distanceFilter;
|
||||
|
||||
private PathFinder(TopList<Path<T>> paths, Path<T> head, Vertex<T> target, int deep, double limit) {
|
||||
this.paths = paths;
|
||||
this.head = head;
|
||||
this.target = target;
|
||||
this.deep = deep;
|
||||
this.limit = limit;
|
||||
distanceFilter = new DistanceFilter(limit, head.getTarget().getEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void compute() {
|
||||
if (target == null || isCancelled()) return;
|
||||
Vertex<T> source = head.getTarget();
|
||||
LOG.trace("Find path to deep from {} to {}, deep {}, limit {}, head {}", source, target, deep, limit, head);
|
||||
Edge<T> edge = source.getEdge(target);
|
||||
if (edge != null){
|
||||
if (distanceFilter.test(edge.getLength())){
|
||||
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 (deep > 0 ){
|
||||
if (source.getEdgesCount() > 0){
|
||||
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;
|
||||
if (!distanceFilter.test(next.getLength())) continue;
|
||||
Path<T> path = head.connectTo(next.getTarget(), limit < next.getLength());
|
||||
double nextLimit = withRefill ? limit - next.getLength() : stock;
|
||||
// refill
|
||||
if (nextLimit < 0) nextLimit = stock - next.getLength();
|
||||
//Recursive search
|
||||
PathFinder task = new PathFinder(paths, path, target, deep - 1, nextLimit);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
import ru.trader.analysis.graph.Vertex;
|
||||
|
||||
public class GraphCallBack<T extends Connectable<T>> {
|
||||
|
||||
private volatile boolean cancel = false;
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class Path<T extends Connectable<T>> {
|
||||
private final Path<T> head;
|
||||
private final Vertex<T> target;
|
||||
private boolean refill;
|
||||
|
||||
|
||||
public Path(Vertex<T> source) {
|
||||
this.head = null;
|
||||
this.target = source;
|
||||
this.refill = false;
|
||||
}
|
||||
|
||||
protected Path(Path<T> head, Vertex<T> vertex, boolean refill) {
|
||||
this.head = head;
|
||||
this.target = vertex;
|
||||
this.refill = refill;
|
||||
}
|
||||
|
||||
public Path<T> connectTo(Vertex<T> vertex, boolean refill){
|
||||
return new Path<>(this, vertex, refill);
|
||||
}
|
||||
|
||||
protected void finish(){
|
||||
if (!isRoot()){
|
||||
head.finish();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRoot(){
|
||||
return head == null;
|
||||
}
|
||||
|
||||
public Path<T> getRoot(){
|
||||
if (isRoot()) return this;
|
||||
return head.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Path)) return false;
|
||||
|
||||
Path path = (Path) o;
|
||||
return (isRoot() ? path.isRoot() : head.equals(path.head)) && target.equals(path.target);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = head != null ? head.hashCode() : 0;
|
||||
result = 31 * result + target.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
if (isRoot()) return target.getEntry().toString();
|
||||
final StringBuilder sb = new StringBuilder(head.toString());
|
||||
if (refill) sb.append("(R)");
|
||||
sb.append(" -> ").append(target.getEntry());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean isConnect(Vertex<T> vertex) {
|
||||
return target.equals(vertex) || (!isRoot() && head.isConnect(vertex));
|
||||
}
|
||||
|
||||
public boolean isConnect(T entry) {
|
||||
return target.getEntry().equals(entry) || (!isRoot() && head.isConnect(entry));
|
||||
}
|
||||
|
||||
public boolean isPathFrom(T entry) {
|
||||
return !isRoot() && head.target.getEntry().equals(entry);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T extends Connectable<T>> Path<T> toPath(T... items){
|
||||
T t = items[0];
|
||||
Path<T> path = new Path<>(new Vertex<>(t));
|
||||
for (int i = 1; i < items.length; i++) {
|
||||
t = items[i];
|
||||
path = new Path<>(path, new Vertex<>(t), false);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public Vertex<T> getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return target.getEntry();
|
||||
}
|
||||
|
||||
public boolean isRefill(){
|
||||
return refill;
|
||||
}
|
||||
|
||||
public void setRefill(boolean refill) {
|
||||
this.refill = refill;
|
||||
}
|
||||
|
||||
protected Path<T> getPrevious(){
|
||||
return head;
|
||||
}
|
||||
|
||||
public int getLength(){
|
||||
return isRoot() ? 0 : 1 + getPrevious().getLength();
|
||||
}
|
||||
|
||||
public Collection<T> getEntries(){
|
||||
Collection<T> res = new HashSet<>();
|
||||
res.add(target.getEntry());
|
||||
Path<T> p = getPrevious();
|
||||
while (p != null){
|
||||
res.add(p.target.getEntry());
|
||||
p = p.getPrevious();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public boolean contains(Collection<T> items){
|
||||
if (items.isEmpty()) return true;
|
||||
Collection<T> remains = new ArrayList<>(items);
|
||||
remains.remove(target.getEntry());
|
||||
if (isRoot()) {
|
||||
return remains.isEmpty();
|
||||
}
|
||||
return getPrevious().contains(remains);
|
||||
}
|
||||
}
|
||||
@@ -1,446 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.core.Offer;
|
||||
import ru.trader.core.Order;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class PathRoute extends Path<Vendor> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(PathRoute.class);
|
||||
|
||||
private final ArrayList<Order> orders = new ArrayList<>();
|
||||
private final boolean byAvg;
|
||||
private double profit = 0;
|
||||
private double balance = 0;
|
||||
private double distance = 0;
|
||||
private int landsCount = 0;
|
||||
private PathRoute tail;
|
||||
public final static Order TRANSIT = null;
|
||||
|
||||
public PathRoute(Vertex<Vendor> source) {
|
||||
this(source, false);
|
||||
}
|
||||
|
||||
public static PathRoute buildAvg(Vertex<Vendor> source){
|
||||
return new PathRoute(source, true);
|
||||
}
|
||||
|
||||
private PathRoute(Vertex<Vendor> source, boolean byAvg) {
|
||||
super(source);
|
||||
this.byAvg = byAvg;
|
||||
}
|
||||
|
||||
|
||||
private PathRoute(PathRoute head, Vertex<Vendor> vertex, boolean refill) {
|
||||
super(head, vertex, refill);
|
||||
assert head.tail == null;
|
||||
head.tail = this;
|
||||
byAvg = head.byAvg;
|
||||
//transit
|
||||
orders.add(TRANSIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path<Vendor> connectTo(Vertex<Vendor> vertex, boolean refill) {
|
||||
LOG.trace("Connect path {} to {}", this, vertex);
|
||||
return new PathRoute(this.getCopy(), vertex, refill);
|
||||
}
|
||||
|
||||
public PathRoute add(PathRoute path, boolean noSort) {
|
||||
LOG.trace("Add path {} to {}", path, this);
|
||||
PathRoute res = this;
|
||||
path = path.getRoot();
|
||||
if (!path.getTarget().equals(getTarget())){
|
||||
LOG.trace("Is not connected path, add edge from {} to {}", path.getTarget(), getTarget());
|
||||
res = new PathRoute(res, path.getTarget(), true);
|
||||
res.updateDistance();
|
||||
}
|
||||
while (path.hasNext()){
|
||||
path = path.getNext();
|
||||
res = new PathRoute(res, path.getTarget(), path.isRefill());
|
||||
if (noSort){
|
||||
copyField(path, res);
|
||||
}
|
||||
}
|
||||
if (noSort){
|
||||
update();
|
||||
} else {
|
||||
res.finish();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void copyField(PathRoute source, PathRoute dest){
|
||||
dest.orders.clear();
|
||||
dest.orders.addAll(source.getOrders());
|
||||
dest.distance = source.distance;
|
||||
dest.profit = source.profit;
|
||||
dest.balance = source.balance;
|
||||
dest.landsCount = source.landsCount;
|
||||
}
|
||||
|
||||
public PathRoute getCopy(){
|
||||
return getCopy(false);
|
||||
}
|
||||
|
||||
public PathRoute getCopy(boolean withOrders){
|
||||
PathRoute path = getRoot();
|
||||
PathRoute res = new PathRoute(path.getTarget(), path.byAvg);
|
||||
if (withOrders) {
|
||||
copyField(path, res);
|
||||
}
|
||||
while (path.hasNext()){
|
||||
path = path.getNext();
|
||||
res = new PathRoute(res, path.getTarget(), path.isRefill());
|
||||
if (withOrders) {
|
||||
copyField(path, res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void addOrder(Order order){
|
||||
LOG.trace("Add order {} to path {}", order, this);
|
||||
orders.add(order);
|
||||
}
|
||||
|
||||
public void refresh(){
|
||||
getEnd().finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
if (!isRoot()){
|
||||
fillOrders();
|
||||
getPrevious().finish();
|
||||
}
|
||||
updateDistance();
|
||||
}
|
||||
|
||||
private void update(){
|
||||
PathRoute p = this;
|
||||
p.updateBalance();
|
||||
while (p.hasNext()){
|
||||
p = p.getNext();
|
||||
p.updateBalance();
|
||||
}
|
||||
p = this;
|
||||
p.updateProfit();
|
||||
p.updateLandsCount();
|
||||
while (!p.isRoot()){
|
||||
p = p.getPrevious();
|
||||
p.updateProfit();
|
||||
p.updateLandsCount();
|
||||
}
|
||||
p.updateDistance();
|
||||
}
|
||||
|
||||
private void fillOrders(){
|
||||
orders.clear();
|
||||
orders.add(TRANSIT);
|
||||
LOG.trace("Fill orders of path {}", this);
|
||||
Vendor seller = getPrevious().get();
|
||||
for (Offer sell : seller.getAllSellOffers()) {
|
||||
PathRoute p = this;
|
||||
while (p != null) {
|
||||
Vendor buyer = p.get();
|
||||
Offer buy = buyer.getBuy(sell.getItem());
|
||||
if (buy != null){
|
||||
Order order = new Order(sell, buy, 1);
|
||||
if (order.getProfit() <= 0) {
|
||||
LOG.trace("{} - is no profit, skip", order);
|
||||
} else {
|
||||
addOrder(order);
|
||||
}
|
||||
}
|
||||
p = p.getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Order> getOrders() {
|
||||
return Collections.unmodifiableList(orders);
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return orders.size() <= 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathRoute getPrevious() {
|
||||
return (PathRoute) super.getPrevious();
|
||||
}
|
||||
|
||||
public PathRoute getNext() {
|
||||
return tail;
|
||||
}
|
||||
|
||||
public boolean hasNext(){
|
||||
return tail != null;
|
||||
}
|
||||
|
||||
public void sort(double balance, long cargo){
|
||||
// start on root only
|
||||
if (isRoot()){
|
||||
this.balance = balance;
|
||||
if (hasNext()){
|
||||
getNext().forwardSort(cargo);
|
||||
}
|
||||
} else {
|
||||
getPrevious().sort(balance, cargo);
|
||||
}
|
||||
}
|
||||
|
||||
private void forwardSort(long cargo){
|
||||
updateBalance();
|
||||
boolean needSort = false;
|
||||
for (Order order : orders) {
|
||||
if (order == TRANSIT) continue;
|
||||
if (order.getCount() < cargo){
|
||||
needSort = true;
|
||||
order.setMax(balance, cargo);
|
||||
}
|
||||
}
|
||||
if (needSort){
|
||||
LOG.trace("Simple sort");
|
||||
orders.sort(this::simpleCompareOrders);
|
||||
LOG.trace("New order of orders {}", orders);
|
||||
}
|
||||
if (hasNext()){
|
||||
getNext().forwardSort(cargo);
|
||||
} else {
|
||||
LOG.trace("Start back sort");
|
||||
Order best = orders.get(0);
|
||||
profit = best == TRANSIT ? 0 : best.getProfit();
|
||||
LOG.trace("Max profit from {} = {}", getPrevious().get(), profit);
|
||||
updateLandsCount();
|
||||
getPrevious().backwardSort();
|
||||
}
|
||||
}
|
||||
|
||||
private void backwardSort(){
|
||||
orders.sort(byAvg ? this::compareByAvgProfit : this::compareOrders);
|
||||
LOG.trace("New order of orders {}", orders);
|
||||
updateProfit();
|
||||
updateLandsCount();
|
||||
if (!isRoot())
|
||||
getPrevious().backwardSort();
|
||||
}
|
||||
|
||||
private void updateBalance() {
|
||||
PathRoute p = getPrevious();
|
||||
balance = p.balance;
|
||||
if (!p.isRoot()) {
|
||||
Vendor buyer = p.get();
|
||||
while (!p.isRoot()){
|
||||
for (Order order : p.orders) {
|
||||
if (order == TRANSIT) continue;
|
||||
if (order.isBuyer(buyer) && balance < p.balance + order.getProfit()){
|
||||
balance = p.balance + order.getProfit();
|
||||
LOG.trace("Order {} is best to {}, new balance {}", order, buyer, balance);
|
||||
}
|
||||
|
||||
}
|
||||
p = p.getPrevious();
|
||||
}
|
||||
}
|
||||
LOG.trace("Max balance on {} = {}", getPrevious().get(), balance);
|
||||
}
|
||||
|
||||
|
||||
private void updateProfit() {
|
||||
Order best = orders.isEmpty()? TRANSIT : orders.get(0);
|
||||
if (best == TRANSIT) profit = getTransitProfit();
|
||||
else profit = getProfit(best);
|
||||
LOG.trace("Max profit from {} = {}", isRoot() ? get() : getPrevious().get(), profit);
|
||||
}
|
||||
|
||||
private double getTransitProfit(){
|
||||
return hasNext() ? getNext().getProfit() : 0;
|
||||
}
|
||||
|
||||
public double getProfit(){
|
||||
return profit;
|
||||
}
|
||||
|
||||
public double getAvgProfit(){
|
||||
return isRoot()? profit/landsCount : getPrevious().getAvgProfit();
|
||||
}
|
||||
|
||||
public double getBalance() {
|
||||
return balance;
|
||||
}
|
||||
|
||||
public double getProfit(Order order){
|
||||
return getProfit(order, true);
|
||||
}
|
||||
|
||||
private double getProfit(Order order, boolean first){
|
||||
if (order == TRANSIT) return getTransitProfit();
|
||||
if (isPathFrom(order.getBuyer())) {
|
||||
return first ? order.getProfit() : order.getProfit() + profit;
|
||||
}
|
||||
return hasNext() ? getNext().getProfit(order, false) : order.getProfit();
|
||||
}
|
||||
|
||||
private int simpleCompareOrders(Order o1, Order o2){
|
||||
if (o1 != TRANSIT && o2 != TRANSIT){
|
||||
return o2.compareTo(o1);
|
||||
}
|
||||
return o1 == TRANSIT ? o2 == TRANSIT ? 0 : Double.compare(o2.getProfit(), 0) : Double.compare(0, o1.getProfit());
|
||||
}
|
||||
|
||||
private int compareOrders(Order o1, Order o2){
|
||||
if (o1 != TRANSIT && o2 != TRANSIT){
|
||||
if (!hasNext() || o1.isBuyer(o2.getBuyer()))
|
||||
return o2.compareTo(o1);
|
||||
}
|
||||
double profit1 = getProfit(o1);
|
||||
double profit2 = getProfit(o2);
|
||||
return Double.compare(profit2, profit1);
|
||||
}
|
||||
|
||||
private int compareByAvgProfit(Order o1, Order o2){
|
||||
if (o1 != TRANSIT && o2 != TRANSIT){
|
||||
if (!hasNext() || o1.isBuyer(o2.getBuyer()))
|
||||
return o2.compareTo(o1);
|
||||
}
|
||||
double profit1 = getProfit(o1)/computeLandsCount(o1);
|
||||
double profit2 = getProfit(o2)/computeLandsCount(o2);
|
||||
return Double.compare(profit2, profit1);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PathRoute getRoot() {
|
||||
return (PathRoute) super.getRoot();
|
||||
}
|
||||
|
||||
public PathRoute getEnd() {
|
||||
return hasNext()? getNext().getEnd() : this;
|
||||
}
|
||||
|
||||
public Order getBest(){
|
||||
if (orders.isEmpty()) return null;
|
||||
return orders.get(0);
|
||||
}
|
||||
|
||||
private double computeDistance(){
|
||||
if (isRoot()){
|
||||
double res = 0;
|
||||
PathRoute p = this;
|
||||
while (p.hasNext()){
|
||||
p = p.getNext();
|
||||
res += p.computeDistance();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
else return getPrevious().get().getDistance(get());
|
||||
}
|
||||
|
||||
private void updateDistance(){
|
||||
this.distance = computeDistance();
|
||||
}
|
||||
|
||||
public double getDistance(){
|
||||
return distance;
|
||||
}
|
||||
|
||||
private int computeLandsCount(Order order){
|
||||
int res = 0;
|
||||
PathRoute p = isRoot()? getNext() : this;
|
||||
while (p.hasNext()){
|
||||
p = p.getNext();
|
||||
// lands for sell
|
||||
if (order != null && p.isPathFrom(order.getBuyer())){
|
||||
LOG.trace("{} is lands for sell by order {}", p, order);
|
||||
return res + p.getLandsCount() + 1;
|
||||
} else {
|
||||
if (order == null){
|
||||
order = p.getBest();
|
||||
if (order != null){
|
||||
LOG.trace("{} is lands for buy by order {}", p, order);
|
||||
return res + p.getLandsCount() + 1;
|
||||
}
|
||||
} else {
|
||||
if (p.isRefill()){
|
||||
LOG.trace("{} is lands for refill", p);
|
||||
res++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
LOG.trace("{} is end, landing", p);
|
||||
res++;
|
||||
return res;
|
||||
}
|
||||
|
||||
private void updateLandsCount(){
|
||||
Order best = isRoot() ? getNext().getBest() : getBest();
|
||||
landsCount = computeLandsCount(best);
|
||||
LOG.trace("Lands count from {} = {}", isRoot() ? get() : getPrevious().get(), landsCount);
|
||||
}
|
||||
|
||||
public int getLandsCount() {
|
||||
return landsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Order order = getBest();
|
||||
if (order != TRANSIT){
|
||||
sb.append(order.getBuy().getItem());
|
||||
sb.append(" (").append(order.getBuyer()).append(") ");
|
||||
}
|
||||
String o = sb.toString();
|
||||
sb = new StringBuilder();
|
||||
if (isRoot()){
|
||||
sb.append(get());
|
||||
if (o.length()>0) sb.append(" (").append(o).append(") ");
|
||||
} else {
|
||||
sb.append(getPrevious().toString());
|
||||
sb.append(" ").append(balance).append(" ");
|
||||
if (isRefill()) sb.append("(R)");
|
||||
if (o.length()>0) sb.append(" (").append(o).append(") ");
|
||||
sb.append(" -> ").append(get());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setOrder(Order order) {
|
||||
orders.set(0, order);
|
||||
}
|
||||
|
||||
public PathRoute dropTo(Vendor vendor){
|
||||
PathRoute p = getCopy(true).getEnd();
|
||||
while (!p.isRoot() && !p.get().equals(vendor)){
|
||||
p = p.getPrevious();
|
||||
}
|
||||
p.tail = null;
|
||||
return p;
|
||||
}
|
||||
|
||||
public static PathRoute toPathRoute(Vendor... items){
|
||||
Vendor t = items[0];
|
||||
PathRoute path = new PathRoute(new Vertex<>(t));
|
||||
for (int i = 1; i < items.length; i++) {
|
||||
t = items[i];
|
||||
path = new PathRoute(path, new Vertex<>(t), false);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isRoute(PathRoute path){
|
||||
return this == path || (isRoot() ? path.isRoot() : !path.isRoot() && getPrevious().isRoute(path.getPrevious()))
|
||||
&& this.getTarget().equals(path.getTarget())
|
||||
&& this.profit == path.profit
|
||||
&& this.balance == path.balance
|
||||
&& (this.getBest() == null && path.getBest() == null || this.getBest().equals(path.getBest()));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class RouteGraph extends Graph<Vendor> {
|
||||
|
||||
private double balance;
|
||||
private int cargo;
|
||||
private boolean groupRes;
|
||||
|
||||
public static Comparator<PathRoute> byProfitComparator = (p1, p2) -> {
|
||||
PathRoute r1 = p1.getRoot();
|
||||
PathRoute r2 = p2.getRoot();
|
||||
int cmp = Double.compare(r2.getAvgProfit(), r1.getAvgProfit());
|
||||
if (cmp != 0 ) return cmp;
|
||||
cmp = Double.compare(r1.getDistance(), r2.getDistance());
|
||||
if (cmp != 0) return cmp;
|
||||
cmp = Double.compare(r1.getLandsCount(), r2.getLandsCount());
|
||||
if (cmp != 0) return cmp;
|
||||
return cmp;
|
||||
};
|
||||
|
||||
public static Comparator<PathRoute> groupByLengthComparator = (p1, p2) -> {
|
||||
int cmp = Integer.compare(p1.getLength(), p2.getLength());
|
||||
if (cmp != 0 ) return cmp;
|
||||
return byProfitComparator.compare(p1, p2);
|
||||
};
|
||||
|
||||
public RouteGraph(Vendor start, Collection<Vendor> set, double stock, double maxDistance, boolean withRefill, int maxDeep) {
|
||||
this(start, set, stock, maxDistance, withRefill, maxDeep, false);
|
||||
}
|
||||
public RouteGraph(Vendor start, Collection<Vendor> set, double stock, double maxDistance, boolean withRefill, int maxDeep, boolean groupRes) {
|
||||
this(start, set, stock, maxDistance, withRefill, maxDeep, groupRes, new GraphCallBack<>());
|
||||
}
|
||||
|
||||
public RouteGraph(Vendor start, Collection<Vendor> set, double stock, double maxDistance, boolean withRefill, int maxDeep, boolean groupRes, GraphCallBack<Vendor> callback) {
|
||||
super(start, set, stock, maxDistance, withRefill, maxDeep, groupRes ? PathRoute::buildAvg : PathRoute::new, callback);
|
||||
if (groupRes){
|
||||
this.groupRes = maxDeep > minJumps;
|
||||
}
|
||||
}
|
||||
|
||||
public void setBalance(double balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public void setCargo(int cargo) {
|
||||
this.cargo = cargo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TopList<Path<Vendor>> newTopList(int count) {
|
||||
int groupSize = 0;
|
||||
if (groupRes && getMinJumps() > 1){
|
||||
groupSize = Math.floorDiv(count, root.getLevel());
|
||||
}
|
||||
return new TopRoutes(count, groupSize);
|
||||
}
|
||||
|
||||
private class TopRoutes extends TopList<Path<Vendor>> {
|
||||
private final int groupSize;
|
||||
|
||||
public TopRoutes(int limit, int groupSize) {
|
||||
super(limit, (p1, p2) -> groupSize > 0 ? groupByLengthComparator.compare((PathRoute)p1, (PathRoute)p2) : RouteGraph.byProfitComparator.compare((PathRoute)p1, (PathRoute)p2));
|
||||
this.groupSize = groupSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Path<Vendor> entry) {
|
||||
if (comparator != null){
|
||||
((PathRoute)entry).sort(balance, cargo);
|
||||
}
|
||||
if (groupSize>0){
|
||||
addToGroupTop(list, entry, limit, comparator, (e) -> e.getLength()-1, groupSize);
|
||||
} else {
|
||||
if (comparator != null){
|
||||
addToTop(list, entry, limit, comparator);
|
||||
} else {
|
||||
if (list.size() >= limit) return false;
|
||||
list.add(entry);
|
||||
if (list.size() >= limit) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.RecursiveTask;
|
||||
|
||||
public class RouteSearcher {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(RouteSearcher.class);
|
||||
private final static ForkJoinPool POOL = new ForkJoinPool();
|
||||
private final static int THRESHOLD = (int) Math.ceil(Runtime.getRuntime().availableProcessors()/2.0);
|
||||
|
||||
private final RouteSearcherCallBack callback;
|
||||
private final double maxDistance;
|
||||
private final double stock;
|
||||
private final int segmentSize;
|
||||
|
||||
public RouteSearcher(double maxDistance, double stock) {
|
||||
this(maxDistance, stock, 0, new RouteSearcherCallBack());
|
||||
}
|
||||
public RouteSearcher(double maxDistance, double stock, int segmentSize, RouteSearcherCallBack callback) {
|
||||
this.maxDistance = maxDistance;
|
||||
this.stock = stock;
|
||||
this.segmentSize = segmentSize;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public List<PathRoute> getPaths(Vendor from, Vendor to, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit){
|
||||
return POOL.invoke(new SegmentSearcher(from, to, vendors, jumps, balance, cargo, limit));
|
||||
}
|
||||
|
||||
public List<PathRoute> getPaths(Vendor from, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit){
|
||||
return POOL.invoke(new SegmentSearcher(from, null, vendors, jumps, balance, cargo, limit));
|
||||
}
|
||||
|
||||
public class SegmentSearcher extends RecursiveTask<List<PathRoute>> {
|
||||
protected final Vendor source;
|
||||
protected final Vendor target;
|
||||
protected final Collection<Vendor> vendors;
|
||||
protected final int jumps;
|
||||
protected final double balance;
|
||||
protected final int cargo;
|
||||
protected int limit;
|
||||
|
||||
public SegmentSearcher(Vendor source, Vendor target, Collection<Vendor> vendors, int jumps, double balance, int cargo, int limit) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.vendors = vendors;
|
||||
this.jumps = jumps;
|
||||
this.balance = balance;
|
||||
this.cargo = cargo;
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<PathRoute> compute() {
|
||||
if (callback.isCancel()) return Collections.emptyList();
|
||||
LOG.trace("Start search route to {} from {}, jumps {}", source, target, jumps);
|
||||
GraphCallBack<Vendor> gCallBack = callback.onStart();
|
||||
RouteGraph sGraph = new RouteGraph(source, vendors, stock, maxDistance, true, jumps, true, gCallBack);
|
||||
int jumpsToAll = sGraph.getMinJumps();
|
||||
LOG.trace("Segment jumps {}", jumpsToAll);
|
||||
sGraph.setCargo(cargo);
|
||||
sGraph.setBalance(balance);
|
||||
TopList<PathRoute> res = new TopList<>(limit, RouteGraph.byProfitComparator);
|
||||
if (jumps <= jumpsToAll){
|
||||
LOG.trace("Is last segment");
|
||||
List<Path<Vendor>> paths;
|
||||
if (target == null){
|
||||
paths = sGraph.getPaths(limit);
|
||||
} else {
|
||||
paths = sGraph.getPathsTo(target, limit);
|
||||
}
|
||||
for (Path<Vendor> path : paths) {
|
||||
res.add((PathRoute) path);
|
||||
}
|
||||
} else {
|
||||
LOG.trace("Split to segments");
|
||||
List<Path<Vendor>> paths = sGraph.getPaths(getPathsOnSegmentCount(sGraph), jumpsToAll).getList();
|
||||
int i = 0;
|
||||
ArrayList<SegmentSearcher> subTasks = new ArrayList<>(THRESHOLD);
|
||||
while (i < paths.size()) {
|
||||
if (target != null){
|
||||
PathRoute path = (PathRoute) paths.get(i);
|
||||
if (path.getTarget().isEntry(target)){
|
||||
LOG.trace("Is path to target, add to res");
|
||||
res.add(path);
|
||||
}
|
||||
}
|
||||
if (callback.isCancel()) break;
|
||||
subTasks.clear();
|
||||
for (int taskIndex = 0; taskIndex < THRESHOLD && i+taskIndex < paths.size(); taskIndex++) {
|
||||
if (callback.isCancel()) break;
|
||||
PathRoute path = (PathRoute) paths.get(i+taskIndex);
|
||||
double newBalance = balance + path.getRoot().getProfit();
|
||||
SegmentSearcher task = new SegmentSearcher(path.get(), target, vendors, jumps - path.getLength(), newBalance, cargo, 1);
|
||||
task.fork();
|
||||
subTasks.add(task);
|
||||
}
|
||||
for (int taskIndex = 0; taskIndex < subTasks.size(); taskIndex++) {
|
||||
PathRoute path = (PathRoute) paths.get(i+taskIndex);
|
||||
add(subTasks.get(taskIndex), path, res);
|
||||
}
|
||||
i+=subTasks.size();
|
||||
}
|
||||
}
|
||||
res.finish();
|
||||
callback.onEnd(gCallBack);
|
||||
return res.getList();
|
||||
}
|
||||
|
||||
private int getPathsOnSegmentCount(RouteGraph graph){
|
||||
if (segmentSize ==0){
|
||||
return graph.vertexes.size()*graph.minJumps;
|
||||
} else {
|
||||
return segmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void add(SegmentSearcher task, PathRoute path, TopList<PathRoute> res){
|
||||
if (callback.isCancel()){
|
||||
task.cancel(true);
|
||||
return;
|
||||
}
|
||||
List<PathRoute> tail = task.join();
|
||||
if (tail.isEmpty()){
|
||||
LOG.trace("Not found route from {} to {}, jumps {}", task.source, task.target, task.jumps);
|
||||
} else {
|
||||
path = path.add(tail.get(0), false);
|
||||
path.sort(balance, cargo);
|
||||
res.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TopList<T> {
|
||||
protected final List<T> list;
|
||||
protected final int limit;
|
||||
protected final Comparator<T> comparator;
|
||||
|
||||
public TopList(int limit) {
|
||||
this(limit, null);
|
||||
}
|
||||
|
||||
|
||||
public TopList(int limit, Comparator<T> comparator) {
|
||||
this.list = new ArrayList<>(limit);
|
||||
this.limit = limit;
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
//return true if is last entry or list is full
|
||||
public boolean add(T entry){
|
||||
if (comparator != null){
|
||||
addToTop(list, entry, limit, comparator);
|
||||
} else {
|
||||
if (list.size() >= limit) return false;
|
||||
list.add(entry);
|
||||
if (list.size() >= limit) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void finish(){
|
||||
if (comparator != null && list.size() < limit)
|
||||
list.sort(comparator);
|
||||
}
|
||||
|
||||
public List<T> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public static <T> void addToGroupTop(List<T> list, T entry, int limit, Comparator<T> comparator, Function<T, Integer> getGroup, int groupSize) {
|
||||
boolean isFull = list.size() >= limit;
|
||||
int group = getGroup.apply(entry);
|
||||
int groupStart = groupSize * group;
|
||||
int groupEnd = groupSize * (group + 1);
|
||||
if (!isFull){
|
||||
if (groupStart >= list.size()) groupStart = list.size();
|
||||
if (groupEnd >= list.size()) groupEnd = list.size();
|
||||
}
|
||||
List<T> groupList = list.subList(groupStart, groupEnd);
|
||||
T removeEntry = addToTop(groupList, entry, groupSize, comparator);
|
||||
if (!isFull && removeEntry != null && group != getGroup.apply(removeEntry)){
|
||||
addToGroupTop(list, removeEntry, limit, comparator, getGroup, groupSize);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T addToTop(List<T> list, T entry, int limit, Comparator<T> comparator) {
|
||||
if (list.size() == limit) {
|
||||
int index = Collections.binarySearch(list, entry, comparator);
|
||||
if (index < 0) index = -1 - index;
|
||||
if (index == limit || list.get(index).equals(entry)) return null;
|
||||
list.add(index, entry);
|
||||
return list.remove(limit);
|
||||
|
||||
} else {
|
||||
if (list.size() < limit - 1) {
|
||||
list.add(entry);
|
||||
} else {
|
||||
list.add(entry);
|
||||
list.sort(comparator);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> void addAllToTop(List<T> list, Collection<T> sortEntries, int limit, Comparator<T> comparator) {
|
||||
for (T entry : sortEntries) {
|
||||
if (list.size() == limit) {
|
||||
int index = Collections.binarySearch(list, entry, comparator);
|
||||
if (index < 0) index = -1 - index;
|
||||
if (index == limit || list.get(index).equals(entry)) return;
|
||||
list.add(index, entry);
|
||||
list.remove(limit);
|
||||
} else {
|
||||
if (list.size() < limit - 1) {
|
||||
list.add(entry);
|
||||
} else {
|
||||
list.add(entry);
|
||||
list.sort(comparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package ru.trader.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class Vertex<T extends Connectable<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);
|
||||
}
|
||||
|
||||
public boolean isConnected(Vertex<T> other){
|
||||
return isConnected(other.entry);
|
||||
}
|
||||
|
||||
public boolean isConnected(T other){
|
||||
return edges.stream().anyMatch((e) -> e.isConnect(other));
|
||||
}
|
||||
|
||||
public void addEdge(Edge<T> edge){
|
||||
synchronized (edges){
|
||||
if (edges.contains(edge)) return;
|
||||
edges.add(edge);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Edge<T>> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
public void sortEdges(Comparator<Edge> comparator){
|
||||
edges.sort(comparator);
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int getEdgesCount(){
|
||||
return edges.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("Vertex{");
|
||||
sb.append(entry);
|
||||
sb.append(", lvl=").append(level);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Edge<T> getEdge(Vertex<T> target) {
|
||||
for (Edge<T> edge : edges) {
|
||||
if (edge.isConnect(target)) return edge;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user