LinearOperator<double> epetraRightScale(
  const LinearOperator<double>& A,
  const Vector<double>& d)
{
  /* Extract the underlying Epetra matrix. Type checking is done
   * ny rcp_dynamic_cast, so we need no error check here. */
  RCP<const Epetra_CrsMatrix> A_crs = EpetraMatrix::getConcretePtr(A);
  
  /* Make a deep copy of A */
  RCP<Epetra_CrsMatrix> mtxCopy = rcp(new Epetra_CrsMatrix(*A_crs));

  /* Extract the underlying Epetra vector. Type checking is done
   * internally, so we need no error check here. */
  const Epetra_Vector& epv = EpetraVector::getConcrete(d);
  
  /* Scale the copy */
  mtxCopy->RightScale(epv);

  /* Prepare an operator object for the scaled matrix */
  RCP<LinearOperatorBase<double> > rtn 
    = rcp(new EpetraMatrix(mtxCopy, A.domain(), A.range()));
  return rtn;
  
}
  template <class Scalar> inline
  SolverState<Scalar> BlockTriangularSolver<Scalar>
  ::solve(const LinearOperator<Scalar>& op,
          const Vector<Scalar>& rhs,
          Vector<Scalar>& soln) const
  {
    int nRows = op.numBlockRows();
    int nCols = op.numBlockCols();

    soln = op.domain().createMember();
    //    bool converged = false;

    TEST_FOR_EXCEPTION(nRows != rhs.space().numBlocks(), std::runtime_error,
                       "number of rows in operator " << op
                       << " not equal to number of blocks on RHS "
                       << rhs);

    TEST_FOR_EXCEPTION(nRows != nCols, std::runtime_error,
                       "nonsquare block structure in block triangular "
                       "solver: nRows=" << nRows << " nCols=" << nCols);

    bool isUpper = false;
    bool isLower = false;

    for (int r=0; r<nRows; r++)
      {
        for (int c=0; c<nCols; c++)
          {
            if (op.getBlock(r,c).ptr().get() == 0 ||
                dynamic_cast<const SimpleZeroOp<Scalar>* >(op.getBlock(r,c).ptr().get()))
              {
                TEST_FOR_EXCEPTION(r==c, std::runtime_error,
                                   "zero diagonal block (" << r << ", " << c 
                                   << " detected in block "
                                   "triangular solver. Operator is " << op);
                continue;
              }
            else
              {
                if (r < c) isUpper = true;
                if (c < r) isLower = true;
              }
          }
      }

    TEST_FOR_EXCEPTION(isUpper && isLower, std::runtime_error, 
                       "block triangular solver detected non-triangular operator "
                       << op);

    bool oneSolverFitsAll = false;
    if ((int) solvers_.size() == 1 && nRows != 1) 
      {
        oneSolverFitsAll = true;
      }

    for (int i=0; i<nRows; i++)
      {
        int r = i;
        if (isUpper) r = nRows - 1 - i;
        Vector<Scalar> rhs_r = rhs.getBlock(r);
        for (int j=0; j<i; j++)
          {
            int c = j;
            if (isUpper) c = nCols - 1 - j;
            if (op.getBlock(r,c).ptr().get() != 0)
              {
                rhs_r = rhs_r - op.getBlock(r,c) * soln.getBlock(c);
              }
          }

        SolverState<Scalar> state;
        Vector<Scalar> soln_r;
        if (oneSolverFitsAll)
          {
            state = solvers_[0].solve(op.getBlock(r,r), rhs_r, soln_r);
          }
        else
          {
            state = solvers_[r].solve(op.getBlock(r,r), rhs_r, soln_r);
          }
        if (nRows > 1) soln.setBlock(r, soln_r);
        else soln = soln_r;
        if (state.finalState() != SolveConverged)
          {
            return state;
          }
      }

    return SolverState<Scalar>(SolveConverged, "block solves converged",
                               0, ScalarTraits<Scalar>::zero());
  }
int main(int argc, char *argv[]) 
{
  typedef Teuchos::ScalarTraits<double> ST;

  try
  {
    GlobalMPISession session(&argc, &argv);

    MPIComm::world().synchronize();

    VectorType<double> type = new EpetraVectorType();


#ifdef HAVE_CONFIG_H
      ParameterXMLFileReader reader(Sundance::searchForFile("SolverParameters/userPrecParams.xml"));
#else
      ParameterXMLFileReader reader("userPrecParams.xml");
#endif

    ParameterList solverParams = reader.getParameters();
    ParameterList innerSolverParams = solverParams.sublist("Inner Solve");
    ParameterList outerSolverParams = solverParams.sublist("Outer Solve");

    /* create the range space  */
    int nLocalRows = solverParams.get<int>("nLocal");

    MatrixLaplacian1D builder(nLocalRows, type);

    LinearOperator<double> A = builder.getOp();

    Vector<double> x = A.domain().createMember();
    int myRank = MPIComm::world().getRank();
    int nProcs = MPIComm::world().getNProc();
      

    Thyra::randomize(-ST::one(),+ST::one(),x.ptr().ptr());
#ifdef TRILINOS_6
    if (myRank==0) x[0] = 0.0;
    if (myRank==nProcs-1) x[nProcs * nLocalRows - 1] = 0.0;
    // need to fix operator[] routine
#else
    if (myRank==0) x.setElement(0, 0);
    if (myRank==nProcs-1) x.setElement(nProcs * nLocalRows - 1, 0.0);
#endif

    cout << "x=" << std::endl;
    x.print(cout);
      
    Vector<double> y = A*x;
    cout << "y=" << std::endl;
    y.print(cout);

    Vector<double> ans = A.range().createMember();

    LinearSolver<double> innerSolver 
      = LinearSolverBuilder::createSolver(innerSolverParams);

    LinearSolver<double> outerSolver 
      = LinearSolverBuilder::createSolver(outerSolverParams);

    /* call the setUserPrec() function to set the operator and solver 
     * to be used for preconditioning */
    outerSolver.setUserPrec(A, innerSolver);

    LinearOperator<double> AInv = inverse(A, outerSolver);
      

    ans = AInv * y;

    //      SolverState<double> state = solver.solve(A, y, ans);
      

      
    //      cout << state << std::endl;

    cout << "answer is " << std::endl;
    ans.print(cout);
      
    double err = (x-ans).norm2();
    cout << "error norm = " << err << std::endl;

    double tol = 1.0e-8;
    if (err > tol)
    {
      cout << "User-defined preconditioner test FAILED" << std::endl;
      return 1;
    }
    else
    {
      cout << "User-defined preconditioner test PASSED" << std::endl;
      return 0;
    }
  }
  catch(std::exception& e)
  {
    cout << "Caught exception: " << e.what() << std::endl;
    return -1;
  }
}
bool DuffingFloquet()
{
  int np = MPIComm::world().getNProc();
  TEUCHOS_TEST_FOR_EXCEPT(np != 1);

  const double pi = 4.0*atan(1.0);

  /* We will do our linear algebra using Epetra */
  VectorType<double> vecType = new EpetraVectorType();

  /* Create a periodic mesh */
  int nx = 128;

  MeshType meshType = new PeriodicMeshType1D();
  MeshSource mesher = new PeriodicLineMesher(0.0, 2.0*pi, nx, meshType);
  Mesh mesh = mesher.getMesh();

  /* Create a cell filter that will identify the maximal cells
   * in the interior of the domain */
  CellFilter interior = new MaximalCellFilter();
  CellFilter pts = new DimensionalCellFilter(0);
      
  CellFilter left = pts.subset(new CoordinateValueCellPredicate(0,0.0));
  CellFilter right = pts.subset(new CoordinateValueCellPredicate(0,2.0*pi));
      
  /* Create unknown and test functions, discretized using first-order
   * Lagrange interpolants */
  Expr u1 = new UnknownFunction(new Lagrange(1), "u1");
  Expr u2 = new UnknownFunction(new Lagrange(1), "u2");
  Expr v1 = new TestFunction(new Lagrange(1), "v1");
  Expr v2 = new TestFunction(new Lagrange(1), "v2");

  /* Create differential operator and coordinate function */
  Expr dx = new Derivative(0);
  Expr x = new CoordExpr(0);

  /* We need a quadrature rule for doing the integrations */
  QuadratureFamily quad = new GaussianQuadrature(4);

  double F0 = 0.5;
  double gamma = 2.0/3.0;
  double a0 = 1.0;
  double w0 = 1.0;
  double eps = 0.5;

  Expr u1Guess = -0.75*cos(x) + 0.237*sin(x);
  Expr u2Guess = 0.237*cos(x) + 0.75*sin(x);

  DiscreteSpace discSpace(mesh, 
    List(new Lagrange(1), new Lagrange(1)),
    vecType);
  L2Projector proj(discSpace, List(u1Guess, u2Guess));
  Expr u0 = proj.project();


  Expr rhs1 = u2;
  Expr rhs2 = -w0*w0*u1 - gamma*u2 - eps*w0*w0*pow(u1,3.0)/a0/a0 
    + F0*w0*w0*sin(x);

  /* Define the weak form */
  Expr eqn = Integral(interior, 
    v1*(dx*u1 - rhs1) + v2*(dx*u2 - rhs2),
    quad);
  Expr dummyBC ; 

  NonlinearProblem prob(mesh, eqn, dummyBC, List(v1,v2), List(u1,u2), 
    u0, vecType);


  ParameterXMLFileReader reader("nox.xml");
  ParameterList solverParams = reader.getParameters();

  Out::root() << "finding periodic solution" << endl;
  NOXSolver solver(solverParams);
  prob.solve(solver);

  /* unfold the solution onto a non-periodic mesh */
      
  Expr uP = unfoldPeriodicDiscreteFunction(u0, "u_p");
  Out::root() << "uP=" << uP << endl;
      
  Mesh unfoldedMesh = DiscreteFunction::discFunc(uP)->mesh();
  DiscreteSpace unfDiscSpace = DiscreteFunction::discFunc(uP)->discreteSpace();

  FieldWriter writer = new MatlabWriter("Floquet.dat");
  writer.addMesh(unfoldedMesh);
  writer.addField("u_p[0]", new ExprFieldWrapper(uP[0]));
  writer.addField("u_p[1]", new ExprFieldWrapper(uP[1]));

  Array<Expr> a(2);
  a[0] = new Sundance::Parameter(0.0, "a1");
  a[1] = new Sundance::Parameter(0.0, "a2");


  Expr bc = EssentialBC(left, v1*(u1-uP[0]-a[0]) + v2*(u2-uP[1]-a[1]), quad);

  NonlinearProblem unfProb(unfoldedMesh, eqn, bc, 
    List(v1,v2), List(u1,u2), uP, vecType);

  unfProb.setEvalPoint(uP);

  LinearOperator<double> J = unfProb.allocateJacobian();
  Vector<double> b = J.domain().createMember();

  LinearSolver<double> linSolver
    = LinearSolverBuilder::createSolver("amesos.xml");
        
  SerialDenseMatrix<int, double> F(a.size(), a.size());

  for (int i=0; i<a.size(); i++)
  {
    Out::root() << "doing perturbed orbit #" << i << endl;
    for (int j=0; j<a.size(); j++) 
    {
      if (i==j) a[j].setParameterValue(1.0);
      else a[j].setParameterValue(0.0);
    }
        
    unfProb.computeJacobianAndFunction(J, b);
    Vector<double> w = b.copy();
    linSolver.solve(J, b, w);
    Expr w_i = new DiscreteFunction(unfDiscSpace, w);

    for (int j=0; j<a.size(); j++)
    {
      Out::root() << "postprocessing" << i << endl;

      writer.addField("w[" + Teuchos::toString(i)
        + ", " + Teuchos::toString(j) + "]", new ExprFieldWrapper(w_i[j]));
      Expr g = Integral(right, w_i[j], quad);
      F(j,i) = evaluateIntegral(unfoldedMesh, g);
    }
  }

  writer.write();

  Out::root() << "Floquet matrix = " << endl
              << F << endl;
        

  Out::root() << "doing eigenvalue analysis" << endl;
  Array<double> ew_r(a.size());
  Array<double> ew_i(a.size());
  int lWork = 6*a.size();
  Array<double> work(lWork);
  int info = 0;
  LAPACK<int, double> lapack;
  lapack.GEEV('N','N', a.size(), F.values(),
    a.size(), &(ew_r[0]), &(ew_i[0]), 0, 1, 0, 1, &(work[0]), lWork,
    &info);

  TEUCHOS_TEST_FOR_EXCEPTION(info != 0,
    std::runtime_error,
    "LAPACK GEEV returned error code =" << info);
      
  Array<double> ew(a.size());
  for (int i=0; i<a.size(); i++)
  {
    ew[i] = sqrt(ew_r[i]*ew_r[i]+ew_i[i]*ew_i[i]);
    Out::root() << setw(5) << i 
                << setw(16) << ew_r[i] 
                << setw(16) << ew_i[i] 
                << setw(16) << ew[i]
                << endl;
  }

  double err = ::fabs(ew[0] - 0.123);
  return SundanceGlobal::checkTest(err, 0.001);
}
inline bool LinearCombinationTester<Scalar>
::selfModifyingOpTests() const
{
  bool pass = true;

  RandomSparseMatrixBuilder<double> ABuilder(nLocalRows_, nLocalRows_, 
    onProcDensity_, offProcDensity_, 
    vecType_);
  RandomSparseMatrixBuilder<double> BBuilder(nLocalRows_, nLocalRows_, 
    onProcDensity_, offProcDensity_, 
    vecType_);
  RandomSparseMatrixBuilder<double> CBuilder(nLocalRows_, nLocalRows_, 
    onProcDensity_, offProcDensity_, 
    vecType_);


  LinearOperator<double> A = ABuilder.getOp();
  LinearOperator<double> B = BBuilder.getOp();
  LinearOperator<double> C = CBuilder.getOp();

  VectorSpace<Scalar> vs = A.domain();
  A = identityOperator(vs);
  B = identityOperator(vs);
  C = identityOperator(vs);

  Vector<Scalar> x = A.domain().createMember();
  Vector<Scalar> y = A.domain().createMember();
  Vector<Scalar> z = A.domain().createMember();

  this->randomizeVec(x);
  this->randomizeVec(y);
  this->randomizeVec(z);

  Vector<Scalar> a = x.copy();
  Vector<Scalar> b = y.copy();
  Vector<Scalar> c = z.copy();
    

  Out::os() << "starting linear combination tests" << std::endl;

  x = 2.0*A*x;
  Scalar err = (x - 2.0*A*a).norm2();
  if (!this->checkTest(spec_, err, "x=2.0*A*x")) pass = false;

  a = x.copy();
  x = x + y;
  err = (x - (a + y)).norm2();
  if (!this->checkTest(spec_, err, "x=x+y")) pass = false;

  a = x.copy();
  x = y + x;
  err = (x - (y + a)).norm2();
  if (!this->checkTest(spec_, err, "x=y+x")) pass = false;

  a = x.copy();
  x = A*x + B*y;
  err = (x - (A*a + B*y)).norm2();
  if (!this->checkTest(spec_, err, "x=A*x+B*y")) pass = false;

  a = x.copy();
  x = B*y + A*x;
  err = (x - (B*y + A*a)).norm2();
  if (!this->checkTest(spec_, err, "x=B*y+A*x")) pass = false;

  a = x.copy();
  x = A*x + (B*y + C*x);
  err = (x - (A*a + (B*y + C*a))).norm2();
  if (!this->checkTest(spec_, err, "x=A*x + (B*y + C*x)")) pass = false;

  a = x.copy();
  x = (A*x + B*y) + C*x;
  err = (x - ((A*a + B*y) + C*a)).norm2();
  if (!this->checkTest(spec_, err, "x=(A*x + B*y) + C*x")) pass = false;

  /* test assignment of OpTimesLC into empty and non-empty vectors */
  Vector<Scalar> u;
  u = 2.0*A*B*x;
  err = (u - 2.0*A*B*x).norm2();
  if (!this->checkTest(spec_, err, "(empty) u=2*A*B*x")) pass = false;

  u = 2.0*A*B*x;
  err = (u - 2.0*A*B*x).norm2();
  if (!this->checkTest(spec_, err, "(non-empty) u=2*A*B*x")) pass = false;

  /* test assignment of LC2 into empty and non-empty vectors */
  Vector<Scalar> v;
  v = 2.0*x + 3.0*y;
  err = (v - (2.0*x + 3.0*y)).norm2();
  if (!this->checkTest(spec_, err, "(empty) v=2*x + 3*y")) pass = false;

  v = 2.0*x + 3.0*y;
  err = (v - (2.0*x + 3.0*y)).norm2();
  if (!this->checkTest(spec_, err, "(non-empty) v=2*x + 3*y")) pass = false;

  /* test assignment of LC3 into empty and non-empty vectors */
  Vector<Scalar> w;
  w = 2.0*x + 3.0*y + 5.0*z;
  err = (w - (2.0*x + 3.0*y + 5.0*z)).norm2();
  if (!this->checkTest(spec_, err, "(empty) w=2*x + 3*y + 5*z")) pass = false;

  w = 2.0*x + 3.0*y + 5.0*z;
  err = (w - (2.0*x + 3.0*y + 5.0*z)).norm2();
  if (!this->checkTest(spec_, err, "(non-empty) w=2*x + 3*y + 5*z")) pass = false;

  /* test assignment of LC4 into empty and non-empty vectors */
  Vector<Scalar> w2;
  w2 = 2.0*x + 3.0*y + 5.0*z + 7.0*u;
  err = (w2 - (2.0*x + 3.0*y + 5.0*z + 7.0*u)).norm2();
  if (!this->checkTest(spec_, err, 
      "(empty) w2=2*x + 3*y + 5*z + 7*u")) pass = false;

  w2 = 2.0*x + 3.0*y + 5.0*z + 7.0*u;
  err = (w2 - (2.0*x + 3.0*y + 5.0*z + 7.0*u)).norm2();
  if (!this->checkTest(spec_, err, 
      "(non-empty) w2=2*x + 3*y + 5*z + 7*u")) pass = false;

  /* test assignment of LC3 into one of the operands */
  x = 2.0*x + 3.0*y + 5.0*z;
  err = (w - x).norm2(); // Note: w contains 2x + 3y + 5z 
  if (!this->checkTest(spec_, err, "x=2*x + 3*y + 5*z")) pass = false;


  return pass;
}
inline bool LinearCombinationTester<Scalar>
::nonModifyingOpTests() const
{
  bool pass = true;

  RandomSparseMatrixBuilder<double> ABuilder(nLocalRows_, nLocalRows_, 
    onProcDensity_, offProcDensity_, 
    vecType_);
  RandomSparseMatrixBuilder<double> BBuilder(nLocalRows_, nLocalRows_, 
    onProcDensity_, offProcDensity_, 
    vecType_);
  RandomSparseMatrixBuilder<double> CBuilder(nLocalRows_, nLocalRows_, 
    onProcDensity_, offProcDensity_, 
    vecType_);


  LinearOperator<double> A = ABuilder.getOp();
  LinearOperator<Scalar> B = BBuilder.getOp();
  LinearOperator<Scalar> C = CBuilder.getOp();

  Vector<Scalar> x = A.domain().createMember();
  Vector<Scalar> y = A.domain().createMember();
  Vector<Scalar> z = A.domain().createMember();

  this->randomizeVec(x);
  this->randomizeVec(y);
  this->randomizeVec(z);


  TESTER(x*2.0, 2.0*x);

  TESTER(2.0*(x + y), 2.0*x + 2.0*y);

  TESTER(2.0*(x - y), 2.0*x - 2.0*y);

  TESTER((2.0*x + y) - y, 2.0*x);

  TESTER(-1.0*y + (2.0*x + y), 2.0*x);

  TESTER((x + y)*2.0, 2.0*x + 2.0*y);

  TESTER((x - y)*2.0, 2.0*x - 2.0*y);

  TESTER(2.0*(x - y), -2.0*(y - x));

  TESTER(0.25*(2.0*(x + y) - 2.0*(x - y)), y);

  TESTER((2.0*A)*x, 2.0*(A*x));

  TESTER(2.0*(A*x), (A*x)*2.0);

  TESTER(A*(B*x), (A*B)*x);

  TESTER(2.0*A*(B*x), A*(B*(2.0*x)));

  TESTER(3.0*(2.0*A)*x, 6.0*(A*x));

  TESTER(y + A*x, A*x + y);


  TESTER(z + (A*x + B*y), (B*y + A*x) + z);


  TESTER(z - (A*x + B*y), -1.0*((B*y + A*x) - z));

  TESTER(C*z + (A*x + B*y), (B*y + A*x) + C*z);

  TESTER(C*z - (A*x + B*y), -1.0*((B*y + A*x) - C*z));

  TESTER(2.0*z + (A*x + B*y), (B*y + A*x) + 2.0*z);

  TESTER(2.0*z - (A*x + B*y), -1.0*((B*y + A*x) - 2.0*z));

  TESTER(A*x - y, -1.0*(y - A*x));

  TESTER(A*x + B*y, B*y + A*x);

  TESTER(A*x - B*y - A*x + B*y +z, z);

  TESTER(2.0*(A*x + y), 2.0*A*x + 2.0*y);

  TESTER(2.0*(A*x + B*y), A*x + B*y + A*x + B*y);

  TESTER(2.0*(y + A*x), 2.0*y + 2.0*(A*x));

  TESTER(x + 2.0*A*y, x + 2.0*(A*y));

  TESTER(2.0*A*y + x, 2.0*(A*y) + x);

  TESTER(2.0*A*(3.0*B)*y, 6.0*(A*B*y));

  TESTER(2.0*A*(3.0*B)*y, 6.0*(A*(B*y)));

  TESTER(2.0*A*(3.0*B + 2.0*A)*x, 6.0*A*B*x + 4.0*A*A*x );

  TESTER(2.0*(A*x + B*y), 2.0*A*x + 2.0*B*y);

  TESTER(2.0*(A*x - B*y), 2.0*A*x - 2.0*B*y);

  TESTER(2.0*(A*x + B*y + z), 2.0*A*x + 2.0*B*y + 2.0*z);

  TESTER(2.0*(A*x + 3.0*B*y), 2.0*A*x + 6.0*B*y);

  TESTER(2.0*(A*x + 3.0*(z + B*y)), 2.0*A*x + 6.0*B*y + 6.0*z);

  TESTER(2.0*(z + A*x + B*y + z), 2.0*A*x + 2.0*B*y + 4.0*z);

  TESTER(2.0*(3.0*(z + A*x) + B*y), 6.0*z + 6.0*A*x + 2.0*B*y);

  TESTER(2.0*(3.0*(z + A*x) + 4.0*(B*y + z)), 6.0*z + 6.0*A*x + 8.0*B*y + 8.0*z);
    
  TESTER((A*x + B*y) + (A*y + B*x), (A + B)*x + (A+B)*y);
  TESTER((A*x + B*y) - (A*y + B*x), A*x - A*y + B*y - B*x);

  TESTER((A*x + B*y) + 2.0*(A*y + B*x), A*(x + 2.0*y) + B*(2.0*x + y));
  TESTER((A*x + B*y) - 2.0*(A*y + B*x), A*(x - 2.0*y) + B*(y - 2.0*x));


  return pass;
}
int main(int argc, char *argv[]) 
{
  typedef Teuchos::ScalarTraits<double> ST;

  try
  {
    GlobalMPISession session(&argc, &argv);

    MPIComm::world().synchronize();

    VectorType<double> type = new EpetraVectorType();


    ParameterXMLFileReader reader("anasazi-ml.xml");
    ParameterList solverParams = reader.getParameters().sublist("Eigensolver");

    /* create the range space  */
    int nLocalRows = 40;
    MatrixLaplacian1D builder(nLocalRows, type);
    typedef Anasazi::MultiVec<double> MV;
    typedef Anasazi::Operator<double> OP;

    LinearOperator<double> A = builder.getOp();
    LinearOperator<double> M;

    Teuchos::RCP<Anasazi::OutputManager<double> > MyOM = Teuchos::rcp( new Anasazi::BasicOutputManager<double>() );
    MyOM->setVerbosity(Anasazi::Warnings);

    int nv = 1;
    RCP<const Array<Vector<double> > > initMV = rcp(new Array<Vector<double> >(nv));
    RCP<Array<Vector<double> > > nc 
      = rcp_const_cast<Array<Vector<double> > >(initMV);
    for (int i=0; i<nv; i++) 
    {
      (*nc)[i] = A.domain().createMember();
      (*nc)[i].randomize();
    }

#ifdef BLARF
    bool mvPass = Anasazi::TestMultiVecTraits<double,Array<Vector<double> > >(MyOM,initMV);
    if (mvPass) Out::os() << "******* MV unit test PASSED ******* " << endl;
    else Out::os() << "******* MV unit test FAILED ******* " << endl;

    RCP<const LinearOperator<double> > APtr = rcp(&A, false);
    bool opPass = Anasazi::TestOperatorTraits<double, Array<Vector<double> >, LinearOperator<double>  >(MyOM, initMV, APtr);
    if (opPass) Out::os() << "******* OP unit test PASSED ******* " << endl;
    else Out::os() << "******* OP unit test FAILED ******* " << endl;
#endif
    Eigensolver<double> solver = new AnasaziEigensolver<double>(solverParams);
    
    Array<Vector<double> > ev;
    Array<std::complex<double> > ew;
    
    solver.solve(A, M, ev, ew);

    Out::os() << "Eigenvalues are " << ew << endl;

    const double pi = 4.0*atan(1.0);
    double err = 0.0;
    for (int i=0; i<ev.size(); i++)
    {
      double x = (i+1)*pi;
      err += ::fabs(ew[i].real()-x*x)/x/x;
    }
    err = err / ew.size();
    
    Out::os() << "error = " << err << endl;
    if (err < 0.01)
    {
      cout << "Belos poisson solve test PASSED" << std::endl;
      return 0;
    }
    else
    {
      cout << "Belos poisson solve test FAILED" << std::endl;
      return 1;
    }
  }
  catch(std::exception& e)
  {
    cout << "Caught exception: " << e.what() << std::endl;
    return -1;
  }
}
int main(int argc, char *argv[]) 
{
  typedef Teuchos::ScalarTraits<double> ST;

  try
    {
      GlobalMPISession session(&argc, &argv);
      

      MPIComm::world().synchronize();

      VectorType<double> type = new EpetraVectorType();

      /* create the range space  */
      int nLocalRows = 10;

      MatrixLaplacian1D builder(nLocalRows, type);

      LinearOperator<double> A = builder.getOp();

      int nBlocks = 3;
      Array<Vector<double> > x(nBlocks);
      Array<VectorSpace<double> > space(nBlocks);
      for (int i=0; i<nBlocks; i++)
        {
          space[i] = A.domain();
          x[i] = A.domain().createMember();
          Thyra::randomize(-ST::one(),+ST::one(),x[i].ptr().ptr());          
        }

      VectorSpace<double> blockSpace = productSpace(space);

      LinearOperator<double> bigA = makeBlockOperator(blockSpace, blockSpace);
      Vector<double> bigRHS = blockSpace.createMember();
      Vector<double> bigX = blockSpace.createMember();
      
      for (int i=0; i<nBlocks; i++)
        {
          bigX.setBlock(i, x[i]);
          for (int j=i; j<nBlocks; j++)
            {
              MatrixLaplacian1D builder(nLocalRows, type);
              LinearOperator<double> Aij = builder.getOp();
              bigA.setBlock(i,j,Aij);
            }
        }
      bigA.endBlockFill();
      
      bigRHS = bigA * bigX;
      Vector<double> bigSoln = blockSpace.createMember();

#ifdef HAVE_CONFIG_H
      ParameterXMLFileReader reader(Sundance::searchForFile("SolverParameters/poissonParams.xml"));
#else
      ParameterXMLFileReader reader("poissonParams.xml");
#endif

      ParameterList solverParams = reader.getParameters();
      LinearSolver<double> solver 
        = LinearSolverBuilder::createSolver(solverParams);
      LinearSolver<double> blockSolver 
        = new BlockTriangularSolver<double>(solver);
      
      SolverState<double> state = blockSolver.solve(bigA, bigRHS, bigSoln);
      
      std::cerr << state << std::endl;

      double err = (bigSoln - bigX).norm2();
      std::cerr << "error norm = " << err << std::endl;

      double tol = 1.0e-8;
      if (err > tol)
        {
          std::cerr << "Poisson solve test FAILED" << std::endl;
          return 1;
        }
      else
        {
          std::cerr << "Poisson solve test PASSED" << std::endl;
          return 0;
        }
    }
  catch(std::exception& e)
    {
      std::cerr << "Caught exception: " << e.what() << std::endl;
      return -1;
    }
  return 0;
}
Preconditioner<double> 
PCDPreconditionerFactory::
createPreconditioner(const LinearOperator<double>& K) const
{
  Tabs tab;

  LinearOperator<double> F = K.getBlock(0,0);
//  F.setName("F");
  LinearOperator<double> FInv = inverse(F, FSolver_);
//  FInv.setName("FInv");
  LinearOperator<double> Bt = K.getBlock(0,1);
//  Bt.setName("Bt");


  LinearOperator<double> Fp = FpProb_.getOperator();

  LinearOperator<double> Mp = MpProb_.getOperator();
//  Mp.setName("Mp");

  LinearOperator<double> MpInv = inverse(Mp, MpSolver_);
//  MpInv.setName("MpInv");

  LinearOperator<double> Ap = ApProb_.getOperator();
//  Ap.setName("Ap");

  LinearOperator<double> ApInv = inverse(Ap, ApSolver_);
//  ApInv.setName("ApInv");


  VectorSpace<double> pDomain = Bt.domain();
  VectorSpace<double> uDomain = F.domain();

  LinearOperator<double> Iu = identityOperator(uDomain);
//  Iu.setName("Iu");
  LinearOperator<double> Ip = identityOperator(pDomain);
//  Ip.setName("Ip");

  LinearOperator<double> XInv = MpInv * Fp * ApInv;

  VectorSpace<double> rowSpace = K.range();
  VectorSpace<double> colSpace = K.domain();
   
  LinearOperator<double> Q1 = makeBlockOperator(colSpace, rowSpace);
//  Q1.setName("Q1");
  LinearOperator<double> Q2 = makeBlockOperator(colSpace, rowSpace);
  // Q2.setName("Q2");
  LinearOperator<double> Q3 = makeBlockOperator(colSpace, rowSpace);
  //Q3.setName("Q3");
   
  Q1.setBlock(0, 0, FInv);
  Q1.setBlock(1, 1, Ip);
  Q1.endBlockFill();
   
  Q2.setBlock(0, 0, Iu);
  Q2.setBlock(0, 1, -1.0*Bt);
  Q2.setBlock(1, 1, Ip);
  Q2.endBlockFill();
   
  Q3.setBlock(0, 0, Iu);
  Q3.setBlock(1, 1, -1.0*XInv);
  Q3.endBlockFill();
   
  LinearOperator<double> P1 = Q2 * Q3;
  LinearOperator<double> PInv = Q1 * P1;

  return new GenericRightPreconditioner<double>(PInv);
}