!== Monitor of CPU TIME
!
! Authors::   Yasuhiro MORIKAWA
! Version::   $Id: dc_clock.f90,v 1.16 2007/08/22 15:47:07 morikawa Exp $
! Tag Name::  $Name: gt4f90io-20070824 $
! Copyright:: Copyright (C) GFD Dennou Club, 2006. All rights reserved.
! License::   See COPYRIGHT[link:../../COPYRIGHT]
!

module dc_clock
  !
  !== Overview
  !
  ! プログラムの処理に要した CPU 時間を計測して表示します.
  !
  !== List
  !
  ! Create        :: 構造型 CLOCK 変数の初期化
  ! Start         :: 計測の開始
  ! Stop          :: 計測の一時停止
  ! Close         :: 構造型 CLOCK 変数の終了処理
  ! Get, EvalSec  :: CPU 時間 (単位: 秒) の取得
  ! toChar        :: CPU 時間を適当に整形して文字型変数に変換
  ! PutLine       :: 構造型 CLOCK 変数の情報を表示
  ! Result        :: CPU 時間に関する総合的な情報を表示
  ! Predict       :: プログラムが終了するまでの予測 CPU 時間, および
  !                  日時を表示
  ! #operator(+)  :: 加算 (dc_clock#CLOCK 型同士)
  !
  !== Usage
  !
  ! 始めに, 構造型 CLOCK の変数を定義し, Create で初期化します.
  ! プログラム中の計測開始地点で Start を呼び出し,
  ! 計測を一時停止する地点で Stop を呼び出します.
  ! Result によって経過時間を表示します.
  ! Predict を使用することでプログラムが終了するまでの残り CPU 時間
  ! の予測値を表示することが可能です.
  !
  !     program dc_clock_sapmle1
  !       use dc_clock, only: CLOCK, Create, Close, Start, Stop, Result, &
  !         & Predict, operator(+)
  !       implicit none
  !       type(CLOCK):: clock1, clock2
  !       integer:: i, j
  !       integer, parameter:: loop_num = 8
  !       real:: a, b
  !
  !       call Create( clk = clock1, name = 'exponential' )
  !       call Create( clk = clock2, name = 'four-operations' )
  !       a = 2.0
  !       b = 1.0
  !       do i = 1, loop_num
  !         call Start( clk = clock1 )
  !         do j = 1, 1000000
  !           a = (a**2)**0.3 + 1.0
  !         enddo
  !         call Stop( clk = clock1 )
  !         call Start( clk = clock2 )
  !         do j = 1, 1000000
  !           b = b / 3.0 * 2.0 + 1.0 - 1.0e-1
  !         enddo
  !         call Stop( clk = clock2 )
  !         call Predict( clk = clock1 + clock2, progress = real(i)/real(loop_num) )
  !       enddo
  !       call Result( clks = (/clock1, clock2/), total_auto = .true. )
  !       call Close( clk = clock1 )
  !       call Close( clk = clock2 )
  !
  !       write(*,*) 'a = ', a
  !       write(*,*) 'b = ', b
  !     end program dc_clock_sapmle1
  !
  !== Note
  !
  ! CPU 時間はシステム CPU 時間とユーザ CPU 時間とに分けることが
  ! できます. dc_clock では CPU 時間の計測に *cpu_time* サブルーチン
  ! (Fortran 95 規格で導入された組込みサブルーチン) を使用しているため,
  ! 計測された CPU 時間がシステム CPU 時間なのかユーザ CPU 時間なのか,
  ! もしくは両方の合計なのかどうかは処理系の *cpu_time* に依存しています.
  ! (大抵は両方の合計である場合が多いようです).
  !

  use dc_types, only: STRING
  use dc_trace, only: BeginSub, EndSub
  use dc_date_types, only: DC_DIFFTIME, DC_DATETIME
  implicit none
  private

  public:: CLOCK
  public:: Create, Close, Start, Stop, PutLine, Result, Set_Name
  public:: operator(+)
  public:: Get, EvalSec, toChar, Predict

  type CLOCK
    !
    ! CPU 時刻計測用の構造体です.
    ! 初期化には Create を, 終了処理には Close を用います.
    !
    ! 詳しい使い方は dc_clock の Usage を参照ください.
    !
    private
    character(STRING):: name
    type(DC_DIFFTIME):: start_time   ! 計測を開始した時間
                                     ! (計測の一時停止中には負の値が設定される)
    type(DC_DIFFTIME):: elapsed_time ! 経過時間の累計値
    type(DC_DATETIME):: start_date   ! 計測を開始した日時
    logical:: dummy
    logical:: initialized = .false. ! CLOCK 構造体の初期化チェック用フラグ
  end type CLOCK

  interface Create
    module procedure DCClockCreate
  end interface

  interface Close
    module procedure DCClockClose
  end interface

  interface Start
    module procedure DCClockStart
  end interface

  interface Stop
    module procedure DCClockStop
  end interface

  interface PutLine
    module procedure DCClockPutLine
  end interface

  interface Get
    module procedure DCClockGetR
    module procedure DCClockGetD
  end interface

  interface EvalSec
    module procedure DCClockEvalSecD
  end interface

  interface toChar
    module procedure DCClockToChar
  end interface

  interface Result
    module procedure DCClockResult
  end interface

  interface operator(+)
    module procedure DCClockAdd
  end interface

  interface Set_Name
    module procedure DCClockSetName
  end interface

  interface Predict
    module procedure DCClockPredict
  end interface

  character(*), parameter:: version = &
    & '$Name: gt4f90io-20070824 $' // &
    & '$Id: dc_clock.f90,v 1.16 2007/08/22 15:47:07 morikawa Exp $'

contains

  subroutine DCClockCreate(clk, name)
    !
    !=== CLOCK の初期化用サブルーチン
    !
    ! CLOCK 型の変数を利用する際にはまずこのサブルーチンによって
    ! 初期化を行ってください. *name* には計測内容を与えてください.
    !
    use dc_message, only: MessageNotify
    use dc_date, only: assignment(=), Create
    implicit none
    type(CLOCK), intent(out):: clk
    character(*), intent(in):: name
    character(STRING):: endsub_msg
    character(*), parameter:: subname = 'DCClockCreate'
  continue
    call BeginSub(subname, 'name=%c', c1=trim(name), version=version)
    if (clk % initialized) then
      call MessageNotify('W', subname, 'This argument (type CLOCK) is already initialized.')
      endsub_msg = 'already initialized'
      goto 999
    end if
    clk % name = name
    clk % elapsed_time = 0.0
    clk % start_time = - 1.0
    clk % initialized = .true.
    call Create(clk % start_date)
    endsub_msg = 'normal initialized'
999 continue
    call EndSub(subname, endsub_msg)
  end subroutine DCClockCreate

  subroutine DCClockClose(clk)
    !
    !=== CLOCK の終了サブルーチン
    !
    ! CLOCK 型の変数をクローズします.
    !
    implicit none
    type(CLOCK), intent(inout):: clk
    character(*), parameter:: subname = 'DCClockClose'
  continue
    call BeginSub(subname)
    if (clk % initialized) then
      clk % initialized = .false.
      clk % name = ''
    end if
    call EndSub(subname)
  end subroutine DCClockClose

  subroutine DCClockStart(clk, err)
    !
    !=== 計測の開始
    !
    ! このサブルーチンを呼んだ時点で計測を開始します.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます. *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_message, only: MessageNotify
    use dc_string, only: toChar, CPrintf
    use dc_types, only: DP
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    use dc_date, only: assignment(=), EvalSec
    implicit none
    type(CLOCK), intent(inout):: clk
    logical, intent(out), optional:: err
    character(STRING):: endsub_msg, cause_c
    real(DP):: start_time
    integer:: stat
    character(*), parameter:: subname = 'DCClockStart'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    if (.not. clk % initialized) then
      call MessageNotify('W', subname, 'Call Create before Start in dc_clock.')
      endsub_msg = 'Ignored because input argument was not initialized.'
      stat = DC_ENOTINIT
      goto 999
    end if
    call cpu_time(start_time)
    clk % start_time = start_time
    endsub_msg = CPrintf('name=%c, cpu_time=%f', &
      & c1=trim(clk % name), d=(/EvalSec(clk % start_time)/))
999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockStart

  subroutine DCClockStop(clk, err)
    !
    !=== 計測の一時停止
    !
    ! このサブルーチンを呼んだ時点で計測を一時停止します.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます. *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_message, only: MessageNotify
    use dc_string, only: toChar, CPrintf
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    use dc_date, only: EvalSec, assignment(=), operator(+), operator(-)
    use dc_date_types, only: DC_DIFFTIME
    use dc_types, only: DP
    implicit none
    type(CLOCK), intent(inout):: clk
    logical, intent(out), optional:: err
    character(STRING):: endsub_msg, cause_c
    type(DC_DIFFTIME):: stop_timediff
    real:: stop_time
    integer:: stat
    character(*), parameter:: subname = 'DCClockStop'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    if (.not. clk % initialized) then
      call MessageNotify('W', subname, 'Call Create before Stop in dc_clock.')
      endsub_msg = 'Ignored because input argument was not initialized.'
      stat = DC_ENOTINIT
      goto 999
    elseif (EvalSec(clk % start_time) < 0.0_DP) then
      call MessageNotify('W', subname, 'Call Start before Stop in dc_clock.')
      endsub_msg = 'Ignored because input argument was not started.'
      goto 999
    end if
    call cpu_time(stop_time)
    stop_timediff = stop_time
    clk % elapsed_time = clk % elapsed_time &
      & + stop_timediff - clk % start_time
    clk % start_time = - 1.0
    endsub_msg = CPrintf('name=%c, cpu_time=%r, elapsed_time=%f', &
      & c1=trim(clk % name), r=(/stop_time/), d=(/EvalSec(clk % elapsed_time)/))
999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockStop

  subroutine DCClockGetR(clk, sec, err) !:doc-priority 40:
    !
    !=== CPU 時間 (単位: 秒) の取得
    !
    ! CPU 時間 (単位: 秒) を *sec* に取得します.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます. *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_types, only: DP
    use dc_message, only: MessageNotify
    use dc_date, only: EvalSec
    use dc_string, only: CPrintf
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    implicit none
    type(CLOCK), intent(in):: clk
    real, intent(out):: sec
    logical, intent(out), optional:: err
    character(STRING):: endsub_msg, cause_c
    integer:: stat
    character(*), parameter:: subname = 'DCClockGetR'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    if (.not. clk % initialized) then
      call MessageNotify('W', subname, 'Call Create before Get in dc_clock.')
      endsub_msg = 'Ignored because input argument was not initialized.'
      stat = DC_ENOTINIT
      goto 999
    end if
    sec = real(EvalSec(clk % elapsed_time))
    endsub_msg = CPrintf('name=%c, return sec=<%r>', &
      & c1=trim(clk % name), r=(/sec/))
999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockGetR

  subroutine DCClockGetD(clk, sec, err) !:doc-priority 60:
    !
    !=== CPU 時間 (単位: 秒) の取得
    !
    ! CPU 時間 (単位: 秒) を *sec* に取得します.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます.
    ! 第二引数 *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_types, only: DP
    use dc_string, only: CPrintf
    use dc_message, only: MessageNotify
    use dc_date, only: EvalSec
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    implicit none
    type(CLOCK), intent(in):: clk
    real(DP), intent(out):: sec
    logical, intent(out), optional:: err
    character(STRING):: endsub_msg, cause_c
    integer:: stat
    character(*), parameter:: subname = 'DCClockGetD'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    if (.not. clk % initialized) then
      call MessageNotify('W', subname, 'Call Create before Get in dc_clock.')
      endsub_msg = 'Ignored because input argument was not initialized.'
      stat = DC_ENOTINIT
      goto 999
    end if
    sec = EvalSec(clk % elapsed_time)
    endsub_msg = CPrintf('name=%c, return sec=<%f>', &
      & c1=trim(clk % name), d=(/sec/))
999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockGetD

  function DCClockEvalSecD(clk) result(result)
    !
    !=== CPU 時間 (単位: 秒) の取得
    !
    ! CPU 時間 (単位: 秒) を返します.
    !
    ! 第一引数 *clk* に対して Create
    ! による初期化が行われていない場合, -1.0 が返ります.
    !
    use dc_types, only: DP
    implicit none
    type(CLOCK), intent(in):: clk
    real(DP):: result
    logical:: err
  continue
    call DCClockGetD(clk, result, err)
    if (err) result = -1.0_DP
  end function DCClockEvalSecD

  function DCClockToChar(clk) result(result)
    !
    !=== CPU 時間を適当に整形して文字型変数に変換
    !
    ! CPU 時間に関して適当に整形を行い, 文字型変数に変換して返します.
    !
    ! 第一引数 *clk* に対して Create
    ! による初期化が行われていない場合, 空文字が返ります.
    !
    use dc_string, only: CPrintf
    use dc_date, only: EvalSec
    implicit none
    type(CLOCK), intent(in):: clk
    character(STRING):: result
    character(20):: clk_name
  continue
    clk_name = clk % name
    if (clk % initialized) then
      result = CPrintf(' %c%c  %c', c1 = clk_name, &
        & c2=trim(result_value_form(EvalSec(clk % elapsed_time))), &
        & c3=trim(fit_unit_value(clk % elapsed_time)))
    else
      result = ''
    end if
  end function DCClockToChar

  subroutine DCClockPutLine(clk, unit, indent, err)
    !
    !=== 構造型 CLOCK 変数の情報を表示
    !
    ! 構造型 CLOCK 変数に関する情報を表示します. *unit* には出力先の装置番号を
    ! 与えてください. *unit* を与えない場合, 標準出力へ表示されます.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます. *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_types, only: STDOUT
    use dc_message, only: MessageNotify
    use dc_string, only: Printf, toChar, CPrintf
    use dc_date, only: EvalSec, EvalDay, toChar
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    use dc_types, only: DP
    implicit none
    type(CLOCK), intent(in):: clk
    integer, intent(in), optional:: unit
    character(*), intent(in), optional:: indent
                              ! 表示されるメッセージの字下げ.
                              !
                              ! Indent of displayed messages.
    logical, intent(out), optional:: err
    integer:: out_unit
    character(STRING):: endsub_msg, cause_c
    integer:: stat
    integer:: indent_len
    character(STRING):: indent_str
    character(*), parameter:: subname = 'DCClockPutLine'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    if (.not. clk % initialized) then
      call MessageNotify('W', subname, 'Call Create before PutLine in dc_clock.')
      endsub_msg = 'Ignored because input argument was not initialized.'
      stat = DC_ENOTINIT
      goto 999
    end if
    if (present(unit)) then
      out_unit = unit
    else
      out_unit = STDOUT
    end if
    indent_len = 0
    indent_str = ''
    if (present(indent)) then
      if (len(indent) /= 0) then
        indent_len = len(indent)
        indent_str(1:indent_len) = indent
      end if
    end if
    call Printf(out_unit, &
      & indent_str(1:indent_len) // &
      & '#<CLOCK:: @name=%c @clocking=%y @elapsed_time=%f sec. %c @start_date=%c>', &
      & c1=trim(clk % name), l=(/EvalSec(clk % start_time) > 0.0_DP/), &
      & d=(/EvalSec(clk % elapsed_time)/), &
      & c2=trim(fit_unit_value(clk % elapsed_time)), &
      & c3=trim(toChar(clk % start_date)))
    endsub_msg = CPrintf('name=%c, output to device number %d', &
      & c1=trim(clk % name), i=(/out_unit/))
999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockPutLine

  subroutine DCClockResult(clks, unit, total_auto, clk_total, total_name, err)
    !
    !=== CPU 時間の総計を表示
    !
    ! CPU 時間の総計を表示します. *clks* へ, CLOCK 変数の配列を
    ! 与えてください. プログラムの最後で呼び出されることを
    ! 想定しています. *unit* には出力先の装置番号を
    ! 与えてください. *unit* を与えない場合, 標準出力へ表示されます.
    !
    ! 引数 *total_auto* に .true. を与えると, *clks* を全て足し合わせた
    ! 合計値を自動的に表示します. 下記の引数 *clk_total* が与えられている
    ! 場合は *clk_total* が優先されます.
    !
    ! 引数 *clk_total* に CLOCK 変数を与えると, この変数を合計値と
    ! して表示します.
    !
    ! 引数 *total_name* に文字型変数を与えると, 総計メッセージの
    ! 冒頭にこの文字列を出力します.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます. *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_types, only: STDOUT, STRING, DP
    use dc_message, only: MessageNotify
    use dc_string, only: Printf, toChar, CPrintf
    use dc_date, only: EvalSec
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    implicit none
    type(CLOCK), intent(in):: clks(:)
    integer, intent(in), optional:: unit
    logical, intent(in), optional:: total_auto
    type(CLOCK), intent(in), optional:: clk_total
    logical, intent(out), optional:: err
    character(*), intent(in), optional:: total_name
    integer:: out_unit, i, clks_size
    character(20):: clk_name
    character(STRING):: endsub_msg, cause_c
    character(STRING):: total_name_work
    type(CLOCK):: clk_auto_total
    logical:: total_print_complete
    real(DP):: elapsed_time_val_cor
    integer:: stat
    character(*), parameter:: total_time_mes = '       TOTAL TIME = '
    character(*), parameter:: subname = 'DCClockResult'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    clks_size = size(clks)
    do i = 1, clks_size
      if (.not. clks(i) % initialized) then
        call MessageNotify('W', subname, 'Call Create before Result in dc_clock.')
        endsub_msg = 'Ignored because input argument was not initialized.'
        stat = DC_ENOTINIT
        goto 999
      end if
    end do
    if (present(unit)) then
      out_unit = unit
    else
      out_unit = STDOUT
    end if
    if (present(total_name)) then
      total_name_work = ' (' // trim(total_name) // ')'
    else
      total_name_work = ''
    end if
    call Printf(out_unit, '')
    call Printf(out_unit, &
      & ' ############## CPU TIME SUMMARY%c################', &
      & c1=trim(total_name_work) // ' ')
    do i = 1, clks_size
      clk_name = clks(i) % name
      elapsed_time_val_cor = EvalSec(clks(i) % elapsed_time)
      if (elapsed_time_val_cor < 0.0_DP) elapsed_time_val_cor = 0.0_DP
      call Printf(out_unit, &
        & ' %c%c  %c', c1=clk_name, &
        & c2=trim(result_value_form(elapsed_time_val_cor)), &
        & c3=trim(fit_unit_value(clks(i) % elapsed_time)))
    end do
    total_print_complete = .false.
    if (present(clk_total)) then
      if (clk_total % initialized) then
        call Printf(out_unit, &
          & ' ------------------------------------------------')
        elapsed_time_val_cor = EvalSec(clk_total % elapsed_time)
        if (elapsed_time_val_cor < 0.0_DP) elapsed_time_val_cor = 0.0_DP
        call Printf(out_unit, &
          & ' %c%c  %c', c1=total_time_mes, &
          & c2=trim(result_value_form(elapsed_time_val_cor)), &
          & c3=trim(fit_unit_value(clk_total % elapsed_time)))
        total_print_complete = .true.
      end if
    end if

    if (present(total_auto) .and. .not. total_print_complete) then
      if (total_auto) then
        clk_auto_total = clks(1)
        if (clks_size > 1) then
          do i = 2, clks_size
            clk_auto_total = clk_auto_total + clks(i)
          end do
        end if
        call Printf(out_unit, &
          & ' ------------------------------------------------')
        elapsed_time_val_cor = EvalSec(clk_auto_total % elapsed_time)
        if (elapsed_time_val_cor < 0.0_DP) elapsed_time_val_cor = 0.0_DP
        call Printf(out_unit, &
          & ' %c%c  %c', c1=total_time_mes, &
          & c2=trim(result_value_form(elapsed_time_val_cor)), &
          & c3=trim(fit_unit_value(clk_auto_total % elapsed_time)))
      end if
    end if

    endsub_msg = CPrintf('total results, output to device number %d', &
      & i=(/out_unit/))
999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockResult

  function result_value_form(value) result(result)
    !
    ! 引数 value として与えられた倍精度実数型のデータを,
    ! 以下のフォーマットに整形して文字型として返します.
    !
    !     0.183400E+02
    !
    use dc_types, only: DP, TOKEN
    implicit none
    character(TOKEN):: result
    real(DP), intent(in):: value
  continue
    write(result, "(e15.6)") value
  end function result_value_form

  function fit_unit_value(diff) result(result)
    !
    ! 引数 diff に与えられた DC_DIFFTIME 型変数に格納される日時データを
    ! 以下のフォーマットに整形して文字型として返します.
    !
    !     (23.18 days)
    !
    ! 単位は days, hrs., minutes から適当に
    ! 選ばれます. (値が 1 以上の値となるように選ばれます).
    ! 1 分以内の場合は空文字を返します.
    !
    use dc_types, only: DP, TOKEN
    use dc_date_types, only: DC_DIFFTIME
    use dc_date, only: EvalDay, EvalHour, EvalMin, EvalSec
    use dc_types, only: DP
    implicit none
    character(TOKEN):: result
    type(DC_DIFFTIME), intent(in):: diff
    character(TOKEN):: unit
    real(DP):: value
    character(TOKEN):: cval
  continue
    if (EvalDay(diff) > 1.0_DP) then
      unit = ' days'
      value = EvalDay(diff)
    elseif (EvalHour(diff) > 1.0_DP) then
      unit = ' hrs.'
      value = EvalHour(diff)
    elseif (EvalMin(diff) > 1.0_DP) then
      unit = ' minutes'
      value = EvalMin(diff)
    else
      result = ''
      return
    end if
    cval = printf_g5_2(value)
    result = '(' // trim(adjustl(cval)) // trim(unit) // ')'
  end function fit_unit_value

  function printf_g5_2(value) result(result)
    !
    ! 引数 value に与えられた数値データを
    ! 以下のフォーマットに整形して文字型として返します.
    !
    !     23.18
    !      0.23
    !
    use dc_types, only: DP, TOKEN, STRING
    use dc_string, only: CPrintf
    implicit none
    character(TOKEN):: result
    real(DP), intent(in):: value
    character(TOKEN):: int_part, dem_part
    integer:: dem_int
  continue
    write(int_part, "(i20)") int(value)
    dem_int = nint((value - int(value)) * 100)
    if (dem_int < 0) dem_int = - dem_int
    if (dem_int == 100) then
      dem_int = 0
      write(int_part, "(i20)") int(value) + 1
    end if
    dem_part = CPrintf('%02d', i=(/dem_int/))
    result = trim(adjustl(int_part)) // '.' // trim(dem_part)
  end function printf_g5_2

  function DCClockAdd(clk1, clk2) result(clk_total)
    !
    !=== CLOCK 変数を足し合わせる
    !
    ! CLOCK 変数 <b>clk1</b> と <b>clk2</b> を足し合わせます.
    ! 与えられた 2 つの CLOCK  変数の CPU 時間を合計し,
    ! CLOCK 変数として返します. 計測内容の名称は <b>clk1</b> と <b>clk2</b>
    ! の名称を '+' で組み合わせたものとなります.
    !
    use dc_string, only: CPrintf
    use dc_date, only: operator(+), operator(<), assignment(=)
    implicit none
    type(CLOCK), intent(in):: clk1
    type(CLOCK), intent(in):: clk2
    type(CLOCK):: clk_total
  continue
    if (.not. clk1 % initialized .or. .not. clk2 % initialized) then
      clk_total % initialized = .false.
      return
    end if

    clk_total % name = CPrintf('%c+%c', &
      & c1=trim(clk1 % name), c2=trim(clk2 % name))
    clk_total % start_time = - 1.0
    clk_total % initialized = .true.
    clk_total % elapsed_time = 0.0

    if (clk1 % start_date < clk2 % start_date) then
      clk_total % start_date = clk1 % start_date
    else
      clk_total % start_date = clk2 % start_date
    end if

    clk_total % elapsed_time = &
      & clk1 % elapsed_time + clk2 % elapsed_time
  end function DCClockAdd

  subroutine DCClockSetName(clk, name, err)
    !
    !=== 測定内容の名称を変更する.
    !
    ! CLOCK 変数 *clk* の計測内容の名称を変更します.
    ! この名称は Create の *name* 引数で指定されたものです.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます. *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_message, only: MessageNotify
    use dc_string, only: toChar, CPrintf
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    implicit none
    type(CLOCK), intent(inout):: clk
    character(*), intent(in):: name
    logical, intent(out), optional:: err
    character(STRING):: endsub_msg, cause_c
    integer:: stat
    character(*), parameter:: subname = 'DCClockStop'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    if (.not. clk % initialized) then
      call MessageNotify('W', subname, 'Call Create before Set_Name in dc_clock.')
      endsub_msg = 'Ignored because input argument was not initialized.'
      stat = DC_ENOTINIT
      goto 999
    end if
    clk % name = name
    endsub_msg = CPrintf('set new name "%c"', c1=trim(clk % name))
999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockSetName

  subroutine DCClockPredict(clk, progress, unit, err)
    !
    !=== プログラムが終了するまでの予測 CPU 時間, および日時を表示
    !
    ! CLOCK 変数 *clk* と *progress* から, プログラムが
    ! 終了するまでの予測 CPU 時間, および日時を以下のように表示します.
    !
    !     ########## PREDICTION OF CALCULATION ###########
    !     Start Date             2007-03-08T16:49:25+09:00
    !     Current Date           2007-03-08T16:49:27+09:00
    !     Progress     66.67%  [****************         ]
    !     Remaining CPU TIME      0.100000E+01
    !     Completion Date        2007-03-08T16:49:28+09:00
    !
    ! 第2引数である *progress* には 0 ～ 1 までの値を与えてください.
    ! プログラムの開始時を 0, 終了時を 1 とします. (例えば,
    ! プログラムが半分進んだ時には 0.5 を与えます).
    !
    ! ここで行う「予測」とは, これまでの経過時間および
    ! 終了したプログラムの分量から単純なアルゴリズムで割り出している
    ! ものなので, 正確な予測値を返すわけではありません.
    ! あくまで目安として利用してください.
    !
    ! 引数 *unit* には出力先の装置番号を
    ! 与えてください. *unit* を与えない場合, 標準出力へ表示されます.
    !
    ! 第一引数 *clk* に対して Create による初期化が行われていない場合,
    ! エラーを発生させます. *err* を与える場合には *err* に .true. が返り,
    ! プログラムは続行されます.
    !
    use dc_types, only: STDOUT, DP
    use dc_message, only: MessageNotify
    use dc_string, only: toChar, CPrintf, Printf
    use dc_error, only: StoreError, DC_ENOTINIT, DC_NOERR
    use dc_date_types, only: DC_DIFFTIME, DC_DATETIME
    use dc_date, only: assignment(=), operator(+), Create, toChar, EvalSec
    implicit none
    type(CLOCK), intent(in):: clk
    real, intent(in):: progress
    integer, intent(in), optional:: unit
    logical, intent(out), optional:: err
    character(STRING):: endsub_msg, cause_c
    integer:: stat, out_unit
    type(DC_DIFFTIME):: remain_diff
    type(DC_DATETIME):: comp_date, cur_date
    character(7):: prog_percent
    character(25):: prog_bar
    integer:: prog_bar_ptr
    real:: prog_valid
    character(*), parameter:: subname = 'DCClockPredict'
  continue
    call BeginSub(subname)
    stat = DC_NOERR
    cause_c = 'CLOCK'
    if (.not. clk % initialized) then
      call MessageNotify('W', subname, 'Call Create before Predict in dc_clock.')
      endsub_msg = 'Ignored because input argument was not initialized.'
      stat = DC_ENOTINIT
      goto 999
    end if
    if (progress <= 0.0) then
      call MessageNotify('W', subname, 'Specify 0.0 -- 1.0 value to "progress"')
      return
    elseif (progress > 1.0) then
      call MessageNotify('W', subname, 'Over 1.0 value to "progress" was modified to 1.0')
      prog_valid = 1.0
    else
      prog_valid = progress
    end if

    if (present(unit)) then
      out_unit = unit
    else
      out_unit = STDOUT
    end if

    remain_diff = real(nint(EvalSec(clk) / prog_valid * (1.0 - prog_valid)))
    call Create(cur_date)
    comp_date = cur_date + remain_diff
    prog_percent = ''
    prog_percent = adjustr(trim(printf_g5_2(real(prog_valid * 100, DP))) // '%')
    prog_bar = ''
    prog_bar_ptr = int(prog_valid * 25)
    if (prog_bar_ptr > 0) prog_bar(1:prog_bar_ptr) = '*************************'

    call Printf(out_unit, '')
    call Printf(out_unit, &
      & ' ########## PREDICTION OF CALCULATION ###########')
    call Printf(out_unit, &
      & ' Start Date             %c', c1=trim(toChar(clk % start_date)))
    call Printf(out_unit, &
      & ' Current Date           %c', c1=trim(toChar(cur_date)))
    call Printf(out_unit, &
      & ' Progress     %c [%c]', c1=prog_percent, c2=prog_bar)
    call Printf(out_unit, &
      & ' Remaining CPU TIME   %c %c', &
      & c1=trim(result_value_form(EvalSec(remain_diff))), &
      & c2=trim(fit_unit_value(remain_diff)))
    call Printf(out_unit, &
      & ' Completion Date        %c', c1=trim(toChar(comp_date)))

999 continue
    call StoreError(stat, subname, err, cause_c)
    call EndSub(subname, endsub_msg)
  end subroutine DCClockPredict

end module dc_clock