# Packages
pacman::p_load(
  here,tidyverse,tidymodels,reactable,thematic,scales,arrow
)

results <- here("Tax_and_pension_progressivity","Processed_data")
data_dir <- here("Raw_data")
# Years
sourceyears = c(1991:2019)
# Graphics
theme_set(theme_bw(base_family = "serif"))
thematic_on(
  bg = "white", fg = "black",
  accent = "auto", 
  qualitative = okabe_ito()
)
# This will be needed later when we check the accuracy of our parametric estimates
f_indices <- 
  function(X){
    # Function to calculate area under curve
    f_area <- 
      function(x,y){
        h = x-lag(x)
        h = ifelse(is.na(h),0,h)
        ab = y+lag(y)
        ab = ifelse(is.na(ab),y,ab)
        L = sum(0.5*h*ab)
        K = 0.5
        S = 1-(L/K)
      }
    
    # Function to caclculate indices
    I <- 
      X %>% 
      # Restrict sample
      filter(x>=0&y>=0&t>=0) %>% 
      select(year,x,y,t) %>% 
      # Sample stats
      mutate(
        across(
          c('x','y','t'),
          list(Mean = mean, Tot = sum),
          .names = "{.fn}_{.col}"
        )
      ) %>% 
      # Average size
      mutate(
       size = Tot_t/Tot_x
      ) %>% 
      # Shares
      mutate(
        px = x/Tot_x, py = y/Tot_y, pt = t/Tot_t, num=1,
        obs =1/sum(num)
      ) %>% 
      # Distribution wrt x (Prefix 'X_')
      arrange(x) %>% 
      mutate(
        across(
          c('px','py','pt','obs'),
          cumsum,
          .names = "X_{.col}"
        ),
        
        # Indices (covariance formula)
        Gx_cov = (2/Mean_x)*(cov(x,X_obs)), # Gini coef
        Ct = (2/Mean_t)*(cov(t,X_obs)),
        St = (2/Mean_t)*(cov(t,X_px)),
        Cy_cov = (2/Mean_y)*(cov(y,X_obs)),
        K_cov  = Ct - Gx_cov,
        S_cov  = St - Gx_cov,
        
        # Indices (area formula)
        Gx_area = f_area(X_obs,X_px), 
        Cy_area = f_area(X_obs,X_py), 
        Ct = f_area(X_obs,X_pt),
        S_area = f_area(X_px,X_pt),
        K_area = Ct - Gx_area
      ) %>% 
      # Redistributive effect
      arrange(y) %>% 
      mutate(
        across(
          c('px','py','pt','obs'),
          cumsum,
          .names = "Y_{.col}"
        ),
        Gy_area = f_area(Y_obs,Y_py),
        Gy_cov  = (2/Mean_y)*(cov(y,Y_obs)),
        R_area = Gx_area - Gy_area,
        R_cov = Gx_cov - Gy_cov,
        Rerank_area = Gy_area - Cy_area,
        Rerank_cov = Gy_cov - Cy_cov,
      ) %>% 
      select(year,size,ends_with('area')|ends_with('cov')) %>% 
      filter(row_number()==1)
    
  }

1 Introduction

This appendix explores in detail the estimation of the parametric tax function used in this paper. The estimation of parameters is non-trivial. Parameter values are highly sensitive to the sample that is used in the estimation. In summary, we find that the parametric function does not provide a good fit on raw tax data. However, the fit significantly improves once we restrict the sample within the bounds of the statutory bottom and top income thresholds and top tax rates for each respective year.

Most importantly, we find that including incomes below the tax-free threshold results in over-estimating average tax rates at the bottom and under-estimating tax rates at the top. In turn, the progressivity of the tax code itself is under-estimated. Thus, incomes below the tax-free threshold needs to be excluded while estimating the parametric tax function.

2 Functional form and estimation method

Let \(x\) be post-tax income and \(y\) be some notion of pre-tax income (we shall explore different income concepts). The parametric function maps from \(y\) to \(x\) as

\[ x=\lambda y^{1-\tau} \]

Income tax is given by

\[ T=y-\lambda y^{1-\tau} \]

The average tax rate is

\[ t=1-\lambda y^{-\tau} \]

We estimate \(\tau\) and \(\lambda\) by two methods - ordinary least squares (OLS) and non-linear least squares (NLS).

# 1.0 OLS (lm)
f_tax_lm <- 
  function(X){
    # Take the natural log of the tax function
    X <- 
      X %>% 
      filter(x>0&y>0) %>% 
      mutate(log_x = log(x), log_y=log(y))
    
    # Estimate the model
    lm_fit <- 
      linear_reg() %>% 
      set_engine('lm') %>% 
      set_mode('regression') %>% 
      fit(log_x~log_y,data=X)
    
    # Collect the results
    Metrics <- 
        glance(lm_fit) %>% 
        select(adj.r.squared,sigma,AIC,BIC,nobs) %>% 
        rename(RMSE=sigma,Rsquared=adj.r.squared)
  
    Coefs <- 
      tidy(lm_fit) %>%
      mutate(
        term = if_else(term=="(Intercept)","lambda","tau"),
        value = if_else(term=="lambda",exp(estimate),1-estimate),
        ci1 = value - 1.96*std.error,
        ci2 = value + 1.96*std.error
      ) %>% 
      select(term,value,ci1,ci2) %>% 
      bind_cols(.,Metrics) %>% 
      mutate(year=X$year[1]) %>% 
      relocate(year)        
  }

# 2.0 NLS
f_tax_nls <- 
  function(X){
    nlsfit <- 
      nls(atr~1-lambda*y^(-tau),X, start = list(lambda=1,tau=0.1))
    
    Metrics <- 
      glance(nlsfit) %>% 
      select(sigma,AIC,BIC,nobs) %>% 
      rename(RMSE=sigma)
    
    Coefs <- 
      tidy(nlsfit) %>%
      mutate(
        ci1 = estimate - 1.96*std.error,
        ci2 = estimate + 1.96*std.error
      ) %>% 
      select(term,estimate,ci1,ci2) %>%
      bind_cols(.,Metrics) %>% 
      mutate(year=X$year[1]) %>% 
      relocate(year) %>% 
      rename(value=estimate)
  }

3 Income and tax concepts

We take taxable income “ic_taxable_income_loss” as pre-tax income, net tax after offsets and credits “tc_net_tax” as our tax variable. Both income and tax are adjusted for inflation using the CPI and expressed in 2019 dollars.

varz <- c("ic_taxable_income_loss","tc_net_tax")

CPI <- 
  read_csv(here(data_dir,"CPI.csv"),show_col_types = F)

f_nominal_to_real <- 
  function(X,varz_to_real,CPI,i){
    X %>% 
      mutate(cpi=CPI$cpi[i],
         across(
           all_of(varz_to_real),
           ~(./cpi)
         ))
  }

4 Income tax code (1991-2019)

Prior to estimating the tax function on the data, we begin by examining the progressivity of the income tax code between 1991-2019. The Australian income tax code is complex with multiple thresholds, marginal tax rates and various tax credits, allowances, deductions and offsets that have changed considerably over the past two decades.

Tracking all these changes to the statutory tax code is a challenging task. Thus, our approach is to focus on the changes to the standard marginal tax rates and thresholds from available public information. We shall capture detailed changes in later sections when we estimate the tax function on the data.

Tax_code_data <- 
  read_csv(here("Raw_data","Tax_rates.csv"),
              show_col_types = FALSE)
Tax_code_data_real <- 
  full_join(Tax_code_data,CPI,by="year") %>% 
  mutate(
    across(
      c(y1,y2,tax),
      ~(./cpi)
    )
  )

The following function estimates tax liability using the tax code data.

f_estimate_tax <- 
  function(X,TD){
# First getting the data into an easier format
## TD - tax code data (can be either nominal or real)
## X - df with raw data (year,inc,tax)

ty <- 
  TD %>% 
  select(year,y1) %>%
  group_by(year) %>% 
  mutate(y=paste0("y",row_number())) %>% 
  pivot_wider(names_from = "y", values_from = "y1")

tr <- 
  TD %>% 
  select(year,mtr) %>%
  group_by(year) %>% 
  mutate(mtr=mtr/100,
         y=paste0("mtr",row_number())) %>% 
  pivot_wider(names_from = "y", values_from = "mtr")

tx <- 
  TD %>% 
  select(year,tax) %>%
  group_by(year) %>% 
  mutate(
    y=paste0("tx",row_number())) %>% 
  pivot_wider(names_from = "y", values_from = "tax")

Tax_code <- 
  left_join(ty,tr,by="year") %>% 
  left_join(.,tx,by="year")

# The following estimates tax liability and selects the right MTR
A <- 
      left_join(X,Tax_code,by="year") %>% 
      arrange(y) %>% 
      mutate(
        tax = 0,
        tax = if_else(y>=y1 & y<y2 , tx1 + (y-y1+1)*mtr1, tax),
        tax = if_else(y>=y2 & y<y3 , tx2 + (y-y2+1)*mtr2, tax),
        tax = if_else(y>=y3 & y<y4 , tx3 + (y-y3+1)*mtr3, tax),
        tax = if_else(y>=y4 & y<y5 , tx4 + (y-y4+1)*mtr4, tax),
        tax = if_else(y>=y5 & year!=1991 & year!=1994,
                      tx5 + (y-y5+1)*mtr5, tax
                      ),
        tax = if_else(
          year==1991,
          case_when(
            y>=y1 & y<y2 ~ tx1 + (y-y1+1)*mtr1,
            y>=y2 & y<y3 ~ tx2 + (y-y2+1)*mtr2,
            y>=y3 & y<y4 ~ tx3 + (y-y3+1)*mtr3,
            y>=y4 & y<y5 ~ tx4 + (y-y4+1)*mtr4,
            y>=y5 & y<y6 ~ tx5 + (y-y5+1)*mtr5,
            y>=y6 & y<y7 ~ tx6 + (y-y6+1)*mtr6,
            y>=y7 & y<y8 ~ tx7 + (y-y7+1)*mtr7,
            y>=y8        ~ tx4 + (y-y8+1)*mtr8,
          ),
          tax
        ),
        tax = if_else(
          year==1994,
          case_when(
            y>=y1 & y<y2 ~ tx1 + (y-y1+1)*mtr1,
            y>=y2 & y<y3 ~ tx2 + (y-y2+1)*mtr2,
            y>=y3 & y<y4 ~ tx3 + (y-y3+1)*mtr3,
            y>=y4 & y<y5 ~ tx4 + (y-y4+1)*mtr4,
            y>=y5 & y<y6 ~ tx5 + (y-y5+1)*mtr5,
            y>=y6 ~ tx6 + (y-y6+1)*mtr6,
          ),
          tax
        ),
        mtr=0,
        mtr = 
          if_else(
            year==1991,
            case_when(
              y>=y1 & y<y2 ~ mtr1,
              y>=y2 & y<y3 ~ mtr2,
              y>=y3 & y<y4 ~ mtr3,
              y>=y4 & y<y5 ~ mtr4,
              y>=y5 & y<y6 ~ mtr5,
              y>=y6 & y<y7 ~ mtr6,
              y>=y7 & y<y8 ~ mtr7,
              y>=y8        ~ mtr8,
            ),
            mtr),
        mtr =
            if_else(
              year==1994,
              case_when(
                y>=y1 & y<y2 ~ mtr1,
                y>=y2 & y<y3 ~ mtr2,
                y>=y3 & y<y4 ~ mtr3,
                y>=y4 & y<y5 ~ mtr4,
                y>=y5 & y<y6 ~ mtr5,
                y>=y6 ~ mtr6,
              ),
              mtr),
        mtr =
          if_else(
            year!=1991&year!=1994,
            case_when(
              y>=y1 & y<y2 ~ mtr1,
              y>=y2 & y<y3 ~ mtr2,
              y>=y3 & y<y4 ~ mtr3,
              y>=y4 & y<y5 ~ mtr4,
              y>=y5 ~ mtr5
              ),
            mtr
            )
          ) %>% 
      rename(taxfree=y2) %>% 
      select(pid,year,y,tax,mtr,taxfree)
}

4.1 Tax code table (nominal)

Z <- 
  Tax_code_data %>% 
  mutate(
    across(
      c(y1,y2,tax),
      ~format(.,big.mark=",")
    ),
    mtr = round(mtr,digits = 2)
  ) %>% 
  rename(
    Year=year,
    From=y1,
    To=y2,
    `Tax amount`=tax,
    `Marginal tax rate (%)`=mtr
  ) 

htmltools::tagList(
  reactable(
    Z,
    defaultColDef = 
      colDef(
        align = "center"
      ),
    bordered = T,
    highlight = T,
    filterable = T
    
  )
)

4.2 Tax code table (real 2019$)

Z <- 
  Tax_code_data_real %>% 
  mutate(
    across(
      c(y1,y2,tax,mtr),
      ~round(.,digits = 2)
    ),
    across(
      c(y1,y2,tax),
      ~format(.,big.mark=",")
    ),
  ) %>% 
  rename(
    Year=year,
    From=y1,
    To=y2,
    `Tax amount`=tax,
    `Marginal tax rate (%)`=mtr
  ) %>% 
  select(-cpi)

htmltools::tagList(
  reactable(
    Z,
    defaultColDef = 
      colDef(
        align = "center"
      ),
    bordered = T,
    highlight = T,
    filterable = T
    
  )
)

4.3 Average tax rates

We estimate statutory tax and the average tax rate for each year.

for (i in seq_along(sourceyears)) {
  Z <- 
    seq(0,200000,by=100) %>%
    as_tibble() %>% 
    rename(y=value) %>% 
    mutate(pid=1, # Artefact in function
           year=sourceyears[i]) %>% 
    f_estimate_tax(.,Tax_code_data_real) %>% 
    mutate(atr=if_else(y==0,0,tax/y))
  
  if (i==1){
    X=Z
  }
  else{
    X=bind_rows(X,Z)
  }
}
X %>% 
  filter(year==1995|year==2000|year==2005|year==2010|year==2019) %>% 
  mutate(Year=as.factor(year),
         atr=atr*100) %>% 
  ggplot(aes(x=y,y=atr,color=Year,shape=Year)) +
  geom_line() +
  labs(x="Real Income (2019$)", y="Average tax rate (%)") +
  scale_x_continuous(label=comma, breaks=seq(0,200000,20000))

ggsave(
  here(results,"TP_01_ATR_Statutory.pdf")
)
Saving 6.88 x 4.25 in image

NA

4.4 Parametric tax function

We begin by exploring the difference between statutory average tax rates caculated using the tax code and those estimated using the parametric tax function.

Estimating the tax function for the entire range of income between 0 and 200,000 including incomes below the tax-free threshold results over-estimating average tax rates at the bottom and under-estimating average tax rates at the top.

Z <- 
    seq(0,200000,by=1000) %>%
    as_tibble() %>% 
    rename(y=value) %>% 
    mutate(pid=1, # Artefact in function
           year=2019) %>% 
    f_estimate_tax(.,Tax_code_data_real) %>% 
    mutate(atr=if_else(y==0,0,tax/y),
           x=y-tax)
X <- 
  f_tax_lm(Z)

Z <- 
  Z %>% 
  mutate(
    OLS = (1-X$value[1]*y^(-(X$value[2])))*100
  ) %>% 
  mutate(atr=atr*100)

Z %>% 
  ggplot(aes(x=y)) +
  geom_line(aes(y=atr,color="Data"),show.legend = T) +
  geom_line(aes(y=OLS,color="Estimated"), show.legend = T) +
  labs(x="Income",y="Average tax rate",
       title="Including incomes below tax-free threshold") +
  scale_color_manual(
    name = "ATR",
    guide = 'legend',
    values = c("Data"="blue","Estimated"="red")
  ) +
  scale_x_continuous(label=comma, breaks=seq(0,200000,20000))

ggsave(
  here(results,"TP_01_ATR_TaxFunc_Including_below_tax_free.pdf")
)
Saving 6.88 x 4.25 in image

Excluding incomes below the tax-free threshold (by restricting the average tax rate to strictly positive values) significantly improves the fit of the estimated tax function.

Z <- 
    seq(0,200000,by=1000) %>%
    as_tibble() %>% 
    rename(y=value) %>% 
    mutate(pid=1, # Artefact in function
           year=2019) %>% 
    f_estimate_tax(.,Tax_code_data_real) %>% 
    mutate(atr=if_else(y==0,0,tax/y),
           x=y-tax) %>% 
    filter(atr>0)

X <- 
  f_tax_lm(Z)

Z <- 
  Z %>% 
  mutate(
    OLS = (1-X$value[1]*y^(-(X$value[2])))*100
  ) %>% 
  mutate(atr=atr*100)

Z %>% 
  ggplot(aes(x=y)) +
  geom_line(aes(y=atr,color="Data"),show.legend = T) +
  geom_line(aes(y=OLS,color="Estimated"), show.legend = T) +
  labs(x="Income",y="Average tax rate",
       title="Excluding incomes below tax-free threshold") +
  scale_color_manual(
    name = "ATR",
    guide = 'legend',
    values = c("Data"="blue","Estimated"="red")
  ) 

ggsave(
  here(results,"TP_01_ATR_TaxFunc_Excluding_below_tax_free.pdf")
)
Saving 6.88 x 4.25 in image

5 ALife data 1991-2019

5.1 Sample restrictions

We impose the following sample restrictions on the data.

  1. \(y^{taxfree}\le y\le200,000\)

    Income concept \(y\) is restricted between the tax free threshold for each year and $200,000 (in real terms). For the years in our sample, the top tax bracket ranges between $70,000 and $193,000

  2. \(0< t \le0.47\)

    Those with a negative average tax rate \(t\) and those with a tax rate over 47% are excluded from the sample. The rationale for this is that the top average tax rate between 1991-2019 was either 45% or 47%. As shown in the previous section, excluding those below the tax-free threshold ensures a better fit with actual tax rates.

5.2 Demonstration on 2019 data

A <- 
  read_feather(
    here("Raw_data","LI_01_Derived_2019.feather"),
    as_data_frame = F
  ) %>% 
  mutate(y=ic_taxable_income_loss) %>% 
  select(pid,age,year,y,inc_tax) %>% 
  collect()

Z <- 
  left_join(A,CPI,by="year") %>% 
  mutate(
    across(
      c(y,inc_tax),
      ~./cpi
    )
  ) %>% 
  filter(inc_tax>=0&y>=0) %>% 
  mutate(
    atr = if_else(y==0,0,inc_tax/y),
    x = y-inc_tax # Post-tax income needed for OLS estimation
  ) %>% 
  filter(y>=0&y<=200000) %>% 
  filter(atr>0&atr<=0.47)

B <- 
  Z %>% 
  f_estimate_tax(.,Tax_code_data_real) %>% 
  mutate(tc_atr=if_else(y==0,0,tax/y), # tc_atr is tax code atr
         tc_tax=tax)

Z <- 
  full_join(B,Z,by=c("pid","year","y")) %>% 
  filter(y>=taxfree&atr>0)

OLS <- f_tax_lm(Z) %>% mutate(method="OLS")
NLS <- f_tax_nls(Z) %>% mutate(method="NLS")

Coefs_temp <- bind_rows(OLS,NLS) 

Q <- 
  Z %>% 
  mutate(
    OLS = 1-OLS$value[1]*y^(-OLS$value[2]),
    NLS = 1-NLS$value[1]*y^(-NLS$value[2])
  )
QQ <- 
  Q %>% 
  slice_sample(prop=0.01) %>% 
  select(y,atr,tc_atr,OLS,NLS) %>% 
  rename(Income=y,Actual=atr,Statutory=tc_atr)

QQ %>% 
  ggplot(aes(x=Income)) +
  geom_point(aes(y=Actual*100,color="Data"),show.legend = T) +
  geom_line(aes(y=Statutory*100,color="Statutory"), show.legend = T) +
  geom_point(aes(y=OLS*100,color="OLS"), show.legend = T) +
  geom_line(aes(y=NLS*100,color="NLS"), show.legend = T) +
  labs(x="Income",y="Average tax rate (%)") +
  scale_color_manual(
    name = "ATR",
    guide = 'legend',
    values = c("Data"="gray",
               "Statutory"="black",
               "OLS"="red",
               "NLS"="green")
  ) +
  scale_x_continuous(label=comma)

ggsave(
  here(results,"TP_01_ATR_OLS_NLS_Actual_2019.pdf")
)
Saving 6.88 x 4.25 in image

5.3 Trend in progressivity

sourceyears <- c(1991:2019)
for (i in seq_along(sourceyears)) {

    A <- 
      read_feather(here("Raw_data", 
                        paste0("LI_01_Derived_",sourceyears[i],".feather")),
                   as_data_frame = F) %>%
      mutate(y = ic_taxable_income_loss) %>%
      select(pid, age, year, y, inc_tax) %>%
      collect()

Z <- 
  left_join(A,CPI,by="year") %>% 
  mutate(
    across(
      c(y,inc_tax),
      ~./cpi
    )
  ) %>% 
  filter(inc_tax>=0&y>=0) %>% 
  mutate(
    atr = if_else(y==0,0,inc_tax/y),
    x = y-inc_tax # Post-tax income needed for OLS estimation
  ) %>% 
  filter(y>=0&y<=200000) %>% 
  filter(atr>0&atr<=0.47)

B <- 
  Z %>% 
  f_estimate_tax(.,Tax_code_data_real) %>% 
  mutate(tc_atr=if_else(y==0,0,tax/y), # tc_atr is tax code atr
         tc_tax=tax)

Z <- 
  full_join(B,Z,by=c("pid","year","y")) %>% 
  filter(y>=taxfree&atr>0)

OLS <- f_tax_lm(Z) %>% mutate(method="OLS")
NLS <- f_tax_nls(Z) %>% mutate(method="NLS")

Coefs_temp <- bind_rows(OLS,NLS)     

    Q1 <- 
      Z %>% 
      mutate(
        t = y-OLS$value[1]*y^(1-OLS$value[2]),
        x = y-t,
      ) %>% 
      f_indices(.)
    
    Q2 <- 
      Z %>% 
      mutate(
        t = y-NLS$value[1]*y^(1-NLS$value[2]),
        x = y-t,
      ) %>% 
      f_indices(.)
    
    QQ <- 
      A %>% 
      mutate(
        t = inc_tax,
        x = y-t
      ) %>% 
      f_indices(.)
    
    Coefs_temp <- 
      bind_rows(OLS,NLS) %>% 
      mutate(Suits_est = if_else(method=="OLS",Q1$S_area[1],Q2$S_area[1]),
             Suits_data = QQ$S_area[1])
    
    if (i==1){
      Coefs=Coefs_temp
    }
    else{
      Coefs=bind_rows(Coefs,Coefs_temp)
    }
    
    print(sourceyears[i])

}

saveRDS(Coefs,here(results,"TP_01_Tau_Lambda_Suits_data.rds"))
X <- 
  readRDS(here(results,"TP_01_Tau_Lambda_Suits_data.rds")) %>% 
  filter(term=="tau")

X %>% 
  ggplot(aes(x=year,y=value,color=method,shape=method)) +
  geom_point() +
  geom_line() +
 labs(
    x = "Year", y = expression(paste("Progressivity ",tau))
  ) +
  scale_x_continuous(breaks = seq(1990,2020,by=2)) +
  theme(
    axis.title.x = element_text(margin = margin(t=10)),
    axis.title.y = element_text(margin = margin(t=10))
  )

ggsave(
  here(results,"TP_01_Tau_trends_data.pdf")
)
Saving 6.88 x 4.25 in image

5.4 Check if Suits index matches

X <- 
  readRDS(here(results,"TP_01_Tau_Lambda_Suits_data.rds")) %>% 
  filter(term=="tau"&method=="NLS") %>% 
  rename(Estimate=Suits_est,Data=Suits_data) %>% 
  pivot_longer(cols = c(Estimate,Data), names_to = "Suits index", values_to = "yy")


X %>% 
  ggplot(aes(x=year,y=yy,color=`Suits index`,shape=`Suits index`)) +
  geom_point() +
  geom_line() +
 labs(
    x = "Year", y = "Suits index (taxable income as base)"
  ) +
  scale_x_continuous(breaks = seq(1990,2020,by=2)) +
  theme(
    axis.title.x = element_text(margin = margin(t=10)),
    axis.title.y = element_text(margin = margin(t=10))
  )

ggsave(
  here(results,"TP_01_Check_suits_index_tau_estimate.pdf")
)
Saving 6.88 x 4.25 in image

LS0tDQp0aXRsZTogIkVzdGltYXRpb24gb2YgdGhlIHBhcmFtZXRyaWMgdGF4IGZ1bmN0aW9uIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KLS0tDQoNCmBgYHtyIFNldHVwfQ0KIyBQYWNrYWdlcw0KcGFjbWFuOjpwX2xvYWQoDQogIGhlcmUsdGlkeXZlcnNlLHRpZHltb2RlbHMscmVhY3RhYmxlLHRoZW1hdGljLHNjYWxlcyxhcnJvdw0KKQ0KDQpyZXN1bHRzIDwtIGhlcmUoIlRheF9hbmRfcGVuc2lvbl9wcm9ncmVzc2l2aXR5IiwiUHJvY2Vzc2VkX2RhdGEiKQ0KZGF0YV9kaXIgPC0gaGVyZSgiUmF3X2RhdGEiKQ0KIyBZZWFycw0Kc291cmNleWVhcnMgPSBjKDE5OTE6MjAxOSkNCiMgR3JhcGhpY3MNCnRoZW1lX3NldCh0aGVtZV9idyhiYXNlX2ZhbWlseSA9ICJzZXJpZiIpKQ0KdGhlbWF0aWNfb24oDQogIGJnID0gIndoaXRlIiwgZmcgPSAiYmxhY2siLA0KICBhY2NlbnQgPSAiYXV0byIsIA0KICBxdWFsaXRhdGl2ZSA9IG9rYWJlX2l0bygpDQopDQpgYGANCg0KYGBge3IgZl9pbmRpY2VzfQ0KIyBUaGlzIHdpbGwgYmUgbmVlZGVkIGxhdGVyIHdoZW4gd2UgY2hlY2sgdGhlIGFjY3VyYWN5IG9mIG91ciBwYXJhbWV0cmljIGVzdGltYXRlcw0KZl9pbmRpY2VzIDwtIA0KICBmdW5jdGlvbihYKXsNCiAgICAjIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBhcmVhIHVuZGVyIGN1cnZlDQogICAgZl9hcmVhIDwtIA0KICAgICAgZnVuY3Rpb24oeCx5KXsNCiAgICAgICAgaCA9IHgtbGFnKHgpDQogICAgICAgIGggPSBpZmVsc2UoaXMubmEoaCksMCxoKQ0KICAgICAgICBhYiA9IHkrbGFnKHkpDQogICAgICAgIGFiID0gaWZlbHNlKGlzLm5hKGFiKSx5LGFiKQ0KICAgICAgICBMID0gc3VtKDAuNSpoKmFiKQ0KICAgICAgICBLID0gMC41DQogICAgICAgIFMgPSAxLShML0spDQogICAgICB9DQogICAgDQogICAgIyBGdW5jdGlvbiB0byBjYWNsY3VsYXRlIGluZGljZXMNCiAgICBJIDwtIA0KICAgICAgWCAlPiUgDQogICAgICAjIFJlc3RyaWN0IHNhbXBsZQ0KICAgICAgZmlsdGVyKHg+PTAmeT49MCZ0Pj0wKSAlPiUgDQogICAgICBzZWxlY3QoeWVhcix4LHksdCkgJT4lIA0KICAgICAgIyBTYW1wbGUgc3RhdHMNCiAgICAgIG11dGF0ZSgNCiAgICAgICAgYWNyb3NzKA0KICAgICAgICAgIGMoJ3gnLCd5JywndCcpLA0KICAgICAgICAgIGxpc3QoTWVhbiA9IG1lYW4sIFRvdCA9IHN1bSksDQogICAgICAgICAgLm5hbWVzID0gInsuZm59X3suY29sfSINCiAgICAgICAgKQ0KICAgICAgKSAlPiUgDQogICAgICAjIEF2ZXJhZ2Ugc2l6ZQ0KICAgICAgbXV0YXRlKA0KICAgICAgIHNpemUgPSBUb3RfdC9Ub3RfeA0KICAgICAgKSAlPiUgDQogICAgICAjIFNoYXJlcw0KICAgICAgbXV0YXRlKA0KICAgICAgICBweCA9IHgvVG90X3gsIHB5ID0geS9Ub3RfeSwgcHQgPSB0L1RvdF90LCBudW09MSwNCiAgICAgICAgb2JzID0xL3N1bShudW0pDQogICAgICApICU+JSANCiAgICAgICMgRGlzdHJpYnV0aW9uIHdydCB4IChQcmVmaXggJ1hfJykNCiAgICAgIGFycmFuZ2UoeCkgJT4lIA0KICAgICAgbXV0YXRlKA0KICAgICAgICBhY3Jvc3MoDQogICAgICAgICAgYygncHgnLCdweScsJ3B0Jywnb2JzJyksDQogICAgICAgICAgY3Vtc3VtLA0KICAgICAgICAgIC5uYW1lcyA9ICJYX3suY29sfSINCiAgICAgICAgKSwNCiAgICAgICAgDQogICAgICAgICMgSW5kaWNlcyAoY292YXJpYW5jZSBmb3JtdWxhKQ0KICAgICAgICBHeF9jb3YgPSAoMi9NZWFuX3gpKihjb3YoeCxYX29icykpLCAjIEdpbmkgY29lZg0KICAgICAgICBDdCA9ICgyL01lYW5fdCkqKGNvdih0LFhfb2JzKSksDQogICAgICAgIFN0ID0gKDIvTWVhbl90KSooY292KHQsWF9weCkpLA0KICAgICAgICBDeV9jb3YgPSAoMi9NZWFuX3kpKihjb3YoeSxYX29icykpLA0KICAgICAgICBLX2NvdiAgPSBDdCAtIEd4X2NvdiwNCiAgICAgICAgU19jb3YgID0gU3QgLSBHeF9jb3YsDQogICAgICAgIA0KICAgICAgICAjIEluZGljZXMgKGFyZWEgZm9ybXVsYSkNCiAgICAgICAgR3hfYXJlYSA9IGZfYXJlYShYX29icyxYX3B4KSwgDQogICAgICAgIEN5X2FyZWEgPSBmX2FyZWEoWF9vYnMsWF9weSksIA0KICAgICAgICBDdCA9IGZfYXJlYShYX29icyxYX3B0KSwNCiAgICAgICAgU19hcmVhID0gZl9hcmVhKFhfcHgsWF9wdCksDQogICAgICAgIEtfYXJlYSA9IEN0IC0gR3hfYXJlYQ0KICAgICAgKSAlPiUgDQogICAgICAjIFJlZGlzdHJpYnV0aXZlIGVmZmVjdA0KICAgICAgYXJyYW5nZSh5KSAlPiUgDQogICAgICBtdXRhdGUoDQogICAgICAgIGFjcm9zcygNCiAgICAgICAgICBjKCdweCcsJ3B5JywncHQnLCdvYnMnKSwNCiAgICAgICAgICBjdW1zdW0sDQogICAgICAgICAgLm5hbWVzID0gIllfey5jb2x9Ig0KICAgICAgICApLA0KICAgICAgICBHeV9hcmVhID0gZl9hcmVhKFlfb2JzLFlfcHkpLA0KICAgICAgICBHeV9jb3YgID0gKDIvTWVhbl95KSooY292KHksWV9vYnMpKSwNCiAgICAgICAgUl9hcmVhID0gR3hfYXJlYSAtIEd5X2FyZWEsDQogICAgICAgIFJfY292ID0gR3hfY292IC0gR3lfY292LA0KICAgICAgICBSZXJhbmtfYXJlYSA9IEd5X2FyZWEgLSBDeV9hcmVhLA0KICAgICAgICBSZXJhbmtfY292ID0gR3lfY292IC0gQ3lfY292LA0KICAgICAgKSAlPiUgDQogICAgICBzZWxlY3QoeWVhcixzaXplLGVuZHNfd2l0aCgnYXJlYScpfGVuZHNfd2l0aCgnY292JykpICU+JSANCiAgICAgIGZpbHRlcihyb3dfbnVtYmVyKCk9PTEpDQogICAgDQogIH0NCmBgYA0KDQojIEludHJvZHVjdGlvbg0KDQpUaGlzIGFwcGVuZGl4IGV4cGxvcmVzIGluIGRldGFpbCB0aGUgZXN0aW1hdGlvbiBvZiB0aGUgcGFyYW1ldHJpYyB0YXggZnVuY3Rpb24gdXNlZCBpbiB0aGlzIHBhcGVyLiBUaGUgZXN0aW1hdGlvbiBvZiBwYXJhbWV0ZXJzIGlzIG5vbi10cml2aWFsLiBQYXJhbWV0ZXIgdmFsdWVzIGFyZSBoaWdobHkgc2Vuc2l0aXZlIHRvIHRoZSBzYW1wbGUgdGhhdCBpcyB1c2VkIGluIHRoZSBlc3RpbWF0aW9uLiBJbiBzdW1tYXJ5LCB3ZSBmaW5kIHRoYXQgdGhlIHBhcmFtZXRyaWMgZnVuY3Rpb24gZG9lcyBub3QgcHJvdmlkZSBhIGdvb2QgZml0IG9uIHJhdyB0YXggZGF0YS4gSG93ZXZlciwgdGhlIGZpdCBzaWduaWZpY2FudGx5IGltcHJvdmVzIG9uY2Ugd2UgcmVzdHJpY3QgdGhlIHNhbXBsZSB3aXRoaW4gdGhlIGJvdW5kcyBvZiB0aGUgc3RhdHV0b3J5IGJvdHRvbSBhbmQgdG9wIGluY29tZSB0aHJlc2hvbGRzIGFuZCB0b3AgdGF4IHJhdGVzIGZvciBlYWNoIHJlc3BlY3RpdmUgeWVhci4NCg0KTW9zdCBpbXBvcnRhbnRseSwgd2UgZmluZCB0aGF0IGluY2x1ZGluZyBpbmNvbWVzIGJlbG93IHRoZSB0YXgtZnJlZSB0aHJlc2hvbGQgcmVzdWx0cyBpbiBvdmVyLWVzdGltYXRpbmcgYXZlcmFnZSB0YXggcmF0ZXMgYXQgdGhlIGJvdHRvbSBhbmQgdW5kZXItZXN0aW1hdGluZyB0YXggcmF0ZXMgYXQgdGhlIHRvcC4gSW4gdHVybiwgdGhlIHByb2dyZXNzaXZpdHkgb2YgdGhlIHRheCBjb2RlIGl0c2VsZiBpcyB1bmRlci1lc3RpbWF0ZWQuIFRodXMsIGluY29tZXMgYmVsb3cgdGhlIHRheC1mcmVlIHRocmVzaG9sZCBuZWVkcyB0byBiZSBleGNsdWRlZCB3aGlsZSBlc3RpbWF0aW5nIHRoZSBwYXJhbWV0cmljIHRheCBmdW5jdGlvbi4NCg0KIyBGdW5jdGlvbmFsIGZvcm0gYW5kIGVzdGltYXRpb24gbWV0aG9kDQoNCkxldCAkeCQgYmUgcG9zdC10YXggaW5jb21lIGFuZCAkeSQgYmUgc29tZSBub3Rpb24gb2YgcHJlLXRheCBpbmNvbWUgKHdlIHNoYWxsIGV4cGxvcmUgZGlmZmVyZW50IGluY29tZSBjb25jZXB0cykuIFRoZSBwYXJhbWV0cmljIGZ1bmN0aW9uIG1hcHMgZnJvbSAkeSQgdG8gJHgkIGFzDQoNCiQkDQp4PVxsYW1iZGEgeV57MS1cdGF1fQ0KJCQNCg0KSW5jb21lIHRheCBpcyBnaXZlbiBieQ0KDQokJA0KVD15LVxsYW1iZGEgeV57MS1cdGF1fQ0KJCQNCg0KVGhlIGF2ZXJhZ2UgdGF4IHJhdGUgaXMNCg0KJCQNCnQ9MS1cbGFtYmRhIHleey1cdGF1fQ0KJCQNCg0KV2UgZXN0aW1hdGUgJFx0YXUkIGFuZCAkXGxhbWJkYSQgYnkgdHdvIG1ldGhvZHMgLSBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzIChPTFMpIGFuZCBub24tbGluZWFyIGxlYXN0IHNxdWFyZXMgKE5MUykuDQoNCmBgYHtyIFJlZ3Jlc3Npb25fbW9kZWxzfQ0KIyAxLjAgT0xTIChsbSkNCmZfdGF4X2xtIDwtIA0KICBmdW5jdGlvbihYKXsNCiAgICAjIFRha2UgdGhlIG5hdHVyYWwgbG9nIG9mIHRoZSB0YXggZnVuY3Rpb24NCiAgICBYIDwtIA0KICAgICAgWCAlPiUgDQogICAgICBmaWx0ZXIoeD4wJnk+MCkgJT4lIA0KICAgICAgbXV0YXRlKGxvZ194ID0gbG9nKHgpLCBsb2dfeT1sb2coeSkpDQogICAgDQogICAgIyBFc3RpbWF0ZSB0aGUgbW9kZWwNCiAgICBsbV9maXQgPC0gDQogICAgICBsaW5lYXJfcmVnKCkgJT4lIA0KICAgICAgc2V0X2VuZ2luZSgnbG0nKSAlPiUgDQogICAgICBzZXRfbW9kZSgncmVncmVzc2lvbicpICU+JSANCiAgICAgIGZpdChsb2dfeH5sb2dfeSxkYXRhPVgpDQogICAgDQogICAgIyBDb2xsZWN0IHRoZSByZXN1bHRzDQogICAgTWV0cmljcyA8LSANCiAgICAgICAgZ2xhbmNlKGxtX2ZpdCkgJT4lIA0KICAgICAgICBzZWxlY3QoYWRqLnIuc3F1YXJlZCxzaWdtYSxBSUMsQklDLG5vYnMpICU+JSANCiAgICAgICAgcmVuYW1lKFJNU0U9c2lnbWEsUnNxdWFyZWQ9YWRqLnIuc3F1YXJlZCkNCiAgDQogICAgQ29lZnMgPC0gDQogICAgICB0aWR5KGxtX2ZpdCkgJT4lDQogICAgICBtdXRhdGUoDQogICAgICAgIHRlcm0gPSBpZl9lbHNlKHRlcm09PSIoSW50ZXJjZXB0KSIsImxhbWJkYSIsInRhdSIpLA0KICAgICAgICB2YWx1ZSA9IGlmX2Vsc2UodGVybT09ImxhbWJkYSIsZXhwKGVzdGltYXRlKSwxLWVzdGltYXRlKSwNCiAgICAgICAgY2kxID0gdmFsdWUgLSAxLjk2KnN0ZC5lcnJvciwNCiAgICAgICAgY2kyID0gdmFsdWUgKyAxLjk2KnN0ZC5lcnJvcg0KICAgICAgKSAlPiUgDQogICAgICBzZWxlY3QodGVybSx2YWx1ZSxjaTEsY2kyKSAlPiUgDQogICAgICBiaW5kX2NvbHMoLixNZXRyaWNzKSAlPiUgDQogICAgICBtdXRhdGUoeWVhcj1YJHllYXJbMV0pICU+JSANCiAgICAgIHJlbG9jYXRlKHllYXIpICAgICAgICANCiAgfQ0KDQojIDIuMCBOTFMNCmZfdGF4X25scyA8LSANCiAgZnVuY3Rpb24oWCl7DQogICAgbmxzZml0IDwtIA0KICAgICAgbmxzKGF0cn4xLWxhbWJkYSp5XigtdGF1KSxYLCBzdGFydCA9IGxpc3QobGFtYmRhPTEsdGF1PTAuMSkpDQogICAgDQogICAgTWV0cmljcyA8LSANCiAgICAgIGdsYW5jZShubHNmaXQpICU+JSANCiAgICAgIHNlbGVjdChzaWdtYSxBSUMsQklDLG5vYnMpICU+JSANCiAgICAgIHJlbmFtZShSTVNFPXNpZ21hKQ0KICAgIA0KICAgIENvZWZzIDwtIA0KICAgICAgdGlkeShubHNmaXQpICU+JQ0KICAgICAgbXV0YXRlKA0KICAgICAgICBjaTEgPSBlc3RpbWF0ZSAtIDEuOTYqc3RkLmVycm9yLA0KICAgICAgICBjaTIgPSBlc3RpbWF0ZSArIDEuOTYqc3RkLmVycm9yDQogICAgICApICU+JSANCiAgICAgIHNlbGVjdCh0ZXJtLGVzdGltYXRlLGNpMSxjaTIpICU+JQ0KICAgICAgYmluZF9jb2xzKC4sTWV0cmljcykgJT4lIA0KICAgICAgbXV0YXRlKHllYXI9WCR5ZWFyWzFdKSAlPiUgDQogICAgICByZWxvY2F0ZSh5ZWFyKSAlPiUgDQogICAgICByZW5hbWUodmFsdWU9ZXN0aW1hdGUpDQogIH0NCmBgYA0KDQojIEluY29tZSBhbmQgdGF4IGNvbmNlcHRzDQoNCldlIHRha2UgdGF4YWJsZSBpbmNvbWUgImljX3RheGFibGVfaW5jb21lX2xvc3MiIGFzIHByZS10YXggaW5jb21lLCBuZXQgdGF4IGFmdGVyIG9mZnNldHMgYW5kIGNyZWRpdHMgInRjX25ldF90YXgiIGFzIG91ciB0YXggdmFyaWFibGUuIEJvdGggaW5jb21lIGFuZCB0YXggYXJlIGFkanVzdGVkIGZvciBpbmZsYXRpb24gdXNpbmcgdGhlIENQSSBhbmQgZXhwcmVzc2VkIGluIDIwMTkgZG9sbGFycy4NCg0KYGBge3IgSW5jb21lX3RheF92YXJzfQ0KdmFyeiA8LSBjKCJpY190YXhhYmxlX2luY29tZV9sb3NzIiwidGNfbmV0X3RheCIpDQpgYGANCg0KYGBge3IgQ29udmVydF9ub21pbmFsX3RvX3JlYWx9DQoNCkNQSSA8LSANCiAgcmVhZF9jc3YoaGVyZShkYXRhX2RpciwiQ1BJLmNzdiIpLHNob3dfY29sX3R5cGVzID0gRikNCg0KZl9ub21pbmFsX3RvX3JlYWwgPC0gDQogIGZ1bmN0aW9uKFgsdmFyel90b19yZWFsLENQSSxpKXsNCiAgICBYICU+JSANCiAgICAgIG11dGF0ZShjcGk9Q1BJJGNwaVtpXSwNCiAgICAgICAgIGFjcm9zcygNCiAgICAgICAgICAgYWxsX29mKHZhcnpfdG9fcmVhbCksDQogICAgICAgICAgIH4oLi9jcGkpDQogICAgICAgICApKQ0KICB9DQpgYGANCg0KIyBJbmNvbWUgdGF4IGNvZGUgKDE5OTEtMjAxOSkgey50YWJzZXR9DQoNClByaW9yIHRvIGVzdGltYXRpbmcgdGhlIHRheCBmdW5jdGlvbiBvbiB0aGUgZGF0YSwgd2UgYmVnaW4gYnkgZXhhbWluaW5nIHRoZSBwcm9ncmVzc2l2aXR5IG9mIHRoZSBpbmNvbWUgdGF4IGNvZGUgYmV0d2VlbiAxOTkxLTIwMTkuIFRoZSBBdXN0cmFsaWFuIGluY29tZSB0YXggY29kZSBpcyBjb21wbGV4IHdpdGggbXVsdGlwbGUgdGhyZXNob2xkcywgbWFyZ2luYWwgdGF4IHJhdGVzIGFuZCB2YXJpb3VzIHRheCBjcmVkaXRzLCBhbGxvd2FuY2VzLCBkZWR1Y3Rpb25zIGFuZCBvZmZzZXRzIHRoYXQgaGF2ZSBjaGFuZ2VkIGNvbnNpZGVyYWJseSBvdmVyIHRoZSBwYXN0IHR3byBkZWNhZGVzLg0KDQpUcmFja2luZyBhbGwgdGhlc2UgY2hhbmdlcyB0byB0aGUgc3RhdHV0b3J5IHRheCBjb2RlIGlzIGEgY2hhbGxlbmdpbmcgdGFzay4gVGh1cywgb3VyIGFwcHJvYWNoIGlzIHRvIGZvY3VzIG9uIHRoZSBjaGFuZ2VzIHRvIHRoZSBzdGFuZGFyZCBtYXJnaW5hbCB0YXggcmF0ZXMgYW5kIHRocmVzaG9sZHMgZnJvbSBhdmFpbGFibGUgcHVibGljIGluZm9ybWF0aW9uLiBXZSBzaGFsbCBjYXB0dXJlIGRldGFpbGVkIGNoYW5nZXMgaW4gbGF0ZXIgc2VjdGlvbnMgd2hlbiB3ZSBlc3RpbWF0ZSB0aGUgdGF4IGZ1bmN0aW9uIG9uIHRoZSBkYXRhLg0KDQpgYGB7ciBMb2FkX3RheF9jb2RlfQ0KVGF4X2NvZGVfZGF0YSA8LSANCiAgcmVhZF9jc3YoaGVyZSgiUmF3X2RhdGEiLCJUYXhfcmF0ZXMuY3N2IiksDQogICAgICAgICAgICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UpDQpgYGANCg0KYGBge3IgQ29udmVydF90YXhfY29kZV90b19yZWFsX3Rlcm1zfQ0KVGF4X2NvZGVfZGF0YV9yZWFsIDwtIA0KICBmdWxsX2pvaW4oVGF4X2NvZGVfZGF0YSxDUEksYnk9InllYXIiKSAlPiUgDQogIG11dGF0ZSgNCiAgICBhY3Jvc3MoDQogICAgICBjKHkxLHkyLHRheCksDQogICAgICB+KC4vY3BpKQ0KICAgICkNCiAgKQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgZnVuY3Rpb24gZXN0aW1hdGVzIHRheCBsaWFiaWxpdHkgdXNpbmcgdGhlIHRheCBjb2RlIGRhdGEuDQoNCmBgYHtyIGZfZXN0aW1hdGVfdGF4fQ0KZl9lc3RpbWF0ZV90YXggPC0gDQogIGZ1bmN0aW9uKFgsVEQpew0KIyBGaXJzdCBnZXR0aW5nIHRoZSBkYXRhIGludG8gYW4gZWFzaWVyIGZvcm1hdA0KIyMgVEQgLSB0YXggY29kZSBkYXRhIChjYW4gYmUgZWl0aGVyIG5vbWluYWwgb3IgcmVhbCkNCiMjIFggLSBkZiB3aXRoIHJhdyBkYXRhICh5ZWFyLGluYyx0YXgpDQoNCnR5IDwtIA0KICBURCAlPiUgDQogIHNlbGVjdCh5ZWFyLHkxKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcikgJT4lIA0KICBtdXRhdGUoeT1wYXN0ZTAoInkiLHJvd19udW1iZXIoKSkpICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJ5IiwgdmFsdWVzX2Zyb20gPSAieTEiKQ0KDQp0ciA8LSANCiAgVEQgJT4lIA0KICBzZWxlY3QoeWVhcixtdHIpICU+JQ0KICBncm91cF9ieSh5ZWFyKSAlPiUgDQogIG11dGF0ZShtdHI9bXRyLzEwMCwNCiAgICAgICAgIHk9cGFzdGUwKCJtdHIiLHJvd19udW1iZXIoKSkpICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJ5IiwgdmFsdWVzX2Zyb20gPSAibXRyIikNCg0KdHggPC0gDQogIFREICU+JSANCiAgc2VsZWN0KHllYXIsdGF4KSAlPiUNCiAgZ3JvdXBfYnkoeWVhcikgJT4lIA0KICBtdXRhdGUoDQogICAgeT1wYXN0ZTAoInR4Iixyb3dfbnVtYmVyKCkpKSAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAieSIsIHZhbHVlc19mcm9tID0gInRheCIpDQoNClRheF9jb2RlIDwtIA0KICBsZWZ0X2pvaW4odHksdHIsYnk9InllYXIiKSAlPiUgDQogIGxlZnRfam9pbiguLHR4LGJ5PSJ5ZWFyIikNCg0KIyBUaGUgZm9sbG93aW5nIGVzdGltYXRlcyB0YXggbGlhYmlsaXR5IGFuZCBzZWxlY3RzIHRoZSByaWdodCBNVFINCkEgPC0gDQogICAgICBsZWZ0X2pvaW4oWCxUYXhfY29kZSxieT0ieWVhciIpICU+JSANCiAgICAgIGFycmFuZ2UoeSkgJT4lIA0KICAgICAgbXV0YXRlKA0KICAgICAgICB0YXggPSAwLA0KICAgICAgICB0YXggPSBpZl9lbHNlKHk+PXkxICYgeTx5MiAsIHR4MSArICh5LXkxKzEpKm10cjEsIHRheCksDQogICAgICAgIHRheCA9IGlmX2Vsc2UoeT49eTIgJiB5PHkzICwgdHgyICsgKHkteTIrMSkqbXRyMiwgdGF4KSwNCiAgICAgICAgdGF4ID0gaWZfZWxzZSh5Pj15MyAmIHk8eTQgLCB0eDMgKyAoeS15MysxKSptdHIzLCB0YXgpLA0KICAgICAgICB0YXggPSBpZl9lbHNlKHk+PXk0ICYgeTx5NSAsIHR4NCArICh5LXk0KzEpKm10cjQsIHRheCksDQogICAgICAgIHRheCA9IGlmX2Vsc2UoeT49eTUgJiB5ZWFyIT0xOTkxICYgeWVhciE9MTk5NCwNCiAgICAgICAgICAgICAgICAgICAgICB0eDUgKyAoeS15NSsxKSptdHI1LCB0YXgNCiAgICAgICAgICAgICAgICAgICAgICApLA0KICAgICAgICB0YXggPSBpZl9lbHNlKA0KICAgICAgICAgIHllYXI9PTE5OTEsDQogICAgICAgICAgY2FzZV93aGVuKA0KICAgICAgICAgICAgeT49eTEgJiB5PHkyIH4gdHgxICsgKHkteTErMSkqbXRyMSwNCiAgICAgICAgICAgIHk+PXkyICYgeTx5MyB+IHR4MiArICh5LXkyKzEpKm10cjIsDQogICAgICAgICAgICB5Pj15MyAmIHk8eTQgfiB0eDMgKyAoeS15MysxKSptdHIzLA0KICAgICAgICAgICAgeT49eTQgJiB5PHk1IH4gdHg0ICsgKHkteTQrMSkqbXRyNCwNCiAgICAgICAgICAgIHk+PXk1ICYgeTx5NiB+IHR4NSArICh5LXk1KzEpKm10cjUsDQogICAgICAgICAgICB5Pj15NiAmIHk8eTcgfiB0eDYgKyAoeS15NisxKSptdHI2LA0KICAgICAgICAgICAgeT49eTcgJiB5PHk4IH4gdHg3ICsgKHkteTcrMSkqbXRyNywNCiAgICAgICAgICAgIHk+PXk4ICAgICAgICB+IHR4NCArICh5LXk4KzEpKm10cjgsDQogICAgICAgICAgKSwNCiAgICAgICAgICB0YXgNCiAgICAgICAgKSwNCiAgICAgICAgdGF4ID0gaWZfZWxzZSgNCiAgICAgICAgICB5ZWFyPT0xOTk0LA0KICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgIHk+PXkxICYgeTx5MiB+IHR4MSArICh5LXkxKzEpKm10cjEsDQogICAgICAgICAgICB5Pj15MiAmIHk8eTMgfiB0eDIgKyAoeS15MisxKSptdHIyLA0KICAgICAgICAgICAgeT49eTMgJiB5PHk0IH4gdHgzICsgKHkteTMrMSkqbXRyMywNCiAgICAgICAgICAgIHk+PXk0ICYgeTx5NSB+IHR4NCArICh5LXk0KzEpKm10cjQsDQogICAgICAgICAgICB5Pj15NSAmIHk8eTYgfiB0eDUgKyAoeS15NSsxKSptdHI1LA0KICAgICAgICAgICAgeT49eTYgfiB0eDYgKyAoeS15NisxKSptdHI2LA0KICAgICAgICAgICksDQogICAgICAgICAgdGF4DQogICAgICAgICksDQogICAgICAgIG10cj0wLA0KICAgICAgICBtdHIgPSANCiAgICAgICAgICBpZl9lbHNlKA0KICAgICAgICAgICAgeWVhcj09MTk5MSwNCiAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgeT49eTEgJiB5PHkyIH4gbXRyMSwNCiAgICAgICAgICAgICAgeT49eTIgJiB5PHkzIH4gbXRyMiwNCiAgICAgICAgICAgICAgeT49eTMgJiB5PHk0IH4gbXRyMywNCiAgICAgICAgICAgICAgeT49eTQgJiB5PHk1IH4gbXRyNCwNCiAgICAgICAgICAgICAgeT49eTUgJiB5PHk2IH4gbXRyNSwNCiAgICAgICAgICAgICAgeT49eTYgJiB5PHk3IH4gbXRyNiwNCiAgICAgICAgICAgICAgeT49eTcgJiB5PHk4IH4gbXRyNywNCiAgICAgICAgICAgICAgeT49eTggICAgICAgIH4gbXRyOCwNCiAgICAgICAgICAgICksDQogICAgICAgICAgICBtdHIpLA0KICAgICAgICBtdHIgPQ0KICAgICAgICAgICAgaWZfZWxzZSgNCiAgICAgICAgICAgICAgeWVhcj09MTk5NCwNCiAgICAgICAgICAgICAgY2FzZV93aGVuKA0KICAgICAgICAgICAgICAgIHk+PXkxICYgeTx5MiB+IG10cjEsDQogICAgICAgICAgICAgICAgeT49eTIgJiB5PHkzIH4gbXRyMiwNCiAgICAgICAgICAgICAgICB5Pj15MyAmIHk8eTQgfiBtdHIzLA0KICAgICAgICAgICAgICAgIHk+PXk0ICYgeTx5NSB+IG10cjQsDQogICAgICAgICAgICAgICAgeT49eTUgJiB5PHk2IH4gbXRyNSwNCiAgICAgICAgICAgICAgICB5Pj15NiB+IG10cjYsDQogICAgICAgICAgICAgICksDQogICAgICAgICAgICAgIG10ciksDQogICAgICAgIG10ciA9DQogICAgICAgICAgaWZfZWxzZSgNCiAgICAgICAgICAgIHllYXIhPTE5OTEmeWVhciE9MTk5NCwNCiAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgeT49eTEgJiB5PHkyIH4gbXRyMSwNCiAgICAgICAgICAgICAgeT49eTIgJiB5PHkzIH4gbXRyMiwNCiAgICAgICAgICAgICAgeT49eTMgJiB5PHk0IH4gbXRyMywNCiAgICAgICAgICAgICAgeT49eTQgJiB5PHk1IH4gbXRyNCwNCiAgICAgICAgICAgICAgeT49eTUgfiBtdHI1DQogICAgICAgICAgICAgICksDQogICAgICAgICAgICBtdHINCiAgICAgICAgICAgICkNCiAgICAgICAgICApICU+JSANCiAgICAgIHJlbmFtZSh0YXhmcmVlPXkyKSAlPiUgDQogICAgICBzZWxlY3QocGlkLHllYXIseSx0YXgsbXRyLHRheGZyZWUpDQp9DQpgYGANCg0KIyMgVGF4IGNvZGUgdGFibGUgKG5vbWluYWwpDQoNCmBgYHtyIE1ha2VfdGF4X2NvZGVfdGFibGV9DQpaIDwtIA0KICBUYXhfY29kZV9kYXRhICU+JSANCiAgbXV0YXRlKA0KICAgIGFjcm9zcygNCiAgICAgIGMoeTEseTIsdGF4KSwNCiAgICAgIH5mb3JtYXQoLixiaWcubWFyaz0iLCIpDQogICAgKSwNCiAgICBtdHIgPSByb3VuZChtdHIsZGlnaXRzID0gMikNCiAgKSAlPiUgDQogIHJlbmFtZSgNCiAgICBZZWFyPXllYXIsDQogICAgRnJvbT15MSwNCiAgICBUbz15MiwNCiAgICBgVGF4IGFtb3VudGA9dGF4LA0KICAgIGBNYXJnaW5hbCB0YXggcmF0ZSAoJSlgPW10cg0KICApIA0KDQpodG1sdG9vbHM6OnRhZ0xpc3QoDQogIHJlYWN0YWJsZSgNCiAgICBaLA0KICAgIGRlZmF1bHRDb2xEZWYgPSANCiAgICAgIGNvbERlZigNCiAgICAgICAgYWxpZ24gPSAiY2VudGVyIg0KICAgICAgKSwNCiAgICBib3JkZXJlZCA9IFQsDQogICAgaGlnaGxpZ2h0ID0gVCwNCiAgICBmaWx0ZXJhYmxlID0gVA0KICAgIA0KICApDQopDQpgYGANCg0KIyMgVGF4IGNvZGUgdGFibGUgKHJlYWwgMjAxOVwkKQ0KDQpgYGB7ciBNYWtlX3RheF9jb2RlX3RhYmxlX3JlYWx9DQpaIDwtIA0KICBUYXhfY29kZV9kYXRhX3JlYWwgJT4lIA0KICBtdXRhdGUoDQogICAgYWNyb3NzKA0KICAgICAgYyh5MSx5Mix0YXgsbXRyKSwNCiAgICAgIH5yb3VuZCguLGRpZ2l0cyA9IDIpDQogICAgKSwNCiAgICBhY3Jvc3MoDQogICAgICBjKHkxLHkyLHRheCksDQogICAgICB+Zm9ybWF0KC4sYmlnLm1hcms9IiwiKQ0KICAgICksDQogICkgJT4lIA0KICByZW5hbWUoDQogICAgWWVhcj15ZWFyLA0KICAgIEZyb209eTEsDQogICAgVG89eTIsDQogICAgYFRheCBhbW91bnRgPXRheCwNCiAgICBgTWFyZ2luYWwgdGF4IHJhdGUgKCUpYD1tdHINCiAgKSAlPiUgDQogIHNlbGVjdCgtY3BpKQ0KDQpodG1sdG9vbHM6OnRhZ0xpc3QoDQogIHJlYWN0YWJsZSgNCiAgICBaLA0KICAgIGRlZmF1bHRDb2xEZWYgPSANCiAgICAgIGNvbERlZigNCiAgICAgICAgYWxpZ24gPSAiY2VudGVyIg0KICAgICAgKSwNCiAgICBib3JkZXJlZCA9IFQsDQogICAgaGlnaGxpZ2h0ID0gVCwNCiAgICBmaWx0ZXJhYmxlID0gVA0KICAgIA0KICApDQopDQpgYGANCg0KIyMgQXZlcmFnZSB0YXggcmF0ZXMNCg0KV2UgZXN0aW1hdGUgc3RhdHV0b3J5IHRheCBhbmQgdGhlIGF2ZXJhZ2UgdGF4IHJhdGUgZm9yIGVhY2ggeWVhci4NCg0KYGBge3IgVGF4X2Z1bmNfb25fdGF4X2NvZGV9DQpmb3IgKGkgaW4gc2VxX2Fsb25nKHNvdXJjZXllYXJzKSkgew0KICBaIDwtIA0KICAgIHNlcSgwLDIwMDAwMCxieT0xMDApICU+JQ0KICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICByZW5hbWUoeT12YWx1ZSkgJT4lIA0KICAgIG11dGF0ZShwaWQ9MSwgIyBBcnRlZmFjdCBpbiBmdW5jdGlvbg0KICAgICAgICAgICB5ZWFyPXNvdXJjZXllYXJzW2ldKSAlPiUgDQogICAgZl9lc3RpbWF0ZV90YXgoLixUYXhfY29kZV9kYXRhX3JlYWwpICU+JSANCiAgICBtdXRhdGUoYXRyPWlmX2Vsc2UoeT09MCwwLHRheC95KSkNCiAgDQogIGlmIChpPT0xKXsNCiAgICBYPVoNCiAgfQ0KICBlbHNlew0KICAgIFg9YmluZF9yb3dzKFgsWikNCiAgfQ0KfQ0KYGBgDQoNCmBgYHtyIGZpZ19BVFJfMTk5NS0yMDE5LnBuZ30NClggJT4lIA0KICBmaWx0ZXIoeWVhcj09MTk5NXx5ZWFyPT0yMDAwfHllYXI9PTIwMDV8eWVhcj09MjAxMHx5ZWFyPT0yMDE5KSAlPiUgDQogIG11dGF0ZShZZWFyPWFzLmZhY3Rvcih5ZWFyKSwNCiAgICAgICAgIGF0cj1hdHIqMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeD15LHk9YXRyLGNvbG9yPVllYXIsc2hhcGU9WWVhcikpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBsYWJzKHg9IlJlYWwgSW5jb21lICgyMDE5JCkiLCB5PSJBdmVyYWdlIHRheCByYXRlICglKSIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVsPWNvbW1hLCBicmVha3M9c2VxKDAsMjAwMDAwLDIwMDAwKSkNCg0KZ2dzYXZlKA0KICBoZXJlKHJlc3VsdHMsIlRQXzAxX0FUUl9TdGF0dXRvcnkucGRmIikNCikNCiAgDQpgYGANCg0KIyMgUGFyYW1ldHJpYyB0YXggZnVuY3Rpb24NCg0KV2UgYmVnaW4gYnkgZXhwbG9yaW5nIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gc3RhdHV0b3J5IGF2ZXJhZ2UgdGF4IHJhdGVzIGNhY3VsYXRlZCB1c2luZyB0aGUgdGF4IGNvZGUgYW5kIHRob3NlIGVzdGltYXRlZCB1c2luZyB0aGUgcGFyYW1ldHJpYyB0YXggZnVuY3Rpb24uDQoNCkVzdGltYXRpbmcgdGhlIHRheCBmdW5jdGlvbiBmb3IgdGhlIGVudGlyZSByYW5nZSBvZiBpbmNvbWUgYmV0d2VlbiAwIGFuZCAyMDAsMDAwIGluY2x1ZGluZyBpbmNvbWVzIGJlbG93IHRoZSB0YXgtZnJlZSB0aHJlc2hvbGQgcmVzdWx0cyBvdmVyLWVzdGltYXRpbmcgYXZlcmFnZSB0YXggcmF0ZXMgYXQgdGhlIGJvdHRvbSBhbmQgdW5kZXItZXN0aW1hdGluZyBhdmVyYWdlIHRheCByYXRlcyBhdCB0aGUgdG9wLg0KDQpgYGB7ciBFc3RpbWF0ZV8yMDE5XzF9DQpaIDwtIA0KICAgIHNlcSgwLDIwMDAwMCxieT0xMDAwKSAlPiUNCiAgICBhc190aWJibGUoKSAlPiUgDQogICAgcmVuYW1lKHk9dmFsdWUpICU+JSANCiAgICBtdXRhdGUocGlkPTEsICMgQXJ0ZWZhY3QgaW4gZnVuY3Rpb24NCiAgICAgICAgICAgeWVhcj0yMDE5KSAlPiUgDQogICAgZl9lc3RpbWF0ZV90YXgoLixUYXhfY29kZV9kYXRhX3JlYWwpICU+JSANCiAgICBtdXRhdGUoYXRyPWlmX2Vsc2UoeT09MCwwLHRheC95KSwNCiAgICAgICAgICAgeD15LXRheCkNClggPC0gDQogIGZfdGF4X2xtKFopDQoNClogPC0gDQogIFogJT4lIA0KICBtdXRhdGUoDQogICAgT0xTID0gKDEtWCR2YWx1ZVsxXSp5XigtKFgkdmFsdWVbMl0pKSkqMTAwDQogICkgJT4lIA0KICBtdXRhdGUoYXRyPWF0cioxMDApDQoNClogJT4lIA0KICBnZ3Bsb3QoYWVzKHg9eSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5PWF0cixjb2xvcj0iRGF0YSIpLHNob3cubGVnZW5kID0gVCkgKw0KICBnZW9tX2xpbmUoYWVzKHk9T0xTLGNvbG9yPSJFc3RpbWF0ZWQiKSwgc2hvdy5sZWdlbmQgPSBUKSArDQogIGxhYnMoeD0iSW5jb21lIix5PSJBdmVyYWdlIHRheCByYXRlIiwNCiAgICAgICB0aXRsZT0iSW5jbHVkaW5nIGluY29tZXMgYmVsb3cgdGF4LWZyZWUgdGhyZXNob2xkIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwoDQogICAgbmFtZSA9ICJBVFIiLA0KICAgIGd1aWRlID0gJ2xlZ2VuZCcsDQogICAgdmFsdWVzID0gYygiRGF0YSI9ImJsdWUiLCJFc3RpbWF0ZWQiPSJyZWQiKQ0KICApICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVsPWNvbW1hLCBicmVha3M9c2VxKDAsMjAwMDAwLDIwMDAwKSkNCg0KZ2dzYXZlKA0KICBoZXJlKHJlc3VsdHMsIlRQXzAxX0FUUl9UYXhGdW5jX0luY2x1ZGluZ19iZWxvd190YXhfZnJlZS5wZGYiKQ0KKQ0KYGBgDQoNCkV4Y2x1ZGluZyBpbmNvbWVzIGJlbG93IHRoZSB0YXgtZnJlZSB0aHJlc2hvbGQgKGJ5IHJlc3RyaWN0aW5nIHRoZSBhdmVyYWdlIHRheCByYXRlIHRvIHN0cmljdGx5IHBvc2l0aXZlIHZhbHVlcykgc2lnbmlmaWNhbnRseSBpbXByb3ZlcyB0aGUgZml0IG9mIHRoZSBlc3RpbWF0ZWQgdGF4IGZ1bmN0aW9uLg0KDQpgYGB7ciBFc3RpbWF0ZV8yMDE5XzJ9DQpaIDwtIA0KICAgIHNlcSgwLDIwMDAwMCxieT0xMDAwKSAlPiUNCiAgICBhc190aWJibGUoKSAlPiUgDQogICAgcmVuYW1lKHk9dmFsdWUpICU+JSANCiAgICBtdXRhdGUocGlkPTEsICMgQXJ0ZWZhY3QgaW4gZnVuY3Rpb24NCiAgICAgICAgICAgeWVhcj0yMDE5KSAlPiUgDQogICAgZl9lc3RpbWF0ZV90YXgoLixUYXhfY29kZV9kYXRhX3JlYWwpICU+JSANCiAgICBtdXRhdGUoYXRyPWlmX2Vsc2UoeT09MCwwLHRheC95KSwNCiAgICAgICAgICAgeD15LXRheCkgJT4lIA0KICAgIGZpbHRlcihhdHI+MCkNCg0KWCA8LSANCiAgZl90YXhfbG0oWikNCg0KWiA8LSANCiAgWiAlPiUgDQogIG11dGF0ZSgNCiAgICBPTFMgPSAoMS1YJHZhbHVlWzFdKnleKC0oWCR2YWx1ZVsyXSkpKSoxMDANCiAgKSAlPiUgDQogIG11dGF0ZShhdHI9YXRyKjEwMCkNCg0KWiAlPiUgDQogIGdncGxvdChhZXMoeD15KSkgKw0KICBnZW9tX2xpbmUoYWVzKHk9YXRyLGNvbG9yPSJEYXRhIiksc2hvdy5sZWdlbmQgPSBUKSArDQogIGdlb21fbGluZShhZXMoeT1PTFMsY29sb3I9IkVzdGltYXRlZCIpLCBzaG93LmxlZ2VuZCA9IFQpICsNCiAgbGFicyh4PSJJbmNvbWUiLHk9IkF2ZXJhZ2UgdGF4IHJhdGUiLA0KICAgICAgIHRpdGxlPSJFeGNsdWRpbmcgaW5jb21lcyBiZWxvdyB0YXgtZnJlZSB0aHJlc2hvbGQiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICBuYW1lID0gIkFUUiIsDQogICAgZ3VpZGUgPSAnbGVnZW5kJywNCiAgICB2YWx1ZXMgPSBjKCJEYXRhIj0iYmx1ZSIsIkVzdGltYXRlZCI9InJlZCIpDQogICkgDQoNCmdnc2F2ZSgNCiAgaGVyZShyZXN1bHRzLCJUUF8wMV9BVFJfVGF4RnVuY19FeGNsdWRpbmdfYmVsb3dfdGF4X2ZyZWUucGRmIikNCikNCmBgYA0KDQojIyBUcmVuZHMgaW4gcHJvZ3Jlc3Npdml0eSBvZiB0aGUgc3RhbmRhcmQgaW5jb21lIHRheCBjb2RlDQoNCldlIGVzdGltYXRlIHRoZSBwcm9ncmVzc2l2aXR5IHBhcmFtZXRlciAkXHRhdSQgb24gdGhlIHN0YW5kYXJkIGluY29tZSB0YXggY29kZSBmcm9tIDE5OTEgdG8gMjAxOS4NCg0KYGBge3IgRXN0aW1hdGVfMTk5MV8yMDE5LCBpbmNsdWRlPVRSVUV9DQpmb3IgKGkgaW4gc2VxX2Fsb25nKHNvdXJjZXllYXJzKSkgew0KWiA8LSANCiAgICBzZXEoMCwyMDAwMDAsYnk9MTAwMCkgJT4lDQogICAgYXNfdGliYmxlKCkgJT4lIA0KICAgIHJlbmFtZSh5PXZhbHVlKSAlPiUgDQogICAgbXV0YXRlKHBpZD0xLCAjIEFydGVmYWN0IGluIGZ1bmN0aW9uDQogICAgICAgICAgIHllYXI9c291cmNleWVhcnNbaV0pICU+JSANCiAgICBmX2VzdGltYXRlX3RheCguLFRheF9jb2RlX2RhdGFfcmVhbCkgJT4lIA0KICAgIG11dGF0ZShhdHI9aWZfZWxzZSh5PT0wLDAsdGF4L3kpLA0KICAgICAgICAgICB4PXktdGF4KSAlPiUgDQogICAgZmlsdGVyKGF0cj4wKSAlPiUgDQogICAgZl90YXhfbG0oLikNCg0KICBpZiAoaT09MSl7DQogICAgWD1aDQogIH0NCiAgZWxzZXsNCiAgICBYPWJpbmRfcm93cyhYLFopDQogIH0NCn0NCg0Kc2F2ZVJEUyhYLGhlcmUocmVzdWx0cywiVFBfMDFfVGF1X1RheF9jb2RlLnJkcyIpKQ0KYGBgDQoNCmBgYHtyIGZpZ19PelRheF8wM19UYXVfVGF4X2NvZGUucG5nfQ0KWCA8LSByZWFkUkRTKChoZXJlKHJlc3VsdHMsIlRQXzAxX1RhdV9UYXhfY29kZS5yZHMiKSkpDQpYICU+JSANCiAgZmlsdGVyKHRlcm09PSJ0YXUiKSAlPiUgDQogIGdncGxvdChhZXMoeD15ZWFyLHk9dmFsdWUpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicygNCiAgICB4ID0gIlllYXIiLCB5ID0gZXhwcmVzc2lvbihwYXN0ZSgiUHJvZ3Jlc3Npdml0eSAiLHRhdSkpDQogICkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5OTAsMjAyMCxieT0yKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAuMDUsMC4yLDAuMDEpKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odD0xMCkpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odD0xMCkpDQogICkNCg0KZ2dzYXZlKA0KICBoZXJlKHJlc3VsdHMsIlRQXzAxX0FUUl9UYXhGdW5jX1RyZW5kc19pbl90YXUucGRmIikNCikNCmBgYA0KDQojIEFMaWZlIGRhdGEgMTk5MS0yMDE5DQoNCiMjIFNhbXBsZSByZXN0cmljdGlvbnMNCg0KV2UgaW1wb3NlIHRoZSBmb2xsb3dpbmcgc2FtcGxlIHJlc3RyaWN0aW9ucyBvbiB0aGUgZGF0YS4NCg0KMS4gICR5Xnt0YXhmcmVlfVxsZSB5XGxlMjAwLDAwMCQNCg0KICAgIEluY29tZSBjb25jZXB0ICR5JCBpcyByZXN0cmljdGVkIGJldHdlZW4gdGhlIHRheCBmcmVlIHRocmVzaG9sZCBmb3IgZWFjaCB5ZWFyIGFuZCBcJDIwMCwwMDAgKGluIHJlYWwgdGVybXMpLiBGb3IgdGhlIHllYXJzIGluIG91ciBzYW1wbGUsIHRoZSB0b3AgdGF4IGJyYWNrZXQgcmFuZ2VzIGJldHdlZW4gXCQ3MCwwMDAgYW5kIFwkMTkzLDAwMA0KDQoyLiAgJDA8IHQgXGxlMC40NyQNCg0KICAgIFRob3NlIHdpdGggYSBuZWdhdGl2ZSBhdmVyYWdlIHRheCByYXRlICR0JCBhbmQgdGhvc2Ugd2l0aCBhIHRheCByYXRlIG92ZXIgNDclIGFyZSBleGNsdWRlZCBmcm9tIHRoZSBzYW1wbGUuIFRoZSByYXRpb25hbGUgZm9yIHRoaXMgaXMgdGhhdCB0aGUgdG9wIGF2ZXJhZ2UgdGF4IHJhdGUgYmV0d2VlbiAxOTkxLTIwMTkgd2FzIGVpdGhlciA0NSUgb3IgNDclLiBBcyBzaG93biBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbiwgZXhjbHVkaW5nIHRob3NlIGJlbG93IHRoZSB0YXgtZnJlZSB0aHJlc2hvbGQgZW5zdXJlcyBhIGJldHRlciBmaXQgd2l0aCBhY3R1YWwgdGF4IHJhdGVzLg0KDQojIyBEZW1vbnN0cmF0aW9uIG9uIDIwMTkgZGF0YQ0KDQpgYGB7cn0NCkEgPC0gDQogIHJlYWRfZmVhdGhlcigNCiAgICBoZXJlKCJSYXdfZGF0YSIsIkxJXzAxX0Rlcml2ZWRfMjAxOS5mZWF0aGVyIiksDQogICAgYXNfZGF0YV9mcmFtZSA9IEYNCiAgKSAlPiUgDQogIG11dGF0ZSh5PWljX3RheGFibGVfaW5jb21lX2xvc3MpICU+JSANCiAgc2VsZWN0KHBpZCxhZ2UseWVhcix5LGluY190YXgpICU+JSANCiAgY29sbGVjdCgpDQoNClogPC0gDQogIGxlZnRfam9pbihBLENQSSxieT0ieWVhciIpICU+JSANCiAgbXV0YXRlKA0KICAgIGFjcm9zcygNCiAgICAgIGMoeSxpbmNfdGF4KSwNCiAgICAgIH4uL2NwaQ0KICAgICkNCiAgKSAlPiUgDQogIGZpbHRlcihpbmNfdGF4Pj0wJnk+PTApICU+JSANCiAgbXV0YXRlKA0KICAgIGF0ciA9IGlmX2Vsc2UoeT09MCwwLGluY190YXgveSksDQogICAgeCA9IHktaW5jX3RheCAjIFBvc3QtdGF4IGluY29tZSBuZWVkZWQgZm9yIE9MUyBlc3RpbWF0aW9uDQogICkgJT4lIA0KICBmaWx0ZXIoeT49MCZ5PD0yMDAwMDApICU+JSANCiAgZmlsdGVyKGF0cj4wJmF0cjw9MC40NykNCg0KQiA8LSANCiAgWiAlPiUgDQogIGZfZXN0aW1hdGVfdGF4KC4sVGF4X2NvZGVfZGF0YV9yZWFsKSAlPiUgDQogIG11dGF0ZSh0Y19hdHI9aWZfZWxzZSh5PT0wLDAsdGF4L3kpLCAjIHRjX2F0ciBpcyB0YXggY29kZSBhdHINCiAgICAgICAgIHRjX3RheD10YXgpDQoNClogPC0gDQogIGZ1bGxfam9pbihCLFosYnk9YygicGlkIiwieWVhciIsInkiKSkgJT4lIA0KICBmaWx0ZXIoeT49dGF4ZnJlZSZhdHI+MCkNCg0KT0xTIDwtIGZfdGF4X2xtKFopICU+JSBtdXRhdGUobWV0aG9kPSJPTFMiKQ0KTkxTIDwtIGZfdGF4X25scyhaKSAlPiUgbXV0YXRlKG1ldGhvZD0iTkxTIikNCg0KQ29lZnNfdGVtcCA8LSBiaW5kX3Jvd3MoT0xTLE5MUykgDQoNClEgPC0gDQogIFogJT4lIA0KICBtdXRhdGUoDQogICAgT0xTID0gMS1PTFMkdmFsdWVbMV0qeV4oLU9MUyR2YWx1ZVsyXSksDQogICAgTkxTID0gMS1OTFMkdmFsdWVbMV0qeV4oLU5MUyR2YWx1ZVsyXSkNCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KUVEgPC0gDQogIFEgJT4lIA0KICBzbGljZV9zYW1wbGUocHJvcD0wLjAxKSAlPiUgDQogIHNlbGVjdCh5LGF0cix0Y19hdHIsT0xTLE5MUykgJT4lIA0KICByZW5hbWUoSW5jb21lPXksQWN0dWFsPWF0cixTdGF0dXRvcnk9dGNfYXRyKQ0KDQpRUSAlPiUgDQogIGdncGxvdChhZXMoeD1JbmNvbWUpKSArDQogIGdlb21fcG9pbnQoYWVzKHk9QWN0dWFsKjEwMCxjb2xvcj0iRGF0YSIpLHNob3cubGVnZW5kID0gVCkgKw0KICBnZW9tX2xpbmUoYWVzKHk9U3RhdHV0b3J5KjEwMCxjb2xvcj0iU3RhdHV0b3J5IiksIHNob3cubGVnZW5kID0gVCkgKw0KICBnZW9tX3BvaW50KGFlcyh5PU9MUyoxMDAsY29sb3I9Ik9MUyIpLCBzaG93LmxlZ2VuZCA9IFQpICsNCiAgZ2VvbV9saW5lKGFlcyh5PU5MUyoxMDAsY29sb3I9Ik5MUyIpLCBzaG93LmxlZ2VuZCA9IFQpICsNCiAgbGFicyh4PSJJbmNvbWUiLHk9IkF2ZXJhZ2UgdGF4IHJhdGUgKCUpIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwoDQogICAgbmFtZSA9ICJBVFIiLA0KICAgIGd1aWRlID0gJ2xlZ2VuZCcsDQogICAgdmFsdWVzID0gYygiRGF0YSI9ImdyYXkiLA0KICAgICAgICAgICAgICAgIlN0YXR1dG9yeSI9ImJsYWNrIiwNCiAgICAgICAgICAgICAgICJPTFMiPSJyZWQiLA0KICAgICAgICAgICAgICAgIk5MUyI9ImdyZWVuIikNCiAgKSArDQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbD1jb21tYSkNCg0KZ2dzYXZlKA0KICBoZXJlKHJlc3VsdHMsIlRQXzAxX0FUUl9PTFNfTkxTX0FjdHVhbF8yMDE5LnBkZiIpDQopDQpgYGANCg0KIyMgVHJlbmQgaW4gcHJvZ3Jlc3Npdml0eQ0KDQpgYGB7ciBHZXRfdGF1X2xvb3AsIGV2YWw9RkFMU0UsIGluY2x1ZGU9VFJVRX0NCnNvdXJjZXllYXJzIDwtIGMoMTk5MToyMDE5KQ0KZm9yIChpIGluIHNlcV9hbG9uZyhzb3VyY2V5ZWFycykpIHsNCg0KICAgIEEgPC0gDQogICAgICByZWFkX2ZlYXRoZXIoaGVyZSgiUmF3X2RhdGEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiTElfMDFfRGVyaXZlZF8iLHNvdXJjZXllYXJzW2ldLCIuZmVhdGhlciIpKSwNCiAgICAgICAgICAgICAgICAgICBhc19kYXRhX2ZyYW1lID0gRikgJT4lDQogICAgICBtdXRhdGUoeSA9IGljX3RheGFibGVfaW5jb21lX2xvc3MpICU+JQ0KICAgICAgc2VsZWN0KHBpZCwgYWdlLCB5ZWFyLCB5LCBpbmNfdGF4KSAlPiUNCiAgICAgIGNvbGxlY3QoKQ0KDQpaIDwtIA0KICBsZWZ0X2pvaW4oQSxDUEksYnk9InllYXIiKSAlPiUgDQogIG11dGF0ZSgNCiAgICBhY3Jvc3MoDQogICAgICBjKHksaW5jX3RheCksDQogICAgICB+Li9jcGkNCiAgICApDQogICkgJT4lIA0KICBmaWx0ZXIoaW5jX3RheD49MCZ5Pj0wKSAlPiUgDQogIG11dGF0ZSgNCiAgICBhdHIgPSBpZl9lbHNlKHk9PTAsMCxpbmNfdGF4L3kpLA0KICAgIHggPSB5LWluY190YXggIyBQb3N0LXRheCBpbmNvbWUgbmVlZGVkIGZvciBPTFMgZXN0aW1hdGlvbg0KICApICU+JSANCiAgZmlsdGVyKHk+PTAmeTw9MjAwMDAwKSAlPiUgDQogIGZpbHRlcihhdHI+MCZhdHI8PTAuNDcpDQoNCkIgPC0gDQogIFogJT4lIA0KICBmX2VzdGltYXRlX3RheCguLFRheF9jb2RlX2RhdGFfcmVhbCkgJT4lIA0KICBtdXRhdGUodGNfYXRyPWlmX2Vsc2UoeT09MCwwLHRheC95KSwgIyB0Y19hdHIgaXMgdGF4IGNvZGUgYXRyDQogICAgICAgICB0Y190YXg9dGF4KQ0KDQpaIDwtIA0KICBmdWxsX2pvaW4oQixaLGJ5PWMoInBpZCIsInllYXIiLCJ5IikpICU+JSANCiAgZmlsdGVyKHk+PXRheGZyZWUmYXRyPjApDQoNCk9MUyA8LSBmX3RheF9sbShaKSAlPiUgbXV0YXRlKG1ldGhvZD0iT0xTIikNCk5MUyA8LSBmX3RheF9ubHMoWikgJT4lIG11dGF0ZShtZXRob2Q9Ik5MUyIpDQoNCkNvZWZzX3RlbXAgPC0gYmluZF9yb3dzKE9MUyxOTFMpICAgICANCg0KICAgIFExIDwtIA0KICAgICAgWiAlPiUgDQogICAgICBtdXRhdGUoDQogICAgICAgIHQgPSB5LU9MUyR2YWx1ZVsxXSp5XigxLU9MUyR2YWx1ZVsyXSksDQogICAgICAgIHggPSB5LXQsDQogICAgICApICU+JSANCiAgICAgIGZfaW5kaWNlcyguKQ0KICAgIA0KICAgIFEyIDwtIA0KICAgICAgWiAlPiUgDQogICAgICBtdXRhdGUoDQogICAgICAgIHQgPSB5LU5MUyR2YWx1ZVsxXSp5XigxLU5MUyR2YWx1ZVsyXSksDQogICAgICAgIHggPSB5LXQsDQogICAgICApICU+JSANCiAgICAgIGZfaW5kaWNlcyguKQ0KICAgIA0KICAgIFFRIDwtIA0KICAgICAgQSAlPiUgDQogICAgICBtdXRhdGUoDQogICAgICAgIHQgPSBpbmNfdGF4LA0KICAgICAgICB4ID0geS10DQogICAgICApICU+JSANCiAgICAgIGZfaW5kaWNlcyguKQ0KICAgIA0KICAgIENvZWZzX3RlbXAgPC0gDQogICAgICBiaW5kX3Jvd3MoT0xTLE5MUykgJT4lIA0KICAgICAgbXV0YXRlKFN1aXRzX2VzdCA9IGlmX2Vsc2UobWV0aG9kPT0iT0xTIixRMSRTX2FyZWFbMV0sUTIkU19hcmVhWzFdKSwNCiAgICAgICAgICAgICBTdWl0c19kYXRhID0gUVEkU19hcmVhWzFdKQ0KICAgIA0KICAgIGlmIChpPT0xKXsNCiAgICAgIENvZWZzPUNvZWZzX3RlbXANCiAgICB9DQogICAgZWxzZXsNCiAgICAgIENvZWZzPWJpbmRfcm93cyhDb2VmcyxDb2Vmc190ZW1wKQ0KICAgIH0NCiAgICANCiAgICBwcmludChzb3VyY2V5ZWFyc1tpXSkNCg0KfQ0KDQpzYXZlUkRTKENvZWZzLGhlcmUocmVzdWx0cywiVFBfMDFfVGF1X0xhbWJkYV9TdWl0c19kYXRhLnJkcyIpKQ0KYGBgDQoNCmBgYHtyfQ0KWCA8LSANCiAgcmVhZFJEUyhoZXJlKHJlc3VsdHMsIlRQXzAxX1RhdV9MYW1iZGFfU3VpdHNfZGF0YS5yZHMiKSkgJT4lIA0KICBmaWx0ZXIodGVybT09InRhdSIpDQoNClggJT4lIA0KICBnZ3Bsb3QoYWVzKHg9eWVhcix5PXZhbHVlLGNvbG9yPW1ldGhvZCxzaGFwZT1tZXRob2QpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZSgpICsNCiBsYWJzKA0KICAgIHggPSAiWWVhciIsIHkgPSBleHByZXNzaW9uKHBhc3RlKCJQcm9ncmVzc2l2aXR5ICIsdGF1KSkNCiAgKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk5MCwyMDIwLGJ5PTIpKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odD0xMCkpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odD0xMCkpDQogICkNCg0KZ2dzYXZlKA0KICBoZXJlKHJlc3VsdHMsIlRQXzAxX1RhdV90cmVuZHNfZGF0YS5wZGYiKQ0KKQ0KYGBgDQoNCiMjIENoZWNrIGlmIFN1aXRzIGluZGV4IG1hdGNoZXMNCg0KYGBge3J9DQpYIDwtIA0KICByZWFkUkRTKGhlcmUocmVzdWx0cywiVFBfMDFfVGF1X0xhbWJkYV9TdWl0c19kYXRhLnJkcyIpKSAlPiUgDQogIGZpbHRlcih0ZXJtPT0idGF1IiZtZXRob2Q9PSJOTFMiKSAlPiUgDQogIHJlbmFtZShFc3RpbWF0ZT1TdWl0c19lc3QsRGF0YT1TdWl0c19kYXRhKSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhFc3RpbWF0ZSxEYXRhKSwgbmFtZXNfdG8gPSAiU3VpdHMgaW5kZXgiLCB2YWx1ZXNfdG8gPSAieXkiKQ0KDQoNClggJT4lIA0KICBnZ3Bsb3QoYWVzKHg9eWVhcix5PXl5LGNvbG9yPWBTdWl0cyBpbmRleGAsc2hhcGU9YFN1aXRzIGluZGV4YCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKCkgKw0KIGxhYnMoDQogICAgeCA9ICJZZWFyIiwgeSA9ICJTdWl0cyBpbmRleCAodGF4YWJsZSBpbmNvbWUgYXMgYmFzZSkiDQogICkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5OTAsMjAyMCxieT0yKSkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQ9MTApKSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQ9MTApKQ0KICApDQoNCmdnc2F2ZSgNCiAgaGVyZShyZXN1bHRzLCJUUF8wMV9DaGVja19zdWl0c19pbmRleF90YXVfZXN0aW1hdGUucGRmIikNCikNCmBgYA0K