Set up environment
library(tidyverse)
library(gridExtra)
library(pander)
library(ggpubr)
theme_set(theme_bw(base_size = 13, base_family = "Times") +
theme(plot.title = element_text(size = 14, hjust = 0.0),
panel.grid.minor = element_blank(),
panel.grid.major = element_line(colour = "gray", linetype="dashed",size = 0.1),
legend.position = "none",
legend.title = element_blank(),
legend.text.align = 0))
## FUNCTION: Get legend
get_legend = function(myggplot){
tmp = ggplot_gtable(ggplot_build(myggplot))
leg = which(sapply(tmp$grobs, function (x) x$name) == "guide-box")
legend = tmp$grobs[[leg]]
return(legend)
}
Data Processing
Use CDO to extract data from “netCDF” to “text” format
Assume that the data including daily observation and model simulation of temperature over period from 2000-2002. These datasets are normally in netCDF format, and can be extracted by using CDO as below:
- cdo -L -outputtab,date,value -fldmean -selvar,tas OBS.nc 2> /dev/null > data-txt/OBS-TAS.txt
- cdo -L -outputtab,date,value -fldmean -selvar,tas Model.nc 2> /dev/null > data-txt/Model-TAS.txt
Read data & re-shape data to work with “tidyverse” environment
df.type = c("OBS", "Model")
# Read data
df.tas = NULL
for (itype in df.type) {
df.tas.tmp = read.table(paste0("data-txt/", itype, "-TAS.txt"), header = FALSE)
colnames(df.tas.tmp) = c("date", "var")
df.tas.tmp$type = itype
df.tas = rbind(df.tas, df.tas.tmp)
}
# Transfrom date to year-mon-day
f.convdate = function(date) {
return(data_frame(year = as.integer(substr(date,1,4)),
mon = as.integer(substr(date,6,7)),
day = as.integer(substr(date,9,10))))
}
df.tas = cbind(type = df.tas$type, f.convdate(df.tas$date), var = df.tas$var)
Quick look at annual cycle (More detailed later)
df.tas %>% group_by(type, mon) %>% summarise(var = mean(var, na.rm = TRUE)) %>%
spread(mon, var) %>% pander(split.table = 130)
| Model |
13.5 |
15.36 |
18.76 |
22.24 |
24.9 |
25.91 |
26.61 |
26.13 |
24.78 |
22.59 |
17.94 |
14.38 |
| OBS |
14.42 |
15.01 |
19.07 |
22.38 |
25.89 |
27.47 |
27.95 |
27.98 |
26.43 |
23.99 |
18.63 |
15.2 |
Daily Analysis
Create basic plot contents (template)
p0 = df.tas %>% spread(type, var) %>%
ggplot(aes(x = OBS, y = Model)) +
scale_x_continuous(breaks = seq(0, 35, 5), limits = c(5, 32.5)) +
scale_y_continuous(breaks = seq(0, 35, 5), limits = c(5, 32.5)) +
coord_fixed(ratio = 1) +
labs(x = "Observation (C-degree)", y = "Model (C-degree)")
Scatter-plot BASIC
Using “alpha”, the density of points can estimately seen
p1 = p0 +
geom_point(alpha = 0.08, color = "red") +
geom_abline(intercept = 0, slope = 1) +
labs(title = "Scatter-plot BASIC") +
stat_cor(aes(label = paste(..r.label.., ..rr.label.., ..p.label.., sep = "~`,`~")),
method = "pearson", label.x = 5, label.y = 31, na.rm = FALSE,
size = 4, color = "blue")
Heatmap of point Density
p2 = p0 +
stat_density_2d(geom = "raster", aes(fill = ..density..), contour = FALSE) +
geom_abline(intercept = 0, slope = 1) +
scale_fill_distiller(palette = "YlOrRd", direction = 1,
limits = c(0, 0.015), oob = scales::squish) +
labs(title = "Heatmap of point Density") +
theme(legend.position = "right")
Heatmap & Hex-Heatmap of point Count over ranges
The ranges are defined by “bins”
p3 = p0 +
geom_bin2d(bins=36, aes(fill=..count..))+
geom_abline(intercept = 0, slope = 1) +
scale_fill_distiller(palette = "YlOrRd", direction = 1,
limits = c(0, 20), oob = scales::squish) +
labs(title = "Heatmap of point Count over ranges") +
stat_cor(aes(label = paste(..r.label.., ..rr.label.., ..p.label.., sep = "~`,`~")),
method = "pearson", label.x = 5, label.y = 31, na.rm = FALSE,
size = 4, color = "blue")
p4 = p0 +
geom_hex(bins=36, aes(fill=..count..))+
geom_abline(intercept = 0, slope = 1) +
scale_fill_distiller(palette = "YlOrRd", direction = 1,
limits = c(0, 20), oob = scales::squish) +
labs(title = "Hex-Heatmap of point Count over ranges") +
theme(legend.position = "right")
2D Kernel density plot of point Density
p5 = p0 +
stat_density_2d(aes(fill = ..level..), geom = "polygon", alpha= 1, col = "black") +
scale_fill_distiller(palette = "Spectral", limits = c(0, 0.015), oob = scales::squish) +
geom_abline(intercept = 0, slope = 1) +
labs(title = "2D Kernel density plot of point Density") +
stat_cor(aes(label = paste(..r.label.., ..rr.label.., ..p.label.., sep = "~`,`~")),
method = "pearson", label.x = 5, label.y = 31, na.rm = FALSE,
size = 4, color = "blue")
p6 = p0 +
stat_density_2d(aes(fill = ..level..), geom = "polygon", alpha = 1, show.legend = F) +
stat_density_2d(aes(size = ..density.., fill = ..density..), geom = "point",
shape = 21, n = 12, contour = FALSE, col = "black") +
scale_fill_distiller(palette = "Spectral", limits = c(0, 0.015), oob = scales::squish) +
geom_abline(intercept = 0, slope = 1) +
labs(title = "Modified 2D Kernel density plot of point Density") +
theme(legend.position = "right")
Put all plots together using grid.arrange (gridExtra)
grid.arrange(p1, p2, p3, p4, p5, p6, ncol = 2, heights = c(3, 3, 3),
layout_matrix = rbind(c(1, 2), c(3, 4), c(5, 6)))

Monthly Analysis
Calculate some climatology indices for each month over period
df.stat = df.tas %>% group_by(type, mon) %>%
summarise(Mean = mean(var, na.rm = TRUE),
Median = quantile(var, 0.50, na.rm = TRUE),
Q25 = quantile(var, 0.25, na.rm = TRUE),
Q75 = quantile(var, 0.75, na.rm = TRUE))
Print out as a table
df.stat %>% ungroup() %>%
gather(key = indices, value = value, -c(type, mon)) %>% spread(key = mon, value = value) %>%
arrange(indices) %>% pander(emphasize.strong.cols = c(1, 2), split.table = 130)
| Model |
Mean |
13.5 |
15.36 |
18.76 |
22.24 |
24.9 |
25.91 |
26.61 |
26.13 |
24.78 |
22.59 |
17.94 |
14.38 |
| OBS |
Mean |
14.42 |
15.01 |
19.07 |
22.38 |
25.89 |
27.47 |
27.95 |
27.98 |
26.43 |
23.99 |
18.63 |
15.2 |
| Model |
Median |
13.38 |
15.45 |
19.16 |
22.55 |
24.82 |
26.15 |
26.44 |
26.1 |
24.98 |
23 |
18.38 |
14.95 |
| OBS |
Median |
14.73 |
14.92 |
19.47 |
22.35 |
25.96 |
27.87 |
27.87 |
28.04 |
26.82 |
24.73 |
18.46 |
14.88 |
| Model |
Q25 |
10.92 |
14.21 |
17.38 |
21.07 |
24.33 |
25.9 |
26.08 |
25.87 |
23.98 |
21.67 |
16.39 |
11.63 |
| OBS |
Q25 |
11.52 |
12.24 |
17.34 |
20.5 |
24.88 |
26.19 |
26.81 |
27.13 |
25.51 |
22.14 |
16.37 |
11.7 |
| Model |
Q75 |
16.41 |
16.91 |
20.43 |
23.6 |
25.85 |
26.53 |
27.12 |
26.33 |
25.72 |
24.04 |
20.32 |
17.82 |
| OBS |
Q75 |
17.39 |
17.93 |
21.13 |
24.93 |
27.14 |
28.62 |
29 |
29 |
27.46 |
25.97 |
20.59 |
18.49 |
Plot the table as heatmap
df.stat.plot = df.stat %>% ungroup() %>%
gather(key = indices, value = value, -c(type, mon)) %>%
mutate(idx = paste0(indices, "-", type))
df.stat.plot$value = round(df.stat.plot$value, digits = 1)
c.month = c("J", "F", "M", "A","M", "J", "J", "A", "S", "O", "N", "D")
p = df.stat.plot %>% ggplot() +
geom_tile(aes(x = factor(mon), y = idx, fill = value), color = "black") +
geom_text(aes(x = factor(mon), y = idx, label = value), color = "black", size = 3) +
geom_hline(yintercept = c(2.5, 4.5, 6.5), size = 1, color = "black") +
scale_fill_distiller(palette = "Spectral", limits = c(10, 30), oob = scales::squish) +
scale_x_discrete(labels = c.month) +
labs(title = "Heatmap for indices comparison",
x = "Month", y = "") +
theme(legend.position = "bottom")
p

Create basic plot contents (template)
# Create function for cleaner code later
f.ggplot = function(p, discrete = FALSE) {
p = p +
scale_color_manual(values = c("red", "blue")) +
scale_fill_manual(values = c("red", "blue")) +
labs(x = "Months", y = "Temperature (C-degree)") +
theme(legend.position = "bottom")
if (discrete == TRUE) {
p = p + scale_x_discrete(breaks = seq(12), labels = c.month)
} else {
p = p + scale_x_continuous(breaks = seq(12), labels = c.month)
}
return(p)
}
Plotting annual cycle
p1 = f.ggplot(df.stat %>% ggplot() +
geom_col(aes(x = mon, y = Mean, fill = factor(type, levels = c("OBS", "Model"))),
alpha = 0.6, position = position_dodge(0.8)) +
geom_errorbar(aes(x = mon , ymin = Q25, ymax = Q75,
group = factor(type, levels = c("OBS", "Model"))),
width = 0.5, position = position_dodge(0.8)) +
labs(title = "Climatology of Temperature over period 2000-2002",
subtitle = "Errorbars present range from Q25 to Q75"))
p2 = f.ggplot(df.stat %>% ggplot() +
geom_line(aes(x = mon, y = Mean, color = factor(type, levels = c("OBS", "Model")))) +
geom_ribbon(aes(x = mon, ymin = Q25, ymax = Q75,
fill = factor(type, levels = c("OBS", "Model"))), alpha = 0.2) +
labs(title = "Climatology of Temperature over period 2000-2002",
subtitle = "The shaded area presents range from Q25 & Q75"))
p3 = f.ggplot(df.tas %>% ggplot() +
stat_summary(aes(x = mon, y = var, color = factor(type, levels = c("OBS", "Model"))),
geom = "pointrange", position = position_dodge(width = 0.5),
fun.y = mean, fun.ymax = max, fun.ymin = min, size = 0.5) +
labs(title = "Climatology of Temperature over period 2000-2002",
subtitle = "The points present for Mean, the lines present range from MIN to MAX"))
p4 = f.ggplot(df.tas %>% ggplot() +
geom_boxplot(aes(x = factor(mon), y = var, fill = factor(type, levels = c("OBS", "Model"))),
alpha = 0.6, outlier.color = "purple", outlier.shape = 4, outlier.size = 1) +
labs(title = "Climatology of Temperature over period 2000-2002",
subtitle = "The boxes present Median, Min, Max, Q25, Q75, and outliers"),
discrete = TRUE)
grid.arrange(p1, p2, p3, p4, ncol = 2, heights = c(3, 3),
layout_matrix = rbind(c(1, 2), c(3, 4)))

PDF
Starting with histogram
m.breaks = seq(4, 32, 0.5)
p1 = df.tas %>% ggplot() +
geom_histogram(aes(x = var, y = ..count.., fill = factor(type, levels = c("OBS", "Model"))),
breaks = m.breaks, position = "identity", alpha = 0.3) +
scale_fill_manual(values = c("red", "blue")) +
scale_y_continuous(breaks = seq(0, 250, 50)) +
labs(title = "Histogram", x = "Temperature", y = "Frequency (count)") +
theme(legend.position = c(0.2, 0.85))
Manually get histogram value and plot as step line
f.hist = function(df, breaks, density = FALSE) {
h = hist(df, breaks = breaks, plot = FALSE)
if (density == FALSE) {
df.h = data_frame(x = h$breaks, y = c(h$count, NA))
} else {
df.h = data_frame(x = h$breaks, y = c(h$density, NA))
}
is.na(df.h$y) = df.h$y == 0.0
return(df.h)
}
df.hist = f.hist(df.tas %>% filter(type == "OBS") %>% .$var, breaks = m.breaks) %>%
mutate(type = "OBS")
df.hist = rbind(df.hist,
f.hist(df.tas %>% filter(type == "Model") %>% .$var, breaks = m.breaks) %>%
mutate(type = "Model"))
p2 = p1 +
geom_step(data = df.hist, aes(x = x, y = y, color = factor(type, levels = c("OBS", "Model"))),
stat = "identity", size = 0.35) +
scale_color_manual(values = c("red", "blue")) +
labs(title = "Histogram with step lines")
Plot histogram with smoothing density
p3 = df.tas %>% ggplot() +
geom_histogram(aes(x = var, y = ..density.., fill = factor(type, levels = c("OBS", "Model"))),
breaks = m.breaks, position = "identity", alpha = 0.3) +
geom_density(aes(x = var , fill = factor(type, levels = c("OBS", "Model"))), alpha = 0.3) +
scale_fill_manual(values = c("red", "blue")) +
scale_y_continuous(breaks = seq(0, 0.3, 0.05)) +
labs(title = "Histogram (Density) with adding smooth", x = "Temperature", y = "Density") +
theme(legend.position = c(0.2, 0.85))
Using combination of Violin-plot and Box-Plot
p4 = df.tas %>% ggplot() +
geom_violin(aes(x = type, y = var, group = type, fill = factor(type, levels = c("OBS", "Model"))),
trim = TRUE, alpha = 0.3) +
geom_boxplot(aes(x = type, y = var, group = type, fill = factor(type, levels = c("OBS", "Model"))),
width=0.1, alpha = 0.6) +
scale_fill_manual(values = c("red", "blue")) +
scale_y_continuous(breaks = seq(10, 30, 10)) +
coord_flip() +
labs(title = "Violin-plot combined with Box-plot", x = "", y = "Temperature") +
theme(legend.position = c(0.2, 0.5))
grid.arrange(p1, p2, p3, p4, ncol = 2, heights = c(3, 3),
layout_matrix = rbind(c(1, 2), c(3, 4)))

Appling “ggridges” density plot for monthly density
library(ggridges)
library(viridis)
p1 = df.tas %>% ggplot() +
geom_density_ridges(aes(x = var, y = factor(mon), fill = factor(mon), col = factor(mon)),
scale = 4, show.legend = FALSE) +
scale_y_discrete(labels = c.month) +
scale_fill_viridis(option = "A",alpha = 0.5, discrete = T)+
scale_color_viridis(option = "C",alpha = 1, discrete = T, begin = 1, end = 0) +
labs(title = "Kernel density plot using 'ggridges'",
x = "Temperature", y = "Months")
p2 = df.tas %>% ggplot() +
geom_density_ridges(aes(x = var, y = factor(mon), fill = factor(mon), col = factor(mon)),
scale = 4, show.legend = FALSE, alpha = 0.5,
stat = "binline", binwidth = 0.5, rel_min_height = 0.005) +
scale_y_discrete(labels = c.month) +
scale_fill_viridis(option = "A",alpha = 0.5, discrete = T)+
scale_color_viridis(option = "C",alpha = 1, discrete = T, begin = 1, end = 0) +
labs(title = "Kernel density plot using 'ggridges'",
x = "Temperature", y = "Months")
grid.arrange(p1, p2, ncol = 2, heights = c(3),
layout_matrix = rbind(c(1, 2)))

CDF
Starting CDF with auto function in “ggplot”
p1 = df.tas %>% ggplot() +
stat_ecdf(aes(x = var, color = type), geom = "step", pad = F) +
scale_color_manual(values = c("red", "blue")) +
labs(title = "CDF using 'ggplot' auto-function", x = "Temperature (C-degree)", y = "CDF") +
theme(legend.position = c(0.2, 0.85))
Manually calculate CDF and plot
This help to extract the correct value
f.getcdf = function (df, step = 1, qth = -999, xth = -999) {
## Recreate ecdf data
df.ecdf = data.frame(x = unique(df),
y = ecdf(df)(unique(df))*length(df))
## rescale y to 0,1 range
df.ecdf$y =
scale(df.ecdf$y,center = min(df.ecdf$y),scale = diff(range(df.ecdf$y)))
df.ecdf = df.ecdf %>% arrange (by = x)
return (df.ecdf[c(seq(1, length(df.ecdf$x), step), length(df.ecdf$x)), ])
## To extract correct value
# if (xth == -999) {
# out = dat_ecdf %>% filter (rank(abs(y - qth)) == min(rank(abs(y - qth))))
# out = round (out, digits = 3)
# } else {
# out = dat_ecdf %>% filter (rank(abs(x - xth)) == min(rank(abs(x - xth))))
# out = round (out, digits = 3)
# }
#
# return (out)
}
##
df.cdf = f.getcdf(df.tas %>% filter(type == "OBS") %>% .$var, step = 20) %>%
mutate(type = "OBS")
df.cdf = rbind(df.cdf,
f.getcdf(df.tas %>% filter(type == "Model") %>% .$var, step = 20) %>%
mutate(type = "Model"))
p2 = df.cdf %>% ggplot() +
geom_step(aes(x = x, y = y, color = type)) +
scale_color_manual(values = c("red", "blue")) +
labs(title = "CDF by manually calculation", x = "Temperature (C-degree)", y = "CDF") +
theme(legend.position = c(0.2, 0.85))
Plot it out
grid.arrange(p1, p2, ncol = 2, heights = c(3),
layout_matrix = rbind(c(1, 2)))

Bonus - Plotly
One reason to use the manual calculation is that the auto function still not working properly with “plotly” for example:
The CDF by “ggplot” auto-function will be broken as:
library(plotly)
ggplotly(p1)
Instead of
library(plotly)
ggplotly(p2)
LS0tCnRpdGxlOiAiVmlzdWFsaXphdGlvbjogVGVtcGVyYXR1cmUgKFNjYXR0ZXItcGxvdCwgSGVhdG1hcCwgQm94LXBsb3QsIFBERiwgQ0RGLCBldGMuKSIKYXV0aG9yOiAiYnkgTlhUVE5YIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQogICAgdGhlbWU6ICJkZWZhdWx0IgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIGRldjogJ3N2ZycgIAotLS0KCjxzdHlsZT4KLyogcmVzaXplIHRoZSB3aWRnZXQgY29udGFpbmVyICovCi5wbG90bHkgeyAKICB3aWR0aDogNjAlICFpbXBvcnRhbnQ7CiAgbWFyZ2luOiBhdXRvOwp9CgovKiBjZW50ZXIgdGhlIHdpZGdldCAqLwpkaXYuc3ZnLWNvbnRhaW5lciB7CiAgbWFyZ2luOiBhdXRvICFpbXBvcnRhbnQ7Cn0KPC9zdHlsZT4KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBmaWcuYWxpZ24gPSAiY2VudGVyIikKYGBgCgpcCioqU2V0IHVwIGVudmlyb25tZW50KioKCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkocGFuZGVyKQpsaWJyYXJ5KGdncHVicikKCnRoZW1lX3NldCh0aGVtZV9idyhiYXNlX3NpemUgPSAxMywgYmFzZV9mYW1pbHkgPSAiVGltZXMiKSArCiAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuMCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyYXkiLCBsaW5ldHlwZT0iZGFzaGVkIixzaXplID0gMC4xKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCAKICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dC5hbGlnbiA9IDApKQoKIyMgRlVOQ1RJT046IEdldCBsZWdlbmQKZ2V0X2xlZ2VuZCA9IGZ1bmN0aW9uKG15Z2dwbG90KXsKICB0bXAgPSBnZ3Bsb3RfZ3RhYmxlKGdncGxvdF9idWlsZChteWdncGxvdCkpCiAgbGVnID0gd2hpY2goc2FwcGx5KHRtcCRncm9icywgZnVuY3Rpb24gKHgpIHgkbmFtZSkgPT0gImd1aWRlLWJveCIpCiAgbGVnZW5kID0gdG1wJGdyb2JzW1tsZWddXQogIHJldHVybihsZWdlbmQpCn0KCmBgYAoKCiMgRGF0YSBQcm9jZXNzaW5nCgpcCioqVXNlIENETyB0byBleHRyYWN0IGRhdGEgZnJvbSAibmV0Q0RGIiB0byAidGV4dCIgZm9ybWF0KioKCkFzc3VtZSB0aGF0IHRoZSBkYXRhIGluY2x1ZGluZyBkYWlseSBvYnNlcnZhdGlvbiBhbmQgbW9kZWwgc2ltdWxhdGlvbiBvZiB0ZW1wZXJhdHVyZSBvdmVyIHBlcmlvZCBmcm9tIAoyMDAwLTIwMDIuIFRoZXNlIGRhdGFzZXRzIGFyZSBub3JtYWxseSBpbiBuZXRDREYgZm9ybWF0LCBhbmQgY2FuIGJlIGV4dHJhY3RlZCBieSB1c2luZyBDRE8gYXMgYmVsb3c6CgoqIGNkbyAtTCAtb3V0cHV0dGFiLGRhdGUsdmFsdWUgLWZsZG1lYW4gLXNlbHZhcix0YXMgT0JTLm5jIDI+IC9kZXYvbnVsbCA+IGRhdGEtdHh0L09CUy1UQVMudHh0CiogY2RvIC1MIC1vdXRwdXR0YWIsZGF0ZSx2YWx1ZSAtZmxkbWVhbiAtc2VsdmFyLHRhcyBNb2RlbC5uYyAyPiAvZGV2L251bGwgPiBkYXRhLXR4dC9Nb2RlbC1UQVMudHh0CgpcCioqUmVhZCBkYXRhICYgcmUtc2hhcGUgZGF0YSB0byB3b3JrIHdpdGggInRpZHl2ZXJzZSIgZW52aXJvbm1lbnQqKgoKCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpkZi50eXBlID0gYygiT0JTIiwgIk1vZGVsIikKCiMgUmVhZCBkYXRhCmRmLnRhcyA9IE5VTEwKZm9yIChpdHlwZSBpbiBkZi50eXBlKSB7CiAgZGYudGFzLnRtcCA9IHJlYWQudGFibGUocGFzdGUwKCJkYXRhLXR4dC8iLCBpdHlwZSwgIi1UQVMudHh0IiksIGhlYWRlciA9IEZBTFNFKQogIAogIGNvbG5hbWVzKGRmLnRhcy50bXApID0gYygiZGF0ZSIsICJ2YXIiKQogIGRmLnRhcy50bXAkdHlwZSA9IGl0eXBlCiAgCiAgZGYudGFzID0gcmJpbmQoZGYudGFzLCBkZi50YXMudG1wKQp9CgojIFRyYW5zZnJvbSBkYXRlIHRvIHllYXItbW9uLWRheQpmLmNvbnZkYXRlID0gZnVuY3Rpb24oZGF0ZSkgewogIHJldHVybihkYXRhX2ZyYW1lKHllYXIgPSBhcy5pbnRlZ2VyKHN1YnN0cihkYXRlLDEsNCkpLAogICAgICAgICAgICAgICAgICAgIG1vbiAgPSBhcy5pbnRlZ2VyKHN1YnN0cihkYXRlLDYsNykpLAogICAgICAgICAgICAgICAgICAgIGRheSAgPSBhcy5pbnRlZ2VyKHN1YnN0cihkYXRlLDksMTApKSkpCn0KCmRmLnRhcyA9IGNiaW5kKHR5cGUgPSBkZi50YXMkdHlwZSwgZi5jb252ZGF0ZShkZi50YXMkZGF0ZSksIHZhciA9IGRmLnRhcyR2YXIpCmBgYAoKXAoqKlF1aWNrIGxvb2sgYXQgYW5udWFsIGN5Y2xlIChNb3JlIGRldGFpbGVkIGxhdGVyKSoqCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KZGYudGFzICU+JSBncm91cF9ieSh0eXBlLCBtb24pICU+JSBzdW1tYXJpc2UodmFyID0gbWVhbih2YXIsIG5hLnJtID0gVFJVRSkpICU+JSAKICBzcHJlYWQobW9uLCB2YXIpICU+JSBwYW5kZXIoc3BsaXQudGFibGUgPSAxMzApCmBgYAoKCiMgRGFpbHkgQW5hbHlzaXMKClwKKipDcmVhdGUgYmFzaWMgcGxvdCBjb250ZW50cyAodGVtcGxhdGUpKioKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CnAwID0gZGYudGFzICU+JSBzcHJlYWQodHlwZSwgdmFyKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBPQlMsIHkgPSBNb2RlbCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDM1LCA1KSwgbGltaXRzID0gYyg1LCAzMi41KSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMzUsIDUpLCBsaW1pdHMgPSBjKDUsIDMyLjUpKSArCiAgY29vcmRfZml4ZWQocmF0aW8gPSAxKSArCiAgbGFicyh4ID0gIk9ic2VydmF0aW9uIChDLWRlZ3JlZSkiLCB5ID0gIk1vZGVsIChDLWRlZ3JlZSkiKQpgYGAKClwKKipTY2F0dGVyLXBsb3QgQkFTSUMqKgoKVXNpbmcgImFscGhhIiwgdGhlIGRlbnNpdHkgb2YgcG9pbnRzIGNhbiBlc3RpbWF0ZWx5IHNlZW4KYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CnAxID0gcDAgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjA4LCBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJTY2F0dGVyLXBsb3QgQkFTSUMiKSArCiAgc3RhdF9jb3IoYWVzKGxhYmVsID0gcGFzdGUoLi5yLmxhYmVsLi4sIC4ucnIubGFiZWwuLiwgLi5wLmxhYmVsLi4sIHNlcCA9ICJ+YCxgfiIpKSwKICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSA1LCBsYWJlbC55ID0gMzEsIG5hLnJtID0gRkFMU0UsIAogICAgICAgICAgIHNpemUgPSA0LCBjb2xvciA9ICJibHVlIikKYGBgCgpcCioqSGVhdG1hcCBvZiBwb2ludCBEZW5zaXR5KioKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CnAyID0gcDAgKwogIHN0YXRfZGVuc2l0eV8yZChnZW9tID0gInJhc3RlciIsIGFlcyhmaWxsID0gLi5kZW5zaXR5Li4pLCBjb250b3VyID0gRkFMU0UpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEpICsKICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIllsT3JSZCIsIGRpcmVjdGlvbiA9IDEsIAogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgMC4wMTUpLCBvb2IgPSBzY2FsZXM6OnNxdWlzaCkgKwogIGxhYnModGl0bGUgPSAiSGVhdG1hcCBvZiBwb2ludCBEZW5zaXR5IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKXAoqKkhlYXRtYXAgJiBIZXgtSGVhdG1hcCBvZiBwb2ludCBDb3VudCBvdmVyIHJhbmdlcyoqCgpUaGUgcmFuZ2VzIGFyZSBkZWZpbmVkIGJ5ICJiaW5zIgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CnAzID0gcDAgKwogIGdlb21fYmluMmQoYmlucz0zNiwgYWVzKGZpbGw9Li5jb3VudC4uKSkrCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJZbE9yUmQiLCBkaXJlY3Rpb24gPSAxLCAKICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDIwKSwgb29iID0gc2NhbGVzOjpzcXVpc2gpICsKICBsYWJzKHRpdGxlID0gIkhlYXRtYXAgb2YgcG9pbnQgQ291bnQgb3ZlciByYW5nZXMiKSArCiAgc3RhdF9jb3IoYWVzKGxhYmVsID0gcGFzdGUoLi5yLmxhYmVsLi4sIC4ucnIubGFiZWwuLiwgLi5wLmxhYmVsLi4sIHNlcCA9ICJ+YCxgfiIpKSwKICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSA1LCBsYWJlbC55ID0gMzEsIG5hLnJtID0gRkFMU0UsIAogICAgICAgICAgIHNpemUgPSA0LCBjb2xvciA9ICJibHVlIikKCnA0ID0gcDAgKwogIGdlb21faGV4KGJpbnM9MzYsIGFlcyhmaWxsPS4uY291bnQuLikpKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGUgPSAiWWxPclJkIiwgZGlyZWN0aW9uID0gMSwgCiAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCAyMCksIG9vYiA9IHNjYWxlczo6c3F1aXNoKSArCiAgbGFicyh0aXRsZSA9ICJIZXgtSGVhdG1hcCBvZiBwb2ludCBDb3VudCBvdmVyIHJhbmdlcyIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKClwKKioyRCBLZXJuZWwgZGVuc2l0eSBwbG90IG9mIHBvaW50IERlbnNpdHkqKgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KcDUgPSBwMCArCiAgc3RhdF9kZW5zaXR5XzJkKGFlcyhmaWxsID0gLi5sZXZlbC4uKSwgZ2VvbSA9ICJwb2x5Z29uIiwgYWxwaGE9IDEsIGNvbCA9ICJibGFjayIpICsKICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlNwZWN0cmFsIiwgbGltaXRzID0gYygwLCAwLjAxNSksIG9vYiA9IHNjYWxlczo6c3F1aXNoKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICIyRCBLZXJuZWwgZGVuc2l0eSBwbG90IG9mIHBvaW50IERlbnNpdHkiKSArCiAgc3RhdF9jb3IoYWVzKGxhYmVsID0gcGFzdGUoLi5yLmxhYmVsLi4sIC4ucnIubGFiZWwuLiwgLi5wLmxhYmVsLi4sIHNlcCA9ICJ+YCxgfiIpKSwKICAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIsIGxhYmVsLnggPSA1LCBsYWJlbC55ID0gMzEsIG5hLnJtID0gRkFMU0UsIAogICAgICAgICAgIHNpemUgPSA0LCBjb2xvciA9ICJibHVlIikKCnA2ID0gcDAgKwogIHN0YXRfZGVuc2l0eV8yZChhZXMoZmlsbCA9IC4ubGV2ZWwuLiksIGdlb20gPSAicG9seWdvbiIsIGFscGhhID0gMSwgc2hvdy5sZWdlbmQgPSBGKSArCiAgc3RhdF9kZW5zaXR5XzJkKGFlcyhzaXplID0gLi5kZW5zaXR5Li4sIGZpbGwgPSAuLmRlbnNpdHkuLiksIGdlb20gPSAicG9pbnQiLCAKICAgICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwgbiA9IDEyLCBjb250b3VyID0gRkFMU0UsIGNvbCA9ICJibGFjayIpICsKICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlNwZWN0cmFsIiwgbGltaXRzID0gYygwLCAwLjAxNSksIG9vYiA9IHNjYWxlczo6c3F1aXNoKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJNb2RpZmllZCAyRCBLZXJuZWwgZGVuc2l0eSBwbG90IG9mIHBvaW50IERlbnNpdHkiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgpcCioqUHV0IGFsbCBwbG90cyB0b2dldGhlciB1c2luZyBncmlkLmFycmFuZ2UgKGdyaWRFeHRyYSkqKgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMn0KZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBwNSwgcDYsIG5jb2wgPSAyLCBoZWlnaHRzID0gYygzLCAzLCAzKSwKICAgICAgICAgICAgIGxheW91dF9tYXRyaXggPSByYmluZChjKDEsIDIpLCBjKDMsIDQpLCBjKDUsIDYpKSkKYGBgCgoKIyBNb250aGx5IEFuYWx5c2lzCgpcCioqQ2FsY3VsYXRlIHNvbWUgY2xpbWF0b2xvZ3kgaW5kaWNlcyBmb3IgZWFjaCBtb250aCBvdmVyIHBlcmlvZCoqCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpkZi5zdGF0ID0gZGYudGFzICU+JSBncm91cF9ieSh0eXBlLCBtb24pICU+JSAKICBzdW1tYXJpc2UoTWVhbiAgID0gbWVhbih2YXIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIE1lZGlhbiA9IHF1YW50aWxlKHZhciwgMC41MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgUTI1ICAgID0gcXVhbnRpbGUodmFyLCAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBRNzUgICAgPSBxdWFudGlsZSh2YXIsIDAuNzUsIG5hLnJtID0gVFJVRSkpCmBgYAoKXAoqKlByaW50IG91dCBhcyBhIHRhYmxlKioKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmRmLnN0YXQgJT4lIHVuZ3JvdXAoKSAlPiUgCiAgZ2F0aGVyKGtleSA9IGluZGljZXMsIHZhbHVlID0gdmFsdWUsIC1jKHR5cGUsIG1vbikpICU+JSBzcHJlYWQoa2V5ID0gbW9uLCB2YWx1ZSA9IHZhbHVlKSAlPiUKICBhcnJhbmdlKGluZGljZXMpICU+JSBwYW5kZXIoZW1waGFzaXplLnN0cm9uZy5jb2xzID0gYygxLCAyKSwgc3BsaXQudGFibGUgPSAxMzApCmBgYAoKXAoqKlBsb3QgdGhlIHRhYmxlIGFzIGhlYXRtYXAqKgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KZGYuc3RhdC5wbG90ID0gZGYuc3RhdCAlPiUgdW5ncm91cCgpICU+JSAKICBnYXRoZXIoa2V5ID0gaW5kaWNlcywgdmFsdWUgPSB2YWx1ZSwgLWModHlwZSwgbW9uKSkgJT4lIAogIG11dGF0ZShpZHggPSBwYXN0ZTAoaW5kaWNlcywgIi0iLCB0eXBlKSkKZGYuc3RhdC5wbG90JHZhbHVlID0gcm91bmQoZGYuc3RhdC5wbG90JHZhbHVlLCBkaWdpdHMgPSAxKQoKYy5tb250aCA9IGMoIkoiLCAiRiIsICJNIiwgIkEiLCJNIiwgIkoiLCAiSiIsICJBIiwgIlMiLCAiTyIsICJOIiwgIkQiKQoKcCA9IGRmLnN0YXQucGxvdCAlPiUgZ2dwbG90KCkgKwogIGdlb21fdGlsZShhZXMoeCA9IGZhY3Rvcihtb24pLCB5ID0gaWR4LCBmaWxsID0gdmFsdWUpLCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoYWVzKHggPSBmYWN0b3IobW9uKSwgeSA9IGlkeCwgbGFiZWwgPSB2YWx1ZSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKDIuNSwgNC41LCA2LjUpLCBzaXplID0gMSwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIsIGxpbWl0cyA9IGMoMTAsIDMwKSwgb29iID0gc2NhbGVzOjpzcXVpc2gpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMubW9udGgpICsKICBsYWJzKHRpdGxlID0gIkhlYXRtYXAgZm9yIGluZGljZXMgY29tcGFyaXNvbiIsCiAgICAgICB4ID0gIk1vbnRoIiwgeSA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCnAKYGBgCgpcCioqQ3JlYXRlIGJhc2ljIHBsb3QgY29udGVudHMgKHRlbXBsYXRlKSoqCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQoKIyBDcmVhdGUgZnVuY3Rpb24gZm9yIGNsZWFuZXIgY29kZSBsYXRlcgpmLmdncGxvdCA9IGZ1bmN0aW9uKHAsIGRpc2NyZXRlID0gRkFMU0UpIHsKICBwID0gcCArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiKSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiKSkgKwogICAgbGFicyh4ID0gIk1vbnRocyIsIHkgPSAiVGVtcGVyYXR1cmUgKEMtZGVncmVlKSIpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQogIGlmIChkaXNjcmV0ZSA9PSBUUlVFKSB7CiAgICBwID0gcCArIHNjYWxlX3hfZGlzY3JldGUoYnJlYWtzID0gc2VxKDEyKSwgbGFiZWxzID0gYy5tb250aCkKICB9IGVsc2UgewogICAgcCA9IHAgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEyKSwgbGFiZWxzID0gYy5tb250aCkKICB9CiAgcmV0dXJuKHApCn0KCmBgYAoKXAoqKlBsb3R0aW5nIGFubnVhbCBjeWNsZSoqCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpwMSA9IGYuZ2dwbG90KGRmLnN0YXQgJT4lIGdncGxvdCgpICsKICBnZW9tX2NvbChhZXMoeCA9IG1vbiwgeSA9IE1lYW4sIGZpbGwgPSBmYWN0b3IodHlwZSwgbGV2ZWxzID0gYygiT0JTIiwgIk1vZGVsIikpKSwKICAgICAgICAgICBhbHBoYSA9IDAuNiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjgpKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeCA9IG1vbiAsIHltaW4gPSBRMjUsIHltYXggPSBRNzUsIAogICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZmFjdG9yKHR5cGUsIGxldmVscyA9IGMoIk9CUyIsICJNb2RlbCIpKSksCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjgpKSArCiAgbGFicyh0aXRsZSA9ICJDbGltYXRvbG9neSBvZiBUZW1wZXJhdHVyZSBvdmVyIHBlcmlvZCAyMDAwLTIwMDIiLAogICAgICAgc3VidGl0bGUgPSAiRXJyb3JiYXJzIHByZXNlbnQgcmFuZ2UgZnJvbSBRMjUgdG8gUTc1IikpCgpwMiA9IGYuZ2dwbG90KGRmLnN0YXQgJT4lIGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSBtb24sIHkgPSBNZWFuLCBjb2xvciA9IGZhY3Rvcih0eXBlLCBsZXZlbHMgPSBjKCJPQlMiLCAiTW9kZWwiKSkpKSArCiAgZ2VvbV9yaWJib24oYWVzKHggPSBtb24sIHltaW4gPSBRMjUsIHltYXggPSBRNzUsIAogICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKHR5cGUsIGxldmVscyA9IGMoIk9CUyIsICJNb2RlbCIpKSksIGFscGhhID0gMC4yKSArCiAgbGFicyh0aXRsZSA9ICJDbGltYXRvbG9neSBvZiBUZW1wZXJhdHVyZSBvdmVyIHBlcmlvZCAyMDAwLTIwMDIiLCAKICAgICAgIHN1YnRpdGxlID0gIlRoZSBzaGFkZWQgYXJlYSBwcmVzZW50cyByYW5nZSBmcm9tIFEyNSAmIFE3NSIpKQogIApwMyA9IGYuZ2dwbG90KGRmLnRhcyAlPiUgZ2dwbG90KCkgKwogIHN0YXRfc3VtbWFyeShhZXMoeCA9IG1vbiwgeSA9IHZhciwgY29sb3IgPSBmYWN0b3IodHlwZSwgbGV2ZWxzID0gYygiT0JTIiwgIk1vZGVsIikpKSwKICAgICAgICAgICAgICAgZ2VvbSA9ICJwb2ludHJhbmdlIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSksCiAgICAgICAgICAgICAgIGZ1bi55ID0gbWVhbiwgZnVuLnltYXggPSBtYXgsIGZ1bi55bWluID0gbWluLCBzaXplID0gMC41KSArCiAgbGFicyh0aXRsZSA9ICJDbGltYXRvbG9neSBvZiBUZW1wZXJhdHVyZSBvdmVyIHBlcmlvZCAyMDAwLTIwMDIiLCAKICAgICAgIHN1YnRpdGxlID0gIlRoZSBwb2ludHMgcHJlc2VudCBmb3IgTWVhbiwgdGhlIGxpbmVzIHByZXNlbnQgcmFuZ2UgZnJvbSBNSU4gdG8gTUFYIikpCgpwNCA9IGYuZ2dwbG90KGRmLnRhcyAlPiUgZ2dwbG90KCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IGZhY3Rvcihtb24pLCB5ID0gdmFyLCBmaWxsID0gZmFjdG9yKHR5cGUsIGxldmVscyA9IGMoIk9CUyIsICJNb2RlbCIpKSksCiAgICAgICAgICAgICAgIGFscGhhID0gMC42LCBvdXRsaWVyLmNvbG9yID0gInB1cnBsZSIsIG91dGxpZXIuc2hhcGUgPSA0LCBvdXRsaWVyLnNpemUgPSAxKSArCiAgbGFicyh0aXRsZSA9ICJDbGltYXRvbG9neSBvZiBUZW1wZXJhdHVyZSBvdmVyIHBlcmlvZCAyMDAwLTIwMDIiLCAKICAgICAgIHN1YnRpdGxlID0gIlRoZSBib3hlcyBwcmVzZW50IE1lZGlhbiwgTWluLCBNYXgsIFEyNSwgUTc1LCBhbmQgb3V0bGllcnMiKSwKICBkaXNjcmV0ZSA9IFRSVUUpCmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDExLCBmaWcuaGVpZ2h0ID0gOH0KZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIHA0LCBuY29sID0gMiwgaGVpZ2h0cyA9IGMoMywgMyksCiAgICAgICAgICAgICBsYXlvdXRfbWF0cml4ID0gcmJpbmQoYygxLCAyKSwgYygzLCA0KSkpCmBgYAoKCiMgUERGCgpcCioqU3RhcnRpbmcgd2l0aCBoaXN0b2dyYW0qKgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbS5icmVha3MgPSBzZXEoNCwgMzIsIDAuNSkKCnAxID0gZGYudGFzICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSB2YXIsIHkgPSAuLmNvdW50Li4sIGZpbGwgPSBmYWN0b3IodHlwZSwgbGV2ZWxzID0gYygiT0JTIiwgIk1vZGVsIikpKSwgCiAgICAgICAgICAgICAgICAgYnJlYWtzID0gbS5icmVha3MsIHBvc2l0aW9uID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjMpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCAiYmx1ZSIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyNTAsIDUwKSkgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIiwgeCA9ICJUZW1wZXJhdHVyZSIsIHkgPSAiRnJlcXVlbmN5IChjb3VudCkiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsIDAuODUpKQpgYGAKClwKKipNYW51YWxseSBnZXQgaGlzdG9ncmFtIHZhbHVlIGFuZCBwbG90IGFzIHN0ZXAgbGluZSoqCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpmLmhpc3QgPSBmdW5jdGlvbihkZiwgYnJlYWtzLCBkZW5zaXR5ID0gRkFMU0UpIHsKICBoID0gaGlzdChkZiwgYnJlYWtzID0gYnJlYWtzLCBwbG90ID0gRkFMU0UpCiAgaWYgKGRlbnNpdHkgPT0gRkFMU0UpIHsKICAgIGRmLmggPSBkYXRhX2ZyYW1lKHggPSBoJGJyZWFrcywgeSA9IGMoaCRjb3VudCwgTkEpKQogIH0gZWxzZSB7CiAgICBkZi5oID0gZGF0YV9mcmFtZSh4ID0gaCRicmVha3MsIHkgPSBjKGgkZGVuc2l0eSwgTkEpKQogIH0KICAKICBpcy5uYShkZi5oJHkpID0gZGYuaCR5ID09IDAuMAogIAogIHJldHVybihkZi5oKQp9CgpkZi5oaXN0ID0gZi5oaXN0KGRmLnRhcyAlPiUgZmlsdGVyKHR5cGUgPT0gIk9CUyIpICU+JSAuJHZhciwgYnJlYWtzID0gbS5icmVha3MpICU+JQogIG11dGF0ZSh0eXBlID0gIk9CUyIpCmRmLmhpc3QgPSByYmluZChkZi5oaXN0LAogICAgICAgICAgICAgICAgZi5oaXN0KGRmLnRhcyAlPiUgZmlsdGVyKHR5cGUgPT0gIk1vZGVsIikgJT4lIC4kdmFyLCBicmVha3MgPSBtLmJyZWFrcykgJT4lCiAgICAgICAgICAgICAgICAgIG11dGF0ZSh0eXBlID0gIk1vZGVsIikpCgpwMiA9IHAxICsgCiAgZ2VvbV9zdGVwKGRhdGEgPSBkZi5oaXN0LCBhZXMoeCA9IHgsIHkgPSB5LCBjb2xvciA9IGZhY3Rvcih0eXBlLCBsZXZlbHMgPSBjKCJPQlMiLCAiTW9kZWwiKSkpLAogICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5Iiwgc2l6ZSA9IDAuMzUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiKSkgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIHdpdGggc3RlcCBsaW5lcyIpCmBgYAoKXAoqKlBsb3QgaGlzdG9ncmFtIHdpdGggc21vb3RoaW5nIGRlbnNpdHkqKgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KcDMgPSBkZi50YXMgJT4lIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHZhciwgeSA9IC4uZGVuc2l0eS4uLCBmaWxsID0gZmFjdG9yKHR5cGUsIGxldmVscyA9IGMoIk9CUyIsICJNb2RlbCIpKSksIAogICAgICAgICAgICAgICAgIGJyZWFrcyA9IG0uYnJlYWtzLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIGFscGhhID0gMC4zKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gdmFyICwgZmlsbCA9IGZhY3Rvcih0eXBlLCBsZXZlbHMgPSBjKCJPQlMiLCAiTW9kZWwiKSkpLCBhbHBoYSA9IDAuMykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJibHVlIikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDAuMywgMC4wNSkpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSAoRGVuc2l0eSkgd2l0aCBhZGRpbmcgc21vb3RoIiwgeCA9ICJUZW1wZXJhdHVyZSIsIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwgMC44NSkpCmBgYApcCioqVXNpbmcgY29tYmluYXRpb24gb2YgVmlvbGluLXBsb3QgYW5kIEJveC1QbG90KioKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CnA0ID0gZGYudGFzICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV92aW9saW4oYWVzKHggPSB0eXBlLCB5ID0gdmFyLCBncm91cCA9IHR5cGUsIGZpbGwgPSBmYWN0b3IodHlwZSwgbGV2ZWxzID0gYygiT0JTIiwgIk1vZGVsIikpKSwgCiAgICAgICAgICAgICAgdHJpbSA9IFRSVUUsIGFscGhhID0gMC4zKSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gdHlwZSwgeSA9IHZhciwgZ3JvdXAgPSB0eXBlLCBmaWxsID0gZmFjdG9yKHR5cGUsIGxldmVscyA9IGMoIk9CUyIsICJNb2RlbCIpKSksIAogICAgICAgICAgICAgICB3aWR0aD0wLjEsIGFscGhhID0gMC42KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEwLCAzMCwgMTApKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHRpdGxlID0gIlZpb2xpbi1wbG90IGNvbWJpbmVkIHdpdGggQm94LXBsb3QiLCB4ID0gIiIsIHkgPSAiVGVtcGVyYXR1cmUiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsIDAuNSkpCmBgYAoKXApgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA3fQpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgcDQsIG5jb2wgPSAyLCBoZWlnaHRzID0gYygzLCAzKSwKICAgICAgICAgICAgIGxheW91dF9tYXRyaXggPSByYmluZChjKDEsIDIpLCBjKDMsIDQpKSkKYGBgCgpcCioqQXBwbGluZyAiZ2dyaWRnZXMiIGRlbnNpdHkgcGxvdCBmb3IgbW9udGhseSBkZW5zaXR5KioKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeSh2aXJpZGlzKQoKcDEgPSBkZi50YXMgJT4lIGdncGxvdCgpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyh4ID0gdmFyLCB5ID0gZmFjdG9yKG1vbiksIGZpbGwgPSBmYWN0b3IobW9uKSwgY29sID0gZmFjdG9yKG1vbikpLCAKICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gYy5tb250aCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAiQSIsYWxwaGEgPSAwLjUsIGRpc2NyZXRlID0gVCkrCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAiQyIsYWxwaGEgPSAxLCBkaXNjcmV0ZSA9IFQsIGJlZ2luID0gMSwgZW5kID0gMCkgKwogIGxhYnModGl0bGUgPSAiS2VybmVsIGRlbnNpdHkgcGxvdCB1c2luZyAnZ2dyaWRnZXMnIiwgCiAgICAgICB4ID0gIlRlbXBlcmF0dXJlIiwgeSA9ICJNb250aHMiKQoKcDIgPSBkZi50YXMgJT4lIGdncGxvdCgpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyh4ID0gdmFyLCB5ID0gZmFjdG9yKG1vbiksIGZpbGwgPSBmYWN0b3IobW9uKSwgY29sID0gZmFjdG9yKG1vbikpLCAKICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgYWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICBzdGF0ID0gImJpbmxpbmUiLCBiaW53aWR0aCA9IDAuNSwgcmVsX21pbl9oZWlnaHQgPSAwLjAwNSkgKwogIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gYy5tb250aCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAiQSIsYWxwaGEgPSAwLjUsIGRpc2NyZXRlID0gVCkrCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAiQyIsYWxwaGEgPSAxLCBkaXNjcmV0ZSA9IFQsIGJlZ2luID0gMSwgZW5kID0gMCkgKwogIGxhYnModGl0bGUgPSAiS2VybmVsIGRlbnNpdHkgcGxvdCB1c2luZyAnZ2dyaWRnZXMnIiwgCiAgICAgICB4ID0gIlRlbXBlcmF0dXJlIiwgeSA9ICJNb250aHMiKQoKCmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAyLCBoZWlnaHRzID0gYygzKSwKICAgICAgICAgICAgIGxheW91dF9tYXRyaXggPSByYmluZChjKDEsIDIpKSkKICAKYGBgCgoKIyBDREYKClwKKipTdGFydGluZyBDREYgd2l0aCBhdXRvIGZ1bmN0aW9uIGluICJnZ3Bsb3QiKioKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CnAxID0gZGYudGFzICU+JSBnZ3Bsb3QoKSArCiAgc3RhdF9lY2RmKGFlcyh4ID0gdmFyLCBjb2xvciA9IHR5cGUpLCBnZW9tID0gInN0ZXAiLCBwYWQgPSBGKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJibHVlIikpICsKICBsYWJzKHRpdGxlID0gIkNERiB1c2luZyAnZ2dwbG90JyBhdXRvLWZ1bmN0aW9uIiwgeCA9ICJUZW1wZXJhdHVyZSAoQy1kZWdyZWUpIiwgeSA9ICJDREYiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsIDAuODUpKQoKYGBgCgpcCioqTWFudWFsbHkgY2FsY3VsYXRlIENERiBhbmQgcGxvdCoqCgpUaGlzIGhlbHAgdG8gZXh0cmFjdCB0aGUgY29ycmVjdCB2YWx1ZQoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmYuZ2V0Y2RmID0gZnVuY3Rpb24gKGRmLCBzdGVwID0gMSwgcXRoID0gLTk5OSwgeHRoID0gLTk5OSkgewogICMjIFJlY3JlYXRlIGVjZGYgZGF0YQogIGRmLmVjZGYgPSBkYXRhLmZyYW1lKHggPSB1bmlxdWUoZGYpLAogICAgICAgICAgICAgICAgICAgICAgICB5ID0gZWNkZihkZikodW5pcXVlKGRmKSkqbGVuZ3RoKGRmKSkKICAjIyByZXNjYWxlIHkgdG8gMCwxIHJhbmdlCiAgZGYuZWNkZiR5ID0gCiAgICBzY2FsZShkZi5lY2RmJHksY2VudGVyID0gbWluKGRmLmVjZGYkeSksc2NhbGUgPSBkaWZmKHJhbmdlKGRmLmVjZGYkeSkpKQogIAogIGRmLmVjZGYgPSBkZi5lY2RmICU+JSBhcnJhbmdlIChieSA9IHgpCiAgcmV0dXJuIChkZi5lY2RmW2Moc2VxKDEsIGxlbmd0aChkZi5lY2RmJHgpLCBzdGVwKSwgbGVuZ3RoKGRmLmVjZGYkeCkpLCBdKQogIAogICMjIFRvIGV4dHJhY3QgY29ycmVjdCB2YWx1ZQogICMgaWYgKHh0aCA9PSAtOTk5KSB7CiAgIyAgIG91dCA9IGRhdF9lY2RmICU+JSBmaWx0ZXIgKHJhbmsoYWJzKHkgLSBxdGgpKSA9PSBtaW4ocmFuayhhYnMoeSAtIHF0aCkpKSkKICAjICAgb3V0ID0gcm91bmQgKG91dCwgZGlnaXRzID0gMykKICAjIH0gZWxzZSB7CiAgIyAgIG91dCA9IGRhdF9lY2RmICU+JSBmaWx0ZXIgKHJhbmsoYWJzKHggLSB4dGgpKSA9PSBtaW4ocmFuayhhYnMoeCAtIHh0aCkpKSkKICAjICAgb3V0ID0gcm91bmQgKG91dCwgZGlnaXRzID0gMykKICAjIH0KICAjIAogICMgcmV0dXJuIChvdXQpCn0KCiMjCmRmLmNkZiA9IGYuZ2V0Y2RmKGRmLnRhcyAlPiUgZmlsdGVyKHR5cGUgPT0gIk9CUyIpICU+JSAuJHZhciwgc3RlcCA9IDIwKSAlPiUgCiAgbXV0YXRlKHR5cGUgPSAiT0JTIikKZGYuY2RmID0gcmJpbmQoZGYuY2RmLAogICAgICAgICAgICAgICBmLmdldGNkZihkZi50YXMgJT4lIGZpbHRlcih0eXBlID09ICJNb2RlbCIpICU+JSAuJHZhciwgc3RlcCA9IDIwKSAlPiUgCiAgICAgICAgICAgICAgICAgbXV0YXRlKHR5cGUgPSAiTW9kZWwiKSkKCnAyID0gZGYuY2RmICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9zdGVwKGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gdHlwZSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiKSkgKwogIGxhYnModGl0bGUgPSAiQ0RGIGJ5IG1hbnVhbGx5IGNhbGN1bGF0aW9uIiwgeCA9ICJUZW1wZXJhdHVyZSAoQy1kZWdyZWUpIiwgeSA9ICJDREYiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjIsIDAuODUpKQpgYGAKClwKKipQbG90IGl0IG91dCoqCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNH0KZ3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbCA9IDIsIGhlaWdodHMgPSBjKDMpLAogICAgICAgICAgICAgbGF5b3V0X21hdHJpeCA9IHJiaW5kKGMoMSwgMikpKQpgYGAKCiMgQm9udXMgLSBQbG90bHkKCk9uZSByZWFzb24gdG8gdXNlIHRoZSBtYW51YWwgY2FsY3VsYXRpb24gaXMgdGhhdCB0aGUgYXV0byBmdW5jdGlvbiBzdGlsbCBub3QKd29ya2luZyBwcm9wZXJseSB3aXRoICJwbG90bHkiIGZvciBleGFtcGxlOgoKKipUaGUgQ0RGIGJ5ICJnZ3Bsb3QiIGF1dG8tZnVuY3Rpb24gd2lsbCBiZSBicm9rZW4gYXM6KioKCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNH0KbGlicmFyeShwbG90bHkpCmdncGxvdGx5KHAxKQpgYGAKClwKKipJbnN0ZWFkIG9mKioKCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNH0KbGlicmFyeShwbG90bHkpCmdncGxvdGx5KHAyKQpgYGAK