package EDU.Washington.CS.Tiber.Analyzer; import EDU.Washington.CS.Tiber.*; import EDU.Washington.CS.Tiber.Analyzer.*; import EDU.Washington.CS.Tiber.TR.STR.SlaveURL; import java.net.*; import java.io.*; import java.util.*; /** * The core Analyzer. */ public class Analyzer { /** Obtains the averages for all paths AB in the historical path listing, and within the time intervals * specified. Note that this consideres AB = BA, and retrieves both sets of data. * *@return A vector of hashtables, the ith table corresponding to the ith timeInterval pair. */ private static Vector getAveragesSymmetric(String nexus, int nexus_port, Vector timeIntervals) throws IOException, IllegalArgumentException, UnknownHostException { if ( timeIntervals.size() %2 != 0 ) { throw new IllegalArgumentException(); } Vector paths = retrieveHistoricalPathListing(nexus,nexus_port); Hashtable tro = null; Vector tros = null; //initialize return tables. Vector toReturn = new Vector(timeIntervals.size()/2); for ( int i = 0; i < timeIntervals.size(); i++ ) { toReturn.addElement( new Hashtable() ); } //for ( int i = 0; i < paths.size(); i++ ) while( paths.size() > 0 ) { Object currentPath = paths.elementAt(0); tros = getTROsBothDirections(nexus,nexus_port, currentPath); //remove both paths from the vector. paths.removeElement(TransmissionSpecification.reverseCanonicalPath(currentPath)); paths.removeElement(currentPath); //Post: Have all TRO's stored for this path section; parse and toss. for ( int t = 0; t < timeIntervals.size(); t += 2 ) { long startTime = ((Long)(timeIntervals.elementAt(t))).longValue(); long endTime = ((Long)(timeIntervals.elementAt(t+1))).longValue(); if ( startTime == -1 || endTime == -1 ) { //Then don't care about the interval. startTime = 0L; endTime = 23 * 60 *60 + 59 * 60 + 59; } // obtain the time specific table. Hashtable pathStats = ((Hashtable)(toReturn.elementAt(t))); path p = ((path)(pathStats.get( currentPath ))); if ( p == null ) { //new path object. p = new path(); p.sourceIP = TransmissionSpecification.getSourceIPFromPath(currentPath); p.targetIP = TransmissionSpecification.getTargetIPFromPath(currentPath); pathStats.put( currentPath, p ); } //for calculating std dev. Vector latencies = new Vector(tros.size() * 3); for ( int j = 0; j < tros.size(); j++ ) { tro = ((Hashtable)(tros.elementAt(j))); if (!isInCurrentTimeInterval(tro, startTime, endTime ) ) { continue; } Vector hops = ((Vector)(tro.get(TransmissionSpecification.KEYWORD_HOPS))); if ( hops.size() > 0 ) { Hashtable lastHop = ((Hashtable)(hops.elementAt(hops.size() - 1))); //if the targetIP is the last IP in the trace (checking for a complete TRO) if ( ((String)(lastHop.get(TransmissionSpecification.KEYWORD_ROUTED_IP))).equals((String)(tro.get(TransmissionSpecification.KEYWORD_TARGET_IP))) ) { //then add in the ping times for the final hop. Vector probes = ((Vector)(lastHop.get(TransmissionSpecification.KEYWORD_PROBES))); for ( int k = 0; k < probes.size(); k++ ) { Hashtable probe = ((Hashtable)(probes.elementAt(k))); Integer curLat = ((Integer)(probe.get(TransmissionSpecification.KEYWORD_ROUND_TRIP_TIME))); latencies.addElement(curLat); int currentLatency = curLat.intValue(); p.totalLatency += ((long)(currentLatency)); p.totalHops += ((Integer)(lastHop.get(TransmissionSpecification.KEYWORD_TTL))).intValue(); p.numHopMeasurements++; p.numLatencies++; }//eo for k } } } //eo for j //Now that we have the average latency for this path, find the standard deviation: // //S = sqrt( (1/(n-1)) * sigma(1,n)(sample_i - mean) ); double averageLatency = p.averageLatency(); double sum = 0.0; for (int j = 0; j < latencies.size(); j++ ) { sum = sum + Math.pow(((Integer)(latencies.elementAt(j))).intValue() - averageLatency,2); } double denominator = ((double)(latencies.size() - 1)); p.variance = ((1.0 / denominator) * sum); p.standardDev = Math.sqrt( p.variance ); //why is this happening? if ( p.variance == Double.NaN || p.standardDev == Double.NaN ) { //throw this one out? debug.print("variance was NaN. p = " + p ); p.variance = p.standardDev = -1; } latencies = null; }//eo for t } //eo while > 0. //Post for: Should have a hashtable of statistics, keyed off of the path canonical form. // System.out.println("\npathStats = " + pathStats); return toReturn; } public static long getBeginningOf868Day(long time) { Date dtime = TimeServer.translate868ToDate(time); Date dbegin = new Date(dtime.getYear(), dtime.getMonth(), dtime.getDate(), 0, 0); return TimeServer.translateDateTo868(dbegin); } /** * Beginning of 868 time, but in PST timezone. */ public static final long PST_TO_GMT_ADD = 8 * 60 * 60; public static final long TIME_ZERO_PST = 0L - 8 * 60 * 60; public static final long LENGTH_OF_DAY_SECONDS = 24 * 60 * 60; /** * Defines a "fringey" containment. If either begin or end time is within the interval, then true is returned. * Remember that stored times are in GMT. * @param startCheckTime A time in seconds, in the interval [0, 23 * 60 + 59 * 60 + 59 * 60]; this time is assumed to be in PST. * @param endCheckTime A time in seconds, in the interval [0, 23 * 60 + 59 * 60 + 59 * 60]this time is assumed to be in PST. * */ public static boolean isInCurrentTimeInterval(Hashtable tro, long startCheckTime, long endCheckTime ) { long troStart = ((Long)(tro.get(TransmissionSpecification.KEYWORD_START_TIME))).longValue(); long troEnd = ((Long)(tro.get(TransmissionSpecification.KEYWORD_END_TIME))).longValue(); long normalizedTROStart = troStart - getBeginningOf868Day(troStart);//troStart - (((long)(troStart / LENGTH_OF_DAY_SECONDS)) * LENGTH_OF_DAY_SECONDS); long normalizedTROEnd = troEnd - getBeginningOf868Day(troEnd);//troEnd - (((long)(troEnd / LENGTH_OF_DAY_SECONDS )) * LENGTH_OF_DAY_SECONDS); if ( (normalizedTROStart >= startCheckTime && normalizedTROStart <= endCheckTime) || (normalizedTROEnd >= startCheckTime && normalizedTROEnd <= endCheckTime) ) { // debug.print("is [" + normalizedTROStart + "," + normalizedTROEnd + "] in [" + startCheckTime + "," + endCheckTime + "]? = true."); return true; } else { // debug.print("is [" + normalizedTROStart + "," + normalizedTROEnd + "] in [" + startCheckTime + "," + endCheckTime + "]? = false."); return false; } } /** * Generates an enormous hashtable containing all traceroutes, into memory. * @return a hashtable of those routes, in hashtable form, each such route containined in a vector keyed off of * the path canonical form. */ public static Hashtable loadGiantDataFile(String filename) throws Exception { Hashtable giant = new Hashtable(); Vector pathOfTROs = null; FileInputStream tros = new FileInputStream(filename); Object currentPath = null; Hashtable h = TransmissionSpecification.getTRO_hashTable(tros); try { while( h != null ) { currentPath = TransmissionSpecification.getCanonicalPathForm( ((String)(h.get(TransmissionSpecification.KEYWORD_SOURCE_IP))), ((String)(h.get(TransmissionSpecification.KEYWORD_TARGET_IP))) ); pathOfTROs = ((Vector)(giant.get(currentPath))); if ( pathOfTROs == null ) { //new vector. pathOfTROs = new Vector(); giant.put(currentPath,pathOfTROs); } pathOfTROs.addElement(h); h = TransmissionSpecification.getTRO_hashTable(tros); } } catch(Exception e) { e.printStackTrace(); } if ( tros != null ) { tros.close(); } return giant; } /** * Performs the triangle test on the given time intervals. These are strict time intervals, * defining particular days or hours, GMT. */ public static void alternatePathTest(String nexus, int nexus_port, Vector timeIntervals ) throws IOException { Vector merges = getAveragesSymmetric(nexus,nexus_port, timeIntervals); for ( int i = 0; i < merges.size(); i += 2 ) { triangleTestAverage( ((Hashtable)(merges.elementAt(i))), "test" + i + "_" + ((Long)(timeIntervals.elementAt(i))).longValue() + "_" + ((Long)(timeIntervals.elementAt(i+1))).longValue() ); } } /** * Performs the bad triangle test, generating a CSV file containing the average "percent betters" on the latency, along * with the variance. This is done over all time values within range. *

* The two times [begin, end] represent a closed interval over which the results will be averaged. Should both of these * values be a -1, all times are taken to be valid. * * @param merged The averages hashtable returned by getAverages, for some period of time, for both AB, BA. * @param fileNameBase The basis filename to use in all output files. */ private static void triangleTestAverage(Hashtable merged, String fileNameBase ) throws IOException { //Find all historical nodes, from those paths. //Hashtable allows O(1) search for previous storage (condensation) Hashtable nodesH = new Hashtable(); for ( Enumeration e = merged.elements(); e.hasMoreElements(); ) { path p = ((path)(e.nextElement())); nodesH.put( p.sourceIP, p.sourceIP ); nodesH.put( p.targetIP, p.targetIP ); } //transfer nodes to a vector, for indexed access. Vector nodes = new Vector(nodesH.size()); for ( Enumeration e = nodesH.keys(); e.hasMoreElements(); ) { nodes.addElement(e.nextElement()); } nodesH = null; //reclaim. int numNodes = nodes.size(); // All triangles predicated on a single path statement, keyed off of the canonical form of that path. Hashtable tri = new Hashtable(); //Pick all paths of size two; A and B. for ( int i = 0; i < numNodes - 1; i++ ) { for ( int j = i + 1; j < numNodes; j++ ) { //Once we have a path A B, for all nodes x in the set of servers, does there exist a path of better cost //A -> x -> B? If so, what is the minimum cost path such, and by how much is it better? for (int k = 0; k < numNodes; k++ ) { if ( k != i && k != j ) { path ab = (path)merged.get( TransmissionSpecification.getCanonicalPathForm( ((String)(nodes.elementAt(i))), ((String)(nodes.elementAt(j))) )); if ( ab == null ) { ab = (path)merged.get( TransmissionSpecification.getCanonicalPathForm( ((String)(nodes.elementAt(j))), ((String)(nodes.elementAt(i))) )); } path aX = (path)merged.get( TransmissionSpecification.getCanonicalPathForm( ((String)(nodes.elementAt(i))), ((String)(nodes.elementAt(k))) )); if ( aX == null ) { aX = (path)merged.get( TransmissionSpecification.getCanonicalPathForm( ((String)(nodes.elementAt(k))), ((String)(nodes.elementAt(i))) )); } path bX = (path)merged.get( TransmissionSpecification.getCanonicalPathForm( ((String)(nodes.elementAt(j))), ((String)(nodes.elementAt(k))) )); if ( bX == null ) { bX = (path)merged.get( TransmissionSpecification.getCanonicalPathForm( ((String)(nodes.elementAt(k))), ((String)(nodes.elementAt(j))) )); } //post clause: we've got the 3 edges of our triangle, where AB = BA. if ( aX != null && ab != null && bX != null && aX.numLatencies != 0 && ab.numLatencies != 0 && bX.numLatencies != 0 ) { triangle t = ((triangle)(tri.get(ab.getCanonicalPathForm()))); if ( t != null ) { //then we've seen this path before; find new minimum. if ( (aX.averageLatency() + bX.averageLatency()) < (t.aX.averageLatency() + t.bX.averageLatency()) ) { //drop the existing point in favor of this. t.aX = aX; t.bX = bX; } } else { try { t = new triangle(ab,aX,bX); tri.put(ab.getCanonicalPathForm(), t); } catch(IllegalArgumentException e) { System.out.println("Triangle creation refused due to zero length path component."); } } } } } //eo for k }//eo for j } //eo for i //post loops; now the best triangles we can be. Let's serialize them. for ( Enumeration e = tri.elements(); e.hasMoreElements(); ) { resultifyAndStore( ((triangle)(e.nextElement())), fileNameBase ); } if ( bw != null ) { bw.flush(); bw.close(); } bw = null; } private static final String COMMA = ","; private static BufferedWriter bw = null; /** * RESULTS NOW IN MS! */ private static void resultifyAndStore(triangle t, String fileNameBase) { try { if ( bw == null ) { bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileNameBase + ".csv"))); } path ab = t.ab; path aX = t.aX; path bX = t.bX; //div by 1000 to convert from mic's to ms. double aXl = aX.averageLatency() / 1000; double abl = ab.averageLatency() / 1000; double bXl = bX.averageLatency() / 1000; double aXs = aX.standardDev / 1000; double abs = ab.standardDev / 1000; double bXs = bX.standardDev / 1000; double aXv = aX.variance / 1000000; double abv = ab.variance / 1000000; double bXv = bX.variance / 1000000; double abh = ab.averageHops(); double aXh = aX.averageHops(); double bXh = bX.averageHops(); StringBuffer buff = new StringBuffer(400); buff.append("ab"); buff.append(COMMA); buff.append(ab.sourceIP); buff.append(":"); buff.append(ab.targetIP); buff.append(COMMA); buff.append("average latency-ms"); buff.append(COMMA); buff.append(abl); buff.append(COMMA); buff.append("std-dev"); buff.append(COMMA); buff.append(abs); buff.append(COMMA); buff.append("variance"); buff.append(COMMA); buff.append(abv); buff.append(COMMA); buff.append("avg hops"); buff.append(COMMA); buff.append(abh); buff.append(COMMA); buff.append("num latency samples"); buff.append(COMMA); buff.append(ab.numLatencies); buff.append(COMMA); buff.append("aX"); buff.append(COMMA); buff.append(aX.sourceIP); buff.append(":"); buff.append(aX.targetIP); buff.append(COMMA); buff.append("average latency-ms"); buff.append(COMMA); buff.append(aXl); buff.append(COMMA); buff.append("std-dev"); buff.append(COMMA); buff.append(aXs); buff.append(COMMA); buff.append("variance"); buff.append(COMMA); buff.append(aXv); buff.append(COMMA); buff.append("avg hops"); buff.append(COMMA); buff.append(aXh); buff.append(COMMA); buff.append("num latency samples"); buff.append(COMMA); buff.append(aX.numLatencies); buff.append(COMMA); buff.append("bX"); buff.append(COMMA); buff.append(bX.sourceIP); buff.append(":"); buff.append(bX.targetIP); buff.append(COMMA); buff.append("average latency-ms"); buff.append(COMMA); buff.append(bXl); buff.append(COMMA); buff.append("std-dev"); buff.append(COMMA); buff.append(bXs); buff.append(COMMA); buff.append("variance"); buff.append(COMMA); buff.append(bXv); buff.append(COMMA); buff.append("avg hops"); buff.append(COMMA); buff.append(bXh); buff.append(COMMA); buff.append("num latency samples"); buff.append(COMMA); buff.append(bX.numLatencies); buff.append(COMMA); buff.append("%better, 2hop vs 1:(ab - (aX + bX))/ ab"); buff.append(COMMA); buff.append(((abl - aXl - bXl) / abl)); buff.append(COMMA); buff.append("magnitude better, 2hop vs 1:ab / (aX + bX)"); buff.append(COMMA); buff.append((abl / (aXl + bXl))); buff.append(COMMA); String toWrite = new String(buff); bw.write(toWrite,0,toWrite.length()); bw.newLine(); } catch(Exception e) { e.printStackTrace(); } } /* CSV (Comma delimited) The CSV (Comma delimited) file format saves only the text and values as they are displayed in cells of the active worksheet. All rows and all characters in each cell are saved. Columns of data are separated by commas, and each row of data ends in a carriage return. If a cell contains a comma, the cell contents are enclosed in double quotation marks. If cells display formulas instead of formula values, the formulas are converted as text. All formatting, graphics, objects, and other worksheet contents are lost. Note If your workbook contains special font characters such as a copyright symbol (©), and you will be using the converted text file on a computer with a different operating system, save the workbook in the text file */ private static Hashtable allAverages = null; /** * @return A hashtable containing all TRO's for both AB and BA paths. */ private static Vector getTROsBothDirections( String nexus, int nexus_port, Object pathToGet ) throws IOException, UnknownHostException { Req req = new Req(); req.requestType = Req.REQ_TYPE_HISTORY; req.historyType = Req.HISTORY_TYPE_PATH; req.history_pathType = Req.HISTORY_PATH_TYPE_RAWDATA; req.sourceIP = TransmissionSpecification.getSourceIPFromPath(pathToGet); req.targetIP = TransmissionSpecification.getTargetIPFromPath(pathToGet); Vector tros1 = TransmissionSpecification.getPathHistory_hashtables( TransmissionSpecification.doServerRequest(nexus,nexus_port,req)); //get the mirror. req.sourceIP = TransmissionSpecification.getTargetIPFromPath(pathToGet); req.targetIP = TransmissionSpecification.getSourceIPFromPath(pathToGet); Vector tros2 = TransmissionSpecification.getPathHistory_hashtables( TransmissionSpecification.doServerRequest(nexus,nexus_port,req)); if ( tros1 == null && tros2 != null ) return tros2; else if ( tros1 != null && tros2 == null ) return tros1; else { //merge for (Enumeration e = tros2.elements(); e.hasMoreElements();) { tros1.addElement(e.nextElement()); } return tros1; } } // private static Hashtable allData = null; /** * Contacts the server for a listing of all available slaves. * * @return The server's slave vector. */ protected static Vector retrieveHistoricalPathListing(String nexus_name, int nexus_port) throws IOException { //Obtain a listing of possible trace sources Req req = new Req(); req.requestType = Req.REQ_TYPE_DETAIL; req.detailType = Req.DETAIL_TYPE_HISTORICAL_PATH_LISTING; Vector paths = TransmissionSpecification.unpackageHistoricalPathListing( TransmissionSpecification.doServerRequest(nexus_name, nexus_port, req)); return paths; } } /** * A triangle represents just that. The idea is to iterate over all * paths AB, and look for a node X that has the property that AX + BX is a minimum for all X. * This is what should be done in the loops. */ class triangle { public static final int COST_METRIC_LATENCY = 0; public static final int COST_METRIC_HOPS = 1; int costMetric = COST_METRIC_LATENCY; public path ab = null; public path aX = null; public path bX = null; /** * The basis path for this triangle. Although duplicate triangles will * arise (ie, involving this link), we care about the triangulation in observation of this particular * pair of points. * * @exception IllegalArgumentException Thrown if any one of the parameters contains a zero latency path. */ public triangle(path ab, path aX, path bX) throws IllegalArgumentException { if ( ab.numLatencies == 0 || aX.numLatencies == 0 || bX.numLatencies == 0 ) { throw new IllegalArgumentException(); } this.ab = ab; this.aX = aX; this.bX = bX; } } /** * path storage structure for statistical purposes. */ class path { public String sourceIP = null; public String targetIP = null; public long startTime = -1; public long endTime = -1; public Object getCanonicalPathForm() { return TransmissionSpecification.getCanonicalPathForm(sourceIP,targetIP); } public long totalLatency = 0; public double standardDev = 0.0; public double variance = 0.0; public int numLatencies = 0; public long totalHops = 0; public long numHopMeasurements = 0; public String toString() { StringBuffer buff = new StringBuffer(300); buff.append(sourceIP); buff.append(":"); buff.append(targetIP); buff.append(":"); buff.append(String.valueOf(totalLatency)); buff.append("/"); buff.append(String.valueOf(numLatencies)); buff.append("="); buff.append(String.valueOf((numLatencies == 0 ? 0 : (totalLatency / numLatencies)))); return new String(buff); } public double averageHops() { return ((double)(totalHops)) / ((double)(numHopMeasurements)); } public double averageLatency() { return ((double)(totalLatency)) / ((double)(numLatencies)); } } /* public static boolean isInCurrentTimeInterval(Hashtable tro, long startCheckTime, long endCheckTime ) { long troStart = ((Long)(tro.get(TransmissionSpecification.KEYWORD_START_TIME))).longValue(); long troEnd = ((Long)(tro.get(TransmissionSpecification.KEYWORD_END_TIME))).longValue(); long normalizedTROStart = troStart - getBeginningOf868Day(troStart);//troStart - (((long)(troStart / LENGTH_OF_DAY_SECONDS)) * LENGTH_OF_DAY_SECONDS); long normalizedTROEnd = troEnd - getBeginningOf868Day(troEnd);//troEnd - (((long)(troEnd / LENGTH_OF_DAY_SECONDS )) * LENGTH_OF_DAY_SECONDS); // debug.print("normalizedTROStart = " + normalizedTROStart + ", normalizedTROEnd = " + normalizedTROEnd ); // debug.print("interval start = " + startCheckTime + ", endCheckTime = " + endCheckTime ); if ( (normalizedTROStart >= startCheckTime && normalizedTROStart <= endCheckTime) || (normalizedTROEnd >= startCheckTime && normalizedTROEnd <= endCheckTime) ) { return true; } else return false; } */ /** * Obtains the averages for all paths AB in the historical path listing, and within the time intervals * specified. Note that this consideres AB = BA, and retrieves both sets of data. * *@return A vector of hashtables, the ith table corresponding to the ith timeInterval pair. */ /* private static Vector getAveragesSymmetric(String nexus, int nexus_port, Vector timeIntervals) throws IOException, IllegalArgumentException, UnknownHostException { if ( timeIntervals.size() %2 != 0 ) { throw new IllegalArgumentException(); } Vector paths = retrieveHistoricalPathListing(nexus,nexus_port); Hashtable tro = null; Vector tros = null; //initialize return tables. Vector toReturn = new Vector(timeIntervals.size()/2); for ( int i = 0; i < timeIntervals.size()/2; i++ ) { toReturn.addElement( new Hashtable() ); } //for ( int i = 0; i < paths.size(); i++ ) while( paths.size() > 0 ) { Object currentPath = paths.elementAt(0); tros = getTROsBothDirections(nexus,nexus_port, currentPath); //remove both paths from the vector. paths.removeElement(TransmissionSpecification.reverseCanonicalPath(currentPath)); paths.removeElement(currentPath); //Post: Have all TRO's stored for this path section; parse and toss. for ( int t = 0; t < timeIntervals.size() - 1; t += 2 ) { long startTime = ((Long)(timeIntervals.elementAt(t))).longValue() ; long endTime = ((Long)(timeIntervals.elementAt(t+1))).longValue(); if ( startTime == -1 || endTime == -1 ) { //Then don't care about the interval. startTime = Long.MIN_VALUE; endTime = Long.MAX_VALUE; } // obtain the time specific table. Hashtable pathStats = ((Hashtable)(toReturn.elementAt(t/2))); path p = ((path)(pathStats.get( currentPath ))); if ( p == null ) { //new path object. p = new path(); p.sourceIP = TransmissionSpecification.getSourceIPFromPath(currentPath); p.targetIP = TransmissionSpecification.getTargetIPFromPath(currentPath); pathStats.put( currentPath, p ); } //for calculating std dev. Vector latencies = new Vector(tros.size() * 3); for ( int j = 0; j < tros.size(); j++ ) { tro = ((Hashtable)(tros.elementAt(j))); //check time interval. if ( ((Long)(tro.get(TransmissionSpecification.KEYWORD_START_TIME))).longValue() >= startTime && ((Long)(tro.get(TransmissionSpecification.KEYWORD_END_TIME))).longValue() <= endTime ) { Vector hops = ((Vector)(tro.get(TransmissionSpecification.KEYWORD_HOPS))); if ( hops.size() > 0 ) { Hashtable lastHop = ((Hashtable)(hops.elementAt(hops.size() - 1))); //if the targetIP is the last IP in the trace (checking for a complete TRO) if ( ((String)(lastHop.get(TransmissionSpecification.KEYWORD_ROUTED_IP))).equals((String)(tro.get(TransmissionSpecification.KEYWORD_TARGET_IP))) ) { //then add in the ping times for the final hop. Vector probes = ((Vector)(lastHop.get(TransmissionSpecification.KEYWORD_PROBES))); for ( int k = 0; k < probes.size(); k++ ) { Hashtable probe = ((Hashtable)(probes.elementAt(k))); Integer curLat = ((Integer)(probe.get(TransmissionSpecification.KEYWORD_ROUND_TRIP_TIME))); latencies.addElement(curLat); int currentLatency = curLat.intValue(); p.totalLatency += ((long)(currentLatency)); p.totalHops += ((Integer)(lastHop.get(TransmissionSpecification.KEYWORD_TTL))).intValue(); p.numHopMeasurements++; p.numLatencies++; }//eo for k } } } } //eo for j //Now that we have the average latency for this path, find the standard deviation: // //S = sqrt( (1/(n-1)) * sigma(1,n)(sample_i - mean) ); double averageLatency = p.averageLatency(); double sum = 0.0; for (int j = 0; j < latencies.size(); j++ ) { sum = sum + Math.pow(((Integer)(latencies.elementAt(j))).intValue() - averageLatency,2); } double denominator = ((double)(latencies.size() - 1)); p.variance = ((1.0 / denominator) * sum); p.standardDev = Math.sqrt( p.variance ); //why is this happening? if ( p.variance == Double.NaN || p.standardDev == Double.NaN ) { //throw this one out? debug.print("variance was NaN. p = " + p ); p.variance = p.standardDev = -1; } latencies = null; }//eo for t } //eo while > 0. //Post for: Should have a hashtable of statistics, keyed off of the path canonical form. // System.out.println("\npathStats = " + pathStats); return toReturn; } */ /* if ( allData == null ) { try { allData = loadGiantDataFile("tro_storage.dat"); }catch(Exception e){e.printStackTrace(); System.exit(1);} } Vector tros1 = ((Vector)(allData.get(pathToGet))); Vector tros2 = ((Vector)(allData.get(TransmissionSpecification.reverseCanonicalPath(pathToGet)))); if ( tros1 == null && tros2 != null ) return tros2; else if ( tros1 != null && tros2 == null ) return tros1; else { //merge for (Enumeration e = tros2.elements(); e.hasMoreElements();) { tros1.addElement(e.nextElement()); } return tros1; } */ /* if ( allData == null ) { try { allData = loadGiantDataFile("tro_storage.dat"); }catch(Exception e){e.printStackTrace(); System.exit(1);} } Vector paths = new Vector(allData.size()); for ( Enumeration e = allData.keys(); e.hasMoreElements(); ) { paths.addElement(e.nextElement()); } return paths; */