<commit_before>//---------------------------------------------------------------------------
//    $Id$
//    Version: $Name$
//    Author: Toby D. Young, Polish Academy of Sciences, 2008-2013
//
//    Copyright (C) 2009-2013 by the deal.II authors
//
//    This file is subject to QPL and may not be  distributed
//    without copyright and license information. Please refer
//    to the file deal.II/doc/license.html for the  text  and
//    further information on this license.
//
//---------------------------------------------------------------------------

#include <deal.II/lac/slepc_solver.h>

#ifdef DEAL_II_USE_SLEPC

#  include <deal.II/lac/petsc_matrix_base.h>
#  include <deal.II/lac/petsc_vector_base.h>
#  include <deal.II/lac/petsc_vector.h>
#  include <deal.II/lac/slepc_spectral_transformation.h>

#  include <cmath>
#  include <vector>

#  include <petscversion.h>
#  include <slepcversion.h>

DEAL_II_NAMESPACE_OPEN

namespace SLEPcWrappers
{

  SolverBase::SolverData::~SolverData ()
  {
    // Destroy the solver object.
#if DEAL_II_PETSC_VERSION_LT(3,2,0)
    int ierr = EPSDestroy (eps);
#else
    int ierr = EPSDestroy (&eps);
#endif
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  SolverBase::SolverBase (SolverControl &cn,
                          const MPI_Comm &mpi_communicator)
    :
    solver_control (cn),
    mpi_communicator (mpi_communicator),
    set_which (EPS_LARGEST_MAGNITUDE),
    opA (NULL), opB (NULL),
    initial_vector (NULL),
    transformation (NULL)
  {}

  SolverBase::~SolverBase ()
  {}

  void
  SolverBase::set_matrices (const PETScWrappers::MatrixBase &A)
  {
    // standard eigenspectrum problem
    opA = &A;
    opB = NULL;
  }

  void
  SolverBase::set_matrices (const PETScWrappers::MatrixBase &A,
                            const PETScWrappers::MatrixBase &B)
  {
    // generalized eigenspectrum problem
    opA = &A;
    opB = &B;
  }

  void
  SolverBase::set_initial_vector (const PETScWrappers::VectorBase &this_initial_vector)
  {
    initial_vector = (&this_initial_vector);
  }

  void
  SolverBase::set_transformation (SLEPcWrappers::TransformationBase &this_transformation)
  {
    transformation = &this_transformation;
  }

  void
  SolverBase::set_which_eigenpairs (const EPSWhich eps_which)
  {
    set_which = eps_which;
  }

  void
  SolverBase::solve (const unsigned int n_eigenvectors, unsigned int *n_converged)
  {
    int ierr;

    AssertThrow (solver_data.get() == 0, ExcSLEPcWrappersUsageError());
    solver_data.reset (new SolverData());

    // create eigensolver context and set operators
    ierr = EPSCreate (mpi_communicator, &solver_data->eps);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set eigenspectrum problem type (general/standard)
    AssertThrow (opA, ExcSLEPcWrappersUsageError());
    if (opB)
      ierr = EPSSetOperators (solver_data->eps, *opA, *opB);
    else
      ierr = EPSSetOperators (solver_data->eps, *opA, PETSC_NULL);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set the initial vector(s) if any
    if (initial_vector && initial_vector->size() != 0)
      {

#if DEAL_II_PETSC_VERSION_LT(3,1,0)
        ierr = EPSSetInitialVector (solver_data->eps, *initial_vector);
#else
        Vec this_vector = *initial_vector;
        ierr = EPSSetInitialSpace (solver_data->eps, 1, &this_vector);
#endif

        AssertThrow (ierr == 0, ExcSLEPcError(ierr));
      }

    // set transformation type if any
    if (transformation)
      transformation->set_context(solver_data->eps);

    // set runtime options
    set_solver_type (solver_data->eps);

    // set which portion of the eigenspectrum to solve for
    ierr = EPSSetWhichEigenpairs (solver_data->eps, set_which);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set number of eigenvectors to compute
    ierr = EPSSetDimensions (solver_data->eps, n_eigenvectors,
                             PETSC_DECIDE, PETSC_DECIDE);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set the solve options to the eigenvalue problem solver context
    ierr = EPSSetFromOptions (solver_data->eps);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // solve the eigensystem
    ierr = EPSSolve (solver_data->eps);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // get number of converged eigenstates
    ierr = EPSGetConverged (solver_data->eps,
#ifdef PETSC_USE_64BIT_INDICES
                            reinterpret_cast<PetscInt *>(n_converged));
#else
                            reinterpret_cast<int *>(n_converged));
#endif
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  void
  SolverBase::get_eigenpair (const unsigned int         index,
#ifndef PETSC_USE_COMPLEX
                             double                    &kr,
#else
                             std::complex<double>      &kr,
#endif
                             PETScWrappers::VectorBase &vr)
  {
    AssertThrow (solver_data.get() != 0, ExcSLEPcWrappersUsageError());

    // get converged eigenpair
    int ierr = EPSGetEigenpair (solver_data->eps, index,
                                &kr, PETSC_NULL, vr, PETSC_NULL);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  void
  SolverBase::reset ()
  {
    AssertThrow (solver_data.get() != 0, ExcSLEPcWrappersUsageError());

    // destroy solver object.
    solver_data.reset ();
  }

  EPS *
  SolverBase::get_EPS ()
  {
    if (solver_data.get() == 0)
      return NULL;

    return &solver_data->eps;
  }

  /* ---------------------- SolverControls ----------------------- */
  SolverControl &
  SolverBase::control () const
  {
    return solver_control;
  }

  int
  SolverBase::convergence_test (EPS                 /*eps*/,
#ifdef PETSC_USE_64BIT_INDICES
                                const PetscInt      iteration,
#else
                                const int           iteration,
#endif
                                const PetscReal     residual_norm,
                                EPSConvergedReason *reason,
                                void               *solver_control_x)
  {
    SolverControl &solver_control
      = *reinterpret_cast<SolverControl *>(solver_control_x);

    const SolverControl::State state
      = solver_control.check (iteration, residual_norm);

    switch (state)
      {
      case ::dealii::SolverControl::iterate:
        *reason = EPS_CONVERGED_ITERATING;
        break;

      case ::dealii::SolverControl::success:
        *reason = static_cast<EPSConvergedReason>(1);
        break;

      case ::dealii::SolverControl::failure:
        if (solver_control.last_step() > solver_control.max_steps())
          *reason = EPS_DIVERGED_ITS;
        else
          *reason = EPS_DIVERGED_BREAKDOWN;
        break;

      default:
        Assert (false, ExcNotImplemented());
      }

    // return without failure.
    return 0;
  }

  /* ---------------------- SolverKrylovSchur ------------------------ */
  SolverKrylovSchur::SolverKrylovSchur (SolverControl        &cn,
                                        const MPI_Comm       &mpi_communicator,
                                        const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverKrylovSchur::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSKRYLOVSCHUR));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances(eps, this->solver_control.tolerance(),
                            this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  /* ---------------------- SolverArnoldi ------------------------ */
  SolverArnoldi::AdditionalData::
  AdditionalData (const bool delayed_reorthogonalization)
    :
    delayed_reorthogonalization (delayed_reorthogonalization)
  {}

  SolverArnoldi::SolverArnoldi (SolverControl        &cn,
                                const MPI_Comm       &mpi_communicator,
                                const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverArnoldi::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSARNOLDI));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances(eps, this->solver_control.tolerance(),
                            this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // if requested, set delayed reorthogonalization in the Arnoldi
    // iteration.
    if (additional_data.delayed_reorthogonalization)
      {
        ierr = EPSArnoldiSetDelayed (eps, PETSC_TRUE);
        AssertThrow (ierr == 0, ExcSLEPcError(ierr));
      }
  }

  /* ---------------------- Lanczos ------------------------ */
  SolverLanczos::SolverLanczos (SolverControl        &cn,
                                const MPI_Comm       &mpi_communicator,
                                const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverLanczos::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSLANCZOS));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  /* ----------------------- Power ------------------------- */
  SolverPower::SolverPower (SolverControl        &cn,
                            const MPI_Comm       &mpi_communicator,
                            const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverPower::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSPOWER));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  /* ---------------- Generalized Davidson ----------------- */
  SolverGeneralizedDavidson::SolverGeneralizedDavidson (SolverControl        &cn,
							const MPI_Comm       &mpi_communicator,
							const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverGeneralizedDavidson::set_solver_type (EPS &eps) const
  {
#if DEAL_II_PETSC_VERSION_GTE(3,1,0)
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSGD));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
#else
    // PETSc/SLEPc version must be > 3.1.0 (supress compiler warning
    // by performing a void operation on eps).
    Assert (false,
            ExcMessage ("Your SLEPc installation does not include a copy of the "
                        "Generalized Davidson solver. A SLEPc version > 3.1.0 is required."));
    Assert (eps, ExcSLEPcWrappersUsageError());
#endif
  }

  /* ------------------ Jacobi Davidson -------------------- */
  SolverJacobiDavidson::SolverJacobiDavidson (SolverControl        &cn,
					      const MPI_Comm       &mpi_communicator,
					      const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverJacobiDavidson::set_solver_type (EPS &eps) const
  {
#if DEAL_II_PETSC_VERSION_GTE(3,1,0)
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSJD));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
#else
    // PETSc/SLEPc version must be > 3.1.0 (supress compiler warning
    // by performing a void operation on eps).
    Assert ((false),
            ExcMessage ("Your SLEPc installation does not include a copy of the "
                        "Jacobi-Davidson solver. A SLEPc version > 3.1.0 is required."));
    Assert (eps, ExcSLEPcWrappersUsageError());
#endif
  }

}

DEAL_II_NAMESPACE_CLOSE

#else

// On gcc2.95 on Alpha OSF1, the native assembler does not like empty
// files, so provide some dummy code
namespace
{
  void dummy () {}
}
#endif // DEAL_II_USE_SLEPC

<commit_msg>Use void() not Assert(NULL) to supress compiler warning.<commit_after>//---------------------------------------------------------------------------
//    $Id$
//    Version: $Name$
//    Author: Toby D. Young, Polish Academy of Sciences, 2008-2013
//
//    Copyright (C) 2009-2013 by the deal.II authors
//
//    This file is subject to QPL and may not be  distributed
//    without copyright and license information. Please refer
//    to the file deal.II/doc/license.html for the  text  and
//    further information on this license.
//
//---------------------------------------------------------------------------

#include <deal.II/lac/slepc_solver.h>

#ifdef DEAL_II_USE_SLEPC

#  include <deal.II/lac/petsc_matrix_base.h>
#  include <deal.II/lac/petsc_vector_base.h>
#  include <deal.II/lac/petsc_vector.h>
#  include <deal.II/lac/slepc_spectral_transformation.h>

#  include <cmath>
#  include <vector>

#  include <petscversion.h>
#  include <slepcversion.h>

DEAL_II_NAMESPACE_OPEN

namespace SLEPcWrappers
{

  SolverBase::SolverData::~SolverData ()
  {
    // Destroy the solver object.
#if DEAL_II_PETSC_VERSION_LT(3,2,0)
    int ierr = EPSDestroy (eps);
#else
    int ierr = EPSDestroy (&eps);
#endif
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  SolverBase::SolverBase (SolverControl &cn,
                          const MPI_Comm &mpi_communicator)
    :
    solver_control (cn),
    mpi_communicator (mpi_communicator),
    set_which (EPS_LARGEST_MAGNITUDE),
    opA (NULL), opB (NULL),
    initial_vector (NULL),
    transformation (NULL)
  {}

  SolverBase::~SolverBase ()
  {}

  void
  SolverBase::set_matrices (const PETScWrappers::MatrixBase &A)
  {
    // standard eigenspectrum problem
    opA = &A;
    opB = NULL;
  }

  void
  SolverBase::set_matrices (const PETScWrappers::MatrixBase &A,
                            const PETScWrappers::MatrixBase &B)
  {
    // generalized eigenspectrum problem
    opA = &A;
    opB = &B;
  }

  void
  SolverBase::set_initial_vector (const PETScWrappers::VectorBase &this_initial_vector)
  {
    initial_vector = (&this_initial_vector);
  }

  void
  SolverBase::set_transformation (SLEPcWrappers::TransformationBase &this_transformation)
  {
    transformation = &this_transformation;
  }

  void
  SolverBase::set_which_eigenpairs (const EPSWhich eps_which)
  {
    set_which = eps_which;
  }

  void
  SolverBase::solve (const unsigned int n_eigenvectors, unsigned int *n_converged)
  {
    int ierr;

    AssertThrow (solver_data.get() == 0, ExcSLEPcWrappersUsageError());
    solver_data.reset (new SolverData());

    // create eigensolver context and set operators
    ierr = EPSCreate (mpi_communicator, &solver_data->eps);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set eigenspectrum problem type (general/standard)
    AssertThrow (opA, ExcSLEPcWrappersUsageError());
    if (opB)
      ierr = EPSSetOperators (solver_data->eps, *opA, *opB);
    else
      ierr = EPSSetOperators (solver_data->eps, *opA, PETSC_NULL);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set the initial vector(s) if any
    if (initial_vector && initial_vector->size() != 0)
      {

#if DEAL_II_PETSC_VERSION_LT(3,1,0)
        ierr = EPSSetInitialVector (solver_data->eps, *initial_vector);
#else
        Vec this_vector = *initial_vector;
        ierr = EPSSetInitialSpace (solver_data->eps, 1, &this_vector);
#endif

        AssertThrow (ierr == 0, ExcSLEPcError(ierr));
      }

    // set transformation type if any
    if (transformation)
      transformation->set_context(solver_data->eps);

    // set runtime options
    set_solver_type (solver_data->eps);

    // set which portion of the eigenspectrum to solve for
    ierr = EPSSetWhichEigenpairs (solver_data->eps, set_which);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set number of eigenvectors to compute
    ierr = EPSSetDimensions (solver_data->eps, n_eigenvectors,
                             PETSC_DECIDE, PETSC_DECIDE);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // set the solve options to the eigenvalue problem solver context
    ierr = EPSSetFromOptions (solver_data->eps);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // solve the eigensystem
    ierr = EPSSolve (solver_data->eps);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // get number of converged eigenstates
    ierr = EPSGetConverged (solver_data->eps,
#ifdef PETSC_USE_64BIT_INDICES
                            reinterpret_cast<PetscInt *>(n_converged));
#else
                            reinterpret_cast<int *>(n_converged));
#endif
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  void
  SolverBase::get_eigenpair (const unsigned int         index,
#ifndef PETSC_USE_COMPLEX
                             double                    &kr,
#else
                             std::complex<double>      &kr,
#endif
                             PETScWrappers::VectorBase &vr)
  {
    AssertThrow (solver_data.get() != 0, ExcSLEPcWrappersUsageError());

    // get converged eigenpair
    int ierr = EPSGetEigenpair (solver_data->eps, index,
                                &kr, PETSC_NULL, vr, PETSC_NULL);
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  void
  SolverBase::reset ()
  {
    AssertThrow (solver_data.get() != 0, ExcSLEPcWrappersUsageError());

    // destroy solver object.
    solver_data.reset ();
  }

  EPS *
  SolverBase::get_EPS ()
  {
    if (solver_data.get() == 0)
      return NULL;

    return &solver_data->eps;
  }

  /* ---------------------- SolverControls ----------------------- */
  SolverControl &
  SolverBase::control () const
  {
    return solver_control;
  }

  int
  SolverBase::convergence_test (EPS                 /*eps*/,
#ifdef PETSC_USE_64BIT_INDICES
                                const PetscInt      iteration,
#else
                                const int           iteration,
#endif
                                const PetscReal     residual_norm,
                                EPSConvergedReason *reason,
                                void               *solver_control_x)
  {
    SolverControl &solver_control
      = *reinterpret_cast<SolverControl *>(solver_control_x);

    const SolverControl::State state
      = solver_control.check (iteration, residual_norm);

    switch (state)
      {
      case ::dealii::SolverControl::iterate:
        *reason = EPS_CONVERGED_ITERATING;
        break;

      case ::dealii::SolverControl::success:
        *reason = static_cast<EPSConvergedReason>(1);
        break;

      case ::dealii::SolverControl::failure:
        if (solver_control.last_step() > solver_control.max_steps())
          *reason = EPS_DIVERGED_ITS;
        else
          *reason = EPS_DIVERGED_BREAKDOWN;
        break;

      default:
        Assert (false, ExcNotImplemented());
      }

    // return without failure.
    return 0;
  }

  /* ---------------------- SolverKrylovSchur ------------------------ */
  SolverKrylovSchur::SolverKrylovSchur (SolverControl        &cn,
                                        const MPI_Comm       &mpi_communicator,
                                        const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverKrylovSchur::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSKRYLOVSCHUR));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances(eps, this->solver_control.tolerance(),
                            this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  /* ---------------------- SolverArnoldi ------------------------ */
  SolverArnoldi::AdditionalData::
  AdditionalData (const bool delayed_reorthogonalization)
    :
    delayed_reorthogonalization (delayed_reorthogonalization)
  {}

  SolverArnoldi::SolverArnoldi (SolverControl        &cn,
                                const MPI_Comm       &mpi_communicator,
                                const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverArnoldi::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSARNOLDI));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances(eps, this->solver_control.tolerance(),
                            this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // if requested, set delayed reorthogonalization in the Arnoldi
    // iteration.
    if (additional_data.delayed_reorthogonalization)
      {
        ierr = EPSArnoldiSetDelayed (eps, PETSC_TRUE);
        AssertThrow (ierr == 0, ExcSLEPcError(ierr));
      }
  }

  /* ---------------------- Lanczos ------------------------ */
  SolverLanczos::SolverLanczos (SolverControl        &cn,
                                const MPI_Comm       &mpi_communicator,
                                const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverLanczos::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSLANCZOS));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  /* ----------------------- Power ------------------------- */
  SolverPower::SolverPower (SolverControl        &cn,
                            const MPI_Comm       &mpi_communicator,
                            const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverPower::set_solver_type (EPS &eps) const
  {
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSPOWER));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
  }

  /* ---------------- Generalized Davidson ----------------- */
  SolverGeneralizedDavidson::SolverGeneralizedDavidson (SolverControl        &cn,
							const MPI_Comm       &mpi_communicator,
							const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverGeneralizedDavidson::set_solver_type (EPS &eps) const
  {
#if DEAL_II_PETSC_VERSION_GTE(3,1,0)
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSGD));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
#else
    // Supress compiler warnings about unused paameters.
    (void) eps;

    // PETSc/SLEPc version must be > 3.1.0.
    Assert (false,
            ExcMessage ("Your SLEPc installation does not include a copy of the "
                        "Generalized Davidson solver. A SLEPc version > 3.1.0 is required."));
#endif
  }

  /* ------------------ Jacobi Davidson -------------------- */
  SolverJacobiDavidson::SolverJacobiDavidson (SolverControl        &cn,
					      const MPI_Comm       &mpi_communicator,
					      const AdditionalData &data)
    :
    SolverBase (cn, mpi_communicator),
    additional_data (data)
  {}

  void
  SolverJacobiDavidson::set_solver_type (EPS &eps) const
  {
#if DEAL_II_PETSC_VERSION_GTE(3,1,0)
    int ierr;
    ierr = EPSSetType (eps, const_cast<char *>(EPSJD));
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));

    // hand over the absolute tolerance and the maximum number of
    // iteration steps to the SLEPc convergence criterion.
    ierr = EPSSetTolerances (eps, this->solver_control.tolerance(),
                             this->solver_control.max_steps());
    AssertThrow (ierr == 0, ExcSLEPcError(ierr));
#else
    // Supress compiler warnings about unused paameters.
    (void) eps;

    // PETSc/SLEPc version must be > 3.1.0.
    Assert ((false),
            ExcMessage ("Your SLEPc installation does not include a copy of the "
                        "Jacobi-Davidson solver. A SLEPc version > 3.1.0 is required."));
#endif
  }

}

DEAL_II_NAMESPACE_CLOSE

#else

// On gcc2.95 on Alpha OSF1, the native assembler does not like empty
// files, so provide some dummy code
namespace
{
  void dummy () {}
}
#endif // DEAL_II_USE_SLEPC

<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <vcl/IconThemeSelector.hxx>

#include <vcl/IconThemeScanner.hxx>
#include <vcl/IconThemeInfo.hxx>

#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/plugin/TestPlugIn.h>

class IconThemeSelectorTest : public CppUnit::TestFixture
{
    void
    OxygenThemeIsReturnedForKde4Desktop();

    void
    TangoThemeIsReturnedForGtkDesktop();

    void
    ThemeIsOverriddenByPreferredTheme();

    void
    ThemeIsOverriddenByHighContrastMode();

    void
    NotInstalledThemeDoesNotOverride();

    void
    InstalledThemeIsFound();

    void
    FirstThemeIsReturnedIfRequestedThemeIsNotFound();

    void
    FallbackThemeIsReturnedForEmptyInput();

    void
    DifferentPreferredThemesAreInequal();

    void
    DifferentHighContrastModesAreInequal();

    static std::vector<vcl::IconThemeInfo>
    GetFakeInstalledThemes();

    // Adds code needed to register the test suite
    CPPUNIT_TEST_SUITE(IconThemeSelectorTest);

    CPPUNIT_TEST(OxygenThemeIsReturnedForKde4Desktop);
    CPPUNIT_TEST(TangoThemeIsReturnedForGtkDesktop);
    CPPUNIT_TEST(ThemeIsOverriddenByPreferredTheme);
    CPPUNIT_TEST(ThemeIsOverriddenByHighContrastMode);
    CPPUNIT_TEST(NotInstalledThemeDoesNotOverride);
    CPPUNIT_TEST(InstalledThemeIsFound);
    CPPUNIT_TEST(FirstThemeIsReturnedIfRequestedThemeIsNotFound);
    CPPUNIT_TEST(FallbackThemeIsReturnedForEmptyInput);
    CPPUNIT_TEST(DifferentPreferredThemesAreInequal);
    CPPUNIT_TEST(DifferentHighContrastModesAreInequal);

    // End of test suite definition

    CPPUNIT_TEST_SUITE_END();
};

/*static*/ std::vector<vcl::IconThemeInfo>
IconThemeSelectorTest::GetFakeInstalledThemes()
{
    std::vector<vcl::IconThemeInfo> r;
    vcl::IconThemeInfo a{};
    a.mThemeId = "tango";
    r.push_back(a);
    a.mThemeId = "oxygen";
    r.push_back(a);
    a.mThemeId = vcl::IconThemeSelector::HIGH_CONTRAST_ICON_THEME_ID;
    r.push_back(a);
    return r;
}

void
IconThemeSelectorTest::OxygenThemeIsReturnedForKde4Desktop()
{
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    vcl::IconThemeSelector s;
    OUString r = s.SelectIconThemeForDesktopEnvironment(themes, "kde4");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is returned for kde4 desktop", OUString("oxygen"), r);
}

void
IconThemeSelectorTest::TangoThemeIsReturnedForGtkDesktop()
{
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    vcl::IconThemeSelector s;
    OUString r = s.SelectIconThemeForDesktopEnvironment(themes, "gtk");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is returned for kde4 desktop", OUString("tango"), r);
}

void
IconThemeSelectorTest::ThemeIsOverriddenByPreferredTheme()
{
    vcl::IconThemeSelector s;
    OUString preferred("oxygen");
    s.SetPreferredIconTheme(preferred);
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconThemeForDesktopEnvironment(themes, "gtk");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is overridden by oxygen", preferred, selected);
}

void
IconThemeSelectorTest::ThemeIsOverriddenByHighContrastMode()
{
    vcl::IconThemeSelector s;
    s.SetUseHighContrastTheme(true);
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "tango");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is overridden by high contrast mode",
            vcl::IconThemeSelector::HIGH_CONTRAST_ICON_THEME_ID, selected);
    s.SetUseHighContrastTheme(false);
    selected = s.SelectIconTheme(themes, "tango");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is no longer overridden by high contrast mode",
            OUString("tango"), selected);
}

void
IconThemeSelectorTest::NotInstalledThemeDoesNotOverride()
{
    vcl::IconThemeSelector s;
    s.SetPreferredIconTheme("oxygen_katze");
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "oxygen");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is not overridden by 'oxygen_katze'", OUString("oxygen"), selected);
}

void
IconThemeSelectorTest::InstalledThemeIsFound()
{
    vcl::IconThemeSelector s;
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "oxygen");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is found", OUString("oxygen"), selected);
}

void
IconThemeSelectorTest::FirstThemeIsReturnedIfRequestedThemeIsNotFound()
{
    vcl::IconThemeSelector s;
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "oxygen_katze");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is found", themes.front().GetThemeId(), selected);
}

void
IconThemeSelectorTest::FallbackThemeIsReturnedForEmptyInput()
{
    vcl::IconThemeSelector s;
    OUString selected = s.SelectIconTheme(std::vector<vcl::IconThemeInfo>{}, "oxygen");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("fallback is returned for empty input",
            vcl::IconThemeSelector::FALLBACK_ICON_THEME_ID, selected);
}

void
IconThemeSelectorTest::DifferentHighContrastModesAreInequal()
{
    vcl::IconThemeSelector s1;
    vcl::IconThemeSelector s2;
    s1.SetUseHighContrastTheme(true);
    s2.SetUseHighContrastTheme(false);
    bool equal = (s1 == s2);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Different high contrast modes are detected as inequal", false, equal);
}

void
IconThemeSelectorTest::DifferentPreferredThemesAreInequal()
{
    vcl::IconThemeSelector s1;
    vcl::IconThemeSelector s2;
    s1.SetPreferredIconTheme("oxygen");
    s2.SetUseHighContrastTheme("katze");
    bool equal = (s1 == s2);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Different preferred themes are detected as inequal", false, equal);
}

// Put the test suite in the registry
CPPUNIT_TEST_SUITE_REGISTRATION(IconThemeSelectorTest);

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>WaE: implicit conversion of literal of type 'const char *' to 'bool'<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <vcl/IconThemeSelector.hxx>

#include <vcl/IconThemeScanner.hxx>
#include <vcl/IconThemeInfo.hxx>

#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/plugin/TestPlugIn.h>

class IconThemeSelectorTest : public CppUnit::TestFixture
{
    void
    OxygenThemeIsReturnedForKde4Desktop();

    void
    TangoThemeIsReturnedForGtkDesktop();

    void
    ThemeIsOverriddenByPreferredTheme();

    void
    ThemeIsOverriddenByHighContrastMode();

    void
    NotInstalledThemeDoesNotOverride();

    void
    InstalledThemeIsFound();

    void
    FirstThemeIsReturnedIfRequestedThemeIsNotFound();

    void
    FallbackThemeIsReturnedForEmptyInput();

    void
    DifferentPreferredThemesAreInequal();

    void
    DifferentHighContrastModesAreInequal();

    static std::vector<vcl::IconThemeInfo>
    GetFakeInstalledThemes();

    // Adds code needed to register the test suite
    CPPUNIT_TEST_SUITE(IconThemeSelectorTest);

    CPPUNIT_TEST(OxygenThemeIsReturnedForKde4Desktop);
    CPPUNIT_TEST(TangoThemeIsReturnedForGtkDesktop);
    CPPUNIT_TEST(ThemeIsOverriddenByPreferredTheme);
    CPPUNIT_TEST(ThemeIsOverriddenByHighContrastMode);
    CPPUNIT_TEST(NotInstalledThemeDoesNotOverride);
    CPPUNIT_TEST(InstalledThemeIsFound);
    CPPUNIT_TEST(FirstThemeIsReturnedIfRequestedThemeIsNotFound);
    CPPUNIT_TEST(FallbackThemeIsReturnedForEmptyInput);
    CPPUNIT_TEST(DifferentPreferredThemesAreInequal);
    CPPUNIT_TEST(DifferentHighContrastModesAreInequal);

    // End of test suite definition

    CPPUNIT_TEST_SUITE_END();
};

/*static*/ std::vector<vcl::IconThemeInfo>
IconThemeSelectorTest::GetFakeInstalledThemes()
{
    std::vector<vcl::IconThemeInfo> r;
    vcl::IconThemeInfo a{};
    a.mThemeId = "tango";
    r.push_back(a);
    a.mThemeId = "oxygen";
    r.push_back(a);
    a.mThemeId = vcl::IconThemeSelector::HIGH_CONTRAST_ICON_THEME_ID;
    r.push_back(a);
    return r;
}

void
IconThemeSelectorTest::OxygenThemeIsReturnedForKde4Desktop()
{
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    vcl::IconThemeSelector s;
    OUString r = s.SelectIconThemeForDesktopEnvironment(themes, "kde4");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is returned for kde4 desktop", OUString("oxygen"), r);
}

void
IconThemeSelectorTest::TangoThemeIsReturnedForGtkDesktop()
{
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    vcl::IconThemeSelector s;
    OUString r = s.SelectIconThemeForDesktopEnvironment(themes, "gtk");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is returned for kde4 desktop", OUString("tango"), r);
}

void
IconThemeSelectorTest::ThemeIsOverriddenByPreferredTheme()
{
    vcl::IconThemeSelector s;
    OUString preferred("oxygen");
    s.SetPreferredIconTheme(preferred);
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconThemeForDesktopEnvironment(themes, "gtk");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is overridden by oxygen", preferred, selected);
}

void
IconThemeSelectorTest::ThemeIsOverriddenByHighContrastMode()
{
    vcl::IconThemeSelector s;
    s.SetUseHighContrastTheme(true);
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "tango");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is overridden by high contrast mode",
            vcl::IconThemeSelector::HIGH_CONTRAST_ICON_THEME_ID, selected);
    s.SetUseHighContrastTheme(false);
    selected = s.SelectIconTheme(themes, "tango");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'tango' theme is no longer overridden by high contrast mode",
            OUString("tango"), selected);
}

void
IconThemeSelectorTest::NotInstalledThemeDoesNotOverride()
{
    vcl::IconThemeSelector s;
    s.SetPreferredIconTheme("oxygen_katze");
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "oxygen");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is not overridden by 'oxygen_katze'", OUString("oxygen"), selected);
}

void
IconThemeSelectorTest::InstalledThemeIsFound()
{
    vcl::IconThemeSelector s;
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "oxygen");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is found", OUString("oxygen"), selected);
}

void
IconThemeSelectorTest::FirstThemeIsReturnedIfRequestedThemeIsNotFound()
{
    vcl::IconThemeSelector s;
    std::vector<vcl::IconThemeInfo> themes = GetFakeInstalledThemes();
    OUString selected = s.SelectIconTheme(themes, "oxygen_katze");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("'oxygen' theme is found", themes.front().GetThemeId(), selected);
}

void
IconThemeSelectorTest::FallbackThemeIsReturnedForEmptyInput()
{
    vcl::IconThemeSelector s;
    OUString selected = s.SelectIconTheme(std::vector<vcl::IconThemeInfo>{}, "oxygen");
    CPPUNIT_ASSERT_EQUAL_MESSAGE("fallback is returned for empty input",
            vcl::IconThemeSelector::FALLBACK_ICON_THEME_ID, selected);
}

void
IconThemeSelectorTest::DifferentHighContrastModesAreInequal()
{
    vcl::IconThemeSelector s1;
    vcl::IconThemeSelector s2;
    s1.SetUseHighContrastTheme(true);
    s2.SetUseHighContrastTheme(false);
    bool equal = (s1 == s2);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Different high contrast modes are detected as inequal", false, equal);
}

void
IconThemeSelectorTest::DifferentPreferredThemesAreInequal()
{
    vcl::IconThemeSelector s1;
    vcl::IconThemeSelector s2;
    s1.SetPreferredIconTheme("oxygen");
    s2.SetUseHighContrastTheme(true);
    bool equal = (s1 == s2);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Different preferred themes are detected as inequal", false, equal);
}

// Put the test suite in the registry
CPPUNIT_TEST_SUITE_REGISTRATION(IconThemeSelectorTest);

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*
 * Copyright © 2012 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Benjamin Segovia <benjamin.segovia@intel.com>
 */

/**
 * \file lowering.cpp
 * \author Benjamin Segovia <benjamin.segovia@intel.com>
 */

#include "ir/context.hpp"
#include "ir/value.hpp"
#include "ir/liveness.hpp"
#include "sys/set.hpp"

namespace gbe {
namespace ir {

  /*! Small helper class to lower return instructions */
  class ContextReturn : public Context
  {
  public:
    /*! Initialize a context dedicated to return instruction lowering */
    ContextReturn(Unit &unit) : Context(unit) {
      this->usedLabels = GBE_NEW_NO_ARG(vector<uint8_t>);
    }
    /*! Lower the return instruction to gotos for the given function */
    void lower(const std::string &functionName);
  };

  void ContextReturn::lower(const std::string &functionName) {
    if ((this->fn = unit.getFunction(functionName)) == NULL)
      return;

    // Append a new block at the end of the function with a return instruction:
    // the only one we are going to have
    this->bb = &this->fn->getBottomBlock();
    const LabelIndex index = this->label();
    this->LABEL(index);
    const BasicBlock *lastBlock = this->bb;
    this->RET();

    // Now traverse all instructions and replace all returns by GOTO index
    fn->foreachInstruction([&](Instruction &insn) {
      if (insn.getParent() == lastBlock) return; // This is the last block
      if (insn.getOpcode() != OP_RET) return;
      const Instruction bra = ir::BRA(index);
      bra.replace(&insn);
    });
  }

  void lowerReturn(Unit &unit, const std::string &functionName) {
    ContextReturn ctx(unit);
    ctx.lower(functionName);
  }

  /*! Characterizes how the argument is used (directly read, indirectly read,
   *  written)
   */
  enum ArgUse {
    ARG_DIRECT_READ = 0,
    ARG_INDIRECT_READ = 1,
    ARG_WRITTEN = 2
  };

  /*! Just to book keep the sequence of instructions that directly load an input
   *  argument
   */
  struct LoadAddImm {
    Instruction *load;    //!< Load from the argument
    Instruction *add;     //!< Can be NULL if we only have load(arg)
    Instruction *loadImm; //!< Can also be NULL
    uint64_t offset;      //!< Offset where to load in the structure
    uint32_t argID;       //!< Associated function argument
  };

  /*! List of direct loads */
  typedef vector<LoadAddImm> LoadAddImmSeq;

  /*! Helper class to lower function arguments if required */
  class FunctionArgumentLowerer : public Context
  {
  public:
    /*! Build the helper structure */
    FunctionArgumentLowerer(Unit &unit);
    /*! Free everything we needed */
    virtual ~FunctionArgumentLowerer(void);
    /*! Perform all function arguments substitution if needed */
    void lower(const std::string &name);
    /*! Lower the given function argument accesses */
    void lower(uint32_t argID);
    /*! Build the constant push for the function */
    void buildConstantPush(void);
    /*! Inspect the given function argument to see how it is used. If this is
     *  direct loads only, we also output the list of instructions used for each
     *  load
     */
    ArgUse getArgUse(uint32_t argID);
    /*! Recursively look if there is a store in the given use */
    bool useStore(const ValueDef &def, set<const Instruction*> &visited);
    /*! Look if the pointer use only load with immediate offsets */
    bool matchLoadAddImm(uint32_t argID);
    Liveness *liveness; //!< To compute the function graph
    FunctionDAG *dag;   //!< Contains complete dependency information
    LoadAddImmSeq seq;  //!< All the direct loads
  };

  INLINE uint64_t getOffsetFromImm(const Immediate &imm) {
    switch (imm.type) {
      // bit-cast these ones
      case TYPE_DOUBLE:
      case TYPE_FLOAT:
      case TYPE_S64:
      case TYPE_U64:
      case TYPE_U32:
      case TYPE_U16:
      case TYPE_U8: return imm.data.u64;
      // sign extend these ones
      case TYPE_S32: return int64_t(imm.data.s32);
      case TYPE_S16: return int64_t(imm.data.s16);
      case TYPE_S8: return int64_t(imm.data.s8);
      case TYPE_BOOL:
      case TYPE_HALF: NOT_SUPPORTED; return 0;
    }
    return 0;
  }

  bool matchLoad(Instruction *insn,
                 Instruction *add,
                 Instruction *loadImm,
                 uint64_t offset,
                 uint32_t argID,
                 LoadAddImm &loadAddImm)
  {
    const Opcode opcode = insn->getOpcode();

    if (opcode == OP_LOAD) {
      LoadInstruction *load = cast<LoadInstruction>(insn);
      if (load->getAddressSpace() != MEM_PRIVATE)
        return false;
      loadAddImm.load = insn;
      loadAddImm.add = add;
      loadAddImm.loadImm = loadImm;
      loadAddImm.offset = offset;
      loadAddImm.argID = argID;
      return true;
    } else
      return false;
  }

  FunctionArgumentLowerer::FunctionArgumentLowerer(Unit &unit) :
    Context(unit), liveness(NULL), dag(NULL) {}
  FunctionArgumentLowerer::~FunctionArgumentLowerer(void) {
    GBE_SAFE_DELETE(dag);
    GBE_SAFE_DELETE(liveness);
  }

  void FunctionArgumentLowerer::lower(const std::string &functionName) {
    if ((this->fn = unit.getFunction(functionName)) == NULL)
      return;
    GBE_SAFE_DELETE(dag);
    GBE_SAFE_DELETE(liveness);
    this->liveness = GBE_NEW(ir::Liveness, *fn);
    this->dag = GBE_NEW(ir::FunctionDAG, *this->liveness);

    // Process all structure arguments and find all the direct loads we can
    // replace
    const uint32_t argNum = fn->argNum();
    for (uint32_t argID = 0; argID < argNum; ++argID) {
      FunctionArgument &arg = fn->getArg(argID);
      if (arg.type != FunctionArgument::STRUCTURE) continue;
      this->lower(argID);
    }

    // Build the constant push description and remove the instruction that
    // therefore become useless
    this->buildConstantPush();
  }

// Remove all the given instructions from the stream (if dead)
#define REMOVE_INSN(WHICH) \
  for (const auto &loadAddImm : seq) { \
    Instruction *WHICH = loadAddImm.WHICH; \
    if (WHICH == NULL) continue; \
    const UseSet &useSet = dag->getUse(WHICH, 0); \
    bool isDead = true; \
    for (auto use : useSet) { \
      if (dead.contains(use->getInstruction()) == false) { \
        isDead = false; \
        break; \
      } \
    } \
    if (isDead) { \
      dead.insert(WHICH); \
      WHICH->remove(); \
    } \
  }

  void FunctionArgumentLowerer::buildConstantPush(void)
  {
    if (seq.size() == 0)
      return;

    // Track instructions we remove to recursively kill them properly
    set<const Instruction*> dead;

    // The argument location we already pushed (since the same argument location
    // can be used several times)
    set<PushLocation> inserted;
    for (const auto &loadAddImm : seq) {
      LoadInstruction *load = cast<LoadInstruction>(loadAddImm.load);
      const uint32_t valueNum = load->getValueNum();
      for (uint32_t valueID = 0; valueID < valueNum; ++valueID) {
        const Type type = load->getValueType();
        const RegisterFamily family = getFamily(type);
        const uint32_t size = getFamilySize(family);
        const uint32_t offset = loadAddImm.offset + valueID * size;
        const PushLocation argLocation(*fn, loadAddImm.argID, offset);
        if (inserted.contains(argLocation))
          continue;
        Register pushed;
        const Register reg = load->getValue(valueID);
        if (offset != 0) {
            pushed = fn->newRegister(family);
            this->appendPushedConstant(pushed, argLocation);
        } else {
            pushed = fn->getArg(loadAddImm.argID).reg;
        }

        // TODO the MOV instruction can be most of the time avoided if the
        // register is never written. We must however support the register
        // replacement in the instruction interface to be able to patch all the
        // instruction that uses "reg"
        const Instruction mov = ir::MOV(type, reg, pushed);
        mov.replace(load);
        dead.insert(load);
      }
    }

    // Remove all unused adds and load immediates
    REMOVE_INSN(add)
    REMOVE_INSN(loadImm)
  }

#undef REMOVE_INSN

  bool FunctionArgumentLowerer::useStore(const ValueDef &def, set<const Instruction*> &visited)
  {
    const UseSet &useSet = dag->getUse(def);
    for (const auto &use : useSet) {
      const Instruction *insn = use->getInstruction();
      const uint32_t srcID = use->getSrcID();
      const Opcode opcode = insn->getOpcode();
      if (visited.contains(insn)) continue;
      visited.insert(insn);
      if (opcode == OP_STORE && srcID == StoreInstruction::addressIndex)
        return true;
      if (insn->isMemberOf<UnaryInstruction>() == false &&
          insn->isMemberOf<BinaryInstruction>() == false)
        continue;
      else {
        const uint32_t dstNum = insn->getDstNum();
        for (uint32_t dstID = 0; dstID < dstNum; ++dstID)
          if (this->useStore(ValueDef(insn, dstID), visited) == true)
            return true;
      }
    }
    return false;
  }

  bool FunctionArgumentLowerer::matchLoadAddImm(uint32_t argID)
  {
    const FunctionArgument &arg = fn->getArg(argID);
    LoadAddImmSeq tmpSeq;

    // Inspect all uses of the function argument pointer
    const UseSet &useSet = dag->getUse(&arg);
    for (auto use : useSet) {
      Instruction *insn = const_cast<Instruction*>(use->getInstruction());
      const Opcode opcode = insn->getOpcode();

      // load dst arg
      LoadAddImm loadAddImm;
      if (matchLoad(insn, NULL, NULL, 0, argID, loadAddImm)) {
        tmpSeq.push_back(loadAddImm);
        continue;
      }

      // add.ptr_type dst ptr other
      if (opcode != OP_ADD) return false;
      BinaryInstruction *add = cast<BinaryInstruction>(insn);
      const Type addType = add->getType();
      const RegisterFamily family = getFamily(addType);
      if (family != unit.getPointerFamily()) return false;
      if (addType == TYPE_FLOAT) return false;

      // step 1 -> check that the other source comes from a load immediate
      const uint32_t srcID = use->getSrcID();
      const uint32_t otherID = srcID ^ 1;
      const DefSet &defSet = dag->getDef(insn, otherID);
      const uint32_t defNum = defSet.size();
      if (defNum == 0 || defNum > 1) continue; // undefined or more than one def
      const ValueDef *otherDef = *defSet.begin();
      if (otherDef->getType() != ValueDef::DEF_INSN_DST) return false;
      Instruction *otherInsn = const_cast<Instruction*>(otherDef->getInstruction());
      if (otherInsn->getOpcode() != OP_LOADI) return false;
      LoadImmInstruction *loadImm = cast<LoadImmInstruction>(otherInsn);
      const Immediate imm = loadImm->getImmediate();
      const uint64_t offset = getOffsetFromImm(imm);

      // step 2 -> check that the results of the add are loads from private
      // memory
      const UseSet &addUseSet = dag->getUse(add, 0);
      for (auto addUse : addUseSet) {
        Instruction *insn = const_cast<Instruction*>(addUse->getInstruction());

        // We finally find something like load dst arg+imm
        LoadAddImm loadAddImm;
        if (matchLoad(insn, add, loadImm, offset, argID, loadAddImm)) {
          tmpSeq.push_back(loadAddImm);
          continue;
        }
      }
    }

    // OK, the argument only need direct loads. We can now append all the
    // direct load definitions we found
    for (const auto &loadImmSeq : tmpSeq)
      seq.push_back(loadImmSeq);
    return true;
  }

  ArgUse FunctionArgumentLowerer::getArgUse(uint32_t argID)
  {
    FunctionArgument &arg = fn->getArg(argID);

    // case 1 - we may store something to the structure argument
    set<const Instruction*> visited;
    if (this->useStore(ValueDef(&arg), visited))
      return ARG_WRITTEN;

    // case 2 - we look for the patterns: LOAD(ptr) or LOAD(ptr+imm)
    if (this->matchLoadAddImm(argID))
      return ARG_DIRECT_READ;

    // case 3 - LOAD(ptr+runtime_value)
    return ARG_INDIRECT_READ;
  }

  void FunctionArgumentLowerer::lower(uint32_t argID) {
    IF_DEBUG(const ArgUse argUse = )this->getArgUse(argID);
#if GBE_DEBUG
    GBE_ASSERTM(argUse != ARG_WRITTEN,
                "TODO A store to a structure argument "
                "(i.e. not a char/short/int/float argument) has been found. "
                "This is not supported yet");
    GBE_ASSERTM(argUse != ARG_INDIRECT_READ,
                "TODO Only direct loads of structure arguments are "
                "supported now");
#endif /* GBE_DEBUG */
  }

  void lowerFunctionArguments(Unit &unit, const std::string &functionName) {
    FunctionArgumentLowerer lowerer(unit);
    lowerer.lower(functionName);
  }

} /* namespace ir */
} /* namespace gbe */

<commit_msg>Fix a build pushMap bug.<commit_after>/*
 * Copyright © 2012 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Benjamin Segovia <benjamin.segovia@intel.com>
 */

/**
 * \file lowering.cpp
 * \author Benjamin Segovia <benjamin.segovia@intel.com>
 */

#include "ir/context.hpp"
#include "ir/value.hpp"
#include "ir/liveness.hpp"
#include "sys/set.hpp"

namespace gbe {
namespace ir {

  /*! Small helper class to lower return instructions */
  class ContextReturn : public Context
  {
  public:
    /*! Initialize a context dedicated to return instruction lowering */
    ContextReturn(Unit &unit) : Context(unit) {
      this->usedLabels = GBE_NEW_NO_ARG(vector<uint8_t>);
    }
    /*! Lower the return instruction to gotos for the given function */
    void lower(const std::string &functionName);
  };

  void ContextReturn::lower(const std::string &functionName) {
    if ((this->fn = unit.getFunction(functionName)) == NULL)
      return;

    // Append a new block at the end of the function with a return instruction:
    // the only one we are going to have
    this->bb = &this->fn->getBottomBlock();
    const LabelIndex index = this->label();
    this->LABEL(index);
    const BasicBlock *lastBlock = this->bb;
    this->RET();

    // Now traverse all instructions and replace all returns by GOTO index
    fn->foreachInstruction([&](Instruction &insn) {
      if (insn.getParent() == lastBlock) return; // This is the last block
      if (insn.getOpcode() != OP_RET) return;
      const Instruction bra = ir::BRA(index);
      bra.replace(&insn);
    });
  }

  void lowerReturn(Unit &unit, const std::string &functionName) {
    ContextReturn ctx(unit);
    ctx.lower(functionName);
  }

  /*! Characterizes how the argument is used (directly read, indirectly read,
   *  written)
   */
  enum ArgUse {
    ARG_DIRECT_READ = 0,
    ARG_INDIRECT_READ = 1,
    ARG_WRITTEN = 2
  };

  /*! Just to book keep the sequence of instructions that directly load an input
   *  argument
   */
  struct LoadAddImm {
    Instruction *load;    //!< Load from the argument
    Instruction *add;     //!< Can be NULL if we only have load(arg)
    Instruction *loadImm; //!< Can also be NULL
    uint64_t offset;      //!< Offset where to load in the structure
    uint32_t argID;       //!< Associated function argument
  };

  /*! List of direct loads */
  typedef vector<LoadAddImm> LoadAddImmSeq;

  /*! Helper class to lower function arguments if required */
  class FunctionArgumentLowerer : public Context
  {
  public:
    /*! Build the helper structure */
    FunctionArgumentLowerer(Unit &unit);
    /*! Free everything we needed */
    virtual ~FunctionArgumentLowerer(void);
    /*! Perform all function arguments substitution if needed */
    void lower(const std::string &name);
    /*! Lower the given function argument accesses */
    void lower(uint32_t argID);
    /*! Build the constant push for the function */
    void buildConstantPush(void);
    /*! Inspect the given function argument to see how it is used. If this is
     *  direct loads only, we also output the list of instructions used for each
     *  load
     */
    ArgUse getArgUse(uint32_t argID);
    /*! Recursively look if there is a store in the given use */
    bool useStore(const ValueDef &def, set<const Instruction*> &visited);
    /*! Look if the pointer use only load with immediate offsets */
    bool matchLoadAddImm(uint32_t argID);
    Liveness *liveness; //!< To compute the function graph
    FunctionDAG *dag;   //!< Contains complete dependency information
    LoadAddImmSeq seq;  //!< All the direct loads
  };

  INLINE uint64_t getOffsetFromImm(const Immediate &imm) {
    switch (imm.type) {
      // bit-cast these ones
      case TYPE_DOUBLE:
      case TYPE_FLOAT:
      case TYPE_S64:
      case TYPE_U64:
      case TYPE_U32:
      case TYPE_U16:
      case TYPE_U8: return imm.data.u64;
      // sign extend these ones
      case TYPE_S32: return int64_t(imm.data.s32);
      case TYPE_S16: return int64_t(imm.data.s16);
      case TYPE_S8: return int64_t(imm.data.s8);
      case TYPE_BOOL:
      case TYPE_HALF: NOT_SUPPORTED; return 0;
    }
    return 0;
  }

  bool matchLoad(Instruction *insn,
                 Instruction *add,
                 Instruction *loadImm,
                 uint64_t offset,
                 uint32_t argID,
                 LoadAddImm &loadAddImm)
  {
    const Opcode opcode = insn->getOpcode();

    if (opcode == OP_LOAD) {
      LoadInstruction *load = cast<LoadInstruction>(insn);
      if (load->getAddressSpace() != MEM_PRIVATE)
        return false;
      loadAddImm.load = insn;
      loadAddImm.add = add;
      loadAddImm.loadImm = loadImm;
      loadAddImm.offset = offset;
      loadAddImm.argID = argID;
      return true;
    } else
      return false;
  }

  FunctionArgumentLowerer::FunctionArgumentLowerer(Unit &unit) :
    Context(unit), liveness(NULL), dag(NULL) {}
  FunctionArgumentLowerer::~FunctionArgumentLowerer(void) {
    GBE_SAFE_DELETE(dag);
    GBE_SAFE_DELETE(liveness);
  }

  void FunctionArgumentLowerer::lower(const std::string &functionName) {
    if ((this->fn = unit.getFunction(functionName)) == NULL)
      return;
    GBE_SAFE_DELETE(dag);
    GBE_SAFE_DELETE(liveness);
    this->liveness = GBE_NEW(ir::Liveness, *fn);
    this->dag = GBE_NEW(ir::FunctionDAG, *this->liveness);

    // Process all structure arguments and find all the direct loads we can
    // replace
    const uint32_t argNum = fn->argNum();
    for (uint32_t argID = 0; argID < argNum; ++argID) {
      FunctionArgument &arg = fn->getArg(argID);
      if (arg.type != FunctionArgument::STRUCTURE) continue;
      this->lower(argID);
    }

    // Build the constant push description and remove the instruction that
    // therefore become useless
    this->buildConstantPush();
  }

// Remove all the given instructions from the stream (if dead)
#define REMOVE_INSN(WHICH) \
  for (const auto &loadAddImm : seq) { \
    Instruction *WHICH = loadAddImm.WHICH; \
    if (WHICH == NULL) continue; \
    const UseSet &useSet = dag->getUse(WHICH, 0); \
    bool isDead = true; \
    for (auto use : useSet) { \
      if (dead.contains(use->getInstruction()) == false) { \
        isDead = false; \
        break; \
      } \
    } \
    if (isDead) { \
      dead.insert(WHICH); \
      WHICH->remove(); \
    } \
  }

  void FunctionArgumentLowerer::buildConstantPush(void)
  {
    if (seq.size() == 0)
      return;

    // Track instructions we remove to recursively kill them properly
    set<const Instruction*> dead;

    // The argument location we already pushed (since the same argument location
    // can be used several times)
    set<PushLocation> inserted;
    for (const auto &loadAddImm : seq) {
      LoadInstruction *load = cast<LoadInstruction>(loadAddImm.load);
      const uint32_t valueNum = load->getValueNum();
      for (uint32_t valueID = 0; valueID < valueNum; ++valueID) {
        const Type type = load->getValueType();
        const RegisterFamily family = getFamily(type);
        const uint32_t size = getFamilySize(family);
        const uint32_t offset = loadAddImm.offset + valueID * size;
        const PushLocation argLocation(*fn, loadAddImm.argID, offset);
        if (inserted.contains(argLocation))
          continue;
        Register pushed;
        const Register reg = load->getValue(valueID);
        if (offset != 0) {
            pushed = fn->newRegister(family);
            this->appendPushedConstant(pushed, argLocation);
            inserted.insert(argLocation);
        } else {
            pushed = fn->getArg(loadAddImm.argID).reg;
        }

        // TODO the MOV instruction can be most of the time avoided if the
        // register is never written. We must however support the register
        // replacement in the instruction interface to be able to patch all the
        // instruction that uses "reg"
        const Instruction mov = ir::MOV(type, reg, pushed);
        mov.replace(load);
        dead.insert(load);
      }
    }

    // Remove all unused adds and load immediates
    REMOVE_INSN(add)
    REMOVE_INSN(loadImm)
  }

#undef REMOVE_INSN

  bool FunctionArgumentLowerer::useStore(const ValueDef &def, set<const Instruction*> &visited)
  {
    const UseSet &useSet = dag->getUse(def);
    for (const auto &use : useSet) {
      const Instruction *insn = use->getInstruction();
      const uint32_t srcID = use->getSrcID();
      const Opcode opcode = insn->getOpcode();
      if (visited.contains(insn)) continue;
      visited.insert(insn);
      if (opcode == OP_STORE && srcID == StoreInstruction::addressIndex)
        return true;
      if (insn->isMemberOf<UnaryInstruction>() == false &&
          insn->isMemberOf<BinaryInstruction>() == false)
        continue;
      else {
        const uint32_t dstNum = insn->getDstNum();
        for (uint32_t dstID = 0; dstID < dstNum; ++dstID)
          if (this->useStore(ValueDef(insn, dstID), visited) == true)
            return true;
      }
    }
    return false;
  }

  bool FunctionArgumentLowerer::matchLoadAddImm(uint32_t argID)
  {
    const FunctionArgument &arg = fn->getArg(argID);
    LoadAddImmSeq tmpSeq;

    // Inspect all uses of the function argument pointer
    const UseSet &useSet = dag->getUse(&arg);
    for (auto use : useSet) {
      Instruction *insn = const_cast<Instruction*>(use->getInstruction());
      const Opcode opcode = insn->getOpcode();

      // load dst arg
      LoadAddImm loadAddImm;
      if (matchLoad(insn, NULL, NULL, 0, argID, loadAddImm)) {
        tmpSeq.push_back(loadAddImm);
        continue;
      }

      // add.ptr_type dst ptr other
      if (opcode != OP_ADD) return false;
      BinaryInstruction *add = cast<BinaryInstruction>(insn);
      const Type addType = add->getType();
      const RegisterFamily family = getFamily(addType);
      if (family != unit.getPointerFamily()) return false;
      if (addType == TYPE_FLOAT) return false;

      // step 1 -> check that the other source comes from a load immediate
      const uint32_t srcID = use->getSrcID();
      const uint32_t otherID = srcID ^ 1;
      const DefSet &defSet = dag->getDef(insn, otherID);
      const uint32_t defNum = defSet.size();
      if (defNum == 0 || defNum > 1) continue; // undefined or more than one def
      const ValueDef *otherDef = *defSet.begin();
      if (otherDef->getType() != ValueDef::DEF_INSN_DST) return false;
      Instruction *otherInsn = const_cast<Instruction*>(otherDef->getInstruction());
      if (otherInsn->getOpcode() != OP_LOADI) return false;
      LoadImmInstruction *loadImm = cast<LoadImmInstruction>(otherInsn);
      const Immediate imm = loadImm->getImmediate();
      const uint64_t offset = getOffsetFromImm(imm);

      // step 2 -> check that the results of the add are loads from private
      // memory
      const UseSet &addUseSet = dag->getUse(add, 0);
      for (auto addUse : addUseSet) {
        Instruction *insn = const_cast<Instruction*>(addUse->getInstruction());

        // We finally find something like load dst arg+imm
        LoadAddImm loadAddImm;
        if (matchLoad(insn, add, loadImm, offset, argID, loadAddImm)) {
          tmpSeq.push_back(loadAddImm);
          continue;
        }
      }
    }

    // OK, the argument only need direct loads. We can now append all the
    // direct load definitions we found
    for (const auto &loadImmSeq : tmpSeq)
      seq.push_back(loadImmSeq);
    return true;
  }

  ArgUse FunctionArgumentLowerer::getArgUse(uint32_t argID)
  {
    FunctionArgument &arg = fn->getArg(argID);

    // case 1 - we may store something to the structure argument
    set<const Instruction*> visited;
    if (this->useStore(ValueDef(&arg), visited))
      return ARG_WRITTEN;

    // case 2 - we look for the patterns: LOAD(ptr) or LOAD(ptr+imm)
    if (this->matchLoadAddImm(argID))
      return ARG_DIRECT_READ;

    // case 3 - LOAD(ptr+runtime_value)
    return ARG_INDIRECT_READ;
  }

  void FunctionArgumentLowerer::lower(uint32_t argID) {
    IF_DEBUG(const ArgUse argUse = )this->getArgUse(argID);
#if GBE_DEBUG
    GBE_ASSERTM(argUse != ARG_WRITTEN,
                "TODO A store to a structure argument "
                "(i.e. not a char/short/int/float argument) has been found. "
                "This is not supported yet");
    GBE_ASSERTM(argUse != ARG_INDIRECT_READ,
                "TODO Only direct loads of structure arguments are "
                "supported now");
#endif /* GBE_DEBUG */
  }

  void lowerFunctionArguments(Unit &unit, const std::string &functionName) {
    FunctionArgumentLowerer lowerer(unit);
    lowerer.lower(functionName);
  }

} /* namespace ir */
} /* namespace gbe */

<|endoftext|>


<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2010, 2011, 2012, 2013, 2014
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#ifndef _ABACLADE_HXX_INTERNAL
   #error "Please #include <abaclade.hxx> instead of this file"
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc globals

namespace abc {

/*! DOC:3549 Enumeration classes

Abaclade features support for advanced enumeration classes. These are the features that set them
apart from C++11 “enum class” enumerations:

•  Strong typing: constants from one enumeration are not implicitly converted to a different
   enumeration type;
•  Scoped constants: the enumeration members are members of the enumeration class, not of its
   containing scope; different classes can therefore use constants of the same name because their
   usage will need to be qualified with the enumeration type name;
•  Conversion from/to string: instances of an Abaclade enumeration class can be serialized and
   de-serialized as strings with no additional code.

The ABC_ENUM() macro declares an enumeration class containing the members provided as a list.

The name provided to ABC_ENUM() is associated to a C++ class, not the C++ enum; the latter is
available through the former, as in as my_enum::enum_type; there should little to no need to ever
directly refer to the C++ enum type.

This design is loosely based on <http://www.python.org/dev/peps/pep-0435/>.
*/

/*! Implementation of the various ABC_ENUM() flavors.

@param name
   Name of the enumeration type.
@param cMembers
   Count of members of the enumeration.
@param members
   C++ enum members.
@param arrayitems
   Internal name/value array items.
@param othermembers
   Additional members to be injected as-is at the beginning of the class.
*/
#define _ABC_ENUM_IMPL(name, cMembers, members, arrayitems, othermembers) \
   class ABC_CPP_CAT(_, name, _e) { \
   othermembers \
   \
   public: \
      /*! Publicly-accessible enumerated constants. */ \
      enum enum_type { \
         members \
      }; \
   \
      /*! Returns a pointer to the name/value map to be used by abc::enum_impl. */ \
      static ::abc::detail::enum_member const * _get_map() { \
         static ::abc::detail::enum_member const sc_map[] = { \
            arrayitems \
            { nullptr, 0, 0 } \
         }; \
         return sc_map; \
      } \
   \
   protected: \
      /*! Number of members specified for the enum. */ \
      static std::size_t const smc_cMembers = cMembers; \
   }; \
   typedef ::abc::enum_impl<ABC_CPP_CAT(_, name, _e)> name

/*! Expands into an enum name/value assignment.

@param name
   Name of the enumeration constant. The associated value is incremental in the ABC_ENUM, just like
   in C++ enums.
*/
#define _ABC_ENUM_MEMBER(name) \
         name = __COUNTER__ - smc_iBase,

/*! Expands into an enum name/value assignment.

@param name
   Name of the enumeration constant.
@param value
   Value of the enumeration constant.
*/
#define _ABC_ENUM_MEMBER_PAIR(name, value) \
         name = value,

/*! Expands into an abc::detail::enum_member initializer.

@param name
   Name of the enumeration constant.
*/
#define _ABC_ENUM_MEMBER_ARRAY_ITEM(name) \
            { \
               ABC_SL(ABC_CPP_TOSTRING(name)), \
               static_cast<unsigned short>(ABC_COUNTOF(ABC_CPP_TOSTRING(name)) - 1 /*NUL*/), \
               name \
            },

/*! Expands into _ABC_ENUM_MEMBER_ARRAY_ITEM().

@param name
   Name of the enumeration constant.
@param value
   Value of the enumeration constant. Not used.
*/
#define _ABC_ENUM_MEMBER_PAIR_ARRAY_ITEM(name, value) \
   _ABC_ENUM_MEMBER_ARRAY_ITEM(name)

/*! Defines an enumeration class as a specialization of abc::enum_impl. See [DOC:3549 Enumeration
classes] for more information.

TODO: support for bit-field enumerations? Allow logical operation, smart conversion to/from string,
etc.

@param name
   Name of the enumeration type.
@param ...
   Sequence of (name, value) pairs; these will be the members of the underlying C++ enum,
   name::enum_type.
*/
#define ABC_ENUM(name, ...) \
   _ABC_ENUM_IMPL( \
      name, \
      ABC_CPP_LIST_COUNT(__VA_ARGS__), \
      ABC_CPP_TUPLELIST_WALK(_ABC_ENUM_MEMBER_PAIR, __VA_ARGS__), \
      ABC_CPP_TUPLELIST_WALK(_ABC_ENUM_MEMBER_PAIR_ARRAY_ITEM, __VA_ARGS__), \
      \
      public: \
   )

/*! Defines an enumeration class as a specialization of abc::enum_impl. See [DOC:3549 Enumeration
classes] for more information. Similar to ABC_ENUM(), except the members are listed individually and
their values cannot be explicitly specified; for example:

   ABC_ENUM_AUTO_VALUES(myenum, item1, item2, item3);

@param name
   Name of the enumeration type.
@param ...
   Sequence of member names; these will be the members of the underlying C++ enum, name::enum_type.
   Their values will start from 0 and increase by 1 for each member.
*/
#define ABC_ENUM_AUTO_VALUES(name, ...) \
   _ABC_ENUM_IMPL( \
      name, \
      ABC_CPP_LIST_COUNT(__VA_ARGS__), \
      ABC_CPP_LIST_WALK(_ABC_ENUM_MEMBER, __VA_ARGS__), \
      ABC_CPP_LIST_WALK(_ABC_ENUM_MEMBER_ARRAY_ITEM, __VA_ARGS__), \
      \
      private: \
         static int const smc_iBase = __COUNTER__ + 1; \
   )

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::detail::enum_member

namespace abc {
namespace detail {

//! Enumeration member (name/value pair).
struct ABACLADE_SYM enum_member {
   //! Name.
   char_t const * pszName;
   //! Size of *pszName, in characters.
   unsigned short cchName;
   //! Value.
   int iValue;

   /*! Finds and returns the member associated to the specified enumerated value or name. If no
   match is found, an exception will be throw.

   @param pem
      Pointer to the first item in the enumeration members array; the last item in the array has a
      nullptr name string pointer.
   @param iValue
      Value of the constant to search for.
   @param sName
      Name of the constant to search for.
   @return
      Pointer to the matching name/value pair.
   */
   static enum_member const * find_in_map(enum_member const * pem, int iValue);
   static enum_member const * find_in_map(enum_member const * pem, istr const & sName);
};

} //namespace detail
} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::enum_impl

namespace abc {

//! Implementation of enumeration classes.
template <class T>
class enum_impl : public T {
public:
   //! Underlying C++ eunm type.
   typedef typename T::enum_type enum_type;

   /*! Constructor.

   @param e
      Source value.
   @param iValue
      Integer value to be converted to enum_type. If i has a value not in enum_type, an exception
      will be thrown.
   @param sName
      String to be converted to enum_type. If this does not match exactly the name of one of the
      members of enum_type, an exception of type abc::domain_error will be thrown.
   */
   enum_impl() {
   }
   enum_impl(enum_type e) :
      m_e(e) {
   }
   enum_impl(enum_impl const & e) :
      m_e(e.m_e) {
   }
   // Conversion from integer.
   explicit enum_impl(int iValue) :
      m_e(static_cast<enum_type>(detail::enum_member::find_in_map(T::_get_map(), iValue)->iValue)) {
   }
   // Conversion from string.
   explicit enum_impl(istr const & sName) :
      m_e(static_cast<enum_type>(detail::enum_member::find_in_map(T::_get_map(), sName)->iValue)) {
   }

   /*! Assignment operator.

   @param e
      Source value.
   @return
      *this.
   */
   enum_impl & operator=(enum_impl const & e) {
      m_e = e.m_e;
      return *this;
   }
   enum_impl & operator=(enum_type e) {
      m_e = e;
      return *this;
   }

   /*! Returns the current base enumerated value.

   @return
      Current value.
   */
   enum_type base() const {
      return m_e;
   }

   /*! Returns the name of the current enumerated value.

   @return
      Name of the current value.
   */
   istr name() const;

   /*! Returns the count of members in the enumeration.

   @return
      Member count.
   */
   static std::size_t size() {
      return T::smc_cMembers;
   }

protected:
   /*! Returns a pointer to the name/value pair for the current value.

   @return
      Pointer to the name/value pair for the current value.
   */
   detail::enum_member const * _member() const {
      return detail::enum_member::find_in_map(T::_get_map(), m_e);
   }

public:
   /*! Count of the members of the enumeration. Same as the value returned by size(), but this can
   be used in constant contexts, such as the size of an array.
   */
   static std::size_t const size_const = T::smc_cMembers;

private:
   //! Enumerated value.
   enum_type m_e;
};

// Relational operators.
#define ABC_RELOP_IMPL(op) \
   template <class T> \
   inline bool operator op(enum_impl<T> const & e1, enum_impl<T> const & e2) { \
      return e1.base() op e2.base(); \
   } \
   template <class T> \
   inline bool operator op(enum_impl<T> const & e1, typename enum_impl<T>::enum_type e2) { \
      return e1.base() op e2; \
   } \
   template <class T> \
   inline bool operator op(typename enum_impl<T>::enum_type e1, enum_impl<T> const & e2) { \
      return e1 op e2.base(); \
   }
ABC_RELOP_IMPL(==)
ABC_RELOP_IMPL(!=)
ABC_RELOP_IMPL(>)
ABC_RELOP_IMPL(>=)
ABC_RELOP_IMPL(<)
ABC_RELOP_IMPL(<=)
#undef ABC_RELOP_IMPL

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
<commit_msg>Change DOC:3549 into enumeration_classes page<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2010, 2011, 2012, 2013, 2014, 2015
Raffaello D. Di Napoli

This file is part of Abaclade.

Abaclade is free software: you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

Abaclade is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with Abaclade. If not, see
<http://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------------------------*/

#ifndef _ABACLADE_HXX_INTERNAL
   #error "Please #include <abaclade.hxx> instead of this file"
#endif

/*! @file
Facilities to create smart enumerations, as described in @ref enumeration_classes
*/
/*! @page enumeration_classes Enumeration classes
Support for advanced enumeration classes. These are the features that set them apart from C++11
“enum class” enumerations:

•  Strong typing: constants from one enumeration are not implicitly converted to a different
   enumeration type;
•  Scoped constants: the enumeration members are members of the enumeration class, not of its
   containing scope; different classes can therefore use constants of the same name because their
   usage will need to be qualified with the enumeration type name;
•  Conversion from/to string: instances of an Abaclade enumeration class can be serialized and
   de-serialized as strings with no additional code.

The ABC_ENUM() macro declares an enumeration class containing the members provided as a list;
ABC_ENUM_AUTO_VALUES() behaves like a C++ enum lacking explicit enumerated values.

The name provided to ABC_ENUM() is associated to a C++ class, not the C++ enum; the latter is
available through the former, as in as my_enum::enum_type; there should little to no need to ever
directly refer to the C++ enum type.

This design is loosely based on <http://www.python.org/dev/peps/pep-0435/>.
*/

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc globals

namespace abc {

//! @cond

/*! Implementation of the various ABC_ENUM*() flavors.

@param name
   Name of the enumeration type.
@param cMembers
   Count of members of the enumeration.
@param members
   C++ enum members.
@param arrayitems
   Internal name/value array items.
@param othermembers
   Additional members to be injected as-is at the beginning of the class.
*/
#define _ABC_ENUM_IMPL(name, cMembers, members, arrayitems, othermembers) \
   class ABC_CPP_CAT(_, name, _e) { \
   othermembers \
   \
   public: \
      /*! Publicly-accessible enumerated constants. */ \
      enum enum_type { \
         members \
      }; \
   \
      /*! Returns a pointer to the name/value map to be used by abc::enum_impl. */ \
      static ::abc::detail::enum_member const * _get_map() { \
         static ::abc::detail::enum_member const sc_map[] = { \
            arrayitems \
            { nullptr, 0, 0 } \
         }; \
         return sc_map; \
      } \
   \
   protected: \
      /*! Number of members specified for the enum. */ \
      static std::size_t const smc_cMembers = cMembers; \
   }; \
   typedef ::abc::enum_impl<ABC_CPP_CAT(_, name, _e)> name

/*! Expands into an enum name/value assignment.

@param name
   Name of the enumeration constant. The associated value is incremental in the ABC_ENUM, just like
   in C++ enums.
*/
#define _ABC_ENUM_MEMBER(name) \
         name = __COUNTER__ - smc_iBase,

/*! Expands into an enum name/value assignment.

@param name
   Name of the enumeration constant.
@param value
   Value of the enumeration constant.
*/
#define _ABC_ENUM_MEMBER_PAIR(name, value) \
         name = value,

/*! Expands into an abc::detail::enum_member initializer.

@param name
   Name of the enumeration constant.
*/
#define _ABC_ENUM_MEMBER_ARRAY_ITEM(name) \
            { \
               ABC_SL(ABC_CPP_TOSTRING(name)), \
               static_cast<unsigned short>(ABC_COUNTOF(ABC_CPP_TOSTRING(name)) - 1 /*NUL*/), \
               name \
            },

/*! Expands into _ABC_ENUM_MEMBER_ARRAY_ITEM().

@param name
   Name of the enumeration constant.
@param value
   Value of the enumeration constant. Not used.
*/
#define _ABC_ENUM_MEMBER_PAIR_ARRAY_ITEM(name, value) \
   _ABC_ENUM_MEMBER_ARRAY_ITEM(name)

//! @endcond

/*! Defines an enumeration class as a specialization of abc::enum_impl. See @ref enumeration_classes
for more information.

TODO: support for bit-field enumerations? Allow logical operation, smart conversion to/from string,
etc.

@param name
   Name of the enumeration type.
@param ...
   Sequence of (name, value) pairs; these will be the members of the underlying C++ enum,
   name::enum_type.
*/
#define ABC_ENUM(name, ...) \
   _ABC_ENUM_IMPL( \
      name, \
      ABC_CPP_LIST_COUNT(__VA_ARGS__), \
      ABC_CPP_TUPLELIST_WALK(_ABC_ENUM_MEMBER_PAIR, __VA_ARGS__), \
      ABC_CPP_TUPLELIST_WALK(_ABC_ENUM_MEMBER_PAIR_ARRAY_ITEM, __VA_ARGS__), \
      \
      public: \
   )

/*! Defines an enumeration class as a specialization of abc::enum_impl. See @ref enumeration_classes
for more information. Similar to ABC_ENUM(), except the members are listed individually and their
values cannot be explicitly specified; for example:

   ABC_ENUM_AUTO_VALUES(myenum, item1, item2, item3);

@param name
   Name of the enumeration type.
@param ...
   Sequence of member names; these will be the members of the underlying C++ enum, name::enum_type.
   Their values will start from 0 and increase by 1 for each member.
*/
#define ABC_ENUM_AUTO_VALUES(name, ...) \
   _ABC_ENUM_IMPL( \
      name, \
      ABC_CPP_LIST_COUNT(__VA_ARGS__), \
      ABC_CPP_LIST_WALK(_ABC_ENUM_MEMBER, __VA_ARGS__), \
      ABC_CPP_LIST_WALK(_ABC_ENUM_MEMBER_ARRAY_ITEM, __VA_ARGS__), \
      \
      private: \
         static int const smc_iBase = __COUNTER__ + 1; \
   )

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::detail::enum_member

namespace abc {
namespace detail {

//! Enumeration member (name/value pair).
struct ABACLADE_SYM enum_member {
   //! Name.
   char_t const * pszName;
   //! Size of *pszName, in characters.
   unsigned short cchName;
   //! Value.
   int iValue;

   /*! Finds and returns the member associated to the specified enumerated value or name. If no
   match is found, an exception will be throw.

   @param pem
      Pointer to the first item in the enumeration members array; the last item in the array has a
      nullptr name string pointer.
   @param iValue
      Value of the constant to search for.
   @param sName
      Name of the constant to search for.
   @return
      Pointer to the matching name/value pair.
   */
   static enum_member const * find_in_map(enum_member const * pem, int iValue);
   static enum_member const * find_in_map(enum_member const * pem, istr const & sName);
};

} //namespace detail
} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
// abc::enum_impl

namespace abc {

//! Implementation of enumeration classes.
template <class T>
class enum_impl : public T {
public:
   //! Underlying C++ eunm type.
   typedef typename T::enum_type enum_type;

   /*! Constructor.

   @param e
      Source value.
   @param iValue
      Integer value to be converted to enum_type. If i has a value not in enum_type, an exception
      will be thrown.
   @param sName
      String to be converted to enum_type. If this does not match exactly the name of one of the
      members of enum_type, an exception of type abc::domain_error will be thrown.
   */
   enum_impl() {
   }
   enum_impl(enum_type e) :
      m_e(e) {
   }
   enum_impl(enum_impl const & e) :
      m_e(e.m_e) {
   }
   // Conversion from integer.
   explicit enum_impl(int iValue) :
      m_e(static_cast<enum_type>(detail::enum_member::find_in_map(T::_get_map(), iValue)->iValue)) {
   }
   // Conversion from string.
   explicit enum_impl(istr const & sName) :
      m_e(static_cast<enum_type>(detail::enum_member::find_in_map(T::_get_map(), sName)->iValue)) {
   }

   /*! Assignment operator.

   @param e
      Source value.
   @return
      *this.
   */
   enum_impl & operator=(enum_impl const & e) {
      m_e = e.m_e;
      return *this;
   }
   enum_impl & operator=(enum_type e) {
      m_e = e;
      return *this;
   }

   /*! Returns the current base enumerated value.

   @return
      Current value.
   */
   enum_type base() const {
      return m_e;
   }

   /*! Returns the name of the current enumerated value.

   @return
      Name of the current value.
   */
   istr name() const;

   /*! Returns the count of members in the enumeration.

   @return
      Member count.
   */
   static std::size_t size() {
      return T::smc_cMembers;
   }

protected:
   /*! Returns a pointer to the name/value pair for the current value.

   @return
      Pointer to the name/value pair for the current value.
   */
   detail::enum_member const * _member() const {
      return detail::enum_member::find_in_map(T::_get_map(), m_e);
   }

public:
   /*! Count of the members of the enumeration. Same as the value returned by size(), but this can
   be used in constant contexts, such as the size of an array.
   */
   static std::size_t const size_const = T::smc_cMembers;

private:
   //! Enumerated value.
   enum_type m_e;
};

// Relational operators.
#define ABC_RELOP_IMPL(op) \
   template <class T> \
   inline bool operator op(enum_impl<T> const & e1, enum_impl<T> const & e2) { \
      return e1.base() op e2.base(); \
   } \
   template <class T> \
   inline bool operator op(enum_impl<T> const & e1, typename enum_impl<T>::enum_type e2) { \
      return e1.base() op e2; \
   } \
   template <class T> \
   inline bool operator op(typename enum_impl<T>::enum_type e1, enum_impl<T> const & e2) { \
      return e1 op e2.base(); \
   }
ABC_RELOP_IMPL(==)
ABC_RELOP_IMPL(!=)
ABC_RELOP_IMPL(>)
ABC_RELOP_IMPL(>=)
ABC_RELOP_IMPL(<)
ABC_RELOP_IMPL(<=)
#undef ABC_RELOP_IMPL

} //namespace abc

////////////////////////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>//
// version.hpp
// ***********
//
// Copyright (c) 2018 Sharon W (sharon at aegis dot gg)
//
// Distributed under the MIT License. (See accompanying file LICENSE)
//

#pragma once

#if !defined(AEGIS_VERSION_LONG)
#define AEGIS_VERSION_LONG 0x00010400
#define AEGIS_VERSION_SHORT 010400
#define AEGIS_VERSION_TEXT "aegis.cpp 1.4.0 2018/10/20"

#define AEGIS_VERSION_MAJOR ((AEGIS_VERSION_LONG & 0x00ff0000) >> 16)
#define AEGIS_VERSION_MINOR ((AEGIS_VERSION_LONG & 0x0000ff00) >> 8)
#define AEGIS_VERSION_PATCH (AEGIS_VERSION_LONG & 0x000000ff)
#endif
<commit_msg>Version bump<commit_after>//
// version.hpp
// ***********
//
// Copyright (c) 2018 Sharon W (sharon at aegis dot gg)
//
// Distributed under the MIT License. (See accompanying file LICENSE)
//

#pragma once

#if !defined(AEGIS_VERSION_LONG)
#define AEGIS_VERSION_LONG 0x00020000
#define AEGIS_VERSION_SHORT 020000
#define AEGIS_VERSION_TEXT "aegis.cpp 2.0.0 2019/2/3"

#define AEGIS_VERSION_MAJOR ((AEGIS_VERSION_LONG & 0x00ff0000) >> 16)
#define AEGIS_VERSION_MINOR ((AEGIS_VERSION_LONG & 0x0000ff00) >> 8)
#define AEGIS_VERSION_PATCH (AEGIS_VERSION_LONG & 0x000000ff)
#endif
<|endoftext|>