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=