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 = 1000 ! 総ステップ数
REAL(8), PARAMETER :: Dt = 1.d-3 ! 時間ステップ
REAL(8), PARAMETER :: R2_ini = 1.0d0 ! 粒子2の初期位置
REAL(8), PARAMETER :: V2_ini = -1.0d0 ! 粒子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,*) '#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, '( 5(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+WkieaVsCkgLS0tLS0KISBEdDog5pmC6ZaT44K544OG44OD44OXCiEgTURTdGVwOiDjgrnjg4bjg4Pjg5fmlbDvvIjnubDjgorov5TjgZfjga7lm57mlbDvvIkKISAgIC0tPiBEdCAgPSAxLmQtMuOAnDEuZC0z44GM6YGp5b2TLiBEdCpNRFN0ZXA9IDHjgJwzIOOBqOOBmeOCiy4KISAgIC0tPiDjgrXjg7zjg5Djg7zjgavosqDojbfjgpLjgYvjgZHjgarjgYTjgojjgYYgRHQg4omnIDEuZC01IOOBqOOBmeOCiwpJTlRFR0VSLCBQQVJBTUVURVIgOjogTURTdGVwICA9IDEwMDAgICAgISDnt4/jgrnjg4bjg4Pjg5fmlbAKUkVBTCg4KSwgUEFSQU1FVEVSIDo6IER0ICAgICAgPSAxLmQtMyAgICEg5pmC6ZaT44K544OG44OD44OXClJFQUwoOCksIFBBUkFNRVRFUiA6OiBSMl9pbmkgID0gMS4wZDAgICEg57KS5a2QMuOBruWIneacn+S9jee9rgpSRUFMKDgpLCBQQVJBTUVURVIgOjogVjJfaW5pICA9IC0xLjBkMCAgISDnspLlrZAy44Gu5Yid6YCfCklOVEVHRVIsIFBBUkFNRVRFUiA6OiBOT3V0ICAgID0gMTAwICAgICAhIOWHuuWKm+ODh+ODvOOCv+aVsO+8iE1EU3RlcOS7peS4i+OBpzEwMOOCkui2heOBiOOBquOBhOaVtOaVsO+8iQoKCiEtLS0tLSDku6XkuIvjga7lpInmlbDjg7vphY3liJfjga/jg5fjg63jgrDjg6njg6DlhoXjgafoh6rli5Xmm7TmlrAgLS0tLS0KSU5URUdFUiBpCklOVEVHRVIgOjogTlN1bSA9IDAsICAgICAmICAgICEg6JOE56mN44Gu5Zue5pWwCiAgICAgICAgICAgbiAgICA9IDAsICAgICAmICAgICEg54++5Zyo44Gu44K544OG44OD44OX5pWwCiAgICAgICAgICAgUHJpbnRJbnQgPSAxICAgICAgICEg5Ye65Yqb6ZaT6ZqUClJFQUwoOCkgOjogJgogIFIwKDMsIE5wVG90KSA9IDAuZDAsICAgICAgJiAhIOWIneacn+S9jee9rgogIFYoMywgTnBUb3QpID0gMC5kMCwgICAgICAgJiAhIOmAn+W6pgogIFIoMywgTnBUb3QpID0gMC5kMCwgICAgICAgJiAhIOS9jee9rgogIGRSKDMsIE5wVG90KSA9IDAuZDAsICAgICAgJiAhIOWIneacn+S9jee9ruOBi+OCieOBruWkieS9jQogIGRSX3ByZXYoMywgTnBUb3QpID0gMC5kMCwgJiAhIOaZguWIu3Qobi0xKeOBqHQobinplpPjga7lpInkvY0KICBkUl9uZXh0KDMsIE5wVG90KSA9IDAuZDAsICYgISDmmYLliLt0KG4p44GodChuKzEp6ZaT44Gu5aSJ5L2NCiAgRigzLCBOcFRvdCkgPSAwLmQwLCAgICAgICAmICEg5YqbCiAgVCA9IDAuZDAsICAgICAgICAgICAgICAgICAmICEg6YGL5YuV44Ko44ON44Or44Ku44O8CiAgUCA9IDAuZDAsICAgICAgICAgICAgICAgICAmICEg44Od44OG44Oz44K344Oj44Or44Ko44ON44Or44Ku44O8CiAgSCA9IDAuZDAsICAgICAgICAgICAgICAgICAmICEg5YWo44Ko44ON44Or44Ku44O877yI44OP44Of44Or44OI44OL44Ki44Oz77yJCiAgSDAgPSAwLmQwLCAgICAgICAgICAgICAgICAmICEg6KiI566X6ZaL5aeL5pmC44Gu5YWo44Ko44ON44Or44Ku44O8CiAgVjAgPSAwLmQwLCAgICAgICAgICAgICAgICAmICEg6KiI566X6ZaL5aeL5pmC44Gu5bmz5Z2H6YCf5bqmCiAgTWF4RXJySCA9IDAuZDAsICAgICAgICAgICAmICEg44OP44Of44Or44OI44OL44Ki44Oz44Gu5pyA5aSn6Kqk5beuCiAgU3VtSCA9IDAuZDAsICAgICAgICAgICAgICAmICEg6JOE56mN44GV44KM44Gf44OP44Of44Or44OI44OL44Ki44OzCiAgU3VtSDIgPSAwLmQwLCAgICAgICAgICAgICAmICEg6JOE56mN44GV44KM44Gf44OP44Of44Or44OI44OL44Ki44Oz44Gu5LqM5LmXCiAgU3VtVCA9IDAuZDAsICAgICAgICAgICAgICAmICEg6JOE56mN44GV44KM44Gf6YGL5YuV44Ko44ON44Or44Ku44O8CiAgU3VtVDIgPSAwLmQwICAgICAgICAgICAgICAgICEg6JOE56mN44GV44KM44Gf6YGL5YuV44Ko44ON44Or44Ku44O844Gu5LqM5LmXCgoKIS0tLS0tIFNhZmV0eSBuZXQgLS0tLS0KaWYgKER0Kk1EU3RlcCA+IDMuZDApIHRoZW4KICB3cml0ZSg2LCopICdUb28gbG9uZyBzaW11bGF0aW9uIHRpbWUgISEnCiAgc3RvcAplbmRpZgoKCiEtLS0tLSDlkITnqK7oqK3lrprlgKTjga7lh7rlipsgLS0tLS0KUHJpbnRJbnQgPSBNRFN0ZXAvTk91dAp3cml0ZSg2LCopICc9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0nCndyaXRlKDYsKikgJ01EIHNpbXVsYXRpb24gYnkgVmVybGV0IG1ldGhvZCcKd3JpdGUoNiwqKSAnPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Jwp3cml0ZSg2LCopICcgIyBvZiBwYXJ0aWNsZXMgICAgPSAnLCBOcFRvdAp3cml0ZSg2LCopICcgTC1KIHBhcmFtZXRlcnM6Jwp3cml0ZSg2LCopICcgICAtLT4gRXBzaWxvbiAgICAgPSAnLCBFcHMKd3JpdGUoNiwqKSAnICAgLS0+IFNpZ21hICAgICAgID0gJywgU2lnbWEKd3JpdGUoNiwqKSAnIE1hc3Mgb2YgcGFydGljbGUgID0gJywgTWFzcwp3cml0ZSg2LCopICcgVGltZSBzdGVwICAgICAgICAgPSAnLCBEdAp3cml0ZSg2LCopICcgIyBvZiBNRCBzdGVwcyAgICAgPSAnLCBNRFN0ZXAKd3JpdGUoNiwqKSAnIFNpbXVsYXRpb24gdGltZSAgID0gJywgRHQqcmVhbChNRFN0ZXAsOCkKd3JpdGUoNiwqKSAnIFByaW50IGludGVydmFsICAgID0gJywgRHQqcmVhbChQcmludEludCw4KQp3cml0ZSg2LCopIAoKCiEtLS0tLSDnspLlrZDjga7liJ3mnJ/mg4XloLHjga7oqK3lrpogLS0tLS0KISDliJ3mnJ/kvY3nva4KUjAoMSwyKSA9IFIyX2luaSAgICEg57KS5a2QMQpSMCgxLDEpID0gLVIwKDEsMikgISDnspLlrZAyCgohIOWInemAnwpWKDEsMikgPSBWMl9pbmkgICAhIOeykuWtkDEKVigxLDEpID0gLVYoMSwyKSAgISDnspLlrZAyCgohIOWInemAn+W6puOBruWkp+OBjeOBleOBruW5s+Wdh+WApApWMD0gMC5kMApkbyBpPTEsIE5wVG90CiAgVjA9IFYwICsgVigxLGkpKioyICsgVigyLGkpKioyICsgVigzLGkpKioyIAplbmRkbyAhIGkKVjA9IHNxcnQoVjAvcmVhbChOcFRvdCw4KSkKCgohLS0tLS0gMOOCueODhuODg+ODl+ebruOBp+OBruWKm+OBruioiOeulyAtLS0tLQpjYWxsIEZvcmNlUG90ZW50aWFsIChOcFRvdCwgbiwgTURTdGVwLCBSMCwgZFIsIEVwcywgU2lnbWEsIFAsIEYpCgoKIS0tLS0tIDHjgrnjg4bjg4Pjg5fnm67jga7luqfmqJnjgpLoqIjnrpcgLS0tLS0KY2FsbCBWZXJsZXQgKE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0sIER0LCBNYXNzLCBSMCwgUCwgUiwgRiwgViwgJgogICAgICAgICAgICAgZFIsIGRSX3ByZXYsIGRSX25leHQsIEgsIFQsIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMikKCgohLS0tLS0g44OP44Of44Or44OI44OL44Ki44Oz44Gu5Yid5pyf5YCk44KS5L+d5a2YIC0tLS0tCkgwID0gSAoKCiEtLS0tLSDlh7rlipsgLS0tLS0KISDjg5jjg4Pjg4Djg7zmg4XloLHjga7lh7rlipsKd3JpdGUoNiwqKSAnI3RpbWUsICBwb3NpdGlvbiwgIHZlbG9jaXR5LCAga2luZXRpYywgIHBvdGVudGlhbCwgIGhhbWlsdG9uaWFuJwoKISDkvY3nva7jgIHpgJ/luqbjgarjganjga7lh7rlipsuCmNhbGwgT3V0cHV0ICgwLCBQcmludEludCwgTnBUb3QsIG4sIE1EU3RlcCwgTlN1bSwgRHQsIFIsIEgsIFQsIFAsIFYsICYKICAgICAgICAgICAgIEgwLCBWMCwgU3VtSCwgU3VtSDIsIFN1bVQsIFN1bVQyLCBNYXhFcnJIKQoKCiEtLS0tLSAy44K544OG44OD44OX55uu5Lul6ZmN44Gu5pmC6ZaT55m65bGVIC0tLS0tCmRvIG49IDEsIE1EU3RlcAoKICAhIG7jgrnjg4bjg4Pjg5fnm67jgafjga7lipvjga7oqIjnrpcKICBjYWxsIEZvcmNlUG90ZW50aWFsIChOcFRvdCwgbiwgTURTdGVwLCBSMCwgZFIsIEVwcywgU2lnbWEsIFAsIEYpCgogICEgKG4rMSnjgrnjg4bjg4Pjg5fnm67jga7luqfmqJnjgpLoqIjnrpcKICBjYWxsIFZlcmxldCAoTnBUb3QsIG4sIE1EU3RlcCwgTlN1bSwgRHQsIE1hc3MsIFIwLCBQLCBSLCBGLCBWLCAmCiAgICAgICAgICAgICAgIGRSLCBkUl9wcmV2LCBkUl9uZXh0LCBILCBULCBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIpCgogICEg5Ye65YqbCiAgY2FsbCBPdXRwdXQgKDAsIFByaW50SW50LCBOcFRvdCwgbiwgTURTdGVwLCBOU3VtLCBEdCwgUiwgSCwgVCwgUCwgViwgJgogICAgICAgICAgICAgICBIMCwgVjAsIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMiwgTWF4RXJySCkKCmVuZGRvICEgbgoKCiEtLS0tLSDlkITnqK7lubPlnYflgKTjgpLlh7rlipsgLS0tLS0KY2FsbCBPdXRwdXQgKDEsIFByaW50SW50LCBOcFRvdCwgbiwgTURTdGVwLCBOU3VtLCBEdCwgUiwgSCwgVCwgUCwgViwgJgogICAgICAgICAgICAgSDAsIFYwLCBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIsIE1heEVyckgpCgp3cml0ZSg2LCopICcgRG9uZS4nCndyaXRlKDYsKikgCgohLS0tLS0g5Li744OX44Ot44Kw44Op44Og44Gu57WC5LqGIC0tLS0tCkVORCBQUk9HUkFNIGNsdXN0ZXIKCgoKU1VCUk9VVElORSBGb3JjZVBvdGVudGlhbChOcFRvdCwgbiwgTURTdGVwLCBSMCwgZFIsIEVwcywgU2lnbWEsIFAsIEYpCiEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIQohIOODneODhuODs+OCt+ODo+ODq+OCqOODjeODq+OCruODvOOBqOWKm+OBruioiOeulyAhCiEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIQpJTVBMSUNJVCBOT05FCklOVEVHRVIsIElOVEVOVChpbikgICAgOjogTnBUb3QsIG4sIE1EU3RlcApSRUFMKDgpLCBJTlRFTlQoaW4pICAgIDo6IFIwKDMsTnBUb3QpLCBkUigzLE5wVG90KSwgRXBzLCBTaWdtYQpSRUFMKDgpLCBJTlRFTlQoaW5vdXQpIDo6IFAsIEYoMyxOcFRvdCkKISBMb2NhbCBzdHVmZgpJTlRFR0VSIGksIGoKUkVBTCg4KSBSMSwgUjIsIFJpaigzKSwgZHBkciwgZHJkdigzKQoKRig6LDopPTAuZDAgOyBQPTAuZDAKCmRvIGk9IDEsIE5wVG90CiAgZG8gaj0gMSwgTnBUb3QKCiAgICBpZiAoaSAvPSBqKSB0aGVuCgogICAgICAhIGRSOiBkaXNwbGFjZW1lbnQgZnJvbSB0aW1lIDAgdG8gdGltZSBuCiAgICAgIFJpaig6KSA9IChkUig6LGopIC0gZFIoOixpKSkgKyAoUjAoOixqKSAtIFIwKDosaSkpCiAgICAgIFIyID0gUmlqKDEpKioyICsgUmlqKDIpKioyICsgUmlqKDMpKioyIAogICAgICBSMSA9IHNxcnQoUjIpCgogICAgICAhIHBvdGVudGlhbCBlbmVyZ3kKICAgICAgUCA9IFAgKyA0LmQwICogRXBzICogKChTaWdtYSoqMi9SMikqKjYgLSAoU2lnbWEqKjIvUjIpKiozKQoKICAgICAgISBmb3JjZQogICAgICBkcGRyID0gNC5kMCAqIEVwcyAqICgtMTIuZDAqKFNpZ21hKioyL1IyKSoqNiArIDYuZDAqKFNpZ21hKioyL1IyKSoqMykgLyBSMQogICAgICBkcmR2KDopID0gLVJpaig6KSAvIFIxCiAgICAgIEYoOixpKSA9IEYoOixpKSAtIGRwZHIqZHJkdig6KQoKICAgIGVuZGlmICEgaSAvPSBqCgogIGVuZGRvICEgagplbmRkbyAhIGkKClAgPSAwLjVkMCpQCgpyZXR1cm4KRU5EIFNVQlJPVVRJTkUgRm9yY2VQb3RlbnRpYWwKCgoKU1VCUk9VVElORSBWZXJsZXQoTnBUb3QsIG4sIE1EU3RlcCwgTlN1bSwgRHQsIE1hc3MsIFIwLCBQLCBSLCBGLCBWLCAmCiAgICAgICAgICAgICAgICAgIGRSLCBkUl9wcmV2LCBkUl9uZXh0LCBILCBULCBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIpCiEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSEKISBWZXJsZXTms5XjgavjgojjgovluqfmqJnjga7mm7TmlrAgIQohLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hCklNUExJQ0lUIE5PTkUKSU5URUdFUiwgSU5URU5UKGluKSAgICA6OiBOcFRvdCwgbiwgTURTdGVwCklOVEVHRVIsIElOVEVOVChpbm91dCkgOjogTlN1bQpSRUFMKDgpLCBJTlRFTlQoaW4pICAgIDo6IFIwKDMsTnBUb3QpLCBQLCBEdCwgTWFzcwpSRUFMKDgpLCBJTlRFTlQoaW5vdXQpIDo6IFIoMyxOcFRvdCksIEYoMyxOcFRvdCksIFYoMyxOcFRvdCksIGRSKDMsTnBUb3QpLCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgZFJfcHJldigzLE5wVG90KSwgZFJfbmV4dCgzLE5wVG90KSwgSCwgVCwgU3VtSCwgICYKICAgICAgICAgICAgICAgICAgICAgICAgICBTdW1IMiwgU3VtVCwgU3VtVDIKISBMb2NhbCBzdHVmZgpJTlRFR0VSIGkKCiEtLS0tLSAwLXRoIHN0ZXAgLS0tLS0KaWYgKG4gPT0gMCkgdGhlbgogICEgY3VycmVudCBwb3NpdGlvbgogIFIoOiw6KSA9IFIwKDosOikgCgogICEgZFIgPSBkUl9uZXh0ID0gUijOlHQpIC0gUigwKSA9IFYoMCnOlHQgKyBhKDApKijOlHQpXjIvMiAgIAogIGRvIGk9IDEsIE5wVG90CiAgICBkUl9uZXh0KDosaSkgPSBWKDosaSkqRHQgKyAwLjVkMCpGKDosaSkqRHQqKjIvTWFzcwogICAgZFIoOixpKSA9IGRSX25leHQoOixpKQogIGVuZGRvICEgaQoKCiEtLS0tLSBsYXRlciBzdGVwcyAtLS0tLQplbHNlaWYgKG4gPj0gMSkgdGhlbgogICEgY3VycmVudCBwb3NpdGlvbgogIFIoOiw6KSA9IFIwKDosOikgKyBkUig6LDopCgogICEgZFJfbmV4dCA9IFIodCvOlHQpIC0gUih0KSA9IFIodCkgLSBSKHQtzpR0KSArIGEodCkqKM6UdCleMiAgIAogICEgZFJfcHJldiA9IFIodCkgLSBSKHQtzpR0KQogICEgZFIgICAgICA9IFIodCvOlHQpIC0gUigwKQogIGRvIGk9IDEsIE5wVG90CiAgICBkUl9uZXh0KDosaSkgPSBkUl9wcmV2KDosaSkgKyBGKDosaSkqRHQqKjIvTWFzcwogICAgZFIoOixpKSA9IGRSKDosaSkgKyBkUl9uZXh0KDosaSkKICAgIFYoOixpKSA9IDAuNWQwICogKGRSX25leHQoOixpKSArIGRSX3ByZXYoOixpKSkgLyBEdAogIGVuZGRvCgplbmRpZgoKCiEtLS0tLSBSZW5hbWluZyBmb3IgdXNlIGF0IHRoZSBuZXh0IHN0ZXAgLS0tLS0KZFJfcHJldig6LDopPSBkUl9uZXh0KDosOikKCgohLS0tLS0g6YGL5YuV44Ko44ON44Or44Ku44O844Gu6KiI566XIC0tLS0tClQgPSAwLmQwCmRvIGk9IDEsIE5wVG90CiAgVCA9IFQgKyAwLjVkMCAqIE1hc3MgKiAoVigxLGkpKioyICsgVigyLGkpKioyICsgVigzLGkpKioyKQplbmRkbwoKCiEtLS0tLSDjg4/jg5/jg6vjg4jjg4vjgqLjg7Pjga7oqIjnrpcgLS0tLS0KSCA9IFQgKyBQCgoKIS0tLS0tIOiThOepjSAtLS0tLQpOU3VtICA9IE5TdW0gICsgMSAgICAgICEg6JOE56mN44Gu5Zue5pWwClN1bUggID0gU3VtSCAgKyBIICAgICAgISDjg4/jg5/jg6vjg4jjg4vjgqLjg7MKU3VtSDIgPSBTdW1IMiArIEgqKjIgICAhIOODj+ODn+ODq+ODiOODi+OCouODs+OBru+8kuS5lwpTdW1UICA9IFN1bVQgICsgVCAgICAgICEg6YGL5YuV44Ko44ON44Or44Ku44O8ClN1bVQyID0gU3VtVDIgKyBUKioyICAgISDpgYvli5Xjgqjjg43jg6vjgq7jg7zjga7vvJLkuZcKCnJldHVybgpFTkQgU1VCUk9VVElORSBWZXJsZXQKCgoKU1VCUk9VVElORSBPdXRwdXQobW9kZSwgUHJpbnRJbnQsIE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0sIER0LCBSLCBILCBULCBQLCBWLCBIMCwgVjAsICYKICAgICAgICAgICAgICAgICAgU3VtSCwgU3VtSDIsIFN1bVQsIFN1bVQyLCBNYXhFcnJIKQohLS0tLS0tLS0tLS0tIQohIOe1kOaenOOBruWHuuWKmyAhCiEtLS0tLS0tLS0tLS0hCklNUExJQ0lUIE5PTkUKSU5URUdFUiwgSU5URU5UKGluKSAgICA6OiBtb2RlLCBQcmludEludCwgTnBUb3QsIG4sIE1EU3RlcCwgTlN1bQpSRUFMKDgpLCBJTlRFTlQoaW4pICAgIDo6IER0LCBSKDMsTnBUb3QpLCBILCBULCBQLCBWKDMsTnBUb3QpLCBIMCwgVjAsIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMgpSRUFMKDgpLCBJTlRFTlQoaW5vdXQpIDo6IE1heEVyckgKISBMb2NhbCBzdHVmZgpSRUFMKDgpIHRpbWUsIEF2ZUgsIEF2ZUgyLCBBdmVULCBBdmVUMiwgUk1TRF9ILCBSTVNEX1QKCmlmIChtb2RlID09IDApIHRoZW4KICAhLS0tLS0gT3V0cHV0IGRhdGEgYXQgdGhlIGN1cnJlbnQgdGltZSAtLS0tLQogIHRpbWUgPSBEdCpyZWFsKG4sOCkKCiAgaWYgKCAobj09TURTdGVwKSAub3IuIChtb2QobixQcmludEludCk9PTApICkgJgogICAgd3JpdGUoNiwgJyggNShlMTQuNiksIGUyMy4xNSApJykgdGltZSwgUigxLDIpLCBWKDEsMiksIFQsIFAsIEgKCiAgTWF4RXJySD0gbWF4KE1heEVyckgsIGFicyhILUgwKSkgISBNYXhpbXVtIGVycm9yIGluIEhhbWlsdG9uaWFuCgoKZWxzZWlmIChtb2RlID09IDEpIHRoZW4KICAhLS0tLS0gQ29tcHV0ZSByb290IG1lYW4gc3F1YXJlIGRldmlhdGlvbiAoUk1TRCkgLS0tLS0KICAhIGNvbXB1dGUgYXZlcmFnZXMKICBBdmVIICA9IFN1bUggL3JlYWwoTlN1bSw4KSAgISDjg4/jg5/jg6vjg4jjg4vjgqLjg7MKICBBdmVIMiA9IFN1bUgyL3JlYWwoTlN1bSw4KSAgISDjg4/jg5/jg6vjg4jjg4vjgqLjg7Pjga7vvJLkuZcKICBBdmVUICA9IFN1bVQgL3JlYWwoTlN1bSw4KSAgISDpgYvli5Xjgqjjg43jg6vjgq7jg7wKICBBdmVUMiA9IFN1bVQyL3JlYWwoTlN1bSw4KSAgISDpgYvli5Xjgqjjg43jg6vjgq7jg7zjga7vvJLkuZcKCiAgISBjb21wdXRlIFJNU0QKICBSTVNEX0ggPSBzcXJ0KGFicyhBdmVIMi1BdmVIKioyKSkKICBSTVNEX1QgPSBzcXJ0KGFicyhBdmVUMi1BdmVUKioyKSkKCiAgISBwcmludAogIHdyaXRlKDYsICopCiAgd3JpdGUoNiwgKikgJy0tLS0tLS0tLScKICB3cml0ZSg2LCAqKSAnIFN1bW1hcnkgJwogIHdyaXRlKDYsICopICctLS0tLS0tLS0nCiAgd3JpdGUoNiwgJyggYSwgZTIzLjE1ICknKSAgICAnRHQgICAgICAgICAgID0nLCBEdAogIHdyaXRlKDYsICcoIGEsIGUyMy4xNSApJykgICAgJ1YwICAgICAgICAgICA9JywgVjAKICB3cml0ZSg2LCAnKCBhLCBlMjMuMTUgKScpICAgICdSTVNEKEgpICAgICAgPScsIFJNU0RfSAogIHdyaXRlKDYsICcoIGEsIGUyMy4xNSApJykgICAgJ01heC4gZXJyLihIKSA9JywgTWF4RXJySAogIHdyaXRlKDYsICcoIGEsIGUyMy4xNSApJykgICAgJ0ZpbmFsIHBvcy4gICA9JywgUigxLDIpCiAgd3JpdGUoNiwgJyggMihhLCBlMjMuMTUpICknKSAnPEg+PScsIEF2ZUgsICcgKy0gJywgUk1TRF9ICiAgd3JpdGUoNiwgJyggMihhLCBlMjMuMTUpICknKSAnPFQ+PScsIEF2ZVQsICcgKy0gJywgUk1TRF9UCiAgd3JpdGUoNiwgJyggYSwgaTEwICknKSAgICAnTm9ybS4gY29uc3QuKE5TdW0pPSAgJywgTlN1bQoKZW5kaWYKCnJldHVybgpFTkQgU1VCUk9VVElORSBPdXRwdXQK