import java.io.*;

/**
 *  Benchmark #11 - Adaptive K-D tree on disk.
 *   @author  Josef.Pelikan@mff.cuni.cz
 *   @version 0.007 (2001/06/25)
 *   @since   0.007
 */
public class BenchmarkAKDTree extends Benchmark {

    /** Number of insertions (1-st phase). */
    protected final static int TREE_INSERTIONS =  50000;

    /** Number of insertions/lookups (2-nd phase). */
    protected final static int TREE_WALK       = 100000;

    /** Total number of tree operations (both phases). */
    protected final static int TREE_OPERATIONS = TREE_INSERTIONS + TREE_WALK;

    /** Block cache size (4KB blocks). */
    protected final static int TREE_CACHE = 2048;

    /** Maximal X-coordinate of the point. */
    protected final static int TREE_XMAX = 10240;

    /** Maximal Y-coordinate of the point. */
    protected final static int TREE_YMAX = 10240;

    /** Maximal amplitude of one walking step. */
    protected final static int TREE_WALK_AMP = 1024;

    /** Middle value of X-coordinate. */
    protected final static int TREE_XHALF = TREE_XMAX >> 1;

    /** File name for the AKD-tree. */
    protected final static String TREE_FILE = "akd-tree.bin";

    /** Array to hold AKD-tree statistics. */
    protected int[] stat;

    /**
     *  Initializing constructor.
     */
    public BenchmarkAKDTree () {
        stat = new int[6];
    }

    /**
     *  Test (benchmark) ordinal number.
     *   @return Ordinal number of the test.
     */
    public int testNumber () {
        return 11;
    }

    /**
     *  The benchmark algorithm itself. Needs not be re-entrant but can be iterated.
     */
    protected void test () {

            // initialize data structures:
        RandomJames rnd = new RandomJames();
        AKDTree tree = new AKDTree(TREE_FILE,TREE_XHALF,TREE_CACHE);
        int i = 0;

            // 1st phase - insertions only:
        while ( i++ < TREE_INSERTIONS ) {
            int x = (int)(rnd.uniformNumber() * TREE_XMAX);
            int y = (int)(rnd.uniformNumber() * TREE_YMAX);
            tree.insert(x,y);
            if ( (i & 0x1FFF) == 0 ) {
                tree.getStat(stat);
                System.out.print("Op: " + (i/1000) + "k, rd: " + (stat[0]/1000) +
                                 "k, wr: " + (stat[1]/1000) + "k, fl: " + (stat[2]/1000) +
                                 "k, leaf: " + (stat[4]) + ", hits: " + (stat[5]) + "\r" );
                }
            }

            // 2-nd phase - random walk (insertions and lookups):
        double x = TREE_XMAX * 0.5;
        double y = TREE_YMAX * 0.5;
        while ( i++ < TREE_OPERATIONS ) {
            double dx = rnd.uniformNumber() * TREE_WALK_AMP - (TREE_WALK_AMP / 2);
            double dy = rnd.uniformNumber() * TREE_WALK_AMP - (TREE_WALK_AMP / 2);
            if ( (x += dx) < 1.0 )    x += TREE_XMAX;
            if ( x >= TREE_XMAX-1.0 ) x -= TREE_XMAX;
            if ( (y += dy) < 1.0 )    y += TREE_YMAX;
            if ( y >= TREE_YMAX-1.0 ) y -= TREE_YMAX;
            if ( rnd.uniformNumber() < 1.0/3.0 ) tree.insert((int)x,(int)y);
            else                                 tree.search((int)x,(int)y);
            if ( (i & 0x1FFF) == 0 ) {
                tree.getStat(stat);
                System.out.print("Op: " + (i/1000) + "k, rd: " + (stat[0]/1000) +
                                 "k, wr: " + (stat[1]/1000) + "k, fl: " + (stat[2]/1000) +
                                 "k, leaf: " + (stat[4]) + ", hits: " + (stat[5]) + "\r" );
                }
            }
        tree.getStat(stat);
        tree.close();

            // remove the working file:
        File f;
        try {
            f = new File(TREE_FILE);
            f.delete();
            }
        catch ( SecurityException e ) {
            }
    }

    /**
     *  Result string of the test. Should be called after the <code>runTest()</code>
     *  was finished.
     *   @return Result of the test.
     */
    public String testResult () {
        return( "Adaptive K-D tree on disk: operations: " + (TREE_OPERATIONS/1000) +
                "k                \nBlock reads: " + (stat[0]/1000) +
                "k, writes: " + (stat[1]/1000) + "k, flushes: " + (stat[2]/1000) +
                "k, leaf nodes: " + (stat[4]) + "\nhits: " + (stat[5]) );
    }

}
