/*
   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   SLEPc - Scalable Library for Eigenvalue Problem Computations
   Copyright (c) 2002-, Universitat Politecnica de Valencia, Spain

   This file is part of SLEPc.
   SLEPc is distributed under a 2-clause BSD license (see LICENSE).
   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
   Basic RG routines
*/

#include <slepc/private/rgimpl.h>      /*I "slepcrg.h" I*/

PetscFunctionList RGList = NULL;
PetscBool         RGRegisterAllCalled = PETSC_FALSE;
PetscClassId      RG_CLASSID = 0;
static PetscBool  RGPackageInitialized = PETSC_FALSE;

/*@C
   RGFinalizePackage - This function destroys everything in the Slepc interface
   to the RG package. It is called from SlepcFinalize().

   Level: developer

.seealso: SlepcFinalize()
@*/
PetscErrorCode RGFinalizePackage(void)
{
  PetscFunctionBegin;
  PetscCall(PetscFunctionListDestroy(&RGList));
  RGPackageInitialized = PETSC_FALSE;
  RGRegisterAllCalled  = PETSC_FALSE;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  RGInitializePackage - This function initializes everything in the RG package.
  It is called from PetscDLLibraryRegister() when using dynamic libraries, and
  on the first call to RGCreate() when using static libraries.

  Level: developer

.seealso: SlepcInitialize()
@*/
PetscErrorCode RGInitializePackage(void)
{
  char           logList[256];
  PetscBool      opt,pkg;
  PetscClassId   classids[1];

  PetscFunctionBegin;
  if (RGPackageInitialized) PetscFunctionReturn(PETSC_SUCCESS);
  RGPackageInitialized = PETSC_TRUE;
  /* Register Classes */
  PetscCall(PetscClassIdRegister("Region",&RG_CLASSID));
  /* Register Constructors */
  PetscCall(RGRegisterAll());
  /* Process Info */
  classids[0] = RG_CLASSID;
  PetscCall(PetscInfoProcessClass("rg",1,&classids[0]));
  /* Process summary exclusions */
  PetscCall(PetscOptionsGetString(NULL,NULL,"-log_exclude",logList,sizeof(logList),&opt));
  if (opt) {
    PetscCall(PetscStrInList("rg",logList,',',&pkg));
    if (pkg) PetscCall(PetscLogEventDeactivateClass(RG_CLASSID));
  }
  /* Register package finalizer */
  PetscCall(PetscRegisterFinalize(RGFinalizePackage));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGCreate - Creates an RG context.

   Collective

   Input Parameter:
.  comm - MPI communicator

   Output Parameter:
.  newrg - location to put the RG context

   Level: beginner

.seealso: RGDestroy(), RG
@*/
PetscErrorCode RGCreate(MPI_Comm comm,RG *newrg)
{
  RG             rg;

  PetscFunctionBegin;
  PetscAssertPointer(newrg,2);
  *newrg = NULL;
  PetscCall(RGInitializePackage());
  PetscCall(SlepcHeaderCreate(rg,RG_CLASSID,"RG","Region","RG",comm,RGDestroy,RGView));
  rg->complement = PETSC_FALSE;
  rg->sfactor    = 1.0;
  rg->osfactor   = 0.0;
  rg->data       = NULL;

  *newrg = rg;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGSetOptionsPrefix - Sets the prefix used for searching for all
   RG options in the database.

   Logically Collective

   Input Parameters:
+  rg     - the region context
-  prefix - the prefix string to prepend to all RG option requests

   Notes:
   A hyphen (-) must NOT be given at the beginning of the prefix name.
   The first character of all runtime options is AUTOMATICALLY the
   hyphen.

   Level: advanced

.seealso: RGAppendOptionsPrefix()
@*/
PetscErrorCode RGSetOptionsPrefix(RG rg,const char *prefix)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscCall(PetscObjectSetOptionsPrefix((PetscObject)rg,prefix));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGAppendOptionsPrefix - Appends to the prefix used for searching for all
   RG options in the database.

   Logically Collective

   Input Parameters:
+  rg     - the region context
-  prefix - the prefix string to prepend to all RG option requests

   Notes:
   A hyphen (-) must NOT be given at the beginning of the prefix name.
   The first character of all runtime options is AUTOMATICALLY the hyphen.

   Level: advanced

.seealso: RGSetOptionsPrefix()
@*/
PetscErrorCode RGAppendOptionsPrefix(RG rg,const char *prefix)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)rg,prefix));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGGetOptionsPrefix - Gets the prefix used for searching for all
   RG options in the database.

   Not Collective

   Input Parameters:
.  rg - the region context

   Output Parameters:
.  prefix - pointer to the prefix string used is returned

   Note:
   On the Fortran side, the user should pass in a string 'prefix' of
   sufficient length to hold the prefix.

   Level: advanced

.seealso: RGSetOptionsPrefix(), RGAppendOptionsPrefix()
@*/
PetscErrorCode RGGetOptionsPrefix(RG rg,const char *prefix[])
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscAssertPointer(prefix,2);
  PetscCall(PetscObjectGetOptionsPrefix((PetscObject)rg,prefix));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGSetType - Selects the type for the RG object.

   Logically Collective

   Input Parameters:
+  rg   - the region context
-  type - a known type

   Level: intermediate

.seealso: RGGetType()
@*/
PetscErrorCode RGSetType(RG rg,RGType type)
{
  PetscErrorCode (*r)(RG);
  PetscBool      match;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscAssertPointer(type,2);

  PetscCall(PetscObjectTypeCompare((PetscObject)rg,type,&match));
  if (match) PetscFunctionReturn(PETSC_SUCCESS);

  PetscCall(PetscFunctionListFind(RGList,type,&r));
  PetscCheck(r,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_UNKNOWN_TYPE,"Unable to find requested RG type %s",type);

  PetscTryTypeMethod(rg,destroy);
  PetscCall(PetscMemzero(rg->ops,sizeof(struct _RGOps)));

  PetscCall(PetscObjectChangeTypeName((PetscObject)rg,type));
  PetscCall((*r)(rg));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGGetType - Gets the RG type name (as a string) from the RG context.

   Not Collective

   Input Parameter:
.  rg - the region context

   Output Parameter:
.  type - name of the region

   Level: intermediate

.seealso: RGSetType()
@*/
PetscErrorCode RGGetType(RG rg,RGType *type)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscAssertPointer(type,2);
  *type = ((PetscObject)rg)->type_name;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGSetFromOptions - Sets RG options from the options database.

   Collective

   Input Parameters:
.  rg - the region context

   Notes:
   To see all options, run your program with the -help option.

   Level: beginner

.seealso: RGSetOptionsPrefix()
@*/
PetscErrorCode RGSetFromOptions(RG rg)
{
  char           type[256];
  PetscBool      flg;
  PetscReal      sfactor;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscCall(RGRegisterAll());
  PetscObjectOptionsBegin((PetscObject)rg);
    PetscCall(PetscOptionsFList("-rg_type","Region type","RGSetType",RGList,(char*)(((PetscObject)rg)->type_name?((PetscObject)rg)->type_name:RGINTERVAL),type,sizeof(type),&flg));
    if (flg) PetscCall(RGSetType(rg,type));
    else if (!((PetscObject)rg)->type_name) PetscCall(RGSetType(rg,RGINTERVAL));

    PetscCall(PetscOptionsBool("-rg_complement","Whether region is complemented or not","RGSetComplement",rg->complement,&rg->complement,NULL));

    PetscCall(PetscOptionsReal("-rg_scale","Scaling factor","RGSetScale",1.0,&sfactor,&flg));
    if (flg) PetscCall(RGSetScale(rg,sfactor));

    PetscTryTypeMethod(rg,setfromoptions,PetscOptionsObject);
    PetscCall(PetscObjectProcessOptionsHandlers((PetscObject)rg,PetscOptionsObject));
  PetscOptionsEnd();
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGView - Prints the RG data structure.

   Collective

   Input Parameters:
+  rg - the region context
-  viewer - optional visualization context

   Note:
   The available visualization contexts include
+     PETSC_VIEWER_STDOUT_SELF - standard output (default)
-     PETSC_VIEWER_STDOUT_WORLD - synchronized standard
         output where only the first processor opens
         the file.  All other processors send their
         data to the first processor to print.

   The user can open an alternative visualization context with
   PetscViewerASCIIOpen() - output to a specified file.

   Level: beginner

.seealso: RGCreate()
@*/
PetscErrorCode RGView(RG rg,PetscViewer viewer)
{
  PetscBool      isdraw,isascii;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PetscObjectComm((PetscObject)rg),&viewer));
  PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
  PetscCheckSameComm(rg,1,viewer,2);
  PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERDRAW,&isdraw));
  PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
  if (isascii) {
    PetscCall(PetscObjectPrintClassNamePrefixType((PetscObject)rg,viewer));
    PetscCall(PetscViewerASCIIPushTab(viewer));
    PetscTryTypeMethod(rg,view,viewer);
    PetscCall(PetscViewerASCIIPopTab(viewer));
    if (rg->complement) PetscCall(PetscViewerASCIIPrintf(viewer,"  selected region is the complement of the specified one\n"));
    if (rg->sfactor!=1.0) PetscCall(PetscViewerASCIIPrintf(viewer,"  scaling factor = %g\n",(double)rg->sfactor));
  } else if (isdraw) PetscTryTypeMethod(rg,view,viewer);
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGViewFromOptions - View from options

   Collective

   Input Parameters:
+  rg   - the region context
.  obj  - optional object
-  name - command line option

   Level: intermediate

.seealso: RGView(), RGCreate()
@*/
PetscErrorCode RGViewFromOptions(RG rg,PetscObject obj,const char name[])
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscCall(PetscObjectViewFromOptions((PetscObject)rg,obj,name));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGIsTrivial - Whether it is the trivial region (whole complex plane).

   Not Collective

   Input Parameter:
.  rg - the region context

   Output Parameter:
.  trivial - true if the region is equal to the whole complex plane, e.g.,
             an interval region with all four endpoints unbounded or an
             ellipse with infinite radius.

   Level: beginner

.seealso: RGCheckInside()
@*/
PetscErrorCode RGIsTrivial(RG rg,PetscBool *trivial)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidType(rg,1);
  PetscAssertPointer(trivial,2);
  *trivial = PETSC_FALSE;
  PetscTryTypeMethod(rg,istrivial,trivial);
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGCheckInside - Determines if a set of given points are inside the region or not.

   Not Collective

   Input Parameters:
+  rg - the region context
.  n  - number of points to check
.  ar - array of real parts
-  ai - array of imaginary parts

   Output Parameter:
.  inside - array of results (1=inside, 0=on the contour, -1=outside)

   Note:
   The point a is expressed as a couple of PetscScalar variables ar,ai.
   If built with complex scalars, the point is supposed to be stored in ar,
   otherwise ar,ai contain the real and imaginary parts, respectively.

   If a scaling factor was set, the points are scaled before checking.

   Level: intermediate

.seealso: RGSetScale(), RGSetComplement()
@*/
PetscErrorCode RGCheckInside(RG rg,PetscInt n,PetscScalar *ar,PetscScalar *ai,PetscInt *inside)
{
  PetscReal      px,py;
  PetscInt       i;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidType(rg,1);
  PetscAssertPointer(ar,3);
#if !defined(PETSC_USE_COMPLEX)
  PetscAssertPointer(ai,4);
#endif
  PetscAssertPointer(inside,5);

  for (i=0;i<n;i++) {
#if defined(PETSC_USE_COMPLEX)
    px = PetscRealPart(ar[i]);
    py = PetscImaginaryPart(ar[i]);
#else
    px = ar[i];
    py = ai[i];
#endif
    if (PetscUnlikely(rg->sfactor != 1.0)) {
      px /= rg->sfactor;
      py /= rg->sfactor;
    }
    PetscUseTypeMethod(rg,checkinside,px,py,inside+i);
    if (PetscUnlikely(rg->complement)) inside[i] = -inside[i];
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGIsAxisymmetric - Determines if the region is symmetric with respect
   to the real or imaginary axis.

   Not Collective

   Input Parameters:
+  rg       - the region context
-  vertical - true if symmetry must be checked against the vertical axis

   Output Parameter:
.  symm - true if the region is axisymmetric

   Note:
   If the vertical argument is true, symmetry is checked with respect to
   the vertical axis, otherwise with respect to the horizontal axis.

   Level: intermediate

.seealso: RGCanUseConjugates()
@*/
PetscErrorCode RGIsAxisymmetric(RG rg,PetscBool vertical,PetscBool *symm)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidType(rg,1);
  PetscAssertPointer(symm,3);
  *symm = PETSC_FALSE;
  PetscTryTypeMethod(rg,isaxisymmetric,vertical,symm);
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGCanUseConjugates - Used in contour integral methods to determine whether
   half of integration points can be avoided (use their conjugates).

   Not Collective

   Input Parameters:
+  rg       - the region context
-  realmats - true if the problem matrices are real

   Output Parameter:
.  useconj  - whether it is possible to use conjugates

   Notes:
   If some integration points are the conjugates of other points, then the
   associated computational cost can be saved. This depends on the problem
   matrices being real and also the region being symmetric with respect to
   the horizontal axis. The result is false if using real arithmetic or
   in the case of a flat region (height equal to zero).

   Level: developer

.seealso: RGIsAxisymmetric()
@*/
PetscErrorCode RGCanUseConjugates(RG rg,PetscBool realmats,PetscBool *useconj)
{
#if defined(PETSC_USE_COMPLEX)
  PetscReal      c,d;
  PetscBool      isaxisymm;
#endif

  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidType(rg,1);
  PetscAssertPointer(useconj,3);
  *useconj = PETSC_FALSE;
#if defined(PETSC_USE_COMPLEX)
  if (realmats) {
    PetscCall(RGIsAxisymmetric(rg,PETSC_FALSE,&isaxisymm));
    if (isaxisymm) {
      PetscCall(RGComputeBoundingBox(rg,NULL,NULL,&c,&d));
      if (c!=d) *useconj = PETSC_TRUE;
    }
  }
#endif
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGComputeContour - Computes the coordinates of several points lying on the
   contour of the region.

   Not Collective

   Input Parameters:
+  rg - the region context
-  n  - number of points to compute

   Output Parameters:
+  cr - location to store real parts
-  ci - location to store imaginary parts

   Notes:
   In real scalars, either cr or ci can be NULL (but not both). In complex
   scalars, the coordinates are stored in cr, which cannot be NULL (ci is
   not referenced).

   Level: intermediate

.seealso: RGComputeBoundingBox()
@*/
PetscErrorCode RGComputeContour(RG rg,PetscInt n,PetscScalar cr[],PetscScalar ci[])
{
  PetscInt       i;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidType(rg,1);
#if defined(PETSC_USE_COMPLEX)
  PetscAssertPointer(cr,3);
#else
  PetscCheck(cr || ci,PetscObjectComm((PetscObject)rg),PETSC_ERR_SUP,"cr and ci cannot be NULL simultaneously");
#endif
  PetscCheck(!rg->complement,PetscObjectComm((PetscObject)rg),PETSC_ERR_SUP,"Cannot compute contour of region with complement flag set");
  PetscUseTypeMethod(rg,computecontour,n,cr,ci);
  for (i=0;i<n;i++) {
    if (cr) cr[i] *= rg->sfactor;
    if (ci) ci[i] *= rg->sfactor;
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGComputeBoundingBox - Determines the endpoints of a rectangle in the complex plane that
   contains the region.

   Not Collective

   Input Parameter:
.  rg - the region context

   Output Parameters:
+  a - left endpoint of the bounding box in the real axis
.  b - right endpoint of the bounding box in the real axis
.  c - bottom endpoint of the bounding box in the imaginary axis
-  d - top endpoint of the bounding box in the imaginary axis

   Notes:
   The bounding box is defined as [a,b]x[c,d]. In regions that are not bounded (e.g. an
   open interval) or with the complement flag set, it makes no sense to compute a bounding
   box, so the return values are infinite.

   Level: intermediate

.seealso: RGComputeContour()
@*/
PetscErrorCode RGComputeBoundingBox(RG rg,PetscReal *a,PetscReal *b,PetscReal *c,PetscReal *d)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidType(rg,1);

  if (rg->complement) {  /* cannot compute bounding box */
    if (a) *a = -PETSC_MAX_REAL;
    if (b) *b =  PETSC_MAX_REAL;
    if (c) *c = -PETSC_MAX_REAL;
    if (d) *d =  PETSC_MAX_REAL;
  } else {
    PetscUseTypeMethod(rg,computebbox,a,b,c,d);
    if (a && *a!=-PETSC_MAX_REAL) *a *= rg->sfactor;
    if (b && *b!= PETSC_MAX_REAL) *b *= rg->sfactor;
    if (c && *c!=-PETSC_MAX_REAL) *c *= rg->sfactor;
    if (d && *d!= PETSC_MAX_REAL) *d *= rg->sfactor;
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGComputeQuadrature - Computes the values of the parameters used in a
   quadrature rule for a contour integral around the boundary of the region.

   Not Collective

   Input Parameters:
+  rg   - the region context
.  quad - the type of quadrature
-  n    - number of quadrature points to compute

   Output Parameters:
+  z  - quadrature points
.  zn - normalized quadrature points
-  w  - quadrature weights

   Notes:
   In complex scalars, the values returned in z are often the same as those
   computed by RGComputeContour(), but this is not the case in real scalars
   where all output arguments are real.

   The computed values change for different quadrature rules (trapezoidal
   or Chebyshev).

   Level: intermediate

.seealso: RGComputeContour()
@*/
PetscErrorCode RGComputeQuadrature(RG rg,RGQuadRule quad,PetscInt n,PetscScalar z[],PetscScalar zn[],PetscScalar w[])
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidType(rg,1);
  PetscAssertPointer(z,4);
  PetscAssertPointer(zn,5);
  PetscAssertPointer(w,6);

  PetscCall(RGComputeContour(rg,n,z,NULL));
  PetscUseTypeMethod(rg,computequadrature,quad,n,z,zn,w);
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGSetComplement - Sets a flag to indicate that the region is the complement
   of the specified one.

   Logically Collective

   Input Parameters:
+  rg  - the region context
-  flg - the boolean flag

   Options Database Key:
.  -rg_complement <bool> - Activate/deactivate the complementation of the region

   Level: intermediate

.seealso: RGGetComplement()
@*/
PetscErrorCode RGSetComplement(RG rg,PetscBool flg)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidLogicalCollectiveBool(rg,flg,2);
  rg->complement = flg;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGGetComplement - Gets a flag that indicates whether the region
   is complemented or not.

   Not Collective

   Input Parameter:
.  rg - the region context

   Output Parameter:
.  flg - the flag

   Level: intermediate

.seealso: RGSetComplement()
@*/
PetscErrorCode RGGetComplement(RG rg,PetscBool *flg)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscAssertPointer(flg,2);
  *flg = rg->complement;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGSetScale - Sets the scaling factor to be used when checking that a
   point is inside the region and when computing the contour.

   Logically Collective

   Input Parameters:
+  rg      - the region context
-  sfactor - the scaling factor

   Options Database Key:
.  -rg_scale <real> - Sets the scaling factor

   Level: advanced

.seealso: RGGetScale(), RGCheckInside()
@*/
PetscErrorCode RGSetScale(RG rg,PetscReal sfactor)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidLogicalCollectiveReal(rg,sfactor,2);
  if (sfactor == (PetscReal)PETSC_DEFAULT || sfactor == (PetscReal)PETSC_DECIDE) sfactor = 1.0;
  PetscCheck(sfactor>0.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"Illegal value of scaling factor. Must be > 0");
  rg->sfactor = sfactor;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGGetScale - Gets the scaling factor.

   Not Collective

   Input Parameter:
.  rg - the region context

   Output Parameter:
.  sfactor - the scaling factor

   Level: advanced

.seealso: RGSetScale()
@*/
PetscErrorCode RGGetScale(RG rg,PetscReal *sfactor)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscAssertPointer(sfactor,2);
  *sfactor = rg->sfactor;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGPushScale - Sets an additional scaling factor, that will multiply the
   user-defined scaling factor.

   Logically Collective

   Input Parameters:
+  rg      - the region context
-  sfactor - the scaling factor

   Notes:
   The current implementation does not allow pushing several scaling factors.

   This is intended for internal use, for instance in polynomial eigensolvers
   that use parameter scaling.

   Level: developer

.seealso: RGPopScale(), RGSetScale()
@*/
PetscErrorCode RGPushScale(RG rg,PetscReal sfactor)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscValidLogicalCollectiveReal(rg,sfactor,2);
  PetscCheck(sfactor>0.0,PetscObjectComm((PetscObject)rg),PETSC_ERR_ARG_OUTOFRANGE,"Illegal value of scaling factor. Must be > 0");
  PetscCheck(!rg->osfactor,PetscObjectComm((PetscObject)rg),PETSC_ERR_SUP,"Current implementation does not allow pushing several scaling factors");
  rg->osfactor = rg->sfactor;
  rg->sfactor *= sfactor;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
   RGPopScale - Pops the scaling factor set with RGPushScale().

   Logically Collective

   Input Parameter:
.  rg - the region context

   Level: developer

.seealso: RGPushScale()
@*/
PetscErrorCode RGPopScale(RG rg)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(rg,RG_CLASSID,1);
  PetscCheck(rg->osfactor,PetscObjectComm((PetscObject)rg),PETSC_ERR_ORDER,"Must call RGPushScale first");
  rg->sfactor  = rg->osfactor;
  rg->osfactor = 0.0;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGDestroy - Destroys RG context that was created with RGCreate().

   Collective

   Input Parameter:
.  rg - the region context

   Level: beginner

.seealso: RGCreate()
@*/
PetscErrorCode RGDestroy(RG *rg)
{
  PetscFunctionBegin;
  if (!*rg) PetscFunctionReturn(PETSC_SUCCESS);
  PetscValidHeaderSpecific(*rg,RG_CLASSID,1);
  if (--((PetscObject)*rg)->refct > 0) { *rg = NULL; PetscFunctionReturn(PETSC_SUCCESS); }
  PetscTryTypeMethod(*rg,destroy);
  PetscCall(PetscHeaderDestroy(rg));
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
   RGRegister - Adds a region to the RG package.

   Not Collective

   Input Parameters:
+  name - name of a new user-defined RG
-  function - routine to create context

   Notes:
   RGRegister() may be called multiple times to add several user-defined regions.

   Level: advanced

.seealso: RGRegisterAll()
@*/
PetscErrorCode RGRegister(const char *name,PetscErrorCode (*function)(RG))
{
  PetscFunctionBegin;
  PetscCall(RGInitializePackage());
  PetscCall(PetscFunctionListAdd(&RGList,name,function));
  PetscFunctionReturn(PETSC_SUCCESS);
}
