! Ejemplo del Metodo de Diferencias Finitas para resolver la ecuacion diferencial parcial:
!    Uxx=-Pi*Pi*cos(Pix)
!        xi<=U<=xf
!        U(xi)=vi y U(xf)=vf
! con solucion analitica: 
!     cos(pix)
!
! Para conocer mas del metodo ver:
!    http://132.248.182.159/acl/MDF
!
! Autor: Antonio Carrillo Ledesma
!    http://mmc.geofisica.unam.mx/acl


	program mdf1D
		implicit none
		integer i, N, nn 
		real*16, allocatable :: A(:,:), b(:), x(:)
		real*16 xi, xf, vi, vf, h, R, P, Q, y

		xi = 0.0                    ! Inicio del diminio
		xf = 1.0                    ! Fin del dominio
		vi = 1.0                    ! Valor en la frontera xi
		vf = -1.0                   ! Valor en la frontera xf
		nn = 11                     ! Particion
		N = nn - 2                  ! Nodos interiores
		h = (xf - xi) / (nn-1)   ! Incremento en la malla

    		allocate (A(N,N), b(N), x(N))
                ! Stencil
		R = 1. / (h * h)
		P = -2. / (h * h)
		Q = 1. / (h * h)

		! Primer renglon de la matriz A y vector b
		A(1,1)=P
		A(2,1)=Q
		call ladoDerecho(xi,y)
		b(1)=y-vi*R
		! Renglones intermedios de la matriz A y vector b
		do i=2,N-1 
			A(i-1,i)=R
			A(i,i)=P
			A(i+1,i)=Q
			call ladoDerecho(xi+h*(i),y)
			b(i)= y
		end do
		! Renglon final de la matriz A y vector b
		A(N-1,N)=R
		A(N,N)=P
		call ladoDerecho(xi+h*N,y)
		b(N)= y -vf*Q

		call gaussSeidel(A, x, b, N, 1000)
                print*, "A: ", A
                print*, "b: ", b
		print*, "x: ", x

                call jacobi(A, x, b, N, 1000)
                print*, "A: ", A
                print*, "b: ", b
                print*, "x: ", x

	end program


	subroutine ladoDerecho(x,y)	
		real*16, intent(in) :: x
		real*16, intent(inout) :: y
		real*16 pi

		pi = 3.1415926535897932384626433832;
		y = -pi * pi * cos(pi * x);
	end subroutine


	subroutine gaussSeidel(a, x, b, nx, iter)
		implicit none
		integer, intent(in) :: nx, iter
		real*16, intent(in) :: a(nx,nx), b(nx)
		real*16, intent(inout) :: x(nx)
		integer i, j, m
		real*16 sum
		
		do m = 1, iter
			do i = 1, nx
				sum = 0.0
				do j = 1, nx
					if (i .NE. j) then
						sum = sum + a(i,j) * x(j)
					end if
				end do
				x(i) = (1.0 / a(i,i)) * (b(i) - sum)
			end do
		end do
	end subroutine


        subroutine jacobi(a, x, b, nx, iter)
                implicit none
                integer, intent(in) :: nx, iter
                real*16, intent(in) :: a(nx,nx), b(nx)
                real*16, intent(inout) :: x(nx)
                real*16, allocatable :: xt(:)
                integer i, j, m
                real*16 sum

                allocate (xt(nx))
                do m = 1, iter
                        do i = 1, nx
                                sum = 0.0
                                do j = 1, nx
                                        if (i .NE. j) then
                                                sum = sum + a(i,j)*x(j)
                                        end if
                                end do
                                xt(i) = (1.0 / a(i,i)) * (b(i) - sum)
                        end do
                        do i = 1, nx
                                x(i) = xt(i)
                        end do
                end do
        end subroutine

