!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubemain_baseline
  use cubetools_structure
  use cube_types
  use cubeadm_cubeid_types
  use cubemain_messaging
  use cubemain_windowing
  use cubemain_range
  use cubemain_spectrum_real
  ! use cubemain_auxiliary
  !
  public :: baseline
  public :: cubemain_baseline_command
  private
  !
  integer(kind=code_k), parameter :: code_medi = 1
  integer(kind=code_k), parameter :: code_poly = 2
  integer(kind=code_k), parameter :: code_wave = 3
  !
  type :: baseline_comm_t
     type(option_t), pointer :: comm  
     type(range_opt_t)       :: range     
     type(option_t), pointer :: median    
     type(option_t), pointer :: wave      
     type(option_t), pointer :: poly      
     ! type(option_t), pointer :: mask
   contains
     procedure, public  :: register     => cubemain_baseline_register
     procedure, private :: parse        => cubemain_baseline_parse
     procedure, private :: parse_degree => cubemain_baseline_parse_degree
     procedure, private :: parse_poly   => cubemain_baseline_parse_poly
     procedure, private :: main         => cubemain_baseline_main
  end type baseline_comm_t
  type(baseline_comm_t) :: baseline
  !
  integer(kind=4), parameter :: icube = 1
  type baseline_user_t
     type(cubeid_user_t)    :: cubeids
     ! type(auxiliary_user_t) :: mask
     type(range_array_t)    :: range               ! Range(s) to be ignored when fitting a baseline
     logical                :: dorange = .false.   ! Was the /range option present
     logical                :: domedi              ! Was the /median option present
     logical                :: dowave              ! Was the /wavelet option present
     logical                :: dopoly              ! Was the /polynomial option present
     integer(kind=4)        :: degree = -1         ! [---] degree for the wavelet
     integer(kind=4)        :: npol = 0            ! [---] Number of polynomials to be used
     character(len=argu_l)  :: trkind              ! Unit kind for the transitions given
     integer(kind=4),   allocatable :: degrees(:) ! [---] degree(s) for the polynomials
     real(kind=coor_k), allocatable :: trans(:)   ! [mhz|kms] transition between different polynomials
   contains
     procedure, private :: toprog => cubemain_baseline_user_toprog
  end type baseline_user_t
  type baseline_prog_t
     integer(kind=code_k)  :: method                 ! Basilining method
     logical               :: domask                 ! Use a mask
     integer(kind=4)       :: degree                 ! [---] degree for the wavelet
     type(window_array_t)  :: wind                   ! Window(s) to be ignored when fitting a baseline
     type(cube_t), pointer :: cube                   ! Input cube
     type(cube_t), pointer :: mask                   ! Input Mask
     type(cube_t), pointer :: baseline               ! Output baseline
     type(cube_t), pointer :: line                   ! Output baselined cube
     integer(kind=4)       :: npol = 0               ! [---] Number of polynomials to be used
     integer(kind=4),      allocatable :: degrees(:) ! [---] degree(s) for the polynomials 
     integer(kind=chan_k), allocatable :: trans(:)   ! [mhz|kms] transition between different polynomials
   contains
     procedure, private :: header          => cubemain_baseline_prog_header
     procedure, private :: data            => cubemain_baseline_prog_data
     procedure, private :: median_loop     => cubemain_baseline_prog_median_loop
     procedure, private :: domedian        => cubemain_baseline_prog_domedian
     procedure, private :: wavelet_loop    => cubemain_baseline_prog_wavelet_loop
     procedure, private :: dowavelet       => cubemain_baseline_prog_dowavelet
     procedure, private :: polynomial_loop => cubemain_baseline_prog_polynomial_loop
     procedure, private :: dopolynomial    => cubemain_baseline_prog_dopolynomial
  end type baseline_prog_t
  !
contains
  !
  subroutine cubemain_baseline_command(line,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    character(len=*), intent(in)    :: line
    logical,          intent(inout) :: error
    !
    type(baseline_user_t) :: user
    character(len=*), parameter :: rname='BASELINE>COMMAND'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    call baseline%parse(line,user,error)
    if (error) return
    call baseline%main(user,error)
    if (error) continue
  end subroutine cubemain_baseline_command
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_baseline_register(baseline,error)
    use cubedag_allflags
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_comm_t), intent(inout) :: baseline
    logical,                intent(inout) :: error
    !
    type(cubeid_arg_t) :: cubearg
    type(standard_arg_t) :: stdarg
    character(len=*), parameter :: comm_abstract = 'Subtract a baseline from a cube'
    character(len=*), parameter :: comm_help = &
         'Three algorithms are available to compute a baseline: &
         &/MEDIAN, /WAVELET and /POLYNOMIAL. Only one of these three&
         & can be given at a time. If no algorithm option is given&
         & BASELINE defaults to /MEDIAN.'
    character(len=*), parameter :: rname='BASELINE>REGISTER'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubetools_register_command(&
         'BASELINE','[cube]',&
         comm_abstract,&
         comm_help,&
         cubemain_baseline_command,&
         baseline%comm,error)
    if (error) return
    call cubearg%register( &
         'CUBE', &
         'Input cube',  &
         strg_id,&
         code_arg_optional,  &
         [flag_cube], &
         error)
    if (error) return
    !
    call baseline%range%register(&
         'RANGE',&
         'Define the signal velocity range(s)', &
         range_is_multiple,error)
    if (error) return
    !
    call cubetools_register_option(&
         'MEDIAN','',&
         'Use a median running filter to define the baseline',&
         strg_id,&
         baseline%median,error)
    if (error) return
    !
    call cubetools_register_option(&
         'WAVELET','degree',&
         'Use a wavelet filter to define the baseline',&
         strg_id,&
         baseline%wave,error)
    if (error) return
    call stdarg%register( &
         'degree',  &
         'Wavelet degree', &
         strg_id,&
         code_arg_mandatory, &
         error)
    if (error) return
    !
    call cubetools_register_option(&
         'POLYNOMIAL','deg1 [tr12 deg2 [... [trij degj [kind]]]]',&
         'Fit Chebyshev polynomials to define the baseline',&
         strg_id,&
         baseline%poly,error)
    if (error) return
    call stdarg%register( &
         'deg1',  &
         '[First] Polynomial degree', &
         'Degree for the first polynomial',&
         code_arg_mandatory, error)
    if (error) return
    call stdarg%register( &
         'tr12',  &
         'Transition between first and second polynomial', &
         strg_id,&
         code_arg_unlimited, &
         error)
    if (error) return
    call stdarg%register( &
         'deg2',  &
         'Degree for the second polynomial', &
         strg_id,&
         code_arg_unlimited, &
         error)
    if (error) return
    call stdarg%register( &
         'trij',  &
         'Transition between ith and jth polynomial', &
         strg_id,&
         code_arg_unlimited, &
         error)
    if (error) return
    call stdarg%register( &
         'degj',  &
         'Degree for the jth polynomial', &
         strg_id,&
         code_arg_unlimited, &
         error)
    if (error) return
    call stdarg%register( &
         'kind',  &
         'Kind of the transition(s) between polynomials', &
         'Available kinds: CHANNEL, VELOCITY (default), or FREQUENCY',&
         code_arg_optional, &
         error)
    if (error) return
    !
    ! call cubemain_auxiliary_register(&
    !      'MASK',&
    !      'Mask with channels to be ignored when fitting the baseline',&
    !      'mask',&
    !      [flag_mask],&
    !      baseline%mask,error)
    ! if (error) return
  end subroutine cubemain_baseline_register
  !
  subroutine cubemain_baseline_parse(baseline,line,user,error)
    !----------------------------------------------------------------------
    ! BASELINE cubname
    ! /RANGE vfirst vlast
    ! /MEDIAN (default)
    ! /WAVELET degree
    ! /POLYNOMIAL deg1 [tr12 deg2 [... degi trij degj] [unit]]
    !----------------------------------------------------------------------
    class(baseline_comm_t), intent(in)    :: baseline
    character(len=*),       intent(in)    :: line
    type(baseline_user_t),  intent(out)   :: user
    logical,                intent(inout) :: error
    !
    character(len=*),parameter :: rname='BASELINE>PARSE'
    !
    logical :: combi
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_parse(line,baseline%comm,user%cubeids,error)
    if (error) return
    !
    call baseline%range%parse(line,user%dorange,user%range,error)
    if (error) return
    !
    call baseline%median%present(line,user%domedi,error)
    if (error) return
    call baseline%wave%present(line,user%dowave,error)
    if (error) return
    call baseline%poly%present(line,user%dopoly,error)
    if (error) return
    combi = ((user%domedi.and.user%dowave).or.(user%domedi.and.user%dopoly).or.(user%dowave.and.user%dopoly))
    !
    if ((.not.user%domedi).and.(.not.user%dowave).and.(.not.user%dopoly)) then
       user%domedi = .true.
    elseif (combi) then
       call cubemain_message(seve%e,rname,'/MEDIAN, /WAVELET, and /POLYNOMIAL are exclusive options')
       error = .true.
       return
    else
       if (user%dowave) then
          call baseline%parse_degree(line,user,error)
          if (error) return
       else if (user%dopoly) then
          call baseline%parse_poly(line,user,error)
          if (error) return
       else
          user%degree = -1
       endif
    endif
    !
    ! call cubemain_auxiliary_parse(line,baseline%mask,user%mask,error)
    ! if (error) return
  end subroutine cubemain_baseline_parse
  !
  subroutine cubemain_baseline_parse_degree(baseline,line,user,error)
    !----------------------------------------------------------------------
    ! BASELINE cubname
    ! /WAVELET degree 
    !----------------------------------------------------------------------
    class(baseline_comm_t), intent(in)    :: baseline
    character(len=*),       intent(in)    :: line
    type(baseline_user_t),  intent(inout) :: user
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PARSE>DEGREE'
    !
    call cubetools_getarg(line,baseline%wave,1,user%degree,mandatory,error)
    if (error) return
    !
    ! *** JP Unclear to me that wavelet will work with degree.eq.0...
    if (user%degree.lt.0) then
       call cubemain_message(seve%e,rname,'Degree must be positive')
       error = .true.
       return
    endif
  end subroutine cubemain_baseline_parse_degree
  !
  subroutine cubemain_baseline_parse_poly(baseline,line,user,error)
    use gkernel_interfaces
    use cubetools_unit
    use cubetools_disambiguate
    !----------------------------------------------------------------------
    ! /POLYNOMIAL degree [tr12 deg2 [trij degj] [kind]]
    !----------------------------------------------------------------------
    class(baseline_comm_t), intent(in)    :: baseline
    character(len=*),       intent(in)    :: line
    type(baseline_user_t),  intent(inout) :: user
    logical,                intent(inout) :: error
    !
    integer(kind=4),parameter :: nkinds=3
    integer(kind=4) :: narg,ipol,ier,pos,remain
    character(len=argu_l)  :: trkind,kinds(nkinds)
    type(unit_user_t) :: unit
    real(kind=coor_k) :: transfac
    character(len=*), parameter :: rname='BASELINE>PARSE>POLY'
    data kinds/'VELOCITY','FREQUENCY','CHANNEL'/
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    narg = baseline%poly%getnarg()
    if (narg.le.1) then
       user%npol = 1
       call cubetools_getarg(line,baseline%poly,1,user%degree,mandatory,error)
       if (error) return
       if (user%degree.lt.0) then
          call cubemain_message(seve%e,rname,'Degree must be positive')
          error = .true.
          return
       end if
    else
       if (narg.lt.3) then
          call cubemain_message(seve%e,rname,'Need at least 3 arguments')
          error = .true.
          return
       endif
       remain = modulo(narg,2)
       if (remain.eq.0) then
          call cubetools_getarg(line,baseline%poly,narg,trkind,mandatory,error)
          if(error) return
          call cubetools_disambiguate_strict(trkind,kinds,pos,user%trkind,error)
          if (error) return
          user%npol = narg/2
       else
          user%trkind = kinds(1)
          user%npol = narg/2+1
       endif
       allocate(user%degrees(user%npol),user%trans(user%npol-1),stat=ier)
       if (failed_allocate(rname,'Degree arrays',ier,error)) then
          error = .true.
          return
       endif
       select case(trim(user%trkind))
       case('VELOCITY')
          call cubetools_unit_get(strg_star,code_unit_velo,unit,error)
          if (error) return
          transfac = unit%prog_per_user
       case('FREQUENCY')
          call cubetools_unit_get(strg_star,code_unit_freq,unit,error)
          if (error) return
          transfac = unit%prog_per_user
       case('CHANNEL')
          transfac = 1d0
       case default
          call cubemain_message(seve%e,rname,'Unknown transition kind '//trim(user%trkind))
          error = .true.
          return
       end select
       do ipol=1, user%npol
          call cubetools_getarg(line,baseline%poly,(ipol-1)*2+1,user%degrees(ipol),mandatory,error)
          if(error) return
          if (user%degrees(ipol).lt.0) then
             call cubemain_message(seve%e,rname,'Degree must be positive')
             error = .true.
             return
          endif
          if (ipol.lt.user%npol) then
             call cubetools_getarg(line,baseline%poly,2*ipol,user%trans(ipol),mandatory,error)
             if(error) return
             user%trans(ipol) = transfac*user%trans(ipol)
          endif
       enddo
    endif
  end subroutine cubemain_baseline_parse_poly
  !
  subroutine cubemain_baseline_main(baseline,user,error)
    use cubedag_parameters
    use cubeadm_timing
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_comm_t), intent(in)    :: baseline
    type(baseline_user_t),  intent(in)    :: user
    logical,                intent(inout) :: error
    !
    type(baseline_prog_t) :: prog
    character(len=*), parameter :: rname='BASELINE>MAIN'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call user%toprog(prog,error)
    if (error) return
    call prog%header(error)
    if (error) return
    !
    call cubeadm_timing_prepro2process()
    call prog%data(error)
    if (error) return
    call cubeadm_timing_process2postpro()
  end subroutine cubemain_baseline_main
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_baseline_user_toprog(user,prog,error)
    use gkernel_interfaces
    use cubetools_unit
    use cubeadm_get
    use cubemain_topology
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_user_t), intent(in)    :: user
    type(baseline_prog_t),  intent(out)   :: prog
    logical,                intent(inout) :: error
    !
    integer(kind=4) :: ier,itr
    type(unit_user_t) :: unit
    character(len=mess_l) :: mess
    character(len=unit_l) :: ouunit
    character(len=*), parameter :: rname='BASELINE>USER>TOPROG'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_get_header(baseline%comm,icube,user%cubeids,code_access_speset,code_read,prog%cube,error)
    if (error) return
    !
    call baseline%range%user2prog(prog%cube,user%range,prog%wind,error)
    if (error) return
    prog%domask = .false.
    !
    if (user%domedi) then
       prog%method = code_medi
    else if (user%dowave) then
       prog%method = code_wave
       prog%degree = user%degree
    else if (user%dopoly) then
       prog%method = code_poly
       prog%npol = user%npol
       allocate(prog%degrees(prog%npol),prog%trans(prog%npol+1),stat=ier)
       if (failed_allocate(rname,'Degree arrays',ier,error)) then
          error = .true.
          return
       endif
       prog%trans(1) = 0
       prog%trans(prog%npol+1) = prog%cube%head%arr%n%c
       if (prog%npol.eq.1) then
          prog%degrees(1) = user%degree          
       else
          prog%degrees(:) = user%degrees(:)
          select case(user%trkind)
          case('CHANNEL')
             do itr=2,prog%npol
                prog%trans(itr) = nint(user%trans(itr-1))
             enddo
             ouunit = 'th channel'
          case('VELOCITY')
             do itr=2,prog%npol
                call cubemain_topo_velocity2channel(prog%cube,user%trans(itr-1),prog%trans(itr),error)
                if(error) return
             enddo
             call cubetools_unit_get(strg_star,code_unit_velo,unit,error)
             if (error) return
             ouunit = unit%name
          case('FREQUENCY')
             do itr=2,prog%npol
                call cubemain_topo_frequency2channel(prog%cube,user%trans(itr-1),prog%trans(itr),error)
                if(error) return
             enddo
             call cubetools_unit_get(strg_star,code_unit_freq,unit,error)
             if (error) return
             ouunit = unit%name
          end select
          do itr=2,prog%npol
             if ((prog%trans(itr).le.1).or.(prog%trans(itr).ge.prog%cube%head%arr%n%c)) then
                write(mess,'(a,1pg14.7,x,2a)') 'Transition at ',user%trans(itr-1),trim(ouunit),&
                     ' goes beyond bounds'
                call cubemain_message(seve%e,rname,mess)
                error = .true.
                return
             endif
             if (itr.gt.1) then
                if (prog%trans(itr).le.prog%trans(itr-1)) then
                   call cubemain_message(seve%e,rname,'Transitions must be in an ascending order of channels')
                   error = .true.
                   return
                endif
             endif
          enddo
       endif
    else
       call cubemain_message(seve%e,rname,'No method to do baselining')
       error = .true.
       return
    endif
  end subroutine cubemain_baseline_user_toprog
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_baseline_prog_header(prog,error)
    use cubetools_consistency_methods
    use cubedag_allflags
    use cubeadm_clone
    use cubeadm_get
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>HEADER'
    ! logical :: prob
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    
    call cubeadm_clone_header(prog%cube,[flag_baseline,flag_base],prog%baseline,error)
    if (error) return
    call cubeadm_clone_header(prog%cube,[flag_baseline,flag_line,flag_cube],prog%line,error)
    if (error) return
    !
    ! if (user%dopoly) then
    !    if (user%mask%do) then
    !       prob = .false.
    !       call cubemain_auxiliary_user2prog(baseline%mask,code_access_speset,  &
    !            user%mask,prog%mask,error)
    !       call cubetools_consistency_grid('Input cube',prog%cube%head,'Mask',prog%mask%head,prob,error)
    !       if (error) return
    !       if (cubetools_consistency_failed(rname,prob,error)) return
    !    endif
    ! endif
    !
  end subroutine cubemain_baseline_prog_header
  !
  subroutine cubemain_baseline_prog_data(prog,error)
    use cubeadm_opened
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>DATA'
    type(cubeadm_iterator_t) :: iter
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_datainit_all(iter,error)
    if (error) return
    !$OMP PARALLEL DEFAULT(none) SHARED(prog,error) FIRSTPRIVATE(iter)
    !$OMP SINGLE
    do while (cubeadm_dataiterate_all(iter,error))
       if (error)  exit
       !$OMP TASK SHARED(prog) FIRSTPRIVATE(iter,error)
       if (.not.error) then
          if (prog%method.eq.code_medi) then 
             call prog%median_loop(iter%first,iter%last,error)
          else if (prog%method.eq.code_wave) then 
             call prog%wavelet_loop(iter%first,iter%last,error)
          else if (prog%method.eq.code_poly) then 
             call prog%polynomial_loop(iter%first,iter%last,error)
          else
             call cubemain_message(seve%e,rname,'No method to do baselining')
             error = .true.
          endif
       endif
       !$OMP END TASK
    enddo ! ie
    !$OMP END SINGLE
    !$OMP END PARALLEL
    !
  end subroutine cubemain_baseline_prog_data
  !
  subroutine cubemain_baseline_prog_median_loop(prog,first,last,error)
    use cubeadm_entryloop
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    integer(kind=entr_k),   intent(in)    :: first
    integer(kind=entr_k),   intent(in)    :: last
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>MEDIAN>LOOP'
    integer(kind=entr_k) :: ie
    type(spectrum_t) :: inspec,line,base,good
    !
    call inspec%reassociate_and_init(prog%cube,error)
    if (error) return
    !
    call line%reallocate('line',prog%cube%head%arr%n%c,error)
    if (error) return
    call base%reallocate('base',prog%cube%head%arr%n%c,error)
    if (error) return
    call good%reallocate('good',prog%cube%head%arr%n%c,error)
    if (error) return
    !
    do ie=first,last
      call cubeadm_entryloop_iterate(ie,error)
      if (error) return
      call prog%domedian(ie,inspec,good,base,line,error)
      if (error) return
    enddo
  end subroutine cubemain_baseline_prog_median_loop
  !
  subroutine cubemain_baseline_prog_domedian(prog,ie,inspec,good,base,line,error)
    use cubetools_nan
    use cubemain_spectrum_blanking
    use cubemain_statistics
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    integer(kind=entr_k),   intent(in)    :: ie
    type(spectrum_t),       intent(inout) :: inspec  ! Working buffer
    type(spectrum_t),       intent(inout) :: good    ! Working buffer
    type(spectrum_t),       intent(inout) :: base    ! Working buffer
    type(spectrum_t),       intent(inout) :: line    ! Working buffer
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>DOMEDIAN'
    !
    real(kind=sign_k) :: median
    integer(kind=chan_k) :: nmed
    !
    call inspec%get(prog%cube,ie,error)
    if (error) return
    !
    call cubemain_spectrum_unblank(inspec,good,error)
    if (error) return
    !
    if (good%n.ge.2) then
       ! VVV Nmed is taken as nmed/2+1 because this is correct for an
       ! odd number samples and a good enough comprise between
       ! performance and accuracy for an even number of samples.
       nmed = good%n/2+1
       call cubemain_find(nmed,good%t(1:good%n),error)
       if (error) return
       median = good%t(nmed)
       base%t(:) = median
       line%t(:) = inspec%t(:)-median
    else
       base%t(:) = gr4nan
       line%t(:) = gr4nan
    endif
    !
    call base%put(prog%baseline,ie,error)
    if (error) return
    call line%put(prog%line,ie,error)
    if (error) return
  end subroutine cubemain_baseline_prog_domedian
  !
  subroutine cubemain_baseline_prog_wavelet_loop(prog,first,last,error)
    use cubeadm_entryloop
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    integer(kind=entr_k),   intent(in)    :: first
    integer(kind=entr_k),   intent(in)    :: last
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>WAVELET>LOOP'
    integer(kind=entr_k) :: ie
    type(spectrum_t) :: inspec,line,base
    !
    call inspec%reassociate_and_init(prog%cube,error)
    if (error) return
    !
    call line%reallocate('line',prog%cube%head%arr%n%c,error)
    if (error) return
    call base%reallocate('base',prog%cube%head%arr%n%c,error)
    if (error) return
    !
    do ie=first,last
      call cubeadm_entryloop_iterate(ie,error)
      if (error) return
      call prog%dowavelet(ie,inspec,base,line,error)
      if (error) return
    enddo
  end subroutine cubemain_baseline_prog_wavelet_loop
  !
  subroutine cubemain_baseline_prog_dowavelet(prog,ie,inspec,base,line,error)
    use cubetools_nan
    use cubemain_spectrum_blanking
    use gkernel_interfaces
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    integer(kind=entr_k),   intent(in)    :: ie
    type(spectrum_t),       intent(inout) :: inspec        ! Working buffer
    type(spectrum_t),       intent(inout) :: base          ! Working buffer
    type(spectrum_t),       intent(inout) :: line          ! Working buffer
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>DOWAVELET'
    !
    real(kind=sign_k), allocatable :: wavelets(:,:)
    !
    call inspec%get(prog%cube,ie,error)
    if (error) return
    !
    if (cubemain_spectrum_blank(inspec)) then
       ! Nothing to do if Spectrum is fully blank
       base%t(:) = gr4nan
       line%t(:) = gr4nan
    else if (any(ieee_is_nan(inspec%t))) then
       ! Error!
       call cubemain_message(seve%e,rname,'Cannot fit wavelength if there are blanks')
       call cubemain_message(seve%e,rname,'Try replacing them with a value first (e.g CUBE\FILL)')
       error = .true.
       return
    else
       line%t = inspec%t
       call gwavelet_gaps(line%t,wavelets,error)
       if (error) return
       call gwavelet_subtract(prog%degree,wavelets,line%t,error)
       if (error) return
       base%t = inspec%t-line%t
    endif
    !
    call base%put(prog%baseline,ie,error)
    if (error) return
    call line%put(prog%line,ie,error)
    if (error) return
  end subroutine cubemain_baseline_prog_dowavelet
  !
  subroutine cubemain_baseline_prog_polynomial_loop(prog,first,last,error)
    use cubeadm_entryloop
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    integer(kind=entr_k),   intent(in)    :: first
    integer(kind=entr_k),   intent(in)    :: last
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>POLYNOMIAL>LOOP'
    integer(kind=entr_k) :: ie
    type(spectrum_t) :: inspec,line,base,good,mask
    !
    call inspec%reassociate_and_init(prog%cube,error)
    if (error) return
    !
    if (prog%domask) then
       call mask%reassociate_and_init(prog%mask,error)
       if (error) return
       inspec%w(:) = mask%t(:)
    else
       call cubemain_window2mask(prog%wind,inspec,error)
       if (error) return
    endif
    !
    call line%reallocate('line',prog%cube%head%arr%n%c,error)
    if (error) return
    call base%reallocate('base',prog%cube%head%arr%n%c,error)
    if (error) return
    call good%reallocate('good',prog%cube%head%arr%n%c,error)
    if (error) return
    !
    do ie=first,last
      call cubeadm_entryloop_iterate(ie,error)
      if (error) return
      call prog%dopolynomial(ie,inspec,mask,good,base,line,error)
      if (error) return
    enddo
  end subroutine cubemain_baseline_prog_polynomial_loop
  !
  subroutine cubemain_baseline_prog_dopolynomial(prog,ie,inspec,mask,good,base,line,error)
    use cubetools_nan
    use cubemain_spectrum_blanking
    use cubemain_chebyshev_svd
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_prog_t), intent(inout) :: prog
    integer(kind=entr_k),   intent(in)    :: ie
    type(spectrum_t),       intent(inout) :: inspec  ! Working buffer
    type(spectrum_t),       intent(inout) :: mask    ! Working buffer
    type(spectrum_t),       intent(inout) :: good    ! Working buffer
    type(spectrum_t),       intent(inout) :: base    ! Working buffer
    type(spectrum_t),       intent(inout) :: line    ! Working buffer
    logical,                intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>PROG>DOPOLYNOMIAL'
    !
    integer(kind=4) :: ipol
    integer(kind=chan_k) :: ifirst,ilast
    type(chebyshev_t) :: poly
    type(svd_t)       :: svd
    type(spectrum_t)  :: extract
    !
    call inspec%get(prog%cube,ie,error)
    if (error) return
    if (cubemain_spectrum_blank(inspec)) then
       line%t(:) = gr4nan
       base%t(:) = gr4nan
    else
       if (prog%domask) then
          call mask%get(prog%mask,ie,error)
          if (error) return
       endif
       do ipol=1,prog%npol
          ifirst = prog%trans(ipol)+1
          ilast = prog%trans(ipol+1)          
          ! Select region to fit the baseline
          call extract%point_to(inspec,ifirst,ilast,inspec%noi,error)
          if(error) return
          call cubemain_spectrum_unmask(extract,good,error)
          if (error) return
          if (good%n.gt.prog%degrees(ipol)) then
             call cubemain_chebyshev_fit(prog%degrees(ipol),good,poly,svd,error)
             if (error) return
             call cubemain_chebyshev_subtract(poly,inspec,ifirst,ilast,base,line,error)
             if (error) return
             call cubemain_free_chebyshev(poly,error)
             if (error) return
          else
             line%t(:) = inspec%t(:)
             base%t(:) = gr4nan
          endif
       enddo
    endif
    !
    call base%put(prog%baseline,ie,error)
    if (error) return
    call line%put(prog%line,ie,error)
    if (error) return
  end subroutine cubemain_baseline_prog_dopolynomial
end module cubemain_baseline
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
