C     =================================================================
C     File: tunn-main.f
C     =================================================================

      subroutine tun_lissajous(n,x,m,lambda,rho,l,u,retcode)

C     This subroutine try to find some y such that f(y) < f(x) by
C     using the Lissajous's curve defined by:
C
C     lis(t) = (cos( p1 * r + t1 ), ...  cos( pn * r + tn ) )
C
C     This algorithm was published on the paper:
C     "OTIMIZACAO GLOBAL USANDO TRAJETORIAS DENSAS E APLICACOES"
C     Mario Salvatierra Junior, UNICAMP
C

C     PARAMETERS
      integer ntrials
      double precision INFINITO
      parameter ( ntrials   = 100000 )
      parameter ( INFINITO  = 10000000000000000.0d0 )

C     SCALAR ARGUMENTS
      integer n,m,retcode

C     ARRAY ARGUMENTS
      double precision x(n),l(n),u(n),lambda(m),rho(m)

C     LOCAL SCALARS
      integer i,j,k,err,itrial
      double precision ind,num,den,signal,flis,r,delta,lim,fc,bl

C     LOCAL ARRAYS
      double precision t(n),p(n),lis(n)

C     Set the local scalars

      delta   = -1.0d0
      lim     = -0.0001d0
      retcode = 0
      bl      = INFINITO

C     SET INITIAL POINT

      do i = 1,n
          lis(i) = x(i)
      end do

C     SET f(x)

      flis    = INFINITO
C      call evalal(n,x,m,fc,err)
      call evalf(n,x,fc,err)

C     Set the curve's initial position

      do i = 1,n

          t( i ) = acos( ( 2.0d0 * x( i ) - l( i ) - u( i ) ) /
     +                 ( u( i ) - l( i ) ) )

      end do

C     Set the p's value in radians
      open(01,file="primes.txt")

      do i = 1,n

          read(01,*) k
          p( i ) = k
          p( i ) = sqrt( p( i ) )

      end do

      close(01)

C      open(01,file="l_1_2.txt" )
C      open(02,file="l_1_3.txt" )
C      open(03,file="l_1_4.txt" )
C      open(04,file="l_2_3.txt" )
C      open(05,file="l_2_4.txt" )
C      open(06,file="l_3_4.txt" )

C     TRY THE BEST SOLUTION MOVING ONE POINT EACH TIME

C      do j = 1,n

C         SET THE LOCAL SCALARS METHOD

          ind     = 1.0d0
          den     = ind
          num     = 1.0d0
          r       = 0.0d0
          signal  = 1.0d0
          itrial  = 0

C         ITERATE THE LISSAJOUS'S TUNNELING METHOD

          do while ( itrial .le. ntrials ) 
C     +               ( flis .gt. fc ) )

              itrial = itrial + 1

C             Set next r, where r is on the form:
C             r = 1, -1, 2/1, 1/2, -2/1, -1/2, ...
              r = signal * ( num / den )

              den = den - 1.0d0
              num = num + 1.0d0

              if ( den .le. 0.0d0 ) then

                  if ( signal .lt. 0.0d0 ) then

                      signal = 1.0d0
                      ind = ind * 2.0d0

                  else

                      signal = -1.0d0

                  end if

                  den = ind
                  num = 1.0d0

              end if

C             MOVE ALONG THE CURVE

              do j = 1,n

C                 Set a point lis in the Lissajous's curve

                  lis( j ) = 0.5d0 * ( l( j ) + u( j )
     +                     + ( u( j ) - l( j ) ) *
     +                     ( cos( p( j ) * r + t( j ) ) ) )

              end do

C             Set f on the lis, point:

C              write(01,0010) itrial,lis(1),lis(2)
C              write(02,0010) itrial,lis(1),lis(3)
C              write(03,0010) itrial,lis(1),lis(4)
C              write(04,0010) itrial,lis(2),lis(3)
C              write(05,0010) itrial,lis(2),lis(4)
C              write(06,0010) itrial,lis(3),lis(4)
C              call evalf(n,lis,flis,err)
C              call evalal(n,lis,m,flis,err)
              call evalf(n,lis,flis,err)

C             DEBUG
              if (bl .gt. flis ) then

                  bl = flis
                  write(*,*) 'BLIS -> [',itrial,']',bl,'/',fc

              end if

             if ( fc .gt. flis ) then

                  fc = flis
                  do j =1,n
                      x(j) = lis(j)
                  end do

              end if

          end do
C          close(01)
C          close(02)
C          close(03)
C          close(04)
C          close(05)
C          close(06)

C         For the next iteration choose the best point found

C          lis( j ) = x( j )

C      end do 
       read(*,*) itrial

C     NON-EXECUTABLE STATEMENTS
 0010 format(i7,D20.12,1X,D20.12)

      end

C     ******************************************************************
C     ******************************************************************

      subroutine tun_grasp(n,x,m,l,u,f,itrial,g,gpsupn,inform)

C     This subroutine try to find some y such that f(y) <= f(x) by
C     using the GRASP algorithm, described in:
C
C     C.N. MENESES, P.M. PARDALOS, AND M.G.C. RESENDE. GRASP FOR
C     NONLINEAR OPTIMIZATION. AT&T Labs Research Technical
C     Report TD-6DUTRG.

C     PARAMETERS
      integer MaxNumIterNoImprov, MaxIter, MaxNoFunctionImprov
      double precision INFINITO,alpha,ZERO
      parameter ( MaxNumIterNoImprov  = 20         )
      parameter ( MaxIter             = 2000       )
      parameter ( INFINITO = 10000000000000000.0d0 )
      parameter ( ZERO     = 0.000000000001d0      )
      parameter ( alpha               = 0.3d0      )
      parameter ( MaxNoFunctionImprov = 100        )

C     SCALAR ARGUMENTS
      integer n,m,itrial
      double precision f

C     ARRAY ARGUMENTS
      double precision x(n),l(n),u(n)

C     SCALAR ARGUMENTS
      integer inform
      double precision gpsupn

C     ARRAY ARGUMENTS
      double precision g(n)

C     LOCAL SCALARS
      real etime,ntime,btime
      integer i,iter,NumIterNoImprov,err,itbest,NoFunctionImprov
      double precision seed,h,BestSolValue,SolValue

C     LOCAL ARRAYS
      real dum(2)
      double precision BestSolution(n),y(n),xlocalopt(n)

C     SET LOCAL SCALARS

      iter         = itrial
      h            = 1.0d0
      SolValue     = INFINITO
      BestSolValue = f
      seed         = 0.0d0
      NumIterNoImprov = 0

C     SET INITIAL POINT

      do i = 1,n

          y(i) = x(i)
          BestSolution = x(i)

      end do

C     START METHOD

C      do while ( ( iter .lt. MaxIter ) .and. 
C     +           ( f .le. BestSolValue ) )

      NoFunctionImprov = 0

      do while ( ( iter .lt. MaxIter ) .and.
     +           ( NoFunctionImprov .le. MaxNoFunctionImprov ) )
C         Set new iteration

          iter = iter + 1

          call ConstructGreedyRandomizedSolution(y,n,h,l,u,seed,alpha,m)
C          call tun_lissajous(n,y,m,lambda,rho,l,u,SolValue,err)

          if ( n .le. 4 )  then 


              call LocalSearch(y,h,n,l,u,xlocalopt,
     +                         m,err)
C              call evalal(n,xlocalopt,m,SolValue,err)
               call evalf(n,xlocalopt,SolValue,err)

C              call tun_lissajous(n,y,m,lambda,rho,l,u,err)
C              call evalal(n,y,m,SolValue,err)

C              if ( SolValue .lt. BestSolValue ) then
	
C                  do i = 1,n
C                      y(i) = xlocalopt(i)
C                  end do

C              end if

          else

C             call EvaluateFunction(x,SolValue)
C              call evalal(n,y,m,SolValue,err)
              call evalf(n,y,SolValue,err)

          end if
 
C         CHECK IF ITS BETTER THAN f(x)

          ntime = etime(dum) 

          if( SolValue .lt. BestSolValue ) then

C             UPDATES FUNCTION IMPROVEMENT ONLY FOR RELEVANT VALUES

              if( abs( SolValue - BestSolValue ) .gt. 1.0E-5 ) then
                  NoFunctionImprov = 0
              end if

C             UPDATES THE BEST SOLUTION

              do i = 1,n
                  BestSolution(i) = y(i)
              end do

              BestSolValue = SolValue
              itbest       = iter
              NumIterNoImprov = 0

              btime = ntime

          else

              NumIterNoImprov  = NumIterNoImprov + 1
              NoFunctionImprov = NoFunctionImprov + 1

          end if

          write(*,*) "=============== ",iter," ==================="
          write(*,*) " "
          write(*,*) "h,i,i2 | f,t | F,bt > ", h,NumIterNoImprov,
     +               NoFunctionImprov,' | ',
     +               SolValue,ntime,' | ',BestSolValue,btime

C         MAKES THE GRID MORE DENSE

          if( NumIterNoImprov .ge. MaxNumIterNoImprov ) then

              h = h / 2.0d0
              NumIterNoImprov = 0

          end if

      end do

      itrial = iter

      if ( f .gt. BestSolValue) then

          f     = BestSolValue

          do i = 1,n

              x(i) = BestSolution(i)

          end do

      end if

      write(*,*) "=============== FINAL ==================="
      write(*,*) " "
      write(*,*) "i,f*,bt,tt ",itbest,BestSolValue,btime,ntime
      do i = 1,n
          write(*,*) 'x (',i,') = ',BestSolution(i)
      end do
      write(*,*) "****************************************"
      do i = 1,n
          write(*,*) 'x0 (',i,') = ',l(i) + ( u(i) - l(i) ) * 0.5d0
      end do

      read(*,*)

      end;

C     ******************************************************************
C     ******************************************************************

      subroutine ConstructGreedyRandomizedSolution(x,n,h,l,u,seed,
     +                                             alpha,m)
C     This subroutine try to find some y such that f(y) <= f(x) by
C     using the GRASP algorithm, described in:
C
C     C.N. MENESES, P.M. PARDALOS, AND M.G.C. RESENDE. GRASP FOR
C     NONLINEAR OPTIMIZATION. AT&T Labs Research Technical
C     Report TD-6DUTRG.

C     PARAMETERS
      double precision INFINITO
      parameter ( INFINITO = 10000000000000000.0d0 )

C     SCALAR ARGUMENTS
      integer n,m
      double precision seed,alpha,h

C     ARRAY ARGUMENTS
      double precision x(n),l(n),u(n)

C     LOCAL SCALARS
      integer i,j,k,RandomlySelectElement
      double precision vmax,vmin,xibest

C     LOCAL ARRAYS
      integer S(n+1),RCL(n+1)
      double precision z(n),g(n),y(n)

C     SET LOCAL SCALARS

      do i = 1,n

          S(i)   = i
          RCL(i) = -1
          y(i)   = x(i)

      end do

      S(n+1)   = n
      RCL(n+1) = 0

C     TRY TO FIND SOME POINT z SUCH THAT f(x) < f(x)

      do while( S(n+1) .gt. 0 )

          vmin = INFINITO
          vmax = ( -1.0d0 ) * INFINITO

          do i = 1,S(n+1)

C             MINIMIZE COMPONENT y( S(i) ) WITH STEP h

C                  Minimize(x,h,indx,n,l,u,xibest,fbest,m)
              call Minimize(y,h,S(i),n,l,u,xibest,g(S(i)),m)

              z(S(i)) = xibest

              if( vmin .gt. g(S(i)) ) then

                  vmin = g(S(i))

              end if

              if( vmax .lt. g(S(i)) ) then

                  vmax = g(S(i))

              end if

          end do

C         SET SOLUTION CANDIDATES

          RCL(n+1) = 0

          do i=1,S(n+1)

              if( g( S(i) ) .le. ( vmin + alpha * ( vmax - vmin ) ) )
     +        then

                  RCL(n+1) = RCL(n+1) + 1
                  RCL( RCL(n+1) ) = i

              end if

          end do

C         RANDOMLY SELECT A RCL ELEMENT

          seed = ( S(n+1) + seed ) / 2.0d0
          j = RandomlySelectElement(seed,1,RCL(n+1))

C         UPDATES x,y

          x( RCL(j) ) = z( RCL(j) )
          y( RCL(j) ) = z( RCL(j) )

C         S = S \ {j}

          k           = S( RCL(j) )
          S( RCL(j) ) = S( S(n+1) )
          S( S(n+1) ) = k
          S(n+1)      = S(n+1) - 1

      end do

      end

C     ******************************************************************
C     ******************************************************************

      integer function RandomlySelectElement(seed,l,u)
C     This subroutine returns a random integer between l and u

C     Parameters of the subroutine:
C
C     On Entry:
C
C     seed     double precision,
C
C     l        integer,
C              intervals lower bound,
C
C     u        integer,
C              intervals upper bound,
C
C     SCALAR ARGUMENTS
      integer l,u
      double precision seed,nran

C     LOCAL SCALARS
      integer delta
      double precision drand

      delta = u - l + 1
      nran  = drand(seed)

      RandomlySelectElement = max(nint( nran * delta ),1)

      return

      end;

C     ******************************************************************
C     ******************************************************************

      subroutine Minimize(x,h,indx,n,l,u,xibest,fbest,m)
C
C     Parameters of the subroutine:
C
C     On Entry:
C
C     x        double precision x(n),
C              initial point,
C
C     h        double precision,
C              grid density
C
C     indx     integer,
C              index to be moved,
C
C     n        integer,
C              number of variables,
C
C     l        double precision l(n),
C              lower bounds on x,
C
C     u        double precision u(n),
C              upper bounds on x,
C
C     On Return
C
C     xibest   double precision,
C              best position found
C
C     fbest    double precision
C              best f's value (with xibest)

C     PARAMETERS
      double precision INFINITO
      parameter ( INFINITO = 10000000000000000.0d0 )

C     SCALAR ARGUMENTS
      integer n,m,indx
      double precision h,xibest,fbest

C     ARRAY ARGUMENTS
      double precision x(n),l(n),u(n)

C     LOCAL SCALARS
      integer i,err
      double precision vmin,vle

C     LOCAL ARRAYS
      double precision t(n)

C     SET LOCAL SCALARS

      do i = 1,n

          t(i) = x(i)

      end do

      t(indx) = l(indx)

      vmin = INFINITO
C      write(*,*) 'xa -----> ',t(1),t(2),t(3),t(4)

      do while ( t(indx) .le. u(indx) )

C          call EvaluateFunction(t,vle)
C          call evalal(n,t,m,vle,err)
          call evalf(n,t,vle,err)

          if( vmin .gt. vle ) then

              vmin = vle
              xibest = t(indx)

          end if

C          write(*,*) 'h,indx,t,xibest,vle,vmin > ', h,indx,t(indx),
C     +     xibest,vle,vmin
          t(indx) = t(indx) + h

      end do

      fbest = vmin
C      write(*,*) 'xd -----> ',t(1),t(2),t(3),t(4)
C      write(*,*) 'FB -> ',fbest
C      read(*,*) i

      end

C     ******************************************************************
C     ******************************************************************

      subroutine LocalSearch(x,h,n,l,u,xlocalopt,
     +                       m,err)

C     PARAMETERS
      double precision INFINITO
      parameter ( INFINITO = 10000000000000000.0d0 )

C     SCALAR ARGUMENTS
      integer n,m,err
      double precision h

C     ARRAY ARGUMENTS
      double precision x(n),xlocalopt(n),
     +                 l(n),u(n)

C     LOCAL SCALARS
      integer i,maxldir,ldir
      double precision xlocaloptValue,xprimeValue
      logical Feasible,Improved

C     ARRAY SCALARS
      double precision all_dir(324),dir(n),xprime(n)

      if ( n .gt. 4 ) then

          err = 1

      else

C         SET LOCAL SCALARS
          Improved = .true.

          do i = 1,n

              xlocalopt(i) = x(i)

          end do
          call GenerateDir(n,all_dir,maxldir,err)
          ldir = 0

C          call evalal(n,xlocalopt,m,xlocaloptValue,err)
          call evalf(n,xlocalopt,xlocaloptValue,err)

          do while ( Improved )

              Improved = .false.

              do while ( ( ldir .lt. maxldir ) .and.
     +                   ( .not. ( Improved ) ) )

C                 SET NEW DIRECTION
                  do i=1,n

                      dir(i) = all_dir( ldir + i )

                  end do

                  ldir = ldir + n

                  do i=1,n

                      xprime(i) = xlocalopt(i) + h * dir(i)

                  end do

C                  call evalal(n,xprime,m,xprimeValue,err)
                  call evalf(n,xprime,xprimeValue,err)

                  if ( ( Feasible(xprime,n,l,u) ) .and.
     +                 ( xprimeValue .lt. xlocaloptValue ) ) then

                      do i=1,n

                          xlocalopt(i) = xprime(i)

                      end do

                      xlocaloptValue = xprimeValue
                      Improved = .true.
                      ldir      = 0

                  end if

              end do

          end do

      end if

      end


C     ******************************************************************
C     ******************************************************************

      subroutine GenerateDir(n,dir,ldir,err)
C     This subroutine generates a set of directions indicated by dir.
C     For n < 5 only.


C     Parameters of the subroutine:
C
C     On Entry:
C
C     n        integer,
C              problem dimension
C
C     dir      integer,
C              intervals lower bound,
C
C     On Return:
C
C     dir      double precision dir(321),
C              directions
C
C     err      integer,
C              return status: -1 error, else ok
C
C     SCALAR ARGUMENTS
      integer n,ldir,err

C     ARRAY ARGUMENTS
      double precision dir(324)

C     LOCAL SCALARS
      integer i,j,ilocal,tset,addone


C     LOCAL ARRAYS
      double precision next(4)

C     SET dir index
      tset = ( 3**n )
      ldir     =  tset * n

C     SET LOCAL SCALARS
      next(1) = -1.0d0
      next(2) = -1.0d0
      next(3) = -1.0d0
      next(4) = -1.0d0

      err = -1

C     VERIFY IF n < 5

      if ( n .lt. 5 ) then

          do i = 1,ldir

              ilocal = mod(i,n)

C             Verify if the series finished
              if ( ilocal .eq. 0 ) then

                  dir(i) = next(n)

                  addone = 1.0d0

                  do j = 1,n

                      next(j) = next(j) + addone

                      if( next(j) .gt. 1.0d0 ) then

                          next(j) = -1.0d0

                      else	

                          addone = 0.0d0

                      end if

                  end do

              else

                  dir(i) = next(ilocal)

              end if

          end do

          err = 0

      end if

      end;

C     ******************************************************************
C     ******************************************************************

      logical function Feasible(x,n,l,u)

C     PARAMETERS

C     SCALAR ARGUMENTS
      integer n

C     ARRAY ARGUMENTS
      double precision x(n),l(n),u(n)

C     LOCAL SCALARS
      integer i
      logical feas

      feas = .true.

      i = 1

      do while ( ( i .le. n ) .and. ( feas ) )

          if ( ( x(i) .lt. l(i) ) .or. ( x(i) .gt. u(i) ) ) then

              feas = .false.

          end if

          i = i + 1

      end do

      Feasible = feas

      return

      end
