c Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
c Copyright (C) INRIA
c 
c This file must be used under the terms of the CeCILL.
c This source file is licensed as described in the file COPYING, which
c you should have received as part of this distribution.  The terms
c are also available at    
c http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
c
      subroutine n1gc2a(simul,prosca,n,x,f,g,dx,df1,eps,imp,io,
     /                  niter,nsim,info,memh,d,xx,gg,tabaux,h,
     /                  izs,rzs,dzs)
      implicit double precision (a-h,o-z)
c
c parametres
      double precision  zero     , un     , deux     , ro
      parameter  (  zero=0.0d+0, un=1.0d+0, deux=2.0d+0, ro=0.20d+0 )
c declaration des tableaux
      double precision    x(n),g(n),d(n),xx(n),gg(n),tabaux(n),h(*),
     /                    dzs(*)
      real rzs(*)
      integer  izs(*)
c declaration des scalaires
      double precision  f, dx, eps, df1
      double precision   dg1, dg, alpha, normg0, aux1, aux2, mu, eta,
     / omega, normg, gcarre, ggcarr, nu, sigma, sscalg, uscalg,
     / sscaek
      integer  n, memh, imp, io, nsim, niter, info
      integer  memuti, nrzuti, memsup, m, retour, iter,
     /   ntotap, nmisaj, i, iu, is, ieta, inu, j, kj, k, kp1
      logical  gc, iterqn, intfor, redfor, redem, termi
      character bufstr*(4096)
c
      external  simul, prosca
c
c  *************************************************************
c  phase i:determination de la methode ( et de m le cas echeant)
c  *************************************************************
c
      memuti=n*(n+1)/2
c
c memsup est aussi la dimension minimale de la matrice h
      memsup=2*n+2
c
      if (memh .ge. memuti) then
      gc=.false.
      nrzuti=memuti+4*n
      if (imp .gt. 1) then 
      write(bufstr,1) nrzuti
      call basout(io_out ,io ,bufstr(1:lnblnk(bufstr)))
      endif
      else if (memh .lt. memsup) then
      info=3
      return
      else
      gc=.true.
c m est le nombre de mises a jour admissible
      m=memh / memsup
c memuti est ici le nombre de places memoire utilisees pour stocker h
      memuti=m * memsup
      nrzuti=memuti+4*n
      if (imp .gt. 1) then
        write(bufstr,2) m
        call basout(io_out ,io ,bufstr(1:lnblnk(bufstr)))
        write(bufstr,3) nrzuti
        call basout(io_out ,io ,bufstr(1:lnblnk(bufstr)))
        endif
      endif
1     format(40h     methode de quasi-newton. nrz utile=,i7)
2     format(38h     methode du gradient conjugue avec,i3)
3     format(14h mises a jour.,11h nrz utile=,i7)
c
c  ***********************************************
c  phase ii:initialisations propres a l'optimiseur
c  ***********************************************
c
c initialisation des compteurs
      iter=0
      ntotap=1
c
c  ******************************************************************
c  phase iii:demarrage a partir de x(0) avec descente suivant -(grad)
c  ******************************************************************
c
3000  i=0
      nmisaj=0
c
c calcul de la direction de descente
      do 3100 j=1,n
      d(j)=-g(j)
3100  continue
c
      call prosca(n,g,d,dg1,izs,rzs,dzs)
      normg0=sqrt(abs(dg1))
      if (iter .eq. 1) then
      omega=eps * normg0
      endif
c
c ************************************************************
c phase iv:debut de l'iteration x(i-1) donne x(i) le long de d
c ************************************************************
c
4000  if (iter .eq. niter) then
      info=4
      goto 99999
      endif
      iter=iter + 1
      i=i+1
c
c determination du type de pas
      if (gc) then
      iterqn=(i .le. m) .and. (2 .le. i)
      endif
c
c  *******************************
c  phase v:initialisation de alpha
c  *******************************
c
      if (iter .eq. 2) then
      alpha=deux * df1 /(-dg1)
      else if (gc) then
                   if (i.eq.1) then
                   alpha=un / normg0
                   else
                            if (iterqn) then
                            alpha=un
                            else
                            alpha=alpha * dg / dg1
                            endif
                   endif
      else
      alpha=un
      endif
c
c ***************************
c phase vi:recherche lineaire
c ***************************
c
      dg=dg1
      intfor=( gc .and. (.not.iterqn)).or. ((.not.gc) .and.(i.eq.1))
      do 6000 j=1,n
      xx(j)=x(j)
      gg(j)=g(j)
6000  continue
      call n1gc2b(n,simul,prosca,xx,f,dg,alpha,d,x,g,imp,io,retour,
     /           ntotap,nsim,intfor,dx,eps,izs,rzs,dzs)
c
      if (imp .gt. 3) then
      write(bufstr,6003)
      call basout(io_out ,io ,bufstr(1:lnblnk(bufstr)))
      endif
      if ((retour .eq. 4).or.((retour .eq. 1).and.(i .eq. 1))) then
      info=6
      return
      else if (retour .eq. 1) then
      if (imp .gt. 1) then
        write(bufstr,6002) iter,ntotap
        call basout(io_out ,io ,bufstr(1:lnblnk(bufstr)))
        endif
      goto 3000
      else
c calcul de (g,g)
      if((i .gt. 1) .and. gc) ggcarr=gcarre
      call prosca(n,g,g,gcarre,izs,rzs,dzs)
      normg=sqrt(gcarre)
      if (imp .gt. 2) then
        write(bufstr,6001)iter,ntotap,f
        call basout(io_out ,io ,bufstr(1:lnblnk(bufstr)))
        endif
      if (retour .eq. 2) then
      info=0
      goto 99999
      else if (retour .eq. 3)then
      info=5
      goto 99999
      endif
      endif
6001  format(4x,6h n1gc2,3x,i4,6h iters,3x,i4,7h simuls,3x,2hf=,d16.9)
6002  format(4x,6h n1gc2,3x,i4,6h iters,3x,i4,7h simuls,
     / 33h necessite d'un redemarrage total)
6003  format()
c
c  ******************************************************
c  phase vii:test d'arret par obtention de la convergence
c  ******************************************************
c
      termi=normg .lt. omega
      if (termi) then
      info=1
      goto 99999
      else
      continue
      endif
c
c *******************************************
c  phase viii:test x(i) point de redemarrage?
c *******************************************
c
c doit on forcer un redemarrage?
      redfor=gc .and. ((i .eq. 1) .or. (i .eq. m+n))
      if (redfor) then
      redem=.true.
      else if (gc .and. .not. iterqn) then
      call prosca(n,g,gg,aux1,izs,rzs,dzs)
      redem=abs(aux1) .gt. abs(ro * ggcarr)
      else
      redem=.false.
      endif
c
c  ********************
c  phase ix:mise a jour
c  ********************
c
c calcul de s stocke dans d et de y stocke dans xx
      do 9000 j=1,n
      d(j)=alpha * d(j)
      xx(j)=g(j)-gg(j)
9000  continue
      if (redem) then
c cas ou x(i) est un point de redemarrage
      i=1
      nmisaj=1
c sauvegarde de s qui est actuellement dans d
c                u=h*y=y
c                nu=(y,hy)=(y,y)
c                eta=(s,y)
c calcul des indices
      inu=1
      ieta=inu + 1
      iu=ieta
      is=iu + n
c
      do 9100 j=1,n
      h(iu +j)=xx(j)
      h(is +j)=d(j)
9100  continue
      call prosca(n,xx,xx,nu,izs,rzs,dzs)
      h(inu)=nu
      call prosca(n,d,xx,eta,izs,rzs,dzs)
      h(ieta)=eta
c h1 est maintenant definie
c calcul de h1*g que l'on range dans xx
      call fmulb1(n,h,g,xx,tabaux,nmisaj,prosca,izs,rzs,dzs)
c
      else if (gc) then
c cas de gc sans redamarrage
c calcul de h*y range dans gg
       call fmulb1(n,h,xx,gg,tabaux,nmisaj,prosca,izs,rzs,dzs)
c calculs de  nu, eta, sscalg, uscalg
      call prosca(n,xx,gg,nu,izs,rzs,dzs)
      call prosca(n,d,xx,eta,izs,rzs,dzs)
      call prosca(n,d,g,sscalg,izs,rzs,dzs)
      call prosca(n,gg,g,uscalg,izs,rzs,dzs)
c calcul de sigma et de mu
      sigma=(uscalg -(un + nu / eta)* sscalg) / eta
      mu=sscalg /eta
c calcul de h*g que l'on range dans xx
      call fmulb1(n,h,g,xx,tabaux,nmisaj,prosca,izs,rzs,dzs)
c calcul de la nouvelle direction de recherche:
c h*g - mu * u - sigma * s
      do 9200 j=1,n
      xx(j)= xx(j) - mu * gg(j) - sigma * d(j)
9200  continue
c
c cas d'une iteration de type quasi newton
      if (iterqn) then
      nmisaj=nmisaj + 1
c sauvegarde des termes utiles pour stocker la matrice mise a jour
      inu=inu + memsup
      ieta=inu + 1
      iu=ieta
      is=iu + n
      do 9300 j=1,n
      h(iu +j)=gg(j)
      h(is +j)=d(j)
9300  continue
      h(inu)=nu
      h(ieta)=eta
      endif
c cas de la methode quasi newton
      else
c calcul de eta=(s,y)
      call prosca(n,d,xx,eta,izs,rzs,dzs)
      if (i .eq. 1) then
c etape initiale calcul de l'approximation initiale de l'inverse de la
c matrice hessienne
c calcul de nu=(y,h0*y)=(y,y)
      call prosca(n,xx,xx,nu,izs,rzs,dzs)
c stockage de cette matrice h=(eta / nu) * i
      kj=1
      aux1=eta / nu
      do 9500 k=1,n
      h(kj)=aux1
      kj=kj +1
      kp1=k+1
      if (n .ge. kp1) then
      do 9400 j=kp1,n
      h(kj)=zero
      kj=kj +1
9400  continue
      endif
      gg(k)=aux1 * xx(k)
9500  continue
      nu=eta
      else
      call fmuls1(n,h,xx,gg)
      call prosca(n,xx,gg,nu,izs,rzs,dzs)
      endif
c calcul de la matrice mise a jour (utilisation de la formule bfgs )
c nu, eta et h*y (stocke dans gg) sont connus
      aux1=un + nu / eta
      kj=1
      do 9800 k=1,n
c calcul du vecteur contenant la keme colonne de h
      lk=k
      km1=k-1
      if (k .ge. 2) then
      do 9610 l=1,km1
      tabaux(l)=h(lk)
      lk=lk + (n-l)
9610  continue
      endif
      do 9620 l=k,n
      tabaux(l)=h(lk)
      lk=lk+1
9620  continue
c
      call prosca(n,xx,tabaux,aux2,izs,rzs,dzs)
      do 9630 l=1,n
      tabaux(l)=zero
9630  continue
      tabaux(k)=un
      call prosca(n,tabaux,d,sscaek,izs,rzs,dzs)
      kj=k-n
      do 9700 j=1,k
      kj=kj+n-j+1
      h(kj)=h(kj) - ( (aux2 - aux1*sscaek)*d(j) + sscaek*gg(j) )/eta
9700  continue
9800  continue
      endif
c
c  *****************************************************
c  phase x :calcul de la nouvelle direction de recherche
c  *****************************************************
c
      if (gc) then
c xx contient -d
      do 10000 j=1,n
      d(j)=-xx(j)
10000 continue
c
       else
c cas de la methode de quasi newton
c la nouvelle direction d egale -(h * g)
      call fmuls1(n,h,g,d)
      do 10100 j=1,n
      d(j)=-d(j)
10100 continue
      endif
c
c test:la direction de recherche est elle bien de descente
      call prosca(n,d,g,dg1,izs,rzs,dzs)
      if (dg1 .ge. zero) then
      info=7
      if (imp .gt. 1) then
        write(bufstr,10101) dg1
        call basout(io_out ,io ,bufstr(1:lnblnk(bufstr)))
        endif
      goto 99999
      else
      goto 4000
      endif
c
c retour au programme appelant
99999 niter=iter
      nsim=ntotap
      if (i .eq. 0) then
      eps=normg0
      else
      eps=normg
      endif
10101 format(40h n1gc2a   erreur dans la hessienne   dg=,d9.2)
      end
