TEUCHOS_UNIT_TEST( EpetraLinearOp, rectangular )
{
  using Teuchos::null;
  using Teuchos::inOutArg;
  using Teuchos::updateSuccess;

  const RCP<const Epetra_Comm> comm = getEpetraComm();
  const int numProcs = comm->NumProc();

  const int numLocalRows = g_localDim;
  const int numRows = numLocalRows * comm->NumProc();
  const int numCols = numLocalRows / 2;

  const RCP<Epetra_CrsMatrix> epetraCrsM = getEpetraMatrix(numRows, numCols);

  const RCP<const LinearOpBase<double> > epetraOp = epetraLinearOp(epetraCrsM);

  LinearOpTester<double> linearOpTester;
  linearOpTester.check_adjoint(numProcs == 1);
  linearOpTester.show_all_tests(g_show_all_tests);
  linearOpTester.dump_all(g_dumpAll);
  updateSuccess(linearOpTester.check(*epetraOp, inOutArg(out)), success);

  // NOTE: Above, it would seem the Epetra_CrsMatrix::Apply(...) does not work
  // when doing and adjoint where the RowMap has empty processes.

}
TEUCHOS_UNIT_TEST( EpetraLinearOp, Blocked_ScalingWithMultiVectors)
{
  using Teuchos::null;
  using Teuchos::inOutArg;
  using Teuchos::updateSuccess;
  using Teuchos::rcp_dynamic_cast;
  typedef ScalarTraits<double> ST;

  // Set up the EpetraLinearOp

  const RCP<const Epetra_Comm> comm = getEpetraComm();
  const RCP<const Teuchos::Comm<Ordinal> > tComm =
      Teuchos::DefaultComm<Ordinal>::getComm();
  const int numLocalRows = 4;
  const int numRows = numLocalRows * comm->NumProc();
  const int numCols = numLocalRows / 2;

  out << "numRows = " << numRows << ", numCols = " << numCols << std::endl;

  const RCP<Epetra_CrsMatrix> epetraCrsM00 = getMyEpetraMatrix(numRows, numRows);
  const RCP<Epetra_CrsMatrix> epetraCrsM00_base = getMyEpetraMatrix(numRows, numRows);
  epetraCrsM00->PutScalar(2.0);
  epetraCrsM00_base->PutScalar(2.0);

  const RCP<LinearOpBase<double> > op00 = nonconstEpetraLinearOp(epetraCrsM00);
  const RCP<const LinearOpBase<double> > op00_base = epetraLinearOp(epetraCrsM00_base);

  RCP<const Thyra::VectorSpaceBase<double> > vs_0 = op00->range();
  RCP<const Thyra::VectorSpaceBase<double> > vs_1 = Thyra::locallyReplicatedDefaultSpmdVectorSpace<double>(tComm,numCols);

  RCP<Thyra::MultiVectorBase<double> > vec_01  = Thyra::createMembers(vs_0,numCols);
  RCP<Thyra::MultiVectorBase<double> > vec_10t = Thyra::createMembers(op00->domain(),numCols); // tranposed
  RCP<Thyra::MultiVectorBase<double> > vec_01_base  = Thyra::createMembers(vs_0,numCols);
  RCP<Thyra::MultiVectorBase<double> > vec_10t_base = Thyra::createMembers(op00->domain(),numCols); // tranposed
  const RCP<LinearOpBase<double> > op10t = vec_10t;
  const RCP<const LinearOpBase<double> > op10t_base = vec_10t_base;
  assign(vec_01.ptr(),-8.0);
  assign(vec_10t.ptr(),-9.0);
  assign(vec_01_base.ptr(),-8.0);
  assign(vec_10t_base.ptr(),-9.0);

  const RCP<LinearOpBase<double> > op01 = vec_01;
  const RCP<LinearOpBase<double> > op10 = nonconstAdjoint(op10t);
  const RCP<LinearOpBase<double> > op11 = nonconstZero(vec_01->domain(),vec_01->domain());

  const RCP<const LinearOpBase<double> > op01_base = vec_01_base;
  const RCP<const LinearOpBase<double> > op10_base = adjoint(op10t_base);
  const RCP<const LinearOpBase<double> > op11_base = zero(vec_01->domain(),vec_01->domain());

  out << "FIRST" << std::endl;
  const RCP<LinearOpBase<double> > blocked = nonconstBlock2x2(op00,op01,op10,op11);
  out << "SECOND" << Teuchos::describe(*blocked,Teuchos::VERB_EXTREME) << std::endl;
  const RCP<const LinearOpBase<double> > blocked_base = block2x2(op00_base,op01_base,op10_base,op11_base);

  const RCP<const RowStatLinearOpBase<double> > rowStatOp =
    rcp_dynamic_cast<const RowStatLinearOpBase<double> >(blocked, true);

  // Get the inverse row sums

  const RCP<VectorBase<double> > inv_row_sums =
    createMember<double>(blocked->range());
  const RCP<VectorBase<double> > row_sums =
    createMember<double>(blocked->range());

  rowStatOp->getRowStat(RowStatLinearOpBaseUtils::ROW_STAT_INV_ROW_SUM,
    inv_row_sums.ptr());
  rowStatOp->getRowStat(RowStatLinearOpBaseUtils::ROW_STAT_ROW_SUM,
    row_sums.ptr());

  TEST_FLOATING_EQUALITY(
    sum<double>(*inv_row_sums),
    as<double>((1.0/(numRows*2.0+numCols*8.0))*numRows + (1.0/(numRows*9.0+numCols*0.0))*numCols),
    as<double>(10.0 * ST::eps())
    );
  TEST_FLOATING_EQUALITY(
    sum<double>(*row_sums),
    as<double>((numRows*2.0+numCols*8.0)*numRows + (numRows*9.0+numCols*0.0)*numCols),
    as<double>(10.0 * ST::eps())
    );

  {
    const RCP<VectorBase<double> > left_scale  = createMember<double>(blocked_base->range());
    const RCP<VectorBase<double> > right_scale = createMember<double>(blocked_base->domain());

    put_scalar(7.0,left_scale.ptr());
    put_scalar(-4.0,right_scale.ptr());

    rcp_dynamic_cast<ScaledLinearOpBase<double> >(blocked)->scaleLeft(*left_scale);

    {
      LinearOpTester<double> tester;
      tester.set_all_error_tol(1e-10);
      tester.show_all_tests(true);
      tester.dump_all(false);
      tester.num_random_vectors(2);
      const RCP<const LinearOpBase<double> > left_op = Thyra::diagonal(left_scale);
      const RCP<const LinearOpBase<double> > ref_op = multiply(left_op,blocked_base);

      updateSuccess(tester.compare(*ref_op, *blocked, ptrFromRef(out)), success);
    }

    rcp_dynamic_cast<ScaledLinearOpBase<double> >(blocked)->scaleRight(*right_scale);

    {
      LinearOpTester<double> tester;
      tester.set_all_error_tol(1e-10);
      tester.show_all_tests(true);
      tester.dump_all(false);
      tester.num_random_vectors(5);
      const RCP<const LinearOpBase<double> > left_op = Thyra::diagonal(left_scale);
      const RCP<const LinearOpBase<double> > right_op = Thyra::diagonal(right_scale);
      const RCP<const LinearOpBase<double> > ref_op = multiply(left_op,blocked_base,right_op);

      updateSuccess(tester.compare(*ref_op, *blocked, ptrFromRef(out)), success);
    }
  }
}
TEUCHOS_UNIT_TEST( EpetraLinearOp, Blocked_ScaledLinearOpBase)
{
  using Teuchos::null;
  using Teuchos::inOutArg;
  using Teuchos::updateSuccess;
  using Teuchos::rcp_dynamic_cast;
  // typedef ScalarTraits<double> ST; // unused

  // Set up the EpetraLinearOp

  const RCP<const Epetra_Comm> comm = getEpetraComm();
  const int numLocalRows = g_localDim;
  const int numRows = numLocalRows * comm->NumProc();
  const int numCols = numLocalRows ;

  out << "numRows = " << numRows << ", numCols = " << numCols << std::endl;

  const RCP<Epetra_CrsMatrix> epetraCrsM00_base = getMyEpetraMatrix(numRows, numRows);
  const RCP<Epetra_CrsMatrix> epetraCrsM01_base = getMyEpetraMatrix(numRows, numCols);
  const RCP<Epetra_CrsMatrix> epetraCrsM10_base = getMyEpetraMatrix(numCols, numRows);
  const RCP<Epetra_CrsMatrix> epetraCrsM11_base = getMyEpetraMatrix(numCols, numCols);
  epetraCrsM00_base->PutScalar(2.0);
  epetraCrsM01_base->PutScalar(-8.0);
  epetraCrsM10_base->PutScalar(-9.0);
  epetraCrsM11_base->PutScalar(3.0);
  const RCP<Epetra_CrsMatrix> epetraCrsM00 = getMyEpetraMatrix(numRows, numRows);
  const RCP<Epetra_CrsMatrix> epetraCrsM01 = getMyEpetraMatrix(numRows, numCols);
  const RCP<Epetra_CrsMatrix> epetraCrsM10 = getMyEpetraMatrix(numCols, numRows);
  const RCP<Epetra_CrsMatrix> epetraCrsM11 = getMyEpetraMatrix(numCols, numCols);
  epetraCrsM00->PutScalar(2.0);
  epetraCrsM01->PutScalar(-8.0);
  epetraCrsM10->PutScalar(-9.0);
  epetraCrsM11->PutScalar(3.0);

  const RCP<const LinearOpBase<double> > op00_base = epetraLinearOp(epetraCrsM00_base);
  const RCP<const LinearOpBase<double> > op01_base = epetraLinearOp(epetraCrsM01_base);
  const RCP<const LinearOpBase<double> > op10_base = epetraLinearOp(epetraCrsM10_base);
  const RCP<const LinearOpBase<double> > op11_base = epetraLinearOp(epetraCrsM11_base);

  const RCP<LinearOpBase<double> > op00 = nonconstEpetraLinearOp(epetraCrsM00);
  const RCP<LinearOpBase<double> > op01 = nonconstEpetraLinearOp(epetraCrsM01);
  const RCP<LinearOpBase<double> > op10 = nonconstEpetraLinearOp(epetraCrsM10);
  const RCP<LinearOpBase<double> > op11 = nonconstEpetraLinearOp(epetraCrsM11);

  const RCP<const LinearOpBase<double> > blocked_base = block2x2(op00_base,op01_base,op10_base,op11_base);
  const RCP<LinearOpBase<double> > blocked = nonconstBlock2x2(op00,op01,op10,op11);

  const RCP<VectorBase<double> > left_scale  = createMember<double>(blocked_base->range());
  const RCP<VectorBase<double> > right_scale = createMember<double>(blocked_base->domain());

  put_scalar(7.0,left_scale.ptr());
  put_scalar(-4.0,right_scale.ptr());

  rcp_dynamic_cast<ScaledLinearOpBase<double> >(blocked)->scaleLeft(*left_scale);

  {
    LinearOpTester<double> tester;
    tester.set_all_error_tol(1e-10);
    tester.show_all_tests(true);
    tester.dump_all(true);
    tester.num_random_vectors(5);
    const RCP<const LinearOpBase<double> > left_op = Thyra::diagonal(left_scale);
    const RCP<const LinearOpBase<double> > ref_op = multiply(left_op,blocked_base);

    updateSuccess(tester.compare(*ref_op, *blocked, ptrFromRef(out)), success);
  }

  rcp_dynamic_cast<ScaledLinearOpBase<double> >(blocked)->scaleRight(*right_scale);

  {
    LinearOpTester<double> tester;
    tester.set_all_error_tol(1e-10);
    tester.show_all_tests(true);
    tester.dump_all(true);
    tester.num_random_vectors(5);
    const RCP<const LinearOpBase<double> > left_op = Thyra::diagonal(left_scale);
    const RCP<const LinearOpBase<double> > right_op = Thyra::diagonal(right_scale);
    const RCP<const LinearOpBase<double> > ref_op = multiply(left_op,blocked_base,right_op);

    updateSuccess(tester.compare(*ref_op, *blocked, ptrFromRef(out)), success);
  }
}