int main( int argc, char **argv )
{
	clock_t	t0 = StartTiming();

/* ---------- */
/* Parameters */
/* ---------- */

	MPIInit( argc, argv );

	if( !gArgs.SetCmdLine( argc, argv ) ||
		!gArgs.GetRanges() ) {

		MPIExit();
		exit( 42 );
	}

/* ------------ */
/* Initial data */
/* ------------ */

	if( !LayerCat( vL, gArgs.tempdir, gArgs.cachedir,
			gArgs.zolo, gArgs.zohi, false ) ) {

		MPIExit();
		exit( 42 );
	}

	InitTables( gArgs.zilo, gArgs.zihi );

	{
		CLoadPoints	*LP = new CLoadPoints;
		LP->Load( gArgs.tempdir, gArgs.cachedir );
		delete LP;
	}

/* ----- */
/* Solve */
/* ----- */

	printf( "\n---- Solve ----\n" );

	SetSolveParams( gArgs.regtype, gArgs.Wr, gArgs.Etol );

	XArray	Xevn, Xodd;

	if( !strcmp( gArgs.mode, "A2A" ) ) {

		Xevn.Load( gArgs.prior );

		if( gArgs.untwist )
			UntwistAffines( Xevn );

		Xodd.Resize( 6 );
		Solve( Xevn, Xodd, gArgs.iters );
	}
	else if( !strcmp( gArgs.mode, "A2H" ) ) {

		Xevn.Resize( 8 );

		{	// limit A lifetime
			XArray	*A = new XArray;
			A->Load( gArgs.prior );

			if( gArgs.untwist )
				UntwistAffines( *A );

			Solve( *A, Xevn, 1 );
			delete A;
		}

		Xodd.Resize( 8 );
		Solve( Xevn, Xodd, gArgs.iters );
	}
	else if( !strcmp( gArgs.mode, "H2H" ) ) {

		Xevn.Load( gArgs.prior );
		Xodd.Resize( 8 );
		Solve( Xevn, Xodd, gArgs.iters );
	}
	else if( !strcmp( gArgs.mode, "eval" ) ) {

		Xevn.Load( gArgs.prior );

		if( gArgs.untwist )
			UntwistAffines( Xevn );

		gArgs.iters = 0;
	}
	else {	// split

		Xevn.Load( gArgs.prior );
		gArgs.iters = 0;
	}

	const XArray& Xfinal = ((gArgs.iters & 1) ? Xodd : Xevn);

/* ----------- */
/* Postprocess */
/* ----------- */

	if( gArgs.mode[0] == 's' ) {

		Split	S( Xfinal, gArgs.splitmin );

		S.Run();
	}
	else {

		Evaluate( Xfinal );

		if( gArgs.mode[0] != 'e' )
			Xfinal.Save();
	}

/* ------- */
/* Cleanup */
/* ------- */

	if( !wkid ) {
		printf( "\n" );
		StopTiming( stdout, "Lsq", t0 );
	}

	MPIExit();
	VMStats( stdout );

	return 0;
}
// Set wkid, and set nwks which determines if MPI is used,
// or not in the case that nwks == 1.
//
void MPIInit( int& argc, char**& argv )
{
// Look for the -nwks=j argument

    for( int i = 1; i < argc; ++i ) {
        if( GetArg( &nwks, "-nwks=%d", argv[i] ) )
            break;
    }

// Get wkid

    int	thrdtype = -1;

    if( nwks > 1 ) {

        // Start MPI

        MPI_Init_thread( &argc, &argv,
                         MPI_THREAD_FUNNELED, &thrdtype );

        MPI_Comm_rank( MPI_COMM_WORLD, &wkid );
    }

// Create log file 'lsqw_i'

    char slog[32];
    sprintf( slog, "lsqw_%d.txt", wkid );
    freopen( slog, "w", stdout );

    printf( "---- Read params ----\n" );

// MPI sanity checks

    if( nwks > 1 ) {

        // Verify worker count

        int	size;

        MPI_Comm_size( MPI_COMM_WORLD, &size );

        if( size != nwks ) {

            printf(
                "MPI: Bad worker count: %d, expected %d\n.",
                size, nwks );

            MPIExit();
            exit( 42 );
        }

        // Verify desired threading type.
        // Funneled means that the application can use
        // multiple threads but MPI communicates only
        // with the main thread.

        if( thrdtype != MPI_THREAD_FUNNELED ) {

            printf(
                "MPI: Not funneled thread type."
                " Try linking with -mt_mpi.\n" );

            MPIExit();
            exit( 42 );
        }
    }

    printf( "Worker %d / %d\n", wkid, nwks );
}