text:
      MODULE ocean_control_mod
!
!svn $Id: op_ocean.h 2328 2014-01-23 20:16:18Z arango $
!================================================== Hernan G. Arango ===
!  Copyright (c) 2002-2014 The ROMS/TOMS Group       Andrew M. Moore   !
!    Licensed under a MIT/X style license                              !
!    See License_ROMS.txt                                              !
!=======================================================================
!                                                                      !
!  ROMS/TOMS Optimal Pertubations (Singular Vectors) Driver:           !
!                                                                      !
!  This driver computes the singular vectors of the propagator R(0,t)  !
!  which measure the  fastest  growing of all possible  perturbations  !
!  over a given time interval.  They  are  usually  used to study the  !
!  dynamics,  sensitivity,  and stability of the ocean circulation to  !
!  naturally occurring pertubations in the prediction system.          !
!                                                                      !
!  These  routines  control the  initialization,  time-stepping,  and  !
!  finalization of  ROMS/TOMS  model following ESMF conventions:       !
!                                                                      !
!     ROMS_initialize                                                  !
!     ROMS_run                                                         !
!     ROMS_finalize                                                    !
!                                                                      !
!  Reference:                                                          !
!                                                                      !
!    Moore, A.M. et al., 2004: A comprehensive ocean prediction and    !
!      analysis system based on the tangent linear and adjoint of a    !
!      regional ocean model, Ocean Modelling, 7, 227-258.              !
!                                                                      !
!=======================================================================
!
      implicit none

      PRIVATE
      PUBLIC  :: ROMS_initialize
      PUBLIC  :: ROMS_run
      PUBLIC  :: ROMS_finalize

      CONTAINS

      SUBROUTINE ROMS_initialize (first, mpiCOMM)
!
!=======================================================================
!                                                                      !
!  This routine allocates and initializes ROMS/TOMS state variables    !
!  and internal and external parameters.                               !
!                                                                      !
!=======================================================================
!
      USE mod_param
      USE mod_parallel
      USE mod_iounits
      USE mod_scalars
      USE mod_storage

#ifdef MCT_LIB
!
# ifdef AIR_OCEAN
      USE ocean_coupler_mod, ONLY : initialize_ocn2atm_coupling
# endif
# ifdef WAVES_OCEAN
      USE ocean_coupler_mod, ONLY : initialize_ocn2wav_coupling
# endif
#endif
!
!  Imported variable declarations.
!
      logical, intent(inout) :: first

      integer, intent(in), optional :: mpiCOMM
!
!  Local variable declarations.
!
      logical :: allocate_vars = .TRUE.

#ifdef DISTRIBUTE
      integer :: MyError, MySize
#endif
      integer :: chunk_size, ng, thread
#ifdef _OPENMP
      integer :: my_threadnum
#endif

#ifdef DISTRIBUTE
!
!-----------------------------------------------------------------------
!  Set distribute-memory (MPI) world communictor.
!-----------------------------------------------------------------------
!
      IF (PRESENT(mpiCOMM)) THEN
        OCN_COMM_WORLD=mpiCOMM
      ELSE
        OCN_COMM_WORLD=MPI_COMM_WORLD
      END IF
      CALL mpi_comm_rank (OCN_COMM_WORLD, MyRank, MyError)
      CALL mpi_comm_size (OCN_COMM_WORLD, MySize, MyError)
#endif
!
!-----------------------------------------------------------------------
!  On first pass, initialize model parameters a variables for all
!  nested/composed grids.  Notice that the logical switch "first"
!  is used to allow multiple calls to this routine during ensemble
!  configurations.
!-----------------------------------------------------------------------
!
      IF (first) THEN
        first=.FALSE.
!
!  Initialize parallel control switches. These scalars switches are
!  independent from standard input parameters.
!
        CALL initialize_parallel
!
!  Read in model tunable parameters from standard input. Allocate and
!  initialize variables in several modules after the number of nested
!  grids and dimension parameters are known.
!
        CALL inp_par (iTLM)
        IF (exit_flag.ne.NoError) RETURN
!
!  Set domain decomposition tile partition range.  This range is
!  computed only once since the "first_tile" and "last_tile" values
!  are private for each parallel thread/node.
!
!$OMP PARALLEL
#if defined _OPENMP
      MyThread=my_threadnum()
#elif defined DISTRIBUTE
      MyThread=MyRank
#else
      MyThread=0
#endif
      DO ng=1,Ngrids
        chunk_size=(NtileX(ng)*NtileE(ng)+numthreads-1)/numthreads
        first_tile(ng)=MyThread*chunk_size
        last_tile (ng)=first_tile(ng)+chunk_size-1
      END DO
!$OMP END PARALLEL
!
!  Initialize internal wall clocks. Notice that the timings does not
!  includes processing standard input because several parameters are
!  needed to allocate clock variables.
!
        IF (Master) THEN
          WRITE (stdout,10)
 10       FORMAT (/,' Process Information:',/)
        END IF
!
        DO ng=1,Ngrids
!$OMP PARALLEL
          DO thread=THREAD_RANGE
            CALL wclock_on (ng, iTLM, 0)
          END DO
!$OMP END PARALLEL
        END DO
!
!  Allocate and initialize modules variables.
!
!$OMP PARALLEL
        CALL mod_arrays (allocate_vars)
!$OMP END PARALLEL

      END IF

#if defined MCT_LIB && (defined AIR_OCEAN || defined WAVES_OCEAN)
!
!-----------------------------------------------------------------------
!  Initialize coupling streams between model(s).
!-----------------------------------------------------------------------
!
      DO ng=1,Ngrids
# ifdef AIR_OCEAN
        CALL initialize_ocn2atm_coupling (ng, MyRank)
# endif
# ifdef WAVES_OCEAN
        CALL initialize_ocn2wav_coupling (ng, MyRank)
# endif
      END DO
#endif
!
!-----------------------------------------------------------------------
!  Initialize tangent linear for all grids first in order to compute
!  the size of the state vector, Nstate.  This size is computed in
!  routine "wpoints".
!-----------------------------------------------------------------------
!
      DO ng=1,Ngrids
#if defined BULK_FLUXES && defined NL_BULK_FLUXES
        BLK(ng)%name=FWD(ng)%name
#endif
!$OMP PARALLEL
        CALL tl_initial (ng)
!$OMP END PARALLEL
        IF (exit_flag.ne.NoError) RETURN
      END DO
!
!  Allocate arrays associated with Generalized Stability Theory (GST)
!  analysis.
!
      CALL allocate_storage
!
!  Initialize various IO flags.
!
      Nrun=0
      DO ng=1,Ngrids
        LdefADJ(ng)=.TRUE.
        LwrtADJ(ng)=.TRUE.
        LdefTLM(ng)=.TRUE.
        LwrtTLM(ng)=.TRUE.
        LwrtPER(ng)=.FALSE.
        LcycleTLM(ng)=.FALSE.
        LcycleADJ(ng)=.FALSE.
        nADJ(ng)=ntimes(ng)
        nTLM(ng)=ntimes(ng)
      END DO
!
!  Initialize ARPACK parameters.
!
      Lrvec=.TRUE.                ! Compute Ritz vectors
      bmat='I'                    ! standard eigenvalue problem
      which='LM'                  ! compute NEV largest eigenvalues
      howmany='A'                 ! compute NEV Ritz vectors
      DO ng=1,Ngrids
        ido(ng)=0                 ! reverse communication flag
        info(ng)=0                ! random initial residual vector
        iparam(1,ng)=1            ! exact shifts
        iparam(3,ng)=MaxIterGST   ! maximum number of Arnoldi iterations
        iparam(4,ng)=1            ! block size in the recurrence
        iparam(7,ng)=1            ! type of eigenproblem being solved
      END DO
!
!  ARPACK debugging parameters.
!
      logfil=stdout               ! output logical unit
      ndigit=-3                   ! number of decimal digits
      msaupd=1                    ! iterations, timings, Ritz
      msaup2=1                    ! norms, Ritz values
      msaitr=0
      mseigt=0
      msapps=0
      msgets=0
      mseupd=0
!
!  Determine size of the eigenproblem (Nsize) and size of work space
!  array SworkL (LworkL).
!
      DO ng=1,Ngrids
        Nconv(ng)=0
        Nsize(ng)=Nend(ng)-Nstr(ng)+1
      END DO

#ifdef CHECKPOINTING
!
!  If restart, read in checkpointing data GST restart NetCDF file.
!  Otherwise, create checkpointing restart NetCDF file.
!
      DO ng=1,Ngrids
        IF (LrstGST) THEN
          CALL get_gst (ng, iTLM)
          ido(ng)=-2
          laup2(1)=.FALSE.        ! cnorm
          laup2(2)=.FALSE.        ! getv0
          laup2(3)=.FALSE.        ! initv
          laup2(4)=.FALSE.        ! update
          laup2(5)=.TRUE.         ! ushift
        ELSE
          CALL def_gst (ng, iTLM)
        END IF
        IF (exit_flag.ne.NoError) RETURN
      END DO
#endif

      RETURN
      END SUBROUTINE ROMS_initialize

      SUBROUTINE ROMS_run (RunInterval)
!
!=======================================================================
!                                                                      !
!  This routine computes the singular vectors of R(0,t) by a single    !
!  integration  of  a  perturbation "u"  forward  in time  with the    !
!  tangent linear model over [0,t],  multiplication  of the  result    !
!  by "X",  followed by an integration of the  result  backwards in    !
!  time with the  adjoint model over [t,0].  This is  equivalmet to    !
!  the matrix-vector operation:                                        !
!                                                                      !
!       transpose[R(t,0)] X R(0,t) u                                   !
!                                                                      !
!  The above operator is symmetric and the  ARPACK library is used     !
!  to select eigenvectors and eigenvalues:                             !
!                                                                      !
!  Lehoucq, R.B., D.C. Sorensen, and C. Yang, 1997:  ARPACK user's     !
!    guide:  solution  of  large  scale  eigenvalue  problems with     !
!    implicit restarted Arnoldi Methods, Rice University, 140p.        !
!                                                                      !
!=======================================================================
!
      USE mod_param
      USE mod_parallel
      USE mod_iounits
      USE mod_ncparam
      USE mod_netcdf
      USE mod_scalars
      USE mod_stepping
      USE mod_storage
!
      USE propagator_mod
      USE packing_mod, ONLY : r_norm2
!
!  Imported variable declarations
!
      real(r8), intent(in) :: RunInterval            ! seconds
!
!  Local variable declarations.
!
      logical :: ITERATE
#ifdef CHECKPOINTING
      logical :: LwrtGST
#endif

      integer :: Fcount, Is, Ie, i, iter, ng
      integer :: NconvRitz(Ngrids)

      real(r8) :: Enorm

      real(r8), dimension(2) :: my_norm, my_value

      TYPE (T_GST), allocatable :: ad_state(:)
      TYPE (T_GST), allocatable :: state(:)

      character (len=55) :: string
!
!-----------------------------------------------------------------------
!  Implicit Restarted Arnoldi Method (IRAM) for the computation of
!  optimal perturbation Ritz eigenfunctions.
!-----------------------------------------------------------------------
!
!  Allocate nested grid pointers for state vectors.
!
      IF (.not.allocated(ad_state)) THEN
        allocate ( ad_state(Ngrids) )
      END IF
      IF (.not.allocated(state)) THEN
        allocate ( state(Ngrids) )
      END IF
!
!  Iterate until either convergence or maximum iterations has been
!  exceeded.
!
      iter=0
      ITERATE=.TRUE.
#ifdef CHECKPOINTING
      LwrtGST=.TRUE.
#endif
!
      ITER_LOOP : DO WHILE (ITERATE)
        iter=iter+1
!
!  Reverse communication interface.
!
        DO ng=1,Ngrids
#ifdef PROFILE
          CALL wclock_on (ng, iTLM, 38)
#endif
#ifdef DISTRIBUTE
          CALL pdsaupd (OCN_COMM_WORLD,                                 &
     &                  ido(ng), bmat, Nsize(ng), which, NEV,           &
     &                  Ritz_tol,                                       &
     &                  STORAGE(ng)%resid(Nstr(ng)), NCV,               &
     &                  STORAGE(ng)%Bvec(Nstr(ng),1), Nsize(ng),        &
     &                  iparam(1,ng), ipntr(1,ng),                      &
     &                  STORAGE(ng)%SworkD,                             &
     &                  SworkL(1,ng), LworkL, info(ng))
#else
          CALL dsaupd (ido(ng), bmat, Nsize(ng), which, NEV,            &
     &                 Ritz_tol,                                        &
     &                 STORAGE(ng)%resid, NCV,                          &
     &                 STORAGE(ng)%Bvec, Nsize(ng),                     &
     &                 iparam(1,ng), ipntr(1,ng),                       &
     &                 STORAGE(ng)%SworkD,                              &
     &                 SworkL(1,ng), LworkL, info(ng))
#endif
          Nconv(ng)=iaup2(4)
#ifdef PROFILE
          CALL wclock_off (ng, iTLM, 38)
#endif
#ifdef CHECKPOINTING
!
!  If appropriate, write out check point data into GST restart NetCDF
!  file. Notice that the restart data is always saved if MaxIterGST
!  is reached without convergence. It is also saved when convergence
!  is achieved (ido=99).
!
          IF ((MOD(iter,nGST).eq.0).or.(iter.ge.MaxIterGST).or.         &
     &        (ANY(ido.eq.99))) THEN
            CALL wrt_gst (ng, iTLM)
            IF (exit_flag.ne.NoError) RETURN
          END IF
#endif
        END DO
!
!  Terminate computations if maximum number of iterations is reached.
!  This will faciliate splitting the analysis in several computational
!  cycles using the restart option.
!
        IF ((iter.ge.MaxIterGST).and.ANY(ido.ne.99)) THEN
          ITERATE=.FALSE.
          EXIT ITER_LOOP
        END IF
!
!  Perform matrix-vector operation:  R`(t,0)XR(0,t)u
!
        IF (ANY(ABS(ido).eq.1)) THEN
          DO ng=1,Ngrids
            Fcount=ADM(ng)%Fcount
            ADM(ng)%Nrec(Fcount)=0
            Fcount=TLM(ng)%Fcount
            TLM(ng)%Nrec(Fcount)=0
            ADM(ng)%Rindex=0
            TLM(ng)%Rindex=0
          END DO
!
!  Set state vectors to process by the propagator via pointer
!  equivalence.
!
          DO ng=1,Ngrids
            IF (ASSOCIATED(state(ng)%vector)) THEN
              nullify (state(ng)%vector)
            END IF
            Is=ipntr(1,ng)
            Ie=Is+Nsize(ng)-1
            state(ng)%vector => STORAGE(ng)%SworkD(Is:Ie)

            IF (ASSOCIATED(ad_state(ng)%vector)) THEN
              nullify (ad_state(ng)%vector)
            END IF
            Is=ipntr(2,ng)
            Ie=Is+Nsize(ng)-1
            ad_state(ng)%vector => STORAGE(ng)%SworkD(Is:Ie)
          END DO

!$OMP PARALLEL
          CALL propagator (RunInterval, state, ad_state)
!$OMP END PARALLEL
          IF (exit_flag.ne.NoError) RETURN
        ELSE
          IF (ANY(info.ne.0)) THEN
            DO ng=1,Ngrids
              IF (info(ng).ne.0) THEN
                IF (Master) THEN
                  CALL IRAM_error (info(ng), string)
                  WRITE (stdout,10) 'DSAUPD', TRIM(string),             &
     &                              ', info = ', info(ng)
                END IF
                RETURN
              END IF
            END DO
          ELSE
!
!  Compute Ritz vectors (the only choice left is IDO=99). They are
!  generated in ARPACK in decreasing magnitude of its eigenvalue.
!  The most significant is first.
!
            DO ng=1,Ngrids
              NconvRitz(ng)=iparam(5,ng)
              IF (Master) THEN
                WRITE (stdout,20) 'Number of converged Ritz values:',   &
     &                            iparam(5,ng)
                WRITE (stdout,20) 'Number of Arnoldi iterations:',      &
     &                            iparam(3,ng)
              END IF
#ifdef PROFILE
              CALL wclock_on (ng, iTLM, 38)
#endif
#ifdef DISTRIBUTE
              CALL pdseupd (OCN_COMM_WORLD,                             &
     &                      Lrvec, howmany, select(1,ng),               &
     &                      RvalueR(1,ng),                              &
     &                      STORAGE(ng)%Rvector(Nstr(ng),1),            &
     &                      Nsize(ng), sigmaR,                          &
     &                      bmat, Nsize(ng), which, NEV, Ritz_tol,      &
     &                      STORAGE(ng)%resid(Nstr(ng)), NCV,           &
     &                      STORAGE(ng)%Bvec(Nstr(ng),1), Nsize(ng),    &
     &                      iparam(1,ng), ipntr(1,ng),                  &
     &                      STORAGE(ng)%SworkD,                         &
     &                      SworkL(1,ng), LworkL, info(ng))
#else
              CALL dseupd (Lrvec, howmany, select(1,ng),                &
     &                     RvalueR(1,ng),                               &
     &                     STORAGE(ng)%Rvector, Nsize(ng),              &
     &                     sigmaR, bmat, Nsize, which, NEV, Ritz_tol,   &
     &                     STORAGE(ng)%resid, NCV,                      &
     &                     STORAGE(ng)%Bvec, Nsize(ng),                 &
     &                     iparam(1,ng), ipntr(1,ng),                   &
     &                     STORAGE(ng)%SworkD,                          &
     &                     SworkL(1,ng), LworkL, info(ng))
#endif
#ifdef PROFILE
              CALL wclock_off (ng, iTLM, 38)
#endif
            END DO

            IF (ANY(info.ne.0)) THEN
              DO ng=1,Ngrids
                IF (info(ng).ne.0) THEN
                  IF (Master) THEN
                    CALL IRAM_error (info(ng), string)
                    WRITE (stdout,10) 'DSEUPD', TRIM(string),           &
     &                                ', info = ', info(ng)
                  END IF
                  RETURN
                END IF
              END DO
            ELSE
!
!  Activate writing of each eigenvector into the  adjoint and tangent
!  linear history NetCDF files. The "ocean_time" is the eigenvector
!  number.
!
              Nrun=0

              DO i=1,MAXVAL(NconvRitz)
                DO ng=1,Ngrids
                  IF ((i.eq.1).or.LmultiGST) THEN
                    Fcount=ADM(ng)%Fcount
                    ADM(ng)%Nrec(Fcount)=0
                    Fcount=TLM(ng)%Fcount
                    TLM(ng)%Nrec(Fcount)=0
                    ADM(ng)%Rindex=0
                    TLM(ng)%Rindex=0
                  END IF
                  IF (LmultiGST) THEN
                    LdefADJ(ng)=.TRUE.
                    LdefTLM(ng)=.TRUE.
                    WRITE (ADM(ng)%name,30) TRIM(ADM(ng)%base), i
                    WRITE (TLM(ng)%name,30) TRIM(TLM(ng)%base), i
                  END IF
                END DO
!
!  Compute and write Ritz eigenvectors.
!
                DO ng=1,Ngrids
                  Is=Nstr(ng)
                  Ie=Nend(ng)
                  IF (ASSOCIATED(state(ng)%vector)) THEN
                    nullify (state(ng)%vector)
                  END IF

                  IF (ASSOCIATED(ad_state(ng)%vector)) THEN
                    nullify (ad_state(ng)%vector)
                  END IF
                  state(ng)%vector => STORAGE(ng)%Rvector(Is:Ie,i)
                  ad_state(ng)%vector => SworkR(Is:Ie)
                END DO

!$OMP PARALLEL
                CALL propagator (RunInterval, state, ad_state)
!$OMP END PARALLEL
                IF (exit_flag.ne.NoError) RETURN
!
                DO ng=1,Ngrids
                  CALL r_norm2 (ng, iTLM, Nstr(ng), Nend(ng),           &
     &                          -RvalueR(i,ng),                         &
     &                          state(ng)%vector,                       &
     &                          ad_state(ng)%vector, Enorm)
                  norm(i,ng)=Enorm
                  IF (Master) THEN
                    WRITE (stdout,40) i, norm(i,ng), RvalueR(i,ng), i
                  END IF
                END DO
!
!  Write out Ritz eigenvalues and Ritz eigenvector Euclidean norm
!  (residual) to NetCDF file(s).  Notice that we write the same value
!  twice in the TLM file for the initial and final perturbation of
!  the eigenvector.
!
                DO ng=1,Ngrids
                  SourceFile='op_ocean.h, ROMS_run'
                  my_norm(1)=norm(i,ng)
                  my_norm(2)=my_norm(1)
                  my_value(1)=RvalueR(i,ng)
                  my_value(2)=my_value(1)

                  IF (LwrtTLM(ng)) THEN
                    CALL netcdf_put_fvar (ng, iTLM, TLM(ng)%name,       &
     &                                    'Ritz_rvalue',                &
     &                                    my_value,                     &
     &                                    start = (/TLM(ng)%Rindex-1/), &
     &                                    total = (/2/),                &
     &                                    ncid = TLM(ng)%ncid)
                    IF (exit_flag.ne. NoError) RETURN

                    CALL netcdf_put_fvar (ng, iTLM, TLM(ng)%name,       &
     &                                    'Ritz_norm',                  &
     &                                    my_norm,                      &
     &                                    start = (/TLM(ng)%Rindex-1/), &
     &                                    total = (/2/),                &
     &                                    ncid = TLM(ng)%ncid)
                    IF (exit_flag.ne. NoError) RETURN

                    IF (LmultiGST) THEN
                      CALL netcdf_close (ng, iTLM, TLM(ng)%ncid,        &
     &                                   TLM(ng)%name)
                      IF (exit_flag.ne.NoError) RETURN
                    END IF
                  END IF
                  IF (LwrtADJ(ng)) THEN
                    CALL netcdf_put_fvar (ng, iADM, ADM(ng)%name,       &
     &                                    'Ritz_rvalue',                &
     &                                    RvalueR(i:,ng),               &
     &                                    start = (/ADM(ng)%Rindex/),   &
     &                                    total = (/1/),                &
     &                                    ncid = ADM(ng)%ncid)
                    IF (exit_flag.ne. NoError) RETURN

                    CALL netcdf_put_fvar (ng, iADM, ADM(ng)%name,       &
     &                                    'Ritz_norm',                  &
     &                                     norm(i:,ng),                 &
     &                                    start = (/ADM(ng)%Rindex/),   &
     &                                    total = (/1/),                &
     &                                    ncid = ADM(ng)%ncid)
                    IF (exit_flag.ne. NoError) RETURN

                    IF (LmultiGST) THEN
                      CALL netcdf_close (ng, iADM, ADM(ng)%ncid,        &
     &                                   ADM(ng)%name)
                      IF (exit_flag.ne.NoError) RETURN
                    END IF
                  END IF
                END DO
              END DO
            END IF
          END IF
          ITERATE=.FALSE.
        END IF

      END DO ITER_LOOP
!
 10   FORMAT (/,1x,'Error in ',a,1x,a,a,1x,i5,/)
 20   FORMAT (/,a,1x,i2,/)
 30   FORMAT (a,'_',i3.3,'.nc')
 40   FORMAT (1x,i4.4,'-th residual',1p,e14.6,0p,                       &
     &        '  Ritz value',1pe14.6,0p,2x,i4.4)

      RETURN
      END SUBROUTINE ROMS_run

      SUBROUTINE ROMS_finalize
!
!=======================================================================
!                                                                      !
!  This routine terminates ROMS/TOMS nonlinear and adjoint models      !
!  execution.                                                          !
!                                                                      !
!=======================================================================
!
      USE mod_param
      USE mod_parallel
      USE mod_iounits
      USE mod_ncparam
      USE mod_scalars
!
!  Local variable declarations.
!
      integer :: Fcount, ng, thread
!
!-----------------------------------------------------------------------
!  If blowing-up, save latest model state into RESTART NetCDF file.
!-----------------------------------------------------------------------
!
!  If cycling restart records, write solution into the next record.
!
      IF (exit_flag.eq.1) THEN
        DO ng=1,Ngrids
          IF (LwrtRST(ng)) THEN
            IF (Master) WRITE (stdout,10)
 10         FORMAT (/,' Blowing-up: Saving latest model state into ',   &
     &                ' RESTART file',/)
            Fcount=RST(ng)%Fcount
            IF (LcycleRST(ng).and.(RST(ng)%Nrec(Fcount).ge.2)) THEN
              RST(ng)%Rindex=2
              LcycleRST(ng)=.FALSE.
            END IF
            blowup=exit_flag
            exit_flag=NoError
            CALL wrt_rst (ng)
          END IF
        END DO
      END IF
!
!-----------------------------------------------------------------------
!  Stop model and time profiling clocks.  Close output NetCDF files.
!-----------------------------------------------------------------------
!
!  Stop time clocks.
!
      IF (Master) THEN
        WRITE (stdout,20)
 20     FORMAT (/,' Elapsed CPU time (seconds):',/)
      END IF

      DO ng=1,Ngrids
!$OMP PARALLEL
        DO thread=THREAD_RANGE
          CALL wclock_off (ng, iTLM, 0)
        END DO
!$OMP END PARALLEL
      END DO
!
!  Close IO files.
!
      CALL close_out

      RETURN
      END SUBROUTINE ROMS_finalize

      SUBROUTINE IRAM_error (info, string)
!
!=======================================================================
!                                                                      !
!  This routine decodes internal error messages from the Implicit      !
!  Restarted Arnoldi Method (IRAM) for the computation of optimal      !
!  perturbation Ritz eigenfunctions.                                   !
!                                                                      !
!=======================================================================
!
!
!  imported variable declarations.
!
      integer, intent(in) :: info

      character (len=*), intent(out) :: string
!
!-----------------------------------------------------------------------
!  Decode error message from IRAM.
!-----------------------------------------------------------------------
!
      IF (info.eq.0)  THEN
        string='Normal exit                                            '
      ELSE IF (info.eq.1) THEN
        string='Maximum number of iterations taken                     '
      ELSE IF (info.eq.3) THEN
        string='No shifts could be applied during an IRAM cycle        '
      ELSE IF (info.eq.-1) THEN
        string='Nstate must be positive                                '
      ELSE IF (info.eq.-2) THEN
        string='NEV must be positive                                   '
      ELSE IF (info.eq.-3) THEN
        string='NCV must be greater NEV and less than or equal Nstate  '
      ELSE IF (info.eq.-4) THEN
        string='Maximum number of iterations must be greater than zero '
      ELSE IF (info.eq.-5) THEN
        string='WHICH must be one of LM, SM, LA, SA or BE              '
      ELSE IF (info.eq.-6) THEN
        string='BMAT must be one of I or G                             '
      ELSE IF (info.eq.-7) THEN
        string='Length of private work array SworkL is not sufficient  '
      ELSE IF (info.eq.-8) THEN
        string='Error in DSTEQR in the eigenvalue calculation          '
      ELSE IF (info.eq.-9) THEN
        string='Starting vector is zero                                '
      ELSE IF (info.eq.-10) THEN
        string='IPARAM(7) must be 1, 2, 3, 4, 5                        '
      ELSE IF (info.eq.-11) THEN
        string='IPARAM(7) = 1 and BMAT = G are incompatable            '
      ELSE IF (info.eq.-12) THEN
        string='IPARAM(1) must be equal to 0 or 1                      '
      ELSE IF (info.eq.-13) THEN
        string='NEV and WHICH = BE are incompatable                    '
      ELSE IF (info.eq.-14) THEN
        string='Did not find any eigenvalues to sufficient accuaracy   '
      ELSE IF (info.eq.-15) THEN
        string='HOWMANY must be one of A or S if RVEC = .TRUE.         '
      ELSE IF (info.eq.-16) THEN
        string='HOWMANY = S not yet implemented                        '
      ELSE IF (info.eq.-17) THEN
        string='Different count of converge Ritz values in DSEUPD      '
      ELSE IF (info.eq.-9999) THEN
        string='Could not build and Arnoldi factorization              '
      END IF

      RETURN
      END SUBROUTINE IRAM_error

      END MODULE ocean_control_mod


text:
FROM alpine:3.3
MAINTAINER Linki <linki+docker.com@posteo.de>

RUN apk add --no-cache ruby ruby-bundler ruby-io-console

RUN bundle config --global frozen 1

RUN mkdir -p /app
WORKDIR /app

COPY Gemfile /app/
COPY Gemfile.lock /app/

RUN bundle install

COPY . /app

ENTRYPOINT ["./dnsseed.rb"]

redpajama_set_name: RedPajamaGithub


text:
<!--[metadata]>
+++
title = "Drivers"
description = "Reference for drivers Docker Machine supports"
keywords = ["machine, drivers, supports"]
[menu.main]
parent="workw_machine"
identifier="smn_machine_drivers"
weight=90
+++
<![end-metadata]-->

# Supported Drivers

-   [Amazon Web Services](aws.md)
-   [Microsoft Azure](azure.md)
-   [Digital Ocean](digital-ocean.md)
-   [Exoscale](exoscale.md)
-   [Google Compute Engine](gce.md)
-   [Generic](generic.md)
-   [Microsoft Hyper-V](hyper-v.md)
-   [OpenStack](openstack.md)
-   [Rackspace](rackspace.md)
-   [IBM Softlayer](soft-layer.md)
-   [Oracle VirtualBox](virtualbox.md)
-   [VMware vCloud Air](vm-cloud.md)
-   [VMware Fusion](vm-fusion.md)
-   [VMware vSphere](vsphere.md)


text:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

    <item name="activity_episodes" type="layout">@layout/activity_episodes_multipane</item>
    <item name="item_addshow" type="layout">@layout/item_addshow_wide</item>
    <item name="item_show" type="layout">@layout/item_show_wide</item>

    <bool name="isLargeTablet">true</bool>

</resources>


text:
<?php

declare(strict_types=1);

namespace Psi\Component\Grid\Filter;

use Psi\Component\ObjectAgent\Query\Comparison;
use Psi\Component\ObjectAgent\Query\Expression;
use Psi\Component\ObjectAgent\Query\Query;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class StringFilter extends AbstractComparatorFilter
{
    const TYPE_EQUAL = 'equal';
    const TYPE_EMPTY = 'empty';
    const TYPE_NOT_EMPTY = 'not_empty';
    const TYPE_CONTAINS = 'contains';
    const TYPE_NOT_CONTAINS = 'not_contains';
    const TYPE_STARTS_WITH = 'starts_with';
    const TYPE_ENDS_WITH = 'ends_with';
    const TYPE_IN = 'in';
    const TYPE_NOT_IN = 'not_in';

    private static $comparatorMap = [
        self::TYPE_EQUAL => Comparison::EQUALS,
        self::TYPE_EMPTY => Comparison::NULL,
        self::TYPE_NOT_EMPTY => Comparison::NOT_NULL,
        self::TYPE_CONTAINS => Comparison::CONTAINS,
        self::TYPE_NOT_CONTAINS => Comparison::NOT_CONTAINS,
        self::TYPE_STARTS_WITH => Comparison::CONTAINS,
        self::TYPE_ENDS_WITH => Comparison::CONTAINS,
        self::TYPE_IN => Comparison::IN,
        self::TYPE_NOT_IN => Comparison::NOT_IN,
    ];

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $this->addComparatorChoice($builder, $options);

        $builder->add('value', TextType::class, [
            'required' => false,
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getExpression(string $fieldName, array $data): Expression
    {
        $comparator = $data['comparator'] ?: self::TYPE_EQUAL;

        return Query::comparison(
            self::$comparatorMap[$comparator],
            $fieldName,
            $this->getValue($comparator, $data['value'])
        );
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $options)
    {
        $options->setDefault('comparators', array_keys(self::$comparatorMap));
    }

    /**
     * {@inheritdoc}
     */
    public function isApplicable(array $filterData): bool
    {
        return isset($filterData['value']);
    }

    protected function getComparatorMap(): array
    {
        return self::$comparatorMap;
    }

    private function getValue($comparator, $value)
    {
        switch ($comparator) {
            case self::TYPE_EQUAL:
                return $value;
            case self::TYPE_EMPTY:
                return;
            case self::TYPE_NOT_EMPTY:
                return;
            case self::TYPE_CONTAINS:
                return '%' . $value . '%';
            case self::TYPE_NOT_CONTAINS:
                return '%' . $value . '%';
            case self::TYPE_STARTS_WITH:
                return $value . '%';
            case self::TYPE_ENDS_WITH:
                return '%' . $value;
            case self::TYPE_IN:
                return array_map('trim', explode(',', $value));
            case self::TYPE_NOT_IN:
                return array_map('trim', explode(',', $value));
        }

        throw new \InvalidArgumentException(sprintf('Could not determine value for comparator "%s"', $comparator));
    }
}


text:
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.facebook.presto.block;

import com.facebook.presto.spi.block.Block;
import com.facebook.presto.spi.block.BlockBuilderStatus;
import com.facebook.presto.spi.block.InterleavedBlock;
import com.facebook.presto.spi.block.InterleavedBlockBuilder;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.spi.type.VarcharType;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import io.airlift.slice.Slice;
import org.testng.annotations.Test;

import java.lang.reflect.Array;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.facebook.presto.spi.type.BigintType.BIGINT;
import static com.facebook.presto.spi.type.VarcharType.VARCHAR;
import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType;
import static java.lang.Math.toIntExact;
import static java.lang.String.format;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

public class TestInterleavedBlock
        extends AbstractTestBlock
{
    private static final int[] SIZE_0 = new int[] {16, 0, 13, 1, 2, 11, 4, 7};
    private static final int SIZE_1 = 8;
    private static final int POSITION_COUNT = SIZE_0.length;
    private static final int COLUMN_COUNT = 2;
    private static final ImmutableList<Type> TYPES = ImmutableList.of(VARCHAR, BIGINT);

    @Test
    public void test()
    {
        Slice[] expectedValues = createExpectedValues();
        assertValues(expectedValues);
        assertValues((Slice[]) alternatingNullValues(expectedValues));
    }

    @Test
    public void testCopyPositions()
    {
        Slice[] expectedValues = createExpectedValues();
        InterleavedBlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues);

        assertBlockFilteredPositions(expectedValues, blockBuilder, Ints.asList(0, 1, 4, 5, 6, 7, 14, 15));
        assertBlockFilteredPositions(expectedValues, blockBuilder.build(), Ints.asList(0, 1, 4, 5, 6, 7, 14, 15));
        assertBlockFilteredPositions(expectedValues, blockBuilder.build(), Ints.asList(2, 3, 4, 5, 8, 9, 12, 13));
    }

    @Test
    private void testGetSizeInBytes()
    {
        int numEntries = 2000;
        VarcharType unboundedVarcharType = createUnboundedVarcharType();
        InterleavedBlockBuilder blockBuilder = new InterleavedBlockBuilder(ImmutableList.of(unboundedVarcharType, BIGINT), new BlockBuilderStatus(), numEntries);
        for (int i = 0; i < numEntries; i += 2) {
            unboundedVarcharType.writeString(blockBuilder, String.valueOf(ThreadLocalRandom.current().nextLong()));
            BIGINT.writeLong(blockBuilder, ThreadLocalRandom.current().nextLong());
        }
        InterleavedBlock block = blockBuilder.build();

        List<Block> splitQuarter = splitBlock(block, 4);
        long sizeInBytes = block.getSizeInBytes();
        long quarter1size = splitQuarter.get(0).getSizeInBytes();
        long quarter2size = splitQuarter.get(1).getSizeInBytes();
        long quarter3size = splitQuarter.get(2).getSizeInBytes();
        long quarter4size = splitQuarter.get(3).getSizeInBytes();
        double expectedQuarterSizeMin = sizeInBytes * 0.2;
        double expectedQuarterSizeMax = sizeInBytes * 0.3;
        assertTrue(quarter1size > expectedQuarterSizeMin && quarter1size < expectedQuarterSizeMax, format("quarter1size is %s, should be between %s and %s", quarter1size, expectedQuarterSizeMin, expectedQuarterSizeMax));
        assertTrue(quarter2size > expectedQuarterSizeMin && quarter2size < expectedQuarterSizeMax, format("quarter2size is %s, should be between %s and %s", quarter2size, expectedQuarterSizeMin, expectedQuarterSizeMax));
        assertTrue(quarter3size > expectedQuarterSizeMin && quarter3size < expectedQuarterSizeMax, format("quarter3size is %s, should be between %s and %s", quarter3size, expectedQuarterSizeMin, expectedQuarterSizeMax));
        assertTrue(quarter4size > expectedQuarterSizeMin && quarter4size < expectedQuarterSizeMax, format("quarter4size is %s, should be between %s and %s", quarter4size, expectedQuarterSizeMin, expectedQuarterSizeMax));
        assertEquals(quarter1size + quarter2size + quarter3size + quarter4size, sizeInBytes);
    }

    private static InterleavedBlockBuilder createBlockBuilderWithValues(Slice[] expectedValues)
    {
        InterleavedBlockBuilder blockBuilder = new InterleavedBlockBuilder(TYPES, new BlockBuilderStatus(), expectedValues.length);

        int valueIndex = 0;
        while (valueIndex < expectedValues.length) {
            for (Type type : TYPES) {
                Class<?> javaType = type.getJavaType();
                Slice expectedValue = expectedValues[valueIndex];

                if (expectedValue == null) {
                    blockBuilder.appendNull();
                }
                else if (javaType == boolean.class) {
                    type.writeBoolean(blockBuilder, expectedValue.getByte(0) != 0);
                }
                else if (javaType == long.class) {
                    type.writeLong(blockBuilder, expectedValue.getLong(0));
                }
                else if (javaType == double.class) {
                    type.writeDouble(blockBuilder, expectedValue.getDouble(0));
                }
                else {
                    blockBuilder.writeBytes(expectedValue, 0, expectedValue.length()).closeEntry();
                }

                valueIndex++;
            }
        }

        return blockBuilder;
    }

    private void assertValues(Slice[] expectedValues)
    {
        InterleavedBlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues);

        assertBlock(blockBuilder, expectedValues);
        assertBlock(blockBuilder.build(), expectedValues);
    }

    private static Slice[] createExpectedValues()
    {
        Slice[] expectedValues = new Slice[POSITION_COUNT * 2];
        for (int position = 0; position < POSITION_COUNT; position++) {
            expectedValues[position * 2] = createExpectedValue(SIZE_0[position]);
            expectedValues[position * 2 + 1] = createExpectedValue(SIZE_1);
        }
        return expectedValues;
    }

    protected static Object[] alternatingNullValues(Object[] objects)
    {
        Object[] objectsWithNulls = (Object[]) Array.newInstance(objects.getClass().getComponentType(), objects.length * 2 + 2);
        for (int i = 0; i < objects.length; i += 2) {
            objectsWithNulls[i * 2] = null;
            objectsWithNulls[i * 2 + 1] = null;
            objectsWithNulls[i * 2 + 2] = objects[i];
            objectsWithNulls[i * 2 + 3] = objects[i + 1];
        }
        objectsWithNulls[objectsWithNulls.length - 2] = null;
        objectsWithNulls[objectsWithNulls.length - 1] = null;
        return objectsWithNulls;
    }

    @Override
    protected <T> void assertBlockPosition(Block block, int position, T expectedValue)
    {
        assertInterleavedPosition(TYPES, block, position, expectedValue);

        Type type = TYPES.get(position % TYPES.size());
        assertInterleavedPosition(ImmutableList.of(type), block.getSingleValueBlock(position), 0, expectedValue);

        int alignedPosition = position - position % COLUMN_COUNT;

        assertInterleavedPosition(ImmutableList.of(type), block.getRegion(alignedPosition, COLUMN_COUNT), position - alignedPosition, expectedValue);
        assertInterleavedPosition(TYPES, block.getRegion(0, alignedPosition + COLUMN_COUNT), position, expectedValue);
        assertInterleavedPosition(ImmutableList.of(type), block.getRegion(alignedPosition, block.getPositionCount() - alignedPosition), position - alignedPosition, expectedValue);

        assertInterleavedPosition(ImmutableList.of(type), block.copyRegion(alignedPosition, COLUMN_COUNT), position - alignedPosition, expectedValue);
        assertInterleavedPosition(TYPES, block.copyRegion(0, alignedPosition + COLUMN_COUNT), position, expectedValue);
        assertInterleavedPosition(ImmutableList.of(type), block.copyRegion(alignedPosition, block.getPositionCount() - alignedPosition), position - alignedPosition, expectedValue);

        assertInterleavedPosition(TYPES, block.copyPositions(IntStream.range(alignedPosition, alignedPosition + COLUMN_COUNT).boxed().collect(Collectors.toList())), position % COLUMN_COUNT, expectedValue);
    }

    private <T> void assertInterleavedPosition(List<Type> types, Block block, int position, T expectedValue)
    {
        assertPositionValue(block, position, expectedValue);

        Type type = types.get(position % types.size());
        if (expectedValue == null) {
            assertTrue(block.isNull(position));
        }
        else if (BIGINT.equals(type)) {
            Slice expectedSliceValue = (Slice) expectedValue;
            assertEquals(expectedSliceValue.length(), Longs.BYTES);
            assertEquals(block.getLong(position, 0), expectedSliceValue.getLong(0));
        }
        else if (VARCHAR.equals(type)) {
            Slice expectedSliceValue = (Slice) expectedValue;
            assertSlicePosition(block, position, expectedSliceValue);
        }
        else {
            throw new IllegalArgumentException("Unsupported type " + type);
        }
    }

    @Override
    protected List<Block> splitBlock(Block block, int count)
    {
        double entriesPerSplit = block.getPositionCount() * 1.0 / count / COLUMN_COUNT;
        ImmutableList.Builder<Block> result = ImmutableList.builder();
        for (int i = 0; i < count; i++) {
            int startPosition = toIntExact(Math.round(entriesPerSplit * i) * COLUMN_COUNT);
            int endPosition = toIntExact(Math.round(entriesPerSplit * (i + 1)) * COLUMN_COUNT);
            result.add(block.getRegion(startPosition, endPosition - startPosition));
        }
        return result.build();
    }

    @Override
    protected boolean isByteAccessSupported()
    {
        return false;
    }

    @Override
    protected boolean isShortAccessSupported()
    {
        return false;
    }

    @Override
    protected boolean isIntAccessSupported()
    {
        return false;
    }

    @Override
    protected boolean isLongAccessSupported()
    {
        return false;
    }

    @Override
    protected boolean isSliceAccessSupported()
    {
        return false;
    }
}


text:
package com.cardinity;

import com.cardinity.exceptions.ValidationException;
import com.cardinity.model.Void;
import com.cardinity.model.*;
import org.junit.BeforeClass;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;

import static com.cardinity.model.Payment.Status.*;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.*;

public class CardinityIntegrationTest extends CardinityBaseTest {

    private static CardinityClient client;

    private static final String TEST_ORDER_ID = "cardinity-SDK-test";
    private static final String TEST_PAYMENT_DESCRIPTION = "cardinity-SDK-payment";
    private static final String TEST_REFUND_DESCRIPTION = "cardinity-SDK-refund";
    private static final String TEST_SETTLEMENT_DESCRIPTION = "cardinity-SDK-settlement";
    private static final String TEST_VOID_DESCRIPTION = "cardinity-SDK-void";

    @BeforeClass
    public static void setUpClass() throws Exception {
        final String consumerKey = System.getenv("CRD_KEY");
        final String consumerSecret = System.getenv("CRD_SECRET");
        if (consumerKey == null || consumerSecret == null) throw new Exception("Authorization keys missing");
        client = new CardinityClient(consumerKey, consumerSecret);
    }

    private static Payment getBaseCCPayment() {
        Payment payment = new Payment();
        payment.setCountry("LT");
        payment.setAmount(new BigDecimal(10));
        payment.setCurrency("EUR");
        payment.setDescription(TEST_PAYMENT_DESCRIPTION);
        payment.setPaymentMethod(Payment.Method.CARD);
        Card card = new Card();
        card.setPan("4111111111111111");
        card.setCvc(123);
        card.setExpYear(Calendar.getInstance().get(Calendar.YEAR) + 1);
        card.setExpMonth(1);
        card.setHolder("Cardinity Cardinity");
        payment.setPaymentInstrument(card);
        return payment;
    }

    private static Threeds2Data getThreeds2Data() {
        Threeds2Data threeds2Data = new Threeds2Data();
        threeds2Data.setNotificationUrl("http://notification.url");

        BrowserInfo browserInfo = new BrowserInfo();
        browserInfo.setAcceptHeader("text/html");
        browserInfo.setBrowserLanguage("en-US");
        browserInfo.setScreenWidth(1920);
        browserInfo.setScreenHeight(1040);
        browserInfo.setChallengeWindowSize(BrowserInfo.ChallengeWindowSize.SIZE_600X400);
        browserInfo.setUserAgent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:21.0) Gecko/20100101 Firefox/21.0");
        browserInfo.setColorDepth(24);
        browserInfo.setTimeZone(-60);
        browserInfo.setIpAddress("216.58.207.35");
        threeds2Data.setBrowserInfo(browserInfo);

        Address billingAddress = new Address();
        billingAddress.setAddressLine1("8239 Louie Street");
        billingAddress.setAddressLine2("line2");
        billingAddress.setAddressLine3("line3");
        billingAddress.setCity("Coltenburgh");
        billingAddress.setCountry("US");
        billingAddress.setPostalCode("84603");
        billingAddress.setState("DC");
        threeds2Data.setBillingAddress(billingAddress);

        Address deliveryAddress = new Address();
        deliveryAddress.setAddressLine1("Schallerallee 33");
        deliveryAddress.setCity("Ravensburg");
        deliveryAddress.setCountry("DE");
        deliveryAddress.setPostalCode("82940");
        threeds2Data.setDeliveryAddress(deliveryAddress);

        CardholderInfo cardholderInfo = new CardholderInfo();
        cardholderInfo.setEmailAddress("card.holder@example.com");
        cardholderInfo.setMobilePhoneNumber("+353209120599");
        cardholderInfo.setHomePhoneNumber("+353209174412");
        cardholderInfo.setWorkPhoneNumber("+353209134251");
        threeds2Data.setCardholderInfo(cardholderInfo);

        return threeds2Data;
    }

    @Test
    public void testCreateApprovedPayment() {
        Payment resultPayment = createApprovedPayment();
        assertEquals(APPROVED, resultPayment.getStatus());
        assertNotNull(resultPayment.getId());
        assertThat(resultPayment.getPaymentInstrument(), instanceOf(Card.class));
        assertFalse(resultPayment.getLive());
        assertEquals(TEST_PAYMENT_DESCRIPTION, resultPayment.getDescription());
        assertFalse(resultPayment.isThreedsV2());
        assertFalse(resultPayment.isThreedsV1());
    }

    @Test
    public void testCreateDeclinedPayment() {
        Payment payment = getBaseCCPayment();
        payment.setAmount(new BigDecimal(160));
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());

        Payment resultPayment = result.getItem();
        assertEquals(DECLINED, resultPayment.getStatus());
        assertNotNull(resultPayment.getId());
        assertNotNull(resultPayment.getError());
        assertThat(resultPayment.getPaymentInstrument(), instanceOf(Card.class));
        assertFalse(resultPayment.getLive());
        assertFalse(resultPayment.isThreedsV2());
        assertFalse(resultPayment.isThreedsV1());
    }

    @Test
    public void testCreateApprovedPaymentWithDescriptorSuffix() {
        Payment payment = getBaseCCPayment();
        payment.setStatementDescriptorSuffix("DS");
        Result<Payment> initialResult = client.createPayment(payment);
        assertTrue(initialResult.isValid());
        Payment resultPayment = initialResult.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());
        assertNotNull(resultPayment.getId());
        assertThat(resultPayment.getPaymentInstrument(), instanceOf(Card.class));
        assertFalse(resultPayment.getLive());
        assertEquals(TEST_PAYMENT_DESCRIPTION, resultPayment.getDescription());
        assertFalse(resultPayment.isThreedsV2());
        assertFalse(resultPayment.isThreedsV1());
    }

    @Test
    public void testApprovedThreeDSecurePaymentV1() {
        Payment payment = getBaseCCPayment();
        payment.setDescription("3d-pass");
        Payment resultPayment = createPendingPayment(payment, false);

        Result<Payment> result = client.finalizePayment(resultPayment.getId(), "3d-pass");
        assertTrue(result.isValid());
        Payment finalizedPayment = result.getItem();
        assertEquals(APPROVED, finalizedPayment.getStatus());
        assertEquals(resultPayment.getId(), finalizedPayment.getId());
    }

    @Test
    public void testDeclinedThreeDSecurePaymentV1() {
        Payment payment = getBaseCCPayment();
        payment.setDescription("3d-pass");
        Payment resultPayment = createPendingPayment(payment, false);

        Result<Payment> result = client.finalizePayment(resultPayment.getId(), "3d-fail");
        assertTrue(result.isValid());
        Payment finalizedPayment = result.getItem();
        assertEquals(DECLINED, finalizedPayment.getStatus());
        assertEquals(resultPayment.getId(), finalizedPayment.getId());
        assertNotNull(finalizedPayment.getError());
        assertTrue(finalizedPayment.getError().startsWith("3333"));
    }

    @Test
    public void testApprovedThreeDSecurePaymentV2() {
        Payment payment = getBaseCCPayment();
        payment.setDescription("3ds2-pass");
        payment.setThreeds2Data(getThreeds2Data());
        Payment resultPayment = createPendingPayment(payment, true);

        Result<Payment> finalizeResult = client.finalizePaymentV2(resultPayment.getId(), "3ds2-pass");
        assertTrue(finalizeResult.isValid());
        Payment finalizedPayment = finalizeResult.getItem();
        assertEquals(APPROVED, finalizedPayment.getStatus());
        assertEquals(resultPayment.getId(), finalizedPayment.getId());
    }

    @Test
    public void testApprovedThreeDSecurePaymentV2WithFallbackToV1OnFinalize() {
        Payment payment = getBaseCCPayment();
        ((Card)payment.getPaymentInstrument()).setPan("5454545454545454");
        payment.setDescription("3ds2-pass");
        payment.setThreeds2Data(getThreeds2Data());
        Payment resultPayment = createPendingPayment(payment, true);

        Result<Payment> finalizeResult = client.finalizePaymentV2(resultPayment.getId(), "3d-pass");
        assertTrue(finalizeResult.isValid());
        Payment finalizedPayment = finalizeResult.getItem();
        assertEquals(PENDING, finalizedPayment.getStatus());
        assertEquals(resultPayment.getId(), finalizedPayment.getId());
        assertFalse(finalizedPayment.isThreedsV2());
        assertTrue(finalizedPayment.isThreedsV1());

        finalizeResult = client.finalizePayment(resultPayment.getId(), "3d-pass");
        assertTrue(finalizeResult.isValid());
        finalizedPayment = finalizeResult.getItem();
        assertEquals(APPROVED, finalizedPayment.getStatus());
        assertEquals(resultPayment.getId(), finalizedPayment.getId());
    }

    @Test
    public void testApprovedThreeDSecurePaymentV2WithFallbackToV1OnCreate() {
        Payment payment = getBaseCCPayment();
        payment.setDescription("3d-pass");
        payment.setThreeds2Data(getThreeds2Data());
        Payment resultPayment = createPendingPayment(payment, false);

        Result<Payment> finalizeResult = client.finalizePayment(resultPayment.getId(), "3d-pass");
        assertTrue(finalizeResult.isValid());
        Payment finalizedPayment = finalizeResult.getItem();
        assertEquals(APPROVED, finalizedPayment.getStatus());
        assertEquals(resultPayment.getId(), finalizedPayment.getId());
    }

    @Test
    public void testCreateApprovedRecurringPayment() {
        Payment initialResultPayment = createApprovedPayment();
        Payment payment = getBaseCCPayment();
        payment.setAmount(new BigDecimal(20));
        payment.setPaymentMethod(Payment.Method.RECURRING);
        Recurring recurringPayment = new Recurring();
        recurringPayment.setPaymentId(initialResultPayment.getId());
        payment.setPaymentInstrument(recurringPayment);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        assertEquals(APPROVED, result.getItem().getStatus());
    }

    @Test
    public void testCreateDeclinedRecurringPayment() {
        Payment initialResultPayment = createApprovedPayment();
        Payment payment = getBaseCCPayment();
        payment.setAmount(new BigDecimal(160));
        payment.setPaymentMethod(Payment.Method.RECURRING);
        Recurring recurringPayment = new Recurring();
        recurringPayment.setPaymentId(initialResultPayment.getId());
        payment.setPaymentInstrument(recurringPayment);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        assertEquals(DECLINED, result.getItem().getStatus());
    }

    @Test
    public void testGetExistingPayment() {
        Payment initialResultPayment = createApprovedPayment();
        Result<Payment> result = client.getPayment(initialResultPayment.getId());
        assertTrue(result.isValid());

        Payment resultPayment = result.getItem();
        assertEquals(initialResultPayment.getId(), resultPayment.getId());
        assertEquals(initialResultPayment.getStatus(), resultPayment.getStatus());
    }

    @Test
    public void testGetNotExistingPayment() {
        UUID paymentId = UUID.fromString("00000000-0000-0000-0000-000000000000");
        Result<Payment> result = client.getPayment(paymentId);
        assertFalse(result.isValid());

        CardinityError cardinityError = result.getCardinityError();
        assertEquals(Integer.valueOf(404), cardinityError.getStatus());
        assertNotNull(cardinityError.getDetail());
        assertNotNull(cardinityError.getTitle());
        assertNotNull(cardinityError.getType());
    }

    @Test
    public void testGetPaymentsListWithLimit() {
        Result<List<Payment>> result = client.getPayments(2);
        assertTrue(result.isValid());
        assertEquals(2, result.getItem().size());
    }

    @Test
    public void testGetPaymentsList() {
        Result<List<Payment>> result = client.getPayments();
        assertTrue(result.isValid());
        assertEquals(10, result.getItem().size());
    }

    @Test
    public void testCreateApprovedRefund() {
        Payment payment = getBaseCCPayment();
        payment.setOrderId(TEST_ORDER_ID);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());
        Refund refund = new Refund();
        refund.setAmount(resultPayment.getAmount());
        refund.setDescription(TEST_REFUND_DESCRIPTION);
        Result<Refund> refundResult = client.createRefund(resultPayment.getId(), refund);
        assertTrue(refundResult.isValid());
        assertEquals(Refund.Status.APPROVED, refundResult.getItem().getStatus());
        assertEquals(TEST_REFUND_DESCRIPTION, refundResult.getItem().getDescription());
    }

    @Test
    public void testCreateDeclinedRefund() {
        Payment resultPayment = createApprovedPayment();
        Refund refund = new Refund(resultPayment.getAmount(), "fail");
        Result<Refund> refundResult = client.createRefund(resultPayment.getId(), refund);
        assertTrue(refundResult.isValid());
        assertEquals(Refund.Status.DECLINED, refundResult.getItem().getStatus());
        assertNotNull(refundResult.getItem().getError());
    }

    @Test
    public void testGetRefunds() {
        Payment resultPayment = createApprovedPayment();
        Refund refund = new Refund();
        refund.setAmount(resultPayment.getAmount());
        Result<Refund> initialRefundResult = client.createRefund(resultPayment.getId(), refund);
        assertTrue(initialRefundResult.isValid());
        assertEquals(Refund.Status.APPROVED, initialRefundResult.getItem().getStatus());
        Refund resultRefund = initialRefundResult.getItem();

        Result<Refund> refundResult = client.getRefund(resultPayment.getId(), resultRefund.getId());
        assertTrue(refundResult.isValid());
        assertEquals(resultRefund.getId(), refundResult.getItem().getId());
        assertEquals(resultRefund.getStatus(), refundResult.getItem().getStatus());

        Result<List<Refund>> refundsResult = client.getRefunds(resultPayment.getId());
        assertTrue(refundsResult.isValid());
        assertEquals(1, refundsResult.getItem().size());
    }

    @Test
    public void testCreateApprovedVoid() {
        Payment payment = getBaseCCPayment();
        payment.setSettle(false);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());

        Void voidP = new Void();
        voidP.setDescription(TEST_VOID_DESCRIPTION);
        Result<Void> voidResult = client.createVoid(resultPayment.getId(), voidP);
        assertTrue(voidResult.isValid());
        assertEquals(Void.Status.APPROVED, voidResult.getItem().getStatus());
        assertEquals(TEST_VOID_DESCRIPTION, voidResult.getItem().getDescription());
    }

    @Test
    public void testCreateDeclinedVoid() {
        Payment payment = getBaseCCPayment();
        payment.setSettle(false);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());

        Void voidP = new Void();
        voidP.setDescription("fail");
        Result<Void> voidResult = client.createVoid(resultPayment.getId(), voidP);
        assertTrue(voidResult.isValid());
        assertEquals(Void.Status.DECLINED, voidResult.getItem().getStatus());
        assertNotNull(voidResult.getItem().getError());
    }

    @Test
    public void testGetVoids() {
        Payment payment = getBaseCCPayment();
        payment.setSettle(false);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());

        Void initialVoid = new Void();
        Result<Void> initialVoidResult = client.createVoid(resultPayment.getId(), initialVoid);
        assertTrue(initialVoidResult.isValid());
        Void initialResultVoid = initialVoidResult.getItem();
        assertEquals(Void.Status.APPROVED, initialResultVoid.getStatus());

        Result<Void> voidResult = client.getVoid(resultPayment.getId(), initialResultVoid.getId());
        assertTrue(voidResult.isValid());
        assertEquals(initialResultVoid.getId(), voidResult.getItem().getId());
        assertEquals(initialResultVoid.getStatus(), voidResult.getItem().getStatus());

        Result<List<Void>> voidsResult = client.getVoids(resultPayment.getId());
        assertTrue(voidsResult.isValid());
        assertEquals(1, voidsResult.getItem().size());
    }

    @Test
    public void testCreateApprovedSettlement() {
        Payment payment = getBaseCCPayment();
        payment.setSettle(false);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());

        Settlement settlement = new Settlement();
        settlement.setAmount(new BigDecimal(9.99));
        settlement.setDescription(TEST_SETTLEMENT_DESCRIPTION);
        Result<Settlement> settlementResult = client.createSettlement(resultPayment.getId(), settlement);
        assertTrue(settlementResult.isValid());
        assertEquals(Settlement.Status.APPROVED, settlementResult.getItem().getStatus());
        assertEquals(TEST_SETTLEMENT_DESCRIPTION, settlementResult.getItem().getDescription());
    }

    @Test
    public void testCreateDeclinedSettlement() {
        Payment payment = getBaseCCPayment();
        payment.setSettle(false);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());

        Settlement settlement = new Settlement();
        settlement.setAmount(new BigDecimal(9.99));
        settlement.setDescription("fail");
        Result<Settlement> settlementResult = client.createSettlement(resultPayment.getId(), settlement);
        assertTrue(settlementResult.isValid());
        assertEquals(Settlement.Status.DECLINED, settlementResult.getItem().getStatus());
        assertNotNull(settlementResult.getItem().getError());
    }

    @Test
    public void testGetSettlements() {
        Payment payment = getBaseCCPayment();
        payment.setSettle(false);
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());

        Settlement initialSettlement = new Settlement();
        initialSettlement.setAmount(new BigDecimal(9.99));
        Result<Settlement> initialSettlementResult = client.createSettlement(resultPayment.getId(), initialSettlement);
        assertTrue(initialSettlementResult.isValid());
        Settlement initialResultSettlement = initialSettlementResult.getItem();
        assertEquals(Settlement.Status.APPROVED, initialResultSettlement.getStatus());

        Result<Settlement> settlementResult = client.getSettlement(resultPayment.getId(), initialResultSettlement.getId());
        assertTrue(settlementResult.isValid());
        assertEquals(initialResultSettlement.getId(), settlementResult.getItem().getId());
        assertEquals(initialResultSettlement.getStatus(), settlementResult.getItem().getStatus());

        Result<List<Settlement>> settlements = client.getSettlements(resultPayment.getId());
        assertTrue(settlements.isValid());
        assertEquals(1, settlements.getItem().size());
    }

    private Payment createApprovedPayment() {
        Payment payment = getBaseCCPayment();
        Result<Payment> initialResult = client.createPayment(payment);
        assertTrue(initialResult.isValid());
        Payment resultPayment = initialResult.getItem();
        assertEquals(APPROVED, resultPayment.getStatus());
        return resultPayment;
    }

    private Payment createPendingPayment(Payment payment, boolean v2) {
        Result<Payment> result = client.createPayment(payment);
        assertTrue(result.isValid());
        Payment resultPayment = result.getItem();
        assertEquals(PENDING, resultPayment.getStatus());
        assertEquals(v2, resultPayment.isThreedsV2());
        assertEquals(!v2, resultPayment.isThreedsV1());
        if (v2) {
            assertNotNull(resultPayment.getThreeds2AuthorizationInformation());
            assertNotNull(resultPayment.getThreeds2AuthorizationInformation().getAcsUrl());
            assertNotNull(resultPayment.getThreeds2AuthorizationInformation().getCReq());
            assertNull(resultPayment.getAuthorizationInformation());
        } else {
            assertNotNull(resultPayment.getAuthorizationInformation());
            assertNotNull(resultPayment.getAuthorizationInformation().getUrl());
            assertNotNull(resultPayment.getAuthorizationInformation().getData());
            assertNull(resultPayment.getThreeds2AuthorizationInformation());
        }
        return resultPayment;
    }
}


text:
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_SCRIPT_ISMINE_H
#define BITCOIN_SCRIPT_ISMINE_H

#include <script/standard.h>

#include <cstdint>

class CKeyStore;
class CScript;

/** IsMine() return codes */
enum isminetype {
    ISMINE_NO = 0,
    ISMINE_WATCH_ONLY = 1,
    ISMINE_SPENDABLE = 2,
    ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
};

/** used for bitflags of isminetype */
typedef uint8_t isminefilter;

/**
 * isInvalid becomes true when the script is found invalid by consensus or
 * policy. This will terminate the recursion and return ISMINE_NO immediately,
 * as an invalid script should never be considered as "mine". Currently the only
 * use of isInvalid is for P2SH-inside-P2SH scripts (as a technicality, to
 * prevent infinite recursion).
 */
isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey,
                  bool &isInvalid);
isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey);
isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest);

#endif // BITCOIN_SCRIPT_ISMINE_H


text:
package com.zrlog.common.vo;

import java.io.Serializable;

/**
 * 这个对应了 last.runType.json 里面的字段
 */
public class Version implements Serializable {

    private String buildId;
    private String releaseDate;
    private String version;
    private long fileSize;

    public long getZipFileSize() {
        return zipFileSize;
    }

    public void setZipFileSize(long zipFileSize) {
        this.zipFileSize = zipFileSize;
    }

    private long zipFileSize;
    private String changeLog;
    private String type;
    private String downloadUrl;

    public String getZipDownloadUrl() {
        return zipDownloadUrl;
    }

    public void setZipDownloadUrl(String zipDownloadUrl) {
        this.zipDownloadUrl = zipDownloadUrl;
    }

    private String zipDownloadUrl;
    private String md5sum;

    public String getZipMd5sum() {
        return zipMd5sum;
    }

    public void setZipMd5sum(String zipMd5sum) {
        this.zipMd5sum = zipMd5sum;
    }

    private String zipMd5sum;

    public String getBuildId() {
        return buildId;
    }

    public void setBuildId(String buildId) {
        this.buildId = buildId;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public void setReleaseDate(String releaseDate) {
        this.releaseDate = releaseDate;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public long getFileSize() {
        return fileSize;
    }

    public void setFileSize(long fileSize) {
        this.fileSize = fileSize;
    }

    public String getChangeLog() {
        return changeLog;
    }

    public void setChangeLog(String changeLog) {
        this.changeLog = changeLog;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDownloadUrl() {
        return downloadUrl;
    }

    public void setDownloadUrl(String downloadUrl) {
        this.downloadUrl = downloadUrl;
    }

    public String getMd5sum() {
        return md5sum;
    }

    public void setMd5sum(String md5sum) {
        this.md5sum = md5sum;
    }

}


text:
package com.kidappolis.cronjobs;

import android.app.Service;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;

import com.kidappolis.constants.LogTags;
import com.kidappolis.database.AppsInfoDatasource;
import com.kidappolis.database.DeviceUseInfo;

import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * Class that collects device usage statistics (a.k.a usage time, how much time device has been up or not...)
 * and save it in the device cache
 */
public class StatsCollector extends Service {

    private static final String TAG = StatsCollector.class.getSimpleName();

    public StatsCollector() {}

    Intent intent;

    UsageStatsManager mUsageStatsManager;
    AppsInfoDatasource appsInfoDatasource;

    @Override
    public IBinder onBind(Intent intent) {
        this.intent = intent;

        return null;
    }

    @Override
    public void onCreate() {
        Log.d("sync lumen", "Stats collector service started");

        appsInfoDatasource = new AppsInfoDatasource(getApplicationContext());
        if (Build.VERSION.SDK_INT < 22) {
            mUsageStatsManager = (UsageStatsManager) getSystemService("usagestats");

        } else {
            mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
        }

        HashMap<String, UsageStats> usageStatsMap = getUsageStatsMap();

        try {
            appsInfoDatasource.open();

            // create entry for device usages
            DeviceUseInfo deviceUseInfo = new DeviceUseInfo();
            deviceUseInfo.day = Calendar.getInstance().get(Calendar.DAY_OF_YEAR);
            deviceUseInfo.elapsedRealtime = SystemClock.elapsedRealtime();
            deviceUseInfo.uptime = SystemClock.uptimeMillis();
            deviceUseInfo.timestamp = System.currentTimeMillis();
            appsInfoDatasource.createDeviceEntry(deviceUseInfo);

            // create entries for application usages
            for (String name : usageStatsMap.keySet()) {
                UsageStats usageStat = usageStatsMap.get(name);
                if (usageStat.getLastTimeUsed() != 0) {
                    Log.i("stats collector: ", "name: " + usageStat.getPackageName() + " last time used: " + String.valueOf(usageStat.getLastTimeUsed()));
                    appsInfoDatasource.createAppEntry(usageStat.getPackageName(), String.valueOf(usageStat.getLastTimeUsed()),
                            usageStat.getTotalTimeInForeground(), new Date().getTime());
                }

            }

        } catch (SQLException e) {
            Log.e(LogTags.APP_EXCEPTION.name(), e.getMessage());
            finish();
        }
        finally {
            appsInfoDatasource.close();
        }

        Log.i(LogTags.APP_INFO.name(), "Service stopped");
        finish();

    }

    private void finish() {
        if (intent != null) {
            StatsCollectionAlarmReceiver.completeWakefulIntent(intent);
        }

        stopSelf();
    }

    @Override
    public void onDestroy() {
        appsInfoDatasource.close();
    }

    private HashMap<String, UsageStats> getUsageStatsMap() {
        Calendar cal = Calendar.getInstance();

        cal.add(Calendar.DAY_OF_YEAR, -1);
//        cal.set(Calendar.MINUTE, 0);
//        cal.set(Calendar.SECOND, 0);
//        cal.set(Calendar.HOUR_OF_DAY, 0);

        List<UsageStats> queryUsageStats = mUsageStatsManager
                .queryUsageStats(UsageStatsManager.INTERVAL_DAILY, cal.getTimeInMillis(),
                        System.currentTimeMillis());
        Log.i("sync lumen", "usage stats amount... " + queryUsageStats.size());

        HashMap<String, UsageStats> usageStatsMap = new HashMap<>();
        for (UsageStats qsm : queryUsageStats) {
            usageStatsMap.put(qsm.getPackageName(), qsm);
            Log.i("sync lumen", "last time used " + qsm.getLastTimeUsed());
        }

        return usageStatsMap;
    }
}


text:
<module xmlns="urn:jboss:module:1.3" name="org.wildfly.swarm.connector" slot="api">
  <resources>
    <artifact name="org.wildfly.swarm:connector:${project.version}"/>
  </resources>

  <dependencies>
    <module name="org.wildfly.swarm.container"/>
  </dependencies>
</module>


text:
package se.nimsa.dcm4che.streams

import akka.NotUsed
import akka.stream.scaladsl.{Flow, Source}
import akka.util.ByteString
import org.dcm4che3.io.DicomStreamException
import se.nimsa.dcm4che.streams.DicomParts._
import se.nimsa.dcm4che.streams.TagPath.{TagPathSequence, TagPathTag}

/**
  * This class defines events for modular construction of DICOM flows. Events correspond to the DICOM parts commonly
  * encountered in a stream of DICOM data. Subclasses and/or traits add functionality by overriding and implementing
  * events. The stream entering the stage can be modified as well as the mapping from DICOM part to event.
  */
abstract class DicomFlow {

  def onPreamble(part: DicomPreamble): List[DicomPart]
  def onHeader(part: DicomHeader): List[DicomPart]
  def onValueChunk(part: DicomValueChunk): List[DicomPart]
  def onSequenceStart(part: DicomSequence): List[DicomPart]
  def onSequenceEnd(part: DicomSequenceDelimitation): List[DicomPart]
  def onFragmentsStart(part: DicomFragments): List[DicomPart]
  def onFragmentsEnd(part: DicomFragmentsDelimitation): List[DicomPart]
  def onSequenceItemStart(part: DicomSequenceItem): List[DicomPart]
  def onSequenceItemEnd(part: DicomSequenceItemDelimitation): List[DicomPart]
  def onFragmentsItemStart(part: DicomFragmentsItem): List[DicomPart]
  def onDeflatedChunk(part: DicomDeflatedChunk): List[DicomPart]
  def onUnknownPart(part: DicomUnknownPart): List[DicomPart]
  def onPart(part: DicomPart): List[DicomPart]

  /**
    * @return the flow of `DicomPart`s entering the stage
    */
  def baseFlow: Flow[DicomPart, DicomPart, NotUsed] = Flow[DicomPart]

  /**
    * This method defines the mapping from `DicomPart` to event used to create the stage, see 'DicomFlowFactory.create'.
    *
    * @param dicomPart the incoming 'DicomPart'
    * @return the `List` of `DicomPart`s
    */
  def handlePart(dicomPart: DicomPart): List[DicomPart] = dicomPart match {
    case part: DicomPreamble => onPreamble(part)
    case part: DicomHeader => onHeader(part)
    case part: DicomValueChunk => onValueChunk(part)
    case part: DicomSequence => onSequenceStart(part)
    case part: DicomSequenceDelimitation => onSequenceEnd(part)
    case part: DicomFragments => onFragmentsStart(part)
    case part: DicomFragmentsDelimitation => onFragmentsEnd(part)
    case part: DicomSequenceItem => onSequenceItemStart(part)
    case part: DicomSequenceItemDelimitation => onSequenceItemEnd(part)
    case part: DicomFragmentsItem => onFragmentsItemStart(part)
    case part: DicomDeflatedChunk => onDeflatedChunk(part)
    case part: DicomUnknownPart => onUnknownPart(part)
    case part => onPart(part)
  }
}

/**
  * Basic implementation of events where DICOM parts are simply passed on downstream. When implementing new flows,
  * this trait is most often included as the first mixin to provide basic behavior which is then overridden for the
  * appropriate events.
  */
class IdentityFlow extends DicomFlow {
  def onPreamble(part: DicomPreamble): List[DicomPart] = part :: Nil
  def onHeader(part: DicomHeader): List[DicomPart] = part :: Nil
  def onValueChunk(part: DicomValueChunk): List[DicomPart] = part :: Nil
  def onSequenceStart(part: DicomSequence): List[DicomPart] = part :: Nil
  def onSequenceEnd(part: DicomSequenceDelimitation): List[DicomPart] = part :: Nil
  def onFragmentsStart(part: DicomFragments): List[DicomPart] = part :: Nil
  def onFragmentsEnd(part: DicomFragmentsDelimitation): List[DicomPart] = part :: Nil
  def onDeflatedChunk(part: DicomDeflatedChunk): List[DicomPart] = part :: Nil
  def onUnknownPart(part: DicomUnknownPart): List[DicomPart] = part :: Nil
  def onPart(part: DicomPart): List[DicomPart] = part :: Nil
  def onSequenceItemStart(part: DicomSequenceItem): List[DicomPart] = part :: Nil
  def onSequenceItemEnd(part: DicomSequenceItemDelimitation): List[DicomPart] = part :: Nil
  def onFragmentsItemStart(part: DicomFragmentsItem): List[DicomPart] = part :: Nil
}

/**
  * Similar implementation to `IdentityFlow` with the difference that all events forward to the `onPart` event. Useful for
  * simple filters which implement a common behavior for all DICOM parts. This implementation is then provided in the
  * `onPart` method.
  */
abstract class DeferToPartFlow extends DicomFlow {
  def onPreamble(part: DicomPreamble): List[DicomPart] = onPart(part)
  def onHeader(part: DicomHeader): List[DicomPart] = onPart(part)
  def onValueChunk(part: DicomValueChunk): List[DicomPart] = onPart(part)
  def onSequenceStart(part: DicomSequence): List[DicomPart] = onPart(part)
  def onSequenceEnd(part: DicomSequenceDelimitation): List[DicomPart] = onPart(part)
  def onFragmentsStart(part: DicomFragments): List[DicomPart] = onPart(part)
  def onFragmentsEnd(part: DicomFragmentsDelimitation): List[DicomPart] = onPart(part)
  def onDeflatedChunk(part: DicomDeflatedChunk): List[DicomPart] = onPart(part)
  def onUnknownPart(part: DicomUnknownPart): List[DicomPart] = onPart(part)
  def onSequenceItemStart(part: DicomSequenceItem): List[DicomPart] = onPart(part)
  def onSequenceItemEnd(part: DicomSequenceItemDelimitation): List[DicomPart] = onPart(part)
  def onFragmentsItemStart(part: DicomFragmentsItem): List[DicomPart] = onPart(part)
  def onPart(part: DicomPart): List[DicomPart]
}

/**
  * This mixin adds an event marking the start of the DICOM stream. It does not add DICOM parts to the stream.
  */
trait StartEvent extends DicomFlow {
  def onStart(): List[DicomPart] = Nil

  override def baseFlow: Flow[DicomPart, DicomPart, NotUsed] =
    super.baseFlow.prepend(Source.single(DicomStartMarker)) // add marker to start of stream

  override def handlePart(dicomPart: DicomPart): List[DicomPart] = dicomPart match {
    case DicomStartMarker => onStart() // call event, do not emit marker
    case part => super.handlePart(part)
  }
}

/**
  * This mixin adds an event marking the end of the DICOM stream. It does not add DICOM parts to the stream.
  */
trait EndEvent extends DicomFlow {
  def onEnd(): List[DicomPart] = Nil

  override def baseFlow: Flow[DicomPart, DicomPart, NotUsed] =
    super.baseFlow.concat(Source.single(DicomEndMarker)) // add marker to end of stream

  override def handlePart(dicomPart: DicomPart): List[DicomPart] = dicomPart match {
    case DicomEndMarker => onEnd() // call event, do not emit marker
    case part => super.handlePart(part)
  }
}

object DicomValueChunkMarker extends DicomValueChunk(bigEndian = false, ByteString.empty, last = true)

/**
  * This mixin makes sure the `onValueChunk` event is called also for empty attributes. This special case requires
  * special handling since empty attributes consist of a `DicomHeader`, but is not followed by a `DicomValueChunk`.
  */
trait GuaranteedValueEvent extends DicomFlow {

  abstract override def onHeader(part: DicomHeader): List[DicomPart] =
    if (part.length == 0)
      super.onHeader(part) ::: onValueChunk(DicomValueChunkMarker)
    else
      super.onHeader(part)

  abstract override def onValueChunk(part: DicomValueChunk): List[DicomPart] =
    super.onValueChunk(part).filterNot(_ == DicomValueChunkMarker)
}

object DicomSequenceDelimitationMarker extends DicomSequenceDelimitation(bigEndian = false, ByteString.empty)

class DicomSequenceItemDelimitationMarker(item: Int) extends DicomSequenceItemDelimitation(item, bigEndian = false, ByteString.empty)

/**
  * By mixing in this trait, sequences and items with determinate length will be concluded by delimitation events, just
  * as is the case with sequences and items with indeterminate length and which are concluded by delimitation parts. This
  * makes it easier to create DICOM flows that react to the beginning and ending of sequences and items, as no special
  * care has to be taken for DICOM data with determinate length sequences and items.
  */
trait GuaranteedDelimitationEvents extends DicomFlow {
  var partStack: List[(LengthPart, Long)] = Nil

  def subtractLength(part: DicomPart): List[(LengthPart, Long)] =
    partStack.map {
      case (item: DicomSequenceItem, bytesLeft) => (item, bytesLeft - part.bytes.length)
      case (sequence, bytesLeft) => (sequence, bytesLeft - part.bytes.length)
    }

  def maybeDelimit(): List[DicomPart] = {
    val delimits: List[DicomPart] = partStack
      .filter(_._2 <= 0) // find items and sequences that have ended
      .map { // create delimiters for those
      case (item: DicomSequenceItem, _) => new DicomSequenceItemDelimitationMarker(item.index)
      case (_, _) => DicomSequenceDelimitationMarker
    }
    partStack = partStack.filter(_._2 > 0) // only keep items and sequences with bytes left to subtract
    delimits.flatMap { // call events, any items will be inserted in stream
      case d: DicomSequenceDelimitation => onSequenceEnd(d)
      case d: DicomSequenceItemDelimitation => onSequenceItemEnd(d)
    }
  }

  def subtractAndEmit[A <: DicomPart](part: A, handle: A => List[DicomPart]): List[DicomPart] = {
    partStack = subtractLength(part)
    handle(part) ::: maybeDelimit()
  }

  abstract override def onSequenceStart(part: DicomSequence): List[DicomPart] =
    if (part.hasLength) {
      partStack = (part, part.length) +: subtractLength(part)
      super.onSequenceStart(part) ::: maybeDelimit()
    } else subtractAndEmit(part, super.onSequenceStart)

  abstract override def onSequenceItemStart(part: DicomSequenceItem): List[DicomPart] =
    if (part.hasLength) {
      partStack = (part, part.length) +: subtractLength(part)
      super.onSequenceItemStart(part) ::: maybeDelimit()
    } else subtractAndEmit(part, super.onSequenceItemStart)

  abstract override def onSequenceEnd(part: DicomSequenceDelimitation): List[DicomPart] =
    subtractAndEmit(part, (p: DicomSequenceDelimitation) =>
      super.onSequenceEnd(p).filterNot(_ == DicomSequenceDelimitationMarker))

  abstract override def onSequenceItemEnd(part: DicomSequenceItemDelimitation): List[DicomPart] =
    subtractAndEmit(part, (p: DicomSequenceItemDelimitation) =>
      super.onSequenceItemEnd(p).filterNot(_.isInstanceOf[DicomSequenceItemDelimitationMarker]))

  abstract override def onHeader(part: DicomHeader): List[DicomPart] = subtractAndEmit(part, super.onHeader)
  abstract override def onValueChunk(part: DicomValueChunk): List[DicomPart] = subtractAndEmit(part, super.onValueChunk)
  abstract override def onFragmentsStart(part: DicomFragments): List[DicomPart] = subtractAndEmit(part, super.onFragmentsStart)
  abstract override def onFragmentsItemStart(part: DicomFragmentsItem): List[DicomPart] = subtractAndEmit(part, super.onFragmentsItemStart)
  abstract override def onFragmentsEnd(part: DicomFragmentsDelimitation): List[DicomPart] = subtractAndEmit(part, super.onFragmentsEnd)
}

/**
  * This mixin keeps track of the current tag path as the stream moves through attributes, sequences and fragments. The
  * tag path state is updated in the event callbacks. This means that implementations of events using this mixin must
  * remember to call the corresponding super method for the tag path to update.
  */
trait TagPathTracking extends DicomFlow with GuaranteedValueEvent with GuaranteedDelimitationEvents {

  protected var tagPath: Option[TagPath] = None
  protected var inFragments = false

  abstract override def onHeader(part: DicomHeader): List[DicomPart] = {
    tagPath = tagPath.map {
      case t: TagPathTag => throw new DicomStreamException(s"Unexpected attribute ${tagToString(part.tag)} in tag path $t.")
      case s: TagPathSequence => s.thenTag(part.tag)
    }.orElse(Some(TagPath.fromTag(part.tag)))
    super.onHeader(part)
  }

  abstract override def onValueChunk(part: DicomValueChunk): List[DicomPart] = {
    if (part.last && !inFragments)
      tagPath = tagPath.flatMap(_.previous)
    super.onValueChunk(part)
  }

  abstract override def onSequenceStart(part: DicomSequence): List[DicomPart] = {
    tagPath = tagPath.map {
      case t: TagPathTag => throw new DicomStreamException(s"Unexpected sequence ${tagToString(part.tag)} in tag path $t.")
      case s: TagPathSequence => s.thenSequence(part.tag)
    }.orElse(Some(TagPath.fromSequence(part.tag)))
    super.onSequenceStart(part)
  }

  abstract override def onSequenceEnd(part: DicomSequenceDelimitation): List[DicomPart] = {
    tagPath = tagPath.flatMap {
      case t: TagPathTag => throw new DicomStreamException(s"Unexpected end of sequence in tag path $t.")
      case s: TagPathSequence => s.previous
    }
    super.onSequenceEnd(part)
  }

  abstract override def onFragmentsStart(part: DicomFragments): List[DicomPart] = {
    inFragments = true
    tagPath = tagPath.map {
      case t: TagPathTag => throw new DicomStreamException(s"Unexpected fragments ${tagToString(part.tag)} in tag path $t.")
      case s: TagPathSequence => s.thenTag(part.tag)
    }.orElse(Some(TagPath.fromTag(part.tag)))
    super.onFragmentsStart(part)
  }

  abstract override def onFragmentsEnd(part: DicomFragmentsDelimitation): List[DicomPart] = {
    inFragments = false
    tagPath = tagPath.flatMap {
      case t: TagPathTag => t.previous.flatMap(_.previous)
      case s: TagPathSequence => throw new DicomStreamException(s"Unexpected end of fragments in tag path $s.")
    }
    super.onFragmentsEnd(part)
  }

  abstract override def onSequenceItemStart(part: DicomSequenceItem): List[DicomPart] = {
    tagPath = tagPath.map {
      case t: TagPathTag => throw new DicomStreamException(s"Unexpected item with index ${part.index} in tag path $t.")
      case s: TagPathSequence => s.previous.map(_.thenSequence(s.tag, part.index)).getOrElse(TagPath.fromSequence(s.tag, part.index))
    }
    super.onSequenceItemStart(part)
  }

  abstract override def onSequenceItemEnd(part: DicomSequenceItemDelimitation): List[DicomPart] = {
    tagPath = tagPath.flatMap {
      case t: TagPathTag => throw new DicomStreamException(s"Unexpected end of item in tag path $t.")
      case s: TagPathSequence => s.previous.map(_.thenSequence(s.tag)).orElse(Some(TagPath.fromSequence(s.tag)))
    }
    super.onSequenceItemEnd(part)
  }
}

/**
  * Provides factory methods for creating `DicomFlow`s
  */
object DicomFlowFactory {

  def create(flow: DicomFlow): Flow[DicomPart, DicomPart, NotUsed] = flow.baseFlow.mapConcat(flow.handlePart)

}