module cubefit_function_spectral_gaussian
  use fit_minuit
  !
  use cubefit_messaging
  use cubefit_spectral_parameters
  use cubefit_spectral_obs
  !
  integer(kind=npar_k),parameter :: nparline = 3
  integer(kind=npar_k),parameter :: iarea   = 1
  integer(kind=npar_k),parameter :: ivelo   = 2
  integer(kind=npar_k),parameter :: ifwhm   = 3
  !
  public cubefit_function_spectral_gaussian_init, cubefit_function_spectral_gaussian_minimize
  public cubefit_function_spectral_gaussian_extract, cubefit_function_spectral_gaussian_residuals
  public cubefit_function_spectral_gaussian_npar, cubefit_function_spectral_gaussian_user2par
  public cubefit_function_spectral_gaussian_par2spec, cubefit_function_spectral_gaussian_spec2par
  public cubefit_function_spectral_gaussian_iterate,cubefit_function_spectral_gaussian_wind2par
  public cubefit_function_spectral_gaussian_flags, cubefit_function_spectral_gaussian_doprofile
  public cubefit_function_spectral_gaussian_units
  private
  !
contains
  !
  subroutine cubefit_function_spectral_gaussian_init(par,obs,minuit,error)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    type(spectral_pars_t), intent(inout) :: par
    type(spectral_obs_t),  intent(in)    :: obs
    type(fit_minuit_t),    intent(inout) :: minuit
    logical,               intent(inout) :: error
    !
    real(kind=coor_k) :: vsup,vinf
    real(kind=sign_k) :: val,area,ymin,ymax
    character(len=mess_l) :: mess
    integer(kind=4) :: ipar,iline
    integer(kind=chan_k) :: ichan
    character(len=*), parameter :: rname='SPECTRAL>GAUSSIAN>INIT'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')    
    !     
    ! Starting values
    if (par%nline.eq.0) then ! Automatic guess
       par%leaders(:) = 0
       par%flag(:,:) = 0
       par%errs(:) = 0
       ymax=0.
       ymin=0.
       area=0.
       vinf=minval(obs%spec%v)
       vsup=maxval(obs%spec%v)
       do ichan=obs%ifirst+1,obs%ilast-1
          if (obs%wfit(ichan).ne.0) then
             val = (obs%spec%t(ichan)+obs%wfit(ichan-1)*obs%spec%t(ichan-1)+ &
                  obs%wfit(ichan+1)*obs%spec%t(ichan+1))/(1+obs%wfit(ichan-1)+obs%wfit(ichan+1))
             if (val.ge.ymax) then
                ymax = val
                vsup = obs%spec%v(ichan)
             endif
             if (val.le.ymin) then
                ymin = val
                vinf = obs%spec%v(ichan)
             endif
             area=area+val*abs((obs%spec%v(ichan+1)-obs%spec%v(ichan-1)))
          endif
       enddo
       area = area*0.5
       if (abs(ymin).lt.abs(ymax)) then
          par%pars(ivelo)=vsup
          par%pars(iarea)=ymax
       else
          par%pars(ivelo)=vinf
          par%pars(iarea)=ymin
       endif
       par%pars(ifwhm)=abs(area/par%pars(iarea)/1.064467)
       if (par%pars(ifwhm).lt.obs%deltav) par%pars(ifwhm) = obs%deltav
       par%pars(iarea)=area
       minuit%nu=nparline
    else ! Manipulate initial guesses from user
       minuit%nu=3*par%nline
       ipar=0
       do iline=1,par%nline
          !
          ! That is quite tricky, since the area is the product of the original
          ! intensity by the width. But for dependant gaussian, it is more subtle
          if (par%flag(iline,iarea).eq.3) then
             continue
          elseif (par%flag(iline,ifwhm).eq.3) then
             par%pars(ipar+iarea) = par%pars(ipar+iarea)*par%pars(ipar+ifwhm)*1.064467
             par%pars(ipar+iarea) = par%pars(ipar+iarea)*par%pars(3*par%leaders(ifwhm))    !
          else
             ! Area=amp*fwhm*sqrt(2pi/8log2)
             par%pars(ipar+iarea) = par%pars(ipar+iarea)*par%pars(ipar+ifwhm)*1.064467 
          endif
          ipar=ipar+3
       enddo
    endif
    minuit%nu=minuit%nu+3
    !
    ! User feedback on par%parsameters used, useful for debug only...
    call cubefit_message(fitseve%others,rname,'Input Parameters:  Area Position Fwhm')
    ipar = 0
    do iline=1,max(par%nline,1)
       write (mess,1002) par%pars(ipar+iarea),par%pars(ipar+ivelo),par%pars(ipar+ifwhm)
       call cubefit_message(fitseve%others,rname,mess)
       ipar=ipar+nparline
    enddo
    !
    ! Set up Parameters for Major Variables
    !
    ! Velocities
    if (par%leaders(ivelo).eq.0) then 
       minuit%u(2)=0.
       minuit%werr(2)=0.
    else
       minuit%u(2)=par%pars(3*par%leaders(ivelo)-1)
       if (par%flag(par%leaders(ivelo),ivelo).eq.4) then
          minuit%werr(2)=0.
       else
          minuit%werr(2)=obs%deltav
          minuit%alim(2)=minuit%u(2)-0.15*obs%spec%n*obs%deltav
          minuit%blim(2)=minuit%u(2)+0.15*obs%spec%n*obs%deltav
       endif
    endif
    !
    ! Line Widths
    if (par%leaders(ifwhm).eq.0) then 
       minuit%u(3)=1./1.665109           ! 1 / (2*SQRT(LN(2)))
       minuit%werr(3)=0.
    else
       minuit%u(3)=abs(par%pars(3*par%leaders(ifwhm)))/1.665109
       if (par%flag(par%leaders(ifwhm),ifwhm).eq.4) then
          minuit%werr(3)=0.
       else
          minuit%werr(3)=obs%deltav
          minuit%alim(3)=0.25*obs%deltav
          minuit%blim(3)=0.75*obs%spec%n*obs%deltav
       endif
    endif
    !
    ! Areas
    if (par%leaders(iarea).eq.0) then 
       minuit%u(1)=0.564189584           ! 1 / SQRT(PI)
       minuit%werr(1)=0.
    else
       minuit%u(1)=par%pars(3*par%leaders(iarea)-2)
       if (par%flag(par%leaders(iarea),iarea).eq.4) then
          minuit%werr(1)=0.
       else
          minuit%werr(1)=obs%sigbase*obs%deltav
          if (minuit%u(1).ne.0.) then
             minuit%alim(1)=dmin1(0.d0,10*minuit%u(1))
             minuit%blim(1)=dmax1(0.d0,10*minuit%u(1))
          else
             minuit%lcode(1)=1
          endif
       endif
    endif
    !
    ! Set up parameters for Secondary Variables
    ipar=4
    do iline=1,max(par%nline,1)
       ! Area
       minuit%u(ipar)=par%pars(ipar-nparline)
       if (par%flag(iline,iarea).eq.0 .or. par%nline.eq.0) then
          minuit%werr(ipar)  =  max(obs%sigbase,obs%sigline)*obs%deltav
          if (par%errs(ipar-nparline).ne.0) minuit%werr(ipar)=par%errs(ipar-nparline)
          if (minuit%u(ipar).ne.0.) then
             minuit%alim(ipar)=dmin1(0.d0,10*minuit%u(ipar))
             minuit%blim(ipar)=dmax1(0.d0,10*minuit%u(ipar))
          else
             minuit%lcode(ipar)=1             ! No Boundaries if minuit%u(ipar)=0.
          endif
       else
          minuit%werr(ipar)=0.
          if (iline.eq.par%leaders(iarea)) then
             minuit%u(ipar)=0.564189584
          else
             minuit%u(ipar)=minuit%u(ipar)*0.564189584
          endif
       endif
       ipar=ipar+1
       !
       ! Velocity
       minuit%u(ipar)=par%pars(ipar-nparline)
       if (par%flag(iline,ivelo).eq.0 .or. par%nline.eq.0) then
          minuit%werr(ipar)=obs%deltav
          if (par%errs(ipar-nparline).ne.0) minuit%werr(ipar)=par%errs(ipar-nparline)
          minuit%alim(ipar)=minuit%u(ipar)-0.15*obs%spec%n*obs%deltav
          minuit%blim(ipar)=minuit%u(ipar)+0.15*obs%spec%n*obs%deltav
       else
          minuit%werr(ipar)=0.
          if (iline.eq.par%leaders(ivelo)) minuit%u(ipar)=0.
       endif
       ipar=ipar+1
       !
       ! Line Width
       minuit%u(ipar)=abs(par%pars(ipar-nparline))
       if (par%flag(iline,ifwhm).eq.0 .or. par%nline.eq.0) then
          minuit%werr(ipar)=obs%deltav
          if (par%errs(ipar-nparline).ne.0) minuit%werr(ipar)=par%errs(ipar-nparline)
          minuit%alim(ipar)=obs%deltav
          minuit%blim(ipar)=0.75*obs%spec%n*obs%deltav
       else
          minuit%werr(ipar)=0.
          if (iline.eq.par%leaders(ifwhm)) minuit%u(ipar)=1.
       endif
       ipar=ipar+1
    enddo
    !
1002 format((3(1pg10.3)))
  end subroutine cubefit_function_spectral_gaussian_init
  !
  subroutine cubefit_function_spectral_gaussian_minimize(npar,grad,chi2,pars,iflag,obs)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=npar_k),   intent(in)    :: npar        ! Number of parameters
    real(kind=grad_k),      intent(out)   :: grad(npar)  ! Gradientes
    real(kind=chi2_k),      intent(out)   :: chi2        ! chi squared
    real(kind=para_k),      intent(in)    :: pars(npar)  ! Parameter values
    integer(kind=4),        intent(in)    :: iflag       ! Code operation
    type(spectral_obs_t),   intent(inout) :: obs         ! Observation
    !
    integer(kind=chan_k) :: ichan,nline
    real(kind=coor_k) :: xvel
    real(kind=sign_k), allocatable :: parsarea(:),parsvelo(:),parsfwhm(:)
    real(kind=8),      allocatable :: argexp(:)
    real(kind=chi2_k), allocatable :: parschi2(:)
    real(kind=grad_k), allocatable :: parsgrad(:)
    real(kind=8) :: arg
    real(kind=chi2_k) :: chi2loc
    real(kind=para_K) :: area,velo,fwhm
    real(kind=grad_k) :: gradarea,gradvelo,gradfwhm
    integer(kind=line_k) :: iline
    logical :: dograd
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>MINIMIZE'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    if (iflag.eq.minuit_rms_flag) then
       call obs%sigma(cubefit_function_spectral_gaussian_profile,.false.)
       return
    endif
    !
    dograd = iflag.eq.minuit_gra_flag
    nline = max(obs%par%nline,1)
    allocate(parsarea(nline),parsvelo(nline),parsfwhm(nline),parschi2(nline),argexp(nline),&
         parsgrad(nline*nparline))
    !
    ! Compute gaussians
    chi2 = 0.
    parsgrad = 0.
    gradarea = 0.
    gradvelo = 0.
    gradfwhm = 0.
    parsarea = 0.
    parsvelo = 0.
    parsfwhm = 0.
    area = pars(iarea)
    velo = pars(ivelo)
    fwhm = pars(ifwhm)       
    do iline=1,nline
       parsarea(iline) = pars(nparline*iline+iarea) * area
       parsvelo(iline) = pars(nparline*iline+ivelo) + velo
       parsfwhm(iline) = pars(nparline*iline+ifwhm) * fwhm
    enddo
    !
    do ichan=obs%ifirst,obs%ilast
       if (obs%wfit(ichan).gt.0) then 
          xvel = obs%spec%v(ichan)
          parschi2 = 0.
          argexp   = 0.
          argexp(1:nline) = (xvel - parsvelo(1:nline)) / parsfwhm(1:nline)
          where(argexp(1:nline).le.4.)
             parschi2(1:nline) = exp(-argexp(1:nline)**2)
          end where
          chi2loc = sum(parschi2(1:nline)*parsarea(1:nline)/parsfwhm(1:nline))
          !
          ! Compute chi^2 (No ponderation, WFIT is ignored)
          chi2loc =  (chi2loc - obs%spec%t(ichan))
          chi2 = chi2 + chi2loc**2
          !
          ! Compute Gradients
          if (dograd) then
             chi2loc = 2. * chi2loc
             do iline=1,nline
                if (parschi2(iline).ne.0.) then
                   arg                                 = parschi2(iline) * chi2loc / parsfwhm(iline)
                   parsgrad(iarea+nparline*(iline-1))  = parsgrad(iarea+nparline*(iline-1)) + arg
                   gradarea                            = gradarea + arg*parsarea(iline)
                   !
                   arg                                 = parsarea(iline)*arg/parsfwhm(iline)
                   parsgrad(ifwhm+nparline*(iline-1))  = parsgrad(ifwhm+nparline*(iline-1)) - arg
                   gradfwhm                            = gradfwhm - arg*parsfwhm(iline)
                   !
                   arg                                 = arg*argexp(iline)*2.
                   parsgrad(ivelo+nparline*(iline-1))  = parsgrad(ivelo+nparline*(iline-1)) + arg
                   parsgrad(ifwhm+nparline*(iline-1))  = parsgrad(ifwhm+nparline*(iline-1)) + arg*argexp(iline)
                   gradvelo                            = gradvelo + arg
                   gradfwhm                            = gradfwhm + arg*argexp(iline)*parsfwhm(iline)
                endif
             enddo
          endif
       endif
    enddo
    !
    ! Setup values and return
    grad(iarea)=gradarea/area
    grad(ivelo)=gradvelo
    grad(ifwhm)=gradfwhm/fwhm
    !
    do iline=1,nline
       grad(nparline*(iline-1)+iarea) = parsgrad(iarea+nparline*(iline-1)) *area
       grad(nparline*(iline-1)+ivelo) = parsgrad(ivelo+nparline*(iline-1))
       grad(nparline*(iline-1)+ifwhm) = parsgrad(ifwhm+nparline*(iline-1)) *fwhm
    enddo
  end subroutine cubefit_function_spectral_gaussian_minimize
  !
  subroutine cubefit_function_spectral_gaussian_extract(minuit,obs,par,error)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    type(fit_minuit_t),    intent(inout) :: minuit
    type(spectral_obs_t),  intent(inout) :: obs
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=npar_k) :: ipar
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>EXTRACT'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    ! Update Parameters to compute RMS
    ipar=0
    do iline=1,max(par%nline,1)
       par%pars(ipar+iarea)=minuit%u(ipar+3+iarea)*minuit%u(1)*1.7724538
       par%pars(ipar+ivelo)=minuit%u(ipar+3+ivelo)+minuit%u(2)
       par%pars(ipar+ifwhm)=minuit%u(ipar+3+ifwhm)*minuit%u(3)*1.665109
       par%errs(ipar+iarea)=minuit%werr(ipar+iarea+3)
       if (iline.eq.par%leaders(iarea)) par%errs(ipar+iarea)=minuit%werr(1)
       par%errs(ipar+ivelo)=minuit%werr(ipar+ivelo+3)
       if (iline.eq.par%leaders(ivelo)) par%errs(ipar+ivelo)=minuit%werr(2)
       par%errs(ipar+ifwhm)=minuit%werr(ipar+ifwhm+3)
       if (iline.eq.par%leaders(ifwhm)) par%errs(ipar+ifwhm)=minuit%werr(3)
       ipar=ipar+nparline
    enddo
  end subroutine cubefit_function_spectral_gaussian_extract
  !
  subroutine cubefit_function_spectral_gaussian_residuals(obs,spec,error)
    use cubemain_spectrum_real
    !------------------------------------------------------------------------
    ! Compute the residuals of a gaussian fit
    !------------------------------------------------------------------------
    type(spectral_obs_t), intent(inout) :: obs          
    type(spectrum_t),     intent(inout) :: spec         
    logical,              intent(inout) :: error
    !
    integer(kind=chan_k) :: ichan
    real(kind=coor_k) :: xvel
    real(kind=sign_k) :: pred
    character(len=*), parameter :: rname = "SPECTRAL>GAUSSIAN>RESIDUALS"
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    do ichan=1, spec%n
       xvel = obs%spec%v(ichan)
       pred = cubefit_function_spectral_gaussian_profile(obs,xvel,0)
       spec%t(ichan) = obs%spec%t(ichan) - pred
    end do
  end subroutine cubefit_function_spectral_gaussian_residuals
  !
  subroutine cubefit_function_spectral_gaussian_doprofile(iline,obs,spec,error)
    use cubemain_spectrum_real
    !------------------------------------------------------------------------
    ! Compute the profile of a gaussian fit
    !------------------------------------------------------------------------
    integer(kind=line_k), intent(in)    :: iline
    type(spectral_obs_t), intent(inout) :: obs          
    type(spectrum_t),     intent(inout) :: spec         
    logical,              intent(inout) :: error
    !
    integer(kind=chan_k) :: ichan
    real(kind=coor_k) :: xvel
    real(kind=sign_k) :: pred
    character(len=*), parameter :: rname = "SPECTRAL>GAUSSIAN>DOPROFILE"
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    do ichan=1, spec%n
       xvel = obs%spec%v(ichan)
       pred = cubefit_function_spectral_gaussian_profile(obs,xvel,iline)
       spec%t(ichan) = pred
    end do
  end subroutine cubefit_function_spectral_gaussian_doprofile
  !
  subroutine cubefit_function_spectral_gaussian_user2par(flag,pars,par,error)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=flag_k),  intent(in)    :: flag(:)
    real(kind=para_k),     intent(in)    :: pars(:)
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=npar_k) :: ipar,jpar
    integer(kind=line_k) :: iline
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>USER2PAR'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    par%leaders(:) = 0
    par%flag(:,:)  = 0
    par%errs(:)    = 0.
    !
    jpar = 1
    do iline=1,par%nline
       do ipar=1,nparline
          par%flag(iline,ipar) = flag(jpar)
          par%pars(jpar)       = pars(jpar)
          jpar=jpar+1
       enddo ! ipar
    enddo ! iline
    !
    call par%check_line(iarea,error)
    if (error) return
    call par%check_line(ivelo,error)
    if (error) return
    call par%check_line(ifwhm,error)
    if (error) return    
    if (par%leaders(iarea).ne.0 .and. par%leaders(ifwhm).ne.0 .and. &
         par%leaders(iarea).ne.par%leaders(ifwhm)) then
       call cubefit_message(seve%e,rname,'Parameters flags are inconsistent')
       error = .true.
       return
    endif
  end subroutine cubefit_function_spectral_gaussian_user2par
  !
  subroutine cubefit_function_spectral_gaussian_par2spec(par,spec,error)
    use cubemain_spectrum_real
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(spectral_pars_t), intent(in)    :: par
    type(spectrum_t),      intent(inout) :: spec
    logical,               intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=chan_k) :: ichan
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>PAR2SPEC'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    ichan = spec_ndaps
    do iline=1,max(par%nline,1)
       ichan = ichan+1
       spec%t(ichan) = par%flag(iline,iarea) 
       ichan = ichan+1
       spec%t(ichan) = par%pars((iline-1)*nparline+iarea)
       ichan = ichan+1
       spec%t(ichan) = par%errs((iline-1)*nparline+iarea)
       ichan = ichan+1
       spec%t(ichan) = par%flag(iline,ivelo)
       ichan = ichan+1
       spec%t(ichan) = par%pars((iline-1)*nparline+ivelo)
       ichan = ichan+1
       spec%t(ichan) = par%errs((iline-1)*nparline+ivelo)
       ichan = ichan+1
       spec%t(ichan) = par%flag(iline,ifwhm)
       ichan = ichan+1
       spec%t(ichan) = par%pars((iline-1)*nparline+ifwhm)
       ichan = ichan+1
       spec%t(ichan) = par%errs((iline-1)*nparline+ifwhm)
    enddo
  end subroutine cubefit_function_spectral_gaussian_par2spec
  !
  subroutine cubefit_function_spectral_gaussian_spec2par(spec,par,error)
    use cubemain_spectrum_real
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(spectrum_t),      intent(in)    :: spec
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=chan_k) :: ichan
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>SPEC2PAR'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    ichan = spec_ndaps
    do iline=1,max(par%nline,1)
       ichan = ichan+1
       par%flag(iline,iarea)              = nint(spec%t(ichan),flag_k)
       ichan = ichan+1                     
       par%pars((iline-1)*nparline+iarea) = spec%t(ichan) 
       ichan = ichan+1                     
       par%errs((iline-1)*nparline+iarea) = spec%t(ichan) 
       ichan = ichan+1                     
       par%flag(iline,ivelo)              = nint(spec%t(ichan),flag_k)
       ichan = ichan+1                     
       par%pars((iline-1)*nparline+ivelo) = spec%t(ichan) 
       ichan = ichan+1                     
       par%errs((iline-1)*nparline+ivelo) = spec%t(ichan) 
       ichan = ichan+1                     
       par%flag(iline,ifwhm)              = nint(spec%t(ichan),flag_k)
       ichan = ichan+1                     
       par%pars((iline-1)*nparline+ifwhm) = spec%t(ichan) 
       ichan = ichan+1                     
       par%errs((iline-1)*nparline+ifwhm) = spec%t(ichan) 
    enddo
  end subroutine cubefit_function_spectral_gaussian_spec2par
  !
  subroutine cubefit_function_spectral_gaussian_iterate(par,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    integer(kind=npar_k) :: ipar
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>ITERATE'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    ipar = 0
    do iline=1,par%nline
       par%pars(ipar+iarea) = par%pars(ipar+iarea)/par%pars(ipar+ifwhm)/1.064467
       if (par%flag(iline,ipar+iarea).eq.3) &
            par%pars(ipar+iarea)=par%pars(ipar+iarea)/par%pars(nparline*(par%leaders(iarea)-1)+iarea)
       if (par%flag(iline,ipar+ivelo).eq.3) &
            par%pars(ipar+ivelo)=par%pars(ipar+ivelo)/par%pars(nparline*(par%leaders(ivelo)-1)+ivelo)
       if (par%flag(iline,ipar+ifwhm).eq.3) &
            par%pars(ipar+ifwhm)=par%pars(ipar+ifwhm)/par%pars(nparline*(par%leaders(ifwhm)-1)+ifwhm)    
       ipar = ipar+nparline
    enddo
  end subroutine cubefit_function_spectral_gaussian_iterate
  !
  subroutine cubefit_function_spectral_gaussian_wind2par(obs,wind,par,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(spectral_obs_t),  intent(in)    :: obs
    integer(kind=chan_k),  intent(in)    :: wind(:)
    type(spectral_pars_t), intent(inout) :: par
    logical,               intent(inout) :: error
    !
    integer(kind=chan_k) :: first,last
    integer(kind=line_k) :: iline
    real(kind=para_k) :: area,velo,fwhm
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>WIND2PAR'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    par%errs(:)     = 0
    par%leaders(:)  = 0
    do iline=1,par%nline
       first = wind(2*iline-1)
       last  = wind(2*iline)
       par%flag(iline,:)   = 0
       !
       call obs%est_gauss(first,last,area,velo,fwhm,error)
       if (error) return
       if (fwhm.lt.obs%deltav) fwhm = obs%deltav
       par%pars((iline-1)*nparline+iarea) = area 
       par%pars((iline-1)*nparline+ivelo) = velo 
       par%pars((iline-1)*nparline+ifwhm) = fwhm
    end do
  end subroutine cubefit_function_spectral_gaussian_wind2par
  !
  !----------------------------------------------------------------------
  !
  function cubefit_function_spectral_gaussian_profile(obs,xvel,iline) result(gauss)
    !----------------------------------------------------------------------
    ! Compute the value of a gaussian or a sum of gaussians at xvel
    !----------------------------------------------------------------------
    type(spectral_obs_t),   intent(in)    :: obs         ! Observation
    real(kind=coor_k),      intent(in)    :: xvel        ! Coordinate to compute value
    integer(kind=line_K),   intent(in)    :: iline       ! Which gaussian is to be computed
    !
    real(kind=sign_k) :: gauss
    !
    integer(kind=line_k) :: ifirst,ilast,jline
    integer(kind=npar_k) :: ipar 
    real(kind=4) :: arg
    !
    gauss=0.
    arg=0.
    ! dobase = .false.
    !
    if (iline.eq.0) then
       ifirst = 1
       ilast = max(obs%par%nline,1)
    else
       ifirst = iline
       ilast = iline
    endif
    !
    do jline = ifirst,ilast
       ipar = nparline*(jline-1)
       arg = abs((xvel-obs%par%pars(ipar+ivelo)) / obs%par%pars(ipar+ifwhm)*1.665109)
       if (arg.lt.4.) then
          gauss = gauss + exp(-arg**2)*obs%par%pars(ipar+iarea)/obs%par%pars(ipar+ifwhm)/1.064467
       endif
    enddo
  end function cubefit_function_spectral_gaussian_profile
  !
  subroutine cubefit_function_spectral_gaussian_flags(ipar,lineflag,parflag,error)
    use cubedag_allflags
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=npar_k), intent(in)    :: ipar
    type(flag_t),         intent(out)   :: lineflag
    type(flag_t),         intent(out)   :: parflag
    logical,              intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>FLAGS'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    iline  = ipar/nparline+1
    select case(mod(ipar,nparline))
    case(1)
       parflag = flag_area
    case(2)
       parflag = flag_velocity
    case(0)
       parflag = flag_fwhm
       iline  = iline-1
    end select
    call cubefit_line2flag(iline,lineflag,error)
    if (error)  return
  end subroutine cubefit_function_spectral_gaussian_flags
  !
  subroutine cubefit_function_spectral_gaussian_units(ipar,unit,error)
    use cubetools_unit
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=npar_k), intent(in)    :: ipar
    character(len=*),     intent(out)   :: unit
    logical,              intent(inout) :: error
    !
    integer(kind=line_k) :: iline
    character(len=*), parameter :: rname = 'SPECTRAL>GAUSSIAN>UNITS'
    !
    call cubefit_message(fitseve%trace,rname,'Welcome')
    !
    iline  = ipar/nparline+1
    select case(mod(ipar,nparline))
    case(1)
       unit = strg_add//unit_velo_name(1)
    case(2)
       unit = unit_velo_name(1) ! km/s
    case(0)
       unit = unit_velo_name(1) ! km/s
    end select
  end subroutine cubefit_function_spectral_gaussian_units
  !
  function cubefit_function_spectral_gaussian_npar(nline) result(npar)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    integer(kind=line_k), intent(in) :: nline
    !
    integer(kind=npar_k) :: npar
    !
    npar = nparline*nline
  end function cubefit_function_spectral_gaussian_npar
end module cubefit_function_spectral_gaussian
