! Module  : utest_math_m
! Purpose : Testing routines for utest_math
!
! Copyright 2021-2022 Rich Townsend & The MSG Team
!
! This file is part of MSG. MSG 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, version 3.
!
! MSG 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 this program.  If not, see <http://www.gnu.org/licenses/>.

#:include 'forum.inc'

module utest_math_m

   ! Uses
   
   use forum_m

   use math_m

   use ISO_FORTRAN_ENV

   ! No implicit typing

   implicit none

   ! Access specifiers

   private

   public :: test


contains

   subroutine test()

      call test_integrate_con_()
      call test_integrate_lin_()

   end subroutine test

   !****

   subroutine test_integrate_con_()

      real(RD), allocatable :: x(:)
      real(RD), allocatable :: y(:)
      real(RD)              :: x_a
      real(RD)              :: x_b
      real(RD)              :: iy
      real(RD)              :: iy_chk

      integer :: i

      ! Iterate over test instances

      print *,'  integrate_con'

      do i = 1, 3
         call integrate_con_arrange_(i)
         call integrate_con_act_(i)
         call integrate_con_assert_(i)
      end do

      ! Finish

      return

   contains

      subroutine integrate_con_arrange_(i)

         integer, intent(in) :: i

         integer :: k

         x = [(k,k=1,10)]
         y = [(1._RD,k=1,9)]

         select case (i)
         case (1)
            x_a = 3.15_RD
            x_b = 3.45_RD
            iy_chk = 0.30_RD
         case (2)
            x_a = 3.15_RD
            x_b = 6.25_RD
            iy_chk = 3.10_RD
         case (3)
            x_a = 1._RD
            x_b = 10._RD
            iy_chk = 9._RD
         case default
            @:ABORT('invalid instance')
         end select

         ! Finish

         return

      end subroutine integrate_con_arrange_

      !****

      subroutine integrate_con_act_(i)

         integer, intent(in) :: i

         iy = integrate_con(x, y, x_a, x_b)

         ! Finish

         return

      end subroutine integrate_con_act_

      !****

      subroutine integrate_con_assert_(i)

         integer, intent(in) :: i

         real(RD) :: err

         err = ABS((iy - iy_chk)/iy_chk)

         if (err < 1E-13) then
            print *, '    PASS', i
         else
            print *, '    FAIL', i, err
         end if

         ! Finish

         return

      end subroutine integrate_con_assert_

   end subroutine test_integrate_con_
   

   !****

   subroutine test_integrate_lin_()

      real(RD), allocatable :: x(:)
      real(RD), allocatable :: y(:)
      real(RD)              :: x_a
      real(RD)              :: x_b
      real(RD)              :: iy
      real(RD)              :: iy_chk

      integer :: i

      ! Iterate over test instances

      print *,'  integrate_lin'

      do i = 1, 3
         call integrate_lin_arrange_(i)
         call integrate_lin_act_(i)
         call integrate_lin_assert_(i)
      end do

      ! Finish

      return

   contains

      subroutine integrate_lin_arrange_(i)

         integer, intent(in) :: i

         integer :: k

         x = [(k,k=1,10)]
         y = x**2

         select case (i)
         case (1)
            x_a = 3.15_RD
            x_b = 3.45_RD
            iy_chk = 3.33_RD
         case (2)
            x_a = 3.15_RD
            x_b = 6.25_RD
            iy_chk = 71.4775_RD
         case (3)
            x_a = 1._RD
            x_b = 10._RD
            iy_chk = 334.5_RD
         case default
            @:ABORT('invalid instance')
         end select

         ! Finish

         return

      end subroutine integrate_lin_arrange_

      !****

      subroutine integrate_lin_act_(i)

         integer, intent(in) :: i

         iy = integrate_lin(x, y, x_a, x_b)

         ! Finish

         return

      end subroutine integrate_lin_act_

      !****

      subroutine integrate_lin_assert_(i)

         integer, intent(in) :: i

         real(RD) :: err

         err = ABS((iy - iy_chk)/iy_chk)

         if (err < 1E-13) then
            print *, '    PASS', i
         else
            print *, '    FAIL', i, err
         end if

         ! Finish

         return

      end subroutine integrate_lin_assert_

   end subroutine test_integrate_lin_

end module utest_math_m
