/*

    main.cc: VUCAKO Bench (CPU / memory / cache benchmark).

    10.6.2001

    Copyright (C) 2001 by Josef Pelikan, MFF UK Prague
        http://sun3.ms.mff.cuni.cz/~pepca/

    VUCAKO Bench is Open Source software (see http://www.opensource.org/
    for description what Open Source is); you may distributed it and/or
    modify it under the terms of GNU General Public License as published
    by the Free Software Foundation; either version 2, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    A copy of the GNU General Public License is available as
    `/usr/doc/copyright/GPL' in the Debian GNU/Linux distribution or on
    the World Wide Web at http://www.gnu.org/copyleft/gpl.html.  You can
    also obtain it by writing to the Free Software Foundation, Inc., 59
    Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include "pch.h"

#define VERSION     "1.006"

//-----------------------------------------------------------
//  live cycle:

#define LiveCycle(n,total)  {printf("%4.0f %%\r",((n)*100.0)/(total));fflush(stdout);}

//-----------------------------------------------------------
//  Quick-sort of double[] array:

#define QSMin           12

void qs ( double *a, int len )
{
    int i, j;
    if ( len < QSMin ) {                    // short array => insert-sort
        while ( len > 1 ) {                 // find minimum and swap it to a[0]
            double min = a[0];
            j = 0;
            for ( i = 1; i < len; i++ )
                if ( a[i] < min ) {
                    min = a[i];
                    j = i;
                    }
            if ( j != 0 ) {
                a[j] = a[0];
                a[0] = min;
                }
            a++;
            len--;
            }
        return;
        }
        // the array is long enough for quick-sort:
    double pivot = (a[0] + a[len-1]) * 0.5;
    i = -1; j = len;
    do {
        do i++;
        while ( i < len && a[i] <= pivot );
        do j--;
        while ( j >= 0  && a[j] >= pivot );
        if ( j < i ) break;
        double tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
        } while ( true );
        // j < i => sort [0 to j] and [i to len-1]:
    if ( j + 1 < len - i ) {                // [0 to j] first
        if ( j > 0 ) qs(a,j+1);
        qs(a+i,len-i);
        }
    else {                                  // [i to len-1] first
        if ( len-i > 1 ) qs(a+i,len-i);
        qs(a,j+1);
        }
}

//-----------------------------------------------------------
//  eratosthen:

#define PrimeOrder        5555555
#define PrimeAssert     100000000

int eratosthen ( int primeOrder, int primeAssert )
{
    startTimer();
    int elapsed;
    int repeat = 0;
    int prime;
    do {
        if ( repeat++ ) putchar('\n');
        CBitMask sieve;
        sieve.assert(2);
        sieve.assert(primeAssert);
        int n = 0;
        prime = 1;
        do {                                // find the next prime
            do prime++;
            while ( sieve.getUnsafe(prime) );
            int k = prime;
            do {
                sieve.setUnsafe(k,true);
                k += prime;
                } while ( k <= primeAssert );
            if ( !(n & 0x7FFF) ) LiveCycle(n,primeOrder);
            } while ( ++n < primeOrder );
        elapsed = elapsedTime();
        } while ( elapsed < MinElapsed );
    elapsed = (elapsed + (repeat>>1)) / repeat;
    printf("The %d-th prime number is: %d, elapsed time: %.2f sec\n",
           primeOrder,prime,elapsed*0.001);
    return elapsed;
}

//-----------------------------------------------------------
//  BYTE bitmap:

#define ImageWidth1          4096
#define ImageHeight1         4096
#define ImageIter1           48

#define ImageWidth2          8192
#define ImageHeight2         8192
#define ImageIter2           12

BYTE *newImage ( int width, int height )
{
    width  &= -4;
    height &= -4;
    return (BYTE*)VIRTUAL_ALLOC(width*height);
}

void freeImage ( BYTE *im, int width, int height )
{
    width  &= -4;
    height &= -4;
    VIRTUAL_FREE(im,width*height);
}

void transpose ( BYTE *dest, int width, int height, BYTE *src )
{
    width  &= -4;
    height &= -4;
    unsigned *dp1 = (unsigned*)dest;        // destination pointers
    dest += width;
    unsigned *dp2 = (unsigned*)dest;
    dest += width;
    unsigned *dp3 = (unsigned*)dest;
    dest += width;
    unsigned *dp4 = (unsigned*)dest;
    dest += width;
    unsigned *sp0 = (unsigned*)src;
    unsigned *sp;
    unsigned dact1, dact2, dact3, dact4, sact;
    int i, j;
    for ( i = 0; i < height; i += 4 ) {     // 4 destination rows
        sp = sp0;
        for ( j = 0; j < width; j += 4 ) {  // 4 destination columns
            sact   = *sp;
            dact1  =   sact        & 0xFF;
            dact2  =  (sact >>  8) & 0xFF;
            dact3  =  (sact >> 16) & 0xFF;
            dact4  =  (sact >> 24) & 0xFF;
            sp += (height >> 2);
            dact1 |= ( sact        & 0xFF) <<  8;
            dact2 |= ((sact >>  8) & 0xFF) <<  8;
            dact3 |= ((sact >> 16) & 0xFF) <<  8;
            dact4 |= ((sact >> 24) & 0xFF) <<  8;
            sp += (height >> 2);
            dact1 |= ( sact        & 0xFF) << 16;
            dact2 |= ((sact >>  8) & 0xFF) << 16;
            dact3 |= ((sact >> 16) & 0xFF) << 16;
            dact4 |= ((sact >> 24) & 0xFF) << 16;
            sp += (height >> 2);
            dact1 |= ( sact        & 0xFF) << 24;
            dact2 |= ((sact >>  8) & 0xFF) << 24;
            dact3 |= ((sact >> 16) & 0xFF) << 24;
            dact4 |= ((sact >> 24) & 0xFF) << 24;
            sp += (height >> 2);
            *dp1++ = dact1;
            *dp2++ = dact2;
            *dp3++ = dact3;
            *dp4++ = dact4;
            }
        sp0++;
        dp1 = (unsigned*)dest;
        dest += width;
        dp2 = (unsigned*)dest;
        dest += width;
        dp3 = (unsigned*)dest;
        dest += width;
        dp4 = (unsigned*)dest;
        dest += width;
        }
}

int imageTransposition ( int width, int height, int iter )
{
    startTimer();
    int elapsed;
    int repeat = 0;
    do {
        if ( repeat++ ) putchar('\n');
        BYTE *im1 = newImage(width,height);
        BYTE *im2 = newImage(height,width);
        int i;
        for ( i = 0; i++ < iter; ) {
            transpose(im2,height,width,im1);
            transpose(im1,width,height,im2);
            LiveCycle(i,iter);
            }
        freeImage(im2,height,width);
        freeImage(im1,width,height);
        elapsed = elapsedTime();
        } while ( elapsed < MinElapsed );
    elapsed = (elapsed + (repeat>>1)) / repeat;
    printf("Transpose the [%d,%d] image, elapsed time: %.2f sec\n",
           width,height,elapsed*0.001);
    return elapsed;
}

//-----------------------------------------------------------
//  arbitrage sequence lookup:

#define MaxN        16
#define ArbitrageN  12
#define MinBound    1.0

double m[MaxN][MaxN];
int n;

void initInstance ()
{
    n = ArbitrageN;
    int i;
    RandomJames rnd;
    for ( i = 0; i < n; i++ )
        rnd.normalNumbers(1.0,0.05,5,m[i],n);
}

int result[MaxN];
int used[MaxN];
int len;

double best;
int bestSeq[MaxN];
int bestLen;

void solve ( double mul, int i )
{
    int j;
    double act;
    if ( i + 1 == len )
        for ( j = 0; j < n; j++ ) {
            if ( !used[j] )
                if ( (act = mul * m[result[i-1]][j] * m[j][result[0]]) > best ) {
                    best = act;
                    bestLen = len;
                    memcpy(bestSeq,result,i*sizeof(int));
                    bestSeq[i] = j;
                    }
            }
    else
        for ( j = 0; j < n; j++ )
            if ( !used[j] ) {
                used[j] = 1;
                result[i] = j;
                solve(mul*m[result[i-1]][j],i+1);
                used[j] = 0;
                }
}

void solveInstance ()
{
    int i;
    memset(used,0,sizeof(used));
    best = MinBound;
    bestLen = 0;
    for ( len = 2; len < n; len++ ) {
        for ( i = 0; i < n; i++ ) {
            used[i] = 1;
            result[0] = i;
            solve(1.0,1);
            used[i] = 0;
            }
        LiveCycle(len-1,n-1);
        }
    if ( bestLen > 0 ) {
        printf("Arbitrage profit:%7.3f, seq:",best);
        for ( i = 0; i < bestLen; i++ )
            printf(" %d",bestSeq[i]+1);
        printf(" %d\n",bestSeq[0]+1);
        }
    else
        puts("No arbitrage sequence exists");
}

//-----------------------------------------------------------
//  needle throwing simulation:

#define NeedleMax   100000000

int needleSimulation ()
{
    startTimer();
    unsigned hit, i;
    double y, height, angle;
    int elapsed;
    int repeat = 0;
    do {
        if ( repeat++ ) putchar('\n');
        RandomJames rnd;
        hit = 0;
        for ( i = 0; i++ < NeedleMax; ) {
            y = rnd.uniformNumber() * 2.0;
            angle = rnd.uniformNumber() * 3.141592653589793;
            height = sin(angle);
            if ( y + height >= 2.0 || y - height < 0.0 ) hit++;
            if ( !(i & 0x1FFFFF) ) LiveCycle(i,NeedleMax);
            }
        elapsed = elapsedTime();
        } while ( elapsed < MinElapsed );
    elapsed = (elapsed + (repeat>>1)) / repeat;
    printf("Needle simulation: N = %dM, elapsed time: %.2f sec, ratio = %.9f\n",
           NeedleMax/1000000,elapsed*0.001,(hit+0.0)/NeedleMax);
    return elapsed;
}

//-----------------------------------------------------------
//  fast mem-copy:

#define MemCopyMax   (16<<20)
#define MemCopyIter  100

int memCopy ()
{
    startTimer();
    int elapsed;
    int repeat = 0;
    do {
        if ( repeat++ ) putchar('\n');
        char *a = (char*)VIRTUAL_ALLOC(MemCopyMax);
        char *b = (char*)VIRTUAL_ALLOC(MemCopyMax);
        int i;
        for ( i = 0; i++ < MemCopyIter; ) {
            memcpy(b,a,MemCopyMax);
            memcpy(a,b,MemCopyMax);
            LiveCycle(i,MemCopyIter);
            }
        VIRTUAL_FREE(a,MemCopyMax);
        VIRTUAL_FREE(b,MemCopyMax);
        elapsed = elapsedTime();
        } while ( elapsed < MinElapsed );
    elapsed = (elapsed + (repeat>>1)) / repeat;
    printf("Fast mem-copy: array size = %d MB, iterations = %d, elapsed time: %.2f sec\n",
           MemCopyMax>>20,MemCopyIter,elapsed*0.001);
    return elapsed;
}

//-----------------------------------------------------------
//  Monte-Carlo form-factor between two identical rectangles:

#define FFIter  50000000

int formFactor ()
{
    startTimer();
    int i;
    unsigned fullHits, halfCount, halfHits;
    double x0, y0;                          // ray origin
    double x1, y1, z1;                      // ray direction
    double x2, y2;
    double tmp;
    bool half;
    int elapsed;
    int repeat = 0;
    do {
        if ( repeat++ ) putchar('\n');
        fullHits = halfCount = halfHits = 0;
        RandomJames rnd;
        for ( i = 0; i++ < FFIter; ) {
            x0 = rnd.uniformNumber();
            y0 = rnd.uniformNumber();
            if ( (half = x0 < 0.5 && y0 < 0.5) ) halfCount++;
            do {
                x1 = rnd.uniformNumber() * 2.0 - 1.0;
                y1 = rnd.uniformNumber() * 2.0 - 1.0;
                tmp = x1 * x1 + y1 * y1;
                } while ( tmp >= 1.0 );
            z1 = sqrt( 1.0 - tmp );
            if ( z1 > 0.01 ) {              // there is a chance
                tmp = 1.0 / z1;
                x2 = x0 + tmp * x1;
                y2 = y0 + tmp * y1;
                if ( x2 >= 0.0 && y2 >= 0.0 )
                    if ( x2 < 1.0 && y2 < 1.0 ) {
                        fullHits++;
                        if ( half && x2 < 0.5 && y2 < 0.5 ) halfHits++;
                        }
                }
            if ( !(i & 0xFFFFF) ) LiveCycle(i,FFIter);
            }
        elapsed = elapsedTime();
        } while ( elapsed < MinElapsed );
    elapsed = (elapsed + (repeat>>1)) / repeat;
    printf("Form-factor: iter = %dM, time: %.2f sec, Ffull =%10.7f, Fhalf =%10.7f\n",
           FFIter/1000000,elapsed*0.001,(fullHits+0.0)/FFIter,(halfHits+0.0)/halfCount);
    return elapsed;
}

//-----------------------------------------------------------
//  Natural merge-sort of huge disk files:

#define SortSize        (1<<23)
#define BucketSize      1024
#define TestFileName    "sort%d.bin"

bool checkFile ( FileOfDouble &f, int &size )
{
    double prev, act;
    f.seek(0);
    act = f.get();
    size = 1;
    while ( true ) {
        prev = act;
        act = f.get();
        if ( act < 0.0 ) return true;
        size++;
        if ( act < prev ) return false;
        }
}

void initialSplit ( FileOfDouble &from, FileOfDouble &to1, int &seg1, FileOfDouble &to2, int &seg2 )
{
    to1.rewrite();
    seg1 = 0;
    to2.rewrite();
    seg2 = 0;
    from.seek(0);
    double prev, act;
    act = from.get();
    if ( act < 0.0 ) return;
    bool one = true;
    to1.put(act);
    seg1 = 1;

    do {
        prev = act;
        act = from.get();
        if ( act < 0.0 ) return;
        if ( act < prev ) {
            one = !one;
            if ( one ) seg1++;
            else       seg2++;
            }
        if ( one ) to1.put(act);
        else       to2.put(act);
        } while ( true );
}

void mergeAndSplit ( FileOfDouble &from1, FileOfDouble &from2, 
                     FileOfDouble &to1, int &seg1, FileOfDouble &to2, int &seg2 )
{
    to1.rewrite();
    seg1 = 0;
    to2.rewrite();
    seg2 = 0;
    from1.seek(0);
    from2.seek(0);
    double prev1, act1;
    double prev2, act2;
    prev1 = from1.get();
    act1 = from1.get();
    prev2 = from2.get();
    act2 = from2.get();
    bool one = true;
    seg1 = 1;

    do {
            // min { prev1, prev2 } should be written out
        if ( prev1 < prev2 ) {
            if ( one ) to1.put(prev1);
            else       to2.put(prev1);
            if ( prev1 > act1 ) {           // copy the rest of from2 segment
                // prev1 > act1, prev1 was processed
                do {
                    if ( one ) to1.put(prev2);
                    else       to2.put(prev2);
                    if ( act2 < prev2 ) break;
                    prev2 = act2;
                    act2 = from2.get();
                    } while ( true );
                // prev2 > act2, prev2 was processed
                if ( act1 < 0.0 || act2 < 0.0 ) // from1 or from2 were exhausted
                    break;
                one = !one;
                if ( one ) seg1++;
                else       seg2++;
                prev2 = act2;
                act2 = from2.get();
                }
            prev1 = act1;
            act1 = from1.get();
            }
        else {                              // prev2 < prev1
            if ( one ) to1.put(prev2);
            else       to2.put(prev2);
            if ( prev2 > act2 ) {           // copy the rest of from1 segment
                // prev2 > act2, prev2 was processed
                do {
                    if ( one ) to1.put(prev1);
                    else       to2.put(prev1);
                    if ( act1 < prev1 ) break;
                    prev1 = act1;
                    act1 = from1.get();
                    } while ( true );
                // prev1 > act1, prev1 was processed
                if ( act1 < 0.0 || act2 < 0.0 ) // from1 or from2 were exhausted
                    break;
                one = !one;
                if ( one ) seg1++;
                else       seg2++;
                prev1 = act1;
                act1 = from1.get();
                }
            prev2 = act2;
            act2 = from2.get();
            }
        } while ( true );

    while ( act1 >= 0.0 ) {                 // from1 wasn't exhausted
        one = seg1 < seg2;
        if ( one ) {                        // put the 1st item
            seg1++;
            to1.put(act1);
            }
        else {
            seg2++;
            to2.put(act1);
            }
        do {
            prev1 = act1;
            act1 = from1.get();
            if ( prev1 > act1 ) break;
            if ( one ) to1.put(act1);
            else       to2.put(act1);
            } while ( true );
        }

    if ( act2 >= 0.0 ) {                    // from2 wasn't exhausted
        one = seg1 < seg2;
        if ( one ) {                        // put the 1st item
            seg1++;
            to1.put(act2);
            }
        else {
            seg2++;
            to2.put(act2);
            }
        do {
            prev2 = act2;
            act2 = from2.get();
            if ( prev2 > act2 ) break;
            if ( one ) to1.put(act2);
            else       to2.put(act2);
            } while ( true );
        }
}

int diskSort ()
{
    printf("init: random generator, bucket-sort\r");
    fflush(stdout);
    startTimer();
    char fn1[32], fn2[32], fn3[32], fn4[32];
    sprintf(fn1,TestFileName,1);
    sprintf(fn2,TestFileName,2);
    sprintf(fn3,TestFileName,3);
    sprintf(fn4,TestFileName,4);
    RandomJames rnd;
    FileOfDouble f1(fn1);
    FileOfDouble f2(fn2);
    FileOfDouble f3(fn3);
    FileOfDouble f4(fn4);
    int seg1, seg2, seg3, seg4;
    seg3 = seg4 = 0;
    int i, j;
        // file init:
    for ( i = 0; i++ < SortSize/BucketSize; ) {
        double bucket[BucketSize];
        for ( j = 0; j < BucketSize; j++ )
            bucket[j] = rnd.uniformNumber();
        qs(bucket,BucketSize);
        for ( j = 0; j < BucketSize; j++ )
            f3.put(bucket[j]);
        }
        // merge-sort:
    initialSplit(f3,f1,seg1,f2,seg2);
    bool ok;
    do {
        printf("%17d %17d\r",seg1,seg2);
        fflush(stdout);
        if ( seg2 == 0 ) {
            ok = checkFile(f1,i);
            break;
            }
        if ( seg1 == 0 ) {
            ok = checkFile(f2,i);
            break;
            }
        mergeAndSplit(f1,f2,f3,seg3,f4,seg4);
        printf("%17d %17d\r",seg3,seg4);
        fflush(stdout);
        if ( seg4 == 0 ) {
            ok = checkFile(f3,i);
            break;
            }
        if ( seg3 == 0 ) {
            ok = checkFile(f4,i);
            break;
            }
        mergeAndSplit(f3,f4,f1,seg1,f2,seg2);
        } while ( true );
        // the right order was checked
    f1.close();
    f2.close();
    f3.close();
    f4.close();
    remove(fn1);
    remove(fn2);
    remove(fn3);
    remove(fn4);
    int elapsed = elapsedTime();
    printf("Merge-sort on disk: %s, file size: %d MB, elapsed time: %.2f sec\n",
           (ok && i == SortSize) ? "OK":"fail",(SortSize*sizeof(double))>>20,elapsed*0.001);
    return elapsed;
}

//-----------------------------------------------------------
//  Wavelet transform (memory cache test):

#define WaveMaxSize     (1<<22)
#define WaveMinSize     (1<<11)
#define WaveIter        20

void liftMemory ( int n0, int32 *s0, int32 *s1, int32 *d1 )
    // forward TS-transform
{
    int n1 = n0 >> 1;
    int32 *ps0, *ps1, *pd1;
    int i;
        // split:
    ps0 = s0;
    ps1 = s1; pd1 = d1;
    for ( i = 0; i++ < n1; ) {
        *ps1++ = *ps0++;
        *pd1++ = *ps0++;
        }
        // S-transform:
    ps1 = s1; pd1 = d1;
    for ( i = 0; i++ < n1; ) {
        *pd1 -= *ps1;
        *ps1++ += (*pd1++ >> 1);
        }
        // predict:
    ps1 = s1; pd1 = d1;
    ps1[n1] = ps1[n1-1];                    // boundary
    ps1[-1] = ps1[0];
    for ( i = 0; i++ < n1; ) {
        *pd1++ += ((ps1[-1] - ps1[1] + 2) >> 2);
        ps1++;
        }
}

void unliftMemory ( int n0, int32 *s0, int32 *s1, int32 *d1 )
    // inverse TS-transform
{
    int n1 = n0 >> 1;
    int32 *ps0, *ps1, *pd1;
    int i;
        // predict*:
    ps1 = s1; pd1 = d1;
    ps1[n1] = ps1[n1-1];                    // boundary
    ps1[-1] = ps1[0];
    for ( i = 0; i++ < n1; ) {
        *pd1++ -= ((ps1[-1] - ps1[1] + 2) >> 2);
        ps1++;
        }
        // S-transform*:
    ps1 = s1; pd1 = d1;
    for ( i = 0; i++ < n1; ) {
        *ps1 -= (*pd1 >> 1);
        *pd1++ += *ps1++;
        }
        // merge:
    ps0 = s0;
    ps1 = s1; pd1 = d1;
    for ( i = 0; i++ < n1; ) {
        *ps0++ = *ps1++;
        *ps0++ = *pd1++;
        }
}

int waveletMemory ()
{
    startTimer();
    int32 *a = (int32*)VIRTUAL_ALLOC((WaveMaxSize+2)*sizeof(int32));
    int total = WaveMaxSize;
    int iter = WaveIter;
    int i;
    int el = 0, elOld;
    do {
        int32 *s1 = a + (total >> 1) + 1;
        int32 *d1 = s1 + (total >> 2) + 1;
        for ( i = 0; i++ < iter; ) {
            liftMemory(total>>1,a,s1,d1);
            unliftMemory(total>>1,a,s1,d1);
            }
        elOld = el;
        el = elapsedTime();
        printf("%6.2f",(el-elOld)*0.001);
        fflush(stdout);
        iter += iter;
        } while ( (total /= 2) >= WaveMinSize );
    VIRTUAL_FREE(a,(WaveMaxSize+2)*sizeof(int32));
    int elapsed = elapsedTime();
    printf("\nWavelet T-S transform: arr: %dMB-%dKB, iter: %d-%d, elapsed time: %.2f sec\n",
           (WaveMaxSize*sizeof(int32))>>20,(WaveMinSize*sizeof(int32))>>10,WaveIter,iter>>1,elapsed*0.001);
    return elapsed;
}

//-----------------------------------------------------------
//  Wavelet transform (disk cache test):

#define S0FileName      "s0.bin"
#define S1FileName      "s1.bin"
#define D1FileName      "d1.bin"
#define WaveDMaxSize    (1<<24)
#define WaveDMinSize    (1<<17)
#define WaveDIter       1

void liftDisk ( int n0, FileOfDouble &s0, FileOfDouble &s1, FileOfDouble &d1 )
    // forward TS-transform
{
    int n1 = n0 >> 1;
    int i;
        // split + S-transform + prediction:
    s0.seek(0);
    s1.seek(0);
    d1.seek(0);
    double s[3], d[3];
    s[1] = s0.get();
    d[1] = s0.get();
    d[1] -= s[1];
    s[1] += d[1] * 0.5;
    s[0] = 0.0;
    for ( i = 1; i++ < n1; ) {
        s[2] = s0.get();
        d[2] = s0.get();
        d[2] -= s[2];
        s[2] += d[2] * 0.5;
        d[1] += (s[2] + s[0]) * 0.25;
        s1.put(s[1]);
        d1.put(d[1]);
        s[0] = s[1];
        s[1] = s[2];
        d[1] = d[2];
        }
    s[2] = 0.0;
    d[1] += (s[2] + s[0]) * 0.25;
    s1.put(s[1]);
    d1.put(d[1]);
}

void unliftDisk ( int n0, FileOfDouble &s0, FileOfDouble &s1, FileOfDouble &d1 )
    // inverse TS-transform
{
    int n1 = n0 >> 1;
    int i;
        // predict* + S-transform* + merge:
    s0.seek(0);
    s1.seek(0);
    d1.seek(0);
    double s[3], d[3];
    s[0] = 0.0;
    s[1] = s1.get();
    d[1] = d1.get();
    for ( i = 1; i++ < n1; ) {
        s[2] = s1.get();
        d[2] = d1.get();
        d[1] -= (s[2] + s[0]) * 0.25;
        double s1act = s[1] - d[1] * 0.5;
        d[1] += s1act;
        s0.put(s1act);
        s0.put(d[1]);
        s[0] = s[1];
        s[1] = s[2];
        d[1] = d[2];
        }
    s[2] = 0.0;
    d[1] -= (s[2] + s[0]) * 0.25;
    s[1] -= d[1] * 0.5;
    d[1] += s[1];
    s0.put(s[1]);
    s0.put(d[1]);
}

int waveletDisk ()
{
    FileOfDouble s0(S0FileName);
    FileOfDouble s1(S1FileName);
    FileOfDouble d1(D1FileName);
    int total = WaveDMaxSize;
    int iter = WaveDIter;
    int i;
    RandomJames rnd;
    startTimer();
    do {
        s0.rewrite();
        s1.rewrite();
        d1.rewrite();
        i = total >> 1;
        do
            s0.put(rnd.uniformNumber());
        while ( --i );
        int elOld = elapsedTime();
        for ( i = 0; i++ < iter; ) {
            liftDisk(total>>1,s0,s1,d1);
            unliftDisk(total>>1,s0,s1,d1);
            }
        int el = elapsedTime();
        printf("%6.2f",(el-elOld)*0.001);
        fflush(stdout);
        iter += iter;
        } while ( (total /= 2) >= WaveDMinSize );
    int elapsed = elapsedTime();
    s0.close();
    s1.close();
    d1.close();
    remove(S0FileName);
    remove(S1FileName);
    remove(D1FileName);
    printf("\nWavelet T-S transform on disk: arr: %dMB-%dMB, iter: %d-%d, time: %.2f sec\n",
           (WaveDMaxSize*sizeof(double))>>20,(WaveDMinSize*sizeof(double))>>20,WaveDIter,iter>>1,elapsed*0.001);
    return elapsed;
}

//-----------------------------------------------------------
//  Adaptive K-D tree on disk:

#define TreeInsertions      50000
#define TreeWalk           100000
#define TreeOperations     (TreeInsertions+TreeWalk)
#define TreeCache            2048
#define TreeXmax            10240
#define TreeYmax            10240
#define TreeWalkAmp          1024
#define TreeXhalf           (TreeXmax>>1)
#define TreeFile            "akd-tree.bin"

int akdTree ()
{
    startTimer();
    RandomJames rnd;
    AKDtree tree(TreeFile,TreeXhalf,TreeCache);
    int i;
    unsigned stat[6];
    for ( i = 0; i++ < TreeInsertions; ) {
        int32 x = (int32)(rnd.uniformNumber() * TreeXmax);
        int32 y = (int32)(rnd.uniformNumber() * TreeYmax);
        tree.insert(x,y);
        if ( !(i & 0x1FFF) ) {
            tree.getStat(stat);
            printf("Op: %dK, rd: %uK, wr: %uK, fl: %uK, leaf: %u, hits: %u\r",
                   i/1000,stat[0]/1000,stat[1]/1000,stat[2]/1000,stat[4],stat[5]);
            fflush(stdout);
            }
        }
    double x = TreeXmax * 0.5;
    double y = TreeYmax * 0.5;
    while ( i++ < TreeOperations ) {
        double dx = rnd.uniformNumber() * TreeWalkAmp - (TreeWalkAmp/2);
        double dy = rnd.uniformNumber() * TreeWalkAmp - (TreeWalkAmp/2);
        if ( (x += dx) < 1.0 ) x += TreeXmax;
        if ( x >= TreeXmax-1.0 ) x -= TreeXmax;
        if ( (y += dy) < 1.0 ) y += TreeYmax;
        if ( y >= TreeYmax-1.0 ) y -= TreeYmax;
        if ( rnd.uniformNumber() < 1.0/3.0 ) tree.insert((int)x,(int)y);
        else                                 tree.search((int)x,(int)y);
        if ( !(i & 0x1FFF) ) {
            tree.getStat(stat);
            printf("Op: %dK, rd: %uK, wr: %uK, fl: %uK, leaf: %u, hits: %u\r",
                   i/1000,stat[0]/1000,stat[1]/1000,stat[2]/1000,stat[4],stat[5]);
            fflush(stdout);
            }
        }
    tree.getStat(stat);
    tree.close();
    remove(TreeFile);
    int elapsed = elapsedTime();
    printf("Adaptive K-D tree on disk: operations: %dK, elapsed time: %.2f sec\n",
           TreeOperations/1000,elapsed*0.001);
    printf("Block reads: %uK, writes: %uK, flushes: %uK, leaf nodes: %u, hits: %u\n",
           stat[0]/1000,stat[1]/1000,stat[2]/1000,stat[4],stat[5]);
    return elapsed;
}

//-----------------------------------------------------------
//  Quick-sort in virtual memory:

#define QuickSortLen1    (8<<20)
#define QuickSortLen2   (32<<20)
#define QuickSortLen3  (128<<20)

bool quickSort ( double *a, int length )
{
    RandomJames rnd;
    int i;
    for ( i = 0; i < length; )              // data init
        a[i++] = rnd.uniformNumber();
    qs(a,length);                           // quick-sort
    for ( i = 1; i++ < length; a++ )        // check
        if ( a[0] > a[1] ) return false;
    return true;
}

int quickSortTest ( int length )
{
    startTimer();
    bool ok;
    int elapsed;
    int repeat = 0;
    do {
        if ( repeat++ ) {
            printf("Pass #%d\n",repeat-1);
            fflush(stdout);
            }
        double *a = (double*)VIRTUAL_ALLOC(length*sizeof(double));
        if ( !a ) return 0;
        ok = quickSort(a,length);
        VIRTUAL_FREE(a,length*sizeof(double));
        elapsed = elapsedTime();
        } while ( elapsed < MinElapsed );
    elapsed = (elapsed + (repeat>>1)) / repeat;
    printf("Quick-sort in virtual memory: %s, double[%dM], elapsed time: %.2f sec\n",
           ok?"OK":"failed",(length>>20),elapsed*0.001);
    return elapsed;
}

//-----------------------------------------------------------
//  state variables:

bool verbose = true;                        // verbose listing mode
bool changed = false;                       // results were changed
bool sort = false;                          // sort the result table
bool ascending = true;                      // ascending order

#define SortTime        0                   // time in seconds
#define SortRef         1                   // ref number (time/clocks)
#define SortClock       2                   // CPU clock speed in MHz

int sortType = SortTime;

unsigned restrictMemory = 0;
unsigned restrictVirtual = 0;
unsigned restrictDisk = 0;

//-----------------------------------------------------------
//  data persistence:

#define DataFile        "results.txt"
#define InitData        128
#define MaxProcName     128
#define CPUFile         "cpu.txt"
#define UndefinedCPU    "undefined"
#define UndefinedClock  1
#define SleepFlush      10000

struct DataItem {
    char *procName;                         // these strings are shared (on the heap)
    int clock;                              // CPU clock speed in MHz
    int stat;                               // test ordinal number
    int time;                               // time in milliseconds
    };

DataItem *di = NULL;
int diLen = 0;
int diAlloc = 0;

char *actCpu = UndefinedCPU;
char cpuBuf[MaxProcName];
int actClock = 0;

void assertData ( int i )
{
    if ( i <= diAlloc ) return;
    i = (i + InitData-1) & -InitData;
    DataItem *ndi = new DataItem[i];
    if ( diAlloc ) {
        memcpy(ndi,di,diAlloc*sizeof(DataItem));
        delete []di;
        }
    di = ndi;
    diAlloc = i;
}

char *assignProcName ( const char *name )
{
    int i;
    for ( i = 0; i < diLen; i++ )
        if ( !strcmp(name,di[i].procName) ) return di[i].procName;
    char *nn = new char[strlen(name)+1];
    strcpy(nn,name);
    return nn;
}

bool readData ()
    // <test#> <time-in-millisec> <clock-MHz> <CPU-name>
{
    char buf[MaxProcName<<1];
    diLen = 0;
    FILE *f = fopen(DataFile,"rt");
    if ( !f ) return false;
    do {
        if ( !fgets(buf,MaxProcName<<1,f) ) break;
        char *ptr = buf;
        int stat = 0;
        int read;
        if ( sscanf(ptr,"%d%n",&stat,&read) < 1 || stat < 1 ) break;
        ptr += read;
        while ( buf[strlen(buf)-1] == '\n' || isspace(buf[strlen(buf)-1]) )
            buf[strlen(buf)-1] = (char)0;
        int ti, cl;
        if ( sscanf(ptr,"%d%d%n",&ti,&cl,&read) < 2 ) break;
        ptr += read;
        assertData(diLen+1);
        di[diLen].stat = stat;
        di[diLen].time = ti;
        di[diLen].clock = cl;
        while ( isspace(*ptr) ) ptr++;
        di[diLen].procName = assignProcName(ptr);
        diLen++;
        } while ( true );
    fclose(f);
    return true;
}

bool writeData ()
{
    if ( !changed ) return false;
    FILE *f = fopen(DataFile,"wt");
    if ( !f ) return false;
    int i;
    for ( i = 0; i < diLen; i++ )
        fprintf(f,"%2d %7d %4d %s\n",di[i].stat,di[i].time,di[i].clock,di[i].procName);
    fclose(f);
    changed = false;
    return true;
}

void newResult ( int stat, int elapsed )
{
    bool insert = true;
    printf("%-44s%7.2f sec(%7.2f%%), ref:%8.3f\n",
           actCpu,elapsed*0.001,100.0,
           elapsed*1.e-6*actClock);
        // print comparisons:
    char buf[48];
    int i;
    for ( i = 0; i < diLen; i++ )
        if ( di[i].stat == stat )
            if ( strcmp(di[i].procName,actCpu) || di[i].clock != actClock ) {
                if ( verbose ) {            // compare it
                    strncpy(buf,di[i].procName,44);
                    buf[44] = (char)0;
                    printf("%-44s%7.2f sec(%7.2f%%), ref:%8.3f\n",
                           buf,di[i].time*0.001,(elapsed*100.0)/di[i].time,
                           di[i].time*1.e-6*di[i].clock);
                    }
                }
            else {                          // improve?
                if ( elapsed < di[i].time ) {
                    di[i].time = elapsed;
                    changed = true;
                    }
                insert = false;
                }
        // insert the new result:
    if ( insert ) {
        changed = true;
        assertData(diLen+1);
        di[diLen].stat = stat;
        di[diLen].time = elapsed;
        di[diLen].clock = actClock;
        di[diLen].procName = assignProcName(actCpu);
        diLen++;
        }
    writeData();
    SLEEP(SleepFlush);
}

int __CDECL cmpTime ( const void *e1, const void *e2 )
{
    int t1 = di[*((int*)e1)].time;
    int t2 = di[*((int*)e2)].time;
    if ( t1 == t2 ) return 0;
    return( (t1 < t2) == ascending ? -1 : 1 );
}

int __CDECL cmpRef ( const void *e1, const void *e2 )
{
    int t1 = di[*((int*)e1)].time * di[*((int*)e1)].clock;
    int t2 = di[*((int*)e2)].time * di[*((int*)e2)].clock;
    if ( t1 == t2 ) return 0;
    return( (t1 < t2) == ascending ? -1 : 1 );
}

int __CDECL cmpClock ( const void *e1, const void *e2 )
{
    int t1 = di[*((int*)e1)].clock;
    int t2 = di[*((int*)e2)].clock;
    if ( t1 == t2 ) {
        t1 = di[*((int*)e2)].time;
        t2 = di[*((int*)e1)].time;
        }
    if ( t1 == t2 ) return 0;
    return( (t1 < t2) == ascending ? -1 : 1 );
}

void listResult ( int stat )
{
    printf("\n  ");
    switch ( stat ) {
        case  1:
            printf("Eratosthen (%d-th prime number):\n",PrimeOrder);
            break;
        case  2:
            printf("Transposition of %dMB image:\n",(ImageWidth1*ImageHeight1)>>20);
            break;
        case  3:
            printf("Transposition of %dMB image:\n",(ImageWidth2*ImageHeight2)>>20);
            break;
        case  4:
            printf("Arbitrage sequence lookup (double[%d][%d] matrix):\n",ArbitrageN,ArbitrageN);
            break;
        case  5:
            printf("Needle-throwing simulation (Monte-Carlo, %dM iterations):\n",NeedleMax/1000000);
            break;
        case  6:
            printf("Fast memory-copy (two %d MB arrays, %d iterations):\n",MemCopyMax>>20,MemCopyIter);
            break;
        case  7:
            printf("Monte-Carlo form-factor (%dM iterations):\n",FFIter/1000000);
            break;
        case  8:
            printf("Merge-sort on disk (double[%dM] array):\n",SortSize>>20);
            break;
        case  9:
            printf("Wavelet (T-S) transform in memory (int32[%dM-%dK] arrays):\n",WaveMaxSize>>20,WaveMinSize>>10);
            break;
        case 10:
            printf("Wavelet (T-S) transform on disk (double[%dM-%dK] arrays):\n",WaveDMaxSize>>20,WaveDMinSize>>10);
            break;
        case 11:
            printf("Adaptive K-D tree on disk (%dK operations):\n",TreeOperations/1000);
            break;
        case 12:
            printf("Quick-sort in virtual memory (double[%dM] array):\n",QuickSortLen1>>20);
            break;
        case 13:
            printf("Quick-sort in virtual memory (double[%dM] array):\n",QuickSortLen2>>20);
            break;
        case 14:
            printf("Quick-sort in virtual memory (double[%dM] array):\n",QuickSortLen3>>20);
            break;
        }

        // print comparisons:
    int i;
    int *outTable = new int[diLen];         // output table to be sorted
    int outLen = 0;                         // length of output table
    for ( i = 0; i < diLen; i++ )
        if ( di[i].stat == stat ) outTable[outLen++] = i;

        // sort the table:
    if ( sort )
        switch ( sortType ) {
            case SortTime:
                qsort(outTable,outLen,sizeof(int*),&cmpTime);
                break;
            case SortRef:
                qsort(outTable,outLen,sizeof(int*),&cmpRef);
                break;
            case SortClock:
                qsort(outTable,outLen,sizeof(int*),&cmpClock);
                break;
            }

        // print the table:
    char buf[56];
    for ( i = 0; i < outLen; i++ ) {
        strncpy(buf,di[outTable[i]].procName,54);
        buf[54] = (char)0;
        printf("%-54s%7.2f sec, ref:%8.3f\n",
               buf,di[outTable[i]].time*0.001,
               di[outTable[i]].time*1.e-6*di[outTable[i]].clock);
        }

    delete []outTable;
}

//-----------------------------------------------------------
//  test suite:

#define MaxTestMask     0x3FFF
#define DefaultTestMask 0x1FFB

#define Mega(x)         ((x)<<20)
#define Giga(x)         ((x)<<30)
#define ReserveMemory   Mega(3)
#define ReserveVirtual  Mega(12)
#define ReserveDisk     Mega(1)

unsigned memoryLimits[] = {
    Mega(13)  + ReserveMemory,              //  1: Eratosthen
    Mega(32)  + ReserveMemory,              //  2: Transposition of 16MB image
    Mega(128) + ReserveMemory,              //  3: Transposition of 64MB image
    Mega(0)   + ReserveMemory,              //  4: Arbitrage
    Mega(0)   + ReserveMemory,              //  5: Needle
    Mega(32)  + ReserveMemory,              //  6: Memory copy
    Mega(0)   + ReserveMemory,              //  7: Monte-Carlo form-factor
    Mega(8)   + ReserveMemory,              //  8: Merge-sort on disk
    Mega(16)  + ReserveMemory,              //  9: Wavelets in memory
    Mega(0)   + ReserveMemory,              // 10: Wavelets on disk
    Mega(8)   + ReserveMemory,              // 11: A-K-D tree on disk
    Mega(0)   + ReserveMemory,              // 12: Quick-sort in VM (64M)
    Mega(0)   + ReserveMemory,              // 13: Quick-sort in VM (256M)
    Mega(0)   + ReserveMemory,              // 14: Quick-sort in VM (1G)
    };

unsigned virtualLimits[] = {
    Mega(13)  + ReserveVirtual,             //  1: Eratosthen
    Mega(32)  + ReserveVirtual,             //  2: Transposition of 16MB image
    Mega(128) + ReserveVirtual,             //  3: Transposition of 64MB image
    Mega(0)   + ReserveVirtual,             //  4: Arbitrage
    Mega(0)   + ReserveVirtual,             //  5: Needle
    Mega(32)  + ReserveVirtual,             //  6: Memory copy
    Mega(0)   + ReserveVirtual,             //  7: Monte-Carlo form-factor
    Mega(8)   + ReserveVirtual,             //  8: Merge-sort on disk
    Mega(16)  + ReserveVirtual,             //  9: Wavelets in memory
    Mega(0)   + ReserveVirtual,             // 10: Wavelets on disk
    Mega(8)   + ReserveVirtual,             // 11: A-K-D tree on disk
    Mega(64)  + ReserveVirtual,             // 12: Quick-sort in VM (64M)
    Mega(256) + ReserveVirtual,             // 13: Quick-sort in VM (256M)
    Giga(1)   + ReserveVirtual,             // 14: Quick-sort in VM (1G)
    };

unsigned diskLimits[] = {
    Mega(0)   + ReserveDisk,                //  1: Eratosthen
    Mega(0)   + ReserveDisk,                //  2: Transposition of 16MB image
    Mega(0)   + ReserveDisk,                //  3: Transposition of 64MB image
    Mega(0)   + ReserveDisk,                //  4: Arbitrage
    Mega(0)   + ReserveDisk,                //  5: Needle
    Mega(0)   + ReserveDisk,                //  6: Memory copy
    Mega(0)   + ReserveDisk,                //  7: Monte-Carlo form-factor
    Mega(128) + ReserveDisk,                //  8: Merge-sort on disk
    Mega(0)   + ReserveDisk,                //  9: Wavelets in memory
    Mega(128) + ReserveDisk,                // 10: Wavelets on disk
    Mega(58)  + ReserveDisk,                // 11: A-K-D tree on disk
    Mega(0)   + ReserveDisk,                // 12: Quick-sort in VM (64M)
    Mega(0)   + ReserveDisk,                // 13: Quick-sort in VM (256M)
    Mega(0)   + ReserveDisk,                // 14: Quick-sort in VM (1G)
    };

unsigned readBytes ( const char *len )
{
    char *ePtr = NULL;
    unsigned long result = strtoul(len,&ePtr,10);
    if ( ePtr )
        switch ( ePtr[0] ) {
            case 'g':
            case 'G':
                result <<= 10;
            case 'm':
            case 'M':
                result <<= 10;
            case 'k':
            case 'K':
                result <<= 10;
            }
    return (unsigned)result;
}

//-----------------------------------------------------------
//  main routine:

void printUsage ()
{
    printf("Usage: bench [-h] [-l] [-b] [-s{a|d}{t|r|c}] [-r{m|v|d}]\n"
           "             [<mask> [<clock> [\"cpu-description\"]]]\n"
           "    -h      print help message (this text)\n"
           "    -l      don't run any tests, print result table(s) only\n"
           "    -b      brief mode (don't print old results)\n"
           "    -s{a|d}{t|r|c}\n"
           "            sort results (ascending/descending, time/ref/clock)\n"
           "    -r{m|v|d} <bytes>\n"
           "            restrict tests according to physical RAM (m),\n"
           "            virtual memory (v) or free disk space (d).\n"
           "            <bytes> is number of bytes, examples: 256k, 64m, 2g\n"
           "    <mask>  is bit-mask (hexadecimal number 0xNNN can be used):\n"
           "\t0x0001 .. Eratosthen (%d-th prime number)\n"
           "\t0x0002 .. Transposition of %dMB image (uses two buffers!)\n"
           "\t0x0004 .. Transposition of %dMB image (uses two buffers!)\n"
           "\t0x0008 .. Arbitrage sequence lookup (double[%d][%d] matrix)\n"
           "\t0x0010 .. Needle-throwing simulation (Monte-Carlo, %dM iterations)\n"
           "\t0x0020 .. Fast memory-copy (two %d MB arrays, %d iterations)\n"
           "\t0x0040 .. Monte-Carlo form-factor (%dM iterations)\n"
           "\t0x0080 .. Merge-sort on disk (double[%dM] array)\n"
           "\t0x0100 .. Wavelet (T-S) transform in memory (int32[%dM-%dK] arrays)\n"
           "\t0x0200 .. Wavelet (T-S) transform on disk (double[%dM-%dK] arrays)\n"
           "\t0x0400 .. Adaptive K-D tree on disk (%dK operations)\n"
           "\t0x0800 .. Quick-sort in virtual memory (double[%dM] array)\n"
           "\t0x1000 .. Quick-sort in virtual memory (double[%dM] array)\n"
           "\t0x2000 .. Quick-sort in virtual memory (double[%dM] array)\n"
           "    <clock> is CPU clock in MHz (see the 2nd column of \"%s\")\n"
           "    \"cpu-description\" is arbitrary string (abbreviations are preferred,\n"
           "                      see \"%s\" ignoring first two columns)\n"
           "    if <clock> and \"cpu-description\" are not specified, program will\n"
           "            read the first line of \"%s\"\n"
           ,PrimeOrder,(ImageWidth1*ImageHeight1)>>20,
           (ImageWidth2*ImageHeight2)>>20,ArbitrageN,ArbitrageN,NeedleMax/1000000,
           MemCopyMax>>20,MemCopyIter,FFIter/1000000,SortSize>>20,
           WaveMaxSize>>20,WaveMinSize>>10,WaveDMaxSize>>20,WaveDMinSize>>10,TreeOperations/1000,
           QuickSortLen1>>20,QuickSortLen2>>20,QuickSortLen3>>20,
           CPUFile,CPUFile,CPUFile);
}

int __CDECL main ( int argc, char **argv )
{
    printf("VUCAKO Bench ver %s (c) 2001 Josef.Pelikan@mff.cuni.cz (Open Source)\n"
        "  Please send result file (%s) back to author!  Help: \"bench -h\"\n",VERSION,DataFile);

    unsigned mask = DefaultTestMask;
    bool listing = false;
    int arg = 1;
    while ( arg < argc && argv[arg][0] == '-' ) { // read one "-*"  argument
        if ( argv[arg][1] == 'h' ) {        // help
            printUsage();
            return 1;
            }
        else
        if ( argv[arg][1] == 'l' ) {        // listing
            listing = true;
            }
        else
        if ( argv[arg][1] == 'b' ) {        // brief mode
            verbose = false;
            }
        else
        if ( argv[arg][1] == 's' ) {        // sort result table
            if ( argv[arg][2] == 'a' || argv[arg][2] == 'd' ) {
                ascending = (argv[arg][2] == 'a');
                sort = true;
                switch ( argv[arg][3] ) {
                    case 't':
                        sortType = SortTime;
                        break;
                    case 'r':
                        sortType = SortRef;
                        break;
                    case 'c':
                        sortType = SortClock;
                        break;
                    default:
                        sort = false;
                    }
                }
            else {
                sort = false;
                fprintf(stderr,"Error in sorting argument \"%s\" (will be ignored)\n",argv[arg]);
                }
            }
        else
        if ( argv[arg][1] == 'r' ) {        // test restrictions
            char type = argv[arg][2];
            if ( type == 'm' || type == 'v' || type == 'd' ) {
                if ( ++arg < argc )
                    switch ( type ) {
                        case 'm':
                            restrictMemory = readBytes(argv[arg]);
                            break;
                        case 'v':
                            restrictVirtual = readBytes(argv[arg]);
                            break;
                        case 'd':
                            restrictDisk = readBytes(argv[arg]);
                            break;
                        }
                }
            else
                fprintf(stderr,"Error in restricting argument \"%s\" (will be ignored)\n",argv[arg]);
            }
        else
            fprintf(stderr,"Unknown argument \"%s\" (will be ignored), type \"bench -h\" for help!\n",
                    argv[arg]);
        arg++;
        }
    bool wasCpu = false;
    if ( arg < argc ) {
        mask = (unsigned)strtol(argv[arg++],NULL,0) & MaxTestMask;
        if ( arg < argc ) {                 // CPU is specified in the command-line
            wasCpu = true;
            actClock = atoi(argv[arg++]);
            if ( arg < argc )
                strcpy(actCpu=cpuBuf,argv[arg++]);
            }
        }

    if ( !wasCpu ) {                        // read CPU-info from CPUFile
        FILE *f = fopen(CPUFile,"rt");
        if ( f ) {
            fscanf(f,"%*s%d",&actClock);    // skip the e-mail
            fgets(cpuBuf,MaxProcName,f);
            while ( cpuBuf[strlen(cpuBuf)-1] == '\n' ) cpuBuf[strlen(cpuBuf)-1] = (char)0;
            actCpu = cpuBuf;
            while ( isspace(*actCpu) ) actCpu++;
            fclose(f);
            }
        else {
            fprintf(stderr,"CPU type & frequency were not specified in the command line,\n"
                           "CPU file \"%s\" was not found => using default values [\"%s\",%d]!\n",
                    CPUFile,actCpu,actClock);
            }
        }

    if ( !readData() )
        fprintf(stderr,"Error reading old result file \"%s\"!\n",DataFile);

    if ( listing ) {                        // print only old results
        arg = 0;
        while ( (1 << arg) & MaxTestMask ) {
            if ( (1 << arg) & mask ) listResult(arg+1);
            arg++;
            }
        return 0;
        }

        // test restrictions
    for ( arg = 0; (1 << arg) & MaxTestMask; arg++ )
        if ( ((1 << arg) & mask) && 
             (restrictMemory  && restrictMemory  < memoryLimits[arg] ||
              restrictVirtual && restrictVirtual < virtualLimits[arg] ||
              restrictDisk    && restrictDisk    < diskLimits[arg]) )
            mask -= (1 << arg);

    printf("Running tests \"%04X\" on \"%s\" (%d MHz):\n",mask,actCpu,actClock);
    int elapsed;

        // 1 - eratosthen:
    if ( mask & 0x0001 ) {
        elapsed = eratosthen(PrimeOrder,PrimeAssert);
        newResult(1,elapsed);
        }

        // image transposition:
    if ( mask & 0x0002 ) {                  // 2 - small image
        elapsed = imageTransposition(ImageWidth1,ImageHeight1,ImageIter1);
        newResult(2,elapsed);
        }

    if ( mask & 0x0004 ) {                  // 3 - bigger image
        elapsed = imageTransposition(ImageWidth2,ImageHeight2,ImageIter2);
        newResult(3,elapsed);
        }

        // 4 - arbitrage sequence:
    if ( mask & 0x0008 ) {
        startTimer();
        initInstance();
        solveInstance();
        elapsed = elapsedTime();
        newResult(4,elapsed);
        }

        // 5 - needle throwing simulation:
    if ( mask & 0x0010 ) {
        elapsed = needleSimulation();
        newResult(5,elapsed);
        }

        // 6 - fast mem-copy:
    if ( mask & 0x0020 ) {
        elapsed = memCopy();
        newResult(6,elapsed);
        }

        // 7 - Monte-Carlo form-factor:
    if ( mask & 0x0040 ) {
        elapsed = formFactor();
        newResult(7,elapsed);
        }

        // 8 - Merge-sort on disk:
    if ( mask & 0x0080 ) {
        elapsed = diskSort();
        newResult(8,elapsed);
        }

        // 9 - T-S wavelet transform in memory:
    if ( mask & 0x0100 ) {
        elapsed = waveletMemory();
        newResult(9,elapsed);
        }

        // 10 - T-S wavelet transform on disk:
    if ( mask & 0x0200 ) {
        elapsed = waveletDisk();
        newResult(10,elapsed);
        }

        // 11 - Adaptive K-D tree on disk:
    if ( mask & 0x0400 ) {
        elapsed = akdTree();
        newResult(11,elapsed);
        }

        // 12 - Quick-sort in virtual memory (short array /64MB/):
    if ( mask & 0x0800 ) {
        elapsed = quickSortTest(QuickSortLen1);
        if ( elapsed > 0 ) newResult(12,elapsed);
        }

        // 13 - Quick-sort in virtual memory (middle array /256MB/):
    if ( mask & 0x1000 ) {
        elapsed = quickSortTest(QuickSortLen2);
        if ( elapsed > 0 ) newResult(13,elapsed);
        }

        // 14 - Quick-sort in virtual memory (large array /1GB/):
    if ( mask & 0x2000 ) {
        elapsed = quickSortTest(QuickSortLen3);
        if ( elapsed > 0 ) newResult(14,elapsed);
        }

    return 0;
}
