Archived
0

remove old classes

This commit is contained in:
iMoHax
2015-07-17 16:10:58 +03:00
parent 77c931d770
commit 1a4cad61dd
10 changed files with 3 additions and 1422 deletions

View File

@@ -4,6 +4,7 @@ import javafx.application.Platform;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.concurrent.Task;
import ru.trader.analysis.graph.Vertex;
import ru.trader.core.MarketAnalyzer;
import ru.trader.core.MarketAnalyzerCallBack;
import ru.trader.core.Place;
@@ -11,7 +12,6 @@ import ru.trader.core.Vendor;
import ru.trader.graph.Connectable;
import ru.trader.graph.GraphCallBack;
import ru.trader.graph.RouteSearcherCallBack;
import ru.trader.graph.Vertex;
import ru.trader.model.MarketModel;
import ru.trader.view.support.Localization;

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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()));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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;
}
}