!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtindex_code2sic(error)
  use gbl_message
  use gkernel_interfaces
  use mrtindex_parameters
  use mrtindex_interfaces, except_this=>mrtindex_code2sic
  !---------------------------------------------------------------------
  ! @ public
  ! Create the MRT% Sic structure mapping the index codes.
  !---------------------------------------------------------------------
  logical, intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='INDEX>CODE2SIC'
  !
  call mrtindex_message(seve%t,rname,'Welcome')
  !
  ! Create structure if needed
  if (.not.sic_varexist('mrt')) then
     call sic_defstructure('mrt%',.true.,error)
     if (error) return
  endif
  !
  ! *** JP why UNKNOWN is not a kind of obstypes as any other? This calls for troubles...
  call sic_def_charn('mrt%backend',   backends_mrtcal,   1,nbackends_mrtcal+1,.false.,error)
  call sic_def_charn('mrt%obstype',   obstypes_mrtcal,   1,nobstypes_mrtcal+1,.false.,error)
  call sic_def_charn('mrt%switchmode',switchmodes_mrtcal,1,nswitchmodes+1,    .false.,error)
  call sic_def_charn('mrt%filstatus', completenesses,    1,ncompleteness,     .false.,error)
  call sic_def_charn('mrt%calstatus', calstatus,         1,ncalstatus,        .false.,error)
  call sic_def_charn('mrt%solstatus', solstatus,         1,nsolstatus,        .false.,error)
  ! If there is an error, the user/developer wants to know... => no resetting
  !
end subroutine mrtindex_code2sic
!
subroutine mrtindex_cx2sic(cx,error)
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_cx2sic
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  ! Create the MDX% Sic structure mapping the CX arrays (always available)
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t), intent(in)    :: cx
  logical,                   intent(inout) :: error
  ! Local
  integer(kind=index_length) :: dim(4)
  character(len=*), parameter :: rname='INDEX>CX2SIC'
  !
  call mrtindex_message(seve%t,rname,'Welcome')
  !
  ! Create structure if needed
  if (.not.sic_varexist('mdx')) then
     call sic_defstructure('mdx%',.true.,error)
     if (error) return
  endif
  !
  ! Removed previously defined structure variable
  call sic_delvariable('mdx%curr',        .false.,error)
  call sic_delvariable('mdx%mnum',        .false.,error)
  call sic_delvariable('mdx%fnum',        .false.,error)
  call sic_delvariable('mdx%num',         .false.,error)
  call sic_delvariable('mdx%ver',         .false.,error)
  call sic_delvariable('mdx%telescope',   .false.,error)
  call sic_delvariable('mdx%projid',      .false.,error)
  call sic_delvariable('mdx%source',      .false.,error)
  call sic_delvariable('mdx%dobs',        .false.,error)
  call sic_delvariable('mdx%ut',          .false.,error)
  call sic_delvariable('mdx%lst',         .false.,error)
  call sic_delvariable('mdx%scan',        .false.,error)
  call sic_delvariable('mdx%backend',     .false.,error)
  call sic_delvariable('mdx%obstype',     .false.,error)
  call sic_delvariable('mdx%switchmode',  .false.,error)
  call sic_delvariable('mdx%polarimetry', .false.,error)
  call sic_delvariable('mdx%filstatus',   .false.,error)
  call sic_delvariable('mdx%calstatus',   .false.,error)
  call sic_delvariable('mdx%solstatus',   .false.,error)
  call sic_delvariable('mdx%filename',    .false.,error)
  error = .false.
  !
  if (cx%next.gt.1) then
     dim(1) = cx%next-1
     call sic_def_long ('mdx%mnum',        cx%mnum,        1,dim,.false.,error)
     call sic_def_long ('mdx%fnum',        cx%fnum,        1,dim,.false.,error)
     call sic_def_long ('mdx%num',         cx%num,         1,dim,.false.,error)
     call sic_def_inte ('mdx%ver',         cx%version,     1,dim,.false.,error)
     call sic_def_inte ('mdx%telescope',   cx%telescope,   1,dim,.false.,error)
     call sic_def_charn('mdx%projid',      cx%projid,      1,dim,.false.,error)
     call sic_def_charn('mdx%source',      cx%source,      1,dim,.false.,error)
     call sic_def_inte ('mdx%dobs',        cx%dobs,        1,dim,.false.,error)
     call sic_def_dble ('mdx%ut',          cx%ut,          1,dim,.false.,error)
     call sic_def_dble ('mdx%lst',         cx%lst,         1,dim,.false.,error)
     call sic_def_inte ('mdx%scan',        cx%scan,        1,dim,.false.,error)
     call sic_def_inte ('mdx%backend',     cx%backend,     1,dim,.false.,error)
     call sic_def_inte ('mdx%obstype',     cx%obstype,     1,dim,.false.,error)
     call sic_def_inte ('mdx%switchmode',  cx%switchmode,  1,dim,.false.,error)
     call sic_def_inte ('mdx%polarimetry', cx%polstatus,   1,dim,.false.,error)
     call sic_def_inte ('mdx%filstatus',   cx%filstatus,   1,dim,.false.,error)
     call sic_def_inte ('mdx%calstatus',   cx%calstatus,   1,dim,.false.,error)
     call sic_def_inte ('mdx%solstatus',   cx%solstatus,   1,dim,.false.,error)
     call sic_def_charn('mdx%filename',    cx%filename,    1,dim,.false.,error)
     error = .false.
  endif
  !
end subroutine mrtindex_cx2sic
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtindex_index_to_optimize_inplace(indx,fileid,fnum,mnum,num,  &
  sort,islast,optx,ioptx,error)
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Write the indx at the given position, overwriting the previous
  ! element, if any
  !---------------------------------------------------------------------
  type(mrtindex_indx_t),        intent(in)    :: indx    ! The entry index on disk
  integer(kind=4),            intent(in)    :: fileid  ! Index file associated to the entry
  integer(kind=entry_length), intent(in)    :: fnum    ! Position in associated file
  integer(kind=entry_length), intent(in)    :: mnum    ! Position in memory (IX)
  integer(kind=entry_length), intent(in)    :: num     ! "Observation" number
  integer(kind=entry_length), intent(in)    :: sort    ! Position in the sorted index
  logical,                    intent(in)    :: islast  ! "is-last-version" status
  type(mrtindex_optimize_t),  intent(inout) :: optx    !
  integer(kind=entry_length), intent(in)    :: ioptx   ! Position in output optx
  logical,                    intent(inout) :: error   !
  !
  ! Memory only elements
  optx%idir(ioptx)         = fileid
  optx%fnum(ioptx)         = fnum
  optx%mnum(ioptx)         = mnum
  optx%num(ioptx)          = num
  optx%sort(ioptx)         = sort
  optx%islast(ioptx)       = islast
  ! Disk elements
  optx%bloc(ioptx)         = indx%bloc
  optx%word(ioptx)         = indx%word
  optx%version(ioptx)      = indx%version
  optx%telescope(ioptx)    = indx%telescope
  optx%source(ioptx)       = indx%source
  optx%projid(ioptx)       = indx%projid
  optx%ut(ioptx)           = indx%ut
  optx%lst(ioptx)          = indx%lst
  optx%az(ioptx)           = indx%az
  optx%el(ioptx)           = indx%el
  optx%lon(ioptx)          = indx%lon
  optx%lat(ioptx)          = indx%lat
  optx%system(ioptx)       = indx%system
  optx%equinox(ioptx)      = indx%equinox
  optx%frontend(:,ioptx)   = indx%frontend(:)
  optx%dobs(ioptx)         = indx%dobs
  optx%scan(ioptx)         = indx%scan
  optx%backend(ioptx)      = indx%backend
  optx%obstype(ioptx)      = indx%obstype
  optx%switchmode(ioptx)   = indx%switchmode
  optx%polstatus(ioptx)    = indx%polstatus
  optx%filstatus(ioptx)    = indx%filstatus
  optx%calstatus(ioptx)    = indx%calstatus
  optx%solstatus(ioptx)    = indx%solstatus
  optx%filename(ioptx)     = indx%filename
  optx%itime(ioptx)        = indx%itime
  !
end subroutine mrtindex_index_to_optimize_inplace
!
subroutine mrtindex_index_to_key(indx,key,error)
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_indx_t), intent(in)    :: indx   !
  type(mrtindex_key_t),  intent(inout) :: key    !
  logical,             intent(inout) :: error  !
  !
  key%version      = indx%version
  key%telescope    = indx%telescope
  key%projid       = indx%projid
  key%source       = indx%source
  key%dobs         = indx%dobs
  key%ut           = indx%ut
  key%lst          = indx%lst
  key%az           = indx%az
  key%el           = indx%el
  key%lon          = indx%lon
  key%lat          = indx%lat
  key%system       = indx%system
  key%equinox      = indx%equinox
  key%frontend(:)  = indx%frontend(:)
  key%scan         = indx%scan
  key%backend      = indx%backend
  key%obstype      = indx%obstype
  key%switchmode   = indx%switchmode
  key%polstatus    = indx%polstatus
  key%filstatus    = indx%filstatus
  key%calstatus    = indx%calstatus
  key%solstatus    = indx%solstatus
  key%filename     = indx%filename
  !
end subroutine mrtindex_index_to_key
!
subroutine mrtindex_key_to_index(key,indx,error)
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_key_t),  intent(in)    :: key    !
  type(mrtindex_indx_t), intent(inout) :: indx   !
  logical,             intent(inout) :: error  !
  !
  ! indx%bloc = not relevant in key
  ! indx%word = not relevant in key
  indx%version     = key%version
  indx%telescope   = key%telescope
  indx%projid      = key%projid
  indx%source      = key%source
  indx%dobs        = key%dobs
  indx%ut          = key%ut
  indx%lst         = key%lst
  indx%az          = key%az
  indx%el          = key%el
  indx%lon         = key%lon
  indx%lat         = key%lat
  indx%system      = key%system
  indx%equinox     = key%equinox
  indx%frontend(:) = key%frontend(:)
  indx%scan        = key%scan
  indx%backend     = key%backend
  indx%obstype     = key%obstype
  indx%switchmode  = key%switchmode
  indx%polstatus   = key%polstatus
  indx%filstatus   = key%filstatus
  indx%calstatus   = key%calstatus
  indx%solstatus   = key%solstatus
  indx%filename    = key%filename
  ! indx%itime = not relevant in key
  !
end subroutine mrtindex_key_to_index
!
subroutine mrtindex_optimize_to_key(optx,ient,key,error)
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),    intent(in)    :: optx   !
  integer(kind=entry_length), intent(in)    :: ient   !
  type(mrtindex_key_t),         intent(out)   :: key    !
  logical,                    intent(inout) :: error  !
  !
  key%version      = optx%version(ient)
  key%telescope    = optx%telescope(ient)
  key%projid       = optx%projid(ient)
  key%source       = optx%source(ient)
  key%dobs         = optx%dobs(ient)
  key%ut           = optx%ut(ient)
  key%lst          = optx%lst(ient)
  key%az           = optx%az(ient)
  key%el           = optx%el(ient)
  key%lon          = optx%lon(ient)
  key%lat          = optx%lat(ient)
  key%system       = optx%system(ient)
  key%equinox      = optx%equinox(ient)
  key%frontend(:)  = optx%frontend(:,ient)
  key%scan         = optx%scan(ient)
  key%backend      = optx%backend(ient)
  key%obstype      = optx%obstype(ient)
  key%switchmode   = optx%switchmode(ient)
  key%polstatus    = optx%polstatus(ient)
  key%filstatus    = optx%filstatus(ient)
  key%calstatus    = optx%calstatus(ient)
  key%solstatus    = optx%solstatus(ient)
  key%filename     = optx%filename(ient)
  !
end subroutine mrtindex_optimize_to_key
!
subroutine mrtindex_optimize_to_entry(optx,ient,entry,error)
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_optimize_to_entry
  use mrtindex_types
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ public
  !  "GET" the full entry (header+sections), read from disk
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),    intent(in)    :: optx   !
  integer(kind=entry_length), intent(in)    :: ient   !
  type(mrtindex_entry_t),       intent(inout) :: entry  !
  logical,                    intent(inout) :: error  !
  ! Local
  integer(kind=4) :: fileid
  integer(kind=entry_length) :: jent
  !
  ! Open file if not already opened:
  fileid = optx%idir(ient)
  call mrtindex_file_old(fileid,.false.,error)
  if (error)  return
  !
  ! Read the whole entry from disk
  jent = optx%fnum(ient)
  call mrtindex_entry_read(ix_files(fileid),jent,entry,error)
  if (error)  return
  !
end subroutine mrtindex_optimize_to_entry
!
subroutine mrtindex_optimize_to_optimize_1d(in,out,error)
  use gbl_message
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_optimize_to_optimize_1d
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public-generic mrtindex_optimize_to_optimize
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t), intent(in)    :: in
  type(mrtindex_optimize_t), intent(inout) :: out
  logical,                 intent(inout) :: error
  ! Local
  integer(kind=entry_length) :: ient,nent
  character(len=*), parameter :: rname='OPTIMIZE>TO>OPTIMIZE>1D'
  !
  call mrtindex_message(seve%t,rname,'Welcome')
  !
  ! Reallocate without keeping the data
  nent = in%next-1
  call reallocate_mrtoptimize(out,nent,.false.,error)
  if (error)  return
  ! Copy
  out%next = 1
  do ient=1,nent
    call mrtindex_optimize_to_optimize(in,ient,out,error)
    if (error)  return
  enddo ! ient
  !
end subroutine mrtindex_optimize_to_optimize_1d
!
subroutine mrtindex_optimize_to_optimize_next(in,i,out,error)
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_optimize_to_optimize_next
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public-generic mrtindex_optimize_to_optimize
  ! Copy the i-th element of the input optimize to the next available
  ! position in output optimize. It does not check if the optimize
  ! index is correctly allocated.
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),    intent(in)    :: in
  integer(kind=entry_length), intent(in)    :: i
  type(mrtindex_optimize_t),    intent(inout) :: out
  logical,                    intent(inout) :: error
  !
  call mrtindex_optimize_to_optimize_inplace(in,i,out,out%next,error)
  if (error)  return
  out%next = out%next+1
  !
end subroutine mrtindex_optimize_to_optimize_next
!
subroutine mrtindex_optimize_to_optimize_inplace(in,i,out,j,error)
  use classic_api
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public-generic mrtindex_optimize_to_optimize
  ! Copy the i-th element of the input optimize to the j-th position
  ! in output optimize. It does not check if the optimize index is
  ! correctly allocated.
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),    intent(in)    :: in
  integer(kind=entry_length), intent(in)    :: i
  type(mrtindex_optimize_t),    intent(inout) :: out
  integer(kind=entry_length), intent(in)    :: j
  logical,                    intent(inout) :: error
  !
  out%bloc(j)         = in%bloc(i)
  out%word(j)         = in%word(i)
  out%version(j)      = in%version(i)
  out%telescope(j)    = in%telescope(i)
  out%projid(j)       = in%projid(i)
  out%source(j)       = in%source(i)
  out%dobs(j)         = in%dobs(i)
  out%ut(j)           = in%ut(i)
  out%lst(j)          = in%lst(i)
  out%az(j)           = in%az(i)
  out%el(j)           = in%el(i)
  out%lon(j)          = in%lon(i)
  out%lat(j)          = in%lat(i)
  out%system(j)       = in%system(i)
  out%equinox(j)      = in%equinox(i)
  out%frontend(:,j)   = in%frontend(:,i)
  out%scan(j)         = in%scan(i)
  out%backend(j)      = in%backend(i)
  out%obstype(j)      = in%obstype(i)
  out%switchmode(j)   = in%switchmode(i)
  out%polstatus(j)    = in%polstatus(i)
  out%filstatus(j)    = in%filstatus(i)
  out%calstatus(j)    = in%calstatus(i)
  out%solstatus(j)    = in%solstatus(i)
  out%filename(j)     = in%filename(i)
  out%itime(j)        = in%itime(i)
  out%num(j)          = in%num(i)
  out%mnum(j)         = in%mnum(i)
  out%fnum(j)         = in%fnum(i)
  out%sort(j)         = j  ! Yes!
  out%idir(j)         = in%idir(i)
  out%islast(j)       = in%islast(i)
  !
end subroutine mrtindex_optimize_to_optimize_inplace
!
subroutine mrtindex_optimize_to_filename(optx,ient,filename,error)
  use mrtindex_types
  use mrtindex_vars
  use mrtindex_interfaces, except_this=>mrtindex_optimize_to_filename
  !---------------------------------------------------------------------
  ! @ public
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),    intent(in)    :: optx
  integer(kind=entry_length), intent(in)    :: ient
  character(len=*),           intent(out)   :: filename
  logical,                    intent(inout) :: error
  !
  filename = trim(ix_dirs(optx%idir(ient)))//'/'//optx%filename(ient)
  !
end subroutine mrtindex_optimize_to_filename
!
subroutine mrtindex_optimize_setsort(in,error)
  use gkernel_interfaces
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_optimize_setsort
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Compute the sorting list of the input index (in%sort)
  !---------------------------------------------------------------------
  type(mrtindex_optimize_t),    intent(inout) :: in           !
  logical,                    intent(inout) :: error        !
  ! Local
  integer(kind=entry_length) :: nelem
  !
  ! Remark: in%sort() must contain the unsorted element before
  ! entering gi0_quicksort_index_with_user_gtge
  nelem = in%next-1
  call gi0_quicksort_index_with_user_gtge(in%sort,nelem,  &
    mrtindex_find_gt,mrtindex_find_ge,error)
  if (error)  return
  !
contains
  !
  ! Sorting functions: first by DOBS then by UT. Then by SCAN for
  ! safety, then by BACKEND for cosmetics, then by VERSION.
  ! Use local functions which know the 'in' object (this avoids
  ! making it a global variable).
  function mrtindex_find_gt(m,l)
    logical :: mrtindex_find_gt
    integer(kind=entry_length), intent(in) :: m,l
    if (in%dobs(m).ne.in%dobs(l)) then
      mrtindex_find_gt = in%dobs(m).gt.in%dobs(l)
      return
    endif
    if (in%ut(m).ne.in%ut(l)) then
      ! If one is NaN, skip this test
      if (in%ut(m).eq.in%ut(m) .and. in%ut(l).eq.in%ut(l)) then
        mrtindex_find_gt = in%ut(m).gt.in%ut(l)
        return
      endif
    endif
    if (in%scan(m).ne.in%scan(l)) then
      mrtindex_find_gt = in%scan(m).gt.in%scan(l)
      return
    endif
    if (in%backend(m).ne.in%backend(l)) then
      mrtindex_find_gt = in%backend(m).gt.in%backend(l)
      return
    endif
    mrtindex_find_gt = in%version(m).gt.in%version(l)
  end function mrtindex_find_gt
  !
  function mrtindex_find_ge(m,l)
    logical :: mrtindex_find_ge
    integer(kind=entry_length), intent(in) :: m,l
    if (in%dobs(m).ne.in%dobs(l)) then
      mrtindex_find_ge = in%dobs(m).ge.in%dobs(l)
      return
    endif
    if (in%ut(m).ne.in%ut(l)) then
      ! If one is NaN, skip this test
      if (in%ut(m).eq.in%ut(m) .and. in%ut(l).eq.in%ut(l)) then
        mrtindex_find_ge = in%ut(m).ge.in%ut(l)
        return
      endif
    endif
    if (in%scan(m).ne.in%scan(l)) then
      mrtindex_find_ge = in%scan(m).ge.in%scan(l)
      return
    endif
    if (in%backend(m).ne.in%backend(l)) then
      mrtindex_find_ge = in%backend(m).ge.in%backend(l)
      return
    endif
    mrtindex_find_ge = in%version(m).ge.in%version(l)
  end function mrtindex_find_ge
  !
end subroutine mrtindex_optimize_setsort
!
!-----------------------------------------------------------------------
!
subroutine mrtindex_entry_zindx(indx,error)
  use gbl_constant
  use gkernel_interfaces
  use imbfits_parameters
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Zero-ify an 'mrtindex_indx_t' structure. Default can not be in
  ! in the structure definition because of the 'sequence' statement
  !---------------------------------------------------------------------
  type(mrtindex_indx_t), intent(out)   :: indx
  logical,             intent(inout) :: error
  !
  indx%bloc = 0
  indx%word = 0
  indx%version = 0
  indx%telescope = telescope_unknown
  indx%source = ''
  indx%projid = ''
  call gag_notanum(indx%ut)
  call gag_notanum(indx%lst)
  call gag_notanum(indx%az)
  call gag_notanum(indx%el)
  call gag_notanum(indx%lon)
  call gag_notanum(indx%lat)
  indx%system = type_un
  indx%equinox = equinox_null
  indx%frontend(:) = ''
  indx%dobs = -32768
  indx%scan = 0
  indx%backend = backend_unknown
  indx%obstype = obstype_unknown
  indx%switchmode = switchmode_unk
  indx%polstatus = 0
  indx%filstatus = complete_unreadable
  indx%calstatus = calstatus_none
  indx%solstatus = solstatus_none
  indx%filename = ''
  indx%itime = 0
  !
end subroutine mrtindex_entry_zindx
!
subroutine mrtindex_entry_zkey(key,error)
  use gbl_constant
  use gkernel_interfaces
  use imbfits_parameters
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Zero-ify an 'mrtindex_key_t' structure
  !---------------------------------------------------------------------
  type(mrtindex_key_t), intent(out)   :: key
  logical,            intent(inout) :: error
  !
  key%version = 0
  key%telescope = telescope_unknown
  key%projid = ''
  key%source = ''
  key%dobs = -32768
  call gag_notanum(key%ut)
  call gag_notanum(key%lst)
  call gag_notanum(key%az)
  call gag_notanum(key%el)
  call gag_notanum(key%lon)
  call gag_notanum(key%lat)
  key%system = type_un
  key%equinox = equinox_null
  key%frontend(:) = ''
  key%scan = 0
  key%backend = backend_unknown
  key%obstype = obstype_unknown
  key%switchmode = switchmode_unk
  key%polstatus = 0
  key%filstatus = complete_unreadable
  key%calstatus = calstatus_none
  key%solstatus = solstatus_none
  key%filename = ''
  !
end subroutine mrtindex_entry_zkey
!
subroutine mrtindex_entry_zprim(prim,error)
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Zero-ify an 'sec_primary_t' structure
  !---------------------------------------------------------------------
  type(sec_primary_t), intent(out)   :: prim
  logical,             intent(inout) :: error
  !
  prim%imbfvers = 0.0
! prim%lam      = 0.d0
! prim%bet      = 0.d0
! prim%timegoal = 0.d0
! prim%nsubgoal = 0
! prim%nsub     = 0
  !
end subroutine mrtindex_entry_zprim
!
subroutine mrtindex_entry_zcalib(calib,error)
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Zero-ify an 'sec_calib_t' structure
  !---------------------------------------------------------------------
  type(sec_calib_t), intent(inout) :: calib
  logical,           intent(inout) :: error
  !
  calib%nfreq = 0
  calib%nset  = 0
  calib%npix  = 0
  if (allocated(calib%frontend))  deallocate(calib%frontend)
  if (allocated(calib%freq))      deallocate(calib%freq)
  if (allocated(calib%atsys))     deallocate(calib%atsys)
  if (allocated(calib%ztau))      deallocate(calib%ztau)
  !
end subroutine mrtindex_entry_zcalib
!
subroutine mrtindex_entry_zscience(sci,error)
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public
  !  Zero-ify an 'sec_science_t' structure
  !---------------------------------------------------------------------
  type(sec_science_t), intent(out)   :: sci
  logical,             intent(inout) :: error
  !
  sci%caldobs = -32768
  sci%calscan = 0
  sci%calback = backend_unknown
  sci%calvers = 0
  !
end subroutine mrtindex_entry_zscience
!
subroutine mrtindex_entry_zheader(head,error)
  use mrtindex_interfaces, except_this=>mrtindex_entry_zheader
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public
  !  Zero-ify an 'mrtindex_header_t' structure
  !---------------------------------------------------------------------
  type(mrtindex_header_t), intent(inout) :: head
  logical,               intent(inout) :: error
  !
  head%presec(:) = .false.
  !
  call mrtindex_entry_zkey(head%key,error)
  if (error)  return
  call mrtindex_entry_zprim(head%pri,error)
  if (error)  return
  call mrtindex_entry_zcalib(head%cal,error)
  if (error)  return
  call mrtindex_entry_zscience(head%sci,error)
  if (error)  return
  call head%poi%zero(error)
  if (error)  return
end subroutine mrtindex_entry_zheader
!
!-----------------------------------------------------------------------
!
subroutine mrtindex_entry_fheader(head,error)
  use mrtindex_interfaces, except_this=>mrtindex_entry_fheader
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public
  !  Free an 'mrtindex_header_t' structure
  !---------------------------------------------------------------------
  type(mrtindex_header_t), intent(inout) :: head
  logical,               intent(inout) :: error
  !
  ! key: nothing to destroy
  !
  ! pri: nothing to destroy
  !
  ! cal:
  call free_calib_section(head%cal,error)
  if (error)  continue
  !
  ! sci: nothing to destroy
  !
  ! poi:
  call head%poi%free(error)
  if (error)  continue
  !
end subroutine mrtindex_entry_fheader
!
subroutine mrtindex_entry_free(entry,error)
  use mrtindex_interfaces, except_this=>mrtindex_entry_free
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public
  !  Free an 'mrtindex_entry_t' structure
  !---------------------------------------------------------------------
  type(mrtindex_entry_t), intent(inout) :: entry
  logical,              intent(inout) :: error
  !
  ! indx: nothing to destroy
  !
  ! desc: nothing to destroy
  !
  ! head:
  call mrtindex_entry_fheader(entry%head,error)
  if (error)  continue
  !
end subroutine mrtindex_entry_free
!
!-----------------------------------------------------------------------
!
subroutine mrtindex_entry_lcalib(head,string,error)
  use gildas_def
  use gkernel_interfaces
  use class_parameter
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  List the calibration section, i.e. produce a string suited for
  ! MLIST
  !---------------------------------------------------------------------
  type(mrtindex_header_t), intent(in)    :: head
  character(len=*),        intent(out)   :: string
  logical,                 intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='ENTRY>LCALIB'
  integer(kind=size_length) :: ndata
  real(kind=4) :: medval
  integer(kind=4) :: ier,ifreq,iset,ipix,nc
  real(kind=4), allocatable :: atsys(:,:)
  character(len=64) :: tmpstring
  !
  string = ''
  if (.not.head%presec(sec_calib_id))  return
  !
  allocate(atsys(head%cal%nfreq,head%cal%npix),stat=ier)
  if (failed_allocate(rname,'atsys',ier,error))  return
  !
  nc = 1
  ndata = head%cal%nfreq * head%cal%npix
  do iset=1,head%cal%nset
    ! Fill temporary (contiguous) buffer
    do ipix=1,head%cal%npix
      do ifreq=1,head%cal%nfreq
        atsys(ifreq,ipix) = head%cal%atsys(ifreq,iset,ipix)
      enddo
    enddo
    ! Compute the median along the Freq and Pix dimensions
    call gr4_median(atsys,ndata,0.,-1.,medval,error)
    if (error)  return
    !
    ! Write in temporary string
    if (medval.eq.class_bad) then
      write(tmpstring,'(A,A,A)')   trim(head%cal%frontend(iset)),':','--'
    else
      write(tmpstring,'(A,A,I0)')  trim(head%cal%frontend(iset)),':',nint(medval)
    endif
    !
    if (truncate_and_return(nc,string))  return  ! Detect if at least 1 char available at 'nc'
    string(nc:) = tmpstring                      ! Possibly truncated
    nc = nc+len_trim(tmpstring)-1                ! Trimmed length
    if (truncate_and_return(nc,string))  return  ! Detect if truncation happened
    nc = nc+2  ! Room for a blank space (not evaluated in truncate_and_return if last loop)
  enddo
  !
  deallocate(atsys)
  !
contains
  !
  function truncate_and_return(nc,string)
    logical :: truncate_and_return
    integer(kind=4),  intent(in)    :: nc
    character(len=*), intent(inout) :: string
    ! Local
    integer(kind=4) :: mc
    !
    truncate_and_return = nc.gt.len(string)
    if (truncate_and_return) then
      mc = len(string)
      string(mc-2:mc) = '...'
    endif
  end function truncate_and_return
  !
end subroutine mrtindex_entry_lcalib
!
subroutine mrtindex_entry_lpointing(head,string,error)
  use gildas_def
  use gkernel_interfaces
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  List the pointing section, i.e. produce a string suited for
  ! MLIST
  !---------------------------------------------------------------------
  type(mrtindex_header_t), intent(in)    :: head
  character(len=*),        intent(out)   :: string
  logical,                 intent(inout) :: error
  ! Local
  integer(kind=4) :: nc,isol
  !
  string = ''
  if (.not.head%presec(sec_pointing_id))  return
  !
  nc = 0
  do isol=1,head%poi%nsol
    ! Produce some mean result for the user
!     write(string(nc+2:),'(A,A,F4.1)')  &
!           head%poi%sol(isol)%val(1),':',  &
!           head%poi%sol(isol)%nfit(2)
!     nc = len_trim(string)
  enddo
  !
  ! ZZZ Dummy results for the time being (the section needs to be redefined)
  write(string,'(A)')  'Az:N/A  El:N/A'
end subroutine mrtindex_entry_lpointing
!
subroutine mrtindex_entry_lscience(head,string,nc,error)
  use gildas_def
  use mrtindex_dependencies_interfaces
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  List the science section, i.e. produce a string suited for
  ! MLIST
  !---------------------------------------------------------------------
  type(mrtindex_header_t), intent(in)    :: head
  character(len=*),      intent(out)   :: string
  integer(kind=4),       intent(out)   :: nc
  logical,               intent(inout) :: error
  !
  string = ''
  nc = 19+backname_length  ! 11+1 + 3+1 + backname_length+1 + 2
  !
  if (.not.head%presec(sec_science_id))  return
  !
  call gag_todate(head%sci%caldobs,string,error)
  if (error)  return
  !
  write(string(13:),'(I3,1X,A,1X,I2)')  &
    head%sci%calscan,                   &
    backends_mrtcal(head%sci%calback),  &
    head%sci%calvers
  !
end subroutine mrtindex_entry_lscience
!
!-----------------------------------------------------------------------
!
subroutine mrtindex_append_entry(entry,ient,oent,ix,error)
  use gbl_message
  use gkernel_interfaces
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_append_entry
  use mrtindex_types
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ public
  !  Append a NEW entry, with a new version number (automatically set
  ! to a new available value) in the file (on disk) and in IX (in
  ! memory). CX is NOT updated (this is a feature)
  !---------------------------------------------------------------------
  type(mrtindex_entry_t),     intent(inout) :: entry  !
  integer(kind=entry_length), intent(in)    :: ient   ! Old entry position in IX
  integer(kind=entry_length), intent(out)   :: oent   ! New entry position in IX
  type(mrtindex_optimize_t),  intent(inout) :: ix
  logical,                    intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='INDEX>APPEND>ENTRY'
  character(len=message_length) :: mess
  integer(kind=4) :: over,fileid
  integer(kind=entry_length) :: ifound,onum,osort
  logical :: dupl,olast
  !
  if (ient.le.0 .or. ient.ge.ix%next) then
    write(mess,'(A,I0,A)') 'No such entry number #',ient,' in IX'
    call mrtindex_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! Set the new version value
  onum = ix%num(ient)  ! Keep same observation number
  over = 0             ! i.e. find the last version
  call mrtindex_numver2ent(rname,ix,onum,over,ifound,dupl,error)
  if (error)  return
  entry%indx%version = ix%version(ifound)+1  ! New available version
  !
  ! In which file?
  if (ox_fileid.ne.0) then
    ! Use a custom output file (INDEX OUTPUT)
    fileid = ox_fileid
    ! For safety, check if the directory associated to the output index is
    ! the same as the input index. Else, this means the user is mixing
    ! several directories and this becomes very complicated...
    if (ix_dirs(ox_fileid).ne.ix_dirs(ix%idir(ient))) then
      call mrtindex_message(seve%e,rname,  &
        'Directories mismatch for input and output indexes')
      error = .true.
      return
    endif
  else
    ! Use the original index file
    fileid = ix%idir(ient)
  endif
  !
  ! Open file if not already opened
  call mrtindex_file_old(fileid,.true.,error)
  if (error)  return
  call mrtindex_entry_write(ix_files(fileid),entry,error)
  if (error)  return
  ! Update the File Descriptor on disk (in particular desc%xnext)
  call classic_filedesc_write(ix_files(fileid),error)
  if (error)  return
  ! Input file has been modified, nullify the reading buffers
  call classic_recordbuf_nullify(ibufbi)
  call classic_recordbuf_nullify(ibufobs)
  !
  ! Also append IX
  ! Make room for 1 more entry
  call reallocate_mrtoptimize(ix,ix%next,.true.,error)
  if (error)  return
  !
  oent = ix%next
  onum = ix%num(ient)  ! Keep same observation number
  osort = oent  ! Declare it at last position in the sorted index, the
                ! re-sorting is done just after
  olast = .true.  ! Is-last-version
  call mrtindex_index_to_optimize_inplace(entry%indx,fileid,entry%desc%xnum,  &
    oent,onum,osort,olast,ix,oent,error)
  if (error)  return
  ix%next = ix%next+1
  !
  ! And keep the sort array sorted!
  call mrtindex_optimize_setsort(ix,error)
  if (error)  return
  !
  ix%islast(ifound) = .false.  ! This is not the last version anymore
  !
end subroutine mrtindex_append_entry
!
subroutine mrtindex_update_entry(entry,ient,ix,error)
  use gbl_message
  use mrtindex_interfaces, except_this=>mrtindex_update_entry
  use mrtindex_types
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ public
  !  Update an old entry in the file (on disk) and in IX (in memory).
  ! CX is NOT updated, this is a feature.
  !---------------------------------------------------------------------
  type(mrtindex_entry_t),     intent(inout) :: entry  !
  integer(kind=entry_length), intent(in)    :: ient   ! Entry position in IX
  type(mrtindex_optimize_t),  intent(inout) :: ix
  logical,                    intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='UPDATE>ENTRY'
  character(len=message_length) :: mess
  !
  call mrtindex_modify_entry(mrtindex_entry_update,entry,ient,ix,error)
  if (error) then
    write(mess,'(A,I0,A,A)')  'Could not update entry #',entry%desc%xnum,  &
      ' in file ',ix_files(ix%idir(ient))%spec
    call mrtindex_message(seve%e,rname,mess)
    return
  endif
  !
end subroutine mrtindex_update_entry
!
subroutine mrtindex_extend_entry(entry,ient,ix,error)
  use gbl_message
  use mrtindex_interfaces, except_this=>mrtindex_extend_entry
  use mrtindex_types
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ public
  !  Extend the LAST entry in the file (on disk, in particular, add the
  ! new sections) and update in IX (in memory).
  !  CX is NOT updated, this is a feature.
  !---------------------------------------------------------------------
  type(mrtindex_entry_t),     intent(inout) :: entry  !
  integer(kind=entry_length), intent(in)    :: ient   ! Entry position in IX
  type(mrtindex_optimize_t),  intent(inout) :: ix
  logical,                    intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='EXTEND>ENTRY'
  character(len=message_length) :: mess
  !
  call mrtindex_modify_entry(mrtindex_entry_extend,entry,ient,ix,error)
  if (error) then
    write(mess,'(A,I0,A,A)')  'Could not extend entry #',entry%desc%xnum,  &
      ' in file ',ix_files(ix%idir(ient))%spec
    call mrtindex_message(seve%e,rname,mess)
    return
  endif
  !
end subroutine mrtindex_extend_entry
!
subroutine mrtindex_modify_entry(mrtindex_entry_modify,entry,ient,ix,error)
  use gbl_message
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_modify_entry
  use mrtindex_types
  use mrtindex_vars
  !---------------------------------------------------------------------
  ! @ private
  !  UPDATE or EXTEND an old entry in the file (on disk) and update in
  ! IX (in memory). CX is NOT updated, this is a feature.
  !  To be called by mrtindex_update_entry or mrtindex_extend_entry only!!!
  !---------------------------------------------------------------------
  interface
    subroutine mrtindex_entry_modify(fileout,entry,error)
    use classic_api
    use mrtindex_types
    type(classic_file_t),   intent(inout) :: fileout
    type(mrtindex_entry_t), intent(inout) :: entry
    logical,                intent(inout) :: error
    end subroutine mrtindex_entry_modify
  end interface
  type(mrtindex_entry_t),     intent(inout) :: entry  !
  integer(kind=entry_length), intent(in)    :: ient   ! Entry position in IX
  type(mrtindex_optimize_t),  intent(inout) :: ix
  logical,                    intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='INDEX>MODIFY>ENTRY'
  character(len=message_length) :: mess
  integer(kind=4) :: fileid
  integer(kind=entry_length) :: inum,isort
  logical :: ilast
  !
  if (ient.le.0 .or. ient.ge.ix%next) then
    write(mess,'(A,I0,A)') 'No such entry number #',ient,' in IX'
    call mrtindex_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! In which file? Always in the same file as before
  fileid = ix%idir(ient)
  !
  ! Open file if not already opened
  call mrtindex_file_old(fileid,.true.,error)
  if (error)  return
  call mrtindex_entry_modify(ix_files(fileid),entry,error)
  if (error)  return
  ! Update the File Descriptor on disk (in particular desc%next because
  ! EXTEND mode may enlarge the file)
  call classic_filedesc_write(ix_files(fileid),error)
  if (error)  return
  call classic_file_fflush(ix_files(fileid),error)
  if (error)  return
  ! Input file has been modified, nullify the reading buffers
  call classic_recordbuf_nullify(ibufbi)
  call classic_recordbuf_nullify(ibufobs)
  !
  ! Update the entry in IX
  inum = ix%num(ient)
  isort = ix%sort(ient)  ! Its position in the sorted index does not change
  ilast = ix%islast(ient)
  call mrtindex_index_to_optimize_inplace(entry%indx,fileid,entry%desc%xnum,  &
    ient,inum,isort,ilast,ix,ient,error)
  if (error)  return
  !
end subroutine mrtindex_modify_entry
!
subroutine mrtindex_numver2ent(rname,ix,num,ver,ient,dupl,error)
  use gbl_message
  use gkernel_interfaces
  use classic_api
  use mrtindex_interfaces, except_this=>mrtindex_numver2ent
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public
  !  Convert an observation number and version to the entry position
  ! in IX. ver<0 means to choose the newest version.
  !---------------------------------------------------------------------
  character(len=*),           intent(in)    :: rname  ! Calling routine name
  type(mrtindex_optimize_t),  intent(in)    :: ix
  integer(kind=entry_length), intent(in)    :: num    ! Observation number
  integer(kind=4),            intent(in)    :: ver    ! Version number
  integer(kind=entry_length), intent(out)   :: ient   ! Entry found
  logical,                    intent(inout) :: dupl   ! Error because of multiple match?
  logical,                    intent(inout) :: error  ! Logical error flag
  ! Local
  logical :: found
  character(len=message_length) :: mess
  integer(kind=entry_length) :: jent,kent,lent
  integer(kind=4) :: iver
  !
  found = .false.
  dupl = .false.
  ient = 0
  !
  ! Protect against empty index
  if (ix%next.le.1)  goto 100
  !
  ! Reject requests beyond the boundaries
  if (ix%num(ix%sort(1))        .gt.num)  goto 100
  if (ix%num(ix%sort(ix%next-1)).lt.num)  goto 100
  !
  call gi0_dicho_with_user_ltgt(ix%next-1,.true.,jent,  &
    mrtindex_ix_num_lt,mrtindex_ix_num_gt,error)
  if (error)  return
  ! In return jent is the observation lower or equal to the request
  ! in the sorted array (ix%sort)
  !
  if (ver.le.0) then
    iver = 0
    do kent=jent,ix%next-1
      lent = ix%sort(kent)
      if (ix%num(lent).eq.num) then
        if (found .and. ix%version(lent).eq.iver)  goto 101
        found = .true.
        ient = lent
        iver = ix%version(lent)
        ! Continue until we reach the last version
      else
        exit
      endif
    enddo
  else
    do kent=jent,ix%next-1
      lent = ix%sort(kent)
      if (ix%num(lent).eq.num) then
        if (ix%version(lent).eq.ver) then
          if (found)  goto 101
          found = .true.
          ient = lent
          ! Continue until we reach last version, in order to check
          ! if there are multiple match
        endif
      else
        exit
      endif
    enddo
  endif
  if (found)  return
  !
100 continue
  if (ver.le.0) then
    write(mess,'(A,I0)') 'No such observation #',num
  else
    write(mess,'(A,I0,A,I0)') 'No such observation #',num,'.',ver
  endif
  call mrtindex_message(seve%e,rname,mess)
  error = .true.
  return
  !
101 continue
  dupl = .true.
  write(mess,'(A,4(I0,A))') 'Multiple match for observation ',  &
    num,'.',ver,' (entries ',ient,' and ',lent,')'
  call mrtindex_message(seve%e,rname,mess)
  error = .true.
  return
  !
contains
  !
  function mrtindex_ix_num_lt(m)
    logical :: mrtindex_ix_num_lt
    integer(kind=entry_length), intent(in) :: m
    mrtindex_ix_num_lt = ix%num(ix%sort(m)).lt.num
  end function mrtindex_ix_num_lt
  !
  function mrtindex_ix_num_gt(m)
    logical :: mrtindex_ix_num_gt
    integer(kind=entry_length), intent(in) :: m
    mrtindex_ix_num_gt = ix%num(ix%sort(m)).gt.num
  end function mrtindex_ix_num_gt
  !
end subroutine mrtindex_numver2ent
!
subroutine mrtindex_mjd2gagut(scandate,mjd,dobs,ut,error)
  use phys_const
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_mjd2gagut
  !---------------------------------------------------------------------
  ! @ public
  ! Split MJD into DATE + UT, but taking care to respect the scan date
  ! which could have been set before midnight. In such a case, UT will
  ! go beyond 2*pi
  !---------------------------------------------------------------------
  integer(kind=4), intent(in)    :: scandate  ! [gag]
  real(kind=8),    intent(in)    :: mjd       ! [day]
  integer(kind=4), intent(out)   :: dobs      ! [gag]
  real(kind=8),    intent(out)   :: ut        ! [rad]
  logical,         intent(inout) :: error     !
  !
  character(len=*), parameter :: rname='MJD2GAGUT'
  character(len=message_length) :: mess
  character(len=8) :: datestring
  !
  call gag_mjd2gagut(mjd,dobs,ut,error)
  if (error)  return
  !
  if (scandate.eq.-32768) then
    call mrtindex_message(seve%w,rname,'Scan date is not set')
    return
  endif
  if (dobs.eq.scandate)  return  ! No midnight overlap
  !
  ! Tolerate 2h=pi/6 forward on next day (1h = max scan length)
  if (dobs.eq.scandate+1 .and. ut.lt.pi/6.d0) then
    ! Keep DOBS as scan date, set UT beyond 24h
    dobs = scandate
    ut = ut+2.d0*pi
  else
    call gag_toyyyymmdd(scandate,datestring,error)
    write(mess,'(a,a8,a,1pg25.16)')  &
      'Inconsistent date in filename (',datestring,') and MJD ',mjd
    call mrtindex_message(seve%e,rname,mess)
    error = .true.
    return
  endif
end subroutine mrtindex_mjd2gagut
!
subroutine datelist_decode(line,optdate,string,error)
  use mrtindex_dependencies_interfaces
  use mrtindex_interfaces, except_this=>datelist_decode
  !---------------------------------------------------------------------
  ! @ private
  !  Re-encode a Sic-list of dates for the CURRENT line as known and
  ! parsed by Sic, into a string of GAG dates (much easier to parse
  ! afterwards).
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line     !
  integer(kind=4),  intent(in)    :: optdate  ! Option /DATE identifier
  character(len=*), intent(out)   :: string   ! Decoded as GAG dates
  logical,          intent(inout) :: error    !
  ! Local
  integer(kind=4) :: oc,iarg,nc,gagdate
  character(len=32) :: argum
  !
  string = ''
  oc = 1
  do iarg=1,sic_narg(optdate)
    call sic_ke(line,optdate,iarg,argum,nc,.true.,error)
    if (error)  return
    !
    if (argum.eq.'TO') then
      ! Date1 TO Date2 (keep "TO" as is)
      ! NB: "BY" not yet recognized, probably useless in this context
      write(string(oc:),'(A)')  'TO'
      oc = oc+3
    else
      call date_decode(argum,gagdate,error)
      if (error)  return
      write(string(oc:),'(I0)')  gagdate
      oc = len_trim(string)+2
    endif
  enddo
  !
end subroutine datelist_decode
!
subroutine date_decode(string,dobs,error)
  use gbl_message
  use mrtindex_dependencies_interfaces
  use mrtindex_interfaces, except_this=>date_decode
  !---------------------------------------------------------------------
  ! @ private
  ! Decode a string date in any supported format into a gag date.
  ! Supported formats are:
  !  - DD-MMM-YYYY
  !  - YYYYMMDD
  !  - Keywords "TODAY" and "YESTERDAY"
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: string  !
  integer(kind=4),  intent(out)   :: dobs    !
  logical,          intent(inout) :: error   !
  ! Local
  character(len=*), parameter :: rname='DATE>DECODE'
  integer(kind=4) :: ikey
  integer(kind=4), parameter :: mkeys=2
  character(len=11) :: date
  character(len=9) :: datekeys(mkeys),keyword
  ! Data
  data datekeys / 'TODAY','YESTERDAY' /
  !
  ! Search for a keyword
  date = string
  call sic_upper(date)
  call sic_ambigs_sub(rname,date,keyword,ikey,datekeys,mkeys,error)
  if (error)  return
  !
  if (ikey.ne.0) then
    call sic_gagdate(dobs)  ! Today
    if (ikey.eq.2)  dobs = dobs-1  ! Yesterday
    return
  endif
  !
  ! Not TODAY or YESTERDAY, try other formats
  if (index(string,'-').gt.0) then  ! DD-MMM-YYYY
    call gag_fromdate(date,dobs,error)
  else                              ! YYYYMMDD
    call gag_fromyyyymmdd(date,dobs,error)
  endif
  if (error) then
    call mrtindex_message(seve%e,rname,'Could not decode date '//string)
    return
  endif
  !
end subroutine date_decode
!
function imbfits_name(date)
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ private
  !   Build an IMB-FITS file name according to the IRAM 30m standard.
  ! This function factorizes this standard at a unique place
  !---------------------------------------------------------------------
  character(len=imbfits_pattern_length) :: imbfits_name  ! Function value on return
  character(len=*), intent(in) :: date   ! The date string, can be '*'
  !
  imbfits_name = 'iram30m-*-'//trim(date)//'s*-imb.fits'
  !
end function imbfits_name
!
!-----------------------------------------------------------------------
!
function mrtindex_telescope(itel)
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Return the telescope name given its identifier.
  ! ---
  ! Note that sharing character arrays between libraries is not
  ! portable, this is why a function is used.
  !---------------------------------------------------------------------
  character(len=telescope_length) :: mrtindex_telescope
  integer(kind=4), intent(in) :: itel
  !
  mrtindex_telescope = telescopes(itel)
  !
end function mrtindex_telescope
!
function mrtindex_backend(iback)
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Return the backend name given its identifier.
  ! ---
  ! Note that sharing character arrays between libraries is not
  ! portable, this is why a function is used.
  !---------------------------------------------------------------------
  character(len=backname_length) :: mrtindex_backend
  integer(kind=4), intent(in) :: iback
  !
  mrtindex_backend = backends_mrtcal(iback)
  !
end function mrtindex_backend
!
function mrtindex_obstype_decode(name,error)
  use gbl_message
  use mrtindex_interfaces, except_this=>mrtindex_obstype_decode
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Decode the IMBFIST obstype name and return the corresponding
  ! MRTCAL code
  !---------------------------------------------------------------------
  integer(kind=4) :: mrtindex_obstype_decode
  character(len=*), intent(in)    :: name
  logical,          intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='OBSTYPE>DECODE'
  character(len=obstype_length) :: obsin,obsfound
  !
  obsin = name
  call sic_upper(obsin)
  call sic_ambigs_sub(rname,obsin,obsfound,mrtindex_obstype_decode,obstypes_imbfits,  &
    nobstypes_imbfits+1,error)
  mrtindex_obstype_decode = mrtindex_obstype_decode-1  ! Because obstypes_imbfits numbering starts at 0
  if (mrtindex_obstype_decode.eq.-1) then
    call mrtindex_message(seve%w,rname,'Observation type '''//trim(obsin)//  &
      ''' not supported')
    mrtindex_obstype_decode = obstype_unknown
    ! Not an error, continue
  elseif (mrtindex_obstype_decode.eq.obstype_onoff) then
    mrtindex_obstype_decode = obstype_tracked
  endif
  !
end function mrtindex_obstype_decode
!
function mrtindex_obstype_imbfits(itype)
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Return the observation type name (IMBFITS) given its identifier.
  ! ---
  ! Note that sharing character arrays between libraries is not
  ! portable, this is why a function is used.
  !---------------------------------------------------------------------
  character(len=obstype_length) :: mrtindex_obstype_imbfits
  integer(kind=4), intent(in) :: itype
  !
  mrtindex_obstype_imbfits = obstypes_imbfits(itype)
  !
end function mrtindex_obstype_imbfits
!
function mrtindex_obstype(itype)
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Return the observation type name (MRTCAL) given its identifier.
  ! ---
  ! Note that sharing character arrays between libraries is not
  ! portable, this is why a function is used.
  !---------------------------------------------------------------------
  character(len=obstype_length) :: mrtindex_obstype
  integer(kind=4), intent(in) :: itype
  !
  mrtindex_obstype = obstypes_mrtcal(itype)
  !
end function mrtindex_obstype
!
function mrtindex_swmode_imbfits(imode)
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Return the switching mode name given its identifier.
  ! ---
  ! Note that sharing character arrays between libraries is not
  ! portable, this is why a function is used.
  !---------------------------------------------------------------------
  character(len=switchmode_length) :: mrtindex_swmode_imbfits
  integer(kind=4), intent(in) :: imode
  !
  mrtindex_swmode_imbfits = switchmodes_imbfits(imode)
  !
end function mrtindex_swmode_imbfits
!
function mrtindex_swmode(imode)
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Return the switching mode name given its identifier.
  ! ---
  ! Note that sharing character arrays between libraries is not
  ! portable, this is why a function is used.
  !---------------------------------------------------------------------
  character(len=switchmode_length) :: mrtindex_swmode
  integer(kind=4), intent(in) :: imode
  !
  mrtindex_swmode = switchmodes_mrtcal(imode)
  !
end function mrtindex_swmode
!
function mrtindex_swmode_voxml(imode)
  use mrtindex_parameters
  !---------------------------------------------------------------------
  ! @ public
  ! Return the switching mode name given its identifier.
  ! ---
  ! Note that sharing character arrays between libraries is not
  ! portable, this is why a function is used.
  !---------------------------------------------------------------------
  character(len=switchmode_length) :: mrtindex_swmode_voxml
  integer(kind=4), intent(in) :: imode
  !
  mrtindex_swmode_voxml = switchmodes_voxml(imode)
  !
end function mrtindex_swmode_voxml
