/**
 *  Benchmark #4 - Arbitrage sequence lookup.
 *   @author  Josef.Pelikan@mff.cuni.cz
 *   @version 0.007 (2001/06/17)
 *   @since   0.007
 */
public class BenchmarkArbitrage extends Benchmark {

    /** Problem size. */
    protected final static int N = 12;

    /** Minimal arbitrage profit. */
    protected final static double MIN_BOUND = 1.0;

    /** Exchange rate matrix. */
    protected static double m[][] = new double[N][N];;

    /** Working copy of arbitrage sequence. */
    protected static int result[] = new int[N];

    /** Used currencies. */
    protected static boolean used[] = new boolean[N];

    /** Actual maximal sequence length (search depth). */
    protected static int len;

    /** Best arbitrage profit found so far. */
    protected static double best;

    /** Best arbitrage sequence found so far. */
    protected static int bestSeq[] = new int[N];

    /** Length of the best sequence found so far. */
    protected static int bestLen;

    /**
     *  Initializing constructor.
     */
    public BenchmarkArbitrage () {
        bestLen = 0;
    }

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

    /**
     *  Recursive solver routine (dynamic programming).
     */
    protected void solve ( double mul, int i )
    {
        int j;
        double act;
        if ( i + 1 == len )
            for ( j = 0; j < N; j++ ) {
                if ( !used[j] &&
                     (act = mul * m[result[i-1]][j] * m[j][result[0]]) > best ) {
                    best = act;
                    bestLen = len;
                    System.arraycopy(result,0,bestSeq,0,i);
                    bestSeq[i] = j;
                    }
                }
        else
            for ( j = 0; j < N; j++ )
                if ( !used[j] ) {
                    used[j] = true;
                    result[i] = j;
                    solve(mul*m[result[i-1]][j],i+1);
                    used[j] = false;
                    }
    }

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

            // initInstance:
        int i;
        RandomJames rnd = new RandomJames();
        for ( i = 0; i < N; i++ )
            rnd.normalNumbers(1.0,0.05,5,m[i]);

            // solveInstance:
        for ( i = 0; i < N; ) used[i++] = false;
        best = MIN_BOUND;
        bestLen = 0;
        for ( len = 2; len < N; len++ )
            for ( i = 0; i < N; i++ ) {
                used[i] = true;
                result[0] = i;
                solve(1.0,1);
                used[i] = false;
                }

    }

    /**
     *  Result string of the test. Should be called after the <code>runTest()</code>
     *  was finished.
     *   @return Result of the test.
     */
    public String testResult () {
        if ( bestLen <= 0 ) return "No arbitrage sequence exists!";
        StringBuffer str = new StringBuffer("Arbitrage profit: ");
        str.append( (int)(best * 1000.0 + 0.5) / 1000.0 );
        str.append(", seq: ");
        int i;
        for ( i = 0; i < bestLen; i++ ) {
            str.append(bestSeq[i]+1);
            str.append(" ");
            }
        str.append(bestSeq[0]+1);
        return new String(str);
    }

}
