!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubetools_header_array_types
  use cubetools_parameters
  use cubetools_messaging
  use cubetools_shape_types
  use cubetools_arrelt_types
  use cubetools_noise_types
  use cubetools_consistency_types
  !
  public :: array_t,array_unit_user_t,array_cons_t
  public :: cubetools_array_init,cubetools_array_put_and_derive
  public :: cubetools_array_final,cubetools_array_get
  public :: cubetools_array_list,cubetools_array_unit_register
  public :: cubetools_array_extrema2userstruct
  public :: cubetools_array_unit_parse, cubetools_array_unit_user2prog
  public :: cubetools_array_unit2uservar
  public :: cubetools_array_sicdef,cubetools_array_copy
  public :: cubetools_array_consistency_init,cubetools_array_consistency_final
  public :: cubetools_array_consistency_check,cubetools_array_consistency_print
  public :: cubetools_array_init_extrema
  public :: cubetools_array_update_image_extrema
  public :: cubetools_array_update_spectrum_extrema
  public :: cubetools_array_update_subcube_extrema
  private
  !
  type array_t
     integer(kind=code_k)  :: type = code_null ! Type of data (e.g., REAL*4)
     character(len=unit_l) :: unit = strg_unk  ! Unit
     type(shape_t)         :: n                ! Shape related numbers
     type(arrelt_t)        :: min              ! Minimum value
     type(arrelt_t)        :: max              ! Maximum value
     type(noise_t)         :: noi              ! Noise properties
  contains
     procedure :: datasize => cubetools_array_datasize
  end type array_t
  !
  type array_unit_user_t
     character(len=argu_l) :: name = strg_unk  ! Unit name
     logical               :: do   = .false.   ! Option was present
  end type array_unit_user_t
  !
  type array_extrema_user_t
     character(len=argu_l) :: min = strg_unk ! [sec] Data min value
     character(len=argu_l) :: max = strg_unk ! [sec] Data max value
     logical               :: do  = .false.  ! Option was present
  end type array_extrema_user_t
  !
  type array_cons_t
     logical                    :: check=.true.  ! Check the section
     logical                    :: prob =.false. ! Is there a problem
     logical                    :: mess =.true.  ! Output message for this section?
     type(consistency_desc_t)   :: unit          ! Unit Consistency
     type(shape_cons_t)         :: n             ! Array shape Consistent
     type(arrelt_cons_t)        :: min           ! Minimum value
     type(arrelt_cons_t)        :: max           ! Maximum value
     type(noise_cons_t)         :: noi           ! Noise properties
  end type array_cons_t
contains
  !
  subroutine cubetools_array_init(array,error)
    !-------------------------------------------------------------------
    ! Initialize
    !-------------------------------------------------------------------
    type(array_t), intent(out)   :: array
    logical,       intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>INIT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_shape_init(array%n,error)
    if (error) return
    call cubetools_arrelt_init('Min',array%min,error)
    if (error) return
    call cubetools_arrelt_init('Max',array%max,error)
    if (error) return
    call cubetools_noise_init(array%noi,error)
    if(error) return
  end subroutine cubetools_array_init
  !
  subroutine cubetools_array_put_and_derive(type,unit,ndim,dim,nl,nm,nc,&
       minval,minloc,maxval,maxloc,sigma,rms,arr,error)
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    integer(kind=code_k),  intent(in)    :: type
    character(len=unit_l), intent(in)    :: unit
    integer(kind=ndim_k),  intent(in)    :: ndim
    integer(kind=data_k),  intent(in)    :: dim(:)
    integer(kind=data_k),  intent(in)    :: nl,nm,nc
    real(kind=sign_k),     intent(in)    :: minval
    integer(kind=data_k),  intent(in)    :: minloc(:)
    real(kind=sign_k),     intent(in)    :: maxval
    integer(kind=data_k),  intent(in)    :: maxloc(:)
    real(kind=sign_k),     intent(in)    :: sigma
    real(kind=sign_k),     intent(in)    :: rms
    type(array_t),         intent(inout) :: arr
    logical,               intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>PUT>AND>DERIVE'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    arr%type = type
    arr%unit = unit
    call cubetools_shape_get_and_derive(ndim,dim,nl,nm,nc,arr%n,error)
    if (error) return
    call cubetools_arrelt_get_and_derive(minval,minloc,arr%min,error)
    if (error) return
    call cubetools_arrelt_get_and_derive(maxval,maxloc,arr%max,error)
    if (error) return
    call cubetools_noise_get_and_derive(sigma,rms,arr%noi,error)
    if (error) return
  end subroutine cubetools_array_put_and_derive
  !
  subroutine cubetools_array_get(arr,type,unit,ndim,dim,nl,nm,nc,&
       minval,minloc,maxval,maxloc,sigma,rms,error)
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    type(array_t),         intent(in)    :: arr
    integer(kind=code_k),  intent(out)   :: type
    character(len=unit_l), intent(out)   :: unit
    integer(kind=ndim_k),  intent(out)   :: ndim
    integer(kind=data_k),  intent(out)   :: dim(:)
    integer(kind=data_k),  intent(out)   :: nl,nm,nc
    real(kind=sign_k),     intent(out)   :: minval
    integer(kind=data_k),  intent(out)   :: minloc(:)
    real(kind=sign_k),     intent(out)   :: maxval
    integer(kind=data_k),  intent(out)   :: maxloc(:)
    real(kind=sign_k),     intent(out)   :: sigma
    real(kind=sign_k),     intent(out)   :: rms
    logical,               intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>GET'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    type = arr%type
    unit = arr%unit
    call cubetools_shape_put(arr%n,ndim,dim,nl,nm,nc,error)
    if (error) return
    call cubetools_arrelt_put(arr%min,minval,minloc,error)
    if (error) return
    call cubetools_arrelt_put(arr%max,maxval,maxloc,error)
    if (error) return
    call cubetools_noise_put(arr%noi,sigma,rms,error)
    if (error) return
  end subroutine cubetools_array_get
  !
  subroutine cubetools_array_final(array,error)
    !-------------------------------------------------------------------
    ! Finalize
    !-------------------------------------------------------------------
    type(array_t), intent(inout) :: array
    logical,       intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>FINAL'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    array%type = code_null
    array%unit = 'unknown'
    call cubetools_shape_final(array%n,error)
    if (error) return
    call cubetools_arrelt_final(array%min,error)
    if (error) return
    call cubetools_arrelt_final(array%max,error)
    if (error) return
    call cubetools_noise_final(array%noi,error)
    if (error) return
  end subroutine cubetools_array_final
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_array_datasize(array,datasize,error)
    use gbl_format
    !-------------------------------------------------------------------
    ! Return the data size
    !-------------------------------------------------------------------
    class(array_t),       intent(in)    :: array
    integer(kind=data_k), intent(out)   :: datasize
    logical,              intent(inout) :: error
    !
    integer(kind=code_k) :: itype
    character(len=*), parameter :: rname='ARRAY>DATASIZE'
    !
    ! ZZZ This duplicates code from cubetools_shape_list
    itype = hardware-array%type ! hardware is defined in the gbl_format module
    select case (itype)
    case (1,3,4)
      datasize = 4  ! Bytes per element
    case (2,7,9)
      datasize = 8
    case (5)
      datasize = 2
    case (6)
      datasize = 1
    case (8)
      datasize = 16
    case default
       ! This should not happen
       call cubetools_message(seve%e,rname,'Unknown data type')
       error = .true.
       return
    end select
    datasize = array%n%dat*datasize
  end subroutine cubetools_array_datasize
  !
  subroutine cubetools_array_list(array,error)
    use cubetools_format
    !-------------------------------------------------------------------
    ! 
    !-------------------------------------------------------------------
    type(array_t), intent(in)    :: array
    logical,       intent(inout) :: error
    !
    character(len=mess_l) :: mess
    character(len=*), parameter :: rname='ARRAY>LIST'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_shape_list(array%type,array%n,error)
    if (error) return
    mess = cubetools_format_stdkey_boldval('Unit',array%unit,22)    
    call cubetools_message(seve%r,rname,mess)
    call cubetools_arrelt_list(array%min,error)
    if (error) return
    call cubetools_arrelt_list(array%max,error)
    if (error) return
    call cubetools_noise_list(array%noi,error)
    if (error) return
    call cubetools_message(seve%r,rname,' ')
  end subroutine cubetools_array_list
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_array_unit_register(name,abstract,option,error)
    use cubetools_structure
    !----------------------------------------------------------------------
    ! Register a /UNIT option under a given name, abstract
    !----------------------------------------------------------------------
    character(len=*),        intent(in)    :: name
    character(len=*),        intent(in)    :: abstract
    type(option_t), pointer, intent(out)   :: option
    logical,                 intent(inout) :: error
    !
    type(standard_arg_t) :: stdarg
    !
    character(len=*), parameter :: rname='ARRAY>UNIT>REGISTER'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_register_option(&
         name,'name',&
         abstract,&
         'Unit can be set to any 12 character string, but only&
         & recognized units are supported',&
         option,error)
    if (error) return
    call stdarg%register( &
         'name',  &
         'Unit name', &
         '"*" or "=" mean previous value is kept',&
         code_arg_mandatory, error)
    if (error) return
  end subroutine cubetools_array_unit_register
  !
  subroutine cubetools_array_unit_parse(line,opt,user,error)
    use cubetools_structure
    !----------------------------------------------------------------------
    ! Parse unit name 
    ! /UNIT name
    ! ----------------------------------------------------------------------
    character(len=*),        intent(in)    :: line
    type(option_t),          intent(in)    :: opt
    type(array_unit_user_t), intent(inout) :: user
    logical,                 intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>UNIT>PARSE'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call opt%present(line,user%do,error)
    if (error) return
    if (user%do) then
       call cubetools_getarg(line,opt,1,user%name,mandatory,error)
       if (error)  return
       if (len_trim(user%name).gt.unit_l) call cubetools_message(seve%w,rname,&
            'Unit name will be truncated at 12 characters')
    endif
  end subroutine cubetools_array_unit_parse
  !
  subroutine cubetools_array_unit_user2prog(user,prog,error)
    use cubetools_brightness
    use cubetools_unit
    !----------------------------------------------------------------------
    ! Resolves unit name based on a previous one according to user
    ! inputs
    ! --------------------------------------------------------------------
    type(array_unit_user_t), intent(in)    :: user
    character(len=unit_l),   intent(inout) :: prog
    logical,                 intent(inout) :: error
    !
    type(unit_user_t) :: unit
    character(len=unit_l) :: newunit
    integer(kind=code_k) :: unitkind
    logical :: valid
    character(len=*), parameter :: rname='ARRAY>UNIT>USER2PROG'
    !
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (user%do) then
       if (trim(user%name).eq.strg_equal.or.trim(user%name).eq.strg_star) then
          continue
       else
          call cubetools_unit_get_kind(user%name,unitkind,error)
          if (error) return
          select case (unitkind)
          case (code_unit_unk) 
             call cubetools_message(seve%w,rname,'Unsupported unit '//trim(user%name))
             prog = user%name
          case (code_unit_brig,code_unit_flux)
             call cubetools_brightness_valid_brightness_or_flux_unit(user%name,newunit,valid,error)
             if (error) return
             if (.not.valid) then
                call cubetools_message(seve%e,rname,'Internal error, brightness/flux unit not recognized')
                error = .true.
                return
             else
                prog = newunit
             endif
          case default
             call cubetools_unit_get(user%name,unitkind,unit,error)
             if (error) return
             prog = unit%name
          end select
       endif
    else
       continue
    endif
  end subroutine cubetools_array_unit_user2prog
  !
  subroutine cubetools_array_unit2uservar(prog,uservar,error)
    use cubetools_userspace
    use cubetools_uservar
    !------------------------------------------------------------------------
    ! Loads array unit onto a user space variable
    !------------------------------------------------------------------------
    type(array_t),      intent(in)    :: prog
    class(userspace_t), intent(inout) :: uservar
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname = 'ARRAY>UNIT2USERVAR'
    !
    call cubetools_message(toolseve%trace,rname,'welcome')
    !
    select type(uservar)
    type is (uservar_t)
       call uservar%set(prog%unit,error)
       if (error) return
    class default
       call cubetools_message(seve%e,rname,'Internal error: object has wrong type')
       error = .true.
       return
    end select
  end subroutine cubetools_array_unit2uservar
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_array_extrema_prog2user(prog,user,error)
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    type(array_t),              intent(in)    :: prog
    type(array_extrema_user_t), intent(out)   :: user
    logical,                    intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>EXTREMA>PROG2USER'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    write(user%min,'(1pg14.7)') prog%min%val
    write(user%max,'(1pg14.7)') prog%max%val
  end subroutine cubetools_array_extrema_prog2user
  !
  subroutine cubetools_array_extrema2userstruct(prog,userstruct,error)
    use cubetools_userspace
    use cubetools_userstruct
    !------------------------------------------------------------------------
    !
    !------------------------------------------------------------------------
    type(array_t),      intent(in)    :: prog
    class(userspace_t), intent(inout) :: userstruct
    logical,            intent(inout) :: error
    !
    type(array_extrema_user_t) :: user
    character(len=*), parameter :: rname='ARRAY>EXTREMA2USERSTRUCT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    select type(userstruct)
    type is (userstruct_t)
       call cubetools_array_extrema_prog2user(prog,user,error)
       if(error) return
       call userstruct%def(error)
       if (error) return
       call userstruct%set_member('min',user%min,error)
       if (error) return
       call userstruct%set_member('max',user%max,error)
       if (error) return
    class default
       call cubetools_message(seve%e,rname,'Internal error: object has wrong type')
       error = .true.
       return
    end select
  end subroutine cubetools_array_extrema2userstruct
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_array_sicdef(name,array,readonly,error)
    use gkernel_interfaces
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    character(len=*), intent(in)    :: name
    type(array_t),    intent(in)    :: array
    logical,          intent(in)    :: readonly
    logical,          intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>SICDEF'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call sic_defstructure(name,global,error)
    if (error) return
    call sic_def_charn(trim(name)//'%unit',array%unit,0,0,readonly,error)
    if (error) return
    call cubetools_shape_sicdef(trim(name)//'%n',array%n,readonly,error)
    if (error) return
    call cubetools_arrelt_sicdef(trim(name)//'%min',array%min,readonly,error)
    if (error) return
    call cubetools_arrelt_sicdef(trim(name)//'%max',array%max,readonly,error)
    if (error) return
    call cubetools_noise_sicdef(trim(name)//'%noi',array%noi,readonly,error)
    if (error) return
  end subroutine cubetools_array_sicdef
  !
  subroutine cubetools_array_copy(in,ou,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    type(array_t), intent(in)    :: in
    type(array_t), intent(out)   :: ou
    logical,       intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>COPY'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    ou%type = in%type
    ou%unit = in%unit
    call cubetools_shape_copy(in%n,ou%n,error)
    if (error) return
    call cubetools_arrelt_copy(in%min,ou%min,error)
    if (error) return
    call cubetools_arrelt_copy(in%max,ou%max,error)
    if (error) return
    call cubetools_noise_copy(in%noi,ou%noi,error)
    if (error) return
  end subroutine cubetools_array_copy
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_array_consistency_init(cons,error)
    !-------------------------------------------------------------------
    ! Init the consistency between two array sections
    !-------------------------------------------------------------------
    type(array_cons_t), intent(out)   :: cons
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>CONSISTENCY>INIT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_consistency_init(notol,check,mess,cons%unit,error)
    if (error) return
    call cubetools_shape_consistency_init(cons%n,error)
    if (error) return
    call cubetools_arrelt_consistency_init(cons%min,error)
    if (error) return
    call cubetools_arrelt_consistency_init(cons%max,error)
    if (error) return
    call cubetools_noise_consistency_init(cons%noi,error)
    if (error) return
    !
  end subroutine cubetools_array_consistency_init
  !
  subroutine cubetools_array_consistency_check(cons,arr1,arr2,error)
    !-------------------------------------------------------------------
    ! Check the consistency between two array sections
    !-------------------------------------------------------------------
    type(array_cons_t), intent(inout) :: cons
    type(array_t),      intent(in)    :: arr1
    type(array_t),      intent(in)    :: arr2
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>CONSISTENCY>CHECK'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (.not.cons%check) return
    !
    call cubetools_consistency_string_check(cons%unit,arr1%unit,arr2%unit,error)
    if (error) return
    call cubetools_shape_consistency_check(cons%n,arr1%n,arr2%n,error)
    if (error) return
    call cubetools_arrelt_consistency_check(cons%min,arr1%min,arr2%min,error)
    if (error) return
    call cubetools_arrelt_consistency_check(cons%max,arr1%max,arr2%max,error)
    if (error) return
    call cubetools_noise_consistency_check(cons%noi,arr1%noi,arr2%noi,error)
    if (error) return
    !
    cons%prob = cons%unit%prob.or.cons%n%prob.or.cons%min%prob.or.cons%max%prob
    !   
  end subroutine cubetools_array_consistency_check
  !
  subroutine cubetools_array_consistency_print(cons,arr1,arr2,error)
    !-------------------------------------------------------------------
    ! Print the consistency between two array sections
    !-------------------------------------------------------------------
    type(array_cons_t), intent(in)    :: cons
    type(array_t),      intent(in)    :: arr1
    type(array_t),      intent(in)    :: arr2
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>CONSISTENCY>PRINT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (.not.cons%mess) return
    !
    call cubetools_consistency_title('data arrays',2,cons%check,cons%prob,error)
    if(error) return
    if (cons%check.and.cons%prob) then
       call cubetools_array_unit_consistency_print(cons%unit,arr1%unit,arr2%unit,error)
       if (error) return
       call cubetools_shape_consistency_print(cons%n,arr1%n,arr2%n,error)
       if (error) return
       call cubetools_arrelt_consistency_print('minima',arr1%unit,arr2%unit,cons%min,arr1%min,arr2%min,error)
       if (error) return
       call cubetools_arrelt_consistency_print('maxima',arr1%unit,arr2%unit,cons%max,arr1%max,arr2%max,error)
       if (error) return
       call cubetools_noise_consistency_print(arr1%unit,arr2%unit,cons%noi,arr1%noi,arr2%noi,error)
       if (error) return
       endif
    call cubetools_message(seve%r,rname,'')
    !
  end subroutine cubetools_array_consistency_print
  !
  subroutine cubetools_array_unit_consistency_print(cons,unit1,unit2,error)
    !-------------------------------------------------------------------
    ! Print the consistency between two units
    !-------------------------------------------------------------------
    type(consistency_desc_t), intent(in)    :: cons
    character(len=*),         intent(in)    :: unit1
    character(len=*),         intent(in)    :: unit2
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>UNIT>CONSISTENCY>PRINT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (.not.cons%mess) return
    !
    call cubetools_consistency_title('units',3,cons%check,cons%prob,error)
    if(error) return
    if (cons%check.and.cons%prob) then
       call cubetools_consistency_string_print('Unit names',cons,unit1,unit2,error)
       if (error) return
    endif
    call cubetools_message(seve%r,rname,'')
  end subroutine cubetools_array_unit_consistency_print
  !
  subroutine cubetools_array_consistency_final(cons,error)
    !-------------------------------------------------------------------
    ! Final the consistency between two array sections
    !-------------------------------------------------------------------
    type(array_cons_t), intent(out)   :: cons
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname='ARRAY>CONSISTENCY>FINAL'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_consistency_final(cons%unit,error)
    if (error) return
    call cubetools_shape_consistency_final(cons%n,error)
    if (error) return
    call cubetools_arrelt_consistency_final(cons%min,error)
    if (error) return
    call cubetools_arrelt_consistency_final(cons%max,error)
    if (error) return
    call cubetools_noise_consistency_final(cons%noi,error)
    if (error) return
    !
  end subroutine cubetools_array_consistency_final
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_array_init_extrema(arr,error)
    !-------------------------------------------------------------------
    ! Also update the number of array elements and initialize the number
    ! of blanked (NaN) values.
    !-------------------------------------------------------------------
    type(array_t), intent(inout) :: arr
    logical,       intent(inout) :: error
    !
    arr%n%dat = product(arr%n%axis(1:arr%n%dim))
    arr%n%nan = 0
    call cubetools_arrelt_init(arr%min%name,arr%min,error)
    if (error)  return
    call cubetools_arrelt_init(arr%max%name,arr%max,error)
    if (error)  return
  end subroutine cubetools_array_init_extrema
  !
  subroutine cubetools_array_update_image_extrema(arr,data,nx,ny,iimage,error)
    use cubetools_nan
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    type(array_t),        intent(inout) :: arr
    integer(kind=pixe_k), intent(in)    :: nx,ny
    real(kind=sign_k),    intent(in)    :: data(nx,ny)
    integer(kind=imag_k), intent(in)    :: iimage
    logical,              intent(inout) :: error
    !
    integer(kind=size_length) :: nmin,nmax,nnan
    real(kind=sign_k) :: zmin,zmax
    integer(kind=pixe_k) :: xpixmin,ypixmin,xpixmax,ypixmax
    character(len=*), parameter :: rname='ARRAY>UPDATE>IMAGE>EXTREMA'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_array_extrema(nx*ny,data,zmin,zmax,nmin,nmax,nnan)
    !
    if (nmin.gt.0) then  ! One or more valid values found
      if (ieee_is_nan(arr%min%val) .or. zmin.lt.arr%min%val) then
        arr%min%val = zmin
        ypixmin = (nmin-1)/nx+1
        xpixmin = nmin-nx*(ypixmin-1)
        arr%min%ix = xpixmin
        arr%min%iy = ypixmin
        arr%min%ic = iimage
      endif
      if (ieee_is_nan(arr%max%val) .or. zmax.gt.arr%max%val) then
        arr%max%val = zmax
        ypixmax = (nmax-1)/nx+1
        xpixmax = nmax-nx*(ypixmax-1)
        arr%max%ix = xpixmax
        arr%max%iy = ypixmax
        arr%max%ic = iimage
      endif
    endif
    !
    if (nnan.gt.0) then
      arr%n%nan = arr%n%nan+nnan
    endif
  end subroutine cubetools_array_update_image_extrema
  !
  subroutine cubetools_array_update_spectrum_extrema(arr,data,ni,ix,iy,error)
    use cubetools_nan
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    type(array_t),        intent(inout) :: arr
    integer(kind=imag_k), intent(in)    :: ni
    real(kind=sign_k),    intent(in)    :: data(ni)
    integer(kind=pixe_k), intent(in)    :: ix,iy
    logical,              intent(inout) :: error
    !
    integer(kind=size_length) :: nmin,nmax,nnan
    real(kind=sign_k) :: zmin,zmax
    character(len=*), parameter :: rname='ARRAY>UPDATE>SPECTRUM>EXTREMA'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_array_extrema(ni,data,zmin,zmax,nmin,nmax,nnan)
    !
    if (nmin.gt.0) then  ! One or more valid values found
      if (ieee_is_nan(arr%min%val) .or. zmin.lt.arr%min%val) then
        arr%min%val = zmin
        arr%min%ix = ix
        arr%min%iy = iy
        arr%min%ic = nmin
      endif
      if (ieee_is_nan(arr%max%val) .or. zmax.gt.arr%max%val) then
        arr%max%val = zmax
        arr%max%ix = ix
        arr%max%iy = iy
        arr%max%ic = nmax
      endif
    endif
    !
    if (nnan.gt.0) then
      arr%n%nan = arr%n%nan+nnan  ! *** JP Still need to be debugged
    endif
  end subroutine cubetools_array_update_spectrum_extrema
  !
  subroutine cubetools_array_update_subcube_extrema(arr,data,n1,n2,n3,error)
    use cubetools_nan
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    type(array_t),        intent(inout) :: arr
    integer(kind=data_k), intent(in)    :: n1,n2,n3
    real(kind=sign_k),    intent(in)    :: data(n1,n2,n3)
    logical,              intent(inout) :: error
    !
    integer(kind=size_length) :: nmin,nmax,nnan
    real(kind=sign_k) :: zmin,zmax
    character(len=*), parameter :: rname='ARRAY>UPDATE>SUBCUBE>EXTREMA'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_array_extrema(n1*n2*n3,data,zmin,zmax,nmin,nmax,nnan)
    !
    if (nmin.gt.0) then  ! One or more valid values found
      if (ieee_is_nan(arr%min%val) .or. zmin.lt.arr%min%val) then
        arr%min%val = zmin
        arr%min%ix = 0  ! Depends on the offset of the subcube in the cube, and dimensions order
        arr%min%iy = 0  ! Same
        arr%min%ic = 0  ! Same
      endif
      if (ieee_is_nan(arr%max%val) .or. zmax.gt.arr%max%val) then
        arr%max%val = zmax
        arr%max%ix = 0  ! Depends on the offset of the subcube in the cube, and dimensions order
        arr%max%iy = 0  ! Same
        arr%max%ic = 0  ! Same
      endif
    endif
    !
    if (nnan.gt.0) then
      arr%n%nan = arr%n%nan+nnan
    endif
  end subroutine cubetools_array_update_subcube_extrema
  !
  subroutine cubetools_array_extrema(nxy,z,zmin,zmax,nmin,nmax,nnan)
    use cubetools_nan
    !---------------------------------------------------------------------
    ! Find extrema of a (sign_k) array.
    ! +/- Infinity are considered as valid extrema in this routine.
    !---------------------------------------------------------------------
    integer(kind=size_length), intent(in)    :: nxy    ! Number of values
    real(kind=sign_k),         intent(in)    :: z(nxy) ! Values
    real(kind=sign_k),         intent(inout) :: zmin   ! Unchanged if no valid value
    real(kind=sign_k),         intent(inout) :: zmax   ! Unchanged if no valid value
    integer(kind=size_length), intent(out)   :: nmin   ! Set to 0 if no valid value
    integer(kind=size_length), intent(out)   :: nmax   ! Set to 0 if no valid value
    integer(kind=size_length), intent(out)   :: nnan   ! Number of NaN
    !
    integer(kind=size_length) :: i,j
    character(len=*), parameter :: rname='ARRAY>EXTREMA'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    j = nxy+1
    nmin = 0
    nmax = 0
    nnan = 0
    !
    do i=1,nxy
      if (ieee_is_nan(z(i))) then
        nnan = nnan+1
        cycle
      endif
      zmin = z(i)
      zmax = z(i)
      nmin = i
      nmax = i
      j = i
      exit
    enddo
    !
    do i=j,nxy
      if (ieee_is_nan(z(i))) then
        nnan = nnan+1
        cycle
      endif
      if (z(i).lt.zmin) then
        zmin = z(i)
        nmin = i
      elseif (z(i).gt.zmax) then
        zmax = z(i)
        nmax = i
      endif
    enddo
  end subroutine cubetools_array_extrema
end module cubetools_header_array_types
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
