subroutine class_resample(set,line,r,error)
  use gbl_constant
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>class_resample
  use class_types
  !---------------------------------------------------------------------
  ! @ public
  ! CLASS Support routine for command
  !   RESAMPLE NX Xref Xval Xinc UNIT [shape] [width] [/FFT] [/NOFFT]
  ! [/LIKE GDFFile]
  ! Resamples a spectrum on a different grid
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set    !
  character(len=*),    intent(in)    :: line   ! Command line
  type(observation),   intent(inout) :: r      !
  logical,             intent(inout) :: error  ! Error flag
  ! Local
  character(len=*), parameter :: rname='RESAMPLE'
  type(resampling) :: new
  logical :: dofft,dolike
  integer(kind=4), parameter :: optfft  =1
  integer(kind=4), parameter :: optnofft=2
  integer(kind=4), parameter :: optlike =3
  !
  ! Basic checks
  if (r%head%xnum.eq.0) then
     call class_message(seve%f,rname,'No spectrum in memory.')
     error = .true.
     return
  endif
  !
  ! Decode input line arguments
  dolike = sic_present(optlike,0)
  if (sic_present(0,1) .and. dolike) then
    call class_message(seve%e,rname,'Command takes no argument if /LIKE is present')
    error = .true.
    return
  endif
  if (dolike) then
    call resample_parse_like(rname,line,optlike,new,error)
  else
    call resample_parse_command(line,0,rname,r%head,new,error)
  endif
  if (error) return
  !
  if (sic_present(optfft,0).and.sic_present(optnofft,0)) then
     call class_message(seve%f,rname,'/FFT and /NOFFT are exclusive from each other')
     error = .true.
     return
  endif
  dofft = sic_present(optfft,0)
  !
  ! Resample the R buffer
  call do_resample(set,r,new,dofft,error)
  if (error)  return
  !
  ! Finish the work
  call newdat(set,r,error)
  call newdat_assoc(set,r,error)
  !
end subroutine class_resample
!
subroutine resample_parse_command(line,iopt,rname,ref,new,error)
  use gbl_message
  use gkernel_interfaces
  use classcore_interfaces, except_this=>resample_parse_command
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  ! Parse resample parameters from command line
  !
  !  If frequency resampling, take care that the new resolution is
  ! returned in the current (usually observatory) frame of the reference
  ! header. BUT if the user invokes the command with an explicit value
  ! (i.e. something different from * or =), it is assumed to be given in
  ! the rest frame, and will be converted to the current frame. The idea
  ! behind this is that RESAMPLE does not modify the spectrum frame, but
  ! the user can RESAMPLE a serie of spectra to the same REST frequency
  ! resolution.
  !
  ! Default channel shapes and widths for 30-m (AOS unaccounted for)
  ! Shape is TPAR for telescopes other than the 30m
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line   ! Command line
  integer(kind=4),  intent(in)    :: iopt   ! Number of the RESAMPLE option
  character(len=*), intent(in)    :: rname  ! Calling routine name
  type(header),     intent(in)    :: ref    ! Reference header
  type(resampling), intent(out)   :: new    ! New definition of X-axis sampling
  logical,          intent(inout) :: error  ! Error flag
  ! Local
  integer(kind=4) :: nc,nchain,nutype
  integer(kind=4), parameter :: mutype=3
  character(len=16) :: utype(mutype),name
  character(len=128) :: chain  ! Should be long enough to accomodate double
                               ! precision values, or even any formula
  character(len=message_length) :: mess
  real(kind=8) :: val,dnu
  logical :: specunit,speckind,reverted
  integer(kind=4), parameter :: iarg_nchan=1
  integer(kind=4), parameter :: iarg_ref  =2
  integer(kind=4), parameter :: iarg_val  =3
  integer(kind=4), parameter :: iarg_inc  =4
  integer(kind=4), parameter :: iarg_unit =5
  integer(kind=4), parameter :: iarg_shape=6
  integer(kind=4), parameter :: iarg_width=7
  ! Accepted type of units
  data utype/'VELOCITY','FREQUENCY','ANGLE'/
  !
  ! Order matters here, especially if using wildcards!
  !
  ! UNIT: Axis unit
  call sic_ke(line,iopt,iarg_unit,chain,nchain,.true.,error)
  if (error) return
  call sic_ambigs(rname,chain,name,nutype,utype,mutype,error)
  if (error) return
  new%unit = name(1:1)
  !
  ! Sanity
  specunit = new%unit.eq.'V' .or. new%unit.eq.'F'
  speckind = ref%gen%kind.eq.kind_spec
  if (specunit.neqv.speckind) then
    call class_message(seve%e,rname,'Unit '//new%unit//' is invalid for this data kind')
    error = .true.
    return
  endif
  !
  ! XVAL: Axis value at ref channel
  call sic_ch(line,iopt,iarg_val,chain,nchain,.true.,error)
  if (error)  return
  if (chain.eq.'=' .or. chain.eq.'*') then
    if (new%unit.eq.'V') then
      new%val = ref%spe%voff
    elseif (new%unit.eq.'F') then
      new%val = ref%spe%restf
    elseif (new%unit.eq.'A') then
      new%val = ref%dri%aref
    endif
  else
    call sic_math_dble(chain,nchain,new%val,error)
    if (error) return
  endif
  !
  ! XINC: Axis resolution
  call sic_ch(line,iopt,iarg_inc,chain,nchain,.true.,error)
  if (error)  return
  reverted = .false.  ! Reverted axis?
  if (chain.eq.'=' .or. chain.eq.'*') then
    if (new%unit.eq.'V') then
      new%inc = ref%spe%vres
    elseif (new%unit.eq.'F') then
      new%inc = ref%spe%fres  ! Reuse the value in the observatory frame
    elseif (new%unit.eq.'A') then
      new%inc = ref%dri%ares
    endif
  else
    call sic_math_dble(chain,nchain,new%inc,error)
    if (error) return
    if (new%unit.eq.'V') then
      reverted = new%inc*ref%spe%vres.lt.0.d0  ! Reverted axis?
    elseif (new%unit.eq.'F') then
      ! User value is given in the rest frame: apply Doppler correction (if
      ! available) to convert it to current (observatory) frame.
      if (ref%spe%doppler.ne.-1.d0)  new%inc = new%inc*(1.d0+ref%spe%doppler)
      reverted = new%inc*ref%spe%fres.lt.0.d0  ! Reverted axis?
    elseif (new%unit.eq.'A') then
      reverted = new%inc*ref%dri%ares.lt.0.d0  ! Reverted axis?
    endif
  endif
  if (new%unit.eq.'F') then
    if (ref%spe%doppler.ne.-1.d0) then
      dnu = new%inc/(1.d0+ref%spe%doppler)
      write(mess,'(A,2(F0.5,A))')  &
        'Frequency resolution: ',new%inc,' MHz (observatory), ',  &
        dnu,' MHz (rest frame)'
    else
      dnu = new%inc
      write(mess,'(A,F0.5,A)')  &
        'Frequency resolution: ',new%inc,' MHz (observatory, Doppler unknown)'
    endif
    call class_message(seve%i,rname,mess)
  endif
  !
  ! XREF: Axis reference channel
  call sic_ch(line,iopt,iarg_ref,chain,nchain,.true.,error)
  if (error)  return
  if (chain.eq.'=') then
    new%ref = ref%spe%rchan
  elseif (chain.eq.'*') then
    ! Automatic: define Xref such as new 1st channel and old 1st channel are
    ! aligned (more precisely: left side of 1st channels). If axis has to
    ! be reverted (opposite sign), align new first channel on old last channel.
    if (new%unit.eq.'V') then
      if (reverted) then
        call abscissa_velo_right(ref,val)
      else
        call abscissa_velo_left(ref,val)
      endif
      new%ref = .5d0 - (val-new%val)/new%inc
    elseif (new%unit.eq.'F') then
      if (reverted) then
        call abscissa_sigabs_right(ref,val)
      else
        call abscissa_sigabs_left(ref,val)
      endif
      new%ref = .5d0 - (val-new%val)/dnu
    elseif (new%unit.eq.'A') then
      if (reverted) then
        call abscissa_angl_right(ref,val)
      else
        call abscissa_angl_left(ref,val)
      endif
      new%ref = .5d0 - (val-new%val)/new%inc
    endif
  else
    call sic_math_dble(chain,nchain,new%ref,error)
    if (error) return
  endif
  !
  ! NCHAN: Axis number of channels
  call sic_ch(line,iopt,iarg_nchan,chain,nchain,.true.,error)
  if (error)  return
  if (chain.eq.'=') then
    new%nchan = ref%spe%nchan
  elseif (chain.eq.'*') then
    ! Automatic: define Nchan such as new last channel and old last channel
    ! are aligned (more precisely: right side of last channels). If axis has
    ! to be reverted (opposite sign), align new last channel on old fist
    ! channel.
    if (new%unit.eq.'V') then
      if (reverted) then
        call abscissa_velo_left(ref,val)
      else
        call abscissa_velo_right(ref,val)
      endif
      new%nchan = ceiling(new%ref - .5d0 + (val-new%val)/new%inc)
    elseif (new%unit.eq.'F') then
      if (reverted) then
        call abscissa_sigabs_left(ref,val)
      else
        call abscissa_sigabs_right(ref,val)
      endif
      new%nchan = ceiling(new%ref - .5d0 + (val-new%val)/dnu)
    elseif (new%unit.eq.'A') then
      if (reverted) then
        call abscissa_angl_left(ref,val)
      else
        call abscissa_angl_right(ref,val)
      endif
      new%nchan = ceiling(new%ref - .5d0 + (val-new%val)/new%inc)
    endif
  else
    call sic_math_inte(chain,nchain,new%nchan,error)
    if (error) return
    new%nchan = max(new%nchan,1)
  endif
  !
  ! SHAPE: Channel shape (optional)
  if (sic_present(iopt,iarg_shape)) then
    call sic_ch(line,iopt,iarg_shape,chain,nchain,.true.,error)
    if (error)  return
    if (chain.eq.'=' .or. chain.eq.'*') then
      new%shape = channel_shape(ref%gen%teles)
    else
      call sic_ke(line,iopt,iarg_shape,new%shape,nc,.true.,error)
      if (error) return
    endif
  else
    new%shape = channel_shape(ref%gen%teles)
  endif
  !
  ! WIDTH: Channel width (optional)
  if (sic_present(iopt,iarg_width)) then
    call sic_ch(line,iopt,iarg_width,chain,nchain,.true.,error)
    if (error)  return
    if (chain.eq.'=' .or. chain.eq.'*') then
      new%width = 1.0
    else
      call sic_math_real(chain,nchain,new%width,error)
      if (error) return
    endif
  else
    new%width = 1.0
  endif
  !
  write(mess,'(A,I0,3(A,F0.3),A,L1,5A,F0.3)')  &
    'Nchan: ',new%nchan,', Ref: ',new%ref,', Val: ',new%val,', Inc: ',  &
    new%inc,' (reverted: ',reverted,'), Unit: ',new%unit,', Shape: ',   &
    trim(new%shape),', Width: ',new%width
  call class_message(seve%d,rname,mess)
  !
end subroutine resample_parse_command
!
subroutine resample_parse_like(rname,line,iopt,new,error)
  use gbl_message
  use image_def
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>resample_parse_like
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Parse /LIKE GDFFile option and setup a resampling structure
  ! according to the GDF header (can be an LMV cube or a UVT)
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: rname  ! Calling routine name
  character(len=*), intent(in)    :: line   ! Command line
  integer(kind=4),  intent(in)    :: iopt   ! Option number
  type(resampling), intent(out)   :: new    ! New definition of X-axis sampling
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  type(gildas) :: hgdf
  type(header) :: hclass
  integer(kind=4) :: nc
  !
  call gildas_null(hgdf)
  !
  ! Parse command line
  call sic_ch(line,iopt,1,hgdf%file,nc,.true.,error)
  if (error)  return
  !
  ! Read GDF header
  call gdf_read_header(hgdf,error)
  if (error) return
  call class_message(seve%i,rname,'/LIKE file is '//hgdf%file)
  !
  ! Build a Class spectro section from GDF header
  call gdf2class_spectro(hgdf,hclass,error)
  if (error)  return
  !
  ! Setup the resampling description
  new%nchan = hclass%spe%nchan
  new%ref   = hclass%spe%rchan
  new%val   = hclass%spe%voff
  new%inc   = hclass%spe%vres
  new%unit  = 'V'
  new%shape = '*'  ! Need the telescope name to guess the shape...
  new%width = 1.0
  !
end subroutine resample_parse_like
!
function channel_shape(teles)
  use gkernel_interfaces
  use classcore_interfaces, except_this=>channel_shape
  !---------------------------------------------------------------------
  ! @ private
  ! Return the channel shape given the telescope name.
  ! Test is not very robust as PI often temper with the telescope name
  ! to get other information like the used backend, the OTF scanning
  ! direction...
  !---------------------------------------------------------------------
  character(len=4) :: channel_shape  ! Function value on return
  character(len=*), intent(in) :: teles  ! Telescope name
  ! Local
  character(len=12) :: cteles
  integer(kind=4) :: backnum
  character(len=4) :: def_shape(9)
  ! Shape of the channel response for each 30m backend.
  data def_shape /3*'FBOX',2*'TPAR',4*'FBOX'/
  !
  cteles = teles
  call sic_upper(cteles)
  if (cteles(1:8).eq.'IRAM-30M') then
     read (cteles(11:11),'(i1)') backnum
     channel_shape = def_shape(backnum)
  else
     channel_shape = 'TPAR'
  endif
  !
end function channel_shape
!
subroutine do_resample(set,obs,new_in,dofft,error)
  use gildas_def
  use phys_const
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>do_resample
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  ! Resample the observation
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set     !
  type(observation),   intent(inout) :: obs     ! Observation before and after resampling
  type(resampling),    intent(in)    :: new_in  ! New definition of X-axis sampling
  logical,             intent(in)    :: dofft   ! In frequency or time domain (the default)
  logical,             intent(out)   :: error   ! Error flag
  ! Local
  character(len=*), parameter :: rname='RESAMPLE'
  logical          :: resample
  character(len=80) :: mess
  integer(kind=4)  :: ikappa
  type(resampling) :: old,new   ! Definition of X-axis sampling
  type(extract_t) :: extr  ! Description of the extraction, if relevant
  real(kind=8) :: kappa,old_restf
  real(kind=8), parameter :: precis=1.d-3  ! Would 1% of a channel on the total spectra bandwidth be enough?
  !
  error = .false.
  new = new_in  ! This avoids modifying the input 'new'
  !
  ! Channel shape and width
  old%shape = channel_shape(obs%head%gen%teles)
  old%width = 1.0  ! Why old%width is arbitrary set to 1? *** JP
  !
  ! Work space
  old%nchan = obs_nchan(obs%head)
  !
  ! Test whether a translation is enough instead of a full resampling
  if (obs%head%presec(class_sec_xcoo_id)) then
     ! Irregularly spaced data
     if (new%unit.ne.set%unitx(1)) then
        mess = 'Unit modification not supported for irregularly sampled data '
        call class_message(seve%f,rname,mess)
        error = .true.
        return
     endif
     resample = .true.
     if (new%unit.eq.'F') then
       ! Modify frequency
       old_restf = obs%head%spe%restf
       obs%head%spe%restf = new%val
       ! We should shift accordingly all the RDATAX. But we can also
       ! put this shift in new%val:
       new%val = obs%head%spe%restf-old_restf
     endif
  else
     ! Regularly spaced data
     if (new%unit.eq.'V') then
        ! Velocity unit
        call modify_velocity(obs,new%val,error)
        if (error)  return
        !
        old%ref = obs%head%spe%rchan
        old%val = obs%head%spe%voff
        old%inc = obs%head%spe%vres
        !
     elseif (new%unit.eq.'F') then
        ! Frequency unit => the reference frequency is set to 'new%val'
        call modify_frequency(obs,new%val,error)
        if (error)  return
        new%val = 0.d0  ! New FOFF
        !
        old%ref = obs%head%spe%rchan
        old%val = 0.d0
        old%inc = obs%head%spe%fres
        !
     elseif (new%unit.eq.'A') then
        ! Angle unit
        old%ref = obs%head%dri%rpoin
        old%val = obs%head%dri%aref
        old%inc = obs%head%dri%ares
        !
     else
        mess = 'Invalid unit type '//new%unit
        call class_message(seve%f,rname,mess)
        error = .true.
        return
     endif
     old%unit = new%unit
     !
     if (dofft) then
       ! Always resample, i.e. do not use the extraction mode if FFT is requested
       resample = .true.
       !
     else
       ! Should we really resample or just extract a subset?
       !    Test 1: Does the new resolution differ too much from old one?
       resample = abs((new%inc/old%inc-1.d0)*new%nchan).gt.precis
       !
       if (.not.resample) then
         ! Test 2: Are the value and reference channels changed inconsistently?
         !         so that: whatever old_i, new%i = old_i + kappa
         !                  with kappa integer
         !  Note that small offsets in value and reference channels do not
         !  amplify with increasing number of channels, i.e. the effect is the
         !  same on the 10th channel and on the 1000000th...
         kappa = (new%ref-old%ref) + (old%val-new%val)/old%inc
         ikappa = nint(kappa)
         resample = abs(kappa-ikappa).gt.precis
       endif
     endif
  endif
  !
  ! Do the real job
  if (resample) then
    ! Resampling (also resamples the Associated Arrays)
    call do_resample_sub(set,obs,old,new,dofft,error)
    if (error)  return
  else
     ! No resampling needed, only extract (also extracts the Associated Arrays)
     if ((1-ikappa).gt.old%nchan .or. (new%nchan-ikappa).lt.1) then
       ! Check is done out of do_extract for efficiency of the EXTRACT
       ! command.
       call do_resample_nointersecterror_regul(old,new)
       error = .true.
     else
       extr%rname = rname
       extr%c1 = 1-ikappa
       extr%c2 = new%nchan-ikappa
       extr%nc = new%nchan
       call do_extract(obs,extr,error)
     endif
  endif
  if (error)  return
  !
  ! Update header
  if (new%unit.eq.'V') then
    obs%head%spe%nchan = new%nchan
    obs%head%spe%rchan = new%ref
  ! obs%head%spe%doppler = unchanged (stay in the same frame)
    obs%head%spe%vres = new%inc
    obs%head%spe%voff = new%val
  ! obs%head%spe%restf = unchanged
    obs%head%spe%fres = -obs%head%spe%restf/clight_kms*obs%head%spe%vres
  elseif (new%unit.eq.'F') then
    obs%head%spe%nchan = new%nchan
    obs%head%spe%rchan = new%ref
  ! obs%head%spe%doppler = unchanged (stay in the same frame)
  ! obs%head%spe%restf = changed in modify_frequency
    obs%head%spe%fres = new%inc
    obs%head%spe%vres = -clight_kms/obs%head%spe%restf*obs%head%spe%fres
  ! obs%head%spe%voff = unchanged
  elseif (new%unit.eq.'A') then
    obs%head%dri%npoin = new%nchan
    obs%head%dri%rpoin = new%ref
    obs%head%dri%ares  = new%inc
    obs%head%dri%aref  = new%val
  ! obs%head%dri%tref  = unknown?
  ! obs%head%dri%tres  = unknown?
  endif
  !
end subroutine do_resample
!
subroutine do_resample_sub(set,obs,old,new,dofft,error)
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>do_resample_sub
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set    !
  type(observation),   intent(inout) :: obs    !
  type(resampling),    intent(in)    :: old    ! Old X-axis sampling
  type(resampling),    intent(inout) :: new    ! New X-axis sampling (increment can be updated)
  logical,             intent(in)    :: dofft  ! In frequency or time domain (the default)
  logical,             intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='RESAMPLE'
  real(kind=4), allocatable :: idatay(:)
  integer(kind=4) :: ier
  logical :: isirreg
  !
  ! Save input Y values in dedicated array
  allocate(idatay(old%nchan),stat=ier)
  if (failed_allocate(rname,'y value workspace',ier,error))  return
  idatay(1:old%nchan) = obs%spectre(1:old%nchan)
  !
  ! Enlarge data space when needed
  call reallocate_obs(obs,new%nchan,error)
  if (error) return
  obs%head%spe%nchan = new%nchan
  obs%cnchan = new%nchan
  !
  isirreg = obs%head%presec(class_sec_xcoo_id)
  call do_resample_generic(set,obs%datax,idatay,obs%spectre,obs%dataw,obs%cbad,  &
    isirreg,old,new,dofft,error)
  if (error)  return
  !
  if (obs%head%presec(class_sec_assoc_id)) then
    ! Also resample Associated Arrays
    isirreg = obs%head%presec(class_sec_xcoo_id)
    call resample_assoc(set,obs%assoc,obs%datax,isirreg,old,new,dofft,error)
    if (error) then
      call class_message(seve%w,rname,  &
        'Section Associated Array could not be resampled. Removed.')
      obs%head%presec(class_sec_assoc_id) = .false.
      call rzero_assoc(obs)
      error = .false.  ! Not fatal
    endif
  endif
  !
  ! Irregularly sampled X axis normally became regular
  obs%head%presec(class_sec_xcoo_id) = isirreg
  !
end subroutine do_resample_sub
!
subroutine do_resample_generic(set,idatax,idatay,odatay,odataw,bad,isirreg,  &
  old,new,dofft,error)
  use classcore_interfaces, except_this=>do_resample_generic
  use class_parameter
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Resample any kind of array
  !---------------------------------------------------------------------
  type(class_setup_t),   intent(in)    :: set        !
  real(kind=xdata_kind), intent(in)    :: idatax(:)  !
  real(kind=4),          intent(in)    :: idatay(:)  !
  real(kind=4),          intent(out)   :: odatay(:)  !
  real(kind=4),          intent(out)   :: odataw(:)  !
  real(kind=4),          intent(in)    :: bad        !
  logical,               intent(inout) :: isirreg    ! Is X axis regularly sampled?
  type(resampling),      intent(in)    :: old        ! Old X-axis sampling
  type(resampling),      intent(inout) :: new        ! New X-axis sampling
  logical,               intent(in)    :: dofft      !
  logical,               intent(inout) :: error      ! Logical error flag
  ! Local
  integer(kind=4) :: irmin,irmax
  !
  ! Look for first non-blanked channel
  irmin = obs_firstgood(idatay,old%nchan,bad)
  !
  if (irmin.eq.old%nchan) then
    ! All channels are blanked. Fill all channels with blanks and return
    odatay(1:new%nchan) = bad
    return
  endif
  !
  if (dofft) then
    ! Look for last non-blanked channel
    irmax = obs_lastgood(idatay,old%nchan,bad)
    call do_resample_fft(idatay,odatay,bad,isirreg,irmin,irmax,old,new,error)
  else
    call do_resample_direct(set,idatax,idatay,odatay,odataw,bad,isirreg,old,new,error)
  endif
  if (error)  return
  !
end subroutine do_resample_generic
!
subroutine do_resample_fft(idatay,odatay,bad,isirreg,irmin,irmax,  &
  old_in,new_in,error)
  use gbl_message
  use gkernel_interfaces
  use classcore_interfaces, except_this=>do_resample_fft
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  ! FFT resampling on a regularly spaced axis
  !---------------------------------------------------------------------
  real(kind=4),         intent(in)    :: idatay(:)  ! Size old%nchan
  real(kind=4), target, intent(out)   :: odatay(:)  ! Size new%nchan
  real(kind=4),         intent(in)    :: bad        !
  logical,              intent(in)    :: isirreg    !
  integer(kind=4),      intent(in)    :: irmin      ! First input channel to be used
  integer(kind=4),      intent(in)    :: irmax      ! Last input channel to be used
  type(resampling),     intent(in)    :: old_in     ! Old X-axis sampling
  type(resampling),     intent(inout) :: new_in     ! New X-axis sampling
  logical,              intent(inout) :: error      ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='RESAMPLE'
  real(kind=4), allocatable :: yvalue(:)
  integer(kind=4) :: ier,ismin,ismax,richan
  type(resampling) :: old,new
  real(kind=4), pointer :: newspectre(:)
  !
  if (isirreg) then
    call class_message(seve%e,rname,  &
      '/FFT is not supported on an irregularly sampled X axis')
    error = .true.
    return
  endif
  !
  ! Compute the output range to be updated:
  old       = old_in
  old%nchan = irmax-irmin+1
  old%ref   = old_in%ref-real(irmin-1,kind=8)
  call resample_interpolate_range(old,new_in,.false.,ismin,ismax,error)
  if (error)  return
  !
  ! Copy input data with special treatment to blanked channels
  allocate (yvalue(old%nchan),stat=ier)
  if (failed_allocate(rname,'y value workspace',ier,error))  goto 100
  do richan=irmin,irmax
    yvalue(richan-irmin+1) = obs_good(idatay,bad,irmin,irmax,richan)
  enddo
  !
  ! Output spectrum: fill blank pixels at edges
  odatay(1:ismin-1) = bad
  odatay(ismax+1:new_in%nchan) = bad
  !
  new       = new_in
  new%nchan = ismax-ismin+1
  new%ref   = new_in%ref-real(ismin-1,kind=8)
  newspectre => odatay(ismin:ismax)
  call fft_interpolate(yvalue,old,newspectre,new,error)
  if (error)  goto 100
  new_in%inc = new%inc  ! May have been updated
  !
100 continue
  ! Free memory
  if (allocated(yvalue)) deallocate(yvalue)
  !
end subroutine do_resample_fft
!
subroutine do_resample_direct(set,idatax,idatay,odatay,odataw,bad,  &
  isirreg,old,new,error)
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>do_resample_direct
  use class_parameter
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  ! Direct interpolation
  !---------------------------------------------------------------------
  type(class_setup_t),   intent(in)    :: set        !
  real(kind=xdata_kind), intent(in)    :: idatax(:)  ! Size old%nchan
  real(kind=4),          intent(in)    :: idatay(:)  ! Size old%nchan
  real(kind=4),          intent(out)   :: odatay(:)  ! Size new%nchan
  real(kind=4),          intent(out)   :: odataw(:)  ! Size new%nchan
  real(kind=4),          intent(in)    :: bad        !
  logical,               intent(inout) :: isirreg    ! Is X axis regularly sampled?
  type(resampling),      intent(in)    :: old        ! Old X-axis sampling
  type(resampling),      intent(in)    :: new        ! New X-axis sampling
  logical,               intent(inout) :: error      ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='RESAMPLE'
  real(kind=4), allocatable :: idataw(:)
  integer(kind=4) :: ier,ismin,ismax
  !
  allocate (idataw(old%nchan),stat=ier)
  if (failed_allocate(rname,'w value workspace',ier,error))  goto 100
  idataw(:) = 1.
  !
  if (isirreg) then
    ! Irregularly spaced axis
    call resample_interpolate_irreg(set,                          &
                                    idatax,idatay,bad,old%nchan,  &
                                    odatay,odataw,bad,new,error)
    if (error)  goto 100
    isirreg = .false.
    !
  else
    call resample_interpolate_regul(set,                    &
                                    idatay,idataw,bad,old,  &
                                    odatay,odataw,bad,new,  &
                                    ismin,ismax,error)
    if (error)  goto 100
  endif
  !
  ! odataw == 0 means no data or bad data: blank them
  where (odataw(1:new%nchan).eq.0.)
    odatay(1:new%nchan) = bad
  end where
  !
100 continue
  ! Free memory
  if (allocated(idataw)) deallocate(idataw)
  !
end subroutine do_resample_direct
!
subroutine do_resample_nointersecterror_regul(old,new)
  use gbl_message
  use classcore_interfaces, except_this=>do_resample_nointersecterror_regul
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(resampling), intent(in) :: old  ! Old X-axis sampling
  type(resampling), intent(in) :: new  ! New X-axis sampling
  ! Local
  character(len=*), parameter :: rname='RESAMPLE'
  real(kind=8) :: rxmin,rxmax,sxmin,sxmax
  character(len=message_length) :: mess
  !
  call class_message(seve%e,rname,  &
    'New spectrum does not intersect the original one')
  !
  rxmin = (           .5d0 -old%ref)*old%inc+old%val
  rxmax = ((old%nchan+.5d0)-old%ref)*old%inc+old%val
  sxmin = (           .5d0 -new%ref)*new%inc+new%val
  sxmax = ((new%nchan+.5d0)-new%ref)*new%inc+new%val
  !
  write(mess,'(A,A,2(A,F0.2,1X,F0.2))')                    &
           'Original ',new%unit,                           &
            ' range: ',min(rxmin,rxmax),max(rxmin,rxmax),  &
             ', new: ',min(sxmin,sxmax),max(sxmin,sxmax)
  call class_message(seve%e,rname,mess)
  !
end subroutine do_resample_nointersecterror_regul
!
subroutine do_resample_nointersecterror_irreg(rdatax,rnchan,new)
  use gbl_message
  use class_parameter
  use classcore_interfaces, except_this=>do_resample_nointersecterror_irreg
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  real(xdata_kind), intent(in) :: rdatax(:)  ! Old irregular X-axis
  integer(kind=4),  intent(in) :: rnchan     ! Number of channels
  type(resampling), intent(in) :: new        ! New X-axis sampling
  ! Local
  character(len=*), parameter :: rname='RESAMPLE'
  real(kind=8) :: rxmin,rxmax,sxmin,sxmax
  character(len=message_length) :: mess
  !
  call class_message(seve%e,rname,  &
    'New spectrum does not intersect the original one')
  !
  rxmin = rdatax(1)
  rxmax = rdatax(rnchan)
  sxmin = (           .5d0 -new%ref)*new%inc+new%val
  sxmax = ((new%nchan+.5d0)-new%ref)*new%inc+new%val
  !
  write(mess,'(A,A,2(A,F0.2,1X,F0.2))')                    &
           'Original ',new%unit,                           &
            ' range: ',min(rxmin,rxmax),max(rxmin,rxmax),  &
             ', new: ',min(sxmin,sxmax),max(sxmin,sxmax)
  call class_message(seve%e,rname,mess)
  !
end subroutine do_resample_nointersecterror_irreg
!
subroutine fft_interpolate(y,old,x,new,error)
  use gbl_message
  use classcore_interfaces, except_this=>fft_interpolate
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  ! Performs the interpolation/integration, using FFT.
  ! SHAPE: Channel shape in TBox, TParabola, FBox or Ftriangle
  !  TBox means a box in delay space          (unsmoothed correlator, w=1)
  !  Ppar means a parabola in delay space     (smoothed correlator, w=1)
  !  FBox means a box in frequency space      (square filter, w=0.5)
  !  FTri means a triangle in frequency space (Hanning smoothed square filter, w=1)
  !---------------------------------------------------------------------
  real(kind=4),     intent(in)    :: y(:)     ! Input spectrum
  type(resampling), intent(in)    :: old      ! Old axis description
  real(kind=4),     intent(out)   :: x(:)     ! Output spectrum
  type(resampling), intent(inout) :: new      ! New axis description (increment can be rounded)
  logical,          intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='FFT_INTERPOLATE'
  integer(kind=4) :: ier,nstep,nx,ioff,p
  complex(kind=4), allocatable :: yp(:), wp(:)
  real(kind=4) :: step,c1,roff,yval1
  character(len=message_length) :: chain
  !
  if (old%width.lt.1.0 .or. new%width.lt.1.0) then
    call class_message(seve%e,rname,'Channel width can not be lower than 1.0')
    error = .true.
    return
  endif
  !
  ! Steps in FFT space are 1/(old%nchan*old%inc)=step
  step = 1d0/(old%nchan*old%inc)
  nx = nint(1d0/step/new%inc)
  nx = abs(nx)
  call pfactor(nx,p)
  do while (p.gt.100)
     nx = nx+1
     call pfactor(nx,p)
  enddo
  nstep = max(nx,2*old%nchan)
  ! check for large prime factors
  if (new%inc.ne.1d0/step/nx) then
     new%inc = sign(1.d0/step/nx,new%inc)
     write(chain,'(a,1pg13.6)') 'Output channel separation rounded to ',new%inc
     call class_message(seve%i,rname,chain)
  endif
  !
  ! Do FFT of input spectrum
  allocate(yp(nstep),wp(nstep),stat=ier)
  if (ier.ne.0) then
     error = .true.
     return
  endif
  call r4toc4(y,yp,old%nchan)
  if (old%inc*new%inc.lt.0d0) then
     ! Ensure that the old and new axes increase in the same direction (channel-wise)
     call reverse(old%nchan,yp)
     yval1 = old%val+(old%nchan-old%ref)*old%inc  ! Value at first old channel
  else
     yval1 = old%val+(1.-old%ref)*old%inc  ! Value at first old channel
  endif
  call fourt(yp,old%nchan,1,1,0,wp)
  call fft_normalize(old%nchan,yp)
  !
  ! Deconvolve from input channel shape
  call fft_deconv(old%nchan,yp,old%width,old%shape)
  if (nstep.gt.old%nchan) then
     call fft_extend(yp,old%nchan,nstep)
  endif
  !
  ! Convolve to output channel shape
  if (nx.lt.nstep) then
     call fft_cutoff(yp,nstep,nx)
  endif
  call fft_reconv(nx,yp,new%width,new%shape)
  !
  ! Correct for channel offset
  c1 = (yval1-new%val)/new%inc+new%ref  ! Position of yval1 on the new axis (channel)
  roff = 1.-c1       ! Channel offset (old-new)
  ioff = nint(roff)  ! Channel offset (integer part)
  roff = roff-ioff   ! Channel offset (residual in the range [-.5,.5])
  call fft_offset(roff,nx,yp)
  !
  ! FFT to output spectrum
  call fourt(yp,nx,1,-1,1,wp)
  if (ioff.ge.0) then
    call c4tor4(yp(1+ioff),x,new%nchan)
  else
    ! This should never happen because when calling fft_interpolate, the
    ! command sends only the subpart of the spectrum to be filled, and
    ! modifies new%ref to loc_ref accordingly. Example:
    !      Xref  Xval Xinc
    ! Old   1.    1.   1.     1st channel has velocity 1.
    ! New  10.    1.   1.     10th channel has velocity 1.
    ! Sent  0.    1.   1.     0th channel has velocity 1.
    ! In the 'sent' spectrum, previous channels are first filled with
    ! blanks and then ignored, and the others are effectively filled here.
    call class_message(seve%e,rname,  &
      'Internal error, unsupported axes configuration')
    call class_message(seve%e,rname,  &
      'Please send your spectrum and the RESAMPLE command to gildas@iram.fr')
    error = .true.
    ! call c4tor4(yp,x,new%nchan)  ! CLASS77 implementation, probably wrong
  endif
  deallocate(wp,yp)
end subroutine fft_interpolate
!
subroutine fft_offset(x,nc,z)
  use phys_const
  !---------------------------------------------------------------------
  ! @ private
  ! Offset by x channels
  !---------------------------------------------------------------------
  real,    intent(in)    :: x     ! offset by x channels
  integer, intent(in)    :: nc    ! Dimension of input array
  complex, intent(inout) :: z(nc) ! Input array
  ! Local
  complex :: expiarg,expinarg
  integer :: ic
  real :: arg
  !
  arg = -2*pi/nc*x
  expiarg = cmplx(cos(arg),sin(arg))
  expinarg = cmplx(1.,0.)
  do ic=2,nc/2
     expinarg = expinarg*expiarg
     z(ic) = z(ic)*expinarg
     z(nc-ic+1) = z(nc-ic+1)/expinarg
  enddo
end subroutine fft_offset
!
subroutine fft_normalize(n,z)
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  integer, intent(in)    :: n    ! Dimension of input array
  complex, intent(inout) :: z(n) ! Input array
  ! Local
  real :: norm
  !
  norm = 1.0/n
  z = z*norm
end subroutine fft_normalize
!
subroutine fft_reconv(n,y,w,shape)
  use phys_const
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  integer,          intent(in)    :: n     ! Dimension of input array
  complex,          intent(inout) :: y(n)  ! Input array
  real,             intent(in)    :: w     ! width (in terms of channel separation)
  character(len=*), intent(in)    :: shape ! Convolution function
  ! Local
  integer :: nmax,j,itab(n)
  real :: tmax,a,arg,fac(n)
  !
  tmax = n/w/2
  nmax = tmax
  a = 1./tmax**2
  arg = pi/tmax/2
  itab =  (/(mod(j-1+n/2,n)-n/2,j=1,n)/)
  select case (shape(1:2))
  case('TB')
     ! Raw Correlator channels (no smoothing, sin(pi f/w)/(pi x/w),
     !  w is the first zero, measured in channel spacings.)
     where (abs(itab).gt.nmax) y = (0.,0.)
  case('TP')
     ! Smoothed correlator channels (parabolic smoothing function)
     where (abs(itab).gt.nmax)
        y = (0.,0.)
     elsewhere
        y = y * (1.-a*itab**2)
     end where
  case('FB')
     ! Frequency Box of width = w * channel-spacing
     fac = arg*itab
     where (fac.ne.0.) y = y*sin(fac)/fac
  case('FT')
     ! Frequency Triangle of full-width = 2w * channel-spacing
     fac = arg*itab
     where (fac.ne.0.) y = y*(sin(fac)/fac)**2
  end select
end subroutine fft_reconv
!
subroutine fft_deconv(n,y,w,shape)
  use phys_const
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  integer,          intent(in)    :: n     ! Dimension of input array
  complex,          intent(inout) :: y(n)  ! Input array
  real,             intent(in)    :: w     ! width (in terms of channel separation)
  character(len=*), intent(in)    :: shape ! Convolution function
  ! Local
  integer :: j,itab(n)
  real :: tmax,a,arg,fac(n),sfac(n)
  !
  tmax = n/w/2
  a = 1./tmax**2
  arg = pi/tmax/2
  itab = (/(mod(j-1+n/2,n)-n/2,j=1,n)/)
  select case (shape(1:2))
  case('TB')
     ! Raw Correlator channels (no smoothing, sin(pi f/w)/(pi x/w),
     !  w is the first zero, measured in channel spacings.)
     ! nothing to do ...
     return
  case('TP')
     ! Smoothed correlator channels (parabolic smoothing function)
     where (a*itab**2.lt.1.) y = y/(1.-a*itab**2)
  case('FB')
     ! Frequency Box of half-width = w * channel-spacing
     fac = arg*itab
     sfac = sin(fac)
     where (sfac.ne.0.) y = y*fac/sfac
  case('FT')
     ! Frequency Triangle of half-full-width = 2w * channel-spacing
     fac = arg*itab
     sfac = sin(fac)
     where (sfac.ne.0.) y = y * (fac/sfac)**2
  end select
  !
end subroutine fft_deconv
!
subroutine reverse(n,r)
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  integer, intent(in)    :: n
  complex, intent(inout) :: r(n)
  ! Local
  integer :: i
  complex :: temp
  !
  do i=1, n/2
     temp = r(i)
     r(i) = r(n-i+1)
     r(n-i+1) = temp
  enddo
end subroutine reverse
!
subroutine fft_extend(c,n,m)
  !---------------------------------------------------------------------
  ! @ private
  ! complete FFT with zeroes in high frequencies
  !   c[1:n/2]       : not touched
  !   c[n/2+1:m-n/2] = 0.
  !   c[m-n/2+1:m]   = c[n/2+1:n]
  !---------------------------------------------------------------------
  integer, intent(in)    :: n    ! Former dimension
  integer, intent(in)    :: m    ! New dimension
  complex, intent(inout) :: c(m) ! New array
  ! Local
  integer :: i,n2
  !
  n2 = n/2
  do i=1, n2
     c(m-i+1) = c(n-i+1)
  enddo
  do i=n2+1, m-n2
     c(i) = 0
  enddo
end subroutine fft_extend
!
subroutine fft_cutoff(c,m,n)
  !---------------------------------------------------------------------
  ! @ private
  ! Cutoff FFT in high frequencies
  !---------------------------------------------------------------------
  integer, intent(in)    :: n    ! Former dimension
  integer, intent(in)    :: m    ! New dimension
  complex, intent(inout) :: c(m) ! New array
  ! Local
  integer :: i,n2
  !
  n2 = n/2
  if (n.gt.2*n2) then
     c(n2+1) = (c(n2+1)+c(m-n2+1))/2.
  endif
  do i=n2, 1, -1
     c(n-i+1) = c(m-i+1)
  enddo
end subroutine fft_cutoff
!
subroutine resample_interpolate_range(old,new,edges,ismin,ismax,error)
  use classcore_interfaces, except_this=>resample_interpolate_range
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  ! Compute the range of channels to be updated in the output spectrum
  !---------------------------------------------------------------------
  type(resampling), intent(in)    :: old     ! Old X axis description
  type(resampling), intent(in)    :: new     ! New X axis description
  logical,          intent(in)    :: edges   ! Should we consider the edges?
  integer(kind=4),  intent(out)   :: ismin   ! Output channel range (min)
  integer(kind=4),  intent(out)   :: ismax   ! Output channel range (max)
  logical,          intent(inout) :: error   ! Logical error flag
  ! Local
  real(kind=8) :: xr1,xr2,is1,is2
  !
  ! Look for the range of output channels we have to work on (no need to
  ! loop on is=1,new%nchan)
  if (edges) then
    xr1 = (          .5-old%ref)*old%inc+old%val
    xr2 = (old%nchan+.5-old%ref)*old%inc+old%val
  else
    xr1 = (       1.-old%ref)*old%inc+old%val
    xr2 = (old%nchan-old%ref)*old%inc+old%val
  endif
  is1 = (xr1-new%val)/new%inc+new%ref  ! Float value
  is2 = (xr2-new%val)/new%inc+new%ref  ! Float value
  ! - We know the range of S we want to work on, but at float channel
  ! values. Say 'eps' is a small value (<< 1). From N-0.5+eps to N+0.5-eps
  ! (float value) fall into channel #N (integer value): so this is the
  ! nint() definition!
  ! - Are there rounding problems at N-0.5 and N+0.5? Well, not really:
  ! in one case we add a channel in which R does not contribute, and in
  ! the other case we miss a channel in which R contributes but at most for
  ! an epsilon amount. So, no worry.
  if (is1.lt.is2) then
    ismin = nint(is1)
    ismax = nint(is2)
  else
    ismin = nint(is2)
    ismax = nint(is1)
  endif
  !
  ! - Is this range out of the spectrum limits?
  if (ismin.gt.new%nchan .or. ismax.lt.1) then
    call do_resample_nointersecterror_regul(old,new)
    error = .true.
    return
  endif
  !
  ! - Finally we minmax the values between 1 and new%nchan since R can be
  ! larger than S but we work only on the S channels.
  ismin = max(ismin,1)
  ismax = min(ismax,new%nchan)
  !
end subroutine resample_interpolate_range
!
subroutine resample_interpolate_regul(set,                     &
                                      rdata1,rdataw,rbad,old,  &
                                      sdata1,sdataw,sbad,new,  &
                                      ismin,ismax,error)
  use classcore_interfaces, except_this=>resample_interpolate_regul
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Main entry point for over- or under-sampling of a regularly
  ! spaced spectrum.
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set        !
  real(kind=4),        intent(in)    :: rdata1(:)  ! Input spectrum
  real(kind=4),        intent(in)    :: rdataw(:)  ! Input weights
  real(kind=4),        intent(in)    :: rbad       ! Input bad value
  type(resampling),    intent(in)    :: old        ! Old axis description
  real(kind=4),        intent(out)   :: sdata1(:)  ! Output spectrum
  real(kind=4),        intent(out)   :: sdataw(:)  ! Output weights
  real(kind=4),        intent(in)    :: sbad       ! Output bad value
  type(resampling),    intent(in)    :: new        ! New axis description
  integer(kind=4),     intent(out)   :: ismin      ! Range of channels updated in...
  integer(kind=4),     intent(out)   :: ismax      ! ... the output spectrum.
  logical,             intent(inout) :: error      ! Logical error flag
  !
  if (new%shape.eq.'FBOX') then
    if (abs(new%inc).le.abs(old%inc)) then
      ! Regularly spaced axis, 3 point oversampling
      call resample_interpolate3_over(set,                     &
                                      rdata1,rdataw,rbad,old,  &
                                      sdata1,sdataw,sbad,new,  &
                                      ismin,ismax,error)
      if (error)  return
    else
      ! Regularly spaced axis, 3 point undersampling
      call resample_interpolate3_under(set,                     &
                                       rdata1,rdataw,rbad,old,  &
                                       sdata1,sdataw,sbad,new,  &
                                       ismin,ismax,error)
      if (error)  return
    endif
    !
  else
    if (abs(new%inc).le.abs(old%inc)) then
      ! Regularly spaced axis, 2 point oversampling
      call resample_interpolate2_over(set,                     &
                                      rdata1,rdataw,rbad,old,  &
                                      sdata1,sdataw,sbad,new,  &
                                      ismin,ismax,error)
      if (error)  return
    else
      ! Regularly spaced axis, 2 point undersampling
      call resample_interpolate2_under(set,                     &
                                       rdata1,rdataw,rbad,old,  &
                                       sdata1,sdataw,sbad,new,  &
                                       ismin,ismax,error)
      if (error)  return
    endif
  endif
  !
end subroutine resample_interpolate_regul
!
subroutine resample_interpolate3_under(set,                     &
                                       rdata1,rdataw,rbad,old,  &
                                       sdata1,sdataw,sbad,new,  &
                                       ismin,ismax,error)
  use classcore_interfaces, except_this=>resample_interpolate3_under
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  3 points undersampling routine.
  !  Performs the linear interpolation/integration.
  !  This routine integrates in a square output channel passband (width
  ! sinc) the piecewise linear interpolation of the original data.
  !   As a result the integration is always on 3 points (hence the name)
  ! and the effective resolution is degraded if the input spectrum was
  ! undersampled.
  !
  ! This is what is needed e.g. for square channel responses in the
  ! frequency domain (fbox) i.e. approximation of filter channels of
  ! separation equal to their width.
  ! * There is no restriction on the output range compared to the input
  !   range (i.e. output can be too large, overlap, or be off limits).
  !   For efficiency purpose, only the intersection is updated, and the
  !   channel range which is updated is returned in 2 variables. It is
  !   up to the caller to do something (or not) out of this range.
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set        !
  real(kind=4),        intent(in)    :: rdata1(:)  ! Input spectrum
  real(kind=4),        intent(in)    :: rdataw(:)  ! Input weights
  real(kind=4),        intent(in)    :: rbad       ! Input bad value
  type(resampling),    intent(in)    :: old        ! Old axis description
  real(kind=4),        intent(out)   :: sdata1(:)  ! Output spectrum
  real(kind=4),        intent(out)   :: sdataw(:)  ! Output weights
  real(kind=4),        intent(in)    :: sbad       ! Output bad value
  type(resampling),    intent(in)    :: new        ! New axis description
  integer(kind=4),     intent(out)   :: ismin      ! Range of channels updated in...
  integer(kind=4),     intent(out)   :: ismax      ! ... the output spectrum.
  logical,             intent(inout) :: error      ! Logical error flag
  ! Local
  integer(kind=4) :: is,ir,irmax,irmin
  real(kind=8) :: minpix, maxpix, expand, ymax, ymin,rval0,sval0
  real(kind=4) :: scale,irf,vr,wr,xs
  logical :: contaminate
  !
  call resample_interpolate_range(old,new,.true.,ismin,ismax,error)
  if (error)  return
  !
  contaminate = set%bad.eq.'O'  ! Bad channels contaminate output or not?
  !
  ! Initialize the output (resampled) observation
  sdata1(:) = 0.  ! Avoid NaN
  sdataw(:) = 0.  ! No data available yet in the spectrum
  !
  ! The algorithm below works with the 0-th channels as references (simpler
  ! equations and faster computations). Compute the X value at these channels:
  rval0 = (0.d0-old%ref)*old%inc+old%val
  sval0 = (0.d0-new%ref)*new%inc+new%val
  !
  expand = abs(new%inc/old%inc)  ! Undersampling: expand>=1
  scale = 1.0d0/(2.0d0*expand)
  !
  do is = ismin,ismax
    ! Compute interval
    xs = new%inc*is+sval0  ! X position
    irf = (xs-rval0)/old%inc  ! Channel position in input spectrum
    !
    maxpix = irf + 0.5d0*expand
    minpix = irf - 0.5d0*expand
    irmin = int(minpix+1.0d0)
    irmax = int(maxpix)
    ! Undersampling: irmin<=irmax
    vr = 0.
    wr = 0.
    !
    ! Left part
    ymin = 0.
    if (rdata1(irmin-1).eq.rbad) then  ! Bad channels
      if (contaminate) then
        sdata1(is) = sbad
        sdataw(is) = 0.0
        cycle  ! Next output channel
      endif
    else
      ymin = rdata1(irmin-1)*(irmin-minpix)
    endif
    if (rdata1(irmin).eq.rbad) then
      if (contaminate) then
        sdata1(is) = sbad
        sdataw(is) = 0.0
        cycle  ! Next output channel
      endif
      if (ymin.ne.0.) then
        vr = (irmin-minpix) * ymin
        wr = (irmin-minpix)
      endif
    else
      ymin = ymin + rdata1(irmin)*(minpix-irmin+1)
      vr = (irmin-minpix) * (ymin+rdata1(irmin))
      wr = (irmin-minpix)
    endif
    !
    ! Central part (if any)
    do ir=irmin, irmax-1  ! Maybe 0 loop
      if (rdata1(ir).eq.rbad) then
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          goto 30  ! Next output channel
        endif
      else
        vr = vr + rdataw(ir)*rdata1(ir)
        wr = wr + rdataw(ir)
      endif
      if (rdata1(ir+1).eq.rbad) then
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          goto 30  ! Next output channel
        endif
      else
        vr = vr + rdataw(ir+1)*rdata1(ir+1)
        wr = wr + rdataw(ir+1)
      endif
    enddo
    !
    ! Right part
    ymax = 0.
    if (rdata1(irmax+1).eq.rbad) then
      if (contaminate) then
        sdata1(is) = sbad
        sdataw(is) = 0.0
        cycle  ! Next output channel
      endif
    else
      ymax = ymax + rdata1(irmax+1)*(maxpix-irmax)
    endif
    if (rdata1(irmax).eq.rbad) then
      if (contaminate) then
        sdata1(is) = sbad
        sdataw(is) = 0.0
        cycle  ! Next output channel
      endif
      if (ymax.ne.0.) then
        vr = vr + (maxpix-irmax)*ymax
        wr = wr + (maxpix-irmax)
      endif
    else
      ymax = ymax + rdata1(irmax)*(irmax+1-maxpix)
      vr = vr + (maxpix-irmax)*(ymax+rdata1(irmax))
      wr = wr + (maxpix-irmax)
    endif
    !
    if (wr.ne.0.) then
      sdata1(is) = vr/wr
      sdataw(is) = wr
    else
      sdata1(is) = 0.
    endif
    !
30  continue
  enddo
  !
end subroutine resample_interpolate3_under
!
subroutine resample_interpolate3_over(set,                     &
                                      rdata1,rdataw,rbad,old,  &
                                      sdata1,sdataw,sbad,new,  &
                                      ismin,ismax,error)
  use classcore_interfaces, except_this=>resample_interpolate3_over
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  3 points oversampling routine.
  !  Performs the linear interpolation/integration.
  !  This routine integrates in a square output channel passband (width
  ! sinc) the piecewise linear interpolation of the original data.
  !   As a result the integration is always on 3 points (hence the name)
  ! and the effective resolution is degraded if the input spectrum was
  ! undersampled.
  !
  ! This is what is needed e.g. for square channel responses in the
  ! frequency domain (fbox) i.e. approximation of filter channels of
  ! separation equal to their width.
  ! * There is no restriction on the output range compared to the input
  !   range (i.e. output can be too large, overlap, or be off limits).
  !   For efficiency purpose, only the intersection is updated, and the
  !   channel range which is updated is returned in 2 variables. It is
  !   up to the caller to do something (or not) out of this range.
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set        !
  real(kind=4),        intent(in)    :: rdata1(:)  ! Input spectrum
  real(kind=4),        intent(in)    :: rdataw(:)  ! Input weights
  real(kind=4),        intent(in)    :: rbad       ! Input bad value
  type(resampling),    intent(in)    :: old        ! Old axis description
  real(kind=4),        intent(out)   :: sdata1(:)  ! Output spectrum
  real(kind=4),        intent(out)   :: sdataw(:)  ! Output weights
  real(kind=4),        intent(in)    :: sbad       ! Output bad value
  type(resampling),    intent(in)    :: new        ! New axis description
  integer(kind=4),     intent(out)   :: ismin      ! Range of channels updated in...
  integer(kind=4),     intent(out)   :: ismax      ! ... the output spectrum.
  logical,             intent(inout) :: error      ! Logical error flag
  ! Local
  integer(kind=4) :: is,irmax,irmin
  real(kind=8) :: minpix, maxpix, expand, ymax, ymin,rval0,sval0
  real(kind=4) :: scale,irf,vr,wr,xs
  logical :: contaminate
  !
  call resample_interpolate_range(old,new,.true.,ismin,ismax,error)
  if (error)  return
  !
  contaminate = set%bad.eq.'O'  ! Bad channels contaminate output or not?
  !
  ! Initialize the output (resampled) observation
  sdata1(:) = 0.  ! Avoid NaN
  sdataw(:) = 0.  ! No data available yet in the spectrum
  !
  ! The algorithm below works with the 0-th channels as references (simpler
  ! equations and faster computations). Compute the X value at these channels:
  rval0 = (0.d0-old%ref)*old%inc+old%val
  sval0 = (0.d0-new%ref)*new%inc+new%val
  !
  expand = abs(new%inc/old%inc)  ! Oversampling: expand<=1
  scale = 1.0d0/(2.0d0*expand)
  !
  do is = ismin,ismax
    ! Compute interval
    xs = new%inc*is+sval0  ! X position
    irf = (xs-rval0)/old%inc  ! Channel position in input spectrum
    !
    maxpix = irf + 0.5d0*expand
    minpix = irf - 0.5d0*expand
    irmin = int(minpix+1.0d0)
    irmax = int(maxpix)
    ! Oversampling: irmin>=irmax
    vr = 0.
    wr = 0.
    !
    if (irmax.eq.irmin) then
      ! New channel overlap the center of an old one
      ! Left part
      ymin = 0.
      if (rdata1(irmin-1).eq.rbad) then  ! Bad channels
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
      else
        ymin = rdata1(irmin-1)*(irmin-minpix)
      endif
      if (rdata1(irmin).eq.rbad) then
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
        if (ymin.ne.0.) then
          vr = (irmin-minpix) * ymin
          wr = (irmin-minpix)
        endif
      else
        ymin = ymin + rdata1(irmin)*(minpix-irmin+1)
        vr = (irmin-minpix) * (ymin+rdata1(irmin))
        wr = (irmin-minpix)
      endif
      !
      ! Right part
      ymax = 0.
      if (rdata1(irmax+1).eq.rbad) then
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
      else
        ymax = ymax + rdata1(irmax+1)*(maxpix-irmax)
      endif
      if (rdata1(irmax).eq.rbad) then
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
        if (ymax.ne.0.) then
          vr = vr + (maxpix-irmax)*ymax
          wr = wr + (maxpix-irmax)
        endif
      else
        ymax = ymax + rdata1(irmax)*(irmax+1-maxpix)
        vr = vr + (maxpix-irmax)*(ymax+rdata1(irmax))
        wr = wr + (maxpix-irmax)
      endif
      !
    else
      ! New channel does not overlap the center of an old one:
      !   2 points interpolation between the 2 channels
      if (rdata1(irmin-1).eq.rbad) then  ! Bad channels
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
      else
        vr = rdata1(irmin-1)*(irmin-irf)
        wr = irmin-irf
      endif
      !
      if (rdata1(irmin).eq.rbad) then
        ! Bad channels
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
      else
        vr = vr + rdata1(irmin)*(irf-irmin+1)
        wr = wr + irf-irmin+1.
      endif
    endif
    !
    if (wr.ne.0.) then
      sdata1(is) = vr/wr
      sdataw(is) = wr
    else
      sdata1(is) = 0.
    endif
    !
  enddo
  !
end subroutine resample_interpolate3_over
!
subroutine resample_interpolate2_under(set,                     &
                                       rdata1,rdataw,rbad,old,  &
                                       sdata1,sdataw,sbad,new,  &
                                       ismin,ismax,error)
  use gbl_constant
  use classcore_interfaces, except_this=>resample_interpolate2_under
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !   2 points undersampling routine (i.e. reso(out) >= reso(in)).
  ! * This resampling engine is generic enough to work without
  !   knowing the X unit. This means in particular that the input
  !   resolutions must be in same units and same referential, e.g.
  !   if unit is frequency the Doppler must be identical OR the
  !   resolution must be provided in the LSR frame. Also if unit is
  !   offset frequency, take care that the 2 axes must have the same
  !   rest frequencies.
  ! * The engine assumes a linear representation of the axis, i.e. it
  !   can be described with:
  !     X(ichan) = (ichan-Xref)*Xinc+Xval
  ! * No global variables here (i.e. multithreading is possible)
  ! * Input weights must have been computed *before* resampling, since
  !   resampling also affect the output weights.
  ! * Blanked channels propagates or not in output sum according to SET
  !   BAD.
  ! * Output channels with 0 weights are *not* yet blanked: it is up to
  !   the caller to blank them or not depending on what it does with
  !   such channels. This is because 'no data' is not the same as 'bad
  !   data'.
  ! * There is no restriction on the output range compared to the input
  !   range (i.e. output can be too large, overlap, or be off limits).
  !   For efficiency purpose, only the intersection is updated, and the
  !   channel range which is updated is returned in 2 variables. It is
  !   up to the caller to do something (or not) out of this range.
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set        !
  real(kind=4),        intent(in)    :: rdata1(:)  ! Input values
  real(kind=4),        intent(in)    :: rdataw(:)  ! Input weights
  real(kind=4),        intent(in)    :: rbad       ! Input bad value
  type(resampling),    intent(in)    :: old        ! Old axis description
  real(kind=4),        intent(out)   :: sdata1(:)  ! Output values
  real(kind=4),        intent(out)   :: sdataw(:)  ! Output weights
  real(kind=4),        intent(in)    :: sbad       ! Output bad value
  type(resampling),    intent(in)    :: new        ! New axis description
  integer(kind=4),     intent(out)   :: ismin      ! Range of channels updated in...
  integer(kind=4),     intent(out)   :: ismax      ! ... the output spectrum.
  logical,             intent(inout) :: error      ! Logical error flag
  ! Local
  logical :: contaminate,equal
  real(kind=4) :: dx,f,fs,nch,rdxmax,rdxmin
  real(kind=4) :: wr1,wr2,wr3,wr,vr
  real(kind=4) :: xs,xr
  real(kind=8) :: rval0,sval0
  integer(kind=4) :: is,ir,irmin,irmax
  !
  call resample_interpolate_range(old,new,.true.,ismin,ismax,error)
  if (error)  return
  !
  contaminate = set%bad.eq.'O'  ! Bad channels contaminate output or not?
  equal = set%weigh.eq.'E'      ! Weight is EQUAL or not?
  !
  ! Initialize the output (resampled) observation
  sdata1(:) = 0.  ! Avoid NaN
  sdataw(:) = 0.  ! No data available yet in the spectrum
  !
  ! The algorithm below works with the 0-th channels as references (simpler
  ! equations and faster computations). Compute the X value at these channels:
  rval0 = (0.d0-old%ref)*old%inc+old%val
  sval0 = (0.d0-new%ref)*new%inc+new%val
  !
  ! A word on the distances in this resampling:
  !
  ! S:      output spectrum
  ! R:      one of the input spectra
  !
  ! is:     number of the current output channel of S considered
  ! ir:     number of the current input channel of R considered
  !
  ! new%inc:   S resolution / channel width
  ! old%inc:   R resolution / channel width. In sumout_param we ensure
  !         that abs(new%inc) >= abs(old%inc)
  !
  ! sval0:  abscissa value in S at reference channel #0
  ! rval0:  abscissa value in R at reference channel #0
  !
  ! xs:     channel center considered in S (output sum)
  ! xr:     channel center considered in R (one of the input spectra)
  !
  ! irmin:  is the minimum channel number in R which contributes at
  !         least partially to the current output channel.
  ! irmax:  is the maximum channel number in R which contributes at
  !         least partially to the current output channel
  !
  !         The left side of channel S(is) is at abscissa
  !           x = xs-abs(new%inc)/2
  !         We search the channel R(irmin) which contains x, i.e.
  !         where the R contribution starts. It verifies:
  !           x = old%inc*(irmin-0)+rval0
  !         Thus:
  !           irmin =     (xs-abs(new%inc)/2-rval0)/old%inc            (float value)
  !           irmin = int((xs-abs(new%inc)/2-rval0)/old%inc + 1/2)     (int value)
  !           irmin = int((xs-abs(new%inc)/2-rval0+old%inc/2)/old%inc) (int value)
  !         Same for the right side of the channel S(is)
  !           irmax =     (xs+abs(new%inc)/2-rval0)/old%inc            (float value)
  !           irmax = int((xs+abs(new%inc)/2-rval0)/old%inc + 1/2)     (int value)
  !           irmax = int((xs+abs(new%inc)/2-rval0+old%inc/2)/old%inc) (int value)
  !
  ! dx:     is the distance between xs and xr
  ! rdxmax: is the highest dx such as the channel in S overlaps
  !         fully or *partially* the channel in R (rdxmax>0)
  ! rdxmin: is the highest dx such as the channel in S overlaps
  !         *fully* (only) the channel in R (rdxmin>0)
  !
  ! wr1:    a constant value in the irmin/irmax formulae above i.e.
  !           wr1 = -abs(new%inc)/2-rval0+old%inc/2
  ! wr2:      wr2 =  abs(new%inc)/2-rval0+old%inc/2
  ! wr3:    see comment in loop
  !
  ! Constants for 1st spectrum (prefix 'r')
  rdxmax = (abs(new%inc)+abs(old%inc))*0.5
  rdxmin = (abs(new%inc)-abs(old%inc))*0.5
  wr1 = -sign(1.d0,old%inc)*rdxmin-rval0  ! Take care of the resolution sign
  wr2 =  sign(1.d0,old%inc)*rdxmax-rval0  ! Idem
  wr3 = 1./(rdxmax-rdxmin)             ! = 1/abs(old%inc)
  !
  do is = ismin,ismax  ! Loop on output channels
     xs = new%inc*is+sval0
     !
     ! Local resample for input observation
     irmin = max(1        ,int((xs+wr1)/old%inc))
     irmax = min(old%nchan,int((xs+wr2)/old%inc))
     nch = 0.  ! Number of channels which contribute
     fs = 0.   ! Fraction sum
     wr = 0.   ! Weight of (possibly resampled) channel from R
     vr = 0.   ! Value of (possibly resampled) channel from R
     do ir = irmin,irmax
        xr = old%inc*ir + rval0
        dx = abs(xr-xs)
        if (dx.lt.rdxmax) then
           ! This channel in R is at least partially overlapped by the
           ! one in S
           if (rdata1(ir).eq.rbad) then
              ! Bad channels
              if (contaminate) then
                 sdata1(is) = sbad
                 sdataw(is) = 0.0
                 goto 30  ! Next output channel
              endif
              f = 0.
           elseif (dx.le.rdxmin) then
              ! This channel in R is fully overlapped by the one in S. It
              ! has a full contribution:
              f = 1.
           else
              ! This channel in R is partially overlapped by the one in S
              ! It contributes, but less. wr3 normalizes dx-rdxmin such as
              ! 0<f<1 . f is proportional to the overlapping.
              f = 1.-(dx-rdxmin)*wr3
           endif
           nch = nch+            f
           fs  = fs +          f*f*rdataw(ir)
           wr  = wr +            f*rdataw(ir)  ! beta
           vr  = vr + rdata1(ir)*f*rdataw(ir)
        endif
     enddo
     !
     if (wr.ne.0.0) then
        vr = vr/wr       ! Value of resampled channel
        if (equal) then
          wr = wr/nch    ! Normalize the weight of the resampled channel
        else
          wr = wr*wr/fs  ! Weight of resampled channel
        endif
     endif
     !
     ! Store new value
     sdata1(is) = vr
     sdataw(is) = wr  ! Possibly 0.0
     !
30   continue
  enddo
  !
end subroutine resample_interpolate2_under
!
subroutine resample_interpolate2_over(set,                     &
                                      rdata1,rdataw,rbad,old,  &
                                      sdata1,sdataw,sbad,new,  &
                                      ismin,ismax,error)
  use gbl_constant
  use classcore_interfaces, except_this=>resample_interpolate2_over
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !   2 points oversampling routine (i.e. reso(out) >= reso(in)).
  ! * This resampling engine is generic enough to work without
  !   knowing the X unit. This means in particular that the input
  !   resolutions must be in same units and same referential, e.g.
  !   if unit is frequency the Doppler must be identical OR the
  !   resolution must be provided in the LSR frame. Also if unit is
  !   offset frequency, take care that the 2 axes must have the same
  !   rest frequencies.
  ! * The engine assumes a linear representation of the axis, i.e. it
  !   can be described with:
  !     X(ichan) = (ichan-Xref)*Xinc+Xval
  ! * No global variables here (i.e. multithreading is possible)
  ! * Input weights must have been computed *before* resampling, since
  !   resampling also affect the output weights.
  ! * Blanked channels propagates or not in output sum according to SET
  !   BAD.
  ! * Output channels with 0 weights are *not* yet blanked: it is up to
  !   the caller to blank them or not depending on what it does with
  !   such channels. This is because 'no data' is not the same as 'bad
  !   data'.
  ! * There is no restriction on the output range compared to the input
  !   range (i.e. output can be too large, overlap, or be off limits).
  !   For efficiency purpose, only the intersection is updated, and the
  !   channel range which is updated is returned in 2 variables. It is
  !   up to the caller to do something (or not) out of this range.
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set        !
  real(kind=4),        intent(in)    :: rdata1(:)  ! Input values
  real(kind=4),        intent(in)    :: rdataw(:)  ! Input weights
  real(kind=4),        intent(in)    :: rbad       ! Input bad value
  type(resampling),    intent(in)    :: old        ! Old axis description
  real(kind=4),        intent(out)   :: sdata1(:)  ! Output values
  real(kind=4),        intent(out)   :: sdataw(:)  ! Output weights
  real(kind=4),        intent(in)    :: sbad       ! Input bad value
  type(resampling),    intent(in)    :: new        ! New axis description
  integer(kind=4),     intent(out)   :: ismin      ! Range of channels updated in...
  integer(kind=4),     intent(out)   :: ismax      ! ... the output spectrum.
  logical,             intent(inout) :: error      ! Logical error flag
  ! Local
  logical :: contaminate,equal
  real(kind=4) :: f,fs,nch,wr,vr
  real(kind=4) :: xs,irf
  real(kind=8) :: rval0,sval0
  integer(kind=4) :: is,ir
  !
  call resample_interpolate_range(old,new,.true.,ismin,ismax,error)
  if (error)  return
  !
  contaminate = set%bad.eq.'O'  ! Bad channels contaminate output or not?
  equal = set%weigh.eq.'E'      ! Weight is EQUAL or not?
  !
  ! Initialize the output (resampled) observation
  sdata1(:) = 0.  ! Avoid NaN
  sdataw(:) = 0.  ! No data available yet in the spectrum
  !
  ! The algorithm below works with the 0-th channels as references (simpler
  ! equations and faster computations). Compute the X value at these channels:
  rval0 = (0.d0-old%ref)*old%inc+old%val
  sval0 = (0.d0-new%ref)*new%inc+new%val
  !
  ! A word on the distances in this resampling:
  !
  ! S:      output spectrum
  ! R:      one of the input spectra
  !
  ! is:     number of the current output channel of S considered
  ! irf:    number of the current input channel of R considered (float)
  ! ir:     number of the current input channel of R considered (int)
  !
  ! sval0:  abscissa value in S at reference channel #0
  ! rval0:  abscissa value in R at reference channel #0
  !
  do is = ismin,ismax  ! Loop on output channels
    xs = new%inc*is+sval0  ! X position
    irf = (xs-rval0)/old%inc  ! Channel position in input spectrum
    !
    nch = 0.
    fs = 0.
    vr = 0.
    wr = 0.
    !
    ir = int(irf)  ! Left channel contribution
    if (ir.ge.1 .and. ir.le.old%nchan) then
      if (rdata1(ir).eq.rbad) then
        ! Bad channels
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
        f = 0.
      else
        f = real(ir+1)-irf
      endif
      nch = nch+            f
      fs  = fs +          f*f*rdataw(ir)
      wr  = wr +            f*rdataw(ir)  ! beta
      vr  = vr + rdata1(ir)*f*rdataw(ir)
      !
    endif
    !
    ir = ir+1  ! Right channel contribution
    if (ir.ge.1 .and. ir.le.old%nchan) then
      if (rdata1(ir).eq.rbad) then
        ! Bad channels
        if (contaminate) then
          sdata1(is) = sbad
          sdataw(is) = 0.0
          cycle  ! Next output channel
        endif
        f = 0.
      else
        f = irf-real(ir-1)
      endif
      nch = nch+            f
      fs  = fs +          f*f*rdataw(ir)
      wr  = wr +            f*rdataw(ir)  ! beta
      vr  = vr + rdata1(ir)*f*rdataw(ir)
      !
    endif
    !
    if (wr.ne.0.0) then
      vr = vr/wr       ! Value of resampled channel
      if (equal) then
        wr = wr/nch    ! Normalize the weight of the resampled channel
      else
        wr = wr*wr/fs  ! Weight of resampled channel
      endif
    endif
    !
    ! Store new value
    sdata1(is) = vr
    sdataw(is) = wr  ! Possibly 0.0
    !
  enddo
  !
end subroutine resample_interpolate2_over
!
subroutine resample_interpolate_irreg(set,                        &
                                      rdatax,rdata1,rbad,rnchan,  &
                                      sdata1,sdataw,sbad,new,     &
                                      error)
  use class_parameter
  use classcore_interfaces, except_this=>resample_interpolate_irreg
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Performs the linear interpolation/integration to resample on a
  ! Regular Grid. Edge handling should be improved one day
  ! * There is no restriction on the output range compared to the input
  !   range (i.e. output can be too large, overlap, or be off limits).
  !   For efficiency purpose, only the intersection is updated, and the
  !   channel range which is updated is returned in 2 variables. It is
  !   up to the caller to do something (or not) out of this range.
  !---------------------------------------------------------------------
  type(class_setup_t),   intent(in)    :: set        !
  real(kind=xdata_kind), intent(in)    :: rdatax(:)  ! Input abscissa
  real(kind=4),          intent(in)    :: rdata1(:)  ! Input spectrum
  real(kind=4),          intent(in)    :: rbad       ! Input bad value
  integer(kind=4),       intent(in)    :: rnchan     ! Input spectrum size
  real(kind=4),          intent(out)   :: sdata1(:)  ! Output spectrum
  real(kind=4),          intent(out)   :: sdataw(:)  ! Output weight
  real(kind=4),          intent(in)    :: sbad       ! Output bad value
  type(resampling),      intent(in)    :: new        ! Output axis
  logical,               intent(inout) :: error      ! Logical error flag
  ! Local
  integer(kind=4) :: is,ir,irlast,irstart,irmin,irmax,rstep
  integer(kind=4) :: ismin,ismax,isfirst,islast,sstep
  real(kind=8) :: xval1,xval2,xvalt,vdata,weight,is1,is2
  real(kind=8) :: xleft,xright,xover1,xover2,overlap
  logical :: contaminate
  !
  contaminate = set%bad.eq.'O'  ! Bad channels contaminate output or not?
  !
  ! Initialize the output (resampled) observation
  sdata1(:) = 0.  ! Avoid NaN
  sdataw(:) = 0.  ! No data available yet in the spectrum
  !
  ! Handle the various orderings
  if ((rdatax(2)-rdatax(1)).gt.0.) then
    irmin = 1
    irmax = rnchan
    rstep = 1
  else
    irmin = rnchan
    irmax = 1
    rstep = -1
  endif
  !
  ! X range is rdatax(irmin) to rdatax(irmax)  (sorted)
  !
  ! Convert to output channel range. The interpolation domain is limited
  ! to the first and last input abscissae: there is no extrapolation.
  is1 = (rdatax(irmin)-new%val)/new%inc+new%ref  ! Float value
  is2 = (rdatax(irmax)-new%val)/new%inc+new%ref  ! Float value
  ! - We know the range of S we want to work on, but at float channel
  ! values. Say 'eps' is a small value (<< 1). From N-0.5+eps to N+0.5-eps
  ! (float value) fall into channel #N (integer value): so this is the
  ! nint() definition!
  if (is1.lt.is2) then
    ismin = nint(is1)
    ismax = nint(is2)
    isfirst = max(ismin,1)
    islast = min(ismax,new%nchan)
    sstep = 1
  else
    ismin = nint(is2)
    ismax = nint(is1)
    isfirst = min(ismax,new%nchan)
    islast = max(ismin,1)
    sstep = -1
  endif
  ! Now isfirst to islast is the range of output channels overlapping
  ! the input interpolation domain, to be iterated for increasing X.
  !
  ! Is this range out of the spectrum limits?
  if (ismin.gt.new%nchan .or. ismax.lt.1) then
    call do_resample_nointersecterror_irreg(rdatax,rnchan,new)
    error = .true.
    return
  endif
  !
  ! Loop on output channels. This code works if traversed by increasing X
  ! values. This might mean traversing the input and/or the output axes
  ! in reverse.
  irlast = irmax-rstep
  irstart = irmin
  output_channel: do is = isfirst, islast, sstep
    ! Compute output interval in increasing X order
    xval1 = (is-0.5d0-new%ref)*new%inc+new%val
    xval2 = (is+0.5d0-new%ref)*new%inc+new%val
    if (xval1.gt.xval2) then
      xvalt = xval1
      xval1 = xval2
      xval2 = xvalt
    endif
    !
    ! Accumulate contributions from input segments. The signal is linearly
    ! interpolated between neighbouring input samples, and integrated over
    ! the output channel passband.
    vdata  = 0.
    weight = 0.
    !
    ! Move to the first input segment which can overlap the output cell.
    do
      call input_segment_edges(irstart,xleft,xright)
      if (xright.gt.xval1 .or. irstart.eq.irlast)  exit
      irstart = irstart+rstep
    enddo
    !
    ir = irstart
    do
      call input_segment_edges(ir,xleft,xright)
      if (xleft.ge.xval2)  exit
      xover1 = max(xleft,xval1)
      xover2 = min(xright,xval2)
      overlap = xover2-xover1
      if (overlap.gt.0.d0) then
        if (rdata1(ir).eq.rbad .or. rdata1(ir+rstep).eq.rbad) then
          ! A bad interpolation segment
          if (contaminate) then
            sdata1(is) = sbad
            sdataw(is) = 0.
            cycle output_channel
          endif
        else
          vdata  = vdata + input_segment_integral(ir,xover1,xover2)
          weight = weight + overlap
        endif
      endif
      if (xright.ge.xval2 .or. ir.eq.irlast)  exit
      ir = ir+rstep
    enddo
    !
    if (weight.ne.0.) then
      sdata1(is) = vdata/weight
      sdataw(is) = weight
    else
      sdata1(is) = 0.
    endif
    !
  enddo output_channel
  !
contains
  !
  subroutine input_segment_edges(ichan,cleft,cright)
    !-------------------------------------------------------------------
    ! Return an input interpolation segment in increasing X order.
    !-------------------------------------------------------------------
    integer(kind=4), intent(in)  :: ichan
    real(kind=8),    intent(out) :: cleft,cright
    !
    cleft  = rdatax(ichan)
    cright = rdatax(ichan+rstep)
    !
  end subroutine input_segment_edges
  !
  function input_segment_integral(ichan,cleft,cright) result(integ)
    !-------------------------------------------------------------------
    ! Integrate the linear interpolation segment over [cleft,cright].
    !-------------------------------------------------------------------
    integer(kind=4), intent(in) :: ichan
    real(kind=8),    intent(in) :: cleft,cright
    real(kind=8) :: integ
    real(kind=8) :: dx,slope,yleft,yright
    !
    dx = rdatax(ichan+rstep)-rdatax(ichan)
    slope = (rdata1(ichan+rstep)-rdata1(ichan))/dx
    yleft  = rdata1(ichan) + slope*(cleft -rdatax(ichan))
    yright = rdata1(ichan) + slope*(cright-rdatax(ichan))
    integ  = 0.5d0*(yleft+yright)*(cright-cleft)
    !
  end function input_segment_integral
  !
end subroutine resample_interpolate_irreg
!
subroutine pfactor(n,p)
  !---------------------------------------------------------------------
  ! @ private
  ! Returns in p the largest prime factor in n
  !---------------------------------------------------------------------
  integer(kind=4), intent(in)  :: n
  integer(kind=4), intent(out) :: p
  ! Local
  integer :: m,q,r
  logical :: prime
  !
  m = n
  p = 1
  do while (m .gt. p)
     if (prime(m,q)) then
        p = max(p,m)
        return
     else
        do while (.not.prime(q,r))
           q = q/r
        enddo
        p = max(p,q)
        m = m/q
     endif
  enddo
end subroutine pfactor
!
function prime(n,p)
  !---------------------------------------------------------------------
  ! @ private
  ! Find if n is prime, returns the smallest integer divisor p.
  !---------------------------------------------------------------------
  logical :: prime  ! Function value on return
  integer(kind=4), intent(in)  :: n  !
  integer(kind=4), intent(out) :: p  !
  ! Local
  integer(kind=4) :: i,nn
  !
  prime = .false.
  p=2
  i=n/2
  if (i.gt.1 .and. i*2.eq.n) return
  p = 3
  nn = sqrt(float(n))
  do while (p.le.nn)
     i = n/p
     if (p*i.eq.n) return
     p = p+2
  enddo
  prime = .true.
end function prime
