Loading R packages needed for data cleaning and data plots
library(tidyverse)
library(tidyquant)
library(lubridate)
library(stringr)
library(ggplot2)
library(esquisse)
library(here)
library(janitor)
library(ggthemes)
library(viridisLite)
library(plotly)
library(plyr)
library(tidyr)
library(scales)
library(countrycode)
library(wbstats)
options(scipen=10) # forces regular notation vs scientific notation (ie5)
##Clean IAM_dataset First, I am removing all the years which I will
not need for my analysis. These are years from 1990 to 2019, and year
2010 which mainly contains missing values because very few output
variables were generated for this year.
#Remove all year columns preceding 2022
IAM_data <- IAM_data %>%
select(-starts_with(c("19","200","201")))
#Remove year column 2110
IAM_data <- IAM_data %>%
select(-'2110')
Variables in the IAM dataset have different categorization, but all
categorization was aggregated into the ‘Variable’ column. To view the
broad variable categories in the dataset, I split the Variable column
based on first delimiter “|” (sep = “\|”), keeping the original
‘Variable’ column (remove = FALSE). Variable category is now under the
column ‘var.category’, and the sub-variable is under ‘variable’. Running
the function unique shows that there are 38 variable categories.
IAM_data <- separate(IAM_data, Variable, into = c("var.category","variable"),sep = "\\|", remove = FALSE, extra = "merge")
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 8946 rows [69, 371, 396, 472, 670, 812, 821, 857, 888, 946, 1310, 1614, 1639, 1749, 2052, 2076, 2322, 2520, 2660, 2669, ...].
unique(IAM_data$var.category) #list of 38 variable categories in IAM_Data
[1] "GDP"
[2] "Post-processed"
[3] "net GDP"
[4] "Agricultural Demand"
[5] "Capacity Additions"
[6] "Capacity"
[7] "Capital Cost"
[8] "Carbon Sequestration"
[9] "Emissions"
[10] "Energy Service"
[11] "Final Energy"
[12] "Investment"
[13] "Land Cover"
[14] "Primary Energy"
[15] "Production"
[16] "Secondary Energy"
[17] "Trade"
[18] "Population"
[19] "Price"
[20] "Revenue"
[21] "Useful Energy"
[22] "AR6 climate diagnostics"
[23] "Concentration"
[24] "Forcing"
[25] "Temperature"
[26] "Agricultural Production"
[27] "Consumption"
[28] "Fertilizer Use"
[29] "Food Demand"
[30] "Forestry Demand"
[31] "Forestry Production"
[32] "Water Consumption"
[33] "Water Withdrawal"
[34] "Yield"
[35] "Damage factor"
[36] "Policy Cost"
[37] "Macro-Economic Climate Damage"
[38] "Policy Cost and Macro-Economic Climate Damage"
The missing values in the newly created ‘variable’ column comes from
variables that do not have sub-labels. These are typically aggregate
values of sub-categories and therefore do not have the delimiter “|”. I
create a new dataframe and filter for only rows that have NA values in
the ‘variable’ column to see these aggregate variables
IAM_data2 <- IAM_data %>%
filter(is.na(variable))
unique(IAM_data2$var.category) #variables that do not have delimiter "|"
[1] "Agricultural Demand" "Land Cover" "Secondary Energy"
[4] "Final Energy" "Population" "Primary Energy"
[7] "Useful Energy" "Forcing" "Agricultural Production"
[10] "Consumption" "Food Demand" "Water Consumption"
[13] "Damage factor" "Investment"
Comparing region coverage between IAM models
Create a new dataframe for each IAM model.
unique(IAM_data$Model)
IAM_GCAM <- IAM_data %>%
filter(Model == "GCAM 5.3+ NGFS")
IAM_MESSAGE <- IAM_data %>%
filter(Model == "MESSAGEix-GLOBIOM 1.1-M-R12")
IAM_REMIND <- IAM_data %>%
filter(Model == "REMIND-MAgPIE 3.0-4.4")
Create a vector containing the list of countries which have output
from the each model.These vectors will be used to compare which regions
are not included in one model but are included in another model.
IAM_GCAM.country <- IAM_GCAM
# filter(str_detect(Region,"(R5)"))
IAM_GCAM.country <- IAM_GCAM.country[!grepl('(R5)|GCAM|World', IAM_GCAM.country$Region),]
IAM_GCAM.country <- unique(IAM_GCAM.country$Region)
IAM_MESSAGE.country <- IAM_MESSAGE
IAM_MESSAGE.country <- IAM_MESSAGE.country[!grepl('(R5)|MESSAGE|World', IAM_MESSAGE.country$Region),]
IAM_MESSAGE.country <- unique(IAM_MESSAGE.country$Region)
IAM_REMIND.country <- IAM_REMIND
IAM_REMIND.country <- IAM_REMIND.country[!grepl('(R5)|REMIND|World', IAM_REMIND.country$Region),]
IAM_REMIND.country <- unique(IAM_REMIND.country$Region)
Show all overlapping individual countries between the 3 IAM
models
Reduce(intersect, list(IAM_GCAM.country,IAM_MESSAGE.country,IAM_REMIND.country))
[1] "AFG" "ALB" "DZA" "AGO" "ARG" "ARM" "ABW" "AUS" "AUT" "AZE" "BHS" "BHR" "BGD"
[14] "BRB" "BLR" "BEL" "BLZ" "BEN" "BTN" "BOL" "BIH" "BWA" "BRN" "BGR" "BFA" "BDI"
[27] "KHM" "CMR" "CAN" "CPV" "CAF" "TCD" "CHL" "COL" "COM" "COG" "COD" "CRI" "HRV"
[40] "CUB" "CYP" "CZE" "CIV" "DNK" "DJI" "DOM" "EU" "ECU" "EGY" "SLV" "GNQ" "ERI"
[53] "EST" "ETH" "BRA" "FJI" "FIN" "FRA" "GAB" "GMB" "GEO" "DEU" "GHA" "GRC" "GTM"
[66] "GIN" "GNB" "GUY" "HTI" "HND" "HKG" "HUN" "ISL" "IDN" "IRN" "IRQ" "IRL" "ISR"
[79] "ITA" "JAM" "JOR" "KAZ" "KEN" "KOR" "KWT" "KGZ" "LAO" "LVA" "LBN" "LSO" "LBR"
[92] "LBY" "LTU" "LUX" "MAC" "MKD" "MDG" "MWI" "MYS" "MLI" "MLT" "MRT" "MDA" "MNG"
[105] "MNE" "MAR" "MOZ" "MMR" "NAM" "NPL" "NLD" "NCL" "NZL" "NIC" "NER" "NGA" "NOR"
[118] "OMN" "PAK" "PSE" "PAN" "PNG" "PRY" "CHN" "PER" "PHL" "POL" "PRT" "PRI" "QAT"
[131] "IND" "ROU" "RUS" "RWA" "LCA" "VCT" "WSM" "STP" "SAU" "SEN" "SRB" "SLE" "SGP"
[144] "SVK" "SVN" "SLB" "SOM" "ZAF" "ESP" "LKA" "JPN" "SDN" "SUR" "SWZ" "SWE" "CHE"
[157] "SYR" "TWN" "TJK" "TZA" "THA" "TLS" "TGO" "TTO" "TUN" "TUR" "TKM" "UGA" "UKR"
[170] "ARE" "GBR" "MEX" "USA" "URY" "UZB" "VUT" "VEN" "VNM" "YEM" "ZMB" "ZWE"
Show countries that are not included in the models. Output shows that
countries with ISO codes “PYF” “MDV” “MUS” “TON” are included in the
MESSAGE model but not in GCAM and REMIND. GCAM and REMIND models also
have the same country coverage.
setdiff(IAM_MESSAGE.country,IAM_GCAM.country) #countries included in MESSAGE but NOT in GCAM
[1] "PYF" "MDV" "MUS" "TON"
setdiff(IAM_MESSAGE.country,IAM_REMIND.country) #countries included in MESSAGE but NOT in REMIND
[1] "PYF" "MDV" "MUS" "TON"
c(setdiff(IAM_GCAM.country, IAM_REMIND.country), setdiff(IAM_REMIND.country, IAM_GCAM.country)) #Complete overlap of individual countries between GCAM and REMIND
character(0)
Data cleaning to evaluate emission pathways
Create a new dataframe for world-level data.
IAM_world <- IAM_data %>%
filter(Region == "World")
#Show variables that have no time skip
na_rows = IAM_world %>%
is.na() %>%
rowSums() > 0
processed.IAM_world <- IAM_world %>%
filter(!na_rows)
Create new dataframe for World emissions data. Variable
“Emissions|CO2” shows annual emissions in Mt CO2, excluding carbon
removal from CCS. Because the values in Mt are too large and are not
suitable for facet charts, a new variable column is created which is the
Mt CO2 value of the Mt.CO2.peryear variable converted into Ct CO2.
IAM_world.emissions <- IAM_world %>%
filter(Variable == "Emissions|CO2") %>%
pivot_longer('2020':'2100', names_to = "Year", values_to = "Mt.CO2.peryear") %>%
mutate(Gt.CO2.peryear = Mt.CO2.peryear*0.001) %>%
mutate(Year = as.numeric(Year)) %>%
filter(!is.na(Gt.CO2.peryear))
#filter(Scenario == c("Current Policies","Delayed Transition")) %>%
glimpse(IAM_world.emissions)
Rows: 414
Columns: 10
$ Model <chr> "GCAM 5.3+ NGFS", "GCAM 5.3+ NGFS", "GCAM 5.3+ NGFS", "GCAM 5.3…
$ Scenario <chr> "Below 2°C", "Below 2°C", "Below 2°C", "Below 2°C", "Below 2°C"…
$ Region <chr> "World", "World", "World", "World", "World", "World", "World", …
$ Variable <chr> "Emissions|CO2", "Emissions|CO2", "Emissions|CO2", "Emissions|C…
$ var.category <chr> "Emissions", "Emissions", "Emissions", "Emissions", "Emissions"…
$ variable <chr> "CO2", "CO2", "CO2", "CO2", "CO2", "CO2", "CO2", "CO2", "CO2", …
$ Unit <chr> "Mt CO2/yr", "Mt CO2/yr", "Mt CO2/yr", "Mt CO2/yr", "Mt CO2/yr"…
$ Year <dbl> 2020, 2025, 2030, 2035, 2040, 2045, 2050, 2055, 2060, 2065, 207…
$ Mt.CO2.peryear <dbl> 38613.0326, 35588.8368, 31691.6707, 26823.8586, 21933.7247, 170…
$ Gt.CO2.peryear <dbl> 38.6130326, 35.5888368, 31.6916707, 26.8238586, 21.9337247, 17.…
Rename scenarios for consistency with NGFS’s published presentation
and easier chart visualization
IAM_world.emissions$Scenario[IAM_world.emissions$Scenario == "Nationally Determined Contributions (NDCs)"] <- "NDCs"
IAM_world.emissions$Scenario[IAM_world.emissions$Scenario == "Delayed transition"] <- "Delayed 2°C"
Data cleaning for share of primary energy from fossil fuels,
renewables & nuclear
I want to display how the percentage share of fossil fuels,
renewables and nuclear energy in primary energy consumption change over
time. The following steps are to calculate this percentage share and how
it changes over time.
First, I create new dataframe containing the 5 variables needed for
my calculation. Data is pivoted from wider to longer so that there’s a
new column for Year and a single column for values.
IAM_world.energyshare <- IAM_world %>%
pivot_longer('2020':'2100', names_to = "Year")%>%
filter(Variable %in% c("Primary Energy","Primary Energy|Fossil","Primary Energy|Biomass","Primary Energy|Non-Biomass Renewables","Primary Energy|Nuclear")) %>%
filter(!is.na(value)) %>%
select(-var.category, -variable,-Region)
unique(IAM_world.energyshare$Variable)
[1] "Primary Energy" "Primary Energy|Biomass"
[3] "Primary Energy|Fossil" "Primary Energy|Non-Biomass Renewables"
[5] "Primary Energy|Nuclear"
Then I create another dataframe where I do my calculation. First I
pivot this data from longer to wider so that new columns are created
based on the Primary energy variable type.
Having these columns then allow me to create new columns containing
the percentage share, calculated by dividing the primary energy type (in
EJ/year) by total primary energy (in EJ/year). I then remove these
columns to leave only the percentage share columns. The percentage share
dataframe is pivoted back from wider to longer.
energy.share <- IAM_world.energyshare %>%
pivot_wider(names_from = Variable, values_from = value) %>%
rename(Primary.Energy = 'Primary Energy',
Primary.Energy.Biomass = 'Primary Energy|Biomass',
Primary.Energy.NonBiomass.Renewables = 'Primary Energy|Non-Biomass Renewables',
Primary.Energy.Nuclear = 'Primary Energy|Nuclear',
Primary.Energy.Fossil = 'Primary Energy|Fossil') %>%
mutate(Primary.Energy.Renewables = (Primary.Energy.Biomass +
Primary.Energy.NonBiomass.Renewables)) %>%
mutate(Fossil.share = Primary.Energy.Fossil/Primary.Energy,
Nuclear.share = Primary.Energy.Nuclear/Primary.Energy,
Renewables.share = Primary.Energy.Renewables/Primary.Energy) %>%
select(-Primary.Energy.Biomass, -Primary.Energy.NonBiomass.Renewables, -Primary.Energy,
-Primary.Energy.Nuclear, -Primary.Energy.Fossil, -Primary.Energy.Renewables)
energy.share$Unit[energy.share$Unit == "EJ/yr"] <- "percent"
energy.share <- energy.share %>%
pivot_longer(c(Fossil.share, Nuclear.share, Renewables.share),
names_to = "Energy.source", values_to = "Percent") %>%
mutate(Year = as.numeric(Year))
glimpse(energy.share)
Rows: 1,242
Columns: 6
$ Model <chr> "GCAM 5.3+ NGFS", "GCAM 5.3+ NGFS", "GCAM 5.3+ NGFS", "GCAM 5.3+…
$ Scenario <chr> "Below 2°C", "Below 2°C", "Below 2°C", "Below 2°C", "Below 2°C",…
$ Unit <chr> "percent", "percent", "percent", "percent", "percent", "percent"…
$ Year <dbl> 2020, 2020, 2020, 2025, 2025, 2025, 2030, 2030, 2030, 2035, 2035…
$ Energy.source <chr> "Fossil.share", "Nuclear.share", "Renewables.share", "Fossil.sha…
$ Percent <dbl> 0.84890693, 0.01580466, 0.13528841, 0.81794416, 0.01686257, 0.16…
Data cleaning for energy mix analysis
Create new dataframe for energy mix in 2100 under 2 scenarios: “Below
2°C” and “Current Policies”
IAM_world.2100energymix <- IAM_world %>%
pivot_longer('2020':'2100', names_to = "Year")%>%
filter(Variable %in% c("Primary Energy|Coal","Primary Energy|Gas","Primary Energy|Oil","Primary Energy|Biomass","Primary Energy|Non-Biomass Renewables","Primary Energy|Nuclear")) %>%
filter(!is.na(value)) %>%
select(-var.category, -variable,-Region) %>%
filter(Year == "2100") %>%
filter(Scenario %in% c("Below 2°C", "Current Policies"))
# Reordering Scenario factor levels
IAM_world.2100energymix$Scenario <- factor(IAM_world.2100energymix$Scenario, levels = c("Current Policies","Below 2°C"))
#Rename Variable into Energy source simplified names
IAM_world.2100energymix$Variable[IAM_world.2100energymix$Variable == "Primary Energy|Coal"] <- "Coal"
IAM_world.2100energymix$Variable[IAM_world.2100energymix$Variable == "Primary Energy|Gas"] <- "Gas"
IAM_world.2100energymix$Variable[IAM_world.2100energymix$Variable == "Primary Energy|Oil"] <- "Oil"
IAM_world.2100energymix$Variable[IAM_world.2100energymix$Variable == "Primary Energy|Biomass"] <- "Biomass"
IAM_world.2100energymix$Variable[IAM_world.2100energymix$Variable == "Primary Energy|Non-Biomass Renewables"] <- "Renewables"
IAM_world.2100energymix$Variable[IAM_world.2100energymix$Variable == "Primary Energy|Nuclear"] <- "Nuclear"
IAM_world.2100energymix
Data cleaning for CCS deployment analysis
Create new dataframe for cumulative CCS deployment from 2020 to 2100.
Data is grouped by ‘Variable’ column using the group_by function to
facilitate showing the cumulative value in the stacked bar chart later
on. Variables are also renamed.
IAM_world.CCS <- IAM_world %>%
filter(Variable %in% c("Carbon Sequestration|Land Use","Carbon Sequestration|CCS|Fossil","Carbon Sequestration|CCS|Biomass","Carbon Sequestration|CCS|Industrial Processes")) %>%
select(-var.category, -variable,-Region) %>%
pivot_longer('2020':'2100', names_to = "Year") %>%
filter(!is.na(value)) %>%
mutate(Gt.CO2.peryear = value*0.001)
IAM_world.CCS$Scenario[IAM_world.CCS$Scenario == "Nationally Determined Contributions (NDCs)"] <- "NDCs"
IAM_world.CCS$Scenario[IAM_world.CCS$Scenario == "Delayed transition"] <- "Delayed 2°C"
IAM_world.CCS$Variable[IAM_world.CCS$Variable == "Carbon Sequestration|Land Use"] <- "Land-based sequestration"
IAM_world.CCS$Variable[IAM_world.CCS$Variable == "Carbon Sequestration|CCS|Biomass"] <- "Bioenergy with CCS"
IAM_world.CCS$Variable[IAM_world.CCS$Variable == "Carbon Sequestration|CCS|Fossil"] <- "Fossil fuel power plants fitted with CCS"
IAM_world.CCS$Variable[IAM_world.CCS$Variable == "Carbon Sequestration|CCS|Industrial Processes"] <- "Industrial processes fitted with CCS"
# Reordering group factor levels
IAM_world.CCS$Scenario <- factor(IAM_world.CCS$Scenario, levels = c("Current Policies","Divergent Net Zero","Net Zero 2050","NDCs","Delayed 2°C","Below 2°C"))
IAM_world.CCS <- IAM_world.CCS %>%
group_by(Variable)
IAM_world.CCS
saveRDS(object = IAM_world.emissions, file = "IAM_world.emissions.rds")
saveRDS(object = IAM_world.CCS, file = "IAM_world.CCS.rds")
saveRDS(object = IAM_world.2100energymix, file = "IAM_world.2100energymix.rds")
saveRDS(object = energy.share, file = "world.energy.share.rds")
LS0tDQp0aXRsZTogIkRhdGEgcHJlcGFyYXRpb24gZm9yIGFuYWx5c2lzIGFuZCBjcmVhdGluZyBjaGFydHMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpMb2FkaW5nIFIgcGFja2FnZXMgbmVlZGVkIGZvciBkYXRhIGNsZWFuaW5nIGFuZCBkYXRhIHBsb3RzDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKSANCmxpYnJhcnkodGlkeXF1YW50KQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCmxpYnJhcnkoZXNxdWlzc2UpDQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KGphbml0b3IpDQpsaWJyYXJ5KGdndGhlbWVzKQ0KDQpsaWJyYXJ5KHZpcmlkaXNMaXRlKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShzY2FsZXMpDQoNCmxpYnJhcnkoY291bnRyeWNvZGUpDQpsaWJyYXJ5KHdic3RhdHMpDQoNCm9wdGlvbnMoc2NpcGVuPTEwKSAjIGZvcmNlcyByZWd1bGFyIG5vdGF0aW9uIHZzIHNjaWVudGlmaWMgbm90YXRpb24gKGllNSkNCmBgYA0KDQoNCiMjQ2xlYW4gSUFNX2RhdGFzZXQNCkZpcnN0LCBJIGFtIHJlbW92aW5nIGFsbCB0aGUgeWVhcnMgd2hpY2ggSSB3aWxsIG5vdCBuZWVkIGZvciBteSBhbmFseXNpcy4gVGhlc2UgYXJlIHllYXJzIGZyb20gMTk5MCB0byAyMDE5LCBhbmQgeWVhciAyMDEwIHdoaWNoIG1haW5seSBjb250YWlucyBtaXNzaW5nIHZhbHVlcyBiZWNhdXNlIHZlcnkgZmV3IG91dHB1dCB2YXJpYWJsZXMgd2VyZSBnZW5lcmF0ZWQgZm9yIHRoaXMgeWVhci4NCg0KYGBge3J9DQojUmVtb3ZlIGFsbCB5ZWFyIGNvbHVtbnMgcHJlY2VkaW5nIDIwMjINCklBTV9kYXRhIDwtIElBTV9kYXRhICU+JQ0KICBzZWxlY3QoLXN0YXJ0c193aXRoKGMoIjE5IiwiMjAwIiwiMjAxIikpKQ0KDQojUmVtb3ZlIHllYXIgY29sdW1uIDIxMTANCklBTV9kYXRhIDwtIElBTV9kYXRhICU+JQ0KICBzZWxlY3QoLScyMTEwJykNCg0KYGBgDQoNClZhcmlhYmxlcyBpbiB0aGUgSUFNIGRhdGFzZXQgaGF2ZSBkaWZmZXJlbnQgY2F0ZWdvcml6YXRpb24sIGJ1dCBhbGwgY2F0ZWdvcml6YXRpb24gd2FzIGFnZ3JlZ2F0ZWQgaW50byB0aGUgJ1ZhcmlhYmxlJyBjb2x1bW4uDQpUbyB2aWV3IHRoZSBicm9hZCB2YXJpYWJsZSBjYXRlZ29yaWVzIGluIHRoZSBkYXRhc2V0LCBJIHNwbGl0IHRoZSBWYXJpYWJsZSBjb2x1bW4gYmFzZWQgb24gZmlyc3QgZGVsaW1pdGVyICJ8IiAoc2VwID0gIlxcfCIpLCBrZWVwaW5nIHRoZSBvcmlnaW5hbCAnVmFyaWFibGUnIGNvbHVtbiAocmVtb3ZlID0gRkFMU0UpLiBWYXJpYWJsZSBjYXRlZ29yeSBpcyBub3cgdW5kZXIgdGhlIGNvbHVtbiAndmFyLmNhdGVnb3J5JywgYW5kIHRoZSBzdWItdmFyaWFibGUgaXMgdW5kZXIgJ3ZhcmlhYmxlJy4NClJ1bm5pbmcgdGhlIGZ1bmN0aW9uIHVuaXF1ZSBzaG93cyB0aGF0IHRoZXJlIGFyZSAzOCB2YXJpYWJsZSBjYXRlZ29yaWVzLg0KYGBge3J9DQpJQU1fZGF0YSA8LSBzZXBhcmF0ZShJQU1fZGF0YSwgVmFyaWFibGUsIGludG8gPSBjKCJ2YXIuY2F0ZWdvcnkiLCJ2YXJpYWJsZSIpLHNlcCA9ICJcXHwiLCByZW1vdmUgPSBGQUxTRSwgZXh0cmEgPSAibWVyZ2UiKQ0KDQp1bmlxdWUoSUFNX2RhdGEkdmFyLmNhdGVnb3J5KSAjbGlzdCBvZiAzOCB2YXJpYWJsZSBjYXRlZ29yaWVzIGluIElBTV9EYXRhDQpgYGANClRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgbmV3bHkgY3JlYXRlZCAndmFyaWFibGUnIGNvbHVtbiBjb21lcyBmcm9tIHZhcmlhYmxlcyB0aGF0IGRvIG5vdCBoYXZlIHN1Yi1sYWJlbHMuIFRoZXNlIGFyZSB0eXBpY2FsbHkgYWdncmVnYXRlIHZhbHVlcyBvZiBzdWItY2F0ZWdvcmllcyBhbmQgdGhlcmVmb3JlIGRvIG5vdCBoYXZlIHRoZSBkZWxpbWl0ZXIgInwiLiBJIGNyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgYW5kIGZpbHRlciBmb3Igb25seSByb3dzIHRoYXQgaGF2ZSBOQSB2YWx1ZXMgaW4gdGhlICd2YXJpYWJsZScgY29sdW1uIHRvIHNlZSB0aGVzZSBhZ2dyZWdhdGUgdmFyaWFibGVzDQpgYGB7cn0NCklBTV9kYXRhMiA8LSBJQU1fZGF0YSAlPiUNCiAgZmlsdGVyKGlzLm5hKHZhcmlhYmxlKSkNCg0KdW5pcXVlKElBTV9kYXRhMiR2YXIuY2F0ZWdvcnkpICN2YXJpYWJsZXMgdGhhdCBkbyBub3QgaGF2ZSBkZWxpbWl0ZXIgInwiDQpgYGANCg0KIyMgQ29tcGFyaW5nIHJlZ2lvbiBjb3ZlcmFnZSBiZXR3ZWVuIElBTSBtb2RlbHMNCkNyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgZm9yIGVhY2ggSUFNIG1vZGVsLg0KYGBge3J9DQp1bmlxdWUoSUFNX2RhdGEkTW9kZWwpDQoNCklBTV9HQ0FNIDwtIElBTV9kYXRhICU+JQ0KICBmaWx0ZXIoTW9kZWwgPT0gIkdDQU0gNS4zKyBOR0ZTIikNCg0KSUFNX01FU1NBR0UgPC0gSUFNX2RhdGEgJT4lDQogIGZpbHRlcihNb2RlbCA9PSAiTUVTU0FHRWl4LUdMT0JJT00gMS4xLU0tUjEyIikNCg0KSUFNX1JFTUlORCA8LSBJQU1fZGF0YSAlPiUNCiAgZmlsdGVyKE1vZGVsID09ICJSRU1JTkQtTUFnUElFIDMuMC00LjQiKQ0KYGBgDQoNCkNyZWF0ZSBhIHZlY3RvciBjb250YWluaW5nIHRoZSBsaXN0IG9mIGNvdW50cmllcyB3aGljaCBoYXZlIG91dHB1dCBmcm9tIHRoZSBlYWNoIG1vZGVsLlRoZXNlIHZlY3RvcnMgd2lsbCBiZSB1c2VkIHRvIGNvbXBhcmUgd2hpY2ggcmVnaW9ucyBhcmUgbm90IGluY2x1ZGVkIGluIG9uZSBtb2RlbCBidXQgYXJlIGluY2x1ZGVkIGluIGFub3RoZXIgbW9kZWwuDQpgYGB7cn0NCklBTV9HQ0FNLmNvdW50cnkgPC0gSUFNX0dDQU0NCiAjIGZpbHRlcihzdHJfZGV0ZWN0KFJlZ2lvbiwiKFI1KSIpKQ0KIA0KSUFNX0dDQU0uY291bnRyeSA8LSBJQU1fR0NBTS5jb3VudHJ5WyFncmVwbCgnKFI1KXxHQ0FNfFdvcmxkJywgSUFNX0dDQU0uY291bnRyeSRSZWdpb24pLF0NCg0KSUFNX0dDQU0uY291bnRyeSA8LSB1bmlxdWUoSUFNX0dDQU0uY291bnRyeSRSZWdpb24pDQoNCmBgYA0KDQpgYGB7cn0NCklBTV9NRVNTQUdFLmNvdW50cnkgPC0gSUFNX01FU1NBR0UNCklBTV9NRVNTQUdFLmNvdW50cnkgPC0gSUFNX01FU1NBR0UuY291bnRyeVshZ3JlcGwoJyhSNSl8TUVTU0FHRXxXb3JsZCcsIElBTV9NRVNTQUdFLmNvdW50cnkkUmVnaW9uKSxdDQoNCklBTV9NRVNTQUdFLmNvdW50cnkgPC0gdW5pcXVlKElBTV9NRVNTQUdFLmNvdW50cnkkUmVnaW9uKQ0KYGBgDQoNCmBgYHtyfQ0KSUFNX1JFTUlORC5jb3VudHJ5IDwtIElBTV9SRU1JTkQNCklBTV9SRU1JTkQuY291bnRyeSA8LSBJQU1fUkVNSU5ELmNvdW50cnlbIWdyZXBsKCcoUjUpfFJFTUlORHxXb3JsZCcsIElBTV9SRU1JTkQuY291bnRyeSRSZWdpb24pLF0NCg0KSUFNX1JFTUlORC5jb3VudHJ5IDwtIHVuaXF1ZShJQU1fUkVNSU5ELmNvdW50cnkkUmVnaW9uKQ0KYGBgDQoNClNob3cgYWxsIG92ZXJsYXBwaW5nIGluZGl2aWR1YWwgY291bnRyaWVzIGJldHdlZW4gdGhlIDMgSUFNIG1vZGVscw0KYGBge3J9DQpSZWR1Y2UoaW50ZXJzZWN0LCBsaXN0KElBTV9HQ0FNLmNvdW50cnksSUFNX01FU1NBR0UuY291bnRyeSxJQU1fUkVNSU5ELmNvdW50cnkpKQ0KYGBgDQoNClNob3cgY291bnRyaWVzIHRoYXQgYXJlIG5vdCBpbmNsdWRlZCBpbiB0aGUgbW9kZWxzLiBPdXRwdXQgc2hvd3MgdGhhdCBjb3VudHJpZXMgd2l0aCBJU08gY29kZXMgIlBZRiIgIk1EViIgIk1VUyIgIlRPTiIgYXJlIGluY2x1ZGVkIGluIHRoZSBNRVNTQUdFIG1vZGVsIGJ1dCBub3QgaW4gR0NBTSBhbmQgUkVNSU5ELiBHQ0FNIGFuZCBSRU1JTkQgbW9kZWxzIGFsc28gaGF2ZSB0aGUgc2FtZSBjb3VudHJ5IGNvdmVyYWdlLg0KYGBge3J9DQpzZXRkaWZmKElBTV9NRVNTQUdFLmNvdW50cnksSUFNX0dDQU0uY291bnRyeSkgI2NvdW50cmllcyBpbmNsdWRlZCBpbiBNRVNTQUdFIGJ1dCBOT1QgaW4gR0NBTQ0KDQpzZXRkaWZmKElBTV9NRVNTQUdFLmNvdW50cnksSUFNX1JFTUlORC5jb3VudHJ5KSAjY291bnRyaWVzIGluY2x1ZGVkIGluIE1FU1NBR0UgYnV0IE5PVCBpbiBSRU1JTkQNCg0KYyhzZXRkaWZmKElBTV9HQ0FNLmNvdW50cnksIElBTV9SRU1JTkQuY291bnRyeSksIHNldGRpZmYoSUFNX1JFTUlORC5jb3VudHJ5LCBJQU1fR0NBTS5jb3VudHJ5KSkgI0NvbXBsZXRlIG92ZXJsYXAgb2YgaW5kaXZpZHVhbCBjb3VudHJpZXMgYmV0d2VlbiBHQ0FNIGFuZCBSRU1JTkQNCg0KYGBgDQoNCiMjIERhdGEgY2xlYW5pbmcgdG8gZXZhbHVhdGUgZW1pc3Npb24gcGF0aHdheXMNCkNyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgZm9yIHdvcmxkLWxldmVsIGRhdGEuDQpgYGB7cn0NCklBTV93b3JsZCA8LSBJQU1fZGF0YSAlPiUNCiAgZmlsdGVyKFJlZ2lvbiA9PSAiV29ybGQiKQ0KICANCiNTaG93IHZhcmlhYmxlcyB0aGF0IGhhdmUgbm8gdGltZSBza2lwDQpuYV9yb3dzID0gSUFNX3dvcmxkICU+JSANCiAgaXMubmEoKSAlPiUgDQogIHJvd1N1bXMoKSA+IDANCg0KcHJvY2Vzc2VkLklBTV93b3JsZCA8LSBJQU1fd29ybGQgJT4lIA0KICBmaWx0ZXIoIW5hX3Jvd3MpDQoNCmBgYA0KDQpDcmVhdGUgbmV3IGRhdGFmcmFtZSBmb3IgV29ybGQgZW1pc3Npb25zIGRhdGEuIFZhcmlhYmxlICJFbWlzc2lvbnN8Q08yIiBzaG93cyBhbm51YWwgZW1pc3Npb25zIGluIE10IENPMiwgZXhjbHVkaW5nIGNhcmJvbiByZW1vdmFsIGZyb20gQ0NTLiBCZWNhdXNlIHRoZSB2YWx1ZXMgaW4gTXQgYXJlIHRvbyBsYXJnZSBhbmQgYXJlIG5vdCBzdWl0YWJsZSBmb3IgZmFjZXQgY2hhcnRzLCBhIG5ldyB2YXJpYWJsZSBjb2x1bW4gaXMgY3JlYXRlZCB3aGljaCBpcyB0aGUgTXQgQ08yIHZhbHVlIG9mIHRoZSBNdC5DTzIucGVyeWVhciB2YXJpYWJsZSBjb252ZXJ0ZWQgaW50byBDdCBDTzIuDQpgYGB7cn0NCklBTV93b3JsZC5lbWlzc2lvbnMgPC0gSUFNX3dvcmxkICU+JQ0KICBmaWx0ZXIoVmFyaWFibGUgPT0gIkVtaXNzaW9uc3xDTzIiKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKCcyMDIwJzonMjEwMCcsIG5hbWVzX3RvID0gIlllYXIiLCB2YWx1ZXNfdG8gPSAiTXQuQ08yLnBlcnllYXIiKSAlPiUNCiAgbXV0YXRlKEd0LkNPMi5wZXJ5ZWFyID0gTXQuQ08yLnBlcnllYXIqMC4wMDEpICU+JQ0KICBtdXRhdGUoWWVhciA9IGFzLm51bWVyaWMoWWVhcikpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEd0LkNPMi5wZXJ5ZWFyKSkNCiAjZmlsdGVyKFNjZW5hcmlvID09IGMoIkN1cnJlbnQgUG9saWNpZXMiLCJEZWxheWVkIFRyYW5zaXRpb24iKSkgJT4lDQoNCmdsaW1wc2UoSUFNX3dvcmxkLmVtaXNzaW9ucykNCmBgYA0KDQpSZW5hbWUgc2NlbmFyaW9zIGZvciBjb25zaXN0ZW5jeSB3aXRoIE5HRlMncyBwdWJsaXNoZWQgcHJlc2VudGF0aW9uIGFuZCBlYXNpZXIgY2hhcnQgdmlzdWFsaXphdGlvbg0KYGBge3J9DQpJQU1fd29ybGQuZW1pc3Npb25zJFNjZW5hcmlvW0lBTV93b3JsZC5lbWlzc2lvbnMkU2NlbmFyaW8gPT0gIk5hdGlvbmFsbHkgRGV0ZXJtaW5lZCBDb250cmlidXRpb25zIChORENzKSJdIDwtICJORENzIg0KSUFNX3dvcmxkLmVtaXNzaW9ucyRTY2VuYXJpb1tJQU1fd29ybGQuZW1pc3Npb25zJFNjZW5hcmlvID09ICJEZWxheWVkIHRyYW5zaXRpb24iXSA8LSAiRGVsYXllZCAywrBDIg0KYGBgDQoNCiMjIERhdGEgY2xlYW5pbmcgZm9yIHNoYXJlIG9mIHByaW1hcnkgZW5lcmd5IGZyb20gZm9zc2lsIGZ1ZWxzLCByZW5ld2FibGVzICYgbnVjbGVhcg0KSSB3YW50IHRvIGRpc3BsYXkgaG93IHRoZSBwZXJjZW50YWdlIHNoYXJlIG9mIGZvc3NpbCBmdWVscywgcmVuZXdhYmxlcyBhbmQgbnVjbGVhciBlbmVyZ3kgaW4gcHJpbWFyeSBlbmVyZ3kgY29uc3VtcHRpb24gY2hhbmdlIG92ZXIgdGltZS4gVGhlIGZvbGxvd2luZyBzdGVwcyBhcmUgdG8gY2FsY3VsYXRlIHRoaXMgcGVyY2VudGFnZSBzaGFyZSBhbmQgaG93IGl0IGNoYW5nZXMgb3ZlciB0aW1lLg0KDQpGaXJzdCwgSSBjcmVhdGUgbmV3IGRhdGFmcmFtZSBjb250YWluaW5nIHRoZSA1IHZhcmlhYmxlcyBuZWVkZWQgZm9yIG15IGNhbGN1bGF0aW9uLiBEYXRhIGlzIHBpdm90ZWQgZnJvbSB3aWRlciB0byBsb25nZXIgc28gdGhhdCB0aGVyZSdzIGEgbmV3IGNvbHVtbiBmb3IgWWVhciBhbmQgYSBzaW5nbGUgY29sdW1uIGZvciB2YWx1ZXMuDQpgYGB7cn0NCklBTV93b3JsZC5lbmVyZ3lzaGFyZSA8LSBJQU1fd29ybGQgJT4lDQogIHBpdm90X2xvbmdlcignMjAyMCc6JzIxMDAnLCBuYW1lc190byA9ICJZZWFyIiklPiUNCiAgZmlsdGVyKFZhcmlhYmxlICVpbiUgYygiUHJpbWFyeSBFbmVyZ3kiLCJQcmltYXJ5IEVuZXJneXxGb3NzaWwiLCJQcmltYXJ5IEVuZXJneXxCaW9tYXNzIiwiUHJpbWFyeSBFbmVyZ3l8Tm9uLUJpb21hc3MgUmVuZXdhYmxlcyIsIlByaW1hcnkgRW5lcmd5fE51Y2xlYXIiKSkgJT4lDQogIGZpbHRlcighaXMubmEodmFsdWUpKSAlPiUNCiAgc2VsZWN0KC12YXIuY2F0ZWdvcnksIC12YXJpYWJsZSwtUmVnaW9uKQ0KDQp1bmlxdWUoSUFNX3dvcmxkLmVuZXJneXNoYXJlJFZhcmlhYmxlKQ0KYGBgDQoNClRoZW4gSSBjcmVhdGUgYW5vdGhlciBkYXRhZnJhbWUgd2hlcmUgSSBkbyBteSBjYWxjdWxhdGlvbi4gRmlyc3QgSSBwaXZvdCB0aGlzIGRhdGEgZnJvbSBsb25nZXIgdG8gd2lkZXIgc28gdGhhdCBuZXcgY29sdW1ucyBhcmUgY3JlYXRlZCBiYXNlZCBvbiB0aGUgUHJpbWFyeSBlbmVyZ3kgdmFyaWFibGUgdHlwZS4NCg0KSGF2aW5nIHRoZXNlIGNvbHVtbnMgdGhlbiBhbGxvdyBtZSB0byBjcmVhdGUgbmV3IGNvbHVtbnMgY29udGFpbmluZyB0aGUgcGVyY2VudGFnZSBzaGFyZSwgY2FsY3VsYXRlZCBieSBkaXZpZGluZyB0aGUgcHJpbWFyeSBlbmVyZ3kgdHlwZSAoaW4gRUoveWVhcikgYnkgdG90YWwgcHJpbWFyeSBlbmVyZ3kgKGluIEVKL3llYXIpLiBJIHRoZW4gcmVtb3ZlIHRoZXNlIGNvbHVtbnMgdG8gbGVhdmUgb25seSB0aGUgcGVyY2VudGFnZSBzaGFyZSBjb2x1bW5zLiBUaGUgcGVyY2VudGFnZSBzaGFyZSBkYXRhZnJhbWUgaXMgcGl2b3RlZCBiYWNrIGZyb20gd2lkZXIgdG8gbG9uZ2VyLg0KYGBge3J9DQplbmVyZ3kuc2hhcmUgPC0gSUFNX3dvcmxkLmVuZXJneXNoYXJlICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gVmFyaWFibGUsIHZhbHVlc19mcm9tID0gdmFsdWUpICU+JQ0KICByZW5hbWUoUHJpbWFyeS5FbmVyZ3kgPSAnUHJpbWFyeSBFbmVyZ3knLA0KICAgICAgICAgUHJpbWFyeS5FbmVyZ3kuQmlvbWFzcyA9ICdQcmltYXJ5IEVuZXJneXxCaW9tYXNzJywNCiAgICAgICAgIFByaW1hcnkuRW5lcmd5Lk5vbkJpb21hc3MuUmVuZXdhYmxlcyA9ICdQcmltYXJ5IEVuZXJneXxOb24tQmlvbWFzcyBSZW5ld2FibGVzJywNCiAgICAgICAgIFByaW1hcnkuRW5lcmd5Lk51Y2xlYXIgPSAnUHJpbWFyeSBFbmVyZ3l8TnVjbGVhcicsDQogICAgICAgICBQcmltYXJ5LkVuZXJneS5Gb3NzaWwgPSAnUHJpbWFyeSBFbmVyZ3l8Rm9zc2lsJykgJT4lDQogIG11dGF0ZShQcmltYXJ5LkVuZXJneS5SZW5ld2FibGVzID0gKFByaW1hcnkuRW5lcmd5LkJpb21hc3MgKyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQcmltYXJ5LkVuZXJneS5Ob25CaW9tYXNzLlJlbmV3YWJsZXMpKSAlPiUNCiAgbXV0YXRlKEZvc3NpbC5zaGFyZSA9IFByaW1hcnkuRW5lcmd5LkZvc3NpbC9QcmltYXJ5LkVuZXJneSwNCiAgICAgICAgIE51Y2xlYXIuc2hhcmUgPSBQcmltYXJ5LkVuZXJneS5OdWNsZWFyL1ByaW1hcnkuRW5lcmd5LA0KICAgICAgICAgUmVuZXdhYmxlcy5zaGFyZSA9IFByaW1hcnkuRW5lcmd5LlJlbmV3YWJsZXMvUHJpbWFyeS5FbmVyZ3kpICU+JQ0KICBzZWxlY3QoLVByaW1hcnkuRW5lcmd5LkJpb21hc3MsIC1QcmltYXJ5LkVuZXJneS5Ob25CaW9tYXNzLlJlbmV3YWJsZXMsIC1QcmltYXJ5LkVuZXJneSwNCiAgICAgICAgIC1QcmltYXJ5LkVuZXJneS5OdWNsZWFyLCAtUHJpbWFyeS5FbmVyZ3kuRm9zc2lsLCAtUHJpbWFyeS5FbmVyZ3kuUmVuZXdhYmxlcykNCg0KDQplbmVyZ3kuc2hhcmUkVW5pdFtlbmVyZ3kuc2hhcmUkVW5pdCA9PSAiRUoveXIiXSA8LSAicGVyY2VudCINCg0KZW5lcmd5LnNoYXJlIDwtIGVuZXJneS5zaGFyZSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGMoRm9zc2lsLnNoYXJlLCBOdWNsZWFyLnNoYXJlLCBSZW5ld2FibGVzLnNoYXJlKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkVuZXJneS5zb3VyY2UiLCB2YWx1ZXNfdG8gPSAiUGVyY2VudCIpICU+JQ0KICBtdXRhdGUoWWVhciA9IGFzLm51bWVyaWMoWWVhcikpDQoNCmBgYA0KYGBge3J9DQpnbGltcHNlKGVuZXJneS5zaGFyZSkNCmBgYA0KDQojIyBEYXRhIGNsZWFuaW5nIGZvciBlbmVyZ3kgbWl4IGFuYWx5c2lzDQpDcmVhdGUgbmV3IGRhdGFmcmFtZSBmb3IgZW5lcmd5IG1peCBpbiAyMTAwIHVuZGVyIDIgc2NlbmFyaW9zOiAiQmVsb3cgMsKwQyIgYW5kICJDdXJyZW50IFBvbGljaWVzIg0KYGBge3J9DQpJQU1fd29ybGQuMjEwMGVuZXJneW1peCA8LSBJQU1fd29ybGQgJT4lDQogIHBpdm90X2xvbmdlcignMjAyMCc6JzIxMDAnLCBuYW1lc190byA9ICJZZWFyIiklPiUNCiAgZmlsdGVyKFZhcmlhYmxlICVpbiUgYygiUHJpbWFyeSBFbmVyZ3l8Q29hbCIsIlByaW1hcnkgRW5lcmd5fEdhcyIsIlByaW1hcnkgRW5lcmd5fE9pbCIsIlByaW1hcnkgRW5lcmd5fEJpb21hc3MiLCJQcmltYXJ5IEVuZXJneXxOb24tQmlvbWFzcyBSZW5ld2FibGVzIiwiUHJpbWFyeSBFbmVyZ3l8TnVjbGVhciIpKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh2YWx1ZSkpICU+JQ0KICBzZWxlY3QoLXZhci5jYXRlZ29yeSwgLXZhcmlhYmxlLC1SZWdpb24pICU+JQ0KICBmaWx0ZXIoWWVhciA9PSAiMjEwMCIpICU+JQ0KICBmaWx0ZXIoU2NlbmFyaW8gJWluJSBjKCJCZWxvdyAywrBDIiwgIkN1cnJlbnQgUG9saWNpZXMiKSkNCg0KIyBSZW9yZGVyaW5nIFNjZW5hcmlvIGZhY3RvciBsZXZlbHMNCklBTV93b3JsZC4yMTAwZW5lcmd5bWl4JFNjZW5hcmlvIDwtIGZhY3RvcihJQU1fd29ybGQuMjEwMGVuZXJneW1peCRTY2VuYXJpbywgbGV2ZWxzID0gYygiQ3VycmVudCBQb2xpY2llcyIsIkJlbG93IDLCsEMiKSkNCg0KI1JlbmFtZSBWYXJpYWJsZSBpbnRvIEVuZXJneSBzb3VyY2Ugc2ltcGxpZmllZCBuYW1lcw0KSUFNX3dvcmxkLjIxMDBlbmVyZ3ltaXgkVmFyaWFibGVbSUFNX3dvcmxkLjIxMDBlbmVyZ3ltaXgkVmFyaWFibGUgPT0gIlByaW1hcnkgRW5lcmd5fENvYWwiXSA8LSAiQ29hbCINCklBTV93b3JsZC4yMTAwZW5lcmd5bWl4JFZhcmlhYmxlW0lBTV93b3JsZC4yMTAwZW5lcmd5bWl4JFZhcmlhYmxlID09ICJQcmltYXJ5IEVuZXJneXxHYXMiXSA8LSAiR2FzIg0KSUFNX3dvcmxkLjIxMDBlbmVyZ3ltaXgkVmFyaWFibGVbSUFNX3dvcmxkLjIxMDBlbmVyZ3ltaXgkVmFyaWFibGUgPT0gIlByaW1hcnkgRW5lcmd5fE9pbCJdIDwtICJPaWwiDQpJQU1fd29ybGQuMjEwMGVuZXJneW1peCRWYXJpYWJsZVtJQU1fd29ybGQuMjEwMGVuZXJneW1peCRWYXJpYWJsZSA9PSAiUHJpbWFyeSBFbmVyZ3l8QmlvbWFzcyJdIDwtICJCaW9tYXNzIg0KSUFNX3dvcmxkLjIxMDBlbmVyZ3ltaXgkVmFyaWFibGVbSUFNX3dvcmxkLjIxMDBlbmVyZ3ltaXgkVmFyaWFibGUgPT0gIlByaW1hcnkgRW5lcmd5fE5vbi1CaW9tYXNzIFJlbmV3YWJsZXMiXSA8LSAiUmVuZXdhYmxlcyINCklBTV93b3JsZC4yMTAwZW5lcmd5bWl4JFZhcmlhYmxlW0lBTV93b3JsZC4yMTAwZW5lcmd5bWl4JFZhcmlhYmxlID09ICJQcmltYXJ5IEVuZXJneXxOdWNsZWFyIl0gPC0gIk51Y2xlYXIiDQpgYGANCmBgYHtyfQ0KSUFNX3dvcmxkLjIxMDBlbmVyZ3ltaXgNCmBgYA0KDQojIyBEYXRhIGNsZWFuaW5nIGZvciBDQ1MgZGVwbG95bWVudCBhbmFseXNpcw0KQ3JlYXRlIG5ldyBkYXRhZnJhbWUgZm9yIGN1bXVsYXRpdmUgQ0NTIGRlcGxveW1lbnQgZnJvbSAyMDIwIHRvIDIxMDAuIERhdGEgaXMgZ3JvdXBlZCBieSAnVmFyaWFibGUnIGNvbHVtbiB1c2luZyB0aGUgZ3JvdXBfYnkgZnVuY3Rpb24gdG8gZmFjaWxpdGF0ZSBzaG93aW5nIHRoZSBjdW11bGF0aXZlIHZhbHVlIGluIHRoZSBzdGFja2VkIGJhciBjaGFydCBsYXRlciBvbi4gVmFyaWFibGVzIGFyZSBhbHNvIHJlbmFtZWQuDQpgYGB7cn0NCklBTV93b3JsZC5DQ1MgPC0gSUFNX3dvcmxkICU+JQ0KICBmaWx0ZXIoVmFyaWFibGUgJWluJSBjKCJDYXJib24gU2VxdWVzdHJhdGlvbnxMYW5kIFVzZSIsIkNhcmJvbiBTZXF1ZXN0cmF0aW9ufENDU3xGb3NzaWwiLCJDYXJib24gU2VxdWVzdHJhdGlvbnxDQ1N8QmlvbWFzcyIsIkNhcmJvbiBTZXF1ZXN0cmF0aW9ufENDU3xJbmR1c3RyaWFsIFByb2Nlc3NlcyIpKSAlPiUNCiAgc2VsZWN0KC12YXIuY2F0ZWdvcnksIC12YXJpYWJsZSwtUmVnaW9uKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKCcyMDIwJzonMjEwMCcsIG5hbWVzX3RvID0gIlllYXIiKSAlPiUNCiAgZmlsdGVyKCFpcy5uYSh2YWx1ZSkpICU+JQ0KICBtdXRhdGUoR3QuQ08yLnBlcnllYXIgPSB2YWx1ZSowLjAwMSkNCg0KSUFNX3dvcmxkLkNDUyRTY2VuYXJpb1tJQU1fd29ybGQuQ0NTJFNjZW5hcmlvID09ICJOYXRpb25hbGx5IERldGVybWluZWQgQ29udHJpYnV0aW9ucyAoTkRDcykiXSA8LSAiTkRDcyINCklBTV93b3JsZC5DQ1MkU2NlbmFyaW9bSUFNX3dvcmxkLkNDUyRTY2VuYXJpbyA9PSAiRGVsYXllZCB0cmFuc2l0aW9uIl0gPC0gIkRlbGF5ZWQgMsKwQyINCg0KSUFNX3dvcmxkLkNDUyRWYXJpYWJsZVtJQU1fd29ybGQuQ0NTJFZhcmlhYmxlID09ICJDYXJib24gU2VxdWVzdHJhdGlvbnxMYW5kIFVzZSJdIDwtICJMYW5kLWJhc2VkIHNlcXVlc3RyYXRpb24iDQpJQU1fd29ybGQuQ0NTJFZhcmlhYmxlW0lBTV93b3JsZC5DQ1MkVmFyaWFibGUgPT0gIkNhcmJvbiBTZXF1ZXN0cmF0aW9ufENDU3xCaW9tYXNzIl0gPC0gIkJpb2VuZXJneSB3aXRoIENDUyINCklBTV93b3JsZC5DQ1MkVmFyaWFibGVbSUFNX3dvcmxkLkNDUyRWYXJpYWJsZSA9PSAiQ2FyYm9uIFNlcXVlc3RyYXRpb258Q0NTfEZvc3NpbCJdIDwtICJGb3NzaWwgZnVlbCBwb3dlciBwbGFudHMgZml0dGVkIHdpdGggQ0NTIg0KSUFNX3dvcmxkLkNDUyRWYXJpYWJsZVtJQU1fd29ybGQuQ0NTJFZhcmlhYmxlID09ICJDYXJib24gU2VxdWVzdHJhdGlvbnxDQ1N8SW5kdXN0cmlhbCBQcm9jZXNzZXMiXSA8LSAiSW5kdXN0cmlhbCBwcm9jZXNzZXMgZml0dGVkIHdpdGggQ0NTIg0KDQojIFJlb3JkZXJpbmcgZ3JvdXAgZmFjdG9yIGxldmVscw0KSUFNX3dvcmxkLkNDUyRTY2VuYXJpbyA8LSBmYWN0b3IoSUFNX3dvcmxkLkNDUyRTY2VuYXJpbywgbGV2ZWxzID0gYygiQ3VycmVudCBQb2xpY2llcyIsIkRpdmVyZ2VudCBOZXQgWmVybyIsIk5ldCBaZXJvIDIwNTAiLCJORENzIiwiRGVsYXllZCAywrBDIiwiQmVsb3cgMsKwQyIpKQ0KDQpJQU1fd29ybGQuQ0NTIDwtIElBTV93b3JsZC5DQ1MgJT4lDQogIGdyb3VwX2J5KFZhcmlhYmxlKQ0KYGBgDQpgYGB7cn0NCklBTV93b3JsZC5DQ1MNCmBgYA0KDQpgYGB7cn0NCnNhdmVSRFMob2JqZWN0ID0gSUFNX3dvcmxkLmVtaXNzaW9ucywgZmlsZSA9ICJJQU1fd29ybGQuZW1pc3Npb25zLnJkcyIpDQpzYXZlUkRTKG9iamVjdCA9IElBTV93b3JsZC5DQ1MsIGZpbGUgPSAiSUFNX3dvcmxkLkNDUy5yZHMiKQ0Kc2F2ZVJEUyhvYmplY3QgPSBJQU1fd29ybGQuMjEwMGVuZXJneW1peCwgZmlsZSA9ICJJQU1fd29ybGQuMjEwMGVuZXJneW1peC5yZHMiKQ0Kc2F2ZVJEUyhvYmplY3QgPSBlbmVyZ3kuc2hhcmUsIGZpbGUgPSAid29ybGQuZW5lcmd5LnNoYXJlLnJkcyIpDQoNCmBgYA0KDQoNCg0KDQo=