 subroutine hupdate_all(n, ds, dgrad, Hess)

!----------------------------------------------------------------------
!  Copyright 2003-2014 Ecole Normale Supérieure de Lyon, 
!  Centre National de la Recherche Scientifique,
!  Université Claude Bernard Lyon 1. All rights reserved.
!
!  This work is registered with the Agency for the Protection of Programs 
!  as IDDN.FR.001.100009.000.S.P.2014.000.30625
!
!  Authors: P. Fleurat-Lessard, P. Dayal
!  Contact: optnpath@gmail.com
!
! This file is part of "Opt'n Path".
!
!  "Opt'n Path" is free software: you can redistribute it and/or modify
!  it under the terms of the GNU Affero General Public License as
!  published by the Free Software Foundation, either version 3 of the License,
!  or (at your option) any later version.
!
!  "Opt'n Path" is distributed in the hope that it will be useful,
!  but WITHOUT ANY WARRANTY; without even the implied warranty of
!
!  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
!  GNU Affero General Public License for more details.
!
!  You should have received a copy of the GNU Affero General Public License
!  along with "Opt'n Path". If not, see <http://www.gnu.org/licenses/>.
!
! Contact The Office of Technology Licensing, valorisation@ens-lyon.fr,
! for commercial licensing opportunities.
!----------------------------------------------------------------------

  use Path_module, only :  HUpdate,Hinv
  use Io_module, only : IoOut

  IMPLICIT NONE

  INTEGER, PARAMETER :: KINT=KIND(1)
  INTEGER, PARAMETER :: KREAL=KIND(1.0D0)

  INTEGER(KINT), INTENT(IN) :: n
  real(KREAL), INTENT(IN) :: ds(n), dgrad(n)
  REAL(KREAL),INTENT(INOUT) :: Hess(n:n)

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! This subroutine is a driver for the update of the Hessian 
! in the Quasi-Newton optimization step.
! We propose mostly update of the inverse Hessian.
!
! input:
!  - n: number of coordinates
!  - ds: GeomOld-Geom (expressed in the optimization coord.)
!  - dgrad: GradOld-Grad  (expressed in the optimization coord.)
!
!  input/output:
!  - Hess: old Hessian in input, replaced by updated Hessian on output
!
!!!!!!!!!!!!!!!!
!
! For now, we propose :
! - For update of inverse Hessian:
!      - BFGS (Broyden, Fletcher, Goldfarb, and Shanno)
!      - DFP (Davidson - Fletcher - Powell )
!      - MS (Murtagh-Sargent)
!
! - For update of hessian:
!      - Bofill (combination of Murtagh-Sargent and Powell-symmetric-Broyden
!      - MS (Murtagh-Sargent)
!      - BFGS
!      - DFP
!----------------------------------------------------
! We use the fact that some formula are the dual of other
! that is, one can convert the formula for update of the Hessian 
! into the update of the inverse Hessian by replacing the Hessian
! (denoted by B in the Fletcher book and in the Nocedal&Wrigth book)
! by the inverse hessian (denoted by H), and replacing ds by dgrad in the
! formula.
! See for example:
! 1) Numerical Optimization, Springer 2nd Ed., 2006
! by Jorge Nocedal & Stephen J. Wright
! 2) ractical Methods of Optimization, John Wiley & Sons, second ed., 1987.
! by R. Fletcher
!
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!



  interface
     function valid(string) result (isValid)
       logical                  :: isValid
       character(*), intent(in) :: string
     end function valid


    SUBROUTINE die(routine, msg, file, line, unit)

      Use VarTypes
      Use io_module

      implicit none
!--------------------------------------------------------------- Input Variables
      character(len=*), intent(in)           :: routine, msg
      character(len=*), intent(in), optional :: file
      integer(KINT), intent(in), optional      :: line, unit

    END SUBROUTINE die

    SUBROUTINE Warning(routine, msg, file, line, unit)

      Use VarTypes
      Use io_module

      implicit none

      character(len=*), intent(in)           :: routine, msg
      character(len=*), intent(in), optional :: file
      integer(KINT), intent(in), optional      :: line, unit

    END SUBROUTINE Warning


  end interface

  ! ======================================================================
  LOGICAL :: Debug

  ! ======================================================================

  debug = valid ('HESS') .OR. Valid('HUPDATE')

  if (debug) WRITE(*,*) "================================= Entering Hupdate_all ==============="

  SELECT CASE (Hinv)
     CASE (.TRUE.)
        SELECT CASE (HUpdate)
           CASE ("BFGS")
              Call Hinvup_BFGS(n,ds,dgrad,hess)
           CASE ("MS")
! we use the fact that MS is self-dual 
              Call Hupdate_MS(n,dgrad,ds,hess)
           CASE ("DFP")
              Call Hinvup_DFP(n,ds,dgrad,hess)
           CASE DEFAULT
              Call Warning("Hupdate_all Hinv=T",Trim(HUpdate) // " not known: using BFGS" &
                   ,Unit=IoOut)
              Call Hinvup_BFGS(n,ds,dgrad,hess)
         END SELECT
     CASE (.FALSE.)
        SELECT CASE (HUpdate)
           CASE ("BOFILL")
              Call Hupdate_Bofill(n,ds,dgrad,hess)
           CASE ("MS")
              Call Hupdate_MS(n,ds,dgrad,hess)
           CASE ("DFP")
! we use the fact that DFP is the dual of BFGS
              Call Hinvup_BFGS(n,dgrad,ds,hess)
           CASE ("BFGS")
! we use the fact that DFP is the dual of BFGS
              Call Hinvup_DFP(n,dgrad,ds,hess)
           CASE DEFAULT
              Call Warning("Hupdate_all Hinv=F",Trim(HUpdate) // " not known: using BoFill" &
                   ,Unit=IoOut)
              Call Hupdate_Bofill(n,ds,dgrad,hess)
           END SELECT
    END SELECT

  if (debug) WRITE(*,*) "================================= Exiting Hupdate_all ==============="
end subroutine hupdate_all
