subroutine sic_math_logi (chain,nch,logi,error)
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_math_logi
  use gildas_def
  use sic_expressions
  use sic_types
  use gbl_format
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  ! Decode a string as a logical argument :
  !  Attempt to read it
  !  Find if it is a variable
  !  Compute logical expressions.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: chain  !
  integer(kind=4),  intent(in)    :: nch    !
  logical,          intent(out)   :: logi   !
  logical,          intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='LOGICAL'
  logical, save :: value  ! Subject to a locwrd()
  integer :: max_level,last_node,type,min_level,tree(formula_length*2)
  type(sic_descriptor_t) :: operand(0:maxoper),result
  character(len=256) :: expres
  integer :: nex, ia, ib, ic
  ! Data
  ! data result/9*0/
!!  Print *,'SIC_MATH_LOGI '//trim(chain)
  !
  if (nch.le.0) then
    call sic_message(seve%e,rname,'String is empty')
    error = .true.
    return
  endif
  !
  call sic_add_expr(chain,nch,expres,nex,error)
  if (error) return
  !
  call sic_get_logi (expres(1:nex),value,error)
  if (.not.error) then
    logi = value
    return
  endif
  !
  ! This works for Logical Expressions, but this code is used in
  !   LET LOGI FILE(SomeThing) 
  !   SAY FILE(SomeThing) /FORMAT L
  ! but not in the implicit formatting like
  !   SAY 'FILE(SomeThing)'
  ! which goes through a different parsing code
  ia = index(expres,'FILE(') 
  if (ia.ne.0) then
    ! We have a FILE function, convert back to initial case
    ib = ia+5
    ic = index(expres(ib:),')')
    if (ic.gt.0) expres(ib:ib+ic) = chain(ib:ib+ic)
  endif
  !
  ! Analyse logical expression
  error = .false.
  call build_tree(expres,nex,operand,tree,last_node,max_level,min_level,error)
  if (error) then
    call sic_message(seve%e,rname,'Invalid logical expression '//expres(1:nex))
    return
  endif
  !
  result%type = fmt_l
  result%readonly = .false.
  result%addr = locwrd (value)
  result%ndim = 0
  result%size = 1
  call evaluate_tree(operand,tree,last_node,max_level,min_level,result,type,  &
    error)
  if (error) then
    logi = .false.
    call sic_message(seve%e,rname,'Error evaluating '//expres(1:nex))
  else
    logi = value
  endif
end subroutine sic_math_logi
!
subroutine sic_math_long (chain,nch,argum,error)
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_math_long
  use gildas_def
  use sic_expressions
  use sic_types
  use gbl_format
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  ! Decode a string as a long integer :
  !  Attempt to read it
  !  Find if it is a variable
  !  Compute arithmetic expressions.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: chain  !
  integer(kind=4),  intent(in)    :: nch    !
  integer(kind=8),  intent(out)   :: argum  !
  logical,          intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='DECODE'
  integer :: max_level,last_node,type,ifirst,min_level,i
  type(sic_descriptor_t) :: operand(0:maxoper),result
  integer :: tree(formula_length*2)
  integer(kind=8) :: value
  character(len=256) :: expres
  integer :: nex, ier
  ! Data
  save value                   ! Subject to a LOCWRD
  save result,operand,last_node,tree
  ! data result/9*0/
  !
  if (nch.le.0) then
    call sic_message(seve%e,rname,'String is empty')
    error = .true.
    return
  endif
  !
  call sic_add_expr(chain,nch,expres,nex,error)
  if (error) return
  !
  ! Protect against -D01 being interpreted as -0.000D+01
  if (expres(1:1).eq.'+' .or. expres(1:1).eq.'-') then
    ifirst = 2
  else
    ifirst = 1
  endif
  !
  ! This test is a protection against the stupid FORTRAN convention of 1+1=1E+1
  do i = ifirst,nex
    if ( (expres(i:i).lt.'0').or.(expres(i:i).gt.'9') ) goto 10
  enddo
  ! Ask the Fortran runtime to read a literal value. Do not use format '*'
  ! because the Intel Fortran Compiler reads the value '1.9' with no error,
  ! and as integer value '1' (and not '2')
  read (expres(1:nex),'(I20)',iostat=ier) value
  if (ier.ne.0) goto 10
  argum = value
  return
  !
  ! Try a variable
10 call sic_get_long (expres(1:nex),argum,error)
  if (.not.error) return
  !
  ! Analyse arithmetic expression
  ! print *,'Calling BUILD TREE'
  call build_tree(expres,nex,operand,tree,last_node,max_level,min_level,error)
  if (error) then
    call sic_message(seve%e,rname,'Invalid arithmetic expression '//  &
    expres(1:nex))
    return
  endif
  !
  result%type = fmt_i8
  result%readonly = .false.
  result%addr = locwrd (value)
  result%ndim = 0
  result%size = 2
  call evaluate_tree(operand,tree,last_node,max_level,min_level,result,type,  &
    error)
  if (error) then
    call sic_message(seve%e,rname,'Error computing '//expres(1:nex))
  else
    argum = value
  endif
end subroutine sic_math_long
!
subroutine sic_math_inte (chain,nch,argum,error)
  use gbl_message
  use sic_interfaces, except_this=>sic_math_inte
  !---------------------------------------------------------------------
  ! @ public
  ! Decode a string as a normal integer :
  !  Attempt to read it
  !  Find if it is a variable
  !  Compute arithmetic expressions.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: chain  !
  integer(kind=4),  intent(in)    :: nch    !
  integer(kind=4),  intent(out)   :: argum  !
  logical,          intent(inout) :: error  !
  ! Local
  integer(kind=8), parameter :: mini4=-2147483648_8  ! -2**31
  integer(kind=8), parameter :: maxi4=2147483647_8  ! 2**31-1
  integer(kind=8) :: i8
  character(len=message_length) :: mess
  !
  call sic_math_long(chain,nch,i8,error)
  if (error)  return
  !
  if (i8.lt.mini4 .or. i8.gt.maxi4) then
    write(mess,'(A,I0,A)')  &
      'Value ',i8,' is too large to accomodate in an INTEGER*4'
    call sic_message(seve%e,'MATH',mess)
    error = .true.
    return
  endif
  argum = int(i8,kind=4)
  !
end subroutine sic_math_inte
!
subroutine sic_math_dble (chain,nch,argum,error)
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_math_dble
  use gildas_def
  use sic_expressions
  use sic_types
  use gbl_format
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  ! Decode a string as a double precision float :
  !  Attempt to read it
  !  Find if it is a variable
  ! Compute arithmetic expressions.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: chain  !
  integer(kind=4),  intent(in)    :: nch    !
  real(kind=8),     intent(out)   :: argum  !
  logical,          intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='DECODE'
  integer :: max_level,last_node,type,ifirst,min_level,i
  type(sic_descriptor_t) :: operand(0:maxoper),result
  integer :: tree(formula_length*2)
  real(8) :: value
  character(len=256) :: expres
  integer :: nex, ier
  ! Data
  save value                   ! Subject to a LOCWRD
  save result,operand,last_node,tree
  ! data result/9*0/
  !
  if (nch.le.0) then
    call sic_message(seve%e,rname,'String is empty')
    error = .true.
    return
  endif
  !
  call sic_add_expr(chain,nch,expres,nex,error)
  if (error) return
  !
  ! Protect against -D01 being interpreted as -0.000D+01
  if (expres(1:1).eq.'+' .or. expres(1:1).eq.'-') then
    ifirst = 2
  else
    ifirst = 1
  endif
  !
  ! This test is a protection against the stupid FORTRAN convention of 1+1=1E+1
  do i = ifirst,nex
    if ( (expres(i:i).lt.'0').or.(expres(i:i).gt.'9') ) goto 10
  enddo
  !
  ! The Iostat code encompasses the EOF case
  ! The end=10 is to prevent against the string being "-" or "+"
  ! otherwise, the program crashes by "End of file" in these cases.
  read (expres(1:nex),*,iostat=ier) value
  if (ier.ne.0) goto 10
  argum = value
  return
  !
  ! Try a variable
10 call sic_get_dble (expres(1:nex),argum,error)
  if (.not.error) return
  !
  ! Analyse arithmetic expression
  ! print *,'Calling BUILD TREE'
  call build_tree(expres,nex,operand,tree,last_node,max_level,min_level,error)
  if (error) then
    call sic_message(seve%e,rname,'Invalid arithmetic expression '//  &
    expres(1:nex))
    return
  endif
  !
  result%type = fmt_r8
  result%readonly = .false.
  result%addr = locwrd (value)
  result%ndim = 0
  result%size = 2
  call evaluate_tree(operand,tree,last_node,max_level,min_level,result,type,  &
    error)
  if (error) then
    call sic_message(seve%e,rname,'Error computing '//expres(1:nex))
  else
    argum = value
  endif
end subroutine sic_math_dble
!
subroutine sic_math_real (chain,nch,argum,error)
  use sic_interfaces, except_this=>sic_math_real
  !---------------------------------------------------------------------
  ! @ public
  ! Decode a string as a single precision float :
  !  Attempt to read it
  !  Find if it is a variable
  ! Compute arithmetic expressions.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: chain  !
  integer(kind=4),  intent(in)    :: nch    !
  real(kind=4),     intent(out)   :: argum  !
  logical,          intent(inout) :: error  !
  ! Local
  real(kind=8) :: r8
  !
  call sic_math_dble(chain,nch,r8,error)
  if (error)  return
  !
  argum = real(r8,kind=4)
  !
end subroutine sic_math_real
!
subroutine sic_math (chain,nch,argum,error)
  use sic_interfaces, except_this=>sic_math
  !---------------------------------------------------------------------
  ! @ public obsolete
  !  Use one of the following routines to evaluate a Sic expression
  ! in a variable:
  !   sic_math_logi
  !   sic_math_inte
  !   sic_math_long
  !   sic_math_real
  !   sic_math_dble
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: chain  !
  integer(kind=4),  intent(in)    :: nch    !
  real(kind=8),     intent(out)   :: argum  !
  logical,          intent(inout) :: error  !
  !
  call sic_math_dble(chain,nch,argum,error)
  !
end subroutine sic_math
!
subroutine sic_math_auto (chain,nch,argum,ocode,error)
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_math_auto
  use gildas_def
  use sic_expressions
  use sic_types
  use gbl_format
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  !  Decode a string as a double precision OR LOGICAL argument :
  !   Attempt to read it
  !   Find if it is a variable
  !   Compute arithmetic expressions.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: chain  !
  integer(kind=4),  intent(in)    :: nch    !
  real(kind=8),     intent(out)   :: argum  ! 2 words buffer for output result
  integer(kind=4),  intent(out)   :: ocode  ! Format of output result
  logical,          intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='DECODE'
  integer :: max_level,last_node,type,ifirst,min_level,i
  type(sic_descriptor_t) :: operand(0:maxoper),result
  real(8) :: value
  character(len=message_length) :: cbuf
  character(len=256) :: expres
  integer :: nex,ier,tree(formula_length*2)
  ! Data
  save value                   ! Subject to a LOCWRD
  save result,operand,last_node,tree
  ! data result/9*0/
  !
  ocode = 0
  if (nch.le.0) return
  !
  call sic_add_expr(chain,nch,expres,nex,error)
  if (error) return
  !
  ! Protect against -D01 being interpreted as -0.000D+01
  ifirst = 1
  if (expres(ifirst:ifirst).eq.'+' .or. expres(ifirst:ifirst).eq.'-') then
    ifirst = 2
  else
    ifirst = 1
  endif
  !
  ! This test is a protection against the stupid FORTRAN convention of 1+1=1E+1
  do i = ifirst,nex
    if ( (expres(i:i).lt.'0').or.(expres(i:i).gt.'9') ) goto 10
  enddo
  read (expres(1:nex),*,iostat=ier) value
  if (ier.ne.0) goto 10
  argum = value
  ocode = fmt_r8
  return
  !
  ! Try a variable
10 continue
  call sic_get_auto (expres(1:nex),argum,ocode,error)
  if (.not.error)  return
  !
  ! Analyse arithmetic expression
  error = .false.
  call build_tree(expres,nex,operand,tree,last_node,max_level,min_level,error)
  if (error) then
    ocode = fmt_un
    call sic_message(seve%e,rname,'Invalid arithmetic expression '//  &
    expres(1:nex))
    return
  endif
  !
  ! We set RESULT%type to FMT_UN (Unknown) so that the result
  ! can be REAL*8 OR Logical, to be decided by EVALUATE_TREE
  !
  result%type = fmt_un
  result%readonly = .false.
  result%addr = locwrd (value)
  result%ndim = 0
  result%size = 2
  call evaluate_tree(operand,tree,last_node,max_level,min_level,result,type,  &
    error)
  if (error) then
    ocode = fmt_un
    call sic_message(seve%e,rname,'Error computing '//expres(1:nex))
    return
  endif
  !
  if (result%type.eq.fmt_l) then
    ocode = fmt_l
    call l4tol4(value,argum,2)
  elseif (result%type.eq.fmt_r4) then
    ocode = fmt_r4
    call r4tor4(value,argum,1)
  elseif (result%type.eq.fmt_i4) then
    ocode = fmt_i8
    call i4toi8(value,argum,1)
  elseif (result%type.eq.fmt_r8) then
    ocode = fmt_r8
    argum = value
  elseif (result%type.eq.fmt_i8) then
    ocode = fmt_i8
    call i8toi8(value,argum,1)
  else
    write(cbuf,'(A,I6)') 'Unsupported type ',result%type
    call sic_message(seve%w,rname,cbuf)
    ocode = 1
    argum = value
  endif
  !
end subroutine sic_math_auto
!
subroutine sic_math_desc(chain,nch,ofmt,osize,desc,error)
  use gbl_format
  use gbl_message
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_math_desc
  use sic_dictionaries
  use sic_expressions
  !---------------------------------------------------------------------
  ! @ private
  !  Evaluate an expression and return the descriptor of the result. The
  ! result can be an array.
  ! The result can be a scratch variable, it is the responsibility of
  ! the caller to free it afterwards.
  ! ---
  !  This subroutine is a factorization of LET /WHERE XXX (parsing of
  ! the 'XXX' vectorized condition). It may need modification to make
  ! it fully generic.
  !---------------------------------------------------------------------
  character(len=*),          intent(in)           :: chain  !
  integer(kind=4),           intent(in)           :: nch    !
  integer(kind=4),           intent(in)           :: ofmt   ! Output result format
  integer(kind=size_length), intent(in), optional :: osize  ! Output size
  type(sic_descriptor_t),    intent(out)          :: desc   !
  logical,                   intent(inout)        :: error  !
  ! Local
  character(len=*), parameter :: rname='SIC'
  integer(kind=4) :: ier,ilen,itype,last_node,max_level,min_level
  integer(kind=4) :: tree(formula_length*2),ioper
  character(len=1024) :: expres
  type(sic_descriptor_t) :: operand(0:maxoper)
  integer(kind=size_length) :: esize
  character(len=message_length) :: mess
  !
  ! Analyze the expression
  call sic_add_expr(chain,nch,expres,ilen,error)
  if (error)  return
  call build_tree(expres(1:ilen),ilen,operand,tree,last_node,max_level,  &
    min_level,error)
  if (error)  return  ! Let the caller issue a relevant message
  !
  ! Operands are now parsed. Need to prepare the descriptor for the result.
  ! What is the size of the expression? Assume it has the largest size of all
  ! involved operands... Is this correct? Note that this is basically
  ! pre-guessing what size evaluate_tree needs... Could this be automatic?
  ! Assume there is only one node in the expression:
  esize = 1
  do ioper=1,tree(5)  ! tree(5) is the number of operands of the first node
                      ! tree(6) is the first operand,
                      ! tree(7) is the second operand, etc
    esize = max( esize, desc_nelem(operand(tree(5+ioper))) )
  enddo
  if (present(osize)) then
    if (esize.eq.osize) then
      ! Input operand has requested size. The resulting descriptor will have
      ! same size as the expression.
      desc%ndim = 1
      desc%dims(:) = esize
    elseif (esize.eq.1 .and. osize.gt.1) then
      ! The expression is scalar but osize is larger. The resulting descriptor
      ! will have size 'osize'. Sic will duplicate the scalar expression evaluation
      ! as many times as needed in the result.
      desc%ndim = 1
      desc%dims(1) = osize
    else
      write(mess,'(A,2(1X,I0))')  &
        'Mathematics on arrays of inconsistent dimensions',osize,esize
      call sic_message(seve%e,rname,mess)
      error = .true.
      return
    endif
  else
    ! Output size not constrained, the resulting descriptor will have same
    ! size as the expression
    desc%ndim = 1
    desc%dims(:) = esize
  endif
  !
  desc%type = ofmt
  desc%status = scratch_operand
  desc%readonly = .false.
  desc%size = desc_nelem(desc)
  if (desc%type.eq.fmt_r8 .or. desc%type.eq.fmt_i8 .or. desc%type.eq.fmt_c4) then
    desc%size = 2*desc%size
  endif
  ier = sic_getvm(desc%size,desc%addr)
  if (ier.ne.1) then
    call sic_message(seve%e,rname,'Memory allocation failure')
    error = .true.
    return
  endif
  !
  ! Then compute it...
  call evaluate_tree(operand,tree,last_node,max_level,min_level,  &
     desc,itype,error)
  if (error) then
    call sic_volatile(desc)
    return
  endif
  !
  ! If an error is returned, the caller has nothing to free.
  !
end subroutine sic_math_desc
!
subroutine sic_sexa(line,nline,value,error)
  use sic_interfaces, except_this=>sic_sexa
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  !  Decode a generalized sexagesimal notation
  !  Expression_1[:Expression_2[:Expression_3]]
  !  The unit of Expression_1 (Degrees or hours) is used for the result.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line   !
  integer(kind=4),  intent(in)    :: nline  !
  real(kind=8),     intent(out)   :: value  !
  logical,          intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='SEXA'
  real(8) :: value1,sign
  integer :: nf,nl,nc,ic
  character(len=64) :: chain
  !
  ! First, get the real string to be decoded:
  nl = index(line(1:nline),':')
  if (nl.eq.0) then
    ! No column, try a character variable
    call sic_get_char(line(1:nline),chain,nc,error)
    if (error) then
      ! Not a character variable
      error = .false.
      chain = line(1:nline)
      nc = nline
    else
      ! A character variable as been read
      nl = index(chain(1:nc),':')
    endif
    if (nl.eq.0) then
      ! No column, just try a numeric variable, a plain numeric value, a formulae...
      call sic_math_dble(chain,nc,value,error)
      return
    endif
  else
    chain = line(1:nline)
    nc = nline
  endif
  !
  ! Then, start decoding
  if (nl.eq.1 .or. nl.eq.nc) then
    call sic_message(seve%e,rname,'Syntax error')
    error = .true.
    return
  endif
  !
  ! Decode HOURS/DEGREES
  call sic_math_dble(chain,nl-1,value,error)
  if (error) return
  if (value.gt.0d0) then
    sign = 1.0d0
  elseif (value.lt.0d0) then
    sign = -1.0d0
    value = -value
  else
    ! Value is 0. Need to keep track of sign, if any.
    ! Ignore leading blank:
    ic = 1
    do while (chain(ic:ic).eq.' ')
      ic = ic+1
    enddo
    if (chain(ic:ic).eq.'-') then
      sign = -1.0d0
    else
      sign = 1.0d0
    endif
  endif
  !
  ! Decode MINUTES
  nf = nl+1
  nl = index(chain(nf:nc),':')
  if (nl.eq.1 .or. nl+nf-1.eq.nc) then
    call sic_message(seve%e,rname,'Syntax error')
    error = .true.
  endif
  if (nl.eq.0) then
    call sic_math_dble(chain(nf:),nc-nf+1,value1,error)
  else
    call sic_math_dble(chain(nf:),nl-1,value1,error)
  endif
  if (error) return
  if (value1.lt.0.d0 .or. value1.ge.60.d0) then
    call sic_message(seve%e,rname,'Invalid minute field in '//chain)
    error = .true.
    return
  endif
  value = value + value1/60.d0
  if (nl.eq.0) then
    ! No SECONDS field: return now
    value = sign * value
    return
  endif
  !
  ! Decode SECONDS
  nf = nf+nl
  call sic_math_dble(chain(nf:),nc-nf+1,value1,error)
  if (error) return
  if (value1.lt.0.d0 .or. value1.ge.60.d0) then
    call sic_message(seve%e,rname,'Invalid second field in '//chain)
    error = .true.
    return
  endif
  value = sign * (value + value1/3600.d0)
  !
end subroutine sic_sexa
!
subroutine sic_vtype(namein,vtype,reado,error)
  use sic_interfaces, except_this=>sic_vtype
  use gildas_def
  use sic_structures
  use sic_dictionaries
  use sic_interactions
  use sic_types
  use gbl_message
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  character(len=*) :: namein        !
  integer :: vtype                  !
  logical :: reado                  !
  logical :: error                  !
  ! Global
  include 'gbl_memory.inc'
  ! Local
  type(sic_identifier_t) :: var
  integer(kind=4) :: i,ier,in
  type(sic_dimensions_t) :: spec
  integer(kind=4) :: impvars(sic_maxdims)
  !
  ! Parse dimensions
  error = .false.
  spec%do%strict   = .true.   ! Can not be an expression like A[1].eq.B[1]
  spec%do%range    = .true.
  spec%do%subset   = .true.   ! A[2,] allowed, like in LET i[,1] 1 2 /PROMPT "Text"
  spec%do%implicit = .true.
  spec%do%twospec  = .false.  ! e.g. A[i][j:k] forbidden
  call sic_parse_var(namein,var,spec,impvars,error)
  if (error) return
  !
  ! Search first in local table
  var%level = var_level
  ier = sic_hasfin(maxvar,pfvar,pnvar,dicvar,var,in)
  if (ier.ne.1 .and. var_level.ne.0) then
    var%level = 0
    ier = sic_hasfin(maxvar,pfvar,pnvar,dicvar,var,in)
  endif
  if (ier.ne.1) then
    ! CALL GAGOUT('E-LET,  No such variable '//NAMEIN)
    error =.true.
    return
  endif
  !
  ! Check number of dimensions
  if (spec%done(1)%ndim.gt.dicvar(in)%desc%ndim) then
    ! This test is not correct for substring specification. Should use
    ! extract_descr (via sic_descriptor?) which does the correct tests, and
    ! get rid of the sic_vtype subroutine.
    call sic_message(seve%e,'LET','Too many dimensions for variable '//  &
    namein)
    error = .true.
    return
  endif
  !
  ! Check whether it is readonly
  vtype = dicvar(in)%desc%type
  reado = dicvar(in)%desc%readonly
  !
  if (spec%done(1)%implicit) then
    ! Clean implicit variables from dictionary
    do i=1,spec%done(1)%ndim
      if (impvars(i).eq.0)  cycle  ! No variable defined (unnamed dimension)
      in = impvars(i)
      call sic_zapvariable(in,.true.,.true.,error)
    enddo
  endif
end subroutine sic_vtype
