// @HEADER
//
// ***********************************************************************
//
//        MueLu: A package for multigrid based preconditioning
//                  Copyright 2012 Sandia Corporation
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the Corporation nor the names of the
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Questions? Contact
//                    Jonathan Hu       (jhu@sandia.gov)
//                    Andrey Prokopenko (aprokop@sandia.gov)
//                    Ray Tuminaro      (rstumin@sandia.gov)
//
// ***********************************************************************
//
// @HEADER
#include "Teuchos_UnitTestHarness.hpp"
#include "MueLu_TestHelpers.hpp"
#include "MueLu_TestHelpersSmoothers.hpp"

#include "MueLu_IfpackSmoother.hpp"

/*
   Comments about tests with hard coded results:
   1) Chebyshev smoothing must pass for any number of processors.
   2) Gauss-Seidel must pass for 1 and 4 processors.
   3) For any processor count except 1 and 4, the Gauss-Seidel test will
   report "passing", but this is only because the Teuchos test macro is skipped.
   */

namespace MueLuTests {

typedef double Scalar;
typedef int LocalOrdinal;
typedef int GlobalOrdinal;
typedef Xpetra::EpetraNode Node;
#include "MueLu_UseShortNames.hpp"

// this namespace already has:  #include "MueLu_UseShortNames.hpp"
using namespace TestHelpers::Smoothers;

TEUCHOS_UNIT_TEST(IfpackSmoother, NotSetup) {
  MUELU_TEST_ONLY_FOR(Xpetra::UseEpetra) {
    IfpackSmoother smoother("point relaxation stand-alone", Teuchos::ParameterList());
    testApplyNoSetup(smoother, out, success);
  }
}

// Tests interface to Ifpack's Gauss-Seidel preconditioner.
TEUCHOS_UNIT_TEST(IfpackSmoother, HardCodedResult_GaussSeidel1) {
  MUELU_TEST_ONLY_FOR(Xpetra::UseEpetra) {
    RCP<const Teuchos::Comm<int> > comm = TestHelpers::Parameters::getDefaultComm();

    Teuchos::ParameterList paramList;
    paramList.set("relaxation: type", "Gauss-Seidel");
    paramList.set("relaxation: sweeps", (int)1);
    paramList.set("relaxation: damping factor", (double)1.0);
    paramList.set("relaxation: zero starting solution", false);

    IfpackSmoother smoother("point relaxation stand-alone", paramList);

    Teuchos::ScalarTraits<SC>::magnitudeType residualNorms = testApply_A125_X1_RHS0(smoother, out, success);

    switch (comm->getSize()) {
      case 1:
      case 4:
        TEST_FLOATING_EQUALITY(residualNorms, 5.773502691896257e-01, 1e-12);
        break;
      default:
        out << "Pass/Fail is checked only for 1 and 4 processes." << std::endl;
        break;
    }  // switch
  }
}

// Tests interface to Ifpack's Gauss-Seidel preconditioner.
TEUCHOS_UNIT_TEST(IfpackSmoother, HardCodedResult_GaussSeidel2) {
  MUELU_TEST_ONLY_FOR(Xpetra::UseEpetra) {
    RCP<const Teuchos::Comm<int> > comm = TestHelpers::Parameters::getDefaultComm();

    Teuchos::ParameterList paramList;
    paramList.set("relaxation: type", "Gauss-Seidel");
    paramList.set("relaxation: sweeps", (int)10);
    paramList.set("relaxation: damping factor", (double)1.0);
    paramList.set("relaxation: zero starting solution", false);

    IfpackSmoother smoother("point relaxation stand-alone", paramList);

    Teuchos::ScalarTraits<SC>::magnitudeType residualNorms = testApply_A125_X1_RHS0(smoother, out, success);

    switch (comm->getSize()) {
      case 1:
        TEST_FLOATING_EQUALITY(residualNorms, 8.326553652741774e-02, 1e-12);
        break;
      case 4:
        TEST_FLOATING_EQUALITY(residualNorms, 8.326553653078517e-02, 1e-12);
        break;
      default:
        out << "Pass/Fail is checked only for 1 and 4 processes." << std::endl;
        break;

    }  // switch
  }
}  // GaussSeidel

// Tests interface to Ifpack's Chebyshev preconditioner.
TEUCHOS_UNIT_TEST(IfpackSmoother, HardCodedResult_Chebyshev) {
  MUELU_TEST_ONLY_FOR(Xpetra::UseEpetra) {
    // TODO This test should really calculate the reduction analytically.

    Teuchos::ParameterList paramList;
    paramList.set("chebyshev: degree", (int)3);
    paramList.set("chebyshev: max eigenvalue", (double)1.98476);
    paramList.set("chebyshev: min eigenvalue", (double)1.0);
    paramList.set("chebyshev: ratio eigenvalue", (double)20);
    paramList.set("chebyshev: zero starting solution", false);

    IfpackSmoother smoother("Chebyshev", paramList);

    Teuchos::ScalarTraits<SC>::magnitudeType residualNorms = testApply_A125_X1_RHS0(smoother, out, success);

    TEST_FLOATING_EQUALITY(residualNorms, 5.269156e-01, 1e-7);  // Compare to residual reported by ML
  }
}  // Chebyshev

// Tests interface to Ifpack's ILU(0) preconditioner.
TEUCHOS_UNIT_TEST(IfpackSmoother, HardCodedResult_ILU) {
  MUELU_TEST_ONLY_FOR(Xpetra::UseEpetra) {
    // FIXME this will probably fail in parallel b/c it becomes block Jacobi

    IfpackSmoother smoother("ILU", Teuchos::ParameterList());

    Teuchos::ScalarTraits<SC>::magnitudeType residualNorms = testApply_A125_X0_RandomRHS(smoother, out, success);

    RCP<const Teuchos::Comm<int> > comm = TestHelpers::Parameters::getDefaultComm();
    if (comm->getSize() == 1) {
      TEST_EQUALITY(residualNorms < 1e-10, true);
    } else {
      out << "Pass/Fail is only checked in serial." << std::endl;
    }
  }
}  // ILU

}  // namespace MueLuTests

// TODO: test if 10 its of ifpack == 10 apply call
// TODO:     TEST_EQUALITY(smoother->GetNIts(),10);
// TODO: check failed if TPETRA
