      program diff
      include 'mpif.h'
      integer xmax,ymax
      parameter (xmax=4000)
      parameter (ymax=1500)
      real*8 f0(xmax,ymax), f1(xmax,ymax)
      real*8 df,gdf,ldf
      integer x,y,n,myid,np,ierr,ierr1,status(MPI_STATUS_SIZE)
      common myid,np
      double precision tstart, tstop, tdiff1, tdiff2
c     Инициализация MPI и определение процессорной конфигурации
      call MPI_INIT( ierr )
      call MPI_COMM_RANK( MPI_COMM_WORLD, myid, ierr )
      call MPI_COMM_SIZE( MPI_COMM_WORLD, np, ierr ) 
      if ( myid .eq. 0 ) write(*,*) 'Starting!'
c     Задаем начальные значения
      call initial(f0,xmax,ymax)
      call initial(f1,xmax,ymax)
c     Задаем граничные значения
      call boundary(f0,xmax,ymax)
      call boundary(f1,xmax,ymax)
c     Начинаем основной итерационный цикл
      df=0.0
      n=0
      if ( myid .eq. 0 ) write(*,*) 'Processing...'
    1 continue
      tstart=MPI_WTIME();
c     При смене итераций меняем местами массивы
      if ( ((n/2)*2) .EQ. n ) then
c       Обрабатываем f0 для каждой четной итерации
        call iter(f0,f1,xmax,ymax,df)
      else
c	Обрабатываем f1 для каждой нечетной итерации
        call iter(f1,f0,xmax,ymax,df)
      endif
      ldf=df
c     Увеличиваем номер цикла на единицу
      n=n+1
      tstop=MPI_WTIME();
      tdiff1=tstop-tstart
c     Получаем разницу значений функции на слоях
c     с каждого узла кластера и находим максимальное значение
c     этой разницы для всей разностной сетки
      call MPI_REDUCE(df, gdf, 1, MPI_REAL8,
     x    MPI_MAX, 0, MPI_COMM_WORLD, ierr1)
c     Сообщаем найденную максимальную разницу
c     всем узлам кластера
      if(myid .eq. 0) df=gdf
      CALL MPI_BCAST(df, 1, MPI_REAL8, 0, MPI_COMM_WORLD, ierr)
      tstart=MPI_WTIME();
      tdiff2=tstart-tstop
c     Выводим на экран разницу между итерациями
      if ( myid .eq. 0 ) 
     x write(*,*) 'D:',df,'R:',tdiff1,ierr1,'B:',tdiff2
c     Если разница больше заданной, то решение не найдено
c     и мы идем на следующий цикл
      if (df .GT. 0.01) goto 1
c     Печатаем количество итераций, потребовавшихся
c     для нахождения решения
      if ( myid .eq. 0 ) write(*,*) 'Iteration count:', n
c     Заканчиваем работу с MPI
      call MPI_FINALIZE(ierr)
      stop
      end

c     Подпрограмма вычисления искомой функции
      subroutine iter(f0,f1,xmax,ymax,df)
      include 'mpif.h'
      integer xmax,ymax
      real*8 f0(xmax,ymax), f1(xmax,ymax)
      real*8 dt,dx,dy
      real*8 df,dff,df1
      integer x,y,n,myid,np,ierr,status(MPI_STATUS_SIZE)
      common myid,np
      double precision tstart, tstop, tdiff
      dt=0.01
      dx=0.5
      dy=0.5
c     Обмениваемся границами с соседом
c      tstart=MPI_WTIME();
      if ( myid .gt. 0 )
     x call MPI_SENDRECV(
     x f0(1,2), xmax, MPI_REAL8, myid-1, 1, 
     x f0(1,1), xmax, MPI_REAL8, myid-1, 1, 
     x MPI_COMM_WORLD, status, ierr)
      if ( myid .lt. np-1 )
     x call MPI_SENDRECV(
     x f0(1,ymax-1), xmax, MPI_REAL8, myid+1, 1, 
     x f0(1,ymax), xmax, MPI_REAL8, myid+1, 1, 
     x MPI_COMM_WORLD, status, ierr)
c      tstart=MPI_WTIME();
c     Вычисляем функцию в ячейках сетки
      df1=0.0
      do y=2,ymax-1
        do x=2,xmax-1
        dff=dt*(
     x    (f0(x+1,y)-2*f0(x,y)+f0(x-1,y))/(dx*dx)
     x    +
     x    (f0(x,y+1)-2*f0(x,y)+f0(x,y-1))/(dy*dy)
     x    )
        f1(x,y)=f0(x,y)+dff
c     Находим максимальную дельту
        if( df1 < abs(dff) ) df1=abs(dff)
        end do
      end do
      df=df1
      call MPI_BARRIER(MPI_COMM_WORLD, ierr)
      return
      end

c     Подпрограмма задания начальных значений
      subroutine initial(f0,xmax,ymax)
      integer rank,xmax,ymax,x,y
      real*8 f0(xmax,ymax)
      common myid,np
c     Задаем начальные значения массива
      do x=1,xmax
        do y=1,ymax
          f0(x,y)=0.0
        end do
      end do
      return
      end

c     Подпрограмма задания граничных условий
      subroutine boundary(f0,xmax,ymax)
      integer rank,xmax,ymax,x,y
      real*8 f0(xmax,ymax)
      common myid,np
c     Задаем граничные значения массива
c     на границах с X=1 и X=xmax
      do y=1,ymax
        f0(1,y)=0.0
        f0(xmax,y)=0.0
      end do
c     Если мы - первый процесс, то
c     задаем граничные условия массива
c     на границе с Y=1
      if ( myid .eq. 0 ) then
        do x=1,xmax
          f0(x,1)=sin((x*1.0)/(xmax/2))
        end do
      endif
c     Если мы - последний процесс, то
c     задаем граничные условия массива
c     на границе с Y=ymax
      if (myid .eq. np-1 ) f0(xmax/2,ymax)=-5.0
      return
      end
