!-----------------------------------------------------------------------
! Support file adding Associated Arrays hooks to Class commands
!-----------------------------------------------------------------------
subroutine extract_assoc(assoc,extr,error)
  use gbl_message
  use classcore_interfaces, except_this=>extract_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Extract a subset of each Associated Array. Extraction is made
  ! inplace
  !---------------------------------------------------------------------
  type(class_assoc_t), intent(inout) :: assoc
  type(extract_t),     intent(in)    :: extr   ! Extraction descriptor
  logical,             intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='EXTRACT>ASSOC'
  integer(kind=4) :: iarray
  type(class_assoc_t) :: old
  !
  if (assoc%n.le.0)  return  ! Nothing to be done
  !
  ! Save the current arrays in the 'old' structure. For efficiency purpose
  ! allocation status is stolen to the input arrays.
  call reassociate_assoc(assoc,old,.true.,error)
  if (error)  return
  !
  do iarray=1,assoc%n
    assoc%array(iarray)%dim1 = extr%nc  ! New size of array
    call reallocate_assoc_sub(assoc%array(iarray),error)
    if (error)  exit
    !
    select case (assoc%array(iarray)%fmt)
    case (fmt_r4)
      call do_extract_data(old%array(iarray)%r4(:,1),old%array(iarray)%dim1,      &
                           assoc%array(iarray)%r4(:,1),assoc%array(iarray)%dim1,  &
                           assoc%array(iarray)%badr4,                             &
                           extr,error)
    case (fmt_i4,fmt_by,fmt_b2)
      call do_extract_data(old%array(iarray)%i4(:,1),old%array(iarray)%dim1,      &
                           assoc%array(iarray)%i4(:,1),assoc%array(iarray)%dim1,  &
                           assoc%array(iarray)%badi4,                             &
                           extr,error)
    case default
      call class_message(seve%e,rname,'Kind of data not implemented')
      error = .true.
    end select
    if (error)  exit
  enddo
  !
  call deallocate_assoc(old,error)
  !
end subroutine extract_assoc
!
subroutine smooth_assoc(assoc,skind,width,space,ushift,nx,error)
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>smooth_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Smooth the associated array section
  !---------------------------------------------------------------------
  type(class_assoc_t), intent(inout) :: assoc   !
  character(len=*),    intent(in)    :: skind   ! SMOOTH kind
  real(kind=4),        intent(in)    :: width   ! Width (SMOOTH GAUSS|HANN)
  real(kind=4),        intent(in)    :: space   ! Channel space (SMOOTH HANN)
  real(kind=4),        intent(in)    :: ushift  ! User shift (SMOOTH HANN)
  integer(kind=4),     intent(in)    :: nx      ! Nchan per box (SMOOTH BOX)
  logical,             intent(inout) :: error   !
  ! Local
  character(len=*), parameter :: rname='SMOOTH'
  integer(kind=4) :: iarray,dim1,ier
  real(kind=4) :: cbad
  real(kind=4), allocatable :: y1(:),y2(:)
  !
  if (assoc%n.le.0)  return  ! Nothing to be done
  !
  if (skind.eq.'NOISE') then
    call class_message(seve%w,rname,'NOISE smoothing skipped for Associated Arrays')
    return
  endif
  !
  dim1 = assoc%array(1)%dim1
  allocate(y1(dim1),y2(dim1),stat=ier)
  if (failed_allocate(rname,'y values workspace',ier,error))  return
  !
  do iarray=1,assoc%n
    call copy_assoc_sub_aator4(rname,assoc%array(iarray),y1,cbad,error)
    if (error)  exit
    !
    ! Do the job
    dim1 = assoc%array(iarray)%dim1
    select case (skind)
    case ('HANNING')
      call smhann(y1,y2,dim1,cbad,width,space,ushift,error)
    case ('GAUSS')
      call smgauss(y1,y2,dim1,cbad,width,error)
    case ('BOX')
      call smbox(y1,y2,dim1,cbad,nx,error)
    case default
      call class_message(seve%e,rname,  &
        'Smoothing kind '//trim(skind)//' not supported for Associated Arrays')
      error = .true.
    end select
    if (error)  exit
    !
    ! NB: dim1 has been updated in return
    call copy_assoc_sub_r4toaa(rname,assoc%array(iarray),y2(1:dim1),cbad,error)
    if (error)  exit
  enddo
  !
  deallocate(y1,y2)
  !
end subroutine smooth_assoc
!
subroutine resample_assoc(set,assoc,idatax,isirreg,old,new,dofft,error)
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>resample_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for resampling Associated Arrays
  !---------------------------------------------------------------------
  type(class_setup_t),   intent(in)    :: set        !
  type(class_assoc_t),   intent(inout) :: assoc      !
  real(kind=xdata_kind), intent(in)    :: idatax(:)  ! Size old%nchan
  logical,               intent(inout) :: isirreg    !
  type(resampling),      intent(in)    :: old        ! Old X-axis sampling
  type(resampling),      intent(inout) :: new        ! New X-axis sampling
  logical,               intent(in)    :: dofft      ! In frequency or time domain
  logical,               intent(inout) :: error      !
  ! Local
  character(len=*), parameter :: rname='RESAMPLE>ASSOC'
  real(kind=4), allocatable :: idatay(:),odatay(:),odataw(:)
  integer(kind=4) :: ier,iarray
  real(kind=4) :: cbad
  logical :: isirreg2
  !
  if (assoc%n.le.0)  return  ! Nothing to be done
  !
  allocate(idatay(old%nchan),odatay(new%nchan),odataw(new%nchan),stat=ier)
  if (failed_allocate(rname,'y and w value workspace',ier,error))  return
  !
  do iarray=1,assoc%n
    call copy_assoc_sub_aator4(rname,assoc%array(iarray),idatay,cbad,error)
    if (error)  return
    !
    isirreg2 = isirreg  ! Always restart with correct irregular status
    call do_resample_generic(set,idatax,idatay,odatay,odataw,cbad,isirreg2,  &
      old,new,dofft,error)
    if (error)  return
    !
    call copy_assoc_sub_r4toaa(rname,assoc%array(iarray),odatay,cbad,error)
    if (error)  return
  enddo
  !
  ! Finally set irregular status
  isirreg = isirreg2
  !
  deallocate(idatay,odatay,odataw)
  !
end subroutine resample_assoc
!
subroutine resample_interpolate_regul_assoc(set,rname,in,idataw,iaxis,  &
  out,odataw,oaxis,error)
  use gbl_message
  use classcore_interfaces, except_this=>resample_interpolate_regul_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for resampling Associated Arrays in the context
  ! of AVERAGE
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set        !
  character(len=*),    intent(in)    :: rname      ! Calling routine name
  type(class_assoc_t), intent(in)    :: in         !
  real(kind=4),        intent(in)    :: idataw(:)  !
  type(resampling),    intent(in)    :: iaxis      !
  type(class_assoc_t), intent(inout) :: out        !
  real(kind=4),        intent(out)   :: odataw(:)  !
  type(resampling),    intent(in)    :: oaxis      !
  logical,             intent(inout) :: error      ! Logical error flag
  ! Local
  integer(kind=4) :: iarray,ismin,ismax
  real(kind=4) :: ibad
  real(kind=4), allocatable :: idatay(:)
  !
  if (in%n.le.0)  return  ! Nothing to be done
  !
  allocate(idatay(iaxis%nchan))
  !
  do iarray=1,in%n
    !
    ! In the context of AVERAGE, we should skip the W array!
    if (in%array(iarray)%name.eq.'W')  cycle
    !
    call copy_assoc_sub_aator4(rname,in%array(iarray),idatay,ibad,error)
    if (error)  return
    !
    ! We ensured that the output (resampled) arrays are R*4. Sanity check:
    if (out%array(iarray)%fmt.ne.fmt_r4) then
      call class_message(seve%e,rname,'Programming error: Associated Array should be R*4')
      error = .true.
      return
    endif
    !
    call resample_interpolate_regul(set,                               &
      idatay,                   idataw,ibad,                   iaxis,  &
      out%array(iarray)%r4(:,1),odataw,out%array(iarray)%badr4,oaxis,  &
      ismin,ismax,error)
    !
  enddo
  !
  deallocate(idatay)
  !
end subroutine resample_interpolate_regul_assoc
!
subroutine waverage_assoc(rname,one,rdataw,sumio,sdataw,schanmin,schanmax,  &
  contaminate,error)
  use gbl_message
  use classcore_interfaces, except_this=>waverage_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for averaging Associated Arrays
  !---------------------------------------------------------------------
  character(len=*),    intent(in)    :: rname        ! Calling routine name
  type(class_assoc_t), intent(in)    :: one          ! Section to be added to the running sum
  real(kind=4),        intent(in)    :: rdataw(:)    ! Weight of input arrays (all the same)
  type(class_assoc_t), intent(inout) :: sumio        ! Running sum
  real(kind=4),        intent(inout) :: sdataw(:)    ! Weight of the running sum (input only, not updated here)
  integer(kind=4),     intent(in)    :: schanmin     ! First channel
  integer(kind=4),     intent(in)    :: schanmax     ! Last channel
  logical,             intent(in)    :: contaminate  ! Bad channels contaminate output or not?
  logical,             intent(inout) :: error        ! Logical error flag
  ! Local
  integer(kind=4) :: nchan,iarray
  real(kind=4), allocatable :: rdatay(:)
  real(kind=4) :: rbad
  !
  if (sumio%n.le.0)  return  ! Nothing to be done
  !
  !---------------------------------------------------------------------
  ! This consistency check is here (i.e. during data processing) for
  ! now, but it should go in the consistency tools (i.e. during header
  ! processing) as soon as they support the Associated Arrays.
  if (one%n.ne.sumio%n) then
    call class_message(seve%e,rname,  &
      'Inconsistent Associated Arrays (not same number of arrays)')
    error = .true.
    return
  endif
  do iarray=1,sumio%n
    ! Compare names, knowing that:
    ! 1) 'one' may be the original arrays (original format with
    !    original names) or a resampled version (R*4 with T prefixed).
    ! 2) 'sumio' contains R*4 version with names prefixed with T.
    ! This issue will be resolved when only the original versions
    ! will be checked for consistency.
    if (one%array(iarray)%name.ne.sumio%array(iarray)%name .and.  &
        one%array(iarray)%name.ne.sumio%array(iarray)%name(2:)) then
      call class_message(seve%e,rname,  &
        'Inconsistent Associated Arrays (name or order differ)')
      error = .true.
      return
    endif
  enddo
  !---------------------------------------------------------------------
  !
  nchan = sumio%array(1)%dim1
  allocate(rdatay(nchan))
  !
  do iarray=1,sumio%n
    ! Convert arrays to R*4
    call copy_assoc_sub_aator4(rname,one%array(iarray),rdatay,rbad,error)
    if (error)  return
    !
    ! - Add rdatay to sumio. Note that we ensured that ALL the AA of sumio
    !   are R*4
    ! - Do not update sdataw in-place, this will be done when dealing with
    !   RY.
    call simple_waverage(rdatay,                     rdataw,rbad,                       &
                         sumio%array(iarray)%r4(:,1),sdataw,sumio%array(iarray)%badr4,  &
                         schanmin,schanmax,contaminate,.false.)
    !
  enddo
  !
  deallocate(rdatay)
  !
end subroutine waverage_assoc
!
subroutine wrms_assoc(rname,one,odataw,aver,adataw,rms,rdataw,  &
  chanmin,chanmax,contaminate,error)
  use classcore_interfaces, except_this=>wrms_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for weighted-rms of Associated Arrays
  !---------------------------------------------------------------------
  character(len=*),    intent(in)    :: rname        ! Calling routine name
  type(class_assoc_t), intent(in)    :: one          ! Section to be added to the running RMS
  real(kind=4),        intent(in)    :: odataw(:)    ! Weight of input arrays (all arrays have the same)
  type(class_assoc_t), intent(in)    :: aver         ! Average of all sections
  real(kind=4),        intent(in)    :: adataw(:)    ! Weight of the average (all arrays have the same)
  type(class_assoc_t), intent(inout) :: rms          ! Running RMS
  real(kind=4),        intent(inout) :: rdataw(:)    ! Weight of the running RMS (input only, not updated here)
  integer(kind=4),     intent(in)    :: chanmin      ! First channel
  integer(kind=4),     intent(in)    :: chanmax      ! Last channel
  logical,             intent(in)    :: contaminate  ! Bad channels contaminate output or not?
  logical,             intent(inout) :: error        ! Logical error flag
  ! Local
  integer(kind=4) :: iarray
  real(kind=4), allocatable :: odatay(:)
  real(kind=4) :: obad
  !
  if (rms%n.le.0)  return  ! Nothing to be done
  !
  allocate(odatay(rms%array(1)%dim1))
  !
  do iarray=1,rms%n
    ! Convert arrays to R*4
    call copy_assoc_sub_aator4(rname,one%array(iarray),odatay,obad,error)
    if (error)  return
    !
    ! Add rdatay to sdatay, do not update sdataw in-place, this will
    ! be done when dealing with RY.
    call simple_wrms(  &
      odatay,                    odataw,obad,                      &  ! One obs to be added
      aver%array(iarray)%r4(:,1),adataw,aver%array(iarray)%badr4,  &  ! Average of all obs
      rms%array(iarray)%r4(:,1), rdataw,rms%array(iarray)%badr4,   &  ! Current RMS, updated
      chanmin,chanmax,contaminate,.false.)
  enddo
  !
  deallocate(odatay)
  !
end subroutine wrms_assoc
!
subroutine fold_assoc(set,assoc,nphase,throw,weight,omin,omax,error)
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>fold_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for folding Associated Arrays
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set             !
  type(class_assoc_t), intent(inout) :: assoc           !
  integer(kind=4),     intent(in)    :: nphase          !
  real(kind=4),        intent(in)    :: throw(nphase)   ! [chan]
  real(kind=4),        intent(in)    :: weight(nphase)  !
  integer(kind=4),     intent(in)    :: omin            !
  integer(kind=4),     intent(in)    :: omax            !
  logical,             intent(inout) :: error           !
  ! Local
  character(len=*), parameter :: rname='FOLD>ASSOC'
  integer(kind=4) :: inchan,onchan,ier,iarray,omin2,omax2,shift
  real(kind=4) :: cbad
  real(kind=4), allocatable :: idatay(:),odatay(:),odataw(:)
  !
  if (assoc%n.le.0)  return  ! Nothing to be done
  !
  inchan = assoc%array(1)%dim1
  onchan = 2*inchan+1
  allocate(idatay(inchan),odatay(onchan),odataw(onchan),stat=ier)
  if (failed_allocate(rname,'y and w value workspace',ier,error))  return
  !
  do iarray=1,assoc%n
    call copy_assoc_sub_aator4(rname,assoc%array(iarray),idatay,cbad,error)
    if (error)  return
    !
    ! NB: keepblank forced to .true., but anyway extracted part will be the
    ! same as RY (see comments below)
    call classcore_fold_obs_sub(set,idatay,idatay,inchan,cbad,  &
                                nphase,throw(:),weight(:),      &
                               .true.,                         &
                               odatay,odataw,onchan,           &
                               omin2,omax2,shift,              &
                               error)
    if (error)  return
    !
    ! We use omin:omax (i.e. the range returned from the RY folding) and not
    ! omin2:omax2 (i.e. the range returned from the current array folding)
    ! because this range depends on the data contents (in particular in terms
    ! of blank values). But we have to ensure that the folded RY and the folded
    ! arrays cover the same channels, we have to be consistent.
    !
    call copy_assoc_sub_r4toaa(rname,assoc%array(iarray),odatay(omin:omax),cbad,error)
    if (error)  return
  enddo
  !
  deallocate(idatay,odatay,odataw)
  !
end subroutine fold_assoc
!
subroutine rzero_assoc(obs)
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  ! Reset the Associated Arrays section to default value
  !---------------------------------------------------------------------
  type(observation), intent(inout) :: obs  !
  !
  obs%assoc%n = 0
  !
  !  Do not deallocate obs%assoc%array nor obs%assoc%array(:)%r4: we keep
  ! them allocated for later reuse.
  !
end subroutine rzero_assoc
!
subroutine newdat_assoc(set,obs,error)
  use gildas_def
  use gbl_format
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>newdat_assoc
  use class_types
  !---------------------------------------------------------------------
  ! @ public (for libclass only)
  ! Recompute the Associated Section related data, typically when a new
  ! observation is loaded. Code is optimized to avoid deleting or
  ! defining the Sic variables if there is no need.
  !---------------------------------------------------------------------
  type(class_setup_t), intent(in)    :: set
  type(observation),   intent(inout) :: obs
  logical,             intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='NEWDAT>ASSOC'
  integer(kind=4) :: iarray,ndim
  integer(kind=index_length) :: dims(sic_maxdims)
  logical :: delete_all,define_all,readonly,delete_one,define_one
  character(len=*), parameter :: parent='R%ASSOC'
  character(len=varname_length) :: strname,varname,badname
  type(class_assoc_t) :: R_assoc
  logical :: got_assoc,ro_assoc
  ! Saved status
  save :: got_assoc,R_assoc,ro_assoc
  ! Data
  data got_assoc /.false./
  !
  if (.not.obs%is_R)  return
  !
  readonly = set%varpresec(class_sec_assoc_id).ne.setvar_write
  !
  delete_all = got_assoc .and. (  &
               (R_assoc%n.gt.0 .and. R_assoc%n.ne.obs%assoc%n)  & ! Was defined but is resized
               .or.  &
               (ro_assoc.neqv.readonly)  &  ! RO status is changing: delete everything
               )
  if (delete_all) then
    ! Easy, no waste of time with sic_varexist
    define_all = .true.
  else
    define_all = .not.sic_varexist(parent)
  endif
  !
  if (delete_all) then
    call sic_delvariable(parent,.false.,error)
    if (error)  error = .false.  ! No error if not found
  endif
  !
  if (obs%assoc%n.eq.0) then
    ! No Associated Array to create. Save current for next calls and leave.
    call get_assoc(error)
    return
  endif
  !
  if (define_all) then
    call sic_defstructure(parent,.true.,error)
    if (error)  return
  endif
  !
  do iarray=1,obs%assoc%n
    !
    ! On which condition those elements should be redefined?
    if (delete_all) then
      delete_one = .false.
      define_one = .true.
    elseif (.not.got_assoc) then
      delete_one = .false.  ! First entering in this subroutine
      define_one = .true.
    elseif (iarray.gt.R_assoc%n) then
      delete_one = .false.  ! No such array to delete
      define_one = .true.
    elseif (R_assoc%array(iarray)%name .ne. obs%assoc%array(iarray)%name .or.  &
            R_assoc%array(iarray)%dim1 .ne. obs%assoc%array(iarray)%dim1 .or.  &
            R_assoc%array(iarray)%dim2 .ne. obs%assoc%array(iarray)%dim2) then
      delete_one = .true.
      define_one = .true.
    else
      select case (obs%assoc%array(iarray)%fmt)
      case (fmt_r4)
        define_one = .not.associated(R_assoc%array(iarray)%r4,obs%assoc%array(iarray)%r4)
      case (fmt_by,fmt_i4)
        define_one = .not.associated(R_assoc%array(iarray)%i4,obs%assoc%array(iarray)%i4)
      case default
        define_one = .false.
      end select
      delete_one = define_one
    endif
    !
    if (delete_one) then
      strname = parent//'%'//R_assoc%array(iarray)%name
      call sic_delvariable(strname,.false.,error)
      if (error)  error = .false.  ! No error if not found, but should not happen
    endif
    !
    if (define_one) then
      ! Parent structure
      strname = parent//'%'//obs%assoc%array(iarray)%name
      call sic_defstructure(strname,.true.,error)
      if (error)  return
      !
      ! Dimensions: always read-only
      varname = trim(strname)//'%DIM2'
      call sic_def_inte(varname,obs%assoc%array(iarray)%dim2,0,0,.true.,error)
      if (error)  return
      !
      ! Unit
      varname = trim(strname)//'%UNIT'
      call sic_def_char(varname,obs%assoc%array(iarray)%unit,readonly,error)
      if (error)  return
      !
      ! Data: always RW (same as RY)
      badname = trim(strname)//'%BAD'
      varname = trim(strname)//'%DATA'
      dims(1) = obs%assoc%array(iarray)%dim1
      dims(2) = obs%assoc%array(iarray)%dim2
      if (dims(2).gt.0) then
        ndim = 2
      else
        ndim = 1
      endif
      !
      select case (obs%assoc%array(iarray)%fmt)
      case (fmt_r4)
        call sic_def_real(badname,obs%assoc%array(iarray)%badr4,0,0,   readonly,error)
        call sic_def_real(varname,obs%assoc%array(iarray)%r4,ndim,dims,.false., error)
      case (fmt_i4,fmt_by,fmt_b2)
        call sic_def_inte(badname,obs%assoc%array(iarray)%badi4,0,0,   readonly,error)
        call sic_def_inte(varname,obs%assoc%array(iarray)%i4,ndim,dims,.false., error)
      case default
        call class_message(seve%e,rname,'Kind of data is not implemented')
        error = .true.
      end select
      if (error)  return
    endif
    !
  enddo
  !
  call get_assoc(error)  ! For next calls
  ro_assoc = readonly
  !
contains
  subroutine get_assoc(error)
    logical, intent(inout) :: error
    integer(kind=4) :: iarray
    call reallocate_assoc(R_assoc,obs%assoc%n,.false.,error)
    if (error)  return
    do iarray=1,R_assoc%n
      call reassociate_assoc_sub(obs%assoc%array(iarray),R_assoc%array(iarray),.false.,error)
      if (error)  return
    enddo
    got_assoc = .true.
  end subroutine get_assoc
  !
end subroutine newdat_assoc
!
subroutine class_assoc_dump(obs,error)
  use gbl_message
  use classcore_dependencies_interfaces
  use classcore_interfaces, except_this=>class_assoc_dump
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !   Dump all the Associated Arrays from the input observation
  !---------------------------------------------------------------------
  type(observation), intent(in)    :: obs    ! Observation
  logical,           intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ASSOC>DUMP'
  integer(kind=4) :: iarray,dim1,dim2
  character(len=message_length) :: mess
  character(len=80) :: badstring,datastring
  !
  if (obs%assoc%n.eq.0) then
    call class_message(seve%w,rname,'Observation has no Associated Array')
    return
  endif
  !
  write(mess,'(2X,A,1X,I0)') 'Number of associated arrays:',obs%assoc%n
  call class_message(seve%r,rname,mess)
  !
  do iarray=1,obs%assoc%n
    write(mess,'(2X,A,I0,A,A)')          &
      'Associated array #',iarray,': ',obs%assoc%array(iarray)%name
    call class_message(seve%r,rname,mess)
    !
    ! Format specific:
    dim1 = obs%assoc%array(iarray)%dim1
    dim2 = max(1,obs%assoc%array(iarray)%dim2)
    select case (obs%assoc%array(iarray)%fmt)
    case (fmt_r4)
      write(badstring,11)  obs%assoc%array(iarray)%badr4
      if (dim1*dim2.le.4) then
        write(datastring,11)  obs%assoc%array(iarray)%r4(1:dim1,1:dim2)
      else
        write(datastring,21)  obs%assoc%array(iarray)%r4(1:2,1),  &
                              obs%assoc%array(iarray)%r4(dim1-1:dim1,dim2)
      endif
    case (fmt_i4)
      write(badstring,12)  obs%assoc%array(iarray)%badi4
      if (dim1*dim2.le.4) then
        write(datastring,12)  obs%assoc%array(iarray)%i4(1:dim1,1:dim2)
      else
        write(datastring,22)  obs%assoc%array(iarray)%i4(1:2,1),  &
                              obs%assoc%array(iarray)%i4(dim1-1:dim1,dim2)
      endif
    case (fmt_by)
      write(badstring,13)  obs%assoc%array(iarray)%badi4
      if (dim1*dim2.le.8) then
        write(datastring,13)  obs%assoc%array(iarray)%i4(1:dim1,1:dim2)
      else
        write(datastring,23)  obs%assoc%array(iarray)%i4(1:4,1),  &
                              obs%assoc%array(iarray)%i4(dim1-3:dim1,dim2)
      endif
    case (fmt_b2)
      write(badstring,14)  obs%assoc%array(iarray)%badi4
      if (dim1*dim2.le.12) then
        write(datastring,14)  obs%assoc%array(iarray)%i4(1:dim1,1:dim2)
      else
        write(datastring,24)  obs%assoc%array(iarray)%i4(1:6,1),  &
                              obs%assoc%array(iarray)%i4(dim1-5:dim1,dim2)
      endif
    case default
      badstring  = 'N/A'
      datastring = '(not implemented)'
    end select
    !
    write(mess,'(4X,A,A,2X,A,I0,2X,A,A,2X,A,I0,A,I0)')  &
      'Unit: ',obs%assoc%array(iarray)%unit,        &
      'Format: ',obs%assoc%array(iarray)%fmt,       &
      'Bad: ',trim(badstring),                      &
      'Dimensions: ',obs%assoc%array(iarray)%dim1,  &
               ' x ',obs%assoc%array(iarray)%dim2
    call class_message(seve%r,rname,mess)
    !
    write(mess,'(4X,2A)')  'Data: ',trim(datastring)
    call class_message(seve%r,rname,mess)
  enddo
  !
11 format(4(1X,1PG14.7))
12 format(4(1X,I10))
13 format(8(1X,I4))
14 format(12(1X,I2))
  !
21 format(2(1X,1PG14.7),'   ...   ',2(1X,1PG14.7))
22 format(2(1X,I10),    '   ...   ',2(1X,I10))
23 format(4(1X,I4),     '   ...   ',4(1X,I4))
24 format(6(1X,I2),     '   ...   ',6(1X,I2))
  !
end subroutine class_assoc_dump
!
subroutine class_assoc_minmax(obs,aaname,rmin,rmax,error)
  use gbl_format
  use gbl_message
  use classcore_interfaces, except_this=>class_assoc_minmax
  use class_types
  !---------------------------------------------------------------------
  ! @ private
  !  Compute the min max range, in the current SET MODE X, plus some
  ! MARGIN. This is suited for plotting (same as subroutine class_minmax
  ! for RY).
  !---------------------------------------------------------------------
  type(observation), intent(in)    :: obs
  character(len=*),  intent(in)    :: aaname
  real(kind=4),      intent(out)   :: rmin
  real(kind=4),      intent(out)   :: rmax
  logical,           intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='MINMAX'
  integer(kind=4) :: n
  type(class_assoc_sub_t) :: array
  !
  if (.not.class_assoc_exists(obs,aaname,array)) then
    call class_message(seve%e,rname,'No such associated array '//aaname)
    error = .true.
    return
  endif
  !
  if (array%dim2.gt.1) then
    call class_message(seve%e,rname,trim(aaname)//' is 2D, can not plot')
    error = .true.
    return
  endif
  !
  n = obs%cimax-obs%cimin+1
  !
  select case (array%fmt)
  case (fmt_r4)
    call class_minmax(rmin,rmax,array%r4(obs%cimin:obs%cimax,1),n,array%badr4)
    !
  case (fmt_i4,fmt_by,fmt_b2)
    call class_minmax(rmin,rmax,array%i4(obs%cimin:obs%cimax,1),n,array%badi4)
    !
  case default
    call class_message(seve%e,rname,'Kind of data not implemented')
    error = .true.
    return
  end select
  !
end subroutine class_assoc_minmax
