import java.lang.*;

/**
 *  Benchmark #15 - Garbage collection.
 *   @author  Josef.Pelikan@mff.cuni.cz
 *   @version 0.008 (2001/07/19)
 *   @since   0.007
 */
public class BenchmarkGC extends Benchmark {

    /** Number of tree-construction steps. */
    protected final static int GC_ITER = 1000000;

    /** Maximum tree path. */
    protected final static int MAX_PATH = 72;

    /** Minimal data array size. */
    protected final static int MIN_DATA = 12;

    /** Maximal amplitude of data array size. */
    protected final static int VAR_DATA = 144;

    /** Java VM runtime class. */
    protected Runtime r;

    /** Total number of processed memory bytes (allocated/deallocated). */
    protected long totalAlloc;

    /** Maximum tree size in bytes. */
    protected int maxAlloc;

    /**
     *  Initializing constructor.
     */
    public BenchmarkGC () {
        r = Runtime.getRuntime();
        if ( VMVersion.microsoft() ) {
            byte dumb[] = new byte[(16<<20)-200000];
            }
    }

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

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

            // initialization:
        RandomJames rnd = new RandomJames();
        TreeNode root = new TreeNode(0,null);
        totalAlloc = maxAlloc = root.sum;
        int i;
        boolean grow = false;

        for ( i = 0; i++ < GC_ITER; ) {

                // path variables:
            int path = (int)(rnd.uniformNumber() * MAX_PATH);
            TreeNode ptr = root;
            boolean change = false;

                // find the hot-spot:
            while ( path-- > 0 ) {          // one step forward
                if ( rnd.uniformNumber() < 0.5 ) { // to the left
                    if ( ptr.left == null ) {
                        ptr.left = new TreeNode( (int)(rnd.uniformNumber()*VAR_DATA + MIN_DATA), ptr );
                        change = true;
                        }
                    ptr = ptr.left;
                    }
                else {                      // to the right
                    if ( ptr.right == null ) {
                        ptr.right = new TreeNode( (int)(rnd.uniformNumber()*VAR_DATA + MIN_DATA), ptr );
                        change = true;
                        }
                    ptr = ptr.right;
                    }
                }

                // truncate the sub-tree:
            if ( rnd.uniformNumber() < 0.5 ) { // truncate the left sub-tree
                if ( ptr.left != null ) {
                    ptr.left = null;
                    change = true;
                    }
                }
            else {                          // truncate the right sub-tree
                if ( ptr.right != null ) {
                    ptr.right = null;
                    change = true;
                    }
                }

                // regenerate all counters:
            if ( change ) {
                int before = root.sum;
                ptr.regenerate();
                int after = root.sum;
                if ( after > before ) {     // memory grows
                    totalAlloc += after - before;
                    grow = true;
                    }
                else {                      // memory shortens
                    if ( grow &&            // it has grown before
                         before > maxAlloc ) maxAlloc = before;
                    grow = false;
                    }
                }

            }

        int before = root.sum;
        if ( before > maxAlloc ) maxAlloc = before;
    }

    /**
     *  Result string of the test. Should be called after the <code>runTest()</code>
     *  was finished.
     *   @return Result of the test.
     */
    public String testResult () {
        return( "Garbage collection test in " + ((r.totalMemory()>>20)+1) + "MB (" +
                ((totalAlloc+(1L<<19))>>20) + "MB, max " + ((maxAlloc+512)>>10) + "KB)" );
    }

}

/**
 *  Binary tree node.
 *   @author  Josef.Pelikan@mff.cuni.cz
 *   @version 0.007 (2001/06/26)
 *   @since   0.007
 */
class TreeNode {                            // occupies 32 bytes of RAM

    /** Static instance size in bytes. */
    protected final static int INSTANCE_SIZE = 32;

    /** Overhead of each array in bytes. */
    protected final static int ARRAY_OVERHEAD = 12;

    /** Roundup boundary of array allocation size (in bytes). */
    protected final static int ARRAY_ROUNDUP = 8;

    /**
     *  Computes allocated array size in bytes.
     *   @param  length Effective array length in bytes.
     *   @return Allocated array size in bytes.
     */
    protected final static int arraySize ( int length ) {
        if ( length <= 0 ) return 0;
        return( (length + ARRAY_OVERHEAD + ARRAY_ROUNDUP-1) & -ARRAY_ROUNDUP );
    }

    /**
     *  Computes memory size occupied by the instance itself.
     *   @return Allocated memory size in bytes.
     */
    protected final int mySize () {
        return( INSTANCE_SIZE + ((data == null) ? 0 : arraySize(data.length)) );
    }

    /** Left son's handle. */
    public TreeNode left;

    /** Right son's handle. */
    public TreeNode right;

    /** Parent's handle. */
    public TreeNode up;

    /** Temporary data array. */
    public byte[] data;

    /** Occupied memory of the whole sub-tree (in bytes). */
    public int sum;

    /**
     *  Initializing constructor.
     *   @param  dataSize Size of a temporary array.
     *   @param  parent Parent's handle.
     */
    public TreeNode ( int dataSize, TreeNode parent ) {
        left = right = null;
        up = parent;
        data = (dataSize > 0) ? new byte[dataSize] : null;
        sum = mySize();
    }

    /**
     *  Refreshes all occupied memory counters (from this node to the root).
     */
    public void regenerate () {
        TreeNode ptr = this;
        do {
            ptr.sum = ptr.mySize();
            if ( ptr.left != null ) ptr.sum += ptr.left.sum;
            if ( ptr.right != null ) ptr.sum += ptr.right.sum;
            } while ( (ptr = ptr.up) != null );
    }

}
