subroutine gdf_transpose (nameou,namein,code,space_gildas,error)
  use gsys_types
  use gio_dependencies_interfaces
  use gio_interfaces, except_this=>gdf_transpose
  use image_def
  use gbl_message
  use gio_params
  use gio_fitsdef
  !---------------------------------------------------------------------
  ! @ public
  ! GDF API routine
  !  Transpose a GILDAS image
  !  Disk version when needed
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: nameou        ! Output transposed file name
  character(len=*), intent(in)    :: namein        ! Input file name
  character(len=*), intent(inout) :: code          ! Transposition code
  integer(kind=4),  intent(in)    :: space_gildas  ! Useable memory size
  logical,          intent(inout) :: error         ! Error flag
  ! Local
  character(len=*), parameter :: rname='TRANSPOSE'
  type(gildas) :: in,ou
  integer(kind=index_length) :: nfirst,nsecon,nmiddl,iblock(5)
  logical :: isfits
  !
  call gildas_null (in)
  in%file = namein
  call sic_parse_file(namein,' ','.gdf',in%file)
  !
  call gdf_read_gdforfits_header(in%file,isfits,in,fd,error)
  if (error) return
  !
  call gdf_toobig(in,error)
  if (error)  goto 100
  !
  call gdf_transpose_header(in,ou,code,error)
  if (error) goto 100          ! Free the IN image...
  ou%file = nameou
  call sic_parse_file(nameou,' ','.gdf',ou%file)
  !
  ! Determine chunk sizes from code and dimensions.
  call transpose_getblock(in%gil%dim,gdf_maxdims,code,iblock,error)
  if (error) goto 100
  nfirst = iblock(2)
  nmiddl = iblock(3)
  nsecon = iblock(4)
  !
  if ((nfirst*nmiddl.eq.1 .or. nmiddl*nsecon.eq.1) .and. .not.isfits) then
    ! Degenerate transposition:
    ! Only the header is transposed in this case
    if (gag_filcopy(in%file,ou%file).ne.0) then
      call gio_message(seve%e,rname,  &
        'Failed to copy '//trim(in%file)//' into '//trim(ou%file))
      error = .true.
      return
    endif
    !
    ! Open the output file in Write mode, and update the header
    call gdf_open_image(ou,error)
    call gdf_update_header(ou,error)
    call gdf_close_image(ou,error)
    !
  elseif (gio_word_length(in).eq.4) then
    call gdf_transpose4(in,ou,code,iblock,space_gildas,isfits,error)
    !
  else
    call gio_message(seve%e,rname,'Format not yet supported')
    error = .true.
  endif
  if (error) goto 100
  !
100 continue
  if (.not.isfits)  call gdf_close_image(in,error)
  return
end subroutine gdf_transpose
!
subroutine gdf_transpose4(in,ou,code,iblock,space_gildas,isfits,error)
  use image_def
  use gbl_message
  use gsys_types
  use gio_dependencies_interfaces
  use gio_interfaces, except_this=>gdf_transpose4
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(gildas),               intent(inout) :: in            !
  type(gildas),               intent(inout) :: ou            !
  character(len=*),           intent(in)    :: code          !
  integer(kind=index_length), intent(in)    :: iblock(5)     !
  integer(kind=4),            intent(in)    :: space_gildas  ! Useable memory size (MB)
  logical,                    intent(in)    :: isfits        !
  logical,                    intent(inout) :: error         !
  ! Local
  character(len=*), parameter :: rname='TRANSPOSE'
  integer(kind=index_length) :: dimo(gdf_maxdims)
  integer(kind=index_length) :: nfirst,nsecon,nlast,nmiddl,nelems
  integer(kind=size_length) :: size
  real(kind=4) :: ratio
  !
  ! Create the image
  call gdf_create_image(ou,error)
  if (error)  return
  !
  dimo(:) = ou%gil%dim(:)  ! Save the dimensions
  !
  nelems = iblock(1)
  nfirst = iblock(2)
  nmiddl = iblock(3)
  nsecon = iblock(4)
  nlast  = iblock(5)
  !
  ! Remap the IN% and OU% headers to a 5-D array shape
  in%gil%ndim = 5
  in%gil%dim(1:5) = (/nelems,nfirst,nmiddl,nsecon,nlast/)
  ou%gil%ndim = 5
  ou%gil%dim(1:5) = (/nelems,nsecon,nmiddl,nfirst,nlast/)
  !
  ! This is a real transposition of Real*4 arrays
  size = nelems*nfirst*nmiddl*nsecon    ! Words
  ! Number of blocks required to read the 2 tables...
  ratio = 8.*size/space_gildas/1024./1024.
  !
  if (ratio.gt.1.) then
    ! The first 4 dimensions of the input and output tables do not
    ! fit in SPACE_GILDAS.
    call gdf_transpose4_disk(in,ou,iblock,space_gildas,isfits,error)
  else
    call gdf_transpose4_memory(in,ou,iblock,isfits,error)
  endif
  if (error)  goto 200
  !
  ! Restore the dimensions...
  ou%gil%dim(:) = dimo(:)
  !
  if (code.eq.'21' .and. (abs(ou%gil%type_gdf).eq.code_gdf_uvt)) then
    ! Treat the special case of UV Tables with Real*8 Data
    call gdf_transpose_uvcolumn8(ou,error)
  endif
  !
200 continue
  call gdf_close_image(ou,error)
  !
end subroutine gdf_transpose4
!
subroutine gdf_transpose4_disk(in,ou,iblock,space_gildas,isfits,error)
  use image_def
  use gbl_message
  use gsys_types
  use gio_dependencies_interfaces
  use gio_interfaces, except_this=>gdf_transpose4_disk
  use gio_fitsdef
  !---------------------------------------------------------------------
  ! @ private
  ! Perform the transposition on disk
  !---------------------------------------------------------------------
  type(gildas),               intent(inout) :: in            !
  type(gildas),               intent(inout) :: ou            !
  integer(kind=index_length), intent(in)    :: iblock(5)     !
  integer(kind=4),            intent(in)    :: space_gildas  ! Useable memory (MB)
  logical,                    intent(in)    :: isfits        !
  logical,                    intent(inout) :: error         !
  ! Local
  character(len=*), parameter :: rname='TRANSPOSE'
  real(kind=4), allocatable :: din(:,:,:,:), dou(:,:,:,:)
  integer(kind=index_length) :: ilast, instep, oustep, lin, kin, lou, kou
  integer(kind=index_length) :: nfirst,nsecon,nlast,nmiddl,nelems
  integer(kind=4) :: ier,nloop,nslicein,nsliceou
  real(kind=4) :: nblock,fracin,fracou
  integer(kind=index_length), parameter :: zero=0
  type(time_t) :: time
  character(len=message_length) :: mess
  integer(kind=size_length) :: ndata,nblank
  integer(kind=record_length) :: datarec
  !
  call gio_message(seve%w,rname,'Arrays do not fit in memory, using disk')
  !
  nelems = iblock(1)
  nfirst = iblock(2)
  nmiddl = iblock(3)
  nsecon = iblock(4)
  nlast  = iblock(5)
  !
  ! Which fraction of SPACE_GILDAS for reading/writing IN and OU?
  ! Big question: do we divide the Input and Output evenly or not ?
  ! Answer: no. It is faster to traverse the input file a few times
  ! in small contiguous slices, rather than many times in large
  ! contiguous slices (see debug message below). So we allocate more
  ! memory to the output file, which means less contiguous output
  ! slices, which means traversing theinput file less times.
  fracin = .25
  fracou = .75
  write(mess,'(3(A,F0.2))')  'Input buffer: ',space_gildas*fracin,  &
    ' MB, output buffer: ',space_gildas*fracou,' MB'
  call gio_message(seve%d,rname,mess)
  !
  ! Compute the block size
  nblock = 4.*nelems*nfirst*nmiddl*nsecon/(space_gildas*1024.*1024.)  ! Float computations!
  instep = min(nsecon,ceiling(nsecon/nblock*fracin))
  oustep = min(nfirst,ceiling(nfirst/nblock*fracou))
  !
  allocate(din(nelems,nfirst,nmiddl,instep),  &
           dou(nelems,nsecon,nmiddl,oustep),stat=ier)
  if (ier.ne.0) then
    call gio_message(seve%e,rname,'Memory allocation failure')
    error = .true.
    goto 200
  endif
  !
  nsliceou = 1+(nfirst-1)/oustep
  nslicein = 1+(nsecon-1)/instep
  write(mess,'(4(A,I0))')  'Writing output file in ',nsliceou,  &
    ' slices, reading input ',nsliceou,' times in ',nslicein,' slices'
  call gio_message(seve%d,rname,mess)
  !
  ! Keep memory of data starting position
  if (isfits)  call gfits_getrecnum(datarec)
  !
  ! Computing time statistics
  nloop = nlast*nsliceou*nslicein
  call gtime_init(time,nloop,error)
  if (error)  return
  !
  do ilast=1,nlast
    ! Loop on the 5th dimension, i.e. reduce the problem to 4D below:
    do kou=1,nfirst,oustep
      ! Select a slice of 'ou' in the 4th dimension [kou:kou+oustep[
      ou%blc(1:5) = (/zero,zero,zero,kou,ilast/)
      lou = min(kou+oustep-1,nfirst)
      ou%trc(1:5) = (/zero,zero,zero,lou,ilast/)
      if (isfits) then
        ! Make sure the FITS file is at the right position each time we
        ! restart reading from start
        call gfits_setrecnum(datarec)
        call gfits_flush_data(error)
        if (error)  goto 200
      endif
      do kin=1,nsecon,instep
        ! We have to read as many 'in' slices as needed to fill the 'ou'
        ! slice.
        in%blc(1:5) = (/zero,zero,zero,kin,ilast/)
        lin = min(kin+instep-1,nsecon)
        in%trc(1:5) = (/zero,zero,zero,lin,ilast/)
        ! !!Print *,'BLC ',in%blc
        ! !!Print *,'TRC ',in%trc
        if (isfits) then
          ndata = product(in%gil%dim(1:3))*(lin-kin+1)
          call gfits_getreal(fd,ndata,din,fd%bscal,fd%bzero,error)
          if (error) goto 200
          call fitscube2gdf_patch_bval(fd,in,din,ndata,nblank,error)
          if (error) goto 200
        else
          call gdf_read_data(in,din,error)
          if (error) goto 200
        endif
        call trans4slice(nelems,nfirst,nmiddl,nsecon,din,kin,lin,dou,kou,lou)
        call gtime_current(time)
      enddo
      call gdf_write_data(ou,dou,error)
      if (error) goto 200
    enddo
  enddo
  !
200 continue
  deallocate(din,dou)
  !
end subroutine gdf_transpose4_disk
!
subroutine gdf_transpose4_memory(in,ou,iblock,isfits,error)
  use image_def
  use gbl_message
  use gsys_types
  use gio_dependencies_interfaces
  use gio_interfaces, except_this=>gdf_transpose4_memory
  use gio_fitsdef
  !---------------------------------------------------------------------
  ! @ private
  ! Problem does fit in memory. Loop on the last dimension
  !---------------------------------------------------------------------
  type(gildas),               intent(inout) :: in         !
  type(gildas),               intent(inout) :: ou         !
  integer(kind=index_length), intent(in)    :: iblock(5)  !
  logical,                    intent(in)    :: isfits     !
  logical,                    intent(inout) :: error      !
  ! Local
  character(len=*), parameter :: rname='TRANSPOSE'
  real(kind=4), allocatable :: din(:,:,:,:), dou(:,:,:,:)
  integer(kind=index_length) :: i
  integer(kind=index_length) :: nfirst,nsecon,nlast,nmiddl,nelems
  integer(kind=4) :: ier
  integer(kind=index_length), parameter :: zero=0
  type(time_t) :: time
  integer(kind=size_length) :: ndata,nblank
  !
  nelems = iblock(1)
  nfirst = iblock(2)
  nmiddl = iblock(3)
  nsecon = iblock(4)
  nlast  = iblock(5)
  !
  allocate(din(nelems,nfirst,nmiddl,nsecon),  &
           dou(nelems,nsecon,nmiddl,nfirst),stat=ier)
  if (ier.ne.0) then
    call gio_message(seve%e,rname,'Memory allocation failure')
    error = .true.
    goto 200
  endif
  !
  ! Computing time statistics
  call gtime_init(time,int(nlast,kind=4),error)
  if (error)  return
  !
  ndata = product(in%gil%dim(1:4))  ! Total number of elements per slice
  do i=1,nlast
    in%blc(1:5) = (/zero,zero,zero,zero,i/)
    in%trc(1:5) = (/zero,zero,zero,zero,i/)
    ou%blc = in%blc
    ou%trc = in%trc
    if (isfits) then
      call gfits_getreal(fd,ndata,din,fd%bscal,fd%bzero,error)
      if (error) goto 200
      call fitscube2gdf_patch_bval(fd,in,din,ndata,nblank,error)
      if (error) goto 200
    else
      call gdf_read_data(in,din,error)
      if (error) goto 200
    endif
    call trans4(din,dou,nelems,nfirst,nmiddl,nsecon)
    call gdf_write_data(ou,dou,error)
    if (error) goto 200
    call gtime_current(time)
  enddo
  !
200 continue
  deallocate(din,dou)
  !
end subroutine gdf_transpose4_memory
!
subroutine gdf_transpose_uvcolumn8(ou,error)
  use image_def
  use gbl_message
  use gio_interfaces, except_this=>gdf_transpose_uvcolumn8
  !---------------------------------------------------------------------
  ! @ private
  !  Transpose the REAL*8 (2 words) columns which have been misordered
  ! by a previous transposition.
  !  GDF UV tables specific, because only GDF UV tables have data
  ! columns spread over several columns.
  !---------------------------------------------------------------------
  type(gildas), intent(inout) :: ou     !
  logical,      intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='TRANSPOSE'
  integer(kind=4) :: iaxis,ier,i
  real(kind=4), allocatable :: tin(:,:), tou(:,:)
  !
  if (any(ou%gil%column_size.eq.2)) then 
    call gio_message(seve%w,rname,'UV Table with Real*8 columns')
    ou%blc = 0
    ou%trc = 0
    if (ou%gil%type_gdf.eq.code_gdf_tuv) then
      iaxis = 2
      allocate(tou(2,ou%gil%dim(1)), tin(ou%gil%dim(1),2), stat=ier)
    else
      iaxis = 1
      allocate(tin(2,ou%gil%dim(1)), tou(ou%gil%dim(1),2), stat=ier)
    endif
    if (ier.ne.0) then
      call gio_message(seve%w,rname,'Memory allocation error')
      error = .true.
      return
    endif
    do i=1,code_uvt_last
      if (ou%gil%column_size(i).eq.2) then
        ou%blc(iaxis) = ou%gil%column_pointer(i)
        ou%trc(iaxis) = ou%gil%column_pointer(i)+1
        !!Print *,'Reading code ',i,iaxis
        !!Print *,'OU dim ',ou%gil%dim(1:3)
        !!Print *,'BLC ',ou%blc(1:3)
        !!Print *,'TRC ',ou%trc(1:3)
        call gdf_read_data(ou, tin, error)
        !!Print *,'Done Reading  ',ERROR
        !!Print *,'Transposing '
        tou(:,:) = transpose (tin)
        !!Print *,'Done Transpose '
        !!Print *,'OU dim ',ou%gil%dim(1:3)
        !!Print *,'BLC ',ou%blc(1:3)
        !!Print *,'TRC ',ou%trc(1:3)
        call gdf_write_data (ou, tou, error)
        !!Print *,'Done wRITING  ',ERROR
      endif
    enddo
    deallocate (tin, tou, stat=ier)
  endif
  !
end subroutine gdf_transpose_uvcolumn8
!
subroutine gdf_nitems_4(logname,nitems,asize)
  use gio_dependencies_interfaces
  use gio_interfaces, only: gdf_nitems_8
  !---------------------------------------------------------------------
  ! @ public-generic gdf_nitems
  !   Return the number of items of size ASIZE in a block size specified
  !   by LOGNAME (typically SPACE_GILDAS)
  !---------------------------------------------------------------------
  character(len=*), intent(in)  :: logname
  integer(kind=4),  intent(out) :: nitems
  integer(kind=4),  intent(in)  :: asize

  call gdf_nitems_8(logname,nitems,int(asize,kind=8))
end subroutine gdf_nitems_4
!
subroutine gdf_nitems_8(logname,nitems,asize)
  use gio_dependencies_interfaces
  !---------------------------------------------------------------------
  ! @ public-generic gdf_nitems
  !   Return the number of items of size ASIZE in a block size specified
  !   by LOGNAME (typically SPACE_GILDAS)
  !---------------------------------------------------------------------
  character(len=*), intent(in)  :: logname
  integer(kind=4),  intent(out) :: nitems
  integer(kind=8),  intent(in)  :: asize
  ! Local
  real(kind=4) :: rmega
  integer(kind=4) :: ier
  !
  rmega = 128  ! Default if logical is not defined
  ier = sic_ramlog(logname,rmega)
  nitems = (rmega*1024.*1024.)/real(asize)/4.  
end subroutine gdf_nitems_8
