!
!= å…¥å‡ºåŠ›ç¯„å›²ã®æ‹˜æŸ
!
! Authors::   Eizi TOYODA, Yasuhiro MORIKAWA
! Version::   $Id: gtvarlimit.f90,v 1.3 2009-05-25 09:55:57 morikawa Exp $
! Tag Name::  $Name:  $
! Copyright:: Copyright (C) GFD Dennou Club, 2000-2005. All rights reserved.
! License::   See COPYRIGHT[link:../../COPYRIGHT]
!
! ä»¥ä¸‹ã®ã‚µãƒ–ãƒ«ãƒ¼ãƒãƒ³ã€é–¢æ•°ã¯ gtdata_generic ã‹ã‚‰ gtdata_generic#Limit
! ã¨ã—ã¦æä¾›ã•ã‚Œã¾ã™ã€‚

subroutine GTVarLimit_iiii(var, dimord, start, count, stride, err)
  !
  !== å…¥å‡ºåŠ›ç¯„å›²ã®æ‹˜æŸ (æ•°å€¤ã§æŒ‡å®š)
  !
  ! å¤‰æ•° *var* æ¬¡å…ƒã®å…¥å‡ºåŠ›ç¯„å›²ã‚’æ‹˜æŸã—ã¾ã™ã€‚
  ! Limit ã‚’å‘¼ã³å‡ºã—ãŸå¾Œã§ã¯ Slice ã§ãã®ç¯„å›²ã®å¤–ã«å…¥å‡ºåŠ›ç¯„å›²ã‚’
  ! è¨å®šã§ããªããªã‚Šã¾ã™ã€‚ã“ã‚Œã«ã‚ˆã‚Šã€å¤‰æ•°å…¨ä½“ã§ã¯ãªãä¸€éƒ¨ã‚’
  ! Slice_Next ã‚µãƒ–ãƒ«ãƒ¼ãƒãƒ³ã‚’ç”¨ã„ã¦èµ°æŸ»ã§ãã‚‹ã‚ˆã†ã«ãªã‚Šã¾ã™ã€‚
  !
  ! æŒ‡å®šæ–¹æ³•ã¯ã€å¤‰æ•° *var* ã® *dimord* ç•ªç›®ã®æ¬¡å…ƒã‚’åŸºç‚¹ *start*,
  ! æ ¼åç·æ•° *count* é–“éš” *stride* ã«é™å®šã—ã¾ã™ã€‚
  ! ã“ã®ã‚ã¨ã§ã¯ã€ã‚¹ãƒ©ã‚¤ã‚¹æŒ‡å®šã®ç¬¬1æ ¼åã¯ *start* ç•ªç›®ã®æ ¼åã‚’
  ! æŒ‡ç¤ºã™ã‚‹ã“ã¨ã«ãªã‚Šã€ã‚¹ãƒ©ã‚¤ã‚¹æŒ‡å®šã§ã®æ ¼åæ•°ã¯ *count* å€‹ã‚’
  ! è¶Šãˆã‚‹ã“ã¨ãŒã§ããªããªã‚Šã€ã‚¹ãƒ©ã‚¤ã‚¹æŒ‡å®šã§é–“å¼•ããªã—ã‚’æŒ‡å®šã™ã‚‹ã¨
  ! *stride* å€‹ã”ã¨ã®æŒ‡å®šã‚’æŒ‡ç¤ºã™ã‚‹ã“ã¨ã«ãªã‚Šã¾ã™ã€‚
  !
  ! ã‚¨ãƒ©ãƒ¼ãŒç”Ÿã˜ãŸå ´åˆã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’å‡ºåŠ›
  ! ã—ã¦ãƒ—ãƒã‚°ãƒ©ãƒ ã¯å¼·åˆ¶çµ‚äº†ã—ã¾ã™ã€‚*err* ã‚’ä¸Žãˆã¦ã‚ã‚‹å ´åˆã«ã¯
  ! ã®å¼•æ•°ã« .true. ãŒè¿”ã‚Šã€ãƒ—ãƒã‚°ãƒ©ãƒ ã¯çµ‚äº†ã—ã¾ã›ã‚“ã€‚
  !
  ! *Limit* ã¯ 2 ã¤ã®ã‚µãƒ–ãƒ«ãƒ¼ãƒãƒ³ã®ç·ç§°åã§ã‚ã‚Šã€
  ! ä»–ã«ã‚‚ {gtool4 netCDF è¦ç´„}[link:../xref.htm#label-6] ã®
  ! ã€Œ5.4 ã‚³ãƒ³ãƒžè¨˜æ³•ã€ã‚’ç”¨ã„ã¦æŒ‡å®šã™ã‚‹ã“ã¨ã‚‚å¯èƒ½ã§ã™ã€‚
  ! ä¸‹è¨˜ã®ã‚µãƒ–ãƒ«ãƒ¼ãƒãƒ³ã‚’å‚ç…§ã—ã¦ãã ã•ã„ã€‚
  !
  use gtdata_types, only: GT_VARIABLE
  use gtdata_internal_map, only: map_lookup, gt_dimmap, dimrange, map_set
  use dc_error, only: dc_noerr, NF90_EINVAL, storeerror
  use dc_trace, only: beginsub, endsub, DbgMessage
  implicit none
  type(GT_VARIABLE), intent(inout):: var
  integer, intent(in)             :: dimord
  integer, intent(in) , optional  :: start, count, stride
  logical, intent(out), optional  :: err
  type(gt_dimmap), allocatable:: map(:)
  integer:: iolo, iohi, uilo, uihi, lowerlim, upperlim, dimlo, dimhi
  integer:: ndims, stat

  stat = NF90_EINVAL
  call beginsub('GTVarLimit_iiii', &
    & 'var%d-dim%d start=%d count=%d stride=%d', &
    & i=(/var%mapid, dimord, start, count, stride/))
  ! ã‚¨ãƒ©ãƒ¼ãƒã‚§ãƒƒã‚¯
  if (dimord < 1) then
    print *, "dimord =", dimord, " < 1"
    goto 999
  endif
  if (stride == 0) then
    print *, "stride == 0"
    goto 999
  endif
  call map_lookup(var, ndims=ndims)
  if (ndims <= 0) then
    print *, "ndims =", ndims, " <= 0"
    goto 999
  endif
  if (dimord > ndims) then
    print *, "dimrod =", dimord, " > ndims =", ndims
    goto 999
  endif
  if (allocated(map)) then
    deallocate(map)
  end if
  allocate(map(ndims))
  call map_lookup(var, map=map)
  ! (/lowerlim, upperlim/) ã¯å†…éƒ¨æ ¼åã®ç¯„å›² (é™é †å¯)
  lowerlim = min(start, start + (count - 1) * stride)
  upperlim = max(start, start + (count - 1) * stride)
  call dimrange(var, dimord, dimlo, dimhi)
  if (lowerlim < dimlo) then
    print *, "lowerlim = ", lowerlim, " < dimlo =", dimlo
    goto 999
  endif
  if (upperlim > dimhi) then
    print *, "upperlim = ", upperlim, " < dimhi =", dimhi
    goto 999
  endif

  call DbgMessage('@ lowerlim=%d upperlim=%d', i=(/lowerlim, upperlim/))

  ! å…¥å‡ºåŠ›ç¯„å›²ã‚’å†…éƒ¨æ ¼åç•ªå·ã«å¤‰ãˆã¦ãŠã
  uilo = map(dimord)%start
  iolo = 1 + map(dimord)%step * (uilo - 1) + map(dimord)%offset
  uihi = map(dimord)%start + (map(dimord)%count - 1) * map(dimord)%stride
  iohi = 1 + map(dimord)%step * (uihi - 1) + map(dimord)%offset

  call DbgMessage('@ userindex=%d %d, internal=%d %d', &
    & i=(/uilo, uihi, iolo, iohi/))
  call DbgMessage('@ DbgMessage offset %d -> %d step=%d', &
    & i=(/map(dimord)%offset, (start-1), stride/))

  ! åˆ¶é™ã‚’èª²ã™ã€‚offset ãŒå¤‰ã‚ã‚Œã°ãƒ¦ãƒ¼ã‚¶æ ¼åç•ªå·ã®æ„å‘³ãŒå¤‰ã‚ã‚‹
  map(dimord)%offset = start - 1
  map(dimord)%allcount = count
  map(dimord)%step = stride

  ! å…¥å‡ºåŠ›ç¯„å›²ã‚’å†…éƒ¨æ ¼åç•ªå·ã‹ã‚‰ãƒ¦ãƒ¼ã‚¶æ ¼åç•ªå·ã«æˆ»ã™
  uilo = 1 + (iolo - 1 - map(dimord)%offset) / map(dimord)%step
  uihi = 1 + (iohi - 1 - map(dimord)%offset) / map(dimord)%step
  call DbgMessage('@ userindex=%d %d', i=(/uilo, uihi/))

  ! ãã‚Œãžã‚Œã¯åˆ¶é™ [1 .. allcount] ã®ä¸ã«ãªã‘ã‚Œã°ãªã‚‰ãªã„
  uilo = max(1, min(map(dimord)%allcount, uilo))
  uihi = max(1, min(map(dimord)%allcount, uihi))

  call DbgMessage('@ userindex=%d %d orig_stride=%d', &
    & i=(/uilo, uihi, map(dimord)%stride/))

  ! å…ƒã®ã‚¹ãƒˆãƒ©ã‚¤ãƒ‰ã®ç¬¦å·ã¯ç„¡è¦–ã—ã€æ£ã«å›ºå®šã™ã‚‹
  map(dimord)%stride = max(1, abs(map(dimord)%stride))
  map(dimord)%start = min(uilo, uihi)
  map(dimord)%count = 1 + abs(uihi - uilo) / map(dimord)%stride

  call map_set(var, map, stat)
  if (stat /= 0) call DbgMessage("map_set fail")

999 continue
  call storeerror(stat, 'GTVarLimit_iiii', err)
  call endsub('GTVarLimit_iiii')
end subroutine GTVarLimit_iiii

! å¤‰æ•° var ã« string ã«ã‚ˆã‚‹ãƒžãƒƒãƒ—æ“ä½œã‚’è¡Œã†ã€‚
! string ã¯ã‚³ãƒ³ãƒžã§åŒºåˆ‡ã‚‰ã‚ŒãŸå¤‰æ›æŒ‡å®šã®åˆ—ã§ã‚ã‚‹ã€‚
! å¤‰æ›æŒ‡å®šã¯é ˜åŸŸè¨å®šã¨æ¬¡å…ƒé †åºå¤‰æ›ã®ã©ã¡ã‚‰ã‹ã§ã‚ã‚‹ã€‚
! é ˜åŸŸè¨å®šã¯è‹±æ•°å—ã§å§‹ã¾ã‚‹ã‚‚ã®ã§ã€<dim>=<lower>, <dim>=<lower>:<upper>,
!   <dim>=<lower>:<upper>:<stride> ã®ã‚ˆã†ãªå½¢å¼ã§ã‚ã‚‹ã€‚
!   ã“ã“ã§ã€dim ã¯æ¬¡å…ƒç•ªå·ã¾ãŸã¯æ¬¡å…ƒåã§ã‚ã‚Šã€<lower>, <upper>
!   ã¯ ^ ã‚’å‰ç½®ã—ãŸåº§æ¨™å³å€¤ã¾ãŸã¯æ ¼åç•ªå·ã§ã‚ã‚‹ã€‚
!   <stride> ã¯æ ¼åæ•°ã§ã‚ã‚‹ã€‚
! (æœªå®Ÿè£…) æ¬¡å…ƒé †åºå¤‰æ›ã¯ = ã§å§‹ã¾ã‚‹ã‚‚ã®ã§ã€
!   IGN:<dim>=<pos>
! ã®å½¢æ…‹ã‚’ã¨ã‚‹ã€‚

subroutine GTVarLimit(var, string, err)
  !
  !== å…¥å‡ºåŠ›ç¯„å›²ã®æ‹˜æŸ (æ–‡å—åˆ—ã§æŒ‡å®š)
  !
  ! å¤‰æ•° *var* æ¬¡å…ƒã®å…¥å‡ºåŠ›ç¯„å›²ã‚’æ‹˜æŸã—ã¾ã™ã€‚
  ! *Limit* ã¯ 2 ã¤ã®ã‚µãƒ–ãƒ«ãƒ¼ãƒãƒ³ã®ç·ç§°åã§ã‚ã‚Šã€
  ! åˆ¥ã®æŒ‡å®šæ–¹æ³•ã‚‚ã‚ã‚Šã¾ã™ã€‚ã¾ãšã¯ä¸Šè¨˜ã®ã‚µãƒ–ãƒ«ãƒ¼ãƒãƒ³ã‚’å‚ç…§ã—ã¦ãã ã•ã„ã€‚
  !
  ! æŒ‡å®šæ–¹æ³•ã¯ã€*string* ã«
  ! {gtool4 netCDF è¦ç´„}[link:../xref.htm#label-6] ã®
  ! ã€Œ5.4 ã‚³ãƒ³ãƒžè¨˜æ³•ã€ã«è¿°ã¹ã‚‰ã‚Œã‚‹ç¯„å›²æŒ‡å®šè¡¨ç¾ã‚’ç”¨ã„ã¾ã™ã€‚
  ! å‡¡ä¾‹ã‚’ä»¥ä¸‹ã«æŒ™ã’ã¾ã™ã€‚
  !
  !     <dim>=<lower>
  !
  !     <dim>=<lower>:<upper>
  !
  !     <dim>=<lower>:<upper>:<stride>
  !
  ! ã“ã“ã§ã€<tt><dim></tt> ã¯æ¬¡å…ƒç•ªå·ã¾ãŸã¯æ¬¡å…ƒåã§ã‚ã‚Šã€
  ! <tt><lower></tt>, <tt><upper></tt>
  ! ã¯åº§æ¨™å€¤ã¾ãŸã¯ "<tt>^</tt>" ã‚’å‰ç½®ã—ãŸæ ¼åç•ªå·ã§ã™ã€‚
  ! <tt><stride></tt> ã¯æ ¼åæ•°ã§ã™ã€‚
  !
  ! ã‚¨ãƒ©ãƒ¼ãŒç”Ÿã˜ãŸå ´åˆã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’å‡ºåŠ›
  ! ã—ã¦ãƒ—ãƒã‚°ãƒ©ãƒ ã¯å¼·åˆ¶çµ‚äº†ã—ã¾ã™ã€‚*err* ã‚’ä¸Žãˆã¦ã‚ã‚‹å ´åˆã«ã¯
  ! ã®å¼•æ•°ã« .true. ãŒè¿”ã‚Šã€ãƒ—ãƒã‚°ãƒ©ãƒ ã¯çµ‚äº†ã—ã¾ã›ã‚“ã€‚
  !
  use gtdata_types, only: GT_VARIABLE
  use dc_trace, only: beginsub, endsub
  use dc_url, only: gt_comma
  use gtdata_internal_map, only: gtvar_dump
  type(GT_VARIABLE),  intent(inout):: var
  character(len = *), intent(in)   :: string
  logical, intent(out), optional   :: err
  integer:: is, ie
continue
  call beginsub('GTVarLimit', 'var=%d lim=<%c>', i=(/var%mapid/), c1=trim(string))
  call gtvar_dump(var)
  ! ã‚³ãƒ³ãƒžã§åŒºåˆ‡ã£ã¦è§£é‡ˆ
  is = 1
  do
    ie = index(string(is: ), gt_comma)
    if (ie == 0) exit
    call limit_one(string(is: is+ie-2))
    is = is + ie
    if (is > len(string)) exit
  enddo
  call limit_one(string(is: ))
  if (present(err)) err = .false.
  call endsub('GTVarLimit')
  return
contains

  subroutine limit_one(string)
    use dc_url,         only: gt_equal
    use dc_string,      only: strieq, stoi
    use gtdata_generic, only: del_dim, dimname_to_dimord
    use gtdata_generic, only: del_dim, dimname_to_dimord, limit
    character(len = *), intent(in):: string
    integer:: equal, dimord
    integer:: start, count, stride, strhead
    logical:: myerr

    if (string == '') return

    strhead = 4
    if (len(string) < 4) strhead = len(string)

    if (strieq(string(1:strhead), "IGN:")) then
      ! éš è”½åž‹æŒ‡å®šå ign:<dim> ã¾ãŸã¯ ign:<dim>=<start>
      equal = index(string, gt_equal)
      if (equal == 0) then
        start = 1
      else
        start = stoi(string(equal+1: ), default=1)
      endif
      dimord = dimname_to_dimord(var, string(5: equal-1))
      call limit(var, dimord, start, 1, 1, err)
      call del_dim(var, dimord, myerr)
      return
    endif

    ! é™å®šåž‹æŒ‡å®šå <dim>=<start>:<finish>:<stride>
    ! ã„ã¾ã¯å®Ÿè£…ãŒãƒã‚°ã£ã¦ã„ã¦ <start>:<count>:<stride> ã«ãªã£ã¦ã‚‹
    !
    equal = index(string, gt_equal)
    if (equal == 0) return
    dimord = dimname_to_dimord(var, string(1: equal-1))
    if (dimord <= 0) return
    !
    call region_spec(dimord, string(equal+1: ), start, count, stride)
    call limit(var, dimord, start, count, stride, err)
  end subroutine limit_one

  !
  ! ç¯„å›²æŒ‡å®šã® = ã®ã‚ã¨ã‚’ : ã§åŒºåˆ‡ã£ã¦ãƒžãƒƒãƒ—ã«ã„ã‚Œã‚‹
  !
  subroutine region_spec(dimord, string, start, count, stride)
    use dc_types, only: token
    use dc_string, only: index_ofs, stoi
    use dc_url, only: gt_circumflex, gt_colon
    use gtdata_internal_map, only: dimrange
    integer, intent(in):: dimord
    integer, intent(out):: start, count, stride
    character(len = *), intent(in):: string
    integer:: colon, prev_colon, finish, dimlo, dimhi
    character(len = token):: val(3)
  continue
    colon = index(string, gt_colon)
    if (colon == 0) then
      ! ã‚³ãƒãƒ³ãŒãªã„å ´åˆã¯ä¸Šä¸‹ç«¯ã«åŒã˜å€¤
      val(1) = string(1: )
      val(2) = val(1)
      val(3) = ""
    else
      val(1) = string(1: colon - 1)
      prev_colon = colon
      colon = index_ofs(string, colon + 1, gt_colon)
      if (colon > 0) then
        val(2) = string(prev_colon + 1: colon - 1)
        val(3) = string(colon + 1: )
      else
        val(2) = string(prev_colon + 1: )
        val(3) = ""
      endif
    endif
    if (val(3) == "") val(3) = "^1"

    if (val(1)(1:1) == gt_circumflex) then
      start = stoi(val(1)(2: ))
    else if (val(1) == val(2)) then
      start = nint(value_to_index(dimord, val(1)))
    else
      start = floor(value_to_index(dimord, val(1)))
    endif
    if (val(2) == val(1)) then
      finish = start
    else if (val(2)(1:1) == gt_circumflex) then
      finish = stoi(val(2)(2: ))
    else
      finish = ceiling(value_to_index(dimord, val(2)))
    endif

    call dimrange(var, dimord, dimlo, dimhi)
    start = min(max(dimlo, start), dimhi)
    finish = min(max(dimlo, finish), dimhi)
    count = abs(finish - start) + 1

    if (val(3)(1:1) == gt_circumflex) then
      stride = stoi(val(3)(2: ))
    else
      stride = stoi(val(3))
    endif
    stride = sign(stride, finish - start)
  end subroutine region_spec

  real function value_to_index(dimord, value) result(result)
    !
    ! GTVarLimit ã®å¼•æ•° *var* ã«æ ¼ç´ã•ã‚Œã‚‹å¤‰æ•°ã®æ¬¡å…ƒ *dimord*
    ! ã«æ ¼ç´ã•ã‚Œã‚‹ãƒ‡ãƒ¼ã‚¿ã®ã†ã¡, *value* ãŒæ ¼ç´ã•ã‚Œã‚‹
    ! æ ¼åç•ªå·ã‚’æ•´æ•°å€¤ã«ã—ã¦è¿”ã—ã¾ã™.
    !
    ! ä¾‹ãˆã°æ¬¡å…ƒã«ä»¥ä¸‹ã®ãƒ‡ãƒ¼ã‚¿ãŒæ ¼ç´ã•ã‚Œã¦ã„ã‚‹ã¨ã—ã¾ã™.
    !
    !     0.05  0.1  0.15  0.20  0.25  0.30
    !
    ! ã“ã®å ´åˆ, *value* ã« 0.15 ãŒä¸Žãˆã‚‰ã‚Œã‚Œã°æˆ»ã‚Šå€¤ã¯ 3. ã¨ãªã‚Šã¾ã™.
    ! ã¾ãŸ *value* ã« 0.225 ãŒä¸Žãˆã‚‰ã‚Œã‚Œã°æˆ»ã‚Šå€¤ã¯ 4.5 ã¨ãªã‚Šã¾ã™.
    !
    !
    use gtdata_types, only: GT_VARIABLE
    use gtdata_generic, only: get, open, close
    use dc_string, only: stod
    use dc_trace, only: beginsub, endsub, DbgMessage
    integer, intent(in):: dimord
    character(len = *), intent(in):: value
    type(GT_VARIABLE):: axisvar
    real, pointer:: axisval(:) => null()
    real:: val
    integer:: i
  continue

    call beginsub('value_to_index', 'var=%d dimord=%d value=%c', &
      & i=(/var%mapid, dimord/), c1=trim(value))

    call Open(axisvar, var, dimord, count_compact=.true.)
    nullify(axisval)
    call Get(axisvar, axisval)
    call Close(axisvar)
    if (.not. associated(axisval)) then
      result = -1.0
      return
    else if (size(axisval) < 2) then
      result = 1.0
      goto 900
    endif

    val = stod(value)

    ! call DbgMessage('value=%f axis=(/%*r/)', r=(/val, axisval(:)/), &
    !    & n=(/size(axisval)/))

    do, i = 1, size(axisval) - 1
      if (axisval(i + 1) == axisval(i)) then
        result = real(i) + 0.5
        goto 900
      endif
      result = i + (val - axisval(i)) / (axisval(i + 1) - axisval(i))
      if (result <= (i + 1)) goto 900
    enddo

900 continue
    call endsub('value_to_index', '(%c) = %r', &
      & c1=trim(value), r=(/result/))
    deallocate(axisval)
  end function value_to_index

end subroutine GTVarLimit