!
!
!   This program demonstrates use of MatShellSetOperation()
!
      subroutine mymatmult(A, x, y, ierr)
#include <petsc/finclude/petscmat.h>
        use petscmat
        implicit none

        Mat A
        Vec x, y
        PetscErrorCode ierr

        print *, 'Called MatMult'
      end

      subroutine mymatmultadd(A, x, y, z, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x, y, z
        PetscErrorCode ierr

        print *, 'Called MatMultAdd'
      end

      subroutine mymatmulttranspose(A, x, y, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x, y
        PetscErrorCode ierr

        print *, 'Called MatMultTranspose'
      end

      subroutine mymatmulthermitiantranspose(A, x, y, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x, y
        PetscErrorCode ierr

        print *, 'Called MatMultHermitianTranspose'
      end

      subroutine mymatmulttransposeadd(A, x, y, z, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x, y, z
        PetscErrorCode ierr

        print *, 'Called MatMultTransposeAdd'
      end

      subroutine mymatmulthermitiantransposeadd(A, x, y, z, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x, y, z
        PetscErrorCode ierr

        print *, 'Called MatMultHermitianTransposeAdd'
      end

      subroutine mymattranspose(A, reuse, B, ierr)
        use petscmat
        implicit none
        Mat A, B
        MatReuse reuse
        PetscErrorCode ierr
        PetscInt i12, i0

        i12 = 12
        i0 = 0
        PetscCallA(MatCreateShell(PETSC_COMM_SELF, i12, i12, i12, i12, i0, B, ierr))
        PetscCallA(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY, ierr))
        PetscCallA(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY, ierr))

        print *, 'Called MatTranspose'
      end

      subroutine mymatgetdiagonal(A, x, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x
        PetscErrorCode ierr

        print *, 'Called MatGetDiagonal'
      end

      subroutine mymatdiagonalscale(A, x, y, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x, y
        PetscErrorCode ierr

        print *, 'Called MatDiagonalScale'
      end

      subroutine mymatzeroentries(A, ierr)
        use petscmat
        implicit none
        Mat A
        PetscErrorCode ierr

        print *, 'Called MatZeroEntries'
      end

      subroutine mymataxpy(A, alpha, B, str, ierr)
        use petscmat
        implicit none
        Mat A, B
        PetscScalar alpha
        MatStructure str
        PetscErrorCode ierr

        print *, 'Called MatAXPY'
      end

      subroutine mymatshift(A, alpha, ierr)
        use petscmat
        implicit none
        Mat A
        PetscScalar alpha
        PetscErrorCode ierr

        print *, 'Called MatShift'
      end

      subroutine mymatdiagonalset(A, x, ins, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x
        InsertMode ins
        PetscErrorCode ierr

        print *, 'Called MatDiagonalSet'
      end

      subroutine mymatdestroy(A, ierr)
        use petscmat
        implicit none
        Mat A
        PetscErrorCode ierr

        print *, 'Called MatDestroy'
      end

      subroutine mymatview(A, viewer, ierr)
        use petscmat
        implicit none
        Mat A
        PetscViewer viewer
        PetscErrorCode ierr

        print *, 'Called MatView'
      end

      subroutine mymatgetvecs(A, x, y, ierr)
        use petscmat
        implicit none
        Mat A
        Vec x, y
        PetscErrorCode ierr

        print *, 'Called MatCreateVecs'
      end

      program main
        use petscmat
        implicit none

        Mat m, mt
        Vec x, y, z
        PetscScalar a
        PetscViewer viewer
        MatOperation op
        PetscErrorCode ierr
        PetscInt i12, i0
        external mymatmult
        external mymatmultadd
        external mymatmulttranspose
        external mymatmulthermitiantranspose
        external mymatmulttransposeadd
        external mymatmulthermitiantransposeadd
        external mymattranspose
        external mymatgetdiagonal
        external mymatdiagonalscale
        external mymatzeroentries
        external mymataxpy
        external mymatshift
        external mymatdiagonalset
        external mymatdestroy
        external mymatview
        external mymatgetvecs

        PetscCallA(PetscInitialize(ierr))

        viewer = PETSC_VIEWER_STDOUT_SELF
        i12 = 12
        i0 = 0
        PetscCallA(VecCreateSeq(PETSC_COMM_SELF, i12, x, ierr))
        PetscCallA(VecCreateSeq(PETSC_COMM_SELF, i12, y, ierr))
        PetscCallA(VecCreateSeq(PETSC_COMM_SELF, i12, z, ierr))
        PetscCallA(MatCreateShell(PETSC_COMM_SELF, i12, i12, i12, i12, i0, m, ierr))
        PetscCallA(MatShellSetManageScalingShifts(m, ierr))
        PetscCallA(MatAssemblyBegin(m, MAT_FINAL_ASSEMBLY, ierr))
        PetscCallA(MatAssemblyEnd(m, MAT_FINAL_ASSEMBLY, ierr))

        op = MATOP_MULT
        PetscCallA(MatShellSetOperation(m, op, mymatmult, ierr))
        op = MATOP_MULT_ADD
        PetscCallA(MatShellSetOperation(m, op, mymatmultadd, ierr))
        op = MATOP_MULT_TRANSPOSE
        PetscCallA(MatShellSetOperation(m, op, mymatmulttranspose, ierr))
        op = MATOP_MULT_HERMITIAN_TRANSPOSE
        PetscCallA(MatShellSetOperation(m, op, mymatmulthermitiantranspose, ierr))
        op = MATOP_MULT_TRANSPOSE_ADD
        PetscCallA(MatShellSetOperation(m, op, mymatmulttransposeadd, ierr))
        op = MATOP_MULT_HERMITIAN_TRANS_ADD
        PetscCallA(MatShellSetOperation(m, op, mymatmulthermitiantransposeadd, ierr))
        op = MATOP_TRANSPOSE
        PetscCallA(MatShellSetOperation(m, op, mymattranspose, ierr))
        op = MATOP_GET_DIAGONAL
        PetscCallA(MatShellSetOperation(m, op, mymatgetdiagonal, ierr))
        op = MATOP_DIAGONAL_SCALE
        PetscCallA(MatShellSetOperation(m, op, mymatdiagonalscale, ierr))
        op = MATOP_ZERO_ENTRIES
        PetscCallA(MatShellSetOperation(m, op, mymatzeroentries, ierr))
        op = MATOP_AXPY
        PetscCallA(MatShellSetOperation(m, op, mymataxpy, ierr))
        op = MATOP_SHIFT
        PetscCallA(MatShellSetOperation(m, op, mymatshift, ierr))
        op = MATOP_DIAGONAL_SET
        PetscCallA(MatShellSetOperation(m, op, mymatdiagonalset, ierr))
        op = MATOP_DESTROY
        PetscCallA(MatShellSetOperation(m, op, mymatdestroy, ierr))
        op = MATOP_VIEW
        PetscCallA(MatShellSetOperation(m, op, mymatview, ierr))
        op = MATOP_CREATE_VECS
        PetscCallA(MatShellSetOperation(m, op, mymatgetvecs, ierr))

        PetscCallA(MatMult(m, x, y, ierr))
        PetscCallA(MatMultAdd(m, x, y, z, ierr))
        PetscCallA(MatMultTranspose(m, x, y, ierr))
        PetscCallA(MatMultHermitianTranspose(m, x, y, ierr))
        PetscCallA(MatMultTransposeAdd(m, x, y, z, ierr))
        PetscCallA(MatMultHermitianTransposeAdd(m, x, y, z, ierr))
        PetscCallA(MatTranspose(m, MAT_INITIAL_MATRIX, mt, ierr))
        PetscCallA(MatGetDiagonal(m, x, ierr))
        PetscCallA(MatDiagonalScale(m, x, y, ierr))
        PetscCallA(MatZeroEntries(m, ierr))
        a = 102.
        PetscCallA(MatAXPY(m, a, mt, SAME_NONZERO_PATTERN, ierr))
        PetscCallA(MatShift(m, a, ierr))
        PetscCallA(MatDiagonalSet(m, x, INSERT_VALUES, ierr))
        PetscCallA(MatView(m, viewer, ierr))
        PetscCallA(MatCreateVecs(m, x, y, ierr))
        PetscCallA(MatDestroy(m, ierr))
        PetscCallA(MatDestroy(mt, ierr))
        PetscCallA(VecDestroy(x, ierr))
        PetscCallA(VecDestroy(y, ierr))
        PetscCallA(VecDestroy(z, ierr))

        PetscCallA(PetscFinalize(ierr))
      end

!/*TEST
!
!   testset:
!     args: -malloc_dump
!     filter: sort -b
!     filter_output: sort -b
!     test:
!       suffix: 1
!       requires: !complex
!     test:
!       suffix: 2
!       requires: complex
!
!TEST*/
