Load the readr, ggplot2, and dplyr packages
library(tidyverse) library(readr) library(ggplot2) library(dplyr) # Read datasets/confirmed_cases_worldwide.csv into confirmed_cases_worldwide confirmed_cases_worldwide <- read_csv(“datasets/confirmed_cases_worldwide.csv”)
See the result
confirmed_cases_worldwide
library(testthat) library(IRkernel.testthat)
soln_confirmed_cases_worldwide <- read_csv(“datasets/confirmed_cases_worldwide.csv”)
run_tests({ test_that(“readr is loaded”, { expect_true( “readr” %in% .packages(), info = “Did you load the readr package?” ) }) test_that(“ggplot2 is loaded”, { expect_true( “ggplot2” %in% .packages(), info = “Did you load the ggplot2 package?” ) }) test_that(“dplyr is loaded”, { expect_true( “dplyr” %in% .packages(), info = “Did you load the dplyr package?” ) })
test_that("confirmed_cases_worldwide is a data.frame", {
expect_s3_class(
confirmed_cases_worldwide,
"data.frame",
)
})
test_that("confirmed_cases_worldwide has the correct column", {
expect_identical(
colnames(confirmed_cases_worldwide),
colnames(soln_confirmed_cases_worldwide),
info = "The column names of the `confirmed_cases_worldwide` data frame do not correspond with the ones in the CSV file: `\"datasets/confirmed_cases_worldwide.csv\"`."
)
})
test_that("has the correct data", {
expect_equal(
confirmed_cases_worldwide,
soln_confirmed_cases_worldwide,
info = "The data of the `confirmed_cases_worldwide` data frame do not correspond with data in the CSV file: \"datasets/confirmed_cases_worldwide.csv\"."
)
})
})
Draw a line plot of cumulative cases vs. date
Label the y-axis
ggplot(confirmed_cases_worldwide, aes(x = date, y = cum_cases)) + geom_line() + ylab(“Cumulative confirmed cases”)
run_tests({ plot <- last_plot() test_that(“the plot is created”, { expect_false( is.null(plot), info = “Could not find a plot created with ggplot().” ) }) test_that(“the plot uses the correct data”, { expect_equal( plot\(data, confirmed_cases_worldwide, info = "The dataset used in the last plot is not `confirmed_cases_worldwide`." ) }) test_that("the plot uses the correct x aesthetic", { expect_equal( quo_name(plot\)mapping\(x), "date", info = "The x aesthetic used in the last plot is not `date`." ) }) test_that("the plot uses the correct y aesthetic", { expect_equal( quo_name(plot\)mapping\(y), "cum_cases", info = "The y aesthetic used in the last plot is not `cum_cases`." ) }) test_that("the plot uses the correct geom", { expect_true( "GeomLine" %in% class(plot\)layers[[1]]\(geom), info = "The geom used in the last plot is not `geom_line()`." ) }) test_that("the plot uses the correct y label", { expect_equal( plot\)labels$y, “Cumulative confirmed cases”, info = “The y label used in the last plot is not \"Cumulative confirmed cases\".” ) }) })
Read in datasets/confirmed_cases_china_vs_world.csv
confirmed_cases_china_vs_world <- read_csv(“datasets/confirmed_cases_china_vs_world.csv”)
See the result
glimpse(confirmed_cases_china_vs_world)
Draw a line plot of cumulative cases vs. date, grouped and colored by is_china
Define aesthetics within the line geom
plt_cum_confirmed_cases_china_vs_world <- ggplot(confirmed_cases_china_vs_world) + geom_line(aes(x = date, y = cum_cases, group = is_china, color = is_china)) + ylab(“Cumulative confirmed cases”)
See the plot
plt_cum_confirmed_cases_china_vs_world
soln_confirmed_cases_china_vs_world <- read_csv(“datasets/confirmed_cases_china_vs_world.csv”)
run_tests({ test_that(“confirmed_cases_china_vs_world is a data.frame”, { expect_s3_class( confirmed_cases_china_vs_world, “data.frame” ) }) test_that(“confirmed_cases_china_vs_world has the correct column names”, { expect_identical( colnames(confirmed_cases_china_vs_world), colnames(soln_confirmed_cases_china_vs_world), info = “The column names of the confirmed_cases_china_vs_world data frame do not correspond with the ones in the CSV file: \"datasets/confirmed_cases_china_vs_world.csv\".” ) }) test_that(“confirmed_cases_china_vs_world has the correct data”, { expect_equal( confirmed_cases_china_vs_world, soln_confirmed_cases_china_vs_world, info = “The data of the confirmed_cases_china_vs_world data frame do not correspond with data in the CSV file: "datasets/confirmed_cases_china_vs_world.csv".” ) }) # NOTE: glimpse is not tested. Can this be done? test_that(“plt_cum_confirmed_cases_china_vs_world is not NULL”, { expect_false( is.null(plt_cum_confirmed_cases_china_vs_world), info = “plt_cum_confirmed_cases_china_vs_world is NULL.” ) }) test_that(“plt_cum_confirmed_cases_china_vs_world is a plot”, { expect_true( “ggplot” %in% class(plt_cum_confirmed_cases_china_vs_world), info = “plt_cum_confirmed_cases_china_vs_world is not a ggplot() object.” ) }) test_that(“plt_cum_confirmed_cases_china_vs_world uses the correct data”, { expect_equal( plt_cum_confirmed_cases_china_vs_world\(data, confirmed_cases_china_vs_world, info = "The dataset used in `plt_cum_confirmed_cases_china_vs_world` is not `confirmed_cases_china_vs_world`." ) }) layer <- plt_cum_confirmed_cases_china_vs_world\)layers[[1]] test_that(“plt_cum_confirmed_cases_china_vs_world uses uses the correct geom”, { expect_false( is.null(layer), info = “The geom used in plt_cum_confirmed_cases_china_vs_world is not geom_line().” ) }) test_that(“plt_cum_confirmed_cases_china_vs_world uses uses the correct geom”, { expect_true( “GeomLine” %in% class(layer\(geom), info = "The geom used in `plt_cum_confirmed_cases_china_vs_world` is not `geom_line()`." ) }) test_that("plt_cum_confirmed_cases_china_vs_world uses uses the correct x aesthetic", { expect_equal( quo_name(layer\)mapping\(x), "date", info = "The x aesthetic used in `plt_cum_confirmed_cases_china_vs_world` is not `date`." ) }) test_that("plt_cum_confirmed_cases_china_vs_world uses uses the correct y aesthetic", { expect_equal( quo_name(layer\)mapping\(y), "cum_cases", info = "The y aesthetic used in `plt_cum_confirmed_cases_china_vs_world` is not `cum_cases`." ) }) test_that("plt_cum_confirmed_cases_china_vs_world uses uses the correct colour aesthetic", { expect_equal( quo_name(layer\)mapping\(colour), "is_china", info = "The colour aesthetic used in `plt_cum_confirmed_cases_china_vs_world` is not `is_china`." ) }) test_that("plt_cum_confirmed_cases_china_vs_world uses uses the correct group aesthetic", { expect_equal( quo_name(layer\)mapping$group), “is_china”, info = “The group aesthetic used in plt_cum_confirmed_cases_china_vs_world is not is_china.” ) }) })
- who_events <- tribble(
- date, ~ event, “2020-01-30”, “Global healthdeclared”, “2020-03-11”, “Pandemic”, “2020-02-13”, “China reporting” ) %>% mutate(date = as.Date(date))
Using who_events, add vertical dashed lines with an xintercept at date
and text at date, labeled by event, and at 100000 on the y-axis
plt_cum_confirmed_cases_china_vs_world + geom_vline(data = who_events, aes(xintercept = date), linetype = ‘dashed’) + geom_text(data = who_events, aes(x = date,label = event), y = 1e5)
run_tests({ plot <- last_plot() test_that(“the plot got created”, { expect_false( is.null(plot), info = “Could not find a plot created with ggplot().” ) }) layer1 <- plot\(layers[[2]] layer2 <- plot\)layers[[3]] test_that(“the plot has both geoms”, { expect_false( is.null(layer1) || is.null(layer2), info = “Could not fin geom_vline() and geom_text() in your last plot.” ) }) test_that(“the plot has both geoms”, { expect_true( “GeomVline” %in% class(layer1\(geom) && "GeomText" %in% class(layer2\)geom) || “GeomText” %in% class(layer1\(geom) && "GeomVline" %in% class(layer2\)geom), info = “Could not fin geom_vline() and geom_text() in your last plot.” ) }) if (“GeomVline” %in% class(layer1\(geom)) { vline <- layer1 text <- layer2 } else { vline <- layer2 text <- layer1 } test_that("the plot uses the correct data", { expect_equal( vline\)data, who_events, info = “The dataset used in the geom_vline() is not who_events.” ) }) test_that(“the geom uses the correct xintercept aesthetic”, { expect_equal( quo_name(vline\(mapping\)xintercept), “date”, info = “The xintercept aesthetic used in the geom_vline() is not date.” ) }) test_that(“the geom uses the correct lintype parameter”, { expect_equal( vline\(aes_params\)linetype, “dashed”, info = “The linetype parameter used in the geom_vline() is not \"dashed\".” ) }) test_that(“the geom uses the correct data”, { expect_equal( text\(data, who_events, info = "The dataset used in the `geom_text()` is not `who_events`." ) }) test_that("the geom uses the correct x aesthetic", { expect_equal( quo_name(text\)mapping\(x), "date", info = "The x aesthetic used in the `geom_text()` is not `date`." ) }) test_that("the geom uses the correct label aesthetic", { expect_equal( quo_name(text\)mapping\(label), "event", info = "The label aesthetic used in the `geom_text()` is not `event`." ) }) test_that("the geom uses the correct y parameter", { expect_equal( text\)aes_params$y, 100000, info = “The y parameter used in the geom_text() is not 1e5.” ) }) })
Filter for China, from Feb 15
china_after_feb15 <- confirmed_cases_china_vs_world %>% filter(is_china==“China”,date >= “2020-02-15”)
Using china_after_feb15, draw a line plot cum_cases vs. date
Add a smooth trend line using linear regression, no error bars
ggplot(china_after_feb15, aes(x= date, y=cum_cases)) + geom_line()+ geom_smooth(method=“lm”, se = FALSE) + ylab(“Cumulative confirmed cases”)
run_tests({ test_that(“the data is filtered correctly”, { soln_china_after_feb15 <- confirmed_cases_china_vs_world %>% filter(is_china == “China”, date >= “2020-02-15”) expect_equivalent( soln_china_after_feb15, china_after_feb15, info = “china_after_feb15 has not been filtered correctly.” ) }) plot <- last_plot() test_that(“the plot is created”, { expect_false( is.null(plot), info = “Could not find a plot created with ggplot().” ) }) test_that(“the plot uses the correct data”, { expect_equal( plot\(data, china_after_feb15, info = "The dataset used in the last plot is not `soln_china_after_feb15`." ) }) test_that("the plot uses the correct x aesthetic", { expect_equal( quo_name(plot\)mapping\(x), "date", info = "The x aesthetic used in the last plot is not `date`." ) }) test_that("the plot uses the correct y aesthetic", { expect_equal( quo_name(plot\)mapping\(y), "cum_cases", info = "The y aesthetic used in the last plot is not `cum_cases`." ) }) layer1 <- plot\)layers[[1]] layer2 <- plot\(layers[[2]] test_that("the plot has the correct geoms", { expect_false( is.null(layer1) || is.null(layer2), info = "Could not fin `geom_line()` and `geom_smooth()` in your last plot." ) }) test_that("the plot has the correct geoms", { expect_true( "GeomLine" %in% class(layer1\)geom) && “GeomSmooth” %in% class(layer2\(geom) || "GeomSmooth" %in% class(layer1\)geom) && “GeomLine” %in% class(layer2\(geom), info = "Could not fin `geom_line()` and `geom_smooth()` in your last plot." ) }) if ("GeomLine" %in% class(layer1\)geom)) { line <- layer1 smooth <- layer2 } else { line <- layer2 smooth <- layer1 } test_that(“the geom has the correct method parameter”, { expect_equal( smooth\(stat_params\)method, “lm”, info = “The method parameter used in the geom_smooth() is not \"lm\".”
)
})
test_that("the geom has the correct se parameter", {
expect_equal(
smooth$stat_params$se,
FALSE,
info = "The se parameter used in the `geom_smooth()` is not `\"FALSE\"`."
)
})
})
Filter confirmed_cases_china_vs_world for not China
not_china <- confirmed_cases_china_vs_world %>% filter(is_china == “Not China”)
Using not_china, draw a line plot cum_cases vs. date
Add a smooth trend line using linear regression, no error bars
plt_not_china_trend_lin <- ggplot(not_china, aes(x= date, y=cum_cases)) + geom_line()+ geom_smooth(method=“lm”, se = FALSE) + ylab(“Cumulative confirmed cases”)
See the result
plt_not_china_trend_lin
run_tests({ test_that(“the data is filtered correctly”, { soln_not_china <- confirmed_cases_china_vs_world %>% filter(is_china == “Not China”) expect_equal( soln_not_china, not_china, info = “not_china has not been filtered correctly.” ) }) plot <- last_plot() test_that(“the plot is created”, { expect_false( is.null(plot), info = “Could not find a plot created with ggplot().” ) }) test_that(“the plot uses the correct data”, { expect_equal( plot\(data, not_china, info = "The dataset used in the last plot is not `not_china`." ) }) test_that("the plot uses the correct x aesthetic", { expect_equal( quo_name(plot\)mapping\(x), "date", info = "The x aesthetic used in the last plot is not `date`." ) }) test_that("the plot uses the correct y aesthetic", { expect_equal( quo_name(plot\)mapping\(y), "cum_cases", info = "The y aesthetic used in the last plot is not `cum_cases`." ) }) layer1 <- plot\)layers[[1]] layer2 <- plot\(layers[[2]] test_that("the plot uses the correct geoms", { expect_false( is.null(layer1) || is.null(layer2), info = "Could not fin `geom_line()` and `geom_smooth()` in your last plot." ) }) test_that("the plot uses the correct geoms", { expect_true( "GeomLine" %in% class(layer1\)geom) && “GeomSmooth” %in% class(layer2\(geom) || "GeomSmooth" %in% class(layer1\)geom) && “GeomLine” %in% class(layer2\(geom), info = "Could not fin `geom_line()` and `geom_smooth()` in your last plot." ) }) if ("GeomLine" %in% class(layer1\)geom)) { line <- layer1 smooth <- layer2 } else { line <- layer2 smooth <- layer1 } test_that(“the geom uses the correct method parameter”, { expect_equal( smooth\(stat_params\)method, “lm”, info = “The method parameter used in the geom_smooth() is not \"lm\".” ) }) test_that(“the geom uses the correct se parameter”, { expect_equal( smooth\(stat_params\)se, FALSE, info = “The se parameter used in the geom_smooth() is not \"FALSE\".” ) }) })
Modify the plot to use a logarithmic scale on the y-axis
plt_not_china_trend_lin + scale_y_log10()
run_tests({ plot <- last_plot() test_that(“the plot is created”, { expect_false( is.null(plot), info = “Could not find a plot created with ggplot().” ) }) scale <- plot\(scales\)get_scales(aes(“y”)) test_that(“the plot has a scale”, { expect_false( is.null(scale), info = “Could not find a scale in your last plot.” ) }) test_that(“the plot uses the correct scale”, { expect_equal( scale\(trans\)name, “log-10”, info = “Could not find a logarithmic y scale: scale_y_log10().” ) }) })
Run this to get the data for each country
confirmed_cases_by_country <- read_csv(“datasets/confirmed_cases_by_country.csv”) glimpse(confirmed_cases_by_country)
Group by country, summarize to calculate total cases, find the top 7
top_countries_by_total_cases <- confirmed_cases_by_country %>% group_by(country) %>% summarize(total_cases= max(cum_cases))%>% top_n(7)
See the result
top_countries_by_total_cases
run_tests({ test_that(“the data is manipulated correctly”, { soln_top_countries_by_total_cases <- confirmed_cases_by_country %>% group_by(country) %>% summarize(total_cases = max(cum_cases)) %>% top_n(7, total_cases) expect_equivalent( soln_top_countries_by_total_cases, top_countries_by_total_cases, info = “top_countries_by_total_cases has not been filtered correctly.” ) }) })
Run this to get the data for the top 7 countries
confirmed_cases_top7_outside_china <- read_csv(“datasets/confirmed_cases_top7_outside_china.csv”)
glimpse(confirmed_cases_top7_outside_china)
Using confirmed_cases_top7_outside_china, draw a line plot of
cum_cases vs. date, grouped and colored by country
ggplot(confirmed_cases_top7_outside_china, aes(x=date, y= cum_cases, group= country, color = country))+geom_line()+ylab(“Cumulative confirmed cases”)
soln_confirmed_cases_top7_outside_china <- read_csv(“datasets/confirmed_cases_top7_outside_china.csv”)
run_tests({ test_that(‘confirmed_cases_top7_outside_china is a data.frame’, { expect_s3_class( confirmed_cases_top7_outside_china, ‘data.frame’ ) }) test_that(‘confirmed_cases_top7_outside_china had the correct column names’, { expect_identical( colnames(confirmed_cases_top7_outside_china), colnames(soln_confirmed_cases_top7_outside_china), info = “The column names of the confirmed_cases_top7_outside_china data frame do not correspond with the ones in the CSV file: \"datasets/confirmed_cases_top7_outside_china.csv\".” ) }) test_that(‘confirmed_cases_top7_outside_china had the correct data’, { expect_equal( confirmed_cases_top7_outside_china, soln_confirmed_cases_top7_outside_china, info = “The data of the confirmed_cases_top7_outside_china data frame do not correspond with data in the CSV file: "datasets/confirmed_cases_top7_outside_china.csv".” ) }) # NOTE: glimpse is not tested. Can this be done? plot <- last_plot() test_that(‘the plot is created’, { expect_false( is.null(plot), info = “Could not find a plot created with ggplot().” ) }) test_that(‘the plot uses the correct data’, { expect_equal( plot\(data, confirmed_cases_top7_outside_china, info = "The dataset used in the last plot is not `not_china`." ) }) line <- plot\)layers[[1]] test_that(‘the plot uses the correct geom’, { expect_false( is.null(line), info = “Could not fin geom_line() in your last plot.” ) }) test_that(‘the plot uses the correct geom’, { expect_true( ‘GeomLine’ %in% class(line\(geom), info = "Could not fin `geom_line()` in your last plot." ) }) mapping <- plot\)mapping geom_mapping <- line\(mapping test_that('the plot uses the correct x aesthetic', { expect_true( !is.null(mapping\)x) && quo_name(mapping\(x) == "date" || !is.null(geom_mapping\)x) && quo_name(geom_mapping$x) == “date”, info = “The x aesthetic used in the last plot is not date.”
)
})
test_that('the plot uses the correct y aesthetic', {
expect_true(
!is.null(mapping$y) && quo_name(mapping$y) == "cum_cases" ||
!is.null(geom_mapping$y) && quo_name(geom_mapping$y) == "cum_cases",
info = "The y aesthetic used in the last plot is not `cum_cases`."
)
})
test_that('the plot uses the correct color aesthetic', {
expect_true(
!is.null(mapping$colour) && quo_name(mapping$colour) == "country" ||
!is.null(geom_mapping$colour) && quo_name(geom_mapping$colour) == "country",
info = "The colour aesthetic used in the last plot is not `country`."
)
})
test_that('the plot uses the correct group aesthetic', {
expect_true(
!is.null(mapping$group) && quo_name(mapping$group) == "country" ||
!is.null(geom_mapping$group) && quo_name(geom_mapping$group) == "country",
info = "The group aesthetic used in the last plot is not `country`."
)
})
test_that('the plot uses the correct y label', {
expect_equal(
plot$labels$y,
"Cumulative confirmed cases",
info = "The y label used in the last plot is not `\"Cumulative confirmed cases\"`."
)
})
})
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBMb2FkIHRoZSByZWFkciwgZ2dwbG90MiwgYW5kIGRwbHlyIHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCiMgUmVhZCBkYXRhc2V0cy9jb25maXJtZWRfY2FzZXNfd29ybGR3aWRlLmNzdiBpbnRvIGNvbmZpcm1lZF9jYXNlc193b3JsZHdpZGUKY29uZmlybWVkX2Nhc2VzX3dvcmxkd2lkZSA8LSByZWFkX2NzdigiZGF0YXNldHMvY29uZmlybWVkX2Nhc2VzX3dvcmxkd2lkZS5jc3YiKQogIAoKIyBTZWUgdGhlIHJlc3VsdApjb25maXJtZWRfY2FzZXNfd29ybGR3aWRlCgpsaWJyYXJ5KHRlc3R0aGF0KSAKbGlicmFyeShJUmtlcm5lbC50ZXN0dGhhdCkKCnNvbG5fY29uZmlybWVkX2Nhc2VzX3dvcmxkd2lkZSA8LSByZWFkX2NzdigiZGF0YXNldHMvY29uZmlybWVkX2Nhc2VzX3dvcmxkd2lkZS5jc3YiKQoKcnVuX3Rlc3RzKHsKICAgIHRlc3RfdGhhdCgicmVhZHIgaXMgbG9hZGVkIiwgewogICAgICAgIGV4cGVjdF90cnVlKAogICAgICAgICAgICAicmVhZHIiICVpbiUgLnBhY2thZ2VzKCksIAogICAgICAgICAgICBpbmZvID0gIkRpZCB5b3UgbG9hZCB0aGUgYHJlYWRyYCBwYWNrYWdlPyIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJnZ3Bsb3QyIGlzIGxvYWRlZCIsIHsKICAgICAgICBleHBlY3RfdHJ1ZSgKICAgICAgICAgICAgImdncGxvdDIiICVpbiUgLnBhY2thZ2VzKCksIAogICAgICAgICAgICBpbmZvID0gIkRpZCB5b3UgbG9hZCB0aGUgYGdncGxvdDJgIHBhY2thZ2U/IgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoImRwbHlyIGlzIGxvYWRlZCIsIHsKICAgICAgICBleHBlY3RfdHJ1ZSgKICAgICAgICAgICAgImRwbHlyIiAlaW4lIC5wYWNrYWdlcygpLCAKICAgICAgICAgICAgaW5mbyA9ICJEaWQgeW91IGxvYWQgdGhlIGBkcGx5cmAgcGFja2FnZT8iCiAgICAgICAgKQogICAgfSkKICAgIAogICAgdGVzdF90aGF0KCJjb25maXJtZWRfY2FzZXNfd29ybGR3aWRlIGlzIGEgZGF0YS5mcmFtZSIsIHsKICAgICAgICBleHBlY3RfczNfY2xhc3MoCiAgICAgICAgICAgIGNvbmZpcm1lZF9jYXNlc193b3JsZHdpZGUsCiAgICAgICAgICAgICJkYXRhLmZyYW1lIiwKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJjb25maXJtZWRfY2FzZXNfd29ybGR3aWRlIGhhcyB0aGUgY29ycmVjdCBjb2x1bW4iLCB7CiAgICAgICAgZXhwZWN0X2lkZW50aWNhbCgKICAgICAgICAgICAgY29sbmFtZXMoY29uZmlybWVkX2Nhc2VzX3dvcmxkd2lkZSksCiAgICAgICAgICAgIGNvbG5hbWVzKHNvbG5fY29uZmlybWVkX2Nhc2VzX3dvcmxkd2lkZSksIAogICAgICAgICAgICBpbmZvID0gIlRoZSBjb2x1bW4gbmFtZXMgb2YgdGhlIGBjb25maXJtZWRfY2FzZXNfd29ybGR3aWRlYCBkYXRhIGZyYW1lIGRvIG5vdCBjb3JyZXNwb25kIHdpdGggdGhlIG9uZXMgaW4gdGhlIENTViBmaWxlOiBgXCJkYXRhc2V0cy9jb25maXJtZWRfY2FzZXNfd29ybGR3aWRlLmNzdlwiYC4iCiAgICAgICAgKSAKICAgIH0pCiAgICB0ZXN0X3RoYXQoImhhcyB0aGUgY29ycmVjdCBkYXRhIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgY29uZmlybWVkX2Nhc2VzX3dvcmxkd2lkZSwKICAgICAgICAgICAgc29sbl9jb25maXJtZWRfY2FzZXNfd29ybGR3aWRlLCAKICAgICAgICAgICAgaW5mbyA9ICJUaGUgZGF0YSBvZiB0aGUgYGNvbmZpcm1lZF9jYXNlc193b3JsZHdpZGVgIGRhdGEgZnJhbWUgZG8gbm90IGNvcnJlc3BvbmQgd2l0aCBkYXRhIGluIHRoZSBDU1YgZmlsZTogXCJkYXRhc2V0cy9jb25maXJtZWRfY2FzZXNfd29ybGR3aWRlLmNzdlwiLiIKICAgICAgICApCiAgICB9KQp9KQoKIyBEcmF3IGEgbGluZSBwbG90IG9mIGN1bXVsYXRpdmUgY2FzZXMgdnMuIGRhdGUKIyBMYWJlbCB0aGUgeS1heGlzCmdncGxvdChjb25maXJtZWRfY2FzZXNfd29ybGR3aWRlLCBhZXMoeCA9IGRhdGUsIHkgPSBjdW1fY2FzZXMpKSArCiAgZ2VvbV9saW5lKCkgKwogIHlsYWIoIkN1bXVsYXRpdmUgY29uZmlybWVkIGNhc2VzIikKCnJ1bl90ZXN0cyh7CiAgICBwbG90IDwtIGxhc3RfcGxvdCgpCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IGlzIGNyZWF0ZWQiLCB7CiAgICAgICAgZXhwZWN0X2ZhbHNlKAogICAgICAgICAgICBpcy5udWxsKHBsb3QpLAogICAgICAgICAgICBpbmZvID0gIkNvdWxkIG5vdCBmaW5kIGEgcGxvdCBjcmVhdGVkIHdpdGggYGdncGxvdCgpYC4iCiAgICAgICAgKQogICAgfSkKICAgIHRlc3RfdGhhdCgidGhlIHBsb3QgdXNlcyB0aGUgY29ycmVjdCBkYXRhIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgcGxvdCRkYXRhLAogICAgICAgICAgICBjb25maXJtZWRfY2FzZXNfd29ybGR3aWRlLAogICAgICAgICAgICBpbmZvID0gIlRoZSBkYXRhc2V0IHVzZWQgaW4gdGhlIGxhc3QgcGxvdCBpcyBub3QgYGNvbmZpcm1lZF9jYXNlc193b3JsZHdpZGVgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IHggYWVzdGhldGljIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgcXVvX25hbWUocGxvdCRtYXBwaW5nJHgpLAogICAgICAgICAgICAiZGF0ZSIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIHggYWVzdGhldGljIHVzZWQgaW4gdGhlIGxhc3QgcGxvdCBpcyBub3QgYGRhdGVgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IHkgYWVzdGhldGljIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgcXVvX25hbWUocGxvdCRtYXBwaW5nJHkpLAogICAgICAgICAgICAiY3VtX2Nhc2VzIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgeSBhZXN0aGV0aWMgdXNlZCBpbiB0aGUgbGFzdCBwbG90IGlzIG5vdCBgY3VtX2Nhc2VzYC4iCiAgICAgICAgKQogICAgfSkKICAgIHRlc3RfdGhhdCgidGhlIHBsb3QgdXNlcyB0aGUgY29ycmVjdCBnZW9tIiwgewogICAgICAgIGV4cGVjdF90cnVlKAogICAgICAgICAgICAiR2VvbUxpbmUiICVpbiUgY2xhc3MocGxvdCRsYXllcnNbWzFdXSRnZW9tKSwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgZ2VvbSB1c2VkIGluIHRoZSBsYXN0IHBsb3QgaXMgbm90IGBnZW9tX2xpbmUoKWAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IHVzZXMgdGhlIGNvcnJlY3QgeSBsYWJlbCIsIHsKICAgICAgICBleHBlY3RfZXF1YWwoCiAgICAgICAgICAgIHBsb3QkbGFiZWxzJHksCiAgICAgICAgICAgICJDdW11bGF0aXZlIGNvbmZpcm1lZCBjYXNlcyIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIHkgbGFiZWwgdXNlZCBpbiB0aGUgbGFzdCBwbG90IGlzIG5vdCBgXCJDdW11bGF0aXZlIGNvbmZpcm1lZCBjYXNlc1wiYC4iCiAgICAgICAgKQogICAgfSkKfSkKCiMgUmVhZCBpbiBkYXRhc2V0cy9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQuY3N2CmNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCA8LSByZWFkX2NzdigiZGF0YXNldHMvY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkLmNzdiIpCgojIFNlZSB0aGUgcmVzdWx0CmdsaW1wc2UoY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkKQoKCgojIERyYXcgYSBsaW5lIHBsb3Qgb2YgY3VtdWxhdGl2ZSBjYXNlcyB2cy4gZGF0ZSwgZ3JvdXBlZCBhbmQgY29sb3JlZCBieSBpc19jaGluYQojIERlZmluZSBhZXN0aGV0aWNzIHdpdGhpbiB0aGUgbGluZSBnZW9tCnBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkIDwtIGdncGxvdChjb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQpICsKICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLCB5ID0gY3VtX2Nhc2VzLCBncm91cCA9IGlzX2NoaW5hLCBjb2xvciA9IGlzX2NoaW5hKSkgKwogIHlsYWIoIkN1bXVsYXRpdmUgY29uZmlybWVkIGNhc2VzIikKIAoKIyBTZWUgdGhlIHBsb3QKcGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQKCnNvbG5fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkIDwtIHJlYWRfY3N2KCJkYXRhc2V0cy9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQuY3N2IikKCnJ1bl90ZXN0cyh7CiAgICB0ZXN0X3RoYXQoImNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCBpcyBhIGRhdGEuZnJhbWUiLCB7CiAgICAgICAgZXhwZWN0X3MzX2NsYXNzKAogICAgICAgICAgICBjb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQsCiAgICAgICAgICAgICJkYXRhLmZyYW1lIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoImNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCBoYXMgdGhlIGNvcnJlY3QgY29sdW1uIG5hbWVzIiwgewogICAgICAgIGV4cGVjdF9pZGVudGljYWwoCiAgICAgICAgICAgIGNvbG5hbWVzKGNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCksCiAgICAgICAgICAgIGNvbG5hbWVzKHNvbG5fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkKSwgCiAgICAgICAgICAgIGluZm8gPSAiVGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgYGNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZGAgZGF0YSBmcmFtZSBkbyBub3QgY29ycmVzcG9uZCB3aXRoIHRoZSBvbmVzIGluIHRoZSBDU1YgZmlsZTogYFwiZGF0YXNldHMvY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkLmNzdlwiYC4iCiAgICAgICAgKSAKICAgIH0pCiAgICB0ZXN0X3RoYXQoImNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCBoYXMgdGhlIGNvcnJlY3QgZGF0YSIsIHsKICAgICAgICBleHBlY3RfZXF1YWwoCiAgICAgICAgICAgIGNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCwKICAgICAgICAgICAgc29sbl9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQsIAogICAgICAgICAgICBpbmZvID0gIlRoZSBkYXRhIG9mIHRoZSBgY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkYCBkYXRhIGZyYW1lIGRvIG5vdCBjb3JyZXNwb25kIHdpdGggZGF0YSBpbiB0aGUgQ1NWIGZpbGU6IFwiZGF0YXNldHMvY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkLmNzdlwiLiIKICAgICAgICApCiAgICB9KQogICAgIyBOT1RFOiBnbGltcHNlIGlzIG5vdCB0ZXN0ZWQuIENhbiB0aGlzIGJlIGRvbmU/CiAgICB0ZXN0X3RoYXQoInBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkIGlzIG5vdCBOVUxMIiwgewogICAgICAgIGV4cGVjdF9mYWxzZSgKICAgICAgICAgICAgaXMubnVsbChwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCksCiAgICAgICAgICAgIGluZm8gPSAiYHBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkYCBpcyBOVUxMLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCBpcyBhIHBsb3QiLCB7CiAgICAgICAgZXhwZWN0X3RydWUoCiAgICAgICAgICAgICJnZ3Bsb3QiICVpbiUgY2xhc3MocGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQpLAogICAgICAgICAgICBpbmZvID0gImBwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZGAgaXMgbm90IGEgYGdncGxvdCgpYCBvYmplY3QuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkIHVzZXMgdGhlIGNvcnJlY3QgZGF0YSIsIHsKICAgICAgICBleHBlY3RfZXF1YWwoCiAgICAgICAgICAgIHBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkJGRhdGEsCiAgICAgICAgICAgIGNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgZGF0YXNldCB1c2VkIGluIGBwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZGAgaXMgbm90IGBjb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGRgLiIKICAgICAgICApCiAgICB9KQogICAgbGF5ZXIgPC0gcGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQkbGF5ZXJzW1sxXV0KICAgIHRlc3RfdGhhdCgicGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQgdXNlcyB1c2VzIHRoZSBjb3JyZWN0IGdlb20iLCB7CiAgICAgICAgZXhwZWN0X2ZhbHNlKAogICAgICAgICAgICBpcy5udWxsKGxheWVyKSwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgZ2VvbSB1c2VkIGluIGBwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZGAgaXMgbm90IGBnZW9tX2xpbmUoKWAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkIHVzZXMgdXNlcyB0aGUgY29ycmVjdCBnZW9tIiwgewogICAgICAgIGV4cGVjdF90cnVlKAogICAgICAgICAgICAiR2VvbUxpbmUiICVpbiUgY2xhc3MobGF5ZXIkZ2VvbSksCiAgICAgICAgICAgIGluZm8gPSAiVGhlIGdlb20gdXNlZCBpbiBgcGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGRgIGlzIG5vdCBgZ2VvbV9saW5lKClgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCB1c2VzIHVzZXMgdGhlIGNvcnJlY3QgeCBhZXN0aGV0aWMiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBxdW9fbmFtZShsYXllciRtYXBwaW5nJHgpLAogICAgICAgICAgICAiZGF0ZSIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIHggYWVzdGhldGljIHVzZWQgaW4gYHBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkYCBpcyBub3QgYGRhdGVgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCB1c2VzIHVzZXMgdGhlIGNvcnJlY3QgeSBhZXN0aGV0aWMiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBxdW9fbmFtZShsYXllciRtYXBwaW5nJHkpLAogICAgICAgICAgICAiY3VtX2Nhc2VzIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgeSBhZXN0aGV0aWMgdXNlZCBpbiBgcGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGRgIGlzIG5vdCBgY3VtX2Nhc2VzYC4iCiAgICAgICAgKQogICAgfSkKICAgIHRlc3RfdGhhdCgicGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQgdXNlcyB1c2VzIHRoZSBjb3JyZWN0IGNvbG91ciBhZXN0aGV0aWMiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBxdW9fbmFtZShsYXllciRtYXBwaW5nJGNvbG91ciksCiAgICAgICAgICAgICJpc19jaGluYSIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIGNvbG91ciBhZXN0aGV0aWMgdXNlZCBpbiBgcGx0X2N1bV9jb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGRgIGlzIG5vdCBgaXNfY2hpbmFgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCB1c2VzIHVzZXMgdGhlIGNvcnJlY3QgZ3JvdXAgYWVzdGhldGljIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgcXVvX25hbWUobGF5ZXIkbWFwcGluZyRncm91cCksCiAgICAgICAgICAgICJpc19jaGluYSIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIGdyb3VwIGFlc3RoZXRpYyB1c2VkIGluIGBwbHRfY3VtX2NvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZGAgaXMgbm90IGBpc19jaGluYWAuIgogICAgICAgICkKICAgIH0pCn0pCgp3aG9fZXZlbnRzIDwtIHRyaWJibGUoCiAgfiBkYXRlLCB+IGV2ZW50LAogICIyMDIwLTAxLTMwIiwgIkdsb2JhbCBoZWFsdGhcbmVtZXJnZW5jeSBkZWNsYXJlZCIsCiAgIjIwMjAtMDMtMTEiLCAiUGFuZGVtaWNcbmRlY2xhcmVkIiwKICAiMjAyMC0wMi0xMyIsICJDaGluYSByZXBvcnRpbmdcbmNoYW5nZSIKKSAlPiUKICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZSkpCgojIFVzaW5nIHdob19ldmVudHMsIGFkZCB2ZXJ0aWNhbCBkYXNoZWQgbGluZXMgd2l0aCBhbiB4aW50ZXJjZXB0IGF0IGRhdGUKIyBhbmQgdGV4dCBhdCBkYXRlLCBsYWJlbGVkIGJ5IGV2ZW50LCBhbmQgYXQgMTAwMDAwIG9uIHRoZSB5LWF4aXMKIHBsdF9jdW1fY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkICsKICBnZW9tX3ZsaW5lKGRhdGEgPSB3aG9fZXZlbnRzLCBhZXMoeGludGVyY2VwdCA9IGRhdGUpLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSB3aG9fZXZlbnRzLCBhZXMoeCA9IGRhdGUsbGFiZWwgPSBldmVudCksIHkgPSAxZTUpCgpydW5fdGVzdHMoewogICAgcGxvdCA8LSBsYXN0X3Bsb3QoKQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCBnb3QgY3JlYXRlZCIsIHsKICAgICAgICBleHBlY3RfZmFsc2UoCiAgICAgICAgICAgIGlzLm51bGwocGxvdCksCiAgICAgICAgICAgIGluZm8gPSAiQ291bGQgbm90IGZpbmQgYSBwbG90IGNyZWF0ZWQgd2l0aCBgZ2dwbG90KClgLiIKICAgICAgICApCiAgICB9KQogICAgbGF5ZXIxIDwtIHBsb3QkbGF5ZXJzW1syXV0KICAgIGxheWVyMiA8LSBwbG90JGxheWVyc1tbM11dCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IGhhcyBib3RoIGdlb21zIiwgewogICAgICAgIGV4cGVjdF9mYWxzZSgKICAgICAgICAgICAgaXMubnVsbChsYXllcjEpIHx8IGlzLm51bGwobGF5ZXIyKSwKICAgICAgICAgICAgaW5mbyA9ICJDb3VsZCBub3QgZmluIGBnZW9tX3ZsaW5lKClgIGFuZCBgZ2VvbV90ZXh0KClgIGluIHlvdXIgbGFzdCBwbG90LiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCBoYXMgYm90aCBnZW9tcyIsIHsKICAgICAgICBleHBlY3RfdHJ1ZSgKICAgICAgICAgICAgIkdlb21WbGluZSIgJWluJSBjbGFzcyhsYXllcjEkZ2VvbSkgJiYgIkdlb21UZXh0IiAlaW4lIGNsYXNzKGxheWVyMiRnZW9tKSB8fAogICAgICAgICAgICAiR2VvbVRleHQiICVpbiUgY2xhc3MobGF5ZXIxJGdlb20pICYmICJHZW9tVmxpbmUiICVpbiUgY2xhc3MobGF5ZXIyJGdlb20pLAogICAgICAgICAgICBpbmZvID0gIkNvdWxkIG5vdCBmaW4gYGdlb21fdmxpbmUoKWAgYW5kIGBnZW9tX3RleHQoKWAgaW4geW91ciBsYXN0IHBsb3QuIgogICAgICAgICkKICAgIH0pCiAgICBpZiAoIkdlb21WbGluZSIgJWluJSBjbGFzcyhsYXllcjEkZ2VvbSkpIHsKICAgICAgICB2bGluZSA8LSBsYXllcjEKICAgICAgICB0ZXh0IDwtIGxheWVyMgogICAgfSBlbHNlIHsKICAgICAgICB2bGluZSA8LSBsYXllcjIKICAgICAgICB0ZXh0IDwtIGxheWVyMQogICAgfQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IGRhdGEiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICB2bGluZSRkYXRhLAogICAgICAgICAgICB3aG9fZXZlbnRzLAogICAgICAgICAgICBpbmZvID0gIlRoZSBkYXRhc2V0IHVzZWQgaW4gdGhlIGBnZW9tX3ZsaW5lKClgIGlzIG5vdCBgd2hvX2V2ZW50c2AuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBnZW9tIHVzZXMgdGhlIGNvcnJlY3QgeGludGVyY2VwdCBhZXN0aGV0aWMiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBxdW9fbmFtZSh2bGluZSRtYXBwaW5nJHhpbnRlcmNlcHQpLAogICAgICAgICAgICAiZGF0ZSIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIHhpbnRlcmNlcHQgYWVzdGhldGljIHVzZWQgaW4gdGhlIGBnZW9tX3ZsaW5lKClgIGlzIG5vdCBgZGF0ZWAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBnZW9tIHVzZXMgdGhlIGNvcnJlY3QgbGludHlwZSBwYXJhbWV0ZXIiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICB2bGluZSRhZXNfcGFyYW1zJGxpbmV0eXBlLAogICAgICAgICAgICAiZGFzaGVkIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgbGluZXR5cGUgcGFyYW1ldGVyIHVzZWQgaW4gdGhlIGBnZW9tX3ZsaW5lKClgIGlzIG5vdCBgXCJkYXNoZWRcImAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBnZW9tIHVzZXMgdGhlIGNvcnJlY3QgZGF0YSIsIHsKICAgICAgICBleHBlY3RfZXF1YWwoCiAgICAgICAgICAgIHRleHQkZGF0YSwKICAgICAgICAgICAgd2hvX2V2ZW50cywKICAgICAgICAgICAgaW5mbyA9ICJUaGUgZGF0YXNldCB1c2VkIGluIHRoZSBgZ2VvbV90ZXh0KClgIGlzIG5vdCBgd2hvX2V2ZW50c2AuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBnZW9tIHVzZXMgdGhlIGNvcnJlY3QgeCBhZXN0aGV0aWMiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBxdW9fbmFtZSh0ZXh0JG1hcHBpbmckeCksCiAgICAgICAgICAgICJkYXRlIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgeCBhZXN0aGV0aWMgdXNlZCBpbiB0aGUgYGdlb21fdGV4dCgpYCBpcyBub3QgYGRhdGVgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgZ2VvbSB1c2VzIHRoZSBjb3JyZWN0IGxhYmVsIGFlc3RoZXRpYyIsIHsKICAgICAgICBleHBlY3RfZXF1YWwoCiAgICAgICAgICAgIHF1b19uYW1lKHRleHQkbWFwcGluZyRsYWJlbCksCiAgICAgICAgICAgICJldmVudCIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIGxhYmVsIGFlc3RoZXRpYyB1c2VkIGluIHRoZSBgZ2VvbV90ZXh0KClgIGlzIG5vdCBgZXZlbnRgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgZ2VvbSB1c2VzIHRoZSBjb3JyZWN0IHkgcGFyYW1ldGVyIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgdGV4dCRhZXNfcGFyYW1zJHksCiAgICAgICAgICAgIDEwMDAwMCwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgeSBwYXJhbWV0ZXIgdXNlZCBpbiB0aGUgYGdlb21fdGV4dCgpYCBpcyBub3QgYDFlNWAuIgogICAgICAgICkKICAgIH0pCn0pCgojIEZpbHRlciBmb3IgQ2hpbmEsIGZyb20gRmViIDE1CmNoaW5hX2FmdGVyX2ZlYjE1IDwtIGNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCAlPiUKICBmaWx0ZXIoaXNfY2hpbmE9PSJDaGluYSIsZGF0ZSA+PSAiMjAyMC0wMi0xNSIpCgojIFVzaW5nIGNoaW5hX2FmdGVyX2ZlYjE1LCBkcmF3IGEgbGluZSBwbG90IGN1bV9jYXNlcyB2cy4gZGF0ZQojIEFkZCBhIHNtb290aCB0cmVuZCBsaW5lIHVzaW5nIGxpbmVhciByZWdyZXNzaW9uLCBubyBlcnJvciBiYXJzCmdncGxvdChjaGluYV9hZnRlcl9mZWIxNSwgYWVzKHg9IGRhdGUsIHk9Y3VtX2Nhc2VzKSkgKwpnZW9tX2xpbmUoKSsgCmdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZSA9IEZBTFNFKSArIAp5bGFiKCJDdW11bGF0aXZlIGNvbmZpcm1lZCBjYXNlcyIpCgpydW5fdGVzdHMoewogICAgdGVzdF90aGF0KCJ0aGUgZGF0YSBpcyBmaWx0ZXJlZCBjb3JyZWN0bHkiLCB7CiAgICAgICAgc29sbl9jaGluYV9hZnRlcl9mZWIxNSA8LSBjb25maXJtZWRfY2FzZXNfY2hpbmFfdnNfd29ybGQgJT4lCiAgICAgICAgICBmaWx0ZXIoaXNfY2hpbmEgPT0gIkNoaW5hIiwgZGF0ZSA+PSAiMjAyMC0wMi0xNSIpCiAgICAgICAgZXhwZWN0X2VxdWl2YWxlbnQoCiAgICAgICAgICAgIHNvbG5fY2hpbmFfYWZ0ZXJfZmViMTUsCiAgICAgICAgICAgIGNoaW5hX2FmdGVyX2ZlYjE1LAogICAgICAgICAgICBpbmZvID0gImBjaGluYV9hZnRlcl9mZWIxNWAgaGFzIG5vdCBiZWVuIGZpbHRlcmVkIGNvcnJlY3RseS4iCiAgICAgICAgKQogICAgfSkKICAgIHBsb3QgPC0gbGFzdF9wbG90KCkKICAgIHRlc3RfdGhhdCgidGhlIHBsb3QgaXMgY3JlYXRlZCIsIHsKICAgICAgICBleHBlY3RfZmFsc2UoCiAgICAgICAgICAgIGlzLm51bGwocGxvdCksCiAgICAgICAgICAgIGluZm8gPSAiQ291bGQgbm90IGZpbmQgYSBwbG90IGNyZWF0ZWQgd2l0aCBgZ2dwbG90KClgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IGRhdGEiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBwbG90JGRhdGEsCiAgICAgICAgICAgIGNoaW5hX2FmdGVyX2ZlYjE1LAogICAgICAgICAgICBpbmZvID0gIlRoZSBkYXRhc2V0IHVzZWQgaW4gdGhlIGxhc3QgcGxvdCBpcyBub3QgYHNvbG5fY2hpbmFfYWZ0ZXJfZmViMTVgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IHggYWVzdGhldGljIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgcXVvX25hbWUocGxvdCRtYXBwaW5nJHgpLAogICAgICAgICAgICAiZGF0ZSIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIHggYWVzdGhldGljIHVzZWQgaW4gdGhlIGxhc3QgcGxvdCBpcyBub3QgYGRhdGVgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IHkgYWVzdGhldGljIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgcXVvX25hbWUocGxvdCRtYXBwaW5nJHkpLAogICAgICAgICAgICAiY3VtX2Nhc2VzIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgeSBhZXN0aGV0aWMgdXNlZCBpbiB0aGUgbGFzdCBwbG90IGlzIG5vdCBgY3VtX2Nhc2VzYC4iCiAgICAgICAgKQogICAgfSkKICAgIGxheWVyMSA8LSBwbG90JGxheWVyc1tbMV1dCiAgICBsYXllcjIgPC0gcGxvdCRsYXllcnNbWzJdXQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCBoYXMgdGhlIGNvcnJlY3QgZ2VvbXMiLCB7CiAgICAgICAgZXhwZWN0X2ZhbHNlKAogICAgICAgICAgICBpcy5udWxsKGxheWVyMSkgfHwgaXMubnVsbChsYXllcjIpLAogICAgICAgICAgICBpbmZvID0gIkNvdWxkIG5vdCBmaW4gYGdlb21fbGluZSgpYCBhbmQgYGdlb21fc21vb3RoKClgIGluIHlvdXIgbGFzdCBwbG90LiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCBoYXMgdGhlIGNvcnJlY3QgZ2VvbXMiLCB7CiAgICAgICAgZXhwZWN0X3RydWUoCiAgICAgICAgICAgICJHZW9tTGluZSIgJWluJSBjbGFzcyhsYXllcjEkZ2VvbSkgJiYgIkdlb21TbW9vdGgiICVpbiUgY2xhc3MobGF5ZXIyJGdlb20pIHx8CiAgICAgICAgICAgICJHZW9tU21vb3RoIiAlaW4lIGNsYXNzKGxheWVyMSRnZW9tKSAmJiAiR2VvbUxpbmUiICVpbiUgY2xhc3MobGF5ZXIyJGdlb20pLAogICAgICAgICAgICBpbmZvID0gIkNvdWxkIG5vdCBmaW4gYGdlb21fbGluZSgpYCBhbmQgYGdlb21fc21vb3RoKClgIGluIHlvdXIgbGFzdCBwbG90LiIKICAgICAgICApCiAgICB9KQogICAgaWYgKCJHZW9tTGluZSIgJWluJSBjbGFzcyhsYXllcjEkZ2VvbSkpIHsKICAgICAgICBsaW5lIDwtIGxheWVyMQogICAgICAgIHNtb290aCA8LSBsYXllcjIKICAgIH0gZWxzZSB7CiAgICAgICAgbGluZSA8LSBsYXllcjIKICAgICAgICBzbW9vdGggPC0gbGF5ZXIxCiAgICB9CiAgICB0ZXN0X3RoYXQoInRoZSBnZW9tIGhhcyB0aGUgY29ycmVjdCBtZXRob2QgcGFyYW1ldGVyIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgc21vb3RoJHN0YXRfcGFyYW1zJG1ldGhvZCwKICAgICAgICAgICAgImxtIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgbWV0aG9kIHBhcmFtZXRlciB1c2VkIGluIHRoZSBgZ2VvbV9zbW9vdGgoKWAgaXMgbm90IGBcImxtXCJgLiIKCiAgICAgICAgKQogICAgfSkKICAgIHRlc3RfdGhhdCgidGhlIGdlb20gaGFzIHRoZSBjb3JyZWN0IHNlIHBhcmFtZXRlciIsIHsKICAgICAgICBleHBlY3RfZXF1YWwoCiAgICAgICAgICAgIHNtb290aCRzdGF0X3BhcmFtcyRzZSwKICAgICAgICAgICAgRkFMU0UsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIHNlIHBhcmFtZXRlciB1c2VkIGluIHRoZSBgZ2VvbV9zbW9vdGgoKWAgaXMgbm90IGBcIkZBTFNFXCJgLiIKICAgICAgICApCiAgICB9KQp9KQoKIyBGaWx0ZXIgY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkIGZvciBub3QgQ2hpbmEKbm90X2NoaW5hIDwtIGNvbmZpcm1lZF9jYXNlc19jaGluYV92c193b3JsZCAlPiUKICBmaWx0ZXIoaXNfY2hpbmEgPT0gIk5vdCBDaGluYSIpCgojIFVzaW5nIG5vdF9jaGluYSwgZHJhdyBhIGxpbmUgcGxvdCBjdW1fY2FzZXMgdnMuIGRhdGUKIyBBZGQgYSBzbW9vdGggdHJlbmQgbGluZSB1c2luZyBsaW5lYXIgcmVncmVzc2lvbiwgbm8gZXJyb3IgYmFycwpwbHRfbm90X2NoaW5hX3RyZW5kX2xpbiA8LSBnZ3Bsb3Qobm90X2NoaW5hLCBhZXMoeD0gZGF0ZSwgeT1jdW1fY2FzZXMpKSArCmdlb21fbGluZSgpKyAKZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlID0gRkFMU0UpICsgCnlsYWIoIkN1bXVsYXRpdmUgY29uZmlybWVkIGNhc2VzIikKCiMgU2VlIHRoZSByZXN1bHQKcGx0X25vdF9jaGluYV90cmVuZF9saW4gCgpydW5fdGVzdHMoewogICAgdGVzdF90aGF0KCJ0aGUgZGF0YSBpcyBmaWx0ZXJlZCBjb3JyZWN0bHkiLCB7CiAgICAgICAgc29sbl9ub3RfY2hpbmEgPC0gY29uZmlybWVkX2Nhc2VzX2NoaW5hX3ZzX3dvcmxkICU+JQogICAgICAgICAgZmlsdGVyKGlzX2NoaW5hID09ICJOb3QgQ2hpbmEiKQogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgc29sbl9ub3RfY2hpbmEsCiAgICAgICAgICAgIG5vdF9jaGluYSwKICAgICAgICAgICAgaW5mbyA9ICJgbm90X2NoaW5hYCBoYXMgbm90IGJlZW4gZmlsdGVyZWQgY29ycmVjdGx5LiIKICAgICAgICApCiAgICB9KQogICAgcGxvdCA8LSBsYXN0X3Bsb3QoKQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCBpcyBjcmVhdGVkIiwgewogICAgICAgIGV4cGVjdF9mYWxzZSgKICAgICAgICAgICAgaXMubnVsbChwbG90KSwKICAgICAgICAgICAgaW5mbyA9ICJDb3VsZCBub3QgZmluZCBhIHBsb3QgY3JlYXRlZCB3aXRoIGBnZ3Bsb3QoKWAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IHVzZXMgdGhlIGNvcnJlY3QgZGF0YSIsIHsKICAgICAgICBleHBlY3RfZXF1YWwoCiAgICAgICAgICAgIHBsb3QkZGF0YSwKICAgICAgICAgICAgbm90X2NoaW5hLAogICAgICAgICAgICBpbmZvID0gIlRoZSBkYXRhc2V0IHVzZWQgaW4gdGhlIGxhc3QgcGxvdCBpcyBub3QgYG5vdF9jaGluYWAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IHVzZXMgdGhlIGNvcnJlY3QgeCBhZXN0aGV0aWMiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBxdW9fbmFtZShwbG90JG1hcHBpbmckeCksCiAgICAgICAgICAgICJkYXRlIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgeCBhZXN0aGV0aWMgdXNlZCBpbiB0aGUgbGFzdCBwbG90IGlzIG5vdCBgZGF0ZWAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IHVzZXMgdGhlIGNvcnJlY3QgeSBhZXN0aGV0aWMiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBxdW9fbmFtZShwbG90JG1hcHBpbmckeSksCiAgICAgICAgICAgICJjdW1fY2FzZXMiLAogICAgICAgICAgICBpbmZvID0gIlRoZSB5IGFlc3RoZXRpYyB1c2VkIGluIHRoZSBsYXN0IHBsb3QgaXMgbm90IGBjdW1fY2FzZXNgLiIKICAgICAgICApCiAgICB9KQogICAgbGF5ZXIxIDwtIHBsb3QkbGF5ZXJzW1sxXV0KICAgIGxheWVyMiA8LSBwbG90JGxheWVyc1tbMl1dCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IHVzZXMgdGhlIGNvcnJlY3QgZ2VvbXMiLCB7CiAgICAgICAgZXhwZWN0X2ZhbHNlKAogICAgICAgICAgICBpcy5udWxsKGxheWVyMSkgfHwgaXMubnVsbChsYXllcjIpLAogICAgICAgICAgICBpbmZvID0gIkNvdWxkIG5vdCBmaW4gYGdlb21fbGluZSgpYCBhbmQgYGdlb21fc21vb3RoKClgIGluIHlvdXIgbGFzdCBwbG90LiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IGdlb21zIiwgewogICAgICAgIGV4cGVjdF90cnVlKAogICAgICAgICAgICAiR2VvbUxpbmUiICVpbiUgY2xhc3MobGF5ZXIxJGdlb20pICYmICJHZW9tU21vb3RoIiAlaW4lIGNsYXNzKGxheWVyMiRnZW9tKSB8fAogICAgICAgICAgICAiR2VvbVNtb290aCIgJWluJSBjbGFzcyhsYXllcjEkZ2VvbSkgJiYgIkdlb21MaW5lIiAlaW4lIGNsYXNzKGxheWVyMiRnZW9tKSwKICAgICAgICAgICAgaW5mbyA9ICJDb3VsZCBub3QgZmluIGBnZW9tX2xpbmUoKWAgYW5kIGBnZW9tX3Ntb290aCgpYCBpbiB5b3VyIGxhc3QgcGxvdC4iCiAgICAgICAgKQogICAgfSkKICAgIGlmICgiR2VvbUxpbmUiICVpbiUgY2xhc3MobGF5ZXIxJGdlb20pKSB7CiAgICAgICAgbGluZSA8LSBsYXllcjEKICAgICAgICBzbW9vdGggPC0gbGF5ZXIyCiAgICB9IGVsc2UgewogICAgICAgIGxpbmUgPC0gbGF5ZXIyCiAgICAgICAgc21vb3RoIDwtIGxheWVyMQogICAgfQogICAgdGVzdF90aGF0KCJ0aGUgZ2VvbSB1c2VzIHRoZSBjb3JyZWN0IG1ldGhvZCBwYXJhbWV0ZXIiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBzbW9vdGgkc3RhdF9wYXJhbXMkbWV0aG9kLAogICAgICAgICAgICAibG0iLAogICAgICAgICAgICBpbmZvID0gIlRoZSBtZXRob2QgcGFyYW1ldGVyIHVzZWQgaW4gdGhlIGBnZW9tX3Ntb290aCgpYCBpcyBub3QgYFwibG1cImAuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBnZW9tIHVzZXMgdGhlIGNvcnJlY3Qgc2UgcGFyYW1ldGVyIiwgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgc21vb3RoJHN0YXRfcGFyYW1zJHNlLAogICAgICAgICAgICBGQUxTRSwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgc2UgcGFyYW1ldGVyIHVzZWQgaW4gdGhlIGBnZW9tX3Ntb290aCgpYCBpcyBub3QgYFwiRkFMU0VcImAuIgogICAgICAgICkKICAgIH0pCn0pCgojIE1vZGlmeSB0aGUgcGxvdCB0byB1c2UgYSBsb2dhcml0aG1pYyBzY2FsZSBvbiB0aGUgeS1heGlzCnBsdF9ub3RfY2hpbmFfdHJlbmRfbGluICsgc2NhbGVfeV9sb2cxMCgpCgpydW5fdGVzdHMoewogICAgcGxvdCA8LSBsYXN0X3Bsb3QoKQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCBpcyBjcmVhdGVkIiwgewogICAgICAgIGV4cGVjdF9mYWxzZSgKICAgICAgICAgICAgaXMubnVsbChwbG90KSwKICAgICAgICAgICAgaW5mbyA9ICJDb3VsZCBub3QgZmluZCBhIHBsb3QgY3JlYXRlZCB3aXRoIGBnZ3Bsb3QoKWAuIgogICAgICAgICkKICAgIH0pCiAgICBzY2FsZSA8LSBwbG90JHNjYWxlcyRnZXRfc2NhbGVzKGFlcygieSIpKQogICAgdGVzdF90aGF0KCJ0aGUgcGxvdCBoYXMgYSBzY2FsZSIsIHsKICAgICAgICBleHBlY3RfZmFsc2UoCiAgICAgICAgICAgIGlzLm51bGwoc2NhbGUpLAogICAgICAgICAgICBpbmZvID0gIkNvdWxkIG5vdCBmaW5kIGEgc2NhbGUgaW4geW91ciBsYXN0IHBsb3QuIgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoInRoZSBwbG90IHVzZXMgdGhlIGNvcnJlY3Qgc2NhbGUiLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBzY2FsZSR0cmFucyRuYW1lLAogICAgICAgICAgICAibG9nLTEwIiwKICAgICAgICAgICAgaW5mbyA9ICJDb3VsZCBub3QgZmluZCBhIGxvZ2FyaXRobWljIHkgc2NhbGU6IGBzY2FsZV95X2xvZzEwKClgLiIKICAgICAgICApCiAgICB9KQp9KQoKIyBSdW4gdGhpcyB0byBnZXQgdGhlIGRhdGEgZm9yIGVhY2ggY291bnRyeQpjb25maXJtZWRfY2FzZXNfYnlfY291bnRyeSA8LSByZWFkX2NzdigiZGF0YXNldHMvY29uZmlybWVkX2Nhc2VzX2J5X2NvdW50cnkuY3N2IikKZ2xpbXBzZShjb25maXJtZWRfY2FzZXNfYnlfY291bnRyeSkKCiMgR3JvdXAgYnkgY291bnRyeSwgc3VtbWFyaXplIHRvIGNhbGN1bGF0ZSB0b3RhbCBjYXNlcywgZmluZCB0aGUgdG9wIDcKdG9wX2NvdW50cmllc19ieV90b3RhbF9jYXNlcyA8LSBjb25maXJtZWRfY2FzZXNfYnlfY291bnRyeSAlPiUKICBncm91cF9ieShjb3VudHJ5KSAlPiUgc3VtbWFyaXplKHRvdGFsX2Nhc2VzPSBtYXgoY3VtX2Nhc2VzKSklPiUgdG9wX24oNykKCiMgU2VlIHRoZSByZXN1bHQKdG9wX2NvdW50cmllc19ieV90b3RhbF9jYXNlcwoKcnVuX3Rlc3RzKHsKICAgIHRlc3RfdGhhdCgidGhlIGRhdGEgaXMgbWFuaXB1bGF0ZWQgY29ycmVjdGx5IiwgewogICAgICAgIHNvbG5fdG9wX2NvdW50cmllc19ieV90b3RhbF9jYXNlcyA8LSBjb25maXJtZWRfY2FzZXNfYnlfY291bnRyeSAlPiUKICAgICAgICAgIGdyb3VwX2J5KGNvdW50cnkpICU+JQogICAgICAgICAgc3VtbWFyaXplKHRvdGFsX2Nhc2VzID0gbWF4KGN1bV9jYXNlcykpICU+JQogICAgICAgICAgdG9wX24oNywgdG90YWxfY2FzZXMpCiAgICAgICAgZXhwZWN0X2VxdWl2YWxlbnQoCiAgICAgICAgICAgIHNvbG5fdG9wX2NvdW50cmllc19ieV90b3RhbF9jYXNlcywKICAgICAgICAgICAgdG9wX2NvdW50cmllc19ieV90b3RhbF9jYXNlcywKICAgICAgICAgICAgaW5mbyA9ICJgdG9wX2NvdW50cmllc19ieV90b3RhbF9jYXNlc2AgaGFzIG5vdCBiZWVuIGZpbHRlcmVkIGNvcnJlY3RseS4iCiAgICAgICAgKQogICAgfSkKfSkKCiMgUnVuIHRoaXMgdG8gZ2V0IHRoZSBkYXRhIGZvciB0aGUgdG9wIDcgY291bnRyaWVzCmNvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEgPC0gcmVhZF9jc3YoImRhdGFzZXRzL2NvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEuY3N2IikKCiMgCmdsaW1wc2UoY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYSkKCiMgVXNpbmcgY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYSwgZHJhdyBhIGxpbmUgcGxvdCBvZgojIGN1bV9jYXNlcyB2cy4gZGF0ZSwgZ3JvdXBlZCBhbmQgY29sb3JlZCBieSBjb3VudHJ5CmdncGxvdChjb25maXJtZWRfY2FzZXNfdG9wN19vdXRzaWRlX2NoaW5hLCBhZXMoeD1kYXRlLCB5PSBjdW1fY2FzZXMsIGdyb3VwPSBjb3VudHJ5LCBjb2xvciA9IGNvdW50cnkpKStnZW9tX2xpbmUoKSt5bGFiKCJDdW11bGF0aXZlIGNvbmZpcm1lZCBjYXNlcyIpCgoKCgpzb2xuX2NvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEgPC0gcmVhZF9jc3YoImRhdGFzZXRzL2NvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEuY3N2IikKCnJ1bl90ZXN0cyh7CiAgICB0ZXN0X3RoYXQoJ2NvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEgaXMgYSBkYXRhLmZyYW1lJywgewogICAgICAgIGV4cGVjdF9zM19jbGFzcygKICAgICAgICAgICAgY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYSwKICAgICAgICAgICAgJ2RhdGEuZnJhbWUnCiAgICAgICAgKQogICAgfSkKICAgIHRlc3RfdGhhdCgnY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYSBoYWQgdGhlIGNvcnJlY3QgY29sdW1uIG5hbWVzJywgewogICAgICAgIGV4cGVjdF9pZGVudGljYWwoCiAgICAgICAgICAgIGNvbG5hbWVzKGNvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEpLAogICAgICAgICAgICBjb2xuYW1lcyhzb2xuX2NvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEpLCAKICAgICAgICAgICAgaW5mbyA9ICJUaGUgY29sdW1uIG5hbWVzIG9mIHRoZSBgY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYWAgZGF0YSBmcmFtZSBkbyBub3QgY29ycmVzcG9uZCB3aXRoIHRoZSBvbmVzIGluIHRoZSBDU1YgZmlsZTogYFwiZGF0YXNldHMvY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYS5jc3ZcImAuIgogICAgICAgICkgCiAgICB9KQogICAgdGVzdF90aGF0KCdjb25maXJtZWRfY2FzZXNfdG9wN19vdXRzaWRlX2NoaW5hIGhhZCB0aGUgY29ycmVjdCBkYXRhJywgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYSwKICAgICAgICAgICAgc29sbl9jb25maXJtZWRfY2FzZXNfdG9wN19vdXRzaWRlX2NoaW5hLAogICAgICAgICAgICBpbmZvID0gIlRoZSBkYXRhIG9mIHRoZSBgY29uZmlybWVkX2Nhc2VzX3RvcDdfb3V0c2lkZV9jaGluYWAgZGF0YSBmcmFtZSBkbyBub3QgY29ycmVzcG9uZCB3aXRoIGRhdGEgaW4gdGhlIENTViBmaWxlOiBcImRhdGFzZXRzL2NvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEuY3N2XCIuIgogICAgICAgICkKICAgIH0pCiAgICAjIE5PVEU6IGdsaW1wc2UgaXMgbm90IHRlc3RlZC4gQ2FuIHRoaXMgYmUgZG9uZT8KICAgIHBsb3QgPC0gbGFzdF9wbG90KCkKICAgIHRlc3RfdGhhdCgndGhlIHBsb3QgaXMgY3JlYXRlZCcsIHsKICAgICAgICBleHBlY3RfZmFsc2UoCiAgICAgICAgICAgIGlzLm51bGwocGxvdCksCiAgICAgICAgICAgIGluZm8gPSAiQ291bGQgbm90IGZpbmQgYSBwbG90IGNyZWF0ZWQgd2l0aCBgZ2dwbG90KClgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCd0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IGRhdGEnLCB7CiAgICAgICAgZXhwZWN0X2VxdWFsKAogICAgICAgICAgICBwbG90JGRhdGEsCiAgICAgICAgICAgIGNvbmZpcm1lZF9jYXNlc190b3A3X291dHNpZGVfY2hpbmEsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIGRhdGFzZXQgdXNlZCBpbiB0aGUgbGFzdCBwbG90IGlzIG5vdCBgbm90X2NoaW5hYC4iCiAgICAgICAgKQogICAgfSkKICAgIGxpbmUgPC0gcGxvdCRsYXllcnNbWzFdXQogICAgdGVzdF90aGF0KCd0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IGdlb20nLCB7CiAgICAgICAgZXhwZWN0X2ZhbHNlKAogICAgICAgICAgICBpcy5udWxsKGxpbmUpLAogICAgICAgICAgICBpbmZvID0gIkNvdWxkIG5vdCBmaW4gYGdlb21fbGluZSgpYCBpbiB5b3VyIGxhc3QgcGxvdC4iCiAgICAgICAgKQogICAgfSkKICAgIHRlc3RfdGhhdCgndGhlIHBsb3QgdXNlcyB0aGUgY29ycmVjdCBnZW9tJywgewogICAgICAgIGV4cGVjdF90cnVlKAogICAgICAgICAgICAnR2VvbUxpbmUnICVpbiUgY2xhc3MobGluZSRnZW9tKSwKICAgICAgICAgICAgaW5mbyA9ICJDb3VsZCBub3QgZmluIGBnZW9tX2xpbmUoKWAgaW4geW91ciBsYXN0IHBsb3QuIgogICAgICAgICkKICAgIH0pCiAgICBtYXBwaW5nIDwtIHBsb3QkbWFwcGluZwogICAgZ2VvbV9tYXBwaW5nIDwtIGxpbmUkbWFwcGluZwogICAgdGVzdF90aGF0KCd0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IHggYWVzdGhldGljJywgewogICAgICAgIGV4cGVjdF90cnVlKAogICAgICAgICAgICAhaXMubnVsbChtYXBwaW5nJHgpICYmIHF1b19uYW1lKG1hcHBpbmckeCkgPT0gImRhdGUiIHx8CiAgICAgICAgICAgICFpcy5udWxsKGdlb21fbWFwcGluZyR4KSAmJiBxdW9fbmFtZShnZW9tX21hcHBpbmckeCkgPT0gImRhdGUiLAogICAgICAgICAgICBpbmZvID0gIlRoZSB4IGFlc3RoZXRpYyB1c2VkIGluIHRoZSBsYXN0IHBsb3QgaXMgbm90IGBkYXRlYC4iCgogICAgICAgICkKICAgIH0pCiAgICB0ZXN0X3RoYXQoJ3RoZSBwbG90IHVzZXMgdGhlIGNvcnJlY3QgeSBhZXN0aGV0aWMnLCB7CiAgICAgICAgZXhwZWN0X3RydWUoCiAgICAgICAgICAgICFpcy5udWxsKG1hcHBpbmckeSkgJiYgcXVvX25hbWUobWFwcGluZyR5KSA9PSAiY3VtX2Nhc2VzIiB8fAogICAgICAgICAgICAhaXMubnVsbChnZW9tX21hcHBpbmckeSkgJiYgcXVvX25hbWUoZ2VvbV9tYXBwaW5nJHkpID09ICJjdW1fY2FzZXMiLAogICAgICAgICAgICBpbmZvID0gIlRoZSB5IGFlc3RoZXRpYyB1c2VkIGluIHRoZSBsYXN0IHBsb3QgaXMgbm90IGBjdW1fY2FzZXNgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCd0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IGNvbG9yIGFlc3RoZXRpYycsIHsKICAgICAgICBleHBlY3RfdHJ1ZSgKICAgICAgICAgICAgIWlzLm51bGwobWFwcGluZyRjb2xvdXIpICYmIHF1b19uYW1lKG1hcHBpbmckY29sb3VyKSA9PSAiY291bnRyeSIgfHwKICAgICAgICAgICAgIWlzLm51bGwoZ2VvbV9tYXBwaW5nJGNvbG91cikgJiYgcXVvX25hbWUoZ2VvbV9tYXBwaW5nJGNvbG91cikgPT0gImNvdW50cnkiLAogICAgICAgICAgICBpbmZvID0gIlRoZSBjb2xvdXIgYWVzdGhldGljIHVzZWQgaW4gdGhlIGxhc3QgcGxvdCBpcyBub3QgYGNvdW50cnlgLiIKICAgICAgICApCiAgICB9KQogICAgdGVzdF90aGF0KCd0aGUgcGxvdCB1c2VzIHRoZSBjb3JyZWN0IGdyb3VwIGFlc3RoZXRpYycsIHsKICAgICAgICBleHBlY3RfdHJ1ZSgKICAgICAgICAgICAgIWlzLm51bGwobWFwcGluZyRncm91cCkgJiYgcXVvX25hbWUobWFwcGluZyRncm91cCkgPT0gImNvdW50cnkiIHx8CiAgICAgICAgICAgICFpcy5udWxsKGdlb21fbWFwcGluZyRncm91cCkgJiYgcXVvX25hbWUoZ2VvbV9tYXBwaW5nJGdyb3VwKSA9PSAiY291bnRyeSIsCiAgICAgICAgICAgIGluZm8gPSAiVGhlIGdyb3VwIGFlc3RoZXRpYyB1c2VkIGluIHRoZSBsYXN0IHBsb3QgaXMgbm90IGBjb3VudHJ5YC4iCiAgICAgICAgKQogICAgfSkKICAgIHRlc3RfdGhhdCgndGhlIHBsb3QgdXNlcyB0aGUgY29ycmVjdCB5IGxhYmVsJywgewogICAgICAgIGV4cGVjdF9lcXVhbCgKICAgICAgICAgICAgcGxvdCRsYWJlbHMkeSwKICAgICAgICAgICAgIkN1bXVsYXRpdmUgY29uZmlybWVkIGNhc2VzIiwKICAgICAgICAgICAgaW5mbyA9ICJUaGUgeSBsYWJlbCB1c2VkIGluIHRoZSBsYXN0IHBsb3QgaXMgbm90IGBcIkN1bXVsYXRpdmUgY29uZmlybWVkIGNhc2VzXCJgLiIKICAgICAgICApCiAgICB9KQp9KQo=