package com.colloquial.arithcode; /** Package local helper class to compute statistics for a single * compression experiment. Stores original and coded bytes, average * and cumulative compression and speeds. Pretty printing is through * toString. * * @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a> * @see TestSet * @version 1.1 * @since 1.1 */ class TestStatistics implements Comparable { // specified in Comparable public int compareTo(Object that) { return compareTo((TestStatistics) that); } // specified in Object public String toString() { StringBuffer report = new StringBuffer(); report.append("\n"); report.append("TEST: " + _name); report.append("\n"); report.append("Overall Compression Rate " + roundOff(8.0 * rate(_totalCodedBytes,_totalOriginalBytes)) + " b/B"); report.append("\n"); report.append("Overall Encode Speed " + roundOff(rate(_totalOriginalBytes, _totalEncodeTime)) + " kB/s"); report.append("\n"); report.append("Overall Decode Speed " + roundOff(rate(_totalOriginalBytes, _totalDecodeTime)) + " kB/s"); report.append("\n"); report.append("Average Compression Rate " + roundOff(8.0 * _totalCompressionRate / (double) _numTests)); report.append("\n"); report.append("Average Encode Speed " + roundOff(_totalEncodeSpeed / (double) _numTests)); report.append("\n"); report.append("Average Decode Speed " + roundOff(_totalDecodeSpeed / (double) _numTests)); return report.toString(); } TestStatistics(String name) { _name = name; } /** Record the result of an experiment with specified number of original and encoded * bytes with specified encode and decode time. * @param originalBytes Number of bytes in original source. * @param codedBytes Number of bytes in decoded result. * @param encodeTime Time taken to do encoding in milliseconds. * @param decodeTime Time taken to do decoding in milliseconds. */ void record(int originalBytes, int codedBytes, long encodeTime, long decodeTime) { ++_numTests; _totalOriginalBytes += originalBytes; _totalCodedBytes += codedBytes; _totalEncodeTime += encodeTime; _totalDecodeTime += decodeTime; _totalCompressionRate += rate(codedBytes,originalBytes); _totalEncodeSpeed += rate(originalBytes,encodeTime); _totalDecodeSpeed += rate(originalBytes,decodeTime); } /** Used for a line of reporting by TestSet. * @return The string representation of the statistics for this test on one line. */ String lineReport() { return (_name + roundOff(8.0 * rate(_totalCodedBytes,_totalOriginalBytes),8,2) + roundOff(rate(_totalOriginalBytes, _totalEncodeTime),8,0) + roundOff(rate(_totalOriginalBytes, _totalDecodeTime),8,0) + roundOff(8.0 * _totalCompressionRate / (double) _numTests,8,2) + roundOff(_totalEncodeSpeed / (double) _numTests,8,0) + roundOff(_totalDecodeSpeed / (double) _numTests,8,0)); } /** Name of test. */ private String _name; /** Cumulative number of original bytes. */ private int _totalOriginalBytes; /** Cumulative number of coded bytes. */ private int _totalCodedBytes; /** Number of tests that have been run. */ private int _numTests; /** Total of KB/s rate for all encodings (for average). */ private double _totalEncodeSpeed; /** Total of KB/s rate for all decodings (for average). */ private double _totalDecodeSpeed; /** Running total of sum of compression rates per file (for average). */ private double _totalCompressionRate; /** Total amount of time in milliseconds spent encoding. */ private long _totalEncodeTime; /** Total amount of time in milliseconds spent decoding. */ private long _totalDecodeTime; /** Compare using average compression rates, breaking ties by name. * Assumes tests have been run on same set. */ private int compareTo(TestStatistics that) { if (_totalCompressionRate < that._totalCompressionRate) return 1; if (_totalCompressionRate > that._totalCompressionRate) return -1; return _name.compareTo(that._name); } /** Rate of first argument divided by second argument as doubles. * @param a Numerator. * @param b Denominator. * @return Numerator over denominator with double division. */ private static double rate(long a, long b) { return ((double) a) / (double) b; } /** Round off a double to 2 decimal places, taking up 8 total characters. * @param x Double to round off and convert to string. * @return Result of rounding as string. */ private static String roundOff(double x) { return roundOff(x,8,2); } /** Round off a double to specified number of decimal places, taking up specified total width * @param x Double to round off and convert to string. * @param width Width of final string representation. * @param numDecimalPlaces Number of decimal places in representation. * @return Result of rounding as string. */ private static String roundOff(double x, int width, int numDecimalPlaces) { double factor = 1.0; for (int i = 0; i < numDecimalPlaces; ++i) factor *= 10.0; return roundOff(x,factor,width); } /** Round off a double with a multiplicative factor, padding to specified width. * @param x Double to round off and convert to string. * @param width Width of final string representation. * @param factor 10 to the number of decimal places to keep. * @return Result of rounding as string. */ private static String roundOff(double x, double factor, int width) { int intRep = (int) (factor * x); String result = "" + ((double)((int) (factor * x)))/factor; for (int i = 0; i < 1; ++i) { if (intRep % 10 != 0) break; result = result + "0"; intRep = intRep / 10; } if (factor == 1.0) result = ""+(int)x; while (result.length() < width) result = " " + result; return result; } }