PROGRAM cluster
!--------------------------------------------------------!
! Example Molecular Dynamics Program ver.2.2 !
! !
! [プログラム概要] !
! ・ヴェルレ法による時間発展(数値積分) !
! ・N粒子孤立系に対するNVEアンサンブル !
! ・Lennard-Jones (12-6) ポテンシャル !
! !
! [改訂履歴] !
! 2002.10.05 ver 1.0 岡田 勇 !
! 2011.06.08 ver 2.0 北 幸海 (Fortran 90化) !
! 2020.12.14 ver 2.1 北 幸海 (単純化) !
! 2020.12.15 ver 2.2 北 幸海 (ウェブ実習用に標準出力化) !
!--------------------------------------------------------!
IMPLICIT NONE
!----- 固定変数 (変更しないこと) -----
INTEGER, PARAMETER :: &
NpTot = 2 ! 粒子数
REAL(8), PARAMETER :: &
Eps = 1.d0, & ! L-Jポテンシャルのパラメータ1
Sigma = 1.d0, & ! L-Jポテンシャルのパラメータ2
Mass = 1.d0 ! 粒子の質量
!----- ユーザー変数 (課題に応じて変更する変数) -----
! Dt: 時間ステップ
! MDStep: ステップ数(繰り返しの回数)
! --> Dt = 1.d-2〜1.d-3が適当. Dt*MDStep= 1〜3 とする.
! --> サーバーに負荷をかけないよう Dt ≧ 1.d-5 とする
INTEGER, PARAMETER :: MDStep = 2000 ! 総ステップ数
REAL(8), PARAMETER :: Dt = 1.d-3 ! 時間ステップ
REAL(8), PARAMETER :: R2_ini = 0.56d0 ! 粒子2の初期位置
REAL(8), PARAMETER :: V2_ini = -0.2d0 ! 粒子2の初速
INTEGER, PARAMETER :: NOut = 100 ! 出力データ数(MDStep以下で100を超えない整数)
!----- 以下の変数・配列はプログラム内で自動更新 -----
INTEGER i
INTEGER :: NSum = 0, & ! 蓄積の回数
n = 0, & ! 現在のステップ数
PrintInt = 1 ! 出力間隔
REAL(8) :: &
R0(3, NpTot) = 0.d0, & ! 初期位置
V(3, NpTot) = 0.d0, & ! 速度
R(3, NpTot) = 0.d0, & ! 位置
dR(3, NpTot) = 0.d0, & ! 初期位置からの変位
dR_prev(3, NpTot) = 0.d0, & ! 時刻t(n-1)とt(n)間の変位
dR_next(3, NpTot) = 0.d0, & ! 時刻t(n)とt(n+1)間の変位
F(3, NpTot) = 0.d0, & ! 力
T = 0.d0, & ! 運動エネルギー
P = 0.d0, & ! ポテンシャルエネルギー
H = 0.d0, & ! 全エネルギー(ハミルトニアン)
H0 = 0.d0, & ! 計算開始時の全エネルギー
V0 = 0.d0, & ! 計算開始時の平均速度
MaxErrH = 0.d0, & ! ハミルトニアンの最大誤差
SumH = 0.d0, & ! 蓄積されたハミルトニアン
SumH2 = 0.d0, & ! 蓄積されたハミルトニアンの二乗
SumT = 0.d0, & ! 蓄積された運動エネルギー
SumT2 = 0.d0 ! 蓄積された運動エネルギーの二乗
!----- Safety net -----
if (Dt*MDStep > 3.d0) then
write(6,*) 'Too long simulation time !!'
stop
endif
!----- 各種設定値の出力 -----
PrintInt = MDStep/NOut
write(6,*) '=============================='
write(6,*) 'MD simulation by Verlet method'
write(6,*) '=============================='
write(6,*) ' # of particles = ', NpTot
write(6,*) ' L-J parameters:'
write(6,*) ' --> Epsilon = ', Eps
write(6,*) ' --> Sigma = ', Sigma
write(6,*) ' Mass of particle = ', Mass
write(6,*) ' Time step = ', Dt
write(6,*) ' # of MD steps = ', MDStep
write(6,*) ' Simulation time = ', Dt*real(MDStep,8)
write(6,*) ' Print interval = ', Dt*real(PrintInt,8)
write(6,*)
!----- 粒子の初期情報の設定 -----
! 初期位置
R0(1,2) = R2_ini ! 粒子1
R0(1,1) = -R0(1,2) ! 粒子2
! 初速
V(1,2) = V2_ini ! 粒子1
V(1,1) = -V(1,2) ! 粒子2
! 初速度の大きさの平均値
V0= 0.d0
do i=1, NpTot
V0= V0 + V(1,i)**2 + V(2,i)**2 + V(3,i)**2
enddo ! i
V0= sqrt(V0/real(NpTot,8))
!----- 0ステップ目での力の計算 -----
call ForcePotential (NpTot, n, MDStep, R0, dR, Eps, Sigma, P, F)
!----- 1ステップ目の座標を計算 -----
call Verlet (NpTot, n, MDStep, NSum, Dt, Mass, R0, P, R, F, V, &
dR, dR_prev, dR_next, H, T, SumH, SumH2, SumT, SumT2)
!----- ハミルトニアンの初期値を保存 -----
H0 = H
!----- 出力 -----
! ヘッダー情報の出力
write(6,'(a)') '#time, position, velocity, kinetic, potential, hamiltonian'
! 位置、速度などの出力.
call Output (0, PrintInt, NpTot, n, MDStep, NSum, Dt, R, H, T, P, V, &
H0, V0, SumH, SumH2, SumT, SumT2, MaxErrH)
!----- 2ステップ目以降の時間発展 -----
do n= 1, MDStep
! nステップ目での力の計算
call ForcePotential (NpTot, n, MDStep, R0, dR, Eps, Sigma, P, F)
! (n+1)ステップ目の座標を計算
call Verlet (NpTot, n, MDStep, NSum, Dt, Mass, R0, P, R, F, V, &
dR, dR_prev, dR_next, H, T, SumH, SumH2, SumT, SumT2)
! 出力
call Output (0, PrintInt, NpTot, n, MDStep, NSum, Dt, R, H, T, P, V, &
H0, V0, SumH, SumH2, SumT, SumT2, MaxErrH)
enddo ! n
!----- 各種平均値を出力 -----
call Output (1, PrintInt, NpTot, n, MDStep, NSum, Dt, R, H, T, P, V, &
H0, V0, SumH, SumH2, SumT, SumT2, MaxErrH)
write(6,*) ' Done.'
write(6,*)
!----- 主プログラムの終了 -----
END PROGRAM cluster
SUBROUTINE ForcePotential(NpTot, n, MDStep, R0, dR, Eps, Sigma, P, F)
!----------------------------------!
! ポテンシャルエネルギーと力の計算 !
!----------------------------------!
IMPLICIT NONE
INTEGER, INTENT(in) :: NpTot, n, MDStep
REAL(8), INTENT(in) :: R0(3,NpTot), dR(3,NpTot), Eps, Sigma
REAL(8), INTENT(inout) :: P, F(3,NpTot)
! Local stuff
INTEGER i, j
REAL(8) R1, R2, Rij(3), dpdr, drdv(3)
F(:,:)=0.d0 ; P=0.d0
do i= 1, NpTot
do j= 1, NpTot
if (i /= j) then
! dR: displacement from time 0 to time n
Rij(:) = (dR(:,j) - dR(:,i)) + (R0(:,j) - R0(:,i))
R2 = Rij(1)**2 + Rij(2)**2 + Rij(3)**2
R1 = sqrt(R2)
! potential energy
P = P + 4.d0 * Eps * ((Sigma**2/R2)**6 - (Sigma**2/R2)**3)
! force
dpdr = 4.d0 * Eps * (-12.d0*(Sigma**2/R2)**6 + 6.d0*(Sigma**2/R2)**3) / R1
drdv(:) = -Rij(:) / R1
F(:,i) = F(:,i) - dpdr*drdv(:)
endif ! i /= j
enddo ! j
enddo ! i
P = 0.5d0*P
return
END SUBROUTINE ForcePotential
SUBROUTINE Verlet(NpTot, n, MDStep, NSum, Dt, Mass, R0, P, R, F, V, &
dR, dR_prev, dR_next, H, T, SumH, SumH2, SumT, SumT2)
!--------------------------!
! Verlet法による座標の更新 !
!--------------------------!
IMPLICIT NONE
INTEGER, INTENT(in) :: NpTot, n, MDStep
INTEGER, INTENT(inout) :: NSum
REAL(8), INTENT(in) :: R0(3,NpTot), P, Dt, Mass
REAL(8), INTENT(inout) :: R(3,NpTot), F(3,NpTot), V(3,NpTot), dR(3,NpTot), &
dR_prev(3,NpTot), dR_next(3,NpTot), H, T, SumH, &
SumH2, SumT, SumT2
! Local stuff
INTEGER i
!----- 0-th step -----
if (n == 0) then
! current position
R(:,:) = R0(:,:)
! dR = dR_next = R(Δt) - R(0) = V(0)Δt + a(0)*(Δt)^2/2
do i= 1, NpTot
dR_next(:,i) = V(:,i)*Dt + 0.5d0*F(:,i)*Dt**2/Mass
dR(:,i) = dR_next(:,i)
enddo ! i
!----- later steps -----
elseif (n >= 1) then
! current position
R(:,:) = R0(:,:) + dR(:,:)
! dR_next = R(t+Δt) - R(t) = R(t) - R(t-Δt) + a(t)*(Δt)^2
! dR_prev = R(t) - R(t-Δt)
! dR = R(t+Δt) - R(0)
do i= 1, NpTot
dR_next(:,i) = dR_prev(:,i) + F(:,i)*Dt**2/Mass
dR(:,i) = dR(:,i) + dR_next(:,i)
V(:,i) = 0.5d0 * (dR_next(:,i) + dR_prev(:,i)) / Dt
enddo
endif
!----- Renaming for use at the next step -----
dR_prev(:,:)= dR_next(:,:)
!----- 運動エネルギーの計算 -----
T = 0.d0
do i= 1, NpTot
T = T + 0.5d0 * Mass * (V(1,i)**2 + V(2,i)**2 + V(3,i)**2)
enddo
!----- ハミルトニアンの計算 -----
H = T + P
!----- 蓄積 -----
NSum = NSum + 1 ! 蓄積の回数
SumH = SumH + H ! ハミルトニアン
SumH2 = SumH2 + H**2 ! ハミルトニアンの2乗
SumT = SumT + T ! 運動エネルギー
SumT2 = SumT2 + T**2 ! 運動エネルギーの2乗
return
END SUBROUTINE Verlet
SUBROUTINE Output(mode, PrintInt, NpTot, n, MDStep, NSum, Dt, R, H, T, P, V, H0, V0, &
SumH, SumH2, SumT, SumT2, MaxErrH)
!------------!
! 結果の出力 !
!------------!
IMPLICIT NONE
INTEGER, INTENT(in) :: mode, PrintInt, NpTot, n, MDStep, NSum
REAL(8), INTENT(in) :: Dt, R(3,NpTot), H, T, P, V(3,NpTot), H0, V0, SumH, SumH2, SumT, SumT2
REAL(8), INTENT(inout) :: MaxErrH
! Local stuff
REAL(8) time, AveH, AveH2, AveT, AveT2, RMSD_H, RMSD_T
if (mode == 0) then
!----- Output data at the current time -----
time = Dt*real(n,8)
if ( (n==MDStep) .or. (mod(n,PrintInt)==0) ) &
write(6, '( e12.6, 4(e14.6), e23.15 )') time, R(1,2), V(1,2), T, P, H
MaxErrH= max(MaxErrH, abs(H-H0)) ! Maximum error in Hamiltonian
elseif (mode == 1) then
!----- Compute root mean square deviation (RMSD) -----
! compute averages
AveH = SumH /real(NSum,8) ! ハミルトニアン
AveH2 = SumH2/real(NSum,8) ! ハミルトニアンの2乗
AveT = SumT /real(NSum,8) ! 運動エネルギー
AveT2 = SumT2/real(NSum,8) ! 運動エネルギーの2乗
! compute RMSD
RMSD_H = sqrt(abs(AveH2-AveH**2))
RMSD_T = sqrt(abs(AveT2-AveT**2))
! print
write(6, *)
write(6, *) '---------'
write(6, *) ' Summary '
write(6, *) '---------'
write(6, '( a, e23.15 )') 'Dt =', Dt
write(6, '( a, e23.15 )') 'V0 =', V0
write(6, '( a, e23.15 )') 'RMSD(H) =', RMSD_H
write(6, '( a, e23.15 )') 'Max. err.(H) =', MaxErrH
write(6, '( a, e23.15 )') 'Final pos. =', R(1,2)
write(6, '( 2(a, e23.15) )') '<H>=', AveH, ' +- ', RMSD_H
write(6, '( 2(a, e23.15) )') '<T>=', AveT, ' +- ', RMSD_T
write(6, '( a, i10 )') 'Norm. const.(NSum)= ', NSum
endif
return
END SUBROUTINE Output
UFJPR1JBTSBjbHVzdGVyCiEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSEKISBFeGFtcGxlIE1vbGVjdWxhciBEeW5hbWljcyBQcm9ncmFtIHZlci4yLjIgICAgICAgICAgICAgIQohICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhCiEgW+ODl+ODreOCsOODqeODoOamguimgV0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhCiEgIOODu+ODtOOCp+ODq+ODrOazleOBq+OCiOOCi+aZgumWk+eZuuWxle+8iOaVsOWApOepjeWIhu+8iSAgICAgICAgICAgICAgICAhCiEgIOODu++8rueykuWtkOWtpOeri+ezu+OBq+WvvuOBmeOCi05WReOCouODs+OCteODs+ODluODqyAgICAgICAgICAgICAgICAgIQohICDjg7tMZW5uYXJkLUpvbmVzICgxMi02KSDjg53jg4bjg7Pjgrfjg6Pjg6sgICAgICAgICAgICAgICAgICAgIQohICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhCiEgW+aUueioguWxpeattF0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhCiEgIDIwMDIuMTAuMDUgdmVyIDEuMCDlsqHnlLAg5YuHICAgICAgICAgICAgICAgICAgICAgICAgICAgICEKISAgMjAxMS4wNi4wOCB2ZXIgMi4wIOWMlyDlubjmtbcgKEZvcnRyYW4gOTDljJYpICAgICAgICAgICAgICEKISAgMjAyMC4xMi4xNCB2ZXIgMi4xIOWMlyDlubjmtbcgKOWNmOe0lOWMlikgICAgICAgICAgICAgICAgICAgIQohICAyMDIwLjEyLjE1IHZlciAyLjIg5YyXIOW5uOa1tyAo44Km44Kn44OW5a6f57+S55So44Gr5qiZ5rqW5Ye65Yqb5YyWKSAhCiEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSEKSU1QTElDSVQgTk9ORQoKIS0tLS0tIOWbuuWumuWkieaVsCAo5aSJ5pu044GX44Gq44GE44GT44GoKSAtLS0tLQpJTlRFR0VSLCBQQVJBTUVURVIgOjogJgogIE5wVG90ID0gMiAgICAgICAgICAgICAhIOeykuWtkOaVsApSRUFMKDgpLCBQQVJBTUVURVIgOjogJgogIEVwcyAgID0gMS5kMCwgICAgICAgJiAhIEwtSuODneODhuODs+OCt+ODo+ODq+OBruODkeODqeODoeODvOOCv++8kQogIFNpZ21hID0gMS5kMCwgICAgICAgJiAhIEwtSuODneODhuODs+OCt+ODo+ODq+OBruODkeODqeODoeODvOOCv++8kgogIE1hc3MgID0gMS5kMCAgICAgICAgICAhIOeykuWtkOOBruizqumHjwoKCiEtLS0tLSDjg6bjg7zjgrbjg7zlpInmlbAgKOiqsumhjOOBq+W/nOOBmOOBpuWkieabtOOBmeOCi+WkieaVsCkgLS0tLS0KISBEdDog5pmC6ZaT44K544OG44OD44OXCiEgTURTdGVwOiDjgrnjg4bjg4Pjg5fmlbDvvIjnubDjgorov5TjgZfjga7lm57mlbDvvIkKISAgIC0tPiBEdCAgPSAxLmQtMuOAnDEuZC0z44GM6YGp5b2TLiBEdCpNRFN0ZXA9IDHjgJwzIOOBqOOBmeOCiy4KISAgIC0tPiDjgrXjg7zjg5Djg7zjgavosqDojbfjgpLjgYvjgZHjgarjgYTjgojjgYYgRHQg4omnIDEuZC01IOOBqOOBmeOCiwpJTlRFR0VSLCBQQVJBTUVURVIgOjogTURTdGVwICA9IDIwMDAgICAgISDnt4/jgrnjg4bjg4Pjg5fmlbAKUkVBTCg4KSwgUEFSQU1FVEVSIDo6IER0ICAgICAgPSAxLmQtMyAgICEg5pmC6ZaT44K544OG44OD44OXClJFQUwoOCksIFBBUkFNRVRFUiA6OiBSMl9pbmkgID0gMC41NmQwICAhIOeykuWtkDLjga7liJ3mnJ/kvY3nva4KUkVBTCg4KSwgUEFSQU1FVEVSIDo6IFYyX2luaSAgPSAtMC4yZDAgICEg57KS5a2QMuOBruWInemAnwpJTlRFR0VSLCBQQVJBTUVURVIgOjogTk91dCAgICA9IDEwMCAgICAgISDlh7rlipvjg4fjg7zjgr/mlbDvvIhNRFN0ZXDku6XkuIvjgacxMDDjgpLotoXjgYjjgarjgYTmlbTmlbDvvIkKCgohLS0tLS0g5Lul5LiL44Gu5aSJ5pWw44O76YWN5YiX44Gv44OX44Ot44Kw44Op44Og5YaF44Gn6Ieq5YuV5pu05pawIC0tLS0tCklOVEVHRVIgaQpJTlRFR0VSIDo6IE5TdW0gPSAwLCAgICAgJiAgICAhIOiThOepjeOBruWbnuaVsAogICAgICAgICAgIG4gICAgPSAwLCAgICAgJiAgICAhIOePvuWcqOOBruOCueODhuODg+ODl+aVsAogICAgICAgICAgIFByaW50SW50ID0gMSAgICAgICAhIOWHuuWKm+mWk+malApSRUFMKDgpIDo6ICYKICBSMCgzLCBOcFRvdCkgPSAwLmQwLCAgICAgICYgISDliJ3mnJ/kvY3nva4KICBWKDMsIE5wVG90KSA9IDAuZDAsICAgICAgICYgISDpgJ/luqYKICBSKDMsIE5wVG90KSA9IDAuZDAsICAgICAgICYgISDkvY3nva4KICBkUigzLCBOcFRvdCkgPSAwLmQwLCAgICAgICYgISDliJ3mnJ/kvY3nva7jgYvjgonjga7lpInkvY0KICBkUl9wcmV2KDMsIE5wVG90KSA9IDAuZDAsICYgISDmmYLliLt0KG4tMSnjgah0KG4p6ZaT44Gu5aSJ5L2NCiAgZFJfbmV4dCgzLCBOcFRvdCkgPSAwLmQwLCAmICEg5pmC5Yi7dChuKeOBqHQobisxKemWk+OBruWkieS9jQogIEYoMywgTnBUb3QpID0gMC5kMCwgICAgICAgJiAhIOWKmwogIFQgPSAwLmQwLCAgICAgICAgICAgICAgICAgJiAhIOmBi+WLleOCqOODjeODq+OCruODvAogIFAgPSAwLmQwLCAgICAgICAgICAgICAgICAgJiAhIOODneODhuODs+OCt+ODo+ODq+OCqOODjeODq+OCruODvAogIEggPSAwLmQwLCAgICAgICAgICAgICAgICAgJiAhIOWFqOOCqOODjeODq+OCruODvO+8iOODj+ODn+ODq+ODiOODi+OCouODs++8iQogIEgwID0gMC5kMCwgICAgICAgICAgICAgICAgJiAhIOioiOeul+mWi+Wni+aZguOBruWFqOOCqOODjeODq+OCruODvAogIFYwID0gMC5kMCwgICAgICAgICAgICAgICAgJiAhIOioiOeul+mWi+Wni+aZguOBruW5s+Wdh+mAn+W6pgogIE1heEVyckggPSAwLmQwLCAgICAgICAgICAgJiAhIOODj+ODn+ODq+ODiOODi+OCouODs+OBruacgOWkp+iqpOW3rgogIFN1bUggPSAwLmQwLCAgICAgICAgICAgICAgJiAhIOiThOepjeOBleOCjOOBn+ODj+ODn+ODq+ODiOODi+OCouODswogIFN1bUgyID0gMC5kMCwgICAgICAgICAgICAgJiAhIOiThOepjeOBleOCjOOBn+ODj+ODn+ODq+ODiOODi+OCouODs+OBruS6jOS5lwogIFN1bVQgPSAwLmQwLCAgICAgICAgICAgICAgJiAhIOiThOepjeOBleOCjOOBn+mBi+WLleOCqOODjeODq+OCruODvAogIFN1bVQyID0gMC5kMCAgICAgICAgICAgICAgICAhIOiThOepjeOBleOCjOOBn+mBi+WLleOCqOODjeODq+OCruODvOOBruS6jOS5lwoKCiEtLS0tLSBTYWZldHkgbmV0IC0tLS0tCmlmIChEdCpNRFN0ZXAgPiAzLmQwKSB0aGVuCiAgd3JpdGUoNiwqKSAnVG9vIGxvbmcgc2ltdWxhdGlvbiB0aW1lICEhJwogIHN0b3AKZW5kaWYKCgohLS0tLS0g5ZCE56iu6Kit5a6a5YCk44Gu5Ye65YqbIC0tLS0tClByaW50SW50ID0gTURTdGVwL05PdXQKd3JpdGUoNiwqKSAnPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Jwp3cml0ZSg2LCopICdNRCBzaW11bGF0aW9uIGJ5IFZlcmxldCBtZXRob2QnCndyaXRlKDYsKikgJz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PScKd3JpdGUoNiwqKSAnICMgb2YgcGFydGljbGVzICAgID0gJywgTnBUb3QKd3JpdGUoNiwqKSAnIEwtSiBwYXJhbWV0ZXJzOicKd3JpdGUoNiwqKSAnICAgLS0+IEVwc2lsb24gICAgID0gJywgRXBzCndyaXRlKDYsKikgJyAgIC0tPiBTaWdtYSAgICAgICA9ICcsIFNpZ21hCndyaXRlKDYsKikgJyBNYXNzIG9mIHBhcnRpY2xlICA9ICcsIE1hc3MKd3JpdGUoNiwqKSAnIFRpbWUgc3RlcCAgICAgICAgID0gJywgRHQKd3JpdGUoNiwqKSAnICMgb2YgTUQgc3RlcHMgICAgID0gJywgTURTdGVwCndyaXRlKDYsKikgJyBTaW11bGF0aW9uIHRpbWUgICA9ICcsIER0KnJlYWwoTURTdGVwLDgpCndyaXRlKDYsKikgJyBQcmludCBpbnRlcnZhbCAgICA9ICcsIER0KnJlYWwoUHJpbnRJbnQsOCkKd3JpdGUoNiwqKSAKCgohLS0tLS0g57KS5a2Q44Gu5Yid5pyf5oOF5aCx44Gu6Kit5a6aIC0tLS0tCiEg5Yid5pyf5L2N572uClIwKDEsMikgPSBSMl9pbmkgICAhIOeykuWtkDEKUjAoMSwxKSA9IC1SMCgxLDIpICEg57KS5a2QMgoKISDliJ3pgJ8KVigxLDIpID0gVjJfaW5pICAgISDnspLlrZAxClYoMSwxKSA9IC1WKDEsMikgICEg57KS5a2QMgoKISDliJ3pgJ/luqbjga7lpKfjgY3jgZXjga7lubPlnYflgKQKVjA9IDAuZDAKZG8gaT0xLCBOcFRvdAogIFYwPSBWMCArIFYoMSxpKSoqMiArIFYoMixpKSoqMiArIFYoMyxpKSoqMiAKZW5kZG8gISBpClYwPSBzcXJ0KFYwL3JlYWwoTnBUb3QsOCkpCgoKIS0tLS0tIDDjgrnjg4bjg4Pjg5fnm67jgafjga7lipvjga7oqIjnrpcgLS0tLS0KY2FsbCBGb3JjZVBvdGVudGlhbCAoTnBUb3QsIG4sIE1EU3RlcCwgUjAsIGRSLCBFcHMsIFNpZ21hLCBQLCBGKQoKCiEtLS0tLSAx44K544OG44OD44OX55uu44Gu5bqn5qiZ44KS6KiI566XIC0tLS0tCmNhbGwgVmVybGV0IChOcFRvdCwgbiwgTURTdGVwLCBOU3VtLCBEdCwgTWFzcywgUjAsIFAsIFIsIEYsIFYsICYKICAgICAgICAgICAgIGRSLCBkUl9wcmV2LCBkUl9uZXh0LCBILCBULCBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIpCgoKIS0tLS0tIOODj+ODn+ODq+ODiOODi+OCouODs+OBruWIneacn+WApOOCkuS/neWtmCAtLS0tLQpIMCA9IEgKCgohLS0tLS0g5Ye65YqbIC0tLS0tCiEg44OY44OD44OA44O85oOF5aCx44Gu5Ye65YqbCndyaXRlKDYsJyhhKScpICcjdGltZSwgIHBvc2l0aW9uLCAgdmVsb2NpdHksICBraW5ldGljLCAgcG90ZW50aWFsLCAgaGFtaWx0b25pYW4nCgohIOS9jee9ruOAgemAn+W6puOBquOBqeOBruWHuuWKmy4KY2FsbCBPdXRwdXQgKDAsIFByaW50SW50LCBOcFRvdCwgbiwgTURTdGVwLCBOU3VtLCBEdCwgUiwgSCwgVCwgUCwgViwgJgogICAgICAgICAgICAgSDAsIFYwLCBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIsIE1heEVyckgpCgoKIS0tLS0tIDLjgrnjg4bjg4Pjg5fnm67ku6XpmY3jga7mmYLplpPnmbrlsZUgLS0tLS0KZG8gbj0gMSwgTURTdGVwCgogICEgbuOCueODhuODg+ODl+ebruOBp+OBruWKm+OBruioiOeulwogIGNhbGwgRm9yY2VQb3RlbnRpYWwgKE5wVG90LCBuLCBNRFN0ZXAsIFIwLCBkUiwgRXBzLCBTaWdtYSwgUCwgRikKCiAgISAobisxKeOCueODhuODg+ODl+ebruOBruW6p+aomeOCkuioiOeulwogIGNhbGwgVmVybGV0IChOcFRvdCwgbiwgTURTdGVwLCBOU3VtLCBEdCwgTWFzcywgUjAsIFAsIFIsIEYsIFYsICYKICAgICAgICAgICAgICAgZFIsIGRSX3ByZXYsIGRSX25leHQsIEgsIFQsIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMikKCiAgISDlh7rlipsKICBjYWxsIE91dHB1dCAoMCwgUHJpbnRJbnQsIE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0sIER0LCBSLCBILCBULCBQLCBWLCAmCiAgICAgICAgICAgICAgIEgwLCBWMCwgU3VtSCwgU3VtSDIsIFN1bVQsIFN1bVQyLCBNYXhFcnJIKQoKZW5kZG8gISBuCgoKIS0tLS0tIOWQhOeoruW5s+Wdh+WApOOCkuWHuuWKmyAtLS0tLQpjYWxsIE91dHB1dCAoMSwgUHJpbnRJbnQsIE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0sIER0LCBSLCBILCBULCBQLCBWLCAmCiAgICAgICAgICAgICBIMCwgVjAsIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMiwgTWF4RXJySCkKCndyaXRlKDYsKikgJyBEb25lLicKd3JpdGUoNiwqKSAKCiEtLS0tLSDkuLvjg5fjg63jgrDjg6njg6Djga7ntYLkuoYgLS0tLS0KRU5EIFBST0dSQU0gY2x1c3RlcgoKCgpTVUJST1VUSU5FIEZvcmNlUG90ZW50aWFsKE5wVG90LCBuLCBNRFN0ZXAsIFIwLCBkUiwgRXBzLCBTaWdtYSwgUCwgRikKIS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hCiEg44Od44OG44Oz44K344Oj44Or44Ko44ON44Or44Ku44O844Go5Yqb44Gu6KiI566XICEKIS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hCklNUExJQ0lUIE5PTkUKSU5URUdFUiwgSU5URU5UKGluKSAgICA6OiBOcFRvdCwgbiwgTURTdGVwClJFQUwoOCksIElOVEVOVChpbikgICAgOjogUjAoMyxOcFRvdCksIGRSKDMsTnBUb3QpLCBFcHMsIFNpZ21hClJFQUwoOCksIElOVEVOVChpbm91dCkgOjogUCwgRigzLE5wVG90KQohIExvY2FsIHN0dWZmCklOVEVHRVIgaSwgagpSRUFMKDgpIFIxLCBSMiwgUmlqKDMpLCBkcGRyLCBkcmR2KDMpCgpGKDosOik9MC5kMCA7IFA9MC5kMAoKZG8gaT0gMSwgTnBUb3QKICBkbyBqPSAxLCBOcFRvdAoKICAgIGlmIChpIC89IGopIHRoZW4KCiAgICAgICEgZFI6IGRpc3BsYWNlbWVudCBmcm9tIHRpbWUgMCB0byB0aW1lIG4KICAgICAgUmlqKDopID0gKGRSKDosaikgLSBkUig6LGkpKSArIChSMCg6LGopIC0gUjAoOixpKSkKICAgICAgUjIgPSBSaWooMSkqKjIgKyBSaWooMikqKjIgKyBSaWooMykqKjIgCiAgICAgIFIxID0gc3FydChSMikKCiAgICAgICEgcG90ZW50aWFsIGVuZXJneQogICAgICBQID0gUCArIDQuZDAgKiBFcHMgKiAoKFNpZ21hKioyL1IyKSoqNiAtIChTaWdtYSoqMi9SMikqKjMpCgogICAgICAhIGZvcmNlCiAgICAgIGRwZHIgPSA0LmQwICogRXBzICogKC0xMi5kMCooU2lnbWEqKjIvUjIpKio2ICsgNi5kMCooU2lnbWEqKjIvUjIpKiozKSAvIFIxCiAgICAgIGRyZHYoOikgPSAtUmlqKDopIC8gUjEKICAgICAgRig6LGkpID0gRig6LGkpIC0gZHBkcipkcmR2KDopCgogICAgZW5kaWYgISBpIC89IGoKCiAgZW5kZG8gISBqCmVuZGRvICEgaQoKUCA9IDAuNWQwKlAKCnJldHVybgpFTkQgU1VCUk9VVElORSBGb3JjZVBvdGVudGlhbAoKCgpTVUJST1VUSU5FIFZlcmxldChOcFRvdCwgbiwgTURTdGVwLCBOU3VtLCBEdCwgTWFzcywgUjAsIFAsIFIsIEYsIFYsICYKICAgICAgICAgICAgICAgICAgZFIsIGRSX3ByZXYsIGRSX25leHQsIEgsIFQsIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMikKIS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIQohIFZlcmxldOazleOBq+OCiOOCi+W6p+aomeOBruabtOaWsCAhCiEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSEKSU1QTElDSVQgTk9ORQpJTlRFR0VSLCBJTlRFTlQoaW4pICAgIDo6IE5wVG90LCBuLCBNRFN0ZXAKSU5URUdFUiwgSU5URU5UKGlub3V0KSA6OiBOU3VtClJFQUwoOCksIElOVEVOVChpbikgICAgOjogUjAoMyxOcFRvdCksIFAsIER0LCBNYXNzClJFQUwoOCksIElOVEVOVChpbm91dCkgOjogUigzLE5wVG90KSwgRigzLE5wVG90KSwgVigzLE5wVG90KSwgZFIoMyxOcFRvdCksICYKICAgICAgICAgICAgICAgICAgICAgICAgICBkUl9wcmV2KDMsTnBUb3QpLCBkUl9uZXh0KDMsTnBUb3QpLCBILCBULCBTdW1ILCAgJgogICAgICAgICAgICAgICAgICAgICAgICAgIFN1bUgyLCBTdW1ULCBTdW1UMgohIExvY2FsIHN0dWZmCklOVEVHRVIgaQoKIS0tLS0tIDAtdGggc3RlcCAtLS0tLQppZiAobiA9PSAwKSB0aGVuCiAgISBjdXJyZW50IHBvc2l0aW9uCiAgUig6LDopID0gUjAoOiw6KSAKCiAgISBkUiA9IGRSX25leHQgPSBSKM6UdCkgLSBSKDApID0gVigwKc6UdCArIGEoMCkqKM6UdCleMi8yICAgCiAgZG8gaT0gMSwgTnBUb3QKICAgIGRSX25leHQoOixpKSA9IFYoOixpKSpEdCArIDAuNWQwKkYoOixpKSpEdCoqMi9NYXNzCiAgICBkUig6LGkpID0gZFJfbmV4dCg6LGkpCiAgZW5kZG8gISBpCgoKIS0tLS0tIGxhdGVyIHN0ZXBzIC0tLS0tCmVsc2VpZiAobiA+PSAxKSB0aGVuCiAgISBjdXJyZW50IHBvc2l0aW9uCiAgUig6LDopID0gUjAoOiw6KSArIGRSKDosOikKCiAgISBkUl9uZXh0ID0gUih0K86UdCkgLSBSKHQpID0gUih0KSAtIFIodC3OlHQpICsgYSh0KSoozpR0KV4yICAgCiAgISBkUl9wcmV2ID0gUih0KSAtIFIodC3OlHQpCiAgISBkUiAgICAgID0gUih0K86UdCkgLSBSKDApCiAgZG8gaT0gMSwgTnBUb3QKICAgIGRSX25leHQoOixpKSA9IGRSX3ByZXYoOixpKSArIEYoOixpKSpEdCoqMi9NYXNzCiAgICBkUig6LGkpID0gZFIoOixpKSArIGRSX25leHQoOixpKQogICAgVig6LGkpID0gMC41ZDAgKiAoZFJfbmV4dCg6LGkpICsgZFJfcHJldig6LGkpKSAvIER0CiAgZW5kZG8KCmVuZGlmCgoKIS0tLS0tIFJlbmFtaW5nIGZvciB1c2UgYXQgdGhlIG5leHQgc3RlcCAtLS0tLQpkUl9wcmV2KDosOik9IGRSX25leHQoOiw6KQoKCiEtLS0tLSDpgYvli5Xjgqjjg43jg6vjgq7jg7zjga7oqIjnrpcgLS0tLS0KVCA9IDAuZDAKZG8gaT0gMSwgTnBUb3QKICBUID0gVCArIDAuNWQwICogTWFzcyAqIChWKDEsaSkqKjIgKyBWKDIsaSkqKjIgKyBWKDMsaSkqKjIpCmVuZGRvCgoKIS0tLS0tIOODj+ODn+ODq+ODiOODi+OCouODs+OBruioiOeulyAtLS0tLQpIID0gVCArIFAKCgohLS0tLS0g6JOE56mNIC0tLS0tCk5TdW0gID0gTlN1bSAgKyAxICAgICAgISDok4TnqY3jga7lm57mlbAKU3VtSCAgPSBTdW1IICArIEggICAgICAhIOODj+ODn+ODq+ODiOODi+OCouODswpTdW1IMiA9IFN1bUgyICsgSCoqMiAgICEg44OP44Of44Or44OI44OL44Ki44Oz44Gu77yS5LmXClN1bVQgID0gU3VtVCAgKyBUICAgICAgISDpgYvli5Xjgqjjg43jg6vjgq7jg7wKU3VtVDIgPSBTdW1UMiArIFQqKjIgICAhIOmBi+WLleOCqOODjeODq+OCruODvOOBru+8kuS5lwoKcmV0dXJuCkVORCBTVUJST1VUSU5FIFZlcmxldAoKCgpTVUJST1VUSU5FIE91dHB1dChtb2RlLCBQcmludEludCwgTnBUb3QsIG4sIE1EU3RlcCwgTlN1bSwgRHQsIFIsIEgsIFQsIFAsIFYsIEgwLCBWMCwgJgogICAgICAgICAgICAgICAgICBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIsIE1heEVyckgpCiEtLS0tLS0tLS0tLS0hCiEg57WQ5p6c44Gu5Ye65YqbICEKIS0tLS0tLS0tLS0tLSEKSU1QTElDSVQgTk9ORQpJTlRFR0VSLCBJTlRFTlQoaW4pICAgIDo6IG1vZGUsIFByaW50SW50LCBOcFRvdCwgbiwgTURTdGVwLCBOU3VtClJFQUwoOCksIElOVEVOVChpbikgICAgOjogRHQsIFIoMyxOcFRvdCksIEgsIFQsIFAsIFYoMyxOcFRvdCksIEgwLCBWMCwgU3VtSCwgU3VtSDIsIFN1bVQsIFN1bVQyClJFQUwoOCksIElOVEVOVChpbm91dCkgOjogTWF4RXJySAohIExvY2FsIHN0dWZmClJFQUwoOCkgdGltZSwgQXZlSCwgQXZlSDIsIEF2ZVQsIEF2ZVQyLCBSTVNEX0gsIFJNU0RfVAoKaWYgKG1vZGUgPT0gMCkgdGhlbgogICEtLS0tLSBPdXRwdXQgZGF0YSBhdCB0aGUgY3VycmVudCB0aW1lIC0tLS0tCiAgdGltZSA9IER0KnJlYWwobiw4KQoKICBpZiAoIChuPT1NRFN0ZXApIC5vci4gKG1vZChuLFByaW50SW50KT09MCkgKSAmCiAgICB3cml0ZSg2LCAnKCBlMTIuNiwgNChlMTQuNiksIGUyMy4xNSApJykgdGltZSwgUigxLDIpLCBWKDEsMiksIFQsIFAsIEgKCiAgTWF4RXJySD0gbWF4KE1heEVyckgsIGFicyhILUgwKSkgISBNYXhpbXVtIGVycm9yIGluIEhhbWlsdG9uaWFuCgoKZWxzZWlmIChtb2RlID09IDEpIHRoZW4KICAhLS0tLS0gQ29tcHV0ZSByb290IG1lYW4gc3F1YXJlIGRldmlhdGlvbiAoUk1TRCkgLS0tLS0KICAhIGNvbXB1dGUgYXZlcmFnZXMKICBBdmVIICA9IFN1bUggL3JlYWwoTlN1bSw4KSAgISDjg4/jg5/jg6vjg4jjg4vjgqLjg7MKICBBdmVIMiA9IFN1bUgyL3JlYWwoTlN1bSw4KSAgISDjg4/jg5/jg6vjg4jjg4vjgqLjg7Pjga7vvJLkuZcKICBBdmVUICA9IFN1bVQgL3JlYWwoTlN1bSw4KSAgISDpgYvli5Xjgqjjg43jg6vjgq7jg7wKICBBdmVUMiA9IFN1bVQyL3JlYWwoTlN1bSw4KSAgISDpgYvli5Xjgqjjg43jg6vjgq7jg7zjga7vvJLkuZcKCiAgISBjb21wdXRlIFJNU0QKICBSTVNEX0ggPSBzcXJ0KGFicyhBdmVIMi1BdmVIKioyKSkKICBSTVNEX1QgPSBzcXJ0KGFicyhBdmVUMi1BdmVUKioyKSkKCiAgISBwcmludAogIHdyaXRlKDYsICopCiAgd3JpdGUoNiwgKikgJy0tLS0tLS0tLScKICB3cml0ZSg2LCAqKSAnIFN1bW1hcnkgJwogIHdyaXRlKDYsICopICctLS0tLS0tLS0nCiAgd3JpdGUoNiwgJyggYSwgZTIzLjE1ICknKSAgICAnRHQgICAgICAgICAgID0nLCBEdAogIHdyaXRlKDYsICcoIGEsIGUyMy4xNSApJykgICAgJ1YwICAgICAgICAgICA9JywgVjAKICB3cml0ZSg2LCAnKCBhLCBlMjMuMTUgKScpICAgICdSTVNEKEgpICAgICAgPScsIFJNU0RfSAogIHdyaXRlKDYsICcoIGEsIGUyMy4xNSApJykgICAgJ01heC4gZXJyLihIKSA9JywgTWF4RXJySAogIHdyaXRlKDYsICcoIGEsIGUyMy4xNSApJykgICAgJ0ZpbmFsIHBvcy4gICA9JywgUigxLDIpCiAgd3JpdGUoNiwgJyggMihhLCBlMjMuMTUpICknKSAnPEg+PScsIEF2ZUgsICcgKy0gJywgUk1TRF9ICiAgd3JpdGUoNiwgJyggMihhLCBlMjMuMTUpICknKSAnPFQ+PScsIEF2ZVQsICcgKy0gJywgUk1TRF9UCiAgd3JpdGUoNiwgJyggYSwgaTEwICknKSAgICAnTm9ybS4gY29uc3QuKE5TdW0pPSAgJywgTlN1bQoKZW5kaWYKCnJldHVybgpFTkQgU1VCUk9VVElORSBPdXRwdXQK