#ifndef PETSCDEVICE_H
#error "included this file before petscdevice.h, this file must be included last to ensure that public petsc headers are well formed"
#endif
#ifndef PETSCDEVICETESTCOMMON_H
#define PETSCDEVICETESTCOMMON_H

/* all of the error checking macros are undefined and redefined verbatim so that they are also
 * defined for optimized builds.
 */

#undef  PetscValidDeviceType
#define PetscValidDeviceType(_p_dev_type__,_p_arg__) do {                                      \
    PetscAssert(                                                                               \
      ((_p_dev_type__) >= PETSC_DEVICE_INVALID) && ((_p_dev_type__) <= PETSC_DEVICE_MAX),      \
      PETSC_COMM_SELF,PETSC_ERR_ARG_UNKNOWN_TYPE,"Unknown PetscDeviceType '%d': Argument #%d", \
      (_p_dev_type__),(_p_arg__)                                                               \
    );                                                                                         \
    if (PetscUnlikely(!PetscDeviceConfiguredFor_Internal(_p_dev_type__))) {                    \
      switch(_p_dev_type__) {                                                                  \
      case PETSC_DEVICE_INVALID:                                                               \
        SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"Invalid PetscDeviceType '%s': Argument #%d;"    \
                " PETSc is not configured with device support",                                \
                PetscDeviceTypes[_p_dev_type__],(_p_arg__));                                   \
        break;                                                                                 \
      case PETSC_DEVICE_MAX:                                                                   \
        SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,                                          \
                "Invalid PetscDeviceType '%s': Argument #%d",                                  \
                PetscDeviceTypes[_p_dev_type__],(_p_arg__));                                   \
        break;                                                                                 \
      default:                                                                                 \
        SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,                                                 \
                "Not configured for PetscDeviceType '%s': Argument #%d;"                       \
                " run configure --help %s for available options",                              \
                PetscDeviceTypes[_p_dev_type__],(_p_arg__),PetscDeviceTypes[_p_dev_type__]);   \
        break;                                                                                 \
      }                                                                                        \
    }                                                                                          \
  } while (0)

#undef  PetscValidDevice
#define PetscValidDevice(_p_dev__,_p_arg__)          do {                                      \
    PetscValidPointer(_p_dev__,_p_arg__);                                                      \
    PetscValidDeviceType((_p_dev__)->type,_p_arg__);                                           \
    PetscAssert(                                                                               \
      (_p_dev__)->id >= 0,PETSC_COMM_SELF,PETSC_ERR_PLIB,                                      \
      "Invalid PetscDevice: Argument #%d; id %" PetscInt_FMT " < 0",(_p_arg__),(_p_dev__)->id  \
    );                                                                                         \
    PetscAssert(                                                                               \
      (_p_dev__)->refcnt >= 0,PETSC_COMM_SELF,PETSC_ERR_PLIB,                                  \
      "Invalid PetscDevice: Argument #%d; negative reference count %" PetscInt_FMT,            \
      (_p_arg__),(_p_dev__)->refcnt                                                            \
    );                                                                                         \
  } while (0)

#undef  PetscCheckCompatibleDevices
#define PetscCheckCompatibleDevices(_p_dev1__,_p_arg1__,_p_dev2__,_p_arg2__) do {       \
    PetscValidDevice(_p_dev1__,_p_arg1__);                                              \
    PetscValidDevice(_p_dev2__,_p_arg2__);                                              \
    PetscAssert(                                                                        \
      (_p_dev1__)->type == (_p_dev2__)->type,PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,      \
      "PetscDevices are incompatible: Arguments #%d and #%d",(_p_arg1__),(_p_arg2__)    \
    );                                                                                  \
  } while (0)

#undef  PetscValidStreamType
#define PetscValidStreamType(_p_strm_type__,_p_arg__)  do {                                    \
    PetscAssert(                                                                               \
      ((_p_strm_type__) >= 0) && ((_p_strm_type__) <= PETSC_STREAM_MAX),                       \
      PETSC_COMM_SELF,PETSC_ERR_ARG_UNKNOWN_TYPE,"Unknown PetscStreamType '%d': Argument #%d", \
      (_p_strm_type__),(_p_arg__)                                                              \
    );                                                                                         \
    PetscAssert(                                                                               \
      (_p_strm_type__) != PETSC_STREAM_MAX,PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,               \
      "Invalid PetscStreamType '%s': Argument #%d",PetscStreamTypes[_p_strm_type__],(_p_arg__) \
    );                                                                                         \
  } while (0)

#undef  PetscValidDeviceContext
#define PetscValidDeviceContext(_p_dev_ctx__,_p_arg__) do {                                    \
    PetscValidPointer(_p_dev_ctx__,_p_arg__);                                                  \
    PetscValidStreamType((_p_dev_ctx__)->streamType,_p_arg__);                                 \
    if ((_p_dev_ctx__)->device) PetscValidDevice((_p_dev_ctx__)->device,_p_arg__);             \
    else PetscAssert(                                                                          \
      !((_p_dev_ctx__)->setup),PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,                       \
      "Invalid PetscDeviceContext: Argument #%d; "                                             \
      "PetscDeviceContext is setup but has no PetscDevice",(_p_arg__)                          \
    );                                                                                         \
    PetscAssert(                                                                               \
      (_p_dev_ctx__)->id >= 1,PETSC_COMM_SELF,PETSC_ERR_PLIB,                                  \
      "Invalid PetscDeviceContext: Argument #%d; id %" PetscInt_FMT " < 1",                    \
      (_p_arg__),(_p_dev_ctx__)->id                                                            \
    );                                                                                         \
    PetscAssert(                                                                               \
      (_p_dev_ctx__)->numChildren <= (_p_dev_ctx__)->maxNumChildren,PETSC_COMM_SELF,           \
      PETSC_ERR_ARG_CORRUPT,"Invalid PetscDeviceContext: Argument #%d; number of children %"   \
      PetscInt_FMT " > max number of children %" PetscInt_FMT,                                 \
      (_p_arg__),(_p_dev_ctx__)->numChildren,(_p_dev_ctx__)->maxNumChildren                    \
    );                                                                                         \
  } while (0)

#undef  PetscCheckCompatibleDeviceContexts
#define PetscCheckCompatibleDeviceContexts(_p_dev_ctx1__,_p_arg1__,_p_dev_ctx2__,_p_arg2__)    \
  do {                                                                                         \
    PetscValidDeviceContext(_p_dev_ctx1__,_p_arg1__);                                          \
    PetscValidDeviceContext(_p_dev_ctx2__,_p_arg2__);                                          \
    PetscCheckCompatibleDevices(                                                               \
      (_p_dev_ctx1__)->device,_p_arg1__,(_p_dev_ctx2__)->device,_p_arg2__                      \
    );                                                                                         \
  } while (0)

/*  This header file should NEVER #include another file and should be the last thing included
 *  in the test file. This is to guard against ill-formed PetscDevice header files!
 */
static inline PetscErrorCode AssertDeviceExists(PetscDevice device)
{
  PetscFunctionBegin;
  PetscValidDevice(device,1);
  PetscFunctionReturn(0);
}

static inline PetscErrorCode AssertDeviceDoesNotExist(PetscDevice device)
{
  PetscFunctionBegin;
  PetscAssert(!device,PETSC_COMM_SELF,PETSC_ERR_PLIB,"PetscDevice was not destroyed for type %s",PetscDeviceTypes[device->type]);
  PetscFunctionReturn(0);
}

static inline PetscErrorCode AssertDeviceContextExists(PetscDeviceContext dctx)
{
  PetscFunctionBegin;
  PetscValidDeviceContext(dctx,1);
  PetscFunctionReturn(0);
}

static inline PetscErrorCode AssertDeviceContextDoesNotExist(PetscDeviceContext dctx)
{
  PetscFunctionBegin;
  PetscAssert(!dctx,PETSC_COMM_SELF,PETSC_ERR_PLIB,"PetscDeviceContext was not destroyed");
  PetscFunctionReturn(0);
}

static inline PetscErrorCode AssertPetscStreamTypesValidAndEqual(PetscStreamType left, PetscStreamType right, const char *errStr)
{
  PetscFunctionBegin;
  PetscValidStreamType(left,1);
  PetscValidStreamType(right,2);
  PetscAssert(left == right,PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,errStr,PetscStreamTypes[left],PetscStreamTypes[right]);
  PetscFunctionReturn(0);
}

static inline PetscErrorCode AssertPetscDevicesValidAndEqual(PetscDevice left, PetscDevice right, const char *errStr)
{
  PetscFunctionBegin;
  PetscCheckCompatibleDevices(left,1,right,2);
  PetscAssert(left == right,PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"%s",errStr);
  PetscFunctionReturn(0);
}

static inline PetscErrorCode AssertPetscDeviceContextsValidAndEqual(PetscDeviceContext left, PetscDeviceContext right, const char *errStr)
{
  PetscFunctionBegin;
  PetscCheckCompatibleDeviceContexts(left,1,right,2);
  PetscAssert(left == right,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"%s",errStr);
  PetscFunctionReturn(0);
}
#endif /* PETSCDEVICETESTCOMMON_H */
