! @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ !
! @@                                                                @@ !
! @@       PROGRAM  ASCOT 2014.440 (ver.4.53)                       @@ !
! @@     "Abinitio Simulation Code for Quantum Transport"           @@ !
! @@                                                                @@ !
! @@                                                                @@ !
! @@  AUTHOR(S): Hisashi KONDO (Univ. Tokyo)                        @@ !
! @@                                                09/May/2014     @@ !
! @@                                                                @@ !
! @@  Contact address: Phase System Consortium                      @@ !
! @@                                                                @@ !
! @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ !

subroutine linear_equation(mat_max,amat_in,b_in,b_out)

  implicit none
  integer, intent(in) :: mat_max
  complex(8), intent(in) :: amat_in(mat_max,mat_max),b_in(mat_max)
  complex(8), intent(out) :: b_out(mat_max)

  integer :: i1_do,i2_do,i3_do,mat1_max,mat2_max,mat_temp,ier,i_smd,i_block
  integer, allocatable :: mat1(:),mat2(:)
  complex(8) :: temp,temp1
  complex(8), allocatable :: amat_temp(:,:),vec_temp(:)

  allocate(amat_temp(mat_max+2,mat_max),vec_temp(mat_max)                      &
       ,mat1(mat_max),mat2(mat_max),stat=ier)
  if( ier /= 0 ) then
     write(6,*) 'error allocate: leaner_equation'
     stop
  end if

  do i1_do=1,mat_max
     mat1(i1_do)=i1_do
     mat2(i1_do)=i1_do
  end do

  do i1_do=1,mat_max
     vec_temp(i1_do)=b_in(i1_do)
  end do

  i_block=64
  do i_smd=1,mat_max,i_block
     do i1_do=1,mat_max
        do i2_do=i_smd,min(i_smd+i_block-1,mat_max)
           amat_temp(i1_do,i2_do)=amat_in(i1_do,i2_do)
        end do
     end do
  end do

  do i1_do=1,mat_max
     mat1_max=i1_do
     mat2_max=i1_do
     temp=amat_temp(mat2(i1_do),mat1(i1_do))
     do i2_do=i1_do,mat_max
        do i3_do=i1_do,mat_max
           if( cdabs(temp) < cdabs(amat_temp(mat2(i3_do),mat1(i2_do))) ) then
              mat1_max=i2_do
              mat2_max=i3_do
              temp=amat_temp(mat2(i3_do),mat1(i2_do))
           end if
        end do
     end do
     if( mat1_max /= i1_do ) then
        mat_temp=mat1(i1_do)
        mat1(i1_do)=mat1(mat1_max)
        mat1(mat1_max)=mat_temp
     end if
     if( mat2_max /= i1_do ) then
        mat_temp=mat2(i1_do)
        mat2(i1_do)=mat2(mat2_max)
        mat2(mat2_max)=mat_temp
     end if
     if( cdabs(amat_temp(mat2(i1_do),mat1(i1_do))) < 1.d-16 ) then
        write(6,*) 'error_ leaner_eq. -- LU:'                                    &
             ,cdabs(amat_temp(mat2(i1_do),mat1(i1_do)))
        stop
     end if

     temp1=1.d0/amat_temp(mat2(i1_do),mat1(i1_do))
     do i2_do=i1_do+1,mat_max
        amat_temp(mat2(i1_do),mat1(i2_do))                                       &
             =amat_temp(mat2(i1_do),mat1(i2_do))*temp1
        do i3_do=i1_do+1,mat_max
           amat_temp(mat2(i3_do),mat1(i2_do))=amat_temp(mat2(i3_do),mat1(i2_do))  &
                -amat_temp(mat2(i1_do),mat1(i2_do))                       &
                *amat_temp(mat2(i3_do),mat1(i1_do))
        end do
     end do
     vec_temp(mat2(i1_do))=vec_temp(mat2(i1_do))*temp1
     do i2_do=i1_do+1,mat_max
        vec_temp(mat2(i2_do))=vec_temp(mat2(i2_do))                              &
             -amat_temp(mat2(i2_do),mat1(i1_do))*vec_temp(mat2(i1_do))
     end do
  end do

  b_out(mat1(mat_max))=vec_temp(mat2(mat_max))
  do i1_do=mat_max-1,1,-1
     b_out(mat1(i1_do))=vec_temp(mat2(i1_do))
     do i2_do=i1_do+1,mat_max
        b_out(mat1(i1_do))=b_out(mat1(i1_do))                                    &
             -amat_temp(mat2(i1_do),mat1(i2_do))*b_out(mat1(i2_do))
     end do
  end do

  deallocate(amat_temp,vec_temp,mat1,mat2,stat=ier)
  if( ier /= 0 ) then
     write(6,*) 'error deallocate: leaner_equation'
     stop
  end if

  return
end subroutine linear_equation
