!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubeadm_spectrum_types
  use cubetools_parameters
  use cubetools_array_types
  use cubetools_axis_types
  use cube_types
  use cubeadm_messaging
  use cubeadm_taskloop
  use cubeadm_taskloop_iteration
  !
  public :: spectrum_t
  private
  !
  type :: spectrum_t
     type(cube_t),               private, pointer :: cube => null() ! Associated cube
     type(cubeadm_iterator_t),   private, pointer :: task => null() ! Associated task iteration
     integer(kind=indx_k),       private          :: crange(2)      ! Min-max range of channels
     character(len=name_l), private :: name = strg_unk ! spectrum name
     integer(kind=chan_k),  private :: m = 0 ! Allocated number of channels
     integer(kind=chan_k),  public  :: n = 0 ! Current number of valid channels
     type(real_1d_t),       public  :: y ! Brightness
     type(real_1d_t),       public  :: w ! Weight
     type(dble_1d_t),       public  :: x ! Spectral  axis
!     type(axis_t),         public  :: v ! Velocity  axis
!     type(axis_t),         public  :: f ! Frequency axis
!     type(axis_t),         public  :: c ! Channel   axis
     real(kind=sign_k) :: noi  = 0.0
     real(kind=coor_k) :: inc = 0d0
     real(kind=coor_k) :: val = 0d0
     real(kind=coor_k) :: ref = 0d0
     ! *** JP The following two fields are used only in mean-spectrum right now
     real(kind=coor_k) :: xoff = 0d0
     real(kind=coor_k) :: yoff = 0d0
   contains
     generic,   public  :: allocate          => allocate_iter,allocate_noiter
     generic,   public  :: associate         => associate_iter,associate_noiter
     procedure, public  :: allocate_y        => spectrum_allocate_y
     procedure, public  :: allocate_w        => spectrum_allocate_w
     procedure, public  :: allocate_xw       => spectrum_allocate_xw
     procedure, public  :: associate_x       => spectrum_associate_x
     procedure, public  :: get_header        => spectrum_get_header
     procedure, public  :: get               => spectrum_get_data
     procedure, public  :: put               => spectrum_put_data
     procedure, public  :: put_in            => spectrum_put_data_in
     procedure, public  :: point_to          => spectrum_point_to
     procedure, public  :: unblank           => spectrum_unblank
     procedure, public  :: mask              => spectrum_mask
     procedure, public  :: list              => spectrum_list
     !
     procedure, public  :: set_line_channels => spectrum_set_line_channels
     procedure, public  :: set_base_channels => spectrum_set_base_channels
!     procedure, public  :: set_line_channels_from_mask => spectrum_set_line_channels_from_mask
     procedure, public  :: set_base_channels_from_mask => spectrum_set_base_channels_from_mask
     !
     procedure, private :: tasknum          => spectrum_task_num
     procedure, private :: allocate_iter    => spectrum_allocate_iter
     procedure, private :: allocate_noiter  => spectrum_allocate_noiter
     procedure, private :: associate_iter   => spectrum_associate_iter
     procedure, private :: associate_noiter => spectrum_associate_noiter
     procedure, private :: prepare          => spectrum_prepare
  end type spectrum_t
  !
contains
  !
  subroutine spectrum_allocate_iter(spec,name,cube,iterator,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(spectrum_t),                intent(inout) :: spec
    character(len=*),                 intent(in)    :: name
    type(cube_t),                     intent(in)    :: cube
    type(cubeadm_iterator_t), target, intent(in)    :: iterator
    logical,                          intent(inout) :: error
    !
    spec%task => iterator
    call spectrum_allocate_noiter(spec,name,cube,error)
    if (error)  return
  end subroutine spectrum_allocate_iter
  !
  subroutine spectrum_allocate_noiter(spec,name,cube,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: spec
    character(len=*),  intent(in)    :: name
    type(cube_t),      intent(in)    :: cube
    logical,           intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>ALLOCATE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call spec%prepare(name,cube,error)
    if (error)  return
    ! Allocate
    call spec%y%reallocate('Y',spec%n,error)
    if (error) return
    ! Success => Complete the object
    spec%m = spec%n
  end subroutine spectrum_allocate_noiter
  !
  subroutine spectrum_associate_iter(spec,name,cube,iterator,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(spectrum_t),                intent(inout) :: spec
    character(len=*),                 intent(in)    :: name
    type(cube_t),                     intent(in)    :: cube
    type(cubeadm_iterator_t), target, intent(in)    :: iterator
    logical,                          intent(inout) :: error
    !
    spec%task => iterator
    call spectrum_associate_noiter(spec,name,cube,error)
    if (error)  return
  end subroutine spectrum_associate_iter
  !
  subroutine spectrum_associate_noiter(spec,name,cube,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: spec
    character(len=*),  intent(in)    :: name
    type(cube_t),      intent(in)    :: cube
    logical,           intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>ASSOCIATE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call spec%prepare(name,cube,error)
    if (error)  return
    ! Associate
    call spec%y%prepare_association('Y',spec%n,error)
    if (error) return
    ! Success => Complete the object
    spec%m = spec%n
  end subroutine spectrum_associate_noiter
  !
  subroutine spectrum_prepare(spec,name,cube,error)
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    class(spectrum_t),    intent(inout) :: spec
    character(len=*),     intent(in)    :: name
    type(cube_t), target, intent(in)    :: cube
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>PREPARE'
    !
    ! Sanity check
    if (cube%iscplx()) then
       call cubeadm_message(seve%e,rname,&
            'Invalid attempt to get a R*4 spectrum from a C*4 cube')
       error = .true.
       return
    endif
    if (cube%access().ne.code_access_speset) then
       call cubeadm_message(seve%e,rname,  &
            'Can not get a spectrum from a cube with other access declared')
       error = .true.
       return
    endif
    if (.not.cube%iter%ready(rname,error)) then
       call cubeadm_message(seve%e,rname,  &
         'Failed to associate or allocate the '''//name//''' spectrum on its cube')
       error = .true.
       return
    endif
    !
    spec%name = name
    spec%cube => cube
    call cube%iter%spectrum_region(spec%crange,error)
    if (error)  return
    call cube%iter%spectrum_size(spec%n,error)
    if (error)  return
  end subroutine spectrum_prepare
  !
  !------------------------------------------------------------------------
  !
  subroutine spectrum_allocate_y(spec,name,nc,error)
    !----------------------------------------------------------------------
    ! Allocate the y array to the right dimension. Contrary to the
    ! associate method, no cube is associated here to the spectrum!  This
    ! allocation is only required by some algorithms. Values are not set!
    !----------------------------------------------------------------------
    class(spectrum_t),    intent(inout) :: spec
    integer(kind=chan_k), intent(in)    :: nc
    character(len=*),     intent(in)    :: name
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>ALLOCATE>Y'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    spec%name = name
    call spec%y%reallocate('Y',nc,error)
    if (error) return
    spec%n = nc
    spec%m = nc
    spec%cube => null()
    spec%task => null()
  end subroutine spectrum_allocate_y
  !
  subroutine spectrum_allocate_w(spec,error)
    !----------------------------------------------------------------------
    ! Allocate the weight array to the right dimension. This allocation is
    ! only required by some algorithms. Values are not set!
    !----------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: spec
    logical,           intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>ALLOCATE>W'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Sanity check
    if (.not.associated(spec%cube)) then
      call cubeadm_message(seve%e,rname,'Internal error: cube pointer is null')
      error = .true.
      return
    endif
    !
    call spec%w%reallocate('W',spec%n,error)
    if (error) return
  end subroutine spectrum_allocate_w
  !
  subroutine spectrum_allocate_xw(spec,error)
    !----------------------------------------------------------------------
    ! Allocate the x-axis and weight arrays to the right dimension
    ! These allocations are only required by some algorithms.
    ! Values are not set!
    !----------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: spec
    logical,           intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>ALLOCATE>XW'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Sanity check
    if (.not.associated(spec%cube)) then
      call cubeadm_message(seve%e,rname,'Internal error: cube pointer is null')
      error = .true.
      return
    endif
    !
    call spec%x%reallocate('X',spec%n,error)
    if (error) return
    call spec%w%reallocate('W',spec%n,error)
    if (error) return
  end subroutine spectrum_allocate_xw
  !
  subroutine spectrum_associate_x(spec,error)
    use cubetools_header_methods
    !----------------------------------------------------------------------
    ! Associate the X axis to the velocity spectral axis when it is genuine
    ! and to the channel axis else.  This association is only required by
    ! some algorithms.
    !
    ! *** JP The kind of axis (velocity/frequency) should be selectable with a code
    !----------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: spec
    logical,           intent(inout) :: error
    !
    type(axis_t) :: axis
    character(len=*), parameter :: rname='SPECTRUM>ASSOCIATE>X'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Sanity check
    if (.not.associated(spec%cube)) then
      call cubeadm_message(seve%e,rname,'Internal error: cube pointer is null')
      error = .true.
      return
    endif
    !
    call cubetools_header_get_axis_head_v(spec%cube%head,axis,error)
    if (error) return
    if (axis%genuine) then
       call spec%x%prepare_association('X => V',axis%n,error)
       if (error) return
    else
       call cubetools_header_get_axis_head_c(spec%cube%head,axis,error)
       if (error) return
       call spec%x%prepare_association('X => C',axis%n,error)
       if (error) return
    endif
    call spec%x%get(axis%coord,error)
    if (error) return
    spec%inc = axis%inc
    spec%val = axis%val
    spec%ref = axis%ref
  end subroutine spectrum_associate_x
  !
  !------------------------------------------------------------------------
  !
  subroutine spectrum_get_header(spec,error)
    use cubetools_header_methods
    !---------------------------------------------------------------------
    ! Get spectrum header from the cube
    !
    ! *** JP The kind of axis (velocity/frequency) should be selectable with a code
    !---------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: spec
    logical,           intent(inout) :: error
    ! 
    type(axis_t) :: axis
    character(len=*), parameter :: rname='SPECTRUM>GET>HEADER'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call cubetools_header_get_axis_head_v(spec%cube%head,axis,error)
    if (error) return
    spec%inc = axis%inc
    spec%val = axis%val
    spec%ref = axis%ref
  end subroutine spectrum_get_header
  !
  subroutine spectrum_get_data(spec,oent,error)
    use cubeio_pix
    use cubetuple_entry
    !---------------------------------------------------------------------
    ! Get the ient spectrum from the cube
    ! When spec%y%val is an allocated pointer, we make a copy.
    ! In all other cases (associated or null), we make it point to the data.
    !---------------------------------------------------------------------
    class(spectrum_t),    intent(inout) :: spec   !
    integer(kind=entr_k), intent(in)    :: oent   ! Output entry number
    logical,              intent(inout) :: error  !
    ! 
    type(cube_pix_t) :: entry
    integer(kind=indx_k) :: xpix,ypix
    character(len=mess_l) :: mess
    character(len=*), parameter :: rname='SPECTRUM>GET>DATA'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call spec%cube%iter%spectrum_number(oent,xpix,ypix,error)
    if (error)  return
    call cubetuple_get_pix(spec%cube%user,  &
                           spec%cube%prog,  &
                           spec%cube,  &
                           xpix,ypix,  &
                           entry,error)
    if (error) return
    !
    ! Sanity check
    if (spec%crange(1).lt.1 .or. spec%crange(2).gt.entry%nc) then
      write(mess,'(5(A,I0))')  &
        'Region overlaps spectrum range. Region: [',  &
        spec%crange(1),':',spec%crange(2),'], spectrum: [',  &
        1,':',entry%nc,']'
      call cubeadm_message(seve%e,rname,mess)
      error = .true.
      return
    endif
    !
    call spec%y%get(entry%r4(spec%crange(1):spec%crange(2)),error)
    if (error) return
    ! *** JP: Why do we have to repeatedly allocate and free the entry
    ! *** JP: while the number of channels should always be the same?
    call cubeio_free_pix(entry,error)
    if (error) return
  end subroutine spectrum_get_data
  !
  subroutine spectrum_put_data(spec,oent,error)
    !---------------------------------------------------------------------
    ! Put the oent spectrum to the cube
    !---------------------------------------------------------------------
    class(spectrum_t),    intent(inout) :: spec   !
    integer(kind=entr_k), intent(in)    :: oent   ! Output entry number
    logical,              intent(inout) :: error  !
    !
    call spectrum_put_data_in(spec,spec%cube,oent,error)
    if (error)  return
  end subroutine spectrum_put_data
  !
  subroutine spectrum_put_data_in(spec,cube,oent,error)
    use cubeio_pix
    use cubetuple_entry
    !---------------------------------------------------------------------
    ! Put the oent spectrum to the cube.
    ! Only use pointers => Nothing to free.
    !
    ! This flavor, which explicitely states the output cube, should be used
    ! when the input spectrum needs to be written in another cube without
    ! copy. See, eg, the copy tool. This should an exotic use compare to
    ! spectrum_put.
    !---------------------------------------------------------------------
    class(spectrum_t),    intent(inout) :: spec   !
    type(cube_t),         intent(inout) :: cube   !
    integer(kind=entr_k), intent(in)    :: oent   ! Output entry number
    logical,              intent(inout) :: error  !
    !
    type(cube_pix_t) :: entry
    integer(kind=indx_k) :: xpix,ypix
    character(len=*), parameter :: rname='SPECTRUM>PUT>DATA>IN'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    entry%allocated = code_pointer_associated
    entry%nc = spec%n
    entry%r4 => spec%y%val
    entry%iscplx = .false.
    !
    call spec%cube%iter%spectrum_number(oent,xpix,ypix,error)
    if (error)  return
    call cubetuple_put_pix(cube%user,      &
                           cube%prog,      &
                           cube,           &
                           spec%tasknum(), &
                           xpix,ypix,      &
                           entry,          &
                           error)
    if (error) return
  end subroutine spectrum_put_data_in
  !
  !------------------------------------------------------------------------
  !
  subroutine spectrum_point_to(ou,in,ifirst,ilast,noise,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(spectrum_t),        intent(inout) :: ou
    type(spectrum_t), target, intent(in)    :: in
    integer(kind=chan_k),     intent(in)    :: ifirst
    integer(kind=chan_k),     intent(in)    :: ilast
    real(kind=sign_k),        intent(in)    :: noise
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>POINT>TO'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call ou%y%point_to(in%y,ifirst,ilast,error)
    if (error) return
    if (in%w%associated()) call ou%w%point_to(in%w,ifirst,ilast,error)
    if (error) return
    if (in%x%associated()) call ou%x%point_to(in%x,ifirst,ilast,error)
    if (error) return
    ou%ref = in%ref
    ou%val = in%val
    ou%inc = in%inc
    ou%noi = noise
    ou%n = ou%y%n
    ou%m = in%m
  end subroutine spectrum_point_to
  !
  subroutine spectrum_unblank(ou,in,error)
    !----------------------------------------------------------------------
    ! Return a compressed spectrum without NaN
    !----------------------------------------------------------------------
    class(spectrum_t),        intent(inout) :: ou
    type(spectrum_t), target, intent(in)    :: in
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPECTRUM>UNBLANK'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call ou%y%unblank(in%y,error)
    if (error) return
    if (in%x%associated().and.ou%x%associated()) call ou%x%unblank_as(in%x,in%y,error)
    if (error) return
    ou%n = ou%y%n
    ou%ref = in%ref
    ou%val = in%val
    ou%inc = in%inc
    ou%noi = in%noi
  end subroutine spectrum_unblank
  !
  !------------------------------------------------------------------------
  !
  subroutine spectrum_set_line_channels(spec,lineset,error)
    use cubetopology_firstlaststride_types
    use cubetopology_sperange_set_types
    use cubetopology_sperange_types
    !---------------------------------------------------------------------
    ! Set the spectrum weights of the spectra to 1 where the lines are
    ! located and zero elsewhere.
    !---------------------------------------------------------------------
    class(spectrum_t),         intent(inout) :: spec
    type(sperange_set_prog_t), intent(in)    :: lineset
    logical,                   intent(inout) :: error
    !
    integer(kind=wind_k) :: il
    type(firstlaststride_t) :: ic
    type(sperange_prog_t) :: line
    character(len=*), parameter :: rname='SET>LINE>CHANNELS'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Sanity check
    if (spec%w%unallocated(error)) return
    !
    spec%w%val = 0.0
    do il=1,lineset%n
       call lineset%get_into(il,line,error)
       if (error) return
       call line%to_axis_channel(ic,error)
       if (error) return
       spec%w%val(ic%first:ic%last) = 0.0
    enddo ! il
  end subroutine spectrum_set_line_channels
  !
  subroutine spectrum_set_base_channels(spec,lineset,error)
    use cubetopology_firstlaststride_types
    use cubetopology_sperange_set_types
    use cubetopology_sperange_types
    !---------------------------------------------------------------------
    ! Set the spectrum weights of the spectra to 1 where the baselines are
    ! located and zero elsewhere.
    !---------------------------------------------------------------------
    class(spectrum_t),         intent(inout) :: spec
    type(sperange_set_prog_t), intent(in)    :: lineset
    logical,                   intent(inout) :: error
    !
    integer(kind=wind_k) :: il
    type(firstlaststride_t) :: ic
    type(sperange_prog_t) :: line
    character(len=*), parameter :: rname='SET>BASE>CHANNELS'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Sanity check
    if (spec%w%unallocated(error)) return
    !
    spec%w%val = 1.0
    do il=1,lineset%n
       call lineset%get_into(il,line,error)
       if (error) return
       call line%to_axis_channel(ic,error)
       if (error) return
       spec%w%val(ic%first:ic%last) = 0.0
    enddo ! il
  end subroutine spectrum_set_base_channels
  !
  subroutine spectrum_set_base_channels_from_mask(spec,mask,error)
    use cubetools_nan
    !---------------------------------------------------------------------
    ! Set the spectrum weights of the spectra to 1 where the baselines are
    ! located and zero elsewhere.
    !---------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: spec
    type(spectrum_t),  intent(in)    :: mask
    logical,           intent(inout) :: error
    !
    integer(kind=chan_k) :: ic
    character(len=*), parameter :: rname='SET>BASE>CHANNELS>FROM>MASK'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Sanity check
    if (spec%w%unallocated(error)) return
    if (mask%y%unallocated(error)) return
    if (mask%y%n.ne.spec%w%n) then
       call cubeadm_message(admseve%trace,rname,'Welcome')
       error = .true.
       return
    endif
    !
    spec%w%val = 1.0
    do ic=1,mask%y%n
       if ((mask%y%val(ic).le.0.0).or.(ieee_is_nan(mask%y%val(ic)))) cycle
       spec%w%val(ic) = 0.0
    enddo ! il
  end subroutine spectrum_set_base_channels_from_mask
  !
  subroutine spectrum_mask(ou,in,error)
    use cubetools_nan
    !----------------------------------------------------------------------
    ! Return a compressed spectrum without NaN or part of the spectrum whose
    ! weights are <= 0.0
    !----------------------------------------------------------------------
    class(spectrum_t), intent(inout) :: ou
    type(spectrum_t),  intent(in)    :: in
    logical,           intent(inout) :: error
    !
    integer(kind=chan_k) :: ichan,jchan    
    character(len=*), parameter :: rname='SPECTRUM>MASK'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Sanity check
    if (in%x%unallocated(error)) return
    if (in%w%unallocated(error)) return
    if (ou%x%unallocated(error)) return
    if (ou%w%unallocated(error)) return
    !
    jchan = 1
    do ichan=1,in%n
       if ((in%w%val(ichan).le.0.0).or.(ieee_is_nan(in%y%val(ichan)))) cycle
       ou%x%val(jchan) = in%x%val(ichan)
       ou%y%val(jchan) = in%y%val(ichan)
       ou%w%val(jchan) = in%w%val(ichan)
       jchan = jchan+1
    enddo ! ichan
    ou%n = jchan-1
    ou%ref = in%ref
    ou%val = in%val
    ou%inc = in%inc
    ou%noi = in%noi
  end subroutine spectrum_mask
  !
  function spectrum_task_num(spe)
    !-------------------------------------------------------------------
    ! Return the task number this spectrum_t is running with
    !-------------------------------------------------------------------
    integer(kind=entr_k) :: spectrum_task_num
    class(spectrum_t), intent(in) :: spe
    !
    if (associated(spe%task)) then
      spectrum_task_num = spe%task%num
    else
      ! Assume single thread
      spectrum_task_num = 1
    endif
  end function spectrum_task_num
  !
  !------------------------------------------------------------------------
  !
  subroutine spectrum_list(spe,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(spectrum_t), intent(in)    :: spe
    logical,           intent(inout) :: error
    !
    integer(kind=chan_k) :: ic
    character(len=*), parameter :: rname='SPECTRUM>LIST'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    print *,spe%name
    print *,''
    print *,'noi: ',spe%noi
    print *,'inc: ',spe%inc
    print *,'val: ',spe%val
    print *,'ref: ',spe%ref
    print *,'xoff: ',spe%xoff
    print *,'yoff: ',spe%yoff
    print *,''
    print *,'nchan:',spe%n,' <= ',spe%m
    print *,''
    if (spe%x%associated()) then
       if (spe%y%associated()) then
          if (spe%w%associated()) then
             print *,'ic, x(ic), y(ic), w(ic)'
             do ic=1,spe%n
                print *,ic,spe%x%val(ic),spe%y%val(ic),spe%w%val(ic)
             enddo ! ic
             print *,'***************************************************************************'
             do ic=spe%n+1,spe%m
                print *,ic,spe%x%val(ic),spe%y%val(ic),spe%w%val(ic)
             enddo ! ic
          else
             print *,'ic, x(ic), y(ic)'
             do ic=1,spe%n
                print *,ic,spe%x%val(ic),spe%y%val(ic)
             enddo ! ic
             print *,'***************************************************************************'
             do ic=spe%n+1,spe%m
                print *,ic,spe%x%val(ic),spe%y%val(ic)
             enddo ! ic
          endif
       else
          if (spe%w%associated()) then
             print *,'ic, x(ic), w(ic)'
             do ic=1,spe%n
                print *,ic,spe%x%val(ic),spe%w%val(ic)
             enddo ! ic
             print *,'***************************************************************************'
             do ic=spe%n+1,spe%m
                print *,ic,spe%x%val(ic),spe%w%val(ic)
             enddo ! ic
          else
             print *,'ic, x(ic)'
             do ic=1,spe%n
                print *,ic,spe%x%val(ic)
             enddo ! ic
             print *,'***************************************************************************'
             do ic=spe%n+1,spe%m
                print *,ic,spe%x%val(ic)
             enddo ! ic
          endif
       endif
    else
       if (spe%y%associated()) then
          if (spe%w%associated()) then
             print *,'ic, y(ic), w(ic)'
             do ic=1,spe%n
                print *,ic,spe%y%val(ic),spe%w%val(ic)
             enddo ! ic
             print *,'***************************************************************************'
             do ic=spe%n+1,spe%m
                print *,ic,spe%y%val(ic),spe%w%val(ic)
             enddo ! ic
          else
             print *,'ic, y(ic)'
             do ic=1,spe%n
                print *,ic,spe%y%val(ic)
             enddo ! ic
             print *,'***************************************************************************'
             do ic=spe%n+1,spe%m
                print *,ic,spe%y%val(ic)
             enddo ! ic
          endif
       else
          if (spe%w%associated()) then
             print *,'ic, w(ic)'
             do ic=1,spe%n
                print *,ic,spe%w%val(ic)
             enddo ! ic
             print *,'***************************************************************************'
             do ic=spe%n+1,spe%m
                print *,ic,spe%w%val(ic)
             enddo ! ic
          else
             print *,'Unassociated X, Y, and W'
          endif
       endif
    endif
  end subroutine spectrum_list
end module cubeadm_spectrum_types
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
