#pragma once

#include <petscmat.h>    /*I      "petscmat.h"          I*/
#include <petscdmplex.h> /*I      "petscdmplex.h"    I*/
#include <petscdmplextransform.h>
#include <petscbt.h>
#include <petscsf.h>
#include <petsc/private/dmimpl.h>

#if defined(PETSC_HAVE_EXODUSII)
  #include <exodusII.h>
#endif

PETSC_EXTERN PetscBool  Plexcite;
PETSC_EXTERN const char PlexCitation[];

PETSC_EXTERN PetscLogEvent DMPLEX_Interpolate;
PETSC_EXTERN PetscLogEvent DMPLEX_Partition;
PETSC_EXTERN PetscLogEvent DMPLEX_PartSelf;
PETSC_EXTERN PetscLogEvent DMPLEX_PartLabelInvert;
PETSC_EXTERN PetscLogEvent DMPLEX_PartLabelCreateSF;
PETSC_EXTERN PetscLogEvent DMPLEX_PartStratSF;
PETSC_EXTERN PetscLogEvent DMPLEX_CreatePointSF;
PETSC_EXTERN PetscLogEvent DMPLEX_Distribute;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributeCones;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributeLabels;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributeSF;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributeOverlap;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributeField;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributeData;
PETSC_EXTERN PetscLogEvent DMPLEX_Migrate;
PETSC_EXTERN PetscLogEvent DMPLEX_InterpolateSF;
PETSC_EXTERN PetscLogEvent DMPLEX_GlobalToNaturalBegin;
PETSC_EXTERN PetscLogEvent DMPLEX_GlobalToNaturalEnd;
PETSC_EXTERN PetscLogEvent DMPLEX_NaturalToGlobalBegin;
PETSC_EXTERN PetscLogEvent DMPLEX_NaturalToGlobalEnd;
PETSC_EXTERN PetscLogEvent DMPLEX_Stratify;
PETSC_EXTERN PetscLogEvent DMPLEX_Symmetrize;
PETSC_EXTERN PetscLogEvent DMPLEX_Preallocate;
PETSC_EXTERN PetscLogEvent DMPLEX_ResidualFEM;
PETSC_EXTERN PetscLogEvent DMPLEX_JacobianFEM;
PETSC_EXTERN PetscLogEvent DMPLEX_InterpolatorFEM;
PETSC_EXTERN PetscLogEvent DMPLEX_InjectorFEM;
PETSC_EXTERN PetscLogEvent DMPLEX_IntegralFEM;
PETSC_EXTERN PetscLogEvent DMPLEX_CreateGmsh;
PETSC_EXTERN PetscLogEvent DMPLEX_CreateBoxSFC;
PETSC_EXTERN PetscLogEvent DMPLEX_RebalanceSharedPoints;
PETSC_EXTERN PetscLogEvent DMPLEX_CreateFromFile;
PETSC_EXTERN PetscLogEvent DMPLEX_CreateFromOptions;
PETSC_EXTERN PetscLogEvent DMPLEX_BuildFromCellList;
PETSC_EXTERN PetscLogEvent DMPLEX_BuildCoordinatesFromCellList;
PETSC_EXTERN PetscLogEvent DMPLEX_LocatePoints;
PETSC_EXTERN PetscLogEvent DMPLEX_TopologyView;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributionView;
PETSC_EXTERN PetscLogEvent DMPLEX_LabelsView;
PETSC_EXTERN PetscLogEvent DMPLEX_CoordinatesView;
PETSC_EXTERN PetscLogEvent DMPLEX_SectionView;
PETSC_EXTERN PetscLogEvent DMPLEX_GlobalVectorView;
PETSC_EXTERN PetscLogEvent DMPLEX_LocalVectorView;
PETSC_EXTERN PetscLogEvent DMPLEX_TopologyLoad;
PETSC_EXTERN PetscLogEvent DMPLEX_DistributionLoad;
PETSC_EXTERN PetscLogEvent DMPLEX_LabelsLoad;
PETSC_EXTERN PetscLogEvent DMPLEX_CoordinatesLoad;
PETSC_EXTERN PetscLogEvent DMPLEX_SectionLoad;
PETSC_EXTERN PetscLogEvent DMPLEX_GlobalVectorLoad;
PETSC_EXTERN PetscLogEvent DMPLEX_LocalVectorLoad;
PETSC_EXTERN PetscLogEvent DMPLEX_MetricEnforceSPD;
PETSC_EXTERN PetscLogEvent DMPLEX_MetricNormalize;
PETSC_EXTERN PetscLogEvent DMPLEX_MetricAverage;
PETSC_EXTERN PetscLogEvent DMPLEX_MetricIntersection;
PETSC_EXTERN PetscLogEvent DMPLEX_Generate;
PETSC_EXTERN PetscLogEvent DMPLEX_Transform;
PETSC_EXTERN PetscLogEvent DMPLEX_GetLocalOffsets;

PETSC_EXTERN PetscLogEvent DMPLEX_RebalBuildGraph;
PETSC_EXTERN PetscLogEvent DMPLEX_RebalRewriteSF;
PETSC_EXTERN PetscLogEvent DMPLEX_RebalGatherGraph;
PETSC_EXTERN PetscLogEvent DMPLEX_RebalPartition;
PETSC_EXTERN PetscLogEvent DMPLEX_RebalScatterPart;
PETSC_EXTERN PetscLogEvent DMPLEX_Uninterpolate;

/* Utility struct to store the contents of a Fluent file in memory */
typedef struct {
  int          index; /* Type of section */
  unsigned int zoneID;
  unsigned int first;
  unsigned int last;
  int          type;
  int          nd; /* Either ND or element-type */
  void        *data;
} FluentSection;

struct _n_PetscGridHash {
  PetscInt     dim;
  PetscReal    lower[3];    /* The lower-left corner */
  PetscReal    upper[3];    /* The upper-right corner */
  PetscReal    extent[3];   /* The box size */
  PetscReal    h[3];        /* The subbox size */
  PetscInt     n[3];        /* The number of subboxes */
  PetscSection cellSection; /* Offsets for cells in each subbox*/
  IS           cells;       /* List of cells in each subbox */
  DMLabel      cellsSparse; /* Sparse storage for cell map */
};

typedef struct {
  PetscBool isotropic;               /* Is the metric isotropic? */
  PetscBool uniform;                 /* Is the metric uniform? */
  PetscBool restrictAnisotropyFirst; /* Should anisotropy or normalization come first? */
  PetscBool noInsert;                /* Should node insertion/deletion be turned off? */
  PetscBool noSwap;                  /* Should facet swapping be turned off? */
  PetscBool noMove;                  /* Should node movement be turned off? */
  PetscBool noSurf;                  /* Should surface modification be turned off? */
  PetscReal h_min, h_max;            /* Minimum/maximum tolerated metric magnitudes */
  PetscReal a_max;                   /* Maximum tolerated anisotropy */
  PetscReal targetComplexity;        /* Target metric complexity */
  PetscReal p;                       /* Degree for L-p normalization methods */
  PetscReal gradationFactor;         /* Maximum tolerated length ratio for adjacent edges */
  PetscReal hausdorffNumber;         /* Max. distance between piecewise linear representation of boundary and reconstructed ideal boundary */
  PetscInt  numIter;                 /* Number of ParMmg mesh adaptation iterations */
  PetscInt  verbosity;               /* Level of verbosity for remesher (-1 = no output, 10 = maximum) */
} DMPlexMetricCtx;

/* Point Numbering in Plex:

   Points are numbered contiguously by stratum. Strate are organized as follows:

   First Stratum:  Cells [height 0]
   Second Stratum: Vertices [depth 0]
   Third Stratum:  Faces [height 1]
   Fourth Stratum: Edges [depth 1]

   We do this so that the numbering of a cell-vertex mesh does not change after interpolation. Within a given stratum,
   we allow additional segregation of by cell type.
*/
typedef struct {
  PetscInt refct;

  PetscSection coneSection;      /* Layout of cones (inedges for DAG) */
  PetscInt    *cones;            /* Cone for each point */
  PetscInt    *coneOrientations; /* Orientation of each cone point, means cone traversal should start on point 'o', and if negative start on -(o+1) and go in reverse */
  PetscSection supportSection;   /* Layout of cones (inedges for DAG) */
  PetscInt    *supports;         /* Cone for each point */

  struct {                  // DMPolytopeType is an enum (usually size 4), but this needs frequent access
    uint8_t value_as_uint8; // in a struct to guard for stronger typing
  } *cellTypes;

  /* Transformation */
  DMPlexTransform tr;                                               /* Type of transform used to define an ephemeral mesh */
  char           *transformType;                                    /* Type of transform for uniform cell refinement */
  PetscBool       refinementUniform;                                /* Flag for uniform cell refinement */
  PetscReal       refinementLimit;                                  /* Maximum volume for refined cell */
  PetscErrorCode (*refinementFunc)(const PetscReal[], PetscReal *); /* Function giving the maximum volume for refined cell */

  /* Interpolation */
  DMPlexInterpolatedFlag interpolated;
  DMPlexInterpolatedFlag interpolatedCollective;
  PetscBool              interpolatePreferTensor; // When different orderings exist, prefer the tensor order

  /* Ordering */
  DMReorderDefaultFlag reorderDefault; /* Reorder the DM by default */

  /* Distribution */
  PetscBool distDefault;      /* Distribute the DM by default */
  PetscInt  overlap;          /* Overlap of the partitions as passed to DMPlexDistribute() or DMPlexDistributeOverlap() */
  PetscInt  numOvLabels;      /* The number of labels used for candidate overlap points */
  DMLabel   ovLabels[16];     /* Labels used for candidate overlap points */
  PetscInt  ovValues[16];     /* Label values used for candidate overlap points */
  PetscInt  numOvExLabels;    /* The number of labels used for exclusion */
  DMLabel   ovExLabels[16];   /* Labels used to exclude points from the overlap */
  PetscInt  ovExValues[16];   /* Label values used to exclude points from the overlap */
  char     *distributionName; /* Name of the specific parallel distribution of the DM */
  MPI_Comm  nonempty_comm;    /* Communicator used for visualization when some processes do not have cells */

  /* Hierarchy */
  PetscBool regularRefinement; /* This flag signals that we are a regular refinement of coarseMesh */

  /* Generation */
  char            *tetgenOpts;
  char            *triangleOpts;
  PetscPartitioner partitioner;
  PetscBool        partitionBalance; /* Evenly divide partition overlap when distributing */
  PetscBool        remeshBd;

  /* Submesh */
  DMLabel          subpointMap;   /* Label each original mesh point in the submesh with its depth, subpoint are the implicit numbering */
  IS               subpointIS;    /* IS holding point number in the enclosing mesh of every point in the submesh chart */
  PetscObjectState subpointState; /* The state of subpointMap when the subpointIS was last created */

  /* Labels and numbering */
  PetscObjectState depthState;    /* State of depth label, so that we can determine if a user changes it */
  PetscObjectState celltypeState; /* State of celltype label, so that we can determine if a user changes it */
  IS               globalVertexNumbers;
  IS               globalCellNumbers;

  /* Constraints */
  PetscSection anchorSection;          /* maps constrained points to anchor points */
  IS           anchorIS;               /* anchors indexed by the above section */
  PetscErrorCode (*createanchors)(DM); /* automatically compute anchors (probably from tree constraints) */
  PetscErrorCode (*computeanchormatrix)(DM, PetscSection, PetscSection, Mat);

  /* Tree: automatically construct constraints for hierarchically non-conforming meshes */
  PetscSection parentSection; /* dof == 1 if point has parent */
  PetscInt    *parents;       /* point to parent */
  PetscInt    *childIDs;      /* point to child ID */
  PetscSection childSection;  /* inverse of parent section */
  PetscInt    *children;      /* point to children */
  DM           referenceTree; /* reference tree to which child ID's refer */
  PetscErrorCode (*getchildsymmetry)(DM, PetscInt, PetscInt, PetscInt, PetscInt, PetscInt, PetscInt *, PetscInt *);

  /* MATIS support */
  PetscSection subdomainSection;

  /* Adjacency */
  PetscBool useAnchors;                                                          /* Replace constrained points with their anchors in adjacency lists */
  PetscErrorCode (*useradjacency)(DM, PetscInt, PetscInt *, PetscInt[], void *); /* User callback for adjacency */
  void *useradjacencyctx;                                                        /* User context for callback */

  // Periodicity
  struct {
    // Specified by the user
    PetscInt num_face_sfs;          // number of face_sfs
    PetscSF *face_sfs;              // root(donor faces) <-- leaf(local faces)
    PetscScalar (*transform)[4][4]; // geometric transform
    // Created eagerly (depends on points)
    PetscSF composed_sf; // root(non-periodic global points) <-- leaf(local points)
    IS     *periodic_points;
  } periodic;

  /* Projection */
  PetscInt maxProjectionHeight; /* maximum height of cells used in DMPlexProject functions */
  PetscInt activePoint;         /* current active point in iteration */

  /* Output */
  PetscInt  vtkCellHeight;          /* The height of cells for output, default is 0 */
  PetscReal scale[NUM_PETSC_UNITS]; /* The scale for each SI unit */

  /* Geometry */
  PetscReal     minradius;                        /* Minimum distance from cell centroid to face */
  PetscBool     useHashLocation;                  /* Use grid hashing for point location */
  PetscGridHash lbox;                             /* Local box for searching */
  void (*coordFunc)(PetscInt, PetscInt, PetscInt, /* Function used to remap newly introduced vertices */
                    const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);

  /* Neighbors */
  PetscMPIInt *neighbors;

  /* Metric */
  DMPlexMetricCtx *metricCtx;

  /* FEM */
  PetscBool useCeed;      /* This should convert to a registration system when there are more FEM backends */
  PetscBool useMatClPerm; /* Use the closure permutation when assembling matrices */

  /* CAD */
  PetscBool ignoreModel; /* If TRUE, Plex refinement will skip Snap-To-Geometry feature ignoring attached CAD geometry information */

  /* Debugging */
  PetscBool printSetValues;
  PetscInt  printAdj;
  PetscInt  printFEM;
  PetscInt  printFVM;
  PetscInt  printL2;
  PetscInt  printLocate;
  PetscInt  printProject;
  PetscReal printTol;
} DM_Plex;

PETSC_INTERN PetscErrorCode DMPlexCopy_Internal(DM, PetscBool, PetscBool, DM);
PETSC_INTERN PetscErrorCode DMPlexReplace_Internal(DM, DM *);
PETSC_INTERN PetscErrorCode DMPlexCopyEGADSInfo_Internal(DM, DM);

PETSC_EXTERN PetscErrorCode DMPlexVTKWriteAll_VTU(DM, PetscViewer);
PETSC_EXTERN PetscErrorCode VecView_Plex_Local(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecView_Plex_Native(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecView_Plex(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecLoad_Plex_Local(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecLoad_Plex_Native(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecLoad_Plex(Vec, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexGetFieldType_Internal(DM, PetscSection, PetscInt, PetscInt *, PetscInt *, PetscViewerVTKFieldType *);
PETSC_INTERN PetscErrorCode DMPlexGetFieldTypes_Internal(DM, PetscSection, PetscInt, PetscInt *, PetscInt **, PetscInt **, PetscViewerVTKFieldType **);
PETSC_INTERN PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM, PetscSection, PetscInt, PetscInt *, PetscInt **, PetscInt **, PetscViewerVTKFieldType **);
PETSC_INTERN PetscErrorCode DMPlexView_GLVis(DM, PetscViewer);
PETSC_INTERN PetscErrorCode DMSetUpGLVisViewer_Plex(PetscObject, PetscViewer);
#if defined(PETSC_HAVE_HDF5)
PETSC_EXTERN PetscErrorCode VecView_Plex_Local_HDF5(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecView_Plex_HDF5(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecLoad_Plex_HDF5(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecView_Plex_HDF5_Native(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode VecLoad_Plex_HDF5_Native(Vec, PetscViewer);
PETSC_EXTERN PetscErrorCode DMPlexView_HDF5(DM, PetscViewer);
PETSC_EXTERN PetscErrorCode DMPlexLoad_HDF5(DM, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexTopologyView_HDF5_Internal(DM, IS, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexCoordinatesView_HDF5_Internal(DM, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexLabelsView_HDF5_Internal(DM, IS, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexSectionView_HDF5_Internal(DM, PetscViewer, DM);
PETSC_INTERN PetscErrorCode DMPlexGlobalVectorView_HDF5_Internal(DM, PetscViewer, DM, Vec);
PETSC_INTERN PetscErrorCode DMPlexLocalVectorView_HDF5_Internal(DM, PetscViewer, DM, Vec);
PETSC_INTERN PetscErrorCode DMPlexTopologyLoad_HDF5_Internal(DM, PetscViewer, PetscSF *);
PETSC_INTERN PetscErrorCode DMPlexCoordinatesLoad_HDF5_Internal(DM, PetscViewer, PetscSF);
PETSC_INTERN PetscErrorCode DMPlexLabelsLoad_HDF5_Internal(DM, PetscViewer, PetscSF);
PETSC_INTERN PetscErrorCode DMPlexSectionLoad_HDF5_Internal(DM, PetscViewer, DM, PetscSF, PetscSF *, PetscSF *);
PETSC_INTERN PetscErrorCode DMPlexVecLoad_HDF5_Internal(DM, PetscViewer, DM, PetscSF, Vec);
PETSC_INTERN PetscErrorCode DMPlexView_HDF5_Internal(DM, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexLoad_HDF5_Internal(DM, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexLoad_HDF5_Xdmf_Internal(DM, PetscViewer);
PETSC_INTERN PetscErrorCode VecView_Plex_HDF5_Internal(Vec, PetscViewer);
PETSC_INTERN PetscErrorCode VecView_Plex_HDF5_Native_Internal(Vec, PetscViewer);
PETSC_INTERN PetscErrorCode VecView_Plex_Local_HDF5_Internal(Vec, PetscViewer);
PETSC_INTERN PetscErrorCode VecLoad_Plex_HDF5_Internal(Vec, PetscViewer);
PETSC_INTERN PetscErrorCode VecLoad_Plex_HDF5_Native_Internal(Vec, PetscViewer);
#endif
PETSC_EXTERN PetscErrorCode VecView_Plex_Local_CGNS(Vec, PetscViewer);
#if defined(PETSC_HAVE_CGNS)
PETSC_EXTERN PetscErrorCode VecLoad_Plex_CGNS_Internal(Vec, PetscViewer);
#endif

PETSC_INTERN PetscErrorCode DMPlexVecGetClosure_Internal(DM, PetscSection, PetscBool, Vec, PetscInt, PetscInt *, PetscScalar *[]);
PETSC_INTERN PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM, PetscSection, Vec, PetscInt, PetscInt, PetscInt *, PetscScalar *[]);
PETSC_INTERN PetscErrorCode DMPlexClosurePoints_Private(DM, PetscInt, const PetscInt[], IS *);
PETSC_INTERN PetscErrorCode DMSetFromOptions_NonRefinement_Plex(DM, PetscOptionItems);
PETSC_INTERN PetscErrorCode DMSetFromOptions_Overlap_Plex(DM, PetscOptionItems, PetscInt *);
PETSC_INTERN PetscErrorCode DMCoarsen_Plex(DM, MPI_Comm, DM *);
PETSC_INTERN PetscErrorCode DMCoarsenHierarchy_Plex(DM, PetscInt, DM[]);
PETSC_INTERN PetscErrorCode DMRefine_Plex(DM, MPI_Comm, DM *);
PETSC_INTERN PetscErrorCode DMRefineHierarchy_Plex(DM, PetscInt, DM[]);
PETSC_INTERN PetscErrorCode DMAdaptLabel_Plex(DM, Vec, DMLabel, DMLabel, DM *);
PETSC_INTERN PetscErrorCode DMExtrude_Plex(DM, PetscInt, DM *);
PETSC_INTERN PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec);
PETSC_INTERN PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec);
PETSC_INTERN PetscErrorCode DMProjectFunctionLocal_Plex(DM, PetscReal, PetscErrorCode (**)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **, InsertMode, Vec);
PETSC_INTERN PetscErrorCode DMProjectFunctionLabelLocal_Plex(DM, PetscReal, DMLabel, PetscInt, const PetscInt[], PetscInt, const PetscInt[], PetscErrorCode (**)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **, InsertMode, Vec);
PETSC_INTERN PetscErrorCode DMProjectFieldLocal_Plex(DM, PetscReal, Vec, void (**)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), InsertMode, Vec);
PETSC_INTERN PetscErrorCode DMProjectFieldLabelLocal_Plex(DM, PetscReal, DMLabel, PetscInt, const PetscInt[], PetscInt, const PetscInt[], Vec, void (**)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), InsertMode, Vec);
PETSC_INTERN PetscErrorCode DMProjectBdFieldLabelLocal_Plex(DM, PetscReal, DMLabel, PetscInt, const PetscInt[], PetscInt, const PetscInt[], Vec, void (**)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), InsertMode, Vec);
PETSC_INTERN PetscErrorCode DMComputeL2Diff_Plex(DM, PetscReal, PetscErrorCode (**)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **, Vec, PetscReal *);
PETSC_INTERN PetscErrorCode DMComputeL2GradientDiff_Plex(DM, PetscReal, PetscErrorCode (**)(PetscInt, PetscReal, const PetscReal[], const PetscReal[], PetscInt, PetscScalar *, void *), void **, Vec, const PetscReal[], PetscReal *);
PETSC_INTERN PetscErrorCode DMComputeL2FieldDiff_Plex(DM, PetscReal, PetscErrorCode (**)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **, Vec, PetscReal *);
PETSC_INTERN PetscErrorCode DMLocatePoints_Plex(DM, Vec, DMPointLocationType, PetscSF);

#if defined(PETSC_HAVE_EXODUSII)
PETSC_INTERN PetscErrorCode DMView_PlexExodusII(DM, PetscViewer);
PETSC_INTERN PetscErrorCode VecView_PlexExodusII_Internal(Vec, PetscViewer);
PETSC_INTERN PetscErrorCode VecLoad_PlexExodusII_Internal(Vec, PetscViewer);
#endif
PETSC_INTERN PetscErrorCode DMView_PlexCGNS(DM, PetscViewer);
PETSC_INTERN PetscErrorCode DMPlexCreateCGNSFromFile_Internal(MPI_Comm, const char *, PetscBool, DM *);
PETSC_INTERN PetscErrorCode DMPlexCreateCGNS_Internal_Serial(MPI_Comm, PetscInt, PetscBool, DM *);
PETSC_INTERN PetscErrorCode DMPlexCreateCGNS_Internal_Parallel(MPI_Comm, PetscInt, PetscBool, DM *);
PETSC_INTERN PetscErrorCode DMPlexVTKGetCellType_Internal(DM, PetscInt, PetscInt, PetscInt *);
PETSC_INTERN PetscErrorCode DMPlexGetAdjacency_Internal(DM, PetscInt, PetscBool, PetscBool, PetscBool, PetscInt *, PetscInt *[]);
PETSC_INTERN PetscErrorCode DMPlexGetRawFaces_Internal(DM, DMPolytopeType, const PetscInt[], PetscInt *, const DMPolytopeType *[], const PetscInt *[], const PetscInt *[]);
PETSC_INTERN PetscErrorCode DMPlexRestoreRawFaces_Internal(DM, DMPolytopeType, const PetscInt[], PetscInt *, const DMPolytopeType *[], const PetscInt *[], const PetscInt *[]);
PETSC_INTERN PetscErrorCode DMPlexComputeCellType_Internal(DM, PetscInt, PetscInt, DMPolytopeType *);
PETSC_INTERN PetscErrorCode DMPlexVecSetFieldClosure_Internal(DM, PetscSection, Vec, PetscBool[], PetscInt, PetscInt, const PetscInt[], DMLabel, PetscInt, const PetscScalar[], InsertMode);
PETSC_INTERN PetscErrorCode DMPlexProjectConstraints_Internal(DM, Vec, Vec);
PETSC_EXTERN PetscErrorCode DMPlexCreateReferenceTree_SetTree(DM, PetscSection, PetscInt[], PetscInt[]);
PETSC_EXTERN PetscErrorCode DMPlexCreateReferenceTree_Union(DM, DM, const char *, DM *);
PETSC_EXTERN PetscErrorCode DMPlexComputeInterpolatorTree(DM, DM, PetscSF, PetscInt *, Mat);
PETSC_EXTERN PetscErrorCode DMPlexComputeInjectorTree(DM, DM, PetscSF, PetscInt *, Mat);
PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat(DM, PetscSection, PetscInt, PetscInt, const PetscInt[], const PetscInt ***, const PetscScalar[], PetscInt *, PetscInt *, PetscInt *[], PetscScalar *[], PetscInt[], PetscBool);
PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat_Internal(DM, PetscSection, PetscInt, PetscInt, const PetscInt[], const PetscInt ***, PetscInt, PetscInt, const PetscScalar[], PetscInt *, PetscInt *, PetscInt *[], PetscScalar *[], PetscInt[], PetscBool, PetscBool);
PETSC_INTERN PetscErrorCode DMPlexAnchorsGetSubMatModification(DM, PetscSection, PetscInt, PetscInt, const PetscInt[], const PetscInt ***, PetscInt *, PetscInt *, PetscInt *[], PetscInt[], PetscScalar *[]);
PETSC_INTERN PetscErrorCode DMPlexLocatePoint_Internal(DM, PetscInt, const PetscScalar[], PetscInt, PetscInt *);
/* this is PETSC_EXTERN just because of src/dm/impls/plex/tests/ex18.c */
PETSC_EXTERN PetscErrorCode DMPlexOrientInterface_Internal(DM);
PETSC_INTERN PetscErrorCode DMPlexOrientCells_Internal(DM, IS, IS);

/* Applications may use this function */
PETSC_EXTERN PetscErrorCode DMPlexCreateNumbering_Plex(DM, PetscInt, PetscInt, PetscInt, PetscInt *, PetscSF, IS *);

PETSC_INTERN PetscErrorCode DMPlexInterpolateInPlace_Internal(DM);
PETSC_INTERN PetscErrorCode DMPlexCreateBoxMesh_Tensor_SFC_Internal(DM, PetscInt, const PetscInt[], const PetscReal[], const PetscReal[], const DMBoundaryType[], PetscBool);
PETSC_INTERN PetscErrorCode DMPlexMigrateIsoperiodicFaceSF_Internal(DM, DM, PetscSF);
PETSC_INTERN PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM, PetscBool, IS *);
PETSC_INTERN PetscErrorCode DMPlexRefine_Internal(DM, Vec, DMLabel, DMLabel, DM *);
PETSC_INTERN PetscErrorCode DMPlexCoarsen_Internal(DM, Vec, DMLabel, DMLabel, DM *);
PETSC_INTERN PetscErrorCode DMCreateMatrix_Plex(DM, Mat *);

PETSC_INTERN PetscErrorCode DMPlexGetOverlap_Plex(DM, PetscInt *);
PETSC_INTERN PetscErrorCode DMPlexSetOverlap_Plex(DM, DM, PetscInt);
PETSC_INTERN PetscErrorCode DMPlexDistributeGetDefault_Plex(DM, PetscBool *);
PETSC_INTERN PetscErrorCode DMPlexDistributeSetDefault_Plex(DM, PetscBool);
PETSC_INTERN PetscErrorCode DMPlexReorderGetDefault_Plex(DM, DMReorderDefaultFlag *);
PETSC_INTERN PetscErrorCode DMPlexReorderSetDefault_Plex(DM, DMReorderDefaultFlag);
PETSC_INTERN PetscErrorCode DMPlexGetUseCeed_Plex(DM, PetscBool *);
PETSC_INTERN PetscErrorCode DMPlexSetUseCeed_Plex(DM, PetscBool);
PETSC_INTERN PetscErrorCode DMReorderSectionGetDefault_Plex(DM, DMReorderDefaultFlag *);
PETSC_INTERN PetscErrorCode DMReorderSectionSetDefault_Plex(DM, DMReorderDefaultFlag);
PETSC_INTERN PetscErrorCode DMReorderSectionGetType_Plex(DM, MatOrderingType *);
PETSC_INTERN PetscErrorCode DMReorderSectionSetType_Plex(DM, MatOrderingType);

#if 1
static inline PetscInt DihedralInvert(PetscInt N, PetscInt a)
{
  return (a <= 0) ? a : (N - a);
}

static inline PetscInt DihedralCompose(PetscInt N, PetscInt a, PetscInt b)
{
  if (!N) return 0;
  return (a >= 0) ? ((b >= 0) ? ((a + b) % N) : -(((a - b - 1) % N) + 1)) : ((b >= 0) ? -(((N - b - a - 1) % N) + 1) : ((N + b - a) % N));
}

static inline PetscInt DihedralSwap(PetscInt N, PetscInt a, PetscInt b)
{
  return DihedralCompose(N, DihedralInvert(N, a), b);
}
#else
/* TODO
   This is a reimplementation of the tensor dihedral symmetries using the new orientations.
   These should be turned on when we convert to new-style orientations in p4est.
*/
/* invert dihedral symmetry: return a^-1,
 * using the representation described in
 * DMPlexGetConeOrientation() */
static inline PetscInt DihedralInvert(PetscInt N, PetscInt a)
{
  switch (N) {
  case 0:
    return 0;
  case 2:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_SEGMENT, 0, a);
  case 4:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_QUADRILATERAL, 0, a);
  case 8:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_HEXAHEDRON, 0, a);
  default:
    SETERRABORT(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid celltype for DihedralInvert()");
  }
  return 0;
}

/* compose dihedral symmetry: return b * a,
 * using the representation described in
 * DMPlexGetConeOrientation() */
static inline PetscInt DihedralCompose(PetscInt N, PetscInt a, PetscInt b)
{
  switch (N) {
  case 0:
    return 0;
  case 2:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_SEGMENT, b, a);
  case 4:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_QUADRILATERAL, b, a);
  case 8:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_HEXAHEDRON, b, a);
  default:
    SETERRABORT(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid celltype for DihedralCompose()");
  }
  return 0;
}

/* swap dihedral symmetries: return b * a^-1,
 * using the representation described in
 * DMPlexGetConeOrientation() */
static inline PetscInt DihedralSwap(PetscInt N, PetscInt a, PetscInt b)
{
  switch (N) {
  case 0:
    return 0;
  case 2:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_SEGMENT, b, a);
  case 4:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_QUADRILATERAL, b, a);
  case 8:
    return DMPolytopeTypeComposeOrientationInv(DM_POLYTOPE_HEXAHEDRON, b, a);
  default:
    SETERRABORT(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid celltype for DihedralCompose()");
  }
  return 0;
}
#endif
PETSC_EXTERN PetscInt       DMPolytopeConvertNewOrientation_Internal(DMPolytopeType, PetscInt);
PETSC_EXTERN PetscInt       DMPolytopeConvertOldOrientation_Internal(DMPolytopeType, PetscInt);
PETSC_EXTERN PetscErrorCode DMPlexConvertOldOrientations_Internal(DM);

PETSC_EXTERN PetscErrorCode DMPlexComputeIntegral_Internal(DM, Vec, PetscInt, PetscInt, PetscScalar *, void *);
PETSC_EXTERN PetscErrorCode DMPlexComputeResidual_Internal(DM, PetscFormKey, IS, PetscReal, Vec, Vec, PetscReal, Vec, void *);
PETSC_EXTERN PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM, PetscFormKey[], IS, PetscReal, Vec, Vec, PetscReal, Vec, void *);
PETSC_EXTERN PetscErrorCode DMPlexComputeJacobian_Internal(DM, PetscFormKey, IS, PetscReal, PetscReal, Vec, Vec, Mat, Mat, void *);
PETSC_EXTERN PetscErrorCode DMPlexComputeJacobian_Hybrid_Internal(DM, PetscFormKey[], IS, PetscReal, PetscReal, Vec, Vec, Mat, Mat, void *);
PETSC_EXTERN PetscErrorCode DMPlexComputeJacobian_Action_Internal(DM, PetscFormKey, IS, PetscReal, PetscReal, Vec, Vec, Vec, Vec, void *);
PETSC_EXTERN PetscErrorCode DMPlexReconstructGradients_Internal(DM, PetscFV, PetscInt, PetscInt, Vec, Vec, Vec, Vec);

/* Matvec with A in row-major storage, x and y can be aliased */
static inline void DMPlex_Mult2D_Internal(const PetscScalar A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[2] = {x[0 * ldx], x[1 * ldx]};
  y[0 * ldx]             = A[0] * z[0] + A[1] * z[1];
  y[1 * ldx]             = A[2] * z[0] + A[3] * z[1];
  (void)PetscLogFlops(6.0);
}
static inline void DMPlex_Mult3D_Internal(const PetscScalar A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[3] = {x[0 * ldx], x[1 * ldx], x[2 * ldx]};
  y[0 * ldx]             = A[0] * z[0] + A[1] * z[1] + A[2] * z[2];
  y[1 * ldx]             = A[3] * z[0] + A[4] * z[1] + A[5] * z[2];
  y[2 * ldx]             = A[6] * z[0] + A[7] * z[1] + A[8] * z[2];
  (void)PetscLogFlops(15.0);
}
static inline void DMPlex_MultTranspose2D_Internal(const PetscScalar A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[2] = {x[0 * ldx], x[1 * ldx]};
  y[0 * ldx]             = A[0] * z[0] + A[2] * z[1];
  y[1 * ldx]             = A[1] * z[0] + A[3] * z[1];
  (void)PetscLogFlops(6.0);
}
static inline void DMPlex_MultTranspose3D_Internal(const PetscScalar A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[3] = {x[0 * ldx], x[1 * ldx], x[2 * ldx]};
  y[0 * ldx]             = PetscConj(A[0]) * z[0] + PetscConj(A[3]) * z[1] + PetscConj(A[6]) * z[2];
  y[1 * ldx]             = PetscConj(A[1]) * z[0] + PetscConj(A[4]) * z[1] + PetscConj(A[7]) * z[2];
  y[2 * ldx]             = PetscConj(A[2]) * z[0] + PetscConj(A[5]) * z[1] + PetscConj(A[8]) * z[2];
  (void)PetscLogFlops(15.0);
}
static inline void DMPlex_Mult2DReal_Internal(const PetscReal A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[2] = {x[0 * ldx], x[1 * ldx]};
  y[0 * ldx]             = A[0] * z[0] + A[1] * z[1];
  y[1 * ldx]             = A[2] * z[0] + A[3] * z[1];
  (void)PetscLogFlops(6.0);
}
static inline void DMPlex_Mult3DReal_Internal(const PetscReal A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[3] = {x[0 * ldx], x[1 * ldx], x[2 * ldx]};
  y[0 * ldx]             = A[0] * z[0] + A[1] * z[1] + A[2] * z[2];
  y[1 * ldx]             = A[3] * z[0] + A[4] * z[1] + A[5] * z[2];
  y[2 * ldx]             = A[6] * z[0] + A[7] * z[1] + A[8] * z[2];
  (void)PetscLogFlops(15.0);
}
static inline void DMPlex_MultAdd2DReal_Internal(const PetscReal A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[2] = {x[0 * ldx], x[1 * ldx]};
  y[0 * ldx] += A[0] * z[0] + A[1] * z[1];
  y[1 * ldx] += A[2] * z[0] + A[3] * z[1];
  (void)PetscLogFlops(6.0);
}
static inline void DMPlex_MultAdd3DReal_Internal(const PetscReal A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[3] = {x[0 * ldx], x[1 * ldx], x[2 * ldx]};
  y[0 * ldx] += A[0] * z[0] + A[1] * z[1] + A[2] * z[2];
  y[1 * ldx] += A[3] * z[0] + A[4] * z[1] + A[5] * z[2];
  y[2 * ldx] += A[6] * z[0] + A[7] * z[1] + A[8] * z[2];
  (void)PetscLogFlops(15.0);
}
/*
  A: packed, row-major m x n array
  x: length m array
  y: length n arra
  ldx: the stride in x and y

  A[i,j] = A[i * n + j]
  A^T[j,i] = A[i,j]
*/
static inline void DMPlex_MultTransposeReal_Internal(const PetscReal A[], PetscInt m, PetscInt n, PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  PetscScalar z[3];
  PetscInt    i, j;
  for (i = 0; i < m; ++i) z[i] = x[i * ldx];
  for (j = 0; j < n; ++j) {
    const PetscInt l = j * ldx;
    y[l]             = 0;
    for (i = 0; i < m; ++i) y[l] += A[i * n + j] * z[i];
  }
  (void)PetscLogFlops(2 * m * n);
}
static inline void DMPlex_MultTranspose2DReal_Internal(const PetscReal A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[2] = {x[0 * ldx], x[1 * ldx]};
  y[0 * ldx]             = A[0] * z[0] + A[2] * z[1];
  y[1 * ldx]             = A[1] * z[0] + A[3] * z[1];
  (void)PetscLogFlops(6.0);
}
static inline void DMPlex_MultTranspose3DReal_Internal(const PetscReal A[], PetscInt ldx, const PetscScalar x[], PetscScalar y[])
{
  const PetscScalar z[3] = {x[0 * ldx], x[1 * ldx], x[2 * ldx]};
  y[0 * ldx]             = A[0] * z[0] + A[3] * z[1] + A[6] * z[2];
  y[1 * ldx]             = A[1] * z[0] + A[4] * z[1] + A[7] * z[2];
  y[2 * ldx]             = A[2] * z[0] + A[5] * z[1] + A[8] * z[2];
  (void)PetscLogFlops(15.0);
}

static inline void DMPlex_MatMult2D_Internal(const PetscScalar A[], PetscInt n, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
#define PLEX_DIM__ 2
  PetscScalar z[PLEX_DIM__];
  for (PetscInt j = 0; j < n; ++j) {
    for (int d = 0; d < PLEX_DIM__; ++d) z[d] = B[d * ldb + j];
    DMPlex_Mult2D_Internal(A, 1, z, z);
    for (int d = 0; d < PLEX_DIM__; ++d) C[d * ldb + j] = z[d];
  }
  (void)PetscLogFlops(8.0 * n);
#undef PLEX_DIM__
}
static inline void DMPlex_MatMult3D_Internal(const PetscScalar A[], PetscInt n, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
#define PLEX_DIM__ 3
  PetscScalar z[PLEX_DIM__];
  for (PetscInt j = 0; j < n; ++j) {
    for (int d = 0; d < PLEX_DIM__; ++d) z[d] = B[d * ldb + j];
    DMPlex_Mult3D_Internal(A, 1, z, z);
    for (int d = 0; d < PLEX_DIM__; ++d) C[d * ldb + j] = z[d];
  }
  (void)PetscLogFlops(8.0 * n);
#undef PLEX_DIM__
}
static inline void DMPlex_MatMultTranspose2D_Internal(const PetscScalar A[], PetscInt n, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
#define PLEX_DIM__ 2
  PetscScalar z[PLEX_DIM__];
  for (PetscInt j = 0; j < n; ++j) {
    for (int d = 0; d < PLEX_DIM__; ++d) z[d] = B[d * ldb + j];
    DMPlex_MultTranspose2D_Internal(A, 1, z, z);
    for (int d = 0; d < PLEX_DIM__; ++d) C[d * ldb + j] = z[d];
  }
  (void)PetscLogFlops(8.0 * n);
#undef PLEX_DIM__
}
static inline void DMPlex_MatMultTranspose3D_Internal(const PetscScalar A[], PetscInt n, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
#define PLEX_DIM__ 3
  PetscScalar z[PLEX_DIM__];
  for (PetscInt j = 0; j < n; ++j) {
    for (int d = 0; d < PLEX_DIM__; ++d) z[d] = B[d * ldb + j];
    DMPlex_MultTranspose3D_Internal(A, 1, z, z);
    for (int d = 0; d < PLEX_DIM__; ++d) C[d * ldb + j] = z[d];
  }
  (void)PetscLogFlops(8.0 * n);
#undef PLEX_DIM__
}

static inline void DMPlex_MatMultLeft2D_Internal(const PetscScalar A[], PetscInt m, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
  for (PetscInt j = 0; j < m; ++j) DMPlex_MultTranspose2D_Internal(A, 1, &B[j * ldb], &C[j * ldb]);
  (void)PetscLogFlops(8.0 * m);
}
static inline void DMPlex_MatMultLeft3D_Internal(const PetscScalar A[], PetscInt m, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
  for (PetscInt j = 0; j < m; ++j) DMPlex_MultTranspose3D_Internal(A, 1, &B[j * ldb], &C[j * ldb]);
  (void)PetscLogFlops(8.0 * m);
}
static inline void DMPlex_MatMultTransposeLeft2D_Internal(const PetscScalar A[], PetscInt m, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
  for (PetscInt j = 0; j < m; ++j) DMPlex_Mult2D_Internal(A, 1, &B[j * ldb], &C[j * ldb]);
  (void)PetscLogFlops(8.0 * m);
}
static inline void DMPlex_MatMultTransposeLeft3D_Internal(const PetscScalar A[], PetscInt m, PetscInt ldb, const PetscScalar B[], PetscScalar C[])
{
  for (PetscInt j = 0; j < m; ++j) DMPlex_Mult3D_Internal(A, 1, &B[j * ldb], &C[j * ldb]);
  (void)PetscLogFlops(8.0 * m);
}

static inline void DMPlex_PTAP2DReal_Internal(const PetscReal P[], const PetscScalar A[], PetscScalar C[])
{
  PetscScalar out[4];
  PetscInt    i, j, k, l;
  for (i = 0; i < 2; ++i) {
    for (j = 0; j < 2; ++j) {
      out[i * 2 + j] = 0.;
      for (k = 0; k < 2; ++k) {
        for (l = 0; l < 2; ++l) out[i * 2 + j] += P[k * 2 + i] * A[k * 2 + l] * P[l * 2 + j];
      }
    }
  }
  for (i = 0; i < 2 * 2; ++i) C[i] = out[i];
  (void)PetscLogFlops(48.0);
}
static inline void DMPlex_PTAP3DReal_Internal(const PetscReal P[], const PetscScalar A[], PetscScalar C[])
{
  PetscScalar out[9];
  PetscInt    i, j, k, l;
  for (i = 0; i < 3; ++i) {
    for (j = 0; j < 3; ++j) {
      out[i * 3 + j] = 0.;
      for (k = 0; k < 3; ++k) {
        for (l = 0; l < 3; ++l) out[i * 3 + j] += P[k * 3 + i] * A[k * 3 + l] * P[l * 3 + j];
      }
    }
  }
  for (i = 0; i < 2 * 2; ++i) C[i] = out[i];
  (void)PetscLogFlops(243.0);
}
/* TODO Fix for aliasing of A and C */
static inline void DMPlex_PTAPReal_Internal(const PetscReal P[], PetscInt m, PetscInt n, const PetscScalar A[], PetscScalar C[])
{
  PetscInt i, j, k, l;
  for (i = 0; i < n; ++i) {
    for (j = 0; j < n; ++j) {
      C[i * n + j] = 0.;
      for (k = 0; k < m; ++k) {
        for (l = 0; l < m; ++l) C[i * n + j] += P[k * n + i] * A[k * m + l] * P[l * n + j];
      }
    }
  }
  (void)PetscLogFlops(243.0);
}

static inline void DMPlex_Transpose2D_Internal(PetscScalar A[])
{
  PetscScalar tmp;
  tmp  = A[1];
  A[1] = A[2];
  A[2] = tmp;
}
static inline void DMPlex_Transpose3D_Internal(PetscScalar A[])
{
  PetscScalar tmp;
  tmp  = A[1];
  A[1] = A[3];
  A[3] = tmp;
  tmp  = A[2];
  A[2] = A[6];
  A[6] = tmp;
  tmp  = A[5];
  A[5] = A[7];
  A[7] = tmp;
}

static inline void DMPlex_Invert2D_Internal(PetscReal invJ[], PetscReal J[], PetscReal detJ)
{
  // Allow zero volume cells
  const PetscReal invDet = detJ == 0 ? 1.0 : (PetscReal)1.0 / detJ;

  invJ[0] = invDet * J[3];
  invJ[1] = -invDet * J[1];
  invJ[2] = -invDet * J[2];
  invJ[3] = invDet * J[0];
  (void)PetscLogFlops(5.0);
}

static inline void DMPlex_Invert3D_Internal(PetscReal invJ[], PetscReal J[], PetscReal detJ)
{
  // Allow zero volume cells
  const PetscReal invDet = detJ == 0 ? 1.0 : (PetscReal)1.0 / detJ;

  invJ[0 * 3 + 0] = invDet * (J[1 * 3 + 1] * J[2 * 3 + 2] - J[1 * 3 + 2] * J[2 * 3 + 1]);
  invJ[0 * 3 + 1] = invDet * (J[0 * 3 + 2] * J[2 * 3 + 1] - J[0 * 3 + 1] * J[2 * 3 + 2]);
  invJ[0 * 3 + 2] = invDet * (J[0 * 3 + 1] * J[1 * 3 + 2] - J[0 * 3 + 2] * J[1 * 3 + 1]);
  invJ[1 * 3 + 0] = invDet * (J[1 * 3 + 2] * J[2 * 3 + 0] - J[1 * 3 + 0] * J[2 * 3 + 2]);
  invJ[1 * 3 + 1] = invDet * (J[0 * 3 + 0] * J[2 * 3 + 2] - J[0 * 3 + 2] * J[2 * 3 + 0]);
  invJ[1 * 3 + 2] = invDet * (J[0 * 3 + 2] * J[1 * 3 + 0] - J[0 * 3 + 0] * J[1 * 3 + 2]);
  invJ[2 * 3 + 0] = invDet * (J[1 * 3 + 0] * J[2 * 3 + 1] - J[1 * 3 + 1] * J[2 * 3 + 0]);
  invJ[2 * 3 + 1] = invDet * (J[0 * 3 + 1] * J[2 * 3 + 0] - J[0 * 3 + 0] * J[2 * 3 + 1]);
  invJ[2 * 3 + 2] = invDet * (J[0 * 3 + 0] * J[1 * 3 + 1] - J[0 * 3 + 1] * J[1 * 3 + 0]);
  (void)PetscLogFlops(37.0);
}

static inline void DMPlex_Det2D_Internal(PetscReal *detJ, const PetscReal J[])
{
  *detJ = J[0] * J[3] - J[1] * J[2];
  (void)PetscLogFlops(3.0);
}

static inline void DMPlex_Det3D_Internal(PetscReal *detJ, const PetscReal J[])
{
  *detJ = (J[0 * 3 + 0] * (J[1 * 3 + 1] * J[2 * 3 + 2] - J[1 * 3 + 2] * J[2 * 3 + 1]) + J[0 * 3 + 1] * (J[1 * 3 + 2] * J[2 * 3 + 0] - J[1 * 3 + 0] * J[2 * 3 + 2]) + J[0 * 3 + 2] * (J[1 * 3 + 0] * J[2 * 3 + 1] - J[1 * 3 + 1] * J[2 * 3 + 0]));
  (void)PetscLogFlops(12.0);
}

static inline void DMPlex_Det2D_Scalar_Internal(PetscReal *detJ, const PetscScalar J[])
{
  *detJ = PetscRealPart(J[0]) * PetscRealPart(J[3]) - PetscRealPart(J[1]) * PetscRealPart(J[2]);
  (void)PetscLogFlops(3.0);
}

static inline void DMPlex_Det3D_Scalar_Internal(PetscReal *detJ, const PetscScalar J[])
{
  *detJ = (PetscRealPart(J[0 * 3 + 0]) * (PetscRealPart(J[1 * 3 + 1]) * PetscRealPart(J[2 * 3 + 2]) - PetscRealPart(J[1 * 3 + 2]) * PetscRealPart(J[2 * 3 + 1])) + PetscRealPart(J[0 * 3 + 1]) * (PetscRealPart(J[1 * 3 + 2]) * PetscRealPart(J[2 * 3 + 0]) - PetscRealPart(J[1 * 3 + 0]) * PetscRealPart(J[2 * 3 + 2])) + PetscRealPart(J[0 * 3 + 2]) * (PetscRealPart(J[1 * 3 + 0]) * PetscRealPart(J[2 * 3 + 1]) - PetscRealPart(J[1 * 3 + 1]) * PetscRealPart(J[2 * 3 + 0])));
  (void)PetscLogFlops(12.0);
}

static inline void DMPlex_WaxpyD_Internal(PetscInt dim, PetscReal a, const PetscReal *x, const PetscReal *y, PetscReal *w)
{
  PetscInt d;
  for (d = 0; d < dim; ++d) w[d] = a * x[d] + y[d];
}

static inline PetscReal DMPlex_DotD_Internal(PetscInt dim, const PetscScalar *x, const PetscReal *y)
{
  PetscReal sum = 0.0;
  PetscInt  d;
  for (d = 0; d < dim; ++d) sum += PetscRealPart(x[d]) * y[d];
  return sum;
}

static inline PetscReal DMPlex_DotRealD_Internal(PetscInt dim, const PetscReal *x, const PetscReal *y)
{
  PetscReal sum = 0.0;
  PetscInt  d;
  for (d = 0; d < dim; ++d) sum += x[d] * y[d];
  return sum;
}

static inline PetscReal DMPlex_NormD_Internal(PetscInt dim, const PetscReal *x)
{
  PetscReal sum = 0.0;
  PetscInt  d;
  for (d = 0; d < dim; ++d) sum += x[d] * x[d];
  return PetscSqrtReal(sum);
}

static inline PetscReal DMPlex_DistD_Internal(PetscInt dim, const PetscScalar *x, const PetscScalar *y)
{
  PetscReal sum = 0.0;
  PetscInt  d;
  for (d = 0; d < dim; ++d) sum += PetscRealPart(PetscConj(x[d] - y[d]) * (x[d] - y[d]));
  return PetscSqrtReal(sum);
}

static inline PetscReal DMPlex_DistRealD_Internal(PetscInt dim, const PetscReal *x, const PetscReal *y)
{
  PetscReal sum = 0.0;
  PetscInt  d;
  for (d = 0; d < dim; ++d) sum += (x[d] - y[d]) * (x[d] - y[d]);
  return PetscSqrtReal(sum);
}

PETSC_INTERN PetscErrorCode DMPlexGetPointDualSpaceFEM(DM, PetscInt, PetscInt, PetscDualSpace *);
PETSC_INTERN PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection, PetscBool, PetscInt, PetscInt, PetscInt *, PetscBool, const PetscInt[], const PetscInt[], PetscInt[]);
PETSC_INTERN PetscErrorCode DMPlexGetIndicesPointFields_Internal(PetscSection, PetscBool, PetscInt, PetscInt, PetscInt[], PetscBool, const PetscInt ***, PetscInt, const PetscInt[], PetscInt[]);
PETSC_INTERN PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM, PetscInt, PetscInt, PetscBool, PetscInt *, PetscInt *[]);
PETSC_INTERN PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM, PetscSection, PetscBool, Vec, PetscInt, PetscInt, PetscInt *, PetscScalar *[]);
PETSC_INTERN PetscErrorCode DMPlexMatSetClosure_Internal(DM, PetscSection, PetscSection, PetscBool, Mat, PetscInt, const PetscScalar[], InsertMode);

PETSC_EXTERN PetscErrorCode DMPlexGetAllCells_Internal(DM, IS *);
PETSC_EXTERN PetscErrorCode DMPlexGetAllFaces_Internal(DM, IS *);
PETSC_EXTERN PetscErrorCode DMSNESGetFEGeom(DMField, IS, PetscQuadrature, PetscFEGeomMode, PetscFEGeom **);
PETSC_EXTERN PetscErrorCode DMSNESRestoreFEGeom(DMField, IS, PetscQuadrature, PetscBool, PetscFEGeom **);
PETSC_EXTERN PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM, PetscSection, IS, PetscReal, Vec, Vec, Vec, void *);
PETSC_EXTERN PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM, PetscSection, PetscSection, IS, PetscReal, PetscReal, Vec, Vec, Mat, Mat, void *);
PETSC_INTERN PetscErrorCode DMCreateSubDomainDM_Plex(DM, DMLabel, PetscInt, IS *, DM *);
PETSC_INTERN PetscErrorCode DMPlexBasisTransformPoint_Internal(DM, DM, Vec, PetscInt, PetscBool[], PetscBool, PetscScalar *);
PETSC_EXTERN PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM, DM, Vec, PetscInt, PetscBool, PetscInt, PetscScalar *);
PETSC_INTERN PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM, const PetscReal[], PetscBool, PetscInt, const PetscReal *, PetscReal *, void *);
PETSC_INTERN PetscErrorCode DMPlexBasisTransformApply_Internal(DM, const PetscReal[], PetscBool, PetscInt, const PetscScalar *, PetscScalar *, void *);
PETSC_INTERN PetscErrorCode DMCreateNeumannOverlap_Plex(DM, IS *, Mat *, PetscErrorCode (**)(Mat, PetscReal, Vec, Vec, PetscReal, IS, void *), void **);
PETSC_INTERN PetscErrorCode DMPlexMarkBoundaryFaces_Internal(DM, PetscInt, PetscInt, DMLabel, PetscBool);
PETSC_INTERN PetscErrorCode DMPlexDistributeOverlap_Internal(DM, PetscInt, MPI_Comm, const char *, PetscSF *, DM *);

PETSC_INTERN PetscErrorCode DMPlexInterpolateFaces_Internal(DM, PetscInt, DM);

PETSC_INTERN PetscErrorCode DMPlexMarkSubmesh_Interpolated(DM, DMLabel, PetscInt, PetscBool, PetscBool, DMLabel, DM);

PETSC_INTERN PetscErrorCode DMPeriodicCoordinateSetUp_Internal(DM);

/* Functions in the vtable */
PETSC_INTERN PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling);
PETSC_INTERN PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat);
PETSC_INTERN PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mat);
PETSC_INTERN PetscErrorCode DMCreateMassMatrixLumped_Plex(DM, Vec *, Vec *);
PETSC_INTERN PetscErrorCode DMCreateLocalSection_Plex(DM dm);
PETSC_INTERN PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm);
PETSC_INTERN PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J);
PETSC_INTERN PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm);
PETSC_INTERN PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field);
PETSC_INTERN PetscErrorCode DMClone_Plex(DM dm, DM *newdm);
PETSC_INTERN PetscErrorCode DMSetUp_Plex(DM dm);
PETSC_INTERN PetscErrorCode DMDestroy_Plex(DM dm);
PETSC_INTERN PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer);
PETSC_INTERN PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer);
PETSC_INTERN PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm);
PETSC_INTERN PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm);
PETSC_INTERN PetscErrorCode DMCreateDomainDecompositionScatters_Plex(DM, PetscInt, DM *, VecScatter **, VecScatter **, VecScatter **);
PETSC_INTERN PetscErrorCode DMCreateDomainDecomposition_Plex(DM, PetscInt *, char ***, IS **, IS **, DM **);
PETSC_INTERN PetscErrorCode DMCreateSectionPermutation_Plex(DM dm, IS *permutation, PetscBT *blockStarts);

// Coordinate mapping functions
PETSC_INTERN void coordMap_identity(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
PETSC_INTERN void coordMap_shear(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
PETSC_INTERN void coordMap_flare(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
PETSC_INTERN void coordMap_annulus(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
PETSC_INTERN void coordMap_shell(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);
PETSC_INTERN void coordMap_sinusoid(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]);

PETSC_EXTERN PetscErrorCode DMSnapToGeomModel_EGADS(DM, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);
PETSC_EXTERN PetscErrorCode DMSnapToGeomModel_EGADSLite(DM, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);

// FIXME: DM with Attached CAD Models (STEP, IGES, BRep, EGADS, EGADSlite)
PETSC_EXTERN PetscErrorCode DMPlexSnapToGeomModel(DM, PetscInt, PetscInt, const PetscScalar[], PetscScalar[]);

// Coordinate <-> Reference mapping functions
PETSC_INTERN PetscErrorCode DMPlexCoordinatesToReference_FE(DM, PetscFE, PetscInt, PetscInt, const PetscReal[], PetscReal[], Vec, PetscInt, PetscInt, PetscInt, PetscReal *);
PETSC_INTERN PetscErrorCode DMPlexReferenceToCoordinates_FE(DM, PetscFE, PetscInt, PetscInt, const PetscReal[], PetscReal[], Vec, PetscInt, PetscInt);
