!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
subroutine mrtcal_bookkeeping_init_time(bufsize,subs,time,error)
  use gbl_message
  use ram_sizes
  use imbfits_types
  use mrtcal_bookkeeping_types
  use mrtcal_interfaces, except_this => mrtcal_bookkeeping_init_time
  use mrtcal_messaging
  !---------------------------------------------------------------------
  ! @ private
  ! Initialize a time loop
  !---------------------------------------------------------------------
  integer(kind=8),         intent(in)    :: bufsize
  type(imbfits_subscan_t), intent(in)    :: subs
  type(book_t),            intent(inout) :: time
  logical,                 intent(inout) :: error
  ! Local
  integer(kind=4) :: npix,nchan
  character(len=message_length) :: mess
  character(len=*), parameter :: rname='BOOKKEEPING>INIT>TIME'
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Buffer size
  if (bufsize.le.0) then
     write(mess,'(a,f0.1,a)') 'Buffer size is <=0 (',float(bufsize)/MB,' MB)'
     call mrtcal_message(seve%e,rname,mess)
     error = .true.
     return
  endif
  !
  ! time%tot% is usually set by get_time_range_for_backend()
  time%tot%i = 0  ! Unused for time%tot% (iteration is done with time%cur%)
  !
  ! Element size
  npix = 1 ! ZZZ TBD: set correctly
  nchan = subs%backdata%head%channels%val
  if (nchan.gt.0 .and. time%tot%n.gt.0) then
    time%block%eltsize = npix*nchan*bytes_per_real
    ! Maximum number of elements (dumps) which can fit per block
    time%block%nelt = bufsize/time%block%eltsize
    ! time%block%nelt is left possibly much larger than needed. Do not truncate
    ! it! In particular, time%tot%n (number of useful elements in compressed
    ! columns) may not reflect the actual number of rows to read in the DATA
    ! (uncompressed) column.
  else
    time%block%eltsize = 0
    time%block%nelt = 0
  endif
  !
  time%block%bufsize = time%block%nelt*time%block%eltsize
  !
  ! Initialize memory current
  time%cur%vfirst = 0.d0
  time%cur%vlast = 0.d0
  time%cur%first = 0
  time%cur%last = 0
  time%cur%n = 0
  time%cur%i = 0
  !
  ! User feedback
  write(mess,'(a,i0,a,i0,a,i0,a)') 'Will iterate by blocks of max size ',  &
    time%block%bufsize/kB,' kB (',time%block%nelt,' x ',time%block%eltsize/kB,' kB)'
  call mrtcal_message(mseve%calib%book,rname,mess)
  !
end subroutine mrtcal_bookkeeping_init_time
!
subroutine mrtcal_bookkeeping_iterate(time,subs,first,last,needupdate,error)
  use imbfits_types
  use mrtcal_interfaces, except_this => mrtcal_bookkeeping_iterate
  use mrtcal_bookkeeping_types
  use mrtcal_messaging
  !---------------------------------------------------------------------
  ! @ private
  !  Fill the time%cur% structure suited for reading at least dumps
  ! 'first' to 'last'. They are the first and last dumps the caller
  ! wants to be present AT LEAST in the buffer. 'first' and 'last' are
  ! absolute positions in the compressed backdata columns.
  !---------------------------------------------------------------------
  type(book_t),            intent(inout) :: time
  type(imbfits_subscan_t), intent(in)    :: subs
  integer(kind=4),         intent(in)    :: first
  integer(kind=4),         intent(in)    :: last
  logical,                 intent(out)   :: needupdate
  logical,                 intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='BOOKKEEPING>ITERATE'
  character(len=message_length) :: mess
  integer(kind=size_length) :: itime,dfirst,dlast
  !
  call mrtcal_message(seve%t,rname,'Welcome')
  !
  ! Sanity check: valid inputs?
  if (first.lt.time%tot%first .or. last.gt.time%tot%last .or. last.lt.first) then
    write(mess,'(4(A,I0))') 'Invalid first and/or last dumps to read: ',  &
      first,'-',last,' not in range ',time%tot%first,'-',time%tot%last
    call mrtcal_message(seve%e,rname,mess)
    error = .true.
    return
  endif
  !
  ! Does the current buffer already contain the desired range?
  needupdate = first.lt.time%cur%first .or. last.gt.time%cur%last
  if (.not.needupdate) then
    ! Nothing to do
    return
  endif
  !
  ! Must load a new piece of the buffer. Convert absolute positions in
  ! compressed columns to absolute position in non-compressed DATA column
  dfirst = subs%backdata%table%backpoin%val(first)
  dlast = subs%backdata%table%backpoin%val(last)
  !
  ! Ensure the desired range will fit in memory
  if (dlast-dfirst+1.gt.time%block%nelt) then
    call mrtcal_message(seve%e,rname,'Desired range does not fit in buffer. Increase buffer size.')
    error = .true.
    return
  endif
  !
  ! Design the new DATA block (described by time%cur%). Note that by construction,
  ! the first and last elements in the buffer are always valid time dumps. In other
  ! words we do not ask CFITSIO to read useless dumps at the block boundaries (there
  ! still can be bad dumps within the block)
  !
! dfirst = unchanged, match the 'first' to read
  time%cur%first = first  ! By construction
  !
  dlast = dfirst+time%block%nelt-1
  if (dlast.ge.subs%backdata%table%backpoin%val(time%tot%last)) then
    ! Reached the end of the total number of dumps
    time%cur%last = time%tot%last
  else
    ! Search the compressed 'last' corresponding to the uncompressed 'dlast'
    time%cur%last = 0
    do itime=dlast,dfirst,-1
      if (subs%backdata%table%forepoin%val(itime).ne.0) then
        time%cur%last = subs%backdata%table%forepoin%val(itime)
        exit
      endif
    enddo
  endif
  !
  time%cur%n = time%cur%last-time%cur%first+1
  time%cur%i = 1
  time%cur%vfirst = subs%backdata%table%mjd%val(time%cur%first)
  time%cur%vlast = subs%backdata%table%mjd%val(time%cur%last)
  !
  ! User feedback
  write(mess,'(2(a,i0))') &
       ' Processing block from element ',time%cur%first,' to ',time%cur%last
  call mrtcal_message(mseve%calib%book,rname,mess)
  !
end subroutine mrtcal_bookkeeping_iterate
!
subroutine mrtcal_bookkeeping_all(subs,time,error)
  use imbfits_types
  use mrtcal_interfaces, except_this=>mrtcal_bookkeeping_all
  use mrtcal_bookkeeping_types
  !---------------------------------------------------------------------
  ! @ public (for mrtholo)
  !  Fill the time%cur% structure suited for reading ALL the block
  ! of data at once.
  !  This is suited for applications which do not want to iterate the
  ! DATA block, i.e. reading it all at once. Use with caution, memory
  ! consumption may be large.
  !  This must come after a 'get_time_range_*' call (which fills the
  ! time%tot).
  !---------------------------------------------------------------------
  type(imbfits_subscan_t), intent(in)    :: subs
  type(book_t),            intent(inout) :: time
  logical,                 intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='BOOKKEEPING>ALL'
  !
  ! Compute time%data%tot from time%tot. NB: use dummy value for buffer
  ! size, since we assume we don't have a limited one.
  call mrtcal_bookkeeping_init_time(1_8,subs,time,error)
  if (error)  return
  !
  ! Set the current blocks, i.e. everything selected here.
  time%cur = time%tot
  !
end subroutine mrtcal_bookkeeping_all
!
subroutine mrtcal_bookkeeping_compr2uncompr(compr,backpoin,uncompr,error)
  use mrtcal_bookkeeping_types
  !---------------------------------------------------------------------
  ! @ private
  !   Convert a "compressed" range_t (i.e. providing indices in the
  ! compressed columns to an "uncompressed" range_t.
  !   "compressed" columns are present in the BackendData e.g. if SET
  ! CALIB BAD is NO
  !---------------------------------------------------------------------
  type(range_t),   intent(in)    :: compr        !
  integer(kind=4), intent(in)    :: backpoin(:)  ! Back-pointer array
  type(range_t),   intent(out)   :: uncompr      !
  logical,         intent(inout) :: error        !
  !
  uncompr%first  = backpoin(compr%first)
  uncompr%last   = backpoin(compr%last)
  uncompr%n      = uncompr%last-uncompr%first+1
  if (compr%i.le.0) then
    uncompr%i    = 0  ! unset -> unset
  else
    uncompr%i    = backpoin(compr%i)
  endif
  uncompr%vfirst = compr%vfirst  ! Associated values are the same!
  uncompr%vlast  = compr%vlast   ! Associated values are the same!
  !
end subroutine mrtcal_bookkeeping_compr2uncompr
