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)
}

1 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)
type 1 2 3 4 5 6 7 8 9 10 11 12
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

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)))

3 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)
type indices 1 2 3 4 5 6 7 8 9 10 11 12
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)))

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)))

5 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)))

6 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