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,'(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+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+d5a2YIC0tLS0tCkgwID0gSAoKCiEtLS0tLSDlh7rlipsgLS0tLS0KISDjg5jjg4Pjg4Djg7zmg4XloLHjga7lh7rlipsKd3JpdGUoNiwnKGEpJykgJyN0aW1lLCAgcG9zaXRpb24sICB2ZWxvY2l0eSwgIGtpbmV0aWMsICBwb3RlbnRpYWwsICBoYW1pbHRvbmlhbicKCiEg5L2N572u44CB6YCf5bqm44Gq44Gp44Gu5Ye65YqbLgpjYWxsIE91dHB1dCAoMCwgUHJpbnRJbnQsIE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0sIER0LCBSLCBILCBULCBQLCBWLCAmCiAgICAgICAgICAgICBIMCwgVjAsIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMiwgTWF4RXJySCkKCgohLS0tLS0gMuOCueODhuODg+ODl+ebruS7pemZjeOBruaZgumWk+eZuuWxlSAtLS0tLQpkbyBuPSAxLCBNRFN0ZXAKCiAgISBu44K544OG44OD44OX55uu44Gn44Gu5Yqb44Gu6KiI566XCiAgY2FsbCBGb3JjZVBvdGVudGlhbCAoTnBUb3QsIG4sIE1EU3RlcCwgUjAsIGRSLCBFcHMsIFNpZ21hLCBQLCBGKQoKICAhIChuKzEp44K544OG44OD44OX55uu44Gu5bqn5qiZ44KS6KiI566XCiAgY2FsbCBWZXJsZXQgKE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0sIER0LCBNYXNzLCBSMCwgUCwgUiwgRiwgViwgJgogICAgICAgICAgICAgICBkUiwgZFJfcHJldiwgZFJfbmV4dCwgSCwgVCwgU3VtSCwgU3VtSDIsIFN1bVQsIFN1bVQyKQoKICAhIOWHuuWKmwogIGNhbGwgT3V0cHV0ICgwLCBQcmludEludCwgTnBUb3QsIG4sIE1EU3RlcCwgTlN1bSwgRHQsIFIsIEgsIFQsIFAsIFYsICYKICAgICAgICAgICAgICAgSDAsIFYwLCBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIsIE1heEVyckgpCgplbmRkbyAhIG4KCgohLS0tLS0g5ZCE56iu5bmz5Z2H5YCk44KS5Ye65YqbIC0tLS0tCmNhbGwgT3V0cHV0ICgxLCBQcmludEludCwgTnBUb3QsIG4sIE1EU3RlcCwgTlN1bSwgRHQsIFIsIEgsIFQsIFAsIFYsICYKICAgICAgICAgICAgIEgwLCBWMCwgU3VtSCwgU3VtSDIsIFN1bVQsIFN1bVQyLCBNYXhFcnJIKQoKd3JpdGUoNiwqKSAnIERvbmUuJwp3cml0ZSg2LCopIAoKIS0tLS0tIOS4u+ODl+ODreOCsOODqeODoOOBrue1guS6hiAtLS0tLQpFTkQgUFJPR1JBTSBjbHVzdGVyCgoKClNVQlJPVVRJTkUgRm9yY2VQb3RlbnRpYWwoTnBUb3QsIG4sIE1EU3RlcCwgUjAsIGRSLCBFcHMsIFNpZ21hLCBQLCBGKQohLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSEKISDjg53jg4bjg7Pjgrfjg6Pjg6vjgqjjg43jg6vjgq7jg7zjgajlipvjga7oqIjnrpcgIQohLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSEKSU1QTElDSVQgTk9ORQpJTlRFR0VSLCBJTlRFTlQoaW4pICAgIDo6IE5wVG90LCBuLCBNRFN0ZXAKUkVBTCg4KSwgSU5URU5UKGluKSAgICA6OiBSMCgzLE5wVG90KSwgZFIoMyxOcFRvdCksIEVwcywgU2lnbWEKUkVBTCg4KSwgSU5URU5UKGlub3V0KSA6OiBQLCBGKDMsTnBUb3QpCiEgTG9jYWwgc3R1ZmYKSU5URUdFUiBpLCBqClJFQUwoOCkgUjEsIFIyLCBSaWooMyksIGRwZHIsIGRyZHYoMykKCkYoOiw6KT0wLmQwIDsgUD0wLmQwCgpkbyBpPSAxLCBOcFRvdAogIGRvIGo9IDEsIE5wVG90CgogICAgaWYgKGkgLz0gaikgdGhlbgoKICAgICAgISBkUjogZGlzcGxhY2VtZW50IGZyb20gdGltZSAwIHRvIHRpbWUgbgogICAgICBSaWooOikgPSAoZFIoOixqKSAtIGRSKDosaSkpICsgKFIwKDosaikgLSBSMCg6LGkpKQogICAgICBSMiA9IFJpaigxKSoqMiArIFJpaigyKSoqMiArIFJpaigzKSoqMiAKICAgICAgUjEgPSBzcXJ0KFIyKQoKICAgICAgISBwb3RlbnRpYWwgZW5lcmd5CiAgICAgIFAgPSBQICsgNC5kMCAqIEVwcyAqICgoU2lnbWEqKjIvUjIpKio2IC0gKFNpZ21hKioyL1IyKSoqMykKCiAgICAgICEgZm9yY2UKICAgICAgZHBkciA9IDQuZDAgKiBFcHMgKiAoLTEyLmQwKihTaWdtYSoqMi9SMikqKjYgKyA2LmQwKihTaWdtYSoqMi9SMikqKjMpIC8gUjEKICAgICAgZHJkdig6KSA9IC1SaWooOikgLyBSMQogICAgICBGKDosaSkgPSBGKDosaSkgLSBkcGRyKmRyZHYoOikKCiAgICBlbmRpZiAhIGkgLz0gagoKICBlbmRkbyAhIGoKZW5kZG8gISBpCgpQID0gMC41ZDAqUAoKcmV0dXJuCkVORCBTVUJST1VUSU5FIEZvcmNlUG90ZW50aWFsCgoKClNVQlJPVVRJTkUgVmVybGV0KE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0sIER0LCBNYXNzLCBSMCwgUCwgUiwgRiwgViwgJgogICAgICAgICAgICAgICAgICBkUiwgZFJfcHJldiwgZFJfbmV4dCwgSCwgVCwgU3VtSCwgU3VtSDIsIFN1bVQsIFN1bVQyKQohLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0hCiEgVmVybGV05rOV44Gr44KI44KL5bqn5qiZ44Gu5pu05pawICEKIS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIQpJTVBMSUNJVCBOT05FCklOVEVHRVIsIElOVEVOVChpbikgICAgOjogTnBUb3QsIG4sIE1EU3RlcApJTlRFR0VSLCBJTlRFTlQoaW5vdXQpIDo6IE5TdW0KUkVBTCg4KSwgSU5URU5UKGluKSAgICA6OiBSMCgzLE5wVG90KSwgUCwgRHQsIE1hc3MKUkVBTCg4KSwgSU5URU5UKGlub3V0KSA6OiBSKDMsTnBUb3QpLCBGKDMsTnBUb3QpLCBWKDMsTnBUb3QpLCBkUigzLE5wVG90KSwgJgogICAgICAgICAgICAgICAgICAgICAgICAgIGRSX3ByZXYoMyxOcFRvdCksIGRSX25leHQoMyxOcFRvdCksIEgsIFQsIFN1bUgsICAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgU3VtSDIsIFN1bVQsIFN1bVQyCiEgTG9jYWwgc3R1ZmYKSU5URUdFUiBpCgohLS0tLS0gMC10aCBzdGVwIC0tLS0tCmlmIChuID09IDApIHRoZW4KICAhIGN1cnJlbnQgcG9zaXRpb24KICBSKDosOikgPSBSMCg6LDopIAoKICAhIGRSID0gZFJfbmV4dCA9IFIozpR0KSAtIFIoMCkgPSBWKDApzpR0ICsgYSgwKSoozpR0KV4yLzIgICAKICBkbyBpPSAxLCBOcFRvdAogICAgZFJfbmV4dCg6LGkpID0gVig6LGkpKkR0ICsgMC41ZDAqRig6LGkpKkR0KioyL01hc3MKICAgIGRSKDosaSkgPSBkUl9uZXh0KDosaSkKICBlbmRkbyAhIGkKCgohLS0tLS0gbGF0ZXIgc3RlcHMgLS0tLS0KZWxzZWlmIChuID49IDEpIHRoZW4KICAhIGN1cnJlbnQgcG9zaXRpb24KICBSKDosOikgPSBSMCg6LDopICsgZFIoOiw6KQoKICAhIGRSX25leHQgPSBSKHQrzpR0KSAtIFIodCkgPSBSKHQpIC0gUih0Lc6UdCkgKyBhKHQpKijOlHQpXjIgICAKICAhIGRSX3ByZXYgPSBSKHQpIC0gUih0Lc6UdCkKICAhIGRSICAgICAgPSBSKHQrzpR0KSAtIFIoMCkKICBkbyBpPSAxLCBOcFRvdAogICAgZFJfbmV4dCg6LGkpID0gZFJfcHJldig6LGkpICsgRig6LGkpKkR0KioyL01hc3MKICAgIGRSKDosaSkgPSBkUig6LGkpICsgZFJfbmV4dCg6LGkpCiAgICBWKDosaSkgPSAwLjVkMCAqIChkUl9uZXh0KDosaSkgKyBkUl9wcmV2KDosaSkpIC8gRHQKICBlbmRkbwoKZW5kaWYKCgohLS0tLS0gUmVuYW1pbmcgZm9yIHVzZSBhdCB0aGUgbmV4dCBzdGVwIC0tLS0tCmRSX3ByZXYoOiw6KT0gZFJfbmV4dCg6LDopCgoKIS0tLS0tIOmBi+WLleOCqOODjeODq+OCruODvOOBruioiOeulyAtLS0tLQpUID0gMC5kMApkbyBpPSAxLCBOcFRvdAogIFQgPSBUICsgMC41ZDAgKiBNYXNzICogKFYoMSxpKSoqMiArIFYoMixpKSoqMiArIFYoMyxpKSoqMikKZW5kZG8KCgohLS0tLS0g44OP44Of44Or44OI44OL44Ki44Oz44Gu6KiI566XIC0tLS0tCkggPSBUICsgUAoKCiEtLS0tLSDok4TnqY0gLS0tLS0KTlN1bSAgPSBOU3VtICArIDEgICAgICAhIOiThOepjeOBruWbnuaVsApTdW1IICA9IFN1bUggICsgSCAgICAgICEg44OP44Of44Or44OI44OL44Ki44OzClN1bUgyID0gU3VtSDIgKyBIKioyICAgISDjg4/jg5/jg6vjg4jjg4vjgqLjg7Pjga7vvJLkuZcKU3VtVCAgPSBTdW1UICArIFQgICAgICAhIOmBi+WLleOCqOODjeODq+OCruODvApTdW1UMiA9IFN1bVQyICsgVCoqMiAgICEg6YGL5YuV44Ko44ON44Or44Ku44O844Gu77yS5LmXCgpyZXR1cm4KRU5EIFNVQlJPVVRJTkUgVmVybGV0CgoKClNVQlJPVVRJTkUgT3V0cHV0KG1vZGUsIFByaW50SW50LCBOcFRvdCwgbiwgTURTdGVwLCBOU3VtLCBEdCwgUiwgSCwgVCwgUCwgViwgSDAsIFYwLCAmCiAgICAgICAgICAgICAgICAgIFN1bUgsIFN1bUgyLCBTdW1ULCBTdW1UMiwgTWF4RXJySCkKIS0tLS0tLS0tLS0tLSEKISDntZDmnpzjga7lh7rlipsgIQohLS0tLS0tLS0tLS0tIQpJTVBMSUNJVCBOT05FCklOVEVHRVIsIElOVEVOVChpbikgICAgOjogbW9kZSwgUHJpbnRJbnQsIE5wVG90LCBuLCBNRFN0ZXAsIE5TdW0KUkVBTCg4KSwgSU5URU5UKGluKSAgICA6OiBEdCwgUigzLE5wVG90KSwgSCwgVCwgUCwgVigzLE5wVG90KSwgSDAsIFYwLCBTdW1ILCBTdW1IMiwgU3VtVCwgU3VtVDIKUkVBTCg4KSwgSU5URU5UKGlub3V0KSA6OiBNYXhFcnJICiEgTG9jYWwgc3R1ZmYKUkVBTCg4KSB0aW1lLCBBdmVILCBBdmVIMiwgQXZlVCwgQXZlVDIsIFJNU0RfSCwgUk1TRF9UCgppZiAobW9kZSA9PSAwKSB0aGVuCiAgIS0tLS0tIE91dHB1dCBkYXRhIGF0IHRoZSBjdXJyZW50IHRpbWUgLS0tLS0KICB0aW1lID0gRHQqcmVhbChuLDgpCgogIGlmICggKG49PU1EU3RlcCkgLm9yLiAobW9kKG4sUHJpbnRJbnQpPT0wKSApICYKICAgIHdyaXRlKDYsICcoIGUxMi42LCA0KGUxNC42KSwgZTIzLjE1ICknKSB0aW1lLCBSKDEsMiksIFYoMSwyKSwgVCwgUCwgSAoKICBNYXhFcnJIPSBtYXgoTWF4RXJySCwgYWJzKEgtSDApKSAhIE1heGltdW0gZXJyb3IgaW4gSGFtaWx0b25pYW4KCgplbHNlaWYgKG1vZGUgPT0gMSkgdGhlbgogICEtLS0tLSBDb21wdXRlIHJvb3QgbWVhbiBzcXVhcmUgZGV2aWF0aW9uIChSTVNEKSAtLS0tLQogICEgY29tcHV0ZSBhdmVyYWdlcwogIEF2ZUggID0gU3VtSCAvcmVhbChOU3VtLDgpICAhIOODj+ODn+ODq+ODiOODi+OCouODswogIEF2ZUgyID0gU3VtSDIvcmVhbChOU3VtLDgpICAhIOODj+ODn+ODq+ODiOODi+OCouODs+OBru+8kuS5lwogIEF2ZVQgID0gU3VtVCAvcmVhbChOU3VtLDgpICAhIOmBi+WLleOCqOODjeODq+OCruODvAogIEF2ZVQyID0gU3VtVDIvcmVhbChOU3VtLDgpICAhIOmBi+WLleOCqOODjeODq+OCruODvOOBru+8kuS5lwoKICAhIGNvbXB1dGUgUk1TRAogIFJNU0RfSCA9IHNxcnQoYWJzKEF2ZUgyLUF2ZUgqKjIpKQogIFJNU0RfVCA9IHNxcnQoYWJzKEF2ZVQyLUF2ZVQqKjIpKQoKICAhIHByaW50CiAgd3JpdGUoNiwgKikKICB3cml0ZSg2LCAqKSAnLS0tLS0tLS0tJwogIHdyaXRlKDYsICopICcgU3VtbWFyeSAnCiAgd3JpdGUoNiwgKikgJy0tLS0tLS0tLScKICB3cml0ZSg2LCAnKCBhLCBlMjMuMTUgKScpICAgICdEdCAgICAgICAgICAgPScsIER0CiAgd3JpdGUoNiwgJyggYSwgZTIzLjE1ICknKSAgICAnVjAgICAgICAgICAgID0nLCBWMAogIHdyaXRlKDYsICcoIGEsIGUyMy4xNSApJykgICAgJ1JNU0QoSCkgICAgICA9JywgUk1TRF9ICiAgd3JpdGUoNiwgJyggYSwgZTIzLjE1ICknKSAgICAnTWF4LiBlcnIuKEgpID0nLCBNYXhFcnJICiAgd3JpdGUoNiwgJyggYSwgZTIzLjE1ICknKSAgICAnRmluYWwgcG9zLiAgID0nLCBSKDEsMikKICB3cml0ZSg2LCAnKCAyKGEsIGUyMy4xNSkgKScpICc8SD49JywgQXZlSCwgJyArLSAnLCBSTVNEX0gKICB3cml0ZSg2LCAnKCAyKGEsIGUyMy4xNSkgKScpICc8VD49JywgQXZlVCwgJyArLSAnLCBSTVNEX1QKICB3cml0ZSg2LCAnKCBhLCBpMTAgKScpICAgICdOb3JtLiBjb25zdC4oTlN1bSk9ICAnLCBOU3VtCgplbmRpZgoKcmV0dXJuCkVORCBTVUJST1VUSU5FIE91dHB1dA==