Motivation

This post was initialized from my graduate dissertation about the impact of climate change on Vietnamese farmers’ income. At the time I was doing it, I could not explore the topic by visualization approach because of some limitations. Before digging into the relationship between farmers’ income and climate change, I would take a look of how climate in Vietnam has changed over the past decade at different levels of administrative boundaries by visualization.

All data used in this post is avaiable and open source. Monthly climte data was downloaded from WorldClim, variables were average minimum temperature (°C), average maximum temperature (°C) and total precipitation (mm) from 2010 to 2020. Geographic data of Vietnam was downloaded from GADM. I prefer to apply animation to the plots so as to get an intuitive sense of the change.

Disclaimer: The maps used on this blog are for illustrative purposes and do not express my implication or my opinion, concerning the legal status of any country or territory or concerning the delimitation of frontiers or boundaries.

Visualization of climate change in Vietnam

Temparature

R Codes for data processing and visualization

Mapping code part was inspired by this Milos’s tutorial. I was trying to employ the gganimate package to add transition of the time. Unfortunately, my laptop was not able to render such a long time, like a decade, to illustrate the time continuously. In the future, I will try with other approaches, it could be using a stronger computer or other transition types of gganimate. Therefore, the plot and codes here are still room for improvement.

# 1. Load packages -----
pacman::p_load(
   data.table, # data wrangling
   collapse, # data wragling
   sf, # geospatial data wrangling
   terra, # spatial data wrangling
   # raster,
   stringr,
   lubridate,
   # hrbrthemes,
   ggplot2, # visualization
   gganimate,
   # ggspatial,
   gifski,
   extrafont
   # showtext
)


# 2. Data processing -----
## 2.1 Geographic data -----

### 2.1.1 Import data -----
st_read("d:/R/rpubs_project/visualization/map_climate-change-VN/00_raw_data/geography/gadm41_VNM_0.shp") -> geom_dt

# Varname to lower case
setnames(geom_dt, new = str_to_lower(names(geom_dt)))


## 2.2 CLimate data -----

### 2.2.1 Average maximum temp -----

#### 2.2.1.1 import data -----
list.files(c("d:/R/rpubs_project/visualization/map_climate-change-VN/00_raw_data/climate/wc2.1_cruts4.06_2.5m_tmax_2010-2019/",
             "d:/R/rpubs_project/visualization/map_climate-change-VN/00_raw_data/climate/wc2.1_cruts4.06_2.5m_tmax_2020-2021/"), 
           full.names = T) |> 
   str_subset("(tmax_2021-\\d{2})(?=\\.tif)", negate = T) |> 
   str_subset("(tmax_2020-0[2-9])(?=\\.tif)", negate = T) |> 
   str_subset("(tmax_2020-1(0|1|2))(?=\\.tif)", negate = T) |> 
   (
      \(.) future.apply::future_lapply(., rast) |> setNames(nm = str_extract(., "(tmax_\\d{4}-\\d{2})(?=\\.)"))
   )() -> t_max_sr_10_20_list

mapply(\(x, y) `names<-`(y, x), 
       names(t_max_sr_10_20_list), t_max_sr_10_20_list) -> 
   t_max_sr_10_20_list

# whether raster and vector objects are identical
all.equal(st_crs(geom_dt) , st_crs(t_max_sr_10_20_list[[1]]))

# transform crs of geom_dt that matching t_max SpatRaster
geom_dt <- st_transform(geom_dt, t_max_sr_10_20_list[[1]] |> st_crs())

# quick ploting 1 layer of t_max spatraster list
# t_max_sr_10_20_list[[1]] |> 
#    as.data.frame(xy = T, na.rm = T) |> 
#    ggplot() +
#    geom_tile(aes(x = x, y = y, fill = wc2.1_2.5m_tmax_01)) +
#    coord_sf() +
#    scale_fill_viridis_c(option = "magma", direction = -1) +
#    theme_void() +
#    theme(legend.justification.right = c(0, .8),
#          legend.title = element_blank())

# cropping global raster to local raster by specified vector (reduced rectangular size)
future.apply::future_lapply(t_max_sr_10_20_list, 
       \(.) crop(., geom_dt) |> 
          # masking local raster by vector shap
          mask(geom_dt) |> 
          as.data.frame(xy = T, na.rm = T) |> 
          as.data.table() |> 
          # transform to long format
          melt(id.vars = c("x", "y"))) |> 
   rbindlist() -> t_max_sr_10_20_dt

t_max_sr_10_20_dt[, layer := variable][, variable := NULL][]

# create date from layer name
t_max_sr_10_20_dt[, date := str_extract(layer, "\\d{4}-\\d{2}") |> 
                     str_c("-01") |> as.IDate()][]

t_max_sr_10_20_dt[, month := date |> month(label = T)] |> 
   _[, year := date |> year()] |> 
   _[, month_year := paste0(month, ", ", year)][] -> 
   t_max_sr_10_20_dt

#### 2.2.1.3 breaks -----
min_value <- t_max_sr_10_20_dt[, min(value)]
min_value
max_value <- t_max_sr_10_20_dt[, max(value)]
max_value


breaks <- classInt::classIntervals(
   t_max_sr_10_20_dt$value,
   n = 14,
   style = "pretty"
)$brks

breaks

#### 2.2.1.5 color -----
cols <- colorRampPalette(rev(RColorBrewer::brewer.pal(11, "Spectral")))

#### 2.2.16 font -----

font_import()
loadfonts()


# setnames(fonts_dt, new = str_to_lower(names(fonts_dt)))
# 
# fonts_dt[, familyname |> unique()]



### 2.2.2 map -----
make_plot <- function(){
   t_max_sr_10_20_dt |> split(t_max_sr_10_20_dt$date) |>
      future.apply::future_lapply(\(data) {
         map_vn <-
            ggplot(data) +
            # ggplot(t_max_sr_10_20_dt[date == "2010-01-01"]) +
            geom_tile(aes(x = x, y = y, fill = value)) +
            # facet_wrap(~ date) +
            scale_fill_gradientn(
               name = "\u00B0C",
               colors = cols(14),
               limits = c(5, 40),
               breaks = seq(5, 40, 5),
               guide = guide_colorbar(
                  barheight = 6,
                  barwidth = .8,
                  nbin = 8,
                  direction = "vertical"
               )
            ) +
            coord_sf(crs = crs(t_max_sr_10_20_list[[1]])) +
            annotate("text", x = 108.1, y = 19, label = data$month_year |> unique(),
               size = 4, family = "Libre Baskerville") +
            theme_minimal() +
            theme(
               # text = element_text(family = "Josefin Sans"),
               axis.line = element_blank(),
               axis.title.x = element_blank(),
               axis.title.y = element_blank(),
               axis.text.x = element_blank(),
               axis.text.y = element_blank(),
               axis.ticks = element_blank(),
               legend.position = c(.2, .45),
               # legend.title = element_blank(),
               legend.text = element_text(
                  size = 8, color = "grey10",
                  family = "Merriweather Sans"
               ),
               plot.title = element_text(
                  size = 32, color = "gray10",
                  hjust = .5, vjust = 0, 
                  family = "Merriweather Sans Medium"
               ),
               plot.subtitle = element_text(
                  size = 16, color = "#c43c4e",
                  hjust = .5, vjust = -1.5,
                  family = "Merriweather Sans"
               ),
               plot.caption = element_text(
                  family = "Libre Baskerville"
               ),
               panel.grid.major = element_blank(),
               panel.grid.minor = element_blank(),
               plot.margin = unit(c(t= 1, r = 0, l = 0, b = 0), "lines")
            ) +
            labs(
               x = "",
               y = "",
               title = "Climate change in Vietnam",
               subtitle = "Monthly maximum temperature (2010 - 2020)",
               caption = "Author: Duy Khanh\nSource: WorldClim"
            )
         print(map_vn)
         }
      )
   }
# ggsave("d:/R/rpubs_project/visualization/map_climate-change-VN/03_plots/temp_vn.png", map_vn,
#        height = 8,
#        width = 7,
#        units = "in",
#        device = ragg::agg_png)
   
# timelapse_vn_map <- map_vn +
#    annotate("text", x = 108, y = 19, label = "{frame_time}",
#          size = 4.5, family = "Josefin Sans") +
#    transition_time(date) +
#    enter_fade() +
#    exit_fade() +
#    ease_aes("linear", interval = .2)
# 
# animated_time_map <- gganimate::animate(
#    timelapse_vn_map,
#    nframes = 121 * 2,
#    # duration = 20,
#    start_pause = 5,
#    end_pause = 30,
#    height = 8,
#    width = 7,
#    units = "in",
#    res = 200
#    # fps = 15,
#    # renderer = gifski_renderer(loop = T)
# )
# gganimate::anim_save(
#    "d:/R/rpubs_project/visualization/map_climate-change-VN/03_plots/vn_temp.gif",
#    animated_time_map
# )

save_gif(make_plot(), "d:/R/rpubs_project/visualization/map_climate-change-VN/03_plots/vn_temp.gif", 
         height = 1100, width = 1100, delay = .5, res = 150)

Precipitation

R codes for data processing and visualization

rm(list = ls())

# 2. Data processing -----
## 2.1 Geographic data -----

### 2.1.1 Import data -----
# st_read("d:/R/rpubs_project/visualization/map_climate-change-VN/00_raw_data/geography/gadm41_VNM_0.shp") -> geom_dt
# 
# # Varname to lower case
# setnames(geom_dt, new = str_to_lower(names(geom_dt)))


## 2.2 CLimate data -----

### 2.2.1 Total precipitation -----

#### 2.2.1.1 import data -----
list.files(c("d:/R/rpubs_project/visualization/map_climate-change-VN/00_raw_data/climate/wc2.1_cruts4.06_2.5m_prec_2010-2019/",
             "d:/R/rpubs_project/visualization/map_climate-change-VN/00_raw_data/climate/wc2.1_cruts4.06_2.5m_prec_2020-2021/"), 
           full.names = T) |> 
   str_subset("(prec_2021-\\d{2})(?=\\.tif)", negate = T) |> 
   str_subset("(prec_2020-0[2-9])(?=\\.tif)", negate = T) |> 
   str_subset("(prec_2020-1(0|1|2))(?=\\.tif)", negate = T) |> 
   (
      \(.) future.apply::future_lapply(., rast) |> setNames(nm = str_extract(., "(prec_\\d{4}-\\d{2})(?=\\.)"))
   )() -> prec_sr_10_20_list

mapply(\(x, y) `names<-`(y, x), 
       names(prec_sr_10_20_list), prec_sr_10_20_list) -> 
   prec_sr_10_20_list

# whether raster and vector objects are identical
all.equal(st_crs(geom_dt) , st_crs(prec_sr_10_20_list[[1]]))

# transform crs of geom_dt that matching prec SpatRaster
geom_dt <- st_transform(geom_dt, prec_sr_10_20_list[[1]] |> st_crs())

# quick ploting 1 layer of prec spatraster list
# prec_sr_10_20_list[[1]] |> 
#    as.data.frame(xy = T, na.rm = T) |> 
#    ggplot() +
#    geom_tile(aes(x = x, y = y, fill = wc2.1_2.5m_prec_01)) +
#    coord_sf() +
#    scale_fill_viridis_c(option = "magma", direction = -1) +
#    theme_void() +
#    theme(legend.justification.right = c(0, .8),
#          legend.title = element_blank())

# cropping global raster to local raster by specified vector (reduced rectangular size)
future.apply::future_lapply(prec_sr_10_20_list, 
       \(.) crop(., geom_dt) |> 
          # masking local raster by vector shap
          mask(geom_dt) |> 
          as.data.frame(xy = T, na.rm = T) |> 
          as.data.table() |> 
          # transform to long format
          melt(id.vars = c("x", "y"))) |> 
   rbindlist() -> prec_sr_10_20_dt

prec_sr_10_20_dt[, layer := variable][, variable := NULL][]

# create date from layer name
prec_sr_10_20_dt[, date := str_extract(layer, "\\d{4}-\\d{2}") |> 
                     str_c("-01") |> as.IDate()][]

prec_sr_10_20_dt[, month := date |> month(label = T)] |> 
   _[, year := date |> year()] |> 
   _[, month_year := paste0(month, ", ", year)][] -> 
   prec_sr_10_20_dt

#### 2.2.1.3 breaks -----
min_value <- prec_sr_10_20_dt[, min(value)]
min_value
max_value <- prec_sr_10_20_dt[, max(value)]
max_value


breaks <- classInt::classIntervals(
   prec_sr_10_20_dt$value,
   n = 14,
   style = "pretty"
)$brks

breaks

#### 2.2.1.5 color -----
cols <- colorRampPalette(RColorBrewer::brewer.pal(9, "Blues")[3:9])

#### 2.2.16 font -----

# font_import()
# loadfonts()


# setnames(fonts_dt, new = str_to_lower(names(fonts_dt)))
# 
# fonts_dt[, familyname |> unique()]



### 2.2.2 map -----
make_plot <- function(){
   prec_sr_10_20_dt |> split(prec_sr_10_20_dt$date) |>
      future.apply::future_lapply(\(data) {
         map_vn <-
            ggplot(data) +
            # ggplot(prec_sr_10_20_dt[date == "2010-01-01"]) +
            geom_tile(aes(x = x, y = y, fill = value)) +
            # facet_wrap(~ date) +
            scale_fill_gradientn(
               name = "mm",
               colors = cols(16),
               limits = c(0, 2200),
               # breaks = breaks,
               guide = guide_colorbar(
                  barheight = 6,
                  barwidth = .8,
                  nbin = 8,
                  direction = "vertical"
               )
            ) +
            coord_sf(crs = crs(prec_sr_10_20_list[[1]])) +
            annotate("text", x = 108.1, y = 19, 
                     label = 
                        # "Jan, 2010",
                        data$month_year |> unique(),
               size = 4, family = "Libre Baskerville") +
            theme_minimal() +
            theme(
               # text = element_text(family = "Josefin Sans"),
               axis.line = element_blank(),
               axis.title.x = element_blank(),
               axis.title.y = element_blank(),
               axis.text.x = element_blank(),
               axis.text.y = element_blank(),
               axis.ticks = element_blank(),
               legend.position = c(.2, .45),
               # legend.title = element_blank(),
               legend.text = element_text(
                  size = 8, color = "grey10",
                  family = "Merriweather Sans"
               ),
               plot.title = element_text(
                  size = 32, color = "gray10",
                  hjust = .5, vjust = 0, 
                  family = "Merriweather Sans Medium"
               ),
               plot.subtitle = element_text(
                  size = 16, color = "#08306B",
                  hjust = .5, vjust = -1.5,
                  family = "Merriweather Sans"
               ),
               plot.caption = element_text(
                  family = "Libre Baskerville"
               ),
               panel.grid.major = element_blank(),
               panel.grid.minor = element_blank(),
               plot.margin = unit(c(t= 1, r = 0, l = 0, b = 0), "lines")
            ) +
            labs(
               x = "",
               y = "",
               title = "Climate change in Vietnam",
               subtitle = "Monthly total precipitation (2010 - 2020)",
               caption = "Author: Duy Khanh\nSource: WorldClim"
            )
         print(map_vn)
         }
      )
}

save_gif(make_plot(), "d:/R/rpubs_project/visualization/map_climate-change-VN/03_plots/vn_prec.gif", 
         height = 1100, width = 1100, delay = .5, res = 150) 
LS0tDQp0aXRsZTogIkhvdyBjbGltYXRlIGNoYW5nZWQgb3ZlciB0aGUgcGFzdCBkZWNhZGUgaW4gVmlldG5hbSA/Ig0Kc3VidGl0bGU6IERhdGEgdmlzdWFsaXphdGlvbiBzZXJpZXMNCmF1dGhvcjogIkR1eSBLaGFuaCINCmRhdGU6ICJGZWJydWFyeSAxNCwgMjAyNCINCm91dHB1dDogDQogICBodG1sX2RvY3VtZW50Og0KICAgICAgDQogICAgICB0b2M6IHRydWUNCiAgICAgICMgdG9jX2RlcHRoOiAyDQogICAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICAgICMgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgIyAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgICAjIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgICAgdGhlbWU6IHVuaXRlZA0KICAgICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgICAgY29kZV9mb2xkaW5nOiBzaG93DQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQotLS0NCg0KYGBge2NzcywgZWNobyA9IEZ9DQouY29kZV9jaHVua19iZ19jb2xvciB7DQogICBiYWNrZ3JvdW5kLWNvbG9yOiBibGFjazsNCn0NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgIGVjaG8gPSBULA0KICAgcmVzdWx0cyA9IEYsDQogICBlcnJvciA9IEYsDQogICB3YXJuaW5nID0gRiwNCiAgIG1lc3NhZ2UgPSBGLA0KICAgZXZhbCA9IEYNCiAgICMgY2xhc3Muc291cmNlID0gImNvZGVfY2h1bmtfYmdfY29sb3IiLA0KICAgIyBjbGFzcy5vdXRwdXQgPSAiYmctcHJpbWFyeSINCiAgICkNCmBgYA0KDQojIE1vdGl2YXRpb24NCg0KVGhpcyBwb3N0IHdhcyBpbml0aWFsaXplZCBmcm9tIG15IGdyYWR1YXRlIGRpc3NlcnRhdGlvbiBhYm91dCB0aGUgaW1wYWN0IG9mIGNsaW1hdGUgY2hhbmdlIG9uIFZpZXRuYW1lc2UgZmFybWVycycgaW5jb21lLiBBdCB0aGUgdGltZSBJIHdhcyBkb2luZyBpdCwgSSBjb3VsZCBub3QgZXhwbG9yZSB0aGUgdG9waWMgYnkgdmlzdWFsaXphdGlvbiBhcHByb2FjaCBiZWNhdXNlIG9mIHNvbWUgbGltaXRhdGlvbnMuIEJlZm9yZSBkaWdnaW5nIGludG8gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGZhcm1lcnMnIGluY29tZSBhbmQgY2xpbWF0ZSBjaGFuZ2UsIEkgd291bGQgdGFrZSBhIGxvb2sgb2YgaG93IGNsaW1hdGUgaW4gVmlldG5hbSBoYXMgY2hhbmdlZCBvdmVyIHRoZSBwYXN0IGRlY2FkZSBhdCBkaWZmZXJlbnQgbGV2ZWxzIG9mIGFkbWluaXN0cmF0aXZlIGJvdW5kYXJpZXMgYnkgdmlzdWFsaXphdGlvbi4NCg0KQWxsIGRhdGEgdXNlZCBpbiB0aGlzIHBvc3QgaXMgYXZhaWFibGUgYW5kIG9wZW4gc291cmNlLiBNb250aGx5IGNsaW10ZSBkYXRhIHdhcyBkb3dubG9hZGVkIGZyb20gW1dvcmxkQ2xpbV0oaHR0cHM6Ly93d3cud29ybGRjbGltLm9yZy8pLCB2YXJpYWJsZXMgd2VyZSBhdmVyYWdlIG1pbmltdW0gdGVtcGVyYXR1cmUgKMKwQyksIGF2ZXJhZ2UgbWF4aW11bSB0ZW1wZXJhdHVyZSAowrBDKSBhbmQgdG90YWwgcHJlY2lwaXRhdGlvbiAobW0pIGZyb20gMjAxMCB0byAyMDIwLiBHZW9ncmFwaGljIGRhdGEgb2YgVmlldG5hbSB3YXMgZG93bmxvYWRlZCBmcm9tIFtHQURNXShodHRwczovL2dhZG0ub3JnL2RhdGEuaHRtbCkuIEkgcHJlZmVyIHRvIGFwcGx5IGFuaW1hdGlvbiB0byB0aGUgcGxvdHMgc28gYXMgdG8gZ2V0IGFuIGludHVpdGl2ZSBzZW5zZSBvZiB0aGUgY2hhbmdlLg0KDQoqKipEaXNjbGFpbWVyKio6IFRoZSBtYXBzIHVzZWQgb24gdGhpcyBibG9nIGFyZSBmb3IgaWxsdXN0cmF0aXZlIHB1cnBvc2VzIGFuZCBkbyBub3QgZXhwcmVzcyBteSBpbXBsaWNhdGlvbiBvciBteSBvcGluaW9uLCBjb25jZXJuaW5nIHRoZSBsZWdhbCBzdGF0dXMgb2YgYW55IGNvdW50cnkgb3IgdGVycml0b3J5IG9yIGNvbmNlcm5pbmcgdGhlIGRlbGltaXRhdGlvbiBvZiBmcm9udGllcnMgb3IgYm91bmRhcmllcy4qDQoNCiMgVmlzdWFsaXphdGlvbiBvZiBjbGltYXRlIGNoYW5nZSBpbiBWaWV0bmFtIHsudGFic2V0fQ0KDQoNCiMjIFRlbXBhcmF0dXJlDQoNCiFbXShpbWFnZXMvdm5fdGVtcC0wMS5naWYpDQoNCiMjIyBSIENvZGVzIGZvciBkYXRhIHByb2Nlc3NpbmcgYW5kIHZpc3VhbGl6YXRpb24NCg0KTWFwcGluZyBjb2RlIHBhcnQgd2FzIGluc3BpcmVkIGJ5IHRoaXMgW01pbG9zJ3MgdHV0b3JpYWxdKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9MlZIdWFGcXRBc1kmdD00NjAwcykuIEkgd2FzIHRyeWluZyB0byBlbXBsb3kgdGhlIGBnZ2FuaW1hdGVgIHBhY2thZ2UgdG8gYWRkIHRyYW5zaXRpb24gb2YgdGhlIHRpbWUuIFVuZm9ydHVuYXRlbHksIG15IGxhcHRvcCB3YXMgbm90IGFibGUgdG8gcmVuZGVyIHN1Y2ggYSBsb25nIHRpbWUsIGxpa2UgYSBkZWNhZGUsIHRvIGlsbHVzdHJhdGUgdGhlIHRpbWUgY29udGludW91c2x5LiBJbiB0aGUgZnV0dXJlLCBJIHdpbGwgdHJ5IHdpdGggb3RoZXIgYXBwcm9hY2hlcywgaXQgY291bGQgYmUgdXNpbmcgYSBzdHJvbmdlciBjb21wdXRlciBvciBvdGhlciB0cmFuc2l0aW9uIHR5cGVzIG9mIGBnZ2FuaW1hdGVgLiBUaGVyZWZvcmUsIHRoZSBwbG90IGFuZCBjb2RlcyBoZXJlIGFyZSBzdGlsbCByb29tIGZvciBpbXByb3ZlbWVudC4NCg0KYGBge3IgY291bnRyeS50ZW1wfQ0KIyAxLiBMb2FkIHBhY2thZ2VzIC0tLS0tDQpwYWNtYW46OnBfbG9hZCgNCiAgIGRhdGEudGFibGUsICMgZGF0YSB3cmFuZ2xpbmcNCiAgIGNvbGxhcHNlLCAjIGRhdGEgd3JhZ2xpbmcNCiAgIHNmLCAjIGdlb3NwYXRpYWwgZGF0YSB3cmFuZ2xpbmcNCiAgIHRlcnJhLCAjIHNwYXRpYWwgZGF0YSB3cmFuZ2xpbmcNCiAgICMgcmFzdGVyLA0KICAgc3RyaW5nciwNCiAgIGx1YnJpZGF0ZSwNCiAgICMgaHJicnRoZW1lcywNCiAgIGdncGxvdDIsICMgdmlzdWFsaXphdGlvbg0KICAgZ2dhbmltYXRlLA0KICAgIyBnZ3NwYXRpYWwsDQogICBnaWZza2ksDQogICBleHRyYWZvbnQNCiAgICMgc2hvd3RleHQNCikNCg0KDQojIDIuIERhdGEgcHJvY2Vzc2luZyAtLS0tLQ0KIyMgMi4xIEdlb2dyYXBoaWMgZGF0YSAtLS0tLQ0KDQojIyMgMi4xLjEgSW1wb3J0IGRhdGEgLS0tLS0NCnN0X3JlYWQoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9nZW9ncmFwaHkvZ2FkbTQxX1ZOTV8wLnNocCIpIC0+IGdlb21fZHQNCg0KIyBWYXJuYW1lIHRvIGxvd2VyIGNhc2UNCnNldG5hbWVzKGdlb21fZHQsIG5ldyA9IHN0cl90b19sb3dlcihuYW1lcyhnZW9tX2R0KSkpDQoNCg0KIyMgMi4yIENMaW1hdGUgZGF0YSAtLS0tLQ0KDQojIyMgMi4yLjEgQXZlcmFnZSBtYXhpbXVtIHRlbXAgLS0tLS0NCg0KIyMjIyAyLjIuMS4xIGltcG9ydCBkYXRhIC0tLS0tDQpsaXN0LmZpbGVzKGMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3RtYXhfMjAxMC0yMDE5LyIsDQogICAgICAgICAgICAgImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3RtYXhfMjAyMC0yMDIxLyIpLCANCiAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpIHw+IA0KICAgc3RyX3N1YnNldCgiKHRtYXhfMjAyMS1cXGR7Mn0pKD89XFwudGlmKSIsIG5lZ2F0ZSA9IFQpIHw+IA0KICAgc3RyX3N1YnNldCgiKHRtYXhfMjAyMC0wWzItOV0pKD89XFwudGlmKSIsIG5lZ2F0ZSA9IFQpIHw+IA0KICAgc3RyX3N1YnNldCgiKHRtYXhfMjAyMC0xKDB8MXwyKSkoPz1cXC50aWYpIiwgbmVnYXRlID0gVCkgfD4gDQogICAoDQogICAgICBcKC4pIGZ1dHVyZS5hcHBseTo6ZnV0dXJlX2xhcHBseSguLCByYXN0KSB8PiBzZXROYW1lcyhubSA9IHN0cl9leHRyYWN0KC4sICIodG1heF9cXGR7NH0tXFxkezJ9KSg/PVxcLikiKSkNCiAgICkoKSAtPiB0X21heF9zcl8xMF8yMF9saXN0DQoNCm1hcHBseShcKHgsIHkpIGBuYW1lczwtYCh5LCB4KSwgDQogICAgICAgbmFtZXModF9tYXhfc3JfMTBfMjBfbGlzdCksIHRfbWF4X3NyXzEwXzIwX2xpc3QpIC0+IA0KICAgdF9tYXhfc3JfMTBfMjBfbGlzdA0KDQojIHdoZXRoZXIgcmFzdGVyIGFuZCB2ZWN0b3Igb2JqZWN0cyBhcmUgaWRlbnRpY2FsDQphbGwuZXF1YWwoc3RfY3JzKGdlb21fZHQpICwgc3RfY3JzKHRfbWF4X3NyXzEwXzIwX2xpc3RbWzFdXSkpDQoNCiMgdHJhbnNmb3JtIGNycyBvZiBnZW9tX2R0IHRoYXQgbWF0Y2hpbmcgdF9tYXggU3BhdFJhc3Rlcg0KZ2VvbV9kdCA8LSBzdF90cmFuc2Zvcm0oZ2VvbV9kdCwgdF9tYXhfc3JfMTBfMjBfbGlzdFtbMV1dIHw+IHN0X2NycygpKQ0KDQojIHF1aWNrIHBsb3RpbmcgMSBsYXllciBvZiB0X21heCBzcGF0cmFzdGVyIGxpc3QNCiMgdF9tYXhfc3JfMTBfMjBfbGlzdFtbMV1dIHw+IA0KIyAgICBhcy5kYXRhLmZyYW1lKHh5ID0gVCwgbmEucm0gPSBUKSB8PiANCiMgICAgZ2dwbG90KCkgKw0KIyAgICBnZW9tX3RpbGUoYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IHdjMi4xXzIuNW1fdG1heF8wMSkpICsNCiMgICAgY29vcmRfc2YoKSArDQojICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJtYWdtYSIsIGRpcmVjdGlvbiA9IC0xKSArDQojICAgIHRoZW1lX3ZvaWQoKSArDQojICAgIHRoZW1lKGxlZ2VuZC5qdXN0aWZpY2F0aW9uLnJpZ2h0ID0gYygwLCAuOCksDQojICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KIyBjcm9wcGluZyBnbG9iYWwgcmFzdGVyIHRvIGxvY2FsIHJhc3RlciBieSBzcGVjaWZpZWQgdmVjdG9yIChyZWR1Y2VkIHJlY3Rhbmd1bGFyIHNpemUpDQpmdXR1cmUuYXBwbHk6OmZ1dHVyZV9sYXBwbHkodF9tYXhfc3JfMTBfMjBfbGlzdCwgDQogICAgICAgXCguKSBjcm9wKC4sIGdlb21fZHQpIHw+IA0KICAgICAgICAgICMgbWFza2luZyBsb2NhbCByYXN0ZXIgYnkgdmVjdG9yIHNoYXANCiAgICAgICAgICBtYXNrKGdlb21fZHQpIHw+IA0KICAgICAgICAgIGFzLmRhdGEuZnJhbWUoeHkgPSBULCBuYS5ybSA9IFQpIHw+IA0KICAgICAgICAgIGFzLmRhdGEudGFibGUoKSB8PiANCiAgICAgICAgICAjIHRyYW5zZm9ybSB0byBsb25nIGZvcm1hdA0KICAgICAgICAgIG1lbHQoaWQudmFycyA9IGMoIngiLCAieSIpKSkgfD4gDQogICByYmluZGxpc3QoKSAtPiB0X21heF9zcl8xMF8yMF9kdA0KDQp0X21heF9zcl8xMF8yMF9kdFssIGxheWVyIDo9IHZhcmlhYmxlXVssIHZhcmlhYmxlIDo9IE5VTExdW10NCg0KIyBjcmVhdGUgZGF0ZSBmcm9tIGxheWVyIG5hbWUNCnRfbWF4X3NyXzEwXzIwX2R0WywgZGF0ZSA6PSBzdHJfZXh0cmFjdChsYXllciwgIlxcZHs0fS1cXGR7Mn0iKSB8PiANCiAgICAgICAgICAgICAgICAgICAgIHN0cl9jKCItMDEiKSB8PiBhcy5JRGF0ZSgpXVtdDQoNCnRfbWF4X3NyXzEwXzIwX2R0WywgbW9udGggOj0gZGF0ZSB8PiBtb250aChsYWJlbCA9IFQpXSB8PiANCiAgIF9bLCB5ZWFyIDo9IGRhdGUgfD4geWVhcigpXSB8PiANCiAgIF9bLCBtb250aF95ZWFyIDo9IHBhc3RlMChtb250aCwgIiwgIiwgeWVhcildW10gLT4gDQogICB0X21heF9zcl8xMF8yMF9kdA0KDQojIyMjIDIuMi4xLjMgYnJlYWtzIC0tLS0tDQptaW5fdmFsdWUgPC0gdF9tYXhfc3JfMTBfMjBfZHRbLCBtaW4odmFsdWUpXQ0KbWluX3ZhbHVlDQptYXhfdmFsdWUgPC0gdF9tYXhfc3JfMTBfMjBfZHRbLCBtYXgodmFsdWUpXQ0KbWF4X3ZhbHVlDQoNCg0KYnJlYWtzIDwtIGNsYXNzSW50OjpjbGFzc0ludGVydmFscygNCiAgIHRfbWF4X3NyXzEwXzIwX2R0JHZhbHVlLA0KICAgbiA9IDE0LA0KICAgc3R5bGUgPSAicHJldHR5Ig0KKSRicmtzDQoNCmJyZWFrcw0KDQojIyMjIDIuMi4xLjUgY29sb3IgLS0tLS0NCmNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShyZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDExLCAiU3BlY3RyYWwiKSkpDQoNCiMjIyMgMi4yLjE2IGZvbnQgLS0tLS0NCg0KZm9udF9pbXBvcnQoKQ0KbG9hZGZvbnRzKCkNCg0KDQojIHNldG5hbWVzKGZvbnRzX2R0LCBuZXcgPSBzdHJfdG9fbG93ZXIobmFtZXMoZm9udHNfZHQpKSkNCiMgDQojIGZvbnRzX2R0WywgZmFtaWx5bmFtZSB8PiB1bmlxdWUoKV0NCg0KDQoNCiMjIyAyLjIuMiBtYXAgLS0tLS0NCm1ha2VfcGxvdCA8LSBmdW5jdGlvbigpew0KICAgdF9tYXhfc3JfMTBfMjBfZHQgfD4gc3BsaXQodF9tYXhfc3JfMTBfMjBfZHQkZGF0ZSkgfD4NCiAgICAgIGZ1dHVyZS5hcHBseTo6ZnV0dXJlX2xhcHBseShcKGRhdGEpIHsNCiAgICAgICAgIG1hcF92biA8LQ0KICAgICAgICAgICAgZ2dwbG90KGRhdGEpICsNCiAgICAgICAgICAgICMgZ2dwbG90KHRfbWF4X3NyXzEwXzIwX2R0W2RhdGUgPT0gIjIwMTAtMDEtMDEiXSkgKw0KICAgICAgICAgICAgZ2VvbV90aWxlKGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSB2YWx1ZSkpICsNCiAgICAgICAgICAgICMgZmFjZXRfd3JhcCh+IGRhdGUpICsNCiAgICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKA0KICAgICAgICAgICAgICAgbmFtZSA9ICJcdTAwQjBDIiwNCiAgICAgICAgICAgICAgIGNvbG9ycyA9IGNvbHMoMTQpLA0KICAgICAgICAgICAgICAgbGltaXRzID0gYyg1LCA0MCksDQogICAgICAgICAgICAgICBicmVha3MgPSBzZXEoNSwgNDAsIDUpLA0KICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcigNCiAgICAgICAgICAgICAgICAgIGJhcmhlaWdodCA9IDYsDQogICAgICAgICAgICAgICAgICBiYXJ3aWR0aCA9IC44LA0KICAgICAgICAgICAgICAgICAgbmJpbiA9IDgsDQogICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAidmVydGljYWwiDQogICAgICAgICAgICAgICApDQogICAgICAgICAgICApICsNCiAgICAgICAgICAgIGNvb3JkX3NmKGNycyA9IGNycyh0X21heF9zcl8xMF8yMF9saXN0W1sxXV0pKSArDQogICAgICAgICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxMDguMSwgeSA9IDE5LCBsYWJlbCA9IGRhdGEkbW9udGhfeWVhciB8PiB1bmlxdWUoKSwNCiAgICAgICAgICAgICAgIHNpemUgPSA0LCBmYW1pbHkgPSAiTGlicmUgQmFza2VydmlsbGUiKSArDQogICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICAgICAgdGhlbWUoDQogICAgICAgICAgICAgICAjIHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkpvc2VmaW4gU2FucyIpLA0KICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKC4yLCAuNDUpLA0KICAgICAgICAgICAgICAgIyBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dCgNCiAgICAgICAgICAgICAgICAgIHNpemUgPSA4LCBjb2xvciA9ICJncmV5MTAiLA0KICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIk1lcnJpd2VhdGhlciBTYW5zIg0KICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoDQogICAgICAgICAgICAgICAgICBzaXplID0gMzIsIGNvbG9yID0gImdyYXkxMCIsDQogICAgICAgICAgICAgICAgICBoanVzdCA9IC41LCB2anVzdCA9IDAsIA0KICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIk1lcnJpd2VhdGhlciBTYW5zIE1lZGl1bSINCiAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KA0KICAgICAgICAgICAgICAgICAgc2l6ZSA9IDE2LCBjb2xvciA9ICIjYzQzYzRlIiwNCiAgICAgICAgICAgICAgICAgIGhqdXN0ID0gLjUsIHZqdXN0ID0gLTEuNSwNCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJNZXJyaXdlYXRoZXIgU2FucyINCiAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoDQogICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiTGlicmUgQmFza2VydmlsbGUiDQogICAgICAgICAgICAgICApLA0KICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYyh0PSAxLCByID0gMCwgbCA9IDAsIGIgPSAwKSwgImxpbmVzIikNCiAgICAgICAgICAgICkgKw0KICAgICAgICAgICAgbGFicygNCiAgICAgICAgICAgICAgIHggPSAiIiwNCiAgICAgICAgICAgICAgIHkgPSAiIiwNCiAgICAgICAgICAgICAgIHRpdGxlID0gIkNsaW1hdGUgY2hhbmdlIGluIFZpZXRuYW0iLA0KICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTW9udGhseSBtYXhpbXVtIHRlbXBlcmF0dXJlICgyMDEwIC0gMjAyMCkiLA0KICAgICAgICAgICAgICAgY2FwdGlvbiA9ICJBdXRob3I6IER1eSBLaGFuaFxuU291cmNlOiBXb3JsZENsaW0iDQogICAgICAgICAgICApDQogICAgICAgICBwcmludChtYXBfdm4pDQogICAgICAgICB9DQogICAgICApDQogICB9DQojIGdnc2F2ZSgiZDovUi9ycHVic19wcm9qZWN0L3Zpc3VhbGl6YXRpb24vbWFwX2NsaW1hdGUtY2hhbmdlLVZOLzAzX3Bsb3RzL3RlbXBfdm4ucG5nIiwgbWFwX3ZuLA0KIyAgICAgICAgaGVpZ2h0ID0gOCwNCiMgICAgICAgIHdpZHRoID0gNywNCiMgICAgICAgIHVuaXRzID0gImluIiwNCiMgICAgICAgIGRldmljZSA9IHJhZ2c6OmFnZ19wbmcpDQogICANCiMgdGltZWxhcHNlX3ZuX21hcCA8LSBtYXBfdm4gKw0KIyAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxMDgsIHkgPSAxOSwgbGFiZWwgPSAie2ZyYW1lX3RpbWV9IiwNCiMgICAgICAgICAgc2l6ZSA9IDQuNSwgZmFtaWx5ID0gIkpvc2VmaW4gU2FucyIpICsNCiMgICAgdHJhbnNpdGlvbl90aW1lKGRhdGUpICsNCiMgICAgZW50ZXJfZmFkZSgpICsNCiMgICAgZXhpdF9mYWRlKCkgKw0KIyAgICBlYXNlX2FlcygibGluZWFyIiwgaW50ZXJ2YWwgPSAuMikNCiMgDQojIGFuaW1hdGVkX3RpbWVfbWFwIDwtIGdnYW5pbWF0ZTo6YW5pbWF0ZSgNCiMgICAgdGltZWxhcHNlX3ZuX21hcCwNCiMgICAgbmZyYW1lcyA9IDEyMSAqIDIsDQojICAgICMgZHVyYXRpb24gPSAyMCwNCiMgICAgc3RhcnRfcGF1c2UgPSA1LA0KIyAgICBlbmRfcGF1c2UgPSAzMCwNCiMgICAgaGVpZ2h0ID0gOCwNCiMgICAgd2lkdGggPSA3LA0KIyAgICB1bml0cyA9ICJpbiIsDQojICAgIHJlcyA9IDIwMA0KIyAgICAjIGZwcyA9IDE1LA0KIyAgICAjIHJlbmRlcmVyID0gZ2lmc2tpX3JlbmRlcmVyKGxvb3AgPSBUKQ0KIyApDQojIGdnYW5pbWF0ZTo6YW5pbV9zYXZlKA0KIyAgICAiZDovUi9ycHVic19wcm9qZWN0L3Zpc3VhbGl6YXRpb24vbWFwX2NsaW1hdGUtY2hhbmdlLVZOLzAzX3Bsb3RzL3ZuX3RlbXAuZ2lmIiwNCiMgICAgYW5pbWF0ZWRfdGltZV9tYXANCiMgKQ0KDQpzYXZlX2dpZihtYWtlX3Bsb3QoKSwgImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wM19wbG90cy92bl90ZW1wLmdpZiIsIA0KICAgICAgICAgaGVpZ2h0ID0gMTEwMCwgd2lkdGggPSAxMTAwLCBkZWxheSA9IC41LCByZXMgPSAxNTApDQpgYGANCg0KIyMgUHJlY2lwaXRhdGlvbg0KDQohW10oaW1hZ2VzL3ZuX3ByZWMuZ2lmKQ0KDQojIyMgUiBjb2RlcyBmb3IgZGF0YSBwcm9jZXNzaW5nIGFuZCB2aXN1YWxpemF0aW9uDQoNCmBgYHtyfQ0Kcm0obGlzdCA9IGxzKCkpDQoNCiMgMi4gRGF0YSBwcm9jZXNzaW5nIC0tLS0tDQojIyAyLjEgR2VvZ3JhcGhpYyBkYXRhIC0tLS0tDQoNCiMjIyAyLjEuMSBJbXBvcnQgZGF0YSAtLS0tLQ0KIyBzdF9yZWFkKCJkOi9SL3JwdWJzX3Byb2plY3QvdmlzdWFsaXphdGlvbi9tYXBfY2xpbWF0ZS1jaGFuZ2UtVk4vMDBfcmF3X2RhdGEvZ2VvZ3JhcGh5L2dhZG00MV9WTk1fMC5zaHAiKSAtPiBnZW9tX2R0DQojIA0KIyAjIFZhcm5hbWUgdG8gbG93ZXIgY2FzZQ0KIyBzZXRuYW1lcyhnZW9tX2R0LCBuZXcgPSBzdHJfdG9fbG93ZXIobmFtZXMoZ2VvbV9kdCkpKQ0KDQoNCiMjIDIuMiBDTGltYXRlIGRhdGEgLS0tLS0NCg0KIyMjIDIuMi4xIFRvdGFsIHByZWNpcGl0YXRpb24gLS0tLS0NCg0KIyMjIyAyLjIuMS4xIGltcG9ydCBkYXRhIC0tLS0tDQpsaXN0LmZpbGVzKGMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3ByZWNfMjAxMC0yMDE5LyIsDQogICAgICAgICAgICAgImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3ByZWNfMjAyMC0yMDIxLyIpLCANCiAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpIHw+IA0KICAgc3RyX3N1YnNldCgiKHByZWNfMjAyMS1cXGR7Mn0pKD89XFwudGlmKSIsIG5lZ2F0ZSA9IFQpIHw+IA0KICAgc3RyX3N1YnNldCgiKHByZWNfMjAyMC0wWzItOV0pKD89XFwudGlmKSIsIG5lZ2F0ZSA9IFQpIHw+IA0KICAgc3RyX3N1YnNldCgiKHByZWNfMjAyMC0xKDB8MXwyKSkoPz1cXC50aWYpIiwgbmVnYXRlID0gVCkgfD4gDQogICAoDQogICAgICBcKC4pIGZ1dHVyZS5hcHBseTo6ZnV0dXJlX2xhcHBseSguLCByYXN0KSB8PiBzZXROYW1lcyhubSA9IHN0cl9leHRyYWN0KC4sICIocHJlY19cXGR7NH0tXFxkezJ9KSg/PVxcLikiKSkNCiAgICkoKSAtPiBwcmVjX3NyXzEwXzIwX2xpc3QNCg0KbWFwcGx5KFwoeCwgeSkgYG5hbWVzPC1gKHksIHgpLCANCiAgICAgICBuYW1lcyhwcmVjX3NyXzEwXzIwX2xpc3QpLCBwcmVjX3NyXzEwXzIwX2xpc3QpIC0+IA0KICAgcHJlY19zcl8xMF8yMF9saXN0DQoNCiMgd2hldGhlciByYXN0ZXIgYW5kIHZlY3RvciBvYmplY3RzIGFyZSBpZGVudGljYWwNCmFsbC5lcXVhbChzdF9jcnMoZ2VvbV9kdCkgLCBzdF9jcnMocHJlY19zcl8xMF8yMF9saXN0W1sxXV0pKQ0KDQojIHRyYW5zZm9ybSBjcnMgb2YgZ2VvbV9kdCB0aGF0IG1hdGNoaW5nIHByZWMgU3BhdFJhc3Rlcg0KZ2VvbV9kdCA8LSBzdF90cmFuc2Zvcm0oZ2VvbV9kdCwgcHJlY19zcl8xMF8yMF9saXN0W1sxXV0gfD4gc3RfY3JzKCkpDQoNCiMgcXVpY2sgcGxvdGluZyAxIGxheWVyIG9mIHByZWMgc3BhdHJhc3RlciBsaXN0DQojIHByZWNfc3JfMTBfMjBfbGlzdFtbMV1dIHw+IA0KIyAgICBhcy5kYXRhLmZyYW1lKHh5ID0gVCwgbmEucm0gPSBUKSB8PiANCiMgICAgZ2dwbG90KCkgKw0KIyAgICBnZW9tX3RpbGUoYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IHdjMi4xXzIuNW1fcHJlY18wMSkpICsNCiMgICAgY29vcmRfc2YoKSArDQojICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJtYWdtYSIsIGRpcmVjdGlvbiA9IC0xKSArDQojICAgIHRoZW1lX3ZvaWQoKSArDQojICAgIHRoZW1lKGxlZ2VuZC5qdXN0aWZpY2F0aW9uLnJpZ2h0ID0gYygwLCAuOCksDQojICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KIyBjcm9wcGluZyBnbG9iYWwgcmFzdGVyIHRvIGxvY2FsIHJhc3RlciBieSBzcGVjaWZpZWQgdmVjdG9yIChyZWR1Y2VkIHJlY3Rhbmd1bGFyIHNpemUpDQpmdXR1cmUuYXBwbHk6OmZ1dHVyZV9sYXBwbHkocHJlY19zcl8xMF8yMF9saXN0LCANCiAgICAgICBcKC4pIGNyb3AoLiwgZ2VvbV9kdCkgfD4gDQogICAgICAgICAgIyBtYXNraW5nIGxvY2FsIHJhc3RlciBieSB2ZWN0b3Igc2hhcA0KICAgICAgICAgIG1hc2soZ2VvbV9kdCkgfD4gDQogICAgICAgICAgYXMuZGF0YS5mcmFtZSh4eSA9IFQsIG5hLnJtID0gVCkgfD4gDQogICAgICAgICAgYXMuZGF0YS50YWJsZSgpIHw+IA0KICAgICAgICAgICMgdHJhbnNmb3JtIHRvIGxvbmcgZm9ybWF0DQogICAgICAgICAgbWVsdChpZC52YXJzID0gYygieCIsICJ5IikpKSB8PiANCiAgIHJiaW5kbGlzdCgpIC0+IHByZWNfc3JfMTBfMjBfZHQNCg0KcHJlY19zcl8xMF8yMF9kdFssIGxheWVyIDo9IHZhcmlhYmxlXVssIHZhcmlhYmxlIDo9IE5VTExdW10NCg0KIyBjcmVhdGUgZGF0ZSBmcm9tIGxheWVyIG5hbWUNCnByZWNfc3JfMTBfMjBfZHRbLCBkYXRlIDo9IHN0cl9leHRyYWN0KGxheWVyLCAiXFxkezR9LVxcZHsyfSIpIHw+IA0KICAgICAgICAgICAgICAgICAgICAgc3RyX2MoIi0wMSIpIHw+IGFzLklEYXRlKCldW10NCg0KcHJlY19zcl8xMF8yMF9kdFssIG1vbnRoIDo9IGRhdGUgfD4gbW9udGgobGFiZWwgPSBUKV0gfD4gDQogICBfWywgeWVhciA6PSBkYXRlIHw+IHllYXIoKV0gfD4gDQogICBfWywgbW9udGhfeWVhciA6PSBwYXN0ZTAobW9udGgsICIsICIsIHllYXIpXVtdIC0+IA0KICAgcHJlY19zcl8xMF8yMF9kdA0KDQojIyMjIDIuMi4xLjMgYnJlYWtzIC0tLS0tDQptaW5fdmFsdWUgPC0gcHJlY19zcl8xMF8yMF9kdFssIG1pbih2YWx1ZSldDQptaW5fdmFsdWUNCm1heF92YWx1ZSA8LSBwcmVjX3NyXzEwXzIwX2R0WywgbWF4KHZhbHVlKV0NCm1heF92YWx1ZQ0KDQoNCmJyZWFrcyA8LSBjbGFzc0ludDo6Y2xhc3NJbnRlcnZhbHMoDQogICBwcmVjX3NyXzEwXzIwX2R0JHZhbHVlLA0KICAgbiA9IDE0LA0KICAgc3R5bGUgPSAicHJldHR5Ig0KKSRicmtzDQoNCmJyZWFrcw0KDQojIyMjIDIuMi4xLjUgY29sb3IgLS0tLS0NCmNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoOSwgIkJsdWVzIilbMzo5XSkNCg0KIyMjIyAyLjIuMTYgZm9udCAtLS0tLQ0KDQojIGZvbnRfaW1wb3J0KCkNCiMgbG9hZGZvbnRzKCkNCg0KDQojIHNldG5hbWVzKGZvbnRzX2R0LCBuZXcgPSBzdHJfdG9fbG93ZXIobmFtZXMoZm9udHNfZHQpKSkNCiMgDQojIGZvbnRzX2R0WywgZmFtaWx5bmFtZSB8PiB1bmlxdWUoKV0NCg0KDQoNCiMjIyAyLjIuMiBtYXAgLS0tLS0NCm1ha2VfcGxvdCA8LSBmdW5jdGlvbigpew0KICAgcHJlY19zcl8xMF8yMF9kdCB8PiBzcGxpdChwcmVjX3NyXzEwXzIwX2R0JGRhdGUpIHw+DQogICAgICBmdXR1cmUuYXBwbHk6OmZ1dHVyZV9sYXBwbHkoXChkYXRhKSB7DQogICAgICAgICBtYXBfdm4gPC0NCiAgICAgICAgICAgIGdncGxvdChkYXRhKSArDQogICAgICAgICAgICAjIGdncGxvdChwcmVjX3NyXzEwXzIwX2R0W2RhdGUgPT0gIjIwMTAtMDEtMDEiXSkgKw0KICAgICAgICAgICAgZ2VvbV90aWxlKGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSB2YWx1ZSkpICsNCiAgICAgICAgICAgICMgZmFjZXRfd3JhcCh+IGRhdGUpICsNCiAgICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKA0KICAgICAgICAgICAgICAgbmFtZSA9ICJtbSIsDQogICAgICAgICAgICAgICBjb2xvcnMgPSBjb2xzKDE2KSwNCiAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgMjIwMCksDQogICAgICAgICAgICAgICAjIGJyZWFrcyA9IGJyZWFrcywNCiAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfY29sb3JiYXIoDQogICAgICAgICAgICAgICAgICBiYXJoZWlnaHQgPSA2LA0KICAgICAgICAgICAgICAgICAgYmFyd2lkdGggPSAuOCwNCiAgICAgICAgICAgICAgICAgIG5iaW4gPSA4LA0KICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gInZlcnRpY2FsIg0KICAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgKSArDQogICAgICAgICAgICBjb29yZF9zZihjcnMgPSBjcnMocHJlY19zcl8xMF8yMF9saXN0W1sxXV0pKSArDQogICAgICAgICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxMDguMSwgeSA9IDE5LCANCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gDQogICAgICAgICAgICAgICAgICAgICAgICAjICJKYW4sIDIwMTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSRtb250aF95ZWFyIHw+IHVuaXF1ZSgpLA0KICAgICAgICAgICAgICAgc2l6ZSA9IDQsIGZhbWlseSA9ICJMaWJyZSBCYXNrZXJ2aWxsZSIpICsNCiAgICAgICAgICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgICAgICAgICB0aGVtZSgNCiAgICAgICAgICAgICAgICMgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSm9zZWZpbiBTYW5zIiksDQogICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoLjIsIC40NSksDQogICAgICAgICAgICAgICAjIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KA0KICAgICAgICAgICAgICAgICAgc2l6ZSA9IDgsIGNvbG9yID0gImdyZXkxMCIsDQogICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiTWVycml3ZWF0aGVyIFNhbnMiDQogICAgICAgICAgICAgICApLA0KICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCgNCiAgICAgICAgICAgICAgICAgIHNpemUgPSAzMiwgY29sb3IgPSAiZ3JheTEwIiwNCiAgICAgICAgICAgICAgICAgIGhqdXN0ID0gLjUsIHZqdXN0ID0gMCwgDQogICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiTWVycml3ZWF0aGVyIFNhbnMgTWVkaXVtIg0KICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoDQogICAgICAgICAgICAgICAgICBzaXplID0gMTYsIGNvbG9yID0gIiMwODMwNkIiLA0KICAgICAgICAgICAgICAgICAgaGp1c3QgPSAuNSwgdmp1c3QgPSAtMS41LA0KICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIk1lcnJpd2VhdGhlciBTYW5zIg0KICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dCgNCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJMaWJyZSBCYXNrZXJ2aWxsZSINCiAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKHQ9IDEsIHIgPSAwLCBsID0gMCwgYiA9IDApLCAibGluZXMiKQ0KICAgICAgICAgICAgKSArDQogICAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgICAgeCA9ICIiLA0KICAgICAgICAgICAgICAgeSA9ICIiLA0KICAgICAgICAgICAgICAgdGl0bGUgPSAiQ2xpbWF0ZSBjaGFuZ2UgaW4gVmlldG5hbSIsDQogICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJNb250aGx5IHRvdGFsIHByZWNpcGl0YXRpb24gKDIwMTAgLSAyMDIwKSIsDQogICAgICAgICAgICAgICBjYXB0aW9uID0gIkF1dGhvcjogRHV5IEtoYW5oXG5Tb3VyY2U6IFdvcmxkQ2xpbSINCiAgICAgICAgICAgICkNCiAgICAgICAgIHByaW50KG1hcF92bikNCiAgICAgICAgIH0NCiAgICAgICkNCn0NCg0Kc2F2ZV9naWYobWFrZV9wbG90KCksICJkOi9SL3JwdWJzX3Byb2plY3QvdmlzdWFsaXphdGlvbi9tYXBfY2xpbWF0ZS1jaGFuZ2UtVk4vMDNfcGxvdHMvdm5fcHJlYy5naWYiLCANCiAgICAgICAgIGhlaWdodCA9IDExMDAsIHdpZHRoID0gMTEwMCwgZGVsYXkgPSAuNSwgcmVzID0gMTUwKSANCmBgYA0KDQo8IS0tICMjIEJ5IHByb3ZpbmNlIC0tPg0KDQo8IS0tICMjIyBSIENvZGVzIGZvciBkYXRhIHByb2Nlc3NpbmcgYW5kIHZpc3VhbGl6YXRpb24gLS0+DQoNCmBgYHtyIGVjaG89RkFMU0V9DQojIDEuIExvYWQgcGFja2FnZXMgLS0tLS0NCnBhY21hbjo6cF9sb2FkKA0KICAgZGF0YS50YWJsZSwgIyBkYXRhIHdyYW5nbGluZw0KICAgY29sbGFwc2UsICMgZGF0YSB3cmFnbGluZw0KICAgc2YsICMgZ2Vvc3BhdGlhbCBkYXRhIHdyYW5nbGluZw0KICAgdGVycmEsDQogICBzdHJpbmdyLA0KICAgbHVicmlkYXRlLA0KICAgIyBocmJydGhlbWVzLA0KICAgZ2dwbG90MiwgIyB2aXN1YWxpemF0aW9uDQogICBnZ2FuaW1hdGUsDQogICBnaWZza2kNCikNCg0KDQojIDIuIERhdGEgcHJvY2Vzc2luZyAtLS0tLQ0KIyMgMi4xIEdlb2dyYXBoaWMgZGF0YSAtLS0tLQ0KDQojIyMgMi4xLjEgSW1wb3J0IGRhdGEgLS0tLS0NCnN0X3JlYWQoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9nZW9ncmFwaHkvZ2FkbTQxX1ZOTV8xLnNocCIpIC0+IGdlb21fZHQNCg0KIyBWYXJuYW1lIHRvIGxvd2VyIGNhc2UNCnNldG5hbWVzKGdlb21fZHQsIG5ldyA9IHN0cl90b19sb3dlcihuYW1lcyhnZW9tX2R0KSkpDQoNCiMjIyAyLjEuMiBVbmlvbiBwcm92aW5jZXMgdG8gcmVnaW9ucyAtLS0tLQ0KDQojIEltcG9ydCBJRCBvZiBwcm92aW5jZXMNCnJlYWR4bDo6cmVhZF94bHN4KCJkOi9SL3JwdWJzX3Byb2plY3QvdmlzdWFsaXphdGlvbi9tYXBfY2xpbWF0ZS1jaGFuZ2UtVk4vMDBfcmF3X2RhdGEvZ2VvZ3JhcGh5L21hX3RpbmgueGxzeCIpIHw+IA0KICAgIyBtZXJnZSB3aXRoIGdlb21fZHQNCiAgIHFEVCgpIHw+IF9bZ2VvbV9kdCwgb24gPSAuKHByb3ZpbmNlID0gdmFybmFtZV8xKV1bLCAuKHJlZ2lvbiwgcHJvdmluY2UsIGdlb21ldHJ5KV0gLT4gZ2VvbV9kdA0KDQojIyAyLjIgQ0xpbWF0ZSBkYXRhIC0tLS0tDQoNCiMjIyAyLjIuMSBBdmVyYWdlIG1heGltdW0gdGVtcCAtLS0tLQ0KDQojIGNyZWF0ZSB2YXJuYW1lDQpjKGxpc3QuZmlsZXMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3RtYXhfMjAxMC0yMDE5LyIpLA0KICBsaXN0LmZpbGVzKCJkOi9SL3JwdWJzX3Byb2plY3QvdmlzdWFsaXphdGlvbi9tYXBfY2xpbWF0ZS1jaGFuZ2UtVk4vMDBfcmF3X2RhdGEvY2xpbWF0ZS93YzIuMV9jcnV0czQuMDZfMi41bV90bWF4XzIwMjAtMjAyMS8iKSkgfD4gDQogICBzdHJfZXh0cmFjdCgidG1heF9cXGR7NH0tXFxkXFxkIikgfD4gDQogICBzdHJfcmVwbGFjZSgiLSIsICJfIikgLT4gdG1heF92YXJuYW1lDQoNCiMgZXh0cmFjdCBjbGltYXRlIGRhdGEgZnJvbSByYXN0ZXIgdG8gdmVjdG9yDQpjKGxpc3QuZmlsZXMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3RtYXhfMjAxMC0yMDE5LyIsDQogICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpLA0KICBsaXN0LmZpbGVzKCJkOi9SL3JwdWJzX3Byb2plY3QvdmlzdWFsaXphdGlvbi9tYXBfY2xpbWF0ZS1jaGFuZ2UtVk4vMDBfcmF3X2RhdGEvY2xpbWF0ZS93YzIuMV9jcnV0czQuMDZfMi41bV90bWF4XzIwMjAtMjAyMS8iLA0KICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUKSkgfD4gDQogICBmdXR1cmUuYXBwbHk6OmZ1dHVyZV9sYXBwbHkoDQogICAgICBcKHJhc3RlcikgcmFzdChyYXN0ZXIpIHw+IA0KICAgICAgICAgIyBleHRyYWN0IHJhc3RlciBieSBwcm92aW5jZSB2ZWN0b3IgdXNpbmcgbWVhbiBmdW5jdGlvbg0KICAgICAgICAgZXhhY3RleHRyYWN0cjo6ZXhhY3RfZXh0cmFjdChzdF9hc19zZihnZW9tX2R0KSwgIm1lYW4iKQ0KICAgKSB8PiBzZXROYW1lcyh0bWF4X3Zhcm5hbWUpIHw+IGFzLmRhdGEudGFibGUoKSAtPiB0bWF4X2R0DQoNCmNiaW5kKGdlb21fZHQsIHRtYXhfZHQpIC0+IHRtYXhfZHQNCg0KIyMjIDIuMi4yIEF2ZXJhZ2UgbWluaW11bSB0ZW1wIC0tLS0tDQoNCiMgY3JlYXRlIHZhcm5hbWUNCmMobGlzdC5maWxlcygiZDovUi9ycHVic19wcm9qZWN0L3Zpc3VhbGl6YXRpb24vbWFwX2NsaW1hdGUtY2hhbmdlLVZOLzAwX3Jhd19kYXRhL2NsaW1hdGUvd2MyLjFfY3J1dHM0LjA2XzIuNW1fdG1pbl8yMDEwLTIwMTkvIiksDQogIGxpc3QuZmlsZXMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3RtaW5fMjAyMC0yMDIxLyIpKSB8PiANCiAgIHN0cl9leHRyYWN0KCJ0bWluX1xcZHs0fS1cXGRcXGQiKSB8PiANCiAgIHN0cl9yZXBsYWNlKCItIiwgIl8iKSAtPiB0bWluX3Zhcm5hbWUNCg0KIyBleHRyYWN0IGNsaW1hdGUgZGF0YSBmcm9tIHJhc3RlciB0byB2ZWN0b3INCmMobGlzdC5maWxlcygiZDovUi9ycHVic19wcm9qZWN0L3Zpc3VhbGl6YXRpb24vbWFwX2NsaW1hdGUtY2hhbmdlLVZOLzAwX3Jhd19kYXRhL2NsaW1hdGUvd2MyLjFfY3J1dHM0LjA2XzIuNW1fdG1pbl8yMDEwLTIwMTkvIiwNCiAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVCksDQogIGxpc3QuZmlsZXMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3RtaW5fMjAyMC0yMDIxLyIsDQogICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpKSB8PiANCiAgIGZ1dHVyZS5hcHBseTo6ZnV0dXJlX2xhcHBseSgNCiAgICAgIFwocmFzdGVyKSByYXN0KHJhc3RlcikgfD4gDQogICAgICAgICAjIGV4dHJhY3QgcmFzdGVyIGJ5IHByb3ZpbmNlIHZlY3RvciB1c2luZyBtZWFuIGZ1bmN0aW9uDQogICAgICAgICBleGFjdGV4dHJhY3RyOjpleGFjdF9leHRyYWN0KHN0X2FzX3NmKGdlb21fZHQpLCAibWVhbiIpDQogICApIHw+IHNldE5hbWVzKHRtaW5fdmFybmFtZSkgfD4gYXMuZGF0YS50YWJsZSgpIC0+IHRtaW5fZHQNCg0KY2JpbmQoZ2VvbV9kdCwgdG1pbl9kdCkgLT4gdG1pbl9kdA0KDQojIyMgMi4yLjIgQXZlcmFnZSBwcmVjIC0tLS0tDQojIGNyZWF0ZSB2YXJuYW1lDQpjKGxpc3QuZmlsZXMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3ByZWNfMjAxMC0yMDE5LyIpLA0KICBsaXN0LmZpbGVzKCJkOi9SL3JwdWJzX3Byb2plY3QvdmlzdWFsaXphdGlvbi9tYXBfY2xpbWF0ZS1jaGFuZ2UtVk4vMDBfcmF3X2RhdGEvY2xpbWF0ZS93YzIuMV9jcnV0czQuMDZfMi41bV9wcmVjXzIwMjAtMjAyMS8iKSkgfD4gDQogICBzdHJfZXh0cmFjdCgicHJlY19cXGR7NH0tXFxkXFxkIikgfD4gDQogICBzdHJfcmVwbGFjZSgiLSIsICJfIikgLT4gcHJlY192YXJuYW1lDQoNCiMgZXh0cmFjdCBjbGltYXRlIGRhdGEgZnJvbSByYXN0ZXIgdG8gdmVjdG9yDQpjKGxpc3QuZmlsZXMoImQ6L1IvcnB1YnNfcHJvamVjdC92aXN1YWxpemF0aW9uL21hcF9jbGltYXRlLWNoYW5nZS1WTi8wMF9yYXdfZGF0YS9jbGltYXRlL3djMi4xX2NydXRzNC4wNl8yLjVtX3ByZWNfMjAxMC0yMDE5LyIsDQogICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpLA0KICBsaXN0LmZpbGVzKCJkOi9SL3JwdWJzX3Byb2plY3QvdmlzdWFsaXphdGlvbi9tYXBfY2xpbWF0ZS1jaGFuZ2UtVk4vMDBfcmF3X2RhdGEvY2xpbWF0ZS93YzIuMV9jcnV0czQuMDZfMi41bV9wcmVjXzIwMjAtMjAyMS8iLA0KICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUKSkgfD4gDQogICBmdXR1cmUuYXBwbHk6OmZ1dHVyZV9sYXBwbHkoDQogICAgICBcKHJhc3RlcikgcmFzdChyYXN0ZXIpIHw+IA0KICAgICAgICAgIyBleHRyYWN0IHJhc3RlciBieSBwcm92aW5jZSB2ZWN0b3IgdXNpbmcgbWVhbiBmdW5jdGlvbg0KICAgICAgICAgZXhhY3RleHRyYWN0cjo6ZXhhY3RfZXh0cmFjdChzdF9hc19zZihnZW9tX2R0KSwgIm1lYW4iKQ0KICAgKSB8PiBzZXROYW1lcyhwcmVjX3Zhcm5hbWUpIHw+IGFzLmRhdGEudGFibGUoKSAtPiBwcmVjX2R0DQoNCmNiaW5kKGdlb21fZHQsIHByZWNfZHQpIC0+IHByZWNfZHQNCg0KIyAzLiBEYXRhIHZpc3VhbGl6YXRpb24gLS0tLS0NCiMjIDMuMSBBdmVyYWdlIG1heGltdW0gdGVtcCAtLS0tLQ0KDQp0bWF4X2R0IHw+IG1lbHQoaWQudmFycyA9IGMoInJlZ2lvbiIsICJwcm92aW5jZSIpLCANCiAgICAgICAgICAgICAgICBtZWFzdXJlLnZhcnMgPSB0bWF4X3Zhcm5hbWUsIA0KICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAidGltZSIpIHw+IA0KICAgX1ssIHRpbWUgOj0gc3RyX3JlbW92ZSh0aW1lLCAidG1heF8iKSB8PiANCiAgICAgICAgc3RyX3JlcGxhY2UoIl8iLCAiLSIpIHw+IHN0cl9jKCItMDEiKSB8PiBhcy5JRGF0ZSgpXVtdIC0+IHRtYXhfZHRfd2lkZV90b19sb25nDQoNCnRtYXhfZHRfd2lkZV90b19sb25nIHw+IA0KICAgX1ssIHJlZ2lvbiA6PSBmYWN0b3IocmVnaW9uKV0gfD4gDQogICBfW29yZGVyKHRpbWUsIHJlZ2lvbiwgdmFsdWUpXSB8Pg0KICAgX1ssIHJhbmsgOj0gMTouTiwgYnkgPSB0aW1lXSB8PiANCiAgIF9bLCBtZWFuX2J5X3RpbWUgOj0gbWVhbih2YWx1ZSksIHRpbWVdIC0+IA0KICAgdG1heF9kdF93aWRlX3RvX2xvbmcNCg0KdG1heF9kdF93aWRlX3RvX2xvbmdbXQ0KDQoNCiMgdG1heF9kdF93aWRlX3RvX2xvbmdbdGltZSA9PSAiMjAxMC0wMS0wMSJdIHw+DQp0bWF4X2R0X3dpZGVfdG9fbG9uZyB8Pg0KICAgZ2dwbG90KGFlcyh5ID0gcmFuaywgeCA9IHZhbHVlKSkgKw0KICAgZ2VvbV9wb2ludChzaXplID0gMiwgYWVzKGNvbG9yID0gcmVnaW9uKSkgKw0KICAgIyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gd2VzYW5kZXJzb246Ondlc19wYWxldHRlKCJJc2xlb2ZEb2dzMiIpKQ0KICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lYW5fYnlfdGltZSksIGNvbG9yID0gInRvbWF0byIpICsNCiAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDYsIDQwKSwNCiAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDEwLCAyMCwgMzApKSArDQogICBnZW9tX3NlZ21lbnQoYWVzKHkgPSByYW5rLCB5ZW5kID0gcmFuaywgeCA9IDEwLCB4ZW5kID0gdmFsdWUpLCBjb2xvciA9ICJncmV5IikgKyANCiAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwcm92aW5jZSksIGhqdXN0ID0gInJpZ2h0IiwgeCA9IDEwLCBzaXplID0gMi41KSArDQogICBsYWJzKGZpbGwgPSBOVUxMKSArDQogICB4bGFiKCIiKSArIHlsYWIoIiIpICsNCiAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikgIC0+IA0KICAgcF9zdGF0aWMNCg0KcF9zdGF0aWMNCg0KcF9zdGF0aWMgKw0KICAgIyBsYWJzKHRpdGxlID0gIntwYXN0ZSh5ZWFyKHltZChcIjIwMTAtMDEtMDFcIikgKyBmcmFtZV90aW1lLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBGQUxTRSksIG1vbnRoKHltZChcIjIwMTAtMDEtMDFcIikgKyBmcmFtZV90aW1lKSl9IikgKw0KICAgdHJhbnNpdGlvbl90aW1lKHRpbWUpIC0+ICANCiAgIHAuZHluYW1pYw0KDQphbmltYXRlKHAuZHluYW1pYywgbmZyYW1lcyA9IDE0NCAqIDQsDQogICAgICAgICAgICAgICAgICAgc3RhcnRfcGF1c2UgPSAxNSwgZW5kX3BhdXNlID0gMTUsIA0KICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDcxNSwgd2lkdGggPSA2MDApDQoNCg0KDQp0bWF4X2R0X3dpZGVfdG9fbG9uZyB8PiANCiAgIGdncGxvdCgpICsNCiAgIGFlcyh4bWluID0gMTAsIHhtYXggPSAzNikgKw0KICAgYWVzKHltaW4gPSByYW5rIC0gLjQ1LA0KICAgICAgIHltYXggPSByYW5rICsgLjQ1LA0KICAgICAgIHkgPSByYW5rKSArDQogICBmYWNldF93cmFwKH4gdGltZSkgKw0KICAgZ2VvbV9yZWN0KGFscGhhID0gLjcpICsNCiAgIGFlcyhmaWxsID0gcmVnaW9uKSArDQogICBzY2FsZV9maWxsX3ZpcmlkaXNfZChvcHRpb24gPSAiaW5mZXJubyIsIGRpcmVjdGlvbiA9IC0xKSArDQogICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygtMTAsIDQwKSwNCiAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsIDEwLCAyMCwgMzApKSArDQogICBnZW9tX3RleHQoY29sID0gImdyYXkxMyIsDQogICAgICAgICAgICAgaGp1c3QgPSAicmlnaHQiLA0KICAgICAgICAgICAgIGFlcyhsYWJlbCA9IHByb3ZpbmNlKSwNCiAgICAgICAgICAgICB4ID0gLTUwKSArDQogICBzY2FsZV95X3JldmVyc2UoKSArDQogICBsYWJzKGZpbGwgPSBOVUxMKSArICANCiAgIGxhYnMoeCA9ICdNb250aGx5IHRlbXBlcmF0dXJlIG92ZXIgdGhlIHBhc3QgZGVjYWRlIChjZWxjaXVzIGRlZ3JlZSknKSArICANCiAgIGxhYnMoeSA9ICIiKSArICANCiAgIG15X3RoZW1lIC0+IHAuc3RhdGljDQogICANCnAuc3RhdGljICsNCiAgIGZhY2V0X251bGwoKQ0KDQpgYGANCg==