Preparing packages and data

#-------------------------------------------------------------
# STEP 1: Load Required Libraries
#-------------------------------------------------------------
# Required packages
required_packages <- c(
  "survival", "dplyr", "ggplot2", "survminer", "tidyr", 
  "car", "pROC", "ROCR", "timeROC"
)

# Install missing packages
missing_packages <- required_packages[!required_packages %in% installed.packages()[,"Package"]]
if(length(missing_packages) > 0) {
  install.packages(missing_packages)
}

# Load packages
suppressPackageStartupMessages({
  library(survival)      # For survival models
  library(dplyr)         # For data manipulation
  library(ggplot2)       # For data visualization
  library(survminer)     # For survival analysis visualizations
  library(tidyr)         # For data tidying
  library(car)           # For VIF analysis
  library(pROC)          # For ROC analysis
  library(ROCR)          # For ROC curves and AUC 
  library(timeROC)       # For time-dependent ROC curves
})
#-------------------------------------------------------------
# STEP 2: Data Loading and Initial Inspection
#-------------------------------------------------------------
# Read the dataset
data <- read.csv("1.3.csv", stringsAsFactors = FALSE)

# Display basic data information
cat("Data dimensions:", dim(data), "\n")
Data dimensions: 63591 29 
print(head(dplyr::select(data, cusip, gsector, start, stop, event_type), n = 5))

# Check for missing values in key columns
missing_key <- colSums(is.na(data[, c("cusip", "gsector", "start", "stop", "event_type")]))
if(sum(missing_key) > 0) {
  cat("WARNING: Missing values in key columns:\n")
  print(missing_key[missing_key > 0])
}

# Check for duplicated rows
potential_duplicates <- data %>%
  group_by(cusip, start, stop) %>%
  filter(n() > 1) %>%
  ungroup()

if(nrow(potential_duplicates) > 0) {
  cat("WARNING:", nrow(potential_duplicates), "potential duplicate entries found.\n")
  
  # Display the first few duplicates for inspection
  cat("Sample of duplicate records:\n")
  duplicate_sample <- potential_duplicates %>%
    dplyr::select(cusip, gsector, start, stop, event_type) %>%
    head(10)
  print(duplicate_sample)
  
  # For a more detailed inspection of exact duplicates
  exact_duplicates <- data[duplicated(data) | duplicated(data, fromLast = TRUE), ]
  cat("\nExact duplicate rows (all columns identical):", nrow(exact_duplicates), "\n")
  if(nrow(exact_duplicates) > 0) {
    cat("Sample of exact duplicates:\n")
    exact_dup_sample <- exact_duplicates %>%
      dplyr::select(cusip, gsector, start, stop, event_type) %>%
      head(5)
    print(exact_dup_sample)
  }
  
  # Ask user whether to remove duplicates
  cat("\nDuplicates can be caused by:\n")
  cat("1. Exact same record entered twice (safe to remove)\n")
  cat("2. Same time period but different financial values (requires inspection)\n")
  cat("3. Data entry errors (may need correction, not just removal)\n\n")
  
  # Remove duplicates after inspection - by default we'll keep the first occurrence
  cat("Removing duplicates and keeping first occurrence of each record.\n")
  data <- distinct(data)
  cat("After removing duplicates, data dimensions:", dim(data), "\n")
}
#-------------------------------------------------------------
# STEP 3: Data Preprocessing (MODIFIED)
#-------------------------------------------------------------
# Convert date strings to Date objects and ensure proper time intervals
data <- data %>%
  # Convert date strings to Date objects
  mutate(
    start_date = as.Date(start),
    stop_date = as.Date(stop)
  ) %>%
  # Filter out records with invalid dates
  filter(!is.na(start_date) & !is.na(stop_date) & start_date <= stop_date)

# Calculate time intervals in years from COMPANY-SPECIFIC origins
data <- data %>%
  group_by(cusip) %>%
  mutate(
    # Find each company's first appearance date
    company_start_date = min(start_date),
    
    # Calculate time since IPO for start and stop
    tstart = as.numeric(start_date - company_start_date) / 365.25,
    tstop = as.numeric(stop_date - company_start_date) / 365.25
  ) %>%
  ungroup() %>%
  # Ensure gsector is a factor
  mutate(gsector = as.factor(gsector))

# Create event indicators for cause-specific analysis
data <- data %>%
  mutate(
    bankruptcy = ifelse(event_type == 1, 1, 0),
    acquisition = ifelse(event_type == 2, 1, 0)
  )

All companies are brought back to time t=0, which is the date of their IPO. The time intervals are calculated in years from this date.

Macro Variables are introduced by matching the calendar year of the macro data with the calendar year of the company data. The macro data is reshaped from wide to long format, and missing values are handled by using the closest available year.

#-------------------------------------------------------------
# STEP 3.5: Add Macroeconomic Variables
#-------------------------------------------------------------
# Load macro data
macro_data <- read.csv("macro.csv", check.names = FALSE)

# First, let's examine the structure of the macro data
cat("Structure of macro data:\n")
Structure of macro data:
str(macro_data)
'data.frame':   3 obs. of  39 variables:
 $ code: chr  "gdp_deflator" "unemployement" "gdp_growth"
 $ 1985: num  2.01 7 3.46
 $ 1986: num  2.01 7 3.46
 $ 1987: num  2.48 6.2 3.45
 $ 1988: num  3.53 5.5 4.18
 $ 1989: num  3.92 5.3 3.67
 $ 1990: num  3.74 5.6 1.89
 $ 1992: num  2.28 7.5 3.52
 $ 1993: num  2.37 6.9 2.75
 $ 1994: num  2.14 6.12 4.03
 $ 1995: num  2.1 5.65 2.68
 $ 1996: num  1.83 5.45 3.77
 $ 1997: num  1.72 5 4.45
 $ 1998: num  1.12 4.51 4.48
 $ 1999: num  1.42 4.22 4.79
 $ 2000: num  2.27 3.99 4.08
 $ 2001: num  2.252 4.731 0.956
 $ 2002: num  1.55 5.78 1.7
 $ 2003: num  1.97 5.99 2.8
 $ 2004: num  2.69 5.53 3.85
 $ 2005: num  3.14 5.08 3.48
 $ 2006: num  3.08 4.62 2.78
 $ 2008: num  1.927 5.784 0.114
 $ 2009: num  0.617 9.254 -2.577
 $ 2010: num  1.22 9.63 2.7
 $ 2011: num  2.06 8.95 1.56
 $ 2012: num  1.86 8.07 2.29
 $ 2013: num  1.7 7.38 2.12
 $ 2014: num  1.74 6.17 2.52
 $ 2015: num  0.928 5.28 2.946
 $ 2016: num  0.95 4.87 1.82
 $ 2017: num  1.79 4.36 2.46
 $ 2018: num  2.29 3.9 2.97
 $ 2019: num  1.65 3.67 2.58
 $ 2020: num  1.33 8.05 -2.16
 $ 2021: num  4.57 5.35 6.06
 $ 2022: num  7.13 3.65 2.51
 $ 2023: num  3.6 3.64 2.89
 $ 2024: num  3.6 3.64 2.89
# Reshape from wide to long format
macro_long <- macro_data %>%
  # Pivot to long format - all columns except 'code'
  pivot_longer(
    cols = -code,
    names_to = "year",
    values_to = "value"
  ) %>%
  # Convert year to numeric
  mutate(year = as.numeric(year)) %>%
  # Pivot wider to get one row per year with columns for each macro variable
  pivot_wider(
    id_cols = year,
    names_from = code,
    values_from = value
  )

# Display the first few rows of the reshaped data
cat("\nFirst few rows of reshaped macro data:\n")

First few rows of reshaped macro data:
print(head(macro_long))

# Extract calendar year from date for joining
data <- data %>%
  mutate(calendar_year = as.numeric(format(start_date, "%Y")))

# Check the range of years in both datasets
cat("\nRange of years in company data:", range(data$calendar_year), "\n")

Range of years in company data: 1985 2024 
cat("Range of years in macro data:", range(macro_long$year), "\n")
Range of years in macro data: 1985 2024 
# Join macro data to company data
data <- data %>%
  left_join(macro_long, by = c("calendar_year" = "year"))

# Check if macro variables were successfully added
cat("\nChecking if macro variables were added successfully:\n")

Checking if macro variables were added successfully:
macro_vars <- c("gdp_deflator", "unemployement", "gdp_growth")
na_count <- colSums(is.na(data[, macro_vars]))
print(na_count)
 gdp_deflator unemployement    gdp_growth 
         2588          2588          2588 
# If there are missing values, handle them
if(sum(na_count) > 0) {
  cat("\nHandling missing macro variable values...\n")
  
  # For any missing year in the macro data, use the closest available year
  for(var in macro_vars) {
    # Get years with available data
    available_years <- macro_long$year[!is.na(macro_long[[var]])]
    
    # For each company-year with missing data
    missing_rows <- which(is.na(data[[var]]))
    
    if(length(missing_rows) > 0) {
      cat("Imputing missing values for", var, "...\n")
      
      for(i in missing_rows) {
        company_year <- data$calendar_year[i]
        
        # Find closest available year
        closest_year <- available_years[which.min(abs(available_years - company_year))]
        
        # Get the value from that year
        replacement_value <- macro_long[[var]][macro_long$year == closest_year]
        
        # Replace missing value
        data[[var]][i] <- replacement_value
      }
    }
  }
  
  # Check if all missing values are now handled
  na_count_after <- colSums(is.na(data[, macro_vars]))
  cat("\nMissing values after imputation:\n")
  print(na_count_after)
}

Handling missing macro variable values...
Imputing missing values for gdp_deflator ...
Imputing missing values for unemployement ...
Imputing missing values for gdp_growth ...

Missing values after imputation:
 gdp_deflator unemployement    gdp_growth 
            0             0             0 
# Define macro covariates for modeling
macro_covariates <- c("gdp_deflator", "unemployement", "gdp_growth")

cat("\nMacroeconomic variables have been successfully integrated into the dataset.\n")

Macroeconomic variables have been successfully integrated into the dataset.

Time continuity is checked by identifying companies with gaps or overlaps in their time intervals. If any issues are found, the problematic companies are removed from the dataset.

#-------------------------------------------------------------
# STEP 4: Check and Fix Time Continuity
#-------------------------------------------------------------
# Identify companies with time issues (gaps or overlaps)
time_issues <- data %>%
  group_by(cusip) %>%
  arrange(cusip, tstart) %>%
  mutate(
    # Calculate gaps and overlaps
    gap = tstart - lag(tstop),
    overlap = lag(tstop) - tstart
  ) %>%
  # Find problematic records
  filter(!is.na(gap) & (gap > 0.01 | overlap > 0.01)) %>%
  ungroup()

if(nrow(time_issues) > 0) {
  cat("WARNING: Found", nrow(time_issues), "records with time continuity issues.\n")
  
  # Identify companies with issues
  problem_companies <- unique(time_issues$cusip)
  cat("Number of companies with time issues:", length(problem_companies), "\n")
  
  # Option 1: Remove companies with time issues
  data_clean <- data %>%
    filter(!cusip %in% problem_companies)
  
  # Use the cleaned dataset
  data <- data_clean
  cat("Using cleaned dataset with", length(unique(data$cusip)), "companies after removing problematic ones.\n")
}

# Save the cleaned dataframe after time continuity checks to CSV
write.csv(data, file = "cox_model_cleaned_data.csv", row.names = FALSE)
cat("Cleaned dataframe saved to 'cox_model_cleaned_data.csv'\n")
Cleaned dataframe saved to 'cox_model_cleaned_data.csv'

Covariates are preprocessed by checking for missing values, winsorizing extreme values, and calculating the Altman Z-score. The Z-score is added to the financial covariates list, and the final list of covariates is prepared for modeling.

#-------------------------------------------------------------
# STEP 5: Financial and Macro Covariates Preprocessing
#-------------------------------------------------------------
# Define financial covariates based on your dataset
financial_covariates <- c(
  "LTMTA", "NIMTA", "CASHMTA", "PRICE", "MBE", "RSIZE", "l_at", "l_mkvalt", 
  "debt_ratio", "debt_service", "current_ratio", "quick_ratio", "cash_to_assets", 
  "wc_ratio", "ebit_margin", "gp_margin", "asset_turnover", "receivables_turnover", 
  "intangibility", "ebit_growth", "EBIT_VOL_3Y"
)

# Define macro covariates separately
macro_covariates <- c("gdp_deflator", "unemployement", "gdp_growth")

# Create combined list when needed
all_covariates <- c(financial_covariates, macro_covariates)

# Check for missing values in financial covariates
missing_financials <- colSums(is.na(data[, financial_covariates]))
if(sum(missing_financials) > 0) {
  cat("Missing values in financial covariates:\n")
  print(missing_financials)
  
  # Impute missing values with median by industry sector
  data <- data %>%
    group_by(gsector) %>%
    mutate(across(all_of(financial_covariates), 
                 ~ifelse(is.na(.), median(., na.rm = TRUE), .))) %>%
    ungroup()
  
  # Check if any missing values remain
  still_missing <- colSums(is.na(data[, financial_covariates]))
  if(sum(still_missing) > 0) {
    # Global median imputation for any remaining missing values
    data <- data %>%
      mutate(across(all_of(financial_covariates), 
                   ~ifelse(is.na(.), median(., na.rm = TRUE), .)))
  }
}

# Check for missing values in macro covariates
missing_macro <- colSums(is.na(data[, macro_covariates]))
if(sum(missing_macro) > 0) {
  cat("Missing values in macro covariates:\n")
  print(missing_macro)
  
  # For macro variables, impute with the closest available year value
  for(var in macro_covariates) {
    if(missing_macro[var] > 0) {
      # Get all available years and values
      available_data <- data[!is.na(data[[var]]), c("calendar_year", var)]
      available_years <- available_data$calendar_year
      
      # For each missing value
      missing_rows <- which(is.na(data[[var]]))
      for(row in missing_rows) {
        year_needed <- data$calendar_year[row]
        # Find closest year
        closest_year_idx <- which.min(abs(available_years - year_needed))
        # Impute with value from closest year
        data[[var]][row] <- available_data[[var]][closest_year_idx]
      }
    }
  }
  
  # Check if all missing values are now handled
  still_missing_macro <- colSums(is.na(data[, macro_covariates]))
  if(sum(still_missing_macro) > 0) {
    cat("Warning: Some macro variables still have missing values after imputation.\n")
    print(still_missing_macro)
  } else {
    cat("All missing macro values successfully imputed.\n")
  }
}

# Winsorize extreme values for financial covariates
cat("Winsorizing financial covariates...\n")
Winsorizing financial covariates...
for(var in financial_covariates) {
  p01 <- quantile(data[[var]], 0.01, na.rm = TRUE)
  p99 <- quantile(data[[var]], 0.99, na.rm = TRUE)
  
  data[[var]] <- ifelse(data[[var]] < p01, p01, data[[var]])
  data[[var]] <- ifelse(data[[var]] > p99, p99, data[[var]])
}

# Winsorize extreme values for macro covariates
cat("Winsorizing macro covariates...\n")
Winsorizing macro covariates...
for(var in macro_covariates) {
  p01 <- quantile(data[[var]], 0.01, na.rm = TRUE)
  p99 <- quantile(data[[var]], 0.99, na.rm = TRUE)
  
  data[[var]] <- ifelse(data[[var]] < p01, p01, data[[var]])
  data[[var]] <- ifelse(data[[var]] > p99, p99, data[[var]])
}

# Print summary statistics separately for financial and macro covariates
cat("\nSummary statistics for financial covariates (sample):\n")

Summary statistics for financial covariates (sample):
# Select a random subset of financial variables to display
sample_fin_vars <- sample(financial_covariates, min(5, length(financial_covariates)))
print(summary(data[, sample_fin_vars]))
    l_mkvalt           LTMTA           quick_ratio          CASHMTA           debt_service      
 Min.   : 0.2585   Min.   :0.006782   Min.   : 0.08372   Min.   :0.0004349   Min.   :-10.20097  
 1st Qu.: 4.1074   1st Qu.:0.113594   1st Qu.: 0.83377   1st Qu.:0.0230676   1st Qu.: -0.02842  
 Median : 5.7775   Median :0.269980   Median : 1.41114   Median :0.0700084   Median :  0.03086  
 Mean   : 5.6766   Mean   :0.326143   Mean   : 2.49695   Mean   :0.1262264   Mean   :  0.15769  
 3rd Qu.: 7.2314   3rd Qu.:0.491489   3rd Qu.: 2.70340   3rd Qu.:0.1617134   3rd Qu.:  0.25824  
 Max.   :10.7932   Max.   :0.960709   Max.   :21.34286   Max.   :0.9537153   Max.   : 11.24850  
cat("\nSummary statistics for macro covariates:\n")

Summary statistics for macro covariates:
print(summary(data[, macro_covariates]))
  gdp_deflator    unemployement     gdp_growth    
 Min.   :0.6168   Min.   :3.638   Min.   :-2.576  
 1st Qu.:1.5540   1st Qu.:4.511   1st Qu.: 2.118  
 Median :1.9743   Median :5.280   Median : 2.785  
 Mean   :2.2536   Mean   :5.565   Mean   : 2.674  
 3rd Qu.:2.6891   3rd Qu.:5.989   3rd Qu.: 3.773  
 Max.   :7.1295   Max.   :9.633   Max.   : 6.055  
# Calculate correlation between macro variables
cat("\nCorrelation between macro variables:\n")

Correlation between macro variables:
macro_cor <- cor(data[, macro_covariates], use = "pairwise.complete.obs")
print(round(macro_cor, 3))
              gdp_deflator unemployement gdp_growth
gdp_deflator         1.000        -0.388      0.259
unemployement       -0.388         1.000     -0.496
gdp_growth           0.259        -0.496      1.000
# Calculate correlation between macro variables and key financial variables
key_financial <- c("LTMTA", "NIMTA", "PRICE", "z_score")  # Select a few key financial vars
key_financial <- intersect(key_financial, names(data))  # Make sure they exist

if(length(key_financial) > 0) {
  cat("\nCorrelation between macro and key financial variables:\n")
  cross_cor <- cor(data[, macro_covariates], data[, key_financial], use = "pairwise.complete.obs")
  print(round(cross_cor, 3))
}

Correlation between macro and key financial variables:
               LTMTA  NIMTA  PRICE
gdp_deflator  -0.017 -0.041  0.048
unemployement -0.026  0.068 -0.015
gdp_growth    -0.062  0.021  0.027
# Save the cleaned dataframe with preprocessed covariates
write.csv(data, file = "cox_model_cleaned_data_with_macro.csv", row.names = FALSE)
cat("Cleaned dataframe with financial and macro covariates saved to 'cox_model_cleaned_data_with_macro.csv'\n")
Cleaned dataframe with financial and macro covariates saved to 'cox_model_cleaned_data_with_macro.csv'

We check for multicollinearity among financial covariates and macro covariates. We also check for correlations between financial and macro covariates. The final list of covariates is prepared for modeling, and the Altman Z-score is calculated.

#-------------------------------------------------------------
# STEP 6: Check for Multicollinearity
#-------------------------------------------------------------
# 6.1: Check multicollinearity among financial covariates
cat("\n=== Checking multicollinearity among financial covariates ===\n")

=== Checking multicollinearity among financial covariates ===
cor_matrix_financial <- cor(data[, financial_covariates], use = "pairwise.complete.obs")
print(round(cor_matrix_financial, 2))
                     LTMTA NIMTA CASHMTA PRICE   MBE RSIZE  l_at l_mkvalt debt_ratio debt_service current_ratio quick_ratio
LTMTA                 1.00 -0.01   -0.12 -0.33 -0.38 -0.30  0.18    -0.30       0.58         0.02         -0.41       -0.39
NIMTA                -0.01  1.00   -0.43  0.47 -0.02  0.37  0.37     0.37      -0.02         0.10         -0.13       -0.15
CASHMTA              -0.12 -0.43    1.00 -0.25 -0.19 -0.26 -0.21    -0.26      -0.31        -0.05          0.46        0.47
PRICE                -0.33  0.47   -0.25  1.00  0.06  0.82  0.66     0.82      -0.19         0.08          0.02        0.01
MBE                  -0.38 -0.02   -0.19  0.06  1.00  0.14 -0.24     0.14       0.11        -0.04          0.08        0.10
RSIZE                -0.30  0.37   -0.26  0.82  0.14  1.00  0.84     1.00      -0.07         0.06         -0.03       -0.02
l_at                  0.18  0.37   -0.21  0.66 -0.24  0.84  1.00     0.84       0.06         0.08         -0.17       -0.16
l_mkvalt             -0.30  0.37   -0.26  0.82  0.14  1.00  0.84     1.00      -0.07         0.06         -0.03       -0.02
debt_ratio            0.58 -0.02   -0.31 -0.19  0.11 -0.07  0.06    -0.07       1.00        -0.02         -0.45       -0.40
debt_service          0.02  0.10   -0.05  0.08 -0.04  0.06  0.08     0.06      -0.02         1.00         -0.04       -0.05
current_ratio        -0.41 -0.13    0.46  0.02  0.08 -0.03 -0.17    -0.03      -0.45        -0.04          1.00        0.94
quick_ratio          -0.39 -0.15    0.47  0.01  0.10 -0.02 -0.16    -0.02      -0.40        -0.05          0.94        1.00
cash_to_assets       -0.48 -0.32    0.64 -0.06  0.25 -0.04 -0.28    -0.04      -0.29        -0.06          0.60        0.64
wc_ratio             -0.50 -0.05    0.48  0.12  0.03  0.01 -0.17     0.01      -0.63         0.00          0.67        0.59
ebit_margin           0.11  0.32   -0.16  0.15 -0.13  0.10  0.18     0.10       0.03         0.03         -0.23       -0.25
gp_margin             0.09  0.26   -0.14  0.12 -0.11  0.09  0.14     0.09       0.02         0.03         -0.22       -0.24
asset_turnover        0.18  0.18   -0.19 -0.10 -0.01 -0.20 -0.15    -0.20       0.21        -0.01         -0.26       -0.29
receivables_turnover  0.06  0.03    0.00 -0.02  0.00 -0.02  0.00    -0.02       0.06        -0.02         -0.04       -0.06
intangibility        -0.04 -0.10   -0.06 -0.08  0.24 -0.03 -0.10    -0.03       0.11        -0.06         -0.02        0.02
ebit_growth          -0.05  0.01   -0.02  0.08  0.03  0.06  0.03     0.06      -0.02         0.01          0.01        0.02
EBIT_VOL_3Y           0.02 -0.05    0.04 -0.11 -0.01 -0.10 -0.09    -0.10      -0.02        -0.02          0.00        0.01
                     cash_to_assets wc_ratio ebit_margin gp_margin asset_turnover receivables_turnover intangibility ebit_growth
LTMTA                         -0.48    -0.50        0.11      0.09           0.18                 0.06         -0.04       -0.05
NIMTA                         -0.32    -0.05        0.32      0.26           0.18                 0.03         -0.10        0.01
CASHMTA                        0.64     0.48       -0.16     -0.14          -0.19                 0.00         -0.06       -0.02
PRICE                         -0.06     0.12        0.15      0.12          -0.10                -0.02         -0.08        0.08
MBE                            0.25     0.03       -0.13     -0.11          -0.01                 0.00          0.24        0.03
RSIZE                         -0.04     0.01        0.10      0.09          -0.20                -0.02         -0.03        0.06
l_at                          -0.28    -0.17        0.18      0.14          -0.15                 0.00         -0.10        0.03
l_mkvalt                      -0.04     0.01        0.10      0.09          -0.20                -0.02         -0.03        0.06
debt_ratio                    -0.29    -0.63        0.03      0.02           0.21                 0.06          0.11       -0.02
debt_service                  -0.06     0.00        0.03      0.03          -0.01                -0.02         -0.06        0.01
current_ratio                  0.60     0.67       -0.23     -0.22          -0.26                -0.04         -0.02        0.01
quick_ratio                    0.64     0.59       -0.25     -0.24          -0.29                -0.06          0.02        0.02
cash_to_assets                 1.00     0.65       -0.29     -0.26          -0.28                -0.02          0.03        0.01
wc_ratio                       0.65     1.00       -0.12     -0.12          -0.14                -0.10         -0.13        0.01
ebit_margin                   -0.29    -0.12        1.00      0.82           0.21                 0.05         -0.09       -0.02
gp_margin                     -0.26    -0.12        0.82      1.00           0.17                 0.04         -0.08       -0.02
asset_turnover                -0.28    -0.14        0.21      0.17           1.00                 0.23         -0.02       -0.02
receivables_turnover          -0.02    -0.10        0.05      0.04           0.23                 1.00         -0.01       -0.01
intangibility                  0.03    -0.13       -0.09     -0.08          -0.02                -0.01          1.00        0.02
ebit_growth                    0.01     0.01       -0.02     -0.02          -0.02                -0.01          0.02        1.00
EBIT_VOL_3Y                    0.01     0.00        0.03      0.04           0.01                -0.01          0.00        0.00
                     EBIT_VOL_3Y
LTMTA                       0.02
NIMTA                      -0.05
CASHMTA                     0.04
PRICE                      -0.11
MBE                        -0.01
RSIZE                      -0.10
l_at                       -0.09
l_mkvalt                   -0.10
debt_ratio                 -0.02
debt_service               -0.02
current_ratio               0.00
quick_ratio                 0.01
cash_to_assets              0.01
wc_ratio                    0.00
ebit_margin                 0.03
gp_margin                   0.04
asset_turnover              0.01
receivables_turnover       -0.01
intangibility               0.00
ebit_growth                 0.00
EBIT_VOL_3Y                 1.00
# Identify high correlation pairs (|r| > 0.7)
high_cor <- which(abs(cor_matrix_financial) > 0.7 & abs(cor_matrix_financial) < 1, arr.ind = TRUE)
if(nrow(high_cor) > 0) {
  cat("High correlations detected among financial variables:\n")
  for(i in 1:nrow(high_cor)) {
    if(high_cor[i, 1] < high_cor[i, 2]) { # avoid printing duplicates
      cat(financial_covariates[high_cor[i, 1]], "and", 
          financial_covariates[high_cor[i, 2]], ":", 
          round(cor_matrix_financial[high_cor[i, 1], high_cor[i, 2]], 2), "\n")
    }
  }
  
  # Identify variables to remove based on highest correlations
  # Calculate the average correlation for each variable
  avg_cor <- rowMeans(abs(cor_matrix_financial))
  print("Average absolute correlation for each financial variable:")
  print(sort(avg_cor, decreasing = TRUE))
  
  # Identify pairs with high correlation
  high_cor_pairs <- data.frame(
    var1 = financial_covariates[high_cor[, 1]],
    var2 = financial_covariates[high_cor[, 2]],
    correlation = cor_matrix_financial[high_cor]
  ) %>%
    filter(var1 < var2) %>%  # Remove duplicates
    arrange(desc(abs(correlation)))
  
  print("Highly correlated financial variable pairs:")
  print(high_cor_pairs)
  
  # Remove variables with highest average correlation from each highly correlated pair
  variables_to_remove <- c()
  for(i in 1:nrow(high_cor_pairs)) {
    var1 <- high_cor_pairs$var1[i]
    var2 <- high_cor_pairs$var2[i]
    
    # Skip if either variable is already marked for removal
    if(var1 %in% variables_to_remove || var2 %in% variables_to_remove) {
      next
    }
    
    # Remove the variable with higher average correlation
    if(avg_cor[var1] > avg_cor[var2]) {
      variables_to_remove <- c(variables_to_remove, var1)
    } else {
      variables_to_remove <- c(variables_to_remove, var2)
    }
  }
  
  cat("Financial variables to remove due to high multicollinearity:\n")
  print(variables_to_remove)
  
  # Update financial covariates list
  financial_covariates <- setdiff(financial_covariates, variables_to_remove)
  cat("Remaining financial covariates after removing highly correlated variables:\n")
  print(financial_covariates)
}
High correlations detected among financial variables:
PRICE and RSIZE : 0.82 
RSIZE and l_at : 0.84 
PRICE and l_mkvalt : 0.82 
l_at and l_mkvalt : 0.84 
current_ratio and quick_ratio : 0.94 
ebit_margin and gp_margin : 0.82 
[1] "Average absolute correlation for each financial variable:"
      cash_to_assets                 l_at          quick_ratio        current_ratio              CASHMTA                RSIZE 
          0.29752252           0.28295661           0.27785758           0.27734226           0.27355856           0.26527350 
            l_mkvalt                LTMTA                PRICE             wc_ratio                NIMTA           debt_ratio 
          0.26527350           0.26441233           0.26313526           0.26296002           0.22552870           0.22341849 
         ebit_margin            gp_margin       asset_turnover                  MBE        intangibility         debt_service 
          0.21060506           0.19541133           0.19192915           0.15758691           0.10866531           0.08741067 
receivables_turnover          EBIT_VOL_3Y          ebit_growth 
          0.08617568           0.07979219           0.07188633 
[1] "Highly correlated financial variable pairs:"
Financial variables to remove due to high multicollinearity:
[1] "quick_ratio" "l_at"        "ebit_margin" "l_mkvalt"    "RSIZE"      
Remaining financial covariates after removing highly correlated variables:
 [1] "LTMTA"                "NIMTA"                "CASHMTA"              "PRICE"                "MBE"                 
 [6] "debt_ratio"           "debt_service"         "current_ratio"        "cash_to_assets"       "wc_ratio"            
[11] "gp_margin"            "asset_turnover"       "receivables_turnover" "intangibility"        "ebit_growth"         
[16] "EBIT_VOL_3Y"         
# 6.2: Check multicollinearity among macro covariates
cat("\n=== Checking multicollinearity among macro covariates ===\n")

=== Checking multicollinearity among macro covariates ===
cor_matrix_macro <- cor(data[, macro_covariates], use = "pairwise.complete.obs")
print(round(cor_matrix_macro, 2))
              gdp_deflator unemployement gdp_growth
gdp_deflator          1.00         -0.39       0.26
unemployement        -0.39          1.00      -0.50
gdp_growth            0.26         -0.50       1.00
# Check for high correlations among macro variables
high_cor_macro <- which(abs(cor_matrix_macro) > 0.7 & abs(cor_matrix_macro) < 1, arr.ind = TRUE)
if(nrow(high_cor_macro) > 0) {
  cat("High correlations detected among macro variables:\n")
  for(i in 1:nrow(high_cor_macro)) {
    if(high_cor_macro[i, 1] < high_cor_macro[i, 2]) { # avoid printing duplicates
      cat(macro_covariates[high_cor_macro[i, 1]], "and", 
          macro_covariates[high_cor_macro[i, 2]], ":", 
          round(cor_matrix_macro[high_cor_macro[i, 1], high_cor_macro[i, 2]], 2), "\n")
    }
  }
  
  # For macro variables, we'll keep all of them and note the correlations for interpretation
  cat("Note: All macro variables will be retained despite correlations for economic interpretation.\n")
  cat("When interpreting results, consider these correlations for proper contextualization.\n")
} else {
  cat("No high correlations detected among macro variables.\n")
}
No high correlations detected among macro variables.
# 6.3: Check correlation between financial and macro variables
cat("\n=== Checking correlation between financial and macro variables ===\n")

=== Checking correlation between financial and macro variables ===
cross_cor_matrix <- cor(data[, financial_covariates], data[, macro_covariates], use = "pairwise.complete.obs")
print(round(cross_cor_matrix, 2))
                     gdp_deflator unemployement gdp_growth
LTMTA                       -0.02         -0.03      -0.06
NIMTA                       -0.04          0.07       0.02
CASHMTA                      0.02          0.02      -0.05
PRICE                        0.05         -0.02       0.03
MBE                          0.03          0.00       0.07
debt_ratio                   0.03         -0.02      -0.04
debt_service                -0.01          0.01      -0.01
current_ratio                0.01          0.00       0.02
cash_to_assets               0.02          0.01      -0.01
wc_ratio                     0.00          0.03       0.03
gp_margin                   -0.02          0.02       0.00
asset_turnover              -0.05          0.03       0.03
receivables_turnover         0.00          0.01      -0.01
intangibility                0.00         -0.01       0.01
ebit_growth                  0.02         -0.02       0.04
EBIT_VOL_3Y                  0.01         -0.01       0.01
# Identify high cross-correlations
high_cross_cor <- which(abs(cross_cor_matrix) > 0.5, arr.ind = TRUE)
if(nrow(high_cross_cor) > 0) {
  cat("Notable correlations between financial and macro variables:\n")
  for(i in 1:nrow(high_cross_cor)) {
    cat(financial_covariates[high_cross_cor[i, 1]], "and", 
        macro_covariates[high_cross_cor[i, 2]], ":", 
        round(cross_cor_matrix[high_cross_cor[i, 1], high_cross_cor[i, 2]], 2), "\n")
  }
  
  cat("These correlations indicate relationships between firm financials and macroeconomic conditions.\n")
  cat("Consider these when interpreting coefficients in the final models.\n")
}

# 6.4: Create the final list of all covariates for modeling
all_covariates <- c(financial_covariates, macro_covariates)
cat("\nFinal list of covariates for modeling:", length(all_covariates), "variables\n")

Final list of covariates for modeling: 19 variables
cat("Financial covariates:", length(financial_covariates), "\n")
Financial covariates: 16 
cat("Macro covariates:", length(macro_covariates), "\n")
Macro covariates: 3 
#-------------------------------------------------------------
# STEP 6.1: Calculate Altman Z-score
#-------------------------------------------------------------
cat("\nCalculating Altman Z-score for bankruptcy prediction...\n")

Calculating Altman Z-score for bankruptcy prediction...
# Create function to calculate Z-score based on available variables
calculate_z_score <- function(data) {
  # Altman Z-score components:
  # Z = 1.2X₁ + 1.4X₂ + 3.3X₃ + 0.6X₄ + 1.0X₅
  # Where:
  # X₁ = Working Capital / Total Assets
  # X₂ = Retained Earnings / Total Assets (we'll use a proxy if not available)
  # X₃ = EBIT / Total Assets
  # X₄ = Market Value of Equity / Total Liabilities
  # X₅ = Sales / Total Assets
  
  # We'll adapt the calculation based on available variables in the dataset
  
  # X₁: Working Capital / Total Assets (using wc_ratio if available)
  X1 <- data$wc_ratio
  
  # X₃: EBIT / Total Assets (using ebit_margin as a proxy, adjusted by asset turnover)
  X3 <- data$ebit_margin * data$asset_turnover
  
  # X₄: Market Value of Equity / Total Liabilities
  # Approximating this using available variables:
  # Market Value of Equity can be derived from LTMTA (Total Liabilities / Market Value)
  # LTMTA = Total Liabilities / Market Value
  # So Market Value = Total Liabilities / LTMTA
  # And X₄ = Market Value / Total Liabilities = 1 / LTMTA
  X4 <- 1 / data$LTMTA
  
  # X₅: Sales / Total Assets (this is asset_turnover)
  X5 <- data$asset_turnover
  
  # For X₂, we don't have direct retained earnings data
  # We can use NIMTA (Net Income / Market Value) as a rough proxy of profitability
  # Adjusted to approximate Retained Earnings / Total Assets
  X2 <- data$NIMTA * (1 / data$LTMTA) * 0.7  # Scaling factor to approximate
  
  # Calculate Z-score with available components
  # If some components are missing, we'll adjust the weights
  z_score <- 1.2 * X1 + 1.4 * X2 + 3.3 * X3 + 0.6 * X4 + 1.0 * X5
  
  return(z_score)
}

# Calculate Z-score for each row
data$z_score <- calculate_z_score(data)

# Check Z-score distribution
z_summary <- summary(data$z_score)
cat("Z-score summary statistics:\n")
Z-score summary statistics:
print(z_summary)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-413.956    2.397    3.872    7.118    6.846  125.627 
# Define risk categories based on Z-score
data$z_risk_category <- cut(data$z_score, 
                          breaks = c(-Inf, 1.81, 2.99, Inf),
                          labels = c("High Risk", "Grey Zone", "Low Risk"))

# Display distribution of risk categories
z_distribution <- table(data$z_risk_category)
cat("\nDistribution of Z-score risk categories:\n")

Distribution of Z-score risk categories:
print(z_distribution)

High Risk Grey Zone  Low Risk 
    10035     12720     40836 
print(prop.table(z_distribution) * 100)

High Risk Grey Zone  Low Risk 
 15.78053  20.00283  64.21663 
# Check correlation between Z-score and event types
cat("\nMean Z-score by final outcome:\n")

Mean Z-score by final outcome:
aggregate(z_score ~ finalevent, data = data, mean)

# Add Z-score to financial covariates list
financial_covariates <- c(financial_covariates, "z_score")
cat("\nAdded Z-score to financial covariates list.\n")

Added Z-score to financial covariates list.
cat("Updated financial covariates:", length(financial_covariates), "variables\n")
Updated financial covariates: 17 variables
# Update the combined covariates list as well
all_covariates <- c(financial_covariates, macro_covariates)
cat("Updated all covariates list:", length(all_covariates), "variables\n")
Updated all covariates list: 20 variables
# Visualize Z-score distribution by event type if ggplot2 is available
if(requireNamespace("ggplot2", quietly = TRUE)) {
  # Create data for visualization
  plot_data <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%  # Get last observation for each company
    ungroup() %>%
    mutate(event_label = case_when(
      event_type == 0 ~ "Censored",
      event_type == 1 ~ "Bankruptcy",
      event_type == 2 ~ "Acquisition"
    ))
  
  # Box plot of Z-score by event type
  p <- ggplot(plot_data, aes(x = event_label, y = z_score, fill = event_label)) +
    geom_boxplot() +
    geom_hline(yintercept = 1.81, linetype = "dashed", color = "red") +
    geom_hline(yintercept = 2.99, linetype = "dashed", color = "blue") +
    scale_fill_manual(values = c("Censored" = "grey", "Bankruptcy" = "red", "Acquisition" = "blue")) +
    labs(title = "Altman Z-score by Company Outcome",
         x = "Final Company Status",
         y = "Z-score",
         subtitle = "Red line = 1.81 (High Risk Threshold), Blue line = 2.99 (Low Risk Threshold)") +
    theme_minimal()
  
  print(p)
  ggsave("z_score_by_outcome.png", p, width = 8, height = 6)
}

Here we show the Kaplan-Meier survival curves for bankruptcy and acquisition events. We also calculate median survival times and plot the curves together for comparison. The survminer package is used to create more detailed plots with risk tables. We also have survival curves stratified by industry sector for both bankruptcy and acquisition events.

#-------------------------------------------------------------
cat("\n=== Survival Curve Analysis for Bankruptcy and Acquisition ===\n")

=== Survival Curve Analysis for Bankruptcy and Acquisition ===
# Create separate survival objects for each event type
surv_bankruptcy <- Surv(data$tstart, data$tstop, data$bankruptcy)
surv_acquisition <- Surv(data$tstart, data$tstop, data$acquisition)

# Fit Kaplan-Meier survival curves
km_bankruptcy <- survfit(surv_bankruptcy ~ 1, data = data)
km_acquisition <- survfit(surv_acquisition ~ 1, data = data)

# Basic summary of survival estimates
cat("\nSummary of Bankruptcy Survival Curve:\n")

Summary of Bankruptcy Survival Curve:
print(summary(km_bankruptcy, times = c(1, 3, 5, 10)))
Call: survfit(formula = surv_bankruptcy ~ 1, data = data)

 time n.risk n.event survival  std.err lower 95% CI upper 95% CI
    1   4909       4    0.999 0.000815        0.997        1.000
    3   4353      30    0.990 0.001767        0.987        0.994
    5   3628      23    0.984 0.002257        0.980        0.989
   10   2413      45    0.965 0.003858        0.958        0.973
cat("\nSummary of Acquisition Survival Curve:\n")

Summary of Acquisition Survival Curve:
print(summary(km_acquisition, times = c(1, 3, 5, 10)))
Call: survfit(formula = surv_acquisition ~ 1, data = data)

 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    1   4909      64    0.980 0.00306        0.974        0.986
    3   4353     271    0.908 0.00514        0.898        0.919
    5   3628     277    0.824 0.00756        0.809        0.839
   10   2413     581    0.641 0.00952        0.622        0.660
# Calculate median survival times (if they exist)
cat("\nMedian Survival Times:\n")

Median Survival Times:
bankruptcy_median <- summary(km_bankruptcy)$table["median"]
if(is.na(bankruptcy_median)) {
  cat("Bankruptcy: More than 50% of companies survive the entire observation period without bankruptcy\n")
} else {
  cat("Bankruptcy: Median survival time =", round(bankruptcy_median, 2), "years\n")
}
Bankruptcy: More than 50% of companies survive the entire observation period without bankruptcy
acquisition_median <- summary(km_acquisition)$table["median"]
if(is.na(acquisition_median)) {
  cat("Acquisition: More than 50% of companies survive the entire observation period without being acquired\n")
} else {
  cat("Acquisition: Median survival time =", round(acquisition_median, 2), "years\n")
}
Acquisition: Median survival time = 15 years
# Plot both survival curves together for comparison
par(mfrow = c(1, 1))  # Reset plotting parameters
combined_plot <- plot(km_bankruptcy, 
                      conf.int = TRUE, 
                      col = "red", 
                      lwd = 2,
                      xlab = "Years", 
                      ylab = "Survival Probability",
                      main = "Kaplan-Meier Survival Curves: Bankruptcy vs Acquisition")

# Add acquisition curve
lines(km_acquisition, col = "blue", lwd = 2, conf.int = TRUE, lty = 1)

# Add legend
legend("topright", 
       legend = c("Bankruptcy", "Acquisition"), 
       col = c("red", "blue"), 
       lwd = 2, 
       cex = 0.8)

# Save the plot
dev.copy(png, "combined_survival_curves.png", width = 800, height = 600)
png 
  3 
dev.off()
png 
  2 

# Create more detailed plots using survminer
if(requireNamespace("survminer", quietly = TRUE)) {
  # Bankruptcy survival curve with risk table
  p_bankruptcy <- ggsurvplot(
    km_bankruptcy,
    data = data,
    risk.table = TRUE,
    risk.table.col = "strata",
    ggtheme = theme_minimal(),
    palette = "red",
    title = "Bankruptcy Survival Curve",
    xlab = "Years",
    ylab = "Probability of Not Going Bankrupt",
    conf.int = TRUE,
    surv.median.line = "hv"
  )
  
  print(p_bankruptcy)
  ggsave("bankruptcy_survival_curve.png", p_bankruptcy$plot, width = 10, height = 8)
  
  # Acquisition survival curve with risk table
  p_acquisition <- ggsurvplot(
    km_acquisition,
    data = data,
    risk.table = TRUE,
    risk.table.col = "strata",
    ggtheme = theme_minimal(),
    palette = "blue",
    title = "Acquisition Survival Curve",
    xlab = "Years",
    ylab = "Probability of Not Being Acquired",
    conf.int = TRUE,
    surv.median.line = "hv"
  )
  
  print(p_acquisition)
  ggsave("acquisition_survival_curve.png", p_acquisition$plot, width = 10, height = 8)
  
  # Combined plot with survminer
  # First create separate survfit objects with labels
  km_bankruptcy_labeled <- survfit(surv_bankruptcy ~ rep("Bankruptcy", nrow(data)), data = data)
  km_acquisition_labeled <- survfit(surv_acquisition ~ rep("Acquisition", nrow(data)), data = data)
  
  # Combine them
  combined_survfit <- list(Bankruptcy = km_bankruptcy, Acquisition = km_acquisition)
  class(combined_survfit) <- c("survfitlist", "list")
  
  # Plot combined curves
  p_combined <- ggsurvplot(
    combined_survfit,
    data = data,
    risk.table = TRUE,
    risk.table.col = "strata",
    ggtheme = theme_minimal(),
    palette = c("red", "blue"),
    title = "Comparison of Bankruptcy and Acquisition Survival Curves",
    xlab = "Years",
    ylab = "Survival Probability",
    conf.int = TRUE,
    legend.labs = c("Bankruptcy", "Acquisition"),
    legend.title = "Event Type"
  )
  
  print(p_combined)
  ggsave("combined_survival_curves_ggplot.png", p_combined$plot, width = 10, height = 8)
  
  # Create stratified survival curves by industry sector for bankruptcy
  sector_bankruptcy <- survfit(surv_bankruptcy ~ gsector, data = data)
  p_sector_bankruptcy <- ggsurvplot(
    sector_bankruptcy,
    data = data,
    risk.table = TRUE,
    risk.table.col = "strata",
    ggtheme = theme_minimal(),
    palette = "jco",
    title = "Bankruptcy Survival Curves by Industry Sector",
    xlab = "Years",
    ylab = "Probability of Not Going Bankrupt",
    conf.int = FALSE,
    legend.title = "Industry Sector"
  )
  
  print(p_sector_bankruptcy)
  ggsave("bankruptcy_by_sector.png", p_sector_bankruptcy$plot, width = 12, height = 9)
  
  # Create stratified survival curves by industry sector for acquisition
  sector_acquisition <- survfit(surv_acquisition ~ gsector, data = data)
  p_sector_acquisition <- ggsurvplot(
    sector_acquisition,
    data = data,
    risk.table = TRUE,
    risk.table.col = "strata",
    ggtheme = theme_minimal(),
    palette = "jco",
    title = "Acquisition Survival Curves by Industry Sector",
    xlab = "Years",
    ylab = "Probability of Not Being Acquired",
    conf.int = FALSE,
    legend.title = "Industry Sector"
  )
  
  print(p_sector_acquisition)
  ggsave("acquisition_by_sector.png", p_sector_acquisition$plot, width = 12, height = 9)
}
Warning: Median survival not reached.
$Bankruptcy

$Acquisition

attr(,"class")
[1] "list"            "ggsurvplot_list"

We start by fitting Cox proportional hazards models with Z-score as the only predictor. We then fit individual Cox models for each macroeconomic variable, including stratification by industry sector. We calculate concordance (C-index) for each model to assess predictive performance. Finally, we visualize the results and compare the models.

#-------------------------------------------------------------
# STEP 6.2: Z-score Only Cox Models
#-------------------------------------------------------------
cat("\nFitting Cox models with Z-score as the only predictor...\n")

Fitting Cox models with Z-score as the only predictor...
# Create formulas with Z-score as the only predictor
# Include stratification by industry sector
z_bankruptcy_formula <- as.formula("Surv(tstart, tstop, bankruptcy) ~ z_score + strata(gsector)")
z_acquisition_formula <- as.formula("Surv(tstart, tstop, acquisition) ~ z_score + strata(gsector)")

# Fit models
z_bankruptcy_model <- coxph(z_bankruptcy_formula, data = data, ties = "efron")
z_acquisition_model <- coxph(z_acquisition_formula, data = data, ties = "efron")

# Display model summaries
cat("\n=== Z-SCORE ONLY: BANKRUPTCY MODEL ===\n")

=== Z-SCORE ONLY: BANKRUPTCY MODEL ===
print(summary(z_bankruptcy_model))
Call:
coxph(formula = z_bankruptcy_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

             coef exp(coef)  se(coef)     z Pr(>|z|)    
z_score -0.015265  0.984851  0.003297 -4.63 3.65e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

        exp(coef) exp(-coef) lower .95 upper .95
z_score    0.9849      1.015    0.9785    0.9912

Concordance= 0.773  (se = 0.026 )
Likelihood ratio test= 12.46  on 1 df,   p=4e-04
Wald test            = 21.44  on 1 df,   p=4e-06
Score (logrank) test = 18.74  on 1 df,   p=1e-05
cat("\n=== Z-SCORE ONLY: ACQUISITION MODEL ===\n")

=== Z-SCORE ONLY: ACQUISITION MODEL ===
print(summary(z_acquisition_model))
Call:
coxph(formula = z_acquisition_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

           coef exp(coef) se(coef)      z Pr(>|z|)    
z_score -0.0131    0.9870   0.0013 -10.08   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

        exp(coef) exp(-coef) lower .95 upper .95
z_score     0.987      1.013    0.9845    0.9895

Concordance= 0.571  (se = 0.007 )
Likelihood ratio test= 64.21  on 1 df,   p=1e-15
Wald test            = 101.6  on 1 df,   p=<2e-16
Score (logrank) test = 67.53  on 1 df,   p=<2e-16
# Calculate concordance (C-index) as a measure of predictive ability
cat("\nPredictive performance (C-index):\n")

Predictive performance (C-index):
cat("Z-score only bankruptcy model:", round(z_bankruptcy_model$concordance["concordance"], 3), "\n")
Z-score only bankruptcy model: 0.773 
cat("Z-score only acquisition model:", round(z_acquisition_model$concordance["concordance"], 3), "\n")
Z-score only acquisition model: 0.571 
# Check proportional hazards assumption
cat("\nTesting proportional hazards assumption for Z-score models...\n")

Testing proportional hazards assumption for Z-score models...
ph_test_z_bankruptcy <- cox.zph(z_bankruptcy_model)
ph_test_z_acquisition <- cox.zph(z_acquisition_model)

cat("\n=== PH TEST FOR Z-SCORE BANKRUPTCY MODEL ===\n")

=== PH TEST FOR Z-SCORE BANKRUPTCY MODEL ===
print(ph_test_z_bankruptcy)
        chisq df    p
z_score  1.25  1 0.26
GLOBAL   1.25  1 0.26
cat("\n=== PH TEST FOR Z-SCORE ACQUISITION MODEL ===\n")

=== PH TEST FOR Z-SCORE ACQUISITION MODEL ===
print(ph_test_z_acquisition)
        chisq df       p
z_score  27.1  1 1.9e-07
GLOBAL   27.1  1 1.9e-07
# Store models for later comparison
z_score_models <- list(
  bankruptcy = z_bankruptcy_model,
  acquisition = z_acquisition_model
)
#-------------------------------------------------------------
# STEP 6.3: Macro Variables Only Cox Models
#-------------------------------------------------------------
cat("\nFitting Cox models with individual macro variables as predictors...\n")

Fitting Cox models with individual macro variables as predictors...
# Create empty lists to store models
macro_bankruptcy_models <- list()
macro_acquisition_models <- list()

# Concordance results for all models
concordance_results <- data.frame(
  Variable = c("z_score", macro_covariates),
  BK_Concordance = NA,
  ACQ_Concordance = NA
)

# Add Z-score concordance
concordance_results$BK_Concordance[concordance_results$Variable == "z_score"] <- 
  z_bankruptcy_model$concordance["concordance"]
concordance_results$ACQ_Concordance[concordance_results$Variable == "z_score"] <- 
  z_acquisition_model$concordance["concordance"]

# Fit individual models for each macro variable
for(var in macro_covariates) {
  cat("\nTesting macro variable:", var, "\n")
  
  # Create formulas with single macro variable
  macro_bk_formula <- as.formula(paste0("Surv(tstart, tstop, bankruptcy) ~ ", var, " + strata(gsector)"))
  macro_acq_formula <- as.formula(paste0("Surv(tstart, tstop, acquisition) ~ ", var, " + strata(gsector)"))
  
  # Fit models
  tryCatch({
    bk_model <- coxph(macro_bk_formula, data = data, ties = "efron")
    macro_bankruptcy_models[[var]] <- bk_model
    
    # Store concordance
    concordance_results$BK_Concordance[concordance_results$Variable == var] <- 
      bk_model$concordance["concordance"]
    
    # Print summary
    cat("\n=== MACRO ONLY (", var, "): BANKRUPTCY MODEL ===\n")
    print(summary(bk_model))
  }, error = function(e) {
    cat("Error fitting bankruptcy model with", var, ":", e$message, "\n")
  })
  
  tryCatch({
    acq_model <- coxph(macro_acq_formula, data = data, ties = "efron")
    macro_acquisition_models[[var]] <- acq_model
    
    # Store concordance
    concordance_results$ACQ_Concordance[concordance_results$Variable == var] <- 
      acq_model$concordance["concordance"]
    
    # Print summary
    cat("\n=== MACRO ONLY (", var, "): ACQUISITION MODEL ===\n")
    print(summary(acq_model))
  }, error = function(e) {
    cat("Error fitting acquisition model with", var, ":", e$message, "\n")
  })
}

Testing macro variable: gdp_deflator 

=== MACRO ONLY ( gdp_deflator ): BANKRUPTCY MODEL ===
Call:
coxph(formula = macro_bk_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                 coef exp(coef) se(coef)      z Pr(>|z|)
gdp_deflator -0.12067   0.88633  0.07614 -1.585    0.113

             exp(coef) exp(-coef) lower .95 upper .95
gdp_deflator    0.8863      1.128    0.7635     1.029

Concordance= 0.57  (se = 0.025 )
Likelihood ratio test= 2.72  on 1 df,   p=0.1
Wald test            = 2.51  on 1 df,   p=0.1
Score (logrank) test = 2.52  on 1 df,   p=0.1


=== MACRO ONLY ( gdp_deflator ): ACQUISITION MODEL ===
Call:
coxph(formula = macro_acq_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                 coef exp(coef) se(coef)      z Pr(>|z|)
gdp_deflator -0.01524   0.98487  0.01989 -0.766    0.443

             exp(coef) exp(-coef) lower .95 upper .95
gdp_deflator    0.9849      1.015    0.9472     1.024

Concordance= 0.548  (se = 0.009 )
Likelihood ratio test= 0.59  on 1 df,   p=0.4
Wald test            = 0.59  on 1 df,   p=0.4
Score (logrank) test = 0.59  on 1 df,   p=0.4


Testing macro variable: unemployement 

=== MACRO ONLY ( unemployement ): BANKRUPTCY MODEL ===
Call:
coxph(formula = macro_bk_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                  coef exp(coef) se(coef)      z Pr(>|z|)    
unemployement -0.19272   0.82471  0.05731 -3.363 0.000771 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

              exp(coef) exp(-coef) lower .95 upper .95
unemployement    0.8247      1.213    0.7371    0.9227

Concordance= 0.583  (se = 0.026 )
Likelihood ratio test= 12.59  on 1 df,   p=4e-04
Wald test            = 11.31  on 1 df,   p=8e-04
Score (logrank) test = 11.51  on 1 df,   p=7e-04


=== MACRO ONLY ( unemployement ): ACQUISITION MODEL ===
Call:
coxph(formula = macro_acq_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                  coef exp(coef) se(coef)     z Pr(>|z|)   
unemployement -0.03775   0.96295  0.01430 -2.64   0.0083 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

              exp(coef) exp(-coef) lower .95 upper .95
unemployement    0.9629      1.038    0.9363    0.9903

Concordance= 0.56  (se = 0.008 )
Likelihood ratio test= 7.09  on 1 df,   p=0.008
Wald test            = 6.97  on 1 df,   p=0.008
Score (logrank) test = 6.97  on 1 df,   p=0.008


Testing macro variable: gdp_growth 

=== MACRO ONLY ( gdp_growth ): BANKRUPTCY MODEL ===
Call:
coxph(formula = macro_bk_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

              coef exp(coef) se(coef)     z Pr(>|z|)
gdp_growth 0.03280   1.03334  0.04525 0.725    0.469

           exp(coef) exp(-coef) lower .95 upper .95
gdp_growth     1.033     0.9677    0.9456     1.129

Concordance= 0.534  (se = 0.03 )
Likelihood ratio test= 0.54  on 1 df,   p=0.5
Wald test            = 0.53  on 1 df,   p=0.5
Score (logrank) test = 0.53  on 1 df,   p=0.5


=== MACRO ONLY ( gdp_growth ): ACQUISITION MODEL ===
Call:
coxph(formula = macro_acq_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

              coef exp(coef) se(coef)     z Pr(>|z|)  
gdp_growth 0.03150   1.03201  0.01266 2.489   0.0128 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

           exp(coef) exp(-coef) lower .95 upper .95
gdp_growth     1.032      0.969     1.007     1.058

Concordance= 0.529  (se = 0.008 )
Likelihood ratio test= 6.32  on 1 df,   p=0.01
Wald test            = 6.2  on 1 df,   p=0.01
Score (logrank) test = 6.2  on 1 df,   p=0.01
# Compare predictive performance
cat("\nComparison of predictive performance (C-index):\n")

Comparison of predictive performance (C-index):
concordance_results <- concordance_results %>%
  arrange(desc(BK_Concordance))

print(concordance_results)

# Visualize comparison
if(requireNamespace("ggplot2", quietly = TRUE)) {
  # Prepare data for plotting
  plot_data <- concordance_results %>%
    pivot_longer(cols = c(BK_Concordance, ACQ_Concordance),
                 names_to = "Model",
                 values_to = "Concordance") %>%
    mutate(Model = ifelse(Model == "BK_Concordance", "Bankruptcy", "Acquisition"))
  
  # Create plot
  p <- ggplot(plot_data, aes(x = reorder(Variable, Concordance), y = Concordance, fill = Model)) +
    geom_bar(stat = "identity", position = "dodge") +
    coord_flip() +
    labs(title = "Predictive Performance of Individual Variables",
         subtitle = "Comparing Z-score vs. Macroeconomic Variables",
         x = "Variable",
         y = "Concordance (C-index)") +
    theme_minimal() +
    theme(legend.position = "bottom")
  
  print(p)
  ggsave("single_predictor_comparison.png", p, width = 10, height = 6)
}


# Test the best macro variable and Z-score together
best_macro <- concordance_results$Variable[concordance_results$Variable != "z_score"][1]
cat("\nTesting Z-score and best macro variable (", best_macro, ") together...\n")

Testing Z-score and best macro variable ( unemployement ) together...
# Create formula with Z-score and best macro variable
combined_bk_formula <- as.formula(paste0("Surv(tstart, tstop, bankruptcy) ~ z_score + ", 
                                        best_macro, " + strata(gsector)"))
combined_acq_formula <- as.formula(paste0("Surv(tstart, tstop, acquisition) ~ z_score + ", 
                                         best_macro, " + strata(gsector)"))

# Fit models
combined_bk_model <- coxph(combined_bk_formula, data = data, ties = "efron")
combined_acq_model <- coxph(combined_acq_formula, data = data, ties = "efron")

# Display model summaries
cat("\n=== Z-SCORE + BEST MACRO: BANKRUPTCY MODEL ===\n")

=== Z-SCORE + BEST MACRO: BANKRUPTCY MODEL ===
print(summary(combined_bk_model))
Call:
coxph(formula = combined_bk_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                   coef exp(coef)  se(coef)      z Pr(>|z|)    
z_score       -0.015370  0.984748  0.003456 -4.447 8.69e-06 ***
unemployement -0.189444  0.827419  0.057053 -3.320 0.000899 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

              exp(coef) exp(-coef) lower .95 upper .95
z_score          0.9847      1.015    0.9781    0.9914
unemployement    0.8274      1.209    0.7399    0.9253

Concordance= 0.636  (se = 0.024 )
Likelihood ratio test= 24.71  on 2 df,   p=4e-06
Wald test            = 30.61  on 2 df,   p=2e-07
Score (logrank) test = 29.38  on 2 df,   p=4e-07
cat("\n=== Z-SCORE + BEST MACRO: ACQUISITION MODEL ===\n")

=== Z-SCORE + BEST MACRO: ACQUISITION MODEL ===
print(summary(combined_acq_model))
Call:
coxph(formula = combined_acq_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                   coef exp(coef)  se(coef)       z Pr(>|z|)    
z_score       -0.013102  0.986984  0.001307 -10.023   <2e-16 ***
unemployement -0.036113  0.964531  0.014267  -2.531   0.0114 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

              exp(coef) exp(-coef) lower .95 upper .95
z_score          0.9870      1.013    0.9845    0.9895
unemployement    0.9645      1.037    0.9379    0.9919

Concordance= 0.584  (se = 0.007 )
Likelihood ratio test= 70.73  on 2 df,   p=4e-16
Wald test            = 106.9  on 2 df,   p=<2e-16
Score (logrank) test = 73.51  on 2 df,   p=<2e-16
# Compare concordance
cat("\nCombined model performance (C-index):\n")

Combined model performance (C-index):
cat("Z-score + Best Macro bankruptcy model:", 
    round(combined_bk_model$concordance["concordance"], 3), "\n")
Z-score + Best Macro bankruptcy model: 0.636 
cat("Z-score + Best Macro acquisition model:", 
    round(combined_acq_model$concordance["concordance"], 3), "\n")
Z-score + Best Macro acquisition model: 0.584 
# Test for improvement using likelihood ratio test
cat("\nLikelihood ratio test for improvement over Z-score alone:\n")

Likelihood ratio test for improvement over Z-score alone:
cat("Bankruptcy model improvement:\n")
Bankruptcy model improvement:
print(anova(z_bankruptcy_model, combined_bk_model))
Analysis of Deviance Table
 Cox model: response is  Surv(tstart, tstop, bankruptcy)
 Model 1: ~ z_score + strata(gsector)
 Model 2: ~ z_score + unemployement + strata(gsector)
   loglik Chisq Df Pr(>|Chi|)    
1 -990.23                        
2 -984.11 12.25  1  0.0004653 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
cat("Acquisition model improvement:\n")
Acquisition model improvement:
print(anova(z_acquisition_model, combined_acq_model))
Analysis of Deviance Table
 Cox model: response is  Surv(tstart, tstop, acquisition)
 Model 1: ~ z_score + strata(gsector)
 Model 2: ~ z_score + unemployement + strata(gsector)
  loglik Chisq Df Pr(>|Chi|)  
1 -12304                      
2 -12300 6.516  1    0.01069 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# Store combined models for later comparison
combined_models <- list(
  bankruptcy = combined_bk_model,
  acquisition = combined_acq_model
)

Here, we start by performing variable selection using stepwise regression and LASSO methods. We then fit Cox models with the selected variables and evaluate their performance. We also visualize the results and compare the models.

#-------------------------------------------------------------
# STEP 7: Variable Selection with Financial and Macro Variables
#-------------------------------------------------------------
cat("\nPerforming variable selection using both stepwise and LASSO methods...\n")

Performing variable selection using both stepwise and LASSO methods...
#-------------------------------------------------------------
# 7.1: Pre-screening of Macroeconomic Variables
#-------------------------------------------------------------
cat("\n7.1: Pre-screening Macroeconomic Variables with Univariate Models\n")

7.1: Pre-screening Macroeconomic Variables with Univariate Models
# Pre-screen macro variables with univariate models
macro_screening <- data.frame(
  Variable = character(),
  BK_Coefficient = numeric(),
  BK_P_Value = numeric(),
  BK_Concordance = numeric(),
  ACQ_Coefficient = numeric(),
  ACQ_P_Value = numeric(),
  ACQ_Concordance = numeric(),
  stringsAsFactors = FALSE
)

for(var in macro_covariates) {
  # Test for bankruptcy
  bk_formula <- as.formula(paste0("Surv(tstart, tstop, bankruptcy) ~ ", var, " + strata(gsector)"))
  bk_model <- coxph(bk_formula, data = data, ties = "efron")
  bk_summary <- summary(bk_model)
  
  # Test for acquisition
  acq_formula <- as.formula(paste0("Surv(tstart, tstop, acquisition) ~ ", var, " + strata(gsector)"))
  acq_model <- coxph(acq_formula, data = data, ties = "efron")
  acq_summary <- summary(acq_model)
  
  # Store results
  macro_screening <- rbind(macro_screening, data.frame(
    Variable = var,
    BK_Coefficient = bk_summary$coefficients[1, 1],
    BK_P_Value = bk_summary$coefficients[1, 5],
    BK_Concordance = bk_model$concordance["concordance"],
    ACQ_Coefficient = acq_summary$coefficients[1, 1],
    ACQ_P_Value = acq_summary$coefficients[1, 5],
    ACQ_Concordance = acq_model$concordance["concordance"],
    stringsAsFactors = FALSE
  ))
}

# Print macro variable screening results
cat("\nMacroeconomic variables univariate testing results:\n")

Macroeconomic variables univariate testing results:
print(macro_screening)

# Identify significant macro variables (p < 0.05)
significant_bk_macro <- macro_screening$Variable[macro_screening$BK_P_Value < 0.05]
significant_acq_macro <- macro_screening$Variable[macro_screening$ACQ_P_Value < 0.05]

cat("\nSignificant macro variables for bankruptcy:", paste(significant_bk_macro, collapse=", "), "\n")

Significant macro variables for bankruptcy: unemployement 
cat("Significant macro variables for acquisition:", paste(significant_acq_macro, collapse=", "), "\n")
Significant macro variables for acquisition: unemployement, gdp_growth 
#-------------------------------------------------------------
# 7.2: AIC-based Stepwise Selection with All Variables
#-------------------------------------------------------------
cat("\n7.2: AIC-based Stepwise Selection with Financial and Macro Variables\n")

7.2: AIC-based Stepwise Selection with Financial and Macro Variables
# Create the full model formula for bankruptcy (including all variables)
full_bankruptcy_formula <- as.formula(paste0(
  "Surv(tstart, tstop, bankruptcy) ~ ", 
  paste(c(financial_covariates, macro_covariates), collapse = " + "), 
  " + strata(gsector)"
))

# Create the full model formula for acquisition (including all variables)
full_acquisition_formula <- as.formula(paste0(
  "Surv(tstart, tstop, acquisition) ~ ", 
  paste(c(financial_covariates, macro_covariates), collapse = " + "), 
  " + strata(gsector)"
))

# Fit the full models
full_bankruptcy_model <- coxph(full_bankruptcy_formula, data = data, ties = "efron")
full_acquisition_model <- coxph(full_acquisition_formula, data = data, ties = "efron")

# Perform stepwise selection for bankruptcy model
stepwise_bankruptcy <- step(full_bankruptcy_model, direction = "both", trace = 1)
Start:  AIC=1596.41
Surv(tstart, tstop, bankruptcy) ~ LTMTA + NIMTA + CASHMTA + PRICE + 
    MBE + debt_ratio + debt_service + current_ratio + cash_to_assets + 
    wc_ratio + gp_margin + asset_turnover + receivables_turnover + 
    intangibility + ebit_growth + EBIT_VOL_3Y + z_score + gdp_deflator + 
    unemployement + gdp_growth + strata(gsector)


Step:  AIC=2243.98
Surv(tstart, tstop, bankruptcy) ~ LTMTA + NIMTA + CASHMTA + PRICE + 
    MBE + debt_ratio + debt_service + current_ratio + cash_to_assets + 
    wc_ratio + gp_margin + asset_turnover + receivables_turnover + 
    intangibility + ebit_growth + EBIT_VOL_3Y + z_score + gdp_deflator + 
    unemployement + gdp_growth
# Perform stepwise selection for acquisition model
stepwise_acquisition <- step(full_acquisition_model, direction = "both", trace = 1)
Start:  AIC=24299.87
Surv(tstart, tstop, acquisition) ~ LTMTA + NIMTA + CASHMTA + 
    PRICE + MBE + debt_ratio + debt_service + current_ratio + 
    cash_to_assets + wc_ratio + gp_margin + asset_turnover + 
    receivables_turnover + intangibility + ebit_growth + EBIT_VOL_3Y + 
    z_score + gdp_deflator + unemployement + gdp_growth + strata(gsector)


Step:  AIC=32170.03
Surv(tstart, tstop, acquisition) ~ LTMTA + NIMTA + CASHMTA + 
    PRICE + MBE + debt_ratio + debt_service + current_ratio + 
    cash_to_assets + wc_ratio + gp_margin + asset_turnover + 
    receivables_turnover + intangibility + ebit_growth + EBIT_VOL_3Y + 
    z_score + gdp_deflator + unemployement + gdp_growth
# Get the final selected variables for each model
bankruptcy_vars_step <- names(coef(stepwise_bankruptcy))
bankruptcy_vars_step <- bankruptcy_vars_step[!grepl("strata", bankruptcy_vars_step)]

acquisition_vars_step <- names(coef(stepwise_acquisition))
acquisition_vars_step <- acquisition_vars_step[!grepl("strata", acquisition_vars_step)]

cat("\nSelected variables for bankruptcy model (stepwise):\n")

Selected variables for bankruptcy model (stepwise):
print(bankruptcy_vars_step)
 [1] "LTMTA"                "NIMTA"                "CASHMTA"              "PRICE"                "MBE"                 
 [6] "debt_ratio"           "debt_service"         "current_ratio"        "cash_to_assets"       "wc_ratio"            
[11] "gp_margin"            "asset_turnover"       "receivables_turnover" "intangibility"        "ebit_growth"         
[16] "EBIT_VOL_3Y"          "z_score"              "gdp_deflator"         "unemployement"        "gdp_growth"          
cat("\nSelected variables for acquisition model (stepwise):\n")

Selected variables for acquisition model (stepwise):
print(acquisition_vars_step)
 [1] "LTMTA"                "NIMTA"                "CASHMTA"              "PRICE"                "MBE"                 
 [6] "debt_ratio"           "debt_service"         "current_ratio"        "cash_to_assets"       "wc_ratio"            
[11] "gp_margin"            "asset_turnover"       "receivables_turnover" "intangibility"        "ebit_growth"         
[16] "EBIT_VOL_3Y"          "z_score"              "gdp_deflator"         "unemployement"        "gdp_growth"          
# Identify financial and macro variables selected
bankruptcy_fin_step <- bankruptcy_vars_step[bankruptcy_vars_step %in% financial_covariates]
bankruptcy_macro_step <- bankruptcy_vars_step[bankruptcy_vars_step %in% macro_covariates]

acquisition_fin_step <- acquisition_vars_step[acquisition_vars_step %in% financial_covariates]
acquisition_macro_step <- acquisition_vars_step[acquisition_vars_step %in% macro_covariates]

cat("\nBreakdown of stepwise selection for bankruptcy model:\n")

Breakdown of stepwise selection for bankruptcy model:
cat("- Financial variables:", length(bankruptcy_fin_step), "of", length(financial_covariates), "\n")
- Financial variables: 17 of 17 
cat("- Macro variables:", length(bankruptcy_macro_step), "of", length(macro_covariates), "\n")
- Macro variables: 3 of 3 
cat("\nBreakdown of stepwise selection for acquisition model:\n")

Breakdown of stepwise selection for acquisition model:
cat("- Financial variables:", length(acquisition_fin_step), "of", length(financial_covariates), "\n")
- Financial variables: 17 of 17 
cat("- Macro variables:", length(acquisition_macro_step), "of", length(macro_covariates), "\n")
- Macro variables: 3 of 3 
#-------------------------------------------------------------
# 7.3: LASSO Variable Selection with All Variables
#-------------------------------------------------------------
cat("\n7.3: LASSO-based Variable Selection with Financial and Macro Variables\n")

7.3: LASSO-based Variable Selection with Financial and Macro Variables
# Check if glmnet is available, install if needed
if(!requireNamespace("glmnet", quietly = TRUE)) {
  install.packages("glmnet")
}
library(glmnet)

# Get last observation for each company
last_obs_data <- data %>%
  group_by(cusip) %>%
  slice_max(tstop) %>%
  ungroup()

cat("Using", nrow(last_obs_data), "observations (last record from each company) for LASSO variable selection\n")
Using 5038 observations (last record from each company) for LASSO variable selection
# Function to prepare data for LASSO Cox model (including macro variables)
prepare_lasso_data <- function(data, outcome_var) {
  # Create predictor matrix with both financial and macro variables
  x_vars <- data[, c(financial_covariates, macro_covariates), drop = FALSE]
  
  # Create model matrix (handles factors appropriately)
  x_matrix <- model.matrix(~ . - 1, data = x_vars)  # -1 removes intercept
  
  # Create Surv object for outcome
  if(outcome_var == "bankruptcy") {
    y_surv <- survival::Surv(data$tstop, data$bankruptcy)  # Using only tstop for final status
  } else {
    y_surv <- survival::Surv(data$tstop, data$acquisition)
  }
  
  return(list(x = x_matrix, y = y_surv))
}

# Prepare data for bankruptcy model
lasso_data_bank <- prepare_lasso_data(last_obs_data, "bankruptcy")

# Prepare data for acquisition model
lasso_data_acq <- prepare_lasso_data(last_obs_data, "acquisition")

# Run LASSO without cross-validation first to see variable paths
fit_bank <- glmnet(lasso_data_bank$x, lasso_data_bank$y, family = "cox", alpha = 1)
fit_acq <- glmnet(lasso_data_acq$x, lasso_data_acq$y, family = "cox", alpha = 1)

# Plot coefficient paths
cat("Creating coefficient path plots...\n")
Creating coefficient path plots...
par(mfrow = c(1, 2))
plot(fit_bank, xvar = "lambda", main = "Bankruptcy Model")
plot(fit_acq, xvar = "lambda", main = "Acquisition Model")
par(mfrow = c(1, 1))

# Run cross-validation to find optimal lambda
cat("Running cross-validation with 3 folds to find optimal lambda...\n")
Running cross-validation with 3 folds to find optimal lambda...
set.seed(123)
cv_fit_bank <- cv.glmnet(lasso_data_bank$x, lasso_data_bank$y, 
                       family = "cox", alpha = 1, nfolds = 3, 
                       parallel = FALSE, keep = FALSE)
cv_fit_acq <- cv.glmnet(lasso_data_acq$x, lasso_data_acq$y, 
                      family = "cox", alpha = 1, nfolds = 3,
                      parallel = FALSE, keep = FALSE)

# Plot cross-validation results
cat("Creating cross-validation plots for lambda selection...\n")
Creating cross-validation plots for lambda selection...
par(mfrow = c(1, 2))

plot(cv_fit_bank, main = "Bankruptcy Model CV")
plot(cv_fit_acq, main = "Acquisition Model CV")
par(mfrow = c(1, 1))


# Get optimal lambda values
lambda_bank_min <- cv_fit_bank$lambda.min  # Lambda that gives minimum CV error
lambda_bank_1se <- cv_fit_bank$lambda.1se  # Lambda within 1 std error of minimum

lambda_acq_min <- cv_fit_acq$lambda.min
lambda_acq_1se <- cv_fit_acq$lambda.1se

cat("\nOptimal lambda values for bankruptcy model:\n")

Optimal lambda values for bankruptcy model:
cat("- lambda.min (minimum error):", lambda_bank_min, "\n")
- lambda.min (minimum error): 0.002012214 
cat("- lambda.1se (1 std error rule):", lambda_bank_1se, "\n")
- lambda.1se (1 std error rule): 0.007401688 
cat("\nOptimal lambda values for acquisition model:\n")

Optimal lambda values for acquisition model:
cat("- lambda.min (minimum error):", lambda_acq_min, "\n")
- lambda.min (minimum error): 0.002494769 
cat("- lambda.1se (1 std error rule):", lambda_acq_1se, "\n")
- lambda.1se (1 std error rule): 0.01213107 
# Get coefficients for the LASSO models at the optimal lambda
coef_bank_min <- coef(cv_fit_bank, s = "lambda.min")
coef_bank_1se <- coef(cv_fit_bank, s = "lambda.1se")

coef_acq_min <- coef(cv_fit_acq, s = "lambda.min")
coef_acq_1se <- coef(cv_fit_acq, s = "lambda.1se")

# Extract variable names selected by LASSO (we'll use lambda.1se for parsimony)
bankruptcy_vars_lasso <- rownames(coef_bank_1se)[which(coef_bank_1se != 0)]
acquisition_vars_lasso <- rownames(coef_acq_1se)[which(coef_acq_1se != 0)]

cat("\nSelected variables for bankruptcy model (LASSO):\n")

Selected variables for bankruptcy model (LASSO):
print(bankruptcy_vars_lasso)
[1] "LTMTA"        "NIMTA"        "PRICE"        "gdp_deflator" "gdp_growth"  
cat("\nSelected variables for acquisition model (LASSO):\n")

Selected variables for acquisition model (LASSO):
print(acquisition_vars_lasso)
 [1] "LTMTA"                "NIMTA"                "PRICE"                "MBE"                  "debt_ratio"          
 [6] "current_ratio"        "cash_to_assets"       "wc_ratio"             "gp_margin"            "asset_turnover"      
[11] "receivables_turnover" "EBIT_VOL_3Y"          "gdp_deflator"         "unemployement"        "gdp_growth"          
# Identify financial and macro variables selected by LASSO
bankruptcy_fin_lasso <- bankruptcy_vars_lasso[bankruptcy_vars_lasso %in% financial_covariates]
bankruptcy_macro_lasso <- bankruptcy_vars_lasso[bankruptcy_vars_lasso %in% macro_covariates]

acquisition_fin_lasso <- acquisition_vars_lasso[acquisition_vars_lasso %in% financial_covariates]
acquisition_macro_lasso <- acquisition_vars_lasso[acquisition_vars_lasso %in% macro_covariates]

cat("\nBreakdown of LASSO selection for bankruptcy model:\n")

Breakdown of LASSO selection for bankruptcy model:
cat("- Financial variables:", length(bankruptcy_fin_lasso), "of", length(financial_covariates), "\n")
- Financial variables: 3 of 17 
cat("- Macro variables:", length(bankruptcy_macro_lasso), "of", length(macro_covariates), "\n")
- Macro variables: 2 of 3 
cat("\nBreakdown of LASSO selection for acquisition model:\n")

Breakdown of LASSO selection for acquisition model:
cat("- Financial variables:", length(acquisition_fin_lasso), "of", length(financial_covariates), "\n")
- Financial variables: 12 of 17 
cat("- Macro variables:", length(acquisition_macro_lasso), "of", length(macro_covariates), "\n")
- Macro variables: 3 of 3 

Now, we compare the variables selected by both methods (stepwise and LASSO) and finalize the variable selection for both bankruptcy and acquisition models. We also create formulas for the final models.

#-------------------------------------------------------------
# 7.5: Compare and Finalize Variable Selection
#-------------------------------------------------------------
cat("\n7.5: Comparing and Finalizing Variable Selection\n")

7.5: Comparing and Finalizing Variable Selection
# Compare variables selected by both methods
cat("\nComparison of selected variables for bankruptcy model:\n")

Comparison of selected variables for bankruptcy model:
compare_bank <- data.frame(
  Variable = unique(c(bankruptcy_vars_step, bankruptcy_vars_lasso)),
  Type = ifelse(unique(c(bankruptcy_vars_step, bankruptcy_vars_lasso)) %in% financial_covariates, 
                "Financial", "Macro"),
  Stepwise = unique(c(bankruptcy_vars_step, bankruptcy_vars_lasso)) %in% bankruptcy_vars_step,
  LASSO = unique(c(bankruptcy_vars_step, bankruptcy_vars_lasso)) %in% bankruptcy_vars_lasso
)
print(compare_bank)

cat("\nComparison of selected variables for acquisition model:\n")

Comparison of selected variables for acquisition model:
compare_acq <- data.frame(
  Variable = unique(c(acquisition_vars_step, acquisition_vars_lasso)),
  Type = ifelse(unique(c(acquisition_vars_step, acquisition_vars_lasso)) %in% financial_covariates, 
                "Financial", "Macro"),
  Stepwise = unique(c(acquisition_vars_step, acquisition_vars_lasso)) %in% acquisition_vars_step,
  LASSO = unique(c(acquisition_vars_step, acquisition_vars_lasso)) %in% acquisition_vars_lasso
)
print(compare_acq)

# Decide on final variable sets
# For bankruptcy model: use all variables selected by both methods, plus important ones from either method
bankruptcy_vars <- unique(c(
  # Variables selected by both methods
  intersect(bankruptcy_vars_step, bankruptcy_vars_lasso),
  
  # Add macro variables that are significant in univariate analysis
  significant_bk_macro,
  
  # Add z-score if available (known to be important for bankruptcy prediction)
  if("z_score" %in% financial_covariates) "z_score" else NULL
))

# For acquisition model: use all variables selected by both methods, plus important ones from either method
acquisition_vars <- unique(c(
  # Variables selected by both methods
  intersect(acquisition_vars_step, acquisition_vars_lasso),
  
  # Add macro variables that are significant in univariate analysis
  significant_acq_macro,
  
  # Add key financial variables if available
  if("LTMTA" %in% financial_covariates) "LTMTA" else NULL,
  if("NIMTA" %in% financial_covariates) "NIMTA" else NULL
))

# Print final variable selections
cat("\nFinal selected variables for bankruptcy model:", length(bankruptcy_vars), "variables\n")

Final selected variables for bankruptcy model: 7 variables
bankruptcy_fin_final <- bankruptcy_vars[bankruptcy_vars %in% financial_covariates]
bankruptcy_macro_final <- bankruptcy_vars[bankruptcy_vars %in% macro_covariates]

cat("- Financial variables (", length(bankruptcy_fin_final), "):", 
    paste(bankruptcy_fin_final, collapse=", "), "\n")
- Financial variables ( 4 ): LTMTA, NIMTA, PRICE, z_score 
cat("- Macro variables (", length(bankruptcy_macro_final), "):", 
    paste(bankruptcy_macro_final, collapse=", "), "\n")
- Macro variables ( 3 ): gdp_deflator, gdp_growth, unemployement 
cat("\nFinal selected variables for acquisition model:", length(acquisition_vars), "variables\n")

Final selected variables for acquisition model: 15 variables
acquisition_fin_final <- acquisition_vars[acquisition_vars %in% financial_covariates]
acquisition_macro_final <- acquisition_vars[acquisition_vars %in% macro_covariates]

cat("- Financial variables (", length(acquisition_fin_final), "):", 
    paste(acquisition_fin_final, collapse=", "), "\n")
- Financial variables ( 12 ): LTMTA, NIMTA, PRICE, MBE, debt_ratio, current_ratio, cash_to_assets, wc_ratio, gp_margin, asset_turnover, receivables_turnover, EBIT_VOL_3Y 
cat("- Macro variables (", length(acquisition_macro_final), "):", 
    paste(acquisition_macro_final, collapse=", "), "\n")
- Macro variables ( 3 ): gdp_deflator, unemployement, gdp_growth 
# Save selection results for later steps
selection_results <- list(
  bankruptcy = list(
    stepwise = bankruptcy_vars_step,
    lasso = bankruptcy_vars_lasso,
    final = bankruptcy_vars,
    financial = bankruptcy_fin_final,
    macro = bankruptcy_macro_final
  ),
  acquisition = list(
    stepwise = acquisition_vars_step,
    lasso = acquisition_vars_lasso,
    final = acquisition_vars,
    financial = acquisition_fin_final,
    macro = acquisition_macro_final
  )
)

# Create formulas for the final models
bankruptcy_formula <- as.formula(paste0(
  "Surv(tstart, tstop, bankruptcy) ~ ", 
  paste(bankruptcy_vars, collapse = " + "),
  " + strata(gsector)"
))

acquisition_formula <- as.formula(paste0(
  "Surv(tstart, tstop, acquisition) ~ ", 
  paste(acquisition_vars, collapse = " + "),
  " + strata(gsector)"
))

cat("\nFinal bankruptcy model formula:\n")

Final bankruptcy model formula:
print(bankruptcy_formula)
Surv(tstart, tstop, bankruptcy) ~ LTMTA + NIMTA + PRICE + gdp_deflator + 
    gdp_growth + unemployement + z_score + strata(gsector)
cat("\nFinal acquisition model formula:\n")

Final acquisition model formula:
print(acquisition_formula)
Surv(tstart, tstop, acquisition) ~ LTMTA + NIMTA + PRICE + MBE + 
    debt_ratio + current_ratio + cash_to_assets + wc_ratio + 
    gp_margin + asset_turnover + receivables_turnover + EBIT_VOL_3Y + 
    gdp_deflator + unemployement + gdp_growth + strata(gsector)
# Save formulas for later steps
selection_results$bankruptcy$formula <- bankruptcy_formula
selection_results$acquisition$formula <- acquisition_formula

Finally, we fit the cause-specific Cox models using the selected variables from both stepwise and LASSO methods. We also create a financial-only model and a macro-enriched model for comparison. We evaluate the improvement of the models using likelihood ratio tests and compare their concordance (C-index).

#-------------------------------------------------------------
# STEP 8: Fit Cause-Specific Cox Models with Selected Variables
#-------------------------------------------------------------
cat("\n=== STEP 8: FITTING CAUSE-SPECIFIC COX MODELS WITH FINANCIAL AND MACRO VARIABLES ===\n")

=== STEP 8: FITTING CAUSE-SPECIFIC COX MODELS WITH FINANCIAL AND MACRO VARIABLES ===
#-------------------------------------------------------------
# 8.1: BANKRUPTCY MODELS
#-------------------------------------------------------------
cat("\n8.1: BANKRUPTCY PREDICTION MODELS\n")

8.1: BANKRUPTCY PREDICTION MODELS
cat("--------------------------------------\n")
--------------------------------------
# Create formulas with different sets of selected variables
# 1. Stepwise AIC selection variables
bankruptcy_step_formula <- as.formula(paste0(
  "Surv(tstart, tstop, bankruptcy) ~ ", 
  paste(bankruptcy_vars_step, collapse = " + "), 
  " + strata(gsector)"
))

# 2. LASSO selection variables
bankruptcy_lasso_formula <- as.formula(paste0(
  "Surv(tstart, tstop, bankruptcy) ~ ", 
  paste(bankruptcy_vars_lasso, collapse = " + "), 
  " + strata(gsector)"
))

# 3. Union of both methods (comprehensive approach)
bankruptcy_union_vars <- unique(c(bankruptcy_vars_step, bankruptcy_vars_lasso))
bankruptcy_union_formula <- as.formula(paste0(
  "Surv(tstart, tstop, bankruptcy) ~ ", 
  paste(bankruptcy_union_vars, collapse = " + "), 
  " + strata(gsector)"
))

# 4. Intersection of both methods (conservative approach)
bankruptcy_intersect_vars <- intersect(bankruptcy_vars_step, bankruptcy_vars_lasso)
# Check if intersection is not empty
if(length(bankruptcy_intersect_vars) > 0) {
  bankruptcy_intersect_formula <- as.formula(paste0(
    "Surv(tstart, tstop, bankruptcy) ~ ", 
    paste(bankruptcy_intersect_vars, collapse = " + "), 
    " + strata(gsector)"
  ))
} else {
  # If intersection is empty, use Z-score and significant macro vars as fallback
  cat("No variables in common between stepwise and LASSO for bankruptcy. Using Z-score and significant macro variables as fallback.\n")
  fallback_vars <- c("z_score", significant_bk_macro)
  bankruptcy_intersect_formula <- as.formula(paste0(
    "Surv(tstart, tstop, bankruptcy) ~ ", 
    paste(fallback_vars, collapse = " + "), 
    " + strata(gsector)"
  ))
  bankruptcy_intersect_vars <- fallback_vars
}

# 5. Financial-only model (for comparison)
bankruptcy_fin_only_vars <- bankruptcy_vars_step[bankruptcy_vars_step %in% financial_covariates]
bankruptcy_fin_only_formula <- as.formula(paste0(
  "Surv(tstart, tstop, bankruptcy) ~ ", 
  paste(bankruptcy_fin_only_vars, collapse = " + "), 
  " + strata(gsector)"
))

# 6. Macro-enriched model (financial variables + all significant macro)
bankruptcy_macro_enriched_vars <- unique(c(
  bankruptcy_fin_only_vars,  # Financial variables from stepwise
  significant_bk_macro       # All significant macro variables
))
bankruptcy_macro_enriched_formula <- as.formula(paste0(
  "Surv(tstart, tstop, bankruptcy) ~ ", 
  paste(bankruptcy_macro_enriched_vars, collapse = " + "), 
  " + strata(gsector)"
))

# Fit the models with Efron approximation for ties
cat("Fitting bankruptcy models with different variable sets...\n")
Fitting bankruptcy models with different variable sets...
# 1. Stepwise AIC model
bankruptcy_step_model <- coxph(bankruptcy_step_formula, data = data, ties = "efron")
cat("\n=== BANKRUPTCY MODEL: STEPWISE AIC SELECTION ===\n")

=== BANKRUPTCY MODEL: STEPWISE AIC SELECTION ===
print(summary(bankruptcy_step_model))
Call:
coxph(formula = bankruptcy_step_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                          coef exp(coef)  se(coef)      z Pr(>|z|)    
LTMTA                 4.093442 59.945877  0.516174  7.930 2.19e-15 ***
NIMTA                -2.003071  0.134920  0.403230 -4.968 6.78e-07 ***
CASHMTA              -2.129608  0.118884  0.942710 -2.259  0.02388 *  
PRICE                -0.311717  0.732188  0.061214 -5.092 3.54e-07 ***
MBE                  -0.141240  0.868281  0.081028 -1.743  0.08132 .  
debt_ratio           -0.195661  0.822291  0.287488 -0.681  0.49613    
debt_service          0.001114  1.001115  0.022548  0.049  0.96058    
current_ratio         0.085782  1.089569  0.040591  2.113  0.03457 *  
cash_to_assets        1.500593  4.484347  0.899430  1.668  0.09524 .  
wc_ratio             -1.015035  0.362390  0.317245 -3.200  0.00138 ** 
gp_margin             0.066733  1.069009  0.047396  1.408  0.15914    
asset_turnover       -0.164702  0.848147  0.094855 -1.736  0.08250 .  
receivables_turnover  0.001338  1.001339  0.001769  0.756  0.44947    
intangibility        -0.285753  0.751448  0.213919 -1.336  0.18161    
ebit_growth          -0.011717  0.988352  0.025372 -0.462  0.64423    
EBIT_VOL_3Y          -0.000883  0.999117  0.012927 -0.068  0.94554    
z_score               0.029200  1.029630  0.012606  2.316  0.02054 *  
gdp_deflator         -0.172516  0.841545  0.087396 -1.974  0.04839 *  
unemployement        -0.079620  0.923467  0.067868 -1.173  0.24074    
gdp_growth            0.099036  1.104107  0.056387  1.756  0.07902 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                  59.9459    0.01668  21.79674  164.8645
NIMTA                   0.1349    7.41178   0.06121    0.2974
CASHMTA                 0.1189    8.41157   0.01874    0.7543
PRICE                   0.7322    1.36577   0.64941    0.8255
MBE                     0.8683    1.15170   0.74078    1.0177
debt_ratio              0.8223    1.21611   0.46807    1.4446
debt_service            1.0011    0.99889   0.95784    1.0464
current_ratio           1.0896    0.91779   1.00624    1.1798
cash_to_assets          4.4843    0.22300   0.76931   26.1394
wc_ratio                0.3624    2.75946   0.19460    0.6749
gp_margin               1.0690    0.93545   0.97418    1.1731
asset_turnover          0.8481    1.17904   0.70426    1.0214
receivables_turnover    1.0013    0.99866   0.99787    1.0048
intangibility           0.7514    1.33076   0.49410    1.1428
ebit_growth             0.9884    1.01179   0.94040    1.0387
EBIT_VOL_3Y             0.9991    1.00088   0.97412    1.0248
z_score                 1.0296    0.97122   1.00450    1.0554
gdp_deflator            0.8415    1.18829   0.70906    0.9988
unemployement           0.9235    1.08288   0.80845    1.0549
gdp_growth              1.1041    0.90571   0.98859    1.2331

Concordance= 0.864  (se = 0.019 )
Likelihood ratio test= 436.5  on 20 df,   p=<2e-16
Wald test            = 394.9  on 20 df,   p=<2e-16
Score (logrank) test = 601.1  on 20 df,   p=<2e-16
# 2. LASSO model
bankruptcy_lasso_model <- coxph(bankruptcy_lasso_formula, data = data, ties = "efron")
cat("\n=== BANKRUPTCY MODEL: LASSO SELECTION ===\n")

=== BANKRUPTCY MODEL: LASSO SELECTION ===
print(summary(bankruptcy_lasso_model))
Call:
coxph(formula = bankruptcy_lasso_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                 coef exp(coef) se(coef)      z Pr(>|z|)    
LTMTA         4.07326  58.74817  0.37487 10.866  < 2e-16 ***
NIMTA        -1.46424   0.23125  0.34806 -4.207 2.59e-05 ***
PRICE        -0.30726   0.73546  0.05707 -5.384 7.29e-08 ***
gdp_deflator -0.15006   0.86066  0.08407 -1.785   0.0743 .  
gdp_growth    0.12371   1.13168  0.04977  2.486   0.0129 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

             exp(coef) exp(-coef) lower .95 upper .95
LTMTA          58.7482    0.01702   28.1775  122.4860
NIMTA           0.2313    4.32426    0.1169    0.4575
PRICE           0.7355    1.35970    0.6576    0.8225
gdp_deflator    0.8607    1.16190    0.7299    1.0148
gdp_growth      1.1317    0.88364    1.0265    1.2476

Concordance= 0.845  (se = 0.021 )
Likelihood ratio test= 393.6  on 5 df,   p=<2e-16
Wald test            = 345.3  on 5 df,   p=<2e-16
Score (logrank) test = 497.7  on 5 df,   p=<2e-16
# 3. Union model
bankruptcy_union_model <- coxph(bankruptcy_union_formula, data = data, ties = "efron")
cat("\n=== BANKRUPTCY MODEL: UNION OF STEPWISE & LASSO ===\n")

=== BANKRUPTCY MODEL: UNION OF STEPWISE & LASSO ===
print(summary(bankruptcy_union_model))
Call:
coxph(formula = bankruptcy_union_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                          coef exp(coef)  se(coef)      z Pr(>|z|)    
LTMTA                 4.093442 59.945877  0.516174  7.930 2.19e-15 ***
NIMTA                -2.003071  0.134920  0.403230 -4.968 6.78e-07 ***
CASHMTA              -2.129608  0.118884  0.942710 -2.259  0.02388 *  
PRICE                -0.311717  0.732188  0.061214 -5.092 3.54e-07 ***
MBE                  -0.141240  0.868281  0.081028 -1.743  0.08132 .  
debt_ratio           -0.195661  0.822291  0.287488 -0.681  0.49613    
debt_service          0.001114  1.001115  0.022548  0.049  0.96058    
current_ratio         0.085782  1.089569  0.040591  2.113  0.03457 *  
cash_to_assets        1.500593  4.484347  0.899430  1.668  0.09524 .  
wc_ratio             -1.015035  0.362390  0.317245 -3.200  0.00138 ** 
gp_margin             0.066733  1.069009  0.047396  1.408  0.15914    
asset_turnover       -0.164702  0.848147  0.094855 -1.736  0.08250 .  
receivables_turnover  0.001338  1.001339  0.001769  0.756  0.44947    
intangibility        -0.285753  0.751448  0.213919 -1.336  0.18161    
ebit_growth          -0.011717  0.988352  0.025372 -0.462  0.64423    
EBIT_VOL_3Y          -0.000883  0.999117  0.012927 -0.068  0.94554    
z_score               0.029200  1.029630  0.012606  2.316  0.02054 *  
gdp_deflator         -0.172516  0.841545  0.087396 -1.974  0.04839 *  
unemployement        -0.079620  0.923467  0.067868 -1.173  0.24074    
gdp_growth            0.099036  1.104107  0.056387  1.756  0.07902 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                  59.9459    0.01668  21.79674  164.8645
NIMTA                   0.1349    7.41178   0.06121    0.2974
CASHMTA                 0.1189    8.41157   0.01874    0.7543
PRICE                   0.7322    1.36577   0.64941    0.8255
MBE                     0.8683    1.15170   0.74078    1.0177
debt_ratio              0.8223    1.21611   0.46807    1.4446
debt_service            1.0011    0.99889   0.95784    1.0464
current_ratio           1.0896    0.91779   1.00624    1.1798
cash_to_assets          4.4843    0.22300   0.76931   26.1394
wc_ratio                0.3624    2.75946   0.19460    0.6749
gp_margin               1.0690    0.93545   0.97418    1.1731
asset_turnover          0.8481    1.17904   0.70426    1.0214
receivables_turnover    1.0013    0.99866   0.99787    1.0048
intangibility           0.7514    1.33076   0.49410    1.1428
ebit_growth             0.9884    1.01179   0.94040    1.0387
EBIT_VOL_3Y             0.9991    1.00088   0.97412    1.0248
z_score                 1.0296    0.97122   1.00450    1.0554
gdp_deflator            0.8415    1.18829   0.70906    0.9988
unemployement           0.9235    1.08288   0.80845    1.0549
gdp_growth              1.1041    0.90571   0.98859    1.2331

Concordance= 0.864  (se = 0.019 )
Likelihood ratio test= 436.5  on 20 df,   p=<2e-16
Wald test            = 394.9  on 20 df,   p=<2e-16
Score (logrank) test = 601.1  on 20 df,   p=<2e-16
# 4. Intersection model
bankruptcy_intersect_model <- coxph(bankruptcy_intersect_formula, data = data, ties = "efron")
cat("\n=== BANKRUPTCY MODEL: INTERSECTION OF STEPWISE & LASSO ===\n")

=== BANKRUPTCY MODEL: INTERSECTION OF STEPWISE & LASSO ===
print(summary(bankruptcy_intersect_model))
Call:
coxph(formula = bankruptcy_intersect_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                 coef exp(coef) se(coef)      z Pr(>|z|)    
LTMTA         4.07326  58.74817  0.37487 10.866  < 2e-16 ***
NIMTA        -1.46424   0.23125  0.34806 -4.207 2.59e-05 ***
PRICE        -0.30726   0.73546  0.05707 -5.384 7.29e-08 ***
gdp_deflator -0.15006   0.86066  0.08407 -1.785   0.0743 .  
gdp_growth    0.12371   1.13168  0.04977  2.486   0.0129 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

             exp(coef) exp(-coef) lower .95 upper .95
LTMTA          58.7482    0.01702   28.1775  122.4860
NIMTA           0.2313    4.32426    0.1169    0.4575
PRICE           0.7355    1.35970    0.6576    0.8225
gdp_deflator    0.8607    1.16190    0.7299    1.0148
gdp_growth      1.1317    0.88364    1.0265    1.2476

Concordance= 0.845  (se = 0.021 )
Likelihood ratio test= 393.6  on 5 df,   p=<2e-16
Wald test            = 345.3  on 5 df,   p=<2e-16
Score (logrank) test = 497.7  on 5 df,   p=<2e-16
# 5. Financial-only model
bankruptcy_fin_only_model <- coxph(bankruptcy_fin_only_formula, data = data, ties = "efron")
cat("\n=== BANKRUPTCY MODEL: FINANCIAL VARIABLES ONLY ===\n")

=== BANKRUPTCY MODEL: FINANCIAL VARIABLES ONLY ===
print(summary(bankruptcy_fin_only_model))
Call:
coxph(formula = bankruptcy_fin_only_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                           coef  exp(coef)   se(coef)      z Pr(>|z|)    
LTMTA                 4.1249753 61.8662806  0.5127014  8.046 8.58e-16 ***
NIMTA                -1.9951107  0.1359986  0.3982891 -5.009 5.47e-07 ***
CASHMTA              -2.2523872  0.1051479  0.9458578 -2.381  0.01725 *  
PRICE                -0.3132494  0.7310676  0.0606465 -5.165 2.40e-07 ***
MBE                  -0.1433073  0.8664878  0.0815467 -1.757  0.07886 .  
debt_ratio           -0.2255837  0.7980503  0.2858620 -0.789  0.43003    
debt_service         -0.0027800  0.9972239  0.0225030 -0.124  0.90168    
current_ratio         0.0903012  1.0945039  0.0405041  2.229  0.02579 *  
cash_to_assets        1.4364328  4.2056666  0.9027987  1.591  0.11159    
wc_ratio             -1.0024948  0.3669628  0.3172596 -3.160  0.00158 ** 
gp_margin             0.0619964  1.0639586  0.0455214  1.362  0.17322    
asset_turnover       -0.1504044  0.8603600  0.0941583 -1.597  0.11019    
receivables_turnover  0.0014753  1.0014764  0.0017743  0.831  0.40570    
intangibility        -0.2975916  0.7426045  0.2153915 -1.382  0.16708    
ebit_growth          -0.0112921  0.9887714  0.0254235 -0.444  0.65693    
EBIT_VOL_3Y          -0.0005538  0.9994464  0.0128319 -0.043  0.96558    
z_score               0.0290187  1.0294438  0.0126951  2.286  0.02226 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                  61.8663    0.01616  22.64862  168.9920
NIMTA                   0.1360    7.35302   0.06230    0.2969
CASHMTA                 0.1051    9.51041   0.01647    0.6713
PRICE                   0.7311    1.36786   0.64914    0.8233
MBE                     0.8665    1.15408   0.73850    1.0167
debt_ratio              0.7981    1.25305   0.45573    1.3975
debt_service            0.9972    1.00278   0.95420    1.0422
current_ratio           1.0945    0.91366   1.01097    1.1849
cash_to_assets          4.2057    0.23777   0.71676   24.6773
wc_ratio                0.3670    2.72507   0.19705    0.6834
gp_margin               1.0640    0.93989   0.97314    1.1632
asset_turnover          0.8604    1.16230   0.71537    1.0347
receivables_turnover    1.0015    0.99853   0.99800    1.0050
intangibility           0.7426    1.34661   0.48687    1.1327
ebit_growth             0.9888    1.01136   0.94071    1.0393
EBIT_VOL_3Y             0.9994    1.00055   0.97462    1.0249
z_score                 1.0294    0.97140   1.00415    1.0554

Concordance= 0.852  (se = 0.022 )
Likelihood ratio test= 426.8  on 17 df,   p=<2e-16
Wald test            = 387.1  on 17 df,   p=<2e-16
Score (logrank) test = 591.1  on 17 df,   p=<2e-16
# 6. Macro-enriched model
bankruptcy_macro_enriched_model <- coxph(bankruptcy_macro_enriched_formula, data = data, ties = "efron")
cat("\n=== BANKRUPTCY MODEL: FINANCIAL + SIGNIFICANT MACRO VARIABLES ===\n")

=== BANKRUPTCY MODEL: FINANCIAL + SIGNIFICANT MACRO VARIABLES ===
print(summary(bankruptcy_macro_enriched_model))
Call:
coxph(formula = bankruptcy_macro_enriched_formula, data = data, 
    ties = "efron")

  n= 63591, number of events= 176 

                           coef  exp(coef)   se(coef)      z Pr(>|z|)    
LTMTA                 4.0601837 57.9849601  0.5143528  7.894 2.93e-15 ***
NIMTA                -1.9442430  0.1430955  0.4016454 -4.841 1.29e-06 ***
CASHMTA              -2.1775400  0.1133200  0.9436560 -2.308  0.02102 *  
PRICE                -0.3149111  0.7298538  0.0609534 -5.166 2.39e-07 ***
MBE                  -0.1409420  0.8685397  0.0809882 -1.740  0.08181 .  
debt_ratio           -0.2136789  0.8076077  0.2861020 -0.747  0.45515    
debt_service         -0.0017857  0.9982159  0.0224728 -0.079  0.93667    
current_ratio         0.0878925  1.0918707  0.0405019  2.170  0.03000 *  
cash_to_assets        1.4301764  4.1794363  0.9005720  1.588  0.11227    
wc_ratio             -0.9951618  0.3696636  0.3174898 -3.134  0.00172 ** 
gp_margin             0.0629773  1.0650027  0.0455182  1.384  0.16649    
asset_turnover       -0.1551107  0.8563203  0.0946421 -1.639  0.10123    
receivables_turnover  0.0014722  1.0014733  0.0017740  0.830  0.40659    
intangibility        -0.2932034  0.7458704  0.2152748 -1.362  0.17320    
ebit_growth          -0.0125071  0.9875708  0.0254905 -0.491  0.62367    
EBIT_VOL_3Y          -0.0007252  0.9992751  0.0128396 -0.056  0.95496    
z_score               0.0289999  1.0294245  0.0126810  2.287  0.02220 *  
unemployement        -0.0898435  0.9140743  0.0569984 -1.576  0.11497    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                  57.9850    0.01725  21.15911  158.9034
NIMTA                   0.1431    6.98834   0.06512    0.3144
CASHMTA                 0.1133    8.82457   0.01783    0.7204
PRICE                   0.7299    1.37014   0.64767    0.8225
MBE                     0.8685    1.15136   0.74106    1.0180
debt_ratio              0.8076    1.23823   0.46097    1.4149
debt_service            0.9982    1.00179   0.95520    1.0432
current_ratio           1.0919    0.91586   1.00855    1.1821
cash_to_assets          4.1794    0.23927   0.71540   24.4166
wc_ratio                0.3697    2.70516   0.19841    0.6887
gp_margin               1.0650    0.93896   0.97410    1.1644
asset_turnover          0.8563    1.16779   0.71134    1.0309
receivables_turnover    1.0015    0.99853   0.99800    1.0050
intangibility           0.7459    1.34072   0.48913    1.1374
ebit_growth             0.9876    1.01259   0.93944    1.0382
EBIT_VOL_3Y             0.9993    1.00073   0.97444    1.0247
z_score                 1.0294    0.97142   1.00415    1.0553
unemployement           0.9141    1.09400   0.81746    1.0221

Concordance= 0.855  (se = 0.021 )
Likelihood ratio test= 429.4  on 18 df,   p=<2e-16
Wald test            = 390.6  on 18 df,   p=<2e-16
Score (logrank) test = 594.6  on 18 df,   p=<2e-16
# Compare models
cat("\nComparison of Bankruptcy Models:\n")

Comparison of Bankruptcy Models:
cat("--------------------------------------\n")
--------------------------------------
bankruptcy_models <- list(
  Stepwise = bankruptcy_step_model,
  LASSO = bankruptcy_lasso_model,
  Union = bankruptcy_union_model,
  Intersection = bankruptcy_intersect_model,
  Financial_Only = bankruptcy_fin_only_model,
  Macro_Enriched = bankruptcy_macro_enriched_model
)

bankruptcy_comparison <- data.frame(
  Model = names(bankruptcy_models),
  Fin_Vars = c(
    sum(bankruptcy_vars_step %in% financial_covariates),
    sum(bankruptcy_vars_lasso %in% financial_covariates),
    sum(bankruptcy_union_vars %in% financial_covariates),
    sum(bankruptcy_intersect_vars %in% financial_covariates),
    length(bankruptcy_fin_only_vars),
    sum(bankruptcy_macro_enriched_vars %in% financial_covariates)
  ),
  Macro_Vars = c(
    sum(bankruptcy_vars_step %in% macro_covariates),
    sum(bankruptcy_vars_lasso %in% macro_covariates),
    sum(bankruptcy_union_vars %in% macro_covariates),
    sum(bankruptcy_intersect_vars %in% macro_covariates),
    0,
    sum(bankruptcy_macro_enriched_vars %in% macro_covariates)
  ),
  Total_Vars = c(
    length(bankruptcy_vars_step),
    length(bankruptcy_vars_lasso),
    length(bankruptcy_union_vars),
    length(bankruptcy_intersect_vars),
    length(bankruptcy_fin_only_vars),
    length(bankruptcy_macro_enriched_vars)
  ),
  AIC = sapply(bankruptcy_models, AIC),
  BIC = sapply(bankruptcy_models, BIC),
  Concordance = sapply(bankruptcy_models, function(m) round(m$concordance["concordance"], 3)),
  LogLik = sapply(bankruptcy_models, function(m) round(m$loglik[2], 2))
)

print(bankruptcy_comparison)

# Identify best bankruptcy model based on AIC
best_bankruptcy_model_idx <- which.min(bankruptcy_comparison$AIC)
best_bankruptcy_model_name <- bankruptcy_comparison$Model[best_bankruptcy_model_idx]
best_bankruptcy_model <- bankruptcy_models[[best_bankruptcy_model_name]]

cat("\nBest bankruptcy model based on AIC:", best_bankruptcy_model_name, "\n")

Best bankruptcy model based on AIC: Stepwise 
# Calculate improvement over financial-only model
if(best_bankruptcy_model_name != "Financial_Only") {
  # Likelihood ratio test
  lrt <- anova(bankruptcy_fin_only_model, best_bankruptcy_model)
  cat("\nLikelihood ratio test comparing best model to financial-only model:\n")
  print(lrt)
  
  # Concordance improvement
  conc_improvement <- best_bankruptcy_model$concordance["concordance"] - 
                    bankruptcy_fin_only_model$concordance["concordance"]
  cat("\nConcordance improvement over financial-only model:", round(conc_improvement, 4), "\n")
}

Likelihood ratio test comparing best model to financial-only model:
Analysis of Deviance Table
 Cox model: response is  Surv(tstart, tstop, bankruptcy)
 Model 1: ~ LTMTA + NIMTA + CASHMTA + PRICE + MBE + debt_ratio + debt_service + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + intangibility + ebit_growth + EBIT_VOL_3Y + z_score + strata(gsector)
 Model 2: ~ LTMTA + NIMTA + CASHMTA + PRICE + MBE + debt_ratio + debt_service + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + intangibility + ebit_growth + EBIT_VOL_3Y + z_score + gdp_deflator + unemployement + gdp_growth + strata(gsector)
   loglik  Chisq Df Pr(>|Chi|)  
1 -783.06                       
2 -778.20 9.7047  3    0.02125 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Concordance improvement over financial-only model: 0.0117 
cat("This model will be used for subsequent analysis.\n")
This model will be used for subsequent analysis.

Same as above, we fit the acquisition models using the selected variables from both stepwise and LASSO methods. We also create a financial-only model and a macro-enriched model for comparison. We evaluate the improvement of the models using likelihood ratio tests and compare their concordance (C-index).

#-------------------------------------------------------------
# 8.2: ACQUISITION MODELS
#-------------------------------------------------------------
cat("\n8.2: ACQUISITION PREDICTION MODELS\n")

8.2: ACQUISITION PREDICTION MODELS
cat("--------------------------------------\n")
--------------------------------------
# Create formulas with different sets of selected variables
# 1. Stepwise AIC selection variables
acquisition_step_formula <- as.formula(paste0(
  "Surv(tstart, tstop, acquisition) ~ ", 
  paste(acquisition_vars_step, collapse = " + "), 
  " + strata(gsector)"
))

# 2. LASSO selection variables
acquisition_lasso_formula <- as.formula(paste0(
  "Surv(tstart, tstop, acquisition) ~ ", 
  paste(acquisition_vars_lasso, collapse = " + "), 
  " + strata(gsector)"
))

# 3. Union of both methods (comprehensive approach)
acquisition_union_vars <- unique(c(acquisition_vars_step, acquisition_vars_lasso))
acquisition_union_formula <- as.formula(paste0(
  "Surv(tstart, tstop, acquisition) ~ ", 
  paste(acquisition_union_vars, collapse = " + "), 
  " + strata(gsector)"
))

# 4. Intersection of both methods (conservative approach)
acquisition_intersect_vars <- intersect(acquisition_vars_step, acquisition_vars_lasso)
# Check if intersection is not empty
if(length(acquisition_intersect_vars) > 0) {
  acquisition_intersect_formula <- as.formula(paste0(
    "Surv(tstart, tstop, acquisition) ~ ", 
    paste(acquisition_intersect_vars, collapse = " + "), 
    " + strata(gsector)"
  ))
} else {
  # If intersection is empty, use key financial and significant macro vars as fallback
  cat("No variables in common between stepwise and LASSO for acquisition. Using key financial and significant macro variables as fallback.\n")
  key_fin_vars <- intersect(c("RSIZE", "LTMTA", "NIMTA"), financial_covariates)
  fallback_vars <- c(key_fin_vars, significant_acq_macro)
  acquisition_intersect_formula <- as.formula(paste0(
    "Surv(tstart, tstop, acquisition) ~ ", 
    paste(fallback_vars, collapse = " + "), 
    " + strata(gsector)"
  ))
  acquisition_intersect_vars <- fallback_vars
}

# 5. Financial-only model (for comparison)
acquisition_fin_only_vars <- acquisition_vars_step[acquisition_vars_step %in% financial_covariates]
acquisition_fin_only_formula <- as.formula(paste0(
  "Surv(tstart, tstop, acquisition) ~ ", 
  paste(acquisition_fin_only_vars, collapse = " + "), 
  " + strata(gsector)"
))

# 6. Macro-enriched model (financial variables + all significant macro)
acquisition_macro_enriched_vars <- unique(c(
  acquisition_fin_only_vars,  # Financial variables from stepwise
  significant_acq_macro       # All significant macro variables
))
acquisition_macro_enriched_formula <- as.formula(paste0(
  "Surv(tstart, tstop, acquisition) ~ ", 
  paste(acquisition_macro_enriched_vars, collapse = " + "), 
  " + strata(gsector)"
))

# Fit the models with Efron approximation for ties
cat("Fitting acquisition models with different variable sets...\n")
Fitting acquisition models with different variable sets...
# 1. Stepwise AIC model
acquisition_step_model <- coxph(acquisition_step_formula, data = data, ties = "efron")
cat("\n=== ACQUISITION MODEL: STEPWISE AIC SELECTION ===\n")

=== ACQUISITION MODEL: STEPWISE AIC SELECTION ===
print(summary(acquisition_step_model))
Call:
coxph(formula = acquisition_step_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                           coef  exp(coef)   se(coef)      z Pr(>|z|)    
LTMTA                -0.0692224  0.9331191  0.1873853 -0.369  0.71182    
NIMTA                 0.0521912  1.0535772  0.2035668  0.256  0.79765    
CASHMTA              -0.0246983  0.9756042  0.2677814 -0.092  0.92651    
PRICE                 0.1292039  1.1379221  0.0203765  6.341 2.29e-10 ***
MBE                  -0.2475084  0.7807436  0.0293828 -8.424  < 2e-16 ***
debt_ratio            0.0648998  1.0670521  0.1420247  0.457  0.64770    
debt_service         -0.0084962  0.9915398  0.0107977 -0.787  0.43137    
current_ratio        -0.0801206  0.9230050  0.0161442 -4.963 6.95e-07 ***
cash_to_assets        0.7240280  2.0627252  0.2260495  3.203  0.00136 ** 
wc_ratio             -0.2478513  0.7804760  0.1588320 -1.560  0.11865    
gp_margin             0.0250246  1.0253404  0.0090720  2.758  0.00581 ** 
asset_turnover       -0.0591702  0.9425463  0.0339169 -1.745  0.08106 .  
receivables_turnover -0.0033603  0.9966454  0.0009792 -3.432  0.00060 ***
intangibility        -0.0939995  0.9102832  0.0708801 -1.326  0.18478    
ebit_growth          -0.0047098  0.9953013  0.0088930 -0.530  0.59639    
EBIT_VOL_3Y          -0.0075151  0.9925131  0.0042030 -1.788  0.07377 .  
z_score               0.0065889  1.0066107  0.0062954  1.047  0.29527    
gdp_deflator         -0.0448223  0.9561674  0.0222194 -2.017  0.04367 *  
unemployement        -0.0273004  0.9730689  0.0170472 -1.601  0.10927    
gdp_growth            0.0380981  1.0388331  0.0149766  2.544  0.01096 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                   0.9331     1.0717    0.6463    1.3472
NIMTA                   1.0536     0.9491    0.7070    1.5702
CASHMTA                 0.9756     1.0250    0.5772    1.6490
PRICE                   1.1379     0.8788    1.0934    1.1843
MBE                     0.7807     1.2808    0.7371    0.8270
debt_ratio              1.0671     0.9372    0.8078    1.4095
debt_service            0.9915     1.0085    0.9708    1.0127
current_ratio           0.9230     1.0834    0.8943    0.9527
cash_to_assets          2.0627     0.4848    1.3244    3.2126
wc_ratio                0.7805     1.2813    0.5717    1.0655
gp_margin               1.0253     0.9753    1.0073    1.0437
asset_turnover          0.9425     1.0610    0.8819    1.0073
receivables_turnover    0.9966     1.0034    0.9947    0.9986
intangibility           0.9103     1.0986    0.7922    1.0459
ebit_growth             0.9953     1.0047    0.9781    1.0128
EBIT_VOL_3Y             0.9925     1.0075    0.9844    1.0007
z_score                 1.0066     0.9934    0.9943    1.0191
gdp_deflator            0.9562     1.0458    0.9154    0.9987
unemployement           0.9731     1.0277    0.9411    1.0061
gdp_growth              1.0388     0.9626    1.0088    1.0698

Concordance= 0.638  (se = 0.007 )
Likelihood ratio test= 411.6  on 20 df,   p=<2e-16
Wald test            = 261.9  on 20 df,   p=<2e-16
Score (logrank) test = 275.2  on 20 df,   p=<2e-16
# 2. LASSO model
acquisition_lasso_model <- coxph(acquisition_lasso_formula, data = data, ties = "efron")
cat("\n=== ACQUISITION MODEL: LASSO SELECTION ===\n")

=== ACQUISITION MODEL: LASSO SELECTION ===
print(summary(acquisition_lasso_model))
Call:
coxph(formula = acquisition_lasso_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                           coef  exp(coef)   se(coef)       z Pr(>|z|)    
LTMTA                -0.0262261  0.9741148  0.1823286  -0.144 0.885627    
NIMTA                 0.1248595  1.1329892  0.1889172   0.661 0.508663    
PRICE                 0.1280657  1.1366276  0.0202920   6.311 2.77e-10 ***
MBE                  -0.2302044  0.7943712  0.0225472 -10.210  < 2e-16 ***
debt_ratio            0.0116288  1.0116967  0.1278588   0.091 0.927532    
current_ratio        -0.0722919  0.9302593  0.0137555  -5.256 1.48e-07 ***
cash_to_assets        0.7196705  2.0537565  0.1629356   4.417 1.00e-05 ***
wc_ratio             -0.2455432  0.7822795  0.1545175  -1.589 0.112039    
gp_margin             0.0254789  1.0258062  0.0090435   2.817 0.004842 ** 
asset_turnover       -0.0474881  0.9536219  0.0325823  -1.457 0.144983    
receivables_turnover -0.0032994  0.9967060  0.0009764  -3.379 0.000727 ***
EBIT_VOL_3Y          -0.0073213  0.9927054  0.0041959  -1.745 0.081005 .  
gdp_deflator         -0.0450158  0.9559824  0.0222042  -2.027 0.042626 *  
unemployement        -0.0275309  0.9728446  0.0170334  -1.616 0.106032    
gdp_growth            0.0381395  1.0388762  0.0149693   2.548 0.010839 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                   0.9741     1.0266    0.6814    1.3925
NIMTA                   1.1330     0.8826    0.7824    1.6407
PRICE                   1.1366     0.8798    1.0923    1.1827
MBE                     0.7944     1.2589    0.7600    0.8303
debt_ratio              1.0117     0.9884    0.7874    1.2998
current_ratio           0.9303     1.0750    0.9055    0.9557
cash_to_assets          2.0538     0.4869    1.4923    2.8264
wc_ratio                0.7823     1.2783    0.5779    1.0590
gp_margin               1.0258     0.9748    1.0078    1.0442
asset_turnover          0.9536     1.0486    0.8946    1.0165
receivables_turnover    0.9967     1.0033    0.9948    0.9986
EBIT_VOL_3Y             0.9927     1.0073    0.9846    1.0009
gdp_deflator            0.9560     1.0460    0.9153    0.9985
unemployement           0.9728     1.0279    0.9409    1.0059
gdp_growth              1.0389     0.9626    1.0088    1.0698

Concordance= 0.638  (se = 0.007 )
Likelihood ratio test= 407.7  on 15 df,   p=<2e-16
Wald test            = 260.8  on 15 df,   p=<2e-16
Score (logrank) test = 260.6  on 15 df,   p=<2e-16
# 3. Union model
acquisition_union_model <- coxph(acquisition_union_formula, data = data, ties = "efron")
cat("\n=== ACQUISITION MODEL: UNION OF STEPWISE & LASSO ===\n")

=== ACQUISITION MODEL: UNION OF STEPWISE & LASSO ===
print(summary(acquisition_union_model))
Call:
coxph(formula = acquisition_union_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                           coef  exp(coef)   se(coef)      z Pr(>|z|)    
LTMTA                -0.0692224  0.9331191  0.1873853 -0.369  0.71182    
NIMTA                 0.0521912  1.0535772  0.2035668  0.256  0.79765    
CASHMTA              -0.0246983  0.9756042  0.2677814 -0.092  0.92651    
PRICE                 0.1292039  1.1379221  0.0203765  6.341 2.29e-10 ***
MBE                  -0.2475084  0.7807436  0.0293828 -8.424  < 2e-16 ***
debt_ratio            0.0648998  1.0670521  0.1420247  0.457  0.64770    
debt_service         -0.0084962  0.9915398  0.0107977 -0.787  0.43137    
current_ratio        -0.0801206  0.9230050  0.0161442 -4.963 6.95e-07 ***
cash_to_assets        0.7240280  2.0627252  0.2260495  3.203  0.00136 ** 
wc_ratio             -0.2478513  0.7804760  0.1588320 -1.560  0.11865    
gp_margin             0.0250246  1.0253404  0.0090720  2.758  0.00581 ** 
asset_turnover       -0.0591702  0.9425463  0.0339169 -1.745  0.08106 .  
receivables_turnover -0.0033603  0.9966454  0.0009792 -3.432  0.00060 ***
intangibility        -0.0939995  0.9102832  0.0708801 -1.326  0.18478    
ebit_growth          -0.0047098  0.9953013  0.0088930 -0.530  0.59639    
EBIT_VOL_3Y          -0.0075151  0.9925131  0.0042030 -1.788  0.07377 .  
z_score               0.0065889  1.0066107  0.0062954  1.047  0.29527    
gdp_deflator         -0.0448223  0.9561674  0.0222194 -2.017  0.04367 *  
unemployement        -0.0273004  0.9730689  0.0170472 -1.601  0.10927    
gdp_growth            0.0380981  1.0388331  0.0149766  2.544  0.01096 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                   0.9331     1.0717    0.6463    1.3472
NIMTA                   1.0536     0.9491    0.7070    1.5702
CASHMTA                 0.9756     1.0250    0.5772    1.6490
PRICE                   1.1379     0.8788    1.0934    1.1843
MBE                     0.7807     1.2808    0.7371    0.8270
debt_ratio              1.0671     0.9372    0.8078    1.4095
debt_service            0.9915     1.0085    0.9708    1.0127
current_ratio           0.9230     1.0834    0.8943    0.9527
cash_to_assets          2.0627     0.4848    1.3244    3.2126
wc_ratio                0.7805     1.2813    0.5717    1.0655
gp_margin               1.0253     0.9753    1.0073    1.0437
asset_turnover          0.9425     1.0610    0.8819    1.0073
receivables_turnover    0.9966     1.0034    0.9947    0.9986
intangibility           0.9103     1.0986    0.7922    1.0459
ebit_growth             0.9953     1.0047    0.9781    1.0128
EBIT_VOL_3Y             0.9925     1.0075    0.9844    1.0007
z_score                 1.0066     0.9934    0.9943    1.0191
gdp_deflator            0.9562     1.0458    0.9154    0.9987
unemployement           0.9731     1.0277    0.9411    1.0061
gdp_growth              1.0388     0.9626    1.0088    1.0698

Concordance= 0.638  (se = 0.007 )
Likelihood ratio test= 411.6  on 20 df,   p=<2e-16
Wald test            = 261.9  on 20 df,   p=<2e-16
Score (logrank) test = 275.2  on 20 df,   p=<2e-16
# 4. Intersection model
acquisition_intersect_model <- coxph(acquisition_intersect_formula, data = data, ties = "efron")
cat("\n=== ACQUISITION MODEL: INTERSECTION OF STEPWISE & LASSO ===\n")

=== ACQUISITION MODEL: INTERSECTION OF STEPWISE & LASSO ===
print(summary(acquisition_intersect_model))
Call:
coxph(formula = acquisition_intersect_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                           coef  exp(coef)   se(coef)       z Pr(>|z|)    
LTMTA                -0.0262261  0.9741148  0.1823286  -0.144 0.885627    
NIMTA                 0.1248595  1.1329892  0.1889172   0.661 0.508663    
PRICE                 0.1280657  1.1366276  0.0202920   6.311 2.77e-10 ***
MBE                  -0.2302044  0.7943712  0.0225472 -10.210  < 2e-16 ***
debt_ratio            0.0116288  1.0116967  0.1278588   0.091 0.927532    
current_ratio        -0.0722919  0.9302593  0.0137555  -5.256 1.48e-07 ***
cash_to_assets        0.7196705  2.0537565  0.1629356   4.417 1.00e-05 ***
wc_ratio             -0.2455432  0.7822795  0.1545175  -1.589 0.112039    
gp_margin             0.0254789  1.0258062  0.0090435   2.817 0.004842 ** 
asset_turnover       -0.0474881  0.9536219  0.0325823  -1.457 0.144983    
receivables_turnover -0.0032994  0.9967060  0.0009764  -3.379 0.000727 ***
EBIT_VOL_3Y          -0.0073213  0.9927054  0.0041959  -1.745 0.081005 .  
gdp_deflator         -0.0450158  0.9559824  0.0222042  -2.027 0.042626 *  
unemployement        -0.0275309  0.9728446  0.0170334  -1.616 0.106032    
gdp_growth            0.0381395  1.0388762  0.0149693   2.548 0.010839 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                   0.9741     1.0266    0.6814    1.3925
NIMTA                   1.1330     0.8826    0.7824    1.6407
PRICE                   1.1366     0.8798    1.0923    1.1827
MBE                     0.7944     1.2589    0.7600    0.8303
debt_ratio              1.0117     0.9884    0.7874    1.2998
current_ratio           0.9303     1.0750    0.9055    0.9557
cash_to_assets          2.0538     0.4869    1.4923    2.8264
wc_ratio                0.7823     1.2783    0.5779    1.0590
gp_margin               1.0258     0.9748    1.0078    1.0442
asset_turnover          0.9536     1.0486    0.8946    1.0165
receivables_turnover    0.9967     1.0033    0.9948    0.9986
EBIT_VOL_3Y             0.9927     1.0073    0.9846    1.0009
gdp_deflator            0.9560     1.0460    0.9153    0.9985
unemployement           0.9728     1.0279    0.9409    1.0059
gdp_growth              1.0389     0.9626    1.0088    1.0698

Concordance= 0.638  (se = 0.007 )
Likelihood ratio test= 407.7  on 15 df,   p=<2e-16
Wald test            = 260.8  on 15 df,   p=<2e-16
Score (logrank) test = 260.6  on 15 df,   p=<2e-16
# 5. Financial-only model
acquisition_fin_only_model <- coxph(acquisition_fin_only_formula, data = data, ties = "efron")
cat("\n=== ACQUISITION MODEL: FINANCIAL VARIABLES ONLY ===\n")

=== ACQUISITION MODEL: FINANCIAL VARIABLES ONLY ===
print(summary(acquisition_fin_only_model))
Call:
coxph(formula = acquisition_fin_only_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                           coef  exp(coef)   se(coef)      z Pr(>|z|)    
LTMTA                -0.0679723  0.9342864  0.1869893 -0.364 0.716225    
NIMTA                 0.0470879  1.0482142  0.2026759  0.232 0.816281    
CASHMTA              -0.0430935  0.9578218  0.2681345 -0.161 0.872317    
PRICE                 0.1286844  1.1373312  0.0203225  6.332 2.42e-10 ***
MBE                  -0.2462611  0.7817181  0.0292530 -8.418  < 2e-16 ***
debt_ratio            0.0579507  1.0596627  0.1415875  0.409 0.682325    
debt_service         -0.0085791  0.9914575  0.0108054 -0.794 0.427212    
current_ratio        -0.0795144  0.9235647  0.0161376 -4.927 8.34e-07 ***
cash_to_assets        0.6841462  1.9820789  0.2259743  3.028 0.002466 ** 
wc_ratio             -0.2347706  0.7907522  0.1589516 -1.477 0.139677    
gp_margin             0.0246483  1.0249546  0.0090474  2.724 0.006443 ** 
asset_turnover       -0.0549353  0.9465463  0.0338395 -1.623 0.104502    
receivables_turnover -0.0034371  0.9965688  0.0009838 -3.494 0.000476 ***
intangibility        -0.0982700  0.9064042  0.0714710 -1.375 0.169143    
ebit_growth          -0.0037420  0.9962649  0.0089013 -0.420 0.674197    
EBIT_VOL_3Y          -0.0073760  0.9926511  0.0041951 -1.758 0.078707 .  
z_score               0.0067290  1.0067517  0.0062627  1.074 0.282620    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                   0.9343     1.0703    0.6476    1.3479
NIMTA                   1.0482     0.9540    0.7046    1.5594
CASHMTA                 0.9578     1.0440    0.5663    1.6200
PRICE                   1.1373     0.8793    1.0929    1.1835
MBE                     0.7817     1.2792    0.7382    0.8278
debt_ratio              1.0597     0.9437    0.8029    1.3986
debt_service            0.9915     1.0086    0.9707    1.0127
current_ratio           0.9236     1.0828    0.8948    0.9532
cash_to_assets          1.9821     0.5045    1.2728    3.0865
wc_ratio                0.7908     1.2646    0.5791    1.0798
gp_margin               1.0250     0.9757    1.0069    1.0433
asset_turnover          0.9465     1.0565    0.8858    1.0115
receivables_turnover    0.9966     1.0034    0.9946    0.9985
intangibility           0.9064     1.1033    0.7879    1.0427
ebit_growth             0.9963     1.0037    0.9790    1.0138
EBIT_VOL_3Y             0.9927     1.0074    0.9845    1.0008
z_score                 1.0068     0.9933    0.9945    1.0192

Concordance= 0.633  (se = 0.007 )
Likelihood ratio test= 396.2  on 17 df,   p=<2e-16
Wald test            = 247  on 17 df,   p=<2e-16
Score (logrank) test = 260.8  on 17 df,   p=<2e-16
# 6. Macro-enriched model
acquisition_macro_enriched_model <- coxph(acquisition_macro_enriched_formula, data = data, ties = "efron")
cat("\n=== ACQUISITION MODEL: FINANCIAL + SIGNIFICANT MACRO VARIABLES ===\n")

=== ACQUISITION MODEL: FINANCIAL + SIGNIFICANT MACRO VARIABLES ===
print(summary(acquisition_macro_enriched_model))
Call:
coxph(formula = acquisition_macro_enriched_formula, data = data, 
    ties = "efron")

  n= 63591, number of events= 2154 

                          coef exp(coef)  se(coef)      z Pr(>|z|)    
LTMTA                -0.064384  0.937645  0.187325 -0.344 0.731069    
NIMTA                 0.062030  1.063994  0.203473  0.305 0.760476    
CASHMTA              -0.034316  0.966267  0.267815 -0.128 0.898045    
PRICE                 0.127374  1.135841  0.020327  6.266 3.70e-10 ***
MBE                  -0.247614  0.780661  0.029346 -8.438  < 2e-16 ***
debt_ratio            0.056037  1.057637  0.141964  0.395 0.693045    
debt_service         -0.008332  0.991703  0.010797 -0.772 0.440300    
current_ratio        -0.080000  0.923116  0.016136 -4.958 7.13e-07 ***
cash_to_assets        0.716034  2.046301  0.226062  3.167 0.001538 ** 
wc_ratio             -0.245810  0.782071  0.158921 -1.547 0.121924    
gp_margin             0.024921  1.025235  0.009055  2.752 0.005917 ** 
asset_turnover       -0.056613  0.944960  0.033866 -1.672 0.094589 .  
receivables_turnover -0.003389  0.996616  0.000980 -3.459 0.000543 ***
intangibility        -0.094923  0.909443  0.071119 -1.335 0.181972    
ebit_growth          -0.004772  0.995239  0.008897 -0.536 0.591671    
EBIT_VOL_3Y          -0.007626  0.992403  0.004205 -1.814 0.069723 .  
z_score               0.006635  1.006657  0.006285  1.056 0.291122    
unemployement        -0.017525  0.982628  0.016319 -1.074 0.282859    
gdp_growth            0.032548  1.033083  0.014639  2.223 0.026190 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                   0.9376     1.0665    0.6495    1.3536
NIMTA                   1.0640     0.9399    0.7141    1.5854
CASHMTA                 0.9663     1.0349    0.5717    1.6333
PRICE                   1.1358     0.8804    1.0915    1.1820
MBE                     0.7807     1.2810    0.7370    0.8269
debt_ratio              1.0576     0.9455    0.8007    1.3969
debt_service            0.9917     1.0084    0.9709    1.0129
current_ratio           0.9231     1.0833    0.8944    0.9528
cash_to_assets          2.0463     0.4887    1.3138    3.1871
wc_ratio                0.7821     1.2787    0.5728    1.0679
gp_margin               1.0252     0.9754    1.0072    1.0436
asset_turnover          0.9450     1.0582    0.8843    1.0098
receivables_turnover    0.9966     1.0034    0.9947    0.9985
intangibility           0.9094     1.0996    0.7911    1.0455
ebit_growth             0.9952     1.0048    0.9780    1.0127
EBIT_VOL_3Y             0.9924     1.0077    0.9843    1.0006
z_score                 1.0067     0.9934    0.9943    1.0191
unemployement           0.9826     1.0177    0.9517    1.0146
gdp_growth              1.0331     0.9680    1.0039    1.0632

Concordance= 0.637  (se = 0.007 )
Likelihood ratio test= 407.4  on 19 df,   p=<2e-16
Wald test            = 257.8  on 19 df,   p=<2e-16
Score (logrank) test = 271.2  on 19 df,   p=<2e-16
# Compare models
cat("\nComparison of Acquisition Models:\n")

Comparison of Acquisition Models:
cat("--------------------------------------\n")
--------------------------------------
acquisition_models <- list(
  Stepwise = acquisition_step_model,
  LASSO = acquisition_lasso_model,
  Union = acquisition_union_model,
  Intersection = acquisition_intersect_model,
  Financial_Only = acquisition_fin_only_model,
  Macro_Enriched = acquisition_macro_enriched_model
)

acquisition_comparison <- data.frame(
  Model = names(acquisition_models),
  Fin_Vars = c(
    sum(acquisition_vars_step %in% financial_covariates),
    sum(acquisition_vars_lasso %in% financial_covariates),
    sum(acquisition_union_vars %in% financial_covariates),
    sum(acquisition_intersect_vars %in% financial_covariates),
    length(acquisition_fin_only_vars),
    sum(acquisition_macro_enriched_vars %in% financial_covariates)
  ),
  Macro_Vars = c(
    sum(acquisition_vars_step %in% macro_covariates),
    sum(acquisition_vars_lasso %in% macro_covariates),
    sum(acquisition_union_vars %in% macro_covariates),
    sum(acquisition_intersect_vars %in% macro_covariates),
    0,
    sum(acquisition_macro_enriched_vars %in% macro_covariates)
  ),
  Total_Vars = c(
    length(acquisition_vars_step),
    length(acquisition_vars_lasso),
    length(acquisition_union_vars),
    length(acquisition_intersect_vars),
    length(acquisition_fin_only_vars),
    length(acquisition_macro_enriched_vars)
  ),
  AIC = sapply(acquisition_models, AIC),
  BIC = sapply(acquisition_models, BIC),
  Concordance = sapply(acquisition_models, function(m) round(m$concordance["concordance"], 3)),
  LogLik = sapply(acquisition_models, function(m) round(m$loglik[2], 2))
)

print(acquisition_comparison)

# Identify best acquisition model based on AIC
best_acquisition_model_idx <- which.min(acquisition_comparison$AIC)
best_acquisition_model_name <- acquisition_comparison$Model[best_acquisition_model_idx]
best_acquisition_model <- acquisition_models[[best_acquisition_model_name]]

cat("\nBest acquisition model based on AIC:", best_acquisition_model_name, "\n")

Best acquisition model based on AIC: LASSO 
# Calculate improvement over financial-only model
if(best_acquisition_model_name != "Financial_Only") {
  # Likelihood ratio test
  lrt <- anova(acquisition_fin_only_model, best_acquisition_model)
  cat("\nLikelihood ratio test comparing best model to financial-only model:\n")
  print(lrt)
  
  # Concordance improvement
  conc_improvement <- best_acquisition_model$concordance["concordance"] - 
                    acquisition_fin_only_model$concordance["concordance"]
  cat("\nConcordance improvement over financial-only model:", round(conc_improvement, 4), "\n")
}

Likelihood ratio test comparing best model to financial-only model:
Analysis of Deviance Table
 Cox model: response is  Surv(tstart, tstop, acquisition)
 Model 1: ~ LTMTA + NIMTA + CASHMTA + PRICE + MBE + debt_ratio + debt_service + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + intangibility + ebit_growth + EBIT_VOL_3Y + z_score + strata(gsector)
 Model 2: ~ LTMTA + NIMTA + PRICE + MBE + debt_ratio + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + EBIT_VOL_3Y + gdp_deflator + unemployement + gdp_growth + strata(gsector)
  loglik  Chisq Df Pr(>|Chi|)   
1 -12138                        
2 -12132 11.505  2   0.003174 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Concordance improvement over financial-only model: 0.0053 
cat("This model will be used for subsequent analysis.\n")
This model will be used for subsequent analysis.
# Store the best models and their variables for next steps
best_models <- list(
  bankruptcy = list(
    model = best_bankruptcy_model,
    name = best_bankruptcy_model_name,
    variables = switch(best_bankruptcy_model_name,
                     "Stepwise" = bankruptcy_vars_step,
                     "LASSO" = bankruptcy_vars_lasso,
                     "Union" = bankruptcy_union_vars,
                     "Intersection" = bankruptcy_intersect_vars,
                     "Financial_Only" = bankruptcy_fin_only_vars,
                     "Macro_Enriched" = bankruptcy_macro_enriched_vars),
    fin_vars = switch(best_bankruptcy_model_name,
                     "Stepwise" = bankruptcy_vars_step[bankruptcy_vars_step %in% financial_covariates],
                     "LASSO" = bankruptcy_vars_lasso[bankruptcy_vars_lasso %in% financial_covariates],
                     "Union" = bankruptcy_union_vars[bankruptcy_union_vars %in% financial_covariates],
                     "Intersection" = bankruptcy_intersect_vars[bankruptcy_intersect_vars %in% financial_covariates],
                     "Financial_Only" = bankruptcy_fin_only_vars,
                     "Macro_Enriched" = bankruptcy_macro_enriched_vars[bankruptcy_macro_enriched_vars %in% financial_covariates]),
    macro_vars = switch(best_bankruptcy_model_name,
                      "Stepwise" = bankruptcy_vars_step[bankruptcy_vars_step %in% macro_covariates],
                      "LASSO" = bankruptcy_vars_lasso[bankruptcy_vars_lasso %in% macro_covariates],
                      "Union" = bankruptcy_union_vars[bankruptcy_union_vars %in% macro_covariates],
                      "Intersection" = bankruptcy_intersect_vars[bankruptcy_intersect_vars %in% macro_covariates],
                      "Financial_Only" = character(0),
                      "Macro_Enriched" = bankruptcy_macro_enriched_vars[bankruptcy_macro_enriched_vars %in% macro_covariates])
  ),
  acquisition = list(
    model = best_acquisition_model,
    name = best_acquisition_model_name,
    variables = switch(best_acquisition_model_name,
                     "Stepwise" = acquisition_vars_step,
                     "LASSO" = acquisition_vars_lasso,
                     "Union" = acquisition_union_vars,
                     "Intersection" = acquisition_intersect_vars,
                     "Financial_Only" = acquisition_fin_only_vars,
                     "Macro_Enriched" = acquisition_macro_enriched_vars),
    fin_vars = switch(best_acquisition_model_name,
                     "Stepwise" = acquisition_vars_step[acquisition_vars_step %in% financial_covariates],
                     "LASSO" = acquisition_vars_lasso[acquisition_vars_lasso %in% financial_covariates],
                     "Union" = acquisition_union_vars[acquisition_union_vars %in% financial_covariates],
                     "Intersection" = acquisition_intersect_vars[acquisition_intersect_vars %in% financial_covariates],
                     "Financial_Only" = acquisition_fin_only_vars,
                     "Macro_Enriched" = acquisition_macro_enriched_vars[acquisition_macro_enriched_vars %in% financial_covariates]),
    macro_vars = switch(best_acquisition_model_name,
                      "Stepwise" = acquisition_vars_step[acquisition_vars_step %in% macro_covariates],
                      "LASSO" = acquisition_vars_lasso[acquisition_vars_lasso %in% macro_covariates],
                      "Union" = acquisition_union_vars[acquisition_union_vars %in% macro_covariates],
                      "Intersection" = acquisition_intersect_vars[acquisition_intersect_vars %in% macro_covariates],
                      "Financial_Only" = character(0),
                      "Macro_Enriched" = acquisition_macro_enriched_vars[acquisition_macro_enriched_vars %in% macro_covariates])
  )
)

# Print a summary of macro variables in the best models
cat("\n=== MACRO VARIABLES IN BEST MODELS ===\n")

=== MACRO VARIABLES IN BEST MODELS ===
cat("Bankruptcy model (", best_models$bankruptcy$name, "):\n", sep="")
Bankruptcy model (Stepwise):
if(length(best_models$bankruptcy$macro_vars) > 0) {
  cat("- Macro variables:", paste(best_models$bankruptcy$macro_vars, collapse=", "), "\n")
} else {
  cat("- No macro variables in this model\n")
}
- Macro variables: gdp_deflator, unemployement, gdp_growth 
cat("\nAcquisition model (", best_models$acquisition$name, "):\n", sep="")

Acquisition model (LASSO):
if(length(best_models$acquisition$macro_vars) > 0) {
  cat("- Macro variables:", paste(best_models$acquisition$macro_vars, collapse=", "), "\n")
} else {
  cat("- No macro variables in this model\n")
}
- Macro variables: gdp_deflator, unemployement, gdp_growth 

An important step in survival analysis is to check the proportional hazards assumption. We will use Schoenfeld residuals to test this assumption for both the bankruptcy and acquisition models. We will also visualize the residuals for financial and macro variables to identify any violations of the assumption.

#-------------------------------------------------------------
# STEP 9: Proportional Hazards Assumption Check
#-------------------------------------------------------------
cat("\n=== STEP 9: TESTING PROPORTIONAL HAZARDS ASSUMPTION WITH FOCUS ON MACRO VARIABLES ===\n")

=== STEP 9: TESTING PROPORTIONAL HAZARDS ASSUMPTION WITH FOCUS ON MACRO VARIABLES ===
#-------------------------------------------------------------
# 9.1: BANKRUPTCY MODEL - PH Assumption Check
#-------------------------------------------------------------
cat("\n9.1: BANKRUPTCY MODEL - Proportional Hazards Test\n")

9.1: BANKRUPTCY MODEL - Proportional Hazards Test
cat("--------------------------------------\n")
--------------------------------------
# Extract best bankruptcy model from previous step
cat("Testing PH assumption for best bankruptcy model:", best_models$bankruptcy$name, "\n")
Testing PH assumption for best bankruptcy model: Stepwise 
bankruptcy_model <- best_models$bankruptcy$model

# Test proportional hazards assumption using Schoenfeld residuals
ph_test_bankruptcy <- cox.zph(bankruptcy_model)
cat("\nProportional Hazards Test Results (Schoenfeld Residuals):\n")

Proportional Hazards Test Results (Schoenfeld Residuals):
print(ph_test_bankruptcy)
                        chisq df     p
LTMTA                5.25e+00  1 0.022
NIMTA                1.20e+00  1 0.274
CASHMTA              2.01e-03  1 0.964
PRICE                6.10e+00  1 0.014
MBE                  1.15e-01  1 0.734
debt_ratio           4.38e+00  1 0.036
debt_service         1.89e-01  1 0.664
current_ratio        5.04e-03  1 0.943
cash_to_assets       2.50e-02  1 0.874
wc_ratio             2.38e+00  1 0.123
gp_margin            6.80e-01  1 0.410
asset_turnover       1.50e+00  1 0.220
receivables_turnover 3.15e-05  1 0.996
intangibility        4.40e-02  1 0.834
ebit_growth          1.72e+00  1 0.190
EBIT_VOL_3Y          3.95e-01  1 0.530
z_score              3.15e-02  1 0.859
gdp_deflator         4.73e-01  1 0.491
unemployement        7.54e-01  1 0.385
gdp_growth           3.92e-01  1 0.531
GLOBAL               1.63e+01 20 0.701
# Identify financial and macro variables violating the PH assumption
bankruptcy_ph_violators <- rownames(ph_test_bankruptcy$table)[
  ph_test_bankruptcy$table[, "p"] < 0.05 & 
  rownames(ph_test_bankruptcy$table) != "GLOBAL"
]

bankruptcy_fin_violators <- bankruptcy_ph_violators[bankruptcy_ph_violators %in% financial_covariates]
bankruptcy_macro_violators <- bankruptcy_ph_violators[bankruptcy_ph_violators %in% macro_covariates]

cat("\nFinancial variables violating PH assumption in bankruptcy model (p < 0.05):\n")

Financial variables violating PH assumption in bankruptcy model (p < 0.05):
if(length(bankruptcy_fin_violators) > 0) {
  print(bankruptcy_fin_violators)
} else {
  cat("None - All financial variables satisfy the proportional hazards assumption\n")
}
[1] "LTMTA"      "PRICE"      "debt_ratio"
cat("\nMacro variables violating PH assumption in bankruptcy model (p < 0.05):\n")

Macro variables violating PH assumption in bankruptcy model (p < 0.05):
if(length(bankruptcy_macro_violators) > 0) {
  print(bankruptcy_macro_violators)
} else {
  cat("None - All macro variables satisfy the proportional hazards assumption\n")
}
None - All macro variables satisfy the proportional hazards assumption
# Visualize Schoenfeld residuals for financial variables
if(length(best_models$bankruptcy$fin_vars) > 0) {
  cat("\nCreating Schoenfeld residual plots for financial variables in bankruptcy model...\n")
  par(mfrow = c(2, 2))  # Adjust based on number of variables
  for(i in 1:min(4, length(best_models$bankruptcy$fin_vars))) {
    var_name <- best_models$bankruptcy$fin_vars[i]
    var_idx <- which(rownames(ph_test_bankruptcy$table) == var_name)
    if(length(var_idx) > 0) {
      plot(ph_test_bankruptcy[var_idx], main=paste("Schoenfeld Residuals for", var_name), 
           xlab="Time", ylab="Beta(t)")
      abline(h=0, col="red", lty=2)
    }
  }
  par(mfrow = c(1, 1))
}

Creating Schoenfeld residual plots for financial variables in bankruptcy model...

# Visualize Schoenfeld residuals for macro variables (if any)
if(length(best_models$bankruptcy$macro_vars) > 0) {
  cat("\nCreating Schoenfeld residual plots for macro variables in bankruptcy model...\n")
  par(mfrow = c(2, 2))  # Adjust based on number of variables
  for(i in 1:min(4, length(best_models$bankruptcy$macro_vars))) {
    var_name <- best_models$bankruptcy$macro_vars[i]
    var_idx <- which(rownames(ph_test_bankruptcy$table) == var_name)
    if(length(var_idx) > 0) {
      plot(ph_test_bankruptcy[var_idx], main=paste("Schoenfeld Residuals for", var_name), 
           xlab="Time", ylab="Beta(t)")
      abline(h=0, col="red", lty=2)
    }
  }
  par(mfrow = c(1, 1))
}

Creating Schoenfeld residual plots for macro variables in bankruptcy model...

# Check global test
global_ph_bankruptcy <- ph_test_bankruptcy$table["GLOBAL", "p"]
cat("\nGlobal test of proportional hazards assumption for bankruptcy model: p =", 
    round(global_ph_bankruptcy, 4), "\n")

Global test of proportional hazards assumption for bankruptcy model: p = 0.7008 
if(global_ph_bankruptcy < 0.05) {
  cat("The global test indicates violation of the PH assumption (p < 0.05).\n")
  cat("Time-dependent coefficients should be considered in the next step.\n")
} else {
  cat("The global test supports the proportional hazards assumption (p >= 0.05).\n")
}
The global test supports the proportional hazards assumption (p >= 0.05).
#-------------------------------------------------------------
# 9.2: ACQUISITION MODEL - PH Assumption Check
#-------------------------------------------------------------
cat("\n9.2: ACQUISITION MODEL - Proportional Hazards Test\n")

9.2: ACQUISITION MODEL - Proportional Hazards Test
cat("--------------------------------------\n")
--------------------------------------
# Extract best acquisition model from previous step
cat("Testing PH assumption for best acquisition model:", best_models$acquisition$name, "\n")
Testing PH assumption for best acquisition model: LASSO 
acquisition_model <- best_models$acquisition$model

# Test proportional hazards assumption using Schoenfeld residuals
ph_test_acquisition <- cox.zph(acquisition_model)
cat("\nProportional Hazards Test Results (Schoenfeld Residuals):\n")

Proportional Hazards Test Results (Schoenfeld Residuals):
print(ph_test_acquisition)
                       chisq df       p
LTMTA                  0.654  1 0.41855
NIMTA                  4.060  1 0.04390
PRICE                 10.341  1 0.00130
MBE                    1.352  1 0.24497
debt_ratio             0.699  1 0.40304
current_ratio          3.843  1 0.04997
cash_to_assets         1.774  1 0.18292
wc_ratio              14.443  1 0.00014
gp_margin              6.634  1 0.01001
asset_turnover        11.341  1 0.00076
receivables_turnover   1.304  1 0.25351
EBIT_VOL_3Y           11.108  1 0.00086
gdp_deflator           0.350  1 0.55432
unemployement         23.700  1 1.1e-06
gdp_growth             0.349  1 0.55484
GLOBAL               106.280 15 8.3e-16
# Identify financial and macro variables violating the PH assumption
acquisition_ph_violators <- rownames(ph_test_acquisition$table)[
  ph_test_acquisition$table[, "p"] < 0.05 & 
  rownames(ph_test_acquisition$table) != "GLOBAL"
]

acquisition_fin_violators <- acquisition_ph_violators[acquisition_ph_violators %in% financial_covariates]
acquisition_macro_violators <- acquisition_ph_violators[acquisition_ph_violators %in% macro_covariates]

cat("\nFinancial variables violating PH assumption in acquisition model (p < 0.05):\n")

Financial variables violating PH assumption in acquisition model (p < 0.05):
if(length(acquisition_fin_violators) > 0) {
  print(acquisition_fin_violators)
} else {
  cat("None - All financial variables satisfy the proportional hazards assumption\n")
}
[1] "NIMTA"          "PRICE"          "current_ratio"  "wc_ratio"       "gp_margin"      "asset_turnover" "EBIT_VOL_3Y"   
cat("\nMacro variables violating PH assumption in acquisition model (p < 0.05):\n")

Macro variables violating PH assumption in acquisition model (p < 0.05):
if(length(acquisition_macro_violators) > 0) {
  print(acquisition_macro_violators)
} else {
  cat("None - All macro variables satisfy the proportional hazards assumption\n")
}
[1] "unemployement"
# Visualize Schoenfeld residuals for financial variables
if(length(best_models$acquisition$fin_vars) > 0) {
  cat("\nCreating Schoenfeld residual plots for financial variables in acquisition model...\n")
  par(mfrow = c(2, 2))  # Adjust based on number of variables
  for(i in 1:min(4, length(best_models$acquisition$fin_vars))) {
    var_name <- best_models$acquisition$fin_vars[i]
    var_idx <- which(rownames(ph_test_acquisition$table) == var_name)
    if(length(var_idx) > 0) {
      plot(ph_test_acquisition[var_idx], main=paste("Schoenfeld Residuals for", var_name), 
           xlab="Time", ylab="Beta(t)")
      abline(h=0, col="red", lty=2)
    }
  }
  par(mfrow = c(1, 1))
}

Creating Schoenfeld residual plots for financial variables in acquisition model...

# Visualize Schoenfeld residuals for macro variables (if any)
if(length(best_models$acquisition$macro_vars) > 0) {
  cat("\nCreating Schoenfeld residual plots for macro variables in acquisition model...\n")
  par(mfrow = c(2, 2))  # Adjust based on number of variables
  for(i in 1:min(4, length(best_models$acquisition$macro_vars))) {
    var_name <- best_models$acquisition$macro_vars[i]
    var_idx <- which(rownames(ph_test_acquisition$table) == var_name)
    if(length(var_idx) > 0) {
      plot(ph_test_acquisition[var_idx], main=paste("Schoenfeld Residuals for", var_name), 
           xlab="Time", ylab="Beta(t)")
      abline(h=0, col="red", lty=2)
    }
  }
  par(mfrow = c(1, 1))
}

Creating Schoenfeld residual plots for macro variables in acquisition model...

# Check global test
global_ph_acquisition <- ph_test_acquisition$table["GLOBAL", "p"]
cat("\nGlobal test of proportional hazards assumption for acquisition model: p =", 
    round(global_ph_acquisition, 4), "\n")

Global test of proportional hazards assumption for acquisition model: p = 0 
if(global_ph_acquisition < 0.05) {
  cat("The global test indicates violation of the PH assumption (p < 0.05).\n")
  cat("Time-dependent coefficients should be considered in the next step.\n")
} else {
  cat("The global test supports the proportional hazards assumption (p >= 0.05).\n")
}
The global test indicates violation of the PH assumption (p < 0.05).
Time-dependent coefficients should be considered in the next step.

Now that we know which variables violate the proportional hazards assumption, we can test for time interactions. We will create a new variable tt that represents time for the interaction terms. We will then fit models with time interactions for both bankruptcy and acquisition models, and analyze the results.

#-------------------------------------------------------------
# 9.3: Alternative Test - Time Interactions (Fixed)
#-------------------------------------------------------------
cat("\n9.3: Alternative Test - Direct Time Interactions (Fixed)\n")

9.3: Alternative Test - Direct Time Interactions (Fixed)
cat("--------------------------------------\n")
--------------------------------------
cat("Testing for non-proportionality using explicit time interactions...\n")
Testing for non-proportionality using explicit time interactions...
# Function to test time interactions for a model - fixed version
test_time_interactions <- function(model, model_type) {
  # Get model variables
  model_vars <- names(coef(model))
  model_vars <- model_vars[!grepl("strata", model_vars)]
  
  # Create data frame to store results
  interaction_results <- data.frame(
    Variable = character(),
    Type = character(),
    Interaction_Coef = numeric(),
    P_value = numeric(),
    stringsAsFactors = FALSE
  )
  
  # Create a time variable for interactions that won't conflict
  data$tt <- data$tstop  # tt = time for time-interactions
  
  # Test each variable with time interaction
  for(var in model_vars) {
    # Determine if this is a financial or macro variable
    var_type <- if(var %in% financial_covariates) "Financial" else "Macroeconomic"
    
    # Create formula with time interaction
    if(model_type == "bankruptcy") {
      formula_with_interaction <- as.formula(paste0(
        "Surv(tstart, tstop, bankruptcy) ~ ", 
        paste(model_vars, collapse = " + "),
        " + ", var, ":tt",  # Using tt instead of tstop
        " + strata(gsector)"
      ))
    } else {
      formula_with_interaction <- as.formula(paste0(
        "Surv(tstart, tstop, acquisition) ~ ", 
        paste(model_vars, collapse = " + "),
        " + ", var, ":tt",  # Using tt instead of tstop
        " + strata(gsector)"
      ))
    }
    
    # Fit model with interaction
    interaction_model <- tryCatch({
      coxph(formula_with_interaction, data = data, ties = "efron")
    }, error = function(e) {
      cat("Error fitting interaction model for", var, ":", e$message, "\n")
      return(NULL)
    })
    
    if(!is.null(interaction_model)) {
      # Extract interaction coefficient and p-value
      interaction_term <- paste0(var, ":tt")
      if(interaction_term %in% names(coef(interaction_model))) {
        coef_val <- coef(interaction_model)[interaction_term]
        p_val <- summary(interaction_model)$coefficients[interaction_term, "Pr(>|z|)"]
        
        # Add to results
        interaction_results <- rbind(interaction_results, data.frame(
          Variable = var,
          Type = var_type,
          Interaction_Coef = coef_val,
          P_value = p_val,
          stringsAsFactors = FALSE
        ))
      }
    }
  }
  
  # Sort by p-value
  interaction_results <- interaction_results[order(interaction_results$P_value), ]
  
  return(interaction_results)
}

# Test time interactions for bankruptcy model
bankruptcy_interactions <- test_time_interactions(bankruptcy_model, "bankruptcy")
cat("\nTime interaction test results for bankruptcy model:\n")

Time interaction test results for bankruptcy model:
print(bankruptcy_interactions)

# Test time interactions for acquisition model
acquisition_interactions <- test_time_interactions(acquisition_model, "acquisition")
cat("\nTime interaction test results for acquisition model:\n")

Time interaction test results for acquisition model:
print(acquisition_interactions)

# Analyze time interactions by variable type
cat("\nAnalysis of time interactions by variable type:\n")

Analysis of time interactions by variable type:
# For bankruptcy model
bankruptcy_fin_interactions <- bankruptcy_interactions[bankruptcy_interactions$Type == "Financial", ]
bankruptcy_macro_interactions <- bankruptcy_interactions[bankruptcy_interactions$Type == "Macroeconomic", ]

cat("\nBankruptcy model - financial variables with significant time interactions (p < 0.05):\n")

Bankruptcy model - financial variables with significant time interactions (p < 0.05):
sig_bk_fin <- bankruptcy_fin_interactions[bankruptcy_fin_interactions$P_value < 0.05, ]
if(nrow(sig_bk_fin) > 0) {
  print(sig_bk_fin)
} else {
  cat("No significant time interactions found for financial variables.\n")
}

cat("\nBankruptcy model - macro variables with significant time interactions (p < 0.05):\n")

Bankruptcy model - macro variables with significant time interactions (p < 0.05):
sig_bk_macro <- bankruptcy_macro_interactions[bankruptcy_macro_interactions$P_value < 0.05, ]
if(nrow(sig_bk_macro) > 0) {
  print(sig_bk_macro)
  cat("\nInterpretation: These macro variables have effects that change over time, suggesting\n")
  cat("that economic conditions have a time-varying impact on bankruptcy risk.\n")
} else {
  cat("No significant time interactions found for macro variables.\n")
}
No significant time interactions found for macro variables.
# For acquisition model
acquisition_fin_interactions <- acquisition_interactions[acquisition_interactions$Type == "Financial", ]
acquisition_macro_interactions <- acquisition_interactions[acquisition_interactions$Type == "Macroeconomic", ]

cat("\nAcquisition model - financial variables with significant time interactions (p < 0.05):\n")

Acquisition model - financial variables with significant time interactions (p < 0.05):
sig_acq_fin <- acquisition_fin_interactions[acquisition_fin_interactions$P_value < 0.05, ]
if(nrow(sig_acq_fin) > 0) {
  print(sig_acq_fin)
} else {
  cat("No significant time interactions found for financial variables.\n")
}

cat("\nAcquisition model - macro variables with significant time interactions (p < 0.05):\n")

Acquisition model - macro variables with significant time interactions (p < 0.05):
sig_acq_macro <- acquisition_macro_interactions[acquisition_macro_interactions$P_value < 0.05, ]
if(nrow(sig_acq_macro) > 0) {
  print(sig_acq_macro)
  cat("\nInterpretation: These macro variables have effects that change over time, suggesting\n")
  cat("that economic conditions have a time-varying impact on acquisition likelihood.\n")
} else {
  cat("No significant time interactions found for macro variables.\n")
}

Interpretation: These macro variables have effects that change over time, suggesting
that economic conditions have a time-varying impact on acquisition likelihood.
# Additional check for business cycle effects
cat("\n9.4: Testing for Business Cycle Effects in Macro Variables\n")

9.4: Testing for Business Cycle Effects in Macro Variables
cat("--------------------------------------\n")
--------------------------------------
cat("Examining if macro variables violate the PH assumption due to business cycle effects...\n")
Examining if macro variables violate the PH assumption due to business cycle effects...
# Macro variables that violate PH assumption in either model
all_macro_violators <- unique(c(bankruptcy_macro_violators, acquisition_macro_violators))

if(length(all_macro_violators) > 0) {
  cat("\nThe following macro variables violate the PH assumption, potentially due to business cycle effects:\n")
  print(all_macro_violators)
  
  cat("\nPossible interpretations:\n")
  cat("1. The effect of these variables changes over time with economic conditions\n")
  cat("2. Their influence may be stronger during recessions or expansions\n")
  cat("3. Time-varying coefficients may be more appropriate for these variables\n")
} else {
  cat("\nNo macro variables violate the PH assumption, suggesting that macroeconomic\n")
  cat("effects on company outcomes are stable over time regardless of business cycles.\n")
}

The following macro variables violate the PH assumption, potentially due to business cycle effects:
[1] "unemployement"

Possible interpretations:
1. The effect of these variables changes over time with economic conditions
2. Their influence may be stronger during recessions or expansions
3. Time-varying coefficients may be more appropriate for these variables
# Store results for next step
ph_test_results <- list(
  bankruptcy = list(
    schoenfeld_test = ph_test_bankruptcy,
    all_violators = bankruptcy_ph_violators,
    fin_violators = bankruptcy_fin_violators,
    macro_violators = bankruptcy_macro_violators,
    time_interactions = bankruptcy_interactions,
    sig_fin_interactions = sig_bk_fin,
    sig_macro_interactions = sig_bk_macro
  ),
  acquisition = list(
    schoenfeld_test = ph_test_acquisition,
    all_violators = acquisition_ph_violators,
    fin_violators = acquisition_fin_violators,
    macro_violators = acquisition_macro_violators,
    time_interactions = acquisition_interactions,
    sig_fin_interactions = sig_acq_fin,
    sig_macro_interactions = sig_acq_macro
  )
)

We will now proceed to fit the final models with time-dependent coefficients. We will create a new variable tt that represents time for the interaction terms. We will then fit models with time interactions for both bankruptcy and acquisition models, and analyze the results.

#-------------------------------------------------------------
# STEP 10: Final Models with Time-Dependent Coefficients (Modified)
#-------------------------------------------------------------
cat("\n=== STEP 10: FINAL MODELS WITH TIME-DEPENDENT COEFFICIENTS AND MACRO EFFECTS ===\n")

=== STEP 10: FINAL MODELS WITH TIME-DEPENDENT COEFFICIENTS AND MACRO EFFECTS ===
# Create a time variable for interactions if it doesn't exist
if(!"tt" %in% colnames(data)) {
  cat("Creating time variable (tt) for time-dependent coefficients...\n")
  data$tt <- data$tstop  # tt = time for time-interactions
}
Creating time variable (tt) for time-dependent coefficients...
#-------------------------------------------------------------
# 10.1: BANKRUPTCY MODEL - Address PH Violations
#-------------------------------------------------------------
cat("\n10.1: BANKRUPTCY MODEL - Addressing PH Violations\n")

10.1: BANKRUPTCY MODEL - Addressing PH Violations
cat("--------------------------------------\n")
--------------------------------------
# Extract violators from previous step's results, separated by variable type
bankruptcy_fin_violators <- ph_test_results$bankruptcy$fin_violators
bankruptcy_macro_violators <- ph_test_results$bankruptcy$macro_violators
bankruptcy_ph_violators <- ph_test_results$bankruptcy$all_violators

cat("Financial variables violating PH assumption in bankruptcy model:\n")
Financial variables violating PH assumption in bankruptcy model:
if(length(bankruptcy_fin_violators) > 0) {
  print(bankruptcy_fin_violators)
} else {
  cat("None found - All financial variables satisfy PH assumption\n")
}
[1] "LTMTA"      "PRICE"      "debt_ratio"
cat("\nMacroeconomic variables violating PH assumption in bankruptcy model:\n")

Macroeconomic variables violating PH assumption in bankruptcy model:
if(length(bankruptcy_macro_violators) > 0) {
  print(bankruptcy_macro_violators)
  cat("\nNote: Violations in macro variables may indicate business cycle effects\n")
} else {
  cat("None found - All macro variables satisfy PH assumption\n")
}
None found - All macro variables satisfy PH assumption
if(length(bankruptcy_ph_violators) > 0) {
  cat("\nTotal variables violating PH assumption:", length(bankruptcy_ph_violators), "\n")
  print(bankruptcy_ph_violators)
  
  # To prevent computational issues, limit to top 3 most significant violators if there are many
  if(length(bankruptcy_ph_violators) > 3) {
    cat("Many variables violate PH assumption. Using only the top 3 most significant violators.\n")
    # Get p-values from the PH test
    violator_pvals <- ph_test_results$bankruptcy$schoenfeld_test$table[bankruptcy_ph_violators, "p"]
    # Select top 3 with lowest p-values
    bankruptcy_ph_violators <- names(sort(violator_pvals)[1:3])
    cat("Selected violators:", paste(bankruptcy_ph_violators, collapse=", "), "\n")
    
    # Check if any macro variables are in the top 3
    has_macro <- any(bankruptcy_ph_violators %in% macro_covariates)
    if(has_macro) {
      cat("Selection includes macro variables, which may indicate business cycle effects.\n")
    } else {
      # If no macro variables are in top 3 but there are macro violators, add the most significant one
      if(length(bankruptcy_macro_violators) > 0) {
        macro_violator_pvals <- ph_test_results$bankruptcy$schoenfeld_test$table[bankruptcy_macro_violators, "p"]
        top_macro_violator <- names(sort(macro_violator_pvals)[1])
        
        # Replace the least significant of the 3 with the top macro violator
        worst_idx <- which.max(violator_pvals[bankruptcy_ph_violators])
        bankruptcy_ph_violators[worst_idx] <- top_macro_violator
        cat("Added macro variable", top_macro_violator, "to ensure business cycle effects are captured.\n")
      }
    }
  }
  
  # Create formula iteratively by adding one interaction at a time
  bankruptcy_vars <- best_models$bankruptcy$variables
  
  # Start with the base formula
  formula_str <- paste0("Surv(tstart, tstop, bankruptcy) ~ ", 
                       paste(bankruptcy_vars, collapse = " + "),
                       " + strata(gsector)")
  
  # Add interactions one by one
  for(var in bankruptcy_ph_violators) {
    # Try adding this interaction
    var_type <- ifelse(var %in% financial_covariates, "financial", "macro")
    cat("Testing time interaction for", var, "(", var_type, "variable )...\n")
    new_formula_str <- paste0(formula_str, " + ", var, ":tt")
    
    # See if the formula works
    tryCatch({
      new_formula <- as.formula(new_formula_str)
      # If it works, update the formula string
      formula_str <- new_formula_str
      cat("Added time interaction for", var, "successfully.\n")
    }, error = function(e) {
      cat("Error adding time interaction for", var, ":", e$message, "\n")
      cat("Skipping this interaction.\n")
    })
  }
  
  # Create the final formula
  cat("Creating final formula for bankruptcy model...\n")
  final_bankruptcy_formula <- as.formula(formula_str)
  
  cat("\nFitting final bankruptcy model with time-dependent coefficients...\n")
  final_bankruptcy_model <- coxph(final_bankruptcy_formula, data = data, ties = "efron")
  
  cat("\n=== FINAL BANKRUPTCY MODEL WITH TIME-DEPENDENT EFFECTS ===\n")
  print(summary(final_bankruptcy_model))
  
} else {
  cat("None found - Using original model\n")
  final_bankruptcy_model <- best_models$bankruptcy$model
  cat("\nFinal bankruptcy model remains unchanged (no PH violations).\n")
}

Total variables violating PH assumption: 3 
[1] "LTMTA"      "PRICE"      "debt_ratio"
Testing time interaction for LTMTA ( financial variable )...
Added time interaction for LTMTA successfully.
Testing time interaction for PRICE ( financial variable )...
Added time interaction for PRICE successfully.
Testing time interaction for debt_ratio ( financial variable )...
Added time interaction for debt_ratio successfully.
Creating final formula for bankruptcy model...

Fitting final bankruptcy model with time-dependent coefficients...

=== FINAL BANKRUPTCY MODEL WITH TIME-DEPENDENT EFFECTS ===
Call:
coxph(formula = final_bankruptcy_formula, data = data, ties = "efron")

  n= 63591, number of events= 176 

                          coef exp(coef)  se(coef)      z Pr(>|z|)    
LTMTA                 3.669656 39.238389  0.783073  4.686 2.78e-06 ***
NIMTA                -2.141137  0.117521  0.407862 -5.250 1.52e-07 ***
CASHMTA              -2.110060  0.121231  0.947459 -2.227 0.025942 *  
PRICE                -0.172198  0.841812  0.104301 -1.651 0.098745 .  
MBE                  -0.164041  0.848707  0.085135 -1.927 0.053999 .  
debt_ratio           -0.492916  0.610842  0.450052 -1.095 0.273411    
debt_service         -0.003393  0.996613  0.022750 -0.149 0.881437    
current_ratio         0.083558  1.087148  0.040743  2.051 0.040279 *  
cash_to_assets        1.438954  4.216283  0.909733  1.582 0.113711    
wc_ratio             -1.104150  0.331492  0.323614 -3.412 0.000645 ***
gp_margin             0.062809  1.064824  0.046584  1.348 0.177562    
asset_turnover       -0.168935  0.844564  0.096454 -1.751 0.079866 .  
receivables_turnover  0.001262  1.001262  0.001797  0.702 0.482692    
intangibility        -0.294410  0.744971  0.214773 -1.371 0.170438    
ebit_growth          -0.017514  0.982638  0.025433 -0.689 0.491044    
EBIT_VOL_3Y          -0.001524  0.998477  0.012983 -0.117 0.906537    
z_score               0.029653  1.030097  0.013010  2.279 0.022658 *  
gdp_deflator         -0.175334  0.839177  0.089871 -1.951 0.051064 .  
unemployement        -0.074394  0.928306  0.068538 -1.085 0.277722    
gdp_growth            0.098686  1.103720  0.057119  1.728 0.084038 .  
LTMTA:tt              0.041183  1.042043  0.061395  0.671 0.502354    
PRICE:tt             -0.011245  0.988818  0.007439 -1.512 0.130626    
debt_ratio:tt         0.027717  1.028105  0.029924  0.926 0.354319    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                  39.2384    0.02549   8.45587  182.0808
NIMTA                   0.1175    8.50911   0.05284    0.2614
CASHMTA                 0.1212    8.24874   0.01893    0.7764
PRICE                   0.8418    1.18791   0.68617    1.0328
MBE                     0.8487    1.17826   0.71828    1.0028
debt_ratio              0.6108    1.63708   0.25284    1.4758
debt_service            0.9966    1.00340   0.95315    1.0421
current_ratio           1.0871    0.91984   1.00371    1.1775
cash_to_assets          4.2163    0.23718   0.70886   25.0782
wc_ratio                0.3315    3.01666   0.17580    0.6251
gp_margin               1.0648    0.93912   0.97191    1.1666
asset_turnover          0.8446    1.18404   0.69909    1.0203
receivables_turnover    1.0013    0.99874   0.99774    1.0048
intangibility           0.7450    1.34233   0.48902    1.1349
ebit_growth             0.9826    1.01767   0.93486    1.0329
EBIT_VOL_3Y             0.9985    1.00153   0.97339    1.0242
z_score                 1.0301    0.97078   1.00416    1.0567
gdp_deflator            0.8392    1.19164   0.70365    1.0008
unemployement           0.9283    1.07723   0.81162    1.0618
gdp_growth              1.1037    0.90603   0.98682    1.2345
LTMTA:tt                1.0420    0.95965   0.92390    1.1753
PRICE:tt                0.9888    1.01131   0.97451    1.0033
debt_ratio:tt           1.0281    0.97266   0.96954    1.0902

Concordance= 0.864  (se = 0.019 )
Likelihood ratio test= 445.2  on 23 df,   p=<2e-16
Wald test            = 386.2  on 23 df,   p=<2e-16
Score (logrank) test = 612.3  on 23 df,   p=<2e-16
#-------------------------------------------------------------
# 10.2: ACQUISITION MODEL - Address PH Violations
#-------------------------------------------------------------
cat("\n10.2: ACQUISITION MODEL - Addressing PH Violations\n")

10.2: ACQUISITION MODEL - Addressing PH Violations
cat("--------------------------------------\n")
--------------------------------------
# Extract violators from previous step's results, separated by variable type
acquisition_fin_violators <- ph_test_results$acquisition$fin_violators
acquisition_macro_violators <- ph_test_results$acquisition$macro_violators
acquisition_ph_violators <- ph_test_results$acquisition$all_violators

cat("Financial variables violating PH assumption in acquisition model:\n")
Financial variables violating PH assumption in acquisition model:
if(length(acquisition_fin_violators) > 0) {
  print(acquisition_fin_violators)
} else {
  cat("None found - All financial variables satisfy PH assumption\n")
}
[1] "NIMTA"          "PRICE"          "current_ratio"  "wc_ratio"       "gp_margin"      "asset_turnover" "EBIT_VOL_3Y"   
cat("\nMacroeconomic variables violating PH assumption in acquisition model:\n")

Macroeconomic variables violating PH assumption in acquisition model:
if(length(acquisition_macro_violators) > 0) {
  print(acquisition_macro_violators)
  cat("\nNote: Violations in macro variables may indicate business cycle effects\n")
} else {
  cat("None found - All macro variables satisfy PH assumption\n")
}
[1] "unemployement"

Note: Violations in macro variables may indicate business cycle effects
if(length(acquisition_ph_violators) > 0) {
  cat("\nTotal variables violating PH assumption:", length(acquisition_ph_violators), "\n")
  print(acquisition_ph_violators)
  
  # To prevent computational issues, limit to top 3 most significant violators if there are many
  if(length(acquisition_ph_violators) > 3) {
    cat("Many variables violate PH assumption. Using only the top 3 most significant violators.\n")
    # Get p-values from the PH test
    violator_pvals <- ph_test_results$acquisition$schoenfeld_test$table[acquisition_ph_violators, "p"]
    # Select top 3 with lowest p-values
    acquisition_ph_violators <- names(sort(violator_pvals)[1:3])
    cat("Selected violators:", paste(acquisition_ph_violators, collapse=", "), "\n")
    
    # Check if any macro variables are in the top 3
    has_macro <- any(acquisition_ph_violators %in% macro_covariates)
    if(has_macro) {
      cat("Selection includes macro variables, which may indicate business cycle effects.\n")
    } else {
      # If no macro variables are in top 3 but there are macro violators, add the most significant one
      if(length(acquisition_macro_violators) > 0) {
        macro_violator_pvals <- ph_test_results$acquisition$schoenfeld_test$table[acquisition_macro_violators, "p"]
        top_macro_violator <- names(sort(macro_violator_pvals)[1])
        
        # Replace the least significant of the 3 with the top macro violator
        worst_idx <- which.max(violator_pvals[acquisition_ph_violators])
        acquisition_ph_violators[worst_idx] <- top_macro_violator
        cat("Added macro variable", top_macro_violator, "to ensure business cycle effects are captured.\n")
      }
    }
  }
  
  # Create formula iteratively by adding one interaction at a time
  acquisition_vars <- best_models$acquisition$variables
  
  # Start with the base formula
  formula_str <- paste0("Surv(tstart, tstop, acquisition) ~ ", 
                       paste(acquisition_vars, collapse = " + "),
                       " + strata(gsector)")
  
  # Add interactions one by one
  for(var in acquisition_ph_violators) {
    # Try adding this interaction
    var_type <- ifelse(var %in% financial_covariates, "financial", "macro")
    cat("Testing time interaction for", var, "(", var_type, "variable )...\n")
    new_formula_str <- paste0(formula_str, " + ", var, ":tt")
    
    # See if the formula works
    tryCatch({
      new_formula <- as.formula(new_formula_str)
      # If it works, update the formula string
      formula_str <- new_formula_str
      cat("Added time interaction for", var, "successfully.\n")
    }, error = function(e) {
      cat("Error adding time interaction for", var, ":", e$message, "\n")
      cat("Skipping this interaction.\n")
    })
  }
  
  # Create the final formula
  cat("Creating final formula for acquisition model...\n")
  final_acquisition_formula <- as.formula(formula_str)
  
  cat("\nFitting final acquisition model with time-dependent coefficients...\n")
  final_acquisition_model <- coxph(final_acquisition_formula, data = data, ties = "efron")
  
  cat("\n=== FINAL ACQUISITION MODEL WITH TIME-DEPENDENT EFFECTS ===\n")
  print(summary(final_acquisition_model))
  
} else {
  cat("None found - Using original model\n")
  final_acquisition_model <- best_models$acquisition$model
  cat("\nFinal acquisition model remains unchanged (no PH violations).\n")
}

Total variables violating PH assumption: 8 
[1] "NIMTA"          "PRICE"          "current_ratio"  "wc_ratio"       "gp_margin"      "asset_turnover" "EBIT_VOL_3Y"   
[8] "unemployement" 
Many variables violate PH assumption. Using only the top 3 most significant violators.
Selected violators: unemployement, wc_ratio, asset_turnover 
Selection includes macro variables, which may indicate business cycle effects.
Testing time interaction for unemployement ( macro variable )...
Added time interaction for unemployement successfully.
Testing time interaction for wc_ratio ( financial variable )...
Added time interaction for wc_ratio successfully.
Testing time interaction for asset_turnover ( financial variable )...
Added time interaction for asset_turnover successfully.
Creating final formula for acquisition model...

Fitting final acquisition model with time-dependent coefficients...

=== FINAL ACQUISITION MODEL WITH TIME-DEPENDENT EFFECTS ===
Call:
coxph(formula = final_acquisition_formula, data = data, ties = "efron")

  n= 63591, number of events= 2154 

                           coef  exp(coef)   se(coef)       z Pr(>|z|)    
LTMTA                -0.0317114  0.9687862  0.1826326  -0.174 0.862152    
NIMTA                 0.1883795  1.2072916  0.1900803   0.991 0.321660    
PRICE                 0.1328157  1.1420395  0.0204355   6.499 8.07e-11 ***
MBE                  -0.2303072  0.7942895  0.0226148 -10.184  < 2e-16 ***
debt_ratio            0.0223924  1.0226450  0.1277378   0.175 0.860844    
current_ratio        -0.0737268  0.9289255  0.0137999  -5.343 9.16e-08 ***
cash_to_assets        0.7377511  2.0912273  0.1628459   4.530 5.89e-06 ***
wc_ratio             -0.7110165  0.4911447  0.1987708  -3.577 0.000347 ***
gp_margin             0.0256220  1.0259531  0.0090340   2.836 0.004566 ** 
asset_turnover       -0.1872699  0.8292199  0.0572374  -3.272 0.001069 ** 
receivables_turnover -0.0032563  0.9967490  0.0009728  -3.347 0.000816 ***
EBIT_VOL_3Y          -0.0075669  0.9924617  0.0042047  -1.800 0.071918 .  
gdp_deflator         -0.0381169  0.9626004  0.0221410  -1.722 0.085150 .  
unemployement        -0.1340807  0.8745195  0.0311795  -4.300 1.71e-05 ***
gdp_growth            0.0369393  1.0376301  0.0148860   2.481 0.013084 *  
unemployement:tt      0.0082911  1.0083256  0.0020019   4.142 3.45e-05 ***
wc_ratio:tt           0.0464175  1.0475116  0.0127462   3.642 0.000271 ***
asset_turnover:tt     0.0129630  1.0130474  0.0042668   3.038 0.002381 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                     exp(coef) exp(-coef) lower .95 upper .95
LTMTA                   0.9688     1.0322    0.6773    1.3858
NIMTA                   1.2073     0.8283    0.8318    1.7523
PRICE                   1.1420     0.8756    1.0972    1.1887
MBE                     0.7943     1.2590    0.7599    0.8303
debt_ratio              1.0226     0.9779    0.7961    1.3136
current_ratio           0.9289     1.0765    0.9041    0.9544
cash_to_assets          2.0912     0.4782    1.5198    2.8775
wc_ratio                0.4911     2.0361    0.3327    0.7251
gp_margin               1.0260     0.9747    1.0079    1.0443
asset_turnover          0.8292     1.2060    0.7412    0.9277
receivables_turnover    0.9967     1.0033    0.9949    0.9987
EBIT_VOL_3Y             0.9925     1.0076    0.9843    1.0007
gdp_deflator            0.9626     1.0389    0.9217    1.0053
unemployement           0.8745     1.1435    0.8227    0.9296
gdp_growth              1.0376     0.9637    1.0078    1.0683
unemployement:tt        1.0083     0.9917    1.0044    1.0123
wc_ratio:tt             1.0475     0.9546    1.0217    1.0740
asset_turnover:tt       1.0130     0.9871    1.0046    1.0216

Concordance= 0.643  (se = 0.007 )
Likelihood ratio test= 448  on 18 df,   p=<2e-16
Wald test            = 298.2  on 18 df,   p=<2e-16
Score (logrank) test = 296.8  on 18 df,   p=<2e-16

Next, we will interpret the time-dependent effects of the macroeconomic variables in both models. We will focus on how these effects change over time and their implications for business cycles.

#-------------------------------------------------------------
# 10.3: Interpret Time-Dependent Effects with Focus on Macro Variables
#-------------------------------------------------------------
cat("\n10.3: Interpreting Time-Dependent Effects with Focus on Macro Variables\n")

10.3: Interpreting Time-Dependent Effects with Focus on Macro Variables
cat("--------------------------------------\n")
--------------------------------------
# Get all time interaction terms from final models
bankruptcy_time_terms <- grep(":tt", names(coef(final_bankruptcy_model)), value = TRUE)
acquisition_time_terms <- grep(":tt", names(coef(final_acquisition_model)), value = TRUE)

# Split terms by variable type
bankruptcy_fin_time_terms <- bankruptcy_time_terms[sapply(bankruptcy_time_terms, function(term) {
  var_name <- strsplit(term, ":")[[1]][1]
  return(var_name %in% financial_covariates)
})]

bankruptcy_macro_time_terms <- bankruptcy_time_terms[sapply(bankruptcy_time_terms, function(term) {
  var_name <- strsplit(term, ":")[[1]][1]
  return(var_name %in% macro_covariates)
})]

acquisition_fin_time_terms <- acquisition_time_terms[sapply(acquisition_time_terms, function(term) {
  var_name <- strsplit(term, ":")[[1]][1]
  return(var_name %in% financial_covariates)
})]

acquisition_macro_time_terms <- acquisition_time_terms[sapply(acquisition_time_terms, function(term) {
  var_name <- strsplit(term, ":")[[1]][1]
  return(var_name %in% macro_covariates)
})]

# Report time-dependent effects by variable type
cat("\nBankruptcy model - financial time-dependent terms:", 
    ifelse(length(bankruptcy_fin_time_terms) > 0, 
           paste(bankruptcy_fin_time_terms, collapse=", "), 
           "None"), "\n")

Bankruptcy model - financial time-dependent terms: LTMTA:tt, PRICE:tt, debt_ratio:tt 
cat("\nBankruptcy model - macro time-dependent terms:", 
    ifelse(length(bankruptcy_macro_time_terms) > 0, 
           paste(bankruptcy_macro_time_terms, collapse=", "), 
           "None"), "\n")

Bankruptcy model - macro time-dependent terms: None 
cat("\nAcquisition model - financial time-dependent terms:", 
    ifelse(length(acquisition_fin_time_terms) > 0, 
           paste(acquisition_fin_time_terms, collapse=", "), 
           "None"), "\n")

Acquisition model - financial time-dependent terms: wc_ratio:tt, asset_turnover:tt 
cat("\nAcquisition model - macro time-dependent terms:", 
    ifelse(length(acquisition_macro_time_terms) > 0, 
           paste(acquisition_macro_time_terms, collapse=", "), 
           "None"), "\n")

Acquisition model - macro time-dependent terms: unemployement:tt 
# Analyze and interpret time-dependent effects of macro variables
if(length(bankruptcy_macro_time_terms) > 0 || length(acquisition_macro_time_terms) > 0) {
  cat("\n=== ANALYSIS OF TIME-DEPENDENT MACROECONOMIC EFFECTS ===\n")
  
  # For bankruptcy model
  if(length(bankruptcy_macro_time_terms) > 0) {
    cat("\nBankruptcy risk - Macroeconomic time-dependent effects:\n")
    for(term in bankruptcy_macro_time_terms) {
      var_name <- strsplit(term, ":")[[1]][1]
      coef_main <- coef(final_bankruptcy_model)[var_name]
      coef_time <- coef(final_bankruptcy_model)[term]
      
      cat("\n", var_name, ":\n", sep="")
      cat("- Base effect (coefficient):", round(coef_main, 4), "\n")
      cat("- Time interaction effect:", round(coef_time, 4), "\n")
      
      if(coef_time > 0) {
        cat("- Effect interpretation: The impact of", var_name, "on bankruptcy risk INCREASES over time\n")
        if(coef_main < 0) {
          cat("  (starts as protective but becomes less protective)\n")
        } else {
          cat("  (becomes more risky over time)\n")
        }
      } else {
        cat("- Effect interpretation: The impact of", var_name, "on bankruptcy risk DECREASES over time\n")
        if(coef_main > 0) {
          cat("  (starts as risky but becomes less risky)\n")
        } else {
          cat("  (becomes more protective over time)\n")
        }
      }
      
      # Business cycle interpretation
      cat("- Business cycle implication: ")
      if(var_name == "gdp_growth") {
        if(coef_time > 0) {
          cat("GDP growth becomes a stronger predictor of bankruptcy avoidance later in a company's lifecycle\n")
        } else {
          cat("GDP growth is most protective against bankruptcy early in a company's lifecycle\n")
        }
      } else if(var_name == "unemployement") {
        if(coef_time > 0) {
          cat("Unemployment has an increasingly detrimental effect on company survival over time\n")
        } else {
          cat("Unemployment is most harmful to company survival early in a company's lifecycle\n")
        }
      } else if(var_name == "gdp_deflator") {
        if(coef_time > 0) {
          cat("Inflation becomes more influential on bankruptcy risk as companies mature\n")
        } else {
          cat("Inflation has its strongest impact on bankruptcy risk early in a company's lifecycle\n")
        }
      }
    }
  }
  
  # For acquisition model
  if(length(acquisition_macro_time_terms) > 0) {
    cat("\nAcquisition likelihood - Macroeconomic time-dependent effects:\n")
    for(term in acquisition_macro_time_terms) {
      var_name <- strsplit(term, ":")[[1]][1]
      coef_main <- coef(final_acquisition_model)[var_name]
      coef_time <- coef(final_acquisition_model)[term]
      
      cat("\n", var_name, ":\n", sep="")
      cat("- Base effect (coefficient):", round(coef_main, 4), "\n")
      cat("- Time interaction effect:", round(coef_time, 4), "\n")
      
      if(coef_time > 0) {
        cat("- Effect interpretation: The impact of", var_name, "on acquisition likelihood INCREASES over time\n")
        if(coef_main < 0) {
          cat("  (starts reducing acquisition chances but this effect weakens)\n")
        } else {
          cat("  (becomes more conducive to acquisitions over time)\n")
        }
      } else {
        cat("- Effect interpretation: The impact of", var_name, "on acquisition likelihood DECREASES over time\n")
        if(coef_main > 0) {
          cat("  (starts increasing acquisition chances but this effect weakens)\n")
        } else {
          cat("  (becomes less conducive to acquisitions over time)\n")
        }
      }
      
      # Business cycle interpretation
      cat("- Business cycle implication: ")
      if(var_name == "gdp_growth") {
        if(coef_time > 0) {
          cat("Strong economic growth becomes more influential for acquisitions as companies mature\n")
        } else {
          cat("Economic growth is most influential for acquisitions early in a company's lifecycle\n")
        }
      } else if(var_name == "unemployement") {
        if(coef_time > 0) {
          cat("Labor market conditions have increasing influence on acquisition likelihood over time\n")
        } else {
          cat("Labor market conditions are most influential for acquisitions early in a company's lifecycle\n")
        }
      } else if(var_name == "gdp_deflator") {
        if(coef_time > 0) {
          cat("Inflation becomes more influential on acquisition activity as companies mature\n")
        } else {
          cat("Inflation has its strongest impact on acquisition activity early in a company's lifecycle\n")
        }
      }
    }
  }
}

=== ANALYSIS OF TIME-DEPENDENT MACROECONOMIC EFFECTS ===

Acquisition likelihood - Macroeconomic time-dependent effects:

unemployement:
- Base effect (coefficient): -0.1341 
- Time interaction effect: 0.0083 
- Effect interpretation: The impact of unemployement on acquisition likelihood INCREASES over time
  (starts reducing acquisition chances but this effect weakens)
- Business cycle implication: Labor market conditions have increasing influence on acquisition likelihood over time

Probably not relevant to the analysis, but included for completeness!

We double check the proportional hazards assumption for the final models with time-dependent coefficients. We will also check if any remaining violations exist and if the global test supports the PH assumption. We see that for the bankruptcy model, the global test indicates that the PH assumption is satisfied. However, for the acquisition model, the global test indicates that the PH assumption is violated. We will need to investigate further.

#-------------------------------------------------------------
# 10.5: Verify Resolution of PH Violations
#-------------------------------------------------------------
cat("\n10.5: Verify Resolution of PH Violations\n")

10.5: Verify Resolution of PH Violations
cat("--------------------------------------\n")
--------------------------------------
cat("Testing if time-dependent coefficients resolved PH violations...\n")
Testing if time-dependent coefficients resolved PH violations...
# Re-check PH assumption for bankruptcy model with time interactions
if(length(grep(":tt", names(coef(final_bankruptcy_model)))) > 0) {
  cat("\nRe-testing PH assumption for bankruptcy model with time interactions:\n")
  ph_test_final_bankruptcy <- cox.zph(final_bankruptcy_model)
  print(ph_test_final_bankruptcy)
  
  # Check if any remaining violations, separated by variable type
  remaining_violators <- rownames(ph_test_final_bankruptcy$table)[
    ph_test_final_bankruptcy$table[, "p"] < 0.05 & 
    rownames(ph_test_final_bankruptcy$table) != "GLOBAL"
  ]
  
  remaining_fin_violators <- remaining_violators[remaining_violators %in% financial_covariates]
  remaining_macro_violators <- remaining_violators[remaining_violators %in% macro_covariates]
  
  cat("\nRemaining financial variables violating PH assumption:\n")
  if(length(remaining_fin_violators) > 0) {
    print(remaining_fin_violators)
  } else {
    cat("None - All financial variables now satisfy PH assumption\n")
  }
  
  cat("\nRemaining macro variables violating PH assumption:\n")
  if(length(remaining_macro_violators) > 0) {
    print(remaining_macro_violators)
    cat("\nNote: Persistent violations in macro variables may indicate complex business cycle effects\n")
    cat("that might require more sophisticated modeling approaches (e.g., stratification or frailty models).\n")
  } else {
    cat("None - All macro variables now satisfy PH assumption\n")
  }
  
  # Check global test
  if(ph_test_final_bankruptcy$table["GLOBAL", "p"] < 0.05) {
    cat("\nWARNING: Global test still indicates PH violations (p < 0.05).\n")
    cat("Further refinements might be needed, especially for economic cycle dynamics.\n")
  } else {
    cat("\nSUCCESS: Global test now supports the PH assumption (p >= 0.05).\n")
  }
}

Re-testing PH assumption for bankruptcy model with time interactions:
                        chisq df    p
LTMTA                 0.19069  1 0.66
NIMTA                 0.13444  1 0.71
CASHMTA               0.06332  1 0.80
PRICE                 0.38656  1 0.53
MBE                   0.23326  1 0.63
debt_ratio            0.54167  1 0.46
debt_service          0.35889  1 0.55
current_ratio         1.33523  1 0.25
cash_to_assets        0.19409  1 0.66
wc_ratio              0.00159  1 0.97
gp_margin             0.55629  1 0.46
asset_turnover        0.34848  1 0.55
receivables_turnover  0.02190  1 0.88
intangibility         0.21885  1 0.64
ebit_growth           1.76649  1 0.18
EBIT_VOL_3Y           0.34856  1 0.55
z_score               0.83217  1 0.36
gdp_deflator          0.39755  1 0.53
unemployement         0.27992  1 0.60
gdp_growth            0.65572  1 0.42
LTMTA:tt              0.34389  1 0.56
PRICE:tt              0.56167  1 0.45
debt_ratio:tt         1.85319  1 0.17
GLOBAL               11.03035 23 0.98

Remaining financial variables violating PH assumption:
None - All financial variables now satisfy PH assumption

Remaining macro variables violating PH assumption:
None - All macro variables now satisfy PH assumption

SUCCESS: Global test now supports the PH assumption (p >= 0.05).
# Re-check PH assumption for acquisition model with time interactions
if(length(grep(":tt", names(coef(final_acquisition_model)))) > 0) {
  cat("\nRe-testing PH assumption for acquisition model with time interactions:\n")
  ph_test_final_acquisition <- cox.zph(final_acquisition_model)
  print(ph_test_final_acquisition)
  
  # Check if any remaining violations, separated by variable type
  remaining_violators <- rownames(ph_test_final_acquisition$table)[
    ph_test_final_acquisition$table[, "p"] < 0.05 & 
    rownames(ph_test_final_acquisition$table) != "GLOBAL"
  ]
  
  remaining_fin_violators <- remaining_violators[remaining_violators %in% financial_covariates]
  remaining_macro_violators <- remaining_violators[remaining_violators %in% macro_covariates]
  
  cat("\nRemaining financial variables violating PH assumption:\n")
  if(length(remaining_fin_violators) > 0) {
    print(remaining_fin_violators)
  } else {
    cat("None - All financial variables now satisfy PH assumption\n")
  }
  
  cat("\nRemaining macro variables violating PH assumption:\n")
  if(length(remaining_macro_violators) > 0) {
    print(remaining_macro_violators)
    cat("\nNote: Persistent violations in macro variables may indicate complex business cycle effects\n")
    cat("that might require more sophisticated modeling approaches (e.g., stratification or frailty models).\n")
  } else {
    cat("None - All macro variables now satisfy PH assumption\n")
  }
  
  # Check global test
  if(ph_test_final_acquisition$table["GLOBAL", "p"] < 0.05) {
    cat("\nWARNING: Global test still indicates PH violations (p < 0.05).\n")
    cat("Further refinements might be needed, especially for economic cycle dynamics.\n")
  } else {
    cat("\nSUCCESS: Global test now supports the PH assumption (p >= 0.05).\n")
  }
}

Re-testing PH assumption for acquisition model with time interactions:
                        chisq df       p
LTMTA                1.27e-01  1 0.72127
NIMTA                2.31e+00  1 0.12855
PRICE                8.70e+00  1 0.00318
MBE                  1.13e+00  1 0.28767
debt_ratio           7.51e-01  1 0.38616
current_ratio        1.55e-04  1 0.99008
cash_to_assets       2.89e-01  1 0.59083
wc_ratio             7.53e-01  1 0.38546
gp_margin            6.45e+00  1 0.01112
asset_turnover       5.78e+00  1 0.01616
receivables_turnover 9.36e-01  1 0.33341
EBIT_VOL_3Y          1.12e+01  1 0.00081
gdp_deflator         5.75e+00  1 0.01652
unemployement        1.11e+01  1 0.00089
gdp_growth           7.10e+00  1 0.00770
unemployement:tt     1.74e+01  1 3.1e-05
wc_ratio:tt          1.42e+00  1 0.23271
asset_turnover:tt    9.55e+00  1 0.00200
GLOBAL               8.44e+01 18 1.4e-10

Remaining financial variables violating PH assumption:
[1] "PRICE"          "gp_margin"      "asset_turnover" "EBIT_VOL_3Y"   

Remaining macro variables violating PH assumption:
[1] "gdp_deflator"  "unemployement" "gdp_growth"   

Note: Persistent violations in macro variables may indicate complex business cycle effects
that might require more sophisticated modeling approaches (e.g., stratification or frailty models).

WARNING: Global test still indicates PH violations (p < 0.05).
Further refinements might be needed, especially for economic cycle dynamics.

We compare the model fit of the final models with time-dependent coefficients to the original models without time interactions. We will look at AIC, BIC, concordance, and perform a likelihood ratio test to see if the addition of time interactions improved the model fit.

#-------------------------------------------------------------
# 10.6: Compare Model Fit Before and After Time Interactions
#-------------------------------------------------------------
cat("\n10.6: Compare Model Fit Before and After Time Interactions\n")

10.6: Compare Model Fit Before and After Time Interactions
cat("--------------------------------------\n")
--------------------------------------
# Compare bankruptcy models
if(length(grep(":tt", names(coef(final_bankruptcy_model)))) > 0) {
  cat("Comparing bankruptcy models before and after adding time interactions:\n")
  original_model <- best_models$bankruptcy$model
  
  # Compare AIC
  aic_original <- AIC(original_model)
  aic_final <- AIC(final_bankruptcy_model)
  
  # Compare BIC
  bic_original <- BIC(original_model)
  bic_final <- BIC(final_bankruptcy_model)
  
  # Compare concordance
  conc_original <- original_model$concordance["concordance"]
  conc_final <- final_bankruptcy_model$concordance["concordance"]
  
  # Likelihood ratio test
  lr_test <- anova(original_model, final_bankruptcy_model)
  
  cat("AIC: Original =", round(aic_original, 2), "vs. With time interactions =", round(aic_final, 2), 
      ifelse(aic_final < aic_original, " (improved)", " (not improved)"), "\n")
  
  cat("BIC: Original =", round(bic_original, 2), "vs. With time interactions =", round(bic_final, 2), 
      ifelse(bic_final < bic_original, " (improved)", " (not improved)"), "\n")
  
  cat("Concordance: Original =", round(conc_original, 4), "vs. With time interactions =", round(conc_final, 4),
      ifelse(conc_final > conc_original, " (improved)", " (not improved)"), "\n")
  
  cat("Likelihood ratio test:\n")
  print(lr_test)
  
  # Check how many time interactions are for macro variables
  macro_time_terms <- grep(":tt", names(coef(final_bankruptcy_model)), value = TRUE)
  macro_time_terms <- macro_time_terms[sapply(macro_time_terms, function(term) {
    var_name <- strsplit(term, ":")[[1]][1]
    return(var_name %in% macro_covariates)
  })]
  
  if(length(macro_time_terms) > 0) {
    cat("\nContribution of macro variable time interactions:\n")
    
    # Create a model with only financial time interactions
    no_macro_time_formula <- formula(final_bankruptcy_model)
    for(term in macro_time_terms) {
      no_macro_time_formula <- update(no_macro_time_formula, paste0(". ~ . - ", term))
    }
    
    no_macro_time_model <- coxph(no_macro_time_formula, data = data, ties = "efron")
    
    # Compare with full model
    macro_time_lr_test <- anova(no_macro_time_model, final_bankruptcy_model)
    
    cat("Likelihood ratio test for macro variable time interactions:\n")
    print(macro_time_lr_test)
    
    if(macro_time_lr_test[2, "Pr(>|Chi|)"] < 0.05) {
      cat("The time-varying effects of macroeconomic variables significantly improve the model (p < 0.05).\n")
    } else {
      cat("The time-varying effects of macroeconomic variables do not significantly improve the model (p >= 0.05).\n")
    }
  }
}
Comparing bankruptcy models before and after adding time interactions:
AIC: Original = 1596.41 vs. With time interactions = 1593.76  (improved) 
BIC: Original = 1659.82 vs. With time interactions = 1666.68  (not improved) 
Concordance: Original = 0.864 vs. With time interactions = 0.8636  (not improved) 
Likelihood ratio test:
Analysis of Deviance Table
 Cox model: response is  Surv(tstart, tstop, bankruptcy)
 Model 1: ~ LTMTA + NIMTA + CASHMTA + PRICE + MBE + debt_ratio + debt_service + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + intangibility + ebit_growth + EBIT_VOL_3Y + z_score + gdp_deflator + unemployement + gdp_growth + strata(gsector)
 Model 2: ~ LTMTA + NIMTA + CASHMTA + PRICE + MBE + debt_ratio + debt_service + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + intangibility + ebit_growth + EBIT_VOL_3Y + z_score + gdp_deflator + unemployement + gdp_growth + strata(gsector) + LTMTA:tt + PRICE:tt + debt_ratio:tt
   loglik  Chisq Df Pr(>|Chi|)  
1 -778.20                       
2 -773.88 8.6503  3    0.03432 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
# Compare acquisition models
if(length(grep(":tt", names(coef(final_acquisition_model)))) > 0) {
  cat("\nComparing acquisition models before and after adding time interactions:\n")
  original_model <- best_models$acquisition$model
  
  # Compare AIC
  aic_original <- AIC(original_model)
  aic_final <- AIC(final_acquisition_model)
  
  # Compare BIC
  bic_original <- BIC(original_model)
  bic_final <- BIC(final_acquisition_model)
  
  # Compare concordance
  conc_original <- original_model$concordance["concordance"]
  conc_final <- final_acquisition_model$concordance["concordance"]
  
  # Likelihood ratio test
  lr_test <- anova(original_model, final_acquisition_model)
  
  cat("AIC: Original =", round(aic_original, 2), "vs. With time interactions =", round(aic_final, 2), 
      ifelse(aic_final < aic_original, " (improved)", " (not improved)"), "\n")
  
  cat("BIC: Original =", round(bic_original, 2), "vs. With time interactions =", round(bic_final, 2), 
      ifelse(bic_final < bic_original, " (improved)", " (not improved)"), "\n")
  
  cat("Concordance: Original =", round(conc_original, 4), "vs. With time interactions =", round(conc_final, 4),
      ifelse(conc_final > conc_original, " (improved)", " (not improved)"), "\n")
  
  cat("Likelihood ratio test:\n")
  print(lr_test)
  
  # Check how many time interactions are for macro variables
  macro_time_terms <- grep(":tt", names(coef(final_acquisition_model)), value = TRUE)
  macro_time_terms <- macro_time_terms[sapply(macro_time_terms, function(term) {
    var_name <- strsplit(term, ":")[[1]][1]
    return(var_name %in% macro_covariates)
  })]
  
  if(length(macro_time_terms) > 0) {
    cat("\nContribution of macro variable time interactions:\n")
    
    # Create a model with only financial time interactions
    no_macro_time_formula <- formula(final_acquisition_model)
    for(term in macro_time_terms) {
      no_macro_time_formula <- update(no_macro_time_formula, paste0(". ~ . - ", term))
    }
    
    no_macro_time_model <- coxph(no_macro_time_formula, data = data, ties = "efron")
    
    # Compare with full model
    macro_time_lr_test <- anova(no_macro_time_model, final_acquisition_model)
    
    cat("Likelihood ratio test for macro variable time interactions:\n")
    print(macro_time_lr_test)
    
    if(macro_time_lr_test[2, "Pr(>|Chi|)"] < 0.05) {
      cat("The time-varying effects of macroeconomic variables significantly improve the model (p < 0.05).\n")
    } else {
      cat("The time-varying effects of macroeconomic variables do not significantly improve the model (p >= 0.05).\n")
    }
  }
}

Comparing acquisition models before and after adding time interactions:
AIC: Original = 24293.78 vs. With time interactions = 24259.44  (improved) 
BIC: Original = 24378.91 vs. With time interactions = 24361.59  (improved) 
Concordance: Original = 0.6379 vs. With time interactions = 0.6428  (improved) 
Likelihood ratio test:
Analysis of Deviance Table
 Cox model: response is  Surv(tstart, tstop, acquisition)
 Model 1: ~ LTMTA + NIMTA + PRICE + MBE + debt_ratio + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + EBIT_VOL_3Y + gdp_deflator + unemployement + gdp_growth + strata(gsector)
 Model 2: ~ LTMTA + NIMTA + PRICE + MBE + debt_ratio + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + EBIT_VOL_3Y + gdp_deflator + unemployement + gdp_growth + strata(gsector) + unemployement:tt + wc_ratio:tt + asset_turnover:tt
  loglik Chisq Df Pr(>|Chi|)    
1 -12132                        
2 -12112 40.34  3  9.025e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Contribution of macro variable time interactions:
Likelihood ratio test for macro variable time interactions:
Analysis of Deviance Table
 Cox model: response is  Surv(tstart, tstop, acquisition)
 Model 1: ~ LTMTA + NIMTA + PRICE + MBE + debt_ratio + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + EBIT_VOL_3Y + gdp_deflator + unemployement + gdp_growth + strata(gsector) + wc_ratio:tt + asset_turnover:tt
 Model 2: ~ LTMTA + NIMTA + PRICE + MBE + debt_ratio + current_ratio + cash_to_assets + wc_ratio + gp_margin + asset_turnover + receivables_turnover + EBIT_VOL_3Y + gdp_deflator + unemployement + gdp_growth + strata(gsector) + unemployement:tt + wc_ratio:tt + asset_turnover:tt
  loglik  Chisq Df Pr(>|Chi|)    
1 -12120                         
2 -12112 17.126  1  3.499e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
The time-varying effects of macroeconomic variables significantly improve the model (p < 0.05).

Again, included only for sake of completeness!

Finally, we will visualize the time-varying effects of the macroeconomic variables in both models. We will create plots that show how the hazard ratios change over time for different reference values of the macroeconomic variables. This will help us understand the dynamic nature of these effects and their implications for business cycles. You can also see all the time-varying effects of the macroeconomic variables in the final models. We will create plots that show how the hazard ratios change over time for different reference values of the macroeconomic variables. This will help us understand the dynamic nature of these effects and their implications for business cycles.

# Function to create enhanced visualization of time-varying effects
visualize_time_varying_effects <- function(model, model_name) {
  cat("Visualizing time-varying effects for", model_name, "model...\n")
  
  # Extract coefficients
  coefs <- coef(model)
  
  # Identify time interaction terms
  time_terms <- names(coefs)[grepl(":tt", names(coefs))]
  
  if(length(time_terms) == 0) {
    cat("No time-varying effects found in the", model_name, "model.\n")
    return(NULL)
  }
  
  # Extract base variable names
  main_terms <- unique(sapply(strsplit(time_terms, ":"), `[`, 1))
  
  # Separate financial and macro variables
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  fin_vars <- main_terms[!main_terms %in% macro_vars]
  macro_terms <- main_terms[main_terms %in% macro_vars]
  
  cat("Identified", length(fin_vars), "financial variables with time-varying effects:\n")
  if(length(fin_vars) > 0) cat(paste(fin_vars, collapse = ", "), "\n\n")
  
  cat("Identified", length(macro_terms), "macroeconomic variables with time-varying effects:\n")
  if(length(macro_terms) > 0) cat(paste(macro_terms, collapse = ", "), "\n\n")
  
  # Process all variables with time-varying effects
  all_vars <- list(
    Financial = fin_vars,
    Macro = macro_terms
  )
  
  # Create mapping for variable names to descriptive labels
  var_labels <- list(
    "LTMTA" = "Liabilities-to-Market Value\n(Higher = More Leverage)",
    "NIMTA" = "Net Income-to-Market Value\n(Higher = Better Profitability)",
    "CASHMTA" = "Cash-to-Market Value\n(Higher = More Liquidity)",
    "PRICE" = "Stock Price\n(Higher = Better Market Perception)",
    "MBE" = "Market-to-Book Equity\n(Higher = Growth Expectations)",
    "RSIZE" = "Relative Size\n(Higher = Larger Company)",
    "debt_ratio" = "Debt Ratio\n(Higher = More Leverage)",
    "current_ratio" = "Current Ratio\n(Higher = Better Liquidity)",
    "quick_ratio" = "Quick Ratio\n(Higher = Better Liquidity)",
    "z_score" = "Altman Z-Score\n(Higher = Lower Bankruptcy Risk)",
    "gdp_growth" = "GDP Growth\n(Higher = Stronger Economy)",
    "gdp_deflator" = "GDP Deflator\n(Higher = Higher Inflation)",
    "unemployement" = "Unemployment Rate\n(Higher = Weaker Labor Market)"
  )
  
  # Process variables by category
  for(var_type in names(all_vars)) {
    variables <- all_vars[[var_type]]
    
    if(length(variables) == 0) next
    
    cat("\nVisualizing", tolower(var_type), "variable time effects:\n")
    
    # Create multi-panel layout for overview plots
    n_vars <- length(variables)
    n_cols <- min(2, n_vars)
    n_rows <- ceiling(n_vars / n_cols)
    
    par(mfrow = c(n_rows, n_cols), mar = c(4, 4, 3, 1) + 0.1)
    
    # Create overview plots for each variable
    for(var in variables) {
      cat("  -", var, "\n")
      
      # Get coefficients
      main_coef <- coefs[var]
      time_coef <- coefs[paste0(var, ":tt")]
      
      # Set up time sequence
      max_time <- max(data$tstop)
      times <- seq(0, max_time, length.out = 100)
      
      # Calculate hazard ratios for 1-unit change in the variable over time
      hrs <- exp(main_coef + time_coef * times)
      
      # Get descriptive label for the variable
      var_title <- if(var %in% names(var_labels)) var_labels[[var]] else var
      
      # Create plot
      plot(times, hrs, type = "l", lwd = 2, col = "darkblue",
           main = var_title,
           xlab = "Time (Years)", ylab = "Hazard Ratio",
           ylim = c(min(0.5, min(hrs)), max(2, max(hrs))))
      
      # Add reference line at HR = 1
      abline(h = 1, lty = 2, col = "darkgray")
      
      # Add grid for readability
      grid()
      
      # Calculate and mark crossover point (if any)
      if(sign(main_coef) != sign(time_coef) && time_coef != 0) {
        crossover <- -main_coef / time_coef
        if(crossover > 0 && crossover < max(times)) {
          points(crossover, 1, pch = 16, col = "red", cex = 1.5)
          text(crossover, 1.1, paste("Crossover at", round(crossover, 1), "years"),
               col = "red", cex = 0.8)
        }
      }
      
      # Color the line based on the current effect direction
      points(times[1], hrs[1], pch = 16, col = ifelse(hrs[1] > 1, "red", "green4"), cex = 1.2)
      points(times[length(times)], hrs[length(times)], pch = 16, 
             col = ifelse(hrs[length(times)] > 1, "red", "green4"), cex = 1.2)
      
      # Add time effect interpretation
      direction <- ifelse(main_coef > 0, "increases", "decreases")
      change <- ifelse(sign(main_coef) == sign(time_coef), "strengthens", "weakens")
      
      mtext(paste0("Effect ", change, " over time"), side = 3, line = 0.5, cex = 0.7, 
            col = ifelse(change == "strengthens", "darkred", "darkblue"))
      
      # Add business insights based on variable type
      if(var_type == "Financial") {
        add_financial_insights(var, model_name, change, times, hrs, max_time)
      } else {
        add_macro_insights(var, model_name, change, times, hrs, max_time)
      }
    }
    
    # Reset plotting parameters
    par(mfrow = c(1, 1), mar = c(5, 4, 4, 2) + 0.1)
    
    # Create detailed plots with quantile-based reference values for each variable
    for(var in variables) {
      create_detailed_plot(model, var, var_type, model_name, var_labels)
    }
  }
  
  # Find and highlight the most important time-varying variable
  if(length(main_terms) > 0) {
    # Find the variable with largest absolute time coefficient
    abs_time_coefs <- abs(coefs[paste0(main_terms, ":tt")])
    most_important_idx <- which.max(abs_time_coefs)
    most_important_var <- main_terms[most_important_idx]
    most_important_type <- if(most_important_var %in% macro_vars) "Macro" else "Financial"
    
    cat("\nMost important time-varying variable:", most_important_var, "(", most_important_type, ")\n")
    cat("Creating enhanced visualization for most important variable...\n")
    
    # Create enhanced plot with business insights for most important variable
    create_enhanced_plot(model, most_important_var, most_important_type, model_name, var_labels)
  }
  
  cat("Time-varying effects visualization completed for", model_name, "model.\n")
}

# Helper function to add financial insights to plots
add_financial_insights <- function(var, model_name, change, times, hrs, max_time) {
  y_pos <- ifelse(hrs[length(times)] > hrs[1], 0.85 * max(hrs), 0.5)
  
  # Add financial insight based on variable and model type
  if(var == "LTMTA" || var == "debt_ratio") {
    if(model_name == "Bankruptcy") {
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Leverage matters\nmost for young firms", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Leverage becomes\nmore risky with age", 
             cex = 0.7, col = "darkred")
      }
    } else {  # Acquisition
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Acquirers focus on\ndebt early in lifecycle", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Maturity increases\nleverage significance", 
             cex = 0.7, col = "darkred")
      }
    }
  } else if(var == "NIMTA") {
    if(model_name == "Bankruptcy") {
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Profitability matters\nmost for young firms", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Profitability becomes\nmore critical with age", 
             cex = 0.7, col = "darkred")
      }
    }
  } else if(var == "CASHMTA" || var == "current_ratio" || var == "quick_ratio") {
    if(model_name == "Bankruptcy") {
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Liquidity most\ncritical early on", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Liquidity becomes\nmore important with age", 
             cex = 0.7, col = "darkred")
      }
    } else {  # Acquisition
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Cash attracts acquirers\nmost in early years", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Cash becomes more\nattractive with maturity", 
             cex = 0.7, col = "darkred")
      }
    }
  } else if(var == "z_score") {
    if(model_name == "Bankruptcy") {
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Z-score most predictive\nfor young firms", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Z-score becomes more\npredictive with age", 
             cex = 0.7, col = "darkred")
      }
    }
  }
}

# Helper function to add macroeconomic insights to plots
add_macro_insights <- function(var, model_name, change, times, hrs, max_time) {
  y_pos <- ifelse(hrs[length(times)] > hrs[1], 0.85 * max(hrs), 0.5)
  
  # Add macro insight based on variable and model type
  if(var == "gdp_growth") {
    if(model_name == "Bankruptcy") {
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Economic growth most\nprotective for new firms", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Growth protection\nincreases with firm age", 
             cex = 0.7, col = "darkred")
      }
    } else {  # Acquisition
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Growth spurs acquisitions\nmore for young firms", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Growth effect on M&A\nincreases with age", 
             cex = 0.7, col = "darkred")
      }
    }
  } else if(var == "unemployement") {
    if(model_name == "Bankruptcy") {
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "High unemployment most\ndangerous for new firms", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Labor market impact\nincreases over time", 
             cex = 0.7, col = "darkred")
      }
    }
  } else if(var == "gdp_deflator") {
    if(model_name == "Bankruptcy") {
      if(change == "weakens") {
        text(max_time * 0.5, y_pos, "Inflation most impactful\nfor young companies", 
             cex = 0.7, col = "darkred")
      } else {
        text(max_time * 0.5, y_pos, "Inflation sensitivity\nincreases with age", 
             cex = 0.7, col = "darkred")
      }
    }
  }
}

# Helper function to create detailed plots with quantile-based reference values
create_detailed_plot <- function(model, var_name, var_type, model_name, var_labels) {
  # Extract coefficients
  coefs <- coef(model)
  main_coef <- coefs[var_name]
  time_coef <- coefs[paste0(var_name, ":tt")]
  
  # Get descriptive label
  var_label <- if(var_name %in% names(var_labels)) var_labels[[var_name]] else var_name
  var_label <- strsplit(var_label, "\n")[[1]][1]  # Just use first line
  
  # Create sequence of time points
  max_time <- max(data$tstop)
  time_points <- seq(0, max_time, by = 0.1)
  
  # Get variable distribution for reference values
  if(var_name %in% colnames(data)) {
    q25 <- quantile(data[[var_name]], 0.25, na.rm = TRUE)
    q50 <- quantile(data[[var_name]], 0.50, na.rm = TRUE)
    q75 <- quantile(data[[var_name]], 0.75, na.rm = TRUE)
    
    # Calculate hazard ratios for each reference value over time
    hr_q25 <- exp((main_coef + time_coef * time_points) * q25)
    hr_q50 <- exp((main_coef + time_coef * time_points) * q50)
    hr_q75 <- exp((main_coef + time_coef * time_points) * q75)
    
    # Plot title
    if(var_type == "Macro") {
      title <- paste("Time-Varying Economic Effect of", var_label)
    } else {
      title <- paste("Time-Varying Effect of", var_label, "on", model_name, "Risk")
    }
    
    # Create plot
    plot(time_points, hr_q50, type = "l", lwd = 2, col = "blue",
         main = title,
         xlab = "Time (Years)", ylab = "Hazard Ratio",
         ylim = c(min(0.5, min(hr_q25, hr_q50, hr_q75, na.rm = TRUE)), 
                 max(2, max(hr_q25, hr_q50, hr_q75, na.rm = TRUE))))
    
    # Add lines for other quantiles
    lines(time_points, hr_q25, lwd = 2, col = "green3", lty = 2)
    lines(time_points, hr_q75, lwd = 2, col = "red", lty = 2)
    
    # Add reference line at HR = 1
    abline(h = 1, lty = 3, col = "darkgray")
    
    # Add grid
    grid()
    
    # Add legend
    legend("topright", 
           legend = c(paste("25th percentile (", round(q25, 2), ")", sep = ""),
                     paste("Median (", round(q50, 2), ")", sep = ""),
                     paste("75th percentile (", round(q75, 2), ")", sep = "")),
           col = c("green3", "blue", "red"), 
           lty = c(2, 1, 2), 
           lwd = 2, 
           bty = "n")
    
    # Add crossover point if it exists
    if(sign(main_coef) != sign(time_coef) && time_coef != 0) {
      crossover <- -main_coef / time_coef
      if(crossover > 0 && crossover < max(time_points)) {
        abline(v = crossover, lty = 2, col = "purple")
        text(crossover, par("usr")[3] + 0.1 * diff(par("usr")[3:4]), 
             paste("Effect reverses at\n", round(crossover, 1), "years"),
             col = "purple")
      }
    }
    
    # Add variable-specific interpretation
    add_variable_interpretation(var_name, var_type, model_name, time_coef)
  }
}

# Helper function for enhanced plot of most important variable
create_enhanced_plot <- function(model, var_name, var_type, model_name, var_labels) {
  # Extract coefficients
  coefs <- coef(model)
  main_coef <- coefs[var_name]
  time_coef <- coefs[paste0(var_name, ":tt")]
  
  # Get variable range from data
  if(var_name %in% colnames(data)) {
    var_mean <- mean(data[[var_name]], na.rm = TRUE)
    var_sd <- sd(data[[var_name]], na.rm = TRUE)
    
    # Calculate representative values
    var_low <- max(0, var_mean - var_sd)  # Ensure non-negative for ratios
    var_high <- var_mean + var_sd
    var_median <- median(data[[var_name]], na.rm = TRUE)
    
    # Set up time sequence
    max_time <- max(data$tstop)
    times <- seq(0, max_time, length.out = 100)
    
    # Calculate HR for different variable values
    hr_low <- exp((main_coef + time_coef * times) * var_low)
    hr_median <- exp((main_coef + time_coef * times) * var_median)
    hr_high <- exp((main_coef + time_coef * times) * var_high)
    
    # Get descriptive label
    var_label <- if(var_name %in% names(var_labels)) {
      strsplit(var_labels[[var_name]], "\n")[[1]][1]  # Just use first line
    } else {
      var_name
    }
    
    # Create enhanced plot
    plot(times, hr_median, type = "l", lwd = 2, col = "blue",
         main = paste("Detailed Time-Varying Effect of", var_label, "on", model_name, "Risk"),
         xlab = "Time (Years)", ylab = "Hazard Ratio",
         ylim = c(min(0.5, min(hr_low, hr_median, hr_high, na.rm = TRUE)), 
                 max(2, max(hr_low, hr_median, hr_high, na.rm = TRUE))))
    
    # Add lines for low and high values
    lines(times, hr_low, lwd = 2, col = "green3", lty = 2)
    lines(times, hr_high, lwd = 2, col = "red", lty = 2)
    
    # Add reference line at HR = 1
    abline(h = 1, lty = 3, col = "darkgray")
    
    # Add grid
    grid()
    
    # Add legend
    legend("topright", 
           legend = c(paste("Low", var_name, "(", round(var_low, 2), ")"),
                     paste("Median", var_name, "(", round(var_median, 2), ")"),
                     paste("High", var_name, "(", round(var_high, 2), ")")),
           col = c("green3", "blue", "red"), 
           lty = c(2, 1, 2), 
           lwd = 2, 
           bty = "n")
    
    # Add crossover point if any
    if(sign(main_coef) != sign(time_coef) && time_coef != 0) {
      crossover <- -main_coef / time_coef
      if(crossover > 0 && crossover < max(times)) {
        abline(v = crossover, lty = 2, col = "purple")
        text(crossover, par("usr")[3] + 0.1 * diff(par("usr")[3:4]), 
             paste("Effect reverses at\n", round(crossover, 1), "years"),
             col = "purple")
      }
    }
    
    # Add detailed business interpretation
    add_detailed_interpretation(var_name, var_type, model_name, time_coef)
  }
}

# Helper function to add variable-specific interpretation
add_variable_interpretation <- function(var_name, var_type, model_name, time_coef) {
  if(var_type == "Macro") {
    if(var_name == "gdp_growth") {
      if(time_coef > 0) {
        mtext("Economic growth effect strengthens over time", 
              side = 3, line = 0.5, cex = 0.8)
      } else {
        mtext("Economic growth effect weakens over time", 
              side = 3, line = 0.5, cex = 0.8)
      }
    } else if(var_name == "unemployement") {
      if(time_coef > 0) {
        mtext("Labor market effect strengthens over time", 
              side = 3, line = 0.5, cex = 0.8)
      } else {
        mtext("Labor market effect weakens over time", 
              side = 3, line = 0.5, cex = 0.8)
      }
    } else if(var_name == "gdp_deflator") {
      if(time_coef > 0) {
        mtext("Inflation effect strengthens over time", 
              side = 3, line = 0.5, cex = 0.8)
      } else {
        mtext("Inflation effect weakens over time", 
              side = 3, line = 0.5, cex = 0.8)
      }
    }
  } else {  # Financial variables
    if(var_name == "LTMTA" || var_name == "debt_ratio") {
      if(model_name == "Bankruptcy") {
        if(time_coef > 0) {
          mtext("Leverage becomes increasingly risky as companies mature", 
                side = 3, line = 0.5, cex = 0.8)
        } else {
          mtext("Leverage is most dangerous for young companies", 
                side = 3, line = 0.5, cex = 0.8) 
        }
      }
    } else if(var_name == "NIMTA") {
      if(model_name == "Bankruptcy") {
        if(time_coef > 0) {
          mtext("Profitability becomes more protective with age", 
                side = 3, line = 0.5, cex = 0.8)
        } else {
          mtext("Profitability is most protective for young companies", 
                side = 3, line = 0.5, cex = 0.8)
        }
      }
    } else if(var_name == "CASHMTA") {
      if(model_name == "Bankruptcy") {
        if(time_coef > 0) {
          mtext("Cash holdings become more important with company age", 
                side = 3, line = 0.5, cex = 0.8)
        } else {
          mtext("Cash is most critical for young company survival", 
                side = 3, line = 0.5, cex = 0.8)
        }
      } else if(model_name == "Acquisition") {
        if(time_coef > 0) {
          mtext("Cash becomes more attractive to acquirers as companies mature", 
                side = 3, line = 0.5, cex = 0.8)
        } else {
          mtext("Cash makes young companies more attractive acquisition targets", 
                side = 3, line = 0.5, cex = 0.8)
        }
      }
    }
  }
}

# Helper function to add detailed interpretation for the enhanced plot
add_detailed_interpretation <- function(var_name, var_type, model_name, time_coef) {
  if(var_type == "Macro") {
    if(var_name == "gdp_growth") {
      if(model_name == "Bankruptcy") {
        if(time_coef > 0) {
          mtext("INSIGHT: Mature firms become increasingly sensitive to economic conditions", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
          mtext("Firms may develop greater economic exposure as they age", 
                side = 3, line = 1.5, cex = 0.8)
        } else {
          mtext("INSIGHT: Young firms are more vulnerable to economic downturns", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
          mtext("New businesses have less buffer against poor economic conditions", 
                side = 3, line = 1.5, cex = 0.8)
        }
      } else {  # Acquisition
        if(time_coef > 0) {
          mtext("INSIGHT: Economic growth increasingly drives acquisition activity for mature firms", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
        } else {
          mtext("INSIGHT: Young companies are more likely acquisition targets during economic upswings", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
        }
      }
    } else if(var_name == "unemployement") {
      if(model_name == "Bankruptcy") {
        if(time_coef > 0) {
          mtext("INSIGHT: Labor market conditions become more critical for mature firms", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
        } else {
          mtext("INSIGHT: High unemployment most threatens young firms' survival", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
        }
      }
    }
  } else {  # Financial variables
    if(var_name == "LTMTA" || var_name == "debt_ratio") {
      if(model_name == "Bankruptcy") {
        if(time_coef > 0) {
          mtext("INSIGHT: Debt burden becomes increasingly problematic as firms mature", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
          mtext("Debt sustainability concerns grow over company lifecycle", 
                side = 3, line = 1.5, cex = 0.8)
        } else {
          mtext("INSIGHT: Early-stage leverage poses greatest bankruptcy risk", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
          mtext("Young firms have less capacity to service debt obligations", 
                side = 3, line = 1.5, cex = 0.8)
        }
      }
    } else if(var_name == "NIMTA" || var_name == "z_score") {
      if(model_name == "Bankruptcy") {
        if(time_coef > 0) {
          mtext("INSIGHT: Financial health metrics gain predictive power over company lifecycle", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
        } else {
          mtext("INSIGHT: Strong financials most critical for survival in early years", 
                side = 3, line = 0.5, cex = 0.9, col = "darkblue")
        }
      }
    }
  }
}

# Execute visualization for bankruptcy model
visualize_time_varying_effects(final_bankruptcy_model, "Bankruptcy")
Visualizing time-varying effects for Bankruptcy model...
Identified 3 financial variables with time-varying effects:
LTMTA, PRICE, debt_ratio 

Identified 0 macroeconomic variables with time-varying effects:

Visualizing financial variable time effects:
  - LTMTA 
  - PRICE 
  - debt_ratio 


Most important time-varying variable: LTMTA ( Financial )
Creating enhanced visualization for most important variable...

Time-varying effects visualization completed for Bankruptcy model.

# Execute visualization for acquisition model
visualize_time_varying_effects(final_acquisition_model, "Acquisition")
Visualizing time-varying effects for Acquisition model...
Identified 2 financial variables with time-varying effects:
wc_ratio, asset_turnover 

Identified 1 macroeconomic variables with time-varying effects:
unemployement 


Visualizing financial variable time effects:
  - wc_ratio 
  - asset_turnover 


Visualizing macro variable time effects:
  - unemployement 


Most important time-varying variable: wc_ratio ( Financial )
Creating enhanced visualization for most important variable...

Time-varying effects visualization completed for Acquisition model.

# Save final model information with time-varying effects data
final_models_with_time_effects <- list(
  bankruptcy = list(
    model = final_bankruptcy_model,
    time_terms = grep(":tt", names(coef(final_bankruptcy_model)), value = TRUE),
    fin_time_vars = intersect(
      sapply(strsplit(grep(":tt", names(coef(final_bankruptcy_model)), value = TRUE), ":"), `[`, 1),
      financial_covariates
    ),
    macro_time_vars = intersect(
      sapply(strsplit(grep(":tt", names(coef(final_bankruptcy_model)), value = TRUE), ":"), `[`, 1),
      macro_covariates
    ),
    ph_test = if(exists("ph_test_final_bankruptcy")) ph_test_final_bankruptcy else NULL
  ),
  acquisition = list(
    model = final_acquisition_model,
    time_terms = grep(":tt", names(coef(final_acquisition_model)), value = TRUE),
    fin_time_vars = intersect(
      sapply(strsplit(grep(":tt", names(coef(final_acquisition_model)), value = TRUE), ":"), `[`, 1),
      financial_covariates
    ),
    macro_time_vars = intersect(
      sapply(strsplit(grep(":tt", names(coef(final_acquisition_model)), value = TRUE), ":"), `[`, 1),
      macro_covariates
    ),
    ph_test = if(exists("ph_test_final_acquisition")) ph_test_final_acquisition else NULL
  )
)

Now we will create a function to analyze the hazard ratios and confidence intervals for the final models, including time-varying effects. We do this by extracting the coefficients and confidence intervals from the models, and then formatting them into a readable table. We will also categorize the variables into financial and macroeconomic categories for better interpretation.

#-------------------------------------------------------------
# 11.1: Hazard Ratios and Confidence Intervals (Improved)
#-------------------------------------------------------------
cat("\n11.1: HAZARD RATIOS WITH CONFIDENCE INTERVALS\n")

11.1: HAZARD RATIOS WITH CONFIDENCE INTERVALS
cat("--------------------------------------\n")
--------------------------------------
# Function to calculate and display hazard ratios in a meaningful way
analyze_hazard_ratios <- function(model, model_name) {
  # Get coefficient estimates and confidence intervals
  coefs <- summary(model)$coefficients
  conf_int <- confint(model)
  
  # Combine into a single matrix and convert to hazard ratios
  hr_matrix <- data.frame(
    "Variable" = rownames(coefs),
    "Coefficient" = coefs[, "coef"],
    "Hazard_Ratio" = exp(coefs[, "coef"]),
    "Lower_CI" = exp(conf_int[, 1]),
    "Upper_CI" = exp(conf_int[, 2]),
    "P_value" = coefs[, "Pr(>|z|)"],
    stringsAsFactors = FALSE
  )
  
  # Separate main effects from time interactions
  main_effects <- hr_matrix[!grepl(":tt", hr_matrix$Variable), ]
  time_effects <- hr_matrix[grepl(":tt", hr_matrix$Variable), ]
  
  # Add variable type classification
  main_effects$Type <- "Other"
  
  # Identify financial variables by category
  financial_categories <- list(
    "Solvency" = c("LTMTA", "debt_ratio", "debt_service", "z_score"),
    "Liquidity" = c("CASHMTA", "current_ratio", "cash_to_assets", "wc_ratio", "quick_ratio"),
    "Profitability" = c("NIMTA", "ebit_margin", "gp_margin", "asset_turnover", "ebit_growth"),
    "Market" = c("PRICE", "MBE", "RSIZE", "l_mkvalt"),
    "Other_Financial" = c("receivables_turnover", "intangibility", "EBIT_VOL_3Y", "l_at")
  )
  
  # Assign financial categories
  for(category_name in names(financial_categories)) {
    main_effects$Type[main_effects$Variable %in% financial_categories[[category_name]]] <- category_name
  }
  
  # Identify macro variables
  macro_categories <- list(
    "Economic_Growth" = c("gdp_growth"),
    "Inflation" = c("gdp_deflator"),
    "Labor_Market" = c("unemployement")
  )
  
  # Assign macro categories
  for(category_name in names(macro_categories)) {
    main_effects$Type[main_effects$Variable %in% macro_categories[[category_name]]] <- category_name
  }
  
  # Print results by category - Financial variables first
  cat("\n=== HAZARD RATIOS FOR", toupper(model_name), "MODEL ===\n\n")
  
  cat("FINANCIAL VARIABLES:\n")
  cat("====================\n\n")
  
  financial_categories_print <- c("Solvency", "Liquidity", "Profitability", "Market", "Other_Financial")
  
  for(category_name in financial_categories_print) {
    cat(gsub("_", " ", category_name), "Measures:\n")
    cat(paste(rep("-", nchar(gsub("_", " ", category_name)) + 10), collapse = ""), "\n")
    
    # Get variables in this category
    category_rows <- main_effects[main_effects$Type == category_name, ]
    
    if(nrow(category_rows) == 0) {
      cat("No variables from this category in the model.\n\n")
      next
    }
    
    # Sort by p-value for presentation
    category_rows <- category_rows[order(category_rows$P_value), ]
    
    # Print each variable in this category
    for(i in 1:nrow(category_rows)) {
      var <- category_rows$Variable[i]
      hr <- category_rows$Hazard_Ratio[i]
      p_val <- category_rows$P_value[i]
      ci_lower <- category_rows$Lower_CI[i]
      ci_upper <- category_rows$Upper_CI[i]
      
      # Determine effect direction and significance
      if(p_val < 0.01) {
        significance <- "strongly significantly"
      } else if(p_val < 0.05) {
        significance <- "significantly"
      } else if(p_val < 0.1) {
        significance <- "marginally significantly"
      } else {
        significance <- "not significantly"
      }
      
      direction <- ifelse(hr > 1, "increases", "decreases")
      
      # Calculate percentage change
      if(hr < 1) {
        pct_change <- (1 - hr) * 100
        effect <- paste0(significance, " decreases ", model_name, " risk by ", round(pct_change, 1), "%")
      } else {
        pct_change <- (hr - 1) * 100
        effect <- paste0(significance, " increases ", model_name, " risk by ", round(pct_change, 1), "%")
      }
      
      # Check for time interaction
      has_time_int <- paste0(var, ":tt") %in% time_effects$Variable
      time_note <- ""
      
      if(has_time_int) {
        time_row <- time_effects[time_effects$Variable == paste0(var, ":tt"), ]
        time_hr <- time_row$Hazard_Ratio
        
        if(time_hr < 1 && hr > 1) {
          time_note <- " (effect weakens over time)"
        } else if(time_hr > 1 && hr < 1) {
          time_note <- " (effect weakens over time)"
        } else if(time_hr > 1 && hr > 1) {
          time_note <- " (effect strengthens over time)"
        } else if(time_hr < 1 && hr < 1) {
          time_note <- " (effect strengthens over time)"
        }
      }
      
      # Print formatted result
      cat(sprintf("%-15s: HR = %5.2f (95%% CI: %5.2f-%5.2f), p = %7.4f - %s%s\n", 
                var, hr, ci_lower, ci_upper, p_val, effect, time_note))
    }
    cat("\n")
  }
  
  # Print macroeconomic variables separately with more detailed interpretation
  cat("\nMACROECONOMIC VARIABLES:\n")
  cat("=======================\n\n")
  
  macro_categories_print <- c("Economic_Growth", "Inflation", "Labor_Market")
  
  for(category_name in macro_categories_print) {
    # Create a more readable category name
    readable_name <- gsub("_", " ", category_name)
    cat(readable_name, ":\n")
    cat(paste(rep("-", nchar(readable_name) + 1), collapse = ""), "\n")
    
    # Get variables in this category
    category_rows <- main_effects[main_effects$Type == category_name, ]
    
    if(nrow(category_rows) == 0) {
      cat("No variables from this category in the model.\n\n")
      next
    }
    
    # Print each variable in this category with enhanced interpretation
    for(i in 1:nrow(category_rows)) {
      var <- category_rows$Variable[i]
      coef_val <- category_rows$Coefficient[i]
      hr <- category_rows$Hazard_Ratio[i]
      p_val <- category_rows$P_value[i]
      ci_lower <- category_rows$Lower_CI[i]
      ci_upper <- category_rows$Upper_CI[i]
      
      # Determine effect direction and significance
      if(p_val < 0.01) {
        significance <- "strong evidence that"
      } else if(p_val < 0.05) {
        significance <- "evidence that"
      } else if(p_val < 0.1) {
        significance <- "weak evidence that"
      } else {
        significance <- "no significant evidence that"
      }
      
      # Calculate percentage change
      if(hr < 1) {
        pct_change <- (1 - hr) * 100
        effect <- paste0("a 1-unit increase in ", var, " is associated with a ", 
                       round(pct_change, 1), "% decrease in ", model_name, " risk")
      } else {
        pct_change <- (hr - 1) * 100
        effect <- paste0("a 1-unit increase in ", var, " is associated with a ", 
                       round(pct_change, 1), "% increase in ", model_name, " risk")
      }
      
      # Check for time interaction
      has_time_int <- paste0(var, ":tt") %in% time_effects$Variable
      time_note <- ""
      
      if(has_time_int) {
        time_row <- time_effects[time_effects$Variable == paste0(var, ":tt"), ]
        time_coef <- time_row$Coefficient
        time_hr <- time_row$Hazard_Ratio
        time_p <- time_row$P_value
        
        if(time_p < 0.05) {
          if(time_coef > 0 && coef_val > 0) {
            time_note <- "\n   → This effect strengthens over time (companies become MORE sensitive to this economic factor as they age)"
          } else if(time_coef < 0 && coef_val < 0) {
            time_note <- "\n   → This effect strengthens over time (companies become MORE protected by this economic factor as they age)"
          } else if(time_coef < 0 && coef_val > 0) {
            time_note <- "\n   → This effect weakens over time (risk from this economic factor DECREASES as companies age)"
          } else if(time_coef > 0 && coef_val < 0) {
            time_note <- "\n   → This effect weakens over time (protection from this economic factor DECREASES as companies age)"
          }
        }
      }
      
      # Provide economic interpretation based on the variable
      econ_interp <- ""
      if(var == "gdp_growth") {
        if(hr < 1) {
          econ_interp <- paste0("\n   → Strong economic growth appears to be protective against company ", model_name)
        } else {
          econ_interp <- paste0("\n   → Surprisingly, stronger economic growth is associated with higher ", model_name, " risk")
        }
      } else if(var == "gdp_deflator") {
        if(hr < 1) {
          econ_interp <- paste0("\n   → Higher inflation appears to be protective against company ", model_name)
        } else {
          econ_interp <- paste0("\n   → Higher inflation is associated with increased company ", model_name, " risk")
        }
      } else if(var == "unemployement") {
        if(hr < 1) {
          econ_interp <- paste0("\n   → Higher unemployment appears to be protective against company ", model_name)
        } else {
          econ_interp <- paste0("\n   → Higher unemployment is associated with increased company ", model_name, " risk")
        }
      }
      
      # Print detailed result for macro variable
      cat(sprintf("%-15s: HR = %5.2f (95%% CI: %5.2f-%5.2f), p = %7.4f\n", 
                var, hr, ci_lower, ci_upper, p_val))
      cat("   Interpretation: There is", significance, effect, "\n")
      cat(econ_interp)
      cat(time_note)
      cat("\n\n")
    }
  }
  
  # Print time interaction effects summary
  if(nrow(time_effects) > 0) {
    cat("TIME-VARYING EFFECTS SUMMARY:\n")
    cat("============================\n\n")
    
    # Separate financial and macro time effects
    fin_time_effects <- time_effects[sapply(time_effects$Variable, function(v) {
      var_name <- sub(":tt", "", v)
      return(any(sapply(financial_categories, function(cat) var_name %in% cat)))
    }), ]
    
    macro_time_effects <- time_effects[sapply(time_effects$Variable, function(v) {
      var_name <- sub(":tt", "", v)
      return(any(sapply(macro_categories, function(cat) var_name %in% cat)))
    }), ]
    
    # Print financial time effects
    if(nrow(fin_time_effects) > 0) {
      cat("Financial Variables with Time-Varying Effects:\n")
      cat("-----------------------------------------\n")
      
      for(i in 1:nrow(fin_time_effects)) {
        var <- sub(":tt", "", fin_time_effects$Variable[i])
        hr <- fin_time_effects$Hazard_Ratio[i]
        p_val <- fin_time_effects$P_value[i]
        
        if(p_val < 0.05) {
          cat(var, "effect changes over time: HR per year =", round(hr, 3), 
              "(p =", format.pval(p_val, digits = 3), ")\n")
          
          # Calculate effects at different time points
          main_coef <- main_effects$Coefficient[main_effects$Variable == var]
          time_coef <- fin_time_effects$Coefficient[i]
          
          cat("  Effect at different time points:\n")
          times <- c(1, 3, 5, 10)
          for(t in times) {
            hr_at_time <- exp(main_coef + time_coef * t)
            cat("  At t =", t, "years: HR =", round(hr_at_time, 3), "\n")
          }
          cat("\n")
        }
      }
    }
    
    # Print macro time effects with business cycle interpretation
    if(nrow(macro_time_effects) > 0) {
      cat("Macroeconomic Variables with Time-Varying Effects:\n")
      cat("----------------------------------------------\n")
      
      for(i in 1:nrow(macro_time_effects)) {
        var <- sub(":tt", "", macro_time_effects$Variable[i])
        hr <- macro_time_effects$Hazard_Ratio[i]
        p_val <- macro_time_effects$P_value[i]
        
        if(p_val < 0.05) {
          cat(var, "effect changes over time: HR per year =", round(hr, 3), 
              "(p =", format.pval(p_val, digits = 3), ")\n")
          
          # Calculate effects at different time points
          main_coef <- main_effects$Coefficient[main_effects$Variable == var]
          time_coef <- macro_time_effects$Coefficient[i]
          
          cat("  Effect at different time points:\n")
          times <- c(1, 3, 5, 10)
          for(t in times) {
            hr_at_time <- exp(main_coef + time_coef * t)
            cat("  At t =", t, "years: HR =", round(hr_at_time, 3), "\n")
          }
          
          # Business cycle interpretation
          cat("  Business cycle implications:\n")
          if(var == "gdp_growth") {
            if(hr > 1) {
              cat("  - The influence of economic growth on", model_name, "risk INCREASES as companies mature\n")
              cat("  - Older companies become more sensitive to economic conditions\n")
            } else {
              cat("  - The influence of economic growth on", model_name, "risk DECREASES as companies mature\n")
              cat("  - Younger companies are more sensitive to economic conditions\n")
            }
          } else if(var == "unemployement") {
            if(hr > 1) {
              cat("  - The influence of labor market conditions on", model_name, "risk INCREASES over time\n")
              cat("  - Unemployment has a stronger effect on companies the longer they exist\n")
            } else {
              cat("  - The influence of labor market conditions on", model_name, "risk DECREASES over time\n")
              cat("  - Unemployment has its strongest effect on younger companies\n")
            }
          } else if(var == "gdp_deflator") {
            if(hr > 1) {
              cat("  - The influence of inflation on", model_name, "risk INCREASES as companies mature\n")
              cat("  - Older companies become more sensitive to inflation\n")
            } else {
              cat("  - The influence of inflation on", model_name, "risk DECREASES as companies mature\n")
              cat("  - Younger companies are more sensitive to inflation\n")
            }
          }
          cat("\n")
        }
      }
    }
  }
  
  # Return the hazard ratio data frames with type information
  return(list(main_effects = main_effects, time_effects = time_effects))
}

# Apply function to both models
cat("\n=== ANALYZING BANKRUPTCY MODEL ===\n")

=== ANALYZING BANKRUPTCY MODEL ===
bankruptcy_hrs <- analyze_hazard_ratios(final_bankruptcy_model, "bankruptcy")

=== HAZARD RATIOS FOR BANKRUPTCY MODEL ===

FINANCIAL VARIABLES:
====================

Solvency Measures:
------------------ 
LTMTA          : HR = 39.24 (95% CI:  8.46-182.08), p =  0.0000 - strongly significantly increases bankruptcy risk by 3823.8% (effect strengthens over time)
z_score        : HR =  1.03 (95% CI:  1.00- 1.06), p =  0.0227 - significantly increases bankruptcy risk by 3%
debt_ratio     : HR =  0.61 (95% CI:  0.25- 1.48), p =  0.2734 - not significantly decreases bankruptcy risk by 38.9% (effect weakens over time)
debt_service   : HR =  1.00 (95% CI:  0.95- 1.04), p =  0.8814 - not significantly decreases bankruptcy risk by 0.3%

Liquidity Measures:
------------------- 
wc_ratio       : HR =  0.33 (95% CI:  0.18- 0.63), p =  0.0006 - strongly significantly decreases bankruptcy risk by 66.9%
CASHMTA        : HR =  0.12 (95% CI:  0.02- 0.78), p =  0.0259 - significantly decreases bankruptcy risk by 87.9%
current_ratio  : HR =  1.09 (95% CI:  1.00- 1.18), p =  0.0403 - significantly increases bankruptcy risk by 8.7%
cash_to_assets : HR =  4.22 (95% CI:  0.71-25.08), p =  0.1137 - not significantly increases bankruptcy risk by 321.6%

Profitability Measures:
----------------------- 
NIMTA          : HR =  0.12 (95% CI:  0.05- 0.26), p =  0.0000 - strongly significantly decreases bankruptcy risk by 88.2%
asset_turnover : HR =  0.84 (95% CI:  0.70- 1.02), p =  0.0799 - marginally significantly decreases bankruptcy risk by 15.5%
gp_margin      : HR =  1.06 (95% CI:  0.97- 1.17), p =  0.1776 - not significantly increases bankruptcy risk by 6.5%
ebit_growth    : HR =  0.98 (95% CI:  0.93- 1.03), p =  0.4910 - not significantly decreases bankruptcy risk by 1.7%

Market Measures:
---------------- 
MBE            : HR =  0.85 (95% CI:  0.72- 1.00), p =  0.0540 - marginally significantly decreases bankruptcy risk by 15.1%
PRICE          : HR =  0.84 (95% CI:  0.69- 1.03), p =  0.0987 - marginally significantly decreases bankruptcy risk by 15.8% (effect strengthens over time)

Other Financial Measures:
------------------------- 
intangibility  : HR =  0.74 (95% CI:  0.49- 1.13), p =  0.1704 - not significantly decreases bankruptcy risk by 25.5%
receivables_turnover: HR =  1.00 (95% CI:  1.00- 1.00), p =  0.4827 - not significantly increases bankruptcy risk by 0.1%
EBIT_VOL_3Y    : HR =  1.00 (95% CI:  0.97- 1.02), p =  0.9065 - not significantly decreases bankruptcy risk by 0.2%


MACROECONOMIC VARIABLES:
=======================

Economic Growth :
---------------- 
gdp_growth     : HR =  1.10 (95% CI:  0.99- 1.23), p =  0.0840
   Interpretation: There is weak evidence that a 1-unit increase in gdp_growth is associated with a 10.4% increase in bankruptcy risk 

   → Surprisingly, stronger economic growth is associated with higher bankruptcy risk

Inflation :
---------- 
gdp_deflator   : HR =  0.84 (95% CI:  0.70- 1.00), p =  0.0511
   Interpretation: There is weak evidence that a 1-unit increase in gdp_deflator is associated with a 16.1% decrease in bankruptcy risk 

   → Higher inflation appears to be protective against company bankruptcy

Labor Market :
------------- 
unemployement  : HR =  0.93 (95% CI:  0.81- 1.06), p =  0.2777
   Interpretation: There is no significant evidence that a 1-unit increase in unemployement is associated with a 7.2% decrease in bankruptcy risk 

   → Higher unemployment appears to be protective against company bankruptcy

TIME-VARYING EFFECTS SUMMARY:
============================

Financial Variables with Time-Varying Effects:
-----------------------------------------
cat("\n=== ANALYZING ACQUISITION MODEL ===\n")

=== ANALYZING ACQUISITION MODEL ===
acquisition_hrs <- analyze_hazard_ratios(final_acquisition_model, "acquisition")

=== HAZARD RATIOS FOR ACQUISITION MODEL ===

FINANCIAL VARIABLES:
====================

Solvency Measures:
------------------ 
debt_ratio     : HR =  1.02 (95% CI:  0.80- 1.31), p =  0.8608 - not significantly increases acquisition risk by 2.3%
LTMTA          : HR =  0.97 (95% CI:  0.68- 1.39), p =  0.8622 - not significantly decreases acquisition risk by 3.1%

Liquidity Measures:
------------------- 
current_ratio  : HR =  0.93 (95% CI:  0.90- 0.95), p =  0.0000 - strongly significantly decreases acquisition risk by 7.1%
cash_to_assets : HR =  2.09 (95% CI:  1.52- 2.88), p =  0.0000 - strongly significantly increases acquisition risk by 109.1%
wc_ratio       : HR =  0.49 (95% CI:  0.33- 0.73), p =  0.0003 - strongly significantly decreases acquisition risk by 50.9% (effect weakens over time)

Profitability Measures:
----------------------- 
asset_turnover : HR =  0.83 (95% CI:  0.74- 0.93), p =  0.0011 - strongly significantly decreases acquisition risk by 17.1% (effect weakens over time)
gp_margin      : HR =  1.03 (95% CI:  1.01- 1.04), p =  0.0046 - strongly significantly increases acquisition risk by 2.6%
NIMTA          : HR =  1.21 (95% CI:  0.83- 1.75), p =  0.3217 - not significantly increases acquisition risk by 20.7%

Market Measures:
---------------- 
MBE            : HR =  0.79 (95% CI:  0.76- 0.83), p =  0.0000 - strongly significantly decreases acquisition risk by 20.6%
PRICE          : HR =  1.14 (95% CI:  1.10- 1.19), p =  0.0000 - strongly significantly increases acquisition risk by 14.2%

Other Financial Measures:
------------------------- 
receivables_turnover: HR =  1.00 (95% CI:  0.99- 1.00), p =  0.0008 - strongly significantly decreases acquisition risk by 0.3%
EBIT_VOL_3Y    : HR =  0.99 (95% CI:  0.98- 1.00), p =  0.0719 - marginally significantly decreases acquisition risk by 0.8%


MACROECONOMIC VARIABLES:
=======================

Economic Growth :
---------------- 
gdp_growth     : HR =  1.04 (95% CI:  1.01- 1.07), p =  0.0131
   Interpretation: There is evidence that a 1-unit increase in gdp_growth is associated with a 3.8% increase in acquisition risk 

   → Surprisingly, stronger economic growth is associated with higher acquisition risk

Inflation :
---------- 
gdp_deflator   : HR =  0.96 (95% CI:  0.92- 1.01), p =  0.0852
   Interpretation: There is weak evidence that a 1-unit increase in gdp_deflator is associated with a 3.7% decrease in acquisition risk 

   → Higher inflation appears to be protective against company acquisition

Labor Market :
------------- 
unemployement  : HR =  0.87 (95% CI:  0.82- 0.93), p =  0.0000
   Interpretation: There is strong evidence that a 1-unit increase in unemployement is associated with a 12.5% decrease in acquisition risk 

   → Higher unemployment appears to be protective against company acquisition
   → This effect weakens over time (protection from this economic factor DECREASES as companies age)

TIME-VARYING EFFECTS SUMMARY:
============================

Financial Variables with Time-Varying Effects:
-----------------------------------------
wc_ratio effect changes over time: HR per year = 1.048 (p = 0.000271 )
  Effect at different time points:
  At t = 1 years: HR = 0.514 
  At t = 3 years: HR = 0.565 
  At t = 5 years: HR = 0.619 
  At t = 10 years: HR = 0.781 

asset_turnover effect changes over time: HR per year = 1.013 (p = 0.00238 )
  Effect at different time points:
  At t = 1 years: HR = 0.84 
  At t = 3 years: HR = 0.862 
  At t = 5 years: HR = 0.885 
  At t = 10 years: HR = 0.944 

Macroeconomic Variables with Time-Varying Effects:
----------------------------------------------
unemployement effect changes over time: HR per year = 1.008 (p = 3.45e-05 )
  Effect at different time points:
  At t = 1 years: HR = 0.882 
  At t = 3 years: HR = 0.897 
  At t = 5 years: HR = 0.912 
  At t = 10 years: HR = 0.95 
  Business cycle implications:
  - The influence of labor market conditions on acquisition risk INCREASES over time
  - Unemployment has a stronger effect on companies the longer they exist
# Compare the impact of macroeconomic variables between models
cat("\n\n=== COMPARATIVE ANALYSIS OF MACROECONOMIC EFFECTS ===\n")


=== COMPARATIVE ANALYSIS OF MACROECONOMIC EFFECTS ===
cat("=================================================\n\n")
=================================================
# Extract macro variables from both models
bankruptcy_macro <- bankruptcy_hrs$main_effects[grep("gdp_|unemployement", bankruptcy_hrs$main_effects$Variable), ]
acquisition_macro <- acquisition_hrs$main_effects[grep("gdp_|unemployement", acquisition_hrs$main_effects$Variable), ]

# Variables in both models
common_vars <- intersect(bankruptcy_macro$Variable, acquisition_macro$Variable)

if(length(common_vars) > 0) {
  cat("Macroeconomic variables in both models:\n")
  cat("-------------------------------------\n")
  
  for(var in common_vars) {
    # Get HRs
    bk_hr <- bankruptcy_macro$Hazard_Ratio[bankruptcy_macro$Variable == var]
    acq_hr <- acquisition_macro$Hazard_Ratio[acquisition_macro$Variable == var]
    
    # Calculate percentage change
    bk_pct <- ifelse(bk_hr < 1, (1 - bk_hr) * 100, (bk_hr - 1) * 100)
    acq_pct <- ifelse(acq_hr < 1, (1 - acq_hr) * 100, (acq_hr - 1) * 100)
    
    # Get p-values
    bk_p <- bankruptcy_macro$P_value[bankruptcy_macro$Variable == var]
    acq_p <- acquisition_macro$P_value[acquisition_macro$Variable == var]
    
    # Check significance
    bk_sig <- ifelse(bk_p < 0.05, "significant", "not significant")
    acq_sig <- ifelse(acq_p < 0.05, "significant", "not significant")
    
    # Check direction
    bk_dir <- ifelse(bk_hr > 1, "increases", "decreases")
    acq_dir <- ifelse(acq_hr > 1, "increases", "decreases")
    
    cat(var, ":\n")
    cat("  - Bankruptcy effect: ", bk_dir, " risk by ", round(abs(bk_pct), 1), "% (", bk_sig, ", p=", round(bk_p, 4), ")\n", sep="")
    cat("  - Acquisition effect: ", acq_dir, " risk by ", round(abs(acq_pct), 1), "% (", acq_sig, ", p=", round(acq_p, 4), ")\n", sep="")
    
    # Check for opposite effects
    if((bk_hr > 1 && acq_hr < 1) || (bk_hr < 1 && acq_hr > 1)) {
      cat("  - NOTE: This economic factor has OPPOSITE effects on bankruptcy and acquisition\n")
    } else {
      cat("  - This economic factor affects both outcomes in the same direction\n")
    }
    
    # Check for time-varying effects in both models
    bk_time_var <- paste0(var, ":tt") %in% bankruptcy_hrs$time_effects$Variable
    acq_time_var <- paste0(var, ":tt") %in% acquisition_hrs$time_effects$Variable
    
    if(bk_time_var && acq_time_var) {
      cat("  - Time-varying effects present in BOTH models\n")
    } else if(bk_time_var) {
      cat("  - Time-varying effects present only in BANKRUPTCY model\n")
    } else if(acq_time_var) {
      cat("  - Time-varying effects present only in ACQUISITION model\n")
    } else {
      cat("  - No time-varying effects in either model\n")
    }
    cat("\n")
  }
}
Macroeconomic variables in both models:
-------------------------------------
gdp_deflator :
  - Bankruptcy effect: decreases risk by 16.1% (not significant, p=0.0511)
  - Acquisition effect: decreases risk by 3.7% (not significant, p=0.0852)
  - This economic factor affects both outcomes in the same direction
  - No time-varying effects in either model

unemployement :
  - Bankruptcy effect: decreases risk by 7.2% (not significant, p=0.2777)
  - Acquisition effect: decreases risk by 12.5% (significant, p=0)
  - This economic factor affects both outcomes in the same direction
  - Time-varying effects present only in ACQUISITION model

gdp_growth :
  - Bankruptcy effect: increases risk by 10.4% (not significant, p=0.084)
  - Acquisition effect: increases risk by 3.8% (significant, p=0.0131)
  - This economic factor affects both outcomes in the same direction
  - No time-varying effects in either model
# Variables unique to bankruptcy model
bk_only_vars <- setdiff(bankruptcy_macro$Variable, acquisition_macro$Variable)
if(length(bk_only_vars) > 0) {
  cat("\nMacroeconomic variables only in bankruptcy model:\n")
  cat("-----------------------------------------\n")
  
  for(var in bk_only_vars) {
    cat(var, "\n")
  }
  cat("\n")
}

# Variables unique to acquisition model
acq_only_vars <- setdiff(acquisition_macro$Variable, bankruptcy_macro$Variable)
if(length(acq_only_vars) > 0) {
  cat("\nMacroeconomic variables only in acquisition model:\n")
  cat("-----------------------------------------\n")
  
  for(var in acq_only_vars) {
    cat(var, "\n")
  }
  cat("\n")
}

# Save hazard ratio analysis results
hazard_ratio_analysis <- list(
  bankruptcy = bankruptcy_hrs,
  acquisition = acquisition_hrs
)

Same here, we will create a function to visualize the time-varying effects of the final models. This function will plot the hazard ratios over time for each variable, and provide detailed interpretations based on the variable type (financial or macroeconomic). We will also include a summary of the time-varying effects for both models.

#-------------------------------------------------------------
# 11.2: Model Fit Statistics with Macro Variable Contribution
#-------------------------------------------------------------
cat("\n11.2: MODEL FIT STATISTICS WITH MACRO CONTRIBUTION ANALYSIS\n")

11.2: MODEL FIT STATISTICS WITH MACRO CONTRIBUTION ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
# Function to calculate alternative concordance using AUC from ROC analysis
calculate_auc_concordance <- function(model, event_type) {
  # Create dataset with last observation for each company
  last_obs_data <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate risk scores
  risk_scores <- predict(model, newdata = last_obs_data, type = "risk")
  
  # Use ROC analysis as an alternative to concordance
  if(requireNamespace("pROC", quietly = TRUE)) {
    library(pROC)
    
    # Use the event indicator
    event_indicator <- last_obs_data[[event_type]]
    
    # Calculate ROC and AUC
    tryCatch({
      roc_obj <- roc(event_indicator, risk_scores, quiet = TRUE)
      auc_value <- as.numeric(auc(roc_obj))
      return(auc_value)
    }, error = function(e) {
      cat("Error calculating AUC:", e$message, "\n")
      return(NA)
    })
  } else {
    return(NA)
  }
}

# Function to create a version of the model without macro variables
create_no_macro_model <- function(model, event_type) {
  # Get model formula
  model_formula <- formula(model)
  
  # Extract all terms
  all_terms <- attr(terms(model), "term.labels")
  
  # Identify macro variables (including their time interactions)
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  macro_terms <- c()
  
  for(term in all_terms) {
    # Check if term contains any macro variable
    is_macro <- FALSE
    for(macro in macro_vars) {
      if(grepl(macro, term)) {
        is_macro <- TRUE
        break
      }
    }
    
    if(is_macro) {
      macro_terms <- c(macro_terms, term)
    }
  }
  
  # If no macro terms in model, return original model
  if(length(macro_terms) == 0) {
    return(model)
  }
  
  # Create formula without macro variables
  no_macro_formula <- model_formula
  for(term in macro_terms) {
    no_macro_formula <- update(no_macro_formula, paste0(". ~ . - ", term))
  }
  
  # Fit model without macro variables
  if(event_type == "bankruptcy") {
    no_macro_model <- coxph(no_macro_formula, data = data, ties = "efron")
  } else {
    no_macro_model <- coxph(no_macro_formula, data = data, ties = "efron")
  }
  
  return(no_macro_model)
}

# Function to analyze model fit with simpler concordance calculation
analyze_model_fit <- function(model, model_name, event_type) {
  # Extract standard model statistics
  model_summary <- summary(model)
  
  # Calculate AIC and BIC
  model_aic <- AIC(model)
  model_bic <- BIC(model)
  
  # Calculate pseudo R-squared (1 - exp(-LR/n))
  lr_test <- model_summary$logtest["test"]
  n <- model$n
  r_squared <- 1 - exp(-lr_test / n)
  
  # Calculate AUC as an alternative to concordance
  auc_value <- calculate_auc_concordance(model, event_type)
  
  # Create model without macro variables for comparison
  no_macro_model <- create_no_macro_model(model, event_type)
  no_macro_summary <- summary(no_macro_model)
  no_macro_aic <- AIC(no_macro_model)
  no_macro_r_squared <- 1 - exp(-no_macro_summary$logtest["test"] / no_macro_model$n)
  no_macro_auc <- calculate_auc_concordance(no_macro_model, event_type)
  
  # Get model terms to identify macro variables
  all_terms <- attr(terms(model), "term.labels")
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  macro_terms <- c()
  
  for(term in all_terms) {
    is_macro <- FALSE
    for(macro in macro_vars) {
      if(grepl(macro, term)) {
        is_macro <- TRUE
        break
      }
    }
    
    if(is_macro) {
      macro_terms <- c(macro_terms, term)
    }
  }
  
  # Print results with interpretation
  cat("\n=== MODEL FIT STATISTICS FOR", toupper(model_name), "MODEL ===\n\n")
  
  # Print macro variables in model
  cat("Macroeconomic Variables in Model:\n")
  cat("--------------------------------\n")
  if(length(macro_terms) > 0) {
    for(term in macro_terms) {
      cat("- ", term, "\n")
    }
  } else {
    cat("No macroeconomic variables in this model.\n")
  }
  cat("\n")
  
  cat("Discrimination Ability:\n")
  cat("-----------------------\n")
  
  # Use AUC instead of concordance
  cat("AUC (Area Under ROC Curve):", ifelse(is.na(auc_value), "Not available", round(auc_value, 4)), "\n")
  if(!is.na(auc_value)) {
    cat("Interpretation: ")
    if(auc_value > 0.8) {
      cat("Excellent discrimination (AUC > 0.8)\n")
    } else if(auc_value > 0.7) {
      cat("Good discrimination (0.7 < AUC < 0.8)\n")
    } else if(auc_value > 0.6) {
      cat("Fair discrimination (0.6 < AUC < 0.7)\n")
    } else {
      cat("Poor discrimination (AUC < 0.6)\n")
    }
  }
  cat("\n")
  
  # Model significance
  cat("Overall Model Significance:\n")
  cat("---------------------------\n")
  cat("Likelihood Ratio Test:", round(model_summary$logtest["test"], 2), 
      "on", model_summary$logtest["df"], "df, p-value:", 
      format.pval(model_summary$logtest["pvalue"], digits = 4), "\n")
  
  # Interpret LR test
  if(model_summary$logtest["pvalue"] < 0.05) {
    cat("Interpretation: Model is statistically significant (p < 0.05)\n")
  } else {
    cat("Interpretation: Model is not statistically significant (p >= 0.05)\n")
  }
  cat("\n")
  
  # Fit statistics with explanation
  cat("Goodness of Fit Measures:\n")
  cat("------------------------\n")
  cat("AIC:", round(model_aic, 2), "- Akaike Information Criterion, lower values indicate better fit\n")
  cat("BIC:", round(model_bic, 2), "- Bayesian Information Criterion, penalizes complexity more than AIC\n")
  cat("Pseudo R-squared:", round(r_squared, 4), 
      "- Proportion of null deviance explained by the model\n")
  
  # Interpret R-squared
  cat("Interpretation: ")
  if(r_squared > 0.3) {
    cat("Strong explanatory power for survival model (R² > 0.3)\n")
  } else if(r_squared > 0.15) {
    cat("Moderate explanatory power for survival model (0.15 < R² < 0.3)\n")
  } else if(r_squared > 0.05) {
    cat("Modest explanatory power for survival model (0.05 < R² < 0.15)\n")
  } else {
    cat("Limited explanatory power for survival model (R² < 0.05), but may still have good predictive value\n")
  }
  cat("\n")
  
  # Penalty term counts
  num_vars <- length(coef(model))
  num_strata <- length(grep("strata", names(model$means)))
  
  cat("Model Complexity:\n")
  cat("----------------\n")
  cat("Number of coefficients:", num_vars, "\n")
  cat("Number of strata:", num_strata, "\n")
  cat("\n")
  
  # Macro variable contribution analysis
  if(length(macro_terms) > 0) {
    cat("CONTRIBUTION OF MACROECONOMIC VARIABLES:\n")
    cat("---------------------------------------\n")
    
    # AIC contribution
    aic_diff <- no_macro_aic - model_aic
    cat("AIC improvement from macro variables:", round(aic_diff, 2), 
        ifelse(aic_diff > 0, " (better fit with macro variables)\n", " (worse fit with macro variables)\n"))
    
    # R-squared contribution
    r_squared_diff <- r_squared - no_macro_r_squared
    cat("R-squared improvement:", round(r_squared_diff, 4), 
        paste0("(", round(r_squared_diff * 100 / r_squared, 1), "% of model's explanatory power)\n"))
    
    # AUC contribution
    if(!is.na(auc_value) && !is.na(no_macro_auc)) {
      auc_diff <- auc_value - no_macro_auc
      cat("AUC improvement:", round(auc_diff, 4), 
          ifelse(auc_diff > 0, " (better discrimination with macro variables)\n", 
                " (worse discrimination with macro variables)\n"))
    }
    
    # Likelihood ratio test for nested models
    if(no_macro_model$loglik[2] < model$loglik[2]) {
      lr_test_macro <- 2 * (model$loglik[2] - no_macro_model$loglik[2])
      df_diff <- length(coef(model)) - length(coef(no_macro_model))
      p_value <- 1 - pchisq(lr_test_macro, df = df_diff)
      
      cat("Likelihood ratio test for macro variables: Chi-square =", round(lr_test_macro, 2), 
          "on", df_diff, "df, p-value =", format.pval(p_value, digits = 4), "\n")
      
      if(p_value < 0.05) {
        cat("Interpretation: Macroeconomic variables SIGNIFICANTLY improve model fit (p < 0.05)\n")
      } else {
        cat("Interpretation: Macroeconomic variables do not significantly improve model fit (p >= 0.05)\n")
      }
    }
    
    cat("\nNote on Economic Interpretation:\n")
    cat("The inclusion of macroeconomic variables allows the model to account for\n")
    cat("broader economic conditions that affect all companies simultaneously, which\n")
    cat("helps distinguish between firm-specific risk factors and systematic risk.\n")
  }
  
  # Return statistics for future use
  return(list(
    auc = auc_value,
    lr_test = model_summary$logtest,
    r_squared = r_squared,
    aic = model_aic,
    bic = model_bic,
    no_macro_auc = no_macro_auc,
    no_macro_r_squared = no_macro_r_squared,
    no_macro_aic = no_macro_aic,
    macro_terms = macro_terms
  ))
}

# Apply function to both models
bankruptcy_fit_stats <- analyze_model_fit(final_bankruptcy_model, "bankruptcy", "bankruptcy")

=== MODEL FIT STATISTICS FOR BANKRUPTCY MODEL ===

Macroeconomic Variables in Model:
--------------------------------
-  gdp_deflator 
-  unemployement 
-  gdp_growth 

Discrimination Ability:
-----------------------
AUC (Area Under ROC Curve): 0.8183 
Interpretation: Excellent discrimination (AUC > 0.8)

Overall Model Significance:
---------------------------
Likelihood Ratio Test: 445.17 on 23 df, p-value: < 2.2e-16 
Interpretation: Model is statistically significant (p < 0.05)

Goodness of Fit Measures:
------------------------
AIC: 1593.76 - Akaike Information Criterion, lower values indicate better fit
BIC: 1666.68 - Bayesian Information Criterion, penalizes complexity more than AIC
Pseudo R-squared: 0.007 - Proportion of null deviance explained by the model
Interpretation: Limited explanatory power for survival model (R² < 0.05), but may still have good predictive value

Model Complexity:
----------------
Number of coefficients: 23 
Number of strata: 0 

CONTRIBUTION OF MACROECONOMIC VARIABLES:
---------------------------------------
AIC improvement from macro variables: 3.22  (better fit with macro variables)
R-squared improvement: 1e-04 (2.1% of model's explanatory power)
AUC improvement: 0.0077  (better discrimination with macro variables)
Likelihood ratio test for macro variables: Chi-square = 9.22 on 3 df, p-value = 0.02646 
Interpretation: Macroeconomic variables SIGNIFICANTLY improve model fit (p < 0.05)

Note on Economic Interpretation:
The inclusion of macroeconomic variables allows the model to account for
broader economic conditions that affect all companies simultaneously, which
helps distinguish between firm-specific risk factors and systematic risk.
acquisition_fit_stats <- analyze_model_fit(final_acquisition_model, "acquisition", "acquisition")

=== MODEL FIT STATISTICS FOR ACQUISITION MODEL ===

Macroeconomic Variables in Model:
--------------------------------
-  gdp_deflator 
-  unemployement 
-  gdp_growth 
-  unemployement:tt 

Discrimination Ability:
-----------------------
AUC (Area Under ROC Curve): 0.5272 
Interpretation: Poor discrimination (AUC < 0.6)

Overall Model Significance:
---------------------------
Likelihood Ratio Test: 448.04 on 18 df, p-value: < 2.2e-16 
Interpretation: Model is statistically significant (p < 0.05)

Goodness of Fit Measures:
------------------------
AIC: 24259.44 - Akaike Information Criterion, lower values indicate better fit
BIC: 24361.59 - Bayesian Information Criterion, penalizes complexity more than AIC
Pseudo R-squared: 0.007 - Proportion of null deviance explained by the model
Interpretation: Limited explanatory power for survival model (R² < 0.05), but may still have good predictive value

Model Complexity:
----------------
Number of coefficients: 18 
Number of strata: 0 

CONTRIBUTION OF MACROECONOMIC VARIABLES:
---------------------------------------
AIC improvement from macro variables: 25.43  (better fit with macro variables)
R-squared improvement: 5e-04 (7.4% of model's explanatory power)
AUC improvement: -0.0546  (worse discrimination with macro variables)
Likelihood ratio test for macro variables: Chi-square = 33.43 on 4 df, p-value = 9.748e-07 
Interpretation: Macroeconomic variables SIGNIFICANTLY improve model fit (p < 0.05)

Note on Economic Interpretation:
The inclusion of macroeconomic variables allows the model to account for
broader economic conditions that affect all companies simultaneously, which
helps distinguish between firm-specific risk factors and systematic risk.
# Compare models
cat("\nCOMPARISON OF MODEL FIT:\n")

COMPARISON OF MODEL FIT:
cat("------------------------\n")
------------------------
cat("AUC (Area Under ROC Curve):\n")
AUC (Area Under ROC Curve):
cat("  Bankruptcy model:", ifelse(is.na(bankruptcy_fit_stats$auc), "NA", 
                                round(bankruptcy_fit_stats$auc, 4)), "\n")
  Bankruptcy model: 0.8183 
cat("  Acquisition model:", ifelse(is.na(acquisition_fit_stats$auc), "NA", 
                                 round(acquisition_fit_stats$auc, 4)), "\n\n")
  Acquisition model: 0.5272 
cat("AIC (lower is better):\n")
AIC (lower is better):
cat("  Bankruptcy model:", round(bankruptcy_fit_stats$aic, 2), "\n")
  Bankruptcy model: 1593.76 
cat("  Acquisition model:", round(acquisition_fit_stats$aic, 2), "\n\n")
  Acquisition model: 24259.44 
cat("Pseudo R-squared:\n")
Pseudo R-squared:
cat("  Bankruptcy model:", round(bankruptcy_fit_stats$r_squared, 4), "\n")
  Bankruptcy model: 0.007 
cat("  Acquisition model:", round(acquisition_fit_stats$r_squared, 4), "\n\n")
  Acquisition model: 0.007 
# Compare contribution of macro variables between models
cat("\nCOMPARATIVE IMPACT OF MACROECONOMIC VARIABLES:\n")

COMPARATIVE IMPACT OF MACROECONOMIC VARIABLES:
cat("---------------------------------------------\n")
---------------------------------------------
# Function to calculate percentage improvement
pct_improvement <- function(with_macro, without_macro) {
  if(is.na(with_macro) || is.na(without_macro)) {
    return(NA)
  }
  return((with_macro - without_macro) / without_macro * 100)
}

# Calculate improvement percentages
bankruptcy_auc_pct <- pct_improvement(bankruptcy_fit_stats$auc, bankruptcy_fit_stats$no_macro_auc)
acquisition_auc_pct <- pct_improvement(acquisition_fit_stats$auc, acquisition_fit_stats$no_macro_auc)

bankruptcy_r2_pct <- pct_improvement(bankruptcy_fit_stats$r_squared, bankruptcy_fit_stats$no_macro_r_squared)
acquisition_r2_pct <- pct_improvement(acquisition_fit_stats$r_squared, acquisition_fit_stats$no_macro_r_squared)

cat("Relative Improvement in AUC:\n")
Relative Improvement in AUC:
cat("  Bankruptcy model:", ifelse(is.na(bankruptcy_auc_pct), "NA", 
                                paste0(round(bankruptcy_auc_pct, 2), "%")), "\n")
  Bankruptcy model: 0.96% 
cat("  Acquisition model:", ifelse(is.na(acquisition_auc_pct), "NA", 
                                 paste0(round(acquisition_auc_pct, 2), "%")), "\n\n")
  Acquisition model: -9.39% 
cat("Relative Improvement in R-squared:\n")
Relative Improvement in R-squared:
cat("  Bankruptcy model:", ifelse(is.na(bankruptcy_r2_pct), "NA", 
                                paste0(round(bankruptcy_r2_pct, 2), "%")), "\n")
  Bankruptcy model: 2.11% 
cat("  Acquisition model:", ifelse(is.na(acquisition_r2_pct), "NA", 
                                 paste0(round(acquisition_r2_pct, 2), "%")), "\n\n")
  Acquisition model: 8.03% 
# Economic interpretation of the comparison
cat("Economic Interpretation:\n")
Economic Interpretation:
cat("----------------------\n")
----------------------
if(!is.na(bankruptcy_r2_pct) && !is.na(acquisition_r2_pct)) {
  if(bankruptcy_r2_pct > acquisition_r2_pct) {
    cat("Macroeconomic variables have a STRONGER impact on bankruptcy risk compared to acquisition likelihood.\n")
    cat("This suggests that systematic economic factors play a more significant role in determining\n")
    cat("when companies fail than when they are acquired.\n\n")
  } else if(acquisition_r2_pct > bankruptcy_r2_pct) {
    cat("Macroeconomic variables have a STRONGER impact on acquisition likelihood compared to bankruptcy risk.\n")
    cat("This suggests that market conditions and economic cycles are more important drivers of\n")
    cat("M&A activity than they are for corporate failures.\n\n")
  } else {
    cat("Macroeconomic variables have a SIMILAR impact on both bankruptcy risk and acquisition likelihood.\n")
    cat("This suggests that economic conditions influence both outcomes to a comparable degree.\n\n")
  }
}
Macroeconomic variables have a STRONGER impact on acquisition likelihood compared to bankruptcy risk.
This suggests that market conditions and economic cycles are more important drivers of
M&A activity than they are for corporate failures.
cat("Broader Implications:\n")
Broader Implications:
cat("- These results help distinguish between firm-specific and systematic risk factors\n")
- These results help distinguish between firm-specific and systematic risk factors
cat("- Understanding the influence of macroeconomic conditions can improve risk management\n")
- Understanding the influence of macroeconomic conditions can improve risk management
cat("- The relative importance of macro factors may vary across different industries and time periods\n")
- The relative importance of macro factors may vary across different industries and time periods
# Save the fit statistics for reporting
fit_statistics <- list(
  bankruptcy = bankruptcy_fit_stats,
  acquisition = acquisition_fit_stats
)

Again, we will create a function to visualize the model fit statistics, including AUC and R-squared values, and provide detailed interpretations based on the model type (bankruptcy or acquisition). We will also include a summary of the macroeconomic variable contributions for both models.

#-------------------------------------------------------------
# 11.3: Variable Importance with Focus on Macroeconomic Factors
#-------------------------------------------------------------
cat("\n11.3: VARIABLE IMPORTANCE WITH FOCUS ON MACROECONOMIC FACTORS\n")

11.3: VARIABLE IMPORTANCE WITH FOCUS ON MACROECONOMIC FACTORS
cat("--------------------------------------\n")
--------------------------------------
# Function to calculate and visualize variable importance
analyze_variable_importance <- function(model, model_name) {
  # Extract coefficients and statistics
  coef_summary <- summary(model)$coefficients
  
  # Create importance data frame
  importance <- data.frame(
    Variable = rownames(coef_summary),
    Coefficient = coef_summary[, "coef"],
    SE = coef_summary[, "se(coef)"],
    Z_Score = coef_summary[, "z"],
    P_Value = coef_summary[, "Pr(>|z|)"],
    Wald_ChiSq = coef_summary[, "z"]^2,
    HR = exp(coef_summary[, "coef"]),
    stringsAsFactors = FALSE
  )
  
  # Separate main effects from time interactions
  main_effects <- importance[!grepl(":tt", importance$Variable), ]
  time_effects <- importance[grepl(":tt", importance$Variable), ]
  
  # Add variable type classification
  main_effects$Type <- "Financial" # Default
  
  # Identify macro variables
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  for(var in main_effects$Variable) {
    if(var %in% macro_vars) {
      main_effects$Type[main_effects$Variable == var] <- "Macro"
    }
  }
  
  # Calculate standardized importance (absolute Z-score)
  main_effects$Abs_Z <- abs(main_effects$Z_Score)
  main_effects$Standardized_Importance <- 100 * main_effects$Abs_Z / sum(main_effects$Abs_Z)
  
  # Sort by importance
  main_effects <- main_effects[order(main_effects$Abs_Z, decreasing = TRUE), ]
  
  # Print results
  cat("\n=== VARIABLE IMPORTANCE FOR", toupper(model_name), "MODEL ===\n\n")
  
  # Table of top variables
  cat("Ranked Variable Importance (based on statistical significance):\n")
  cat("------------------------------------------------------------\n")
  top_vars <- head(main_effects, 10)  # Show top 10
  
  # Format and print the table
  for(i in 1:nrow(top_vars)) {
    var <- top_vars$Variable[i]
    type <- top_vars$Type[i]
    coef <- top_vars$Coefficient[i]
    hr <- top_vars$HR[i]
    p_val <- top_vars$P_Value[i]
    importance_pct <- top_vars$Standardized_Importance[i]
    
    # Format effect direction
    direction <- ifelse(coef > 0, "increases", "decreases")
    
    # Format p-value
    significance <- ""
    if(p_val < 0.001) significance <- "***"
    else if(p_val < 0.01) significance <- "**"
    else if(p_val < 0.05) significance <- "*"
    else if(p_val < 0.1) significance <- "."
    
    # Check if this variable has a time interaction
    has_time_int <- paste0(var, ":tt") %in% time_effects$Variable
    time_note <- ifelse(has_time_int, " (has time-varying effect)", "")
    
    # Format differently for macro variables
    if(type == "Macro") {
      cat(sprintf("%2d. %-15s: Importance = %5.1f%%, HR = %5.2f, %s %s risk %s%s [MACROECONOMIC VARIABLE]\n", 
                  i, var, importance_pct, hr, direction, model_name, significance, time_note))
    } else {
      cat(sprintf("%2d. %-15s: Importance = %5.1f%%, HR = %5.2f, %s %s risk %s%s\n", 
                  i, var, importance_pct, hr, direction, model_name, significance, time_note))
    }
  }
  
  cat("\nSignificance codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n\n")
  
  # Visualization placeholder (in actual code, would create a plot)
  cat("Top 5 Variables by Importance:\n")
  cat("-----------------------------\n")
  
  for(i in 1:min(5, nrow(main_effects))) {
    var <- main_effects$Variable[i]
    type <- main_effects$Type[i]
    importance <- main_effects$Standardized_Importance[i]
    bar <- paste(rep("#", round(importance/2)), collapse="")
    
    # Add indicator for macro variables
    var_label <- var
    if(type == "Macro") {
      var_label <- paste0(var, " (M)")
    }
    
    cat(sprintf("%-15s |%s %5.1f%%\n", var_label, bar, importance))
  }
  
  # Separate analysis of macroeconomic variables regardless of rank
  macro_rows <- main_effects[main_effects$Type == "Macro", ]
  
  if(nrow(macro_rows) > 0) {
    cat("\nANALYSIS OF MACROECONOMIC VARIABLES:\n")
    cat("----------------------------------\n")
    
    for(i in 1:nrow(macro_rows)) {
      var <- macro_rows$Variable[i]
      coef <- macro_rows$Coefficient[i]
      hr <- macro_rows$HR[i]
      p_val <- macro_rows$P_Value[i]
      rank <- which(main_effects$Variable == var)
      importance_pct <- macro_rows$Standardized_Importance[i]
      
      # Format effect and interpretation
      direction <- ifelse(coef > 0, "increases", "decreases")
      
      cat(sprintf("%-15s: Overall rank #%d (Importance = %5.1f%%)\n", var, rank, importance_pct))
      cat(sprintf("  Effect: %s %s risk (HR = %5.2f, p = %7.4f)\n", 
                  direction, model_name, hr, p_val))
      
      # Economic interpretation
      cat("  Economic interpretation: ")
      if(var == "gdp_growth") {
        if(coef > 0) {
          cat("Stronger economic growth is associated with HIGHER", model_name, "risk\n")
          cat("  This may indicate that during economic expansions, either:\n")
          cat("  - Companies take on more risk, leading to vulnerability\n")
          cat("  - More competitive pressure during growth periods\n")
        } else {
          cat("Stronger economic growth is associated with LOWER", model_name, "risk\n")
          cat("  This aligns with economic theory that growing economies support business success\n")
          cat("  and provide more opportunities for struggling firms\n")
        }
      } else if(var == "gdp_deflator") {
        if(coef > 0) {
          cat("Higher inflation is associated with HIGHER", model_name, "risk\n")
          cat("  This suggests inflation may create challenges through:\n")
          cat("  - Increased input costs\n")
          cat("  - Potential monetary tightening responses\n")
          cat("  - Reduced consumer purchasing power\n")
        } else {
          cat("Higher inflation is associated with LOWER", model_name, "risk\n")
          cat("  This counterintuitive finding might reflect:\n")
          cat("  - Inflation benefiting debtors by reducing real debt burden\n")
          cat("  - Price increases helping company revenues in certain sectors\n")
        }
      } else if(var == "unemployement") {
        if(coef > 0) {
          cat("Higher unemployment is associated with HIGHER", model_name, "risk\n")
          cat("  This aligns with economic theory that weaker labor markets signal:\n")
          cat("  - Overall economic weakness\n")
          cat("  - Reduced consumer spending\n")
          cat("  - Broader contraction in business activity\n")
        } else {
          cat("Higher unemployment is associated with LOWER", model_name, "risk\n")
          cat("  This counterintuitive finding might reflect:\n")
          cat("  - Labor cost savings for businesses during high unemployment\n")
          cat("  - Survival bias where only stronger companies remain after economic downturns\n")
        }
      }
      
      # Check for time interaction and add interpretation
      has_time_int <- paste0(var, ":tt") %in% time_effects$Variable
      if(has_time_int) {
        time_row <- time_effects[time_effects$Variable == paste0(var, ":tt"), ]
        time_coef <- time_row$Coefficient
        time_p <- time_row$P_Value
        
        if(time_p < 0.1) {
          cat("\n  Time-varying effect: ")
          
          if(time_coef > 0 && coef > 0) {
            cat("The adverse impact of this economic factor INCREASES as companies age\n")
            cat("  Mature companies may be less adaptable to this economic condition\n")
          } else if(time_coef < 0 && coef < 0) {
            cat("The protective effect of this economic factor STRENGTHENS as companies age\n")
            cat("  Mature companies may benefit more from this economic condition\n")
          } else if(time_coef < 0 && coef > 0) {
            cat("The adverse impact of this economic factor DECREASES as companies age\n")
            cat("  Young companies are more vulnerable to this economic condition\n")
          } else if(time_coef > 0 && coef < 0) {
            cat("The protective effect of this economic factor WEAKENS as companies age\n")
            cat("  Young companies benefit more from this economic condition\n")
          }
          
          # Calculate effect at different time points
          times <- c(1, 3, 5, 10)
          cat("  Impact at different company ages:\n")
          for(t in times) {
            effect_at_t <- coef + time_coef * t
            hr_at_t <- exp(effect_at_t)
            age_desc <- paste0("Year ", t)
            cat(sprintf("    %-7s: HR = %5.3f (%s risk by %5.1f%%)\n", 
                        age_desc, hr_at_t, 
                        ifelse(effect_at_t > 0, "increases", "decreases"),
                        ifelse(effect_at_t > 0, (hr_at_t - 1) * 100, (1 - hr_at_t) * 100)))
          }
        }
      }
      cat("\n")
    }
  } else {
    cat("\nNo macroeconomic variables in this model.\n\n")
  }
  
  # Analyze time-varying effects separately
  if(nrow(time_effects) > 0) {
    cat("\nTime-Varying Effects Analysis:\n")
    cat("-----------------------------\n")
    
    # Separate macro and financial time effects
    fin_time_effects <- time_effects[!sapply(time_effects$Variable, function(v) {
      var_name <- sub(":tt", "", v)
      return(var_name %in% macro_vars)
    }), ]
    
    macro_time_effects <- time_effects[sapply(time_effects$Variable, function(v) {
      var_name <- sub(":tt", "", v)
      return(var_name %in% macro_vars)
    }), ]
    
    # Print financial variable time effects
    if(nrow(fin_time_effects) > 0) {
      cat("\nTime-varying effects for FINANCIAL variables:\n")
      
      for(i in 1:nrow(fin_time_effects)) {
        time_var <- fin_time_effects$Variable[i]
        var <- sub(":tt", "", time_var)
        time_coef <- fin_time_effects$Coefficient[i]
        time_p <- fin_time_effects$P_Value[i]
        
        # Only show significant time interactions
        if(time_p < 0.1) {  # Showing marginally significant ones too
          main_coef <- main_effects$Coefficient[main_effects$Variable == var]
          
          cat("\nTime-varying importance for", var, ":\n")
          
          # Effect direction at baseline
          baseline_effect <- ifelse(main_coef > 0, "increases", "decreases")
          
          # Effect of time interaction
          time_effect <- ifelse(time_coef > 0, "strengthens", "weakens")
          if((main_coef > 0 && time_coef < 0) || (main_coef < 0 && time_coef > 0)) {
            time_effect <- "weakens"
          } else {
            time_effect <- "strengthens"
          }
          
          cat("  Baseline effect: ", baseline_effect, " risk (HR = ", round(exp(main_coef), 2), ")\n", sep="")
          cat("  Time effect: Effect ", time_effect, " over time (HR per year = ", 
              round(exp(time_coef), 3), ", p = ", format.pval(time_p, digits = 3), ")\n", sep="")
          
          # Calculate effect at different time points
          times <- c(1, 3, 5, 10)
          cat("  Effect at different time points:\n")
          for(t in times) {
            effect_at_t <- main_coef + time_coef * t
            hr_at_t <- exp(effect_at_t)
            cat("  At t =", t, "years: HR =", round(hr_at_t, 3), 
                "Effect =", ifelse(effect_at_t > 0, "increases", "decreases"), 
                model_name, "risk\n")
          }
        }
      }
    }
  }
  
  # Return importance data frame with type information
  return(list(main_effects = main_effects, time_effects = time_effects))
}

# Apply function to both models
bankruptcy_importance <- analyze_variable_importance(final_bankruptcy_model, "bankruptcy")

=== VARIABLE IMPORTANCE FOR BANKRUPTCY MODEL ===

Ranked Variable Importance (based on statistical significance):
------------------------------------------------------------
 1. NIMTA          : Importance =  14.2%, HR =  0.12, decreases bankruptcy risk ***
 2. LTMTA          : Importance =  12.6%, HR = 39.24, increases bankruptcy risk *** (has time-varying effect)
 3. wc_ratio       : Importance =   9.2%, HR =  0.33, decreases bankruptcy risk ***
 4. z_score        : Importance =   6.2%, HR =  1.03, increases bankruptcy risk *
 5. CASHMTA        : Importance =   6.0%, HR =  0.12, decreases bankruptcy risk *
 6. current_ratio  : Importance =   5.5%, HR =  1.09, increases bankruptcy risk *
 7. gdp_deflator   : Importance =   5.3%, HR =  0.84, decreases bankruptcy risk . [MACROECONOMIC VARIABLE]
 8. MBE            : Importance =   5.2%, HR =  0.85, decreases bankruptcy risk .
 9. asset_turnover : Importance =   4.7%, HR =  0.84, decreases bankruptcy risk .
10. gdp_growth     : Importance =   4.7%, HR =  1.10, increases bankruptcy risk . [MACROECONOMIC VARIABLE]

Significance codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Top 5 Variables by Importance:
-----------------------------
NIMTA           |#######  14.2%
LTMTA           |######  12.6%
wc_ratio        |#####   9.2%
z_score         |###   6.2%
CASHMTA         |###   6.0%

ANALYSIS OF MACROECONOMIC VARIABLES:
----------------------------------
gdp_deflator   : Overall rank #7 (Importance =   5.3%)
  Effect: decreases bankruptcy risk (HR =  0.84, p =  0.0511)
  Economic interpretation: Higher inflation is associated with LOWER bankruptcy risk
  This counterintuitive finding might reflect:
  - Inflation benefiting debtors by reducing real debt burden
  - Price increases helping company revenues in certain sectors

gdp_growth     : Overall rank #10 (Importance =   4.7%)
  Effect: increases bankruptcy risk (HR =  1.10, p =  0.0840)
  Economic interpretation: Stronger economic growth is associated with HIGHER bankruptcy risk
  This may indicate that during economic expansions, either:
  - Companies take on more risk, leading to vulnerability
  - More competitive pressure during growth periods

unemployement  : Overall rank #16 (Importance =   2.9%)
  Effect: decreases bankruptcy risk (HR =  0.93, p =  0.2777)
  Economic interpretation: Higher unemployment is associated with LOWER bankruptcy risk
  This counterintuitive finding might reflect:
  - Labor cost savings for businesses during high unemployment
  - Survival bias where only stronger companies remain after economic downturns


Time-Varying Effects Analysis:
-----------------------------

Time-varying effects for FINANCIAL variables:
acquisition_importance <- analyze_variable_importance(final_acquisition_model, "acquisition")

=== VARIABLE IMPORTANCE FOR ACQUISITION MODEL ===

Ranked Variable Importance (based on statistical significance):
------------------------------------------------------------
 1. MBE            : Importance =  19.9%, HR =  0.79, decreases acquisition risk ***
 2. PRICE          : Importance =  12.7%, HR =  1.14, increases acquisition risk ***
 3. current_ratio  : Importance =  10.4%, HR =  0.93, decreases acquisition risk ***
 4. cash_to_assets : Importance =   8.8%, HR =  2.09, increases acquisition risk ***
 5. unemployement  : Importance =   8.4%, HR =  0.87, decreases acquisition risk *** (has time-varying effect) [MACROECONOMIC VARIABLE]
 6. wc_ratio       : Importance =   7.0%, HR =  0.49, decreases acquisition risk *** (has time-varying effect)
 7. receivables_turnover: Importance =   6.5%, HR =  1.00, decreases acquisition risk ***
 8. asset_turnover : Importance =   6.4%, HR =  0.83, decreases acquisition risk ** (has time-varying effect)
 9. gp_margin      : Importance =   5.5%, HR =  1.03, increases acquisition risk **
10. gdp_growth     : Importance =   4.8%, HR =  1.04, increases acquisition risk * [MACROECONOMIC VARIABLE]

Significance codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Top 5 Variables by Importance:
-----------------------------
MBE             |##########  19.9%
PRICE           |######  12.7%
current_ratio   |#####  10.4%
cash_to_assets  |####   8.8%
unemployement (M) |####   8.4%

ANALYSIS OF MACROECONOMIC VARIABLES:
----------------------------------
unemployement  : Overall rank #5 (Importance =   8.4%)
  Effect: decreases acquisition risk (HR =  0.87, p =  0.0000)
  Economic interpretation: Higher unemployment is associated with LOWER acquisition risk
  This counterintuitive finding might reflect:
  - Labor cost savings for businesses during high unemployment
  - Survival bias where only stronger companies remain after economic downturns

  Time-varying effect: The protective effect of this economic factor WEAKENS as companies age
  Young companies benefit more from this economic condition
  Impact at different company ages:
    Year 1 : HR = 0.882 (decreases risk by  11.8%)
    Year 3 : HR = 0.897 (decreases risk by  10.3%)
    Year 5 : HR = 0.912 (decreases risk by   8.8%)
    Year 10: HR = 0.950 (decreases risk by   5.0%)

gdp_growth     : Overall rank #10 (Importance =   4.8%)
  Effect: increases acquisition risk (HR =  1.04, p =  0.0131)
  Economic interpretation: Stronger economic growth is associated with HIGHER acquisition risk
  This may indicate that during economic expansions, either:
  - Companies take on more risk, leading to vulnerability
  - More competitive pressure during growth periods

gdp_deflator   : Overall rank #12 (Importance =   3.4%)
  Effect: decreases acquisition risk (HR =  0.96, p =  0.0852)
  Economic interpretation: Higher inflation is associated with LOWER acquisition risk
  This counterintuitive finding might reflect:
  - Inflation benefiting debtors by reducing real debt burden
  - Price increases helping company revenues in certain sectors


Time-Varying Effects Analysis:
-----------------------------

Time-varying effects for FINANCIAL variables:

Time-varying importance for wc_ratio :
  Baseline effect: decreases risk (HR = 0.49)
  Time effect: Effect weakens over time (HR per year = 1.048, p = 0.000271)
  Effect at different time points:
  At t = 1 years: HR = 0.514 Effect = decreases acquisition risk
  At t = 3 years: HR = 0.565 Effect = decreases acquisition risk
  At t = 5 years: HR = 0.619 Effect = decreases acquisition risk
  At t = 10 years: HR = 0.781 Effect = decreases acquisition risk

Time-varying importance for asset_turnover :
  Baseline effect: decreases risk (HR = 0.83)
  Time effect: Effect weakens over time (HR per year = 1.013, p = 0.00238)
  Effect at different time points:
  At t = 1 years: HR = 0.84 Effect = decreases acquisition risk
  At t = 3 years: HR = 0.862 Effect = decreases acquisition risk
  At t = 5 years: HR = 0.885 Effect = decreases acquisition risk
  At t = 10 years: HR = 0.944 Effect = decreases acquisition risk
# Compare variable importance between models
cat("\nCOMPARING KEY PREDICTORS BETWEEN MODELS:\n")

COMPARING KEY PREDICTORS BETWEEN MODELS:
cat("--------------------------------------\n")
--------------------------------------
# Identify common top predictors
bank_top <- head(bankruptcy_importance$main_effects$Variable, 10)
acq_top <- head(acquisition_importance$main_effects$Variable, 10)
common_predictors <- intersect(bank_top, acq_top)

if(length(common_predictors) > 0) {
  cat("Variables important for both bankruptcy and acquisition prediction:\n")
  for(var in common_predictors) {
    bank_rank <- which(bankruptcy_importance$main_effects$Variable == var)
    acq_rank <- which(acquisition_importance$main_effects$Variable == var)
    bank_coef <- bankruptcy_importance$main_effects$Coefficient[bank_rank]
    acq_coef <- acquisition_importance$main_effects$Coefficient[acq_rank]
    
    # Check if it's a macro variable
    is_macro <- var %in% c("gdp_growth", "gdp_deflator", "unemployement")
    macro_label <- ifelse(is_macro, " [MACRO]", "")
    
    # Check for opposite effects
    if(sign(bank_coef) != sign(acq_coef)) {
      cat(sprintf("- %-15s: Rank #%d in bankruptcy model, #%d in acquisition model%s\n  *** Has OPPOSITE effects: %s bankruptcy risk but %s acquisition risk ***\n", 
                var, bank_rank, acq_rank, macro_label,
                ifelse(bank_coef > 0, "increases", "decreases"),
                ifelse(acq_coef > 0, "increases", "decreases")))
    } else {
      cat(sprintf("- %-15s: Rank #%d in bankruptcy model, #%d in acquisition model%s\n", 
                var, bank_rank, acq_rank, macro_label))
    }
  }
} else {
  cat("No common variables among top 10 predictors of both models.\n")
}
Variables important for both bankruptcy and acquisition prediction:
- wc_ratio       : Rank #3 in bankruptcy model, #6 in acquisition model
- current_ratio  : Rank #6 in bankruptcy model, #3 in acquisition model
  *** Has OPPOSITE effects: increases bankruptcy risk but decreases acquisition risk ***
- MBE            : Rank #8 in bankruptcy model, #1 in acquisition model
- asset_turnover : Rank #9 in bankruptcy model, #8 in acquisition model
- gdp_growth     : Rank #10 in bankruptcy model, #10 in acquisition model [MACRO]
cat("\nUnique predictors for bankruptcy (not in top 10 for acquisition):\n")

Unique predictors for bankruptcy (not in top 10 for acquisition):
unique_bank <- setdiff(bank_top, acq_top)
for(var in unique_bank) {
  bank_rank <- which(bankruptcy_importance$main_effects$Variable == var)
  
  # Check if it's a macro variable
  is_macro <- var %in% c("gdp_growth", "gdp_deflator", "unemployement")
  macro_label <- ifelse(is_macro, " [MACRO]", "")
  
  cat(sprintf("- %-15s: Rank #%d in bankruptcy model%s\n", var, bank_rank, macro_label))
}
- NIMTA          : Rank #1 in bankruptcy model
- LTMTA          : Rank #2 in bankruptcy model
- z_score        : Rank #4 in bankruptcy model
- CASHMTA        : Rank #5 in bankruptcy model
- gdp_deflator   : Rank #7 in bankruptcy model [MACRO]
cat("\nUnique predictors for acquisition (not in top 10 for bankruptcy):\n")

Unique predictors for acquisition (not in top 10 for bankruptcy):
unique_acq <- setdiff(acq_top, bank_top)
for(var in unique_acq) {
  acq_rank <- which(acquisition_importance$main_effects$Variable == var)
  
  # Check if it's a macro variable
  is_macro <- var %in% c("gdp_growth", "gdp_deflator", "unemployement")
  macro_label <- ifelse(is_macro, " [MACRO]", "")
  
  cat(sprintf("- %-15s: Rank #%d in acquisition model%s\n", var, acq_rank, macro_label))
}
- PRICE          : Rank #2 in acquisition model
- cash_to_assets : Rank #4 in acquisition model
- unemployement  : Rank #5 in acquisition model [MACRO]
- receivables_turnover: Rank #7 in acquisition model
- gp_margin      : Rank #9 in acquisition model
# Specific comparison of macro variables between models
cat("\nCOMPARATIVE ANALYSIS OF MACROECONOMIC VARIABLES:\n")

COMPARATIVE ANALYSIS OF MACROECONOMIC VARIABLES:
cat("-------------------------------------------\n")
-------------------------------------------
macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
bank_macro <- bankruptcy_importance$main_effects[bankruptcy_importance$main_effects$Variable %in% macro_vars, ]
acq_macro <- acquisition_importance$main_effects[acquisition_importance$main_effects$Variable %in% macro_vars, ]

if(nrow(bank_macro) > 0 || nrow(acq_macro) > 0) {
  # Combine all macro variables from both models
  all_macro <- unique(c(bank_macro$Variable, acq_macro$Variable))
  
  for(var in all_macro) {
    cat("\nMacroeconomic variable:", var, "\n")
    cat("----------------------------------------\n")
    
    # Get stats for bankruptcy model
    if(var %in% bank_macro$Variable) {
      bank_row <- bank_macro[bank_macro$Variable == var, ]
      bank_rank <- which(bankruptcy_importance$main_effects$Variable == var)
      bank_hr <- bank_row$HR
      bank_coef <- bank_row$Coefficient
      bank_p <- bank_row$P_Value
      bank_imp <- bank_row$Standardized_Importance
      
      cat("Bankruptcy model:\n")
      cat("  Rank:", bank_rank, "of", nrow(bankruptcy_importance$main_effects), "\n")
      cat("  Importance:", round(bank_imp, 1), "%\n")
      cat("  Effect:", ifelse(bank_coef > 0, "Increases", "Decreases"), 
          "risk (HR =", round(bank_hr, 2), ")\n")
      cat("  Significance:", ifelse(bank_p < 0.05, "Significant", "Not significant"), 
          "(p =", format.pval(bank_p, digits = 3), ")\n")
      
      # Check for time interaction
      bank_time_var <- paste0(var, ":tt") %in% bankruptcy_importance$time_effects$Variable
      if(bank_time_var) {
        bank_time_row <- bankruptcy_importance$time_effects[bankruptcy_importance$time_effects$Variable == paste0(var, ":tt"), ]
        bank_time_coef <- bank_time_row$Coefficient
        bank_time_p <- bank_time_row$P_Value
        
        if(bank_time_p < 0.1) {
          cat("  Time-varying effect:", ifelse(bank_time_coef > 0, "Strengthens", "Weakens"), 
              "with age (p =", format.pval(bank_time_p, digits = 3), ")\n")
        }
      }
    } else {
      cat("Bankruptcy model: Not included\n")
    }
    
    # Get stats for acquisition model
    if(var %in% acq_macro$Variable) {
      acq_row <- acq_macro[acq_macro$Variable == var, ]
      acq_rank <- which(acquisition_importance$main_effects$Variable == var)
      acq_hr <- acq_row$HR
      acq_coef <- acq_row$Coefficient
      acq_p <- acq_row$P_Value
      acq_imp <- acq_row$Standardized_Importance
      
      cat("\nAcquisition model:\n")
      cat("  Rank:", acq_rank, "of", nrow(acquisition_importance$main_effects), "\n")
      cat("  Importance:", round(acq_imp, 1), "%\n")
      cat("  Effect:", ifelse(acq_coef > 0, "Increases", "Decreases"), 
          "risk (HR =", round(acq_hr, 2), ")\n")
      cat("  Significance:", ifelse(acq_p < 0.05, "Significant", "Not significant"), 
          "(p =", format.pval(acq_p, digits = 3), ")\n")
      
      # Check for time interaction
      acq_time_var <- paste0(var, ":tt") %in% acquisition_importance$time_effects$Variable
      if(acq_time_var) {
        acq_time_row <- acquisition_importance$time_effects[acquisition_importance$time_effects$Variable == paste0(var, ":tt"), ]
        acq_time_coef <- acq_time_row$Coefficient
        acq_time_p <- acq_time_row$P_Value
        
        if(acq_time_p < 0.1) {
          cat("  Time-varying effect:", ifelse(acq_time_coef > 0, "Strengthens", "Weakens"), 
              "with age (p =", format.pval(acq_time_p, digits = 3), ")\n")
        }
      }
    } else {
      cat("\nAcquisition model: Not included\n")
    }
    
    # Comparison between models if variable is in both
    if(var %in% bank_macro$Variable && var %in% acq_macro$Variable) {
      bank_row <- bank_macro[bank_macro$Variable == var, ]
      acq_row <- acq_macro[acq_macro$Variable == var, ]
      
      cat("\nComparison:\n")
      
      # Compare directions
      if(sign(bank_row$Coefficient) == sign(acq_row$Coefficient)) {
        cat("  Direction: Same direction in both models (", 
            ifelse(bank_row$Coefficient > 0, "increases", "decreases"), " risk)\n", sep="")
      } else {
        cat("  Direction: OPPOSITE effects between models\n")
        cat("    - ", ifelse(bank_row$Coefficient > 0, "Increases", "Decreases"), 
            " bankruptcy risk\n", sep="")
        cat("    - ", ifelse(acq_row$Coefficient > 0, "Increases", "Decreases"), 
            " acquisition risk\n", sep="")
      }
      
      # Compare relative importance
      if(bank_row$Standardized_Importance > acq_row$Standardized_Importance) {
        ratio <- bank_row$Standardized_Importance / acq_row$Standardized_Importance
        cat("  Importance: ", round(ratio, 1), "x more important for bankruptcy than acquisition\n", sep="")
      } else {
        ratio <- acq_row$Standardized_Importance / bank_row$Standardized_Importance
        cat("  Importance: ", round(ratio, 1), "x more important for acquisition than bankruptcy\n", sep="")
      }
      
      # Economic interpretation
      cat("  Economic insight: ")
      if(var == "gdp_growth") {
        if(sign(bank_row$Coefficient) != sign(acq_row$Coefficient)) {
          cat("Economic growth has divergent effects on company outcomes\n")
          if(bank_row$Coefficient < 0 && acq_row$Coefficient > 0) {
            cat("    - Growth periods reduce bankruptcy risk but increase acquisition activity\n")
            cat("    - This suggests acquisitions are more opportunity-driven during expansions\n")
            cat("    - While bankruptcies are more threat-driven during contractions\n")
          } else {
            cat("    - Unusual pattern: Growth increases bankruptcy risk but reduces acquisitions\n")
            cat("    - This might indicate market distortions or industry-specific effects\n")
          }
        } else {
          cat("Economic growth affects both outcomes in the same direction\n")
          cat("    - This suggests systemic economic forces drive both processes similarly\n")
        }
      } else if(var == "unemployement") {
        if(sign(bank_row$Coefficient) != sign(acq_row$Coefficient)) {
          cat("Labor market conditions have divergent effects on company outcomes\n")
          if(bank_row$Coefficient > 0 && acq_row$Coefficient < 0) {
            cat("    - High unemployment increases bankruptcy risk but reduces acquisition activity\n")
            cat("    - This aligns with economic theory that distressed M&A decreases in recessions\n")
            cat("    - Poor labor markets may limit buyers' ability to finance acquisitions\n")
          } else {
            cat("    - Unusual pattern: High unemployment reduces bankruptcy risk but increases acquisitions\n")
            cat("    - This might indicate industry-specific labor market effects\n")
          }
        } else {
          cat("Labor market conditions affect both outcomes in the same direction\n")
          cat("    - This suggests unemployment serves as a general economic indicator\n")
        }
      } else if(var == "gdp_deflator") {
        if(sign(bank_row$Coefficient) != sign(acq_row$Coefficient)) {
          cat("Inflation has divergent effects on company outcomes\n")
          if(bank_row$Coefficient > 0 && acq_row$Coefficient < 0) {
            cat("    - Higher inflation increases bankruptcy risk but reduces acquisition activity\n")
            cat("    - This suggests inflation creates operational challenges while depressing M&A markets\n")
            cat("    - Inflation uncertainty may limit buyers' willingness to make long-term commitments\n")
          } else {
            cat("    - Unusual pattern: Inflation reduces bankruptcy risk but increases acquisitions\n")
            cat("    - This might indicate inflation benefits debtors while creating buying opportunities\n")
          }
        } else {
          cat("Inflation affects both outcomes in the same direction\n")
          cat("    - This suggests inflation serves as a general economic indicator\n")
        }
      }
    }
    cat("\n")
  }
} else {
  cat("No macroeconomic variables in either model.\n")
}

Macroeconomic variable: gdp_deflator 
----------------------------------------
Bankruptcy model:
  Rank: 7 of 20 
  Importance: 5.3 %
  Effect: Decreases risk (HR = 0.84 )
  Significance: Not significant (p = 0.0511 )

Acquisition model:
  Rank: 12 of 15 
  Importance: 3.4 %
  Effect: Decreases risk (HR = 0.96 )
  Significance: Not significant (p = 0.0852 )

Comparison:
  Direction: Same direction in both models (decreases risk)
  Importance: 1.6x more important for bankruptcy than acquisition
  Economic insight: Inflation affects both outcomes in the same direction
    - This suggests inflation serves as a general economic indicator


Macroeconomic variable: gdp_growth 
----------------------------------------
Bankruptcy model:
  Rank: 10 of 20 
  Importance: 4.7 %
  Effect: Increases risk (HR = 1.1 )
  Significance: Not significant (p = 0.084 )

Acquisition model:
  Rank: 10 of 15 
  Importance: 4.8 %
  Effect: Increases risk (HR = 1.04 )
  Significance: Significant (p = 0.0131 )

Comparison:
  Direction: Same direction in both models (increases risk)
  Importance: 1x more important for acquisition than bankruptcy
  Economic insight: Economic growth affects both outcomes in the same direction
    - This suggests systemic economic forces drive both processes similarly


Macroeconomic variable: unemployement 
----------------------------------------
Bankruptcy model:
  Rank: 16 of 20 
  Importance: 2.9 %
  Effect: Decreases risk (HR = 0.93 )
  Significance: Not significant (p = 0.278 )

Acquisition model:
  Rank: 5 of 15 
  Importance: 8.4 %
  Effect: Decreases risk (HR = 0.87 )
  Significance: Significant (p = 1.71e-05 )
  Time-varying effect: Strengthens with age (p = 3.45e-05 )

Comparison:
  Direction: Same direction in both models (decreases risk)
  Importance: 2.9x more important for acquisition than bankruptcy
  Economic insight: Labor market conditions affect both outcomes in the same direction
    - This suggests unemployment serves as a general economic indicator
# Save variable importance information
variable_importance <- list(
  bankruptcy = bankruptcy_importance,
  acquisition = acquisition_importance
)

Not sure about this approach ! But, we predict the risk scores for the last observation of each company in the dataset. We then use these scores to evaluate the model’s performance using ROC analysis. The function also allows for a comparison of the model with and without macroeconomic variables, providing insights into their contribution to predictive performance.

#-------------------------------------------------------------
# 11.4: Prediction Performance Analysis with Macro Impact Assessment
#-------------------------------------------------------------
cat("\n11.4: PREDICTION PERFORMANCE ANALYSIS WITH MACRO IMPACT ASSESSMENT\n")

11.4: PREDICTION PERFORMANCE ANALYSIS WITH MACRO IMPACT ASSESSMENT
cat("--------------------------------------\n")
--------------------------------------
# Function to evaluate prediction performance
evaluate_prediction_performance <- function(model, model_name, event_type, assess_macro_impact = TRUE) {
  cat("\nEvaluating prediction performance for", model_name, "model...\n")
  
  # Get last observation for each company
  last_obs_data <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate risk scores from full model
  risk_scores <- predict(model, newdata = last_obs_data, type = "risk")
  
  # Add scores to data
  last_obs_data$risk_score <- risk_scores
  
  # If requested, create model without macro variables for comparison
  if(assess_macro_impact) {
    cat("\nAssessing impact of macroeconomic variables on predictive performance...\n")
    
    # Identify macro variables in the model
    macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
    model_vars <- names(coef(model))
    macro_terms <- c()
    
    for(var in model_vars) {
      # Check if variable contains any macro variable name
      # This handles both main effects and interactions
      is_macro <- FALSE
      for(macro in macro_vars) {
        if(grepl(macro, var)) {
          is_macro <- TRUE
          break
        }
      }
      
      if(is_macro) {
        macro_terms <- c(macro_terms, var)
      }
    }
    
    if(length(macro_terms) > 0) {
      cat("Found", length(macro_terms), "macroeconomic terms in model:\n")
      for(term in macro_terms) {
        cat("- ", term, "\n")
      }
      
      # Create model without macro variables
      no_macro_formula <- formula(model)
      for(term in macro_terms) {
        no_macro_formula <- update(no_macro_formula, paste0(". ~ . - ", term))
      }
      
      # Fit model without macro variables
      no_macro_model <- coxph(no_macro_formula, data = data, ties = "efron")
      
      # Calculate risk scores without macro variables
      no_macro_scores <- predict(no_macro_model, newdata = last_obs_data, type = "risk")
      last_obs_data$no_macro_score <- no_macro_scores
    } else {
      cat("No macroeconomic variables found in the model.\n")
      no_macro_model <- NULL
    }
  }
  
  # Evaluate discrimination with ROC curve analysis
  if(requireNamespace("pROC", quietly = TRUE)) {
    library(pROC)
    
    # Create ROC object for full model
    roc_obj <- tryCatch({
      roc(last_obs_data[[event_type]], risk_scores, quiet = TRUE)
    }, error = function(e) {
      cat("Error calculating ROC:", e$message, "\n")
      return(NULL)
    })
    
    # If assessing macro impact and we have a no-macro model, create ROC for it too
    if(assess_macro_impact && !is.null(no_macro_model)) {
      no_macro_roc <- tryCatch({
        roc(last_obs_data[[event_type]], no_macro_scores, quiet = TRUE)
      }, error = function(e) {
        cat("Error calculating ROC for no-macro model:", e$message, "\n")
        no_macro_roc <- NULL
      })
    } else {
      no_macro_roc <- NULL
    }
    
    if(!is.null(roc_obj)) {
      # Calculate AUC and confidence interval
      auc_value <- auc(roc_obj)
      ci_values <- ci(roc_obj)
      
      cat("\nDiscrimination Metrics:\n")
      cat("-----------------------\n")
      cat("AUC (Area Under ROC Curve):", round(auc_value, 4), "\n")
      cat("95% Confidence Interval:", paste(round(ci_values[1:2], 4), collapse=" - "), "\n")
      
      # Compare with no-macro model if available
      if(!is.null(no_macro_roc)) {
        no_macro_auc <- auc(no_macro_roc)
        auc_diff <- auc_value - no_macro_auc
        pct_improvement <- auc_diff / no_macro_auc * 100
        
        cat("\nMacroeconomic Variables Contribution to AUC:\n")
        cat("------------------------------------------\n")
        cat("AUC with macro variables:", round(auc_value, 4), "\n")
        cat("AUC without macro variables:", round(no_macro_auc, 4), "\n")
        cat("Absolute improvement:", round(auc_diff, 4), "\n")
        cat("Relative improvement:", round(pct_improvement, 2), "%\n")
        
        # Test if improvement is significant
        if(requireNamespace("pROC", quietly = TRUE)) {
          roc_test <- roc.test(roc_obj, no_macro_roc, method="delong")
          cat("Statistical significance: p =", format.pval(roc_test$p.value, digits = 3), "\n")
          
          if(roc_test$p.value < 0.05) {
            cat("The improvement from macroeconomic variables is statistically significant (p < 0.05)\n")
          } else {
            cat("The improvement from macroeconomic variables is not statistically significant (p >= 0.05)\n")
          }
        }
      }
      
      # Calculate sensitivity and specificity at optimal threshold
      coord_results <- coords(roc_obj, "best", ret = c("threshold", "sensitivity", "specificity"))
      optimal_threshold <- as.numeric(coord_results["threshold"])
      optimal_sensitivity <- as.numeric(coord_results["sensitivity"])
      optimal_specificity <- as.numeric(coord_results["specificity"])
      
      cat("\nOptimal threshold:", round(optimal_threshold, 4), "\n")
      cat("Sensitivity at optimal threshold:", round(optimal_sensitivity, 4), "\n")
      cat("Specificity at optimal threshold:", round(optimal_specificity, 4), "\n")
      
      # Additional performance metrics
      # Count events
      n_events <- sum(last_obs_data[[event_type]])
      n_total <- nrow(last_obs_data)
      
      # Calculate predictions at optimal threshold
      predictions <- ifelse(risk_scores >= optimal_threshold, 1, 0)
      
      # Confusion matrix
      TP <- sum(predictions == 1 & last_obs_data[[event_type]] == 1)
      TN <- sum(predictions == 0 & last_obs_data[[event_type]] == 0)
      FP <- sum(predictions == 1 & last_obs_data[[event_type]] == 0)
      FN <- sum(predictions == 0 & last_obs_data[[event_type]] == 1)
      
      # Calculate additional metrics
      accuracy <- (TP + TN) / (TP + TN + FP + FN)
      precision <- TP / (TP + FP)
      recall <- TP / (TP + FN)  # same as sensitivity
      f1_score <- 2 * precision * recall / (precision + recall)
      ppv <- TP / (TP + FP)  # positive predictive value
      npv <- TN / (TN + FN)  # negative predictive value
      
      cat("\nAdditional Performance Metrics:\n")
      cat("------------------------------\n")
      cat("Total companies:", n_total, "\n")
      cat("Number of", model_name, "events:", n_events, 
          "(", round(100*n_events/n_total, 1), "%)\n")
      cat("Accuracy:", round(accuracy, 4), "\n")
      cat("Precision (PPV):", round(precision, 4), "\n")
      cat("Recall (Sensitivity):", round(recall, 4), "\n")
      cat("F1 Score:", round(f1_score, 4), "\n")
      cat("Negative Predictive Value:", round(npv, 4), "\n")
      
      # Calculate the same metrics for the no-macro model if available
      if(!is.null(no_macro_roc)) {
        # Find optimal threshold for no-macro model
        no_macro_coords <- coords(no_macro_roc, "best", ret = c("threshold", "sensitivity", "specificity"))
        no_macro_threshold <- as.numeric(no_macro_coords["threshold"])
        
        # Calculate predictions using no-macro scores
        no_macro_predictions <- ifelse(no_macro_scores >= no_macro_threshold, 1, 0)
        
        # Confusion matrix for no-macro model
        no_macro_TP <- sum(no_macro_predictions == 1 & last_obs_data[[event_type]] == 1)
        no_macro_TN <- sum(no_macro_predictions == 0 & last_obs_data[[event_type]] == 0)
        no_macro_FP <- sum(no_macro_predictions == 1 & last_obs_data[[event_type]] == 0)
        no_macro_FN <- sum(no_macro_predictions == 0 & last_obs_data[[event_type]] == 1)
        
        # Calculate metrics for no-macro model
        no_macro_accuracy <- (no_macro_TP + no_macro_TN) / (no_macro_TP + no_macro_TN + no_macro_FP + no_macro_FN)
        no_macro_precision <- no_macro_TP / (no_macro_TP + no_macro_FP)
        no_macro_recall <- no_macro_TP / (no_macro_TP + no_macro_FN)
        no_macro_f1 <- 2 * no_macro_precision * no_macro_recall / (no_macro_precision + no_macro_recall)
        
        cat("\nComparison with Model Without Macroeconomic Variables:\n")
        cat("----------------------------------------------------\n")
        metrics <- c("Accuracy", "Precision", "Recall", "F1 Score")
        full_values <- c(accuracy, precision, recall, f1_score)
        no_macro_values <- c(no_macro_accuracy, no_macro_precision, no_macro_recall, no_macro_f1)
        
        for(i in 1:length(metrics)) {
          diff <- full_values[i] - no_macro_values[i]
          pct_diff <- diff / no_macro_values[i] * 100
          
          cat(sprintf("%-10s: With macro = %6.4f, Without macro = %6.4f (Δ = %+6.4f, %+5.1f%%)\n", 
                    metrics[i], full_values[i], no_macro_values[i], diff, pct_diff))
        }
      }
      
      # Risk distribution analysis
      risk_quantiles <- quantile(risk_scores, probs = seq(0, 1, 0.25))
      
      cat("\nRisk Score Distribution:\n")
      cat("------------------------\n")
      cat("Minimum:", round(min(risk_scores), 4), "\n")
      cat("25th percentile:", round(risk_quantiles[2], 4), "\n")
      cat("Median:", round(median(risk_scores), 4), "\n")
      cat("75th percentile:", round(risk_quantiles[4], 4), "\n")
      cat("Maximum:", round(max(risk_scores), 4), "\n")
      
      # Risk stratification
      cat("\nEvent Rates by Risk Quartile:\n")
      last_obs_data$risk_quartile <- cut(risk_scores, 
                                      breaks = risk_quantiles, 
                                      include.lowest = TRUE, 
                                      labels = c("Q1 (Lowest)", "Q2", "Q3", "Q4 (Highest)"))
      
      # Use traditional approach for compatibility
      event_by_quartile <- aggregate(
        last_obs_data[[event_type]], 
        by = list(risk_quartile = last_obs_data$risk_quartile), 
        FUN = function(x) c(events = sum(x), n = length(x), rate = mean(x))
      )
      
      for(i in 1:nrow(event_by_quartile)) {
        q <- event_by_quartile$risk_quartile[i]
        stats <- event_by_quartile$x[i,]
        events <- stats["events"]
        n <- stats["n"]
        rate <- stats["rate"]
        
        cat(q, ": ", events, " events out of ", n, " companies (", 
            round(100*rate, 1), "%)\n", sep="")
      }
      
      # If macro assessment is enabled and we have both models, analyze macro impact on risk stratification
      if(assess_macro_impact && !is.null(no_macro_roc)) {
        cat("\nMacroeconomic Impact on Risk Stratification:\n")
        cat("-----------------------------------------\n")
        
        # Get companies in highest risk quartile according to both models
        full_high_risk <- last_obs_data$cusip[last_obs_data$risk_quartile == "Q4 (Highest)"]
        
        # Create risk quartiles for no-macro model
        no_macro_quantiles <- quantile(no_macro_scores, probs = seq(0, 1, 0.25))
        last_obs_data$no_macro_quartile <- cut(no_macro_scores, 
                                          breaks = no_macro_quantiles, 
                                          include.lowest = TRUE, 
                                          labels = c("Q1 (Lowest)", "Q2", "Q3", "Q4 (Highest)"))
        
        no_macro_high_risk <- last_obs_data$cusip[last_obs_data$no_macro_quartile == "Q4 (Highest)"]
        
        # Find companies that changed risk classification due to macro variables
        moved_to_high <- setdiff(full_high_risk, no_macro_high_risk)
        moved_from_high <- setdiff(no_macro_high_risk, full_high_risk)
        
        cat("Companies reclassified to highest risk quartile when using macro variables:", length(moved_to_high), "\n")
        cat("Companies moved out of highest risk quartile when using macro variables:", length(moved_from_high), "\n")
        
        # Check if reclassification improved prediction
        if(length(moved_to_high) > 0) {
          moved_to_high_data <- last_obs_data[last_obs_data$cusip %in% moved_to_high, ]
          event_rate_moved_to_high <- mean(moved_to_high_data[[event_type]])
          cat("Event rate among companies moved to highest risk quartile:", 
              round(100*event_rate_moved_to_high, 1), "%\n")
        }
        
        if(length(moved_from_high) > 0) {
          moved_from_high_data <- last_obs_data[last_obs_data$cusip %in% moved_from_high, ]
          event_rate_moved_from_high <- mean(moved_from_high_data[[event_type]])
          cat("Event rate among companies moved from highest risk quartile:", 
              round(100*event_rate_moved_from_high, 1), "%\n")
        }
        
        # Assess accuracy of reclassification
        if(length(moved_to_high) > 0 && length(moved_from_high) > 0) {
          if(event_rate_moved_to_high > event_rate_moved_from_high) {
            cat("\nReclassification analysis: Macroeconomic variables IMPROVED risk stratification\n")
            cat("Companies moved to high risk had higher event rates than those moved out of high risk\n")
          } else {
            cat("\nReclassification analysis: Macroeconomic variables did NOT improve risk stratification\n")
            cat("Companies moved to high risk had lower event rates than those moved out of high risk\n")
          }
        }
      }
      
      # Economic condition analysis - examine how prediction varies with economic conditions
      if(assess_macro_impact && !is.null(no_macro_model)) {
        cat("\nPrediction Performance Across Economic Conditions:\n")
        cat("-----------------------------------------------\n")
        
        # Check if we have economic indicators in the data
        if(all(c("gdp_growth", "unemployement") %in% colnames(last_obs_data))) {
          # Define economic conditions
          median_gdp <- median(last_obs_data$gdp_growth, na.rm = TRUE)
          median_unemp <- median(last_obs_data$unemployement, na.rm = TRUE)
          
          # Create economic condition groups
          last_obs_data$econ_condition <- "Mixed"
          last_obs_data$econ_condition[last_obs_data$gdp_growth > median_gdp & 
                                    last_obs_data$unemployement < median_unemp] <- "Strong"
          last_obs_data$econ_condition[last_obs_data$gdp_growth < median_gdp & 
                                    last_obs_data$unemployement > median_unemp] <- "Weak"
          
          # Calculate AUC by economic condition for both models
          for(condition in c("Strong", "Mixed", "Weak")) {
            condition_data <- last_obs_data[last_obs_data$econ_condition == condition, ]
            
            if(nrow(condition_data) > 0 && sum(condition_data[[event_type]]) > 0) {
              cat("\nEconomic condition:", condition, "(", nrow(condition_data), "companies)\n")
              
              # Calculate AUC for full model
              condition_roc <- tryCatch({
                roc(condition_data[[event_type]], condition_data$risk_score, quiet= TRUE)
              }, error = function(e) NULL)
              
              # Calculate AUC for no-macro model
              condition_no_macro_roc <- tryCatch({
                roc(condition_data[[event_type]], condition_data$no_macro_score, quiet= TRUE)
              }, error = function(e) NULL)
              
              if(!is.null(condition_roc) && !is.null(condition_no_macro_roc)) {
                condition_auc <- auc(condition_roc)
                condition_no_macro_auc <- auc(condition_no_macro_roc)
                auc_diff <- condition_auc - condition_no_macro_auc
                
                cat("Full model AUC:", round(condition_auc, 4), "\n")
                cat("No-macro model AUC:", round(condition_no_macro_auc, 4), "\n")
                cat("Improvement from macro variables:", round(auc_diff, 4), "\n")
                
                if(auc_diff > 0) {
                  cat("Macroeconomic variables improve prediction in", condition, "economic conditions\n")
                } else {
                  cat("Macroeconomic variables do not improve prediction in", condition, "economic conditions\n")
                }
              } else {
                cat("Could not calculate AUC for this economic condition due to insufficient data\n")
              }
            } else {
              cat("\nEconomic condition:", condition, "- insufficient data for analysis\n")
            }
          }
        } else {
          cat("Economic indicators not available in dataset for condition-specific analysis\n")
        }
      }
      
      # Return performance metrics
      return(list(
        auc = auc_value,
        ci = ci_values,
        optimal_threshold = optimal_threshold,
        sensitivity = optimal_sensitivity,
        specificity = optimal_specificity,
        accuracy = accuracy,
        precision = precision,
        recall = recall,
        f1_score = f1_score,
        risk_quantiles = risk_quantiles,
        no_macro_model = if(exists("no_macro_model")) no_macro_model else NULL,
        no_macro_auc = if(exists("no_macro_auc")) no_macro_auc else NULL,
        no_macro_metrics = if(exists("no_macro_values")) 
          list(accuracy = no_macro_accuracy, 
               precision = no_macro_precision, 
               recall = no_macro_recall, 
               f1 = no_macro_f1) else NULL
      ))
    } else {
      cat("Could not calculate ROC curve.\n")
      return(NULL)
    }
  } else {
    cat("pROC package not available for ROC analysis.\n")
    return(NULL)
  }
}

# Apply function to both models
bankruptcy_performance <- evaluate_prediction_performance(final_bankruptcy_model, "bankruptcy", "bankruptcy")

Evaluating prediction performance for bankruptcy model...

Assessing impact of macroeconomic variables on predictive performance...
Found 3 macroeconomic terms in model:
-  gdp_deflator 
-  unemployement 
-  gdp_growth 

Discrimination Metrics:
-----------------------
AUC (Area Under ROC Curve): 0.8183 
95% Confidence Interval: 0.7901 - 0.8183 

Macroeconomic Variables Contribution to AUC:
------------------------------------------
AUC with macro variables: 0.8183 
AUC without macro variables: 0.8106 
Absolute improvement: 0.0077 
Relative improvement: 0.96 %
Statistical significance: p = 0.00142 
The improvement from macroeconomic variables is statistically significant (p < 0.05)

Optimal threshold: 3.2518 
Sensitivity at optimal threshold: 0.8523 
Specificity at optimal threshold: 0.6972 

Additional Performance Metrics:
------------------------------
Total companies: 5038 
Number of bankruptcy events: 176 ( 3.5 %)
Accuracy: 0.7027 
Precision (PPV): 0.0925 
Recall (Sensitivity): 0.8523 
F1 Score: 0.1669 
Negative Predictive Value: 0.9924 

Comparison with Model Without Macroeconomic Variables:
----------------------------------------------------
Accuracy  : With macro = 0.7027, Without macro = 0.7382 (Δ = -0.0355,  -4.8%)
Precision : With macro = 0.0925, Without macro = 0.0984 (Δ = -0.0059,  -6.0%)
Recall    : With macro = 0.8523, Without macro = 0.7955 (Δ = +0.0568,  +7.1%)
F1 Score  : With macro = 0.1669, Without macro = 0.1751 (Δ = -0.0083,  -4.7%)

Risk Score Distribution:
------------------------
Minimum: 3e-04 
25th percentile: 0.5495 
Median: 1.4055 
75th percentile: 5.2813 
Maximum: 850.2138 

Event Rates by Risk Quartile:
1: 5 events out of 1260 companies (0.4%)
2: 10 events out of 1259 companies (0.8%)
3: 30 events out of 1259 companies (2.4%)
4: 131 events out of 1260 companies (10.4%)

Macroeconomic Impact on Risk Stratification:
-----------------------------------------
Companies reclassified to highest risk quartile when using macro variables: 62 
Companies moved out of highest risk quartile when using macro variables: 62 
Event rate among companies moved to highest risk quartile: 9.7 %
Event rate among companies moved from highest risk quartile: 6.5 %

Reclassification analysis: Macroeconomic variables IMPROVED risk stratification
Companies moved to high risk had higher event rates than those moved out of high risk

Prediction Performance Across Economic Conditions:
-----------------------------------------------

Economic condition: Strong ( 335 companies)
Full model AUC: 0.8125 
No-macro model AUC: 0.8125 
Improvement from macro variables: 0 
Macroeconomic variables do not improve prediction in Strong economic conditions

Economic condition: Mixed ( 2921 companies)
Full model AUC: 0.832 
No-macro model AUC: 0.8114 
Improvement from macro variables: 0.0207 
Macroeconomic variables improve prediction in Mixed economic conditions

Economic condition: Weak ( 1782 companies)
Full model AUC: 0.7778 
No-macro model AUC: 0.7763 
Improvement from macro variables: 0.0015 
Macroeconomic variables improve prediction in Weak economic conditions
acquisition_performance <- evaluate_prediction_performance(final_acquisition_model, "acquisition", "acquisition")

Evaluating prediction performance for acquisition model...

Assessing impact of macroeconomic variables on predictive performance...
Found 4 macroeconomic terms in model:
-  gdp_deflator 
-  unemployement 
-  gdp_growth 
-  unemployement:tt 

Discrimination Metrics:
-----------------------
AUC (Area Under ROC Curve): 0.5272 
95% Confidence Interval: 0.5113 - 0.5272 

Macroeconomic Variables Contribution to AUC:
------------------------------------------
AUC with macro variables: 0.5272 
AUC without macro variables: 0.5818 
Absolute improvement: -0.0546 
Relative improvement: -9.39 %
Statistical significance: p = <2e-16 
The improvement from macroeconomic variables is statistically significant (p < 0.05)

Optimal threshold: 0.9551 
Sensitivity at optimal threshold: 0.7804 
Specificity at optimal threshold: 0.3152 

Additional Performance Metrics:
------------------------------
Total companies: 5038 
Number of acquisition events: 2154 ( 42.8 %)
Accuracy: 0.5141 
Precision (PPV): 0.4598 
Recall (Sensitivity): 0.7804 
F1 Score: 0.5787 
Negative Predictive Value: 0.6577 

Comparison with Model Without Macroeconomic Variables:
----------------------------------------------------
Accuracy  : With macro = 0.5141, Without macro = 0.5486 (Δ = -0.0345,  -6.3%)
Precision : With macro = 0.4598, Without macro = 0.4833 (Δ = -0.0235,  -4.9%)
Recall    : With macro = 0.7804, Without macro = 0.8041 (Δ = -0.0237,  -2.9%)
F1 Score  : With macro = 0.5787, Without macro = 0.6037 (Δ = -0.0250,  -4.1%)

Risk Score Distribution:
------------------------
Minimum: 7e-04 
25th percentile: 0.9152 
Median: 1.3647 
75th percentile: 2.2478 
Maximum: 15.5969 

Event Rates by Risk Quartile:
1: 433 events out of 1260 companies (34.4%)
2: 578 events out of 1259 companies (45.9%)
3: 654 events out of 1259 companies (51.9%)
4: 489 events out of 1260 companies (38.8%)

Macroeconomic Impact on Risk Stratification:
-----------------------------------------
Companies reclassified to highest risk quartile when using macro variables: 273 
Companies moved out of highest risk quartile when using macro variables: 273 
Event rate among companies moved to highest risk quartile: 31.1 %
Event rate among companies moved from highest risk quartile: 64.8 %

Reclassification analysis: Macroeconomic variables did NOT improve risk stratification
Companies moved to high risk had lower event rates than those moved out of high risk

Prediction Performance Across Economic Conditions:
-----------------------------------------------

Economic condition: Strong ( 335 companies)
Full model AUC: 0.6925 
No-macro model AUC: 0.7178 
Improvement from macro variables: -0.0253 
Macroeconomic variables do not improve prediction in Strong economic conditions

Economic condition: Mixed ( 2921 companies)
Full model AUC: 0.5519 
No-macro model AUC: 0.5083 
Improvement from macro variables: 0.0436 
Macroeconomic variables improve prediction in Mixed economic conditions

Economic condition: Weak ( 1782 companies)
Full model AUC: 0.7043 
No-macro model AUC: 0.7646 
Improvement from macro variables: -0.0604 
Macroeconomic variables do not improve prediction in Weak economic conditions
# Compare key performance metrics
if(!is.null(bankruptcy_performance) && !is.null(acquisition_performance)) {
  cat("\nCOMPARISON OF PREDICTION PERFORMANCE:\n")
  cat("------------------------------------\n")
  
  metrics <- c("AUC", "Accuracy", "Precision", "Recall", "F1 Score")
  bankruptcy_values <- c(
    bankruptcy_performance$auc,
    bankruptcy_performance$accuracy,
    bankruptcy_performance$precision,
    bankruptcy_performance$recall,
    bankruptcy_performance$f1_score
  )
  
  acquisition_values <- c(
    acquisition_performance$auc,
    acquisition_performance$accuracy,
    acquisition_performance$precision,
    acquisition_performance$recall,
    acquisition_performance$f1_score
  )
  
  for(i in 1:length(metrics)) {
    cat(sprintf("%-10s: Bankruptcy = %6.4f, Acquisition = %6.4f\n", 
              metrics[i], bankruptcy_values[i], acquisition_values[i]))
  }
  
  # Compare impact of macroeconomic variables between models
  if(!is.null(bankruptcy_performance$no_macro_auc) && 
     !is.null(acquisition_performance$no_macro_auc)) {
    
    cat("\nCOMPARISON OF MACROECONOMIC IMPACT ON PREDICTION:\n")
    cat("----------------------------------------------\n")
    
    bank_auc_improvement <- bankruptcy_performance$auc - bankruptcy_performance$no_macro_auc
    bank_pct_improvement <- bank_auc_improvement / bankruptcy_performance$no_macro_auc * 100
    
    acq_auc_improvement <- acquisition_performance$auc - acquisition_performance$no_macro_auc
    acq_pct_improvement <- acq_auc_improvement / acquisition_performance$no_macro_auc * 100
    
    cat("AUC improvement from macroeconomic variables:\n")
    cat("  Bankruptcy model: +", round(bank_auc_improvement, 4), 
        "(", round(bank_pct_improvement, 2), "%)\n", sep="")
    cat("  Acquisition model: +", round(acq_auc_improvement, 4), 
        "(", round(acq_pct_improvement, 2), "%)\n", sep="")
    
    if(bank_auc_improvement > acq_auc_improvement) {
      cat("\nMacroeconomic variables have a GREATER impact on bankruptcy prediction\n")
      cat("This aligns with the theory that external economic conditions play a more\n")
      cat("significant role in company failure than in acquisition likelihood.\n")
    } else if(acq_auc_improvement > bank_auc_improvement) {
      cat("\nMacroeconomic variables have a GREATER impact on acquisition prediction\n")
      cat("This suggests that M&A activity is more strongly influenced by broader\n")
      cat("economic conditions than bankruptcy risk.\n")
    } else {
      cat("\nMacroeconomic variables have SIMILAR impact on both outcomes\n")
      cat("This indicates that both bankruptcy and acquisition risks are\n")
      cat("similarly affected by changing economic conditions.\n")
    }
    
    # Check which model shows most significant improvement from macro variables
    cat("\nECONOMIC INSIGHT: ")
    if(bank_pct_improvement > 5 && acq_pct_improvement > 5) {
      cat("Macroeconomic conditions are important predictors for BOTH corporate outcomes\n")
      cat("Companies should monitor economic indicators to assess both bankruptcy and acquisition risks\n")
    } else if(bank_pct_improvement > 5) {
      cat("Bankruptcy risk is more systematically linked to economic cycles\n")
      cat("Economic downturns may disproportionately affect financially vulnerable companies\n")
    } else if(acq_pct_improvement > 5) {
      cat("Acquisition likelihood is more systematically linked to economic cycles\n")
      cat("M&A activities appear to be more sensitive to changing macroeconomic conditions\n")
    } else {
      cat("Limited systematic impact of macroeconomic variables on both outcomes\n")
      cat("Company-specific factors may be more important than broad economic conditions\n")
    }
  }
}

COMPARISON OF PREDICTION PERFORMANCE:
------------------------------------
AUC       : Bankruptcy = 0.8183, Acquisition = 0.5272
Accuracy  : Bankruptcy = 0.7027, Acquisition = 0.5141
Precision : Bankruptcy = 0.0925, Acquisition = 0.4598
Recall    : Bankruptcy = 0.8523, Acquisition = 0.7804
F1 Score  : Bankruptcy = 0.1669, Acquisition = 0.5787

COMPARISON OF MACROECONOMIC IMPACT ON PREDICTION:
----------------------------------------------
AUC improvement from macroeconomic variables:
  Bankruptcy model: +0.0077(0.96%)
  Acquisition model: +-0.0546(-9.39%)

Macroeconomic variables have a GREATER impact on bankruptcy prediction
This aligns with the theory that external economic conditions play a more
significant role in company failure than in acquisition likelihood.

ECONOMIC INSIGHT: Limited systematic impact of macroeconomic variables on both outcomes
Company-specific factors may be more important than broad economic conditions

In step 11.5 we analyze the timing of events in the dataset. We check for the first occurrence of bankruptcy and acquisition events for each company, and then calculate the quantiles of these event times. This information is used to determine appropriate time horizons for prediction models. The analysis also includes a comparison of the timing of bankruptcy and acquisition events, providing insights into their relationship.

#-------------------------------------------------------------
# 11.5: Time-Dependent Prediction Performance with Macro Analysis
#-------------------------------------------------------------
cat("\n11.5: TIME-DEPENDENT PREDICTION PERFORMANCE WITH MACRO ANALYSIS\n")

11.5: TIME-DEPENDENT PREDICTION PERFORMANCE WITH MACRO ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
# Simplified event timing analysis using base R
event_timing_analysis <- function() {
  cat("Analyzing event timing in the dataset:\n")
  
  # Get all unique companies
  all_companies <- unique(data$cusip)
  bank_times <- numeric(0)
  acq_times <- numeric(0)
  
  # For each company, find if and when events occurred
  for(company in all_companies) {
    company_data <- data[data$cusip == company, ]
    
    # Check for bankruptcy
    if(any(company_data$bankruptcy == 1)) {
      bank_index <- which(company_data$bankruptcy == 1)[1]
      bank_times <- c(bank_times, company_data$tstop[bank_index])
    }
    
    # Check for acquisition
    if(any(company_data$acquisition == 1)) {
      acq_index <- which(company_data$acquisition == 1)[1]
      acq_times <- c(acq_times, company_data$tstop[acq_index])
    }
  }
  
  # Analyze bankruptcy timing
  if(length(bank_times) > 0) {
    bank_quantiles <- quantile(bank_times, probs = c(0.25, 0.5, 0.75), na.rm = TRUE)
    cat("\nBankruptcy timing (years):\n")
    cat("  Total bankruptcies:", length(bank_times), "\n")
    cat("  Minimum time:", round(min(bank_times), 2), "\n")
    cat("  25th percentile:", round(bank_quantiles[1], 2), "\n")
    cat("  Median time:", round(bank_quantiles[2], 2), "\n")
    cat("  75th percentile:", round(bank_quantiles[3], 2), "\n")
    cat("  Maximum time:", round(max(bank_times), 2), "\n")
  } else {
    cat("\nNo bankruptcy events found in the dataset.\n")
  }
  
  # Analyze acquisition timing
  if(length(acq_times) > 0) {
    acq_quantiles <- quantile(acq_times, probs = c(0.25, 0.5, 0.75), na.rm = TRUE)
    cat("\nAcquisition timing (years):\n")
    cat("  Total acquisitions:", length(acq_times), "\n")
    cat("  Minimum time:", round(min(acq_times), 2), "\n")
    cat("  25th percentile:", round(acq_quantiles[1], 2), "\n")
    cat("  Median time:", round(acq_quantiles[2], 2), "\n")
    cat("  75th percentile:", round(acq_quantiles[3], 2), "\n")
    cat("  Maximum time:", round(max(acq_times), 2), "\n")
  } else {
    cat("\nNo acquisition events found in the dataset.\n")
  }
  
  # Return event quantiles for adaptive horizon selection
  return(list(
    bankruptcy = if(length(bank_times) > 0) bank_quantiles else NULL,
    acquisition = if(length(acq_times) > 0) acq_quantiles else NULL
  ))
}

# Run event timing analysis
event_timing <- event_timing_analysis()
Analyzing event timing in the dataset:

Bankruptcy timing (years):
  Total bankruptcies: 176 
  Minimum time: 1 
  25th percentile: 4 
  Median time: 8.5 
  75th percentile: 15 
  Maximum time: 35 

Acquisition timing (years):
  Total acquisitions: 2154 
  Minimum time: 1 
  25th percentile: 5 
  Median time: 9 
  75th percentile: 15 
  Maximum time: 37 
# Based on analysis, determine appropriate time horizons
determine_horizons <- function(event_timing) {
  bank_horizons <- NULL
  acq_horizons <- NULL
  
  # For bankruptcy
  if(!is.null(event_timing$bankruptcy)) {
    # Use quartiles of actual event times as horizons
    bank_horizons <- c(event_timing$bankruptcy[1], 
                      event_timing$bankruptcy[2], 
                      event_timing$bankruptcy[3])
    bank_horizons <- round(bank_horizons) # Round to nearest year
    bank_horizons <- unique(bank_horizons) # Remove duplicates
  } else {
    # Default horizons
    bank_horizons <- c(3, 5, 10)
  }
  
  # For acquisition
  if(!is.null(event_timing$acquisition)) {
    # Use quartiles of actual event times as horizons
    acq_horizons <- c(event_timing$acquisition[1], 
                    event_timing$acquisition[2], 
                    event_timing$acquisition[3])
    acq_horizons <- round(acq_horizons) # Round to nearest year
    acq_horizons <- unique(acq_horizons) # Remove duplicates
  } else {
    # Default horizons
    acq_horizons <- c(3, 5, 10)
  }
  
  # Ensure we have short, medium, and long-term horizons
  if(length(bank_horizons) == 1) {
    bank_horizons <- c(max(1, bank_horizons - 2), bank_horizons, min(20, bank_horizons + 5))
  } else if(length(bank_horizons) == 2) {
    bank_horizons <- c(bank_horizons, min(20, max(bank_horizons) + 5))
  }
  
  if(length(acq_horizons) == 1) {
    acq_horizons <- c(max(1, acq_horizons - 2), acq_horizons, min(20, acq_horizons + 5))
  } else if(length(acq_horizons) == 2) {
    acq_horizons <- c(acq_horizons, min(20, max(acq_horizons) + 5))
  }
  
  return(list(bankruptcy = bank_horizons, acquisition = acq_horizons))
}

# Get appropriate horizons
horizons <- determine_horizons(event_timing)

cat("\nUsing adaptive time horizons based on event distribution:\n")

Using adaptive time horizons based on event distribution:
cat("Bankruptcy horizons (years):", paste(horizons$bankruptcy, collapse=", "), "\n")
Bankruptcy horizons (years): 4, 8, 15 
cat("Acquisition horizons (years):", paste(horizons$acquisition, collapse=", "), "\n")
Acquisition horizons (years): 5, 9, 15 
# Find no-macro versions of models
create_no_macro_model <- function(model, event_type) {
  # Get model formula
  model_formula <- formula(model)
  
  # Extract all terms
  all_terms <- attr(terms(model), "term.labels")
  
  # Identify macro variables (including their time interactions)
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  macro_terms <- c()
  
  for(term in all_terms) {
    # Check if term contains any macro variable
    is_macro <- FALSE
    for(macro in macro_vars) {
      if(grepl(macro, term)) {
        is_macro <- TRUE
        break
      }
    }
    
    if(is_macro) {
      macro_terms <- c(macro_terms, term)
    }
  }
  
  # If no macro terms in model, return original model
  if(length(macro_terms) == 0) {
    cat("No macroeconomic variables found in the model.\n")
    return(model)
  }
  
  cat("Found", length(macro_terms), "macroeconomic terms in model.\n")
  
  # Create formula without macro variables
  no_macro_formula <- model_formula
  for(term in macro_terms) {
    no_macro_formula <- update(no_macro_formula, paste0(". ~ . - ", term))
  }
  
  # Fit model without macro variables
  if(event_type == "bankruptcy") {
    no_macro_model <- coxph(no_macro_formula, data = data, ties = "efron")
  } else {
    no_macro_model <- coxph(no_macro_formula, data = data, ties = "efron")
  }
  
  return(no_macro_model)
}

# Create no-macro models
no_macro_bankruptcy_model <- create_no_macro_model(final_bankruptcy_model, "bankruptcy")
Found 3 macroeconomic terms in model.
no_macro_acquisition_model <- create_no_macro_model(final_acquisition_model, "acquisition")
Found 4 macroeconomic terms in model.
# Enhanced time-dependent AUC function with macro comparison
time_dependent_auc_with_macro <- function(full_model, no_macro_model, model_name, event_type, horizons) {
  cat("\nCalculating time-dependent AUC for", model_name, "model with macro comparison...\n")
  
  # Prepare results
  results <- data.frame(
    Horizon = horizons,
    Full_AUC = NA,
    NoMacro_AUC = NA,
    AUC_Difference = NA,
    AUC_Pct_Improvement = NA,
    Events = NA,
    Total = NA
  )
  
  for(i in 1:length(horizons)) {
    horizon <- horizons[i]
    cat("Processing horizon:", horizon, "years...\n")
    
    # Get all companies
    all_companies <- unique(data$cusip)
    
    # Prepare data for this horizon
    horizon_data <- data.frame()
    
    for(company in all_companies) {
      # Get data for this company
      company_data <- data[data$cusip == company, ]
      
      # Get data up to horizon
      company_at_horizon <- company_data[company_data$tstop <= horizon, ]
      
      if(nrow(company_at_horizon) > 0) {
        # Get last observation before horizon
        last_obs_index <- which.max(company_at_horizon$tstop)
        last_obs <- company_at_horizon[last_obs_index, ]
        
        # Add to horizon data
        horizon_data <- rbind(horizon_data, last_obs)
      }
    }
    
    if(nrow(horizon_data) > 0) {
      # Get event indicators
      event_indicator <- horizon_data[[event_type]]
      
      # Get risk scores for full model
      full_risk_scores <- tryCatch({
        predict(full_model, newdata = horizon_data, type = "risk")
      }, error = function(e) {
        cat("  Error calculating full model risk scores:", e$message, "\n")
        return(rep(NA, nrow(horizon_data)))
      })
      
      # Get risk scores for no-macro model
      no_macro_risk_scores <- tryCatch({
        predict(no_macro_model, newdata = horizon_data, type = "risk")
      }, error = function(e) {
        cat("  Error calculating no-macro model risk scores:", e$message, "\n")
        return(rep(NA, nrow(horizon_data)))
      })
      
      # Count events
      n_events <- sum(event_indicator)
      n_total <- nrow(horizon_data)
      
      cat("  Companies observed at horizon:", n_total, "\n")
      cat("  Events by horizon:", n_events, "(", round(100*n_events/n_total, 1), "%)\n")
      
      # Store event counts
      results$Events[i] <- n_events
      results$Total[i] <- n_total
      
      # Calculate AUCs if possible
      if(n_events > 0 && n_events < n_total) {
        if(requireNamespace("pROC", quietly = TRUE)) {
          library(pROC)
          
          # Calculate AUC for full model
          full_auc <- NA
          if(all(!is.na(full_risk_scores))) {
            tryCatch({
              full_roc <- roc(event_indicator, full_risk_scores, quiet = TRUE)
              full_auc <- as.numeric(auc(full_roc))
              results$Full_AUC[i] <- full_auc
              cat("  Full model AUC at", horizon, "years:", round(full_auc, 4), "\n")
            }, error = function(e) {
              cat("  Error calculating full model AUC:", e$message, "\n")
            })
          }
          
          # Calculate AUC for no-macro model
          no_macro_auc <- NA
          if(all(!is.na(no_macro_risk_scores))) {
            tryCatch({
              no_macro_roc <- roc(event_indicator, no_macro_risk_scores, quiet = TRUE)
              no_macro_auc <- as.numeric(auc(no_macro_roc))
              results$NoMacro_AUC[i] <- no_macro_auc
              cat("  No-macro model AUC at", horizon, "years:", round(no_macro_auc, 4), "\n")
            }, error = function(e) {
              cat("  Error calculating no-macro model AUC:", e$message, "\n")
            })
          }
          
          # Calculate improvement from macro variables
          if(!is.na(full_auc) && !is.na(no_macro_auc)) {
            auc_diff <- full_auc - no_macro_auc
            pct_improvement <- 100 * auc_diff / no_macro_auc
            
            results$AUC_Difference[i] <- auc_diff
            results$AUC_Pct_Improvement[i] <- pct_improvement
            
            cat("  Improvement from macro variables:", round(auc_diff, 4), 
                "(", round(pct_improvement, 2), "%)\n")
            
            # Test if improvement is significant
            tryCatch({
              roc_test <- roc.test(full_roc, no_macro_roc, method="delong")
              is_significant <- roc_test$p.value < 0.05
              
              cat("  Statistical significance: p =", format.pval(roc_test$p.value, digits = 3),
                  ifelse(is_significant, "(significant)", "(not significant)"), "\n")
            }, error = function(e) {
              cat("  Could not test statistical significance:", e$message, "\n")
            })
          }
        }
      } else {
        cat("  Insufficient events for AUC calculation\n")
      }
    } else {
      cat("  No data available for this horizon\n")
    }
  }
  
  # Print summary of results
  cat("\nTime-dependent AUC summary for", model_name, "prediction:\n")
  cat("----------------------------------------------------\n")
  
  for(i in 1:nrow(results)) {
    horizon <- results$Horizon[i]
    full_auc <- results$Full_AUC[i]
    no_macro_auc <- results$NoMacro_AUC[i]
    auc_diff <- results$AUC_Difference[i]
    pct_imp <- results$AUC_Pct_Improvement[i]
    events <- results$Events[i]
    total <- results$Total[i]
    
    if(!is.na(full_auc)) {
      cat(sprintf("At %d-year horizon (%d events, %.1f%%):\n", 
                horizon, events, 100*events/total))
      cat(sprintf("  Full model AUC = %.4f\n", full_auc))
      
      if(!is.na(no_macro_auc)) {
        cat(sprintf("  No-macro AUC = %.4f\n", no_macro_auc))
        cat(sprintf("  Macro contribution = %.4f (%.2f%%)\n", auc_diff, pct_imp))
        
        # Interpretation of macro contribution
        cat("  Interpretation: Macro variables ")
        if(auc_diff > 0.03) {
          cat("SUBSTANTIALLY improve prediction at this horizon\n")
        } else if(auc_diff > 0.01) {
          cat("MODERATELY improve prediction at this horizon\n")
        } else if(auc_diff > 0) {
          cat("SLIGHTLY improve prediction at this horizon\n")
        } else if(auc_diff > -0.01) {
          cat("do not meaningfully affect prediction at this horizon\n")
        } else {
          cat("WORSEN prediction at this horizon\n")
        }
      }
      cat("\n")
    } else {
      cat(sprintf("At %d-year horizon: AUC not available\n\n", horizon))
    }
  }
  
  return(results)
}

# Calculate time-dependent AUC with macro comparison
bankruptcy_td_auc <- time_dependent_auc_with_macro(
  final_bankruptcy_model, no_macro_bankruptcy_model,
  "bankruptcy", "bankruptcy", horizons$bankruptcy)

Calculating time-dependent AUC for bankruptcy model with macro comparison...
Processing horizon: 4 years...
  Companies observed at horizon: 5038 
  Events by horizon: 48 ( 1 %)
  Full model AUC at 4 years: 0.8424 
  No-macro model AUC at 4 years: 0.831 
  Improvement from macro variables: 0.0113 ( 1.36 %)
  Statistical significance: p = 0.0693 (not significant) 
Processing horizon: 8 years...
  Companies observed at horizon: 5038 
  Events by horizon: 88 ( 1.7 %)
  Full model AUC at 8 years: 0.8121 
  No-macro model AUC at 8 years: 0.8025 
  Improvement from macro variables: 0.0096 ( 1.19 %)
  Statistical significance: p = 0.0233 (significant) 
Processing horizon: 15 years...
  Companies observed at horizon: 5038 
  Events by horizon: 136 ( 2.7 %)
  Full model AUC at 15 years: 0.8167 
  No-macro model AUC at 15 years: 0.8047 
  Improvement from macro variables: 0.0119 ( 1.48 %)
  Statistical significance: p = 8.66e-05 (significant) 

Time-dependent AUC summary for bankruptcy prediction:
----------------------------------------------------
At 4-year horizon (48 events, 1.0%):
  Full model AUC = 0.8424
  No-macro AUC = 0.8310
  Macro contribution = 0.0113 (1.36%)
  Interpretation: Macro variables MODERATELY improve prediction at this horizon

At 8-year horizon (88 events, 1.7%):
  Full model AUC = 0.8121
  No-macro AUC = 0.8025
  Macro contribution = 0.0096 (1.19%)
  Interpretation: Macro variables SLIGHTLY improve prediction at this horizon

At 15-year horizon (136 events, 2.7%):
  Full model AUC = 0.8167
  No-macro AUC = 0.8047
  Macro contribution = 0.0119 (1.48%)
  Interpretation: Macro variables MODERATELY improve prediction at this horizon
acquisition_td_auc <- time_dependent_auc_with_macro(
  final_acquisition_model, no_macro_acquisition_model,
  "acquisition", "acquisition", horizons$acquisition)

Calculating time-dependent AUC for acquisition model with macro comparison...
Processing horizon: 5 years...
  Companies observed at horizon: 5038 
  Events by horizon: 612 ( 12.1 %)
  Full model AUC at 5 years: 0.5944 
  No-macro model AUC at 5 years: 0.6204 
  Improvement from macro variables: -0.0259 ( -4.18 %)
  Statistical significance: p = 3.23e-06 (significant) 
Processing horizon: 9 years...
  Companies observed at horizon: 5038 
  Events by horizon: 1096 ( 21.8 %)
  Full model AUC at 9 years: 0.5045 
  No-macro model AUC at 9 years: 0.5662 
  Improvement from macro variables: -0.0618 ( -10.91 %)
Warning: DeLong's test should not be applied to ROC curves with a different direction.
  Statistical significance: p = 0.000795 (significant) 
Processing horizon: 15 years...
  Companies observed at horizon: 5038 
  Events by horizon: 1628 ( 32.3 %)
  Full model AUC at 15 years: 0.5494 
  No-macro model AUC at 15 years: 0.5415 
  Improvement from macro variables: 0.0078 ( 1.45 %)
Warning: DeLong's test should not be applied to ROC curves with a different direction.
  Statistical significance: p = 0.627 (not significant) 

Time-dependent AUC summary for acquisition prediction:
----------------------------------------------------
At 5-year horizon (612 events, 12.1%):
  Full model AUC = 0.5944
  No-macro AUC = 0.6204
  Macro contribution = -0.0259 (-4.18%)
  Interpretation: Macro variables WORSEN prediction at this horizon

At 9-year horizon (1096 events, 21.8%):
  Full model AUC = 0.5045
  No-macro AUC = 0.5662
  Macro contribution = -0.0618 (-10.91%)
  Interpretation: Macro variables WORSEN prediction at this horizon

At 15-year horizon (1628 events, 32.3%):
  Full model AUC = 0.5494
  No-macro AUC = 0.5415
  Macro contribution = 0.0078 (1.45%)
  Interpretation: Macro variables SLIGHTLY improve prediction at this horizon
# Analyze macro variable importance across time horizons
cat("\nANALYSIS OF MACROECONOMIC IMPACT ACROSS TIME HORIZONS:\n")

ANALYSIS OF MACROECONOMIC IMPACT ACROSS TIME HORIZONS:
cat("----------------------------------------------------\n")
----------------------------------------------------
# Function to analyze macro impact trend
analyze_macro_trend <- function(td_auc_results, model_name) {
  if(all(is.na(td_auc_results$AUC_Difference))) {
    cat("Insufficient data to analyze macro impact trend for", model_name, "model.\n")
    return(NULL)
  }
  
  # Extract non-NA rows
  valid_rows <- !is.na(td_auc_results$AUC_Difference)
  valid_results <- td_auc_results[valid_rows, ]
  
  if(nrow(valid_results) < 2) {
    cat("Need at least two valid time horizons to analyze trend for", model_name, "model.\n")
    return(NULL)
  }
  
  # Sort by horizon
  valid_results <- valid_results[order(valid_results$Horizon), ]
  
  # Analyze trend
  first_horizon <- valid_results$Horizon[1]
  last_horizon <- valid_results$Horizon[nrow(valid_results)]
  
  first_impact <- valid_results$AUC_Difference[1]
  last_impact <- valid_results$AUC_Difference[nrow(valid_results)]
  
  impact_diff <- last_impact - first_impact
  
  cat("\nMacro impact trend for", model_name, "model:\n")
  cat("- Short-term horizon (", first_horizon, "years): Impact = ", round(first_impact, 4), "\n", sep="")
  cat("- Long-term horizon (", last_horizon, "years): Impact = ", round(last_impact, 4), "\n", sep="")
  
  if(impact_diff > 0.01) {
    cat("- Trend: Macro impact INCREASES with longer time horizons\n")
    cat("  Economic interpretation: Macroeconomic factors become MORE important\n")
    cat("  for predicting long-term outcomes compared to short-term outcomes\n")
  } else if(impact_diff < -0.01) {
    cat("- Trend: Macro impact DECREASES with longer time horizons\n")
    cat("  Economic interpretation: Macroeconomic factors are MORE important\n")
    cat("  for predicting short-term outcomes compared to long-term outcomes\n")
  } else {
    cat("- Trend: Macro impact is CONSISTENT across time horizons\n")
    cat("  Economic interpretation: Macroeconomic factors have similar importance\n")
    cat("  for both short-term and long-term prediction\n")
  }
  
  # Potential explanation based on model type
  if(model_name == "bankruptcy") {
    if(impact_diff > 0.01) {
      cat("  Business implication: Long-term bankruptcy risk is more systematically\n")
      cat("  linked to economic conditions, while short-term risk depends more on\n")
      cat("  company-specific financial health\n")
    } else if(impact_diff < -0.01) {
      cat("  Business implication: Immediate bankruptcy risk is more sensitive to\n")
      cat("  economic conditions, while long-term survival depends more on\n")
      cat("  company-specific factors and adaptability\n")
    }
  } else if(model_name == "acquisition") {
    if(impact_diff > 0.01) {
      cat("  Business implication: Long-term acquisition patterns follow economic\n")
      cat("  cycles more closely, while short-term M&A activity may be driven more\n")
      cat("  by strategic opportunities and company-specific factors\n")
    } else if(impact_diff < -0.01) {
      cat("  Business implication: Short-term acquisition likelihood is more\n")
      cat("  influenced by current economic conditions, while long-term acquisition\n")
      cat("  patterns depend more on company-specific attractiveness\n")
    }
  }
  
  return(impact_diff)
}

# Analyze trends for both models
bank_trend <- analyze_macro_trend(bankruptcy_td_auc, "bankruptcy")

Macro impact trend for bankruptcy model:
- Short-term horizon (4years): Impact = 0.0113
- Long-term horizon (15years): Impact = 0.0119
- Trend: Macro impact is CONSISTENT across time horizons
  Economic interpretation: Macroeconomic factors have similar importance
  for both short-term and long-term prediction
acq_trend <- analyze_macro_trend(acquisition_td_auc, "acquisition")

Macro impact trend for acquisition model:
- Short-term horizon (5years): Impact = -0.0259
- Long-term horizon (15years): Impact = 0.0078
- Trend: Macro impact INCREASES with longer time horizons
  Economic interpretation: Macroeconomic factors become MORE important
  for predicting long-term outcomes compared to short-term outcomes
  Business implication: Long-term acquisition patterns follow economic
  cycles more closely, while short-term M&A activity may be driven more
  by strategic opportunities and company-specific factors
# Compare trends between models
if(!is.null(bank_trend) && !is.null(acq_trend)) {
  cat("\nCOMPARATIVE ANALYSIS OF TIME-DEPENDENT MACRO EFFECTS:\n")
  cat("--------------------------------------------------\n")
  
  if(sign(bank_trend) == sign(acq_trend)) {
    if(bank_trend > 0 && acq_trend > 0) {
      cat("Both bankruptcy and acquisition models show INCREASING importance of\n")
      cat("macroeconomic factors over longer time horizons.\n\n")
      cat("This suggests that long-term corporate outcomes are more systematically\n")
      cat("linked to economic cycles than short-term outcomes, regardless of whether\n")
      cat("the outcome is bankruptcy or acquisition.\n")
    } else if(bank_trend < 0 && acq_trend < 0) {
      cat("Both bankruptcy and acquisition models show DECREASING importance of\n")
      cat("macroeconomic factors over longer time horizons.\n\n")
      cat("This suggests that immediate corporate outcomes are more sensitive to\n")
      cat("current economic conditions, while long-term outcomes depend more on\n")
      cat("company-specific factors and adaptability.\n")
    } else {
      cat("Both bankruptcy and acquisition models show CONSISTENT importance of\n")
      cat("macroeconomic factors across time horizons.\n\n")
      cat("This suggests that economic conditions have similar influence on corporate\n")
      cat("outcomes regardless of the time horizon considered.\n")
    }
  } else {
    cat("Bankruptcy and acquisition models show OPPOSITE trends in the importance\n")
    cat("of macroeconomic factors across time horizons.\n\n")
    
    if(bank_trend > 0 && acq_trend < 0) {
      cat("For bankruptcy: Macro factors become MORE important over longer horizons\n")
      cat("For acquisition: Macro factors become LESS important over longer horizons\n\n")
      cat("This divergent pattern suggests that:\n")
      cat("- Long-term corporate failure is more systematically linked to economic cycles\n")
      cat("- Short-term acquisition activity is more sensitive to current economic conditions\n")
      cat("- Different mechanisms may drive these two corporate outcomes over time\n")
    } else {
      cat("For bankruptcy: Macro factors become LESS important over longer horizons\n")
      cat("For acquisition: Macro factors become MORE important over longer horizons\n\n")
      cat("This divergent pattern suggests that:\n")
      cat("- Short-term corporate failure is more sensitive to current economic conditions\n")
      cat("- Long-term acquisition patterns follow economic cycles more closely\n")
      cat("- Different mechanisms may drive these two corporate outcomes over time\n")
    }
  }
}

COMPARATIVE ANALYSIS OF TIME-DEPENDENT MACRO EFFECTS:
--------------------------------------------------
Both bankruptcy and acquisition models show INCREASING importance of
macroeconomic factors over longer time horizons.

This suggests that long-term corporate outcomes are more systematically
linked to economic cycles than short-term outcomes, regardless of whether
the outcome is bankruptcy or acquisition.
# Summarize findings
cat("\nTIME-DEPENDENT PREDICTION SUMMARY:\n")

TIME-DEPENDENT PREDICTION SUMMARY:
cat("----------------------------------\n")
----------------------------------
if((all(is.na(bankruptcy_td_auc$Full_AUC)) || nrow(bankruptcy_td_auc) == 0) && 
   (all(is.na(acquisition_td_auc$Full_AUC)) || nrow(acquisition_td_auc) == 0)) {
  cat("Could not calculate time-dependent AUC values with the available data.\n")
  cat("Possible reasons include:\n")
  cat("1. Insufficient events at the specified time horizons\n")
  cat("2. Lack of variation in predictors or outcomes\n")
  cat("3. Data structure issues related to time-varying covariates\n\n")
  
  cat("For a robust evaluation, consider using:\n")
  cat("- The overall AUC values from section 11.4\n")
  cat("- In-sample vs. out-of-sample performance comparisons\n")
  cat("- Calibration assessment of predicted vs. observed event rates\n")
} else {
  valid_bk <- !is.na(bankruptcy_td_auc$Full_AUC)
  valid_acq <- !is.na(acquisition_td_auc$Full_AUC)
  
  cat("The time-dependent evaluation provides insights into how prediction performance\n")
  cat("and the importance of macroeconomic variables change across different time horizons.\n\n")
  
  # Summarize overall findings
  if(any(valid_bk)) {
    cat("Bankruptcy model:\n")
    cat("- Performance varies across time horizons, with AUC values ranging from\n")
    cat("  ", min(bankruptcy_td_auc$Full_AUC[valid_bk], na.rm = TRUE), 
        "to", max(bankruptcy_td_auc$Full_AUC[valid_bk], na.rm = TRUE), "\n")
    
    if(any(!is.na(bankruptcy_td_auc$AUC_Difference))) {
      cat("- Macroeconomic variables contribute between", 
          min(bankruptcy_td_auc$AUC_Difference, na.rm = TRUE), "and",
          max(bankruptcy_td_auc$AUC_Difference, na.rm = TRUE), "to AUC values\n")
      
      # Identify the horizon where macro variables have the greatest impact
      max_impact_idx <- which.max(bankruptcy_td_auc$AUC_Difference)
      max_impact_horizon <- bankruptcy_td_auc$Horizon[max_impact_idx]
      
      cat("- Macroeconomic variables have the greatest impact at the", 
          max_impact_horizon, "year horizon\n")
    }
    cat("\n")
  }
  
  if(any(valid_acq)) {
    cat("Acquisition model:\n")
    cat("- Performance varies across time horizons, with AUC values ranging from\n")
    cat("  ", min(acquisition_td_auc$Full_AUC[valid_acq], na.rm = TRUE), 
        "to", max(acquisition_td_auc$Full_AUC[valid_acq], na.rm = TRUE), "\n")
    
    if(any(!is.na(acquisition_td_auc$AUC_Difference))) {
      cat("- Macroeconomic variables contribute between", 
          min(acquisition_td_auc$AUC_Difference, na.rm = TRUE), "and",
          max(acquisition_td_auc$AUC_Difference, na.rm = TRUE), "to AUC values\n")
      
      # Identify the horizon where macro variables have the greatest impact
      max_impact_idx <- which.max(acquisition_td_auc$AUC_Difference)
      max_impact_horizon <- acquisition_td_auc$Horizon[max_impact_idx]
      
      cat("- Macroeconomic variables have the greatest impact at the", 
          max_impact_horizon, "year horizon\n")
    }
  }
  
  # Key implications
  cat("\nKey implications:\n")
  cat("1. The predictive ability of the models varies across different time horizons\n")
  cat("2. The contribution of macroeconomic variables is not constant over time\n")
  cat("3. Understanding the time-varying nature of economic effects is crucial for\n")
  cat("   developing robust risk management and strategic planning frameworks\n")
}
The time-dependent evaluation provides insights into how prediction performance
and the importance of macroeconomic variables change across different time horizons.

Bankruptcy model:
- Performance varies across time horizons, with AUC values ranging from
   0.8121166 to 0.8423597 
- Macroeconomic variables contribute between 0.009573003 and 0.0119489 to AUC values
- Macroeconomic variables have the greatest impact at the 15 year horizon

Acquisition model:
- Performance varies across time horizons, with AUC values ranging from
   0.5044683 to 0.5944412 
- Macroeconomic variables contribute between -0.06176882 and 0.007844215 to AUC values
- Macroeconomic variables have the greatest impact at the 15 year horizon

Key implications:
1. The predictive ability of the models varies across different time horizons
2. The contribution of macroeconomic variables is not constant over time
3. Understanding the time-varying nature of economic effects is crucial for
   developing robust risk management and strategic planning frameworks

In this section, we start with a comprehensive out-of-sample validation of the models. We split the dataset into training and testing sets based on company identifiers, ensuring that the same companies are not present in both sets. We then analyze the event distribution in both sets to ensure that they are comparable. We also check the distribution of macroeconomic variables across the sets to ensure that they are similar. The first part uses random sampling to create the training and testing sets, while the second part uses a more systematic approach based on company identifiers. We also check the distribution of macroeconomic variables across the sets to ensure that they are similar. We start with company-based data splitting, while the second part uses time-based (company) data splitting. We then refit the models on the training data, both with and without macroeconomic variables, and evaluate their performance on the testing data. Finally, we analyze the impact of macroeconomic variables on prediction performance and provide insights into their importance for bankruptcy and acquisition predictions.

#-------------------------------------------------------------
# STEP 12: Comprehensive Out-of-Sample Validation
#-------------------------------------------------------------
cat("\n=== STEP 12: COMPREHENSIVE OUT-OF-SAMPLE VALIDATION ===\n")

=== STEP 12: COMPREHENSIVE OUT-OF-SAMPLE VALIDATION ===
#-------------------------------------------------------------
# 12.1: Company-Based Data Splitting
#-------------------------------------------------------------
cat("\n12.1: COMPANY-BASED DATA SPLITTING\n")

12.1: COMPANY-BASED DATA SPLITTING
cat("--------------------------------------\n")
--------------------------------------
# Set seed for reproducibility
set.seed(123)

# Get unique company identifiers
all_companies <- unique(data$cusip)
n_companies <- length(all_companies)
cat("Total number of companies in dataset:", n_companies, "\n")
Total number of companies in dataset: 5038 
# Create training and testing sets (70% training, 30% testing)
train_size <- round(0.7 * n_companies)
train_companies <- sample(all_companies, size = train_size)
test_companies <- setdiff(all_companies, train_companies)

# Split data
train_data <- data[data$cusip %in% train_companies, ]
test_data <- data[data$cusip %in% test_companies, ]

cat("Training set:", length(train_companies), "companies (", nrow(train_data), "observations)\n")
Training set: 3527 companies ( 44594 observations)
cat("Testing set:", length(test_companies), "companies (", nrow(test_data), "observations)\n")
Testing set: 1511 companies ( 18997 observations)
# Check event distribution in training and testing sets
analyze_events <- function(data_subset, label) {
  # Get final observation for each company
  final_obs <- data_subset %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Count event types
  n_bankruptcy <- sum(final_obs$bankruptcy)
  n_acquisition <- sum(final_obs$acquisition)
  n_censored <- sum(final_obs$bankruptcy == 0 & final_obs$acquisition == 0)
  n_total <- nrow(final_obs)
  
  cat("\nEvent distribution in", label, "set:\n")
  cat("- Bankruptcies:", n_bankruptcy, sprintf("(%.1f%%)", 100*n_bankruptcy/n_total), "\n")
  cat("- Acquisitions:", n_acquisition, sprintf("(%.1f%%)", 100*n_acquisition/n_total), "\n")
  cat("- Censored:", n_censored, sprintf("(%.1f%%)", 100*n_censored/n_total), "\n")
  
  return(data.frame(
    bankruptcies = n_bankruptcy,
    acquisitions = n_acquisition,
    censored = n_censored,
    total = n_total
  ))
}

train_events <- analyze_events(train_data, "training")

Event distribution in training set:
- Bankruptcies: 124 (3.5%) 
- Acquisitions: 1533 (43.5%) 
- Censored: 1870 (53.0%) 
test_events <- analyze_events(test_data, "testing")

Event distribution in testing set:
- Bankruptcies: 52 (3.4%) 
- Acquisitions: 621 (41.1%) 
- Censored: 838 (55.5%) 
# Check distribution of macroeconomic variables across sets
check_macro_distribution <- function(train_data, test_data) {
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  available_macro <- intersect(macro_vars, colnames(train_data))
  
  if(length(available_macro) > 0) {
    cat("\nDistribution of macroeconomic variables across sets:\n")
    cat("--------------------------------------------------------\n")
    
    for(var in available_macro) {
      train_mean <- mean(train_data[[var]], na.rm = TRUE)
      train_sd <- sd(train_data[[var]], na.rm = TRUE)
      train_min <- min(train_data[[var]], na.rm = TRUE)
      train_max <- max(train_data[[var]], na.rm = TRUE)
      
      test_mean <- mean(test_data[[var]], na.rm = TRUE)
      test_sd <- sd(test_data[[var]], na.rm = TRUE)
      test_min <- min(test_data[[var]], na.rm = TRUE)
      test_max <- max(test_data[[var]], na.rm = TRUE)
      
      cat("\nVariable:", var, "\n")
      cat("Training set: Mean =", round(train_mean, 2), "SD =", round(train_sd, 2), 
          "Range =", round(train_min, 2), "to", round(train_max, 2), "\n")
      cat("Testing set:  Mean =", round(test_mean, 2), "SD =", round(test_sd, 2), 
          "Range =", round(test_min, 2), "to", round(test_max, 2), "\n")
      
      # Calculate the difference in means as percentage of training SD
      mean_diff <- abs(train_mean - test_mean)
      pct_diff <- mean_diff / train_sd * 100
      
      cat("Difference:   ", round(mean_diff, 2), "(", round(pct_diff, 1), 
          "% of training SD)\n")
      
      if(pct_diff > 25) {
        cat("WARNING: Substantial difference in distribution of", var, "between sets\n")
        cat("         This may affect the generalizability of economic effects\n")
      }
    }
  } else {
    cat("\nNo macroeconomic variables found in the dataset.\n")
  }
}

check_macro_distribution(train_data, test_data)

Distribution of macroeconomic variables across sets:
--------------------------------------------------------

Variable: gdp_growth 
Training set: Mean = 2.68 SD = 1.69 Range = -2.58 to 6.06 
Testing set:  Mean = 2.67 SD = 1.71 Range = -2.58 to 6.06 
Difference:    0.01 ( 0.5 % of training SD)

Variable: gdp_deflator 
Training set: Mean = 2.25 SD = 1.19 Range = 0.62 to 7.13 
Testing set:  Mean = 2.26 SD = 1.22 Range = 0.62 to 7.13 
Difference:    0.02 ( 1.4 % of training SD)

Variable: unemployement 
Training set: Mean = 5.57 SD = 1.6 Range = 3.64 to 9.63 
Testing set:  Mean = 5.56 SD = 1.61 Range = 3.64 to 9.63 
Difference:    0 ( 0.2 % of training SD)
# Ensure the time variable exists in both sets
if(!"tt" %in% colnames(train_data)) train_data$tt <- train_data$tstop
if(!"tt" %in% colnames(test_data)) test_data$tt <- test_data$tstop
#-------------------------------------------------------------
# 12.2: Refit Models on Training Data (With and Without Macro)
#-------------------------------------------------------------
cat("\n12.2: REFITTING MODELS ON TRAINING DATA\n")

12.2: REFITTING MODELS ON TRAINING DATA
cat("--------------------------------------\n")
--------------------------------------
# Function to create formula without macro variables
create_no_macro_formula <- function(original_formula) {
  # Extract terms from the formula
  all_terms <- attr(terms(as.formula(original_formula)), "term.labels")
  
  # Identify macro variables (including their time interactions)
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  macro_terms <- c()
  
  for(term in all_terms) {
    # Check if term contains any macro variable
    is_macro <- FALSE
    for(macro in macro_vars) {
      if(grepl(macro, term)) {
        is_macro <- TRUE
        break
      }
    }
    
    if(is_macro) {
      macro_terms <- c(macro_terms, term)
    }
  }
  
  # If no macro terms, return original formula
  if(length(macro_terms) == 0) {
    return(original_formula)
  }
  
  # Create formula without macro variables
  no_macro_formula <- original_formula
  for(term in macro_terms) {
    no_macro_formula <- update(as.formula(no_macro_formula), paste0(". ~ . - ", term))
  }
  
  return(formula(no_macro_formula))
}

# For bankruptcy model
cat("Refitting bankruptcy model on training data...\n")
Refitting bankruptcy model on training data...
# Get formula from the final model
bank_formula <- formula(final_bankruptcy_model)

# Create version without macro variables
no_macro_bank_formula <- create_no_macro_formula(bank_formula)

# Check if formulas are different
has_macro_bank <- !identical(bank_formula, no_macro_bank_formula)

if(has_macro_bank) {
  cat("Bankruptcy model contains macroeconomic variables.\n")
  cat("Fitting two versions for comparison:\n")
  
  # Fit full model with macro variables
  train_bank_model <- coxph(bank_formula, data = train_data, ties = "efron")
  cat("- Full model with macro variables: fitted successfully\n")
  cat("  Number of coefficients:", length(coef(train_bank_model)), "\n")
  
  # Fit version without macro variables
  train_bank_no_macro_model <- coxph(no_macro_bank_formula, data = train_data, ties = "efron")
  cat("- Model without macro variables: fitted successfully\n")
  cat("  Number of coefficients:", length(coef(train_bank_no_macro_model)), "\n")
} else {
  cat("Bankruptcy model does not contain macroeconomic variables.\n")
  train_bank_model <- coxph(bank_formula, data = train_data, ties = "efron")
  cat("Model fitted successfully.\n")
  cat("Number of coefficients:", length(coef(train_bank_model)), "\n")
  
  # Set no-macro model to be the same as full model
  train_bank_no_macro_model <- train_bank_model
}
Bankruptcy model contains macroeconomic variables.
Fitting two versions for comparison:
- Full model with macro variables: fitted successfully
  Number of coefficients: 23 
- Model without macro variables: fitted successfully
  Number of coefficients: 20 
# For acquisition model
cat("\nRefitting acquisition model on training data...\n")

Refitting acquisition model on training data...
# Get formula from the final model
acq_formula <- formula(final_acquisition_model)

# Create version without macro variables
no_macro_acq_formula <- create_no_macro_formula(acq_formula)

# Check if formulas are different
has_macro_acq <- !identical(acq_formula, no_macro_acq_formula)

if(has_macro_acq) {
  cat("Acquisition model contains macroeconomic variables.\n")
  cat("Fitting two versions for comparison:\n")
  
  # Fit full model with macro variables
  train_acq_model <- coxph(acq_formula, data = train_data, ties = "efron")
  cat("- Full model with macro variables: fitted successfully\n")
  cat("  Number of coefficients:", length(coef(train_acq_model)), "\n")
  
  # Fit version without macro variables
  train_acq_no_macro_model <- coxph(no_macro_acq_formula, data = train_data, ties = "efron")
  cat("- Model without macro variables: fitted successfully\n")
  cat("  Number of coefficients:", length(coef(train_acq_no_macro_model)), "\n")
} else {
  cat("Acquisition model does not contain macroeconomic variables.\n")
  train_acq_model <- coxph(acq_formula, data = train_data, ties = "efron")
  cat("Model fitted successfully.\n")
  cat("Number of coefficients:", length(coef(train_acq_model)), "\n")
  
  # Set no-macro model to be the same as full model
  train_acq_no_macro_model <- train_acq_model
}
Acquisition model contains macroeconomic variables.
Fitting two versions for comparison:
- Full model with macro variables: fitted successfully
  Number of coefficients: 18 
- Model without macro variables: fitted successfully
  Number of coefficients: 14 
#-------------------------------------------------------------
# 12.3: In-Sample Performance Evaluation
#-------------------------------------------------------------
cat("\n12.3: IN-SAMPLE PERFORMANCE EVALUATION\n")

12.3: IN-SAMPLE PERFORMANCE EVALUATION
cat("--------------------------------------\n")
--------------------------------------
# Function to evaluate in-sample performance
evaluate_in_sample <- function(model, event_type, data_subset, model_name) {
  cat("Evaluating in-sample performance for", model_name, "model...\n")
  
  # Get last observation for each company
  final_obs <- data_subset %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate risk scores
  risk_scores <- predict(model, newdata = final_obs, type = "risk")
  
  # Evaluate with ROC/AUC
  if(requireNamespace("pROC", quietly = TRUE)) {
    library(pROC)
    
    tryCatch({
      roc_obj <- roc(final_obs[[event_type]], risk_scores, quiet = TRUE)
      auc_val <- as.numeric(auc(roc_obj))
      
      cat("In-sample AUC:", round(auc_val, 4), "\n")
      
      # Calculate optimal threshold
      coords <- coords(roc_obj, "best", ret = c("threshold", "sensitivity", "specificity"))
      threshold <- as.numeric(coords["threshold"])
      sensitivity <- as.numeric(coords["sensitivity"])
      specificity <- as.numeric(coords["specificity"])
      
      cat("Optimal threshold:", round(threshold, 4), "\n")
      cat("Sensitivity at optimal threshold:", round(sensitivity, 4), "\n")
      cat("Specificity at optimal threshold:", round(specificity, 4), "\n")
      
      # Additional metrics at optimal threshold
      predictions <- ifelse(risk_scores >= threshold, 1, 0)
      true_values <- final_obs[[event_type]]
      
      TP <- sum(predictions == 1 & true_values == 1)
      TN <- sum(predictions == 0 & true_values == 0)
      FP <- sum(predictions == 1 & true_values == 0)
      FN <- sum(predictions == 0 & true_values == 1)
      
      accuracy <- (TP + TN) / (TP + TN + FP + FN)
      precision <- TP / (TP + FP)
      recall <- sensitivity  # Same as sensitivity
      f1_score <- 2 * precision * recall / (precision + recall)
      
      cat("Accuracy:", round(accuracy, 4), "\n")
      cat("Precision:", round(precision, 4), "\n")
      cat("F1 Score:", round(f1_score, 4), "\n")
      
      # Return performance metrics
      return(list(
        auc = auc_val,
        threshold = threshold,
        sensitivity = sensitivity,
        specificity = specificity,
        accuracy = accuracy,
        precision = precision,
        recall = recall,
        f1_score = f1_score
      ))
    }, error = function(e) {
      cat("Error calculating in-sample performance:", e$message, "\n")
      return(NULL)
    })
  } else {
    cat("pROC package not available for ROC analysis.\n")
    return(NULL)
  }
}

# Evaluate in-sample performance for full models
bank_in_sample <- evaluate_in_sample(train_bank_model, "bankruptcy", train_data, "bankruptcy")
Evaluating in-sample performance for bankruptcy model...
In-sample AUC: 0.826 
Optimal threshold: 6.8424 
Sensitivity at optimal threshold: 0.7581 
Specificity at optimal threshold: 0.7972 
Accuracy: 0.7959 
Precision: 0.1199 
F1 Score: 0.207 
acq_in_sample <- evaluate_in_sample(train_acq_model, "acquisition", train_data, "acquisition")
Evaluating in-sample performance for acquisition model...
In-sample AUC: 0.5276 
Optimal threshold: 0.9753 
Sensitivity at optimal threshold: 0.7736 
Specificity at optimal threshold: 0.322 
Accuracy: 0.5183 
Precision: 0.4673 
F1 Score: 0.5827 
# Evaluate in-sample performance for no-macro models (if different)
if(has_macro_bank) {
  bank_in_sample_no_macro <- evaluate_in_sample(train_bank_no_macro_model, "bankruptcy", train_data, 
                                              "bankruptcy (no macro)")
}
Evaluating in-sample performance for bankruptcy (no macro) model...
In-sample AUC: 0.8221 
Optimal threshold: 7.3194 
Sensitivity at optimal threshold: 0.7581 
Specificity at optimal threshold: 0.8078 
Accuracy: 0.8061 
Precision: 0.1257 
F1 Score: 0.2156 
if(has_macro_acq) {
  acq_in_sample_no_macro <- evaluate_in_sample(train_acq_no_macro_model, "acquisition", train_data, 
                                             "acquisition (no macro)")
}
Evaluating in-sample performance for acquisition (no macro) model...
In-sample AUC: 0.5926 
Optimal threshold: 1.1538 
Sensitivity at optimal threshold: 0.7449 
Specificity at optimal threshold: 0.4343 
Accuracy: 0.5693 
Precision: 0.5031 
F1 Score: 0.6006 
#-------------------------------------------------------------
# 12.4: Out-of-Sample Performance Evaluation & Macro Impact
#-------------------------------------------------------------
cat("\n12.4: OUT-OF-SAMPLE PERFORMANCE & MACRO IMPACT EVALUATION\n")

12.4: OUT-OF-SAMPLE PERFORMANCE & MACRO IMPACT EVALUATION
cat("------------------------------------------------------\n")
------------------------------------------------------
# Function to evaluate out-of-sample performance
evaluate_oos_performance <- function(model, no_macro_model, test_data, event_type, model_name) {
  cat("\nEvaluating out-of-sample performance for", model_name, "model...\n")
  
  # Get last observation for each company in test set
  test_last_obs <- test_data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate risk scores from full model
  full_risk_scores <- predict(model, newdata = test_last_obs, type = "risk")
  
  # Calculate risk scores from model without macro variables
  no_macro_risk_scores <- predict(no_macro_model, newdata = test_last_obs, type = "risk")
  
  # Calculate AUC using pROC package
  if(requireNamespace("pROC", quietly = TRUE)) {
    library(pROC)
    
    # Create ROC objects
    full_roc <- roc(test_last_obs[[event_type]], full_risk_scores, quiet = TRUE)
    no_macro_roc <- roc(test_last_obs[[event_type]], no_macro_risk_scores, quiet = TRUE)
    
    # Calculate AUCs
    full_auc <- auc(full_roc)
    no_macro_auc <- auc(no_macro_roc)
    
    # Calculate confidence intervals
    full_ci <- ci(full_roc)
    no_macro_ci <- ci(no_macro_roc)
    
    # Print results
    cat("Out-of-sample AUC with all variables:", round(full_auc, 4), "\n")
    cat("95% Confidence Interval:", paste(round(full_ci[1:2], 4), collapse=" - "), "\n")
    
    if(!identical(model, no_macro_model)) {
      cat("Out-of-sample AUC without macro variables:", round(no_macro_auc, 4), "\n")
      cat("95% Confidence Interval:", paste(round(no_macro_ci[1:2], 4), collapse=" - "), "\n")
      
      # Calculate improvement from macro variables
      auc_diff <- full_auc - no_macro_auc
      pct_improvement <- auc_diff / no_macro_auc * 100
      
      cat("AUC improvement from macro variables:", round(auc_diff, 4), 
          "(", round(pct_improvement, 1), "%)\n")
      
      # Test if improvement is significant
      roc_test <- roc.test(full_roc, no_macro_roc, method = "delong")
      cat("Statistical significance: p =", format.pval(roc_test$p.value, digits = 3), "\n")
      
      if(roc_test$p.value < 0.05) {
        cat("The improvement from macroeconomic variables is STATISTICALLY SIGNIFICANT (p < 0.05)\n")
      } else {
        cat("The improvement from macroeconomic variables is not statistically significant (p >= 0.05)\n")
      }
    }
    
    # Calculate optimal thresholds and classification metrics
    full_coords <- coords(full_roc, "best", ret = c("threshold", "sensitivity", "specificity"))
    optimal_threshold <- as.numeric(full_coords["threshold"])
    optimal_sensitivity <- as.numeric(full_coords["sensitivity"])
    optimal_specificity <- as.numeric(full_coords["specificity"])
    
    # Make predictions at optimal threshold
    predictions <- ifelse(full_risk_scores >= optimal_threshold, 1, 0)
    
    # Create confusion matrix
    TP <- sum(predictions == 1 & test_last_obs[[event_type]] == 1)
    TN <- sum(predictions == 0 & test_last_obs[[event_type]] == 0)
    FP <- sum(predictions == 1 & test_last_obs[[event_type]] == 0)
    FN <- sum(predictions == 0 & test_last_obs[[event_type]] == 1)
    
    # Calculate metrics
    accuracy <- (TP + TN) / (TP + TN + FP + FN)
    precision <- if(TP + FP > 0) TP / (TP + FP) else 0
    recall <- if(TP + FN > 0) TP / (TP + FN) else 0
    f1_score <- if(precision + recall > 0) 2 * precision * recall / (precision + recall) else 0
    
    cat("\nOut-of-sample classification metrics at optimal threshold:\n")
    cat("Accuracy:", round(accuracy, 4), "\n")
    cat("Precision:", round(precision, 4), "\n")
    cat("Recall:", round(recall, 4), "\n")
    cat("F1 Score:", round(f1_score, 4), "\n")
    
    # If we have no-macro model, compare metrics
    if(!identical(model, no_macro_model)) {
      # Get optimal threshold for no-macro model
      no_macro_coords <- coords(no_macro_roc, "best", ret = c("threshold", "sensitivity", "specificity"))
      no_macro_threshold <- as.numeric(no_macro_coords["threshold"])
      
      # Make predictions at optimal threshold
      no_macro_predictions <- ifelse(no_macro_risk_scores >= no_macro_threshold, 1, 0)
      
      # Create confusion matrix
      no_macro_TP <- sum(no_macro_predictions == 1 & test_last_obs[[event_type]] == 1)
      no_macro_TN <- sum(no_macro_predictions == 0 & test_last_obs[[event_type]] == 0)
      no_macro_FP <- sum(no_macro_predictions == 1 & test_last_obs[[event_type]] == 0)
      no_macro_FN <- sum(no_macro_predictions == 0 & test_last_obs[[event_type]] == 1)
      
      # Calculate metrics
      no_macro_accuracy <- (no_macro_TP + no_macro_TN) / (no_macro_TP + no_macro_TN + no_macro_FP + no_macro_FN)
      no_macro_precision <- if(no_macro_TP + no_macro_FP > 0) no_macro_TP / (no_macro_TP + no_macro_FP) else 0
      no_macro_recall <- if(no_macro_TP + no_macro_FN > 0) no_macro_TP / (no_macro_TP + no_macro_FN) else 0
      no_macro_f1 <- if(no_macro_precision + no_macro_recall > 0) 
                     2 * no_macro_precision * no_macro_recall / (no_macro_precision + no_macro_recall) else 0
      
      cat("\nComparison of classification metrics:\n")
      cat("                   | With Macro | Without Macro | Difference\n")
      cat("-----------------------------------------------------------\n")
      cat(sprintf("Accuracy          | %10.4f | %13.4f | %+10.4f\n", 
                accuracy, no_macro_accuracy, accuracy - no_macro_accuracy))
      cat(sprintf("Precision         | %10.4f | %13.4f | %+10.4f\n", 
                precision, no_macro_precision, precision - no_macro_precision))
      cat(sprintf("Recall            | %10.4f | %13.4f | %+10.4f\n", 
                recall, no_macro_recall, recall - no_macro_recall))
      cat(sprintf("F1 Score          | %10.4f | %13.4f | %+10.4f\n", 
                f1_score, no_macro_f1, f1_score - no_macro_f1))
    }
    
    # Return performance metrics
    return(list(
      full_auc = full_auc,
      full_ci = full_ci,
      no_macro_auc = no_macro_auc,
      no_macro_ci = no_macro_ci,
      auc_diff = if(!identical(model, no_macro_model)) auc_diff else NA,
      p_value = if(!identical(model, no_macro_model)) roc_test$p.value else NA,
      accuracy = accuracy,
      precision = precision,
      recall = recall,
      f1_score = f1_score,
      no_macro_accuracy = if(!identical(model, no_macro_model)) no_macro_accuracy else NA,
      no_macro_precision = if(!identical(model, no_macro_model)) no_macro_precision else NA,
      no_macro_recall = if(!identical(model, no_macro_model)) no_macro_recall else NA,
      no_macro_f1 = if(!identical(model, no_macro_model)) no_macro_f1 else NA
    ))
  } else {
    cat("pROC package not available. Please install it for ROC analysis.\n")
    return(NULL)
  }
}

# Evaluate bankruptcy model
bankruptcy_oos <- evaluate_oos_performance(
  train_bank_model, 
  train_bank_no_macro_model, 
  test_data, 
  "bankruptcy", 
  "bankruptcy"
)

Evaluating out-of-sample performance for bankruptcy model...
Out-of-sample AUC with all variables: 0.799 
95% Confidence Interval: 0.7486 - 0.799 
Out-of-sample AUC without macro variables: 0.8071 
95% Confidence Interval: 0.7579 - 0.8071 
AUC improvement from macro variables: -0.0081 ( -1 %)
Statistical significance: p = 0.184 
The improvement from macroeconomic variables is not statistically significant (p >= 0.05)

Out-of-sample classification metrics at optimal threshold:
Accuracy: 0.6797 
Precision: 0.0878 
Recall: 0.8846 
F1 Score: 0.1597 

Comparison of classification metrics:
                   | With Macro | Without Macro | Difference
-----------------------------------------------------------
Accuracy          |     0.6797 |        0.7022 |    -0.0225
Precision         |     0.0878 |        0.0905 |    -0.0027
Recall            |     0.8846 |        0.8462 |    +0.0385
F1 Score          |     0.1597 |        0.1636 |    -0.0038
# Evaluate acquisition model
acquisition_oos <- evaluate_oos_performance(
  train_acq_model, 
  train_acq_no_macro_model, 
  test_data, 
  "acquisition", 
  "acquisition"
)

Evaluating out-of-sample performance for acquisition model...
Out-of-sample AUC with all variables: 0.5285 
95% Confidence Interval: 0.4996 - 0.5285 
Out-of-sample AUC without macro variables: 0.5831 
95% Confidence Interval: 0.5547 - 0.5831 
AUC improvement from macro variables: -0.0546 ( -9.4 %)
Statistical significance: p = <2e-16 
The improvement from macroeconomic variables is STATISTICALLY SIGNIFICANT (p < 0.05)

Out-of-sample classification metrics at optimal threshold:
Accuracy: 0.5156 
Precision: 0.4497 
Recall: 0.7987 
F1 Score: 0.5754 

Comparison of classification metrics:
                   | With Macro | Without Macro | Difference
-----------------------------------------------------------
Accuracy          |     0.5156 |        0.5328 |    -0.0172
Precision         |     0.4497 |        0.4647 |    -0.0150
Recall            |     0.7987 |        0.9018 |    -0.1031
F1 Score          |     0.5754 |        0.6134 |    -0.0380

Now we do an innovative analysis of the model performance across different economic conditions. We categorize the test data into three groups based on the last observation of each company: “Strong”, “Mixed”, and “Weak” economy. We then evaluate the model performance for each condition, comparing the full model with macroeconomic variables to the no-macro model. This analysis provides insights into how macroeconomic factors influence prediction performance under different economic conditions.

#-------------------------------------------------------------
# 12.5: Economic Condition-Specific Performance
#-------------------------------------------------------------
cat("\n12.5: ECONOMIC CONDITION-SPECIFIC PERFORMANCE\n")

12.5: ECONOMIC CONDITION-SPECIFIC PERFORMANCE
cat("--------------------------------------\n")
--------------------------------------
# Check if we have economic indicators in the test data
if(all(c("gdp_growth", "unemployement") %in% colnames(test_data))) {
  cat("Analyzing model performance across different economic conditions...\n")
  
  # Get last observation for each company in test set
  test_last_obs <- test_data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate median values to define conditions
  median_gdp <- median(test_last_obs$gdp_growth, na.rm = TRUE)
  median_unemp <- median(test_last_obs$unemployement, na.rm = TRUE)
  
  # Create economic condition groups
  test_last_obs$econ_condition <- "Mixed"
  test_last_obs$econ_condition[test_last_obs$gdp_growth > median_gdp & 
                             test_last_obs$unemployement < median_unemp] <- "Strong"
  test_last_obs$econ_condition[test_last_obs$gdp_growth < median_gdp & 
                             test_last_obs$unemployement > median_unemp] <- "Weak"
  
  # Calculate counts by condition
  strong_count <- sum(test_last_obs$econ_condition == "Strong")
  mixed_count <- sum(test_last_obs$econ_condition == "Mixed")
  weak_count <- sum(test_last_obs$econ_condition == "Weak")
  
  cat("\nEconomic condition distribution in test set:\n")
  cat("- Strong economy:", strong_count, "companies\n")
  cat("- Mixed economy:", mixed_count, "companies\n")
  cat("- Weak economy:", weak_count, "companies\n")
  
  # Function to evaluate performance by economic condition
  evaluate_by_condition <- function(model, no_macro_model, data, event_type, model_name) {
    cat("\nEvaluating", model_name, "model performance across economic conditions...\n")
    
    results <- data.frame(
      Condition = c("Strong", "Mixed", "Weak"),
      Count = c(strong_count, mixed_count, weak_count),
      Full_AUC = NA,
      No_Macro_AUC = NA,
      Improvement = NA,
      P_Value = NA
    )
    
    # Calculate risk scores for full and no-macro models
    full_risk_scores <- predict(model, newdata = data, type = "risk")
    no_macro_risk_scores <- predict(no_macro_model, newdata = data, type = "risk")
    
    # Add scores to data
    data$full_risk <- full_risk_scores
    data$no_macro_risk <- no_macro_risk_scores
    
    # Analyze each condition
    for(condition in c("Strong", "Mixed", "Weak")) {
      condition_data <- data[data$econ_condition == condition, ]
      
      if(nrow(condition_data) > 10 && sum(condition_data[[event_type]]) > 0) {
        cat("\n", condition, "Economy (", nrow(condition_data), "companies, ", 
            sum(condition_data[[event_type]]), event_type, "events ):\n", sep="")
        
        # Calculate AUCs for each model
        full_roc <- tryCatch({
          roc(condition_data[[event_type]], condition_data$full_risk, quiet = TRUE)
        }, error = function(e) NULL)
        
        no_macro_roc <- tryCatch({
          roc(condition_data[[event_type]], condition_data$no_macro_risk, quiet = TRUE)
        }, error = function(e) NULL)
        
        if(!is.null(full_roc) && !is.null(no_macro_roc)) {
          full_auc <- auc(full_roc)
          no_macro_auc <- auc(no_macro_roc)
          
          # Store results
          row_idx <- which(results$Condition == condition)
          results$Full_AUC[row_idx] <- full_auc
          results$No_Macro_AUC[row_idx] <- no_macro_auc
          results$Improvement[row_idx] <- full_auc - no_macro_auc
          
          # Test for significance if models are different
          if(!identical(model, no_macro_model)) {
            roc_test <- tryCatch({
              roc.test(full_roc, no_macro_roc, method = "delong")
            }, error = function(e) NULL)
            
            if(!is.null(roc_test)) {
              results$P_Value[row_idx] <- roc_test$p.value
            }
          }
          
          # Print results
          cat("- Full model AUC:", round(full_auc, 4), "\n")
          
          if(!identical(model, no_macro_model)) {
            cat("- No-macro model AUC:", round(no_macro_auc, 4), "\n")
            improvement <- full_auc - no_macro_auc
            pct_improvement <- improvement / no_macro_auc * 100
            
            cat("- Improvement from macro variables:", round(improvement, 4), 
                "(", round(pct_improvement, 1), "%)\n", sep="")
            
            if(!is.null(roc_test)) {
              cat("- Statistical significance: p =", format.pval(roc_test$p.value, digits = 3), "\n")
            }
          }
        } else {
          cat("- Insufficient data for ROC analysis\n")
        }
      } else {
        cat("\n", condition, "Economy: Insufficient data for analysis\n", sep="")
      }
    }
    
    # Compare across conditions
    cat("\nSummary of economic condition-specific performance:\n")
    print(results)
    
    # Interpret results
    if(!identical(model, no_macro_model) && 
       !all(is.na(results$Improvement)) && 
       max(results$Improvement, na.rm = TRUE) > 0) {
      
      # Find condition with maximum improvement
      max_idx <- which.max(results$Improvement)
      max_condition <- results$Condition[max_idx]
      max_improvement <- results$Improvement[max_idx]
      
      cat("\nInterpretation:\n")
      cat("- Macroeconomic variables contribute most during", max_condition, "economic conditions\n")
      cat("  (AUC improvement: +", round(max_improvement, 4), ")\n", sep="")
      
      if(max_condition == "Strong") {
        cat("- This suggests that economic factors may be more important for", 
            model_name, "prediction\n  during economic expansions\n")
      } else if(max_condition == "Weak") {
        cat("- This suggests that economic factors may be more important for", 
            model_name, "prediction\n  during economic downturns\n")
      }
    }
    
    return(results)
  }
  
  # Analyze bankruptcy model by economic condition
  bankruptcy_econ <- evaluate_by_condition(
    train_bank_model, 
    train_bank_no_macro_model, 
    test_last_obs, 
    "bankruptcy", 
    "bankruptcy"
  )
  
  # Analyze acquisition model by economic condition
  acquisition_econ <- evaluate_by_condition(
    train_acq_model, 
    train_acq_no_macro_model, 
    test_last_obs, 
    "acquisition", 
    "acquisition"
  )
  
  # Compare economic sensitivity between models
  if(has_macro_bank && has_macro_acq && 
     !all(is.na(bankruptcy_econ$Improvement)) && 
     !all(is.na(acquisition_econ$Improvement))) {
    
    cat("\nCOMPARATIVE ECONOMIC SENSITIVITY ANALYSIS:\n")
    cat("------------------------------------------\n")
    
    # Calculate maximum improvements for each model
    max_bk_improvement <- max(bankruptcy_econ$Improvement, na.rm = TRUE)
    max_bk_condition <- bankruptcy_econ$Condition[which.max(bankruptcy_econ$Improvement)]
    
    max_acq_improvement <- max(acquisition_econ$Improvement, na.rm = TRUE)
    max_acq_condition <- acquisition_econ$Condition[which.max(acquisition_econ$Improvement)]
    
    cat("Maximum macroeconomic contribution by model:\n")
    cat("- Bankruptcy model: +", round(max_bk_improvement, 4), 
        "during", max_bk_condition, "economy\n", sep="")
    cat("- Acquisition model: +", round(max_acq_improvement, 4), 
        "during", max_acq_condition, "economy\n", sep="")
    
    # Compare overall economic sensitivity
    if(max_bk_improvement > max_acq_improvement) {
      cat("\nThe bankruptcy model shows GREATER sensitivity to economic conditions\n")
      cat("This suggests that bankruptcy risk is more systematically linked to the\n")
      cat("broader economic environment than acquisition likelihood\n")
    } else if(max_acq_improvement > max_bk_improvement) {
      cat("\nThe acquisition model shows GREATER sensitivity to economic conditions\n")
      cat("This suggests that M&A activity is more systematically linked to the\n")
      cat("broader economic environment than bankruptcy risk\n")
    } else {
      cat("\nBoth models show SIMILAR sensitivity to economic conditions\n")
    }
    
    # Compare condition-specific sensitivity
    if(max_bk_condition != max_acq_condition) {
      cat("\nThe models show DIFFERENT patterns of economic sensitivity:\n")
      cat("- Bankruptcy prediction benefits most from macro variables during", 
          max_bk_condition, "economy\n")
      cat("- Acquisition prediction benefits most from macro variables during", 
          max_acq_condition, "economy\n")
      cat("\nThis suggests that different economic mechanisms drive these corporate outcomes\n")
    } else {
      cat("\nBoth models benefit most from macro variables during", max_bk_condition, "economy\n")
      cat("This suggests that similar economic mechanisms influence both outcomes\n")
    }
  }
} else {
  cat("Economic indicators not available in test data for condition-specific analysis.\n")
}
Analyzing model performance across different economic conditions...

Economic condition distribution in test set:
- Strong economy: 107 companies
- Mixed economy: 880 companies
- Weak economy: 524 companies

Evaluating bankruptcy model performance across economic conditions...

StrongEconomy (107companies, 9bankruptcyevents ):
- Full model AUC: 0.7188 
- No-macro model AUC: 0.7256 
- Improvement from macro variables:-0.0068(-0.9%)
- Statistical significance: p = 0.137 

MixedEconomy (880companies, 15bankruptcyevents ):
- Full model AUC: 0.8353 
- No-macro model AUC: 0.8339 
- Improvement from macro variables:0.0014(0.2%)
- Statistical significance: p = 0.913 

WeakEconomy (524companies, 28bankruptcyevents ):
- Full model AUC: 0.7495 
- No-macro model AUC: 0.7607 
- Improvement from macro variables:-0.0112(-1.5%)
- Statistical significance: p = 0.117 

Summary of economic condition-specific performance:

Interpretation:
- Macroeconomic variables contribute most during Mixed economic conditions
  (AUC improvement: +0.0014)

Evaluating acquisition model performance across economic conditions...

StrongEconomy (107companies, 64acquisitionevents ):
- Full model AUC: 0.6969 
- No-macro model AUC: 0.7318 
- Improvement from macro variables:-0.0349(-4.8%)
- Statistical significance: p = 0.0986 

MixedEconomy (880companies, 225acquisitionevents ):
Warning: DeLong's test should not be applied to ROC curves with a different direction.
- Full model AUC: 0.5488 
- No-macro model AUC: 0.5036 
- Improvement from macro variables:0.0452(9%)
- Statistical significance: p = 0.247 

WeakEconomy (524companies, 332acquisitionevents ):
- Full model AUC: 0.7401 
- No-macro model AUC: 0.7953 
- Improvement from macro variables:-0.0552(-6.9%)
- Statistical significance: p = 1.73e-05 

Summary of economic condition-specific performance:

Interpretation:
- Macroeconomic variables contribute most during Mixed economic conditions
  (AUC improvement: +0.0452)

COMPARATIVE ECONOMIC SENSITIVITY ANALYSIS:
------------------------------------------
Maximum macroeconomic contribution by model:
- Bankruptcy model: +0.0014duringMixedeconomy
- Acquisition model: +0.0452duringMixedeconomy

The acquisition model shows GREATER sensitivity to economic conditions
This suggests that M&A activity is more systematically linked to the
broader economic environment than bankruptcy risk

Both models benefit most from macro variables during Mixed economy
This suggests that similar economic mechanisms influence both outcomes

We now consider time horizons for out-of-sample evaluation. We use the previously identified bankruptcy and acquisition horizons from the training data to evaluate the models’ performance at these specific time points. This analysis helps us understand how well the models perform over time, particularly in predicting events that occur at different intervals.

#-------------------------------------------------------------
# 12.6: Out-of-Sample Time-Dependent Performance
#-------------------------------------------------------------
cat("\n12.6: OUT-OF-SAMPLE TIME-DEPENDENT PERFORMANCE\n")

12.6: OUT-OF-SAMPLE TIME-DEPENDENT PERFORMANCE
cat("--------------------------------------\n")
--------------------------------------
# Function for out-of-sample time-dependent evaluation
evaluate_time_dependent_out_of_sample <- function(model, model_name, event_type, data_subset, horizons) {
  cat("Evaluating out-of-sample time-dependent performance for", model_name, "model...\n")
  
  # Prepare results
  results <- data.frame(
    Horizon = horizons,
    AUC = NA,
    Events = NA
  )
  
  for(i in 1:length(horizons)) {
    horizon <- horizons[i]
    cat("Processing horizon:", horizon, "years...\n")
    
    # Get all companies
    test_companies <- unique(data_subset$cusip)
    
    # Prepare data for this horizon
    horizon_data <- data.frame()
    
    for(company in test_companies) {
      # Get data for this company
      company_data <- data_subset[data_subset$cusip == company, ]
      
      # Get data up to horizon
      company_at_horizon <- company_data[company_data$tstop <= horizon, ]
      
      if(nrow(company_at_horizon) > 0) {
        # Get last observation before horizon
        last_obs_index <- which.max(company_at_horizon$tstop)
        last_obs <- company_at_horizon[last_obs_index, ]
        
        # Add to horizon data
        horizon_data <- rbind(horizon_data, last_obs)
      }
    }
    
    if(nrow(horizon_data) > 0) {
      # Get event indicators and risk scores
      event_indicator <- horizon_data[[event_type]]
      
      risk_scores <- tryCatch({
        predict(model, newdata = horizon_data, type = "risk")
      }, error = function(e) {
        cat("  Error calculating risk scores:", e$message, "\n")
        return(rep(NA, nrow(horizon_data)))
      })
      
      # Count events
      n_events <- sum(event_indicator)
      n_total <- nrow(horizon_data)
      
      cat("  Companies observed at horizon:", n_total, "\n")
      cat("  Events by horizon:", n_events, "\n")
      
      # Calculate AUC if possible
      if(n_events > 0 && n_events < n_total && all(!is.na(risk_scores))) {
        if(requireNamespace("pROC", quietly = TRUE)) {
          library(pROC)
          tryCatch({
            roc_obj <- roc(event_indicator, risk_scores, quiet = TRUE)
            auc_val <- as.numeric(auc(roc_obj))
            
            # Store results
            results$AUC[i] <- auc_val
            results$Events[i] <- n_events
            
            cat("  Out-of-sample AUC at", horizon, "years:", round(auc_val, 4), "\n")
          }, error = function(e) {
            cat("  Error calculating AUC:", e$message, "\n")
          })
        }
      } else {
        cat("  Insufficient events for AUC calculation\n")
      }
    } else {
      cat("  No data available for this horizon\n")
    }
  }
  
  # Print results
  cat("\nOut-of-sample time-dependent AUC for", model_name, "prediction:\n")
  cat("------------------------------------------------\n")
  
  for(i in 1:nrow(results)) {
    horizon <- results$Horizon[i]
    auc_val <- results$AUC[i]
    events <- results$Events[i]
    
    if(!is.na(auc_val)) {
      cat(sprintf("At %d-year horizon: AUC = %.4f (%d events)\n", 
                horizon, auc_val, events))
    } else {
      cat(sprintf("At %d-year horizon: AUC not available\n", horizon))
    }
  }
  
  return(results)
}

# Use the same horizons as determined in Step 11.5
cat("Using previously identified time horizons for out-of-sample evaluation...\n")
Using previously identified time horizons for out-of-sample evaluation...
cat("Bankruptcy horizons (years):", paste(horizons$bankruptcy, collapse=", "), "\n")
Bankruptcy horizons (years): 4, 8, 15 
cat("Acquisition horizons (years):", paste(horizons$acquisition, collapse=", "), "\n")
Acquisition horizons (years): 5, 9, 15 
# Calculate out-of-sample time-dependent AUC
bankruptcy_td_out <- evaluate_time_dependent_out_of_sample(
  train_bank_model, "bankruptcy", "bankruptcy", test_data, horizons$bankruptcy)
Evaluating out-of-sample time-dependent performance for bankruptcy model...
Processing horizon: 4 years...
  Companies observed at horizon: 1511 
  Events by horizon: 9 
  Out-of-sample AUC at 4 years: 0.8117 
Processing horizon: 8 years...
  Companies observed at horizon: 1511 
  Events by horizon: 22 
  Out-of-sample AUC at 8 years: 0.7858 
Processing horizon: 15 years...
  Companies observed at horizon: 1511 
  Events by horizon: 37 
  Out-of-sample AUC at 15 years: 0.8071 

Out-of-sample time-dependent AUC for bankruptcy prediction:
------------------------------------------------
At 4-year horizon: AUC = 0.8117 (9 events)
At 8-year horizon: AUC = 0.7858 (22 events)
At 15-year horizon: AUC = 0.8071 (37 events)
acquisition_td_out <- evaluate_time_dependent_out_of_sample(
  train_acq_model, "acquisition", "acquisition", test_data, horizons$acquisition)
Evaluating out-of-sample time-dependent performance for acquisition model...
Processing horizon: 5 years...
  Companies observed at horizon: 1511 
  Events by horizon: 189 
  Out-of-sample AUC at 5 years: 0.5955 
Processing horizon: 9 years...
  Companies observed at horizon: 1511 
  Events by horizon: 320 
  Out-of-sample AUC at 9 years: 0.487 
Processing horizon: 15 years...
  Companies observed at horizon: 1511 
  Events by horizon: 474 
  Out-of-sample AUC at 15 years: 0.5358 

Out-of-sample time-dependent AUC for acquisition prediction:
------------------------------------------------
At 5-year horizon: AUC = 0.5955 (189 events)
At 9-year horizon: AUC = 0.4870 (320 events)
At 15-year horizon: AUC = 0.5358 (474 events)

This analysis provides insights into how the models perform over time, particularly in predicting events that occur at different intervals. It helps us understand the stability and reliability of the models’ predictions across various time horizons.

Finally, we conduct a coefficient stability analysis to compare the coefficients of the full model with those of the training model. This analysis helps us identify any significant changes in the coefficients, which may indicate instability or sensitivity to the training data.

#-------------------------------------------------------------
# 12.7: Coefficient Stability Analysis
#-------------------------------------------------------------
cat("\n12.7: COEFFICIENT STABILITY ANALYSIS\n")

12.7: COEFFICIENT STABILITY ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
# Function to compare coefficients between full and training models
compare_coefficients <- function(full_model, train_model, model_name) {
  cat("Comparing coefficients for", model_name, "model:\n\n")
  
  # Get coefficients from both models
  full_coef <- coef(full_model)
  train_coef <- coef(train_model)
  
  # Find common variables
  common_vars <- intersect(names(full_coef), names(train_coef))
  
  # Create comparison dataframe
  comparison <- data.frame(
    Variable = common_vars,
    Full_Model = full_coef[common_vars],
    Training_Model = train_coef[common_vars],
    Change_Pct = NA,
    Direction_Change = FALSE,
    stringsAsFactors = FALSE
  )
  
  # Calculate percent change and direction changes
  for(i in 1:nrow(comparison)) {
    full_val <- comparison$Full_Model[i]
    train_val <- comparison$Training_Model[i]
    
    # Percent change (using absolute values to handle negative coefficients)
    if(full_val != 0) {
      comparison$Change_Pct[i] <- abs((train_val - full_val) / full_val) * 100
    } else {
      comparison$Change_Pct[i] <- NA
    }
    
    # Direction change
    comparison$Direction_Change[i] <- sign(full_val) != sign(train_val)
  }
  
  # Sort by absolute percent change
  comparison <- comparison[order(comparison$Change_Pct, decreasing = TRUE), ]
  
  # Print table
  cat("Variable               Full Model    Train Model   % Change   Direction Change\n")
  cat("----------------------------------------------------------------------------\n")
  
  for(i in 1:nrow(comparison)) {
    var <- comparison$Variable[i]
    full_val <- comparison$Full_Model[i]
    train_val <- comparison$Training_Model[i]
    pct_change <- comparison$Change_Pct[i]
    dir_change <- comparison$Direction_Change[i]
    
    # Create formatted string
    cat(sprintf("%-20s %12.4f %12.4f %10.1f%%   %s\n",
              var, full_val, train_val, 
              ifelse(is.na(pct_change), 0, pct_change),
              ifelse(dir_change, "YES", "no")))
  }
  
  # Count direction changes
  n_dir_changes <- sum(comparison$Direction_Change)
  cat("\nVariables with direction changes:", n_dir_changes, "out of", nrow(comparison), "\n")
  
  # Identify unstable variables (large changes)
  unstable <- comparison[comparison$Change_Pct > 50 | comparison$Direction_Change, ]
  if(nrow(unstable) > 0) {
    cat("\nPotentially unstable variables (>50% change or direction change):\n")
    for(i in 1:nrow(unstable)) {
      cat("- ", unstable$Variable[i], "\n")
    }
  } else {
    cat("\nNo potentially unstable variables identified.\n")
  }
  
  # Check specifically for stability of macro variable coefficients
  if(has_macro_bank || has_macro_acq) {
    macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
    macro_coeffs <- comparison[grepl(paste(macro_vars, collapse="|"), comparison$Variable), ]
    
    if(nrow(macro_coeffs) > 0) {
      cat("\nStability of macroeconomic variable coefficients:\n")
      for(i in 1:nrow(macro_coeffs)) {
        var <- macro_coeffs$Variable[i]
        pct_change <- macro_coeffs$Change_Pct[i]
        dir_change <- macro_coeffs$Direction_Change[i]
        
        stability <- if(dir_change) {
                     "UNSTABLE (direction change)"
                   } else if(pct_change > 50) {
                     "UNSTABLE (large magnitude change)"
                   } else if(pct_change > 25) {
                     "MODERATELY STABLE"
                   } else {
                     "HIGHLY STABLE"
                   }
        
        cat("- ", var, ": ", stability, " (", round(pct_change, 1), "% change)\n", sep="")
      }
    }
  }
  
  # Return comparison data
  return(comparison)
}

# Compare coefficients for both models
bank_coef_comparison <- compare_coefficients(final_bankruptcy_model, train_bank_model, "bankruptcy")
Comparing coefficients for bankruptcy model:

Variable               Full Model    Train Model   % Change   Direction Change
----------------------------------------------------------------------------
gp_margin                  0.0628       0.1786      184.4%   no
unemployement             -0.0744      -0.1748      134.9%   no
ebit_growth               -0.0175      -0.0378      116.1%   no
LTMTA:tt                   0.0412       0.0006       98.6%   no
debt_ratio:tt              0.0277       0.0022       92.0%   no
EBIT_VOL_3Y               -0.0015      -0.0028       82.5%   no
gdp_growth                 0.0987       0.0241       75.6%   no
PRICE:tt                  -0.0112      -0.0183       62.6%   no
receivables_turnover       0.0013       0.0005       61.5%   no
PRICE                     -0.1722      -0.0752       56.3%   no
debt_service              -0.0034      -0.0050       47.2%   no
MBE                       -0.1640      -0.0919       43.9%   no
intangibility             -0.2944      -0.4126       40.1%   no
asset_turnover            -0.1689      -0.1170       30.7%   no
LTMTA                      3.6697       4.4828       22.2%   no
CASHMTA                   -2.1101      -2.5229       19.6%   no
NIMTA                     -2.1411      -2.5101       17.2%   no
cash_to_assets             1.4390       1.6187       12.5%   no
z_score                    0.0297       0.0327       10.2%   no
current_ratio              0.0836       0.0766        8.3%   no
wc_ratio                  -1.1042      -1.1921        8.0%   no
debt_ratio                -0.4929      -0.4583        7.0%   no
gdp_deflator              -0.1753      -0.1680        4.2%   no

Variables with direction changes: 0 out of 23 

Potentially unstable variables (>50% change or direction change):
-  gp_margin 
-  unemployement 
-  ebit_growth 
-  LTMTA:tt 
-  debt_ratio:tt 
-  EBIT_VOL_3Y 
-  gdp_growth 
-  PRICE:tt 
-  receivables_turnover 
-  PRICE 

Stability of macroeconomic variable coefficients:
- unemployement: UNSTABLE (large magnitude change) (134.9% change)
- gdp_growth: UNSTABLE (large magnitude change) (75.6% change)
- gdp_deflator: HIGHLY STABLE (4.2% change)
acq_coef_comparison <- compare_coefficients(final_acquisition_model, train_acq_model, "acquisition")
Comparing coefficients for acquisition model:

Variable               Full Model    Train Model   % Change   Direction Change
----------------------------------------------------------------------------
debt_ratio                 0.0224       0.0771      244.3%   no
NIMTA                      0.1884       0.2831       50.3%   no
gdp_growth                 0.0369       0.0257       30.4%   no
asset_turnover:tt          0.0130       0.0092       28.8%   no
LTMTA                     -0.0317      -0.0394       24.1%   no
gdp_deflator              -0.0381      -0.0309       19.0%   no
gp_margin                  0.0256       0.0209       18.6%   no
EBIT_VOL_3Y               -0.0076      -0.0089       17.8%   no
cash_to_assets             0.7378       0.6467       12.3%   no
receivables_turnover      -0.0033      -0.0029       10.1%   no
wc_ratio:tt                0.0464       0.0506        9.1%   no
wc_ratio                  -0.7110      -0.6565        7.7%   no
unemployement             -0.1341      -0.1417        5.7%   no
MBE                       -0.2303      -0.2222        3.5%   no
PRICE                      0.1328       0.1361        2.5%   no
unemployement:tt           0.0083       0.0082        1.0%   no
asset_turnover            -0.1873      -0.1856        0.9%   no
current_ratio             -0.0737      -0.0743        0.8%   no

Variables with direction changes: 0 out of 18 

Potentially unstable variables (>50% change or direction change):
-  debt_ratio 
-  NIMTA 

Stability of macroeconomic variable coefficients:
- gdp_growth: MODERATELY STABLE (30.4% change)
- gdp_deflator: HIGHLY STABLE (19% change)
- unemployement: HIGHLY STABLE (5.7% change)
- unemployement:tt: HIGHLY STABLE (1% change)

Now, instead of using the full dataset for cross-validation, we will use a company-based K-fold cross-validation approach. This method ensures that all observations from a single company are either in the training or testing set, preventing data leakage and providing a more realistic evaluation of model performance. We will also check the distribution of events across folds to ensure that they are balanced.

#-------------------------------------------------------------
# STEP 13: K-Fold Cross-Validation for Survival Analysis
#-------------------------------------------------------------
cat("\n=== STEP 13: K-FOLD CROSS-VALIDATION FOR SURVIVAL ANALYSIS ===\n")

=== STEP 13: K-FOLD CROSS-VALIDATION FOR SURVIVAL ANALYSIS ===
#-------------------------------------------------------------
# 13.1: Company-Based K-Fold Setup
#-------------------------------------------------------------
cat("\n13.1: COMPANY-BASED K-FOLD SETUP\n")

13.1: COMPANY-BASED K-FOLD SETUP
cat("--------------------------------------\n")
--------------------------------------
# Set seed for reproducibility
set.seed(456)

# Specify number of folds
k_folds <- 5
cat("Setting up", k_folds, "fold cross-validation...\n")
Setting up 5 fold cross-validation...
# Get unique company identifiers
all_companies <- unique(data$cusip)
n_companies <- length(all_companies)
cat("Total number of companies in dataset:", n_companies, "\n")
Total number of companies in dataset: 5038 
# Randomly assign companies to folds
fold_assignments <- sample(1:k_folds, n_companies, replace = TRUE)
company_folds <- data.frame(
  cusip = all_companies,
  fold = fold_assignments
)

# Check distribution of companies across folds
fold_counts <- table(company_folds$fold)
cat("Distribution of companies across folds:\n")
Distribution of companies across folds:
print(fold_counts)

   1    2    3    4    5 
 973 1029 1009 1006 1021 
# Create function to check event distribution in folds
check_fold_event_distribution <- function(data, company_folds) {
  # Create a data frame to store results
  fold_events <- data.frame(
    fold = 1:k_folds,
    n_companies = as.numeric(fold_counts),
    n_bankruptcy = NA,
    n_acquisition = NA,
    n_censored = NA
  )
  
  # Get final observation for each company
  final_obs <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Add fold information
  final_obs <- final_obs %>%
    left_join(company_folds, by = "cusip")
  
  # Count events by fold
  for (i in 1:k_folds) {
    fold_data <- final_obs %>% filter(fold == i)
    fold_events$n_bankruptcy[i] <- sum(fold_data$bankruptcy)
    fold_events$n_acquisition[i] <- sum(fold_data$acquisition)
    fold_events$n_censored[i] <- sum(fold_data$bankruptcy == 0 & fold_data$acquisition == 0)
  }
  
  # Calculate percentages
  fold_events$pct_bankruptcy <- fold_events$n_bankruptcy / fold_events$n_companies * 100
  fold_events$pct_acquisition <- fold_events$n_acquisition / fold_events$n_companies * 100
  fold_events$pct_censored <- fold_events$n_censored / fold_events$n_companies * 100
  
  return(fold_events)
}

# Check event distribution
fold_events <- check_fold_event_distribution(data, company_folds)
cat("\nEvent distribution across folds:\n")

Event distribution across folds:
print(fold_events[, c("fold", "n_companies", "n_bankruptcy", "n_acquisition", "n_censored")])
cat("\nEvent percentages across folds:\n")

Event percentages across folds:
print(fold_events[, c("fold", "pct_bankruptcy", "pct_acquisition", "pct_censored")])

# Check if distribution is too imbalanced
max_bankruptcy_diff <- max(fold_events$pct_bankruptcy) - min(fold_events$pct_bankruptcy)
max_acquisition_diff <- max(fold_events$pct_acquisition) - min(fold_events$pct_acquisition)

cat("\nMaximum difference in bankruptcy percentage across folds:", round(max_bankruptcy_diff, 2), "%\n")

Maximum difference in bankruptcy percentage across folds: 0.65 %
cat("Maximum difference in acquisition percentage across folds:", round(max_acquisition_diff, 2), "%\n")
Maximum difference in acquisition percentage across folds: 5.03 %
# If distribution is too imbalanced, consider stratified sampling
imbalance_threshold <- 5  # 5% difference threshold
if (max_bankruptcy_diff > imbalance_threshold || max_acquisition_diff > imbalance_threshold) {
  cat("\nWARNING: Event distribution across folds is imbalanced (>5% difference)\n")
  cat("Implementing stratified sampling to balance event distribution...\n")
  
  # Get event types for each company
  company_events <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    mutate(event_category = case_when(
      bankruptcy == 1 ~ "bankruptcy",
      acquisition == 1 ~ "acquisition",
      TRUE ~ "censored"
    )) %>%
    select(cusip, event_category) %>%
    ungroup()
  
  # Stratified sampling by event type
  set.seed(789)
  company_folds_stratified <- data.frame()
  
  for (event_type in c("bankruptcy", "acquisition", "censored")) {
    event_companies <- company_events %>% 
      filter(event_category == event_type) %>%
      pull(cusip)
    
    n_event_companies <- length(event_companies)
    event_folds <- sample(rep(1:k_folds, length.out = n_event_companies))
    
    event_df <- data.frame(
      cusip = event_companies,
      fold = event_folds,
      event_category = event_type
    )
    
    company_folds_stratified <- rbind(company_folds_stratified, event_df)
  }
  
  # Replace original fold assignments with stratified ones
  company_folds <- company_folds_stratified[, c("cusip", "fold")]
  
  # Recheck distribution
  fold_events_stratified <- check_fold_event_distribution(data, company_folds)
  cat("\nEvent distribution after stratification:\n")
  print(fold_events_stratified[, c("fold", "n_companies", "n_bankruptcy", "n_acquisition", "n_censored")])
  cat("\nEvent percentages after stratification:\n")
  print(fold_events_stratified[, c("fold", "pct_bankruptcy", "pct_acquisition", "pct_censored")])
  
  # Update fold events
  fold_events <- fold_events_stratified
}

WARNING: Event distribution across folds is imbalanced (>5% difference)
Implementing stratified sampling to balance event distribution...

Event distribution after stratification:

Event percentages after stratification:
# Store fold assignments for later use
fold_assignments <- company_folds
#-------------------------------------------------------------
# 13.2: K-Fold Cross-Validation Execution
#-------------------------------------------------------------
cat("\n13.2: K-FOLD CROSS-VALIDATION EXECUTION\n")

13.2: K-FOLD CROSS-VALIDATION EXECUTION
cat("--------------------------------------\n")
--------------------------------------
# Initialize data frames to store results
bankruptcy_cv_results <- data.frame(
  fold = 1:k_folds,
  auc_with_macro = NA,
  auc_no_macro = NA,
  improvement = NA,
  p_value = NA,
  concordance = NA
)

acquisition_cv_results <- data.frame(
  fold = 1:k_folds,
  auc_with_macro = NA,
  auc_no_macro = NA,
  improvement = NA,
  p_value = NA,
  concordance = NA
)

# Create copies of model formulas for full and no-macro models
bank_formula <- formula(final_bankruptcy_model)
no_macro_bank_formula <- create_no_macro_formula(bank_formula)
acq_formula <- formula(final_acquisition_model)
no_macro_acq_formula <- create_no_macro_formula(acq_formula)

# Check if models contain macro variables
has_macro_bank <- !identical(bank_formula, no_macro_bank_formula)
has_macro_acq <- !identical(acq_formula, no_macro_acq_formula)

# Execute k-fold cross-validation
for (fold_idx in 1:k_folds) {
  cat("\n----- Processing Fold", fold_idx, "of", k_folds, "-----\n")
  
  # Create training and test sets for this fold
  train_companies <- fold_assignments$cusip[fold_assignments$fold != fold_idx]
  test_companies <- fold_assignments$cusip[fold_assignments$fold == fold_idx]
  
  cv_train_data <- data[data$cusip %in% train_companies, ]
  cv_test_data <- data[data$cusip %in% test_companies, ]
  
  cat("Training set:", length(train_companies), "companies (", nrow(cv_train_data), "observations)\n")
  cat("Testing set:", length(test_companies), "companies (", nrow(cv_test_data), "observations)\n")
  
  # Ensure time variable exists
  if(!"tt" %in% colnames(cv_train_data)) cv_train_data$tt <- cv_train_data$tstop
  if(!"tt" %in% colnames(cv_test_data)) cv_test_data$tt <- cv_test_data$tstop
  
  # ----- Bankruptcy Model -----
  cat("\nFitting bankruptcy models for fold", fold_idx, "...\n")
  
  # Fit full model
  cv_bank_model <- tryCatch({
    coxph(bank_formula, data = cv_train_data, ties = "efron")
  }, error = function(e) {
    cat("Error fitting full bankruptcy model:", e$message, "\n")
    cat("Attempting to fit simplified model...\n")
    
    # Try to fit a simpler model without time interactions
    simple_bank_formula <- update(bank_formula, . ~ . - .:.)
    
    tryCatch({
      coxph(simple_bank_formula, data = cv_train_data, ties = "efron")
    }, error = function(e2) {
      cat("Error fitting simplified model:", e2$message, "\n")
      return(NULL)
    })
  })
  
  # Fit no-macro model (if applicable)
  cv_bank_no_macro_model <- if(has_macro_bank) {
    tryCatch({
      coxph(no_macro_bank_formula, data = cv_train_data, ties = "efron")
    }, error = function(e) {
      cat("Error fitting no-macro bankruptcy model:", e$message, "\n")
      return(NULL)
    })
  } else {
    cv_bank_model
  }
  
  # ----- Acquisition Model -----
  cat("\nFitting acquisition models for fold", fold_idx, "...\n")
  
  # Fit full model
  cv_acq_model <- tryCatch({
    coxph(acq_formula, data = cv_train_data, ties = "efron")
  }, error = function(e) {
    cat("Error fitting full acquisition model:", e$message, "\n")
    cat("Attempting to fit simplified model...\n")
    
    # Try to fit a simpler model without time interactions
    simple_acq_formula <- update(acq_formula, . ~ . - .:.)
    
    tryCatch({
      coxph(simple_acq_formula, data = cv_train_data, ties = "efron")
    }, error = function(e2) {
      cat("Error fitting simplified model:", e2$message, "\n")
      return(NULL)
    })
  })
  
  # Fit no-macro model (if applicable)
  cv_acq_no_macro_model <- if(has_macro_acq) {
    tryCatch({
      coxph(no_macro_acq_formula, data = cv_train_data, ties = "efron")
    }, error = function(e) {
      cat("Error fitting no-macro acquisition model:", e$message, "\n")
      return(NULL)
    })
  } else {
    cv_acq_model
  }
  
  # ----- Evaluation -----
  # Get last observation for each company in test set
  test_last_obs <- cv_test_data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Evaluate bankruptcy model
  if (!is.null(cv_bank_model)) {
    cat("\nEvaluating bankruptcy model...\n")
    
    tryCatch({
      # Get full model predictions
      bank_full_scores <- predict(cv_bank_model, newdata = test_last_obs, type = "risk")
      
      # Get no-macro model predictions (if applicable)
      bank_no_macro_scores <- if (!is.null(cv_bank_no_macro_model)) {
        predict(cv_bank_no_macro_model, newdata = test_last_obs, type = "risk")
      } else {
        bank_full_scores
      }
      
      # Calculate AUC
      full_bank_auc <- as.numeric(pROC::auc(pROC::roc(test_last_obs$bankruptcy, bank_full_scores, quiet = TRUE)))
      
      # Get concordance (Harrell's C-index) for bankruptcy model
      if (!is.null(cv_bank_model)) {
        full_bank_concordance <- as.numeric(cv_bank_model$concordance["concordance"])
        cat("Bankruptcy model concordance (C-index):", round(full_bank_concordance, 4), "\n")
      } else {
        full_bank_concordance <- NA
      }
      
      # Calculate no-macro AUC if applicable
      no_macro_bank_auc <- if (has_macro_bank && !is.null(cv_bank_no_macro_model)) {
        as.numeric(pROC::auc(pROC::roc(test_last_obs$bankruptcy, bank_no_macro_scores, quiet = TRUE)))
      } else {
        full_bank_auc
      }
      
      # Calculate improvement
      bank_improvement <- full_bank_auc - no_macro_bank_auc
      
      # Statistical test if applicable
      bank_p_value <- if (has_macro_bank && !is.null(cv_bank_no_macro_model)) {
        test_result <- pROC::roc.test(
          pROC::roc(test_last_obs$bankruptcy, bank_full_scores, quiet = TRUE),
          pROC::roc(test_last_obs$bankruptcy, bank_no_macro_scores, quiet = TRUE),
          method = "delong"
        )
        test_result$p.value
      } else {
        NA
      }
      
      bankruptcy_cv_results$auc_with_macro[fold_idx] <- full_bank_auc
      bankruptcy_cv_results$auc_no_macro[fold_idx] <- no_macro_bank_auc
      bankruptcy_cv_results$improvement[fold_idx] <- bank_improvement
      bankruptcy_cv_results$p_value[fold_idx] <- bank_p_value
      bankruptcy_cv_results$concordance[fold_idx] <- full_bank_concordance
      
      cat("Bankruptcy model fold", fold_idx, "- AUC with macro:", round(full_bank_auc, 4), 
          "- AUC without macro:", round(no_macro_bank_auc, 4), 
          "- Improvement:", round(bank_improvement, 4), 
          "- Concordance:", round(full_bank_concordance, 4), "\n")
      
    }, error = function(e) {
      cat("Error evaluating bankruptcy model:", e$message, "\n")
    })
  }
  
  # Evaluate acquisition model
  if (!is.null(cv_acq_model)) {
    cat("\nEvaluating acquisition model...\n")
    
    tryCatch({
      # Get full model predictions
      acq_full_scores <- predict(cv_acq_model, newdata = test_last_obs, type = "risk")
      
      # Get no-macro model predictions (if applicable)
      acq_no_macro_scores <- if (!is.null(cv_acq_no_macro_model)) {
        predict(cv_acq_no_macro_model, newdata = test_last_obs, type = "risk")
      } else {
        acq_full_scores
      }
      
      # Calculate AUC
      full_acq_auc <- as.numeric(pROC::auc(pROC::roc(test_last_obs$acquisition, acq_full_scores, quiet = TRUE)))
      
      # Get concordance (Harrell's C-index) for acquisition model
      if (!is.null(cv_acq_model)) {
        full_acq_concordance <- as.numeric(cv_acq_model$concordance["concordance"])
        cat("Acquisition model concordance (C-index):", round(full_acq_concordance, 4), "\n")
      } else {
        full_acq_concordance <- NA
      }
      
      # Calculate no-macro AUC if applicable
      no_macro_acq_auc <- if (has_macro_acq && !is.null(cv_acq_no_macro_model)) {
        as.numeric(pROC::auc(pROC::roc(test_last_obs$acquisition, acq_no_macro_scores, quiet = TRUE)))
      } else {
        full_acq_auc
      }
      
      # Calculate improvement
      acq_improvement <- full_acq_auc - no_macro_acq_auc
      
      # Statistical test if applicable
      acq_p_value <- if (has_macro_acq && !is.null(cv_acq_no_macro_model)) {
        test_result <- pROC::roc.test(
          pROC::roc(test_last_obs$acquisition, acq_full_scores, quiet = TRUE),
          pROC::roc(test_last_obs$acquisition, acq_no_macro_scores, quiet = TRUE),
          method = "delong"
        )
        test_result$p.value
      } else {
        NA
      }
      
      # Store results
      acquisition_cv_results$auc_with_macro[fold_idx] <- full_acq_auc
      acquisition_cv_results$auc_no_macro[fold_idx] <- no_macro_acq_auc
      acquisition_cv_results$improvement[fold_idx] <- acq_improvement
      acquisition_cv_results$p_value[fold_idx] <- acq_p_value
      acquisition_cv_results$concordance[fold_idx] <- full_acq_concordance
      
      cat("Acquisition model fold", fold_idx, "- AUC with macro:", round(full_acq_auc, 4), 
          "- AUC without macro:", round(no_macro_acq_auc, 4), 
          "- Improvement:", round(acq_improvement, 4),
          "- Concordance:", round(full_acq_concordance, 4), "\n")
      
    }, error = function(e) {
      cat("Error evaluating acquisition model:", e$message, "\n")
    })
  }
}

----- Processing Fold 1 of 5 -----
Training set: 4029 companies ( 51032 observations)
Testing set: 1009 companies ( 12559 observations)

Fitting bankruptcy models for fold 1 ...

Fitting acquisition models for fold 1 ...

Evaluating bankruptcy model...
Bankruptcy model concordance (C-index): 0.8794 
Bankruptcy model fold 1 - AUC with macro: 0.7567 - AUC without macro: 0.7662 - Improvement: -0.0096 - Concordance: 0.8794 

Evaluating acquisition model...
Acquisition model concordance (C-index): 0.6397 
Acquisition model fold 1 - AUC with macro: 0.5426 - AUC without macro: 0.5968 - Improvement: -0.0542 - Concordance: 0.6397 

----- Processing Fold 2 of 5 -----
Training set: 4030 companies ( 51042 observations)
Testing set: 1008 companies ( 12549 observations)

Fitting bankruptcy models for fold 2 ...

Fitting acquisition models for fold 2 ...

Evaluating bankruptcy model...
Bankruptcy model concordance (C-index): 0.8646 
Bankruptcy model fold 2 - AUC with macro: 0.8055 - AUC without macro: 0.7994 - Improvement: 0.0061 - Concordance: 0.8646 

Evaluating acquisition model...
Acquisition model concordance (C-index): 0.636 
Acquisition model fold 2 - AUC with macro: 0.5313 - AUC without macro: 0.5884 - Improvement: -0.0572 - Concordance: 0.636 

----- Processing Fold 3 of 5 -----
Training set: 4030 companies ( 50731 observations)
Testing set: 1008 companies ( 12860 observations)

Fitting bankruptcy models for fold 3 ...

Fitting acquisition models for fold 3 ...

Evaluating bankruptcy model...
Bankruptcy model concordance (C-index): 0.8528 
Bankruptcy model fold 3 - AUC with macro: 0.8726 - AUC without macro: 0.8667 - Improvement: 0.0059 - Concordance: 0.8528 

Evaluating acquisition model...
Acquisition model concordance (C-index): 0.6466 
Acquisition model fold 3 - AUC with macro: 0.5407 - AUC without macro: 0.5932 - Improvement: -0.0524 - Concordance: 0.6466 

----- Processing Fold 4 of 5 -----
Training set: 4031 companies ( 50498 observations)
Testing set: 1007 companies ( 13093 observations)

Fitting bankruptcy models for fold 4 ...

Fitting acquisition models for fold 4 ...

Evaluating bankruptcy model...
Bankruptcy model concordance (C-index): 0.8876 
Bankruptcy model fold 4 - AUC with macro: 0.7609 - AUC without macro: 0.7547 - Improvement: 0.0062 - Concordance: 0.8876 

Evaluating acquisition model...
Acquisition model concordance (C-index): 0.6497 
Acquisition model fold 4 - AUC with macro: 0.5033 - AUC without macro: 0.5447 - Improvement: -0.0414 - Concordance: 0.6497 

----- Processing Fold 5 of 5 -----
Training set: 4032 companies ( 51061 observations)
Testing set: 1006 companies ( 12530 observations)

Fitting bankruptcy models for fold 5 ...

Fitting acquisition models for fold 5 ...

Evaluating bankruptcy model...
Bankruptcy model concordance (C-index): 0.8471 
Bankruptcy model fold 5 - AUC with macro: 0.808 - AUC without macro: 0.8011 - Improvement: 0.0069 - Concordance: 0.8471 

Evaluating acquisition model...
Acquisition model concordance (C-index): 0.6431 
Acquisition model fold 5 - AUC with macro: 0.508 - AUC without macro: 0.5742 - Improvement: -0.0662 - Concordance: 0.6431 
#-------------------------------------------------------------
# 13.3: Summarize K-Fold CV Results
#-------------------------------------------------------------
cat("\n13.3: SUMMARIZE K-FOLD CROSS-VALIDATION RESULTS\n")

13.3: SUMMARIZE K-FOLD CROSS-VALIDATION RESULTS
cat("--------------------------------------\n")
--------------------------------------
# Calculate summary statistics for bankruptcy model
bankruptcy_summary <- data.frame(
  metric = c("AUC with macro", "AUC without macro", "Improvement", "Concordance"),
  mean = c(
    mean(bankruptcy_cv_results$auc_with_macro, na.rm = TRUE),
    mean(bankruptcy_cv_results$auc_no_macro, na.rm = TRUE),
    mean(bankruptcy_cv_results$improvement, na.rm = TRUE),
    mean(bankruptcy_cv_results$concordance, na.rm = TRUE)
  ),
  sd = c(
    sd(bankruptcy_cv_results$auc_with_macro, na.rm = TRUE),
    sd(bankruptcy_cv_results$auc_no_macro, na.rm = TRUE),
    sd(bankruptcy_cv_results$improvement, na.rm = TRUE),
    sd(bankruptcy_cv_results$concordance, na.rm = TRUE)
  ),
  min = c(
    min(bankruptcy_cv_results$auc_with_macro, na.rm = TRUE),
    min(bankruptcy_cv_results$auc_no_macro, na.rm = TRUE),
    min(bankruptcy_cv_results$improvement, na.rm = TRUE),
    min(bankruptcy_cv_results$concordance, na.rm = TRUE)
  ),
  max = c(
    max(bankruptcy_cv_results$auc_with_macro, na.rm = TRUE),
    max(bankruptcy_cv_results$auc_no_macro, na.rm = TRUE),
    max(bankruptcy_cv_results$improvement, na.rm = TRUE),
    max(bankruptcy_cv_results$concordance, na.rm = TRUE)
  )
)

# Calculate summary statistics for acquisition model
acquisition_summary <- data.frame(
  metric = c("AUC with macro", "AUC without macro", "Improvement", "Concordance"),
  mean = c(
    mean(acquisition_cv_results$auc_with_macro, na.rm = TRUE),
    mean(acquisition_cv_results$auc_no_macro, na.rm = TRUE),
    mean(acquisition_cv_results$improvement, na.rm = TRUE),
    mean(acquisition_cv_results$concordance, na.rm = TRUE)
  ),
  sd = c(
    sd(acquisition_cv_results$auc_with_macro, na.rm = TRUE),
    sd(acquisition_cv_results$auc_no_macro, na.rm = TRUE),
    sd(acquisition_cv_results$improvement, na.rm = TRUE),
    sd(acquisition_cv_results$concordance, na.rm = TRUE)
  ),
  min = c(
    min(acquisition_cv_results$auc_with_macro, na.rm = TRUE),
    min(acquisition_cv_results$auc_no_macro, na.rm = TRUE),
    min(acquisition_cv_results$improvement, na.rm = TRUE),
    min(acquisition_cv_results$concordance, na.rm = TRUE)
  ),
  max = c(
    max(acquisition_cv_results$auc_with_macro, na.rm = TRUE),
    max(acquisition_cv_results$auc_no_macro, na.rm = TRUE),
    max(acquisition_cv_results$improvement, na.rm = TRUE),
    max(acquisition_cv_results$concordance, na.rm = TRUE)
  )
)

# Print summary results
cat("\nBankruptcy Model Cross-Validation Summary:\n")

Bankruptcy Model Cross-Validation Summary:
print(bankruptcy_summary)

cat("\nAcquisition Model Cross-Validation Summary:\n")

Acquisition Model Cross-Validation Summary:
print(acquisition_summary)

# Count significant improvements for bankruptcy model
sig_bank_improvements <- sum(bankruptcy_cv_results$p_value < 0.05, na.rm = TRUE)
total_bank_tests <- sum(!is.na(bankruptcy_cv_results$p_value))
cat("\nSignificant macro variable improvements for bankruptcy model:", 
    sig_bank_improvements, "out of", total_bank_tests, "folds\n")

Significant macro variable improvements for bankruptcy model: 0 out of 5 folds
# Count significant improvements for acquisition model
sig_acq_improvements <- sum(acquisition_cv_results$p_value < 0.05, na.rm = TRUE)
total_acq_tests <- sum(!is.na(acquisition_cv_results$p_value))
cat("Significant macro variable improvements for acquisition model:", 
    sig_acq_improvements, "out of", total_acq_tests, "folds\n")
Significant macro variable improvements for acquisition model: 5 out of 5 folds
# Overall significance test (one-sample t-test on improvements)
if (has_macro_bank) {
  bank_t_test <- t.test(bankruptcy_cv_results$improvement)
  cat("\nOverall significance test for bankruptcy model macro improvements:\n")
  cat("Mean improvement:", round(bank_t_test$estimate, 4), "\n")
  cat("95% CI:", paste(round(bank_t_test$conf.int, 4), collapse=" to "), "\n")
  cat("p-value:", format.pval(bank_t_test$p.value, digits = 3), "\n")
  
  if (bank_t_test$p.value < 0.05) {
    cat("Conclusion: Macroeconomic variables significantly improve bankruptcy prediction (p < 0.05)\n")
  } else {
    cat("Conclusion: Macro improvements for bankruptcy prediction are not statistically significant\n")
  }
}

Overall significance test for bankruptcy model macro improvements:
Mean improvement: 0.0031 
95% CI: -0.0057 to 0.0119 
p-value: 0.385 
Conclusion: Macro improvements for bankruptcy prediction are not statistically significant
if (has_macro_acq) {
  acq_t_test <- t.test(acquisition_cv_results$improvement)
  cat("\nOverall significance test for acquisition model macro improvements:\n")
  cat("Mean improvement:", round(acq_t_test$estimate, 4), "\n")
  cat("95% CI:", paste(round(acq_t_test$conf.int, 4), collapse=" to "), "\n")
  cat("p-value:", format.pval(acq_t_test$p.value, digits = 3), "\n")
  
  if (acq_t_test$p.value < 0.05) {
    cat("Conclusion: Macroeconomic variables significantly improve acquisition prediction (p < 0.05)\n")
  } else {
    cat("Conclusion: Macro improvements for acquisition prediction are not statistically significant\n")
  }
}

Overall significance test for acquisition model macro improvements:
Mean improvement: -0.0543 
95% CI: -0.0653 to -0.0432 
p-value: 0.000169 
Conclusion: Macroeconomic variables significantly improve acquisition prediction (p < 0.05)
# Create plots to visualize results
if (requireNamespace("ggplot2", quietly = TRUE)) {
  # Prepare data for plotting
  bank_plot_data <- bankruptcy_cv_results %>%
    pivot_longer(cols = c(auc_with_macro, auc_no_macro),
                 names_to = "model_type",
                 values_to = "auc") %>%
    mutate(model_type = factor(model_type, 
                              levels = c("auc_with_macro", "auc_no_macro"),
                              labels = c("With Macro", "Without Macro")),
           fold = as.factor(fold))
  
  acq_plot_data <- acquisition_cv_results %>%
    pivot_longer(cols = c(auc_with_macro, auc_no_macro),
                 names_to = "model_type",
                 values_to = "auc") %>%
    mutate(model_type = factor(model_type, 
                              levels = c("auc_with_macro", "auc_no_macro"),
                              labels = c("With Macro", "Without Macro")),
           fold = as.factor(fold))
  
  # Create bankruptcy model plot
  bank_plot <- ggplot(bank_plot_data, aes(x = fold, y = auc, fill = model_type)) +
    geom_bar(stat = "identity", position = "dodge") +
    geom_hline(yintercept = mean(bankruptcy_cv_results$auc_with_macro, na.rm = TRUE), 
               linetype = "dashed", color = "darkred") +
    geom_hline(yintercept = mean(bankruptcy_cv_results$auc_no_macro, na.rm = TRUE), 
               linetype = "dashed", color = "darkblue") +
    labs(title = "Bankruptcy Model Performance Across Folds",
         subtitle = paste("Average AUC With Macro:", 
                         round(mean(bankruptcy_cv_results$auc_with_macro, na.rm = TRUE), 4)),
         x = "Fold",
         y = "AUC",
         fill = "Model Type") +
    theme_minimal() +
    scale_fill_manual(values = c("With Macro" = "darkred", "Without Macro" = "darkblue"))
  
  # Create acquisition model plot
  acq_plot <- ggplot(acq_plot_data, aes(x = fold, y = auc, fill = model_type)) +
    geom_bar(stat = "identity", position = "dodge") +
    geom_hline(yintercept = mean(acquisition_cv_results$auc_with_macro, na.rm = TRUE), 
               linetype = "dashed", color = "darkgreen") +
    geom_hline(yintercept = mean(acquisition_cv_results$auc_no_macro, na.rm = TRUE), 
               linetype = "dashed", color = "darkorange") +
    labs(title = "Acquisition Model Performance Across Folds",
         subtitle = paste("Average AUC With Macro:", 
                         round(mean(acquisition_cv_results$auc_with_macro, na.rm = TRUE), 4)),
         x = "Fold",
         y = "AUC",
         fill = "Model Type") +
    theme_minimal() +
    scale_fill_manual(values = c("With Macro" = "darkgreen", "Without Macro" = "darkorange"))
  
  print(bank_plot)
  print(acq_plot)
  
  # Save plots
  ggsave("bankruptcy_cv_results.png", bank_plot, width = 10, height = 6)
  ggsave("acquisition_cv_results.png", acq_plot, width = 10, height = 6)
}

Next, we use a different approach where we split the dataset based on the IPO date of the companies. This method allows us to analyze how the model performs over time, which can be particularly useful in survival analysis. We will create a training set with companies that went public before a certain date and a testing set with companies that went public after that date. This approach helps us understand how well our model generalizes to new companies entering the market.

#-------------------------------------------------------------
# STEP 14: Time-Based Split for Survival Analysis
#-------------------------------------------------------------
cat("\n=== STEP 14: TIME-BASED SPLIT FOR SURVIVAL ANALYSIS ===\n")

=== STEP 14: TIME-BASED SPLIT FOR SURVIVAL ANALYSIS ===
#-------------------------------------------------------------
# 14.1: IPO Date-Based Splitting
#-------------------------------------------------------------
cat("\n14.1: IPO DATE-BASED SPLITTING\n")

14.1: IPO DATE-BASED SPLITTING
cat("--------------------------------------\n")
--------------------------------------
# Function to get company IPO dates (using first appearance date as proxy)
get_company_ipo_dates <- function(data) {
  # For each company, use the company_start_date already calculated
  company_ipo_dates <- data %>%
    group_by(cusip) %>%
    summarize(
      ipo_date = min(company_start_date, na.rm = TRUE)
    ) %>%
    ungroup()
  
  return(company_ipo_dates)
}

# Get IPO dates
company_ipo_dates <- get_company_ipo_dates(data)

# Display range of IPO dates
cat("IPO date range in dataset:\n")
IPO date range in dataset:
date_range <- range(company_ipo_dates$ipo_date, na.rm = TRUE)
cat("Earliest IPO date:", as.character(date_range[1]), "\n")
Earliest IPO date: 1985-01-01 
cat("Latest IPO date:", as.character(date_range[2]), "\n")
Latest IPO date: 2024-01-01 
# Determine temporal distribution of IPOs
ipo_years <- as.numeric(format(company_ipo_dates$ipo_date, "%Y"))
ipo_year_counts <- table(ipo_years)
cat("\nIPO distribution by year:\n")

IPO distribution by year:
print(ipo_year_counts)
ipo_years
1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 
   7   54  150   56   38   63  150  203  265  231  270  431  343  269  342  268   44   35   28   93  109  120  122   26   33   65   64 
2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 
  74   91  127   72   54   71   86   68  115  265   58   47   31 
# Determine splitting approach
cat("\nChoosing temporal split approach...\n")

Choosing temporal split approach...
# Option 1: Split at specific year (e.g., 2010)
split_year <- 2010
early_companies <- company_ipo_dates$cusip[as.numeric(format(company_ipo_dates$ipo_date, "%Y")) < split_year]
late_companies <- company_ipo_dates$cusip[as.numeric(format(company_ipo_dates$ipo_date, "%Y")) >= split_year]

cat("Option 1: Split at year", split_year, "\n")
Option 1: Split at year 2010 
cat("Training set (before", split_year, "):", length(early_companies), "companies\n")
Training set (before 2010 ): 3750 companies
cat("Testing set (", split_year, "and after):", length(late_companies), "companies\n")
Testing set ( 2010 and after): 1288 companies
# Option 2: Split by percentile (e.g., first 70% of IPOs chronologically)
ipo_sorted <- company_ipo_dates[order(company_ipo_dates$ipo_date), ]
train_size_pct <- 0.7
train_cutoff_idx <- round(nrow(ipo_sorted) * train_size_pct)
train_cutoff_date <- ipo_sorted$ipo_date[train_cutoff_idx]

chronological_early <- ipo_sorted$cusip[1:train_cutoff_idx]
chronological_late <- ipo_sorted$cusip[(train_cutoff_idx+1):nrow(ipo_sorted)]

cat("\nOption 2: Split at", train_size_pct*100, "percentile of IPO dates\n")

Option 2: Split at 70 percentile of IPO dates
cat("Training set (IPO before", as.character(train_cutoff_date), "):", 
    length(chronological_early), "companies\n")
Training set (IPO before 2006-01-01 ): 3527 companies
cat("Testing set (IPO on or after", as.character(train_cutoff_date), "):", 
    length(chronological_late), "companies\n")
Testing set (IPO on or after 2006-01-01 ): 1511 companies
# Choose the splitting approach (could prompt user here)
# For now, let's use Option 1 (specific year)
train_companies <- early_companies
test_companies <- late_companies
split_method <- paste("IPO year <", split_year, "vs >=", split_year)

cat("\nUsing Option 1: Split at specific year (", split_year, ")\n")

Using Option 1: Split at specific year ( 2010 )
# Create the time-split datasets
time_train_data <- data[data$cusip %in% train_companies, ]
time_test_data <- data[data$cusip %in% test_companies, ]

cat("Training set:", length(train_companies), "companies (", nrow(time_train_data), "observations)\n")
Training set: 3750 companies ( 55794 observations)
cat("Testing set:", length(test_companies), "companies (", nrow(time_test_data), "observations)\n")
Testing set: 1288 companies ( 7797 observations)
# Check event distribution in training and testing sets
analyze_time_events <- function(data_subset, label) {
  # Get final observation for each company
  final_obs <- data_subset %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Count event types
  n_bankruptcy <- sum(final_obs$bankruptcy)
  n_acquisition <- sum(final_obs$acquisition)
  n_censored <- sum(final_obs$bankruptcy == 0 & final_obs$acquisition == 0)
  n_total <- nrow(final_obs)
  
  cat("\nEvent distribution in", label, "set:\n")
  cat("- Bankruptcies:", n_bankruptcy, sprintf("(%.1f%%)", 100*n_bankruptcy/n_total), "\n")
  cat("- Acquisitions:", n_acquisition, sprintf("(%.1f%%)", 100*n_acquisition/n_total), "\n")
  cat("- Censored:", n_censored, sprintf("(%.1f%%)", 100*n_censored/n_total), "\n")
  
  return(data.frame(
    bankruptcies = n_bankruptcy,
    acquisitions = n_acquisition,
    censored = n_censored,
    total = n_total
  ))
}

time_train_events <- analyze_time_events(time_train_data, "training (early IPOs)")

Event distribution in training (early IPOs) set:
- Bankruptcies: 155 (4.1%) 
- Acquisitions: 1842 (49.1%) 
- Censored: 1753 (46.7%) 
time_test_events <- analyze_time_events(time_test_data, "testing (late IPOs)")

Event distribution in testing (late IPOs) set:
- Bankruptcies: 21 (1.6%) 
- Acquisitions: 312 (24.2%) 
- Censored: 955 (74.1%) 
# Check distribution of macroeconomic variables across sets
check_macro_time_distribution <- function(train_data, test_data) {
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  available_macro <- intersect(macro_vars, colnames(train_data))
  
  if(length(available_macro) > 0) {
    cat("\nDistribution of macroeconomic variables across time-based sets:\n")
    cat("--------------------------------------------------------\n")
    
    for(var in available_macro) {
      train_mean <- mean(train_data[[var]], na.rm = TRUE)
      train_sd <- sd(train_data[[var]], na.rm = TRUE)
      train_min <- min(train_data[[var]], na.rm = TRUE)
      train_max <- max(train_data[[var]], na.rm = TRUE)
      
      test_mean <- mean(test_data[[var]], na.rm = TRUE)
      test_sd <- sd(test_data[[var]], na.rm = TRUE)
      test_min <- min(test_data[[var]], na.rm = TRUE)
      test_max <- max(test_data[[var]], na.rm = TRUE)
      
      cat("\nVariable:", var, "\n")
      cat("Early IPO set: Mean =", round(train_mean, 2), "SD =", round(train_sd, 2), 
          "Range =", round(train_min, 2), "to", round(train_max, 2), "\n")
      cat("Late IPO set:  Mean =", round(test_mean, 2), "SD =", round(test_sd, 2), 
          "Range =", round(test_min, 2), "to", round(test_max, 2), "\n")
      
      # Calculate the difference in means as percentage of training SD
      mean_diff <- abs(train_mean - test_mean)
      pct_diff <- mean_diff / train_sd * 100
      
      cat("Difference:   ", round(mean_diff, 2), "(", round(pct_diff, 1), 
          "% of early IPO SD)\n")
      
      if(pct_diff > 25) {
        cat("WARNING: Substantial difference in distribution of", var, "between time periods\n")
        cat("         This may reflect genuine economic changes between the periods\n")
      }
    }
  } else {
    cat("\nNo macroeconomic variables found in the dataset.\n")
  }
}

check_macro_time_distribution(time_train_data, time_test_data)

Distribution of macroeconomic variables across time-based sets:
--------------------------------------------------------

Variable: gdp_growth 
Early IPO set: Mean = 2.69 SD = 1.67 Range = -2.58 to 6.06 
Late IPO set:  Mean = 2.58 SD = 1.87 Range = -2.16 to 6.06 
Difference:    0.11 ( 6.7 % of early IPO SD)

Variable: gdp_deflator 
Early IPO set: Mean = 2.15 SD = 1.03 Range = 0.62 to 7.13 
Late IPO set:  Mean = 2.99 SD = 1.9 Range = 0.93 to 7.13 
Difference:    0.84 ( 81.5 % of early IPO SD)
WARNING: Substantial difference in distribution of gdp_deflator between time periods
         This may reflect genuine economic changes between the periods

Variable: unemployement 
Early IPO set: Mean = 5.65 SD = 1.58 Range = 3.64 to 9.63 
Late IPO set:  Mean = 4.98 SD = 1.62 Range = 3.64 to 9.63 
Difference:    0.66 ( 41.8 % of early IPO SD)
WARNING: Substantial difference in distribution of unemployement between time periods
         This may reflect genuine economic changes between the periods
# Ensure the time variable exists in both sets
if(!"tt" %in% colnames(time_train_data)) time_train_data$tt <- time_train_data$tstop
if(!"tt" %in% colnames(time_test_data)) time_test_data$tt <- time_test_data$tstop
#-------------------------------------------------------------
# 14.2: TIME-BASED SPLIT MODEL TRAINING
#-------------------------------------------------------------
cat("\n14.2: TIME-BASED SPLIT MODEL TRAINING\n")

14.2: TIME-BASED SPLIT MODEL TRAINING
cat("--------------------------------------\n")
--------------------------------------
# Get formula from the final models
bank_formula <- formula(final_bankruptcy_model)
acq_formula <- formula(final_acquisition_model)

# Create versions without macro variables
no_macro_bank_formula <- create_no_macro_formula(bank_formula)
no_macro_acq_formula <- create_no_macro_formula(acq_formula)

# Check if formulas are different
has_macro_bank <- !identical(bank_formula, no_macro_bank_formula)
has_macro_acq <- !identical(acq_formula, no_macro_acq_formula)

# For bankruptcy model
cat("Training bankruptcy models on early IPO companies...\n")
Training bankruptcy models on early IPO companies...
# Fit full model with macro variables
time_bank_model <- tryCatch({
  coxph(bank_formula, data = time_train_data, ties = "efron")
}, error = function(e) {
  cat("Error fitting full bankruptcy model on early IPO data:", e$message, "\n")
  cat("Attempting simplified model without time interactions...\n")
  
  # Try without time interactions
  simple_bank_formula <- update(bank_formula, . ~ . - .:.)
  
  tryCatch({
    coxph(simple_bank_formula, data = time_train_data, ties = "efron")
  }, error = function(e2) {
    cat("Error fitting simplified model:", e2$message, "\n")
    return(NULL)
  })
})

# Fit version without macro variables (if applicable)
if (has_macro_bank) {
  time_bank_no_macro_model <- tryCatch({
    coxph(no_macro_bank_formula, data = time_train_data, ties = "efron")
  }, error = function(e) {
    cat("Error fitting no-macro bankruptcy model:", e$message, "\n")
    
    # Try without time interactions
    simple_no_macro_formula <- update(no_macro_bank_formula, . ~ . - .:.)
    
    tryCatch({
      coxph(simple_no_macro_formula, data = time_train_data, ties = "efron")
    }, error = function(e2) {
      cat("Error fitting simplified no-macro model:", e2$message, "\n")
      return(NULL)
    })
  })
} else {
  time_bank_no_macro_model <- time_bank_model
  cat("Bankruptcy model does not contain macro variables.\n")
}

# For acquisition model
cat("\nTraining acquisition models on early IPO companies...\n")

Training acquisition models on early IPO companies...
# Fit full model with macro variables
time_acq_model <- tryCatch({
  coxph(acq_formula, data = time_train_data, ties = "efron")
}, error = function(e) {
  cat("Error fitting full acquisition model on early IPO data:", e$message, "\n")
  cat("Attempting simplified model without time interactions...\n")
  
  # Try without time interactions
  simple_acq_formula <- update(acq_formula, . ~ . - .:.)
  
  tryCatch({
    coxph(simple_acq_formula, data = time_train_data, ties = "efron")
  }, error = function(e2) {
    cat("Error fitting simplified model:", e2$message, "\n")
    return(NULL)
  })
})

# Fit version without macro variables (if applicable)
if (has_macro_acq) {
  time_acq_no_macro_model <- tryCatch({
    coxph(no_macro_acq_formula, data = time_train_data, ties = "efron")
  }, error = function(e) {
    cat("Error fitting no-macro acquisition model:", e$message, "\n")
    
    # Try without time interactions
    simple_no_macro_formula <- update(no_macro_acq_formula, . ~ . - .:.)
    
    tryCatch({
      coxph(simple_no_macro_formula, data = time_train_data, ties = "efron")
    }, error = function(e2) {
      cat("Error fitting simplified no-macro model:", e2$message, "\n")
      return(NULL)
    })
  })
} else {
  time_acq_no_macro_model <- time_acq_model
  cat("Acquisition model does not contain macro variables.\n")
}

# Check if models were successfully fit
cat("\nModels successfully trained on early IPO data:\n")

Models successfully trained on early IPO data:
if (!is.null(time_bank_model)) {
  cat("- Bankruptcy model (full): Yes\n")
  cat("  Number of coefficients:", length(coef(time_bank_model)), "\n")
} else {
  cat("- Bankruptcy model (full): FAILED\n")
}
- Bankruptcy model (full): Yes
  Number of coefficients: 23 
if (has_macro_bank && !is.null(time_bank_no_macro_model)) {
  cat("- Bankruptcy model (no macro): Yes\n")
  cat("  Number of coefficients:", length(coef(time_bank_no_macro_model)), "\n")
} else if (has_macro_bank) {
  cat("- Bankruptcy model (no macro): FAILED\n")
}
- Bankruptcy model (no macro): Yes
  Number of coefficients: 20 
if (!is.null(time_acq_model)) {
  cat("- Acquisition model (full): Yes\n")
  cat("  Number of coefficients:", length(coef(time_acq_model)), "\n")
} else {
  cat("- Acquisition model (full): FAILED\n")
}
- Acquisition model (full): Yes
  Number of coefficients: 18 
if (has_macro_acq && !is.null(time_acq_no_macro_model)) {
  cat("- Acquisition model (no macro): Yes\n")
  cat("  Number of coefficients:", length(coef(time_acq_no_macro_model)), "\n")
} else if (has_macro_acq) {
  cat("- Acquisition model (no macro): FAILED\n")
}
- Acquisition model (no macro): Yes
  Number of coefficients: 14 
#-------------------------------------------------------------
# 14.3: Time-Based Split Model Evaluation
#-------------------------------------------------------------
cat("\n14.3: TIME-BASED SPLIT MODEL EVALUATION\n")

14.3: TIME-BASED SPLIT MODEL EVALUATION
cat("--------------------------------------\n")
--------------------------------------
# Function to evaluate models on the late IPO test set
evaluate_time_split_performance <- function(full_model, no_macro_model, test_data, event_type, model_name) {
  cat("\nEvaluating", model_name, "model on late IPO companies...\n")
  
  # Skip if model is NULL
  if (is.null(full_model)) {
    cat("Full model is NULL, skipping evaluation.\n")
    return(NULL)
  }
  
  # Get last observation for each company in test set
  test_last_obs <- test_data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate risk scores from full model
  tryCatch({
    # Calculate risk scores from full model
    full_risk_scores <- predict(full_model, newdata = test_last_obs, type = "risk")
    
    # Get concordance (Harrell's C-index)
    concordance <- as.numeric(full_model$concordance["concordance"])
    cat("Concordance (C-index):", round(concordance, 4), "\n")
    
    # Calculate risk scores from model without macro variables (if applicable)
    if (!is.null(no_macro_model) && !identical(full_model, no_macro_model)) {
      no_macro_risk_scores <- predict(no_macro_model, newdata = test_last_obs, type = "risk")
      has_both_models <- TRUE
    } else {
      no_macro_risk_scores <- full_risk_scores
      has_both_models <- FALSE
    }
    
    # Calculate AUC using pROC package
    if(requireNamespace("pROC", quietly = TRUE)) {
      # Create ROC objects
      full_roc <- pROC::roc(test_last_obs[[event_type]], full_risk_scores, quiet = TRUE)
      if (has_both_models) {
        no_macro_roc <- pROC::roc(test_last_obs[[event_type]], no_macro_risk_scores, quiet = TRUE)
      }
      
      # Calculate AUCs
      full_auc <- pROC::auc(full_roc)
      if (has_both_models) {
        no_macro_auc <- pROC::auc(no_macro_roc)
      } else {
        no_macro_auc <- full_auc
      }
      
      # Calculate confidence intervals
      full_ci <- pROC::ci(full_roc)
      if (has_both_models) {
        no_macro_ci <- pROC::ci(no_macro_roc)
      } else {
        no_macro_ci <- full_ci
      }
      
      # Print results
      cat("Out-of-time AUC with all variables:", round(full_auc, 4), "\n")
      cat("95% Confidence Interval:", paste(round(full_ci[1:2], 4), collapse=" - "), "\n")
      
      if(has_both_models) {
        cat("Out-of-time AUC without macro variables:", round(no_macro_auc, 4), "\n")
        cat("95% Confidence Interval:", paste(round(no_macro_ci[1:2], 4), collapse=" - "), "\n")
        
        # Calculate improvement from macro variables
        auc_diff <- full_auc - no_macro_auc
        pct_improvement <- auc_diff / no_macro_auc * 100
        
        cat("AUC improvement from macro variables:", round(auc_diff, 4), 
            "(", round(pct_improvement, 1), "%)\n")
        
        # Test if improvement is significant
        roc_test <- pROC::roc.test(full_roc, no_macro_roc, method = "delong")
        cat("Statistical significance: p =", format.pval(roc_test$p.value, digits = 3), "\n")
        
        if(roc_test$p.value < 0.05) {
          cat("The improvement from macroeconomic variables is STATISTICALLY SIGNIFICANT (p < 0.05)\n")
        } else {
          cat("The improvement from macroeconomic variables is not statistically significant (p >= 0.05)\n")
        }
      }
      
      # Calculate performance metrics at optimal threshold
      coords <- pROC::coords(full_roc, "best", ret = c("threshold", "specificity", "sensitivity"))
      threshold <- as.numeric(coords["threshold"])
      specificity <- as.numeric(coords["specificity"])
      sensitivity <- as.numeric(coords["sensitivity"])
      
      predictions <- ifelse(full_risk_scores >= threshold, 1, 0)
      actual <- test_last_obs[[event_type]]
      
      TP <- sum(predictions == 1 & actual == 1)
      TN <- sum(predictions == 0 & actual == 0)
      FP <- sum(predictions == 1 & actual == 0)
      FN <- sum(predictions == 0 & actual == 1)
      
      accuracy <- (TP + TN) / (TP + TN + FP + FN)
      precision <- if(TP + FP > 0) TP / (TP + FP) else 0
      recall <- sensitivity
      f1_score <- if(precision + recall > 0) 2 * precision * recall / (precision + recall) else 0
      
      cat("\nClassification metrics at optimal threshold:\n")
      cat("Threshold:", round(threshold, 4), "\n")
      cat("Accuracy:", round(accuracy, 4), "\n")
      cat("Precision:", round(precision, 4), "\n")
      cat("Recall:", round(recall, 4), "\n")
      cat("F1 Score:", round(f1_score, 4), "\n")
      
      # Return results
      return(list(
        full_auc = as.numeric(full_auc),
        no_macro_auc = as.numeric(no_macro_auc),
        auc_diff = as.numeric(full_auc - no_macro_auc),
        p_value = if(has_both_models) roc_test$p.value else NA,
        threshold = threshold,
        accuracy = accuracy,
        precision = precision,
        recall = recall,
        f1_score = f1_score,
        concordance = concordance
      ))
    } else {
      cat("pROC package not available. Please install it for ROC analysis.\n")
      return(NULL)
    }
  }, error = function(e) {
    cat("Error evaluating model:", e$message, "\n")
    return(NULL)
  })
}

# Evaluate bankruptcy model
bank_time_results <- evaluate_time_split_performance(
  time_bank_model, 
  time_bank_no_macro_model, 
  time_test_data, 
  "bankruptcy", 
  "bankruptcy"
)

Evaluating bankruptcy model on late IPO companies...
Concordance (C-index): 0.8679 
Out-of-time AUC with all variables: 0.8044 
95% Confidence Interval: 0.7081 - 0.8044 
Out-of-time AUC without macro variables: 0.7939 
95% Confidence Interval: 0.6952 - 0.7939 
AUC improvement from macro variables: 0.0105 ( 1.3 %)
Statistical significance: p = 0.337 
The improvement from macroeconomic variables is not statistically significant (p >= 0.05)

Classification metrics at optimal threshold:
Threshold: 5.0779 
Accuracy: 0.8463 
Precision: 0.0683 
Recall: 0.6667 
F1 Score: 0.1239 
# Evaluate acquisition model
acq_time_results <- evaluate_time_split_performance(
  time_acq_model, 
  time_acq_no_macro_model, 
  time_test_data, 
  "acquisition", 
  "acquisition"
)

Evaluating acquisition model on late IPO companies...
Concordance (C-index): 0.6353 
Out-of-time AUC with all variables: 0.5171 
95% Confidence Interval: 0.4818 - 0.5171 
Out-of-time AUC without macro variables: 0.585 
95% Confidence Interval: 0.5504 - 0.585 
AUC improvement from macro variables: -0.0679 ( -11.6 %)
Statistical significance: p = 1.2e-12 
The improvement from macroeconomic variables is STATISTICALLY SIGNIFICANT (p < 0.05)

Classification metrics at optimal threshold:
Threshold: 0.492 
Accuracy: 0.3354 
Precision: 0.2589 
Recall: 0.9359 
F1 Score: 0.4056 

We see that the models are performing well on the test set, with AUC values indicating good discrimination ability. The macroeconomic variables also seem to provide a significant improvement in model performance, as indicated by the p-values from the statistical tests.

Finally, we can visualize the hazard ratios for the models, highlighting the variables to interpret their impact on bankruptcy and acquisition predictions.

#-------------------------------------------------------------
# STEP 13: Enhanced Visualization and Interpretation with Macroeconomic Focus
#-------------------------------------------------------------
cat("\n=== STEP 13: ENHANCED VISUALIZATION AND INTERPRETATION WITH MACROECONOMIC FOCUS ===\n")

=== STEP 13: ENHANCED VISUALIZATION AND INTERPRETATION WITH MACROECONOMIC FOCUS ===
#-------------------------------------------------------------
# 13.1: Enhanced Hazard Ratio Visualization with Macro Highlighting
#-------------------------------------------------------------
cat("\n13.1: ENHANCED HAZARD RATIO VISUALIZATION WITH MACRO HIGHLIGHTING\n")

13.1: ENHANCED HAZARD RATIO VISUALIZATION WITH MACRO HIGHLIGHTING
cat("--------------------------------------\n")
--------------------------------------
# Function to create hazard ratio forest plots with macro variable highlighting
create_hazard_ratio_plot <- function(model, model_name, top_n = 10, highlight_macro = TRUE) {
  cat("Creating hazard ratio plot for", model_name, "model...\n")
  
  # Extract coefficients and confidence intervals
  coefs <- summary(model)$coefficients
  conf_int <- confint(model)
  
  # Create data frame for plotting
  hr_data <- data.frame(
    Variable = rownames(coefs),
    HR = exp(coefs[, "coef"]),
    Lower = exp(conf_int[, 1]),
    Upper = exp(conf_int[, 2]),
    Pvalue = coefs[, "Pr(>|z|)"],
    stringsAsFactors = FALSE
  )
  
  # Remove time interactions for this plot
  hr_data <- hr_data[!grepl(":tt", hr_data$Variable), ]
  
  # Identify macro variables
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  hr_data$is_macro <- sapply(hr_data$Variable, function(x) 
    any(sapply(macro_vars, function(m) grepl(m, x))))
  
  # Sort by statistical significance
  hr_data <- hr_data[order(hr_data$Pvalue), ]
  
  # If highlighting macro, ensure they're included in top variables
  if(highlight_macro) {
    macro_rows <- hr_data[hr_data$is_macro, ]
    non_macro_rows <- hr_data[!hr_data$is_macro, ]
    
    # Take top non-macro variables
    top_non_macro <- min(nrow(non_macro_rows), max(5, top_n - nrow(macro_rows)))
    non_macro_rows <- non_macro_rows[1:top_non_macro, ]
    
    # Combine and re-sort by p-value
    hr_data <- rbind(non_macro_rows, macro_rows)
    hr_data <- hr_data[order(hr_data$Pvalue), ]
  } else {
    # Take top N most significant variables
    if(nrow(hr_data) > top_n) {
      hr_data <- hr_data[1:top_n, ]
    }
  }
  
  # Reverse order for plotting (bottom to top)
  hr_data <- hr_data[nrow(hr_data):1, ]
  
  # Create significance symbols
  hr_data$sig <- ""
  hr_data$sig[hr_data$Pvalue < 0.001] <- "***"
  hr_data$sig[hr_data$Pvalue >= 0.001 & hr_data$Pvalue < 0.01] <- "**"
  hr_data$sig[hr_data$Pvalue >= 0.01 & hr_data$Pvalue < 0.05] <- "*"
  hr_data$sig[hr_data$Pvalue >= 0.05 & hr_data$Pvalue < 0.1] <- "."
  
  # Set up the plotting area
  par(mar = c(5, 10, 4, 2) + 0.1)  # Adjust margins for variable names
  
  # Create empty plot with appropriate limits
  max_upper <- max(hr_data$Upper)
  if(max_upper > 10) max_upper <- 10  # Cap for extreme values
  plot_max <- max(3, max_upper)
  
  plot(NULL, xlim = c(0, plot_max), ylim = c(0.5, nrow(hr_data) + 0.5),
       xlab = "Hazard Ratio (log scale)", ylab = "", yaxt = "n",
       main = paste("Hazard Ratios for", model_name, "Model"))
  
  # Add reference line at HR = 1
  abline(v = 1, lty = 2, col = "darkgray")
  
  # Add variable names with different colors for macro variables
  var_labels <- hr_data$Variable
  
  # Add variable names - need to handle colors separately since col.axis must be a single color
  axis(2, at = 1:nrow(hr_data), labels = paste0(var_labels, " ", hr_data$sig), 
       las = 2, cex.axis = 0.8)
  
  # Highlight macro variables with colored rectangles if any exist
  if(any(hr_data$is_macro)) {
    for(i in which(hr_data$is_macro)) {
      rect(par("usr")[1], i - 0.4, par("usr")[1] + 0.1, i + 0.4, 
           col = "lightblue", border = NA, xpd = TRUE)
    }
  }
  
  # Plot confidence intervals
  segments(hr_data$Lower, 1:nrow(hr_data), hr_data$Upper, 1:nrow(hr_data),
           lwd = 2, col = ifelse(hr_data$is_macro, "navy", "darkblue"))
  
  # Plot point estimates with different colors for macro vs financial variables
  point_colors <- ifelse(hr_data$is_macro, 
                      ifelse(hr_data$HR > 1, "darkred", "darkgreen"),
                      ifelse(hr_data$HR > 1, "red", "green4"))
  
  points(hr_data$HR, 1:nrow(hr_data), pch = 16, cex = 1.2, 
         col = point_colors)
  
  # Add numeric values
  text(hr_data$HR + 0.1, 1:nrow(hr_data), 
       labels = sprintf("%.2f", hr_data$HR), cex = 0.7)
  
  # Add significance legend
  legend("topright", 
         legend = c("p < 0.001 (***)", "p < 0.01 (**)", "p < 0.05 (*)", "p < 0.1 (.)"),
         bty = "n", cex = 0.8)
  
  # Add color legend for macro vs financial variables
  if(any(hr_data$is_macro)) {
    legend("bottomright",
           legend = c("Macroeconomic Variable", "Financial Variable"),
           fill = c("lightblue", "white"),
           border = "black",
           bty = "n", cex = 0.8)
  }
  
  # Add interpretation note
  mtext("HR > 1: Increased risk, HR < 1: Decreased risk", side = 3, line = 0.5, cex = 0.8)
  
  cat("Hazard ratio plot created for", model_name, "model.\n")
  
  # Return the hazard ratio data for further analysis
  return(hr_data)
}

# Create hazard ratio plots for both models
bankruptcy_hrs <- create_hazard_ratio_plot(final_bankruptcy_model, "Bankruptcy")
Creating hazard ratio plot for Bankruptcy model...
Hazard ratio plot created for Bankruptcy model.

acquisition_hrs <- create_hazard_ratio_plot(final_acquisition_model, "Acquisition")
Creating hazard ratio plot for Acquisition model...
Hazard ratio plot created for Acquisition model.

Now that we’re done with the model training and evaluation, we do an additional visualization for the macroeconomic impact on the models. This will help us understand how changes in macroeconomic variables affect the risk of bankruptcy and acquisition.

#-------------------------------------------------------------
# 13.2: Dedicated Macroeconomic Impact Visualization
#-------------------------------------------------------------
cat("\n13.2: MACROECONOMIC IMPACT VISUALIZATION\n")

13.2: MACROECONOMIC IMPACT VISUALIZATION
cat("--------------------------------------\n")
--------------------------------------
visualize_macro_impact <- function(model, model_name, event_type) {
  # Extract coefficients related to macro variables
  coefs <- coef(model)
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  
  # Find macro variables in the model
  model_macro_vars <- macro_vars[sapply(macro_vars, function(v) v %in% names(coefs))]
  
  if(length(model_macro_vars) == 0) {
    cat("No macroeconomic variables found in the", model_name, "model.\n")
    return(NULL)
  }
  
  cat("Visualizing macroeconomic impacts for", model_name, "model...\n")
  
  # Extract coefficients and confidence intervals for macro variables
  coef_summary <- summary(model)$coefficients
  conf_int <- confint(model)
  
  # Create data frame for plotting
  macro_data <- data.frame(
    Variable = model_macro_vars,
    Coefficient = coefs[model_macro_vars],
    HR = exp(coefs[model_macro_vars]),
    Lower = exp(conf_int[model_macro_vars, 1]),
    Upper = exp(conf_int[model_macro_vars, 2]),
    Pvalue = coef_summary[model_macro_vars, "Pr(>|z|)"],
    stringsAsFactors = FALSE
  )
  
  # Add interpretable variable names
  macro_data$Label <- sapply(macro_data$Variable, function(v) {
    if(v == "gdp_growth") return("GDP Growth")
    if(v == "gdp_deflator") return("Inflation (GDP Deflator)")
    if(v == "unemployement") return("Unemployment Rate")
    return(v)
  })
  
  # Sort by effect size (absolute coefficient)
  macro_data <- macro_data[order(abs(macro_data$Coefficient), decreasing = TRUE), ]
  
  # 1. Create bar plot of macro variable hazard ratios
  # Set up the plotting area
  par(mar = c(5, 10, 4, 2) + 0.1)
  
  # Create horizontal bar plot of hazard ratios
  barplot_result <- barplot(macro_data$HR - 1, names.arg = macro_data$Label,
                          main = paste("Impact of Macroeconomic Variables on", model_name, "Risk"),
                          xlab = "Change in Risk (%)", ylab = "",
                          horiz = TRUE, xlim = c(min(-0.5, min(macro_data$HR - 1) * 1.2), 
                                             max(0.5, max(macro_data$HR - 1) * 1.2)),
                          col = ifelse(macro_data$HR > 1, "firebrick", "forestgreen"),
                          las = 1)
  
  # Add vertical reference line at 0
  abline(v = 0, lty = 2, col = "black")
  
  # Add data labels
  text_pos <- ifelse(macro_data$HR > 1, 
                   macro_data$HR - 1 + 0.03, 
                   macro_data$HR - 1 - 0.03)
  
  text(text_pos, barplot_result, 
       labels = sprintf("%+.1f%%", (macro_data$HR - 1) * 100),
       cex = 0.9, col = ifelse(macro_data$HR > 1, "firebrick", "forestgreen"))
  
  # Add significance symbols
  significance <- rep("", nrow(macro_data))
  significance[macro_data$Pvalue < 0.001] <- "***"
  significance[macro_data$Pvalue >= 0.001 & macro_data$Pvalue < 0.01] <- "**"
  significance[macro_data$Pvalue >= 0.01 & macro_data$Pvalue < 0.05] <- "*"
  significance[macro_data$Pvalue >= 0.05 & macro_data$Pvalue < 0.1] <- "."
  
  text(ifelse(macro_data$HR > 1, max(0.1, max(macro_data$HR - 1) * 0.8), 
            min(-0.1, min(macro_data$HR - 1) * 0.8)), 
       barplot_result, 
       labels = significance,
       cex = 1.2)
  
  # Add interpretation note
  mtext("Effect of a 1-unit increase in each variable", side = 3, line = 0.5, cex = 0.8)
  
  # Add significance legend
  legend("bottomright", 
         legend = c("p < 0.001 (***)", "p < 0.01 (**)", "p < 0.05 (*)", "p < 0.1 (.)"),
         bty = "n", cex = 0.8)
  
  # 2. Create visual representation of how economic indicators affect risk
  # Only if we have at least two macro variables
  if(nrow(macro_data) >= 2) {
    # Calculate min and max values of each macro variable in the data
    var_ranges <- data.frame(
      Variable = model_macro_vars,
      Min = NA,
      Max = NA,
      Mean = NA,
      SD = NA
    )
    
    for(i in 1:nrow(var_ranges)) {
      var <- var_ranges$Variable[i]
      if(var %in% colnames(data)) {
        var_ranges$Min[i] <- min(data[[var]], na.rm = TRUE)
        var_ranges$Max[i] <- max(data[[var]], na.rm = TRUE)
        var_ranges$Mean[i] <- mean(data[[var]], na.rm = TRUE)
        var_ranges$SD[i] <- sd(data[[var]], na.rm = TRUE)
      }
    }
    
    # Set up a new plot
    par(mar = c(5, 10, 4, 2) + 0.1)
    
    # Create a risk matrix visualization
    plot(NULL, xlim = c(-1, 1), ylim = c(0, nrow(macro_data) + 1),
         xlab = "Economic Condition", ylab = "", yaxt = "n",
         main = paste("Economic Conditions and", model_name, "Risk"))
    
    # Add y-axis labels
    axis(2, at = 1:nrow(macro_data), labels = macro_data$Label, las = 1)
    
    # Add x-axis
    axis(1, at = seq(-1, 1, 0.5), 
         labels = c("Very Low", "Low", "Average", "High", "Very High"))
    
    # Add reference line at 0
    abline(v = 0, lty = 2, col = "darkgray")
    
    # Add arrows showing the direction of effect
    for(i in 1:nrow(macro_data)) {
      arrow_color <- ifelse(macro_data$HR[i] > 1, "red", "green4")
      arrow_length <- min(0.9, abs(log(macro_data$HR[i])))
      arrow_dir <- sign(log(macro_data$HR[i]))
      
      arrows(0, i, arrow_dir * arrow_length, i, col = arrow_color, 
             length = 0.15, lwd = 2 + abs(log(macro_data$HR[i])))
      
      # Add text showing increased/decreased risk
      if(macro_data$HR[i] > 1) {
        text(arrow_dir * arrow_length + 0.1, i, 
             paste0("+", round((macro_data$HR[i] - 1) * 100), "% risk"), 
             col = arrow_color, cex = 0.8)
      } else {
        text(arrow_dir * arrow_length - 0.1, i, 
             paste0("-", round((1 - macro_data$HR[i]) * 100), "% risk"), 
             col = arrow_color, cex = 0.8)
      }
    }
    
    # Add legend
    legend("topright",
           legend = c("Increases Risk", "Decreases Risk"),
           col = c("red", "green4"),
           lwd = 2, bty = "n")
    
    mtext("How Economic Indicators Affect Risk", side = 3, line = 0.5, cex = 0.8)
  }
  
  # 3. If we have time interactions, create a time-varying effects visualization
  time_terms <- names(coefs)[grepl(":tt", names(coefs))]
  macro_time_terms <- time_terms[sapply(time_terms, function(term) {
    var_name <- strsplit(term, ":")[[1]][1]
    return(var_name %in% macro_vars)
  })]
  
  if(length(macro_time_terms) > 0) {
    cat("\nVisualize time-varying effects of macroeconomic variables...\n")
    
    # For each macro variable with a time-varying effect, create a plot
    for(time_term in macro_time_terms) {
      var_name <- strsplit(time_term, ":")[[1]][1]
      
      # Get coefficients
      main_coef <- coefs[var_name]
      time_coef <- coefs[time_term]
      
      # Create readable variable name
      var_label <- if(var_name == "gdp_growth") "GDP Growth" else
                   if(var_name == "gdp_deflator") "Inflation (GDP Deflator)" else
                   if(var_name == "unemployement") "Unemployment Rate" else var_name
      
      # Set up time sequence
      max_time <- max(data$tstop)
      times <- seq(0, max_time, length.out = 100)
      
      # Calculate hazard ratios over time (for a 1-unit change in the variable)
      hrs <- exp(main_coef + time_coef * times)
      
      # Set up a new plot
      par(mar = c(5, 5, 4, 2) + 0.1)
      
      # Create plot
      plot(times, hrs, type = "l", lwd = 2, col = "blue",
           main = paste("Time-Varying Effect of", var_label, "on", model_name, "Risk"),
           xlab = "Time (Years)", ylab = "Hazard Ratio",
           ylim = c(min(0.5, min(hrs)), max(2, max(hrs))))
      
      # Add reference line at HR = 1
      abline(h = 1, lty = 2, col = "red")
      
      # Add grid for readability
      grid()
      
      # Calculate and mark crossover point (if any)
      if(sign(main_coef) != sign(time_coef) && time_coef != 0) {
        crossover <- -main_coef / time_coef
        if(crossover > 0 && crossover < max(times)) {
          points(crossover, 1, pch = 16, col = "red", cex = 1.5)
          text(crossover, 1.1, paste("Effect reverses at", round(crossover, 1), "years"),
               col = "red")
        }
      }
      
      # Add phase labels for economic interpretation
      if(main_coef > 0) {
        if(time_coef > 0) {
          # Both positive - increasing risk that gets worse over time
          early_label <- "Early: Increases Risk"
          late_label <- "Later: Risk Increase Strengthens"
          economic_msg <- "Economic sensitivity increases with company age"
        } else {
          # Main positive, time negative - increasing risk that diminishes
          early_label <- "Early: Increases Risk"
          late_label <- "Later: Risk Increase Weakens"
          economic_msg <- "Young companies more sensitive to this economic factor"
        }
      } else {
        if(time_coef > 0) {
          # Main negative, time positive - decreasing risk that diminishes
          early_label <- "Early: Decreases Risk"
          late_label <- "Later: Risk Reduction Weakens"
          economic_msg <- "Young companies more protected by this economic factor"
        } else {
          # Both negative - decreasing risk that gets stronger
          early_label <- "Early: Decreases Risk"
          late_label <- "Later: Risk Reduction Strengthens"
          economic_msg <- "Economic protection increases with company age"
        }
      }
      
      # Add the phase labels to the plot
      text(times[20], hrs[20] + 0.15, early_label, col = "darkgreen", cex = 0.9)
      text(times[80], hrs[80] + 0.15, late_label, col = "darkblue", cex = 0.9)
      
      # Add economic interpretation
      mtext(economic_msg, side = 3, line = 0.5, cex = 0.8)
    }
  }
  
  # Print a summary of macroeconomic effects
  cat("\nSummary of Macroeconomic Effects on", model_name, "Risk:\n")
  cat("---------------------------------------------------\n")
  
  for(i in 1:nrow(macro_data)) {
    var <- macro_data$Variable[i]
    label <- macro_data$Label[i]
    hr <- macro_data$HR[i]
    p_val <- macro_data$Pvalue[i]
    
    # Direction of effect
    direction <- ifelse(hr > 1, "increases", "decreases")
    
    # Magnitude in percentage
    if(hr > 1) {
      magnitude <- paste0("+", round((hr - 1) * 100, 1), "%")
    } else {
      magnitude <- paste0("-", round((1 - hr) * 100, 1), "%")
    }
    
    # Significance
    significance <- ""
    if(p_val < 0.001) significance <- " (p < 0.001) ***"
    else if(p_val < 0.01) significance <- paste0(" (p = ", round(p_val, 3), ") **")
    else if(p_val < 0.05) significance <- paste0(" (p = ", round(p_val, 3), ") *")
    else if(p_val < 0.1) significance <- paste0(" (p = ", round(p_val, 3), ") .")
    else significance <- paste0(" (p = ", round(p_val, 3), ")")
    
    # Check for time-varying effect
    has_time_effect <- paste0(var, ":tt") %in% names(coefs)
    time_effect <- if(has_time_effect) {
      time_coef <- coefs[paste0(var, ":tt")]
      if(time_coef > 0 && hr > 1) "effect strengthens over time"
      else if(time_coef < 0 && hr > 1) "effect weakens over time"
      else if(time_coef > 0 && hr < 1) "protection weakens over time"
      else "protection strengthens over time"
    } else ""
    
    # Economic interpretation
    interpretation <- ""
    if(var == "gdp_growth") {
      if(hr > 1) {
        interpretation <- "Higher economic growth corresponds to INCREASED risk"
      } else {
        interpretation <- "Higher economic growth corresponds to DECREASED risk"
      }
    } else if(var == "gdp_deflator") {
      if(hr > 1) {
        interpretation <- "Higher inflation corresponds to INCREASED risk"
      } else {
        interpretation <- "Higher inflation corresponds to DECREASED risk"
      }
    } else if(var == "unemployement") {
      if(hr > 1) {
        interpretation <- "Higher unemployment corresponds to INCREASED risk"
      } else {
        interpretation <- "Higher unemployment corresponds to DECREASED risk"
      }
    }
    
    # Print the summary
    cat(label, direction, model_name, "risk by", magnitude, significance, "\n")
    if(has_time_effect) cat("Time effect:", time_effect, "\n")
    cat("Economic interpretation:", interpretation, "\n\n")
  }
  
  # Return the macro data for further analysis
  return(macro_data)
}

# Visualize macroeconomic impacts for both models
bankruptcy_macro <- visualize_macro_impact(final_bankruptcy_model, "Bankruptcy", "bankruptcy")
Visualizing macroeconomic impacts for Bankruptcy model...


Summary of Macroeconomic Effects on Bankruptcy Risk:
---------------------------------------------------
Inflation (GDP Deflator) decreases Bankruptcy risk by -16.1%  (p = 0.051) . 
Economic interpretation: Higher inflation corresponds to DECREASED risk 

GDP Growth increases Bankruptcy risk by +10.4%  (p = 0.084) . 
Economic interpretation: Higher economic growth corresponds to INCREASED risk 

Unemployment Rate decreases Bankruptcy risk by -7.2%  (p = 0.278) 
Economic interpretation: Higher unemployment corresponds to DECREASED risk 

acquisition_macro <- visualize_macro_impact(final_acquisition_model, "Acquisition", "acquisition")
Visualizing macroeconomic impacts for Acquisition model...


Visualize time-varying effects of macroeconomic variables...


Summary of Macroeconomic Effects on Acquisition Risk:
---------------------------------------------------
Unemployment Rate decreases Acquisition risk by -12.5%  (p < 0.001) *** 
Time effect: protection weakens over time 
Economic interpretation: Higher unemployment corresponds to DECREASED risk 

Inflation (GDP Deflator) decreases Acquisition risk by -3.7%  (p = 0.085) . 
Economic interpretation: Higher inflation corresponds to DECREASED risk 

GDP Growth increases Acquisition risk by +3.8%  (p = 0.013) * 
Economic interpretation: Higher economic growth corresponds to INCREASED risk 

More visualizaitions of the macroeconomic impact on the models can be done by comparing the effects of macroeconomic variables on both bankruptcy and acquisition models. This will help us understand how these variables influence the risk of bankruptcy and acquisition in different ways.

#-------------------------------------------------------------
# 13.3: Comparative Macroeconomic Impact Analysis
#-------------------------------------------------------------
cat("\n13.3: COMPARATIVE MACROECONOMIC IMPACT ANALYSIS\n")

13.3: COMPARATIVE MACROECONOMIC IMPACT ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
compare_macro_effects <- function() {
  # Get common macro variables that exist in both models
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  
  # Extract coefficients from both models
  bank_coefs <- coef(final_bankruptcy_model)
  acq_coefs <- coef(final_acquisition_model)
  
  # Find common macro variables in both models
  bank_macro_vars <- macro_vars[sapply(macro_vars, function(v) v %in% names(bank_coefs))]
  acq_macro_vars <- macro_vars[sapply(macro_vars, function(v) v %in% names(acq_coefs))]
  common_macro_vars <- intersect(bank_macro_vars, acq_macro_vars)
  
  if(length(common_macro_vars) == 0) {
    cat("No common macroeconomic variables found in both models for comparison.\n")
    return(NULL)
  }
  
  cat("Comparing macroeconomic effects between bankruptcy and acquisition models...\n")
  
  # Create data frame for comparison
  comparison <- data.frame(
    Variable = common_macro_vars,
    Bank_Coef = bank_coefs[common_macro_vars],
    Bank_HR = exp(bank_coefs[common_macro_vars]),
    Acq_Coef = acq_coefs[common_macro_vars],
    Acq_HR = exp(acq_coefs[common_macro_vars]),
    stringsAsFactors = FALSE
  )
  
  # Add variable labels
  comparison$Label <- sapply(comparison$Variable, function(v) {
    if(v == "gdp_growth") return("GDP Growth")
    if(v == "gdp_deflator") return("Inflation (GDP Deflator)")
    if(v == "unemployement") return("Unemployment Rate")
    return(v)
  })
  
  # Add direction of effects
  comparison$Bank_Direction <- ifelse(comparison$Bank_HR > 1, 
                                   "Increases Risk", "Decreases Risk")
  comparison$Acq_Direction <- ifelse(comparison$Acq_HR > 1, 
                                  "Increases Risk", "Decreases Risk")
  
  # Check for opposite effects
  comparison$Opposite_Effects <- comparison$Bank_Direction != comparison$Acq_Direction
  
  # 1. Create side-by-side bar plot comparing effects
  # Set up the plotting area
  par(mar = c(5, 10, 4, 2) + 0.1)
  
  # Prepare data for plotting
  barplot_data <- cbind(
    (comparison$Bank_HR - 1) * 100,  # Convert to percentage change
    (comparison$Acq_HR - 1) * 100
  )
  
  # Find limits for x-axis
  max_val <- max(abs(barplot_data)) * 1.2
  xlim <- c(-max_val, max_val)
  
  # Create horizontal bar plot
  barplot_result <- barplot(t(barplot_data), beside = TRUE, horiz = TRUE,
                         names.arg = comparison$Label,
                         main = "Comparative Economic Impact on Company Outcomes",
                         xlab = "Change in Risk (%)", ylab = "",
                         col = c("firebrick", "royalblue"),
                         xlim = xlim,
                         las = 1)
  
  # Add vertical reference line at 0
  abline(v = 0, lty = 2, col = "black")
  
  # Add legend
  legend("topright", legend = c("Bankruptcy Risk", "Acquisition Risk"),
         fill = c("firebrick", "royalblue"), bty = "n")
  
  # Add data labels
  for(i in 1:nrow(comparison)) {
    # For bankruptcy
    bank_pos <- barplot_result[1, i]
    bank_val <- barplot_data[i, 1]
    text_pos_bank <- ifelse(bank_val > 0, bank_val + 2, bank_val - 2)
    text(text_pos_bank, bank_pos, 
         labels = sprintf("%+.1f%%", bank_val),
         cex = 0.8)
    
    # For acquisition
    acq_pos <- barplot_result[2, i]
    acq_val <- barplot_data[i, 2]
    text_pos_acq <- ifelse(acq_val > 0, acq_val + 2, acq_val - 2)
    text(text_pos_acq, acq_pos, 
         labels = sprintf("%+.1f%%", acq_val),
         cex = 0.8)
  }
  
  # Mark variables with opposite effects
  if(any(comparison$Opposite_Effects)) {
    mtext("* Variables with opposite effects on bankruptcy vs acquisition", 
          side = 3, line = 0.5, cex = 0.8, col = "purple")
    
    # Add markers for opposite effects
    for(i in which(comparison$Opposite_Effects)) {
      bank_pos <- barplot_result[1, i]
      acq_pos <- barplot_result[2, i]
      
      # Add asterisk at the middle
      text(xlim[1] + 1, (bank_pos + acq_pos)/2, "*", col = "purple", cex = 1.5)
    }
  }
  
  # 2. Create a 2x2 economic condition matrix visualization
  # This shows how different economic conditions affect both outcomes
  if(length(common_macro_vars) >= 2) {
    # Create a new plot
    par(mar = c(5, 5, 4, 2) + 0.1)
    
    # Set up the plot area - economic conditions matrix
    plot(NULL, xlim = c(-1, 1), ylim = c(-1, 1),
         xlab = "Bankruptcy Risk", ylab = "Acquisition Risk",
         main = "Company Outcomes Under Different Economic Conditions")
    
    # Add reference lines
    abline(h = 0, v = 0, lty = 2, col = "darkgray")
    
    # Add quadrant labels
    text(0.8, 0.8, "High Bankruptcy\nHigh Acquisition", col = "purple", cex = 0.8)
    text(-0.8, 0.8, "Low Bankruptcy\nHigh Acquisition", col = "blue", cex = 0.8)
    text(0.8, -0.8, "High Bankruptcy\nLow Acquisition", col = "red", cex = 0.8)
    text(-0.8, -0.8, "Low Bankruptcy\nLow Acquisition", col = "green4", cex = 0.8)
    
    # Add arrows for each macro variable
    for(i in 1:nrow(comparison)) {
      # Calculate arrow direction based on coefficients
      bank_effect <- comparison$Bank_Coef[i]
      acq_effect <- comparison$Acq_Coef[i]
      
      # Normalize to a reasonable length for visualization
      arrow_length <- sqrt(bank_effect^2 + acq_effect^2)
      norm_factor <- min(0.8, max(0.3, arrow_length)) / max(0.0001, arrow_length)
      
      x_end <- bank_effect * norm_factor
      y_end <- acq_effect * norm_factor
      
      # Draw the arrow
      arrows(0, 0, x_end, y_end, length = 0.1, 
             col = ifelse(comparison$Opposite_Effects[i], "purple", "black"),
             lwd = 2)
      
      # Add variable name
      text(x_end * 1.1, y_end * 1.1, comparison$Label[i], 
           cex = 0.8, 
           col = ifelse(comparison$Opposite_Effects[i], "purple", "black"))
    }
    
    # Add interpretation note
    mtext("Arrows show how each economic indicator affects both outcomes", 
          side = 3, line = 0.5, cex = 0.8)
  }
  
  # Print a summary of the comparative analysis
  cat("\nComparative Analysis of Macroeconomic Effects:\n")
  cat("-------------------------------------------\n")
  
  for(i in 1:nrow(comparison)) {
    var <- comparison$Variable[i]
    label <- comparison$Label[i]
    bank_hr <- comparison$Bank_HR[i]
    acq_hr <- comparison$Acq_HR[i]
    opposite <- comparison$Opposite_Effects[i]
    
    # Format effects as percentages
    if(bank_hr > 1) {
      bank_effect <- paste0("+", round((bank_hr - 1) * 100, 1), "%")
    } else {
      bank_effect <- paste0("-", round((1 - bank_hr) * 100, 1), "%")
    }
    
    if(acq_hr > 1) {
      acq_effect <- paste0("+", round((acq_hr - 1) * 100, 1), "%")
    } else {
      acq_effect <- paste0("-", round((1 - acq_hr) * 100, 1), "%")
    }
    
    cat(label, ":\n")
    cat("  - Effect on bankruptcy risk:", bank_effect, "\n")
    cat("  - Effect on acquisition risk:", acq_effect, "\n")
    
    if(opposite) {
      cat("  - This variable has OPPOSITE effects on bankruptcy vs. acquisition\n")
      
      # Add economic interpretation for opposite effects
      if(var == "gdp_growth") {
        if(bank_hr < 1 && acq_hr > 1) {
          cat("  - Interpretation: Economic growth REDUCES financial distress while INCREASING M&A activity\n")
          cat("  - This aligns with theories of opportunity-driven vs. distress-driven corporate events\n")
        } else {
          cat("  - Interpretation: Unusual pattern - Economic growth has unconventional effect on outcomes\n")
        }
      } else if(var == "gdp_deflator") {
        if(bank_hr > 1 && acq_hr < 1) {
          cat("  - Interpretation: Inflation INCREASES financial distress but REDUCES acquisition likelihood\n")
          cat("  - High inflation may create operational challenges while depressing M&A markets\n")
        } else {
          cat("  - Interpretation: Inflation has complex effects on corporate outcomes\n")
        }
      } else if(var == "unemployement") {
        if(bank_hr > 1 && acq_hr < 1) {
          cat("  - Interpretation: High unemployment INCREASES bankruptcy risk but REDUCES acquisitions\n")
          cat("  - Poor economic conditions increase distress while reducing M&A activity\n")
        } else {
          cat("  - Interpretation: Unemployment has complex effects on corporate outcomes\n")
        }
      }
    } else {
      cat("  - This variable affects both outcomes in the SAME direction\n")
      
      # Add economic interpretation for same-direction effects
      if(bank_hr > 1 && acq_hr > 1) {
        cat("  - Interpretation: This economic factor increases BOTH bankruptcy and acquisition risk\n")
        cat("  - May indicate a general economic stress factor increasing all corporate events\n")
      } else if(bank_hr < 1 && acq_hr < 1) {
        cat("  - Interpretation: This economic factor decreases BOTH bankruptcy and acquisition risk\n")
        cat("  - May indicate a stabilizing economic condition that reduces corporate events\n")
      }
    }
    cat("\n")
  }
  
  # Return comparison for further analysis
  return(comparison)
}

# Compare macroeconomic effects between models
macro_comparison <- compare_macro_effects()
Comparing macroeconomic effects between bankruptcy and acquisition models...


Comparative Analysis of Macroeconomic Effects:
-------------------------------------------
GDP Growth :
  - Effect on bankruptcy risk: +10.4% 
  - Effect on acquisition risk: +3.8% 
  - This variable affects both outcomes in the SAME direction
  - Interpretation: This economic factor increases BOTH bankruptcy and acquisition risk
  - May indicate a general economic stress factor increasing all corporate events

Inflation (GDP Deflator) :
  - Effect on bankruptcy risk: -16.1% 
  - Effect on acquisition risk: -3.7% 
  - This variable affects both outcomes in the SAME direction
  - Interpretation: This economic factor decreases BOTH bankruptcy and acquisition risk
  - May indicate a stabilizing economic condition that reduces corporate events

Unemployment Rate :
  - Effect on bankruptcy risk: -7.2% 
  - Effect on acquisition risk: -12.5% 
  - This variable affects both outcomes in the SAME direction
  - Interpretation: This economic factor decreases BOTH bankruptcy and acquisition risk
  - May indicate a stabilizing economic condition that reduces corporate events

Here the survival curves are visualized by economic conditions. This will help us understand how the risk of bankruptcy and acquisition varies under different economic conditions.

#-------------------------------------------------------------
# 13.4: Survival Curve Visualization with Economic Conditions
#-------------------------------------------------------------
cat("\n13.4: SURVIVAL CURVES BY ECONOMIC CONDITION\n")

13.4: SURVIVAL CURVES BY ECONOMIC CONDITION
cat("--------------------------------------\n")
--------------------------------------
visualize_survival_by_economy <- function(model, model_name, event_type) {
  cat("Creating survival curves by economic condition for", model_name, "model...\n")
  
  # Check if we have economic indicators in the data
  has_macro <- all(c("gdp_growth", "unemployement") %in% colnames(data))
  
  if(!has_macro) {
    cat("Economic indicators not available for condition-specific visualization.\n")
    return(NULL)
  }
  
  # Get last observation for each company
  final_obs <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate median values to define conditions
  median_gdp <- median(final_obs$gdp_growth, na.rm = TRUE)
  median_unemp <- median(final_obs$unemployement, na.rm = TRUE)
  
  # Create economic condition groups
  final_obs$econ_condition <- "Mixed"
  final_obs$econ_condition[final_obs$gdp_growth > median_gdp & 
                         final_obs$unemployement < median_unemp] <- "Strong"
  final_obs$econ_condition[final_obs$gdp_growth < median_gdp & 
                         final_obs$unemployement > median_unemp] <- "Weak"
  
  # Convert to factor with desired order
  final_obs$econ_condition <- factor(final_obs$econ_condition, 
                                  levels = c("Strong", "Mixed", "Weak"))
  
  # Calculate risk scores
  risk_scores <- predict(model, newdata = final_obs, type = "risk")
  
  # Add scores to data
  final_obs$risk_score <- risk_scores
  
  # Create survival object
  surv_obj <- Surv(time = final_obs$tstop, event = final_obs[[event_type]])
  
  # Fit Kaplan-Meier curves by economic condition
  km_fit <- survfit(surv_obj ~ econ_condition, data = final_obs)
  
  # Plot curves
  plot(km_fit, col = c("green3", "orange", "red"), lwd = 2,
       main = paste("Survival Curves by Economic Condition -", model_name),
       xlab = "Time (Years)", ylab = "Survival Probability")
  
  # Prepare legend text with counts
  condition_counts <- table(final_obs$econ_condition)
  condition_events <- sapply(levels(final_obs$econ_condition), function(cond) {
    sum(final_obs[[event_type]][final_obs$econ_condition == cond])
  })
  
  legend_text <- paste0(levels(final_obs$econ_condition), " Economy (n=", 
                      condition_counts, ", events=", condition_events, ")")
  
  # Add legend
  legend("bottomleft", legend = legend_text,
         col = c("green3", "orange", "red"), lwd = 2, bty = "n")
  
  # Log-rank test
  log_rank <- survdiff(surv_obj ~ econ_condition, data = final_obs)
  
  cat("\nLog-rank test comparing survival curves by economic condition:\n")
  print(log_rank)
  
  if(log_rank$chisq > qchisq(0.95, df = length(levels(final_obs$econ_condition)) - 1)) {
    cat("The survival curves differ significantly by economic condition (p < 0.05).\n")
  } else {
    cat("No significant difference in survival curves by economic condition (p >= 0.05).\n")
  }
  
  # Calculate event rates by economic condition
  cat("\nEvent rates by economic condition:\n")
  for(cond in levels(final_obs$econ_condition)) {
    n_companies <- sum(final_obs$econ_condition == cond)
    n_events <- sum(final_obs[[event_type]][final_obs$econ_condition == cond])
    event_rate <- n_events / n_companies * 100
    
    cat(cond, "Economy:", n_events, "events out of", n_companies, 
        "companies (", round(event_rate, 1), "%)\n", sep=" ")
  }
  
  # Calculate average risk scores by economic condition
  cat("\nAverage risk scores by economic condition:\n")
  for(cond in levels(final_obs$econ_condition)) {
    avg_risk <- mean(final_obs$risk_score[final_obs$econ_condition == cond])
    cat(cond, "Economy:", round(avg_risk, 4), "\n")
  }
  
  # Alternative visualization: boxplot of risk scores by economic condition
  boxplot(risk_score ~ econ_condition, data = final_obs,
          main = paste(model_name, "Risk by Economic Condition"),
          xlab = "Economic Condition", ylab = "Risk Score",
          col = c("green3", "orange", "red"))
  
  # Add event rates to boxplot
  for(i in 1:length(levels(final_obs$econ_condition))) {
    cond <- levels(final_obs$econ_condition)[i]
    event_rate <- sum(final_obs[[event_type]][final_obs$econ_condition == cond]) / 
               sum(final_obs$econ_condition == cond) * 100
    
    text(i, par("usr")[3] - 0.02 * diff(par("usr")[3:4]), 
         paste0(round(event_rate, 1), "% events"), 
         cex = 0.8, xpd = TRUE)
  }
  
  # Return the data with economic conditions and risk scores
  return(list(
    data = final_obs,
    km_fit = km_fit,
    log_rank = log_rank
  ))
}

# Create survival curves by economic condition
bankruptcy_econ <- visualize_survival_by_economy(final_bankruptcy_model, "Bankruptcy", "bankruptcy")
Creating survival curves by economic condition for Bankruptcy model...

Log-rank test comparing survival curves by economic condition:
Call:
survdiff(formula = surv_obj ~ econ_condition, data = final_obs)

                         N Observed Expected (O-E)^2/E (O-E)^2/V
econ_condition=Strong  335       27     7.65      49.0      51.5
econ_condition=Mixed  2921       64   111.11      20.0      57.0
econ_condition=Weak   1782       85    57.24      13.5      20.9

 Chisq= 85.1  on 2 degrees of freedom, p= <2e-16 
The survival curves differ significantly by economic condition (p < 0.05).

Event rates by economic condition:
Strong Economy: 27 events out of 335 companies ( 8.1 %)
Mixed Economy: 64 events out of 2921 companies ( 2.2 %)
Weak Economy: 85 events out of 1782 companies ( 4.8 %)

Average risk scores by economic condition:
Strong Economy: 16.4733 
Mixed Economy: 8.1441 
Weak Economy: 11.2469 

acquisition_econ <- visualize_survival_by_economy(final_acquisition_model, "Acquisition", "acquisition")
Creating survival curves by economic condition for Acquisition model...

Log-rank test comparing survival curves by economic condition:
Call:
survdiff(formula = surv_obj ~ econ_condition, data = final_obs)

                         N Observed Expected (O-E)^2/E (O-E)^2/V
econ_condition=Strong  335      186     91.1        99       106
econ_condition=Mixed  2921      815   1363.5       221       646
econ_condition=Weak   1782     1153    699.5       294       466

 Chisq= 658  on 2 degrees of freedom, p= <2e-16 
The survival curves differ significantly by economic condition (p < 0.05).

Event rates by economic condition:
Strong Economy: 186 events out of 335 companies ( 55.5 %)
Mixed Economy: 815 events out of 2921 companies ( 27.9 %)
Weak Economy: 1153 events out of 1782 companies ( 64.7 %)

Average risk scores by economic condition:
Strong Economy: 1.4515 
Mixed Economy: 2.0726 
Weak Economy: 1.623 

For more exploration we can analyze the sensitivity of the models to economic cycles. This will help us understand how the models perform under different economic conditions and whether they are robust to changes in the economic environment.

#-------------------------------------------------------------
# 13.5: Economic Cycle Sensitivity Analysis
#-------------------------------------------------------------
cat("\n13.5: ECONOMIC CYCLE SENSITIVITY ANALYSIS\n")

13.5: ECONOMIC CYCLE SENSITIVITY ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
analyze_economic_sensitivity <- function() {
  cat("Analyzing sensitivity to economic cycles...\n")
  
  # Check if we have economic indicators in the data
  has_macro <- all(c("gdp_growth", "unemployement") %in% colnames(data))
  
  if(!has_macro) {
    cat("Economic indicators not available for cycle sensitivity analysis.\n")
    return(NULL)
  }
  
  # Get last observation for each company and year
  last_by_year <- data %>%
    group_by(cusip, calendar_year) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate annual event rates - using base R aggregation to avoid dplyr dependency
  annual_stats <- aggregate(cbind(bankruptcy, acquisition, gdp_growth, unemployement, gdp_deflator) ~ calendar_year, 
                          data = last_by_year, 
                          FUN = function(x) c(sum = sum(x), mean = mean(x, na.rm = TRUE)))
  
  # Reshape the output to the desired format
  annual_stats <- data.frame(
    calendar_year = annual_stats$calendar_year,
    n_companies = as.numeric(table(last_by_year$calendar_year)),
    n_bankruptcies = annual_stats$bankruptcy[, "sum"],
    n_acquisitions = annual_stats$acquisition[, "sum"],
    bankruptcy_rate = annual_stats$bankruptcy[, "mean"] * 100,
    acquisition_rate = annual_stats$acquisition[, "mean"] * 100,
    gdp_growth = annual_stats$gdp_growth[, "mean"],
    unemployment = annual_stats$unemployement[, "mean"],
    inflation = annual_stats$gdp_deflator[, "mean"]
  )
  
  # Sort by year
  annual_stats <- annual_stats[order(annual_stats$calendar_year), ]
  
  # 1. Create time series plot of event rates and economic indicators
  # Set up multi-panel plot (2 rows, 1 column)
  par(mfrow = c(2, 1), mar = c(3, 4, 2, 4))
  
  # Plot event rates over time
  plot(annual_stats$calendar_year, annual_stats$bankruptcy_rate, 
       type = "o", col = "red", lwd = 2, pch = 16,
       xlab = "", ylab = "Event Rate (%)",
       main = "Corporate Event Rates Over Time",
       ylim = c(0, max(c(annual_stats$bankruptcy_rate, annual_stats$acquisition_rate)) * 1.2))
  
  # Add acquisition rates
  lines(annual_stats$calendar_year, annual_stats$acquisition_rate, 
        type = "o", col = "blue", lwd = 2, pch = 17)
  
  # Add legend
  legend("topright", legend = c("Bankruptcy Rate", "Acquisition Rate"),
         col = c("red", "blue"), lwd = 2, pch = c(16, 17), bty = "n")
  
  # Plot economic indicators
  plot(annual_stats$calendar_year, annual_stats$gdp_growth, 
       type = "o", col = "green4", lwd = 2, pch = 15,
       xlab = "Year", ylab = "GDP Growth (%)",
       main = "Economic Indicators Over Time",
       ylim = c(min(annual_stats$gdp_growth) * 1.2, max(annual_stats$gdp_growth) * 1.2))
  
  # Add unemployment on secondary y-axis
  par(new = TRUE)
  plot(annual_stats$calendar_year, annual_stats$unemployment, 
       type = "o", col = "brown", lwd = 2, pch = 18,
       xlab = "", ylab = "", axes = FALSE,
       ylim = c(min(annual_stats$unemployment) * 0.9, max(annual_stats$unemployment) * 1.1))
  
  # Add secondary y-axis
  axis(4, col = "brown", col.axis = "brown")
  mtext("Unemployment Rate (%)", side = 4, line = 2, col = "brown")
  
  # Add legend
  legend("bottomright", legend = c("GDP Growth", "Unemployment"),
         col = c("green4", "brown"), lwd = 2, pch = c(15, 18), bty = "n")
  
  # Reset plotting parameters
  par(mfrow = c(1, 1), mar = c(5, 4, 4, 2) + 0.1)
  
  # 2. Create scatter plots of event rates vs. economic indicators
  # Set up multi-panel plot (1 row, 2 columns)
  par(mfrow = c(1, 2))
  
  # Scatter plot for GDP growth
  plot(annual_stats$gdp_growth, annual_stats$bankruptcy_rate, 
       col = "red", pch = 16, cex = 1.2,
       xlab = "GDP Growth (%)", ylab = "Event Rate (%)",
       main = "Event Rates vs. GDP Growth",
       ylim = c(0, max(c(annual_stats$bankruptcy_rate, annual_stats$acquisition_rate)) * 1.2),
       xlim = c(min(annual_stats$gdp_growth) * 1.2, max(annual_stats$gdp_growth) * 1.2))
  
  points(annual_stats$gdp_growth, annual_stats$acquisition_rate, 
         col = "blue", pch = 17, cex = 1.2)
  
  # Add regression lines
  abline(lm(bankruptcy_rate ~ gdp_growth, data = annual_stats), col = "red", lty = 2)
  abline(lm(acquisition_rate ~ gdp_growth, data = annual_stats), col = "blue", lty = 2)
  
  # Add correlation coefficients
  bank_cor <- cor(annual_stats$gdp_growth, annual_stats$bankruptcy_rate, use = "complete.obs")
  acq_cor <- cor(annual_stats$gdp_growth, annual_stats$acquisition_rate, use = "complete.obs")
  
  text(min(annual_stats$gdp_growth) * 1.1, max(annual_stats$bankruptcy_rate) * 1.1,
       paste0("Bank r = ", round(bank_cor, 2)), col = "red", cex = 0.8)
  text(min(annual_stats$gdp_growth) * 1.1, max(annual_stats$bankruptcy_rate) * 1.0,
       paste0("Acq r = ", round(acq_cor, 2)), col = "blue", cex = 0.8)
  
  # Add legend
  legend("topleft", legend = c("Bankruptcy Rate", "Acquisition Rate"),
         col = c("red", "blue"), pch = c(16, 17), lty = 2, bty = "n")
  
  # Scatter plot for unemployment
  plot(annual_stats$unemployment, annual_stats$bankruptcy_rate, 
       col = "red", pch = 16, cex = 1.2,
       xlab = "Unemployment Rate (%)", ylab = "Event Rate (%)",
       main = "Event Rates vs. Unemployment",
       ylim = c(0, max(c(annual_stats$bankruptcy_rate, annual_stats$acquisition_rate)) * 1.2),
       xlim = c(min(annual_stats$unemployment) * 0.9, max(annual_stats$unemployment) * 1.1))
  
  points(annual_stats$unemployment, annual_stats$acquisition_rate, 
         col = "blue", pch = 17, cex = 1.2)
  
  # Add regression lines
  abline(lm(bankruptcy_rate ~ unemployment, data = annual_stats), col = "red", lty = 2)
  abline(lm(acquisition_rate ~ unemployment, data = annual_stats), col = "blue", lty = 2)
  
  # Add correlation coefficients
  bank_cor <- cor(annual_stats$unemployment, annual_stats$bankruptcy_rate, use = "complete.obs")
  acq_cor <- cor(annual_stats$unemployment, annual_stats$acquisition_rate, use = "complete.obs")
  
  text(max(annual_stats$unemployment) * 0.95, max(annual_stats$bankruptcy_rate) * 1.1,
       paste0("Bank r = ", round(bank_cor, 2)), col = "red", cex = 0.8)
  text(max(annual_stats$unemployment) * 0.95, max(annual_stats$bankruptcy_rate) * 1.0,
       paste0("Acq r = ", round(acq_cor, 2)), col = "blue", cex = 0.8)
  
  # Reset plotting parameters
  par(mfrow = c(1, 1))
  
  # Compute correlation matrix between event rates and economic indicators
  corr_vars <- c("bankruptcy_rate", "acquisition_rate", "gdp_growth", "unemployment", "inflation")
  corr_matrix <- cor(annual_stats[, corr_vars], use = "complete.obs")
  
  # Print correlation matrix
  cat("\nCorrelation Matrix of Event Rates and Economic Indicators:\n")
  print(round(corr_matrix, 3))
  
  # Classification of economic periods
  annual_stats$econ_period <- "Normal"
  annual_stats$econ_period[annual_stats$gdp_growth > quantile(annual_stats$gdp_growth, 0.67, na.rm = TRUE) &
                         annual_stats$unemployment < quantile(annual_stats$unemployment, 0.33, na.rm = TRUE)] <- "Expansion"
  annual_stats$econ_period[annual_stats$gdp_growth < quantile(annual_stats$gdp_growth, 0.33, na.rm = TRUE) &
                         annual_stats$unemployment > quantile(annual_stats$unemployment, 0.67, na.rm = TRUE)] <- "Recession"
  
  # Calculate average event rates by economic period using base R
  period_stats <- aggregate(
    cbind(bankruptcy_rate, acquisition_rate) ~ econ_period, 
    data = annual_stats, 
    FUN = mean
  )
  
  # Add count of years per period
  period_counts <- table(annual_stats$econ_period)
  period_stats$n_years <- period_counts[match(period_stats$econ_period, names(period_counts))]
  
  # Rename columns to match with the previous structure for compatibility with the barplot
  names(period_stats)[names(period_stats) == "bankruptcy_rate"] <- "avg_bankruptcy_rate"
  names(period_stats)[names(period_stats) == "acquisition_rate"] <- "avg_acquisition_rate"
  
  cat("\nAverage Event Rates by Economic Period:\n")
  print(period_stats)
  
  # Create bar plot of event rates by economic period - with error handling
  tryCatch({
    # Create matrix properly from the period_stats dataframe
    barplot_data <- as.matrix(period_stats[, c("avg_bankruptcy_rate", "avg_acquisition_rate"), drop=FALSE])
    rownames(barplot_data) <- period_stats$econ_period
    
    barplot(t(barplot_data), beside = TRUE,
            main = "Event Rates by Economic Period",
            xlab = "Economic Period", ylab = "Average Event Rate (%)",
            col = c("red", "blue"),
            ylim = c(0, max(barplot_data, na.rm = TRUE) * 1.2))
    
    # Add legend
    legend("topright", legend = c("Bankruptcy Rate", "Acquisition Rate"),
           fill = c("red", "blue"), bty = "n")
  }, error = function(e) {
    cat("Warning: Could not create barplot of event rates by economic period:", e$message, "\n")
    cat("Printing data frame instead:\n")
    print(period_stats)
  })
  
  # Return the annual statistics for further analysis
  return(list(
    annual_stats = annual_stats,
    period_stats = period_stats,
    correlations = corr_matrix
  ))
}

# Analyze economic cycle sensitivity
economic_cycles <- analyze_economic_sensitivity()
Analyzing sensitivity to economic cycles...


Correlation Matrix of Event Rates and Economic Indicators:
                 bankruptcy_rate acquisition_rate gdp_growth unemployment inflation
bankruptcy_rate            1.000            0.584     -0.084       -0.256    -0.227
acquisition_rate           0.584            1.000     -0.171       -0.068    -0.198
gdp_growth                -0.084           -0.171      1.000       -0.423     0.276
unemployment              -0.256           -0.068     -0.423        1.000    -0.398
inflation                 -0.227           -0.198      0.276       -0.398     1.000

Average Event Rates by Economic Period:

Not sure if this has any value…

We also look at sector-specific macroeconomic sensitivity. This will help us understand how different sectors respond to macroeconomic changes and whether there are significant differences in their sensitivities.

#-------------------------------------------------------------
# 13.10: Sector-Specific Macroeconomic Sensitivity
#-------------------------------------------------------------
cat("\n13.10: SECTOR-SPECIFIC MACROECONOMIC SENSITIVITY\n")

13.10: SECTOR-SPECIFIC MACROECONOMIC SENSITIVITY
cat("--------------------------------------\n")
--------------------------------------
analyze_sector_macro_sensitivity <- function() {
  cat("Analyzing sector-specific sensitivity to macroeconomic factors...\n")
  
  # Check if we have macro variables in our dataset
  if(!all(c("gdp_growth", "unemployement", "gdp_deflator") %in% colnames(data))) {
    cat("Macro variables not found in dataset. Cannot perform sector analysis.\n")
    return(NULL)
  }
  
  # Get last observation for each company
  final_obs <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Ensure gsector is a factor
  final_obs$gsector <- as.factor(final_obs$gsector)
  
  # Get sectors with sufficient data
  sector_counts <- table(final_obs$gsector)
  valid_sectors <- names(sector_counts)[sector_counts >= 30]
  
  if(length(valid_sectors) == 0) {
    cat("Not enough sectors with sufficient observations for analysis.\n")
    return(NULL)
  }
  
  # Filter to valid sectors
  sector_data <- final_obs[final_obs$gsector %in% valid_sectors, ]
  
  # Initialize matrices to store correlations
  sectors <- unique(sector_data$gsector)
  macro_vars <- c("gdp_growth", "unemployement", "gdp_deflator")
  
  # Create storage for correlation matrices
  sector_correlations <- array(NA, dim = c(length(sectors), 2, length(macro_vars)),
                              dimnames = list(sectors, c("bankruptcy", "acquisition"), macro_vars))
  
  # Calculate correlations for each sector and outcome
  for(s in sectors) {
    sector_subset <- sector_data[sector_data$gsector == s, ]
    
    # Only proceed if we have sufficient data
    if(nrow(sector_subset) >= 10) {
      for(m in macro_vars) {
        if(all(c(m, "bankruptcy", "acquisition") %in% colnames(sector_subset))) {
          # Calculate correlations
          cor_bank <- cor(sector_subset[[m]], sector_subset$bankruptcy, use = "complete.obs")
          cor_acq <- cor(sector_subset[[m]], sector_subset$acquisition, use = "complete.obs")
          
          # Store in the array
          sector_correlations[s, "bankruptcy", m] <- cor_bank
          sector_correlations[s, "acquisition", m] <- cor_acq
        }
      }
    }
  }
  
  # Convert to more manageable data frame for visualization
  correlation_df <- data.frame()
  for(s in sectors) {
    for(m in macro_vars) {
      for(outcome in c("bankruptcy", "acquisition")) {
        correlation_df <- rbind(correlation_df, data.frame(
          Sector = s,
          MacroVar = m,
          Outcome = outcome,
          Correlation = sector_correlations[s, outcome, m],
          stringsAsFactors = FALSE
        ))
      }
    }
  }
  
  # Create a readable macro variable name mapping
  correlation_df$MacroVarLabel <- ifelse(correlation_df$MacroVar == "gdp_growth", "GDP Growth",
                                       ifelse(correlation_df$MacroVar == "unemployement", "Unemployment",
                                              "Inflation"))
  
  # Calculate sector sensitivity scores (average absolute correlation)
  sector_sensitivity <- aggregate(abs(Correlation) ~ Sector, data = correlation_df, FUN = mean, na.rm = TRUE)
  colnames(sector_sensitivity)[2] <- "AvgSensitivity"
  
  # Sort by sensitivity
  sector_sensitivity <- sector_sensitivity[order(sector_sensitivity$AvgSensitivity, decreasing = TRUE), ]
  
  # Create barplot of sector sensitivity
  barplot(sector_sensitivity$AvgSensitivity, 
          names.arg = paste("Sector", sector_sensitivity$Sector),
          main = "Sector Sensitivity to Macroeconomic Factors",
          xlab = "Industry Sector", ylab = "Average Absolute Correlation",
          col = colorRampPalette(c("lightblue", "darkblue"))(nrow(sector_sensitivity)),
          las = 2)
  
  # Create heatmap for bankruptcy correlations
  bank_cor <- correlation_df[correlation_df$Outcome == "bankruptcy", ]
  bank_matrix <- reshape(bank_cor, idvar = "Sector", timevar = "MacroVarLabel", 
                        direction = "wide", drop = c("Outcome", "MacroVar"))
  rownames(bank_matrix) <- paste("Sector", bank_matrix$Sector)
  bank_matrix <- bank_matrix[, -1]
  colnames(bank_matrix) <- sub("Correlation.", "", colnames(bank_matrix))
  
  # Sort sectors by average sensitivity
  bank_matrix <- bank_matrix[match(paste("Sector", sector_sensitivity$Sector), rownames(bank_matrix)), ]
  
  # Plot heatmap for bankruptcy
  par(mar = c(5, 12, 4, 2) + 0.1)  # Adjust margins for sector names
  image(z = t(as.matrix(bank_matrix)), 
        col = colorRampPalette(c("blue", "white", "red"))(100),
        main = "Sector Sensitivity to Macro Factors - Bankruptcy",
        xlab = "", ylab = "")
  
  # Add sector and macro variable labels
  axis(2, at = seq(0, 1, length.out = nrow(bank_matrix)), 
       labels = rownames(bank_matrix), las = 2, cex.axis = 0.8)
  axis(1, at = seq(0, 1, length.out = 3), 
       labels = colnames(bank_matrix), las = 1)
  
  # Add color legend
  legend_colors <- colorRampPalette(c("blue", "white", "red"))(5)
  legend_values <- c(-0.5, -0.25, 0, 0.25, 0.5)
  legend("bottom", legend = legend_values, fill = legend_colors, 
         horiz = TRUE, cex = 0.7, title = "Correlation", bty = "n")
  
  # Print summary of sector sensitivities
  cat("\nSector Sensitivity to Macroeconomic Factors (Ranked):\n")
  cat("----------------------------------------------------\n")
  print(sector_sensitivity)
  
  # Identify most and least sensitive sectors
  most_sensitive <- sector_sensitivity$Sector[1]
  least_sensitive <- sector_sensitivity$Sector[nrow(sector_sensitivity)]
  
  cat("\nMost economically sensitive sector:", most_sensitive, 
      "(average correlation magnitude =", round(sector_sensitivity$AvgSensitivity[1], 3), ")\n")
  cat("Least economically sensitive sector:", least_sensitive, 
      "(average correlation magnitude =", round(sector_sensitivity$AvgSensitivity[nrow(sector_sensitivity)], 3), ")\n")
  
  return(sector_sensitivity)
}

# Run the sector-specific macro sensitivity analysis
sector_macro_sensitivity <- analyze_sector_macro_sensitivity()
Analyzing sector-specific sensitivity to macroeconomic factors...
Warning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zeroWarning: the standard deviation is zero


Sector Sensitivity to Macroeconomic Factors (Ranked):
----------------------------------------------------

Most economically sensitive sector: 60 (average correlation magnitude = 0.412 )
Least economically sensitive sector: 15 (average correlation magnitude = 0.127 )

This analysis provides insights into how different sectors respond to macroeconomic changes, which can be useful for investors and policymakers.

Next is the business cycle impact analysis. This will help us understand how corporate events are influenced by the economic cycle and whether there are significant differences in event rates during different economic conditions.

#-------------------------------------------------------------
# 13.11: Business Cycle Impact Analysis
#-------------------------------------------------------------
cat("\n13.11: BUSINESS CYCLE IMPACT ANALYSIS\n")

13.11: BUSINESS CYCLE IMPACT ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
analyze_business_cycle_impact <- function() {
  cat("Analyzing impact of business cycles on corporate outcomes...\n")
  
  # Check for necessary macro variables
  if(!all(c("gdp_growth", "unemployement") %in% colnames(data))) {
    cat("Required macroeconomic variables not found. Cannot perform business cycle analysis.\n")
    return(NULL)
  }
  
  # Get last observation for each company by year
  year_obs <- data %>%
    group_by(cusip, calendar_year) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Define business cycle phases based on economic indicators
  # Calculate median values for thresholds
  median_gdp <- median(year_obs$gdp_growth, na.rm = TRUE)
  median_unemp <- median(year_obs$unemployement, na.rm = TRUE)
  
  # Define economic cycle phases
  year_obs$econ_condition <- "Normal"
  year_obs$econ_condition[year_obs$gdp_growth > median_gdp & 
                          year_obs$unemployement < median_unemp] <- "Expansion"
  year_obs$econ_condition[year_obs$gdp_growth < median_gdp & 
                          year_obs$unemployement > median_unemp] <- "Contraction"
  
  # Calculate event rates by economic condition
  condition_stats <- aggregate(cbind(bankruptcy, acquisition) ~ econ_condition, 
                             data = year_obs, FUN = mean)
  condition_stats$bankruptcy <- condition_stats$bankruptcy * 100  # Convert to percentage
  condition_stats$acquisition <- condition_stats$acquisition * 100
  
  # Count number of observations per condition
  condition_counts <- table(year_obs$econ_condition)
  condition_stats$count <- as.vector(condition_counts[match(condition_stats$econ_condition, 
                                                           names(condition_counts))])
  
  # Create barplot data
  barplot_data <- as.matrix(condition_stats[, c("bankruptcy", "acquisition")])
  rownames(barplot_data) <- condition_stats$econ_condition
  
  # Create barplot
  barplot(t(barplot_data), 
          beside = TRUE, 
          col = c("firebrick", "steelblue"),
          main = "Corporate Event Rates by Economic Condition",
          xlab = "Economic Condition", 
          ylab = "Event Rate (%)",
          legend.text = c("Bankruptcy", "Acquisition"),
          args.legend = list(x = "topright", bty = "n"),
          ylim = c(0, max(barplot_data) * 1.2))
  
  # Add count labels
  mtext(paste0("n=", condition_stats$count), 
        side = 1, line = 3, 
        at = seq(1.5, by = 3, length.out = nrow(condition_stats)))
  
  # Print summary statistics
  cat("\nEvent Rates by Economic Condition:\n")
  cat("----------------------------------\n")
  print(condition_stats[, c("econ_condition", "count", "bankruptcy", "acquisition")])
  
  # Calculate statistical tests for rate differences
  cat("\nStatistical tests for differences in event rates by economic condition:\n")
  
  # For bankruptcy
  binom_test_bank <- prop.test(
    x = c(sum(year_obs$bankruptcy[year_obs$econ_condition == "Expansion"]),
          sum(year_obs$bankruptcy[year_obs$econ_condition == "Contraction"])),
    n = c(sum(year_obs$econ_condition == "Expansion"),
          sum(year_obs$econ_condition == "Contraction"))
  )
  
  # For acquisition
  binom_test_acq <- prop.test(
    x = c(sum(year_obs$acquisition[year_obs$econ_condition == "Expansion"]),
          sum(year_obs$acquisition[year_obs$econ_condition == "Contraction"])),
    n = c(sum(year_obs$econ_condition == "Expansion"),
          sum(year_obs$econ_condition == "Contraction"))
  )
  
  cat("Bankruptcy rates (Expansion vs Contraction): p-value =", 
      format.pval(binom_test_bank$p.value, digits = 3), "\n")
  cat("Acquisition rates (Expansion vs Contraction): p-value =", 
      format.pval(binom_test_acq$p.value, digits = 3), "\n")
  
  # Create survival curves by economic condition
  # Create survival object
  surv_bank <- Surv(time = year_obs$tstop, event = year_obs$bankruptcy)
  surv_acq <- Surv(time = year_obs$tstop, event = year_obs$acquisition)
  
  # Fit KM curves by economic condition
  km_bank <- survfit(surv_bank ~ econ_condition, data = year_obs)
  km_acq <- survfit(surv_acq ~ econ_condition, data = year_obs)
  
  # Plot KM curves for bankruptcy
  plot(km_bank, col = c("green3", "blue", "red"), lwd = 2,
       main = "Bankruptcy-Free Survival by Economic Condition",
       xlab = "Time (Years)", ylab = "Survival Probability")
  
  # Add legend
  legend("bottomleft", legend = levels(as.factor(year_obs$econ_condition)),
         col = c("green3", "blue", "red"), lwd = 2, bty = "n")
  
  # Log-rank test for bankruptcy
  log_rank_bank <- survdiff(surv_bank ~ econ_condition, data = year_obs)
  
  cat("\nLog-rank test for bankruptcy by economic condition:\n")
  print(log_rank_bank)
  
  # Plot KM curves for acquisition
  plot(km_acq, col = c("green3", "blue", "red"), lwd = 2,
       main = "Acquisition-Free Survival by Economic Condition",
       xlab = "Time (Years)", ylab = "Survival Probability")
  
  # Add legend
  legend("bottomleft", legend = levels(as.factor(year_obs$econ_condition)),
         col = c("green3", "blue", "red"), lwd = 2, bty = "n")
  
  # Log-rank test for acquisition
  log_rank_acq <- survdiff(surv_acq ~ econ_condition, data = year_obs)
  
  cat("\nLog-rank test for acquisition by economic condition:\n")
  print(log_rank_acq)
  
  # Summary of findings
  cat("\nSummary of Business Cycle Impact Analysis:\n")
  cat("----------------------------------------\n")
  
  # Bankruptcy analysis
  cat("Bankruptcy:\n")
  bank_exp <- condition_stats$bankruptcy[condition_stats$econ_condition == "Expansion"]
  bank_cont <- condition_stats$bankruptcy[condition_stats$econ_condition == "Contraction"]
  bank_ratio <- bank_cont / bank_exp
  
  if(bank_cont > bank_exp) {
    cat("- Bankruptcy risk is", round(bank_ratio, 1), "times higher during economic contractions\n")
  } else {
    cat("- Bankruptcy risk is", round(1/bank_ratio, 1), "times higher during economic expansions\n")
  }
  
  if(binom_test_bank$p.value < 0.05) {
    cat("- The difference is statistically significant (p < 0.05)\n")
  } else {
    cat("- The difference is not statistically significant (p >= 0.05)\n")
  }
  
  # Acquisition analysis
  cat("\nAcquisition:\n")
  acq_exp <- condition_stats$acquisition[condition_stats$econ_condition == "Expansion"]
  acq_cont <- condition_stats$acquisition[condition_stats$econ_condition == "Contraction"]
  acq_ratio <- acq_exp / acq_cont
  
  if(acq_exp > acq_cont) {
    cat("- Acquisition likelihood is", round(acq_ratio, 1), "times higher during economic expansions\n")
  } else {
    cat("- Acquisition likelihood is", round(1/acq_ratio, 1), "times higher during economic contractions\n")
  }
  
  if(binom_test_acq$p.value < 0.05) {
    cat("- The difference is statistically significant (p < 0.05)\n")
  } else {
    cat("- The difference is not statistically significant (p >= 0.05)\n")
  }
  
  # Return analysis results
  return(list(
    condition_stats = condition_stats,
    bankruptcy_test = binom_test_bank,
    acquisition_test = binom_test_acq,
    km_bank = km_bank,
    km_acq = km_acq
  ))
}

# Run the business cycle impact analysis
business_cycle_analysis <- analyze_business_cycle_impact()
Analyzing impact of business cycles on corporate outcomes...

Event Rates by Economic Condition:
----------------------------------

Statistical tests for differences in event rates by economic condition:
Bankruptcy rates (Expansion vs Contraction): p-value = 0.0861 
Acquisition rates (Expansion vs Contraction): p-value = 0.0657 


Log-rank test for bankruptcy by economic condition:
Call:
survdiff(formula = surv_bank ~ econ_condition, data = year_obs)

                               N Observed Expected (O-E)^2/E (O-E)^2/V
econ_condition=Contraction 20150       48     59.8     2.318     3.577
econ_condition=Expansion   17129       58     41.4     6.699     8.908
econ_condition=Normal      26312       70     74.9     0.317     0.553

 Chisq= 9.5  on 2 degrees of freedom, p= 0.008 


Log-rank test for acquisition by economic condition:
Call:
survdiff(formula = surv_acq ~ econ_condition, data = year_obs)

                               N Observed Expected (O-E)^2/E (O-E)^2/V
econ_condition=Contraction 20150      657      736      8.55      13.3
econ_condition=Expansion   17129      619      500     28.34      37.7
econ_condition=Normal      26312      878      918      1.72       3.0

 Chisq= 39.5  on 2 degrees of freedom, p= 3e-09 

Summary of Business Cycle Impact Analysis:
----------------------------------------
Bankruptcy:
- Bankruptcy risk is 1.4 times higher during economic expansions
- The difference is not statistically significant (p >= 0.05)

Acquisition:
- Acquisition likelihood is 1.1 times higher during economic expansions
- The difference is not statistically significant (p >= 0.05)

We also stratify surival curces by sector. This will help us understand how different sectors respond to corporate events and whether there are significant differences in survival rates across sectors.

#-------------------------------------------------------------
# 13.12: Enhanced Sector-Specific Survival Analysis
#-------------------------------------------------------------
cat("\n13.12: ENHANCED SECTOR-SPECIFIC SURVIVAL ANALYSIS\n")

13.12: ENHANCED SECTOR-SPECIFIC SURVIVAL ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
# Function to create enhanced KM curves stratified by sector with additional analysis
create_enhanced_sector_km_curves <- function() {#-------------------------------------------------------------
# 13.12: Enhanced Sector-Specific Survival Analysis (FIXED)
#-------------------------------------------------------------
cat("\n13.12: ENHANCED SECTOR-SPECIFIC SURVIVAL ANALYSIS\n")
cat("--------------------------------------\n")

# Function to create enhanced KM curves stratified by sector with additional analysis
create_enhanced_sector_km_curves <- function() {
  cat("Creating enhanced Kaplan-Meier curves by industry sector for both event types...\n")
  
  # Get the last observation for each company
  final_obs <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Ensure gsector is a factor
  final_obs$gsector <- as.factor(final_obs$gsector)
  
  # Count events by sector
  sector_bankruptcy <- tapply(final_obs$bankruptcy, final_obs$gsector, sum)
  sector_acquisition <- tapply(final_obs$acquisition, final_obs$gsector, sum)
  
  # Count companies per sector
  sector_counts <- table(final_obs$gsector)
  
  # Create data frame of sector statistics
  sector_stats <- data.frame(
    Sector = names(sector_counts),
    Companies = as.numeric(sector_counts),
    Bankruptcy_Events = as.numeric(sector_bankruptcy),
    Bankruptcy_Rate = as.numeric(sector_bankruptcy) / as.numeric(sector_counts) * 100,
    Acquisition_Events = as.numeric(sector_acquisition),
    Acquisition_Rate = as.numeric(sector_acquisition) / as.numeric(sector_counts) * 100,
    stringsAsFactors = FALSE
  )
  
  # Sort by number of companies
  sector_stats <- sector_stats[order(sector_stats$Companies, decreasing = TRUE), ]
  
  # Select sectors for KM plots (top sectors by company count with sufficient events)
  valid_bankruptcy_sectors <- sector_stats$Sector[
    sector_stats$Companies >= 30 & sector_stats$Bankruptcy_Events >= 5
  ]
  
  valid_acquisition_sectors <- sector_stats$Sector[
    sector_stats$Companies >= 30 & sector_stats$Acquisition_Events >= 5
  ]
  
  # Take top 5 sectors for each event type
  if(length(valid_bankruptcy_sectors) > 5) {
    bankruptcy_sectors <- valid_bankruptcy_sectors[1:5]
  } else {
    bankruptcy_sectors <- valid_bankruptcy_sectors
  }
  
  if(length(valid_acquisition_sectors) > 5) {
    acquisition_sectors <- valid_acquisition_sectors[1:5]
  } else {
    acquisition_sectors <- valid_acquisition_sectors
  }
  
  # Create function for fitting and plotting KM curves by sector for an event type
  plot_sector_km <- function(event_type, sectors, title) {
    # Filter data to these sectors
    sector_data <- final_obs[final_obs$gsector %in% sectors, ]
    
    # Create survival object
    surv_obj <- Surv(time = sector_data$tstop, event = sector_data[[event_type]])
    
    # Fit KM curves by sector
    km_fit <- survfit(surv_obj ~ gsector, data = sector_data)
    
    # Create colors for the sectors
    sector_colors <- rainbow(length(sectors))
    
    # Set up plot area
    par(mar = c(5, 4, 4, 8) + 0.1)  # Extra space for legend
    
    # Plot KM curves
    plot(km_fit, col = sector_colors, lwd = 2,
         main = title,
         xlab = "Time (Years)", ylab = "Survival Probability",
         lty = 1:length(sectors))
    
    # Create sector labels with event counts and rates
    sector_labels <- sapply(sectors, function(s) {
      count <- sum(sector_data$gsector == s)
      events <- sum(sector_data[[event_type]][sector_data$gsector == s])
      rate <- events / count * 100
      paste0("Sector ", s, " (", events, "/", count, ", ", round(rate, 1), "%)")
    })
    
    # Add legend with more detailed information
    legend("topright", inset = c(-0.35, 0), legend = sector_labels,
           col = sector_colors, lwd = 2, lty = 1:length(sectors), 
           bty = "n", xpd = TRUE, cex = 0.8)
    
    # Log-rank test
    log_rank <- survdiff(surv_obj ~ gsector, data = sector_data)
    
    # Add p-value to plot
    p_value <- 1 - pchisq(log_rank$chisq, df = length(sectors) - 1)
    significance <- ifelse(p_value < 0.05, "Significant", "Not significant")
    mtext(paste0("Log-rank p = ", format.pval(p_value, digits = 3), " (", significance, ")"), 
          side = 3, line = 0.5, cex = 0.8)
    
    # Calculate and return median survival times
    medians <- summary(km_fit)$table[, "median"]
    names(medians) <- sectors
    
    return(list(
      km_fit = km_fit,
      log_rank = log_rank,
      medians = medians,
      sectors = sectors
    ))
  }
  
  # Plot KM curves for bankruptcy
  if(length(bankruptcy_sectors) >= 2) {
    cat("\nPlotting KM curves for bankruptcy by sector...\n")
    bankruptcy_km <- plot_sector_km("bankruptcy", bankruptcy_sectors, 
                                 "Bankruptcy-Free Survival by Industry Sector")
    
    # Print log-rank test results
    cat("\nLog-rank test for bankruptcy by sector:\n")
    print(bankruptcy_km$log_rank)
    
    # Print median survival times
    cat("\nMedian time to bankruptcy by sector:\n")
    print(bankruptcy_km$medians)
    
    # Calculate hazard ratios between sectors using Cox model
    cat("\nHazard ratios between sectors for bankruptcy:\n")
    # Use the first sector as reference
    ref_sector <- bankruptcy_sectors[1]
    bankruptcy_sectors_formula <- as.formula(paste0("Surv(tstop, bankruptcy) ~ gsector"))
    bankruptcy_sectors_model <- coxph(bankruptcy_sectors_formula, 
                                    data = final_obs[final_obs$gsector %in% bankruptcy_sectors, ])
    print(summary(bankruptcy_sectors_model)$conf.int)
  } else {
    cat("\nNot enough sectors with sufficient bankruptcy events for KM curves.\n")
  }
  
  # Plot KM curves for acquisition
  if(length(acquisition_sectors) >= 2) {
    cat("\nPlotting KM curves for acquisition by sector...\n")
    acquisition_km <- plot_sector_km("acquisition", acquisition_sectors, 
                                  "Acquisition-Free Survival by Industry Sector")
    
    # Print log-rank test results
    cat("\nLog-rank test for acquisition by sector:\n")
    print(acquisition_km$log_rank)
    
    # Print median survival times
    cat("\nMedian time to acquisition by sector:\n")
    print(acquisition_km$medians)
    
    # Calculate hazard ratios between sectors using Cox model
    cat("\nHazard ratios between sectors for acquisition:\n")
    # Use the first sector as reference
    ref_sector <- acquisition_sectors[1]
    acquisition_sectors_formula <- as.formula(paste0("Surv(tstop, acquisition) ~ gsector"))
    acquisition_sectors_model <- coxph(acquisition_sectors_formula, 
                                     data = final_obs[final_obs$gsector %in% acquisition_sectors, ])
    print(summary(acquisition_sectors_model)$conf.int)
  } else {
    cat("\nNot enough sectors with sufficient acquisition events for KM curves.\n")
  }
  
  # Create a comparative analysis of sector risk profiles
  if(length(intersect(bankruptcy_sectors, acquisition_sectors)) > 0) {
    cat("\nComparative Analysis of Sector Risk Profiles:\n")
    cat("------------------------------------------\n")
    
    common_sectors <- intersect(bankruptcy_sectors, acquisition_sectors)
    
    for(sector in common_sectors) {
      # Find the row index for this sector in sector_stats
      sector_idx <- which(sector_stats$Sector == sector)
      
      # Access the rates using the index
      if(length(sector_idx) > 0) {
        bank_rate <- sector_stats$Bankruptcy_Rate[sector_idx]
        acq_rate <- sector_stats$Acquisition_Rate[sector_idx]
        
        cat("Sector", sector, ":\n")
        cat("  Bankruptcy Rate:", round(bank_rate, 1), "%\n")
        cat("  Acquisition Rate:", round(acq_rate, 1), "%\n")
        cat("  Ratio (Acquisition/Bankruptcy):", round(acq_rate/bank_rate, 1), "\n\n")
      }
    }
  }
  
  # Reset plotting parameters
  par(mar = c(5, 4, 4, 2) + 0.1)
  
  # Return sector statistics for further analysis
  return(sector_stats)
}

# Create enhanced KM curves by sector
sector_survival_analysis <- create_enhanced_sector_km_curves()}

# Create enhanced KM curves by sector
sector_survival_analysis <- create_enhanced_sector_km_curves()

13.12: ENHANCED SECTOR-SPECIFIC SURVIVAL ANALYSIS
--------------------------------------
Creating enhanced Kaplan-Meier curves by industry sector for both event types...

Plotting KM curves for bankruptcy by sector...

Log-rank test for bankruptcy by sector:
Call:
survdiff(formula = surv_obj ~ gsector, data = sector_data)

              N Observed Expected (O-E)^2/E (O-E)^2/V
gsector=20  726       24    26.04     0.160   0.19554
gsector=25  916       58    31.33    22.702  28.91212
gsector=35 1037       22    35.86     5.357   7.09959
gsector=45 1391       35    45.61     2.468   3.58749
gsector=50  266        8     8.16     0.003   0.00319

 Chisq= 30.8  on 4 degrees of freedom, p= 3e-06 

Median time to bankruptcy by sector:
45 35 25 20 50 
NA NA NA NA NA 

Hazard ratios between sectors for bankruptcy:
          exp(coef) exp(-coef) lower .95 upper .95
gsector15        NA         NA        NA        NA
gsector20 0.9396264  1.0642527 0.4220346  2.092003
gsector25 1.8897915  0.5291589 0.9022807  3.958094
gsector30        NA         NA        NA        NA
gsector35 0.6254985  1.5987249 0.2784621  1.405033
gsector45 0.7825090  1.2779406 0.3629861  1.686898
gsector50        NA         NA        NA        NA
gsector55        NA         NA        NA        NA
gsector60        NA         NA        NA        NA

Plotting KM curves for acquisition by sector...


Log-rank test for acquisition by sector:
Call:
survdiff(formula = surv_obj ~ gsector, data = sector_data)

              N Observed Expected (O-E)^2/E (O-E)^2/V
gsector=20  726      266      330    12.567    15.687
gsector=25  916      349      398     6.145     8.012
gsector=35 1037      446      457     0.271     0.368
gsector=45 1391      724      579    36.441    54.178
gsector=50  266       83      103     3.944     4.281

 Chisq= 60.9  on 4 degrees of freedom, p= 2e-12 

Median time to acquisition by sector:
      45       35       25       20       50 
24.99658 20.99932 18.99795 15.99726 24.99932 

Hazard ratios between sectors for acquisition:
          exp(coef) exp(-coef) lower .95 upper .95
gsector15        NA         NA        NA        NA
gsector20  1.001371  0.9986308 0.7826237  1.281260
gsector25  1.091240  0.9163883 0.8589343  1.386376
gsector30        NA         NA        NA        NA
gsector35  1.216512  0.8220226 0.9623909  1.537733
gsector45  1.565884  0.6386171 1.2476895  1.965226
gsector50        NA         NA        NA        NA
gsector55        NA         NA        NA        NA
gsector60        NA         NA        NA        NA

Comparative Analysis of Sector Risk Profiles:
------------------------------------------
Sector 45 :
  Bankruptcy Rate: 2.5 %
  Acquisition Rate: 52 %
  Ratio (Acquisition/Bankruptcy): 20.7 

Sector 35 :
  Bankruptcy Rate: 2.1 %
  Acquisition Rate: 43 %
  Ratio (Acquisition/Bankruptcy): 20.3 

Sector 25 :
  Bankruptcy Rate: 6.3 %
  Acquisition Rate: 38.1 %
  Ratio (Acquisition/Bankruptcy): 6 

Sector 20 :
  Bankruptcy Rate: 3.3 %
  Acquisition Rate: 36.6 %
  Ratio (Acquisition/Bankruptcy): 11.1 

Sector 50 :
  Bankruptcy Rate: 3 %
  Acquisition Rate: 31.2 %
  Ratio (Acquisition/Bankruptcy): 10.4 

We see how the model performs by industry sector. This will help us understand how the model’s performance varies across different sectors and whether there are significant differences in event rates by sector.

#-------------------------------------------------------------
# 13.14: Model Performance by Industry Sector
#-------------------------------------------------------------
cat("\n13.14: MODEL PERFORMANCE BY INDUSTRY SECTOR\n")

13.14: MODEL PERFORMANCE BY INDUSTRY SECTOR
cat("--------------------------------------\n")
--------------------------------------
evaluate_model_by_sector <- function(model, model_name, event_type) {
  cat("Evaluating", model_name, "model performance by industry sector...\n")
  
  # Get last observation for each company
  final_obs <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Ensure gsector is a factor
  final_obs$gsector <- as.factor(final_obs$gsector)
  
  # Calculate risk scores
  risk_scores <- predict(model, newdata = final_obs, type = "risk")
  final_obs$risk_score <- risk_scores
  
  # Get unique sectors with sufficient data
  sectors <- names(table(final_obs$gsector)[table(final_obs$gsector) >= 20])
  
  # Create data frame for results
  sector_perf <- data.frame(
    Sector = sectors,
    Companies = NA,
    Events = NA,
    Event_Rate = NA,
    AUC = NA,
    C_Index = NA,
    stringsAsFactors = FALSE
  )
  
  # Calculate metrics for each sector
  for(i in 1:length(sectors)) {
    s <- sectors[i]
    sector_data <- final_obs[final_obs$gsector == s, ]
    
    # Count companies and events
    n_companies <- nrow(sector_data)
    n_events <- sum(sector_data[[event_type]])
    event_rate <- n_events / n_companies * 100
    
    sector_perf$Companies[i] <- n_companies
    sector_perf$Events[i] <- n_events
    sector_perf$Event_Rate[i] <- event_rate
    
    # Calculate AUC if possible (at least 5 events and 5 non-events)
    if(n_events >= 5 && (n_companies - n_events) >= 5) {
      if(requireNamespace("pROC", quietly = TRUE)) {
        tryCatch({
          # Calculate AUC
          roc_obj <- pROC::roc(sector_data[[event_type]], sector_data$risk_score, quiet = TRUE)
          sector_perf$AUC[i] <- as.numeric(pROC::auc(roc_obj))
          
          # Calculate confidence interval for reporting
          ci <- pROC::ci(roc_obj)
          cat(sprintf("Sector %s: AUC = %.3f (95%% CI: %.3f-%.3f) with %d events (%.1f%%)\n", 
                    s, sector_perf$AUC[i], ci[1], ci[3], n_events, event_rate))
        }, error = function(e) {
          cat("Error calculating AUC for sector", s, ":", e$message, "\n")
        })
      }
      
      # Calculate Harrell's C-index (concordance) using survival package
      tryCatch({
        # Create a simple Cox model for this sector only
        sector_formula <- as.formula(paste0("Surv(tstop, ", event_type, ") ~ risk_score"))
        sector_model <- coxph(sector_formula, data = sector_data)
        sector_perf$C_Index[i] <- sector_model$concordance["concordance"]
      }, error = function(e) {
        cat("Error calculating C-index for sector", s, ":", e$message, "\n")
      })
    } else {
      cat("Insufficient events in sector", s, "for AUC calculation (", n_events, "events)\n")
    }
  }
  
  # Sort by number of companies
  sector_perf <- sector_perf[order(sector_perf$Companies, decreasing = TRUE), ]
  
  # Print summary statistics
  cat("\nPerformance of", model_name, "model by industry sector:\n")
  print(sector_perf)
  
  # Create visualization of performance by sector
  # Filter sectors with valid AUC
  plot_data <- sector_perf[!is.na(sector_perf$AUC), ]
  
  if(nrow(plot_data) > 0) {
    # Sort by AUC for visualization
    plot_data <- plot_data[order(plot_data$AUC, decreasing = TRUE), ]
    
    # Create sector labels
    sector_labels <- paste0("Sector ", plot_data$Sector, 
                           " (n=", plot_data$Companies, ", ", 
                           plot_data$Events, " events)")
    
    # Set up plotting area with extra space for labels
    par(mar = c(5, 12, 4, 4) + 0.1)
    
    # Create horizontal bar plot of AUC values
    barplot_result <- barplot(plot_data$AUC, 
                            names.arg = sector_labels,
                            main = paste(model_name, "Model Performance by Industry Sector"),
                            xlab = "AUC", 
                            horiz = TRUE, 
                            xlim = c(0.5, 1),
                            col = colorRampPalette(c("skyblue", "royalblue", "navyblue"))(nrow(plot_data)),
                            las = 1,
                            cex.names = 0.7)
    
    # Add event rate as text
    for(i in 1:length(barplot_result)) {
      text(plot_data$AUC[i] + 0.03, barplot_result[i], 
           sprintf("%.3f (%.1f%% events)", 
                 plot_data$AUC[i], plot_data$Event_Rate[i]), 
           cex = 0.7)
    }
    
    # Add reference lines
    abline(v = c(0.5, 0.7, 0.8, 0.9), lty = c(1, 2, 2, 2), 
           col = c("red", "orange", "green3", "darkgreen"))
    
    # Add legend for reference lines
    legend("bottomright", 
           legend = c("AUC = 0.5 (Random)", "AUC = 0.7 (Acceptable)", 
                     "AUC = 0.8 (Excellent)", "AUC = 0.9 (Outstanding)"),
           lty = c(1, 2, 2, 2), 
           col = c("red", "orange", "green3", "darkgreen"),
           cex = 0.7, bty = "n")
    
    # Reset plotting parameters
    par(mar = c(5, 4, 4, 2) + 0.1)
    
    # Create bubble plot of AUC vs event rate, with bubble size representing number of companies
    plot(plot_data$Event_Rate, plot_data$AUC,
         main = paste("AUC vs Event Rate by Sector -", model_name, "Model"),
         xlab = "Event Rate (%)", ylab = "AUC",
         xlim = c(0, max(plot_data$Event_Rate) * 1.1),
         ylim = c(min(0.5, min(plot_data$AUC, na.rm=TRUE)), 1),
         type = "n")
    
    # Add reference line at AUC = 0.5 (random prediction)
    abline(h = 0.5, lty = 2, col = "red")
    abline(h = c(0.7, 0.8, 0.9), lty = 3, col = "darkgray")
    
    # Add grid for readability
    grid()
    
    # Add sector bubbles
    symbols(plot_data$Event_Rate, plot_data$AUC, 
           circles = sqrt(plot_data$Companies) / 5,  # Scale bubble size
           inches = FALSE, 
           bg = adjustcolor("royalblue", alpha.f = 0.7),
           fg = "black",
           add = TRUE)
    
    # Add sector labels
    text(plot_data$Event_Rate, plot_data$AUC, 
         labels = plot_data$Sector, 
         pos = 3, cex = 0.7)
    
    # Add interpretation labels
    text(x = max(plot_data$Event_Rate) * 0.9, y = 0.55, 
         "Poor Performance", col = "red", cex = 0.8)
    text(x = max(plot_data$Event_Rate) * 0.9, y = 0.75, 
         "Good Performance", col = "darkgreen", cex = 0.8)
    text(x = max(plot_data$Event_Rate) * 0.9, y = 0.95, 
         "Excellent Performance", col = "darkgreen", cex = 0.8, font = 2)
    
    # Add bubble size legend
    legend_sizes <- c(min(plot_data$Companies), 
                      median(plot_data$Companies), 
                      max(plot_data$Companies))
    
    legend("bottomright", 
           legend = paste0(round(legend_sizes), " companies"),
           pt.cex = sqrt(legend_sizes) / 5 * 2,  # Adjust for better visibility
           pch = 21, 
           col = "black", 
           pt.bg = adjustcolor("royalblue", alpha.f = 0.7),
           cex = 0.7, bty = "n", 
           title = "Bubble Size Legend")
  } else {
    cat("No sectors with valid AUC values for plotting.\n")
  }
  
  # Identify best and worst performing sectors
  valid_sectors <- sector_perf[!is.na(sector_perf$AUC) & sector_perf$Events >= 5, ]
  if(nrow(valid_sectors) > 0) {
    best_sector <- valid_sectors[which.max(valid_sectors$AUC), ]
    worst_sector <- valid_sectors[which.min(valid_sectors$AUC), ]
    
    cat("\nSector with best model performance:", best_sector$Sector, 
        "(AUC =", round(best_sector$AUC, 3), "with", best_sector$Events, 
        "events, event rate =", round(best_sector$Event_Rate, 1), "%)\n")
    
    cat("Sector with worst model performance:", worst_sector$Sector, 
        "(AUC =", round(worst_sector$AUC, 3), "with", worst_sector$Events, 
        "events, event rate =", round(worst_sector$Event_Rate, 1), "%)\n\n")
    
    # Provide potential explanation for performance differences
    cat("Potential explanations for performance differences:\n")
    
    # Check if event rate correlates with performance
    cor_rate_auc <- cor(valid_sectors$Event_Rate, valid_sectors$AUC, use = "complete.obs")
    
    if(abs(cor_rate_auc) > 0.5) {
      if(cor_rate_auc > 0) {
        cat("- Sectors with higher event rates tend to have better model performance\n")
        cat("  This suggests the model works best in high-risk industries\n")
      } else {
        cat("- Sectors with lower event rates tend to have better model performance\n")
        cat("  This suggests the model works best in more stable industries\n")
      }
    } else {
      cat("- No strong relationship between event rates and model performance across sectors\n")
      cat("  Sector-specific factors beyond frequency of events affect predictability\n")
    }
    
    # Compare best and worst sectors
    cat("\nComparison of best vs. worst performing sectors:\n")
    cat("- Best (Sector", best_sector$Sector, "): AUC =", round(best_sector$AUC, 3), 
        "with event rate of", round(best_sector$Event_Rate, 1), "%\n")
    cat("- Worst (Sector", worst_sector$Sector, "): AUC =", round(worst_sector$AUC, 3), 
        "with event rate of", round(worst_sector$Event_Rate, 1), "%\n")
    
    # Compare number of companies
    if(best_sector$Companies > worst_sector$Companies * 2) {
      cat("- The best performing sector has significantly more companies,\n")
      cat("  suggesting more data improves prediction accuracy\n")
    } else if(worst_sector$Companies > best_sector$Companies * 2) {
      cat("- The worst performing sector has more companies but poorer performance,\n")
      cat("  suggesting structural differences in predictability rather than data volume issues\n")
    }
  }
  
  # Return sector performance data
  return(sector_perf)
}

# Evaluate model performance by sector for both models
bankruptcy_sector_perf <- evaluate_model_by_sector(final_bankruptcy_model, "Bankruptcy", "bankruptcy")
Evaluating Bankruptcy model performance by industry sector...
Sector 10: AUC = 0.846 (95% CI: 0.770-0.922) with 16 events (6.6%)
Sector 15: AUC = 0.860 (95% CI: 0.762-0.958) with 6 events (3.3%)
Sector 20: AUC = 0.846 (95% CI: 0.776-0.915) with 24 events (3.3%)
Sector 25: AUC = 0.812 (95% CI: 0.765-0.859) with 58 events (6.3%)
Sector 30: AUC = 0.792 (95% CI: 0.583-1.000) with 7 events (3.3%)
Sector 35: AUC = 0.715 (95% CI: 0.609-0.821) with 22 events (2.1%)
Sector 45: AUC = 0.823 (95% CI: 0.756-0.891) with 35 events (2.5%)
Sector 50: AUC = 0.886 (95% CI: 0.811-0.961) with 8 events (3.0%)
Insufficient events in sector 55 for AUC calculation ( 0 events)
Insufficient events in sector 60 for AUC calculation ( 0 events)

Performance of Bankruptcy model by industry sector:


Sector with best model performance: 50 (AUC = 0.886 with 8 events, event rate = 3 %)
Sector with worst model performance: 35 (AUC = 0.715 with 22 events, event rate = 2.1 %)

Potential explanations for performance differences:
- No strong relationship between event rates and model performance across sectors
  Sector-specific factors beyond frequency of events affect predictability

Comparison of best vs. worst performing sectors:
- Best (Sector 50 ): AUC = 0.886 with event rate of 3 %
- Worst (Sector 35 ): AUC = 0.715 with event rate of 2.1 %
- The worst performing sector has more companies but poorer performance,
  suggesting structural differences in predictability rather than data volume issues

acquisition_sector_perf <- evaluate_model_by_sector(final_acquisition_model, "Acquisition", "acquisition")
Evaluating Acquisition model performance by industry sector...
Sector 10: AUC = 0.555 (95% CI: 0.482-0.627) with 123 events (50.4%)
Sector 15: AUC = 0.541 (95% CI: 0.456-0.626) with 67 events (36.8%)
Sector 20: AUC = 0.505 (95% CI: 0.463-0.547) with 266 events (36.6%)
Sector 25: AUC = 0.553 (95% CI: 0.516-0.590) with 349 events (38.1%)
Sector 30: AUC = 0.529 (95% CI: 0.452-0.605) with 80 events (37.4%)
Sector 35: AUC = 0.538 (95% CI: 0.503-0.573) with 446 events (43.0%)
Sector 45: AUC = 0.528 (95% CI: 0.497-0.559) with 724 events (52.0%)
Sector 50: AUC = 0.546 (95% CI: 0.476-0.615) with 83 events (31.2%)
Sector 55: AUC = 0.765 (95% CI: 0.599-0.931) with 13 events (40.6%)
Insufficient events in sector 60 for AUC calculation ( 3 events)

Performance of Acquisition model by industry sector:


Sector with best model performance: 55 (AUC = 0.765 with 13 events, event rate = 40.6 %)
Sector with worst model performance: 20 (AUC = 0.505 with 266 events, event rate = 36.6 %)

Potential explanations for performance differences:
- No strong relationship between event rates and model performance across sectors
  Sector-specific factors beyond frequency of events affect predictability

Comparison of best vs. worst performing sectors:
- Best (Sector 55 ): AUC = 0.765 with event rate of 40.6 %
- Worst (Sector 20 ): AUC = 0.505 with event rate of 36.6 %
- The worst performing sector has more companies but poorer performance,
  suggesting structural differences in predictability rather than data volume issues

# Compare sector performance between models
if(!is.null(bankruptcy_sector_perf) && !is.null(acquisition_sector_perf)) {
  cat("\nCOMPARING MODEL PERFORMANCE ACROSS SECTORS:\n")
  cat("----------------------------------------\n")
  
  # Find common sectors with valid performance metrics
  common_sectors <- intersect(
    bankruptcy_sector_perf$Sector[!is.na(bankruptcy_sector_perf$AUC)],
    acquisition_sector_perf$Sector[!is.na(acquisition_sector_perf$AUC)]
  )
  
  if(length(common_sectors) > 0) {
    comparison <- data.frame(
      Sector = common_sectors,
      Bankruptcy_AUC = NA,
      Acquisition_AUC = NA,
      Difference = NA,
      stringsAsFactors = FALSE
    )
    
    # Fill in values
    for(i in 1:length(common_sectors)) {
      s <- common_sectors[i]
      comparison$Bankruptcy_AUC[i] <- bankruptcy_sector_perf$AUC[bankruptcy_sector_perf$Sector == s]
      comparison$Acquisition_AUC[i] <- acquisition_sector_perf$AUC[acquisition_sector_perf$Sector == s]
      comparison$Difference[i] <- comparison$Bankruptcy_AUC[i] - comparison$Acquisition_AUC[i]
    }
    
    # Sort by absolute difference
    comparison <- comparison[order(abs(comparison$Difference), decreasing = TRUE), ]
    
    # Print comparison
    cat("Performance comparison across", length(common_sectors), "sectors with valid metrics:\n\n")
    print(comparison)
    
    # Identify sectors with largest differences
    if(nrow(comparison) > 0) {
      max_diff_sector <- comparison[which.max(abs(comparison$Difference)), ]
      
      cat("\nSector with largest performance difference:", max_diff_sector$Sector, "\n")
      cat("- Bankruptcy model AUC:", round(max_diff_sector$Bankruptcy_AUC, 3), "\n")
      cat("- Acquisition model AUC:", round(max_diff_sector$Acquisition_AUC, 3), "\n")
      cat("- Absolute difference:", round(abs(max_diff_sector$Difference), 3), "\n")
      
      if(max_diff_sector$Bankruptcy_AUC > max_diff_sector$Acquisition_AUC) {
        cat("- Bankruptcy is more predictable than acquisition in this sector\n")
      } else {
        cat("- Acquisition is more predictable than bankruptcy in this sector\n")
      }
      
      # Create scatter plot comparing performance across sectors
      plot(comparison$Bankruptcy_AUC, comparison$Acquisition_AUC,
           main = "Model Performance Comparison by Sector",
           xlab = "Bankruptcy Model AUC", ylab = "Acquisition Model AUC",
           xlim = c(0.5, 1), ylim = c(0.5, 1),
           pch = 16, col = "blue")
      
      # Add sector labels
      text(comparison$Bankruptcy_AUC, comparison$Acquisition_AUC, 
           labels = comparison$Sector, pos = 3, cex = 0.7)
      
      # Add diagonal line for equal performance
      abline(0, 1, lty = 2, col = "gray")
      
      # Add quadrant labels
      text(0.55, 0.95, "Acquisition better\npredicted", col = "darkred", cex = 0.8)
      text(0.95, 0.55, "Bankruptcy better\npredicted", col = "darkblue", cex = 0.8)
      text(0.95, 0.95, "Both well\npredicted", col = "darkgreen", cex = 0.8)
      text(0.55, 0.55, "Both poorly\npredicted", col = "purple", cex = 0.8)
      
      # Add reference lines
      abline(h = 0.7, v = 0.7, lty = 3, col = "darkgray")
      
      # Calculate average performance difference
      avg_diff <- mean(comparison$Bankruptcy_AUC - comparison$Acquisition_AUC)
      
      if(abs(avg_diff) > 0.03) {
        if(avg_diff > 0) {
          cat("\nOn average, bankruptcy is more predictable across sectors (by", 
              round(avg_diff, 3), "AUC points)\n")
        } else {
          cat("\nOn average, acquisition is more predictable across sectors (by", 
              round(-avg_diff, 3), "AUC points)\n")
        }
      } else {
        cat("\nOn average, both outcomes have similar predictability across sectors\n")
      }
      
      # Calculate correlation between sector performance on both models
      perf_cor <- cor(comparison$Bankruptcy_AUC, comparison$Acquisition_AUC)
      
      cat("Correlation between bankruptcy and acquisition prediction performance:", 
          round(perf_cor, 3), "\n")
      
      if(perf_cor > 0.7) {
        cat("The strong positive correlation suggests sectors that are predictable for one\n")
        cat("outcome tend to be predictable for the other as well. This indicates similar\n")
        cat("underlying drivers of predictability across sectors.\n")
      } else if(perf_cor > 0.3) {
        cat("The moderate correlation suggests some common factors affecting predictability\n")
        cat("across outcomes, but also important differences in what makes sectors predictable\n")
        cat("for bankruptcy versus acquisition.\n")
      } else {
        cat("The weak correlation suggests different factors drive predictability for\n")
        cat("bankruptcy versus acquisition across sectors. Sectors predictable for one\n")
        cat("outcome aren't necessarily predictable for the other.\n")
      }
    }
  } else {
    cat("No common sectors with valid performance metrics for both models.\n")
  }
}

COMPARING MODEL PERFORMANCE ACROSS SECTORS:
----------------------------------------
Performance comparison across 8 sectors with valid metrics:

Sector with largest performance difference: 20 
- Bankruptcy model AUC: 0.846 
- Acquisition model AUC: 0.505 
- Absolute difference: 0.341 
- Bankruptcy is more predictable than acquisition in this sector

On average, bankruptcy is more predictable across sectors (by 0.286 AUC points)
Correlation between bankruptcy and acquisition prediction performance: 0.055 
The weak correlation suggests different factors drive predictability for
bankruptcy versus acquisition across sectors. Sectors predictable for one
outcome aren't necessarily predictable for the other.

Again, not sure if really necessary ^^^^^^^^^^^^^^^

Next we do an enhanced risk stratification analysis. This will help us understand how the model’s predictions vary across different risk levels and whether there are significant differences in event rates by risk level.

#-------------------------------------------------------------
# 13.15: ENHANCED RISK STRATIFICATION ANALYSIS (FIXED)
#-------------------------------------------------------------
cat("\n13.15: ENHANCED RISK STRATIFICATION ANALYSIS\n")

13.15: ENHANCED RISK STRATIFICATION ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
# Function to create comprehensive risk stratification with macroeconomic context
create_risk_stratification <- function(model, model_name, event_type_name) {
  cat("Creating enhanced risk stratification for", model_name, "model...\n")
  
  # Get last observation for each company
  final_obs <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate risk scores
  risk_scores <- predict(model, newdata = final_obs, type = "risk")
  
  # Add scores to data
  final_obs$risk_score <- risk_scores
  
  # Create risk quintiles for more granular analysis
  risk_quantiles <- quantile(risk_scores, probs = seq(0, 1, 0.2))
  final_obs$risk_quintile <- cut(risk_scores, 
                               breaks = risk_quantiles, 
                               include.lowest = TRUE, 
                               labels = c("Q1 (Lowest)", "Q2", "Q3", "Q4", "Q5 (Highest)"))
  
  # Get the event column directly (don't use [[ ]] with a variable)
  if(event_type_name == "bankruptcy") {
    event_col <- final_obs$bankruptcy
  } else if(event_type_name == "acquisition") {
    event_col <- final_obs$acquisition
  } else {
    stop("Unknown event type: ", event_type_name)
  }
  
  # Calculate descriptive statistics by quintile
  quintile_stats <- aggregate(event_col ~ risk_quintile, data = final_obs, 
                            FUN = function(x) c(mean = mean(x), sum = sum(x), n = length(x)))
  
  # Format the data for easier use
  quintile_summary <- data.frame(
    Quintile = quintile_stats$risk_quintile,
    Events = quintile_stats$event_col[, "sum"],
    Companies = quintile_stats$event_col[, "n"],
    Event_Rate = quintile_stats$event_col[, "mean"] * 100,
    stringsAsFactors = FALSE
  )
  
  # Calculate additional metrics
  quintile_summary$Cumulative_Events <- cumsum(quintile_summary$Events)
  quintile_summary$Cumulative_Percent <- quintile_summary$Cumulative_Events / sum(quintile_summary$Events) * 100
  quintile_summary$Capture_Rate <- quintile_summary$Events / quintile_summary$Companies * 100
  
  # Calculate relative risk versus lowest quintile
  base_rate <- quintile_summary$Event_Rate[1]
  quintile_summary$Relative_Risk <- quintile_summary$Event_Rate / base_rate
  
  # Print summary table
  cat("\nRisk Stratification Summary for", model_name, "Model:\n")
  print(quintile_summary)
  
  # Calculate discrimination ratio (highest quintile rate / lowest quintile rate)
  discrimination_ratio <- quintile_summary$Event_Rate[5] / quintile_summary$Event_Rate[1]
  
  # Create bar plot of event rates
  barplot_result <- barplot(quintile_summary$Event_Rate, 
                          names.arg = quintile_summary$Quintile,
                          main = paste("Event Rates by Risk Quintile -", model_name, "Model"),
                          xlab = "Risk Quintile", ylab = "Event Rate (%)",
                          col = colorRampPalette(c("green", "yellow", "orange", "red", "darkred"))(5),
                          ylim = c(0, max(quintile_summary$Event_Rate) * 1.3))
  
  # Add text labels with event counts
  text(barplot_result, 
       quintile_summary$Event_Rate + max(quintile_summary$Event_Rate) * 0.05, 
       paste0(round(quintile_summary$Event_Rate, 1), "%\n(", 
              quintile_summary$Events, "/", quintile_summary$Companies, ")"), 
       cex = 0.8)
  
  # Add discrimination ratio
  mtext(paste("Discrimination Ratio (Q5/Q1):", round(discrimination_ratio, 1), "×"), 
        side = 3, line = 0.5, cex = 0.8)
  
  # Create Lorenz curve for event concentration
  # Calculate cumulative percentage of companies and events
  cum_pct_companies <- cumsum(quintile_summary$Companies) / sum(quintile_summary$Companies) * 100
  cum_pct_events <- cumsum(quintile_summary$Events) / sum(quintile_summary$Events) * 100
  
  # Points for Lorenz curve
  lorenz_x <- c(0, cum_pct_companies)
  lorenz_y <- c(0, cum_pct_events)
  
  # Create Lorenz curve plot
  plot(lorenz_x, lorenz_y, type = "b", pch = 16, col = "blue",
       main = paste("Event Concentration Curve -", model_name, "Model"),
       xlab = "Cumulative % of Companies (sorted by risk score)",
       ylab = "Cumulative % of Events",
       xlim = c(0, 100), ylim = c(0, 100))
  
  # Add reference line for random prediction
  abline(0, 1, lty = 2, col = "darkgray")
  
  # Add grid for readability
  grid()
  
  # Add quintile labels
  text(cum_pct_companies[2:6], cum_pct_events[2:6], 
       labels = c("Q1", "Q2", "Q3", "Q4", "Q5"), 
       pos = 4, cex = 0.8)
  
  # Calculate Gini coefficient (area between Lorenz curve and diagonal, normalized)
  # Approximate using trapezoid rule
  gini <- 0
  for(i in 1:5) {
    gini <- gini + (lorenz_x[i+1] - lorenz_x[i]) * (lorenz_y[i+1] + lorenz_y[i])
  }
  gini <- 1 - gini/10000
  
  # Add Gini coefficient to plot
  mtext(paste("Gini Coefficient:", round(gini, 3)), side = 3, line = 0.5, cex = 0.8)
  
  # Add practical interpretation
  cat("\nPerformance Interpretation:\n")
  cat("- Discrimination Ratio (Q5/Q1):", round(discrimination_ratio, 1), "×\n")
  
  if(discrimination_ratio >= 10) {
    cat("  Excellent discrimination (>10×)\n")
  } else if(discrimination_ratio >= 5) {
    cat("  Good discrimination (5-10×)\n")
  } else if(discrimination_ratio >= 3) {
    cat("  Moderate discrimination (3-5×)\n")
  } else {
    cat("  Limited discrimination (<3×)\n")
  }
  
  cat("- Gini Coefficient:", round(gini, 3), "\n")
  
  if(gini >= 0.7) {
    cat("  Excellent concentration of events (≥0.7)\n")
  } else if(gini >= 0.5) {
    cat("  Good concentration of events (0.5-0.7)\n")
  } else if(gini >= 0.3) {
    cat("  Moderate concentration of events (0.3-0.5)\n")
  } else {
    cat("  Limited concentration of events (<0.3)\n")
  }
  
  # Top quintile capture rate
  top_quintile_capture <- quintile_summary$Events[5] / sum(quintile_summary$Events) * 100
  cat("- Top quintile capture rate:", round(top_quintile_capture, 1), 
      "% of all events captured in highest risk quintile\n")
  
  # Analyze macroeconomic influence on risk stratification
  cat("\nMacroeconomic Context of Risk Stratification:\n")
  
  # Check if macro variables are present
  macro_vars <- c("gdp_growth", "unemployement", "gdp_deflator")
  available_macro <- intersect(macro_vars, colnames(final_obs))
  
  if(length(available_macro) > 0) {
    # Calculate average macro values by quintile
    macro_by_quintile <- aggregate(final_obs[, available_macro], 
                               by = list(Quintile = final_obs$risk_quintile), 
                               FUN = mean, na.rm = TRUE)
    
    cat("\nAverage Macroeconomic Conditions by Risk Quintile:\n")
    print(macro_by_quintile)
    
    # Create parallel plots to visualize relationship
    # Set up plotting area
    par(mar = c(5, 5, 4, 5) + 0.1)
    
    # Calculate mean and SD for standardizing
    means <- colMeans(final_obs[, available_macro], na.rm = TRUE)
    sds <- apply(final_obs[, available_macro], 2, sd, na.rm = TRUE)
    
    # Standardize values for plotting
    std_values <- matrix(NA, nrow = 5, ncol = length(available_macro))
    for(i in 1:length(available_macro)) {
      std_values[, i] <- (macro_by_quintile[, i+1] - means[i]) / sds[i]
    }
    
    # Set plot limits
    y_lim <- c(min(-2, min(std_values, na.rm = TRUE)), 
               max(2, max(std_values, na.rm = TRUE)))
    
    # Create plot
    plot(1:5, std_values[, 1], type = "l", lwd = 2, col = "blue",
         main = "Macroeconomic Profile by Risk Quintile",
         xlab = "Risk Quintile (Low to High)",
         ylab = "Standardized Value (Z-score)",
         xlim = c(1, 5), ylim = y_lim,
         xaxt = "n")
    
    # Add axis
    axis(1, at = 1:5, labels = c("Q1\n(Lowest)", "Q2", "Q3", "Q4", "Q5\n(Highest)"))
    
    # Add lines for other macro variables
    if(length(available_macro) > 1) {
      for(i in 2:length(available_macro)) {
        lines(1:5, std_values[, i], lwd = 2, 
              col = rainbow(length(available_macro))[i])
      }
    }
    
    # Add reference line at 0
    abline(h = 0, lty = 2, col = "darkgray")
    
    # Add legend
    macro_labels <- c("GDP Growth", "Unemployment", "Inflation")
    legend_labels <- macro_labels[match(available_macro, macro_vars)]
    
    legend("topright", legend = legend_labels, 
           col = rainbow(length(available_macro)), 
           lwd = 2, bty = "n")
    
    # Reset plotting parameters
    par(mar = c(5, 4, 4, 2) + 0.1)
    
    # Calculate correlations between risk scores and macro variables
    cor_risk_macro <- cor(final_obs$risk_score, final_obs[, available_macro], 
                        use = "pairwise.complete.obs")
    
    cat("\nCorrelation between risk scores and macroeconomic variables:\n")
    for(i in 1:length(available_macro)) {
      var <- available_macro[i]
      var_label <- ifelse(var == "gdp_growth", "GDP Growth", 
                        ifelse(var == "unemployement", "Unemployment", "Inflation"))
      
      cor_val <- cor_risk_macro[1, i]
      
      cat("- ", var_label, ": r = ", round(cor_val, 3), " (", 
          ifelse(cor_val > 0, "positive", "negative"), " relationship)\n", sep="")
      
      # Add interpretation
      if(abs(cor_val) > 0.1) {
        if(var == "gdp_growth") {
          if(cor_val > 0) {
            cat("  Higher economic growth associated with higher", model_name, "risk\n")
          } else {
            cat("  Lower economic growth associated with higher", model_name, "risk\n")
          }
        } else if(var == "unemployement") {
          if(cor_val > 0) {
            cat("  Higher unemployment associated with higher", model_name, "risk\n")
          } else {
            cat("  Lower unemployment associated with higher", model_name, "risk\n")
          }
        } else if(var == "gdp_deflator") {
          if(cor_val > 0) {
            cat("  Higher inflation associated with higher", model_name, "risk\n")
          } else {
            cat("  Lower inflation associated with higher", model_name, "risk\n")
          }
        }
      } else {
        cat("  Minimal relationship with", model_name, "risk\n")
      }
    }
    
    # Check for patterns in event rates across macro conditions
    # Create macro condition categories based on GDP growth and unemployment
    if(all(c("gdp_growth", "unemployement") %in% available_macro)) {
      # Define economic conditions
      median_gdp <- median(final_obs$gdp_growth, na.rm = TRUE)
      median_unemp <- median(final_obs$unemployement, na.rm = TRUE)
      
      final_obs$econ_condition <- "Normal"
      final_obs$econ_condition[final_obs$gdp_growth > median_gdp & 
                              final_obs$unemployement < median_unemp] <- "Strong"
      final_obs$econ_condition[final_obs$gdp_growth < median_gdp & 
                              final_obs$unemployement > median_unemp] <- "Weak"
      
      # Calculate event rates by quintile and economic condition
      # Use the appropriate event column based on event_type_name
      if(event_type_name == "bankruptcy") {
        econ_quintile_rates <- aggregate(bankruptcy ~ risk_quintile + econ_condition, 
                                      data = final_obs, FUN = mean)
      } else {
        econ_quintile_rates <- aggregate(acquisition ~ risk_quintile + econ_condition, 
                                      data = final_obs, FUN = mean)
      }
      
      # Reshape to wide format
      econ_quintile_wide <- reshape(econ_quintile_rates, 
                                  idvar = "risk_quintile", 
                                  timevar = "econ_condition", 
                                  direction = "wide")
      
      # Fix column names
      colnames(econ_quintile_wide) <- gsub(paste0(event_type_name, "."), "", 
                                         colnames(econ_quintile_wide))
      
      # Multiply by 100 to get percentages
      for(col in 2:ncol(econ_quintile_wide)) {
        econ_quintile_wide[, col] <- econ_quintile_wide[, col] * 100
      }
      
      cat("\nEvent Rates by Risk Quintile and Economic Condition:\n")
      print(econ_quintile_wide)
      
      # Create interaction plot
      # Use reshape2 package for melt function if available
      if(requireNamespace("reshape2", quietly = TRUE)) {
        # Create data in long format for plotting
        plot_data <- reshape2::melt(econ_quintile_wide, id.vars = "risk_quintile")
        names(plot_data) <- c("Quintile", "Condition", "Rate")
      } else {
        # Fallback if reshape2 is not available
        # Create a manual equivalent of melt for this specific case
        plot_data <- data.frame(
          Quintile = rep(econ_quintile_wide$risk_quintile, each = ncol(econ_quintile_wide) - 1),
          Condition = rep(colnames(econ_quintile_wide)[-1], times = nrow(econ_quintile_wide)),
          Rate = as.vector(unlist(econ_quintile_wide[, -1])),
          stringsAsFactors = FALSE
        )
      }
      
      # Create interaction plot
      interaction.plot(x.factor = plot_data$Quintile, 
                      trace.factor = plot_data$Condition, 
                      response = plot_data$Rate,
                      fun = mean,
                      type = "b",
                      col = c("green3", "blue", "red"),
                      lty = 1,
                      pch = c(15, 16, 17),
                      lwd = 2,
                      legend = TRUE,
                      xlab = "Risk Quintile",
                      ylab = paste(model_name, "Event Rate (%)"),
                      main = "Risk Quintile Performance by Economic Condition")
      
      # Calculate discrimination ratios by economic condition
      conditions <- unique(plot_data$Condition)
      discrim_by_condition <- numeric(length(conditions))
      names(discrim_by_condition) <- conditions
      
      for(cond in conditions) {
        quintile_rates <- tapply(plot_data$Rate[plot_data$Condition == cond], 
                               plot_data$Quintile[plot_data$Condition == cond], mean)
        if(length(quintile_rates) == 5 && !is.na(quintile_rates[1]) && 
           !is.na(quintile_rates[5]) && quintile_rates[1] > 0) {
          discrim_by_condition[cond] <- quintile_rates[5] / quintile_rates[1]
        }
      }
      
      # Add to plot as annotation
      ratio_text <- paste(names(discrim_by_condition), ": ", 
                        round(discrim_by_condition, 1), "×", 
                        collapse = " | ")
      mtext(paste("Discrimination Ratios -", ratio_text), 
            side = 3, line = 0.5, cex = 0.7)
      
      # Discuss implications
      cat("\nMacroeconomic Impact on Risk Stratification:\n")
      
      # Find condition with highest discrimination ratio
      best_condition <- names(which.max(discrim_by_condition))
      max_discrim <- max(discrim_by_condition, na.rm = TRUE)
      
      cat("- Best model discrimination in", best_condition, "economic conditions (ratio =", 
          round(max_discrim, 1), "×)\n")
      
      # Check pattern across conditions
      strong_idx <- which(names(discrim_by_condition) == "Strong")
      weak_idx <- which(names(discrim_by_condition) == "Weak")
      
      if(length(strong_idx) == 0 || length(weak_idx) == 0 || 
         is.na(discrim_by_condition[strong_idx]) || is.na(discrim_by_condition[weak_idx])) {
        cat("- Insufficient data to compare across all economic conditions\n")
      } else if(discrim_by_condition[weak_idx] > discrim_by_condition[strong_idx]) {
        cat("- Model discrimination improves in weak economic conditions\n")
        cat("  This suggests economic downturns make", model_name, "events more predictable\n")
      } else if(discrim_by_condition[strong_idx] > discrim_by_condition[weak_idx]) {
        cat("- Model discrimination improves in strong economic conditions\n")
        cat("  This suggests economic expansions make", model_name, "events more predictable\n")
      } else {
        cat("- Similar discrimination across economic conditions\n")
        cat("  This suggests economic cycles have limited impact on predictability\n")
      }
    }
  } else {
    cat("Macroeconomic variables not available in the dataset.\n")
  }
  
  # Return risk stratification results
  return(list(
    quintile_summary = quintile_summary,
    discrimination_ratio = discrimination_ratio,
    gini = gini,
    top_quintile_capture = top_quintile_capture
  ))
}

# Create risk stratification analyses for both models
bankruptcy_risk_strat <- create_risk_stratification(final_bankruptcy_model, "Bankruptcy", "bankruptcy")
Creating enhanced risk stratification for Bankruptcy model...

Risk Stratification Summary for Bankruptcy Model:


Performance Interpretation:
- Discrimination Ratio (Q5/Q1): 59 ×
  Excellent discrimination (>10×)
- Gini Coefficient: 0.593 
  Good concentration of events (0.5-0.7)
- Top quintile capture rate: 67 % of all events captured in highest risk quintile

Macroeconomic Context of Risk Stratification:

Average Macroeconomic Conditions by Risk Quintile:


Correlation between risk scores and macroeconomic variables:
- GDP Growth: r = -0.018 (negative relationship)
  Minimal relationship with Bankruptcy risk
- Unemployment: r = 0.001 (positive relationship)
  Minimal relationship with Bankruptcy risk
- Inflation: r = -0.074 (negative relationship)
  Minimal relationship with Bankruptcy risk

Event Rates by Risk Quintile and Economic Condition:


Macroeconomic Impact on Risk Stratification:
- Best model discrimination in Normal economic conditions (ratio = 64.9 ×)
- Model discrimination improves in weak economic conditions
  This suggests economic downturns make Bankruptcy events more predictable

acquisition_risk_strat <- create_risk_stratification(final_acquisition_model, "Acquisition", "acquisition")
Creating enhanced risk stratification for Acquisition model...

Risk Stratification Summary for Acquisition Model:


Performance Interpretation:
- Discrimination Ratio (Q5/Q1): 1.1 ×
  Limited discrimination (<3×)
- Gini Coefficient: 0.027 
  Limited concentration of events (<0.3)
- Top quintile capture rate: 16.6 % of all events captured in highest risk quintile

Macroeconomic Context of Risk Stratification:

Average Macroeconomic Conditions by Risk Quintile:


Correlation between risk scores and macroeconomic variables:
- GDP Growth: r = -0.002 (negative relationship)
  Minimal relationship with Acquisition risk
- Unemployment: r = -0.092 (negative relationship)
  Minimal relationship with Acquisition risk
- Inflation: r = 0.143 (positive relationship)
  Higher inflation associated with higher Acquisition risk

Event Rates by Risk Quintile and Economic Condition:


Macroeconomic Impact on Risk Stratification:
- Best model discrimination in Strong economic conditions (ratio = 2.7 ×)
- Model discrimination improves in strong economic conditions
  This suggests economic expansions make Acquisition events more predictable

# Compare risk stratification between models
if(!is.null(bankruptcy_risk_strat) && !is.null(acquisition_risk_strat)) {
  cat("\nCOMPARING RISK STRATIFICATION BETWEEN MODELS:\n")
  cat("-------------------------------------------\n")
  
  # Create comparison table
  comparison <- data.frame(
    Metric = c("Discrimination Ratio (Q5/Q1)", 
               "Gini Coefficient", 
               "Top Quintile Capture Rate"),
    Bankruptcy = c(bankruptcy_risk_strat$discrimination_ratio,
                  bankruptcy_risk_strat$gini,
                  bankruptcy_risk_strat$top_quintile_capture),
    Acquisition = c(acquisition_risk_strat$discrimination_ratio,
                   acquisition_risk_strat$gini,
                   acquisition_risk_strat$top_quintile_capture),
    stringsAsFactors = FALSE
  )
  
  comparison$Difference <- comparison$Bankruptcy - comparison$Acquisition
  comparison$Pct_Difference <- comparison$Difference / comparison$Acquisition * 100
  
  # Format for display
  formatted_comparison <- comparison
  formatted_comparison$Bankruptcy <- round(formatted_comparison$Bankruptcy, 3)
  formatted_comparison$Acquisition <- round(formatted_comparison$Acquisition, 3)
  formatted_comparison$Difference <- round(formatted_comparison$Difference, 3)
  formatted_comparison$Pct_Difference <- round(formatted_comparison$Pct_Difference, 1)
  
  print(formatted_comparison)
  
  # Interpret differences
  cat("\nInterpretation of Model Differences:\n")
  
  # Compare discrimination ratios
  if(comparison$Bankruptcy[1] > comparison$Acquisition[1] * 1.25) {
    cat("- The bankruptcy model has significantly better discrimination between\n")
    cat("  high and low risk companies (", round(comparison$Pct_Difference[1], 1), "% higher)\n", sep="")
  } else if(comparison$Acquisition[1] > comparison$Bankruptcy[1] * 1.25) {
    cat("- The acquisition model has significantly better discrimination between\n")
    cat("  high and low risk companies (", round(-comparison$Pct_Difference[1], 1), "% higher)\n", sep="")
  } else {
    cat("- Both models have similar discrimination between high and low risk companies\n")
  }
  
  # Compare Gini coefficients
  if(comparison$Bankruptcy[2] > comparison$Acquisition[2] * 1.15) {
    cat("- The bankruptcy model has better event concentration (higher Gini coefficient)\n")
  } else if(comparison$Acquisition[2] > comparison$Bankruptcy[2] * 1.15) {
    cat("- The acquisition model has better event concentration (higher Gini coefficient)\n")
  } else {
    cat("- Both models have similar event concentration patterns\n")
  }
  
  # Compare capture rates
  if(comparison$Bankruptcy[3] > comparison$Acquisition[3] * 1.15) {
    cat("- The bankruptcy model captures more events in its highest risk quintile\n")
  } else if(comparison$Acquisition[3] > comparison$Bankruptcy[3] * 1.15) {
    cat("- The acquisition model captures more events in its highest risk quintile\n")
  } else {
    cat("- Both models capture a similar proportion of events in their highest risk quintile\n")
  }
  
  # Overall conclusion
  if(sum(comparison$Bankruptcy) > sum(comparison$Acquisition) * 1.15) {
    cat("\nOverall, the bankruptcy model provides better risk stratification,\n")
    cat("suggesting bankruptcy events are more predictable than acquisitions.\n")
  } else if(sum(comparison$Acquisition) > sum(comparison$Bankruptcy) * 1.15) {
    cat("\nOverall, the acquisition model provides better risk stratification,\n")
    cat("suggesting acquisition events are more predictable than bankruptcies.\n")
  } else {
    cat("\nOverall, both models provide comparable risk stratification,\n")
    cat("suggesting similar levels of predictability for both types of events.\n")
  }
}

COMPARING RISK STRATIFICATION BETWEEN MODELS:
-------------------------------------------

Interpretation of Model Differences:
- The bankruptcy model has significantly better discrimination between
  high and low risk companies (5205% higher)
- The bankruptcy model has better event concentration (higher Gini coefficient)
- The bankruptcy model captures more events in its highest risk quintile

Overall, the bankruptcy model provides better risk stratification,
suggesting bankruptcy events are more predictable than acquisitions.

Not entirely sure of this application here but its an innovative approach (still not entirely correct here). Basically we are using the Cox model predictions to rank companies by risk. This is a common practice in survival analysis, where we want to identify which companies are at higher risk of experiencing an event (like bankruptcy or acquisition) based on their characteristics and the time until the event occurs. I think its innovative because i havent seen it in any of the research, it also allows us to kinda apply our model IRL.

#-------------------------------------------------------------
# STEP 14: Ranking Companies by Risk (Using Cox Model Predictions)
#-------------------------------------------------------------
cat("\n=== STEP 14: RANKING COMPANIES BY RISK USING COX MODEL PREDICTIONS ===\n")

=== STEP 14: RANKING COMPANIES BY RISK USING COX MODEL PREDICTIONS ===
#-------------------------------------------------------------
# 14.1: Create Risk Ranking Function
#-------------------------------------------------------------
cat("\n14.1: CREATING RISK RANKINGS BASED ON PREDICTED HAZARDS\n")

14.1: CREATING RISK RANKINGS BASED ON PREDICTED HAZARDS
cat("--------------------------------------\n")
--------------------------------------
# Function to rank companies by risk using Cox models
rank_companies_by_risk <- function(bankruptcy_model, acquisition_model, top_n = 20) {
  cat("Ranking companies by risk using Cox model predictions...\n")
  
  # Get the latest observation for each company
  latest_obs <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate company age (time since first observation)
  company_age <- latest_obs$tstop
  
  # Calculate risk scores from both models
  bankruptcy_risk <- predict(bankruptcy_model, newdata = latest_obs, type = "risk")
  acquisition_risk <- predict(acquisition_model, newdata = latest_obs, type = "risk")
  
  # Add scores to data
  latest_obs$bankruptcy_risk <- bankruptcy_risk
  latest_obs$acquisition_risk <- acquisition_risk
  
  # Calculate inverse survival probability (1 - exp(-risk))
  # This gives an estimation of event probability
  latest_obs$bankruptcy_prob <- 1 - exp(-bankruptcy_risk)
  latest_obs$acquisition_prob <- 1 - exp(-acquisition_risk)
  
  # Calculate survival probability for both events combined
  latest_obs$overall_survival_prob <- exp(-(bankruptcy_risk + acquisition_risk))
  
  # Calculate conditional probabilities (given that an event happens)
  denominator <- bankruptcy_risk + acquisition_risk
  latest_obs$bankruptcy_cond_prob <- ifelse(denominator > 0, 
                                         bankruptcy_risk / denominator, 0)
  latest_obs$acquisition_cond_prob <- ifelse(denominator > 0, 
                                          acquisition_risk / denominator, 0)
  
  # Create combined risk score (weighted sum of both risks)
  latest_obs$combined_risk <- bankruptcy_risk * 0.6 + acquisition_risk * 0.4  # Example weights
  
  # Calculate time-adjusted probabilities (higher risk for shorter time periods)
  max_time <- max(company_age)
  time_factor <- (max_time - company_age) / max_time
  latest_obs$time_adj_bankruptcy_prob <- latest_obs$bankruptcy_prob * (1 + time_factor)
  latest_obs$time_adj_acquisition_prob <- latest_obs$acquisition_prob * (1 + time_factor)
  
  # Select key variables for the ranking
  risk_ranking <- latest_obs %>%
    select(cusip, gsector, bankruptcy_risk, acquisition_risk, bankruptcy_prob, 
           acquisition_prob, combined_risk, overall_survival_prob,
           bankruptcy_cond_prob, acquisition_cond_prob)
  
  # Add company financial information for context
  key_financials <- c("LTMTA", "NIMTA", "CASHMTA", "PRICE", "z_score")
  risk_ranking <- cbind(risk_ranking, latest_obs[, intersect(key_financials, colnames(latest_obs))])
  
  # Add macroeconomic context for the latest observation
  key_macro <- c("gdp_growth", "unemployement", "gdp_deflator")
  risk_ranking <- cbind(risk_ranking, latest_obs[, intersect(key_macro, colnames(latest_obs))])
  
  # Rank companies for bankruptcy risk
  cat("\nRanking companies by bankruptcy risk...\n")
  bankruptcy_ranking <- risk_ranking %>%
    arrange(desc(bankruptcy_risk))
  
  # Rank companies for acquisition risk
  cat("Ranking companies by acquisition risk...\n")
  acquisition_ranking <- risk_ranking %>%
    arrange(desc(acquisition_risk))
  
  # Create a "safe investments" ranking (low risk of both events)
  cat("Ranking companies by overall survival probability (safe investments)...\n")
  safe_ranking <- risk_ranking %>%
    arrange(desc(overall_survival_prob))
  
  # Print top companies by bankruptcy risk
  cat("\nTop", top_n, "Companies with Highest Bankruptcy Risk:\n")
  cat("------------------------------------------------\n")
  top_bankruptcy <- head(bankruptcy_ranking, top_n)
  # Format probabilities as percentages
  top_bankruptcy$bankruptcy_prob <- round(top_bankruptcy$bankruptcy_prob * 100, 1)
  print(top_bankruptcy[, c("cusip", "gsector", "bankruptcy_risk", "bankruptcy_prob", 
                           intersect(c("LTMTA", "NIMTA", "z_score"), colnames(top_bankruptcy)))])
  
  # Print top companies by acquisition risk
  cat("\nTop", top_n, "Companies with Highest Acquisition Risk:\n")
  cat("-----------------------------------------------\n")
  top_acquisition <- head(acquisition_ranking, top_n)
  # Format probabilities as percentages
  top_acquisition$acquisition_prob <- round(top_acquisition$acquisition_prob * 100, 1)
  print(top_acquisition[, c("cusip", "gsector", "acquisition_risk", "acquisition_prob", 
                            intersect(c("PRICE", "CASHMTA", "MBE"), colnames(top_acquisition)))])
  
  # Print top safe investment options
  cat("\nTop", top_n, "Safest Companies (Lowest Combined Risk):\n")
  cat("----------------------------------------------\n")
  top_safe <- head(safe_ranking, top_n)
  # Format survival probability as percentage
  top_safe$overall_survival_prob <- round(top_safe$overall_survival_prob * 100, 1)
  print(top_safe[, c("cusip", "gsector", "overall_survival_prob", "bankruptcy_risk", 
                      "acquisition_risk")])
  
  # Return the full rankings
  return(list(
    bankruptcy_ranking = bankruptcy_ranking,
    acquisition_ranking = acquisition_ranking,
    safe_ranking = safe_ranking
  ))
}
#-------------------------------------------------------------
# 14.2: Apply Risk Ranking 
#-------------------------------------------------------------
cat("\n14.2: APPLYING RISK RANKING TO COMPANIES IN THE DATASET\n")

14.2: APPLYING RISK RANKING TO COMPANIES IN THE DATASET
cat("--------------------------------------\n")
--------------------------------------
# Execute the ranking function on the models
company_rankings <- rank_companies_by_risk(final_bankruptcy_model, final_acquisition_model)
Ranking companies by risk using Cox model predictions...

Ranking companies by bankruptcy risk...
Ranking companies by acquisition risk...
Ranking companies by overall survival probability (safe investments)...

Top 20 Companies with Highest Bankruptcy Risk:
------------------------------------------------

Top 20 Companies with Highest Acquisition Risk:
-----------------------------------------------

Top 20 Safest Companies (Lowest Combined Risk):
----------------------------------------------
# Get sector-specific risk rankings
rank_by_sector <- function(bankruptcy_ranking, acquisition_ranking) {
  cat("\nIdentifying highest-risk companies by sector:\n")
  cat("-------------------------------------------\n")
  
  # Get unique sectors
  sectors <- unique(bankruptcy_ranking$gsector)
  
  for(s in sectors) {
    # Skip sectors with too few companies
    sector_companies <- sum(bankruptcy_ranking$gsector == s)
    if(sector_companies < 5) {
      next
    }
    
    cat("\nSector", s, "(", sector_companies, "companies):\n")
    
    # Top bankruptcy risks in this sector
    top_bankruptcy <- bankruptcy_ranking %>%
      filter(gsector == s) %>%
      arrange(desc(bankruptcy_risk)) %>%
      head(3)
    
    # Top acquisition risks in this sector
    top_acquisition <- acquisition_ranking %>%
      filter(gsector == s) %>%
      arrange(desc(acquisition_risk)) %>%
      head(3)
    
    cat("  Highest Bankruptcy Risk:\n")
    for(i in 1:nrow(top_bankruptcy)) {
      cat("    ", i, ". CUSIP: ", top_bankruptcy$cusip[i], 
          " (Risk Score: ", round(top_bankruptcy$bankruptcy_risk[i], 4), 
          ", Probability: ", round(top_bankruptcy$bankruptcy_prob[i], 1), "%)\n", sep="")
    }
    
    cat("  Highest Acquisition Risk:\n")
    for(i in 1:nrow(top_acquisition)) {
      cat("    ", i, ". CUSIP: ", top_acquisition$cusip[i], 
          " (Risk Score: ", round(top_acquisition$acquisition_risk[i], 4), 
          ", Probability: ", round(top_acquisition$acquisition_prob[i], 1), "%)\n", sep="")
    }
  }
}

# Apply sector-specific ranking
rank_by_sector(company_rankings$bankruptcy_ranking, company_rankings$acquisition_ranking)

Identifying highest-risk companies by sector:
-------------------------------------------

Sector 35 ( 1037 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 682311105 (Risk Score: 850.2138, Probability: 1%)
    2. CUSIP: 88362L209 (Risk Score: 793.5069, Probability: 1%)
    3. CUSIP: 90934C105 (Risk Score: 487.982, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 444859102 (Risk Score: 15.5969, Probability: 1%)
    2. CUSIP: 03073E105 (Risk Score: 14.213, Probability: 1%)
    3. CUSIP: 834223604 (Risk Score: 11.7315, Probability: 1%)

Sector 45 ( 1391 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 00951K104 (Risk Score: 709.594, Probability: 1%)
    2. CUSIP: 466212107 (Risk Score: 341.8142, Probability: 1%)
    3. CUSIP: 784109209 (Risk Score: 315.826, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 729132100 (Risk Score: 12.9866, Probability: 1%)
    2. CUSIP: 08160H101 (Risk Score: 10.9338, Probability: 1%)
    3. CUSIP: 801056102 (Risk Score: 10.1817, Probability: 1%)

Sector 20 ( 726 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 09175M804 (Risk Score: 621.0165, Probability: 1%)
    2. CUSIP: 04964A103 (Risk Score: 449.4098, Probability: 1%)
    3. CUSIP: 021373303 (Risk Score: 256.0836, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 56418H100 (Risk Score: 9.1366, Probability: 1%)
    2. CUSIP: 278878103 (Risk Score: 7.9283, Probability: 1%)
    3. CUSIP: 535555106 (Risk Score: 7.5928, Probability: 1%)

Sector 25 ( 916 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 74838C106 (Risk Score: 449.5963, Probability: 1%)
    2. CUSIP: 98912M201 (Risk Score: 373.4899, Probability: 1%)
    3. CUSIP: 825397102 (Risk Score: 260.3327, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 02341Q205 (Risk Score: 13.2884, Probability: 1%)
    2. CUSIP: 09629F108 (Risk Score: 9.6906, Probability: 1%)
    3. CUSIP: 36237H101 (Risk Score: 9.4212, Probability: 1%)

Sector 10 ( 244 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 140781105 (Risk Score: 300.7726, Probability: 1%)
    2. CUSIP: 93564A100 (Risk Score: 271.9417, Probability: 1%)
    3. CUSIP: 18911Q102 (Risk Score: 146.7998, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 858122203 (Risk Score: 5.508, Probability: 1%)
    2. CUSIP: 15643U104 (Risk Score: 5.2662, Probability: 1%)
    3. CUSIP: 402307102 (Risk Score: 4.4583, Probability: 1%)

Sector 30 ( 214 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 242370203 (Risk Score: 209.4095, Probability: 1%)
    2. CUSIP: 762831303 (Risk Score: 155.2022, Probability: 1%)
    3. CUSIP: 65332E101 (Risk Score: 140.0007, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 71377A103 (Risk Score: 10.0892, Probability: 1%)
    2. CUSIP: 72147K108 (Risk Score: 10.013, Probability: 1%)
    3. CUSIP: 911163103 (Risk Score: 6.8234, Probability: 1%)

Sector 50 ( 266 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 579489303 (Risk Score: 207.8258, Probability: 1%)
    2. CUSIP: 888733102 (Risk Score: 140.8568, Probability: 1%)
    3. CUSIP: 220874101 (Risk Score: 128.2872, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 448947507 (Risk Score: 6.4337, Probability: 1%)
    2. CUSIP: 92556H206 (Risk Score: 5.4554, Probability: 1%)
    3. CUSIP: 911684108 (Risk Score: 5.3986, Probability: 1%)

Sector 15 ( 182 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 949702104 (Risk Score: 177.0007, Probability: 1%)
    2. CUSIP: 038196101 (Risk Score: 107.1073, Probability: 1%)
    3. CUSIP: 16941J205 (Risk Score: 104.353, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 03940R107 (Risk Score: 6.7298, Probability: 1%)
    2. CUSIP: 483007704 (Risk Score: 5.5725, Probability: 1%)
    3. CUSIP: 68162K106 (Risk Score: 5.1622, Probability: 1%)

Sector 55 ( 32 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 29406L201 (Risk Score: 89.2126, Probability: 1%)
    2. CUSIP: G9376R209 (Risk Score: 12.9888, Probability: 1%)
    3. CUSIP: V0393H103 (Risk Score: 8.2652, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 85512C105 (Risk Score: 3.0625, Probability: 1%)
    2. CUSIP: 00130H105 (Risk Score: 2.5815, Probability: 0.9%)
    3. CUSIP: 629377508 (Risk Score: 2.4923, Probability: 0.9%)

Sector 60 ( 30 companies):
  Highest Bankruptcy Risk:
    1. CUSIP: 75605Y106 (Risk Score: 22.1886, Probability: 1%)
    2. CUSIP: M4793C102 (Risk Score: 20.5921, Probability: 1%)
    3. CUSIP: 489398107 (Risk Score: 16.6066, Probability: 1%)
  Highest Acquisition Risk:
    1. CUSIP: 48020Q107 (Risk Score: 3.7792, Probability: 1%)
    2. CUSIP: 78377T107 (Risk Score: 3.0541, Probability: 1%)
    3. CUSIP: 876664103 (Risk Score: 2.4286, Probability: 0.9%)

Here, we are analyzing the risk profiles of companies based on their bankruptcy and acquisition risks. We categorize companies into different risk categories based on their predicted risks and analyze the distribution of these categories across sectors. We also visualize the risk landscape using scatter plots and identify sectors with high proportions of companies in each risk category.

#-------------------------------------------------------------
# 14.3: Risk Profile Analysis
#-------------------------------------------------------------
cat("\n14.3: RISK PROFILE ANALYSIS\n")

14.3: RISK PROFILE ANALYSIS
cat("--------------------------------------\n")
--------------------------------------
analyze_risk_profiles <- function(bankruptcy_ranking, acquisition_ranking) {
  cat("Analyzing company risk profiles...\n")
  
  # Create a combined ranking with risk categorization
  combined_ranking <- bankruptcy_ranking
  
  # Determine risk categories based on percentiles
  bankruptcy_threshold <- quantile(combined_ranking$bankruptcy_risk, 0.75)
  acquisition_threshold <- quantile(combined_ranking$acquisition_risk, 0.75)
  
  # Categorize companies by risk profile
  combined_ranking$risk_category <- "Low Risk"
  combined_ranking$risk_category[combined_ranking$bankruptcy_risk > bankruptcy_threshold &
                                combined_ranking$acquisition_risk <= acquisition_threshold] <- "Bankruptcy Risk"
  combined_ranking$risk_category[combined_ranking$bankruptcy_risk <= bankruptcy_threshold &
                                combined_ranking$acquisition_risk > acquisition_threshold] <- "Acquisition Risk"
  combined_ranking$risk_category[combined_ranking$bankruptcy_risk > bankruptcy_threshold &
                                combined_ranking$acquisition_risk > acquisition_threshold] <- "High Risk (Both)"
  
  # Convert to factor with meaningful order
  combined_ranking$risk_category <- factor(combined_ranking$risk_category, 
                                        levels = c("Low Risk", "Bankruptcy Risk", 
                                                  "Acquisition Risk", "High Risk (Both)"))
  
  # Count companies in each category
  category_counts <- table(combined_ranking$risk_category)
  category_pcts <- prop.table(category_counts) * 100
  
  cat("\nCompany Risk Profile Distribution:\n")
  cat("--------------------------------\n")
  for(i in 1:length(category_counts)) {
    cat(names(category_counts)[i], ": ", category_counts[i], 
        " companies (", round(category_pcts[i], 1), "%)\n", sep="")
  }
  
  # Analyze risk profiles by sector
  sector_profiles <- table(combined_ranking$gsector, combined_ranking$risk_category)
  sector_profile_pcts <- prop.table(sector_profiles, margin = 1) * 100
  
  cat("\nRisk Profile Distribution by Sector:\n")
  cat("----------------------------------\n")
  print(round(sector_profile_pcts, 1))
  
  # Identify sectors with highest proportion of each risk type
  high_bankruptcy_sectors <- sort(sector_profile_pcts[, "Bankruptcy Risk"], decreasing = TRUE)
  high_acquisition_sectors <- sort(sector_profile_pcts[, "Acquisition Risk"], decreasing = TRUE)
  high_both_sectors <- sort(sector_profile_pcts[, "High Risk (Both)"], decreasing = TRUE)
  low_risk_sectors <- sort(sector_profile_pcts[, "Low Risk"], decreasing = TRUE)
  
  cat("\nSectors with Highest Proportion of Bankruptcy Risk:\n")
  for(i in 1:min(3, length(high_bankruptcy_sectors))) {
    sector <- names(high_bankruptcy_sectors)[i]
    pct <- high_bankruptcy_sectors[i]
    cat("  Sector", sector, ": ", round(pct, 1), "% of companies at high bankruptcy risk\n", sep="")
  }
  
  cat("\nSectors with Highest Proportion of Acquisition Risk:\n")
  for(i in 1:min(3, length(high_acquisition_sectors))) {
    sector <- names(high_acquisition_sectors)[i]
    pct <- high_acquisition_sectors[i]
    cat("  Sector", sector, ": ", round(pct, 1), "% of companies at high acquisition risk\n", sep="")
  }
  
  cat("\nSectors with Highest Proportion of Low Risk Companies:\n")
  for(i in 1:min(3, length(low_risk_sectors))) {
    sector <- names(low_risk_sectors)[i]
    pct <- low_risk_sectors[i]
    cat("  Sector", sector, ": ", round(pct, 1), "% of companies at low risk\n", sep="")
  }
  
  # Create visualization of risk profiles
  # Risk-Risk scatter plot
  plot(combined_ranking$bankruptcy_risk, combined_ranking$acquisition_risk,
       main = "Company Risk Landscape",
       xlab = "Bankruptcy Risk", ylab = "Acquisition Risk",
       pch = 16, col = adjustcolor("blue", alpha.f = 0.4),
       xlim = c(0, max(combined_ranking$bankruptcy_risk) * 1.1),
       ylim = c(0, max(combined_ranking$acquisition_risk) * 1.1))
  
  # Add sector-specific coloring if preferred
  # Use different colors for different risk categories
  points(combined_ranking$bankruptcy_risk[combined_ranking$risk_category == "Bankruptcy Risk"],
         combined_ranking$acquisition_risk[combined_ranking$risk_category == "Bankruptcy Risk"],
         pch = 16, col = adjustcolor("red", alpha.f = 0.6))
  
  points(combined_ranking$bankruptcy_risk[combined_ranking$risk_category == "Acquisition Risk"],
         combined_ranking$acquisition_risk[combined_ranking$risk_category == "Acquisition Risk"],
         pch = 16, col = adjustcolor("green", alpha.f = 0.6))
  
  points(combined_ranking$bankruptcy_risk[combined_ranking$risk_category == "High Risk (Both)"],
         combined_ranking$acquisition_risk[combined_ranking$risk_category == "High Risk (Both)"],
         pch = 16, col = adjustcolor("purple", alpha.f = 0.6))
  
  # Add lines to demarcate risk quadrants
  abline(v = bankruptcy_threshold, lty = 2, col = "darkgray")
  abline(h = acquisition_threshold, lty = 2, col = "darkgray")
  
  # Add quadrant labels
  text(max(combined_ranking$bankruptcy_risk) * 0.25, 
       max(combined_ranking$acquisition_risk) * 0.25, 
       "Low Risk Zone", col = "blue", cex = 1.2)
  
  text(max(combined_ranking$bankruptcy_risk) * 0.75, 
       max(combined_ranking$acquisition_risk) * 0.25, 
       "Bankruptcy Risk Zone", col = "red", cex = 1.2)
  
  text(max(combined_ranking$bankruptcy_risk) * 0.25, 
       max(combined_ranking$acquisition_risk) * 0.75, 
       "Acquisition Risk Zone", col = "green4", cex = 1.2)
  
  text(max(combined_ranking$bankruptcy_risk) * 0.75, 
       max(combined_ranking$acquisition_risk) * 0.75, 
       "High Risk Zone", col = "purple", cex = 1.2)
  
  # Add correlation information
  risk_correlation <- cor(combined_ranking$bankruptcy_risk, 
                       combined_ranking$acquisition_risk)
  
  mtext(paste("Correlation between risk types:", round(risk_correlation, 3)), 
        side = 3, line = 0.5, cex = 0.8)
  
  # Return the combined ranking with risk categories
  return(combined_ranking)
}

# Execute the risk profile analysis
risk_profiles <- analyze_risk_profiles(company_rankings$bankruptcy_ranking, 
                                    company_rankings$acquisition_ranking)
Analyzing company risk profiles...

Company Risk Profile Distribution:
--------------------------------
Low Risk: 2728 companies (54.1%)
Bankruptcy Risk: 1050 companies (20.8%)
Acquisition Risk: 1050 companies (20.8%)
High Risk (Both): 210 companies (4.2%)

Risk Profile Distribution by Sector:
----------------------------------
    
     Low Risk Bankruptcy Risk Acquisition Risk High Risk (Both)
  10     61.1            20.5             16.4              2.0
  15     54.4            23.1             20.3              2.2
  20     53.9            23.1             19.7              3.3
  25     48.0            28.9             17.8              5.2
  30     57.9            23.4             16.4              2.3
  35     51.6            16.9             25.8              5.7
  45     55.8            17.0             23.6              3.7
  50     63.9            20.3             10.9              4.9
  55     71.9            15.6             12.5              0.0
  60     70.0            16.7             10.0              3.3

Sectors with Highest Proportion of Bankruptcy Risk:
  Sector25: 28.9% of companies at high bankruptcy risk
  Sector30: 23.4% of companies at high bankruptcy risk
  Sector20: 23.1% of companies at high bankruptcy risk

Sectors with Highest Proportion of Acquisition Risk:
  Sector35: 25.8% of companies at high acquisition risk
  Sector45: 23.6% of companies at high acquisition risk
  Sector15: 20.3% of companies at high acquisition risk

Sectors with Highest Proportion of Low Risk Companies:
  Sector55: 71.9% of companies at low risk
  Sector60: 70% of companies at low risk
  Sector50: 63.9% of companies at low risk

We also rank the companies by risk using the Cox model predictions. This involves calculating the predicted risks for each company and categorizing them into different risk levels. We then visualize the risk landscape using scatter plots and identify sectors with high proportions of companies in each risk category. The analysis also includes a discussion of the implications of these findings for investors and stakeholders. We include time till event estimates for each company, which can be useful for risk management and investment decisions. Time is calculated using the linear predictor from the Cox model and the median time to event from the data. We also cap extremely long estimates at a reasonable value (e.g., 100 years) to avoid unrealistic predictions. The final output includes a ranking of companies by risk, along with their estimated remaining time until bankruptcy or acquisition.

#-------------------------------------------------------------
# 14.1: Rank Companies by Risk (Using Cox Model Predictions)
#-------------------------------------------------------------
cat("\n14.1: COMPANY RISK RANKING WITH REMAINING TIME ESTIMATION\n")

14.1: COMPANY RISK RANKING WITH REMAINING TIME ESTIMATION
cat("--------------------------------------\n")
--------------------------------------
# Function to rank companies by risk with survival-based time estimates
rank_companies_by_risk <- function() {
  cat("Ranking companies by bankruptcy and acquisition risk...\n")
  
  # Get last observation for each company
  company_data <- data %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    ungroup()
  
  # Calculate bankruptcy and acquisition risk scores
  company_data$bankruptcy_risk <- predict(final_bankruptcy_model, newdata = company_data, type = "risk")
  company_data$acquisition_risk <- predict(final_acquisition_model, newdata = company_data, type = "risk")
  
  # Calculate percentile ranks
  company_data$bankruptcy_percentile <- rank(company_data$bankruptcy_risk) / nrow(company_data) * 100
  company_data$acquisition_percentile <- rank(company_data$acquisition_risk) / nrow(company_data) * 100
  
  # Add risk categories
  company_data$bankruptcy_risk_category <- cut(company_data$bankruptcy_percentile,
                                               breaks = c(0, 80, 90, 95, 100),
                                               labels = c("Low", "Moderate", "High", "Very High"))
  
  company_data$acquisition_risk_category <- cut(company_data$acquisition_percentile,
                                                breaks = c(0, 80, 90, 95, 100),
                                                labels = c("Low", "Moderate", "High", "Very High"))
  
  # Estimate remaining time using survival analysis principles
  cat("Estimating remaining time using survival analysis principles...\n")
  
  # For bankruptcy model
  # Create typical covariate values for reference - FIX: use dplyr verbs correctly
  bankruptcy_vars <- names(coef(final_bankruptcy_model))
  bankruptcy_vars <- bankruptcy_vars[!grepl(":tt", bankruptcy_vars)]
  
  # Create reference values manually instead of using across()
  reference_bankruptcy <- data.frame(matrix(NA, nrow = 1, ncol = length(bankruptcy_vars)))
  colnames(reference_bankruptcy) <- bankruptcy_vars
  
  for(var in bankruptcy_vars) {
    if(var %in% colnames(company_data)) {
      reference_bankruptcy[1, var] <- median(company_data[[var]], na.rm = TRUE)
    } else {
      # For variables not in the data (could be interaction terms or transformations)
      reference_bankruptcy[1, var] <- 0
    }
  }
  
  # Calculate linear predictor for reference company
  lp_reference_bankruptcy <- sum(coef(final_bankruptcy_model)[bankruptcy_vars] * 
                                   as.numeric(reference_bankruptcy[1, ]))
  
  # Calculate linear predictor for each company
  lp_company_bankruptcy <- predict(final_bankruptcy_model, newdata = company_data, type = "lp")
  
  # Estimate relative risk compared to reference company
  relative_risk_bankruptcy <- exp(lp_company_bankruptcy - lp_reference_bankruptcy)
  
  # Use a data-driven approach to determine median reference time
  # Based on event distribution in the data
  event_times_bankruptcy <- data %>%
    filter(bankruptcy == 1) %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    pull(tstop)
  
  if(length(event_times_bankruptcy) > 0) {
    median_reference_time_bankruptcy <- median(event_times_bankruptcy)
  } else {
    # Fallback if no bankruptcy events
    median_reference_time_bankruptcy <- 25
  }
  
  # Calculate company-specific median times
  company_data$remaining_time_to_bankruptcy <- median_reference_time_bankruptcy / relative_risk_bankruptcy
  
  # For companies already in the event, set remaining time to 0
  company_data$remaining_time_to_bankruptcy[company_data$bankruptcy == 1] <- 0
  
  # Cap extremely long estimates at a reasonable value (e.g., 100 years)
  company_data$remaining_time_to_bankruptcy[company_data$remaining_time_to_bankruptcy > 100] <- 100
  
  # Similarly for acquisition model
  acquisition_vars <- names(coef(final_acquisition_model))
  acquisition_vars <- acquisition_vars[!grepl(":tt", acquisition_vars)]
  
  # Create reference values manually
  reference_acquisition <- data.frame(matrix(NA, nrow = 1, ncol = length(acquisition_vars)))
  colnames(reference_acquisition) <- acquisition_vars
  
  for(var in acquisition_vars) {
    if(var %in% colnames(company_data)) {
      reference_acquisition[1, var] <- median(company_data[[var]], na.rm = TRUE)
    } else {
      reference_acquisition[1, var] <- 0
    }
  }
  
  lp_reference_acquisition <- sum(coef(final_acquisition_model)[acquisition_vars] * 
                                    as.numeric(reference_acquisition[1, ]))
  
  lp_company_acquisition <- predict(final_acquisition_model, newdata = company_data, type = "lp")
  
  relative_risk_acquisition <- exp(lp_company_acquisition - lp_reference_acquisition)
  
  # Get median time from data
  event_times_acquisition <- data %>%
    filter(acquisition == 1) %>%
    group_by(cusip) %>%
    slice_max(tstop) %>%
    pull(tstop)
  
  if(length(event_times_acquisition) > 0) {
    median_reference_time_acquisition <- median(event_times_acquisition)
  } else {
    median_reference_time_acquisition <- 25
  }
  
  company_data$remaining_time_to_acquisition <- median_reference_time_acquisition / relative_risk_acquisition
  
  # For companies already acquired, set remaining time to 0
  company_data$remaining_time_to_acquisition[company_data$acquisition == 1] <- 0
  
  # Cap extremely long estimates
  company_data$remaining_time_to_acquisition[company_data$remaining_time_to_acquisition > 100] <- 100
  
  # Add confidence around time estimates (simplified approach)
  # Lower and upper bounds based on assumption that hazard ratio has ±25% uncertainty
  company_data$bankruptcy_time_lower <- company_data$remaining_time_to_bankruptcy / 1.25
  company_data$bankruptcy_time_upper <- company_data$remaining_time_to_bankruptcy * 1.25
  
  company_data$acquisition_time_lower <- company_data$remaining_time_to_acquisition / 1.25
  company_data$acquisition_time_upper <- company_data$remaining_time_to_acquisition * 1.25
  
  # Add "most likely outcome" column
  company_data <- company_data %>%
    mutate(most_likely_outcome = case_when(
      bankruptcy == 1 ~ "Already Bankrupt",
      acquisition == 1 ~ "Already Acquired",
      bankruptcy_risk > acquisition_risk & remaining_time_to_bankruptcy < remaining_time_to_acquisition ~ "Bankruptcy",
      acquisition_risk > bankruptcy_risk & remaining_time_to_acquisition < remaining_time_to_bankruptcy ~ "Acquisition",
      TRUE ~ "Inconclusive"
    ))
  
  # Return ranked company data
  return(company_data)
}

# Rank companies by risk
ranked_companies <- rank_companies_by_risk()
Ranking companies by bankruptcy and acquisition risk...
Estimating remaining time using survival analysis principles...
# Display top companies at risk of bankruptcy
cat("\nTOP 20 COMPANIES AT RISK OF BANKRUPTCY:\n")

TOP 20 COMPANIES AT RISK OF BANKRUPTCY:
cat("------------------------------------\n")
------------------------------------
top_bankruptcy <- ranked_companies %>%
  filter(bankruptcy == 0) %>%  # Only include companies not yet bankrupt
  arrange(desc(bankruptcy_risk)) %>%
  head(20)

# Create a dashboard-style display
bankruptcy_display <- data.frame(
  Rank = 1:nrow(top_bankruptcy),
  CUSIP = top_bankruptcy$cusip,
  Industry = top_bankruptcy$gsector,
  "Risk Score" = round(top_bankruptcy$bankruptcy_risk, 4),
  "Risk Percentile" = paste0(round(top_bankruptcy$bankruptcy_percentile, 1), "%"),
  "Est. Remaining Years" = round(top_bankruptcy$remaining_time_to_bankruptcy, 1),
  "Time Range" = paste0("[", round(top_bankruptcy$bankruptcy_time_lower, 1), "-", 
                        round(top_bankruptcy$bankruptcy_time_upper, 1), "]"),
  "Current Lifetime" = round(top_bankruptcy$tstop, 1),
  "Risk Category" = as.character(top_bankruptcy$bankruptcy_risk_category),
  stringsAsFactors = FALSE
)

print(bankruptcy_display, row.names = FALSE)

# Display top companies likely to be acquired
cat("\nTOP 20 COMPANIES LIKELY TO BE ACQUIRED:\n")

TOP 20 COMPANIES LIKELY TO BE ACQUIRED:
cat("------------------------------------\n")
------------------------------------
top_acquisition <- ranked_companies %>%
  filter(acquisition == 0) %>%  # Only include companies not yet acquired
  arrange(desc(acquisition_risk)) %>%
  head(20)

# Create a dashboard-style display
acquisition_display <- data.frame(
  Rank = 1:nrow(top_acquisition),
  CUSIP = top_acquisition$cusip,
  Industry = top_acquisition$gsector,
  "Risk Score" = round(top_acquisition$acquisition_risk, 4),
  "Risk Percentile" = paste0(round(top_acquisition$acquisition_percentile, 1), "%"),
  "Est. Remaining Years" = round(top_acquisition$remaining_time_to_acquisition, 1),
  "Time Range" = paste0("[", round(top_acquisition$acquisition_time_lower, 1), "-", 
                        round(top_acquisition$acquisition_time_upper, 1), "]"),
  "Current Lifetime" = round(top_acquisition$tstop, 1),
  "Risk Category" = as.character(top_acquisition$acquisition_risk_category),
  stringsAsFactors = FALSE
)

print(acquisition_display, row.names = FALSE)

# Companies with competing risks (at high risk for both outcomes)
cat("\nCOMPANIES AT HIGH RISK FOR BOTH OUTCOMES:\n")

COMPANIES AT HIGH RISK FOR BOTH OUTCOMES:
cat("----------------------------------------\n")
----------------------------------------
competing_risks <- ranked_companies %>%
  filter(bankruptcy == 0 & acquisition == 0) %>%  # Only include active companies
  filter(bankruptcy_percentile > 90 & acquisition_percentile > 90) %>%  # High risk for both
  arrange(desc(bankruptcy_risk + acquisition_risk)) %>%
  head(10)

if(nrow(competing_risks) > 0) {
  competing_display <- data.frame(
    CUSIP = competing_risks$cusip,
    Industry = competing_risks$gsector,
    "B-Risk %" = paste0(round(competing_risks$bankruptcy_percentile, 1), "%"),
    "A-Risk %" = paste0(round(competing_risks$acquisition_percentile, 1), "%"),
    "B-Years" = round(competing_risks$remaining_time_to_bankruptcy, 1),
    "A-Years" = round(competing_risks$remaining_time_to_acquisition, 1),
    "Most Likely" = competing_risks$most_likely_outcome,
    stringsAsFactors = FALSE
  )
  
  print(competing_display, row.names = FALSE)
} else {
  cat("No companies found at high risk for both outcomes.\n")
}

Finally we look at industry specific effects of financial variables on bankruptcy and acquisition risks. This involves analyzing the coefficients of the Cox models for different sectors and identifying any significant differences in the effects of financial variables across industries. We also visualize these effects using plots to illustrate how the impact of financial variables varies by sector. This analysis can provide insights into which financial factors are most predictive of bankruptcy or acquisition risk in different industries, helping investors and stakeholders make informed decisions.

#-------------------------------------------------------------
# STEP 15: Industry-Specific Financial Variable Effects Analysis
#-------------------------------------------------------------
cat("\n=== STEP 15: INDUSTRY-SPECIFIC FINANCIAL VARIABLE EFFECTS ANALYSIS ===\n")

=== STEP 15: INDUSTRY-SPECIFIC FINANCIAL VARIABLE EFFECTS ANALYSIS ===
analyze_industry_effects <- function() {
  cat("\n15.1: INDUSTRY-SPECIFIC VARIABLE EFFECTS\n")
  cat("--------------------------------------\n")
  
  # Get unique industry sectors
  sectors <- unique(data$gsector)
  cat("Analyzing variable effects across", length(sectors), "industry sectors...\n\n")
  
  # Identify all non-macro variables with significant effects
  bankruptcy_vars <- names(coef(final_bankruptcy_model))
  acquisition_vars <- names(coef(final_acquisition_model))
  
  # Remove macro variables and their time interactions
  macro_vars <- c("gdp_growth", "gdp_deflator", "unemployement")
  bankruptcy_vars <- bankruptcy_vars[!sapply(bankruptcy_vars, function(v) {
    base_var <- sub(":tt", "", v)
    return(base_var %in% macro_vars)
  })]
  
  acquisition_vars <- acquisition_vars[!sapply(acquisition_vars, function(v) {
    base_var <- sub(":tt", "", v)
    return(base_var %in% macro_vars)
  })]
  
  # Identify time-varying effects for financial variables
  bankruptcy_time_vars <- grep(":tt", bankruptcy_vars, value = TRUE)
  acquisition_time_vars <- grep(":tt", acquisition_vars, value = TRUE)
  
  if(length(bankruptcy_time_vars) == 0 && length(acquisition_time_vars) == 0) {
    cat("No time-varying effects found for financial variables in the models.\n")
    return(NULL)
  }
  
  # Store results
  industry_results <- list()
  
  # 1. Test time-varying effects for financial variables by industry
  cat("A. TESTING FINANCIAL VARIABLE TIME-VARYING EFFECTS BY INDUSTRY\n")
  cat("--------------------------------------------------------\n\n")
  
  # Function to test industry-specific effects
  test_industry_effects <- function(model_name) {
    cat(model_name, "Model:\n\n")
    
    # Use the appropriate model
    model <- if(model_name == "Bankruptcy") final_bankruptcy_model else final_acquisition_model
    model_vars <- if(model_name == "Bankruptcy") bankruptcy_vars else acquisition_vars
    model_time_vars <- if(model_name == "Bankruptcy") bankruptcy_time_vars else acquisition_time_vars
    event_type <- tolower(model_name)
    
    if(length(model_time_vars) == 0) {
      cat("No time-varying effects found for financial variables in this model.\n\n")
      return(NULL)
    }
    
    # Get main variable names from time interactions
    main_vars <- sub(":tt", "", model_time_vars)
    
    # Results storage
    industry_var_results <- data.frame(
      Variable = character(),
      Sector = character(),
      Main_Coef = numeric(),
      Time_Coef = numeric(),
      Initial_HR = numeric(),
      HR_Year10 = numeric(),
      Effect_Pattern = character(),
      stringsAsFactors = FALSE
    )
    
    # For each major sector, fit a sector-specific model
    for(sector in sectors) {
      # Get only data for this sector
      sector_data <- data %>% 
        filter(gsector == sector)
      
      # Check if enough data
      if(nrow(sector_data) < 100) {
        cat("  Sector", sector, "has insufficient data (n =", nrow(sector_data), ").\n")
        next
      }
      
      # Try to fit sector-specific model with only financial variables
      tryCatch({
        # Create formula focusing on financial time interactions
        formula_str <- paste0("Surv(tstart, tstop, ", event_type, ") ~ ", 
                              paste(main_vars, collapse = " + "),
                              " + ", paste(model_time_vars, collapse = " + "))
        
        sector_model <- coxph(as.formula(formula_str), data = sector_data, ties = "efron")
        
        # Extract coefficients for time variables
        for(i in 1:length(main_vars)) {
          var <- main_vars[i]
          time_var <- model_time_vars[i]
          
          if(var %in% names(coef(sector_model)) && time_var %in% names(coef(sector_model))) {
            main_coef <- coef(sector_model)[var]
            time_coef <- coef(sector_model)[time_var]
            
            # Calculate hazard ratios
            initial_hr <- exp(main_coef)
            hr_year10 <- exp(main_coef + time_coef * 10)
            
            # Determine effect pattern
            initial_effect <- ifelse(main_coef > 0, "risk-increasing", "risk-decreasing")
            if(sign(main_coef) == sign(time_coef)) {
              effect_pattern <- paste0(initial_effect, " effect strengthens")
            } else {
              effect_pattern <- paste0(initial_effect, " effect weakens")
            }
            
            # Add to results
            industry_var_results <- rbind(industry_var_results, data.frame(
              Variable = var,
              Sector = as.character(sector),
              Main_Coef = main_coef,
              Time_Coef = time_coef,
              Initial_HR = initial_hr,
              HR_Year10 = hr_year10,
              Effect_Pattern = effect_pattern,
              stringsAsFactors = FALSE
            ))
          }
        }
        
        cat("  Sector", sector, "model fitted successfully.\n")
      }, error = function(e) {
        cat("  Error fitting model for sector", sector, ":", conditionMessage(e), "\n")
      })
    }
    
    if(nrow(industry_var_results) > 0) {
      cat("\n  Industry-specific time patterns for financial variables in", model_name, "model:\n\n")
      
      # Analyze each variable across sectors
      for(var in unique(industry_var_results$Variable)) {
        cat("  Variable:", var, "\n")
        var_results <- industry_var_results %>% filter(Variable == var)
        
        # Check for diversity in patterns
        patterns <- table(var_results$Effect_Pattern)
        cat("  Effect patterns across sectors:\n")
        for(p in names(patterns)) {
          cat("    -", p, ":", patterns[p], "sectors\n")
        }
        
        # Show specific sector effects
        cat("  Sector-specific effects:\n")
        for(i in 1:nrow(var_results)) {
          cat(sprintf("    - Sector %s: Initial HR = %.2f, HR at 10 years = %.2f (%s)\n",
                      var_results$Sector[i], var_results$Initial_HR[i], 
                      var_results$HR_Year10[i], var_results$Effect_Pattern[i]))
        }
        cat("\n")
      }
    } else {
      cat("  No industry-specific results available for financial variables.\n\n")
    }
    
    return(industry_var_results)
  }
  
  # Test financial variable effects for both models
  industry_results$bankruptcy <- test_industry_effects("Bankruptcy")
  industry_results$acquisition <- test_industry_effects("Acquisition")
  
  # 2. Create visualizations of industry-specific effects
  cat("\n15.2: INDUSTRY-SPECIFIC FINANCIAL EFFECTS VISUALIZATION\n")
  cat("--------------------------------------------------\n")
  
  # Function to create visualization for a single financial variable
  visualize_variable_by_industry <- function(industry_results, model_name, variable) {
    if(is.null(industry_results) || !variable %in% industry_results$Variable) {
      cat("  No data available for", variable, "in", model_name, "model.\n")
      return(NULL)
    }
    
    # Filter data for this variable
    var_data <- industry_results[industry_results$Variable == variable, ]
    
    # Set up time sequence
    times <- seq(0, 10, by = 0.1)
    
    # Calculate hazard ratios over time for each sector
    sectors <- unique(var_data$Sector)
    hr_matrix <- matrix(NA, nrow = length(times), ncol = length(sectors))
    
    for(i in 1:length(sectors)) {
      sector_row <- var_data[var_data$Sector == sectors[i], ]
      if(nrow(sector_row) > 0) {
        main_coef <- sector_row$Main_Coef[1]
        time_coef <- sector_row$Time_Coef[1]
        hr_matrix[, i] <- exp(main_coef + time_coef * times)
      }
    }
    
    # Create plot
    matplot(times, hr_matrix, type = "l", lty = 1:length(sectors), col = 1:length(sectors),
            main = paste("Effect of", variable, "by Sector -", model_name, "Model"),
            xlab = "Time (Years)", ylab = "Hazard Ratio",
            ylim = c(min(0.5, min(hr_matrix, na.rm = TRUE)), 
                    max(2, max(hr_matrix, na.rm = TRUE))))
    
    # Add reference line
    abline(h = 1, lty = 2, col = "gray")
    
    # Add legend
    legend("topright", legend = paste("Sector", sectors), 
           lty = 1:length(sectors), col = 1:length(sectors), 
           cex = 0.8, bty = "n")
    
    return(var_data)
  }
  
  # Visualize top financial variables with time interactions
  if(!is.null(industry_results$bankruptcy) && nrow(industry_results$bankruptcy) > 0) {
    top_variables <- names(table(industry_results$bankruptcy$Variable))[1:min(3, length(table(industry_results$bankruptcy$Variable)))]
    
    cat("  Creating visualizations for top financial variables in bankruptcy model...\n")
    for(var in top_variables) {
      visualize_variable_by_industry(industry_results$bankruptcy, "Bankruptcy", var)
    }
  }
  
  if(!is.null(industry_results$acquisition) && nrow(industry_results$acquisition) > 0) {
    top_variables <- names(table(industry_results$acquisition$Variable))[1:min(3, length(table(industry_results$acquisition$Variable)))]
    
    cat("  Creating visualizations for top financial variables in acquisition model...\n")
    for(var in top_variables) {
      visualize_variable_by_industry(industry_results$acquisition, "Acquisition", var)
    }
  }
  
  # 3. Generate sector-specific recommendations
  cat("\n15.3: INDUSTRY-SPECIFIC FINANCIAL RISK FACTORS\n")
  cat("------------------------------------------\n\n")
  
  # Function to identify key financial risk factors by sector
  identify_key_risk_factors <- function(industry_results, model_name) {
    if(is.null(industry_results) || nrow(industry_results) == 0) {
      cat("  No data available for", model_name, "model risk factors.\n\n")
      return(NULL)
    }
    
    cat("Key Financial Risk Factors by Industry Sector -", model_name, "Model:\n\n")
    
    # Process each sector
    for(sector in unique(industry_results$Sector)) {
      sector_data <- industry_results[industry_results$Sector == sector, ]
      
      cat("  Industry Sector", sector, ":\n")
      
      # Sort by effect size (using abs(log(HR)) at year 10)
      sector_data$effect_size <- abs(log(sector_data$HR_Year10))
      sector_data <- sector_data[order(sector_data$effect_size, decreasing = TRUE), ]
      
      # Report top risk factors
      for(i in 1:min(3, nrow(sector_data))) {
        var <- sector_data$Variable[i]
        
        # Create readable variable name
        var_name <- var
        if(var == "LTMTA") var_name <- "Liabilities-to-Market Value"
        else if(var == "NIMTA") var_name <- "Net Income-to-Market Value"
        else if(var == "CASHMTA") var_name <- "Cash-to-Market Value"
        else if(var == "PRICE") var_name <- "Stock Price"
        else if(var == "MBE") var_name <- "Market-to-Book Equity"
        else if(var == "RSIZE") var_name <- "Relative Size"
        else if(var == "z_score") var_name <- "Altman Z-Score"
        
        cat("    • KEY FACTOR:", var_name, "\n")
        cat("      Initial HR =", round(sector_data$Initial_HR[i], 2), 
            ", HR at Year 10 =", round(sector_data$HR_Year10[i], 2), "\n")
        cat("      Pattern:", sector_data$Effect_Pattern[i], "\n")
        
        # Add interpretation
        cat("      Interpretation: ")
        if(grepl("risk-increasing", sector_data$Effect_Pattern[i])) {
          cat("Higher", var_name, "is associated with increased", tolower(model_name), "risk")
        } else {
          cat("Higher", var_name, "is associated with decreased", tolower(model_name), "risk")
        }
        
        if(grepl("strengthens", sector_data$Effect_Pattern[i])) {
          cat(", and this effect becomes stronger over time\n")
        } else {
          cat(", but this effect diminishes over time\n")
        }
      }
      
      # Add sector-specific recommendation
      cat("    • SECTOR RECOMMENDATION: ")
      if(model_name == "Bankruptcy") {
        cat("Companies in Sector", sector, "should focus on ")
        top_var <- sector_data$Variable[1]
        if(grepl("risk-increasing", sector_data$Effect_Pattern[1])) {
          cat("reducing their", top_var, "to minimize bankruptcy risk.\n")
        } else {
          cat("maintaining strong", top_var, "to protect against bankruptcy risk.\n")
        }
      } else {
        cat("Companies in Sector", sector, "looking to avoid acquisition should focus on ")
        top_var <- sector_data$Variable[1]
        if(grepl("risk-increasing", sector_data$Effect_Pattern[1])) {
          cat("managing their", top_var, "levels.\n")
        } else {
          cat("strategically positioning their", top_var, "metrics.\n")
        }
      }
      cat("\n")
    }
  }
  
  # Generate sector-specific financial risk factors
  identify_key_risk_factors(industry_results$bankruptcy, "Bankruptcy")
  identify_key_risk_factors(industry_results$acquisition, "Acquisition")
  
  return(industry_results)
}

# Run industry analysis
industry_results <- analyze_industry_effects()

15.1: INDUSTRY-SPECIFIC VARIABLE EFFECTS
--------------------------------------
Analyzing variable effects across 10 industry sectors...

A. TESTING FINANCIAL VARIABLE TIME-VARYING EFFECTS BY INDUSTRY
--------------------------------------------------------

Bankruptcy Model:

  Sector 20 model fitted successfully.
  Sector 35 model fitted successfully.
  Sector 45 model fitted successfully.
  Sector 25 model fitted successfully.
  Error fitting model for sector 55 : missing value where TRUE/FALSE needed 
  Sector 15 model fitted successfully.
  Sector 50 model fitted successfully.
  Sector 10 model fitted successfully.
  Sector 30 model fitted successfully.
  Error fitting model for sector 60 : missing value where TRUE/FALSE needed 

  Industry-specific time patterns for financial variables in Bankruptcy model:

  Variable: LTMTA 
  Effect patterns across sectors:
    - risk-decreasing effect weakens : 2 sectors
    - risk-increasing effect strengthens : 4 sectors
    - risk-increasing effect weakens : 2 sectors
  Sector-specific effects:
    - Sector 20: Initial HR = 849.50, HR at 10 years = 484.19 (risk-increasing effect weakens)
    - Sector 35: Initial HR = 6.09, HR at 10 years = 12.62 (risk-increasing effect strengthens)
    - Sector 45: Initial HR = 27.20, HR at 10 years = 101.94 (risk-increasing effect strengthens)
    - Sector 25: Initial HR = 21.21, HR at 10 years = 66.27 (risk-increasing effect strengthens)
    - Sector 15: Initial HR = 0.00, HR at 10 years = 418711389019.32 (risk-decreasing effect weakens)
    - Sector 50: Initial HR = 751.09, HR at 10 years = 1219.04 (risk-increasing effect strengthens)
    - Sector 10: Initial HR = 2347.67, HR at 10 years = 1148.53 (risk-increasing effect weakens)
    - Sector 30: Initial HR = 0.00, HR at 10 years = 644178955453673366046088286282626462400.00 (risk-decreasing effect weakens)

  Variable: PRICE 
  Effect patterns across sectors:
    - risk-decreasing effect strengthens : 2 sectors
    - risk-decreasing effect weakens : 3 sectors
    - risk-increasing effect weakens : 3 sectors
  Sector-specific effects:
    - Sector 20: Initial HR = 0.82, HR at 10 years = 0.73 (risk-decreasing effect strengthens)
    - Sector 35: Initial HR = 0.68, HR at 10 years = 0.73 (risk-decreasing effect weakens)
    - Sector 45: Initial HR = 0.67, HR at 10 years = 0.80 (risk-decreasing effect weakens)
    - Sector 25: Initial HR = 0.63, HR at 10 years = 0.63 (risk-decreasing effect weakens)
    - Sector 15: Initial HR = 0.99, HR at 10 years = 0.66 (risk-decreasing effect strengthens)
    - Sector 50: Initial HR = 2.46, HR at 10 years = 0.44 (risk-increasing effect weakens)
    - Sector 10: Initial HR = 1.09, HR at 10 years = 0.49 (risk-increasing effect weakens)
    - Sector 30: Initial HR = 2.55, HR at 10 years = 0.09 (risk-increasing effect weakens)

  Variable: debt_ratio 
  Effect patterns across sectors:
    - risk-decreasing effect weakens : 3 sectors
    - risk-increasing effect weakens : 5 sectors
  Sector-specific effects:
    - Sector 20: Initial HR = 0.23, HR at 10 years = 0.52 (risk-decreasing effect weakens)
    - Sector 35: Initial HR = 1.47, HR at 10 years = 0.80 (risk-increasing effect weakens)
    - Sector 45: Initial HR = 0.19, HR at 10 years = 0.81 (risk-decreasing effect weakens)
    - Sector 25: Initial HR = 0.62, HR at 10 years = 0.79 (risk-decreasing effect weakens)
    - Sector 15: Initial HR = 4.17, HR at 10 years = 0.43 (risk-increasing effect weakens)
    - Sector 50: Initial HR = 15.95, HR at 10 years = 0.23 (risk-increasing effect weakens)
    - Sector 10: Initial HR = 4.84, HR at 10 years = 0.11 (risk-increasing effect weakens)
    - Sector 30: Initial HR = 490.97, HR at 10 years = 0.00 (risk-increasing effect weakens)

Acquisition Model:

  Sector 20 model fitted successfully.
  Sector 35 model fitted successfully.
  Sector 45 model fitted successfully.
  Sector 25 model fitted successfully.
  Sector 55 model fitted successfully.
  Sector 15 model fitted successfully.
  Sector 50 model fitted successfully.
  Sector 10 model fitted successfully.
  Sector 30 model fitted successfully.
  Sector 60 model fitted successfully.

  Industry-specific time patterns for financial variables in Acquisition model:

  Variable: wc_ratio 
  Effect patterns across sectors:
    - risk-decreasing effect weakens : 7 sectors
    - risk-increasing effect strengthens : 1 sectors
    - risk-increasing effect weakens : 2 sectors
  Sector-specific effects:
    - Sector 20: Initial HR = 0.51, HR at 10 years = 0.63 (risk-decreasing effect weakens)
    - Sector 35: Initial HR = 0.44, HR at 10 years = 0.77 (risk-decreasing effect weakens)
    - Sector 45: Initial HR = 0.44, HR at 10 years = 0.70 (risk-decreasing effect weakens)
    - Sector 25: Initial HR = 0.55, HR at 10 years = 0.87 (risk-decreasing effect weakens)
    - Sector 55: Initial HR = 0.82, HR at 10 years = 856.01 (risk-decreasing effect weakens)
    - Sector 15: Initial HR = 1.68, HR at 10 years = 1.75 (risk-increasing effect strengthens)
    - Sector 50: Initial HR = 0.54, HR at 10 years = 0.66 (risk-decreasing effect weakens)
    - Sector 10: Initial HR = 1.11, HR at 10 years = 0.53 (risk-increasing effect weakens)
    - Sector 30: Initial HR = 1.17, HR at 10 years = 0.88 (risk-increasing effect weakens)
    - Sector 60: Initial HR = 0.00, HR at 10 years = 0.00 (risk-decreasing effect weakens)

  Variable: asset_turnover 
  Effect patterns across sectors:
    - risk-decreasing effect weakens : 7 sectors
    - risk-increasing effect strengthens : 1 sectors
    - risk-increasing effect weakens : 2 sectors
  Sector-specific effects:
    - Sector 20: Initial HR = 1.04, HR at 10 years = 1.11 (risk-increasing effect strengthens)
    - Sector 35: Initial HR = 0.98, HR at 10 years = 1.03 (risk-decreasing effect weakens)
    - Sector 45: Initial HR = 0.59, HR at 10 years = 0.80 (risk-decreasing effect weakens)
    - Sector 25: Initial HR = 0.81, HR at 10 years = 0.86 (risk-decreasing effect weakens)
    - Sector 55: Initial HR = 0.22, HR at 10 years = 0.47 (risk-decreasing effect weakens)
    - Sector 15: Initial HR = 1.40, HR at 10 years = 1.31 (risk-increasing effect weakens)
    - Sector 50: Initial HR = 0.94, HR at 10 years = 1.06 (risk-decreasing effect weakens)
    - Sector 10: Initial HR = 0.64, HR at 10 years = 0.89 (risk-decreasing effect weakens)
    - Sector 30: Initial HR = 0.83, HR at 10 years = 0.88 (risk-decreasing effect weakens)
    - Sector 60: Initial HR = 7.13, HR at 10 years = 0.00 (risk-increasing effect weakens)


15.2: INDUSTRY-SPECIFIC FINANCIAL EFFECTS VISUALIZATION
--------------------------------------------------
  Creating visualizations for top financial variables in bankruptcy model...

  Creating visualizations for top financial variables in acquisition model...


15.3: INDUSTRY-SPECIFIC FINANCIAL RISK FACTORS
------------------------------------------

Key Financial Risk Factors by Industry Sector - Bankruptcy Model:

  Industry Sector 20 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 849.5 , HR at Year 10 = 484.19 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher Liabilities-to-Market Value is associated with increased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 0.23 , HR at Year 10 = 0.52 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher debt_ratio is associated with decreased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: Stock Price 
      Initial HR = 0.82 , HR at Year 10 = 0.73 
      Pattern: risk-decreasing effect strengthens 
      Interpretation: Higher Stock Price is associated with decreased bankruptcy risk, and this effect becomes stronger over time
    • SECTOR RECOMMENDATION: Companies in Sector 20 should focus on reducing their LTMTA to minimize bankruptcy risk.

  Industry Sector 35 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 6.09 , HR at Year 10 = 12.62 
      Pattern: risk-increasing effect strengthens 
      Interpretation: Higher Liabilities-to-Market Value is associated with increased bankruptcy risk, and this effect becomes stronger over time
    • KEY FACTOR: Stock Price 
      Initial HR = 0.68 , HR at Year 10 = 0.73 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher Stock Price is associated with decreased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 1.47 , HR at Year 10 = 0.8 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher debt_ratio is associated with increased bankruptcy risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 35 should focus on reducing their LTMTA to minimize bankruptcy risk.

  Industry Sector 45 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 27.2 , HR at Year 10 = 101.94 
      Pattern: risk-increasing effect strengthens 
      Interpretation: Higher Liabilities-to-Market Value is associated with increased bankruptcy risk, and this effect becomes stronger over time
    • KEY FACTOR: Stock Price 
      Initial HR = 0.67 , HR at Year 10 = 0.8 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher Stock Price is associated with decreased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 0.19 , HR at Year 10 = 0.81 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher debt_ratio is associated with decreased bankruptcy risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 45 should focus on reducing their LTMTA to minimize bankruptcy risk.

  Industry Sector 25 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 21.21 , HR at Year 10 = 66.27 
      Pattern: risk-increasing effect strengthens 
      Interpretation: Higher Liabilities-to-Market Value is associated with increased bankruptcy risk, and this effect becomes stronger over time
    • KEY FACTOR: Stock Price 
      Initial HR = 0.63 , HR at Year 10 = 0.63 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher Stock Price is associated with decreased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 0.62 , HR at Year 10 = 0.79 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher debt_ratio is associated with decreased bankruptcy risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 25 should focus on reducing their LTMTA to minimize bankruptcy risk.

  Industry Sector 15 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 0 , HR at Year 10 = 418711389019 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher Liabilities-to-Market Value is associated with decreased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 4.17 , HR at Year 10 = 0.43 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher debt_ratio is associated with increased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: Stock Price 
      Initial HR = 0.99 , HR at Year 10 = 0.66 
      Pattern: risk-decreasing effect strengthens 
      Interpretation: Higher Stock Price is associated with decreased bankruptcy risk, and this effect becomes stronger over time
    • SECTOR RECOMMENDATION: Companies in Sector 15 should focus on maintaining strong LTMTA to protect against bankruptcy risk.

  Industry Sector 50 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 751.09 , HR at Year 10 = 1219.04 
      Pattern: risk-increasing effect strengthens 
      Interpretation: Higher Liabilities-to-Market Value is associated with increased bankruptcy risk, and this effect becomes stronger over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 15.95 , HR at Year 10 = 0.23 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher debt_ratio is associated with increased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: Stock Price 
      Initial HR = 2.46 , HR at Year 10 = 0.44 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher Stock Price is associated with increased bankruptcy risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 50 should focus on reducing their LTMTA to minimize bankruptcy risk.

  Industry Sector 10 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 2347.67 , HR at Year 10 = 1148.53 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher Liabilities-to-Market Value is associated with increased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 4.84 , HR at Year 10 = 0.11 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher debt_ratio is associated with increased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: Stock Price 
      Initial HR = 1.09 , HR at Year 10 = 0.49 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher Stock Price is associated with increased bankruptcy risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 10 should focus on reducing their LTMTA to minimize bankruptcy risk.

  Industry Sector 30 :
    • KEY FACTOR: Liabilities-to-Market Value 
      Initial HR = 0 , HR at Year 10 = 6.44179e+38 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher Liabilities-to-Market Value is associated with decreased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: debt_ratio 
      Initial HR = 490.97 , HR at Year 10 = 0 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher debt_ratio is associated with increased bankruptcy risk, but this effect diminishes over time
    • KEY FACTOR: Stock Price 
      Initial HR = 2.55 , HR at Year 10 = 0.09 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher Stock Price is associated with increased bankruptcy risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 30 should focus on maintaining strong LTMTA to protect against bankruptcy risk.

Key Financial Risk Factors by Industry Sector - Acquisition Model:

  Industry Sector 20 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 0.51 , HR at Year 10 = 0.63 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher wc_ratio is associated with decreased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 1.04 , HR at Year 10 = 1.11 
      Pattern: risk-increasing effect strengthens 
      Interpretation: Higher asset_turnover is associated with increased acquisition risk, and this effect becomes stronger over time
    • SECTOR RECOMMENDATION: Companies in Sector 20 looking to avoid acquisition should focus on strategically positioning their wc_ratio metrics.

  Industry Sector 35 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 0.44 , HR at Year 10 = 0.77 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher wc_ratio is associated with decreased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 0.98 , HR at Year 10 = 1.03 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher asset_turnover is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 35 looking to avoid acquisition should focus on strategically positioning their wc_ratio metrics.

  Industry Sector 45 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 0.44 , HR at Year 10 = 0.7 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher wc_ratio is associated with decreased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 0.59 , HR at Year 10 = 0.8 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher asset_turnover is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 45 looking to avoid acquisition should focus on strategically positioning their wc_ratio metrics.

  Industry Sector 25 :
    • KEY FACTOR: asset_turnover 
      Initial HR = 0.81 , HR at Year 10 = 0.86 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher asset_turnover is associated with decreased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: wc_ratio 
      Initial HR = 0.55 , HR at Year 10 = 0.87 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher wc_ratio is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 25 looking to avoid acquisition should focus on strategically positioning their asset_turnover metrics.

  Industry Sector 55 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 0.82 , HR at Year 10 = 856.01 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher wc_ratio is associated with decreased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 0.22 , HR at Year 10 = 0.47 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher asset_turnover is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 55 looking to avoid acquisition should focus on strategically positioning their wc_ratio metrics.

  Industry Sector 15 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 1.68 , HR at Year 10 = 1.75 
      Pattern: risk-increasing effect strengthens 
      Interpretation: Higher wc_ratio is associated with increased acquisition risk, and this effect becomes stronger over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 1.4 , HR at Year 10 = 1.31 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher asset_turnover is associated with increased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 15 looking to avoid acquisition should focus on managing their wc_ratio levels.

  Industry Sector 50 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 0.54 , HR at Year 10 = 0.66 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher wc_ratio is associated with decreased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 0.94 , HR at Year 10 = 1.06 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher asset_turnover is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 50 looking to avoid acquisition should focus on strategically positioning their wc_ratio metrics.

  Industry Sector 10 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 1.11 , HR at Year 10 = 0.53 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher wc_ratio is associated with increased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 0.64 , HR at Year 10 = 0.89 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher asset_turnover is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 10 looking to avoid acquisition should focus on managing their wc_ratio levels.

  Industry Sector 30 :
    • KEY FACTOR: wc_ratio 
      Initial HR = 1.17 , HR at Year 10 = 0.88 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher wc_ratio is associated with increased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: asset_turnover 
      Initial HR = 0.83 , HR at Year 10 = 0.88 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher asset_turnover is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 30 looking to avoid acquisition should focus on managing their wc_ratio levels.

  Industry Sector 60 :
    • KEY FACTOR: asset_turnover 
      Initial HR = 7.13 , HR at Year 10 = 0 
      Pattern: risk-increasing effect weakens 
      Interpretation: Higher asset_turnover is associated with increased acquisition risk, but this effect diminishes over time
    • KEY FACTOR: wc_ratio 
      Initial HR = 0 , HR at Year 10 = 0 
      Pattern: risk-decreasing effect weakens 
      Interpretation: Higher wc_ratio is associated with decreased acquisition risk, but this effect diminishes over time
    • SECTOR RECOMMENDATION: Companies in Sector 60 looking to avoid acquisition should focus on managing their asset_turnover levels.

CURE MODELS!

This part is explorative into the application of cure models to the bankruptcy and acquisition data. Cure models are used to analyze survival data where a proportion of subjects are “cured” or immune to the event of interest. In this case, we will explore the use of cure models to understand the long-term survival of companies in terms of bankruptcy and acquisition risks.

#-------------------------------------------------------------
# STEP 1: Load Required Packages for Cure Models
#-------------------------------------------------------------
# Required packages for cure models
required_packages <- c(
  "smcure", "flexsurvcure", "survival", "dplyr", "ggplot2", "survminer"
)

# Install missing packages
missing_packages <- required_packages[!required_packages %in% installed.packages()[,"Package"]]
if(length(missing_packages) > 0) {
  install.packages(missing_packages)
}

# Load packages
suppressPackageStartupMessages({
  library(smcure)        # For semi-parametric mixture cure models
  library(flexsurvcure)  # For parametric mixture cure models
  library(survival)      # For survival models
  library(dplyr)         # For data manipulation
  library(ggplot2)       # For data visualization
  library(survminer)     # For survival analysis visualizations
})
#-------------------------------------------------------------
# STEP 1: Load Required Packages for Cure Models
#-------------------------------------------------------------
# Required packages for cure models
required_packages <- c(
  "smcure", "flexsurvcure", "survival", "dplyr", "ggplot2", "survminer"
)

# Install missing packages
missing_packages <- required_packages[!required_packages %in% installed.packages()[,"Package"]]
if(length(missing_packages) > 0) {
  install.packages(missing_packages)
}

# Load packages
suppressPackageStartupMessages({
  library(smcure)        # For semi-parametric mixture cure models
  library(flexsurvcure)  # For parametric mixture cure models
  library(survival)      # For survival models
  library(dplyr)         # For data manipulation
  library(ggplot2)       # For data visualization
  library(survminer)     # For survival analysis visualizations
})

#-------------------------------------------------------------
# STEP 2: Data Preparation for Cure Models
#-------------------------------------------------------------
# Cure models require one row per subject, so we'll use the last observation for each company
data_cure <- data %>%
  group_by(cusip) %>%
  slice_max(tstop) %>%
  ungroup()

# Check the data structure
cat("Data dimensions for cure model analysis:", dim(data_cure), "\n")
Data dimensions for cure model analysis: 5038 43 
cat("Event frequencies:\n")
Event frequencies:
print(table(data_cure$event_type))

   0    1    2 
2708  176 2154 
print(table(data_cure$bankruptcy))

   0    1 
4862  176 
print(table(data_cure$acquisition))

   0    1 
2884 2154 
# Create separate datasets for bankruptcy and acquisition
data_bankruptcy <- data_cure
data_acquisition <- data_cure

# Note: 'smcure' requires time > 0, event indicator (0/1), and covariates
# Check for zero or negative times
cat("Min time to event:", min(data_cure$tstop), "\n")
Min time to event: 0.9965777 
# Verify that key variables exist in the dataset
cat("Checking for key financial variables...\n")
Checking for key financial variables...
key_financial <- c("LTMTA", "NIMTA", "CASHMTA", "PRICE", "RSIZE")
existing_vars <- intersect(key_financial, names(data_cure))
cat("Available key financial variables:", paste(existing_vars, collapse=", "), "\n")
Available key financial variables: LTMTA, NIMTA, CASHMTA, PRICE, RSIZE 
# Define variables to use for cure models
bankruptcy_fin_vars <- intersect(c("LTMTA", "NIMTA", "CASHMTA", "PRICE"), names(data_cure))
acquisition_fin_vars <- intersect(c("RSIZE", "LTMTA", "PRICE"), names(data_cure))
macro_vars <- intersect(c("gdp_growth", "unemployement", "gdp_deflator"), names(data_cure))

cat("Variables to be used in bankruptcy cure model:", 
    paste(c(bankruptcy_fin_vars, macro_vars), collapse=", "), "\n")
Variables to be used in bankruptcy cure model: LTMTA, NIMTA, CASHMTA, PRICE, gdp_growth, unemployement, gdp_deflator 
cat("Variables to be used in acquisition cure model:", 
    paste(c(acquisition_fin_vars, macro_vars), collapse=", "), "\n")
Variables to be used in acquisition cure model: RSIZE, LTMTA, PRICE, gdp_growth, unemployement, gdp_deflator 
# No need for imputation since there are no missing values
cat("No missing values - imputation not needed.\n")
No missing values - imputation not needed.
# STEP 3: Exploratory Analysis for Cure Fraction
#-------------------------------------------------------------
# The survival curves we've seen already provide strong evidence of a cure fraction,
# especially for bankruptcy. Let's quantify this with Kaplan-Meier plateau estimates.

# Create Kaplan-Meier curves
km_bankruptcy <- survfit(Surv(tstop, bankruptcy) ~ 1, data = data_bankruptcy)
km_acquisition <- survfit(Surv(tstop, acquisition) ~ 1, data = data_acquisition)

# Plot the curves with emphasis on plateaus
par(mfrow = c(1, 2))
plot(km_bankruptcy, conf.int = TRUE, xlab = "Years", ylab = "Probability of No Bankruptcy",
     main = "Bankruptcy-Free Survival with Potential Cure Fraction")
abline(h = tail(km_bankruptcy$surv, 1), col = "red", lty = 2)
text(max(km_bankruptcy$time) * 0.7, tail(km_bankruptcy$surv, 1) + 0.02, 
     paste0("Estimated cure fraction: ", round(tail(km_bankruptcy$surv, 1) * 100, 1), "%"),
     col = "red")

plot(km_acquisition, conf.int = TRUE, xlab = "Years", ylab = "Probability of No Acquisition",
     main = "Acquisition-Free Survival with Potential Cure Fraction")
abline(h = tail(km_acquisition$surv, 1), col = "blue", lty = 2)
text(max(km_acquisition$time) * 0.7, tail(km_acquisition$surv, 1) + 0.02, 
     paste0("Estimated cure fraction: ", round(tail(km_acquisition$surv, 1) * 100, 1), "%"),
     col = "blue")
par(mfrow = c(1, 1))


# Quantify the plateau (estimated cure fraction)
bankruptcy_plateau <- tail(km_bankruptcy$surv, 1)
acquisition_plateau <- tail(km_acquisition$surv, 1)

cat("\nEstimated cure fractions from KM curves:\n")

Estimated cure fractions from KM curves:
cat("Bankruptcy: Approximately", round(bankruptcy_plateau * 100, 1), "% of companies may never go bankrupt\n")
Bankruptcy: Approximately 90.2 % of companies may never go bankrupt
cat("Acquisition: Approximately", round(acquisition_plateau * 100, 1), "% of companies may never be acquired\n")
Acquisition: Approximately 28.1 % of companies may never be acquired
# Stratified analysis by industry
km_bankruptcy_sector <- survfit(Surv(tstop, bankruptcy) ~ gsector, data = data_bankruptcy)
km_acquisition_sector <- survfit(Surv(tstop, acquisition) ~ gsector, data = data_acquisition)

# Function to safely extract the last survival probability for each stratum
extract_last_surv_prob <- function(km_fit) {
  # Get summary
  km_sum <- summary(km_fit)
  
  # Extract strata information
  strata_info <- km_sum$strata
  if (is.null(strata_info)) return(tail(km_sum$surv, 1))
  
  # Initialize result vector
  plateau_values <- numeric(length(strata_info))
  names(plateau_values) <- names(strata_info)
  
  # Need to manually extract the last survival probability for each stratum
  strata_names <- names(strata_info)
  
  # Loop through each stratum
  for (i in seq_along(strata_names)) {
    # Get subset of data for this stratum
    strata_indices <- which(km_sum$strata == strata_names[i])
    
    # Extract last survival probability if available
    if (length(strata_indices) > 0) {
      plateau_values[i] <- km_sum$surv[max(strata_indices)]
    } else {
      plateau_values[i] <- NA
    }
  }
  
  return(plateau_values)
}

# Get plateau estimates
bk_plateaus_by_sector <- extract_last_surv_prob(km_bankruptcy_sector)
acq_plateaus_by_sector <- extract_last_surv_prob(km_acquisition_sector)
LS0tDQp0aXRsZTogIlN1cnZpdmFsIEFuYWx5c2lzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQpQcmVwYXJpbmcgcGFja2FnZXMgYW5kIGRhdGENCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1RFUCAxOiBMb2FkIFJlcXVpcmVkIExpYnJhcmllcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgUmVxdWlyZWQgcGFja2FnZXMNCnJlcXVpcmVkX3BhY2thZ2VzIDwtIGMoDQogICJzdXJ2aXZhbCIsICJkcGx5ciIsICJnZ3Bsb3QyIiwgInN1cnZtaW5lciIsICJ0aWR5ciIsIA0KICAiY2FyIiwgInBST0MiLCAiUk9DUiIsICJ0aW1lUk9DIg0KKQ0KDQojIEluc3RhbGwgbWlzc2luZyBwYWNrYWdlcw0KbWlzc2luZ19wYWNrYWdlcyA8LSByZXF1aXJlZF9wYWNrYWdlc1shcmVxdWlyZWRfcGFja2FnZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssIlBhY2thZ2UiXV0NCmlmKGxlbmd0aChtaXNzaW5nX3BhY2thZ2VzKSA+IDApIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhtaXNzaW5nX3BhY2thZ2VzKQ0KfQ0KDQojIExvYWQgcGFja2FnZXMNCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7DQogIGxpYnJhcnkoc3Vydml2YWwpICAgICAgIyBGb3Igc3Vydml2YWwgbW9kZWxzDQogIGxpYnJhcnkoZHBseXIpICAgICAgICAgIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24NCiAgbGlicmFyeShnZ3Bsb3QyKSAgICAgICAjIEZvciBkYXRhIHZpc3VhbGl6YXRpb24NCiAgbGlicmFyeShzdXJ2bWluZXIpICAgICAjIEZvciBzdXJ2aXZhbCBhbmFseXNpcyB2aXN1YWxpemF0aW9ucw0KICBsaWJyYXJ5KHRpZHlyKSAgICAgICAgICMgRm9yIGRhdGEgdGlkeWluZw0KICBsaWJyYXJ5KGNhcikgICAgICAgICAgICMgRm9yIFZJRiBhbmFseXNpcw0KICBsaWJyYXJ5KHBST0MpICAgICAgICAgICMgRm9yIFJPQyBhbmFseXNpcw0KICBsaWJyYXJ5KFJPQ1IpICAgICAgICAgICMgRm9yIFJPQyBjdXJ2ZXMgYW5kIEFVQyANCiAgbGlicmFyeSh0aW1lUk9DKSAgICAgICAjIEZvciB0aW1lLWRlcGVuZGVudCBST0MgY3VydmVzDQp9KQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMjogRGF0YSBMb2FkaW5nIGFuZCBJbml0aWFsIEluc3BlY3Rpb24NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFJlYWQgdGhlIGRhdGFzZXQNCmRhdGEgPC0gcmVhZC5jc3YoIjEuMy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgRGlzcGxheSBiYXNpYyBkYXRhIGluZm9ybWF0aW9uDQpjYXQoIkRhdGEgZGltZW5zaW9uczoiLCBkaW0oZGF0YSksICJcbiIpDQpwcmludChoZWFkKGRwbHlyOjpzZWxlY3QoZGF0YSwgY3VzaXAsIGdzZWN0b3IsIHN0YXJ0LCBzdG9wLCBldmVudF90eXBlKSwgbiA9IDUpKQ0KDQojIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcyBpbiBrZXkgY29sdW1ucw0KbWlzc2luZ19rZXkgPC0gY29sU3Vtcyhpcy5uYShkYXRhWywgYygiY3VzaXAiLCAiZ3NlY3RvciIsICJzdGFydCIsICJzdG9wIiwgImV2ZW50X3R5cGUiKV0pKQ0KaWYoc3VtKG1pc3Npbmdfa2V5KSA+IDApIHsNCiAgY2F0KCJXQVJOSU5HOiBNaXNzaW5nIHZhbHVlcyBpbiBrZXkgY29sdW1uczpcbiIpDQogIHByaW50KG1pc3Npbmdfa2V5W21pc3Npbmdfa2V5ID4gMF0pDQp9DQoNCiMgQ2hlY2sgZm9yIGR1cGxpY2F0ZWQgcm93cw0KcG90ZW50aWFsX2R1cGxpY2F0ZXMgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoY3VzaXAsIHN0YXJ0LCBzdG9wKSAlPiUNCiAgZmlsdGVyKG4oKSA+IDEpICU+JQ0KICB1bmdyb3VwKCkNCg0KaWYobnJvdyhwb3RlbnRpYWxfZHVwbGljYXRlcykgPiAwKSB7DQogIGNhdCgiV0FSTklORzoiLCBucm93KHBvdGVudGlhbF9kdXBsaWNhdGVzKSwgInBvdGVudGlhbCBkdXBsaWNhdGUgZW50cmllcyBmb3VuZC5cbiIpDQogIA0KICAjIERpc3BsYXkgdGhlIGZpcnN0IGZldyBkdXBsaWNhdGVzIGZvciBpbnNwZWN0aW9uDQogIGNhdCgiU2FtcGxlIG9mIGR1cGxpY2F0ZSByZWNvcmRzOlxuIikNCiAgZHVwbGljYXRlX3NhbXBsZSA8LSBwb3RlbnRpYWxfZHVwbGljYXRlcyAlPiUNCiAgICBkcGx5cjo6c2VsZWN0KGN1c2lwLCBnc2VjdG9yLCBzdGFydCwgc3RvcCwgZXZlbnRfdHlwZSkgJT4lDQogICAgaGVhZCgxMCkNCiAgcHJpbnQoZHVwbGljYXRlX3NhbXBsZSkNCiAgDQogICMgRm9yIGEgbW9yZSBkZXRhaWxlZCBpbnNwZWN0aW9uIG9mIGV4YWN0IGR1cGxpY2F0ZXMNCiAgZXhhY3RfZHVwbGljYXRlcyA8LSBkYXRhW2R1cGxpY2F0ZWQoZGF0YSkgfCBkdXBsaWNhdGVkKGRhdGEsIGZyb21MYXN0ID0gVFJVRSksIF0NCiAgY2F0KCJcbkV4YWN0IGR1cGxpY2F0ZSByb3dzIChhbGwgY29sdW1ucyBpZGVudGljYWwpOiIsIG5yb3coZXhhY3RfZHVwbGljYXRlcyksICJcbiIpDQogIGlmKG5yb3coZXhhY3RfZHVwbGljYXRlcykgPiAwKSB7DQogICAgY2F0KCJTYW1wbGUgb2YgZXhhY3QgZHVwbGljYXRlczpcbiIpDQogICAgZXhhY3RfZHVwX3NhbXBsZSA8LSBleGFjdF9kdXBsaWNhdGVzICU+JQ0KICAgICAgZHBseXI6OnNlbGVjdChjdXNpcCwgZ3NlY3Rvciwgc3RhcnQsIHN0b3AsIGV2ZW50X3R5cGUpICU+JQ0KICAgICAgaGVhZCg1KQ0KICAgIHByaW50KGV4YWN0X2R1cF9zYW1wbGUpDQogIH0NCiAgDQogICMgQXNrIHVzZXIgd2hldGhlciB0byByZW1vdmUgZHVwbGljYXRlcw0KICBjYXQoIlxuRHVwbGljYXRlcyBjYW4gYmUgY2F1c2VkIGJ5OlxuIikNCiAgY2F0KCIxLiBFeGFjdCBzYW1lIHJlY29yZCBlbnRlcmVkIHR3aWNlIChzYWZlIHRvIHJlbW92ZSlcbiIpDQogIGNhdCgiMi4gU2FtZSB0aW1lIHBlcmlvZCBidXQgZGlmZmVyZW50IGZpbmFuY2lhbCB2YWx1ZXMgKHJlcXVpcmVzIGluc3BlY3Rpb24pXG4iKQ0KICBjYXQoIjMuIERhdGEgZW50cnkgZXJyb3JzIChtYXkgbmVlZCBjb3JyZWN0aW9uLCBub3QganVzdCByZW1vdmFsKVxuXG4iKQ0KICANCiAgIyBSZW1vdmUgZHVwbGljYXRlcyBhZnRlciBpbnNwZWN0aW9uIC0gYnkgZGVmYXVsdCB3ZSdsbCBrZWVwIHRoZSBmaXJzdCBvY2N1cnJlbmNlDQogIGNhdCgiUmVtb3ZpbmcgZHVwbGljYXRlcyBhbmQga2VlcGluZyBmaXJzdCBvY2N1cnJlbmNlIG9mIGVhY2ggcmVjb3JkLlxuIikNCiAgZGF0YSA8LSBkaXN0aW5jdChkYXRhKQ0KICBjYXQoIkFmdGVyIHJlbW92aW5nIGR1cGxpY2F0ZXMsIGRhdGEgZGltZW5zaW9uczoiLCBkaW0oZGF0YSksICJcbiIpDQp9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1RFUCAzOiBEYXRhIFByZXByb2Nlc3NpbmcgKE1PRElGSUVEKQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgQ29udmVydCBkYXRlIHN0cmluZ3MgdG8gRGF0ZSBvYmplY3RzIGFuZCBlbnN1cmUgcHJvcGVyIHRpbWUgaW50ZXJ2YWxzDQpkYXRhIDwtIGRhdGEgJT4lDQogICMgQ29udmVydCBkYXRlIHN0cmluZ3MgdG8gRGF0ZSBvYmplY3RzDQogIG11dGF0ZSgNCiAgICBzdGFydF9kYXRlID0gYXMuRGF0ZShzdGFydCksDQogICAgc3RvcF9kYXRlID0gYXMuRGF0ZShzdG9wKQ0KICApICU+JQ0KICAjIEZpbHRlciBvdXQgcmVjb3JkcyB3aXRoIGludmFsaWQgZGF0ZXMNCiAgZmlsdGVyKCFpcy5uYShzdGFydF9kYXRlKSAmICFpcy5uYShzdG9wX2RhdGUpICYgc3RhcnRfZGF0ZSA8PSBzdG9wX2RhdGUpDQoNCiMgQ2FsY3VsYXRlIHRpbWUgaW50ZXJ2YWxzIGluIHllYXJzIGZyb20gQ09NUEFOWS1TUEVDSUZJQyBvcmlnaW5zDQpkYXRhIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgbXV0YXRlKA0KICAgICMgRmluZCBlYWNoIGNvbXBhbnkncyBmaXJzdCBhcHBlYXJhbmNlIGRhdGUNCiAgICBjb21wYW55X3N0YXJ0X2RhdGUgPSBtaW4oc3RhcnRfZGF0ZSksDQogICAgDQogICAgIyBDYWxjdWxhdGUgdGltZSBzaW5jZSBJUE8gZm9yIHN0YXJ0IGFuZCBzdG9wDQogICAgdHN0YXJ0ID0gYXMubnVtZXJpYyhzdGFydF9kYXRlIC0gY29tcGFueV9zdGFydF9kYXRlKSAvIDM2NS4yNSwNCiAgICB0c3RvcCA9IGFzLm51bWVyaWMoc3RvcF9kYXRlIC0gY29tcGFueV9zdGFydF9kYXRlKSAvIDM2NS4yNQ0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogICMgRW5zdXJlIGdzZWN0b3IgaXMgYSBmYWN0b3INCiAgbXV0YXRlKGdzZWN0b3IgPSBhcy5mYWN0b3IoZ3NlY3RvcikpDQoNCiMgQ3JlYXRlIGV2ZW50IGluZGljYXRvcnMgZm9yIGNhdXNlLXNwZWNpZmljIGFuYWx5c2lzDQpkYXRhIDwtIGRhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBiYW5rcnVwdGN5ID0gaWZlbHNlKGV2ZW50X3R5cGUgPT0gMSwgMSwgMCksDQogICAgYWNxdWlzaXRpb24gPSBpZmVsc2UoZXZlbnRfdHlwZSA9PSAyLCAxLCAwKQ0KICApDQpgYGANCkFsbCBjb21wYW5pZXMgYXJlIGJyb3VnaHQgYmFjayB0byB0aW1lIHQ9MCwgd2hpY2ggaXMgdGhlIGRhdGUgb2YgdGhlaXIgSVBPLiBUaGUgdGltZSBpbnRlcnZhbHMgYXJlIGNhbGN1bGF0ZWQgaW4geWVhcnMgZnJvbSB0aGlzIGRhdGUuDQoNCk1hY3JvIFZhcmlhYmxlcyBhcmUgaW50cm9kdWNlZCBieSBtYXRjaGluZyB0aGUgY2FsZW5kYXIgeWVhciBvZiB0aGUgbWFjcm8gZGF0YSB3aXRoIHRoZSBjYWxlbmRhciB5ZWFyIG9mIHRoZSBjb21wYW55IGRhdGEuIFRoZSBtYWNybyBkYXRhIGlzIHJlc2hhcGVkIGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgYW5kIG1pc3NpbmcgdmFsdWVzIGFyZSBoYW5kbGVkIGJ5IHVzaW5nIHRoZSBjbG9zZXN0IGF2YWlsYWJsZSB5ZWFyLg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMy41OiBBZGQgTWFjcm9lY29ub21pYyBWYXJpYWJsZXMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIExvYWQgbWFjcm8gZGF0YQ0KbWFjcm9fZGF0YSA8LSByZWFkLmNzdigibWFjcm8uY3N2IiwgY2hlY2submFtZXMgPSBGQUxTRSkNCg0KIyBGaXJzdCwgbGV0J3MgZXhhbWluZSB0aGUgc3RydWN0dXJlIG9mIHRoZSBtYWNybyBkYXRhDQpjYXQoIlN0cnVjdHVyZSBvZiBtYWNybyBkYXRhOlxuIikNCnN0cihtYWNyb19kYXRhKQ0KDQojIFJlc2hhcGUgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQptYWNyb19sb25nIDwtIG1hY3JvX2RhdGEgJT4lDQogICMgUGl2b3QgdG8gbG9uZyBmb3JtYXQgLSBhbGwgY29sdW1ucyBleGNlcHQgJ2NvZGUnDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gLWNvZGUsDQogICAgbmFtZXNfdG8gPSAieWVhciIsDQogICAgdmFsdWVzX3RvID0gInZhbHVlIg0KICApICU+JQ0KICAjIENvbnZlcnQgeWVhciB0byBudW1lcmljDQogIG11dGF0ZSh5ZWFyID0gYXMubnVtZXJpYyh5ZWFyKSkgJT4lDQogICMgUGl2b3Qgd2lkZXIgdG8gZ2V0IG9uZSByb3cgcGVyIHllYXIgd2l0aCBjb2x1bW5zIGZvciBlYWNoIG1hY3JvIHZhcmlhYmxlDQogIHBpdm90X3dpZGVyKA0KICAgIGlkX2NvbHMgPSB5ZWFyLA0KICAgIG5hbWVzX2Zyb20gPSBjb2RlLA0KICAgIHZhbHVlc19mcm9tID0gdmFsdWUNCiAgKQ0KDQojIERpc3BsYXkgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByZXNoYXBlZCBkYXRhDQpjYXQoIlxuRmlyc3QgZmV3IHJvd3Mgb2YgcmVzaGFwZWQgbWFjcm8gZGF0YTpcbiIpDQpwcmludChoZWFkKG1hY3JvX2xvbmcpKQ0KDQojIEV4dHJhY3QgY2FsZW5kYXIgeWVhciBmcm9tIGRhdGUgZm9yIGpvaW5pbmcNCmRhdGEgPC0gZGF0YSAlPiUNCiAgbXV0YXRlKGNhbGVuZGFyX3llYXIgPSBhcy5udW1lcmljKGZvcm1hdChzdGFydF9kYXRlLCAiJVkiKSkpDQoNCiMgQ2hlY2sgdGhlIHJhbmdlIG9mIHllYXJzIGluIGJvdGggZGF0YXNldHMNCmNhdCgiXG5SYW5nZSBvZiB5ZWFycyBpbiBjb21wYW55IGRhdGE6IiwgcmFuZ2UoZGF0YSRjYWxlbmRhcl95ZWFyKSwgIlxuIikNCmNhdCgiUmFuZ2Ugb2YgeWVhcnMgaW4gbWFjcm8gZGF0YToiLCByYW5nZShtYWNyb19sb25nJHllYXIpLCAiXG4iKQ0KDQojIEpvaW4gbWFjcm8gZGF0YSB0byBjb21wYW55IGRhdGENCmRhdGEgPC0gZGF0YSAlPiUNCiAgbGVmdF9qb2luKG1hY3JvX2xvbmcsIGJ5ID0gYygiY2FsZW5kYXJfeWVhciIgPSAieWVhciIpKQ0KDQojIENoZWNrIGlmIG1hY3JvIHZhcmlhYmxlcyB3ZXJlIHN1Y2Nlc3NmdWxseSBhZGRlZA0KY2F0KCJcbkNoZWNraW5nIGlmIG1hY3JvIHZhcmlhYmxlcyB3ZXJlIGFkZGVkIHN1Y2Nlc3NmdWxseTpcbiIpDQptYWNyb192YXJzIDwtIGMoImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IiwgImdkcF9ncm93dGgiKQ0KbmFfY291bnQgPC0gY29sU3Vtcyhpcy5uYShkYXRhWywgbWFjcm9fdmFyc10pKQ0KcHJpbnQobmFfY291bnQpDQoNCiMgSWYgdGhlcmUgYXJlIG1pc3NpbmcgdmFsdWVzLCBoYW5kbGUgdGhlbQ0KaWYoc3VtKG5hX2NvdW50KSA+IDApIHsNCiAgY2F0KCJcbkhhbmRsaW5nIG1pc3NpbmcgbWFjcm8gdmFyaWFibGUgdmFsdWVzLi4uXG4iKQ0KICANCiAgIyBGb3IgYW55IG1pc3NpbmcgeWVhciBpbiB0aGUgbWFjcm8gZGF0YSwgdXNlIHRoZSBjbG9zZXN0IGF2YWlsYWJsZSB5ZWFyDQogIGZvcih2YXIgaW4gbWFjcm9fdmFycykgew0KICAgICMgR2V0IHllYXJzIHdpdGggYXZhaWxhYmxlIGRhdGENCiAgICBhdmFpbGFibGVfeWVhcnMgPC0gbWFjcm9fbG9uZyR5ZWFyWyFpcy5uYShtYWNyb19sb25nW1t2YXJdXSldDQogICAgDQogICAgIyBGb3IgZWFjaCBjb21wYW55LXllYXIgd2l0aCBtaXNzaW5nIGRhdGENCiAgICBtaXNzaW5nX3Jvd3MgPC0gd2hpY2goaXMubmEoZGF0YVtbdmFyXV0pKQ0KICAgIA0KICAgIGlmKGxlbmd0aChtaXNzaW5nX3Jvd3MpID4gMCkgew0KICAgICAgY2F0KCJJbXB1dGluZyBtaXNzaW5nIHZhbHVlcyBmb3IiLCB2YXIsICIuLi5cbiIpDQogICAgICANCiAgICAgIGZvcihpIGluIG1pc3Npbmdfcm93cykgew0KICAgICAgICBjb21wYW55X3llYXIgPC0gZGF0YSRjYWxlbmRhcl95ZWFyW2ldDQogICAgICAgIA0KICAgICAgICAjIEZpbmQgY2xvc2VzdCBhdmFpbGFibGUgeWVhcg0KICAgICAgICBjbG9zZXN0X3llYXIgPC0gYXZhaWxhYmxlX3llYXJzW3doaWNoLm1pbihhYnMoYXZhaWxhYmxlX3llYXJzIC0gY29tcGFueV95ZWFyKSldDQogICAgICAgIA0KICAgICAgICAjIEdldCB0aGUgdmFsdWUgZnJvbSB0aGF0IHllYXINCiAgICAgICAgcmVwbGFjZW1lbnRfdmFsdWUgPC0gbWFjcm9fbG9uZ1tbdmFyXV1bbWFjcm9fbG9uZyR5ZWFyID09IGNsb3Nlc3RfeWVhcl0NCiAgICAgICAgDQogICAgICAgICMgUmVwbGFjZSBtaXNzaW5nIHZhbHVlDQogICAgICAgIGRhdGFbW3Zhcl1dW2ldIDwtIHJlcGxhY2VtZW50X3ZhbHVlDQogICAgICB9DQogICAgfQ0KICB9DQogIA0KICAjIENoZWNrIGlmIGFsbCBtaXNzaW5nIHZhbHVlcyBhcmUgbm93IGhhbmRsZWQNCiAgbmFfY291bnRfYWZ0ZXIgPC0gY29sU3Vtcyhpcy5uYShkYXRhWywgbWFjcm9fdmFyc10pKQ0KICBjYXQoIlxuTWlzc2luZyB2YWx1ZXMgYWZ0ZXIgaW1wdXRhdGlvbjpcbiIpDQogIHByaW50KG5hX2NvdW50X2FmdGVyKQ0KfQ0KDQojIERlZmluZSBtYWNybyBjb3ZhcmlhdGVzIGZvciBtb2RlbGluZw0KbWFjcm9fY292YXJpYXRlcyA8LSBjKCJnZHBfZGVmbGF0b3IiLCAidW5lbXBsb3llbWVudCIsICJnZHBfZ3Jvd3RoIikNCg0KY2F0KCJcbk1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGhhdmUgYmVlbiBzdWNjZXNzZnVsbHkgaW50ZWdyYXRlZCBpbnRvIHRoZSBkYXRhc2V0LlxuIikNCmBgYA0KDQpUaW1lIGNvbnRpbnVpdHkgaXMgY2hlY2tlZCBieSBpZGVudGlmeWluZyBjb21wYW5pZXMgd2l0aCBnYXBzIG9yIG92ZXJsYXBzIGluIHRoZWlyIHRpbWUgaW50ZXJ2YWxzLiBJZiBhbnkgaXNzdWVzIGFyZSBmb3VuZCwgdGhlIHByb2JsZW1hdGljIGNvbXBhbmllcyBhcmUgcmVtb3ZlZCBmcm9tIHRoZSBkYXRhc2V0Lg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBTVEVQIDQ6IENoZWNrIGFuZCBGaXggVGltZSBDb250aW51aXR5DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBJZGVudGlmeSBjb21wYW5pZXMgd2l0aCB0aW1lIGlzc3VlcyAoZ2FwcyBvciBvdmVybGFwcykNCnRpbWVfaXNzdWVzIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgYXJyYW5nZShjdXNpcCwgdHN0YXJ0KSAlPiUNCiAgbXV0YXRlKA0KICAgICMgQ2FsY3VsYXRlIGdhcHMgYW5kIG92ZXJsYXBzDQogICAgZ2FwID0gdHN0YXJ0IC0gbGFnKHRzdG9wKSwNCiAgICBvdmVybGFwID0gbGFnKHRzdG9wKSAtIHRzdGFydA0KICApICU+JQ0KICAjIEZpbmQgcHJvYmxlbWF0aWMgcmVjb3Jkcw0KICBmaWx0ZXIoIWlzLm5hKGdhcCkgJiAoZ2FwID4gMC4wMSB8IG92ZXJsYXAgPiAwLjAxKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQppZihucm93KHRpbWVfaXNzdWVzKSA+IDApIHsNCiAgY2F0KCJXQVJOSU5HOiBGb3VuZCIsIG5yb3codGltZV9pc3N1ZXMpLCAicmVjb3JkcyB3aXRoIHRpbWUgY29udGludWl0eSBpc3N1ZXMuXG4iKQ0KICANCiAgIyBJZGVudGlmeSBjb21wYW5pZXMgd2l0aCBpc3N1ZXMNCiAgcHJvYmxlbV9jb21wYW5pZXMgPC0gdW5pcXVlKHRpbWVfaXNzdWVzJGN1c2lwKQ0KICBjYXQoIk51bWJlciBvZiBjb21wYW5pZXMgd2l0aCB0aW1lIGlzc3VlczoiLCBsZW5ndGgocHJvYmxlbV9jb21wYW5pZXMpLCAiXG4iKQ0KICANCiAgIyBPcHRpb24gMTogUmVtb3ZlIGNvbXBhbmllcyB3aXRoIHRpbWUgaXNzdWVzDQogIGRhdGFfY2xlYW4gPC0gZGF0YSAlPiUNCiAgICBmaWx0ZXIoIWN1c2lwICVpbiUgcHJvYmxlbV9jb21wYW5pZXMpDQogIA0KICAjIFVzZSB0aGUgY2xlYW5lZCBkYXRhc2V0DQogIGRhdGEgPC0gZGF0YV9jbGVhbg0KICBjYXQoIlVzaW5nIGNsZWFuZWQgZGF0YXNldCB3aXRoIiwgbGVuZ3RoKHVuaXF1ZShkYXRhJGN1c2lwKSksICJjb21wYW5pZXMgYWZ0ZXIgcmVtb3ZpbmcgcHJvYmxlbWF0aWMgb25lcy5cbiIpDQp9DQoNCiMgU2F2ZSB0aGUgY2xlYW5lZCBkYXRhZnJhbWUgYWZ0ZXIgdGltZSBjb250aW51aXR5IGNoZWNrcyB0byBDU1YNCndyaXRlLmNzdihkYXRhLCBmaWxlID0gImNveF9tb2RlbF9jbGVhbmVkX2RhdGEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpjYXQoIkNsZWFuZWQgZGF0YWZyYW1lIHNhdmVkIHRvICdjb3hfbW9kZWxfY2xlYW5lZF9kYXRhLmNzdidcbiIpDQpgYGANCkNvdmFyaWF0ZXMgYXJlIHByZXByb2Nlc3NlZCBieSBjaGVja2luZyBmb3IgbWlzc2luZyB2YWx1ZXMsIHdpbnNvcml6aW5nIGV4dHJlbWUgdmFsdWVzLCBhbmQgY2FsY3VsYXRpbmcgdGhlIEFsdG1hbiBaLXNjb3JlLiBUaGUgWi1zY29yZSBpcyBhZGRlZCB0byB0aGUgZmluYW5jaWFsIGNvdmFyaWF0ZXMgbGlzdCwgYW5kIHRoZSBmaW5hbCBsaXN0IG9mIGNvdmFyaWF0ZXMgaXMgcHJlcGFyZWQgZm9yIG1vZGVsaW5nLg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBTVEVQIDU6IEZpbmFuY2lhbCBhbmQgTWFjcm8gQ292YXJpYXRlcyBQcmVwcm9jZXNzaW5nDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBEZWZpbmUgZmluYW5jaWFsIGNvdmFyaWF0ZXMgYmFzZWQgb24geW91ciBkYXRhc2V0DQpmaW5hbmNpYWxfY292YXJpYXRlcyA8LSBjKA0KICAiTFRNVEEiLCAiTklNVEEiLCAiQ0FTSE1UQSIsICJQUklDRSIsICJNQkUiLCAiUlNJWkUiLCAibF9hdCIsICJsX21rdmFsdCIsIA0KICAiZGVidF9yYXRpbyIsICJkZWJ0X3NlcnZpY2UiLCAiY3VycmVudF9yYXRpbyIsICJxdWlja19yYXRpbyIsICJjYXNoX3RvX2Fzc2V0cyIsIA0KICAid2NfcmF0aW8iLCAiZWJpdF9tYXJnaW4iLCAiZ3BfbWFyZ2luIiwgImFzc2V0X3R1cm5vdmVyIiwgInJlY2VpdmFibGVzX3R1cm5vdmVyIiwgDQogICJpbnRhbmdpYmlsaXR5IiwgImViaXRfZ3Jvd3RoIiwgIkVCSVRfVk9MXzNZIg0KKQ0KDQojIERlZmluZSBtYWNybyBjb3ZhcmlhdGVzIHNlcGFyYXRlbHkNCm1hY3JvX2NvdmFyaWF0ZXMgPC0gYygiZ2RwX2RlZmxhdG9yIiwgInVuZW1wbG95ZW1lbnQiLCAiZ2RwX2dyb3d0aCIpDQoNCiMgQ3JlYXRlIGNvbWJpbmVkIGxpc3Qgd2hlbiBuZWVkZWQNCmFsbF9jb3ZhcmlhdGVzIDwtIGMoZmluYW5jaWFsX2NvdmFyaWF0ZXMsIG1hY3JvX2NvdmFyaWF0ZXMpDQoNCiMgQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzIGluIGZpbmFuY2lhbCBjb3ZhcmlhdGVzDQptaXNzaW5nX2ZpbmFuY2lhbHMgPC0gY29sU3Vtcyhpcy5uYShkYXRhWywgZmluYW5jaWFsX2NvdmFyaWF0ZXNdKSkNCmlmKHN1bShtaXNzaW5nX2ZpbmFuY2lhbHMpID4gMCkgew0KICBjYXQoIk1pc3NpbmcgdmFsdWVzIGluIGZpbmFuY2lhbCBjb3ZhcmlhdGVzOlxuIikNCiAgcHJpbnQobWlzc2luZ19maW5hbmNpYWxzKQ0KICANCiAgIyBJbXB1dGUgbWlzc2luZyB2YWx1ZXMgd2l0aCBtZWRpYW4gYnkgaW5kdXN0cnkgc2VjdG9yDQogIGRhdGEgPC0gZGF0YSAlPiUNCiAgICBncm91cF9ieShnc2VjdG9yKSAlPiUNCiAgICBtdXRhdGUoYWNyb3NzKGFsbF9vZihmaW5hbmNpYWxfY292YXJpYXRlcyksIA0KICAgICAgICAgICAgICAgICB+aWZlbHNlKGlzLm5hKC4pLCBtZWRpYW4oLiwgbmEucm0gPSBUUlVFKSwgLikpKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgQ2hlY2sgaWYgYW55IG1pc3NpbmcgdmFsdWVzIHJlbWFpbg0KICBzdGlsbF9taXNzaW5nIDwtIGNvbFN1bXMoaXMubmEoZGF0YVssIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXSkpDQogIGlmKHN1bShzdGlsbF9taXNzaW5nKSA+IDApIHsNCiAgICAjIEdsb2JhbCBtZWRpYW4gaW1wdXRhdGlvbiBmb3IgYW55IHJlbWFpbmluZyBtaXNzaW5nIHZhbHVlcw0KICAgIGRhdGEgPC0gZGF0YSAlPiUNCiAgICAgIG11dGF0ZShhY3Jvc3MoYWxsX29mKGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSwgDQogICAgICAgICAgICAgICAgICAgfmlmZWxzZShpcy5uYSguKSwgbWVkaWFuKC4sIG5hLnJtID0gVFJVRSksIC4pKSkNCiAgfQ0KfQ0KDQojIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcyBpbiBtYWNybyBjb3ZhcmlhdGVzDQptaXNzaW5nX21hY3JvIDwtIGNvbFN1bXMoaXMubmEoZGF0YVssIG1hY3JvX2NvdmFyaWF0ZXNdKSkNCmlmKHN1bShtaXNzaW5nX21hY3JvKSA+IDApIHsNCiAgY2F0KCJNaXNzaW5nIHZhbHVlcyBpbiBtYWNybyBjb3ZhcmlhdGVzOlxuIikNCiAgcHJpbnQobWlzc2luZ19tYWNybykNCiAgDQogICMgRm9yIG1hY3JvIHZhcmlhYmxlcywgaW1wdXRlIHdpdGggdGhlIGNsb3Nlc3QgYXZhaWxhYmxlIHllYXIgdmFsdWUNCiAgZm9yKHZhciBpbiBtYWNyb19jb3ZhcmlhdGVzKSB7DQogICAgaWYobWlzc2luZ19tYWNyb1t2YXJdID4gMCkgew0KICAgICAgIyBHZXQgYWxsIGF2YWlsYWJsZSB5ZWFycyBhbmQgdmFsdWVzDQogICAgICBhdmFpbGFibGVfZGF0YSA8LSBkYXRhWyFpcy5uYShkYXRhW1t2YXJdXSksIGMoImNhbGVuZGFyX3llYXIiLCB2YXIpXQ0KICAgICAgYXZhaWxhYmxlX3llYXJzIDwtIGF2YWlsYWJsZV9kYXRhJGNhbGVuZGFyX3llYXINCiAgICAgIA0KICAgICAgIyBGb3IgZWFjaCBtaXNzaW5nIHZhbHVlDQogICAgICBtaXNzaW5nX3Jvd3MgPC0gd2hpY2goaXMubmEoZGF0YVtbdmFyXV0pKQ0KICAgICAgZm9yKHJvdyBpbiBtaXNzaW5nX3Jvd3MpIHsNCiAgICAgICAgeWVhcl9uZWVkZWQgPC0gZGF0YSRjYWxlbmRhcl95ZWFyW3Jvd10NCiAgICAgICAgIyBGaW5kIGNsb3Nlc3QgeWVhcg0KICAgICAgICBjbG9zZXN0X3llYXJfaWR4IDwtIHdoaWNoLm1pbihhYnMoYXZhaWxhYmxlX3llYXJzIC0geWVhcl9uZWVkZWQpKQ0KICAgICAgICAjIEltcHV0ZSB3aXRoIHZhbHVlIGZyb20gY2xvc2VzdCB5ZWFyDQogICAgICAgIGRhdGFbW3Zhcl1dW3Jvd10gPC0gYXZhaWxhYmxlX2RhdGFbW3Zhcl1dW2Nsb3Nlc3RfeWVhcl9pZHhdDQogICAgICB9DQogICAgfQ0KICB9DQogIA0KICAjIENoZWNrIGlmIGFsbCBtaXNzaW5nIHZhbHVlcyBhcmUgbm93IGhhbmRsZWQNCiAgc3RpbGxfbWlzc2luZ19tYWNybyA8LSBjb2xTdW1zKGlzLm5hKGRhdGFbLCBtYWNyb19jb3ZhcmlhdGVzXSkpDQogIGlmKHN1bShzdGlsbF9taXNzaW5nX21hY3JvKSA+IDApIHsNCiAgICBjYXQoIldhcm5pbmc6IFNvbWUgbWFjcm8gdmFyaWFibGVzIHN0aWxsIGhhdmUgbWlzc2luZyB2YWx1ZXMgYWZ0ZXIgaW1wdXRhdGlvbi5cbiIpDQogICAgcHJpbnQoc3RpbGxfbWlzc2luZ19tYWNybykNCiAgfSBlbHNlIHsNCiAgICBjYXQoIkFsbCBtaXNzaW5nIG1hY3JvIHZhbHVlcyBzdWNjZXNzZnVsbHkgaW1wdXRlZC5cbiIpDQogIH0NCn0NCg0KIyBXaW5zb3JpemUgZXh0cmVtZSB2YWx1ZXMgZm9yIGZpbmFuY2lhbCBjb3ZhcmlhdGVzDQpjYXQoIldpbnNvcml6aW5nIGZpbmFuY2lhbCBjb3ZhcmlhdGVzLi4uXG4iKQ0KZm9yKHZhciBpbiBmaW5hbmNpYWxfY292YXJpYXRlcykgew0KICBwMDEgPC0gcXVhbnRpbGUoZGF0YVtbdmFyXV0sIDAuMDEsIG5hLnJtID0gVFJVRSkNCiAgcDk5IDwtIHF1YW50aWxlKGRhdGFbW3Zhcl1dLCAwLjk5LCBuYS5ybSA9IFRSVUUpDQogIA0KICBkYXRhW1t2YXJdXSA8LSBpZmVsc2UoZGF0YVtbdmFyXV0gPCBwMDEsIHAwMSwgZGF0YVtbdmFyXV0pDQogIGRhdGFbW3Zhcl1dIDwtIGlmZWxzZShkYXRhW1t2YXJdXSA+IHA5OSwgcDk5LCBkYXRhW1t2YXJdXSkNCn0NCg0KIyBXaW5zb3JpemUgZXh0cmVtZSB2YWx1ZXMgZm9yIG1hY3JvIGNvdmFyaWF0ZXMNCmNhdCgiV2luc29yaXppbmcgbWFjcm8gY292YXJpYXRlcy4uLlxuIikNCmZvcih2YXIgaW4gbWFjcm9fY292YXJpYXRlcykgew0KICBwMDEgPC0gcXVhbnRpbGUoZGF0YVtbdmFyXV0sIDAuMDEsIG5hLnJtID0gVFJVRSkNCiAgcDk5IDwtIHF1YW50aWxlKGRhdGFbW3Zhcl1dLCAwLjk5LCBuYS5ybSA9IFRSVUUpDQogIA0KICBkYXRhW1t2YXJdXSA8LSBpZmVsc2UoZGF0YVtbdmFyXV0gPCBwMDEsIHAwMSwgZGF0YVtbdmFyXV0pDQogIGRhdGFbW3Zhcl1dIDwtIGlmZWxzZShkYXRhW1t2YXJdXSA+IHA5OSwgcDk5LCBkYXRhW1t2YXJdXSkNCn0NCg0KIyBQcmludCBzdW1tYXJ5IHN0YXRpc3RpY3Mgc2VwYXJhdGVseSBmb3IgZmluYW5jaWFsIGFuZCBtYWNybyBjb3ZhcmlhdGVzDQpjYXQoIlxuU3VtbWFyeSBzdGF0aXN0aWNzIGZvciBmaW5hbmNpYWwgY292YXJpYXRlcyAoc2FtcGxlKTpcbiIpDQojIFNlbGVjdCBhIHJhbmRvbSBzdWJzZXQgb2YgZmluYW5jaWFsIHZhcmlhYmxlcyB0byBkaXNwbGF5DQpzYW1wbGVfZmluX3ZhcnMgPC0gc2FtcGxlKGZpbmFuY2lhbF9jb3ZhcmlhdGVzLCBtaW4oNSwgbGVuZ3RoKGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSkpDQpwcmludChzdW1tYXJ5KGRhdGFbLCBzYW1wbGVfZmluX3ZhcnNdKSkNCg0KY2F0KCJcblN1bW1hcnkgc3RhdGlzdGljcyBmb3IgbWFjcm8gY292YXJpYXRlczpcbiIpDQpwcmludChzdW1tYXJ5KGRhdGFbLCBtYWNyb19jb3ZhcmlhdGVzXSkpDQoNCiMgQ2FsY3VsYXRlIGNvcnJlbGF0aW9uIGJldHdlZW4gbWFjcm8gdmFyaWFibGVzDQpjYXQoIlxuQ29ycmVsYXRpb24gYmV0d2VlbiBtYWNybyB2YXJpYWJsZXM6XG4iKQ0KbWFjcm9fY29yIDwtIGNvcihkYXRhWywgbWFjcm9fY292YXJpYXRlc10sIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQ0KcHJpbnQocm91bmQobWFjcm9fY29yLCAzKSkNCg0KIyBDYWxjdWxhdGUgY29ycmVsYXRpb24gYmV0d2VlbiBtYWNybyB2YXJpYWJsZXMgYW5kIGtleSBmaW5hbmNpYWwgdmFyaWFibGVzDQprZXlfZmluYW5jaWFsIDwtIGMoIkxUTVRBIiwgIk5JTVRBIiwgIlBSSUNFIiwgInpfc2NvcmUiKSAgIyBTZWxlY3QgYSBmZXcga2V5IGZpbmFuY2lhbCB2YXJzDQprZXlfZmluYW5jaWFsIDwtIGludGVyc2VjdChrZXlfZmluYW5jaWFsLCBuYW1lcyhkYXRhKSkgICMgTWFrZSBzdXJlIHRoZXkgZXhpc3QNCg0KaWYobGVuZ3RoKGtleV9maW5hbmNpYWwpID4gMCkgew0KICBjYXQoIlxuQ29ycmVsYXRpb24gYmV0d2VlbiBtYWNybyBhbmQga2V5IGZpbmFuY2lhbCB2YXJpYWJsZXM6XG4iKQ0KICBjcm9zc19jb3IgPC0gY29yKGRhdGFbLCBtYWNyb19jb3ZhcmlhdGVzXSwgZGF0YVssIGtleV9maW5hbmNpYWxdLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikNCiAgcHJpbnQocm91bmQoY3Jvc3NfY29yLCAzKSkNCn0NCg0KIyBTYXZlIHRoZSBjbGVhbmVkIGRhdGFmcmFtZSB3aXRoIHByZXByb2Nlc3NlZCBjb3ZhcmlhdGVzDQp3cml0ZS5jc3YoZGF0YSwgZmlsZSA9ICJjb3hfbW9kZWxfY2xlYW5lZF9kYXRhX3dpdGhfbWFjcm8uY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpjYXQoIkNsZWFuZWQgZGF0YWZyYW1lIHdpdGggZmluYW5jaWFsIGFuZCBtYWNybyBjb3ZhcmlhdGVzIHNhdmVkIHRvICdjb3hfbW9kZWxfY2xlYW5lZF9kYXRhX3dpdGhfbWFjcm8uY3N2J1xuIikNCmBgYA0KDQpXZSBjaGVjayBmb3IgbXVsdGljb2xsaW5lYXJpdHkgYW1vbmcgZmluYW5jaWFsIGNvdmFyaWF0ZXMgYW5kIG1hY3JvIGNvdmFyaWF0ZXMuIFdlIGFsc28gY2hlY2sgZm9yIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGZpbmFuY2lhbCBhbmQgbWFjcm8gY292YXJpYXRlcy4gVGhlIGZpbmFsIGxpc3Qgb2YgY292YXJpYXRlcyBpcyBwcmVwYXJlZCBmb3IgbW9kZWxpbmcsIGFuZCB0aGUgQWx0bWFuIFotc2NvcmUgaXMgY2FsY3VsYXRlZC4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1RFUCA2OiBDaGVjayBmb3IgTXVsdGljb2xsaW5lYXJpdHkNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDYuMTogQ2hlY2sgbXVsdGljb2xsaW5lYXJpdHkgYW1vbmcgZmluYW5jaWFsIGNvdmFyaWF0ZXMNCmNhdCgiXG49PT0gQ2hlY2tpbmcgbXVsdGljb2xsaW5lYXJpdHkgYW1vbmcgZmluYW5jaWFsIGNvdmFyaWF0ZXMgPT09XG4iKQ0KY29yX21hdHJpeF9maW5hbmNpYWwgPC0gY29yKGRhdGFbLCBmaW5hbmNpYWxfY292YXJpYXRlc10sIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQ0KcHJpbnQocm91bmQoY29yX21hdHJpeF9maW5hbmNpYWwsIDIpKQ0KDQojIElkZW50aWZ5IGhpZ2ggY29ycmVsYXRpb24gcGFpcnMgKHxyfCA+IDAuNykNCmhpZ2hfY29yIDwtIHdoaWNoKGFicyhjb3JfbWF0cml4X2ZpbmFuY2lhbCkgPiAwLjcgJiBhYnMoY29yX21hdHJpeF9maW5hbmNpYWwpIDwgMSwgYXJyLmluZCA9IFRSVUUpDQppZihucm93KGhpZ2hfY29yKSA+IDApIHsNCiAgY2F0KCJIaWdoIGNvcnJlbGF0aW9ucyBkZXRlY3RlZCBhbW9uZyBmaW5hbmNpYWwgdmFyaWFibGVzOlxuIikNCiAgZm9yKGkgaW4gMTpucm93KGhpZ2hfY29yKSkgew0KICAgIGlmKGhpZ2hfY29yW2ksIDFdIDwgaGlnaF9jb3JbaSwgMl0pIHsgIyBhdm9pZCBwcmludGluZyBkdXBsaWNhdGVzDQogICAgICBjYXQoZmluYW5jaWFsX2NvdmFyaWF0ZXNbaGlnaF9jb3JbaSwgMV1dLCAiYW5kIiwgDQogICAgICAgICAgZmluYW5jaWFsX2NvdmFyaWF0ZXNbaGlnaF9jb3JbaSwgMl1dLCAiOiIsIA0KICAgICAgICAgIHJvdW5kKGNvcl9tYXRyaXhfZmluYW5jaWFsW2hpZ2hfY29yW2ksIDFdLCBoaWdoX2NvcltpLCAyXV0sIDIpLCAiXG4iKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBJZGVudGlmeSB2YXJpYWJsZXMgdG8gcmVtb3ZlIGJhc2VkIG9uIGhpZ2hlc3QgY29ycmVsYXRpb25zDQogICMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIGNvcnJlbGF0aW9uIGZvciBlYWNoIHZhcmlhYmxlDQogIGF2Z19jb3IgPC0gcm93TWVhbnMoYWJzKGNvcl9tYXRyaXhfZmluYW5jaWFsKSkNCiAgcHJpbnQoIkF2ZXJhZ2UgYWJzb2x1dGUgY29ycmVsYXRpb24gZm9yIGVhY2ggZmluYW5jaWFsIHZhcmlhYmxlOiIpDQogIHByaW50KHNvcnQoYXZnX2NvciwgZGVjcmVhc2luZyA9IFRSVUUpKQ0KICANCiAgIyBJZGVudGlmeSBwYWlycyB3aXRoIGhpZ2ggY29ycmVsYXRpb24NCiAgaGlnaF9jb3JfcGFpcnMgPC0gZGF0YS5mcmFtZSgNCiAgICB2YXIxID0gZmluYW5jaWFsX2NvdmFyaWF0ZXNbaGlnaF9jb3JbLCAxXV0sDQogICAgdmFyMiA9IGZpbmFuY2lhbF9jb3ZhcmlhdGVzW2hpZ2hfY29yWywgMl1dLA0KICAgIGNvcnJlbGF0aW9uID0gY29yX21hdHJpeF9maW5hbmNpYWxbaGlnaF9jb3JdDQogICkgJT4lDQogICAgZmlsdGVyKHZhcjEgPCB2YXIyKSAlPiUgICMgUmVtb3ZlIGR1cGxpY2F0ZXMNCiAgICBhcnJhbmdlKGRlc2MoYWJzKGNvcnJlbGF0aW9uKSkpDQogIA0KICBwcmludCgiSGlnaGx5IGNvcnJlbGF0ZWQgZmluYW5jaWFsIHZhcmlhYmxlIHBhaXJzOiIpDQogIHByaW50KGhpZ2hfY29yX3BhaXJzKQ0KICANCiAgIyBSZW1vdmUgdmFyaWFibGVzIHdpdGggaGlnaGVzdCBhdmVyYWdlIGNvcnJlbGF0aW9uIGZyb20gZWFjaCBoaWdobHkgY29ycmVsYXRlZCBwYWlyDQogIHZhcmlhYmxlc190b19yZW1vdmUgPC0gYygpDQogIGZvcihpIGluIDE6bnJvdyhoaWdoX2Nvcl9wYWlycykpIHsNCiAgICB2YXIxIDwtIGhpZ2hfY29yX3BhaXJzJHZhcjFbaV0NCiAgICB2YXIyIDwtIGhpZ2hfY29yX3BhaXJzJHZhcjJbaV0NCiAgICANCiAgICAjIFNraXAgaWYgZWl0aGVyIHZhcmlhYmxlIGlzIGFscmVhZHkgbWFya2VkIGZvciByZW1vdmFsDQogICAgaWYodmFyMSAlaW4lIHZhcmlhYmxlc190b19yZW1vdmUgfHwgdmFyMiAlaW4lIHZhcmlhYmxlc190b19yZW1vdmUpIHsNCiAgICAgIG5leHQNCiAgICB9DQogICAgDQogICAgIyBSZW1vdmUgdGhlIHZhcmlhYmxlIHdpdGggaGlnaGVyIGF2ZXJhZ2UgY29ycmVsYXRpb24NCiAgICBpZihhdmdfY29yW3ZhcjFdID4gYXZnX2Nvclt2YXIyXSkgew0KICAgICAgdmFyaWFibGVzX3RvX3JlbW92ZSA8LSBjKHZhcmlhYmxlc190b19yZW1vdmUsIHZhcjEpDQogICAgfSBlbHNlIHsNCiAgICAgIHZhcmlhYmxlc190b19yZW1vdmUgPC0gYyh2YXJpYWJsZXNfdG9fcmVtb3ZlLCB2YXIyKQ0KICAgIH0NCiAgfQ0KICANCiAgY2F0KCJGaW5hbmNpYWwgdmFyaWFibGVzIHRvIHJlbW92ZSBkdWUgdG8gaGlnaCBtdWx0aWNvbGxpbmVhcml0eTpcbiIpDQogIHByaW50KHZhcmlhYmxlc190b19yZW1vdmUpDQogIA0KICAjIFVwZGF0ZSBmaW5hbmNpYWwgY292YXJpYXRlcyBsaXN0DQogIGZpbmFuY2lhbF9jb3ZhcmlhdGVzIDwtIHNldGRpZmYoZmluYW5jaWFsX2NvdmFyaWF0ZXMsIHZhcmlhYmxlc190b19yZW1vdmUpDQogIGNhdCgiUmVtYWluaW5nIGZpbmFuY2lhbCBjb3ZhcmlhdGVzIGFmdGVyIHJlbW92aW5nIGhpZ2hseSBjb3JyZWxhdGVkIHZhcmlhYmxlczpcbiIpDQogIHByaW50KGZpbmFuY2lhbF9jb3ZhcmlhdGVzKQ0KfQ0KDQojIDYuMjogQ2hlY2sgbXVsdGljb2xsaW5lYXJpdHkgYW1vbmcgbWFjcm8gY292YXJpYXRlcw0KY2F0KCJcbj09PSBDaGVja2luZyBtdWx0aWNvbGxpbmVhcml0eSBhbW9uZyBtYWNybyBjb3ZhcmlhdGVzID09PVxuIikNCmNvcl9tYXRyaXhfbWFjcm8gPC0gY29yKGRhdGFbLCBtYWNyb19jb3ZhcmlhdGVzXSwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpDQpwcmludChyb3VuZChjb3JfbWF0cml4X21hY3JvLCAyKSkNCg0KIyBDaGVjayBmb3IgaGlnaCBjb3JyZWxhdGlvbnMgYW1vbmcgbWFjcm8gdmFyaWFibGVzDQpoaWdoX2Nvcl9tYWNybyA8LSB3aGljaChhYnMoY29yX21hdHJpeF9tYWNybykgPiAwLjcgJiBhYnMoY29yX21hdHJpeF9tYWNybykgPCAxLCBhcnIuaW5kID0gVFJVRSkNCmlmKG5yb3coaGlnaF9jb3JfbWFjcm8pID4gMCkgew0KICBjYXQoIkhpZ2ggY29ycmVsYXRpb25zIGRldGVjdGVkIGFtb25nIG1hY3JvIHZhcmlhYmxlczpcbiIpDQogIGZvcihpIGluIDE6bnJvdyhoaWdoX2Nvcl9tYWNybykpIHsNCiAgICBpZihoaWdoX2Nvcl9tYWNyb1tpLCAxXSA8IGhpZ2hfY29yX21hY3JvW2ksIDJdKSB7ICMgYXZvaWQgcHJpbnRpbmcgZHVwbGljYXRlcw0KICAgICAgY2F0KG1hY3JvX2NvdmFyaWF0ZXNbaGlnaF9jb3JfbWFjcm9baSwgMV1dLCAiYW5kIiwgDQogICAgICAgICAgbWFjcm9fY292YXJpYXRlc1toaWdoX2Nvcl9tYWNyb1tpLCAyXV0sICI6IiwgDQogICAgICAgICAgcm91bmQoY29yX21hdHJpeF9tYWNyb1toaWdoX2Nvcl9tYWNyb1tpLCAxXSwgaGlnaF9jb3JfbWFjcm9baSwgMl1dLCAyKSwgIlxuIikNCiAgICB9DQogIH0NCiAgDQogICMgRm9yIG1hY3JvIHZhcmlhYmxlcywgd2UnbGwga2VlcCBhbGwgb2YgdGhlbSBhbmQgbm90ZSB0aGUgY29ycmVsYXRpb25zIGZvciBpbnRlcnByZXRhdGlvbg0KICBjYXQoIk5vdGU6IEFsbCBtYWNybyB2YXJpYWJsZXMgd2lsbCBiZSByZXRhaW5lZCBkZXNwaXRlIGNvcnJlbGF0aW9ucyBmb3IgZWNvbm9taWMgaW50ZXJwcmV0YXRpb24uXG4iKQ0KICBjYXQoIldoZW4gaW50ZXJwcmV0aW5nIHJlc3VsdHMsIGNvbnNpZGVyIHRoZXNlIGNvcnJlbGF0aW9ucyBmb3IgcHJvcGVyIGNvbnRleHR1YWxpemF0aW9uLlxuIikNCn0gZWxzZSB7DQogIGNhdCgiTm8gaGlnaCBjb3JyZWxhdGlvbnMgZGV0ZWN0ZWQgYW1vbmcgbWFjcm8gdmFyaWFibGVzLlxuIikNCn0NCg0KIyA2LjM6IENoZWNrIGNvcnJlbGF0aW9uIGJldHdlZW4gZmluYW5jaWFsIGFuZCBtYWNybyB2YXJpYWJsZXMNCmNhdCgiXG49PT0gQ2hlY2tpbmcgY29ycmVsYXRpb24gYmV0d2VlbiBmaW5hbmNpYWwgYW5kIG1hY3JvIHZhcmlhYmxlcyA9PT1cbiIpDQpjcm9zc19jb3JfbWF0cml4IDwtIGNvcihkYXRhWywgZmluYW5jaWFsX2NvdmFyaWF0ZXNdLCBkYXRhWywgbWFjcm9fY292YXJpYXRlc10sIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQ0KcHJpbnQocm91bmQoY3Jvc3NfY29yX21hdHJpeCwgMikpDQoNCiMgSWRlbnRpZnkgaGlnaCBjcm9zcy1jb3JyZWxhdGlvbnMNCmhpZ2hfY3Jvc3NfY29yIDwtIHdoaWNoKGFicyhjcm9zc19jb3JfbWF0cml4KSA+IDAuNSwgYXJyLmluZCA9IFRSVUUpDQppZihucm93KGhpZ2hfY3Jvc3NfY29yKSA+IDApIHsNCiAgY2F0KCJOb3RhYmxlIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGZpbmFuY2lhbCBhbmQgbWFjcm8gdmFyaWFibGVzOlxuIikNCiAgZm9yKGkgaW4gMTpucm93KGhpZ2hfY3Jvc3NfY29yKSkgew0KICAgIGNhdChmaW5hbmNpYWxfY292YXJpYXRlc1toaWdoX2Nyb3NzX2NvcltpLCAxXV0sICJhbmQiLCANCiAgICAgICAgbWFjcm9fY292YXJpYXRlc1toaWdoX2Nyb3NzX2NvcltpLCAyXV0sICI6IiwgDQogICAgICAgIHJvdW5kKGNyb3NzX2Nvcl9tYXRyaXhbaGlnaF9jcm9zc19jb3JbaSwgMV0sIGhpZ2hfY3Jvc3NfY29yW2ksIDJdXSwgMiksICJcbiIpDQogIH0NCiAgDQogIGNhdCgiVGhlc2UgY29ycmVsYXRpb25zIGluZGljYXRlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBmaXJtIGZpbmFuY2lhbHMgYW5kIG1hY3JvZWNvbm9taWMgY29uZGl0aW9ucy5cbiIpDQogIGNhdCgiQ29uc2lkZXIgdGhlc2Ugd2hlbiBpbnRlcnByZXRpbmcgY29lZmZpY2llbnRzIGluIHRoZSBmaW5hbCBtb2RlbHMuXG4iKQ0KfQ0KDQojIDYuNDogQ3JlYXRlIHRoZSBmaW5hbCBsaXN0IG9mIGFsbCBjb3ZhcmlhdGVzIGZvciBtb2RlbGluZw0KYWxsX2NvdmFyaWF0ZXMgPC0gYyhmaW5hbmNpYWxfY292YXJpYXRlcywgbWFjcm9fY292YXJpYXRlcykNCmNhdCgiXG5GaW5hbCBsaXN0IG9mIGNvdmFyaWF0ZXMgZm9yIG1vZGVsaW5nOiIsIGxlbmd0aChhbGxfY292YXJpYXRlcyksICJ2YXJpYWJsZXNcbiIpDQpjYXQoIkZpbmFuY2lhbCBjb3ZhcmlhdGVzOiIsIGxlbmd0aChmaW5hbmNpYWxfY292YXJpYXRlcyksICJcbiIpDQpjYXQoIk1hY3JvIGNvdmFyaWF0ZXM6IiwgbGVuZ3RoKG1hY3JvX2NvdmFyaWF0ZXMpLCAiXG4iKQ0KYGBgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBTVEVQIDYuMTogQ2FsY3VsYXRlIEFsdG1hbiBaLXNjb3JlDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbkNhbGN1bGF0aW5nIEFsdG1hbiBaLXNjb3JlIGZvciBiYW5rcnVwdGN5IHByZWRpY3Rpb24uLi5cbiIpDQoNCiMgQ3JlYXRlIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBaLXNjb3JlIGJhc2VkIG9uIGF2YWlsYWJsZSB2YXJpYWJsZXMNCmNhbGN1bGF0ZV96X3Njb3JlIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgIyBBbHRtYW4gWi1zY29yZSBjb21wb25lbnRzOg0KICAjIFogPSAxLjJY4oKBICsgMS40WOKCgiArIDMuM1jigoMgKyAwLjZY4oKEICsgMS4wWOKChQ0KICAjIFdoZXJlOg0KICAjIFjigoEgPSBXb3JraW5nIENhcGl0YWwgLyBUb3RhbCBBc3NldHMNCiAgIyBY4oKCID0gUmV0YWluZWQgRWFybmluZ3MgLyBUb3RhbCBBc3NldHMgKHdlJ2xsIHVzZSBhIHByb3h5IGlmIG5vdCBhdmFpbGFibGUpDQogICMgWOKCgyA9IEVCSVQgLyBUb3RhbCBBc3NldHMNCiAgIyBY4oKEID0gTWFya2V0IFZhbHVlIG9mIEVxdWl0eSAvIFRvdGFsIExpYWJpbGl0aWVzDQogICMgWOKChSA9IFNhbGVzIC8gVG90YWwgQXNzZXRzDQogIA0KICAjIFdlJ2xsIGFkYXB0IHRoZSBjYWxjdWxhdGlvbiBiYXNlZCBvbiBhdmFpbGFibGUgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0DQogIA0KICAjIFjigoE6IFdvcmtpbmcgQ2FwaXRhbCAvIFRvdGFsIEFzc2V0cyAodXNpbmcgd2NfcmF0aW8gaWYgYXZhaWxhYmxlKQ0KICBYMSA8LSBkYXRhJHdjX3JhdGlvDQogIA0KICAjIFjigoM6IEVCSVQgLyBUb3RhbCBBc3NldHMgKHVzaW5nIGViaXRfbWFyZ2luIGFzIGEgcHJveHksIGFkanVzdGVkIGJ5IGFzc2V0IHR1cm5vdmVyKQ0KICBYMyA8LSBkYXRhJGViaXRfbWFyZ2luICogZGF0YSRhc3NldF90dXJub3Zlcg0KICANCiAgIyBY4oKEOiBNYXJrZXQgVmFsdWUgb2YgRXF1aXR5IC8gVG90YWwgTGlhYmlsaXRpZXMNCiAgIyBBcHByb3hpbWF0aW5nIHRoaXMgdXNpbmcgYXZhaWxhYmxlIHZhcmlhYmxlczoNCiAgIyBNYXJrZXQgVmFsdWUgb2YgRXF1aXR5IGNhbiBiZSBkZXJpdmVkIGZyb20gTFRNVEEgKFRvdGFsIExpYWJpbGl0aWVzIC8gTWFya2V0IFZhbHVlKQ0KICAjIExUTVRBID0gVG90YWwgTGlhYmlsaXRpZXMgLyBNYXJrZXQgVmFsdWUNCiAgIyBTbyBNYXJrZXQgVmFsdWUgPSBUb3RhbCBMaWFiaWxpdGllcyAvIExUTVRBDQogICMgQW5kIFjigoQgPSBNYXJrZXQgVmFsdWUgLyBUb3RhbCBMaWFiaWxpdGllcyA9IDEgLyBMVE1UQQ0KICBYNCA8LSAxIC8gZGF0YSRMVE1UQQ0KICANCiAgIyBY4oKFOiBTYWxlcyAvIFRvdGFsIEFzc2V0cyAodGhpcyBpcyBhc3NldF90dXJub3ZlcikNCiAgWDUgPC0gZGF0YSRhc3NldF90dXJub3Zlcg0KICANCiAgIyBGb3IgWOKCgiwgd2UgZG9uJ3QgaGF2ZSBkaXJlY3QgcmV0YWluZWQgZWFybmluZ3MgZGF0YQ0KICAjIFdlIGNhbiB1c2UgTklNVEEgKE5ldCBJbmNvbWUgLyBNYXJrZXQgVmFsdWUpIGFzIGEgcm91Z2ggcHJveHkgb2YgcHJvZml0YWJpbGl0eQ0KICAjIEFkanVzdGVkIHRvIGFwcHJveGltYXRlIFJldGFpbmVkIEVhcm5pbmdzIC8gVG90YWwgQXNzZXRzDQogIFgyIDwtIGRhdGEkTklNVEEgKiAoMSAvIGRhdGEkTFRNVEEpICogMC43ICAjIFNjYWxpbmcgZmFjdG9yIHRvIGFwcHJveGltYXRlDQogIA0KICAjIENhbGN1bGF0ZSBaLXNjb3JlIHdpdGggYXZhaWxhYmxlIGNvbXBvbmVudHMNCiAgIyBJZiBzb21lIGNvbXBvbmVudHMgYXJlIG1pc3NpbmcsIHdlJ2xsIGFkanVzdCB0aGUgd2VpZ2h0cw0KICB6X3Njb3JlIDwtIDEuMiAqIFgxICsgMS40ICogWDIgKyAzLjMgKiBYMyArIDAuNiAqIFg0ICsgMS4wICogWDUNCiAgDQogIHJldHVybih6X3Njb3JlKQ0KfQ0KDQojIENhbGN1bGF0ZSBaLXNjb3JlIGZvciBlYWNoIHJvdw0KZGF0YSR6X3Njb3JlIDwtIGNhbGN1bGF0ZV96X3Njb3JlKGRhdGEpDQoNCiMgQ2hlY2sgWi1zY29yZSBkaXN0cmlidXRpb24NCnpfc3VtbWFyeSA8LSBzdW1tYXJ5KGRhdGEkel9zY29yZSkNCmNhdCgiWi1zY29yZSBzdW1tYXJ5IHN0YXRpc3RpY3M6XG4iKQ0KcHJpbnQoel9zdW1tYXJ5KQ0KDQojIERlZmluZSByaXNrIGNhdGVnb3JpZXMgYmFzZWQgb24gWi1zY29yZQ0KZGF0YSR6X3Jpc2tfY2F0ZWdvcnkgPC0gY3V0KGRhdGEkel9zY29yZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoLUluZiwgMS44MSwgMi45OSwgSW5mKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiSGlnaCBSaXNrIiwgIkdyZXkgWm9uZSIsICJMb3cgUmlzayIpKQ0KDQojIERpc3BsYXkgZGlzdHJpYnV0aW9uIG9mIHJpc2sgY2F0ZWdvcmllcw0Kel9kaXN0cmlidXRpb24gPC0gdGFibGUoZGF0YSR6X3Jpc2tfY2F0ZWdvcnkpDQpjYXQoIlxuRGlzdHJpYnV0aW9uIG9mIFotc2NvcmUgcmlzayBjYXRlZ29yaWVzOlxuIikNCnByaW50KHpfZGlzdHJpYnV0aW9uKQ0KcHJpbnQocHJvcC50YWJsZSh6X2Rpc3RyaWJ1dGlvbikgKiAxMDApDQoNCiMgQ2hlY2sgY29ycmVsYXRpb24gYmV0d2VlbiBaLXNjb3JlIGFuZCBldmVudCB0eXBlcw0KY2F0KCJcbk1lYW4gWi1zY29yZSBieSBmaW5hbCBvdXRjb21lOlxuIikNCmFnZ3JlZ2F0ZSh6X3Njb3JlIH4gZmluYWxldmVudCwgZGF0YSA9IGRhdGEsIG1lYW4pDQoNCiMgQWRkIFotc2NvcmUgdG8gZmluYW5jaWFsIGNvdmFyaWF0ZXMgbGlzdA0KZmluYW5jaWFsX2NvdmFyaWF0ZXMgPC0gYyhmaW5hbmNpYWxfY292YXJpYXRlcywgInpfc2NvcmUiKQ0KY2F0KCJcbkFkZGVkIFotc2NvcmUgdG8gZmluYW5jaWFsIGNvdmFyaWF0ZXMgbGlzdC5cbiIpDQpjYXQoIlVwZGF0ZWQgZmluYW5jaWFsIGNvdmFyaWF0ZXM6IiwgbGVuZ3RoKGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSwgInZhcmlhYmxlc1xuIikNCg0KIyBVcGRhdGUgdGhlIGNvbWJpbmVkIGNvdmFyaWF0ZXMgbGlzdCBhcyB3ZWxsDQphbGxfY292YXJpYXRlcyA8LSBjKGZpbmFuY2lhbF9jb3ZhcmlhdGVzLCBtYWNyb19jb3ZhcmlhdGVzKQ0KY2F0KCJVcGRhdGVkIGFsbCBjb3ZhcmlhdGVzIGxpc3Q6IiwgbGVuZ3RoKGFsbF9jb3ZhcmlhdGVzKSwgInZhcmlhYmxlc1xuIikNCg0KIyBWaXN1YWxpemUgWi1zY29yZSBkaXN0cmlidXRpb24gYnkgZXZlbnQgdHlwZSBpZiBnZ3Bsb3QyIGlzIGF2YWlsYWJsZQ0KaWYocmVxdWlyZU5hbWVzcGFjZSgiZ2dwbG90MiIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAjIENyZWF0ZSBkYXRhIGZvciB2aXN1YWxpemF0aW9uDQogIHBsb3RfZGF0YSA8LSBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JSAgIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55DQogICAgdW5ncm91cCgpICU+JQ0KICAgIG11dGF0ZShldmVudF9sYWJlbCA9IGNhc2Vfd2hlbigNCiAgICAgIGV2ZW50X3R5cGUgPT0gMCB+ICJDZW5zb3JlZCIsDQogICAgICBldmVudF90eXBlID09IDEgfiAiQmFua3J1cHRjeSIsDQogICAgICBldmVudF90eXBlID09IDIgfiAiQWNxdWlzaXRpb24iDQogICAgKSkNCiAgDQogICMgQm94IHBsb3Qgb2YgWi1zY29yZSBieSBldmVudCB0eXBlDQogIHAgPC0gZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSBldmVudF9sYWJlbCwgeSA9IHpfc2NvcmUsIGZpbGwgPSBldmVudF9sYWJlbCkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS44MSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDIuOTksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiQ2Vuc29yZWQiID0gImdyZXkiLCAiQmFua3J1cHRjeSIgPSAicmVkIiwgIkFjcXVpc2l0aW9uIiA9ICJibHVlIikpICsNCiAgICBsYWJzKHRpdGxlID0gIkFsdG1hbiBaLXNjb3JlIGJ5IENvbXBhbnkgT3V0Y29tZSIsDQogICAgICAgICB4ID0gIkZpbmFsIENvbXBhbnkgU3RhdHVzIiwNCiAgICAgICAgIHkgPSAiWi1zY29yZSIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJSZWQgbGluZSA9IDEuODEgKEhpZ2ggUmlzayBUaHJlc2hvbGQpLCBCbHVlIGxpbmUgPSAyLjk5IChMb3cgUmlzayBUaHJlc2hvbGQpIikgKw0KICAgIHRoZW1lX21pbmltYWwoKQ0KICANCiAgcHJpbnQocCkNCiAgZ2dzYXZlKCJ6X3Njb3JlX2J5X291dGNvbWUucG5nIiwgcCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2KQ0KfQ0KYGBgDQoNCkhlcmUgd2Ugc2hvdyB0aGUgS2FwbGFuLU1laWVyIHN1cnZpdmFsIGN1cnZlcyBmb3IgYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gZXZlbnRzLiBXZSBhbHNvIGNhbGN1bGF0ZSBtZWRpYW4gc3Vydml2YWwgdGltZXMgYW5kIHBsb3QgdGhlIGN1cnZlcyB0b2dldGhlciBmb3IgY29tcGFyaXNvbi4gVGhlIGBzdXJ2bWluZXJgIHBhY2thZ2UgaXMgdXNlZCB0byBjcmVhdGUgbW9yZSBkZXRhaWxlZCBwbG90cyB3aXRoIHJpc2sgdGFibGVzLiBXZSBhbHNvIGhhdmUgc3Vydml2YWwgY3VydmVzIHN0cmF0aWZpZWQgYnkgaW5kdXN0cnkgc2VjdG9yIGZvciBib3RoIGJhbmtydXB0Y3kgYW5kIGFjcXVpc2l0aW9uIGV2ZW50cy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG49PT0gU3Vydml2YWwgQ3VydmUgQW5hbHlzaXMgZm9yIEJhbmtydXB0Y3kgYW5kIEFjcXVpc2l0aW9uID09PVxuIikNCg0KIyBDcmVhdGUgc2VwYXJhdGUgc3Vydml2YWwgb2JqZWN0cyBmb3IgZWFjaCBldmVudCB0eXBlDQpzdXJ2X2JhbmtydXB0Y3kgPC0gU3VydihkYXRhJHRzdGFydCwgZGF0YSR0c3RvcCwgZGF0YSRiYW5rcnVwdGN5KQ0Kc3Vydl9hY3F1aXNpdGlvbiA8LSBTdXJ2KGRhdGEkdHN0YXJ0LCBkYXRhJHRzdG9wLCBkYXRhJGFjcXVpc2l0aW9uKQ0KDQojIEZpdCBLYXBsYW4tTWVpZXIgc3Vydml2YWwgY3VydmVzDQprbV9iYW5rcnVwdGN5IDwtIHN1cnZmaXQoc3Vydl9iYW5rcnVwdGN5IH4gMSwgZGF0YSA9IGRhdGEpDQprbV9hY3F1aXNpdGlvbiA8LSBzdXJ2Zml0KHN1cnZfYWNxdWlzaXRpb24gfiAxLCBkYXRhID0gZGF0YSkNCg0KIyBCYXNpYyBzdW1tYXJ5IG9mIHN1cnZpdmFsIGVzdGltYXRlcw0KY2F0KCJcblN1bW1hcnkgb2YgQmFua3J1cHRjeSBTdXJ2aXZhbCBDdXJ2ZTpcbiIpDQpwcmludChzdW1tYXJ5KGttX2JhbmtydXB0Y3ksIHRpbWVzID0gYygxLCAzLCA1LCAxMCkpKQ0KDQpjYXQoIlxuU3VtbWFyeSBvZiBBY3F1aXNpdGlvbiBTdXJ2aXZhbCBDdXJ2ZTpcbiIpDQpwcmludChzdW1tYXJ5KGttX2FjcXVpc2l0aW9uLCB0aW1lcyA9IGMoMSwgMywgNSwgMTApKSkNCg0KIyBDYWxjdWxhdGUgbWVkaWFuIHN1cnZpdmFsIHRpbWVzIChpZiB0aGV5IGV4aXN0KQ0KY2F0KCJcbk1lZGlhbiBTdXJ2aXZhbCBUaW1lczpcbiIpDQoNCmJhbmtydXB0Y3lfbWVkaWFuIDwtIHN1bW1hcnkoa21fYmFua3J1cHRjeSkkdGFibGVbIm1lZGlhbiJdDQppZihpcy5uYShiYW5rcnVwdGN5X21lZGlhbikpIHsNCiAgY2F0KCJCYW5rcnVwdGN5OiBNb3JlIHRoYW4gNTAlIG9mIGNvbXBhbmllcyBzdXJ2aXZlIHRoZSBlbnRpcmUgb2JzZXJ2YXRpb24gcGVyaW9kIHdpdGhvdXQgYmFua3J1cHRjeVxuIikNCn0gZWxzZSB7DQogIGNhdCgiQmFua3J1cHRjeTogTWVkaWFuIHN1cnZpdmFsIHRpbWUgPSIsIHJvdW5kKGJhbmtydXB0Y3lfbWVkaWFuLCAyKSwgInllYXJzXG4iKQ0KfQ0KDQphY3F1aXNpdGlvbl9tZWRpYW4gPC0gc3VtbWFyeShrbV9hY3F1aXNpdGlvbikkdGFibGVbIm1lZGlhbiJdDQppZihpcy5uYShhY3F1aXNpdGlvbl9tZWRpYW4pKSB7DQogIGNhdCgiQWNxdWlzaXRpb246IE1vcmUgdGhhbiA1MCUgb2YgY29tcGFuaWVzIHN1cnZpdmUgdGhlIGVudGlyZSBvYnNlcnZhdGlvbiBwZXJpb2Qgd2l0aG91dCBiZWluZyBhY3F1aXJlZFxuIikNCn0gZWxzZSB7DQogIGNhdCgiQWNxdWlzaXRpb246IE1lZGlhbiBzdXJ2aXZhbCB0aW1lID0iLCByb3VuZChhY3F1aXNpdGlvbl9tZWRpYW4sIDIpLCAieWVhcnNcbiIpDQp9DQoNCiMgUGxvdCBib3RoIHN1cnZpdmFsIGN1cnZlcyB0b2dldGhlciBmb3IgY29tcGFyaXNvbg0KcGFyKG1mcm93ID0gYygxLCAxKSkgICMgUmVzZXQgcGxvdHRpbmcgcGFyYW1ldGVycw0KY29tYmluZWRfcGxvdCA8LSBwbG90KGttX2JhbmtydXB0Y3ksIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbmYuaW50ID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgY29sID0gInJlZCIsIA0KICAgICAgICAgICAgICAgICAgICAgIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgIHlsYWIgPSAiU3Vydml2YWwgUHJvYmFiaWxpdHkiLA0KICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSAiS2FwbGFuLU1laWVyIFN1cnZpdmFsIEN1cnZlczogQmFua3J1cHRjeSB2cyBBY3F1aXNpdGlvbiIpDQoNCiMgQWRkIGFjcXVpc2l0aW9uIGN1cnZlDQpsaW5lcyhrbV9hY3F1aXNpdGlvbiwgY29sID0gImJsdWUiLCBsd2QgPSAyLCBjb25mLmludCA9IFRSVUUsIGx0eSA9IDEpDQoNCiMgQWRkIGxlZ2VuZA0KbGVnZW5kKCJ0b3ByaWdodCIsIA0KICAgICAgIGxlZ2VuZCA9IGMoIkJhbmtydXB0Y3kiLCAiQWNxdWlzaXRpb24iKSwgDQogICAgICAgY29sID0gYygicmVkIiwgImJsdWUiKSwgDQogICAgICAgbHdkID0gMiwgDQogICAgICAgY2V4ID0gMC44KQ0KDQojIFNhdmUgdGhlIHBsb3QNCmRldi5jb3B5KHBuZywgImNvbWJpbmVkX3N1cnZpdmFsX2N1cnZlcy5wbmciLCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNjAwKQ0KZGV2Lm9mZigpDQoNCiMgQ3JlYXRlIG1vcmUgZGV0YWlsZWQgcGxvdHMgdXNpbmcgc3Vydm1pbmVyDQppZihyZXF1aXJlTmFtZXNwYWNlKCJzdXJ2bWluZXIiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgIyBCYW5rcnVwdGN5IHN1cnZpdmFsIGN1cnZlIHdpdGggcmlzayB0YWJsZQ0KICBwX2JhbmtydXB0Y3kgPC0gZ2dzdXJ2cGxvdCgNCiAgICBrbV9iYW5rcnVwdGN5LA0KICAgIGRhdGEgPSBkYXRhLA0KICAgIHJpc2sudGFibGUgPSBUUlVFLA0KICAgIHJpc2sudGFibGUuY29sID0gInN0cmF0YSIsDQogICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSwNCiAgICBwYWxldHRlID0gInJlZCIsDQogICAgdGl0bGUgPSAiQmFua3J1cHRjeSBTdXJ2aXZhbCBDdXJ2ZSIsDQogICAgeGxhYiA9ICJZZWFycyIsDQogICAgeWxhYiA9ICJQcm9iYWJpbGl0eSBvZiBOb3QgR29pbmcgQmFua3J1cHQiLA0KICAgIGNvbmYuaW50ID0gVFJVRSwNCiAgICBzdXJ2Lm1lZGlhbi5saW5lID0gImh2Ig0KICApDQogIA0KICBwcmludChwX2JhbmtydXB0Y3kpDQogIGdnc2F2ZSgiYmFua3J1cHRjeV9zdXJ2aXZhbF9jdXJ2ZS5wbmciLCBwX2JhbmtydXB0Y3kkcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkNCiAgDQogICMgQWNxdWlzaXRpb24gc3Vydml2YWwgY3VydmUgd2l0aCByaXNrIHRhYmxlDQogIHBfYWNxdWlzaXRpb24gPC0gZ2dzdXJ2cGxvdCgNCiAgICBrbV9hY3F1aXNpdGlvbiwNCiAgICBkYXRhID0gZGF0YSwNCiAgICByaXNrLnRhYmxlID0gVFJVRSwNCiAgICByaXNrLnRhYmxlLmNvbCA9ICJzdHJhdGEiLA0KICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCksDQogICAgcGFsZXR0ZSA9ICJibHVlIiwNCiAgICB0aXRsZSA9ICJBY3F1aXNpdGlvbiBTdXJ2aXZhbCBDdXJ2ZSIsDQogICAgeGxhYiA9ICJZZWFycyIsDQogICAgeWxhYiA9ICJQcm9iYWJpbGl0eSBvZiBOb3QgQmVpbmcgQWNxdWlyZWQiLA0KICAgIGNvbmYuaW50ID0gVFJVRSwNCiAgICBzdXJ2Lm1lZGlhbi5saW5lID0gImh2Ig0KICApDQogIA0KICBwcmludChwX2FjcXVpc2l0aW9uKQ0KICBnZ3NhdmUoImFjcXVpc2l0aW9uX3N1cnZpdmFsX2N1cnZlLnBuZyIsIHBfYWNxdWlzaXRpb24kcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkNCiAgDQogICMgQ29tYmluZWQgcGxvdCB3aXRoIHN1cnZtaW5lcg0KICAjIEZpcnN0IGNyZWF0ZSBzZXBhcmF0ZSBzdXJ2Zml0IG9iamVjdHMgd2l0aCBsYWJlbHMNCiAga21fYmFua3J1cHRjeV9sYWJlbGVkIDwtIHN1cnZmaXQoc3Vydl9iYW5rcnVwdGN5IH4gcmVwKCJCYW5rcnVwdGN5IiwgbnJvdyhkYXRhKSksIGRhdGEgPSBkYXRhKQ0KICBrbV9hY3F1aXNpdGlvbl9sYWJlbGVkIDwtIHN1cnZmaXQoc3Vydl9hY3F1aXNpdGlvbiB+IHJlcCgiQWNxdWlzaXRpb24iLCBucm93KGRhdGEpKSwgZGF0YSA9IGRhdGEpDQogIA0KICAjIENvbWJpbmUgdGhlbQ0KICBjb21iaW5lZF9zdXJ2Zml0IDwtIGxpc3QoQmFua3J1cHRjeSA9IGttX2JhbmtydXB0Y3ksIEFjcXVpc2l0aW9uID0ga21fYWNxdWlzaXRpb24pDQogIGNsYXNzKGNvbWJpbmVkX3N1cnZmaXQpIDwtIGMoInN1cnZmaXRsaXN0IiwgImxpc3QiKQ0KICANCiAgIyBQbG90IGNvbWJpbmVkIGN1cnZlcw0KICBwX2NvbWJpbmVkIDwtIGdnc3VydnBsb3QoDQogICAgY29tYmluZWRfc3VydmZpdCwNCiAgICBkYXRhID0gZGF0YSwNCiAgICByaXNrLnRhYmxlID0gVFJVRSwNCiAgICByaXNrLnRhYmxlLmNvbCA9ICJzdHJhdGEiLA0KICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCksDQogICAgcGFsZXR0ZSA9IGMoInJlZCIsICJibHVlIiksDQogICAgdGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBCYW5rcnVwdGN5IGFuZCBBY3F1aXNpdGlvbiBTdXJ2aXZhbCBDdXJ2ZXMiLA0KICAgIHhsYWIgPSAiWWVhcnMiLA0KICAgIHlsYWIgPSAiU3Vydml2YWwgUHJvYmFiaWxpdHkiLA0KICAgIGNvbmYuaW50ID0gVFJVRSwNCiAgICBsZWdlbmQubGFicyA9IGMoIkJhbmtydXB0Y3kiLCAiQWNxdWlzaXRpb24iKSwNCiAgICBsZWdlbmQudGl0bGUgPSAiRXZlbnQgVHlwZSINCiAgKQ0KICANCiAgcHJpbnQocF9jb21iaW5lZCkNCiAgZ2dzYXZlKCJjb21iaW5lZF9zdXJ2aXZhbF9jdXJ2ZXNfZ2dwbG90LnBuZyIsIHBfY29tYmluZWQkcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOCkNCiAgDQogICMgQ3JlYXRlIHN0cmF0aWZpZWQgc3Vydml2YWwgY3VydmVzIGJ5IGluZHVzdHJ5IHNlY3RvciBmb3IgYmFua3J1cHRjeQ0KICBzZWN0b3JfYmFua3J1cHRjeSA8LSBzdXJ2Zml0KHN1cnZfYmFua3J1cHRjeSB+IGdzZWN0b3IsIGRhdGEgPSBkYXRhKQ0KICBwX3NlY3Rvcl9iYW5rcnVwdGN5IDwtIGdnc3VydnBsb3QoDQogICAgc2VjdG9yX2JhbmtydXB0Y3ksDQogICAgZGF0YSA9IGRhdGEsDQogICAgcmlzay50YWJsZSA9IFRSVUUsDQogICAgcmlzay50YWJsZS5jb2wgPSAic3RyYXRhIiwNCiAgICBnZ3RoZW1lID0gdGhlbWVfbWluaW1hbCgpLA0KICAgIHBhbGV0dGUgPSAiamNvIiwNCiAgICB0aXRsZSA9ICJCYW5rcnVwdGN5IFN1cnZpdmFsIEN1cnZlcyBieSBJbmR1c3RyeSBTZWN0b3IiLA0KICAgIHhsYWIgPSAiWWVhcnMiLA0KICAgIHlsYWIgPSAiUHJvYmFiaWxpdHkgb2YgTm90IEdvaW5nIEJhbmtydXB0IiwNCiAgICBjb25mLmludCA9IEZBTFNFLA0KICAgIGxlZ2VuZC50aXRsZSA9ICJJbmR1c3RyeSBTZWN0b3IiDQogICkNCiAgDQogIHByaW50KHBfc2VjdG9yX2JhbmtydXB0Y3kpDQogIGdnc2F2ZSgiYmFua3J1cHRjeV9ieV9zZWN0b3IucG5nIiwgcF9zZWN0b3JfYmFua3J1cHRjeSRwbG90LCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA5KQ0KICANCiAgIyBDcmVhdGUgc3RyYXRpZmllZCBzdXJ2aXZhbCBjdXJ2ZXMgYnkgaW5kdXN0cnkgc2VjdG9yIGZvciBhY3F1aXNpdGlvbg0KICBzZWN0b3JfYWNxdWlzaXRpb24gPC0gc3VydmZpdChzdXJ2X2FjcXVpc2l0aW9uIH4gZ3NlY3RvciwgZGF0YSA9IGRhdGEpDQogIHBfc2VjdG9yX2FjcXVpc2l0aW9uIDwtIGdnc3VydnBsb3QoDQogICAgc2VjdG9yX2FjcXVpc2l0aW9uLA0KICAgIGRhdGEgPSBkYXRhLA0KICAgIHJpc2sudGFibGUgPSBUUlVFLA0KICAgIHJpc2sudGFibGUuY29sID0gInN0cmF0YSIsDQogICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKSwNCiAgICBwYWxldHRlID0gImpjbyIsDQogICAgdGl0bGUgPSAiQWNxdWlzaXRpb24gU3Vydml2YWwgQ3VydmVzIGJ5IEluZHVzdHJ5IFNlY3RvciIsDQogICAgeGxhYiA9ICJZZWFycyIsDQogICAgeWxhYiA9ICJQcm9iYWJpbGl0eSBvZiBOb3QgQmVpbmcgQWNxdWlyZWQiLA0KICAgIGNvbmYuaW50ID0gRkFMU0UsDQogICAgbGVnZW5kLnRpdGxlID0gIkluZHVzdHJ5IFNlY3RvciINCiAgKQ0KICANCiAgcHJpbnQocF9zZWN0b3JfYWNxdWlzaXRpb24pDQogIGdnc2F2ZSgiYWNxdWlzaXRpb25fYnlfc2VjdG9yLnBuZyIsIHBfc2VjdG9yX2FjcXVpc2l0aW9uJHBsb3QsIHdpZHRoID0gMTIsIGhlaWdodCA9IDkpDQp9DQpgYGANCg0KV2Ugc3RhcnQgYnkgZml0dGluZyBDb3ggcHJvcG9ydGlvbmFsIGhhemFyZHMgbW9kZWxzIHdpdGggWi1zY29yZSBhcyB0aGUgb25seSBwcmVkaWN0b3IuIFdlIHRoZW4gZml0IGluZGl2aWR1YWwgQ294IG1vZGVscyBmb3IgZWFjaCBtYWNyb2Vjb25vbWljIHZhcmlhYmxlLCBpbmNsdWRpbmcgc3RyYXRpZmljYXRpb24gYnkgaW5kdXN0cnkgc2VjdG9yLiBXZSBjYWxjdWxhdGUgY29uY29yZGFuY2UgKEMtaW5kZXgpIGZvciBlYWNoIG1vZGVsIHRvIGFzc2VzcyBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLiBGaW5hbGx5LCB3ZSB2aXN1YWxpemUgdGhlIHJlc3VsdHMgYW5kIGNvbXBhcmUgdGhlIG1vZGVscy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1RFUCA2LjI6IFotc2NvcmUgT25seSBDb3ggTW9kZWxzDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbkZpdHRpbmcgQ294IG1vZGVscyB3aXRoIFotc2NvcmUgYXMgdGhlIG9ubHkgcHJlZGljdG9yLi4uXG4iKQ0KDQojIENyZWF0ZSBmb3JtdWxhcyB3aXRoIFotc2NvcmUgYXMgdGhlIG9ubHkgcHJlZGljdG9yDQojIEluY2x1ZGUgc3RyYXRpZmljYXRpb24gYnkgaW5kdXN0cnkgc2VjdG9yDQp6X2JhbmtydXB0Y3lfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKCJTdXJ2KHRzdGFydCwgdHN0b3AsIGJhbmtydXB0Y3kpIH4gel9zY29yZSArIHN0cmF0YShnc2VjdG9yKSIpDQp6X2FjcXVpc2l0aW9uX2Zvcm11bGEgPC0gYXMuZm9ybXVsYSgiU3Vydih0c3RhcnQsIHRzdG9wLCBhY3F1aXNpdGlvbikgfiB6X3Njb3JlICsgc3RyYXRhKGdzZWN0b3IpIikNCg0KIyBGaXQgbW9kZWxzDQp6X2JhbmtydXB0Y3lfbW9kZWwgPC0gY294cGgoel9iYW5rcnVwdGN5X2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCnpfYWNxdWlzaXRpb25fbW9kZWwgPC0gY294cGgoel9hY3F1aXNpdGlvbl9mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQoNCiMgRGlzcGxheSBtb2RlbCBzdW1tYXJpZXMNCmNhdCgiXG49PT0gWi1TQ09SRSBPTkxZOiBCQU5LUlVQVENZIE1PREVMID09PVxuIikNCnByaW50KHN1bW1hcnkoel9iYW5rcnVwdGN5X21vZGVsKSkNCg0KY2F0KCJcbj09PSBaLVNDT1JFIE9OTFk6IEFDUVVJU0lUSU9OIE1PREVMID09PVxuIikNCnByaW50KHN1bW1hcnkoel9hY3F1aXNpdGlvbl9tb2RlbCkpDQoNCiMgQ2FsY3VsYXRlIGNvbmNvcmRhbmNlIChDLWluZGV4KSBhcyBhIG1lYXN1cmUgb2YgcHJlZGljdGl2ZSBhYmlsaXR5DQpjYXQoIlxuUHJlZGljdGl2ZSBwZXJmb3JtYW5jZSAoQy1pbmRleCk6XG4iKQ0KY2F0KCJaLXNjb3JlIG9ubHkgYmFua3J1cHRjeSBtb2RlbDoiLCByb3VuZCh6X2JhbmtydXB0Y3lfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0sIDMpLCAiXG4iKQ0KY2F0KCJaLXNjb3JlIG9ubHkgYWNxdWlzaXRpb24gbW9kZWw6Iiwgcm91bmQoel9hY3F1aXNpdGlvbl9tb2RlbCRjb25jb3JkYW5jZVsiY29uY29yZGFuY2UiXSwgMyksICJcbiIpDQoNCiMgQ2hlY2sgcHJvcG9ydGlvbmFsIGhhemFyZHMgYXNzdW1wdGlvbg0KY2F0KCJcblRlc3RpbmcgcHJvcG9ydGlvbmFsIGhhemFyZHMgYXNzdW1wdGlvbiBmb3IgWi1zY29yZSBtb2RlbHMuLi5cbiIpDQpwaF90ZXN0X3pfYmFua3J1cHRjeSA8LSBjb3guenBoKHpfYmFua3J1cHRjeV9tb2RlbCkNCnBoX3Rlc3Rfel9hY3F1aXNpdGlvbiA8LSBjb3guenBoKHpfYWNxdWlzaXRpb25fbW9kZWwpDQoNCmNhdCgiXG49PT0gUEggVEVTVCBGT1IgWi1TQ09SRSBCQU5LUlVQVENZIE1PREVMID09PVxuIikNCnByaW50KHBoX3Rlc3Rfel9iYW5rcnVwdGN5KQ0KDQpjYXQoIlxuPT09IFBIIFRFU1QgRk9SIFotU0NPUkUgQUNRVUlTSVRJT04gTU9ERUwgPT09XG4iKQ0KcHJpbnQocGhfdGVzdF96X2FjcXVpc2l0aW9uKQ0KDQojIFN0b3JlIG1vZGVscyBmb3IgbGF0ZXIgY29tcGFyaXNvbg0Kel9zY29yZV9tb2RlbHMgPC0gbGlzdCgNCiAgYmFua3J1cHRjeSA9IHpfYmFua3J1cHRjeV9tb2RlbCwNCiAgYWNxdWlzaXRpb24gPSB6X2FjcXVpc2l0aW9uX21vZGVsDQopDQpgYGANCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1RFUCA2LjM6IE1hY3JvIFZhcmlhYmxlcyBPbmx5IENveCBNb2RlbHMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuRml0dGluZyBDb3ggbW9kZWxzIHdpdGggaW5kaXZpZHVhbCBtYWNybyB2YXJpYWJsZXMgYXMgcHJlZGljdG9ycy4uLlxuIikNCg0KIyBDcmVhdGUgZW1wdHkgbGlzdHMgdG8gc3RvcmUgbW9kZWxzDQptYWNyb19iYW5rcnVwdGN5X21vZGVscyA8LSBsaXN0KCkNCm1hY3JvX2FjcXVpc2l0aW9uX21vZGVscyA8LSBsaXN0KCkNCg0KIyBDb25jb3JkYW5jZSByZXN1bHRzIGZvciBhbGwgbW9kZWxzDQpjb25jb3JkYW5jZV9yZXN1bHRzIDwtIGRhdGEuZnJhbWUoDQogIFZhcmlhYmxlID0gYygiel9zY29yZSIsIG1hY3JvX2NvdmFyaWF0ZXMpLA0KICBCS19Db25jb3JkYW5jZSA9IE5BLA0KICBBQ1FfQ29uY29yZGFuY2UgPSBOQQ0KKQ0KDQojIEFkZCBaLXNjb3JlIGNvbmNvcmRhbmNlDQpjb25jb3JkYW5jZV9yZXN1bHRzJEJLX0NvbmNvcmRhbmNlW2NvbmNvcmRhbmNlX3Jlc3VsdHMkVmFyaWFibGUgPT0gInpfc2NvcmUiXSA8LSANCiAgel9iYW5rcnVwdGN5X21vZGVsJGNvbmNvcmRhbmNlWyJjb25jb3JkYW5jZSJdDQpjb25jb3JkYW5jZV9yZXN1bHRzJEFDUV9Db25jb3JkYW5jZVtjb25jb3JkYW5jZV9yZXN1bHRzJFZhcmlhYmxlID09ICJ6X3Njb3JlIl0gPC0gDQogIHpfYWNxdWlzaXRpb25fbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0NCg0KIyBGaXQgaW5kaXZpZHVhbCBtb2RlbHMgZm9yIGVhY2ggbWFjcm8gdmFyaWFibGUNCmZvcih2YXIgaW4gbWFjcm9fY292YXJpYXRlcykgew0KICBjYXQoIlxuVGVzdGluZyBtYWNybyB2YXJpYWJsZToiLCB2YXIsICJcbiIpDQogIA0KICAjIENyZWF0ZSBmb3JtdWxhcyB3aXRoIHNpbmdsZSBtYWNybyB2YXJpYWJsZQ0KICBtYWNyb19ia19mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKCJTdXJ2KHRzdGFydCwgdHN0b3AsIGJhbmtydXB0Y3kpIH4gIiwgdmFyLCAiICsgc3RyYXRhKGdzZWN0b3IpIikpDQogIG1hY3JvX2FjcV9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKCJTdXJ2KHRzdGFydCwgdHN0b3AsIGFjcXVpc2l0aW9uKSB+ICIsIHZhciwgIiArIHN0cmF0YShnc2VjdG9yKSIpKQ0KICANCiAgIyBGaXQgbW9kZWxzDQogIHRyeUNhdGNoKHsNCiAgICBia19tb2RlbCA8LSBjb3hwaChtYWNyb19ia19mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogICAgbWFjcm9fYmFua3J1cHRjeV9tb2RlbHNbW3Zhcl1dIDwtIGJrX21vZGVsDQogICAgDQogICAgIyBTdG9yZSBjb25jb3JkYW5jZQ0KICAgIGNvbmNvcmRhbmNlX3Jlc3VsdHMkQktfQ29uY29yZGFuY2VbY29uY29yZGFuY2VfcmVzdWx0cyRWYXJpYWJsZSA9PSB2YXJdIDwtIA0KICAgICAgYmtfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0NCiAgICANCiAgICAjIFByaW50IHN1bW1hcnkNCiAgICBjYXQoIlxuPT09IE1BQ1JPIE9OTFkgKCIsIHZhciwgIik6IEJBTktSVVBUQ1kgTU9ERUwgPT09XG4iKQ0KICAgIHByaW50KHN1bW1hcnkoYmtfbW9kZWwpKQ0KICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICBjYXQoIkVycm9yIGZpdHRpbmcgYmFua3J1cHRjeSBtb2RlbCB3aXRoIiwgdmFyLCAiOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgfSkNCiAgDQogIHRyeUNhdGNoKHsNCiAgICBhY3FfbW9kZWwgPC0gY294cGgobWFjcm9fYWNxX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCiAgICBtYWNyb19hY3F1aXNpdGlvbl9tb2RlbHNbW3Zhcl1dIDwtIGFjcV9tb2RlbA0KICAgIA0KICAgICMgU3RvcmUgY29uY29yZGFuY2UNCiAgICBjb25jb3JkYW5jZV9yZXN1bHRzJEFDUV9Db25jb3JkYW5jZVtjb25jb3JkYW5jZV9yZXN1bHRzJFZhcmlhYmxlID09IHZhcl0gPC0gDQogICAgICBhY3FfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0NCiAgICANCiAgICAjIFByaW50IHN1bW1hcnkNCiAgICBjYXQoIlxuPT09IE1BQ1JPIE9OTFkgKCIsIHZhciwgIik6IEFDUVVJU0lUSU9OIE1PREVMID09PVxuIikNCiAgICBwcmludChzdW1tYXJ5KGFjcV9tb2RlbCkpDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgIGNhdCgiRXJyb3IgZml0dGluZyBhY3F1aXNpdGlvbiBtb2RlbCB3aXRoIiwgdmFyLCAiOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgfSkNCn0NCg0KIyBDb21wYXJlIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UNCmNhdCgiXG5Db21wYXJpc29uIG9mIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UgKEMtaW5kZXgpOlxuIikNCmNvbmNvcmRhbmNlX3Jlc3VsdHMgPC0gY29uY29yZGFuY2VfcmVzdWx0cyAlPiUNCiAgYXJyYW5nZShkZXNjKEJLX0NvbmNvcmRhbmNlKSkNCg0KcHJpbnQoY29uY29yZGFuY2VfcmVzdWx0cykNCg0KIyBWaXN1YWxpemUgY29tcGFyaXNvbg0KaWYocmVxdWlyZU5hbWVzcGFjZSgiZ2dwbG90MiIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAjIFByZXBhcmUgZGF0YSBmb3IgcGxvdHRpbmcNCiAgcGxvdF9kYXRhIDwtIGNvbmNvcmRhbmNlX3Jlc3VsdHMgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKEJLX0NvbmNvcmRhbmNlLCBBQ1FfQ29uY29yZGFuY2UpLA0KICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJNb2RlbCIsDQogICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJDb25jb3JkYW5jZSIpICU+JQ0KICAgIG11dGF0ZShNb2RlbCA9IGlmZWxzZShNb2RlbCA9PSAiQktfQ29uY29yZGFuY2UiLCAiQmFua3J1cHRjeSIsICJBY3F1aXNpdGlvbiIpKQ0KICANCiAgIyBDcmVhdGUgcGxvdA0KICBwIDwtIGdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gcmVvcmRlcihWYXJpYWJsZSwgQ29uY29yZGFuY2UpLCB5ID0gQ29uY29yZGFuY2UsIGZpbGwgPSBNb2RlbCkpICsNCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogICAgY29vcmRfZmxpcCgpICsNCiAgICBsYWJzKHRpdGxlID0gIlByZWRpY3RpdmUgUGVyZm9ybWFuY2Ugb2YgSW5kaXZpZHVhbCBWYXJpYWJsZXMiLA0KICAgICAgICAgc3VidGl0bGUgPSAiQ29tcGFyaW5nIFotc2NvcmUgdnMuIE1hY3JvZWNvbm9taWMgVmFyaWFibGVzIiwNCiAgICAgICAgIHggPSAiVmFyaWFibGUiLA0KICAgICAgICAgeSA9ICJDb25jb3JkYW5jZSAoQy1pbmRleCkiKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCiAgDQogIHByaW50KHApDQogIGdnc2F2ZSgic2luZ2xlX3ByZWRpY3Rvcl9jb21wYXJpc29uLnBuZyIsIHAsIHdpZHRoID0gMTAsIGhlaWdodCA9IDYpDQp9DQoNCiMgVGVzdCB0aGUgYmVzdCBtYWNybyB2YXJpYWJsZSBhbmQgWi1zY29yZSB0b2dldGhlcg0KYmVzdF9tYWNybyA8LSBjb25jb3JkYW5jZV9yZXN1bHRzJFZhcmlhYmxlW2NvbmNvcmRhbmNlX3Jlc3VsdHMkVmFyaWFibGUgIT0gInpfc2NvcmUiXVsxXQ0KY2F0KCJcblRlc3RpbmcgWi1zY29yZSBhbmQgYmVzdCBtYWNybyB2YXJpYWJsZSAoIiwgYmVzdF9tYWNybywgIikgdG9nZXRoZXIuLi5cbiIpDQoNCiMgQ3JlYXRlIGZvcm11bGEgd2l0aCBaLXNjb3JlIGFuZCBiZXN0IG1hY3JvIHZhcmlhYmxlDQpjb21iaW5lZF9ia19mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKCJTdXJ2KHRzdGFydCwgdHN0b3AsIGJhbmtydXB0Y3kpIH4gel9zY29yZSArICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJlc3RfbWFjcm8sICIgKyBzdHJhdGEoZ3NlY3RvcikiKSkNCmNvbWJpbmVkX2FjcV9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKCJTdXJ2KHRzdGFydCwgdHN0b3AsIGFjcXVpc2l0aW9uKSB+IHpfc2NvcmUgKyAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmVzdF9tYWNybywgIiArIHN0cmF0YShnc2VjdG9yKSIpKQ0KDQojIEZpdCBtb2RlbHMNCmNvbWJpbmVkX2JrX21vZGVsIDwtIGNveHBoKGNvbWJpbmVkX2JrX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCmNvbWJpbmVkX2FjcV9tb2RlbCA8LSBjb3hwaChjb21iaW5lZF9hY3FfZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KDQojIERpc3BsYXkgbW9kZWwgc3VtbWFyaWVzDQpjYXQoIlxuPT09IFotU0NPUkUgKyBCRVNUIE1BQ1JPOiBCQU5LUlVQVENZIE1PREVMID09PVxuIikNCnByaW50KHN1bW1hcnkoY29tYmluZWRfYmtfbW9kZWwpKQ0KDQpjYXQoIlxuPT09IFotU0NPUkUgKyBCRVNUIE1BQ1JPOiBBQ1FVSVNJVElPTiBNT0RFTCA9PT1cbiIpDQpwcmludChzdW1tYXJ5KGNvbWJpbmVkX2FjcV9tb2RlbCkpDQoNCiMgQ29tcGFyZSBjb25jb3JkYW5jZQ0KY2F0KCJcbkNvbWJpbmVkIG1vZGVsIHBlcmZvcm1hbmNlIChDLWluZGV4KTpcbiIpDQpjYXQoIlotc2NvcmUgKyBCZXN0IE1hY3JvIGJhbmtydXB0Y3kgbW9kZWw6IiwgDQogICAgcm91bmQoY29tYmluZWRfYmtfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0sIDMpLCAiXG4iKQ0KY2F0KCJaLXNjb3JlICsgQmVzdCBNYWNybyBhY3F1aXNpdGlvbiBtb2RlbDoiLCANCiAgICByb3VuZChjb21iaW5lZF9hY3FfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0sIDMpLCAiXG4iKQ0KDQojIFRlc3QgZm9yIGltcHJvdmVtZW50IHVzaW5nIGxpa2VsaWhvb2QgcmF0aW8gdGVzdA0KY2F0KCJcbkxpa2VsaWhvb2QgcmF0aW8gdGVzdCBmb3IgaW1wcm92ZW1lbnQgb3ZlciBaLXNjb3JlIGFsb25lOlxuIikNCg0KY2F0KCJCYW5rcnVwdGN5IG1vZGVsIGltcHJvdmVtZW50OlxuIikNCnByaW50KGFub3ZhKHpfYmFua3J1cHRjeV9tb2RlbCwgY29tYmluZWRfYmtfbW9kZWwpKQ0KDQpjYXQoIkFjcXVpc2l0aW9uIG1vZGVsIGltcHJvdmVtZW50OlxuIikNCnByaW50KGFub3ZhKHpfYWNxdWlzaXRpb25fbW9kZWwsIGNvbWJpbmVkX2FjcV9tb2RlbCkpDQoNCiMgU3RvcmUgY29tYmluZWQgbW9kZWxzIGZvciBsYXRlciBjb21wYXJpc29uDQpjb21iaW5lZF9tb2RlbHMgPC0gbGlzdCgNCiAgYmFua3J1cHRjeSA9IGNvbWJpbmVkX2JrX21vZGVsLA0KICBhY3F1aXNpdGlvbiA9IGNvbWJpbmVkX2FjcV9tb2RlbA0KKQ0KYGBgDQpIZXJlLCB3ZSBzdGFydCBieSBwZXJmb3JtaW5nIHZhcmlhYmxlIHNlbGVjdGlvbiB1c2luZyBzdGVwd2lzZSByZWdyZXNzaW9uIGFuZCBMQVNTTyBtZXRob2RzLiBXZSB0aGVuIGZpdCBDb3ggbW9kZWxzIHdpdGggdGhlIHNlbGVjdGVkIHZhcmlhYmxlcyBhbmQgZXZhbHVhdGUgdGhlaXIgcGVyZm9ybWFuY2UuIFdlIGFsc28gdmlzdWFsaXplIHRoZSByZXN1bHRzIGFuZCBjb21wYXJlIHRoZSBtb2RlbHMuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgNzogVmFyaWFibGUgU2VsZWN0aW9uIHdpdGggRmluYW5jaWFsIGFuZCBNYWNybyBWYXJpYWJsZXMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuUGVyZm9ybWluZyB2YXJpYWJsZSBzZWxlY3Rpb24gdXNpbmcgYm90aCBzdGVwd2lzZSBhbmQgTEFTU08gbWV0aG9kcy4uLlxuIikNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgNy4xOiBQcmUtc2NyZWVuaW5nIG9mIE1hY3JvZWNvbm9taWMgVmFyaWFibGVzDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjcuMTogUHJlLXNjcmVlbmluZyBNYWNyb2Vjb25vbWljIFZhcmlhYmxlcyB3aXRoIFVuaXZhcmlhdGUgTW9kZWxzXG4iKQ0KDQojIFByZS1zY3JlZW4gbWFjcm8gdmFyaWFibGVzIHdpdGggdW5pdmFyaWF0ZSBtb2RlbHMNCm1hY3JvX3NjcmVlbmluZyA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZSA9IGNoYXJhY3RlcigpLA0KICBCS19Db2VmZmljaWVudCA9IG51bWVyaWMoKSwNCiAgQktfUF9WYWx1ZSA9IG51bWVyaWMoKSwNCiAgQktfQ29uY29yZGFuY2UgPSBudW1lcmljKCksDQogIEFDUV9Db2VmZmljaWVudCA9IG51bWVyaWMoKSwNCiAgQUNRX1BfVmFsdWUgPSBudW1lcmljKCksDQogIEFDUV9Db25jb3JkYW5jZSA9IG51bWVyaWMoKSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQoNCmZvcih2YXIgaW4gbWFjcm9fY292YXJpYXRlcykgew0KICAjIFRlc3QgZm9yIGJhbmtydXB0Y3kNCiAgYmtfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgiU3Vydih0c3RhcnQsIHRzdG9wLCBiYW5rcnVwdGN5KSB+ICIsIHZhciwgIiArIHN0cmF0YShnc2VjdG9yKSIpKQ0KICBia19tb2RlbCA8LSBjb3hwaChia19mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIGJrX3N1bW1hcnkgPC0gc3VtbWFyeShia19tb2RlbCkNCiAgDQogICMgVGVzdCBmb3IgYWNxdWlzaXRpb24NCiAgYWNxX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoIlN1cnYodHN0YXJ0LCB0c3RvcCwgYWNxdWlzaXRpb24pIH4gIiwgdmFyLCAiICsgc3RyYXRhKGdzZWN0b3IpIikpDQogIGFjcV9tb2RlbCA8LSBjb3hwaChhY3FfZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICBhY3Ffc3VtbWFyeSA8LSBzdW1tYXJ5KGFjcV9tb2RlbCkNCiAgDQogICMgU3RvcmUgcmVzdWx0cw0KICBtYWNyb19zY3JlZW5pbmcgPC0gcmJpbmQobWFjcm9fc2NyZWVuaW5nLCBkYXRhLmZyYW1lKA0KICAgIFZhcmlhYmxlID0gdmFyLA0KICAgIEJLX0NvZWZmaWNpZW50ID0gYmtfc3VtbWFyeSRjb2VmZmljaWVudHNbMSwgMV0sDQogICAgQktfUF9WYWx1ZSA9IGJrX3N1bW1hcnkkY29lZmZpY2llbnRzWzEsIDVdLA0KICAgIEJLX0NvbmNvcmRhbmNlID0gYmtfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0sDQogICAgQUNRX0NvZWZmaWNpZW50ID0gYWNxX3N1bW1hcnkkY29lZmZpY2llbnRzWzEsIDFdLA0KICAgIEFDUV9QX1ZhbHVlID0gYWNxX3N1bW1hcnkkY29lZmZpY2llbnRzWzEsIDVdLA0KICAgIEFDUV9Db25jb3JkYW5jZSA9IGFjcV9tb2RlbCRjb25jb3JkYW5jZVsiY29uY29yZGFuY2UiXSwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKSkNCn0NCg0KIyBQcmludCBtYWNybyB2YXJpYWJsZSBzY3JlZW5pbmcgcmVzdWx0cw0KY2F0KCJcbk1hY3JvZWNvbm9taWMgdmFyaWFibGVzIHVuaXZhcmlhdGUgdGVzdGluZyByZXN1bHRzOlxuIikNCnByaW50KG1hY3JvX3NjcmVlbmluZykNCg0KIyBJZGVudGlmeSBzaWduaWZpY2FudCBtYWNybyB2YXJpYWJsZXMgKHAgPCAwLjA1KQ0Kc2lnbmlmaWNhbnRfYmtfbWFjcm8gPC0gbWFjcm9fc2NyZWVuaW5nJFZhcmlhYmxlW21hY3JvX3NjcmVlbmluZyRCS19QX1ZhbHVlIDwgMC4wNV0NCnNpZ25pZmljYW50X2FjcV9tYWNybyA8LSBtYWNyb19zY3JlZW5pbmckVmFyaWFibGVbbWFjcm9fc2NyZWVuaW5nJEFDUV9QX1ZhbHVlIDwgMC4wNV0NCg0KY2F0KCJcblNpZ25pZmljYW50IG1hY3JvIHZhcmlhYmxlcyBmb3IgYmFua3J1cHRjeToiLCBwYXN0ZShzaWduaWZpY2FudF9ia19tYWNybywgY29sbGFwc2U9IiwgIiksICJcbiIpDQpjYXQoIlNpZ25pZmljYW50IG1hY3JvIHZhcmlhYmxlcyBmb3IgYWNxdWlzaXRpb246IiwgcGFzdGUoc2lnbmlmaWNhbnRfYWNxX21hY3JvLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikNCmBgYA0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDcuMjogQUlDLWJhc2VkIFN0ZXB3aXNlIFNlbGVjdGlvbiB3aXRoIEFsbCBWYXJpYWJsZXMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuNy4yOiBBSUMtYmFzZWQgU3RlcHdpc2UgU2VsZWN0aW9uIHdpdGggRmluYW5jaWFsIGFuZCBNYWNybyBWYXJpYWJsZXNcbiIpDQoNCiMgQ3JlYXRlIHRoZSBmdWxsIG1vZGVsIGZvcm11bGEgZm9yIGJhbmtydXB0Y3kgKGluY2x1ZGluZyBhbGwgdmFyaWFibGVzKQ0KZnVsbF9iYW5rcnVwdGN5X2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoDQogICJTdXJ2KHRzdGFydCwgdHN0b3AsIGJhbmtydXB0Y3kpIH4gIiwgDQogIHBhc3RlKGMoZmluYW5jaWFsX2NvdmFyaWF0ZXMsIG1hY3JvX2NvdmFyaWF0ZXMpLCBjb2xsYXBzZSA9ICIgKyAiKSwgDQogICIgKyBzdHJhdGEoZ3NlY3RvcikiDQopKQ0KDQojIENyZWF0ZSB0aGUgZnVsbCBtb2RlbCBmb3JtdWxhIGZvciBhY3F1aXNpdGlvbiAoaW5jbHVkaW5nIGFsbCB2YXJpYWJsZXMpDQpmdWxsX2FjcXVpc2l0aW9uX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoDQogICJTdXJ2KHRzdGFydCwgdHN0b3AsIGFjcXVpc2l0aW9uKSB+ICIsIA0KICBwYXN0ZShjKGZpbmFuY2lhbF9jb3ZhcmlhdGVzLCBtYWNyb19jb3ZhcmlhdGVzKSwgY29sbGFwc2UgPSAiICsgIiksIA0KICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KKSkNCg0KIyBGaXQgdGhlIGZ1bGwgbW9kZWxzDQpmdWxsX2JhbmtydXB0Y3lfbW9kZWwgPC0gY294cGgoZnVsbF9iYW5rcnVwdGN5X2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCmZ1bGxfYWNxdWlzaXRpb25fbW9kZWwgPC0gY294cGgoZnVsbF9hY3F1aXNpdGlvbl9mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQoNCiMgUGVyZm9ybSBzdGVwd2lzZSBzZWxlY3Rpb24gZm9yIGJhbmtydXB0Y3kgbW9kZWwNCnN0ZXB3aXNlX2JhbmtydXB0Y3kgPC0gc3RlcChmdWxsX2JhbmtydXB0Y3lfbW9kZWwsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSAxKQ0KDQojIFBlcmZvcm0gc3RlcHdpc2Ugc2VsZWN0aW9uIGZvciBhY3F1aXNpdGlvbiBtb2RlbA0Kc3RlcHdpc2VfYWNxdWlzaXRpb24gPC0gc3RlcChmdWxsX2FjcXVpc2l0aW9uX21vZGVsLCBkaXJlY3Rpb24gPSAiYm90aCIsIHRyYWNlID0gMSkNCg0KIyBHZXQgdGhlIGZpbmFsIHNlbGVjdGVkIHZhcmlhYmxlcyBmb3IgZWFjaCBtb2RlbA0KYmFua3J1cHRjeV92YXJzX3N0ZXAgPC0gbmFtZXMoY29lZihzdGVwd2lzZV9iYW5rcnVwdGN5KSkNCmJhbmtydXB0Y3lfdmFyc19zdGVwIDwtIGJhbmtydXB0Y3lfdmFyc19zdGVwWyFncmVwbCgic3RyYXRhIiwgYmFua3J1cHRjeV92YXJzX3N0ZXApXQ0KDQphY3F1aXNpdGlvbl92YXJzX3N0ZXAgPC0gbmFtZXMoY29lZihzdGVwd2lzZV9hY3F1aXNpdGlvbikpDQphY3F1aXNpdGlvbl92YXJzX3N0ZXAgPC0gYWNxdWlzaXRpb25fdmFyc19zdGVwWyFncmVwbCgic3RyYXRhIiwgYWNxdWlzaXRpb25fdmFyc19zdGVwKV0NCg0KY2F0KCJcblNlbGVjdGVkIHZhcmlhYmxlcyBmb3IgYmFua3J1cHRjeSBtb2RlbCAoc3RlcHdpc2UpOlxuIikNCnByaW50KGJhbmtydXB0Y3lfdmFyc19zdGVwKQ0KDQpjYXQoIlxuU2VsZWN0ZWQgdmFyaWFibGVzIGZvciBhY3F1aXNpdGlvbiBtb2RlbCAoc3RlcHdpc2UpOlxuIikNCnByaW50KGFjcXVpc2l0aW9uX3ZhcnNfc3RlcCkNCg0KIyBJZGVudGlmeSBmaW5hbmNpYWwgYW5kIG1hY3JvIHZhcmlhYmxlcyBzZWxlY3RlZA0KYmFua3J1cHRjeV9maW5fc3RlcCA8LSBiYW5rcnVwdGN5X3ZhcnNfc3RlcFtiYW5rcnVwdGN5X3ZhcnNfc3RlcCAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXQ0KYmFua3J1cHRjeV9tYWNyb19zdGVwIDwtIGJhbmtydXB0Y3lfdmFyc19zdGVwW2JhbmtydXB0Y3lfdmFyc19zdGVwICVpbiUgbWFjcm9fY292YXJpYXRlc10NCg0KYWNxdWlzaXRpb25fZmluX3N0ZXAgPC0gYWNxdWlzaXRpb25fdmFyc19zdGVwW2FjcXVpc2l0aW9uX3ZhcnNfc3RlcCAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXQ0KYWNxdWlzaXRpb25fbWFjcm9fc3RlcCA8LSBhY3F1aXNpdGlvbl92YXJzX3N0ZXBbYWNxdWlzaXRpb25fdmFyc19zdGVwICVpbiUgbWFjcm9fY292YXJpYXRlc10NCg0KY2F0KCJcbkJyZWFrZG93biBvZiBzdGVwd2lzZSBzZWxlY3Rpb24gZm9yIGJhbmtydXB0Y3kgbW9kZWw6XG4iKQ0KY2F0KCItIEZpbmFuY2lhbCB2YXJpYWJsZXM6IiwgbGVuZ3RoKGJhbmtydXB0Y3lfZmluX3N0ZXApLCAib2YiLCBsZW5ndGgoZmluYW5jaWFsX2NvdmFyaWF0ZXMpLCAiXG4iKQ0KY2F0KCItIE1hY3JvIHZhcmlhYmxlczoiLCBsZW5ndGgoYmFua3J1cHRjeV9tYWNyb19zdGVwKSwgIm9mIiwgbGVuZ3RoKG1hY3JvX2NvdmFyaWF0ZXMpLCAiXG4iKQ0KDQpjYXQoIlxuQnJlYWtkb3duIG9mIHN0ZXB3aXNlIHNlbGVjdGlvbiBmb3IgYWNxdWlzaXRpb24gbW9kZWw6XG4iKQ0KY2F0KCItIEZpbmFuY2lhbCB2YXJpYWJsZXM6IiwgbGVuZ3RoKGFjcXVpc2l0aW9uX2Zpbl9zdGVwKSwgIm9mIiwgbGVuZ3RoKGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSwgIlxuIikNCmNhdCgiLSBNYWNybyB2YXJpYWJsZXM6IiwgbGVuZ3RoKGFjcXVpc2l0aW9uX21hY3JvX3N0ZXApLCAib2YiLCBsZW5ndGgobWFjcm9fY292YXJpYXRlcyksICJcbiIpDQpgYGANCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDcuMzogTEFTU08gVmFyaWFibGUgU2VsZWN0aW9uIHdpdGggQWxsIFZhcmlhYmxlcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG43LjM6IExBU1NPLWJhc2VkIFZhcmlhYmxlIFNlbGVjdGlvbiB3aXRoIEZpbmFuY2lhbCBhbmQgTWFjcm8gVmFyaWFibGVzXG4iKQ0KDQojIENoZWNrIGlmIGdsbW5ldCBpcyBhdmFpbGFibGUsIGluc3RhbGwgaWYgbmVlZGVkDQppZighcmVxdWlyZU5hbWVzcGFjZSgiZ2xtbmV0IiwgcXVpZXRseSA9IFRSVUUpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImdsbW5ldCIpDQp9DQpsaWJyYXJ5KGdsbW5ldCkNCg0KIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55DQpsYXN0X29ic19kYXRhIDwtIGRhdGEgJT4lDQogIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgc2xpY2VfbWF4KHRzdG9wKSAlPiUNCiAgdW5ncm91cCgpDQoNCmNhdCgiVXNpbmciLCBucm93KGxhc3Rfb2JzX2RhdGEpLCAib2JzZXJ2YXRpb25zIChsYXN0IHJlY29yZCBmcm9tIGVhY2ggY29tcGFueSkgZm9yIExBU1NPIHZhcmlhYmxlIHNlbGVjdGlvblxuIikNCg0KIyBGdW5jdGlvbiB0byBwcmVwYXJlIGRhdGEgZm9yIExBU1NPIENveCBtb2RlbCAoaW5jbHVkaW5nIG1hY3JvIHZhcmlhYmxlcykNCnByZXBhcmVfbGFzc29fZGF0YSA8LSBmdW5jdGlvbihkYXRhLCBvdXRjb21lX3Zhcikgew0KICAjIENyZWF0ZSBwcmVkaWN0b3IgbWF0cml4IHdpdGggYm90aCBmaW5hbmNpYWwgYW5kIG1hY3JvIHZhcmlhYmxlcw0KICB4X3ZhcnMgPC0gZGF0YVssIGMoZmluYW5jaWFsX2NvdmFyaWF0ZXMsIG1hY3JvX2NvdmFyaWF0ZXMpLCBkcm9wID0gRkFMU0VdDQogIA0KICAjIENyZWF0ZSBtb2RlbCBtYXRyaXggKGhhbmRsZXMgZmFjdG9ycyBhcHByb3ByaWF0ZWx5KQ0KICB4X21hdHJpeCA8LSBtb2RlbC5tYXRyaXgofiAuIC0gMSwgZGF0YSA9IHhfdmFycykgICMgLTEgcmVtb3ZlcyBpbnRlcmNlcHQNCiAgDQogICMgQ3JlYXRlIFN1cnYgb2JqZWN0IGZvciBvdXRjb21lDQogIGlmKG91dGNvbWVfdmFyID09ICJiYW5rcnVwdGN5Iikgew0KICAgIHlfc3VydiA8LSBzdXJ2aXZhbDo6U3VydihkYXRhJHRzdG9wLCBkYXRhJGJhbmtydXB0Y3kpICAjIFVzaW5nIG9ubHkgdHN0b3AgZm9yIGZpbmFsIHN0YXR1cw0KICB9IGVsc2Ugew0KICAgIHlfc3VydiA8LSBzdXJ2aXZhbDo6U3VydihkYXRhJHRzdG9wLCBkYXRhJGFjcXVpc2l0aW9uKQ0KICB9DQogIA0KICByZXR1cm4obGlzdCh4ID0geF9tYXRyaXgsIHkgPSB5X3N1cnYpKQ0KfQ0KDQojIFByZXBhcmUgZGF0YSBmb3IgYmFua3J1cHRjeSBtb2RlbA0KbGFzc29fZGF0YV9iYW5rIDwtIHByZXBhcmVfbGFzc29fZGF0YShsYXN0X29ic19kYXRhLCAiYmFua3J1cHRjeSIpDQoNCiMgUHJlcGFyZSBkYXRhIGZvciBhY3F1aXNpdGlvbiBtb2RlbA0KbGFzc29fZGF0YV9hY3EgPC0gcHJlcGFyZV9sYXNzb19kYXRhKGxhc3Rfb2JzX2RhdGEsICJhY3F1aXNpdGlvbiIpDQoNCiMgUnVuIExBU1NPIHdpdGhvdXQgY3Jvc3MtdmFsaWRhdGlvbiBmaXJzdCB0byBzZWUgdmFyaWFibGUgcGF0aHMNCmZpdF9iYW5rIDwtIGdsbW5ldChsYXNzb19kYXRhX2JhbmskeCwgbGFzc29fZGF0YV9iYW5rJHksIGZhbWlseSA9ICJjb3giLCBhbHBoYSA9IDEpDQpmaXRfYWNxIDwtIGdsbW5ldChsYXNzb19kYXRhX2FjcSR4LCBsYXNzb19kYXRhX2FjcSR5LCBmYW1pbHkgPSAiY294IiwgYWxwaGEgPSAxKQ0KDQojIFBsb3QgY29lZmZpY2llbnQgcGF0aHMNCmNhdCgiQ3JlYXRpbmcgY29lZmZpY2llbnQgcGF0aCBwbG90cy4uLlxuIikNCnBhcihtZnJvdyA9IGMoMSwgMikpDQpwbG90KGZpdF9iYW5rLCB4dmFyID0gImxhbWJkYSIsIG1haW4gPSAiQmFua3J1cHRjeSBNb2RlbCIpDQpwbG90KGZpdF9hY3EsIHh2YXIgPSAibGFtYmRhIiwgbWFpbiA9ICJBY3F1aXNpdGlvbiBNb2RlbCIpDQpwYXIobWZyb3cgPSBjKDEsIDEpKQ0KDQojIFJ1biBjcm9zcy12YWxpZGF0aW9uIHRvIGZpbmQgb3B0aW1hbCBsYW1iZGENCmNhdCgiUnVubmluZyBjcm9zcy12YWxpZGF0aW9uIHdpdGggMyBmb2xkcyB0byBmaW5kIG9wdGltYWwgbGFtYmRhLi4uXG4iKQ0Kc2V0LnNlZWQoMTIzKQ0KY3ZfZml0X2JhbmsgPC0gY3YuZ2xtbmV0KGxhc3NvX2RhdGFfYmFuayR4LCBsYXNzb19kYXRhX2JhbmskeSwgDQogICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJjb3giLCBhbHBoYSA9IDEsIG5mb2xkcyA9IDMsIA0KICAgICAgICAgICAgICAgICAgICAgICBwYXJhbGxlbCA9IEZBTFNFLCBrZWVwID0gRkFMU0UpDQpjdl9maXRfYWNxIDwtIGN2LmdsbW5ldChsYXNzb19kYXRhX2FjcSR4LCBsYXNzb19kYXRhX2FjcSR5LCANCiAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiY294IiwgYWxwaGEgPSAxLCBuZm9sZHMgPSAzLA0KICAgICAgICAgICAgICAgICAgICAgIHBhcmFsbGVsID0gRkFMU0UsIGtlZXAgPSBGQUxTRSkNCg0KIyBQbG90IGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cw0KY2F0KCJDcmVhdGluZyBjcm9zcy12YWxpZGF0aW9uIHBsb3RzIGZvciBsYW1iZGEgc2VsZWN0aW9uLi4uXG4iKQ0KcGFyKG1mcm93ID0gYygxLCAyKSkNCnBsb3QoY3ZfZml0X2JhbmssIG1haW4gPSAiQmFua3J1cHRjeSBNb2RlbCBDViIpDQpwbG90KGN2X2ZpdF9hY3EsIG1haW4gPSAiQWNxdWlzaXRpb24gTW9kZWwgQ1YiKQ0KcGFyKG1mcm93ID0gYygxLCAxKSkNCg0KIyBHZXQgb3B0aW1hbCBsYW1iZGEgdmFsdWVzDQpsYW1iZGFfYmFua19taW4gPC0gY3ZfZml0X2JhbmskbGFtYmRhLm1pbiAgIyBMYW1iZGEgdGhhdCBnaXZlcyBtaW5pbXVtIENWIGVycm9yDQpsYW1iZGFfYmFua18xc2UgPC0gY3ZfZml0X2JhbmskbGFtYmRhLjFzZSAgIyBMYW1iZGEgd2l0aGluIDEgc3RkIGVycm9yIG9mIG1pbmltdW0NCg0KbGFtYmRhX2FjcV9taW4gPC0gY3ZfZml0X2FjcSRsYW1iZGEubWluDQpsYW1iZGFfYWNxXzFzZSA8LSBjdl9maXRfYWNxJGxhbWJkYS4xc2UNCg0KY2F0KCJcbk9wdGltYWwgbGFtYmRhIHZhbHVlcyBmb3IgYmFua3J1cHRjeSBtb2RlbDpcbiIpDQpjYXQoIi0gbGFtYmRhLm1pbiAobWluaW11bSBlcnJvcik6IiwgbGFtYmRhX2JhbmtfbWluLCAiXG4iKQ0KY2F0KCItIGxhbWJkYS4xc2UgKDEgc3RkIGVycm9yIHJ1bGUpOiIsIGxhbWJkYV9iYW5rXzFzZSwgIlxuIikNCg0KY2F0KCJcbk9wdGltYWwgbGFtYmRhIHZhbHVlcyBmb3IgYWNxdWlzaXRpb24gbW9kZWw6XG4iKQ0KY2F0KCItIGxhbWJkYS5taW4gKG1pbmltdW0gZXJyb3IpOiIsIGxhbWJkYV9hY3FfbWluLCAiXG4iKQ0KY2F0KCItIGxhbWJkYS4xc2UgKDEgc3RkIGVycm9yIHJ1bGUpOiIsIGxhbWJkYV9hY3FfMXNlLCAiXG4iKQ0KDQojIEdldCBjb2VmZmljaWVudHMgZm9yIHRoZSBMQVNTTyBtb2RlbHMgYXQgdGhlIG9wdGltYWwgbGFtYmRhDQpjb2VmX2JhbmtfbWluIDwtIGNvZWYoY3ZfZml0X2JhbmssIHMgPSAibGFtYmRhLm1pbiIpDQpjb2VmX2JhbmtfMXNlIDwtIGNvZWYoY3ZfZml0X2JhbmssIHMgPSAibGFtYmRhLjFzZSIpDQoNCmNvZWZfYWNxX21pbiA8LSBjb2VmKGN2X2ZpdF9hY3EsIHMgPSAibGFtYmRhLm1pbiIpDQpjb2VmX2FjcV8xc2UgPC0gY29lZihjdl9maXRfYWNxLCBzID0gImxhbWJkYS4xc2UiKQ0KDQojIEV4dHJhY3QgdmFyaWFibGUgbmFtZXMgc2VsZWN0ZWQgYnkgTEFTU08gKHdlJ2xsIHVzZSBsYW1iZGEuMXNlIGZvciBwYXJzaW1vbnkpDQpiYW5rcnVwdGN5X3ZhcnNfbGFzc28gPC0gcm93bmFtZXMoY29lZl9iYW5rXzFzZSlbd2hpY2goY29lZl9iYW5rXzFzZSAhPSAwKV0NCmFjcXVpc2l0aW9uX3ZhcnNfbGFzc28gPC0gcm93bmFtZXMoY29lZl9hY3FfMXNlKVt3aGljaChjb2VmX2FjcV8xc2UgIT0gMCldDQoNCmNhdCgiXG5TZWxlY3RlZCB2YXJpYWJsZXMgZm9yIGJhbmtydXB0Y3kgbW9kZWwgKExBU1NPKTpcbiIpDQpwcmludChiYW5rcnVwdGN5X3ZhcnNfbGFzc28pDQoNCmNhdCgiXG5TZWxlY3RlZCB2YXJpYWJsZXMgZm9yIGFjcXVpc2l0aW9uIG1vZGVsIChMQVNTTyk6XG4iKQ0KcHJpbnQoYWNxdWlzaXRpb25fdmFyc19sYXNzbykNCg0KIyBJZGVudGlmeSBmaW5hbmNpYWwgYW5kIG1hY3JvIHZhcmlhYmxlcyBzZWxlY3RlZCBieSBMQVNTTw0KYmFua3J1cHRjeV9maW5fbGFzc28gPC0gYmFua3J1cHRjeV92YXJzX2xhc3NvW2JhbmtydXB0Y3lfdmFyc19sYXNzbyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXQ0KYmFua3J1cHRjeV9tYWNyb19sYXNzbyA8LSBiYW5rcnVwdGN5X3ZhcnNfbGFzc29bYmFua3J1cHRjeV92YXJzX2xhc3NvICVpbiUgbWFjcm9fY292YXJpYXRlc10NCg0KYWNxdWlzaXRpb25fZmluX2xhc3NvIDwtIGFjcXVpc2l0aW9uX3ZhcnNfbGFzc29bYWNxdWlzaXRpb25fdmFyc19sYXNzbyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXQ0KYWNxdWlzaXRpb25fbWFjcm9fbGFzc28gPC0gYWNxdWlzaXRpb25fdmFyc19sYXNzb1thY3F1aXNpdGlvbl92YXJzX2xhc3NvICVpbiUgbWFjcm9fY292YXJpYXRlc10NCg0KY2F0KCJcbkJyZWFrZG93biBvZiBMQVNTTyBzZWxlY3Rpb24gZm9yIGJhbmtydXB0Y3kgbW9kZWw6XG4iKQ0KY2F0KCItIEZpbmFuY2lhbCB2YXJpYWJsZXM6IiwgbGVuZ3RoKGJhbmtydXB0Y3lfZmluX2xhc3NvKSwgIm9mIiwgbGVuZ3RoKGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSwgIlxuIikNCmNhdCgiLSBNYWNybyB2YXJpYWJsZXM6IiwgbGVuZ3RoKGJhbmtydXB0Y3lfbWFjcm9fbGFzc28pLCAib2YiLCBsZW5ndGgobWFjcm9fY292YXJpYXRlcyksICJcbiIpDQoNCmNhdCgiXG5CcmVha2Rvd24gb2YgTEFTU08gc2VsZWN0aW9uIGZvciBhY3F1aXNpdGlvbiBtb2RlbDpcbiIpDQpjYXQoIi0gRmluYW5jaWFsIHZhcmlhYmxlczoiLCBsZW5ndGgoYWNxdWlzaXRpb25fZmluX2xhc3NvKSwgIm9mIiwgbGVuZ3RoKGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSwgIlxuIikNCmNhdCgiLSBNYWNybyB2YXJpYWJsZXM6IiwgbGVuZ3RoKGFjcXVpc2l0aW9uX21hY3JvX2xhc3NvKSwgIm9mIiwgbGVuZ3RoKG1hY3JvX2NvdmFyaWF0ZXMpLCAiXG4iKQ0KYGBgDQoNCk5vdywgd2UgY29tcGFyZSB0aGUgdmFyaWFibGVzIHNlbGVjdGVkIGJ5IGJvdGggbWV0aG9kcyAoc3RlcHdpc2UgYW5kIExBU1NPKSBhbmQgZmluYWxpemUgdGhlIHZhcmlhYmxlIHNlbGVjdGlvbiBmb3IgYm90aCBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiBtb2RlbHMuIFdlIGFsc28gY3JlYXRlIGZvcm11bGFzIGZvciB0aGUgZmluYWwgbW9kZWxzLg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyA3LjU6IENvbXBhcmUgYW5kIEZpbmFsaXplIFZhcmlhYmxlIFNlbGVjdGlvbg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG43LjU6IENvbXBhcmluZyBhbmQgRmluYWxpemluZyBWYXJpYWJsZSBTZWxlY3Rpb25cbiIpDQoNCiMgQ29tcGFyZSB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgYm90aCBtZXRob2RzDQpjYXQoIlxuQ29tcGFyaXNvbiBvZiBzZWxlY3RlZCB2YXJpYWJsZXMgZm9yIGJhbmtydXB0Y3kgbW9kZWw6XG4iKQ0KY29tcGFyZV9iYW5rIDwtIGRhdGEuZnJhbWUoDQogIFZhcmlhYmxlID0gdW5pcXVlKGMoYmFua3J1cHRjeV92YXJzX3N0ZXAsIGJhbmtydXB0Y3lfdmFyc19sYXNzbykpLA0KICBUeXBlID0gaWZlbHNlKHVuaXF1ZShjKGJhbmtydXB0Y3lfdmFyc19zdGVwLCBiYW5rcnVwdGN5X3ZhcnNfbGFzc28pKSAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzLCANCiAgICAgICAgICAgICAgICAiRmluYW5jaWFsIiwgIk1hY3JvIiksDQogIFN0ZXB3aXNlID0gdW5pcXVlKGMoYmFua3J1cHRjeV92YXJzX3N0ZXAsIGJhbmtydXB0Y3lfdmFyc19sYXNzbykpICVpbiUgYmFua3J1cHRjeV92YXJzX3N0ZXAsDQogIExBU1NPID0gdW5pcXVlKGMoYmFua3J1cHRjeV92YXJzX3N0ZXAsIGJhbmtydXB0Y3lfdmFyc19sYXNzbykpICVpbiUgYmFua3J1cHRjeV92YXJzX2xhc3NvDQopDQpwcmludChjb21wYXJlX2JhbmspDQoNCmNhdCgiXG5Db21wYXJpc29uIG9mIHNlbGVjdGVkIHZhcmlhYmxlcyBmb3IgYWNxdWlzaXRpb24gbW9kZWw6XG4iKQ0KY29tcGFyZV9hY3EgPC0gZGF0YS5mcmFtZSgNCiAgVmFyaWFibGUgPSB1bmlxdWUoYyhhY3F1aXNpdGlvbl92YXJzX3N0ZXAsIGFjcXVpc2l0aW9uX3ZhcnNfbGFzc28pKSwNCiAgVHlwZSA9IGlmZWxzZSh1bmlxdWUoYyhhY3F1aXNpdGlvbl92YXJzX3N0ZXAsIGFjcXVpc2l0aW9uX3ZhcnNfbGFzc28pKSAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzLCANCiAgICAgICAgICAgICAgICAiRmluYW5jaWFsIiwgIk1hY3JvIiksDQogIFN0ZXB3aXNlID0gdW5pcXVlKGMoYWNxdWlzaXRpb25fdmFyc19zdGVwLCBhY3F1aXNpdGlvbl92YXJzX2xhc3NvKSkgJWluJSBhY3F1aXNpdGlvbl92YXJzX3N0ZXAsDQogIExBU1NPID0gdW5pcXVlKGMoYWNxdWlzaXRpb25fdmFyc19zdGVwLCBhY3F1aXNpdGlvbl92YXJzX2xhc3NvKSkgJWluJSBhY3F1aXNpdGlvbl92YXJzX2xhc3NvDQopDQpwcmludChjb21wYXJlX2FjcSkNCg0KIyBEZWNpZGUgb24gZmluYWwgdmFyaWFibGUgc2V0cw0KIyBGb3IgYmFua3J1cHRjeSBtb2RlbDogdXNlIGFsbCB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgYm90aCBtZXRob2RzLCBwbHVzIGltcG9ydGFudCBvbmVzIGZyb20gZWl0aGVyIG1ldGhvZA0KYmFua3J1cHRjeV92YXJzIDwtIHVuaXF1ZShjKA0KICAjIFZhcmlhYmxlcyBzZWxlY3RlZCBieSBib3RoIG1ldGhvZHMNCiAgaW50ZXJzZWN0KGJhbmtydXB0Y3lfdmFyc19zdGVwLCBiYW5rcnVwdGN5X3ZhcnNfbGFzc28pLA0KICANCiAgIyBBZGQgbWFjcm8gdmFyaWFibGVzIHRoYXQgYXJlIHNpZ25pZmljYW50IGluIHVuaXZhcmlhdGUgYW5hbHlzaXMNCiAgc2lnbmlmaWNhbnRfYmtfbWFjcm8sDQogIA0KICAjIEFkZCB6LXNjb3JlIGlmIGF2YWlsYWJsZSAoa25vd24gdG8gYmUgaW1wb3J0YW50IGZvciBiYW5rcnVwdGN5IHByZWRpY3Rpb24pDQogIGlmKCJ6X3Njb3JlIiAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSAiel9zY29yZSIgZWxzZSBOVUxMDQopKQ0KDQojIEZvciBhY3F1aXNpdGlvbiBtb2RlbDogdXNlIGFsbCB2YXJpYWJsZXMgc2VsZWN0ZWQgYnkgYm90aCBtZXRob2RzLCBwbHVzIGltcG9ydGFudCBvbmVzIGZyb20gZWl0aGVyIG1ldGhvZA0KYWNxdWlzaXRpb25fdmFycyA8LSB1bmlxdWUoYygNCiAgIyBWYXJpYWJsZXMgc2VsZWN0ZWQgYnkgYm90aCBtZXRob2RzDQogIGludGVyc2VjdChhY3F1aXNpdGlvbl92YXJzX3N0ZXAsIGFjcXVpc2l0aW9uX3ZhcnNfbGFzc28pLA0KICANCiAgIyBBZGQgbWFjcm8gdmFyaWFibGVzIHRoYXQgYXJlIHNpZ25pZmljYW50IGluIHVuaXZhcmlhdGUgYW5hbHlzaXMNCiAgc2lnbmlmaWNhbnRfYWNxX21hY3JvLA0KICANCiAgIyBBZGQga2V5IGZpbmFuY2lhbCB2YXJpYWJsZXMgaWYgYXZhaWxhYmxlDQogIGlmKCJMVE1UQSIgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcykgIkxUTVRBIiBlbHNlIE5VTEwsDQogIGlmKCJOSU1UQSIgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcykgIk5JTVRBIiBlbHNlIE5VTEwNCikpDQoNCiMgUHJpbnQgZmluYWwgdmFyaWFibGUgc2VsZWN0aW9ucw0KY2F0KCJcbkZpbmFsIHNlbGVjdGVkIHZhcmlhYmxlcyBmb3IgYmFua3J1cHRjeSBtb2RlbDoiLCBsZW5ndGgoYmFua3J1cHRjeV92YXJzKSwgInZhcmlhYmxlc1xuIikNCmJhbmtydXB0Y3lfZmluX2ZpbmFsIDwtIGJhbmtydXB0Y3lfdmFyc1tiYW5rcnVwdGN5X3ZhcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlc10NCmJhbmtydXB0Y3lfbWFjcm9fZmluYWwgPC0gYmFua3J1cHRjeV92YXJzW2JhbmtydXB0Y3lfdmFycyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXNdDQoNCmNhdCgiLSBGaW5hbmNpYWwgdmFyaWFibGVzICgiLCBsZW5ndGgoYmFua3J1cHRjeV9maW5fZmluYWwpLCAiKToiLCANCiAgICBwYXN0ZShiYW5rcnVwdGN5X2Zpbl9maW5hbCwgY29sbGFwc2U9IiwgIiksICJcbiIpDQpjYXQoIi0gTWFjcm8gdmFyaWFibGVzICgiLCBsZW5ndGgoYmFua3J1cHRjeV9tYWNyb19maW5hbCksICIpOiIsIA0KICAgIHBhc3RlKGJhbmtydXB0Y3lfbWFjcm9fZmluYWwsIGNvbGxhcHNlPSIsICIpLCAiXG4iKQ0KDQpjYXQoIlxuRmluYWwgc2VsZWN0ZWQgdmFyaWFibGVzIGZvciBhY3F1aXNpdGlvbiBtb2RlbDoiLCBsZW5ndGgoYWNxdWlzaXRpb25fdmFycyksICJ2YXJpYWJsZXNcbiIpDQphY3F1aXNpdGlvbl9maW5fZmluYWwgPC0gYWNxdWlzaXRpb25fdmFyc1thY3F1aXNpdGlvbl92YXJzICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXNdDQphY3F1aXNpdGlvbl9tYWNyb19maW5hbCA8LSBhY3F1aXNpdGlvbl92YXJzW2FjcXVpc2l0aW9uX3ZhcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzXQ0KDQpjYXQoIi0gRmluYW5jaWFsIHZhcmlhYmxlcyAoIiwgbGVuZ3RoKGFjcXVpc2l0aW9uX2Zpbl9maW5hbCksICIpOiIsIA0KICAgIHBhc3RlKGFjcXVpc2l0aW9uX2Zpbl9maW5hbCwgY29sbGFwc2U9IiwgIiksICJcbiIpDQpjYXQoIi0gTWFjcm8gdmFyaWFibGVzICgiLCBsZW5ndGgoYWNxdWlzaXRpb25fbWFjcm9fZmluYWwpLCAiKToiLCANCiAgICBwYXN0ZShhY3F1aXNpdGlvbl9tYWNyb19maW5hbCwgY29sbGFwc2U9IiwgIiksICJcbiIpDQoNCiMgU2F2ZSBzZWxlY3Rpb24gcmVzdWx0cyBmb3IgbGF0ZXIgc3RlcHMNCnNlbGVjdGlvbl9yZXN1bHRzIDwtIGxpc3QoDQogIGJhbmtydXB0Y3kgPSBsaXN0KA0KICAgIHN0ZXB3aXNlID0gYmFua3J1cHRjeV92YXJzX3N0ZXAsDQogICAgbGFzc28gPSBiYW5rcnVwdGN5X3ZhcnNfbGFzc28sDQogICAgZmluYWwgPSBiYW5rcnVwdGN5X3ZhcnMsDQogICAgZmluYW5jaWFsID0gYmFua3J1cHRjeV9maW5fZmluYWwsDQogICAgbWFjcm8gPSBiYW5rcnVwdGN5X21hY3JvX2ZpbmFsDQogICksDQogIGFjcXVpc2l0aW9uID0gbGlzdCgNCiAgICBzdGVwd2lzZSA9IGFjcXVpc2l0aW9uX3ZhcnNfc3RlcCwNCiAgICBsYXNzbyA9IGFjcXVpc2l0aW9uX3ZhcnNfbGFzc28sDQogICAgZmluYWwgPSBhY3F1aXNpdGlvbl92YXJzLA0KICAgIGZpbmFuY2lhbCA9IGFjcXVpc2l0aW9uX2Zpbl9maW5hbCwNCiAgICBtYWNybyA9IGFjcXVpc2l0aW9uX21hY3JvX2ZpbmFsDQogICkNCikNCg0KIyBDcmVhdGUgZm9ybXVsYXMgZm9yIHRoZSBmaW5hbCBtb2RlbHMNCmJhbmtydXB0Y3lfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgNCiAgIlN1cnYodHN0YXJ0LCB0c3RvcCwgYmFua3J1cHRjeSkgfiAiLCANCiAgcGFzdGUoYmFua3J1cHRjeV92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwNCiAgIiArIHN0cmF0YShnc2VjdG9yKSINCikpDQoNCmFjcXVpc2l0aW9uX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoDQogICJTdXJ2KHRzdGFydCwgdHN0b3AsIGFjcXVpc2l0aW9uKSB+ICIsIA0KICBwYXN0ZShhY3F1aXNpdGlvbl92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwNCiAgIiArIHN0cmF0YShnc2VjdG9yKSINCikpDQoNCmNhdCgiXG5GaW5hbCBiYW5rcnVwdGN5IG1vZGVsIGZvcm11bGE6XG4iKQ0KcHJpbnQoYmFua3J1cHRjeV9mb3JtdWxhKQ0KDQpjYXQoIlxuRmluYWwgYWNxdWlzaXRpb24gbW9kZWwgZm9ybXVsYTpcbiIpDQpwcmludChhY3F1aXNpdGlvbl9mb3JtdWxhKQ0KDQojIFNhdmUgZm9ybXVsYXMgZm9yIGxhdGVyIHN0ZXBzDQpzZWxlY3Rpb25fcmVzdWx0cyRiYW5rcnVwdGN5JGZvcm11bGEgPC0gYmFua3J1cHRjeV9mb3JtdWxhDQpzZWxlY3Rpb25fcmVzdWx0cyRhY3F1aXNpdGlvbiRmb3JtdWxhIDwtIGFjcXVpc2l0aW9uX2Zvcm11bGENCmBgYA0KRmluYWxseSwgd2UgZml0IHRoZSBjYXVzZS1zcGVjaWZpYyBDb3ggbW9kZWxzIHVzaW5nIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMgZnJvbSBib3RoIHN0ZXB3aXNlIGFuZCBMQVNTTyBtZXRob2RzLiBXZSBhbHNvIGNyZWF0ZSBhIGZpbmFuY2lhbC1vbmx5IG1vZGVsIGFuZCBhIG1hY3JvLWVucmljaGVkIG1vZGVsIGZvciBjb21wYXJpc29uLiBXZSBldmFsdWF0ZSB0aGUgaW1wcm92ZW1lbnQgb2YgdGhlIG1vZGVscyB1c2luZyBsaWtlbGlob29kIHJhdGlvIHRlc3RzIGFuZCBjb21wYXJlIHRoZWlyIGNvbmNvcmRhbmNlIChDLWluZGV4KS4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1RFUCA4OiBGaXQgQ2F1c2UtU3BlY2lmaWMgQ294IE1vZGVscyB3aXRoIFNlbGVjdGVkIFZhcmlhYmxlcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG49PT0gU1RFUCA4OiBGSVRUSU5HIENBVVNFLVNQRUNJRklDIENPWCBNT0RFTFMgV0lUSCBGSU5BTkNJQUwgQU5EIE1BQ1JPIFZBUklBQkxFUyA9PT1cbiIpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDguMTogQkFOS1JVUFRDWSBNT0RFTFMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuOC4xOiBCQU5LUlVQVENZIFBSRURJQ1RJT04gTU9ERUxTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBDcmVhdGUgZm9ybXVsYXMgd2l0aCBkaWZmZXJlbnQgc2V0cyBvZiBzZWxlY3RlZCB2YXJpYWJsZXMNCiMgMS4gU3RlcHdpc2UgQUlDIHNlbGVjdGlvbiB2YXJpYWJsZXMNCmJhbmtydXB0Y3lfc3RlcF9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKA0KICAiU3Vydih0c3RhcnQsIHRzdG9wLCBiYW5rcnVwdGN5KSB+ICIsIA0KICBwYXN0ZShiYW5rcnVwdGN5X3ZhcnNfc3RlcCwgY29sbGFwc2UgPSAiICsgIiksIA0KICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KKSkNCg0KIyAyLiBMQVNTTyBzZWxlY3Rpb24gdmFyaWFibGVzDQpiYW5rcnVwdGN5X2xhc3NvX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoDQogICJTdXJ2KHRzdGFydCwgdHN0b3AsIGJhbmtydXB0Y3kpIH4gIiwgDQogIHBhc3RlKGJhbmtydXB0Y3lfdmFyc19sYXNzbywgY29sbGFwc2UgPSAiICsgIiksIA0KICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KKSkNCg0KIyAzLiBVbmlvbiBvZiBib3RoIG1ldGhvZHMgKGNvbXByZWhlbnNpdmUgYXBwcm9hY2gpDQpiYW5rcnVwdGN5X3VuaW9uX3ZhcnMgPC0gdW5pcXVlKGMoYmFua3J1cHRjeV92YXJzX3N0ZXAsIGJhbmtydXB0Y3lfdmFyc19sYXNzbykpDQpiYW5rcnVwdGN5X3VuaW9uX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoDQogICJTdXJ2KHRzdGFydCwgdHN0b3AsIGJhbmtydXB0Y3kpIH4gIiwgDQogIHBhc3RlKGJhbmtydXB0Y3lfdW5pb25fdmFycywgY29sbGFwc2UgPSAiICsgIiksIA0KICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KKSkNCg0KIyA0LiBJbnRlcnNlY3Rpb24gb2YgYm90aCBtZXRob2RzIChjb25zZXJ2YXRpdmUgYXBwcm9hY2gpDQpiYW5rcnVwdGN5X2ludGVyc2VjdF92YXJzIDwtIGludGVyc2VjdChiYW5rcnVwdGN5X3ZhcnNfc3RlcCwgYmFua3J1cHRjeV92YXJzX2xhc3NvKQ0KIyBDaGVjayBpZiBpbnRlcnNlY3Rpb24gaXMgbm90IGVtcHR5DQppZihsZW5ndGgoYmFua3J1cHRjeV9pbnRlcnNlY3RfdmFycykgPiAwKSB7DQogIGJhbmtydXB0Y3lfaW50ZXJzZWN0X2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoDQogICAgIlN1cnYodHN0YXJ0LCB0c3RvcCwgYmFua3J1cHRjeSkgfiAiLCANCiAgICBwYXN0ZShiYW5rcnVwdGN5X2ludGVyc2VjdF92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwgDQogICAgIiArIHN0cmF0YShnc2VjdG9yKSINCiAgKSkNCn0gZWxzZSB7DQogICMgSWYgaW50ZXJzZWN0aW9uIGlzIGVtcHR5LCB1c2UgWi1zY29yZSBhbmQgc2lnbmlmaWNhbnQgbWFjcm8gdmFycyBhcyBmYWxsYmFjaw0KICBjYXQoIk5vIHZhcmlhYmxlcyBpbiBjb21tb24gYmV0d2VlbiBzdGVwd2lzZSBhbmQgTEFTU08gZm9yIGJhbmtydXB0Y3kuIFVzaW5nIFotc2NvcmUgYW5kIHNpZ25pZmljYW50IG1hY3JvIHZhcmlhYmxlcyBhcyBmYWxsYmFjay5cbiIpDQogIGZhbGxiYWNrX3ZhcnMgPC0gYygiel9zY29yZSIsIHNpZ25pZmljYW50X2JrX21hY3JvKQ0KICBiYW5rcnVwdGN5X2ludGVyc2VjdF9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKA0KICAgICJTdXJ2KHRzdGFydCwgdHN0b3AsIGJhbmtydXB0Y3kpIH4gIiwgDQogICAgcGFzdGUoZmFsbGJhY2tfdmFycywgY29sbGFwc2UgPSAiICsgIiksIA0KICAgICIgKyBzdHJhdGEoZ3NlY3RvcikiDQogICkpDQogIGJhbmtydXB0Y3lfaW50ZXJzZWN0X3ZhcnMgPC0gZmFsbGJhY2tfdmFycw0KfQ0KDQojIDUuIEZpbmFuY2lhbC1vbmx5IG1vZGVsIChmb3IgY29tcGFyaXNvbikNCmJhbmtydXB0Y3lfZmluX29ubHlfdmFycyA8LSBiYW5rcnVwdGN5X3ZhcnNfc3RlcFtiYW5rcnVwdGN5X3ZhcnNfc3RlcCAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXQ0KYmFua3J1cHRjeV9maW5fb25seV9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKA0KICAiU3Vydih0c3RhcnQsIHRzdG9wLCBiYW5rcnVwdGN5KSB+ICIsIA0KICBwYXN0ZShiYW5rcnVwdGN5X2Zpbl9vbmx5X3ZhcnMsIGNvbGxhcHNlID0gIiArICIpLCANCiAgIiArIHN0cmF0YShnc2VjdG9yKSINCikpDQoNCiMgNi4gTWFjcm8tZW5yaWNoZWQgbW9kZWwgKGZpbmFuY2lhbCB2YXJpYWJsZXMgKyBhbGwgc2lnbmlmaWNhbnQgbWFjcm8pDQpiYW5rcnVwdGN5X21hY3JvX2VucmljaGVkX3ZhcnMgPC0gdW5pcXVlKGMoDQogIGJhbmtydXB0Y3lfZmluX29ubHlfdmFycywgICMgRmluYW5jaWFsIHZhcmlhYmxlcyBmcm9tIHN0ZXB3aXNlDQogIHNpZ25pZmljYW50X2JrX21hY3JvICAgICAgICMgQWxsIHNpZ25pZmljYW50IG1hY3JvIHZhcmlhYmxlcw0KKSkNCmJhbmtydXB0Y3lfbWFjcm9fZW5yaWNoZWRfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgNCiAgIlN1cnYodHN0YXJ0LCB0c3RvcCwgYmFua3J1cHRjeSkgfiAiLCANCiAgcGFzdGUoYmFua3J1cHRjeV9tYWNyb19lbnJpY2hlZF92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwgDQogICIgKyBzdHJhdGEoZ3NlY3RvcikiDQopKQ0KDQojIEZpdCB0aGUgbW9kZWxzIHdpdGggRWZyb24gYXBwcm94aW1hdGlvbiBmb3IgdGllcw0KY2F0KCJGaXR0aW5nIGJhbmtydXB0Y3kgbW9kZWxzIHdpdGggZGlmZmVyZW50IHZhcmlhYmxlIHNldHMuLi5cbiIpDQoNCiMgMS4gU3RlcHdpc2UgQUlDIG1vZGVsDQpiYW5rcnVwdGN5X3N0ZXBfbW9kZWwgPC0gY294cGgoYmFua3J1cHRjeV9zdGVwX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCmNhdCgiXG49PT0gQkFOS1JVUFRDWSBNT0RFTDogU1RFUFdJU0UgQUlDIFNFTEVDVElPTiA9PT1cbiIpDQpwcmludChzdW1tYXJ5KGJhbmtydXB0Y3lfc3RlcF9tb2RlbCkpDQoNCiMgMi4gTEFTU08gbW9kZWwNCmJhbmtydXB0Y3lfbGFzc29fbW9kZWwgPC0gY294cGgoYmFua3J1cHRjeV9sYXNzb19mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQpjYXQoIlxuPT09IEJBTktSVVBUQ1kgTU9ERUw6IExBU1NPIFNFTEVDVElPTiA9PT1cbiIpDQpwcmludChzdW1tYXJ5KGJhbmtydXB0Y3lfbGFzc29fbW9kZWwpKQ0KDQojIDMuIFVuaW9uIG1vZGVsDQpiYW5rcnVwdGN5X3VuaW9uX21vZGVsIDwtIGNveHBoKGJhbmtydXB0Y3lfdW5pb25fZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KY2F0KCJcbj09PSBCQU5LUlVQVENZIE1PREVMOiBVTklPTiBPRiBTVEVQV0lTRSAmIExBU1NPID09PVxuIikNCnByaW50KHN1bW1hcnkoYmFua3J1cHRjeV91bmlvbl9tb2RlbCkpDQoNCiMgNC4gSW50ZXJzZWN0aW9uIG1vZGVsDQpiYW5rcnVwdGN5X2ludGVyc2VjdF9tb2RlbCA8LSBjb3hwaChiYW5rcnVwdGN5X2ludGVyc2VjdF9mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQpjYXQoIlxuPT09IEJBTktSVVBUQ1kgTU9ERUw6IElOVEVSU0VDVElPTiBPRiBTVEVQV0lTRSAmIExBU1NPID09PVxuIikNCnByaW50KHN1bW1hcnkoYmFua3J1cHRjeV9pbnRlcnNlY3RfbW9kZWwpKQ0KDQojIDUuIEZpbmFuY2lhbC1vbmx5IG1vZGVsDQpiYW5rcnVwdGN5X2Zpbl9vbmx5X21vZGVsIDwtIGNveHBoKGJhbmtydXB0Y3lfZmluX29ubHlfZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KY2F0KCJcbj09PSBCQU5LUlVQVENZIE1PREVMOiBGSU5BTkNJQUwgVkFSSUFCTEVTIE9OTFkgPT09XG4iKQ0KcHJpbnQoc3VtbWFyeShiYW5rcnVwdGN5X2Zpbl9vbmx5X21vZGVsKSkNCg0KIyA2LiBNYWNyby1lbnJpY2hlZCBtb2RlbA0KYmFua3J1cHRjeV9tYWNyb19lbnJpY2hlZF9tb2RlbCA8LSBjb3hwaChiYW5rcnVwdGN5X21hY3JvX2VucmljaGVkX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCmNhdCgiXG49PT0gQkFOS1JVUFRDWSBNT0RFTDogRklOQU5DSUFMICsgU0lHTklGSUNBTlQgTUFDUk8gVkFSSUFCTEVTID09PVxuIikNCnByaW50KHN1bW1hcnkoYmFua3J1cHRjeV9tYWNyb19lbnJpY2hlZF9tb2RlbCkpDQoNCiMgQ29tcGFyZSBtb2RlbHMNCmNhdCgiXG5Db21wYXJpc29uIG9mIEJhbmtydXB0Y3kgTW9kZWxzOlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQpiYW5rcnVwdGN5X21vZGVscyA8LSBsaXN0KA0KICBTdGVwd2lzZSA9IGJhbmtydXB0Y3lfc3RlcF9tb2RlbCwNCiAgTEFTU08gPSBiYW5rcnVwdGN5X2xhc3NvX21vZGVsLA0KICBVbmlvbiA9IGJhbmtydXB0Y3lfdW5pb25fbW9kZWwsDQogIEludGVyc2VjdGlvbiA9IGJhbmtydXB0Y3lfaW50ZXJzZWN0X21vZGVsLA0KICBGaW5hbmNpYWxfT25seSA9IGJhbmtydXB0Y3lfZmluX29ubHlfbW9kZWwsDQogIE1hY3JvX0VucmljaGVkID0gYmFua3J1cHRjeV9tYWNyb19lbnJpY2hlZF9tb2RlbA0KKQ0KDQpiYW5rcnVwdGN5X2NvbXBhcmlzb24gPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBuYW1lcyhiYW5rcnVwdGN5X21vZGVscyksDQogIEZpbl9WYXJzID0gYygNCiAgICBzdW0oYmFua3J1cHRjeV92YXJzX3N0ZXAgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcyksDQogICAgc3VtKGJhbmtydXB0Y3lfdmFyc19sYXNzbyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzKSwNCiAgICBzdW0oYmFua3J1cHRjeV91bmlvbl92YXJzICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXMpLA0KICAgIHN1bShiYW5rcnVwdGN5X2ludGVyc2VjdF92YXJzICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXMpLA0KICAgIGxlbmd0aChiYW5rcnVwdGN5X2Zpbl9vbmx5X3ZhcnMpLA0KICAgIHN1bShiYW5rcnVwdGN5X21hY3JvX2VucmljaGVkX3ZhcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcykNCiAgKSwNCiAgTWFjcm9fVmFycyA9IGMoDQogICAgc3VtKGJhbmtydXB0Y3lfdmFyc19zdGVwICVpbiUgbWFjcm9fY292YXJpYXRlcyksDQogICAgc3VtKGJhbmtydXB0Y3lfdmFyc19sYXNzbyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXMpLA0KICAgIHN1bShiYW5rcnVwdGN5X3VuaW9uX3ZhcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzKSwNCiAgICBzdW0oYmFua3J1cHRjeV9pbnRlcnNlY3RfdmFycyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXMpLA0KICAgIDAsDQogICAgc3VtKGJhbmtydXB0Y3lfbWFjcm9fZW5yaWNoZWRfdmFycyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXMpDQogICksDQogIFRvdGFsX1ZhcnMgPSBjKA0KICAgIGxlbmd0aChiYW5rcnVwdGN5X3ZhcnNfc3RlcCksDQogICAgbGVuZ3RoKGJhbmtydXB0Y3lfdmFyc19sYXNzbyksDQogICAgbGVuZ3RoKGJhbmtydXB0Y3lfdW5pb25fdmFycyksDQogICAgbGVuZ3RoKGJhbmtydXB0Y3lfaW50ZXJzZWN0X3ZhcnMpLA0KICAgIGxlbmd0aChiYW5rcnVwdGN5X2Zpbl9vbmx5X3ZhcnMpLA0KICAgIGxlbmd0aChiYW5rcnVwdGN5X21hY3JvX2VucmljaGVkX3ZhcnMpDQogICksDQogIEFJQyA9IHNhcHBseShiYW5rcnVwdGN5X21vZGVscywgQUlDKSwNCiAgQklDID0gc2FwcGx5KGJhbmtydXB0Y3lfbW9kZWxzLCBCSUMpLA0KICBDb25jb3JkYW5jZSA9IHNhcHBseShiYW5rcnVwdGN5X21vZGVscywgZnVuY3Rpb24obSkgcm91bmQobSRjb25jb3JkYW5jZVsiY29uY29yZGFuY2UiXSwgMykpLA0KICBMb2dMaWsgPSBzYXBwbHkoYmFua3J1cHRjeV9tb2RlbHMsIGZ1bmN0aW9uKG0pIHJvdW5kKG0kbG9nbGlrWzJdLCAyKSkNCikNCg0KcHJpbnQoYmFua3J1cHRjeV9jb21wYXJpc29uKQ0KDQojIElkZW50aWZ5IGJlc3QgYmFua3J1cHRjeSBtb2RlbCBiYXNlZCBvbiBBSUMNCmJlc3RfYmFua3J1cHRjeV9tb2RlbF9pZHggPC0gd2hpY2gubWluKGJhbmtydXB0Y3lfY29tcGFyaXNvbiRBSUMpDQpiZXN0X2JhbmtydXB0Y3lfbW9kZWxfbmFtZSA8LSBiYW5rcnVwdGN5X2NvbXBhcmlzb24kTW9kZWxbYmVzdF9iYW5rcnVwdGN5X21vZGVsX2lkeF0NCmJlc3RfYmFua3J1cHRjeV9tb2RlbCA8LSBiYW5rcnVwdGN5X21vZGVsc1tbYmVzdF9iYW5rcnVwdGN5X21vZGVsX25hbWVdXQ0KDQpjYXQoIlxuQmVzdCBiYW5rcnVwdGN5IG1vZGVsIGJhc2VkIG9uIEFJQzoiLCBiZXN0X2JhbmtydXB0Y3lfbW9kZWxfbmFtZSwgIlxuIikNCg0KIyBDYWxjdWxhdGUgaW1wcm92ZW1lbnQgb3ZlciBmaW5hbmNpYWwtb25seSBtb2RlbA0KaWYoYmVzdF9iYW5rcnVwdGN5X21vZGVsX25hbWUgIT0gIkZpbmFuY2lhbF9Pbmx5Iikgew0KICAjIExpa2VsaWhvb2QgcmF0aW8gdGVzdA0KICBscnQgPC0gYW5vdmEoYmFua3J1cHRjeV9maW5fb25seV9tb2RlbCwgYmVzdF9iYW5rcnVwdGN5X21vZGVsKQ0KICBjYXQoIlxuTGlrZWxpaG9vZCByYXRpbyB0ZXN0IGNvbXBhcmluZyBiZXN0IG1vZGVsIHRvIGZpbmFuY2lhbC1vbmx5IG1vZGVsOlxuIikNCiAgcHJpbnQobHJ0KQ0KICANCiAgIyBDb25jb3JkYW5jZSBpbXByb3ZlbWVudA0KICBjb25jX2ltcHJvdmVtZW50IDwtIGJlc3RfYmFua3J1cHRjeV9tb2RlbCRjb25jb3JkYW5jZVsiY29uY29yZGFuY2UiXSAtIA0KICAgICAgICAgICAgICAgICAgICBiYW5rcnVwdGN5X2Zpbl9vbmx5X21vZGVsJGNvbmNvcmRhbmNlWyJjb25jb3JkYW5jZSJdDQogIGNhdCgiXG5Db25jb3JkYW5jZSBpbXByb3ZlbWVudCBvdmVyIGZpbmFuY2lhbC1vbmx5IG1vZGVsOiIsIHJvdW5kKGNvbmNfaW1wcm92ZW1lbnQsIDQpLCAiXG4iKQ0KfQ0KDQpjYXQoIlRoaXMgbW9kZWwgd2lsbCBiZSB1c2VkIGZvciBzdWJzZXF1ZW50IGFuYWx5c2lzLlxuIikNCmBgYA0KU2FtZSBhcyBhYm92ZSwgd2UgZml0IHRoZSBhY3F1aXNpdGlvbiBtb2RlbHMgdXNpbmcgdGhlIHNlbGVjdGVkIHZhcmlhYmxlcyBmcm9tIGJvdGggc3RlcHdpc2UgYW5kIExBU1NPIG1ldGhvZHMuIFdlIGFsc28gY3JlYXRlIGEgZmluYW5jaWFsLW9ubHkgbW9kZWwgYW5kIGEgbWFjcm8tZW5yaWNoZWQgbW9kZWwgZm9yIGNvbXBhcmlzb24uIFdlIGV2YWx1YXRlIHRoZSBpbXByb3ZlbWVudCBvZiB0aGUgbW9kZWxzIHVzaW5nIGxpa2VsaWhvb2QgcmF0aW8gdGVzdHMgYW5kIGNvbXBhcmUgdGhlaXIgY29uY29yZGFuY2UgKEMtaW5kZXgpLg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyA4LjI6IEFDUVVJU0lUSU9OIE1PREVMUw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG44LjI6IEFDUVVJU0lUSU9OIFBSRURJQ1RJT04gTU9ERUxTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBDcmVhdGUgZm9ybXVsYXMgd2l0aCBkaWZmZXJlbnQgc2V0cyBvZiBzZWxlY3RlZCB2YXJpYWJsZXMNCiMgMS4gU3RlcHdpc2UgQUlDIHNlbGVjdGlvbiB2YXJpYWJsZXMNCmFjcXVpc2l0aW9uX3N0ZXBfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgNCiAgIlN1cnYodHN0YXJ0LCB0c3RvcCwgYWNxdWlzaXRpb24pIH4gIiwgDQogIHBhc3RlKGFjcXVpc2l0aW9uX3ZhcnNfc3RlcCwgY29sbGFwc2UgPSAiICsgIiksIA0KICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KKSkNCg0KIyAyLiBMQVNTTyBzZWxlY3Rpb24gdmFyaWFibGVzDQphY3F1aXNpdGlvbl9sYXNzb19mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKA0KICAiU3Vydih0c3RhcnQsIHRzdG9wLCBhY3F1aXNpdGlvbikgfiAiLCANCiAgcGFzdGUoYWNxdWlzaXRpb25fdmFyc19sYXNzbywgY29sbGFwc2UgPSAiICsgIiksIA0KICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KKSkNCg0KIyAzLiBVbmlvbiBvZiBib3RoIG1ldGhvZHMgKGNvbXByZWhlbnNpdmUgYXBwcm9hY2gpDQphY3F1aXNpdGlvbl91bmlvbl92YXJzIDwtIHVuaXF1ZShjKGFjcXVpc2l0aW9uX3ZhcnNfc3RlcCwgYWNxdWlzaXRpb25fdmFyc19sYXNzbykpDQphY3F1aXNpdGlvbl91bmlvbl9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKA0KICAiU3Vydih0c3RhcnQsIHRzdG9wLCBhY3F1aXNpdGlvbikgfiAiLCANCiAgcGFzdGUoYWNxdWlzaXRpb25fdW5pb25fdmFycywgY29sbGFwc2UgPSAiICsgIiksIA0KICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KKSkNCg0KIyA0LiBJbnRlcnNlY3Rpb24gb2YgYm90aCBtZXRob2RzIChjb25zZXJ2YXRpdmUgYXBwcm9hY2gpDQphY3F1aXNpdGlvbl9pbnRlcnNlY3RfdmFycyA8LSBpbnRlcnNlY3QoYWNxdWlzaXRpb25fdmFyc19zdGVwLCBhY3F1aXNpdGlvbl92YXJzX2xhc3NvKQ0KIyBDaGVjayBpZiBpbnRlcnNlY3Rpb24gaXMgbm90IGVtcHR5DQppZihsZW5ndGgoYWNxdWlzaXRpb25faW50ZXJzZWN0X3ZhcnMpID4gMCkgew0KICBhY3F1aXNpdGlvbl9pbnRlcnNlY3RfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgNCiAgICAiU3Vydih0c3RhcnQsIHRzdG9wLCBhY3F1aXNpdGlvbikgfiAiLCANCiAgICBwYXN0ZShhY3F1aXNpdGlvbl9pbnRlcnNlY3RfdmFycywgY29sbGFwc2UgPSAiICsgIiksIA0KICAgICIgKyBzdHJhdGEoZ3NlY3RvcikiDQogICkpDQp9IGVsc2Ugew0KICAjIElmIGludGVyc2VjdGlvbiBpcyBlbXB0eSwgdXNlIGtleSBmaW5hbmNpYWwgYW5kIHNpZ25pZmljYW50IG1hY3JvIHZhcnMgYXMgZmFsbGJhY2sNCiAgY2F0KCJObyB2YXJpYWJsZXMgaW4gY29tbW9uIGJldHdlZW4gc3RlcHdpc2UgYW5kIExBU1NPIGZvciBhY3F1aXNpdGlvbi4gVXNpbmcga2V5IGZpbmFuY2lhbCBhbmQgc2lnbmlmaWNhbnQgbWFjcm8gdmFyaWFibGVzIGFzIGZhbGxiYWNrLlxuIikNCiAga2V5X2Zpbl92YXJzIDwtIGludGVyc2VjdChjKCJSU0laRSIsICJMVE1UQSIsICJOSU1UQSIpLCBmaW5hbmNpYWxfY292YXJpYXRlcykNCiAgZmFsbGJhY2tfdmFycyA8LSBjKGtleV9maW5fdmFycywgc2lnbmlmaWNhbnRfYWNxX21hY3JvKQ0KICBhY3F1aXNpdGlvbl9pbnRlcnNlY3RfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgNCiAgICAiU3Vydih0c3RhcnQsIHRzdG9wLCBhY3F1aXNpdGlvbikgfiAiLCANCiAgICBwYXN0ZShmYWxsYmFja192YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwgDQogICAgIiArIHN0cmF0YShnc2VjdG9yKSINCiAgKSkNCiAgYWNxdWlzaXRpb25faW50ZXJzZWN0X3ZhcnMgPC0gZmFsbGJhY2tfdmFycw0KfQ0KDQojIDUuIEZpbmFuY2lhbC1vbmx5IG1vZGVsIChmb3IgY29tcGFyaXNvbikNCmFjcXVpc2l0aW9uX2Zpbl9vbmx5X3ZhcnMgPC0gYWNxdWlzaXRpb25fdmFyc19zdGVwW2FjcXVpc2l0aW9uX3ZhcnNfc3RlcCAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXQ0KYWNxdWlzaXRpb25fZmluX29ubHlfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlMCgNCiAgIlN1cnYodHN0YXJ0LCB0c3RvcCwgYWNxdWlzaXRpb24pIH4gIiwgDQogIHBhc3RlKGFjcXVpc2l0aW9uX2Zpbl9vbmx5X3ZhcnMsIGNvbGxhcHNlID0gIiArICIpLCANCiAgIiArIHN0cmF0YShnc2VjdG9yKSINCikpDQoNCiMgNi4gTWFjcm8tZW5yaWNoZWQgbW9kZWwgKGZpbmFuY2lhbCB2YXJpYWJsZXMgKyBhbGwgc2lnbmlmaWNhbnQgbWFjcm8pDQphY3F1aXNpdGlvbl9tYWNyb19lbnJpY2hlZF92YXJzIDwtIHVuaXF1ZShjKA0KICBhY3F1aXNpdGlvbl9maW5fb25seV92YXJzLCAgIyBGaW5hbmNpYWwgdmFyaWFibGVzIGZyb20gc3RlcHdpc2UNCiAgc2lnbmlmaWNhbnRfYWNxX21hY3JvICAgICAgICMgQWxsIHNpZ25pZmljYW50IG1hY3JvIHZhcmlhYmxlcw0KKSkNCmFjcXVpc2l0aW9uX21hY3JvX2VucmljaGVkX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoDQogICJTdXJ2KHRzdGFydCwgdHN0b3AsIGFjcXVpc2l0aW9uKSB+ICIsIA0KICBwYXN0ZShhY3F1aXNpdGlvbl9tYWNyb19lbnJpY2hlZF92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwgDQogICIgKyBzdHJhdGEoZ3NlY3RvcikiDQopKQ0KDQojIEZpdCB0aGUgbW9kZWxzIHdpdGggRWZyb24gYXBwcm94aW1hdGlvbiBmb3IgdGllcw0KY2F0KCJGaXR0aW5nIGFjcXVpc2l0aW9uIG1vZGVscyB3aXRoIGRpZmZlcmVudCB2YXJpYWJsZSBzZXRzLi4uXG4iKQ0KDQojIDEuIFN0ZXB3aXNlIEFJQyBtb2RlbA0KYWNxdWlzaXRpb25fc3RlcF9tb2RlbCA8LSBjb3hwaChhY3F1aXNpdGlvbl9zdGVwX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCmNhdCgiXG49PT0gQUNRVUlTSVRJT04gTU9ERUw6IFNURVBXSVNFIEFJQyBTRUxFQ1RJT04gPT09XG4iKQ0KcHJpbnQoc3VtbWFyeShhY3F1aXNpdGlvbl9zdGVwX21vZGVsKSkNCg0KIyAyLiBMQVNTTyBtb2RlbA0KYWNxdWlzaXRpb25fbGFzc29fbW9kZWwgPC0gY294cGgoYWNxdWlzaXRpb25fbGFzc29fZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KY2F0KCJcbj09PSBBQ1FVSVNJVElPTiBNT0RFTDogTEFTU08gU0VMRUNUSU9OID09PVxuIikNCnByaW50KHN1bW1hcnkoYWNxdWlzaXRpb25fbGFzc29fbW9kZWwpKQ0KDQojIDMuIFVuaW9uIG1vZGVsDQphY3F1aXNpdGlvbl91bmlvbl9tb2RlbCA8LSBjb3hwaChhY3F1aXNpdGlvbl91bmlvbl9mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQpjYXQoIlxuPT09IEFDUVVJU0lUSU9OIE1PREVMOiBVTklPTiBPRiBTVEVQV0lTRSAmIExBU1NPID09PVxuIikNCnByaW50KHN1bW1hcnkoYWNxdWlzaXRpb25fdW5pb25fbW9kZWwpKQ0KDQojIDQuIEludGVyc2VjdGlvbiBtb2RlbA0KYWNxdWlzaXRpb25faW50ZXJzZWN0X21vZGVsIDwtIGNveHBoKGFjcXVpc2l0aW9uX2ludGVyc2VjdF9mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQpjYXQoIlxuPT09IEFDUVVJU0lUSU9OIE1PREVMOiBJTlRFUlNFQ1RJT04gT0YgU1RFUFdJU0UgJiBMQVNTTyA9PT1cbiIpDQpwcmludChzdW1tYXJ5KGFjcXVpc2l0aW9uX2ludGVyc2VjdF9tb2RlbCkpDQoNCiMgNS4gRmluYW5jaWFsLW9ubHkgbW9kZWwNCmFjcXVpc2l0aW9uX2Zpbl9vbmx5X21vZGVsIDwtIGNveHBoKGFjcXVpc2l0aW9uX2Zpbl9vbmx5X2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCmNhdCgiXG49PT0gQUNRVUlTSVRJT04gTU9ERUw6IEZJTkFOQ0lBTCBWQVJJQUJMRVMgT05MWSA9PT1cbiIpDQpwcmludChzdW1tYXJ5KGFjcXVpc2l0aW9uX2Zpbl9vbmx5X21vZGVsKSkNCg0KIyA2LiBNYWNyby1lbnJpY2hlZCBtb2RlbA0KYWNxdWlzaXRpb25fbWFjcm9fZW5yaWNoZWRfbW9kZWwgPC0gY294cGgoYWNxdWlzaXRpb25fbWFjcm9fZW5yaWNoZWRfZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KY2F0KCJcbj09PSBBQ1FVSVNJVElPTiBNT0RFTDogRklOQU5DSUFMICsgU0lHTklGSUNBTlQgTUFDUk8gVkFSSUFCTEVTID09PVxuIikNCnByaW50KHN1bW1hcnkoYWNxdWlzaXRpb25fbWFjcm9fZW5yaWNoZWRfbW9kZWwpKQ0KDQojIENvbXBhcmUgbW9kZWxzDQpjYXQoIlxuQ29tcGFyaXNvbiBvZiBBY3F1aXNpdGlvbiBNb2RlbHM6XG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCmFjcXVpc2l0aW9uX21vZGVscyA8LSBsaXN0KA0KICBTdGVwd2lzZSA9IGFjcXVpc2l0aW9uX3N0ZXBfbW9kZWwsDQogIExBU1NPID0gYWNxdWlzaXRpb25fbGFzc29fbW9kZWwsDQogIFVuaW9uID0gYWNxdWlzaXRpb25fdW5pb25fbW9kZWwsDQogIEludGVyc2VjdGlvbiA9IGFjcXVpc2l0aW9uX2ludGVyc2VjdF9tb2RlbCwNCiAgRmluYW5jaWFsX09ubHkgPSBhY3F1aXNpdGlvbl9maW5fb25seV9tb2RlbCwNCiAgTWFjcm9fRW5yaWNoZWQgPSBhY3F1aXNpdGlvbl9tYWNyb19lbnJpY2hlZF9tb2RlbA0KKQ0KDQphY3F1aXNpdGlvbl9jb21wYXJpc29uIDwtIGRhdGEuZnJhbWUoDQogIE1vZGVsID0gbmFtZXMoYWNxdWlzaXRpb25fbW9kZWxzKSwNCiAgRmluX1ZhcnMgPSBjKA0KICAgIHN1bShhY3F1aXNpdGlvbl92YXJzX3N0ZXAgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcyksDQogICAgc3VtKGFjcXVpc2l0aW9uX3ZhcnNfbGFzc28gJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcyksDQogICAgc3VtKGFjcXVpc2l0aW9uX3VuaW9uX3ZhcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcyksDQogICAgc3VtKGFjcXVpc2l0aW9uX2ludGVyc2VjdF92YXJzICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXMpLA0KICAgIGxlbmd0aChhY3F1aXNpdGlvbl9maW5fb25seV92YXJzKSwNCiAgICBzdW0oYWNxdWlzaXRpb25fbWFjcm9fZW5yaWNoZWRfdmFycyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzKQ0KICApLA0KICBNYWNyb19WYXJzID0gYygNCiAgICBzdW0oYWNxdWlzaXRpb25fdmFyc19zdGVwICVpbiUgbWFjcm9fY292YXJpYXRlcyksDQogICAgc3VtKGFjcXVpc2l0aW9uX3ZhcnNfbGFzc28gJWluJSBtYWNyb19jb3ZhcmlhdGVzKSwNCiAgICBzdW0oYWNxdWlzaXRpb25fdW5pb25fdmFycyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXMpLA0KICAgIHN1bShhY3F1aXNpdGlvbl9pbnRlcnNlY3RfdmFycyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXMpLA0KICAgIDAsDQogICAgc3VtKGFjcXVpc2l0aW9uX21hY3JvX2VucmljaGVkX3ZhcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzKQ0KICApLA0KICBUb3RhbF9WYXJzID0gYygNCiAgICBsZW5ndGgoYWNxdWlzaXRpb25fdmFyc19zdGVwKSwNCiAgICBsZW5ndGgoYWNxdWlzaXRpb25fdmFyc19sYXNzbyksDQogICAgbGVuZ3RoKGFjcXVpc2l0aW9uX3VuaW9uX3ZhcnMpLA0KICAgIGxlbmd0aChhY3F1aXNpdGlvbl9pbnRlcnNlY3RfdmFycyksDQogICAgbGVuZ3RoKGFjcXVpc2l0aW9uX2Zpbl9vbmx5X3ZhcnMpLA0KICAgIGxlbmd0aChhY3F1aXNpdGlvbl9tYWNyb19lbnJpY2hlZF92YXJzKQ0KICApLA0KICBBSUMgPSBzYXBwbHkoYWNxdWlzaXRpb25fbW9kZWxzLCBBSUMpLA0KICBCSUMgPSBzYXBwbHkoYWNxdWlzaXRpb25fbW9kZWxzLCBCSUMpLA0KICBDb25jb3JkYW5jZSA9IHNhcHBseShhY3F1aXNpdGlvbl9tb2RlbHMsIGZ1bmN0aW9uKG0pIHJvdW5kKG0kY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0sIDMpKSwNCiAgTG9nTGlrID0gc2FwcGx5KGFjcXVpc2l0aW9uX21vZGVscywgZnVuY3Rpb24obSkgcm91bmQobSRsb2dsaWtbMl0sIDIpKQ0KKQ0KDQpwcmludChhY3F1aXNpdGlvbl9jb21wYXJpc29uKQ0KDQojIElkZW50aWZ5IGJlc3QgYWNxdWlzaXRpb24gbW9kZWwgYmFzZWQgb24gQUlDDQpiZXN0X2FjcXVpc2l0aW9uX21vZGVsX2lkeCA8LSB3aGljaC5taW4oYWNxdWlzaXRpb25fY29tcGFyaXNvbiRBSUMpDQpiZXN0X2FjcXVpc2l0aW9uX21vZGVsX25hbWUgPC0gYWNxdWlzaXRpb25fY29tcGFyaXNvbiRNb2RlbFtiZXN0X2FjcXVpc2l0aW9uX21vZGVsX2lkeF0NCmJlc3RfYWNxdWlzaXRpb25fbW9kZWwgPC0gYWNxdWlzaXRpb25fbW9kZWxzW1tiZXN0X2FjcXVpc2l0aW9uX21vZGVsX25hbWVdXQ0KDQpjYXQoIlxuQmVzdCBhY3F1aXNpdGlvbiBtb2RlbCBiYXNlZCBvbiBBSUM6IiwgYmVzdF9hY3F1aXNpdGlvbl9tb2RlbF9uYW1lLCAiXG4iKQ0KDQojIENhbGN1bGF0ZSBpbXByb3ZlbWVudCBvdmVyIGZpbmFuY2lhbC1vbmx5IG1vZGVsDQppZihiZXN0X2FjcXVpc2l0aW9uX21vZGVsX25hbWUgIT0gIkZpbmFuY2lhbF9Pbmx5Iikgew0KICAjIExpa2VsaWhvb2QgcmF0aW8gdGVzdA0KICBscnQgPC0gYW5vdmEoYWNxdWlzaXRpb25fZmluX29ubHlfbW9kZWwsIGJlc3RfYWNxdWlzaXRpb25fbW9kZWwpDQogIGNhdCgiXG5MaWtlbGlob29kIHJhdGlvIHRlc3QgY29tcGFyaW5nIGJlc3QgbW9kZWwgdG8gZmluYW5jaWFsLW9ubHkgbW9kZWw6XG4iKQ0KICBwcmludChscnQpDQogIA0KICAjIENvbmNvcmRhbmNlIGltcHJvdmVtZW50DQogIGNvbmNfaW1wcm92ZW1lbnQgPC0gYmVzdF9hY3F1aXNpdGlvbl9tb2RlbCRjb25jb3JkYW5jZVsiY29uY29yZGFuY2UiXSAtIA0KICAgICAgICAgICAgICAgICAgICBhY3F1aXNpdGlvbl9maW5fb25seV9tb2RlbCRjb25jb3JkYW5jZVsiY29uY29yZGFuY2UiXQ0KICBjYXQoIlxuQ29uY29yZGFuY2UgaW1wcm92ZW1lbnQgb3ZlciBmaW5hbmNpYWwtb25seSBtb2RlbDoiLCByb3VuZChjb25jX2ltcHJvdmVtZW50LCA0KSwgIlxuIikNCn0NCg0KY2F0KCJUaGlzIG1vZGVsIHdpbGwgYmUgdXNlZCBmb3Igc3Vic2VxdWVudCBhbmFseXNpcy5cbiIpDQoNCiMgU3RvcmUgdGhlIGJlc3QgbW9kZWxzIGFuZCB0aGVpciB2YXJpYWJsZXMgZm9yIG5leHQgc3RlcHMNCmJlc3RfbW9kZWxzIDwtIGxpc3QoDQogIGJhbmtydXB0Y3kgPSBsaXN0KA0KICAgIG1vZGVsID0gYmVzdF9iYW5rcnVwdGN5X21vZGVsLA0KICAgIG5hbWUgPSBiZXN0X2JhbmtydXB0Y3lfbW9kZWxfbmFtZSwNCiAgICB2YXJpYWJsZXMgPSBzd2l0Y2goYmVzdF9iYW5rcnVwdGN5X21vZGVsX25hbWUsDQogICAgICAgICAgICAgICAgICAgICAiU3RlcHdpc2UiID0gYmFua3J1cHRjeV92YXJzX3N0ZXAsDQogICAgICAgICAgICAgICAgICAgICAiTEFTU08iID0gYmFua3J1cHRjeV92YXJzX2xhc3NvLA0KICAgICAgICAgICAgICAgICAgICAgIlVuaW9uIiA9IGJhbmtydXB0Y3lfdW5pb25fdmFycywNCiAgICAgICAgICAgICAgICAgICAgICJJbnRlcnNlY3Rpb24iID0gYmFua3J1cHRjeV9pbnRlcnNlY3RfdmFycywNCiAgICAgICAgICAgICAgICAgICAgICJGaW5hbmNpYWxfT25seSIgPSBiYW5rcnVwdGN5X2Zpbl9vbmx5X3ZhcnMsDQogICAgICAgICAgICAgICAgICAgICAiTWFjcm9fRW5yaWNoZWQiID0gYmFua3J1cHRjeV9tYWNyb19lbnJpY2hlZF92YXJzKSwNCiAgICBmaW5fdmFycyA9IHN3aXRjaChiZXN0X2JhbmtydXB0Y3lfbW9kZWxfbmFtZSwNCiAgICAgICAgICAgICAgICAgICAgICJTdGVwd2lzZSIgPSBiYW5rcnVwdGN5X3ZhcnNfc3RlcFtiYW5rcnVwdGN5X3ZhcnNfc3RlcCAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXSwNCiAgICAgICAgICAgICAgICAgICAgICJMQVNTTyIgPSBiYW5rcnVwdGN5X3ZhcnNfbGFzc29bYmFua3J1cHRjeV92YXJzX2xhc3NvICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXNdLA0KICAgICAgICAgICAgICAgICAgICAgIlVuaW9uIiA9IGJhbmtydXB0Y3lfdW5pb25fdmFyc1tiYW5rcnVwdGN5X3VuaW9uX3ZhcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlc10sDQogICAgICAgICAgICAgICAgICAgICAiSW50ZXJzZWN0aW9uIiA9IGJhbmtydXB0Y3lfaW50ZXJzZWN0X3ZhcnNbYmFua3J1cHRjeV9pbnRlcnNlY3RfdmFycyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXSwNCiAgICAgICAgICAgICAgICAgICAgICJGaW5hbmNpYWxfT25seSIgPSBiYW5rcnVwdGN5X2Zpbl9vbmx5X3ZhcnMsDQogICAgICAgICAgICAgICAgICAgICAiTWFjcm9fRW5yaWNoZWQiID0gYmFua3J1cHRjeV9tYWNyb19lbnJpY2hlZF92YXJzW2JhbmtydXB0Y3lfbWFjcm9fZW5yaWNoZWRfdmFycyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXSksDQogICAgbWFjcm9fdmFycyA9IHN3aXRjaChiZXN0X2JhbmtydXB0Y3lfbW9kZWxfbmFtZSwNCiAgICAgICAgICAgICAgICAgICAgICAiU3RlcHdpc2UiID0gYmFua3J1cHRjeV92YXJzX3N0ZXBbYmFua3J1cHRjeV92YXJzX3N0ZXAgJWluJSBtYWNyb19jb3ZhcmlhdGVzXSwNCiAgICAgICAgICAgICAgICAgICAgICAiTEFTU08iID0gYmFua3J1cHRjeV92YXJzX2xhc3NvW2JhbmtydXB0Y3lfdmFyc19sYXNzbyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXNdLA0KICAgICAgICAgICAgICAgICAgICAgICJVbmlvbiIgPSBiYW5rcnVwdGN5X3VuaW9uX3ZhcnNbYmFua3J1cHRjeV91bmlvbl92YXJzICVpbiUgbWFjcm9fY292YXJpYXRlc10sDQogICAgICAgICAgICAgICAgICAgICAgIkludGVyc2VjdGlvbiIgPSBiYW5rcnVwdGN5X2ludGVyc2VjdF92YXJzW2JhbmtydXB0Y3lfaW50ZXJzZWN0X3ZhcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzXSwNCiAgICAgICAgICAgICAgICAgICAgICAiRmluYW5jaWFsX09ubHkiID0gY2hhcmFjdGVyKDApLA0KICAgICAgICAgICAgICAgICAgICAgICJNYWNyb19FbnJpY2hlZCIgPSBiYW5rcnVwdGN5X21hY3JvX2VucmljaGVkX3ZhcnNbYmFua3J1cHRjeV9tYWNyb19lbnJpY2hlZF92YXJzICVpbiUgbWFjcm9fY292YXJpYXRlc10pDQogICksDQogIGFjcXVpc2l0aW9uID0gbGlzdCgNCiAgICBtb2RlbCA9IGJlc3RfYWNxdWlzaXRpb25fbW9kZWwsDQogICAgbmFtZSA9IGJlc3RfYWNxdWlzaXRpb25fbW9kZWxfbmFtZSwNCiAgICB2YXJpYWJsZXMgPSBzd2l0Y2goYmVzdF9hY3F1aXNpdGlvbl9tb2RlbF9uYW1lLA0KICAgICAgICAgICAgICAgICAgICAgIlN0ZXB3aXNlIiA9IGFjcXVpc2l0aW9uX3ZhcnNfc3RlcCwNCiAgICAgICAgICAgICAgICAgICAgICJMQVNTTyIgPSBhY3F1aXNpdGlvbl92YXJzX2xhc3NvLA0KICAgICAgICAgICAgICAgICAgICAgIlVuaW9uIiA9IGFjcXVpc2l0aW9uX3VuaW9uX3ZhcnMsDQogICAgICAgICAgICAgICAgICAgICAiSW50ZXJzZWN0aW9uIiA9IGFjcXVpc2l0aW9uX2ludGVyc2VjdF92YXJzLA0KICAgICAgICAgICAgICAgICAgICAgIkZpbmFuY2lhbF9Pbmx5IiA9IGFjcXVpc2l0aW9uX2Zpbl9vbmx5X3ZhcnMsDQogICAgICAgICAgICAgICAgICAgICAiTWFjcm9fRW5yaWNoZWQiID0gYWNxdWlzaXRpb25fbWFjcm9fZW5yaWNoZWRfdmFycyksDQogICAgZmluX3ZhcnMgPSBzd2l0Y2goYmVzdF9hY3F1aXNpdGlvbl9tb2RlbF9uYW1lLA0KICAgICAgICAgICAgICAgICAgICAgIlN0ZXB3aXNlIiA9IGFjcXVpc2l0aW9uX3ZhcnNfc3RlcFthY3F1aXNpdGlvbl92YXJzX3N0ZXAgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlc10sDQogICAgICAgICAgICAgICAgICAgICAiTEFTU08iID0gYWNxdWlzaXRpb25fdmFyc19sYXNzb1thY3F1aXNpdGlvbl92YXJzX2xhc3NvICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXNdLA0KICAgICAgICAgICAgICAgICAgICAgIlVuaW9uIiA9IGFjcXVpc2l0aW9uX3VuaW9uX3ZhcnNbYWNxdWlzaXRpb25fdW5pb25fdmFycyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXSwNCiAgICAgICAgICAgICAgICAgICAgICJJbnRlcnNlY3Rpb24iID0gYWNxdWlzaXRpb25faW50ZXJzZWN0X3ZhcnNbYWNxdWlzaXRpb25faW50ZXJzZWN0X3ZhcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlc10sDQogICAgICAgICAgICAgICAgICAgICAiRmluYW5jaWFsX09ubHkiID0gYWNxdWlzaXRpb25fZmluX29ubHlfdmFycywNCiAgICAgICAgICAgICAgICAgICAgICJNYWNyb19FbnJpY2hlZCIgPSBhY3F1aXNpdGlvbl9tYWNyb19lbnJpY2hlZF92YXJzW2FjcXVpc2l0aW9uX21hY3JvX2VucmljaGVkX3ZhcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlc10pLA0KICAgIG1hY3JvX3ZhcnMgPSBzd2l0Y2goYmVzdF9hY3F1aXNpdGlvbl9tb2RlbF9uYW1lLA0KICAgICAgICAgICAgICAgICAgICAgICJTdGVwd2lzZSIgPSBhY3F1aXNpdGlvbl92YXJzX3N0ZXBbYWNxdWlzaXRpb25fdmFyc19zdGVwICVpbiUgbWFjcm9fY292YXJpYXRlc10sDQogICAgICAgICAgICAgICAgICAgICAgIkxBU1NPIiA9IGFjcXVpc2l0aW9uX3ZhcnNfbGFzc29bYWNxdWlzaXRpb25fdmFyc19sYXNzbyAlaW4lIG1hY3JvX2NvdmFyaWF0ZXNdLA0KICAgICAgICAgICAgICAgICAgICAgICJVbmlvbiIgPSBhY3F1aXNpdGlvbl91bmlvbl92YXJzW2FjcXVpc2l0aW9uX3VuaW9uX3ZhcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzXSwNCiAgICAgICAgICAgICAgICAgICAgICAiSW50ZXJzZWN0aW9uIiA9IGFjcXVpc2l0aW9uX2ludGVyc2VjdF92YXJzW2FjcXVpc2l0aW9uX2ludGVyc2VjdF92YXJzICVpbiUgbWFjcm9fY292YXJpYXRlc10sDQogICAgICAgICAgICAgICAgICAgICAgIkZpbmFuY2lhbF9Pbmx5IiA9IGNoYXJhY3RlcigwKSwNCiAgICAgICAgICAgICAgICAgICAgICAiTWFjcm9fRW5yaWNoZWQiID0gYWNxdWlzaXRpb25fbWFjcm9fZW5yaWNoZWRfdmFyc1thY3F1aXNpdGlvbl9tYWNyb19lbnJpY2hlZF92YXJzICVpbiUgbWFjcm9fY292YXJpYXRlc10pDQogICkNCikNCg0KIyBQcmludCBhIHN1bW1hcnkgb2YgbWFjcm8gdmFyaWFibGVzIGluIHRoZSBiZXN0IG1vZGVscw0KY2F0KCJcbj09PSBNQUNSTyBWQVJJQUJMRVMgSU4gQkVTVCBNT0RFTFMgPT09XG4iKQ0KY2F0KCJCYW5rcnVwdGN5IG1vZGVsICgiLCBiZXN0X21vZGVscyRiYW5rcnVwdGN5JG5hbWUsICIpOlxuIiwgc2VwPSIiKQ0KaWYobGVuZ3RoKGJlc3RfbW9kZWxzJGJhbmtydXB0Y3kkbWFjcm9fdmFycykgPiAwKSB7DQogIGNhdCgiLSBNYWNybyB2YXJpYWJsZXM6IiwgcGFzdGUoYmVzdF9tb2RlbHMkYmFua3J1cHRjeSRtYWNyb192YXJzLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikNCn0gZWxzZSB7DQogIGNhdCgiLSBObyBtYWNybyB2YXJpYWJsZXMgaW4gdGhpcyBtb2RlbFxuIikNCn0NCg0KY2F0KCJcbkFjcXVpc2l0aW9uIG1vZGVsICgiLCBiZXN0X21vZGVscyRhY3F1aXNpdGlvbiRuYW1lLCAiKTpcbiIsIHNlcD0iIikNCmlmKGxlbmd0aChiZXN0X21vZGVscyRhY3F1aXNpdGlvbiRtYWNyb192YXJzKSA+IDApIHsNCiAgY2F0KCItIE1hY3JvIHZhcmlhYmxlczoiLCBwYXN0ZShiZXN0X21vZGVscyRhY3F1aXNpdGlvbiRtYWNyb192YXJzLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikNCn0gZWxzZSB7DQogIGNhdCgiLSBObyBtYWNybyB2YXJpYWJsZXMgaW4gdGhpcyBtb2RlbFxuIikNCn0NCmBgYA0KQW4gaW1wb3J0YW50IHN0ZXAgaW4gc3Vydml2YWwgYW5hbHlzaXMgaXMgdG8gY2hlY2sgdGhlIHByb3BvcnRpb25hbCBoYXphcmRzIGFzc3VtcHRpb24uIFdlIHdpbGwgdXNlIFNjaG9lbmZlbGQgcmVzaWR1YWxzIHRvIHRlc3QgdGhpcyBhc3N1bXB0aW9uIGZvciBib3RoIHRoZSBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiBtb2RlbHMuIFdlIHdpbGwgYWxzbyB2aXN1YWxpemUgdGhlIHJlc2lkdWFscyBmb3IgZmluYW5jaWFsIGFuZCBtYWNybyB2YXJpYWJsZXMgdG8gaWRlbnRpZnkgYW55IHZpb2xhdGlvbnMgb2YgdGhlIGFzc3VtcHRpb24uDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgOTogUHJvcG9ydGlvbmFsIEhhemFyZHMgQXNzdW1wdGlvbiBDaGVjaw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG49PT0gU1RFUCA5OiBURVNUSU5HIFBST1BPUlRJT05BTCBIQVpBUkRTIEFTU1VNUFRJT04gV0lUSCBGT0NVUyBPTiBNQUNSTyBWQVJJQUJMRVMgPT09XG4iKQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyA5LjE6IEJBTktSVVBUQ1kgTU9ERUwgLSBQSCBBc3N1bXB0aW9uIENoZWNrDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjkuMTogQkFOS1JVUFRDWSBNT0RFTCAtIFByb3BvcnRpb25hbCBIYXphcmRzIFRlc3RcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQojIEV4dHJhY3QgYmVzdCBiYW5rcnVwdGN5IG1vZGVsIGZyb20gcHJldmlvdXMgc3RlcA0KY2F0KCJUZXN0aW5nIFBIIGFzc3VtcHRpb24gZm9yIGJlc3QgYmFua3J1cHRjeSBtb2RlbDoiLCBiZXN0X21vZGVscyRiYW5rcnVwdGN5JG5hbWUsICJcbiIpDQpiYW5rcnVwdGN5X21vZGVsIDwtIGJlc3RfbW9kZWxzJGJhbmtydXB0Y3kkbW9kZWwNCg0KIyBUZXN0IHByb3BvcnRpb25hbCBoYXphcmRzIGFzc3VtcHRpb24gdXNpbmcgU2Nob2VuZmVsZCByZXNpZHVhbHMNCnBoX3Rlc3RfYmFua3J1cHRjeSA8LSBjb3guenBoKGJhbmtydXB0Y3lfbW9kZWwpDQpjYXQoIlxuUHJvcG9ydGlvbmFsIEhhemFyZHMgVGVzdCBSZXN1bHRzIChTY2hvZW5mZWxkIFJlc2lkdWFscyk6XG4iKQ0KcHJpbnQocGhfdGVzdF9iYW5rcnVwdGN5KQ0KDQojIElkZW50aWZ5IGZpbmFuY2lhbCBhbmQgbWFjcm8gdmFyaWFibGVzIHZpb2xhdGluZyB0aGUgUEggYXNzdW1wdGlvbg0KYmFua3J1cHRjeV9waF92aW9sYXRvcnMgPC0gcm93bmFtZXMocGhfdGVzdF9iYW5rcnVwdGN5JHRhYmxlKVsNCiAgcGhfdGVzdF9iYW5rcnVwdGN5JHRhYmxlWywgInAiXSA8IDAuMDUgJiANCiAgcm93bmFtZXMocGhfdGVzdF9iYW5rcnVwdGN5JHRhYmxlKSAhPSAiR0xPQkFMIg0KXQ0KDQpiYW5rcnVwdGN5X2Zpbl92aW9sYXRvcnMgPC0gYmFua3J1cHRjeV9waF92aW9sYXRvcnNbYmFua3J1cHRjeV9waF92aW9sYXRvcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlc10NCmJhbmtydXB0Y3lfbWFjcm9fdmlvbGF0b3JzIDwtIGJhbmtydXB0Y3lfcGhfdmlvbGF0b3JzW2JhbmtydXB0Y3lfcGhfdmlvbGF0b3JzICVpbiUgbWFjcm9fY292YXJpYXRlc10NCg0KY2F0KCJcbkZpbmFuY2lhbCB2YXJpYWJsZXMgdmlvbGF0aW5nIFBIIGFzc3VtcHRpb24gaW4gYmFua3J1cHRjeSBtb2RlbCAocCA8IDAuMDUpOlxuIikNCmlmKGxlbmd0aChiYW5rcnVwdGN5X2Zpbl92aW9sYXRvcnMpID4gMCkgew0KICBwcmludChiYW5rcnVwdGN5X2Zpbl92aW9sYXRvcnMpDQp9IGVsc2Ugew0KICBjYXQoIk5vbmUgLSBBbGwgZmluYW5jaWFsIHZhcmlhYmxlcyBzYXRpc2Z5IHRoZSBwcm9wb3J0aW9uYWwgaGF6YXJkcyBhc3N1bXB0aW9uXG4iKQ0KfQ0KDQpjYXQoIlxuTWFjcm8gdmFyaWFibGVzIHZpb2xhdGluZyBQSCBhc3N1bXB0aW9uIGluIGJhbmtydXB0Y3kgbW9kZWwgKHAgPCAwLjA1KTpcbiIpDQppZihsZW5ndGgoYmFua3J1cHRjeV9tYWNyb192aW9sYXRvcnMpID4gMCkgew0KICBwcmludChiYW5rcnVwdGN5X21hY3JvX3Zpb2xhdG9ycykNCn0gZWxzZSB7DQogIGNhdCgiTm9uZSAtIEFsbCBtYWNybyB2YXJpYWJsZXMgc2F0aXNmeSB0aGUgcHJvcG9ydGlvbmFsIGhhemFyZHMgYXNzdW1wdGlvblxuIikNCn0NCg0KIyBWaXN1YWxpemUgU2Nob2VuZmVsZCByZXNpZHVhbHMgZm9yIGZpbmFuY2lhbCB2YXJpYWJsZXMNCmlmKGxlbmd0aChiZXN0X21vZGVscyRiYW5rcnVwdGN5JGZpbl92YXJzKSA+IDApIHsNCiAgY2F0KCJcbkNyZWF0aW5nIFNjaG9lbmZlbGQgcmVzaWR1YWwgcGxvdHMgZm9yIGZpbmFuY2lhbCB2YXJpYWJsZXMgaW4gYmFua3J1cHRjeSBtb2RlbC4uLlxuIikNCiAgcGFyKG1mcm93ID0gYygyLCAyKSkgICMgQWRqdXN0IGJhc2VkIG9uIG51bWJlciBvZiB2YXJpYWJsZXMNCiAgZm9yKGkgaW4gMTptaW4oNCwgbGVuZ3RoKGJlc3RfbW9kZWxzJGJhbmtydXB0Y3kkZmluX3ZhcnMpKSkgew0KICAgIHZhcl9uYW1lIDwtIGJlc3RfbW9kZWxzJGJhbmtydXB0Y3kkZmluX3ZhcnNbaV0NCiAgICB2YXJfaWR4IDwtIHdoaWNoKHJvd25hbWVzKHBoX3Rlc3RfYmFua3J1cHRjeSR0YWJsZSkgPT0gdmFyX25hbWUpDQogICAgaWYobGVuZ3RoKHZhcl9pZHgpID4gMCkgew0KICAgICAgcGxvdChwaF90ZXN0X2JhbmtydXB0Y3lbdmFyX2lkeF0sIG1haW49cGFzdGUoIlNjaG9lbmZlbGQgUmVzaWR1YWxzIGZvciIsIHZhcl9uYW1lKSwgDQogICAgICAgICAgIHhsYWI9IlRpbWUiLCB5bGFiPSJCZXRhKHQpIikNCiAgICAgIGFibGluZShoPTAsIGNvbD0icmVkIiwgbHR5PTIpDQogICAgfQ0KICB9DQogIHBhcihtZnJvdyA9IGMoMSwgMSkpDQp9DQoNCiMgVmlzdWFsaXplIFNjaG9lbmZlbGQgcmVzaWR1YWxzIGZvciBtYWNybyB2YXJpYWJsZXMgKGlmIGFueSkNCmlmKGxlbmd0aChiZXN0X21vZGVscyRiYW5rcnVwdGN5JG1hY3JvX3ZhcnMpID4gMCkgew0KICBjYXQoIlxuQ3JlYXRpbmcgU2Nob2VuZmVsZCByZXNpZHVhbCBwbG90cyBmb3IgbWFjcm8gdmFyaWFibGVzIGluIGJhbmtydXB0Y3kgbW9kZWwuLi5cbiIpDQogIHBhcihtZnJvdyA9IGMoMiwgMikpICAjIEFkanVzdCBiYXNlZCBvbiBudW1iZXIgb2YgdmFyaWFibGVzDQogIGZvcihpIGluIDE6bWluKDQsIGxlbmd0aChiZXN0X21vZGVscyRiYW5rcnVwdGN5JG1hY3JvX3ZhcnMpKSkgew0KICAgIHZhcl9uYW1lIDwtIGJlc3RfbW9kZWxzJGJhbmtydXB0Y3kkbWFjcm9fdmFyc1tpXQ0KICAgIHZhcl9pZHggPC0gd2hpY2gocm93bmFtZXMocGhfdGVzdF9iYW5rcnVwdGN5JHRhYmxlKSA9PSB2YXJfbmFtZSkNCiAgICBpZihsZW5ndGgodmFyX2lkeCkgPiAwKSB7DQogICAgICBwbG90KHBoX3Rlc3RfYmFua3J1cHRjeVt2YXJfaWR4XSwgbWFpbj1wYXN0ZSgiU2Nob2VuZmVsZCBSZXNpZHVhbHMgZm9yIiwgdmFyX25hbWUpLCANCiAgICAgICAgICAgeGxhYj0iVGltZSIsIHlsYWI9IkJldGEodCkiKQ0KICAgICAgYWJsaW5lKGg9MCwgY29sPSJyZWQiLCBsdHk9MikNCiAgICB9DQogIH0NCiAgcGFyKG1mcm93ID0gYygxLCAxKSkNCn0NCg0KIyBDaGVjayBnbG9iYWwgdGVzdA0KZ2xvYmFsX3BoX2JhbmtydXB0Y3kgPC0gcGhfdGVzdF9iYW5rcnVwdGN5JHRhYmxlWyJHTE9CQUwiLCAicCJdDQpjYXQoIlxuR2xvYmFsIHRlc3Qgb2YgcHJvcG9ydGlvbmFsIGhhemFyZHMgYXNzdW1wdGlvbiBmb3IgYmFua3J1cHRjeSBtb2RlbDogcCA9IiwgDQogICAgcm91bmQoZ2xvYmFsX3BoX2JhbmtydXB0Y3ksIDQpLCAiXG4iKQ0KaWYoZ2xvYmFsX3BoX2JhbmtydXB0Y3kgPCAwLjA1KSB7DQogIGNhdCgiVGhlIGdsb2JhbCB0ZXN0IGluZGljYXRlcyB2aW9sYXRpb24gb2YgdGhlIFBIIGFzc3VtcHRpb24gKHAgPCAwLjA1KS5cbiIpDQogIGNhdCgiVGltZS1kZXBlbmRlbnQgY29lZmZpY2llbnRzIHNob3VsZCBiZSBjb25zaWRlcmVkIGluIHRoZSBuZXh0IHN0ZXAuXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCJUaGUgZ2xvYmFsIHRlc3Qgc3VwcG9ydHMgdGhlIHByb3BvcnRpb25hbCBoYXphcmRzIGFzc3VtcHRpb24gKHAgPj0gMC4wNSkuXG4iKQ0KfQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyA5LjI6IEFDUVVJU0lUSU9OIE1PREVMIC0gUEggQXNzdW1wdGlvbiBDaGVjaw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG45LjI6IEFDUVVJU0lUSU9OIE1PREVMIC0gUHJvcG9ydGlvbmFsIEhhemFyZHMgVGVzdFxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRXh0cmFjdCBiZXN0IGFjcXVpc2l0aW9uIG1vZGVsIGZyb20gcHJldmlvdXMgc3RlcA0KY2F0KCJUZXN0aW5nIFBIIGFzc3VtcHRpb24gZm9yIGJlc3QgYWNxdWlzaXRpb24gbW9kZWw6IiwgYmVzdF9tb2RlbHMkYWNxdWlzaXRpb24kbmFtZSwgIlxuIikNCmFjcXVpc2l0aW9uX21vZGVsIDwtIGJlc3RfbW9kZWxzJGFjcXVpc2l0aW9uJG1vZGVsDQoNCiMgVGVzdCBwcm9wb3J0aW9uYWwgaGF6YXJkcyBhc3N1bXB0aW9uIHVzaW5nIFNjaG9lbmZlbGQgcmVzaWR1YWxzDQpwaF90ZXN0X2FjcXVpc2l0aW9uIDwtIGNveC56cGgoYWNxdWlzaXRpb25fbW9kZWwpDQpjYXQoIlxuUHJvcG9ydGlvbmFsIEhhemFyZHMgVGVzdCBSZXN1bHRzIChTY2hvZW5mZWxkIFJlc2lkdWFscyk6XG4iKQ0KcHJpbnQocGhfdGVzdF9hY3F1aXNpdGlvbikNCg0KIyBJZGVudGlmeSBmaW5hbmNpYWwgYW5kIG1hY3JvIHZhcmlhYmxlcyB2aW9sYXRpbmcgdGhlIFBIIGFzc3VtcHRpb24NCmFjcXVpc2l0aW9uX3BoX3Zpb2xhdG9ycyA8LSByb3duYW1lcyhwaF90ZXN0X2FjcXVpc2l0aW9uJHRhYmxlKVsNCiAgcGhfdGVzdF9hY3F1aXNpdGlvbiR0YWJsZVssICJwIl0gPCAwLjA1ICYgDQogIHJvd25hbWVzKHBoX3Rlc3RfYWNxdWlzaXRpb24kdGFibGUpICE9ICJHTE9CQUwiDQpdDQoNCmFjcXVpc2l0aW9uX2Zpbl92aW9sYXRvcnMgPC0gYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzW2FjcXVpc2l0aW9uX3BoX3Zpb2xhdG9ycyAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzXQ0KYWNxdWlzaXRpb25fbWFjcm9fdmlvbGF0b3JzIDwtIGFjcXVpc2l0aW9uX3BoX3Zpb2xhdG9yc1thY3F1aXNpdGlvbl9waF92aW9sYXRvcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzXQ0KDQpjYXQoIlxuRmluYW5jaWFsIHZhcmlhYmxlcyB2aW9sYXRpbmcgUEggYXNzdW1wdGlvbiBpbiBhY3F1aXNpdGlvbiBtb2RlbCAocCA8IDAuMDUpOlxuIikNCmlmKGxlbmd0aChhY3F1aXNpdGlvbl9maW5fdmlvbGF0b3JzKSA+IDApIHsNCiAgcHJpbnQoYWNxdWlzaXRpb25fZmluX3Zpb2xhdG9ycykNCn0gZWxzZSB7DQogIGNhdCgiTm9uZSAtIEFsbCBmaW5hbmNpYWwgdmFyaWFibGVzIHNhdGlzZnkgdGhlIHByb3BvcnRpb25hbCBoYXphcmRzIGFzc3VtcHRpb25cbiIpDQp9DQoNCmNhdCgiXG5NYWNybyB2YXJpYWJsZXMgdmlvbGF0aW5nIFBIIGFzc3VtcHRpb24gaW4gYWNxdWlzaXRpb24gbW9kZWwgKHAgPCAwLjA1KTpcbiIpDQppZihsZW5ndGgoYWNxdWlzaXRpb25fbWFjcm9fdmlvbGF0b3JzKSA+IDApIHsNCiAgcHJpbnQoYWNxdWlzaXRpb25fbWFjcm9fdmlvbGF0b3JzKQ0KfSBlbHNlIHsNCiAgY2F0KCJOb25lIC0gQWxsIG1hY3JvIHZhcmlhYmxlcyBzYXRpc2Z5IHRoZSBwcm9wb3J0aW9uYWwgaGF6YXJkcyBhc3N1bXB0aW9uXG4iKQ0KfQ0KDQojIFZpc3VhbGl6ZSBTY2hvZW5mZWxkIHJlc2lkdWFscyBmb3IgZmluYW5jaWFsIHZhcmlhYmxlcw0KaWYobGVuZ3RoKGJlc3RfbW9kZWxzJGFjcXVpc2l0aW9uJGZpbl92YXJzKSA+IDApIHsNCiAgY2F0KCJcbkNyZWF0aW5nIFNjaG9lbmZlbGQgcmVzaWR1YWwgcGxvdHMgZm9yIGZpbmFuY2lhbCB2YXJpYWJsZXMgaW4gYWNxdWlzaXRpb24gbW9kZWwuLi5cbiIpDQogIHBhcihtZnJvdyA9IGMoMiwgMikpICAjIEFkanVzdCBiYXNlZCBvbiBudW1iZXIgb2YgdmFyaWFibGVzDQogIGZvcihpIGluIDE6bWluKDQsIGxlbmd0aChiZXN0X21vZGVscyRhY3F1aXNpdGlvbiRmaW5fdmFycykpKSB7DQogICAgdmFyX25hbWUgPC0gYmVzdF9tb2RlbHMkYWNxdWlzaXRpb24kZmluX3ZhcnNbaV0NCiAgICB2YXJfaWR4IDwtIHdoaWNoKHJvd25hbWVzKHBoX3Rlc3RfYWNxdWlzaXRpb24kdGFibGUpID09IHZhcl9uYW1lKQ0KICAgIGlmKGxlbmd0aCh2YXJfaWR4KSA+IDApIHsNCiAgICAgIHBsb3QocGhfdGVzdF9hY3F1aXNpdGlvblt2YXJfaWR4XSwgbWFpbj1wYXN0ZSgiU2Nob2VuZmVsZCBSZXNpZHVhbHMgZm9yIiwgdmFyX25hbWUpLCANCiAgICAgICAgICAgeGxhYj0iVGltZSIsIHlsYWI9IkJldGEodCkiKQ0KICAgICAgYWJsaW5lKGg9MCwgY29sPSJyZWQiLCBsdHk9MikNCiAgICB9DQogIH0NCiAgcGFyKG1mcm93ID0gYygxLCAxKSkNCn0NCg0KIyBWaXN1YWxpemUgU2Nob2VuZmVsZCByZXNpZHVhbHMgZm9yIG1hY3JvIHZhcmlhYmxlcyAoaWYgYW55KQ0KaWYobGVuZ3RoKGJlc3RfbW9kZWxzJGFjcXVpc2l0aW9uJG1hY3JvX3ZhcnMpID4gMCkgew0KICBjYXQoIlxuQ3JlYXRpbmcgU2Nob2VuZmVsZCByZXNpZHVhbCBwbG90cyBmb3IgbWFjcm8gdmFyaWFibGVzIGluIGFjcXVpc2l0aW9uIG1vZGVsLi4uXG4iKQ0KICBwYXIobWZyb3cgPSBjKDIsIDIpKSAgIyBBZGp1c3QgYmFzZWQgb24gbnVtYmVyIG9mIHZhcmlhYmxlcw0KICBmb3IoaSBpbiAxOm1pbig0LCBsZW5ndGgoYmVzdF9tb2RlbHMkYWNxdWlzaXRpb24kbWFjcm9fdmFycykpKSB7DQogICAgdmFyX25hbWUgPC0gYmVzdF9tb2RlbHMkYWNxdWlzaXRpb24kbWFjcm9fdmFyc1tpXQ0KICAgIHZhcl9pZHggPC0gd2hpY2gocm93bmFtZXMocGhfdGVzdF9hY3F1aXNpdGlvbiR0YWJsZSkgPT0gdmFyX25hbWUpDQogICAgaWYobGVuZ3RoKHZhcl9pZHgpID4gMCkgew0KICAgICAgcGxvdChwaF90ZXN0X2FjcXVpc2l0aW9uW3Zhcl9pZHhdLCBtYWluPXBhc3RlKCJTY2hvZW5mZWxkIFJlc2lkdWFscyBmb3IiLCB2YXJfbmFtZSksIA0KICAgICAgICAgICB4bGFiPSJUaW1lIiwgeWxhYj0iQmV0YSh0KSIpDQogICAgICBhYmxpbmUoaD0wLCBjb2w9InJlZCIsIGx0eT0yKQ0KICAgIH0NCiAgfQ0KICBwYXIobWZyb3cgPSBjKDEsIDEpKQ0KfQ0KDQojIENoZWNrIGdsb2JhbCB0ZXN0DQpnbG9iYWxfcGhfYWNxdWlzaXRpb24gPC0gcGhfdGVzdF9hY3F1aXNpdGlvbiR0YWJsZVsiR0xPQkFMIiwgInAiXQ0KY2F0KCJcbkdsb2JhbCB0ZXN0IG9mIHByb3BvcnRpb25hbCBoYXphcmRzIGFzc3VtcHRpb24gZm9yIGFjcXVpc2l0aW9uIG1vZGVsOiBwID0iLCANCiAgICByb3VuZChnbG9iYWxfcGhfYWNxdWlzaXRpb24sIDQpLCAiXG4iKQ0KaWYoZ2xvYmFsX3BoX2FjcXVpc2l0aW9uIDwgMC4wNSkgew0KICBjYXQoIlRoZSBnbG9iYWwgdGVzdCBpbmRpY2F0ZXMgdmlvbGF0aW9uIG9mIHRoZSBQSCBhc3N1bXB0aW9uIChwIDwgMC4wNSkuXG4iKQ0KICBjYXQoIlRpbWUtZGVwZW5kZW50IGNvZWZmaWNpZW50cyBzaG91bGQgYmUgY29uc2lkZXJlZCBpbiB0aGUgbmV4dCBzdGVwLlxuIikNCn0gZWxzZSB7DQogIGNhdCgiVGhlIGdsb2JhbCB0ZXN0IHN1cHBvcnRzIHRoZSBwcm9wb3J0aW9uYWwgaGF6YXJkcyBhc3N1bXB0aW9uIChwID49IDAuMDUpLlxuIikNCn0NCmBgYA0KTm93IHRoYXQgd2Uga25vdyB3aGljaCB2YXJpYWJsZXMgdmlvbGF0ZSB0aGUgcHJvcG9ydGlvbmFsIGhhemFyZHMgYXNzdW1wdGlvbiwgd2UgY2FuIHRlc3QgZm9yIHRpbWUgaW50ZXJhY3Rpb25zLiBXZSB3aWxsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBgdHRgIHRoYXQgcmVwcmVzZW50cyB0aW1lIGZvciB0aGUgaW50ZXJhY3Rpb24gdGVybXMuIFdlIHdpbGwgdGhlbiBmaXQgbW9kZWxzIHdpdGggdGltZSBpbnRlcmFjdGlvbnMgZm9yIGJvdGggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gbW9kZWxzLCBhbmQgYW5hbHl6ZSB0aGUgcmVzdWx0cy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgOS4zOiBBbHRlcm5hdGl2ZSBUZXN0IC0gVGltZSBJbnRlcmFjdGlvbnMgKEZpeGVkKQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG45LjM6IEFsdGVybmF0aXZlIFRlc3QgLSBEaXJlY3QgVGltZSBJbnRlcmFjdGlvbnMgKEZpeGVkKVxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQpjYXQoIlRlc3RpbmcgZm9yIG5vbi1wcm9wb3J0aW9uYWxpdHkgdXNpbmcgZXhwbGljaXQgdGltZSBpbnRlcmFjdGlvbnMuLi5cbiIpDQoNCiMgRnVuY3Rpb24gdG8gdGVzdCB0aW1lIGludGVyYWN0aW9ucyBmb3IgYSBtb2RlbCAtIGZpeGVkIHZlcnNpb24NCnRlc3RfdGltZV9pbnRlcmFjdGlvbnMgPC0gZnVuY3Rpb24obW9kZWwsIG1vZGVsX3R5cGUpIHsNCiAgIyBHZXQgbW9kZWwgdmFyaWFibGVzDQogIG1vZGVsX3ZhcnMgPC0gbmFtZXMoY29lZihtb2RlbCkpDQogIG1vZGVsX3ZhcnMgPC0gbW9kZWxfdmFyc1shZ3JlcGwoInN0cmF0YSIsIG1vZGVsX3ZhcnMpXQ0KICANCiAgIyBDcmVhdGUgZGF0YSBmcmFtZSB0byBzdG9yZSByZXN1bHRzDQogIGludGVyYWN0aW9uX3Jlc3VsdHMgPC0gZGF0YS5mcmFtZSgNCiAgICBWYXJpYWJsZSA9IGNoYXJhY3RlcigpLA0KICAgIFR5cGUgPSBjaGFyYWN0ZXIoKSwNCiAgICBJbnRlcmFjdGlvbl9Db2VmID0gbnVtZXJpYygpLA0KICAgIFBfdmFsdWUgPSBudW1lcmljKCksDQogICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQogICkNCiAgDQogICMgQ3JlYXRlIGEgdGltZSB2YXJpYWJsZSBmb3IgaW50ZXJhY3Rpb25zIHRoYXQgd29uJ3QgY29uZmxpY3QNCiAgZGF0YSR0dCA8LSBkYXRhJHRzdG9wICAjIHR0ID0gdGltZSBmb3IgdGltZS1pbnRlcmFjdGlvbnMNCiAgDQogICMgVGVzdCBlYWNoIHZhcmlhYmxlIHdpdGggdGltZSBpbnRlcmFjdGlvbg0KICBmb3IodmFyIGluIG1vZGVsX3ZhcnMpIHsNCiAgICAjIERldGVybWluZSBpZiB0aGlzIGlzIGEgZmluYW5jaWFsIG9yIG1hY3JvIHZhcmlhYmxlDQogICAgdmFyX3R5cGUgPC0gaWYodmFyICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXMpICJGaW5hbmNpYWwiIGVsc2UgIk1hY3JvZWNvbm9taWMiDQogICAgDQogICAgIyBDcmVhdGUgZm9ybXVsYSB3aXRoIHRpbWUgaW50ZXJhY3Rpb24NCiAgICBpZihtb2RlbF90eXBlID09ICJiYW5rcnVwdGN5Iikgew0KICAgICAgZm9ybXVsYV93aXRoX2ludGVyYWN0aW9uIDwtIGFzLmZvcm11bGEocGFzdGUwKA0KICAgICAgICAiU3Vydih0c3RhcnQsIHRzdG9wLCBiYW5rcnVwdGN5KSB+ICIsIA0KICAgICAgICBwYXN0ZShtb2RlbF92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwNCiAgICAgICAgIiArICIsIHZhciwgIjp0dCIsICAjIFVzaW5nIHR0IGluc3RlYWQgb2YgdHN0b3ANCiAgICAgICAgIiArIHN0cmF0YShnc2VjdG9yKSINCiAgICAgICkpDQogICAgfSBlbHNlIHsNCiAgICAgIGZvcm11bGFfd2l0aF9pbnRlcmFjdGlvbiA8LSBhcy5mb3JtdWxhKHBhc3RlMCgNCiAgICAgICAgIlN1cnYodHN0YXJ0LCB0c3RvcCwgYWNxdWlzaXRpb24pIH4gIiwgDQogICAgICAgIHBhc3RlKG1vZGVsX3ZhcnMsIGNvbGxhcHNlID0gIiArICIpLA0KICAgICAgICAiICsgIiwgdmFyLCAiOnR0IiwgICMgVXNpbmcgdHQgaW5zdGVhZCBvZiB0c3RvcA0KICAgICAgICAiICsgc3RyYXRhKGdzZWN0b3IpIg0KICAgICAgKSkNCiAgICB9DQogICAgDQogICAgIyBGaXQgbW9kZWwgd2l0aCBpbnRlcmFjdGlvbg0KICAgIGludGVyYWN0aW9uX21vZGVsIDwtIHRyeUNhdGNoKHsNCiAgICAgIGNveHBoKGZvcm11bGFfd2l0aF9pbnRlcmFjdGlvbiwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICAgY2F0KCJFcnJvciBmaXR0aW5nIGludGVyYWN0aW9uIG1vZGVsIGZvciIsIHZhciwgIjoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9KQ0KICAgIA0KICAgIGlmKCFpcy5udWxsKGludGVyYWN0aW9uX21vZGVsKSkgew0KICAgICAgIyBFeHRyYWN0IGludGVyYWN0aW9uIGNvZWZmaWNpZW50IGFuZCBwLXZhbHVlDQogICAgICBpbnRlcmFjdGlvbl90ZXJtIDwtIHBhc3RlMCh2YXIsICI6dHQiKQ0KICAgICAgaWYoaW50ZXJhY3Rpb25fdGVybSAlaW4lIG5hbWVzKGNvZWYoaW50ZXJhY3Rpb25fbW9kZWwpKSkgew0KICAgICAgICBjb2VmX3ZhbCA8LSBjb2VmKGludGVyYWN0aW9uX21vZGVsKVtpbnRlcmFjdGlvbl90ZXJtXQ0KICAgICAgICBwX3ZhbCA8LSBzdW1tYXJ5KGludGVyYWN0aW9uX21vZGVsKSRjb2VmZmljaWVudHNbaW50ZXJhY3Rpb25fdGVybSwgIlByKD58enwpIl0NCiAgICAgICAgDQogICAgICAgICMgQWRkIHRvIHJlc3VsdHMNCiAgICAgICAgaW50ZXJhY3Rpb25fcmVzdWx0cyA8LSByYmluZChpbnRlcmFjdGlvbl9yZXN1bHRzLCBkYXRhLmZyYW1lKA0KICAgICAgICAgIFZhcmlhYmxlID0gdmFyLA0KICAgICAgICAgIFR5cGUgPSB2YXJfdHlwZSwNCiAgICAgICAgICBJbnRlcmFjdGlvbl9Db2VmID0gY29lZl92YWwsDQogICAgICAgICAgUF92YWx1ZSA9IHBfdmFsLA0KICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICAgICAgICApKQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICANCiAgIyBTb3J0IGJ5IHAtdmFsdWUNCiAgaW50ZXJhY3Rpb25fcmVzdWx0cyA8LSBpbnRlcmFjdGlvbl9yZXN1bHRzW29yZGVyKGludGVyYWN0aW9uX3Jlc3VsdHMkUF92YWx1ZSksIF0NCiAgDQogIHJldHVybihpbnRlcmFjdGlvbl9yZXN1bHRzKQ0KfQ0KDQojIFRlc3QgdGltZSBpbnRlcmFjdGlvbnMgZm9yIGJhbmtydXB0Y3kgbW9kZWwNCmJhbmtydXB0Y3lfaW50ZXJhY3Rpb25zIDwtIHRlc3RfdGltZV9pbnRlcmFjdGlvbnMoYmFua3J1cHRjeV9tb2RlbCwgImJhbmtydXB0Y3kiKQ0KY2F0KCJcblRpbWUgaW50ZXJhY3Rpb24gdGVzdCByZXN1bHRzIGZvciBiYW5rcnVwdGN5IG1vZGVsOlxuIikNCnByaW50KGJhbmtydXB0Y3lfaW50ZXJhY3Rpb25zKQ0KDQojIFRlc3QgdGltZSBpbnRlcmFjdGlvbnMgZm9yIGFjcXVpc2l0aW9uIG1vZGVsDQphY3F1aXNpdGlvbl9pbnRlcmFjdGlvbnMgPC0gdGVzdF90aW1lX2ludGVyYWN0aW9ucyhhY3F1aXNpdGlvbl9tb2RlbCwgImFjcXVpc2l0aW9uIikNCmNhdCgiXG5UaW1lIGludGVyYWN0aW9uIHRlc3QgcmVzdWx0cyBmb3IgYWNxdWlzaXRpb24gbW9kZWw6XG4iKQ0KcHJpbnQoYWNxdWlzaXRpb25faW50ZXJhY3Rpb25zKQ0KDQojIEFuYWx5emUgdGltZSBpbnRlcmFjdGlvbnMgYnkgdmFyaWFibGUgdHlwZQ0KY2F0KCJcbkFuYWx5c2lzIG9mIHRpbWUgaW50ZXJhY3Rpb25zIGJ5IHZhcmlhYmxlIHR5cGU6XG4iKQ0KDQojIEZvciBiYW5rcnVwdGN5IG1vZGVsDQpiYW5rcnVwdGN5X2Zpbl9pbnRlcmFjdGlvbnMgPC0gYmFua3J1cHRjeV9pbnRlcmFjdGlvbnNbYmFua3J1cHRjeV9pbnRlcmFjdGlvbnMkVHlwZSA9PSAiRmluYW5jaWFsIiwgXQ0KYmFua3J1cHRjeV9tYWNyb19pbnRlcmFjdGlvbnMgPC0gYmFua3J1cHRjeV9pbnRlcmFjdGlvbnNbYmFua3J1cHRjeV9pbnRlcmFjdGlvbnMkVHlwZSA9PSAiTWFjcm9lY29ub21pYyIsIF0NCg0KY2F0KCJcbkJhbmtydXB0Y3kgbW9kZWwgLSBmaW5hbmNpYWwgdmFyaWFibGVzIHdpdGggc2lnbmlmaWNhbnQgdGltZSBpbnRlcmFjdGlvbnMgKHAgPCAwLjA1KTpcbiIpDQpzaWdfYmtfZmluIDwtIGJhbmtydXB0Y3lfZmluX2ludGVyYWN0aW9uc1tiYW5rcnVwdGN5X2Zpbl9pbnRlcmFjdGlvbnMkUF92YWx1ZSA8IDAuMDUsIF0NCmlmKG5yb3coc2lnX2JrX2ZpbikgPiAwKSB7DQogIHByaW50KHNpZ19ia19maW4pDQp9IGVsc2Ugew0KICBjYXQoIk5vIHNpZ25pZmljYW50IHRpbWUgaW50ZXJhY3Rpb25zIGZvdW5kIGZvciBmaW5hbmNpYWwgdmFyaWFibGVzLlxuIikNCn0NCg0KY2F0KCJcbkJhbmtydXB0Y3kgbW9kZWwgLSBtYWNybyB2YXJpYWJsZXMgd2l0aCBzaWduaWZpY2FudCB0aW1lIGludGVyYWN0aW9ucyAocCA8IDAuMDUpOlxuIikNCnNpZ19ia19tYWNybyA8LSBiYW5rcnVwdGN5X21hY3JvX2ludGVyYWN0aW9uc1tiYW5rcnVwdGN5X21hY3JvX2ludGVyYWN0aW9ucyRQX3ZhbHVlIDwgMC4wNSwgXQ0KaWYobnJvdyhzaWdfYmtfbWFjcm8pID4gMCkgew0KICBwcmludChzaWdfYmtfbWFjcm8pDQogIGNhdCgiXG5JbnRlcnByZXRhdGlvbjogVGhlc2UgbWFjcm8gdmFyaWFibGVzIGhhdmUgZWZmZWN0cyB0aGF0IGNoYW5nZSBvdmVyIHRpbWUsIHN1Z2dlc3RpbmdcbiIpDQogIGNhdCgidGhhdCBlY29ub21pYyBjb25kaXRpb25zIGhhdmUgYSB0aW1lLXZhcnlpbmcgaW1wYWN0IG9uIGJhbmtydXB0Y3kgcmlzay5cbiIpDQp9IGVsc2Ugew0KICBjYXQoIk5vIHNpZ25pZmljYW50IHRpbWUgaW50ZXJhY3Rpb25zIGZvdW5kIGZvciBtYWNybyB2YXJpYWJsZXMuXG4iKQ0KfQ0KDQojIEZvciBhY3F1aXNpdGlvbiBtb2RlbA0KYWNxdWlzaXRpb25fZmluX2ludGVyYWN0aW9ucyA8LSBhY3F1aXNpdGlvbl9pbnRlcmFjdGlvbnNbYWNxdWlzaXRpb25faW50ZXJhY3Rpb25zJFR5cGUgPT0gIkZpbmFuY2lhbCIsIF0NCmFjcXVpc2l0aW9uX21hY3JvX2ludGVyYWN0aW9ucyA8LSBhY3F1aXNpdGlvbl9pbnRlcmFjdGlvbnNbYWNxdWlzaXRpb25faW50ZXJhY3Rpb25zJFR5cGUgPT0gIk1hY3JvZWNvbm9taWMiLCBdDQoNCmNhdCgiXG5BY3F1aXNpdGlvbiBtb2RlbCAtIGZpbmFuY2lhbCB2YXJpYWJsZXMgd2l0aCBzaWduaWZpY2FudCB0aW1lIGludGVyYWN0aW9ucyAocCA8IDAuMDUpOlxuIikNCnNpZ19hY3FfZmluIDwtIGFjcXVpc2l0aW9uX2Zpbl9pbnRlcmFjdGlvbnNbYWNxdWlzaXRpb25fZmluX2ludGVyYWN0aW9ucyRQX3ZhbHVlIDwgMC4wNSwgXQ0KaWYobnJvdyhzaWdfYWNxX2ZpbikgPiAwKSB7DQogIHByaW50KHNpZ19hY3FfZmluKQ0KfSBlbHNlIHsNCiAgY2F0KCJObyBzaWduaWZpY2FudCB0aW1lIGludGVyYWN0aW9ucyBmb3VuZCBmb3IgZmluYW5jaWFsIHZhcmlhYmxlcy5cbiIpDQp9DQoNCmNhdCgiXG5BY3F1aXNpdGlvbiBtb2RlbCAtIG1hY3JvIHZhcmlhYmxlcyB3aXRoIHNpZ25pZmljYW50IHRpbWUgaW50ZXJhY3Rpb25zIChwIDwgMC4wNSk6XG4iKQ0Kc2lnX2FjcV9tYWNybyA8LSBhY3F1aXNpdGlvbl9tYWNyb19pbnRlcmFjdGlvbnNbYWNxdWlzaXRpb25fbWFjcm9faW50ZXJhY3Rpb25zJFBfdmFsdWUgPCAwLjA1LCBdDQppZihucm93KHNpZ19hY3FfbWFjcm8pID4gMCkgew0KICBwcmludChzaWdfYWNxX21hY3JvKQ0KICBjYXQoIlxuSW50ZXJwcmV0YXRpb246IFRoZXNlIG1hY3JvIHZhcmlhYmxlcyBoYXZlIGVmZmVjdHMgdGhhdCBjaGFuZ2Ugb3ZlciB0aW1lLCBzdWdnZXN0aW5nXG4iKQ0KICBjYXQoInRoYXQgZWNvbm9taWMgY29uZGl0aW9ucyBoYXZlIGEgdGltZS12YXJ5aW5nIGltcGFjdCBvbiBhY3F1aXNpdGlvbiBsaWtlbGlob29kLlxuIikNCn0gZWxzZSB7DQogIGNhdCgiTm8gc2lnbmlmaWNhbnQgdGltZSBpbnRlcmFjdGlvbnMgZm91bmQgZm9yIG1hY3JvIHZhcmlhYmxlcy5cbiIpDQp9DQoNCiMgQWRkaXRpb25hbCBjaGVjayBmb3IgYnVzaW5lc3MgY3ljbGUgZWZmZWN0cw0KY2F0KCJcbjkuNDogVGVzdGluZyBmb3IgQnVzaW5lc3MgQ3ljbGUgRWZmZWN0cyBpbiBNYWNybyBWYXJpYWJsZXNcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KY2F0KCJFeGFtaW5pbmcgaWYgbWFjcm8gdmFyaWFibGVzIHZpb2xhdGUgdGhlIFBIIGFzc3VtcHRpb24gZHVlIHRvIGJ1c2luZXNzIGN5Y2xlIGVmZmVjdHMuLi5cbiIpDQoNCiMgTWFjcm8gdmFyaWFibGVzIHRoYXQgdmlvbGF0ZSBQSCBhc3N1bXB0aW9uIGluIGVpdGhlciBtb2RlbA0KYWxsX21hY3JvX3Zpb2xhdG9ycyA8LSB1bmlxdWUoYyhiYW5rcnVwdGN5X21hY3JvX3Zpb2xhdG9ycywgYWNxdWlzaXRpb25fbWFjcm9fdmlvbGF0b3JzKSkNCg0KaWYobGVuZ3RoKGFsbF9tYWNyb192aW9sYXRvcnMpID4gMCkgew0KICBjYXQoIlxuVGhlIGZvbGxvd2luZyBtYWNybyB2YXJpYWJsZXMgdmlvbGF0ZSB0aGUgUEggYXNzdW1wdGlvbiwgcG90ZW50aWFsbHkgZHVlIHRvIGJ1c2luZXNzIGN5Y2xlIGVmZmVjdHM6XG4iKQ0KICBwcmludChhbGxfbWFjcm9fdmlvbGF0b3JzKQ0KICANCiAgY2F0KCJcblBvc3NpYmxlIGludGVycHJldGF0aW9uczpcbiIpDQogIGNhdCgiMS4gVGhlIGVmZmVjdCBvZiB0aGVzZSB2YXJpYWJsZXMgY2hhbmdlcyBvdmVyIHRpbWUgd2l0aCBlY29ub21pYyBjb25kaXRpb25zXG4iKQ0KICBjYXQoIjIuIFRoZWlyIGluZmx1ZW5jZSBtYXkgYmUgc3Ryb25nZXIgZHVyaW5nIHJlY2Vzc2lvbnMgb3IgZXhwYW5zaW9uc1xuIikNCiAgY2F0KCIzLiBUaW1lLXZhcnlpbmcgY29lZmZpY2llbnRzIG1heSBiZSBtb3JlIGFwcHJvcHJpYXRlIGZvciB0aGVzZSB2YXJpYWJsZXNcbiIpDQp9IGVsc2Ugew0KICBjYXQoIlxuTm8gbWFjcm8gdmFyaWFibGVzIHZpb2xhdGUgdGhlIFBIIGFzc3VtcHRpb24sIHN1Z2dlc3RpbmcgdGhhdCBtYWNyb2Vjb25vbWljXG4iKQ0KICBjYXQoImVmZmVjdHMgb24gY29tcGFueSBvdXRjb21lcyBhcmUgc3RhYmxlIG92ZXIgdGltZSByZWdhcmRsZXNzIG9mIGJ1c2luZXNzIGN5Y2xlcy5cbiIpDQp9DQoNCiMgU3RvcmUgcmVzdWx0cyBmb3IgbmV4dCBzdGVwDQpwaF90ZXN0X3Jlc3VsdHMgPC0gbGlzdCgNCiAgYmFua3J1cHRjeSA9IGxpc3QoDQogICAgc2Nob2VuZmVsZF90ZXN0ID0gcGhfdGVzdF9iYW5rcnVwdGN5LA0KICAgIGFsbF92aW9sYXRvcnMgPSBiYW5rcnVwdGN5X3BoX3Zpb2xhdG9ycywNCiAgICBmaW5fdmlvbGF0b3JzID0gYmFua3J1cHRjeV9maW5fdmlvbGF0b3JzLA0KICAgIG1hY3JvX3Zpb2xhdG9ycyA9IGJhbmtydXB0Y3lfbWFjcm9fdmlvbGF0b3JzLA0KICAgIHRpbWVfaW50ZXJhY3Rpb25zID0gYmFua3J1cHRjeV9pbnRlcmFjdGlvbnMsDQogICAgc2lnX2Zpbl9pbnRlcmFjdGlvbnMgPSBzaWdfYmtfZmluLA0KICAgIHNpZ19tYWNyb19pbnRlcmFjdGlvbnMgPSBzaWdfYmtfbWFjcm8NCiAgKSwNCiAgYWNxdWlzaXRpb24gPSBsaXN0KA0KICAgIHNjaG9lbmZlbGRfdGVzdCA9IHBoX3Rlc3RfYWNxdWlzaXRpb24sDQogICAgYWxsX3Zpb2xhdG9ycyA9IGFjcXVpc2l0aW9uX3BoX3Zpb2xhdG9ycywNCiAgICBmaW5fdmlvbGF0b3JzID0gYWNxdWlzaXRpb25fZmluX3Zpb2xhdG9ycywNCiAgICBtYWNyb192aW9sYXRvcnMgPSBhY3F1aXNpdGlvbl9tYWNyb192aW9sYXRvcnMsDQogICAgdGltZV9pbnRlcmFjdGlvbnMgPSBhY3F1aXNpdGlvbl9pbnRlcmFjdGlvbnMsDQogICAgc2lnX2Zpbl9pbnRlcmFjdGlvbnMgPSBzaWdfYWNxX2ZpbiwNCiAgICBzaWdfbWFjcm9faW50ZXJhY3Rpb25zID0gc2lnX2FjcV9tYWNybw0KICApDQopDQpgYGANCg0KDQpXZSB3aWxsIG5vdyBwcm9jZWVkIHRvIGZpdCB0aGUgZmluYWwgbW9kZWxzIHdpdGggdGltZS1kZXBlbmRlbnQgY29lZmZpY2llbnRzLiBXZSB3aWxsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBgdHRgIHRoYXQgcmVwcmVzZW50cyB0aW1lIGZvciB0aGUgaW50ZXJhY3Rpb24gdGVybXMuIFdlIHdpbGwgdGhlbiBmaXQgbW9kZWxzIHdpdGggdGltZSBpbnRlcmFjdGlvbnMgZm9yIGJvdGggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gbW9kZWxzLCBhbmQgYW5hbHl6ZSB0aGUgcmVzdWx0cy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgU1RFUCAxMDogRmluYWwgTW9kZWxzIHdpdGggVGltZS1EZXBlbmRlbnQgQ29lZmZpY2llbnRzIChNb2RpZmllZCkNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuPT09IFNURVAgMTA6IEZJTkFMIE1PREVMUyBXSVRIIFRJTUUtREVQRU5ERU5UIENPRUZGSUNJRU5UUyBBTkQgTUFDUk8gRUZGRUNUUyA9PT1cbiIpDQoNCiMgQ3JlYXRlIGEgdGltZSB2YXJpYWJsZSBmb3IgaW50ZXJhY3Rpb25zIGlmIGl0IGRvZXNuJ3QgZXhpc3QNCmlmKCEidHQiICVpbiUgY29sbmFtZXMoZGF0YSkpIHsNCiAgY2F0KCJDcmVhdGluZyB0aW1lIHZhcmlhYmxlICh0dCkgZm9yIHRpbWUtZGVwZW5kZW50IGNvZWZmaWNpZW50cy4uLlxuIikNCiAgZGF0YSR0dCA8LSBkYXRhJHRzdG9wICAjIHR0ID0gdGltZSBmb3IgdGltZS1pbnRlcmFjdGlvbnMNCn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTAuMTogQkFOS1JVUFRDWSBNT0RFTCAtIEFkZHJlc3MgUEggVmlvbGF0aW9ucw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMC4xOiBCQU5LUlVQVENZIE1PREVMIC0gQWRkcmVzc2luZyBQSCBWaW9sYXRpb25zXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBFeHRyYWN0IHZpb2xhdG9ycyBmcm9tIHByZXZpb3VzIHN0ZXAncyByZXN1bHRzLCBzZXBhcmF0ZWQgYnkgdmFyaWFibGUgdHlwZQ0KYmFua3J1cHRjeV9maW5fdmlvbGF0b3JzIDwtIHBoX3Rlc3RfcmVzdWx0cyRiYW5rcnVwdGN5JGZpbl92aW9sYXRvcnMNCmJhbmtydXB0Y3lfbWFjcm9fdmlvbGF0b3JzIDwtIHBoX3Rlc3RfcmVzdWx0cyRiYW5rcnVwdGN5JG1hY3JvX3Zpb2xhdG9ycw0KYmFua3J1cHRjeV9waF92aW9sYXRvcnMgPC0gcGhfdGVzdF9yZXN1bHRzJGJhbmtydXB0Y3kkYWxsX3Zpb2xhdG9ycw0KDQpjYXQoIkZpbmFuY2lhbCB2YXJpYWJsZXMgdmlvbGF0aW5nIFBIIGFzc3VtcHRpb24gaW4gYmFua3J1cHRjeSBtb2RlbDpcbiIpDQppZihsZW5ndGgoYmFua3J1cHRjeV9maW5fdmlvbGF0b3JzKSA+IDApIHsNCiAgcHJpbnQoYmFua3J1cHRjeV9maW5fdmlvbGF0b3JzKQ0KfSBlbHNlIHsNCiAgY2F0KCJOb25lIGZvdW5kIC0gQWxsIGZpbmFuY2lhbCB2YXJpYWJsZXMgc2F0aXNmeSBQSCBhc3N1bXB0aW9uXG4iKQ0KfQ0KDQpjYXQoIlxuTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgdmlvbGF0aW5nIFBIIGFzc3VtcHRpb24gaW4gYmFua3J1cHRjeSBtb2RlbDpcbiIpDQppZihsZW5ndGgoYmFua3J1cHRjeV9tYWNyb192aW9sYXRvcnMpID4gMCkgew0KICBwcmludChiYW5rcnVwdGN5X21hY3JvX3Zpb2xhdG9ycykNCiAgY2F0KCJcbk5vdGU6IFZpb2xhdGlvbnMgaW4gbWFjcm8gdmFyaWFibGVzIG1heSBpbmRpY2F0ZSBidXNpbmVzcyBjeWNsZSBlZmZlY3RzXG4iKQ0KfSBlbHNlIHsNCiAgY2F0KCJOb25lIGZvdW5kIC0gQWxsIG1hY3JvIHZhcmlhYmxlcyBzYXRpc2Z5IFBIIGFzc3VtcHRpb25cbiIpDQp9DQoNCmlmKGxlbmd0aChiYW5rcnVwdGN5X3BoX3Zpb2xhdG9ycykgPiAwKSB7DQogIGNhdCgiXG5Ub3RhbCB2YXJpYWJsZXMgdmlvbGF0aW5nIFBIIGFzc3VtcHRpb246IiwgbGVuZ3RoKGJhbmtydXB0Y3lfcGhfdmlvbGF0b3JzKSwgIlxuIikNCiAgcHJpbnQoYmFua3J1cHRjeV9waF92aW9sYXRvcnMpDQogIA0KICAjIFRvIHByZXZlbnQgY29tcHV0YXRpb25hbCBpc3N1ZXMsIGxpbWl0IHRvIHRvcCAzIG1vc3Qgc2lnbmlmaWNhbnQgdmlvbGF0b3JzIGlmIHRoZXJlIGFyZSBtYW55DQogIGlmKGxlbmd0aChiYW5rcnVwdGN5X3BoX3Zpb2xhdG9ycykgPiAzKSB7DQogICAgY2F0KCJNYW55IHZhcmlhYmxlcyB2aW9sYXRlIFBIIGFzc3VtcHRpb24uIFVzaW5nIG9ubHkgdGhlIHRvcCAzIG1vc3Qgc2lnbmlmaWNhbnQgdmlvbGF0b3JzLlxuIikNCiAgICAjIEdldCBwLXZhbHVlcyBmcm9tIHRoZSBQSCB0ZXN0DQogICAgdmlvbGF0b3JfcHZhbHMgPC0gcGhfdGVzdF9yZXN1bHRzJGJhbmtydXB0Y3kkc2Nob2VuZmVsZF90ZXN0JHRhYmxlW2JhbmtydXB0Y3lfcGhfdmlvbGF0b3JzLCAicCJdDQogICAgIyBTZWxlY3QgdG9wIDMgd2l0aCBsb3dlc3QgcC12YWx1ZXMNCiAgICBiYW5rcnVwdGN5X3BoX3Zpb2xhdG9ycyA8LSBuYW1lcyhzb3J0KHZpb2xhdG9yX3B2YWxzKVsxOjNdKQ0KICAgIGNhdCgiU2VsZWN0ZWQgdmlvbGF0b3JzOiIsIHBhc3RlKGJhbmtydXB0Y3lfcGhfdmlvbGF0b3JzLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikNCiAgICANCiAgICAjIENoZWNrIGlmIGFueSBtYWNybyB2YXJpYWJsZXMgYXJlIGluIHRoZSB0b3AgMw0KICAgIGhhc19tYWNybyA8LSBhbnkoYmFua3J1cHRjeV9waF92aW9sYXRvcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzKQ0KICAgIGlmKGhhc19tYWNybykgew0KICAgICAgY2F0KCJTZWxlY3Rpb24gaW5jbHVkZXMgbWFjcm8gdmFyaWFibGVzLCB3aGljaCBtYXkgaW5kaWNhdGUgYnVzaW5lc3MgY3ljbGUgZWZmZWN0cy5cbiIpDQogICAgfSBlbHNlIHsNCiAgICAgICMgSWYgbm8gbWFjcm8gdmFyaWFibGVzIGFyZSBpbiB0b3AgMyBidXQgdGhlcmUgYXJlIG1hY3JvIHZpb2xhdG9ycywgYWRkIHRoZSBtb3N0IHNpZ25pZmljYW50IG9uZQ0KICAgICAgaWYobGVuZ3RoKGJhbmtydXB0Y3lfbWFjcm9fdmlvbGF0b3JzKSA+IDApIHsNCiAgICAgICAgbWFjcm9fdmlvbGF0b3JfcHZhbHMgPC0gcGhfdGVzdF9yZXN1bHRzJGJhbmtydXB0Y3kkc2Nob2VuZmVsZF90ZXN0JHRhYmxlW2JhbmtydXB0Y3lfbWFjcm9fdmlvbGF0b3JzLCAicCJdDQogICAgICAgIHRvcF9tYWNyb192aW9sYXRvciA8LSBuYW1lcyhzb3J0KG1hY3JvX3Zpb2xhdG9yX3B2YWxzKVsxXSkNCiAgICAgICAgDQogICAgICAgICMgUmVwbGFjZSB0aGUgbGVhc3Qgc2lnbmlmaWNhbnQgb2YgdGhlIDMgd2l0aCB0aGUgdG9wIG1hY3JvIHZpb2xhdG9yDQogICAgICAgIHdvcnN0X2lkeCA8LSB3aGljaC5tYXgodmlvbGF0b3JfcHZhbHNbYmFua3J1cHRjeV9waF92aW9sYXRvcnNdKQ0KICAgICAgICBiYW5rcnVwdGN5X3BoX3Zpb2xhdG9yc1t3b3JzdF9pZHhdIDwtIHRvcF9tYWNyb192aW9sYXRvcg0KICAgICAgICBjYXQoIkFkZGVkIG1hY3JvIHZhcmlhYmxlIiwgdG9wX21hY3JvX3Zpb2xhdG9yLCAidG8gZW5zdXJlIGJ1c2luZXNzIGN5Y2xlIGVmZmVjdHMgYXJlIGNhcHR1cmVkLlxuIikNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgDQogICMgQ3JlYXRlIGZvcm11bGEgaXRlcmF0aXZlbHkgYnkgYWRkaW5nIG9uZSBpbnRlcmFjdGlvbiBhdCBhIHRpbWUNCiAgYmFua3J1cHRjeV92YXJzIDwtIGJlc3RfbW9kZWxzJGJhbmtydXB0Y3kkdmFyaWFibGVzDQogIA0KICAjIFN0YXJ0IHdpdGggdGhlIGJhc2UgZm9ybXVsYQ0KICBmb3JtdWxhX3N0ciA8LSBwYXN0ZTAoIlN1cnYodHN0YXJ0LCB0c3RvcCwgYmFua3J1cHRjeSkgfiAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoYmFua3J1cHRjeV92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIiArIHN0cmF0YShnc2VjdG9yKSIpDQogIA0KICAjIEFkZCBpbnRlcmFjdGlvbnMgb25lIGJ5IG9uZQ0KICBmb3IodmFyIGluIGJhbmtydXB0Y3lfcGhfdmlvbGF0b3JzKSB7DQogICAgIyBUcnkgYWRkaW5nIHRoaXMgaW50ZXJhY3Rpb24NCiAgICB2YXJfdHlwZSA8LSBpZmVsc2UodmFyICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXMsICJmaW5hbmNpYWwiLCAibWFjcm8iKQ0KICAgIGNhdCgiVGVzdGluZyB0aW1lIGludGVyYWN0aW9uIGZvciIsIHZhciwgIigiLCB2YXJfdHlwZSwgInZhcmlhYmxlICkuLi5cbiIpDQogICAgbmV3X2Zvcm11bGFfc3RyIDwtIHBhc3RlMChmb3JtdWxhX3N0ciwgIiArICIsIHZhciwgIjp0dCIpDQogICAgDQogICAgIyBTZWUgaWYgdGhlIGZvcm11bGEgd29ya3MNCiAgICB0cnlDYXRjaCh7DQogICAgICBuZXdfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKG5ld19mb3JtdWxhX3N0cikNCiAgICAgICMgSWYgaXQgd29ya3MsIHVwZGF0ZSB0aGUgZm9ybXVsYSBzdHJpbmcNCiAgICAgIGZvcm11bGFfc3RyIDwtIG5ld19mb3JtdWxhX3N0cg0KICAgICAgY2F0KCJBZGRlZCB0aW1lIGludGVyYWN0aW9uIGZvciIsIHZhciwgInN1Y2Nlc3NmdWxseS5cbiIpDQogICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgICBjYXQoIkVycm9yIGFkZGluZyB0aW1lIGludGVyYWN0aW9uIGZvciIsIHZhciwgIjoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICBjYXQoIlNraXBwaW5nIHRoaXMgaW50ZXJhY3Rpb24uXG4iKQ0KICAgIH0pDQogIH0NCiAgDQogICMgQ3JlYXRlIHRoZSBmaW5hbCBmb3JtdWxhDQogIGNhdCgiQ3JlYXRpbmcgZmluYWwgZm9ybXVsYSBmb3IgYmFua3J1cHRjeSBtb2RlbC4uLlxuIikNCiAgZmluYWxfYmFua3J1cHRjeV9mb3JtdWxhIDwtIGFzLmZvcm11bGEoZm9ybXVsYV9zdHIpDQogIA0KICBjYXQoIlxuRml0dGluZyBmaW5hbCBiYW5rcnVwdGN5IG1vZGVsIHdpdGggdGltZS1kZXBlbmRlbnQgY29lZmZpY2llbnRzLi4uXG4iKQ0KICBmaW5hbF9iYW5rcnVwdGN5X21vZGVsIDwtIGNveHBoKGZpbmFsX2JhbmtydXB0Y3lfZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICANCiAgY2F0KCJcbj09PSBGSU5BTCBCQU5LUlVQVENZIE1PREVMIFdJVEggVElNRS1ERVBFTkRFTlQgRUZGRUNUUyA9PT1cbiIpDQogIHByaW50KHN1bW1hcnkoZmluYWxfYmFua3J1cHRjeV9tb2RlbCkpDQogIA0KfSBlbHNlIHsNCiAgY2F0KCJOb25lIGZvdW5kIC0gVXNpbmcgb3JpZ2luYWwgbW9kZWxcbiIpDQogIGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwgPC0gYmVzdF9tb2RlbHMkYmFua3J1cHRjeSRtb2RlbA0KICBjYXQoIlxuRmluYWwgYmFua3J1cHRjeSBtb2RlbCByZW1haW5zIHVuY2hhbmdlZCAobm8gUEggdmlvbGF0aW9ucykuXG4iKQ0KfQ0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxMC4yOiBBQ1FVSVNJVElPTiBNT0RFTCAtIEFkZHJlc3MgUEggVmlvbGF0aW9ucw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMC4yOiBBQ1FVSVNJVElPTiBNT0RFTCAtIEFkZHJlc3NpbmcgUEggVmlvbGF0aW9uc1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRXh0cmFjdCB2aW9sYXRvcnMgZnJvbSBwcmV2aW91cyBzdGVwJ3MgcmVzdWx0cywgc2VwYXJhdGVkIGJ5IHZhcmlhYmxlIHR5cGUNCmFjcXVpc2l0aW9uX2Zpbl92aW9sYXRvcnMgPC0gcGhfdGVzdF9yZXN1bHRzJGFjcXVpc2l0aW9uJGZpbl92aW9sYXRvcnMNCmFjcXVpc2l0aW9uX21hY3JvX3Zpb2xhdG9ycyA8LSBwaF90ZXN0X3Jlc3VsdHMkYWNxdWlzaXRpb24kbWFjcm9fdmlvbGF0b3JzDQphY3F1aXNpdGlvbl9waF92aW9sYXRvcnMgPC0gcGhfdGVzdF9yZXN1bHRzJGFjcXVpc2l0aW9uJGFsbF92aW9sYXRvcnMNCg0KY2F0KCJGaW5hbmNpYWwgdmFyaWFibGVzIHZpb2xhdGluZyBQSCBhc3N1bXB0aW9uIGluIGFjcXVpc2l0aW9uIG1vZGVsOlxuIikNCmlmKGxlbmd0aChhY3F1aXNpdGlvbl9maW5fdmlvbGF0b3JzKSA+IDApIHsNCiAgcHJpbnQoYWNxdWlzaXRpb25fZmluX3Zpb2xhdG9ycykNCn0gZWxzZSB7DQogIGNhdCgiTm9uZSBmb3VuZCAtIEFsbCBmaW5hbmNpYWwgdmFyaWFibGVzIHNhdGlzZnkgUEggYXNzdW1wdGlvblxuIikNCn0NCg0KY2F0KCJcbk1hY3JvZWNvbm9taWMgdmFyaWFibGVzIHZpb2xhdGluZyBQSCBhc3N1bXB0aW9uIGluIGFjcXVpc2l0aW9uIG1vZGVsOlxuIikNCmlmKGxlbmd0aChhY3F1aXNpdGlvbl9tYWNyb192aW9sYXRvcnMpID4gMCkgew0KICBwcmludChhY3F1aXNpdGlvbl9tYWNyb192aW9sYXRvcnMpDQogIGNhdCgiXG5Ob3RlOiBWaW9sYXRpb25zIGluIG1hY3JvIHZhcmlhYmxlcyBtYXkgaW5kaWNhdGUgYnVzaW5lc3MgY3ljbGUgZWZmZWN0c1xuIikNCn0gZWxzZSB7DQogIGNhdCgiTm9uZSBmb3VuZCAtIEFsbCBtYWNybyB2YXJpYWJsZXMgc2F0aXNmeSBQSCBhc3N1bXB0aW9uXG4iKQ0KfQ0KDQppZihsZW5ndGgoYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzKSA+IDApIHsNCiAgY2F0KCJcblRvdGFsIHZhcmlhYmxlcyB2aW9sYXRpbmcgUEggYXNzdW1wdGlvbjoiLCBsZW5ndGgoYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzKSwgIlxuIikNCiAgcHJpbnQoYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzKQ0KICANCiAgIyBUbyBwcmV2ZW50IGNvbXB1dGF0aW9uYWwgaXNzdWVzLCBsaW1pdCB0byB0b3AgMyBtb3N0IHNpZ25pZmljYW50IHZpb2xhdG9ycyBpZiB0aGVyZSBhcmUgbWFueQ0KICBpZihsZW5ndGgoYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzKSA+IDMpIHsNCiAgICBjYXQoIk1hbnkgdmFyaWFibGVzIHZpb2xhdGUgUEggYXNzdW1wdGlvbi4gVXNpbmcgb25seSB0aGUgdG9wIDMgbW9zdCBzaWduaWZpY2FudCB2aW9sYXRvcnMuXG4iKQ0KICAgICMgR2V0IHAtdmFsdWVzIGZyb20gdGhlIFBIIHRlc3QNCiAgICB2aW9sYXRvcl9wdmFscyA8LSBwaF90ZXN0X3Jlc3VsdHMkYWNxdWlzaXRpb24kc2Nob2VuZmVsZF90ZXN0JHRhYmxlW2FjcXVpc2l0aW9uX3BoX3Zpb2xhdG9ycywgInAiXQ0KICAgICMgU2VsZWN0IHRvcCAzIHdpdGggbG93ZXN0IHAtdmFsdWVzDQogICAgYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzIDwtIG5hbWVzKHNvcnQodmlvbGF0b3JfcHZhbHMpWzE6M10pDQogICAgY2F0KCJTZWxlY3RlZCB2aW9sYXRvcnM6IiwgcGFzdGUoYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikNCiAgICANCiAgICAjIENoZWNrIGlmIGFueSBtYWNybyB2YXJpYWJsZXMgYXJlIGluIHRoZSB0b3AgMw0KICAgIGhhc19tYWNybyA8LSBhbnkoYWNxdWlzaXRpb25fcGhfdmlvbGF0b3JzICVpbiUgbWFjcm9fY292YXJpYXRlcykNCiAgICBpZihoYXNfbWFjcm8pIHsNCiAgICAgIGNhdCgiU2VsZWN0aW9uIGluY2x1ZGVzIG1hY3JvIHZhcmlhYmxlcywgd2hpY2ggbWF5IGluZGljYXRlIGJ1c2luZXNzIGN5Y2xlIGVmZmVjdHMuXG4iKQ0KICAgIH0gZWxzZSB7DQogICAgICAjIElmIG5vIG1hY3JvIHZhcmlhYmxlcyBhcmUgaW4gdG9wIDMgYnV0IHRoZXJlIGFyZSBtYWNybyB2aW9sYXRvcnMsIGFkZCB0aGUgbW9zdCBzaWduaWZpY2FudCBvbmUNCiAgICAgIGlmKGxlbmd0aChhY3F1aXNpdGlvbl9tYWNyb192aW9sYXRvcnMpID4gMCkgew0KICAgICAgICBtYWNyb192aW9sYXRvcl9wdmFscyA8LSBwaF90ZXN0X3Jlc3VsdHMkYWNxdWlzaXRpb24kc2Nob2VuZmVsZF90ZXN0JHRhYmxlW2FjcXVpc2l0aW9uX21hY3JvX3Zpb2xhdG9ycywgInAiXQ0KICAgICAgICB0b3BfbWFjcm9fdmlvbGF0b3IgPC0gbmFtZXMoc29ydChtYWNyb192aW9sYXRvcl9wdmFscylbMV0pDQogICAgICAgIA0KICAgICAgICAjIFJlcGxhY2UgdGhlIGxlYXN0IHNpZ25pZmljYW50IG9mIHRoZSAzIHdpdGggdGhlIHRvcCBtYWNybyB2aW9sYXRvcg0KICAgICAgICB3b3JzdF9pZHggPC0gd2hpY2gubWF4KHZpb2xhdG9yX3B2YWxzW2FjcXVpc2l0aW9uX3BoX3Zpb2xhdG9yc10pDQogICAgICAgIGFjcXVpc2l0aW9uX3BoX3Zpb2xhdG9yc1t3b3JzdF9pZHhdIDwtIHRvcF9tYWNyb192aW9sYXRvcg0KICAgICAgICBjYXQoIkFkZGVkIG1hY3JvIHZhcmlhYmxlIiwgdG9wX21hY3JvX3Zpb2xhdG9yLCAidG8gZW5zdXJlIGJ1c2luZXNzIGN5Y2xlIGVmZmVjdHMgYXJlIGNhcHR1cmVkLlxuIikNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgDQogICMgQ3JlYXRlIGZvcm11bGEgaXRlcmF0aXZlbHkgYnkgYWRkaW5nIG9uZSBpbnRlcmFjdGlvbiBhdCBhIHRpbWUNCiAgYWNxdWlzaXRpb25fdmFycyA8LSBiZXN0X21vZGVscyRhY3F1aXNpdGlvbiR2YXJpYWJsZXMNCiAgDQogICMgU3RhcnQgd2l0aCB0aGUgYmFzZSBmb3JtdWxhDQogIGZvcm11bGFfc3RyIDwtIHBhc3RlMCgiU3Vydih0c3RhcnQsIHRzdG9wLCBhY3F1aXNpdGlvbikgfiAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoYWNxdWlzaXRpb25fdmFycywgY29sbGFwc2UgPSAiICsgIiksDQogICAgICAgICAgICAgICAgICAgICAgICIgKyBzdHJhdGEoZ3NlY3RvcikiKQ0KICANCiAgIyBBZGQgaW50ZXJhY3Rpb25zIG9uZSBieSBvbmUNCiAgZm9yKHZhciBpbiBhY3F1aXNpdGlvbl9waF92aW9sYXRvcnMpIHsNCiAgICAjIFRyeSBhZGRpbmcgdGhpcyBpbnRlcmFjdGlvbg0KICAgIHZhcl90eXBlIDwtIGlmZWxzZSh2YXIgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcywgImZpbmFuY2lhbCIsICJtYWNybyIpDQogICAgY2F0KCJUZXN0aW5nIHRpbWUgaW50ZXJhY3Rpb24gZm9yIiwgdmFyLCAiKCIsIHZhcl90eXBlLCAidmFyaWFibGUgKS4uLlxuIikNCiAgICBuZXdfZm9ybXVsYV9zdHIgPC0gcGFzdGUwKGZvcm11bGFfc3RyLCAiICsgIiwgdmFyLCAiOnR0IikNCiAgICANCiAgICAjIFNlZSBpZiB0aGUgZm9ybXVsYSB3b3Jrcw0KICAgIHRyeUNhdGNoKHsNCiAgICAgIG5ld19mb3JtdWxhIDwtIGFzLmZvcm11bGEobmV3X2Zvcm11bGFfc3RyKQ0KICAgICAgIyBJZiBpdCB3b3JrcywgdXBkYXRlIHRoZSBmb3JtdWxhIHN0cmluZw0KICAgICAgZm9ybXVsYV9zdHIgPC0gbmV3X2Zvcm11bGFfc3RyDQogICAgICBjYXQoIkFkZGVkIHRpbWUgaW50ZXJhY3Rpb24gZm9yIiwgdmFyLCAic3VjY2Vzc2Z1bGx5LlxuIikNCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgIGNhdCgiRXJyb3IgYWRkaW5nIHRpbWUgaW50ZXJhY3Rpb24gZm9yIiwgdmFyLCAiOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICAgIGNhdCgiU2tpcHBpbmcgdGhpcyBpbnRlcmFjdGlvbi5cbiIpDQogICAgfSkNCiAgfQ0KICANCiAgIyBDcmVhdGUgdGhlIGZpbmFsIGZvcm11bGENCiAgY2F0KCJDcmVhdGluZyBmaW5hbCBmb3JtdWxhIGZvciBhY3F1aXNpdGlvbiBtb2RlbC4uLlxuIikNCiAgZmluYWxfYWNxdWlzaXRpb25fZm9ybXVsYSA8LSBhcy5mb3JtdWxhKGZvcm11bGFfc3RyKQ0KICANCiAgY2F0KCJcbkZpdHRpbmcgZmluYWwgYWNxdWlzaXRpb24gbW9kZWwgd2l0aCB0aW1lLWRlcGVuZGVudCBjb2VmZmljaWVudHMuLi5cbiIpDQogIGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsIDwtIGNveHBoKGZpbmFsX2FjcXVpc2l0aW9uX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCiAgDQogIGNhdCgiXG49PT0gRklOQUwgQUNRVUlTSVRJT04gTU9ERUwgV0lUSCBUSU1FLURFUEVOREVOVCBFRkZFQ1RTID09PVxuIikNCiAgcHJpbnQoc3VtbWFyeShmaW5hbF9hY3F1aXNpdGlvbl9tb2RlbCkpDQogIA0KfSBlbHNlIHsNCiAgY2F0KCJOb25lIGZvdW5kIC0gVXNpbmcgb3JpZ2luYWwgbW9kZWxcbiIpDQogIGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsIDwtIGJlc3RfbW9kZWxzJGFjcXVpc2l0aW9uJG1vZGVsDQogIGNhdCgiXG5GaW5hbCBhY3F1aXNpdGlvbiBtb2RlbCByZW1haW5zIHVuY2hhbmdlZCAobm8gUEggdmlvbGF0aW9ucykuXG4iKQ0KfQ0KYGBgDQoNCk5leHQsIHdlIHdpbGwgaW50ZXJwcmV0IHRoZSB0aW1lLWRlcGVuZGVudCBlZmZlY3RzIG9mIHRoZSBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpbiBib3RoIG1vZGVscy4gV2Ugd2lsbCBmb2N1cyBvbiBob3cgdGhlc2UgZWZmZWN0cyBjaGFuZ2Ugb3ZlciB0aW1lIGFuZCB0aGVpciBpbXBsaWNhdGlvbnMgZm9yIGJ1c2luZXNzIGN5Y2xlcy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTAuMzogSW50ZXJwcmV0IFRpbWUtRGVwZW5kZW50IEVmZmVjdHMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTAuMzogSW50ZXJwcmV0aW5nIFRpbWUtRGVwZW5kZW50IEVmZmVjdHMgd2l0aCBGb2N1cyBvbiBNYWNybyBWYXJpYWJsZXNcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQojIEdldCBhbGwgdGltZSBpbnRlcmFjdGlvbiB0ZXJtcyBmcm9tIGZpbmFsIG1vZGVscw0KYmFua3J1cHRjeV90aW1lX3Rlcm1zIDwtIGdyZXAoIjp0dCIsIG5hbWVzKGNvZWYoZmluYWxfYmFua3J1cHRjeV9tb2RlbCkpLCB2YWx1ZSA9IFRSVUUpDQphY3F1aXNpdGlvbl90aW1lX3Rlcm1zIDwtIGdyZXAoIjp0dCIsIG5hbWVzKGNvZWYoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpKSwgdmFsdWUgPSBUUlVFKQ0KDQojIFNwbGl0IHRlcm1zIGJ5IHZhcmlhYmxlIHR5cGUNCmJhbmtydXB0Y3lfZmluX3RpbWVfdGVybXMgPC0gYmFua3J1cHRjeV90aW1lX3Rlcm1zW3NhcHBseShiYW5rcnVwdGN5X3RpbWVfdGVybXMsIGZ1bmN0aW9uKHRlcm0pIHsNCiAgdmFyX25hbWUgPC0gc3Ryc3BsaXQodGVybSwgIjoiKVtbMV1dWzFdDQogIHJldHVybih2YXJfbmFtZSAlaW4lIGZpbmFuY2lhbF9jb3ZhcmlhdGVzKQ0KfSldDQoNCmJhbmtydXB0Y3lfbWFjcm9fdGltZV90ZXJtcyA8LSBiYW5rcnVwdGN5X3RpbWVfdGVybXNbc2FwcGx5KGJhbmtydXB0Y3lfdGltZV90ZXJtcywgZnVuY3Rpb24odGVybSkgew0KICB2YXJfbmFtZSA8LSBzdHJzcGxpdCh0ZXJtLCAiOiIpW1sxXV1bMV0NCiAgcmV0dXJuKHZhcl9uYW1lICVpbiUgbWFjcm9fY292YXJpYXRlcykNCn0pXQ0KDQphY3F1aXNpdGlvbl9maW5fdGltZV90ZXJtcyA8LSBhY3F1aXNpdGlvbl90aW1lX3Rlcm1zW3NhcHBseShhY3F1aXNpdGlvbl90aW1lX3Rlcm1zLCBmdW5jdGlvbih0ZXJtKSB7DQogIHZhcl9uYW1lIDwtIHN0cnNwbGl0KHRlcm0sICI6IilbWzFdXVsxXQ0KICByZXR1cm4odmFyX25hbWUgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlcykNCn0pXQ0KDQphY3F1aXNpdGlvbl9tYWNyb190aW1lX3Rlcm1zIDwtIGFjcXVpc2l0aW9uX3RpbWVfdGVybXNbc2FwcGx5KGFjcXVpc2l0aW9uX3RpbWVfdGVybXMsIGZ1bmN0aW9uKHRlcm0pIHsNCiAgdmFyX25hbWUgPC0gc3Ryc3BsaXQodGVybSwgIjoiKVtbMV1dWzFdDQogIHJldHVybih2YXJfbmFtZSAlaW4lIG1hY3JvX2NvdmFyaWF0ZXMpDQp9KV0NCg0KIyBSZXBvcnQgdGltZS1kZXBlbmRlbnQgZWZmZWN0cyBieSB2YXJpYWJsZSB0eXBlDQpjYXQoIlxuQmFua3J1cHRjeSBtb2RlbCAtIGZpbmFuY2lhbCB0aW1lLWRlcGVuZGVudCB0ZXJtczoiLCANCiAgICBpZmVsc2UobGVuZ3RoKGJhbmtydXB0Y3lfZmluX3RpbWVfdGVybXMpID4gMCwgDQogICAgICAgICAgIHBhc3RlKGJhbmtydXB0Y3lfZmluX3RpbWVfdGVybXMsIGNvbGxhcHNlPSIsICIpLCANCiAgICAgICAgICAgIk5vbmUiKSwgIlxuIikNCg0KY2F0KCJcbkJhbmtydXB0Y3kgbW9kZWwgLSBtYWNybyB0aW1lLWRlcGVuZGVudCB0ZXJtczoiLCANCiAgICBpZmVsc2UobGVuZ3RoKGJhbmtydXB0Y3lfbWFjcm9fdGltZV90ZXJtcykgPiAwLCANCiAgICAgICAgICAgcGFzdGUoYmFua3J1cHRjeV9tYWNyb190aW1lX3Rlcm1zLCBjb2xsYXBzZT0iLCAiKSwgDQogICAgICAgICAgICJOb25lIiksICJcbiIpDQoNCmNhdCgiXG5BY3F1aXNpdGlvbiBtb2RlbCAtIGZpbmFuY2lhbCB0aW1lLWRlcGVuZGVudCB0ZXJtczoiLCANCiAgICBpZmVsc2UobGVuZ3RoKGFjcXVpc2l0aW9uX2Zpbl90aW1lX3Rlcm1zKSA+IDAsIA0KICAgICAgICAgICBwYXN0ZShhY3F1aXNpdGlvbl9maW5fdGltZV90ZXJtcywgY29sbGFwc2U9IiwgIiksIA0KICAgICAgICAgICAiTm9uZSIpLCAiXG4iKQ0KDQpjYXQoIlxuQWNxdWlzaXRpb24gbW9kZWwgLSBtYWNybyB0aW1lLWRlcGVuZGVudCB0ZXJtczoiLCANCiAgICBpZmVsc2UobGVuZ3RoKGFjcXVpc2l0aW9uX21hY3JvX3RpbWVfdGVybXMpID4gMCwgDQogICAgICAgICAgIHBhc3RlKGFjcXVpc2l0aW9uX21hY3JvX3RpbWVfdGVybXMsIGNvbGxhcHNlPSIsICIpLCANCiAgICAgICAgICAgIk5vbmUiKSwgIlxuIikNCg0KIyBBbmFseXplIGFuZCBpbnRlcnByZXQgdGltZS1kZXBlbmRlbnQgZWZmZWN0cyBvZiBtYWNybyB2YXJpYWJsZXMNCmlmKGxlbmd0aChiYW5rcnVwdGN5X21hY3JvX3RpbWVfdGVybXMpID4gMCB8fCBsZW5ndGgoYWNxdWlzaXRpb25fbWFjcm9fdGltZV90ZXJtcykgPiAwKSB7DQogIGNhdCgiXG49PT0gQU5BTFlTSVMgT0YgVElNRS1ERVBFTkRFTlQgTUFDUk9FQ09OT01JQyBFRkZFQ1RTID09PVxuIikNCiAgDQogICMgRm9yIGJhbmtydXB0Y3kgbW9kZWwNCiAgaWYobGVuZ3RoKGJhbmtydXB0Y3lfbWFjcm9fdGltZV90ZXJtcykgPiAwKSB7DQogICAgY2F0KCJcbkJhbmtydXB0Y3kgcmlzayAtIE1hY3JvZWNvbm9taWMgdGltZS1kZXBlbmRlbnQgZWZmZWN0czpcbiIpDQogICAgZm9yKHRlcm0gaW4gYmFua3J1cHRjeV9tYWNyb190aW1lX3Rlcm1zKSB7DQogICAgICB2YXJfbmFtZSA8LSBzdHJzcGxpdCh0ZXJtLCAiOiIpW1sxXV1bMV0NCiAgICAgIGNvZWZfbWFpbiA8LSBjb2VmKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpW3Zhcl9uYW1lXQ0KICAgICAgY29lZl90aW1lIDwtIGNvZWYoZmluYWxfYmFua3J1cHRjeV9tb2RlbClbdGVybV0NCiAgICAgIA0KICAgICAgY2F0KCJcbiIsIHZhcl9uYW1lLCAiOlxuIiwgc2VwPSIiKQ0KICAgICAgY2F0KCItIEJhc2UgZWZmZWN0IChjb2VmZmljaWVudCk6Iiwgcm91bmQoY29lZl9tYWluLCA0KSwgIlxuIikNCiAgICAgIGNhdCgiLSBUaW1lIGludGVyYWN0aW9uIGVmZmVjdDoiLCByb3VuZChjb2VmX3RpbWUsIDQpLCAiXG4iKQ0KICAgICAgDQogICAgICBpZihjb2VmX3RpbWUgPiAwKSB7DQogICAgICAgIGNhdCgiLSBFZmZlY3QgaW50ZXJwcmV0YXRpb246IFRoZSBpbXBhY3Qgb2YiLCB2YXJfbmFtZSwgIm9uIGJhbmtydXB0Y3kgcmlzayBJTkNSRUFTRVMgb3ZlciB0aW1lXG4iKQ0KICAgICAgICBpZihjb2VmX21haW4gPCAwKSB7DQogICAgICAgICAgY2F0KCIgIChzdGFydHMgYXMgcHJvdGVjdGl2ZSBidXQgYmVjb21lcyBsZXNzIHByb3RlY3RpdmUpXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiICAoYmVjb21lcyBtb3JlIHJpc2t5IG92ZXIgdGltZSlcbiIpDQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGNhdCgiLSBFZmZlY3QgaW50ZXJwcmV0YXRpb246IFRoZSBpbXBhY3Qgb2YiLCB2YXJfbmFtZSwgIm9uIGJhbmtydXB0Y3kgcmlzayBERUNSRUFTRVMgb3ZlciB0aW1lXG4iKQ0KICAgICAgICBpZihjb2VmX21haW4gPiAwKSB7DQogICAgICAgICAgY2F0KCIgIChzdGFydHMgYXMgcmlza3kgYnV0IGJlY29tZXMgbGVzcyByaXNreSlcbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCIgIChiZWNvbWVzIG1vcmUgcHJvdGVjdGl2ZSBvdmVyIHRpbWUpXG4iKQ0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgICMgQnVzaW5lc3MgY3ljbGUgaW50ZXJwcmV0YXRpb24NCiAgICAgIGNhdCgiLSBCdXNpbmVzcyBjeWNsZSBpbXBsaWNhdGlvbjogIikNCiAgICAgIGlmKHZhcl9uYW1lID09ICJnZHBfZ3Jvd3RoIikgew0KICAgICAgICBpZihjb2VmX3RpbWUgPiAwKSB7DQogICAgICAgICAgY2F0KCJHRFAgZ3Jvd3RoIGJlY29tZXMgYSBzdHJvbmdlciBwcmVkaWN0b3Igb2YgYmFua3J1cHRjeSBhdm9pZGFuY2UgbGF0ZXIgaW4gYSBjb21wYW55J3MgbGlmZWN5Y2xlXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiR0RQIGdyb3d0aCBpcyBtb3N0IHByb3RlY3RpdmUgYWdhaW5zdCBiYW5rcnVwdGN5IGVhcmx5IGluIGEgY29tcGFueSdzIGxpZmVjeWNsZVxuIikNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIGlmKHZhcl9uYW1lID09ICJ1bmVtcGxveWVtZW50Iikgew0KICAgICAgICBpZihjb2VmX3RpbWUgPiAwKSB7DQogICAgICAgICAgY2F0KCJVbmVtcGxveW1lbnQgaGFzIGFuIGluY3JlYXNpbmdseSBkZXRyaW1lbnRhbCBlZmZlY3Qgb24gY29tcGFueSBzdXJ2aXZhbCBvdmVyIHRpbWVcbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCJVbmVtcGxveW1lbnQgaXMgbW9zdCBoYXJtZnVsIHRvIGNvbXBhbnkgc3Vydml2YWwgZWFybHkgaW4gYSBjb21wYW55J3MgbGlmZWN5Y2xlXG4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2UgaWYodmFyX25hbWUgPT0gImdkcF9kZWZsYXRvciIpIHsNCiAgICAgICAgaWYoY29lZl90aW1lID4gMCkgew0KICAgICAgICAgIGNhdCgiSW5mbGF0aW9uIGJlY29tZXMgbW9yZSBpbmZsdWVudGlhbCBvbiBiYW5rcnVwdGN5IHJpc2sgYXMgY29tcGFuaWVzIG1hdHVyZVxuIikNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBjYXQoIkluZmxhdGlvbiBoYXMgaXRzIHN0cm9uZ2VzdCBpbXBhY3Qgb24gYmFua3J1cHRjeSByaXNrIGVhcmx5IGluIGEgY29tcGFueSdzIGxpZmVjeWNsZVxuIikNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICANCiAgIyBGb3IgYWNxdWlzaXRpb24gbW9kZWwNCiAgaWYobGVuZ3RoKGFjcXVpc2l0aW9uX21hY3JvX3RpbWVfdGVybXMpID4gMCkgew0KICAgIGNhdCgiXG5BY3F1aXNpdGlvbiBsaWtlbGlob29kIC0gTWFjcm9lY29ub21pYyB0aW1lLWRlcGVuZGVudCBlZmZlY3RzOlxuIikNCiAgICBmb3IodGVybSBpbiBhY3F1aXNpdGlvbl9tYWNyb190aW1lX3Rlcm1zKSB7DQogICAgICB2YXJfbmFtZSA8LSBzdHJzcGxpdCh0ZXJtLCAiOiIpW1sxXV1bMV0NCiAgICAgIGNvZWZfbWFpbiA8LSBjb2VmKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsKVt2YXJfbmFtZV0NCiAgICAgIGNvZWZfdGltZSA8LSBjb2VmKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsKVt0ZXJtXQ0KICAgICAgDQogICAgICBjYXQoIlxuIiwgdmFyX25hbWUsICI6XG4iLCBzZXA9IiIpDQogICAgICBjYXQoIi0gQmFzZSBlZmZlY3QgKGNvZWZmaWNpZW50KToiLCByb3VuZChjb2VmX21haW4sIDQpLCAiXG4iKQ0KICAgICAgY2F0KCItIFRpbWUgaW50ZXJhY3Rpb24gZWZmZWN0OiIsIHJvdW5kKGNvZWZfdGltZSwgNCksICJcbiIpDQogICAgICANCiAgICAgIGlmKGNvZWZfdGltZSA+IDApIHsNCiAgICAgICAgY2F0KCItIEVmZmVjdCBpbnRlcnByZXRhdGlvbjogVGhlIGltcGFjdCBvZiIsIHZhcl9uYW1lLCAib24gYWNxdWlzaXRpb24gbGlrZWxpaG9vZCBJTkNSRUFTRVMgb3ZlciB0aW1lXG4iKQ0KICAgICAgICBpZihjb2VmX21haW4gPCAwKSB7DQogICAgICAgICAgY2F0KCIgIChzdGFydHMgcmVkdWNpbmcgYWNxdWlzaXRpb24gY2hhbmNlcyBidXQgdGhpcyBlZmZlY3Qgd2Vha2VucylcbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCIgIChiZWNvbWVzIG1vcmUgY29uZHVjaXZlIHRvIGFjcXVpc2l0aW9ucyBvdmVyIHRpbWUpXG4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIi0gRWZmZWN0IGludGVycHJldGF0aW9uOiBUaGUgaW1wYWN0IG9mIiwgdmFyX25hbWUsICJvbiBhY3F1aXNpdGlvbiBsaWtlbGlob29kIERFQ1JFQVNFUyBvdmVyIHRpbWVcbiIpDQogICAgICAgIGlmKGNvZWZfbWFpbiA+IDApIHsNCiAgICAgICAgICBjYXQoIiAgKHN0YXJ0cyBpbmNyZWFzaW5nIGFjcXVpc2l0aW9uIGNoYW5jZXMgYnV0IHRoaXMgZWZmZWN0IHdlYWtlbnMpXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiICAoYmVjb21lcyBsZXNzIGNvbmR1Y2l2ZSB0byBhY3F1aXNpdGlvbnMgb3ZlciB0aW1lKVxuIikNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIEJ1c2luZXNzIGN5Y2xlIGludGVycHJldGF0aW9uDQogICAgICBjYXQoIi0gQnVzaW5lc3MgY3ljbGUgaW1wbGljYXRpb246ICIpDQogICAgICBpZih2YXJfbmFtZSA9PSAiZ2RwX2dyb3d0aCIpIHsNCiAgICAgICAgaWYoY29lZl90aW1lID4gMCkgew0KICAgICAgICAgIGNhdCgiU3Ryb25nIGVjb25vbWljIGdyb3d0aCBiZWNvbWVzIG1vcmUgaW5mbHVlbnRpYWwgZm9yIGFjcXVpc2l0aW9ucyBhcyBjb21wYW5pZXMgbWF0dXJlXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiRWNvbm9taWMgZ3Jvd3RoIGlzIG1vc3QgaW5mbHVlbnRpYWwgZm9yIGFjcXVpc2l0aW9ucyBlYXJseSBpbiBhIGNvbXBhbnkncyBsaWZlY3ljbGVcbiIpDQogICAgICAgIH0NCiAgICAgIH0gZWxzZSBpZih2YXJfbmFtZSA9PSAidW5lbXBsb3llbWVudCIpIHsNCiAgICAgICAgaWYoY29lZl90aW1lID4gMCkgew0KICAgICAgICAgIGNhdCgiTGFib3IgbWFya2V0IGNvbmRpdGlvbnMgaGF2ZSBpbmNyZWFzaW5nIGluZmx1ZW5jZSBvbiBhY3F1aXNpdGlvbiBsaWtlbGlob29kIG92ZXIgdGltZVxuIikNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBjYXQoIkxhYm9yIG1hcmtldCBjb25kaXRpb25zIGFyZSBtb3N0IGluZmx1ZW50aWFsIGZvciBhY3F1aXNpdGlvbnMgZWFybHkgaW4gYSBjb21wYW55J3MgbGlmZWN5Y2xlXG4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2UgaWYodmFyX25hbWUgPT0gImdkcF9kZWZsYXRvciIpIHsNCiAgICAgICAgaWYoY29lZl90aW1lID4gMCkgew0KICAgICAgICAgIGNhdCgiSW5mbGF0aW9uIGJlY29tZXMgbW9yZSBpbmZsdWVudGlhbCBvbiBhY3F1aXNpdGlvbiBhY3Rpdml0eSBhcyBjb21wYW5pZXMgbWF0dXJlXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiSW5mbGF0aW9uIGhhcyBpdHMgc3Ryb25nZXN0IGltcGFjdCBvbiBhY3F1aXNpdGlvbiBhY3Rpdml0eSBlYXJseSBpbiBhIGNvbXBhbnkncyBsaWZlY3ljbGVcbiIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9DQogIH0NCn0NCmBgYA0KUHJvYmFibHkgbm90IHJlbGV2YW50IHRvIHRoZSBhbmFseXNpcywgYnV0IGluY2x1ZGVkIGZvciBjb21wbGV0ZW5lc3MhDQoNCldlIGRvdWJsZSBjaGVjayB0aGUgcHJvcG9ydGlvbmFsIGhhemFyZHMgYXNzdW1wdGlvbiBmb3IgdGhlIGZpbmFsIG1vZGVscyB3aXRoIHRpbWUtZGVwZW5kZW50IGNvZWZmaWNpZW50cy4gV2Ugd2lsbCBhbHNvIGNoZWNrIGlmIGFueSByZW1haW5pbmcgdmlvbGF0aW9ucyBleGlzdCBhbmQgaWYgdGhlIGdsb2JhbCB0ZXN0IHN1cHBvcnRzIHRoZSBQSCBhc3N1bXB0aW9uLiBXZSBzZWUgdGhhdCBmb3IgdGhlIGJhbmtydXB0Y3kgbW9kZWwsIHRoZSBnbG9iYWwgdGVzdCBpbmRpY2F0ZXMgdGhhdCB0aGUgUEggYXNzdW1wdGlvbiBpcyBzYXRpc2ZpZWQuIEhvd2V2ZXIsIGZvciB0aGUgYWNxdWlzaXRpb24gbW9kZWwsIHRoZSBnbG9iYWwgdGVzdCBpbmRpY2F0ZXMgdGhhdCB0aGUgUEggYXNzdW1wdGlvbiBpcyB2aW9sYXRlZC4gV2Ugd2lsbCBuZWVkIHRvIGludmVzdGlnYXRlIGZ1cnRoZXIuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDEwLjU6IFZlcmlmeSBSZXNvbHV0aW9uIG9mIFBIIFZpb2xhdGlvbnMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTAuNTogVmVyaWZ5IFJlc29sdXRpb24gb2YgUEggVmlvbGF0aW9uc1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCmNhdCgiVGVzdGluZyBpZiB0aW1lLWRlcGVuZGVudCBjb2VmZmljaWVudHMgcmVzb2x2ZWQgUEggdmlvbGF0aW9ucy4uLlxuIikNCg0KIyBSZS1jaGVjayBQSCBhc3N1bXB0aW9uIGZvciBiYW5rcnVwdGN5IG1vZGVsIHdpdGggdGltZSBpbnRlcmFjdGlvbnMNCmlmKGxlbmd0aChncmVwKCI6dHQiLCBuYW1lcyhjb2VmKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpKSkpID4gMCkgew0KICBjYXQoIlxuUmUtdGVzdGluZyBQSCBhc3N1bXB0aW9uIGZvciBiYW5rcnVwdGN5IG1vZGVsIHdpdGggdGltZSBpbnRlcmFjdGlvbnM6XG4iKQ0KICBwaF90ZXN0X2ZpbmFsX2JhbmtydXB0Y3kgPC0gY294LnpwaChmaW5hbF9iYW5rcnVwdGN5X21vZGVsKQ0KICBwcmludChwaF90ZXN0X2ZpbmFsX2JhbmtydXB0Y3kpDQogIA0KICAjIENoZWNrIGlmIGFueSByZW1haW5pbmcgdmlvbGF0aW9ucywgc2VwYXJhdGVkIGJ5IHZhcmlhYmxlIHR5cGUNCiAgcmVtYWluaW5nX3Zpb2xhdG9ycyA8LSByb3duYW1lcyhwaF90ZXN0X2ZpbmFsX2JhbmtydXB0Y3kkdGFibGUpWw0KICAgIHBoX3Rlc3RfZmluYWxfYmFua3J1cHRjeSR0YWJsZVssICJwIl0gPCAwLjA1ICYgDQogICAgcm93bmFtZXMocGhfdGVzdF9maW5hbF9iYW5rcnVwdGN5JHRhYmxlKSAhPSAiR0xPQkFMIg0KICBdDQogIA0KICByZW1haW5pbmdfZmluX3Zpb2xhdG9ycyA8LSByZW1haW5pbmdfdmlvbGF0b3JzW3JlbWFpbmluZ192aW9sYXRvcnMgJWluJSBmaW5hbmNpYWxfY292YXJpYXRlc10NCiAgcmVtYWluaW5nX21hY3JvX3Zpb2xhdG9ycyA8LSByZW1haW5pbmdfdmlvbGF0b3JzW3JlbWFpbmluZ192aW9sYXRvcnMgJWluJSBtYWNyb19jb3ZhcmlhdGVzXQ0KICANCiAgY2F0KCJcblJlbWFpbmluZyBmaW5hbmNpYWwgdmFyaWFibGVzIHZpb2xhdGluZyBQSCBhc3N1bXB0aW9uOlxuIikNCiAgaWYobGVuZ3RoKHJlbWFpbmluZ19maW5fdmlvbGF0b3JzKSA+IDApIHsNCiAgICBwcmludChyZW1haW5pbmdfZmluX3Zpb2xhdG9ycykNCiAgfSBlbHNlIHsNCiAgICBjYXQoIk5vbmUgLSBBbGwgZmluYW5jaWFsIHZhcmlhYmxlcyBub3cgc2F0aXNmeSBQSCBhc3N1bXB0aW9uXG4iKQ0KICB9DQogIA0KICBjYXQoIlxuUmVtYWluaW5nIG1hY3JvIHZhcmlhYmxlcyB2aW9sYXRpbmcgUEggYXNzdW1wdGlvbjpcbiIpDQogIGlmKGxlbmd0aChyZW1haW5pbmdfbWFjcm9fdmlvbGF0b3JzKSA+IDApIHsNCiAgICBwcmludChyZW1haW5pbmdfbWFjcm9fdmlvbGF0b3JzKQ0KICAgIGNhdCgiXG5Ob3RlOiBQZXJzaXN0ZW50IHZpb2xhdGlvbnMgaW4gbWFjcm8gdmFyaWFibGVzIG1heSBpbmRpY2F0ZSBjb21wbGV4IGJ1c2luZXNzIGN5Y2xlIGVmZmVjdHNcbiIpDQogICAgY2F0KCJ0aGF0IG1pZ2h0IHJlcXVpcmUgbW9yZSBzb3BoaXN0aWNhdGVkIG1vZGVsaW5nIGFwcHJvYWNoZXMgKGUuZy4sIHN0cmF0aWZpY2F0aW9uIG9yIGZyYWlsdHkgbW9kZWxzKS5cbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJOb25lIC0gQWxsIG1hY3JvIHZhcmlhYmxlcyBub3cgc2F0aXNmeSBQSCBhc3N1bXB0aW9uXG4iKQ0KICB9DQogIA0KICAjIENoZWNrIGdsb2JhbCB0ZXN0DQogIGlmKHBoX3Rlc3RfZmluYWxfYmFua3J1cHRjeSR0YWJsZVsiR0xPQkFMIiwgInAiXSA8IDAuMDUpIHsNCiAgICBjYXQoIlxuV0FSTklORzogR2xvYmFsIHRlc3Qgc3RpbGwgaW5kaWNhdGVzIFBIIHZpb2xhdGlvbnMgKHAgPCAwLjA1KS5cbiIpDQogICAgY2F0KCJGdXJ0aGVyIHJlZmluZW1lbnRzIG1pZ2h0IGJlIG5lZWRlZCwgZXNwZWNpYWxseSBmb3IgZWNvbm9taWMgY3ljbGUgZHluYW1pY3MuXG4iKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiXG5TVUNDRVNTOiBHbG9iYWwgdGVzdCBub3cgc3VwcG9ydHMgdGhlIFBIIGFzc3VtcHRpb24gKHAgPj0gMC4wNSkuXG4iKQ0KICB9DQp9DQoNCiMgUmUtY2hlY2sgUEggYXNzdW1wdGlvbiBmb3IgYWNxdWlzaXRpb24gbW9kZWwgd2l0aCB0aW1lIGludGVyYWN0aW9ucw0KaWYobGVuZ3RoKGdyZXAoIjp0dCIsIG5hbWVzKGNvZWYoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpKSkpID4gMCkgew0KICBjYXQoIlxuUmUtdGVzdGluZyBQSCBhc3N1bXB0aW9uIGZvciBhY3F1aXNpdGlvbiBtb2RlbCB3aXRoIHRpbWUgaW50ZXJhY3Rpb25zOlxuIikNCiAgcGhfdGVzdF9maW5hbF9hY3F1aXNpdGlvbiA8LSBjb3guenBoKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsKQ0KICBwcmludChwaF90ZXN0X2ZpbmFsX2FjcXVpc2l0aW9uKQ0KICANCiAgIyBDaGVjayBpZiBhbnkgcmVtYWluaW5nIHZpb2xhdGlvbnMsIHNlcGFyYXRlZCBieSB2YXJpYWJsZSB0eXBlDQogIHJlbWFpbmluZ192aW9sYXRvcnMgPC0gcm93bmFtZXMocGhfdGVzdF9maW5hbF9hY3F1aXNpdGlvbiR0YWJsZSlbDQogICAgcGhfdGVzdF9maW5hbF9hY3F1aXNpdGlvbiR0YWJsZVssICJwIl0gPCAwLjA1ICYgDQogICAgcm93bmFtZXMocGhfdGVzdF9maW5hbF9hY3F1aXNpdGlvbiR0YWJsZSkgIT0gIkdMT0JBTCINCiAgXQ0KICANCiAgcmVtYWluaW5nX2Zpbl92aW9sYXRvcnMgPC0gcmVtYWluaW5nX3Zpb2xhdG9yc1tyZW1haW5pbmdfdmlvbGF0b3JzICVpbiUgZmluYW5jaWFsX2NvdmFyaWF0ZXNdDQogIHJlbWFpbmluZ19tYWNyb192aW9sYXRvcnMgPC0gcmVtYWluaW5nX3Zpb2xhdG9yc1tyZW1haW5pbmdfdmlvbGF0b3JzICVpbiUgbWFjcm9fY292YXJpYXRlc10NCiAgDQogIGNhdCgiXG5SZW1haW5pbmcgZmluYW5jaWFsIHZhcmlhYmxlcyB2aW9sYXRpbmcgUEggYXNzdW1wdGlvbjpcbiIpDQogIGlmKGxlbmd0aChyZW1haW5pbmdfZmluX3Zpb2xhdG9ycykgPiAwKSB7DQogICAgcHJpbnQocmVtYWluaW5nX2Zpbl92aW9sYXRvcnMpDQogIH0gZWxzZSB7DQogICAgY2F0KCJOb25lIC0gQWxsIGZpbmFuY2lhbCB2YXJpYWJsZXMgbm93IHNhdGlzZnkgUEggYXNzdW1wdGlvblxuIikNCiAgfQ0KICANCiAgY2F0KCJcblJlbWFpbmluZyBtYWNybyB2YXJpYWJsZXMgdmlvbGF0aW5nIFBIIGFzc3VtcHRpb246XG4iKQ0KICBpZihsZW5ndGgocmVtYWluaW5nX21hY3JvX3Zpb2xhdG9ycykgPiAwKSB7DQogICAgcHJpbnQocmVtYWluaW5nX21hY3JvX3Zpb2xhdG9ycykNCiAgICBjYXQoIlxuTm90ZTogUGVyc2lzdGVudCB2aW9sYXRpb25zIGluIG1hY3JvIHZhcmlhYmxlcyBtYXkgaW5kaWNhdGUgY29tcGxleCBidXNpbmVzcyBjeWNsZSBlZmZlY3RzXG4iKQ0KICAgIGNhdCgidGhhdCBtaWdodCByZXF1aXJlIG1vcmUgc29waGlzdGljYXRlZCBtb2RlbGluZyBhcHByb2FjaGVzIChlLmcuLCBzdHJhdGlmaWNhdGlvbiBvciBmcmFpbHR5IG1vZGVscykuXG4iKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiTm9uZSAtIEFsbCBtYWNybyB2YXJpYWJsZXMgbm93IHNhdGlzZnkgUEggYXNzdW1wdGlvblxuIikNCiAgfQ0KICANCiAgIyBDaGVjayBnbG9iYWwgdGVzdA0KICBpZihwaF90ZXN0X2ZpbmFsX2FjcXVpc2l0aW9uJHRhYmxlWyJHTE9CQUwiLCAicCJdIDwgMC4wNSkgew0KICAgIGNhdCgiXG5XQVJOSU5HOiBHbG9iYWwgdGVzdCBzdGlsbCBpbmRpY2F0ZXMgUEggdmlvbGF0aW9ucyAocCA8IDAuMDUpLlxuIikNCiAgICBjYXQoIkZ1cnRoZXIgcmVmaW5lbWVudHMgbWlnaHQgYmUgbmVlZGVkLCBlc3BlY2lhbGx5IGZvciBlY29ub21pYyBjeWNsZSBkeW5hbWljcy5cbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJcblNVQ0NFU1M6IEdsb2JhbCB0ZXN0IG5vdyBzdXBwb3J0cyB0aGUgUEggYXNzdW1wdGlvbiAocCA+PSAwLjA1KS5cbiIpDQogIH0NCn0NCmBgYA0KDQpXZSBjb21wYXJlIHRoZSBtb2RlbCBmaXQgb2YgdGhlIGZpbmFsIG1vZGVscyB3aXRoIHRpbWUtZGVwZW5kZW50IGNvZWZmaWNpZW50cyB0byB0aGUgb3JpZ2luYWwgbW9kZWxzIHdpdGhvdXQgdGltZSBpbnRlcmFjdGlvbnMuIFdlIHdpbGwgbG9vayBhdCBBSUMsIEJJQywgY29uY29yZGFuY2UsIGFuZCBwZXJmb3JtIGEgbGlrZWxpaG9vZCByYXRpbyB0ZXN0IHRvIHNlZSBpZiB0aGUgYWRkaXRpb24gb2YgdGltZSBpbnRlcmFjdGlvbnMgaW1wcm92ZWQgdGhlIG1vZGVsIGZpdC4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTAuNjogQ29tcGFyZSBNb2RlbCBGaXQgQmVmb3JlIGFuZCBBZnRlciBUaW1lIEludGVyYWN0aW9ucw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMC42OiBDb21wYXJlIE1vZGVsIEZpdCBCZWZvcmUgYW5kIEFmdGVyIFRpbWUgSW50ZXJhY3Rpb25zXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBDb21wYXJlIGJhbmtydXB0Y3kgbW9kZWxzDQppZihsZW5ndGgoZ3JlcCgiOnR0IiwgbmFtZXMoY29lZihmaW5hbF9iYW5rcnVwdGN5X21vZGVsKSkpKSA+IDApIHsNCiAgY2F0KCJDb21wYXJpbmcgYmFua3J1cHRjeSBtb2RlbHMgYmVmb3JlIGFuZCBhZnRlciBhZGRpbmcgdGltZSBpbnRlcmFjdGlvbnM6XG4iKQ0KICBvcmlnaW5hbF9tb2RlbCA8LSBiZXN0X21vZGVscyRiYW5rcnVwdGN5JG1vZGVsDQogIA0KICAjIENvbXBhcmUgQUlDDQogIGFpY19vcmlnaW5hbCA8LSBBSUMob3JpZ2luYWxfbW9kZWwpDQogIGFpY19maW5hbCA8LSBBSUMoZmluYWxfYmFua3J1cHRjeV9tb2RlbCkNCiAgDQogICMgQ29tcGFyZSBCSUMNCiAgYmljX29yaWdpbmFsIDwtIEJJQyhvcmlnaW5hbF9tb2RlbCkNCiAgYmljX2ZpbmFsIDwtIEJJQyhmaW5hbF9iYW5rcnVwdGN5X21vZGVsKQ0KICANCiAgIyBDb21wYXJlIGNvbmNvcmRhbmNlDQogIGNvbmNfb3JpZ2luYWwgPC0gb3JpZ2luYWxfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0NCiAgY29uY19maW5hbCA8LSBmaW5hbF9iYW5rcnVwdGN5X21vZGVsJGNvbmNvcmRhbmNlWyJjb25jb3JkYW5jZSJdDQogIA0KICAjIExpa2VsaWhvb2QgcmF0aW8gdGVzdA0KICBscl90ZXN0IDwtIGFub3ZhKG9yaWdpbmFsX21vZGVsLCBmaW5hbF9iYW5rcnVwdGN5X21vZGVsKQ0KICANCiAgY2F0KCJBSUM6IE9yaWdpbmFsID0iLCByb3VuZChhaWNfb3JpZ2luYWwsIDIpLCAidnMuIFdpdGggdGltZSBpbnRlcmFjdGlvbnMgPSIsIHJvdW5kKGFpY19maW5hbCwgMiksIA0KICAgICAgaWZlbHNlKGFpY19maW5hbCA8IGFpY19vcmlnaW5hbCwgIiAoaW1wcm92ZWQpIiwgIiAobm90IGltcHJvdmVkKSIpLCAiXG4iKQ0KICANCiAgY2F0KCJCSUM6IE9yaWdpbmFsID0iLCByb3VuZChiaWNfb3JpZ2luYWwsIDIpLCAidnMuIFdpdGggdGltZSBpbnRlcmFjdGlvbnMgPSIsIHJvdW5kKGJpY19maW5hbCwgMiksIA0KICAgICAgaWZlbHNlKGJpY19maW5hbCA8IGJpY19vcmlnaW5hbCwgIiAoaW1wcm92ZWQpIiwgIiAobm90IGltcHJvdmVkKSIpLCAiXG4iKQ0KICANCiAgY2F0KCJDb25jb3JkYW5jZTogT3JpZ2luYWwgPSIsIHJvdW5kKGNvbmNfb3JpZ2luYWwsIDQpLCAidnMuIFdpdGggdGltZSBpbnRlcmFjdGlvbnMgPSIsIHJvdW5kKGNvbmNfZmluYWwsIDQpLA0KICAgICAgaWZlbHNlKGNvbmNfZmluYWwgPiBjb25jX29yaWdpbmFsLCAiIChpbXByb3ZlZCkiLCAiIChub3QgaW1wcm92ZWQpIiksICJcbiIpDQogIA0KICBjYXQoIkxpa2VsaWhvb2QgcmF0aW8gdGVzdDpcbiIpDQogIHByaW50KGxyX3Rlc3QpDQogIA0KICAjIENoZWNrIGhvdyBtYW55IHRpbWUgaW50ZXJhY3Rpb25zIGFyZSBmb3IgbWFjcm8gdmFyaWFibGVzDQogIG1hY3JvX3RpbWVfdGVybXMgPC0gZ3JlcCgiOnR0IiwgbmFtZXMoY29lZihmaW5hbF9iYW5rcnVwdGN5X21vZGVsKSksIHZhbHVlID0gVFJVRSkNCiAgbWFjcm9fdGltZV90ZXJtcyA8LSBtYWNyb190aW1lX3Rlcm1zW3NhcHBseShtYWNyb190aW1lX3Rlcm1zLCBmdW5jdGlvbih0ZXJtKSB7DQogICAgdmFyX25hbWUgPC0gc3Ryc3BsaXQodGVybSwgIjoiKVtbMV1dWzFdDQogICAgcmV0dXJuKHZhcl9uYW1lICVpbiUgbWFjcm9fY292YXJpYXRlcykNCiAgfSldDQogIA0KICBpZihsZW5ndGgobWFjcm9fdGltZV90ZXJtcykgPiAwKSB7DQogICAgY2F0KCJcbkNvbnRyaWJ1dGlvbiBvZiBtYWNybyB2YXJpYWJsZSB0aW1lIGludGVyYWN0aW9uczpcbiIpDQogICAgDQogICAgIyBDcmVhdGUgYSBtb2RlbCB3aXRoIG9ubHkgZmluYW5jaWFsIHRpbWUgaW50ZXJhY3Rpb25zDQogICAgbm9fbWFjcm9fdGltZV9mb3JtdWxhIDwtIGZvcm11bGEoZmluYWxfYmFua3J1cHRjeV9tb2RlbCkNCiAgICBmb3IodGVybSBpbiBtYWNyb190aW1lX3Rlcm1zKSB7DQogICAgICBub19tYWNyb190aW1lX2Zvcm11bGEgPC0gdXBkYXRlKG5vX21hY3JvX3RpbWVfZm9ybXVsYSwgcGFzdGUwKCIuIH4gLiAtICIsIHRlcm0pKQ0KICAgIH0NCiAgICANCiAgICBub19tYWNyb190aW1lX21vZGVsIDwtIGNveHBoKG5vX21hY3JvX3RpbWVfZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICAgIA0KICAgICMgQ29tcGFyZSB3aXRoIGZ1bGwgbW9kZWwNCiAgICBtYWNyb190aW1lX2xyX3Rlc3QgPC0gYW5vdmEobm9fbWFjcm9fdGltZV9tb2RlbCwgZmluYWxfYmFua3J1cHRjeV9tb2RlbCkNCiAgICANCiAgICBjYXQoIkxpa2VsaWhvb2QgcmF0aW8gdGVzdCBmb3IgbWFjcm8gdmFyaWFibGUgdGltZSBpbnRlcmFjdGlvbnM6XG4iKQ0KICAgIHByaW50KG1hY3JvX3RpbWVfbHJfdGVzdCkNCiAgICANCiAgICBpZihtYWNyb190aW1lX2xyX3Rlc3RbMiwgIlByKD58Q2hpfCkiXSA8IDAuMDUpIHsNCiAgICAgIGNhdCgiVGhlIHRpbWUtdmFyeWluZyBlZmZlY3RzIG9mIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIHNpZ25pZmljYW50bHkgaW1wcm92ZSB0aGUgbW9kZWwgKHAgPCAwLjA1KS5cbiIpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiVGhlIHRpbWUtdmFyeWluZyBlZmZlY3RzIG9mIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGRvIG5vdCBzaWduaWZpY2FudGx5IGltcHJvdmUgdGhlIG1vZGVsIChwID49IDAuMDUpLlxuIikNCiAgICB9DQogIH0NCn0NCg0KIyBDb21wYXJlIGFjcXVpc2l0aW9uIG1vZGVscw0KaWYobGVuZ3RoKGdyZXAoIjp0dCIsIG5hbWVzKGNvZWYoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpKSkpID4gMCkgew0KICBjYXQoIlxuQ29tcGFyaW5nIGFjcXVpc2l0aW9uIG1vZGVscyBiZWZvcmUgYW5kIGFmdGVyIGFkZGluZyB0aW1lIGludGVyYWN0aW9uczpcbiIpDQogIG9yaWdpbmFsX21vZGVsIDwtIGJlc3RfbW9kZWxzJGFjcXVpc2l0aW9uJG1vZGVsDQogIA0KICAjIENvbXBhcmUgQUlDDQogIGFpY19vcmlnaW5hbCA8LSBBSUMob3JpZ2luYWxfbW9kZWwpDQogIGFpY19maW5hbCA8LSBBSUMoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpDQogIA0KICAjIENvbXBhcmUgQklDDQogIGJpY19vcmlnaW5hbCA8LSBCSUMob3JpZ2luYWxfbW9kZWwpDQogIGJpY19maW5hbCA8LSBCSUMoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpDQogIA0KICAjIENvbXBhcmUgY29uY29yZGFuY2UNCiAgY29uY19vcmlnaW5hbCA8LSBvcmlnaW5hbF9tb2RlbCRjb25jb3JkYW5jZVsiY29uY29yZGFuY2UiXQ0KICBjb25jX2ZpbmFsIDwtIGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsJGNvbmNvcmRhbmNlWyJjb25jb3JkYW5jZSJdDQogIA0KICAjIExpa2VsaWhvb2QgcmF0aW8gdGVzdA0KICBscl90ZXN0IDwtIGFub3ZhKG9yaWdpbmFsX21vZGVsLCBmaW5hbF9hY3F1aXNpdGlvbl9tb2RlbCkNCiAgDQogIGNhdCgiQUlDOiBPcmlnaW5hbCA9Iiwgcm91bmQoYWljX29yaWdpbmFsLCAyKSwgInZzLiBXaXRoIHRpbWUgaW50ZXJhY3Rpb25zID0iLCByb3VuZChhaWNfZmluYWwsIDIpLCANCiAgICAgIGlmZWxzZShhaWNfZmluYWwgPCBhaWNfb3JpZ2luYWwsICIgKGltcHJvdmVkKSIsICIgKG5vdCBpbXByb3ZlZCkiKSwgIlxuIikNCiAgDQogIGNhdCgiQklDOiBPcmlnaW5hbCA9Iiwgcm91bmQoYmljX29yaWdpbmFsLCAyKSwgInZzLiBXaXRoIHRpbWUgaW50ZXJhY3Rpb25zID0iLCByb3VuZChiaWNfZmluYWwsIDIpLCANCiAgICAgIGlmZWxzZShiaWNfZmluYWwgPCBiaWNfb3JpZ2luYWwsICIgKGltcHJvdmVkKSIsICIgKG5vdCBpbXByb3ZlZCkiKSwgIlxuIikNCiAgDQogIGNhdCgiQ29uY29yZGFuY2U6IE9yaWdpbmFsID0iLCByb3VuZChjb25jX29yaWdpbmFsLCA0KSwgInZzLiBXaXRoIHRpbWUgaW50ZXJhY3Rpb25zID0iLCByb3VuZChjb25jX2ZpbmFsLCA0KSwNCiAgICAgIGlmZWxzZShjb25jX2ZpbmFsID4gY29uY19vcmlnaW5hbCwgIiAoaW1wcm92ZWQpIiwgIiAobm90IGltcHJvdmVkKSIpLCAiXG4iKQ0KICANCiAgY2F0KCJMaWtlbGlob29kIHJhdGlvIHRlc3Q6XG4iKQ0KICBwcmludChscl90ZXN0KQ0KICANCiAgIyBDaGVjayBob3cgbWFueSB0aW1lIGludGVyYWN0aW9ucyBhcmUgZm9yIG1hY3JvIHZhcmlhYmxlcw0KICBtYWNyb190aW1lX3Rlcm1zIDwtIGdyZXAoIjp0dCIsIG5hbWVzKGNvZWYoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpKSwgdmFsdWUgPSBUUlVFKQ0KICBtYWNyb190aW1lX3Rlcm1zIDwtIG1hY3JvX3RpbWVfdGVybXNbc2FwcGx5KG1hY3JvX3RpbWVfdGVybXMsIGZ1bmN0aW9uKHRlcm0pIHsNCiAgICB2YXJfbmFtZSA8LSBzdHJzcGxpdCh0ZXJtLCAiOiIpW1sxXV1bMV0NCiAgICByZXR1cm4odmFyX25hbWUgJWluJSBtYWNyb19jb3ZhcmlhdGVzKQ0KICB9KV0NCiAgDQogIGlmKGxlbmd0aChtYWNyb190aW1lX3Rlcm1zKSA+IDApIHsNCiAgICBjYXQoIlxuQ29udHJpYnV0aW9uIG9mIG1hY3JvIHZhcmlhYmxlIHRpbWUgaW50ZXJhY3Rpb25zOlxuIikNCiAgICANCiAgICAjIENyZWF0ZSBhIG1vZGVsIHdpdGggb25seSBmaW5hbmNpYWwgdGltZSBpbnRlcmFjdGlvbnMNCiAgICBub19tYWNyb190aW1lX2Zvcm11bGEgPC0gZm9ybXVsYShmaW5hbF9hY3F1aXNpdGlvbl9tb2RlbCkNCiAgICBmb3IodGVybSBpbiBtYWNyb190aW1lX3Rlcm1zKSB7DQogICAgICBub19tYWNyb190aW1lX2Zvcm11bGEgPC0gdXBkYXRlKG5vX21hY3JvX3RpbWVfZm9ybXVsYSwgcGFzdGUwKCIuIH4gLiAtICIsIHRlcm0pKQ0KICAgIH0NCiAgICANCiAgICBub19tYWNyb190aW1lX21vZGVsIDwtIGNveHBoKG5vX21hY3JvX3RpbWVfZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICAgIA0KICAgICMgQ29tcGFyZSB3aXRoIGZ1bGwgbW9kZWwNCiAgICBtYWNyb190aW1lX2xyX3Rlc3QgPC0gYW5vdmEobm9fbWFjcm9fdGltZV9tb2RlbCwgZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpDQogICAgDQogICAgY2F0KCJMaWtlbGlob29kIHJhdGlvIHRlc3QgZm9yIG1hY3JvIHZhcmlhYmxlIHRpbWUgaW50ZXJhY3Rpb25zOlxuIikNCiAgICBwcmludChtYWNyb190aW1lX2xyX3Rlc3QpDQogICAgDQogICAgaWYobWFjcm9fdGltZV9scl90ZXN0WzIsICJQcig+fENoaXwpIl0gPCAwLjA1KSB7DQogICAgICBjYXQoIlRoZSB0aW1lLXZhcnlpbmcgZWZmZWN0cyBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBzaWduaWZpY2FudGx5IGltcHJvdmUgdGhlIG1vZGVsIChwIDwgMC4wNSkuXG4iKQ0KICAgIH0gZWxzZSB7DQogICAgICBjYXQoIlRoZSB0aW1lLXZhcnlpbmcgZWZmZWN0cyBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBkbyBub3Qgc2lnbmlmaWNhbnRseSBpbXByb3ZlIHRoZSBtb2RlbCAocCA+PSAwLjA1KS5cbiIpDQogICAgfQ0KICB9DQp9DQpgYGANCkFnYWluLCBpbmNsdWRlZCBvbmx5IGZvciBzYWtlIG9mIGNvbXBsZXRlbmVzcyENCg0KRmluYWxseSwgd2Ugd2lsbCB2aXN1YWxpemUgdGhlIHRpbWUtdmFyeWluZyBlZmZlY3RzIG9mIHRoZSBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpbiBib3RoIG1vZGVscy4gV2Ugd2lsbCBjcmVhdGUgcGxvdHMgdGhhdCBzaG93IGhvdyB0aGUgaGF6YXJkIHJhdGlvcyBjaGFuZ2Ugb3ZlciB0aW1lIGZvciBkaWZmZXJlbnQgcmVmZXJlbmNlIHZhbHVlcyBvZiB0aGUgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMuIFRoaXMgd2lsbCBoZWxwIHVzIHVuZGVyc3RhbmQgdGhlIGR5bmFtaWMgbmF0dXJlIG9mIHRoZXNlIGVmZmVjdHMgYW5kIHRoZWlyIGltcGxpY2F0aW9ucyBmb3IgYnVzaW5lc3MgY3ljbGVzLiBZb3UgY2FuIGFsc28gc2VlIGFsbCB0aGUgdGltZS12YXJ5aW5nIGVmZmVjdHMgb2YgdGhlIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGluIHRoZSBmaW5hbCBtb2RlbHMuIFdlIHdpbGwgY3JlYXRlIHBsb3RzIHRoYXQgc2hvdyBob3cgdGhlIGhhemFyZCByYXRpb3MgY2hhbmdlIG92ZXIgdGltZSBmb3IgZGlmZmVyZW50IHJlZmVyZW5jZSB2YWx1ZXMgb2YgdGhlIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzLiBUaGlzIHdpbGwgaGVscCB1cyB1bmRlcnN0YW5kIHRoZSBkeW5hbWljIG5hdHVyZSBvZiB0aGVzZSBlZmZlY3RzIGFuZCB0aGVpciBpbXBsaWNhdGlvbnMgZm9yIGJ1c2luZXNzIGN5Y2xlcy4NCmBgYHtyfQ0KIyBGdW5jdGlvbiB0byBjcmVhdGUgZW5oYW5jZWQgdmlzdWFsaXphdGlvbiBvZiB0aW1lLXZhcnlpbmcgZWZmZWN0cw0KdmlzdWFsaXplX3RpbWVfdmFyeWluZ19lZmZlY3RzIDwtIGZ1bmN0aW9uKG1vZGVsLCBtb2RlbF9uYW1lKSB7DQogIGNhdCgiVmlzdWFsaXppbmcgdGltZS12YXJ5aW5nIGVmZmVjdHMgZm9yIiwgbW9kZWxfbmFtZSwgIm1vZGVsLi4uXG4iKQ0KICANCiAgIyBFeHRyYWN0IGNvZWZmaWNpZW50cw0KICBjb2VmcyA8LSBjb2VmKG1vZGVsKQ0KICANCiAgIyBJZGVudGlmeSB0aW1lIGludGVyYWN0aW9uIHRlcm1zDQogIHRpbWVfdGVybXMgPC0gbmFtZXMoY29lZnMpW2dyZXBsKCI6dHQiLCBuYW1lcyhjb2VmcykpXQ0KICANCiAgaWYobGVuZ3RoKHRpbWVfdGVybXMpID09IDApIHsNCiAgICBjYXQoIk5vIHRpbWUtdmFyeWluZyBlZmZlY3RzIGZvdW5kIGluIHRoZSIsIG1vZGVsX25hbWUsICJtb2RlbC5cbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0NCiAgDQogICMgRXh0cmFjdCBiYXNlIHZhcmlhYmxlIG5hbWVzDQogIG1haW5fdGVybXMgPC0gdW5pcXVlKHNhcHBseShzdHJzcGxpdCh0aW1lX3Rlcm1zLCAiOiIpLCBgW2AsIDEpKQ0KICANCiAgIyBTZXBhcmF0ZSBmaW5hbmNpYWwgYW5kIG1hY3JvIHZhcmlhYmxlcw0KICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAiZ2RwX2RlZmxhdG9yIiwgInVuZW1wbG95ZW1lbnQiKQ0KICBmaW5fdmFycyA8LSBtYWluX3Rlcm1zWyFtYWluX3Rlcm1zICVpbiUgbWFjcm9fdmFyc10NCiAgbWFjcm9fdGVybXMgPC0gbWFpbl90ZXJtc1ttYWluX3Rlcm1zICVpbiUgbWFjcm9fdmFyc10NCiAgDQogIGNhdCgiSWRlbnRpZmllZCIsIGxlbmd0aChmaW5fdmFycyksICJmaW5hbmNpYWwgdmFyaWFibGVzIHdpdGggdGltZS12YXJ5aW5nIGVmZmVjdHM6XG4iKQ0KICBpZihsZW5ndGgoZmluX3ZhcnMpID4gMCkgY2F0KHBhc3RlKGZpbl92YXJzLCBjb2xsYXBzZSA9ICIsICIpLCAiXG5cbiIpDQogIA0KICBjYXQoIklkZW50aWZpZWQiLCBsZW5ndGgobWFjcm9fdGVybXMpLCAibWFjcm9lY29ub21pYyB2YXJpYWJsZXMgd2l0aCB0aW1lLXZhcnlpbmcgZWZmZWN0czpcbiIpDQogIGlmKGxlbmd0aChtYWNyb190ZXJtcykgPiAwKSBjYXQocGFzdGUobWFjcm9fdGVybXMsIGNvbGxhcHNlID0gIiwgIiksICJcblxuIikNCiAgDQogICMgUHJvY2VzcyBhbGwgdmFyaWFibGVzIHdpdGggdGltZS12YXJ5aW5nIGVmZmVjdHMNCiAgYWxsX3ZhcnMgPC0gbGlzdCgNCiAgICBGaW5hbmNpYWwgPSBmaW5fdmFycywNCiAgICBNYWNybyA9IG1hY3JvX3Rlcm1zDQogICkNCiAgDQogICMgQ3JlYXRlIG1hcHBpbmcgZm9yIHZhcmlhYmxlIG5hbWVzIHRvIGRlc2NyaXB0aXZlIGxhYmVscw0KICB2YXJfbGFiZWxzIDwtIGxpc3QoDQogICAgIkxUTVRBIiA9ICJMaWFiaWxpdGllcy10by1NYXJrZXQgVmFsdWVcbihIaWdoZXIgPSBNb3JlIExldmVyYWdlKSIsDQogICAgIk5JTVRBIiA9ICJOZXQgSW5jb21lLXRvLU1hcmtldCBWYWx1ZVxuKEhpZ2hlciA9IEJldHRlciBQcm9maXRhYmlsaXR5KSIsDQogICAgIkNBU0hNVEEiID0gIkNhc2gtdG8tTWFya2V0IFZhbHVlXG4oSGlnaGVyID0gTW9yZSBMaXF1aWRpdHkpIiwNCiAgICAiUFJJQ0UiID0gIlN0b2NrIFByaWNlXG4oSGlnaGVyID0gQmV0dGVyIE1hcmtldCBQZXJjZXB0aW9uKSIsDQogICAgIk1CRSIgPSAiTWFya2V0LXRvLUJvb2sgRXF1aXR5XG4oSGlnaGVyID0gR3Jvd3RoIEV4cGVjdGF0aW9ucykiLA0KICAgICJSU0laRSIgPSAiUmVsYXRpdmUgU2l6ZVxuKEhpZ2hlciA9IExhcmdlciBDb21wYW55KSIsDQogICAgImRlYnRfcmF0aW8iID0gIkRlYnQgUmF0aW9cbihIaWdoZXIgPSBNb3JlIExldmVyYWdlKSIsDQogICAgImN1cnJlbnRfcmF0aW8iID0gIkN1cnJlbnQgUmF0aW9cbihIaWdoZXIgPSBCZXR0ZXIgTGlxdWlkaXR5KSIsDQogICAgInF1aWNrX3JhdGlvIiA9ICJRdWljayBSYXRpb1xuKEhpZ2hlciA9IEJldHRlciBMaXF1aWRpdHkpIiwNCiAgICAiel9zY29yZSIgPSAiQWx0bWFuIFotU2NvcmVcbihIaWdoZXIgPSBMb3dlciBCYW5rcnVwdGN5IFJpc2spIiwNCiAgICAiZ2RwX2dyb3d0aCIgPSAiR0RQIEdyb3d0aFxuKEhpZ2hlciA9IFN0cm9uZ2VyIEVjb25vbXkpIiwNCiAgICAiZ2RwX2RlZmxhdG9yIiA9ICJHRFAgRGVmbGF0b3JcbihIaWdoZXIgPSBIaWdoZXIgSW5mbGF0aW9uKSIsDQogICAgInVuZW1wbG95ZW1lbnQiID0gIlVuZW1wbG95bWVudCBSYXRlXG4oSGlnaGVyID0gV2Vha2VyIExhYm9yIE1hcmtldCkiDQogICkNCiAgDQogICMgUHJvY2VzcyB2YXJpYWJsZXMgYnkgY2F0ZWdvcnkNCiAgZm9yKHZhcl90eXBlIGluIG5hbWVzKGFsbF92YXJzKSkgew0KICAgIHZhcmlhYmxlcyA8LSBhbGxfdmFyc1tbdmFyX3R5cGVdXQ0KICAgIA0KICAgIGlmKGxlbmd0aCh2YXJpYWJsZXMpID09IDApIG5leHQNCiAgICANCiAgICBjYXQoIlxuVmlzdWFsaXppbmciLCB0b2xvd2VyKHZhcl90eXBlKSwgInZhcmlhYmxlIHRpbWUgZWZmZWN0czpcbiIpDQogICAgDQogICAgIyBDcmVhdGUgbXVsdGktcGFuZWwgbGF5b3V0IGZvciBvdmVydmlldyBwbG90cw0KICAgIG5fdmFycyA8LSBsZW5ndGgodmFyaWFibGVzKQ0KICAgIG5fY29scyA8LSBtaW4oMiwgbl92YXJzKQ0KICAgIG5fcm93cyA8LSBjZWlsaW5nKG5fdmFycyAvIG5fY29scykNCiAgICANCiAgICBwYXIobWZyb3cgPSBjKG5fcm93cywgbl9jb2xzKSwgbWFyID0gYyg0LCA0LCAzLCAxKSArIDAuMSkNCiAgICANCiAgICAjIENyZWF0ZSBvdmVydmlldyBwbG90cyBmb3IgZWFjaCB2YXJpYWJsZQ0KICAgIGZvcih2YXIgaW4gdmFyaWFibGVzKSB7DQogICAgICBjYXQoIiAgLSIsIHZhciwgIlxuIikNCiAgICAgIA0KICAgICAgIyBHZXQgY29lZmZpY2llbnRzDQogICAgICBtYWluX2NvZWYgPC0gY29lZnNbdmFyXQ0KICAgICAgdGltZV9jb2VmIDwtIGNvZWZzW3Bhc3RlMCh2YXIsICI6dHQiKV0NCiAgICAgIA0KICAgICAgIyBTZXQgdXAgdGltZSBzZXF1ZW5jZQ0KICAgICAgbWF4X3RpbWUgPC0gbWF4KGRhdGEkdHN0b3ApDQogICAgICB0aW1lcyA8LSBzZXEoMCwgbWF4X3RpbWUsIGxlbmd0aC5vdXQgPSAxMDApDQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIGhhemFyZCByYXRpb3MgZm9yIDEtdW5pdCBjaGFuZ2UgaW4gdGhlIHZhcmlhYmxlIG92ZXIgdGltZQ0KICAgICAgaHJzIDwtIGV4cChtYWluX2NvZWYgKyB0aW1lX2NvZWYgKiB0aW1lcykNCiAgICAgIA0KICAgICAgIyBHZXQgZGVzY3JpcHRpdmUgbGFiZWwgZm9yIHRoZSB2YXJpYWJsZQ0KICAgICAgdmFyX3RpdGxlIDwtIGlmKHZhciAlaW4lIG5hbWVzKHZhcl9sYWJlbHMpKSB2YXJfbGFiZWxzW1t2YXJdXSBlbHNlIHZhcg0KICAgICAgDQogICAgICAjIENyZWF0ZSBwbG90DQogICAgICBwbG90KHRpbWVzLCBocnMsIHR5cGUgPSAibCIsIGx3ZCA9IDIsIGNvbCA9ICJkYXJrYmx1ZSIsDQogICAgICAgICAgIG1haW4gPSB2YXJfdGl0bGUsDQogICAgICAgICAgIHhsYWIgPSAiVGltZSAoWWVhcnMpIiwgeWxhYiA9ICJIYXphcmQgUmF0aW8iLA0KICAgICAgICAgICB5bGltID0gYyhtaW4oMC41LCBtaW4oaHJzKSksIG1heCgyLCBtYXgoaHJzKSkpKQ0KICAgICAgDQogICAgICAjIEFkZCByZWZlcmVuY2UgbGluZSBhdCBIUiA9IDENCiAgICAgIGFibGluZShoID0gMSwgbHR5ID0gMiwgY29sID0gImRhcmtncmF5IikNCiAgICAgIA0KICAgICAgIyBBZGQgZ3JpZCBmb3IgcmVhZGFiaWxpdHkNCiAgICAgIGdyaWQoKQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBhbmQgbWFyayBjcm9zc292ZXIgcG9pbnQgKGlmIGFueSkNCiAgICAgIGlmKHNpZ24obWFpbl9jb2VmKSAhPSBzaWduKHRpbWVfY29lZikgJiYgdGltZV9jb2VmICE9IDApIHsNCiAgICAgICAgY3Jvc3NvdmVyIDwtIC1tYWluX2NvZWYgLyB0aW1lX2NvZWYNCiAgICAgICAgaWYoY3Jvc3NvdmVyID4gMCAmJiBjcm9zc292ZXIgPCBtYXgodGltZXMpKSB7DQogICAgICAgICAgcG9pbnRzKGNyb3Nzb3ZlciwgMSwgcGNoID0gMTYsIGNvbCA9ICJyZWQiLCBjZXggPSAxLjUpDQogICAgICAgICAgdGV4dChjcm9zc292ZXIsIDEuMSwgcGFzdGUoIkNyb3Nzb3ZlciBhdCIsIHJvdW5kKGNyb3Nzb3ZlciwgMSksICJ5ZWFycyIpLA0KICAgICAgICAgICAgICAgY29sID0gInJlZCIsIGNleCA9IDAuOCkNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIENvbG9yIHRoZSBsaW5lIGJhc2VkIG9uIHRoZSBjdXJyZW50IGVmZmVjdCBkaXJlY3Rpb24NCiAgICAgIHBvaW50cyh0aW1lc1sxXSwgaHJzWzFdLCBwY2ggPSAxNiwgY29sID0gaWZlbHNlKGhyc1sxXSA+IDEsICJyZWQiLCAiZ3JlZW40IiksIGNleCA9IDEuMikNCiAgICAgIHBvaW50cyh0aW1lc1tsZW5ndGgodGltZXMpXSwgaHJzW2xlbmd0aCh0aW1lcyldLCBwY2ggPSAxNiwgDQogICAgICAgICAgICAgY29sID0gaWZlbHNlKGhyc1tsZW5ndGgodGltZXMpXSA+IDEsICJyZWQiLCAiZ3JlZW40IiksIGNleCA9IDEuMikNCiAgICAgIA0KICAgICAgIyBBZGQgdGltZSBlZmZlY3QgaW50ZXJwcmV0YXRpb24NCiAgICAgIGRpcmVjdGlvbiA8LSBpZmVsc2UobWFpbl9jb2VmID4gMCwgImluY3JlYXNlcyIsICJkZWNyZWFzZXMiKQ0KICAgICAgY2hhbmdlIDwtIGlmZWxzZShzaWduKG1haW5fY29lZikgPT0gc2lnbih0aW1lX2NvZWYpLCAic3RyZW5ndGhlbnMiLCAid2Vha2VucyIpDQogICAgICANCiAgICAgIG10ZXh0KHBhc3RlMCgiRWZmZWN0ICIsIGNoYW5nZSwgIiBvdmVyIHRpbWUiKSwgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuNywgDQogICAgICAgICAgICBjb2wgPSBpZmVsc2UoY2hhbmdlID09ICJzdHJlbmd0aGVucyIsICJkYXJrcmVkIiwgImRhcmtibHVlIikpDQogICAgICANCiAgICAgICMgQWRkIGJ1c2luZXNzIGluc2lnaHRzIGJhc2VkIG9uIHZhcmlhYmxlIHR5cGUNCiAgICAgIGlmKHZhcl90eXBlID09ICJGaW5hbmNpYWwiKSB7DQogICAgICAgIGFkZF9maW5hbmNpYWxfaW5zaWdodHModmFyLCBtb2RlbF9uYW1lLCBjaGFuZ2UsIHRpbWVzLCBocnMsIG1heF90aW1lKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgYWRkX21hY3JvX2luc2lnaHRzKHZhciwgbW9kZWxfbmFtZSwgY2hhbmdlLCB0aW1lcywgaHJzLCBtYXhfdGltZSkNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBSZXNldCBwbG90dGluZyBwYXJhbWV0ZXJzDQogICAgcGFyKG1mcm93ID0gYygxLCAxKSwgbWFyID0gYyg1LCA0LCA0LCAyKSArIDAuMSkNCiAgICANCiAgICAjIENyZWF0ZSBkZXRhaWxlZCBwbG90cyB3aXRoIHF1YW50aWxlLWJhc2VkIHJlZmVyZW5jZSB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUNCiAgICBmb3IodmFyIGluIHZhcmlhYmxlcykgew0KICAgICAgY3JlYXRlX2RldGFpbGVkX3Bsb3QobW9kZWwsIHZhciwgdmFyX3R5cGUsIG1vZGVsX25hbWUsIHZhcl9sYWJlbHMpDQogICAgfQ0KICB9DQogIA0KICAjIEZpbmQgYW5kIGhpZ2hsaWdodCB0aGUgbW9zdCBpbXBvcnRhbnQgdGltZS12YXJ5aW5nIHZhcmlhYmxlDQogIGlmKGxlbmd0aChtYWluX3Rlcm1zKSA+IDApIHsNCiAgICAjIEZpbmQgdGhlIHZhcmlhYmxlIHdpdGggbGFyZ2VzdCBhYnNvbHV0ZSB0aW1lIGNvZWZmaWNpZW50DQogICAgYWJzX3RpbWVfY29lZnMgPC0gYWJzKGNvZWZzW3Bhc3RlMChtYWluX3Rlcm1zLCAiOnR0IildKQ0KICAgIG1vc3RfaW1wb3J0YW50X2lkeCA8LSB3aGljaC5tYXgoYWJzX3RpbWVfY29lZnMpDQogICAgbW9zdF9pbXBvcnRhbnRfdmFyIDwtIG1haW5fdGVybXNbbW9zdF9pbXBvcnRhbnRfaWR4XQ0KICAgIG1vc3RfaW1wb3J0YW50X3R5cGUgPC0gaWYobW9zdF9pbXBvcnRhbnRfdmFyICVpbiUgbWFjcm9fdmFycykgIk1hY3JvIiBlbHNlICJGaW5hbmNpYWwiDQogICAgDQogICAgY2F0KCJcbk1vc3QgaW1wb3J0YW50IHRpbWUtdmFyeWluZyB2YXJpYWJsZToiLCBtb3N0X2ltcG9ydGFudF92YXIsICIoIiwgbW9zdF9pbXBvcnRhbnRfdHlwZSwgIilcbiIpDQogICAgY2F0KCJDcmVhdGluZyBlbmhhbmNlZCB2aXN1YWxpemF0aW9uIGZvciBtb3N0IGltcG9ydGFudCB2YXJpYWJsZS4uLlxuIikNCiAgICANCiAgICAjIENyZWF0ZSBlbmhhbmNlZCBwbG90IHdpdGggYnVzaW5lc3MgaW5zaWdodHMgZm9yIG1vc3QgaW1wb3J0YW50IHZhcmlhYmxlDQogICAgY3JlYXRlX2VuaGFuY2VkX3Bsb3QobW9kZWwsIG1vc3RfaW1wb3J0YW50X3ZhciwgbW9zdF9pbXBvcnRhbnRfdHlwZSwgbW9kZWxfbmFtZSwgdmFyX2xhYmVscykNCiAgfQ0KICANCiAgY2F0KCJUaW1lLXZhcnlpbmcgZWZmZWN0cyB2aXN1YWxpemF0aW9uIGNvbXBsZXRlZCBmb3IiLCBtb2RlbF9uYW1lLCAibW9kZWwuXG4iKQ0KfQ0KDQojIEhlbHBlciBmdW5jdGlvbiB0byBhZGQgZmluYW5jaWFsIGluc2lnaHRzIHRvIHBsb3RzDQphZGRfZmluYW5jaWFsX2luc2lnaHRzIDwtIGZ1bmN0aW9uKHZhciwgbW9kZWxfbmFtZSwgY2hhbmdlLCB0aW1lcywgaHJzLCBtYXhfdGltZSkgew0KICB5X3BvcyA8LSBpZmVsc2UoaHJzW2xlbmd0aCh0aW1lcyldID4gaHJzWzFdLCAwLjg1ICogbWF4KGhycyksIDAuNSkNCiAgDQogICMgQWRkIGZpbmFuY2lhbCBpbnNpZ2h0IGJhc2VkIG9uIHZhcmlhYmxlIGFuZCBtb2RlbCB0eXBlDQogIGlmKHZhciA9PSAiTFRNVEEiIHx8IHZhciA9PSAiZGVidF9yYXRpbyIpIHsNCiAgICBpZihtb2RlbF9uYW1lID09ICJCYW5rcnVwdGN5Iikgew0KICAgICAgaWYoY2hhbmdlID09ICJ3ZWFrZW5zIikgew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkxldmVyYWdlIG1hdHRlcnNcbm1vc3QgZm9yIHlvdW5nIGZpcm1zIiwgDQogICAgICAgICAgICAgY2V4ID0gMC43LCBjb2wgPSAiZGFya3JlZCIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkxldmVyYWdlIGJlY29tZXNcbm1vcmUgcmlza3kgd2l0aCBhZ2UiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0NCiAgICB9IGVsc2UgeyAgIyBBY3F1aXNpdGlvbg0KICAgICAgaWYoY2hhbmdlID09ICJ3ZWFrZW5zIikgew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkFjcXVpcmVycyBmb2N1cyBvblxuZGVidCBlYXJseSBpbiBsaWZlY3ljbGUiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHRleHQobWF4X3RpbWUgKiAwLjUsIHlfcG9zLCAiTWF0dXJpdHkgaW5jcmVhc2VzXG5sZXZlcmFnZSBzaWduaWZpY2FuY2UiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0NCiAgICB9DQogIH0gZWxzZSBpZih2YXIgPT0gIk5JTVRBIikgew0KICAgIGlmKG1vZGVsX25hbWUgPT0gIkJhbmtydXB0Y3kiKSB7DQogICAgICBpZihjaGFuZ2UgPT0gIndlYWtlbnMiKSB7DQogICAgICAgIHRleHQobWF4X3RpbWUgKiAwLjUsIHlfcG9zLCAiUHJvZml0YWJpbGl0eSBtYXR0ZXJzXG5tb3N0IGZvciB5b3VuZyBmaXJtcyIsIA0KICAgICAgICAgICAgIGNleCA9IDAuNywgY29sID0gImRhcmtyZWQiKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgdGV4dChtYXhfdGltZSAqIDAuNSwgeV9wb3MsICJQcm9maXRhYmlsaXR5IGJlY29tZXNcbm1vcmUgY3JpdGljYWwgd2l0aCBhZ2UiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0NCiAgICB9DQogIH0gZWxzZSBpZih2YXIgPT0gIkNBU0hNVEEiIHx8IHZhciA9PSAiY3VycmVudF9yYXRpbyIgfHwgdmFyID09ICJxdWlja19yYXRpbyIpIHsNCiAgICBpZihtb2RlbF9uYW1lID09ICJCYW5rcnVwdGN5Iikgew0KICAgICAgaWYoY2hhbmdlID09ICJ3ZWFrZW5zIikgew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkxpcXVpZGl0eSBtb3N0XG5jcml0aWNhbCBlYXJseSBvbiIsIA0KICAgICAgICAgICAgIGNleCA9IDAuNywgY29sID0gImRhcmtyZWQiKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgdGV4dChtYXhfdGltZSAqIDAuNSwgeV9wb3MsICJMaXF1aWRpdHkgYmVjb21lc1xubW9yZSBpbXBvcnRhbnQgd2l0aCBhZ2UiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0NCiAgICB9IGVsc2UgeyAgIyBBY3F1aXNpdGlvbg0KICAgICAgaWYoY2hhbmdlID09ICJ3ZWFrZW5zIikgew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkNhc2ggYXR0cmFjdHMgYWNxdWlyZXJzXG5tb3N0IGluIGVhcmx5IHllYXJzIiwgDQogICAgICAgICAgICAgY2V4ID0gMC43LCBjb2wgPSAiZGFya3JlZCIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkNhc2ggYmVjb21lcyBtb3JlXG5hdHRyYWN0aXZlIHdpdGggbWF0dXJpdHkiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0NCiAgICB9DQogIH0gZWxzZSBpZih2YXIgPT0gInpfc2NvcmUiKSB7DQogICAgaWYobW9kZWxfbmFtZSA9PSAiQmFua3J1cHRjeSIpIHsNCiAgICAgIGlmKGNoYW5nZSA9PSAid2Vha2VucyIpIHsNCiAgICAgICAgdGV4dChtYXhfdGltZSAqIDAuNSwgeV9wb3MsICJaLXNjb3JlIG1vc3QgcHJlZGljdGl2ZVxuZm9yIHlvdW5nIGZpcm1zIiwgDQogICAgICAgICAgICAgY2V4ID0gMC43LCBjb2wgPSAiZGFya3JlZCIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIlotc2NvcmUgYmVjb21lcyBtb3JlXG5wcmVkaWN0aXZlIHdpdGggYWdlIiwgDQogICAgICAgICAgICAgY2V4ID0gMC43LCBjb2wgPSAiZGFya3JlZCIpDQogICAgICB9DQogICAgfQ0KICB9DQp9DQoNCiMgSGVscGVyIGZ1bmN0aW9uIHRvIGFkZCBtYWNyb2Vjb25vbWljIGluc2lnaHRzIHRvIHBsb3RzDQphZGRfbWFjcm9faW5zaWdodHMgPC0gZnVuY3Rpb24odmFyLCBtb2RlbF9uYW1lLCBjaGFuZ2UsIHRpbWVzLCBocnMsIG1heF90aW1lKSB7DQogIHlfcG9zIDwtIGlmZWxzZShocnNbbGVuZ3RoKHRpbWVzKV0gPiBocnNbMV0sIDAuODUgKiBtYXgoaHJzKSwgMC41KQ0KICANCiAgIyBBZGQgbWFjcm8gaW5zaWdodCBiYXNlZCBvbiB2YXJpYWJsZSBhbmQgbW9kZWwgdHlwZQ0KICBpZih2YXIgPT0gImdkcF9ncm93dGgiKSB7DQogICAgaWYobW9kZWxfbmFtZSA9PSAiQmFua3J1cHRjeSIpIHsNCiAgICAgIGlmKGNoYW5nZSA9PSAid2Vha2VucyIpIHsNCiAgICAgICAgdGV4dChtYXhfdGltZSAqIDAuNSwgeV9wb3MsICJFY29ub21pYyBncm93dGggbW9zdFxucHJvdGVjdGl2ZSBmb3IgbmV3IGZpcm1zIiwgDQogICAgICAgICAgICAgY2V4ID0gMC43LCBjb2wgPSAiZGFya3JlZCIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkdyb3d0aCBwcm90ZWN0aW9uXG5pbmNyZWFzZXMgd2l0aCBmaXJtIGFnZSIsIA0KICAgICAgICAgICAgIGNleCA9IDAuNywgY29sID0gImRhcmtyZWQiKQ0KICAgICAgfQ0KICAgIH0gZWxzZSB7ICAjIEFjcXVpc2l0aW9uDQogICAgICBpZihjaGFuZ2UgPT0gIndlYWtlbnMiKSB7DQogICAgICAgIHRleHQobWF4X3RpbWUgKiAwLjUsIHlfcG9zLCAiR3Jvd3RoIHNwdXJzIGFjcXVpc2l0aW9uc1xubW9yZSBmb3IgeW91bmcgZmlybXMiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHRleHQobWF4X3RpbWUgKiAwLjUsIHlfcG9zLCAiR3Jvd3RoIGVmZmVjdCBvbiBNJkFcbmluY3JlYXNlcyB3aXRoIGFnZSIsIA0KICAgICAgICAgICAgIGNleCA9IDAuNywgY29sID0gImRhcmtyZWQiKQ0KICAgICAgfQ0KICAgIH0NCiAgfSBlbHNlIGlmKHZhciA9PSAidW5lbXBsb3llbWVudCIpIHsNCiAgICBpZihtb2RlbF9uYW1lID09ICJCYW5rcnVwdGN5Iikgew0KICAgICAgaWYoY2hhbmdlID09ICJ3ZWFrZW5zIikgew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkhpZ2ggdW5lbXBsb3ltZW50IG1vc3RcbmRhbmdlcm91cyBmb3IgbmV3IGZpcm1zIiwgDQogICAgICAgICAgICAgY2V4ID0gMC43LCBjb2wgPSAiZGFya3JlZCIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkxhYm9yIG1hcmtldCBpbXBhY3RcbmluY3JlYXNlcyBvdmVyIHRpbWUiLCANCiAgICAgICAgICAgICBjZXggPSAwLjcsIGNvbCA9ICJkYXJrcmVkIikNCiAgICAgIH0NCiAgICB9DQogIH0gZWxzZSBpZih2YXIgPT0gImdkcF9kZWZsYXRvciIpIHsNCiAgICBpZihtb2RlbF9uYW1lID09ICJCYW5rcnVwdGN5Iikgew0KICAgICAgaWYoY2hhbmdlID09ICJ3ZWFrZW5zIikgew0KICAgICAgICB0ZXh0KG1heF90aW1lICogMC41LCB5X3BvcywgIkluZmxhdGlvbiBtb3N0IGltcGFjdGZ1bFxuZm9yIHlvdW5nIGNvbXBhbmllcyIsIA0KICAgICAgICAgICAgIGNleCA9IDAuNywgY29sID0gImRhcmtyZWQiKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgdGV4dChtYXhfdGltZSAqIDAuNSwgeV9wb3MsICJJbmZsYXRpb24gc2Vuc2l0aXZpdHlcbmluY3JlYXNlcyB3aXRoIGFnZSIsIA0KICAgICAgICAgICAgIGNleCA9IDAuNywgY29sID0gImRhcmtyZWQiKQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KfQ0KDQojIEhlbHBlciBmdW5jdGlvbiB0byBjcmVhdGUgZGV0YWlsZWQgcGxvdHMgd2l0aCBxdWFudGlsZS1iYXNlZCByZWZlcmVuY2UgdmFsdWVzDQpjcmVhdGVfZGV0YWlsZWRfcGxvdCA8LSBmdW5jdGlvbihtb2RlbCwgdmFyX25hbWUsIHZhcl90eXBlLCBtb2RlbF9uYW1lLCB2YXJfbGFiZWxzKSB7DQogICMgRXh0cmFjdCBjb2VmZmljaWVudHMNCiAgY29lZnMgPC0gY29lZihtb2RlbCkNCiAgbWFpbl9jb2VmIDwtIGNvZWZzW3Zhcl9uYW1lXQ0KICB0aW1lX2NvZWYgPC0gY29lZnNbcGFzdGUwKHZhcl9uYW1lLCAiOnR0IildDQogIA0KICAjIEdldCBkZXNjcmlwdGl2ZSBsYWJlbA0KICB2YXJfbGFiZWwgPC0gaWYodmFyX25hbWUgJWluJSBuYW1lcyh2YXJfbGFiZWxzKSkgdmFyX2xhYmVsc1tbdmFyX25hbWVdXSBlbHNlIHZhcl9uYW1lDQogIHZhcl9sYWJlbCA8LSBzdHJzcGxpdCh2YXJfbGFiZWwsICJcbiIpW1sxXV1bMV0gICMgSnVzdCB1c2UgZmlyc3QgbGluZQ0KICANCiAgIyBDcmVhdGUgc2VxdWVuY2Ugb2YgdGltZSBwb2ludHMNCiAgbWF4X3RpbWUgPC0gbWF4KGRhdGEkdHN0b3ApDQogIHRpbWVfcG9pbnRzIDwtIHNlcSgwLCBtYXhfdGltZSwgYnkgPSAwLjEpDQogIA0KICAjIEdldCB2YXJpYWJsZSBkaXN0cmlidXRpb24gZm9yIHJlZmVyZW5jZSB2YWx1ZXMNCiAgaWYodmFyX25hbWUgJWluJSBjb2xuYW1lcyhkYXRhKSkgew0KICAgIHEyNSA8LSBxdWFudGlsZShkYXRhW1t2YXJfbmFtZV1dLCAwLjI1LCBuYS5ybSA9IFRSVUUpDQogICAgcTUwIDwtIHF1YW50aWxlKGRhdGFbW3Zhcl9uYW1lXV0sIDAuNTAsIG5hLnJtID0gVFJVRSkNCiAgICBxNzUgPC0gcXVhbnRpbGUoZGF0YVtbdmFyX25hbWVdXSwgMC43NSwgbmEucm0gPSBUUlVFKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIGhhemFyZCByYXRpb3MgZm9yIGVhY2ggcmVmZXJlbmNlIHZhbHVlIG92ZXIgdGltZQ0KICAgIGhyX3EyNSA8LSBleHAoKG1haW5fY29lZiArIHRpbWVfY29lZiAqIHRpbWVfcG9pbnRzKSAqIHEyNSkNCiAgICBocl9xNTAgPC0gZXhwKChtYWluX2NvZWYgKyB0aW1lX2NvZWYgKiB0aW1lX3BvaW50cykgKiBxNTApDQogICAgaHJfcTc1IDwtIGV4cCgobWFpbl9jb2VmICsgdGltZV9jb2VmICogdGltZV9wb2ludHMpICogcTc1KQ0KICAgIA0KICAgICMgUGxvdCB0aXRsZQ0KICAgIGlmKHZhcl90eXBlID09ICJNYWNybyIpIHsNCiAgICAgIHRpdGxlIDwtIHBhc3RlKCJUaW1lLVZhcnlpbmcgRWNvbm9taWMgRWZmZWN0IG9mIiwgdmFyX2xhYmVsKQ0KICAgIH0gZWxzZSB7DQogICAgICB0aXRsZSA8LSBwYXN0ZSgiVGltZS1WYXJ5aW5nIEVmZmVjdCBvZiIsIHZhcl9sYWJlbCwgIm9uIiwgbW9kZWxfbmFtZSwgIlJpc2siKQ0KICAgIH0NCiAgICANCiAgICAjIENyZWF0ZSBwbG90DQogICAgcGxvdCh0aW1lX3BvaW50cywgaHJfcTUwLCB0eXBlID0gImwiLCBsd2QgPSAyLCBjb2wgPSAiYmx1ZSIsDQogICAgICAgICBtYWluID0gdGl0bGUsDQogICAgICAgICB4bGFiID0gIlRpbWUgKFllYXJzKSIsIHlsYWIgPSAiSGF6YXJkIFJhdGlvIiwNCiAgICAgICAgIHlsaW0gPSBjKG1pbigwLjUsIG1pbihocl9xMjUsIGhyX3E1MCwgaHJfcTc1LCBuYS5ybSA9IFRSVUUpKSwgDQogICAgICAgICAgICAgICAgIG1heCgyLCBtYXgoaHJfcTI1LCBocl9xNTAsIGhyX3E3NSwgbmEucm0gPSBUUlVFKSkpKQ0KICAgIA0KICAgICMgQWRkIGxpbmVzIGZvciBvdGhlciBxdWFudGlsZXMNCiAgICBsaW5lcyh0aW1lX3BvaW50cywgaHJfcTI1LCBsd2QgPSAyLCBjb2wgPSAiZ3JlZW4zIiwgbHR5ID0gMikNCiAgICBsaW5lcyh0aW1lX3BvaW50cywgaHJfcTc1LCBsd2QgPSAyLCBjb2wgPSAicmVkIiwgbHR5ID0gMikNCiAgICANCiAgICAjIEFkZCByZWZlcmVuY2UgbGluZSBhdCBIUiA9IDENCiAgICBhYmxpbmUoaCA9IDEsIGx0eSA9IDMsIGNvbCA9ICJkYXJrZ3JheSIpDQogICAgDQogICAgIyBBZGQgZ3JpZA0KICAgIGdyaWQoKQ0KICAgIA0KICAgICMgQWRkIGxlZ2VuZA0KICAgIGxlZ2VuZCgidG9wcmlnaHQiLCANCiAgICAgICAgICAgbGVnZW5kID0gYyhwYXN0ZSgiMjV0aCBwZXJjZW50aWxlICgiLCByb3VuZChxMjUsIDIpLCAiKSIsIHNlcCA9ICIiKSwNCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJNZWRpYW4gKCIsIHJvdW5kKHE1MCwgMiksICIpIiwgc2VwID0gIiIpLA0KICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIjc1dGggcGVyY2VudGlsZSAoIiwgcm91bmQocTc1LCAyKSwgIikiLCBzZXAgPSAiIikpLA0KICAgICAgICAgICBjb2wgPSBjKCJncmVlbjMiLCAiYmx1ZSIsICJyZWQiKSwgDQogICAgICAgICAgIGx0eSA9IGMoMiwgMSwgMiksIA0KICAgICAgICAgICBsd2QgPSAyLCANCiAgICAgICAgICAgYnR5ID0gIm4iKQ0KICAgIA0KICAgICMgQWRkIGNyb3Nzb3ZlciBwb2ludCBpZiBpdCBleGlzdHMNCiAgICBpZihzaWduKG1haW5fY29lZikgIT0gc2lnbih0aW1lX2NvZWYpICYmIHRpbWVfY29lZiAhPSAwKSB7DQogICAgICBjcm9zc292ZXIgPC0gLW1haW5fY29lZiAvIHRpbWVfY29lZg0KICAgICAgaWYoY3Jvc3NvdmVyID4gMCAmJiBjcm9zc292ZXIgPCBtYXgodGltZV9wb2ludHMpKSB7DQogICAgICAgIGFibGluZSh2ID0gY3Jvc3NvdmVyLCBsdHkgPSAyLCBjb2wgPSAicHVycGxlIikNCiAgICAgICAgdGV4dChjcm9zc292ZXIsIHBhcigidXNyIilbM10gKyAwLjEgKiBkaWZmKHBhcigidXNyIilbMzo0XSksIA0KICAgICAgICAgICAgIHBhc3RlKCJFZmZlY3QgcmV2ZXJzZXMgYXRcbiIsIHJvdW5kKGNyb3Nzb3ZlciwgMSksICJ5ZWFycyIpLA0KICAgICAgICAgICAgIGNvbCA9ICJwdXJwbGUiKQ0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICAjIEFkZCB2YXJpYWJsZS1zcGVjaWZpYyBpbnRlcnByZXRhdGlvbg0KICAgIGFkZF92YXJpYWJsZV9pbnRlcnByZXRhdGlvbih2YXJfbmFtZSwgdmFyX3R5cGUsIG1vZGVsX25hbWUsIHRpbWVfY29lZikNCiAgfQ0KfQ0KDQojIEhlbHBlciBmdW5jdGlvbiBmb3IgZW5oYW5jZWQgcGxvdCBvZiBtb3N0IGltcG9ydGFudCB2YXJpYWJsZQ0KY3JlYXRlX2VuaGFuY2VkX3Bsb3QgPC0gZnVuY3Rpb24obW9kZWwsIHZhcl9uYW1lLCB2YXJfdHlwZSwgbW9kZWxfbmFtZSwgdmFyX2xhYmVscykgew0KICAjIEV4dHJhY3QgY29lZmZpY2llbnRzDQogIGNvZWZzIDwtIGNvZWYobW9kZWwpDQogIG1haW5fY29lZiA8LSBjb2Vmc1t2YXJfbmFtZV0NCiAgdGltZV9jb2VmIDwtIGNvZWZzW3Bhc3RlMCh2YXJfbmFtZSwgIjp0dCIpXQ0KICANCiAgIyBHZXQgdmFyaWFibGUgcmFuZ2UgZnJvbSBkYXRhDQogIGlmKHZhcl9uYW1lICVpbiUgY29sbmFtZXMoZGF0YSkpIHsNCiAgICB2YXJfbWVhbiA8LSBtZWFuKGRhdGFbW3Zhcl9uYW1lXV0sIG5hLnJtID0gVFJVRSkNCiAgICB2YXJfc2QgPC0gc2QoZGF0YVtbdmFyX25hbWVdXSwgbmEucm0gPSBUUlVFKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIHJlcHJlc2VudGF0aXZlIHZhbHVlcw0KICAgIHZhcl9sb3cgPC0gbWF4KDAsIHZhcl9tZWFuIC0gdmFyX3NkKSAgIyBFbnN1cmUgbm9uLW5lZ2F0aXZlIGZvciByYXRpb3MNCiAgICB2YXJfaGlnaCA8LSB2YXJfbWVhbiArIHZhcl9zZA0KICAgIHZhcl9tZWRpYW4gPC0gbWVkaWFuKGRhdGFbW3Zhcl9uYW1lXV0sIG5hLnJtID0gVFJVRSkNCiAgICANCiAgICAjIFNldCB1cCB0aW1lIHNlcXVlbmNlDQogICAgbWF4X3RpbWUgPC0gbWF4KGRhdGEkdHN0b3ApDQogICAgdGltZXMgPC0gc2VxKDAsIG1heF90aW1lLCBsZW5ndGgub3V0ID0gMTAwKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIEhSIGZvciBkaWZmZXJlbnQgdmFyaWFibGUgdmFsdWVzDQogICAgaHJfbG93IDwtIGV4cCgobWFpbl9jb2VmICsgdGltZV9jb2VmICogdGltZXMpICogdmFyX2xvdykNCiAgICBocl9tZWRpYW4gPC0gZXhwKChtYWluX2NvZWYgKyB0aW1lX2NvZWYgKiB0aW1lcykgKiB2YXJfbWVkaWFuKQ0KICAgIGhyX2hpZ2ggPC0gZXhwKChtYWluX2NvZWYgKyB0aW1lX2NvZWYgKiB0aW1lcykgKiB2YXJfaGlnaCkNCiAgICANCiAgICAjIEdldCBkZXNjcmlwdGl2ZSBsYWJlbA0KICAgIHZhcl9sYWJlbCA8LSBpZih2YXJfbmFtZSAlaW4lIG5hbWVzKHZhcl9sYWJlbHMpKSB7DQogICAgICBzdHJzcGxpdCh2YXJfbGFiZWxzW1t2YXJfbmFtZV1dLCAiXG4iKVtbMV1dWzFdICAjIEp1c3QgdXNlIGZpcnN0IGxpbmUNCiAgICB9IGVsc2Ugew0KICAgICAgdmFyX25hbWUNCiAgICB9DQogICAgDQogICAgIyBDcmVhdGUgZW5oYW5jZWQgcGxvdA0KICAgIHBsb3QodGltZXMsIGhyX21lZGlhbiwgdHlwZSA9ICJsIiwgbHdkID0gMiwgY29sID0gImJsdWUiLA0KICAgICAgICAgbWFpbiA9IHBhc3RlKCJEZXRhaWxlZCBUaW1lLVZhcnlpbmcgRWZmZWN0IG9mIiwgdmFyX2xhYmVsLCAib24iLCBtb2RlbF9uYW1lLCAiUmlzayIpLA0KICAgICAgICAgeGxhYiA9ICJUaW1lIChZZWFycykiLCB5bGFiID0gIkhhemFyZCBSYXRpbyIsDQogICAgICAgICB5bGltID0gYyhtaW4oMC41LCBtaW4oaHJfbG93LCBocl9tZWRpYW4sIGhyX2hpZ2gsIG5hLnJtID0gVFJVRSkpLCANCiAgICAgICAgICAgICAgICAgbWF4KDIsIG1heChocl9sb3csIGhyX21lZGlhbiwgaHJfaGlnaCwgbmEucm0gPSBUUlVFKSkpKQ0KICAgIA0KICAgICMgQWRkIGxpbmVzIGZvciBsb3cgYW5kIGhpZ2ggdmFsdWVzDQogICAgbGluZXModGltZXMsIGhyX2xvdywgbHdkID0gMiwgY29sID0gImdyZWVuMyIsIGx0eSA9IDIpDQogICAgbGluZXModGltZXMsIGhyX2hpZ2gsIGx3ZCA9IDIsIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQ0KICAgIA0KICAgICMgQWRkIHJlZmVyZW5jZSBsaW5lIGF0IEhSID0gMQ0KICAgIGFibGluZShoID0gMSwgbHR5ID0gMywgY29sID0gImRhcmtncmF5IikNCiAgICANCiAgICAjIEFkZCBncmlkDQogICAgZ3JpZCgpDQogICAgDQogICAgIyBBZGQgbGVnZW5kDQogICAgbGVnZW5kKCJ0b3ByaWdodCIsIA0KICAgICAgICAgICBsZWdlbmQgPSBjKHBhc3RlKCJMb3ciLCB2YXJfbmFtZSwgIigiLCByb3VuZCh2YXJfbG93LCAyKSwgIikiKSwNCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJNZWRpYW4iLCB2YXJfbmFtZSwgIigiLCByb3VuZCh2YXJfbWVkaWFuLCAyKSwgIikiKSwNCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCJIaWdoIiwgdmFyX25hbWUsICIoIiwgcm91bmQodmFyX2hpZ2gsIDIpLCAiKSIpKSwNCiAgICAgICAgICAgY29sID0gYygiZ3JlZW4zIiwgImJsdWUiLCAicmVkIiksIA0KICAgICAgICAgICBsdHkgPSBjKDIsIDEsIDIpLCANCiAgICAgICAgICAgbHdkID0gMiwgDQogICAgICAgICAgIGJ0eSA9ICJuIikNCiAgICANCiAgICAjIEFkZCBjcm9zc292ZXIgcG9pbnQgaWYgYW55DQogICAgaWYoc2lnbihtYWluX2NvZWYpICE9IHNpZ24odGltZV9jb2VmKSAmJiB0aW1lX2NvZWYgIT0gMCkgew0KICAgICAgY3Jvc3NvdmVyIDwtIC1tYWluX2NvZWYgLyB0aW1lX2NvZWYNCiAgICAgIGlmKGNyb3Nzb3ZlciA+IDAgJiYgY3Jvc3NvdmVyIDwgbWF4KHRpbWVzKSkgew0KICAgICAgICBhYmxpbmUodiA9IGNyb3Nzb3ZlciwgbHR5ID0gMiwgY29sID0gInB1cnBsZSIpDQogICAgICAgIHRleHQoY3Jvc3NvdmVyLCBwYXIoInVzciIpWzNdICsgMC4xICogZGlmZihwYXIoInVzciIpWzM6NF0pLCANCiAgICAgICAgICAgICBwYXN0ZSgiRWZmZWN0IHJldmVyc2VzIGF0XG4iLCByb3VuZChjcm9zc292ZXIsIDEpLCAieWVhcnMiKSwNCiAgICAgICAgICAgICBjb2wgPSAicHVycGxlIikNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBBZGQgZGV0YWlsZWQgYnVzaW5lc3MgaW50ZXJwcmV0YXRpb24NCiAgICBhZGRfZGV0YWlsZWRfaW50ZXJwcmV0YXRpb24odmFyX25hbWUsIHZhcl90eXBlLCBtb2RlbF9uYW1lLCB0aW1lX2NvZWYpDQogIH0NCn0NCg0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gYWRkIHZhcmlhYmxlLXNwZWNpZmljIGludGVycHJldGF0aW9uDQphZGRfdmFyaWFibGVfaW50ZXJwcmV0YXRpb24gPC0gZnVuY3Rpb24odmFyX25hbWUsIHZhcl90eXBlLCBtb2RlbF9uYW1lLCB0aW1lX2NvZWYpIHsNCiAgaWYodmFyX3R5cGUgPT0gIk1hY3JvIikgew0KICAgIGlmKHZhcl9uYW1lID09ICJnZHBfZ3Jvd3RoIikgew0KICAgICAgaWYodGltZV9jb2VmID4gMCkgew0KICAgICAgICBtdGV4dCgiRWNvbm9taWMgZ3Jvd3RoIGVmZmVjdCBzdHJlbmd0aGVucyBvdmVyIHRpbWUiLCANCiAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIG10ZXh0KCJFY29ub21pYyBncm93dGggZWZmZWN0IHdlYWtlbnMgb3ZlciB0aW1lIiwgDQogICAgICAgICAgICAgIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpDQogICAgICB9DQogICAgfSBlbHNlIGlmKHZhcl9uYW1lID09ICJ1bmVtcGxveWVtZW50Iikgew0KICAgICAgaWYodGltZV9jb2VmID4gMCkgew0KICAgICAgICBtdGV4dCgiTGFib3IgbWFya2V0IGVmZmVjdCBzdHJlbmd0aGVucyBvdmVyIHRpbWUiLCANCiAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIG10ZXh0KCJMYWJvciBtYXJrZXQgZWZmZWN0IHdlYWtlbnMgb3ZlciB0aW1lIiwgDQogICAgICAgICAgICAgIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpDQogICAgICB9DQogICAgfSBlbHNlIGlmKHZhcl9uYW1lID09ICJnZHBfZGVmbGF0b3IiKSB7DQogICAgICBpZih0aW1lX2NvZWYgPiAwKSB7DQogICAgICAgIG10ZXh0KCJJbmZsYXRpb24gZWZmZWN0IHN0cmVuZ3RoZW5zIG92ZXIgdGltZSIsIA0KICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC44KQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgbXRleHQoIkluZmxhdGlvbiBlZmZlY3Qgd2Vha2VucyBvdmVyIHRpbWUiLCANCiAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICAgIH0NCiAgICB9DQogIH0gZWxzZSB7ICAjIEZpbmFuY2lhbCB2YXJpYWJsZXMNCiAgICBpZih2YXJfbmFtZSA9PSAiTFRNVEEiIHx8IHZhcl9uYW1lID09ICJkZWJ0X3JhdGlvIikgew0KICAgICAgaWYobW9kZWxfbmFtZSA9PSAiQmFua3J1cHRjeSIpIHsNCiAgICAgICAgaWYodGltZV9jb2VmID4gMCkgew0KICAgICAgICAgIG10ZXh0KCJMZXZlcmFnZSBiZWNvbWVzIGluY3JlYXNpbmdseSByaXNreSBhcyBjb21wYW5pZXMgbWF0dXJlIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBtdGV4dCgiTGV2ZXJhZ2UgaXMgbW9zdCBkYW5nZXJvdXMgZm9yIHlvdW5nIGNvbXBhbmllcyIsIA0KICAgICAgICAgICAgICAgIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpIA0KICAgICAgICB9DQogICAgICB9DQogICAgfSBlbHNlIGlmKHZhcl9uYW1lID09ICJOSU1UQSIpIHsNCiAgICAgIGlmKG1vZGVsX25hbWUgPT0gIkJhbmtydXB0Y3kiKSB7DQogICAgICAgIGlmKHRpbWVfY29lZiA+IDApIHsNCiAgICAgICAgICBtdGV4dCgiUHJvZml0YWJpbGl0eSBiZWNvbWVzIG1vcmUgcHJvdGVjdGl2ZSB3aXRoIGFnZSIsIA0KICAgICAgICAgICAgICAgIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgbXRleHQoIlByb2ZpdGFiaWxpdHkgaXMgbW9zdCBwcm90ZWN0aXZlIGZvciB5b3VuZyBjb21wYW5pZXMiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC44KQ0KICAgICAgICB9DQogICAgICB9DQogICAgfSBlbHNlIGlmKHZhcl9uYW1lID09ICJDQVNITVRBIikgew0KICAgICAgaWYobW9kZWxfbmFtZSA9PSAiQmFua3J1cHRjeSIpIHsNCiAgICAgICAgaWYodGltZV9jb2VmID4gMCkgew0KICAgICAgICAgIG10ZXh0KCJDYXNoIGhvbGRpbmdzIGJlY29tZSBtb3JlIGltcG9ydGFudCB3aXRoIGNvbXBhbnkgYWdlIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBtdGV4dCgiQ2FzaCBpcyBtb3N0IGNyaXRpY2FsIGZvciB5b3VuZyBjb21wYW55IHN1cnZpdmFsIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIGlmKG1vZGVsX25hbWUgPT0gIkFjcXVpc2l0aW9uIikgew0KICAgICAgICBpZih0aW1lX2NvZWYgPiAwKSB7DQogICAgICAgICAgbXRleHQoIkNhc2ggYmVjb21lcyBtb3JlIGF0dHJhY3RpdmUgdG8gYWNxdWlyZXJzIGFzIGNvbXBhbmllcyBtYXR1cmUiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC44KQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIG10ZXh0KCJDYXNoIG1ha2VzIHlvdW5nIGNvbXBhbmllcyBtb3JlIGF0dHJhY3RpdmUgYWNxdWlzaXRpb24gdGFyZ2V0cyIsIA0KICAgICAgICAgICAgICAgIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9DQogIH0NCn0NCg0KIyBIZWxwZXIgZnVuY3Rpb24gdG8gYWRkIGRldGFpbGVkIGludGVycHJldGF0aW9uIGZvciB0aGUgZW5oYW5jZWQgcGxvdA0KYWRkX2RldGFpbGVkX2ludGVycHJldGF0aW9uIDwtIGZ1bmN0aW9uKHZhcl9uYW1lLCB2YXJfdHlwZSwgbW9kZWxfbmFtZSwgdGltZV9jb2VmKSB7DQogIGlmKHZhcl90eXBlID09ICJNYWNybyIpIHsNCiAgICBpZih2YXJfbmFtZSA9PSAiZ2RwX2dyb3d0aCIpIHsNCiAgICAgIGlmKG1vZGVsX25hbWUgPT0gIkJhbmtydXB0Y3kiKSB7DQogICAgICAgIGlmKHRpbWVfY29lZiA+IDApIHsNCiAgICAgICAgICBtdGV4dCgiSU5TSUdIVDogTWF0dXJlIGZpcm1zIGJlY29tZSBpbmNyZWFzaW5nbHkgc2Vuc2l0aXZlIHRvIGVjb25vbWljIGNvbmRpdGlvbnMiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC45LCBjb2wgPSAiZGFya2JsdWUiKQ0KICAgICAgICAgIG10ZXh0KCJGaXJtcyBtYXkgZGV2ZWxvcCBncmVhdGVyIGVjb25vbWljIGV4cG9zdXJlIGFzIHRoZXkgYWdlIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAxLjUsIGNleCA9IDAuOCkNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBtdGV4dCgiSU5TSUdIVDogWW91bmcgZmlybXMgYXJlIG1vcmUgdnVsbmVyYWJsZSB0byBlY29ub21pYyBkb3dudHVybnMiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC45LCBjb2wgPSAiZGFya2JsdWUiKQ0KICAgICAgICAgIG10ZXh0KCJOZXcgYnVzaW5lc3NlcyBoYXZlIGxlc3MgYnVmZmVyIGFnYWluc3QgcG9vciBlY29ub21pYyBjb25kaXRpb25zIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAxLjUsIGNleCA9IDAuOCkNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsgICMgQWNxdWlzaXRpb24NCiAgICAgICAgaWYodGltZV9jb2VmID4gMCkgew0KICAgICAgICAgIG10ZXh0KCJJTlNJR0hUOiBFY29ub21pYyBncm93dGggaW5jcmVhc2luZ2x5IGRyaXZlcyBhY3F1aXNpdGlvbiBhY3Rpdml0eSBmb3IgbWF0dXJlIGZpcm1zIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOSwgY29sID0gImRhcmtibHVlIikNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBtdGV4dCgiSU5TSUdIVDogWW91bmcgY29tcGFuaWVzIGFyZSBtb3JlIGxpa2VseSBhY3F1aXNpdGlvbiB0YXJnZXRzIGR1cmluZyBlY29ub21pYyB1cHN3aW5ncyIsIA0KICAgICAgICAgICAgICAgIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjksIGNvbCA9ICJkYXJrYmx1ZSIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9IGVsc2UgaWYodmFyX25hbWUgPT0gInVuZW1wbG95ZW1lbnQiKSB7DQogICAgICBpZihtb2RlbF9uYW1lID09ICJCYW5rcnVwdGN5Iikgew0KICAgICAgICBpZih0aW1lX2NvZWYgPiAwKSB7DQogICAgICAgICAgbXRleHQoIklOU0lHSFQ6IExhYm9yIG1hcmtldCBjb25kaXRpb25zIGJlY29tZSBtb3JlIGNyaXRpY2FsIGZvciBtYXR1cmUgZmlybXMiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC45LCBjb2wgPSAiZGFya2JsdWUiKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIG10ZXh0KCJJTlNJR0hUOiBIaWdoIHVuZW1wbG95bWVudCBtb3N0IHRocmVhdGVucyB5b3VuZyBmaXJtcycgc3Vydml2YWwiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC45LCBjb2wgPSAiZGFya2JsdWUiKQ0KICAgICAgICB9DQogICAgICB9DQogICAgfQ0KICB9IGVsc2UgeyAgIyBGaW5hbmNpYWwgdmFyaWFibGVzDQogICAgaWYodmFyX25hbWUgPT0gIkxUTVRBIiB8fCB2YXJfbmFtZSA9PSAiZGVidF9yYXRpbyIpIHsNCiAgICAgIGlmKG1vZGVsX25hbWUgPT0gIkJhbmtydXB0Y3kiKSB7DQogICAgICAgIGlmKHRpbWVfY29lZiA+IDApIHsNCiAgICAgICAgICBtdGV4dCgiSU5TSUdIVDogRGVidCBidXJkZW4gYmVjb21lcyBpbmNyZWFzaW5nbHkgcHJvYmxlbWF0aWMgYXMgZmlybXMgbWF0dXJlIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOSwgY29sID0gImRhcmtibHVlIikNCiAgICAgICAgICBtdGV4dCgiRGVidCBzdXN0YWluYWJpbGl0eSBjb25jZXJucyBncm93IG92ZXIgY29tcGFueSBsaWZlY3ljbGUiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDEuNSwgY2V4ID0gMC44KQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIG10ZXh0KCJJTlNJR0hUOiBFYXJseS1zdGFnZSBsZXZlcmFnZSBwb3NlcyBncmVhdGVzdCBiYW5rcnVwdGN5IHJpc2siLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC45LCBjb2wgPSAiZGFya2JsdWUiKQ0KICAgICAgICAgIG10ZXh0KCJZb3VuZyBmaXJtcyBoYXZlIGxlc3MgY2FwYWNpdHkgdG8gc2VydmljZSBkZWJ0IG9ibGlnYXRpb25zIiwgDQogICAgICAgICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAxLjUsIGNleCA9IDAuOCkNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0gZWxzZSBpZih2YXJfbmFtZSA9PSAiTklNVEEiIHx8IHZhcl9uYW1lID09ICJ6X3Njb3JlIikgew0KICAgICAgaWYobW9kZWxfbmFtZSA9PSAiQmFua3J1cHRjeSIpIHsNCiAgICAgICAgaWYodGltZV9jb2VmID4gMCkgew0KICAgICAgICAgIG10ZXh0KCJJTlNJR0hUOiBGaW5hbmNpYWwgaGVhbHRoIG1ldHJpY3MgZ2FpbiBwcmVkaWN0aXZlIHBvd2VyIG92ZXIgY29tcGFueSBsaWZlY3ljbGUiLCANCiAgICAgICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC45LCBjb2wgPSAiZGFya2JsdWUiKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIG10ZXh0KCJJTlNJR0hUOiBTdHJvbmcgZmluYW5jaWFscyBtb3N0IGNyaXRpY2FsIGZvciBzdXJ2aXZhbCBpbiBlYXJseSB5ZWFycyIsIA0KICAgICAgICAgICAgICAgIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjksIGNvbCA9ICJkYXJrYmx1ZSIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9DQogIH0NCn0NCg0KIyBFeGVjdXRlIHZpc3VhbGl6YXRpb24gZm9yIGJhbmtydXB0Y3kgbW9kZWwNCnZpc3VhbGl6ZV90aW1lX3ZhcnlpbmdfZWZmZWN0cyhmaW5hbF9iYW5rcnVwdGN5X21vZGVsLCAiQmFua3J1cHRjeSIpDQoNCiMgRXhlY3V0ZSB2aXN1YWxpemF0aW9uIGZvciBhY3F1aXNpdGlvbiBtb2RlbA0KdmlzdWFsaXplX3RpbWVfdmFyeWluZ19lZmZlY3RzKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiQWNxdWlzaXRpb24iKQ0KDQojIFNhdmUgZmluYWwgbW9kZWwgaW5mb3JtYXRpb24gd2l0aCB0aW1lLXZhcnlpbmcgZWZmZWN0cyBkYXRhDQpmaW5hbF9tb2RlbHNfd2l0aF90aW1lX2VmZmVjdHMgPC0gbGlzdCgNCiAgYmFua3J1cHRjeSA9IGxpc3QoDQogICAgbW9kZWwgPSBmaW5hbF9iYW5rcnVwdGN5X21vZGVsLA0KICAgIHRpbWVfdGVybXMgPSBncmVwKCI6dHQiLCBuYW1lcyhjb2VmKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpKSwgdmFsdWUgPSBUUlVFKSwNCiAgICBmaW5fdGltZV92YXJzID0gaW50ZXJzZWN0KA0KICAgICAgc2FwcGx5KHN0cnNwbGl0KGdyZXAoIjp0dCIsIG5hbWVzKGNvZWYoZmluYWxfYmFua3J1cHRjeV9tb2RlbCkpLCB2YWx1ZSA9IFRSVUUpLCAiOiIpLCBgW2AsIDEpLA0KICAgICAgZmluYW5jaWFsX2NvdmFyaWF0ZXMNCiAgICApLA0KICAgIG1hY3JvX3RpbWVfdmFycyA9IGludGVyc2VjdCgNCiAgICAgIHNhcHBseShzdHJzcGxpdChncmVwKCI6dHQiLCBuYW1lcyhjb2VmKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpKSwgdmFsdWUgPSBUUlVFKSwgIjoiKSwgYFtgLCAxKSwNCiAgICAgIG1hY3JvX2NvdmFyaWF0ZXMNCiAgICApLA0KICAgIHBoX3Rlc3QgPSBpZihleGlzdHMoInBoX3Rlc3RfZmluYWxfYmFua3J1cHRjeSIpKSBwaF90ZXN0X2ZpbmFsX2JhbmtydXB0Y3kgZWxzZSBOVUxMDQogICksDQogIGFjcXVpc2l0aW9uID0gbGlzdCgNCiAgICBtb2RlbCA9IGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLA0KICAgIHRpbWVfdGVybXMgPSBncmVwKCI6dHQiLCBuYW1lcyhjb2VmKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsKSksIHZhbHVlID0gVFJVRSksDQogICAgZmluX3RpbWVfdmFycyA9IGludGVyc2VjdCgNCiAgICAgIHNhcHBseShzdHJzcGxpdChncmVwKCI6dHQiLCBuYW1lcyhjb2VmKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsKSksIHZhbHVlID0gVFJVRSksICI6IiksIGBbYCwgMSksDQogICAgICBmaW5hbmNpYWxfY292YXJpYXRlcw0KICAgICksDQogICAgbWFjcm9fdGltZV92YXJzID0gaW50ZXJzZWN0KA0KICAgICAgc2FwcGx5KHN0cnNwbGl0KGdyZXAoIjp0dCIsIG5hbWVzKGNvZWYoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpKSwgdmFsdWUgPSBUUlVFKSwgIjoiKSwgYFtgLCAxKSwNCiAgICAgIG1hY3JvX2NvdmFyaWF0ZXMNCiAgICApLA0KICAgIHBoX3Rlc3QgPSBpZihleGlzdHMoInBoX3Rlc3RfZmluYWxfYWNxdWlzaXRpb24iKSkgcGhfdGVzdF9maW5hbF9hY3F1aXNpdGlvbiBlbHNlIE5VTEwNCiAgKQ0KKQ0KYGBgDQoNCg0KTm93IHdlIHdpbGwgY3JlYXRlIGEgZnVuY3Rpb24gdG8gYW5hbHl6ZSB0aGUgaGF6YXJkIHJhdGlvcyBhbmQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSBmaW5hbCBtb2RlbHMsIGluY2x1ZGluZyB0aW1lLXZhcnlpbmcgZWZmZWN0cy4gV2UgZG8gdGhpcyBieSBleHRyYWN0aW5nIHRoZSBjb2VmZmljaWVudHMgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZyb20gdGhlIG1vZGVscywgYW5kIHRoZW4gZm9ybWF0dGluZyB0aGVtIGludG8gYSByZWFkYWJsZSB0YWJsZS4gV2Ugd2lsbCBhbHNvIGNhdGVnb3JpemUgdGhlIHZhcmlhYmxlcyBpbnRvIGZpbmFuY2lhbCBhbmQgbWFjcm9lY29ub21pYyBjYXRlZ29yaWVzIGZvciBiZXR0ZXIgaW50ZXJwcmV0YXRpb24uDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDExLjE6IEhhemFyZCBSYXRpb3MgYW5kIENvbmZpZGVuY2UgSW50ZXJ2YWxzIChJbXByb3ZlZCkNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTEuMTogSEFaQVJEIFJBVElPUyBXSVRIIENPTkZJREVOQ0UgSU5URVJWQUxTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgYW5kIGRpc3BsYXkgaGF6YXJkIHJhdGlvcyBpbiBhIG1lYW5pbmdmdWwgd2F5DQphbmFseXplX2hhemFyZF9yYXRpb3MgPC0gZnVuY3Rpb24obW9kZWwsIG1vZGVsX25hbWUpIHsNCiAgIyBHZXQgY29lZmZpY2llbnQgZXN0aW1hdGVzIGFuZCBjb25maWRlbmNlIGludGVydmFscw0KICBjb2VmcyA8LSBzdW1tYXJ5KG1vZGVsKSRjb2VmZmljaWVudHMNCiAgY29uZl9pbnQgPC0gY29uZmludChtb2RlbCkNCiAgDQogICMgQ29tYmluZSBpbnRvIGEgc2luZ2xlIG1hdHJpeCBhbmQgY29udmVydCB0byBoYXphcmQgcmF0aW9zDQogIGhyX21hdHJpeCA8LSBkYXRhLmZyYW1lKA0KICAgICJWYXJpYWJsZSIgPSByb3duYW1lcyhjb2VmcyksDQogICAgIkNvZWZmaWNpZW50IiA9IGNvZWZzWywgImNvZWYiXSwNCiAgICAiSGF6YXJkX1JhdGlvIiA9IGV4cChjb2Vmc1ssICJjb2VmIl0pLA0KICAgICJMb3dlcl9DSSIgPSBleHAoY29uZl9pbnRbLCAxXSksDQogICAgIlVwcGVyX0NJIiA9IGV4cChjb25mX2ludFssIDJdKSwNCiAgICAiUF92YWx1ZSIgPSBjb2Vmc1ssICJQcig+fHp8KSJdLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIA0KICAjIFNlcGFyYXRlIG1haW4gZWZmZWN0cyBmcm9tIHRpbWUgaW50ZXJhY3Rpb25zDQogIG1haW5fZWZmZWN0cyA8LSBocl9tYXRyaXhbIWdyZXBsKCI6dHQiLCBocl9tYXRyaXgkVmFyaWFibGUpLCBdDQogIHRpbWVfZWZmZWN0cyA8LSBocl9tYXRyaXhbZ3JlcGwoIjp0dCIsIGhyX21hdHJpeCRWYXJpYWJsZSksIF0NCiAgDQogICMgQWRkIHZhcmlhYmxlIHR5cGUgY2xhc3NpZmljYXRpb24NCiAgbWFpbl9lZmZlY3RzJFR5cGUgPC0gIk90aGVyIg0KICANCiAgIyBJZGVudGlmeSBmaW5hbmNpYWwgdmFyaWFibGVzIGJ5IGNhdGVnb3J5DQogIGZpbmFuY2lhbF9jYXRlZ29yaWVzIDwtIGxpc3QoDQogICAgIlNvbHZlbmN5IiA9IGMoIkxUTVRBIiwgImRlYnRfcmF0aW8iLCAiZGVidF9zZXJ2aWNlIiwgInpfc2NvcmUiKSwNCiAgICAiTGlxdWlkaXR5IiA9IGMoIkNBU0hNVEEiLCAiY3VycmVudF9yYXRpbyIsICJjYXNoX3RvX2Fzc2V0cyIsICJ3Y19yYXRpbyIsICJxdWlja19yYXRpbyIpLA0KICAgICJQcm9maXRhYmlsaXR5IiA9IGMoIk5JTVRBIiwgImViaXRfbWFyZ2luIiwgImdwX21hcmdpbiIsICJhc3NldF90dXJub3ZlciIsICJlYml0X2dyb3d0aCIpLA0KICAgICJNYXJrZXQiID0gYygiUFJJQ0UiLCAiTUJFIiwgIlJTSVpFIiwgImxfbWt2YWx0IiksDQogICAgIk90aGVyX0ZpbmFuY2lhbCIgPSBjKCJyZWNlaXZhYmxlc190dXJub3ZlciIsICJpbnRhbmdpYmlsaXR5IiwgIkVCSVRfVk9MXzNZIiwgImxfYXQiKQ0KICApDQogIA0KICAjIEFzc2lnbiBmaW5hbmNpYWwgY2F0ZWdvcmllcw0KICBmb3IoY2F0ZWdvcnlfbmFtZSBpbiBuYW1lcyhmaW5hbmNpYWxfY2F0ZWdvcmllcykpIHsNCiAgICBtYWluX2VmZmVjdHMkVHlwZVttYWluX2VmZmVjdHMkVmFyaWFibGUgJWluJSBmaW5hbmNpYWxfY2F0ZWdvcmllc1tbY2F0ZWdvcnlfbmFtZV1dXSA8LSBjYXRlZ29yeV9uYW1lDQogIH0NCiAgDQogICMgSWRlbnRpZnkgbWFjcm8gdmFyaWFibGVzDQogIG1hY3JvX2NhdGVnb3JpZXMgPC0gbGlzdCgNCiAgICAiRWNvbm9taWNfR3Jvd3RoIiA9IGMoImdkcF9ncm93dGgiKSwNCiAgICAiSW5mbGF0aW9uIiA9IGMoImdkcF9kZWZsYXRvciIpLA0KICAgICJMYWJvcl9NYXJrZXQiID0gYygidW5lbXBsb3llbWVudCIpDQogICkNCiAgDQogICMgQXNzaWduIG1hY3JvIGNhdGVnb3JpZXMNCiAgZm9yKGNhdGVnb3J5X25hbWUgaW4gbmFtZXMobWFjcm9fY2F0ZWdvcmllcykpIHsNCiAgICBtYWluX2VmZmVjdHMkVHlwZVttYWluX2VmZmVjdHMkVmFyaWFibGUgJWluJSBtYWNyb19jYXRlZ29yaWVzW1tjYXRlZ29yeV9uYW1lXV1dIDwtIGNhdGVnb3J5X25hbWUNCiAgfQ0KICANCiAgIyBQcmludCByZXN1bHRzIGJ5IGNhdGVnb3J5IC0gRmluYW5jaWFsIHZhcmlhYmxlcyBmaXJzdA0KICBjYXQoIlxuPT09IEhBWkFSRCBSQVRJT1MgRk9SIiwgdG91cHBlcihtb2RlbF9uYW1lKSwgIk1PREVMID09PVxuXG4iKQ0KICANCiAgY2F0KCJGSU5BTkNJQUwgVkFSSUFCTEVTOlxuIikNCiAgY2F0KCI9PT09PT09PT09PT09PT09PT09PVxuXG4iKQ0KICANCiAgZmluYW5jaWFsX2NhdGVnb3JpZXNfcHJpbnQgPC0gYygiU29sdmVuY3kiLCAiTGlxdWlkaXR5IiwgIlByb2ZpdGFiaWxpdHkiLCAiTWFya2V0IiwgIk90aGVyX0ZpbmFuY2lhbCIpDQogIA0KICBmb3IoY2F0ZWdvcnlfbmFtZSBpbiBmaW5hbmNpYWxfY2F0ZWdvcmllc19wcmludCkgew0KICAgIGNhdChnc3ViKCJfIiwgIiAiLCBjYXRlZ29yeV9uYW1lKSwgIk1lYXN1cmVzOlxuIikNCiAgICBjYXQocGFzdGUocmVwKCItIiwgbmNoYXIoZ3N1YigiXyIsICIgIiwgY2F0ZWdvcnlfbmFtZSkpICsgMTApLCBjb2xsYXBzZSA9ICIiKSwgIlxuIikNCiAgICANCiAgICAjIEdldCB2YXJpYWJsZXMgaW4gdGhpcyBjYXRlZ29yeQ0KICAgIGNhdGVnb3J5X3Jvd3MgPC0gbWFpbl9lZmZlY3RzW21haW5fZWZmZWN0cyRUeXBlID09IGNhdGVnb3J5X25hbWUsIF0NCiAgICANCiAgICBpZihucm93KGNhdGVnb3J5X3Jvd3MpID09IDApIHsNCiAgICAgIGNhdCgiTm8gdmFyaWFibGVzIGZyb20gdGhpcyBjYXRlZ29yeSBpbiB0aGUgbW9kZWwuXG5cbiIpDQogICAgICBuZXh0DQogICAgfQ0KICAgIA0KICAgICMgU29ydCBieSBwLXZhbHVlIGZvciBwcmVzZW50YXRpb24NCiAgICBjYXRlZ29yeV9yb3dzIDwtIGNhdGVnb3J5X3Jvd3Nbb3JkZXIoY2F0ZWdvcnlfcm93cyRQX3ZhbHVlKSwgXQ0KICAgIA0KICAgICMgUHJpbnQgZWFjaCB2YXJpYWJsZSBpbiB0aGlzIGNhdGVnb3J5DQogICAgZm9yKGkgaW4gMTpucm93KGNhdGVnb3J5X3Jvd3MpKSB7DQogICAgICB2YXIgPC0gY2F0ZWdvcnlfcm93cyRWYXJpYWJsZVtpXQ0KICAgICAgaHIgPC0gY2F0ZWdvcnlfcm93cyRIYXphcmRfUmF0aW9baV0NCiAgICAgIHBfdmFsIDwtIGNhdGVnb3J5X3Jvd3MkUF92YWx1ZVtpXQ0KICAgICAgY2lfbG93ZXIgPC0gY2F0ZWdvcnlfcm93cyRMb3dlcl9DSVtpXQ0KICAgICAgY2lfdXBwZXIgPC0gY2F0ZWdvcnlfcm93cyRVcHBlcl9DSVtpXQ0KICAgICAgDQogICAgICAjIERldGVybWluZSBlZmZlY3QgZGlyZWN0aW9uIGFuZCBzaWduaWZpY2FuY2UNCiAgICAgIGlmKHBfdmFsIDwgMC4wMSkgew0KICAgICAgICBzaWduaWZpY2FuY2UgPC0gInN0cm9uZ2x5IHNpZ25pZmljYW50bHkiDQogICAgICB9IGVsc2UgaWYocF92YWwgPCAwLjA1KSB7DQogICAgICAgIHNpZ25pZmljYW5jZSA8LSAic2lnbmlmaWNhbnRseSINCiAgICAgIH0gZWxzZSBpZihwX3ZhbCA8IDAuMSkgew0KICAgICAgICBzaWduaWZpY2FuY2UgPC0gIm1hcmdpbmFsbHkgc2lnbmlmaWNhbnRseSINCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHNpZ25pZmljYW5jZSA8LSAibm90IHNpZ25pZmljYW50bHkiDQogICAgICB9DQogICAgICANCiAgICAgIGRpcmVjdGlvbiA8LSBpZmVsc2UoaHIgPiAxLCAiaW5jcmVhc2VzIiwgImRlY3JlYXNlcyIpDQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2UgY2hhbmdlDQogICAgICBpZihociA8IDEpIHsNCiAgICAgICAgcGN0X2NoYW5nZSA8LSAoMSAtIGhyKSAqIDEwMA0KICAgICAgICBlZmZlY3QgPC0gcGFzdGUwKHNpZ25pZmljYW5jZSwgIiBkZWNyZWFzZXMgIiwgbW9kZWxfbmFtZSwgIiByaXNrIGJ5ICIsIHJvdW5kKHBjdF9jaGFuZ2UsIDEpLCAiJSIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBwY3RfY2hhbmdlIDwtIChociAtIDEpICogMTAwDQogICAgICAgIGVmZmVjdCA8LSBwYXN0ZTAoc2lnbmlmaWNhbmNlLCAiIGluY3JlYXNlcyAiLCBtb2RlbF9uYW1lLCAiIHJpc2sgYnkgIiwgcm91bmQocGN0X2NoYW5nZSwgMSksICIlIikNCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBDaGVjayBmb3IgdGltZSBpbnRlcmFjdGlvbg0KICAgICAgaGFzX3RpbWVfaW50IDwtIHBhc3RlMCh2YXIsICI6dHQiKSAlaW4lIHRpbWVfZWZmZWN0cyRWYXJpYWJsZQ0KICAgICAgdGltZV9ub3RlIDwtICIiDQogICAgICANCiAgICAgIGlmKGhhc190aW1lX2ludCkgew0KICAgICAgICB0aW1lX3JvdyA8LSB0aW1lX2VmZmVjdHNbdGltZV9lZmZlY3RzJFZhcmlhYmxlID09IHBhc3RlMCh2YXIsICI6dHQiKSwgXQ0KICAgICAgICB0aW1lX2hyIDwtIHRpbWVfcm93JEhhemFyZF9SYXRpbw0KICAgICAgICANCiAgICAgICAgaWYodGltZV9ociA8IDEgJiYgaHIgPiAxKSB7DQogICAgICAgICAgdGltZV9ub3RlIDwtICIgKGVmZmVjdCB3ZWFrZW5zIG92ZXIgdGltZSkiDQogICAgICAgIH0gZWxzZSBpZih0aW1lX2hyID4gMSAmJiBociA8IDEpIHsNCiAgICAgICAgICB0aW1lX25vdGUgPC0gIiAoZWZmZWN0IHdlYWtlbnMgb3ZlciB0aW1lKSINCiAgICAgICAgfSBlbHNlIGlmKHRpbWVfaHIgPiAxICYmIGhyID4gMSkgew0KICAgICAgICAgIHRpbWVfbm90ZSA8LSAiIChlZmZlY3Qgc3RyZW5ndGhlbnMgb3ZlciB0aW1lKSINCiAgICAgICAgfSBlbHNlIGlmKHRpbWVfaHIgPCAxICYmIGhyIDwgMSkgew0KICAgICAgICAgIHRpbWVfbm90ZSA8LSAiIChlZmZlY3Qgc3RyZW5ndGhlbnMgb3ZlciB0aW1lKSINCiAgICAgICAgfQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIFByaW50IGZvcm1hdHRlZCByZXN1bHQNCiAgICAgIGNhdChzcHJpbnRmKCIlLTE1czogSFIgPSAlNS4yZiAoOTUlJSBDSTogJTUuMmYtJTUuMmYpLCBwID0gJTcuNGYgLSAlcyVzXG4iLCANCiAgICAgICAgICAgICAgICB2YXIsIGhyLCBjaV9sb3dlciwgY2lfdXBwZXIsIHBfdmFsLCBlZmZlY3QsIHRpbWVfbm90ZSkpDQogICAgfQ0KICAgIGNhdCgiXG4iKQ0KICB9DQogIA0KICAjIFByaW50IG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIHNlcGFyYXRlbHkgd2l0aCBtb3JlIGRldGFpbGVkIGludGVycHJldGF0aW9uDQogIGNhdCgiXG5NQUNST0VDT05PTUlDIFZBUklBQkxFUzpcbiIpDQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT1cblxuIikNCiAgDQogIG1hY3JvX2NhdGVnb3JpZXNfcHJpbnQgPC0gYygiRWNvbm9taWNfR3Jvd3RoIiwgIkluZmxhdGlvbiIsICJMYWJvcl9NYXJrZXQiKQ0KICANCiAgZm9yKGNhdGVnb3J5X25hbWUgaW4gbWFjcm9fY2F0ZWdvcmllc19wcmludCkgew0KICAgICMgQ3JlYXRlIGEgbW9yZSByZWFkYWJsZSBjYXRlZ29yeSBuYW1lDQogICAgcmVhZGFibGVfbmFtZSA8LSBnc3ViKCJfIiwgIiAiLCBjYXRlZ29yeV9uYW1lKQ0KICAgIGNhdChyZWFkYWJsZV9uYW1lLCAiOlxuIikNCiAgICBjYXQocGFzdGUocmVwKCItIiwgbmNoYXIocmVhZGFibGVfbmFtZSkgKyAxKSwgY29sbGFwc2UgPSAiIiksICJcbiIpDQogICAgDQogICAgIyBHZXQgdmFyaWFibGVzIGluIHRoaXMgY2F0ZWdvcnkNCiAgICBjYXRlZ29yeV9yb3dzIDwtIG1haW5fZWZmZWN0c1ttYWluX2VmZmVjdHMkVHlwZSA9PSBjYXRlZ29yeV9uYW1lLCBdDQogICAgDQogICAgaWYobnJvdyhjYXRlZ29yeV9yb3dzKSA9PSAwKSB7DQogICAgICBjYXQoIk5vIHZhcmlhYmxlcyBmcm9tIHRoaXMgY2F0ZWdvcnkgaW4gdGhlIG1vZGVsLlxuXG4iKQ0KICAgICAgbmV4dA0KICAgIH0NCiAgICANCiAgICAjIFByaW50IGVhY2ggdmFyaWFibGUgaW4gdGhpcyBjYXRlZ29yeSB3aXRoIGVuaGFuY2VkIGludGVycHJldGF0aW9uDQogICAgZm9yKGkgaW4gMTpucm93KGNhdGVnb3J5X3Jvd3MpKSB7DQogICAgICB2YXIgPC0gY2F0ZWdvcnlfcm93cyRWYXJpYWJsZVtpXQ0KICAgICAgY29lZl92YWwgPC0gY2F0ZWdvcnlfcm93cyRDb2VmZmljaWVudFtpXQ0KICAgICAgaHIgPC0gY2F0ZWdvcnlfcm93cyRIYXphcmRfUmF0aW9baV0NCiAgICAgIHBfdmFsIDwtIGNhdGVnb3J5X3Jvd3MkUF92YWx1ZVtpXQ0KICAgICAgY2lfbG93ZXIgPC0gY2F0ZWdvcnlfcm93cyRMb3dlcl9DSVtpXQ0KICAgICAgY2lfdXBwZXIgPC0gY2F0ZWdvcnlfcm93cyRVcHBlcl9DSVtpXQ0KICAgICAgDQogICAgICAjIERldGVybWluZSBlZmZlY3QgZGlyZWN0aW9uIGFuZCBzaWduaWZpY2FuY2UNCiAgICAgIGlmKHBfdmFsIDwgMC4wMSkgew0KICAgICAgICBzaWduaWZpY2FuY2UgPC0gInN0cm9uZyBldmlkZW5jZSB0aGF0Ig0KICAgICAgfSBlbHNlIGlmKHBfdmFsIDwgMC4wNSkgew0KICAgICAgICBzaWduaWZpY2FuY2UgPC0gImV2aWRlbmNlIHRoYXQiDQogICAgICB9IGVsc2UgaWYocF92YWwgPCAwLjEpIHsNCiAgICAgICAgc2lnbmlmaWNhbmNlIDwtICJ3ZWFrIGV2aWRlbmNlIHRoYXQiDQogICAgICB9IGVsc2Ugew0KICAgICAgICBzaWduaWZpY2FuY2UgPC0gIm5vIHNpZ25pZmljYW50IGV2aWRlbmNlIHRoYXQiDQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2UgY2hhbmdlDQogICAgICBpZihociA8IDEpIHsNCiAgICAgICAgcGN0X2NoYW5nZSA8LSAoMSAtIGhyKSAqIDEwMA0KICAgICAgICBlZmZlY3QgPC0gcGFzdGUwKCJhIDEtdW5pdCBpbmNyZWFzZSBpbiAiLCB2YXIsICIgaXMgYXNzb2NpYXRlZCB3aXRoIGEgIiwgDQogICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHBjdF9jaGFuZ2UsIDEpLCAiJSBkZWNyZWFzZSBpbiAiLCBtb2RlbF9uYW1lLCAiIHJpc2siKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgcGN0X2NoYW5nZSA8LSAoaHIgLSAxKSAqIDEwMA0KICAgICAgICBlZmZlY3QgPC0gcGFzdGUwKCJhIDEtdW5pdCBpbmNyZWFzZSBpbiAiLCB2YXIsICIgaXMgYXNzb2NpYXRlZCB3aXRoIGEgIiwgDQogICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHBjdF9jaGFuZ2UsIDEpLCAiJSBpbmNyZWFzZSBpbiAiLCBtb2RlbF9uYW1lLCAiIHJpc2siKQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIENoZWNrIGZvciB0aW1lIGludGVyYWN0aW9uDQogICAgICBoYXNfdGltZV9pbnQgPC0gcGFzdGUwKHZhciwgIjp0dCIpICVpbiUgdGltZV9lZmZlY3RzJFZhcmlhYmxlDQogICAgICB0aW1lX25vdGUgPC0gIiINCiAgICAgIA0KICAgICAgaWYoaGFzX3RpbWVfaW50KSB7DQogICAgICAgIHRpbWVfcm93IDwtIHRpbWVfZWZmZWN0c1t0aW1lX2VmZmVjdHMkVmFyaWFibGUgPT0gcGFzdGUwKHZhciwgIjp0dCIpLCBdDQogICAgICAgIHRpbWVfY29lZiA8LSB0aW1lX3JvdyRDb2VmZmljaWVudA0KICAgICAgICB0aW1lX2hyIDwtIHRpbWVfcm93JEhhemFyZF9SYXRpbw0KICAgICAgICB0aW1lX3AgPC0gdGltZV9yb3ckUF92YWx1ZQ0KICAgICAgICANCiAgICAgICAgaWYodGltZV9wIDwgMC4wNSkgew0KICAgICAgICAgIGlmKHRpbWVfY29lZiA+IDAgJiYgY29lZl92YWwgPiAwKSB7DQogICAgICAgICAgICB0aW1lX25vdGUgPC0gIlxuICAg4oaSIFRoaXMgZWZmZWN0IHN0cmVuZ3RoZW5zIG92ZXIgdGltZSAoY29tcGFuaWVzIGJlY29tZSBNT1JFIHNlbnNpdGl2ZSB0byB0aGlzIGVjb25vbWljIGZhY3RvciBhcyB0aGV5IGFnZSkiDQogICAgICAgICAgfSBlbHNlIGlmKHRpbWVfY29lZiA8IDAgJiYgY29lZl92YWwgPCAwKSB7DQogICAgICAgICAgICB0aW1lX25vdGUgPC0gIlxuICAg4oaSIFRoaXMgZWZmZWN0IHN0cmVuZ3RoZW5zIG92ZXIgdGltZSAoY29tcGFuaWVzIGJlY29tZSBNT1JFIHByb3RlY3RlZCBieSB0aGlzIGVjb25vbWljIGZhY3RvciBhcyB0aGV5IGFnZSkiDQogICAgICAgICAgfSBlbHNlIGlmKHRpbWVfY29lZiA8IDAgJiYgY29lZl92YWwgPiAwKSB7DQogICAgICAgICAgICB0aW1lX25vdGUgPC0gIlxuICAg4oaSIFRoaXMgZWZmZWN0IHdlYWtlbnMgb3ZlciB0aW1lIChyaXNrIGZyb20gdGhpcyBlY29ub21pYyBmYWN0b3IgREVDUkVBU0VTIGFzIGNvbXBhbmllcyBhZ2UpIg0KICAgICAgICAgIH0gZWxzZSBpZih0aW1lX2NvZWYgPiAwICYmIGNvZWZfdmFsIDwgMCkgew0KICAgICAgICAgICAgdGltZV9ub3RlIDwtICJcbiAgIOKGkiBUaGlzIGVmZmVjdCB3ZWFrZW5zIG92ZXIgdGltZSAocHJvdGVjdGlvbiBmcm9tIHRoaXMgZWNvbm9taWMgZmFjdG9yIERFQ1JFQVNFUyBhcyBjb21wYW5pZXMgYWdlKSINCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBQcm92aWRlIGVjb25vbWljIGludGVycHJldGF0aW9uIGJhc2VkIG9uIHRoZSB2YXJpYWJsZQ0KICAgICAgZWNvbl9pbnRlcnAgPC0gIiINCiAgICAgIGlmKHZhciA9PSAiZ2RwX2dyb3d0aCIpIHsNCiAgICAgICAgaWYoaHIgPCAxKSB7DQogICAgICAgICAgZWNvbl9pbnRlcnAgPC0gcGFzdGUwKCJcbiAgIOKGkiBTdHJvbmcgZWNvbm9taWMgZ3Jvd3RoIGFwcGVhcnMgdG8gYmUgcHJvdGVjdGl2ZSBhZ2FpbnN0IGNvbXBhbnkgIiwgbW9kZWxfbmFtZSkNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBlY29uX2ludGVycCA8LSBwYXN0ZTAoIlxuICAg4oaSIFN1cnByaXNpbmdseSwgc3Ryb25nZXIgZWNvbm9taWMgZ3Jvd3RoIGlzIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIgIiwgbW9kZWxfbmFtZSwgIiByaXNrIikNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIGlmKHZhciA9PSAiZ2RwX2RlZmxhdG9yIikgew0KICAgICAgICBpZihociA8IDEpIHsNCiAgICAgICAgICBlY29uX2ludGVycCA8LSBwYXN0ZTAoIlxuICAg4oaSIEhpZ2hlciBpbmZsYXRpb24gYXBwZWFycyB0byBiZSBwcm90ZWN0aXZlIGFnYWluc3QgY29tcGFueSAiLCBtb2RlbF9uYW1lKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGVjb25faW50ZXJwIDwtIHBhc3RlMCgiXG4gICDihpIgSGlnaGVyIGluZmxhdGlvbiBpcyBhc3NvY2lhdGVkIHdpdGggaW5jcmVhc2VkIGNvbXBhbnkgIiwgbW9kZWxfbmFtZSwgIiByaXNrIikNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIGlmKHZhciA9PSAidW5lbXBsb3llbWVudCIpIHsNCiAgICAgICAgaWYoaHIgPCAxKSB7DQogICAgICAgICAgZWNvbl9pbnRlcnAgPC0gcGFzdGUwKCJcbiAgIOKGkiBIaWdoZXIgdW5lbXBsb3ltZW50IGFwcGVhcnMgdG8gYmUgcHJvdGVjdGl2ZSBhZ2FpbnN0IGNvbXBhbnkgIiwgbW9kZWxfbmFtZSkNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBlY29uX2ludGVycCA8LSBwYXN0ZTAoIlxuICAg4oaSIEhpZ2hlciB1bmVtcGxveW1lbnQgaXMgYXNzb2NpYXRlZCB3aXRoIGluY3JlYXNlZCBjb21wYW55ICIsIG1vZGVsX25hbWUsICIgcmlzayIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBQcmludCBkZXRhaWxlZCByZXN1bHQgZm9yIG1hY3JvIHZhcmlhYmxlDQogICAgICBjYXQoc3ByaW50ZigiJS0xNXM6IEhSID0gJTUuMmYgKDk1JSUgQ0k6ICU1LjJmLSU1LjJmKSwgcCA9ICU3LjRmXG4iLCANCiAgICAgICAgICAgICAgICB2YXIsIGhyLCBjaV9sb3dlciwgY2lfdXBwZXIsIHBfdmFsKSkNCiAgICAgIGNhdCgiICAgSW50ZXJwcmV0YXRpb246IFRoZXJlIGlzIiwgc2lnbmlmaWNhbmNlLCBlZmZlY3QsICJcbiIpDQogICAgICBjYXQoZWNvbl9pbnRlcnApDQogICAgICBjYXQodGltZV9ub3RlKQ0KICAgICAgY2F0KCJcblxuIikNCiAgICB9DQogIH0NCiAgDQogICMgUHJpbnQgdGltZSBpbnRlcmFjdGlvbiBlZmZlY3RzIHN1bW1hcnkNCiAgaWYobnJvdyh0aW1lX2VmZmVjdHMpID4gMCkgew0KICAgIGNhdCgiVElNRS1WQVJZSU5HIEVGRkVDVFMgU1VNTUFSWTpcbiIpDQogICAgY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiIpDQogICAgDQogICAgIyBTZXBhcmF0ZSBmaW5hbmNpYWwgYW5kIG1hY3JvIHRpbWUgZWZmZWN0cw0KICAgIGZpbl90aW1lX2VmZmVjdHMgPC0gdGltZV9lZmZlY3RzW3NhcHBseSh0aW1lX2VmZmVjdHMkVmFyaWFibGUsIGZ1bmN0aW9uKHYpIHsNCiAgICAgIHZhcl9uYW1lIDwtIHN1YigiOnR0IiwgIiIsIHYpDQogICAgICByZXR1cm4oYW55KHNhcHBseShmaW5hbmNpYWxfY2F0ZWdvcmllcywgZnVuY3Rpb24oY2F0KSB2YXJfbmFtZSAlaW4lIGNhdCkpKQ0KICAgIH0pLCBdDQogICAgDQogICAgbWFjcm9fdGltZV9lZmZlY3RzIDwtIHRpbWVfZWZmZWN0c1tzYXBwbHkodGltZV9lZmZlY3RzJFZhcmlhYmxlLCBmdW5jdGlvbih2KSB7DQogICAgICB2YXJfbmFtZSA8LSBzdWIoIjp0dCIsICIiLCB2KQ0KICAgICAgcmV0dXJuKGFueShzYXBwbHkobWFjcm9fY2F0ZWdvcmllcywgZnVuY3Rpb24oY2F0KSB2YXJfbmFtZSAlaW4lIGNhdCkpKQ0KICAgIH0pLCBdDQogICAgDQogICAgIyBQcmludCBmaW5hbmNpYWwgdGltZSBlZmZlY3RzDQogICAgaWYobnJvdyhmaW5fdGltZV9lZmZlY3RzKSA+IDApIHsNCiAgICAgIGNhdCgiRmluYW5jaWFsIFZhcmlhYmxlcyB3aXRoIFRpbWUtVmFyeWluZyBFZmZlY3RzOlxuIikNCiAgICAgIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogICAgICANCiAgICAgIGZvcihpIGluIDE6bnJvdyhmaW5fdGltZV9lZmZlY3RzKSkgew0KICAgICAgICB2YXIgPC0gc3ViKCI6dHQiLCAiIiwgZmluX3RpbWVfZWZmZWN0cyRWYXJpYWJsZVtpXSkNCiAgICAgICAgaHIgPC0gZmluX3RpbWVfZWZmZWN0cyRIYXphcmRfUmF0aW9baV0NCiAgICAgICAgcF92YWwgPC0gZmluX3RpbWVfZWZmZWN0cyRQX3ZhbHVlW2ldDQogICAgICAgIA0KICAgICAgICBpZihwX3ZhbCA8IDAuMDUpIHsNCiAgICAgICAgICBjYXQodmFyLCAiZWZmZWN0IGNoYW5nZXMgb3ZlciB0aW1lOiBIUiBwZXIgeWVhciA9Iiwgcm91bmQoaHIsIDMpLCANCiAgICAgICAgICAgICAgIihwID0iLCBmb3JtYXQucHZhbChwX3ZhbCwgZGlnaXRzID0gMyksICIpXG4iKQ0KICAgICAgICAgIA0KICAgICAgICAgICMgQ2FsY3VsYXRlIGVmZmVjdHMgYXQgZGlmZmVyZW50IHRpbWUgcG9pbnRzDQogICAgICAgICAgbWFpbl9jb2VmIDwtIG1haW5fZWZmZWN0cyRDb2VmZmljaWVudFttYWluX2VmZmVjdHMkVmFyaWFibGUgPT0gdmFyXQ0KICAgICAgICAgIHRpbWVfY29lZiA8LSBmaW5fdGltZV9lZmZlY3RzJENvZWZmaWNpZW50W2ldDQogICAgICAgICAgDQogICAgICAgICAgY2F0KCIgIEVmZmVjdCBhdCBkaWZmZXJlbnQgdGltZSBwb2ludHM6XG4iKQ0KICAgICAgICAgIHRpbWVzIDwtIGMoMSwgMywgNSwgMTApDQogICAgICAgICAgZm9yKHQgaW4gdGltZXMpIHsNCiAgICAgICAgICAgIGhyX2F0X3RpbWUgPC0gZXhwKG1haW5fY29lZiArIHRpbWVfY29lZiAqIHQpDQogICAgICAgICAgICBjYXQoIiAgQXQgdCA9IiwgdCwgInllYXJzOiBIUiA9Iiwgcm91bmQoaHJfYXRfdGltZSwgMyksICJcbiIpDQogICAgICAgICAgfQ0KICAgICAgICAgIGNhdCgiXG4iKQ0KICAgICAgICB9DQogICAgICB9DQogICAgfQ0KICAgIA0KICAgICMgUHJpbnQgbWFjcm8gdGltZSBlZmZlY3RzIHdpdGggYnVzaW5lc3MgY3ljbGUgaW50ZXJwcmV0YXRpb24NCiAgICBpZihucm93KG1hY3JvX3RpbWVfZWZmZWN0cykgPiAwKSB7DQogICAgICBjYXQoIk1hY3JvZWNvbm9taWMgVmFyaWFibGVzIHdpdGggVGltZS1WYXJ5aW5nIEVmZmVjdHM6XG4iKQ0KICAgICAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICAgICAgDQogICAgICBmb3IoaSBpbiAxOm5yb3cobWFjcm9fdGltZV9lZmZlY3RzKSkgew0KICAgICAgICB2YXIgPC0gc3ViKCI6dHQiLCAiIiwgbWFjcm9fdGltZV9lZmZlY3RzJFZhcmlhYmxlW2ldKQ0KICAgICAgICBociA8LSBtYWNyb190aW1lX2VmZmVjdHMkSGF6YXJkX1JhdGlvW2ldDQogICAgICAgIHBfdmFsIDwtIG1hY3JvX3RpbWVfZWZmZWN0cyRQX3ZhbHVlW2ldDQogICAgICAgIA0KICAgICAgICBpZihwX3ZhbCA8IDAuMDUpIHsNCiAgICAgICAgICBjYXQodmFyLCAiZWZmZWN0IGNoYW5nZXMgb3ZlciB0aW1lOiBIUiBwZXIgeWVhciA9Iiwgcm91bmQoaHIsIDMpLCANCiAgICAgICAgICAgICAgIihwID0iLCBmb3JtYXQucHZhbChwX3ZhbCwgZGlnaXRzID0gMyksICIpXG4iKQ0KICAgICAgICAgIA0KICAgICAgICAgICMgQ2FsY3VsYXRlIGVmZmVjdHMgYXQgZGlmZmVyZW50IHRpbWUgcG9pbnRzDQogICAgICAgICAgbWFpbl9jb2VmIDwtIG1haW5fZWZmZWN0cyRDb2VmZmljaWVudFttYWluX2VmZmVjdHMkVmFyaWFibGUgPT0gdmFyXQ0KICAgICAgICAgIHRpbWVfY29lZiA8LSBtYWNyb190aW1lX2VmZmVjdHMkQ29lZmZpY2llbnRbaV0NCiAgICAgICAgICANCiAgICAgICAgICBjYXQoIiAgRWZmZWN0IGF0IGRpZmZlcmVudCB0aW1lIHBvaW50czpcbiIpDQogICAgICAgICAgdGltZXMgPC0gYygxLCAzLCA1LCAxMCkNCiAgICAgICAgICBmb3IodCBpbiB0aW1lcykgew0KICAgICAgICAgICAgaHJfYXRfdGltZSA8LSBleHAobWFpbl9jb2VmICsgdGltZV9jb2VmICogdCkNCiAgICAgICAgICAgIGNhdCgiICBBdCB0ID0iLCB0LCAieWVhcnM6IEhSID0iLCByb3VuZChocl9hdF90aW1lLCAzKSwgIlxuIikNCiAgICAgICAgICB9DQogICAgICAgICAgDQogICAgICAgICAgIyBCdXNpbmVzcyBjeWNsZSBpbnRlcnByZXRhdGlvbg0KICAgICAgICAgIGNhdCgiICBCdXNpbmVzcyBjeWNsZSBpbXBsaWNhdGlvbnM6XG4iKQ0KICAgICAgICAgIGlmKHZhciA9PSAiZ2RwX2dyb3d0aCIpIHsNCiAgICAgICAgICAgIGlmKGhyID4gMSkgew0KICAgICAgICAgICAgICBjYXQoIiAgLSBUaGUgaW5mbHVlbmNlIG9mIGVjb25vbWljIGdyb3d0aCBvbiIsIG1vZGVsX25hbWUsICJyaXNrIElOQ1JFQVNFUyBhcyBjb21wYW5pZXMgbWF0dXJlXG4iKQ0KICAgICAgICAgICAgICBjYXQoIiAgLSBPbGRlciBjb21wYW5pZXMgYmVjb21lIG1vcmUgc2Vuc2l0aXZlIHRvIGVjb25vbWljIGNvbmRpdGlvbnNcbiIpDQogICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICBjYXQoIiAgLSBUaGUgaW5mbHVlbmNlIG9mIGVjb25vbWljIGdyb3d0aCBvbiIsIG1vZGVsX25hbWUsICJyaXNrIERFQ1JFQVNFUyBhcyBjb21wYW5pZXMgbWF0dXJlXG4iKQ0KICAgICAgICAgICAgICBjYXQoIiAgLSBZb3VuZ2VyIGNvbXBhbmllcyBhcmUgbW9yZSBzZW5zaXRpdmUgdG8gZWNvbm9taWMgY29uZGl0aW9uc1xuIikNCiAgICAgICAgICAgIH0NCiAgICAgICAgICB9IGVsc2UgaWYodmFyID09ICJ1bmVtcGxveWVtZW50Iikgew0KICAgICAgICAgICAgaWYoaHIgPiAxKSB7DQogICAgICAgICAgICAgIGNhdCgiICAtIFRoZSBpbmZsdWVuY2Ugb2YgbGFib3IgbWFya2V0IGNvbmRpdGlvbnMgb24iLCBtb2RlbF9uYW1lLCAicmlzayBJTkNSRUFTRVMgb3ZlciB0aW1lXG4iKQ0KICAgICAgICAgICAgICBjYXQoIiAgLSBVbmVtcGxveW1lbnQgaGFzIGEgc3Ryb25nZXIgZWZmZWN0IG9uIGNvbXBhbmllcyB0aGUgbG9uZ2VyIHRoZXkgZXhpc3RcbiIpDQogICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICBjYXQoIiAgLSBUaGUgaW5mbHVlbmNlIG9mIGxhYm9yIG1hcmtldCBjb25kaXRpb25zIG9uIiwgbW9kZWxfbmFtZSwgInJpc2sgREVDUkVBU0VTIG92ZXIgdGltZVxuIikNCiAgICAgICAgICAgICAgY2F0KCIgIC0gVW5lbXBsb3ltZW50IGhhcyBpdHMgc3Ryb25nZXN0IGVmZmVjdCBvbiB5b3VuZ2VyIGNvbXBhbmllc1xuIikNCiAgICAgICAgICAgIH0NCiAgICAgICAgICB9IGVsc2UgaWYodmFyID09ICJnZHBfZGVmbGF0b3IiKSB7DQogICAgICAgICAgICBpZihociA+IDEpIHsNCiAgICAgICAgICAgICAgY2F0KCIgIC0gVGhlIGluZmx1ZW5jZSBvZiBpbmZsYXRpb24gb24iLCBtb2RlbF9uYW1lLCAicmlzayBJTkNSRUFTRVMgYXMgY29tcGFuaWVzIG1hdHVyZVxuIikNCiAgICAgICAgICAgICAgY2F0KCIgIC0gT2xkZXIgY29tcGFuaWVzIGJlY29tZSBtb3JlIHNlbnNpdGl2ZSB0byBpbmZsYXRpb25cbiIpDQogICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICBjYXQoIiAgLSBUaGUgaW5mbHVlbmNlIG9mIGluZmxhdGlvbiBvbiIsIG1vZGVsX25hbWUsICJyaXNrIERFQ1JFQVNFUyBhcyBjb21wYW5pZXMgbWF0dXJlXG4iKQ0KICAgICAgICAgICAgICBjYXQoIiAgLSBZb3VuZ2VyIGNvbXBhbmllcyBhcmUgbW9yZSBzZW5zaXRpdmUgdG8gaW5mbGF0aW9uXG4iKQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgIH0NCiAgICAgICAgICBjYXQoIlxuIikNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICANCiAgIyBSZXR1cm4gdGhlIGhhemFyZCByYXRpbyBkYXRhIGZyYW1lcyB3aXRoIHR5cGUgaW5mb3JtYXRpb24NCiAgcmV0dXJuKGxpc3QobWFpbl9lZmZlY3RzID0gbWFpbl9lZmZlY3RzLCB0aW1lX2VmZmVjdHMgPSB0aW1lX2VmZmVjdHMpKQ0KfQ0KDQojIEFwcGx5IGZ1bmN0aW9uIHRvIGJvdGggbW9kZWxzDQpjYXQoIlxuPT09IEFOQUxZWklORyBCQU5LUlVQVENZIE1PREVMID09PVxuIikNCmJhbmtydXB0Y3lfaHJzIDwtIGFuYWx5emVfaGF6YXJkX3JhdGlvcyhmaW5hbF9iYW5rcnVwdGN5X21vZGVsLCAiYmFua3J1cHRjeSIpDQoNCmNhdCgiXG49PT0gQU5BTFlaSU5HIEFDUVVJU0lUSU9OIE1PREVMID09PVxuIikNCmFjcXVpc2l0aW9uX2hycyA8LSBhbmFseXplX2hhemFyZF9yYXRpb3MoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwsICJhY3F1aXNpdGlvbiIpDQoNCiMgQ29tcGFyZSB0aGUgaW1wYWN0IG9mIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGJldHdlZW4gbW9kZWxzDQpjYXQoIlxuXG49PT0gQ09NUEFSQVRJVkUgQU5BTFlTSVMgT0YgTUFDUk9FQ09OT01JQyBFRkZFQ1RTID09PVxuIikNCmNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4iKQ0KDQojIEV4dHJhY3QgbWFjcm8gdmFyaWFibGVzIGZyb20gYm90aCBtb2RlbHMNCmJhbmtydXB0Y3lfbWFjcm8gPC0gYmFua3J1cHRjeV9ocnMkbWFpbl9lZmZlY3RzW2dyZXAoImdkcF98dW5lbXBsb3llbWVudCIsIGJhbmtydXB0Y3lfaHJzJG1haW5fZWZmZWN0cyRWYXJpYWJsZSksIF0NCmFjcXVpc2l0aW9uX21hY3JvIDwtIGFjcXVpc2l0aW9uX2hycyRtYWluX2VmZmVjdHNbZ3JlcCgiZ2RwX3x1bmVtcGxveWVtZW50IiwgYWNxdWlzaXRpb25faHJzJG1haW5fZWZmZWN0cyRWYXJpYWJsZSksIF0NCg0KIyBWYXJpYWJsZXMgaW4gYm90aCBtb2RlbHMNCmNvbW1vbl92YXJzIDwtIGludGVyc2VjdChiYW5rcnVwdGN5X21hY3JvJFZhcmlhYmxlLCBhY3F1aXNpdGlvbl9tYWNybyRWYXJpYWJsZSkNCg0KaWYobGVuZ3RoKGNvbW1vbl92YXJzKSA+IDApIHsNCiAgY2F0KCJNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpbiBib3RoIG1vZGVsczpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgDQogIGZvcih2YXIgaW4gY29tbW9uX3ZhcnMpIHsNCiAgICAjIEdldCBIUnMNCiAgICBia19ociA8LSBiYW5rcnVwdGN5X21hY3JvJEhhemFyZF9SYXRpb1tiYW5rcnVwdGN5X21hY3JvJFZhcmlhYmxlID09IHZhcl0NCiAgICBhY3FfaHIgPC0gYWNxdWlzaXRpb25fbWFjcm8kSGF6YXJkX1JhdGlvW2FjcXVpc2l0aW9uX21hY3JvJFZhcmlhYmxlID09IHZhcl0NCiAgICANCiAgICAjIENhbGN1bGF0ZSBwZXJjZW50YWdlIGNoYW5nZQ0KICAgIGJrX3BjdCA8LSBpZmVsc2UoYmtfaHIgPCAxLCAoMSAtIGJrX2hyKSAqIDEwMCwgKGJrX2hyIC0gMSkgKiAxMDApDQogICAgYWNxX3BjdCA8LSBpZmVsc2UoYWNxX2hyIDwgMSwgKDEgLSBhY3FfaHIpICogMTAwLCAoYWNxX2hyIC0gMSkgKiAxMDApDQogICAgDQogICAgIyBHZXQgcC12YWx1ZXMNCiAgICBia19wIDwtIGJhbmtydXB0Y3lfbWFjcm8kUF92YWx1ZVtiYW5rcnVwdGN5X21hY3JvJFZhcmlhYmxlID09IHZhcl0NCiAgICBhY3FfcCA8LSBhY3F1aXNpdGlvbl9tYWNybyRQX3ZhbHVlW2FjcXVpc2l0aW9uX21hY3JvJFZhcmlhYmxlID09IHZhcl0NCiAgICANCiAgICAjIENoZWNrIHNpZ25pZmljYW5jZQ0KICAgIGJrX3NpZyA8LSBpZmVsc2UoYmtfcCA8IDAuMDUsICJzaWduaWZpY2FudCIsICJub3Qgc2lnbmlmaWNhbnQiKQ0KICAgIGFjcV9zaWcgPC0gaWZlbHNlKGFjcV9wIDwgMC4wNSwgInNpZ25pZmljYW50IiwgIm5vdCBzaWduaWZpY2FudCIpDQogICAgDQogICAgIyBDaGVjayBkaXJlY3Rpb24NCiAgICBia19kaXIgPC0gaWZlbHNlKGJrX2hyID4gMSwgImluY3JlYXNlcyIsICJkZWNyZWFzZXMiKQ0KICAgIGFjcV9kaXIgPC0gaWZlbHNlKGFjcV9ociA+IDEsICJpbmNyZWFzZXMiLCAiZGVjcmVhc2VzIikNCiAgICANCiAgICBjYXQodmFyLCAiOlxuIikNCiAgICBjYXQoIiAgLSBCYW5rcnVwdGN5IGVmZmVjdDogIiwgYmtfZGlyLCAiIHJpc2sgYnkgIiwgcm91bmQoYWJzKGJrX3BjdCksIDEpLCAiJSAoIiwgYmtfc2lnLCAiLCBwPSIsIHJvdW5kKGJrX3AsIDQpLCAiKVxuIiwgc2VwPSIiKQ0KICAgIGNhdCgiICAtIEFjcXVpc2l0aW9uIGVmZmVjdDogIiwgYWNxX2RpciwgIiByaXNrIGJ5ICIsIHJvdW5kKGFicyhhY3FfcGN0KSwgMSksICIlICgiLCBhY3Ffc2lnLCAiLCBwPSIsIHJvdW5kKGFjcV9wLCA0KSwgIilcbiIsIHNlcD0iIikNCiAgICANCiAgICAjIENoZWNrIGZvciBvcHBvc2l0ZSBlZmZlY3RzDQogICAgaWYoKGJrX2hyID4gMSAmJiBhY3FfaHIgPCAxKSB8fCAoYmtfaHIgPCAxICYmIGFjcV9ociA+IDEpKSB7DQogICAgICBjYXQoIiAgLSBOT1RFOiBUaGlzIGVjb25vbWljIGZhY3RvciBoYXMgT1BQT1NJVEUgZWZmZWN0cyBvbiBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvblxuIikNCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCIgIC0gVGhpcyBlY29ub21pYyBmYWN0b3IgYWZmZWN0cyBib3RoIG91dGNvbWVzIGluIHRoZSBzYW1lIGRpcmVjdGlvblxuIikNCiAgICB9DQogICAgDQogICAgIyBDaGVjayBmb3IgdGltZS12YXJ5aW5nIGVmZmVjdHMgaW4gYm90aCBtb2RlbHMNCiAgICBia190aW1lX3ZhciA8LSBwYXN0ZTAodmFyLCAiOnR0IikgJWluJSBiYW5rcnVwdGN5X2hycyR0aW1lX2VmZmVjdHMkVmFyaWFibGUNCiAgICBhY3FfdGltZV92YXIgPC0gcGFzdGUwKHZhciwgIjp0dCIpICVpbiUgYWNxdWlzaXRpb25faHJzJHRpbWVfZWZmZWN0cyRWYXJpYWJsZQ0KICAgIA0KICAgIGlmKGJrX3RpbWVfdmFyICYmIGFjcV90aW1lX3Zhcikgew0KICAgICAgY2F0KCIgIC0gVGltZS12YXJ5aW5nIGVmZmVjdHMgcHJlc2VudCBpbiBCT1RIIG1vZGVsc1xuIikNCiAgICB9IGVsc2UgaWYoYmtfdGltZV92YXIpIHsNCiAgICAgIGNhdCgiICAtIFRpbWUtdmFyeWluZyBlZmZlY3RzIHByZXNlbnQgb25seSBpbiBCQU5LUlVQVENZIG1vZGVsXG4iKQ0KICAgIH0gZWxzZSBpZihhY3FfdGltZV92YXIpIHsNCiAgICAgIGNhdCgiICAtIFRpbWUtdmFyeWluZyBlZmZlY3RzIHByZXNlbnQgb25seSBpbiBBQ1FVSVNJVElPTiBtb2RlbFxuIikNCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCIgIC0gTm8gdGltZS12YXJ5aW5nIGVmZmVjdHMgaW4gZWl0aGVyIG1vZGVsXG4iKQ0KICAgIH0NCiAgICBjYXQoIlxuIikNCiAgfQ0KfQ0KDQojIFZhcmlhYmxlcyB1bmlxdWUgdG8gYmFua3J1cHRjeSBtb2RlbA0KYmtfb25seV92YXJzIDwtIHNldGRpZmYoYmFua3J1cHRjeV9tYWNybyRWYXJpYWJsZSwgYWNxdWlzaXRpb25fbWFjcm8kVmFyaWFibGUpDQppZihsZW5ndGgoYmtfb25seV92YXJzKSA+IDApIHsNCiAgY2F0KCJcbk1hY3JvZWNvbm9taWMgdmFyaWFibGVzIG9ubHkgaW4gYmFua3J1cHRjeSBtb2RlbDpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIA0KICBmb3IodmFyIGluIGJrX29ubHlfdmFycykgew0KICAgIGNhdCh2YXIsICJcbiIpDQogIH0NCiAgY2F0KCJcbiIpDQp9DQoNCiMgVmFyaWFibGVzIHVuaXF1ZSB0byBhY3F1aXNpdGlvbiBtb2RlbA0KYWNxX29ubHlfdmFycyA8LSBzZXRkaWZmKGFjcXVpc2l0aW9uX21hY3JvJFZhcmlhYmxlLCBiYW5rcnVwdGN5X21hY3JvJFZhcmlhYmxlKQ0KaWYobGVuZ3RoKGFjcV9vbmx5X3ZhcnMpID4gMCkgew0KICBjYXQoIlxuTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgb25seSBpbiBhY3F1aXNpdGlvbiBtb2RlbDpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIA0KICBmb3IodmFyIGluIGFjcV9vbmx5X3ZhcnMpIHsNCiAgICBjYXQodmFyLCAiXG4iKQ0KICB9DQogIGNhdCgiXG4iKQ0KfQ0KDQojIFNhdmUgaGF6YXJkIHJhdGlvIGFuYWx5c2lzIHJlc3VsdHMNCmhhemFyZF9yYXRpb19hbmFseXNpcyA8LSBsaXN0KA0KICBiYW5rcnVwdGN5ID0gYmFua3J1cHRjeV9ocnMsDQogIGFjcXVpc2l0aW9uID0gYWNxdWlzaXRpb25faHJzDQopDQpgYGANClNhbWUgaGVyZSwgd2Ugd2lsbCBjcmVhdGUgYSBmdW5jdGlvbiB0byB2aXN1YWxpemUgdGhlIHRpbWUtdmFyeWluZyBlZmZlY3RzIG9mIHRoZSBmaW5hbCBtb2RlbHMuIFRoaXMgZnVuY3Rpb24gd2lsbCBwbG90IHRoZSBoYXphcmQgcmF0aW9zIG92ZXIgdGltZSBmb3IgZWFjaCB2YXJpYWJsZSwgYW5kIHByb3ZpZGUgZGV0YWlsZWQgaW50ZXJwcmV0YXRpb25zIGJhc2VkIG9uIHRoZSB2YXJpYWJsZSB0eXBlIChmaW5hbmNpYWwgb3IgbWFjcm9lY29ub21pYykuIFdlIHdpbGwgYWxzbyBpbmNsdWRlIGEgc3VtbWFyeSBvZiB0aGUgdGltZS12YXJ5aW5nIGVmZmVjdHMgZm9yIGJvdGggbW9kZWxzLg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxMS4yOiBNb2RlbCBGaXQgU3RhdGlzdGljcyB3aXRoIE1hY3JvIFZhcmlhYmxlIENvbnRyaWJ1dGlvbg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMS4yOiBNT0RFTCBGSVQgU1RBVElTVElDUyBXSVRIIE1BQ1JPIENPTlRSSUJVVElPTiBBTkFMWVNJU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIGFsdGVybmF0aXZlIGNvbmNvcmRhbmNlIHVzaW5nIEFVQyBmcm9tIFJPQyBhbmFseXNpcw0KY2FsY3VsYXRlX2F1Y19jb25jb3JkYW5jZSA8LSBmdW5jdGlvbihtb2RlbCwgZXZlbnRfdHlwZSkgew0KICAjIENyZWF0ZSBkYXRhc2V0IHdpdGggbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55DQogIGxhc3Rfb2JzX2RhdGEgPC0gZGF0YSAlPiUNCiAgICBncm91cF9ieShjdXNpcCkgJT4lDQogICAgc2xpY2VfbWF4KHRzdG9wKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgQ2FsY3VsYXRlIHJpc2sgc2NvcmVzDQogIHJpc2tfc2NvcmVzIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBsYXN0X29ic19kYXRhLCB0eXBlID0gInJpc2siKQ0KICANCiAgIyBVc2UgUk9DIGFuYWx5c2lzIGFzIGFuIGFsdGVybmF0aXZlIHRvIGNvbmNvcmRhbmNlDQogIGlmKHJlcXVpcmVOYW1lc3BhY2UoInBST0MiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgICBsaWJyYXJ5KHBST0MpDQogICAgDQogICAgIyBVc2UgdGhlIGV2ZW50IGluZGljYXRvcg0KICAgIGV2ZW50X2luZGljYXRvciA8LSBsYXN0X29ic19kYXRhW1tldmVudF90eXBlXV0NCiAgICANCiAgICAjIENhbGN1bGF0ZSBST0MgYW5kIEFVQw0KICAgIHRyeUNhdGNoKHsNCiAgICAgIHJvY19vYmogPC0gcm9jKGV2ZW50X2luZGljYXRvciwgcmlza19zY29yZXMsIHF1aWV0ID0gVFJVRSkNCiAgICAgIGF1Y192YWx1ZSA8LSBhcy5udW1lcmljKGF1Yyhyb2Nfb2JqKSkNCiAgICAgIHJldHVybihhdWNfdmFsdWUpDQogICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgICBjYXQoIkVycm9yIGNhbGN1bGF0aW5nIEFVQzoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTkEpDQogICAgfSkNCiAgfSBlbHNlIHsNCiAgICByZXR1cm4oTkEpDQogIH0NCn0NCg0KIyBGdW5jdGlvbiB0byBjcmVhdGUgYSB2ZXJzaW9uIG9mIHRoZSBtb2RlbCB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcw0KY3JlYXRlX25vX21hY3JvX21vZGVsIDwtIGZ1bmN0aW9uKG1vZGVsLCBldmVudF90eXBlKSB7DQogICMgR2V0IG1vZGVsIGZvcm11bGENCiAgbW9kZWxfZm9ybXVsYSA8LSBmb3JtdWxhKG1vZGVsKQ0KICANCiAgIyBFeHRyYWN0IGFsbCB0ZXJtcw0KICBhbGxfdGVybXMgPC0gYXR0cih0ZXJtcyhtb2RlbCksICJ0ZXJtLmxhYmVscyIpDQogIA0KICAjIElkZW50aWZ5IG1hY3JvIHZhcmlhYmxlcyAoaW5jbHVkaW5nIHRoZWlyIHRpbWUgaW50ZXJhY3Rpb25zKQ0KICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAiZ2RwX2RlZmxhdG9yIiwgInVuZW1wbG95ZW1lbnQiKQ0KICBtYWNyb190ZXJtcyA8LSBjKCkNCiAgDQogIGZvcih0ZXJtIGluIGFsbF90ZXJtcykgew0KICAgICMgQ2hlY2sgaWYgdGVybSBjb250YWlucyBhbnkgbWFjcm8gdmFyaWFibGUNCiAgICBpc19tYWNybyA8LSBGQUxTRQ0KICAgIGZvcihtYWNybyBpbiBtYWNyb192YXJzKSB7DQogICAgICBpZihncmVwbChtYWNybywgdGVybSkpIHsNCiAgICAgICAgaXNfbWFjcm8gPC0gVFJVRQ0KICAgICAgICBicmVhaw0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICBpZihpc19tYWNybykgew0KICAgICAgbWFjcm9fdGVybXMgPC0gYyhtYWNyb190ZXJtcywgdGVybSkNCiAgICB9DQogIH0NCiAgDQogICMgSWYgbm8gbWFjcm8gdGVybXMgaW4gbW9kZWwsIHJldHVybiBvcmlnaW5hbCBtb2RlbA0KICBpZihsZW5ndGgobWFjcm9fdGVybXMpID09IDApIHsNCiAgICByZXR1cm4obW9kZWwpDQogIH0NCiAgDQogICMgQ3JlYXRlIGZvcm11bGEgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCiAgbm9fbWFjcm9fZm9ybXVsYSA8LSBtb2RlbF9mb3JtdWxhDQogIGZvcih0ZXJtIGluIG1hY3JvX3Rlcm1zKSB7DQogICAgbm9fbWFjcm9fZm9ybXVsYSA8LSB1cGRhdGUobm9fbWFjcm9fZm9ybXVsYSwgcGFzdGUwKCIuIH4gLiAtICIsIHRlcm0pKQ0KICB9DQogIA0KICAjIEZpdCBtb2RlbCB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcw0KICBpZihldmVudF90eXBlID09ICJiYW5rcnVwdGN5Iikgew0KICAgIG5vX21hY3JvX21vZGVsIDwtIGNveHBoKG5vX21hY3JvX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCiAgfSBlbHNlIHsNCiAgICBub19tYWNyb19tb2RlbCA8LSBjb3hwaChub19tYWNyb19mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIH0NCiAgDQogIHJldHVybihub19tYWNyb19tb2RlbCkNCn0NCg0KIyBGdW5jdGlvbiB0byBhbmFseXplIG1vZGVsIGZpdCB3aXRoIHNpbXBsZXIgY29uY29yZGFuY2UgY2FsY3VsYXRpb24NCmFuYWx5emVfbW9kZWxfZml0IDwtIGZ1bmN0aW9uKG1vZGVsLCBtb2RlbF9uYW1lLCBldmVudF90eXBlKSB7DQogICMgRXh0cmFjdCBzdGFuZGFyZCBtb2RlbCBzdGF0aXN0aWNzDQogIG1vZGVsX3N1bW1hcnkgPC0gc3VtbWFyeShtb2RlbCkNCiAgDQogICMgQ2FsY3VsYXRlIEFJQyBhbmQgQklDDQogIG1vZGVsX2FpYyA8LSBBSUMobW9kZWwpDQogIG1vZGVsX2JpYyA8LSBCSUMobW9kZWwpDQogIA0KICAjIENhbGN1bGF0ZSBwc2V1ZG8gUi1zcXVhcmVkICgxIC0gZXhwKC1MUi9uKSkNCiAgbHJfdGVzdCA8LSBtb2RlbF9zdW1tYXJ5JGxvZ3Rlc3RbInRlc3QiXQ0KICBuIDwtIG1vZGVsJG4NCiAgcl9zcXVhcmVkIDwtIDEgLSBleHAoLWxyX3Rlc3QgLyBuKQ0KICANCiAgIyBDYWxjdWxhdGUgQVVDIGFzIGFuIGFsdGVybmF0aXZlIHRvIGNvbmNvcmRhbmNlDQogIGF1Y192YWx1ZSA8LSBjYWxjdWxhdGVfYXVjX2NvbmNvcmRhbmNlKG1vZGVsLCBldmVudF90eXBlKQ0KICANCiAgIyBDcmVhdGUgbW9kZWwgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMgZm9yIGNvbXBhcmlzb24NCiAgbm9fbWFjcm9fbW9kZWwgPC0gY3JlYXRlX25vX21hY3JvX21vZGVsKG1vZGVsLCBldmVudF90eXBlKQ0KICBub19tYWNyb19zdW1tYXJ5IDwtIHN1bW1hcnkobm9fbWFjcm9fbW9kZWwpDQogIG5vX21hY3JvX2FpYyA8LSBBSUMobm9fbWFjcm9fbW9kZWwpDQogIG5vX21hY3JvX3Jfc3F1YXJlZCA8LSAxIC0gZXhwKC1ub19tYWNyb19zdW1tYXJ5JGxvZ3Rlc3RbInRlc3QiXSAvIG5vX21hY3JvX21vZGVsJG4pDQogIG5vX21hY3JvX2F1YyA8LSBjYWxjdWxhdGVfYXVjX2NvbmNvcmRhbmNlKG5vX21hY3JvX21vZGVsLCBldmVudF90eXBlKQ0KICANCiAgIyBHZXQgbW9kZWwgdGVybXMgdG8gaWRlbnRpZnkgbWFjcm8gdmFyaWFibGVzDQogIGFsbF90ZXJtcyA8LSBhdHRyKHRlcm1zKG1vZGVsKSwgInRlcm0ubGFiZWxzIikNCiAgbWFjcm9fdmFycyA8LSBjKCJnZHBfZ3Jvd3RoIiwgImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IikNCiAgbWFjcm9fdGVybXMgPC0gYygpDQogIA0KICBmb3IodGVybSBpbiBhbGxfdGVybXMpIHsNCiAgICBpc19tYWNybyA8LSBGQUxTRQ0KICAgIGZvcihtYWNybyBpbiBtYWNyb192YXJzKSB7DQogICAgICBpZihncmVwbChtYWNybywgdGVybSkpIHsNCiAgICAgICAgaXNfbWFjcm8gPC0gVFJVRQ0KICAgICAgICBicmVhaw0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICBpZihpc19tYWNybykgew0KICAgICAgbWFjcm9fdGVybXMgPC0gYyhtYWNyb190ZXJtcywgdGVybSkNCiAgICB9DQogIH0NCiAgDQogICMgUHJpbnQgcmVzdWx0cyB3aXRoIGludGVycHJldGF0aW9uDQogIGNhdCgiXG49PT0gTU9ERUwgRklUIFNUQVRJU1RJQ1MgRk9SIiwgdG91cHBlcihtb2RlbF9uYW1lKSwgIk1PREVMID09PVxuXG4iKQ0KICANCiAgIyBQcmludCBtYWNybyB2YXJpYWJsZXMgaW4gbW9kZWwNCiAgY2F0KCJNYWNyb2Vjb25vbWljIFZhcmlhYmxlcyBpbiBNb2RlbDpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIGlmKGxlbmd0aChtYWNyb190ZXJtcykgPiAwKSB7DQogICAgZm9yKHRlcm0gaW4gbWFjcm9fdGVybXMpIHsNCiAgICAgIGNhdCgiLSAiLCB0ZXJtLCAiXG4iKQ0KICAgIH0NCiAgfSBlbHNlIHsNCiAgICBjYXQoIk5vIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGluIHRoaXMgbW9kZWwuXG4iKQ0KICB9DQogIGNhdCgiXG4iKQ0KICANCiAgY2F0KCJEaXNjcmltaW5hdGlvbiBBYmlsaXR5OlxuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgDQogICMgVXNlIEFVQyBpbnN0ZWFkIG9mIGNvbmNvcmRhbmNlDQogIGNhdCgiQVVDIChBcmVhIFVuZGVyIFJPQyBDdXJ2ZSk6IiwgaWZlbHNlKGlzLm5hKGF1Y192YWx1ZSksICJOb3QgYXZhaWxhYmxlIiwgcm91bmQoYXVjX3ZhbHVlLCA0KSksICJcbiIpDQogIGlmKCFpcy5uYShhdWNfdmFsdWUpKSB7DQogICAgY2F0KCJJbnRlcnByZXRhdGlvbjogIikNCiAgICBpZihhdWNfdmFsdWUgPiAwLjgpIHsNCiAgICAgIGNhdCgiRXhjZWxsZW50IGRpc2NyaW1pbmF0aW9uIChBVUMgPiAwLjgpXG4iKQ0KICAgIH0gZWxzZSBpZihhdWNfdmFsdWUgPiAwLjcpIHsNCiAgICAgIGNhdCgiR29vZCBkaXNjcmltaW5hdGlvbiAoMC43IDwgQVVDIDwgMC44KVxuIikNCiAgICB9IGVsc2UgaWYoYXVjX3ZhbHVlID4gMC42KSB7DQogICAgICBjYXQoIkZhaXIgZGlzY3JpbWluYXRpb24gKDAuNiA8IEFVQyA8IDAuNylcbiIpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiUG9vciBkaXNjcmltaW5hdGlvbiAoQVVDIDwgMC42KVxuIikNCiAgICB9DQogIH0NCiAgY2F0KCJcbiIpDQogIA0KICAjIE1vZGVsIHNpZ25pZmljYW5jZQ0KICBjYXQoIk92ZXJhbGwgTW9kZWwgU2lnbmlmaWNhbmNlOlxuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIGNhdCgiTGlrZWxpaG9vZCBSYXRpbyBUZXN0OiIsIHJvdW5kKG1vZGVsX3N1bW1hcnkkbG9ndGVzdFsidGVzdCJdLCAyKSwgDQogICAgICAib24iLCBtb2RlbF9zdW1tYXJ5JGxvZ3Rlc3RbImRmIl0sICJkZiwgcC12YWx1ZToiLCANCiAgICAgIGZvcm1hdC5wdmFsKG1vZGVsX3N1bW1hcnkkbG9ndGVzdFsicHZhbHVlIl0sIGRpZ2l0cyA9IDQpLCAiXG4iKQ0KICANCiAgIyBJbnRlcnByZXQgTFIgdGVzdA0KICBpZihtb2RlbF9zdW1tYXJ5JGxvZ3Rlc3RbInB2YWx1ZSJdIDwgMC4wNSkgew0KICAgIGNhdCgiSW50ZXJwcmV0YXRpb246IE1vZGVsIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKHAgPCAwLjA1KVxuIikNCiAgfSBlbHNlIHsNCiAgICBjYXQoIkludGVycHJldGF0aW9uOiBNb2RlbCBpcyBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCAocCA+PSAwLjA1KVxuIikNCiAgfQ0KICBjYXQoIlxuIikNCiAgDQogICMgRml0IHN0YXRpc3RpY3Mgd2l0aCBleHBsYW5hdGlvbg0KICBjYXQoIkdvb2RuZXNzIG9mIEZpdCBNZWFzdXJlczpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICBjYXQoIkFJQzoiLCByb3VuZChtb2RlbF9haWMsIDIpLCAiLSBBa2Fpa2UgSW5mb3JtYXRpb24gQ3JpdGVyaW9uLCBsb3dlciB2YWx1ZXMgaW5kaWNhdGUgYmV0dGVyIGZpdFxuIikNCiAgY2F0KCJCSUM6Iiwgcm91bmQobW9kZWxfYmljLCAyKSwgIi0gQmF5ZXNpYW4gSW5mb3JtYXRpb24gQ3JpdGVyaW9uLCBwZW5hbGl6ZXMgY29tcGxleGl0eSBtb3JlIHRoYW4gQUlDXG4iKQ0KICBjYXQoIlBzZXVkbyBSLXNxdWFyZWQ6Iiwgcm91bmQocl9zcXVhcmVkLCA0KSwgDQogICAgICAiLSBQcm9wb3J0aW9uIG9mIG51bGwgZGV2aWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBtb2RlbFxuIikNCiAgDQogICMgSW50ZXJwcmV0IFItc3F1YXJlZA0KICBjYXQoIkludGVycHJldGF0aW9uOiAiKQ0KICBpZihyX3NxdWFyZWQgPiAwLjMpIHsNCiAgICBjYXQoIlN0cm9uZyBleHBsYW5hdG9yeSBwb3dlciBmb3Igc3Vydml2YWwgbW9kZWwgKFLCsiA+IDAuMylcbiIpDQogIH0gZWxzZSBpZihyX3NxdWFyZWQgPiAwLjE1KSB7DQogICAgY2F0KCJNb2RlcmF0ZSBleHBsYW5hdG9yeSBwb3dlciBmb3Igc3Vydml2YWwgbW9kZWwgKDAuMTUgPCBSwrIgPCAwLjMpXG4iKQ0KICB9IGVsc2UgaWYocl9zcXVhcmVkID4gMC4wNSkgew0KICAgIGNhdCgiTW9kZXN0IGV4cGxhbmF0b3J5IHBvd2VyIGZvciBzdXJ2aXZhbCBtb2RlbCAoMC4wNSA8IFLCsiA8IDAuMTUpXG4iKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiTGltaXRlZCBleHBsYW5hdG9yeSBwb3dlciBmb3Igc3Vydml2YWwgbW9kZWwgKFLCsiA8IDAuMDUpLCBidXQgbWF5IHN0aWxsIGhhdmUgZ29vZCBwcmVkaWN0aXZlIHZhbHVlXG4iKQ0KICB9DQogIGNhdCgiXG4iKQ0KICANCiAgIyBQZW5hbHR5IHRlcm0gY291bnRzDQogIG51bV92YXJzIDwtIGxlbmd0aChjb2VmKG1vZGVsKSkNCiAgbnVtX3N0cmF0YSA8LSBsZW5ndGgoZ3JlcCgic3RyYXRhIiwgbmFtZXMobW9kZWwkbWVhbnMpKSkNCiAgDQogIGNhdCgiTW9kZWwgQ29tcGxleGl0eTpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgY2F0KCJOdW1iZXIgb2YgY29lZmZpY2llbnRzOiIsIG51bV92YXJzLCAiXG4iKQ0KICBjYXQoIk51bWJlciBvZiBzdHJhdGE6IiwgbnVtX3N0cmF0YSwgIlxuIikNCiAgY2F0KCJcbiIpDQogIA0KICAjIE1hY3JvIHZhcmlhYmxlIGNvbnRyaWJ1dGlvbiBhbmFseXNpcw0KICBpZihsZW5ndGgobWFjcm9fdGVybXMpID4gMCkgew0KICAgIGNhdCgiQ09OVFJJQlVUSU9OIE9GIE1BQ1JPRUNPTk9NSUMgVkFSSUFCTEVTOlxuIikNCiAgICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgICANCiAgICAjIEFJQyBjb250cmlidXRpb24NCiAgICBhaWNfZGlmZiA8LSBub19tYWNyb19haWMgLSBtb2RlbF9haWMNCiAgICBjYXQoIkFJQyBpbXByb3ZlbWVudCBmcm9tIG1hY3JvIHZhcmlhYmxlczoiLCByb3VuZChhaWNfZGlmZiwgMiksIA0KICAgICAgICBpZmVsc2UoYWljX2RpZmYgPiAwLCAiIChiZXR0ZXIgZml0IHdpdGggbWFjcm8gdmFyaWFibGVzKVxuIiwgIiAod29yc2UgZml0IHdpdGggbWFjcm8gdmFyaWFibGVzKVxuIikpDQogICAgDQogICAgIyBSLXNxdWFyZWQgY29udHJpYnV0aW9uDQogICAgcl9zcXVhcmVkX2RpZmYgPC0gcl9zcXVhcmVkIC0gbm9fbWFjcm9fcl9zcXVhcmVkDQogICAgY2F0KCJSLXNxdWFyZWQgaW1wcm92ZW1lbnQ6Iiwgcm91bmQocl9zcXVhcmVkX2RpZmYsIDQpLCANCiAgICAgICAgcGFzdGUwKCIoIiwgcm91bmQocl9zcXVhcmVkX2RpZmYgKiAxMDAgLyByX3NxdWFyZWQsIDEpLCAiJSBvZiBtb2RlbCdzIGV4cGxhbmF0b3J5IHBvd2VyKVxuIikpDQogICAgDQogICAgIyBBVUMgY29udHJpYnV0aW9uDQogICAgaWYoIWlzLm5hKGF1Y192YWx1ZSkgJiYgIWlzLm5hKG5vX21hY3JvX2F1YykpIHsNCiAgICAgIGF1Y19kaWZmIDwtIGF1Y192YWx1ZSAtIG5vX21hY3JvX2F1Yw0KICAgICAgY2F0KCJBVUMgaW1wcm92ZW1lbnQ6Iiwgcm91bmQoYXVjX2RpZmYsIDQpLCANCiAgICAgICAgICBpZmVsc2UoYXVjX2RpZmYgPiAwLCAiIChiZXR0ZXIgZGlzY3JpbWluYXRpb24gd2l0aCBtYWNybyB2YXJpYWJsZXMpXG4iLCANCiAgICAgICAgICAgICAgICAiICh3b3JzZSBkaXNjcmltaW5hdGlvbiB3aXRoIG1hY3JvIHZhcmlhYmxlcylcbiIpKQ0KICAgIH0NCiAgICANCiAgICAjIExpa2VsaWhvb2QgcmF0aW8gdGVzdCBmb3IgbmVzdGVkIG1vZGVscw0KICAgIGlmKG5vX21hY3JvX21vZGVsJGxvZ2xpa1syXSA8IG1vZGVsJGxvZ2xpa1syXSkgew0KICAgICAgbHJfdGVzdF9tYWNybyA8LSAyICogKG1vZGVsJGxvZ2xpa1syXSAtIG5vX21hY3JvX21vZGVsJGxvZ2xpa1syXSkNCiAgICAgIGRmX2RpZmYgPC0gbGVuZ3RoKGNvZWYobW9kZWwpKSAtIGxlbmd0aChjb2VmKG5vX21hY3JvX21vZGVsKSkNCiAgICAgIHBfdmFsdWUgPC0gMSAtIHBjaGlzcShscl90ZXN0X21hY3JvLCBkZiA9IGRmX2RpZmYpDQogICAgICANCiAgICAgIGNhdCgiTGlrZWxpaG9vZCByYXRpbyB0ZXN0IGZvciBtYWNybyB2YXJpYWJsZXM6IENoaS1zcXVhcmUgPSIsIHJvdW5kKGxyX3Rlc3RfbWFjcm8sIDIpLCANCiAgICAgICAgICAib24iLCBkZl9kaWZmLCAiZGYsIHAtdmFsdWUgPSIsIGZvcm1hdC5wdmFsKHBfdmFsdWUsIGRpZ2l0cyA9IDQpLCAiXG4iKQ0KICAgICAgDQogICAgICBpZihwX3ZhbHVlIDwgMC4wNSkgew0KICAgICAgICBjYXQoIkludGVycHJldGF0aW9uOiBNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBTSUdOSUZJQ0FOVExZIGltcHJvdmUgbW9kZWwgZml0IChwIDwgMC4wNSlcbiIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIkludGVycHJldGF0aW9uOiBNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBkbyBub3Qgc2lnbmlmaWNhbnRseSBpbXByb3ZlIG1vZGVsIGZpdCAocCA+PSAwLjA1KVxuIikNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgY2F0KCJcbk5vdGUgb24gRWNvbm9taWMgSW50ZXJwcmV0YXRpb246XG4iKQ0KICAgIGNhdCgiVGhlIGluY2x1c2lvbiBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBhbGxvd3MgdGhlIG1vZGVsIHRvIGFjY291bnQgZm9yXG4iKQ0KICAgIGNhdCgiYnJvYWRlciBlY29ub21pYyBjb25kaXRpb25zIHRoYXQgYWZmZWN0IGFsbCBjb21wYW5pZXMgc2ltdWx0YW5lb3VzbHksIHdoaWNoXG4iKQ0KICAgIGNhdCgiaGVscHMgZGlzdGluZ3Vpc2ggYmV0d2VlbiBmaXJtLXNwZWNpZmljIHJpc2sgZmFjdG9ycyBhbmQgc3lzdGVtYXRpYyByaXNrLlxuIikNCiAgfQ0KICANCiAgIyBSZXR1cm4gc3RhdGlzdGljcyBmb3IgZnV0dXJlIHVzZQ0KICByZXR1cm4obGlzdCgNCiAgICBhdWMgPSBhdWNfdmFsdWUsDQogICAgbHJfdGVzdCA9IG1vZGVsX3N1bW1hcnkkbG9ndGVzdCwNCiAgICByX3NxdWFyZWQgPSByX3NxdWFyZWQsDQogICAgYWljID0gbW9kZWxfYWljLA0KICAgIGJpYyA9IG1vZGVsX2JpYywNCiAgICBub19tYWNyb19hdWMgPSBub19tYWNyb19hdWMsDQogICAgbm9fbWFjcm9fcl9zcXVhcmVkID0gbm9fbWFjcm9fcl9zcXVhcmVkLA0KICAgIG5vX21hY3JvX2FpYyA9IG5vX21hY3JvX2FpYywNCiAgICBtYWNyb190ZXJtcyA9IG1hY3JvX3Rlcm1zDQogICkpDQp9DQoNCiMgQXBwbHkgZnVuY3Rpb24gdG8gYm90aCBtb2RlbHMNCmJhbmtydXB0Y3lfZml0X3N0YXRzIDwtIGFuYWx5emVfbW9kZWxfZml0KGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwsICJiYW5rcnVwdGN5IiwgImJhbmtydXB0Y3kiKQ0KYWNxdWlzaXRpb25fZml0X3N0YXRzIDwtIGFuYWx5emVfbW9kZWxfZml0KGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiYWNxdWlzaXRpb24iLCAiYWNxdWlzaXRpb24iKQ0KDQojIENvbXBhcmUgbW9kZWxzDQpjYXQoIlxuQ09NUEFSSVNPTiBPRiBNT0RFTCBGSVQ6XG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCmNhdCgiQVVDIChBcmVhIFVuZGVyIFJPQyBDdXJ2ZSk6XG4iKQ0KY2F0KCIgIEJhbmtydXB0Y3kgbW9kZWw6IiwgaWZlbHNlKGlzLm5hKGJhbmtydXB0Y3lfZml0X3N0YXRzJGF1YyksICJOQSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChiYW5rcnVwdGN5X2ZpdF9zdGF0cyRhdWMsIDQpKSwgIlxuIikNCmNhdCgiICBBY3F1aXNpdGlvbiBtb2RlbDoiLCBpZmVsc2UoaXMubmEoYWNxdWlzaXRpb25fZml0X3N0YXRzJGF1YyksICJOQSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoYWNxdWlzaXRpb25fZml0X3N0YXRzJGF1YywgNCkpLCAiXG5cbiIpDQoNCmNhdCgiQUlDIChsb3dlciBpcyBiZXR0ZXIpOlxuIikNCmNhdCgiICBCYW5rcnVwdGN5IG1vZGVsOiIsIHJvdW5kKGJhbmtydXB0Y3lfZml0X3N0YXRzJGFpYywgMiksICJcbiIpDQpjYXQoIiAgQWNxdWlzaXRpb24gbW9kZWw6Iiwgcm91bmQoYWNxdWlzaXRpb25fZml0X3N0YXRzJGFpYywgMiksICJcblxuIikNCg0KY2F0KCJQc2V1ZG8gUi1zcXVhcmVkOlxuIikNCmNhdCgiICBCYW5rcnVwdGN5IG1vZGVsOiIsIHJvdW5kKGJhbmtydXB0Y3lfZml0X3N0YXRzJHJfc3F1YXJlZCwgNCksICJcbiIpDQpjYXQoIiAgQWNxdWlzaXRpb24gbW9kZWw6Iiwgcm91bmQoYWNxdWlzaXRpb25fZml0X3N0YXRzJHJfc3F1YXJlZCwgNCksICJcblxuIikNCg0KIyBDb21wYXJlIGNvbnRyaWJ1dGlvbiBvZiBtYWNybyB2YXJpYWJsZXMgYmV0d2VlbiBtb2RlbHMNCmNhdCgiXG5DT01QQVJBVElWRSBJTVBBQ1QgT0YgTUFDUk9FQ09OT01JQyBWQVJJQUJMRVM6XG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIHBlcmNlbnRhZ2UgaW1wcm92ZW1lbnQNCnBjdF9pbXByb3ZlbWVudCA8LSBmdW5jdGlvbih3aXRoX21hY3JvLCB3aXRob3V0X21hY3JvKSB7DQogIGlmKGlzLm5hKHdpdGhfbWFjcm8pIHx8IGlzLm5hKHdpdGhvdXRfbWFjcm8pKSB7DQogICAgcmV0dXJuKE5BKQ0KICB9DQogIHJldHVybigod2l0aF9tYWNybyAtIHdpdGhvdXRfbWFjcm8pIC8gd2l0aG91dF9tYWNybyAqIDEwMCkNCn0NCg0KIyBDYWxjdWxhdGUgaW1wcm92ZW1lbnQgcGVyY2VudGFnZXMNCmJhbmtydXB0Y3lfYXVjX3BjdCA8LSBwY3RfaW1wcm92ZW1lbnQoYmFua3J1cHRjeV9maXRfc3RhdHMkYXVjLCBiYW5rcnVwdGN5X2ZpdF9zdGF0cyRub19tYWNyb19hdWMpDQphY3F1aXNpdGlvbl9hdWNfcGN0IDwtIHBjdF9pbXByb3ZlbWVudChhY3F1aXNpdGlvbl9maXRfc3RhdHMkYXVjLCBhY3F1aXNpdGlvbl9maXRfc3RhdHMkbm9fbWFjcm9fYXVjKQ0KDQpiYW5rcnVwdGN5X3IyX3BjdCA8LSBwY3RfaW1wcm92ZW1lbnQoYmFua3J1cHRjeV9maXRfc3RhdHMkcl9zcXVhcmVkLCBiYW5rcnVwdGN5X2ZpdF9zdGF0cyRub19tYWNyb19yX3NxdWFyZWQpDQphY3F1aXNpdGlvbl9yMl9wY3QgPC0gcGN0X2ltcHJvdmVtZW50KGFjcXVpc2l0aW9uX2ZpdF9zdGF0cyRyX3NxdWFyZWQsIGFjcXVpc2l0aW9uX2ZpdF9zdGF0cyRub19tYWNyb19yX3NxdWFyZWQpDQoNCmNhdCgiUmVsYXRpdmUgSW1wcm92ZW1lbnQgaW4gQVVDOlxuIikNCmNhdCgiICBCYW5rcnVwdGN5IG1vZGVsOiIsIGlmZWxzZShpcy5uYShiYW5rcnVwdGN5X2F1Y19wY3QpLCAiTkEiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHJvdW5kKGJhbmtydXB0Y3lfYXVjX3BjdCwgMiksICIlIikpLCAiXG4iKQ0KY2F0KCIgIEFjcXVpc2l0aW9uIG1vZGVsOiIsIGlmZWxzZShpcy5uYShhY3F1aXNpdGlvbl9hdWNfcGN0KSwgIk5BIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAocm91bmQoYWNxdWlzaXRpb25fYXVjX3BjdCwgMiksICIlIikpLCAiXG5cbiIpDQoNCmNhdCgiUmVsYXRpdmUgSW1wcm92ZW1lbnQgaW4gUi1zcXVhcmVkOlxuIikNCmNhdCgiICBCYW5rcnVwdGN5IG1vZGVsOiIsIGlmZWxzZShpcy5uYShiYW5rcnVwdGN5X3IyX3BjdCksICJOQSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAocm91bmQoYmFua3J1cHRjeV9yMl9wY3QsIDIpLCAiJSIpKSwgIlxuIikNCmNhdCgiICBBY3F1aXNpdGlvbiBtb2RlbDoiLCBpZmVsc2UoaXMubmEoYWNxdWlzaXRpb25fcjJfcGN0KSwgIk5BIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAocm91bmQoYWNxdWlzaXRpb25fcjJfcGN0LCAyKSwgIiUiKSksICJcblxuIikNCg0KIyBFY29ub21pYyBpbnRlcnByZXRhdGlvbiBvZiB0aGUgY29tcGFyaXNvbg0KY2F0KCJFY29ub21pYyBJbnRlcnByZXRhdGlvbjpcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCmlmKCFpcy5uYShiYW5rcnVwdGN5X3IyX3BjdCkgJiYgIWlzLm5hKGFjcXVpc2l0aW9uX3IyX3BjdCkpIHsNCiAgaWYoYmFua3J1cHRjeV9yMl9wY3QgPiBhY3F1aXNpdGlvbl9yMl9wY3QpIHsNCiAgICBjYXQoIk1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGhhdmUgYSBTVFJPTkdFUiBpbXBhY3Qgb24gYmFua3J1cHRjeSByaXNrIGNvbXBhcmVkIHRvIGFjcXVpc2l0aW9uIGxpa2VsaWhvb2QuXG4iKQ0KICAgIGNhdCgiVGhpcyBzdWdnZXN0cyB0aGF0IHN5c3RlbWF0aWMgZWNvbm9taWMgZmFjdG9ycyBwbGF5IGEgbW9yZSBzaWduaWZpY2FudCByb2xlIGluIGRldGVybWluaW5nXG4iKQ0KICAgIGNhdCgid2hlbiBjb21wYW5pZXMgZmFpbCB0aGFuIHdoZW4gdGhleSBhcmUgYWNxdWlyZWQuXG5cbiIpDQogIH0gZWxzZSBpZihhY3F1aXNpdGlvbl9yMl9wY3QgPiBiYW5rcnVwdGN5X3IyX3BjdCkgew0KICAgIGNhdCgiTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgaGF2ZSBhIFNUUk9OR0VSIGltcGFjdCBvbiBhY3F1aXNpdGlvbiBsaWtlbGlob29kIGNvbXBhcmVkIHRvIGJhbmtydXB0Y3kgcmlzay5cbiIpDQogICAgY2F0KCJUaGlzIHN1Z2dlc3RzIHRoYXQgbWFya2V0IGNvbmRpdGlvbnMgYW5kIGVjb25vbWljIGN5Y2xlcyBhcmUgbW9yZSBpbXBvcnRhbnQgZHJpdmVycyBvZlxuIikNCiAgICBjYXQoIk0mQSBhY3Rpdml0eSB0aGFuIHRoZXkgYXJlIGZvciBjb3Jwb3JhdGUgZmFpbHVyZXMuXG5cbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBoYXZlIGEgU0lNSUxBUiBpbXBhY3Qgb24gYm90aCBiYW5rcnVwdGN5IHJpc2sgYW5kIGFjcXVpc2l0aW9uIGxpa2VsaWhvb2QuXG4iKQ0KICAgIGNhdCgiVGhpcyBzdWdnZXN0cyB0aGF0IGVjb25vbWljIGNvbmRpdGlvbnMgaW5mbHVlbmNlIGJvdGggb3V0Y29tZXMgdG8gYSBjb21wYXJhYmxlIGRlZ3JlZS5cblxuIikNCiAgfQ0KfQ0KDQpjYXQoIkJyb2FkZXIgSW1wbGljYXRpb25zOlxuIikNCmNhdCgiLSBUaGVzZSByZXN1bHRzIGhlbHAgZGlzdGluZ3Vpc2ggYmV0d2VlbiBmaXJtLXNwZWNpZmljIGFuZCBzeXN0ZW1hdGljIHJpc2sgZmFjdG9yc1xuIikNCmNhdCgiLSBVbmRlcnN0YW5kaW5nIHRoZSBpbmZsdWVuY2Ugb2YgbWFjcm9lY29ub21pYyBjb25kaXRpb25zIGNhbiBpbXByb3ZlIHJpc2sgbWFuYWdlbWVudFxuIikNCmNhdCgiLSBUaGUgcmVsYXRpdmUgaW1wb3J0YW5jZSBvZiBtYWNybyBmYWN0b3JzIG1heSB2YXJ5IGFjcm9zcyBkaWZmZXJlbnQgaW5kdXN0cmllcyBhbmQgdGltZSBwZXJpb2RzXG4iKQ0KDQojIFNhdmUgdGhlIGZpdCBzdGF0aXN0aWNzIGZvciByZXBvcnRpbmcNCmZpdF9zdGF0aXN0aWNzIDwtIGxpc3QoDQogIGJhbmtydXB0Y3kgPSBiYW5rcnVwdGN5X2ZpdF9zdGF0cywNCiAgYWNxdWlzaXRpb24gPSBhY3F1aXNpdGlvbl9maXRfc3RhdHMNCikNCmBgYA0KDQpBZ2Fpbiwgd2Ugd2lsbCBjcmVhdGUgYSBmdW5jdGlvbiB0byB2aXN1YWxpemUgdGhlIG1vZGVsIGZpdCBzdGF0aXN0aWNzLCBpbmNsdWRpbmcgQVVDIGFuZCBSLXNxdWFyZWQgdmFsdWVzLCBhbmQgcHJvdmlkZSBkZXRhaWxlZCBpbnRlcnByZXRhdGlvbnMgYmFzZWQgb24gdGhlIG1vZGVsIHR5cGUgKGJhbmtydXB0Y3kgb3IgYWNxdWlzaXRpb24pLiBXZSB3aWxsIGFsc28gaW5jbHVkZSBhIHN1bW1hcnkgb2YgdGhlIG1hY3JvZWNvbm9taWMgdmFyaWFibGUgY29udHJpYnV0aW9ucyBmb3IgYm90aCBtb2RlbHMuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDExLjM6IFZhcmlhYmxlIEltcG9ydGFuY2Ugd2l0aCBGb2N1cyBvbiBNYWNyb2Vjb25vbWljIEZhY3RvcnMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTEuMzogVkFSSUFCTEUgSU1QT1JUQU5DRSBXSVRIIEZPQ1VTIE9OIE1BQ1JPRUNPTk9NSUMgRkFDVE9SU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIGFuZCB2aXN1YWxpemUgdmFyaWFibGUgaW1wb3J0YW5jZQ0KYW5hbHl6ZV92YXJpYWJsZV9pbXBvcnRhbmNlIDwtIGZ1bmN0aW9uKG1vZGVsLCBtb2RlbF9uYW1lKSB7DQogICMgRXh0cmFjdCBjb2VmZmljaWVudHMgYW5kIHN0YXRpc3RpY3MNCiAgY29lZl9zdW1tYXJ5IDwtIHN1bW1hcnkobW9kZWwpJGNvZWZmaWNpZW50cw0KICANCiAgIyBDcmVhdGUgaW1wb3J0YW5jZSBkYXRhIGZyYW1lDQogIGltcG9ydGFuY2UgPC0gZGF0YS5mcmFtZSgNCiAgICBWYXJpYWJsZSA9IHJvd25hbWVzKGNvZWZfc3VtbWFyeSksDQogICAgQ29lZmZpY2llbnQgPSBjb2VmX3N1bW1hcnlbLCAiY29lZiJdLA0KICAgIFNFID0gY29lZl9zdW1tYXJ5WywgInNlKGNvZWYpIl0sDQogICAgWl9TY29yZSA9IGNvZWZfc3VtbWFyeVssICJ6Il0sDQogICAgUF9WYWx1ZSA9IGNvZWZfc3VtbWFyeVssICJQcig+fHp8KSJdLA0KICAgIFdhbGRfQ2hpU3EgPSBjb2VmX3N1bW1hcnlbLCAieiJdXjIsDQogICAgSFIgPSBleHAoY29lZl9zdW1tYXJ5WywgImNvZWYiXSksDQogICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQogICkNCiAgDQogICMgU2VwYXJhdGUgbWFpbiBlZmZlY3RzIGZyb20gdGltZSBpbnRlcmFjdGlvbnMNCiAgbWFpbl9lZmZlY3RzIDwtIGltcG9ydGFuY2VbIWdyZXBsKCI6dHQiLCBpbXBvcnRhbmNlJFZhcmlhYmxlKSwgXQ0KICB0aW1lX2VmZmVjdHMgPC0gaW1wb3J0YW5jZVtncmVwbCgiOnR0IiwgaW1wb3J0YW5jZSRWYXJpYWJsZSksIF0NCiAgDQogICMgQWRkIHZhcmlhYmxlIHR5cGUgY2xhc3NpZmljYXRpb24NCiAgbWFpbl9lZmZlY3RzJFR5cGUgPC0gIkZpbmFuY2lhbCIgIyBEZWZhdWx0DQogIA0KICAjIElkZW50aWZ5IG1hY3JvIHZhcmlhYmxlcw0KICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAiZ2RwX2RlZmxhdG9yIiwgInVuZW1wbG95ZW1lbnQiKQ0KICBmb3IodmFyIGluIG1haW5fZWZmZWN0cyRWYXJpYWJsZSkgew0KICAgIGlmKHZhciAlaW4lIG1hY3JvX3ZhcnMpIHsNCiAgICAgIG1haW5fZWZmZWN0cyRUeXBlW21haW5fZWZmZWN0cyRWYXJpYWJsZSA9PSB2YXJdIDwtICJNYWNybyINCiAgICB9DQogIH0NCiAgDQogICMgQ2FsY3VsYXRlIHN0YW5kYXJkaXplZCBpbXBvcnRhbmNlIChhYnNvbHV0ZSBaLXNjb3JlKQ0KICBtYWluX2VmZmVjdHMkQWJzX1ogPC0gYWJzKG1haW5fZWZmZWN0cyRaX1Njb3JlKQ0KICBtYWluX2VmZmVjdHMkU3RhbmRhcmRpemVkX0ltcG9ydGFuY2UgPC0gMTAwICogbWFpbl9lZmZlY3RzJEFic19aIC8gc3VtKG1haW5fZWZmZWN0cyRBYnNfWikNCiAgDQogICMgU29ydCBieSBpbXBvcnRhbmNlDQogIG1haW5fZWZmZWN0cyA8LSBtYWluX2VmZmVjdHNbb3JkZXIobWFpbl9lZmZlY3RzJEFic19aLCBkZWNyZWFzaW5nID0gVFJVRSksIF0NCiAgDQogICMgUHJpbnQgcmVzdWx0cw0KICBjYXQoIlxuPT09IFZBUklBQkxFIElNUE9SVEFOQ0UgRk9SIiwgdG91cHBlcihtb2RlbF9uYW1lKSwgIk1PREVMID09PVxuXG4iKQ0KICANCiAgIyBUYWJsZSBvZiB0b3AgdmFyaWFibGVzDQogIGNhdCgiUmFua2VkIFZhcmlhYmxlIEltcG9ydGFuY2UgKGJhc2VkIG9uIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSk6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgdG9wX3ZhcnMgPC0gaGVhZChtYWluX2VmZmVjdHMsIDEwKSAgIyBTaG93IHRvcCAxMA0KICANCiAgIyBGb3JtYXQgYW5kIHByaW50IHRoZSB0YWJsZQ0KICBmb3IoaSBpbiAxOm5yb3codG9wX3ZhcnMpKSB7DQogICAgdmFyIDwtIHRvcF92YXJzJFZhcmlhYmxlW2ldDQogICAgdHlwZSA8LSB0b3BfdmFycyRUeXBlW2ldDQogICAgY29lZiA8LSB0b3BfdmFycyRDb2VmZmljaWVudFtpXQ0KICAgIGhyIDwtIHRvcF92YXJzJEhSW2ldDQogICAgcF92YWwgPC0gdG9wX3ZhcnMkUF9WYWx1ZVtpXQ0KICAgIGltcG9ydGFuY2VfcGN0IDwtIHRvcF92YXJzJFN0YW5kYXJkaXplZF9JbXBvcnRhbmNlW2ldDQogICAgDQogICAgIyBGb3JtYXQgZWZmZWN0IGRpcmVjdGlvbg0KICAgIGRpcmVjdGlvbiA8LSBpZmVsc2UoY29lZiA+IDAsICJpbmNyZWFzZXMiLCAiZGVjcmVhc2VzIikNCiAgICANCiAgICAjIEZvcm1hdCBwLXZhbHVlDQogICAgc2lnbmlmaWNhbmNlIDwtICIiDQogICAgaWYocF92YWwgPCAwLjAwMSkgc2lnbmlmaWNhbmNlIDwtICIqKioiDQogICAgZWxzZSBpZihwX3ZhbCA8IDAuMDEpIHNpZ25pZmljYW5jZSA8LSAiKioiDQogICAgZWxzZSBpZihwX3ZhbCA8IDAuMDUpIHNpZ25pZmljYW5jZSA8LSAiKiINCiAgICBlbHNlIGlmKHBfdmFsIDwgMC4xKSBzaWduaWZpY2FuY2UgPC0gIi4iDQogICAgDQogICAgIyBDaGVjayBpZiB0aGlzIHZhcmlhYmxlIGhhcyBhIHRpbWUgaW50ZXJhY3Rpb24NCiAgICBoYXNfdGltZV9pbnQgPC0gcGFzdGUwKHZhciwgIjp0dCIpICVpbiUgdGltZV9lZmZlY3RzJFZhcmlhYmxlDQogICAgdGltZV9ub3RlIDwtIGlmZWxzZShoYXNfdGltZV9pbnQsICIgKGhhcyB0aW1lLXZhcnlpbmcgZWZmZWN0KSIsICIiKQ0KICAgIA0KICAgICMgRm9ybWF0IGRpZmZlcmVudGx5IGZvciBtYWNybyB2YXJpYWJsZXMNCiAgICBpZih0eXBlID09ICJNYWNybyIpIHsNCiAgICAgIGNhdChzcHJpbnRmKCIlMmQuICUtMTVzOiBJbXBvcnRhbmNlID0gJTUuMWYlJSwgSFIgPSAlNS4yZiwgJXMgJXMgcmlzayAlcyVzIFtNQUNST0VDT05PTUlDIFZBUklBQkxFXVxuIiwgDQogICAgICAgICAgICAgICAgICBpLCB2YXIsIGltcG9ydGFuY2VfcGN0LCBociwgZGlyZWN0aW9uLCBtb2RlbF9uYW1lLCBzaWduaWZpY2FuY2UsIHRpbWVfbm90ZSkpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdChzcHJpbnRmKCIlMmQuICUtMTVzOiBJbXBvcnRhbmNlID0gJTUuMWYlJSwgSFIgPSAlNS4yZiwgJXMgJXMgcmlzayAlcyVzXG4iLCANCiAgICAgICAgICAgICAgICAgIGksIHZhciwgaW1wb3J0YW5jZV9wY3QsIGhyLCBkaXJlY3Rpb24sIG1vZGVsX25hbWUsIHNpZ25pZmljYW5jZSwgdGltZV9ub3RlKSkNCiAgICB9DQogIH0NCiAgDQogIGNhdCgiXG5TaWduaWZpY2FuY2UgY29kZXM6IDAgJyoqKicgMC4wMDEgJyoqJyAwLjAxICcqJyAwLjA1ICcuJyAwLjEgJyAnIDFcblxuIikNCiAgDQogICMgVmlzdWFsaXphdGlvbiBwbGFjZWhvbGRlciAoaW4gYWN0dWFsIGNvZGUsIHdvdWxkIGNyZWF0ZSBhIHBsb3QpDQogIGNhdCgiVG9wIDUgVmFyaWFibGVzIGJ5IEltcG9ydGFuY2U6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICANCiAgZm9yKGkgaW4gMTptaW4oNSwgbnJvdyhtYWluX2VmZmVjdHMpKSkgew0KICAgIHZhciA8LSBtYWluX2VmZmVjdHMkVmFyaWFibGVbaV0NCiAgICB0eXBlIDwtIG1haW5fZWZmZWN0cyRUeXBlW2ldDQogICAgaW1wb3J0YW5jZSA8LSBtYWluX2VmZmVjdHMkU3RhbmRhcmRpemVkX0ltcG9ydGFuY2VbaV0NCiAgICBiYXIgPC0gcGFzdGUocmVwKCIjIiwgcm91bmQoaW1wb3J0YW5jZS8yKSksIGNvbGxhcHNlPSIiKQ0KICAgIA0KICAgICMgQWRkIGluZGljYXRvciBmb3IgbWFjcm8gdmFyaWFibGVzDQogICAgdmFyX2xhYmVsIDwtIHZhcg0KICAgIGlmKHR5cGUgPT0gIk1hY3JvIikgew0KICAgICAgdmFyX2xhYmVsIDwtIHBhc3RlMCh2YXIsICIgKE0pIikNCiAgICB9DQogICAgDQogICAgY2F0KHNwcmludGYoIiUtMTVzIHwlcyAlNS4xZiUlXG4iLCB2YXJfbGFiZWwsIGJhciwgaW1wb3J0YW5jZSkpDQogIH0NCiAgDQogICMgU2VwYXJhdGUgYW5hbHlzaXMgb2YgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgcmVnYXJkbGVzcyBvZiByYW5rDQogIG1hY3JvX3Jvd3MgPC0gbWFpbl9lZmZlY3RzW21haW5fZWZmZWN0cyRUeXBlID09ICJNYWNybyIsIF0NCiAgDQogIGlmKG5yb3cobWFjcm9fcm93cykgPiAwKSB7DQogICAgY2F0KCJcbkFOQUxZU0lTIE9GIE1BQ1JPRUNPTk9NSUMgVkFSSUFCTEVTOlxuIikNCiAgICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogICAgDQogICAgZm9yKGkgaW4gMTpucm93KG1hY3JvX3Jvd3MpKSB7DQogICAgICB2YXIgPC0gbWFjcm9fcm93cyRWYXJpYWJsZVtpXQ0KICAgICAgY29lZiA8LSBtYWNyb19yb3dzJENvZWZmaWNpZW50W2ldDQogICAgICBociA8LSBtYWNyb19yb3dzJEhSW2ldDQogICAgICBwX3ZhbCA8LSBtYWNyb19yb3dzJFBfVmFsdWVbaV0NCiAgICAgIHJhbmsgPC0gd2hpY2gobWFpbl9lZmZlY3RzJFZhcmlhYmxlID09IHZhcikNCiAgICAgIGltcG9ydGFuY2VfcGN0IDwtIG1hY3JvX3Jvd3MkU3RhbmRhcmRpemVkX0ltcG9ydGFuY2VbaV0NCiAgICAgIA0KICAgICAgIyBGb3JtYXQgZWZmZWN0IGFuZCBpbnRlcnByZXRhdGlvbg0KICAgICAgZGlyZWN0aW9uIDwtIGlmZWxzZShjb2VmID4gMCwgImluY3JlYXNlcyIsICJkZWNyZWFzZXMiKQ0KICAgICAgDQogICAgICBjYXQoc3ByaW50ZigiJS0xNXM6IE92ZXJhbGwgcmFuayAjJWQgKEltcG9ydGFuY2UgPSAlNS4xZiUlKVxuIiwgdmFyLCByYW5rLCBpbXBvcnRhbmNlX3BjdCkpDQogICAgICBjYXQoc3ByaW50ZigiICBFZmZlY3Q6ICVzICVzIHJpc2sgKEhSID0gJTUuMmYsIHAgPSAlNy40ZilcbiIsIA0KICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uLCBtb2RlbF9uYW1lLCBociwgcF92YWwpKQ0KICAgICAgDQogICAgICAjIEVjb25vbWljIGludGVycHJldGF0aW9uDQogICAgICBjYXQoIiAgRWNvbm9taWMgaW50ZXJwcmV0YXRpb246ICIpDQogICAgICBpZih2YXIgPT0gImdkcF9ncm93dGgiKSB7DQogICAgICAgIGlmKGNvZWYgPiAwKSB7DQogICAgICAgICAgY2F0KCJTdHJvbmdlciBlY29ub21pYyBncm93dGggaXMgYXNzb2NpYXRlZCB3aXRoIEhJR0hFUiIsIG1vZGVsX25hbWUsICJyaXNrXG4iKQ0KICAgICAgICAgIGNhdCgiICBUaGlzIG1heSBpbmRpY2F0ZSB0aGF0IGR1cmluZyBlY29ub21pYyBleHBhbnNpb25zLCBlaXRoZXI6XG4iKQ0KICAgICAgICAgIGNhdCgiICAtIENvbXBhbmllcyB0YWtlIG9uIG1vcmUgcmlzaywgbGVhZGluZyB0byB2dWxuZXJhYmlsaXR5XG4iKQ0KICAgICAgICAgIGNhdCgiICAtIE1vcmUgY29tcGV0aXRpdmUgcHJlc3N1cmUgZHVyaW5nIGdyb3d0aCBwZXJpb2RzXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiU3Ryb25nZXIgZWNvbm9taWMgZ3Jvd3RoIGlzIGFzc29jaWF0ZWQgd2l0aCBMT1dFUiIsIG1vZGVsX25hbWUsICJyaXNrXG4iKQ0KICAgICAgICAgIGNhdCgiICBUaGlzIGFsaWducyB3aXRoIGVjb25vbWljIHRoZW9yeSB0aGF0IGdyb3dpbmcgZWNvbm9taWVzIHN1cHBvcnQgYnVzaW5lc3Mgc3VjY2Vzc1xuIikNCiAgICAgICAgICBjYXQoIiAgYW5kIHByb3ZpZGUgbW9yZSBvcHBvcnR1bml0aWVzIGZvciBzdHJ1Z2dsaW5nIGZpcm1zXG4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2UgaWYodmFyID09ICJnZHBfZGVmbGF0b3IiKSB7DQogICAgICAgIGlmKGNvZWYgPiAwKSB7DQogICAgICAgICAgY2F0KCJIaWdoZXIgaW5mbGF0aW9uIGlzIGFzc29jaWF0ZWQgd2l0aCBISUdIRVIiLCBtb2RlbF9uYW1lLCAicmlza1xuIikNCiAgICAgICAgICBjYXQoIiAgVGhpcyBzdWdnZXN0cyBpbmZsYXRpb24gbWF5IGNyZWF0ZSBjaGFsbGVuZ2VzIHRocm91Z2g6XG4iKQ0KICAgICAgICAgIGNhdCgiICAtIEluY3JlYXNlZCBpbnB1dCBjb3N0c1xuIikNCiAgICAgICAgICBjYXQoIiAgLSBQb3RlbnRpYWwgbW9uZXRhcnkgdGlnaHRlbmluZyByZXNwb25zZXNcbiIpDQogICAgICAgICAgY2F0KCIgIC0gUmVkdWNlZCBjb25zdW1lciBwdXJjaGFzaW5nIHBvd2VyXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiSGlnaGVyIGluZmxhdGlvbiBpcyBhc3NvY2lhdGVkIHdpdGggTE9XRVIiLCBtb2RlbF9uYW1lLCAicmlza1xuIikNCiAgICAgICAgICBjYXQoIiAgVGhpcyBjb3VudGVyaW50dWl0aXZlIGZpbmRpbmcgbWlnaHQgcmVmbGVjdDpcbiIpDQogICAgICAgICAgY2F0KCIgIC0gSW5mbGF0aW9uIGJlbmVmaXRpbmcgZGVidG9ycyBieSByZWR1Y2luZyByZWFsIGRlYnQgYnVyZGVuXG4iKQ0KICAgICAgICAgIGNhdCgiICAtIFByaWNlIGluY3JlYXNlcyBoZWxwaW5nIGNvbXBhbnkgcmV2ZW51ZXMgaW4gY2VydGFpbiBzZWN0b3JzXG4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2UgaWYodmFyID09ICJ1bmVtcGxveWVtZW50Iikgew0KICAgICAgICBpZihjb2VmID4gMCkgew0KICAgICAgICAgIGNhdCgiSGlnaGVyIHVuZW1wbG95bWVudCBpcyBhc3NvY2lhdGVkIHdpdGggSElHSEVSIiwgbW9kZWxfbmFtZSwgInJpc2tcbiIpDQogICAgICAgICAgY2F0KCIgIFRoaXMgYWxpZ25zIHdpdGggZWNvbm9taWMgdGhlb3J5IHRoYXQgd2Vha2VyIGxhYm9yIG1hcmtldHMgc2lnbmFsOlxuIikNCiAgICAgICAgICBjYXQoIiAgLSBPdmVyYWxsIGVjb25vbWljIHdlYWtuZXNzXG4iKQ0KICAgICAgICAgIGNhdCgiICAtIFJlZHVjZWQgY29uc3VtZXIgc3BlbmRpbmdcbiIpDQogICAgICAgICAgY2F0KCIgIC0gQnJvYWRlciBjb250cmFjdGlvbiBpbiBidXNpbmVzcyBhY3Rpdml0eVxuIikNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBjYXQoIkhpZ2hlciB1bmVtcGxveW1lbnQgaXMgYXNzb2NpYXRlZCB3aXRoIExPV0VSIiwgbW9kZWxfbmFtZSwgInJpc2tcbiIpDQogICAgICAgICAgY2F0KCIgIFRoaXMgY291bnRlcmludHVpdGl2ZSBmaW5kaW5nIG1pZ2h0IHJlZmxlY3Q6XG4iKQ0KICAgICAgICAgIGNhdCgiICAtIExhYm9yIGNvc3Qgc2F2aW5ncyBmb3IgYnVzaW5lc3NlcyBkdXJpbmcgaGlnaCB1bmVtcGxveW1lbnRcbiIpDQogICAgICAgICAgY2F0KCIgIC0gU3Vydml2YWwgYmlhcyB3aGVyZSBvbmx5IHN0cm9uZ2VyIGNvbXBhbmllcyByZW1haW4gYWZ0ZXIgZWNvbm9taWMgZG93bnR1cm5zXG4iKQ0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgICMgQ2hlY2sgZm9yIHRpbWUgaW50ZXJhY3Rpb24gYW5kIGFkZCBpbnRlcnByZXRhdGlvbg0KICAgICAgaGFzX3RpbWVfaW50IDwtIHBhc3RlMCh2YXIsICI6dHQiKSAlaW4lIHRpbWVfZWZmZWN0cyRWYXJpYWJsZQ0KICAgICAgaWYoaGFzX3RpbWVfaW50KSB7DQogICAgICAgIHRpbWVfcm93IDwtIHRpbWVfZWZmZWN0c1t0aW1lX2VmZmVjdHMkVmFyaWFibGUgPT0gcGFzdGUwKHZhciwgIjp0dCIpLCBdDQogICAgICAgIHRpbWVfY29lZiA8LSB0aW1lX3JvdyRDb2VmZmljaWVudA0KICAgICAgICB0aW1lX3AgPC0gdGltZV9yb3ckUF9WYWx1ZQ0KICAgICAgICANCiAgICAgICAgaWYodGltZV9wIDwgMC4xKSB7DQogICAgICAgICAgY2F0KCJcbiAgVGltZS12YXJ5aW5nIGVmZmVjdDogIikNCiAgICAgICAgICANCiAgICAgICAgICBpZih0aW1lX2NvZWYgPiAwICYmIGNvZWYgPiAwKSB7DQogICAgICAgICAgICBjYXQoIlRoZSBhZHZlcnNlIGltcGFjdCBvZiB0aGlzIGVjb25vbWljIGZhY3RvciBJTkNSRUFTRVMgYXMgY29tcGFuaWVzIGFnZVxuIikNCiAgICAgICAgICAgIGNhdCgiICBNYXR1cmUgY29tcGFuaWVzIG1heSBiZSBsZXNzIGFkYXB0YWJsZSB0byB0aGlzIGVjb25vbWljIGNvbmRpdGlvblxuIikNCiAgICAgICAgICB9IGVsc2UgaWYodGltZV9jb2VmIDwgMCAmJiBjb2VmIDwgMCkgew0KICAgICAgICAgICAgY2F0KCJUaGUgcHJvdGVjdGl2ZSBlZmZlY3Qgb2YgdGhpcyBlY29ub21pYyBmYWN0b3IgU1RSRU5HVEhFTlMgYXMgY29tcGFuaWVzIGFnZVxuIikNCiAgICAgICAgICAgIGNhdCgiICBNYXR1cmUgY29tcGFuaWVzIG1heSBiZW5lZml0IG1vcmUgZnJvbSB0aGlzIGVjb25vbWljIGNvbmRpdGlvblxuIikNCiAgICAgICAgICB9IGVsc2UgaWYodGltZV9jb2VmIDwgMCAmJiBjb2VmID4gMCkgew0KICAgICAgICAgICAgY2F0KCJUaGUgYWR2ZXJzZSBpbXBhY3Qgb2YgdGhpcyBlY29ub21pYyBmYWN0b3IgREVDUkVBU0VTIGFzIGNvbXBhbmllcyBhZ2VcbiIpDQogICAgICAgICAgICBjYXQoIiAgWW91bmcgY29tcGFuaWVzIGFyZSBtb3JlIHZ1bG5lcmFibGUgdG8gdGhpcyBlY29ub21pYyBjb25kaXRpb25cbiIpDQogICAgICAgICAgfSBlbHNlIGlmKHRpbWVfY29lZiA+IDAgJiYgY29lZiA8IDApIHsNCiAgICAgICAgICAgIGNhdCgiVGhlIHByb3RlY3RpdmUgZWZmZWN0IG9mIHRoaXMgZWNvbm9taWMgZmFjdG9yIFdFQUtFTlMgYXMgY29tcGFuaWVzIGFnZVxuIikNCiAgICAgICAgICAgIGNhdCgiICBZb3VuZyBjb21wYW5pZXMgYmVuZWZpdCBtb3JlIGZyb20gdGhpcyBlY29ub21pYyBjb25kaXRpb25cbiIpDQogICAgICAgICAgfQ0KICAgICAgICAgIA0KICAgICAgICAgICMgQ2FsY3VsYXRlIGVmZmVjdCBhdCBkaWZmZXJlbnQgdGltZSBwb2ludHMNCiAgICAgICAgICB0aW1lcyA8LSBjKDEsIDMsIDUsIDEwKQ0KICAgICAgICAgIGNhdCgiICBJbXBhY3QgYXQgZGlmZmVyZW50IGNvbXBhbnkgYWdlczpcbiIpDQogICAgICAgICAgZm9yKHQgaW4gdGltZXMpIHsNCiAgICAgICAgICAgIGVmZmVjdF9hdF90IDwtIGNvZWYgKyB0aW1lX2NvZWYgKiB0DQogICAgICAgICAgICBocl9hdF90IDwtIGV4cChlZmZlY3RfYXRfdCkNCiAgICAgICAgICAgIGFnZV9kZXNjIDwtIHBhc3RlMCgiWWVhciAiLCB0KQ0KICAgICAgICAgICAgY2F0KHNwcmludGYoIiAgICAlLTdzOiBIUiA9ICU1LjNmICglcyByaXNrIGJ5ICU1LjFmJSUpXG4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGFnZV9kZXNjLCBocl9hdF90LCANCiAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShlZmZlY3RfYXRfdCA+IDAsICJpbmNyZWFzZXMiLCAiZGVjcmVhc2VzIiksDQogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZWZmZWN0X2F0X3QgPiAwLCAoaHJfYXRfdCAtIDEpICogMTAwLCAoMSAtIGhyX2F0X3QpICogMTAwKSkpDQogICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICB9DQogICAgICBjYXQoIlxuIikNCiAgICB9DQogIH0gZWxzZSB7DQogICAgY2F0KCJcbk5vIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGluIHRoaXMgbW9kZWwuXG5cbiIpDQogIH0NCiAgDQogICMgQW5hbHl6ZSB0aW1lLXZhcnlpbmcgZWZmZWN0cyBzZXBhcmF0ZWx5DQogIGlmKG5yb3codGltZV9lZmZlY3RzKSA+IDApIHsNCiAgICBjYXQoIlxuVGltZS1WYXJ5aW5nIEVmZmVjdHMgQW5hbHlzaXM6XG4iKQ0KICAgIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogICAgDQogICAgIyBTZXBhcmF0ZSBtYWNybyBhbmQgZmluYW5jaWFsIHRpbWUgZWZmZWN0cw0KICAgIGZpbl90aW1lX2VmZmVjdHMgPC0gdGltZV9lZmZlY3RzWyFzYXBwbHkodGltZV9lZmZlY3RzJFZhcmlhYmxlLCBmdW5jdGlvbih2KSB7DQogICAgICB2YXJfbmFtZSA8LSBzdWIoIjp0dCIsICIiLCB2KQ0KICAgICAgcmV0dXJuKHZhcl9uYW1lICVpbiUgbWFjcm9fdmFycykNCiAgICB9KSwgXQ0KICAgIA0KICAgIG1hY3JvX3RpbWVfZWZmZWN0cyA8LSB0aW1lX2VmZmVjdHNbc2FwcGx5KHRpbWVfZWZmZWN0cyRWYXJpYWJsZSwgZnVuY3Rpb24odikgew0KICAgICAgdmFyX25hbWUgPC0gc3ViKCI6dHQiLCAiIiwgdikNCiAgICAgIHJldHVybih2YXJfbmFtZSAlaW4lIG1hY3JvX3ZhcnMpDQogICAgfSksIF0NCiAgICANCiAgICAjIFByaW50IGZpbmFuY2lhbCB2YXJpYWJsZSB0aW1lIGVmZmVjdHMNCiAgICBpZihucm93KGZpbl90aW1lX2VmZmVjdHMpID4gMCkgew0KICAgICAgY2F0KCJcblRpbWUtdmFyeWluZyBlZmZlY3RzIGZvciBGSU5BTkNJQUwgdmFyaWFibGVzOlxuIikNCiAgICAgIA0KICAgICAgZm9yKGkgaW4gMTpucm93KGZpbl90aW1lX2VmZmVjdHMpKSB7DQogICAgICAgIHRpbWVfdmFyIDwtIGZpbl90aW1lX2VmZmVjdHMkVmFyaWFibGVbaV0NCiAgICAgICAgdmFyIDwtIHN1YigiOnR0IiwgIiIsIHRpbWVfdmFyKQ0KICAgICAgICB0aW1lX2NvZWYgPC0gZmluX3RpbWVfZWZmZWN0cyRDb2VmZmljaWVudFtpXQ0KICAgICAgICB0aW1lX3AgPC0gZmluX3RpbWVfZWZmZWN0cyRQX1ZhbHVlW2ldDQogICAgICAgIA0KICAgICAgICAjIE9ubHkgc2hvdyBzaWduaWZpY2FudCB0aW1lIGludGVyYWN0aW9ucw0KICAgICAgICBpZih0aW1lX3AgPCAwLjEpIHsgICMgU2hvd2luZyBtYXJnaW5hbGx5IHNpZ25pZmljYW50IG9uZXMgdG9vDQogICAgICAgICAgbWFpbl9jb2VmIDwtIG1haW5fZWZmZWN0cyRDb2VmZmljaWVudFttYWluX2VmZmVjdHMkVmFyaWFibGUgPT0gdmFyXQ0KICAgICAgICAgIA0KICAgICAgICAgIGNhdCgiXG5UaW1lLXZhcnlpbmcgaW1wb3J0YW5jZSBmb3IiLCB2YXIsICI6XG4iKQ0KICAgICAgICAgIA0KICAgICAgICAgICMgRWZmZWN0IGRpcmVjdGlvbiBhdCBiYXNlbGluZQ0KICAgICAgICAgIGJhc2VsaW5lX2VmZmVjdCA8LSBpZmVsc2UobWFpbl9jb2VmID4gMCwgImluY3JlYXNlcyIsICJkZWNyZWFzZXMiKQ0KICAgICAgICAgIA0KICAgICAgICAgICMgRWZmZWN0IG9mIHRpbWUgaW50ZXJhY3Rpb24NCiAgICAgICAgICB0aW1lX2VmZmVjdCA8LSBpZmVsc2UodGltZV9jb2VmID4gMCwgInN0cmVuZ3RoZW5zIiwgIndlYWtlbnMiKQ0KICAgICAgICAgIGlmKChtYWluX2NvZWYgPiAwICYmIHRpbWVfY29lZiA8IDApIHx8IChtYWluX2NvZWYgPCAwICYmIHRpbWVfY29lZiA+IDApKSB7DQogICAgICAgICAgICB0aW1lX2VmZmVjdCA8LSAid2Vha2VucyINCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgdGltZV9lZmZlY3QgPC0gInN0cmVuZ3RoZW5zIg0KICAgICAgICAgIH0NCiAgICAgICAgICANCiAgICAgICAgICBjYXQoIiAgQmFzZWxpbmUgZWZmZWN0OiAiLCBiYXNlbGluZV9lZmZlY3QsICIgcmlzayAoSFIgPSAiLCByb3VuZChleHAobWFpbl9jb2VmKSwgMiksICIpXG4iLCBzZXA9IiIpDQogICAgICAgICAgY2F0KCIgIFRpbWUgZWZmZWN0OiBFZmZlY3QgIiwgdGltZV9lZmZlY3QsICIgb3ZlciB0aW1lIChIUiBwZXIgeWVhciA9ICIsIA0KICAgICAgICAgICAgICByb3VuZChleHAodGltZV9jb2VmKSwgMyksICIsIHAgPSAiLCBmb3JtYXQucHZhbCh0aW1lX3AsIGRpZ2l0cyA9IDMpLCAiKVxuIiwgc2VwPSIiKQ0KICAgICAgICAgIA0KICAgICAgICAgICMgQ2FsY3VsYXRlIGVmZmVjdCBhdCBkaWZmZXJlbnQgdGltZSBwb2ludHMNCiAgICAgICAgICB0aW1lcyA8LSBjKDEsIDMsIDUsIDEwKQ0KICAgICAgICAgIGNhdCgiICBFZmZlY3QgYXQgZGlmZmVyZW50IHRpbWUgcG9pbnRzOlxuIikNCiAgICAgICAgICBmb3IodCBpbiB0aW1lcykgew0KICAgICAgICAgICAgZWZmZWN0X2F0X3QgPC0gbWFpbl9jb2VmICsgdGltZV9jb2VmICogdA0KICAgICAgICAgICAgaHJfYXRfdCA8LSBleHAoZWZmZWN0X2F0X3QpDQogICAgICAgICAgICBjYXQoIiAgQXQgdCA9IiwgdCwgInllYXJzOiBIUiA9Iiwgcm91bmQoaHJfYXRfdCwgMyksIA0KICAgICAgICAgICAgICAgICJFZmZlY3QgPSIsIGlmZWxzZShlZmZlY3RfYXRfdCA+IDAsICJpbmNyZWFzZXMiLCAiZGVjcmVhc2VzIiksIA0KICAgICAgICAgICAgICAgIG1vZGVsX25hbWUsICJyaXNrXG4iKQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICANCiAgIyBSZXR1cm4gaW1wb3J0YW5jZSBkYXRhIGZyYW1lIHdpdGggdHlwZSBpbmZvcm1hdGlvbg0KICByZXR1cm4obGlzdChtYWluX2VmZmVjdHMgPSBtYWluX2VmZmVjdHMsIHRpbWVfZWZmZWN0cyA9IHRpbWVfZWZmZWN0cykpDQp9DQoNCiMgQXBwbHkgZnVuY3Rpb24gdG8gYm90aCBtb2RlbHMNCmJhbmtydXB0Y3lfaW1wb3J0YW5jZSA8LSBhbmFseXplX3ZhcmlhYmxlX2ltcG9ydGFuY2UoZmluYWxfYmFua3J1cHRjeV9tb2RlbCwgImJhbmtydXB0Y3kiKQ0KYWNxdWlzaXRpb25faW1wb3J0YW5jZSA8LSBhbmFseXplX3ZhcmlhYmxlX2ltcG9ydGFuY2UoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwsICJhY3F1aXNpdGlvbiIpDQoNCiMgQ29tcGFyZSB2YXJpYWJsZSBpbXBvcnRhbmNlIGJldHdlZW4gbW9kZWxzDQpjYXQoIlxuQ09NUEFSSU5HIEtFWSBQUkVESUNUT1JTIEJFVFdFRU4gTU9ERUxTOlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgSWRlbnRpZnkgY29tbW9uIHRvcCBwcmVkaWN0b3JzDQpiYW5rX3RvcCA8LSBoZWFkKGJhbmtydXB0Y3lfaW1wb3J0YW5jZSRtYWluX2VmZmVjdHMkVmFyaWFibGUsIDEwKQ0KYWNxX3RvcCA8LSBoZWFkKGFjcXVpc2l0aW9uX2ltcG9ydGFuY2UkbWFpbl9lZmZlY3RzJFZhcmlhYmxlLCAxMCkNCmNvbW1vbl9wcmVkaWN0b3JzIDwtIGludGVyc2VjdChiYW5rX3RvcCwgYWNxX3RvcCkNCg0KaWYobGVuZ3RoKGNvbW1vbl9wcmVkaWN0b3JzKSA+IDApIHsNCiAgY2F0KCJWYXJpYWJsZXMgaW1wb3J0YW50IGZvciBib3RoIGJhbmtydXB0Y3kgYW5kIGFjcXVpc2l0aW9uIHByZWRpY3Rpb246XG4iKQ0KICBmb3IodmFyIGluIGNvbW1vbl9wcmVkaWN0b3JzKSB7DQogICAgYmFua19yYW5rIDwtIHdoaWNoKGJhbmtydXB0Y3lfaW1wb3J0YW5jZSRtYWluX2VmZmVjdHMkVmFyaWFibGUgPT0gdmFyKQ0KICAgIGFjcV9yYW5rIDwtIHdoaWNoKGFjcXVpc2l0aW9uX2ltcG9ydGFuY2UkbWFpbl9lZmZlY3RzJFZhcmlhYmxlID09IHZhcikNCiAgICBiYW5rX2NvZWYgPC0gYmFua3J1cHRjeV9pbXBvcnRhbmNlJG1haW5fZWZmZWN0cyRDb2VmZmljaWVudFtiYW5rX3JhbmtdDQogICAgYWNxX2NvZWYgPC0gYWNxdWlzaXRpb25faW1wb3J0YW5jZSRtYWluX2VmZmVjdHMkQ29lZmZpY2llbnRbYWNxX3JhbmtdDQogICAgDQogICAgIyBDaGVjayBpZiBpdCdzIGEgbWFjcm8gdmFyaWFibGUNCiAgICBpc19tYWNybyA8LSB2YXIgJWluJSBjKCJnZHBfZ3Jvd3RoIiwgImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IikNCiAgICBtYWNyb19sYWJlbCA8LSBpZmVsc2UoaXNfbWFjcm8sICIgW01BQ1JPXSIsICIiKQ0KICAgIA0KICAgICMgQ2hlY2sgZm9yIG9wcG9zaXRlIGVmZmVjdHMNCiAgICBpZihzaWduKGJhbmtfY29lZikgIT0gc2lnbihhY3FfY29lZikpIHsNCiAgICAgIGNhdChzcHJpbnRmKCItICUtMTVzOiBSYW5rICMlZCBpbiBiYW5rcnVwdGN5IG1vZGVsLCAjJWQgaW4gYWNxdWlzaXRpb24gbW9kZWwlc1xuICAqKiogSGFzIE9QUE9TSVRFIGVmZmVjdHM6ICVzIGJhbmtydXB0Y3kgcmlzayBidXQgJXMgYWNxdWlzaXRpb24gcmlzayAqKipcbiIsIA0KICAgICAgICAgICAgICAgIHZhciwgYmFua19yYW5rLCBhY3FfcmFuaywgbWFjcm9fbGFiZWwsDQogICAgICAgICAgICAgICAgaWZlbHNlKGJhbmtfY29lZiA+IDAsICJpbmNyZWFzZXMiLCAiZGVjcmVhc2VzIiksDQogICAgICAgICAgICAgICAgaWZlbHNlKGFjcV9jb2VmID4gMCwgImluY3JlYXNlcyIsICJkZWNyZWFzZXMiKSkpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdChzcHJpbnRmKCItICUtMTVzOiBSYW5rICMlZCBpbiBiYW5rcnVwdGN5IG1vZGVsLCAjJWQgaW4gYWNxdWlzaXRpb24gbW9kZWwlc1xuIiwgDQogICAgICAgICAgICAgICAgdmFyLCBiYW5rX3JhbmssIGFjcV9yYW5rLCBtYWNyb19sYWJlbCkpDQogICAgfQ0KICB9DQp9IGVsc2Ugew0KICBjYXQoIk5vIGNvbW1vbiB2YXJpYWJsZXMgYW1vbmcgdG9wIDEwIHByZWRpY3RvcnMgb2YgYm90aCBtb2RlbHMuXG4iKQ0KfQ0KDQpjYXQoIlxuVW5pcXVlIHByZWRpY3RvcnMgZm9yIGJhbmtydXB0Y3kgKG5vdCBpbiB0b3AgMTAgZm9yIGFjcXVpc2l0aW9uKTpcbiIpDQp1bmlxdWVfYmFuayA8LSBzZXRkaWZmKGJhbmtfdG9wLCBhY3FfdG9wKQ0KZm9yKHZhciBpbiB1bmlxdWVfYmFuaykgew0KICBiYW5rX3JhbmsgPC0gd2hpY2goYmFua3J1cHRjeV9pbXBvcnRhbmNlJG1haW5fZWZmZWN0cyRWYXJpYWJsZSA9PSB2YXIpDQogIA0KICAjIENoZWNrIGlmIGl0J3MgYSBtYWNybyB2YXJpYWJsZQ0KICBpc19tYWNybyA8LSB2YXIgJWluJSBjKCJnZHBfZ3Jvd3RoIiwgImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IikNCiAgbWFjcm9fbGFiZWwgPC0gaWZlbHNlKGlzX21hY3JvLCAiIFtNQUNST10iLCAiIikNCiAgDQogIGNhdChzcHJpbnRmKCItICUtMTVzOiBSYW5rICMlZCBpbiBiYW5rcnVwdGN5IG1vZGVsJXNcbiIsIHZhciwgYmFua19yYW5rLCBtYWNyb19sYWJlbCkpDQp9DQoNCmNhdCgiXG5VbmlxdWUgcHJlZGljdG9ycyBmb3IgYWNxdWlzaXRpb24gKG5vdCBpbiB0b3AgMTAgZm9yIGJhbmtydXB0Y3kpOlxuIikNCnVuaXF1ZV9hY3EgPC0gc2V0ZGlmZihhY3FfdG9wLCBiYW5rX3RvcCkNCmZvcih2YXIgaW4gdW5pcXVlX2FjcSkgew0KICBhY3FfcmFuayA8LSB3aGljaChhY3F1aXNpdGlvbl9pbXBvcnRhbmNlJG1haW5fZWZmZWN0cyRWYXJpYWJsZSA9PSB2YXIpDQogIA0KICAjIENoZWNrIGlmIGl0J3MgYSBtYWNybyB2YXJpYWJsZQ0KICBpc19tYWNybyA8LSB2YXIgJWluJSBjKCJnZHBfZ3Jvd3RoIiwgImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IikNCiAgbWFjcm9fbGFiZWwgPC0gaWZlbHNlKGlzX21hY3JvLCAiIFtNQUNST10iLCAiIikNCiAgDQogIGNhdChzcHJpbnRmKCItICUtMTVzOiBSYW5rICMlZCBpbiBhY3F1aXNpdGlvbiBtb2RlbCVzXG4iLCB2YXIsIGFjcV9yYW5rLCBtYWNyb19sYWJlbCkpDQp9DQoNCiMgU3BlY2lmaWMgY29tcGFyaXNvbiBvZiBtYWNybyB2YXJpYWJsZXMgYmV0d2VlbiBtb2RlbHMNCmNhdCgiXG5DT01QQVJBVElWRSBBTkFMWVNJUyBPRiBNQUNST0VDT05PTUlDIFZBUklBQkxFUzpcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCm1hY3JvX3ZhcnMgPC0gYygiZ2RwX2dyb3d0aCIsICJnZHBfZGVmbGF0b3IiLCAidW5lbXBsb3llbWVudCIpDQpiYW5rX21hY3JvIDwtIGJhbmtydXB0Y3lfaW1wb3J0YW5jZSRtYWluX2VmZmVjdHNbYmFua3J1cHRjeV9pbXBvcnRhbmNlJG1haW5fZWZmZWN0cyRWYXJpYWJsZSAlaW4lIG1hY3JvX3ZhcnMsIF0NCmFjcV9tYWNybyA8LSBhY3F1aXNpdGlvbl9pbXBvcnRhbmNlJG1haW5fZWZmZWN0c1thY3F1aXNpdGlvbl9pbXBvcnRhbmNlJG1haW5fZWZmZWN0cyRWYXJpYWJsZSAlaW4lIG1hY3JvX3ZhcnMsIF0NCg0KaWYobnJvdyhiYW5rX21hY3JvKSA+IDAgfHwgbnJvdyhhY3FfbWFjcm8pID4gMCkgew0KICAjIENvbWJpbmUgYWxsIG1hY3JvIHZhcmlhYmxlcyBmcm9tIGJvdGggbW9kZWxzDQogIGFsbF9tYWNybyA8LSB1bmlxdWUoYyhiYW5rX21hY3JvJFZhcmlhYmxlLCBhY3FfbWFjcm8kVmFyaWFibGUpKQ0KICANCiAgZm9yKHZhciBpbiBhbGxfbWFjcm8pIHsNCiAgICBjYXQoIlxuTWFjcm9lY29ub21pYyB2YXJpYWJsZToiLCB2YXIsICJcbiIpDQogICAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICAgIA0KICAgICMgR2V0IHN0YXRzIGZvciBiYW5rcnVwdGN5IG1vZGVsDQogICAgaWYodmFyICVpbiUgYmFua19tYWNybyRWYXJpYWJsZSkgew0KICAgICAgYmFua19yb3cgPC0gYmFua19tYWNyb1tiYW5rX21hY3JvJFZhcmlhYmxlID09IHZhciwgXQ0KICAgICAgYmFua19yYW5rIDwtIHdoaWNoKGJhbmtydXB0Y3lfaW1wb3J0YW5jZSRtYWluX2VmZmVjdHMkVmFyaWFibGUgPT0gdmFyKQ0KICAgICAgYmFua19ociA8LSBiYW5rX3JvdyRIUg0KICAgICAgYmFua19jb2VmIDwtIGJhbmtfcm93JENvZWZmaWNpZW50DQogICAgICBiYW5rX3AgPC0gYmFua19yb3ckUF9WYWx1ZQ0KICAgICAgYmFua19pbXAgPC0gYmFua19yb3ckU3RhbmRhcmRpemVkX0ltcG9ydGFuY2UNCiAgICAgIA0KICAgICAgY2F0KCJCYW5rcnVwdGN5IG1vZGVsOlxuIikNCiAgICAgIGNhdCgiICBSYW5rOiIsIGJhbmtfcmFuaywgIm9mIiwgbnJvdyhiYW5rcnVwdGN5X2ltcG9ydGFuY2UkbWFpbl9lZmZlY3RzKSwgIlxuIikNCiAgICAgIGNhdCgiICBJbXBvcnRhbmNlOiIsIHJvdW5kKGJhbmtfaW1wLCAxKSwgIiVcbiIpDQogICAgICBjYXQoIiAgRWZmZWN0OiIsIGlmZWxzZShiYW5rX2NvZWYgPiAwLCAiSW5jcmVhc2VzIiwgIkRlY3JlYXNlcyIpLCANCiAgICAgICAgICAicmlzayAoSFIgPSIsIHJvdW5kKGJhbmtfaHIsIDIpLCAiKVxuIikNCiAgICAgIGNhdCgiICBTaWduaWZpY2FuY2U6IiwgaWZlbHNlKGJhbmtfcCA8IDAuMDUsICJTaWduaWZpY2FudCIsICJOb3Qgc2lnbmlmaWNhbnQiKSwgDQogICAgICAgICAgIihwID0iLCBmb3JtYXQucHZhbChiYW5rX3AsIGRpZ2l0cyA9IDMpLCAiKVxuIikNCiAgICAgIA0KICAgICAgIyBDaGVjayBmb3IgdGltZSBpbnRlcmFjdGlvbg0KICAgICAgYmFua190aW1lX3ZhciA8LSBwYXN0ZTAodmFyLCAiOnR0IikgJWluJSBiYW5rcnVwdGN5X2ltcG9ydGFuY2UkdGltZV9lZmZlY3RzJFZhcmlhYmxlDQogICAgICBpZihiYW5rX3RpbWVfdmFyKSB7DQogICAgICAgIGJhbmtfdGltZV9yb3cgPC0gYmFua3J1cHRjeV9pbXBvcnRhbmNlJHRpbWVfZWZmZWN0c1tiYW5rcnVwdGN5X2ltcG9ydGFuY2UkdGltZV9lZmZlY3RzJFZhcmlhYmxlID09IHBhc3RlMCh2YXIsICI6dHQiKSwgXQ0KICAgICAgICBiYW5rX3RpbWVfY29lZiA8LSBiYW5rX3RpbWVfcm93JENvZWZmaWNpZW50DQogICAgICAgIGJhbmtfdGltZV9wIDwtIGJhbmtfdGltZV9yb3ckUF9WYWx1ZQ0KICAgICAgICANCiAgICAgICAgaWYoYmFua190aW1lX3AgPCAwLjEpIHsNCiAgICAgICAgICBjYXQoIiAgVGltZS12YXJ5aW5nIGVmZmVjdDoiLCBpZmVsc2UoYmFua190aW1lX2NvZWYgPiAwLCAiU3RyZW5ndGhlbnMiLCAiV2Vha2VucyIpLCANCiAgICAgICAgICAgICAgIndpdGggYWdlIChwID0iLCBmb3JtYXQucHZhbChiYW5rX3RpbWVfcCwgZGlnaXRzID0gMyksICIpXG4iKQ0KICAgICAgICB9DQogICAgICB9DQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiQmFua3J1cHRjeSBtb2RlbDogTm90IGluY2x1ZGVkXG4iKQ0KICAgIH0NCiAgICANCiAgICAjIEdldCBzdGF0cyBmb3IgYWNxdWlzaXRpb24gbW9kZWwNCiAgICBpZih2YXIgJWluJSBhY3FfbWFjcm8kVmFyaWFibGUpIHsNCiAgICAgIGFjcV9yb3cgPC0gYWNxX21hY3JvW2FjcV9tYWNybyRWYXJpYWJsZSA9PSB2YXIsIF0NCiAgICAgIGFjcV9yYW5rIDwtIHdoaWNoKGFjcXVpc2l0aW9uX2ltcG9ydGFuY2UkbWFpbl9lZmZlY3RzJFZhcmlhYmxlID09IHZhcikNCiAgICAgIGFjcV9ociA8LSBhY3Ffcm93JEhSDQogICAgICBhY3FfY29lZiA8LSBhY3Ffcm93JENvZWZmaWNpZW50DQogICAgICBhY3FfcCA8LSBhY3Ffcm93JFBfVmFsdWUNCiAgICAgIGFjcV9pbXAgPC0gYWNxX3JvdyRTdGFuZGFyZGl6ZWRfSW1wb3J0YW5jZQ0KICAgICAgDQogICAgICBjYXQoIlxuQWNxdWlzaXRpb24gbW9kZWw6XG4iKQ0KICAgICAgY2F0KCIgIFJhbms6IiwgYWNxX3JhbmssICJvZiIsIG5yb3coYWNxdWlzaXRpb25faW1wb3J0YW5jZSRtYWluX2VmZmVjdHMpLCAiXG4iKQ0KICAgICAgY2F0KCIgIEltcG9ydGFuY2U6Iiwgcm91bmQoYWNxX2ltcCwgMSksICIlXG4iKQ0KICAgICAgY2F0KCIgIEVmZmVjdDoiLCBpZmVsc2UoYWNxX2NvZWYgPiAwLCAiSW5jcmVhc2VzIiwgIkRlY3JlYXNlcyIpLCANCiAgICAgICAgICAicmlzayAoSFIgPSIsIHJvdW5kKGFjcV9ociwgMiksICIpXG4iKQ0KICAgICAgY2F0KCIgIFNpZ25pZmljYW5jZToiLCBpZmVsc2UoYWNxX3AgPCAwLjA1LCAiU2lnbmlmaWNhbnQiLCAiTm90IHNpZ25pZmljYW50IiksIA0KICAgICAgICAgICIocCA9IiwgZm9ybWF0LnB2YWwoYWNxX3AsIGRpZ2l0cyA9IDMpLCAiKVxuIikNCiAgICAgIA0KICAgICAgIyBDaGVjayBmb3IgdGltZSBpbnRlcmFjdGlvbg0KICAgICAgYWNxX3RpbWVfdmFyIDwtIHBhc3RlMCh2YXIsICI6dHQiKSAlaW4lIGFjcXVpc2l0aW9uX2ltcG9ydGFuY2UkdGltZV9lZmZlY3RzJFZhcmlhYmxlDQogICAgICBpZihhY3FfdGltZV92YXIpIHsNCiAgICAgICAgYWNxX3RpbWVfcm93IDwtIGFjcXVpc2l0aW9uX2ltcG9ydGFuY2UkdGltZV9lZmZlY3RzW2FjcXVpc2l0aW9uX2ltcG9ydGFuY2UkdGltZV9lZmZlY3RzJFZhcmlhYmxlID09IHBhc3RlMCh2YXIsICI6dHQiKSwgXQ0KICAgICAgICBhY3FfdGltZV9jb2VmIDwtIGFjcV90aW1lX3JvdyRDb2VmZmljaWVudA0KICAgICAgICBhY3FfdGltZV9wIDwtIGFjcV90aW1lX3JvdyRQX1ZhbHVlDQogICAgICAgIA0KICAgICAgICBpZihhY3FfdGltZV9wIDwgMC4xKSB7DQogICAgICAgICAgY2F0KCIgIFRpbWUtdmFyeWluZyBlZmZlY3Q6IiwgaWZlbHNlKGFjcV90aW1lX2NvZWYgPiAwLCAiU3RyZW5ndGhlbnMiLCAiV2Vha2VucyIpLCANCiAgICAgICAgICAgICAgIndpdGggYWdlIChwID0iLCBmb3JtYXQucHZhbChhY3FfdGltZV9wLCBkaWdpdHMgPSAzKSwgIilcbiIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCJcbkFjcXVpc2l0aW9uIG1vZGVsOiBOb3QgaW5jbHVkZWRcbiIpDQogICAgfQ0KICAgIA0KICAgICMgQ29tcGFyaXNvbiBiZXR3ZWVuIG1vZGVscyBpZiB2YXJpYWJsZSBpcyBpbiBib3RoDQogICAgaWYodmFyICVpbiUgYmFua19tYWNybyRWYXJpYWJsZSAmJiB2YXIgJWluJSBhY3FfbWFjcm8kVmFyaWFibGUpIHsNCiAgICAgIGJhbmtfcm93IDwtIGJhbmtfbWFjcm9bYmFua19tYWNybyRWYXJpYWJsZSA9PSB2YXIsIF0NCiAgICAgIGFjcV9yb3cgPC0gYWNxX21hY3JvW2FjcV9tYWNybyRWYXJpYWJsZSA9PSB2YXIsIF0NCiAgICAgIA0KICAgICAgY2F0KCJcbkNvbXBhcmlzb246XG4iKQ0KICAgICAgDQogICAgICAjIENvbXBhcmUgZGlyZWN0aW9ucw0KICAgICAgaWYoc2lnbihiYW5rX3JvdyRDb2VmZmljaWVudCkgPT0gc2lnbihhY3Ffcm93JENvZWZmaWNpZW50KSkgew0KICAgICAgICBjYXQoIiAgRGlyZWN0aW9uOiBTYW1lIGRpcmVjdGlvbiBpbiBib3RoIG1vZGVscyAoIiwgDQogICAgICAgICAgICBpZmVsc2UoYmFua19yb3ckQ29lZmZpY2llbnQgPiAwLCAiaW5jcmVhc2VzIiwgImRlY3JlYXNlcyIpLCAiIHJpc2spXG4iLCBzZXA9IiIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIiAgRGlyZWN0aW9uOiBPUFBPU0lURSBlZmZlY3RzIGJldHdlZW4gbW9kZWxzXG4iKQ0KICAgICAgICBjYXQoIiAgICAtICIsIGlmZWxzZShiYW5rX3JvdyRDb2VmZmljaWVudCA+IDAsICJJbmNyZWFzZXMiLCAiRGVjcmVhc2VzIiksIA0KICAgICAgICAgICAgIiBiYW5rcnVwdGN5IHJpc2tcbiIsIHNlcD0iIikNCiAgICAgICAgY2F0KCIgICAgLSAiLCBpZmVsc2UoYWNxX3JvdyRDb2VmZmljaWVudCA+IDAsICJJbmNyZWFzZXMiLCAiRGVjcmVhc2VzIiksIA0KICAgICAgICAgICAgIiBhY3F1aXNpdGlvbiByaXNrXG4iLCBzZXA9IiIpDQogICAgICB9DQogICAgICANCiAgICAgICMgQ29tcGFyZSByZWxhdGl2ZSBpbXBvcnRhbmNlDQogICAgICBpZihiYW5rX3JvdyRTdGFuZGFyZGl6ZWRfSW1wb3J0YW5jZSA+IGFjcV9yb3ckU3RhbmRhcmRpemVkX0ltcG9ydGFuY2UpIHsNCiAgICAgICAgcmF0aW8gPC0gYmFua19yb3ckU3RhbmRhcmRpemVkX0ltcG9ydGFuY2UgLyBhY3Ffcm93JFN0YW5kYXJkaXplZF9JbXBvcnRhbmNlDQogICAgICAgIGNhdCgiICBJbXBvcnRhbmNlOiAiLCByb3VuZChyYXRpbywgMSksICJ4IG1vcmUgaW1wb3J0YW50IGZvciBiYW5rcnVwdGN5IHRoYW4gYWNxdWlzaXRpb25cbiIsIHNlcD0iIikNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHJhdGlvIDwtIGFjcV9yb3ckU3RhbmRhcmRpemVkX0ltcG9ydGFuY2UgLyBiYW5rX3JvdyRTdGFuZGFyZGl6ZWRfSW1wb3J0YW5jZQ0KICAgICAgICBjYXQoIiAgSW1wb3J0YW5jZTogIiwgcm91bmQocmF0aW8sIDEpLCAieCBtb3JlIGltcG9ydGFudCBmb3IgYWNxdWlzaXRpb24gdGhhbiBiYW5rcnVwdGN5XG4iLCBzZXA9IiIpDQogICAgICB9DQogICAgICANCiAgICAgICMgRWNvbm9taWMgaW50ZXJwcmV0YXRpb24NCiAgICAgIGNhdCgiICBFY29ub21pYyBpbnNpZ2h0OiAiKQ0KICAgICAgaWYodmFyID09ICJnZHBfZ3Jvd3RoIikgew0KICAgICAgICBpZihzaWduKGJhbmtfcm93JENvZWZmaWNpZW50KSAhPSBzaWduKGFjcV9yb3ckQ29lZmZpY2llbnQpKSB7DQogICAgICAgICAgY2F0KCJFY29ub21pYyBncm93dGggaGFzIGRpdmVyZ2VudCBlZmZlY3RzIG9uIGNvbXBhbnkgb3V0Y29tZXNcbiIpDQogICAgICAgICAgaWYoYmFua19yb3ckQ29lZmZpY2llbnQgPCAwICYmIGFjcV9yb3ckQ29lZmZpY2llbnQgPiAwKSB7DQogICAgICAgICAgICBjYXQoIiAgICAtIEdyb3d0aCBwZXJpb2RzIHJlZHVjZSBiYW5rcnVwdGN5IHJpc2sgYnV0IGluY3JlYXNlIGFjcXVpc2l0aW9uIGFjdGl2aXR5XG4iKQ0KICAgICAgICAgICAgY2F0KCIgICAgLSBUaGlzIHN1Z2dlc3RzIGFjcXVpc2l0aW9ucyBhcmUgbW9yZSBvcHBvcnR1bml0eS1kcml2ZW4gZHVyaW5nIGV4cGFuc2lvbnNcbiIpDQogICAgICAgICAgICBjYXQoIiAgICAtIFdoaWxlIGJhbmtydXB0Y2llcyBhcmUgbW9yZSB0aHJlYXQtZHJpdmVuIGR1cmluZyBjb250cmFjdGlvbnNcbiIpDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIGNhdCgiICAgIC0gVW51c3VhbCBwYXR0ZXJuOiBHcm93dGggaW5jcmVhc2VzIGJhbmtydXB0Y3kgcmlzayBidXQgcmVkdWNlcyBhY3F1aXNpdGlvbnNcbiIpDQogICAgICAgICAgICBjYXQoIiAgICAtIFRoaXMgbWlnaHQgaW5kaWNhdGUgbWFya2V0IGRpc3RvcnRpb25zIG9yIGluZHVzdHJ5LXNwZWNpZmljIGVmZmVjdHNcbiIpDQogICAgICAgICAgfQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiRWNvbm9taWMgZ3Jvd3RoIGFmZmVjdHMgYm90aCBvdXRjb21lcyBpbiB0aGUgc2FtZSBkaXJlY3Rpb25cbiIpDQogICAgICAgICAgY2F0KCIgICAgLSBUaGlzIHN1Z2dlc3RzIHN5c3RlbWljIGVjb25vbWljIGZvcmNlcyBkcml2ZSBib3RoIHByb2Nlc3NlcyBzaW1pbGFybHlcbiIpDQogICAgICAgIH0NCiAgICAgIH0gZWxzZSBpZih2YXIgPT0gInVuZW1wbG95ZW1lbnQiKSB7DQogICAgICAgIGlmKHNpZ24oYmFua19yb3ckQ29lZmZpY2llbnQpICE9IHNpZ24oYWNxX3JvdyRDb2VmZmljaWVudCkpIHsNCiAgICAgICAgICBjYXQoIkxhYm9yIG1hcmtldCBjb25kaXRpb25zIGhhdmUgZGl2ZXJnZW50IGVmZmVjdHMgb24gY29tcGFueSBvdXRjb21lc1xuIikNCiAgICAgICAgICBpZihiYW5rX3JvdyRDb2VmZmljaWVudCA+IDAgJiYgYWNxX3JvdyRDb2VmZmljaWVudCA8IDApIHsNCiAgICAgICAgICAgIGNhdCgiICAgIC0gSGlnaCB1bmVtcGxveW1lbnQgaW5jcmVhc2VzIGJhbmtydXB0Y3kgcmlzayBidXQgcmVkdWNlcyBhY3F1aXNpdGlvbiBhY3Rpdml0eVxuIikNCiAgICAgICAgICAgIGNhdCgiICAgIC0gVGhpcyBhbGlnbnMgd2l0aCBlY29ub21pYyB0aGVvcnkgdGhhdCBkaXN0cmVzc2VkIE0mQSBkZWNyZWFzZXMgaW4gcmVjZXNzaW9uc1xuIikNCiAgICAgICAgICAgIGNhdCgiICAgIC0gUG9vciBsYWJvciBtYXJrZXRzIG1heSBsaW1pdCBidXllcnMnIGFiaWxpdHkgdG8gZmluYW5jZSBhY3F1aXNpdGlvbnNcbiIpDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIGNhdCgiICAgIC0gVW51c3VhbCBwYXR0ZXJuOiBIaWdoIHVuZW1wbG95bWVudCByZWR1Y2VzIGJhbmtydXB0Y3kgcmlzayBidXQgaW5jcmVhc2VzIGFjcXVpc2l0aW9uc1xuIikNCiAgICAgICAgICAgIGNhdCgiICAgIC0gVGhpcyBtaWdodCBpbmRpY2F0ZSBpbmR1c3RyeS1zcGVjaWZpYyBsYWJvciBtYXJrZXQgZWZmZWN0c1xuIikNCiAgICAgICAgICB9DQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCJMYWJvciBtYXJrZXQgY29uZGl0aW9ucyBhZmZlY3QgYm90aCBvdXRjb21lcyBpbiB0aGUgc2FtZSBkaXJlY3Rpb25cbiIpDQogICAgICAgICAgY2F0KCIgICAgLSBUaGlzIHN1Z2dlc3RzIHVuZW1wbG95bWVudCBzZXJ2ZXMgYXMgYSBnZW5lcmFsIGVjb25vbWljIGluZGljYXRvclxuIikNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIGlmKHZhciA9PSAiZ2RwX2RlZmxhdG9yIikgew0KICAgICAgICBpZihzaWduKGJhbmtfcm93JENvZWZmaWNpZW50KSAhPSBzaWduKGFjcV9yb3ckQ29lZmZpY2llbnQpKSB7DQogICAgICAgICAgY2F0KCJJbmZsYXRpb24gaGFzIGRpdmVyZ2VudCBlZmZlY3RzIG9uIGNvbXBhbnkgb3V0Y29tZXNcbiIpDQogICAgICAgICAgaWYoYmFua19yb3ckQ29lZmZpY2llbnQgPiAwICYmIGFjcV9yb3ckQ29lZmZpY2llbnQgPCAwKSB7DQogICAgICAgICAgICBjYXQoIiAgICAtIEhpZ2hlciBpbmZsYXRpb24gaW5jcmVhc2VzIGJhbmtydXB0Y3kgcmlzayBidXQgcmVkdWNlcyBhY3F1aXNpdGlvbiBhY3Rpdml0eVxuIikNCiAgICAgICAgICAgIGNhdCgiICAgIC0gVGhpcyBzdWdnZXN0cyBpbmZsYXRpb24gY3JlYXRlcyBvcGVyYXRpb25hbCBjaGFsbGVuZ2VzIHdoaWxlIGRlcHJlc3NpbmcgTSZBIG1hcmtldHNcbiIpDQogICAgICAgICAgICBjYXQoIiAgICAtIEluZmxhdGlvbiB1bmNlcnRhaW50eSBtYXkgbGltaXQgYnV5ZXJzJyB3aWxsaW5nbmVzcyB0byBtYWtlIGxvbmctdGVybSBjb21taXRtZW50c1xuIikNCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgY2F0KCIgICAgLSBVbnVzdWFsIHBhdHRlcm46IEluZmxhdGlvbiByZWR1Y2VzIGJhbmtydXB0Y3kgcmlzayBidXQgaW5jcmVhc2VzIGFjcXVpc2l0aW9uc1xuIikNCiAgICAgICAgICAgIGNhdCgiICAgIC0gVGhpcyBtaWdodCBpbmRpY2F0ZSBpbmZsYXRpb24gYmVuZWZpdHMgZGVidG9ycyB3aGlsZSBjcmVhdGluZyBidXlpbmcgb3Bwb3J0dW5pdGllc1xuIikNCiAgICAgICAgICB9DQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCJJbmZsYXRpb24gYWZmZWN0cyBib3RoIG91dGNvbWVzIGluIHRoZSBzYW1lIGRpcmVjdGlvblxuIikNCiAgICAgICAgICBjYXQoIiAgICAtIFRoaXMgc3VnZ2VzdHMgaW5mbGF0aW9uIHNlcnZlcyBhcyBhIGdlbmVyYWwgZWNvbm9taWMgaW5kaWNhdG9yXG4iKQ0KICAgICAgICB9DQogICAgICB9DQogICAgfQ0KICAgIGNhdCgiXG4iKQ0KICB9DQp9IGVsc2Ugew0KICBjYXQoIk5vIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGluIGVpdGhlciBtb2RlbC5cbiIpDQp9DQoNCiMgU2F2ZSB2YXJpYWJsZSBpbXBvcnRhbmNlIGluZm9ybWF0aW9uDQp2YXJpYWJsZV9pbXBvcnRhbmNlIDwtIGxpc3QoDQogIGJhbmtydXB0Y3kgPSBiYW5rcnVwdGN5X2ltcG9ydGFuY2UsDQogIGFjcXVpc2l0aW9uID0gYWNxdWlzaXRpb25faW1wb3J0YW5jZQ0KKQ0KYGBgDQoNCk5vdCBzdXJlIGFib3V0IHRoaXMgYXBwcm9hY2ggISBCdXQsIHdlIHByZWRpY3QgdGhlIHJpc2sgc2NvcmVzIGZvciB0aGUgbGFzdCBvYnNlcnZhdGlvbiBvZiBlYWNoIGNvbXBhbnkgaW4gdGhlIGRhdGFzZXQuIFdlIHRoZW4gdXNlIHRoZXNlIHNjb3JlcyB0byBldmFsdWF0ZSB0aGUgbW9kZWwncyBwZXJmb3JtYW5jZSB1c2luZyBST0MgYW5hbHlzaXMuIFRoZSBmdW5jdGlvbiBhbHNvIGFsbG93cyBmb3IgYSBjb21wYXJpc29uIG9mIHRoZSBtb2RlbCB3aXRoIGFuZCB3aXRob3V0IG1hY3JvZWNvbm9taWMgdmFyaWFibGVzLCBwcm92aWRpbmcgaW5zaWdodHMgaW50byB0aGVpciBjb250cmlidXRpb24gdG8gcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTEuNDogUHJlZGljdGlvbiBQZXJmb3JtYW5jZSBBbmFseXNpcyB3aXRoIE1hY3JvIEltcGFjdCBBc3Nlc3NtZW50DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjExLjQ6IFBSRURJQ1RJT04gUEVSRk9STUFOQ0UgQU5BTFlTSVMgV0lUSCBNQUNSTyBJTVBBQ1QgQVNTRVNTTUVOVFxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gZXZhbHVhdGUgcHJlZGljdGlvbiBwZXJmb3JtYW5jZQ0KZXZhbHVhdGVfcHJlZGljdGlvbl9wZXJmb3JtYW5jZSA8LSBmdW5jdGlvbihtb2RlbCwgbW9kZWxfbmFtZSwgZXZlbnRfdHlwZSwgYXNzZXNzX21hY3JvX2ltcGFjdCA9IFRSVUUpIHsNCiAgY2F0KCJcbkV2YWx1YXRpbmcgcHJlZGljdGlvbiBwZXJmb3JtYW5jZSBmb3IiLCBtb2RlbF9uYW1lLCAibW9kZWwuLi5cbiIpDQogIA0KICAjIEdldCBsYXN0IG9ic2VydmF0aW9uIGZvciBlYWNoIGNvbXBhbnkNCiAgbGFzdF9vYnNfZGF0YSA8LSBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICANCiAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMgZnJvbSBmdWxsIG1vZGVsDQogIHJpc2tfc2NvcmVzIDwtIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBsYXN0X29ic19kYXRhLCB0eXBlID0gInJpc2siKQ0KICANCiAgIyBBZGQgc2NvcmVzIHRvIGRhdGENCiAgbGFzdF9vYnNfZGF0YSRyaXNrX3Njb3JlIDwtIHJpc2tfc2NvcmVzDQogIA0KICAjIElmIHJlcXVlc3RlZCwgY3JlYXRlIG1vZGVsIHdpdGhvdXQgbWFjcm8gdmFyaWFibGVzIGZvciBjb21wYXJpc29uDQogIGlmKGFzc2Vzc19tYWNyb19pbXBhY3QpIHsNCiAgICBjYXQoIlxuQXNzZXNzaW5nIGltcGFjdCBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBvbiBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLi4uXG4iKQ0KICAgIA0KICAgICMgSWRlbnRpZnkgbWFjcm8gdmFyaWFibGVzIGluIHRoZSBtb2RlbA0KICAgIG1hY3JvX3ZhcnMgPC0gYygiZ2RwX2dyb3d0aCIsICJnZHBfZGVmbGF0b3IiLCAidW5lbXBsb3llbWVudCIpDQogICAgbW9kZWxfdmFycyA8LSBuYW1lcyhjb2VmKG1vZGVsKSkNCiAgICBtYWNyb190ZXJtcyA8LSBjKCkNCiAgICANCiAgICBmb3IodmFyIGluIG1vZGVsX3ZhcnMpIHsNCiAgICAgICMgQ2hlY2sgaWYgdmFyaWFibGUgY29udGFpbnMgYW55IG1hY3JvIHZhcmlhYmxlIG5hbWUNCiAgICAgICMgVGhpcyBoYW5kbGVzIGJvdGggbWFpbiBlZmZlY3RzIGFuZCBpbnRlcmFjdGlvbnMNCiAgICAgIGlzX21hY3JvIDwtIEZBTFNFDQogICAgICBmb3IobWFjcm8gaW4gbWFjcm9fdmFycykgew0KICAgICAgICBpZihncmVwbChtYWNybywgdmFyKSkgew0KICAgICAgICAgIGlzX21hY3JvIDwtIFRSVUUNCiAgICAgICAgICBicmVhaw0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgIGlmKGlzX21hY3JvKSB7DQogICAgICAgIG1hY3JvX3Rlcm1zIDwtIGMobWFjcm9fdGVybXMsIHZhcikNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgaWYobGVuZ3RoKG1hY3JvX3Rlcm1zKSA+IDApIHsNCiAgICAgIGNhdCgiRm91bmQiLCBsZW5ndGgobWFjcm9fdGVybXMpLCAibWFjcm9lY29ub21pYyB0ZXJtcyBpbiBtb2RlbDpcbiIpDQogICAgICBmb3IodGVybSBpbiBtYWNyb190ZXJtcykgew0KICAgICAgICBjYXQoIi0gIiwgdGVybSwgIlxuIikNCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBDcmVhdGUgbW9kZWwgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCiAgICAgIG5vX21hY3JvX2Zvcm11bGEgPC0gZm9ybXVsYShtb2RlbCkNCiAgICAgIGZvcih0ZXJtIGluIG1hY3JvX3Rlcm1zKSB7DQogICAgICAgIG5vX21hY3JvX2Zvcm11bGEgPC0gdXBkYXRlKG5vX21hY3JvX2Zvcm11bGEsIHBhc3RlMCgiLiB+IC4gLSAiLCB0ZXJtKSkNCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBGaXQgbW9kZWwgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCiAgICAgIG5vX21hY3JvX21vZGVsIDwtIGNveHBoKG5vX21hY3JvX2Zvcm11bGEsIGRhdGEgPSBkYXRhLCB0aWVzID0gImVmcm9uIikNCiAgICAgIA0KICAgICAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCiAgICAgIG5vX21hY3JvX3Njb3JlcyA8LSBwcmVkaWN0KG5vX21hY3JvX21vZGVsLCBuZXdkYXRhID0gbGFzdF9vYnNfZGF0YSwgdHlwZSA9ICJyaXNrIikNCiAgICAgIGxhc3Rfb2JzX2RhdGEkbm9fbWFjcm9fc2NvcmUgPC0gbm9fbWFjcm9fc2NvcmVzDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiTm8gbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgZm91bmQgaW4gdGhlIG1vZGVsLlxuIikNCiAgICAgIG5vX21hY3JvX21vZGVsIDwtIE5VTEwNCiAgICB9DQogIH0NCiAgDQogICMgRXZhbHVhdGUgZGlzY3JpbWluYXRpb24gd2l0aCBST0MgY3VydmUgYW5hbHlzaXMNCiAgaWYocmVxdWlyZU5hbWVzcGFjZSgicFJPQyIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgIGxpYnJhcnkocFJPQykNCiAgICANCiAgICAjIENyZWF0ZSBST0Mgb2JqZWN0IGZvciBmdWxsIG1vZGVsDQogICAgcm9jX29iaiA8LSB0cnlDYXRjaCh7DQogICAgICByb2MobGFzdF9vYnNfZGF0YVtbZXZlbnRfdHlwZV1dLCByaXNrX3Njb3JlcywgcXVpZXQgPSBUUlVFKQ0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICAgY2F0KCJFcnJvciBjYWxjdWxhdGluZyBST0M6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgICAgcmV0dXJuKE5VTEwpDQogICAgfSkNCiAgICANCiAgICAjIElmIGFzc2Vzc2luZyBtYWNybyBpbXBhY3QgYW5kIHdlIGhhdmUgYSBuby1tYWNybyBtb2RlbCwgY3JlYXRlIFJPQyBmb3IgaXQgdG9vDQogICAgaWYoYXNzZXNzX21hY3JvX2ltcGFjdCAmJiAhaXMubnVsbChub19tYWNyb19tb2RlbCkpIHsNCiAgICAgIG5vX21hY3JvX3JvYyA8LSB0cnlDYXRjaCh7DQogICAgICAgIHJvYyhsYXN0X29ic19kYXRhW1tldmVudF90eXBlXV0sIG5vX21hY3JvX3Njb3JlcywgcXVpZXQgPSBUUlVFKQ0KICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgICAgIGNhdCgiRXJyb3IgY2FsY3VsYXRpbmcgUk9DIGZvciBuby1tYWNybyBtb2RlbDoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICAgIG5vX21hY3JvX3JvYyA8LSBOVUxMDQogICAgICB9KQ0KICAgIH0gZWxzZSB7DQogICAgICBub19tYWNyb19yb2MgPC0gTlVMTA0KICAgIH0NCiAgICANCiAgICBpZighaXMubnVsbChyb2Nfb2JqKSkgew0KICAgICAgIyBDYWxjdWxhdGUgQVVDIGFuZCBjb25maWRlbmNlIGludGVydmFsDQogICAgICBhdWNfdmFsdWUgPC0gYXVjKHJvY19vYmopDQogICAgICBjaV92YWx1ZXMgPC0gY2kocm9jX29iaikNCiAgICAgIA0KICAgICAgY2F0KCJcbkRpc2NyaW1pbmF0aW9uIE1ldHJpY3M6XG4iKQ0KICAgICAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgICAgIGNhdCgiQVVDIChBcmVhIFVuZGVyIFJPQyBDdXJ2ZSk6Iiwgcm91bmQoYXVjX3ZhbHVlLCA0KSwgIlxuIikNCiAgICAgIGNhdCgiOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWw6IiwgcGFzdGUocm91bmQoY2lfdmFsdWVzWzE6Ml0sIDQpLCBjb2xsYXBzZT0iIC0gIiksICJcbiIpDQogICAgICANCiAgICAgICMgQ29tcGFyZSB3aXRoIG5vLW1hY3JvIG1vZGVsIGlmIGF2YWlsYWJsZQ0KICAgICAgaWYoIWlzLm51bGwobm9fbWFjcm9fcm9jKSkgew0KICAgICAgICBub19tYWNyb19hdWMgPC0gYXVjKG5vX21hY3JvX3JvYykNCiAgICAgICAgYXVjX2RpZmYgPC0gYXVjX3ZhbHVlIC0gbm9fbWFjcm9fYXVjDQogICAgICAgIHBjdF9pbXByb3ZlbWVudCA8LSBhdWNfZGlmZiAvIG5vX21hY3JvX2F1YyAqIDEwMA0KICAgICAgICANCiAgICAgICAgY2F0KCJcbk1hY3JvZWNvbm9taWMgVmFyaWFibGVzIENvbnRyaWJ1dGlvbiB0byBBVUM6XG4iKQ0KICAgICAgICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgICAgICAgY2F0KCJBVUMgd2l0aCBtYWNybyB2YXJpYWJsZXM6Iiwgcm91bmQoYXVjX3ZhbHVlLCA0KSwgIlxuIikNCiAgICAgICAgY2F0KCJBVUMgd2l0aG91dCBtYWNybyB2YXJpYWJsZXM6Iiwgcm91bmQobm9fbWFjcm9fYXVjLCA0KSwgIlxuIikNCiAgICAgICAgY2F0KCJBYnNvbHV0ZSBpbXByb3ZlbWVudDoiLCByb3VuZChhdWNfZGlmZiwgNCksICJcbiIpDQogICAgICAgIGNhdCgiUmVsYXRpdmUgaW1wcm92ZW1lbnQ6Iiwgcm91bmQocGN0X2ltcHJvdmVtZW50LCAyKSwgIiVcbiIpDQogICAgICAgIA0KICAgICAgICAjIFRlc3QgaWYgaW1wcm92ZW1lbnQgaXMgc2lnbmlmaWNhbnQNCiAgICAgICAgaWYocmVxdWlyZU5hbWVzcGFjZSgicFJPQyIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgICAgICAgIHJvY190ZXN0IDwtIHJvYy50ZXN0KHJvY19vYmosIG5vX21hY3JvX3JvYywgbWV0aG9kPSJkZWxvbmciKQ0KICAgICAgICAgIGNhdCgiU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlOiBwID0iLCBmb3JtYXQucHZhbChyb2NfdGVzdCRwLnZhbHVlLCBkaWdpdHMgPSAzKSwgIlxuIikNCiAgICAgICAgICANCiAgICAgICAgICBpZihyb2NfdGVzdCRwLnZhbHVlIDwgMC4wNSkgew0KICAgICAgICAgICAgY2F0KCJUaGUgaW1wcm92ZW1lbnQgZnJvbSBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IChwIDwgMC4wNSlcbiIpDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIGNhdCgiVGhlIGltcHJvdmVtZW50IGZyb20gbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgaXMgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKHAgPj0gMC4wNSlcbiIpDQogICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSBhdCBvcHRpbWFsIHRocmVzaG9sZA0KICAgICAgY29vcmRfcmVzdWx0cyA8LSBjb29yZHMocm9jX29iaiwgImJlc3QiLCByZXQgPSBjKCJ0aHJlc2hvbGQiLCAic2Vuc2l0aXZpdHkiLCAic3BlY2lmaWNpdHkiKSkNCiAgICAgIG9wdGltYWxfdGhyZXNob2xkIDwtIGFzLm51bWVyaWMoY29vcmRfcmVzdWx0c1sidGhyZXNob2xkIl0pDQogICAgICBvcHRpbWFsX3NlbnNpdGl2aXR5IDwtIGFzLm51bWVyaWMoY29vcmRfcmVzdWx0c1sic2Vuc2l0aXZpdHkiXSkNCiAgICAgIG9wdGltYWxfc3BlY2lmaWNpdHkgPC0gYXMubnVtZXJpYyhjb29yZF9yZXN1bHRzWyJzcGVjaWZpY2l0eSJdKQ0KICAgICAgDQogICAgICBjYXQoIlxuT3B0aW1hbCB0aHJlc2hvbGQ6Iiwgcm91bmQob3B0aW1hbF90aHJlc2hvbGQsIDQpLCAiXG4iKQ0KICAgICAgY2F0KCJTZW5zaXRpdml0eSBhdCBvcHRpbWFsIHRocmVzaG9sZDoiLCByb3VuZChvcHRpbWFsX3NlbnNpdGl2aXR5LCA0KSwgIlxuIikNCiAgICAgIGNhdCgiU3BlY2lmaWNpdHkgYXQgb3B0aW1hbCB0aHJlc2hvbGQ6Iiwgcm91bmQob3B0aW1hbF9zcGVjaWZpY2l0eSwgNCksICJcbiIpDQogICAgICANCiAgICAgICMgQWRkaXRpb25hbCBwZXJmb3JtYW5jZSBtZXRyaWNzDQogICAgICAjIENvdW50IGV2ZW50cw0KICAgICAgbl9ldmVudHMgPC0gc3VtKGxhc3Rfb2JzX2RhdGFbW2V2ZW50X3R5cGVdXSkNCiAgICAgIG5fdG90YWwgPC0gbnJvdyhsYXN0X29ic19kYXRhKQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBwcmVkaWN0aW9ucyBhdCBvcHRpbWFsIHRocmVzaG9sZA0KICAgICAgcHJlZGljdGlvbnMgPC0gaWZlbHNlKHJpc2tfc2NvcmVzID49IG9wdGltYWxfdGhyZXNob2xkLCAxLCAwKQ0KICAgICAgDQogICAgICAjIENvbmZ1c2lvbiBtYXRyaXgNCiAgICAgIFRQIDwtIHN1bShwcmVkaWN0aW9ucyA9PSAxICYgbGFzdF9vYnNfZGF0YVtbZXZlbnRfdHlwZV1dID09IDEpDQogICAgICBUTiA8LSBzdW0ocHJlZGljdGlvbnMgPT0gMCAmIGxhc3Rfb2JzX2RhdGFbW2V2ZW50X3R5cGVdXSA9PSAwKQ0KICAgICAgRlAgPC0gc3VtKHByZWRpY3Rpb25zID09IDEgJiBsYXN0X29ic19kYXRhW1tldmVudF90eXBlXV0gPT0gMCkNCiAgICAgIEZOIDwtIHN1bShwcmVkaWN0aW9ucyA9PSAwICYgbGFzdF9vYnNfZGF0YVtbZXZlbnRfdHlwZV1dID09IDEpDQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIGFkZGl0aW9uYWwgbWV0cmljcw0KICAgICAgYWNjdXJhY3kgPC0gKFRQICsgVE4pIC8gKFRQICsgVE4gKyBGUCArIEZOKQ0KICAgICAgcHJlY2lzaW9uIDwtIFRQIC8gKFRQICsgRlApDQogICAgICByZWNhbGwgPC0gVFAgLyAoVFAgKyBGTikgICMgc2FtZSBhcyBzZW5zaXRpdml0eQ0KICAgICAgZjFfc2NvcmUgPC0gMiAqIHByZWNpc2lvbiAqIHJlY2FsbCAvIChwcmVjaXNpb24gKyByZWNhbGwpDQogICAgICBwcHYgPC0gVFAgLyAoVFAgKyBGUCkgICMgcG9zaXRpdmUgcHJlZGljdGl2ZSB2YWx1ZQ0KICAgICAgbnB2IDwtIFROIC8gKFROICsgRk4pICAjIG5lZ2F0aXZlIHByZWRpY3RpdmUgdmFsdWUNCiAgICAgIA0KICAgICAgY2F0KCJcbkFkZGl0aW9uYWwgUGVyZm9ybWFuY2UgTWV0cmljczpcbiIpDQogICAgICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgICAgIGNhdCgiVG90YWwgY29tcGFuaWVzOiIsIG5fdG90YWwsICJcbiIpDQogICAgICBjYXQoIk51bWJlciBvZiIsIG1vZGVsX25hbWUsICJldmVudHM6Iiwgbl9ldmVudHMsIA0KICAgICAgICAgICIoIiwgcm91bmQoMTAwKm5fZXZlbnRzL25fdG90YWwsIDEpLCAiJSlcbiIpDQogICAgICBjYXQoIkFjY3VyYWN5OiIsIHJvdW5kKGFjY3VyYWN5LCA0KSwgIlxuIikNCiAgICAgIGNhdCgiUHJlY2lzaW9uIChQUFYpOiIsIHJvdW5kKHByZWNpc2lvbiwgNCksICJcbiIpDQogICAgICBjYXQoIlJlY2FsbCAoU2Vuc2l0aXZpdHkpOiIsIHJvdW5kKHJlY2FsbCwgNCksICJcbiIpDQogICAgICBjYXQoIkYxIFNjb3JlOiIsIHJvdW5kKGYxX3Njb3JlLCA0KSwgIlxuIikNCiAgICAgIGNhdCgiTmVnYXRpdmUgUHJlZGljdGl2ZSBWYWx1ZToiLCByb3VuZChucHYsIDQpLCAiXG4iKQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSB0aGUgc2FtZSBtZXRyaWNzIGZvciB0aGUgbm8tbWFjcm8gbW9kZWwgaWYgYXZhaWxhYmxlDQogICAgICBpZighaXMubnVsbChub19tYWNyb19yb2MpKSB7DQogICAgICAgICMgRmluZCBvcHRpbWFsIHRocmVzaG9sZCBmb3Igbm8tbWFjcm8gbW9kZWwNCiAgICAgICAgbm9fbWFjcm9fY29vcmRzIDwtIGNvb3Jkcyhub19tYWNyb19yb2MsICJiZXN0IiwgcmV0ID0gYygidGhyZXNob2xkIiwgInNlbnNpdGl2aXR5IiwgInNwZWNpZmljaXR5IikpDQogICAgICAgIG5vX21hY3JvX3RocmVzaG9sZCA8LSBhcy5udW1lcmljKG5vX21hY3JvX2Nvb3Jkc1sidGhyZXNob2xkIl0pDQogICAgICAgIA0KICAgICAgICAjIENhbGN1bGF0ZSBwcmVkaWN0aW9ucyB1c2luZyBuby1tYWNybyBzY29yZXMNCiAgICAgICAgbm9fbWFjcm9fcHJlZGljdGlvbnMgPC0gaWZlbHNlKG5vX21hY3JvX3Njb3JlcyA+PSBub19tYWNyb190aHJlc2hvbGQsIDEsIDApDQogICAgICAgIA0KICAgICAgICAjIENvbmZ1c2lvbiBtYXRyaXggZm9yIG5vLW1hY3JvIG1vZGVsDQogICAgICAgIG5vX21hY3JvX1RQIDwtIHN1bShub19tYWNyb19wcmVkaWN0aW9ucyA9PSAxICYgbGFzdF9vYnNfZGF0YVtbZXZlbnRfdHlwZV1dID09IDEpDQogICAgICAgIG5vX21hY3JvX1ROIDwtIHN1bShub19tYWNyb19wcmVkaWN0aW9ucyA9PSAwICYgbGFzdF9vYnNfZGF0YVtbZXZlbnRfdHlwZV1dID09IDApDQogICAgICAgIG5vX21hY3JvX0ZQIDwtIHN1bShub19tYWNyb19wcmVkaWN0aW9ucyA9PSAxICYgbGFzdF9vYnNfZGF0YVtbZXZlbnRfdHlwZV1dID09IDApDQogICAgICAgIG5vX21hY3JvX0ZOIDwtIHN1bShub19tYWNyb19wcmVkaWN0aW9ucyA9PSAwICYgbGFzdF9vYnNfZGF0YVtbZXZlbnRfdHlwZV1dID09IDEpDQogICAgICAgIA0KICAgICAgICAjIENhbGN1bGF0ZSBtZXRyaWNzIGZvciBuby1tYWNybyBtb2RlbA0KICAgICAgICBub19tYWNyb19hY2N1cmFjeSA8LSAobm9fbWFjcm9fVFAgKyBub19tYWNyb19UTikgLyAobm9fbWFjcm9fVFAgKyBub19tYWNyb19UTiArIG5vX21hY3JvX0ZQICsgbm9fbWFjcm9fRk4pDQogICAgICAgIG5vX21hY3JvX3ByZWNpc2lvbiA8LSBub19tYWNyb19UUCAvIChub19tYWNyb19UUCArIG5vX21hY3JvX0ZQKQ0KICAgICAgICBub19tYWNyb19yZWNhbGwgPC0gbm9fbWFjcm9fVFAgLyAobm9fbWFjcm9fVFAgKyBub19tYWNyb19GTikNCiAgICAgICAgbm9fbWFjcm9fZjEgPC0gMiAqIG5vX21hY3JvX3ByZWNpc2lvbiAqIG5vX21hY3JvX3JlY2FsbCAvIChub19tYWNyb19wcmVjaXNpb24gKyBub19tYWNyb19yZWNhbGwpDQogICAgICAgIA0KICAgICAgICBjYXQoIlxuQ29tcGFyaXNvbiB3aXRoIE1vZGVsIFdpdGhvdXQgTWFjcm9lY29ub21pYyBWYXJpYWJsZXM6XG4iKQ0KICAgICAgICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogICAgICAgIG1ldHJpY3MgPC0gYygiQWNjdXJhY3kiLCAiUHJlY2lzaW9uIiwgIlJlY2FsbCIsICJGMSBTY29yZSIpDQogICAgICAgIGZ1bGxfdmFsdWVzIDwtIGMoYWNjdXJhY3ksIHByZWNpc2lvbiwgcmVjYWxsLCBmMV9zY29yZSkNCiAgICAgICAgbm9fbWFjcm9fdmFsdWVzIDwtIGMobm9fbWFjcm9fYWNjdXJhY3ksIG5vX21hY3JvX3ByZWNpc2lvbiwgbm9fbWFjcm9fcmVjYWxsLCBub19tYWNyb19mMSkNCiAgICAgICAgDQogICAgICAgIGZvcihpIGluIDE6bGVuZ3RoKG1ldHJpY3MpKSB7DQogICAgICAgICAgZGlmZiA8LSBmdWxsX3ZhbHVlc1tpXSAtIG5vX21hY3JvX3ZhbHVlc1tpXQ0KICAgICAgICAgIHBjdF9kaWZmIDwtIGRpZmYgLyBub19tYWNyb192YWx1ZXNbaV0gKiAxMDANCiAgICAgICAgICANCiAgICAgICAgICBjYXQoc3ByaW50ZigiJS0xMHM6IFdpdGggbWFjcm8gPSAlNi40ZiwgV2l0aG91dCBtYWNybyA9ICU2LjRmICjOlCA9ICUrNi40ZiwgJSs1LjFmJSUpXG4iLCANCiAgICAgICAgICAgICAgICAgICAgbWV0cmljc1tpXSwgZnVsbF92YWx1ZXNbaV0sIG5vX21hY3JvX3ZhbHVlc1tpXSwgZGlmZiwgcGN0X2RpZmYpKQ0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgICMgUmlzayBkaXN0cmlidXRpb24gYW5hbHlzaXMNCiAgICAgIHJpc2tfcXVhbnRpbGVzIDwtIHF1YW50aWxlKHJpc2tfc2NvcmVzLCBwcm9icyA9IHNlcSgwLCAxLCAwLjI1KSkNCiAgICAgIA0KICAgICAgY2F0KCJcblJpc2sgU2NvcmUgRGlzdHJpYnV0aW9uOlxuIikNCiAgICAgIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICAgICAgY2F0KCJNaW5pbXVtOiIsIHJvdW5kKG1pbihyaXNrX3Njb3JlcyksIDQpLCAiXG4iKQ0KICAgICAgY2F0KCIyNXRoIHBlcmNlbnRpbGU6Iiwgcm91bmQocmlza19xdWFudGlsZXNbMl0sIDQpLCAiXG4iKQ0KICAgICAgY2F0KCJNZWRpYW46Iiwgcm91bmQobWVkaWFuKHJpc2tfc2NvcmVzKSwgNCksICJcbiIpDQogICAgICBjYXQoIjc1dGggcGVyY2VudGlsZToiLCByb3VuZChyaXNrX3F1YW50aWxlc1s0XSwgNCksICJcbiIpDQogICAgICBjYXQoIk1heGltdW06Iiwgcm91bmQobWF4KHJpc2tfc2NvcmVzKSwgNCksICJcbiIpDQogICAgICANCiAgICAgICMgUmlzayBzdHJhdGlmaWNhdGlvbg0KICAgICAgY2F0KCJcbkV2ZW50IFJhdGVzIGJ5IFJpc2sgUXVhcnRpbGU6XG4iKQ0KICAgICAgbGFzdF9vYnNfZGF0YSRyaXNrX3F1YXJ0aWxlIDwtIGN1dChyaXNrX3Njb3JlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHJpc2tfcXVhbnRpbGVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiUTEgKExvd2VzdCkiLCAiUTIiLCAiUTMiLCAiUTQgKEhpZ2hlc3QpIikpDQogICAgICANCiAgICAgICMgVXNlIHRyYWRpdGlvbmFsIGFwcHJvYWNoIGZvciBjb21wYXRpYmlsaXR5DQogICAgICBldmVudF9ieV9xdWFydGlsZSA8LSBhZ2dyZWdhdGUoDQogICAgICAgIGxhc3Rfb2JzX2RhdGFbW2V2ZW50X3R5cGVdXSwgDQogICAgICAgIGJ5ID0gbGlzdChyaXNrX3F1YXJ0aWxlID0gbGFzdF9vYnNfZGF0YSRyaXNrX3F1YXJ0aWxlKSwgDQogICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIGMoZXZlbnRzID0gc3VtKHgpLCBuID0gbGVuZ3RoKHgpLCByYXRlID0gbWVhbih4KSkNCiAgICAgICkNCiAgICAgIA0KICAgICAgZm9yKGkgaW4gMTpucm93KGV2ZW50X2J5X3F1YXJ0aWxlKSkgew0KICAgICAgICBxIDwtIGV2ZW50X2J5X3F1YXJ0aWxlJHJpc2tfcXVhcnRpbGVbaV0NCiAgICAgICAgc3RhdHMgPC0gZXZlbnRfYnlfcXVhcnRpbGUkeFtpLF0NCiAgICAgICAgZXZlbnRzIDwtIHN0YXRzWyJldmVudHMiXQ0KICAgICAgICBuIDwtIHN0YXRzWyJuIl0NCiAgICAgICAgcmF0ZSA8LSBzdGF0c1sicmF0ZSJdDQogICAgICAgIA0KICAgICAgICBjYXQocSwgIjogIiwgZXZlbnRzLCAiIGV2ZW50cyBvdXQgb2YgIiwgbiwgIiBjb21wYW5pZXMgKCIsIA0KICAgICAgICAgICAgcm91bmQoMTAwKnJhdGUsIDEpLCAiJSlcbiIsIHNlcD0iIikNCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBJZiBtYWNybyBhc3Nlc3NtZW50IGlzIGVuYWJsZWQgYW5kIHdlIGhhdmUgYm90aCBtb2RlbHMsIGFuYWx5emUgbWFjcm8gaW1wYWN0IG9uIHJpc2sgc3RyYXRpZmljYXRpb24NCiAgICAgIGlmKGFzc2Vzc19tYWNyb19pbXBhY3QgJiYgIWlzLm51bGwobm9fbWFjcm9fcm9jKSkgew0KICAgICAgICBjYXQoIlxuTWFjcm9lY29ub21pYyBJbXBhY3Qgb24gUmlzayBTdHJhdGlmaWNhdGlvbjpcbiIpDQogICAgICAgIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogICAgICAgIA0KICAgICAgICAjIEdldCBjb21wYW5pZXMgaW4gaGlnaGVzdCByaXNrIHF1YXJ0aWxlIGFjY29yZGluZyB0byBib3RoIG1vZGVscw0KICAgICAgICBmdWxsX2hpZ2hfcmlzayA8LSBsYXN0X29ic19kYXRhJGN1c2lwW2xhc3Rfb2JzX2RhdGEkcmlza19xdWFydGlsZSA9PSAiUTQgKEhpZ2hlc3QpIl0NCiAgICAgICAgDQogICAgICAgICMgQ3JlYXRlIHJpc2sgcXVhcnRpbGVzIGZvciBuby1tYWNybyBtb2RlbA0KICAgICAgICBub19tYWNyb19xdWFudGlsZXMgPC0gcXVhbnRpbGUobm9fbWFjcm9fc2NvcmVzLCBwcm9icyA9IHNlcSgwLCAxLCAwLjI1KSkNCiAgICAgICAgbGFzdF9vYnNfZGF0YSRub19tYWNyb19xdWFydGlsZSA8LSBjdXQobm9fbWFjcm9fc2NvcmVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5vX21hY3JvX3F1YW50aWxlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiUTEgKExvd2VzdCkiLCAiUTIiLCAiUTMiLCAiUTQgKEhpZ2hlc3QpIikpDQogICAgICAgIA0KICAgICAgICBub19tYWNyb19oaWdoX3Jpc2sgPC0gbGFzdF9vYnNfZGF0YSRjdXNpcFtsYXN0X29ic19kYXRhJG5vX21hY3JvX3F1YXJ0aWxlID09ICJRNCAoSGlnaGVzdCkiXQ0KICAgICAgICANCiAgICAgICAgIyBGaW5kIGNvbXBhbmllcyB0aGF0IGNoYW5nZWQgcmlzayBjbGFzc2lmaWNhdGlvbiBkdWUgdG8gbWFjcm8gdmFyaWFibGVzDQogICAgICAgIG1vdmVkX3RvX2hpZ2ggPC0gc2V0ZGlmZihmdWxsX2hpZ2hfcmlzaywgbm9fbWFjcm9faGlnaF9yaXNrKQ0KICAgICAgICBtb3ZlZF9mcm9tX2hpZ2ggPC0gc2V0ZGlmZihub19tYWNyb19oaWdoX3Jpc2ssIGZ1bGxfaGlnaF9yaXNrKQ0KICAgICAgICANCiAgICAgICAgY2F0KCJDb21wYW5pZXMgcmVjbGFzc2lmaWVkIHRvIGhpZ2hlc3QgcmlzayBxdWFydGlsZSB3aGVuIHVzaW5nIG1hY3JvIHZhcmlhYmxlczoiLCBsZW5ndGgobW92ZWRfdG9faGlnaCksICJcbiIpDQogICAgICAgIGNhdCgiQ29tcGFuaWVzIG1vdmVkIG91dCBvZiBoaWdoZXN0IHJpc2sgcXVhcnRpbGUgd2hlbiB1c2luZyBtYWNybyB2YXJpYWJsZXM6IiwgbGVuZ3RoKG1vdmVkX2Zyb21faGlnaCksICJcbiIpDQogICAgICAgIA0KICAgICAgICAjIENoZWNrIGlmIHJlY2xhc3NpZmljYXRpb24gaW1wcm92ZWQgcHJlZGljdGlvbg0KICAgICAgICBpZihsZW5ndGgobW92ZWRfdG9faGlnaCkgPiAwKSB7DQogICAgICAgICAgbW92ZWRfdG9faGlnaF9kYXRhIDwtIGxhc3Rfb2JzX2RhdGFbbGFzdF9vYnNfZGF0YSRjdXNpcCAlaW4lIG1vdmVkX3RvX2hpZ2gsIF0NCiAgICAgICAgICBldmVudF9yYXRlX21vdmVkX3RvX2hpZ2ggPC0gbWVhbihtb3ZlZF90b19oaWdoX2RhdGFbW2V2ZW50X3R5cGVdXSkNCiAgICAgICAgICBjYXQoIkV2ZW50IHJhdGUgYW1vbmcgY29tcGFuaWVzIG1vdmVkIHRvIGhpZ2hlc3QgcmlzayBxdWFydGlsZToiLCANCiAgICAgICAgICAgICAgcm91bmQoMTAwKmV2ZW50X3JhdGVfbW92ZWRfdG9faGlnaCwgMSksICIlXG4iKQ0KICAgICAgICB9DQogICAgICAgIA0KICAgICAgICBpZihsZW5ndGgobW92ZWRfZnJvbV9oaWdoKSA+IDApIHsNCiAgICAgICAgICBtb3ZlZF9mcm9tX2hpZ2hfZGF0YSA8LSBsYXN0X29ic19kYXRhW2xhc3Rfb2JzX2RhdGEkY3VzaXAgJWluJSBtb3ZlZF9mcm9tX2hpZ2gsIF0NCiAgICAgICAgICBldmVudF9yYXRlX21vdmVkX2Zyb21faGlnaCA8LSBtZWFuKG1vdmVkX2Zyb21faGlnaF9kYXRhW1tldmVudF90eXBlXV0pDQogICAgICAgICAgY2F0KCJFdmVudCByYXRlIGFtb25nIGNvbXBhbmllcyBtb3ZlZCBmcm9tIGhpZ2hlc3QgcmlzayBxdWFydGlsZToiLCANCiAgICAgICAgICAgICAgcm91bmQoMTAwKmV2ZW50X3JhdGVfbW92ZWRfZnJvbV9oaWdoLCAxKSwgIiVcbiIpDQogICAgICAgIH0NCiAgICAgICAgDQogICAgICAgICMgQXNzZXNzIGFjY3VyYWN5IG9mIHJlY2xhc3NpZmljYXRpb24NCiAgICAgICAgaWYobGVuZ3RoKG1vdmVkX3RvX2hpZ2gpID4gMCAmJiBsZW5ndGgobW92ZWRfZnJvbV9oaWdoKSA+IDApIHsNCiAgICAgICAgICBpZihldmVudF9yYXRlX21vdmVkX3RvX2hpZ2ggPiBldmVudF9yYXRlX21vdmVkX2Zyb21faGlnaCkgew0KICAgICAgICAgICAgY2F0KCJcblJlY2xhc3NpZmljYXRpb24gYW5hbHlzaXM6IE1hY3JvZWNvbm9taWMgdmFyaWFibGVzIElNUFJPVkVEIHJpc2sgc3RyYXRpZmljYXRpb25cbiIpDQogICAgICAgICAgICBjYXQoIkNvbXBhbmllcyBtb3ZlZCB0byBoaWdoIHJpc2sgaGFkIGhpZ2hlciBldmVudCByYXRlcyB0aGFuIHRob3NlIG1vdmVkIG91dCBvZiBoaWdoIHJpc2tcbiIpDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIGNhdCgiXG5SZWNsYXNzaWZpY2F0aW9uIGFuYWx5c2lzOiBNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBkaWQgTk9UIGltcHJvdmUgcmlzayBzdHJhdGlmaWNhdGlvblxuIikNCiAgICAgICAgICAgIGNhdCgiQ29tcGFuaWVzIG1vdmVkIHRvIGhpZ2ggcmlzayBoYWQgbG93ZXIgZXZlbnQgcmF0ZXMgdGhhbiB0aG9zZSBtb3ZlZCBvdXQgb2YgaGlnaCByaXNrXG4iKQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIEVjb25vbWljIGNvbmRpdGlvbiBhbmFseXNpcyAtIGV4YW1pbmUgaG93IHByZWRpY3Rpb24gdmFyaWVzIHdpdGggZWNvbm9taWMgY29uZGl0aW9ucw0KICAgICAgaWYoYXNzZXNzX21hY3JvX2ltcGFjdCAmJiAhaXMubnVsbChub19tYWNyb19tb2RlbCkpIHsNCiAgICAgICAgY2F0KCJcblByZWRpY3Rpb24gUGVyZm9ybWFuY2UgQWNyb3NzIEVjb25vbWljIENvbmRpdGlvbnM6XG4iKQ0KICAgICAgICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICAgICAgICANCiAgICAgICAgIyBDaGVjayBpZiB3ZSBoYXZlIGVjb25vbWljIGluZGljYXRvcnMgaW4gdGhlIGRhdGENCiAgICAgICAgaWYoYWxsKGMoImdkcF9ncm93dGgiLCAidW5lbXBsb3llbWVudCIpICVpbiUgY29sbmFtZXMobGFzdF9vYnNfZGF0YSkpKSB7DQogICAgICAgICAgIyBEZWZpbmUgZWNvbm9taWMgY29uZGl0aW9ucw0KICAgICAgICAgIG1lZGlhbl9nZHAgPC0gbWVkaWFuKGxhc3Rfb2JzX2RhdGEkZ2RwX2dyb3d0aCwgbmEucm0gPSBUUlVFKQ0KICAgICAgICAgIG1lZGlhbl91bmVtcCA8LSBtZWRpYW4obGFzdF9vYnNfZGF0YSR1bmVtcGxveWVtZW50LCBuYS5ybSA9IFRSVUUpDQogICAgICAgICAgDQogICAgICAgICAgIyBDcmVhdGUgZWNvbm9taWMgY29uZGl0aW9uIGdyb3Vwcw0KICAgICAgICAgIGxhc3Rfb2JzX2RhdGEkZWNvbl9jb25kaXRpb24gPC0gIk1peGVkIg0KICAgICAgICAgIGxhc3Rfb2JzX2RhdGEkZWNvbl9jb25kaXRpb25bbGFzdF9vYnNfZGF0YSRnZHBfZ3Jvd3RoID4gbWVkaWFuX2dkcCAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdF9vYnNfZGF0YSR1bmVtcGxveWVtZW50IDwgbWVkaWFuX3VuZW1wXSA8LSAiU3Ryb25nIg0KICAgICAgICAgIGxhc3Rfb2JzX2RhdGEkZWNvbl9jb25kaXRpb25bbGFzdF9vYnNfZGF0YSRnZHBfZ3Jvd3RoIDwgbWVkaWFuX2dkcCAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdF9vYnNfZGF0YSR1bmVtcGxveWVtZW50ID4gbWVkaWFuX3VuZW1wXSA8LSAiV2VhayINCiAgICAgICAgICANCiAgICAgICAgICAjIENhbGN1bGF0ZSBBVUMgYnkgZWNvbm9taWMgY29uZGl0aW9uIGZvciBib3RoIG1vZGVscw0KICAgICAgICAgIGZvcihjb25kaXRpb24gaW4gYygiU3Ryb25nIiwgIk1peGVkIiwgIldlYWsiKSkgew0KICAgICAgICAgICAgY29uZGl0aW9uX2RhdGEgPC0gbGFzdF9vYnNfZGF0YVtsYXN0X29ic19kYXRhJGVjb25fY29uZGl0aW9uID09IGNvbmRpdGlvbiwgXQ0KICAgICAgICAgICAgDQogICAgICAgICAgICBpZihucm93KGNvbmRpdGlvbl9kYXRhKSA+IDAgJiYgc3VtKGNvbmRpdGlvbl9kYXRhW1tldmVudF90eXBlXV0pID4gMCkgew0KICAgICAgICAgICAgICBjYXQoIlxuRWNvbm9taWMgY29uZGl0aW9uOiIsIGNvbmRpdGlvbiwgIigiLCBucm93KGNvbmRpdGlvbl9kYXRhKSwgImNvbXBhbmllcylcbiIpDQogICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAjIENhbGN1bGF0ZSBBVUMgZm9yIGZ1bGwgbW9kZWwNCiAgICAgICAgICAgICAgY29uZGl0aW9uX3JvYyA8LSB0cnlDYXRjaCh7DQogICAgICAgICAgICAgICAgcm9jKGNvbmRpdGlvbl9kYXRhW1tldmVudF90eXBlXV0sIGNvbmRpdGlvbl9kYXRhJHJpc2tfc2NvcmUsIHF1aWV0PSBUUlVFKQ0KICAgICAgICAgICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIE5VTEwpDQogICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAjIENhbGN1bGF0ZSBBVUMgZm9yIG5vLW1hY3JvIG1vZGVsDQogICAgICAgICAgICAgIGNvbmRpdGlvbl9ub19tYWNyb19yb2MgPC0gdHJ5Q2F0Y2goew0KICAgICAgICAgICAgICAgIHJvYyhjb25kaXRpb25fZGF0YVtbZXZlbnRfdHlwZV1dLCBjb25kaXRpb25fZGF0YSRub19tYWNyb19zY29yZSwgcXVpZXQ9IFRSVUUpDQogICAgICAgICAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIGlmKCFpcy5udWxsKGNvbmRpdGlvbl9yb2MpICYmICFpcy5udWxsKGNvbmRpdGlvbl9ub19tYWNyb19yb2MpKSB7DQogICAgICAgICAgICAgICAgY29uZGl0aW9uX2F1YyA8LSBhdWMoY29uZGl0aW9uX3JvYykNCiAgICAgICAgICAgICAgICBjb25kaXRpb25fbm9fbWFjcm9fYXVjIDwtIGF1Yyhjb25kaXRpb25fbm9fbWFjcm9fcm9jKQ0KICAgICAgICAgICAgICAgIGF1Y19kaWZmIDwtIGNvbmRpdGlvbl9hdWMgLSBjb25kaXRpb25fbm9fbWFjcm9fYXVjDQogICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgY2F0KCJGdWxsIG1vZGVsIEFVQzoiLCByb3VuZChjb25kaXRpb25fYXVjLCA0KSwgIlxuIikNCiAgICAgICAgICAgICAgICBjYXQoIk5vLW1hY3JvIG1vZGVsIEFVQzoiLCByb3VuZChjb25kaXRpb25fbm9fbWFjcm9fYXVjLCA0KSwgIlxuIikNCiAgICAgICAgICAgICAgICBjYXQoIkltcHJvdmVtZW50IGZyb20gbWFjcm8gdmFyaWFibGVzOiIsIHJvdW5kKGF1Y19kaWZmLCA0KSwgIlxuIikNCiAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICBpZihhdWNfZGlmZiA+IDApIHsNCiAgICAgICAgICAgICAgICAgIGNhdCgiTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgaW1wcm92ZSBwcmVkaWN0aW9uIGluIiwgY29uZGl0aW9uLCAiZWNvbm9taWMgY29uZGl0aW9uc1xuIikNCiAgICAgICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICAgICAgY2F0KCJNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBkbyBub3QgaW1wcm92ZSBwcmVkaWN0aW9uIGluIiwgY29uZGl0aW9uLCAiZWNvbm9taWMgY29uZGl0aW9uc1xuIikNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICAgICAgY2F0KCJDb3VsZCBub3QgY2FsY3VsYXRlIEFVQyBmb3IgdGhpcyBlY29ub21pYyBjb25kaXRpb24gZHVlIHRvIGluc3VmZmljaWVudCBkYXRhXG4iKQ0KICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgICBjYXQoIlxuRWNvbm9taWMgY29uZGl0aW9uOiIsIGNvbmRpdGlvbiwgIi0gaW5zdWZmaWNpZW50IGRhdGEgZm9yIGFuYWx5c2lzXG4iKQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgIH0NCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBjYXQoIkVjb25vbWljIGluZGljYXRvcnMgbm90IGF2YWlsYWJsZSBpbiBkYXRhc2V0IGZvciBjb25kaXRpb24tc3BlY2lmaWMgYW5hbHlzaXNcbiIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBSZXR1cm4gcGVyZm9ybWFuY2UgbWV0cmljcw0KICAgICAgcmV0dXJuKGxpc3QoDQogICAgICAgIGF1YyA9IGF1Y192YWx1ZSwNCiAgICAgICAgY2kgPSBjaV92YWx1ZXMsDQogICAgICAgIG9wdGltYWxfdGhyZXNob2xkID0gb3B0aW1hbF90aHJlc2hvbGQsDQogICAgICAgIHNlbnNpdGl2aXR5ID0gb3B0aW1hbF9zZW5zaXRpdml0eSwNCiAgICAgICAgc3BlY2lmaWNpdHkgPSBvcHRpbWFsX3NwZWNpZmljaXR5LA0KICAgICAgICBhY2N1cmFjeSA9IGFjY3VyYWN5LA0KICAgICAgICBwcmVjaXNpb24gPSBwcmVjaXNpb24sDQogICAgICAgIHJlY2FsbCA9IHJlY2FsbCwNCiAgICAgICAgZjFfc2NvcmUgPSBmMV9zY29yZSwNCiAgICAgICAgcmlza19xdWFudGlsZXMgPSByaXNrX3F1YW50aWxlcywNCiAgICAgICAgbm9fbWFjcm9fbW9kZWwgPSBpZihleGlzdHMoIm5vX21hY3JvX21vZGVsIikpIG5vX21hY3JvX21vZGVsIGVsc2UgTlVMTCwNCiAgICAgICAgbm9fbWFjcm9fYXVjID0gaWYoZXhpc3RzKCJub19tYWNyb19hdWMiKSkgbm9fbWFjcm9fYXVjIGVsc2UgTlVMTCwNCiAgICAgICAgbm9fbWFjcm9fbWV0cmljcyA9IGlmKGV4aXN0cygibm9fbWFjcm9fdmFsdWVzIikpIA0KICAgICAgICAgIGxpc3QoYWNjdXJhY3kgPSBub19tYWNyb19hY2N1cmFjeSwgDQogICAgICAgICAgICAgICBwcmVjaXNpb24gPSBub19tYWNyb19wcmVjaXNpb24sIA0KICAgICAgICAgICAgICAgcmVjYWxsID0gbm9fbWFjcm9fcmVjYWxsLCANCiAgICAgICAgICAgICAgIGYxID0gbm9fbWFjcm9fZjEpIGVsc2UgTlVMTA0KICAgICAgKSkNCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCJDb3VsZCBub3QgY2FsY3VsYXRlIFJPQyBjdXJ2ZS5cbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9DQogIH0gZWxzZSB7DQogICAgY2F0KCJwUk9DIHBhY2thZ2Ugbm90IGF2YWlsYWJsZSBmb3IgUk9DIGFuYWx5c2lzLlxuIikNCiAgICByZXR1cm4oTlVMTCkNCiAgfQ0KfQ0KDQojIEFwcGx5IGZ1bmN0aW9uIHRvIGJvdGggbW9kZWxzDQpiYW5rcnVwdGN5X3BlcmZvcm1hbmNlIDwtIGV2YWx1YXRlX3ByZWRpY3Rpb25fcGVyZm9ybWFuY2UoZmluYWxfYmFua3J1cHRjeV9tb2RlbCwgImJhbmtydXB0Y3kiLCAiYmFua3J1cHRjeSIpDQphY3F1aXNpdGlvbl9wZXJmb3JtYW5jZSA8LSBldmFsdWF0ZV9wcmVkaWN0aW9uX3BlcmZvcm1hbmNlKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiYWNxdWlzaXRpb24iLCAiYWNxdWlzaXRpb24iKQ0KDQojIENvbXBhcmUga2V5IHBlcmZvcm1hbmNlIG1ldHJpY3MNCmlmKCFpcy5udWxsKGJhbmtydXB0Y3lfcGVyZm9ybWFuY2UpICYmICFpcy5udWxsKGFjcXVpc2l0aW9uX3BlcmZvcm1hbmNlKSkgew0KICBjYXQoIlxuQ09NUEFSSVNPTiBPRiBQUkVESUNUSU9OIFBFUkZPUk1BTkNFOlxuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIA0KICBtZXRyaWNzIDwtIGMoIkFVQyIsICJBY2N1cmFjeSIsICJQcmVjaXNpb24iLCAiUmVjYWxsIiwgIkYxIFNjb3JlIikNCiAgYmFua3J1cHRjeV92YWx1ZXMgPC0gYygNCiAgICBiYW5rcnVwdGN5X3BlcmZvcm1hbmNlJGF1YywNCiAgICBiYW5rcnVwdGN5X3BlcmZvcm1hbmNlJGFjY3VyYWN5LA0KICAgIGJhbmtydXB0Y3lfcGVyZm9ybWFuY2UkcHJlY2lzaW9uLA0KICAgIGJhbmtydXB0Y3lfcGVyZm9ybWFuY2UkcmVjYWxsLA0KICAgIGJhbmtydXB0Y3lfcGVyZm9ybWFuY2UkZjFfc2NvcmUNCiAgKQ0KICANCiAgYWNxdWlzaXRpb25fdmFsdWVzIDwtIGMoDQogICAgYWNxdWlzaXRpb25fcGVyZm9ybWFuY2UkYXVjLA0KICAgIGFjcXVpc2l0aW9uX3BlcmZvcm1hbmNlJGFjY3VyYWN5LA0KICAgIGFjcXVpc2l0aW9uX3BlcmZvcm1hbmNlJHByZWNpc2lvbiwNCiAgICBhY3F1aXNpdGlvbl9wZXJmb3JtYW5jZSRyZWNhbGwsDQogICAgYWNxdWlzaXRpb25fcGVyZm9ybWFuY2UkZjFfc2NvcmUNCiAgKQ0KICANCiAgZm9yKGkgaW4gMTpsZW5ndGgobWV0cmljcykpIHsNCiAgICBjYXQoc3ByaW50ZigiJS0xMHM6IEJhbmtydXB0Y3kgPSAlNi40ZiwgQWNxdWlzaXRpb24gPSAlNi40ZlxuIiwgDQogICAgICAgICAgICAgIG1ldHJpY3NbaV0sIGJhbmtydXB0Y3lfdmFsdWVzW2ldLCBhY3F1aXNpdGlvbl92YWx1ZXNbaV0pKQ0KICB9DQogIA0KICAjIENvbXBhcmUgaW1wYWN0IG9mIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGJldHdlZW4gbW9kZWxzDQogIGlmKCFpcy5udWxsKGJhbmtydXB0Y3lfcGVyZm9ybWFuY2Ukbm9fbWFjcm9fYXVjKSAmJiANCiAgICAgIWlzLm51bGwoYWNxdWlzaXRpb25fcGVyZm9ybWFuY2Ukbm9fbWFjcm9fYXVjKSkgew0KICAgIA0KICAgIGNhdCgiXG5DT01QQVJJU09OIE9GIE1BQ1JPRUNPTk9NSUMgSU1QQUNUIE9OIFBSRURJQ1RJT046XG4iKQ0KICAgIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgICANCiAgICBiYW5rX2F1Y19pbXByb3ZlbWVudCA8LSBiYW5rcnVwdGN5X3BlcmZvcm1hbmNlJGF1YyAtIGJhbmtydXB0Y3lfcGVyZm9ybWFuY2Ukbm9fbWFjcm9fYXVjDQogICAgYmFua19wY3RfaW1wcm92ZW1lbnQgPC0gYmFua19hdWNfaW1wcm92ZW1lbnQgLyBiYW5rcnVwdGN5X3BlcmZvcm1hbmNlJG5vX21hY3JvX2F1YyAqIDEwMA0KICAgIA0KICAgIGFjcV9hdWNfaW1wcm92ZW1lbnQgPC0gYWNxdWlzaXRpb25fcGVyZm9ybWFuY2UkYXVjIC0gYWNxdWlzaXRpb25fcGVyZm9ybWFuY2Ukbm9fbWFjcm9fYXVjDQogICAgYWNxX3BjdF9pbXByb3ZlbWVudCA8LSBhY3FfYXVjX2ltcHJvdmVtZW50IC8gYWNxdWlzaXRpb25fcGVyZm9ybWFuY2Ukbm9fbWFjcm9fYXVjICogMTAwDQogICAgDQogICAgY2F0KCJBVUMgaW1wcm92ZW1lbnQgZnJvbSBtYWNyb2Vjb25vbWljIHZhcmlhYmxlczpcbiIpDQogICAgY2F0KCIgIEJhbmtydXB0Y3kgbW9kZWw6ICsiLCByb3VuZChiYW5rX2F1Y19pbXByb3ZlbWVudCwgNCksIA0KICAgICAgICAiKCIsIHJvdW5kKGJhbmtfcGN0X2ltcHJvdmVtZW50LCAyKSwgIiUpXG4iLCBzZXA9IiIpDQogICAgY2F0KCIgIEFjcXVpc2l0aW9uIG1vZGVsOiArIiwgcm91bmQoYWNxX2F1Y19pbXByb3ZlbWVudCwgNCksIA0KICAgICAgICAiKCIsIHJvdW5kKGFjcV9wY3RfaW1wcm92ZW1lbnQsIDIpLCAiJSlcbiIsIHNlcD0iIikNCiAgICANCiAgICBpZihiYW5rX2F1Y19pbXByb3ZlbWVudCA+IGFjcV9hdWNfaW1wcm92ZW1lbnQpIHsNCiAgICAgIGNhdCgiXG5NYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBoYXZlIGEgR1JFQVRFUiBpbXBhY3Qgb24gYmFua3J1cHRjeSBwcmVkaWN0aW9uXG4iKQ0KICAgICAgY2F0KCJUaGlzIGFsaWducyB3aXRoIHRoZSB0aGVvcnkgdGhhdCBleHRlcm5hbCBlY29ub21pYyBjb25kaXRpb25zIHBsYXkgYSBtb3JlXG4iKQ0KICAgICAgY2F0KCJzaWduaWZpY2FudCByb2xlIGluIGNvbXBhbnkgZmFpbHVyZSB0aGFuIGluIGFjcXVpc2l0aW9uIGxpa2VsaWhvb2QuXG4iKQ0KICAgIH0gZWxzZSBpZihhY3FfYXVjX2ltcHJvdmVtZW50ID4gYmFua19hdWNfaW1wcm92ZW1lbnQpIHsNCiAgICAgIGNhdCgiXG5NYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBoYXZlIGEgR1JFQVRFUiBpbXBhY3Qgb24gYWNxdWlzaXRpb24gcHJlZGljdGlvblxuIikNCiAgICAgIGNhdCgiVGhpcyBzdWdnZXN0cyB0aGF0IE0mQSBhY3Rpdml0eSBpcyBtb3JlIHN0cm9uZ2x5IGluZmx1ZW5jZWQgYnkgYnJvYWRlclxuIikNCiAgICAgIGNhdCgiZWNvbm9taWMgY29uZGl0aW9ucyB0aGFuIGJhbmtydXB0Y3kgcmlzay5cbiIpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiXG5NYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBoYXZlIFNJTUlMQVIgaW1wYWN0IG9uIGJvdGggb3V0Y29tZXNcbiIpDQogICAgICBjYXQoIlRoaXMgaW5kaWNhdGVzIHRoYXQgYm90aCBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiByaXNrcyBhcmVcbiIpDQogICAgICBjYXQoInNpbWlsYXJseSBhZmZlY3RlZCBieSBjaGFuZ2luZyBlY29ub21pYyBjb25kaXRpb25zLlxuIikNCiAgICB9DQogICAgDQogICAgIyBDaGVjayB3aGljaCBtb2RlbCBzaG93cyBtb3N0IHNpZ25pZmljYW50IGltcHJvdmVtZW50IGZyb20gbWFjcm8gdmFyaWFibGVzDQogICAgY2F0KCJcbkVDT05PTUlDIElOU0lHSFQ6ICIpDQogICAgaWYoYmFua19wY3RfaW1wcm92ZW1lbnQgPiA1ICYmIGFjcV9wY3RfaW1wcm92ZW1lbnQgPiA1KSB7DQogICAgICBjYXQoIk1hY3JvZWNvbm9taWMgY29uZGl0aW9ucyBhcmUgaW1wb3J0YW50IHByZWRpY3RvcnMgZm9yIEJPVEggY29ycG9yYXRlIG91dGNvbWVzXG4iKQ0KICAgICAgY2F0KCJDb21wYW5pZXMgc2hvdWxkIG1vbml0b3IgZWNvbm9taWMgaW5kaWNhdG9ycyB0byBhc3Nlc3MgYm90aCBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiByaXNrc1xuIikNCiAgICB9IGVsc2UgaWYoYmFua19wY3RfaW1wcm92ZW1lbnQgPiA1KSB7DQogICAgICBjYXQoIkJhbmtydXB0Y3kgcmlzayBpcyBtb3JlIHN5c3RlbWF0aWNhbGx5IGxpbmtlZCB0byBlY29ub21pYyBjeWNsZXNcbiIpDQogICAgICBjYXQoIkVjb25vbWljIGRvd250dXJucyBtYXkgZGlzcHJvcG9ydGlvbmF0ZWx5IGFmZmVjdCBmaW5hbmNpYWxseSB2dWxuZXJhYmxlIGNvbXBhbmllc1xuIikNCiAgICB9IGVsc2UgaWYoYWNxX3BjdF9pbXByb3ZlbWVudCA+IDUpIHsNCiAgICAgIGNhdCgiQWNxdWlzaXRpb24gbGlrZWxpaG9vZCBpcyBtb3JlIHN5c3RlbWF0aWNhbGx5IGxpbmtlZCB0byBlY29ub21pYyBjeWNsZXNcbiIpDQogICAgICBjYXQoIk0mQSBhY3Rpdml0aWVzIGFwcGVhciB0byBiZSBtb3JlIHNlbnNpdGl2ZSB0byBjaGFuZ2luZyBtYWNyb2Vjb25vbWljIGNvbmRpdGlvbnNcbiIpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiTGltaXRlZCBzeXN0ZW1hdGljIGltcGFjdCBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBvbiBib3RoIG91dGNvbWVzXG4iKQ0KICAgICAgY2F0KCJDb21wYW55LXNwZWNpZmljIGZhY3RvcnMgbWF5IGJlIG1vcmUgaW1wb3J0YW50IHRoYW4gYnJvYWQgZWNvbm9taWMgY29uZGl0aW9uc1xuIikNCiAgICB9DQogIH0NCn0NCmBgYA0KDQoNCkluIHN0ZXAgMTEuNSB3ZSBhbmFseXplIHRoZSB0aW1pbmcgb2YgZXZlbnRzIGluIHRoZSBkYXRhc2V0LiBXZSBjaGVjayBmb3IgdGhlIGZpcnN0IG9jY3VycmVuY2Ugb2YgYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gZXZlbnRzIGZvciBlYWNoIGNvbXBhbnksIGFuZCB0aGVuIGNhbGN1bGF0ZSB0aGUgcXVhbnRpbGVzIG9mIHRoZXNlIGV2ZW50IHRpbWVzLiBUaGlzIGluZm9ybWF0aW9uIGlzIHVzZWQgdG8gZGV0ZXJtaW5lIGFwcHJvcHJpYXRlIHRpbWUgaG9yaXpvbnMgZm9yIHByZWRpY3Rpb24gbW9kZWxzLiBUaGUgYW5hbHlzaXMgYWxzbyBpbmNsdWRlcyBhIGNvbXBhcmlzb24gb2YgdGhlIHRpbWluZyBvZiBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiBldmVudHMsIHByb3ZpZGluZyBpbnNpZ2h0cyBpbnRvIHRoZWlyIHJlbGF0aW9uc2hpcC4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTEuNTogVGltZS1EZXBlbmRlbnQgUHJlZGljdGlvbiBQZXJmb3JtYW5jZSB3aXRoIE1hY3JvIEFuYWx5c2lzDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjExLjU6IFRJTUUtREVQRU5ERU5UIFBSRURJQ1RJT04gUEVSRk9STUFOQ0UgV0lUSCBNQUNSTyBBTkFMWVNJU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgU2ltcGxpZmllZCBldmVudCB0aW1pbmcgYW5hbHlzaXMgdXNpbmcgYmFzZSBSDQpldmVudF90aW1pbmdfYW5hbHlzaXMgPC0gZnVuY3Rpb24oKSB7DQogIGNhdCgiQW5hbHl6aW5nIGV2ZW50IHRpbWluZyBpbiB0aGUgZGF0YXNldDpcbiIpDQogIA0KICAjIEdldCBhbGwgdW5pcXVlIGNvbXBhbmllcw0KICBhbGxfY29tcGFuaWVzIDwtIHVuaXF1ZShkYXRhJGN1c2lwKQ0KICBiYW5rX3RpbWVzIDwtIG51bWVyaWMoMCkNCiAgYWNxX3RpbWVzIDwtIG51bWVyaWMoMCkNCiAgDQogICMgRm9yIGVhY2ggY29tcGFueSwgZmluZCBpZiBhbmQgd2hlbiBldmVudHMgb2NjdXJyZWQNCiAgZm9yKGNvbXBhbnkgaW4gYWxsX2NvbXBhbmllcykgew0KICAgIGNvbXBhbnlfZGF0YSA8LSBkYXRhW2RhdGEkY3VzaXAgPT0gY29tcGFueSwgXQ0KICAgIA0KICAgICMgQ2hlY2sgZm9yIGJhbmtydXB0Y3kNCiAgICBpZihhbnkoY29tcGFueV9kYXRhJGJhbmtydXB0Y3kgPT0gMSkpIHsNCiAgICAgIGJhbmtfaW5kZXggPC0gd2hpY2goY29tcGFueV9kYXRhJGJhbmtydXB0Y3kgPT0gMSlbMV0NCiAgICAgIGJhbmtfdGltZXMgPC0gYyhiYW5rX3RpbWVzLCBjb21wYW55X2RhdGEkdHN0b3BbYmFua19pbmRleF0pDQogICAgfQ0KICAgIA0KICAgICMgQ2hlY2sgZm9yIGFjcXVpc2l0aW9uDQogICAgaWYoYW55KGNvbXBhbnlfZGF0YSRhY3F1aXNpdGlvbiA9PSAxKSkgew0KICAgICAgYWNxX2luZGV4IDwtIHdoaWNoKGNvbXBhbnlfZGF0YSRhY3F1aXNpdGlvbiA9PSAxKVsxXQ0KICAgICAgYWNxX3RpbWVzIDwtIGMoYWNxX3RpbWVzLCBjb21wYW55X2RhdGEkdHN0b3BbYWNxX2luZGV4XSkNCiAgICB9DQogIH0NCiAgDQogICMgQW5hbHl6ZSBiYW5rcnVwdGN5IHRpbWluZw0KICBpZihsZW5ndGgoYmFua190aW1lcykgPiAwKSB7DQogICAgYmFua19xdWFudGlsZXMgPC0gcXVhbnRpbGUoYmFua190aW1lcywgcHJvYnMgPSBjKDAuMjUsIDAuNSwgMC43NSksIG5hLnJtID0gVFJVRSkNCiAgICBjYXQoIlxuQmFua3J1cHRjeSB0aW1pbmcgKHllYXJzKTpcbiIpDQogICAgY2F0KCIgIFRvdGFsIGJhbmtydXB0Y2llczoiLCBsZW5ndGgoYmFua190aW1lcyksICJcbiIpDQogICAgY2F0KCIgIE1pbmltdW0gdGltZToiLCByb3VuZChtaW4oYmFua190aW1lcyksIDIpLCAiXG4iKQ0KICAgIGNhdCgiICAyNXRoIHBlcmNlbnRpbGU6Iiwgcm91bmQoYmFua19xdWFudGlsZXNbMV0sIDIpLCAiXG4iKQ0KICAgIGNhdCgiICBNZWRpYW4gdGltZToiLCByb3VuZChiYW5rX3F1YW50aWxlc1syXSwgMiksICJcbiIpDQogICAgY2F0KCIgIDc1dGggcGVyY2VudGlsZToiLCByb3VuZChiYW5rX3F1YW50aWxlc1szXSwgMiksICJcbiIpDQogICAgY2F0KCIgIE1heGltdW0gdGltZToiLCByb3VuZChtYXgoYmFua190aW1lcyksIDIpLCAiXG4iKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiXG5ObyBiYW5rcnVwdGN5IGV2ZW50cyBmb3VuZCBpbiB0aGUgZGF0YXNldC5cbiIpDQogIH0NCiAgDQogICMgQW5hbHl6ZSBhY3F1aXNpdGlvbiB0aW1pbmcNCiAgaWYobGVuZ3RoKGFjcV90aW1lcykgPiAwKSB7DQogICAgYWNxX3F1YW50aWxlcyA8LSBxdWFudGlsZShhY3FfdGltZXMsIHByb2JzID0gYygwLjI1LCAwLjUsIDAuNzUpLCBuYS5ybSA9IFRSVUUpDQogICAgY2F0KCJcbkFjcXVpc2l0aW9uIHRpbWluZyAoeWVhcnMpOlxuIikNCiAgICBjYXQoIiAgVG90YWwgYWNxdWlzaXRpb25zOiIsIGxlbmd0aChhY3FfdGltZXMpLCAiXG4iKQ0KICAgIGNhdCgiICBNaW5pbXVtIHRpbWU6Iiwgcm91bmQobWluKGFjcV90aW1lcyksIDIpLCAiXG4iKQ0KICAgIGNhdCgiICAyNXRoIHBlcmNlbnRpbGU6Iiwgcm91bmQoYWNxX3F1YW50aWxlc1sxXSwgMiksICJcbiIpDQogICAgY2F0KCIgIE1lZGlhbiB0aW1lOiIsIHJvdW5kKGFjcV9xdWFudGlsZXNbMl0sIDIpLCAiXG4iKQ0KICAgIGNhdCgiICA3NXRoIHBlcmNlbnRpbGU6Iiwgcm91bmQoYWNxX3F1YW50aWxlc1szXSwgMiksICJcbiIpDQogICAgY2F0KCIgIE1heGltdW0gdGltZToiLCByb3VuZChtYXgoYWNxX3RpbWVzKSwgMiksICJcbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJcbk5vIGFjcXVpc2l0aW9uIGV2ZW50cyBmb3VuZCBpbiB0aGUgZGF0YXNldC5cbiIpDQogIH0NCiAgDQogICMgUmV0dXJuIGV2ZW50IHF1YW50aWxlcyBmb3IgYWRhcHRpdmUgaG9yaXpvbiBzZWxlY3Rpb24NCiAgcmV0dXJuKGxpc3QoDQogICAgYmFua3J1cHRjeSA9IGlmKGxlbmd0aChiYW5rX3RpbWVzKSA+IDApIGJhbmtfcXVhbnRpbGVzIGVsc2UgTlVMTCwNCiAgICBhY3F1aXNpdGlvbiA9IGlmKGxlbmd0aChhY3FfdGltZXMpID4gMCkgYWNxX3F1YW50aWxlcyBlbHNlIE5VTEwNCiAgKSkNCn0NCg0KIyBSdW4gZXZlbnQgdGltaW5nIGFuYWx5c2lzDQpldmVudF90aW1pbmcgPC0gZXZlbnRfdGltaW5nX2FuYWx5c2lzKCkNCg0KIyBCYXNlZCBvbiBhbmFseXNpcywgZGV0ZXJtaW5lIGFwcHJvcHJpYXRlIHRpbWUgaG9yaXpvbnMNCmRldGVybWluZV9ob3Jpem9ucyA8LSBmdW5jdGlvbihldmVudF90aW1pbmcpIHsNCiAgYmFua19ob3Jpem9ucyA8LSBOVUxMDQogIGFjcV9ob3Jpem9ucyA8LSBOVUxMDQogIA0KICAjIEZvciBiYW5rcnVwdGN5DQogIGlmKCFpcy5udWxsKGV2ZW50X3RpbWluZyRiYW5rcnVwdGN5KSkgew0KICAgICMgVXNlIHF1YXJ0aWxlcyBvZiBhY3R1YWwgZXZlbnQgdGltZXMgYXMgaG9yaXpvbnMNCiAgICBiYW5rX2hvcml6b25zIDwtIGMoZXZlbnRfdGltaW5nJGJhbmtydXB0Y3lbMV0sIA0KICAgICAgICAgICAgICAgICAgICAgIGV2ZW50X3RpbWluZyRiYW5rcnVwdGN5WzJdLCANCiAgICAgICAgICAgICAgICAgICAgICBldmVudF90aW1pbmckYmFua3J1cHRjeVszXSkNCiAgICBiYW5rX2hvcml6b25zIDwtIHJvdW5kKGJhbmtfaG9yaXpvbnMpICMgUm91bmQgdG8gbmVhcmVzdCB5ZWFyDQogICAgYmFua19ob3Jpem9ucyA8LSB1bmlxdWUoYmFua19ob3Jpem9ucykgIyBSZW1vdmUgZHVwbGljYXRlcw0KICB9IGVsc2Ugew0KICAgICMgRGVmYXVsdCBob3Jpem9ucw0KICAgIGJhbmtfaG9yaXpvbnMgPC0gYygzLCA1LCAxMCkNCiAgfQ0KICANCiAgIyBGb3IgYWNxdWlzaXRpb24NCiAgaWYoIWlzLm51bGwoZXZlbnRfdGltaW5nJGFjcXVpc2l0aW9uKSkgew0KICAgICMgVXNlIHF1YXJ0aWxlcyBvZiBhY3R1YWwgZXZlbnQgdGltZXMgYXMgaG9yaXpvbnMNCiAgICBhY3FfaG9yaXpvbnMgPC0gYyhldmVudF90aW1pbmckYWNxdWlzaXRpb25bMV0sIA0KICAgICAgICAgICAgICAgICAgICBldmVudF90aW1pbmckYWNxdWlzaXRpb25bMl0sIA0KICAgICAgICAgICAgICAgICAgICBldmVudF90aW1pbmckYWNxdWlzaXRpb25bM10pDQogICAgYWNxX2hvcml6b25zIDwtIHJvdW5kKGFjcV9ob3Jpem9ucykgIyBSb3VuZCB0byBuZWFyZXN0IHllYXINCiAgICBhY3FfaG9yaXpvbnMgPC0gdW5pcXVlKGFjcV9ob3Jpem9ucykgIyBSZW1vdmUgZHVwbGljYXRlcw0KICB9IGVsc2Ugew0KICAgICMgRGVmYXVsdCBob3Jpem9ucw0KICAgIGFjcV9ob3Jpem9ucyA8LSBjKDMsIDUsIDEwKQ0KICB9DQogIA0KICAjIEVuc3VyZSB3ZSBoYXZlIHNob3J0LCBtZWRpdW0sIGFuZCBsb25nLXRlcm0gaG9yaXpvbnMNCiAgaWYobGVuZ3RoKGJhbmtfaG9yaXpvbnMpID09IDEpIHsNCiAgICBiYW5rX2hvcml6b25zIDwtIGMobWF4KDEsIGJhbmtfaG9yaXpvbnMgLSAyKSwgYmFua19ob3Jpem9ucywgbWluKDIwLCBiYW5rX2hvcml6b25zICsgNSkpDQogIH0gZWxzZSBpZihsZW5ndGgoYmFua19ob3Jpem9ucykgPT0gMikgew0KICAgIGJhbmtfaG9yaXpvbnMgPC0gYyhiYW5rX2hvcml6b25zLCBtaW4oMjAsIG1heChiYW5rX2hvcml6b25zKSArIDUpKQ0KICB9DQogIA0KICBpZihsZW5ndGgoYWNxX2hvcml6b25zKSA9PSAxKSB7DQogICAgYWNxX2hvcml6b25zIDwtIGMobWF4KDEsIGFjcV9ob3Jpem9ucyAtIDIpLCBhY3FfaG9yaXpvbnMsIG1pbigyMCwgYWNxX2hvcml6b25zICsgNSkpDQogIH0gZWxzZSBpZihsZW5ndGgoYWNxX2hvcml6b25zKSA9PSAyKSB7DQogICAgYWNxX2hvcml6b25zIDwtIGMoYWNxX2hvcml6b25zLCBtaW4oMjAsIG1heChhY3FfaG9yaXpvbnMpICsgNSkpDQogIH0NCiAgDQogIHJldHVybihsaXN0KGJhbmtydXB0Y3kgPSBiYW5rX2hvcml6b25zLCBhY3F1aXNpdGlvbiA9IGFjcV9ob3Jpem9ucykpDQp9DQoNCiMgR2V0IGFwcHJvcHJpYXRlIGhvcml6b25zDQpob3Jpem9ucyA8LSBkZXRlcm1pbmVfaG9yaXpvbnMoZXZlbnRfdGltaW5nKQ0KDQpjYXQoIlxuVXNpbmcgYWRhcHRpdmUgdGltZSBob3Jpem9ucyBiYXNlZCBvbiBldmVudCBkaXN0cmlidXRpb246XG4iKQ0KY2F0KCJCYW5rcnVwdGN5IGhvcml6b25zICh5ZWFycyk6IiwgcGFzdGUoaG9yaXpvbnMkYmFua3J1cHRjeSwgY29sbGFwc2U9IiwgIiksICJcbiIpDQpjYXQoIkFjcXVpc2l0aW9uIGhvcml6b25zICh5ZWFycyk6IiwgcGFzdGUoaG9yaXpvbnMkYWNxdWlzaXRpb24sIGNvbGxhcHNlPSIsICIpLCAiXG4iKQ0KDQojIEZpbmQgbm8tbWFjcm8gdmVyc2lvbnMgb2YgbW9kZWxzDQpjcmVhdGVfbm9fbWFjcm9fbW9kZWwgPC0gZnVuY3Rpb24obW9kZWwsIGV2ZW50X3R5cGUpIHsNCiAgIyBHZXQgbW9kZWwgZm9ybXVsYQ0KICBtb2RlbF9mb3JtdWxhIDwtIGZvcm11bGEobW9kZWwpDQogIA0KICAjIEV4dHJhY3QgYWxsIHRlcm1zDQogIGFsbF90ZXJtcyA8LSBhdHRyKHRlcm1zKG1vZGVsKSwgInRlcm0ubGFiZWxzIikNCiAgDQogICMgSWRlbnRpZnkgbWFjcm8gdmFyaWFibGVzIChpbmNsdWRpbmcgdGhlaXIgdGltZSBpbnRlcmFjdGlvbnMpDQogIG1hY3JvX3ZhcnMgPC0gYygiZ2RwX2dyb3d0aCIsICJnZHBfZGVmbGF0b3IiLCAidW5lbXBsb3llbWVudCIpDQogIG1hY3JvX3Rlcm1zIDwtIGMoKQ0KICANCiAgZm9yKHRlcm0gaW4gYWxsX3Rlcm1zKSB7DQogICAgIyBDaGVjayBpZiB0ZXJtIGNvbnRhaW5zIGFueSBtYWNybyB2YXJpYWJsZQ0KICAgIGlzX21hY3JvIDwtIEZBTFNFDQogICAgZm9yKG1hY3JvIGluIG1hY3JvX3ZhcnMpIHsNCiAgICAgIGlmKGdyZXBsKG1hY3JvLCB0ZXJtKSkgew0KICAgICAgICBpc19tYWNybyA8LSBUUlVFDQogICAgICAgIGJyZWFrDQogICAgICB9DQogICAgfQ0KICAgIA0KICAgIGlmKGlzX21hY3JvKSB7DQogICAgICBtYWNyb190ZXJtcyA8LSBjKG1hY3JvX3Rlcm1zLCB0ZXJtKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBJZiBubyBtYWNybyB0ZXJtcyBpbiBtb2RlbCwgcmV0dXJuIG9yaWdpbmFsIG1vZGVsDQogIGlmKGxlbmd0aChtYWNyb190ZXJtcykgPT0gMCkgew0KICAgIGNhdCgiTm8gbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgZm91bmQgaW4gdGhlIG1vZGVsLlxuIikNCiAgICByZXR1cm4obW9kZWwpDQogIH0NCiAgDQogIGNhdCgiRm91bmQiLCBsZW5ndGgobWFjcm9fdGVybXMpLCAibWFjcm9lY29ub21pYyB0ZXJtcyBpbiBtb2RlbC5cbiIpDQogIA0KICAjIENyZWF0ZSBmb3JtdWxhIHdpdGhvdXQgbWFjcm8gdmFyaWFibGVzDQogIG5vX21hY3JvX2Zvcm11bGEgPC0gbW9kZWxfZm9ybXVsYQ0KICBmb3IodGVybSBpbiBtYWNyb190ZXJtcykgew0KICAgIG5vX21hY3JvX2Zvcm11bGEgPC0gdXBkYXRlKG5vX21hY3JvX2Zvcm11bGEsIHBhc3RlMCgiLiB+IC4gLSAiLCB0ZXJtKSkNCiAgfQ0KICANCiAgIyBGaXQgbW9kZWwgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCiAgaWYoZXZlbnRfdHlwZSA9PSAiYmFua3J1cHRjeSIpIHsNCiAgICBub19tYWNyb19tb2RlbCA8LSBjb3hwaChub19tYWNyb19mb3JtdWxhLCBkYXRhID0gZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIH0gZWxzZSB7DQogICAgbm9fbWFjcm9fbW9kZWwgPC0gY294cGgobm9fbWFjcm9fZm9ybXVsYSwgZGF0YSA9IGRhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICB9DQogIA0KICByZXR1cm4obm9fbWFjcm9fbW9kZWwpDQp9DQoNCiMgQ3JlYXRlIG5vLW1hY3JvIG1vZGVscw0Kbm9fbWFjcm9fYmFua3J1cHRjeV9tb2RlbCA8LSBjcmVhdGVfbm9fbWFjcm9fbW9kZWwoZmluYWxfYmFua3J1cHRjeV9tb2RlbCwgImJhbmtydXB0Y3kiKQ0Kbm9fbWFjcm9fYWNxdWlzaXRpb25fbW9kZWwgPC0gY3JlYXRlX25vX21hY3JvX21vZGVsKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiYWNxdWlzaXRpb24iKQ0KDQojIEVuaGFuY2VkIHRpbWUtZGVwZW5kZW50IEFVQyBmdW5jdGlvbiB3aXRoIG1hY3JvIGNvbXBhcmlzb24NCnRpbWVfZGVwZW5kZW50X2F1Y193aXRoX21hY3JvIDwtIGZ1bmN0aW9uKGZ1bGxfbW9kZWwsIG5vX21hY3JvX21vZGVsLCBtb2RlbF9uYW1lLCBldmVudF90eXBlLCBob3Jpem9ucykgew0KICBjYXQoIlxuQ2FsY3VsYXRpbmcgdGltZS1kZXBlbmRlbnQgQVVDIGZvciIsIG1vZGVsX25hbWUsICJtb2RlbCB3aXRoIG1hY3JvIGNvbXBhcmlzb24uLi5cbiIpDQogIA0KICAjIFByZXBhcmUgcmVzdWx0cw0KICByZXN1bHRzIDwtIGRhdGEuZnJhbWUoDQogICAgSG9yaXpvbiA9IGhvcml6b25zLA0KICAgIEZ1bGxfQVVDID0gTkEsDQogICAgTm9NYWNyb19BVUMgPSBOQSwNCiAgICBBVUNfRGlmZmVyZW5jZSA9IE5BLA0KICAgIEFVQ19QY3RfSW1wcm92ZW1lbnQgPSBOQSwNCiAgICBFdmVudHMgPSBOQSwNCiAgICBUb3RhbCA9IE5BDQogICkNCiAgDQogIGZvcihpIGluIDE6bGVuZ3RoKGhvcml6b25zKSkgew0KICAgIGhvcml6b24gPC0gaG9yaXpvbnNbaV0NCiAgICBjYXQoIlByb2Nlc3NpbmcgaG9yaXpvbjoiLCBob3Jpem9uLCAieWVhcnMuLi5cbiIpDQogICAgDQogICAgIyBHZXQgYWxsIGNvbXBhbmllcw0KICAgIGFsbF9jb21wYW5pZXMgPC0gdW5pcXVlKGRhdGEkY3VzaXApDQogICAgDQogICAgIyBQcmVwYXJlIGRhdGEgZm9yIHRoaXMgaG9yaXpvbg0KICAgIGhvcml6b25fZGF0YSA8LSBkYXRhLmZyYW1lKCkNCiAgICANCiAgICBmb3IoY29tcGFueSBpbiBhbGxfY29tcGFuaWVzKSB7DQogICAgICAjIEdldCBkYXRhIGZvciB0aGlzIGNvbXBhbnkNCiAgICAgIGNvbXBhbnlfZGF0YSA8LSBkYXRhW2RhdGEkY3VzaXAgPT0gY29tcGFueSwgXQ0KICAgICAgDQogICAgICAjIEdldCBkYXRhIHVwIHRvIGhvcml6b24NCiAgICAgIGNvbXBhbnlfYXRfaG9yaXpvbiA8LSBjb21wYW55X2RhdGFbY29tcGFueV9kYXRhJHRzdG9wIDw9IGhvcml6b24sIF0NCiAgICAgIA0KICAgICAgaWYobnJvdyhjb21wYW55X2F0X2hvcml6b24pID4gMCkgew0KICAgICAgICAjIEdldCBsYXN0IG9ic2VydmF0aW9uIGJlZm9yZSBob3Jpem9uDQogICAgICAgIGxhc3Rfb2JzX2luZGV4IDwtIHdoaWNoLm1heChjb21wYW55X2F0X2hvcml6b24kdHN0b3ApDQogICAgICAgIGxhc3Rfb2JzIDwtIGNvbXBhbnlfYXRfaG9yaXpvbltsYXN0X29ic19pbmRleCwgXQ0KICAgICAgICANCiAgICAgICAgIyBBZGQgdG8gaG9yaXpvbiBkYXRhDQogICAgICAgIGhvcml6b25fZGF0YSA8LSByYmluZChob3Jpem9uX2RhdGEsIGxhc3Rfb2JzKQ0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICBpZihucm93KGhvcml6b25fZGF0YSkgPiAwKSB7DQogICAgICAjIEdldCBldmVudCBpbmRpY2F0b3JzDQogICAgICBldmVudF9pbmRpY2F0b3IgPC0gaG9yaXpvbl9kYXRhW1tldmVudF90eXBlXV0NCiAgICAgIA0KICAgICAgIyBHZXQgcmlzayBzY29yZXMgZm9yIGZ1bGwgbW9kZWwNCiAgICAgIGZ1bGxfcmlza19zY29yZXMgPC0gdHJ5Q2F0Y2goew0KICAgICAgICBwcmVkaWN0KGZ1bGxfbW9kZWwsIG5ld2RhdGEgPSBob3Jpem9uX2RhdGEsIHR5cGUgPSAicmlzayIpDQogICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgY2F0KCIgIEVycm9yIGNhbGN1bGF0aW5nIGZ1bGwgbW9kZWwgcmlzayBzY29yZXM6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgICAgICByZXR1cm4ocmVwKE5BLCBucm93KGhvcml6b25fZGF0YSkpKQ0KICAgICAgfSkNCiAgICAgIA0KICAgICAgIyBHZXQgcmlzayBzY29yZXMgZm9yIG5vLW1hY3JvIG1vZGVsDQogICAgICBub19tYWNyb19yaXNrX3Njb3JlcyA8LSB0cnlDYXRjaCh7DQogICAgICAgIHByZWRpY3Qobm9fbWFjcm9fbW9kZWwsIG5ld2RhdGEgPSBob3Jpem9uX2RhdGEsIHR5cGUgPSAicmlzayIpDQogICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgY2F0KCIgIEVycm9yIGNhbGN1bGF0aW5nIG5vLW1hY3JvIG1vZGVsIHJpc2sgc2NvcmVzOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICAgICAgcmV0dXJuKHJlcChOQSwgbnJvdyhob3Jpem9uX2RhdGEpKSkNCiAgICAgIH0pDQogICAgICANCiAgICAgICMgQ291bnQgZXZlbnRzDQogICAgICBuX2V2ZW50cyA8LSBzdW0oZXZlbnRfaW5kaWNhdG9yKQ0KICAgICAgbl90b3RhbCA8LSBucm93KGhvcml6b25fZGF0YSkNCiAgICAgIA0KICAgICAgY2F0KCIgIENvbXBhbmllcyBvYnNlcnZlZCBhdCBob3Jpem9uOiIsIG5fdG90YWwsICJcbiIpDQogICAgICBjYXQoIiAgRXZlbnRzIGJ5IGhvcml6b246Iiwgbl9ldmVudHMsICIoIiwgcm91bmQoMTAwKm5fZXZlbnRzL25fdG90YWwsIDEpLCAiJSlcbiIpDQogICAgICANCiAgICAgICMgU3RvcmUgZXZlbnQgY291bnRzDQogICAgICByZXN1bHRzJEV2ZW50c1tpXSA8LSBuX2V2ZW50cw0KICAgICAgcmVzdWx0cyRUb3RhbFtpXSA8LSBuX3RvdGFsDQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIEFVQ3MgaWYgcG9zc2libGUNCiAgICAgIGlmKG5fZXZlbnRzID4gMCAmJiBuX2V2ZW50cyA8IG5fdG90YWwpIHsNCiAgICAgICAgaWYocmVxdWlyZU5hbWVzcGFjZSgicFJPQyIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgICAgICAgIGxpYnJhcnkocFJPQykNCiAgICAgICAgICANCiAgICAgICAgICAjIENhbGN1bGF0ZSBBVUMgZm9yIGZ1bGwgbW9kZWwNCiAgICAgICAgICBmdWxsX2F1YyA8LSBOQQ0KICAgICAgICAgIGlmKGFsbCghaXMubmEoZnVsbF9yaXNrX3Njb3JlcykpKSB7DQogICAgICAgICAgICB0cnlDYXRjaCh7DQogICAgICAgICAgICAgIGZ1bGxfcm9jIDwtIHJvYyhldmVudF9pbmRpY2F0b3IsIGZ1bGxfcmlza19zY29yZXMsIHF1aWV0ID0gVFJVRSkNCiAgICAgICAgICAgICAgZnVsbF9hdWMgPC0gYXMubnVtZXJpYyhhdWMoZnVsbF9yb2MpKQ0KICAgICAgICAgICAgICByZXN1bHRzJEZ1bGxfQVVDW2ldIDwtIGZ1bGxfYXVjDQogICAgICAgICAgICAgIGNhdCgiICBGdWxsIG1vZGVsIEFVQyBhdCIsIGhvcml6b24sICJ5ZWFyczoiLCByb3VuZChmdWxsX2F1YywgNCksICJcbiIpDQogICAgICAgICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgICAgICAgY2F0KCIgIEVycm9yIGNhbGN1bGF0aW5nIGZ1bGwgbW9kZWwgQVVDOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICAgICAgICAgIH0pDQogICAgICAgICAgfQ0KICAgICAgICAgIA0KICAgICAgICAgICMgQ2FsY3VsYXRlIEFVQyBmb3Igbm8tbWFjcm8gbW9kZWwNCiAgICAgICAgICBub19tYWNyb19hdWMgPC0gTkENCiAgICAgICAgICBpZihhbGwoIWlzLm5hKG5vX21hY3JvX3Jpc2tfc2NvcmVzKSkpIHsNCiAgICAgICAgICAgIHRyeUNhdGNoKHsNCiAgICAgICAgICAgICAgbm9fbWFjcm9fcm9jIDwtIHJvYyhldmVudF9pbmRpY2F0b3IsIG5vX21hY3JvX3Jpc2tfc2NvcmVzLCBxdWlldCA9IFRSVUUpDQogICAgICAgICAgICAgIG5vX21hY3JvX2F1YyA8LSBhcy5udW1lcmljKGF1Yyhub19tYWNyb19yb2MpKQ0KICAgICAgICAgICAgICByZXN1bHRzJE5vTWFjcm9fQVVDW2ldIDwtIG5vX21hY3JvX2F1Yw0KICAgICAgICAgICAgICBjYXQoIiAgTm8tbWFjcm8gbW9kZWwgQVVDIGF0IiwgaG9yaXpvbiwgInllYXJzOiIsIHJvdW5kKG5vX21hY3JvX2F1YywgNCksICJcbiIpDQogICAgICAgICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgICAgICAgY2F0KCIgIEVycm9yIGNhbGN1bGF0aW5nIG5vLW1hY3JvIG1vZGVsIEFVQzoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICAgICAgICB9KQ0KICAgICAgICAgIH0NCiAgICAgICAgICANCiAgICAgICAgICAjIENhbGN1bGF0ZSBpbXByb3ZlbWVudCBmcm9tIG1hY3JvIHZhcmlhYmxlcw0KICAgICAgICAgIGlmKCFpcy5uYShmdWxsX2F1YykgJiYgIWlzLm5hKG5vX21hY3JvX2F1YykpIHsNCiAgICAgICAgICAgIGF1Y19kaWZmIDwtIGZ1bGxfYXVjIC0gbm9fbWFjcm9fYXVjDQogICAgICAgICAgICBwY3RfaW1wcm92ZW1lbnQgPC0gMTAwICogYXVjX2RpZmYgLyBub19tYWNyb19hdWMNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgcmVzdWx0cyRBVUNfRGlmZmVyZW5jZVtpXSA8LSBhdWNfZGlmZg0KICAgICAgICAgICAgcmVzdWx0cyRBVUNfUGN0X0ltcHJvdmVtZW50W2ldIDwtIHBjdF9pbXByb3ZlbWVudA0KICAgICAgICAgICAgDQogICAgICAgICAgICBjYXQoIiAgSW1wcm92ZW1lbnQgZnJvbSBtYWNybyB2YXJpYWJsZXM6Iiwgcm91bmQoYXVjX2RpZmYsIDQpLCANCiAgICAgICAgICAgICAgICAiKCIsIHJvdW5kKHBjdF9pbXByb3ZlbWVudCwgMiksICIlKVxuIikNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgIyBUZXN0IGlmIGltcHJvdmVtZW50IGlzIHNpZ25pZmljYW50DQogICAgICAgICAgICB0cnlDYXRjaCh7DQogICAgICAgICAgICAgIHJvY190ZXN0IDwtIHJvYy50ZXN0KGZ1bGxfcm9jLCBub19tYWNyb19yb2MsIG1ldGhvZD0iZGVsb25nIikNCiAgICAgICAgICAgICAgaXNfc2lnbmlmaWNhbnQgPC0gcm9jX3Rlc3QkcC52YWx1ZSA8IDAuMDUNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIGNhdCgiICBTdGF0aXN0aWNhbCBzaWduaWZpY2FuY2U6IHAgPSIsIGZvcm1hdC5wdmFsKHJvY190ZXN0JHAudmFsdWUsIGRpZ2l0cyA9IDMpLA0KICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzX3NpZ25pZmljYW50LCAiKHNpZ25pZmljYW50KSIsICIobm90IHNpZ25pZmljYW50KSIpLCAiXG4iKQ0KICAgICAgICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgICAgICAgICAgIGNhdCgiICBDb3VsZCBub3QgdGVzdCBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2U6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgICAgICAgICAgfSkNCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGNhdCgiICBJbnN1ZmZpY2llbnQgZXZlbnRzIGZvciBBVUMgY2FsY3VsYXRpb25cbiIpDQogICAgICB9DQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiICBObyBkYXRhIGF2YWlsYWJsZSBmb3IgdGhpcyBob3Jpem9uXG4iKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBQcmludCBzdW1tYXJ5IG9mIHJlc3VsdHMNCiAgY2F0KCJcblRpbWUtZGVwZW5kZW50IEFVQyBzdW1tYXJ5IGZvciIsIG1vZGVsX25hbWUsICJwcmVkaWN0aW9uOlxuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICANCiAgZm9yKGkgaW4gMTpucm93KHJlc3VsdHMpKSB7DQogICAgaG9yaXpvbiA8LSByZXN1bHRzJEhvcml6b25baV0NCiAgICBmdWxsX2F1YyA8LSByZXN1bHRzJEZ1bGxfQVVDW2ldDQogICAgbm9fbWFjcm9fYXVjIDwtIHJlc3VsdHMkTm9NYWNyb19BVUNbaV0NCiAgICBhdWNfZGlmZiA8LSByZXN1bHRzJEFVQ19EaWZmZXJlbmNlW2ldDQogICAgcGN0X2ltcCA8LSByZXN1bHRzJEFVQ19QY3RfSW1wcm92ZW1lbnRbaV0NCiAgICBldmVudHMgPC0gcmVzdWx0cyRFdmVudHNbaV0NCiAgICB0b3RhbCA8LSByZXN1bHRzJFRvdGFsW2ldDQogICAgDQogICAgaWYoIWlzLm5hKGZ1bGxfYXVjKSkgew0KICAgICAgY2F0KHNwcmludGYoIkF0ICVkLXllYXIgaG9yaXpvbiAoJWQgZXZlbnRzLCAlLjFmJSUpOlxuIiwgDQogICAgICAgICAgICAgICAgaG9yaXpvbiwgZXZlbnRzLCAxMDAqZXZlbnRzL3RvdGFsKSkNCiAgICAgIGNhdChzcHJpbnRmKCIgIEZ1bGwgbW9kZWwgQVVDID0gJS40ZlxuIiwgZnVsbF9hdWMpKQ0KICAgICAgDQogICAgICBpZighaXMubmEobm9fbWFjcm9fYXVjKSkgew0KICAgICAgICBjYXQoc3ByaW50ZigiICBOby1tYWNybyBBVUMgPSAlLjRmXG4iLCBub19tYWNyb19hdWMpKQ0KICAgICAgICBjYXQoc3ByaW50ZigiICBNYWNybyBjb250cmlidXRpb24gPSAlLjRmICglLjJmJSUpXG4iLCBhdWNfZGlmZiwgcGN0X2ltcCkpDQogICAgICAgIA0KICAgICAgICAjIEludGVycHJldGF0aW9uIG9mIG1hY3JvIGNvbnRyaWJ1dGlvbg0KICAgICAgICBjYXQoIiAgSW50ZXJwcmV0YXRpb246IE1hY3JvIHZhcmlhYmxlcyAiKQ0KICAgICAgICBpZihhdWNfZGlmZiA+IDAuMDMpIHsNCiAgICAgICAgICBjYXQoIlNVQlNUQU5USUFMTFkgaW1wcm92ZSBwcmVkaWN0aW9uIGF0IHRoaXMgaG9yaXpvblxuIikNCiAgICAgICAgfSBlbHNlIGlmKGF1Y19kaWZmID4gMC4wMSkgew0KICAgICAgICAgIGNhdCgiTU9ERVJBVEVMWSBpbXByb3ZlIHByZWRpY3Rpb24gYXQgdGhpcyBob3Jpem9uXG4iKQ0KICAgICAgICB9IGVsc2UgaWYoYXVjX2RpZmYgPiAwKSB7DQogICAgICAgICAgY2F0KCJTTElHSFRMWSBpbXByb3ZlIHByZWRpY3Rpb24gYXQgdGhpcyBob3Jpem9uXG4iKQ0KICAgICAgICB9IGVsc2UgaWYoYXVjX2RpZmYgPiAtMC4wMSkgew0KICAgICAgICAgIGNhdCgiZG8gbm90IG1lYW5pbmdmdWxseSBhZmZlY3QgcHJlZGljdGlvbiBhdCB0aGlzIGhvcml6b25cbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCJXT1JTRU4gcHJlZGljdGlvbiBhdCB0aGlzIGhvcml6b25cbiIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICAgIGNhdCgiXG4iKQ0KICAgIH0gZWxzZSB7DQogICAgICBjYXQoc3ByaW50ZigiQXQgJWQteWVhciBob3Jpem9uOiBBVUMgbm90IGF2YWlsYWJsZVxuXG4iLCBob3Jpem9uKSkNCiAgICB9DQogIH0NCiAgDQogIHJldHVybihyZXN1bHRzKQ0KfQ0KDQojIENhbGN1bGF0ZSB0aW1lLWRlcGVuZGVudCBBVUMgd2l0aCBtYWNybyBjb21wYXJpc29uDQpiYW5rcnVwdGN5X3RkX2F1YyA8LSB0aW1lX2RlcGVuZGVudF9hdWNfd2l0aF9tYWNybygNCiAgZmluYWxfYmFua3J1cHRjeV9tb2RlbCwgbm9fbWFjcm9fYmFua3J1cHRjeV9tb2RlbCwNCiAgImJhbmtydXB0Y3kiLCAiYmFua3J1cHRjeSIsIGhvcml6b25zJGJhbmtydXB0Y3kpDQoNCmFjcXVpc2l0aW9uX3RkX2F1YyA8LSB0aW1lX2RlcGVuZGVudF9hdWNfd2l0aF9tYWNybygNCiAgZmluYWxfYWNxdWlzaXRpb25fbW9kZWwsIG5vX21hY3JvX2FjcXVpc2l0aW9uX21vZGVsLA0KICAiYWNxdWlzaXRpb24iLCAiYWNxdWlzaXRpb24iLCBob3Jpem9ucyRhY3F1aXNpdGlvbikNCg0KIyBBbmFseXplIG1hY3JvIHZhcmlhYmxlIGltcG9ydGFuY2UgYWNyb3NzIHRpbWUgaG9yaXpvbnMNCmNhdCgiXG5BTkFMWVNJUyBPRiBNQUNST0VDT05PTUlDIElNUEFDVCBBQ1JPU1MgVElNRSBIT1JJWk9OUzpcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gYW5hbHl6ZSBtYWNybyBpbXBhY3QgdHJlbmQNCmFuYWx5emVfbWFjcm9fdHJlbmQgPC0gZnVuY3Rpb24odGRfYXVjX3Jlc3VsdHMsIG1vZGVsX25hbWUpIHsNCiAgaWYoYWxsKGlzLm5hKHRkX2F1Y19yZXN1bHRzJEFVQ19EaWZmZXJlbmNlKSkpIHsNCiAgICBjYXQoIkluc3VmZmljaWVudCBkYXRhIHRvIGFuYWx5emUgbWFjcm8gaW1wYWN0IHRyZW5kIGZvciIsIG1vZGVsX25hbWUsICJtb2RlbC5cbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0NCiAgDQogICMgRXh0cmFjdCBub24tTkEgcm93cw0KICB2YWxpZF9yb3dzIDwtICFpcy5uYSh0ZF9hdWNfcmVzdWx0cyRBVUNfRGlmZmVyZW5jZSkNCiAgdmFsaWRfcmVzdWx0cyA8LSB0ZF9hdWNfcmVzdWx0c1t2YWxpZF9yb3dzLCBdDQogIA0KICBpZihucm93KHZhbGlkX3Jlc3VsdHMpIDwgMikgew0KICAgIGNhdCgiTmVlZCBhdCBsZWFzdCB0d28gdmFsaWQgdGltZSBob3Jpem9ucyB0byBhbmFseXplIHRyZW5kIGZvciIsIG1vZGVsX25hbWUsICJtb2RlbC5cbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0NCiAgDQogICMgU29ydCBieSBob3Jpem9uDQogIHZhbGlkX3Jlc3VsdHMgPC0gdmFsaWRfcmVzdWx0c1tvcmRlcih2YWxpZF9yZXN1bHRzJEhvcml6b24pLCBdDQogIA0KICAjIEFuYWx5emUgdHJlbmQNCiAgZmlyc3RfaG9yaXpvbiA8LSB2YWxpZF9yZXN1bHRzJEhvcml6b25bMV0NCiAgbGFzdF9ob3Jpem9uIDwtIHZhbGlkX3Jlc3VsdHMkSG9yaXpvbltucm93KHZhbGlkX3Jlc3VsdHMpXQ0KICANCiAgZmlyc3RfaW1wYWN0IDwtIHZhbGlkX3Jlc3VsdHMkQVVDX0RpZmZlcmVuY2VbMV0NCiAgbGFzdF9pbXBhY3QgPC0gdmFsaWRfcmVzdWx0cyRBVUNfRGlmZmVyZW5jZVtucm93KHZhbGlkX3Jlc3VsdHMpXQ0KICANCiAgaW1wYWN0X2RpZmYgPC0gbGFzdF9pbXBhY3QgLSBmaXJzdF9pbXBhY3QNCiAgDQogIGNhdCgiXG5NYWNybyBpbXBhY3QgdHJlbmQgZm9yIiwgbW9kZWxfbmFtZSwgIm1vZGVsOlxuIikNCiAgY2F0KCItIFNob3J0LXRlcm0gaG9yaXpvbiAoIiwgZmlyc3RfaG9yaXpvbiwgInllYXJzKTogSW1wYWN0ID0gIiwgcm91bmQoZmlyc3RfaW1wYWN0LCA0KSwgIlxuIiwgc2VwPSIiKQ0KICBjYXQoIi0gTG9uZy10ZXJtIGhvcml6b24gKCIsIGxhc3RfaG9yaXpvbiwgInllYXJzKTogSW1wYWN0ID0gIiwgcm91bmQobGFzdF9pbXBhY3QsIDQpLCAiXG4iLCBzZXA9IiIpDQogIA0KICBpZihpbXBhY3RfZGlmZiA+IDAuMDEpIHsNCiAgICBjYXQoIi0gVHJlbmQ6IE1hY3JvIGltcGFjdCBJTkNSRUFTRVMgd2l0aCBsb25nZXIgdGltZSBob3Jpem9uc1xuIikNCiAgICBjYXQoIiAgRWNvbm9taWMgaW50ZXJwcmV0YXRpb246IE1hY3JvZWNvbm9taWMgZmFjdG9ycyBiZWNvbWUgTU9SRSBpbXBvcnRhbnRcbiIpDQogICAgY2F0KCIgIGZvciBwcmVkaWN0aW5nIGxvbmctdGVybSBvdXRjb21lcyBjb21wYXJlZCB0byBzaG9ydC10ZXJtIG91dGNvbWVzXG4iKQ0KICB9IGVsc2UgaWYoaW1wYWN0X2RpZmYgPCAtMC4wMSkgew0KICAgIGNhdCgiLSBUcmVuZDogTWFjcm8gaW1wYWN0IERFQ1JFQVNFUyB3aXRoIGxvbmdlciB0aW1lIGhvcml6b25zXG4iKQ0KICAgIGNhdCgiICBFY29ub21pYyBpbnRlcnByZXRhdGlvbjogTWFjcm9lY29ub21pYyBmYWN0b3JzIGFyZSBNT1JFIGltcG9ydGFudFxuIikNCiAgICBjYXQoIiAgZm9yIHByZWRpY3Rpbmcgc2hvcnQtdGVybSBvdXRjb21lcyBjb21wYXJlZCB0byBsb25nLXRlcm0gb3V0Y29tZXNcbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCItIFRyZW5kOiBNYWNybyBpbXBhY3QgaXMgQ09OU0lTVEVOVCBhY3Jvc3MgdGltZSBob3Jpem9uc1xuIikNCiAgICBjYXQoIiAgRWNvbm9taWMgaW50ZXJwcmV0YXRpb246IE1hY3JvZWNvbm9taWMgZmFjdG9ycyBoYXZlIHNpbWlsYXIgaW1wb3J0YW5jZVxuIikNCiAgICBjYXQoIiAgZm9yIGJvdGggc2hvcnQtdGVybSBhbmQgbG9uZy10ZXJtIHByZWRpY3Rpb25cbiIpDQogIH0NCiAgDQogICMgUG90ZW50aWFsIGV4cGxhbmF0aW9uIGJhc2VkIG9uIG1vZGVsIHR5cGUNCiAgaWYobW9kZWxfbmFtZSA9PSAiYmFua3J1cHRjeSIpIHsNCiAgICBpZihpbXBhY3RfZGlmZiA+IDAuMDEpIHsNCiAgICAgIGNhdCgiICBCdXNpbmVzcyBpbXBsaWNhdGlvbjogTG9uZy10ZXJtIGJhbmtydXB0Y3kgcmlzayBpcyBtb3JlIHN5c3RlbWF0aWNhbGx5XG4iKQ0KICAgICAgY2F0KCIgIGxpbmtlZCB0byBlY29ub21pYyBjb25kaXRpb25zLCB3aGlsZSBzaG9ydC10ZXJtIHJpc2sgZGVwZW5kcyBtb3JlIG9uXG4iKQ0KICAgICAgY2F0KCIgIGNvbXBhbnktc3BlY2lmaWMgZmluYW5jaWFsIGhlYWx0aFxuIikNCiAgICB9IGVsc2UgaWYoaW1wYWN0X2RpZmYgPCAtMC4wMSkgew0KICAgICAgY2F0KCIgIEJ1c2luZXNzIGltcGxpY2F0aW9uOiBJbW1lZGlhdGUgYmFua3J1cHRjeSByaXNrIGlzIG1vcmUgc2Vuc2l0aXZlIHRvXG4iKQ0KICAgICAgY2F0KCIgIGVjb25vbWljIGNvbmRpdGlvbnMsIHdoaWxlIGxvbmctdGVybSBzdXJ2aXZhbCBkZXBlbmRzIG1vcmUgb25cbiIpDQogICAgICBjYXQoIiAgY29tcGFueS1zcGVjaWZpYyBmYWN0b3JzIGFuZCBhZGFwdGFiaWxpdHlcbiIpDQogICAgfQ0KICB9IGVsc2UgaWYobW9kZWxfbmFtZSA9PSAiYWNxdWlzaXRpb24iKSB7DQogICAgaWYoaW1wYWN0X2RpZmYgPiAwLjAxKSB7DQogICAgICBjYXQoIiAgQnVzaW5lc3MgaW1wbGljYXRpb246IExvbmctdGVybSBhY3F1aXNpdGlvbiBwYXR0ZXJucyBmb2xsb3cgZWNvbm9taWNcbiIpDQogICAgICBjYXQoIiAgY3ljbGVzIG1vcmUgY2xvc2VseSwgd2hpbGUgc2hvcnQtdGVybSBNJkEgYWN0aXZpdHkgbWF5IGJlIGRyaXZlbiBtb3JlXG4iKQ0KICAgICAgY2F0KCIgIGJ5IHN0cmF0ZWdpYyBvcHBvcnR1bml0aWVzIGFuZCBjb21wYW55LXNwZWNpZmljIGZhY3RvcnNcbiIpDQogICAgfSBlbHNlIGlmKGltcGFjdF9kaWZmIDwgLTAuMDEpIHsNCiAgICAgIGNhdCgiICBCdXNpbmVzcyBpbXBsaWNhdGlvbjogU2hvcnQtdGVybSBhY3F1aXNpdGlvbiBsaWtlbGlob29kIGlzIG1vcmVcbiIpDQogICAgICBjYXQoIiAgaW5mbHVlbmNlZCBieSBjdXJyZW50IGVjb25vbWljIGNvbmRpdGlvbnMsIHdoaWxlIGxvbmctdGVybSBhY3F1aXNpdGlvblxuIikNCiAgICAgIGNhdCgiICBwYXR0ZXJucyBkZXBlbmQgbW9yZSBvbiBjb21wYW55LXNwZWNpZmljIGF0dHJhY3RpdmVuZXNzXG4iKQ0KICAgIH0NCiAgfQ0KICANCiAgcmV0dXJuKGltcGFjdF9kaWZmKQ0KfQ0KDQojIEFuYWx5emUgdHJlbmRzIGZvciBib3RoIG1vZGVscw0KYmFua190cmVuZCA8LSBhbmFseXplX21hY3JvX3RyZW5kKGJhbmtydXB0Y3lfdGRfYXVjLCAiYmFua3J1cHRjeSIpDQphY3FfdHJlbmQgPC0gYW5hbHl6ZV9tYWNyb190cmVuZChhY3F1aXNpdGlvbl90ZF9hdWMsICJhY3F1aXNpdGlvbiIpDQoNCiMgQ29tcGFyZSB0cmVuZHMgYmV0d2VlbiBtb2RlbHMNCmlmKCFpcy5udWxsKGJhbmtfdHJlbmQpICYmICFpcy5udWxsKGFjcV90cmVuZCkpIHsNCiAgY2F0KCJcbkNPTVBBUkFUSVZFIEFOQUxZU0lTIE9GIFRJTUUtREVQRU5ERU5UIE1BQ1JPIEVGRkVDVFM6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICANCiAgaWYoc2lnbihiYW5rX3RyZW5kKSA9PSBzaWduKGFjcV90cmVuZCkpIHsNCiAgICBpZihiYW5rX3RyZW5kID4gMCAmJiBhY3FfdHJlbmQgPiAwKSB7DQogICAgICBjYXQoIkJvdGggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gbW9kZWxzIHNob3cgSU5DUkVBU0lORyBpbXBvcnRhbmNlIG9mXG4iKQ0KICAgICAgY2F0KCJtYWNyb2Vjb25vbWljIGZhY3RvcnMgb3ZlciBsb25nZXIgdGltZSBob3Jpem9ucy5cblxuIikNCiAgICAgIGNhdCgiVGhpcyBzdWdnZXN0cyB0aGF0IGxvbmctdGVybSBjb3Jwb3JhdGUgb3V0Y29tZXMgYXJlIG1vcmUgc3lzdGVtYXRpY2FsbHlcbiIpDQogICAgICBjYXQoImxpbmtlZCB0byBlY29ub21pYyBjeWNsZXMgdGhhbiBzaG9ydC10ZXJtIG91dGNvbWVzLCByZWdhcmRsZXNzIG9mIHdoZXRoZXJcbiIpDQogICAgICBjYXQoInRoZSBvdXRjb21lIGlzIGJhbmtydXB0Y3kgb3IgYWNxdWlzaXRpb24uXG4iKQ0KICAgIH0gZWxzZSBpZihiYW5rX3RyZW5kIDwgMCAmJiBhY3FfdHJlbmQgPCAwKSB7DQogICAgICBjYXQoIkJvdGggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gbW9kZWxzIHNob3cgREVDUkVBU0lORyBpbXBvcnRhbmNlIG9mXG4iKQ0KICAgICAgY2F0KCJtYWNyb2Vjb25vbWljIGZhY3RvcnMgb3ZlciBsb25nZXIgdGltZSBob3Jpem9ucy5cblxuIikNCiAgICAgIGNhdCgiVGhpcyBzdWdnZXN0cyB0aGF0IGltbWVkaWF0ZSBjb3Jwb3JhdGUgb3V0Y29tZXMgYXJlIG1vcmUgc2Vuc2l0aXZlIHRvXG4iKQ0KICAgICAgY2F0KCJjdXJyZW50IGVjb25vbWljIGNvbmRpdGlvbnMsIHdoaWxlIGxvbmctdGVybSBvdXRjb21lcyBkZXBlbmQgbW9yZSBvblxuIikNCiAgICAgIGNhdCgiY29tcGFueS1zcGVjaWZpYyBmYWN0b3JzIGFuZCBhZGFwdGFiaWxpdHkuXG4iKQ0KICAgIH0gZWxzZSB7DQogICAgICBjYXQoIkJvdGggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gbW9kZWxzIHNob3cgQ09OU0lTVEVOVCBpbXBvcnRhbmNlIG9mXG4iKQ0KICAgICAgY2F0KCJtYWNyb2Vjb25vbWljIGZhY3RvcnMgYWNyb3NzIHRpbWUgaG9yaXpvbnMuXG5cbiIpDQogICAgICBjYXQoIlRoaXMgc3VnZ2VzdHMgdGhhdCBlY29ub21pYyBjb25kaXRpb25zIGhhdmUgc2ltaWxhciBpbmZsdWVuY2Ugb24gY29ycG9yYXRlXG4iKQ0KICAgICAgY2F0KCJvdXRjb21lcyByZWdhcmRsZXNzIG9mIHRoZSB0aW1lIGhvcml6b24gY29uc2lkZXJlZC5cbiIpDQogICAgfQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiQmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gbW9kZWxzIHNob3cgT1BQT1NJVEUgdHJlbmRzIGluIHRoZSBpbXBvcnRhbmNlXG4iKQ0KICAgIGNhdCgib2YgbWFjcm9lY29ub21pYyBmYWN0b3JzIGFjcm9zcyB0aW1lIGhvcml6b25zLlxuXG4iKQ0KICAgIA0KICAgIGlmKGJhbmtfdHJlbmQgPiAwICYmIGFjcV90cmVuZCA8IDApIHsNCiAgICAgIGNhdCgiRm9yIGJhbmtydXB0Y3k6IE1hY3JvIGZhY3RvcnMgYmVjb21lIE1PUkUgaW1wb3J0YW50IG92ZXIgbG9uZ2VyIGhvcml6b25zXG4iKQ0KICAgICAgY2F0KCJGb3IgYWNxdWlzaXRpb246IE1hY3JvIGZhY3RvcnMgYmVjb21lIExFU1MgaW1wb3J0YW50IG92ZXIgbG9uZ2VyIGhvcml6b25zXG5cbiIpDQogICAgICBjYXQoIlRoaXMgZGl2ZXJnZW50IHBhdHRlcm4gc3VnZ2VzdHMgdGhhdDpcbiIpDQogICAgICBjYXQoIi0gTG9uZy10ZXJtIGNvcnBvcmF0ZSBmYWlsdXJlIGlzIG1vcmUgc3lzdGVtYXRpY2FsbHkgbGlua2VkIHRvIGVjb25vbWljIGN5Y2xlc1xuIikNCiAgICAgIGNhdCgiLSBTaG9ydC10ZXJtIGFjcXVpc2l0aW9uIGFjdGl2aXR5IGlzIG1vcmUgc2Vuc2l0aXZlIHRvIGN1cnJlbnQgZWNvbm9taWMgY29uZGl0aW9uc1xuIikNCiAgICAgIGNhdCgiLSBEaWZmZXJlbnQgbWVjaGFuaXNtcyBtYXkgZHJpdmUgdGhlc2UgdHdvIGNvcnBvcmF0ZSBvdXRjb21lcyBvdmVyIHRpbWVcbiIpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiRm9yIGJhbmtydXB0Y3k6IE1hY3JvIGZhY3RvcnMgYmVjb21lIExFU1MgaW1wb3J0YW50IG92ZXIgbG9uZ2VyIGhvcml6b25zXG4iKQ0KICAgICAgY2F0KCJGb3IgYWNxdWlzaXRpb246IE1hY3JvIGZhY3RvcnMgYmVjb21lIE1PUkUgaW1wb3J0YW50IG92ZXIgbG9uZ2VyIGhvcml6b25zXG5cbiIpDQogICAgICBjYXQoIlRoaXMgZGl2ZXJnZW50IHBhdHRlcm4gc3VnZ2VzdHMgdGhhdDpcbiIpDQogICAgICBjYXQoIi0gU2hvcnQtdGVybSBjb3Jwb3JhdGUgZmFpbHVyZSBpcyBtb3JlIHNlbnNpdGl2ZSB0byBjdXJyZW50IGVjb25vbWljIGNvbmRpdGlvbnNcbiIpDQogICAgICBjYXQoIi0gTG9uZy10ZXJtIGFjcXVpc2l0aW9uIHBhdHRlcm5zIGZvbGxvdyBlY29ub21pYyBjeWNsZXMgbW9yZSBjbG9zZWx5XG4iKQ0KICAgICAgY2F0KCItIERpZmZlcmVudCBtZWNoYW5pc21zIG1heSBkcml2ZSB0aGVzZSB0d28gY29ycG9yYXRlIG91dGNvbWVzIG92ZXIgdGltZVxuIikNCiAgICB9DQogIH0NCn0NCg0KIyBTdW1tYXJpemUgZmluZGluZ3MNCmNhdCgiXG5USU1FLURFUEVOREVOVCBQUkVESUNUSU9OIFNVTU1BUlk6XG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQppZigoYWxsKGlzLm5hKGJhbmtydXB0Y3lfdGRfYXVjJEZ1bGxfQVVDKSkgfHwgbnJvdyhiYW5rcnVwdGN5X3RkX2F1YykgPT0gMCkgJiYgDQogICAoYWxsKGlzLm5hKGFjcXVpc2l0aW9uX3RkX2F1YyRGdWxsX0FVQykpIHx8IG5yb3coYWNxdWlzaXRpb25fdGRfYXVjKSA9PSAwKSkgew0KICBjYXQoIkNvdWxkIG5vdCBjYWxjdWxhdGUgdGltZS1kZXBlbmRlbnQgQVVDIHZhbHVlcyB3aXRoIHRoZSBhdmFpbGFibGUgZGF0YS5cbiIpDQogIGNhdCgiUG9zc2libGUgcmVhc29ucyBpbmNsdWRlOlxuIikNCiAgY2F0KCIxLiBJbnN1ZmZpY2llbnQgZXZlbnRzIGF0IHRoZSBzcGVjaWZpZWQgdGltZSBob3Jpem9uc1xuIikNCiAgY2F0KCIyLiBMYWNrIG9mIHZhcmlhdGlvbiBpbiBwcmVkaWN0b3JzIG9yIG91dGNvbWVzXG4iKQ0KICBjYXQoIjMuIERhdGEgc3RydWN0dXJlIGlzc3VlcyByZWxhdGVkIHRvIHRpbWUtdmFyeWluZyBjb3ZhcmlhdGVzXG5cbiIpDQogIA0KICBjYXQoIkZvciBhIHJvYnVzdCBldmFsdWF0aW9uLCBjb25zaWRlciB1c2luZzpcbiIpDQogIGNhdCgiLSBUaGUgb3ZlcmFsbCBBVUMgdmFsdWVzIGZyb20gc2VjdGlvbiAxMS40XG4iKQ0KICBjYXQoIi0gSW4tc2FtcGxlIHZzLiBvdXQtb2Ytc2FtcGxlIHBlcmZvcm1hbmNlIGNvbXBhcmlzb25zXG4iKQ0KICBjYXQoIi0gQ2FsaWJyYXRpb24gYXNzZXNzbWVudCBvZiBwcmVkaWN0ZWQgdnMuIG9ic2VydmVkIGV2ZW50IHJhdGVzXG4iKQ0KfSBlbHNlIHsNCiAgdmFsaWRfYmsgPC0gIWlzLm5hKGJhbmtydXB0Y3lfdGRfYXVjJEZ1bGxfQVVDKQ0KICB2YWxpZF9hY3EgPC0gIWlzLm5hKGFjcXVpc2l0aW9uX3RkX2F1YyRGdWxsX0FVQykNCiAgDQogIGNhdCgiVGhlIHRpbWUtZGVwZW5kZW50IGV2YWx1YXRpb24gcHJvdmlkZXMgaW5zaWdodHMgaW50byBob3cgcHJlZGljdGlvbiBwZXJmb3JtYW5jZVxuIikNCiAgY2F0KCJhbmQgdGhlIGltcG9ydGFuY2Ugb2YgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgY2hhbmdlIGFjcm9zcyBkaWZmZXJlbnQgdGltZSBob3Jpem9ucy5cblxuIikNCiAgDQogICMgU3VtbWFyaXplIG92ZXJhbGwgZmluZGluZ3MNCiAgaWYoYW55KHZhbGlkX2JrKSkgew0KICAgIGNhdCgiQmFua3J1cHRjeSBtb2RlbDpcbiIpDQogICAgY2F0KCItIFBlcmZvcm1hbmNlIHZhcmllcyBhY3Jvc3MgdGltZSBob3Jpem9ucywgd2l0aCBBVUMgdmFsdWVzIHJhbmdpbmcgZnJvbVxuIikNCiAgICBjYXQoIiAgIiwgbWluKGJhbmtydXB0Y3lfdGRfYXVjJEZ1bGxfQVVDW3ZhbGlkX2JrXSwgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICJ0byIsIG1heChiYW5rcnVwdGN5X3RkX2F1YyRGdWxsX0FVQ1t2YWxpZF9ia10sIG5hLnJtID0gVFJVRSksICJcbiIpDQogICAgDQogICAgaWYoYW55KCFpcy5uYShiYW5rcnVwdGN5X3RkX2F1YyRBVUNfRGlmZmVyZW5jZSkpKSB7DQogICAgICBjYXQoIi0gTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgY29udHJpYnV0ZSBiZXR3ZWVuIiwgDQogICAgICAgICAgbWluKGJhbmtydXB0Y3lfdGRfYXVjJEFVQ19EaWZmZXJlbmNlLCBuYS5ybSA9IFRSVUUpLCAiYW5kIiwNCiAgICAgICAgICBtYXgoYmFua3J1cHRjeV90ZF9hdWMkQVVDX0RpZmZlcmVuY2UsIG5hLnJtID0gVFJVRSksICJ0byBBVUMgdmFsdWVzXG4iKQ0KICAgICAgDQogICAgICAjIElkZW50aWZ5IHRoZSBob3Jpem9uIHdoZXJlIG1hY3JvIHZhcmlhYmxlcyBoYXZlIHRoZSBncmVhdGVzdCBpbXBhY3QNCiAgICAgIG1heF9pbXBhY3RfaWR4IDwtIHdoaWNoLm1heChiYW5rcnVwdGN5X3RkX2F1YyRBVUNfRGlmZmVyZW5jZSkNCiAgICAgIG1heF9pbXBhY3RfaG9yaXpvbiA8LSBiYW5rcnVwdGN5X3RkX2F1YyRIb3Jpem9uW21heF9pbXBhY3RfaWR4XQ0KICAgICAgDQogICAgICBjYXQoIi0gTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgaGF2ZSB0aGUgZ3JlYXRlc3QgaW1wYWN0IGF0IHRoZSIsIA0KICAgICAgICAgIG1heF9pbXBhY3RfaG9yaXpvbiwgInllYXIgaG9yaXpvblxuIikNCiAgICB9DQogICAgY2F0KCJcbiIpDQogIH0NCiAgDQogIGlmKGFueSh2YWxpZF9hY3EpKSB7DQogICAgY2F0KCJBY3F1aXNpdGlvbiBtb2RlbDpcbiIpDQogICAgY2F0KCItIFBlcmZvcm1hbmNlIHZhcmllcyBhY3Jvc3MgdGltZSBob3Jpem9ucywgd2l0aCBBVUMgdmFsdWVzIHJhbmdpbmcgZnJvbVxuIikNCiAgICBjYXQoIiAgIiwgbWluKGFjcXVpc2l0aW9uX3RkX2F1YyRGdWxsX0FVQ1t2YWxpZF9hY3FdLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgInRvIiwgbWF4KGFjcXVpc2l0aW9uX3RkX2F1YyRGdWxsX0FVQ1t2YWxpZF9hY3FdLCBuYS5ybSA9IFRSVUUpLCAiXG4iKQ0KICAgIA0KICAgIGlmKGFueSghaXMubmEoYWNxdWlzaXRpb25fdGRfYXVjJEFVQ19EaWZmZXJlbmNlKSkpIHsNCiAgICAgIGNhdCgiLSBNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBjb250cmlidXRlIGJldHdlZW4iLCANCiAgICAgICAgICBtaW4oYWNxdWlzaXRpb25fdGRfYXVjJEFVQ19EaWZmZXJlbmNlLCBuYS5ybSA9IFRSVUUpLCAiYW5kIiwNCiAgICAgICAgICBtYXgoYWNxdWlzaXRpb25fdGRfYXVjJEFVQ19EaWZmZXJlbmNlLCBuYS5ybSA9IFRSVUUpLCAidG8gQVVDIHZhbHVlc1xuIikNCiAgICAgIA0KICAgICAgIyBJZGVudGlmeSB0aGUgaG9yaXpvbiB3aGVyZSBtYWNybyB2YXJpYWJsZXMgaGF2ZSB0aGUgZ3JlYXRlc3QgaW1wYWN0DQogICAgICBtYXhfaW1wYWN0X2lkeCA8LSB3aGljaC5tYXgoYWNxdWlzaXRpb25fdGRfYXVjJEFVQ19EaWZmZXJlbmNlKQ0KICAgICAgbWF4X2ltcGFjdF9ob3Jpem9uIDwtIGFjcXVpc2l0aW9uX3RkX2F1YyRIb3Jpem9uW21heF9pbXBhY3RfaWR4XQ0KICAgICAgDQogICAgICBjYXQoIi0gTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgaGF2ZSB0aGUgZ3JlYXRlc3QgaW1wYWN0IGF0IHRoZSIsIA0KICAgICAgICAgIG1heF9pbXBhY3RfaG9yaXpvbiwgInllYXIgaG9yaXpvblxuIikNCiAgICB9DQogIH0NCiAgDQogICMgS2V5IGltcGxpY2F0aW9ucw0KICBjYXQoIlxuS2V5IGltcGxpY2F0aW9uczpcbiIpDQogIGNhdCgiMS4gVGhlIHByZWRpY3RpdmUgYWJpbGl0eSBvZiB0aGUgbW9kZWxzIHZhcmllcyBhY3Jvc3MgZGlmZmVyZW50IHRpbWUgaG9yaXpvbnNcbiIpDQogIGNhdCgiMi4gVGhlIGNvbnRyaWJ1dGlvbiBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpcyBub3QgY29uc3RhbnQgb3ZlciB0aW1lXG4iKQ0KICBjYXQoIjMuIFVuZGVyc3RhbmRpbmcgdGhlIHRpbWUtdmFyeWluZyBuYXR1cmUgb2YgZWNvbm9taWMgZWZmZWN0cyBpcyBjcnVjaWFsIGZvclxuIikNCiAgY2F0KCIgICBkZXZlbG9waW5nIHJvYnVzdCByaXNrIG1hbmFnZW1lbnQgYW5kIHN0cmF0ZWdpYyBwbGFubmluZyBmcmFtZXdvcmtzXG4iKQ0KfQ0KYGBgDQoNCg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSBzdGFydCB3aXRoIGEgY29tcHJlaGVuc2l2ZSBvdXQtb2Ytc2FtcGxlIHZhbGlkYXRpb24gb2YgdGhlIG1vZGVscy4gV2Ugc3BsaXQgdGhlIGRhdGFzZXQgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIGJhc2VkIG9uIGNvbXBhbnkgaWRlbnRpZmllcnMsIGVuc3VyaW5nIHRoYXQgdGhlIHNhbWUgY29tcGFuaWVzIGFyZSBub3QgcHJlc2VudCBpbiBib3RoIHNldHMuIFdlIHRoZW4gYW5hbHl6ZSB0aGUgZXZlbnQgZGlzdHJpYnV0aW9uIGluIGJvdGggc2V0cyB0byBlbnN1cmUgdGhhdCB0aGV5IGFyZSBjb21wYXJhYmxlLiBXZSBhbHNvIGNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgYWNyb3NzIHRoZSBzZXRzIHRvIGVuc3VyZSB0aGF0IHRoZXkgYXJlIHNpbWlsYXIuIFRoZSBmaXJzdCBwYXJ0IHVzZXMgcmFuZG9tIHNhbXBsaW5nIHRvIGNyZWF0ZSB0aGUgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cywgd2hpbGUgdGhlIHNlY29uZCBwYXJ0IHVzZXMgYSBtb3JlIHN5c3RlbWF0aWMgYXBwcm9hY2ggYmFzZWQgb24gY29tcGFueSBpZGVudGlmaWVycy4gV2UgYWxzbyBjaGVjayB0aGUgZGlzdHJpYnV0aW9uIG9mIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGFjcm9zcyB0aGUgc2V0cyB0byBlbnN1cmUgdGhhdCB0aGV5IGFyZSBzaW1pbGFyLiBXZSBzdGFydCB3aXRoIGNvbXBhbnktYmFzZWQgZGF0YSBzcGxpdHRpbmcsIHdoaWxlIHRoZSBzZWNvbmQgcGFydCB1c2VzIHRpbWUtYmFzZWQgKGNvbXBhbnkpIGRhdGEgc3BsaXR0aW5nLiBXZSB0aGVuIHJlZml0IHRoZSBtb2RlbHMgb24gdGhlIHRyYWluaW5nIGRhdGEsIGJvdGggd2l0aCBhbmQgd2l0aG91dCBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcywgYW5kIGV2YWx1YXRlIHRoZWlyIHBlcmZvcm1hbmNlIG9uIHRoZSB0ZXN0aW5nIGRhdGEuIEZpbmFsbHksIHdlIGFuYWx5emUgdGhlIGltcGFjdCBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBvbiBwcmVkaWN0aW9uIHBlcmZvcm1hbmNlIGFuZCBwcm92aWRlIGluc2lnaHRzIGludG8gdGhlaXIgaW1wb3J0YW5jZSBmb3IgYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gcHJlZGljdGlvbnMuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMTI6IENvbXByZWhlbnNpdmUgT3V0LW9mLVNhbXBsZSBWYWxpZGF0aW9uDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbj09PSBTVEVQIDEyOiBDT01QUkVIRU5TSVZFIE9VVC1PRi1TQU1QTEUgVkFMSURBVElPTiA9PT1cbiIpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDEyLjE6IENvbXBhbnktQmFzZWQgRGF0YSBTcGxpdHRpbmcNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTIuMTogQ09NUEFOWS1CQVNFRCBEQVRBIFNQTElUVElOR1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQ0Kc2V0LnNlZWQoMTIzKQ0KDQojIEdldCB1bmlxdWUgY29tcGFueSBpZGVudGlmaWVycw0KYWxsX2NvbXBhbmllcyA8LSB1bmlxdWUoZGF0YSRjdXNpcCkNCm5fY29tcGFuaWVzIDwtIGxlbmd0aChhbGxfY29tcGFuaWVzKQ0KY2F0KCJUb3RhbCBudW1iZXIgb2YgY29tcGFuaWVzIGluIGRhdGFzZXQ6Iiwgbl9jb21wYW5pZXMsICJcbiIpDQoNCiMgQ3JlYXRlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgKDcwJSB0cmFpbmluZywgMzAlIHRlc3RpbmcpDQp0cmFpbl9zaXplIDwtIHJvdW5kKDAuNyAqIG5fY29tcGFuaWVzKQ0KdHJhaW5fY29tcGFuaWVzIDwtIHNhbXBsZShhbGxfY29tcGFuaWVzLCBzaXplID0gdHJhaW5fc2l6ZSkNCnRlc3RfY29tcGFuaWVzIDwtIHNldGRpZmYoYWxsX2NvbXBhbmllcywgdHJhaW5fY29tcGFuaWVzKQ0KDQojIFNwbGl0IGRhdGENCnRyYWluX2RhdGEgPC0gZGF0YVtkYXRhJGN1c2lwICVpbiUgdHJhaW5fY29tcGFuaWVzLCBdDQp0ZXN0X2RhdGEgPC0gZGF0YVtkYXRhJGN1c2lwICVpbiUgdGVzdF9jb21wYW5pZXMsIF0NCg0KY2F0KCJUcmFpbmluZyBzZXQ6IiwgbGVuZ3RoKHRyYWluX2NvbXBhbmllcyksICJjb21wYW5pZXMgKCIsIG5yb3codHJhaW5fZGF0YSksICJvYnNlcnZhdGlvbnMpXG4iKQ0KY2F0KCJUZXN0aW5nIHNldDoiLCBsZW5ndGgodGVzdF9jb21wYW5pZXMpLCAiY29tcGFuaWVzICgiLCBucm93KHRlc3RfZGF0YSksICJvYnNlcnZhdGlvbnMpXG4iKQ0KDQojIENoZWNrIGV2ZW50IGRpc3RyaWJ1dGlvbiBpbiB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzDQphbmFseXplX2V2ZW50cyA8LSBmdW5jdGlvbihkYXRhX3N1YnNldCwgbGFiZWwpIHsNCiAgIyBHZXQgZmluYWwgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY29tcGFueQ0KICBmaW5hbF9vYnMgPC0gZGF0YV9zdWJzZXQgJT4lDQogICAgZ3JvdXBfYnkoY3VzaXApICU+JQ0KICAgIHNsaWNlX21heCh0c3RvcCkgJT4lDQogICAgdW5ncm91cCgpDQogIA0KICAjIENvdW50IGV2ZW50IHR5cGVzDQogIG5fYmFua3J1cHRjeSA8LSBzdW0oZmluYWxfb2JzJGJhbmtydXB0Y3kpDQogIG5fYWNxdWlzaXRpb24gPC0gc3VtKGZpbmFsX29icyRhY3F1aXNpdGlvbikNCiAgbl9jZW5zb3JlZCA8LSBzdW0oZmluYWxfb2JzJGJhbmtydXB0Y3kgPT0gMCAmIGZpbmFsX29icyRhY3F1aXNpdGlvbiA9PSAwKQ0KICBuX3RvdGFsIDwtIG5yb3coZmluYWxfb2JzKQ0KICANCiAgY2F0KCJcbkV2ZW50IGRpc3RyaWJ1dGlvbiBpbiIsIGxhYmVsLCAic2V0OlxuIikNCiAgY2F0KCItIEJhbmtydXB0Y2llczoiLCBuX2JhbmtydXB0Y3ksIHNwcmludGYoIiglLjFmJSUpIiwgMTAwKm5fYmFua3J1cHRjeS9uX3RvdGFsKSwgIlxuIikNCiAgY2F0KCItIEFjcXVpc2l0aW9uczoiLCBuX2FjcXVpc2l0aW9uLCBzcHJpbnRmKCIoJS4xZiUlKSIsIDEwMCpuX2FjcXVpc2l0aW9uL25fdG90YWwpLCAiXG4iKQ0KICBjYXQoIi0gQ2Vuc29yZWQ6Iiwgbl9jZW5zb3JlZCwgc3ByaW50ZigiKCUuMWYlJSkiLCAxMDAqbl9jZW5zb3JlZC9uX3RvdGFsKSwgIlxuIikNCiAgDQogIHJldHVybihkYXRhLmZyYW1lKA0KICAgIGJhbmtydXB0Y2llcyA9IG5fYmFua3J1cHRjeSwNCiAgICBhY3F1aXNpdGlvbnMgPSBuX2FjcXVpc2l0aW9uLA0KICAgIGNlbnNvcmVkID0gbl9jZW5zb3JlZCwNCiAgICB0b3RhbCA9IG5fdG90YWwNCiAgKSkNCn0NCg0KdHJhaW5fZXZlbnRzIDwtIGFuYWx5emVfZXZlbnRzKHRyYWluX2RhdGEsICJ0cmFpbmluZyIpDQp0ZXN0X2V2ZW50cyA8LSBhbmFseXplX2V2ZW50cyh0ZXN0X2RhdGEsICJ0ZXN0aW5nIikNCg0KIyBDaGVjayBkaXN0cmlidXRpb24gb2YgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgYWNyb3NzIHNldHMNCmNoZWNrX21hY3JvX2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbih0cmFpbl9kYXRhLCB0ZXN0X2RhdGEpIHsNCiAgbWFjcm9fdmFycyA8LSBjKCJnZHBfZ3Jvd3RoIiwgImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IikNCiAgYXZhaWxhYmxlX21hY3JvIDwtIGludGVyc2VjdChtYWNyb192YXJzLCBjb2xuYW1lcyh0cmFpbl9kYXRhKSkNCiAgDQogIGlmKGxlbmd0aChhdmFpbGFibGVfbWFjcm8pID4gMCkgew0KICAgIGNhdCgiXG5EaXN0cmlidXRpb24gb2YgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgYWNyb3NzIHNldHM6XG4iKQ0KICAgIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogICAgDQogICAgZm9yKHZhciBpbiBhdmFpbGFibGVfbWFjcm8pIHsNCiAgICAgIHRyYWluX21lYW4gPC0gbWVhbih0cmFpbl9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgdHJhaW5fc2QgPC0gc2QodHJhaW5fZGF0YVtbdmFyXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgIHRyYWluX21pbiA8LSBtaW4odHJhaW5fZGF0YVtbdmFyXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgIHRyYWluX21heCA8LSBtYXgodHJhaW5fZGF0YVtbdmFyXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgIA0KICAgICAgdGVzdF9tZWFuIDwtIG1lYW4odGVzdF9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgdGVzdF9zZCA8LSBzZCh0ZXN0X2RhdGFbW3Zhcl1dLCBuYS5ybSA9IFRSVUUpDQogICAgICB0ZXN0X21pbiA8LSBtaW4odGVzdF9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgdGVzdF9tYXggPC0gbWF4KHRlc3RfZGF0YVtbdmFyXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgIA0KICAgICAgY2F0KCJcblZhcmlhYmxlOiIsIHZhciwgIlxuIikNCiAgICAgIGNhdCgiVHJhaW5pbmcgc2V0OiBNZWFuID0iLCByb3VuZCh0cmFpbl9tZWFuLCAyKSwgIlNEID0iLCByb3VuZCh0cmFpbl9zZCwgMiksIA0KICAgICAgICAgICJSYW5nZSA9Iiwgcm91bmQodHJhaW5fbWluLCAyKSwgInRvIiwgcm91bmQodHJhaW5fbWF4LCAyKSwgIlxuIikNCiAgICAgIGNhdCgiVGVzdGluZyBzZXQ6ICBNZWFuID0iLCByb3VuZCh0ZXN0X21lYW4sIDIpLCAiU0QgPSIsIHJvdW5kKHRlc3Rfc2QsIDIpLCANCiAgICAgICAgICAiUmFuZ2UgPSIsIHJvdW5kKHRlc3RfbWluLCAyKSwgInRvIiwgcm91bmQodGVzdF9tYXgsIDIpLCAiXG4iKQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBpbiBtZWFucyBhcyBwZXJjZW50YWdlIG9mIHRyYWluaW5nIFNEDQogICAgICBtZWFuX2RpZmYgPC0gYWJzKHRyYWluX21lYW4gLSB0ZXN0X21lYW4pDQogICAgICBwY3RfZGlmZiA8LSBtZWFuX2RpZmYgLyB0cmFpbl9zZCAqIDEwMA0KICAgICAgDQogICAgICBjYXQoIkRpZmZlcmVuY2U6ICAgIiwgcm91bmQobWVhbl9kaWZmLCAyKSwgIigiLCByb3VuZChwY3RfZGlmZiwgMSksIA0KICAgICAgICAgICIlIG9mIHRyYWluaW5nIFNEKVxuIikNCiAgICAgIA0KICAgICAgaWYocGN0X2RpZmYgPiAyNSkgew0KICAgICAgICBjYXQoIldBUk5JTkc6IFN1YnN0YW50aWFsIGRpZmZlcmVuY2UgaW4gZGlzdHJpYnV0aW9uIG9mIiwgdmFyLCAiYmV0d2VlbiBzZXRzXG4iKQ0KICAgICAgICBjYXQoIiAgICAgICAgIFRoaXMgbWF5IGFmZmVjdCB0aGUgZ2VuZXJhbGl6YWJpbGl0eSBvZiBlY29ub21pYyBlZmZlY3RzXG4iKQ0KICAgICAgfQ0KICAgIH0NCiAgfSBlbHNlIHsNCiAgICBjYXQoIlxuTm8gbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgZm91bmQgaW4gdGhlIGRhdGFzZXQuXG4iKQ0KICB9DQp9DQoNCmNoZWNrX21hY3JvX2Rpc3RyaWJ1dGlvbih0cmFpbl9kYXRhLCB0ZXN0X2RhdGEpDQoNCiMgRW5zdXJlIHRoZSB0aW1lIHZhcmlhYmxlIGV4aXN0cyBpbiBib3RoIHNldHMNCmlmKCEidHQiICVpbiUgY29sbmFtZXModHJhaW5fZGF0YSkpIHRyYWluX2RhdGEkdHQgPC0gdHJhaW5fZGF0YSR0c3RvcA0KaWYoISJ0dCIgJWluJSBjb2xuYW1lcyh0ZXN0X2RhdGEpKSB0ZXN0X2RhdGEkdHQgPC0gdGVzdF9kYXRhJHRzdG9wDQpgYGANCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxMi4yOiBSZWZpdCBNb2RlbHMgb24gVHJhaW5pbmcgRGF0YSAoV2l0aCBhbmQgV2l0aG91dCBNYWNybykNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTIuMjogUkVGSVRUSU5HIE1PREVMUyBPTiBUUkFJTklORyBEQVRBXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBGdW5jdGlvbiB0byBjcmVhdGUgZm9ybXVsYSB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcw0KY3JlYXRlX25vX21hY3JvX2Zvcm11bGEgPC0gZnVuY3Rpb24ob3JpZ2luYWxfZm9ybXVsYSkgew0KICAjIEV4dHJhY3QgdGVybXMgZnJvbSB0aGUgZm9ybXVsYQ0KICBhbGxfdGVybXMgPC0gYXR0cih0ZXJtcyhhcy5mb3JtdWxhKG9yaWdpbmFsX2Zvcm11bGEpKSwgInRlcm0ubGFiZWxzIikNCiAgDQogICMgSWRlbnRpZnkgbWFjcm8gdmFyaWFibGVzIChpbmNsdWRpbmcgdGhlaXIgdGltZSBpbnRlcmFjdGlvbnMpDQogIG1hY3JvX3ZhcnMgPC0gYygiZ2RwX2dyb3d0aCIsICJnZHBfZGVmbGF0b3IiLCAidW5lbXBsb3llbWVudCIpDQogIG1hY3JvX3Rlcm1zIDwtIGMoKQ0KICANCiAgZm9yKHRlcm0gaW4gYWxsX3Rlcm1zKSB7DQogICAgIyBDaGVjayBpZiB0ZXJtIGNvbnRhaW5zIGFueSBtYWNybyB2YXJpYWJsZQ0KICAgIGlzX21hY3JvIDwtIEZBTFNFDQogICAgZm9yKG1hY3JvIGluIG1hY3JvX3ZhcnMpIHsNCiAgICAgIGlmKGdyZXBsKG1hY3JvLCB0ZXJtKSkgew0KICAgICAgICBpc19tYWNybyA8LSBUUlVFDQogICAgICAgIGJyZWFrDQogICAgICB9DQogICAgfQ0KICAgIA0KICAgIGlmKGlzX21hY3JvKSB7DQogICAgICBtYWNyb190ZXJtcyA8LSBjKG1hY3JvX3Rlcm1zLCB0ZXJtKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBJZiBubyBtYWNybyB0ZXJtcywgcmV0dXJuIG9yaWdpbmFsIGZvcm11bGENCiAgaWYobGVuZ3RoKG1hY3JvX3Rlcm1zKSA9PSAwKSB7DQogICAgcmV0dXJuKG9yaWdpbmFsX2Zvcm11bGEpDQogIH0NCiAgDQogICMgQ3JlYXRlIGZvcm11bGEgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCiAgbm9fbWFjcm9fZm9ybXVsYSA8LSBvcmlnaW5hbF9mb3JtdWxhDQogIGZvcih0ZXJtIGluIG1hY3JvX3Rlcm1zKSB7DQogICAgbm9fbWFjcm9fZm9ybXVsYSA8LSB1cGRhdGUoYXMuZm9ybXVsYShub19tYWNyb19mb3JtdWxhKSwgcGFzdGUwKCIuIH4gLiAtICIsIHRlcm0pKQ0KICB9DQogIA0KICByZXR1cm4oZm9ybXVsYShub19tYWNyb19mb3JtdWxhKSkNCn0NCg0KIyBGb3IgYmFua3J1cHRjeSBtb2RlbA0KY2F0KCJSZWZpdHRpbmcgYmFua3J1cHRjeSBtb2RlbCBvbiB0cmFpbmluZyBkYXRhLi4uXG4iKQ0KDQojIEdldCBmb3JtdWxhIGZyb20gdGhlIGZpbmFsIG1vZGVsDQpiYW5rX2Zvcm11bGEgPC0gZm9ybXVsYShmaW5hbF9iYW5rcnVwdGN5X21vZGVsKQ0KDQojIENyZWF0ZSB2ZXJzaW9uIHdpdGhvdXQgbWFjcm8gdmFyaWFibGVzDQpub19tYWNyb19iYW5rX2Zvcm11bGEgPC0gY3JlYXRlX25vX21hY3JvX2Zvcm11bGEoYmFua19mb3JtdWxhKQ0KDQojIENoZWNrIGlmIGZvcm11bGFzIGFyZSBkaWZmZXJlbnQNCmhhc19tYWNyb19iYW5rIDwtICFpZGVudGljYWwoYmFua19mb3JtdWxhLCBub19tYWNyb19iYW5rX2Zvcm11bGEpDQoNCmlmKGhhc19tYWNyb19iYW5rKSB7DQogIGNhdCgiQmFua3J1cHRjeSBtb2RlbCBjb250YWlucyBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcy5cbiIpDQogIGNhdCgiRml0dGluZyB0d28gdmVyc2lvbnMgZm9yIGNvbXBhcmlzb246XG4iKQ0KICANCiAgIyBGaXQgZnVsbCBtb2RlbCB3aXRoIG1hY3JvIHZhcmlhYmxlcw0KICB0cmFpbl9iYW5rX21vZGVsIDwtIGNveHBoKGJhbmtfZm9ybXVsYSwgZGF0YSA9IHRyYWluX2RhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICBjYXQoIi0gRnVsbCBtb2RlbCB3aXRoIG1hY3JvIHZhcmlhYmxlczogZml0dGVkIHN1Y2Nlc3NmdWxseVxuIikNCiAgY2F0KCIgIE51bWJlciBvZiBjb2VmZmljaWVudHM6IiwgbGVuZ3RoKGNvZWYodHJhaW5fYmFua19tb2RlbCkpLCAiXG4iKQ0KICANCiAgIyBGaXQgdmVyc2lvbiB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcw0KICB0cmFpbl9iYW5rX25vX21hY3JvX21vZGVsIDwtIGNveHBoKG5vX21hY3JvX2JhbmtfZm9ybXVsYSwgZGF0YSA9IHRyYWluX2RhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICBjYXQoIi0gTW9kZWwgd2l0aG91dCBtYWNybyB2YXJpYWJsZXM6IGZpdHRlZCBzdWNjZXNzZnVsbHlcbiIpDQogIGNhdCgiICBOdW1iZXIgb2YgY29lZmZpY2llbnRzOiIsIGxlbmd0aChjb2VmKHRyYWluX2Jhbmtfbm9fbWFjcm9fbW9kZWwpKSwgIlxuIikNCn0gZWxzZSB7DQogIGNhdCgiQmFua3J1cHRjeSBtb2RlbCBkb2VzIG5vdCBjb250YWluIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzLlxuIikNCiAgdHJhaW5fYmFua19tb2RlbCA8LSBjb3hwaChiYW5rX2Zvcm11bGEsIGRhdGEgPSB0cmFpbl9kYXRhLCB0aWVzID0gImVmcm9uIikNCiAgY2F0KCJNb2RlbCBmaXR0ZWQgc3VjY2Vzc2Z1bGx5LlxuIikNCiAgY2F0KCJOdW1iZXIgb2YgY29lZmZpY2llbnRzOiIsIGxlbmd0aChjb2VmKHRyYWluX2JhbmtfbW9kZWwpKSwgIlxuIikNCiAgDQogICMgU2V0IG5vLW1hY3JvIG1vZGVsIHRvIGJlIHRoZSBzYW1lIGFzIGZ1bGwgbW9kZWwNCiAgdHJhaW5fYmFua19ub19tYWNyb19tb2RlbCA8LSB0cmFpbl9iYW5rX21vZGVsDQp9DQoNCiMgRm9yIGFjcXVpc2l0aW9uIG1vZGVsDQpjYXQoIlxuUmVmaXR0aW5nIGFjcXVpc2l0aW9uIG1vZGVsIG9uIHRyYWluaW5nIGRhdGEuLi5cbiIpDQoNCiMgR2V0IGZvcm11bGEgZnJvbSB0aGUgZmluYWwgbW9kZWwNCmFjcV9mb3JtdWxhIDwtIGZvcm11bGEoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpDQoNCiMgQ3JlYXRlIHZlcnNpb24gd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCm5vX21hY3JvX2FjcV9mb3JtdWxhIDwtIGNyZWF0ZV9ub19tYWNyb19mb3JtdWxhKGFjcV9mb3JtdWxhKQ0KDQojIENoZWNrIGlmIGZvcm11bGFzIGFyZSBkaWZmZXJlbnQNCmhhc19tYWNyb19hY3EgPC0gIWlkZW50aWNhbChhY3FfZm9ybXVsYSwgbm9fbWFjcm9fYWNxX2Zvcm11bGEpDQoNCmlmKGhhc19tYWNyb19hY3EpIHsNCiAgY2F0KCJBY3F1aXNpdGlvbiBtb2RlbCBjb250YWlucyBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcy5cbiIpDQogIGNhdCgiRml0dGluZyB0d28gdmVyc2lvbnMgZm9yIGNvbXBhcmlzb246XG4iKQ0KICANCiAgIyBGaXQgZnVsbCBtb2RlbCB3aXRoIG1hY3JvIHZhcmlhYmxlcw0KICB0cmFpbl9hY3FfbW9kZWwgPC0gY294cGgoYWNxX2Zvcm11bGEsIGRhdGEgPSB0cmFpbl9kYXRhLCB0aWVzID0gImVmcm9uIikNCiAgY2F0KCItIEZ1bGwgbW9kZWwgd2l0aCBtYWNybyB2YXJpYWJsZXM6IGZpdHRlZCBzdWNjZXNzZnVsbHlcbiIpDQogIGNhdCgiICBOdW1iZXIgb2YgY29lZmZpY2llbnRzOiIsIGxlbmd0aChjb2VmKHRyYWluX2FjcV9tb2RlbCkpLCAiXG4iKQ0KICANCiAgIyBGaXQgdmVyc2lvbiB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcw0KICB0cmFpbl9hY3Ffbm9fbWFjcm9fbW9kZWwgPC0gY294cGgobm9fbWFjcm9fYWNxX2Zvcm11bGEsIGRhdGEgPSB0cmFpbl9kYXRhLCB0aWVzID0gImVmcm9uIikNCiAgY2F0KCItIE1vZGVsIHdpdGhvdXQgbWFjcm8gdmFyaWFibGVzOiBmaXR0ZWQgc3VjY2Vzc2Z1bGx5XG4iKQ0KICBjYXQoIiAgTnVtYmVyIG9mIGNvZWZmaWNpZW50czoiLCBsZW5ndGgoY29lZih0cmFpbl9hY3Ffbm9fbWFjcm9fbW9kZWwpKSwgIlxuIikNCn0gZWxzZSB7DQogIGNhdCgiQWNxdWlzaXRpb24gbW9kZWwgZG9lcyBub3QgY29udGFpbiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcy5cbiIpDQogIHRyYWluX2FjcV9tb2RlbCA8LSBjb3hwaChhY3FfZm9ybXVsYSwgZGF0YSA9IHRyYWluX2RhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICBjYXQoIk1vZGVsIGZpdHRlZCBzdWNjZXNzZnVsbHkuXG4iKQ0KICBjYXQoIk51bWJlciBvZiBjb2VmZmljaWVudHM6IiwgbGVuZ3RoKGNvZWYodHJhaW5fYWNxX21vZGVsKSksICJcbiIpDQogIA0KICAjIFNldCBuby1tYWNybyBtb2RlbCB0byBiZSB0aGUgc2FtZSBhcyBmdWxsIG1vZGVsDQogIHRyYWluX2FjcV9ub19tYWNyb19tb2RlbCA8LSB0cmFpbl9hY3FfbW9kZWwNCn0NCmBgYA0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDEyLjM6IEluLVNhbXBsZSBQZXJmb3JtYW5jZSBFdmFsdWF0aW9uDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjEyLjM6IElOLVNBTVBMRSBQRVJGT1JNQU5DRSBFVkFMVUFUSU9OXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBGdW5jdGlvbiB0byBldmFsdWF0ZSBpbi1zYW1wbGUgcGVyZm9ybWFuY2UNCmV2YWx1YXRlX2luX3NhbXBsZSA8LSBmdW5jdGlvbihtb2RlbCwgZXZlbnRfdHlwZSwgZGF0YV9zdWJzZXQsIG1vZGVsX25hbWUpIHsNCiAgY2F0KCJFdmFsdWF0aW5nIGluLXNhbXBsZSBwZXJmb3JtYW5jZSBmb3IiLCBtb2RlbF9uYW1lLCAibW9kZWwuLi5cbiIpDQogIA0KICAjIEdldCBsYXN0IG9ic2VydmF0aW9uIGZvciBlYWNoIGNvbXBhbnkNCiAgZmluYWxfb2JzIDwtIGRhdGFfc3Vic2V0ICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICANCiAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMNCiAgcmlza19zY29yZXMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IGZpbmFsX29icywgdHlwZSA9ICJyaXNrIikNCiAgDQogICMgRXZhbHVhdGUgd2l0aCBST0MvQVVDDQogIGlmKHJlcXVpcmVOYW1lc3BhY2UoInBST0MiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgICBsaWJyYXJ5KHBST0MpDQogICAgDQogICAgdHJ5Q2F0Y2goew0KICAgICAgcm9jX29iaiA8LSByb2MoZmluYWxfb2JzW1tldmVudF90eXBlXV0sIHJpc2tfc2NvcmVzLCBxdWlldCA9IFRSVUUpDQogICAgICBhdWNfdmFsIDwtIGFzLm51bWVyaWMoYXVjKHJvY19vYmopKQ0KICAgICAgDQogICAgICBjYXQoIkluLXNhbXBsZSBBVUM6Iiwgcm91bmQoYXVjX3ZhbCwgNCksICJcbiIpDQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIG9wdGltYWwgdGhyZXNob2xkDQogICAgICBjb29yZHMgPC0gY29vcmRzKHJvY19vYmosICJiZXN0IiwgcmV0ID0gYygidGhyZXNob2xkIiwgInNlbnNpdGl2aXR5IiwgInNwZWNpZmljaXR5IikpDQogICAgICB0aHJlc2hvbGQgPC0gYXMubnVtZXJpYyhjb29yZHNbInRocmVzaG9sZCJdKQ0KICAgICAgc2Vuc2l0aXZpdHkgPC0gYXMubnVtZXJpYyhjb29yZHNbInNlbnNpdGl2aXR5Il0pDQogICAgICBzcGVjaWZpY2l0eSA8LSBhcy5udW1lcmljKGNvb3Jkc1sic3BlY2lmaWNpdHkiXSkNCiAgICAgIA0KICAgICAgY2F0KCJPcHRpbWFsIHRocmVzaG9sZDoiLCByb3VuZCh0aHJlc2hvbGQsIDQpLCAiXG4iKQ0KICAgICAgY2F0KCJTZW5zaXRpdml0eSBhdCBvcHRpbWFsIHRocmVzaG9sZDoiLCByb3VuZChzZW5zaXRpdml0eSwgNCksICJcbiIpDQogICAgICBjYXQoIlNwZWNpZmljaXR5IGF0IG9wdGltYWwgdGhyZXNob2xkOiIsIHJvdW5kKHNwZWNpZmljaXR5LCA0KSwgIlxuIikNCiAgICAgIA0KICAgICAgIyBBZGRpdGlvbmFsIG1ldHJpY3MgYXQgb3B0aW1hbCB0aHJlc2hvbGQNCiAgICAgIHByZWRpY3Rpb25zIDwtIGlmZWxzZShyaXNrX3Njb3JlcyA+PSB0aHJlc2hvbGQsIDEsIDApDQogICAgICB0cnVlX3ZhbHVlcyA8LSBmaW5hbF9vYnNbW2V2ZW50X3R5cGVdXQ0KICAgICAgDQogICAgICBUUCA8LSBzdW0ocHJlZGljdGlvbnMgPT0gMSAmIHRydWVfdmFsdWVzID09IDEpDQogICAgICBUTiA8LSBzdW0ocHJlZGljdGlvbnMgPT0gMCAmIHRydWVfdmFsdWVzID09IDApDQogICAgICBGUCA8LSBzdW0ocHJlZGljdGlvbnMgPT0gMSAmIHRydWVfdmFsdWVzID09IDApDQogICAgICBGTiA8LSBzdW0ocHJlZGljdGlvbnMgPT0gMCAmIHRydWVfdmFsdWVzID09IDEpDQogICAgICANCiAgICAgIGFjY3VyYWN5IDwtIChUUCArIFROKSAvIChUUCArIFROICsgRlAgKyBGTikNCiAgICAgIHByZWNpc2lvbiA8LSBUUCAvIChUUCArIEZQKQ0KICAgICAgcmVjYWxsIDwtIHNlbnNpdGl2aXR5ICAjIFNhbWUgYXMgc2Vuc2l0aXZpdHkNCiAgICAgIGYxX3Njb3JlIDwtIDIgKiBwcmVjaXNpb24gKiByZWNhbGwgLyAocHJlY2lzaW9uICsgcmVjYWxsKQ0KICAgICAgDQogICAgICBjYXQoIkFjY3VyYWN5OiIsIHJvdW5kKGFjY3VyYWN5LCA0KSwgIlxuIikNCiAgICAgIGNhdCgiUHJlY2lzaW9uOiIsIHJvdW5kKHByZWNpc2lvbiwgNCksICJcbiIpDQogICAgICBjYXQoIkYxIFNjb3JlOiIsIHJvdW5kKGYxX3Njb3JlLCA0KSwgIlxuIikNCiAgICAgIA0KICAgICAgIyBSZXR1cm4gcGVyZm9ybWFuY2UgbWV0cmljcw0KICAgICAgcmV0dXJuKGxpc3QoDQogICAgICAgIGF1YyA9IGF1Y192YWwsDQogICAgICAgIHRocmVzaG9sZCA9IHRocmVzaG9sZCwNCiAgICAgICAgc2Vuc2l0aXZpdHkgPSBzZW5zaXRpdml0eSwNCiAgICAgICAgc3BlY2lmaWNpdHkgPSBzcGVjaWZpY2l0eSwNCiAgICAgICAgYWNjdXJhY3kgPSBhY2N1cmFjeSwNCiAgICAgICAgcHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICAgICAgICByZWNhbGwgPSByZWNhbGwsDQogICAgICAgIGYxX3Njb3JlID0gZjFfc2NvcmUNCiAgICAgICkpDQogICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgICBjYXQoIkVycm9yIGNhbGN1bGF0aW5nIGluLXNhbXBsZSBwZXJmb3JtYW5jZToiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9KQ0KICB9IGVsc2Ugew0KICAgIGNhdCgicFJPQyBwYWNrYWdlIG5vdCBhdmFpbGFibGUgZm9yIFJPQyBhbmFseXNpcy5cbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0NCn0NCg0KIyBFdmFsdWF0ZSBpbi1zYW1wbGUgcGVyZm9ybWFuY2UgZm9yIGZ1bGwgbW9kZWxzDQpiYW5rX2luX3NhbXBsZSA8LSBldmFsdWF0ZV9pbl9zYW1wbGUodHJhaW5fYmFua19tb2RlbCwgImJhbmtydXB0Y3kiLCB0cmFpbl9kYXRhLCAiYmFua3J1cHRjeSIpDQphY3FfaW5fc2FtcGxlIDwtIGV2YWx1YXRlX2luX3NhbXBsZSh0cmFpbl9hY3FfbW9kZWwsICJhY3F1aXNpdGlvbiIsIHRyYWluX2RhdGEsICJhY3F1aXNpdGlvbiIpDQoNCiMgRXZhbHVhdGUgaW4tc2FtcGxlIHBlcmZvcm1hbmNlIGZvciBuby1tYWNybyBtb2RlbHMgKGlmIGRpZmZlcmVudCkNCmlmKGhhc19tYWNyb19iYW5rKSB7DQogIGJhbmtfaW5fc2FtcGxlX25vX21hY3JvIDwtIGV2YWx1YXRlX2luX3NhbXBsZSh0cmFpbl9iYW5rX25vX21hY3JvX21vZGVsLCAiYmFua3J1cHRjeSIsIHRyYWluX2RhdGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJiYW5rcnVwdGN5IChubyBtYWNybykiKQ0KfQ0KDQppZihoYXNfbWFjcm9fYWNxKSB7DQogIGFjcV9pbl9zYW1wbGVfbm9fbWFjcm8gPC0gZXZhbHVhdGVfaW5fc2FtcGxlKHRyYWluX2FjcV9ub19tYWNyb19tb2RlbCwgImFjcXVpc2l0aW9uIiwgdHJhaW5fZGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYWNxdWlzaXRpb24gKG5vIG1hY3JvKSIpDQp9DQpgYGANCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDEyLjQ6IE91dC1vZi1TYW1wbGUgUGVyZm9ybWFuY2UgRXZhbHVhdGlvbiAmIE1hY3JvIEltcGFjdA0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMi40OiBPVVQtT0YtU0FNUExFIFBFUkZPUk1BTkNFICYgTUFDUk8gSU1QQUNUIEVWQUxVQVRJT05cbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBGdW5jdGlvbiB0byBldmFsdWF0ZSBvdXQtb2Ytc2FtcGxlIHBlcmZvcm1hbmNlDQpldmFsdWF0ZV9vb3NfcGVyZm9ybWFuY2UgPC0gZnVuY3Rpb24obW9kZWwsIG5vX21hY3JvX21vZGVsLCB0ZXN0X2RhdGEsIGV2ZW50X3R5cGUsIG1vZGVsX25hbWUpIHsNCiAgY2F0KCJcbkV2YWx1YXRpbmcgb3V0LW9mLXNhbXBsZSBwZXJmb3JtYW5jZSBmb3IiLCBtb2RlbF9uYW1lLCAibW9kZWwuLi5cbiIpDQogIA0KICAjIEdldCBsYXN0IG9ic2VydmF0aW9uIGZvciBlYWNoIGNvbXBhbnkgaW4gdGVzdCBzZXQNCiAgdGVzdF9sYXN0X29icyA8LSB0ZXN0X2RhdGEgJT4lDQogICAgZ3JvdXBfYnkoY3VzaXApICU+JQ0KICAgIHNsaWNlX21heCh0c3RvcCkgJT4lDQogICAgdW5ncm91cCgpDQogIA0KICAjIENhbGN1bGF0ZSByaXNrIHNjb3JlcyBmcm9tIGZ1bGwgbW9kZWwNCiAgZnVsbF9yaXNrX3Njb3JlcyA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gdGVzdF9sYXN0X29icywgdHlwZSA9ICJyaXNrIikNCiAgDQogICMgQ2FsY3VsYXRlIHJpc2sgc2NvcmVzIGZyb20gbW9kZWwgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMNCiAgbm9fbWFjcm9fcmlza19zY29yZXMgPC0gcHJlZGljdChub19tYWNyb19tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfbGFzdF9vYnMsIHR5cGUgPSAicmlzayIpDQogIA0KICAjIENhbGN1bGF0ZSBBVUMgdXNpbmcgcFJPQyBwYWNrYWdlDQogIGlmKHJlcXVpcmVOYW1lc3BhY2UoInBST0MiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgICBsaWJyYXJ5KHBST0MpDQogICAgDQogICAgIyBDcmVhdGUgUk9DIG9iamVjdHMNCiAgICBmdWxsX3JvYyA8LSByb2ModGVzdF9sYXN0X29ic1tbZXZlbnRfdHlwZV1dLCBmdWxsX3Jpc2tfc2NvcmVzLCBxdWlldCA9IFRSVUUpDQogICAgbm9fbWFjcm9fcm9jIDwtIHJvYyh0ZXN0X2xhc3Rfb2JzW1tldmVudF90eXBlXV0sIG5vX21hY3JvX3Jpc2tfc2NvcmVzLCBxdWlldCA9IFRSVUUpDQogICAgDQogICAgIyBDYWxjdWxhdGUgQVVDcw0KICAgIGZ1bGxfYXVjIDwtIGF1YyhmdWxsX3JvYykNCiAgICBub19tYWNyb19hdWMgPC0gYXVjKG5vX21hY3JvX3JvYykNCiAgICANCiAgICAjIENhbGN1bGF0ZSBjb25maWRlbmNlIGludGVydmFscw0KICAgIGZ1bGxfY2kgPC0gY2koZnVsbF9yb2MpDQogICAgbm9fbWFjcm9fY2kgPC0gY2kobm9fbWFjcm9fcm9jKQ0KICAgIA0KICAgICMgUHJpbnQgcmVzdWx0cw0KICAgIGNhdCgiT3V0LW9mLXNhbXBsZSBBVUMgd2l0aCBhbGwgdmFyaWFibGVzOiIsIHJvdW5kKGZ1bGxfYXVjLCA0KSwgIlxuIikNCiAgICBjYXQoIjk1JSBDb25maWRlbmNlIEludGVydmFsOiIsIHBhc3RlKHJvdW5kKGZ1bGxfY2lbMToyXSwgNCksIGNvbGxhcHNlPSIgLSAiKSwgIlxuIikNCiAgICANCiAgICBpZighaWRlbnRpY2FsKG1vZGVsLCBub19tYWNyb19tb2RlbCkpIHsNCiAgICAgIGNhdCgiT3V0LW9mLXNhbXBsZSBBVUMgd2l0aG91dCBtYWNybyB2YXJpYWJsZXM6Iiwgcm91bmQobm9fbWFjcm9fYXVjLCA0KSwgIlxuIikNCiAgICAgIGNhdCgiOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWw6IiwgcGFzdGUocm91bmQobm9fbWFjcm9fY2lbMToyXSwgNCksIGNvbGxhcHNlPSIgLSAiKSwgIlxuIikNCiAgICAgIA0KICAgICAgIyBDYWxjdWxhdGUgaW1wcm92ZW1lbnQgZnJvbSBtYWNybyB2YXJpYWJsZXMNCiAgICAgIGF1Y19kaWZmIDwtIGZ1bGxfYXVjIC0gbm9fbWFjcm9fYXVjDQogICAgICBwY3RfaW1wcm92ZW1lbnQgPC0gYXVjX2RpZmYgLyBub19tYWNyb19hdWMgKiAxMDANCiAgICAgIA0KICAgICAgY2F0KCJBVUMgaW1wcm92ZW1lbnQgZnJvbSBtYWNybyB2YXJpYWJsZXM6Iiwgcm91bmQoYXVjX2RpZmYsIDQpLCANCiAgICAgICAgICAiKCIsIHJvdW5kKHBjdF9pbXByb3ZlbWVudCwgMSksICIlKVxuIikNCiAgICAgIA0KICAgICAgIyBUZXN0IGlmIGltcHJvdmVtZW50IGlzIHNpZ25pZmljYW50DQogICAgICByb2NfdGVzdCA8LSByb2MudGVzdChmdWxsX3JvYywgbm9fbWFjcm9fcm9jLCBtZXRob2QgPSAiZGVsb25nIikNCiAgICAgIGNhdCgiU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlOiBwID0iLCBmb3JtYXQucHZhbChyb2NfdGVzdCRwLnZhbHVlLCBkaWdpdHMgPSAzKSwgIlxuIikNCiAgICAgIA0KICAgICAgaWYocm9jX3Rlc3QkcC52YWx1ZSA8IDAuMDUpIHsNCiAgICAgICAgY2F0KCJUaGUgaW1wcm92ZW1lbnQgZnJvbSBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpcyBTVEFUSVNUSUNBTExZIFNJR05JRklDQU5UIChwIDwgMC4wNSlcbiIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIlRoZSBpbXByb3ZlbWVudCBmcm9tIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGlzIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IChwID49IDAuMDUpXG4iKQ0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICAjIENhbGN1bGF0ZSBvcHRpbWFsIHRocmVzaG9sZHMgYW5kIGNsYXNzaWZpY2F0aW9uIG1ldHJpY3MNCiAgICBmdWxsX2Nvb3JkcyA8LSBjb29yZHMoZnVsbF9yb2MsICJiZXN0IiwgcmV0ID0gYygidGhyZXNob2xkIiwgInNlbnNpdGl2aXR5IiwgInNwZWNpZmljaXR5IikpDQogICAgb3B0aW1hbF90aHJlc2hvbGQgPC0gYXMubnVtZXJpYyhmdWxsX2Nvb3Jkc1sidGhyZXNob2xkIl0pDQogICAgb3B0aW1hbF9zZW5zaXRpdml0eSA8LSBhcy5udW1lcmljKGZ1bGxfY29vcmRzWyJzZW5zaXRpdml0eSJdKQ0KICAgIG9wdGltYWxfc3BlY2lmaWNpdHkgPC0gYXMubnVtZXJpYyhmdWxsX2Nvb3Jkc1sic3BlY2lmaWNpdHkiXSkNCiAgICANCiAgICAjIE1ha2UgcHJlZGljdGlvbnMgYXQgb3B0aW1hbCB0aHJlc2hvbGQNCiAgICBwcmVkaWN0aW9ucyA8LSBpZmVsc2UoZnVsbF9yaXNrX3Njb3JlcyA+PSBvcHRpbWFsX3RocmVzaG9sZCwgMSwgMCkNCiAgICANCiAgICAjIENyZWF0ZSBjb25mdXNpb24gbWF0cml4DQogICAgVFAgPC0gc3VtKHByZWRpY3Rpb25zID09IDEgJiB0ZXN0X2xhc3Rfb2JzW1tldmVudF90eXBlXV0gPT0gMSkNCiAgICBUTiA8LSBzdW0ocHJlZGljdGlvbnMgPT0gMCAmIHRlc3RfbGFzdF9vYnNbW2V2ZW50X3R5cGVdXSA9PSAwKQ0KICAgIEZQIDwtIHN1bShwcmVkaWN0aW9ucyA9PSAxICYgdGVzdF9sYXN0X29ic1tbZXZlbnRfdHlwZV1dID09IDApDQogICAgRk4gPC0gc3VtKHByZWRpY3Rpb25zID09IDAgJiB0ZXN0X2xhc3Rfb2JzW1tldmVudF90eXBlXV0gPT0gMSkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBtZXRyaWNzDQogICAgYWNjdXJhY3kgPC0gKFRQICsgVE4pIC8gKFRQICsgVE4gKyBGUCArIEZOKQ0KICAgIHByZWNpc2lvbiA8LSBpZihUUCArIEZQID4gMCkgVFAgLyAoVFAgKyBGUCkgZWxzZSAwDQogICAgcmVjYWxsIDwtIGlmKFRQICsgRk4gPiAwKSBUUCAvIChUUCArIEZOKSBlbHNlIDANCiAgICBmMV9zY29yZSA8LSBpZihwcmVjaXNpb24gKyByZWNhbGwgPiAwKSAyICogcHJlY2lzaW9uICogcmVjYWxsIC8gKHByZWNpc2lvbiArIHJlY2FsbCkgZWxzZSAwDQogICAgDQogICAgY2F0KCJcbk91dC1vZi1zYW1wbGUgY2xhc3NpZmljYXRpb24gbWV0cmljcyBhdCBvcHRpbWFsIHRocmVzaG9sZDpcbiIpDQogICAgY2F0KCJBY2N1cmFjeToiLCByb3VuZChhY2N1cmFjeSwgNCksICJcbiIpDQogICAgY2F0KCJQcmVjaXNpb246Iiwgcm91bmQocHJlY2lzaW9uLCA0KSwgIlxuIikNCiAgICBjYXQoIlJlY2FsbDoiLCByb3VuZChyZWNhbGwsIDQpLCAiXG4iKQ0KICAgIGNhdCgiRjEgU2NvcmU6Iiwgcm91bmQoZjFfc2NvcmUsIDQpLCAiXG4iKQ0KICAgIA0KICAgICMgSWYgd2UgaGF2ZSBuby1tYWNybyBtb2RlbCwgY29tcGFyZSBtZXRyaWNzDQogICAgaWYoIWlkZW50aWNhbChtb2RlbCwgbm9fbWFjcm9fbW9kZWwpKSB7DQogICAgICAjIEdldCBvcHRpbWFsIHRocmVzaG9sZCBmb3Igbm8tbWFjcm8gbW9kZWwNCiAgICAgIG5vX21hY3JvX2Nvb3JkcyA8LSBjb29yZHMobm9fbWFjcm9fcm9jLCAiYmVzdCIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQ0KICAgICAgbm9fbWFjcm9fdGhyZXNob2xkIDwtIGFzLm51bWVyaWMobm9fbWFjcm9fY29vcmRzWyJ0aHJlc2hvbGQiXSkNCiAgICAgIA0KICAgICAgIyBNYWtlIHByZWRpY3Rpb25zIGF0IG9wdGltYWwgdGhyZXNob2xkDQogICAgICBub19tYWNyb19wcmVkaWN0aW9ucyA8LSBpZmVsc2Uobm9fbWFjcm9fcmlza19zY29yZXMgPj0gbm9fbWFjcm9fdGhyZXNob2xkLCAxLCAwKQ0KICAgICAgDQogICAgICAjIENyZWF0ZSBjb25mdXNpb24gbWF0cml4DQogICAgICBub19tYWNyb19UUCA8LSBzdW0obm9fbWFjcm9fcHJlZGljdGlvbnMgPT0gMSAmIHRlc3RfbGFzdF9vYnNbW2V2ZW50X3R5cGVdXSA9PSAxKQ0KICAgICAgbm9fbWFjcm9fVE4gPC0gc3VtKG5vX21hY3JvX3ByZWRpY3Rpb25zID09IDAgJiB0ZXN0X2xhc3Rfb2JzW1tldmVudF90eXBlXV0gPT0gMCkNCiAgICAgIG5vX21hY3JvX0ZQIDwtIHN1bShub19tYWNyb19wcmVkaWN0aW9ucyA9PSAxICYgdGVzdF9sYXN0X29ic1tbZXZlbnRfdHlwZV1dID09IDApDQogICAgICBub19tYWNyb19GTiA8LSBzdW0obm9fbWFjcm9fcHJlZGljdGlvbnMgPT0gMCAmIHRlc3RfbGFzdF9vYnNbW2V2ZW50X3R5cGVdXSA9PSAxKQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBtZXRyaWNzDQogICAgICBub19tYWNyb19hY2N1cmFjeSA8LSAobm9fbWFjcm9fVFAgKyBub19tYWNyb19UTikgLyAobm9fbWFjcm9fVFAgKyBub19tYWNyb19UTiArIG5vX21hY3JvX0ZQICsgbm9fbWFjcm9fRk4pDQogICAgICBub19tYWNyb19wcmVjaXNpb24gPC0gaWYobm9fbWFjcm9fVFAgKyBub19tYWNyb19GUCA+IDApIG5vX21hY3JvX1RQIC8gKG5vX21hY3JvX1RQICsgbm9fbWFjcm9fRlApIGVsc2UgMA0KICAgICAgbm9fbWFjcm9fcmVjYWxsIDwtIGlmKG5vX21hY3JvX1RQICsgbm9fbWFjcm9fRk4gPiAwKSBub19tYWNyb19UUCAvIChub19tYWNyb19UUCArIG5vX21hY3JvX0ZOKSBlbHNlIDANCiAgICAgIG5vX21hY3JvX2YxIDwtIGlmKG5vX21hY3JvX3ByZWNpc2lvbiArIG5vX21hY3JvX3JlY2FsbCA+IDApIA0KICAgICAgICAgICAgICAgICAgICAgMiAqIG5vX21hY3JvX3ByZWNpc2lvbiAqIG5vX21hY3JvX3JlY2FsbCAvIChub19tYWNyb19wcmVjaXNpb24gKyBub19tYWNyb19yZWNhbGwpIGVsc2UgMA0KICAgICAgDQogICAgICBjYXQoIlxuQ29tcGFyaXNvbiBvZiBjbGFzc2lmaWNhdGlvbiBtZXRyaWNzOlxuIikNCiAgICAgIGNhdCgiICAgICAgICAgICAgICAgICAgIHwgV2l0aCBNYWNybyB8IFdpdGhvdXQgTWFjcm8gfCBEaWZmZXJlbmNlXG4iKQ0KICAgICAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgICAgIGNhdChzcHJpbnRmKCJBY2N1cmFjeSAgICAgICAgICB8ICUxMC40ZiB8ICUxMy40ZiB8ICUrMTAuNGZcbiIsIA0KICAgICAgICAgICAgICAgIGFjY3VyYWN5LCBub19tYWNyb19hY2N1cmFjeSwgYWNjdXJhY3kgLSBub19tYWNyb19hY2N1cmFjeSkpDQogICAgICBjYXQoc3ByaW50ZigiUHJlY2lzaW9uICAgICAgICAgfCAlMTAuNGYgfCAlMTMuNGYgfCAlKzEwLjRmXG4iLCANCiAgICAgICAgICAgICAgICBwcmVjaXNpb24sIG5vX21hY3JvX3ByZWNpc2lvbiwgcHJlY2lzaW9uIC0gbm9fbWFjcm9fcHJlY2lzaW9uKSkNCiAgICAgIGNhdChzcHJpbnRmKCJSZWNhbGwgICAgICAgICAgICB8ICUxMC40ZiB8ICUxMy40ZiB8ICUrMTAuNGZcbiIsIA0KICAgICAgICAgICAgICAgIHJlY2FsbCwgbm9fbWFjcm9fcmVjYWxsLCByZWNhbGwgLSBub19tYWNyb19yZWNhbGwpKQ0KICAgICAgY2F0KHNwcmludGYoIkYxIFNjb3JlICAgICAgICAgIHwgJTEwLjRmIHwgJTEzLjRmIHwgJSsxMC40ZlxuIiwgDQogICAgICAgICAgICAgICAgZjFfc2NvcmUsIG5vX21hY3JvX2YxLCBmMV9zY29yZSAtIG5vX21hY3JvX2YxKSkNCiAgICB9DQogICAgDQogICAgIyBSZXR1cm4gcGVyZm9ybWFuY2UgbWV0cmljcw0KICAgIHJldHVybihsaXN0KA0KICAgICAgZnVsbF9hdWMgPSBmdWxsX2F1YywNCiAgICAgIGZ1bGxfY2kgPSBmdWxsX2NpLA0KICAgICAgbm9fbWFjcm9fYXVjID0gbm9fbWFjcm9fYXVjLA0KICAgICAgbm9fbWFjcm9fY2kgPSBub19tYWNyb19jaSwNCiAgICAgIGF1Y19kaWZmID0gaWYoIWlkZW50aWNhbChtb2RlbCwgbm9fbWFjcm9fbW9kZWwpKSBhdWNfZGlmZiBlbHNlIE5BLA0KICAgICAgcF92YWx1ZSA9IGlmKCFpZGVudGljYWwobW9kZWwsIG5vX21hY3JvX21vZGVsKSkgcm9jX3Rlc3QkcC52YWx1ZSBlbHNlIE5BLA0KICAgICAgYWNjdXJhY3kgPSBhY2N1cmFjeSwNCiAgICAgIHByZWNpc2lvbiA9IHByZWNpc2lvbiwNCiAgICAgIHJlY2FsbCA9IHJlY2FsbCwNCiAgICAgIGYxX3Njb3JlID0gZjFfc2NvcmUsDQogICAgICBub19tYWNyb19hY2N1cmFjeSA9IGlmKCFpZGVudGljYWwobW9kZWwsIG5vX21hY3JvX21vZGVsKSkgbm9fbWFjcm9fYWNjdXJhY3kgZWxzZSBOQSwNCiAgICAgIG5vX21hY3JvX3ByZWNpc2lvbiA9IGlmKCFpZGVudGljYWwobW9kZWwsIG5vX21hY3JvX21vZGVsKSkgbm9fbWFjcm9fcHJlY2lzaW9uIGVsc2UgTkEsDQogICAgICBub19tYWNyb19yZWNhbGwgPSBpZighaWRlbnRpY2FsKG1vZGVsLCBub19tYWNyb19tb2RlbCkpIG5vX21hY3JvX3JlY2FsbCBlbHNlIE5BLA0KICAgICAgbm9fbWFjcm9fZjEgPSBpZighaWRlbnRpY2FsKG1vZGVsLCBub19tYWNyb19tb2RlbCkpIG5vX21hY3JvX2YxIGVsc2UgTkENCiAgICApKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgicFJPQyBwYWNrYWdlIG5vdCBhdmFpbGFibGUuIFBsZWFzZSBpbnN0YWxsIGl0IGZvciBST0MgYW5hbHlzaXMuXG4iKQ0KICAgIHJldHVybihOVUxMKQ0KICB9DQp9DQoNCiMgRXZhbHVhdGUgYmFua3J1cHRjeSBtb2RlbA0KYmFua3J1cHRjeV9vb3MgPC0gZXZhbHVhdGVfb29zX3BlcmZvcm1hbmNlKA0KICB0cmFpbl9iYW5rX21vZGVsLCANCiAgdHJhaW5fYmFua19ub19tYWNyb19tb2RlbCwgDQogIHRlc3RfZGF0YSwgDQogICJiYW5rcnVwdGN5IiwgDQogICJiYW5rcnVwdGN5Ig0KKQ0KDQojIEV2YWx1YXRlIGFjcXVpc2l0aW9uIG1vZGVsDQphY3F1aXNpdGlvbl9vb3MgPC0gZXZhbHVhdGVfb29zX3BlcmZvcm1hbmNlKA0KICB0cmFpbl9hY3FfbW9kZWwsIA0KICB0cmFpbl9hY3Ffbm9fbWFjcm9fbW9kZWwsIA0KICB0ZXN0X2RhdGEsIA0KICAiYWNxdWlzaXRpb24iLCANCiAgImFjcXVpc2l0aW9uIg0KKQ0KDQpgYGANCk5vdyB3ZSBkbyBhbiBpbm5vdmF0aXZlIGFuYWx5c2lzIG9mIHRoZSBtb2RlbCBwZXJmb3JtYW5jZSBhY3Jvc3MgZGlmZmVyZW50IGVjb25vbWljIGNvbmRpdGlvbnMuIFdlIGNhdGVnb3JpemUgdGhlIHRlc3QgZGF0YSBpbnRvIHRocmVlIGdyb3VwcyBiYXNlZCBvbiB0aGUgbGFzdCBvYnNlcnZhdGlvbiBvZiBlYWNoIGNvbXBhbnk6ICJTdHJvbmciLCAiTWl4ZWQiLCBhbmQgIldlYWsiIGVjb25vbXkuIFdlIHRoZW4gZXZhbHVhdGUgdGhlIG1vZGVsIHBlcmZvcm1hbmNlIGZvciBlYWNoIGNvbmRpdGlvbiwgY29tcGFyaW5nIHRoZSBmdWxsIG1vZGVsIHdpdGggbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgdG8gdGhlIG5vLW1hY3JvIG1vZGVsLiBUaGlzIGFuYWx5c2lzIHByb3ZpZGVzIGluc2lnaHRzIGludG8gaG93IG1hY3JvZWNvbm9taWMgZmFjdG9ycyBpbmZsdWVuY2UgcHJlZGljdGlvbiBwZXJmb3JtYW5jZSB1bmRlciBkaWZmZXJlbnQgZWNvbm9taWMgY29uZGl0aW9ucy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTIuNTogRWNvbm9taWMgQ29uZGl0aW9uLVNwZWNpZmljIFBlcmZvcm1hbmNlDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjEyLjU6IEVDT05PTUlDIENPTkRJVElPTi1TUEVDSUZJQyBQRVJGT1JNQU5DRVxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgQ2hlY2sgaWYgd2UgaGF2ZSBlY29ub21pYyBpbmRpY2F0b3JzIGluIHRoZSB0ZXN0IGRhdGENCmlmKGFsbChjKCJnZHBfZ3Jvd3RoIiwgInVuZW1wbG95ZW1lbnQiKSAlaW4lIGNvbG5hbWVzKHRlc3RfZGF0YSkpKSB7DQogIGNhdCgiQW5hbHl6aW5nIG1vZGVsIHBlcmZvcm1hbmNlIGFjcm9zcyBkaWZmZXJlbnQgZWNvbm9taWMgY29uZGl0aW9ucy4uLlxuIikNCiAgDQogICMgR2V0IGxhc3Qgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY29tcGFueSBpbiB0ZXN0IHNldA0KICB0ZXN0X2xhc3Rfb2JzIDwtIHRlc3RfZGF0YSAlPiUNCiAgICBncm91cF9ieShjdXNpcCkgJT4lDQogICAgc2xpY2VfbWF4KHRzdG9wKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgQ2FsY3VsYXRlIG1lZGlhbiB2YWx1ZXMgdG8gZGVmaW5lIGNvbmRpdGlvbnMNCiAgbWVkaWFuX2dkcCA8LSBtZWRpYW4odGVzdF9sYXN0X29icyRnZHBfZ3Jvd3RoLCBuYS5ybSA9IFRSVUUpDQogIG1lZGlhbl91bmVtcCA8LSBtZWRpYW4odGVzdF9sYXN0X29icyR1bmVtcGxveWVtZW50LCBuYS5ybSA9IFRSVUUpDQogIA0KICAjIENyZWF0ZSBlY29ub21pYyBjb25kaXRpb24gZ3JvdXBzDQogIHRlc3RfbGFzdF9vYnMkZWNvbl9jb25kaXRpb24gPC0gIk1peGVkIg0KICB0ZXN0X2xhc3Rfb2JzJGVjb25fY29uZGl0aW9uW3Rlc3RfbGFzdF9vYnMkZ2RwX2dyb3d0aCA+IG1lZGlhbl9nZHAgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdF9sYXN0X29icyR1bmVtcGxveWVtZW50IDwgbWVkaWFuX3VuZW1wXSA8LSAiU3Ryb25nIg0KICB0ZXN0X2xhc3Rfb2JzJGVjb25fY29uZGl0aW9uW3Rlc3RfbGFzdF9vYnMkZ2RwX2dyb3d0aCA8IG1lZGlhbl9nZHAgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdF9sYXN0X29icyR1bmVtcGxveWVtZW50ID4gbWVkaWFuX3VuZW1wXSA8LSAiV2VhayINCiAgDQogICMgQ2FsY3VsYXRlIGNvdW50cyBieSBjb25kaXRpb24NCiAgc3Ryb25nX2NvdW50IDwtIHN1bSh0ZXN0X2xhc3Rfb2JzJGVjb25fY29uZGl0aW9uID09ICJTdHJvbmciKQ0KICBtaXhlZF9jb3VudCA8LSBzdW0odGVzdF9sYXN0X29icyRlY29uX2NvbmRpdGlvbiA9PSAiTWl4ZWQiKQ0KICB3ZWFrX2NvdW50IDwtIHN1bSh0ZXN0X2xhc3Rfb2JzJGVjb25fY29uZGl0aW9uID09ICJXZWFrIikNCiAgDQogIGNhdCgiXG5FY29ub21pYyBjb25kaXRpb24gZGlzdHJpYnV0aW9uIGluIHRlc3Qgc2V0OlxuIikNCiAgY2F0KCItIFN0cm9uZyBlY29ub215OiIsIHN0cm9uZ19jb3VudCwgImNvbXBhbmllc1xuIikNCiAgY2F0KCItIE1peGVkIGVjb25vbXk6IiwgbWl4ZWRfY291bnQsICJjb21wYW5pZXNcbiIpDQogIGNhdCgiLSBXZWFrIGVjb25vbXk6Iiwgd2Vha19jb3VudCwgImNvbXBhbmllc1xuIikNCiAgDQogICMgRnVuY3Rpb24gdG8gZXZhbHVhdGUgcGVyZm9ybWFuY2UgYnkgZWNvbm9taWMgY29uZGl0aW9uDQogIGV2YWx1YXRlX2J5X2NvbmRpdGlvbiA8LSBmdW5jdGlvbihtb2RlbCwgbm9fbWFjcm9fbW9kZWwsIGRhdGEsIGV2ZW50X3R5cGUsIG1vZGVsX25hbWUpIHsNCiAgICBjYXQoIlxuRXZhbHVhdGluZyIsIG1vZGVsX25hbWUsICJtb2RlbCBwZXJmb3JtYW5jZSBhY3Jvc3MgZWNvbm9taWMgY29uZGl0aW9ucy4uLlxuIikNCiAgICANCiAgICByZXN1bHRzIDwtIGRhdGEuZnJhbWUoDQogICAgICBDb25kaXRpb24gPSBjKCJTdHJvbmciLCAiTWl4ZWQiLCAiV2VhayIpLA0KICAgICAgQ291bnQgPSBjKHN0cm9uZ19jb3VudCwgbWl4ZWRfY291bnQsIHdlYWtfY291bnQpLA0KICAgICAgRnVsbF9BVUMgPSBOQSwNCiAgICAgIE5vX01hY3JvX0FVQyA9IE5BLA0KICAgICAgSW1wcm92ZW1lbnQgPSBOQSwNCiAgICAgIFBfVmFsdWUgPSBOQQ0KICAgICkNCiAgICANCiAgICAjIENhbGN1bGF0ZSByaXNrIHNjb3JlcyBmb3IgZnVsbCBhbmQgbm8tbWFjcm8gbW9kZWxzDQogICAgZnVsbF9yaXNrX3Njb3JlcyA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gZGF0YSwgdHlwZSA9ICJyaXNrIikNCiAgICBub19tYWNyb19yaXNrX3Njb3JlcyA8LSBwcmVkaWN0KG5vX21hY3JvX21vZGVsLCBuZXdkYXRhID0gZGF0YSwgdHlwZSA9ICJyaXNrIikNCiAgICANCiAgICAjIEFkZCBzY29yZXMgdG8gZGF0YQ0KICAgIGRhdGEkZnVsbF9yaXNrIDwtIGZ1bGxfcmlza19zY29yZXMNCiAgICBkYXRhJG5vX21hY3JvX3Jpc2sgPC0gbm9fbWFjcm9fcmlza19zY29yZXMNCiAgICANCiAgICAjIEFuYWx5emUgZWFjaCBjb25kaXRpb24NCiAgICBmb3IoY29uZGl0aW9uIGluIGMoIlN0cm9uZyIsICJNaXhlZCIsICJXZWFrIikpIHsNCiAgICAgIGNvbmRpdGlvbl9kYXRhIDwtIGRhdGFbZGF0YSRlY29uX2NvbmRpdGlvbiA9PSBjb25kaXRpb24sIF0NCiAgICAgIA0KICAgICAgaWYobnJvdyhjb25kaXRpb25fZGF0YSkgPiAxMCAmJiBzdW0oY29uZGl0aW9uX2RhdGFbW2V2ZW50X3R5cGVdXSkgPiAwKSB7DQogICAgICAgIGNhdCgiXG4iLCBjb25kaXRpb24sICJFY29ub215ICgiLCBucm93KGNvbmRpdGlvbl9kYXRhKSwgImNvbXBhbmllcywgIiwgDQogICAgICAgICAgICBzdW0oY29uZGl0aW9uX2RhdGFbW2V2ZW50X3R5cGVdXSksIGV2ZW50X3R5cGUsICJldmVudHMgKTpcbiIsIHNlcD0iIikNCiAgICAgICAgDQogICAgICAgICMgQ2FsY3VsYXRlIEFVQ3MgZm9yIGVhY2ggbW9kZWwNCiAgICAgICAgZnVsbF9yb2MgPC0gdHJ5Q2F0Y2goew0KICAgICAgICAgIHJvYyhjb25kaXRpb25fZGF0YVtbZXZlbnRfdHlwZV1dLCBjb25kaXRpb25fZGF0YSRmdWxsX3Jpc2ssIHF1aWV0ID0gVFJVRSkNCiAgICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQ0KICAgICAgICANCiAgICAgICAgbm9fbWFjcm9fcm9jIDwtIHRyeUNhdGNoKHsNCiAgICAgICAgICByb2MoY29uZGl0aW9uX2RhdGFbW2V2ZW50X3R5cGVdXSwgY29uZGl0aW9uX2RhdGEkbm9fbWFjcm9fcmlzaywgcXVpZXQgPSBUUlVFKQ0KICAgICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIE5VTEwpDQogICAgICAgIA0KICAgICAgICBpZighaXMubnVsbChmdWxsX3JvYykgJiYgIWlzLm51bGwobm9fbWFjcm9fcm9jKSkgew0KICAgICAgICAgIGZ1bGxfYXVjIDwtIGF1YyhmdWxsX3JvYykNCiAgICAgICAgICBub19tYWNyb19hdWMgPC0gYXVjKG5vX21hY3JvX3JvYykNCiAgICAgICAgICANCiAgICAgICAgICAjIFN0b3JlIHJlc3VsdHMNCiAgICAgICAgICByb3dfaWR4IDwtIHdoaWNoKHJlc3VsdHMkQ29uZGl0aW9uID09IGNvbmRpdGlvbikNCiAgICAgICAgICByZXN1bHRzJEZ1bGxfQVVDW3Jvd19pZHhdIDwtIGZ1bGxfYXVjDQogICAgICAgICAgcmVzdWx0cyROb19NYWNyb19BVUNbcm93X2lkeF0gPC0gbm9fbWFjcm9fYXVjDQogICAgICAgICAgcmVzdWx0cyRJbXByb3ZlbWVudFtyb3dfaWR4XSA8LSBmdWxsX2F1YyAtIG5vX21hY3JvX2F1Yw0KICAgICAgICAgIA0KICAgICAgICAgICMgVGVzdCBmb3Igc2lnbmlmaWNhbmNlIGlmIG1vZGVscyBhcmUgZGlmZmVyZW50DQogICAgICAgICAgaWYoIWlkZW50aWNhbChtb2RlbCwgbm9fbWFjcm9fbW9kZWwpKSB7DQogICAgICAgICAgICByb2NfdGVzdCA8LSB0cnlDYXRjaCh7DQogICAgICAgICAgICAgIHJvYy50ZXN0KGZ1bGxfcm9jLCBub19tYWNyb19yb2MsIG1ldGhvZCA9ICJkZWxvbmciKQ0KICAgICAgICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQ0KICAgICAgICAgICAgDQogICAgICAgICAgICBpZighaXMubnVsbChyb2NfdGVzdCkpIHsNCiAgICAgICAgICAgICAgcmVzdWx0cyRQX1ZhbHVlW3Jvd19pZHhdIDwtIHJvY190ZXN0JHAudmFsdWUNCiAgICAgICAgICAgIH0NCiAgICAgICAgICB9DQogICAgICAgICAgDQogICAgICAgICAgIyBQcmludCByZXN1bHRzDQogICAgICAgICAgY2F0KCItIEZ1bGwgbW9kZWwgQVVDOiIsIHJvdW5kKGZ1bGxfYXVjLCA0KSwgIlxuIikNCiAgICAgICAgICANCiAgICAgICAgICBpZighaWRlbnRpY2FsKG1vZGVsLCBub19tYWNyb19tb2RlbCkpIHsNCiAgICAgICAgICAgIGNhdCgiLSBOby1tYWNybyBtb2RlbCBBVUM6Iiwgcm91bmQobm9fbWFjcm9fYXVjLCA0KSwgIlxuIikNCiAgICAgICAgICAgIGltcHJvdmVtZW50IDwtIGZ1bGxfYXVjIC0gbm9fbWFjcm9fYXVjDQogICAgICAgICAgICBwY3RfaW1wcm92ZW1lbnQgPC0gaW1wcm92ZW1lbnQgLyBub19tYWNyb19hdWMgKiAxMDANCiAgICAgICAgICAgIA0KICAgICAgICAgICAgY2F0KCItIEltcHJvdmVtZW50IGZyb20gbWFjcm8gdmFyaWFibGVzOiIsIHJvdW5kKGltcHJvdmVtZW50LCA0KSwgDQogICAgICAgICAgICAgICAgIigiLCByb3VuZChwY3RfaW1wcm92ZW1lbnQsIDEpLCAiJSlcbiIsIHNlcD0iIikNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgaWYoIWlzLm51bGwocm9jX3Rlc3QpKSB7DQogICAgICAgICAgICAgIGNhdCgiLSBTdGF0aXN0aWNhbCBzaWduaWZpY2FuY2U6IHAgPSIsIGZvcm1hdC5wdmFsKHJvY190ZXN0JHAudmFsdWUsIGRpZ2l0cyA9IDMpLCAiXG4iKQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgIH0NCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBjYXQoIi0gSW5zdWZmaWNpZW50IGRhdGEgZm9yIFJPQyBhbmFseXNpc1xuIikNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgY2F0KCJcbiIsIGNvbmRpdGlvbiwgIkVjb25vbXk6IEluc3VmZmljaWVudCBkYXRhIGZvciBhbmFseXNpc1xuIiwgc2VwPSIiKQ0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICAjIENvbXBhcmUgYWNyb3NzIGNvbmRpdGlvbnMNCiAgICBjYXQoIlxuU3VtbWFyeSBvZiBlY29ub21pYyBjb25kaXRpb24tc3BlY2lmaWMgcGVyZm9ybWFuY2U6XG4iKQ0KICAgIHByaW50KHJlc3VsdHMpDQogICAgDQogICAgIyBJbnRlcnByZXQgcmVzdWx0cw0KICAgIGlmKCFpZGVudGljYWwobW9kZWwsIG5vX21hY3JvX21vZGVsKSAmJiANCiAgICAgICAhYWxsKGlzLm5hKHJlc3VsdHMkSW1wcm92ZW1lbnQpKSAmJiANCiAgICAgICBtYXgocmVzdWx0cyRJbXByb3ZlbWVudCwgbmEucm0gPSBUUlVFKSA+IDApIHsNCiAgICAgIA0KICAgICAgIyBGaW5kIGNvbmRpdGlvbiB3aXRoIG1heGltdW0gaW1wcm92ZW1lbnQNCiAgICAgIG1heF9pZHggPC0gd2hpY2gubWF4KHJlc3VsdHMkSW1wcm92ZW1lbnQpDQogICAgICBtYXhfY29uZGl0aW9uIDwtIHJlc3VsdHMkQ29uZGl0aW9uW21heF9pZHhdDQogICAgICBtYXhfaW1wcm92ZW1lbnQgPC0gcmVzdWx0cyRJbXByb3ZlbWVudFttYXhfaWR4XQ0KICAgICAgDQogICAgICBjYXQoIlxuSW50ZXJwcmV0YXRpb246XG4iKQ0KICAgICAgY2F0KCItIE1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGNvbnRyaWJ1dGUgbW9zdCBkdXJpbmciLCBtYXhfY29uZGl0aW9uLCAiZWNvbm9taWMgY29uZGl0aW9uc1xuIikNCiAgICAgIGNhdCgiICAoQVVDIGltcHJvdmVtZW50OiArIiwgcm91bmQobWF4X2ltcHJvdmVtZW50LCA0KSwgIilcbiIsIHNlcD0iIikNCiAgICAgIA0KICAgICAgaWYobWF4X2NvbmRpdGlvbiA9PSAiU3Ryb25nIikgew0KICAgICAgICBjYXQoIi0gVGhpcyBzdWdnZXN0cyB0aGF0IGVjb25vbWljIGZhY3RvcnMgbWF5IGJlIG1vcmUgaW1wb3J0YW50IGZvciIsIA0KICAgICAgICAgICAgbW9kZWxfbmFtZSwgInByZWRpY3Rpb25cbiAgZHVyaW5nIGVjb25vbWljIGV4cGFuc2lvbnNcbiIpDQogICAgICB9IGVsc2UgaWYobWF4X2NvbmRpdGlvbiA9PSAiV2VhayIpIHsNCiAgICAgICAgY2F0KCItIFRoaXMgc3VnZ2VzdHMgdGhhdCBlY29ub21pYyBmYWN0b3JzIG1heSBiZSBtb3JlIGltcG9ydGFudCBmb3IiLCANCiAgICAgICAgICAgIG1vZGVsX25hbWUsICJwcmVkaWN0aW9uXG4gIGR1cmluZyBlY29ub21pYyBkb3dudHVybnNcbiIpDQogICAgICB9DQogICAgfQ0KICAgIA0KICAgIHJldHVybihyZXN1bHRzKQ0KICB9DQogIA0KICAjIEFuYWx5emUgYmFua3J1cHRjeSBtb2RlbCBieSBlY29ub21pYyBjb25kaXRpb24NCiAgYmFua3J1cHRjeV9lY29uIDwtIGV2YWx1YXRlX2J5X2NvbmRpdGlvbigNCiAgICB0cmFpbl9iYW5rX21vZGVsLCANCiAgICB0cmFpbl9iYW5rX25vX21hY3JvX21vZGVsLCANCiAgICB0ZXN0X2xhc3Rfb2JzLCANCiAgICAiYmFua3J1cHRjeSIsIA0KICAgICJiYW5rcnVwdGN5Ig0KICApDQogIA0KICAjIEFuYWx5emUgYWNxdWlzaXRpb24gbW9kZWwgYnkgZWNvbm9taWMgY29uZGl0aW9uDQogIGFjcXVpc2l0aW9uX2Vjb24gPC0gZXZhbHVhdGVfYnlfY29uZGl0aW9uKA0KICAgIHRyYWluX2FjcV9tb2RlbCwgDQogICAgdHJhaW5fYWNxX25vX21hY3JvX21vZGVsLCANCiAgICB0ZXN0X2xhc3Rfb2JzLCANCiAgICAiYWNxdWlzaXRpb24iLCANCiAgICAiYWNxdWlzaXRpb24iDQogICkNCiAgDQogICMgQ29tcGFyZSBlY29ub21pYyBzZW5zaXRpdml0eSBiZXR3ZWVuIG1vZGVscw0KICBpZihoYXNfbWFjcm9fYmFuayAmJiBoYXNfbWFjcm9fYWNxICYmIA0KICAgICAhYWxsKGlzLm5hKGJhbmtydXB0Y3lfZWNvbiRJbXByb3ZlbWVudCkpICYmIA0KICAgICAhYWxsKGlzLm5hKGFjcXVpc2l0aW9uX2Vjb24kSW1wcm92ZW1lbnQpKSkgew0KICAgIA0KICAgIGNhdCgiXG5DT01QQVJBVElWRSBFQ09OT01JQyBTRU5TSVRJVklUWSBBTkFMWVNJUzpcbiIpDQogICAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogICAgDQogICAgIyBDYWxjdWxhdGUgbWF4aW11bSBpbXByb3ZlbWVudHMgZm9yIGVhY2ggbW9kZWwNCiAgICBtYXhfYmtfaW1wcm92ZW1lbnQgPC0gbWF4KGJhbmtydXB0Y3lfZWNvbiRJbXByb3ZlbWVudCwgbmEucm0gPSBUUlVFKQ0KICAgIG1heF9ia19jb25kaXRpb24gPC0gYmFua3J1cHRjeV9lY29uJENvbmRpdGlvblt3aGljaC5tYXgoYmFua3J1cHRjeV9lY29uJEltcHJvdmVtZW50KV0NCiAgICANCiAgICBtYXhfYWNxX2ltcHJvdmVtZW50IDwtIG1heChhY3F1aXNpdGlvbl9lY29uJEltcHJvdmVtZW50LCBuYS5ybSA9IFRSVUUpDQogICAgbWF4X2FjcV9jb25kaXRpb24gPC0gYWNxdWlzaXRpb25fZWNvbiRDb25kaXRpb25bd2hpY2gubWF4KGFjcXVpc2l0aW9uX2Vjb24kSW1wcm92ZW1lbnQpXQ0KICAgIA0KICAgIGNhdCgiTWF4aW11bSBtYWNyb2Vjb25vbWljIGNvbnRyaWJ1dGlvbiBieSBtb2RlbDpcbiIpDQogICAgY2F0KCItIEJhbmtydXB0Y3kgbW9kZWw6ICsiLCByb3VuZChtYXhfYmtfaW1wcm92ZW1lbnQsIDQpLCANCiAgICAgICAgImR1cmluZyIsIG1heF9ia19jb25kaXRpb24sICJlY29ub215XG4iLCBzZXA9IiIpDQogICAgY2F0KCItIEFjcXVpc2l0aW9uIG1vZGVsOiArIiwgcm91bmQobWF4X2FjcV9pbXByb3ZlbWVudCwgNCksIA0KICAgICAgICAiZHVyaW5nIiwgbWF4X2FjcV9jb25kaXRpb24sICJlY29ub215XG4iLCBzZXA9IiIpDQogICAgDQogICAgIyBDb21wYXJlIG92ZXJhbGwgZWNvbm9taWMgc2Vuc2l0aXZpdHkNCiAgICBpZihtYXhfYmtfaW1wcm92ZW1lbnQgPiBtYXhfYWNxX2ltcHJvdmVtZW50KSB7DQogICAgICBjYXQoIlxuVGhlIGJhbmtydXB0Y3kgbW9kZWwgc2hvd3MgR1JFQVRFUiBzZW5zaXRpdml0eSB0byBlY29ub21pYyBjb25kaXRpb25zXG4iKQ0KICAgICAgY2F0KCJUaGlzIHN1Z2dlc3RzIHRoYXQgYmFua3J1cHRjeSByaXNrIGlzIG1vcmUgc3lzdGVtYXRpY2FsbHkgbGlua2VkIHRvIHRoZVxuIikNCiAgICAgIGNhdCgiYnJvYWRlciBlY29ub21pYyBlbnZpcm9ubWVudCB0aGFuIGFjcXVpc2l0aW9uIGxpa2VsaWhvb2RcbiIpDQogICAgfSBlbHNlIGlmKG1heF9hY3FfaW1wcm92ZW1lbnQgPiBtYXhfYmtfaW1wcm92ZW1lbnQpIHsNCiAgICAgIGNhdCgiXG5UaGUgYWNxdWlzaXRpb24gbW9kZWwgc2hvd3MgR1JFQVRFUiBzZW5zaXRpdml0eSB0byBlY29ub21pYyBjb25kaXRpb25zXG4iKQ0KICAgICAgY2F0KCJUaGlzIHN1Z2dlc3RzIHRoYXQgTSZBIGFjdGl2aXR5IGlzIG1vcmUgc3lzdGVtYXRpY2FsbHkgbGlua2VkIHRvIHRoZVxuIikNCiAgICAgIGNhdCgiYnJvYWRlciBlY29ub21pYyBlbnZpcm9ubWVudCB0aGFuIGJhbmtydXB0Y3kgcmlza1xuIikNCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCJcbkJvdGggbW9kZWxzIHNob3cgU0lNSUxBUiBzZW5zaXRpdml0eSB0byBlY29ub21pYyBjb25kaXRpb25zXG4iKQ0KICAgIH0NCiAgICANCiAgICAjIENvbXBhcmUgY29uZGl0aW9uLXNwZWNpZmljIHNlbnNpdGl2aXR5DQogICAgaWYobWF4X2JrX2NvbmRpdGlvbiAhPSBtYXhfYWNxX2NvbmRpdGlvbikgew0KICAgICAgY2F0KCJcblRoZSBtb2RlbHMgc2hvdyBESUZGRVJFTlQgcGF0dGVybnMgb2YgZWNvbm9taWMgc2Vuc2l0aXZpdHk6XG4iKQ0KICAgICAgY2F0KCItIEJhbmtydXB0Y3kgcHJlZGljdGlvbiBiZW5lZml0cyBtb3N0IGZyb20gbWFjcm8gdmFyaWFibGVzIGR1cmluZyIsIA0KICAgICAgICAgIG1heF9ia19jb25kaXRpb24sICJlY29ub215XG4iKQ0KICAgICAgY2F0KCItIEFjcXVpc2l0aW9uIHByZWRpY3Rpb24gYmVuZWZpdHMgbW9zdCBmcm9tIG1hY3JvIHZhcmlhYmxlcyBkdXJpbmciLCANCiAgICAgICAgICBtYXhfYWNxX2NvbmRpdGlvbiwgImVjb25vbXlcbiIpDQogICAgICBjYXQoIlxuVGhpcyBzdWdnZXN0cyB0aGF0IGRpZmZlcmVudCBlY29ub21pYyBtZWNoYW5pc21zIGRyaXZlIHRoZXNlIGNvcnBvcmF0ZSBvdXRjb21lc1xuIikNCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCJcbkJvdGggbW9kZWxzIGJlbmVmaXQgbW9zdCBmcm9tIG1hY3JvIHZhcmlhYmxlcyBkdXJpbmciLCBtYXhfYmtfY29uZGl0aW9uLCAiZWNvbm9teVxuIikNCiAgICAgIGNhdCgiVGhpcyBzdWdnZXN0cyB0aGF0IHNpbWlsYXIgZWNvbm9taWMgbWVjaGFuaXNtcyBpbmZsdWVuY2UgYm90aCBvdXRjb21lc1xuIikNCiAgICB9DQogIH0NCn0gZWxzZSB7DQogIGNhdCgiRWNvbm9taWMgaW5kaWNhdG9ycyBub3QgYXZhaWxhYmxlIGluIHRlc3QgZGF0YSBmb3IgY29uZGl0aW9uLXNwZWNpZmljIGFuYWx5c2lzLlxuIikNCn0NCg0KYGBgDQoNCg0KV2Ugbm93IGNvbnNpZGVyIHRpbWUgaG9yaXpvbnMgZm9yIG91dC1vZi1zYW1wbGUgZXZhbHVhdGlvbi4gV2UgdXNlIHRoZSBwcmV2aW91c2x5IGlkZW50aWZpZWQgYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gaG9yaXpvbnMgZnJvbSB0aGUgdHJhaW5pbmcgZGF0YSB0byBldmFsdWF0ZSB0aGUgbW9kZWxzJyBwZXJmb3JtYW5jZSBhdCB0aGVzZSBzcGVjaWZpYyB0aW1lIHBvaW50cy4gVGhpcyBhbmFseXNpcyBoZWxwcyB1cyB1bmRlcnN0YW5kIGhvdyB3ZWxsIHRoZSBtb2RlbHMgcGVyZm9ybSBvdmVyIHRpbWUsIHBhcnRpY3VsYXJseSBpbiBwcmVkaWN0aW5nIGV2ZW50cyB0aGF0IG9jY3VyIGF0IGRpZmZlcmVudCBpbnRlcnZhbHMuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDEyLjY6IE91dC1vZi1TYW1wbGUgVGltZS1EZXBlbmRlbnQgUGVyZm9ybWFuY2UNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTIuNjogT1VULU9GLVNBTVBMRSBUSU1FLURFUEVOREVOVCBQRVJGT1JNQU5DRVxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gZm9yIG91dC1vZi1zYW1wbGUgdGltZS1kZXBlbmRlbnQgZXZhbHVhdGlvbg0KZXZhbHVhdGVfdGltZV9kZXBlbmRlbnRfb3V0X29mX3NhbXBsZSA8LSBmdW5jdGlvbihtb2RlbCwgbW9kZWxfbmFtZSwgZXZlbnRfdHlwZSwgZGF0YV9zdWJzZXQsIGhvcml6b25zKSB7DQogIGNhdCgiRXZhbHVhdGluZyBvdXQtb2Ytc2FtcGxlIHRpbWUtZGVwZW5kZW50IHBlcmZvcm1hbmNlIGZvciIsIG1vZGVsX25hbWUsICJtb2RlbC4uLlxuIikNCiAgDQogICMgUHJlcGFyZSByZXN1bHRzDQogIHJlc3VsdHMgPC0gZGF0YS5mcmFtZSgNCiAgICBIb3Jpem9uID0gaG9yaXpvbnMsDQogICAgQVVDID0gTkEsDQogICAgRXZlbnRzID0gTkENCiAgKQ0KICANCiAgZm9yKGkgaW4gMTpsZW5ndGgoaG9yaXpvbnMpKSB7DQogICAgaG9yaXpvbiA8LSBob3Jpem9uc1tpXQ0KICAgIGNhdCgiUHJvY2Vzc2luZyBob3Jpem9uOiIsIGhvcml6b24sICJ5ZWFycy4uLlxuIikNCiAgICANCiAgICAjIEdldCBhbGwgY29tcGFuaWVzDQogICAgdGVzdF9jb21wYW5pZXMgPC0gdW5pcXVlKGRhdGFfc3Vic2V0JGN1c2lwKQ0KICAgIA0KICAgICMgUHJlcGFyZSBkYXRhIGZvciB0aGlzIGhvcml6b24NCiAgICBob3Jpem9uX2RhdGEgPC0gZGF0YS5mcmFtZSgpDQogICAgDQogICAgZm9yKGNvbXBhbnkgaW4gdGVzdF9jb21wYW5pZXMpIHsNCiAgICAgICMgR2V0IGRhdGEgZm9yIHRoaXMgY29tcGFueQ0KICAgICAgY29tcGFueV9kYXRhIDwtIGRhdGFfc3Vic2V0W2RhdGFfc3Vic2V0JGN1c2lwID09IGNvbXBhbnksIF0NCiAgICAgIA0KICAgICAgIyBHZXQgZGF0YSB1cCB0byBob3Jpem9uDQogICAgICBjb21wYW55X2F0X2hvcml6b24gPC0gY29tcGFueV9kYXRhW2NvbXBhbnlfZGF0YSR0c3RvcCA8PSBob3Jpem9uLCBdDQogICAgICANCiAgICAgIGlmKG5yb3coY29tcGFueV9hdF9ob3Jpem9uKSA+IDApIHsNCiAgICAgICAgIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBiZWZvcmUgaG9yaXpvbg0KICAgICAgICBsYXN0X29ic19pbmRleCA8LSB3aGljaC5tYXgoY29tcGFueV9hdF9ob3Jpem9uJHRzdG9wKQ0KICAgICAgICBsYXN0X29icyA8LSBjb21wYW55X2F0X2hvcml6b25bbGFzdF9vYnNfaW5kZXgsIF0NCiAgICAgICAgDQogICAgICAgICMgQWRkIHRvIGhvcml6b24gZGF0YQ0KICAgICAgICBob3Jpem9uX2RhdGEgPC0gcmJpbmQoaG9yaXpvbl9kYXRhLCBsYXN0X29icykNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgaWYobnJvdyhob3Jpem9uX2RhdGEpID4gMCkgew0KICAgICAgIyBHZXQgZXZlbnQgaW5kaWNhdG9ycyBhbmQgcmlzayBzY29yZXMNCiAgICAgIGV2ZW50X2luZGljYXRvciA8LSBob3Jpem9uX2RhdGFbW2V2ZW50X3R5cGVdXQ0KICAgICAgDQogICAgICByaXNrX3Njb3JlcyA8LSB0cnlDYXRjaCh7DQogICAgICAgIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBob3Jpem9uX2RhdGEsIHR5cGUgPSAicmlzayIpDQogICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgY2F0KCIgIEVycm9yIGNhbGN1bGF0aW5nIHJpc2sgc2NvcmVzOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICAgICAgcmV0dXJuKHJlcChOQSwgbnJvdyhob3Jpem9uX2RhdGEpKSkNCiAgICAgIH0pDQogICAgICANCiAgICAgICMgQ291bnQgZXZlbnRzDQogICAgICBuX2V2ZW50cyA8LSBzdW0oZXZlbnRfaW5kaWNhdG9yKQ0KICAgICAgbl90b3RhbCA8LSBucm93KGhvcml6b25fZGF0YSkNCiAgICAgIA0KICAgICAgY2F0KCIgIENvbXBhbmllcyBvYnNlcnZlZCBhdCBob3Jpem9uOiIsIG5fdG90YWwsICJcbiIpDQogICAgICBjYXQoIiAgRXZlbnRzIGJ5IGhvcml6b246Iiwgbl9ldmVudHMsICJcbiIpDQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIEFVQyBpZiBwb3NzaWJsZQ0KICAgICAgaWYobl9ldmVudHMgPiAwICYmIG5fZXZlbnRzIDwgbl90b3RhbCAmJiBhbGwoIWlzLm5hKHJpc2tfc2NvcmVzKSkpIHsNCiAgICAgICAgaWYocmVxdWlyZU5hbWVzcGFjZSgicFJPQyIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgICAgICAgIGxpYnJhcnkocFJPQykNCiAgICAgICAgICB0cnlDYXRjaCh7DQogICAgICAgICAgICByb2Nfb2JqIDwtIHJvYyhldmVudF9pbmRpY2F0b3IsIHJpc2tfc2NvcmVzLCBxdWlldCA9IFRSVUUpDQogICAgICAgICAgICBhdWNfdmFsIDwtIGFzLm51bWVyaWMoYXVjKHJvY19vYmopKQ0KICAgICAgICAgICAgDQogICAgICAgICAgICAjIFN0b3JlIHJlc3VsdHMNCiAgICAgICAgICAgIHJlc3VsdHMkQVVDW2ldIDwtIGF1Y192YWwNCiAgICAgICAgICAgIHJlc3VsdHMkRXZlbnRzW2ldIDwtIG5fZXZlbnRzDQogICAgICAgICAgICANCiAgICAgICAgICAgIGNhdCgiICBPdXQtb2Ytc2FtcGxlIEFVQyBhdCIsIGhvcml6b24sICJ5ZWFyczoiLCByb3VuZChhdWNfdmFsLCA0KSwgIlxuIikNCiAgICAgICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgICAgIGNhdCgiICBFcnJvciBjYWxjdWxhdGluZyBBVUM6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgICAgICAgIH0pDQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGNhdCgiICBJbnN1ZmZpY2llbnQgZXZlbnRzIGZvciBBVUMgY2FsY3VsYXRpb25cbiIpDQogICAgICB9DQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiICBObyBkYXRhIGF2YWlsYWJsZSBmb3IgdGhpcyBob3Jpem9uXG4iKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBQcmludCByZXN1bHRzDQogIGNhdCgiXG5PdXQtb2Ytc2FtcGxlIHRpbWUtZGVwZW5kZW50IEFVQyBmb3IiLCBtb2RlbF9uYW1lLCAicHJlZGljdGlvbjpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICANCiAgZm9yKGkgaW4gMTpucm93KHJlc3VsdHMpKSB7DQogICAgaG9yaXpvbiA8LSByZXN1bHRzJEhvcml6b25baV0NCiAgICBhdWNfdmFsIDwtIHJlc3VsdHMkQVVDW2ldDQogICAgZXZlbnRzIDwtIHJlc3VsdHMkRXZlbnRzW2ldDQogICAgDQogICAgaWYoIWlzLm5hKGF1Y192YWwpKSB7DQogICAgICBjYXQoc3ByaW50ZigiQXQgJWQteWVhciBob3Jpem9uOiBBVUMgPSAlLjRmICglZCBldmVudHMpXG4iLCANCiAgICAgICAgICAgICAgICBob3Jpem9uLCBhdWNfdmFsLCBldmVudHMpKQ0KICAgIH0gZWxzZSB7DQogICAgICBjYXQoc3ByaW50ZigiQXQgJWQteWVhciBob3Jpem9uOiBBVUMgbm90IGF2YWlsYWJsZVxuIiwgaG9yaXpvbikpDQogICAgfQ0KICB9DQogIA0KICByZXR1cm4ocmVzdWx0cykNCn0NCg0KIyBVc2UgdGhlIHNhbWUgaG9yaXpvbnMgYXMgZGV0ZXJtaW5lZCBpbiBTdGVwIDExLjUNCmNhdCgiVXNpbmcgcHJldmlvdXNseSBpZGVudGlmaWVkIHRpbWUgaG9yaXpvbnMgZm9yIG91dC1vZi1zYW1wbGUgZXZhbHVhdGlvbi4uLlxuIikNCmNhdCgiQmFua3J1cHRjeSBob3Jpem9ucyAoeWVhcnMpOiIsIHBhc3RlKGhvcml6b25zJGJhbmtydXB0Y3ksIGNvbGxhcHNlPSIsICIpLCAiXG4iKQ0KY2F0KCJBY3F1aXNpdGlvbiBob3Jpem9ucyAoeWVhcnMpOiIsIHBhc3RlKGhvcml6b25zJGFjcXVpc2l0aW9uLCBjb2xsYXBzZT0iLCAiKSwgIlxuIikNCg0KIyBDYWxjdWxhdGUgb3V0LW9mLXNhbXBsZSB0aW1lLWRlcGVuZGVudCBBVUMNCmJhbmtydXB0Y3lfdGRfb3V0IDwtIGV2YWx1YXRlX3RpbWVfZGVwZW5kZW50X291dF9vZl9zYW1wbGUoDQogIHRyYWluX2JhbmtfbW9kZWwsICJiYW5rcnVwdGN5IiwgImJhbmtydXB0Y3kiLCB0ZXN0X2RhdGEsIGhvcml6b25zJGJhbmtydXB0Y3kpDQoNCmFjcXVpc2l0aW9uX3RkX291dCA8LSBldmFsdWF0ZV90aW1lX2RlcGVuZGVudF9vdXRfb2Zfc2FtcGxlKA0KICB0cmFpbl9hY3FfbW9kZWwsICJhY3F1aXNpdGlvbiIsICJhY3F1aXNpdGlvbiIsIHRlc3RfZGF0YSwgaG9yaXpvbnMkYWNxdWlzaXRpb24pDQpgYGANCg0KVGhpcyBhbmFseXNpcyBwcm92aWRlcyBpbnNpZ2h0cyBpbnRvIGhvdyB0aGUgbW9kZWxzIHBlcmZvcm0gb3ZlciB0aW1lLCBwYXJ0aWN1bGFybHkgaW4gcHJlZGljdGluZyBldmVudHMgdGhhdCBvY2N1ciBhdCBkaWZmZXJlbnQgaW50ZXJ2YWxzLiBJdCBoZWxwcyB1cyB1bmRlcnN0YW5kIHRoZSBzdGFiaWxpdHkgYW5kIHJlbGlhYmlsaXR5IG9mIHRoZSBtb2RlbHMnIHByZWRpY3Rpb25zIGFjcm9zcyB2YXJpb3VzIHRpbWUgaG9yaXpvbnMuDQoNCkZpbmFsbHksIHdlIGNvbmR1Y3QgYSBjb2VmZmljaWVudCBzdGFiaWxpdHkgYW5hbHlzaXMgdG8gY29tcGFyZSB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBmdWxsIG1vZGVsIHdpdGggdGhvc2Ugb2YgdGhlIHRyYWluaW5nIG1vZGVsLiBUaGlzIGFuYWx5c2lzIGhlbHBzIHVzIGlkZW50aWZ5IGFueSBzaWduaWZpY2FudCBjaGFuZ2VzIGluIHRoZSBjb2VmZmljaWVudHMsIHdoaWNoIG1heSBpbmRpY2F0ZSBpbnN0YWJpbGl0eSBvciBzZW5zaXRpdml0eSB0byB0aGUgdHJhaW5pbmcgZGF0YS4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTIuNzogQ29lZmZpY2llbnQgU3RhYmlsaXR5IEFuYWx5c2lzDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjEyLjc6IENPRUZGSUNJRU5UIFNUQUJJTElUWSBBTkFMWVNJU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gY29tcGFyZSBjb2VmZmljaWVudHMgYmV0d2VlbiBmdWxsIGFuZCB0cmFpbmluZyBtb2RlbHMNCmNvbXBhcmVfY29lZmZpY2llbnRzIDwtIGZ1bmN0aW9uKGZ1bGxfbW9kZWwsIHRyYWluX21vZGVsLCBtb2RlbF9uYW1lKSB7DQogIGNhdCgiQ29tcGFyaW5nIGNvZWZmaWNpZW50cyBmb3IiLCBtb2RlbF9uYW1lLCAibW9kZWw6XG5cbiIpDQogIA0KICAjIEdldCBjb2VmZmljaWVudHMgZnJvbSBib3RoIG1vZGVscw0KICBmdWxsX2NvZWYgPC0gY29lZihmdWxsX21vZGVsKQ0KICB0cmFpbl9jb2VmIDwtIGNvZWYodHJhaW5fbW9kZWwpDQogIA0KICAjIEZpbmQgY29tbW9uIHZhcmlhYmxlcw0KICBjb21tb25fdmFycyA8LSBpbnRlcnNlY3QobmFtZXMoZnVsbF9jb2VmKSwgbmFtZXModHJhaW5fY29lZikpDQogIA0KICAjIENyZWF0ZSBjb21wYXJpc29uIGRhdGFmcmFtZQ0KICBjb21wYXJpc29uIDwtIGRhdGEuZnJhbWUoDQogICAgVmFyaWFibGUgPSBjb21tb25fdmFycywNCiAgICBGdWxsX01vZGVsID0gZnVsbF9jb2VmW2NvbW1vbl92YXJzXSwNCiAgICBUcmFpbmluZ19Nb2RlbCA9IHRyYWluX2NvZWZbY29tbW9uX3ZhcnNdLA0KICAgIENoYW5nZV9QY3QgPSBOQSwNCiAgICBEaXJlY3Rpb25fQ2hhbmdlID0gRkFMU0UsDQogICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQogICkNCiAgDQogICMgQ2FsY3VsYXRlIHBlcmNlbnQgY2hhbmdlIGFuZCBkaXJlY3Rpb24gY2hhbmdlcw0KICBmb3IoaSBpbiAxOm5yb3coY29tcGFyaXNvbikpIHsNCiAgICBmdWxsX3ZhbCA8LSBjb21wYXJpc29uJEZ1bGxfTW9kZWxbaV0NCiAgICB0cmFpbl92YWwgPC0gY29tcGFyaXNvbiRUcmFpbmluZ19Nb2RlbFtpXQ0KICAgIA0KICAgICMgUGVyY2VudCBjaGFuZ2UgKHVzaW5nIGFic29sdXRlIHZhbHVlcyB0byBoYW5kbGUgbmVnYXRpdmUgY29lZmZpY2llbnRzKQ0KICAgIGlmKGZ1bGxfdmFsICE9IDApIHsNCiAgICAgIGNvbXBhcmlzb24kQ2hhbmdlX1BjdFtpXSA8LSBhYnMoKHRyYWluX3ZhbCAtIGZ1bGxfdmFsKSAvIGZ1bGxfdmFsKSAqIDEwMA0KICAgIH0gZWxzZSB7DQogICAgICBjb21wYXJpc29uJENoYW5nZV9QY3RbaV0gPC0gTkENCiAgICB9DQogICAgDQogICAgIyBEaXJlY3Rpb24gY2hhbmdlDQogICAgY29tcGFyaXNvbiREaXJlY3Rpb25fQ2hhbmdlW2ldIDwtIHNpZ24oZnVsbF92YWwpICE9IHNpZ24odHJhaW5fdmFsKQ0KICB9DQogIA0KICAjIFNvcnQgYnkgYWJzb2x1dGUgcGVyY2VudCBjaGFuZ2UNCiAgY29tcGFyaXNvbiA8LSBjb21wYXJpc29uW29yZGVyKGNvbXBhcmlzb24kQ2hhbmdlX1BjdCwgZGVjcmVhc2luZyA9IFRSVUUpLCBdDQogIA0KICAjIFByaW50IHRhYmxlDQogIGNhdCgiVmFyaWFibGUgICAgICAgICAgICAgICBGdWxsIE1vZGVsICAgIFRyYWluIE1vZGVsICAgJSBDaGFuZ2UgICBEaXJlY3Rpb24gQ2hhbmdlXG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIA0KICBmb3IoaSBpbiAxOm5yb3coY29tcGFyaXNvbikpIHsNCiAgICB2YXIgPC0gY29tcGFyaXNvbiRWYXJpYWJsZVtpXQ0KICAgIGZ1bGxfdmFsIDwtIGNvbXBhcmlzb24kRnVsbF9Nb2RlbFtpXQ0KICAgIHRyYWluX3ZhbCA8LSBjb21wYXJpc29uJFRyYWluaW5nX01vZGVsW2ldDQogICAgcGN0X2NoYW5nZSA8LSBjb21wYXJpc29uJENoYW5nZV9QY3RbaV0NCiAgICBkaXJfY2hhbmdlIDwtIGNvbXBhcmlzb24kRGlyZWN0aW9uX0NoYW5nZVtpXQ0KICAgIA0KICAgICMgQ3JlYXRlIGZvcm1hdHRlZCBzdHJpbmcNCiAgICBjYXQoc3ByaW50ZigiJS0yMHMgJTEyLjRmICUxMi40ZiAlMTAuMWYlJSAgICVzXG4iLA0KICAgICAgICAgICAgICB2YXIsIGZ1bGxfdmFsLCB0cmFpbl92YWwsIA0KICAgICAgICAgICAgICBpZmVsc2UoaXMubmEocGN0X2NoYW5nZSksIDAsIHBjdF9jaGFuZ2UpLA0KICAgICAgICAgICAgICBpZmVsc2UoZGlyX2NoYW5nZSwgIllFUyIsICJubyIpKSkNCiAgfQ0KICANCiAgIyBDb3VudCBkaXJlY3Rpb24gY2hhbmdlcw0KICBuX2Rpcl9jaGFuZ2VzIDwtIHN1bShjb21wYXJpc29uJERpcmVjdGlvbl9DaGFuZ2UpDQogIGNhdCgiXG5WYXJpYWJsZXMgd2l0aCBkaXJlY3Rpb24gY2hhbmdlczoiLCBuX2Rpcl9jaGFuZ2VzLCAib3V0IG9mIiwgbnJvdyhjb21wYXJpc29uKSwgIlxuIikNCiAgDQogICMgSWRlbnRpZnkgdW5zdGFibGUgdmFyaWFibGVzIChsYXJnZSBjaGFuZ2VzKQ0KICB1bnN0YWJsZSA8LSBjb21wYXJpc29uW2NvbXBhcmlzb24kQ2hhbmdlX1BjdCA+IDUwIHwgY29tcGFyaXNvbiREaXJlY3Rpb25fQ2hhbmdlLCBdDQogIGlmKG5yb3codW5zdGFibGUpID4gMCkgew0KICAgIGNhdCgiXG5Qb3RlbnRpYWxseSB1bnN0YWJsZSB2YXJpYWJsZXMgKD41MCUgY2hhbmdlIG9yIGRpcmVjdGlvbiBjaGFuZ2UpOlxuIikNCiAgICBmb3IoaSBpbiAxOm5yb3codW5zdGFibGUpKSB7DQogICAgICBjYXQoIi0gIiwgdW5zdGFibGUkVmFyaWFibGVbaV0sICJcbiIpDQogICAgfQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiXG5ObyBwb3RlbnRpYWxseSB1bnN0YWJsZSB2YXJpYWJsZXMgaWRlbnRpZmllZC5cbiIpDQogIH0NCiAgDQogICMgQ2hlY2sgc3BlY2lmaWNhbGx5IGZvciBzdGFiaWxpdHkgb2YgbWFjcm8gdmFyaWFibGUgY29lZmZpY2llbnRzDQogIGlmKGhhc19tYWNyb19iYW5rIHx8IGhhc19tYWNyb19hY3EpIHsNCiAgICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAiZ2RwX2RlZmxhdG9yIiwgInVuZW1wbG95ZW1lbnQiKQ0KICAgIG1hY3JvX2NvZWZmcyA8LSBjb21wYXJpc29uW2dyZXBsKHBhc3RlKG1hY3JvX3ZhcnMsIGNvbGxhcHNlPSJ8IiksIGNvbXBhcmlzb24kVmFyaWFibGUpLCBdDQogICAgDQogICAgaWYobnJvdyhtYWNyb19jb2VmZnMpID4gMCkgew0KICAgICAgY2F0KCJcblN0YWJpbGl0eSBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlIGNvZWZmaWNpZW50czpcbiIpDQogICAgICBmb3IoaSBpbiAxOm5yb3cobWFjcm9fY29lZmZzKSkgew0KICAgICAgICB2YXIgPC0gbWFjcm9fY29lZmZzJFZhcmlhYmxlW2ldDQogICAgICAgIHBjdF9jaGFuZ2UgPC0gbWFjcm9fY29lZmZzJENoYW5nZV9QY3RbaV0NCiAgICAgICAgZGlyX2NoYW5nZSA8LSBtYWNyb19jb2VmZnMkRGlyZWN0aW9uX0NoYW5nZVtpXQ0KICAgICAgICANCiAgICAgICAgc3RhYmlsaXR5IDwtIGlmKGRpcl9jaGFuZ2UpIHsNCiAgICAgICAgICAgICAgICAgICAgICJVTlNUQUJMRSAoZGlyZWN0aW9uIGNoYW5nZSkiDQogICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmKHBjdF9jaGFuZ2UgPiA1MCkgew0KICAgICAgICAgICAgICAgICAgICAgIlVOU1RBQkxFIChsYXJnZSBtYWduaXR1ZGUgY2hhbmdlKSINCiAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYocGN0X2NoYW5nZSA+IDI1KSB7DQogICAgICAgICAgICAgICAgICAgICAiTU9ERVJBVEVMWSBTVEFCTEUiDQogICAgICAgICAgICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgICAgICAgICAgICJISUdITFkgU1RBQkxFIg0KICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgDQogICAgICAgIGNhdCgiLSAiLCB2YXIsICI6ICIsIHN0YWJpbGl0eSwgIiAoIiwgcm91bmQocGN0X2NoYW5nZSwgMSksICIlIGNoYW5nZSlcbiIsIHNlcD0iIikNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgDQogICMgUmV0dXJuIGNvbXBhcmlzb24gZGF0YQ0KICByZXR1cm4oY29tcGFyaXNvbikNCn0NCg0KIyBDb21wYXJlIGNvZWZmaWNpZW50cyBmb3IgYm90aCBtb2RlbHMNCmJhbmtfY29lZl9jb21wYXJpc29uIDwtIGNvbXBhcmVfY29lZmZpY2llbnRzKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwsIHRyYWluX2JhbmtfbW9kZWwsICJiYW5rcnVwdGN5IikNCmFjcV9jb2VmX2NvbXBhcmlzb24gPC0gY29tcGFyZV9jb2VmZmljaWVudHMoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwsIHRyYWluX2FjcV9tb2RlbCwgImFjcXVpc2l0aW9uIikNCg0KYGBgDQoNCk5vdywgaW5zdGVhZCBvZiB1c2luZyB0aGUgZnVsbCBkYXRhc2V0IGZvciBjcm9zcy12YWxpZGF0aW9uLCB3ZSB3aWxsIHVzZSBhIGNvbXBhbnktYmFzZWQgSy1mb2xkIGNyb3NzLXZhbGlkYXRpb24gYXBwcm9hY2guIFRoaXMgbWV0aG9kIGVuc3VyZXMgdGhhdCBhbGwgb2JzZXJ2YXRpb25zIGZyb20gYSBzaW5nbGUgY29tcGFueSBhcmUgZWl0aGVyIGluIHRoZSB0cmFpbmluZyBvciB0ZXN0aW5nIHNldCwgcHJldmVudGluZyBkYXRhIGxlYWthZ2UgYW5kIHByb3ZpZGluZyBhIG1vcmUgcmVhbGlzdGljIGV2YWx1YXRpb24gb2YgbW9kZWwgcGVyZm9ybWFuY2UuIFdlIHdpbGwgYWxzbyBjaGVjayB0aGUgZGlzdHJpYnV0aW9uIG9mIGV2ZW50cyBhY3Jvc3MgZm9sZHMgdG8gZW5zdXJlIHRoYXQgdGhleSBhcmUgYmFsYW5jZWQuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMTM6IEstRm9sZCBDcm9zcy1WYWxpZGF0aW9uIGZvciBTdXJ2aXZhbCBBbmFseXNpcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG49PT0gU1RFUCAxMzogSy1GT0xEIENST1NTLVZBTElEQVRJT04gRk9SIFNVUlZJVkFMIEFOQUxZU0lTID09PVxuIikNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuMTogQ29tcGFueS1CYXNlZCBLLUZvbGQgU2V0dXANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTMuMTogQ09NUEFOWS1CQVNFRCBLLUZPTEQgU0VUVVBcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNldC5zZWVkKDQ1NikNCg0KIyBTcGVjaWZ5IG51bWJlciBvZiBmb2xkcw0Ka19mb2xkcyA8LSA1DQpjYXQoIlNldHRpbmcgdXAiLCBrX2ZvbGRzLCAiZm9sZCBjcm9zcy12YWxpZGF0aW9uLi4uXG4iKQ0KDQojIEdldCB1bmlxdWUgY29tcGFueSBpZGVudGlmaWVycw0KYWxsX2NvbXBhbmllcyA8LSB1bmlxdWUoZGF0YSRjdXNpcCkNCm5fY29tcGFuaWVzIDwtIGxlbmd0aChhbGxfY29tcGFuaWVzKQ0KY2F0KCJUb3RhbCBudW1iZXIgb2YgY29tcGFuaWVzIGluIGRhdGFzZXQ6Iiwgbl9jb21wYW5pZXMsICJcbiIpDQoNCiMgUmFuZG9tbHkgYXNzaWduIGNvbXBhbmllcyB0byBmb2xkcw0KZm9sZF9hc3NpZ25tZW50cyA8LSBzYW1wbGUoMTprX2ZvbGRzLCBuX2NvbXBhbmllcywgcmVwbGFjZSA9IFRSVUUpDQpjb21wYW55X2ZvbGRzIDwtIGRhdGEuZnJhbWUoDQogIGN1c2lwID0gYWxsX2NvbXBhbmllcywNCiAgZm9sZCA9IGZvbGRfYXNzaWdubWVudHMNCikNCg0KIyBDaGVjayBkaXN0cmlidXRpb24gb2YgY29tcGFuaWVzIGFjcm9zcyBmb2xkcw0KZm9sZF9jb3VudHMgPC0gdGFibGUoY29tcGFueV9mb2xkcyRmb2xkKQ0KY2F0KCJEaXN0cmlidXRpb24gb2YgY29tcGFuaWVzIGFjcm9zcyBmb2xkczpcbiIpDQpwcmludChmb2xkX2NvdW50cykNCg0KIyBDcmVhdGUgZnVuY3Rpb24gdG8gY2hlY2sgZXZlbnQgZGlzdHJpYnV0aW9uIGluIGZvbGRzDQpjaGVja19mb2xkX2V2ZW50X2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbihkYXRhLCBjb21wYW55X2ZvbGRzKSB7DQogICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB0byBzdG9yZSByZXN1bHRzDQogIGZvbGRfZXZlbnRzIDwtIGRhdGEuZnJhbWUoDQogICAgZm9sZCA9IDE6a19mb2xkcywNCiAgICBuX2NvbXBhbmllcyA9IGFzLm51bWVyaWMoZm9sZF9jb3VudHMpLA0KICAgIG5fYmFua3J1cHRjeSA9IE5BLA0KICAgIG5fYWNxdWlzaXRpb24gPSBOQSwNCiAgICBuX2NlbnNvcmVkID0gTkENCiAgKQ0KICANCiAgIyBHZXQgZmluYWwgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY29tcGFueQ0KICBmaW5hbF9vYnMgPC0gZGF0YSAlPiUNCiAgICBncm91cF9ieShjdXNpcCkgJT4lDQogICAgc2xpY2VfbWF4KHRzdG9wKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgQWRkIGZvbGQgaW5mb3JtYXRpb24NCiAgZmluYWxfb2JzIDwtIGZpbmFsX29icyAlPiUNCiAgICBsZWZ0X2pvaW4oY29tcGFueV9mb2xkcywgYnkgPSAiY3VzaXAiKQ0KICANCiAgIyBDb3VudCBldmVudHMgYnkgZm9sZA0KICBmb3IgKGkgaW4gMTprX2ZvbGRzKSB7DQogICAgZm9sZF9kYXRhIDwtIGZpbmFsX29icyAlPiUgZmlsdGVyKGZvbGQgPT0gaSkNCiAgICBmb2xkX2V2ZW50cyRuX2JhbmtydXB0Y3lbaV0gPC0gc3VtKGZvbGRfZGF0YSRiYW5rcnVwdGN5KQ0KICAgIGZvbGRfZXZlbnRzJG5fYWNxdWlzaXRpb25baV0gPC0gc3VtKGZvbGRfZGF0YSRhY3F1aXNpdGlvbikNCiAgICBmb2xkX2V2ZW50cyRuX2NlbnNvcmVkW2ldIDwtIHN1bShmb2xkX2RhdGEkYmFua3J1cHRjeSA9PSAwICYgZm9sZF9kYXRhJGFjcXVpc2l0aW9uID09IDApDQogIH0NCiAgDQogICMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzDQogIGZvbGRfZXZlbnRzJHBjdF9iYW5rcnVwdGN5IDwtIGZvbGRfZXZlbnRzJG5fYmFua3J1cHRjeSAvIGZvbGRfZXZlbnRzJG5fY29tcGFuaWVzICogMTAwDQogIGZvbGRfZXZlbnRzJHBjdF9hY3F1aXNpdGlvbiA8LSBmb2xkX2V2ZW50cyRuX2FjcXVpc2l0aW9uIC8gZm9sZF9ldmVudHMkbl9jb21wYW5pZXMgKiAxMDANCiAgZm9sZF9ldmVudHMkcGN0X2NlbnNvcmVkIDwtIGZvbGRfZXZlbnRzJG5fY2Vuc29yZWQgLyBmb2xkX2V2ZW50cyRuX2NvbXBhbmllcyAqIDEwMA0KICANCiAgcmV0dXJuKGZvbGRfZXZlbnRzKQ0KfQ0KDQojIENoZWNrIGV2ZW50IGRpc3RyaWJ1dGlvbg0KZm9sZF9ldmVudHMgPC0gY2hlY2tfZm9sZF9ldmVudF9kaXN0cmlidXRpb24oZGF0YSwgY29tcGFueV9mb2xkcykNCmNhdCgiXG5FdmVudCBkaXN0cmlidXRpb24gYWNyb3NzIGZvbGRzOlxuIikNCnByaW50KGZvbGRfZXZlbnRzWywgYygiZm9sZCIsICJuX2NvbXBhbmllcyIsICJuX2JhbmtydXB0Y3kiLCAibl9hY3F1aXNpdGlvbiIsICJuX2NlbnNvcmVkIildKQ0KY2F0KCJcbkV2ZW50IHBlcmNlbnRhZ2VzIGFjcm9zcyBmb2xkczpcbiIpDQpwcmludChmb2xkX2V2ZW50c1ssIGMoImZvbGQiLCAicGN0X2JhbmtydXB0Y3kiLCAicGN0X2FjcXVpc2l0aW9uIiwgInBjdF9jZW5zb3JlZCIpXSkNCg0KIyBDaGVjayBpZiBkaXN0cmlidXRpb24gaXMgdG9vIGltYmFsYW5jZWQNCm1heF9iYW5rcnVwdGN5X2RpZmYgPC0gbWF4KGZvbGRfZXZlbnRzJHBjdF9iYW5rcnVwdGN5KSAtIG1pbihmb2xkX2V2ZW50cyRwY3RfYmFua3J1cHRjeSkNCm1heF9hY3F1aXNpdGlvbl9kaWZmIDwtIG1heChmb2xkX2V2ZW50cyRwY3RfYWNxdWlzaXRpb24pIC0gbWluKGZvbGRfZXZlbnRzJHBjdF9hY3F1aXNpdGlvbikNCg0KY2F0KCJcbk1heGltdW0gZGlmZmVyZW5jZSBpbiBiYW5rcnVwdGN5IHBlcmNlbnRhZ2UgYWNyb3NzIGZvbGRzOiIsIHJvdW5kKG1heF9iYW5rcnVwdGN5X2RpZmYsIDIpLCAiJVxuIikNCmNhdCgiTWF4aW11bSBkaWZmZXJlbmNlIGluIGFjcXVpc2l0aW9uIHBlcmNlbnRhZ2UgYWNyb3NzIGZvbGRzOiIsIHJvdW5kKG1heF9hY3F1aXNpdGlvbl9kaWZmLCAyKSwgIiVcbiIpDQoNCiMgSWYgZGlzdHJpYnV0aW9uIGlzIHRvbyBpbWJhbGFuY2VkLCBjb25zaWRlciBzdHJhdGlmaWVkIHNhbXBsaW5nDQppbWJhbGFuY2VfdGhyZXNob2xkIDwtIDUgICMgNSUgZGlmZmVyZW5jZSB0aHJlc2hvbGQNCmlmIChtYXhfYmFua3J1cHRjeV9kaWZmID4gaW1iYWxhbmNlX3RocmVzaG9sZCB8fCBtYXhfYWNxdWlzaXRpb25fZGlmZiA+IGltYmFsYW5jZV90aHJlc2hvbGQpIHsNCiAgY2F0KCJcbldBUk5JTkc6IEV2ZW50IGRpc3RyaWJ1dGlvbiBhY3Jvc3MgZm9sZHMgaXMgaW1iYWxhbmNlZCAoPjUlIGRpZmZlcmVuY2UpXG4iKQ0KICBjYXQoIkltcGxlbWVudGluZyBzdHJhdGlmaWVkIHNhbXBsaW5nIHRvIGJhbGFuY2UgZXZlbnQgZGlzdHJpYnV0aW9uLi4uXG4iKQ0KICANCiAgIyBHZXQgZXZlbnQgdHlwZXMgZm9yIGVhY2ggY29tcGFueQ0KICBjb21wYW55X2V2ZW50cyA8LSBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIG11dGF0ZShldmVudF9jYXRlZ29yeSA9IGNhc2Vfd2hlbigNCiAgICAgIGJhbmtydXB0Y3kgPT0gMSB+ICJiYW5rcnVwdGN5IiwNCiAgICAgIGFjcXVpc2l0aW9uID09IDEgfiAiYWNxdWlzaXRpb24iLA0KICAgICAgVFJVRSB+ICJjZW5zb3JlZCINCiAgICApKSAlPiUNCiAgICBzZWxlY3QoY3VzaXAsIGV2ZW50X2NhdGVnb3J5KSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgU3RyYXRpZmllZCBzYW1wbGluZyBieSBldmVudCB0eXBlDQogIHNldC5zZWVkKDc4OSkNCiAgY29tcGFueV9mb2xkc19zdHJhdGlmaWVkIDwtIGRhdGEuZnJhbWUoKQ0KICANCiAgZm9yIChldmVudF90eXBlIGluIGMoImJhbmtydXB0Y3kiLCAiYWNxdWlzaXRpb24iLCAiY2Vuc29yZWQiKSkgew0KICAgIGV2ZW50X2NvbXBhbmllcyA8LSBjb21wYW55X2V2ZW50cyAlPiUgDQogICAgICBmaWx0ZXIoZXZlbnRfY2F0ZWdvcnkgPT0gZXZlbnRfdHlwZSkgJT4lDQogICAgICBwdWxsKGN1c2lwKQ0KICAgIA0KICAgIG5fZXZlbnRfY29tcGFuaWVzIDwtIGxlbmd0aChldmVudF9jb21wYW5pZXMpDQogICAgZXZlbnRfZm9sZHMgPC0gc2FtcGxlKHJlcCgxOmtfZm9sZHMsIGxlbmd0aC5vdXQgPSBuX2V2ZW50X2NvbXBhbmllcykpDQogICAgDQogICAgZXZlbnRfZGYgPC0gZGF0YS5mcmFtZSgNCiAgICAgIGN1c2lwID0gZXZlbnRfY29tcGFuaWVzLA0KICAgICAgZm9sZCA9IGV2ZW50X2ZvbGRzLA0KICAgICAgZXZlbnRfY2F0ZWdvcnkgPSBldmVudF90eXBlDQogICAgKQ0KICAgIA0KICAgIGNvbXBhbnlfZm9sZHNfc3RyYXRpZmllZCA8LSByYmluZChjb21wYW55X2ZvbGRzX3N0cmF0aWZpZWQsIGV2ZW50X2RmKQ0KICB9DQogIA0KICAjIFJlcGxhY2Ugb3JpZ2luYWwgZm9sZCBhc3NpZ25tZW50cyB3aXRoIHN0cmF0aWZpZWQgb25lcw0KICBjb21wYW55X2ZvbGRzIDwtIGNvbXBhbnlfZm9sZHNfc3RyYXRpZmllZFssIGMoImN1c2lwIiwgImZvbGQiKV0NCiAgDQogICMgUmVjaGVjayBkaXN0cmlidXRpb24NCiAgZm9sZF9ldmVudHNfc3RyYXRpZmllZCA8LSBjaGVja19mb2xkX2V2ZW50X2Rpc3RyaWJ1dGlvbihkYXRhLCBjb21wYW55X2ZvbGRzKQ0KICBjYXQoIlxuRXZlbnQgZGlzdHJpYnV0aW9uIGFmdGVyIHN0cmF0aWZpY2F0aW9uOlxuIikNCiAgcHJpbnQoZm9sZF9ldmVudHNfc3RyYXRpZmllZFssIGMoImZvbGQiLCAibl9jb21wYW5pZXMiLCAibl9iYW5rcnVwdGN5IiwgIm5fYWNxdWlzaXRpb24iLCAibl9jZW5zb3JlZCIpXSkNCiAgY2F0KCJcbkV2ZW50IHBlcmNlbnRhZ2VzIGFmdGVyIHN0cmF0aWZpY2F0aW9uOlxuIikNCiAgcHJpbnQoZm9sZF9ldmVudHNfc3RyYXRpZmllZFssIGMoImZvbGQiLCAicGN0X2JhbmtydXB0Y3kiLCAicGN0X2FjcXVpc2l0aW9uIiwgInBjdF9jZW5zb3JlZCIpXSkNCiAgDQogICMgVXBkYXRlIGZvbGQgZXZlbnRzDQogIGZvbGRfZXZlbnRzIDwtIGZvbGRfZXZlbnRzX3N0cmF0aWZpZWQNCn0NCg0KIyBTdG9yZSBmb2xkIGFzc2lnbm1lbnRzIGZvciBsYXRlciB1c2UNCmZvbGRfYXNzaWdubWVudHMgPC0gY29tcGFueV9mb2xkcw0KYGBgDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuMjogSy1Gb2xkIENyb3NzLVZhbGlkYXRpb24gRXhlY3V0aW9uDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjEzLjI6IEstRk9MRCBDUk9TUy1WQUxJREFUSU9OIEVYRUNVVElPTlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgSW5pdGlhbGl6ZSBkYXRhIGZyYW1lcyB0byBzdG9yZSByZXN1bHRzDQpiYW5rcnVwdGN5X2N2X3Jlc3VsdHMgPC0gZGF0YS5mcmFtZSgNCiAgZm9sZCA9IDE6a19mb2xkcywNCiAgYXVjX3dpdGhfbWFjcm8gPSBOQSwNCiAgYXVjX25vX21hY3JvID0gTkEsDQogIGltcHJvdmVtZW50ID0gTkEsDQogIHBfdmFsdWUgPSBOQSwNCiAgY29uY29yZGFuY2UgPSBOQQ0KKQ0KDQphY3F1aXNpdGlvbl9jdl9yZXN1bHRzIDwtIGRhdGEuZnJhbWUoDQogIGZvbGQgPSAxOmtfZm9sZHMsDQogIGF1Y193aXRoX21hY3JvID0gTkEsDQogIGF1Y19ub19tYWNybyA9IE5BLA0KICBpbXByb3ZlbWVudCA9IE5BLA0KICBwX3ZhbHVlID0gTkEsDQogIGNvbmNvcmRhbmNlID0gTkENCikNCg0KIyBDcmVhdGUgY29waWVzIG9mIG1vZGVsIGZvcm11bGFzIGZvciBmdWxsIGFuZCBuby1tYWNybyBtb2RlbHMNCmJhbmtfZm9ybXVsYSA8LSBmb3JtdWxhKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpDQpub19tYWNyb19iYW5rX2Zvcm11bGEgPC0gY3JlYXRlX25vX21hY3JvX2Zvcm11bGEoYmFua19mb3JtdWxhKQ0KYWNxX2Zvcm11bGEgPC0gZm9ybXVsYShmaW5hbF9hY3F1aXNpdGlvbl9tb2RlbCkNCm5vX21hY3JvX2FjcV9mb3JtdWxhIDwtIGNyZWF0ZV9ub19tYWNyb19mb3JtdWxhKGFjcV9mb3JtdWxhKQ0KDQojIENoZWNrIGlmIG1vZGVscyBjb250YWluIG1hY3JvIHZhcmlhYmxlcw0KaGFzX21hY3JvX2JhbmsgPC0gIWlkZW50aWNhbChiYW5rX2Zvcm11bGEsIG5vX21hY3JvX2JhbmtfZm9ybXVsYSkNCmhhc19tYWNyb19hY3EgPC0gIWlkZW50aWNhbChhY3FfZm9ybXVsYSwgbm9fbWFjcm9fYWNxX2Zvcm11bGEpDQoNCiMgRXhlY3V0ZSBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbg0KZm9yIChmb2xkX2lkeCBpbiAxOmtfZm9sZHMpIHsNCiAgY2F0KCJcbi0tLS0tIFByb2Nlc3NpbmcgRm9sZCIsIGZvbGRfaWR4LCAib2YiLCBrX2ZvbGRzLCAiLS0tLS1cbiIpDQogIA0KICAjIENyZWF0ZSB0cmFpbmluZyBhbmQgdGVzdCBzZXRzIGZvciB0aGlzIGZvbGQNCiAgdHJhaW5fY29tcGFuaWVzIDwtIGZvbGRfYXNzaWdubWVudHMkY3VzaXBbZm9sZF9hc3NpZ25tZW50cyRmb2xkICE9IGZvbGRfaWR4XQ0KICB0ZXN0X2NvbXBhbmllcyA8LSBmb2xkX2Fzc2lnbm1lbnRzJGN1c2lwW2ZvbGRfYXNzaWdubWVudHMkZm9sZCA9PSBmb2xkX2lkeF0NCiAgDQogIGN2X3RyYWluX2RhdGEgPC0gZGF0YVtkYXRhJGN1c2lwICVpbiUgdHJhaW5fY29tcGFuaWVzLCBdDQogIGN2X3Rlc3RfZGF0YSA8LSBkYXRhW2RhdGEkY3VzaXAgJWluJSB0ZXN0X2NvbXBhbmllcywgXQ0KICANCiAgY2F0KCJUcmFpbmluZyBzZXQ6IiwgbGVuZ3RoKHRyYWluX2NvbXBhbmllcyksICJjb21wYW5pZXMgKCIsIG5yb3coY3ZfdHJhaW5fZGF0YSksICJvYnNlcnZhdGlvbnMpXG4iKQ0KICBjYXQoIlRlc3Rpbmcgc2V0OiIsIGxlbmd0aCh0ZXN0X2NvbXBhbmllcyksICJjb21wYW5pZXMgKCIsIG5yb3coY3ZfdGVzdF9kYXRhKSwgIm9ic2VydmF0aW9ucylcbiIpDQogIA0KICAjIEVuc3VyZSB0aW1lIHZhcmlhYmxlIGV4aXN0cw0KICBpZighInR0IiAlaW4lIGNvbG5hbWVzKGN2X3RyYWluX2RhdGEpKSBjdl90cmFpbl9kYXRhJHR0IDwtIGN2X3RyYWluX2RhdGEkdHN0b3ANCiAgaWYoISJ0dCIgJWluJSBjb2xuYW1lcyhjdl90ZXN0X2RhdGEpKSBjdl90ZXN0X2RhdGEkdHQgPC0gY3ZfdGVzdF9kYXRhJHRzdG9wDQogIA0KICAjIC0tLS0tIEJhbmtydXB0Y3kgTW9kZWwgLS0tLS0NCiAgY2F0KCJcbkZpdHRpbmcgYmFua3J1cHRjeSBtb2RlbHMgZm9yIGZvbGQiLCBmb2xkX2lkeCwgIi4uLlxuIikNCiAgDQogICMgRml0IGZ1bGwgbW9kZWwNCiAgY3ZfYmFua19tb2RlbCA8LSB0cnlDYXRjaCh7DQogICAgY294cGgoYmFua19mb3JtdWxhLCBkYXRhID0gY3ZfdHJhaW5fZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgIGNhdCgiRXJyb3IgZml0dGluZyBmdWxsIGJhbmtydXB0Y3kgbW9kZWw6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgIGNhdCgiQXR0ZW1wdGluZyB0byBmaXQgc2ltcGxpZmllZCBtb2RlbC4uLlxuIikNCiAgICANCiAgICAjIFRyeSB0byBmaXQgYSBzaW1wbGVyIG1vZGVsIHdpdGhvdXQgdGltZSBpbnRlcmFjdGlvbnMNCiAgICBzaW1wbGVfYmFua19mb3JtdWxhIDwtIHVwZGF0ZShiYW5rX2Zvcm11bGEsIC4gfiAuIC0gLjouKQ0KICAgIA0KICAgIHRyeUNhdGNoKHsNCiAgICAgIGNveHBoKHNpbXBsZV9iYW5rX2Zvcm11bGEsIGRhdGEgPSBjdl90cmFpbl9kYXRhLCB0aWVzID0gImVmcm9uIikNCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUyKSB7DQogICAgICBjYXQoIkVycm9yIGZpdHRpbmcgc2ltcGxpZmllZCBtb2RlbDoiLCBlMiRtZXNzYWdlLCAiXG4iKQ0KICAgICAgcmV0dXJuKE5VTEwpDQogICAgfSkNCiAgfSkNCiAgDQogICMgRml0IG5vLW1hY3JvIG1vZGVsIChpZiBhcHBsaWNhYmxlKQ0KICBjdl9iYW5rX25vX21hY3JvX21vZGVsIDwtIGlmKGhhc19tYWNyb19iYW5rKSB7DQogICAgdHJ5Q2F0Y2goew0KICAgICAgY294cGgobm9fbWFjcm9fYmFua19mb3JtdWxhLCBkYXRhID0gY3ZfdHJhaW5fZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgICBjYXQoIkVycm9yIGZpdHRpbmcgbm8tbWFjcm8gYmFua3J1cHRjeSBtb2RlbDoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9KQ0KICB9IGVsc2Ugew0KICAgIGN2X2JhbmtfbW9kZWwNCiAgfQ0KICANCiAgIyAtLS0tLSBBY3F1aXNpdGlvbiBNb2RlbCAtLS0tLQ0KICBjYXQoIlxuRml0dGluZyBhY3F1aXNpdGlvbiBtb2RlbHMgZm9yIGZvbGQiLCBmb2xkX2lkeCwgIi4uLlxuIikNCiAgDQogICMgRml0IGZ1bGwgbW9kZWwNCiAgY3ZfYWNxX21vZGVsIDwtIHRyeUNhdGNoKHsNCiAgICBjb3hwaChhY3FfZm9ybXVsYSwgZGF0YSA9IGN2X3RyYWluX2RhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICBjYXQoIkVycm9yIGZpdHRpbmcgZnVsbCBhY3F1aXNpdGlvbiBtb2RlbDoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgY2F0KCJBdHRlbXB0aW5nIHRvIGZpdCBzaW1wbGlmaWVkIG1vZGVsLi4uXG4iKQ0KICAgIA0KICAgICMgVHJ5IHRvIGZpdCBhIHNpbXBsZXIgbW9kZWwgd2l0aG91dCB0aW1lIGludGVyYWN0aW9ucw0KICAgIHNpbXBsZV9hY3FfZm9ybXVsYSA8LSB1cGRhdGUoYWNxX2Zvcm11bGEsIC4gfiAuIC0gLjouKQ0KICAgIA0KICAgIHRyeUNhdGNoKHsNCiAgICAgIGNveHBoKHNpbXBsZV9hY3FfZm9ybXVsYSwgZGF0YSA9IGN2X3RyYWluX2RhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZTIpIHsNCiAgICAgIGNhdCgiRXJyb3IgZml0dGluZyBzaW1wbGlmaWVkIG1vZGVsOiIsIGUyJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9KQ0KICB9KQ0KICANCiAgIyBGaXQgbm8tbWFjcm8gbW9kZWwgKGlmIGFwcGxpY2FibGUpDQogIGN2X2FjcV9ub19tYWNyb19tb2RlbCA8LSBpZihoYXNfbWFjcm9fYWNxKSB7DQogICAgdHJ5Q2F0Y2goew0KICAgICAgY294cGgobm9fbWFjcm9fYWNxX2Zvcm11bGEsIGRhdGEgPSBjdl90cmFpbl9kYXRhLCB0aWVzID0gImVmcm9uIikNCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgIGNhdCgiRXJyb3IgZml0dGluZyBuby1tYWNybyBhY3F1aXNpdGlvbiBtb2RlbDoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9KQ0KICB9IGVsc2Ugew0KICAgIGN2X2FjcV9tb2RlbA0KICB9DQogIA0KICAjIC0tLS0tIEV2YWx1YXRpb24gLS0tLS0NCiAgIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55IGluIHRlc3Qgc2V0DQogIHRlc3RfbGFzdF9vYnMgPC0gY3ZfdGVzdF9kYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICANCiAgIyBFdmFsdWF0ZSBiYW5rcnVwdGN5IG1vZGVsDQogIGlmICghaXMubnVsbChjdl9iYW5rX21vZGVsKSkgew0KICAgIGNhdCgiXG5FdmFsdWF0aW5nIGJhbmtydXB0Y3kgbW9kZWwuLi5cbiIpDQogICAgDQogICAgdHJ5Q2F0Y2goew0KICAgICAgIyBHZXQgZnVsbCBtb2RlbCBwcmVkaWN0aW9ucw0KICAgICAgYmFua19mdWxsX3Njb3JlcyA8LSBwcmVkaWN0KGN2X2JhbmtfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2xhc3Rfb2JzLCB0eXBlID0gInJpc2siKQ0KICAgICAgDQogICAgICAjIEdldCBuby1tYWNybyBtb2RlbCBwcmVkaWN0aW9ucyAoaWYgYXBwbGljYWJsZSkNCiAgICAgIGJhbmtfbm9fbWFjcm9fc2NvcmVzIDwtIGlmICghaXMubnVsbChjdl9iYW5rX25vX21hY3JvX21vZGVsKSkgew0KICAgICAgICBwcmVkaWN0KGN2X2Jhbmtfbm9fbWFjcm9fbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2xhc3Rfb2JzLCB0eXBlID0gInJpc2siKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgYmFua19mdWxsX3Njb3Jlcw0KICAgICAgfQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBBVUMNCiAgICAgIGZ1bGxfYmFua19hdWMgPC0gYXMubnVtZXJpYyhwUk9DOjphdWMocFJPQzo6cm9jKHRlc3RfbGFzdF9vYnMkYmFua3J1cHRjeSwgYmFua19mdWxsX3Njb3JlcywgcXVpZXQgPSBUUlVFKSkpDQogICAgICANCiAgICAgICMgR2V0IGNvbmNvcmRhbmNlIChIYXJyZWxsJ3MgQy1pbmRleCkgZm9yIGJhbmtydXB0Y3kgbW9kZWwNCiAgICAgIGlmICghaXMubnVsbChjdl9iYW5rX21vZGVsKSkgew0KICAgICAgICBmdWxsX2JhbmtfY29uY29yZGFuY2UgPC0gYXMubnVtZXJpYyhjdl9iYW5rX21vZGVsJGNvbmNvcmRhbmNlWyJjb25jb3JkYW5jZSJdKQ0KICAgICAgICBjYXQoIkJhbmtydXB0Y3kgbW9kZWwgY29uY29yZGFuY2UgKEMtaW5kZXgpOiIsIHJvdW5kKGZ1bGxfYmFua19jb25jb3JkYW5jZSwgNCksICJcbiIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBmdWxsX2JhbmtfY29uY29yZGFuY2UgPC0gTkENCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBDYWxjdWxhdGUgbm8tbWFjcm8gQVVDIGlmIGFwcGxpY2FibGUNCiAgICAgIG5vX21hY3JvX2JhbmtfYXVjIDwtIGlmIChoYXNfbWFjcm9fYmFuayAmJiAhaXMubnVsbChjdl9iYW5rX25vX21hY3JvX21vZGVsKSkgew0KICAgICAgICBhcy5udW1lcmljKHBST0M6OmF1YyhwUk9DOjpyb2ModGVzdF9sYXN0X29icyRiYW5rcnVwdGN5LCBiYW5rX25vX21hY3JvX3Njb3JlcywgcXVpZXQgPSBUUlVFKSkpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBmdWxsX2JhbmtfYXVjDQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIGltcHJvdmVtZW50DQogICAgICBiYW5rX2ltcHJvdmVtZW50IDwtIGZ1bGxfYmFua19hdWMgLSBub19tYWNyb19iYW5rX2F1Yw0KICAgICAgDQogICAgICAjIFN0YXRpc3RpY2FsIHRlc3QgaWYgYXBwbGljYWJsZQ0KICAgICAgYmFua19wX3ZhbHVlIDwtIGlmIChoYXNfbWFjcm9fYmFuayAmJiAhaXMubnVsbChjdl9iYW5rX25vX21hY3JvX21vZGVsKSkgew0KICAgICAgICB0ZXN0X3Jlc3VsdCA8LSBwUk9DOjpyb2MudGVzdCgNCiAgICAgICAgICBwUk9DOjpyb2ModGVzdF9sYXN0X29icyRiYW5rcnVwdGN5LCBiYW5rX2Z1bGxfc2NvcmVzLCBxdWlldCA9IFRSVUUpLA0KICAgICAgICAgIHBST0M6OnJvYyh0ZXN0X2xhc3Rfb2JzJGJhbmtydXB0Y3ksIGJhbmtfbm9fbWFjcm9fc2NvcmVzLCBxdWlldCA9IFRSVUUpLA0KICAgICAgICAgIG1ldGhvZCA9ICJkZWxvbmciDQogICAgICAgICkNCiAgICAgICAgdGVzdF9yZXN1bHQkcC52YWx1ZQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgTkENCiAgICAgIH0NCiAgICAgIA0KICAgICAgYmFua3J1cHRjeV9jdl9yZXN1bHRzJGF1Y193aXRoX21hY3JvW2ZvbGRfaWR4XSA8LSBmdWxsX2JhbmtfYXVjDQogICAgICBiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkYXVjX25vX21hY3JvW2ZvbGRfaWR4XSA8LSBub19tYWNyb19iYW5rX2F1Yw0KICAgICAgYmFua3J1cHRjeV9jdl9yZXN1bHRzJGltcHJvdmVtZW50W2ZvbGRfaWR4XSA8LSBiYW5rX2ltcHJvdmVtZW50DQogICAgICBiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkcF92YWx1ZVtmb2xkX2lkeF0gPC0gYmFua19wX3ZhbHVlDQogICAgICBiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkY29uY29yZGFuY2VbZm9sZF9pZHhdIDwtIGZ1bGxfYmFua19jb25jb3JkYW5jZQ0KICAgICAgDQogICAgICBjYXQoIkJhbmtydXB0Y3kgbW9kZWwgZm9sZCIsIGZvbGRfaWR4LCAiLSBBVUMgd2l0aCBtYWNybzoiLCByb3VuZChmdWxsX2JhbmtfYXVjLCA0KSwgDQogICAgICAgICAgIi0gQVVDIHdpdGhvdXQgbWFjcm86Iiwgcm91bmQobm9fbWFjcm9fYmFua19hdWMsIDQpLCANCiAgICAgICAgICAiLSBJbXByb3ZlbWVudDoiLCByb3VuZChiYW5rX2ltcHJvdmVtZW50LCA0KSwgDQogICAgICAgICAgIi0gQ29uY29yZGFuY2U6Iiwgcm91bmQoZnVsbF9iYW5rX2NvbmNvcmRhbmNlLCA0KSwgIlxuIikNCiAgICAgIA0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICAgY2F0KCJFcnJvciBldmFsdWF0aW5nIGJhbmtydXB0Y3kgbW9kZWw6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgIH0pDQogIH0NCiAgDQogICMgRXZhbHVhdGUgYWNxdWlzaXRpb24gbW9kZWwNCiAgaWYgKCFpcy5udWxsKGN2X2FjcV9tb2RlbCkpIHsNCiAgICBjYXQoIlxuRXZhbHVhdGluZyBhY3F1aXNpdGlvbiBtb2RlbC4uLlxuIikNCiAgICANCiAgICB0cnlDYXRjaCh7DQogICAgICAjIEdldCBmdWxsIG1vZGVsIHByZWRpY3Rpb25zDQogICAgICBhY3FfZnVsbF9zY29yZXMgPC0gcHJlZGljdChjdl9hY3FfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2xhc3Rfb2JzLCB0eXBlID0gInJpc2siKQ0KICAgICAgDQogICAgICAjIEdldCBuby1tYWNybyBtb2RlbCBwcmVkaWN0aW9ucyAoaWYgYXBwbGljYWJsZSkNCiAgICAgIGFjcV9ub19tYWNyb19zY29yZXMgPC0gaWYgKCFpcy5udWxsKGN2X2FjcV9ub19tYWNyb19tb2RlbCkpIHsNCiAgICAgICAgcHJlZGljdChjdl9hY3Ffbm9fbWFjcm9fbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2xhc3Rfb2JzLCB0eXBlID0gInJpc2siKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgYWNxX2Z1bGxfc2NvcmVzDQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIEFVQw0KICAgICAgZnVsbF9hY3FfYXVjIDwtIGFzLm51bWVyaWMocFJPQzo6YXVjKHBST0M6OnJvYyh0ZXN0X2xhc3Rfb2JzJGFjcXVpc2l0aW9uLCBhY3FfZnVsbF9zY29yZXMsIHF1aWV0ID0gVFJVRSkpKQ0KICAgICAgDQogICAgICAjIEdldCBjb25jb3JkYW5jZSAoSGFycmVsbCdzIEMtaW5kZXgpIGZvciBhY3F1aXNpdGlvbiBtb2RlbA0KICAgICAgaWYgKCFpcy5udWxsKGN2X2FjcV9tb2RlbCkpIHsNCiAgICAgICAgZnVsbF9hY3FfY29uY29yZGFuY2UgPC0gYXMubnVtZXJpYyhjdl9hY3FfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0pDQogICAgICAgIGNhdCgiQWNxdWlzaXRpb24gbW9kZWwgY29uY29yZGFuY2UgKEMtaW5kZXgpOiIsIHJvdW5kKGZ1bGxfYWNxX2NvbmNvcmRhbmNlLCA0KSwgIlxuIikNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGZ1bGxfYWNxX2NvbmNvcmRhbmNlIDwtIE5BDQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIG5vLW1hY3JvIEFVQyBpZiBhcHBsaWNhYmxlDQogICAgICBub19tYWNyb19hY3FfYXVjIDwtIGlmIChoYXNfbWFjcm9fYWNxICYmICFpcy5udWxsKGN2X2FjcV9ub19tYWNyb19tb2RlbCkpIHsNCiAgICAgICAgYXMubnVtZXJpYyhwUk9DOjphdWMocFJPQzo6cm9jKHRlc3RfbGFzdF9vYnMkYWNxdWlzaXRpb24sIGFjcV9ub19tYWNyb19zY29yZXMsIHF1aWV0ID0gVFJVRSkpKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgZnVsbF9hY3FfYXVjDQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIGltcHJvdmVtZW50DQogICAgICBhY3FfaW1wcm92ZW1lbnQgPC0gZnVsbF9hY3FfYXVjIC0gbm9fbWFjcm9fYWNxX2F1Yw0KICAgICAgDQogICAgICAjIFN0YXRpc3RpY2FsIHRlc3QgaWYgYXBwbGljYWJsZQ0KICAgICAgYWNxX3BfdmFsdWUgPC0gaWYgKGhhc19tYWNyb19hY3EgJiYgIWlzLm51bGwoY3ZfYWNxX25vX21hY3JvX21vZGVsKSkgew0KICAgICAgICB0ZXN0X3Jlc3VsdCA8LSBwUk9DOjpyb2MudGVzdCgNCiAgICAgICAgICBwUk9DOjpyb2ModGVzdF9sYXN0X29icyRhY3F1aXNpdGlvbiwgYWNxX2Z1bGxfc2NvcmVzLCBxdWlldCA9IFRSVUUpLA0KICAgICAgICAgIHBST0M6OnJvYyh0ZXN0X2xhc3Rfb2JzJGFjcXVpc2l0aW9uLCBhY3Ffbm9fbWFjcm9fc2NvcmVzLCBxdWlldCA9IFRSVUUpLA0KICAgICAgICAgIG1ldGhvZCA9ICJkZWxvbmciDQogICAgICAgICkNCiAgICAgICAgdGVzdF9yZXN1bHQkcC52YWx1ZQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgTkENCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBTdG9yZSByZXN1bHRzDQogICAgICBhY3F1aXNpdGlvbl9jdl9yZXN1bHRzJGF1Y193aXRoX21hY3JvW2ZvbGRfaWR4XSA8LSBmdWxsX2FjcV9hdWMNCiAgICAgIGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkYXVjX25vX21hY3JvW2ZvbGRfaWR4XSA8LSBub19tYWNyb19hY3FfYXVjDQogICAgICBhY3F1aXNpdGlvbl9jdl9yZXN1bHRzJGltcHJvdmVtZW50W2ZvbGRfaWR4XSA8LSBhY3FfaW1wcm92ZW1lbnQNCiAgICAgIGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkcF92YWx1ZVtmb2xkX2lkeF0gPC0gYWNxX3BfdmFsdWUNCiAgICAgIGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkY29uY29yZGFuY2VbZm9sZF9pZHhdIDwtIGZ1bGxfYWNxX2NvbmNvcmRhbmNlDQogICAgICANCiAgICAgIGNhdCgiQWNxdWlzaXRpb24gbW9kZWwgZm9sZCIsIGZvbGRfaWR4LCAiLSBBVUMgd2l0aCBtYWNybzoiLCByb3VuZChmdWxsX2FjcV9hdWMsIDQpLCANCiAgICAgICAgICAiLSBBVUMgd2l0aG91dCBtYWNybzoiLCByb3VuZChub19tYWNyb19hY3FfYXVjLCA0KSwgDQogICAgICAgICAgIi0gSW1wcm92ZW1lbnQ6Iiwgcm91bmQoYWNxX2ltcHJvdmVtZW50LCA0KSwNCiAgICAgICAgICAiLSBDb25jb3JkYW5jZToiLCByb3VuZChmdWxsX2FjcV9jb25jb3JkYW5jZSwgNCksICJcbiIpDQogICAgICANCiAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgIGNhdCgiRXJyb3IgZXZhbHVhdGluZyBhY3F1aXNpdGlvbiBtb2RlbDoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgfSkNCiAgfQ0KfQ0KDQpgYGANCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuMzogU3VtbWFyaXplIEstRm9sZCBDViBSZXN1bHRzDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjEzLjM6IFNVTU1BUklaRSBLLUZPTEQgQ1JPU1MtVkFMSURBVElPTiBSRVNVTFRTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBiYW5rcnVwdGN5IG1vZGVsDQpiYW5rcnVwdGN5X3N1bW1hcnkgPC0gZGF0YS5mcmFtZSgNCiAgbWV0cmljID0gYygiQVVDIHdpdGggbWFjcm8iLCAiQVVDIHdpdGhvdXQgbWFjcm8iLCAiSW1wcm92ZW1lbnQiLCAiQ29uY29yZGFuY2UiKSwNCiAgbWVhbiA9IGMoDQogICAgbWVhbihiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkYXVjX3dpdGhfbWFjcm8sIG5hLnJtID0gVFJVRSksDQogICAgbWVhbihiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkYXVjX25vX21hY3JvLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW4oYmFua3J1cHRjeV9jdl9yZXN1bHRzJGltcHJvdmVtZW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW4oYmFua3J1cHRjeV9jdl9yZXN1bHRzJGNvbmNvcmRhbmNlLCBuYS5ybSA9IFRSVUUpDQogICksDQogIHNkID0gYygNCiAgICBzZChiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkYXVjX3dpdGhfbWFjcm8sIG5hLnJtID0gVFJVRSksDQogICAgc2QoYmFua3J1cHRjeV9jdl9yZXN1bHRzJGF1Y19ub19tYWNybywgbmEucm0gPSBUUlVFKSwNCiAgICBzZChiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkaW1wcm92ZW1lbnQsIG5hLnJtID0gVFJVRSksDQogICAgc2QoYmFua3J1cHRjeV9jdl9yZXN1bHRzJGNvbmNvcmRhbmNlLCBuYS5ybSA9IFRSVUUpDQogICksDQogIG1pbiA9IGMoDQogICAgbWluKGJhbmtydXB0Y3lfY3ZfcmVzdWx0cyRhdWNfd2l0aF9tYWNybywgbmEucm0gPSBUUlVFKSwNCiAgICBtaW4oYmFua3J1cHRjeV9jdl9yZXN1bHRzJGF1Y19ub19tYWNybywgbmEucm0gPSBUUlVFKSwNCiAgICBtaW4oYmFua3J1cHRjeV9jdl9yZXN1bHRzJGltcHJvdmVtZW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1pbihiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkY29uY29yZGFuY2UsIG5hLnJtID0gVFJVRSkNCiAgKSwNCiAgbWF4ID0gYygNCiAgICBtYXgoYmFua3J1cHRjeV9jdl9yZXN1bHRzJGF1Y193aXRoX21hY3JvLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1heChiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkYXVjX25vX21hY3JvLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1heChiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkaW1wcm92ZW1lbnQsIG5hLnJtID0gVFJVRSksDQogICAgbWF4KGJhbmtydXB0Y3lfY3ZfcmVzdWx0cyRjb25jb3JkYW5jZSwgbmEucm0gPSBUUlVFKQ0KICApDQopDQoNCiMgQ2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYWNxdWlzaXRpb24gbW9kZWwNCmFjcXVpc2l0aW9uX3N1bW1hcnkgPC0gZGF0YS5mcmFtZSgNCiAgbWV0cmljID0gYygiQVVDIHdpdGggbWFjcm8iLCAiQVVDIHdpdGhvdXQgbWFjcm8iLCAiSW1wcm92ZW1lbnQiLCAiQ29uY29yZGFuY2UiKSwNCiAgbWVhbiA9IGMoDQogICAgbWVhbihhY3F1aXNpdGlvbl9jdl9yZXN1bHRzJGF1Y193aXRoX21hY3JvLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW4oYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfbm9fbWFjcm8sIG5hLnJtID0gVFJVRSksDQogICAgbWVhbihhY3F1aXNpdGlvbl9jdl9yZXN1bHRzJGltcHJvdmVtZW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW4oYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRjb25jb3JkYW5jZSwgbmEucm0gPSBUUlVFKQ0KICApLA0KICBzZCA9IGMoDQogICAgc2QoYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfd2l0aF9tYWNybywgbmEucm0gPSBUUlVFKSwNCiAgICBzZChhY3F1aXNpdGlvbl9jdl9yZXN1bHRzJGF1Y19ub19tYWNybywgbmEucm0gPSBUUlVFKSwNCiAgICBzZChhY3F1aXNpdGlvbl9jdl9yZXN1bHRzJGltcHJvdmVtZW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIHNkKGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkY29uY29yZGFuY2UsIG5hLnJtID0gVFJVRSkNCiAgKSwNCiAgbWluID0gYygNCiAgICBtaW4oYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfd2l0aF9tYWNybywgbmEucm0gPSBUUlVFKSwNCiAgICBtaW4oYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfbm9fbWFjcm8sIG5hLnJtID0gVFJVRSksDQogICAgbWluKGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkaW1wcm92ZW1lbnQsIG5hLnJtID0gVFJVRSksDQogICAgbWluKGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkY29uY29yZGFuY2UsIG5hLnJtID0gVFJVRSkNCiAgKSwNCiAgbWF4ID0gYygNCiAgICBtYXgoYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfd2l0aF9tYWNybywgbmEucm0gPSBUUlVFKSwNCiAgICBtYXgoYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfbm9fbWFjcm8sIG5hLnJtID0gVFJVRSksDQogICAgbWF4KGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkaW1wcm92ZW1lbnQsIG5hLnJtID0gVFJVRSksDQogICAgbWF4KGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkY29uY29yZGFuY2UsIG5hLnJtID0gVFJVRSkNCiAgKQ0KKQ0KDQojIFByaW50IHN1bW1hcnkgcmVzdWx0cw0KY2F0KCJcbkJhbmtydXB0Y3kgTW9kZWwgQ3Jvc3MtVmFsaWRhdGlvbiBTdW1tYXJ5OlxuIikNCnByaW50KGJhbmtydXB0Y3lfc3VtbWFyeSkNCg0KY2F0KCJcbkFjcXVpc2l0aW9uIE1vZGVsIENyb3NzLVZhbGlkYXRpb24gU3VtbWFyeTpcbiIpDQpwcmludChhY3F1aXNpdGlvbl9zdW1tYXJ5KQ0KDQojIENvdW50IHNpZ25pZmljYW50IGltcHJvdmVtZW50cyBmb3IgYmFua3J1cHRjeSBtb2RlbA0Kc2lnX2JhbmtfaW1wcm92ZW1lbnRzIDwtIHN1bShiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkcF92YWx1ZSA8IDAuMDUsIG5hLnJtID0gVFJVRSkNCnRvdGFsX2JhbmtfdGVzdHMgPC0gc3VtKCFpcy5uYShiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkcF92YWx1ZSkpDQpjYXQoIlxuU2lnbmlmaWNhbnQgbWFjcm8gdmFyaWFibGUgaW1wcm92ZW1lbnRzIGZvciBiYW5rcnVwdGN5IG1vZGVsOiIsIA0KICAgIHNpZ19iYW5rX2ltcHJvdmVtZW50cywgIm91dCBvZiIsIHRvdGFsX2JhbmtfdGVzdHMsICJmb2xkc1xuIikNCg0KIyBDb3VudCBzaWduaWZpY2FudCBpbXByb3ZlbWVudHMgZm9yIGFjcXVpc2l0aW9uIG1vZGVsDQpzaWdfYWNxX2ltcHJvdmVtZW50cyA8LSBzdW0oYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRwX3ZhbHVlIDwgMC4wNSwgbmEucm0gPSBUUlVFKQ0KdG90YWxfYWNxX3Rlc3RzIDwtIHN1bSghaXMubmEoYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRwX3ZhbHVlKSkNCmNhdCgiU2lnbmlmaWNhbnQgbWFjcm8gdmFyaWFibGUgaW1wcm92ZW1lbnRzIGZvciBhY3F1aXNpdGlvbiBtb2RlbDoiLCANCiAgICBzaWdfYWNxX2ltcHJvdmVtZW50cywgIm91dCBvZiIsIHRvdGFsX2FjcV90ZXN0cywgImZvbGRzXG4iKQ0KDQojIE92ZXJhbGwgc2lnbmlmaWNhbmNlIHRlc3QgKG9uZS1zYW1wbGUgdC10ZXN0IG9uIGltcHJvdmVtZW50cykNCmlmIChoYXNfbWFjcm9fYmFuaykgew0KICBiYW5rX3RfdGVzdCA8LSB0LnRlc3QoYmFua3J1cHRjeV9jdl9yZXN1bHRzJGltcHJvdmVtZW50KQ0KICBjYXQoIlxuT3ZlcmFsbCBzaWduaWZpY2FuY2UgdGVzdCBmb3IgYmFua3J1cHRjeSBtb2RlbCBtYWNybyBpbXByb3ZlbWVudHM6XG4iKQ0KICBjYXQoIk1lYW4gaW1wcm92ZW1lbnQ6Iiwgcm91bmQoYmFua190X3Rlc3QkZXN0aW1hdGUsIDQpLCAiXG4iKQ0KICBjYXQoIjk1JSBDSToiLCBwYXN0ZShyb3VuZChiYW5rX3RfdGVzdCRjb25mLmludCwgNCksIGNvbGxhcHNlPSIgdG8gIiksICJcbiIpDQogIGNhdCgicC12YWx1ZToiLCBmb3JtYXQucHZhbChiYW5rX3RfdGVzdCRwLnZhbHVlLCBkaWdpdHMgPSAzKSwgIlxuIikNCiAgDQogIGlmIChiYW5rX3RfdGVzdCRwLnZhbHVlIDwgMC4wNSkgew0KICAgIGNhdCgiQ29uY2x1c2lvbjogTWFjcm9lY29ub21pYyB2YXJpYWJsZXMgc2lnbmlmaWNhbnRseSBpbXByb3ZlIGJhbmtydXB0Y3kgcHJlZGljdGlvbiAocCA8IDAuMDUpXG4iKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiQ29uY2x1c2lvbjogTWFjcm8gaW1wcm92ZW1lbnRzIGZvciBiYW5rcnVwdGN5IHByZWRpY3Rpb24gYXJlIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50XG4iKQ0KICB9DQp9DQoNCmlmIChoYXNfbWFjcm9fYWNxKSB7DQogIGFjcV90X3Rlc3QgPC0gdC50ZXN0KGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkaW1wcm92ZW1lbnQpDQogIGNhdCgiXG5PdmVyYWxsIHNpZ25pZmljYW5jZSB0ZXN0IGZvciBhY3F1aXNpdGlvbiBtb2RlbCBtYWNybyBpbXByb3ZlbWVudHM6XG4iKQ0KICBjYXQoIk1lYW4gaW1wcm92ZW1lbnQ6Iiwgcm91bmQoYWNxX3RfdGVzdCRlc3RpbWF0ZSwgNCksICJcbiIpDQogIGNhdCgiOTUlIENJOiIsIHBhc3RlKHJvdW5kKGFjcV90X3Rlc3QkY29uZi5pbnQsIDQpLCBjb2xsYXBzZT0iIHRvICIpLCAiXG4iKQ0KICBjYXQoInAtdmFsdWU6IiwgZm9ybWF0LnB2YWwoYWNxX3RfdGVzdCRwLnZhbHVlLCBkaWdpdHMgPSAzKSwgIlxuIikNCiAgDQogIGlmIChhY3FfdF90ZXN0JHAudmFsdWUgPCAwLjA1KSB7DQogICAgY2F0KCJDb25jbHVzaW9uOiBNYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBzaWduaWZpY2FudGx5IGltcHJvdmUgYWNxdWlzaXRpb24gcHJlZGljdGlvbiAocCA8IDAuMDUpXG4iKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiQ29uY2x1c2lvbjogTWFjcm8gaW1wcm92ZW1lbnRzIGZvciBhY3F1aXNpdGlvbiBwcmVkaWN0aW9uIGFyZSBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudFxuIikNCiAgfQ0KfQ0KDQojIENyZWF0ZSBwbG90cyB0byB2aXN1YWxpemUgcmVzdWx0cw0KaWYgKHJlcXVpcmVOYW1lc3BhY2UoImdncGxvdDIiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgIyBQcmVwYXJlIGRhdGEgZm9yIHBsb3R0aW5nDQogIGJhbmtfcGxvdF9kYXRhIDwtIGJhbmtydXB0Y3lfY3ZfcmVzdWx0cyAlPiUNCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoYXVjX3dpdGhfbWFjcm8sIGF1Y19ub19tYWNybyksDQogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1vZGVsX3R5cGUiLA0KICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiYXVjIikgJT4lDQogICAgbXV0YXRlKG1vZGVsX3R5cGUgPSBmYWN0b3IobW9kZWxfdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJhdWNfd2l0aF9tYWNybyIsICJhdWNfbm9fbWFjcm8iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIldpdGggTWFjcm8iLCAiV2l0aG91dCBNYWNybyIpKSwNCiAgICAgICAgICAgZm9sZCA9IGFzLmZhY3Rvcihmb2xkKSkNCiAgDQogIGFjcV9wbG90X2RhdGEgPC0gYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyAlPiUNCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoYXVjX3dpdGhfbWFjcm8sIGF1Y19ub19tYWNybyksDQogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1vZGVsX3R5cGUiLA0KICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiYXVjIikgJT4lDQogICAgbXV0YXRlKG1vZGVsX3R5cGUgPSBmYWN0b3IobW9kZWxfdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJhdWNfd2l0aF9tYWNybyIsICJhdWNfbm9fbWFjcm8iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIldpdGggTWFjcm8iLCAiV2l0aG91dCBNYWNybyIpKSwNCiAgICAgICAgICAgZm9sZCA9IGFzLmZhY3Rvcihmb2xkKSkNCiAgDQogICMgQ3JlYXRlIGJhbmtydXB0Y3kgbW9kZWwgcGxvdA0KICBiYW5rX3Bsb3QgPC0gZ2dwbG90KGJhbmtfcGxvdF9kYXRhLCBhZXMoeCA9IGZvbGQsIHkgPSBhdWMsIGZpbGwgPSBtb2RlbF90eXBlKSkgKw0KICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKGJhbmtydXB0Y3lfY3ZfcmVzdWx0cyRhdWNfd2l0aF9tYWNybywgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJkYXJrcmVkIikgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4oYmFua3J1cHRjeV9jdl9yZXN1bHRzJGF1Y19ub19tYWNybywgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsNCiAgICBsYWJzKHRpdGxlID0gIkJhbmtydXB0Y3kgTW9kZWwgUGVyZm9ybWFuY2UgQWNyb3NzIEZvbGRzIiwNCiAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIkF2ZXJhZ2UgQVVDIFdpdGggTWFjcm86IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQobWVhbihiYW5rcnVwdGN5X2N2X3Jlc3VsdHMkYXVjX3dpdGhfbWFjcm8sIG5hLnJtID0gVFJVRSksIDQpKSwNCiAgICAgICAgIHggPSAiRm9sZCIsDQogICAgICAgICB5ID0gIkFVQyIsDQogICAgICAgICBmaWxsID0gIk1vZGVsIFR5cGUiKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJXaXRoIE1hY3JvIiA9ICJkYXJrcmVkIiwgIldpdGhvdXQgTWFjcm8iID0gImRhcmtibHVlIikpDQogIA0KICAjIENyZWF0ZSBhY3F1aXNpdGlvbiBtb2RlbCBwbG90DQogIGFjcV9wbG90IDwtIGdncGxvdChhY3FfcGxvdF9kYXRhLCBhZXMoeCA9IGZvbGQsIHkgPSBhdWMsIGZpbGwgPSBtb2RlbF90eXBlKSkgKw0KICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKGFjcXVpc2l0aW9uX2N2X3Jlc3VsdHMkYXVjX3dpdGhfbWFjcm8sIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4oYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfbm9fbWFjcm8sIG5hLnJtID0gVFJVRSksIA0KICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya29yYW5nZSIpICsNCiAgICBsYWJzKHRpdGxlID0gIkFjcXVpc2l0aW9uIE1vZGVsIFBlcmZvcm1hbmNlIEFjcm9zcyBGb2xkcyIsDQogICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJBdmVyYWdlIEFVQyBXaXRoIE1hY3JvOiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKG1lYW4oYWNxdWlzaXRpb25fY3ZfcmVzdWx0cyRhdWNfd2l0aF9tYWNybywgbmEucm0gPSBUUlVFKSwgNCkpLA0KICAgICAgICAgeCA9ICJGb2xkIiwNCiAgICAgICAgIHkgPSAiQVVDIiwNCiAgICAgICAgIGZpbGwgPSAiTW9kZWwgVHlwZSIpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIldpdGggTWFjcm8iID0gImRhcmtncmVlbiIsICJXaXRob3V0IE1hY3JvIiA9ICJkYXJrb3JhbmdlIikpDQogIA0KICBwcmludChiYW5rX3Bsb3QpDQogIHByaW50KGFjcV9wbG90KQ0KICANCiAgIyBTYXZlIHBsb3RzDQogIGdnc2F2ZSgiYmFua3J1cHRjeV9jdl9yZXN1bHRzLnBuZyIsIGJhbmtfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNikNCiAgZ2dzYXZlKCJhY3F1aXNpdGlvbl9jdl9yZXN1bHRzLnBuZyIsIGFjcV9wbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA2KQ0KfQ0KYGBgDQoNCk5leHQsIHdlIHVzZSBhIGRpZmZlcmVudCBhcHByb2FjaCB3aGVyZSB3ZSBzcGxpdCB0aGUgZGF0YXNldCBiYXNlZCBvbiB0aGUgSVBPIGRhdGUgb2YgdGhlIGNvbXBhbmllcy4gVGhpcyBtZXRob2QgYWxsb3dzIHVzIHRvIGFuYWx5emUgaG93IHRoZSBtb2RlbCBwZXJmb3JtcyBvdmVyIHRpbWUsIHdoaWNoIGNhbiBiZSBwYXJ0aWN1bGFybHkgdXNlZnVsIGluIHN1cnZpdmFsIGFuYWx5c2lzLiBXZSB3aWxsIGNyZWF0ZSBhIHRyYWluaW5nIHNldCB3aXRoIGNvbXBhbmllcyB0aGF0IHdlbnQgcHVibGljIGJlZm9yZSBhIGNlcnRhaW4gZGF0ZSBhbmQgYSB0ZXN0aW5nIHNldCB3aXRoIGNvbXBhbmllcyB0aGF0IHdlbnQgcHVibGljIGFmdGVyIHRoYXQgZGF0ZS4gVGhpcyBhcHByb2FjaCBoZWxwcyB1cyB1bmRlcnN0YW5kIGhvdyB3ZWxsIG91ciBtb2RlbCBnZW5lcmFsaXplcyB0byBuZXcgY29tcGFuaWVzIGVudGVyaW5nIHRoZSBtYXJrZXQuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMTQ6IFRpbWUtQmFzZWQgU3BsaXQgZm9yIFN1cnZpdmFsIEFuYWx5c2lzDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbj09PSBTVEVQIDE0OiBUSU1FLUJBU0VEIFNQTElUIEZPUiBTVVJWSVZBTCBBTkFMWVNJUyA9PT1cbiIpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDE0LjE6IElQTyBEYXRlLUJhc2VkIFNwbGl0dGluZw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xNC4xOiBJUE8gREFURS1CQVNFRCBTUExJVFRJTkdcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQojIEZ1bmN0aW9uIHRvIGdldCBjb21wYW55IElQTyBkYXRlcyAodXNpbmcgZmlyc3QgYXBwZWFyYW5jZSBkYXRlIGFzIHByb3h5KQ0KZ2V0X2NvbXBhbnlfaXBvX2RhdGVzIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgIyBGb3IgZWFjaCBjb21wYW55LCB1c2UgdGhlIGNvbXBhbnlfc3RhcnRfZGF0ZSBhbHJlYWR5IGNhbGN1bGF0ZWQNCiAgY29tcGFueV9pcG9fZGF0ZXMgPC0gZGF0YSAlPiUNCiAgICBncm91cF9ieShjdXNpcCkgJT4lDQogICAgc3VtbWFyaXplKA0KICAgICAgaXBvX2RhdGUgPSBtaW4oY29tcGFueV9zdGFydF9kYXRlLCBuYS5ybSA9IFRSVUUpDQogICAgKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogIHJldHVybihjb21wYW55X2lwb19kYXRlcykNCn0NCg0KIyBHZXQgSVBPIGRhdGVzDQpjb21wYW55X2lwb19kYXRlcyA8LSBnZXRfY29tcGFueV9pcG9fZGF0ZXMoZGF0YSkNCg0KIyBEaXNwbGF5IHJhbmdlIG9mIElQTyBkYXRlcw0KY2F0KCJJUE8gZGF0ZSByYW5nZSBpbiBkYXRhc2V0OlxuIikNCmRhdGVfcmFuZ2UgPC0gcmFuZ2UoY29tcGFueV9pcG9fZGF0ZXMkaXBvX2RhdGUsIG5hLnJtID0gVFJVRSkNCmNhdCgiRWFybGllc3QgSVBPIGRhdGU6IiwgYXMuY2hhcmFjdGVyKGRhdGVfcmFuZ2VbMV0pLCAiXG4iKQ0KY2F0KCJMYXRlc3QgSVBPIGRhdGU6IiwgYXMuY2hhcmFjdGVyKGRhdGVfcmFuZ2VbMl0pLCAiXG4iKQ0KDQojIERldGVybWluZSB0ZW1wb3JhbCBkaXN0cmlidXRpb24gb2YgSVBPcw0KaXBvX3llYXJzIDwtIGFzLm51bWVyaWMoZm9ybWF0KGNvbXBhbnlfaXBvX2RhdGVzJGlwb19kYXRlLCAiJVkiKSkNCmlwb195ZWFyX2NvdW50cyA8LSB0YWJsZShpcG9feWVhcnMpDQpjYXQoIlxuSVBPIGRpc3RyaWJ1dGlvbiBieSB5ZWFyOlxuIikNCnByaW50KGlwb195ZWFyX2NvdW50cykNCg0KIyBEZXRlcm1pbmUgc3BsaXR0aW5nIGFwcHJvYWNoDQpjYXQoIlxuQ2hvb3NpbmcgdGVtcG9yYWwgc3BsaXQgYXBwcm9hY2guLi5cbiIpDQoNCiMgT3B0aW9uIDE6IFNwbGl0IGF0IHNwZWNpZmljIHllYXIgKGUuZy4sIDIwMTApDQpzcGxpdF95ZWFyIDwtIDIwMTANCmVhcmx5X2NvbXBhbmllcyA8LSBjb21wYW55X2lwb19kYXRlcyRjdXNpcFthcy5udW1lcmljKGZvcm1hdChjb21wYW55X2lwb19kYXRlcyRpcG9fZGF0ZSwgIiVZIikpIDwgc3BsaXRfeWVhcl0NCmxhdGVfY29tcGFuaWVzIDwtIGNvbXBhbnlfaXBvX2RhdGVzJGN1c2lwW2FzLm51bWVyaWMoZm9ybWF0KGNvbXBhbnlfaXBvX2RhdGVzJGlwb19kYXRlLCAiJVkiKSkgPj0gc3BsaXRfeWVhcl0NCg0KY2F0KCJPcHRpb24gMTogU3BsaXQgYXQgeWVhciIsIHNwbGl0X3llYXIsICJcbiIpDQpjYXQoIlRyYWluaW5nIHNldCAoYmVmb3JlIiwgc3BsaXRfeWVhciwgIik6IiwgbGVuZ3RoKGVhcmx5X2NvbXBhbmllcyksICJjb21wYW5pZXNcbiIpDQpjYXQoIlRlc3Rpbmcgc2V0ICgiLCBzcGxpdF95ZWFyLCAiYW5kIGFmdGVyKToiLCBsZW5ndGgobGF0ZV9jb21wYW5pZXMpLCAiY29tcGFuaWVzXG4iKQ0KDQojIE9wdGlvbiAyOiBTcGxpdCBieSBwZXJjZW50aWxlIChlLmcuLCBmaXJzdCA3MCUgb2YgSVBPcyBjaHJvbm9sb2dpY2FsbHkpDQppcG9fc29ydGVkIDwtIGNvbXBhbnlfaXBvX2RhdGVzW29yZGVyKGNvbXBhbnlfaXBvX2RhdGVzJGlwb19kYXRlKSwgXQ0KdHJhaW5fc2l6ZV9wY3QgPC0gMC43DQp0cmFpbl9jdXRvZmZfaWR4IDwtIHJvdW5kKG5yb3coaXBvX3NvcnRlZCkgKiB0cmFpbl9zaXplX3BjdCkNCnRyYWluX2N1dG9mZl9kYXRlIDwtIGlwb19zb3J0ZWQkaXBvX2RhdGVbdHJhaW5fY3V0b2ZmX2lkeF0NCg0KY2hyb25vbG9naWNhbF9lYXJseSA8LSBpcG9fc29ydGVkJGN1c2lwWzE6dHJhaW5fY3V0b2ZmX2lkeF0NCmNocm9ub2xvZ2ljYWxfbGF0ZSA8LSBpcG9fc29ydGVkJGN1c2lwWyh0cmFpbl9jdXRvZmZfaWR4KzEpOm5yb3coaXBvX3NvcnRlZCldDQoNCmNhdCgiXG5PcHRpb24gMjogU3BsaXQgYXQiLCB0cmFpbl9zaXplX3BjdCoxMDAsICJwZXJjZW50aWxlIG9mIElQTyBkYXRlc1xuIikNCmNhdCgiVHJhaW5pbmcgc2V0IChJUE8gYmVmb3JlIiwgYXMuY2hhcmFjdGVyKHRyYWluX2N1dG9mZl9kYXRlKSwgIik6IiwgDQogICAgbGVuZ3RoKGNocm9ub2xvZ2ljYWxfZWFybHkpLCAiY29tcGFuaWVzXG4iKQ0KY2F0KCJUZXN0aW5nIHNldCAoSVBPIG9uIG9yIGFmdGVyIiwgYXMuY2hhcmFjdGVyKHRyYWluX2N1dG9mZl9kYXRlKSwgIik6IiwgDQogICAgbGVuZ3RoKGNocm9ub2xvZ2ljYWxfbGF0ZSksICJjb21wYW5pZXNcbiIpDQoNCiMgQ2hvb3NlIHRoZSBzcGxpdHRpbmcgYXBwcm9hY2ggKGNvdWxkIHByb21wdCB1c2VyIGhlcmUpDQojIEZvciBub3csIGxldCdzIHVzZSBPcHRpb24gMSAoc3BlY2lmaWMgeWVhcikNCnRyYWluX2NvbXBhbmllcyA8LSBlYXJseV9jb21wYW5pZXMNCnRlc3RfY29tcGFuaWVzIDwtIGxhdGVfY29tcGFuaWVzDQpzcGxpdF9tZXRob2QgPC0gcGFzdGUoIklQTyB5ZWFyIDwiLCBzcGxpdF95ZWFyLCAidnMgPj0iLCBzcGxpdF95ZWFyKQ0KDQpjYXQoIlxuVXNpbmcgT3B0aW9uIDE6IFNwbGl0IGF0IHNwZWNpZmljIHllYXIgKCIsIHNwbGl0X3llYXIsICIpXG4iKQ0KDQojIENyZWF0ZSB0aGUgdGltZS1zcGxpdCBkYXRhc2V0cw0KdGltZV90cmFpbl9kYXRhIDwtIGRhdGFbZGF0YSRjdXNpcCAlaW4lIHRyYWluX2NvbXBhbmllcywgXQ0KdGltZV90ZXN0X2RhdGEgPC0gZGF0YVtkYXRhJGN1c2lwICVpbiUgdGVzdF9jb21wYW5pZXMsIF0NCg0KY2F0KCJUcmFpbmluZyBzZXQ6IiwgbGVuZ3RoKHRyYWluX2NvbXBhbmllcyksICJjb21wYW5pZXMgKCIsIG5yb3codGltZV90cmFpbl9kYXRhKSwgIm9ic2VydmF0aW9ucylcbiIpDQpjYXQoIlRlc3Rpbmcgc2V0OiIsIGxlbmd0aCh0ZXN0X2NvbXBhbmllcyksICJjb21wYW5pZXMgKCIsIG5yb3codGltZV90ZXN0X2RhdGEpLCAib2JzZXJ2YXRpb25zKVxuIikNCg0KIyBDaGVjayBldmVudCBkaXN0cmlidXRpb24gaW4gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cw0KYW5hbHl6ZV90aW1lX2V2ZW50cyA8LSBmdW5jdGlvbihkYXRhX3N1YnNldCwgbGFiZWwpIHsNCiAgIyBHZXQgZmluYWwgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY29tcGFueQ0KICBmaW5hbF9vYnMgPC0gZGF0YV9zdWJzZXQgJT4lDQogICAgZ3JvdXBfYnkoY3VzaXApICU+JQ0KICAgIHNsaWNlX21heCh0c3RvcCkgJT4lDQogICAgdW5ncm91cCgpDQogIA0KICAjIENvdW50IGV2ZW50IHR5cGVzDQogIG5fYmFua3J1cHRjeSA8LSBzdW0oZmluYWxfb2JzJGJhbmtydXB0Y3kpDQogIG5fYWNxdWlzaXRpb24gPC0gc3VtKGZpbmFsX29icyRhY3F1aXNpdGlvbikNCiAgbl9jZW5zb3JlZCA8LSBzdW0oZmluYWxfb2JzJGJhbmtydXB0Y3kgPT0gMCAmIGZpbmFsX29icyRhY3F1aXNpdGlvbiA9PSAwKQ0KICBuX3RvdGFsIDwtIG5yb3coZmluYWxfb2JzKQ0KICANCiAgY2F0KCJcbkV2ZW50IGRpc3RyaWJ1dGlvbiBpbiIsIGxhYmVsLCAic2V0OlxuIikNCiAgY2F0KCItIEJhbmtydXB0Y2llczoiLCBuX2JhbmtydXB0Y3ksIHNwcmludGYoIiglLjFmJSUpIiwgMTAwKm5fYmFua3J1cHRjeS9uX3RvdGFsKSwgIlxuIikNCiAgY2F0KCItIEFjcXVpc2l0aW9uczoiLCBuX2FjcXVpc2l0aW9uLCBzcHJpbnRmKCIoJS4xZiUlKSIsIDEwMCpuX2FjcXVpc2l0aW9uL25fdG90YWwpLCAiXG4iKQ0KICBjYXQoIi0gQ2Vuc29yZWQ6Iiwgbl9jZW5zb3JlZCwgc3ByaW50ZigiKCUuMWYlJSkiLCAxMDAqbl9jZW5zb3JlZC9uX3RvdGFsKSwgIlxuIikNCiAgDQogIHJldHVybihkYXRhLmZyYW1lKA0KICAgIGJhbmtydXB0Y2llcyA9IG5fYmFua3J1cHRjeSwNCiAgICBhY3F1aXNpdGlvbnMgPSBuX2FjcXVpc2l0aW9uLA0KICAgIGNlbnNvcmVkID0gbl9jZW5zb3JlZCwNCiAgICB0b3RhbCA9IG5fdG90YWwNCiAgKSkNCn0NCg0KdGltZV90cmFpbl9ldmVudHMgPC0gYW5hbHl6ZV90aW1lX2V2ZW50cyh0aW1lX3RyYWluX2RhdGEsICJ0cmFpbmluZyAoZWFybHkgSVBPcykiKQ0KdGltZV90ZXN0X2V2ZW50cyA8LSBhbmFseXplX3RpbWVfZXZlbnRzKHRpbWVfdGVzdF9kYXRhLCAidGVzdGluZyAobGF0ZSBJUE9zKSIpDQoNCiMgQ2hlY2sgZGlzdHJpYnV0aW9uIG9mIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGFjcm9zcyBzZXRzDQpjaGVja19tYWNyb190aW1lX2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbih0cmFpbl9kYXRhLCB0ZXN0X2RhdGEpIHsNCiAgbWFjcm9fdmFycyA8LSBjKCJnZHBfZ3Jvd3RoIiwgImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IikNCiAgYXZhaWxhYmxlX21hY3JvIDwtIGludGVyc2VjdChtYWNyb192YXJzLCBjb2xuYW1lcyh0cmFpbl9kYXRhKSkNCiAgDQogIGlmKGxlbmd0aChhdmFpbGFibGVfbWFjcm8pID4gMCkgew0KICAgIGNhdCgiXG5EaXN0cmlidXRpb24gb2YgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgYWNyb3NzIHRpbWUtYmFzZWQgc2V0czpcbiIpDQogICAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgICANCiAgICBmb3IodmFyIGluIGF2YWlsYWJsZV9tYWNybykgew0KICAgICAgdHJhaW5fbWVhbiA8LSBtZWFuKHRyYWluX2RhdGFbW3Zhcl1dLCBuYS5ybSA9IFRSVUUpDQogICAgICB0cmFpbl9zZCA8LSBzZCh0cmFpbl9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgdHJhaW5fbWluIDwtIG1pbih0cmFpbl9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgdHJhaW5fbWF4IDwtIG1heCh0cmFpbl9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgDQogICAgICB0ZXN0X21lYW4gPC0gbWVhbih0ZXN0X2RhdGFbW3Zhcl1dLCBuYS5ybSA9IFRSVUUpDQogICAgICB0ZXN0X3NkIDwtIHNkKHRlc3RfZGF0YVtbdmFyXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgIHRlc3RfbWluIDwtIG1pbih0ZXN0X2RhdGFbW3Zhcl1dLCBuYS5ybSA9IFRSVUUpDQogICAgICB0ZXN0X21heCA8LSBtYXgodGVzdF9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgDQogICAgICBjYXQoIlxuVmFyaWFibGU6IiwgdmFyLCAiXG4iKQ0KICAgICAgY2F0KCJFYXJseSBJUE8gc2V0OiBNZWFuID0iLCByb3VuZCh0cmFpbl9tZWFuLCAyKSwgIlNEID0iLCByb3VuZCh0cmFpbl9zZCwgMiksIA0KICAgICAgICAgICJSYW5nZSA9Iiwgcm91bmQodHJhaW5fbWluLCAyKSwgInRvIiwgcm91bmQodHJhaW5fbWF4LCAyKSwgIlxuIikNCiAgICAgIGNhdCgiTGF0ZSBJUE8gc2V0OiAgTWVhbiA9Iiwgcm91bmQodGVzdF9tZWFuLCAyKSwgIlNEID0iLCByb3VuZCh0ZXN0X3NkLCAyKSwgDQogICAgICAgICAgIlJhbmdlID0iLCByb3VuZCh0ZXN0X21pbiwgMiksICJ0byIsIHJvdW5kKHRlc3RfbWF4LCAyKSwgIlxuIikNCiAgICAgIA0KICAgICAgIyBDYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgaW4gbWVhbnMgYXMgcGVyY2VudGFnZSBvZiB0cmFpbmluZyBTRA0KICAgICAgbWVhbl9kaWZmIDwtIGFicyh0cmFpbl9tZWFuIC0gdGVzdF9tZWFuKQ0KICAgICAgcGN0X2RpZmYgPC0gbWVhbl9kaWZmIC8gdHJhaW5fc2QgKiAxMDANCiAgICAgIA0KICAgICAgY2F0KCJEaWZmZXJlbmNlOiAgICIsIHJvdW5kKG1lYW5fZGlmZiwgMiksICIoIiwgcm91bmQocGN0X2RpZmYsIDEpLCANCiAgICAgICAgICAiJSBvZiBlYXJseSBJUE8gU0QpXG4iKQ0KICAgICAgDQogICAgICBpZihwY3RfZGlmZiA+IDI1KSB7DQogICAgICAgIGNhdCgiV0FSTklORzogU3Vic3RhbnRpYWwgZGlmZmVyZW5jZSBpbiBkaXN0cmlidXRpb24gb2YiLCB2YXIsICJiZXR3ZWVuIHRpbWUgcGVyaW9kc1xuIikNCiAgICAgICAgY2F0KCIgICAgICAgICBUaGlzIG1heSByZWZsZWN0IGdlbnVpbmUgZWNvbm9taWMgY2hhbmdlcyBiZXR3ZWVuIHRoZSBwZXJpb2RzXG4iKQ0KICAgICAgfQ0KICAgIH0NCiAgfSBlbHNlIHsNCiAgICBjYXQoIlxuTm8gbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgZm91bmQgaW4gdGhlIGRhdGFzZXQuXG4iKQ0KICB9DQp9DQoNCmNoZWNrX21hY3JvX3RpbWVfZGlzdHJpYnV0aW9uKHRpbWVfdHJhaW5fZGF0YSwgdGltZV90ZXN0X2RhdGEpDQoNCiMgRW5zdXJlIHRoZSB0aW1lIHZhcmlhYmxlIGV4aXN0cyBpbiBib3RoIHNldHMNCmlmKCEidHQiICVpbiUgY29sbmFtZXModGltZV90cmFpbl9kYXRhKSkgdGltZV90cmFpbl9kYXRhJHR0IDwtIHRpbWVfdHJhaW5fZGF0YSR0c3RvcA0KaWYoISJ0dCIgJWluJSBjb2xuYW1lcyh0aW1lX3Rlc3RfZGF0YSkpIHRpbWVfdGVzdF9kYXRhJHR0IDwtIHRpbWVfdGVzdF9kYXRhJHRzdG9wDQpgYGANCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTQuMjogVElNRS1CQVNFRCBTUExJVCBNT0RFTCBUUkFJTklORw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xNC4yOiBUSU1FLUJBU0VEIFNQTElUIE1PREVMIFRSQUlOSU5HXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBHZXQgZm9ybXVsYSBmcm9tIHRoZSBmaW5hbCBtb2RlbHMNCmJhbmtfZm9ybXVsYSA8LSBmb3JtdWxhKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpDQphY3FfZm9ybXVsYSA8LSBmb3JtdWxhKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsKQ0KDQojIENyZWF0ZSB2ZXJzaW9ucyB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcw0Kbm9fbWFjcm9fYmFua19mb3JtdWxhIDwtIGNyZWF0ZV9ub19tYWNyb19mb3JtdWxhKGJhbmtfZm9ybXVsYSkNCm5vX21hY3JvX2FjcV9mb3JtdWxhIDwtIGNyZWF0ZV9ub19tYWNyb19mb3JtdWxhKGFjcV9mb3JtdWxhKQ0KDQojIENoZWNrIGlmIGZvcm11bGFzIGFyZSBkaWZmZXJlbnQNCmhhc19tYWNyb19iYW5rIDwtICFpZGVudGljYWwoYmFua19mb3JtdWxhLCBub19tYWNyb19iYW5rX2Zvcm11bGEpDQpoYXNfbWFjcm9fYWNxIDwtICFpZGVudGljYWwoYWNxX2Zvcm11bGEsIG5vX21hY3JvX2FjcV9mb3JtdWxhKQ0KDQojIEZvciBiYW5rcnVwdGN5IG1vZGVsDQpjYXQoIlRyYWluaW5nIGJhbmtydXB0Y3kgbW9kZWxzIG9uIGVhcmx5IElQTyBjb21wYW5pZXMuLi5cbiIpDQoNCiMgRml0IGZ1bGwgbW9kZWwgd2l0aCBtYWNybyB2YXJpYWJsZXMNCnRpbWVfYmFua19tb2RlbCA8LSB0cnlDYXRjaCh7DQogIGNveHBoKGJhbmtfZm9ybXVsYSwgZGF0YSA9IHRpbWVfdHJhaW5fZGF0YSwgdGllcyA9ICJlZnJvbiIpDQp9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgY2F0KCJFcnJvciBmaXR0aW5nIGZ1bGwgYmFua3J1cHRjeSBtb2RlbCBvbiBlYXJseSBJUE8gZGF0YToiLCBlJG1lc3NhZ2UsICJcbiIpDQogIGNhdCgiQXR0ZW1wdGluZyBzaW1wbGlmaWVkIG1vZGVsIHdpdGhvdXQgdGltZSBpbnRlcmFjdGlvbnMuLi5cbiIpDQogIA0KICAjIFRyeSB3aXRob3V0IHRpbWUgaW50ZXJhY3Rpb25zDQogIHNpbXBsZV9iYW5rX2Zvcm11bGEgPC0gdXBkYXRlKGJhbmtfZm9ybXVsYSwgLiB+IC4gLSAuOi4pDQogIA0KICB0cnlDYXRjaCh7DQogICAgY294cGgoc2ltcGxlX2JhbmtfZm9ybXVsYSwgZGF0YSA9IHRpbWVfdHJhaW5fZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZTIpIHsNCiAgICBjYXQoIkVycm9yIGZpdHRpbmcgc2ltcGxpZmllZCBtb2RlbDoiLCBlMiRtZXNzYWdlLCAiXG4iKQ0KICAgIHJldHVybihOVUxMKQ0KICB9KQ0KfSkNCg0KIyBGaXQgdmVyc2lvbiB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcyAoaWYgYXBwbGljYWJsZSkNCmlmIChoYXNfbWFjcm9fYmFuaykgew0KICB0aW1lX2Jhbmtfbm9fbWFjcm9fbW9kZWwgPC0gdHJ5Q2F0Y2goew0KICAgIGNveHBoKG5vX21hY3JvX2JhbmtfZm9ybXVsYSwgZGF0YSA9IHRpbWVfdHJhaW5fZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgIGNhdCgiRXJyb3IgZml0dGluZyBuby1tYWNybyBiYW5rcnVwdGN5IG1vZGVsOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICANCiAgICAjIFRyeSB3aXRob3V0IHRpbWUgaW50ZXJhY3Rpb25zDQogICAgc2ltcGxlX25vX21hY3JvX2Zvcm11bGEgPC0gdXBkYXRlKG5vX21hY3JvX2JhbmtfZm9ybXVsYSwgLiB+IC4gLSAuOi4pDQogICAgDQogICAgdHJ5Q2F0Y2goew0KICAgICAgY294cGgoc2ltcGxlX25vX21hY3JvX2Zvcm11bGEsIGRhdGEgPSB0aW1lX3RyYWluX2RhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZTIpIHsNCiAgICAgIGNhdCgiRXJyb3IgZml0dGluZyBzaW1wbGlmaWVkIG5vLW1hY3JvIG1vZGVsOiIsIGUyJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9KQ0KICB9KQ0KfSBlbHNlIHsNCiAgdGltZV9iYW5rX25vX21hY3JvX21vZGVsIDwtIHRpbWVfYmFua19tb2RlbA0KICBjYXQoIkJhbmtydXB0Y3kgbW9kZWwgZG9lcyBub3QgY29udGFpbiBtYWNybyB2YXJpYWJsZXMuXG4iKQ0KfQ0KDQojIEZvciBhY3F1aXNpdGlvbiBtb2RlbA0KY2F0KCJcblRyYWluaW5nIGFjcXVpc2l0aW9uIG1vZGVscyBvbiBlYXJseSBJUE8gY29tcGFuaWVzLi4uXG4iKQ0KDQojIEZpdCBmdWxsIG1vZGVsIHdpdGggbWFjcm8gdmFyaWFibGVzDQp0aW1lX2FjcV9tb2RlbCA8LSB0cnlDYXRjaCh7DQogIGNveHBoKGFjcV9mb3JtdWxhLCBkYXRhID0gdGltZV90cmFpbl9kYXRhLCB0aWVzID0gImVmcm9uIikNCn0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICBjYXQoIkVycm9yIGZpdHRpbmcgZnVsbCBhY3F1aXNpdGlvbiBtb2RlbCBvbiBlYXJseSBJUE8gZGF0YToiLCBlJG1lc3NhZ2UsICJcbiIpDQogIGNhdCgiQXR0ZW1wdGluZyBzaW1wbGlmaWVkIG1vZGVsIHdpdGhvdXQgdGltZSBpbnRlcmFjdGlvbnMuLi5cbiIpDQogIA0KICAjIFRyeSB3aXRob3V0IHRpbWUgaW50ZXJhY3Rpb25zDQogIHNpbXBsZV9hY3FfZm9ybXVsYSA8LSB1cGRhdGUoYWNxX2Zvcm11bGEsIC4gfiAuIC0gLjouKQ0KICANCiAgdHJ5Q2F0Y2goew0KICAgIGNveHBoKHNpbXBsZV9hY3FfZm9ybXVsYSwgZGF0YSA9IHRpbWVfdHJhaW5fZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZTIpIHsNCiAgICBjYXQoIkVycm9yIGZpdHRpbmcgc2ltcGxpZmllZCBtb2RlbDoiLCBlMiRtZXNzYWdlLCAiXG4iKQ0KICAgIHJldHVybihOVUxMKQ0KICB9KQ0KfSkNCg0KIyBGaXQgdmVyc2lvbiB3aXRob3V0IG1hY3JvIHZhcmlhYmxlcyAoaWYgYXBwbGljYWJsZSkNCmlmIChoYXNfbWFjcm9fYWNxKSB7DQogIHRpbWVfYWNxX25vX21hY3JvX21vZGVsIDwtIHRyeUNhdGNoKHsNCiAgICBjb3hwaChub19tYWNyb19hY3FfZm9ybXVsYSwgZGF0YSA9IHRpbWVfdHJhaW5fZGF0YSwgdGllcyA9ICJlZnJvbiIpDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgIGNhdCgiRXJyb3IgZml0dGluZyBuby1tYWNybyBhY3F1aXNpdGlvbiBtb2RlbDoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgDQogICAgIyBUcnkgd2l0aG91dCB0aW1lIGludGVyYWN0aW9ucw0KICAgIHNpbXBsZV9ub19tYWNyb19mb3JtdWxhIDwtIHVwZGF0ZShub19tYWNyb19hY3FfZm9ybXVsYSwgLiB+IC4gLSAuOi4pDQogICAgDQogICAgdHJ5Q2F0Y2goew0KICAgICAgY294cGgoc2ltcGxlX25vX21hY3JvX2Zvcm11bGEsIGRhdGEgPSB0aW1lX3RyYWluX2RhdGEsIHRpZXMgPSAiZWZyb24iKQ0KICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZTIpIHsNCiAgICAgIGNhdCgiRXJyb3IgZml0dGluZyBzaW1wbGlmaWVkIG5vLW1hY3JvIG1vZGVsOiIsIGUyJG1lc3NhZ2UsICJcbiIpDQogICAgICByZXR1cm4oTlVMTCkNCiAgICB9KQ0KICB9KQ0KfSBlbHNlIHsNCiAgdGltZV9hY3Ffbm9fbWFjcm9fbW9kZWwgPC0gdGltZV9hY3FfbW9kZWwNCiAgY2F0KCJBY3F1aXNpdGlvbiBtb2RlbCBkb2VzIG5vdCBjb250YWluIG1hY3JvIHZhcmlhYmxlcy5cbiIpDQp9DQoNCiMgQ2hlY2sgaWYgbW9kZWxzIHdlcmUgc3VjY2Vzc2Z1bGx5IGZpdA0KY2F0KCJcbk1vZGVscyBzdWNjZXNzZnVsbHkgdHJhaW5lZCBvbiBlYXJseSBJUE8gZGF0YTpcbiIpDQppZiAoIWlzLm51bGwodGltZV9iYW5rX21vZGVsKSkgew0KICBjYXQoIi0gQmFua3J1cHRjeSBtb2RlbCAoZnVsbCk6IFllc1xuIikNCiAgY2F0KCIgIE51bWJlciBvZiBjb2VmZmljaWVudHM6IiwgbGVuZ3RoKGNvZWYodGltZV9iYW5rX21vZGVsKSksICJcbiIpDQp9IGVsc2Ugew0KICBjYXQoIi0gQmFua3J1cHRjeSBtb2RlbCAoZnVsbCk6IEZBSUxFRFxuIikNCn0NCg0KaWYgKGhhc19tYWNyb19iYW5rICYmICFpcy5udWxsKHRpbWVfYmFua19ub19tYWNyb19tb2RlbCkpIHsNCiAgY2F0KCItIEJhbmtydXB0Y3kgbW9kZWwgKG5vIG1hY3JvKTogWWVzXG4iKQ0KICBjYXQoIiAgTnVtYmVyIG9mIGNvZWZmaWNpZW50czoiLCBsZW5ndGgoY29lZih0aW1lX2Jhbmtfbm9fbWFjcm9fbW9kZWwpKSwgIlxuIikNCn0gZWxzZSBpZiAoaGFzX21hY3JvX2JhbmspIHsNCiAgY2F0KCItIEJhbmtydXB0Y3kgbW9kZWwgKG5vIG1hY3JvKTogRkFJTEVEXG4iKQ0KfQ0KDQppZiAoIWlzLm51bGwodGltZV9hY3FfbW9kZWwpKSB7DQogIGNhdCgiLSBBY3F1aXNpdGlvbiBtb2RlbCAoZnVsbCk6IFllc1xuIikNCiAgY2F0KCIgIE51bWJlciBvZiBjb2VmZmljaWVudHM6IiwgbGVuZ3RoKGNvZWYodGltZV9hY3FfbW9kZWwpKSwgIlxuIikNCn0gZWxzZSB7DQogIGNhdCgiLSBBY3F1aXNpdGlvbiBtb2RlbCAoZnVsbCk6IEZBSUxFRFxuIikNCn0NCg0KaWYgKGhhc19tYWNyb19hY3EgJiYgIWlzLm51bGwodGltZV9hY3Ffbm9fbWFjcm9fbW9kZWwpKSB7DQogIGNhdCgiLSBBY3F1aXNpdGlvbiBtb2RlbCAobm8gbWFjcm8pOiBZZXNcbiIpDQogIGNhdCgiICBOdW1iZXIgb2YgY29lZmZpY2llbnRzOiIsIGxlbmd0aChjb2VmKHRpbWVfYWNxX25vX21hY3JvX21vZGVsKSksICJcbiIpDQp9IGVsc2UgaWYgKGhhc19tYWNyb19hY3EpIHsNCiAgY2F0KCItIEFjcXVpc2l0aW9uIG1vZGVsIChubyBtYWNybyk6IEZBSUxFRFxuIikNCn0NCmBgYA0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxNC4zOiBUaW1lLUJhc2VkIFNwbGl0IE1vZGVsIEV2YWx1YXRpb24NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTQuMzogVElNRS1CQVNFRCBTUExJVCBNT0RFTCBFVkFMVUFUSU9OXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBGdW5jdGlvbiB0byBldmFsdWF0ZSBtb2RlbHMgb24gdGhlIGxhdGUgSVBPIHRlc3Qgc2V0DQpldmFsdWF0ZV90aW1lX3NwbGl0X3BlcmZvcm1hbmNlIDwtIGZ1bmN0aW9uKGZ1bGxfbW9kZWwsIG5vX21hY3JvX21vZGVsLCB0ZXN0X2RhdGEsIGV2ZW50X3R5cGUsIG1vZGVsX25hbWUpIHsNCiAgY2F0KCJcbkV2YWx1YXRpbmciLCBtb2RlbF9uYW1lLCAibW9kZWwgb24gbGF0ZSBJUE8gY29tcGFuaWVzLi4uXG4iKQ0KICANCiAgIyBTa2lwIGlmIG1vZGVsIGlzIE5VTEwNCiAgaWYgKGlzLm51bGwoZnVsbF9tb2RlbCkpIHsNCiAgICBjYXQoIkZ1bGwgbW9kZWwgaXMgTlVMTCwgc2tpcHBpbmcgZXZhbHVhdGlvbi5cbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0NCiAgDQogICMgR2V0IGxhc3Qgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY29tcGFueSBpbiB0ZXN0IHNldA0KICB0ZXN0X2xhc3Rfb2JzIDwtIHRlc3RfZGF0YSAlPiUNCiAgICBncm91cF9ieShjdXNpcCkgJT4lDQogICAgc2xpY2VfbWF4KHRzdG9wKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgQ2FsY3VsYXRlIHJpc2sgc2NvcmVzIGZyb20gZnVsbCBtb2RlbA0KICB0cnlDYXRjaCh7DQogICAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMgZnJvbSBmdWxsIG1vZGVsDQogICAgZnVsbF9yaXNrX3Njb3JlcyA8LSBwcmVkaWN0KGZ1bGxfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2xhc3Rfb2JzLCB0eXBlID0gInJpc2siKQ0KICAgIA0KICAgICMgR2V0IGNvbmNvcmRhbmNlIChIYXJyZWxsJ3MgQy1pbmRleCkNCiAgICBjb25jb3JkYW5jZSA8LSBhcy5udW1lcmljKGZ1bGxfbW9kZWwkY29uY29yZGFuY2VbImNvbmNvcmRhbmNlIl0pDQogICAgY2F0KCJDb25jb3JkYW5jZSAoQy1pbmRleCk6Iiwgcm91bmQoY29uY29yZGFuY2UsIDQpLCAiXG4iKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIHJpc2sgc2NvcmVzIGZyb20gbW9kZWwgd2l0aG91dCBtYWNybyB2YXJpYWJsZXMgKGlmIGFwcGxpY2FibGUpDQogICAgaWYgKCFpcy5udWxsKG5vX21hY3JvX21vZGVsKSAmJiAhaWRlbnRpY2FsKGZ1bGxfbW9kZWwsIG5vX21hY3JvX21vZGVsKSkgew0KICAgICAgbm9fbWFjcm9fcmlza19zY29yZXMgPC0gcHJlZGljdChub19tYWNyb19tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfbGFzdF9vYnMsIHR5cGUgPSAicmlzayIpDQogICAgICBoYXNfYm90aF9tb2RlbHMgPC0gVFJVRQ0KICAgIH0gZWxzZSB7DQogICAgICBub19tYWNyb19yaXNrX3Njb3JlcyA8LSBmdWxsX3Jpc2tfc2NvcmVzDQogICAgICBoYXNfYm90aF9tb2RlbHMgPC0gRkFMU0UNCiAgICB9DQogICAgDQogICAgIyBDYWxjdWxhdGUgQVVDIHVzaW5nIHBST0MgcGFja2FnZQ0KICAgIGlmKHJlcXVpcmVOYW1lc3BhY2UoInBST0MiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgICAgICMgQ3JlYXRlIFJPQyBvYmplY3RzDQogICAgICBmdWxsX3JvYyA8LSBwUk9DOjpyb2ModGVzdF9sYXN0X29ic1tbZXZlbnRfdHlwZV1dLCBmdWxsX3Jpc2tfc2NvcmVzLCBxdWlldCA9IFRSVUUpDQogICAgICBpZiAoaGFzX2JvdGhfbW9kZWxzKSB7DQogICAgICAgIG5vX21hY3JvX3JvYyA8LSBwUk9DOjpyb2ModGVzdF9sYXN0X29ic1tbZXZlbnRfdHlwZV1dLCBub19tYWNyb19yaXNrX3Njb3JlcywgcXVpZXQgPSBUUlVFKQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBBVUNzDQogICAgICBmdWxsX2F1YyA8LSBwUk9DOjphdWMoZnVsbF9yb2MpDQogICAgICBpZiAoaGFzX2JvdGhfbW9kZWxzKSB7DQogICAgICAgIG5vX21hY3JvX2F1YyA8LSBwUk9DOjphdWMobm9fbWFjcm9fcm9jKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgbm9fbWFjcm9fYXVjIDwtIGZ1bGxfYXVjDQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQogICAgICBmdWxsX2NpIDwtIHBST0M6OmNpKGZ1bGxfcm9jKQ0KICAgICAgaWYgKGhhc19ib3RoX21vZGVscykgew0KICAgICAgICBub19tYWNyb19jaSA8LSBwUk9DOjpjaShub19tYWNyb19yb2MpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBub19tYWNyb19jaSA8LSBmdWxsX2NpDQogICAgICB9DQogICAgICANCiAgICAgICMgUHJpbnQgcmVzdWx0cw0KICAgICAgY2F0KCJPdXQtb2YtdGltZSBBVUMgd2l0aCBhbGwgdmFyaWFibGVzOiIsIHJvdW5kKGZ1bGxfYXVjLCA0KSwgIlxuIikNCiAgICAgIGNhdCgiOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWw6IiwgcGFzdGUocm91bmQoZnVsbF9jaVsxOjJdLCA0KSwgY29sbGFwc2U9IiAtICIpLCAiXG4iKQ0KICAgICAgDQogICAgICBpZihoYXNfYm90aF9tb2RlbHMpIHsNCiAgICAgICAgY2F0KCJPdXQtb2YtdGltZSBBVUMgd2l0aG91dCBtYWNybyB2YXJpYWJsZXM6Iiwgcm91bmQobm9fbWFjcm9fYXVjLCA0KSwgIlxuIikNCiAgICAgICAgY2F0KCI5NSUgQ29uZmlkZW5jZSBJbnRlcnZhbDoiLCBwYXN0ZShyb3VuZChub19tYWNyb19jaVsxOjJdLCA0KSwgY29sbGFwc2U9IiAtICIpLCAiXG4iKQ0KICAgICAgICANCiAgICAgICAgIyBDYWxjdWxhdGUgaW1wcm92ZW1lbnQgZnJvbSBtYWNybyB2YXJpYWJsZXMNCiAgICAgICAgYXVjX2RpZmYgPC0gZnVsbF9hdWMgLSBub19tYWNyb19hdWMNCiAgICAgICAgcGN0X2ltcHJvdmVtZW50IDwtIGF1Y19kaWZmIC8gbm9fbWFjcm9fYXVjICogMTAwDQogICAgICAgIA0KICAgICAgICBjYXQoIkFVQyBpbXByb3ZlbWVudCBmcm9tIG1hY3JvIHZhcmlhYmxlczoiLCByb3VuZChhdWNfZGlmZiwgNCksIA0KICAgICAgICAgICAgIigiLCByb3VuZChwY3RfaW1wcm92ZW1lbnQsIDEpLCAiJSlcbiIpDQogICAgICAgIA0KICAgICAgICAjIFRlc3QgaWYgaW1wcm92ZW1lbnQgaXMgc2lnbmlmaWNhbnQNCiAgICAgICAgcm9jX3Rlc3QgPC0gcFJPQzo6cm9jLnRlc3QoZnVsbF9yb2MsIG5vX21hY3JvX3JvYywgbWV0aG9kID0gImRlbG9uZyIpDQogICAgICAgIGNhdCgiU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlOiBwID0iLCBmb3JtYXQucHZhbChyb2NfdGVzdCRwLnZhbHVlLCBkaWdpdHMgPSAzKSwgIlxuIikNCiAgICAgICAgDQogICAgICAgIGlmKHJvY190ZXN0JHAudmFsdWUgPCAwLjA1KSB7DQogICAgICAgICAgY2F0KCJUaGUgaW1wcm92ZW1lbnQgZnJvbSBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpcyBTVEFUSVNUSUNBTExZIFNJR05JRklDQU5UIChwIDwgMC4wNSlcbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCJUaGUgaW1wcm92ZW1lbnQgZnJvbSBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBpcyBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCAocCA+PSAwLjA1KVxuIikNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBwZXJmb3JtYW5jZSBtZXRyaWNzIGF0IG9wdGltYWwgdGhyZXNob2xkDQogICAgICBjb29yZHMgPC0gcFJPQzo6Y29vcmRzKGZ1bGxfcm9jLCAiYmVzdCIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzcGVjaWZpY2l0eSIsICJzZW5zaXRpdml0eSIpKQ0KICAgICAgdGhyZXNob2xkIDwtIGFzLm51bWVyaWMoY29vcmRzWyJ0aHJlc2hvbGQiXSkNCiAgICAgIHNwZWNpZmljaXR5IDwtIGFzLm51bWVyaWMoY29vcmRzWyJzcGVjaWZpY2l0eSJdKQ0KICAgICAgc2Vuc2l0aXZpdHkgPC0gYXMubnVtZXJpYyhjb29yZHNbInNlbnNpdGl2aXR5Il0pDQogICAgICANCiAgICAgIHByZWRpY3Rpb25zIDwtIGlmZWxzZShmdWxsX3Jpc2tfc2NvcmVzID49IHRocmVzaG9sZCwgMSwgMCkNCiAgICAgIGFjdHVhbCA8LSB0ZXN0X2xhc3Rfb2JzW1tldmVudF90eXBlXV0NCiAgICAgIA0KICAgICAgVFAgPC0gc3VtKHByZWRpY3Rpb25zID09IDEgJiBhY3R1YWwgPT0gMSkNCiAgICAgIFROIDwtIHN1bShwcmVkaWN0aW9ucyA9PSAwICYgYWN0dWFsID09IDApDQogICAgICBGUCA8LSBzdW0ocHJlZGljdGlvbnMgPT0gMSAmIGFjdHVhbCA9PSAwKQ0KICAgICAgRk4gPC0gc3VtKHByZWRpY3Rpb25zID09IDAgJiBhY3R1YWwgPT0gMSkNCiAgICAgIA0KICAgICAgYWNjdXJhY3kgPC0gKFRQICsgVE4pIC8gKFRQICsgVE4gKyBGUCArIEZOKQ0KICAgICAgcHJlY2lzaW9uIDwtIGlmKFRQICsgRlAgPiAwKSBUUCAvIChUUCArIEZQKSBlbHNlIDANCiAgICAgIHJlY2FsbCA8LSBzZW5zaXRpdml0eQ0KICAgICAgZjFfc2NvcmUgPC0gaWYocHJlY2lzaW9uICsgcmVjYWxsID4gMCkgMiAqIHByZWNpc2lvbiAqIHJlY2FsbCAvIChwcmVjaXNpb24gKyByZWNhbGwpIGVsc2UgMA0KICAgICAgDQogICAgICBjYXQoIlxuQ2xhc3NpZmljYXRpb24gbWV0cmljcyBhdCBvcHRpbWFsIHRocmVzaG9sZDpcbiIpDQogICAgICBjYXQoIlRocmVzaG9sZDoiLCByb3VuZCh0aHJlc2hvbGQsIDQpLCAiXG4iKQ0KICAgICAgY2F0KCJBY2N1cmFjeToiLCByb3VuZChhY2N1cmFjeSwgNCksICJcbiIpDQogICAgICBjYXQoIlByZWNpc2lvbjoiLCByb3VuZChwcmVjaXNpb24sIDQpLCAiXG4iKQ0KICAgICAgY2F0KCJSZWNhbGw6Iiwgcm91bmQocmVjYWxsLCA0KSwgIlxuIikNCiAgICAgIGNhdCgiRjEgU2NvcmU6Iiwgcm91bmQoZjFfc2NvcmUsIDQpLCAiXG4iKQ0KICAgICAgDQogICAgICAjIFJldHVybiByZXN1bHRzDQogICAgICByZXR1cm4obGlzdCgNCiAgICAgICAgZnVsbF9hdWMgPSBhcy5udW1lcmljKGZ1bGxfYXVjKSwNCiAgICAgICAgbm9fbWFjcm9fYXVjID0gYXMubnVtZXJpYyhub19tYWNyb19hdWMpLA0KICAgICAgICBhdWNfZGlmZiA9IGFzLm51bWVyaWMoZnVsbF9hdWMgLSBub19tYWNyb19hdWMpLA0KICAgICAgICBwX3ZhbHVlID0gaWYoaGFzX2JvdGhfbW9kZWxzKSByb2NfdGVzdCRwLnZhbHVlIGVsc2UgTkEsDQogICAgICAgIHRocmVzaG9sZCA9IHRocmVzaG9sZCwNCiAgICAgICAgYWNjdXJhY3kgPSBhY2N1cmFjeSwNCiAgICAgICAgcHJlY2lzaW9uID0gcHJlY2lzaW9uLA0KICAgICAgICByZWNhbGwgPSByZWNhbGwsDQogICAgICAgIGYxX3Njb3JlID0gZjFfc2NvcmUsDQogICAgICAgIGNvbmNvcmRhbmNlID0gY29uY29yZGFuY2UNCiAgICAgICkpDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgicFJPQyBwYWNrYWdlIG5vdCBhdmFpbGFibGUuIFBsZWFzZSBpbnN0YWxsIGl0IGZvciBST0MgYW5hbHlzaXMuXG4iKQ0KICAgICAgcmV0dXJuKE5VTEwpDQogICAgfQ0KICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICBjYXQoIkVycm9yIGV2YWx1YXRpbmcgbW9kZWw6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgIHJldHVybihOVUxMKQ0KICB9KQ0KfQ0KDQojIEV2YWx1YXRlIGJhbmtydXB0Y3kgbW9kZWwNCmJhbmtfdGltZV9yZXN1bHRzIDwtIGV2YWx1YXRlX3RpbWVfc3BsaXRfcGVyZm9ybWFuY2UoDQogIHRpbWVfYmFua19tb2RlbCwgDQogIHRpbWVfYmFua19ub19tYWNyb19tb2RlbCwgDQogIHRpbWVfdGVzdF9kYXRhLCANCiAgImJhbmtydXB0Y3kiLCANCiAgImJhbmtydXB0Y3kiDQopDQoNCiMgRXZhbHVhdGUgYWNxdWlzaXRpb24gbW9kZWwNCmFjcV90aW1lX3Jlc3VsdHMgPC0gZXZhbHVhdGVfdGltZV9zcGxpdF9wZXJmb3JtYW5jZSgNCiAgdGltZV9hY3FfbW9kZWwsIA0KICB0aW1lX2FjcV9ub19tYWNyb19tb2RlbCwgDQogIHRpbWVfdGVzdF9kYXRhLCANCiAgImFjcXVpc2l0aW9uIiwgDQogICJhY3F1aXNpdGlvbiINCikNCmBgYA0KV2Ugc2VlIHRoYXQgdGhlIG1vZGVscyBhcmUgcGVyZm9ybWluZyB3ZWxsIG9uIHRoZSB0ZXN0IHNldCwgd2l0aCBBVUMgdmFsdWVzIGluZGljYXRpbmcgZ29vZCBkaXNjcmltaW5hdGlvbiBhYmlsaXR5LiBUaGUgbWFjcm9lY29ub21pYyB2YXJpYWJsZXMgYWxzbyBzZWVtIHRvIHByb3ZpZGUgYSBzaWduaWZpY2FudCBpbXByb3ZlbWVudCBpbiBtb2RlbCBwZXJmb3JtYW5jZSwgYXMgaW5kaWNhdGVkIGJ5IHRoZSBwLXZhbHVlcyBmcm9tIHRoZSBzdGF0aXN0aWNhbCB0ZXN0cy4NCg0KRmluYWxseSwgd2UgY2FuIHZpc3VhbGl6ZSB0aGUgaGF6YXJkIHJhdGlvcyBmb3IgdGhlIG1vZGVscywgaGlnaGxpZ2h0aW5nIHRoZSB2YXJpYWJsZXMgdG8gaW50ZXJwcmV0IHRoZWlyIGltcGFjdCBvbiBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiBwcmVkaWN0aW9ucy4NCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBTVEVQIDEzOiBFbmhhbmNlZCBWaXN1YWxpemF0aW9uIGFuZCBJbnRlcnByZXRhdGlvbg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxMy4xOiBFbmhhbmNlZCBIYXphcmQgUmF0aW8gVmlzdWFsaXphdGlvbiB3aXRoIE1hY3JvIEhpZ2hsaWdodGluZw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMy4xOiBFTkhBTkNFRCBIQVpBUkQgUkFUSU8gVklTVUFMSVpBVElPTiBXSVRIIE1BQ1JPIEhJR0hMSUdIVElOR1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gY3JlYXRlIGhhemFyZCByYXRpbyBmb3Jlc3QgcGxvdHMgd2l0aCBtYWNybyB2YXJpYWJsZSBoaWdobGlnaHRpbmcNCmNyZWF0ZV9oYXphcmRfcmF0aW9fcGxvdCA8LSBmdW5jdGlvbihtb2RlbCwgbW9kZWxfbmFtZSwgdG9wX24gPSAxMCwgaGlnaGxpZ2h0X21hY3JvID0gVFJVRSkgew0KICBjYXQoIkNyZWF0aW5nIGhhemFyZCByYXRpbyBwbG90IGZvciIsIG1vZGVsX25hbWUsICJtb2RlbC4uLlxuIikNCiAgDQogICMgRXh0cmFjdCBjb2VmZmljaWVudHMgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQogIGNvZWZzIDwtIHN1bW1hcnkobW9kZWwpJGNvZWZmaWNpZW50cw0KICBjb25mX2ludCA8LSBjb25maW50KG1vZGVsKQ0KICANCiAgIyBDcmVhdGUgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcNCiAgaHJfZGF0YSA8LSBkYXRhLmZyYW1lKA0KICAgIFZhcmlhYmxlID0gcm93bmFtZXMoY29lZnMpLA0KICAgIEhSID0gZXhwKGNvZWZzWywgImNvZWYiXSksDQogICAgTG93ZXIgPSBleHAoY29uZl9pbnRbLCAxXSksDQogICAgVXBwZXIgPSBleHAoY29uZl9pbnRbLCAyXSksDQogICAgUHZhbHVlID0gY29lZnNbLCAiUHIoPnx6fCkiXSwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KICANCiAgIyBSZW1vdmUgdGltZSBpbnRlcmFjdGlvbnMgZm9yIHRoaXMgcGxvdA0KICBocl9kYXRhIDwtIGhyX2RhdGFbIWdyZXBsKCI6dHQiLCBocl9kYXRhJFZhcmlhYmxlKSwgXQ0KICANCiAgIyBJZGVudGlmeSBtYWNybyB2YXJpYWJsZXMNCiAgbWFjcm9fdmFycyA8LSBjKCJnZHBfZ3Jvd3RoIiwgImdkcF9kZWZsYXRvciIsICJ1bmVtcGxveWVtZW50IikNCiAgaHJfZGF0YSRpc19tYWNybyA8LSBzYXBwbHkoaHJfZGF0YSRWYXJpYWJsZSwgZnVuY3Rpb24oeCkgDQogICAgYW55KHNhcHBseShtYWNyb192YXJzLCBmdW5jdGlvbihtKSBncmVwbChtLCB4KSkpKQ0KICANCiAgIyBTb3J0IGJ5IHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQ0KICBocl9kYXRhIDwtIGhyX2RhdGFbb3JkZXIoaHJfZGF0YSRQdmFsdWUpLCBdDQogIA0KICAjIElmIGhpZ2hsaWdodGluZyBtYWNybywgZW5zdXJlIHRoZXkncmUgaW5jbHVkZWQgaW4gdG9wIHZhcmlhYmxlcw0KICBpZihoaWdobGlnaHRfbWFjcm8pIHsNCiAgICBtYWNyb19yb3dzIDwtIGhyX2RhdGFbaHJfZGF0YSRpc19tYWNybywgXQ0KICAgIG5vbl9tYWNyb19yb3dzIDwtIGhyX2RhdGFbIWhyX2RhdGEkaXNfbWFjcm8sIF0NCiAgICANCiAgICAjIFRha2UgdG9wIG5vbi1tYWNybyB2YXJpYWJsZXMNCiAgICB0b3Bfbm9uX21hY3JvIDwtIG1pbihucm93KG5vbl9tYWNyb19yb3dzKSwgbWF4KDUsIHRvcF9uIC0gbnJvdyhtYWNyb19yb3dzKSkpDQogICAgbm9uX21hY3JvX3Jvd3MgPC0gbm9uX21hY3JvX3Jvd3NbMTp0b3Bfbm9uX21hY3JvLCBdDQogICAgDQogICAgIyBDb21iaW5lIGFuZCByZS1zb3J0IGJ5IHAtdmFsdWUNCiAgICBocl9kYXRhIDwtIHJiaW5kKG5vbl9tYWNyb19yb3dzLCBtYWNyb19yb3dzKQ0KICAgIGhyX2RhdGEgPC0gaHJfZGF0YVtvcmRlcihocl9kYXRhJFB2YWx1ZSksIF0NCiAgfSBlbHNlIHsNCiAgICAjIFRha2UgdG9wIE4gbW9zdCBzaWduaWZpY2FudCB2YXJpYWJsZXMNCiAgICBpZihucm93KGhyX2RhdGEpID4gdG9wX24pIHsNCiAgICAgIGhyX2RhdGEgPC0gaHJfZGF0YVsxOnRvcF9uLCBdDQogICAgfQ0KICB9DQogIA0KICAjIFJldmVyc2Ugb3JkZXIgZm9yIHBsb3R0aW5nIChib3R0b20gdG8gdG9wKQ0KICBocl9kYXRhIDwtIGhyX2RhdGFbbnJvdyhocl9kYXRhKToxLCBdDQogIA0KICAjIENyZWF0ZSBzaWduaWZpY2FuY2Ugc3ltYm9scw0KICBocl9kYXRhJHNpZyA8LSAiIg0KICBocl9kYXRhJHNpZ1tocl9kYXRhJFB2YWx1ZSA8IDAuMDAxXSA8LSAiKioqIg0KICBocl9kYXRhJHNpZ1tocl9kYXRhJFB2YWx1ZSA+PSAwLjAwMSAmIGhyX2RhdGEkUHZhbHVlIDwgMC4wMV0gPC0gIioqIg0KICBocl9kYXRhJHNpZ1tocl9kYXRhJFB2YWx1ZSA+PSAwLjAxICYgaHJfZGF0YSRQdmFsdWUgPCAwLjA1XSA8LSAiKiINCiAgaHJfZGF0YSRzaWdbaHJfZGF0YSRQdmFsdWUgPj0gMC4wNSAmIGhyX2RhdGEkUHZhbHVlIDwgMC4xXSA8LSAiLiINCiAgDQogICMgU2V0IHVwIHRoZSBwbG90dGluZyBhcmVhDQogIHBhcihtYXIgPSBjKDUsIDEwLCA0LCAyKSArIDAuMSkgICMgQWRqdXN0IG1hcmdpbnMgZm9yIHZhcmlhYmxlIG5hbWVzDQogIA0KICAjIENyZWF0ZSBlbXB0eSBwbG90IHdpdGggYXBwcm9wcmlhdGUgbGltaXRzDQogIG1heF91cHBlciA8LSBtYXgoaHJfZGF0YSRVcHBlcikNCiAgaWYobWF4X3VwcGVyID4gMTApIG1heF91cHBlciA8LSAxMCAgIyBDYXAgZm9yIGV4dHJlbWUgdmFsdWVzDQogIHBsb3RfbWF4IDwtIG1heCgzLCBtYXhfdXBwZXIpDQogIA0KICBwbG90KE5VTEwsIHhsaW0gPSBjKDAsIHBsb3RfbWF4KSwgeWxpbSA9IGMoMC41LCBucm93KGhyX2RhdGEpICsgMC41KSwNCiAgICAgICB4bGFiID0gIkhhemFyZCBSYXRpbyAobG9nIHNjYWxlKSIsIHlsYWIgPSAiIiwgeWF4dCA9ICJuIiwNCiAgICAgICBtYWluID0gcGFzdGUoIkhhemFyZCBSYXRpb3MgZm9yIiwgbW9kZWxfbmFtZSwgIk1vZGVsIikpDQogIA0KICAjIEFkZCByZWZlcmVuY2UgbGluZSBhdCBIUiA9IDENCiAgYWJsaW5lKHYgPSAxLCBsdHkgPSAyLCBjb2wgPSAiZGFya2dyYXkiKQ0KICANCiAgIyBBZGQgdmFyaWFibGUgbmFtZXMgd2l0aCBkaWZmZXJlbnQgY29sb3JzIGZvciBtYWNybyB2YXJpYWJsZXMNCiAgdmFyX2xhYmVscyA8LSBocl9kYXRhJFZhcmlhYmxlDQogIA0KICAjIEFkZCB2YXJpYWJsZSBuYW1lcyAtIG5lZWQgdG8gaGFuZGxlIGNvbG9ycyBzZXBhcmF0ZWx5IHNpbmNlIGNvbC5heGlzIG11c3QgYmUgYSBzaW5nbGUgY29sb3INCiAgYXhpcygyLCBhdCA9IDE6bnJvdyhocl9kYXRhKSwgbGFiZWxzID0gcGFzdGUwKHZhcl9sYWJlbHMsICIgIiwgaHJfZGF0YSRzaWcpLCANCiAgICAgICBsYXMgPSAyLCBjZXguYXhpcyA9IDAuOCkNCiAgDQogICMgSGlnaGxpZ2h0IG1hY3JvIHZhcmlhYmxlcyB3aXRoIGNvbG9yZWQgcmVjdGFuZ2xlcyBpZiBhbnkgZXhpc3QNCiAgaWYoYW55KGhyX2RhdGEkaXNfbWFjcm8pKSB7DQogICAgZm9yKGkgaW4gd2hpY2goaHJfZGF0YSRpc19tYWNybykpIHsNCiAgICAgIHJlY3QocGFyKCJ1c3IiKVsxXSwgaSAtIDAuNCwgcGFyKCJ1c3IiKVsxXSArIDAuMSwgaSArIDAuNCwgDQogICAgICAgICAgIGNvbCA9ICJsaWdodGJsdWUiLCBib3JkZXIgPSBOQSwgeHBkID0gVFJVRSkNCiAgICB9DQogIH0NCiAgDQogICMgUGxvdCBjb25maWRlbmNlIGludGVydmFscw0KICBzZWdtZW50cyhocl9kYXRhJExvd2VyLCAxOm5yb3coaHJfZGF0YSksIGhyX2RhdGEkVXBwZXIsIDE6bnJvdyhocl9kYXRhKSwNCiAgICAgICAgICAgbHdkID0gMiwgY29sID0gaWZlbHNlKGhyX2RhdGEkaXNfbWFjcm8sICJuYXZ5IiwgImRhcmtibHVlIikpDQogIA0KICAjIFBsb3QgcG9pbnQgZXN0aW1hdGVzIHdpdGggZGlmZmVyZW50IGNvbG9ycyBmb3IgbWFjcm8gdnMgZmluYW5jaWFsIHZhcmlhYmxlcw0KICBwb2ludF9jb2xvcnMgPC0gaWZlbHNlKGhyX2RhdGEkaXNfbWFjcm8sIA0KICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShocl9kYXRhJEhSID4gMSwgImRhcmtyZWQiLCAiZGFya2dyZWVuIiksDQogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGhyX2RhdGEkSFIgPiAxLCAicmVkIiwgImdyZWVuNCIpKQ0KICANCiAgcG9pbnRzKGhyX2RhdGEkSFIsIDE6bnJvdyhocl9kYXRhKSwgcGNoID0gMTYsIGNleCA9IDEuMiwgDQogICAgICAgICBjb2wgPSBwb2ludF9jb2xvcnMpDQogIA0KICAjIEFkZCBudW1lcmljIHZhbHVlcw0KICB0ZXh0KGhyX2RhdGEkSFIgKyAwLjEsIDE6bnJvdyhocl9kYXRhKSwgDQogICAgICAgbGFiZWxzID0gc3ByaW50ZigiJS4yZiIsIGhyX2RhdGEkSFIpLCBjZXggPSAwLjcpDQogIA0KICAjIEFkZCBzaWduaWZpY2FuY2UgbGVnZW5kDQogIGxlZ2VuZCgidG9wcmlnaHQiLCANCiAgICAgICAgIGxlZ2VuZCA9IGMoInAgPCAwLjAwMSAoKioqKSIsICJwIDwgMC4wMSAoKiopIiwgInAgPCAwLjA1ICgqKSIsICJwIDwgMC4xICguKSIpLA0KICAgICAgICAgYnR5ID0gIm4iLCBjZXggPSAwLjgpDQogIA0KICAjIEFkZCBjb2xvciBsZWdlbmQgZm9yIG1hY3JvIHZzIGZpbmFuY2lhbCB2YXJpYWJsZXMNCiAgaWYoYW55KGhyX2RhdGEkaXNfbWFjcm8pKSB7DQogICAgbGVnZW5kKCJib3R0b21yaWdodCIsDQogICAgICAgICAgIGxlZ2VuZCA9IGMoIk1hY3JvZWNvbm9taWMgVmFyaWFibGUiLCAiRmluYW5jaWFsIFZhcmlhYmxlIiksDQogICAgICAgICAgIGZpbGwgPSBjKCJsaWdodGJsdWUiLCAid2hpdGUiKSwNCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIiwNCiAgICAgICAgICAgYnR5ID0gIm4iLCBjZXggPSAwLjgpDQogIH0NCiAgDQogICMgQWRkIGludGVycHJldGF0aW9uIG5vdGUNCiAgbXRleHQoIkhSID4gMTogSW5jcmVhc2VkIHJpc2ssIEhSIDwgMTogRGVjcmVhc2VkIHJpc2siLCBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC44KQ0KICANCiAgY2F0KCJIYXphcmQgcmF0aW8gcGxvdCBjcmVhdGVkIGZvciIsIG1vZGVsX25hbWUsICJtb2RlbC5cbiIpDQogIA0KICAjIFJldHVybiB0aGUgaGF6YXJkIHJhdGlvIGRhdGEgZm9yIGZ1cnRoZXIgYW5hbHlzaXMNCiAgcmV0dXJuKGhyX2RhdGEpDQp9DQoNCiMgQ3JlYXRlIGhhemFyZCByYXRpbyBwbG90cyBmb3IgYm90aCBtb2RlbHMNCmJhbmtydXB0Y3lfaHJzIDwtIGNyZWF0ZV9oYXphcmRfcmF0aW9fcGxvdChmaW5hbF9iYW5rcnVwdGN5X21vZGVsLCAiQmFua3J1cHRjeSIpDQphY3F1aXNpdGlvbl9ocnMgPC0gY3JlYXRlX2hhemFyZF9yYXRpb19wbG90KGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiQWNxdWlzaXRpb24iKQ0KDQpgYGANCg0KDQpOb3cgdGhhdCB3ZSdyZSBkb25lIHdpdGggdGhlIG1vZGVsIHRyYWluaW5nIGFuZCBldmFsdWF0aW9uLCB3ZSBkbyBhbiBhZGRpdGlvbmFsIHZpc3VhbGl6YXRpb24gZm9yIHRoZSBtYWNyb2Vjb25vbWljIGltcGFjdCBvbiB0aGUgbW9kZWxzLiBUaGlzIHdpbGwgaGVscCB1cyB1bmRlcnN0YW5kIGhvdyBjaGFuZ2VzIGluIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIGFmZmVjdCB0aGUgcmlzayBvZiBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbi4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuMjogRGVkaWNhdGVkIE1hY3JvZWNvbm9taWMgSW1wYWN0IFZpc3VhbGl6YXRpb24NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTMuMjogTUFDUk9FQ09OT01JQyBJTVBBQ1QgVklTVUFMSVpBVElPTlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCnZpc3VhbGl6ZV9tYWNyb19pbXBhY3QgPC0gZnVuY3Rpb24obW9kZWwsIG1vZGVsX25hbWUsIGV2ZW50X3R5cGUpIHsNCiAgIyBFeHRyYWN0IGNvZWZmaWNpZW50cyByZWxhdGVkIHRvIG1hY3JvIHZhcmlhYmxlcw0KICBjb2VmcyA8LSBjb2VmKG1vZGVsKQ0KICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAiZ2RwX2RlZmxhdG9yIiwgInVuZW1wbG95ZW1lbnQiKQ0KICANCiAgIyBGaW5kIG1hY3JvIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwNCiAgbW9kZWxfbWFjcm9fdmFycyA8LSBtYWNyb192YXJzW3NhcHBseShtYWNyb192YXJzLCBmdW5jdGlvbih2KSB2ICVpbiUgbmFtZXMoY29lZnMpKV0NCiAgDQogIGlmKGxlbmd0aChtb2RlbF9tYWNyb192YXJzKSA9PSAwKSB7DQogICAgY2F0KCJObyBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBmb3VuZCBpbiB0aGUiLCBtb2RlbF9uYW1lLCAibW9kZWwuXG4iKQ0KICAgIHJldHVybihOVUxMKQ0KICB9DQogIA0KICBjYXQoIlZpc3VhbGl6aW5nIG1hY3JvZWNvbm9taWMgaW1wYWN0cyBmb3IiLCBtb2RlbF9uYW1lLCAibW9kZWwuLi5cbiIpDQogIA0KICAjIEV4dHJhY3QgY29lZmZpY2llbnRzIGFuZCBjb25maWRlbmNlIGludGVydmFscyBmb3IgbWFjcm8gdmFyaWFibGVzDQogIGNvZWZfc3VtbWFyeSA8LSBzdW1tYXJ5KG1vZGVsKSRjb2VmZmljaWVudHMNCiAgY29uZl9pbnQgPC0gY29uZmludChtb2RlbCkNCiAgDQogICMgQ3JlYXRlIGRhdGEgZnJhbWUgZm9yIHBsb3R0aW5nDQogIG1hY3JvX2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgICBWYXJpYWJsZSA9IG1vZGVsX21hY3JvX3ZhcnMsDQogICAgQ29lZmZpY2llbnQgPSBjb2Vmc1ttb2RlbF9tYWNyb192YXJzXSwNCiAgICBIUiA9IGV4cChjb2Vmc1ttb2RlbF9tYWNyb192YXJzXSksDQogICAgTG93ZXIgPSBleHAoY29uZl9pbnRbbW9kZWxfbWFjcm9fdmFycywgMV0pLA0KICAgIFVwcGVyID0gZXhwKGNvbmZfaW50W21vZGVsX21hY3JvX3ZhcnMsIDJdKSwNCiAgICBQdmFsdWUgPSBjb2VmX3N1bW1hcnlbbW9kZWxfbWFjcm9fdmFycywgIlByKD58enwpIl0sDQogICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQogICkNCiAgDQogICMgQWRkIGludGVycHJldGFibGUgdmFyaWFibGUgbmFtZXMNCiAgbWFjcm9fZGF0YSRMYWJlbCA8LSBzYXBwbHkobWFjcm9fZGF0YSRWYXJpYWJsZSwgZnVuY3Rpb24odikgew0KICAgIGlmKHYgPT0gImdkcF9ncm93dGgiKSByZXR1cm4oIkdEUCBHcm93dGgiKQ0KICAgIGlmKHYgPT0gImdkcF9kZWZsYXRvciIpIHJldHVybigiSW5mbGF0aW9uIChHRFAgRGVmbGF0b3IpIikNCiAgICBpZih2ID09ICJ1bmVtcGxveWVtZW50IikgcmV0dXJuKCJVbmVtcGxveW1lbnQgUmF0ZSIpDQogICAgcmV0dXJuKHYpDQogIH0pDQogIA0KICAjIFNvcnQgYnkgZWZmZWN0IHNpemUgKGFic29sdXRlIGNvZWZmaWNpZW50KQ0KICBtYWNyb19kYXRhIDwtIG1hY3JvX2RhdGFbb3JkZXIoYWJzKG1hY3JvX2RhdGEkQ29lZmZpY2llbnQpLCBkZWNyZWFzaW5nID0gVFJVRSksIF0NCiAgDQogICMgMS4gQ3JlYXRlIGJhciBwbG90IG9mIG1hY3JvIHZhcmlhYmxlIGhhemFyZCByYXRpb3MNCiAgIyBTZXQgdXAgdGhlIHBsb3R0aW5nIGFyZWENCiAgcGFyKG1hciA9IGMoNSwgMTAsIDQsIDIpICsgMC4xKQ0KICANCiAgIyBDcmVhdGUgaG9yaXpvbnRhbCBiYXIgcGxvdCBvZiBoYXphcmQgcmF0aW9zDQogIGJhcnBsb3RfcmVzdWx0IDwtIGJhcnBsb3QobWFjcm9fZGF0YSRIUiAtIDEsIG5hbWVzLmFyZyA9IG1hY3JvX2RhdGEkTGFiZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSBwYXN0ZSgiSW1wYWN0IG9mIE1hY3JvZWNvbm9taWMgVmFyaWFibGVzIG9uIiwgbW9kZWxfbmFtZSwgIlJpc2siKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJDaGFuZ2UgaW4gUmlzayAoJSkiLCB5bGFiID0gIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGhvcml6ID0gVFJVRSwgeGxpbSA9IGMobWluKC0wLjUsIG1pbihtYWNyb19kYXRhJEhSIC0gMSkgKiAxLjIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heCgwLjUsIG1heChtYWNyb19kYXRhJEhSIC0gMSkgKiAxLjIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gaWZlbHNlKG1hY3JvX2RhdGEkSFIgPiAxLCAiZmlyZWJyaWNrIiwgImZvcmVzdGdyZWVuIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxhcyA9IDEpDQogIA0KICAjIEFkZCB2ZXJ0aWNhbCByZWZlcmVuY2UgbGluZSBhdCAwDQogIGFibGluZSh2ID0gMCwgbHR5ID0gMiwgY29sID0gImJsYWNrIikNCiAgDQogICMgQWRkIGRhdGEgbGFiZWxzDQogIHRleHRfcG9zIDwtIGlmZWxzZShtYWNyb19kYXRhJEhSID4gMSwgDQogICAgICAgICAgICAgICAgICAgbWFjcm9fZGF0YSRIUiAtIDEgKyAwLjAzLCANCiAgICAgICAgICAgICAgICAgICBtYWNyb19kYXRhJEhSIC0gMSAtIDAuMDMpDQogIA0KICB0ZXh0KHRleHRfcG9zLCBiYXJwbG90X3Jlc3VsdCwgDQogICAgICAgbGFiZWxzID0gc3ByaW50ZigiJSsuMWYlJSIsIChtYWNyb19kYXRhJEhSIC0gMSkgKiAxMDApLA0KICAgICAgIGNleCA9IDAuOSwgY29sID0gaWZlbHNlKG1hY3JvX2RhdGEkSFIgPiAxLCAiZmlyZWJyaWNrIiwgImZvcmVzdGdyZWVuIikpDQogIA0KICAjIEFkZCBzaWduaWZpY2FuY2Ugc3ltYm9scw0KICBzaWduaWZpY2FuY2UgPC0gcmVwKCIiLCBucm93KG1hY3JvX2RhdGEpKQ0KICBzaWduaWZpY2FuY2VbbWFjcm9fZGF0YSRQdmFsdWUgPCAwLjAwMV0gPC0gIioqKiINCiAgc2lnbmlmaWNhbmNlW21hY3JvX2RhdGEkUHZhbHVlID49IDAuMDAxICYgbWFjcm9fZGF0YSRQdmFsdWUgPCAwLjAxXSA8LSAiKioiDQogIHNpZ25pZmljYW5jZVttYWNyb19kYXRhJFB2YWx1ZSA+PSAwLjAxICYgbWFjcm9fZGF0YSRQdmFsdWUgPCAwLjA1XSA8LSAiKiINCiAgc2lnbmlmaWNhbmNlW21hY3JvX2RhdGEkUHZhbHVlID49IDAuMDUgJiBtYWNyb19kYXRhJFB2YWx1ZSA8IDAuMV0gPC0gIi4iDQogIA0KICB0ZXh0KGlmZWxzZShtYWNyb19kYXRhJEhSID4gMSwgbWF4KDAuMSwgbWF4KG1hY3JvX2RhdGEkSFIgLSAxKSAqIDAuOCksIA0KICAgICAgICAgICAgbWluKC0wLjEsIG1pbihtYWNyb19kYXRhJEhSIC0gMSkgKiAwLjgpKSwgDQogICAgICAgYmFycGxvdF9yZXN1bHQsIA0KICAgICAgIGxhYmVscyA9IHNpZ25pZmljYW5jZSwNCiAgICAgICBjZXggPSAxLjIpDQogIA0KICAjIEFkZCBpbnRlcnByZXRhdGlvbiBub3RlDQogIG10ZXh0KCJFZmZlY3Qgb2YgYSAxLXVuaXQgaW5jcmVhc2UgaW4gZWFjaCB2YXJpYWJsZSIsIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpDQogIA0KICAjIEFkZCBzaWduaWZpY2FuY2UgbGVnZW5kDQogIGxlZ2VuZCgiYm90dG9tcmlnaHQiLCANCiAgICAgICAgIGxlZ2VuZCA9IGMoInAgPCAwLjAwMSAoKioqKSIsICJwIDwgMC4wMSAoKiopIiwgInAgPCAwLjA1ICgqKSIsICJwIDwgMC4xICguKSIpLA0KICAgICAgICAgYnR5ID0gIm4iLCBjZXggPSAwLjgpDQogIA0KICAjIDIuIENyZWF0ZSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb2YgaG93IGVjb25vbWljIGluZGljYXRvcnMgYWZmZWN0IHJpc2sNCiAgIyBPbmx5IGlmIHdlIGhhdmUgYXQgbGVhc3QgdHdvIG1hY3JvIHZhcmlhYmxlcw0KICBpZihucm93KG1hY3JvX2RhdGEpID49IDIpIHsNCiAgICAjIENhbGN1bGF0ZSBtaW4gYW5kIG1heCB2YWx1ZXMgb2YgZWFjaCBtYWNybyB2YXJpYWJsZSBpbiB0aGUgZGF0YQ0KICAgIHZhcl9yYW5nZXMgPC0gZGF0YS5mcmFtZSgNCiAgICAgIFZhcmlhYmxlID0gbW9kZWxfbWFjcm9fdmFycywNCiAgICAgIE1pbiA9IE5BLA0KICAgICAgTWF4ID0gTkEsDQogICAgICBNZWFuID0gTkEsDQogICAgICBTRCA9IE5BDQogICAgKQ0KICAgIA0KICAgIGZvcihpIGluIDE6bnJvdyh2YXJfcmFuZ2VzKSkgew0KICAgICAgdmFyIDwtIHZhcl9yYW5nZXMkVmFyaWFibGVbaV0NCiAgICAgIGlmKHZhciAlaW4lIGNvbG5hbWVzKGRhdGEpKSB7DQogICAgICAgIHZhcl9yYW5nZXMkTWluW2ldIDwtIG1pbihkYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgICB2YXJfcmFuZ2VzJE1heFtpXSA8LSBtYXgoZGF0YVtbdmFyXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgICAgdmFyX3JhbmdlcyRNZWFuW2ldIDwtIG1lYW4oZGF0YVtbdmFyXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgICAgdmFyX3JhbmdlcyRTRFtpXSA8LSBzZChkYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICAjIFNldCB1cCBhIG5ldyBwbG90DQogICAgcGFyKG1hciA9IGMoNSwgMTAsIDQsIDIpICsgMC4xKQ0KICAgIA0KICAgICMgQ3JlYXRlIGEgcmlzayBtYXRyaXggdmlzdWFsaXphdGlvbg0KICAgIHBsb3QoTlVMTCwgeGxpbSA9IGMoLTEsIDEpLCB5bGltID0gYygwLCBucm93KG1hY3JvX2RhdGEpICsgMSksDQogICAgICAgICB4bGFiID0gIkVjb25vbWljIENvbmRpdGlvbiIsIHlsYWIgPSAiIiwgeWF4dCA9ICJuIiwNCiAgICAgICAgIG1haW4gPSBwYXN0ZSgiRWNvbm9taWMgQ29uZGl0aW9ucyBhbmQiLCBtb2RlbF9uYW1lLCAiUmlzayIpKQ0KICAgIA0KICAgICMgQWRkIHktYXhpcyBsYWJlbHMNCiAgICBheGlzKDIsIGF0ID0gMTpucm93KG1hY3JvX2RhdGEpLCBsYWJlbHMgPSBtYWNyb19kYXRhJExhYmVsLCBsYXMgPSAxKQ0KICAgIA0KICAgICMgQWRkIHgtYXhpcw0KICAgIGF4aXMoMSwgYXQgPSBzZXEoLTEsIDEsIDAuNSksIA0KICAgICAgICAgbGFiZWxzID0gYygiVmVyeSBMb3ciLCAiTG93IiwgIkF2ZXJhZ2UiLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkNCiAgICANCiAgICAjIEFkZCByZWZlcmVuY2UgbGluZSBhdCAwDQogICAgYWJsaW5lKHYgPSAwLCBsdHkgPSAyLCBjb2wgPSAiZGFya2dyYXkiKQ0KICAgIA0KICAgICMgQWRkIGFycm93cyBzaG93aW5nIHRoZSBkaXJlY3Rpb24gb2YgZWZmZWN0DQogICAgZm9yKGkgaW4gMTpucm93KG1hY3JvX2RhdGEpKSB7DQogICAgICBhcnJvd19jb2xvciA8LSBpZmVsc2UobWFjcm9fZGF0YSRIUltpXSA+IDEsICJyZWQiLCAiZ3JlZW40IikNCiAgICAgIGFycm93X2xlbmd0aCA8LSBtaW4oMC45LCBhYnMobG9nKG1hY3JvX2RhdGEkSFJbaV0pKSkNCiAgICAgIGFycm93X2RpciA8LSBzaWduKGxvZyhtYWNyb19kYXRhJEhSW2ldKSkNCiAgICAgIA0KICAgICAgYXJyb3dzKDAsIGksIGFycm93X2RpciAqIGFycm93X2xlbmd0aCwgaSwgY29sID0gYXJyb3dfY29sb3IsIA0KICAgICAgICAgICAgIGxlbmd0aCA9IDAuMTUsIGx3ZCA9IDIgKyBhYnMobG9nKG1hY3JvX2RhdGEkSFJbaV0pKSkNCiAgICAgIA0KICAgICAgIyBBZGQgdGV4dCBzaG93aW5nIGluY3JlYXNlZC9kZWNyZWFzZWQgcmlzaw0KICAgICAgaWYobWFjcm9fZGF0YSRIUltpXSA+IDEpIHsNCiAgICAgICAgdGV4dChhcnJvd19kaXIgKiBhcnJvd19sZW5ndGggKyAwLjEsIGksIA0KICAgICAgICAgICAgIHBhc3RlMCgiKyIsIHJvdW5kKChtYWNyb19kYXRhJEhSW2ldIC0gMSkgKiAxMDApLCAiJSByaXNrIiksIA0KICAgICAgICAgICAgIGNvbCA9IGFycm93X2NvbG9yLCBjZXggPSAwLjgpDQogICAgICB9IGVsc2Ugew0KICAgICAgICB0ZXh0KGFycm93X2RpciAqIGFycm93X2xlbmd0aCAtIDAuMSwgaSwgDQogICAgICAgICAgICAgcGFzdGUwKCItIiwgcm91bmQoKDEgLSBtYWNyb19kYXRhJEhSW2ldKSAqIDEwMCksICIlIHJpc2siKSwgDQogICAgICAgICAgICAgY29sID0gYXJyb3dfY29sb3IsIGNleCA9IDAuOCkNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBBZGQgbGVnZW5kDQogICAgbGVnZW5kKCJ0b3ByaWdodCIsDQogICAgICAgICAgIGxlZ2VuZCA9IGMoIkluY3JlYXNlcyBSaXNrIiwgIkRlY3JlYXNlcyBSaXNrIiksDQogICAgICAgICAgIGNvbCA9IGMoInJlZCIsICJncmVlbjQiKSwNCiAgICAgICAgICAgbHdkID0gMiwgYnR5ID0gIm4iKQ0KICAgIA0KICAgIG10ZXh0KCJIb3cgRWNvbm9taWMgSW5kaWNhdG9ycyBBZmZlY3QgUmlzayIsIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpDQogIH0NCiAgDQogICMgMy4gSWYgd2UgaGF2ZSB0aW1lIGludGVyYWN0aW9ucywgY3JlYXRlIGEgdGltZS12YXJ5aW5nIGVmZmVjdHMgdmlzdWFsaXphdGlvbg0KICB0aW1lX3Rlcm1zIDwtIG5hbWVzKGNvZWZzKVtncmVwbCgiOnR0IiwgbmFtZXMoY29lZnMpKV0NCiAgbWFjcm9fdGltZV90ZXJtcyA8LSB0aW1lX3Rlcm1zW3NhcHBseSh0aW1lX3Rlcm1zLCBmdW5jdGlvbih0ZXJtKSB7DQogICAgdmFyX25hbWUgPC0gc3Ryc3BsaXQodGVybSwgIjoiKVtbMV1dWzFdDQogICAgcmV0dXJuKHZhcl9uYW1lICVpbiUgbWFjcm9fdmFycykNCiAgfSldDQogIA0KICBpZihsZW5ndGgobWFjcm9fdGltZV90ZXJtcykgPiAwKSB7DQogICAgY2F0KCJcblZpc3VhbGl6ZSB0aW1lLXZhcnlpbmcgZWZmZWN0cyBvZiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcy4uLlxuIikNCiAgICANCiAgICAjIEZvciBlYWNoIG1hY3JvIHZhcmlhYmxlIHdpdGggYSB0aW1lLXZhcnlpbmcgZWZmZWN0LCBjcmVhdGUgYSBwbG90DQogICAgZm9yKHRpbWVfdGVybSBpbiBtYWNyb190aW1lX3Rlcm1zKSB7DQogICAgICB2YXJfbmFtZSA8LSBzdHJzcGxpdCh0aW1lX3Rlcm0sICI6IilbWzFdXVsxXQ0KICAgICAgDQogICAgICAjIEdldCBjb2VmZmljaWVudHMNCiAgICAgIG1haW5fY29lZiA8LSBjb2Vmc1t2YXJfbmFtZV0NCiAgICAgIHRpbWVfY29lZiA8LSBjb2Vmc1t0aW1lX3Rlcm1dDQogICAgICANCiAgICAgICMgQ3JlYXRlIHJlYWRhYmxlIHZhcmlhYmxlIG5hbWUNCiAgICAgIHZhcl9sYWJlbCA8LSBpZih2YXJfbmFtZSA9PSAiZ2RwX2dyb3d0aCIpICJHRFAgR3Jvd3RoIiBlbHNlDQogICAgICAgICAgICAgICAgICAgaWYodmFyX25hbWUgPT0gImdkcF9kZWZsYXRvciIpICJJbmZsYXRpb24gKEdEUCBEZWZsYXRvcikiIGVsc2UNCiAgICAgICAgICAgICAgICAgICBpZih2YXJfbmFtZSA9PSAidW5lbXBsb3llbWVudCIpICJVbmVtcGxveW1lbnQgUmF0ZSIgZWxzZSB2YXJfbmFtZQ0KICAgICAgDQogICAgICAjIFNldCB1cCB0aW1lIHNlcXVlbmNlDQogICAgICBtYXhfdGltZSA8LSBtYXgoZGF0YSR0c3RvcCkNCiAgICAgIHRpbWVzIDwtIHNlcSgwLCBtYXhfdGltZSwgbGVuZ3RoLm91dCA9IDEwMCkNCiAgICAgIA0KICAgICAgIyBDYWxjdWxhdGUgaGF6YXJkIHJhdGlvcyBvdmVyIHRpbWUgKGZvciBhIDEtdW5pdCBjaGFuZ2UgaW4gdGhlIHZhcmlhYmxlKQ0KICAgICAgaHJzIDwtIGV4cChtYWluX2NvZWYgKyB0aW1lX2NvZWYgKiB0aW1lcykNCiAgICAgIA0KICAgICAgIyBTZXQgdXAgYSBuZXcgcGxvdA0KICAgICAgcGFyKG1hciA9IGMoNSwgNSwgNCwgMikgKyAwLjEpDQogICAgICANCiAgICAgICMgQ3JlYXRlIHBsb3QNCiAgICAgIHBsb3QodGltZXMsIGhycywgdHlwZSA9ICJsIiwgbHdkID0gMiwgY29sID0gImJsdWUiLA0KICAgICAgICAgICBtYWluID0gcGFzdGUoIlRpbWUtVmFyeWluZyBFZmZlY3Qgb2YiLCB2YXJfbGFiZWwsICJvbiIsIG1vZGVsX25hbWUsICJSaXNrIiksDQogICAgICAgICAgIHhsYWIgPSAiVGltZSAoWWVhcnMpIiwgeWxhYiA9ICJIYXphcmQgUmF0aW8iLA0KICAgICAgICAgICB5bGltID0gYyhtaW4oMC41LCBtaW4oaHJzKSksIG1heCgyLCBtYXgoaHJzKSkpKQ0KICAgICAgDQogICAgICAjIEFkZCByZWZlcmVuY2UgbGluZSBhdCBIUiA9IDENCiAgICAgIGFibGluZShoID0gMSwgbHR5ID0gMiwgY29sID0gInJlZCIpDQogICAgICANCiAgICAgICMgQWRkIGdyaWQgZm9yIHJlYWRhYmlsaXR5DQogICAgICBncmlkKCkNCiAgICAgIA0KICAgICAgIyBDYWxjdWxhdGUgYW5kIG1hcmsgY3Jvc3NvdmVyIHBvaW50IChpZiBhbnkpDQogICAgICBpZihzaWduKG1haW5fY29lZikgIT0gc2lnbih0aW1lX2NvZWYpICYmIHRpbWVfY29lZiAhPSAwKSB7DQogICAgICAgIGNyb3Nzb3ZlciA8LSAtbWFpbl9jb2VmIC8gdGltZV9jb2VmDQogICAgICAgIGlmKGNyb3Nzb3ZlciA+IDAgJiYgY3Jvc3NvdmVyIDwgbWF4KHRpbWVzKSkgew0KICAgICAgICAgIHBvaW50cyhjcm9zc292ZXIsIDEsIHBjaCA9IDE2LCBjb2wgPSAicmVkIiwgY2V4ID0gMS41KQ0KICAgICAgICAgIHRleHQoY3Jvc3NvdmVyLCAxLjEsIHBhc3RlKCJFZmZlY3QgcmV2ZXJzZXMgYXQiLCByb3VuZChjcm9zc292ZXIsIDEpLCAieWVhcnMiKSwNCiAgICAgICAgICAgICAgIGNvbCA9ICJyZWQiKQ0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgICMgQWRkIHBoYXNlIGxhYmVscyBmb3IgZWNvbm9taWMgaW50ZXJwcmV0YXRpb24NCiAgICAgIGlmKG1haW5fY29lZiA+IDApIHsNCiAgICAgICAgaWYodGltZV9jb2VmID4gMCkgew0KICAgICAgICAgICMgQm90aCBwb3NpdGl2ZSAtIGluY3JlYXNpbmcgcmlzayB0aGF0IGdldHMgd29yc2Ugb3ZlciB0aW1lDQogICAgICAgICAgZWFybHlfbGFiZWwgPC0gIkVhcmx5OiBJbmNyZWFzZXMgUmlzayINCiAgICAgICAgICBsYXRlX2xhYmVsIDwtICJMYXRlcjogUmlzayBJbmNyZWFzZSBTdHJlbmd0aGVucyINCiAgICAgICAgICBlY29ub21pY19tc2cgPC0gIkVjb25vbWljIHNlbnNpdGl2aXR5IGluY3JlYXNlcyB3aXRoIGNvbXBhbnkgYWdlIg0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICMgTWFpbiBwb3NpdGl2ZSwgdGltZSBuZWdhdGl2ZSAtIGluY3JlYXNpbmcgcmlzayB0aGF0IGRpbWluaXNoZXMNCiAgICAgICAgICBlYXJseV9sYWJlbCA8LSAiRWFybHk6IEluY3JlYXNlcyBSaXNrIg0KICAgICAgICAgIGxhdGVfbGFiZWwgPC0gIkxhdGVyOiBSaXNrIEluY3JlYXNlIFdlYWtlbnMiDQogICAgICAgICAgZWNvbm9taWNfbXNnIDwtICJZb3VuZyBjb21wYW5pZXMgbW9yZSBzZW5zaXRpdmUgdG8gdGhpcyBlY29ub21pYyBmYWN0b3IiDQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGlmKHRpbWVfY29lZiA+IDApIHsNCiAgICAgICAgICAjIE1haW4gbmVnYXRpdmUsIHRpbWUgcG9zaXRpdmUgLSBkZWNyZWFzaW5nIHJpc2sgdGhhdCBkaW1pbmlzaGVzDQogICAgICAgICAgZWFybHlfbGFiZWwgPC0gIkVhcmx5OiBEZWNyZWFzZXMgUmlzayINCiAgICAgICAgICBsYXRlX2xhYmVsIDwtICJMYXRlcjogUmlzayBSZWR1Y3Rpb24gV2Vha2VucyINCiAgICAgICAgICBlY29ub21pY19tc2cgPC0gIllvdW5nIGNvbXBhbmllcyBtb3JlIHByb3RlY3RlZCBieSB0aGlzIGVjb25vbWljIGZhY3RvciINCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAjIEJvdGggbmVnYXRpdmUgLSBkZWNyZWFzaW5nIHJpc2sgdGhhdCBnZXRzIHN0cm9uZ2VyDQogICAgICAgICAgZWFybHlfbGFiZWwgPC0gIkVhcmx5OiBEZWNyZWFzZXMgUmlzayINCiAgICAgICAgICBsYXRlX2xhYmVsIDwtICJMYXRlcjogUmlzayBSZWR1Y3Rpb24gU3RyZW5ndGhlbnMiDQogICAgICAgICAgZWNvbm9taWNfbXNnIDwtICJFY29ub21pYyBwcm90ZWN0aW9uIGluY3JlYXNlcyB3aXRoIGNvbXBhbnkgYWdlIg0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgICMgQWRkIHRoZSBwaGFzZSBsYWJlbHMgdG8gdGhlIHBsb3QNCiAgICAgIHRleHQodGltZXNbMjBdLCBocnNbMjBdICsgMC4xNSwgZWFybHlfbGFiZWwsIGNvbCA9ICJkYXJrZ3JlZW4iLCBjZXggPSAwLjkpDQogICAgICB0ZXh0KHRpbWVzWzgwXSwgaHJzWzgwXSArIDAuMTUsIGxhdGVfbGFiZWwsIGNvbCA9ICJkYXJrYmx1ZSIsIGNleCA9IDAuOSkNCiAgICAgIA0KICAgICAgIyBBZGQgZWNvbm9taWMgaW50ZXJwcmV0YXRpb24NCiAgICAgIG10ZXh0KGVjb25vbWljX21zZywgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICB9DQogIH0NCiAgDQogICMgUHJpbnQgYSBzdW1tYXJ5IG9mIG1hY3JvZWNvbm9taWMgZWZmZWN0cw0KICBjYXQoIlxuU3VtbWFyeSBvZiBNYWNyb2Vjb25vbWljIEVmZmVjdHMgb24iLCBtb2RlbF9uYW1lLCAiUmlzazpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICANCiAgZm9yKGkgaW4gMTpucm93KG1hY3JvX2RhdGEpKSB7DQogICAgdmFyIDwtIG1hY3JvX2RhdGEkVmFyaWFibGVbaV0NCiAgICBsYWJlbCA8LSBtYWNyb19kYXRhJExhYmVsW2ldDQogICAgaHIgPC0gbWFjcm9fZGF0YSRIUltpXQ0KICAgIHBfdmFsIDwtIG1hY3JvX2RhdGEkUHZhbHVlW2ldDQogICAgDQogICAgIyBEaXJlY3Rpb24gb2YgZWZmZWN0DQogICAgZGlyZWN0aW9uIDwtIGlmZWxzZShociA+IDEsICJpbmNyZWFzZXMiLCAiZGVjcmVhc2VzIikNCiAgICANCiAgICAjIE1hZ25pdHVkZSBpbiBwZXJjZW50YWdlDQogICAgaWYoaHIgPiAxKSB7DQogICAgICBtYWduaXR1ZGUgPC0gcGFzdGUwKCIrIiwgcm91bmQoKGhyIC0gMSkgKiAxMDAsIDEpLCAiJSIpDQogICAgfSBlbHNlIHsNCiAgICAgIG1hZ25pdHVkZSA8LSBwYXN0ZTAoIi0iLCByb3VuZCgoMSAtIGhyKSAqIDEwMCwgMSksICIlIikNCiAgICB9DQogICAgDQogICAgIyBTaWduaWZpY2FuY2UNCiAgICBzaWduaWZpY2FuY2UgPC0gIiINCiAgICBpZihwX3ZhbCA8IDAuMDAxKSBzaWduaWZpY2FuY2UgPC0gIiAocCA8IDAuMDAxKSAqKioiDQogICAgZWxzZSBpZihwX3ZhbCA8IDAuMDEpIHNpZ25pZmljYW5jZSA8LSBwYXN0ZTAoIiAocCA9ICIsIHJvdW5kKHBfdmFsLCAzKSwgIikgKioiKQ0KICAgIGVsc2UgaWYocF92YWwgPCAwLjA1KSBzaWduaWZpY2FuY2UgPC0gcGFzdGUwKCIgKHAgPSAiLCByb3VuZChwX3ZhbCwgMyksICIpICoiKQ0KICAgIGVsc2UgaWYocF92YWwgPCAwLjEpIHNpZ25pZmljYW5jZSA8LSBwYXN0ZTAoIiAocCA9ICIsIHJvdW5kKHBfdmFsLCAzKSwgIikgLiIpDQogICAgZWxzZSBzaWduaWZpY2FuY2UgPC0gcGFzdGUwKCIgKHAgPSAiLCByb3VuZChwX3ZhbCwgMyksICIpIikNCiAgICANCiAgICAjIENoZWNrIGZvciB0aW1lLXZhcnlpbmcgZWZmZWN0DQogICAgaGFzX3RpbWVfZWZmZWN0IDwtIHBhc3RlMCh2YXIsICI6dHQiKSAlaW4lIG5hbWVzKGNvZWZzKQ0KICAgIHRpbWVfZWZmZWN0IDwtIGlmKGhhc190aW1lX2VmZmVjdCkgew0KICAgICAgdGltZV9jb2VmIDwtIGNvZWZzW3Bhc3RlMCh2YXIsICI6dHQiKV0NCiAgICAgIGlmKHRpbWVfY29lZiA+IDAgJiYgaHIgPiAxKSAiZWZmZWN0IHN0cmVuZ3RoZW5zIG92ZXIgdGltZSINCiAgICAgIGVsc2UgaWYodGltZV9jb2VmIDwgMCAmJiBociA+IDEpICJlZmZlY3Qgd2Vha2VucyBvdmVyIHRpbWUiDQogICAgICBlbHNlIGlmKHRpbWVfY29lZiA+IDAgJiYgaHIgPCAxKSAicHJvdGVjdGlvbiB3ZWFrZW5zIG92ZXIgdGltZSINCiAgICAgIGVsc2UgInByb3RlY3Rpb24gc3RyZW5ndGhlbnMgb3ZlciB0aW1lIg0KICAgIH0gZWxzZSAiIg0KICAgIA0KICAgICMgRWNvbm9taWMgaW50ZXJwcmV0YXRpb24NCiAgICBpbnRlcnByZXRhdGlvbiA8LSAiIg0KICAgIGlmKHZhciA9PSAiZ2RwX2dyb3d0aCIpIHsNCiAgICAgIGlmKGhyID4gMSkgew0KICAgICAgICBpbnRlcnByZXRhdGlvbiA8LSAiSGlnaGVyIGVjb25vbWljIGdyb3d0aCBjb3JyZXNwb25kcyB0byBJTkNSRUFTRUQgcmlzayINCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGludGVycHJldGF0aW9uIDwtICJIaWdoZXIgZWNvbm9taWMgZ3Jvd3RoIGNvcnJlc3BvbmRzIHRvIERFQ1JFQVNFRCByaXNrIg0KICAgICAgfQ0KICAgIH0gZWxzZSBpZih2YXIgPT0gImdkcF9kZWZsYXRvciIpIHsNCiAgICAgIGlmKGhyID4gMSkgew0KICAgICAgICBpbnRlcnByZXRhdGlvbiA8LSAiSGlnaGVyIGluZmxhdGlvbiBjb3JyZXNwb25kcyB0byBJTkNSRUFTRUQgcmlzayINCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGludGVycHJldGF0aW9uIDwtICJIaWdoZXIgaW5mbGF0aW9uIGNvcnJlc3BvbmRzIHRvIERFQ1JFQVNFRCByaXNrIg0KICAgICAgfQ0KICAgIH0gZWxzZSBpZih2YXIgPT0gInVuZW1wbG95ZW1lbnQiKSB7DQogICAgICBpZihociA+IDEpIHsNCiAgICAgICAgaW50ZXJwcmV0YXRpb24gPC0gIkhpZ2hlciB1bmVtcGxveW1lbnQgY29ycmVzcG9uZHMgdG8gSU5DUkVBU0VEIHJpc2siDQogICAgICB9IGVsc2Ugew0KICAgICAgICBpbnRlcnByZXRhdGlvbiA8LSAiSGlnaGVyIHVuZW1wbG95bWVudCBjb3JyZXNwb25kcyB0byBERUNSRUFTRUQgcmlzayINCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBQcmludCB0aGUgc3VtbWFyeQ0KICAgIGNhdChsYWJlbCwgZGlyZWN0aW9uLCBtb2RlbF9uYW1lLCAicmlzayBieSIsIG1hZ25pdHVkZSwgc2lnbmlmaWNhbmNlLCAiXG4iKQ0KICAgIGlmKGhhc190aW1lX2VmZmVjdCkgY2F0KCJUaW1lIGVmZmVjdDoiLCB0aW1lX2VmZmVjdCwgIlxuIikNCiAgICBjYXQoIkVjb25vbWljIGludGVycHJldGF0aW9uOiIsIGludGVycHJldGF0aW9uLCAiXG5cbiIpDQogIH0NCiAgDQogICMgUmV0dXJuIHRoZSBtYWNybyBkYXRhIGZvciBmdXJ0aGVyIGFuYWx5c2lzDQogIHJldHVybihtYWNyb19kYXRhKQ0KfQ0KDQojIFZpc3VhbGl6ZSBtYWNyb2Vjb25vbWljIGltcGFjdHMgZm9yIGJvdGggbW9kZWxzDQpiYW5rcnVwdGN5X21hY3JvIDwtIHZpc3VhbGl6ZV9tYWNyb19pbXBhY3QoZmluYWxfYmFua3J1cHRjeV9tb2RlbCwgIkJhbmtydXB0Y3kiLCAiYmFua3J1cHRjeSIpDQphY3F1aXNpdGlvbl9tYWNybyA8LSB2aXN1YWxpemVfbWFjcm9faW1wYWN0KGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiQWNxdWlzaXRpb24iLCAiYWNxdWlzaXRpb24iKQ0KDQpgYGANCg0KTW9yZSB2aXN1YWxpemFpdGlvbnMgb2YgdGhlIG1hY3JvZWNvbm9taWMgaW1wYWN0IG9uIHRoZSBtb2RlbHMgY2FuIGJlIGRvbmUgYnkgY29tcGFyaW5nIHRoZSBlZmZlY3RzIG9mIG1hY3JvZWNvbm9taWMgdmFyaWFibGVzIG9uIGJvdGggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gbW9kZWxzLiBUaGlzIHdpbGwgaGVscCB1cyB1bmRlcnN0YW5kIGhvdyB0aGVzZSB2YXJpYWJsZXMgaW5mbHVlbmNlIHRoZSByaXNrIG9mIGJhbmtydXB0Y3kgYW5kIGFjcXVpc2l0aW9uIGluIGRpZmZlcmVudCB3YXlzLg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxMy4zOiBDb21wYXJhdGl2ZSBNYWNyb2Vjb25vbWljIEltcGFjdCBBbmFseXNpcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMy4zOiBDT01QQVJBVElWRSBNQUNST0VDT05PTUlDIElNUEFDVCBBTkFMWVNJU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCmNvbXBhcmVfbWFjcm9fZWZmZWN0cyA8LSBmdW5jdGlvbigpIHsNCiAgIyBHZXQgY29tbW9uIG1hY3JvIHZhcmlhYmxlcyB0aGF0IGV4aXN0IGluIGJvdGggbW9kZWxzDQogIG1hY3JvX3ZhcnMgPC0gYygiZ2RwX2dyb3d0aCIsICJnZHBfZGVmbGF0b3IiLCAidW5lbXBsb3llbWVudCIpDQogIA0KICAjIEV4dHJhY3QgY29lZmZpY2llbnRzIGZyb20gYm90aCBtb2RlbHMNCiAgYmFua19jb2VmcyA8LSBjb2VmKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpDQogIGFjcV9jb2VmcyA8LSBjb2VmKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsKQ0KICANCiAgIyBGaW5kIGNvbW1vbiBtYWNybyB2YXJpYWJsZXMgaW4gYm90aCBtb2RlbHMNCiAgYmFua19tYWNyb192YXJzIDwtIG1hY3JvX3ZhcnNbc2FwcGx5KG1hY3JvX3ZhcnMsIGZ1bmN0aW9uKHYpIHYgJWluJSBuYW1lcyhiYW5rX2NvZWZzKSldDQogIGFjcV9tYWNyb192YXJzIDwtIG1hY3JvX3ZhcnNbc2FwcGx5KG1hY3JvX3ZhcnMsIGZ1bmN0aW9uKHYpIHYgJWluJSBuYW1lcyhhY3FfY29lZnMpKV0NCiAgY29tbW9uX21hY3JvX3ZhcnMgPC0gaW50ZXJzZWN0KGJhbmtfbWFjcm9fdmFycywgYWNxX21hY3JvX3ZhcnMpDQogIA0KICBpZihsZW5ndGgoY29tbW9uX21hY3JvX3ZhcnMpID09IDApIHsNCiAgICBjYXQoIk5vIGNvbW1vbiBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBmb3VuZCBpbiBib3RoIG1vZGVscyBmb3IgY29tcGFyaXNvbi5cbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0NCiAgDQogIGNhdCgiQ29tcGFyaW5nIG1hY3JvZWNvbm9taWMgZWZmZWN0cyBiZXR3ZWVuIGJhbmtydXB0Y3kgYW5kIGFjcXVpc2l0aW9uIG1vZGVscy4uLlxuIikNCiAgDQogICMgQ3JlYXRlIGRhdGEgZnJhbWUgZm9yIGNvbXBhcmlzb24NCiAgY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKA0KICAgIFZhcmlhYmxlID0gY29tbW9uX21hY3JvX3ZhcnMsDQogICAgQmFua19Db2VmID0gYmFua19jb2Vmc1tjb21tb25fbWFjcm9fdmFyc10sDQogICAgQmFua19IUiA9IGV4cChiYW5rX2NvZWZzW2NvbW1vbl9tYWNyb192YXJzXSksDQogICAgQWNxX0NvZWYgPSBhY3FfY29lZnNbY29tbW9uX21hY3JvX3ZhcnNdLA0KICAgIEFjcV9IUiA9IGV4cChhY3FfY29lZnNbY29tbW9uX21hY3JvX3ZhcnNdKSwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KICANCiAgIyBBZGQgdmFyaWFibGUgbGFiZWxzDQogIGNvbXBhcmlzb24kTGFiZWwgPC0gc2FwcGx5KGNvbXBhcmlzb24kVmFyaWFibGUsIGZ1bmN0aW9uKHYpIHsNCiAgICBpZih2ID09ICJnZHBfZ3Jvd3RoIikgcmV0dXJuKCJHRFAgR3Jvd3RoIikNCiAgICBpZih2ID09ICJnZHBfZGVmbGF0b3IiKSByZXR1cm4oIkluZmxhdGlvbiAoR0RQIERlZmxhdG9yKSIpDQogICAgaWYodiA9PSAidW5lbXBsb3llbWVudCIpIHJldHVybigiVW5lbXBsb3ltZW50IFJhdGUiKQ0KICAgIHJldHVybih2KQ0KICB9KQ0KICANCiAgIyBBZGQgZGlyZWN0aW9uIG9mIGVmZmVjdHMNCiAgY29tcGFyaXNvbiRCYW5rX0RpcmVjdGlvbiA8LSBpZmVsc2UoY29tcGFyaXNvbiRCYW5rX0hSID4gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJbmNyZWFzZXMgUmlzayIsICJEZWNyZWFzZXMgUmlzayIpDQogIGNvbXBhcmlzb24kQWNxX0RpcmVjdGlvbiA8LSBpZmVsc2UoY29tcGFyaXNvbiRBY3FfSFIgPiAxLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSW5jcmVhc2VzIFJpc2siLCAiRGVjcmVhc2VzIFJpc2siKQ0KICANCiAgIyBDaGVjayBmb3Igb3Bwb3NpdGUgZWZmZWN0cw0KICBjb21wYXJpc29uJE9wcG9zaXRlX0VmZmVjdHMgPC0gY29tcGFyaXNvbiRCYW5rX0RpcmVjdGlvbiAhPSBjb21wYXJpc29uJEFjcV9EaXJlY3Rpb24NCiAgDQogICMgMS4gQ3JlYXRlIHNpZGUtYnktc2lkZSBiYXIgcGxvdCBjb21wYXJpbmcgZWZmZWN0cw0KICAjIFNldCB1cCB0aGUgcGxvdHRpbmcgYXJlYQ0KICBwYXIobWFyID0gYyg1LCAxMCwgNCwgMikgKyAwLjEpDQogIA0KICAjIFByZXBhcmUgZGF0YSBmb3IgcGxvdHRpbmcNCiAgYmFycGxvdF9kYXRhIDwtIGNiaW5kKA0KICAgIChjb21wYXJpc29uJEJhbmtfSFIgLSAxKSAqIDEwMCwgICMgQ29udmVydCB0byBwZXJjZW50YWdlIGNoYW5nZQ0KICAgIChjb21wYXJpc29uJEFjcV9IUiAtIDEpICogMTAwDQogICkNCiAgDQogICMgRmluZCBsaW1pdHMgZm9yIHgtYXhpcw0KICBtYXhfdmFsIDwtIG1heChhYnMoYmFycGxvdF9kYXRhKSkgKiAxLjINCiAgeGxpbSA8LSBjKC1tYXhfdmFsLCBtYXhfdmFsKQ0KICANCiAgIyBDcmVhdGUgaG9yaXpvbnRhbCBiYXIgcGxvdA0KICBiYXJwbG90X3Jlc3VsdCA8LSBiYXJwbG90KHQoYmFycGxvdF9kYXRhKSwgYmVzaWRlID0gVFJVRSwgaG9yaXogPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzLmFyZyA9IGNvbXBhcmlzb24kTGFiZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWFpbiA9ICJDb21wYXJhdGl2ZSBFY29ub21pYyBJbXBhY3Qgb24gQ29tcGFueSBPdXRjb21lcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJDaGFuZ2UgaW4gUmlzayAoJSkiLCB5bGFiID0gIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gYygiZmlyZWJyaWNrIiwgInJveWFsYmx1ZSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHhsaW0gPSB4bGltLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxhcyA9IDEpDQogIA0KICAjIEFkZCB2ZXJ0aWNhbCByZWZlcmVuY2UgbGluZSBhdCAwDQogIGFibGluZSh2ID0gMCwgbHR5ID0gMiwgY29sID0gImJsYWNrIikNCiAgDQogICMgQWRkIGxlZ2VuZA0KICBsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gYygiQmFua3J1cHRjeSBSaXNrIiwgIkFjcXVpc2l0aW9uIFJpc2siKSwNCiAgICAgICAgIGZpbGwgPSBjKCJmaXJlYnJpY2siLCAicm95YWxibHVlIiksIGJ0eSA9ICJuIikNCiAgDQogICMgQWRkIGRhdGEgbGFiZWxzDQogIGZvcihpIGluIDE6bnJvdyhjb21wYXJpc29uKSkgew0KICAgICMgRm9yIGJhbmtydXB0Y3kNCiAgICBiYW5rX3BvcyA8LSBiYXJwbG90X3Jlc3VsdFsxLCBpXQ0KICAgIGJhbmtfdmFsIDwtIGJhcnBsb3RfZGF0YVtpLCAxXQ0KICAgIHRleHRfcG9zX2JhbmsgPC0gaWZlbHNlKGJhbmtfdmFsID4gMCwgYmFua192YWwgKyAyLCBiYW5rX3ZhbCAtIDIpDQogICAgdGV4dCh0ZXh0X3Bvc19iYW5rLCBiYW5rX3BvcywgDQogICAgICAgICBsYWJlbHMgPSBzcHJpbnRmKCIlKy4xZiUlIiwgYmFua192YWwpLA0KICAgICAgICAgY2V4ID0gMC44KQ0KICAgIA0KICAgICMgRm9yIGFjcXVpc2l0aW9uDQogICAgYWNxX3BvcyA8LSBiYXJwbG90X3Jlc3VsdFsyLCBpXQ0KICAgIGFjcV92YWwgPC0gYmFycGxvdF9kYXRhW2ksIDJdDQogICAgdGV4dF9wb3NfYWNxIDwtIGlmZWxzZShhY3FfdmFsID4gMCwgYWNxX3ZhbCArIDIsIGFjcV92YWwgLSAyKQ0KICAgIHRleHQodGV4dF9wb3NfYWNxLCBhY3FfcG9zLCANCiAgICAgICAgIGxhYmVscyA9IHNwcmludGYoIiUrLjFmJSUiLCBhY3FfdmFsKSwNCiAgICAgICAgIGNleCA9IDAuOCkNCiAgfQ0KICANCiAgIyBNYXJrIHZhcmlhYmxlcyB3aXRoIG9wcG9zaXRlIGVmZmVjdHMNCiAgaWYoYW55KGNvbXBhcmlzb24kT3Bwb3NpdGVfRWZmZWN0cykpIHsNCiAgICBtdGV4dCgiKiBWYXJpYWJsZXMgd2l0aCBvcHBvc2l0ZSBlZmZlY3RzIG9uIGJhbmtydXB0Y3kgdnMgYWNxdWlzaXRpb24iLCANCiAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC44LCBjb2wgPSAicHVycGxlIikNCiAgICANCiAgICAjIEFkZCBtYXJrZXJzIGZvciBvcHBvc2l0ZSBlZmZlY3RzDQogICAgZm9yKGkgaW4gd2hpY2goY29tcGFyaXNvbiRPcHBvc2l0ZV9FZmZlY3RzKSkgew0KICAgICAgYmFua19wb3MgPC0gYmFycGxvdF9yZXN1bHRbMSwgaV0NCiAgICAgIGFjcV9wb3MgPC0gYmFycGxvdF9yZXN1bHRbMiwgaV0NCiAgICAgIA0KICAgICAgIyBBZGQgYXN0ZXJpc2sgYXQgdGhlIG1pZGRsZQ0KICAgICAgdGV4dCh4bGltWzFdICsgMSwgKGJhbmtfcG9zICsgYWNxX3BvcykvMiwgIioiLCBjb2wgPSAicHVycGxlIiwgY2V4ID0gMS41KQ0KICAgIH0NCiAgfQ0KICANCiAgIyAyLiBDcmVhdGUgYSAyeDIgZWNvbm9taWMgY29uZGl0aW9uIG1hdHJpeCB2aXN1YWxpemF0aW9uDQogICMgVGhpcyBzaG93cyBob3cgZGlmZmVyZW50IGVjb25vbWljIGNvbmRpdGlvbnMgYWZmZWN0IGJvdGggb3V0Y29tZXMNCiAgaWYobGVuZ3RoKGNvbW1vbl9tYWNyb192YXJzKSA+PSAyKSB7DQogICAgIyBDcmVhdGUgYSBuZXcgcGxvdA0KICAgIHBhcihtYXIgPSBjKDUsIDUsIDQsIDIpICsgMC4xKQ0KICAgIA0KICAgICMgU2V0IHVwIHRoZSBwbG90IGFyZWEgLSBlY29ub21pYyBjb25kaXRpb25zIG1hdHJpeA0KICAgIHBsb3QoTlVMTCwgeGxpbSA9IGMoLTEsIDEpLCB5bGltID0gYygtMSwgMSksDQogICAgICAgICB4bGFiID0gIkJhbmtydXB0Y3kgUmlzayIsIHlsYWIgPSAiQWNxdWlzaXRpb24gUmlzayIsDQogICAgICAgICBtYWluID0gIkNvbXBhbnkgT3V0Y29tZXMgVW5kZXIgRGlmZmVyZW50IEVjb25vbWljIENvbmRpdGlvbnMiKQ0KICAgIA0KICAgICMgQWRkIHJlZmVyZW5jZSBsaW5lcw0KICAgIGFibGluZShoID0gMCwgdiA9IDAsIGx0eSA9IDIsIGNvbCA9ICJkYXJrZ3JheSIpDQogICAgDQogICAgIyBBZGQgcXVhZHJhbnQgbGFiZWxzDQogICAgdGV4dCgwLjgsIDAuOCwgIkhpZ2ggQmFua3J1cHRjeVxuSGlnaCBBY3F1aXNpdGlvbiIsIGNvbCA9ICJwdXJwbGUiLCBjZXggPSAwLjgpDQogICAgdGV4dCgtMC44LCAwLjgsICJMb3cgQmFua3J1cHRjeVxuSGlnaCBBY3F1aXNpdGlvbiIsIGNvbCA9ICJibHVlIiwgY2V4ID0gMC44KQ0KICAgIHRleHQoMC44LCAtMC44LCAiSGlnaCBCYW5rcnVwdGN5XG5Mb3cgQWNxdWlzaXRpb24iLCBjb2wgPSAicmVkIiwgY2V4ID0gMC44KQ0KICAgIHRleHQoLTAuOCwgLTAuOCwgIkxvdyBCYW5rcnVwdGN5XG5Mb3cgQWNxdWlzaXRpb24iLCBjb2wgPSAiZ3JlZW40IiwgY2V4ID0gMC44KQ0KICAgIA0KICAgICMgQWRkIGFycm93cyBmb3IgZWFjaCBtYWNybyB2YXJpYWJsZQ0KICAgIGZvcihpIGluIDE6bnJvdyhjb21wYXJpc29uKSkgew0KICAgICAgIyBDYWxjdWxhdGUgYXJyb3cgZGlyZWN0aW9uIGJhc2VkIG9uIGNvZWZmaWNpZW50cw0KICAgICAgYmFua19lZmZlY3QgPC0gY29tcGFyaXNvbiRCYW5rX0NvZWZbaV0NCiAgICAgIGFjcV9lZmZlY3QgPC0gY29tcGFyaXNvbiRBY3FfQ29lZltpXQ0KICAgICAgDQogICAgICAjIE5vcm1hbGl6ZSB0byBhIHJlYXNvbmFibGUgbGVuZ3RoIGZvciB2aXN1YWxpemF0aW9uDQogICAgICBhcnJvd19sZW5ndGggPC0gc3FydChiYW5rX2VmZmVjdF4yICsgYWNxX2VmZmVjdF4yKQ0KICAgICAgbm9ybV9mYWN0b3IgPC0gbWluKDAuOCwgbWF4KDAuMywgYXJyb3dfbGVuZ3RoKSkgLyBtYXgoMC4wMDAxLCBhcnJvd19sZW5ndGgpDQogICAgICANCiAgICAgIHhfZW5kIDwtIGJhbmtfZWZmZWN0ICogbm9ybV9mYWN0b3INCiAgICAgIHlfZW5kIDwtIGFjcV9lZmZlY3QgKiBub3JtX2ZhY3Rvcg0KICAgICAgDQogICAgICAjIERyYXcgdGhlIGFycm93DQogICAgICBhcnJvd3MoMCwgMCwgeF9lbmQsIHlfZW5kLCBsZW5ndGggPSAwLjEsIA0KICAgICAgICAgICAgIGNvbCA9IGlmZWxzZShjb21wYXJpc29uJE9wcG9zaXRlX0VmZmVjdHNbaV0sICJwdXJwbGUiLCAiYmxhY2siKSwNCiAgICAgICAgICAgICBsd2QgPSAyKQ0KICAgICAgDQogICAgICAjIEFkZCB2YXJpYWJsZSBuYW1lDQogICAgICB0ZXh0KHhfZW5kICogMS4xLCB5X2VuZCAqIDEuMSwgY29tcGFyaXNvbiRMYWJlbFtpXSwgDQogICAgICAgICAgIGNleCA9IDAuOCwgDQogICAgICAgICAgIGNvbCA9IGlmZWxzZShjb21wYXJpc29uJE9wcG9zaXRlX0VmZmVjdHNbaV0sICJwdXJwbGUiLCAiYmxhY2siKSkNCiAgICB9DQogICAgDQogICAgIyBBZGQgaW50ZXJwcmV0YXRpb24gbm90ZQ0KICAgIG10ZXh0KCJBcnJvd3Mgc2hvdyBob3cgZWFjaCBlY29ub21pYyBpbmRpY2F0b3IgYWZmZWN0cyBib3RoIG91dGNvbWVzIiwgDQogICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgfQ0KICANCiAgIyBQcmludCBhIHN1bW1hcnkgb2YgdGhlIGNvbXBhcmF0aXZlIGFuYWx5c2lzDQogIGNhdCgiXG5Db21wYXJhdGl2ZSBBbmFseXNpcyBvZiBNYWNyb2Vjb25vbWljIEVmZmVjdHM6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIA0KICBmb3IoaSBpbiAxOm5yb3coY29tcGFyaXNvbikpIHsNCiAgICB2YXIgPC0gY29tcGFyaXNvbiRWYXJpYWJsZVtpXQ0KICAgIGxhYmVsIDwtIGNvbXBhcmlzb24kTGFiZWxbaV0NCiAgICBiYW5rX2hyIDwtIGNvbXBhcmlzb24kQmFua19IUltpXQ0KICAgIGFjcV9ociA8LSBjb21wYXJpc29uJEFjcV9IUltpXQ0KICAgIG9wcG9zaXRlIDwtIGNvbXBhcmlzb24kT3Bwb3NpdGVfRWZmZWN0c1tpXQ0KICAgIA0KICAgICMgRm9ybWF0IGVmZmVjdHMgYXMgcGVyY2VudGFnZXMNCiAgICBpZihiYW5rX2hyID4gMSkgew0KICAgICAgYmFua19lZmZlY3QgPC0gcGFzdGUwKCIrIiwgcm91bmQoKGJhbmtfaHIgLSAxKSAqIDEwMCwgMSksICIlIikNCiAgICB9IGVsc2Ugew0KICAgICAgYmFua19lZmZlY3QgPC0gcGFzdGUwKCItIiwgcm91bmQoKDEgLSBiYW5rX2hyKSAqIDEwMCwgMSksICIlIikNCiAgICB9DQogICAgDQogICAgaWYoYWNxX2hyID4gMSkgew0KICAgICAgYWNxX2VmZmVjdCA8LSBwYXN0ZTAoIisiLCByb3VuZCgoYWNxX2hyIC0gMSkgKiAxMDAsIDEpLCAiJSIpDQogICAgfSBlbHNlIHsNCiAgICAgIGFjcV9lZmZlY3QgPC0gcGFzdGUwKCItIiwgcm91bmQoKDEgLSBhY3FfaHIpICogMTAwLCAxKSwgIiUiKQ0KICAgIH0NCiAgICANCiAgICBjYXQobGFiZWwsICI6XG4iKQ0KICAgIGNhdCgiICAtIEVmZmVjdCBvbiBiYW5rcnVwdGN5IHJpc2s6IiwgYmFua19lZmZlY3QsICJcbiIpDQogICAgY2F0KCIgIC0gRWZmZWN0IG9uIGFjcXVpc2l0aW9uIHJpc2s6IiwgYWNxX2VmZmVjdCwgIlxuIikNCiAgICANCiAgICBpZihvcHBvc2l0ZSkgew0KICAgICAgY2F0KCIgIC0gVGhpcyB2YXJpYWJsZSBoYXMgT1BQT1NJVEUgZWZmZWN0cyBvbiBiYW5rcnVwdGN5IHZzLiBhY3F1aXNpdGlvblxuIikNCiAgICAgIA0KICAgICAgIyBBZGQgZWNvbm9taWMgaW50ZXJwcmV0YXRpb24gZm9yIG9wcG9zaXRlIGVmZmVjdHMNCiAgICAgIGlmKHZhciA9PSAiZ2RwX2dyb3d0aCIpIHsNCiAgICAgICAgaWYoYmFua19ociA8IDEgJiYgYWNxX2hyID4gMSkgew0KICAgICAgICAgIGNhdCgiICAtIEludGVycHJldGF0aW9uOiBFY29ub21pYyBncm93dGggUkVEVUNFUyBmaW5hbmNpYWwgZGlzdHJlc3Mgd2hpbGUgSU5DUkVBU0lORyBNJkEgYWN0aXZpdHlcbiIpDQogICAgICAgICAgY2F0KCIgIC0gVGhpcyBhbGlnbnMgd2l0aCB0aGVvcmllcyBvZiBvcHBvcnR1bml0eS1kcml2ZW4gdnMuIGRpc3RyZXNzLWRyaXZlbiBjb3Jwb3JhdGUgZXZlbnRzXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiICAtIEludGVycHJldGF0aW9uOiBVbnVzdWFsIHBhdHRlcm4gLSBFY29ub21pYyBncm93dGggaGFzIHVuY29udmVudGlvbmFsIGVmZmVjdCBvbiBvdXRjb21lc1xuIikNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIGlmKHZhciA9PSAiZ2RwX2RlZmxhdG9yIikgew0KICAgICAgICBpZihiYW5rX2hyID4gMSAmJiBhY3FfaHIgPCAxKSB7DQogICAgICAgICAgY2F0KCIgIC0gSW50ZXJwcmV0YXRpb246IEluZmxhdGlvbiBJTkNSRUFTRVMgZmluYW5jaWFsIGRpc3RyZXNzIGJ1dCBSRURVQ0VTIGFjcXVpc2l0aW9uIGxpa2VsaWhvb2RcbiIpDQogICAgICAgICAgY2F0KCIgIC0gSGlnaCBpbmZsYXRpb24gbWF5IGNyZWF0ZSBvcGVyYXRpb25hbCBjaGFsbGVuZ2VzIHdoaWxlIGRlcHJlc3NpbmcgTSZBIG1hcmtldHNcbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCIgIC0gSW50ZXJwcmV0YXRpb246IEluZmxhdGlvbiBoYXMgY29tcGxleCBlZmZlY3RzIG9uIGNvcnBvcmF0ZSBvdXRjb21lc1xuIikNCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIGlmKHZhciA9PSAidW5lbXBsb3llbWVudCIpIHsNCiAgICAgICAgaWYoYmFua19ociA+IDEgJiYgYWNxX2hyIDwgMSkgew0KICAgICAgICAgIGNhdCgiICAtIEludGVycHJldGF0aW9uOiBIaWdoIHVuZW1wbG95bWVudCBJTkNSRUFTRVMgYmFua3J1cHRjeSByaXNrIGJ1dCBSRURVQ0VTIGFjcXVpc2l0aW9uc1xuIikNCiAgICAgICAgICBjYXQoIiAgLSBQb29yIGVjb25vbWljIGNvbmRpdGlvbnMgaW5jcmVhc2UgZGlzdHJlc3Mgd2hpbGUgcmVkdWNpbmcgTSZBIGFjdGl2aXR5XG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgiICAtIEludGVycHJldGF0aW9uOiBVbmVtcGxveW1lbnQgaGFzIGNvbXBsZXggZWZmZWN0cyBvbiBjb3Jwb3JhdGUgb3V0Y29tZXNcbiIpDQogICAgICAgIH0NCiAgICAgIH0NCiAgICB9IGVsc2Ugew0KICAgICAgY2F0KCIgIC0gVGhpcyB2YXJpYWJsZSBhZmZlY3RzIGJvdGggb3V0Y29tZXMgaW4gdGhlIFNBTUUgZGlyZWN0aW9uXG4iKQ0KICAgICAgDQogICAgICAjIEFkZCBlY29ub21pYyBpbnRlcnByZXRhdGlvbiBmb3Igc2FtZS1kaXJlY3Rpb24gZWZmZWN0cw0KICAgICAgaWYoYmFua19ociA+IDEgJiYgYWNxX2hyID4gMSkgew0KICAgICAgICBjYXQoIiAgLSBJbnRlcnByZXRhdGlvbjogVGhpcyBlY29ub21pYyBmYWN0b3IgaW5jcmVhc2VzIEJPVEggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gcmlza1xuIikNCiAgICAgICAgY2F0KCIgIC0gTWF5IGluZGljYXRlIGEgZ2VuZXJhbCBlY29ub21pYyBzdHJlc3MgZmFjdG9yIGluY3JlYXNpbmcgYWxsIGNvcnBvcmF0ZSBldmVudHNcbiIpDQogICAgICB9IGVsc2UgaWYoYmFua19ociA8IDEgJiYgYWNxX2hyIDwgMSkgew0KICAgICAgICBjYXQoIiAgLSBJbnRlcnByZXRhdGlvbjogVGhpcyBlY29ub21pYyBmYWN0b3IgZGVjcmVhc2VzIEJPVEggYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gcmlza1xuIikNCiAgICAgICAgY2F0KCIgIC0gTWF5IGluZGljYXRlIGEgc3RhYmlsaXppbmcgZWNvbm9taWMgY29uZGl0aW9uIHRoYXQgcmVkdWNlcyBjb3Jwb3JhdGUgZXZlbnRzXG4iKQ0KICAgICAgfQ0KICAgIH0NCiAgICBjYXQoIlxuIikNCiAgfQ0KICANCiAgIyBSZXR1cm4gY29tcGFyaXNvbiBmb3IgZnVydGhlciBhbmFseXNpcw0KICByZXR1cm4oY29tcGFyaXNvbikNCn0NCg0KIyBDb21wYXJlIG1hY3JvZWNvbm9taWMgZWZmZWN0cyBiZXR3ZWVuIG1vZGVscw0KbWFjcm9fY29tcGFyaXNvbiA8LSBjb21wYXJlX21hY3JvX2VmZmVjdHMoKQ0KDQpgYGANCg0KSGVyZSB0aGUgc3Vydml2YWwgY3VydmVzIGFyZSB2aXN1YWxpemVkIGJ5IGVjb25vbWljIGNvbmRpdGlvbnMuIFRoaXMgd2lsbCBoZWxwIHVzIHVuZGVyc3RhbmQgaG93IHRoZSByaXNrIG9mIGJhbmtydXB0Y3kgYW5kIGFjcXVpc2l0aW9uIHZhcmllcyB1bmRlciBkaWZmZXJlbnQgZWNvbm9taWMgY29uZGl0aW9ucy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuNDogU3Vydml2YWwgQ3VydmUgVmlzdWFsaXphdGlvbiB3aXRoIEVjb25vbWljIENvbmRpdGlvbnMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTMuNDogU1VSVklWQUwgQ1VSVkVTIEJZIEVDT05PTUlDIENPTkRJVElPTlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCnZpc3VhbGl6ZV9zdXJ2aXZhbF9ieV9lY29ub215IDwtIGZ1bmN0aW9uKG1vZGVsLCBtb2RlbF9uYW1lLCBldmVudF90eXBlKSB7DQogIGNhdCgiQ3JlYXRpbmcgc3Vydml2YWwgY3VydmVzIGJ5IGVjb25vbWljIGNvbmRpdGlvbiBmb3IiLCBtb2RlbF9uYW1lLCAibW9kZWwuLi5cbiIpDQogIA0KICAjIENoZWNrIGlmIHdlIGhhdmUgZWNvbm9taWMgaW5kaWNhdG9ycyBpbiB0aGUgZGF0YQ0KICBoYXNfbWFjcm8gPC0gYWxsKGMoImdkcF9ncm93dGgiLCAidW5lbXBsb3llbWVudCIpICVpbiUgY29sbmFtZXMoZGF0YSkpDQogIA0KICBpZighaGFzX21hY3JvKSB7DQogICAgY2F0KCJFY29ub21pYyBpbmRpY2F0b3JzIG5vdCBhdmFpbGFibGUgZm9yIGNvbmRpdGlvbi1zcGVjaWZpYyB2aXN1YWxpemF0aW9uLlxuIikNCiAgICByZXR1cm4oTlVMTCkNCiAgfQ0KICANCiAgIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55DQogIGZpbmFsX29icyA8LSBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICANCiAgIyBDYWxjdWxhdGUgbWVkaWFuIHZhbHVlcyB0byBkZWZpbmUgY29uZGl0aW9ucw0KICBtZWRpYW5fZ2RwIDwtIG1lZGlhbihmaW5hbF9vYnMkZ2RwX2dyb3d0aCwgbmEucm0gPSBUUlVFKQ0KICBtZWRpYW5fdW5lbXAgPC0gbWVkaWFuKGZpbmFsX29icyR1bmVtcGxveWVtZW50LCBuYS5ybSA9IFRSVUUpDQogIA0KICAjIENyZWF0ZSBlY29ub21pYyBjb25kaXRpb24gZ3JvdXBzDQogIGZpbmFsX29icyRlY29uX2NvbmRpdGlvbiA8LSAiTWl4ZWQiDQogIGZpbmFsX29icyRlY29uX2NvbmRpdGlvbltmaW5hbF9vYnMkZ2RwX2dyb3d0aCA+IG1lZGlhbl9nZHAgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICBmaW5hbF9vYnMkdW5lbXBsb3llbWVudCA8IG1lZGlhbl91bmVtcF0gPC0gIlN0cm9uZyINCiAgZmluYWxfb2JzJGVjb25fY29uZGl0aW9uW2ZpbmFsX29icyRnZHBfZ3Jvd3RoIDwgbWVkaWFuX2dkcCAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmFsX29icyR1bmVtcGxveWVtZW50ID4gbWVkaWFuX3VuZW1wXSA8LSAiV2VhayINCiAgDQogICMgQ29udmVydCB0byBmYWN0b3Igd2l0aCBkZXNpcmVkIG9yZGVyDQogIGZpbmFsX29icyRlY29uX2NvbmRpdGlvbiA8LSBmYWN0b3IoZmluYWxfb2JzJGVjb25fY29uZGl0aW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJTdHJvbmciLCAiTWl4ZWQiLCAiV2VhayIpKQ0KICANCiAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMNCiAgcmlza19zY29yZXMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IGZpbmFsX29icywgdHlwZSA9ICJyaXNrIikNCiAgDQogICMgQWRkIHNjb3JlcyB0byBkYXRhDQogIGZpbmFsX29icyRyaXNrX3Njb3JlIDwtIHJpc2tfc2NvcmVzDQogIA0KICAjIENyZWF0ZSBzdXJ2aXZhbCBvYmplY3QNCiAgc3Vydl9vYmogPC0gU3Vydih0aW1lID0gZmluYWxfb2JzJHRzdG9wLCBldmVudCA9IGZpbmFsX29ic1tbZXZlbnRfdHlwZV1dKQ0KICANCiAgIyBGaXQgS2FwbGFuLU1laWVyIGN1cnZlcyBieSBlY29ub21pYyBjb25kaXRpb24NCiAga21fZml0IDwtIHN1cnZmaXQoc3Vydl9vYmogfiBlY29uX2NvbmRpdGlvbiwgZGF0YSA9IGZpbmFsX29icykNCiAgDQogICMgUGxvdCBjdXJ2ZXMNCiAgcGxvdChrbV9maXQsIGNvbCA9IGMoImdyZWVuMyIsICJvcmFuZ2UiLCAicmVkIiksIGx3ZCA9IDIsDQogICAgICAgbWFpbiA9IHBhc3RlKCJTdXJ2aXZhbCBDdXJ2ZXMgYnkgRWNvbm9taWMgQ29uZGl0aW9uIC0iLCBtb2RlbF9uYW1lKSwNCiAgICAgICB4bGFiID0gIlRpbWUgKFllYXJzKSIsIHlsYWIgPSAiU3Vydml2YWwgUHJvYmFiaWxpdHkiKQ0KICANCiAgIyBQcmVwYXJlIGxlZ2VuZCB0ZXh0IHdpdGggY291bnRzDQogIGNvbmRpdGlvbl9jb3VudHMgPC0gdGFibGUoZmluYWxfb2JzJGVjb25fY29uZGl0aW9uKQ0KICBjb25kaXRpb25fZXZlbnRzIDwtIHNhcHBseShsZXZlbHMoZmluYWxfb2JzJGVjb25fY29uZGl0aW9uKSwgZnVuY3Rpb24oY29uZCkgew0KICAgIHN1bShmaW5hbF9vYnNbW2V2ZW50X3R5cGVdXVtmaW5hbF9vYnMkZWNvbl9jb25kaXRpb24gPT0gY29uZF0pDQogIH0pDQogIA0KICBsZWdlbmRfdGV4dCA8LSBwYXN0ZTAobGV2ZWxzKGZpbmFsX29icyRlY29uX2NvbmRpdGlvbiksICIgRWNvbm9teSAobj0iLCANCiAgICAgICAgICAgICAgICAgICAgICBjb25kaXRpb25fY291bnRzLCAiLCBldmVudHM9IiwgY29uZGl0aW9uX2V2ZW50cywgIikiKQ0KICANCiAgIyBBZGQgbGVnZW5kDQogIGxlZ2VuZCgiYm90dG9tbGVmdCIsIGxlZ2VuZCA9IGxlZ2VuZF90ZXh0LA0KICAgICAgICAgY29sID0gYygiZ3JlZW4zIiwgIm9yYW5nZSIsICJyZWQiKSwgbHdkID0gMiwgYnR5ID0gIm4iKQ0KICANCiAgIyBMb2ctcmFuayB0ZXN0DQogIGxvZ19yYW5rIDwtIHN1cnZkaWZmKHN1cnZfb2JqIH4gZWNvbl9jb25kaXRpb24sIGRhdGEgPSBmaW5hbF9vYnMpDQogIA0KICBjYXQoIlxuTG9nLXJhbmsgdGVzdCBjb21wYXJpbmcgc3Vydml2YWwgY3VydmVzIGJ5IGVjb25vbWljIGNvbmRpdGlvbjpcbiIpDQogIHByaW50KGxvZ19yYW5rKQ0KICANCiAgaWYobG9nX3JhbmskY2hpc3EgPiBxY2hpc3EoMC45NSwgZGYgPSBsZW5ndGgobGV2ZWxzKGZpbmFsX29icyRlY29uX2NvbmRpdGlvbikpIC0gMSkpIHsNCiAgICBjYXQoIlRoZSBzdXJ2aXZhbCBjdXJ2ZXMgZGlmZmVyIHNpZ25pZmljYW50bHkgYnkgZWNvbm9taWMgY29uZGl0aW9uIChwIDwgMC4wNSkuXG4iKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiTm8gc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBzdXJ2aXZhbCBjdXJ2ZXMgYnkgZWNvbm9taWMgY29uZGl0aW9uIChwID49IDAuMDUpLlxuIikNCiAgfQ0KICANCiAgIyBDYWxjdWxhdGUgZXZlbnQgcmF0ZXMgYnkgZWNvbm9taWMgY29uZGl0aW9uDQogIGNhdCgiXG5FdmVudCByYXRlcyBieSBlY29ub21pYyBjb25kaXRpb246XG4iKQ0KICBmb3IoY29uZCBpbiBsZXZlbHMoZmluYWxfb2JzJGVjb25fY29uZGl0aW9uKSkgew0KICAgIG5fY29tcGFuaWVzIDwtIHN1bShmaW5hbF9vYnMkZWNvbl9jb25kaXRpb24gPT0gY29uZCkNCiAgICBuX2V2ZW50cyA8LSBzdW0oZmluYWxfb2JzW1tldmVudF90eXBlXV1bZmluYWxfb2JzJGVjb25fY29uZGl0aW9uID09IGNvbmRdKQ0KICAgIGV2ZW50X3JhdGUgPC0gbl9ldmVudHMgLyBuX2NvbXBhbmllcyAqIDEwMA0KICAgIA0KICAgIGNhdChjb25kLCAiRWNvbm9teToiLCBuX2V2ZW50cywgImV2ZW50cyBvdXQgb2YiLCBuX2NvbXBhbmllcywgDQogICAgICAgICJjb21wYW5pZXMgKCIsIHJvdW5kKGV2ZW50X3JhdGUsIDEpLCAiJSlcbiIsIHNlcD0iICIpDQogIH0NCiAgDQogICMgQ2FsY3VsYXRlIGF2ZXJhZ2UgcmlzayBzY29yZXMgYnkgZWNvbm9taWMgY29uZGl0aW9uDQogIGNhdCgiXG5BdmVyYWdlIHJpc2sgc2NvcmVzIGJ5IGVjb25vbWljIGNvbmRpdGlvbjpcbiIpDQogIGZvcihjb25kIGluIGxldmVscyhmaW5hbF9vYnMkZWNvbl9jb25kaXRpb24pKSB7DQogICAgYXZnX3Jpc2sgPC0gbWVhbihmaW5hbF9vYnMkcmlza19zY29yZVtmaW5hbF9vYnMkZWNvbl9jb25kaXRpb24gPT0gY29uZF0pDQogICAgY2F0KGNvbmQsICJFY29ub215OiIsIHJvdW5kKGF2Z19yaXNrLCA0KSwgIlxuIikNCiAgfQ0KICANCiAgIyBBbHRlcm5hdGl2ZSB2aXN1YWxpemF0aW9uOiBib3hwbG90IG9mIHJpc2sgc2NvcmVzIGJ5IGVjb25vbWljIGNvbmRpdGlvbg0KICBib3hwbG90KHJpc2tfc2NvcmUgfiBlY29uX2NvbmRpdGlvbiwgZGF0YSA9IGZpbmFsX29icywNCiAgICAgICAgICBtYWluID0gcGFzdGUobW9kZWxfbmFtZSwgIlJpc2sgYnkgRWNvbm9taWMgQ29uZGl0aW9uIiksDQogICAgICAgICAgeGxhYiA9ICJFY29ub21pYyBDb25kaXRpb24iLCB5bGFiID0gIlJpc2sgU2NvcmUiLA0KICAgICAgICAgIGNvbCA9IGMoImdyZWVuMyIsICJvcmFuZ2UiLCAicmVkIikpDQogIA0KICAjIEFkZCBldmVudCByYXRlcyB0byBib3hwbG90DQogIGZvcihpIGluIDE6bGVuZ3RoKGxldmVscyhmaW5hbF9vYnMkZWNvbl9jb25kaXRpb24pKSkgew0KICAgIGNvbmQgPC0gbGV2ZWxzKGZpbmFsX29icyRlY29uX2NvbmRpdGlvbilbaV0NCiAgICBldmVudF9yYXRlIDwtIHN1bShmaW5hbF9vYnNbW2V2ZW50X3R5cGVdXVtmaW5hbF9vYnMkZWNvbl9jb25kaXRpb24gPT0gY29uZF0pIC8gDQogICAgICAgICAgICAgICBzdW0oZmluYWxfb2JzJGVjb25fY29uZGl0aW9uID09IGNvbmQpICogMTAwDQogICAgDQogICAgdGV4dChpLCBwYXIoInVzciIpWzNdIC0gMC4wMiAqIGRpZmYocGFyKCJ1c3IiKVszOjRdKSwgDQogICAgICAgICBwYXN0ZTAocm91bmQoZXZlbnRfcmF0ZSwgMSksICIlIGV2ZW50cyIpLCANCiAgICAgICAgIGNleCA9IDAuOCwgeHBkID0gVFJVRSkNCiAgfQ0KICANCiAgIyBSZXR1cm4gdGhlIGRhdGEgd2l0aCBlY29ub21pYyBjb25kaXRpb25zIGFuZCByaXNrIHNjb3Jlcw0KICByZXR1cm4obGlzdCgNCiAgICBkYXRhID0gZmluYWxfb2JzLA0KICAgIGttX2ZpdCA9IGttX2ZpdCwNCiAgICBsb2dfcmFuayA9IGxvZ19yYW5rDQogICkpDQp9DQoNCiMgQ3JlYXRlIHN1cnZpdmFsIGN1cnZlcyBieSBlY29ub21pYyBjb25kaXRpb24NCmJhbmtydXB0Y3lfZWNvbiA8LSB2aXN1YWxpemVfc3Vydml2YWxfYnlfZWNvbm9teShmaW5hbF9iYW5rcnVwdGN5X21vZGVsLCAiQmFua3J1cHRjeSIsICJiYW5rcnVwdGN5IikNCmFjcXVpc2l0aW9uX2Vjb24gPC0gdmlzdWFsaXplX3N1cnZpdmFsX2J5X2Vjb25vbXkoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwsICJBY3F1aXNpdGlvbiIsICJhY3F1aXNpdGlvbiIpDQoNCmBgYA0KRm9yIG1vcmUgZXhwbG9yYXRpb24gd2UgY2FuIGFuYWx5emUgdGhlIHNlbnNpdGl2aXR5IG9mIHRoZSBtb2RlbHMgdG8gZWNvbm9taWMgY3ljbGVzLiBUaGlzIHdpbGwgaGVscCB1cyB1bmRlcnN0YW5kIGhvdyB0aGUgbW9kZWxzIHBlcmZvcm0gdW5kZXIgZGlmZmVyZW50IGVjb25vbWljIGNvbmRpdGlvbnMgYW5kIHdoZXRoZXIgdGhleSBhcmUgcm9idXN0IHRvIGNoYW5nZXMgaW4gdGhlIGVjb25vbWljIGVudmlyb25tZW50Lg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxMy41OiBFY29ub21pYyBDeWNsZSBTZW5zaXRpdml0eSBBbmFseXNpcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMy41OiBFQ09OT01JQyBDWUNMRSBTRU5TSVRJVklUWSBBTkFMWVNJU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCmFuYWx5emVfZWNvbm9taWNfc2Vuc2l0aXZpdHkgPC0gZnVuY3Rpb24oKSB7DQogIGNhdCgiQW5hbHl6aW5nIHNlbnNpdGl2aXR5IHRvIGVjb25vbWljIGN5Y2xlcy4uLlxuIikNCiAgDQogICMgQ2hlY2sgaWYgd2UgaGF2ZSBlY29ub21pYyBpbmRpY2F0b3JzIGluIHRoZSBkYXRhDQogIGhhc19tYWNybyA8LSBhbGwoYygiZ2RwX2dyb3d0aCIsICJ1bmVtcGxveWVtZW50IikgJWluJSBjb2xuYW1lcyhkYXRhKSkNCiAgDQogIGlmKCFoYXNfbWFjcm8pIHsNCiAgICBjYXQoIkVjb25vbWljIGluZGljYXRvcnMgbm90IGF2YWlsYWJsZSBmb3IgY3ljbGUgc2Vuc2l0aXZpdHkgYW5hbHlzaXMuXG4iKQ0KICAgIHJldHVybihOVUxMKQ0KICB9DQogIA0KICAjIEdldCBsYXN0IG9ic2VydmF0aW9uIGZvciBlYWNoIGNvbXBhbnkgYW5kIHllYXINCiAgbGFzdF9ieV95ZWFyIDwtIGRhdGEgJT4lDQogICAgZ3JvdXBfYnkoY3VzaXAsIGNhbGVuZGFyX3llYXIpICU+JQ0KICAgIHNsaWNlX21heCh0c3RvcCkgJT4lDQogICAgdW5ncm91cCgpDQogIA0KICAjIENhbGN1bGF0ZSBhbm51YWwgZXZlbnQgcmF0ZXMgLSB1c2luZyBiYXNlIFIgYWdncmVnYXRpb24gdG8gYXZvaWQgZHBseXIgZGVwZW5kZW5jeQ0KICBhbm51YWxfc3RhdHMgPC0gYWdncmVnYXRlKGNiaW5kKGJhbmtydXB0Y3ksIGFjcXVpc2l0aW9uLCBnZHBfZ3Jvd3RoLCB1bmVtcGxveWVtZW50LCBnZHBfZGVmbGF0b3IpIH4gY2FsZW5kYXJfeWVhciwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBsYXN0X2J5X3llYXIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSBjKHN1bSA9IHN1bSh4KSwgbWVhbiA9IG1lYW4oeCwgbmEucm0gPSBUUlVFKSkpDQogIA0KICAjIFJlc2hhcGUgdGhlIG91dHB1dCB0byB0aGUgZGVzaXJlZCBmb3JtYXQNCiAgYW5udWFsX3N0YXRzIDwtIGRhdGEuZnJhbWUoDQogICAgY2FsZW5kYXJfeWVhciA9IGFubnVhbF9zdGF0cyRjYWxlbmRhcl95ZWFyLA0KICAgIG5fY29tcGFuaWVzID0gYXMubnVtZXJpYyh0YWJsZShsYXN0X2J5X3llYXIkY2FsZW5kYXJfeWVhcikpLA0KICAgIG5fYmFua3J1cHRjaWVzID0gYW5udWFsX3N0YXRzJGJhbmtydXB0Y3lbLCAic3VtIl0sDQogICAgbl9hY3F1aXNpdGlvbnMgPSBhbm51YWxfc3RhdHMkYWNxdWlzaXRpb25bLCAic3VtIl0sDQogICAgYmFua3J1cHRjeV9yYXRlID0gYW5udWFsX3N0YXRzJGJhbmtydXB0Y3lbLCAibWVhbiJdICogMTAwLA0KICAgIGFjcXVpc2l0aW9uX3JhdGUgPSBhbm51YWxfc3RhdHMkYWNxdWlzaXRpb25bLCAibWVhbiJdICogMTAwLA0KICAgIGdkcF9ncm93dGggPSBhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aFssICJtZWFuIl0sDQogICAgdW5lbXBsb3ltZW50ID0gYW5udWFsX3N0YXRzJHVuZW1wbG95ZW1lbnRbLCAibWVhbiJdLA0KICAgIGluZmxhdGlvbiA9IGFubnVhbF9zdGF0cyRnZHBfZGVmbGF0b3JbLCAibWVhbiJdDQogICkNCiAgDQogICMgU29ydCBieSB5ZWFyDQogIGFubnVhbF9zdGF0cyA8LSBhbm51YWxfc3RhdHNbb3JkZXIoYW5udWFsX3N0YXRzJGNhbGVuZGFyX3llYXIpLCBdDQogIA0KICAjIDEuIENyZWF0ZSB0aW1lIHNlcmllcyBwbG90IG9mIGV2ZW50IHJhdGVzIGFuZCBlY29ub21pYyBpbmRpY2F0b3JzDQogICMgU2V0IHVwIG11bHRpLXBhbmVsIHBsb3QgKDIgcm93cywgMSBjb2x1bW4pDQogIHBhcihtZnJvdyA9IGMoMiwgMSksIG1hciA9IGMoMywgNCwgMiwgNCkpDQogIA0KICAjIFBsb3QgZXZlbnQgcmF0ZXMgb3ZlciB0aW1lDQogIHBsb3QoYW5udWFsX3N0YXRzJGNhbGVuZGFyX3llYXIsIGFubnVhbF9zdGF0cyRiYW5rcnVwdGN5X3JhdGUsIA0KICAgICAgIHR5cGUgPSAibyIsIGNvbCA9ICJyZWQiLCBsd2QgPSAyLCBwY2ggPSAxNiwNCiAgICAgICB4bGFiID0gIiIsIHlsYWIgPSAiRXZlbnQgUmF0ZSAoJSkiLA0KICAgICAgIG1haW4gPSAiQ29ycG9yYXRlIEV2ZW50IFJhdGVzIE92ZXIgVGltZSIsDQogICAgICAgeWxpbSA9IGMoMCwgbWF4KGMoYW5udWFsX3N0YXRzJGJhbmtydXB0Y3lfcmF0ZSwgYW5udWFsX3N0YXRzJGFjcXVpc2l0aW9uX3JhdGUpKSAqIDEuMikpDQogIA0KICAjIEFkZCBhY3F1aXNpdGlvbiByYXRlcw0KICBsaW5lcyhhbm51YWxfc3RhdHMkY2FsZW5kYXJfeWVhciwgYW5udWFsX3N0YXRzJGFjcXVpc2l0aW9uX3JhdGUsIA0KICAgICAgICB0eXBlID0gIm8iLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIsIHBjaCA9IDE3KQ0KICANCiAgIyBBZGQgbGVnZW5kDQogIGxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBjKCJCYW5rcnVwdGN5IFJhdGUiLCAiQWNxdWlzaXRpb24gUmF0ZSIpLA0KICAgICAgICAgY29sID0gYygicmVkIiwgImJsdWUiKSwgbHdkID0gMiwgcGNoID0gYygxNiwgMTcpLCBidHkgPSAibiIpDQogIA0KICAjIFBsb3QgZWNvbm9taWMgaW5kaWNhdG9ycw0KICBwbG90KGFubnVhbF9zdGF0cyRjYWxlbmRhcl95ZWFyLCBhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCwgDQogICAgICAgdHlwZSA9ICJvIiwgY29sID0gImdyZWVuNCIsIGx3ZCA9IDIsIHBjaCA9IDE1LA0KICAgICAgIHhsYWIgPSAiWWVhciIsIHlsYWIgPSAiR0RQIEdyb3d0aCAoJSkiLA0KICAgICAgIG1haW4gPSAiRWNvbm9taWMgSW5kaWNhdG9ycyBPdmVyIFRpbWUiLA0KICAgICAgIHlsaW0gPSBjKG1pbihhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCkgKiAxLjIsIG1heChhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCkgKiAxLjIpKQ0KICANCiAgIyBBZGQgdW5lbXBsb3ltZW50IG9uIHNlY29uZGFyeSB5LWF4aXMNCiAgcGFyKG5ldyA9IFRSVUUpDQogIHBsb3QoYW5udWFsX3N0YXRzJGNhbGVuZGFyX3llYXIsIGFubnVhbF9zdGF0cyR1bmVtcGxveW1lbnQsIA0KICAgICAgIHR5cGUgPSAibyIsIGNvbCA9ICJicm93biIsIGx3ZCA9IDIsIHBjaCA9IDE4LA0KICAgICAgIHhsYWIgPSAiIiwgeWxhYiA9ICIiLCBheGVzID0gRkFMU0UsDQogICAgICAgeWxpbSA9IGMobWluKGFubnVhbF9zdGF0cyR1bmVtcGxveW1lbnQpICogMC45LCBtYXgoYW5udWFsX3N0YXRzJHVuZW1wbG95bWVudCkgKiAxLjEpKQ0KICANCiAgIyBBZGQgc2Vjb25kYXJ5IHktYXhpcw0KICBheGlzKDQsIGNvbCA9ICJicm93biIsIGNvbC5heGlzID0gImJyb3duIikNCiAgbXRleHQoIlVuZW1wbG95bWVudCBSYXRlICglKSIsIHNpZGUgPSA0LCBsaW5lID0gMiwgY29sID0gImJyb3duIikNCiAgDQogICMgQWRkIGxlZ2VuZA0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gYygiR0RQIEdyb3d0aCIsICJVbmVtcGxveW1lbnQiKSwNCiAgICAgICAgIGNvbCA9IGMoImdyZWVuNCIsICJicm93biIpLCBsd2QgPSAyLCBwY2ggPSBjKDE1LCAxOCksIGJ0eSA9ICJuIikNCiAgDQogICMgUmVzZXQgcGxvdHRpbmcgcGFyYW1ldGVycw0KICBwYXIobWZyb3cgPSBjKDEsIDEpLCBtYXIgPSBjKDUsIDQsIDQsIDIpICsgMC4xKQ0KICANCiAgIyAyLiBDcmVhdGUgc2NhdHRlciBwbG90cyBvZiBldmVudCByYXRlcyB2cy4gZWNvbm9taWMgaW5kaWNhdG9ycw0KICAjIFNldCB1cCBtdWx0aS1wYW5lbCBwbG90ICgxIHJvdywgMiBjb2x1bW5zKQ0KICBwYXIobWZyb3cgPSBjKDEsIDIpKQ0KICANCiAgIyBTY2F0dGVyIHBsb3QgZm9yIEdEUCBncm93dGgNCiAgcGxvdChhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCwgYW5udWFsX3N0YXRzJGJhbmtydXB0Y3lfcmF0ZSwgDQogICAgICAgY29sID0gInJlZCIsIHBjaCA9IDE2LCBjZXggPSAxLjIsDQogICAgICAgeGxhYiA9ICJHRFAgR3Jvd3RoICglKSIsIHlsYWIgPSAiRXZlbnQgUmF0ZSAoJSkiLA0KICAgICAgIG1haW4gPSAiRXZlbnQgUmF0ZXMgdnMuIEdEUCBHcm93dGgiLA0KICAgICAgIHlsaW0gPSBjKDAsIG1heChjKGFubnVhbF9zdGF0cyRiYW5rcnVwdGN5X3JhdGUsIGFubnVhbF9zdGF0cyRhY3F1aXNpdGlvbl9yYXRlKSkgKiAxLjIpLA0KICAgICAgIHhsaW0gPSBjKG1pbihhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCkgKiAxLjIsIG1heChhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCkgKiAxLjIpKQ0KICANCiAgcG9pbnRzKGFubnVhbF9zdGF0cyRnZHBfZ3Jvd3RoLCBhbm51YWxfc3RhdHMkYWNxdWlzaXRpb25fcmF0ZSwgDQogICAgICAgICBjb2wgPSAiYmx1ZSIsIHBjaCA9IDE3LCBjZXggPSAxLjIpDQogIA0KICAjIEFkZCByZWdyZXNzaW9uIGxpbmVzDQogIGFibGluZShsbShiYW5rcnVwdGN5X3JhdGUgfiBnZHBfZ3Jvd3RoLCBkYXRhID0gYW5udWFsX3N0YXRzKSwgY29sID0gInJlZCIsIGx0eSA9IDIpDQogIGFibGluZShsbShhY3F1aXNpdGlvbl9yYXRlIH4gZ2RwX2dyb3d0aCwgZGF0YSA9IGFubnVhbF9zdGF0cyksIGNvbCA9ICJibHVlIiwgbHR5ID0gMikNCiAgDQogICMgQWRkIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cw0KICBiYW5rX2NvciA8LSBjb3IoYW5udWFsX3N0YXRzJGdkcF9ncm93dGgsIGFubnVhbF9zdGF0cyRiYW5rcnVwdGN5X3JhdGUsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KICBhY3FfY29yIDwtIGNvcihhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCwgYW5udWFsX3N0YXRzJGFjcXVpc2l0aW9uX3JhdGUsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KICANCiAgdGV4dChtaW4oYW5udWFsX3N0YXRzJGdkcF9ncm93dGgpICogMS4xLCBtYXgoYW5udWFsX3N0YXRzJGJhbmtydXB0Y3lfcmF0ZSkgKiAxLjEsDQogICAgICAgcGFzdGUwKCJCYW5rIHIgPSAiLCByb3VuZChiYW5rX2NvciwgMikpLCBjb2wgPSAicmVkIiwgY2V4ID0gMC44KQ0KICB0ZXh0KG1pbihhbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCkgKiAxLjEsIG1heChhbm51YWxfc3RhdHMkYmFua3J1cHRjeV9yYXRlKSAqIDEuMCwNCiAgICAgICBwYXN0ZTAoIkFjcSByID0gIiwgcm91bmQoYWNxX2NvciwgMikpLCBjb2wgPSAiYmx1ZSIsIGNleCA9IDAuOCkNCiAgDQogICMgQWRkIGxlZ2VuZA0KICBsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQgPSBjKCJCYW5rcnVwdGN5IFJhdGUiLCAiQWNxdWlzaXRpb24gUmF0ZSIpLA0KICAgICAgICAgY29sID0gYygicmVkIiwgImJsdWUiKSwgcGNoID0gYygxNiwgMTcpLCBsdHkgPSAyLCBidHkgPSAibiIpDQogIA0KICAjIFNjYXR0ZXIgcGxvdCBmb3IgdW5lbXBsb3ltZW50DQogIHBsb3QoYW5udWFsX3N0YXRzJHVuZW1wbG95bWVudCwgYW5udWFsX3N0YXRzJGJhbmtydXB0Y3lfcmF0ZSwgDQogICAgICAgY29sID0gInJlZCIsIHBjaCA9IDE2LCBjZXggPSAxLjIsDQogICAgICAgeGxhYiA9ICJVbmVtcGxveW1lbnQgUmF0ZSAoJSkiLCB5bGFiID0gIkV2ZW50IFJhdGUgKCUpIiwNCiAgICAgICBtYWluID0gIkV2ZW50IFJhdGVzIHZzLiBVbmVtcGxveW1lbnQiLA0KICAgICAgIHlsaW0gPSBjKDAsIG1heChjKGFubnVhbF9zdGF0cyRiYW5rcnVwdGN5X3JhdGUsIGFubnVhbF9zdGF0cyRhY3F1aXNpdGlvbl9yYXRlKSkgKiAxLjIpLA0KICAgICAgIHhsaW0gPSBjKG1pbihhbm51YWxfc3RhdHMkdW5lbXBsb3ltZW50KSAqIDAuOSwgbWF4KGFubnVhbF9zdGF0cyR1bmVtcGxveW1lbnQpICogMS4xKSkNCiAgDQogIHBvaW50cyhhbm51YWxfc3RhdHMkdW5lbXBsb3ltZW50LCBhbm51YWxfc3RhdHMkYWNxdWlzaXRpb25fcmF0ZSwgDQogICAgICAgICBjb2wgPSAiYmx1ZSIsIHBjaCA9IDE3LCBjZXggPSAxLjIpDQogIA0KICAjIEFkZCByZWdyZXNzaW9uIGxpbmVzDQogIGFibGluZShsbShiYW5rcnVwdGN5X3JhdGUgfiB1bmVtcGxveW1lbnQsIGRhdGEgPSBhbm51YWxfc3RhdHMpLCBjb2wgPSAicmVkIiwgbHR5ID0gMikNCiAgYWJsaW5lKGxtKGFjcXVpc2l0aW9uX3JhdGUgfiB1bmVtcGxveW1lbnQsIGRhdGEgPSBhbm51YWxfc3RhdHMpLCBjb2wgPSAiYmx1ZSIsIGx0eSA9IDIpDQogIA0KICAjIEFkZCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMNCiAgYmFua19jb3IgPC0gY29yKGFubnVhbF9zdGF0cyR1bmVtcGxveW1lbnQsIGFubnVhbF9zdGF0cyRiYW5rcnVwdGN5X3JhdGUsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KICBhY3FfY29yIDwtIGNvcihhbm51YWxfc3RhdHMkdW5lbXBsb3ltZW50LCBhbm51YWxfc3RhdHMkYWNxdWlzaXRpb25fcmF0ZSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQogIA0KICB0ZXh0KG1heChhbm51YWxfc3RhdHMkdW5lbXBsb3ltZW50KSAqIDAuOTUsIG1heChhbm51YWxfc3RhdHMkYmFua3J1cHRjeV9yYXRlKSAqIDEuMSwNCiAgICAgICBwYXN0ZTAoIkJhbmsgciA9ICIsIHJvdW5kKGJhbmtfY29yLCAyKSksIGNvbCA9ICJyZWQiLCBjZXggPSAwLjgpDQogIHRleHQobWF4KGFubnVhbF9zdGF0cyR1bmVtcGxveW1lbnQpICogMC45NSwgbWF4KGFubnVhbF9zdGF0cyRiYW5rcnVwdGN5X3JhdGUpICogMS4wLA0KICAgICAgIHBhc3RlMCgiQWNxIHIgPSAiLCByb3VuZChhY3FfY29yLCAyKSksIGNvbCA9ICJibHVlIiwgY2V4ID0gMC44KQ0KICANCiAgIyBSZXNldCBwbG90dGluZyBwYXJhbWV0ZXJzDQogIHBhcihtZnJvdyA9IGMoMSwgMSkpDQogIA0KICAjIENvbXB1dGUgY29ycmVsYXRpb24gbWF0cml4IGJldHdlZW4gZXZlbnQgcmF0ZXMgYW5kIGVjb25vbWljIGluZGljYXRvcnMNCiAgY29ycl92YXJzIDwtIGMoImJhbmtydXB0Y3lfcmF0ZSIsICJhY3F1aXNpdGlvbl9yYXRlIiwgImdkcF9ncm93dGgiLCAidW5lbXBsb3ltZW50IiwgImluZmxhdGlvbiIpDQogIGNvcnJfbWF0cml4IDwtIGNvcihhbm51YWxfc3RhdHNbLCBjb3JyX3ZhcnNdLCB1c2UgPSAiY29tcGxldGUub2JzIikNCiAgDQogICMgUHJpbnQgY29ycmVsYXRpb24gbWF0cml4DQogIGNhdCgiXG5Db3JyZWxhdGlvbiBNYXRyaXggb2YgRXZlbnQgUmF0ZXMgYW5kIEVjb25vbWljIEluZGljYXRvcnM6XG4iKQ0KICBwcmludChyb3VuZChjb3JyX21hdHJpeCwgMykpDQogIA0KICAjIENsYXNzaWZpY2F0aW9uIG9mIGVjb25vbWljIHBlcmlvZHMNCiAgYW5udWFsX3N0YXRzJGVjb25fcGVyaW9kIDwtICJOb3JtYWwiDQogIGFubnVhbF9zdGF0cyRlY29uX3BlcmlvZFthbm51YWxfc3RhdHMkZ2RwX2dyb3d0aCA+IHF1YW50aWxlKGFubnVhbF9zdGF0cyRnZHBfZ3Jvd3RoLCAwLjY3LCBuYS5ybSA9IFRSVUUpICYNCiAgICAgICAgICAgICAgICAgICAgICAgICBhbm51YWxfc3RhdHMkdW5lbXBsb3ltZW50IDwgcXVhbnRpbGUoYW5udWFsX3N0YXRzJHVuZW1wbG95bWVudCwgMC4zMywgbmEucm0gPSBUUlVFKV0gPC0gIkV4cGFuc2lvbiINCiAgYW5udWFsX3N0YXRzJGVjb25fcGVyaW9kW2FubnVhbF9zdGF0cyRnZHBfZ3Jvd3RoIDwgcXVhbnRpbGUoYW5udWFsX3N0YXRzJGdkcF9ncm93dGgsIDAuMzMsIG5hLnJtID0gVFJVRSkgJg0KICAgICAgICAgICAgICAgICAgICAgICAgIGFubnVhbF9zdGF0cyR1bmVtcGxveW1lbnQgPiBxdWFudGlsZShhbm51YWxfc3RhdHMkdW5lbXBsb3ltZW50LCAwLjY3LCBuYS5ybSA9IFRSVUUpXSA8LSAiUmVjZXNzaW9uIg0KICANCiAgIyBDYWxjdWxhdGUgYXZlcmFnZSBldmVudCByYXRlcyBieSBlY29ub21pYyBwZXJpb2QgdXNpbmcgYmFzZSBSDQogIHBlcmlvZF9zdGF0cyA8LSBhZ2dyZWdhdGUoDQogICAgY2JpbmQoYmFua3J1cHRjeV9yYXRlLCBhY3F1aXNpdGlvbl9yYXRlKSB+IGVjb25fcGVyaW9kLCANCiAgICBkYXRhID0gYW5udWFsX3N0YXRzLCANCiAgICBGVU4gPSBtZWFuDQogICkNCiAgDQogICMgQWRkIGNvdW50IG9mIHllYXJzIHBlciBwZXJpb2QNCiAgcGVyaW9kX2NvdW50cyA8LSB0YWJsZShhbm51YWxfc3RhdHMkZWNvbl9wZXJpb2QpDQogIHBlcmlvZF9zdGF0cyRuX3llYXJzIDwtIHBlcmlvZF9jb3VudHNbbWF0Y2gocGVyaW9kX3N0YXRzJGVjb25fcGVyaW9kLCBuYW1lcyhwZXJpb2RfY291bnRzKSldDQogIA0KICAjIFJlbmFtZSBjb2x1bW5zIHRvIG1hdGNoIHdpdGggdGhlIHByZXZpb3VzIHN0cnVjdHVyZSBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIHRoZSBiYXJwbG90DQogIG5hbWVzKHBlcmlvZF9zdGF0cylbbmFtZXMocGVyaW9kX3N0YXRzKSA9PSAiYmFua3J1cHRjeV9yYXRlIl0gPC0gImF2Z19iYW5rcnVwdGN5X3JhdGUiDQogIG5hbWVzKHBlcmlvZF9zdGF0cylbbmFtZXMocGVyaW9kX3N0YXRzKSA9PSAiYWNxdWlzaXRpb25fcmF0ZSJdIDwtICJhdmdfYWNxdWlzaXRpb25fcmF0ZSINCiAgDQogIGNhdCgiXG5BdmVyYWdlIEV2ZW50IFJhdGVzIGJ5IEVjb25vbWljIFBlcmlvZDpcbiIpDQogIHByaW50KHBlcmlvZF9zdGF0cykNCiAgDQogICMgQ3JlYXRlIGJhciBwbG90IG9mIGV2ZW50IHJhdGVzIGJ5IGVjb25vbWljIHBlcmlvZCAtIHdpdGggZXJyb3IgaGFuZGxpbmcNCiAgdHJ5Q2F0Y2goew0KICAgICMgQ3JlYXRlIG1hdHJpeCBwcm9wZXJseSBmcm9tIHRoZSBwZXJpb2Rfc3RhdHMgZGF0YWZyYW1lDQogICAgYmFycGxvdF9kYXRhIDwtIGFzLm1hdHJpeChwZXJpb2Rfc3RhdHNbLCBjKCJhdmdfYmFua3J1cHRjeV9yYXRlIiwgImF2Z19hY3F1aXNpdGlvbl9yYXRlIiksIGRyb3A9RkFMU0VdKQ0KICAgIHJvd25hbWVzKGJhcnBsb3RfZGF0YSkgPC0gcGVyaW9kX3N0YXRzJGVjb25fcGVyaW9kDQogICAgDQogICAgYmFycGxvdCh0KGJhcnBsb3RfZGF0YSksIGJlc2lkZSA9IFRSVUUsDQogICAgICAgICAgICBtYWluID0gIkV2ZW50IFJhdGVzIGJ5IEVjb25vbWljIFBlcmlvZCIsDQogICAgICAgICAgICB4bGFiID0gIkVjb25vbWljIFBlcmlvZCIsIHlsYWIgPSAiQXZlcmFnZSBFdmVudCBSYXRlICglKSIsDQogICAgICAgICAgICBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIpLA0KICAgICAgICAgICAgeWxpbSA9IGMoMCwgbWF4KGJhcnBsb3RfZGF0YSwgbmEucm0gPSBUUlVFKSAqIDEuMikpDQogICAgDQogICAgIyBBZGQgbGVnZW5kDQogICAgbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoIkJhbmtydXB0Y3kgUmF0ZSIsICJBY3F1aXNpdGlvbiBSYXRlIiksDQogICAgICAgICAgIGZpbGwgPSBjKCJyZWQiLCAiYmx1ZSIpLCBidHkgPSAibiIpDQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgIGNhdCgiV2FybmluZzogQ291bGQgbm90IGNyZWF0ZSBiYXJwbG90IG9mIGV2ZW50IHJhdGVzIGJ5IGVjb25vbWljIHBlcmlvZDoiLCBlJG1lc3NhZ2UsICJcbiIpDQogICAgY2F0KCJQcmludGluZyBkYXRhIGZyYW1lIGluc3RlYWQ6XG4iKQ0KICAgIHByaW50KHBlcmlvZF9zdGF0cykNCiAgfSkNCiAgDQogICMgUmV0dXJuIHRoZSBhbm51YWwgc3RhdGlzdGljcyBmb3IgZnVydGhlciBhbmFseXNpcw0KICByZXR1cm4obGlzdCgNCiAgICBhbm51YWxfc3RhdHMgPSBhbm51YWxfc3RhdHMsDQogICAgcGVyaW9kX3N0YXRzID0gcGVyaW9kX3N0YXRzLA0KICAgIGNvcnJlbGF0aW9ucyA9IGNvcnJfbWF0cml4DQogICkpDQp9DQoNCiMgQW5hbHl6ZSBlY29ub21pYyBjeWNsZSBzZW5zaXRpdml0eQ0KZWNvbm9taWNfY3ljbGVzIDwtIGFuYWx5emVfZWNvbm9taWNfc2Vuc2l0aXZpdHkoKQ0KDQpgYGANCk5vdCBzdXJlIGlmIHRoaXMgaGFzIGFueSB2YWx1ZS4uLiANCg0KV2UgYWxzbyBsb29rIGF0IHNlY3Rvci1zcGVjaWZpYyBtYWNyb2Vjb25vbWljIHNlbnNpdGl2aXR5LiBUaGlzIHdpbGwgaGVscCB1cyB1bmRlcnN0YW5kIGhvdyBkaWZmZXJlbnQgc2VjdG9ycyByZXNwb25kIHRvIG1hY3JvZWNvbm9taWMgY2hhbmdlcyBhbmQgd2hldGhlciB0aGVyZSBhcmUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgaW4gdGhlaXIgc2Vuc2l0aXZpdGllcy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuMTA6IFNlY3Rvci1TcGVjaWZpYyBNYWNyb2Vjb25vbWljIFNlbnNpdGl2aXR5DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjEzLjEwOiBTRUNUT1ItU1BFQ0lGSUMgTUFDUk9FQ09OT01JQyBTRU5TSVRJVklUWVxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCmFuYWx5emVfc2VjdG9yX21hY3JvX3NlbnNpdGl2aXR5IDwtIGZ1bmN0aW9uKCkgew0KICBjYXQoIkFuYWx5emluZyBzZWN0b3Itc3BlY2lmaWMgc2Vuc2l0aXZpdHkgdG8gbWFjcm9lY29ub21pYyBmYWN0b3JzLi4uXG4iKQ0KICANCiAgIyBDaGVjayBpZiB3ZSBoYXZlIG1hY3JvIHZhcmlhYmxlcyBpbiBvdXIgZGF0YXNldA0KICBpZighYWxsKGMoImdkcF9ncm93dGgiLCAidW5lbXBsb3llbWVudCIsICJnZHBfZGVmbGF0b3IiKSAlaW4lIGNvbG5hbWVzKGRhdGEpKSkgew0KICAgIGNhdCgiTWFjcm8gdmFyaWFibGVzIG5vdCBmb3VuZCBpbiBkYXRhc2V0LiBDYW5ub3QgcGVyZm9ybSBzZWN0b3IgYW5hbHlzaXMuXG4iKQ0KICAgIHJldHVybihOVUxMKQ0KICB9DQogIA0KICAjIEdldCBsYXN0IG9ic2VydmF0aW9uIGZvciBlYWNoIGNvbXBhbnkNCiAgZmluYWxfb2JzIDwtIGRhdGEgJT4lDQogICAgZ3JvdXBfYnkoY3VzaXApICU+JQ0KICAgIHNsaWNlX21heCh0c3RvcCkgJT4lDQogICAgdW5ncm91cCgpDQogIA0KICAjIEVuc3VyZSBnc2VjdG9yIGlzIGEgZmFjdG9yDQogIGZpbmFsX29icyRnc2VjdG9yIDwtIGFzLmZhY3RvcihmaW5hbF9vYnMkZ3NlY3RvcikNCiAgDQogICMgR2V0IHNlY3RvcnMgd2l0aCBzdWZmaWNpZW50IGRhdGENCiAgc2VjdG9yX2NvdW50cyA8LSB0YWJsZShmaW5hbF9vYnMkZ3NlY3RvcikNCiAgdmFsaWRfc2VjdG9ycyA8LSBuYW1lcyhzZWN0b3JfY291bnRzKVtzZWN0b3JfY291bnRzID49IDMwXQ0KICANCiAgaWYobGVuZ3RoKHZhbGlkX3NlY3RvcnMpID09IDApIHsNCiAgICBjYXQoIk5vdCBlbm91Z2ggc2VjdG9ycyB3aXRoIHN1ZmZpY2llbnQgb2JzZXJ2YXRpb25zIGZvciBhbmFseXNpcy5cbiIpDQogICAgcmV0dXJuKE5VTEwpDQogIH0NCiAgDQogICMgRmlsdGVyIHRvIHZhbGlkIHNlY3RvcnMNCiAgc2VjdG9yX2RhdGEgPC0gZmluYWxfb2JzW2ZpbmFsX29icyRnc2VjdG9yICVpbiUgdmFsaWRfc2VjdG9ycywgXQ0KICANCiAgIyBJbml0aWFsaXplIG1hdHJpY2VzIHRvIHN0b3JlIGNvcnJlbGF0aW9ucw0KICBzZWN0b3JzIDwtIHVuaXF1ZShzZWN0b3JfZGF0YSRnc2VjdG9yKQ0KICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAidW5lbXBsb3llbWVudCIsICJnZHBfZGVmbGF0b3IiKQ0KICANCiAgIyBDcmVhdGUgc3RvcmFnZSBmb3IgY29ycmVsYXRpb24gbWF0cmljZXMNCiAgc2VjdG9yX2NvcnJlbGF0aW9ucyA8LSBhcnJheShOQSwgZGltID0gYyhsZW5ndGgoc2VjdG9ycyksIDIsIGxlbmd0aChtYWNyb192YXJzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3Qoc2VjdG9ycywgYygiYmFua3J1cHRjeSIsICJhY3F1aXNpdGlvbiIpLCBtYWNyb192YXJzKSkNCiAgDQogICMgQ2FsY3VsYXRlIGNvcnJlbGF0aW9ucyBmb3IgZWFjaCBzZWN0b3IgYW5kIG91dGNvbWUNCiAgZm9yKHMgaW4gc2VjdG9ycykgew0KICAgIHNlY3Rvcl9zdWJzZXQgPC0gc2VjdG9yX2RhdGFbc2VjdG9yX2RhdGEkZ3NlY3RvciA9PSBzLCBdDQogICAgDQogICAgIyBPbmx5IHByb2NlZWQgaWYgd2UgaGF2ZSBzdWZmaWNpZW50IGRhdGENCiAgICBpZihucm93KHNlY3Rvcl9zdWJzZXQpID49IDEwKSB7DQogICAgICBmb3IobSBpbiBtYWNyb192YXJzKSB7DQogICAgICAgIGlmKGFsbChjKG0sICJiYW5rcnVwdGN5IiwgImFjcXVpc2l0aW9uIikgJWluJSBjb2xuYW1lcyhzZWN0b3Jfc3Vic2V0KSkpIHsNCiAgICAgICAgICAjIENhbGN1bGF0ZSBjb3JyZWxhdGlvbnMNCiAgICAgICAgICBjb3JfYmFuayA8LSBjb3Ioc2VjdG9yX3N1YnNldFtbbV1dLCBzZWN0b3Jfc3Vic2V0JGJhbmtydXB0Y3ksIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KICAgICAgICAgIGNvcl9hY3EgPC0gY29yKHNlY3Rvcl9zdWJzZXRbW21dXSwgc2VjdG9yX3N1YnNldCRhY3F1aXNpdGlvbiwgdXNlID0gImNvbXBsZXRlLm9icyIpDQogICAgICAgICAgDQogICAgICAgICAgIyBTdG9yZSBpbiB0aGUgYXJyYXkNCiAgICAgICAgICBzZWN0b3JfY29ycmVsYXRpb25zW3MsICJiYW5rcnVwdGN5IiwgbV0gPC0gY29yX2JhbmsNCiAgICAgICAgICBzZWN0b3JfY29ycmVsYXRpb25zW3MsICJhY3F1aXNpdGlvbiIsIG1dIDwtIGNvcl9hY3ENCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICANCiAgIyBDb252ZXJ0IHRvIG1vcmUgbWFuYWdlYWJsZSBkYXRhIGZyYW1lIGZvciB2aXN1YWxpemF0aW9uDQogIGNvcnJlbGF0aW9uX2RmIDwtIGRhdGEuZnJhbWUoKQ0KICBmb3IocyBpbiBzZWN0b3JzKSB7DQogICAgZm9yKG0gaW4gbWFjcm9fdmFycykgew0KICAgICAgZm9yKG91dGNvbWUgaW4gYygiYmFua3J1cHRjeSIsICJhY3F1aXNpdGlvbiIpKSB7DQogICAgICAgIGNvcnJlbGF0aW9uX2RmIDwtIHJiaW5kKGNvcnJlbGF0aW9uX2RmLCBkYXRhLmZyYW1lKA0KICAgICAgICAgIFNlY3RvciA9IHMsDQogICAgICAgICAgTWFjcm9WYXIgPSBtLA0KICAgICAgICAgIE91dGNvbWUgPSBvdXRjb21lLA0KICAgICAgICAgIENvcnJlbGF0aW9uID0gc2VjdG9yX2NvcnJlbGF0aW9uc1tzLCBvdXRjb21lLCBtXSwNCiAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgICAgICAgKSkNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgDQogICMgQ3JlYXRlIGEgcmVhZGFibGUgbWFjcm8gdmFyaWFibGUgbmFtZSBtYXBwaW5nDQogIGNvcnJlbGF0aW9uX2RmJE1hY3JvVmFyTGFiZWwgPC0gaWZlbHNlKGNvcnJlbGF0aW9uX2RmJE1hY3JvVmFyID09ICJnZHBfZ3Jvd3RoIiwgIkdEUCBHcm93dGgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGNvcnJlbGF0aW9uX2RmJE1hY3JvVmFyID09ICJ1bmVtcGxveWVtZW50IiwgIlVuZW1wbG95bWVudCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkluZmxhdGlvbiIpKQ0KICANCiAgIyBDYWxjdWxhdGUgc2VjdG9yIHNlbnNpdGl2aXR5IHNjb3JlcyAoYXZlcmFnZSBhYnNvbHV0ZSBjb3JyZWxhdGlvbikNCiAgc2VjdG9yX3NlbnNpdGl2aXR5IDwtIGFnZ3JlZ2F0ZShhYnMoQ29ycmVsYXRpb24pIH4gU2VjdG9yLCBkYXRhID0gY29ycmVsYXRpb25fZGYsIEZVTiA9IG1lYW4sIG5hLnJtID0gVFJVRSkNCiAgY29sbmFtZXMoc2VjdG9yX3NlbnNpdGl2aXR5KVsyXSA8LSAiQXZnU2Vuc2l0aXZpdHkiDQogIA0KICAjIFNvcnQgYnkgc2Vuc2l0aXZpdHkNCiAgc2VjdG9yX3NlbnNpdGl2aXR5IDwtIHNlY3Rvcl9zZW5zaXRpdml0eVtvcmRlcihzZWN0b3Jfc2Vuc2l0aXZpdHkkQXZnU2Vuc2l0aXZpdHksIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQ0KICANCiAgIyBDcmVhdGUgYmFycGxvdCBvZiBzZWN0b3Igc2Vuc2l0aXZpdHkNCiAgYmFycGxvdChzZWN0b3Jfc2Vuc2l0aXZpdHkkQXZnU2Vuc2l0aXZpdHksIA0KICAgICAgICAgIG5hbWVzLmFyZyA9IHBhc3RlKCJTZWN0b3IiLCBzZWN0b3Jfc2Vuc2l0aXZpdHkkU2VjdG9yKSwNCiAgICAgICAgICBtYWluID0gIlNlY3RvciBTZW5zaXRpdml0eSB0byBNYWNyb2Vjb25vbWljIEZhY3RvcnMiLA0KICAgICAgICAgIHhsYWIgPSAiSW5kdXN0cnkgU2VjdG9yIiwgeWxhYiA9ICJBdmVyYWdlIEFic29sdXRlIENvcnJlbGF0aW9uIiwNCiAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoImxpZ2h0Ymx1ZSIsICJkYXJrYmx1ZSIpKShucm93KHNlY3Rvcl9zZW5zaXRpdml0eSkpLA0KICAgICAgICAgIGxhcyA9IDIpDQogIA0KICAjIENyZWF0ZSBoZWF0bWFwIGZvciBiYW5rcnVwdGN5IGNvcnJlbGF0aW9ucw0KICBiYW5rX2NvciA8LSBjb3JyZWxhdGlvbl9kZltjb3JyZWxhdGlvbl9kZiRPdXRjb21lID09ICJiYW5rcnVwdGN5IiwgXQ0KICBiYW5rX21hdHJpeCA8LSByZXNoYXBlKGJhbmtfY29yLCBpZHZhciA9ICJTZWN0b3IiLCB0aW1ldmFyID0gIk1hY3JvVmFyTGFiZWwiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJ3aWRlIiwgZHJvcCA9IGMoIk91dGNvbWUiLCAiTWFjcm9WYXIiKSkNCiAgcm93bmFtZXMoYmFua19tYXRyaXgpIDwtIHBhc3RlKCJTZWN0b3IiLCBiYW5rX21hdHJpeCRTZWN0b3IpDQogIGJhbmtfbWF0cml4IDwtIGJhbmtfbWF0cml4WywgLTFdDQogIGNvbG5hbWVzKGJhbmtfbWF0cml4KSA8LSBzdWIoIkNvcnJlbGF0aW9uLiIsICIiLCBjb2xuYW1lcyhiYW5rX21hdHJpeCkpDQogIA0KICAjIFNvcnQgc2VjdG9ycyBieSBhdmVyYWdlIHNlbnNpdGl2aXR5DQogIGJhbmtfbWF0cml4IDwtIGJhbmtfbWF0cml4W21hdGNoKHBhc3RlKCJTZWN0b3IiLCBzZWN0b3Jfc2Vuc2l0aXZpdHkkU2VjdG9yKSwgcm93bmFtZXMoYmFua19tYXRyaXgpKSwgXQ0KICANCiAgIyBQbG90IGhlYXRtYXAgZm9yIGJhbmtydXB0Y3kNCiAgcGFyKG1hciA9IGMoNSwgMTIsIDQsIDIpICsgMC4xKSAgIyBBZGp1c3QgbWFyZ2lucyBmb3Igc2VjdG9yIG5hbWVzDQogIGltYWdlKHogPSB0KGFzLm1hdHJpeChiYW5rX21hdHJpeCkpLCANCiAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKSgxMDApLA0KICAgICAgICBtYWluID0gIlNlY3RvciBTZW5zaXRpdml0eSB0byBNYWNybyBGYWN0b3JzIC0gQmFua3J1cHRjeSIsDQogICAgICAgIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KICANCiAgIyBBZGQgc2VjdG9yIGFuZCBtYWNybyB2YXJpYWJsZSBsYWJlbHMNCiAgYXhpcygyLCBhdCA9IHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gbnJvdyhiYW5rX21hdHJpeCkpLCANCiAgICAgICBsYWJlbHMgPSByb3duYW1lcyhiYW5rX21hdHJpeCksIGxhcyA9IDIsIGNleC5heGlzID0gMC44KQ0KICBheGlzKDEsIGF0ID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSAzKSwgDQogICAgICAgbGFiZWxzID0gY29sbmFtZXMoYmFua19tYXRyaXgpLCBsYXMgPSAxKQ0KICANCiAgIyBBZGQgY29sb3IgbGVnZW5kDQogIGxlZ2VuZF9jb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKSg1KQ0KICBsZWdlbmRfdmFsdWVzIDwtIGMoLTAuNSwgLTAuMjUsIDAsIDAuMjUsIDAuNSkNCiAgbGVnZW5kKCJib3R0b20iLCBsZWdlbmQgPSBsZWdlbmRfdmFsdWVzLCBmaWxsID0gbGVnZW5kX2NvbG9ycywgDQogICAgICAgICBob3JpeiA9IFRSVUUsIGNleCA9IDAuNywgdGl0bGUgPSAiQ29ycmVsYXRpb24iLCBidHkgPSAibiIpDQogIA0KICAjIFByaW50IHN1bW1hcnkgb2Ygc2VjdG9yIHNlbnNpdGl2aXRpZXMNCiAgY2F0KCJcblNlY3RvciBTZW5zaXRpdml0eSB0byBNYWNyb2Vjb25vbWljIEZhY3RvcnMgKFJhbmtlZCk6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIHByaW50KHNlY3Rvcl9zZW5zaXRpdml0eSkNCiAgDQogICMgSWRlbnRpZnkgbW9zdCBhbmQgbGVhc3Qgc2Vuc2l0aXZlIHNlY3RvcnMNCiAgbW9zdF9zZW5zaXRpdmUgPC0gc2VjdG9yX3NlbnNpdGl2aXR5JFNlY3RvclsxXQ0KICBsZWFzdF9zZW5zaXRpdmUgPC0gc2VjdG9yX3NlbnNpdGl2aXR5JFNlY3Rvcltucm93KHNlY3Rvcl9zZW5zaXRpdml0eSldDQogIA0KICBjYXQoIlxuTW9zdCBlY29ub21pY2FsbHkgc2Vuc2l0aXZlIHNlY3RvcjoiLCBtb3N0X3NlbnNpdGl2ZSwgDQogICAgICAiKGF2ZXJhZ2UgY29ycmVsYXRpb24gbWFnbml0dWRlID0iLCByb3VuZChzZWN0b3Jfc2Vuc2l0aXZpdHkkQXZnU2Vuc2l0aXZpdHlbMV0sIDMpLCAiKVxuIikNCiAgY2F0KCJMZWFzdCBlY29ub21pY2FsbHkgc2Vuc2l0aXZlIHNlY3RvcjoiLCBsZWFzdF9zZW5zaXRpdmUsIA0KICAgICAgIihhdmVyYWdlIGNvcnJlbGF0aW9uIG1hZ25pdHVkZSA9Iiwgcm91bmQoc2VjdG9yX3NlbnNpdGl2aXR5JEF2Z1NlbnNpdGl2aXR5W25yb3coc2VjdG9yX3NlbnNpdGl2aXR5KV0sIDMpLCAiKVxuIikNCiAgDQogIHJldHVybihzZWN0b3Jfc2Vuc2l0aXZpdHkpDQp9DQoNCiMgUnVuIHRoZSBzZWN0b3Itc3BlY2lmaWMgbWFjcm8gc2Vuc2l0aXZpdHkgYW5hbHlzaXMNCnNlY3Rvcl9tYWNyb19zZW5zaXRpdml0eSA8LSBhbmFseXplX3NlY3Rvcl9tYWNyb19zZW5zaXRpdml0eSgpDQpgYGANClRoaXMgYW5hbHlzaXMgcHJvdmlkZXMgaW5zaWdodHMgaW50byBob3cgZGlmZmVyZW50IHNlY3RvcnMgcmVzcG9uZCB0byBtYWNyb2Vjb25vbWljIGNoYW5nZXMsIHdoaWNoIGNhbiBiZSB1c2VmdWwgZm9yIGludmVzdG9ycyBhbmQgcG9saWN5bWFrZXJzLg0KDQoNCk5leHQgaXMgdGhlIGJ1c2luZXNzIGN5Y2xlIGltcGFjdCBhbmFseXNpcy4gVGhpcyB3aWxsIGhlbHAgdXMgdW5kZXJzdGFuZCBob3cgY29ycG9yYXRlIGV2ZW50cyBhcmUgaW5mbHVlbmNlZCBieSB0aGUgZWNvbm9taWMgY3ljbGUgYW5kIHdoZXRoZXIgdGhlcmUgYXJlIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGluIGV2ZW50IHJhdGVzIGR1cmluZyBkaWZmZXJlbnQgZWNvbm9taWMgY29uZGl0aW9ucy4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuMTE6IEJ1c2luZXNzIEN5Y2xlIEltcGFjdCBBbmFseXNpcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMy4xMTogQlVTSU5FU1MgQ1lDTEUgSU1QQUNUIEFOQUxZU0lTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KYW5hbHl6ZV9idXNpbmVzc19jeWNsZV9pbXBhY3QgPC0gZnVuY3Rpb24oKSB7DQogIGNhdCgiQW5hbHl6aW5nIGltcGFjdCBvZiBidXNpbmVzcyBjeWNsZXMgb24gY29ycG9yYXRlIG91dGNvbWVzLi4uXG4iKQ0KICANCiAgIyBDaGVjayBmb3IgbmVjZXNzYXJ5IG1hY3JvIHZhcmlhYmxlcw0KICBpZighYWxsKGMoImdkcF9ncm93dGgiLCAidW5lbXBsb3llbWVudCIpICVpbiUgY29sbmFtZXMoZGF0YSkpKSB7DQogICAgY2F0KCJSZXF1aXJlZCBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBub3QgZm91bmQuIENhbm5vdCBwZXJmb3JtIGJ1c2luZXNzIGN5Y2xlIGFuYWx5c2lzLlxuIikNCiAgICByZXR1cm4oTlVMTCkNCiAgfQ0KICANCiAgIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55IGJ5IHllYXINCiAgeWVhcl9vYnMgPC0gZGF0YSAlPiUNCiAgICBncm91cF9ieShjdXNpcCwgY2FsZW5kYXJfeWVhcikgJT4lDQogICAgc2xpY2VfbWF4KHRzdG9wKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgRGVmaW5lIGJ1c2luZXNzIGN5Y2xlIHBoYXNlcyBiYXNlZCBvbiBlY29ub21pYyBpbmRpY2F0b3JzDQogICMgQ2FsY3VsYXRlIG1lZGlhbiB2YWx1ZXMgZm9yIHRocmVzaG9sZHMNCiAgbWVkaWFuX2dkcCA8LSBtZWRpYW4oeWVhcl9vYnMkZ2RwX2dyb3d0aCwgbmEucm0gPSBUUlVFKQ0KICBtZWRpYW5fdW5lbXAgPC0gbWVkaWFuKHllYXJfb2JzJHVuZW1wbG95ZW1lbnQsIG5hLnJtID0gVFJVRSkNCiAgDQogICMgRGVmaW5lIGVjb25vbWljIGN5Y2xlIHBoYXNlcw0KICB5ZWFyX29icyRlY29uX2NvbmRpdGlvbiA8LSAiTm9ybWFsIg0KICB5ZWFyX29icyRlY29uX2NvbmRpdGlvblt5ZWFyX29icyRnZHBfZ3Jvd3RoID4gbWVkaWFuX2dkcCAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyX29icyR1bmVtcGxveWVtZW50IDwgbWVkaWFuX3VuZW1wXSA8LSAiRXhwYW5zaW9uIg0KICB5ZWFyX29icyRlY29uX2NvbmRpdGlvblt5ZWFyX29icyRnZHBfZ3Jvd3RoIDwgbWVkaWFuX2dkcCAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyX29icyR1bmVtcGxveWVtZW50ID4gbWVkaWFuX3VuZW1wXSA8LSAiQ29udHJhY3Rpb24iDQogIA0KICAjIENhbGN1bGF0ZSBldmVudCByYXRlcyBieSBlY29ub21pYyBjb25kaXRpb24NCiAgY29uZGl0aW9uX3N0YXRzIDwtIGFnZ3JlZ2F0ZShjYmluZChiYW5rcnVwdGN5LCBhY3F1aXNpdGlvbikgfiBlY29uX2NvbmRpdGlvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSB5ZWFyX29icywgRlVOID0gbWVhbikNCiAgY29uZGl0aW9uX3N0YXRzJGJhbmtydXB0Y3kgPC0gY29uZGl0aW9uX3N0YXRzJGJhbmtydXB0Y3kgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlDQogIGNvbmRpdGlvbl9zdGF0cyRhY3F1aXNpdGlvbiA8LSBjb25kaXRpb25fc3RhdHMkYWNxdWlzaXRpb24gKiAxMDANCiAgDQogICMgQ291bnQgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBwZXIgY29uZGl0aW9uDQogIGNvbmRpdGlvbl9jb3VudHMgPC0gdGFibGUoeWVhcl9vYnMkZWNvbl9jb25kaXRpb24pDQogIGNvbmRpdGlvbl9zdGF0cyRjb3VudCA8LSBhcy52ZWN0b3IoY29uZGl0aW9uX2NvdW50c1ttYXRjaChjb25kaXRpb25fc3RhdHMkZWNvbl9jb25kaXRpb24sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lcyhjb25kaXRpb25fY291bnRzKSldKQ0KICANCiAgIyBDcmVhdGUgYmFycGxvdCBkYXRhDQogIGJhcnBsb3RfZGF0YSA8LSBhcy5tYXRyaXgoY29uZGl0aW9uX3N0YXRzWywgYygiYmFua3J1cHRjeSIsICJhY3F1aXNpdGlvbiIpXSkNCiAgcm93bmFtZXMoYmFycGxvdF9kYXRhKSA8LSBjb25kaXRpb25fc3RhdHMkZWNvbl9jb25kaXRpb24NCiAgDQogICMgQ3JlYXRlIGJhcnBsb3QNCiAgYmFycGxvdCh0KGJhcnBsb3RfZGF0YSksIA0KICAgICAgICAgIGJlc2lkZSA9IFRSVUUsIA0KICAgICAgICAgIGNvbCA9IGMoImZpcmVicmljayIsICJzdGVlbGJsdWUiKSwNCiAgICAgICAgICBtYWluID0gIkNvcnBvcmF0ZSBFdmVudCBSYXRlcyBieSBFY29ub21pYyBDb25kaXRpb24iLA0KICAgICAgICAgIHhsYWIgPSAiRWNvbm9taWMgQ29uZGl0aW9uIiwgDQogICAgICAgICAgeWxhYiA9ICJFdmVudCBSYXRlICglKSIsDQogICAgICAgICAgbGVnZW5kLnRleHQgPSBjKCJCYW5rcnVwdGN5IiwgIkFjcXVpc2l0aW9uIiksDQogICAgICAgICAgYXJncy5sZWdlbmQgPSBsaXN0KHggPSAidG9wcmlnaHQiLCBidHkgPSAibiIpLA0KICAgICAgICAgIHlsaW0gPSBjKDAsIG1heChiYXJwbG90X2RhdGEpICogMS4yKSkNCiAgDQogICMgQWRkIGNvdW50IGxhYmVscw0KICBtdGV4dChwYXN0ZTAoIm49IiwgY29uZGl0aW9uX3N0YXRzJGNvdW50KSwgDQogICAgICAgIHNpZGUgPSAxLCBsaW5lID0gMywgDQogICAgICAgIGF0ID0gc2VxKDEuNSwgYnkgPSAzLCBsZW5ndGgub3V0ID0gbnJvdyhjb25kaXRpb25fc3RhdHMpKSkNCiAgDQogICMgUHJpbnQgc3VtbWFyeSBzdGF0aXN0aWNzDQogIGNhdCgiXG5FdmVudCBSYXRlcyBieSBFY29ub21pYyBDb25kaXRpb246XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIHByaW50KGNvbmRpdGlvbl9zdGF0c1ssIGMoImVjb25fY29uZGl0aW9uIiwgImNvdW50IiwgImJhbmtydXB0Y3kiLCAiYWNxdWlzaXRpb24iKV0pDQogIA0KICAjIENhbGN1bGF0ZSBzdGF0aXN0aWNhbCB0ZXN0cyBmb3IgcmF0ZSBkaWZmZXJlbmNlcw0KICBjYXQoIlxuU3RhdGlzdGljYWwgdGVzdHMgZm9yIGRpZmZlcmVuY2VzIGluIGV2ZW50IHJhdGVzIGJ5IGVjb25vbWljIGNvbmRpdGlvbjpcbiIpDQogIA0KICAjIEZvciBiYW5rcnVwdGN5DQogIGJpbm9tX3Rlc3RfYmFuayA8LSBwcm9wLnRlc3QoDQogICAgeCA9IGMoc3VtKHllYXJfb2JzJGJhbmtydXB0Y3lbeWVhcl9vYnMkZWNvbl9jb25kaXRpb24gPT0gIkV4cGFuc2lvbiJdKSwNCiAgICAgICAgICBzdW0oeWVhcl9vYnMkYmFua3J1cHRjeVt5ZWFyX29icyRlY29uX2NvbmRpdGlvbiA9PSAiQ29udHJhY3Rpb24iXSkpLA0KICAgIG4gPSBjKHN1bSh5ZWFyX29icyRlY29uX2NvbmRpdGlvbiA9PSAiRXhwYW5zaW9uIiksDQogICAgICAgICAgc3VtKHllYXJfb2JzJGVjb25fY29uZGl0aW9uID09ICJDb250cmFjdGlvbiIpKQ0KICApDQogIA0KICAjIEZvciBhY3F1aXNpdGlvbg0KICBiaW5vbV90ZXN0X2FjcSA8LSBwcm9wLnRlc3QoDQogICAgeCA9IGMoc3VtKHllYXJfb2JzJGFjcXVpc2l0aW9uW3llYXJfb2JzJGVjb25fY29uZGl0aW9uID09ICJFeHBhbnNpb24iXSksDQogICAgICAgICAgc3VtKHllYXJfb2JzJGFjcXVpc2l0aW9uW3llYXJfb2JzJGVjb25fY29uZGl0aW9uID09ICJDb250cmFjdGlvbiJdKSksDQogICAgbiA9IGMoc3VtKHllYXJfb2JzJGVjb25fY29uZGl0aW9uID09ICJFeHBhbnNpb24iKSwNCiAgICAgICAgICBzdW0oeWVhcl9vYnMkZWNvbl9jb25kaXRpb24gPT0gIkNvbnRyYWN0aW9uIikpDQogICkNCiAgDQogIGNhdCgiQmFua3J1cHRjeSByYXRlcyAoRXhwYW5zaW9uIHZzIENvbnRyYWN0aW9uKTogcC12YWx1ZSA9IiwgDQogICAgICBmb3JtYXQucHZhbChiaW5vbV90ZXN0X2JhbmskcC52YWx1ZSwgZGlnaXRzID0gMyksICJcbiIpDQogIGNhdCgiQWNxdWlzaXRpb24gcmF0ZXMgKEV4cGFuc2lvbiB2cyBDb250cmFjdGlvbik6IHAtdmFsdWUgPSIsIA0KICAgICAgZm9ybWF0LnB2YWwoYmlub21fdGVzdF9hY3EkcC52YWx1ZSwgZGlnaXRzID0gMyksICJcbiIpDQogIA0KICAjIENyZWF0ZSBzdXJ2aXZhbCBjdXJ2ZXMgYnkgZWNvbm9taWMgY29uZGl0aW9uDQogICMgQ3JlYXRlIHN1cnZpdmFsIG9iamVjdA0KICBzdXJ2X2JhbmsgPC0gU3Vydih0aW1lID0geWVhcl9vYnMkdHN0b3AsIGV2ZW50ID0geWVhcl9vYnMkYmFua3J1cHRjeSkNCiAgc3Vydl9hY3EgPC0gU3Vydih0aW1lID0geWVhcl9vYnMkdHN0b3AsIGV2ZW50ID0geWVhcl9vYnMkYWNxdWlzaXRpb24pDQogIA0KICAjIEZpdCBLTSBjdXJ2ZXMgYnkgZWNvbm9taWMgY29uZGl0aW9uDQogIGttX2JhbmsgPC0gc3VydmZpdChzdXJ2X2JhbmsgfiBlY29uX2NvbmRpdGlvbiwgZGF0YSA9IHllYXJfb2JzKQ0KICBrbV9hY3EgPC0gc3VydmZpdChzdXJ2X2FjcSB+IGVjb25fY29uZGl0aW9uLCBkYXRhID0geWVhcl9vYnMpDQogIA0KICAjIFBsb3QgS00gY3VydmVzIGZvciBiYW5rcnVwdGN5DQogIHBsb3Qoa21fYmFuaywgY29sID0gYygiZ3JlZW4zIiwgImJsdWUiLCAicmVkIiksIGx3ZCA9IDIsDQogICAgICAgbWFpbiA9ICJCYW5rcnVwdGN5LUZyZWUgU3Vydml2YWwgYnkgRWNvbm9taWMgQ29uZGl0aW9uIiwNCiAgICAgICB4bGFiID0gIlRpbWUgKFllYXJzKSIsIHlsYWIgPSAiU3Vydml2YWwgUHJvYmFiaWxpdHkiKQ0KICANCiAgIyBBZGQgbGVnZW5kDQogIGxlZ2VuZCgiYm90dG9tbGVmdCIsIGxlZ2VuZCA9IGxldmVscyhhcy5mYWN0b3IoeWVhcl9vYnMkZWNvbl9jb25kaXRpb24pKSwNCiAgICAgICAgIGNvbCA9IGMoImdyZWVuMyIsICJibHVlIiwgInJlZCIpLCBsd2QgPSAyLCBidHkgPSAibiIpDQogIA0KICAjIExvZy1yYW5rIHRlc3QgZm9yIGJhbmtydXB0Y3kNCiAgbG9nX3JhbmtfYmFuayA8LSBzdXJ2ZGlmZihzdXJ2X2JhbmsgfiBlY29uX2NvbmRpdGlvbiwgZGF0YSA9IHllYXJfb2JzKQ0KICANCiAgY2F0KCJcbkxvZy1yYW5rIHRlc3QgZm9yIGJhbmtydXB0Y3kgYnkgZWNvbm9taWMgY29uZGl0aW9uOlxuIikNCiAgcHJpbnQobG9nX3JhbmtfYmFuaykNCiAgDQogICMgUGxvdCBLTSBjdXJ2ZXMgZm9yIGFjcXVpc2l0aW9uDQogIHBsb3Qoa21fYWNxLCBjb2wgPSBjKCJncmVlbjMiLCAiYmx1ZSIsICJyZWQiKSwgbHdkID0gMiwNCiAgICAgICBtYWluID0gIkFjcXVpc2l0aW9uLUZyZWUgU3Vydml2YWwgYnkgRWNvbm9taWMgQ29uZGl0aW9uIiwNCiAgICAgICB4bGFiID0gIlRpbWUgKFllYXJzKSIsIHlsYWIgPSAiU3Vydml2YWwgUHJvYmFiaWxpdHkiKQ0KICANCiAgIyBBZGQgbGVnZW5kDQogIGxlZ2VuZCgiYm90dG9tbGVmdCIsIGxlZ2VuZCA9IGxldmVscyhhcy5mYWN0b3IoeWVhcl9vYnMkZWNvbl9jb25kaXRpb24pKSwNCiAgICAgICAgIGNvbCA9IGMoImdyZWVuMyIsICJibHVlIiwgInJlZCIpLCBsd2QgPSAyLCBidHkgPSAibiIpDQogIA0KICAjIExvZy1yYW5rIHRlc3QgZm9yIGFjcXVpc2l0aW9uDQogIGxvZ19yYW5rX2FjcSA8LSBzdXJ2ZGlmZihzdXJ2X2FjcSB+IGVjb25fY29uZGl0aW9uLCBkYXRhID0geWVhcl9vYnMpDQogIA0KICBjYXQoIlxuTG9nLXJhbmsgdGVzdCBmb3IgYWNxdWlzaXRpb24gYnkgZWNvbm9taWMgY29uZGl0aW9uOlxuIikNCiAgcHJpbnQobG9nX3JhbmtfYWNxKQ0KICANCiAgIyBTdW1tYXJ5IG9mIGZpbmRpbmdzDQogIGNhdCgiXG5TdW1tYXJ5IG9mIEJ1c2luZXNzIEN5Y2xlIEltcGFjdCBBbmFseXNpczpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgDQogICMgQmFua3J1cHRjeSBhbmFseXNpcw0KICBjYXQoIkJhbmtydXB0Y3k6XG4iKQ0KICBiYW5rX2V4cCA8LSBjb25kaXRpb25fc3RhdHMkYmFua3J1cHRjeVtjb25kaXRpb25fc3RhdHMkZWNvbl9jb25kaXRpb24gPT0gIkV4cGFuc2lvbiJdDQogIGJhbmtfY29udCA8LSBjb25kaXRpb25fc3RhdHMkYmFua3J1cHRjeVtjb25kaXRpb25fc3RhdHMkZWNvbl9jb25kaXRpb24gPT0gIkNvbnRyYWN0aW9uIl0NCiAgYmFua19yYXRpbyA8LSBiYW5rX2NvbnQgLyBiYW5rX2V4cA0KICANCiAgaWYoYmFua19jb250ID4gYmFua19leHApIHsNCiAgICBjYXQoIi0gQmFua3J1cHRjeSByaXNrIGlzIiwgcm91bmQoYmFua19yYXRpbywgMSksICJ0aW1lcyBoaWdoZXIgZHVyaW5nIGVjb25vbWljIGNvbnRyYWN0aW9uc1xuIikNCiAgfSBlbHNlIHsNCiAgICBjYXQoIi0gQmFua3J1cHRjeSByaXNrIGlzIiwgcm91bmQoMS9iYW5rX3JhdGlvLCAxKSwgInRpbWVzIGhpZ2hlciBkdXJpbmcgZWNvbm9taWMgZXhwYW5zaW9uc1xuIikNCiAgfQ0KICANCiAgaWYoYmlub21fdGVzdF9iYW5rJHAudmFsdWUgPCAwLjA1KSB7DQogICAgY2F0KCItIFRoZSBkaWZmZXJlbmNlIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKHAgPCAwLjA1KVxuIikNCiAgfSBlbHNlIHsNCiAgICBjYXQoIi0gVGhlIGRpZmZlcmVuY2UgaXMgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKHAgPj0gMC4wNSlcbiIpDQogIH0NCiAgDQogICMgQWNxdWlzaXRpb24gYW5hbHlzaXMNCiAgY2F0KCJcbkFjcXVpc2l0aW9uOlxuIikNCiAgYWNxX2V4cCA8LSBjb25kaXRpb25fc3RhdHMkYWNxdWlzaXRpb25bY29uZGl0aW9uX3N0YXRzJGVjb25fY29uZGl0aW9uID09ICJFeHBhbnNpb24iXQ0KICBhY3FfY29udCA8LSBjb25kaXRpb25fc3RhdHMkYWNxdWlzaXRpb25bY29uZGl0aW9uX3N0YXRzJGVjb25fY29uZGl0aW9uID09ICJDb250cmFjdGlvbiJdDQogIGFjcV9yYXRpbyA8LSBhY3FfZXhwIC8gYWNxX2NvbnQNCiAgDQogIGlmKGFjcV9leHAgPiBhY3FfY29udCkgew0KICAgIGNhdCgiLSBBY3F1aXNpdGlvbiBsaWtlbGlob29kIGlzIiwgcm91bmQoYWNxX3JhdGlvLCAxKSwgInRpbWVzIGhpZ2hlciBkdXJpbmcgZWNvbm9taWMgZXhwYW5zaW9uc1xuIikNCiAgfSBlbHNlIHsNCiAgICBjYXQoIi0gQWNxdWlzaXRpb24gbGlrZWxpaG9vZCBpcyIsIHJvdW5kKDEvYWNxX3JhdGlvLCAxKSwgInRpbWVzIGhpZ2hlciBkdXJpbmcgZWNvbm9taWMgY29udHJhY3Rpb25zXG4iKQ0KICB9DQogIA0KICBpZihiaW5vbV90ZXN0X2FjcSRwLnZhbHVlIDwgMC4wNSkgew0KICAgIGNhdCgiLSBUaGUgZGlmZmVyZW5jZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IChwIDwgMC4wNSlcbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCItIFRoZSBkaWZmZXJlbmNlIGlzIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IChwID49IDAuMDUpXG4iKQ0KICB9DQogIA0KICAjIFJldHVybiBhbmFseXNpcyByZXN1bHRzDQogIHJldHVybihsaXN0KA0KICAgIGNvbmRpdGlvbl9zdGF0cyA9IGNvbmRpdGlvbl9zdGF0cywNCiAgICBiYW5rcnVwdGN5X3Rlc3QgPSBiaW5vbV90ZXN0X2JhbmssDQogICAgYWNxdWlzaXRpb25fdGVzdCA9IGJpbm9tX3Rlc3RfYWNxLA0KICAgIGttX2JhbmsgPSBrbV9iYW5rLA0KICAgIGttX2FjcSA9IGttX2FjcQ0KICApKQ0KfQ0KDQojIFJ1biB0aGUgYnVzaW5lc3MgY3ljbGUgaW1wYWN0IGFuYWx5c2lzDQpidXNpbmVzc19jeWNsZV9hbmFseXNpcyA8LSBhbmFseXplX2J1c2luZXNzX2N5Y2xlX2ltcGFjdCgpDQpgYGANCg0KV2UgYWxzbyBzdHJhdGlmeSBzdXJpdmFsIGN1cmNlcyBieSBzZWN0b3IuIFRoaXMgd2lsbCBoZWxwIHVzIHVuZGVyc3RhbmQgaG93IGRpZmZlcmVudCBzZWN0b3JzIHJlc3BvbmQgdG8gY29ycG9yYXRlIGV2ZW50cyBhbmQgd2hldGhlciB0aGVyZSBhcmUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgaW4gc3Vydml2YWwgcmF0ZXMgYWNyb3NzIHNlY3RvcnMuDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDEzLjEyOiBFbmhhbmNlZCBTZWN0b3ItU3BlY2lmaWMgU3Vydml2YWwgQW5hbHlzaXMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTMuMTI6IEVOSEFOQ0VEIFNFQ1RPUi1TUEVDSUZJQyBTVVJWSVZBTCBBTkFMWVNJU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gY3JlYXRlIGVuaGFuY2VkIEtNIGN1cnZlcyBzdHJhdGlmaWVkIGJ5IHNlY3RvciB3aXRoIGFkZGl0aW9uYWwgYW5hbHlzaXMNCmNyZWF0ZV9lbmhhbmNlZF9zZWN0b3Jfa21fY3VydmVzIDwtIGZ1bmN0aW9uKCkgeyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDEzLjEyOiBFbmhhbmNlZCBTZWN0b3ItU3BlY2lmaWMgU3Vydml2YWwgQW5hbHlzaXMgKEZJWEVEKQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMy4xMjogRU5IQU5DRUQgU0VDVE9SLVNQRUNJRklDIFNVUlZJVkFMIEFOQUxZU0lTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBGdW5jdGlvbiB0byBjcmVhdGUgZW5oYW5jZWQgS00gY3VydmVzIHN0cmF0aWZpZWQgYnkgc2VjdG9yIHdpdGggYWRkaXRpb25hbCBhbmFseXNpcw0KY3JlYXRlX2VuaGFuY2VkX3NlY3Rvcl9rbV9jdXJ2ZXMgPC0gZnVuY3Rpb24oKSB7DQogIGNhdCgiQ3JlYXRpbmcgZW5oYW5jZWQgS2FwbGFuLU1laWVyIGN1cnZlcyBieSBpbmR1c3RyeSBzZWN0b3IgZm9yIGJvdGggZXZlbnQgdHlwZXMuLi5cbiIpDQogIA0KICAjIEdldCB0aGUgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55DQogIGZpbmFsX29icyA8LSBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICANCiAgIyBFbnN1cmUgZ3NlY3RvciBpcyBhIGZhY3Rvcg0KICBmaW5hbF9vYnMkZ3NlY3RvciA8LSBhcy5mYWN0b3IoZmluYWxfb2JzJGdzZWN0b3IpDQogIA0KICAjIENvdW50IGV2ZW50cyBieSBzZWN0b3INCiAgc2VjdG9yX2JhbmtydXB0Y3kgPC0gdGFwcGx5KGZpbmFsX29icyRiYW5rcnVwdGN5LCBmaW5hbF9vYnMkZ3NlY3Rvciwgc3VtKQ0KICBzZWN0b3JfYWNxdWlzaXRpb24gPC0gdGFwcGx5KGZpbmFsX29icyRhY3F1aXNpdGlvbiwgZmluYWxfb2JzJGdzZWN0b3IsIHN1bSkNCiAgDQogICMgQ291bnQgY29tcGFuaWVzIHBlciBzZWN0b3INCiAgc2VjdG9yX2NvdW50cyA8LSB0YWJsZShmaW5hbF9vYnMkZ3NlY3RvcikNCiAgDQogICMgQ3JlYXRlIGRhdGEgZnJhbWUgb2Ygc2VjdG9yIHN0YXRpc3RpY3MNCiAgc2VjdG9yX3N0YXRzIDwtIGRhdGEuZnJhbWUoDQogICAgU2VjdG9yID0gbmFtZXMoc2VjdG9yX2NvdW50cyksDQogICAgQ29tcGFuaWVzID0gYXMubnVtZXJpYyhzZWN0b3JfY291bnRzKSwNCiAgICBCYW5rcnVwdGN5X0V2ZW50cyA9IGFzLm51bWVyaWMoc2VjdG9yX2JhbmtydXB0Y3kpLA0KICAgIEJhbmtydXB0Y3lfUmF0ZSA9IGFzLm51bWVyaWMoc2VjdG9yX2JhbmtydXB0Y3kpIC8gYXMubnVtZXJpYyhzZWN0b3JfY291bnRzKSAqIDEwMCwNCiAgICBBY3F1aXNpdGlvbl9FdmVudHMgPSBhcy5udW1lcmljKHNlY3Rvcl9hY3F1aXNpdGlvbiksDQogICAgQWNxdWlzaXRpb25fUmF0ZSA9IGFzLm51bWVyaWMoc2VjdG9yX2FjcXVpc2l0aW9uKSAvIGFzLm51bWVyaWMoc2VjdG9yX2NvdW50cykgKiAxMDAsDQogICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQogICkNCiAgDQogICMgU29ydCBieSBudW1iZXIgb2YgY29tcGFuaWVzDQogIHNlY3Rvcl9zdGF0cyA8LSBzZWN0b3Jfc3RhdHNbb3JkZXIoc2VjdG9yX3N0YXRzJENvbXBhbmllcywgZGVjcmVhc2luZyA9IFRSVUUpLCBdDQogIA0KICAjIFNlbGVjdCBzZWN0b3JzIGZvciBLTSBwbG90cyAodG9wIHNlY3RvcnMgYnkgY29tcGFueSBjb3VudCB3aXRoIHN1ZmZpY2llbnQgZXZlbnRzKQ0KICB2YWxpZF9iYW5rcnVwdGN5X3NlY3RvcnMgPC0gc2VjdG9yX3N0YXRzJFNlY3RvclsNCiAgICBzZWN0b3Jfc3RhdHMkQ29tcGFuaWVzID49IDMwICYgc2VjdG9yX3N0YXRzJEJhbmtydXB0Y3lfRXZlbnRzID49IDUNCiAgXQ0KICANCiAgdmFsaWRfYWNxdWlzaXRpb25fc2VjdG9ycyA8LSBzZWN0b3Jfc3RhdHMkU2VjdG9yWw0KICAgIHNlY3Rvcl9zdGF0cyRDb21wYW5pZXMgPj0gMzAgJiBzZWN0b3Jfc3RhdHMkQWNxdWlzaXRpb25fRXZlbnRzID49IDUNCiAgXQ0KICANCiAgIyBUYWtlIHRvcCA1IHNlY3RvcnMgZm9yIGVhY2ggZXZlbnQgdHlwZQ0KICBpZihsZW5ndGgodmFsaWRfYmFua3J1cHRjeV9zZWN0b3JzKSA+IDUpIHsNCiAgICBiYW5rcnVwdGN5X3NlY3RvcnMgPC0gdmFsaWRfYmFua3J1cHRjeV9zZWN0b3JzWzE6NV0NCiAgfSBlbHNlIHsNCiAgICBiYW5rcnVwdGN5X3NlY3RvcnMgPC0gdmFsaWRfYmFua3J1cHRjeV9zZWN0b3JzDQogIH0NCiAgDQogIGlmKGxlbmd0aCh2YWxpZF9hY3F1aXNpdGlvbl9zZWN0b3JzKSA+IDUpIHsNCiAgICBhY3F1aXNpdGlvbl9zZWN0b3JzIDwtIHZhbGlkX2FjcXVpc2l0aW9uX3NlY3RvcnNbMTo1XQ0KICB9IGVsc2Ugew0KICAgIGFjcXVpc2l0aW9uX3NlY3RvcnMgPC0gdmFsaWRfYWNxdWlzaXRpb25fc2VjdG9ycw0KICB9DQogIA0KICAjIENyZWF0ZSBmdW5jdGlvbiBmb3IgZml0dGluZyBhbmQgcGxvdHRpbmcgS00gY3VydmVzIGJ5IHNlY3RvciBmb3IgYW4gZXZlbnQgdHlwZQ0KICBwbG90X3NlY3Rvcl9rbSA8LSBmdW5jdGlvbihldmVudF90eXBlLCBzZWN0b3JzLCB0aXRsZSkgew0KICAgICMgRmlsdGVyIGRhdGEgdG8gdGhlc2Ugc2VjdG9ycw0KICAgIHNlY3Rvcl9kYXRhIDwtIGZpbmFsX29ic1tmaW5hbF9vYnMkZ3NlY3RvciAlaW4lIHNlY3RvcnMsIF0NCiAgICANCiAgICAjIENyZWF0ZSBzdXJ2aXZhbCBvYmplY3QNCiAgICBzdXJ2X29iaiA8LSBTdXJ2KHRpbWUgPSBzZWN0b3JfZGF0YSR0c3RvcCwgZXZlbnQgPSBzZWN0b3JfZGF0YVtbZXZlbnRfdHlwZV1dKQ0KICAgIA0KICAgICMgRml0IEtNIGN1cnZlcyBieSBzZWN0b3INCiAgICBrbV9maXQgPC0gc3VydmZpdChzdXJ2X29iaiB+IGdzZWN0b3IsIGRhdGEgPSBzZWN0b3JfZGF0YSkNCiAgICANCiAgICAjIENyZWF0ZSBjb2xvcnMgZm9yIHRoZSBzZWN0b3JzDQogICAgc2VjdG9yX2NvbG9ycyA8LSByYWluYm93KGxlbmd0aChzZWN0b3JzKSkNCiAgICANCiAgICAjIFNldCB1cCBwbG90IGFyZWENCiAgICBwYXIobWFyID0gYyg1LCA0LCA0LCA4KSArIDAuMSkgICMgRXh0cmEgc3BhY2UgZm9yIGxlZ2VuZA0KICAgIA0KICAgICMgUGxvdCBLTSBjdXJ2ZXMNCiAgICBwbG90KGttX2ZpdCwgY29sID0gc2VjdG9yX2NvbG9ycywgbHdkID0gMiwNCiAgICAgICAgIG1haW4gPSB0aXRsZSwNCiAgICAgICAgIHhsYWIgPSAiVGltZSAoWWVhcnMpIiwgeWxhYiA9ICJTdXJ2aXZhbCBQcm9iYWJpbGl0eSIsDQogICAgICAgICBsdHkgPSAxOmxlbmd0aChzZWN0b3JzKSkNCiAgICANCiAgICAjIENyZWF0ZSBzZWN0b3IgbGFiZWxzIHdpdGggZXZlbnQgY291bnRzIGFuZCByYXRlcw0KICAgIHNlY3Rvcl9sYWJlbHMgPC0gc2FwcGx5KHNlY3RvcnMsIGZ1bmN0aW9uKHMpIHsNCiAgICAgIGNvdW50IDwtIHN1bShzZWN0b3JfZGF0YSRnc2VjdG9yID09IHMpDQogICAgICBldmVudHMgPC0gc3VtKHNlY3Rvcl9kYXRhW1tldmVudF90eXBlXV1bc2VjdG9yX2RhdGEkZ3NlY3RvciA9PSBzXSkNCiAgICAgIHJhdGUgPC0gZXZlbnRzIC8gY291bnQgKiAxMDANCiAgICAgIHBhc3RlMCgiU2VjdG9yICIsIHMsICIgKCIsIGV2ZW50cywgIi8iLCBjb3VudCwgIiwgIiwgcm91bmQocmF0ZSwgMSksICIlKSIpDQogICAgfSkNCiAgICANCiAgICAjIEFkZCBsZWdlbmQgd2l0aCBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uDQogICAgbGVnZW5kKCJ0b3ByaWdodCIsIGluc2V0ID0gYygtMC4zNSwgMCksIGxlZ2VuZCA9IHNlY3Rvcl9sYWJlbHMsDQogICAgICAgICAgIGNvbCA9IHNlY3Rvcl9jb2xvcnMsIGx3ZCA9IDIsIGx0eSA9IDE6bGVuZ3RoKHNlY3RvcnMpLCANCiAgICAgICAgICAgYnR5ID0gIm4iLCB4cGQgPSBUUlVFLCBjZXggPSAwLjgpDQogICAgDQogICAgIyBMb2ctcmFuayB0ZXN0DQogICAgbG9nX3JhbmsgPC0gc3VydmRpZmYoc3Vydl9vYmogfiBnc2VjdG9yLCBkYXRhID0gc2VjdG9yX2RhdGEpDQogICAgDQogICAgIyBBZGQgcC12YWx1ZSB0byBwbG90DQogICAgcF92YWx1ZSA8LSAxIC0gcGNoaXNxKGxvZ19yYW5rJGNoaXNxLCBkZiA9IGxlbmd0aChzZWN0b3JzKSAtIDEpDQogICAgc2lnbmlmaWNhbmNlIDwtIGlmZWxzZShwX3ZhbHVlIDwgMC4wNSwgIlNpZ25pZmljYW50IiwgIk5vdCBzaWduaWZpY2FudCIpDQogICAgbXRleHQocGFzdGUwKCJMb2ctcmFuayBwID0gIiwgZm9ybWF0LnB2YWwocF92YWx1ZSwgZGlnaXRzID0gMyksICIgKCIsIHNpZ25pZmljYW5jZSwgIikiKSwgDQogICAgICAgICAgc2lkZSA9IDMsIGxpbmUgPSAwLjUsIGNleCA9IDAuOCkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBhbmQgcmV0dXJuIG1lZGlhbiBzdXJ2aXZhbCB0aW1lcw0KICAgIG1lZGlhbnMgPC0gc3VtbWFyeShrbV9maXQpJHRhYmxlWywgIm1lZGlhbiJdDQogICAgbmFtZXMobWVkaWFucykgPC0gc2VjdG9ycw0KICAgIA0KICAgIHJldHVybihsaXN0KA0KICAgICAga21fZml0ID0ga21fZml0LA0KICAgICAgbG9nX3JhbmsgPSBsb2dfcmFuaywNCiAgICAgIG1lZGlhbnMgPSBtZWRpYW5zLA0KICAgICAgc2VjdG9ycyA9IHNlY3RvcnMNCiAgICApKQ0KICB9DQogIA0KICAjIFBsb3QgS00gY3VydmVzIGZvciBiYW5rcnVwdGN5DQogIGlmKGxlbmd0aChiYW5rcnVwdGN5X3NlY3RvcnMpID49IDIpIHsNCiAgICBjYXQoIlxuUGxvdHRpbmcgS00gY3VydmVzIGZvciBiYW5rcnVwdGN5IGJ5IHNlY3Rvci4uLlxuIikNCiAgICBiYW5rcnVwdGN5X2ttIDwtIHBsb3Rfc2VjdG9yX2ttKCJiYW5rcnVwdGN5IiwgYmFua3J1cHRjeV9zZWN0b3JzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCYW5rcnVwdGN5LUZyZWUgU3Vydml2YWwgYnkgSW5kdXN0cnkgU2VjdG9yIikNCiAgICANCiAgICAjIFByaW50IGxvZy1yYW5rIHRlc3QgcmVzdWx0cw0KICAgIGNhdCgiXG5Mb2ctcmFuayB0ZXN0IGZvciBiYW5rcnVwdGN5IGJ5IHNlY3RvcjpcbiIpDQogICAgcHJpbnQoYmFua3J1cHRjeV9rbSRsb2dfcmFuaykNCiAgICANCiAgICAjIFByaW50IG1lZGlhbiBzdXJ2aXZhbCB0aW1lcw0KICAgIGNhdCgiXG5NZWRpYW4gdGltZSB0byBiYW5rcnVwdGN5IGJ5IHNlY3RvcjpcbiIpDQogICAgcHJpbnQoYmFua3J1cHRjeV9rbSRtZWRpYW5zKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIGhhemFyZCByYXRpb3MgYmV0d2VlbiBzZWN0b3JzIHVzaW5nIENveCBtb2RlbA0KICAgIGNhdCgiXG5IYXphcmQgcmF0aW9zIGJldHdlZW4gc2VjdG9ycyBmb3IgYmFua3J1cHRjeTpcbiIpDQogICAgIyBVc2UgdGhlIGZpcnN0IHNlY3RvciBhcyByZWZlcmVuY2UNCiAgICByZWZfc2VjdG9yIDwtIGJhbmtydXB0Y3lfc2VjdG9yc1sxXQ0KICAgIGJhbmtydXB0Y3lfc2VjdG9yc19mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKCJTdXJ2KHRzdG9wLCBiYW5rcnVwdGN5KSB+IGdzZWN0b3IiKSkNCiAgICBiYW5rcnVwdGN5X3NlY3RvcnNfbW9kZWwgPC0gY294cGgoYmFua3J1cHRjeV9zZWN0b3JzX2Zvcm11bGEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGZpbmFsX29ic1tmaW5hbF9vYnMkZ3NlY3RvciAlaW4lIGJhbmtydXB0Y3lfc2VjdG9ycywgXSkNCiAgICBwcmludChzdW1tYXJ5KGJhbmtydXB0Y3lfc2VjdG9yc19tb2RlbCkkY29uZi5pbnQpDQogIH0gZWxzZSB7DQogICAgY2F0KCJcbk5vdCBlbm91Z2ggc2VjdG9ycyB3aXRoIHN1ZmZpY2llbnQgYmFua3J1cHRjeSBldmVudHMgZm9yIEtNIGN1cnZlcy5cbiIpDQogIH0NCiAgDQogICMgUGxvdCBLTSBjdXJ2ZXMgZm9yIGFjcXVpc2l0aW9uDQogIGlmKGxlbmd0aChhY3F1aXNpdGlvbl9zZWN0b3JzKSA+PSAyKSB7DQogICAgY2F0KCJcblBsb3R0aW5nIEtNIGN1cnZlcyBmb3IgYWNxdWlzaXRpb24gYnkgc2VjdG9yLi4uXG4iKQ0KICAgIGFjcXVpc2l0aW9uX2ttIDwtIHBsb3Rfc2VjdG9yX2ttKCJhY3F1aXNpdGlvbiIsIGFjcXVpc2l0aW9uX3NlY3RvcnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBY3F1aXNpdGlvbi1GcmVlIFN1cnZpdmFsIGJ5IEluZHVzdHJ5IFNlY3RvciIpDQogICAgDQogICAgIyBQcmludCBsb2ctcmFuayB0ZXN0IHJlc3VsdHMNCiAgICBjYXQoIlxuTG9nLXJhbmsgdGVzdCBmb3IgYWNxdWlzaXRpb24gYnkgc2VjdG9yOlxuIikNCiAgICBwcmludChhY3F1aXNpdGlvbl9rbSRsb2dfcmFuaykNCiAgICANCiAgICAjIFByaW50IG1lZGlhbiBzdXJ2aXZhbCB0aW1lcw0KICAgIGNhdCgiXG5NZWRpYW4gdGltZSB0byBhY3F1aXNpdGlvbiBieSBzZWN0b3I6XG4iKQ0KICAgIHByaW50KGFjcXVpc2l0aW9uX2ttJG1lZGlhbnMpDQogICAgDQogICAgIyBDYWxjdWxhdGUgaGF6YXJkIHJhdGlvcyBiZXR3ZWVuIHNlY3RvcnMgdXNpbmcgQ294IG1vZGVsDQogICAgY2F0KCJcbkhhemFyZCByYXRpb3MgYmV0d2VlbiBzZWN0b3JzIGZvciBhY3F1aXNpdGlvbjpcbiIpDQogICAgIyBVc2UgdGhlIGZpcnN0IHNlY3RvciBhcyByZWZlcmVuY2UNCiAgICByZWZfc2VjdG9yIDwtIGFjcXVpc2l0aW9uX3NlY3RvcnNbMV0NCiAgICBhY3F1aXNpdGlvbl9zZWN0b3JzX2Zvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAoIlN1cnYodHN0b3AsIGFjcXVpc2l0aW9uKSB+IGdzZWN0b3IiKSkNCiAgICBhY3F1aXNpdGlvbl9zZWN0b3JzX21vZGVsIDwtIGNveHBoKGFjcXVpc2l0aW9uX3NlY3RvcnNfZm9ybXVsYSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGZpbmFsX29ic1tmaW5hbF9vYnMkZ3NlY3RvciAlaW4lIGFjcXVpc2l0aW9uX3NlY3RvcnMsIF0pDQogICAgcHJpbnQoc3VtbWFyeShhY3F1aXNpdGlvbl9zZWN0b3JzX21vZGVsKSRjb25mLmludCkNCiAgfSBlbHNlIHsNCiAgICBjYXQoIlxuTm90IGVub3VnaCBzZWN0b3JzIHdpdGggc3VmZmljaWVudCBhY3F1aXNpdGlvbiBldmVudHMgZm9yIEtNIGN1cnZlcy5cbiIpDQogIH0NCiAgDQogICMgQ3JlYXRlIGEgY29tcGFyYXRpdmUgYW5hbHlzaXMgb2Ygc2VjdG9yIHJpc2sgcHJvZmlsZXMNCiAgaWYobGVuZ3RoKGludGVyc2VjdChiYW5rcnVwdGN5X3NlY3RvcnMsIGFjcXVpc2l0aW9uX3NlY3RvcnMpKSA+IDApIHsNCiAgICBjYXQoIlxuQ29tcGFyYXRpdmUgQW5hbHlzaXMgb2YgU2VjdG9yIFJpc2sgUHJvZmlsZXM6XG4iKQ0KICAgIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICAgIA0KICAgIGNvbW1vbl9zZWN0b3JzIDwtIGludGVyc2VjdChiYW5rcnVwdGN5X3NlY3RvcnMsIGFjcXVpc2l0aW9uX3NlY3RvcnMpDQogICAgDQogICAgZm9yKHNlY3RvciBpbiBjb21tb25fc2VjdG9ycykgew0KICAgICAgIyBGaW5kIHRoZSByb3cgaW5kZXggZm9yIHRoaXMgc2VjdG9yIGluIHNlY3Rvcl9zdGF0cw0KICAgICAgc2VjdG9yX2lkeCA8LSB3aGljaChzZWN0b3Jfc3RhdHMkU2VjdG9yID09IHNlY3RvcikNCiAgICAgIA0KICAgICAgIyBBY2Nlc3MgdGhlIHJhdGVzIHVzaW5nIHRoZSBpbmRleA0KICAgICAgaWYobGVuZ3RoKHNlY3Rvcl9pZHgpID4gMCkgew0KICAgICAgICBiYW5rX3JhdGUgPC0gc2VjdG9yX3N0YXRzJEJhbmtydXB0Y3lfUmF0ZVtzZWN0b3JfaWR4XQ0KICAgICAgICBhY3FfcmF0ZSA8LSBzZWN0b3Jfc3RhdHMkQWNxdWlzaXRpb25fUmF0ZVtzZWN0b3JfaWR4XQ0KICAgICAgICANCiAgICAgICAgY2F0KCJTZWN0b3IiLCBzZWN0b3IsICI6XG4iKQ0KICAgICAgICBjYXQoIiAgQmFua3J1cHRjeSBSYXRlOiIsIHJvdW5kKGJhbmtfcmF0ZSwgMSksICIlXG4iKQ0KICAgICAgICBjYXQoIiAgQWNxdWlzaXRpb24gUmF0ZToiLCByb3VuZChhY3FfcmF0ZSwgMSksICIlXG4iKQ0KICAgICAgICBjYXQoIiAgUmF0aW8gKEFjcXVpc2l0aW9uL0JhbmtydXB0Y3kpOiIsIHJvdW5kKGFjcV9yYXRlL2JhbmtfcmF0ZSwgMSksICJcblxuIikNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgDQogICMgUmVzZXQgcGxvdHRpbmcgcGFyYW1ldGVycw0KICBwYXIobWFyID0gYyg1LCA0LCA0LCAyKSArIDAuMSkNCiAgDQogICMgUmV0dXJuIHNlY3RvciBzdGF0aXN0aWNzIGZvciBmdXJ0aGVyIGFuYWx5c2lzDQogIHJldHVybihzZWN0b3Jfc3RhdHMpDQp9DQoNCiMgQ3JlYXRlIGVuaGFuY2VkIEtNIGN1cnZlcyBieSBzZWN0b3INCnNlY3Rvcl9zdXJ2aXZhbF9hbmFseXNpcyA8LSBjcmVhdGVfZW5oYW5jZWRfc2VjdG9yX2ttX2N1cnZlcygpfQ0KDQojIENyZWF0ZSBlbmhhbmNlZCBLTSBjdXJ2ZXMgYnkgc2VjdG9yDQpzZWN0b3Jfc3Vydml2YWxfYW5hbHlzaXMgPC0gY3JlYXRlX2VuaGFuY2VkX3NlY3Rvcl9rbV9jdXJ2ZXMoKQ0KYGBgDQoNCldlIHNlZSBob3cgdGhlIG1vZGVsIHBlcmZvcm1zIGJ5IGluZHVzdHJ5IHNlY3Rvci4gVGhpcyB3aWxsIGhlbHAgdXMgdW5kZXJzdGFuZCBob3cgdGhlIG1vZGVsJ3MgcGVyZm9ybWFuY2UgdmFyaWVzIGFjcm9zcyBkaWZmZXJlbnQgc2VjdG9ycyBhbmQgd2hldGhlciB0aGVyZSBhcmUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgaW4gZXZlbnQgcmF0ZXMgYnkgc2VjdG9yLg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxMy4xNDogTW9kZWwgUGVyZm9ybWFuY2UgYnkgSW5kdXN0cnkgU2VjdG9yDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KY2F0KCJcbjEzLjE0OiBNT0RFTCBQRVJGT1JNQU5DRSBCWSBJTkRVU1RSWSBTRUNUT1JcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQpldmFsdWF0ZV9tb2RlbF9ieV9zZWN0b3IgPC0gZnVuY3Rpb24obW9kZWwsIG1vZGVsX25hbWUsIGV2ZW50X3R5cGUpIHsNCiAgY2F0KCJFdmFsdWF0aW5nIiwgbW9kZWxfbmFtZSwgIm1vZGVsIHBlcmZvcm1hbmNlIGJ5IGluZHVzdHJ5IHNlY3Rvci4uLlxuIikNCiAgDQogICMgR2V0IGxhc3Qgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY29tcGFueQ0KICBmaW5hbF9vYnMgPC0gZGF0YSAlPiUNCiAgICBncm91cF9ieShjdXNpcCkgJT4lDQogICAgc2xpY2VfbWF4KHRzdG9wKSAlPiUNCiAgICB1bmdyb3VwKCkNCiAgDQogICMgRW5zdXJlIGdzZWN0b3IgaXMgYSBmYWN0b3INCiAgZmluYWxfb2JzJGdzZWN0b3IgPC0gYXMuZmFjdG9yKGZpbmFsX29icyRnc2VjdG9yKQ0KICANCiAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMNCiAgcmlza19zY29yZXMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IGZpbmFsX29icywgdHlwZSA9ICJyaXNrIikNCiAgZmluYWxfb2JzJHJpc2tfc2NvcmUgPC0gcmlza19zY29yZXMNCiAgDQogICMgR2V0IHVuaXF1ZSBzZWN0b3JzIHdpdGggc3VmZmljaWVudCBkYXRhDQogIHNlY3RvcnMgPC0gbmFtZXModGFibGUoZmluYWxfb2JzJGdzZWN0b3IpW3RhYmxlKGZpbmFsX29icyRnc2VjdG9yKSA+PSAyMF0pDQogIA0KICAjIENyZWF0ZSBkYXRhIGZyYW1lIGZvciByZXN1bHRzDQogIHNlY3Rvcl9wZXJmIDwtIGRhdGEuZnJhbWUoDQogICAgU2VjdG9yID0gc2VjdG9ycywNCiAgICBDb21wYW5pZXMgPSBOQSwNCiAgICBFdmVudHMgPSBOQSwNCiAgICBFdmVudF9SYXRlID0gTkEsDQogICAgQVVDID0gTkEsDQogICAgQ19JbmRleCA9IE5BLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIA0KICAjIENhbGN1bGF0ZSBtZXRyaWNzIGZvciBlYWNoIHNlY3Rvcg0KICBmb3IoaSBpbiAxOmxlbmd0aChzZWN0b3JzKSkgew0KICAgIHMgPC0gc2VjdG9yc1tpXQ0KICAgIHNlY3Rvcl9kYXRhIDwtIGZpbmFsX29ic1tmaW5hbF9vYnMkZ3NlY3RvciA9PSBzLCBdDQogICAgDQogICAgIyBDb3VudCBjb21wYW5pZXMgYW5kIGV2ZW50cw0KICAgIG5fY29tcGFuaWVzIDwtIG5yb3coc2VjdG9yX2RhdGEpDQogICAgbl9ldmVudHMgPC0gc3VtKHNlY3Rvcl9kYXRhW1tldmVudF90eXBlXV0pDQogICAgZXZlbnRfcmF0ZSA8LSBuX2V2ZW50cyAvIG5fY29tcGFuaWVzICogMTAwDQogICAgDQogICAgc2VjdG9yX3BlcmYkQ29tcGFuaWVzW2ldIDwtIG5fY29tcGFuaWVzDQogICAgc2VjdG9yX3BlcmYkRXZlbnRzW2ldIDwtIG5fZXZlbnRzDQogICAgc2VjdG9yX3BlcmYkRXZlbnRfUmF0ZVtpXSA8LSBldmVudF9yYXRlDQogICAgDQogICAgIyBDYWxjdWxhdGUgQVVDIGlmIHBvc3NpYmxlIChhdCBsZWFzdCA1IGV2ZW50cyBhbmQgNSBub24tZXZlbnRzKQ0KICAgIGlmKG5fZXZlbnRzID49IDUgJiYgKG5fY29tcGFuaWVzIC0gbl9ldmVudHMpID49IDUpIHsNCiAgICAgIGlmKHJlcXVpcmVOYW1lc3BhY2UoInBST0MiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgICAgICAgdHJ5Q2F0Y2goew0KICAgICAgICAgICMgQ2FsY3VsYXRlIEFVQw0KICAgICAgICAgIHJvY19vYmogPC0gcFJPQzo6cm9jKHNlY3Rvcl9kYXRhW1tldmVudF90eXBlXV0sIHNlY3Rvcl9kYXRhJHJpc2tfc2NvcmUsIHF1aWV0ID0gVFJVRSkNCiAgICAgICAgICBzZWN0b3JfcGVyZiRBVUNbaV0gPC0gYXMubnVtZXJpYyhwUk9DOjphdWMocm9jX29iaikpDQogICAgICAgICAgDQogICAgICAgICAgIyBDYWxjdWxhdGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgcmVwb3J0aW5nDQogICAgICAgICAgY2kgPC0gcFJPQzo6Y2kocm9jX29iaikNCiAgICAgICAgICBjYXQoc3ByaW50ZigiU2VjdG9yICVzOiBBVUMgPSAlLjNmICg5NSUlIENJOiAlLjNmLSUuM2YpIHdpdGggJWQgZXZlbnRzICglLjFmJSUpXG4iLCANCiAgICAgICAgICAgICAgICAgICAgcywgc2VjdG9yX3BlcmYkQVVDW2ldLCBjaVsxXSwgY2lbM10sIG5fZXZlbnRzLCBldmVudF9yYXRlKSkNCiAgICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7DQogICAgICAgICAgY2F0KCJFcnJvciBjYWxjdWxhdGluZyBBVUMgZm9yIHNlY3RvciIsIHMsICI6IiwgZSRtZXNzYWdlLCAiXG4iKQ0KICAgICAgICB9KQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBIYXJyZWxsJ3MgQy1pbmRleCAoY29uY29yZGFuY2UpIHVzaW5nIHN1cnZpdmFsIHBhY2thZ2UNCiAgICAgIHRyeUNhdGNoKHsNCiAgICAgICAgIyBDcmVhdGUgYSBzaW1wbGUgQ294IG1vZGVsIGZvciB0aGlzIHNlY3RvciBvbmx5DQogICAgICAgIHNlY3Rvcl9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUwKCJTdXJ2KHRzdG9wLCAiLCBldmVudF90eXBlLCAiKSB+IHJpc2tfc2NvcmUiKSkNCiAgICAgICAgc2VjdG9yX21vZGVsIDwtIGNveHBoKHNlY3Rvcl9mb3JtdWxhLCBkYXRhID0gc2VjdG9yX2RhdGEpDQogICAgICAgIHNlY3Rvcl9wZXJmJENfSW5kZXhbaV0gPC0gc2VjdG9yX21vZGVsJGNvbmNvcmRhbmNlWyJjb25jb3JkYW5jZSJdDQogICAgICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICAgY2F0KCJFcnJvciBjYWxjdWxhdGluZyBDLWluZGV4IGZvciBzZWN0b3IiLCBzLCAiOiIsIGUkbWVzc2FnZSwgIlxuIikNCiAgICAgIH0pDQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiSW5zdWZmaWNpZW50IGV2ZW50cyBpbiBzZWN0b3IiLCBzLCAiZm9yIEFVQyBjYWxjdWxhdGlvbiAoIiwgbl9ldmVudHMsICJldmVudHMpXG4iKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBTb3J0IGJ5IG51bWJlciBvZiBjb21wYW5pZXMNCiAgc2VjdG9yX3BlcmYgPC0gc2VjdG9yX3BlcmZbb3JkZXIoc2VjdG9yX3BlcmYkQ29tcGFuaWVzLCBkZWNyZWFzaW5nID0gVFJVRSksIF0NCiAgDQogICMgUHJpbnQgc3VtbWFyeSBzdGF0aXN0aWNzDQogIGNhdCgiXG5QZXJmb3JtYW5jZSBvZiIsIG1vZGVsX25hbWUsICJtb2RlbCBieSBpbmR1c3RyeSBzZWN0b3I6XG4iKQ0KICBwcmludChzZWN0b3JfcGVyZikNCiAgDQogICMgQ3JlYXRlIHZpc3VhbGl6YXRpb24gb2YgcGVyZm9ybWFuY2UgYnkgc2VjdG9yDQogICMgRmlsdGVyIHNlY3RvcnMgd2l0aCB2YWxpZCBBVUMNCiAgcGxvdF9kYXRhIDwtIHNlY3Rvcl9wZXJmWyFpcy5uYShzZWN0b3JfcGVyZiRBVUMpLCBdDQogIA0KICBpZihucm93KHBsb3RfZGF0YSkgPiAwKSB7DQogICAgIyBTb3J0IGJ5IEFVQyBmb3IgdmlzdWFsaXphdGlvbg0KICAgIHBsb3RfZGF0YSA8LSBwbG90X2RhdGFbb3JkZXIocGxvdF9kYXRhJEFVQywgZGVjcmVhc2luZyA9IFRSVUUpLCBdDQogICAgDQogICAgIyBDcmVhdGUgc2VjdG9yIGxhYmVscw0KICAgIHNlY3Rvcl9sYWJlbHMgPC0gcGFzdGUwKCJTZWN0b3IgIiwgcGxvdF9kYXRhJFNlY3RvciwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiIChuPSIsIHBsb3RfZGF0YSRDb21wYW5pZXMsICIsICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9kYXRhJEV2ZW50cywgIiBldmVudHMpIikNCiAgICANCiAgICAjIFNldCB1cCBwbG90dGluZyBhcmVhIHdpdGggZXh0cmEgc3BhY2UgZm9yIGxhYmVscw0KICAgIHBhcihtYXIgPSBjKDUsIDEyLCA0LCA0KSArIDAuMSkNCiAgICANCiAgICAjIENyZWF0ZSBob3Jpem9udGFsIGJhciBwbG90IG9mIEFVQyB2YWx1ZXMNCiAgICBiYXJwbG90X3Jlc3VsdCA8LSBiYXJwbG90KHBsb3RfZGF0YSRBVUMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzLmFyZyA9IHNlY3Rvcl9sYWJlbHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbiA9IHBhc3RlKG1vZGVsX25hbWUsICJNb2RlbCBQZXJmb3JtYW5jZSBieSBJbmR1c3RyeSBTZWN0b3IiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB4bGFiID0gIkFVQyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhvcml6ID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgeGxpbSA9IGMoMC41LCAxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjb2xvclJhbXBQYWxldHRlKGMoInNreWJsdWUiLCAicm95YWxibHVlIiwgIm5hdnlibHVlIikpKG5yb3cocGxvdF9kYXRhKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZXgubmFtZXMgPSAwLjcpDQogICAgDQogICAgIyBBZGQgZXZlbnQgcmF0ZSBhcyB0ZXh0DQogICAgZm9yKGkgaW4gMTpsZW5ndGgoYmFycGxvdF9yZXN1bHQpKSB7DQogICAgICB0ZXh0KHBsb3RfZGF0YSRBVUNbaV0gKyAwLjAzLCBiYXJwbG90X3Jlc3VsdFtpXSwgDQogICAgICAgICAgIHNwcmludGYoIiUuM2YgKCUuMWYlJSBldmVudHMpIiwgDQogICAgICAgICAgICAgICAgIHBsb3RfZGF0YSRBVUNbaV0sIHBsb3RfZGF0YSRFdmVudF9SYXRlW2ldKSwgDQogICAgICAgICAgIGNleCA9IDAuNykNCiAgICB9DQogICAgDQogICAgIyBBZGQgcmVmZXJlbmNlIGxpbmVzDQogICAgYWJsaW5lKHYgPSBjKDAuNSwgMC43LCAwLjgsIDAuOSksIGx0eSA9IGMoMSwgMiwgMiwgMiksIA0KICAgICAgICAgICBjb2wgPSBjKCJyZWQiLCAib3JhbmdlIiwgImdyZWVuMyIsICJkYXJrZ3JlZW4iKSkNCiAgICANCiAgICAjIEFkZCBsZWdlbmQgZm9yIHJlZmVyZW5jZSBsaW5lcw0KICAgIGxlZ2VuZCgiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgbGVnZW5kID0gYygiQVVDID0gMC41IChSYW5kb20pIiwgIkFVQyA9IDAuNyAoQWNjZXB0YWJsZSkiLCANCiAgICAgICAgICAgICAgICAgICAgICJBVUMgPSAwLjggKEV4Y2VsbGVudCkiLCAiQVVDID0gMC45IChPdXRzdGFuZGluZykiKSwNCiAgICAgICAgICAgbHR5ID0gYygxLCAyLCAyLCAyKSwgDQogICAgICAgICAgIGNvbCA9IGMoInJlZCIsICJvcmFuZ2UiLCAiZ3JlZW4zIiwgImRhcmtncmVlbiIpLA0KICAgICAgICAgICBjZXggPSAwLjcsIGJ0eSA9ICJuIikNCiAgICANCiAgICAjIFJlc2V0IHBsb3R0aW5nIHBhcmFtZXRlcnMNCiAgICBwYXIobWFyID0gYyg1LCA0LCA0LCAyKSArIDAuMSkNCiAgICANCiAgICAjIENyZWF0ZSBidWJibGUgcGxvdCBvZiBBVUMgdnMgZXZlbnQgcmF0ZSwgd2l0aCBidWJibGUgc2l6ZSByZXByZXNlbnRpbmcgbnVtYmVyIG9mIGNvbXBhbmllcw0KICAgIHBsb3QocGxvdF9kYXRhJEV2ZW50X1JhdGUsIHBsb3RfZGF0YSRBVUMsDQogICAgICAgICBtYWluID0gcGFzdGUoIkFVQyB2cyBFdmVudCBSYXRlIGJ5IFNlY3RvciAtIiwgbW9kZWxfbmFtZSwgIk1vZGVsIiksDQogICAgICAgICB4bGFiID0gIkV2ZW50IFJhdGUgKCUpIiwgeWxhYiA9ICJBVUMiLA0KICAgICAgICAgeGxpbSA9IGMoMCwgbWF4KHBsb3RfZGF0YSRFdmVudF9SYXRlKSAqIDEuMSksDQogICAgICAgICB5bGltID0gYyhtaW4oMC41LCBtaW4ocGxvdF9kYXRhJEFVQywgbmEucm09VFJVRSkpLCAxKSwNCiAgICAgICAgIHR5cGUgPSAibiIpDQogICAgDQogICAgIyBBZGQgcmVmZXJlbmNlIGxpbmUgYXQgQVVDID0gMC41IChyYW5kb20gcHJlZGljdGlvbikNCiAgICBhYmxpbmUoaCA9IDAuNSwgbHR5ID0gMiwgY29sID0gInJlZCIpDQogICAgYWJsaW5lKGggPSBjKDAuNywgMC44LCAwLjkpLCBsdHkgPSAzLCBjb2wgPSAiZGFya2dyYXkiKQ0KICAgIA0KICAgICMgQWRkIGdyaWQgZm9yIHJlYWRhYmlsaXR5DQogICAgZ3JpZCgpDQogICAgDQogICAgIyBBZGQgc2VjdG9yIGJ1YmJsZXMNCiAgICBzeW1ib2xzKHBsb3RfZGF0YSRFdmVudF9SYXRlLCBwbG90X2RhdGEkQVVDLCANCiAgICAgICAgICAgY2lyY2xlcyA9IHNxcnQocGxvdF9kYXRhJENvbXBhbmllcykgLyA1LCAgIyBTY2FsZSBidWJibGUgc2l6ZQ0KICAgICAgICAgICBpbmNoZXMgPSBGQUxTRSwgDQogICAgICAgICAgIGJnID0gYWRqdXN0Y29sb3IoInJveWFsYmx1ZSIsIGFscGhhLmYgPSAwLjcpLA0KICAgICAgICAgICBmZyA9ICJibGFjayIsDQogICAgICAgICAgIGFkZCA9IFRSVUUpDQogICAgDQogICAgIyBBZGQgc2VjdG9yIGxhYmVscw0KICAgIHRleHQocGxvdF9kYXRhJEV2ZW50X1JhdGUsIHBsb3RfZGF0YSRBVUMsIA0KICAgICAgICAgbGFiZWxzID0gcGxvdF9kYXRhJFNlY3RvciwgDQogICAgICAgICBwb3MgPSAzLCBjZXggPSAwLjcpDQogICAgDQogICAgIyBBZGQgaW50ZXJwcmV0YXRpb24gbGFiZWxzDQogICAgdGV4dCh4ID0gbWF4KHBsb3RfZGF0YSRFdmVudF9SYXRlKSAqIDAuOSwgeSA9IDAuNTUsIA0KICAgICAgICAgIlBvb3IgUGVyZm9ybWFuY2UiLCBjb2wgPSAicmVkIiwgY2V4ID0gMC44KQ0KICAgIHRleHQoeCA9IG1heChwbG90X2RhdGEkRXZlbnRfUmF0ZSkgKiAwLjksIHkgPSAwLjc1LCANCiAgICAgICAgICJHb29kIFBlcmZvcm1hbmNlIiwgY29sID0gImRhcmtncmVlbiIsIGNleCA9IDAuOCkNCiAgICB0ZXh0KHggPSBtYXgocGxvdF9kYXRhJEV2ZW50X1JhdGUpICogMC45LCB5ID0gMC45NSwgDQogICAgICAgICAiRXhjZWxsZW50IFBlcmZvcm1hbmNlIiwgY29sID0gImRhcmtncmVlbiIsIGNleCA9IDAuOCwgZm9udCA9IDIpDQogICAgDQogICAgIyBBZGQgYnViYmxlIHNpemUgbGVnZW5kDQogICAgbGVnZW5kX3NpemVzIDwtIGMobWluKHBsb3RfZGF0YSRDb21wYW5pZXMpLCANCiAgICAgICAgICAgICAgICAgICAgICBtZWRpYW4ocGxvdF9kYXRhJENvbXBhbmllcyksIA0KICAgICAgICAgICAgICAgICAgICAgIG1heChwbG90X2RhdGEkQ29tcGFuaWVzKSkNCiAgICANCiAgICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgDQogICAgICAgICAgIGxlZ2VuZCA9IHBhc3RlMChyb3VuZChsZWdlbmRfc2l6ZXMpLCAiIGNvbXBhbmllcyIpLA0KICAgICAgICAgICBwdC5jZXggPSBzcXJ0KGxlZ2VuZF9zaXplcykgLyA1ICogMiwgICMgQWRqdXN0IGZvciBiZXR0ZXIgdmlzaWJpbGl0eQ0KICAgICAgICAgICBwY2ggPSAyMSwgDQogICAgICAgICAgIGNvbCA9ICJibGFjayIsIA0KICAgICAgICAgICBwdC5iZyA9IGFkanVzdGNvbG9yKCJyb3lhbGJsdWUiLCBhbHBoYS5mID0gMC43KSwNCiAgICAgICAgICAgY2V4ID0gMC43LCBidHkgPSAibiIsIA0KICAgICAgICAgICB0aXRsZSA9ICJCdWJibGUgU2l6ZSBMZWdlbmQiKQ0KICB9IGVsc2Ugew0KICAgIGNhdCgiTm8gc2VjdG9ycyB3aXRoIHZhbGlkIEFVQyB2YWx1ZXMgZm9yIHBsb3R0aW5nLlxuIikNCiAgfQ0KICANCiAgIyBJZGVudGlmeSBiZXN0IGFuZCB3b3JzdCBwZXJmb3JtaW5nIHNlY3RvcnMNCiAgdmFsaWRfc2VjdG9ycyA8LSBzZWN0b3JfcGVyZlshaXMubmEoc2VjdG9yX3BlcmYkQVVDKSAmIHNlY3Rvcl9wZXJmJEV2ZW50cyA+PSA1LCBdDQogIGlmKG5yb3codmFsaWRfc2VjdG9ycykgPiAwKSB7DQogICAgYmVzdF9zZWN0b3IgPC0gdmFsaWRfc2VjdG9yc1t3aGljaC5tYXgodmFsaWRfc2VjdG9ycyRBVUMpLCBdDQogICAgd29yc3Rfc2VjdG9yIDwtIHZhbGlkX3NlY3RvcnNbd2hpY2gubWluKHZhbGlkX3NlY3RvcnMkQVVDKSwgXQ0KICAgIA0KICAgIGNhdCgiXG5TZWN0b3Igd2l0aCBiZXN0IG1vZGVsIHBlcmZvcm1hbmNlOiIsIGJlc3Rfc2VjdG9yJFNlY3RvciwgDQogICAgICAgICIoQVVDID0iLCByb3VuZChiZXN0X3NlY3RvciRBVUMsIDMpLCAid2l0aCIsIGJlc3Rfc2VjdG9yJEV2ZW50cywgDQogICAgICAgICJldmVudHMsIGV2ZW50IHJhdGUgPSIsIHJvdW5kKGJlc3Rfc2VjdG9yJEV2ZW50X1JhdGUsIDEpLCAiJSlcbiIpDQogICAgDQogICAgY2F0KCJTZWN0b3Igd2l0aCB3b3JzdCBtb2RlbCBwZXJmb3JtYW5jZToiLCB3b3JzdF9zZWN0b3IkU2VjdG9yLCANCiAgICAgICAgIihBVUMgPSIsIHJvdW5kKHdvcnN0X3NlY3RvciRBVUMsIDMpLCAid2l0aCIsIHdvcnN0X3NlY3RvciRFdmVudHMsIA0KICAgICAgICAiZXZlbnRzLCBldmVudCByYXRlID0iLCByb3VuZCh3b3JzdF9zZWN0b3IkRXZlbnRfUmF0ZSwgMSksICIlKVxuXG4iKQ0KICAgIA0KICAgICMgUHJvdmlkZSBwb3RlbnRpYWwgZXhwbGFuYXRpb24gZm9yIHBlcmZvcm1hbmNlIGRpZmZlcmVuY2VzDQogICAgY2F0KCJQb3RlbnRpYWwgZXhwbGFuYXRpb25zIGZvciBwZXJmb3JtYW5jZSBkaWZmZXJlbmNlczpcbiIpDQogICAgDQogICAgIyBDaGVjayBpZiBldmVudCByYXRlIGNvcnJlbGF0ZXMgd2l0aCBwZXJmb3JtYW5jZQ0KICAgIGNvcl9yYXRlX2F1YyA8LSBjb3IodmFsaWRfc2VjdG9ycyRFdmVudF9SYXRlLCB2YWxpZF9zZWN0b3JzJEFVQywgdXNlID0gImNvbXBsZXRlLm9icyIpDQogICAgDQogICAgaWYoYWJzKGNvcl9yYXRlX2F1YykgPiAwLjUpIHsNCiAgICAgIGlmKGNvcl9yYXRlX2F1YyA+IDApIHsNCiAgICAgICAgY2F0KCItIFNlY3RvcnMgd2l0aCBoaWdoZXIgZXZlbnQgcmF0ZXMgdGVuZCB0byBoYXZlIGJldHRlciBtb2RlbCBwZXJmb3JtYW5jZVxuIikNCiAgICAgICAgY2F0KCIgIFRoaXMgc3VnZ2VzdHMgdGhlIG1vZGVsIHdvcmtzIGJlc3QgaW4gaGlnaC1yaXNrIGluZHVzdHJpZXNcbiIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIi0gU2VjdG9ycyB3aXRoIGxvd2VyIGV2ZW50IHJhdGVzIHRlbmQgdG8gaGF2ZSBiZXR0ZXIgbW9kZWwgcGVyZm9ybWFuY2VcbiIpDQogICAgICAgIGNhdCgiICBUaGlzIHN1Z2dlc3RzIHRoZSBtb2RlbCB3b3JrcyBiZXN0IGluIG1vcmUgc3RhYmxlIGluZHVzdHJpZXNcbiIpDQogICAgICB9DQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiLSBObyBzdHJvbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gZXZlbnQgcmF0ZXMgYW5kIG1vZGVsIHBlcmZvcm1hbmNlIGFjcm9zcyBzZWN0b3JzXG4iKQ0KICAgICAgY2F0KCIgIFNlY3Rvci1zcGVjaWZpYyBmYWN0b3JzIGJleW9uZCBmcmVxdWVuY3kgb2YgZXZlbnRzIGFmZmVjdCBwcmVkaWN0YWJpbGl0eVxuIikNCiAgICB9DQogICAgDQogICAgIyBDb21wYXJlIGJlc3QgYW5kIHdvcnN0IHNlY3RvcnMNCiAgICBjYXQoIlxuQ29tcGFyaXNvbiBvZiBiZXN0IHZzLiB3b3JzdCBwZXJmb3JtaW5nIHNlY3RvcnM6XG4iKQ0KICAgIGNhdCgiLSBCZXN0IChTZWN0b3IiLCBiZXN0X3NlY3RvciRTZWN0b3IsICIpOiBBVUMgPSIsIHJvdW5kKGJlc3Rfc2VjdG9yJEFVQywgMyksIA0KICAgICAgICAid2l0aCBldmVudCByYXRlIG9mIiwgcm91bmQoYmVzdF9zZWN0b3IkRXZlbnRfUmF0ZSwgMSksICIlXG4iKQ0KICAgIGNhdCgiLSBXb3JzdCAoU2VjdG9yIiwgd29yc3Rfc2VjdG9yJFNlY3RvciwgIik6IEFVQyA9Iiwgcm91bmQod29yc3Rfc2VjdG9yJEFVQywgMyksIA0KICAgICAgICAid2l0aCBldmVudCByYXRlIG9mIiwgcm91bmQod29yc3Rfc2VjdG9yJEV2ZW50X1JhdGUsIDEpLCAiJVxuIikNCiAgICANCiAgICAjIENvbXBhcmUgbnVtYmVyIG9mIGNvbXBhbmllcw0KICAgIGlmKGJlc3Rfc2VjdG9yJENvbXBhbmllcyA+IHdvcnN0X3NlY3RvciRDb21wYW5pZXMgKiAyKSB7DQogICAgICBjYXQoIi0gVGhlIGJlc3QgcGVyZm9ybWluZyBzZWN0b3IgaGFzIHNpZ25pZmljYW50bHkgbW9yZSBjb21wYW5pZXMsXG4iKQ0KICAgICAgY2F0KCIgIHN1Z2dlc3RpbmcgbW9yZSBkYXRhIGltcHJvdmVzIHByZWRpY3Rpb24gYWNjdXJhY3lcbiIpDQogICAgfSBlbHNlIGlmKHdvcnN0X3NlY3RvciRDb21wYW5pZXMgPiBiZXN0X3NlY3RvciRDb21wYW5pZXMgKiAyKSB7DQogICAgICBjYXQoIi0gVGhlIHdvcnN0IHBlcmZvcm1pbmcgc2VjdG9yIGhhcyBtb3JlIGNvbXBhbmllcyBidXQgcG9vcmVyIHBlcmZvcm1hbmNlLFxuIikNCiAgICAgIGNhdCgiICBzdWdnZXN0aW5nIHN0cnVjdHVyYWwgZGlmZmVyZW5jZXMgaW4gcHJlZGljdGFiaWxpdHkgcmF0aGVyIHRoYW4gZGF0YSB2b2x1bWUgaXNzdWVzXG4iKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBSZXR1cm4gc2VjdG9yIHBlcmZvcm1hbmNlIGRhdGENCiAgcmV0dXJuKHNlY3Rvcl9wZXJmKQ0KfQ0KDQojIEV2YWx1YXRlIG1vZGVsIHBlcmZvcm1hbmNlIGJ5IHNlY3RvciBmb3IgYm90aCBtb2RlbHMNCmJhbmtydXB0Y3lfc2VjdG9yX3BlcmYgPC0gZXZhbHVhdGVfbW9kZWxfYnlfc2VjdG9yKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwsICJCYW5rcnVwdGN5IiwgImJhbmtydXB0Y3kiKQ0KYWNxdWlzaXRpb25fc2VjdG9yX3BlcmYgPC0gZXZhbHVhdGVfbW9kZWxfYnlfc2VjdG9yKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiQWNxdWlzaXRpb24iLCAiYWNxdWlzaXRpb24iKQ0KDQojIENvbXBhcmUgc2VjdG9yIHBlcmZvcm1hbmNlIGJldHdlZW4gbW9kZWxzDQppZighaXMubnVsbChiYW5rcnVwdGN5X3NlY3Rvcl9wZXJmKSAmJiAhaXMubnVsbChhY3F1aXNpdGlvbl9zZWN0b3JfcGVyZikpIHsNCiAgY2F0KCJcbkNPTVBBUklORyBNT0RFTCBQRVJGT1JNQU5DRSBBQ1JPU1MgU0VDVE9SUzpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgDQogICMgRmluZCBjb21tb24gc2VjdG9ycyB3aXRoIHZhbGlkIHBlcmZvcm1hbmNlIG1ldHJpY3MNCiAgY29tbW9uX3NlY3RvcnMgPC0gaW50ZXJzZWN0KA0KICAgIGJhbmtydXB0Y3lfc2VjdG9yX3BlcmYkU2VjdG9yWyFpcy5uYShiYW5rcnVwdGN5X3NlY3Rvcl9wZXJmJEFVQyldLA0KICAgIGFjcXVpc2l0aW9uX3NlY3Rvcl9wZXJmJFNlY3RvclshaXMubmEoYWNxdWlzaXRpb25fc2VjdG9yX3BlcmYkQVVDKV0NCiAgKQ0KICANCiAgaWYobGVuZ3RoKGNvbW1vbl9zZWN0b3JzKSA+IDApIHsNCiAgICBjb21wYXJpc29uIDwtIGRhdGEuZnJhbWUoDQogICAgICBTZWN0b3IgPSBjb21tb25fc2VjdG9ycywNCiAgICAgIEJhbmtydXB0Y3lfQVVDID0gTkEsDQogICAgICBBY3F1aXNpdGlvbl9BVUMgPSBOQSwNCiAgICAgIERpZmZlcmVuY2UgPSBOQSwNCiAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICAgICkNCiAgICANCiAgICAjIEZpbGwgaW4gdmFsdWVzDQogICAgZm9yKGkgaW4gMTpsZW5ndGgoY29tbW9uX3NlY3RvcnMpKSB7DQogICAgICBzIDwtIGNvbW1vbl9zZWN0b3JzW2ldDQogICAgICBjb21wYXJpc29uJEJhbmtydXB0Y3lfQVVDW2ldIDwtIGJhbmtydXB0Y3lfc2VjdG9yX3BlcmYkQVVDW2JhbmtydXB0Y3lfc2VjdG9yX3BlcmYkU2VjdG9yID09IHNdDQogICAgICBjb21wYXJpc29uJEFjcXVpc2l0aW9uX0FVQ1tpXSA8LSBhY3F1aXNpdGlvbl9zZWN0b3JfcGVyZiRBVUNbYWNxdWlzaXRpb25fc2VjdG9yX3BlcmYkU2VjdG9yID09IHNdDQogICAgICBjb21wYXJpc29uJERpZmZlcmVuY2VbaV0gPC0gY29tcGFyaXNvbiRCYW5rcnVwdGN5X0FVQ1tpXSAtIGNvbXBhcmlzb24kQWNxdWlzaXRpb25fQVVDW2ldDQogICAgfQ0KICAgIA0KICAgICMgU29ydCBieSBhYnNvbHV0ZSBkaWZmZXJlbmNlDQogICAgY29tcGFyaXNvbiA8LSBjb21wYXJpc29uW29yZGVyKGFicyhjb21wYXJpc29uJERpZmZlcmVuY2UpLCBkZWNyZWFzaW5nID0gVFJVRSksIF0NCiAgICANCiAgICAjIFByaW50IGNvbXBhcmlzb24NCiAgICBjYXQoIlBlcmZvcm1hbmNlIGNvbXBhcmlzb24gYWNyb3NzIiwgbGVuZ3RoKGNvbW1vbl9zZWN0b3JzKSwgInNlY3RvcnMgd2l0aCB2YWxpZCBtZXRyaWNzOlxuXG4iKQ0KICAgIHByaW50KGNvbXBhcmlzb24pDQogICAgDQogICAgIyBJZGVudGlmeSBzZWN0b3JzIHdpdGggbGFyZ2VzdCBkaWZmZXJlbmNlcw0KICAgIGlmKG5yb3coY29tcGFyaXNvbikgPiAwKSB7DQogICAgICBtYXhfZGlmZl9zZWN0b3IgPC0gY29tcGFyaXNvblt3aGljaC5tYXgoYWJzKGNvbXBhcmlzb24kRGlmZmVyZW5jZSkpLCBdDQogICAgICANCiAgICAgIGNhdCgiXG5TZWN0b3Igd2l0aCBsYXJnZXN0IHBlcmZvcm1hbmNlIGRpZmZlcmVuY2U6IiwgbWF4X2RpZmZfc2VjdG9yJFNlY3RvciwgIlxuIikNCiAgICAgIGNhdCgiLSBCYW5rcnVwdGN5IG1vZGVsIEFVQzoiLCByb3VuZChtYXhfZGlmZl9zZWN0b3IkQmFua3J1cHRjeV9BVUMsIDMpLCAiXG4iKQ0KICAgICAgY2F0KCItIEFjcXVpc2l0aW9uIG1vZGVsIEFVQzoiLCByb3VuZChtYXhfZGlmZl9zZWN0b3IkQWNxdWlzaXRpb25fQVVDLCAzKSwgIlxuIikNCiAgICAgIGNhdCgiLSBBYnNvbHV0ZSBkaWZmZXJlbmNlOiIsIHJvdW5kKGFicyhtYXhfZGlmZl9zZWN0b3IkRGlmZmVyZW5jZSksIDMpLCAiXG4iKQ0KICAgICAgDQogICAgICBpZihtYXhfZGlmZl9zZWN0b3IkQmFua3J1cHRjeV9BVUMgPiBtYXhfZGlmZl9zZWN0b3IkQWNxdWlzaXRpb25fQVVDKSB7DQogICAgICAgIGNhdCgiLSBCYW5rcnVwdGN5IGlzIG1vcmUgcHJlZGljdGFibGUgdGhhbiBhY3F1aXNpdGlvbiBpbiB0aGlzIHNlY3RvclxuIikNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGNhdCgiLSBBY3F1aXNpdGlvbiBpcyBtb3JlIHByZWRpY3RhYmxlIHRoYW4gYmFua3J1cHRjeSBpbiB0aGlzIHNlY3RvclxuIikNCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBDcmVhdGUgc2NhdHRlciBwbG90IGNvbXBhcmluZyBwZXJmb3JtYW5jZSBhY3Jvc3Mgc2VjdG9ycw0KICAgICAgcGxvdChjb21wYXJpc29uJEJhbmtydXB0Y3lfQVVDLCBjb21wYXJpc29uJEFjcXVpc2l0aW9uX0FVQywNCiAgICAgICAgICAgbWFpbiA9ICJNb2RlbCBQZXJmb3JtYW5jZSBDb21wYXJpc29uIGJ5IFNlY3RvciIsDQogICAgICAgICAgIHhsYWIgPSAiQmFua3J1cHRjeSBNb2RlbCBBVUMiLCB5bGFiID0gIkFjcXVpc2l0aW9uIE1vZGVsIEFVQyIsDQogICAgICAgICAgIHhsaW0gPSBjKDAuNSwgMSksIHlsaW0gPSBjKDAuNSwgMSksDQogICAgICAgICAgIHBjaCA9IDE2LCBjb2wgPSAiYmx1ZSIpDQogICAgICANCiAgICAgICMgQWRkIHNlY3RvciBsYWJlbHMNCiAgICAgIHRleHQoY29tcGFyaXNvbiRCYW5rcnVwdGN5X0FVQywgY29tcGFyaXNvbiRBY3F1aXNpdGlvbl9BVUMsIA0KICAgICAgICAgICBsYWJlbHMgPSBjb21wYXJpc29uJFNlY3RvciwgcG9zID0gMywgY2V4ID0gMC43KQ0KICAgICAgDQogICAgICAjIEFkZCBkaWFnb25hbCBsaW5lIGZvciBlcXVhbCBwZXJmb3JtYW5jZQ0KICAgICAgYWJsaW5lKDAsIDEsIGx0eSA9IDIsIGNvbCA9ICJncmF5IikNCiAgICAgIA0KICAgICAgIyBBZGQgcXVhZHJhbnQgbGFiZWxzDQogICAgICB0ZXh0KDAuNTUsIDAuOTUsICJBY3F1aXNpdGlvbiBiZXR0ZXJcbnByZWRpY3RlZCIsIGNvbCA9ICJkYXJrcmVkIiwgY2V4ID0gMC44KQ0KICAgICAgdGV4dCgwLjk1LCAwLjU1LCAiQmFua3J1cHRjeSBiZXR0ZXJcbnByZWRpY3RlZCIsIGNvbCA9ICJkYXJrYmx1ZSIsIGNleCA9IDAuOCkNCiAgICAgIHRleHQoMC45NSwgMC45NSwgIkJvdGggd2VsbFxucHJlZGljdGVkIiwgY29sID0gImRhcmtncmVlbiIsIGNleCA9IDAuOCkNCiAgICAgIHRleHQoMC41NSwgMC41NSwgIkJvdGggcG9vcmx5XG5wcmVkaWN0ZWQiLCBjb2wgPSAicHVycGxlIiwgY2V4ID0gMC44KQ0KICAgICAgDQogICAgICAjIEFkZCByZWZlcmVuY2UgbGluZXMNCiAgICAgIGFibGluZShoID0gMC43LCB2ID0gMC43LCBsdHkgPSAzLCBjb2wgPSAiZGFya2dyYXkiKQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBhdmVyYWdlIHBlcmZvcm1hbmNlIGRpZmZlcmVuY2UNCiAgICAgIGF2Z19kaWZmIDwtIG1lYW4oY29tcGFyaXNvbiRCYW5rcnVwdGN5X0FVQyAtIGNvbXBhcmlzb24kQWNxdWlzaXRpb25fQVVDKQ0KICAgICAgDQogICAgICBpZihhYnMoYXZnX2RpZmYpID4gMC4wMykgew0KICAgICAgICBpZihhdmdfZGlmZiA+IDApIHsNCiAgICAgICAgICBjYXQoIlxuT24gYXZlcmFnZSwgYmFua3J1cHRjeSBpcyBtb3JlIHByZWRpY3RhYmxlIGFjcm9zcyBzZWN0b3JzIChieSIsIA0KICAgICAgICAgICAgICByb3VuZChhdmdfZGlmZiwgMyksICJBVUMgcG9pbnRzKVxuIikNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICBjYXQoIlxuT24gYXZlcmFnZSwgYWNxdWlzaXRpb24gaXMgbW9yZSBwcmVkaWN0YWJsZSBhY3Jvc3Mgc2VjdG9ycyAoYnkiLCANCiAgICAgICAgICAgICAgcm91bmQoLWF2Z19kaWZmLCAzKSwgIkFVQyBwb2ludHMpXG4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIlxuT24gYXZlcmFnZSwgYm90aCBvdXRjb21lcyBoYXZlIHNpbWlsYXIgcHJlZGljdGFiaWxpdHkgYWNyb3NzIHNlY3RvcnNcbiIpDQogICAgICB9DQogICAgICANCiAgICAgICMgQ2FsY3VsYXRlIGNvcnJlbGF0aW9uIGJldHdlZW4gc2VjdG9yIHBlcmZvcm1hbmNlIG9uIGJvdGggbW9kZWxzDQogICAgICBwZXJmX2NvciA8LSBjb3IoY29tcGFyaXNvbiRCYW5rcnVwdGN5X0FVQywgY29tcGFyaXNvbiRBY3F1aXNpdGlvbl9BVUMpDQogICAgICANCiAgICAgIGNhdCgiQ29ycmVsYXRpb24gYmV0d2VlbiBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiBwcmVkaWN0aW9uIHBlcmZvcm1hbmNlOiIsIA0KICAgICAgICAgIHJvdW5kKHBlcmZfY29yLCAzKSwgIlxuIikNCiAgICAgIA0KICAgICAgaWYocGVyZl9jb3IgPiAwLjcpIHsNCiAgICAgICAgY2F0KCJUaGUgc3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIHN1Z2dlc3RzIHNlY3RvcnMgdGhhdCBhcmUgcHJlZGljdGFibGUgZm9yIG9uZVxuIikNCiAgICAgICAgY2F0KCJvdXRjb21lIHRlbmQgdG8gYmUgcHJlZGljdGFibGUgZm9yIHRoZSBvdGhlciBhcyB3ZWxsLiBUaGlzIGluZGljYXRlcyBzaW1pbGFyXG4iKQ0KICAgICAgICBjYXQoInVuZGVybHlpbmcgZHJpdmVycyBvZiBwcmVkaWN0YWJpbGl0eSBhY3Jvc3Mgc2VjdG9ycy5cbiIpDQogICAgICB9IGVsc2UgaWYocGVyZl9jb3IgPiAwLjMpIHsNCiAgICAgICAgY2F0KCJUaGUgbW9kZXJhdGUgY29ycmVsYXRpb24gc3VnZ2VzdHMgc29tZSBjb21tb24gZmFjdG9ycyBhZmZlY3RpbmcgcHJlZGljdGFiaWxpdHlcbiIpDQogICAgICAgIGNhdCgiYWNyb3NzIG91dGNvbWVzLCBidXQgYWxzbyBpbXBvcnRhbnQgZGlmZmVyZW5jZXMgaW4gd2hhdCBtYWtlcyBzZWN0b3JzIHByZWRpY3RhYmxlXG4iKQ0KICAgICAgICBjYXQoImZvciBiYW5rcnVwdGN5IHZlcnN1cyBhY3F1aXNpdGlvbi5cbiIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIlRoZSB3ZWFrIGNvcnJlbGF0aW9uIHN1Z2dlc3RzIGRpZmZlcmVudCBmYWN0b3JzIGRyaXZlIHByZWRpY3RhYmlsaXR5IGZvclxuIikNCiAgICAgICAgY2F0KCJiYW5rcnVwdGN5IHZlcnN1cyBhY3F1aXNpdGlvbiBhY3Jvc3Mgc2VjdG9ycy4gU2VjdG9ycyBwcmVkaWN0YWJsZSBmb3Igb25lXG4iKQ0KICAgICAgICBjYXQoIm91dGNvbWUgYXJlbid0IG5lY2Vzc2FyaWx5IHByZWRpY3RhYmxlIGZvciB0aGUgb3RoZXIuXG4iKQ0KICAgICAgfQ0KICAgIH0NCiAgfSBlbHNlIHsNCiAgICBjYXQoIk5vIGNvbW1vbiBzZWN0b3JzIHdpdGggdmFsaWQgcGVyZm9ybWFuY2UgbWV0cmljcyBmb3IgYm90aCBtb2RlbHMuXG4iKQ0KICB9DQp9DQpgYGANCkFnYWluLCBub3Qgc3VyZSBpZiByZWFsbHkgbmVjZXNzYXJ5IF5eXl5eXl5eXl5eXl5eXg0KDQpOZXh0IHdlIGRvIGFuIGVuaGFuY2VkIHJpc2sgc3RyYXRpZmljYXRpb24gYW5hbHlzaXMuIFRoaXMgd2lsbCBoZWxwIHVzIHVuZGVyc3RhbmQgaG93IHRoZSBtb2RlbCdzIHByZWRpY3Rpb25zIHZhcnkgYWNyb3NzIGRpZmZlcmVudCByaXNrIGxldmVscyBhbmQgd2hldGhlciB0aGVyZSBhcmUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgaW4gZXZlbnQgcmF0ZXMgYnkgcmlzayBsZXZlbC4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTMuMTU6IEVOSEFOQ0VEIFJJU0sgU1RSQVRJRklDQVRJT04gQU5BTFlTSVMgKEZJWEVEKQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xMy4xNTogRU5IQU5DRUQgUklTSyBTVFJBVElGSUNBVElPTiBBTkFMWVNJU1xuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRnVuY3Rpb24gdG8gY3JlYXRlIGNvbXByZWhlbnNpdmUgcmlzayBzdHJhdGlmaWNhdGlvbiB3aXRoIG1hY3JvZWNvbm9taWMgY29udGV4dA0KY3JlYXRlX3Jpc2tfc3RyYXRpZmljYXRpb24gPC0gZnVuY3Rpb24obW9kZWwsIG1vZGVsX25hbWUsIGV2ZW50X3R5cGVfbmFtZSkgew0KICBjYXQoIkNyZWF0aW5nIGVuaGFuY2VkIHJpc2sgc3RyYXRpZmljYXRpb24gZm9yIiwgbW9kZWxfbmFtZSwgIm1vZGVsLi4uXG4iKQ0KICANCiAgIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55DQogIGZpbmFsX29icyA8LSBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICANCiAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMNCiAgcmlza19zY29yZXMgPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IGZpbmFsX29icywgdHlwZSA9ICJyaXNrIikNCiAgDQogICMgQWRkIHNjb3JlcyB0byBkYXRhDQogIGZpbmFsX29icyRyaXNrX3Njb3JlIDwtIHJpc2tfc2NvcmVzDQogIA0KICAjIENyZWF0ZSByaXNrIHF1aW50aWxlcyBmb3IgbW9yZSBncmFudWxhciBhbmFseXNpcw0KICByaXNrX3F1YW50aWxlcyA8LSBxdWFudGlsZShyaXNrX3Njb3JlcywgcHJvYnMgPSBzZXEoMCwgMSwgMC4yKSkNCiAgZmluYWxfb2JzJHJpc2tfcXVpbnRpbGUgPC0gY3V0KHJpc2tfc2NvcmVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSByaXNrX3F1YW50aWxlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJRMSAoTG93ZXN0KSIsICJRMiIsICJRMyIsICJRNCIsICJRNSAoSGlnaGVzdCkiKSkNCiAgDQogICMgR2V0IHRoZSBldmVudCBjb2x1bW4gZGlyZWN0bHkgKGRvbid0IHVzZSBbWyBdXSB3aXRoIGEgdmFyaWFibGUpDQogIGlmKGV2ZW50X3R5cGVfbmFtZSA9PSAiYmFua3J1cHRjeSIpIHsNCiAgICBldmVudF9jb2wgPC0gZmluYWxfb2JzJGJhbmtydXB0Y3kNCiAgfSBlbHNlIGlmKGV2ZW50X3R5cGVfbmFtZSA9PSAiYWNxdWlzaXRpb24iKSB7DQogICAgZXZlbnRfY29sIDwtIGZpbmFsX29icyRhY3F1aXNpdGlvbg0KICB9IGVsc2Ugew0KICAgIHN0b3AoIlVua25vd24gZXZlbnQgdHlwZTogIiwgZXZlbnRfdHlwZV9uYW1lKQ0KICB9DQogIA0KICAjIENhbGN1bGF0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGJ5IHF1aW50aWxlDQogIHF1aW50aWxlX3N0YXRzIDwtIGFnZ3JlZ2F0ZShldmVudF9jb2wgfiByaXNrX3F1aW50aWxlLCBkYXRhID0gZmluYWxfb2JzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSBjKG1lYW4gPSBtZWFuKHgpLCBzdW0gPSBzdW0oeCksIG4gPSBsZW5ndGgoeCkpKQ0KICANCiAgIyBGb3JtYXQgdGhlIGRhdGEgZm9yIGVhc2llciB1c2UNCiAgcXVpbnRpbGVfc3VtbWFyeSA8LSBkYXRhLmZyYW1lKA0KICAgIFF1aW50aWxlID0gcXVpbnRpbGVfc3RhdHMkcmlza19xdWludGlsZSwNCiAgICBFdmVudHMgPSBxdWludGlsZV9zdGF0cyRldmVudF9jb2xbLCAic3VtIl0sDQogICAgQ29tcGFuaWVzID0gcXVpbnRpbGVfc3RhdHMkZXZlbnRfY29sWywgIm4iXSwNCiAgICBFdmVudF9SYXRlID0gcXVpbnRpbGVfc3RhdHMkZXZlbnRfY29sWywgIm1lYW4iXSAqIDEwMCwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KICANCiAgIyBDYWxjdWxhdGUgYWRkaXRpb25hbCBtZXRyaWNzDQogIHF1aW50aWxlX3N1bW1hcnkkQ3VtdWxhdGl2ZV9FdmVudHMgPC0gY3Vtc3VtKHF1aW50aWxlX3N1bW1hcnkkRXZlbnRzKQ0KICBxdWludGlsZV9zdW1tYXJ5JEN1bXVsYXRpdmVfUGVyY2VudCA8LSBxdWludGlsZV9zdW1tYXJ5JEN1bXVsYXRpdmVfRXZlbnRzIC8gc3VtKHF1aW50aWxlX3N1bW1hcnkkRXZlbnRzKSAqIDEwMA0KICBxdWludGlsZV9zdW1tYXJ5JENhcHR1cmVfUmF0ZSA8LSBxdWludGlsZV9zdW1tYXJ5JEV2ZW50cyAvIHF1aW50aWxlX3N1bW1hcnkkQ29tcGFuaWVzICogMTAwDQogIA0KICAjIENhbGN1bGF0ZSByZWxhdGl2ZSByaXNrIHZlcnN1cyBsb3dlc3QgcXVpbnRpbGUNCiAgYmFzZV9yYXRlIDwtIHF1aW50aWxlX3N1bW1hcnkkRXZlbnRfUmF0ZVsxXQ0KICBxdWludGlsZV9zdW1tYXJ5JFJlbGF0aXZlX1Jpc2sgPC0gcXVpbnRpbGVfc3VtbWFyeSRFdmVudF9SYXRlIC8gYmFzZV9yYXRlDQogIA0KICAjIFByaW50IHN1bW1hcnkgdGFibGUNCiAgY2F0KCJcblJpc2sgU3RyYXRpZmljYXRpb24gU3VtbWFyeSBmb3IiLCBtb2RlbF9uYW1lLCAiTW9kZWw6XG4iKQ0KICBwcmludChxdWludGlsZV9zdW1tYXJ5KQ0KICANCiAgIyBDYWxjdWxhdGUgZGlzY3JpbWluYXRpb24gcmF0aW8gKGhpZ2hlc3QgcXVpbnRpbGUgcmF0ZSAvIGxvd2VzdCBxdWludGlsZSByYXRlKQ0KICBkaXNjcmltaW5hdGlvbl9yYXRpbyA8LSBxdWludGlsZV9zdW1tYXJ5JEV2ZW50X1JhdGVbNV0gLyBxdWludGlsZV9zdW1tYXJ5JEV2ZW50X1JhdGVbMV0NCiAgDQogICMgQ3JlYXRlIGJhciBwbG90IG9mIGV2ZW50IHJhdGVzDQogIGJhcnBsb3RfcmVzdWx0IDwtIGJhcnBsb3QocXVpbnRpbGVfc3VtbWFyeSRFdmVudF9SYXRlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMuYXJnID0gcXVpbnRpbGVfc3VtbWFyeSRRdWludGlsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbiA9IHBhc3RlKCJFdmVudCBSYXRlcyBieSBSaXNrIFF1aW50aWxlIC0iLCBtb2RlbF9uYW1lLCAiTW9kZWwiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJSaXNrIFF1aW50aWxlIiwgeWxhYiA9ICJFdmVudCBSYXRlICglKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZ3JlZW4iLCAieWVsbG93IiwgIm9yYW5nZSIsICJyZWQiLCAiZGFya3JlZCIpKSg1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHF1aW50aWxlX3N1bW1hcnkkRXZlbnRfUmF0ZSkgKiAxLjMpKQ0KICANCiAgIyBBZGQgdGV4dCBsYWJlbHMgd2l0aCBldmVudCBjb3VudHMNCiAgdGV4dChiYXJwbG90X3Jlc3VsdCwgDQogICAgICAgcXVpbnRpbGVfc3VtbWFyeSRFdmVudF9SYXRlICsgbWF4KHF1aW50aWxlX3N1bW1hcnkkRXZlbnRfUmF0ZSkgKiAwLjA1LCANCiAgICAgICBwYXN0ZTAocm91bmQocXVpbnRpbGVfc3VtbWFyeSRFdmVudF9SYXRlLCAxKSwgIiVcbigiLCANCiAgICAgICAgICAgICAgcXVpbnRpbGVfc3VtbWFyeSRFdmVudHMsICIvIiwgcXVpbnRpbGVfc3VtbWFyeSRDb21wYW5pZXMsICIpIiksIA0KICAgICAgIGNleCA9IDAuOCkNCiAgDQogICMgQWRkIGRpc2NyaW1pbmF0aW9uIHJhdGlvDQogIG10ZXh0KHBhc3RlKCJEaXNjcmltaW5hdGlvbiBSYXRpbyAoUTUvUTEpOiIsIHJvdW5kKGRpc2NyaW1pbmF0aW9uX3JhdGlvLCAxKSwgIsOXIiksIA0KICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC44KQ0KICANCiAgIyBDcmVhdGUgTG9yZW56IGN1cnZlIGZvciBldmVudCBjb25jZW50cmF0aW9uDQogICMgQ2FsY3VsYXRlIGN1bXVsYXRpdmUgcGVyY2VudGFnZSBvZiBjb21wYW5pZXMgYW5kIGV2ZW50cw0KICBjdW1fcGN0X2NvbXBhbmllcyA8LSBjdW1zdW0ocXVpbnRpbGVfc3VtbWFyeSRDb21wYW5pZXMpIC8gc3VtKHF1aW50aWxlX3N1bW1hcnkkQ29tcGFuaWVzKSAqIDEwMA0KICBjdW1fcGN0X2V2ZW50cyA8LSBjdW1zdW0ocXVpbnRpbGVfc3VtbWFyeSRFdmVudHMpIC8gc3VtKHF1aW50aWxlX3N1bW1hcnkkRXZlbnRzKSAqIDEwMA0KICANCiAgIyBQb2ludHMgZm9yIExvcmVueiBjdXJ2ZQ0KICBsb3JlbnpfeCA8LSBjKDAsIGN1bV9wY3RfY29tcGFuaWVzKQ0KICBsb3JlbnpfeSA8LSBjKDAsIGN1bV9wY3RfZXZlbnRzKQ0KICANCiAgIyBDcmVhdGUgTG9yZW56IGN1cnZlIHBsb3QNCiAgcGxvdChsb3JlbnpfeCwgbG9yZW56X3ksIHR5cGUgPSAiYiIsIHBjaCA9IDE2LCBjb2wgPSAiYmx1ZSIsDQogICAgICAgbWFpbiA9IHBhc3RlKCJFdmVudCBDb25jZW50cmF0aW9uIEN1cnZlIC0iLCBtb2RlbF9uYW1lLCAiTW9kZWwiKSwNCiAgICAgICB4bGFiID0gIkN1bXVsYXRpdmUgJSBvZiBDb21wYW5pZXMgKHNvcnRlZCBieSByaXNrIHNjb3JlKSIsDQogICAgICAgeWxhYiA9ICJDdW11bGF0aXZlICUgb2YgRXZlbnRzIiwNCiAgICAgICB4bGltID0gYygwLCAxMDApLCB5bGltID0gYygwLCAxMDApKQ0KICANCiAgIyBBZGQgcmVmZXJlbmNlIGxpbmUgZm9yIHJhbmRvbSBwcmVkaWN0aW9uDQogIGFibGluZSgwLCAxLCBsdHkgPSAyLCBjb2wgPSAiZGFya2dyYXkiKQ0KICANCiAgIyBBZGQgZ3JpZCBmb3IgcmVhZGFiaWxpdHkNCiAgZ3JpZCgpDQogIA0KICAjIEFkZCBxdWludGlsZSBsYWJlbHMNCiAgdGV4dChjdW1fcGN0X2NvbXBhbmllc1syOjZdLCBjdW1fcGN0X2V2ZW50c1syOjZdLCANCiAgICAgICBsYWJlbHMgPSBjKCJRMSIsICJRMiIsICJRMyIsICJRNCIsICJRNSIpLCANCiAgICAgICBwb3MgPSA0LCBjZXggPSAwLjgpDQogIA0KICAjIENhbGN1bGF0ZSBHaW5pIGNvZWZmaWNpZW50IChhcmVhIGJldHdlZW4gTG9yZW56IGN1cnZlIGFuZCBkaWFnb25hbCwgbm9ybWFsaXplZCkNCiAgIyBBcHByb3hpbWF0ZSB1c2luZyB0cmFwZXpvaWQgcnVsZQ0KICBnaW5pIDwtIDANCiAgZm9yKGkgaW4gMTo1KSB7DQogICAgZ2luaSA8LSBnaW5pICsgKGxvcmVuel94W2krMV0gLSBsb3JlbnpfeFtpXSkgKiAobG9yZW56X3lbaSsxXSArIGxvcmVuel95W2ldKQ0KICB9DQogIGdpbmkgPC0gMSAtIGdpbmkvMTAwMDANCiAgDQogICMgQWRkIEdpbmkgY29lZmZpY2llbnQgdG8gcGxvdA0KICBtdGV4dChwYXN0ZSgiR2luaSBDb2VmZmljaWVudDoiLCByb3VuZChnaW5pLCAzKSksIHNpZGUgPSAzLCBsaW5lID0gMC41LCBjZXggPSAwLjgpDQogIA0KICAjIEFkZCBwcmFjdGljYWwgaW50ZXJwcmV0YXRpb24NCiAgY2F0KCJcblBlcmZvcm1hbmNlIEludGVycHJldGF0aW9uOlxuIikNCiAgY2F0KCItIERpc2NyaW1pbmF0aW9uIFJhdGlvIChRNS9RMSk6Iiwgcm91bmQoZGlzY3JpbWluYXRpb25fcmF0aW8sIDEpLCAiw5dcbiIpDQogIA0KICBpZihkaXNjcmltaW5hdGlvbl9yYXRpbyA+PSAxMCkgew0KICAgIGNhdCgiICBFeGNlbGxlbnQgZGlzY3JpbWluYXRpb24gKD4xMMOXKVxuIikNCiAgfSBlbHNlIGlmKGRpc2NyaW1pbmF0aW9uX3JhdGlvID49IDUpIHsNCiAgICBjYXQoIiAgR29vZCBkaXNjcmltaW5hdGlvbiAoNS0xMMOXKVxuIikNCiAgfSBlbHNlIGlmKGRpc2NyaW1pbmF0aW9uX3JhdGlvID49IDMpIHsNCiAgICBjYXQoIiAgTW9kZXJhdGUgZGlzY3JpbWluYXRpb24gKDMtNcOXKVxuIikNCiAgfSBlbHNlIHsNCiAgICBjYXQoIiAgTGltaXRlZCBkaXNjcmltaW5hdGlvbiAoPDPDlylcbiIpDQogIH0NCiAgDQogIGNhdCgiLSBHaW5pIENvZWZmaWNpZW50OiIsIHJvdW5kKGdpbmksIDMpLCAiXG4iKQ0KICANCiAgaWYoZ2luaSA+PSAwLjcpIHsNCiAgICBjYXQoIiAgRXhjZWxsZW50IGNvbmNlbnRyYXRpb24gb2YgZXZlbnRzICjiiaUwLjcpXG4iKQ0KICB9IGVsc2UgaWYoZ2luaSA+PSAwLjUpIHsNCiAgICBjYXQoIiAgR29vZCBjb25jZW50cmF0aW9uIG9mIGV2ZW50cyAoMC41LTAuNylcbiIpDQogIH0gZWxzZSBpZihnaW5pID49IDAuMykgew0KICAgIGNhdCgiICBNb2RlcmF0ZSBjb25jZW50cmF0aW9uIG9mIGV2ZW50cyAoMC4zLTAuNSlcbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCIgIExpbWl0ZWQgY29uY2VudHJhdGlvbiBvZiBldmVudHMgKDwwLjMpXG4iKQ0KICB9DQogIA0KICAjIFRvcCBxdWludGlsZSBjYXB0dXJlIHJhdGUNCiAgdG9wX3F1aW50aWxlX2NhcHR1cmUgPC0gcXVpbnRpbGVfc3VtbWFyeSRFdmVudHNbNV0gLyBzdW0ocXVpbnRpbGVfc3VtbWFyeSRFdmVudHMpICogMTAwDQogIGNhdCgiLSBUb3AgcXVpbnRpbGUgY2FwdHVyZSByYXRlOiIsIHJvdW5kKHRvcF9xdWludGlsZV9jYXB0dXJlLCAxKSwgDQogICAgICAiJSBvZiBhbGwgZXZlbnRzIGNhcHR1cmVkIGluIGhpZ2hlc3QgcmlzayBxdWludGlsZVxuIikNCiAgDQogICMgQW5hbHl6ZSBtYWNyb2Vjb25vbWljIGluZmx1ZW5jZSBvbiByaXNrIHN0cmF0aWZpY2F0aW9uDQogIGNhdCgiXG5NYWNyb2Vjb25vbWljIENvbnRleHQgb2YgUmlzayBTdHJhdGlmaWNhdGlvbjpcbiIpDQogIA0KICAjIENoZWNrIGlmIG1hY3JvIHZhcmlhYmxlcyBhcmUgcHJlc2VudA0KICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAidW5lbXBsb3llbWVudCIsICJnZHBfZGVmbGF0b3IiKQ0KICBhdmFpbGFibGVfbWFjcm8gPC0gaW50ZXJzZWN0KG1hY3JvX3ZhcnMsIGNvbG5hbWVzKGZpbmFsX29icykpDQogIA0KICBpZihsZW5ndGgoYXZhaWxhYmxlX21hY3JvKSA+IDApIHsNCiAgICAjIENhbGN1bGF0ZSBhdmVyYWdlIG1hY3JvIHZhbHVlcyBieSBxdWludGlsZQ0KICAgIG1hY3JvX2J5X3F1aW50aWxlIDwtIGFnZ3JlZ2F0ZShmaW5hbF9vYnNbLCBhdmFpbGFibGVfbWFjcm9dLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGxpc3QoUXVpbnRpbGUgPSBmaW5hbF9vYnMkcmlza19xdWludGlsZSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IG1lYW4sIG5hLnJtID0gVFJVRSkNCiAgICANCiAgICBjYXQoIlxuQXZlcmFnZSBNYWNyb2Vjb25vbWljIENvbmRpdGlvbnMgYnkgUmlzayBRdWludGlsZTpcbiIpDQogICAgcHJpbnQobWFjcm9fYnlfcXVpbnRpbGUpDQogICAgDQogICAgIyBDcmVhdGUgcGFyYWxsZWwgcGxvdHMgdG8gdmlzdWFsaXplIHJlbGF0aW9uc2hpcA0KICAgICMgU2V0IHVwIHBsb3R0aW5nIGFyZWENCiAgICBwYXIobWFyID0gYyg1LCA1LCA0LCA1KSArIDAuMSkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBtZWFuIGFuZCBTRCBmb3Igc3RhbmRhcmRpemluZw0KICAgIG1lYW5zIDwtIGNvbE1lYW5zKGZpbmFsX29ic1ssIGF2YWlsYWJsZV9tYWNyb10sIG5hLnJtID0gVFJVRSkNCiAgICBzZHMgPC0gYXBwbHkoZmluYWxfb2JzWywgYXZhaWxhYmxlX21hY3JvXSwgMiwgc2QsIG5hLnJtID0gVFJVRSkNCiAgICANCiAgICAjIFN0YW5kYXJkaXplIHZhbHVlcyBmb3IgcGxvdHRpbmcNCiAgICBzdGRfdmFsdWVzIDwtIG1hdHJpeChOQSwgbnJvdyA9IDUsIG5jb2wgPSBsZW5ndGgoYXZhaWxhYmxlX21hY3JvKSkNCiAgICBmb3IoaSBpbiAxOmxlbmd0aChhdmFpbGFibGVfbWFjcm8pKSB7DQogICAgICBzdGRfdmFsdWVzWywgaV0gPC0gKG1hY3JvX2J5X3F1aW50aWxlWywgaSsxXSAtIG1lYW5zW2ldKSAvIHNkc1tpXQ0KICAgIH0NCiAgICANCiAgICAjIFNldCBwbG90IGxpbWl0cw0KICAgIHlfbGltIDwtIGMobWluKC0yLCBtaW4oc3RkX3ZhbHVlcywgbmEucm0gPSBUUlVFKSksIA0KICAgICAgICAgICAgICAgbWF4KDIsIG1heChzdGRfdmFsdWVzLCBuYS5ybSA9IFRSVUUpKSkNCiAgICANCiAgICAjIENyZWF0ZSBwbG90DQogICAgcGxvdCgxOjUsIHN0ZF92YWx1ZXNbLCAxXSwgdHlwZSA9ICJsIiwgbHdkID0gMiwgY29sID0gImJsdWUiLA0KICAgICAgICAgbWFpbiA9ICJNYWNyb2Vjb25vbWljIFByb2ZpbGUgYnkgUmlzayBRdWludGlsZSIsDQogICAgICAgICB4bGFiID0gIlJpc2sgUXVpbnRpbGUgKExvdyB0byBIaWdoKSIsDQogICAgICAgICB5bGFiID0gIlN0YW5kYXJkaXplZCBWYWx1ZSAoWi1zY29yZSkiLA0KICAgICAgICAgeGxpbSA9IGMoMSwgNSksIHlsaW0gPSB5X2xpbSwNCiAgICAgICAgIHhheHQgPSAibiIpDQogICAgDQogICAgIyBBZGQgYXhpcw0KICAgIGF4aXMoMSwgYXQgPSAxOjUsIGxhYmVscyA9IGMoIlExXG4oTG93ZXN0KSIsICJRMiIsICJRMyIsICJRNCIsICJRNVxuKEhpZ2hlc3QpIikpDQogICAgDQogICAgIyBBZGQgbGluZXMgZm9yIG90aGVyIG1hY3JvIHZhcmlhYmxlcw0KICAgIGlmKGxlbmd0aChhdmFpbGFibGVfbWFjcm8pID4gMSkgew0KICAgICAgZm9yKGkgaW4gMjpsZW5ndGgoYXZhaWxhYmxlX21hY3JvKSkgew0KICAgICAgICBsaW5lcygxOjUsIHN0ZF92YWx1ZXNbLCBpXSwgbHdkID0gMiwgDQogICAgICAgICAgICAgIGNvbCA9IHJhaW5ib3cobGVuZ3RoKGF2YWlsYWJsZV9tYWNybykpW2ldKQ0KICAgICAgfQ0KICAgIH0NCiAgICANCiAgICAjIEFkZCByZWZlcmVuY2UgbGluZSBhdCAwDQogICAgYWJsaW5lKGggPSAwLCBsdHkgPSAyLCBjb2wgPSAiZGFya2dyYXkiKQ0KICAgIA0KICAgICMgQWRkIGxlZ2VuZA0KICAgIG1hY3JvX2xhYmVscyA8LSBjKCJHRFAgR3Jvd3RoIiwgIlVuZW1wbG95bWVudCIsICJJbmZsYXRpb24iKQ0KICAgIGxlZ2VuZF9sYWJlbHMgPC0gbWFjcm9fbGFiZWxzW21hdGNoKGF2YWlsYWJsZV9tYWNybywgbWFjcm9fdmFycyldDQogICAgDQogICAgbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGxlZ2VuZF9sYWJlbHMsIA0KICAgICAgICAgICBjb2wgPSByYWluYm93KGxlbmd0aChhdmFpbGFibGVfbWFjcm8pKSwgDQogICAgICAgICAgIGx3ZCA9IDIsIGJ0eSA9ICJuIikNCiAgICANCiAgICAjIFJlc2V0IHBsb3R0aW5nIHBhcmFtZXRlcnMNCiAgICBwYXIobWFyID0gYyg1LCA0LCA0LCAyKSArIDAuMSkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiByaXNrIHNjb3JlcyBhbmQgbWFjcm8gdmFyaWFibGVzDQogICAgY29yX3Jpc2tfbWFjcm8gPC0gY29yKGZpbmFsX29icyRyaXNrX3Njb3JlLCBmaW5hbF9vYnNbLCBhdmFpbGFibGVfbWFjcm9dLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQ0KICAgIA0KICAgIGNhdCgiXG5Db3JyZWxhdGlvbiBiZXR3ZWVuIHJpc2sgc2NvcmVzIGFuZCBtYWNyb2Vjb25vbWljIHZhcmlhYmxlczpcbiIpDQogICAgZm9yKGkgaW4gMTpsZW5ndGgoYXZhaWxhYmxlX21hY3JvKSkgew0KICAgICAgdmFyIDwtIGF2YWlsYWJsZV9tYWNyb1tpXQ0KICAgICAgdmFyX2xhYmVsIDwtIGlmZWxzZSh2YXIgPT0gImdkcF9ncm93dGgiLCAiR0RQIEdyb3d0aCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHZhciA9PSAidW5lbXBsb3llbWVudCIsICJVbmVtcGxveW1lbnQiLCAiSW5mbGF0aW9uIikpDQogICAgICANCiAgICAgIGNvcl92YWwgPC0gY29yX3Jpc2tfbWFjcm9bMSwgaV0NCiAgICAgIA0KICAgICAgY2F0KCItICIsIHZhcl9sYWJlbCwgIjogciA9ICIsIHJvdW5kKGNvcl92YWwsIDMpLCAiICgiLCANCiAgICAgICAgICBpZmVsc2UoY29yX3ZhbCA+IDAsICJwb3NpdGl2ZSIsICJuZWdhdGl2ZSIpLCAiIHJlbGF0aW9uc2hpcClcbiIsIHNlcD0iIikNCiAgICAgIA0KICAgICAgIyBBZGQgaW50ZXJwcmV0YXRpb24NCiAgICAgIGlmKGFicyhjb3JfdmFsKSA+IDAuMSkgew0KICAgICAgICBpZih2YXIgPT0gImdkcF9ncm93dGgiKSB7DQogICAgICAgICAgaWYoY29yX3ZhbCA+IDApIHsNCiAgICAgICAgICAgIGNhdCgiICBIaWdoZXIgZWNvbm9taWMgZ3Jvd3RoIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIiLCBtb2RlbF9uYW1lLCAicmlza1xuIikNCiAgICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgY2F0KCIgIExvd2VyIGVjb25vbWljIGdyb3d0aCBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIiwgbW9kZWxfbmFtZSwgInJpc2tcbiIpDQogICAgICAgICAgfQ0KICAgICAgICB9IGVsc2UgaWYodmFyID09ICJ1bmVtcGxveWVtZW50Iikgew0KICAgICAgICAgIGlmKGNvcl92YWwgPiAwKSB7DQogICAgICAgICAgICBjYXQoIiAgSGlnaGVyIHVuZW1wbG95bWVudCBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIiwgbW9kZWxfbmFtZSwgInJpc2tcbiIpDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIGNhdCgiICBMb3dlciB1bmVtcGxveW1lbnQgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciIsIG1vZGVsX25hbWUsICJyaXNrXG4iKQ0KICAgICAgICAgIH0NCiAgICAgICAgfSBlbHNlIGlmKHZhciA9PSAiZ2RwX2RlZmxhdG9yIikgew0KICAgICAgICAgIGlmKGNvcl92YWwgPiAwKSB7DQogICAgICAgICAgICBjYXQoIiAgSGlnaGVyIGluZmxhdGlvbiBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIiwgbW9kZWxfbmFtZSwgInJpc2tcbiIpDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIGNhdCgiICBMb3dlciBpbmZsYXRpb24gYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciIsIG1vZGVsX25hbWUsICJyaXNrXG4iKQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgY2F0KCIgIE1pbmltYWwgcmVsYXRpb25zaGlwIHdpdGgiLCBtb2RlbF9uYW1lLCAicmlza1xuIikNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBDaGVjayBmb3IgcGF0dGVybnMgaW4gZXZlbnQgcmF0ZXMgYWNyb3NzIG1hY3JvIGNvbmRpdGlvbnMNCiAgICAjIENyZWF0ZSBtYWNybyBjb25kaXRpb24gY2F0ZWdvcmllcyBiYXNlZCBvbiBHRFAgZ3Jvd3RoIGFuZCB1bmVtcGxveW1lbnQNCiAgICBpZihhbGwoYygiZ2RwX2dyb3d0aCIsICJ1bmVtcGxveWVtZW50IikgJWluJSBhdmFpbGFibGVfbWFjcm8pKSB7DQogICAgICAjIERlZmluZSBlY29ub21pYyBjb25kaXRpb25zDQogICAgICBtZWRpYW5fZ2RwIDwtIG1lZGlhbihmaW5hbF9vYnMkZ2RwX2dyb3d0aCwgbmEucm0gPSBUUlVFKQ0KICAgICAgbWVkaWFuX3VuZW1wIDwtIG1lZGlhbihmaW5hbF9vYnMkdW5lbXBsb3llbWVudCwgbmEucm0gPSBUUlVFKQ0KICAgICAgDQogICAgICBmaW5hbF9vYnMkZWNvbl9jb25kaXRpb24gPC0gIk5vcm1hbCINCiAgICAgIGZpbmFsX29icyRlY29uX2NvbmRpdGlvbltmaW5hbF9vYnMkZ2RwX2dyb3d0aCA+IG1lZGlhbl9nZHAgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmFsX29icyR1bmVtcGxveWVtZW50IDwgbWVkaWFuX3VuZW1wXSA8LSAiU3Ryb25nIg0KICAgICAgZmluYWxfb2JzJGVjb25fY29uZGl0aW9uW2ZpbmFsX29icyRnZHBfZ3Jvd3RoIDwgbWVkaWFuX2dkcCAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluYWxfb2JzJHVuZW1wbG95ZW1lbnQgPiBtZWRpYW5fdW5lbXBdIDwtICJXZWFrIg0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBldmVudCByYXRlcyBieSBxdWludGlsZSBhbmQgZWNvbm9taWMgY29uZGl0aW9uDQogICAgICAjIFVzZSB0aGUgYXBwcm9wcmlhdGUgZXZlbnQgY29sdW1uIGJhc2VkIG9uIGV2ZW50X3R5cGVfbmFtZQ0KICAgICAgaWYoZXZlbnRfdHlwZV9uYW1lID09ICJiYW5rcnVwdGN5Iikgew0KICAgICAgICBlY29uX3F1aW50aWxlX3JhdGVzIDwtIGFnZ3JlZ2F0ZShiYW5rcnVwdGN5IH4gcmlza19xdWludGlsZSArIGVjb25fY29uZGl0aW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGZpbmFsX29icywgRlVOID0gbWVhbikNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIGVjb25fcXVpbnRpbGVfcmF0ZXMgPC0gYWdncmVnYXRlKGFjcXVpc2l0aW9uIH4gcmlza19xdWludGlsZSArIGVjb25fY29uZGl0aW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGZpbmFsX29icywgRlVOID0gbWVhbikNCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBSZXNoYXBlIHRvIHdpZGUgZm9ybWF0DQogICAgICBlY29uX3F1aW50aWxlX3dpZGUgPC0gcmVzaGFwZShlY29uX3F1aW50aWxlX3JhdGVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZHZhciA9ICJyaXNrX3F1aW50aWxlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXZhciA9ICJlY29uX2NvbmRpdGlvbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJ3aWRlIikNCiAgICAgIA0KICAgICAgIyBGaXggY29sdW1uIG5hbWVzDQogICAgICBjb2xuYW1lcyhlY29uX3F1aW50aWxlX3dpZGUpIDwtIGdzdWIocGFzdGUwKGV2ZW50X3R5cGVfbmFtZSwgIi4iKSwgIiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xuYW1lcyhlY29uX3F1aW50aWxlX3dpZGUpKQ0KICAgICAgDQogICAgICAjIE11bHRpcGx5IGJ5IDEwMCB0byBnZXQgcGVyY2VudGFnZXMNCiAgICAgIGZvcihjb2wgaW4gMjpuY29sKGVjb25fcXVpbnRpbGVfd2lkZSkpIHsNCiAgICAgICAgZWNvbl9xdWludGlsZV93aWRlWywgY29sXSA8LSBlY29uX3F1aW50aWxlX3dpZGVbLCBjb2xdICogMTAwDQogICAgICB9DQogICAgICANCiAgICAgIGNhdCgiXG5FdmVudCBSYXRlcyBieSBSaXNrIFF1aW50aWxlIGFuZCBFY29ub21pYyBDb25kaXRpb246XG4iKQ0KICAgICAgcHJpbnQoZWNvbl9xdWludGlsZV93aWRlKQ0KICAgICAgDQogICAgICAjIENyZWF0ZSBpbnRlcmFjdGlvbiBwbG90DQogICAgICAjIFVzZSByZXNoYXBlMiBwYWNrYWdlIGZvciBtZWx0IGZ1bmN0aW9uIGlmIGF2YWlsYWJsZQ0KICAgICAgaWYocmVxdWlyZU5hbWVzcGFjZSgicmVzaGFwZTIiLCBxdWlldGx5ID0gVFJVRSkpIHsNCiAgICAgICAgIyBDcmVhdGUgZGF0YSBpbiBsb25nIGZvcm1hdCBmb3IgcGxvdHRpbmcNCiAgICAgICAgcGxvdF9kYXRhIDwtIHJlc2hhcGUyOjptZWx0KGVjb25fcXVpbnRpbGVfd2lkZSwgaWQudmFycyA9ICJyaXNrX3F1aW50aWxlIikNCiAgICAgICAgbmFtZXMocGxvdF9kYXRhKSA8LSBjKCJRdWludGlsZSIsICJDb25kaXRpb24iLCAiUmF0ZSIpDQogICAgICB9IGVsc2Ugew0KICAgICAgICAjIEZhbGxiYWNrIGlmIHJlc2hhcGUyIGlzIG5vdCBhdmFpbGFibGUNCiAgICAgICAgIyBDcmVhdGUgYSBtYW51YWwgZXF1aXZhbGVudCBvZiBtZWx0IGZvciB0aGlzIHNwZWNpZmljIGNhc2UNCiAgICAgICAgcGxvdF9kYXRhIDwtIGRhdGEuZnJhbWUoDQogICAgICAgICAgUXVpbnRpbGUgPSByZXAoZWNvbl9xdWludGlsZV93aWRlJHJpc2tfcXVpbnRpbGUsIGVhY2ggPSBuY29sKGVjb25fcXVpbnRpbGVfd2lkZSkgLSAxKSwNCiAgICAgICAgICBDb25kaXRpb24gPSByZXAoY29sbmFtZXMoZWNvbl9xdWludGlsZV93aWRlKVstMV0sIHRpbWVzID0gbnJvdyhlY29uX3F1aW50aWxlX3dpZGUpKSwNCiAgICAgICAgICBSYXRlID0gYXMudmVjdG9yKHVubGlzdChlY29uX3F1aW50aWxlX3dpZGVbLCAtMV0pKSwNCiAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgICAgICAgKQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIENyZWF0ZSBpbnRlcmFjdGlvbiBwbG90DQogICAgICBpbnRlcmFjdGlvbi5wbG90KHguZmFjdG9yID0gcGxvdF9kYXRhJFF1aW50aWxlLCANCiAgICAgICAgICAgICAgICAgICAgICB0cmFjZS5mYWN0b3IgPSBwbG90X2RhdGEkQ29uZGl0aW9uLCANCiAgICAgICAgICAgICAgICAgICAgICByZXNwb25zZSA9IHBsb3RfZGF0YSRSYXRlLA0KICAgICAgICAgICAgICAgICAgICAgIGZ1biA9IG1lYW4sDQogICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJiIiwNCiAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjKCJncmVlbjMiLCAiYmx1ZSIsICJyZWQiKSwNCiAgICAgICAgICAgICAgICAgICAgICBsdHkgPSAxLA0KICAgICAgICAgICAgICAgICAgICAgIHBjaCA9IGMoMTUsIDE2LCAxNyksDQogICAgICAgICAgICAgICAgICAgICAgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiUmlzayBRdWludGlsZSIsDQogICAgICAgICAgICAgICAgICAgICAgeWxhYiA9IHBhc3RlKG1vZGVsX25hbWUsICJFdmVudCBSYXRlICglKSIpLA0KICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSAiUmlzayBRdWludGlsZSBQZXJmb3JtYW5jZSBieSBFY29ub21pYyBDb25kaXRpb24iKQ0KICAgICAgDQogICAgICAjIENhbGN1bGF0ZSBkaXNjcmltaW5hdGlvbiByYXRpb3MgYnkgZWNvbm9taWMgY29uZGl0aW9uDQogICAgICBjb25kaXRpb25zIDwtIHVuaXF1ZShwbG90X2RhdGEkQ29uZGl0aW9uKQ0KICAgICAgZGlzY3JpbV9ieV9jb25kaXRpb24gPC0gbnVtZXJpYyhsZW5ndGgoY29uZGl0aW9ucykpDQogICAgICBuYW1lcyhkaXNjcmltX2J5X2NvbmRpdGlvbikgPC0gY29uZGl0aW9ucw0KICAgICAgDQogICAgICBmb3IoY29uZCBpbiBjb25kaXRpb25zKSB7DQogICAgICAgIHF1aW50aWxlX3JhdGVzIDwtIHRhcHBseShwbG90X2RhdGEkUmF0ZVtwbG90X2RhdGEkQ29uZGl0aW9uID09IGNvbmRdLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90X2RhdGEkUXVpbnRpbGVbcGxvdF9kYXRhJENvbmRpdGlvbiA9PSBjb25kXSwgbWVhbikNCiAgICAgICAgaWYobGVuZ3RoKHF1aW50aWxlX3JhdGVzKSA9PSA1ICYmICFpcy5uYShxdWludGlsZV9yYXRlc1sxXSkgJiYgDQogICAgICAgICAgICFpcy5uYShxdWludGlsZV9yYXRlc1s1XSkgJiYgcXVpbnRpbGVfcmF0ZXNbMV0gPiAwKSB7DQogICAgICAgICAgZGlzY3JpbV9ieV9jb25kaXRpb25bY29uZF0gPC0gcXVpbnRpbGVfcmF0ZXNbNV0gLyBxdWludGlsZV9yYXRlc1sxXQ0KICAgICAgICB9DQogICAgICB9DQogICAgICANCiAgICAgICMgQWRkIHRvIHBsb3QgYXMgYW5ub3RhdGlvbg0KICAgICAgcmF0aW9fdGV4dCA8LSBwYXN0ZShuYW1lcyhkaXNjcmltX2J5X2NvbmRpdGlvbiksICI6ICIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQoZGlzY3JpbV9ieV9jb25kaXRpb24sIDEpLCAiw5ciLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIiB8ICIpDQogICAgICBtdGV4dChwYXN0ZSgiRGlzY3JpbWluYXRpb24gUmF0aW9zIC0iLCByYXRpb190ZXh0KSwgDQogICAgICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC43KQ0KICAgICAgDQogICAgICAjIERpc2N1c3MgaW1wbGljYXRpb25zDQogICAgICBjYXQoIlxuTWFjcm9lY29ub21pYyBJbXBhY3Qgb24gUmlzayBTdHJhdGlmaWNhdGlvbjpcbiIpDQogICAgICANCiAgICAgICMgRmluZCBjb25kaXRpb24gd2l0aCBoaWdoZXN0IGRpc2NyaW1pbmF0aW9uIHJhdGlvDQogICAgICBiZXN0X2NvbmRpdGlvbiA8LSBuYW1lcyh3aGljaC5tYXgoZGlzY3JpbV9ieV9jb25kaXRpb24pKQ0KICAgICAgbWF4X2Rpc2NyaW0gPC0gbWF4KGRpc2NyaW1fYnlfY29uZGl0aW9uLCBuYS5ybSA9IFRSVUUpDQogICAgICANCiAgICAgIGNhdCgiLSBCZXN0IG1vZGVsIGRpc2NyaW1pbmF0aW9uIGluIiwgYmVzdF9jb25kaXRpb24sICJlY29ub21pYyBjb25kaXRpb25zIChyYXRpbyA9IiwgDQogICAgICAgICAgcm91bmQobWF4X2Rpc2NyaW0sIDEpLCAiw5cpXG4iKQ0KICAgICAgDQogICAgICAjIENoZWNrIHBhdHRlcm4gYWNyb3NzIGNvbmRpdGlvbnMNCiAgICAgIHN0cm9uZ19pZHggPC0gd2hpY2gobmFtZXMoZGlzY3JpbV9ieV9jb25kaXRpb24pID09ICJTdHJvbmciKQ0KICAgICAgd2Vha19pZHggPC0gd2hpY2gobmFtZXMoZGlzY3JpbV9ieV9jb25kaXRpb24pID09ICJXZWFrIikNCiAgICAgIA0KICAgICAgaWYobGVuZ3RoKHN0cm9uZ19pZHgpID09IDAgfHwgbGVuZ3RoKHdlYWtfaWR4KSA9PSAwIHx8IA0KICAgICAgICAgaXMubmEoZGlzY3JpbV9ieV9jb25kaXRpb25bc3Ryb25nX2lkeF0pIHx8IGlzLm5hKGRpc2NyaW1fYnlfY29uZGl0aW9uW3dlYWtfaWR4XSkpIHsNCiAgICAgICAgY2F0KCItIEluc3VmZmljaWVudCBkYXRhIHRvIGNvbXBhcmUgYWNyb3NzIGFsbCBlY29ub21pYyBjb25kaXRpb25zXG4iKQ0KICAgICAgfSBlbHNlIGlmKGRpc2NyaW1fYnlfY29uZGl0aW9uW3dlYWtfaWR4XSA+IGRpc2NyaW1fYnlfY29uZGl0aW9uW3N0cm9uZ19pZHhdKSB7DQogICAgICAgIGNhdCgiLSBNb2RlbCBkaXNjcmltaW5hdGlvbiBpbXByb3ZlcyBpbiB3ZWFrIGVjb25vbWljIGNvbmRpdGlvbnNcbiIpDQogICAgICAgIGNhdCgiICBUaGlzIHN1Z2dlc3RzIGVjb25vbWljIGRvd250dXJucyBtYWtlIiwgbW9kZWxfbmFtZSwgImV2ZW50cyBtb3JlIHByZWRpY3RhYmxlXG4iKQ0KICAgICAgfSBlbHNlIGlmKGRpc2NyaW1fYnlfY29uZGl0aW9uW3N0cm9uZ19pZHhdID4gZGlzY3JpbV9ieV9jb25kaXRpb25bd2Vha19pZHhdKSB7DQogICAgICAgIGNhdCgiLSBNb2RlbCBkaXNjcmltaW5hdGlvbiBpbXByb3ZlcyBpbiBzdHJvbmcgZWNvbm9taWMgY29uZGl0aW9uc1xuIikNCiAgICAgICAgY2F0KCIgIFRoaXMgc3VnZ2VzdHMgZWNvbm9taWMgZXhwYW5zaW9ucyBtYWtlIiwgbW9kZWxfbmFtZSwgImV2ZW50cyBtb3JlIHByZWRpY3RhYmxlXG4iKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgY2F0KCItIFNpbWlsYXIgZGlzY3JpbWluYXRpb24gYWNyb3NzIGVjb25vbWljIGNvbmRpdGlvbnNcbiIpDQogICAgICAgIGNhdCgiICBUaGlzIHN1Z2dlc3RzIGVjb25vbWljIGN5Y2xlcyBoYXZlIGxpbWl0ZWQgaW1wYWN0IG9uIHByZWRpY3RhYmlsaXR5XG4iKQ0KICAgICAgfQ0KICAgIH0NCiAgfSBlbHNlIHsNCiAgICBjYXQoIk1hY3JvZWNvbm9taWMgdmFyaWFibGVzIG5vdCBhdmFpbGFibGUgaW4gdGhlIGRhdGFzZXQuXG4iKQ0KICB9DQogIA0KICAjIFJldHVybiByaXNrIHN0cmF0aWZpY2F0aW9uIHJlc3VsdHMNCiAgcmV0dXJuKGxpc3QoDQogICAgcXVpbnRpbGVfc3VtbWFyeSA9IHF1aW50aWxlX3N1bW1hcnksDQogICAgZGlzY3JpbWluYXRpb25fcmF0aW8gPSBkaXNjcmltaW5hdGlvbl9yYXRpbywNCiAgICBnaW5pID0gZ2luaSwNCiAgICB0b3BfcXVpbnRpbGVfY2FwdHVyZSA9IHRvcF9xdWludGlsZV9jYXB0dXJlDQogICkpDQp9DQoNCiMgQ3JlYXRlIHJpc2sgc3RyYXRpZmljYXRpb24gYW5hbHlzZXMgZm9yIGJvdGggbW9kZWxzDQpiYW5rcnVwdGN5X3Jpc2tfc3RyYXQgPC0gY3JlYXRlX3Jpc2tfc3RyYXRpZmljYXRpb24oZmluYWxfYmFua3J1cHRjeV9tb2RlbCwgIkJhbmtydXB0Y3kiLCAiYmFua3J1cHRjeSIpDQphY3F1aXNpdGlvbl9yaXNrX3N0cmF0IDwtIGNyZWF0ZV9yaXNrX3N0cmF0aWZpY2F0aW9uKGZpbmFsX2FjcXVpc2l0aW9uX21vZGVsLCAiQWNxdWlzaXRpb24iLCAiYWNxdWlzaXRpb24iKQ0KDQojIENvbXBhcmUgcmlzayBzdHJhdGlmaWNhdGlvbiBiZXR3ZWVuIG1vZGVscw0KaWYoIWlzLm51bGwoYmFua3J1cHRjeV9yaXNrX3N0cmF0KSAmJiAhaXMubnVsbChhY3F1aXNpdGlvbl9yaXNrX3N0cmF0KSkgew0KICBjYXQoIlxuQ09NUEFSSU5HIFJJU0sgU1RSQVRJRklDQVRJT04gQkVUV0VFTiBNT0RFTFM6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIA0KICAjIENyZWF0ZSBjb21wYXJpc29uIHRhYmxlDQogIGNvbXBhcmlzb24gPC0gZGF0YS5mcmFtZSgNCiAgICBNZXRyaWMgPSBjKCJEaXNjcmltaW5hdGlvbiBSYXRpbyAoUTUvUTEpIiwgDQogICAgICAgICAgICAgICAiR2luaSBDb2VmZmljaWVudCIsIA0KICAgICAgICAgICAgICAgIlRvcCBRdWludGlsZSBDYXB0dXJlIFJhdGUiKSwNCiAgICBCYW5rcnVwdGN5ID0gYyhiYW5rcnVwdGN5X3Jpc2tfc3RyYXQkZGlzY3JpbWluYXRpb25fcmF0aW8sDQogICAgICAgICAgICAgICAgICBiYW5rcnVwdGN5X3Jpc2tfc3RyYXQkZ2luaSwNCiAgICAgICAgICAgICAgICAgIGJhbmtydXB0Y3lfcmlza19zdHJhdCR0b3BfcXVpbnRpbGVfY2FwdHVyZSksDQogICAgQWNxdWlzaXRpb24gPSBjKGFjcXVpc2l0aW9uX3Jpc2tfc3RyYXQkZGlzY3JpbWluYXRpb25fcmF0aW8sDQogICAgICAgICAgICAgICAgICAgYWNxdWlzaXRpb25fcmlza19zdHJhdCRnaW5pLA0KICAgICAgICAgICAgICAgICAgIGFjcXVpc2l0aW9uX3Jpc2tfc3RyYXQkdG9wX3F1aW50aWxlX2NhcHR1cmUpLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIA0KICBjb21wYXJpc29uJERpZmZlcmVuY2UgPC0gY29tcGFyaXNvbiRCYW5rcnVwdGN5IC0gY29tcGFyaXNvbiRBY3F1aXNpdGlvbg0KICBjb21wYXJpc29uJFBjdF9EaWZmZXJlbmNlIDwtIGNvbXBhcmlzb24kRGlmZmVyZW5jZSAvIGNvbXBhcmlzb24kQWNxdWlzaXRpb24gKiAxMDANCiAgDQogICMgRm9ybWF0IGZvciBkaXNwbGF5DQogIGZvcm1hdHRlZF9jb21wYXJpc29uIDwtIGNvbXBhcmlzb24NCiAgZm9ybWF0dGVkX2NvbXBhcmlzb24kQmFua3J1cHRjeSA8LSByb3VuZChmb3JtYXR0ZWRfY29tcGFyaXNvbiRCYW5rcnVwdGN5LCAzKQ0KICBmb3JtYXR0ZWRfY29tcGFyaXNvbiRBY3F1aXNpdGlvbiA8LSByb3VuZChmb3JtYXR0ZWRfY29tcGFyaXNvbiRBY3F1aXNpdGlvbiwgMykNCiAgZm9ybWF0dGVkX2NvbXBhcmlzb24kRGlmZmVyZW5jZSA8LSByb3VuZChmb3JtYXR0ZWRfY29tcGFyaXNvbiREaWZmZXJlbmNlLCAzKQ0KICBmb3JtYXR0ZWRfY29tcGFyaXNvbiRQY3RfRGlmZmVyZW5jZSA8LSByb3VuZChmb3JtYXR0ZWRfY29tcGFyaXNvbiRQY3RfRGlmZmVyZW5jZSwgMSkNCiAgDQogIHByaW50KGZvcm1hdHRlZF9jb21wYXJpc29uKQ0KICANCiAgIyBJbnRlcnByZXQgZGlmZmVyZW5jZXMNCiAgY2F0KCJcbkludGVycHJldGF0aW9uIG9mIE1vZGVsIERpZmZlcmVuY2VzOlxuIikNCiAgDQogICMgQ29tcGFyZSBkaXNjcmltaW5hdGlvbiByYXRpb3MNCiAgaWYoY29tcGFyaXNvbiRCYW5rcnVwdGN5WzFdID4gY29tcGFyaXNvbiRBY3F1aXNpdGlvblsxXSAqIDEuMjUpIHsNCiAgICBjYXQoIi0gVGhlIGJhbmtydXB0Y3kgbW9kZWwgaGFzIHNpZ25pZmljYW50bHkgYmV0dGVyIGRpc2NyaW1pbmF0aW9uIGJldHdlZW5cbiIpDQogICAgY2F0KCIgIGhpZ2ggYW5kIGxvdyByaXNrIGNvbXBhbmllcyAoIiwgcm91bmQoY29tcGFyaXNvbiRQY3RfRGlmZmVyZW5jZVsxXSwgMSksICIlIGhpZ2hlcilcbiIsIHNlcD0iIikNCiAgfSBlbHNlIGlmKGNvbXBhcmlzb24kQWNxdWlzaXRpb25bMV0gPiBjb21wYXJpc29uJEJhbmtydXB0Y3lbMV0gKiAxLjI1KSB7DQogICAgY2F0KCItIFRoZSBhY3F1aXNpdGlvbiBtb2RlbCBoYXMgc2lnbmlmaWNhbnRseSBiZXR0ZXIgZGlzY3JpbWluYXRpb24gYmV0d2VlblxuIikNCiAgICBjYXQoIiAgaGlnaCBhbmQgbG93IHJpc2sgY29tcGFuaWVzICgiLCByb3VuZCgtY29tcGFyaXNvbiRQY3RfRGlmZmVyZW5jZVsxXSwgMSksICIlIGhpZ2hlcilcbiIsIHNlcD0iIikNCiAgfSBlbHNlIHsNCiAgICBjYXQoIi0gQm90aCBtb2RlbHMgaGF2ZSBzaW1pbGFyIGRpc2NyaW1pbmF0aW9uIGJldHdlZW4gaGlnaCBhbmQgbG93IHJpc2sgY29tcGFuaWVzXG4iKQ0KICB9DQogIA0KICAjIENvbXBhcmUgR2luaSBjb2VmZmljaWVudHMNCiAgaWYoY29tcGFyaXNvbiRCYW5rcnVwdGN5WzJdID4gY29tcGFyaXNvbiRBY3F1aXNpdGlvblsyXSAqIDEuMTUpIHsNCiAgICBjYXQoIi0gVGhlIGJhbmtydXB0Y3kgbW9kZWwgaGFzIGJldHRlciBldmVudCBjb25jZW50cmF0aW9uIChoaWdoZXIgR2luaSBjb2VmZmljaWVudClcbiIpDQogIH0gZWxzZSBpZihjb21wYXJpc29uJEFjcXVpc2l0aW9uWzJdID4gY29tcGFyaXNvbiRCYW5rcnVwdGN5WzJdICogMS4xNSkgew0KICAgIGNhdCgiLSBUaGUgYWNxdWlzaXRpb24gbW9kZWwgaGFzIGJldHRlciBldmVudCBjb25jZW50cmF0aW9uIChoaWdoZXIgR2luaSBjb2VmZmljaWVudClcbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCItIEJvdGggbW9kZWxzIGhhdmUgc2ltaWxhciBldmVudCBjb25jZW50cmF0aW9uIHBhdHRlcm5zXG4iKQ0KICB9DQogIA0KICAjIENvbXBhcmUgY2FwdHVyZSByYXRlcw0KICBpZihjb21wYXJpc29uJEJhbmtydXB0Y3lbM10gPiBjb21wYXJpc29uJEFjcXVpc2l0aW9uWzNdICogMS4xNSkgew0KICAgIGNhdCgiLSBUaGUgYmFua3J1cHRjeSBtb2RlbCBjYXB0dXJlcyBtb3JlIGV2ZW50cyBpbiBpdHMgaGlnaGVzdCByaXNrIHF1aW50aWxlXG4iKQ0KICB9IGVsc2UgaWYoY29tcGFyaXNvbiRBY3F1aXNpdGlvblszXSA+IGNvbXBhcmlzb24kQmFua3J1cHRjeVszXSAqIDEuMTUpIHsNCiAgICBjYXQoIi0gVGhlIGFjcXVpc2l0aW9uIG1vZGVsIGNhcHR1cmVzIG1vcmUgZXZlbnRzIGluIGl0cyBoaWdoZXN0IHJpc2sgcXVpbnRpbGVcbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCItIEJvdGggbW9kZWxzIGNhcHR1cmUgYSBzaW1pbGFyIHByb3BvcnRpb24gb2YgZXZlbnRzIGluIHRoZWlyIGhpZ2hlc3QgcmlzayBxdWludGlsZVxuIikNCiAgfQ0KICANCiAgIyBPdmVyYWxsIGNvbmNsdXNpb24NCiAgaWYoc3VtKGNvbXBhcmlzb24kQmFua3J1cHRjeSkgPiBzdW0oY29tcGFyaXNvbiRBY3F1aXNpdGlvbikgKiAxLjE1KSB7DQogICAgY2F0KCJcbk92ZXJhbGwsIHRoZSBiYW5rcnVwdGN5IG1vZGVsIHByb3ZpZGVzIGJldHRlciByaXNrIHN0cmF0aWZpY2F0aW9uLFxuIikNCiAgICBjYXQoInN1Z2dlc3RpbmcgYmFua3J1cHRjeSBldmVudHMgYXJlIG1vcmUgcHJlZGljdGFibGUgdGhhbiBhY3F1aXNpdGlvbnMuXG4iKQ0KICB9IGVsc2UgaWYoc3VtKGNvbXBhcmlzb24kQWNxdWlzaXRpb24pID4gc3VtKGNvbXBhcmlzb24kQmFua3J1cHRjeSkgKiAxLjE1KSB7DQogICAgY2F0KCJcbk92ZXJhbGwsIHRoZSBhY3F1aXNpdGlvbiBtb2RlbCBwcm92aWRlcyBiZXR0ZXIgcmlzayBzdHJhdGlmaWNhdGlvbixcbiIpDQogICAgY2F0KCJzdWdnZXN0aW5nIGFjcXVpc2l0aW9uIGV2ZW50cyBhcmUgbW9yZSBwcmVkaWN0YWJsZSB0aGFuIGJhbmtydXB0Y2llcy5cbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJcbk92ZXJhbGwsIGJvdGggbW9kZWxzIHByb3ZpZGUgY29tcGFyYWJsZSByaXNrIHN0cmF0aWZpY2F0aW9uLFxuIikNCiAgICBjYXQoInN1Z2dlc3Rpbmcgc2ltaWxhciBsZXZlbHMgb2YgcHJlZGljdGFiaWxpdHkgZm9yIGJvdGggdHlwZXMgb2YgZXZlbnRzLlxuIikNCiAgfQ0KfQ0KYGBgDQoNCg0KDQpOb3QgZW50aXJlbHkgc3VyZSBvZiB0aGlzIGFwcGxpY2F0aW9uIGhlcmUgYnV0IGl0cyBhbiBpbm5vdmF0aXZlIGFwcHJvYWNoIChzdGlsbCBub3QgZW50aXJlbHkgY29ycmVjdCBoZXJlKS4gQmFzaWNhbGx5IHdlIGFyZSB1c2luZyB0aGUgQ294IG1vZGVsIHByZWRpY3Rpb25zIHRvIHJhbmsgY29tcGFuaWVzIGJ5IHJpc2suIFRoaXMgaXMgYSBjb21tb24gcHJhY3RpY2UgaW4gc3Vydml2YWwgYW5hbHlzaXMsIHdoZXJlIHdlIHdhbnQgdG8gaWRlbnRpZnkgd2hpY2ggY29tcGFuaWVzIGFyZSBhdCBoaWdoZXIgcmlzayBvZiBleHBlcmllbmNpbmcgYW4gZXZlbnQgKGxpa2UgYmFua3J1cHRjeSBvciBhY3F1aXNpdGlvbikgYmFzZWQgb24gdGhlaXIgY2hhcmFjdGVyaXN0aWNzIGFuZCB0aGUgdGltZSB1bnRpbCB0aGUgZXZlbnQgb2NjdXJzLiBJIHRoaW5rIGl0cyBpbm5vdmF0aXZlIGJlY2F1c2UgaSBoYXZlbnQgc2VlbiBpdCBpbiBhbnkgb2YgdGhlIHJlc2VhcmNoLCBpdCBhbHNvIGFsbG93cyB1cyB0byBraW5kYSBhcHBseSBvdXIgbW9kZWwgSVJMLg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMTQ6IFJhbmtpbmcgQ29tcGFuaWVzIGJ5IFJpc2sgKFVzaW5nIENveCBNb2RlbCBQcmVkaWN0aW9ucykNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuPT09IFNURVAgMTQ6IFJBTktJTkcgQ09NUEFOSUVTIEJZIFJJU0sgVVNJTkcgQ09YIE1PREVMIFBSRURJQ1RJT05TID09PVxuIikNCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTQuMTogQ3JlYXRlIFJpc2sgUmFua2luZyBGdW5jdGlvbg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xNC4xOiBDUkVBVElORyBSSVNLIFJBTktJTkdTIEJBU0VEIE9OIFBSRURJQ1RFRCBIQVpBUkRTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KIyBGdW5jdGlvbiB0byByYW5rIGNvbXBhbmllcyBieSByaXNrIHVzaW5nIENveCBtb2RlbHMNCnJhbmtfY29tcGFuaWVzX2J5X3Jpc2sgPC0gZnVuY3Rpb24oYmFua3J1cHRjeV9tb2RlbCwgYWNxdWlzaXRpb25fbW9kZWwsIHRvcF9uID0gMjApIHsNCiAgY2F0KCJSYW5raW5nIGNvbXBhbmllcyBieSByaXNrIHVzaW5nIENveCBtb2RlbCBwcmVkaWN0aW9ucy4uLlxuIikNCiAgDQogICMgR2V0IHRoZSBsYXRlc3Qgb2JzZXJ2YXRpb24gZm9yIGVhY2ggY29tcGFueQ0KICBsYXRlc3Rfb2JzIDwtIGRhdGEgJT4lDQogICAgZ3JvdXBfYnkoY3VzaXApICU+JQ0KICAgIHNsaWNlX21heCh0c3RvcCkgJT4lDQogICAgdW5ncm91cCgpDQogIA0KICAjIENhbGN1bGF0ZSBjb21wYW55IGFnZSAodGltZSBzaW5jZSBmaXJzdCBvYnNlcnZhdGlvbikNCiAgY29tcGFueV9hZ2UgPC0gbGF0ZXN0X29icyR0c3RvcA0KICANCiAgIyBDYWxjdWxhdGUgcmlzayBzY29yZXMgZnJvbSBib3RoIG1vZGVscw0KICBiYW5rcnVwdGN5X3Jpc2sgPC0gcHJlZGljdChiYW5rcnVwdGN5X21vZGVsLCBuZXdkYXRhID0gbGF0ZXN0X29icywgdHlwZSA9ICJyaXNrIikNCiAgYWNxdWlzaXRpb25fcmlzayA8LSBwcmVkaWN0KGFjcXVpc2l0aW9uX21vZGVsLCBuZXdkYXRhID0gbGF0ZXN0X29icywgdHlwZSA9ICJyaXNrIikNCiAgDQogICMgQWRkIHNjb3JlcyB0byBkYXRhDQogIGxhdGVzdF9vYnMkYmFua3J1cHRjeV9yaXNrIDwtIGJhbmtydXB0Y3lfcmlzaw0KICBsYXRlc3Rfb2JzJGFjcXVpc2l0aW9uX3Jpc2sgPC0gYWNxdWlzaXRpb25fcmlzaw0KICANCiAgIyBDYWxjdWxhdGUgaW52ZXJzZSBzdXJ2aXZhbCBwcm9iYWJpbGl0eSAoMSAtIGV4cCgtcmlzaykpDQogICMgVGhpcyBnaXZlcyBhbiBlc3RpbWF0aW9uIG9mIGV2ZW50IHByb2JhYmlsaXR5DQogIGxhdGVzdF9vYnMkYmFua3J1cHRjeV9wcm9iIDwtIDEgLSBleHAoLWJhbmtydXB0Y3lfcmlzaykNCiAgbGF0ZXN0X29icyRhY3F1aXNpdGlvbl9wcm9iIDwtIDEgLSBleHAoLWFjcXVpc2l0aW9uX3Jpc2spDQogIA0KICAjIENhbGN1bGF0ZSBzdXJ2aXZhbCBwcm9iYWJpbGl0eSBmb3IgYm90aCBldmVudHMgY29tYmluZWQNCiAgbGF0ZXN0X29icyRvdmVyYWxsX3N1cnZpdmFsX3Byb2IgPC0gZXhwKC0oYmFua3J1cHRjeV9yaXNrICsgYWNxdWlzaXRpb25fcmlzaykpDQogIA0KICAjIENhbGN1bGF0ZSBjb25kaXRpb25hbCBwcm9iYWJpbGl0aWVzIChnaXZlbiB0aGF0IGFuIGV2ZW50IGhhcHBlbnMpDQogIGRlbm9taW5hdG9yIDwtIGJhbmtydXB0Y3lfcmlzayArIGFjcXVpc2l0aW9uX3Jpc2sNCiAgbGF0ZXN0X29icyRiYW5rcnVwdGN5X2NvbmRfcHJvYiA8LSBpZmVsc2UoZGVub21pbmF0b3IgPiAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFua3J1cHRjeV9yaXNrIC8gZGVub21pbmF0b3IsIDApDQogIGxhdGVzdF9vYnMkYWNxdWlzaXRpb25fY29uZF9wcm9iIDwtIGlmZWxzZShkZW5vbWluYXRvciA+IDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNxdWlzaXRpb25fcmlzayAvIGRlbm9taW5hdG9yLCAwKQ0KICANCiAgIyBDcmVhdGUgY29tYmluZWQgcmlzayBzY29yZSAod2VpZ2h0ZWQgc3VtIG9mIGJvdGggcmlza3MpDQogIGxhdGVzdF9vYnMkY29tYmluZWRfcmlzayA8LSBiYW5rcnVwdGN5X3Jpc2sgKiAwLjYgKyBhY3F1aXNpdGlvbl9yaXNrICogMC40ICAjIEV4YW1wbGUgd2VpZ2h0cw0KICANCiAgIyBDYWxjdWxhdGUgdGltZS1hZGp1c3RlZCBwcm9iYWJpbGl0aWVzIChoaWdoZXIgcmlzayBmb3Igc2hvcnRlciB0aW1lIHBlcmlvZHMpDQogIG1heF90aW1lIDwtIG1heChjb21wYW55X2FnZSkNCiAgdGltZV9mYWN0b3IgPC0gKG1heF90aW1lIC0gY29tcGFueV9hZ2UpIC8gbWF4X3RpbWUNCiAgbGF0ZXN0X29icyR0aW1lX2Fkal9iYW5rcnVwdGN5X3Byb2IgPC0gbGF0ZXN0X29icyRiYW5rcnVwdGN5X3Byb2IgKiAoMSArIHRpbWVfZmFjdG9yKQ0KICBsYXRlc3Rfb2JzJHRpbWVfYWRqX2FjcXVpc2l0aW9uX3Byb2IgPC0gbGF0ZXN0X29icyRhY3F1aXNpdGlvbl9wcm9iICogKDEgKyB0aW1lX2ZhY3RvcikNCiAgDQogICMgU2VsZWN0IGtleSB2YXJpYWJsZXMgZm9yIHRoZSByYW5raW5nDQogIHJpc2tfcmFua2luZyA8LSBsYXRlc3Rfb2JzICU+JQ0KICAgIHNlbGVjdChjdXNpcCwgZ3NlY3RvciwgYmFua3J1cHRjeV9yaXNrLCBhY3F1aXNpdGlvbl9yaXNrLCBiYW5rcnVwdGN5X3Byb2IsIA0KICAgICAgICAgICBhY3F1aXNpdGlvbl9wcm9iLCBjb21iaW5lZF9yaXNrLCBvdmVyYWxsX3N1cnZpdmFsX3Byb2IsDQogICAgICAgICAgIGJhbmtydXB0Y3lfY29uZF9wcm9iLCBhY3F1aXNpdGlvbl9jb25kX3Byb2IpDQogIA0KICAjIEFkZCBjb21wYW55IGZpbmFuY2lhbCBpbmZvcm1hdGlvbiBmb3IgY29udGV4dA0KICBrZXlfZmluYW5jaWFscyA8LSBjKCJMVE1UQSIsICJOSU1UQSIsICJDQVNITVRBIiwgIlBSSUNFIiwgInpfc2NvcmUiKQ0KICByaXNrX3JhbmtpbmcgPC0gY2JpbmQocmlza19yYW5raW5nLCBsYXRlc3Rfb2JzWywgaW50ZXJzZWN0KGtleV9maW5hbmNpYWxzLCBjb2xuYW1lcyhsYXRlc3Rfb2JzKSldKQ0KICANCiAgIyBBZGQgbWFjcm9lY29ub21pYyBjb250ZXh0IGZvciB0aGUgbGF0ZXN0IG9ic2VydmF0aW9uDQogIGtleV9tYWNybyA8LSBjKCJnZHBfZ3Jvd3RoIiwgInVuZW1wbG95ZW1lbnQiLCAiZ2RwX2RlZmxhdG9yIikNCiAgcmlza19yYW5raW5nIDwtIGNiaW5kKHJpc2tfcmFua2luZywgbGF0ZXN0X29ic1ssIGludGVyc2VjdChrZXlfbWFjcm8sIGNvbG5hbWVzKGxhdGVzdF9vYnMpKV0pDQogIA0KICAjIFJhbmsgY29tcGFuaWVzIGZvciBiYW5rcnVwdGN5IHJpc2sNCiAgY2F0KCJcblJhbmtpbmcgY29tcGFuaWVzIGJ5IGJhbmtydXB0Y3kgcmlzay4uLlxuIikNCiAgYmFua3J1cHRjeV9yYW5raW5nIDwtIHJpc2tfcmFua2luZyAlPiUNCiAgICBhcnJhbmdlKGRlc2MoYmFua3J1cHRjeV9yaXNrKSkNCiAgDQogICMgUmFuayBjb21wYW5pZXMgZm9yIGFjcXVpc2l0aW9uIHJpc2sNCiAgY2F0KCJSYW5raW5nIGNvbXBhbmllcyBieSBhY3F1aXNpdGlvbiByaXNrLi4uXG4iKQ0KICBhY3F1aXNpdGlvbl9yYW5raW5nIDwtIHJpc2tfcmFua2luZyAlPiUNCiAgICBhcnJhbmdlKGRlc2MoYWNxdWlzaXRpb25fcmlzaykpDQogIA0KICAjIENyZWF0ZSBhICJzYWZlIGludmVzdG1lbnRzIiByYW5raW5nIChsb3cgcmlzayBvZiBib3RoIGV2ZW50cykNCiAgY2F0KCJSYW5raW5nIGNvbXBhbmllcyBieSBvdmVyYWxsIHN1cnZpdmFsIHByb2JhYmlsaXR5IChzYWZlIGludmVzdG1lbnRzKS4uLlxuIikNCiAgc2FmZV9yYW5raW5nIDwtIHJpc2tfcmFua2luZyAlPiUNCiAgICBhcnJhbmdlKGRlc2Mob3ZlcmFsbF9zdXJ2aXZhbF9wcm9iKSkNCiAgDQogICMgUHJpbnQgdG9wIGNvbXBhbmllcyBieSBiYW5rcnVwdGN5IHJpc2sNCiAgY2F0KCJcblRvcCIsIHRvcF9uLCAiQ29tcGFuaWVzIHdpdGggSGlnaGVzdCBCYW5rcnVwdGN5IFJpc2s6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgdG9wX2JhbmtydXB0Y3kgPC0gaGVhZChiYW5rcnVwdGN5X3JhbmtpbmcsIHRvcF9uKQ0KICAjIEZvcm1hdCBwcm9iYWJpbGl0aWVzIGFzIHBlcmNlbnRhZ2VzDQogIHRvcF9iYW5rcnVwdGN5JGJhbmtydXB0Y3lfcHJvYiA8LSByb3VuZCh0b3BfYmFua3J1cHRjeSRiYW5rcnVwdGN5X3Byb2IgKiAxMDAsIDEpDQogIHByaW50KHRvcF9iYW5rcnVwdGN5WywgYygiY3VzaXAiLCAiZ3NlY3RvciIsICJiYW5rcnVwdGN5X3Jpc2siLCAiYmFua3J1cHRjeV9wcm9iIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcnNlY3QoYygiTFRNVEEiLCAiTklNVEEiLCAiel9zY29yZSIpLCBjb2xuYW1lcyh0b3BfYmFua3J1cHRjeSkpKV0pDQogIA0KICAjIFByaW50IHRvcCBjb21wYW5pZXMgYnkgYWNxdWlzaXRpb24gcmlzaw0KICBjYXQoIlxuVG9wIiwgdG9wX24sICJDb21wYW5pZXMgd2l0aCBIaWdoZXN0IEFjcXVpc2l0aW9uIFJpc2s6XG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICB0b3BfYWNxdWlzaXRpb24gPC0gaGVhZChhY3F1aXNpdGlvbl9yYW5raW5nLCB0b3BfbikNCiAgIyBGb3JtYXQgcHJvYmFiaWxpdGllcyBhcyBwZXJjZW50YWdlcw0KICB0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fcHJvYiA8LSByb3VuZCh0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fcHJvYiAqIDEwMCwgMSkNCiAgcHJpbnQodG9wX2FjcXVpc2l0aW9uWywgYygiY3VzaXAiLCAiZ3NlY3RvciIsICJhY3F1aXNpdGlvbl9yaXNrIiwgImFjcXVpc2l0aW9uX3Byb2IiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnRlcnNlY3QoYygiUFJJQ0UiLCAiQ0FTSE1UQSIsICJNQkUiKSwgY29sbmFtZXModG9wX2FjcXVpc2l0aW9uKSkpXSkNCiAgDQogICMgUHJpbnQgdG9wIHNhZmUgaW52ZXN0bWVudCBvcHRpb25zDQogIGNhdCgiXG5Ub3AiLCB0b3BfbiwgIlNhZmVzdCBDb21wYW5pZXMgKExvd2VzdCBDb21iaW5lZCBSaXNrKTpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgdG9wX3NhZmUgPC0gaGVhZChzYWZlX3JhbmtpbmcsIHRvcF9uKQ0KICAjIEZvcm1hdCBzdXJ2aXZhbCBwcm9iYWJpbGl0eSBhcyBwZXJjZW50YWdlDQogIHRvcF9zYWZlJG92ZXJhbGxfc3Vydml2YWxfcHJvYiA8LSByb3VuZCh0b3Bfc2FmZSRvdmVyYWxsX3N1cnZpdmFsX3Byb2IgKiAxMDAsIDEpDQogIHByaW50KHRvcF9zYWZlWywgYygiY3VzaXAiLCAiZ3NlY3RvciIsICJvdmVyYWxsX3N1cnZpdmFsX3Byb2IiLCAiYmFua3J1cHRjeV9yaXNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgImFjcXVpc2l0aW9uX3Jpc2siKV0pDQogIA0KICAjIFJldHVybiB0aGUgZnVsbCByYW5raW5ncw0KICByZXR1cm4obGlzdCgNCiAgICBiYW5rcnVwdGN5X3JhbmtpbmcgPSBiYW5rcnVwdGN5X3JhbmtpbmcsDQogICAgYWNxdWlzaXRpb25fcmFua2luZyA9IGFjcXVpc2l0aW9uX3JhbmtpbmcsDQogICAgc2FmZV9yYW5raW5nID0gc2FmZV9yYW5raW5nDQogICkpDQp9DQoNCmBgYA0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDE0LjI6IEFwcGx5IFJpc2sgUmFua2luZyANCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTQuMjogQVBQTFlJTkcgUklTSyBSQU5LSU5HIFRPIENPTVBBTklFUyBJTiBUSEUgREFUQVNFVFxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQoNCiMgRXhlY3V0ZSB0aGUgcmFua2luZyBmdW5jdGlvbiBvbiB0aGUgbW9kZWxzDQpjb21wYW55X3JhbmtpbmdzIDwtIHJhbmtfY29tcGFuaWVzX2J5X3Jpc2soZmluYWxfYmFua3J1cHRjeV9tb2RlbCwgZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpDQoNCiMgR2V0IHNlY3Rvci1zcGVjaWZpYyByaXNrIHJhbmtpbmdzDQpyYW5rX2J5X3NlY3RvciA8LSBmdW5jdGlvbihiYW5rcnVwdGN5X3JhbmtpbmcsIGFjcXVpc2l0aW9uX3JhbmtpbmcpIHsNCiAgY2F0KCJcbklkZW50aWZ5aW5nIGhpZ2hlc3QtcmlzayBjb21wYW5pZXMgYnkgc2VjdG9yOlxuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICANCiAgIyBHZXQgdW5pcXVlIHNlY3RvcnMNCiAgc2VjdG9ycyA8LSB1bmlxdWUoYmFua3J1cHRjeV9yYW5raW5nJGdzZWN0b3IpDQogIA0KICBmb3IocyBpbiBzZWN0b3JzKSB7DQogICAgIyBTa2lwIHNlY3RvcnMgd2l0aCB0b28gZmV3IGNvbXBhbmllcw0KICAgIHNlY3Rvcl9jb21wYW5pZXMgPC0gc3VtKGJhbmtydXB0Y3lfcmFua2luZyRnc2VjdG9yID09IHMpDQogICAgaWYoc2VjdG9yX2NvbXBhbmllcyA8IDUpIHsNCiAgICAgIG5leHQNCiAgICB9DQogICAgDQogICAgY2F0KCJcblNlY3RvciIsIHMsICIoIiwgc2VjdG9yX2NvbXBhbmllcywgImNvbXBhbmllcyk6XG4iKQ0KICAgIA0KICAgICMgVG9wIGJhbmtydXB0Y3kgcmlza3MgaW4gdGhpcyBzZWN0b3INCiAgICB0b3BfYmFua3J1cHRjeSA8LSBiYW5rcnVwdGN5X3JhbmtpbmcgJT4lDQogICAgICBmaWx0ZXIoZ3NlY3RvciA9PSBzKSAlPiUNCiAgICAgIGFycmFuZ2UoZGVzYyhiYW5rcnVwdGN5X3Jpc2spKSAlPiUNCiAgICAgIGhlYWQoMykNCiAgICANCiAgICAjIFRvcCBhY3F1aXNpdGlvbiByaXNrcyBpbiB0aGlzIHNlY3Rvcg0KICAgIHRvcF9hY3F1aXNpdGlvbiA8LSBhY3F1aXNpdGlvbl9yYW5raW5nICU+JQ0KICAgICAgZmlsdGVyKGdzZWN0b3IgPT0gcykgJT4lDQogICAgICBhcnJhbmdlKGRlc2MoYWNxdWlzaXRpb25fcmlzaykpICU+JQ0KICAgICAgaGVhZCgzKQ0KICAgIA0KICAgIGNhdCgiICBIaWdoZXN0IEJhbmtydXB0Y3kgUmlzazpcbiIpDQogICAgZm9yKGkgaW4gMTpucm93KHRvcF9iYW5rcnVwdGN5KSkgew0KICAgICAgY2F0KCIgICAgIiwgaSwgIi4gQ1VTSVA6ICIsIHRvcF9iYW5rcnVwdGN5JGN1c2lwW2ldLCANCiAgICAgICAgICAiIChSaXNrIFNjb3JlOiAiLCByb3VuZCh0b3BfYmFua3J1cHRjeSRiYW5rcnVwdGN5X3Jpc2tbaV0sIDQpLCANCiAgICAgICAgICAiLCBQcm9iYWJpbGl0eTogIiwgcm91bmQodG9wX2JhbmtydXB0Y3kkYmFua3J1cHRjeV9wcm9iW2ldLCAxKSwgIiUpXG4iLCBzZXA9IiIpDQogICAgfQ0KICAgIA0KICAgIGNhdCgiICBIaWdoZXN0IEFjcXVpc2l0aW9uIFJpc2s6XG4iKQ0KICAgIGZvcihpIGluIDE6bnJvdyh0b3BfYWNxdWlzaXRpb24pKSB7DQogICAgICBjYXQoIiAgICAiLCBpLCAiLiBDVVNJUDogIiwgdG9wX2FjcXVpc2l0aW9uJGN1c2lwW2ldLCANCiAgICAgICAgICAiIChSaXNrIFNjb3JlOiAiLCByb3VuZCh0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fcmlza1tpXSwgNCksIA0KICAgICAgICAgICIsIFByb2JhYmlsaXR5OiAiLCByb3VuZCh0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fcHJvYltpXSwgMSksICIlKVxuIiwgc2VwPSIiKQ0KICAgIH0NCiAgfQ0KfQ0KDQojIEFwcGx5IHNlY3Rvci1zcGVjaWZpYyByYW5raW5nDQpyYW5rX2J5X3NlY3Rvcihjb21wYW55X3JhbmtpbmdzJGJhbmtydXB0Y3lfcmFua2luZywgY29tcGFueV9yYW5raW5ncyRhY3F1aXNpdGlvbl9yYW5raW5nKQ0KDQpgYGANCg0KDQpIZXJlLCB3ZSBhcmUgYW5hbHl6aW5nIHRoZSByaXNrIHByb2ZpbGVzIG9mIGNvbXBhbmllcyBiYXNlZCBvbiB0aGVpciBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiByaXNrcy4gV2UgY2F0ZWdvcml6ZSBjb21wYW5pZXMgaW50byBkaWZmZXJlbnQgcmlzayBjYXRlZ29yaWVzIGJhc2VkIG9uIHRoZWlyIHByZWRpY3RlZCByaXNrcyBhbmQgYW5hbHl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZXNlIGNhdGVnb3JpZXMgYWNyb3NzIHNlY3RvcnMuIFdlIGFsc28gdmlzdWFsaXplIHRoZSByaXNrIGxhbmRzY2FwZSB1c2luZyBzY2F0dGVyIHBsb3RzIGFuZCBpZGVudGlmeSBzZWN0b3JzIHdpdGggaGlnaCBwcm9wb3J0aW9ucyBvZiBjb21wYW5pZXMgaW4gZWFjaCByaXNrIGNhdGVnb3J5Lg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAxNC4zOiBSaXNrIFByb2ZpbGUgQW5hbHlzaXMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpjYXQoIlxuMTQuMzogUklTSyBQUk9GSUxFIEFOQUxZU0lTXG4iKQ0KY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KYW5hbHl6ZV9yaXNrX3Byb2ZpbGVzIDwtIGZ1bmN0aW9uKGJhbmtydXB0Y3lfcmFua2luZywgYWNxdWlzaXRpb25fcmFua2luZykgew0KICBjYXQoIkFuYWx5emluZyBjb21wYW55IHJpc2sgcHJvZmlsZXMuLi5cbiIpDQogIA0KICAjIENyZWF0ZSBhIGNvbWJpbmVkIHJhbmtpbmcgd2l0aCByaXNrIGNhdGVnb3JpemF0aW9uDQogIGNvbWJpbmVkX3JhbmtpbmcgPC0gYmFua3J1cHRjeV9yYW5raW5nDQogIA0KICAjIERldGVybWluZSByaXNrIGNhdGVnb3JpZXMgYmFzZWQgb24gcGVyY2VudGlsZXMNCiAgYmFua3J1cHRjeV90aHJlc2hvbGQgPC0gcXVhbnRpbGUoY29tYmluZWRfcmFua2luZyRiYW5rcnVwdGN5X3Jpc2ssIDAuNzUpDQogIGFjcXVpc2l0aW9uX3RocmVzaG9sZCA8LSBxdWFudGlsZShjb21iaW5lZF9yYW5raW5nJGFjcXVpc2l0aW9uX3Jpc2ssIDAuNzUpDQogIA0KICAjIENhdGVnb3JpemUgY29tcGFuaWVzIGJ5IHJpc2sgcHJvZmlsZQ0KICBjb21iaW5lZF9yYW5raW5nJHJpc2tfY2F0ZWdvcnkgPC0gIkxvdyBSaXNrIg0KICBjb21iaW5lZF9yYW5raW5nJHJpc2tfY2F0ZWdvcnlbY29tYmluZWRfcmFua2luZyRiYW5rcnVwdGN5X3Jpc2sgPiBiYW5rcnVwdGN5X3RocmVzaG9sZCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlzayA8PSBhY3F1aXNpdGlvbl90aHJlc2hvbGRdIDwtICJCYW5rcnVwdGN5IFJpc2siDQogIGNvbWJpbmVkX3Jhbmtpbmckcmlza19jYXRlZ29yeVtjb21iaW5lZF9yYW5raW5nJGJhbmtydXB0Y3lfcmlzayA8PSBiYW5rcnVwdGN5X3RocmVzaG9sZCAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlzayA+IGFjcXVpc2l0aW9uX3RocmVzaG9sZF0gPC0gIkFjcXVpc2l0aW9uIFJpc2siDQogIGNvbWJpbmVkX3Jhbmtpbmckcmlza19jYXRlZ29yeVtjb21iaW5lZF9yYW5raW5nJGJhbmtydXB0Y3lfcmlzayA+IGJhbmtydXB0Y3lfdGhyZXNob2xkICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tYmluZWRfcmFua2luZyRhY3F1aXNpdGlvbl9yaXNrID4gYWNxdWlzaXRpb25fdGhyZXNob2xkXSA8LSAiSGlnaCBSaXNrIChCb3RoKSINCiAgDQogICMgQ29udmVydCB0byBmYWN0b3Igd2l0aCBtZWFuaW5nZnVsIG9yZGVyDQogIGNvbWJpbmVkX3Jhbmtpbmckcmlza19jYXRlZ29yeSA8LSBmYWN0b3IoY29tYmluZWRfcmFua2luZyRyaXNrX2NhdGVnb3J5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJMb3cgUmlzayIsICJCYW5rcnVwdGN5IFJpc2siLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFjcXVpc2l0aW9uIFJpc2siLCAiSGlnaCBSaXNrIChCb3RoKSIpKQ0KICANCiAgIyBDb3VudCBjb21wYW5pZXMgaW4gZWFjaCBjYXRlZ29yeQ0KICBjYXRlZ29yeV9jb3VudHMgPC0gdGFibGUoY29tYmluZWRfcmFua2luZyRyaXNrX2NhdGVnb3J5KQ0KICBjYXRlZ29yeV9wY3RzIDwtIHByb3AudGFibGUoY2F0ZWdvcnlfY291bnRzKSAqIDEwMA0KICANCiAgY2F0KCJcbkNvbXBhbnkgUmlzayBQcm9maWxlIERpc3RyaWJ1dGlvbjpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpDQogIGZvcihpIGluIDE6bGVuZ3RoKGNhdGVnb3J5X2NvdW50cykpIHsNCiAgICBjYXQobmFtZXMoY2F0ZWdvcnlfY291bnRzKVtpXSwgIjogIiwgY2F0ZWdvcnlfY291bnRzW2ldLCANCiAgICAgICAgIiBjb21wYW5pZXMgKCIsIHJvdW5kKGNhdGVnb3J5X3BjdHNbaV0sIDEpLCAiJSlcbiIsIHNlcD0iIikNCiAgfQ0KICANCiAgIyBBbmFseXplIHJpc2sgcHJvZmlsZXMgYnkgc2VjdG9yDQogIHNlY3Rvcl9wcm9maWxlcyA8LSB0YWJsZShjb21iaW5lZF9yYW5raW5nJGdzZWN0b3IsIGNvbWJpbmVkX3Jhbmtpbmckcmlza19jYXRlZ29yeSkNCiAgc2VjdG9yX3Byb2ZpbGVfcGN0cyA8LSBwcm9wLnRhYmxlKHNlY3Rvcl9wcm9maWxlcywgbWFyZ2luID0gMSkgKiAxMDANCiAgDQogIGNhdCgiXG5SaXNrIFByb2ZpbGUgRGlzdHJpYnV0aW9uIGJ5IFNlY3RvcjpcbiIpDQogIGNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgcHJpbnQocm91bmQoc2VjdG9yX3Byb2ZpbGVfcGN0cywgMSkpDQogIA0KICAjIElkZW50aWZ5IHNlY3RvcnMgd2l0aCBoaWdoZXN0IHByb3BvcnRpb24gb2YgZWFjaCByaXNrIHR5cGUNCiAgaGlnaF9iYW5rcnVwdGN5X3NlY3RvcnMgPC0gc29ydChzZWN0b3JfcHJvZmlsZV9wY3RzWywgIkJhbmtydXB0Y3kgUmlzayJdLCBkZWNyZWFzaW5nID0gVFJVRSkNCiAgaGlnaF9hY3F1aXNpdGlvbl9zZWN0b3JzIDwtIHNvcnQoc2VjdG9yX3Byb2ZpbGVfcGN0c1ssICJBY3F1aXNpdGlvbiBSaXNrIl0sIGRlY3JlYXNpbmcgPSBUUlVFKQ0KICBoaWdoX2JvdGhfc2VjdG9ycyA8LSBzb3J0KHNlY3Rvcl9wcm9maWxlX3BjdHNbLCAiSGlnaCBSaXNrIChCb3RoKSJdLCBkZWNyZWFzaW5nID0gVFJVRSkNCiAgbG93X3Jpc2tfc2VjdG9ycyA8LSBzb3J0KHNlY3Rvcl9wcm9maWxlX3BjdHNbLCAiTG93IFJpc2siXSwgZGVjcmVhc2luZyA9IFRSVUUpDQogIA0KICBjYXQoIlxuU2VjdG9ycyB3aXRoIEhpZ2hlc3QgUHJvcG9ydGlvbiBvZiBCYW5rcnVwdGN5IFJpc2s6XG4iKQ0KICBmb3IoaSBpbiAxOm1pbigzLCBsZW5ndGgoaGlnaF9iYW5rcnVwdGN5X3NlY3RvcnMpKSkgew0KICAgIHNlY3RvciA8LSBuYW1lcyhoaWdoX2JhbmtydXB0Y3lfc2VjdG9ycylbaV0NCiAgICBwY3QgPC0gaGlnaF9iYW5rcnVwdGN5X3NlY3RvcnNbaV0NCiAgICBjYXQoIiAgU2VjdG9yIiwgc2VjdG9yLCAiOiAiLCByb3VuZChwY3QsIDEpLCAiJSBvZiBjb21wYW5pZXMgYXQgaGlnaCBiYW5rcnVwdGN5IHJpc2tcbiIsIHNlcD0iIikNCiAgfQ0KICANCiAgY2F0KCJcblNlY3RvcnMgd2l0aCBIaWdoZXN0IFByb3BvcnRpb24gb2YgQWNxdWlzaXRpb24gUmlzazpcbiIpDQogIGZvcihpIGluIDE6bWluKDMsIGxlbmd0aChoaWdoX2FjcXVpc2l0aW9uX3NlY3RvcnMpKSkgew0KICAgIHNlY3RvciA8LSBuYW1lcyhoaWdoX2FjcXVpc2l0aW9uX3NlY3RvcnMpW2ldDQogICAgcGN0IDwtIGhpZ2hfYWNxdWlzaXRpb25fc2VjdG9yc1tpXQ0KICAgIGNhdCgiICBTZWN0b3IiLCBzZWN0b3IsICI6ICIsIHJvdW5kKHBjdCwgMSksICIlIG9mIGNvbXBhbmllcyBhdCBoaWdoIGFjcXVpc2l0aW9uIHJpc2tcbiIsIHNlcD0iIikNCiAgfQ0KICANCiAgY2F0KCJcblNlY3RvcnMgd2l0aCBIaWdoZXN0IFByb3BvcnRpb24gb2YgTG93IFJpc2sgQ29tcGFuaWVzOlxuIikNCiAgZm9yKGkgaW4gMTptaW4oMywgbGVuZ3RoKGxvd19yaXNrX3NlY3RvcnMpKSkgew0KICAgIHNlY3RvciA8LSBuYW1lcyhsb3dfcmlza19zZWN0b3JzKVtpXQ0KICAgIHBjdCA8LSBsb3dfcmlza19zZWN0b3JzW2ldDQogICAgY2F0KCIgIFNlY3RvciIsIHNlY3RvciwgIjogIiwgcm91bmQocGN0LCAxKSwgIiUgb2YgY29tcGFuaWVzIGF0IGxvdyByaXNrXG4iLCBzZXA9IiIpDQogIH0NCiAgDQogICMgQ3JlYXRlIHZpc3VhbGl6YXRpb24gb2YgcmlzayBwcm9maWxlcw0KICAjIFJpc2stUmlzayBzY2F0dGVyIHBsb3QNCiAgcGxvdChjb21iaW5lZF9yYW5raW5nJGJhbmtydXB0Y3lfcmlzaywgY29tYmluZWRfcmFua2luZyRhY3F1aXNpdGlvbl9yaXNrLA0KICAgICAgIG1haW4gPSAiQ29tcGFueSBSaXNrIExhbmRzY2FwZSIsDQogICAgICAgeGxhYiA9ICJCYW5rcnVwdGN5IFJpc2siLCB5bGFiID0gIkFjcXVpc2l0aW9uIFJpc2siLA0KICAgICAgIHBjaCA9IDE2LCBjb2wgPSBhZGp1c3Rjb2xvcigiYmx1ZSIsIGFscGhhLmYgPSAwLjQpLA0KICAgICAgIHhsaW0gPSBjKDAsIG1heChjb21iaW5lZF9yYW5raW5nJGJhbmtydXB0Y3lfcmlzaykgKiAxLjEpLA0KICAgICAgIHlsaW0gPSBjKDAsIG1heChjb21iaW5lZF9yYW5raW5nJGFjcXVpc2l0aW9uX3Jpc2spICogMS4xKSkNCiAgDQogICMgQWRkIHNlY3Rvci1zcGVjaWZpYyBjb2xvcmluZyBpZiBwcmVmZXJyZWQNCiAgIyBVc2UgZGlmZmVyZW50IGNvbG9ycyBmb3IgZGlmZmVyZW50IHJpc2sgY2F0ZWdvcmllcw0KICBwb2ludHMoY29tYmluZWRfcmFua2luZyRiYW5rcnVwdGN5X3Jpc2tbY29tYmluZWRfcmFua2luZyRyaXNrX2NhdGVnb3J5ID09ICJCYW5rcnVwdGN5IFJpc2siXSwNCiAgICAgICAgIGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlza1tjb21iaW5lZF9yYW5raW5nJHJpc2tfY2F0ZWdvcnkgPT0gIkJhbmtydXB0Y3kgUmlzayJdLA0KICAgICAgICAgcGNoID0gMTYsIGNvbCA9IGFkanVzdGNvbG9yKCJyZWQiLCBhbHBoYS5mID0gMC42KSkNCiAgDQogIHBvaW50cyhjb21iaW5lZF9yYW5raW5nJGJhbmtydXB0Y3lfcmlza1tjb21iaW5lZF9yYW5raW5nJHJpc2tfY2F0ZWdvcnkgPT0gIkFjcXVpc2l0aW9uIFJpc2siXSwNCiAgICAgICAgIGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlza1tjb21iaW5lZF9yYW5raW5nJHJpc2tfY2F0ZWdvcnkgPT0gIkFjcXVpc2l0aW9uIFJpc2siXSwNCiAgICAgICAgIHBjaCA9IDE2LCBjb2wgPSBhZGp1c3Rjb2xvcigiZ3JlZW4iLCBhbHBoYS5mID0gMC42KSkNCiAgDQogIHBvaW50cyhjb21iaW5lZF9yYW5raW5nJGJhbmtydXB0Y3lfcmlza1tjb21iaW5lZF9yYW5raW5nJHJpc2tfY2F0ZWdvcnkgPT0gIkhpZ2ggUmlzayAoQm90aCkiXSwNCiAgICAgICAgIGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlza1tjb21iaW5lZF9yYW5raW5nJHJpc2tfY2F0ZWdvcnkgPT0gIkhpZ2ggUmlzayAoQm90aCkiXSwNCiAgICAgICAgIHBjaCA9IDE2LCBjb2wgPSBhZGp1c3Rjb2xvcigicHVycGxlIiwgYWxwaGEuZiA9IDAuNikpDQogIA0KICAjIEFkZCBsaW5lcyB0byBkZW1hcmNhdGUgcmlzayBxdWFkcmFudHMNCiAgYWJsaW5lKHYgPSBiYW5rcnVwdGN5X3RocmVzaG9sZCwgbHR5ID0gMiwgY29sID0gImRhcmtncmF5IikNCiAgYWJsaW5lKGggPSBhY3F1aXNpdGlvbl90aHJlc2hvbGQsIGx0eSA9IDIsIGNvbCA9ICJkYXJrZ3JheSIpDQogIA0KICAjIEFkZCBxdWFkcmFudCBsYWJlbHMNCiAgdGV4dChtYXgoY29tYmluZWRfcmFua2luZyRiYW5rcnVwdGN5X3Jpc2spICogMC4yNSwgDQogICAgICAgbWF4KGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlzaykgKiAwLjI1LCANCiAgICAgICAiTG93IFJpc2sgWm9uZSIsIGNvbCA9ICJibHVlIiwgY2V4ID0gMS4yKQ0KICANCiAgdGV4dChtYXgoY29tYmluZWRfcmFua2luZyRiYW5rcnVwdGN5X3Jpc2spICogMC43NSwgDQogICAgICAgbWF4KGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlzaykgKiAwLjI1LCANCiAgICAgICAiQmFua3J1cHRjeSBSaXNrIFpvbmUiLCBjb2wgPSAicmVkIiwgY2V4ID0gMS4yKQ0KICANCiAgdGV4dChtYXgoY29tYmluZWRfcmFua2luZyRiYW5rcnVwdGN5X3Jpc2spICogMC4yNSwgDQogICAgICAgbWF4KGNvbWJpbmVkX3JhbmtpbmckYWNxdWlzaXRpb25fcmlzaykgKiAwLjc1LCANCiAgICAgICAiQWNxdWlzaXRpb24gUmlzayBab25lIiwgY29sID0gImdyZWVuNCIsIGNleCA9IDEuMikNCiAgDQogIHRleHQobWF4KGNvbWJpbmVkX3JhbmtpbmckYmFua3J1cHRjeV9yaXNrKSAqIDAuNzUsIA0KICAgICAgIG1heChjb21iaW5lZF9yYW5raW5nJGFjcXVpc2l0aW9uX3Jpc2spICogMC43NSwgDQogICAgICAgIkhpZ2ggUmlzayBab25lIiwgY29sID0gInB1cnBsZSIsIGNleCA9IDEuMikNCiAgDQogICMgQWRkIGNvcnJlbGF0aW9uIGluZm9ybWF0aW9uDQogIHJpc2tfY29ycmVsYXRpb24gPC0gY29yKGNvbWJpbmVkX3JhbmtpbmckYmFua3J1cHRjeV9yaXNrLCANCiAgICAgICAgICAgICAgICAgICAgICAgY29tYmluZWRfcmFua2luZyRhY3F1aXNpdGlvbl9yaXNrKQ0KICANCiAgbXRleHQocGFzdGUoIkNvcnJlbGF0aW9uIGJldHdlZW4gcmlzayB0eXBlczoiLCByb3VuZChyaXNrX2NvcnJlbGF0aW9uLCAzKSksIA0KICAgICAgICBzaWRlID0gMywgbGluZSA9IDAuNSwgY2V4ID0gMC44KQ0KICANCiAgIyBSZXR1cm4gdGhlIGNvbWJpbmVkIHJhbmtpbmcgd2l0aCByaXNrIGNhdGVnb3JpZXMNCiAgcmV0dXJuKGNvbWJpbmVkX3JhbmtpbmcpDQp9DQoNCiMgRXhlY3V0ZSB0aGUgcmlzayBwcm9maWxlIGFuYWx5c2lzDQpyaXNrX3Byb2ZpbGVzIDwtIGFuYWx5emVfcmlza19wcm9maWxlcyhjb21wYW55X3JhbmtpbmdzJGJhbmtydXB0Y3lfcmFua2luZywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYW55X3JhbmtpbmdzJGFjcXVpc2l0aW9uX3JhbmtpbmcpDQpgYGANCg0KDQpXZSBhbHNvIHJhbmsgdGhlIGNvbXBhbmllcyBieSByaXNrIHVzaW5nIHRoZSBDb3ggbW9kZWwgcHJlZGljdGlvbnMuIFRoaXMgaW52b2x2ZXMgY2FsY3VsYXRpbmcgdGhlIHByZWRpY3RlZCByaXNrcyBmb3IgZWFjaCBjb21wYW55IGFuZCBjYXRlZ29yaXppbmcgdGhlbSBpbnRvIGRpZmZlcmVudCByaXNrIGxldmVscy4gV2UgdGhlbiB2aXN1YWxpemUgdGhlIHJpc2sgbGFuZHNjYXBlIHVzaW5nIHNjYXR0ZXIgcGxvdHMgYW5kIGlkZW50aWZ5IHNlY3RvcnMgd2l0aCBoaWdoIHByb3BvcnRpb25zIG9mIGNvbXBhbmllcyBpbiBlYWNoIHJpc2sgY2F0ZWdvcnkuIFRoZSBhbmFseXNpcyBhbHNvIGluY2x1ZGVzIGEgZGlzY3Vzc2lvbiBvZiB0aGUgaW1wbGljYXRpb25zIG9mIHRoZXNlIGZpbmRpbmdzIGZvciBpbnZlc3RvcnMgYW5kIHN0YWtlaG9sZGVycy4gV2UgaW5jbHVkZSB0aW1lIHRpbGwgZXZlbnQgZXN0aW1hdGVzIGZvciBlYWNoIGNvbXBhbnksIHdoaWNoIGNhbiBiZSB1c2VmdWwgZm9yIHJpc2sgbWFuYWdlbWVudCBhbmQgaW52ZXN0bWVudCBkZWNpc2lvbnMuIFRpbWUgaXMgY2FsY3VsYXRlZCB1c2luZyB0aGUgbGluZWFyIHByZWRpY3RvciBmcm9tIHRoZSBDb3ggbW9kZWwgYW5kIHRoZSBtZWRpYW4gdGltZSB0byBldmVudCBmcm9tIHRoZSBkYXRhLiBXZSBhbHNvIGNhcCBleHRyZW1lbHkgbG9uZyBlc3RpbWF0ZXMgYXQgYSByZWFzb25hYmxlIHZhbHVlIChlLmcuLCAxMDAgeWVhcnMpIHRvIGF2b2lkIHVucmVhbGlzdGljIHByZWRpY3Rpb25zLiBUaGUgZmluYWwgb3V0cHV0IGluY2x1ZGVzIGEgcmFua2luZyBvZiBjb21wYW5pZXMgYnkgcmlzaywgYWxvbmcgd2l0aCB0aGVpciBlc3RpbWF0ZWQgcmVtYWluaW5nIHRpbWUgdW50aWwgYmFua3J1cHRjeSBvciBhY3F1aXNpdGlvbi4NCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMTQuMTogUmFuayBDb21wYW5pZXMgYnkgUmlzayAoVXNpbmcgQ294IE1vZGVsIFByZWRpY3Rpb25zKQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG4xNC4xOiBDT01QQU5ZIFJJU0sgUkFOS0lORyBXSVRIIFJFTUFJTklORyBUSU1FIEVTVElNQVRJT05cbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQojIEZ1bmN0aW9uIHRvIHJhbmsgY29tcGFuaWVzIGJ5IHJpc2sgd2l0aCBzdXJ2aXZhbC1iYXNlZCB0aW1lIGVzdGltYXRlcw0KcmFua19jb21wYW5pZXNfYnlfcmlzayA8LSBmdW5jdGlvbigpIHsNCiAgY2F0KCJSYW5raW5nIGNvbXBhbmllcyBieSBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiByaXNrLi4uXG4iKQ0KICANCiAgIyBHZXQgbGFzdCBvYnNlcnZhdGlvbiBmb3IgZWFjaCBjb21wYW55DQogIGNvbXBhbnlfZGF0YSA8LSBkYXRhICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHVuZ3JvdXAoKQ0KICANCiAgIyBDYWxjdWxhdGUgYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gcmlzayBzY29yZXMNCiAgY29tcGFueV9kYXRhJGJhbmtydXB0Y3lfcmlzayA8LSBwcmVkaWN0KGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwsIG5ld2RhdGEgPSBjb21wYW55X2RhdGEsIHR5cGUgPSAicmlzayIpDQogIGNvbXBhbnlfZGF0YSRhY3F1aXNpdGlvbl9yaXNrIDwtIHByZWRpY3QoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwsIG5ld2RhdGEgPSBjb21wYW55X2RhdGEsIHR5cGUgPSAicmlzayIpDQogIA0KICAjIENhbGN1bGF0ZSBwZXJjZW50aWxlIHJhbmtzDQogIGNvbXBhbnlfZGF0YSRiYW5rcnVwdGN5X3BlcmNlbnRpbGUgPC0gcmFuayhjb21wYW55X2RhdGEkYmFua3J1cHRjeV9yaXNrKSAvIG5yb3coY29tcGFueV9kYXRhKSAqIDEwMA0KICBjb21wYW55X2RhdGEkYWNxdWlzaXRpb25fcGVyY2VudGlsZSA8LSByYW5rKGNvbXBhbnlfZGF0YSRhY3F1aXNpdGlvbl9yaXNrKSAvIG5yb3coY29tcGFueV9kYXRhKSAqIDEwMA0KICANCiAgIyBBZGQgcmlzayBjYXRlZ29yaWVzDQogIGNvbXBhbnlfZGF0YSRiYW5rcnVwdGN5X3Jpc2tfY2F0ZWdvcnkgPC0gY3V0KGNvbXBhbnlfZGF0YSRiYW5rcnVwdGN5X3BlcmNlbnRpbGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwgODAsIDkwLCA5NSwgMTAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTG93IiwgIk1vZGVyYXRlIiwgIkhpZ2giLCAiVmVyeSBIaWdoIikpDQogIA0KICBjb21wYW55X2RhdGEkYWNxdWlzaXRpb25fcmlza19jYXRlZ29yeSA8LSBjdXQoY29tcGFueV9kYXRhJGFjcXVpc2l0aW9uX3BlcmNlbnRpbGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsIDgwLCA5MCwgOTUsIDEwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJMb3ciLCAiTW9kZXJhdGUiLCAiSGlnaCIsICJWZXJ5IEhpZ2giKSkNCiAgDQogICMgRXN0aW1hdGUgcmVtYWluaW5nIHRpbWUgdXNpbmcgc3Vydml2YWwgYW5hbHlzaXMgcHJpbmNpcGxlcw0KICBjYXQoIkVzdGltYXRpbmcgcmVtYWluaW5nIHRpbWUgdXNpbmcgc3Vydml2YWwgYW5hbHlzaXMgcHJpbmNpcGxlcy4uLlxuIikNCiAgDQogICMgRm9yIGJhbmtydXB0Y3kgbW9kZWwNCiAgIyBDcmVhdGUgdHlwaWNhbCBjb3ZhcmlhdGUgdmFsdWVzIGZvciByZWZlcmVuY2UgLSBGSVg6IHVzZSBkcGx5ciB2ZXJicyBjb3JyZWN0bHkNCiAgYmFua3J1cHRjeV92YXJzIDwtIG5hbWVzKGNvZWYoZmluYWxfYmFua3J1cHRjeV9tb2RlbCkpDQogIGJhbmtydXB0Y3lfdmFycyA8LSBiYW5rcnVwdGN5X3ZhcnNbIWdyZXBsKCI6dHQiLCBiYW5rcnVwdGN5X3ZhcnMpXQ0KICANCiAgIyBDcmVhdGUgcmVmZXJlbmNlIHZhbHVlcyBtYW51YWxseSBpbnN0ZWFkIG9mIHVzaW5nIGFjcm9zcygpDQogIHJlZmVyZW5jZV9iYW5rcnVwdGN5IDwtIGRhdGEuZnJhbWUobWF0cml4KE5BLCBucm93ID0gMSwgbmNvbCA9IGxlbmd0aChiYW5rcnVwdGN5X3ZhcnMpKSkNCiAgY29sbmFtZXMocmVmZXJlbmNlX2JhbmtydXB0Y3kpIDwtIGJhbmtydXB0Y3lfdmFycw0KICANCiAgZm9yKHZhciBpbiBiYW5rcnVwdGN5X3ZhcnMpIHsNCiAgICBpZih2YXIgJWluJSBjb2xuYW1lcyhjb21wYW55X2RhdGEpKSB7DQogICAgICByZWZlcmVuY2VfYmFua3J1cHRjeVsxLCB2YXJdIDwtIG1lZGlhbihjb21wYW55X2RhdGFbW3Zhcl1dLCBuYS5ybSA9IFRSVUUpDQogICAgfSBlbHNlIHsNCiAgICAgICMgRm9yIHZhcmlhYmxlcyBub3QgaW4gdGhlIGRhdGEgKGNvdWxkIGJlIGludGVyYWN0aW9uIHRlcm1zIG9yIHRyYW5zZm9ybWF0aW9ucykNCiAgICAgIHJlZmVyZW5jZV9iYW5rcnVwdGN5WzEsIHZhcl0gPC0gMA0KICAgIH0NCiAgfQ0KICANCiAgIyBDYWxjdWxhdGUgbGluZWFyIHByZWRpY3RvciBmb3IgcmVmZXJlbmNlIGNvbXBhbnkNCiAgbHBfcmVmZXJlbmNlX2JhbmtydXB0Y3kgPC0gc3VtKGNvZWYoZmluYWxfYmFua3J1cHRjeV9tb2RlbClbYmFua3J1cHRjeV92YXJzXSAqIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5udW1lcmljKHJlZmVyZW5jZV9iYW5rcnVwdGN5WzEsIF0pKQ0KICANCiAgIyBDYWxjdWxhdGUgbGluZWFyIHByZWRpY3RvciBmb3IgZWFjaCBjb21wYW55DQogIGxwX2NvbXBhbnlfYmFua3J1cHRjeSA8LSBwcmVkaWN0KGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwsIG5ld2RhdGEgPSBjb21wYW55X2RhdGEsIHR5cGUgPSAibHAiKQ0KICANCiAgIyBFc3RpbWF0ZSByZWxhdGl2ZSByaXNrIGNvbXBhcmVkIHRvIHJlZmVyZW5jZSBjb21wYW55DQogIHJlbGF0aXZlX3Jpc2tfYmFua3J1cHRjeSA8LSBleHAobHBfY29tcGFueV9iYW5rcnVwdGN5IC0gbHBfcmVmZXJlbmNlX2JhbmtydXB0Y3kpDQogIA0KICAjIFVzZSBhIGRhdGEtZHJpdmVuIGFwcHJvYWNoIHRvIGRldGVybWluZSBtZWRpYW4gcmVmZXJlbmNlIHRpbWUNCiAgIyBCYXNlZCBvbiBldmVudCBkaXN0cmlidXRpb24gaW4gdGhlIGRhdGENCiAgZXZlbnRfdGltZXNfYmFua3J1cHRjeSA8LSBkYXRhICU+JQ0KICAgIGZpbHRlcihiYW5rcnVwdGN5ID09IDEpICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHB1bGwodHN0b3ApDQogIA0KICBpZihsZW5ndGgoZXZlbnRfdGltZXNfYmFua3J1cHRjeSkgPiAwKSB7DQogICAgbWVkaWFuX3JlZmVyZW5jZV90aW1lX2JhbmtydXB0Y3kgPC0gbWVkaWFuKGV2ZW50X3RpbWVzX2JhbmtydXB0Y3kpDQogIH0gZWxzZSB7DQogICAgIyBGYWxsYmFjayBpZiBubyBiYW5rcnVwdGN5IGV2ZW50cw0KICAgIG1lZGlhbl9yZWZlcmVuY2VfdGltZV9iYW5rcnVwdGN5IDwtIDI1DQogIH0NCiAgDQogICMgQ2FsY3VsYXRlIGNvbXBhbnktc3BlY2lmaWMgbWVkaWFuIHRpbWVzDQogIGNvbXBhbnlfZGF0YSRyZW1haW5pbmdfdGltZV90b19iYW5rcnVwdGN5IDwtIG1lZGlhbl9yZWZlcmVuY2VfdGltZV9iYW5rcnVwdGN5IC8gcmVsYXRpdmVfcmlza19iYW5rcnVwdGN5DQogIA0KICAjIEZvciBjb21wYW5pZXMgYWxyZWFkeSBpbiB0aGUgZXZlbnQsIHNldCByZW1haW5pbmcgdGltZSB0byAwDQogIGNvbXBhbnlfZGF0YSRyZW1haW5pbmdfdGltZV90b19iYW5rcnVwdGN5W2NvbXBhbnlfZGF0YSRiYW5rcnVwdGN5ID09IDFdIDwtIDANCiAgDQogICMgQ2FwIGV4dHJlbWVseSBsb25nIGVzdGltYXRlcyBhdCBhIHJlYXNvbmFibGUgdmFsdWUgKGUuZy4sIDEwMCB5ZWFycykNCiAgY29tcGFueV9kYXRhJHJlbWFpbmluZ190aW1lX3RvX2JhbmtydXB0Y3lbY29tcGFueV9kYXRhJHJlbWFpbmluZ190aW1lX3RvX2JhbmtydXB0Y3kgPiAxMDBdIDwtIDEwMA0KICANCiAgIyBTaW1pbGFybHkgZm9yIGFjcXVpc2l0aW9uIG1vZGVsDQogIGFjcXVpc2l0aW9uX3ZhcnMgPC0gbmFtZXMoY29lZihmaW5hbF9hY3F1aXNpdGlvbl9tb2RlbCkpDQogIGFjcXVpc2l0aW9uX3ZhcnMgPC0gYWNxdWlzaXRpb25fdmFyc1shZ3JlcGwoIjp0dCIsIGFjcXVpc2l0aW9uX3ZhcnMpXQ0KICANCiAgIyBDcmVhdGUgcmVmZXJlbmNlIHZhbHVlcyBtYW51YWxseQ0KICByZWZlcmVuY2VfYWNxdWlzaXRpb24gPC0gZGF0YS5mcmFtZShtYXRyaXgoTkEsIG5yb3cgPSAxLCBuY29sID0gbGVuZ3RoKGFjcXVpc2l0aW9uX3ZhcnMpKSkNCiAgY29sbmFtZXMocmVmZXJlbmNlX2FjcXVpc2l0aW9uKSA8LSBhY3F1aXNpdGlvbl92YXJzDQogIA0KICBmb3IodmFyIGluIGFjcXVpc2l0aW9uX3ZhcnMpIHsNCiAgICBpZih2YXIgJWluJSBjb2xuYW1lcyhjb21wYW55X2RhdGEpKSB7DQogICAgICByZWZlcmVuY2VfYWNxdWlzaXRpb25bMSwgdmFyXSA8LSBtZWRpYW4oY29tcGFueV9kYXRhW1t2YXJdXSwgbmEucm0gPSBUUlVFKQ0KICAgIH0gZWxzZSB7DQogICAgICByZWZlcmVuY2VfYWNxdWlzaXRpb25bMSwgdmFyXSA8LSAwDQogICAgfQ0KICB9DQogIA0KICBscF9yZWZlcmVuY2VfYWNxdWlzaXRpb24gPC0gc3VtKGNvZWYoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpW2FjcXVpc2l0aW9uX3ZhcnNdICogDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5udW1lcmljKHJlZmVyZW5jZV9hY3F1aXNpdGlvblsxLCBdKSkNCiAgDQogIGxwX2NvbXBhbnlfYWNxdWlzaXRpb24gPC0gcHJlZGljdChmaW5hbF9hY3F1aXNpdGlvbl9tb2RlbCwgbmV3ZGF0YSA9IGNvbXBhbnlfZGF0YSwgdHlwZSA9ICJscCIpDQogIA0KICByZWxhdGl2ZV9yaXNrX2FjcXVpc2l0aW9uIDwtIGV4cChscF9jb21wYW55X2FjcXVpc2l0aW9uIC0gbHBfcmVmZXJlbmNlX2FjcXVpc2l0aW9uKQ0KICANCiAgIyBHZXQgbWVkaWFuIHRpbWUgZnJvbSBkYXRhDQogIGV2ZW50X3RpbWVzX2FjcXVpc2l0aW9uIDwtIGRhdGEgJT4lDQogICAgZmlsdGVyKGFjcXVpc2l0aW9uID09IDEpICU+JQ0KICAgIGdyb3VwX2J5KGN1c2lwKSAlPiUNCiAgICBzbGljZV9tYXgodHN0b3ApICU+JQ0KICAgIHB1bGwodHN0b3ApDQogIA0KICBpZihsZW5ndGgoZXZlbnRfdGltZXNfYWNxdWlzaXRpb24pID4gMCkgew0KICAgIG1lZGlhbl9yZWZlcmVuY2VfdGltZV9hY3F1aXNpdGlvbiA8LSBtZWRpYW4oZXZlbnRfdGltZXNfYWNxdWlzaXRpb24pDQogIH0gZWxzZSB7DQogICAgbWVkaWFuX3JlZmVyZW5jZV90aW1lX2FjcXVpc2l0aW9uIDwtIDI1DQogIH0NCiAgDQogIGNvbXBhbnlfZGF0YSRyZW1haW5pbmdfdGltZV90b19hY3F1aXNpdGlvbiA8LSBtZWRpYW5fcmVmZXJlbmNlX3RpbWVfYWNxdWlzaXRpb24gLyByZWxhdGl2ZV9yaXNrX2FjcXVpc2l0aW9uDQogIA0KICAjIEZvciBjb21wYW5pZXMgYWxyZWFkeSBhY3F1aXJlZCwgc2V0IHJlbWFpbmluZyB0aW1lIHRvIDANCiAgY29tcGFueV9kYXRhJHJlbWFpbmluZ190aW1lX3RvX2FjcXVpc2l0aW9uW2NvbXBhbnlfZGF0YSRhY3F1aXNpdGlvbiA9PSAxXSA8LSAwDQogIA0KICAjIENhcCBleHRyZW1lbHkgbG9uZyBlc3RpbWF0ZXMNCiAgY29tcGFueV9kYXRhJHJlbWFpbmluZ190aW1lX3RvX2FjcXVpc2l0aW9uW2NvbXBhbnlfZGF0YSRyZW1haW5pbmdfdGltZV90b19hY3F1aXNpdGlvbiA+IDEwMF0gPC0gMTAwDQogIA0KICAjIEFkZCBjb25maWRlbmNlIGFyb3VuZCB0aW1lIGVzdGltYXRlcyAoc2ltcGxpZmllZCBhcHByb2FjaCkNCiAgIyBMb3dlciBhbmQgdXBwZXIgYm91bmRzIGJhc2VkIG9uIGFzc3VtcHRpb24gdGhhdCBoYXphcmQgcmF0aW8gaGFzIMKxMjUlIHVuY2VydGFpbnR5DQogIGNvbXBhbnlfZGF0YSRiYW5rcnVwdGN5X3RpbWVfbG93ZXIgPC0gY29tcGFueV9kYXRhJHJlbWFpbmluZ190aW1lX3RvX2JhbmtydXB0Y3kgLyAxLjI1DQogIGNvbXBhbnlfZGF0YSRiYW5rcnVwdGN5X3RpbWVfdXBwZXIgPC0gY29tcGFueV9kYXRhJHJlbWFpbmluZ190aW1lX3RvX2JhbmtydXB0Y3kgKiAxLjI1DQogIA0KICBjb21wYW55X2RhdGEkYWNxdWlzaXRpb25fdGltZV9sb3dlciA8LSBjb21wYW55X2RhdGEkcmVtYWluaW5nX3RpbWVfdG9fYWNxdWlzaXRpb24gLyAxLjI1DQogIGNvbXBhbnlfZGF0YSRhY3F1aXNpdGlvbl90aW1lX3VwcGVyIDwtIGNvbXBhbnlfZGF0YSRyZW1haW5pbmdfdGltZV90b19hY3F1aXNpdGlvbiAqIDEuMjUNCiAgDQogICMgQWRkICJtb3N0IGxpa2VseSBvdXRjb21lIiBjb2x1bW4NCiAgY29tcGFueV9kYXRhIDwtIGNvbXBhbnlfZGF0YSAlPiUNCiAgICBtdXRhdGUobW9zdF9saWtlbHlfb3V0Y29tZSA9IGNhc2Vfd2hlbigNCiAgICAgIGJhbmtydXB0Y3kgPT0gMSB+ICJBbHJlYWR5IEJhbmtydXB0IiwNCiAgICAgIGFjcXVpc2l0aW9uID09IDEgfiAiQWxyZWFkeSBBY3F1aXJlZCIsDQogICAgICBiYW5rcnVwdGN5X3Jpc2sgPiBhY3F1aXNpdGlvbl9yaXNrICYgcmVtYWluaW5nX3RpbWVfdG9fYmFua3J1cHRjeSA8IHJlbWFpbmluZ190aW1lX3RvX2FjcXVpc2l0aW9uIH4gIkJhbmtydXB0Y3kiLA0KICAgICAgYWNxdWlzaXRpb25fcmlzayA+IGJhbmtydXB0Y3lfcmlzayAmIHJlbWFpbmluZ190aW1lX3RvX2FjcXVpc2l0aW9uIDwgcmVtYWluaW5nX3RpbWVfdG9fYmFua3J1cHRjeSB+ICJBY3F1aXNpdGlvbiIsDQogICAgICBUUlVFIH4gIkluY29uY2x1c2l2ZSINCiAgICApKQ0KICANCiAgIyBSZXR1cm4gcmFua2VkIGNvbXBhbnkgZGF0YQ0KICByZXR1cm4oY29tcGFueV9kYXRhKQ0KfQ0KDQojIFJhbmsgY29tcGFuaWVzIGJ5IHJpc2sNCnJhbmtlZF9jb21wYW5pZXMgPC0gcmFua19jb21wYW5pZXNfYnlfcmlzaygpDQoNCiMgRGlzcGxheSB0b3AgY29tcGFuaWVzIGF0IHJpc2sgb2YgYmFua3J1cHRjeQ0KY2F0KCJcblRPUCAyMCBDT01QQU5JRVMgQVQgUklTSyBPRiBCQU5LUlVQVENZOlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KDQp0b3BfYmFua3J1cHRjeSA8LSByYW5rZWRfY29tcGFuaWVzICU+JQ0KICBmaWx0ZXIoYmFua3J1cHRjeSA9PSAwKSAlPiUgICMgT25seSBpbmNsdWRlIGNvbXBhbmllcyBub3QgeWV0IGJhbmtydXB0DQogIGFycmFuZ2UoZGVzYyhiYW5rcnVwdGN5X3Jpc2spKSAlPiUNCiAgaGVhZCgyMCkNCg0KIyBDcmVhdGUgYSBkYXNoYm9hcmQtc3R5bGUgZGlzcGxheQ0KYmFua3J1cHRjeV9kaXNwbGF5IDwtIGRhdGEuZnJhbWUoDQogIFJhbmsgPSAxOm5yb3codG9wX2JhbmtydXB0Y3kpLA0KICBDVVNJUCA9IHRvcF9iYW5rcnVwdGN5JGN1c2lwLA0KICBJbmR1c3RyeSA9IHRvcF9iYW5rcnVwdGN5JGdzZWN0b3IsDQogICJSaXNrIFNjb3JlIiA9IHJvdW5kKHRvcF9iYW5rcnVwdGN5JGJhbmtydXB0Y3lfcmlzaywgNCksDQogICJSaXNrIFBlcmNlbnRpbGUiID0gcGFzdGUwKHJvdW5kKHRvcF9iYW5rcnVwdGN5JGJhbmtydXB0Y3lfcGVyY2VudGlsZSwgMSksICIlIiksDQogICJFc3QuIFJlbWFpbmluZyBZZWFycyIgPSByb3VuZCh0b3BfYmFua3J1cHRjeSRyZW1haW5pbmdfdGltZV90b19iYW5rcnVwdGN5LCAxKSwNCiAgIlRpbWUgUmFuZ2UiID0gcGFzdGUwKCJbIiwgcm91bmQodG9wX2JhbmtydXB0Y3kkYmFua3J1cHRjeV90aW1lX2xvd2VyLCAxKSwgIi0iLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHRvcF9iYW5rcnVwdGN5JGJhbmtydXB0Y3lfdGltZV91cHBlciwgMSksICJdIiksDQogICJDdXJyZW50IExpZmV0aW1lIiA9IHJvdW5kKHRvcF9iYW5rcnVwdGN5JHRzdG9wLCAxKSwNCiAgIlJpc2sgQ2F0ZWdvcnkiID0gYXMuY2hhcmFjdGVyKHRvcF9iYW5rcnVwdGN5JGJhbmtydXB0Y3lfcmlza19jYXRlZ29yeSksDQogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KKQ0KDQpwcmludChiYW5rcnVwdGN5X2Rpc3BsYXksIHJvdy5uYW1lcyA9IEZBTFNFKQ0KDQojIERpc3BsYXkgdG9wIGNvbXBhbmllcyBsaWtlbHkgdG8gYmUgYWNxdWlyZWQNCmNhdCgiXG5UT1AgMjAgQ09NUEFOSUVTIExJS0VMWSBUTyBCRSBBQ1FVSVJFRDpcbiIpDQpjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KdG9wX2FjcXVpc2l0aW9uIDwtIHJhbmtlZF9jb21wYW5pZXMgJT4lDQogIGZpbHRlcihhY3F1aXNpdGlvbiA9PSAwKSAlPiUgICMgT25seSBpbmNsdWRlIGNvbXBhbmllcyBub3QgeWV0IGFjcXVpcmVkDQogIGFycmFuZ2UoZGVzYyhhY3F1aXNpdGlvbl9yaXNrKSkgJT4lDQogIGhlYWQoMjApDQoNCiMgQ3JlYXRlIGEgZGFzaGJvYXJkLXN0eWxlIGRpc3BsYXkNCmFjcXVpc2l0aW9uX2Rpc3BsYXkgPC0gZGF0YS5mcmFtZSgNCiAgUmFuayA9IDE6bnJvdyh0b3BfYWNxdWlzaXRpb24pLA0KICBDVVNJUCA9IHRvcF9hY3F1aXNpdGlvbiRjdXNpcCwNCiAgSW5kdXN0cnkgPSB0b3BfYWNxdWlzaXRpb24kZ3NlY3RvciwNCiAgIlJpc2sgU2NvcmUiID0gcm91bmQodG9wX2FjcXVpc2l0aW9uJGFjcXVpc2l0aW9uX3Jpc2ssIDQpLA0KICAiUmlzayBQZXJjZW50aWxlIiA9IHBhc3RlMChyb3VuZCh0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fcGVyY2VudGlsZSwgMSksICIlIiksDQogICJFc3QuIFJlbWFpbmluZyBZZWFycyIgPSByb3VuZCh0b3BfYWNxdWlzaXRpb24kcmVtYWluaW5nX3RpbWVfdG9fYWNxdWlzaXRpb24sIDEpLA0KICAiVGltZSBSYW5nZSIgPSBwYXN0ZTAoIlsiLCByb3VuZCh0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fdGltZV9sb3dlciwgMSksICItIiwgDQogICAgICAgICAgICAgICAgICAgICAgICByb3VuZCh0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fdGltZV91cHBlciwgMSksICJdIiksDQogICJDdXJyZW50IExpZmV0aW1lIiA9IHJvdW5kKHRvcF9hY3F1aXNpdGlvbiR0c3RvcCwgMSksDQogICJSaXNrIENhdGVnb3J5IiA9IGFzLmNoYXJhY3Rlcih0b3BfYWNxdWlzaXRpb24kYWNxdWlzaXRpb25fcmlza19jYXRlZ29yeSksDQogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KKQ0KDQpwcmludChhY3F1aXNpdGlvbl9kaXNwbGF5LCByb3cubmFtZXMgPSBGQUxTRSkNCg0KIyBDb21wYW5pZXMgd2l0aCBjb21wZXRpbmcgcmlza3MgKGF0IGhpZ2ggcmlzayBmb3IgYm90aCBvdXRjb21lcykNCmNhdCgiXG5DT01QQU5JRVMgQVQgSElHSCBSSVNLIEZPUiBCT1RIIE9VVENPTUVTOlxuIikNCmNhdCgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCg0KY29tcGV0aW5nX3Jpc2tzIDwtIHJhbmtlZF9jb21wYW5pZXMgJT4lDQogIGZpbHRlcihiYW5rcnVwdGN5ID09IDAgJiBhY3F1aXNpdGlvbiA9PSAwKSAlPiUgICMgT25seSBpbmNsdWRlIGFjdGl2ZSBjb21wYW5pZXMNCiAgZmlsdGVyKGJhbmtydXB0Y3lfcGVyY2VudGlsZSA+IDkwICYgYWNxdWlzaXRpb25fcGVyY2VudGlsZSA+IDkwKSAlPiUgICMgSGlnaCByaXNrIGZvciBib3RoDQogIGFycmFuZ2UoZGVzYyhiYW5rcnVwdGN5X3Jpc2sgKyBhY3F1aXNpdGlvbl9yaXNrKSkgJT4lDQogIGhlYWQoMTApDQoNCmlmKG5yb3coY29tcGV0aW5nX3Jpc2tzKSA+IDApIHsNCiAgY29tcGV0aW5nX2Rpc3BsYXkgPC0gZGF0YS5mcmFtZSgNCiAgICBDVVNJUCA9IGNvbXBldGluZ19yaXNrcyRjdXNpcCwNCiAgICBJbmR1c3RyeSA9IGNvbXBldGluZ19yaXNrcyRnc2VjdG9yLA0KICAgICJCLVJpc2sgJSIgPSBwYXN0ZTAocm91bmQoY29tcGV0aW5nX3Jpc2tzJGJhbmtydXB0Y3lfcGVyY2VudGlsZSwgMSksICIlIiksDQogICAgIkEtUmlzayAlIiA9IHBhc3RlMChyb3VuZChjb21wZXRpbmdfcmlza3MkYWNxdWlzaXRpb25fcGVyY2VudGlsZSwgMSksICIlIiksDQogICAgIkItWWVhcnMiID0gcm91bmQoY29tcGV0aW5nX3Jpc2tzJHJlbWFpbmluZ190aW1lX3RvX2JhbmtydXB0Y3ksIDEpLA0KICAgICJBLVllYXJzIiA9IHJvdW5kKGNvbXBldGluZ19yaXNrcyRyZW1haW5pbmdfdGltZV90b19hY3F1aXNpdGlvbiwgMSksDQogICAgIk1vc3QgTGlrZWx5IiA9IGNvbXBldGluZ19yaXNrcyRtb3N0X2xpa2VseV9vdXRjb21lLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIA0KICBwcmludChjb21wZXRpbmdfZGlzcGxheSwgcm93Lm5hbWVzID0gRkFMU0UpDQp9IGVsc2Ugew0KICBjYXQoIk5vIGNvbXBhbmllcyBmb3VuZCBhdCBoaWdoIHJpc2sgZm9yIGJvdGggb3V0Y29tZXMuXG4iKQ0KfQ0KYGBgDQoNCkZpbmFsbHkgd2UgbG9vayBhdCBpbmR1c3RyeSBzcGVjaWZpYyBlZmZlY3RzIG9mIGZpbmFuY2lhbCB2YXJpYWJsZXMgb24gYmFua3J1cHRjeSBhbmQgYWNxdWlzaXRpb24gcmlza3MuIFRoaXMgaW52b2x2ZXMgYW5hbHl6aW5nIHRoZSBjb2VmZmljaWVudHMgb2YgdGhlIENveCBtb2RlbHMgZm9yIGRpZmZlcmVudCBzZWN0b3JzIGFuZCBpZGVudGlmeWluZyBhbnkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgaW4gdGhlIGVmZmVjdHMgb2YgZmluYW5jaWFsIHZhcmlhYmxlcyBhY3Jvc3MgaW5kdXN0cmllcy4gV2UgYWxzbyB2aXN1YWxpemUgdGhlc2UgZWZmZWN0cyB1c2luZyBwbG90cyB0byBpbGx1c3RyYXRlIGhvdyB0aGUgaW1wYWN0IG9mIGZpbmFuY2lhbCB2YXJpYWJsZXMgdmFyaWVzIGJ5IHNlY3Rvci4gVGhpcyBhbmFseXNpcyBjYW4gcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHdoaWNoIGZpbmFuY2lhbCBmYWN0b3JzIGFyZSBtb3N0IHByZWRpY3RpdmUgb2YgYmFua3J1cHRjeSBvciBhY3F1aXNpdGlvbiByaXNrIGluIGRpZmZlcmVudCBpbmR1c3RyaWVzLCBoZWxwaW5nIGludmVzdG9ycyBhbmQgc3Rha2Vob2xkZXJzIG1ha2UgaW5mb3JtZWQgZGVjaXNpb25zLg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBTVEVQIDE1OiBJbmR1c3RyeS1TcGVjaWZpYyBGaW5hbmNpYWwgVmFyaWFibGUgRWZmZWN0cyBBbmFseXNpcw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmNhdCgiXG49PT0gU1RFUCAxNTogSU5EVVNUUlktU1BFQ0lGSUMgRklOQU5DSUFMIFZBUklBQkxFIEVGRkVDVFMgQU5BTFlTSVMgPT09XG4iKQ0KDQphbmFseXplX2luZHVzdHJ5X2VmZmVjdHMgPC0gZnVuY3Rpb24oKSB7DQogIGNhdCgiXG4xNS4xOiBJTkRVU1RSWS1TUEVDSUZJQyBWQVJJQUJMRSBFRkZFQ1RTXG4iKQ0KICBjYXQoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQ0KICANCiAgIyBHZXQgdW5pcXVlIGluZHVzdHJ5IHNlY3RvcnMNCiAgc2VjdG9ycyA8LSB1bmlxdWUoZGF0YSRnc2VjdG9yKQ0KICBjYXQoIkFuYWx5emluZyB2YXJpYWJsZSBlZmZlY3RzIGFjcm9zcyIsIGxlbmd0aChzZWN0b3JzKSwgImluZHVzdHJ5IHNlY3RvcnMuLi5cblxuIikNCiAgDQogICMgSWRlbnRpZnkgYWxsIG5vbi1tYWNybyB2YXJpYWJsZXMgd2l0aCBzaWduaWZpY2FudCBlZmZlY3RzDQogIGJhbmtydXB0Y3lfdmFycyA8LSBuYW1lcyhjb2VmKGZpbmFsX2JhbmtydXB0Y3lfbW9kZWwpKQ0KICBhY3F1aXNpdGlvbl92YXJzIDwtIG5hbWVzKGNvZWYoZmluYWxfYWNxdWlzaXRpb25fbW9kZWwpKQ0KICANCiAgIyBSZW1vdmUgbWFjcm8gdmFyaWFibGVzIGFuZCB0aGVpciB0aW1lIGludGVyYWN0aW9ucw0KICBtYWNyb192YXJzIDwtIGMoImdkcF9ncm93dGgiLCAiZ2RwX2RlZmxhdG9yIiwgInVuZW1wbG95ZW1lbnQiKQ0KICBiYW5rcnVwdGN5X3ZhcnMgPC0gYmFua3J1cHRjeV92YXJzWyFzYXBwbHkoYmFua3J1cHRjeV92YXJzLCBmdW5jdGlvbih2KSB7DQogICAgYmFzZV92YXIgPC0gc3ViKCI6dHQiLCAiIiwgdikNCiAgICByZXR1cm4oYmFzZV92YXIgJWluJSBtYWNyb192YXJzKQ0KICB9KV0NCiAgDQogIGFjcXVpc2l0aW9uX3ZhcnMgPC0gYWNxdWlzaXRpb25fdmFyc1shc2FwcGx5KGFjcXVpc2l0aW9uX3ZhcnMsIGZ1bmN0aW9uKHYpIHsNCiAgICBiYXNlX3ZhciA8LSBzdWIoIjp0dCIsICIiLCB2KQ0KICAgIHJldHVybihiYXNlX3ZhciAlaW4lIG1hY3JvX3ZhcnMpDQogIH0pXQ0KICANCiAgIyBJZGVudGlmeSB0aW1lLXZhcnlpbmcgZWZmZWN0cyBmb3IgZmluYW5jaWFsIHZhcmlhYmxlcw0KICBiYW5rcnVwdGN5X3RpbWVfdmFycyA8LSBncmVwKCI6dHQiLCBiYW5rcnVwdGN5X3ZhcnMsIHZhbHVlID0gVFJVRSkNCiAgYWNxdWlzaXRpb25fdGltZV92YXJzIDwtIGdyZXAoIjp0dCIsIGFjcXVpc2l0aW9uX3ZhcnMsIHZhbHVlID0gVFJVRSkNCiAgDQogIGlmKGxlbmd0aChiYW5rcnVwdGN5X3RpbWVfdmFycykgPT0gMCAmJiBsZW5ndGgoYWNxdWlzaXRpb25fdGltZV92YXJzKSA9PSAwKSB7DQogICAgY2F0KCJObyB0aW1lLXZhcnlpbmcgZWZmZWN0cyBmb3VuZCBmb3IgZmluYW5jaWFsIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWxzLlxuIikNCiAgICByZXR1cm4oTlVMTCkNCiAgfQ0KICANCiAgIyBTdG9yZSByZXN1bHRzDQogIGluZHVzdHJ5X3Jlc3VsdHMgPC0gbGlzdCgpDQogIA0KICAjIDEuIFRlc3QgdGltZS12YXJ5aW5nIGVmZmVjdHMgZm9yIGZpbmFuY2lhbCB2YXJpYWJsZXMgYnkgaW5kdXN0cnkNCiAgY2F0KCJBLiBURVNUSU5HIEZJTkFOQ0lBTCBWQVJJQUJMRSBUSU1FLVZBUllJTkcgRUZGRUNUUyBCWSBJTkRVU1RSWVxuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4iKQ0KICANCiAgIyBGdW5jdGlvbiB0byB0ZXN0IGluZHVzdHJ5LXNwZWNpZmljIGVmZmVjdHMNCiAgdGVzdF9pbmR1c3RyeV9lZmZlY3RzIDwtIGZ1bmN0aW9uKG1vZGVsX25hbWUpIHsNCiAgICBjYXQobW9kZWxfbmFtZSwgIk1vZGVsOlxuXG4iKQ0KICAgIA0KICAgICMgVXNlIHRoZSBhcHByb3ByaWF0ZSBtb2RlbA0KICAgIG1vZGVsIDwtIGlmKG1vZGVsX25hbWUgPT0gIkJhbmtydXB0Y3kiKSBmaW5hbF9iYW5rcnVwdGN5X21vZGVsIGVsc2UgZmluYWxfYWNxdWlzaXRpb25fbW9kZWwNCiAgICBtb2RlbF92YXJzIDwtIGlmKG1vZGVsX25hbWUgPT0gIkJhbmtydXB0Y3kiKSBiYW5rcnVwdGN5X3ZhcnMgZWxzZSBhY3F1aXNpdGlvbl92YXJzDQogICAgbW9kZWxfdGltZV92YXJzIDwtIGlmKG1vZGVsX25hbWUgPT0gIkJhbmtydXB0Y3kiKSBiYW5rcnVwdGN5X3RpbWVfdmFycyBlbHNlIGFjcXVpc2l0aW9uX3RpbWVfdmFycw0KICAgIGV2ZW50X3R5cGUgPC0gdG9sb3dlcihtb2RlbF9uYW1lKQ0KICAgIA0KICAgIGlmKGxlbmd0aChtb2RlbF90aW1lX3ZhcnMpID09IDApIHsNCiAgICAgIGNhdCgiTm8gdGltZS12YXJ5aW5nIGVmZmVjdHMgZm91bmQgZm9yIGZpbmFuY2lhbCB2YXJpYWJsZXMgaW4gdGhpcyBtb2RlbC5cblxuIikNCiAgICAgIHJldHVybihOVUxMKQ0KICAgIH0NCiAgICANCiAgICAjIEdldCBtYWluIHZhcmlhYmxlIG5hbWVzIGZyb20gdGltZSBpbnRlcmFjdGlvbnMNCiAgICBtYWluX3ZhcnMgPC0gc3ViKCI6dHQiLCAiIiwgbW9kZWxfdGltZV92YXJzKQ0KICAgIA0KICAgICMgUmVzdWx0cyBzdG9yYWdlDQogICAgaW5kdXN0cnlfdmFyX3Jlc3VsdHMgPC0gZGF0YS5mcmFtZSgNCiAgICAgIFZhcmlhYmxlID0gY2hhcmFjdGVyKCksDQogICAgICBTZWN0b3IgPSBjaGFyYWN0ZXIoKSwNCiAgICAgIE1haW5fQ29lZiA9IG51bWVyaWMoKSwNCiAgICAgIFRpbWVfQ29lZiA9IG51bWVyaWMoKSwNCiAgICAgIEluaXRpYWxfSFIgPSBudW1lcmljKCksDQogICAgICBIUl9ZZWFyMTAgPSBudW1lcmljKCksDQogICAgICBFZmZlY3RfUGF0dGVybiA9IGNoYXJhY3RlcigpLA0KICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQogICAgKQ0KICAgIA0KICAgICMgRm9yIGVhY2ggbWFqb3Igc2VjdG9yLCBmaXQgYSBzZWN0b3Itc3BlY2lmaWMgbW9kZWwNCiAgICBmb3Ioc2VjdG9yIGluIHNlY3RvcnMpIHsNCiAgICAgICMgR2V0IG9ubHkgZGF0YSBmb3IgdGhpcyBzZWN0b3INCiAgICAgIHNlY3Rvcl9kYXRhIDwtIGRhdGEgJT4lIA0KICAgICAgICBmaWx0ZXIoZ3NlY3RvciA9PSBzZWN0b3IpDQogICAgICANCiAgICAgICMgQ2hlY2sgaWYgZW5vdWdoIGRhdGENCiAgICAgIGlmKG5yb3coc2VjdG9yX2RhdGEpIDwgMTAwKSB7DQogICAgICAgIGNhdCgiICBTZWN0b3IiLCBzZWN0b3IsICJoYXMgaW5zdWZmaWNpZW50IGRhdGEgKG4gPSIsIG5yb3coc2VjdG9yX2RhdGEpLCAiKS5cbiIpDQogICAgICAgIG5leHQNCiAgICAgIH0NCiAgICAgIA0KICAgICAgIyBUcnkgdG8gZml0IHNlY3Rvci1zcGVjaWZpYyBtb2RlbCB3aXRoIG9ubHkgZmluYW5jaWFsIHZhcmlhYmxlcw0KICAgICAgdHJ5Q2F0Y2goew0KICAgICAgICAjIENyZWF0ZSBmb3JtdWxhIGZvY3VzaW5nIG9uIGZpbmFuY2lhbCB0aW1lIGludGVyYWN0aW9ucw0KICAgICAgICBmb3JtdWxhX3N0ciA8LSBwYXN0ZTAoIlN1cnYodHN0YXJ0LCB0c3RvcCwgIiwgZXZlbnRfdHlwZSwgIikgfiAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKG1haW5fdmFycywgY29sbGFwc2UgPSAiICsgIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiICsgIiwgcGFzdGUobW9kZWxfdGltZV92YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSkNCiAgICAgICAgDQogICAgICAgIHNlY3Rvcl9tb2RlbCA8LSBjb3hwaChhcy5mb3JtdWxhKGZvcm11bGFfc3RyKSwgZGF0YSA9IHNlY3Rvcl9kYXRhLCB0aWVzID0gImVmcm9uIikNCiAgICAgICAgDQogICAgICAgICMgRXh0cmFjdCBjb2VmZmljaWVudHMgZm9yIHRpbWUgdmFyaWFibGVzDQogICAgICAgIGZvcihpIGluIDE6bGVuZ3RoKG1haW5fdmFycykpIHsNCiAgICAgICAgICB2YXIgPC0gbWFpbl92YXJzW2ldDQogICAgICAgICAgdGltZV92YXIgPC0gbW9kZWxfdGltZV92YXJzW2ldDQogICAgICAgICAgDQogICAgICAgICAgaWYodmFyICVpbiUgbmFtZXMoY29lZihzZWN0b3JfbW9kZWwpKSAmJiB0aW1lX3ZhciAlaW4lIG5hbWVzKGNvZWYoc2VjdG9yX21vZGVsKSkpIHsNCiAgICAgICAgICAgIG1haW5fY29lZiA8LSBjb2VmKHNlY3Rvcl9tb2RlbClbdmFyXQ0KICAgICAgICAgICAgdGltZV9jb2VmIDwtIGNvZWYoc2VjdG9yX21vZGVsKVt0aW1lX3Zhcl0NCiAgICAgICAgICAgIA0KICAgICAgICAgICAgIyBDYWxjdWxhdGUgaGF6YXJkIHJhdGlvcw0KICAgICAgICAgICAgaW5pdGlhbF9ociA8LSBleHAobWFpbl9jb2VmKQ0KICAgICAgICAgICAgaHJfeWVhcjEwIDwtIGV4cChtYWluX2NvZWYgKyB0aW1lX2NvZWYgKiAxMCkNCiAgICAgICAgICAgIA0KICAgICAgICAgICAgIyBEZXRlcm1pbmUgZWZmZWN0IHBhdHRlcm4NCiAgICAgICAgICAgIGluaXRpYWxfZWZmZWN0IDwtIGlmZWxzZShtYWluX2NvZWYgPiAwLCAicmlzay1pbmNyZWFzaW5nIiwgInJpc2stZGVjcmVhc2luZyIpDQogICAgICAgICAgICBpZihzaWduKG1haW5fY29lZikgPT0gc2lnbih0aW1lX2NvZWYpKSB7DQogICAgICAgICAgICAgIGVmZmVjdF9wYXR0ZXJuIDwtIHBhc3RlMChpbml0aWFsX2VmZmVjdCwgIiBlZmZlY3Qgc3RyZW5ndGhlbnMiKQ0KICAgICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgICAgZWZmZWN0X3BhdHRlcm4gPC0gcGFzdGUwKGluaXRpYWxfZWZmZWN0LCAiIGVmZmVjdCB3ZWFrZW5zIikNCiAgICAgICAgICAgIH0NCiAgICAgICAgICAgIA0KICAgICAgICAgICAgIyBBZGQgdG8gcmVzdWx0cw0KICAgICAgICAgICAgaW5kdXN0cnlfdmFyX3Jlc3VsdHMgPC0gcmJpbmQoaW5kdXN0cnlfdmFyX3Jlc3VsdHMsIGRhdGEuZnJhbWUoDQogICAgICAgICAgICAgIFZhcmlhYmxlID0gdmFyLA0KICAgICAgICAgICAgICBTZWN0b3IgPSBhcy5jaGFyYWN0ZXIoc2VjdG9yKSwNCiAgICAgICAgICAgICAgTWFpbl9Db2VmID0gbWFpbl9jb2VmLA0KICAgICAgICAgICAgICBUaW1lX0NvZWYgPSB0aW1lX2NvZWYsDQogICAgICAgICAgICAgIEluaXRpYWxfSFIgPSBpbml0aWFsX2hyLA0KICAgICAgICAgICAgICBIUl9ZZWFyMTAgPSBocl95ZWFyMTAsDQogICAgICAgICAgICAgIEVmZmVjdF9QYXR0ZXJuID0gZWZmZWN0X3BhdHRlcm4sDQogICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICAgICAgICAgICAgKSkNCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgICAgDQogICAgICAgIGNhdCgiICBTZWN0b3IiLCBzZWN0b3IsICJtb2RlbCBmaXR0ZWQgc3VjY2Vzc2Z1bGx5LlxuIikNCiAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICAgICBjYXQoIiAgRXJyb3IgZml0dGluZyBtb2RlbCBmb3Igc2VjdG9yIiwgc2VjdG9yLCAiOiIsIGNvbmRpdGlvbk1lc3NhZ2UoZSksICJcbiIpDQogICAgICB9KQ0KICAgIH0NCiAgICANCiAgICBpZihucm93KGluZHVzdHJ5X3Zhcl9yZXN1bHRzKSA+IDApIHsNCiAgICAgIGNhdCgiXG4gIEluZHVzdHJ5LXNwZWNpZmljIHRpbWUgcGF0dGVybnMgZm9yIGZpbmFuY2lhbCB2YXJpYWJsZXMgaW4iLCBtb2RlbF9uYW1lLCAibW9kZWw6XG5cbiIpDQogICAgICANCiAgICAgICMgQW5hbHl6ZSBlYWNoIHZhcmlhYmxlIGFjcm9zcyBzZWN0b3JzDQogICAgICBmb3IodmFyIGluIHVuaXF1ZShpbmR1c3RyeV92YXJfcmVzdWx0cyRWYXJpYWJsZSkpIHsNCiAgICAgICAgY2F0KCIgIFZhcmlhYmxlOiIsIHZhciwgIlxuIikNCiAgICAgICAgdmFyX3Jlc3VsdHMgPC0gaW5kdXN0cnlfdmFyX3Jlc3VsdHMgJT4lIGZpbHRlcihWYXJpYWJsZSA9PSB2YXIpDQogICAgICAgIA0KICAgICAgICAjIENoZWNrIGZvciBkaXZlcnNpdHkgaW4gcGF0dGVybnMNCiAgICAgICAgcGF0dGVybnMgPC0gdGFibGUodmFyX3Jlc3VsdHMkRWZmZWN0X1BhdHRlcm4pDQogICAgICAgIGNhdCgiICBFZmZlY3QgcGF0dGVybnMgYWNyb3NzIHNlY3RvcnM6XG4iKQ0KICAgICAgICBmb3IocCBpbiBuYW1lcyhwYXR0ZXJucykpIHsNCiAgICAgICAgICBjYXQoIiAgICAtIiwgcCwgIjoiLCBwYXR0ZXJuc1twXSwgInNlY3RvcnNcbiIpDQogICAgICAgIH0NCiAgICAgICAgDQogICAgICAgICMgU2hvdyBzcGVjaWZpYyBzZWN0b3IgZWZmZWN0cw0KICAgICAgICBjYXQoIiAgU2VjdG9yLXNwZWNpZmljIGVmZmVjdHM6XG4iKQ0KICAgICAgICBmb3IoaSBpbiAxOm5yb3codmFyX3Jlc3VsdHMpKSB7DQogICAgICAgICAgY2F0KHNwcmludGYoIiAgICAtIFNlY3RvciAlczogSW5pdGlhbCBIUiA9ICUuMmYsIEhSIGF0IDEwIHllYXJzID0gJS4yZiAoJXMpXG4iLA0KICAgICAgICAgICAgICAgICAgICAgIHZhcl9yZXN1bHRzJFNlY3RvcltpXSwgdmFyX3Jlc3VsdHMkSW5pdGlhbF9IUltpXSwgDQogICAgICAgICAgICAgICAgICAgICAgdmFyX3Jlc3VsdHMkSFJfWWVhcjEwW2ldLCB2YXJfcmVzdWx0cyRFZmZlY3RfUGF0dGVybltpXSkpDQogICAgICAgIH0NCiAgICAgICAgY2F0KCJcbiIpDQogICAgICB9DQogICAgfSBlbHNlIHsNCiAgICAgIGNhdCgiICBObyBpbmR1c3RyeS1zcGVjaWZpYyByZXN1bHRzIGF2YWlsYWJsZSBmb3IgZmluYW5jaWFsIHZhcmlhYmxlcy5cblxuIikNCiAgICB9DQogICAgDQogICAgcmV0dXJuKGluZHVzdHJ5X3Zhcl9yZXN1bHRzKQ0KICB9DQogIA0KICAjIFRlc3QgZmluYW5jaWFsIHZhcmlhYmxlIGVmZmVjdHMgZm9yIGJvdGggbW9kZWxzDQogIGluZHVzdHJ5X3Jlc3VsdHMkYmFua3J1cHRjeSA8LSB0ZXN0X2luZHVzdHJ5X2VmZmVjdHMoIkJhbmtydXB0Y3kiKQ0KICBpbmR1c3RyeV9yZXN1bHRzJGFjcXVpc2l0aW9uIDwtIHRlc3RfaW5kdXN0cnlfZWZmZWN0cygiQWNxdWlzaXRpb24iKQ0KICANCiAgIyAyLiBDcmVhdGUgdmlzdWFsaXphdGlvbnMgb2YgaW5kdXN0cnktc3BlY2lmaWMgZWZmZWN0cw0KICBjYXQoIlxuMTUuMjogSU5EVVNUUlktU1BFQ0lGSUMgRklOQU5DSUFMIEVGRkVDVFMgVklTVUFMSVpBVElPTlxuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIikNCiAgDQogICMgRnVuY3Rpb24gdG8gY3JlYXRlIHZpc3VhbGl6YXRpb24gZm9yIGEgc2luZ2xlIGZpbmFuY2lhbCB2YXJpYWJsZQ0KICB2aXN1YWxpemVfdmFyaWFibGVfYnlfaW5kdXN0cnkgPC0gZnVuY3Rpb24oaW5kdXN0cnlfcmVzdWx0cywgbW9kZWxfbmFtZSwgdmFyaWFibGUpIHsNCiAgICBpZihpcy5udWxsKGluZHVzdHJ5X3Jlc3VsdHMpIHx8ICF2YXJpYWJsZSAlaW4lIGluZHVzdHJ5X3Jlc3VsdHMkVmFyaWFibGUpIHsNCiAgICAgIGNhdCgiICBObyBkYXRhIGF2YWlsYWJsZSBmb3IiLCB2YXJpYWJsZSwgImluIiwgbW9kZWxfbmFtZSwgIm1vZGVsLlxuIikNCiAgICAgIHJldHVybihOVUxMKQ0KICAgIH0NCiAgICANCiAgICAjIEZpbHRlciBkYXRhIGZvciB0aGlzIHZhcmlhYmxlDQogICAgdmFyX2RhdGEgPC0gaW5kdXN0cnlfcmVzdWx0c1tpbmR1c3RyeV9yZXN1bHRzJFZhcmlhYmxlID09IHZhcmlhYmxlLCBdDQogICAgDQogICAgIyBTZXQgdXAgdGltZSBzZXF1ZW5jZQ0KICAgIHRpbWVzIDwtIHNlcSgwLCAxMCwgYnkgPSAwLjEpDQogICAgDQogICAgIyBDYWxjdWxhdGUgaGF6YXJkIHJhdGlvcyBvdmVyIHRpbWUgZm9yIGVhY2ggc2VjdG9yDQogICAgc2VjdG9ycyA8LSB1bmlxdWUodmFyX2RhdGEkU2VjdG9yKQ0KICAgIGhyX21hdHJpeCA8LSBtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgodGltZXMpLCBuY29sID0gbGVuZ3RoKHNlY3RvcnMpKQ0KICAgIA0KICAgIGZvcihpIGluIDE6bGVuZ3RoKHNlY3RvcnMpKSB7DQogICAgICBzZWN0b3Jfcm93IDwtIHZhcl9kYXRhW3Zhcl9kYXRhJFNlY3RvciA9PSBzZWN0b3JzW2ldLCBdDQogICAgICBpZihucm93KHNlY3Rvcl9yb3cpID4gMCkgew0KICAgICAgICBtYWluX2NvZWYgPC0gc2VjdG9yX3JvdyRNYWluX0NvZWZbMV0NCiAgICAgICAgdGltZV9jb2VmIDwtIHNlY3Rvcl9yb3ckVGltZV9Db2VmWzFdDQogICAgICAgIGhyX21hdHJpeFssIGldIDwtIGV4cChtYWluX2NvZWYgKyB0aW1lX2NvZWYgKiB0aW1lcykNCiAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBDcmVhdGUgcGxvdA0KICAgIG1hdHBsb3QodGltZXMsIGhyX21hdHJpeCwgdHlwZSA9ICJsIiwgbHR5ID0gMTpsZW5ndGgoc2VjdG9ycyksIGNvbCA9IDE6bGVuZ3RoKHNlY3RvcnMpLA0KICAgICAgICAgICAgbWFpbiA9IHBhc3RlKCJFZmZlY3Qgb2YiLCB2YXJpYWJsZSwgImJ5IFNlY3RvciAtIiwgbW9kZWxfbmFtZSwgIk1vZGVsIiksDQogICAgICAgICAgICB4bGFiID0gIlRpbWUgKFllYXJzKSIsIHlsYWIgPSAiSGF6YXJkIFJhdGlvIiwNCiAgICAgICAgICAgIHlsaW0gPSBjKG1pbigwLjUsIG1pbihocl9tYXRyaXgsIG5hLnJtID0gVFJVRSkpLCANCiAgICAgICAgICAgICAgICAgICAgbWF4KDIsIG1heChocl9tYXRyaXgsIG5hLnJtID0gVFJVRSkpKSkNCiAgICANCiAgICAjIEFkZCByZWZlcmVuY2UgbGluZQ0KICAgIGFibGluZShoID0gMSwgbHR5ID0gMiwgY29sID0gImdyYXkiKQ0KICAgIA0KICAgICMgQWRkIGxlZ2VuZA0KICAgIGxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBwYXN0ZSgiU2VjdG9yIiwgc2VjdG9ycyksIA0KICAgICAgICAgICBsdHkgPSAxOmxlbmd0aChzZWN0b3JzKSwgY29sID0gMTpsZW5ndGgoc2VjdG9ycyksIA0KICAgICAgICAgICBjZXggPSAwLjgsIGJ0eSA9ICJuIikNCiAgICANCiAgICByZXR1cm4odmFyX2RhdGEpDQogIH0NCiAgDQogICMgVmlzdWFsaXplIHRvcCBmaW5hbmNpYWwgdmFyaWFibGVzIHdpdGggdGltZSBpbnRlcmFjdGlvbnMNCiAgaWYoIWlzLm51bGwoaW5kdXN0cnlfcmVzdWx0cyRiYW5rcnVwdGN5KSAmJiBucm93KGluZHVzdHJ5X3Jlc3VsdHMkYmFua3J1cHRjeSkgPiAwKSB7DQogICAgdG9wX3ZhcmlhYmxlcyA8LSBuYW1lcyh0YWJsZShpbmR1c3RyeV9yZXN1bHRzJGJhbmtydXB0Y3kkVmFyaWFibGUpKVsxOm1pbigzLCBsZW5ndGgodGFibGUoaW5kdXN0cnlfcmVzdWx0cyRiYW5rcnVwdGN5JFZhcmlhYmxlKSkpXQ0KICAgIA0KICAgIGNhdCgiICBDcmVhdGluZyB2aXN1YWxpemF0aW9ucyBmb3IgdG9wIGZpbmFuY2lhbCB2YXJpYWJsZXMgaW4gYmFua3J1cHRjeSBtb2RlbC4uLlxuIikNCiAgICBmb3IodmFyIGluIHRvcF92YXJpYWJsZXMpIHsNCiAgICAgIHZpc3VhbGl6ZV92YXJpYWJsZV9ieV9pbmR1c3RyeShpbmR1c3RyeV9yZXN1bHRzJGJhbmtydXB0Y3ksICJCYW5rcnVwdGN5IiwgdmFyKQ0KICAgIH0NCiAgfQ0KICANCiAgaWYoIWlzLm51bGwoaW5kdXN0cnlfcmVzdWx0cyRhY3F1aXNpdGlvbikgJiYgbnJvdyhpbmR1c3RyeV9yZXN1bHRzJGFjcXVpc2l0aW9uKSA+IDApIHsNCiAgICB0b3BfdmFyaWFibGVzIDwtIG5hbWVzKHRhYmxlKGluZHVzdHJ5X3Jlc3VsdHMkYWNxdWlzaXRpb24kVmFyaWFibGUpKVsxOm1pbigzLCBsZW5ndGgodGFibGUoaW5kdXN0cnlfcmVzdWx0cyRhY3F1aXNpdGlvbiRWYXJpYWJsZSkpKV0NCiAgICANCiAgICBjYXQoIiAgQ3JlYXRpbmcgdmlzdWFsaXphdGlvbnMgZm9yIHRvcCBmaW5hbmNpYWwgdmFyaWFibGVzIGluIGFjcXVpc2l0aW9uIG1vZGVsLi4uXG4iKQ0KICAgIGZvcih2YXIgaW4gdG9wX3ZhcmlhYmxlcykgew0KICAgICAgdmlzdWFsaXplX3ZhcmlhYmxlX2J5X2luZHVzdHJ5KGluZHVzdHJ5X3Jlc3VsdHMkYWNxdWlzaXRpb24sICJBY3F1aXNpdGlvbiIsIHZhcikNCiAgICB9DQogIH0NCiAgDQogICMgMy4gR2VuZXJhdGUgc2VjdG9yLXNwZWNpZmljIHJlY29tbWVuZGF0aW9ucw0KICBjYXQoIlxuMTUuMzogSU5EVVNUUlktU1BFQ0lGSUMgRklOQU5DSUFMIFJJU0sgRkFDVE9SU1xuIikNCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuIikNCiAgDQogICMgRnVuY3Rpb24gdG8gaWRlbnRpZnkga2V5IGZpbmFuY2lhbCByaXNrIGZhY3RvcnMgYnkgc2VjdG9yDQogIGlkZW50aWZ5X2tleV9yaXNrX2ZhY3RvcnMgPC0gZnVuY3Rpb24oaW5kdXN0cnlfcmVzdWx0cywgbW9kZWxfbmFtZSkgew0KICAgIGlmKGlzLm51bGwoaW5kdXN0cnlfcmVzdWx0cykgfHwgbnJvdyhpbmR1c3RyeV9yZXN1bHRzKSA9PSAwKSB7DQogICAgICBjYXQoIiAgTm8gZGF0YSBhdmFpbGFibGUgZm9yIiwgbW9kZWxfbmFtZSwgIm1vZGVsIHJpc2sgZmFjdG9ycy5cblxuIikNCiAgICAgIHJldHVybihOVUxMKQ0KICAgIH0NCiAgICANCiAgICBjYXQoIktleSBGaW5hbmNpYWwgUmlzayBGYWN0b3JzIGJ5IEluZHVzdHJ5IFNlY3RvciAtIiwgbW9kZWxfbmFtZSwgIk1vZGVsOlxuXG4iKQ0KICAgIA0KICAgICMgUHJvY2VzcyBlYWNoIHNlY3Rvcg0KICAgIGZvcihzZWN0b3IgaW4gdW5pcXVlKGluZHVzdHJ5X3Jlc3VsdHMkU2VjdG9yKSkgew0KICAgICAgc2VjdG9yX2RhdGEgPC0gaW5kdXN0cnlfcmVzdWx0c1tpbmR1c3RyeV9yZXN1bHRzJFNlY3RvciA9PSBzZWN0b3IsIF0NCiAgICAgIA0KICAgICAgY2F0KCIgIEluZHVzdHJ5IFNlY3RvciIsIHNlY3RvciwgIjpcbiIpDQogICAgICANCiAgICAgICMgU29ydCBieSBlZmZlY3Qgc2l6ZSAodXNpbmcgYWJzKGxvZyhIUikpIGF0IHllYXIgMTApDQogICAgICBzZWN0b3JfZGF0YSRlZmZlY3Rfc2l6ZSA8LSBhYnMobG9nKHNlY3Rvcl9kYXRhJEhSX1llYXIxMCkpDQogICAgICBzZWN0b3JfZGF0YSA8LSBzZWN0b3JfZGF0YVtvcmRlcihzZWN0b3JfZGF0YSRlZmZlY3Rfc2l6ZSwgZGVjcmVhc2luZyA9IFRSVUUpLCBdDQogICAgICANCiAgICAgICMgUmVwb3J0IHRvcCByaXNrIGZhY3RvcnMNCiAgICAgIGZvcihpIGluIDE6bWluKDMsIG5yb3coc2VjdG9yX2RhdGEpKSkgew0KICAgICAgICB2YXIgPC0gc2VjdG9yX2RhdGEkVmFyaWFibGVbaV0NCiAgICAgICAgDQogICAgICAgICMgQ3JlYXRlIHJlYWRhYmxlIHZhcmlhYmxlIG5hbWUNCiAgICAgICAgdmFyX25hbWUgPC0gdmFyDQogICAgICAgIGlmKHZhciA9PSAiTFRNVEEiKSB2YXJfbmFtZSA8LSAiTGlhYmlsaXRpZXMtdG8tTWFya2V0IFZhbHVlIg0KICAgICAgICBlbHNlIGlmKHZhciA9PSAiTklNVEEiKSB2YXJfbmFtZSA8LSAiTmV0IEluY29tZS10by1NYXJrZXQgVmFsdWUiDQogICAgICAgIGVsc2UgaWYodmFyID09ICJDQVNITVRBIikgdmFyX25hbWUgPC0gIkNhc2gtdG8tTWFya2V0IFZhbHVlIg0KICAgICAgICBlbHNlIGlmKHZhciA9PSAiUFJJQ0UiKSB2YXJfbmFtZSA8LSAiU3RvY2sgUHJpY2UiDQogICAgICAgIGVsc2UgaWYodmFyID09ICJNQkUiKSB2YXJfbmFtZSA8LSAiTWFya2V0LXRvLUJvb2sgRXF1aXR5Ig0KICAgICAgICBlbHNlIGlmKHZhciA9PSAiUlNJWkUiKSB2YXJfbmFtZSA8LSAiUmVsYXRpdmUgU2l6ZSINCiAgICAgICAgZWxzZSBpZih2YXIgPT0gInpfc2NvcmUiKSB2YXJfbmFtZSA8LSAiQWx0bWFuIFotU2NvcmUiDQogICAgICAgIA0KICAgICAgICBjYXQoIiAgICDigKIgS0VZIEZBQ1RPUjoiLCB2YXJfbmFtZSwgIlxuIikNCiAgICAgICAgY2F0KCIgICAgICBJbml0aWFsIEhSID0iLCByb3VuZChzZWN0b3JfZGF0YSRJbml0aWFsX0hSW2ldLCAyKSwgDQogICAgICAgICAgICAiLCBIUiBhdCBZZWFyIDEwID0iLCByb3VuZChzZWN0b3JfZGF0YSRIUl9ZZWFyMTBbaV0sIDIpLCAiXG4iKQ0KICAgICAgICBjYXQoIiAgICAgIFBhdHRlcm46Iiwgc2VjdG9yX2RhdGEkRWZmZWN0X1BhdHRlcm5baV0sICJcbiIpDQogICAgICAgIA0KICAgICAgICAjIEFkZCBpbnRlcnByZXRhdGlvbg0KICAgICAgICBjYXQoIiAgICAgIEludGVycHJldGF0aW9uOiAiKQ0KICAgICAgICBpZihncmVwbCgicmlzay1pbmNyZWFzaW5nIiwgc2VjdG9yX2RhdGEkRWZmZWN0X1BhdHRlcm5baV0pKSB7DQogICAgICAgICAgY2F0KCJIaWdoZXIiLCB2YXJfbmFtZSwgImlzIGFzc29jaWF0ZWQgd2l0aCBpbmNyZWFzZWQiLCB0b2xvd2VyKG1vZGVsX25hbWUpLCAicmlzayIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCJIaWdoZXIiLCB2YXJfbmFtZSwgImlzIGFzc29jaWF0ZWQgd2l0aCBkZWNyZWFzZWQiLCB0b2xvd2VyKG1vZGVsX25hbWUpLCAicmlzayIpDQogICAgICAgIH0NCiAgICAgICAgDQogICAgICAgIGlmKGdyZXBsKCJzdHJlbmd0aGVucyIsIHNlY3Rvcl9kYXRhJEVmZmVjdF9QYXR0ZXJuW2ldKSkgew0KICAgICAgICAgIGNhdCgiLCBhbmQgdGhpcyBlZmZlY3QgYmVjb21lcyBzdHJvbmdlciBvdmVyIHRpbWVcbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCIsIGJ1dCB0aGlzIGVmZmVjdCBkaW1pbmlzaGVzIG92ZXIgdGltZVxuIikNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgICAgDQogICAgICAjIEFkZCBzZWN0b3Itc3BlY2lmaWMgcmVjb21tZW5kYXRpb24NCiAgICAgIGNhdCgiICAgIOKAoiBTRUNUT1IgUkVDT01NRU5EQVRJT046ICIpDQogICAgICBpZihtb2RlbF9uYW1lID09ICJCYW5rcnVwdGN5Iikgew0KICAgICAgICBjYXQoIkNvbXBhbmllcyBpbiBTZWN0b3IiLCBzZWN0b3IsICJzaG91bGQgZm9jdXMgb24gIikNCiAgICAgICAgdG9wX3ZhciA8LSBzZWN0b3JfZGF0YSRWYXJpYWJsZVsxXQ0KICAgICAgICBpZihncmVwbCgicmlzay1pbmNyZWFzaW5nIiwgc2VjdG9yX2RhdGEkRWZmZWN0X1BhdHRlcm5bMV0pKSB7DQogICAgICAgICAgY2F0KCJyZWR1Y2luZyB0aGVpciIsIHRvcF92YXIsICJ0byBtaW5pbWl6ZSBiYW5rcnVwdGN5IHJpc2suXG4iKQ0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIGNhdCgibWFpbnRhaW5pbmcgc3Ryb25nIiwgdG9wX3ZhciwgInRvIHByb3RlY3QgYWdhaW5zdCBiYW5rcnVwdGN5IHJpc2suXG4iKQ0KICAgICAgICB9DQogICAgICB9IGVsc2Ugew0KICAgICAgICBjYXQoIkNvbXBhbmllcyBpbiBTZWN0b3IiLCBzZWN0b3IsICJsb29raW5nIHRvIGF2b2lkIGFjcXVpc2l0aW9uIHNob3VsZCBmb2N1cyBvbiAiKQ0KICAgICAgICB0b3BfdmFyIDwtIHNlY3Rvcl9kYXRhJFZhcmlhYmxlWzFdDQogICAgICAgIGlmKGdyZXBsKCJyaXNrLWluY3JlYXNpbmciLCBzZWN0b3JfZGF0YSRFZmZlY3RfUGF0dGVyblsxXSkpIHsNCiAgICAgICAgICBjYXQoIm1hbmFnaW5nIHRoZWlyIiwgdG9wX3ZhciwgImxldmVscy5cbiIpDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgY2F0KCJzdHJhdGVnaWNhbGx5IHBvc2l0aW9uaW5nIHRoZWlyIiwgdG9wX3ZhciwgIm1ldHJpY3MuXG4iKQ0KICAgICAgICB9DQogICAgICB9DQogICAgICBjYXQoIlxuIikNCiAgICB9DQogIH0NCiAgDQogICMgR2VuZXJhdGUgc2VjdG9yLXNwZWNpZmljIGZpbmFuY2lhbCByaXNrIGZhY3RvcnMNCiAgaWRlbnRpZnlfa2V5X3Jpc2tfZmFjdG9ycyhpbmR1c3RyeV9yZXN1bHRzJGJhbmtydXB0Y3ksICJCYW5rcnVwdGN5IikNCiAgaWRlbnRpZnlfa2V5X3Jpc2tfZmFjdG9ycyhpbmR1c3RyeV9yZXN1bHRzJGFjcXVpc2l0aW9uLCAiQWNxdWlzaXRpb24iKQ0KICANCiAgcmV0dXJuKGluZHVzdHJ5X3Jlc3VsdHMpDQp9DQoNCiMgUnVuIGluZHVzdHJ5IGFuYWx5c2lzDQppbmR1c3RyeV9yZXN1bHRzIDwtIGFuYWx5emVfaW5kdXN0cnlfZWZmZWN0cygpDQpgYGANCg0KDQpDVVJFIE1PREVMUyENCg0KVGhpcyBwYXJ0IGlzIGV4cGxvcmF0aXZlIGludG8gdGhlIGFwcGxpY2F0aW9uIG9mIGN1cmUgbW9kZWxzIHRvIHRoZSBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiBkYXRhLiBDdXJlIG1vZGVscyBhcmUgdXNlZCB0byBhbmFseXplIHN1cnZpdmFsIGRhdGEgd2hlcmUgYSBwcm9wb3J0aW9uIG9mIHN1YmplY3RzIGFyZSAiY3VyZWQiIG9yIGltbXVuZSB0byB0aGUgZXZlbnQgb2YgaW50ZXJlc3QuIEluIHRoaXMgY2FzZSwgd2Ugd2lsbCBleHBsb3JlIHRoZSB1c2Ugb2YgY3VyZSBtb2RlbHMgdG8gdW5kZXJzdGFuZCB0aGUgbG9uZy10ZXJtIHN1cnZpdmFsIG9mIGNvbXBhbmllcyBpbiB0ZXJtcyBvZiBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbiByaXNrcy4NCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBTVEVQIDE6IExvYWQgUmVxdWlyZWQgUGFja2FnZXMgZm9yIEN1cmUgTW9kZWxzDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBSZXF1aXJlZCBwYWNrYWdlcyBmb3IgY3VyZSBtb2RlbHMNCnJlcXVpcmVkX3BhY2thZ2VzIDwtIGMoDQogICJzbWN1cmUiLCAiZmxleHN1cnZjdXJlIiwgInN1cnZpdmFsIiwgImRwbHlyIiwgImdncGxvdDIiLCAic3Vydm1pbmVyIg0KKQ0KDQojIEluc3RhbGwgbWlzc2luZyBwYWNrYWdlcw0KbWlzc2luZ19wYWNrYWdlcyA8LSByZXF1aXJlZF9wYWNrYWdlc1shcmVxdWlyZWRfcGFja2FnZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssIlBhY2thZ2UiXV0NCmlmKGxlbmd0aChtaXNzaW5nX3BhY2thZ2VzKSA+IDApIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhtaXNzaW5nX3BhY2thZ2VzKQ0KfQ0KDQojIExvYWQgcGFja2FnZXMNCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7DQogIGxpYnJhcnkoc21jdXJlKSAgICAgICAgIyBGb3Igc2VtaS1wYXJhbWV0cmljIG1peHR1cmUgY3VyZSBtb2RlbHMNCiAgbGlicmFyeShmbGV4c3VydmN1cmUpICAjIEZvciBwYXJhbWV0cmljIG1peHR1cmUgY3VyZSBtb2RlbHMNCiAgbGlicmFyeShzdXJ2aXZhbCkgICAgICAjIEZvciBzdXJ2aXZhbCBtb2RlbHMNCiAgbGlicmFyeShkcGx5cikgICAgICAgICAjIEZvciBkYXRhIG1hbmlwdWxhdGlvbg0KICBsaWJyYXJ5KGdncGxvdDIpICAgICAgICMgRm9yIGRhdGEgdmlzdWFsaXphdGlvbg0KICBsaWJyYXJ5KHN1cnZtaW5lcikgICAgICMgRm9yIHN1cnZpdmFsIGFuYWx5c2lzIHZpc3VhbGl6YXRpb25zDQp9KQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMTogTG9hZCBSZXF1aXJlZCBQYWNrYWdlcyBmb3IgQ3VyZSBNb2RlbHMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFJlcXVpcmVkIHBhY2thZ2VzIGZvciBjdXJlIG1vZGVscw0KcmVxdWlyZWRfcGFja2FnZXMgPC0gYygNCiAgInNtY3VyZSIsICJmbGV4c3VydmN1cmUiLCAic3Vydml2YWwiLCAiZHBseXIiLCAiZ2dwbG90MiIsICJzdXJ2bWluZXIiDQopDQoNCiMgSW5zdGFsbCBtaXNzaW5nIHBhY2thZ2VzDQptaXNzaW5nX3BhY2thZ2VzIDwtIHJlcXVpcmVkX3BhY2thZ2VzWyFyZXF1aXJlZF9wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdXQ0KaWYobGVuZ3RoKG1pc3NpbmdfcGFja2FnZXMpID4gMCkgew0KICBpbnN0YWxsLnBhY2thZ2VzKG1pc3NpbmdfcGFja2FnZXMpDQp9DQoNCiMgTG9hZCBwYWNrYWdlcw0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsNCiAgbGlicmFyeShzbWN1cmUpICAgICAgICAjIEZvciBzZW1pLXBhcmFtZXRyaWMgbWl4dHVyZSBjdXJlIG1vZGVscw0KICBsaWJyYXJ5KGZsZXhzdXJ2Y3VyZSkgICMgRm9yIHBhcmFtZXRyaWMgbWl4dHVyZSBjdXJlIG1vZGVscw0KICBsaWJyYXJ5KHN1cnZpdmFsKSAgICAgICMgRm9yIHN1cnZpdmFsIG1vZGVscw0KICBsaWJyYXJ5KGRwbHlyKSAgICAgICAgICMgRm9yIGRhdGEgbWFuaXB1bGF0aW9uDQogIGxpYnJhcnkoZ2dwbG90MikgICAgICAgIyBGb3IgZGF0YSB2aXN1YWxpemF0aW9uDQogIGxpYnJhcnkoc3Vydm1pbmVyKSAgICAgIyBGb3Igc3Vydml2YWwgYW5hbHlzaXMgdmlzdWFsaXphdGlvbnMNCn0pDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIFNURVAgMjogRGF0YSBQcmVwYXJhdGlvbiBmb3IgQ3VyZSBNb2RlbHMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIEN1cmUgbW9kZWxzIHJlcXVpcmUgb25lIHJvdyBwZXIgc3ViamVjdCwgc28gd2UnbGwgdXNlIHRoZSBsYXN0IG9ic2VydmF0aW9uIGZvciBlYWNoIGNvbXBhbnkNCmRhdGFfY3VyZSA8LSBkYXRhICU+JQ0KICBncm91cF9ieShjdXNpcCkgJT4lDQogIHNsaWNlX21heCh0c3RvcCkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIENoZWNrIHRoZSBkYXRhIHN0cnVjdHVyZQ0KY2F0KCJEYXRhIGRpbWVuc2lvbnMgZm9yIGN1cmUgbW9kZWwgYW5hbHlzaXM6IiwgZGltKGRhdGFfY3VyZSksICJcbiIpDQpjYXQoIkV2ZW50IGZyZXF1ZW5jaWVzOlxuIikNCnByaW50KHRhYmxlKGRhdGFfY3VyZSRldmVudF90eXBlKSkNCnByaW50KHRhYmxlKGRhdGFfY3VyZSRiYW5rcnVwdGN5KSkNCnByaW50KHRhYmxlKGRhdGFfY3VyZSRhY3F1aXNpdGlvbikpDQoNCiMgQ3JlYXRlIHNlcGFyYXRlIGRhdGFzZXRzIGZvciBiYW5rcnVwdGN5IGFuZCBhY3F1aXNpdGlvbg0KZGF0YV9iYW5rcnVwdGN5IDwtIGRhdGFfY3VyZQ0KZGF0YV9hY3F1aXNpdGlvbiA8LSBkYXRhX2N1cmUNCg0KIyBOb3RlOiAnc21jdXJlJyByZXF1aXJlcyB0aW1lID4gMCwgZXZlbnQgaW5kaWNhdG9yICgwLzEpLCBhbmQgY292YXJpYXRlcw0KIyBDaGVjayBmb3IgemVybyBvciBuZWdhdGl2ZSB0aW1lcw0KY2F0KCJNaW4gdGltZSB0byBldmVudDoiLCBtaW4oZGF0YV9jdXJlJHRzdG9wKSwgIlxuIikNCg0KIyBWZXJpZnkgdGhhdCBrZXkgdmFyaWFibGVzIGV4aXN0IGluIHRoZSBkYXRhc2V0DQpjYXQoIkNoZWNraW5nIGZvciBrZXkgZmluYW5jaWFsIHZhcmlhYmxlcy4uLlxuIikNCmtleV9maW5hbmNpYWwgPC0gYygiTFRNVEEiLCAiTklNVEEiLCAiQ0FTSE1UQSIsICJQUklDRSIsICJSU0laRSIpDQpleGlzdGluZ192YXJzIDwtIGludGVyc2VjdChrZXlfZmluYW5jaWFsLCBuYW1lcyhkYXRhX2N1cmUpKQ0KY2F0KCJBdmFpbGFibGUga2V5IGZpbmFuY2lhbCB2YXJpYWJsZXM6IiwgcGFzdGUoZXhpc3RpbmdfdmFycywgY29sbGFwc2U9IiwgIiksICJcbiIpDQoNCiMgRGVmaW5lIHZhcmlhYmxlcyB0byB1c2UgZm9yIGN1cmUgbW9kZWxzDQpiYW5rcnVwdGN5X2Zpbl92YXJzIDwtIGludGVyc2VjdChjKCJMVE1UQSIsICJOSU1UQSIsICJDQVNITVRBIiwgIlBSSUNFIiksIG5hbWVzKGRhdGFfY3VyZSkpDQphY3F1aXNpdGlvbl9maW5fdmFycyA8LSBpbnRlcnNlY3QoYygiUlNJWkUiLCAiTFRNVEEiLCAiUFJJQ0UiKSwgbmFtZXMoZGF0YV9jdXJlKSkNCm1hY3JvX3ZhcnMgPC0gaW50ZXJzZWN0KGMoImdkcF9ncm93dGgiLCAidW5lbXBsb3llbWVudCIsICJnZHBfZGVmbGF0b3IiKSwgbmFtZXMoZGF0YV9jdXJlKSkNCg0KY2F0KCJWYXJpYWJsZXMgdG8gYmUgdXNlZCBpbiBiYW5rcnVwdGN5IGN1cmUgbW9kZWw6IiwgDQogICAgcGFzdGUoYyhiYW5rcnVwdGN5X2Zpbl92YXJzLCBtYWNyb192YXJzKSwgY29sbGFwc2U9IiwgIiksICJcbiIpDQpjYXQoIlZhcmlhYmxlcyB0byBiZSB1c2VkIGluIGFjcXVpc2l0aW9uIGN1cmUgbW9kZWw6IiwgDQogICAgcGFzdGUoYyhhY3F1aXNpdGlvbl9maW5fdmFycywgbWFjcm9fdmFycyksIGNvbGxhcHNlPSIsICIpLCAiXG4iKQ0KDQojIE5vIG5lZWQgZm9yIGltcHV0YXRpb24gc2luY2UgdGhlcmUgYXJlIG5vIG1pc3NpbmcgdmFsdWVzDQpjYXQoIk5vIG1pc3NpbmcgdmFsdWVzIC0gaW1wdXRhdGlvbiBub3QgbmVlZGVkLlxuIikNCmBgYA0KDQpgYGB7cn0NCiMgU1RFUCAzOiBFeHBsb3JhdG9yeSBBbmFseXNpcyBmb3IgQ3VyZSBGcmFjdGlvbg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgVGhlIHN1cnZpdmFsIGN1cnZlcyB3ZSd2ZSBzZWVuIGFscmVhZHkgcHJvdmlkZSBzdHJvbmcgZXZpZGVuY2Ugb2YgYSBjdXJlIGZyYWN0aW9uLA0KIyBlc3BlY2lhbGx5IGZvciBiYW5rcnVwdGN5LiBMZXQncyBxdWFudGlmeSB0aGlzIHdpdGggS2FwbGFuLU1laWVyIHBsYXRlYXUgZXN0aW1hdGVzLg0KDQojIENyZWF0ZSBLYXBsYW4tTWVpZXIgY3VydmVzDQprbV9iYW5rcnVwdGN5IDwtIHN1cnZmaXQoU3Vydih0c3RvcCwgYmFua3J1cHRjeSkgfiAxLCBkYXRhID0gZGF0YV9iYW5rcnVwdGN5KQ0Ka21fYWNxdWlzaXRpb24gPC0gc3VydmZpdChTdXJ2KHRzdG9wLCBhY3F1aXNpdGlvbikgfiAxLCBkYXRhID0gZGF0YV9hY3F1aXNpdGlvbikNCg0KIyBQbG90IHRoZSBjdXJ2ZXMgd2l0aCBlbXBoYXNpcyBvbiBwbGF0ZWF1cw0KcGFyKG1mcm93ID0gYygxLCAyKSkNCnBsb3Qoa21fYmFua3J1cHRjeSwgY29uZi5pbnQgPSBUUlVFLCB4bGFiID0gIlllYXJzIiwgeWxhYiA9ICJQcm9iYWJpbGl0eSBvZiBObyBCYW5rcnVwdGN5IiwNCiAgICAgbWFpbiA9ICJCYW5rcnVwdGN5LUZyZWUgU3Vydml2YWwgd2l0aCBQb3RlbnRpYWwgQ3VyZSBGcmFjdGlvbiIpDQphYmxpbmUoaCA9IHRhaWwoa21fYmFua3J1cHRjeSRzdXJ2LCAxKSwgY29sID0gInJlZCIsIGx0eSA9IDIpDQp0ZXh0KG1heChrbV9iYW5rcnVwdGN5JHRpbWUpICogMC43LCB0YWlsKGttX2JhbmtydXB0Y3kkc3VydiwgMSkgKyAwLjAyLCANCiAgICAgcGFzdGUwKCJFc3RpbWF0ZWQgY3VyZSBmcmFjdGlvbjogIiwgcm91bmQodGFpbChrbV9iYW5rcnVwdGN5JHN1cnYsIDEpICogMTAwLCAxKSwgIiUiKSwNCiAgICAgY29sID0gInJlZCIpDQoNCnBsb3Qoa21fYWNxdWlzaXRpb24sIGNvbmYuaW50ID0gVFJVRSwgeGxhYiA9ICJZZWFycyIsIHlsYWIgPSAiUHJvYmFiaWxpdHkgb2YgTm8gQWNxdWlzaXRpb24iLA0KICAgICBtYWluID0gIkFjcXVpc2l0aW9uLUZyZWUgU3Vydml2YWwgd2l0aCBQb3RlbnRpYWwgQ3VyZSBGcmFjdGlvbiIpDQphYmxpbmUoaCA9IHRhaWwoa21fYWNxdWlzaXRpb24kc3VydiwgMSksIGNvbCA9ICJibHVlIiwgbHR5ID0gMikNCnRleHQobWF4KGttX2FjcXVpc2l0aW9uJHRpbWUpICogMC43LCB0YWlsKGttX2FjcXVpc2l0aW9uJHN1cnYsIDEpICsgMC4wMiwgDQogICAgIHBhc3RlMCgiRXN0aW1hdGVkIGN1cmUgZnJhY3Rpb246ICIsIHJvdW5kKHRhaWwoa21fYWNxdWlzaXRpb24kc3VydiwgMSkgKiAxMDAsIDEpLCAiJSIpLA0KICAgICBjb2wgPSAiYmx1ZSIpDQpwYXIobWZyb3cgPSBjKDEsIDEpKQ0KDQojIFF1YW50aWZ5IHRoZSBwbGF0ZWF1IChlc3RpbWF0ZWQgY3VyZSBmcmFjdGlvbikNCmJhbmtydXB0Y3lfcGxhdGVhdSA8LSB0YWlsKGttX2JhbmtydXB0Y3kkc3VydiwgMSkNCmFjcXVpc2l0aW9uX3BsYXRlYXUgPC0gdGFpbChrbV9hY3F1aXNpdGlvbiRzdXJ2LCAxKQ0KDQpjYXQoIlxuRXN0aW1hdGVkIGN1cmUgZnJhY3Rpb25zIGZyb20gS00gY3VydmVzOlxuIikNCmNhdCgiQmFua3J1cHRjeTogQXBwcm94aW1hdGVseSIsIHJvdW5kKGJhbmtydXB0Y3lfcGxhdGVhdSAqIDEwMCwgMSksICIlIG9mIGNvbXBhbmllcyBtYXkgbmV2ZXIgZ28gYmFua3J1cHRcbiIpDQpjYXQoIkFjcXVpc2l0aW9uOiBBcHByb3hpbWF0ZWx5Iiwgcm91bmQoYWNxdWlzaXRpb25fcGxhdGVhdSAqIDEwMCwgMSksICIlIG9mIGNvbXBhbmllcyBtYXkgbmV2ZXIgYmUgYWNxdWlyZWRcbiIpDQoNCiMgU3RyYXRpZmllZCBhbmFseXNpcyBieSBpbmR1c3RyeQ0Ka21fYmFua3J1cHRjeV9zZWN0b3IgPC0gc3VydmZpdChTdXJ2KHRzdG9wLCBiYW5rcnVwdGN5KSB+IGdzZWN0b3IsIGRhdGEgPSBkYXRhX2JhbmtydXB0Y3kpDQprbV9hY3F1aXNpdGlvbl9zZWN0b3IgPC0gc3VydmZpdChTdXJ2KHRzdG9wLCBhY3F1aXNpdGlvbikgfiBnc2VjdG9yLCBkYXRhID0gZGF0YV9hY3F1aXNpdGlvbikNCg0KIyBGdW5jdGlvbiB0byBzYWZlbHkgZXh0cmFjdCB0aGUgbGFzdCBzdXJ2aXZhbCBwcm9iYWJpbGl0eSBmb3IgZWFjaCBzdHJhdHVtDQpleHRyYWN0X2xhc3Rfc3Vydl9wcm9iIDwtIGZ1bmN0aW9uKGttX2ZpdCkgew0KICAjIEdldCBzdW1tYXJ5DQogIGttX3N1bSA8LSBzdW1tYXJ5KGttX2ZpdCkNCiAgDQogICMgRXh0cmFjdCBzdHJhdGEgaW5mb3JtYXRpb24NCiAgc3RyYXRhX2luZm8gPC0ga21fc3VtJHN0cmF0YQ0KICBpZiAoaXMubnVsbChzdHJhdGFfaW5mbykpIHJldHVybih0YWlsKGttX3N1bSRzdXJ2LCAxKSkNCiAgDQogICMgSW5pdGlhbGl6ZSByZXN1bHQgdmVjdG9yDQogIHBsYXRlYXVfdmFsdWVzIDwtIG51bWVyaWMobGVuZ3RoKHN0cmF0YV9pbmZvKSkNCiAgbmFtZXMocGxhdGVhdV92YWx1ZXMpIDwtIG5hbWVzKHN0cmF0YV9pbmZvKQ0KICANCiAgIyBOZWVkIHRvIG1hbnVhbGx5IGV4dHJhY3QgdGhlIGxhc3Qgc3Vydml2YWwgcHJvYmFiaWxpdHkgZm9yIGVhY2ggc3RyYXR1bQ0KICBzdHJhdGFfbmFtZXMgPC0gbmFtZXMoc3RyYXRhX2luZm8pDQogIA0KICAjIExvb3AgdGhyb3VnaCBlYWNoIHN0cmF0dW0NCiAgZm9yIChpIGluIHNlcV9hbG9uZyhzdHJhdGFfbmFtZXMpKSB7DQogICAgIyBHZXQgc3Vic2V0IG9mIGRhdGEgZm9yIHRoaXMgc3RyYXR1bQ0KICAgIHN0cmF0YV9pbmRpY2VzIDwtIHdoaWNoKGttX3N1bSRzdHJhdGEgPT0gc3RyYXRhX25hbWVzW2ldKQ0KICAgIA0KICAgICMgRXh0cmFjdCBsYXN0IHN1cnZpdmFsIHByb2JhYmlsaXR5IGlmIGF2YWlsYWJsZQ0KICAgIGlmIChsZW5ndGgoc3RyYXRhX2luZGljZXMpID4gMCkgew0KICAgICAgcGxhdGVhdV92YWx1ZXNbaV0gPC0ga21fc3VtJHN1cnZbbWF4KHN0cmF0YV9pbmRpY2VzKV0NCiAgICB9IGVsc2Ugew0KICAgICAgcGxhdGVhdV92YWx1ZXNbaV0gPC0gTkENCiAgICB9DQogIH0NCiAgDQogIHJldHVybihwbGF0ZWF1X3ZhbHVlcykNCn0NCg0KIyBHZXQgcGxhdGVhdSBlc3RpbWF0ZXMNCmJrX3BsYXRlYXVzX2J5X3NlY3RvciA8LSBleHRyYWN0X2xhc3Rfc3Vydl9wcm9iKGttX2JhbmtydXB0Y3lfc2VjdG9yKQ0KYWNxX3BsYXRlYXVzX2J5X3NlY3RvciA8LSBleHRyYWN0X2xhc3Rfc3Vydl9wcm9iKGttX2FjcXVpc2l0aW9uX3NlY3RvcikNCg0KYGBgDQo=