Go back to the Contents page.
Press Show to reveal the code chunks.
# Set seed for reproducibility
set.seed(1982)
# Set global options for all code chunks
knitr::opts_chunk$set(
# Disable messages printed by R code chunks
message = FALSE,
# Disable warnings printed by R code chunks
warning = FALSE,
# Show R code within code chunks in output
echo = TRUE,
# Include both R code and its results in output
include = TRUE,
# Evaluate R code chunks
eval = TRUE,
# Enable caching of R code chunks for faster rendering
cache = FALSE,
# Align figures in the center of the output
fig.align = "center",
# Enable retina display for high-resolution figures
retina = 2,
# Show errors in the output instead of stopping rendering
error = TRUE,
# Do not collapse code and output into a single block
collapse = FALSE
)
# Start the figure counter
fig_count <- 0
# Define the captioner function
captioner <- function(caption) {
fig_count <<- fig_count + 1
paste0("Figure ", fig_count, ": ", caption)
}
capture.output(
knitr::purl(here::here("functionality.Rmd"), output = here::here("functionality.R")),
file = here::here("purl_log.txt")
)
source(here::here("functionality.R"))
library(fmesher)
library(Matrix)
We consider the following equation on the sphere
\[\begin{equation}
\label{eq:maineq}
\tag{1}
\left\{
\begin{aligned}
\partial_t u(s,t) + (\kappa^2 - \Delta_{\mathbb{S}^2})^{\alpha/2}
u(s,t) &= f(s,t), && \quad (s,t) \in \mathbb{S}^2 \times (0,
T), \\
u(s,0) &= u_0(s), && \quad s \in \mathbb{S}^2,
\end{aligned}
\right.
\end{equation}\]
where \(\Delta_{\mathbb{S}^2}\) is
the Laplace-Beltrami operator on the sphere \(\mathbb{S}^2\).
The eigenvalues and eigenfunctions of the Laplace-Beltrami operator
on the sphere are known explicitly. The eigenvalues are given by \(\lambda_\ell = \ell(\ell+1)\), with
multiplicity \(2\ell + 1\), and the
eigenfunctions are the spherical harmonics \(Y_{\ell,m}\), for \(\ell = 0, 1, 2, \ldots\) and \(m = -\ell, \ldots, \ell\), which satisfy
the eigenvalue equation
\[\begin{equation}
-\Delta_{\mathbb{S}^2} Y_{\ell,m} = \lambda_\ell Y_{\ell,m}.
\end{equation}\]
Below we use the eigenpairs to construct an exact solution to
equation \(\eqref{eq:maineq}\) and
compare it with the numerical approximation. We take \(u_0(s) = x_3 Y_{3,0}(s)\) with \(x_3 = 1\) and \(f(s,t) = y_2 \sin(\pi t) Y_{2,0}(s)\) with
\(y_2 = 10\). Then the exact solution
is given by \[\begin{equation}
u(s,t) = x_3\text{e}^{-\lambda^{\alpha/2}_3t}Y_{3,0}(s)+y_2
G_2(t)\text{e}^{-\lambda^{\alpha/2}_2t}Y_{2,0}(s),
\end{equation}\] where \(G_2(t)=
\displaystyle\int_0^t \text{e}^{\lambda^{\alpha/2}_2r}\sin(\pi
r)dr\). Below the exact solution is computed as
U_true and compared with the numerical approximation
U_approx.
We take parameters \(T = 2\), \(\kappa = 4\), \(\alpha = 1.8\), \(m=4\), \(h =
0.05\), and time step size \(\tau =
0.05\).
T_final <- 2
kappa <- 4
L_max <- 10
rot.inv <- TRUE
coeff_U_0 <- rep(0, L_max+1)
coeff_U_0[3] <- 1
coeff_FF <- rep(0, L_max+1)
coeff_FF[2] <- 10
AAA = 1
OMEGA = pi
alpha <- 1.8
m = 4
beta <- alpha/2
time_step <- 0.05
time_seq <- seq(0, T_final, length.out = ((T_final - 0) / time_step + 1))
h <- 0.05
globe_coarse <- globe_from_h(h)
globe_fine <- 2*globe_coarse
mesh_coarse <- fm_rcdt_2d(globe = globe_coarse)
FEM_coarse <- fm_fem(mesh_coarse, order = 1)
C <- FEM_coarse$c1
G <- FEM_coarse$g1
L <- kappa^2 * C + G
eig <- compute_true_eigen_sphere(
mesh = mesh_coarse,
kappa = kappa,
alpha = alpha,
L_max = L_max,
rot.inv = rot.inv)
EIGENVAL_ALPHA <- eig$EIGENVAL_ALPHA
EIGENFUN <- eig$EIGENFUN
U_0 <- EIGENFUN %*% coeff_U_0
U_true <- EIGENFUN %*%
outer(1:length(coeff_U_0),
1:length(time_seq),
function(i, j) (coeff_U_0[i] + coeff_FF[i] * G_sin(t = time_seq[j], A = AAA, lambda_j_alpha_half = EIGENVAL_ALPHA[i], omega = OMEGA)) * exp(-EIGENVAL_ALPHA[i] * time_seq[j]))
mesh_fine <- fm_rcdt_2d(globe = globe_fine)
FEM_fine <- fm_fem(mesh_fine, order = 1)
Psi <- fm_basis(mesh_coarse, mesh_fine$loc)
eig_fine <- compute_true_eigen_sphere(
mesh = mesh_fine,
kappa = kappa,
alpha = alpha,
L_max = L_max,
rot.inv = rot.inv)
EIGENFUN_fine <- eig_fine$EIGENFUN
FF_approx <- t(Psi) %*%
FEM_fine$c1 %*%
EIGENFUN_fine %*%
outer(1:length(coeff_FF),
1:length(time_seq),
function(i, j) coeff_FF[i] * g_sin(r = time_seq[j], A = AAA, omega = OMEGA))
my_op_frac <- my.fractional.operators.frac(L, beta, C, scale.factor = kappa^2, m = m, time_step)
U_approx <- solve_fractional_evolution(my_op_frac, time_step, time_seq, val_at_0 = U_0, RHST = FF_approx)
diff <- U_true - U_approx
error <- sqrt(as.double(time_step * sum(diff * (C %*% diff))))
error
## [1] 0.1540148
Below we visualize the first few eigenfunctions on the sphere.
plot_3d_slider_sphere(mesh_coarse, eig$EIGENVAL, EIGENFUN, fixed_colorscale = TRUE)
Below we show the absolute value of the difference \(|u(s, 0.5) - U^\tau_{h,m}(s,0.5)|\) and the
function value \(U^\tau_{h,m}(s, 0.5)\)
at \(t=0.5\).
idx <- which.min(abs(time_seq - 0.5))
error_sphere <- plot_3d_sphere_surface_onecol(mesh_coarse, abs(diff[,idx]))
save(error_sphere, file = here::here("data_files/exp_1_error_sphere.RData"))
error_sphere
f_sphere <- plot_3d_sphere_surface_onecol(mesh_coarse, U_approx[,idx])
f_sphere
save(f_sphere, file = here::here("data_files/exp_1_f_sphere.RData"))
LS0tCnRpdGxlOiAiRXhhbXBsZSBvbiB0aGUgc3BoZXJlIgpkYXRlOiAiTGFzdCBtb2RpZmllZDogYHIgZm9ybWF0KFN5cy50aW1lKCksICclZC0lbS0lWS4nKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgbWF0aGpheDogImh0dHBzOi8vY2RuLmpzZGVsaXZyLm5ldC9ucG0vbWF0aGpheEAzL2VzNS90ZXgtbW1sLWNodG1sLmpzIgogICAgaGlnaGxpZ2h0OiBweWdtZW50cwogICAgdGhlbWU6IGZsYXRseQogICAgY29kZV9mb2xkaW5nOiBoaWRlICMgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIgdG8gaGlkZSBjb2RlIGFuZCBhZGQgYSBidXR0b24gdG8gc2hvdyBpdAogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNzczogdmlzdWFsLmNzcwphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZQpiaWJsaW9ncmFwaHk6IAogIC0gcmVmZXJlbmNlcy5iaWIKICAtIGdyYXRlZnVsLXJlZnMuYmliCmhlYWRlci1pbmNsdWRlczoKICAtIFxuZXdjb21tYW5ke1xhcn17XG1hdGhiYntSfX0KICAtIFxuZXdjb21tYW5ke1xsbGF2fVsxXXtcbGVmdFx7IzFccmlnaHRcfX0KICAtIFxuZXdjb21tYW5ke1xwYXJlfVsxXXtcbGVmdCgjMVxyaWdodCl9CiAgLSBcbmV3Y29tbWFuZHtcTmNhbH17XG1hdGhjYWx7Tn19CiAgLSBcbmV3Y29tbWFuZHtcVmNhbH17XG1hdGhjYWx7Vn19CiAgLSBcbmV3Y29tbWFuZHtcRWNhbH17XG1hdGhjYWx7RX19CiAgLSBcbmV3Y29tbWFuZHtcV2NhbH17XG1hdGhjYWx7V319Ci0tLQoKR28gYmFjayB0byB0aGUgW0NvbnRlbnRzXShhYm91dC5odG1sKSBwYWdlLgoKPGRpdiBzdHlsZT0iY29sb3I6ICMyYzNlNTA7IHRleHQtYWxpZ246IHJpZ2h0OyI+CioqKioqKioqICAKPHN0cm9uZz5QcmVzcyBTaG93IHRvIHJldmVhbCB0aGUgY29kZSBjaHVua3MuPC9zdHJvbmc+ICAKCioqKioqKioqCjwvZGl2PgoKCmBgYHtyLCBwdXJsID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0KIyBDcmVhdGUgYSBjbGlwYm9hcmQgYnV0dG9uIG9uIHRoZSByZW5kZXJlZCBIVE1MIHBhZ2UKc291cmNlKGhlcmU6OmhlcmUoImNsaXBib2FyZC5SIikpOyBjbGlwYm9hcmQKYGBgCgoKYGBge3IsIHB1cmwgPSBGQUxTRSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxOTgyKSAKIyBTZXQgZ2xvYmFsIG9wdGlvbnMgZm9yIGFsbCBjb2RlIGNodW5rcwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgIyBEaXNhYmxlIG1lc3NhZ2VzIHByaW50ZWQgYnkgUiBjb2RlIGNodW5rcwogIG1lc3NhZ2UgPSBGQUxTRSwgICAgCiAgIyBEaXNhYmxlIHdhcm5pbmdzIHByaW50ZWQgYnkgUiBjb2RlIGNodW5rcwogIHdhcm5pbmcgPSBGQUxTRSwgICAgCiAgIyBTaG93IFIgY29kZSB3aXRoaW4gY29kZSBjaHVua3MgaW4gb3V0cHV0CiAgZWNobyA9IFRSVUUsICAgICAgICAKICAjIEluY2x1ZGUgYm90aCBSIGNvZGUgYW5kIGl0cyByZXN1bHRzIGluIG91dHB1dAogIGluY2x1ZGUgPSBUUlVFLCAgICAgCiAgIyBFdmFsdWF0ZSBSIGNvZGUgY2h1bmtzCiAgZXZhbCA9IFRSVUUsICAgICAgIAogICMgRW5hYmxlIGNhY2hpbmcgb2YgUiBjb2RlIGNodW5rcyBmb3IgZmFzdGVyIHJlbmRlcmluZwogIGNhY2hlID0gRkFMU0UsICAgICAgCiAgIyBBbGlnbiBmaWd1cmVzIGluIHRoZSBjZW50ZXIgb2YgdGhlIG91dHB1dAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogICMgRW5hYmxlIHJldGluYSBkaXNwbGF5IGZvciBoaWdoLXJlc29sdXRpb24gZmlndXJlcwogIHJldGluYSA9IDIsCiAgIyBTaG93IGVycm9ycyBpbiB0aGUgb3V0cHV0IGluc3RlYWQgb2Ygc3RvcHBpbmcgcmVuZGVyaW5nCiAgZXJyb3IgPSBUUlVFLAogICMgRG8gbm90IGNvbGxhcHNlIGNvZGUgYW5kIG91dHB1dCBpbnRvIGEgc2luZ2xlIGJsb2NrCiAgY29sbGFwc2UgPSBGQUxTRQopCiMgU3RhcnQgdGhlIGZpZ3VyZSBjb3VudGVyCmZpZ19jb3VudCA8LSAwCiMgRGVmaW5lIHRoZSBjYXB0aW9uZXIgZnVuY3Rpb24KY2FwdGlvbmVyIDwtIGZ1bmN0aW9uKGNhcHRpb24pIHsKICBmaWdfY291bnQgPDwtIGZpZ19jb3VudCArIDEKICBwYXN0ZTAoIkZpZ3VyZSAiLCBmaWdfY291bnQsICI6ICIsIGNhcHRpb24pCn0KYGBgCgoKYGBge3J9CmNhcHR1cmUub3V0cHV0KAogIGtuaXRyOjpwdXJsKGhlcmU6OmhlcmUoImZ1bmN0aW9uYWxpdHkuUm1kIiksIG91dHB1dCA9IGhlcmU6OmhlcmUoImZ1bmN0aW9uYWxpdHkuUiIpKSwKICBmaWxlID0gaGVyZTo6aGVyZSgicHVybF9sb2cudHh0IikKKQpzb3VyY2UoaGVyZTo6aGVyZSgiZnVuY3Rpb25hbGl0eS5SIikpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGZtZXNoZXIpCmxpYnJhcnkoTWF0cml4KQpgYGAKCgpXZSBjb25zaWRlciB0aGUgZm9sbG93aW5nIGVxdWF0aW9uIG9uIHRoZSBzcGhlcmUKClxiZWdpbntlcXVhdGlvbn0KXGxhYmVse2VxOm1haW5lcX0KXHRhZ3sxfQpcbGVmdFx7ClxiZWdpbnthbGlnbmVkfQogICAgXHBhcnRpYWxfdCB1KHMsdCkgKyAoXGthcHBhXjIgLSBcRGVsdGFfe1xtYXRoYmJ7U31eMn0pXntcYWxwaGEvMn0gdShzLHQpICY9IGYocyx0KSwgJiYgXHF1YWQgKHMsdCkgXGluIFxtYXRoYmJ7U31eMiBcdGltZXMgKDAsIFQpLCBcXAogICAgdShzLDApICY9IHVfMChzKSwgJiYgXHF1YWQgcyBcaW4gXG1hdGhiYntTfV4yLApcZW5ke2FsaWduZWR9ClxyaWdodC4KXGVuZHtlcXVhdGlvbn0KCndoZXJlICRcRGVsdGFfe1xtYXRoYmJ7U31eMn0kIGlzIHRoZSBMYXBsYWNlLUJlbHRyYW1pIG9wZXJhdG9yIG9uIHRoZSBzcGhlcmUgJFxtYXRoYmJ7U31eMiQuCgpUaGUgZWlnZW52YWx1ZXMgYW5kIGVpZ2VuZnVuY3Rpb25zIG9mIHRoZSBMYXBsYWNlLUJlbHRyYW1pIG9wZXJhdG9yIG9uIHRoZSBzcGhlcmUgYXJlIGtub3duIGV4cGxpY2l0bHkuIFRoZSBlaWdlbnZhbHVlcyBhcmUgZ2l2ZW4gYnkgJFxsYW1iZGFfXGVsbCA9IFxlbGwoXGVsbCsxKSQsIHdpdGggbXVsdGlwbGljaXR5ICQyXGVsbCArIDEkLCBhbmQgdGhlIGVpZ2VuZnVuY3Rpb25zIGFyZSB0aGUgc3BoZXJpY2FsIGhhcm1vbmljcyAkWV97XGVsbCxtfSQsIGZvciAkXGVsbCA9IDAsIDEsIDIsIFxsZG90cyQgYW5kICRtID0gLVxlbGwsIFxsZG90cywgXGVsbCQsIHdoaWNoIHNhdGlzZnkgdGhlIGVpZ2VudmFsdWUgZXF1YXRpb24KClxiZWdpbntlcXVhdGlvbn0KLVxEZWx0YV97XG1hdGhiYntTfV4yfSBZX3tcZWxsLG19ID0gXGxhbWJkYV9cZWxsIFlfe1xlbGwsbX0uClxlbmR7ZXF1YXRpb259CgoKQmVsb3cgd2UgdXNlIHRoZSBlaWdlbnBhaXJzIHRvIGNvbnN0cnVjdCBhbiBleGFjdCBzb2x1dGlvbiB0byBlcXVhdGlvbiBcZXFyZWZ7ZXE6bWFpbmVxfSBhbmQgY29tcGFyZSBpdCB3aXRoIHRoZSBudW1lcmljYWwgYXBwcm94aW1hdGlvbi4gV2UgdGFrZSAkdV8wKHMpID0geF8zIFlfezMsMH0ocykkIHdpdGggJHhfMyA9IDEkIGFuZCAkZihzLHQpID0geV8yIFxzaW4oXHBpIHQpIFlfezIsMH0ocykkIHdpdGggJHlfMiA9IDEwJC4gVGhlbiB0aGUgZXhhY3Qgc29sdXRpb24gaXMgZ2l2ZW4gYnkgClxiZWdpbntlcXVhdGlvbn0KdShzLHQpID0geF8zXHRleHR7ZX1eey1cbGFtYmRhXntcYWxwaGEvMn1fM3R9WV97MywwfShzKSt5XzIgR18yKHQpXHRleHR7ZX1eey1cbGFtYmRhXntcYWxwaGEvMn1fMnR9WV97MiwwfShzKSwKXGVuZHtlcXVhdGlvbn0Kd2hlcmUgJEdfMih0KT0gXGRpc3BsYXlzdHlsZVxpbnRfMF50IFx0ZXh0e2V9XntcbGFtYmRhXntcYWxwaGEvMn1fMnJ9XHNpbihccGkgcilkciQuIEJlbG93IHRoZSBleGFjdCBzb2x1dGlvbiAgaXMgY29tcHV0ZWQgYXMgYFVfdHJ1ZWAgYW5kIGNvbXBhcmVkIHdpdGggdGhlIG51bWVyaWNhbCBhcHByb3hpbWF0aW9uIGBVX2FwcHJveGAuCgoKV2UgdGFrZSBwYXJhbWV0ZXJzICRUID0gMiQsICRca2FwcGEgPSA0JCwgJFxhbHBoYSA9IDEuOCQsICRtPTQkLCAkaCA9IDAuMDUkLCBhbmQgdGltZSBzdGVwIHNpemUgJFx0YXUgPSAwLjA1JC4KCgoKCgoKYGBge3J9ClRfZmluYWwgPC0gMgprYXBwYSA8LSA0CgoKTF9tYXggPC0gMTAKcm90LmludiA8LSBUUlVFCgoKY29lZmZfVV8wIDwtIHJlcCgwLCBMX21heCsxKQpjb2VmZl9VXzBbM10gPC0gMQpjb2VmZl9GRiA8LSByZXAoMCwgTF9tYXgrMSkKY29lZmZfRkZbMl0gPC0gMTAKCgpBQUEgPSAxCk9NRUdBID0gcGkKCmFscGhhIDwtIDEuOAptID0gNApiZXRhIDwtIGFscGhhLzIKCgp0aW1lX3N0ZXAgPC0gMC4wNQp0aW1lX3NlcSA8LSBzZXEoMCwgVF9maW5hbCwgbGVuZ3RoLm91dCA9ICgoVF9maW5hbCAtIDApIC8gdGltZV9zdGVwICsgMSkpCmggPC0gMC4wNQpnbG9iZV9jb2Fyc2UgPC0gZ2xvYmVfZnJvbV9oKGgpCmdsb2JlX2ZpbmUgPC0gMipnbG9iZV9jb2Fyc2UKYGBgCgoKCmBgYHtyfQptZXNoX2NvYXJzZSA8LSBmbV9yY2R0XzJkKGdsb2JlID0gZ2xvYmVfY29hcnNlKQpGRU1fY29hcnNlIDwtIGZtX2ZlbShtZXNoX2NvYXJzZSwgb3JkZXIgPSAxKQpDIDwtIEZFTV9jb2Fyc2UkYzEKRyA8LSBGRU1fY29hcnNlJGcxCkwgPC0ga2FwcGFeMiAqIEMgKyBHCgplaWcgPC0gY29tcHV0ZV90cnVlX2VpZ2VuX3NwaGVyZSgKICBtZXNoID0gbWVzaF9jb2Fyc2UsIAogIGthcHBhID0ga2FwcGEsIAogIGFscGhhID0gYWxwaGEsCiAgTF9tYXggPSBMX21heCwgCiAgcm90LmludiA9IHJvdC5pbnYpCkVJR0VOVkFMX0FMUEhBIDwtIGVpZyRFSUdFTlZBTF9BTFBIQQpFSUdFTkZVTiA8LSBlaWckRUlHRU5GVU4KVV8wIDwtIEVJR0VORlVOICUqJSBjb2VmZl9VXzAKVV90cnVlIDwtIEVJR0VORlVOICUqJSAKICAgIG91dGVyKDE6bGVuZ3RoKGNvZWZmX1VfMCksIAogICAgICAgICAgMTpsZW5ndGgodGltZV9zZXEpLCAKICAgICAgICAgIGZ1bmN0aW9uKGksIGopIChjb2VmZl9VXzBbaV0gKyBjb2VmZl9GRltpXSAqIEdfc2luKHQgPSB0aW1lX3NlcVtqXSwgQSA9IEFBQSwgbGFtYmRhX2pfYWxwaGFfaGFsZiA9IEVJR0VOVkFMX0FMUEhBW2ldLCBvbWVnYSA9IE9NRUdBKSkgKiBleHAoLUVJR0VOVkFMX0FMUEhBW2ldICogdGltZV9zZXFbal0pKQpgYGAKCgoKYGBge3J9Cm1lc2hfZmluZSA8LSBmbV9yY2R0XzJkKGdsb2JlID0gZ2xvYmVfZmluZSkKRkVNX2ZpbmUgPC0gZm1fZmVtKG1lc2hfZmluZSwgb3JkZXIgPSAxKQpQc2kgPC0gZm1fYmFzaXMobWVzaF9jb2Fyc2UsIG1lc2hfZmluZSRsb2MpCgplaWdfZmluZSA8LSBjb21wdXRlX3RydWVfZWlnZW5fc3BoZXJlKAogIG1lc2ggPSBtZXNoX2ZpbmUsIAogIGthcHBhID0ga2FwcGEsIAogIGFscGhhID0gYWxwaGEsIAogIExfbWF4ID0gTF9tYXgsIAogIHJvdC5pbnYgPSByb3QuaW52KQpFSUdFTkZVTl9maW5lIDwtIGVpZ19maW5lJEVJR0VORlVOCgpGRl9hcHByb3ggPC0gdChQc2kpICUqJSAKICBGRU1fZmluZSRjMSAlKiUKICBFSUdFTkZVTl9maW5lICUqJQogICAgICBvdXRlcigxOmxlbmd0aChjb2VmZl9GRiksIAogICAgICAgICAgICAxOmxlbmd0aCh0aW1lX3NlcSksIAogICAgICAgIGZ1bmN0aW9uKGksIGopIGNvZWZmX0ZGW2ldICogZ19zaW4ociA9IHRpbWVfc2VxW2pdLCBBID0gQUFBLCBvbWVnYSA9IE9NRUdBKSkKCm15X29wX2ZyYWMgPC0gbXkuZnJhY3Rpb25hbC5vcGVyYXRvcnMuZnJhYyhMLCBiZXRhLCBDLCBzY2FsZS5mYWN0b3IgPSBrYXBwYV4yLCBtID0gbSwgdGltZV9zdGVwKQpVX2FwcHJveCA8LSBzb2x2ZV9mcmFjdGlvbmFsX2V2b2x1dGlvbihteV9vcF9mcmFjLCB0aW1lX3N0ZXAsIHRpbWVfc2VxLCB2YWxfYXRfMCA9IFVfMCwgUkhTVCA9IEZGX2FwcHJveCkKCgpkaWZmIDwtIFVfdHJ1ZSAtIFVfYXBwcm94CmVycm9yIDwtIHNxcnQoYXMuZG91YmxlKHRpbWVfc3RlcCAqIHN1bShkaWZmICogKEMgJSolIGRpZmYpKSkpCmVycm9yCmBgYAoKQmVsb3cgd2UgdmlzdWFsaXplIHRoZSBmaXJzdCBmZXcgZWlnZW5mdW5jdGlvbnMgb24gdGhlIHNwaGVyZS4KCmBgYHtyLCBmaWcuaGVpZ2h0ID0gNiwgb3V0LndpZHRoID0gIjEwMCUiLCBmaWcuY2FwID0gY2FwdGlvbmVyKCJFaWdlbmZ1bmN0aW9ucyBvbiB0aGUgc3BoZXJlLiIpfQpwbG90XzNkX3NsaWRlcl9zcGhlcmUobWVzaF9jb2Fyc2UsIGVpZyRFSUdFTlZBTCwgRUlHRU5GVU4sIGZpeGVkX2NvbG9yc2NhbGUgPSBUUlVFKQpgYGAKCgoKQmVsb3cgd2Ugc2hvdyB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgdGhlIGRpZmZlcmVuY2UgJHx1KHMsIDAuNSkgLSBVXlx0YXVfe2gsbX0ocywwLjUpfCQgYW5kIHRoZSBmdW5jdGlvbiB2YWx1ZSAkVV5cdGF1X3toLG19KHMsIDAuNSkkIGF0ICR0PTAuNSQuCgpgYGB7ciwgZmlnLmhlaWdodCA9IDYsIG91dC53aWR0aCA9ICIxMDAlIiwgZmlnLmNhcCA9IGNhcHRpb25lcigiQWJzb2x1dGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBleGFjdCBhbmQgYXBwcm94aW1hdGUgc29sdXRpb24gYXQgJHRfMD0wLjUkLiIpfQppZHggPC0gd2hpY2gubWluKGFicyh0aW1lX3NlcSAtIDAuNSkpCmVycm9yX3NwaGVyZSA8LSBwbG90XzNkX3NwaGVyZV9zdXJmYWNlX29uZWNvbChtZXNoX2NvYXJzZSwgIGFicyhkaWZmWyxpZHhdKSkKc2F2ZShlcnJvcl9zcGhlcmUsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhX2ZpbGVzL2V4cF8xX2Vycm9yX3NwaGVyZS5SRGF0YSIpKQplcnJvcl9zcGhlcmUKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQgPSA2LCBvdXQud2lkdGggPSAiMTAwJSIsIGZpZy5jYXAgPSBjYXB0aW9uZXIoIkFwcHJveGltYXRlIHNvbHV0aW9uIGF0ICR0XzA9MC41JC4iKX0KZl9zcGhlcmUgPC0gcGxvdF8zZF9zcGhlcmVfc3VyZmFjZV9vbmVjb2wobWVzaF9jb2Fyc2UsICBVX2FwcHJveFssaWR4XSkKZl9zcGhlcmUKc2F2ZShmX3NwaGVyZSwgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGFfZmlsZXMvZXhwXzFfZl9zcGhlcmUuUkRhdGEiKSkKYGBgCgoKCgoKCgoKCgo=