*> \brief <b> DSYEV computes the eigenvalues and, optionally, the left and/or right eigenvectors for SY matrices</b>
*
*  =========== DOCUMENTATION ===========
*
* Online html documentation available at 
*            http://www.netlib.org/lapack/explore-html/ 
*
*> \htmlonly
*> Download DSYEV + dependencies 
*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dsyev.f"> 
*> [TGZ]</a> 
*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dsyev.f"> 
*> [ZIP]</a> 
*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dsyev.f"> 
*> [TXT]</a>
*> \endhtmlonly 
*
*  Definition:
*  ===========
*
*       SUBROUTINE DSYEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO )
* 
*       .. Scalar Arguments ..
*       CHARACTER          JOBZ, UPLO
*       INTEGER            INFO, LDA, LWORK, N
*       ..
*       .. Array Arguments ..
*       DOUBLE PRECISION   A( LDA, * ), W( * ), WORK( * )
*       ..
*  
*
*> \par Purpose:
*  =============
*>
*> \verbatim
*>
*> DSYEV computes all eigenvalues and, optionally, eigenvectors of a
*> real symmetric matrix A.
*> \endverbatim
*
*  Arguments:
*  ==========
*
*> \param[in] JOBZ
*> \verbatim
*>          JOBZ is CHARACTER*1
*>          = 'N':  Compute eigenvalues only;
*>          = 'V':  Compute eigenvalues and eigenvectors.
*> \endverbatim
*>
*> \param[in] UPLO
*> \verbatim
*>          UPLO is CHARACTER*1
*>          = 'U':  Upper triangle of A is stored;
*>          = 'L':  Lower triangle of A is stored.
*> \endverbatim
*>
*> \param[in] N
*> \verbatim
*>          N is INTEGER
*>          The order of the matrix A.  N >= 0.
*> \endverbatim
*>
*> \param[in,out] A
*> \verbatim
*>          A is DOUBLE PRECISION array, dimension (LDA, N)
*>          On entry, the symmetric matrix A.  If UPLO = 'U', the
*>          leading N-by-N upper triangular part of A contains the
*>          upper triangular part of the matrix A.  If UPLO = 'L',
*>          the leading N-by-N lower triangular part of A contains
*>          the lower triangular part of the matrix A.
*>          On exit, if JOBZ = 'V', then if INFO = 0, A contains the
*>          orthonormal eigenvectors of the matrix A.
*>          If JOBZ = 'N', then on exit the lower triangle (if UPLO='L')
*>          or the upper triangle (if UPLO='U') of A, including the
*>          diagonal, is destroyed.
*> \endverbatim
*>
*> \param[in] LDA
*> \verbatim
*>          LDA is INTEGER
*>          The leading dimension of the array A.  LDA >= max(1,N).
*> \endverbatim
*>
*> \param[out] W
*> \verbatim
*>          W is DOUBLE PRECISION array, dimension (N)
*>          If INFO = 0, the eigenvalues in ascending order.
*> \endverbatim
*>
*> \param[out] WORK
*> \verbatim
*>          WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK))
*>          On exit, if INFO = 0, WORK(1) returns the optimal LWORK.
*> \endverbatim
*>
*> \param[in] LWORK
*> \verbatim
*>          LWORK is INTEGER
*>          The length of the array WORK.  LWORK >= max(1,3*N-1).
*>          For optimal efficiency, LWORK >= (NB+2)*N,
*>          where NB is the blocksize for DSYTRD returned by ILAENV.
*>
*>          If LWORK = -1, then a workspace query is assumed; the routine
*>          only calculates the optimal size of the WORK array, returns
*>          this value as the first entry of the WORK array, and no error
*>          message related to LWORK is issued by XERBLA.
*> \endverbatim
*>
*> \param[out] INFO
*> \verbatim
*>          INFO is INTEGER
*>          = 0:  successful exit
*>          < 0:  if INFO = -i, the i-th argument had an illegal value
*>          > 0:  if INFO = i, the algorithm failed to converge; i
*>                off-diagonal elements of an intermediate tridiagonal
*>                form did not converge to zero.
*> \endverbatim
*
*  Authors:
*  ========
*
*> \author Univ. of Tennessee 
*> \author Univ. of California Berkeley 
*> \author Univ. of Colorado Denver 
*> \author NAG Ltd. 
*
*> \date November 2011
*
*> \ingroup doubleSYeigen
*
*  =====================================================================
      SUBROUTINE GAL_DSYEV(JOBZ,UPLO,N,A,LDA,W,WORK,LWORK,INFO)
*
*  -- LAPACK driver routine (version 3.4.0) --
*  -- LAPACK is a software package provided by Univ. of Tennessee,    --
*  -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--
*     November 2011
*
*     .. Scalar Arguments ..
      CHARACTERJOBZ,UPLO
      INTEGER INFO,LDA,LWORK,N
*     ..
*     .. Array Arguments ..
      DOUBLE PRECISION A(LDA,*),W(*),WORK(*)
*     ..
*
*  =====================================================================
*
*     .. Parameters ..
      DOUBLE PRECISION ZERO,ONE
      PARAMETER (ZERO=0.0D0,ONE=1.0D0)
*     ..
*     .. Local Scalars ..
      LOGICAL LOWER,LQUERY,WANTZ
      INTEGER IINFO,IMAX,INDE,INDTAU,INDWRK,ISCALE,
     $LLWORK,LWKOPT,NB
      DOUBLE PRECISION ANRM,BIGNUM,EPS,RMAX,RMIN,SAFMIN,SIGMA,
     $SMLNUM
*     ..
*     .. External Functions ..
      LOGICAL GAL_LSAME
      INTEGER GAL_ILAENV
      DOUBLE PRECISION GAL_DLAMCH,GAL_DLANSY
      EXTERNAL GAL_LSAME,GAL_ILAENV,GAL_DLAMCH,GAL_DLANSY
*     ..
*     .. External Subroutines ..
      EXTERNAL GAL_DLASCL,GAL_DORGTR,GAL_DSCAL,
     $GAL_DSTEQR,GAL_DSTERF,GAL_DSYTRD,
     $GAL_XERBLA
*     ..
*     .. Intrinsic Functions ..
      INTRINSIC MAX,SQRT
*     ..
*     .. Executable Statements ..
*
*     Test the input parameters.
*
      WANTZ=GAL_LSAME(JOBZ,'V')
      LOWER=GAL_LSAME(UPLO,'L')
      LQUERY=(LWORK.EQ.-1)
*
      INFO=0
      IF(.NOT.(WANTZ.OR.GAL_LSAME(JOBZ,'N')))THEN
      INFO=-1
      ELSE IF(.NOT.(LOWER.OR.GAL_LSAME(UPLO,'U')))THEN
      INFO=-2
      ELSE IF(N.LT.0)THEN
      INFO=-3
      ELSE IF(LDA.LT.MAX(1,N))THEN
      INFO=-5
      END IF
*
      IF(INFO.EQ.0)THEN
      NB=GAL_ILAENV(1,'GAL_DSYTRD',UPLO,N,-1,-1,-1)
      LWKOPT=MAX(1,(NB+2)*N)
      WORK(1)=LWKOPT
*
      IF(LWORK.LT.MAX(1,3*N-1).AND..NOT.LQUERY)
     $INFO=-8
      END IF
*
      IF(INFO.NE.0)THEN
      CALL GAL_XERBLA('GAL_DSYEV',-INFO)
      RETURN
      ELSE IF(LQUERY)THEN
      RETURN
      END IF
*
*     Quick return if possible
*
      IF(N.EQ.0)THEN
      RETURN
      END IF
*
      IF(N.EQ.1)THEN
      W(1)=A(1,1)
      WORK(1)=2
      IF(WANTZ)
     $A(1,1)=ONE
      RETURN
      END IF
*
*     Get machine constants.
*
      SAFMIN=GAL_DLAMCH('SAFEMINIMUM')
      EPS=GAL_DLAMCH('PRECISION')
      SMLNUM=SAFMIN/EPS
      BIGNUM=ONE/SMLNUM
      RMIN=SQRT(SMLNUM)
      RMAX=SQRT(BIGNUM)
*
*     Scale matrix to allowable range, if necessary.
*
      ANRM=GAL_DLANSY('M',UPLO,N,A,LDA,WORK)
      ISCALE=0
      IF(ANRM.GT.ZERO.AND.ANRM.LT.RMIN)THEN
      ISCALE=1
      SIGMA=RMIN/ANRM
      ELSE IF(ANRM.GT.RMAX)THEN
      ISCALE=1
      SIGMA=RMAX/ANRM
      END IF
      IF(ISCALE.EQ.1)
     $CALL GAL_DLASCL(UPLO,0,0,ONE,SIGMA,N,N,A,LDA,INFO)
*
*     Call DSYTRD to reduce symmetric matrix to tridiagonal form.
*
      INDE=1
      INDTAU=INDE+N
      INDWRK=INDTAU+N
      LLWORK=LWORK-INDWRK+1
      CALL GAL_DSYTRD(UPLO,N,A,LDA,W,WORK(INDE),WORK(INDTAU),
     $WORK(INDWRK),LLWORK,IINFO)
*
*     For eigenvalues only, call DSTERF.  For eigenvectors, first call
*     DORGTR to generate the orthogonal matrix, then call DSTEQR.
*
      IF(.NOT.WANTZ)THEN
      CALL GAL_DSTERF(N,W,WORK(INDE),INFO)
      ELSE 
      CALL GAL_DORGTR(UPLO,N,A,LDA,WORK(INDTAU),WORK(INDWRK),
     $LLWORK,IINFO)
      CALL GAL_DSTEQR(JOBZ,N,W,WORK(INDE),A,LDA,WORK(INDTAU),
     $INFO)
      END IF
*
*     If matrix was scaled, then rescale eigenvalues appropriately.
*
      IF(ISCALE.EQ.1)THEN
      IF(INFO.EQ.0)THEN
      IMAX=N
      ELSE 
      IMAX=INFO-1
      END IF
      CALL GAL_DSCAL(IMAX,ONE/SIGMA,W,1)
      END IF
*
*     Set WORK(1) to optimal workspace size.
*
      WORK(1)=LWKOPT
*
      RETURN
*
*     End of DSYEV
*
      END 
