import java.io.*;

/**
 *  Benchmark #8 - Merge sort on disk (alternative).
 *   @author  Josef.Pelikan@mff.cuni.cz
 *   @version 0.007 (2001/06/20)
 *   @since   0.007
 */
public class BenchmarkMergeSortAlt extends BenchmarkMergeSort {

    /**
     *  Initializing constructor.
     */
    public BenchmarkMergeSortAlt () {
    }

    /**
     *  Checks whether the given file is sorted (in ascending order).
     *   @param  f File to be checked.
     *   @return <code>true</code> if the given file is OK.
     */
    protected static boolean checkFile ( RandomAccessFile f )
    {
        double prev, act;
        try {
            f.seek(0L);
            long len = f.length() >> 3;
            if ( len < 2L ) return true;
            act = f.readDouble();
            long i;
            for ( i = 1; i++ < len; ) {
                prev = act;
                act = f.readDouble();
                if ( act < prev ) return false;
                }
            }
        catch ( IOException e ) {
            return false;
            }
        return true;
    }

    protected static void initialSplit ( RandomAccessFile from,
                                         RandomAccessFile to1, RandomAccessFile to2, long seg[] )
    {
        double prev, act;
        seg[0] = seg[1] = 0L;
        boolean one = true;
        try {
            to1.seek(0L);
            to1.setLength(0L);
            to2.seek(0L);
            to2.setLength(0L);
            from.seek(0L);
            act = from.readDouble();
            to1.writeDouble(act);
            seg[0]++;
            }
        catch ( IOException e ) {
            return;
            }

        do {
            prev = act;
            try {
                act = from.readDouble();
                if ( act < prev ) {
                    one = !one;
                    seg[ one ? 0 : 1 ]++;
                    }
                if ( one ) to1.writeDouble(act);
                else       to2.writeDouble(act);
                }
            catch ( IOException e ) {
                return;
                }
            } while ( true );
    }

    protected static void mergeAndSplit ( RandomAccessFile from1, RandomAccessFile from2,
                                          RandomAccessFile to1, RandomAccessFile to2, long seg[] )
    {
        double prev1, act1;
        double prev2, act2;
        seg[0] = seg[1] = 0L;
        boolean one = true;
        try {
            to1.seek(0L);
            to1.setLength(0L);
            to2.seek(0L);
            to2.setLength(0L);
            from1.seek(0L);
            from2.seek(0L);
            prev1 = from1.readDouble();
            act1 = from1.readDouble();
            prev2 = from2.readDouble();
            act2 = from2.readDouble();
            seg[0]++;
            }
        catch ( IOException e ) {
            return;
            }

        do {
                // min { prev1, prev2 } should be written out
            if ( prev1 < prev2 ) {
                try {
                    if ( one ) to1.writeDouble(prev1);
                    else       to2.writeDouble(prev1);
                    }
                catch ( IOException e ) {
                    return;
                    }
                if ( prev1 > act1 ) {       // copy the rest of from2 segment
                    // prev1 > act1, prev1 was processed
                    do {
                        try {
                            if ( one ) to1.writeDouble(prev2);
                            else       to2.writeDouble(prev2);
                            }
                        catch ( IOException e ) {
                            return;
                            }
                        if ( act2 < prev2 ) break;
                        prev2 = act2;
                        try {
                            act2 = from2.readDouble();
                            }
                        catch ( IOException e ) {
                            act2 = -1.0;
                            }
                        } while ( true );
                    // prev2 > act2, prev2 was processed
                    if ( act1 < 0.0 || act2 < 0.0 ) // from1 or from2 were exhausted
                        break;
                    one = !one;
                    seg[ one ? 0 : 1 ]++;
                    prev2 = act2;
                    try {
                        act2 = from2.readDouble();
                        }
                    catch ( IOException e ) {
                        act2 = -1.0;
                        }
                    }
                prev1 = act1;
                try {
                    act1 = from1.readDouble();
                    }
                catch ( IOException e ) {
                    act1 = -1.0;
                    }
                }

            else {                          // prev2 < prev1
                try {
                    if ( one ) to1.writeDouble(prev2);
                    else       to2.writeDouble(prev2);
                    }
                catch ( IOException e ) {
                    return;
                    }
                if ( prev2 > act2 ) {       // copy the rest of from1 segment
                    // prev2 > act2, prev2 was processed
                    do {
                        try {
                            if ( one ) to1.writeDouble(prev1);
                            else       to2.writeDouble(prev1);
                            }
                        catch ( IOException e ) {
                            return;
                            }
                        if ( act1 < prev1 ) break;
                        prev1 = act1;
                        try {
                            act1 = from1.readDouble();
                            }
                        catch ( IOException e ) {
                            act1 = -1.0;
                            }
                        } while ( true );
                    // prev1 > act1, prev1 was processed
                    if ( act1 < 0.0 || act2 < 0.0 ) // from1 or from2 were exhausted
                        break;
                    one = !one;
                    seg[ one ? 0 : 1 ]++;
                    prev1 = act1;
                    try {
                        act1 = from1.readDouble();
                        }
                    catch ( IOException e ) {
                        act1 = -1.0;
                        }
                    }
                prev2 = act2;
                try {
                    act2 = from2.readDouble();
                    }
                catch ( IOException e ) {
                    act2 = -1.0;
                    }
                }
            } while ( true );

        while ( act1 >= 0.0 ) {             // from1 wasn't exhausted
            one = seg[0] < seg[1];
            try {
                if ( one ) {                // put the 1st item
                    seg[0]++;
                    to1.writeDouble(act1);
                    }
                else {
                    seg[1]++;
                    to2.writeDouble(act1);
                    }
                }
            catch ( IOException e ) {
                return;
                }
            do {
                prev1 = act1;
                try {
                    act1 = from1.readDouble();
                    }
                catch ( IOException e ) {
                    act1 = -1.0;
                    }
                if ( prev1 > act1 ) break;
                try {
                    if ( one ) to1.writeDouble(act1);
                    else       to2.writeDouble(act1);
                    }
                catch ( IOException e ) {
                    return;
                    }
                } while ( true );
            }

        if ( act2 >= 0.0 ) {                // from2 wasn't exhausted
            one = seg[0] < seg[1];
            try {
                if ( one ) {                // put the 1st item
                    seg[0]++;
                    to1.writeDouble(act2);
                    }
                else {
                    seg[1]++;
                    to2.writeDouble(act2);
                    }
                }
            catch ( IOException e ) {
                return;
                }
            do {
                prev2 = act2;
                try {
                    act2 = from2.readDouble();
                    }
                catch ( IOException e ) {
                    act2 = -1.0;
                    }
                if ( prev2 > act2 ) break;
                try {
                    if ( one ) to1.writeDouble(act2);
                    else       to2.writeDouble(act2);
                    }
                catch ( IOException e ) {
                    return;
                    }
                } while ( true );
            }
    }

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

        ok = false;
        System.out.print("init: random generator, bucket-sort\r");
        RandomJames rnd = new RandomJames();
        RandomAccessFile f1, f2, f3, f4;
        try {
            f1 = new RandomAccessFile("sort1.bin","rw");
            f2 = new RandomAccessFile("sort2.bin","rw");
            f3 = new RandomAccessFile("sort3.bin","rw");
            f4 = new RandomAccessFile("sort4.bin","rw");
            }
        catch ( FileNotFoundException e ) {
            return;
            }
        long seg12[] = { 0, 0 };
        long seg34[] = { 0, 0 };
        long i, j;

            // file init:
        double bucket[] = new double[(int)BUCKET_SIZE];
        for ( i = 0; i++ < SORT_SIZE/BUCKET_SIZE; ) {
            rnd.uniformNumbers(bucket);
            QuickSortDouble.sort(bucket,0,(int)BUCKET_SIZE);
            try {
                for ( j = 0; j < BUCKET_SIZE; j++ )
                    f3.writeDouble(bucket[(int)j]);
                }
            catch ( IOException e ) {
                return;
                }
            }

            // merge-sort:
        initialSplit(f3,f1,f2,seg12);
        do {
            System.out.print( " " + seg12[0] + " " + seg12[1] + "                            \r" );
            System.out.flush();
            if ( seg12[1] == 0L ) {
                ok = checkFile(f1);
                break;
                }
            if ( seg12[0] == 0L ) {
                ok = checkFile(f2);
                break;
                }
            mergeAndSplit(f1,f2,f3,f4,seg34);
            System.out.print( " " + seg34[0] + " " + seg34[1] + "                            \r" );
            System.out.flush();
            if ( seg34[1] == 0L ) {
                ok = checkFile(f3);
                break;
                }
            if ( seg34[0] == 0L ) {
                ok = checkFile(f4);
                break;
                }
            mergeAndSplit(f3,f4,f1,f2,seg12);
            } while ( true );

            // the right order was still checked
        try {
            f1.close();
            f2.close();
            f3.close();
            f4.close();
            }
        catch ( IOException e ) {
            }
        File f;
        try {
            f = new File("sort1.bin");
            f.delete();
            f = new File("sort2.bin");
            f.delete();
            f = new File("sort3.bin");
            f.delete();
            f = new File("sort4.bin");
            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( "Merge-sort on disk (alt): " + (ok ? "OK" : "failed") +
                ", file size: " + (SORT_SIZE >> 17) + " MB" );
    }

}
