This is a quickly pared down set of a much longer project I started working several months ago (before covid-19). It’s not particularly pretty, but I wanted to make this relatively succinct and easy to follow.

requirements and initialization


library(tidyverse) # dplyr, ggplot, etc
library(janitor)   # column renaming
library(reshape2)  # restructuring data via melt() and dcast()
library(rsdmx)     # API to fetch data from OECD via SDMX (XML)
library(ggpmisc)   # printing formula and fit statistics on plots 
library(ggrepel)   # smarter plot labels
library(hrbrthemes) # theme for ggplot
require(Hmisc)      # pretty printing


fetchSDMXUrl=function(url)
{
  as.data.frame(readSDMX(url))
}


exclude_usFn=function(df) {
  filter(df,location!='USA')
}


simpleData=list()

Make “multiplier” table to convert values expressed in current LCU to various other measures, especially PPPs.

I do this by fetching transactions corresponding to GDP and AIC from OECD’s Main SNA table expressed with measures for both in:

  1. Current Dollars (C)
  2. constant PPPs, per capita (HVPVOB)
  3. current PPPs per capita (HCPC)

The ratios between these measures, specifically Current LCUs to the PPPs define the multiplier to be used for each type.

OECD also publishes PPPs for AIC and GDP, but only in current dollar terms, insofar as I am aware. This method makes it much easier.




getPPPMultipliers=function(url) {
  


  rawData=fetchSDMXUrl(url) %>%
    clean_names() %>%
    filter(
      time_format=='P1Y', # annual
      transact %in% c('B1_GE','P41'), # GDP (expenditure method) and AIC
      measure %in% c('C','HVPVOB','HCPC') # current dollars (nominal LCU) and per capita PPP adjusted in constant and current terms
    ) %>%
    transmute(
      location,
      year=as.integer(obs_time),
      transact,
      measure,
      value=obs_value
    )  


  rawMult=rawData %>%
    group_by(location,year,transact) %>%
    mutate(
      # extract the figure expressed in current LCU for each location, year, and transaction set.
      ref_value=mean(ifelse(measure=='C',value,NA),na.rm=T) 
    ) %>%
    ungroup() %>%
    transmute(
      location,
      year,
      multiplier=value/ref_value,
      multiplier_type=case_when(
        measure=='C' ~ 'CURRENT_LCU_MILLIONS',
        T ~ sprintf(
          "%s_%s_PER_CAPITA",
          ifelse(transact=='P41','AIC','GDP'),
          ifelse(measure=='HVPVOB','CONSTANT','CURRENT')
        )
      )
    ) %>%
    unique() # we dont need multiple results for Current/LCU!
  
    
  return(rawMult)

}


url='https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/SNA_TABLE1/AUS+AUT+BEL+CAN+CHL+CZE+DNK+EST+FIN+FRA+DEU+GRC+HUN+ISL+IRL+ISR+ITA+JPN+KOR+LVA+LTU+LUX+MEX+NLD+NZL+NOR+POL+PRT+SVK+SVN+ESP+SWE+CHE+TUR+GBR+USA+EA19+EU28+EU15+OECDE+OECD+OTF+NMEC+ARG+BRA+BGR+CPV+CHN+COL+CRI+HRV+CYP+HKG+IND+IDN+MDG+MLT+MAR+MKD+PER+ROU+RUS+SAU+SRB+ZAF+ZMB+FRME+DEW.B1_GE+P41.C+HCPC+HVPVOB/all?startTime=1970&endTime=2019'

simpleData$ppp_conversion=getPPPMultipliers(url)

Get population figures from OECD



getPops=function(url) {
  
  fetchSDMXUrl(url) %>%
    clean_names() %>%
    filter( # not necessary to filter this dataset, but just to be clear....
      time_format == 'P1Y', 
      transact == 'POPNC'
    ) %>% 
    transmute(
      location,
      year=as.integer(obs_time),
      pop=obs_value # thousands
    )
  
}

url='https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/SNA_TABLE3/AUS+AUT+BEL+CAN+CHL+CZE+DNK+EST+FIN+FRA+DEU+GRC+HUN+ISL+IRL+ISR+ITA+JPN+KOR+LVA+LTU+LUX+MEX+NLD+NZL+NOR+POL+PRT+SVK+SVN+ESP+SWE+CHE+TUR+GBR+USA+EA19+EU28+EU15+OECDE+OECD+OTF+NMEC+ARG+BRA+BGR+CPV+CHN+COL+CRI+HRV+CYP+IND+IDN+MDG+MLT+MAR+MKD+PER+ROU+RUS+SAU+SRB+ZAF+ZMB+FRME+DEW.POPNC.PER/all?startTime=1970&endTime=2018'
simpleData$pop=getPops(url)

Get transactions from detailed non-financial accounts (SNA)

Use OECD’s SNA table for non-financial accounts by sector to obtain more granular SNA data for different sectors and transaction types. Although I use this for several other purposes (hence the extra transactions and sectors), this provides the transactions in LCU for gross adjusted disposable income and consumption of fixed capital for the household sector.

HH gross adjusted disposable income less their consumption of fixed capital equals net adjusted household disposable income.


getSectoral=function(url,priceCW) {
  sectoral_raw=fetchSDMXUrl(url) %>%
    clean_names()
    
    nfa_sector_cw=tribble(
      ~sector,~sec_short,~sec_long,
      'S1','TOT','Total economy',
      'S11','NFC','Non-Financial corporations',
      'S12','FIN','Financial corporations',
      'S13','GOV','Geneal Government',
      'S14_S15','HH','Households and non-profit institutions serving households'
    )
  
  
  nfa_transact_cw=tribble(
    ~transact,~tx_short,~tx_long,
    'NFB1GR','GVA','Gross Domestic Product / Gross Value Add',
    'NFB5GR','GPI','Gross National Income / Balance of Primary Income',
    'NFB6GP','GDI','Gross Disposable Income',
    'NFB7GP','AGDI','Adjusted Gross Disposable Income',
    'NFB8GP','GS','Gross Saving',
    'NFK1P','CFC','Consumption of Fixed Capital',  
    'NFP5P','GCF','Gross Capital Formation',
    'NFB9P','NL','Net Lending (+), Net Borrowing (-)'
  )
  
  sectoral_long=sectoral_raw %>%
    merge(nfa_sector_cw,by='sector') %>%
    merge(nfa_transact_cw,by='transact') %>%
    transmute(
      location,
      year=as.integer(obs_time),
      sector=sec_short,
      transaction=tx_short,
      value=obs_value
    ) %>%
    merge(priceCW,by=c('location','year')) %>%
    mutate(
      raw_value=value,
      value=raw_value*multiplier
    )
  
  
  # subtract consumption of fixed capital from disposable income, standard and "adjusted" (+STiK)
  # to derive net * disposable income
  sectoral_net_disposable=sectoral_long %>%
    filter(
      transaction %in% c('CFC','GDI','AGDI')
    ) %>%
    dcast(location+year+sector+multiplier_type~transaction) %>%
    mutate(
      ANDI=AGDI-CFC,
      NDI=GDI-CFC
    ) %>%
    melt(measure.vars=c('ANDI','NDI')) %>%
    mutate(
      transaction=as.character(variable)
    ) %>%
    select(-variable)
  
  sectoral_wide=sectoral_long %>%
    bind_rows(sectoral_net_disposable) %>%
    mutate(
      variable=str_to_lower(sprintf("nfa_%s_%s",transaction,sector))
    ) %>%
    dcast(location+year+multiplier_type~variable)
  
  return(sectoral_wide)

}

url='https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/SNA_TABLE14A/AUS+AUT+BEL+CAN+CHL+CZE+DNK+EST+FIN+FRA+DEU+GRC+HUN+ISL+IRL+ISR+ITA+JPN+KOR+LVA+LTU+LUX+MEX+NLD+NZL+NOR+POL+PRT+SVK+SVN+ESP+SWE+CHE+TUR+GBR+USA+EA19+EU28+NMEC+BRA+BGR+CPV+CHN+COL+CRI+HRV+CYP+HKG+IND+IDN+MDG+MLT+MAR+MKD+PER+ROU+RUS+SAU+SRB+ZAF+ZMB+FRME+DEW.NFB5GR+NFB7GP+NFB6GP+NFB8GP+NFP5P+NFK1P+NFB9P+NFB1GR.S1+S11+S12+S13+S14_S15.C/all?startTime=1970&endTime=2018'


simpleData$sectoral=getSectoral(url,simpleData$ppp_conversion)

Get current health expenditures from OECD’s SHA tables

Get SHA health expenditures from OECD. I am fetching this in current LCU and converting them locally to be consistent with the other figures used. OECD does provide measures in per capita AIC PPPs, constant and current. I’ve found the results to be highly comparable, with some rare exceptions, however this ensures the population and PPP values are consistent throughout my analysis. Using their PPP-adjusted figures instead will not substantively change my analysis.


getSHAExpend=function(url,priceCW) {
  
  hce=fetchSDMXUrl(url) %>%
    clean_names() %>%
    filter(
      hf=='HFTOT',
      hc=='HCTOT',
      hp=='HPTOT'
    ) %>%
    transmute(
        location,
        year=as.integer(obs_time),
        variable=sprintf("sha_hce_%s",measure),
        value=obs_value
    ) %>%
    dcast(location+year~variable) %>%
    merge(priceCW,by=c('location','year')) %>%
    mutate(
      sha_hce_adj=sha_hce_MLLNCU*multiplier
    )
  
  return(hce)

}


url='https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/SHA/HFTOT.HCTOT.HPTOT.MLLNCU.AUS+AUT+BEL+CAN+CHL+COL+CZE+DNK+EST+FIN+FRA+DEU+GRC+HUN+ISL+IRL+ISR+ITA+JPN+KOR+LVA+LTU+LUX+MEX+NLD+NZL+NOR+POL+PRT+SVK+SVN+ESP+SWE+CHE+TUR+GBR+USA+BRA+CHN+CRI+IND+IDN+RUS+ZAF/all?startTime=1970&endTime=2019'


simpleData$hce=getSHAExpend(url,simpleData$ppp_conversion)

Get GDP, AIC, and some other NA aggregates for analysis from OECD’s SNA Table 1

Fetch several transactions corresponding to more widely understood SNA aggregates in Current LCU from OECD’s Main SNA table.


getSNA1Basic=function(url,priceCW) {
  
  gdp_disagg_cw=tribble(
    ~transact,~short_lab,~name,
    'B1_GE','gdp','GDP (expenditure approach)',
    'P41','aic','Actual Individual Consumption', 
    'P3','final_consumption','Final Consumption',
    'P5','gross_capital_formation','Gross Capital Formation',
    'B11','net_exports','Net Exports',
    'P31S13','gov_consumption_ind','Government Consumption: Individual',
    'P32S13','gov_consumption_col','Government Consumption: Collective',
    'P3S13','gov_consumption_all','Government Consumption (combined)',
    'P31S14_S15','hh_consumption','Household and NPISH consumption',
    'P6','exports','Exports',
    'P7','imports','Imports',
    'P51','gross_fixed_capital_formation','Gross Fixed Capital Formation',
    'P52_P53','changes_in_inventories','Changes in inventories (stocks)',
    'P3_P5','domestic_demand','Domestic Demand'
  )
  
  gdp_disagg_raw=fetchSDMXUrl(url) %>%
    clean_names() %>%
    transmute(
      location,
      year=as.integer(obs_time),
      transact,
      value=obs_value
    )

  gdp_disagg_long=merge(gdp_disagg_raw,gdp_disagg_cw,by='transact',all.x=T) %>%
    merge(priceCW,by=c('location','year')) %>%
    mutate(
      raw_value=value,
      value=raw_value*multiplier
    )
  
  
  gdp_disagg_wide= gdp_disagg_long %>%
    mutate(
      variable=sprintf("sna1_%s",short_lab)
    ) %>%
    dcast(location+year+multiplier_type~variable)  
  
  return(gdp_disagg_wide)  
}


url='https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/SNA_TABLE1/AUS+AUT+BEL+CAN+CHL+COL+CZE+DNK+EST+FIN+FRA+DEU+GRC+HUN+ISL+IRL+ISR+ITA+JPN+KOR+LVA+LTU+LUX+MEX+NLD+NZL+NOR+POL+PRT+SVK+SVN+ESP+SWE+CHE+TUR+GBR+USA+EA19+EU28+EU15+OECDE+OECD+OTF+NMEC+ALB+ARG+BRA+BGR+CPV+CHN+CRI+HRV+CYP+HKG+IND+IDN+MDG+MLT+MAR+MKD+PER+ROU+RUS+SAU+SRB+SGP+ZAF+ZMB+FRME+DEW.B1_GE+P3_P5+P3+P31S14_S15+P3S13+P41.C/all?startTime=1970&endTime=2019'

simpleData$key_aggregates=getSNA1Basic(url,simpleData$ppp_conversion)

bring the basic data together




simpleData$merged=simpleData$hce %>%
  merge(simpleData$sectoral,by=c('location','year','multiplier_type'),all=T) %>%
  merge(simpleData$key_aggregates,by=c('location','year','multiplier_type'),all=T) %>%
  merge(simpleData$pop,by=c('location','year'),all=T)

simpleData$often_referenced=simpleData$merged %>%
  transmute(
    year,
    location,
    multiplier_type,
    pop,         # population
    sha_hce_adj, # current health expenditures (SHA)
    nfa_andi_hh, # net adjusted household disposable income
    nfa_agdi_hh, # gross adjusted household disposable income
    sna1_gdp,    # GDP
    sna1_aic     # Actual Individual Consumption
  ) %>%
  filter( 
    !is.na(multiplier_type)  
  )




# available PPP-related conversions
cat(unique(simpleData$often_referenced$multiplier_type))
GDP_CONSTANT_PER_CAPITA GDP_CURRENT_PER_CAPITA CURRENT_LCU_MILLIONS AIC_CONSTANT_PER_CAPITA AIC_CURRENT_PER_CAPITA

You will want to be sure to filter these data by the appropriate field in multiplier_type!!

sample usage, number one



exclude_usFn=function(df) {
  filter(df,location!='USA')
}

simpleData$often_referenced %>%
  filter(
    year==2017,
    !is.na(nfa_andi_hh),
    !is.na(sha_hce_adj),
    
    # filter by the desired multiplier or you will get weird results!!!
    multiplier_type=='AIC_CONSTANT_PER_CAPITA', 
    
    # exclude Luxembourg and South Africa
    !location %in% c('ZAF','LUX'), 
    T
  ) %>%
  ggplot(aes(log(nfa_andi_hh),log(sha_hce_adj) )) +
    geom_point(color='red',aes(size=pop),alpha=0.7) +
    scale_y_continuous(
      sec.axis = sec_axis(
        ~exp(.),
        labels=dollar_format(),
        breaks=exp(seq(1,20,.5))
      )
    ) +
    scale_x_continuous(
      sec.axis = sec_axis(
        ~exp(.),
        labels=dollar_format(),
        breaks=exp(seq(1,20,.2))
      )
    ) +

    geom_text_repel(aes(label=location)) +
    theme_ipsum() +
    geom_smooth(
      method=lm,
      data=exclude_usFn,
      fullrange=T
    ) +
    labs(
      x='log 2017 net adjusted household disposable income per capita\nat PPPs for AIC (constant dollars)',
      y='log 2017 current heatlh expendiures per capita\nat PPPs for AIC (constant dollars)'
      
    ) +
    stat_poly_eq(
      aes(label =  paste(stat(eq.label),stat(rr.label), sep = "*\", \"*")),
      formula = 'y~x',
      parse = TRUE,
      data=exclude_usFn
    ) 

NA
NA
NA

sample usage, number two




simpleData$often_referenced %>%
  filter(
    year==2017,
    !is.na(nfa_andi_hh),
    !is.na(sna1_aic),
    multiplier_type=='AIC_CONSTANT_PER_CAPITA',
    T
  ) %>%
  ggplot(aes(log(nfa_andi_hh),log(sna1_aic) )) +
    geom_point(color='red',aes(size=pop),alpha=0.7) +
    geom_abline(slope=1) +
    scale_y_continuous(
      sec.axis = sec_axis(
        ~exp(.),
        labels=dollar_format(accuracy=1),
        breaks=exp(seq(1,20,.5))
      )
    ) +
    scale_x_continuous(
      sec.axis = sec_axis(
        ~exp(.),
        labels=dollar_format(accuracy=1),
        breaks=exp(seq(1,20,.2))
      )
    ) +

    geom_text_repel(aes(label=location)) +
    theme_ipsum() +
    geom_smooth(
      method=lm,
      data=exclude_usFn,
      fullrange=T
    ) +
    labs(
      x='log 2017 net adjusted household disposable income per capita\nat PPPs for AIC (constant dollars)',
      y='log 2017 AIC per capita\nat PPPs for AIC (constant dollars)'
      
    ) +
    stat_poly_eq(
      aes(label =  paste(stat(eq.label),stat(rr.label), sep = "*\", \"*")),
      formula = 'y~x',
      parse = TRUE,
      data=exclude_usFn
    ) 

NA
NA
NA

compare to BLI Net AHDI figures

Extract Net Adjusted Household Disposable Income per capita from the Better Life Index and compare. This figure is presumably for 2016 and in current PPPs. I’d hope it’d be closer (last time I looked it was!). Still, I think it’s close enough. Statistical discrepancies can arise because of revisions to the underlying data set. It’s more likely the BLI data are a bit outdated than sources I am pulling this.


url='https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/BLI/AUS+AUT+BEL+CAN+CHL+COL+CZE+DNK+EST+FIN+FRA+DEU+GRC+HUN+ISL+IRL+ISR+ITA+JPN+KOR+LVA+LTU+LUX+MEX+NLD+NZL+NOR+POL+PRT+SVK+SVN+ESP+SWE+CHE+TUR+GBR+USA+OECD+NMEC+BRA+RUS+ZAF.HO_BASE+HO_HISH+HO_NUMR+IW_HADI+IW_HNFW.L.TOT/all?'

bliData=fetchSDMXUrl(url) %>%
    clean_names() %>%
    dcast(location~indicator,value.var='obs_value') %>%
    mutate(
      year=2016 
    )



simpleData$often_referenced %>%
  filter(
    multiplier_type == 'AIC_CURRENT_PER_CAPITA',
    T
  ) %>%
  merge(bliData,by=c('location','year')) %>%
  filter(
    !is.na(IW_HADI),
    !is.na(nfa_andi_hh)
  ) %>%
  ggplot(aes(log(nfa_andi_hh),log(IW_HADI) )) +
    #geom_abline(slope=1) +
    geom_point(color='red',aes(size=pop),alpha=0.5) +
    geom_text_repel(aes(label=location)) +  
    geom_smooth(
      method=lm,
      fullrange=T
    ) +
    labs(
      x='log 2016 net adjusted household disposable income per capita\nat PPPs for AIC (current dollars)\nper my calculations',
      y='log 2016 net adjusted household disposable income per capita\nat PPPs for AIC (current dollars)\nper OECD\'s 2019 Better Life Index'
    ) +
    stat_poly_eq(
      aes(label =  paste(stat(eq.label),stat(rr.label), sep = "*\", \"*")),
      formula = 'y~x',
      parse = TRUE
    ) +
    theme_ipsum()

NA

crosscheck with OECD NAAG

Use estimates published in OECD’s National Accounts At A Glance for gross adjusted household disposable income in current prices, current PPPs (AIC) to cross-check my figures. These correspond exactly.

crosscheck against GDP and AIC

These match exactly (+/- negligible floating point precision error)



url='https://stats.oecd.org/restsdmx/sdmx.ashx/GetData/SNA_TABLE1/AUS+AUT+BEL+CAN+CHL+COL+CZE+DNK+EST+FIN+FRA+DEU+GRC+HUN+ISL+IRL+ISR+ITA+JPN+KOR+LVA+LTU+LUX+MEX+NLD+NZL+NOR+POL+PRT+SVK+SVN+ESP+SWE+CHE+TUR+GBR+USA+EA19+EU28+EU15+OECDE+OECD+OTF+NMEC+ALB+ARG+BRA+BGR+CPV+CHN+CRI+HRV+CYP+HKG+IND+IDN+MDG+MLT+MAR+MKD+PER+ROU+RUS+SAU+SRB+SGP+ZAF+ZMB+FRME+DEW.B1_GE+P41.HCPC+HVPVOB/all?startTime=1970&endTime=2019'

basicAggs=fetchSDMXUrl(url) %>%
  clean_names() %>%
  mutate(
    year=as.integer(obs_time),
    variable=sprintf("%s_%s",transact,measure)
  ) %>%
  dcast(location+year~variable,value.var='obs_value')




basicAggs %>%
  merge(simpleData$often_referenced,by=c('location','year')) %>%
  filter(
    year %in% c(2005,2010,2015,2018),
    multiplier_type=='AIC_CONSTANT_PER_CAPITA'
  ) %>%
  ggplot(aes(log(sna1_aic),log(P41_HVPVOB))) +
    geom_abline(slope=1) +
    facet_wrap(~year,scale='free_y') +
    geom_point(color='red',aes(size=pop),alpha=0.7) +
    geom_smooth(
      method=lm,
      fullrange=T
    ) +
    stat_poly_eq(
      aes(label =  paste(stat(eq.label),stat(rr.label), sep = "*\", \"*")),
      formula = 'y~x',
      parse = TRUE
    ) +
    theme_ipsum() +
    guides(size='none') +
    labs(
      x='log AIC per capita at constant PPPs for AIC (my side)',
      y='log AIC per capita at constant PPPs for AIC (OECD side)'
    )




basicAggs %>%
  merge(simpleData$often_referenced,by=c('location','year')) %>%
  filter(
    year %in% c(2005,2010,2015,2018),
    multiplier_type=='AIC_CURRENT_PER_CAPITA'
  ) %>%
  ggplot(aes(log(sna1_aic),log(P41_HCPC))) +
    geom_abline(slope=1) +
    facet_wrap(~year,scale='free_y') +
    geom_point(color='red',aes(size=pop),alpha=0.7) +
    geom_smooth(
      method=lm,
      fullrange=T
    ) +
    stat_poly_eq(
      aes(label =  paste(stat(eq.label),stat(rr.label), sep = "*\", \"*")),
      formula = 'y~x',
      parse = TRUE
    ) +
    theme_ipsum() +
    guides(size='none') +
    labs(
      x='log AIC per capita at current PPPs for AIC (my side)',
      y='log AIC per capita at current PPPs for AIC (OECD side)'
    )





basicAggs %>%
  merge(simpleData$often_referenced,by=c('location','year')) %>%
  filter(
    year %in% c(2005,2010,2015,2018),
    multiplier_type=='GDP_CONSTANT_PER_CAPITA'
  ) %>%
  ggplot(aes(log(sna1_gdp),log(B1_GE_HVPVOB))) +
    geom_abline(slope=1) +
    facet_wrap(~year,scale='free_y') +
    geom_point(color='red',aes(size=pop),alpha=0.7) +
    geom_smooth(
      method=lm,
      fullrange=T
    ) +
    stat_poly_eq(
      aes(label =  paste(stat(eq.label),stat(rr.label), sep = "*\", \"*")),
      formula = 'y~x',
      parse = TRUE
    ) +
    theme_ipsum() +
    guides(size='none') +
    labs(
      x='log GDP per capita at constant PPPs for GDP (my side)',
      y='log GDP per capita at constant PPPs for GDP (OECD side)'
    )




basicAggs %>%
  merge(simpleData$often_referenced,by=c('location','year')) %>%
  filter(
    year %in% c(2005,2010,2015,2018),
    multiplier_type=='GDP_CURRENT_PER_CAPITA'
  ) %>%
  ggplot(aes(log(sna1_gdp),log(B1_GE_HCPC))) +
    geom_abline(slope=1) +
    facet_wrap(~year,scale='free_y') +
    geom_point(color='red',aes(size=pop),alpha=0.7) +
    geom_smooth(
      method=lm,
      fullrange=T
    ) +
    stat_poly_eq(
      aes(label =  paste(stat(eq.label),stat(rr.label), sep = "*\", \"*")),
      formula = 'y~x',
      parse = TRUE
    ) +
    theme_ipsum() +
    guides(size='none') +
    labs(
      x='log GDP per capita at current PPPs for GDP (my side)',
      y='log GDP per capita at current PPPs for GDP (OECD side)'
    )

LS0tDQp0aXRsZTogIlNpbXBsaWZpZWQgY29kZSB0byBwcm9kdWNlIGJhc2ljIGFuYWx5c2VzIG9mIGhlYWx0aCBzcGVuZGluZyB3aXRoaW4gT0VDRCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCiAgICB0aGVtZTogam91cm5hbA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAyDQotLS0NCg0KDQpUaGlzIGlzIGEgcXVpY2tseSBwYXJlZCBkb3duIHNldCBvZiBhIG11Y2ggbG9uZ2VyIHByb2plY3QgSSBzdGFydGVkIHdvcmtpbmcgc2V2ZXJhbCBtb250aHMgYWdvIChiZWZvcmUgY292aWQtMTkpLiBJdCdzIG5vdCBwYXJ0aWN1bGFybHkgcHJldHR5LCBidXQgSSB3YW50ZWQgdG8gbWFrZSB0aGlzIHJlbGF0aXZlbHkgc3VjY2luY3QgYW5kIGVhc3kgdG8gZm9sbG93Lg0KDQoNCg0KIyMgcmVxdWlyZW1lbnRzIGFuZCBpbml0aWFsaXphdGlvbg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBkcGx5ciwgZ2dwbG90LCBldGMNCmxpYnJhcnkoamFuaXRvcikgICAjIGNvbHVtbiByZW5hbWluZw0KbGlicmFyeShyZXNoYXBlMikgICMgcmVzdHJ1Y3R1cmluZyBkYXRhIHZpYSBtZWx0KCkgYW5kIGRjYXN0KCkNCmxpYnJhcnkocnNkbXgpICAgICAjIEFQSSB0byBmZXRjaCBkYXRhIGZyb20gT0VDRCB2aWEgU0RNWCAoWE1MKQ0KbGlicmFyeShnZ3BtaXNjKSAgICMgcHJpbnRpbmcgZm9ybXVsYSBhbmQgZml0IHN0YXRpc3RpY3Mgb24gcGxvdHMgDQpsaWJyYXJ5KGdncmVwZWwpICAgIyBzbWFydGVyIHBsb3QgbGFiZWxzDQpsaWJyYXJ5KGhyYnJ0aGVtZXMpICMgdGhlbWUgZm9yIGdncGxvdA0KcmVxdWlyZShIbWlzYykgICAgICAjIHByZXR0eSBwcmludGluZw0KDQoNCmZldGNoU0RNWFVybD1mdW5jdGlvbih1cmwpDQp7DQogIGFzLmRhdGEuZnJhbWUocmVhZFNETVgodXJsKSkNCn0NCg0KDQpleGNsdWRlX3VzRm49ZnVuY3Rpb24oZGYpIHsNCiAgZmlsdGVyKGRmLGxvY2F0aW9uIT0nVVNBJykNCn0NCg0KDQpzaW1wbGVEYXRhPWxpc3QoKQ0KDQpgYGANCg0KDQojIyBNYWtlICJtdWx0aXBsaWVyIiB0YWJsZSB0byBjb252ZXJ0IHZhbHVlcyBleHByZXNzZWQgaW4gY3VycmVudCBMQ1UgdG8gdmFyaW91cyBvdGhlciBtZWFzdXJlcywgZXNwZWNpYWxseSBQUFBzLg0KDQpJIGRvIHRoaXMgYnkgZmV0Y2hpbmcgdHJhbnNhY3Rpb25zIGNvcnJlc3BvbmRpbmcgdG8gR0RQIGFuZCBBSUMgZnJvbSBbT0VDRCdzIE1haW4gU05BIHRhYmxlXShodHRwczovL3N0YXRzLm9lY2Qub3JnL0luZGV4LmFzcHg/RGF0YVNldENvZGU9U05BX1RBQkxFMSkgZXhwcmVzc2VkIHdpdGggbWVhc3VyZXMgZm9yIGJvdGggaW46DQoNCiAgKDEpIEN1cnJlbnQgRG9sbGFycyAoQykgDQogICgyKSBjb25zdGFudCBQUFBzLCBwZXIgY2FwaXRhIChIVlBWT0IpDQogICgzKSBjdXJyZW50IFBQUHMgcGVyIGNhcGl0YSAoSENQQykNCg0KVGhlIHJhdGlvcyBiZXR3ZWVuIHRoZXNlIG1lYXN1cmVzLCBzcGVjaWZpY2FsbHkgQ3VycmVudCBMQ1VzIHRvIHRoZSBQUFBzIGRlZmluZSB0aGUgbXVsdGlwbGllciB0byBiZSB1c2VkIGZvciBlYWNoIHR5cGUuDQoNCk9FQ0QgYWxzbyBbcHVibGlzaGVzIFBQUHMgZm9yIEFJQyBhbmQgR0RQXShodHRwczovL3N0YXRzLm9lY2Qub3JnL0luZGV4LmFzcHg/RGF0YVNldENvZGU9U05BX1RBQkxFNCksIGJ1dCBvbmx5IGluIGN1cnJlbnQgZG9sbGFyIHRlcm1zLCBpbnNvZmFyIGFzIEkgYW0gYXdhcmUuICBUaGlzIG1ldGhvZCBtYWtlcyBpdCBtdWNoIGVhc2llci4NCg0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCg0KDQpnZXRQUFBNdWx0aXBsaWVycz1mdW5jdGlvbih1cmwpIHsNCiAgDQoNCg0KICByYXdEYXRhPWZldGNoU0RNWFVybCh1cmwpICU+JQ0KICAgIGNsZWFuX25hbWVzKCkgJT4lDQogICAgZmlsdGVyKA0KICAgICAgdGltZV9mb3JtYXQ9PSdQMVknLCAjIGFubnVhbA0KICAgICAgdHJhbnNhY3QgJWluJSBjKCdCMV9HRScsJ1A0MScpLCAjIEdEUCAoZXhwZW5kaXR1cmUgbWV0aG9kKSBhbmQgQUlDDQogICAgICBtZWFzdXJlICVpbiUgYygnQycsJ0hWUFZPQicsJ0hDUEMnKSAjIGN1cnJlbnQgZG9sbGFycyAobm9taW5hbCBMQ1UpIGFuZCBwZXIgY2FwaXRhIFBQUCBhZGp1c3RlZCBpbiBjb25zdGFudCBhbmQgY3VycmVudCB0ZXJtcw0KICAgICkgJT4lDQogICAgdHJhbnNtdXRlKA0KICAgICAgbG9jYXRpb24sDQogICAgICB5ZWFyPWFzLmludGVnZXIob2JzX3RpbWUpLA0KICAgICAgdHJhbnNhY3QsDQogICAgICBtZWFzdXJlLA0KICAgICAgdmFsdWU9b2JzX3ZhbHVlDQogICAgKSAgDQoNCg0KICByYXdNdWx0PXJhd0RhdGEgJT4lDQogICAgZ3JvdXBfYnkobG9jYXRpb24seWVhcix0cmFuc2FjdCkgJT4lDQogICAgbXV0YXRlKA0KICAgICAgIyBleHRyYWN0IHRoZSBmaWd1cmUgZXhwcmVzc2VkIGluIGN1cnJlbnQgTENVIGZvciBlYWNoIGxvY2F0aW9uLCB5ZWFyLCBhbmQgdHJhbnNhY3Rpb24gc2V0Lg0KICAgICAgcmVmX3ZhbHVlPW1lYW4oaWZlbHNlKG1lYXN1cmU9PSdDJyx2YWx1ZSxOQSksbmEucm09VCkgDQogICAgKSAlPiUNCiAgICB1bmdyb3VwKCkgJT4lDQogICAgdHJhbnNtdXRlKA0KICAgICAgbG9jYXRpb24sDQogICAgICB5ZWFyLA0KICAgICAgbXVsdGlwbGllcj12YWx1ZS9yZWZfdmFsdWUsDQogICAgICBtdWx0aXBsaWVyX3R5cGU9Y2FzZV93aGVuKA0KICAgICAgICBtZWFzdXJlPT0nQycgfiAnQ1VSUkVOVF9MQ1VfTUlMTElPTlMnLA0KICAgICAgICBUIH4gc3ByaW50ZigNCiAgICAgICAgICAiJXNfJXNfUEVSX0NBUElUQSIsDQogICAgICAgICAgaWZlbHNlKHRyYW5zYWN0PT0nUDQxJywnQUlDJywnR0RQJyksDQogICAgICAgICAgaWZlbHNlKG1lYXN1cmU9PSdIVlBWT0InLCdDT05TVEFOVCcsJ0NVUlJFTlQnKQ0KICAgICAgICApDQogICAgICApDQogICAgKSAlPiUNCiAgICB1bmlxdWUoKSAjIHdlIGRvbnQgbmVlZCBtdWx0aXBsZSByZXN1bHRzIGZvciBDdXJyZW50L0xDVSENCiAgDQogICAgDQogIHJldHVybihyYXdNdWx0KQ0KDQp9DQoNCg0KdXJsPSdodHRwczovL3N0YXRzLm9lY2Qub3JnL3Jlc3RzZG14L3NkbXguYXNoeC9HZXREYXRhL1NOQV9UQUJMRTEvQVVTK0FVVCtCRUwrQ0FOK0NITCtDWkUrRE5LK0VTVCtGSU4rRlJBK0RFVStHUkMrSFVOK0lTTCtJUkwrSVNSK0lUQStKUE4rS09SK0xWQStMVFUrTFVYK01FWCtOTEQrTlpMK05PUitQT0wrUFJUK1NWSytTVk4rRVNQK1NXRStDSEUrVFVSK0dCUitVU0ErRUExOStFVTI4K0VVMTUrT0VDREUrT0VDRCtPVEYrTk1FQytBUkcrQlJBK0JHUitDUFYrQ0hOK0NPTCtDUkkrSFJWK0NZUCtIS0crSU5EK0lETitNREcrTUxUK01BUitNS0QrUEVSK1JPVStSVVMrU0FVK1NSQitaQUYrWk1CK0ZSTUUrREVXLkIxX0dFK1A0MS5DK0hDUEMrSFZQVk9CL2FsbD9zdGFydFRpbWU9MTk3MCZlbmRUaW1lPTIwMTknDQoNCnNpbXBsZURhdGEkcHBwX2NvbnZlcnNpb249Z2V0UFBQTXVsdGlwbGllcnModXJsKQ0KDQoNCmBgYA0KDQoNCiMjIEdldCBwb3B1bGF0aW9uIGZpZ3VyZXMgZnJvbSBPRUNEDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCg0KZ2V0UG9wcz1mdW5jdGlvbih1cmwpIHsNCiAgDQogIGZldGNoU0RNWFVybCh1cmwpICU+JQ0KICAgIGNsZWFuX25hbWVzKCkgJT4lDQogICAgZmlsdGVyKCAjIG5vdCBuZWNlc3NhcnkgdG8gZmlsdGVyIHRoaXMgZGF0YXNldCwgYnV0IGp1c3QgdG8gYmUgY2xlYXIuLi4uDQogICAgICB0aW1lX2Zvcm1hdCA9PSAnUDFZJywgDQogICAgICB0cmFuc2FjdCA9PSAnUE9QTkMnDQogICAgKSAlPiUgDQogICAgdHJhbnNtdXRlKA0KICAgICAgbG9jYXRpb24sDQogICAgICB5ZWFyPWFzLmludGVnZXIob2JzX3RpbWUpLA0KICAgICAgcG9wPW9ic192YWx1ZSAjIHRob3VzYW5kcw0KICAgICkNCiAgDQp9DQoNCnVybD0naHR0cHM6Ly9zdGF0cy5vZWNkLm9yZy9yZXN0c2RteC9zZG14LmFzaHgvR2V0RGF0YS9TTkFfVEFCTEUzL0FVUytBVVQrQkVMK0NBTitDSEwrQ1pFK0ROSytFU1QrRklOK0ZSQStERVUrR1JDK0hVTitJU0wrSVJMK0lTUitJVEErSlBOK0tPUitMVkErTFRVK0xVWCtNRVgrTkxEK05aTCtOT1IrUE9MK1BSVCtTVksrU1ZOK0VTUCtTV0UrQ0hFK1RVUitHQlIrVVNBK0VBMTkrRVUyOCtFVTE1K09FQ0RFK09FQ0QrT1RGK05NRUMrQVJHK0JSQStCR1IrQ1BWK0NITitDT0wrQ1JJK0hSVitDWVArSU5EK0lETitNREcrTUxUK01BUitNS0QrUEVSK1JPVStSVVMrU0FVK1NSQitaQUYrWk1CK0ZSTUUrREVXLlBPUE5DLlBFUi9hbGw/c3RhcnRUaW1lPTE5NzAmZW5kVGltZT0yMDE4Jw0Kc2ltcGxlRGF0YSRwb3A9Z2V0UG9wcyh1cmwpDQoNCmBgYA0KDQoNCg0KIyMgR2V0IHRyYW5zYWN0aW9ucyBmcm9tIGRldGFpbGVkIG5vbi1maW5hbmNpYWwgYWNjb3VudHMgKFNOQSkNCg0KVXNlIE9FQ0QncyBbU05BIHRhYmxlIGZvciBub24tZmluYW5jaWFsIGFjY291bnRzIGJ5IHNlY3Rvcl0oaHR0cHM6Ly9zdGF0cy5vZWNkLm9yZy9JbmRleC5hc3B4P0RhdGFTZXRDb2RlPVNOQV9UQUJMRTE0QSkgdG8gb2J0YWluIG1vcmUgZ3JhbnVsYXIgU05BIGRhdGEgZm9yIGRpZmZlcmVudCBzZWN0b3JzIGFuZCB0cmFuc2FjdGlvbiB0eXBlcy4gQWx0aG91Z2ggSSB1c2UgdGhpcyBmb3Igc2V2ZXJhbCBvdGhlciBwdXJwb3NlcyAoaGVuY2UgdGhlIGV4dHJhIHRyYW5zYWN0aW9ucyBhbmQgc2VjdG9ycyksIHRoaXMgcHJvdmlkZXMgdGhlIHRyYW5zYWN0aW9ucyBpbiBMQ1UgZm9yIGdyb3NzIGFkanVzdGVkIGRpc3Bvc2FibGUgaW5jb21lIGFuZCBjb25zdW1wdGlvbiBvZiBmaXhlZCBjYXBpdGFsIGZvciB0aGUgaG91c2Vob2xkIHNlY3Rvci4gICANCg0KSEggZ3Jvc3MgYWRqdXN0ZWQgZGlzcG9zYWJsZSBpbmNvbWUgbGVzcyB0aGVpciBjb25zdW1wdGlvbiBvZiBmaXhlZCBjYXBpdGFsIGVxdWFscyBuZXQgYWRqdXN0ZWQgaG91c2Vob2xkIGRpc3Bvc2FibGUgaW5jb21lLg0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmdldFNlY3RvcmFsPWZ1bmN0aW9uKHVybCxwcmljZUNXKSB7DQogIHNlY3RvcmFsX3Jhdz1mZXRjaFNETVhVcmwodXJsKSAlPiUNCiAgICBjbGVhbl9uYW1lcygpDQogICAgDQogICAgbmZhX3NlY3Rvcl9jdz10cmliYmxlKA0KICAgICAgfnNlY3Rvcix+c2VjX3Nob3J0LH5zZWNfbG9uZywNCiAgICAgICdTMScsJ1RPVCcsJ1RvdGFsIGVjb25vbXknLA0KICAgICAgJ1MxMScsJ05GQycsJ05vbi1GaW5hbmNpYWwgY29ycG9yYXRpb25zJywNCiAgICAgICdTMTInLCdGSU4nLCdGaW5hbmNpYWwgY29ycG9yYXRpb25zJywNCiAgICAgICdTMTMnLCdHT1YnLCdHZW5lYWwgR292ZXJubWVudCcsDQogICAgICAnUzE0X1MxNScsJ0hIJywnSG91c2Vob2xkcyBhbmQgbm9uLXByb2ZpdCBpbnN0aXR1dGlvbnMgc2VydmluZyBob3VzZWhvbGRzJw0KICAgICkNCiAgDQogIA0KICBuZmFfdHJhbnNhY3RfY3c9dHJpYmJsZSgNCiAgICB+dHJhbnNhY3QsfnR4X3Nob3J0LH50eF9sb25nLA0KICAgICdORkIxR1InLCdHVkEnLCdHcm9zcyBEb21lc3RpYyBQcm9kdWN0IC8gR3Jvc3MgVmFsdWUgQWRkJywNCiAgICAnTkZCNUdSJywnR1BJJywnR3Jvc3MgTmF0aW9uYWwgSW5jb21lIC8gQmFsYW5jZSBvZiBQcmltYXJ5IEluY29tZScsDQogICAgJ05GQjZHUCcsJ0dESScsJ0dyb3NzIERpc3Bvc2FibGUgSW5jb21lJywNCiAgICAnTkZCN0dQJywnQUdESScsJ0FkanVzdGVkIEdyb3NzIERpc3Bvc2FibGUgSW5jb21lJywNCiAgICAnTkZCOEdQJywnR1MnLCdHcm9zcyBTYXZpbmcnLA0KICAgICdORksxUCcsJ0NGQycsJ0NvbnN1bXB0aW9uIG9mIEZpeGVkIENhcGl0YWwnLCAgDQogICAgJ05GUDVQJywnR0NGJywnR3Jvc3MgQ2FwaXRhbCBGb3JtYXRpb24nLA0KICAgICdORkI5UCcsJ05MJywnTmV0IExlbmRpbmcgKCspLCBOZXQgQm9ycm93aW5nICgtKScNCiAgKQ0KICANCiAgc2VjdG9yYWxfbG9uZz1zZWN0b3JhbF9yYXcgJT4lDQogICAgbWVyZ2UobmZhX3NlY3Rvcl9jdyxieT0nc2VjdG9yJykgJT4lDQogICAgbWVyZ2UobmZhX3RyYW5zYWN0X2N3LGJ5PSd0cmFuc2FjdCcpICU+JQ0KICAgIHRyYW5zbXV0ZSgNCiAgICAgIGxvY2F0aW9uLA0KICAgICAgeWVhcj1hcy5pbnRlZ2VyKG9ic190aW1lKSwNCiAgICAgIHNlY3Rvcj1zZWNfc2hvcnQsDQogICAgICB0cmFuc2FjdGlvbj10eF9zaG9ydCwNCiAgICAgIHZhbHVlPW9ic192YWx1ZQ0KICAgICkgJT4lDQogICAgbWVyZ2UocHJpY2VDVyxieT1jKCdsb2NhdGlvbicsJ3llYXInKSkgJT4lDQogICAgbXV0YXRlKA0KICAgICAgcmF3X3ZhbHVlPXZhbHVlLA0KICAgICAgdmFsdWU9cmF3X3ZhbHVlKm11bHRpcGxpZXINCiAgICApDQogIA0KICANCiAgIyBzdWJ0cmFjdCBjb25zdW1wdGlvbiBvZiBmaXhlZCBjYXBpdGFsIGZyb20gZGlzcG9zYWJsZSBpbmNvbWUsIHN0YW5kYXJkIGFuZCAiYWRqdXN0ZWQiICgrU1RpSykNCiAgIyB0byBkZXJpdmUgbmV0ICogZGlzcG9zYWJsZSBpbmNvbWUNCiAgc2VjdG9yYWxfbmV0X2Rpc3Bvc2FibGU9c2VjdG9yYWxfbG9uZyAlPiUNCiAgICBmaWx0ZXIoDQogICAgICB0cmFuc2FjdGlvbiAlaW4lIGMoJ0NGQycsJ0dESScsJ0FHREknKQ0KICAgICkgJT4lDQogICAgZGNhc3QobG9jYXRpb24reWVhcitzZWN0b3IrbXVsdGlwbGllcl90eXBlfnRyYW5zYWN0aW9uKSAlPiUNCiAgICBtdXRhdGUoDQogICAgICBBTkRJPUFHREktQ0ZDLA0KICAgICAgTkRJPUdESS1DRkMNCiAgICApICU+JQ0KICAgIG1lbHQobWVhc3VyZS52YXJzPWMoJ0FOREknLCdOREknKSkgJT4lDQogICAgbXV0YXRlKA0KICAgICAgdHJhbnNhY3Rpb249YXMuY2hhcmFjdGVyKHZhcmlhYmxlKQ0KICAgICkgJT4lDQogICAgc2VsZWN0KC12YXJpYWJsZSkNCiAgDQogIHNlY3RvcmFsX3dpZGU9c2VjdG9yYWxfbG9uZyAlPiUNCiAgICBiaW5kX3Jvd3Moc2VjdG9yYWxfbmV0X2Rpc3Bvc2FibGUpICU+JQ0KICAgIG11dGF0ZSgNCiAgICAgIHZhcmlhYmxlPXN0cl90b19sb3dlcihzcHJpbnRmKCJuZmFfJXNfJXMiLHRyYW5zYWN0aW9uLHNlY3RvcikpDQogICAgKSAlPiUNCiAgICBkY2FzdChsb2NhdGlvbit5ZWFyK211bHRpcGxpZXJfdHlwZX52YXJpYWJsZSkNCiAgDQogIHJldHVybihzZWN0b3JhbF93aWRlKQ0KDQp9DQoNCnVybD0naHR0cHM6Ly9zdGF0cy5vZWNkLm9yZy9yZXN0c2RteC9zZG14LmFzaHgvR2V0RGF0YS9TTkFfVEFCTEUxNEEvQVVTK0FVVCtCRUwrQ0FOK0NITCtDWkUrRE5LK0VTVCtGSU4rRlJBK0RFVStHUkMrSFVOK0lTTCtJUkwrSVNSK0lUQStKUE4rS09SK0xWQStMVFUrTFVYK01FWCtOTEQrTlpMK05PUitQT0wrUFJUK1NWSytTVk4rRVNQK1NXRStDSEUrVFVSK0dCUitVU0ErRUExOStFVTI4K05NRUMrQlJBK0JHUitDUFYrQ0hOK0NPTCtDUkkrSFJWK0NZUCtIS0crSU5EK0lETitNREcrTUxUK01BUitNS0QrUEVSK1JPVStSVVMrU0FVK1NSQitaQUYrWk1CK0ZSTUUrREVXLk5GQjVHUitORkI3R1ArTkZCNkdQK05GQjhHUCtORlA1UCtORksxUCtORkI5UCtORkIxR1IuUzErUzExK1MxMitTMTMrUzE0X1MxNS5DL2FsbD9zdGFydFRpbWU9MTk3MCZlbmRUaW1lPTIwMTgnDQoNCg0Kc2ltcGxlRGF0YSRzZWN0b3JhbD1nZXRTZWN0b3JhbCh1cmwsc2ltcGxlRGF0YSRwcHBfY29udmVyc2lvbikNCg0KYGBgDQoNCg0KIyMgR2V0IGN1cnJlbnQgaGVhbHRoIGV4cGVuZGl0dXJlcyBmcm9tIE9FQ0QncyBTSEEgdGFibGVzDQoNCg0KR2V0IFtTSEEgaGVhbHRoIGV4cGVuZGl0dXJlc10oaHR0cHM6Ly9zdGF0cy5vZWNkLm9yZy9JbmRleC5hc3B4P0RhdGFTZXRDb2RlPVNIQSkgZnJvbSBPRUNELiAgSSBhbSBmZXRjaGluZyB0aGlzIGluIGN1cnJlbnQgTENVIGFuZCBjb252ZXJ0aW5nIHRoZW0gbG9jYWxseSB0byBiZSBjb25zaXN0ZW50IHdpdGggdGhlIG90aGVyIGZpZ3VyZXMgdXNlZC4gT0VDRCBkb2VzIHByb3ZpZGUgbWVhc3VyZXMgaW4gcGVyIGNhcGl0YSBBSUMgUFBQcywgY29uc3RhbnQgYW5kIGN1cnJlbnQuICBJJ3ZlIGZvdW5kIHRoZSByZXN1bHRzIHRvIGJlIGhpZ2hseSBjb21wYXJhYmxlLCB3aXRoIHNvbWUgcmFyZSBleGNlcHRpb25zLCBob3dldmVyIHRoaXMgZW5zdXJlcyB0aGUgcG9wdWxhdGlvbiBhbmQgUFBQIHZhbHVlcyBhcmUgY29uc2lzdGVudCB0aHJvdWdob3V0IG15IGFuYWx5c2lzLiAgVXNpbmcgdGhlaXIgUFBQLWFkanVzdGVkIGZpZ3VyZXMgaW5zdGVhZCB3aWxsIG5vdCBzdWJzdGFudGl2ZWx5IGNoYW5nZSBteSBhbmFseXNpcy4NCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpnZXRTSEFFeHBlbmQ9ZnVuY3Rpb24odXJsLHByaWNlQ1cpIHsNCiAgDQogIGhjZT1mZXRjaFNETVhVcmwodXJsKSAlPiUNCiAgICBjbGVhbl9uYW1lcygpICU+JQ0KICAgIGZpbHRlcigNCiAgICAgIGhmPT0nSEZUT1QnLA0KICAgICAgaGM9PSdIQ1RPVCcsDQogICAgICBocD09J0hQVE9UJw0KICAgICkgJT4lDQogICAgdHJhbnNtdXRlKA0KICAgICAgICBsb2NhdGlvbiwNCiAgICAgICAgeWVhcj1hcy5pbnRlZ2VyKG9ic190aW1lKSwNCiAgICAgICAgdmFyaWFibGU9c3ByaW50Zigic2hhX2hjZV8lcyIsbWVhc3VyZSksDQogICAgICAgIHZhbHVlPW9ic192YWx1ZQ0KICAgICkgJT4lDQogICAgZGNhc3QobG9jYXRpb24reWVhcn52YXJpYWJsZSkgJT4lDQogICAgbWVyZ2UocHJpY2VDVyxieT1jKCdsb2NhdGlvbicsJ3llYXInKSkgJT4lDQogICAgbXV0YXRlKA0KICAgICAgc2hhX2hjZV9hZGo9c2hhX2hjZV9NTExOQ1UqbXVsdGlwbGllcg0KICAgICkNCiAgDQogIHJldHVybihoY2UpDQoNCn0NCg0KDQp1cmw9J2h0dHBzOi8vc3RhdHMub2VjZC5vcmcvcmVzdHNkbXgvc2RteC5hc2h4L0dldERhdGEvU0hBL0hGVE9ULkhDVE9ULkhQVE9ULk1MTE5DVS5BVVMrQVVUK0JFTCtDQU4rQ0hMK0NPTCtDWkUrRE5LK0VTVCtGSU4rRlJBK0RFVStHUkMrSFVOK0lTTCtJUkwrSVNSK0lUQStKUE4rS09SK0xWQStMVFUrTFVYK01FWCtOTEQrTlpMK05PUitQT0wrUFJUK1NWSytTVk4rRVNQK1NXRStDSEUrVFVSK0dCUitVU0ErQlJBK0NITitDUkkrSU5EK0lETitSVVMrWkFGL2FsbD9zdGFydFRpbWU9MTk3MCZlbmRUaW1lPTIwMTknDQoNCg0Kc2ltcGxlRGF0YSRoY2U9Z2V0U0hBRXhwZW5kKHVybCxzaW1wbGVEYXRhJHBwcF9jb252ZXJzaW9uKQ0KDQoNCg0KYGBgDQoNCg0KDQoNCiMjIEdldCBHRFAsIEFJQywgYW5kIHNvbWUgb3RoZXIgTkEgYWdncmVnYXRlcyBmb3IgYW5hbHlzaXMgZnJvbSBPRUNEJ3MgU05BIFRhYmxlIDENCg0KRmV0Y2ggc2V2ZXJhbCB0cmFuc2FjdGlvbnMgY29ycmVzcG9uZGluZyB0byBtb3JlIHdpZGVseSB1bmRlcnN0b29kIFNOQSBhZ2dyZWdhdGVzIGluIEN1cnJlbnQgTENVIGZyb20gW09FQ0QncyBNYWluIFNOQSB0YWJsZV0oaHR0cHM6Ly9zdGF0cy5vZWNkLm9yZy9JbmRleC5hc3B4P0RhdGFTZXRDb2RlPVNOQV9UQUJMRTEpLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpnZXRTTkExQmFzaWM9ZnVuY3Rpb24odXJsLHByaWNlQ1cpIHsNCiAgDQogIGdkcF9kaXNhZ2dfY3c9dHJpYmJsZSgNCiAgICB+dHJhbnNhY3QsfnNob3J0X2xhYix+bmFtZSwNCiAgICAnQjFfR0UnLCdnZHAnLCdHRFAgKGV4cGVuZGl0dXJlIGFwcHJvYWNoKScsDQogICAgJ1A0MScsJ2FpYycsJ0FjdHVhbCBJbmRpdmlkdWFsIENvbnN1bXB0aW9uJywgDQogICAgJ1AzJywnZmluYWxfY29uc3VtcHRpb24nLCdGaW5hbCBDb25zdW1wdGlvbicsDQogICAgJ1A1JywnZ3Jvc3NfY2FwaXRhbF9mb3JtYXRpb24nLCdHcm9zcyBDYXBpdGFsIEZvcm1hdGlvbicsDQogICAgJ0IxMScsJ25ldF9leHBvcnRzJywnTmV0IEV4cG9ydHMnLA0KICAgICdQMzFTMTMnLCdnb3ZfY29uc3VtcHRpb25faW5kJywnR292ZXJubWVudCBDb25zdW1wdGlvbjogSW5kaXZpZHVhbCcsDQogICAgJ1AzMlMxMycsJ2dvdl9jb25zdW1wdGlvbl9jb2wnLCdHb3Zlcm5tZW50IENvbnN1bXB0aW9uOiBDb2xsZWN0aXZlJywNCiAgICAnUDNTMTMnLCdnb3ZfY29uc3VtcHRpb25fYWxsJywnR292ZXJubWVudCBDb25zdW1wdGlvbiAoY29tYmluZWQpJywNCiAgICAnUDMxUzE0X1MxNScsJ2hoX2NvbnN1bXB0aW9uJywnSG91c2Vob2xkIGFuZCBOUElTSCBjb25zdW1wdGlvbicsDQogICAgJ1A2JywnZXhwb3J0cycsJ0V4cG9ydHMnLA0KICAgICdQNycsJ2ltcG9ydHMnLCdJbXBvcnRzJywNCiAgICAnUDUxJywnZ3Jvc3NfZml4ZWRfY2FwaXRhbF9mb3JtYXRpb24nLCdHcm9zcyBGaXhlZCBDYXBpdGFsIEZvcm1hdGlvbicsDQogICAgJ1A1Ml9QNTMnLCdjaGFuZ2VzX2luX2ludmVudG9yaWVzJywnQ2hhbmdlcyBpbiBpbnZlbnRvcmllcyAoc3RvY2tzKScsDQogICAgJ1AzX1A1JywnZG9tZXN0aWNfZGVtYW5kJywnRG9tZXN0aWMgRGVtYW5kJw0KICApDQogIA0KICBnZHBfZGlzYWdnX3Jhdz1mZXRjaFNETVhVcmwodXJsKSAlPiUNCiAgICBjbGVhbl9uYW1lcygpICU+JQ0KICAgIHRyYW5zbXV0ZSgNCiAgICAgIGxvY2F0aW9uLA0KICAgICAgeWVhcj1hcy5pbnRlZ2VyKG9ic190aW1lKSwNCiAgICAgIHRyYW5zYWN0LA0KICAgICAgdmFsdWU9b2JzX3ZhbHVlDQogICAgKQ0KDQogIGdkcF9kaXNhZ2dfbG9uZz1tZXJnZShnZHBfZGlzYWdnX3JhdyxnZHBfZGlzYWdnX2N3LGJ5PSd0cmFuc2FjdCcsYWxsLng9VCkgJT4lDQogICAgbWVyZ2UocHJpY2VDVyxieT1jKCdsb2NhdGlvbicsJ3llYXInKSkgJT4lDQogICAgbXV0YXRlKA0KICAgICAgcmF3X3ZhbHVlPXZhbHVlLA0KICAgICAgdmFsdWU9cmF3X3ZhbHVlKm11bHRpcGxpZXINCiAgICApDQogIA0KICANCiAgZ2RwX2Rpc2FnZ193aWRlPSBnZHBfZGlzYWdnX2xvbmcgJT4lDQogICAgbXV0YXRlKA0KICAgICAgdmFyaWFibGU9c3ByaW50Zigic25hMV8lcyIsc2hvcnRfbGFiKQ0KICAgICkgJT4lDQogICAgZGNhc3QobG9jYXRpb24reWVhcittdWx0aXBsaWVyX3R5cGV+dmFyaWFibGUpICANCiAgDQogIHJldHVybihnZHBfZGlzYWdnX3dpZGUpICANCn0NCg0KDQp1cmw9J2h0dHBzOi8vc3RhdHMub2VjZC5vcmcvcmVzdHNkbXgvc2RteC5hc2h4L0dldERhdGEvU05BX1RBQkxFMS9BVVMrQVVUK0JFTCtDQU4rQ0hMK0NPTCtDWkUrRE5LK0VTVCtGSU4rRlJBK0RFVStHUkMrSFVOK0lTTCtJUkwrSVNSK0lUQStKUE4rS09SK0xWQStMVFUrTFVYK01FWCtOTEQrTlpMK05PUitQT0wrUFJUK1NWSytTVk4rRVNQK1NXRStDSEUrVFVSK0dCUitVU0ErRUExOStFVTI4K0VVMTUrT0VDREUrT0VDRCtPVEYrTk1FQytBTEIrQVJHK0JSQStCR1IrQ1BWK0NITitDUkkrSFJWK0NZUCtIS0crSU5EK0lETitNREcrTUxUK01BUitNS0QrUEVSK1JPVStSVVMrU0FVK1NSQitTR1ArWkFGK1pNQitGUk1FK0RFVy5CMV9HRStQM19QNStQMytQMzFTMTRfUzE1K1AzUzEzK1A0MS5DL2FsbD9zdGFydFRpbWU9MTk3MCZlbmRUaW1lPTIwMTknDQoNCnNpbXBsZURhdGEka2V5X2FnZ3JlZ2F0ZXM9Z2V0U05BMUJhc2ljKHVybCxzaW1wbGVEYXRhJHBwcF9jb252ZXJzaW9uKQ0KDQpgYGANCg0KDQoNCiMjIGJyaW5nIHRoZSBiYXNpYyBkYXRhIHRvZ2V0aGVyDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQoNCg0KDQpzaW1wbGVEYXRhJG1lcmdlZD1zaW1wbGVEYXRhJGhjZSAlPiUNCiAgbWVyZ2Uoc2ltcGxlRGF0YSRzZWN0b3JhbCxieT1jKCdsb2NhdGlvbicsJ3llYXInLCdtdWx0aXBsaWVyX3R5cGUnKSxhbGw9VCkgJT4lDQogIG1lcmdlKHNpbXBsZURhdGEka2V5X2FnZ3JlZ2F0ZXMsYnk9YygnbG9jYXRpb24nLCd5ZWFyJywnbXVsdGlwbGllcl90eXBlJyksYWxsPVQpICU+JQ0KICBtZXJnZShzaW1wbGVEYXRhJHBvcCxieT1jKCdsb2NhdGlvbicsJ3llYXInKSxhbGw9VCkNCg0Kc2ltcGxlRGF0YSRvZnRlbl9yZWZlcmVuY2VkPXNpbXBsZURhdGEkbWVyZ2VkICU+JQ0KICB0cmFuc211dGUoDQogICAgeWVhciwNCiAgICBsb2NhdGlvbiwNCiAgICBtdWx0aXBsaWVyX3R5cGUsDQogICAgcG9wLCAgICAgICAgICMgcG9wdWxhdGlvbg0KICAgIHNoYV9oY2VfYWRqLCAjIGN1cnJlbnQgaGVhbHRoIGV4cGVuZGl0dXJlcyAoU0hBKQ0KICAgIG5mYV9hbmRpX2hoLCAjIG5ldCBhZGp1c3RlZCBob3VzZWhvbGQgZGlzcG9zYWJsZSBpbmNvbWUNCiAgICBuZmFfYWdkaV9oaCwgIyBncm9zcyBhZGp1c3RlZCBob3VzZWhvbGQgZGlzcG9zYWJsZSBpbmNvbWUNCiAgICBzbmExX2dkcCwgICAgIyBHRFANCiAgICBzbmExX2FpYyAgICAgIyBBY3R1YWwgSW5kaXZpZHVhbCBDb25zdW1wdGlvbg0KICApICU+JQ0KICBmaWx0ZXIoIA0KICAgICFpcy5uYShtdWx0aXBsaWVyX3R5cGUpICANCiAgKQ0KDQoNCg0KDQojIGF2YWlsYWJsZSBQUFAtcmVsYXRlZCBjb252ZXJzaW9ucw0KY2F0KHVuaXF1ZShzaW1wbGVEYXRhJG9mdGVuX3JlZmVyZW5jZWQkbXVsdGlwbGllcl90eXBlKSkNCg0KDQpgYGANCg0KWW91IHdpbGwgd2FudCB0byBiZSBzdXJlIHRvIGZpbHRlciB0aGVzZSBkYXRhIGJ5IHRoZSBhcHByb3ByaWF0ZSBmaWVsZCBpbiBgbXVsdGlwbGllcl90eXBlYCEhDQoNCg0KDQojIyBzYW1wbGUgdXNhZ2UsIG51bWJlciBvbmUNCg0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KDQpleGNsdWRlX3VzRm49ZnVuY3Rpb24oZGYpIHsNCiAgZmlsdGVyKGRmLGxvY2F0aW9uIT0nVVNBJykNCn0NCg0Kc2ltcGxlRGF0YSRvZnRlbl9yZWZlcmVuY2VkICU+JQ0KICBmaWx0ZXIoDQogICAgeWVhcj09MjAxNywNCiAgICAhaXMubmEobmZhX2FuZGlfaGgpLA0KICAgICFpcy5uYShzaGFfaGNlX2FkaiksDQogICAgDQogICAgIyBmaWx0ZXIgYnkgdGhlIGRlc2lyZWQgbXVsdGlwbGllciBvciB5b3Ugd2lsbCBnZXQgd2VpcmQgcmVzdWx0cyEhIQ0KICAgIG11bHRpcGxpZXJfdHlwZT09J0FJQ19DT05TVEFOVF9QRVJfQ0FQSVRBJywgDQogICAgDQogICAgIyBleGNsdWRlIEx1eGVtYm91cmcgYW5kIFNvdXRoIEFmcmljYQ0KICAgICFsb2NhdGlvbiAlaW4lIGMoJ1pBRicsJ0xVWCcpLCANCiAgICBUDQogICkgJT4lDQogIGdncGxvdChhZXMobG9nKG5mYV9hbmRpX2hoKSxsb2coc2hhX2hjZV9hZGopICkpICsNCiAgICBnZW9tX3BvaW50KGNvbG9yPSdyZWQnLGFlcyhzaXplPXBvcCksYWxwaGE9MC43KSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgICAgc2VjLmF4aXMgPSBzZWNfYXhpcygNCiAgICAgICAgfmV4cCguKSwNCiAgICAgICAgbGFiZWxzPWRvbGxhcl9mb3JtYXQoKSwNCiAgICAgICAgYnJlYWtzPWV4cChzZXEoMSwyMCwuNSkpDQogICAgICApDQogICAgKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgICAgc2VjLmF4aXMgPSBzZWNfYXhpcygNCiAgICAgICAgfmV4cCguKSwNCiAgICAgICAgbGFiZWxzPWRvbGxhcl9mb3JtYXQoKSwNCiAgICAgICAgYnJlYWtzPWV4cChzZXEoMSwyMCwuMikpDQogICAgICApDQogICAgKSArDQoNCiAgICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPWxvY2F0aW9uKSkgKw0KICAgIHRoZW1lX2lwc3VtKCkgKw0KICAgIGdlb21fc21vb3RoKA0KICAgICAgbWV0aG9kPWxtLA0KICAgICAgZGF0YT1leGNsdWRlX3VzRm4sDQogICAgICBmdWxscmFuZ2U9VA0KICAgICkgKw0KICAgIGxhYnMoDQogICAgICB4PSdsb2cgMjAxNyBuZXQgYWRqdXN0ZWQgaG91c2Vob2xkIGRpc3Bvc2FibGUgaW5jb21lIHBlciBjYXBpdGFcbmF0IFBQUHMgZm9yIEFJQyAoY29uc3RhbnQgZG9sbGFycyknLA0KICAgICAgeT0nbG9nIDIwMTcgY3VycmVudCBoZWF0bGggZXhwZW5kaXVyZXMgcGVyIGNhcGl0YVxuYXQgUFBQcyBmb3IgQUlDIChjb25zdGFudCBkb2xsYXJzKScNCiAgICAgIA0KICAgICkgKw0KICAgIHN0YXRfcG9seV9lcSgNCiAgICAgIGFlcyhsYWJlbCA9ICBwYXN0ZShzdGF0KGVxLmxhYmVsKSxzdGF0KHJyLmxhYmVsKSwgc2VwID0gIipcIiwgXCIqIikpLA0KICAgICAgZm9ybXVsYSA9ICd5fngnLA0KICAgICAgcGFyc2UgPSBUUlVFLA0KICAgICAgZGF0YT1leGNsdWRlX3VzRm4NCiAgICApIA0KDQogIA0KDQpgYGANCg0KDQoNCiMjIHNhbXBsZSB1c2FnZSwgbnVtYmVyIHR3bw0KDQpgYGB7ciBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQoNCg0Kc2ltcGxlRGF0YSRvZnRlbl9yZWZlcmVuY2VkICU+JQ0KICBmaWx0ZXIoDQogICAgeWVhcj09MjAxNywNCiAgICAhaXMubmEobmZhX2FuZGlfaGgpLA0KICAgICFpcy5uYShzbmExX2FpYyksDQogICAgbXVsdGlwbGllcl90eXBlPT0nQUlDX0NPTlNUQU5UX1BFUl9DQVBJVEEnLA0KICAgIFQNCiAgKSAlPiUNCiAgZ2dwbG90KGFlcyhsb2cobmZhX2FuZGlfaGgpLGxvZyhzbmExX2FpYykgKSkgKw0KICAgIGdlb21fcG9pbnQoY29sb3I9J3JlZCcsYWVzKHNpemU9cG9wKSxhbHBoYT0wLjcpICsNCiAgICBnZW9tX2FibGluZShzbG9wZT0xKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgICAgc2VjLmF4aXMgPSBzZWNfYXhpcygNCiAgICAgICAgfmV4cCguKSwNCiAgICAgICAgbGFiZWxzPWRvbGxhcl9mb3JtYXQoYWNjdXJhY3k9MSksDQogICAgICAgIGJyZWFrcz1leHAoc2VxKDEsMjAsLjUpKQ0KICAgICAgKQ0KICAgICkgKw0KICAgIHNjYWxlX3hfY29udGludW91cygNCiAgICAgIHNlYy5heGlzID0gc2VjX2F4aXMoDQogICAgICAgIH5leHAoLiksDQogICAgICAgIGxhYmVscz1kb2xsYXJfZm9ybWF0KGFjY3VyYWN5PTEpLA0KICAgICAgICBicmVha3M9ZXhwKHNlcSgxLDIwLC4yKSkNCiAgICAgICkNCiAgICApICsNCg0KICAgIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9bG9jYXRpb24pKSArDQogICAgdGhlbWVfaXBzdW0oKSArDQogICAgZ2VvbV9zbW9vdGgoDQogICAgICBtZXRob2Q9bG0sDQogICAgICBkYXRhPWV4Y2x1ZGVfdXNGbiwNCiAgICAgIGZ1bGxyYW5nZT1UDQogICAgKSArDQogICAgbGFicygNCiAgICAgIHg9J2xvZyAyMDE3IG5ldCBhZGp1c3RlZCBob3VzZWhvbGQgZGlzcG9zYWJsZSBpbmNvbWUgcGVyIGNhcGl0YVxuYXQgUFBQcyBmb3IgQUlDIChjb25zdGFudCBkb2xsYXJzKScsDQogICAgICB5PSdsb2cgMjAxNyBBSUMgcGVyIGNhcGl0YVxuYXQgUFBQcyBmb3IgQUlDIChjb25zdGFudCBkb2xsYXJzKScNCiAgICAgIA0KICAgICkgKw0KICAgIHN0YXRfcG9seV9lcSgNCiAgICAgIGFlcyhsYWJlbCA9ICBwYXN0ZShzdGF0KGVxLmxhYmVsKSxzdGF0KHJyLmxhYmVsKSwgc2VwID0gIipcIiwgXCIqIikpLA0KICAgICAgZm9ybXVsYSA9ICd5fngnLA0KICAgICAgcGFyc2UgPSBUUlVFLA0KICAgICAgZGF0YT1leGNsdWRlX3VzRm4NCiAgICApIA0KDQoNCg0KYGBgDQoNCg0KIyMgY29tcGFyZSB0byBCTEkgTmV0IEFIREkgZmlndXJlcw0KDQpFeHRyYWN0IE5ldCBBZGp1c3RlZCBIb3VzZWhvbGQgRGlzcG9zYWJsZSBJbmNvbWUgcGVyIGNhcGl0YSBmcm9tIFt0aGUgQmV0dGVyIExpZmUgSW5kZXhdKGh0dHBzOi8vc3RhdHMub2VjZC5vcmcvSW5kZXguYXNweD9EYXRhU2V0Q29kZT1CTEkpIGFuZCBjb21wYXJlLiAgVGhpcyBmaWd1cmUgaXMgcHJlc3VtYWJseSBmb3IgMjAxNiBhbmQgaW4gY3VycmVudCBQUFBzLiAgSSdkIGhvcGUgaXQnZCBiZSBjbG9zZXIgKGxhc3QgdGltZSBJIGxvb2tlZCBpdCB3YXMhKS4gU3RpbGwsIEkgdGhpbmsgaXQncyBjbG9zZSBlbm91Z2guICBTdGF0aXN0aWNhbCBkaXNjcmVwYW5jaWVzIGNhbiBhcmlzZSBiZWNhdXNlIG9mIHJldmlzaW9ucyB0byB0aGUgdW5kZXJseWluZyBkYXRhIHNldC4gIEl0J3MgbW9yZSBsaWtlbHkgdGhlIEJMSSBkYXRhIGFyZSBhIGJpdCBvdXRkYXRlZCB0aGFuIHNvdXJjZXMgSSBhbSBwdWxsaW5nIHRoaXMuDQoNCg0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KdXJsPSdodHRwczovL3N0YXRzLm9lY2Qub3JnL3Jlc3RzZG14L3NkbXguYXNoeC9HZXREYXRhL0JMSS9BVVMrQVVUK0JFTCtDQU4rQ0hMK0NPTCtDWkUrRE5LK0VTVCtGSU4rRlJBK0RFVStHUkMrSFVOK0lTTCtJUkwrSVNSK0lUQStKUE4rS09SK0xWQStMVFUrTFVYK01FWCtOTEQrTlpMK05PUitQT0wrUFJUK1NWSytTVk4rRVNQK1NXRStDSEUrVFVSK0dCUitVU0ErT0VDRCtOTUVDK0JSQStSVVMrWkFGLkhPX0JBU0UrSE9fSElTSCtIT19OVU1SK0lXX0hBREkrSVdfSE5GVy5MLlRPVC9hbGw/Jw0KDQpibGlEYXRhPWZldGNoU0RNWFVybCh1cmwpICU+JQ0KICAgIGNsZWFuX25hbWVzKCkgJT4lDQogICAgZGNhc3QobG9jYXRpb25+aW5kaWNhdG9yLHZhbHVlLnZhcj0nb2JzX3ZhbHVlJykgJT4lDQogICAgbXV0YXRlKA0KICAgICAgeWVhcj0yMDE2IA0KICAgICkNCg0KDQoNCnNpbXBsZURhdGEkb2Z0ZW5fcmVmZXJlbmNlZCAlPiUNCiAgZmlsdGVyKA0KICAgIG11bHRpcGxpZXJfdHlwZSA9PSAnQUlDX0NVUlJFTlRfUEVSX0NBUElUQScsDQogICAgVA0KICApICU+JQ0KICBtZXJnZShibGlEYXRhLGJ5PWMoJ2xvY2F0aW9uJywneWVhcicpKSAlPiUNCiAgZmlsdGVyKA0KICAgICFpcy5uYShJV19IQURJKSwNCiAgICAhaXMubmEobmZhX2FuZGlfaGgpDQogICkgJT4lDQogIGdncGxvdChhZXMobG9nKG5mYV9hbmRpX2hoKSxsb2coSVdfSEFESSkgKSkgKw0KICAgICNnZW9tX2FibGluZShzbG9wZT0xKSArDQogICAgZ2VvbV9wb2ludChjb2xvcj0ncmVkJyxhZXMoc2l6ZT1wb3ApLGFscGhhPTAuNSkgKw0KICAgIGdlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9bG9jYXRpb24pKSArICANCiAgICBnZW9tX3Ntb290aCgNCiAgICAgIG1ldGhvZD1sbSwNCiAgICAgIGZ1bGxyYW5nZT1UDQogICAgKSArDQogICAgbGFicygNCiAgICAgIHg9J2xvZyAyMDE2IG5ldCBhZGp1c3RlZCBob3VzZWhvbGQgZGlzcG9zYWJsZSBpbmNvbWUgcGVyIGNhcGl0YVxuYXQgUFBQcyBmb3IgQUlDIChjdXJyZW50IGRvbGxhcnMpXG5wZXIgbXkgY2FsY3VsYXRpb25zJywNCiAgICAgIHk9J2xvZyAyMDE2IG5ldCBhZGp1c3RlZCBob3VzZWhvbGQgZGlzcG9zYWJsZSBpbmNvbWUgcGVyIGNhcGl0YVxuYXQgUFBQcyBmb3IgQUlDIChjdXJyZW50IGRvbGxhcnMpXG5wZXIgT0VDRFwncyAyMDE5IEJldHRlciBMaWZlIEluZGV4Jw0KICAgICkgKw0KICAgIHN0YXRfcG9seV9lcSgNCiAgICAgIGFlcyhsYWJlbCA9ICBwYXN0ZShzdGF0KGVxLmxhYmVsKSxzdGF0KHJyLmxhYmVsKSwgc2VwID0gIipcIiwgXCIqIikpLA0KICAgICAgZm9ybXVsYSA9ICd5fngnLA0KICAgICAgcGFyc2UgPSBUUlVFDQogICAgKSArDQogICAgdGhlbWVfaXBzdW0oKQ0KICAgIA0KYGBgDQoNCg0KIyMgY3Jvc3NjaGVjayB3aXRoIE9FQ0QgTkFBRw0KDQpVc2UgZXN0aW1hdGVzIHB1Ymxpc2hlZCBpbiBPRUNEJ3MgW05hdGlvbmFsIEFjY291bnRzIEF0IEEgR2xhbmNlXShodHRwczovL3N0YXRzLm9lY2Qub3JnL0luZGV4LmFzcHg/RGF0YVNldENvZGU9TkFBRykgZm9yIGdyb3NzIGFkanVzdGVkIGhvdXNlaG9sZCBkaXNwb3NhYmxlIGluY29tZSBpbiBjdXJyZW50IHByaWNlcywgY3VycmVudCBQUFBzIChBSUMpIHRvIGNyb3NzLWNoZWNrIG15IGZpZ3VyZXMuICBUaGVzZSBjb3JyZXNwb25kIGV4YWN0bHkuDQoNCg0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KdXJsPSdodHRwczovL3N0YXRzLm9lY2Qub3JnL3Jlc3RzZG14L3NkbXguYXNoeC9HZXREYXRhL05BQUcvQVVTK0FVVCtCRUwrQ0FOK0NITCtDT0wrQ1pFK0ROSytFU1QrRklOK0ZSQStERVUrR1JDK0hVTitJU0wrSVJMK0lTUitJVEErSlBOK0tPUitMVkErTFRVK0xVWCtNRVgrTkxEK05aTCtOT1IrUE9MK1BSVCtTVksrU1ZOK0VTUCtTV0UrQ0hFK1RVUitHQlIrVVNBK0VNVStPVE8rTk1FQytCUkErQ0hOK0NSSStJTkQrSUROK1JVUytaQUYuQjdHUzE0X1MxNUhDUEMvYWxsP3N0YXJ0VGltZT0xOTcwJmVuZFRpbWU9MjAxOScNCg0KDQpuYWFnRGF0YT1mZXRjaFNETVhVcmwodXJsKSAlPiUNCiAgICBjbGVhbl9uYW1lcygpICU+JQ0KICAgIGZpbHRlcigNCiAgICAgIHRpbWVfZm9ybWF0PT0nUDFZJywNCiAgICAgIGluZGljYXRvcj09J0I3R1MxNF9TMTVIQ1BDJyAjIGdyb3NzIGFkanVzdGVkIGhvdXNlaG9sZCBkaXNwb3NhYmxlIGluY29tZSBwZXIgY2FwaXRhIGluIGN1cnJlbnQgUFBQcywgY3VycmVudCBwcmljZXMuDQogICAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIHllYXI9YXMuaW50ZWdlcihvYnNfdGltZSkNCiAgKSAlPiUNCiAgdHJhbnNtdXRlKA0KICAgIGxvY2F0aW9uLA0KICAgIHllYXIsDQogICAgbmFhZ19nYWhkaV9jdXJyZW50PW9ic192YWx1ZQ0KICApDQoNCg0Kc2ltcGxlRGF0YSRvZnRlbl9yZWZlcmVuY2VkICU+JQ0KICBmaWx0ZXIoDQogICAgbXVsdGlwbGllcl90eXBlID09ICdBSUNfQ1VSUkVOVF9QRVJfQ0FQSVRBJywNCiAgICB5ZWFyICVpbiUgYygyMDA1LDIwMTAsMjAxNSwyMDE4KQ0KICApICU+JQ0KICBtZXJnZSggbmFhZ0RhdGEsYnk9YygnbG9jYXRpb24nLCd5ZWFyJykgKSAlPiUNCiAgZ2dwbG90KGFlcyhsb2cobmZhX2FnZGlfaGgpLGxvZyhuYWFnX2dhaGRpX2N1cnJlbnQpICkpICsNCiAgICBnZW9tX2FibGluZShzbG9wZT0xKSArDQogICAgZ2VvbV9wb2ludChjb2xvcj0ncmVkJyxhZXMoc2l6ZT1wb3ApLGFscGhhPTAuNykgKw0KICAgIGdlb21fc21vb3RoKA0KICAgICAgbWV0aG9kPWxtLA0KICAgICAgZnVsbHJhbmdlPVQNCiAgICApICsNCiAgICBsYWJzKA0KICAgICAgeD0nbG9nIGdyb3NzIGFkanVzdGVkIGhvdXNlaG9sZCBkaXNwb3NhYmxlIGluY29tZSBwZXIgY2FwaXRhIGF0IFBQUHMgZm9yIEFJQyAoY3VycmVudCBkb2xsYXJzKVxucGVyIG15IGNhbGN1bGF0aW9ucycsDQogICAgICB5PSdsb2cgZ3Jvc3MgYWRqdXN0ZWQgaG91c2Vob2xkIGRpc3Bvc2FibGUgaW5jb21lIHBlciBjYXBpdGEgYXQgUFBQcyBmb3IgQUlDIChjdXJyZW50IGRvbGxhcnMpXG5wZXIgT0VDRCBOQUFHJw0KICAgICkgKw0KICAgIHN0YXRfcG9seV9lcSgNCiAgICAgIGFlcyhsYWJlbCA9ICBwYXN0ZShzdGF0KGVxLmxhYmVsKSxzdGF0KHJyLmxhYmVsKSwgc2VwID0gIipcIiwgXCIqIikpLA0KICAgICAgZm9ybXVsYSA9ICd5fngnLA0KICAgICAgcGFyc2UgPSBUUlVFDQogICAgKSArDQogICAgdGhlbWVfaXBzdW0oKSArDQogICAgZmFjZXRfd3JhcCh+eWVhcixzY2FsZT0nZnJlZScpDQoNCg0KDQpgYGANCg0KDQojIyBjcm9zc2NoZWNrIGFnYWluc3QgR0RQIGFuZCBBSUMgDQoNClRoZXNlIG1hdGNoIGV4YWN0bHkgKCsvLSBuZWdsaWdpYmxlIGZsb2F0aW5nIHBvaW50IHByZWNpc2lvbiBlcnJvcikNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9VFJVRX0NCg0KDQp1cmw9J2h0dHBzOi8vc3RhdHMub2VjZC5vcmcvcmVzdHNkbXgvc2RteC5hc2h4L0dldERhdGEvU05BX1RBQkxFMS9BVVMrQVVUK0JFTCtDQU4rQ0hMK0NPTCtDWkUrRE5LK0VTVCtGSU4rRlJBK0RFVStHUkMrSFVOK0lTTCtJUkwrSVNSK0lUQStKUE4rS09SK0xWQStMVFUrTFVYK01FWCtOTEQrTlpMK05PUitQT0wrUFJUK1NWSytTVk4rRVNQK1NXRStDSEUrVFVSK0dCUitVU0ErRUExOStFVTI4K0VVMTUrT0VDREUrT0VDRCtPVEYrTk1FQytBTEIrQVJHK0JSQStCR1IrQ1BWK0NITitDUkkrSFJWK0NZUCtIS0crSU5EK0lETitNREcrTUxUK01BUitNS0QrUEVSK1JPVStSVVMrU0FVK1NSQitTR1ArWkFGK1pNQitGUk1FK0RFVy5CMV9HRStQNDEuSENQQytIVlBWT0IvYWxsP3N0YXJ0VGltZT0xOTcwJmVuZFRpbWU9MjAxOScNCg0KYmFzaWNBZ2dzPWZldGNoU0RNWFVybCh1cmwpICU+JQ0KICBjbGVhbl9uYW1lcygpICU+JQ0KICBtdXRhdGUoDQogICAgeWVhcj1hcy5pbnRlZ2VyKG9ic190aW1lKSwNCiAgICB2YXJpYWJsZT1zcHJpbnRmKCIlc18lcyIsdHJhbnNhY3QsbWVhc3VyZSkNCiAgKSAlPiUNCiAgZGNhc3QobG9jYXRpb24reWVhcn52YXJpYWJsZSx2YWx1ZS52YXI9J29ic192YWx1ZScpDQoNCg0KDQoNCmJhc2ljQWdncyAlPiUNCiAgbWVyZ2Uoc2ltcGxlRGF0YSRvZnRlbl9yZWZlcmVuY2VkLGJ5PWMoJ2xvY2F0aW9uJywneWVhcicpKSAlPiUNCiAgZmlsdGVyKA0KICAgIHllYXIgJWluJSBjKDIwMDUsMjAxMCwyMDE1LDIwMTgpLA0KICAgIG11bHRpcGxpZXJfdHlwZT09J0FJQ19DT05TVEFOVF9QRVJfQ0FQSVRBJw0KICApICU+JQ0KICBnZ3Bsb3QoYWVzKGxvZyhzbmExX2FpYyksbG9nKFA0MV9IVlBWT0IpKSkgKw0KICAgIGdlb21fYWJsaW5lKHNsb3BlPTEpICsNCiAgICBmYWNldF93cmFwKH55ZWFyLHNjYWxlPSdmcmVlX3knKSArDQogICAgZ2VvbV9wb2ludChjb2xvcj0ncmVkJyxhZXMoc2l6ZT1wb3ApLGFscGhhPTAuNykgKw0KICAgIGdlb21fc21vb3RoKA0KICAgICAgbWV0aG9kPWxtLA0KICAgICAgZnVsbHJhbmdlPVQNCiAgICApICsNCiAgICBzdGF0X3BvbHlfZXEoDQogICAgICBhZXMobGFiZWwgPSAgcGFzdGUoc3RhdChlcS5sYWJlbCksc3RhdChyci5sYWJlbCksIHNlcCA9ICIqXCIsIFwiKiIpKSwNCiAgICAgIGZvcm11bGEgPSAneX54JywNCiAgICAgIHBhcnNlID0gVFJVRQ0KICAgICkgKw0KICAgIHRoZW1lX2lwc3VtKCkgKw0KICAgIGd1aWRlcyhzaXplPSdub25lJykgKw0KICAgIGxhYnMoDQogICAgICB4PSdsb2cgQUlDIHBlciBjYXBpdGEgYXQgY29uc3RhbnQgUFBQcyBmb3IgQUlDIChteSBzaWRlKScsDQogICAgICB5PSdsb2cgQUlDIHBlciBjYXBpdGEgYXQgY29uc3RhbnQgUFBQcyBmb3IgQUlDIChPRUNEIHNpZGUpJw0KICAgICkNCg0KDQoNCmJhc2ljQWdncyAlPiUNCiAgbWVyZ2Uoc2ltcGxlRGF0YSRvZnRlbl9yZWZlcmVuY2VkLGJ5PWMoJ2xvY2F0aW9uJywneWVhcicpKSAlPiUNCiAgZmlsdGVyKA0KICAgIHllYXIgJWluJSBjKDIwMDUsMjAxMCwyMDE1LDIwMTgpLA0KICAgIG11bHRpcGxpZXJfdHlwZT09J0FJQ19DVVJSRU5UX1BFUl9DQVBJVEEnDQogICkgJT4lDQogIGdncGxvdChhZXMobG9nKHNuYTFfYWljKSxsb2coUDQxX0hDUEMpKSkgKw0KICAgIGdlb21fYWJsaW5lKHNsb3BlPTEpICsNCiAgICBmYWNldF93cmFwKH55ZWFyLHNjYWxlPSdmcmVlX3knKSArDQogICAgZ2VvbV9wb2ludChjb2xvcj0ncmVkJyxhZXMoc2l6ZT1wb3ApLGFscGhhPTAuNykgKw0KICAgIGdlb21fc21vb3RoKA0KICAgICAgbWV0aG9kPWxtLA0KICAgICAgZnVsbHJhbmdlPVQNCiAgICApICsNCiAgICBzdGF0X3BvbHlfZXEoDQogICAgICBhZXMobGFiZWwgPSAgcGFzdGUoc3RhdChlcS5sYWJlbCksc3RhdChyci5sYWJlbCksIHNlcCA9ICIqXCIsIFwiKiIpKSwNCiAgICAgIGZvcm11bGEgPSAneX54JywNCiAgICAgIHBhcnNlID0gVFJVRQ0KICAgICkgKw0KICAgIHRoZW1lX2lwc3VtKCkgKw0KICAgIGd1aWRlcyhzaXplPSdub25lJykgKw0KICAgIGxhYnMoDQogICAgICB4PSdsb2cgQUlDIHBlciBjYXBpdGEgYXQgY3VycmVudCBQUFBzIGZvciBBSUMgKG15IHNpZGUpJywNCiAgICAgIHk9J2xvZyBBSUMgcGVyIGNhcGl0YSBhdCBjdXJyZW50IFBQUHMgZm9yIEFJQyAoT0VDRCBzaWRlKScNCiAgICApDQoNCg0KDQoNCmJhc2ljQWdncyAlPiUNCiAgbWVyZ2Uoc2ltcGxlRGF0YSRvZnRlbl9yZWZlcmVuY2VkLGJ5PWMoJ2xvY2F0aW9uJywneWVhcicpKSAlPiUNCiAgZmlsdGVyKA0KICAgIHllYXIgJWluJSBjKDIwMDUsMjAxMCwyMDE1LDIwMTgpLA0KICAgIG11bHRpcGxpZXJfdHlwZT09J0dEUF9DT05TVEFOVF9QRVJfQ0FQSVRBJw0KICApICU+JQ0KICBnZ3Bsb3QoYWVzKGxvZyhzbmExX2dkcCksbG9nKEIxX0dFX0hWUFZPQikpKSArDQogICAgZ2VvbV9hYmxpbmUoc2xvcGU9MSkgKw0KICAgIGZhY2V0X3dyYXAofnllYXIsc2NhbGU9J2ZyZWVfeScpICsNCiAgICBnZW9tX3BvaW50KGNvbG9yPSdyZWQnLGFlcyhzaXplPXBvcCksYWxwaGE9MC43KSArDQogICAgZ2VvbV9zbW9vdGgoDQogICAgICBtZXRob2Q9bG0sDQogICAgICBmdWxscmFuZ2U9VA0KICAgICkgKw0KICAgIHN0YXRfcG9seV9lcSgNCiAgICAgIGFlcyhsYWJlbCA9ICBwYXN0ZShzdGF0KGVxLmxhYmVsKSxzdGF0KHJyLmxhYmVsKSwgc2VwID0gIipcIiwgXCIqIikpLA0KICAgICAgZm9ybXVsYSA9ICd5fngnLA0KICAgICAgcGFyc2UgPSBUUlVFDQogICAgKSArDQogICAgdGhlbWVfaXBzdW0oKSArDQogICAgZ3VpZGVzKHNpemU9J25vbmUnKSArDQogICAgbGFicygNCiAgICAgIHg9J2xvZyBHRFAgcGVyIGNhcGl0YSBhdCBjb25zdGFudCBQUFBzIGZvciBHRFAgKG15IHNpZGUpJywNCiAgICAgIHk9J2xvZyBHRFAgcGVyIGNhcGl0YSBhdCBjb25zdGFudCBQUFBzIGZvciBHRFAgKE9FQ0Qgc2lkZSknDQogICAgKQ0KDQoNCg0KYmFzaWNBZ2dzICU+JQ0KICBtZXJnZShzaW1wbGVEYXRhJG9mdGVuX3JlZmVyZW5jZWQsYnk9YygnbG9jYXRpb24nLCd5ZWFyJykpICU+JQ0KICBmaWx0ZXIoDQogICAgeWVhciAlaW4lIGMoMjAwNSwyMDEwLDIwMTUsMjAxOCksDQogICAgbXVsdGlwbGllcl90eXBlPT0nR0RQX0NVUlJFTlRfUEVSX0NBUElUQScNCiAgKSAlPiUNCiAgZ2dwbG90KGFlcyhsb2coc25hMV9nZHApLGxvZyhCMV9HRV9IQ1BDKSkpICsNCiAgICBnZW9tX2FibGluZShzbG9wZT0xKSArDQogICAgZmFjZXRfd3JhcCh+eWVhcixzY2FsZT0nZnJlZV95JykgKw0KICAgIGdlb21fcG9pbnQoY29sb3I9J3JlZCcsYWVzKHNpemU9cG9wKSxhbHBoYT0wLjcpICsNCiAgICBnZW9tX3Ntb290aCgNCiAgICAgIG1ldGhvZD1sbSwNCiAgICAgIGZ1bGxyYW5nZT1UDQogICAgKSArDQogICAgc3RhdF9wb2x5X2VxKA0KICAgICAgYWVzKGxhYmVsID0gIHBhc3RlKHN0YXQoZXEubGFiZWwpLHN0YXQocnIubGFiZWwpLCBzZXAgPSAiKlwiLCBcIioiKSksDQogICAgICBmb3JtdWxhID0gJ3l+eCcsDQogICAgICBwYXJzZSA9IFRSVUUNCiAgICApICsNCiAgICB0aGVtZV9pcHN1bSgpICsNCiAgICBndWlkZXMoc2l6ZT0nbm9uZScpICsNCiAgICBsYWJzKA0KICAgICAgeD0nbG9nIEdEUCBwZXIgY2FwaXRhIGF0IGN1cnJlbnQgUFBQcyBmb3IgR0RQIChteSBzaWRlKScsDQogICAgICB5PSdsb2cgR0RQIHBlciBjYXBpdGEgYXQgY3VycmVudCBQUFBzIGZvciBHRFAgKE9FQ0Qgc2lkZSknDQogICAgKQ0KDQpgYGANCg0K