Cluster
package com.tegl.commercial.trade.marketagent.analyzer;
import com.tegl.commercial.trade.marketagent.entities.MarketOrder;
import com.tegl.commercial.trade.marketagent.entities.SS;
import com.tegl.commercial.trade.marketagent.navigation.Navigator;
import com.tegl.commercial.trade.marketagent.navigation.Path;
import com.tegl.commercial.trade.marketagent.util.MAC;
import com.tegl.commercial.trade.marketagent.util.MAUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.*;
/**
*
*
*/
public class Cluster implements Comparable{
private Log log = LogFactory.getLog(this.getClass());
private static int counter = 0;
int id = 0;
// SS.name : ArrayList-MarketOrders
private HashMap cluster = new HashMap();
private int proximity;
private int diameter;
float interestLimit;
int type;
private double score = -1.1;
private ArrayList rejectionsByProximity = new ArrayList();
// List of market orders which were passed into the constructors
// of a cluster. The reason these are important is because later
// clusters will never have been shown these, even tho they could
// be relevant.
private static ArrayList INITIAL_CLUSTER_MOS = new ArrayList();
private int stackDepth = 0;
public static final int BUY_ORDER_CLUSTER = 0;
public static final int SELL_ORDER_CLUSTER = 1;
private Cluster(int proximity, int diameter, float interestLimit, int type, MarketOrder mo, Map neighbors) {
this.proximity = proximity;
this.diameter = diameter;
this.interestLimit = interestLimit;
this.type = type;
id = ++counter;
ArrayList mos = new ArrayList();
mos.add(mo);
cluster.put(mo.systemName, mos);
addNeighbors(neighbors);
if (log.isDebugEnabled()) {
log.debug("new Cluster: " + this);
}
}
static Cluster newCluster(MarketOrder marketOrder, int proximity, int diameter, float interestLimit, int type, Map systemsInProx) {
Cluster c = new Cluster(proximity, diameter, interestLimit, type, marketOrder, systemsInProx);
c.addInitialClusterMos(marketOrder);
return c;
}
static Cluster newCluster(MarketOrder marketOrder, int clusterProximity, int diameter, float interestLimit, int type) {
return newCluster(marketOrder, clusterProximity, diameter, interestLimit, type, null);
}
private void addInitialClusterMos(MarketOrder marketOrder) {
for (int i = 0; i < INITIAL_CLUSTER_MOS.size(); i++) {
MarketOrder mo = (MarketOrder) INITIAL_CLUSTER_MOS.get(i);
try {
// We don't really care how this method call turns out.
// We just want to try to add previous market orders that
// this guy never saw.
addMarketOrder(mo);
} catch (ClusterException e) {
continue; // move on to the next one.
}
}
// Finally add THIS one to the list, if it was passed in:
if (marketOrder != null) {
INITIAL_CLUSTER_MOS.add(marketOrder);
}
}
/**
* Once this method is called, it will continue to return the same value.<p>
*
* This cluster contains n systems. Each system has n MarketOrders. Each MarketOrder
* has a score based on it's quantity and price (relative to the interestLimit).<br>
* The total score of this cluster is the sum total of every Market Order's score:
* <pre>
* score(mo) = (mo.price - interestLimit) * quantity</pre>
* The sign is reversed, if the cluster contains sell orders.
*
* @return the total score of this cluster
*/
public double getScore() {
if (score != -1.1) { // We've already calculated it.
return score;
}
Set clusterKeys = cluster.keySet();
ArrayList mos = null;
for (Iterator i = clusterKeys.iterator(); i.hasNext();) {
mos = (ArrayList) cluster.get(i.next());
for (int j = 0; j < mos.size(); j++) {
MarketOrder mo = (MarketOrder) mos.get(j);
if (type == BUY_ORDER_CLUSTER) {
score += (d(mo.price) - d(interestLimit)) * d(mo.quantity);
}
else if (type == SELL_ORDER_CLUSTER) {
score += (d(interestLimit) - d(mo.price)) * d(mo.quantity);
}
}
}
return score;
}
/**
* Attempts to add a marketOrder (and it's system) to this cluster.<br>
* It will be evaluated based on proximity and diameter settings, and
* either rejected or accepted.<br>
* If rejected, a map of systems<->marketOrders that were in proximity will be returned.
*
* @param mo
* @return
* <ul>
* <li>null if the order was accepted into the cluster </li>
* <li>A map of systems<->marketOrders to which the added system was in proximity if it
* was rejected due to diameter failure</li>
* </ul>
* @throws ClusterException if the attempt was rejected based due to proximity failure
*/
public Map addMarketOrder(MarketOrder mo) throws ClusterException {
if (stackDepth > 0) {
log.debug("> stackDepth = " + stackDepth);
}
SS moSS = SS.ss(mo.ssid);
ArrayList mosForSystem;
// ===================================================================================================
// If this is the first, one, accept it.
// ===================================================================================================
if (cluster.size() == 0) {
mosForSystem = new ArrayList();
mosForSystem.add(mo);
cluster.put(moSS.getName(), mosForSystem);
return null; // accepted
}
// ===================================================================================================
// Check if we've already seen this system and accepted it. If so, add this market order to
// our cluster, and return null.
// ===================================================================================================
mosForSystem = (ArrayList) cluster.get(moSS.getName());
if (mosForSystem != null) {
if (log.isDebugEnabled()) {
track("Accept Prior", mo); // Only if debug enabled
}
if (!mosForSystem.contains(mo)) { // Don't add duplicate mos. Each is unique
mosForSystem.add(mo);
}
return null; // accepted
}
// ===================================================================================================
// Check his proximity to the systems already in the cluster.
// If he is not in proximity of ANY of the other systems in the cluster, he's outta here.
// Either way, save off the list of systems to which he is near - so that means, go through the WHOLE
// cluster. Don't stop when you've found the first match.
// ===================================================================================================
boolean inProximity = false;
Map sysInProx = new HashMap();
Set clusterKeys = cluster.keySet();
for (Iterator i = clusterKeys.iterator(); i.hasNext();) {
String sysName = (String) i.next();
SS ss = (SS) SS.ss(sysName);
Path path = Navigator.findPath(moSS, ss, proximity, 0.0f, 1.0f); // Null if not in proximity
if (path != null) {
sysInProx.put(ss.getName(), cluster.get(ss.getName()));
inProximity = true;
}
}
if (!inProximity) {
track("Rejected (proximity)", mo); // Only if debug enabled
rejectionsByProximity.add(mo); // These are checked later, if a new system is added.
throw new ClusterException();
}
// ===================================================================================================
// If he is not within the diameter of ALL of the other systems in the cluster, he's outta here - but
// return that list of systems to which he is near.
// ===================================================================================================
// If the lists are the same size, we've DEFINITELY got a match, because that means he's within prox
// range of ALL systems in the cluster.
// Add the mo to the list of mo's stored for this system, and return null.
boolean clusterMate = false;
if ((sysInProx.size() == cluster.size())) {
clusterMate = true;
}
// So, if some are in question, go through the systems in the cluster again, and see if they're within
// diameter distance.
else {
clusterMate = true; // Set to true by default
for (Iterator i = clusterKeys.iterator(); i.hasNext();) {
String sysName = (String) i.next();
SS ss = (SS) SS.ss(sysName);
Path path = Navigator.findPath(moSS, ss, diameter, 0.0f, 1.0f); // Null if not in diameter
// If we encounter ANY that are not within range, we're done.
if (path == null) {
track("Rejected (diameter)", mo); // Only if debug enabled
clusterMate = false;
break;
}
}
}
// ===================================================================================================
// Here is the money. We've decided that, on all counts, this guy is a clustermate. Don't ask what
// that is.
// That also means that we should check to see if previous proximity-failures are now within prox
// distance of this new system. If so, we should try to add them. This is a recursive process.
// ===================================================================================================
if (clusterMate) {
track("Placing", mo); // Only if debug enabled
mosForSystem = new ArrayList();
mosForSystem.add(mo);
cluster.put(moSS.getName(), mosForSystem);
if (rejectionsByProximity.contains(mo)) {
rejectionsByProximity.remove(mo);
track("AcceptRej", mo); // Only if debug enabled
}
else {
track("Accepted", mo); // Only if debug enabled
}
log.debug("Looking thru previous rejections...");
for (int i = 0; i < rejectionsByProximity.size(); i++) {
MarketOrder previouslyRejectedOrder = (MarketOrder) rejectionsByProximity.get(i);
SS rejectedSS = SS.ss(previouslyRejectedOrder.ssid);
Path path = Navigator.findPath(moSS, rejectedSS, proximity, 0.0f, 1.0f); // Null if not in proximity
// If it's now within range, try to add it:
if (path != null) {
stackDepth++;
addMarketOrder(previouslyRejectedOrder);
stackDepth--;
log.debug("< stackDepth = " + stackDepth);
}
}
return null;
}
// Otherwise, the market order doesn't belong in this cluster. Get a map of what this guy IS in
// proximity to, and return it.
else {
return sysInProx;
}
}
MarketOrder[] getMarketOrders() {
ArrayList mos = null;
ArrayList allMos = new ArrayList();
Set clusterKeys = cluster.keySet();
for (Iterator i = clusterKeys.iterator(); i.hasNext();) {
mos = (ArrayList) cluster.get(i.next());
allMos.addAll(mos);
}
return (MarketOrder[]) allMos.toArray(new MarketOrder[0]);
}
MarketOrder[] getMarketOrdersFor(String systemName) {
ArrayList mos = (ArrayList) cluster.get(systemName);
return (MarketOrder[]) mos.toArray(new MarketOrder[0]);
}
int getMarketOrderSize() {
Set clusterKeys = cluster.keySet();
int total = 0;
for (Iterator i = clusterKeys.iterator(); i.hasNext();) {
String systemNameKey = (String) i.next();
ArrayList moList = (ArrayList) cluster.get(systemNameKey);
total += moList.size();
}
return total;
}
SS[] getSystems() {
Set clusterKeys = cluster.keySet();
ArrayList systems = new ArrayList();
for (Iterator i = clusterKeys.iterator(); i.hasNext();) {
String sysName = (String) i.next();
systems.add(SS.ss(sysName));
}
return (SS[]) systems.toArray(new SS[systems.size()]);
}
MarketOrder getMarketOrder(int marketOrderId) {
Set clusterKeys = cluster.keySet();
for (Iterator i = clusterKeys.iterator(); i.hasNext();) {
String sysName = (String) i.next();
ArrayList mos = (ArrayList) cluster.get(sysName);
for (int j = 0; j < mos.size(); j++) {
MarketOrder mo = (MarketOrder) mos.get(j);
if (mo.orderId == marketOrderId) {
return mo;
}
}
}
return null;
}
public String toString() {
if (!log.isDebugEnabled()) {
return "" + id + " (Debug off)";
}
StringBuffer buf = new StringBuffer(id + MAC.I);
buf.append(proximity + MAC.I + diameter + MAC.I);
Set systems = cluster.keySet();
for (Iterator i = systems.iterator(); i.hasNext();) {
String sysName = (String) i.next();
ArrayList mos = (ArrayList) cluster.get(sysName);
buf.append(MAUtil.padTrimRight(sysName, 10) + " " + mos.size() + MAC.I);
}
return buf.toString();
}
int getSize() {
return cluster.size();
}
public int getProximity() {
return proximity;
}
public int getDiameter() {
return diameter;
}
public int getType() {
return type;
}
public float getInterestLimit() {
return interestLimit;
}
private void addNeighbors(Map systemsInProx) {
if (systemsInProx != null) {
cluster.putAll(systemsInProx);
}
}
private double d(float f) {
return new Float(f).doubleValue();
}
private double d(int i) {
return new Integer(i).doubleValue();
}
public boolean equals(Object obj) {
if (!(obj instanceof Cluster)) {
return false;
}
// If they're not the same size, they're different.
Cluster other = (Cluster) obj;
if (getSize() != other.getSize()) {
return false;
}
// If they are, check thru all their systems
Set systems = cluster.keySet();
Set otherSystems = other.cluster.keySet();
boolean identical = true;
for (Iterator i = systems.iterator(); i.hasNext();) {
String system = (String) i.next();
if (otherSystems.contains(system)) {
continue;
}
identical = false;
break;
}
return identical;
}
public int hashCode() {
StringBuffer buf = new StringBuffer();
Set systems = cluster.keySet();
for (Iterator i = systems.iterator(); i.hasNext();) {
String sysName = (String) i.next();
buf.append(sysName);
}
return buf.toString().hashCode();
}
public int compareTo(Object o) {
Cluster c = (Cluster) o;
if (getScore() > c.getScore()) {
return -1; // Backwards to ease sorting, which in a Set is ascending
}
else if (getScore() < c.getScore()) {
return 1;
}
else /*if (getScore() == c.getScore())*/ {
return 0;
}
}
// ==========================================================================================
// Debug stuff
// ==========================================================================================
private static int zcounter = 0;
private void track(String note, MarketOrder mo) {
if (!log.isDebugEnabled()) {
return;
}
note = rejectionsByProximity.contains(mo) ? note + "(R)" : note;
long time = (mo != null)? mo.exportDate.getTime() : 11111111111l;
log.debug(MAUtil.padRight(Integer.toString(++zcounter), 4) + " " + MAUtil.padRight(note, 15) + id + MAC.I + mo + MAC.I + time);
}
}