This is a tutorial to show the use and mode of operation of the Environmental Impacts toolbox from the ADVANCE project. The toolbox is freely available from the ADVANCE project site.

How to Use the Toolbox

Requirements

The only data requirement are the results from an integrated assessment model (IAM) that detail electricity production, installed capacity, and capacity additions of power production technologies.

These data need to be in the format supplied by the IIASA databases, e.g. the AR5 database.

Output

The toolbox produces three output files in the same format as the IAM inputs.

The first contains the indirect energy requirements and the environmental impacts by region, technology, and life-cycle phase (construction and operation, end-of-life impacts are generally very small and included with construction), for all models and scenarios of the input.

The other two contain energy requirements and environmental impacts separately, for all different technology variants represented in the LCA data (e.g. different types of PV cells) and allow for the estimation of uncertainty ranges.

Four Steps to Run the Toolbox

Before running the ADVANCE_Toolbox_EI.R script, some small modifications in the configuration section may be necessary.

  1. Indicate the file with the IAM results.
IAM_results_file <- "./data/REMIND_example_data.csv"
  1. Match the scenarios in your data to the LCA scenarios.
    The LCA data from the THEMIS model contains two scenarios, Baseline, which assumes a business-as-usual trajectory without concerted climate change mitigation effort, and BLUE_Map, which assumes changes to the structure and efficiency of (industrial) processes and power sector technology improvements. BLUE_Map is used for all climate policy scenarios, regardless policy instruments or level of ambition.
TL$scenarios <- inline_data_frame(
"THEMIS;      IAMs",
"Baseline;    ADV_WP5_Base",
"BLUE_Map;    ADV_WP5_P240_FullTech")
  1. Match the regions of your data to those of THEMIS.
    Regions should be matched to the regions most strongly resembling regional characteristics. For details, see the worksheet region classification in the file ./data/NTNU_LCA_coefficients.xlsx.
TL$regions <- inline_data_frame(
    # "MESSAGE-GLOBIOM_1.0" added for illustration
    "THEMIS;   REMIND 1.7;   MESSAGE-GLOBIOM_1.0",
    "AME;      AFR;          AFR",
    "AME;      MEA;          MEA",
    "AS;       OAS;          PAS",
    ...)
  1. Match the names of the technologies in your data to those of THEMIS.
TL$technologies <- inline_data_frame(
    "THEMIS;            IAMs",
    ...
    "Wind|onshore;      Wind|Onshore",
    "Wind|offshore;     Wind|Offshore",
    "Solar|CSP;         Solar|CSP",
    "Solar|PV;          Solar|PV")

(Scenarios and technologies must be identical across models, as in the IIASA databases, while regions can differ.)

You can now run the toolbox.

How Does the Toolbox Work

Load and Tidy Data

After loading required packages and configuration, the IAM data and the LCA coefficients from the THEMIS model are loaded and converted into the proper format for use. If the data has not changed, cached versions of the data will be used.

# ---- load LCA and IAM data ----
if (-1 == file.access("./cache/LCA_coefficients.Rdata")
    | force_tidy_LCA
    | (max(file.info(c("./scripts/tidy_LCA_data.R",
                       LCA_coefficients_file))$mtime)
       > file.info("./cache/LCA_coefficients.Rdata")$mtime)) {
    cat("Processing LCA coefficients\n")
    source("./scripts/tidy_LCA_data.R")
} else {
    load("./cache/LCA_coefficients.Rdata")
}
if (-1 == file.access("./cache/IAM_data.Rdata")
    | force_tidy_LCA
    | (max(file.info(c("./scripts/tidy_IAM_data.R", IAM_results_file))$mtime)
       > file.info("./cache/IAM_data.Rdata")$mtime)) {
    cat("\nProcessing IAM results\n")
    source("./scripts/tidy_IAM_data.R")
} else {
    load("./cache/IAM_data.Rdata")
}

IAM Data

The script tidy_IAM_data.R returns two tables:

  • IAM.Electricity.Production contains the electricity production in seven columns:
    • model: lists the IAM model.
    • scenario: lists the IAM scenario.
    • region: lists the IAM region.
    • technology: lists the type of power plant.
    • unit: is always EJ/year.
    • period: lists model time-step.
    • value: holds the electricity production.
car::some(IAM.Electricity.Production)
  • IAM.Capacity contains the currently installed capacities and new capacity additions of the power plants in much the same format, except that
    • phase: distinguishes between Capacity (in GW) and Capacity Additions (in GW/year).
car::some(IAM.Capacity)

LCA Data

The script tidy_LCA_data returns three tables:

  • energy.requirements contains the coefficients of indirect energy requirements for the construction, operation, and end-of-life of power generation capacities and has 11 columns:
    • scenario: lists the IAM scenario.
    • region: lists the IAM region.
    • technology: lists the power sector technology.
    • technology.variant: distinguishes between different variants of technologies with different LCA coefficients (e.g. different types of PV cells). The mix variant is an average weighted by assumed likely shares of future deplyment of the different variants.
    • phase: lists the life-cycle phase (Construction, Operation, or End-of-life).
    • quantity: lists the service the energy is consumed for. Either a direct or not-specified energy service (Energy), or one of three key industry services (Cement and concrete, Iron and steel, Freight transport). This allows for further extensions to use model endogenous energy intensities for these service, if available.
    • energy.carrier: lists the consumed final energy carrier (to calculate indirect CO2 emissions).
    • unit.numerator and unit.denominator: list the unit of the coefficient, in a way to easily adjust them during calculations.
    • period: list the model time-step. LCA coefficients are available from THEMIS for 2010, 2030, and 2050. Time-steps between those are interpolated linearly, while the coefficients are kept constant before and after, on the 2010 or 2050 level, respectively.
    • value: holds the value of the coefficient.
car::some(energy.requirements)
  • environmental.impacts contains the environmental impacts and has generally the same format as energy.requirements, except that
    • impact: lists the impact category, namely
      • Aluminium use,
      • Cement use,
      • Copper use,
      • Iron use,
      • Mineral resource depletion (a price-weighted sum of the above),
      • Freshwater ecotoxicity,
      • Freshwater eutrophication,
      • Marine eutrophication,
      • Land occupation,
      • Human toxicity, and
      • Ionising radiation
  • MAgPIE.luluc.emissions contains specific land-use and land-use change (LULUC) change emissions of CO2, CH4, and N2O for different regions and biomass production regimes derived from the MAgPIE model.

For increased clarity in this tutorial, we limit the data to two technologies, two impacts, two life-cycle phases as well as one combination of model/scenario/region/period. We also drop the unused dimensions when showing data.

select_technologies <- c('Coal|w/o CCS', 'Solar|PV')
IAM.Capacity <- IAM.Capacity %>% 
    filter('REMIND 1.7' == model,
           'ADV_WP5_P240_FullTech' == scenario,
           'IND' == region,
           2025 == period,
           technology %in% select_technologies)
IAM.Capacity %>% 
    select(technology, phase, unit, value)
IAM.Electricity.Production <- IAM.Electricity.Production %>% 
    filter('REMIND 1.7' == model,
           'ADV_WP5_P240_FullTech' == scenario,
           'IND' == region,
           2025 == period,
           technology %in% select_technologies)
IAM.Electricity.Production %>% 
    select(technology, unit, value)
energy.requirements <- energy.requirements %>% 
    filter('ADV_WP5_P240_FullTech' == scenario,
           'IN' == region,
           'mix' == technology.variant,
           2025 == period,
           technology %in% select_technologies,
           quantity %in% c('Energy', 'Iron'),
           phase %in% c('Construction', 'Operation')) 
energy.requirements %>% 
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, phase, quantity, unit, value)
environmental.impacts <- environmental.impacts %>% 
    filter('ADV_WP5_P240_FullTech' == scenario,
           'IN' == region,
           'mix' == technology.variant,
           2025 == period,
           technology %in% select_technologies,
           impact %in% c('Aluminium', 'Human toxicity'),
           phase %in% c('Construction', 'Operation'))
environmental.impacts %>% 
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, impact, phase, unit, value)

Calculation of Energy Requirements

The energy requirements are calculated in the script calculate_energy_requirements.R.

Construction Phase

Energy requirements for construction (in which those of the end-of-life are included) are calculated by joining the two tables of capacity additions

IAM.Capacity %>%
    filter(phase == "Capacity Additions") %>%
    select(technology, capacity.additions = value, unit)

and the LCA coefficients

energy.requirements %>%
        filter(phase %in% c("Construction", "End-of-life")) %>%
        group_by(scenario, region, technology, technology.variant, quantity,
                 energy.carrier, unit.numerator, unit.denominator, period) %>%
        summarise(energy.requirements = sum(value)) %>%
        ungroup() %>% 
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, quantity, energy.carrier, energy.requirements, unit)

and multiplying the capacity additions with the LCA coefficients 1.

inner_join(
    IAM.Capacity %>%
        filter(phase == "Capacity Additions") %>%
        select(technology, capacity.additions = value, unit),
    
    energy.requirements %>%
        filter(phase %in% c("Construction", "End-of-life")) %>%
        group_by(scenario, region, technology, technology.variant, quantity,
                 energy.carrier, unit.numerator, unit.denominator, period) %>%
        summarise(energy.requirements = sum(value)) %>%
        ungroup() %>% 
        select(technology, quantity, energy.carrier, 
               unit.numerator, unit.denominator, energy.requirements),
    
    by = 'technology'
) %>%
    # GJ/MW * GW/a * 1000 MW/GW = GJ/a
    mutate(value = energy.requirements * capacity.additions * 1e3,
           phase = "Construction",
           unit.denominator = "yr") %>%
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, phase, quantity, energy.carrier, value, unit)

Operation Phase

Energy requirements for operation are calculated in a similar manner using capacity data for technologies that don’t use fuels,

IAM.Capacity %>% 
    filter(phase == "Capacity") %>% 
    select(technology, capacity = value, unit)
energy.requirements %>% 
    filter(phase == "Operation",
           unit.denominator == "MW/yr") %>% 
    select(technology, quantity, energy.carrier, 
           energy.requirements = value, unit.numerator, unit.denominator)
inner_join(
    IAM.Capacity %>% 
        filter(phase == "Capacity") %>% 
        select(technology, capacity = value, unit),
    
    energy.requirements %>% 
        filter(phase == "Operation",
               unit.denominator == "MW/yr") %>% 
        select(technology, phase, quantity, energy.carrier, 
               energy.requirements = value, unit.numerator, unit.denominator),
    
    by = 'technology'
) %>%
    # GJ/MWa * GW * 1000 MW/GW = GJ/a
    mutate(value = energy.requirements * capacity * 1e3,
           phase = "Operation",
           unit.denominator = "yr") %>% 
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, phase, quantity, energy.carrier, value, unit)

and using electricity production otherwise.

IAM.Electricity.Production %>% 
    select(technology, electricity.production = value, unit)
energy.requirements %>%
        filter(phase == "Operation",
               unit.denominator == "kWh") %>%
        select(technology, quantity, energy.carrier, 
               energy.requirements = value, unit.numerator, unit.denominator)
inner_join(
    IAM.Electricity.Production %>% 
        select(technology, electricity.production = value, unit),
    
    energy.requirements %>%
        filter(phase == "Operation",
               unit.denominator == "kWh") %>%
        select(technology, phase, quantity, energy.carrier, 
               energy.requirements = value, unit.numerator, unit.denominator),
    
    by = 'technology'
) %>%
    # GJ/kWh * EJ/a / (3.6e-12 EJ/kWh) = GJ/a
    mutate(value = energy.requirements * electricity.production / 3.6e-12,
           phase = "Operation",
           unit.denominator = "yr") %>% 
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, phase, quantity, energy.carrier, value, unit)

Calculation of Environmental Impacts

The calculation of environmental impacts is analogous to that of energy requirements, but uses different coefficients.

Construction Phase

Environmental impacts from construction (again including those of the end-of-life) are calculated by joining the the tables of capacity additions and the LCA coefficients:

IAM.Capacity %>% 
    filter(phase == "Capacity Additions") %>% 
    select(technology, capacity.additions = value, unit)
environmental.impacts %>%
    filter(phase %in% c("Construction", "End-of-life")) %>%
    group_by(scenario, region, technology, technology.variant, impact,
             unit.numerator, unit.denominator, period) %>%
    summarise(environmental.impacts = sum(value)) %>%
    ungroup() %>% 
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, impact, environmental.impacts, unit)
inner_join(
    IAM.Capacity %>% 
    filter(phase == "Capacity Additions") %>% 
        select(technology, capacity.additions = value, unit),
    
    environmental.impacts %>%
        filter(phase %in% c("Construction", "End-of-life")) %>%
        group_by(scenario, region, technology, technology.variant, impact,
                 unit.numerator, unit.denominator, period) %>%
        summarise(environmental.impacts = sum(value)) %>%
        ungroup() %>% 
        select(technology, impact, environmental.impacts, unit.numerator,
               unit.denominator),
    
    by = 'technology'
) %>%
    # x/MW * GW/a * 1000 MW/GW = x/a
    mutate(value = environmental.impacts * capacity.additions * 1e3,
           phase = "Construction",
           unit.denominator = "yr") %>% 
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, phase, impact, value, unit)

Operation Phase

Like energy requirements, the environmental impacts of the operation phase are calculated from LCA coefficients and data on either capacity additions

IAM.Capacity %>% 
    filter(phase == "Capacity") %>% 
    select(technology, capacity = value, unit)
environmental.impacts %>%
    filter(phase == "Operation",
           unit.denominator == "MW/yr") %>%
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, impact, environmental.impacts = value, unit)
inner_join(
    IAM.Capacity %>% 
    filter(phase == "Capacity") %>% 
        select(technology, capacity = value, unit),
    
    environmental.impacts %>%
        filter(phase == "Operation",
               unit.denominator == "MW/yr") %>%
        select(technology, impact, environmental.impacts = value, 
               unit.numerator, unit.denominator),
    
    by = 'technology'
) %>%
        # x/MWa * GW * 1000 MW/GW = x/a
        mutate(value = environmental.impacts * capacity * 1e3,
               phase = "Operation",
               unit.denominator = "yr") %>%
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, phase, impact, value, unit)

or electricity production, depending on the technology

IAM.Electricity.Production %>% 
    select(technology, electricity.production = value, unit)
environmental.impacts %>% 
    filter(phase == "Operation",
           unit.denominator == "kWh") %>%
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, impact, environmental.impacts = value, unit)
inner_join(
    IAM.Electricity.Production %>% 
        select(technology, electricity.production = value, unit),
    
    environmental.impacts %>% 
        filter(phase == "Operation",
               unit.denominator == "kWh") %>%
        select(technology, impact, environmental.impacts = value, 
               unit.numerator, unit.denominator),
    
    by = 'technology'
) %>%
        # x/kWh * EJ/a / (3.6e-12 EJ/kWh) = x/a
        mutate(value = environmental.impacts * electricity.production / 3.6e-12,
               phase = "Operation",
               unit.denominator = "yr") %>%
    unite(unit, unit.numerator, unit.denominator, sep = '/') %>% 
    select(technology, phase, impact, value, unit)

LULUC Emissions

Emissions from land use and land-use change are calculated separately in the same manner, but based on the coefficients in MAgPIE.luluc.emissions from the MAgPIE model.

Write Output

Results from the calculations are simply concatenated and written to .csv files in the same format as the LCA inputs.

Three files are written: one for both energy requirements and environmental impacts, but only for the default technology variant.

rbind(
    rbind(
        IAM.energy.requirements.Construction,
        IAM.energy.requirements.Operation
    ) %>%
        filter(technology.variant == "mix") %>%
        select(-technology.variant) %>%
        unite(variable, phase, quantity, energy.carrier, technology, sep = "|"),
    rbind(
        IAM.environmental.impacts.Construction,
        IAM.environmentla.impacts.Operation
    ) %>%
        filter(technology.variant == "mix") %>%
        select(-technology.variant) %>%
        unite(variable, phase, impact, technology, sep = "|"),
    IAM.LULUC.emissions %>%
        filter(technology.variant == "mix") %>%
        mutate(variable = paste("Emissions", emissions, "LULUC", technology,
                                sep = "|")) %>%
        select(model, scenario, region, variable, unit.numerator,
               unit.denominator, period, value)
) %>%
    unite(unit, unit.numerator, unit.denominator, sep = "/") %>%
    spread(period, value) %>%
    write.csv(paste0("./output/", output_file), row.names = FALSE, na = "")

And two files for all technology variants, with energy requirements

rbind(
    IAM.energy.requirements.Construction,
    IAM.energy.requirements.Operation
) %>%
    # GJ/a * 1e-9 EJ/GJ = EJ/a
    mutate(value = value * 1e-9,
           unit.numerator = "EJ") %>%
    unite(unit, unit.numerator, unit.denominator, sep = "/") %>%
    spread(period, value) %>%
    write.csv(sub("(^.*)(\\.[^\\.]+$)", "./output/\\1_energy\\2", output_file),
              row.names = FALSE, na = "")

and environmental impacts separated.

rbind(
    IAM.environmental.impacts.Construction,
    IAM.environmentla.impacts.Operation,
    IAM.LULUC.emissions %>%
        mutate(phase = "Operation",
               impact = paste("LULUC", emissions, "emissions")) %>%
        select(-emissions)
) %>%
    unite(unit, unit.numerator, unit.denominator, sep = "/") %>%
    spread(period, value) %>%
    write.csv(sub("(^.*)(\\.[^\\.]+$)", "./output/\\1_environment\\2",
                  output_file), row.names = FALSE, na = "")

Example Plot

A simple example plot from the data this tutorial used:


  1. At this step, the IAM and LCA regions are also matched, which is not shown here.

LS0tCnRpdGxlOiAiQURWQU5DRSBUb29sYm94IG9uICpFbnZpcm9ubWVudGFsIEltcGFjdHMqIC0tIFR1dG9yaWFsIgphdXRob3I6ICJNaWNoYWphIFBlaGwgYW5kIEd1bm5hciBMdWRlcmVyIgpvdXRwdXQ6IAogICAgaHRtbF9ub3RlYm9vazoKICAgICAgZmlnX2hlaWdodDogNgogICAgICBmaWdfd2lkdGg6IDkKICAgICAgdG9jOiB5ZXMKICAjIGh0bWxfZG9jdW1lbnQ6CiAgICAjIGRmX3ByaW50OiB0aWJibGUKICAgICMga2VlcF9tZDogeWVzCiAgICAjIHRoZW1lOiBmbGF0bHkKICAgICMgdG9jOiB5ZXMKLS0tCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKClRoaXMgaXMgYSB0dXRvcmlhbCB0byBzaG93IHRoZSB1c2UgYW5kIG1vZGUgb2Ygb3BlcmF0aW9uIG9mIHRoZSAqRW52aXJvbm1lbnRhbCAKSW1wYWN0cyogdG9vbGJveCBmcm9tIHRoZSBBRFZBTkNFIHByb2plY3QuIFRoZSB0b29sYm94IGlzIGZyZWVseSBhdmFpbGFibGUgZnJvbSAKdGhlIFtBRFZBTkNFIHByb2plY3Qgc2l0ZV0oaHR0cDovL2ZwNy1hZHZhbmNlLmV1L2NvbnRlbnQvZW52aXJvbm1lbnRhbC1pbXBhY3RzLW1vZHVsZSkuCgojIEhvdyB0byBVc2UgdGhlIFRvb2xib3gKCiMjIFJlcXVpcmVtZW50cwoKVGhlIG9ubHkgZGF0YSByZXF1aXJlbWVudCBhcmUgdGhlIHJlc3VsdHMgZnJvbSBhbiBpbnRlZ3JhdGVkIGFzc2Vzc21lbnQgbW9kZWwgCihJQU0pIHRoYXQgZGV0YWlsICplbGVjdHJpY2l0eSBwcm9kdWN0aW9uKiwgKmluc3RhbGxlZCBjYXBhY2l0eSosIGFuZCAKKmNhcGFjaXR5IGFkZGl0aW9ucyogb2YgcG93ZXIgcHJvZHVjdGlvbiB0ZWNobm9sb2dpZXMuIAoKVGhlc2UgZGF0YSBuZWVkIHRvIGJlIGluIHRoZSBmb3JtYXQgc3VwcGxpZWQgYnkgdGhlIElJQVNBIGRhdGFiYXNlcywgZS5nLiB0aGUgCltBUjUgZGF0YWJhc2VdKGh0dHA6Ly93d3cuaWlhc2EuYWMuYXQvd2ViL2hvbWUvcmVzZWFyY2gvcmVzZWFyY2hQcm9ncmFtcy9FbmVyZ3kvSVBDQ19BUjVfRGF0YWJhc2UuaHRtbCkuCgojIyBPdXRwdXQKClRoZSB0b29sYm94IHByb2R1Y2VzIHRocmVlIG91dHB1dCBmaWxlcyBpbiB0aGUgc2FtZSBmb3JtYXQgYXMgdGhlIElBTSBpbnB1dHMuIAoKVGhlIGZpcnN0IGNvbnRhaW5zIHRoZSBpbmRpcmVjdCBlbmVyZ3kgcmVxdWlyZW1lbnRzIGFuZCB0aGUgZW52aXJvbm1lbnRhbCAKaW1wYWN0cyBieSByZWdpb24sIHRlY2hub2xvZ3ksIGFuZCBsaWZlLWN5Y2xlIHBoYXNlIChjb25zdHJ1Y3Rpb24gYW5kIG9wZXJhdGlvbiwKZW5kLW9mLWxpZmUgaW1wYWN0cyBhcmUgZ2VuZXJhbGx5IHZlcnkgc21hbGwgYW5kIGluY2x1ZGVkIHdpdGggY29uc3RydWN0aW9uKSwgCmZvciBhbGwgbW9kZWxzIGFuZCBzY2VuYXJpb3Mgb2YgdGhlIGlucHV0LgoKVGhlIG90aGVyIHR3byBjb250YWluIGVuZXJneSByZXF1aXJlbWVudHMgYW5kIGVudmlyb25tZW50YWwgaW1wYWN0cyBzZXBhcmF0ZWx5LApmb3IgYWxsIGRpZmZlcmVudCB0ZWNobm9sb2d5IHZhcmlhbnRzIHJlcHJlc2VudGVkIGluIHRoZSBMQ0EgZGF0YSAoZS5nLiAKZGlmZmVyZW50IHR5cGVzIG9mIFBWIGNlbGxzKSBhbmQgYWxsb3cgZm9yIHRoZSBlc3RpbWF0aW9uIG9mIHVuY2VydGFpbnR5IHJhbmdlcy4KCiMjIEZvdXIgU3RlcHMgdG8gUnVuIHRoZSBUb29sYm94CgpCZWZvcmUgcnVubmluZyB0aGUgYEFEVkFOQ0VfVG9vbGJveF9FSS5SYCBzY3JpcHQsIHNvbWUgc21hbGwgbW9kaWZpY2F0aW9ucyBpbiAKdGhlIGBjb25maWd1cmF0aW9uYCBzZWN0aW9uIG1heSBiZSBuZWNlc3NhcnkuCgoxLiBJbmRpY2F0ZSB0aGUgZmlsZSB3aXRoIHRoZSBJQU0gcmVzdWx0cy4KYGBge3Igc3RlcCAxLCBldmFsID0gRkFMU0V9CklBTV9yZXN1bHRzX2ZpbGUgPC0gIi4vZGF0YS9SRU1JTkRfZXhhbXBsZV9kYXRhLmNzdiIKYGBgCgoyLiBNYXRjaCB0aGUgc2NlbmFyaW9zIGluIHlvdXIgZGF0YSB0byB0aGUgTENBIHNjZW5hcmlvcy4gIAogICBUaGUgTENBIGRhdGEgZnJvbSB0aGUgVEhFTUlTIG1vZGVsIGNvbnRhaW5zIHR3byBzY2VuYXJpb3MsIGBCYXNlbGluZWAsIHdoaWNoIAogICBhc3N1bWVzIGEgYnVzaW5lc3MtYXMtdXN1YWwgdHJhamVjdG9yeSB3aXRob3V0IGNvbmNlcnRlZCBjbGltYXRlIGNoYW5nZSAKICAgbWl0aWdhdGlvbiBlZmZvcnQsIGFuZCBgQkxVRV9NYXBgLCB3aGljaCBhc3N1bWVzIGNoYW5nZXMgdG8gdGhlIHN0cnVjdHVyZSAKICAgYW5kIGVmZmljaWVuY3kgb2YgKGluZHVzdHJpYWwpIHByb2Nlc3NlcyBhbmQgcG93ZXIgc2VjdG9yIHRlY2hub2xvZ3kgCiAgIGltcHJvdmVtZW50cy4gYEJMVUVfTWFwYCBpcyB1c2VkIGZvciBhbGwgY2xpbWF0ZSBwb2xpY3kgc2NlbmFyaW9zLCAKICAgcmVnYXJkbGVzcyBwb2xpY3kgaW5zdHJ1bWVudHMgb3IgbGV2ZWwgb2YgYW1iaXRpb24uIApgYGB7ciBzdGVwIDIsIGV2YWwgPSBGQUxTRX0KVEwkc2NlbmFyaW9zIDwtIGlubGluZV9kYXRhX2ZyYW1lKAoiVEhFTUlTOyAgICAgIElBTXMiLAoiQmFzZWxpbmU7ICAgIEFEVl9XUDVfQmFzZSIsCiJCTFVFX01hcDsgICAgQURWX1dQNV9QMjQwX0Z1bGxUZWNoIikKYGBgCgozLiBNYXRjaCB0aGUgcmVnaW9ucyBvZiB5b3VyIGRhdGEgdG8gdGhvc2Ugb2YgVEhFTUlTLiAgCiAgIFJlZ2lvbnMgc2hvdWxkIGJlIG1hdGNoZWQgdG8gdGhlIHJlZ2lvbnMgbW9zdCBzdHJvbmdseSByZXNlbWJsaW5nIHJlZ2lvbmFsIAogICBjaGFyYWN0ZXJpc3RpY3MuIEZvciBkZXRhaWxzLCBzZWUgdGhlIHdvcmtzaGVldCAqcmVnaW9uIGNsYXNzaWZpY2F0aW9uKiBpbiAKICAgdGhlIGZpbGUgYC4vZGF0YS9OVE5VX0xDQV9jb2VmZmljaWVudHMueGxzeGAuCmBgYHtyIHN0ZXAgMywgZXZhbCA9IEZBTFNFfQpUTCRyZWdpb25zIDwtIGlubGluZV9kYXRhX2ZyYW1lKAogICAgIyAiTUVTU0FHRS1HTE9CSU9NXzEuMCIgYWRkZWQgZm9yIGlsbHVzdHJhdGlvbgogICAgIlRIRU1JUzsgICBSRU1JTkQgMS43OyAgIE1FU1NBR0UtR0xPQklPTV8xLjAiLAogICAgIkFNRTsgICAgICBBRlI7ICAgICAgICAgIEFGUiIsCiAgICAiQU1FOyAgICAgIE1FQTsgICAgICAgICAgTUVBIiwKICAgICJBUzsgICAgICAgT0FTOyAgICAgICAgICBQQVMiLAogICAgLi4uKQpgYGAKCjQuIE1hdGNoIHRoZSBuYW1lcyBvZiB0aGUgdGVjaG5vbG9naWVzIGluIHlvdXIgZGF0YSB0byB0aG9zZSBvZiBUSEVNSVMuCmBgYHtyIHN0ZXAgNCwgZXZhbCA9IEZBTFNFfQpUTCR0ZWNobm9sb2dpZXMgPC0gaW5saW5lX2RhdGFfZnJhbWUoCiAgICAiVEhFTUlTOyAgICAgICAgICAgIElBTXMiLAogICAgLi4uCiAgICAiV2luZHxvbnNob3JlOyAgICAgIFdpbmR8T25zaG9yZSIsCiAgICAiV2luZHxvZmZzaG9yZTsgICAgIFdpbmR8T2Zmc2hvcmUiLAogICAgIlNvbGFyfENTUDsgICAgICAgICBTb2xhcnxDU1AiLAogICAgIlNvbGFyfFBWOyAgICAgICAgICBTb2xhcnxQViIpCmBgYAoKKFNjZW5hcmlvcyBhbmQgdGVjaG5vbG9naWVzIG11c3QgYmUgaWRlbnRpY2FsIGFjcm9zcyBtb2RlbHMsIGFzIGluIHRoZSBJSUFTQSAKZGF0YWJhc2VzLCB3aGlsZSByZWdpb25zIGNhbiBkaWZmZXIuKQoKWW91IGNhbiBub3cgcnVuIHRoZSB0b29sYm94LgoKIyBIb3cgRG9lcyB0aGUgVG9vbGJveCBXb3JrCmBgYHtyIHNjcmlwdCBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQojIC0tLS0gbG9hZCBvciBpbnN0YWxsIHBhY2thZ2VzIC0tLS0Kc291cmNlKCIuL3NjcmlwdHMvbG9hZF9vcl9pbnN0YWxsLlIiKQpsb2FkX29yX2luc3RhbGwoYygiZHBseXIiLCAidGlkeXIiLCAib3Blbnhsc3giLCAidXRpbHMiLCAiem9vIikpCnNvdXJjZSgiLi9zY3JpcHRzL2lubGluZV9kYXRhX2ZyYW1lLlIiKQoKIyAtLS0tIGNvbmZpZ3VyYXRpb24gLS0tLQpJQU1fcmVzdWx0c19maWxlICAgICAgPC0gIi4vZGF0YS9SRU1JTkRfZXhhbXBsZV9kYXRhLmNzdiIKCkxDQV9jb2VmZmljaWVudHNfZmlsZSA8LSAiLi9kYXRhL05UTlVfTENBX2NvZWZmaWNpZW50cy54bHN4IgpNQWdQSUVfTFVMVUNfZmlsZSAgICAgPC0gIi4vZGF0YS9NQWdQSUVfTFVMVUNfR0hHX2NvZWZmaWNpZW50cy5jc3YiCgpmb3JjZV90aWR5X0xDQSA8LSBGQUxTRSAgICMgcmVhZCBkYXRhIGFnYWluIChUUlVFKSBvciB1c2UgY2FjaGVkIGRhdGEgKEZBTFNFKT8KZm9yY2VfdGlkeV9JQU0gPC0gRkFMU0UKCm91dHB1dF9maWxlIDwtICJBRFZBTkNFX3Rlc3QuY3N2IgoKIyBUTCBpcyBhIGxpc3QgaG9sZGluZyBkaWZmZXJlbnQgbWFwcGluZ3MKVEwgPC0gbGlzdCgpCgojIHNjZW5hcmlvIG1hcHBpbmcKVEwkc2NlbmFyaW9zIDwtIGlubGluZV9kYXRhX2ZyYW1lKAogICAgIlRIRU1JUzsgICAgICBJQU1zIiwKICAgICJCYXNlbGluZTsgICAgQURWX1dQNV9CYXNlIiwKICAgICJCTFVFX01hcDsgICAgQURWX1dQNV9QMjQwX0Z1bGxUZWNoIiwKICAgIE5VTEwpCgojIHJlZ2lvbiBtYXBwaW5nClRMJHJlZ2lvbnMgPC0gaW5saW5lX2RhdGFfZnJhbWUoCiAgICAjICJNRVNTQUdFLUdMT0JJT01fMS4wIiBhZGRlZCBmb3IgaWxsdXN0cmF0aW9uCiAgICAiVEhFTUlTOyAgIFJFTUlORCAxLjc7ICAgTUVTU0FHRS1HTE9CSU9NXzEuMCIsCiAgICAiQU1FOyAgICAgIEFGUjsgICAgICAgICAgQUZSIiwKICAgICJBTUU7ICAgICAgTUVBOyAgICAgICAgICBNRUEiLAogICAgIkFTOyAgICAgICBPQVM7ICAgICAgICAgIFBBUyIsCiAgICAiQ047ICAgICAgIENITjsgICAgICAgICAgQ1BBIiwKICAgICJFSVQ7ICAgICAgUlVTOyAgICAgICAgICBFRVUiLAogICAgIkVJVDsgICAgICBOQTsgICAgICAgICAgIEZTVSIsCiAgICAiSU47ICAgICAgIElORDsgICAgICAgICAgU0FTIiwKICAgICJMQTsgICAgICAgTEFNOyAgICAgICAgICBMQU0iLAogICAgIlBBQzsgICAgICBKUE47ICAgICAgICAgIFBBTyIsCiAgICAiUEFDOyAgICAgIFJPVzsgICAgICAgICAgTkEiLAogICAgIlJFUjsgICAgICBFVVI7ICAgICAgICAgIFdFVSIsCiAgICAiVVM7ICAgICAgIFVTQTsgICAgICAgICAgTkFNIiwKICAgIE5VTEwpCgojIHRlY2hub2xvZ3kgbWFwcGluZwpUTCR0ZWNobm9sb2dpZXMgPC0gaW5saW5lX2RhdGFfZnJhbWUoCiAgICAiVEhFTUlTOyAgICAgICAgICAgIElBTXMiLAogICAgIkNvYWx8dy9vIENDUzsgICAgICBDb2FsfHcvbyBDQ1MiLAogICAgIkNvYWx8dy8gQ0NTOyAgICAgICBDb2FsfHcvIENDUyIsCiAgICAiR2FzfHcvbyBDQ1M7ICAgICAgIEdhc3x3L28gQ0NTIiwKICAgICJHYXN8dy8gQ0NTOyAgICAgICAgR2FzfHcvIENDUyIsCiAgICAiQmlvbWFzc3x3L28gQ0NTOyAgIEJpb21hc3N8dy9vIENDUyIsCiAgICAiQmlvbWFzc3x3LyBDQ1M7ICAgIEJpb21hc3N8dy8gQ0NTIiwKICAgICJIeWRybzsgICAgICAgICAgICAgSHlkcm8iLAogICAgIk51Y2xlYXI7ICAgICAgICAgICBOdWNsZWFyIiwKICAgICJXaW5kfG9uc2hvcmU7ICAgICAgV2luZCIsCiAgICAiV2luZHxvbnNob3JlOyAgICAgIFdpbmR8T25zaG9yZSIsCiAgICAiV2luZHxvZmZzaG9yZTsgICAgIFdpbmR8T2Zmc2hvcmUiLAogICAgIlNvbGFyfENTUDsgICAgICAgICBTb2xhcnxDU1AiLAogICAgIlNvbGFyfFBWOyAgICAgICAgICBTb2xhcnxQViIpCmBgYAoKIyMgTG9hZCBhbmQgVGlkeSBEYXRhCgpBZnRlciBsb2FkaW5nIHJlcXVpcmVkIHBhY2thZ2VzIGFuZCBjb25maWd1cmF0aW9uLCB0aGUgSUFNIGRhdGEgYW5kIHRoZSBMQ0EgCmNvZWZmaWNpZW50cyBmcm9tIHRoZSBUSEVNSVMgbW9kZWwgYXJlIGxvYWRlZCBhbmQgY29udmVydGVkIGludG8gdGhlIHByb3BlciAKZm9ybWF0IGZvciB1c2UuIElmIHRoZSBkYXRhIGhhcyBub3QgY2hhbmdlZCwgY2FjaGVkIHZlcnNpb25zIG9mIHRoZSBkYXRhIAp3aWxsIGJlIHVzZWQuCgpgYGB7ciBsb2FkIGFuZCB0aWR5IGRhdGF9CiMgLS0tLSBsb2FkIExDQSBhbmQgSUFNIGRhdGEgLS0tLQppZiAoLTEgPT0gZmlsZS5hY2Nlc3MoIi4vY2FjaGUvTENBX2NvZWZmaWNpZW50cy5SZGF0YSIpCiAgICB8IGZvcmNlX3RpZHlfTENBCiAgICB8IChtYXgoZmlsZS5pbmZvKGMoIi4vc2NyaXB0cy90aWR5X0xDQV9kYXRhLlIiLAogICAgICAgICAgICAgICAgICAgICAgIExDQV9jb2VmZmljaWVudHNfZmlsZSkpJG10aW1lKQogICAgICAgPiBmaWxlLmluZm8oIi4vY2FjaGUvTENBX2NvZWZmaWNpZW50cy5SZGF0YSIpJG10aW1lKSkgewogICAgY2F0KCJQcm9jZXNzaW5nIExDQSBjb2VmZmljaWVudHNcbiIpCiAgICBzb3VyY2UoIi4vc2NyaXB0cy90aWR5X0xDQV9kYXRhLlIiKQp9IGVsc2UgewogICAgbG9hZCgiLi9jYWNoZS9MQ0FfY29lZmZpY2llbnRzLlJkYXRhIikKfQoKaWYgKC0xID09IGZpbGUuYWNjZXNzKCIuL2NhY2hlL0lBTV9kYXRhLlJkYXRhIikKICAgIHwgZm9yY2VfdGlkeV9MQ0EKICAgIHwgKG1heChmaWxlLmluZm8oYygiLi9zY3JpcHRzL3RpZHlfSUFNX2RhdGEuUiIsIElBTV9yZXN1bHRzX2ZpbGUpKSRtdGltZSkKICAgICAgID4gZmlsZS5pbmZvKCIuL2NhY2hlL0lBTV9kYXRhLlJkYXRhIikkbXRpbWUpKSB7CiAgICBjYXQoIlxuUHJvY2Vzc2luZyBJQU0gcmVzdWx0c1xuIikKICAgIHNvdXJjZSgiLi9zY3JpcHRzL3RpZHlfSUFNX2RhdGEuUiIpCn0gZWxzZSB7CiAgICBsb2FkKCIuL2NhY2hlL0lBTV9kYXRhLlJkYXRhIikKfQpgYGAKCiMjIyBJQU0gRGF0YQoKVGhlIHNjcmlwdCBgdGlkeV9JQU1fZGF0YS5SYCByZXR1cm5zIHR3byB0YWJsZXM6CgoqIGBJQU0uRWxlY3RyaWNpdHkuUHJvZHVjdGlvbmAgY29udGFpbnMgdGhlIGVsZWN0cmljaXR5IHByb2R1Y3Rpb24gaW4gc2V2ZW4gCiAgY29sdW1uczoKICAgICogYG1vZGVsYDogbGlzdHMgdGhlIElBTSBtb2RlbC4KICAgICogYHNjZW5hcmlvYDogbGlzdHMgdGhlIElBTSBzY2VuYXJpby4KICAgICogYHJlZ2lvbmA6IGxpc3RzIHRoZSBJQU0gcmVnaW9uLgogICAgKiBgdGVjaG5vbG9neWA6IGxpc3RzIHRoZSB0eXBlIG9mIHBvd2VyIHBsYW50LgogICAgKiBgdW5pdGA6IGlzIGFsd2F5cyBFSi95ZWFyLgogICAgKiBgcGVyaW9kYDogbGlzdHMgbW9kZWwgdGltZS1zdGVwLgogICAgKiBgdmFsdWVgOiBob2xkcyB0aGUgZWxlY3RyaWNpdHkgcHJvZHVjdGlvbi4KICAgIApgYGB7ciBkaXNwbGF5IElBTS5FbGVjdHJpY2l0eS5Qcm9kdWN0aW9ufQpjYXI6OnNvbWUoSUFNLkVsZWN0cmljaXR5LlByb2R1Y3Rpb24pCmBgYAogICAgCiogYElBTS5DYXBhY2l0eWAgY29udGFpbnMgdGhlIGN1cnJlbnRseSBpbnN0YWxsZWQgY2FwYWNpdGllcyBhbmQgbmV3IGNhcGFjaXR5IAogIGFkZGl0aW9ucyBvZiB0aGUgcG93ZXIgcGxhbnRzIGluIG11Y2ggdGhlIHNhbWUgZm9ybWF0LCBleGNlcHQgdGhhdAogICAgKiBgcGhhc2VgOiBkaXN0aW5ndWlzaGVzIGJldHdlZW4gYENhcGFjaXR5YCAoaW4gR1cpIGFuZCBgQ2FwYWNpdHkgQWRkaXRpb25zYCAKICAgICAgKGluIEdXL3llYXIpLgogICAgICAKYGBge3IgZGlzcGxheSBJQU0uQ2FwYWNpdHl9CmNhcjo6c29tZShJQU0uQ2FwYWNpdHkpCmBgYAoKIyMjIExDQSBEYXRhCgpUaGUgc2NyaXB0IGB0aWR5X0xDQV9kYXRhYCByZXR1cm5zIHRocmVlIHRhYmxlczoKCiogYGVuZXJneS5yZXF1aXJlbWVudHNgIGNvbnRhaW5zIHRoZSBjb2VmZmljaWVudHMgb2YgaW5kaXJlY3QgZW5lcmd5IAogIHJlcXVpcmVtZW50cyBmb3IgdGhlIGNvbnN0cnVjdGlvbiwgb3BlcmF0aW9uLCBhbmQgZW5kLW9mLWxpZmUgb2YgcG93ZXIgCiAgZ2VuZXJhdGlvbiBjYXBhY2l0aWVzIGFuZCBoYXMgMTEgY29sdW1uczoKICAgICogYHNjZW5hcmlvYDogbGlzdHMgdGhlIElBTSBzY2VuYXJpby4KICAgICogYHJlZ2lvbmA6IGxpc3RzIHRoZSBJQU0gcmVnaW9uLgogICAgKiBgdGVjaG5vbG9neWA6IGxpc3RzIHRoZSBwb3dlciBzZWN0b3IgdGVjaG5vbG9neS4KICAgICogYHRlY2hub2xvZ3kudmFyaWFudGA6IGRpc3Rpbmd1aXNoZXMgYmV0d2VlbiBkaWZmZXJlbnQgdmFyaWFudHMgb2YgCiAgICAgIHRlY2hub2xvZ2llcyB3aXRoIGRpZmZlcmVudCBMQ0EgY29lZmZpY2llbnRzIChlLmcuIGRpZmZlcmVudCB0eXBlcyBvZiBQViAKICAgICAgY2VsbHMpLiBUaGUgYG1peGAgdmFyaWFudCBpcyBhbiBhdmVyYWdlIHdlaWdodGVkIGJ5IGFzc3VtZWQgbGlrZWx5IHNoYXJlcyAKICAgICAgb2YgZnV0dXJlIGRlcGx5bWVudCBvZiB0aGUgZGlmZmVyZW50IHZhcmlhbnRzLgogICAgKiBgcGhhc2VgOiBsaXN0cyB0aGUgbGlmZS1jeWNsZSBwaGFzZSAoYENvbnN0cnVjdGlvbmAsIGBPcGVyYXRpb25gLCBvciAKICAgICAgYEVuZC1vZi1saWZlYCkuCiAgICAqIGBxdWFudGl0eWA6IGxpc3RzIHRoZSBzZXJ2aWNlIHRoZSBlbmVyZ3kgaXMgY29uc3VtZWQgZm9yLiBFaXRoZXIgYSAKICAgICAgZGlyZWN0IG9yIG5vdC1zcGVjaWZpZWQgZW5lcmd5IHNlcnZpY2UgKGBFbmVyZ3lgKSwgb3Igb25lIG9mIHRocmVlIGtleSAKICAgICAgaW5kdXN0cnkgc2VydmljZXMgKGBDZW1lbnQgYW5kIGNvbmNyZXRlYCwgYElyb24gYW5kIHN0ZWVsYCwgCiAgICAgIGBGcmVpZ2h0IHRyYW5zcG9ydGApLiBUaGlzIGFsbG93cyBmb3IgZnVydGhlciBleHRlbnNpb25zIHRvIHVzZSBtb2RlbCAKICAgICAgZW5kb2dlbm91cyBlbmVyZ3kgaW50ZW5zaXRpZXMgZm9yIHRoZXNlIHNlcnZpY2UsIGlmIGF2YWlsYWJsZS4KICAgICogYGVuZXJneS5jYXJyaWVyYDogbGlzdHMgdGhlIGNvbnN1bWVkIGZpbmFsIGVuZXJneSBjYXJyaWVyICh0byBjYWxjdWxhdGUgCiAgICAgIGluZGlyZWN0IENPfjJ+IGVtaXNzaW9ucykuCiAgICAqIGB1bml0Lm51bWVyYXRvcmAgYW5kIGB1bml0LmRlbm9taW5hdG9yYDogbGlzdCB0aGUgdW5pdCBvZiB0aGUgCiAgICAgIGNvZWZmaWNpZW50LCBpbiBhIHdheSB0byBlYXNpbHkgYWRqdXN0IHRoZW0gZHVyaW5nIGNhbGN1bGF0aW9ucy4KICAgICogYHBlcmlvZGA6IGxpc3QgdGhlIG1vZGVsIHRpbWUtc3RlcC4gTENBIGNvZWZmaWNpZW50cyBhcmUgYXZhaWxhYmxlIGZyb20gCiAgICAgIFRIRU1JUyBmb3IgMjAxMCwgMjAzMCwgYW5kIDIwNTAuIFRpbWUtc3RlcHMgYmV0d2VlbiB0aG9zZSBhcmUgCiAgICAgIGludGVycG9sYXRlZCBsaW5lYXJseSwgd2hpbGUgdGhlIGNvZWZmaWNpZW50cyBhcmUga2VwdCBjb25zdGFudCBiZWZvcmUgCiAgICAgIGFuZCBhZnRlciwgb24gdGhlIDIwMTAgb3IgMjA1MCBsZXZlbCwgcmVzcGVjdGl2ZWx5LgogICAgKiBgdmFsdWVgOiBob2xkcyB0aGUgdmFsdWUgb2YgdGhlIGNvZWZmaWNpZW50LgoKYGBge3IgZGlzcGxheSBlbmVyZ3kucmVxdWlyZW1lbnRzfQpjYXI6OnNvbWUoZW5lcmd5LnJlcXVpcmVtZW50cykKCmBgYAoKKiBgZW52aXJvbm1lbnRhbC5pbXBhY3RzYCBjb250YWlucyB0aGUgZW52aXJvbm1lbnRhbCBpbXBhY3RzIGFuZCBoYXMgZ2VuZXJhbGx5IAogIHRoZSBzYW1lIGZvcm1hdCBhcyBgZW5lcmd5LnJlcXVpcmVtZW50c2AsIGV4Y2VwdCB0aGF0CiAgICAqIGBpbXBhY3RgOiBsaXN0cyB0aGUgaW1wYWN0IGNhdGVnb3J5LCBuYW1lbHkKICAgICAgICAqICpBbHVtaW5pdW0gdXNlKiwKICAgICAgICAqICpDZW1lbnQgdXNlKiwKICAgICAgICAqICpDb3BwZXIgdXNlKiwKICAgICAgICAqICpJcm9uIHVzZSosCiAgICAgICAgKiAqTWluZXJhbCByZXNvdXJjZSBkZXBsZXRpb24qIChhIHByaWNlLXdlaWdodGVkIHN1bSBvZiB0aGUgYWJvdmUpLAogICAgICAgICogKkZyZXNod2F0ZXIgZWNvdG94aWNpdHkqLAogICAgICAgICogKkZyZXNod2F0ZXIgZXV0cm9waGljYXRpb24qLAogICAgICAgICogKk1hcmluZSBldXRyb3BoaWNhdGlvbiosCiAgICAgICAgKiAqTGFuZCBvY2N1cGF0aW9uKiwKICAgICAgICAqICpIdW1hbiB0b3hpY2l0eSosIGFuZAogICAgICAgICogKklvbmlzaW5nIHJhZGlhdGlvbioKCiogYE1BZ1BJRS5sdWx1Yy5lbWlzc2lvbnNgIGNvbnRhaW5zIHNwZWNpZmljIGxhbmQtdXNlIGFuZCBsYW5kLXVzZSBjaGFuZ2UgCiAgKExVTFVDKSBjaGFuZ2UgZW1pc3Npb25zIG9mIENPfjJ+LCBDSH40fiwgYW5kIE5+Mn5PIGZvciBkaWZmZXJlbnQgcmVnaW9ucyBhbmQgCiAgYmlvbWFzcyBwcm9kdWN0aW9uIHJlZ2ltZXMgZGVyaXZlZCBmcm9tIHRoZSBNQWdQSUUgbW9kZWwuICAKCioqKgoKKipGb3IgaW5jcmVhc2VkIGNsYXJpdHkgaW4gdGhpcyB0dXRvcmlhbCwgd2UgbGltaXQgdGhlIGRhdGEgdG8gdHdvKioKKip0ZWNobm9sb2dpZXMsIHR3byBpbXBhY3RzLCB0d28gbGlmZS1jeWNsZSBwaGFzZXMgYXMgd2VsbCBhcyBvbmUgY29tYmluYXRpb24qKgoqKm9mIG1vZGVsL3NjZW5hcmlvL3JlZ2lvbi9wZXJpb2QuIFdlIGFsc28gZHJvcCB0aGUgdW51c2VkIGRpbWVuc2lvbnMgd2hlbioqCioqc2hvd2luZyBkYXRhLioqCgpgYGB7ciBsaW1pdCBkYXRhfQpzZWxlY3RfdGVjaG5vbG9naWVzIDwtIGMoJ0NvYWx8dy9vIENDUycsICdTb2xhcnxQVicpCgpJQU0uQ2FwYWNpdHkgPC0gSUFNLkNhcGFjaXR5ICU+JSAKICAgIGZpbHRlcignUkVNSU5EIDEuNycgPT0gbW9kZWwsCiAgICAgICAgICAgJ0FEVl9XUDVfUDI0MF9GdWxsVGVjaCcgPT0gc2NlbmFyaW8sCiAgICAgICAgICAgJ0lORCcgPT0gcmVnaW9uLAogICAgICAgICAgIDIwMjUgPT0gcGVyaW9kLAogICAgICAgICAgIHRlY2hub2xvZ3kgJWluJSBzZWxlY3RfdGVjaG5vbG9naWVzKQoKSUFNLkNhcGFjaXR5ICU+JSAKICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBwaGFzZSwgdW5pdCwgdmFsdWUpCgpJQU0uRWxlY3RyaWNpdHkuUHJvZHVjdGlvbiA8LSBJQU0uRWxlY3RyaWNpdHkuUHJvZHVjdGlvbiAlPiUgCiAgICBmaWx0ZXIoJ1JFTUlORCAxLjcnID09IG1vZGVsLAogICAgICAgICAgICdBRFZfV1A1X1AyNDBfRnVsbFRlY2gnID09IHNjZW5hcmlvLAogICAgICAgICAgICdJTkQnID09IHJlZ2lvbiwKICAgICAgICAgICAyMDI1ID09IHBlcmlvZCwKICAgICAgICAgICB0ZWNobm9sb2d5ICVpbiUgc2VsZWN0X3RlY2hub2xvZ2llcykKCklBTS5FbGVjdHJpY2l0eS5Qcm9kdWN0aW9uICU+JSAKICAgIHNlbGVjdCh0ZWNobm9sb2d5LCB1bml0LCB2YWx1ZSkKCmVuZXJneS5yZXF1aXJlbWVudHMgPC0gZW5lcmd5LnJlcXVpcmVtZW50cyAlPiUgCiAgICBmaWx0ZXIoJ0FEVl9XUDVfUDI0MF9GdWxsVGVjaCcgPT0gc2NlbmFyaW8sCiAgICAgICAgICAgJ0lOJyA9PSByZWdpb24sCiAgICAgICAgICAgJ21peCcgPT0gdGVjaG5vbG9neS52YXJpYW50LAogICAgICAgICAgIDIwMjUgPT0gcGVyaW9kLAogICAgICAgICAgIHRlY2hub2xvZ3kgJWluJSBzZWxlY3RfdGVjaG5vbG9naWVzLAogICAgICAgICAgIHF1YW50aXR5ICVpbiUgYygnRW5lcmd5JywgJ0lyb24nKSwKICAgICAgICAgICBwaGFzZSAlaW4lIGMoJ0NvbnN0cnVjdGlvbicsICdPcGVyYXRpb24nKSkgCgplbmVyZ3kucmVxdWlyZW1lbnRzICU+JSAKICAgIHVuaXRlKHVuaXQsIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yLCBzZXAgPSAnLycpICU+JSAKICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBwaGFzZSwgcXVhbnRpdHksIHVuaXQsIHZhbHVlKQoKZW52aXJvbm1lbnRhbC5pbXBhY3RzIDwtIGVudmlyb25tZW50YWwuaW1wYWN0cyAlPiUgCiAgICBmaWx0ZXIoJ0FEVl9XUDVfUDI0MF9GdWxsVGVjaCcgPT0gc2NlbmFyaW8sCiAgICAgICAgICAgJ0lOJyA9PSByZWdpb24sCiAgICAgICAgICAgJ21peCcgPT0gdGVjaG5vbG9neS52YXJpYW50LAogICAgICAgICAgIDIwMjUgPT0gcGVyaW9kLAogICAgICAgICAgIHRlY2hub2xvZ3kgJWluJSBzZWxlY3RfdGVjaG5vbG9naWVzLAogICAgICAgICAgIGltcGFjdCAlaW4lIGMoJ0FsdW1pbml1bScsICdIdW1hbiB0b3hpY2l0eScpLAogICAgICAgICAgIHBoYXNlICVpbiUgYygnQ29uc3RydWN0aW9uJywgJ09wZXJhdGlvbicpKQoKZW52aXJvbm1lbnRhbC5pbXBhY3RzICU+JSAKICAgIHVuaXRlKHVuaXQsIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yLCBzZXAgPSAnLycpICU+JSAKICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBpbXBhY3QsIHBoYXNlLCB1bml0LCB2YWx1ZSkKYGBgCgojIyBDYWxjdWxhdGlvbiBvZiBFbmVyZ3kgUmVxdWlyZW1lbnRzCgpUaGUgZW5lcmd5IHJlcXVpcmVtZW50cyBhcmUgY2FsY3VsYXRlZCBpbiB0aGUgc2NyaXB0IApgY2FsY3VsYXRlX2VuZXJneV9yZXF1aXJlbWVudHMuUmAuICAKCiMjIyBDb25zdHJ1Y3Rpb24gUGhhc2UKCkVuZXJneSByZXF1aXJlbWVudHMgZm9yIGNvbnN0cnVjdGlvbiAoaW4gd2hpY2ggdGhvc2Ugb2YgdGhlIGVuZC1vZi1saWZlIGFyZSAKaW5jbHVkZWQpIGFyZSBjYWxjdWxhdGVkIGJ5IGpvaW5pbmcgdGhlIHR3byB0YWJsZXMgb2YgY2FwYWNpdHkgYWRkaXRpb25zCmBgYHtyIHNlbGVjdCBjYXBhY2l0eSBhZGRpdGlvbnN9CklBTS5DYXBhY2l0eSAlPiUKICAgIGZpbHRlcihwaGFzZSA9PSAiQ2FwYWNpdHkgQWRkaXRpb25zIikgJT4lCiAgICBzZWxlY3QodGVjaG5vbG9neSwgY2FwYWNpdHkuYWRkaXRpb25zID0gdmFsdWUsIHVuaXQpCmBgYAphbmQgdGhlIExDQSBjb2VmZmljaWVudHMKYGBge3Igc2VsZWN0IGVuZXJneSByZXF1aXJlbWVudHMgZm9yIGNvbnN0cnVjdGlvbn0KZW5lcmd5LnJlcXVpcmVtZW50cyAlPiUKICAgICAgICBmaWx0ZXIocGhhc2UgJWluJSBjKCJDb25zdHJ1Y3Rpb24iLCAiRW5kLW9mLWxpZmUiKSkgJT4lCiAgICAgICAgZ3JvdXBfYnkoc2NlbmFyaW8sIHJlZ2lvbiwgdGVjaG5vbG9neSwgdGVjaG5vbG9neS52YXJpYW50LCBxdWFudGl0eSwKICAgICAgICAgICAgICAgICBlbmVyZ3kuY2FycmllciwgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IsIHBlcmlvZCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKGVuZXJneS5yZXF1aXJlbWVudHMgPSBzdW0odmFsdWUpKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lIAogICAgdW5pdGUodW5pdCwgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IsIHNlcCA9ICcvJykgJT4lIAogICAgc2VsZWN0KHRlY2hub2xvZ3ksIHF1YW50aXR5LCBlbmVyZ3kuY2FycmllciwgZW5lcmd5LnJlcXVpcmVtZW50cywgdW5pdCkKYGBgCmFuZCBtdWx0aXBseWluZyB0aGUgY2FwYWNpdHkgYWRkaXRpb25zIHdpdGggdGhlIExDQSBjb2VmZmljaWVudHMgW14xXS4KYGBge3IgY2FsY3VsYXRlIGNhcGFjaXR5IGFkZGl0aW9ucyBlbmVyZ3kgcmVxdWlyZW1lbnRzLCB3YXJuaW5nID0gRkFMU0V9CmlubmVyX2pvaW4oCiAgICBJQU0uQ2FwYWNpdHkgJT4lCiAgICAgICAgZmlsdGVyKHBoYXNlID09ICJDYXBhY2l0eSBBZGRpdGlvbnMiKSAlPiUKICAgICAgICBzZWxlY3QodGVjaG5vbG9neSwgY2FwYWNpdHkuYWRkaXRpb25zID0gdmFsdWUsIHVuaXQpLAogICAgCiAgICBlbmVyZ3kucmVxdWlyZW1lbnRzICU+JQogICAgICAgIGZpbHRlcihwaGFzZSAlaW4lIGMoIkNvbnN0cnVjdGlvbiIsICJFbmQtb2YtbGlmZSIpKSAlPiUKICAgICAgICBncm91cF9ieShzY2VuYXJpbywgcmVnaW9uLCB0ZWNobm9sb2d5LCB0ZWNobm9sb2d5LnZhcmlhbnQsIHF1YW50aXR5LAogICAgICAgICAgICAgICAgIGVuZXJneS5jYXJyaWVyLCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgcGVyaW9kKSAlPiUKICAgICAgICBzdW1tYXJpc2UoZW5lcmd5LnJlcXVpcmVtZW50cyA9IHN1bSh2YWx1ZSkpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUgCiAgICAgICAgc2VsZWN0KHRlY2hub2xvZ3ksIHF1YW50aXR5LCBlbmVyZ3kuY2FycmllciwgCiAgICAgICAgICAgICAgIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yLCBlbmVyZ3kucmVxdWlyZW1lbnRzKSwKICAgIAogICAgYnkgPSAndGVjaG5vbG9neScKKSAlPiUKICAgICMgR0ovTVcgKiBHVy9hICogMTAwMCBNVy9HVyA9IEdKL2EKICAgIG11dGF0ZSh2YWx1ZSA9IGVuZXJneS5yZXF1aXJlbWVudHMgKiBjYXBhY2l0eS5hZGRpdGlvbnMgKiAxZTMsCiAgICAgICAgICAgcGhhc2UgPSAiQ29uc3RydWN0aW9uIiwKICAgICAgICAgICB1bml0LmRlbm9taW5hdG9yID0gInlyIikgJT4lCiAgICB1bml0ZSh1bml0LCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgc2VwID0gJy8nKSAlPiUgCiAgICBzZWxlY3QodGVjaG5vbG9neSwgcGhhc2UsIHF1YW50aXR5LCBlbmVyZ3kuY2FycmllciwgdmFsdWUsIHVuaXQpCmBgYAoKIyMjIE9wZXJhdGlvbiBQaGFzZQoKRW5lcmd5IHJlcXVpcmVtZW50cyBmb3Igb3BlcmF0aW9uIGFyZSBjYWxjdWxhdGVkIGluIGEgc2ltaWxhciBtYW5uZXIgdXNpbmcgCmNhcGFjaXR5IGRhdGEgZm9yIHRlY2hub2xvZ2llcyB0aGF0IGRvbid0IHVzZSBmdWVscywgCmBgYHtyIGNhbGN1bGF0ZSBvcGVyYXRpb24gZW5lcmd5IHJlcXVpcmVtZW50cyBiYXNlZCBvbiBjYXBhY2l0eSwgd2FybmluZyA9IEZBTFNFfQpJQU0uQ2FwYWNpdHkgJT4lIAogICAgZmlsdGVyKHBoYXNlID09ICJDYXBhY2l0eSIpICU+JSAKICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBjYXBhY2l0eSA9IHZhbHVlLCB1bml0KQoKZW5lcmd5LnJlcXVpcmVtZW50cyAlPiUgCiAgICBmaWx0ZXIocGhhc2UgPT0gIk9wZXJhdGlvbiIsCiAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciA9PSAiTVcveXIiKSAlPiUgCiAgICBzZWxlY3QodGVjaG5vbG9neSwgcXVhbnRpdHksIGVuZXJneS5jYXJyaWVyLCAKICAgICAgICAgICBlbmVyZ3kucmVxdWlyZW1lbnRzID0gdmFsdWUsIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yKQoKaW5uZXJfam9pbigKICAgIElBTS5DYXBhY2l0eSAlPiUgCiAgICAgICAgZmlsdGVyKHBoYXNlID09ICJDYXBhY2l0eSIpICU+JSAKICAgICAgICBzZWxlY3QodGVjaG5vbG9neSwgY2FwYWNpdHkgPSB2YWx1ZSwgdW5pdCksCiAgICAKICAgIGVuZXJneS5yZXF1aXJlbWVudHMgJT4lIAogICAgICAgIGZpbHRlcihwaGFzZSA9PSAiT3BlcmF0aW9uIiwKICAgICAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciA9PSAiTVcveXIiKSAlPiUgCiAgICAgICAgc2VsZWN0KHRlY2hub2xvZ3ksIHBoYXNlLCBxdWFudGl0eSwgZW5lcmd5LmNhcnJpZXIsIAogICAgICAgICAgICAgICBlbmVyZ3kucmVxdWlyZW1lbnRzID0gdmFsdWUsIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yKSwKICAgIAogICAgYnkgPSAndGVjaG5vbG9neScKKSAlPiUKICAgICMgR0ovTVdhICogR1cgKiAxMDAwIE1XL0dXID0gR0ovYQogICAgbXV0YXRlKHZhbHVlID0gZW5lcmd5LnJlcXVpcmVtZW50cyAqIGNhcGFjaXR5ICogMWUzLAogICAgICAgICAgIHBoYXNlID0gIk9wZXJhdGlvbiIsCiAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciA9ICJ5ciIpICU+JSAKICAgIHVuaXRlKHVuaXQsIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yLCBzZXAgPSAnLycpICU+JSAKICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBwaGFzZSwgcXVhbnRpdHksIGVuZXJneS5jYXJyaWVyLCB2YWx1ZSwgdW5pdCkKYGBgCmFuZCB1c2luZyBlbGVjdHJpY2l0eSBwcm9kdWN0aW9uIG90aGVyd2lzZS4KYGBge3IgY2FsY3VsYXRlIG9wZXJhdGlvbiBlbmVyZ3kgcmVxdWlyZW1lbnRzIGJhc2VkIG9uIHByb2R1Y3Rpb24sIHdhcm5pbmcgPSBGQUxTRX0KSUFNLkVsZWN0cmljaXR5LlByb2R1Y3Rpb24gJT4lIAogICAgc2VsZWN0KHRlY2hub2xvZ3ksIGVsZWN0cmljaXR5LnByb2R1Y3Rpb24gPSB2YWx1ZSwgdW5pdCkKCmVuZXJneS5yZXF1aXJlbWVudHMgJT4lCiAgICAgICAgZmlsdGVyKHBoYXNlID09ICJPcGVyYXRpb24iLAogICAgICAgICAgICAgICB1bml0LmRlbm9taW5hdG9yID09ICJrV2giKSAlPiUKICAgICAgICBzZWxlY3QodGVjaG5vbG9neSwgcXVhbnRpdHksIGVuZXJneS5jYXJyaWVyLCAKICAgICAgICAgICAgICAgZW5lcmd5LnJlcXVpcmVtZW50cyA9IHZhbHVlLCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvcikKCmlubmVyX2pvaW4oCiAgICBJQU0uRWxlY3RyaWNpdHkuUHJvZHVjdGlvbiAlPiUgCiAgICAgICAgc2VsZWN0KHRlY2hub2xvZ3ksIGVsZWN0cmljaXR5LnByb2R1Y3Rpb24gPSB2YWx1ZSwgdW5pdCksCiAgICAKICAgIGVuZXJneS5yZXF1aXJlbWVudHMgJT4lCiAgICAgICAgZmlsdGVyKHBoYXNlID09ICJPcGVyYXRpb24iLAogICAgICAgICAgICAgICB1bml0LmRlbm9taW5hdG9yID09ICJrV2giKSAlPiUKICAgICAgICBzZWxlY3QodGVjaG5vbG9neSwgcGhhc2UsIHF1YW50aXR5LCBlbmVyZ3kuY2FycmllciwgCiAgICAgICAgICAgICAgIGVuZXJneS5yZXF1aXJlbWVudHMgPSB2YWx1ZSwgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IpLAogICAgCiAgICBieSA9ICd0ZWNobm9sb2d5JwopICU+JQogICAgIyBHSi9rV2ggKiBFSi9hIC8gKDMuNmUtMTIgRUova1doKSA9IEdKL2EKICAgIG11dGF0ZSh2YWx1ZSA9IGVuZXJneS5yZXF1aXJlbWVudHMgKiBlbGVjdHJpY2l0eS5wcm9kdWN0aW9uIC8gMy42ZS0xMiwKICAgICAgICAgICBwaGFzZSA9ICJPcGVyYXRpb24iLAogICAgICAgICAgIHVuaXQuZGVub21pbmF0b3IgPSAieXIiKSAlPiUgCiAgICB1bml0ZSh1bml0LCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgc2VwID0gJy8nKSAlPiUgCiAgICBzZWxlY3QodGVjaG5vbG9neSwgcGhhc2UsIHF1YW50aXR5LCBlbmVyZ3kuY2FycmllciwgdmFsdWUsIHVuaXQpCmBgYAoKIyMgQ2FsY3VsYXRpb24gb2YgRW52aXJvbm1lbnRhbCBJbXBhY3RzCgpUaGUgY2FsY3VsYXRpb24gb2YgZW52aXJvbm1lbnRhbCBpbXBhY3RzIGlzIGFuYWxvZ291cyB0byB0aGF0IG9mIGVuZXJneSAKcmVxdWlyZW1lbnRzLCBidXQgdXNlcyBkaWZmZXJlbnQgY29lZmZpY2llbnRzLgoKIyMjIENvbnN0cnVjdGlvbiBQaGFzZQoKRW52aXJvbm1lbnRhbCBpbXBhY3RzIGZyb20gY29uc3RydWN0aW9uIChhZ2FpbiBpbmNsdWRpbmcgdGhvc2Ugb2YgdGhlIAplbmQtb2YtbGlmZSkgYXJlIGNhbGN1bGF0ZWQgYnkgam9pbmluZyB0aGUgdGhlIHRhYmxlcyBvZiBjYXBhY2l0eSBhZGRpdGlvbnMgYW5kIAp0aGUgTENBIGNvZWZmaWNpZW50czoKYGBge3IgY2FsY3VsYXRlIGNhcGFjaXR5IGFkZGl0aW9ucyBlbnZpcm9ubWVudGFsIGltcGFjdHMsIHdhcm5pbmcgPSBGQUxTRX0KSUFNLkNhcGFjaXR5ICU+JSAKICAgIGZpbHRlcihwaGFzZSA9PSAiQ2FwYWNpdHkgQWRkaXRpb25zIikgJT4lIAogICAgc2VsZWN0KHRlY2hub2xvZ3ksIGNhcGFjaXR5LmFkZGl0aW9ucyA9IHZhbHVlLCB1bml0KQoKZW52aXJvbm1lbnRhbC5pbXBhY3RzICU+JQogICAgZmlsdGVyKHBoYXNlICVpbiUgYygiQ29uc3RydWN0aW9uIiwgIkVuZC1vZi1saWZlIikpICU+JQogICAgZ3JvdXBfYnkoc2NlbmFyaW8sIHJlZ2lvbiwgdGVjaG5vbG9neSwgdGVjaG5vbG9neS52YXJpYW50LCBpbXBhY3QsCiAgICAgICAgICAgICB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgcGVyaW9kKSAlPiUKICAgIHN1bW1hcmlzZShlbnZpcm9ubWVudGFsLmltcGFjdHMgPSBzdW0odmFsdWUpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUgCiAgICB1bml0ZSh1bml0LCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgc2VwID0gJy8nKSAlPiUgCiAgICBzZWxlY3QodGVjaG5vbG9neSwgaW1wYWN0LCBlbnZpcm9ubWVudGFsLmltcGFjdHMsIHVuaXQpCgppbm5lcl9qb2luKAogICAgSUFNLkNhcGFjaXR5ICU+JSAKICAgIGZpbHRlcihwaGFzZSA9PSAiQ2FwYWNpdHkgQWRkaXRpb25zIikgJT4lIAogICAgICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBjYXBhY2l0eS5hZGRpdGlvbnMgPSB2YWx1ZSwgdW5pdCksCiAgICAKICAgIGVudmlyb25tZW50YWwuaW1wYWN0cyAlPiUKICAgICAgICBmaWx0ZXIocGhhc2UgJWluJSBjKCJDb25zdHJ1Y3Rpb24iLCAiRW5kLW9mLWxpZmUiKSkgJT4lCiAgICAgICAgZ3JvdXBfYnkoc2NlbmFyaW8sIHJlZ2lvbiwgdGVjaG5vbG9neSwgdGVjaG5vbG9neS52YXJpYW50LCBpbXBhY3QsCiAgICAgICAgICAgICAgICAgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IsIHBlcmlvZCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKGVudmlyb25tZW50YWwuaW1wYWN0cyA9IHN1bSh2YWx1ZSkpICU+JQogICAgICAgIHVuZ3JvdXAoKSAlPiUgCiAgICAgICAgc2VsZWN0KHRlY2hub2xvZ3ksIGltcGFjdCwgZW52aXJvbm1lbnRhbC5pbXBhY3RzLCB1bml0Lm51bWVyYXRvciwKICAgICAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciksCiAgICAKICAgIGJ5ID0gJ3RlY2hub2xvZ3knCikgJT4lCiAgICAjIHgvTVcgKiBHVy9hICogMTAwMCBNVy9HVyA9IHgvYQogICAgbXV0YXRlKHZhbHVlID0gZW52aXJvbm1lbnRhbC5pbXBhY3RzICogY2FwYWNpdHkuYWRkaXRpb25zICogMWUzLAogICAgICAgICAgIHBoYXNlID0gIkNvbnN0cnVjdGlvbiIsCiAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciA9ICJ5ciIpICU+JSAKICAgIHVuaXRlKHVuaXQsIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yLCBzZXAgPSAnLycpICU+JSAKICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBwaGFzZSwgaW1wYWN0LCB2YWx1ZSwgdW5pdCkKYGBgCgojIyMgT3BlcmF0aW9uIFBoYXNlCgpMaWtlIGVuZXJneSByZXF1aXJlbWVudHMsIHRoZSBlbnZpcm9ubWVudGFsIGltcGFjdHMgb2YgdGhlIG9wZXJhdGlvbiBwaGFzZSBhcmUgCmNhbGN1bGF0ZWQgZnJvbSBMQ0EgY29lZmZpY2llbnRzIGFuZCBkYXRhIG9uIGVpdGhlciBjYXBhY2l0eSBhZGRpdGlvbnMKYGBge3IgY2FsY3VsYXRlIG9wZXJhdGlvbiBlbnZpcm9ubWVudGFsIGltcGFjdHMgYmFzZWQgb24gY2FwYWNpdHksIHdhcm5pbmcgPSBGQUxTRX0KSUFNLkNhcGFjaXR5ICU+JSAKICAgIGZpbHRlcihwaGFzZSA9PSAiQ2FwYWNpdHkiKSAlPiUgCiAgICBzZWxlY3QodGVjaG5vbG9neSwgY2FwYWNpdHkgPSB2YWx1ZSwgdW5pdCkKCmVudmlyb25tZW50YWwuaW1wYWN0cyAlPiUKICAgIGZpbHRlcihwaGFzZSA9PSAiT3BlcmF0aW9uIiwKICAgICAgICAgICB1bml0LmRlbm9taW5hdG9yID09ICJNVy95ciIpICU+JQogICAgdW5pdGUodW5pdCwgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IsIHNlcCA9ICcvJykgJT4lIAogICAgc2VsZWN0KHRlY2hub2xvZ3ksIGltcGFjdCwgZW52aXJvbm1lbnRhbC5pbXBhY3RzID0gdmFsdWUsIHVuaXQpCgppbm5lcl9qb2luKAogICAgSUFNLkNhcGFjaXR5ICU+JSAKICAgIGZpbHRlcihwaGFzZSA9PSAiQ2FwYWNpdHkiKSAlPiUgCiAgICAgICAgc2VsZWN0KHRlY2hub2xvZ3ksIGNhcGFjaXR5ID0gdmFsdWUsIHVuaXQpLAogICAgCiAgICBlbnZpcm9ubWVudGFsLmltcGFjdHMgJT4lCiAgICAgICAgZmlsdGVyKHBoYXNlID09ICJPcGVyYXRpb24iLAogICAgICAgICAgICAgICB1bml0LmRlbm9taW5hdG9yID09ICJNVy95ciIpICU+JQogICAgICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBpbXBhY3QsIGVudmlyb25tZW50YWwuaW1wYWN0cyA9IHZhbHVlLCAKICAgICAgICAgICAgICAgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IpLAogICAgCiAgICBieSA9ICd0ZWNobm9sb2d5JwopICU+JQogICAgICAgICMgeC9NV2EgKiBHVyAqIDEwMDAgTVcvR1cgPSB4L2EKICAgICAgICBtdXRhdGUodmFsdWUgPSBlbnZpcm9ubWVudGFsLmltcGFjdHMgKiBjYXBhY2l0eSAqIDFlMywKICAgICAgICAgICAgICAgcGhhc2UgPSAiT3BlcmF0aW9uIiwKICAgICAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciA9ICJ5ciIpICU+JQogICAgdW5pdGUodW5pdCwgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IsIHNlcCA9ICcvJykgJT4lIAogICAgc2VsZWN0KHRlY2hub2xvZ3ksIHBoYXNlLCBpbXBhY3QsIHZhbHVlLCB1bml0KQpgYGAKb3IgZWxlY3RyaWNpdHkgcHJvZHVjdGlvbiwgZGVwZW5kaW5nIG9uIHRoZSB0ZWNobm9sb2d5CmBgYHtyIGNhbGN1bGF0ZSBvcGVyYXRpb24gZW52aXJvbm1lbnRhbCBpbXBhY3RzIGJhc2VkIG9uIGVsZWN0cmljaXR5IHByb2R1Y3Rpb24sIHdhcm5pbmcgPSBGQUxTRX0KSUFNLkVsZWN0cmljaXR5LlByb2R1Y3Rpb24gJT4lIAogICAgc2VsZWN0KHRlY2hub2xvZ3ksIGVsZWN0cmljaXR5LnByb2R1Y3Rpb24gPSB2YWx1ZSwgdW5pdCkKCmVudmlyb25tZW50YWwuaW1wYWN0cyAlPiUgCiAgICBmaWx0ZXIocGhhc2UgPT0gIk9wZXJhdGlvbiIsCiAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciA9PSAia1doIikgJT4lCiAgICB1bml0ZSh1bml0LCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgc2VwID0gJy8nKSAlPiUgCiAgICBzZWxlY3QodGVjaG5vbG9neSwgaW1wYWN0LCBlbnZpcm9ubWVudGFsLmltcGFjdHMgPSB2YWx1ZSwgdW5pdCkKCmlubmVyX2pvaW4oCiAgICBJQU0uRWxlY3RyaWNpdHkuUHJvZHVjdGlvbiAlPiUgCiAgICAgICAgc2VsZWN0KHRlY2hub2xvZ3ksIGVsZWN0cmljaXR5LnByb2R1Y3Rpb24gPSB2YWx1ZSwgdW5pdCksCiAgICAKICAgIGVudmlyb25tZW50YWwuaW1wYWN0cyAlPiUgCiAgICAgICAgZmlsdGVyKHBoYXNlID09ICJPcGVyYXRpb24iLAogICAgICAgICAgICAgICB1bml0LmRlbm9taW5hdG9yID09ICJrV2giKSAlPiUKICAgICAgICBzZWxlY3QodGVjaG5vbG9neSwgaW1wYWN0LCBlbnZpcm9ubWVudGFsLmltcGFjdHMgPSB2YWx1ZSwgCiAgICAgICAgICAgICAgIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yKSwKICAgIAogICAgYnkgPSAndGVjaG5vbG9neScKKSAlPiUKICAgICAgICAjIHgva1doICogRUovYSAvICgzLjZlLTEyIEVKL2tXaCkgPSB4L2EKICAgICAgICBtdXRhdGUodmFsdWUgPSBlbnZpcm9ubWVudGFsLmltcGFjdHMgKiBlbGVjdHJpY2l0eS5wcm9kdWN0aW9uIC8gMy42ZS0xMiwKICAgICAgICAgICAgICAgcGhhc2UgPSAiT3BlcmF0aW9uIiwKICAgICAgICAgICAgICAgdW5pdC5kZW5vbWluYXRvciA9ICJ5ciIpICU+JQogICAgdW5pdGUodW5pdCwgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IsIHNlcCA9ICcvJykgJT4lIAogICAgc2VsZWN0KHRlY2hub2xvZ3ksIHBoYXNlLCBpbXBhY3QsIHZhbHVlLCB1bml0KQpgYGAKCiMjIyBMVUxVQyBFbWlzc2lvbnMKCkVtaXNzaW9ucyBmcm9tIGxhbmQgdXNlIGFuZCBsYW5kLXVzZSBjaGFuZ2UgYXJlIGNhbGN1bGF0ZWQgc2VwYXJhdGVseSBpbiB0aGUgCnNhbWUgbWFubmVyLCBidXQgYmFzZWQgb24gdGhlIGNvZWZmaWNpZW50cyBpbiBgTUFnUElFLmx1bHVjLmVtaXNzaW9uc2AgZnJvbSB0aGUgCk1BZ1BJRSBtb2RlbC4KCmBgYHtyIGNhbGN1bGF0ZSBlbmVyZ3kgcmVxdWlyZW1lbnRzIGFuZCBlbnZpcm9ubWVudGFsIGltcGFjdHMgZm9yIHJlYWwsIGluY2x1ZGUgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpzb3VyY2UoJy4vc2NyaXB0cy9jYWxjdWxhdGVfZW5lcmd5X3JlcXVpcmVtZW50cy5SJykKc291cmNlKCcuL3NjcmlwdHMvY2FsY3VsYXRlX2Vudmlyb25tZW50YWxfaW1wYWN0cy5SJykKYGBgCgojIyBXcml0ZSBPdXRwdXQKYGBge3Igb3V0cHV0IHByZXBhcmF0aW9uLCBpbmNsdWRlID0gRkFMU0V9CmlmICgtMSA9PSBmaWxlLmFjY2VzcygiLi9vdXRwdXQvIiwgbW9kZSA9IDYpKQogICAgZGlyLmNyZWF0ZSgiLi9vdXRwdXQvIiwgbW9kZSA9ICIwNzc1IikKYGBgCgpSZXN1bHRzIGZyb20gdGhlIGNhbGN1bGF0aW9ucyBhcmUgc2ltcGx5IGNvbmNhdGVuYXRlZCBhbmQgd3JpdHRlbiB0byBgLmNzdmAgCmZpbGVzIGluIHRoZSBzYW1lIGZvcm1hdCBhcyB0aGUgTENBIGlucHV0cy4KClRocmVlIGZpbGVzIGFyZSB3cml0dGVuOiBvbmUgZm9yIGJvdGggZW5lcmd5IHJlcXVpcmVtZW50cyBhbmQgZW52aXJvbm1lbnRhbCAKaW1wYWN0cywgYnV0IG9ubHkgZm9yIHRoZSBkZWZhdWx0IHRlY2hub2xvZ3kgdmFyaWFudC4KYGBge3Igd3JpdGUgbWl4IHZhcmlhbnQgb3V0cHV0fQpyYmluZCgKICAgIHJiaW5kKAogICAgICAgIElBTS5lbmVyZ3kucmVxdWlyZW1lbnRzLkNvbnN0cnVjdGlvbiwKICAgICAgICBJQU0uZW5lcmd5LnJlcXVpcmVtZW50cy5PcGVyYXRpb24KICAgICkgJT4lCiAgICAgICAgZmlsdGVyKHRlY2hub2xvZ3kudmFyaWFudCA9PSAibWl4IikgJT4lCiAgICAgICAgc2VsZWN0KC10ZWNobm9sb2d5LnZhcmlhbnQpICU+JQogICAgICAgIHVuaXRlKHZhcmlhYmxlLCBwaGFzZSwgcXVhbnRpdHksIGVuZXJneS5jYXJyaWVyLCB0ZWNobm9sb2d5LCBzZXAgPSAifCIpLAoKICAgIHJiaW5kKAogICAgICAgIElBTS5lbnZpcm9ubWVudGFsLmltcGFjdHMuQ29uc3RydWN0aW9uLAogICAgICAgIElBTS5lbnZpcm9ubWVudGxhLmltcGFjdHMuT3BlcmF0aW9uCiAgICApICU+JQogICAgICAgIGZpbHRlcih0ZWNobm9sb2d5LnZhcmlhbnQgPT0gIm1peCIpICU+JQogICAgICAgIHNlbGVjdCgtdGVjaG5vbG9neS52YXJpYW50KSAlPiUKICAgICAgICB1bml0ZSh2YXJpYWJsZSwgcGhhc2UsIGltcGFjdCwgdGVjaG5vbG9neSwgc2VwID0gInwiKSwKCiAgICBJQU0uTFVMVUMuZW1pc3Npb25zICU+JQogICAgICAgIGZpbHRlcih0ZWNobm9sb2d5LnZhcmlhbnQgPT0gIm1peCIpICU+JQogICAgICAgIG11dGF0ZSh2YXJpYWJsZSA9IHBhc3RlKCJFbWlzc2lvbnMiLCBlbWlzc2lvbnMsICJMVUxVQyIsIHRlY2hub2xvZ3ksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gInwiKSkgJT4lCiAgICAgICAgc2VsZWN0KG1vZGVsLCBzY2VuYXJpbywgcmVnaW9uLCB2YXJpYWJsZSwgdW5pdC5udW1lcmF0b3IsCiAgICAgICAgICAgICAgIHVuaXQuZGVub21pbmF0b3IsIHBlcmlvZCwgdmFsdWUpCikgJT4lCiAgICB1bml0ZSh1bml0LCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgc2VwID0gIi8iKSAlPiUKICAgIHNwcmVhZChwZXJpb2QsIHZhbHVlKSAlPiUKICAgIHdyaXRlLmNzdihwYXN0ZTAoIi4vb3V0cHV0LyIsIG91dHB1dF9maWxlKSwgcm93Lm5hbWVzID0gRkFMU0UsIG5hID0gIiIpCmBgYAoKQW5kIHR3byBmaWxlcyBmb3IgYWxsIHRlY2hub2xvZ3kgdmFyaWFudHMsIHdpdGggZW5lcmd5IHJlcXVpcmVtZW50cwpgYGB7ciB3cml0ZSBlbmVyZ3kgcmVxdWlyZW1lbnRzfQpyYmluZCgKICAgIElBTS5lbmVyZ3kucmVxdWlyZW1lbnRzLkNvbnN0cnVjdGlvbiwKICAgIElBTS5lbmVyZ3kucmVxdWlyZW1lbnRzLk9wZXJhdGlvbgopICU+JQogICAgIyBHSi9hICogMWUtOSBFSi9HSiA9IEVKL2EKICAgIG11dGF0ZSh2YWx1ZSA9IHZhbHVlICogMWUtOSwKICAgICAgICAgICB1bml0Lm51bWVyYXRvciA9ICJFSiIpICU+JQogICAgdW5pdGUodW5pdCwgdW5pdC5udW1lcmF0b3IsIHVuaXQuZGVub21pbmF0b3IsIHNlcCA9ICIvIikgJT4lCiAgICBzcHJlYWQocGVyaW9kLCB2YWx1ZSkgJT4lCiAgICB3cml0ZS5jc3Yoc3ViKCIoXi4qKShcXC5bXlxcLl0rJCkiLCAiLi9vdXRwdXQvXFwxX2VuZXJneVxcMiIsIG91dHB1dF9maWxlKSwKICAgICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSwgbmEgPSAiIikKYGBgCmFuZCBlbnZpcm9ubWVudGFsIGltcGFjdHMgc2VwYXJhdGVkLgpgYGB7ciB3cml0ZSBlbnZpcm9ubWVudGFsIGltcGFjdHN9CnJiaW5kKAogICAgSUFNLmVudmlyb25tZW50YWwuaW1wYWN0cy5Db25zdHJ1Y3Rpb24sCiAgICBJQU0uZW52aXJvbm1lbnRsYS5pbXBhY3RzLk9wZXJhdGlvbiwKCiAgICBJQU0uTFVMVUMuZW1pc3Npb25zICU+JQogICAgICAgIG11dGF0ZShwaGFzZSA9ICJPcGVyYXRpb24iLAogICAgICAgICAgICAgICBpbXBhY3QgPSBwYXN0ZSgiTFVMVUMiLCBlbWlzc2lvbnMsICJlbWlzc2lvbnMiKSkgJT4lCiAgICAgICAgc2VsZWN0KC1lbWlzc2lvbnMpCikgJT4lCiAgICB1bml0ZSh1bml0LCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgc2VwID0gIi8iKSAlPiUKICAgIHNwcmVhZChwZXJpb2QsIHZhbHVlKSAlPiUKICAgIHdyaXRlLmNzdihzdWIoIiheLiopKFxcLlteXFwuXSskKSIsICIuL291dHB1dC9cXDFfZW52aXJvbm1lbnRcXDIiLAogICAgICAgICAgICAgICAgICBvdXRwdXRfZmlsZSksIHJvdy5uYW1lcyA9IEZBTFNFLCBuYSA9ICIiKQpgYGAKCiMgRXhhbXBsZSBQbG90CgpBIHNpbXBsZSBleGFtcGxlIHBsb3QgZnJvbSB0aGUgZGF0YSB0aGlzIHR1dG9yaWFsIHVzZWQ6CmBgYHtyIGV4YW1wbGUgcGxvdCwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmdncGxvdCgpICsKICAgIGdlb21fY29sKAogICAgICAgIGRhdGEgPSBiaW5kX3Jvd3MoCiAgICAgICAgICAgIElBTS5lbmVyZ3kucmVxdWlyZW1lbnRzLkNvbnN0cnVjdGlvbiAlPiUgCiAgICAgICAgICAgICAgICB1bml0ZSh1bml0LCB1bml0Lm51bWVyYXRvciwgdW5pdC5kZW5vbWluYXRvciwgc2VwID0gJy8nKSAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUoaW1wYWN0ID0gcGFzdGUwKGVuZXJneS5jYXJyaWVyLCAnIFsnLCB1bml0LCAnXScpKSAlPiUgCiAgICAgICAgICAgICAgICBzZWxlY3QodGVjaG5vbG9neSwgaW1wYWN0LCBwaGFzZSwgdmFsdWUpLAogICAgICAgICAgICAKICAgICAgICAgICAgSUFNLmVudmlyb25tZW50YWwuaW1wYWN0cy5Db25zdHJ1Y3Rpb24gJT4lIAogICAgICAgICAgICAgICAgbXV0YXRlKGltcGFjdCA9IHBhc3RlMChpbXBhY3QsICcgWycsIHVuaXQubnVtZXJhdG9yLCAnLycsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bml0LmRlbm9taW5hdG9yLCAnXScpKSAlPiUgCiAgICAgICAgICAgICAgICBzZWxlY3QodGVjaG5vbG9neSwgaW1wYWN0LCBwaGFzZSwgdmFsdWUpLAogICAgICAgICAgICAKICAgICAgICAgICAgSUFNLmVuZXJneS5yZXF1aXJlbWVudHMuT3BlcmF0aW9uICU+JSAKICAgICAgICAgICAgICAgIHVuaXRlKHVuaXQsIHVuaXQubnVtZXJhdG9yLCB1bml0LmRlbm9taW5hdG9yLCBzZXAgPSAnLycpICU+JSAKICAgICAgICAgICAgICAgIG11dGF0ZShpbXBhY3QgPSBwYXN0ZTAoZW5lcmd5LmNhcnJpZXIsICcgWycsIHVuaXQsICddJykpICU+JSAKICAgICAgICAgICAgICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBpbXBhY3QsIHBoYXNlLCB2YWx1ZSksCiAgICAgICAgICAgIAogICAgICAgICAgICBJQU0uZW52aXJvbm1lbnRsYS5pbXBhY3RzLk9wZXJhdGlvbiAlPiUgCiAgICAgICAgICAgICAgICBtdXRhdGUoaW1wYWN0ID0gcGFzdGUwKGltcGFjdCwgJyBbJywgdW5pdC5udW1lcmF0b3IsICcvJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXQuZGVub21pbmF0b3IsICddJykpICU+JSAKICAgICAgICAgICAgICAgIHNlbGVjdCh0ZWNobm9sb2d5LCBpbXBhY3QsIHBoYXNlLCB2YWx1ZSkKICAgICAgICApLAogICAgICAgIAogICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHRlY2hub2xvZ3ksIHkgPSB2YWx1ZSwgZmlsbCA9IHBoYXNlKSkgKwogICAgZmFjZXRfd3JhcCh+aW1wYWN0LCBzY2FsZXMgPSAnZnJlZScpICsKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMLCAKICAgICAgICAgdGl0bGUgPSAnRXhhbXBsZSBFbmVyZ3kgRGVtYW5kcyBhbmQgRW52aXJvbm1lbnRhbCBJbXBhY3RzJykgKwogICAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gTlVMTCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScpCmBgYAoKW14xXTogQXQgdGhpcyBzdGVwLCB0aGUgSUFNIGFuZCBMQ0EgcmVnaW9ucyBhcmUgYWxzbyBtYXRjaGVkLCB3aGljaCBpcyBub3QgCiAgICAgIHNob3duIGhlcmUuCiAgICAgIAo=