package jamaica;

import java.io.PrintWriter;

/**
 * The testing program for the Matrix class
 *
 * @author Hanhua Feng - hanhua@cs.columbia.edu
 * @version $Id: MatrixTest.java,v 1.8 2003/05/21 15:32:24 hanhua Exp $
 */
public class MatrixTest {
    static Range xr;
    static Range yr;

    final static int rand( int n ) {
        return (int) Math.floor( Math.random() * n );
    }

    final static void randRange( int rows, int cols ) {
        int i;
        i = rand(cols-1);
        xr = new Range( i, rand(cols-i)+1, 1 );
        i = rand(rows-1);
        yr = new Range( i, rand(rows-i)+1, 1 );
    }

    public static void verifyMatrix( Matrix x, int rows, int cols ) {
        if ( rows != x.m || cols != x.n )
            throw new TestError( "Invalid dimensions: " 
                                 + x.m + "*" + x.n
                                 + " [should be " 
                                 + rows + "*" + cols + " ]" );
        if ( x.a == null )
            throw new TestError( "Null data" );
        int[] corners = { x.r, 
                          x.r+(rows-1)*x.ms,
                          x.r+(cols-1)*x.ns, 
                          x.r+(rows-1)*x.ms+(cols-1)*x.ns };
        for ( int i=0; i<4; i++ )
            if ( corners[i] < 0 || corners[i] >= x.a.length )
                throw new TestError( "Boundary error: " 
                                     + x.m + "*" + x.n + ", "
                                     + "length=" + x.a.length
                                     + ", r=" + x.r
                                     + ", ms=" + x.ms
                                     + ", ns=" + x.ns );
    }

    public static void compareMatrix( Matrix x, Matrix y, int nd ) {
        verifyMatrix( x, x.height(), x.width() );
        verifyMatrix( y, y.height(), y.width() );
        int n = x.ne(y,1E-10).count();
        int nx = x.eq(y,1E-10).count();
        if ( n != nd || nx != x.height()*x.width() - nd ) 
        {
            System.out.println( "X = " );
            x.print( 10, 4, 2 );
            System.out.println( "Y = " );
            y.print( 10, 4, 2 );

            BitArray ba = ( n != nd ) ? x.ne( y, 1E-10 ) : x.eq( y, 1E-10 );
            System.out.print( (n != nd) ? "  NE: " : "  EQ: " );
            for ( int i=0; i<ba.length(); i++ )
                System.out.print( ba.get(i)? "1" : "0" );
            System.out.println();

            throw new TestError( "Two matrices are different: " 
                                 + n + " diff (" + nx + " same) [should be " 
                                 + nd + " diff (" 
                                 + (x.height()*x.width() - nd) + " same)" );
        }
    }

    public static String testConstructor( int rows, int cols ) {
        Matrix A = new Matrix( rows, cols );
        verifyMatrix( A, rows, cols );
        Matrix B = new Matrix( rows, cols, 3.0 );
        verifyMatrix( B, rows, cols );
        Matrix C = new Matrix( A );
        verifyMatrix( C, rows, cols );
        Matrix D = Matrix.random( rows, cols );
        verifyMatrix( C, rows, cols );
        A.assign( B );
        compareMatrix( C, B, 0 );
        C.assign( 3.0 );
        compareMatrix( A, B, 0 );
        A.assign( D );
        compareMatrix( C, D, 0 );
        compareMatrix( D, D.copy(), 0 );

        A.refer( D );
        C.refer( D.copy() );
        A.set( rows/2, cols/2, 0.73 );        
        compareMatrix( A.copy(), C, 1 );
        
        return null;
    }

    public static String testReadWrite( int rows, int cols ) {
        Matrix A = new Matrix( rows, cols );
        Matrix B = Matrix.random( rows, cols );
        for ( int j=0; j<cols; j++ )
            for ( int i=0; i<rows; i++ )
                A.set( i, j, B.get( i, j ) );
        for ( int j=0; j<cols; j++ )
            for ( int i=0; i<rows; i++ )
                if ( A.get( i, j ) != B.get( i, j ) )
                    return "Read/Write failed";
        compareMatrix( A, B, 0 );
        for ( int j=0; j<cols; j++ )
            for ( int i=0; i<rows; i++ )
            {
                A.setAdd( i, j, A.get( i, j ) );
                B.setMul( i, j, 2.0 );
            }
        compareMatrix( A, B, 0 );
        for ( int j=0; j<cols; j++ )
            for ( int i=0; i<rows; i++ )
                A.setSub( i, j, B.get( i, j ) );
        compareMatrix( A, new Matrix( A.m, A.n ), 0 );
        return null;
    }

    public static String testSlicing( int rows, int cols ) {
        Matrix A = Matrix.random( rows, cols );
        randRange( rows, cols );
        compareMatrix( A.slice( yr, xr ), 
                       A.transpose().slice( xr, yr ).transpose(), 0 );
        compareMatrix( A.transpose().copy().slice( xr, yr ),
                       A.copy().slice( yr, xr ).transpose(), 0 );

        Matrix B = A.copy();
        A.slice( yr, xr ).assign( 3.4 );
        compareMatrix( A, B, xr.size() * yr.size() );
        B.transpose().slice( xr, yr ).assign( -3.4 ).selfneg();
        compareMatrix( A, B, 0 );

        B.slice( yr, xr ).assign( 0 );
        compareMatrix( A, B, xr.size() * yr.size() );
        for ( int i=xr.first(); i<=xr.last(); i++ )
            for ( int j=yr.first(); j<=yr.last(); j++ )
                if ( B.get( j, i ) != 0 )
                    return "Wrong result [" + j + "," + i + "] = " + B.get(j,i)
                        + " should be 0.";
        return null;
    }

    public static String testArithmetic( int rows, int cols ) {
        Matrix A = Matrix.random( rows, cols );
        Matrix B = A.uminus();
        compareMatrix( A.selfneg(), B, 0 );
        compareMatrix( A, B.assign( 1.23 ), rows*cols );

        B = A.copy();
        B.selfadd( A );
        B.selfadd( B );
        compareMatrix( A.plus( A.times(3.0) ), B, 0 );

        B.selfsub( B.minus( A ) );
        compareMatrix( A, B, 0 );

        B.selfemul( new Matrix(rows,cols,3.0) );
        compareMatrix( A.minus( A.times(2.0).uminus() ), B, 0 );

        B = Matrix.random( cols, rand( rows+cols ) + 1 );
        int n = Math.max( Math.max( rows, cols ), B.width() );
        int i = 1;
        while ( i < n )
            i <<= 1;
        n = i;

        Matrix A1 = new Matrix( n, n );
        Matrix B1 = new Matrix( n, n );
        A1.slice( new Range(0,rows,1), new Range(0,cols,1) ).assign( A );
        B1.slice( new Range(0,cols,1), new Range(0,B.width(),1) ).assign( B );
        Matrix C = A.transpose().copy();
        compareMatrix( A.selfrmul( B ), 
                       A1.strassen(B1).slice( new Range(0,rows,1), 
                                              new Range(0,B.width(),1) ), 0 );
        compareMatrix( C.selflmul( B.transpose() ).transpose(), 
                       B1.transpose().strassen(A1.transpose()).slice( 
                           new Range(0,B.width(),1),
                           new Range(0,rows,1) ).transpose(), 0 );
        return null;
    }

    public static String testInversion( int rows, int cols ) {
        Matrix B = Matrix.random( rows, cols );
        Matrix A = Matrix.random( rows, rows );
        compareMatrix( B, A.times( B.ldivide(A) ), 0 );
        return null;
    }

    public static String testRelational( int rows, int cols ) {
        Matrix A = Matrix.random(rows,cols);
        A.set(0,cols-1,0.5);
        compareMatrix( A.copy().assign( A.gt( 0.5 ), 1.0 ),
                       A.copy().assign( A.ge( 0.5 ), 1.0 ), 1 );
        compareMatrix( A.copy().assign( A.lt( 0.5 ), 1.0 ),
                       A.copy().assign( A.le( 0.5 ), 1.0 ), 1 );
        compareMatrix( A.copy().assign( A.eq( 0.5 ), 10.0 ),
                       A.copy().assign( A.ne( 0.5 ), 10.0 ), rows*cols );
        Matrix B = A.copy();
        B.assign( A.gt( 0.5 ), 0.0 );
        B.assign( A.lt( 0.5 ), 0.0 );
        compareMatrix( B, new Matrix( rows, cols ), 1 );
        B = A.times(3.0);
        B.assign( A.eq( 0.5 ), 0.0 );
        B.assign( A.ne( 0.5 ), 0.0 );
        compareMatrix( B, new Matrix( rows, cols ), 0 );

        B = Matrix.random( rows, cols );
        B.set( rows/2,cols/2,A.get(rows/2,cols/2) );
        compareMatrix( B.copy().assign( B.gt(A).not(), -1.0 ),
                       B.copy().assign( B.le(A), -1.0 ), 0 );
        compareMatrix( B.copy().assign( B.gt(A).not(), -1.0 ),
                       B.copy().assign( B.lt(A), -1.0 ), 1 );
        compareMatrix( B.copy().assign( B.ge(A).not(), -1.0 ),
                       B.copy().assign( B.lt(A), -1.0 ), 0 );
        compareMatrix( B.copy().assign( B.ne(A).not(), -1.0 ),
                       B.copy().assign( B.eq(A), -1.0 ), 0 );
        compareMatrix( B.copy().assign( B.ne(A,0.1).not(), -1.0 ),
                       B.copy().assign( B.eq(A,0.1), -1.0 ), 0 );
        return null;
    }

    public static String testException( int rows, int cols ) {
        return null;
    }

    public static void main( String[] args ) {
        for ( int rows=1; rows<100; rows += rand(rows)+1 )
            for ( int cols=1; cols<100; cols += rand(cols)+1 ) 
            {
                System.out.println( "Test " + rows + "*" + cols + "..." );
                String r;
                r = testConstructor( rows, cols );
                if ( r != null )
                    System.out.println( r );
                r = testReadWrite( rows, cols );
                if ( r != null )
                    System.out.println( r );
                r = testSlicing( rows, cols );
                if ( r != null )
                    System.out.println( r );
                r = testArithmetic( rows, cols );
                if ( r != null )
                    System.out.println( r );
                r = testInversion( rows, cols );
                if ( r != null )
                    System.out.println( r );
                r = testRelational( rows, cols );
                if ( r != null )
                    System.out.println( r );
            }
    }
}

class TestError extends RuntimeException {
    TestError( String msg ) {
        System.out.println( "Test error: " + msg );
    }
}

