Load libraries

# use this to save rmarkdown::render("~/Nextcloud/Dropbox/global_logs/win/notebook.Rmd")
# Load required libraries
library(arrow)
library(fixest)
library(dplyr)
library(ggplot2)
library(modelsummary)
library(ggplot2)
library(tidyr)
library(gridExtra)
# Load the merged Compustat data
df <- read_parquet("compustat_merged_data.parquet")
  # Matplotlib default colors (first 10)
  matplotlib_colors <- c(
    "#1f77b4",  # blue
    "#ff7f0e",  # orange  
    "#2ca02c",  # green
    "#d62728",  # red
    "#9467bd",  # purple
    "#8c564b",  # brown
    "#e377c2",  # pink
    "#7f7f7f",  # gray
    "#bcbd22",  # olive
    "#17becf"   # cyan
  )

# Create leverage variables after loading data
df <- df %>%
  mutate(
    leverage = ifelse(leverage > 0, leverage, NA),
    leverage_decile = ntile(leverage, 10) - 1
  )

# Verify the results
cat("Leverage summary:\n")
Leverage summary:
summary(df$leverage)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
  0.000   0.334   0.507   0.512   0.697  13.503   32052 
cat("\nLeverage decile distribution:\n")

Leverage decile distribution:
table(df$leverage_decile, useNA = "ifany")

    0     1     2     3     4     5     6     7     8     9  <NA> 
65611 65611 65611 65611 65611 65611 65611 65610 65610 65610 32052 

Basic data exploration

cat(paste(
  "Dataset dimensions:", paste(dim(df), collapse=" x "),
  "\nTime period:", paste(range(df$fyear, na.rm=TRUE), collapse=" - "),
  "\nNumber of firms:", length(unique(df$gvkey)),
  "\nCountries:", length(unique(df$country)), "\n"
))
Dataset dimensions: 688159 x 62 
Time period: 1981 - 2025 
Number of firms: 65533 
Countries: 119 

Check data distribution by source/country

table(df$country, useNA = "ifany")

   ARE    ARG    AUS    AUT    AZE    BEL    BFA    BGD    BGR    BHR    BMU    BRA    BRB    BWA    CAN    CHE    CHL    CHN    CIV    COL    CYM    CYP    CZE    DEU    DNK    ECU    EGY    ESP    EST    FIN    FLK    FRA    FRO    GAB    GBR 
   880    948  25851   1014     19   1568      1   2379    771    277    452   4403     11    175     40   3029   1605  59166     64    532    584    837    171   9023   1912     27   1656   2166    318   2260     12   9595      6     19  19259 
   GGY    GHA    GIB    GRC    GRL    GUF    HKG    HRV    HUN    IDN    IMN    IND    IRL    ISL    ISR    ITA    JAM    JEY    JOR    JPN    KAZ    KEN    KGZ    KHM    KOR    KWT    LBN    LBR    LKA    LTU    LUX    LVA    MAC    MAR    MCO 
   172    173     57   3071      7      3  18003   1044    314   5843    117  49076    684    104   4998   4194    485    207   1682  53923    217    590     16     19  22709   1427     26      2   3253    456    431    297    136    523     27 
   MEX    MKD    MLT    MNG    MOZ    MUS    MWI    MYS    NAM    NGA    NLD    NOR    NZL    OMN    PAK    PAN    PER    PHL    PNG    POL    PRT    PSE    QAT    ROU    RUS    SAU    SDN    SEN    SGP    SLB    SRB    SVK    SVN    SWE    THA 
  1496     15    230     15     19    536     53  15344     73   1379   1711   3035   1852   1126   5192     33   1297   2775     78   7863    705    216    374   2298   2133   1980     15      9   9045     11    291     93    369   8851   9125 
   TTO    TUN    TUR    TWN    TZA    UGA    UKR    USA    VEN    VGB    VNM    ZAF    ZMB    ZWE 
   205    366   3945  30246     99     61    233 238086     92    197   5290   3704    171    511 

Remove observations with missing key variables for regression

df_reg <- df %>% 
  filter(
    !is.na(mv_usd), !is.na(bv_usd), !is.na(ni_usd), !is.na(at_usd),
    mv_usd > 0, at_usd > 0
  ) %>% 
  mutate(industry = substr(as.character(sich), 1, 2))

Basic descriptive statistics for key variables

summary(df[c("sich", "mv_usd", "bv_usd", "ni_usd", "at_usd")])
      sich           mv_usd              bv_usd               ni_usd               at_usd         
 Min.   : 100    Min.   :      0.0   Min.   : -139965.0   Min.   :  -99289.0   Min.   :0.000e+00  
 1st Qu.:2836    1st Qu.:     22.0   1st Qu.:      14.7   1st Qu.:      -1.0   1st Qu.:3.440e+01  
 Median :3674    Median :    103.3   Median :      69.9   Median :       2.9   Median :1.612e+02  
 Mean   :4289    Mean   :   2003.4   Mean   :     788.0   Mean   :      99.0   Mean   :2.994e+03  
 3rd Qu.:5812    3rd Qu.:    564.7   3rd Qu.:     292.5   3rd Qu.:      22.2   3rd Qu.:7.324e+02  
 Max.   :9998    Max.   :3522211.1   Max.   :13003448.8   Max.   :11854605.8   Max.   :1.365e+07  
 NA's   :43311                                                                                    
summary(df[c("at_usd", "m2b", "roe", "roa")])
     at_usd               m2b                roe                 roa           
 Min.   :0.000e+00   Min.   :-99.9653   Min.   :-98.28070   Min.   :-97.33333  
 1st Qu.:3.440e+01   1st Qu.:  0.7509   1st Qu.: -0.03534   1st Qu.: -0.02769  
 Median :1.612e+02   Median :  1.4403   Median :  0.06659   Median :  0.02312  
 Mean   :2.994e+03   Mean   :  2.4510   Mean   : -0.00484   Mean   : -0.09716  
 3rd Qu.:7.324e+02   3rd Qu.:  2.8006   3rd Qu.:  0.14949   3rd Qu.:  0.06560  
 Max.   :1.365e+07   Max.   : 99.9534   Max.   : 99.82143   Max.   : 85.26677  

Model 1: Basic market cap on book value regression

# Model 1: Basic market cap on book value regression
model1 <- feols(ln_abs_mv_usd ~ ln_abs_bv_usd, data = df_reg, vcov = "hetero")

Model 2: Add net income and other

model2 <- feols(ln_abs_mv_usd ~ ln_abs_bv_0_usd + ln_abs_ni_usd + ln_abs_oth_usd, data = df_reg,  vcov = "hetero")

Model 3: Add total assets and total liabilities

model3 <- feols(ln_abs_mv_usd ~ ln_abs_at_usd + ln_abs_lt_usd, data = df_reg, vcov = "hetero")

Model 4: Add year fixed effects

model4 <- feols(ln_abs_mv_usd ~ ln_abs_bv_usd | fyear, data = df_reg, vcov = "hetero")

Model 5: Add country fixed effects

model5 <- feols(ln_abs_mv_usd ~ ln_abs_bv_usd | fyear + country, data = df_reg, vcov = "hetero")

Model 6: Add industry fixed effects (if sich available)

model6 <- feols(ln_abs_mv_usd ~ ln_abs_bv_usd | fyear + country + industry, data = df_reg, vcov = "hetero")

Model 7: Firm fixed effects (panel regression)

model7 <- feols(ln_abs_mv_usd ~ ln_abs_bv_usd | gvkey, data = df_reg, vcov = "hetero")
model8 <- feols(ln_abs_mv_usd ~ ln_abs_bv_usd | fyear + country,  data = df_reg, cluster = c("gvkey", "fyear"))

Create model list

models <- list("Basic" = model1,
               "+ Income and Other" = model2, 
               "+ Asset Liabilities" = model3,
               "Income + Year FE" = model4,
               "+ Country FE" = model5,
               "+ Industry FE" = model6,
               "Firm FE" = model7,
               "Cluster Firm Year " = model8)

Display regression results

etable(models, title = "Market Capitalization Regressions", 
       notes = "Dependent variable: Log(Market Cap USD). Standard errors are heteroskedasticity-robust.", 
       digits = 3)
                            Basic + Income and Ot.. + Asset Liabili..  Income + Year FE      + Country FE     + Industry FE          Firm FE  Cluster Firm Y..
Dependent Var.:     ln_abs_mv_usd     ln_abs_mv_usd     ln_abs_mv_usd     ln_abs_mv_usd     ln_abs_mv_usd     ln_abs_mv_usd    ln_abs_mv_usd     ln_abs_mv_usd
                                                                                                                                                              
Constant         0.681*** (0.004)   1.53*** (0.005)  0.128*** (0.007)                                                                                         
ln_abs_bv_usd   0.947*** (0.0007)                                     0.948*** (0.0007) 0.918*** (0.0008) 0.931*** (0.0008) 0.691*** (0.002)  0.918*** (0.004)
ln_abs_bv_0_usd                    0.594*** (0.002)                                                                                                           
ln_abs_ni_usd                      0.331*** (0.001)                                                                                                           
ln_abs_oth_usd                    0.028*** (0.0003)                                                                                                           
ln_abs_at_usd                                         1.09*** (0.004)                                                                                         
ln_abs_lt_usd                                       -0.220*** (0.003)                                                                                         
Fixed-Effects:  ----------------- ----------------- ----------------- ----------------- ----------------- ----------------- ----------------  ----------------
fyear                          No                No                No               Yes               Yes               Yes               No               Yes
country                        No                No                No                No               Yes               Yes               No               Yes
industry                       No                No                No                No                No               Yes               No                No
gvkey                          No                No                No                No                No                No              Yes                No
_______________ _________________ _________________ _________________ _________________ _________________ _________________ ________________  ________________
S.E. type       Heteroskeda.-rob. Heteroskeda.-rob. Heteroskeda.-rob. Heteroskeda.-rob. Heteroskeda.-rob. Heteroskeda.-rob. Heterosked.-rob. by: gvkey & fyear
Observations              688,159           688,159           687,627           688,158           688,157           644,846          683,319           688,157
R2                        0.77862           0.77200           0.74348           0.78422           0.80440           0.80891          0.92023           0.80440
Within R2                      --                --                --           0.78007           0.75552           0.75016          0.36578           0.75552
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Define the slope coefficient names we want to sum

slope_vars <- c("ln_abs_bv_usd", "ln_abs_bv_0_usd", "ln_abs_ni_usd", "ln_abs_oth_usd", "ln_abs_at_usd", "ln_abs_lt_usd")

# Function to sum coefficients for a model
sum_coefficients <- function(model, var_names) {
  coeffs <- coef(model)
  # Get coefficients that exist in the model
  existing_vars <- intersect(names(coeffs), var_names)
  if(length(existing_vars) > 0) {
    return(sum(coeffs[existing_vars]))
  } else {
    return(NA)
  }
}

Calculate the sum of coefficients for each model

coeff_sums <- sapply(models, sum_coefficients, var_names = slope_vars)

# Create a nice summary table
coeff_summary <- data.frame(
  Model = names(models),
  Sum_of_Coefficients = round(coeff_sums, 4),
  stringsAsFactors = FALSE
)

print(coeff_summary)

Visualization of coefficient sums

# Remove NA values for plotting
plot_data <- coeff_summary[!is.na(coeff_summary$Sum_of_Coefficients), ]

ggplot(plot_data, aes(x = reorder(Model, Sum_of_Coefficients), y = Sum_of_Coefficients)) +
  geom_col(fill = "steelblue", alpha = 0.7) +
  geom_text(aes(label = round(Sum_of_Coefficients, 3)), 
            hjust = -0.1, size = 3) +
  coord_flip() +
  labs(title = "Sum of Slope Coefficients by Model",
       x = "Model",
       y = "Sum of Coefficients") +
  theme_minimal()

Detailed breakdown of coefficients by model

# Show which coefficients are present in each model
for(i in 1:length(models)) {
  model_name <- names(models)[i]
  model <- models[[i]]
  coeffs <- coef(model)
  
  # Find slope coefficients that exist in this model
  existing_slopes <- intersect(names(coeffs), slope_vars)
  
  cat("\n", model_name, ":\n")
  if(length(existing_slopes) > 0) {
    for(var in existing_slopes) {
      cat("  ", var, ":", round(coeffs[var], 4), "\n")
    }
    cat("  Sum:", round(sum(coeffs[existing_slopes]), 4), "\n")
  } else {
    cat("  No slope coefficients found\n")
  }
}

 Basic :
   ln_abs_bv_usd : 0.9466 
  Sum: 0.9466 

 + Income and Other :
   ln_abs_bv_0_usd : 0.5939 
   ln_abs_ni_usd : 0.3308 
   ln_abs_oth_usd : 0.0276 
  Sum: 0.9523 

 + Asset Liabilities :
   ln_abs_at_usd : 1.0857 
   ln_abs_lt_usd : -0.2202 
  Sum: 0.8656 

 Income + Year FE :
   ln_abs_bv_usd : 0.9485 
  Sum: 0.9485 

 + Country FE :
   ln_abs_bv_usd : 0.918 
  Sum: 0.918 

 + Industry FE :
   ln_abs_bv_usd : 0.931 
  Sum: 0.931 

 Firm FE :
   ln_abs_bv_usd : 0.6911 
  Sum: 0.6911 

 Cluster Firm Year  :
   ln_abs_bv_usd : 0.918 
  Sum: 0.918 

Annual regression model

annual_regression <- function(df, regmodel, copy_to_clipboard = FALSE) {  
  # Get sorted years
  years <- sort(unique(df$fyear[!is.na(df$fyear)]))
  # Initialize list to store results
  results_by_year <- list()
  # Extract expected variable names from the formula
  formula_vars <- all.vars(as.formula(regmodel))
  expected_vars <- formula_vars[-1]  # Remove dependent variable
  expected_vars <- c("(Intercept)", expected_vars)  # Add intercept
  # Loop through each year
  for (y in years) {
    cat("Running regression for year", y, "...\n")
    # Try to run regression for this year
    tryCatch({
      # Filter data for this year
      df_year <- df[df$fyear == y & !is.na(df$fyear), ]
      # Check minimum observations
      if (nrow(df_year) < 5) {
        cat("⚠️ Skipping year", y, "- insufficient observations (", nrow(df_year), ")\n")
        next
      }
      # Run regression with clustering by gvkey
      model <- feols(as.formula(regmodel), 
                     data = df_year, 
                     cluster = "gvkey")
      # Extract coefficients
      coeffs <- coef(model)
      # Create a standardized data frame with all expected variables
      tidy_row <- data.frame(fyear = y)
      # Add each expected coefficient (NA if not present)
      for (var in expected_vars) {
        if (var %in% names(coeffs)) {
          tidy_row[[var]] <- coeffs[var]
        } else {
          tidy_row[[var]] <- NA
        }
      }
      # Store the result
      results_by_year[[length(results_by_year) + 1]] <- tidy_row
      
    }, error = function(e) {
      cat("⚠️ Regression for year", y, "failed:", e$message, "\n")
    })
  }
  # Combine results if any exist
  if (length(results_by_year) > 0) {
    # Bind all rows together (now they all have the same columns)
    df_yearly_results <- do.call(rbind, results_by_year)
    # Calculate sum of slopes (excluding intercept)
    slope_cols <- setdiff(names(df_yearly_results), c("fyear", "(Intercept)"))
    df_yearly_results$sum_slopes <- rowSums(df_yearly_results[slope_cols], na.rm = TRUE)
    # Set fyear as row names and remove the column
    rownames(df_yearly_results) <- df_yearly_results$fyear
    df_yearly_results$fyear <- NULL
    # Copy to clipboard if requested
    if (copy_to_clipboard) {
      write.table(df_yearly_results, "clipboard", sep = "\t", row.names = TRUE)
      cat("Results copied to clipboard!\n")
    }
    return(df_yearly_results)
  } else {
    cat("❌ No valid regressions could be run.\n")
    return(NULL)
  }
}

Run annual regression model

regmodel <- "ln_abs_mv_usd ~ ln_abs_bv_0_usd + ln_abs_ni_usd + ln_abs_oth_usd"
# Run the annual regression
df_y <- annual_regression(df_reg %>% filter(fyear <= 2024), regmodel)
Running regression for year 1981 ...
Running regression for year 1982 ...
Running regression for year 1983 ...
Running regression for year 1984 ...
Running regression for year 1985 ...
Running regression for year 1986 ...
Running regression for year 1987 ...
Running regression for year 1988 ...
Running regression for year 1989 ...
Running regression for year 1990 ...
Running regression for year 1991 ...
Running regression for year 1992 ...
Running regression for year 1993 ...
Running regression for year 1994 ...
Running regression for year 1995 ...
Running regression for year 1996 ...
Running regression for year 1997 ...
Running regression for year 1998 ...
Running regression for year 1999 ...
Running regression for year 2000 ...
Running regression for year 2001 ...
Running regression for year 2002 ...
Running regression for year 2003 ...
Running regression for year 2004 ...
Running regression for year 2005 ...
Running regression for year 2006 ...
Running regression for year 2007 ...
Running regression for year 2008 ...
Running regression for year 2009 ...
Running regression for year 2010 ...
Running regression for year 2011 ...
Running regression for year 2012 ...
Running regression for year 2013 ...
Running regression for year 2014 ...
Running regression for year 2015 ...
Running regression for year 2016 ...
Running regression for year 2017 ...
Running regression for year 2018 ...
Running regression for year 2019 ...
Running regression for year 2020 ...
Running regression for year 2021 ...
Running regression for year 2022 ...
Running regression for year 2023 ...
Running regression for year 2024 ...

Display results

print(df_y)

Run annual regressions for each continent

continents <- c("Europe", "Asia", "Oceania", "Africa", "Latin America", "North America")
continent_results <- list()
for (cont in continents) {
  cat("\n=== Running regressions for", cont, "===\n")
  # Filter data for this continent
  df_cont <- df_reg %>% filter(continent == cont, fyear <= 2024, fyr>0)
  # Check if we have data for this continent
  if (nrow(df_cont) > 0) {
    continent_results[[cont]] <- annual_regression(df_cont, regmodel)
  } else {
    cat("No data available for", cont, "\n")
    continent_results[[cont]] <- NULL
  }
}

=== Running regressions for Europe ===
Running regression for year 2005 ...
Running regression for year 2006 ...
Running regression for year 2007 ...
Running regression for year 2008 ...
Running regression for year 2009 ...
Running regression for year 2010 ...
Running regression for year 2011 ...
Running regression for year 2012 ...
Running regression for year 2013 ...
Running regression for year 2014 ...
Running regression for year 2015 ...
Running regression for year 2016 ...
Running regression for year 2017 ...
Running regression for year 2018 ...
Running regression for year 2019 ...
Running regression for year 2020 ...
Running regression for year 2021 ...
Running regression for year 2022 ...
Running regression for year 2023 ...
Running regression for year 2024 ...

=== Running regressions for Asia ===
Running regression for year 2005 ...
Running regression for year 2006 ...
Running regression for year 2007 ...
Running regression for year 2008 ...
Running regression for year 2009 ...
Running regression for year 2010 ...
Running regression for year 2011 ...
Running regression for year 2012 ...
Running regression for year 2013 ...
Running regression for year 2014 ...
Running regression for year 2015 ...
Running regression for year 2016 ...
Running regression for year 2017 ...
Running regression for year 2018 ...
Running regression for year 2019 ...
Running regression for year 2020 ...
Running regression for year 2021 ...
Running regression for year 2022 ...
Running regression for year 2023 ...
Running regression for year 2024 ...

=== Running regressions for Oceania ===
Running regression for year 2005 ...
⚠️ Skipping year 2005 - insufficient observations ( 1 )
Running regression for year 2006 ...
Running regression for year 2007 ...
Running regression for year 2008 ...
Running regression for year 2009 ...
Running regression for year 2010 ...
Running regression for year 2011 ...
Running regression for year 2012 ...
Running regression for year 2013 ...
Running regression for year 2014 ...
Running regression for year 2015 ...
Running regression for year 2016 ...
Running regression for year 2017 ...
Running regression for year 2018 ...
Running regression for year 2019 ...
Running regression for year 2020 ...
Running regression for year 2021 ...
Running regression for year 2022 ...
Running regression for year 2023 ...
Running regression for year 2024 ...

=== Running regressions for Africa ===
Running regression for year 2005 ...
⚠️ Skipping year 2005 - insufficient observations ( 2 )
Running regression for year 2006 ...
Running regression for year 2007 ...
Running regression for year 2008 ...
Running regression for year 2009 ...
Running regression for year 2010 ...
Running regression for year 2011 ...
Running regression for year 2012 ...
Running regression for year 2013 ...
Running regression for year 2014 ...
Running regression for year 2015 ...
Running regression for year 2016 ...
Running regression for year 2017 ...
Running regression for year 2018 ...
Running regression for year 2019 ...
Running regression for year 2020 ...
Running regression for year 2021 ...
Running regression for year 2022 ...
Running regression for year 2023 ...
Running regression for year 2024 ...

=== Running regressions for Latin America ===
Running regression for year 2006 ...
Running regression for year 2007 ...
Running regression for year 2008 ...
Running regression for year 2009 ...
Running regression for year 2010 ...
Running regression for year 2011 ...
Running regression for year 2012 ...
Running regression for year 2013 ...
Running regression for year 2014 ...
Running regression for year 2015 ...
Running regression for year 2016 ...
Running regression for year 2017 ...
Running regression for year 2018 ...
Running regression for year 2019 ...
Running regression for year 2020 ...
Running regression for year 2021 ...
Running regression for year 2022 ...
Running regression for year 2023 ...
Running regression for year 2024 ...

=== Running regressions for North America ===
Running regression for year 1981 ...
Running regression for year 1982 ...
Running regression for year 1983 ...
Running regression for year 1984 ...
Running regression for year 1985 ...
Running regression for year 1986 ...
Running regression for year 1987 ...
Running regression for year 1988 ...
Running regression for year 1989 ...
Running regression for year 1990 ...
Running regression for year 1991 ...
Running regression for year 1992 ...
Running regression for year 1993 ...
Running regression for year 1994 ...
Running regression for year 1995 ...
Running regression for year 1996 ...
Running regression for year 1997 ...
Running regression for year 1998 ...
Running regression for year 1999 ...
Running regression for year 2000 ...
Running regression for year 2001 ...
Running regression for year 2002 ...
Running regression for year 2003 ...
Running regression for year 2004 ...
Running regression for year 2005 ...
Running regression for year 2006 ...
Running regression for year 2007 ...
Running regression for year 2008 ...
Running regression for year 2009 ...
Running regression for year 2010 ...
Running regression for year 2011 ...
Running regression for year 2012 ...
Running regression for year 2013 ...
Running regression for year 2014 ...
Running regression for year 2015 ...
Running regression for year 2016 ...
Running regression for year 2017 ...
Running regression for year 2018 ...
Running regression for year 2019 ...
Running regression for year 2020 ...
Running regression for year 2021 ...
Running regression for year 2022 ...
Running regression for year 2023 ...
Running regression for year 2024 ...
# Access results
df_eu_loop <- continent_results[["Europe"]]
df_as_loop <- continent_results[["Asia"]]
df_oc_loop <- continent_results[["Oceania"]]
df_af_loop <- continent_results[["Africa"]]
df_latam_loop <- continent_results[["Latin America"]]
df_usacan_loop <- continent_results[["North America"]]

Plot line function

plot_lines <- function(df_y, lyst, region) {
  
  # Check if df_y is NULL or empty
  if (is.null(df_y) || nrow(df_y) == 0) {
    cat("No data available for", region, "\n")
    return(NULL)
  }
  
  # Convert rownames to numeric years for plotting
  df_y$fyear <- as.numeric(rownames(df_y))
  
  # Select only the variables we want to plot
  plot_data <- df_y[c("fyear", lyst)]
  
  # Remove rows with all NA values in the variables to plot
  plot_data <- plot_data[rowSums(is.na(plot_data[lyst])) < length(lyst), ]
  
  if (nrow(plot_data) == 0) {
    cat("No valid data to plot for", region, "\n")
    return(NULL)
  }
  
  # Reshape data for ggplot (wide to long format)
  plot_data_long <- plot_data %>%
    pivot_longer(cols = all_of(lyst), 
                 names_to = "variable", 
                 values_to = "value") %>%
    filter(!is.na(value))
  
  # Determine x-axis breaks based on number of years
  n_years <- length(unique(plot_data$fyear))
  if (n_years > 15) {
    x_breaks <- seq(min(plot_data$fyear), max(plot_data$fyear), by = 2)
  } else {
    x_breaks <- sort(unique(plot_data$fyear))
  }
  
  # Create the plot
  p <- ggplot(plot_data_long, aes(x = fyear, y = value, color = variable)) +
    geom_line(linewidth = 1, alpha = 0.8) +
    geom_point(size = 1.25, alpha = 0.7) +
    labs(title = paste("Coefficient Evolution -", region),
         x = "Year",
         y = "Coefficient Value",
         color = "Variable: ") +
    theme_minimal() +
    theme(
      plot.title = element_text(size = 14, hjust = 0.5),
      panel.grid.major = element_line(linetype = "dashed", linewidth = 0.3),
      panel.grid.minor = element_line(linetype = "dotted", linewidth = 0.2),
      axis.text.x = element_text(angle = 45, hjust = 1, size = 9),
      axis.text.y = element_text(size = 9),
      legend.position = "bottom",
      legend.title = element_text(size = 10),
      legend.text = element_text(size = 9),
      plot.margin = margin(10, 20, 10, 10)
    ) +
    scale_x_continuous(breaks = x_breaks, labels = x_breaks) +
    scale_color_manual(
      values = matplotlib_colors,
      labels = c("ln_abs_bv_0_usd" = "BV_0",
                 "ln_abs_ni_usd" = "NI", 
                 "ln_abs_oth_usd" = "Other",
                 "sum_slopes" = "SumSlopes")
    )
  return(p)
}

Plot coefficients for each continent

plot_variables <- c("ln_abs_bv_0_usd", "ln_abs_ni_usd", "ln_abs_oth_usd" , "sum_slopes")
df_eu_filtered <- df_eu_loop[as.numeric(rownames(df_eu_loop)) >= 2008, ]

plot_lines(df_y, plot_variables, region = 'Global')

plot_lines(df_y,       plot_variables, region = 'Global')

plot_lines(df_eu_loop, plot_variables, region = 'Europe')

plot_lines(df_eu_filtered, plot_variables, region = 'Europe')

plot_lines(df_as_loop, plot_variables, region = 'Asia')

plot_lines(df_oc_loop, plot_variables, region = 'Oceania')

plot_lines(df_af_loop, plot_variables, region = 'Africa')

plot_lines(df_latam_loop, plot_variables, region = 'Latin America')

plot_lines(df_usacan_loop, plot_variables, region = 'North America')

NA
NA
NA
regression_by_leverage_decile <- function(df, regmodel, copy_to_clipboard = FALSE) {
  
  # Drop rows with missing decile information
  df_valid <- df %>% filter(!is.na(leverage_decile))
  
  # Get unique deciles
  deciles <- sort(unique(df_valid$leverage_decile))
  
  # Initialize list to store results
  results_by_decile <- list()
  
  # Loop through each decile
  for (decile in deciles) {
    cat("Running regression for leverage decile:", decile, "\n")
    
    # Filter data for this decile
    df_sub <- df_valid %>% filter(leverage_decile == decile)
    
    # Check minimum observations
    if (nrow(df_sub) < 50) {
      cat("Skipping decile", decile, "(only", nrow(df_sub), "observations)\n")
      next
    }
    
    # Try to run regression
    tryCatch({
      # Run regression with clustering by gvkey
      model <- feols(as.formula(regmodel), 
                     data = df_sub, 
                     cluster = "gvkey")
      
      # Extract coefficients
      coeffs <- coef(model)
      
      # Create a data frame with coefficients
      tidy_row <- data.frame(
        leverage_decile = decile,
        t(coeffs),
        stringsAsFactors = FALSE
      )
      
      # Store the result
      results_by_decile[[length(results_by_decile) + 1]] <- tidy_row
      
    }, error = function(e) {
      cat("Error in decile", decile, ":", e$message, "\n")
    })
  }
  
  # Combine results if any exist
  if (length(results_by_decile) > 0) {
    # Bind all rows together
    df_decile_results <- do.call(rbind, results_by_decile)
    
    # Set leverage_decile as row names and remove the column
    rownames(df_decile_results) <- df_decile_results$leverage_decile
    df_decile_results$leverage_decile <- NULL
    
    # Calculate sum of slopes (excluding intercept - handle different intercept names)
    # Check what the actual intercept column name is
    intercept_cols <- names(df_decile_results)[grepl("Intercept", names(df_decile_results), ignore.case = TRUE)]
    slope_cols <- setdiff(names(df_decile_results), intercept_cols)
    
    if (length(slope_cols) > 0) {
      df_decile_results$sum_slopes <- rowSums(df_decile_results[slope_cols], na.rm = TRUE)
    }
    
    # Copy to clipboard if requested
    if (copy_to_clipboard) {
      # Add model and year information
      df_copy <- df_decile_results
      df_copy <- cbind(
        Model = regmodel,
        Year = paste(range(df_valid$fyear, na.rm = TRUE), collapse = "-"),
        df_copy
      )
      
      write.table(df_copy, "clipboard", sep = "\t", row.names = TRUE)
      cat("Results copied to clipboard!\n")
    }
    
    return(df_decile_results)
    
  } else {
    cat("❌ No valid regressions could be run.\n")
    return(NULL)
  }
}

df_l <- regression_by_leverage_decile(df_reg %>% filter(fyear <= 2024), regmodel)
Running regression for leverage decile: 0 
Running regression for leverage decile: 1 
Running regression for leverage decile: 2 
Running regression for leverage decile: 3 
Running regression for leverage decile: 4 
Running regression for leverage decile: 5 
Running regression for leverage decile: 6 
Running regression for leverage decile: 7 
Running regression for leverage decile: 8 
Running regression for leverage decile: 9 

Plotting function for leverage decile regression results


plot_leverage_deciles <- function(df_leverage, lyst, title_suffix = "") {
  
  # Check if df_leverage is NULL or empty
  if (is.null(df_leverage) || nrow(df_leverage) == 0) {
    cat("No data available for leverage decile plot\n")
    return(NULL)
  }
  
  # Convert rownames to numeric deciles for plotting
  df_leverage$leverage_decile <- as.numeric(rownames(df_leverage))
  
  # Select only the variables we want to plot
  plot_data <- df_leverage[c("leverage_decile", lyst)]
  
  # Remove rows with all NA values in the variables to plot
  plot_data <- plot_data[rowSums(is.na(plot_data[lyst])) < length(lyst), ]
  
  if (nrow(plot_data) == 0) {
    cat("No valid data to plot for leverage deciles\n")
    return(NULL)
  }

  # Reshape data for ggplot (wide to long format)
  plot_data_long <- plot_data %>%
    pivot_longer(cols = all_of(lyst), 
                 names_to = "variable", 
                 values_to = "value") %>%
    filter(!is.na(value))
  
  # Create the plot
  p <- ggplot(plot_data_long, aes(x = leverage_decile, y = value, color = variable)) +
    geom_line(linewidth = 1, alpha = 0.8) +
    geom_point(size = 1.25, alpha = 0.7) +
    labs(title = paste("Coefficient Evolution by Leverage Decile", title_suffix),
         x = "Leverage Decile",
         y = "Coefficient Value",
         color = "Variable") +
    theme_minimal() +
    theme(
      plot.title = element_text(size = 14, hjust = 0.5),
      panel.grid.major = element_line(linetype = "dashed", linewidth = 0.3),
      panel.grid.minor = element_line(linetype = "dotted", linewidth = 0.2),
      axis.text.x = element_text(size = 9),
      axis.text.y = element_text(size = 9),
      legend.position = "bottom",
      legend.title = element_text(size = 10),
      legend.text = element_text(size = 9),
      plot.margin = margin(10, 20, 10, 10)
    ) +
    scale_x_continuous(breaks = sort(unique(plot_data$leverage_decile)), 
                       labels = sort(unique(plot_data$leverage_decile))) +
    scale_color_manual(
      values = matplotlib_colors,
      labels = c("ln_abs_bv_0_usd" = "BV_0",
                 "ln_abs_ni_usd" = "NI", 
                 "ln_abs_oth_usd" = "Other",
                 "sum_slopes" = "SumSlopes")
    )
  
  return(p)
}
# For your specified variables (if they exist in the leverage regression)
plot_leverage_deciles(df_l, plot_variables)

Ci0tLQp0aXRsZTogIlIgTm90ZWJvb2sgRm9yIEdsb2JhbCBsb2cgcmVncmVzc2lvbnMgcmVzZWFyY2giCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KKipMb2FkIGxpYnJhcmllcyoqCgpgYGB7cn0KIyB1c2UgdGhpcyB0byBzYXZlIHJtYXJrZG93bjo6cmVuZGVyKCJ+L05leHRjbG91ZC9Ecm9wYm94L2dsb2JhbF9sb2dzL3dpbi9ub3RlYm9vay5SbWQiKQojIExvYWQgcmVxdWlyZWQgbGlicmFyaWVzCmxpYnJhcnkoYXJyb3cpCmxpYnJhcnkoZml4ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobW9kZWxzdW1tYXJ5KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ3JpZEV4dHJhKQojIExvYWQgdGhlIG1lcmdlZCBDb21wdXN0YXQgZGF0YQpkZiA8LSByZWFkX3BhcnF1ZXQoImNvbXB1c3RhdF9tZXJnZWRfZGF0YS5wYXJxdWV0IikKICAjIE1hdHBsb3RsaWIgZGVmYXVsdCBjb2xvcnMgKGZpcnN0IDEwKQogIG1hdHBsb3RsaWJfY29sb3JzIDwtIGMoCiAgICAiIzFmNzdiNCIsICAjIGJsdWUKICAgICIjZmY3ZjBlIiwgICMgb3JhbmdlICAKICAgICIjMmNhMDJjIiwgICMgZ3JlZW4KICAgICIjZDYyNzI4IiwgICMgcmVkCiAgICAiIzk0NjdiZCIsICAjIHB1cnBsZQogICAgIiM4YzU2NGIiLCAgIyBicm93bgogICAgIiNlMzc3YzIiLCAgIyBwaW5rCiAgICAiIzdmN2Y3ZiIsICAjIGdyYXkKICAgICIjYmNiZDIyIiwgICMgb2xpdmUKICAgICIjMTdiZWNmIiAgICMgY3lhbgogICkKYGBgCgpgYGB7cn0KCiMgQ3JlYXRlIGxldmVyYWdlIHZhcmlhYmxlcyBhZnRlciBsb2FkaW5nIGRhdGEKZGYgPC0gZGYgJT4lCiAgbXV0YXRlKAogICAgbGV2ZXJhZ2UgPSBpZmVsc2UobGV2ZXJhZ2UgPiAwLCBsZXZlcmFnZSwgTkEpLAogICAgbGV2ZXJhZ2VfZGVjaWxlID0gbnRpbGUobGV2ZXJhZ2UsIDEwKSAtIDEKICApCgojIFZlcmlmeSB0aGUgcmVzdWx0cwpjYXQoIkxldmVyYWdlIHN1bW1hcnk6XG4iKQpzdW1tYXJ5KGRmJGxldmVyYWdlKQpjYXQoIlxuTGV2ZXJhZ2UgZGVjaWxlIGRpc3RyaWJ1dGlvbjpcbiIpCnRhYmxlKGRmJGxldmVyYWdlX2RlY2lsZSwgdXNlTkEgPSAiaWZhbnkiKQpgYGAKCioqQmFzaWMgZGF0YSBleHBsb3JhdGlvbioqCgpgYGB7cn0KY2F0KHBhc3RlKAogICJEYXRhc2V0IGRpbWVuc2lvbnM6IiwgcGFzdGUoZGltKGRmKSwgY29sbGFwc2U9IiB4ICIpLAogICJcblRpbWUgcGVyaW9kOiIsIHBhc3RlKHJhbmdlKGRmJGZ5ZWFyLCBuYS5ybT1UUlVFKSwgY29sbGFwc2U9IiAtICIpLAogICJcbk51bWJlciBvZiBmaXJtczoiLCBsZW5ndGgodW5pcXVlKGRmJGd2a2V5KSksCiAgIlxuQ291bnRyaWVzOiIsIGxlbmd0aCh1bmlxdWUoZGYkY291bnRyeSkpLCAiXG4iCikpCmBgYAoKKipDaGVjayBkYXRhIGRpc3RyaWJ1dGlvbiBieSBzb3VyY2UvY291bnRyeSoqCgpgYGB7cn0KdGFibGUoZGYkY291bnRyeSwgdXNlTkEgPSAiaWZhbnkiKQpgYGAKCioqUmVtb3ZlIG9ic2VydmF0aW9ucyB3aXRoIG1pc3Npbmcga2V5IHZhcmlhYmxlcyBmb3IgcmVncmVzc2lvbioqCgpgYGB7cn0KZGZfcmVnIDwtIGRmICU+JSAKICBmaWx0ZXIoCiAgICAhaXMubmEobXZfdXNkKSwgIWlzLm5hKGJ2X3VzZCksICFpcy5uYShuaV91c2QpLCAhaXMubmEoYXRfdXNkKSwKICAgIG12X3VzZCA+IDAsIGF0X3VzZCA+IDAKICApICU+JSAKICBtdXRhdGUoaW5kdXN0cnkgPSBzdWJzdHIoYXMuY2hhcmFjdGVyKHNpY2gpLCAxLCAyKSkKYGBgCgoqKkJhc2ljIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgZm9yIGtleSB2YXJpYWJsZXMqKgoKYGBge3J9CnN1bW1hcnkoZGZbYygic2ljaCIsICJtdl91c2QiLCAiYnZfdXNkIiwgIm5pX3VzZCIsICJhdF91c2QiKV0pCmBgYAoKYGBge3J9CnN1bW1hcnkoZGZbYygiYXRfdXNkIiwgIm0yYiIsICJyb2UiLCAicm9hIildKQpgYGAKCioqTW9kZWwgMTogQmFzaWMgbWFya2V0IGNhcCBvbiBib29rIHZhbHVlIHJlZ3Jlc3Npb24qKgoKYGBge3J9CiMgTW9kZWwgMTogQmFzaWMgbWFya2V0IGNhcCBvbiBib29rIHZhbHVlIHJlZ3Jlc3Npb24KbW9kZWwxIDwtIGZlb2xzKGxuX2Fic19tdl91c2QgfiBsbl9hYnNfYnZfdXNkLCBkYXRhID0gZGZfcmVnLCB2Y292ID0gImhldGVybyIpCmBgYAoKKipNb2RlbCAyOiBBZGQgbmV0IGluY29tZSBhbmQgb3RoZXIqKgoKYGBge3J9Cm1vZGVsMiA8LSBmZW9scyhsbl9hYnNfbXZfdXNkIH4gbG5fYWJzX2J2XzBfdXNkICsgbG5fYWJzX25pX3VzZCArIGxuX2Fic19vdGhfdXNkLCBkYXRhID0gZGZfcmVnLCAgdmNvdiA9ICJoZXRlcm8iKQpgYGAKCioqTW9kZWwgMzogQWRkIHRvdGFsIGFzc2V0cyBhbmQgdG90YWwgbGlhYmlsaXRpZXMqKgoKYGBge3J9Cm1vZGVsMyA8LSBmZW9scyhsbl9hYnNfbXZfdXNkIH4gbG5fYWJzX2F0X3VzZCArIGxuX2Fic19sdF91c2QsIGRhdGEgPSBkZl9yZWcsIHZjb3YgPSAiaGV0ZXJvIikKYGBgCgoqKk1vZGVsIDQ6IEFkZCB5ZWFyIGZpeGVkIGVmZmVjdHMqKgoKYGBge3J9Cm1vZGVsNCA8LSBmZW9scyhsbl9hYnNfbXZfdXNkIH4gbG5fYWJzX2J2X3VzZCB8IGZ5ZWFyLCBkYXRhID0gZGZfcmVnLCB2Y292ID0gImhldGVybyIpCmBgYAoKKipNb2RlbCA1OiBBZGQgY291bnRyeSBmaXhlZCBlZmZlY3RzKioKCmBgYHtyfQptb2RlbDUgPC0gZmVvbHMobG5fYWJzX212X3VzZCB+IGxuX2Fic19idl91c2QgfCBmeWVhciArIGNvdW50cnksIGRhdGEgPSBkZl9yZWcsIHZjb3YgPSAiaGV0ZXJvIikKYGBgCgoqKk1vZGVsIDY6IEFkZCBpbmR1c3RyeSBmaXhlZCBlZmZlY3RzIChpZiBzaWNoIGF2YWlsYWJsZSkqKgoKYGBge3J9Cm1vZGVsNiA8LSBmZW9scyhsbl9hYnNfbXZfdXNkIH4gbG5fYWJzX2J2X3VzZCB8IGZ5ZWFyICsgY291bnRyeSArIGluZHVzdHJ5LCBkYXRhID0gZGZfcmVnLCB2Y292ID0gImhldGVybyIpCmBgYAoKKipNb2RlbCA3OiBGaXJtIGZpeGVkIGVmZmVjdHMgKHBhbmVsIHJlZ3Jlc3Npb24pKioKCmBgYHtyfQptb2RlbDcgPC0gZmVvbHMobG5fYWJzX212X3VzZCB+IGxuX2Fic19idl91c2QgfCBndmtleSwgZGF0YSA9IGRmX3JlZywgdmNvdiA9ICJoZXRlcm8iKQpgYGAKCmBgYHtyfQptb2RlbDggPC0gZmVvbHMobG5fYWJzX212X3VzZCB+IGxuX2Fic19idl91c2QgfCBmeWVhciArIGNvdW50cnksICBkYXRhID0gZGZfcmVnLCBjbHVzdGVyID0gYygiZ3ZrZXkiLCAiZnllYXIiKSkKYGBgCgoqKkNyZWF0ZSBtb2RlbCBsaXN0KioKCmBgYHtyfQptb2RlbHMgPC0gbGlzdCgiQmFzaWMiID0gbW9kZWwxLAogICAgICAgICAgICAgICAiKyBJbmNvbWUgYW5kIE90aGVyIiA9IG1vZGVsMiwgCiAgICAgICAgICAgICAgICIrIEFzc2V0IExpYWJpbGl0aWVzIiA9IG1vZGVsMywKICAgICAgICAgICAgICAgIkluY29tZSArIFllYXIgRkUiID0gbW9kZWw0LAogICAgICAgICAgICAgICAiKyBDb3VudHJ5IEZFIiA9IG1vZGVsNSwKICAgICAgICAgICAgICAgIisgSW5kdXN0cnkgRkUiID0gbW9kZWw2LAogICAgICAgICAgICAgICAiRmlybSBGRSIgPSBtb2RlbDcsCiAgICAgICAgICAgICAgICJDbHVzdGVyIEZpcm0gWWVhciAiID0gbW9kZWw4KQpgYGAKCioqRGlzcGxheSByZWdyZXNzaW9uIHJlc3VsdHMqKgoKYGBge3J9CmV0YWJsZShtb2RlbHMsIHRpdGxlID0gIk1hcmtldCBDYXBpdGFsaXphdGlvbiBSZWdyZXNzaW9ucyIsIAogICAgICAgbm90ZXMgPSAiRGVwZW5kZW50IHZhcmlhYmxlOiBMb2coTWFya2V0IENhcCBVU0QpLiBTdGFuZGFyZCBlcnJvcnMgYXJlIGhldGVyb3NrZWRhc3RpY2l0eS1yb2J1c3QuIiwgCiAgICAgICBkaWdpdHMgPSAzKQpgYGAKCioqRGVmaW5lIHRoZSBzbG9wZSBjb2VmZmljaWVudCBuYW1lcyB3ZSB3YW50IHRvIHN1bSoqCgpgYGB7cn0Kc2xvcGVfdmFycyA8LSBjKCJsbl9hYnNfYnZfdXNkIiwgImxuX2Fic19idl8wX3VzZCIsICJsbl9hYnNfbmlfdXNkIiwgImxuX2Fic19vdGhfdXNkIiwgImxuX2Fic19hdF91c2QiLCAibG5fYWJzX2x0X3VzZCIpCgojIEZ1bmN0aW9uIHRvIHN1bSBjb2VmZmljaWVudHMgZm9yIGEgbW9kZWwKc3VtX2NvZWZmaWNpZW50cyA8LSBmdW5jdGlvbihtb2RlbCwgdmFyX25hbWVzKSB7CiAgY29lZmZzIDwtIGNvZWYobW9kZWwpCiAgIyBHZXQgY29lZmZpY2llbnRzIHRoYXQgZXhpc3QgaW4gdGhlIG1vZGVsCiAgZXhpc3RpbmdfdmFycyA8LSBpbnRlcnNlY3QobmFtZXMoY29lZmZzKSwgdmFyX25hbWVzKQogIGlmKGxlbmd0aChleGlzdGluZ192YXJzKSA+IDApIHsKICAgIHJldHVybihzdW0oY29lZmZzW2V4aXN0aW5nX3ZhcnNdKSkKICB9IGVsc2UgewogICAgcmV0dXJuKE5BKQogIH0KfQpgYGAKCioqQ2FsY3VsYXRlIHRoZSBzdW0gb2YgY29lZmZpY2llbnRzIGZvciBlYWNoIG1vZGVsKioKCmBgYHtyfQpjb2VmZl9zdW1zIDwtIHNhcHBseShtb2RlbHMsIHN1bV9jb2VmZmljaWVudHMsIHZhcl9uYW1lcyA9IHNsb3BlX3ZhcnMpCgojIENyZWF0ZSBhIG5pY2Ugc3VtbWFyeSB0YWJsZQpjb2VmZl9zdW1tYXJ5IDwtIGRhdGEuZnJhbWUoCiAgTW9kZWwgPSBuYW1lcyhtb2RlbHMpLAogIFN1bV9vZl9Db2VmZmljaWVudHMgPSByb3VuZChjb2VmZl9zdW1zLCA0KSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKcHJpbnQoY29lZmZfc3VtbWFyeSkKYGBgCgoqKlZpc3VhbGl6YXRpb24gb2YgY29lZmZpY2llbnQgc3VtcyoqCgpgYGB7cn0KIyBSZW1vdmUgTkEgdmFsdWVzIGZvciBwbG90dGluZwpwbG90X2RhdGEgPC0gY29lZmZfc3VtbWFyeVshaXMubmEoY29lZmZfc3VtbWFyeSRTdW1fb2ZfQ29lZmZpY2llbnRzKSwgXQoKZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSByZW9yZGVyKE1vZGVsLCBTdW1fb2ZfQ29lZmZpY2llbnRzKSwgeSA9IFN1bV9vZl9Db2VmZmljaWVudHMpKSArCiAgZ2VvbV9jb2woZmlsbCA9ICJzdGVlbGJsdWUiLCBhbHBoYSA9IDAuNykgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChTdW1fb2ZfQ29lZmZpY2llbnRzLCAzKSksIAogICAgICAgICAgICBoanVzdCA9IC0wLjEsIHNpemUgPSAzKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHRpdGxlID0gIlN1bSBvZiBTbG9wZSBDb2VmZmljaWVudHMgYnkgTW9kZWwiLAogICAgICAgeCA9ICJNb2RlbCIsCiAgICAgICB5ID0gIlN1bSBvZiBDb2VmZmljaWVudHMiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKKipEZXRhaWxlZCBicmVha2Rvd24gb2YgY29lZmZpY2llbnRzIGJ5IG1vZGVsKioKCmBgYHtyfQojIFNob3cgd2hpY2ggY29lZmZpY2llbnRzIGFyZSBwcmVzZW50IGluIGVhY2ggbW9kZWwKZm9yKGkgaW4gMTpsZW5ndGgobW9kZWxzKSkgewogIG1vZGVsX25hbWUgPC0gbmFtZXMobW9kZWxzKVtpXQogIG1vZGVsIDwtIG1vZGVsc1tbaV1dCiAgY29lZmZzIDwtIGNvZWYobW9kZWwpCiAgCiAgIyBGaW5kIHNsb3BlIGNvZWZmaWNpZW50cyB0aGF0IGV4aXN0IGluIHRoaXMgbW9kZWwKICBleGlzdGluZ19zbG9wZXMgPC0gaW50ZXJzZWN0KG5hbWVzKGNvZWZmcyksIHNsb3BlX3ZhcnMpCiAgCiAgY2F0KCJcbiIsIG1vZGVsX25hbWUsICI6XG4iKQogIGlmKGxlbmd0aChleGlzdGluZ19zbG9wZXMpID4gMCkgewogICAgZm9yKHZhciBpbiBleGlzdGluZ19zbG9wZXMpIHsKICAgICAgY2F0KCIgICIsIHZhciwgIjoiLCByb3VuZChjb2VmZnNbdmFyXSwgNCksICJcbiIpCiAgICB9CiAgICBjYXQoIiAgU3VtOiIsIHJvdW5kKHN1bShjb2VmZnNbZXhpc3Rpbmdfc2xvcGVzXSksIDQpLCAiXG4iKQogIH0gZWxzZSB7CiAgICBjYXQoIiAgTm8gc2xvcGUgY29lZmZpY2llbnRzIGZvdW5kXG4iKQogIH0KfQpgYGAKCioqQW5udWFsIHJlZ3Jlc3Npb24gbW9kZWwqKgoKYGBge3J9CmFubnVhbF9yZWdyZXNzaW9uIDwtIGZ1bmN0aW9uKGRmLCByZWdtb2RlbCwgY29weV90b19jbGlwYm9hcmQgPSBGQUxTRSkgeyAgCiAgIyBHZXQgc29ydGVkIHllYXJzCiAgeWVhcnMgPC0gc29ydCh1bmlxdWUoZGYkZnllYXJbIWlzLm5hKGRmJGZ5ZWFyKV0pKQogICMgSW5pdGlhbGl6ZSBsaXN0IHRvIHN0b3JlIHJlc3VsdHMKICByZXN1bHRzX2J5X3llYXIgPC0gbGlzdCgpCiAgIyBFeHRyYWN0IGV4cGVjdGVkIHZhcmlhYmxlIG5hbWVzIGZyb20gdGhlIGZvcm11bGEKICBmb3JtdWxhX3ZhcnMgPC0gYWxsLnZhcnMoYXMuZm9ybXVsYShyZWdtb2RlbCkpCiAgZXhwZWN0ZWRfdmFycyA8LSBmb3JtdWxhX3ZhcnNbLTFdICAjIFJlbW92ZSBkZXBlbmRlbnQgdmFyaWFibGUKICBleHBlY3RlZF92YXJzIDwtIGMoIihJbnRlcmNlcHQpIiwgZXhwZWN0ZWRfdmFycykgICMgQWRkIGludGVyY2VwdAogICMgTG9vcCB0aHJvdWdoIGVhY2ggeWVhcgogIGZvciAoeSBpbiB5ZWFycykgewogICAgY2F0KCJSdW5uaW5nIHJlZ3Jlc3Npb24gZm9yIHllYXIiLCB5LCAiLi4uXG4iKQogICAgIyBUcnkgdG8gcnVuIHJlZ3Jlc3Npb24gZm9yIHRoaXMgeWVhcgogICAgdHJ5Q2F0Y2goewogICAgICAjIEZpbHRlciBkYXRhIGZvciB0aGlzIHllYXIKICAgICAgZGZfeWVhciA8LSBkZltkZiRmeWVhciA9PSB5ICYgIWlzLm5hKGRmJGZ5ZWFyKSwgXQogICAgICAjIENoZWNrIG1pbmltdW0gb2JzZXJ2YXRpb25zCiAgICAgIGlmIChucm93KGRmX3llYXIpIDwgNSkgewogICAgICAgIGNhdCgi4pqg77iPIFNraXBwaW5nIHllYXIiLCB5LCAiLSBpbnN1ZmZpY2llbnQgb2JzZXJ2YXRpb25zICgiLCBucm93KGRmX3llYXIpLCAiKVxuIikKICAgICAgICBuZXh0CiAgICAgIH0KICAgICAgIyBSdW4gcmVncmVzc2lvbiB3aXRoIGNsdXN0ZXJpbmcgYnkgZ3ZrZXkKICAgICAgbW9kZWwgPC0gZmVvbHMoYXMuZm9ybXVsYShyZWdtb2RlbCksIAogICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGZfeWVhciwgCiAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXIgPSAiZ3ZrZXkiKQogICAgICAjIEV4dHJhY3QgY29lZmZpY2llbnRzCiAgICAgIGNvZWZmcyA8LSBjb2VmKG1vZGVsKQogICAgICAjIENyZWF0ZSBhIHN0YW5kYXJkaXplZCBkYXRhIGZyYW1lIHdpdGggYWxsIGV4cGVjdGVkIHZhcmlhYmxlcwogICAgICB0aWR5X3JvdyA8LSBkYXRhLmZyYW1lKGZ5ZWFyID0geSkKICAgICAgIyBBZGQgZWFjaCBleHBlY3RlZCBjb2VmZmljaWVudCAoTkEgaWYgbm90IHByZXNlbnQpCiAgICAgIGZvciAodmFyIGluIGV4cGVjdGVkX3ZhcnMpIHsKICAgICAgICBpZiAodmFyICVpbiUgbmFtZXMoY29lZmZzKSkgewogICAgICAgICAgdGlkeV9yb3dbW3Zhcl1dIDwtIGNvZWZmc1t2YXJdCiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHRpZHlfcm93W1t2YXJdXSA8LSBOQQogICAgICAgIH0KICAgICAgfQogICAgICAjIFN0b3JlIHRoZSByZXN1bHQKICAgICAgcmVzdWx0c19ieV95ZWFyW1tsZW5ndGgocmVzdWx0c19ieV95ZWFyKSArIDFdXSA8LSB0aWR5X3JvdwogICAgICAKICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogICAgICBjYXQoIuKaoO+4jyBSZWdyZXNzaW9uIGZvciB5ZWFyIiwgeSwgImZhaWxlZDoiLCBlJG1lc3NhZ2UsICJcbiIpCiAgICB9KQogIH0KICAjIENvbWJpbmUgcmVzdWx0cyBpZiBhbnkgZXhpc3QKICBpZiAobGVuZ3RoKHJlc3VsdHNfYnlfeWVhcikgPiAwKSB7CiAgICAjIEJpbmQgYWxsIHJvd3MgdG9nZXRoZXIgKG5vdyB0aGV5IGFsbCBoYXZlIHRoZSBzYW1lIGNvbHVtbnMpCiAgICBkZl95ZWFybHlfcmVzdWx0cyA8LSBkby5jYWxsKHJiaW5kLCByZXN1bHRzX2J5X3llYXIpCiAgICAjIENhbGN1bGF0ZSBzdW0gb2Ygc2xvcGVzIChleGNsdWRpbmcgaW50ZXJjZXB0KQogICAgc2xvcGVfY29scyA8LSBzZXRkaWZmKG5hbWVzKGRmX3llYXJseV9yZXN1bHRzKSwgYygiZnllYXIiLCAiKEludGVyY2VwdCkiKSkKICAgIGRmX3llYXJseV9yZXN1bHRzJHN1bV9zbG9wZXMgPC0gcm93U3VtcyhkZl95ZWFybHlfcmVzdWx0c1tzbG9wZV9jb2xzXSwgbmEucm0gPSBUUlVFKQogICAgIyBTZXQgZnllYXIgYXMgcm93IG5hbWVzIGFuZCByZW1vdmUgdGhlIGNvbHVtbgogICAgcm93bmFtZXMoZGZfeWVhcmx5X3Jlc3VsdHMpIDwtIGRmX3llYXJseV9yZXN1bHRzJGZ5ZWFyCiAgICBkZl95ZWFybHlfcmVzdWx0cyRmeWVhciA8LSBOVUxMCiAgICAjIENvcHkgdG8gY2xpcGJvYXJkIGlmIHJlcXVlc3RlZAogICAgaWYgKGNvcHlfdG9fY2xpcGJvYXJkKSB7CiAgICAgIHdyaXRlLnRhYmxlKGRmX3llYXJseV9yZXN1bHRzLCAiY2xpcGJvYXJkIiwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gVFJVRSkKICAgICAgY2F0KCJSZXN1bHRzIGNvcGllZCB0byBjbGlwYm9hcmQhXG4iKQogICAgfQogICAgcmV0dXJuKGRmX3llYXJseV9yZXN1bHRzKQogIH0gZWxzZSB7CiAgICBjYXQoIuKdjCBObyB2YWxpZCByZWdyZXNzaW9ucyBjb3VsZCBiZSBydW4uXG4iKQogICAgcmV0dXJuKE5VTEwpCiAgfQp9CmBgYAoKKipSdW4gYW5udWFsIHJlZ3Jlc3Npb24gbW9kZWwqKgoKYGBge3J9CnJlZ21vZGVsIDwtICJsbl9hYnNfbXZfdXNkIH4gbG5fYWJzX2J2XzBfdXNkICsgbG5fYWJzX25pX3VzZCArIGxuX2Fic19vdGhfdXNkIgojIFJ1biB0aGUgYW5udWFsIHJlZ3Jlc3Npb24KZGZfeSA8LSBhbm51YWxfcmVncmVzc2lvbihkZl9yZWcgJT4lIGZpbHRlcihmeWVhciA8PSAyMDI0KSwgcmVnbW9kZWwpCmBgYAoKKipEaXNwbGF5IHJlc3VsdHMqKgoKYGBge3J9CnByaW50KGRmX3kpCmBgYAoKKipSdW4gYW5udWFsIHJlZ3Jlc3Npb25zIGZvciBlYWNoIGNvbnRpbmVudCoqCgpgYGB7cn0KY29udGluZW50cyA8LSBjKCJFdXJvcGUiLCAiQXNpYSIsICJPY2VhbmlhIiwgIkFmcmljYSIsICJMYXRpbiBBbWVyaWNhIiwgIk5vcnRoIEFtZXJpY2EiKQpjb250aW5lbnRfcmVzdWx0cyA8LSBsaXN0KCkKZm9yIChjb250IGluIGNvbnRpbmVudHMpIHsKICBjYXQoIlxuPT09IFJ1bm5pbmcgcmVncmVzc2lvbnMgZm9yIiwgY29udCwgIj09PVxuIikKICAjIEZpbHRlciBkYXRhIGZvciB0aGlzIGNvbnRpbmVudAogIGRmX2NvbnQgPC0gZGZfcmVnICU+JSBmaWx0ZXIoY29udGluZW50ID09IGNvbnQsIGZ5ZWFyIDw9IDIwMjQsIGZ5cj4wKQogICMgQ2hlY2sgaWYgd2UgaGF2ZSBkYXRhIGZvciB0aGlzIGNvbnRpbmVudAogIGlmIChucm93KGRmX2NvbnQpID4gMCkgewogICAgY29udGluZW50X3Jlc3VsdHNbW2NvbnRdXSA8LSBhbm51YWxfcmVncmVzc2lvbihkZl9jb250LCByZWdtb2RlbCkKICB9IGVsc2UgewogICAgY2F0KCJObyBkYXRhIGF2YWlsYWJsZSBmb3IiLCBjb250LCAiXG4iKQogICAgY29udGluZW50X3Jlc3VsdHNbW2NvbnRdXSA8LSBOVUxMCiAgfQp9CgojIEFjY2VzcyByZXN1bHRzCmRmX2V1X2xvb3AgPC0gY29udGluZW50X3Jlc3VsdHNbWyJFdXJvcGUiXV0KZGZfYXNfbG9vcCA8LSBjb250aW5lbnRfcmVzdWx0c1tbIkFzaWEiXV0KZGZfb2NfbG9vcCA8LSBjb250aW5lbnRfcmVzdWx0c1tbIk9jZWFuaWEiXV0KZGZfYWZfbG9vcCA8LSBjb250aW5lbnRfcmVzdWx0c1tbIkFmcmljYSJdXQpkZl9sYXRhbV9sb29wIDwtIGNvbnRpbmVudF9yZXN1bHRzW1siTGF0aW4gQW1lcmljYSJdXQpkZl91c2FjYW5fbG9vcCA8LSBjb250aW5lbnRfcmVzdWx0c1tbIk5vcnRoIEFtZXJpY2EiXV0KYGBgCgoqKlBsb3QgbGluZSBmdW5jdGlvbioqCgpgYGB7cn0KcGxvdF9saW5lcyA8LSBmdW5jdGlvbihkZl95LCBseXN0LCByZWdpb24pIHsKICAKICAjIENoZWNrIGlmIGRmX3kgaXMgTlVMTCBvciBlbXB0eQogIGlmIChpcy5udWxsKGRmX3kpIHx8IG5yb3coZGZfeSkgPT0gMCkgewogICAgY2F0KCJObyBkYXRhIGF2YWlsYWJsZSBmb3IiLCByZWdpb24sICJcbiIpCiAgICByZXR1cm4oTlVMTCkKICB9CiAgCiAgIyBDb252ZXJ0IHJvd25hbWVzIHRvIG51bWVyaWMgeWVhcnMgZm9yIHBsb3R0aW5nCiAgZGZfeSRmeWVhciA8LSBhcy5udW1lcmljKHJvd25hbWVzKGRmX3kpKQogIAogICMgU2VsZWN0IG9ubHkgdGhlIHZhcmlhYmxlcyB3ZSB3YW50IHRvIHBsb3QKICBwbG90X2RhdGEgPC0gZGZfeVtjKCJmeWVhciIsIGx5c3QpXQogIAogICMgUmVtb3ZlIHJvd3Mgd2l0aCBhbGwgTkEgdmFsdWVzIGluIHRoZSB2YXJpYWJsZXMgdG8gcGxvdAogIHBsb3RfZGF0YSA8LSBwbG90X2RhdGFbcm93U3Vtcyhpcy5uYShwbG90X2RhdGFbbHlzdF0pKSA8IGxlbmd0aChseXN0KSwgXQogIAogIGlmIChucm93KHBsb3RfZGF0YSkgPT0gMCkgewogICAgY2F0KCJObyB2YWxpZCBkYXRhIHRvIHBsb3QgZm9yIiwgcmVnaW9uLCAiXG4iKQogICAgcmV0dXJuKE5VTEwpCiAgfQogIAogICMgUmVzaGFwZSBkYXRhIGZvciBnZ3Bsb3QgKHdpZGUgdG8gbG9uZyBmb3JtYXQpCiAgcGxvdF9kYXRhX2xvbmcgPC0gcGxvdF9kYXRhICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBhbGxfb2YobHlzdCksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgCiAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lCiAgICBmaWx0ZXIoIWlzLm5hKHZhbHVlKSkKICAKICAjIERldGVybWluZSB4LWF4aXMgYnJlYWtzIGJhc2VkIG9uIG51bWJlciBvZiB5ZWFycwogIG5feWVhcnMgPC0gbGVuZ3RoKHVuaXF1ZShwbG90X2RhdGEkZnllYXIpKQogIGlmIChuX3llYXJzID4gMTUpIHsKICAgIHhfYnJlYWtzIDwtIHNlcShtaW4ocGxvdF9kYXRhJGZ5ZWFyKSwgbWF4KHBsb3RfZGF0YSRmeWVhciksIGJ5ID0gMikKICB9IGVsc2UgewogICAgeF9icmVha3MgPC0gc29ydCh1bmlxdWUocGxvdF9kYXRhJGZ5ZWFyKSkKICB9CiAgCiAgIyBDcmVhdGUgdGhlIHBsb3QKICBwIDwtIGdncGxvdChwbG90X2RhdGFfbG9uZywgYWVzKHggPSBmeWVhciwgeSA9IHZhbHVlLCBjb2xvciA9IHZhcmlhYmxlKSkgKwogICAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEsIGFscGhhID0gMC44KSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAxLjI1LCBhbHBoYSA9IDAuNykgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJDb2VmZmljaWVudCBFdm9sdXRpb24gLSIsIHJlZ2lvbiksCiAgICAgICAgIHggPSAiWWVhciIsCiAgICAgICAgIHkgPSAiQ29lZmZpY2llbnQgVmFsdWUiLAogICAgICAgICBjb2xvciA9ICJWYXJpYWJsZTogIikgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKAogICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUpLAogICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImRhc2hlZCIsIGxpbmV3aWR0aCA9IDAuMyksCiAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUobGluZXR5cGUgPSAiZG90dGVkIiwgbGluZXdpZHRoID0gMC4yKSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gOSksCiAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOSksCiAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDEwLCAyMCwgMTAsIDEwKQogICAgKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0geF9icmVha3MsIGxhYmVscyA9IHhfYnJlYWtzKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwoCiAgICAgIHZhbHVlcyA9IG1hdHBsb3RsaWJfY29sb3JzLAogICAgICBsYWJlbHMgPSBjKCJsbl9hYnNfYnZfMF91c2QiID0gIkJWXzAiLAogICAgICAgICAgICAgICAgICJsbl9hYnNfbmlfdXNkIiA9ICJOSSIsIAogICAgICAgICAgICAgICAgICJsbl9hYnNfb3RoX3VzZCIgPSAiT3RoZXIiLAogICAgICAgICAgICAgICAgICJzdW1fc2xvcGVzIiA9ICJTdW1TbG9wZXMiKQogICAgKQogIHJldHVybihwKQp9CmBgYAoKKipQbG90IGNvZWZmaWNpZW50cyBmb3IgZWFjaCBjb250aW5lbnQqKgoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQpwbG90X3ZhcmlhYmxlcyA8LSBjKCJsbl9hYnNfYnZfMF91c2QiLCAibG5fYWJzX25pX3VzZCIsICJsbl9hYnNfb3RoX3VzZCIgLCAic3VtX3Nsb3BlcyIpCmRmX2V1X2ZpbHRlcmVkIDwtIGRmX2V1X2xvb3BbYXMubnVtZXJpYyhyb3duYW1lcyhkZl9ldV9sb29wKSkgPj0gMjAwOCwgXQoKcGxvdF9saW5lcyhkZl95LCBwbG90X3ZhcmlhYmxlcywgcmVnaW9uID0gJ0dsb2JhbCcpCnBsb3RfbGluZXMoZGZfeSwgICAgICAgcGxvdF92YXJpYWJsZXMsIHJlZ2lvbiA9ICdHbG9iYWwnKQpwbG90X2xpbmVzKGRmX2V1X2xvb3AsIHBsb3RfdmFyaWFibGVzLCByZWdpb24gPSAnRXVyb3BlJykKcGxvdF9saW5lcyhkZl9ldV9maWx0ZXJlZCwgcGxvdF92YXJpYWJsZXMsIHJlZ2lvbiA9ICdFdXJvcGUnKQpwbG90X2xpbmVzKGRmX2FzX2xvb3AsIHBsb3RfdmFyaWFibGVzLCByZWdpb24gPSAnQXNpYScpCnBsb3RfbGluZXMoZGZfb2NfbG9vcCwgcGxvdF92YXJpYWJsZXMsIHJlZ2lvbiA9ICdPY2VhbmlhJykKcGxvdF9saW5lcyhkZl9hZl9sb29wLCBwbG90X3ZhcmlhYmxlcywgcmVnaW9uID0gJ0FmcmljYScpCnBsb3RfbGluZXMoZGZfbGF0YW1fbG9vcCwgcGxvdF92YXJpYWJsZXMsIHJlZ2lvbiA9ICdMYXRpbiBBbWVyaWNhJykKcGxvdF9saW5lcyhkZl91c2FjYW5fbG9vcCwgcGxvdF92YXJpYWJsZXMsIHJlZ2lvbiA9ICdOb3J0aCBBbWVyaWNhJykKCgoKYGBgCgpgYGB7cn0KcmVncmVzc2lvbl9ieV9sZXZlcmFnZV9kZWNpbGUgPC0gZnVuY3Rpb24oZGYsIHJlZ21vZGVsLCBjb3B5X3RvX2NsaXBib2FyZCA9IEZBTFNFKSB7CiAgCiAgIyBEcm9wIHJvd3Mgd2l0aCBtaXNzaW5nIGRlY2lsZSBpbmZvcm1hdGlvbgogIGRmX3ZhbGlkIDwtIGRmICU+JSBmaWx0ZXIoIWlzLm5hKGxldmVyYWdlX2RlY2lsZSkpCiAgCiAgIyBHZXQgdW5pcXVlIGRlY2lsZXMKICBkZWNpbGVzIDwtIHNvcnQodW5pcXVlKGRmX3ZhbGlkJGxldmVyYWdlX2RlY2lsZSkpCiAgCiAgIyBJbml0aWFsaXplIGxpc3QgdG8gc3RvcmUgcmVzdWx0cwogIHJlc3VsdHNfYnlfZGVjaWxlIDwtIGxpc3QoKQogIAogICMgTG9vcCB0aHJvdWdoIGVhY2ggZGVjaWxlCiAgZm9yIChkZWNpbGUgaW4gZGVjaWxlcykgewogICAgY2F0KCJSdW5uaW5nIHJlZ3Jlc3Npb24gZm9yIGxldmVyYWdlIGRlY2lsZToiLCBkZWNpbGUsICJcbiIpCiAgICAKICAgICMgRmlsdGVyIGRhdGEgZm9yIHRoaXMgZGVjaWxlCiAgICBkZl9zdWIgPC0gZGZfdmFsaWQgJT4lIGZpbHRlcihsZXZlcmFnZV9kZWNpbGUgPT0gZGVjaWxlKQogICAgCiAgICAjIENoZWNrIG1pbmltdW0gb2JzZXJ2YXRpb25zCiAgICBpZiAobnJvdyhkZl9zdWIpIDwgNTApIHsKICAgICAgY2F0KCJTa2lwcGluZyBkZWNpbGUiLCBkZWNpbGUsICIob25seSIsIG5yb3coZGZfc3ViKSwgIm9ic2VydmF0aW9ucylcbiIpCiAgICAgIG5leHQKICAgIH0KICAgIAogICAgIyBUcnkgdG8gcnVuIHJlZ3Jlc3Npb24KICAgIHRyeUNhdGNoKHsKICAgICAgIyBSdW4gcmVncmVzc2lvbiB3aXRoIGNsdXN0ZXJpbmcgYnkgZ3ZrZXkKICAgICAgbW9kZWwgPC0gZmVvbHMoYXMuZm9ybXVsYShyZWdtb2RlbCksIAogICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGZfc3ViLCAKICAgICAgICAgICAgICAgICAgICAgY2x1c3RlciA9ICJndmtleSIpCiAgICAgIAogICAgICAjIEV4dHJhY3QgY29lZmZpY2llbnRzCiAgICAgIGNvZWZmcyA8LSBjb2VmKG1vZGVsKQogICAgICAKICAgICAgIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggY29lZmZpY2llbnRzCiAgICAgIHRpZHlfcm93IDwtIGRhdGEuZnJhbWUoCiAgICAgICAgbGV2ZXJhZ2VfZGVjaWxlID0gZGVjaWxlLAogICAgICAgIHQoY29lZmZzKSwKICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICAgKQogICAgICAKICAgICAgIyBTdG9yZSB0aGUgcmVzdWx0CiAgICAgIHJlc3VsdHNfYnlfZGVjaWxlW1tsZW5ndGgocmVzdWx0c19ieV9kZWNpbGUpICsgMV1dIDwtIHRpZHlfcm93CiAgICAgIAogICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgICAgIGNhdCgiRXJyb3IgaW4gZGVjaWxlIiwgZGVjaWxlLCAiOiIsIGUkbWVzc2FnZSwgIlxuIikKICAgIH0pCiAgfQogIAogICMgQ29tYmluZSByZXN1bHRzIGlmIGFueSBleGlzdAogIGlmIChsZW5ndGgocmVzdWx0c19ieV9kZWNpbGUpID4gMCkgewogICAgIyBCaW5kIGFsbCByb3dzIHRvZ2V0aGVyCiAgICBkZl9kZWNpbGVfcmVzdWx0cyA8LSBkby5jYWxsKHJiaW5kLCByZXN1bHRzX2J5X2RlY2lsZSkKICAgIAogICAgIyBTZXQgbGV2ZXJhZ2VfZGVjaWxlIGFzIHJvdyBuYW1lcyBhbmQgcmVtb3ZlIHRoZSBjb2x1bW4KICAgIHJvd25hbWVzKGRmX2RlY2lsZV9yZXN1bHRzKSA8LSBkZl9kZWNpbGVfcmVzdWx0cyRsZXZlcmFnZV9kZWNpbGUKICAgIGRmX2RlY2lsZV9yZXN1bHRzJGxldmVyYWdlX2RlY2lsZSA8LSBOVUxMCiAgICAKICAgICMgQ2FsY3VsYXRlIHN1bSBvZiBzbG9wZXMgKGV4Y2x1ZGluZyBpbnRlcmNlcHQgLSBoYW5kbGUgZGlmZmVyZW50IGludGVyY2VwdCBuYW1lcykKICAgICMgQ2hlY2sgd2hhdCB0aGUgYWN0dWFsIGludGVyY2VwdCBjb2x1bW4gbmFtZSBpcwogICAgaW50ZXJjZXB0X2NvbHMgPC0gbmFtZXMoZGZfZGVjaWxlX3Jlc3VsdHMpW2dyZXBsKCJJbnRlcmNlcHQiLCBuYW1lcyhkZl9kZWNpbGVfcmVzdWx0cyksIGlnbm9yZS5jYXNlID0gVFJVRSldCiAgICBzbG9wZV9jb2xzIDwtIHNldGRpZmYobmFtZXMoZGZfZGVjaWxlX3Jlc3VsdHMpLCBpbnRlcmNlcHRfY29scykKICAgIAogICAgaWYgKGxlbmd0aChzbG9wZV9jb2xzKSA+IDApIHsKICAgICAgZGZfZGVjaWxlX3Jlc3VsdHMkc3VtX3Nsb3BlcyA8LSByb3dTdW1zKGRmX2RlY2lsZV9yZXN1bHRzW3Nsb3BlX2NvbHNdLCBuYS5ybSA9IFRSVUUpCiAgICB9CiAgICAKICAgICMgQ29weSB0byBjbGlwYm9hcmQgaWYgcmVxdWVzdGVkCiAgICBpZiAoY29weV90b19jbGlwYm9hcmQpIHsKICAgICAgIyBBZGQgbW9kZWwgYW5kIHllYXIgaW5mb3JtYXRpb24KICAgICAgZGZfY29weSA8LSBkZl9kZWNpbGVfcmVzdWx0cwogICAgICBkZl9jb3B5IDwtIGNiaW5kKAogICAgICAgIE1vZGVsID0gcmVnbW9kZWwsCiAgICAgICAgWWVhciA9IHBhc3RlKHJhbmdlKGRmX3ZhbGlkJGZ5ZWFyLCBuYS5ybSA9IFRSVUUpLCBjb2xsYXBzZSA9ICItIiksCiAgICAgICAgZGZfY29weQogICAgICApCiAgICAgIAogICAgICB3cml0ZS50YWJsZShkZl9jb3B5LCAiY2xpcGJvYXJkIiwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gVFJVRSkKICAgICAgY2F0KCJSZXN1bHRzIGNvcGllZCB0byBjbGlwYm9hcmQhXG4iKQogICAgfQogICAgCiAgICByZXR1cm4oZGZfZGVjaWxlX3Jlc3VsdHMpCiAgICAKICB9IGVsc2UgewogICAgY2F0KCLinYwgTm8gdmFsaWQgcmVncmVzc2lvbnMgY291bGQgYmUgcnVuLlxuIikKICAgIHJldHVybihOVUxMKQogIH0KfQoKZGZfbCA8LSByZWdyZXNzaW9uX2J5X2xldmVyYWdlX2RlY2lsZShkZl9yZWcgJT4lIGZpbHRlcihmeWVhciA8PSAyMDI0KSwgcmVnbW9kZWwpCgpgYGAKCioqUGxvdHRpbmcgZnVuY3Rpb24gZm9yIGxldmVyYWdlIGRlY2lsZSByZWdyZXNzaW9uIHJlc3VsdHMqKgoKYGBge3J9CgpwbG90X2xldmVyYWdlX2RlY2lsZXMgPC0gZnVuY3Rpb24oZGZfbGV2ZXJhZ2UsIGx5c3QsIHRpdGxlX3N1ZmZpeCA9ICIiKSB7CiAgCiAgIyBDaGVjayBpZiBkZl9sZXZlcmFnZSBpcyBOVUxMIG9yIGVtcHR5CiAgaWYgKGlzLm51bGwoZGZfbGV2ZXJhZ2UpIHx8IG5yb3coZGZfbGV2ZXJhZ2UpID09IDApIHsKICAgIGNhdCgiTm8gZGF0YSBhdmFpbGFibGUgZm9yIGxldmVyYWdlIGRlY2lsZSBwbG90XG4iKQogICAgcmV0dXJuKE5VTEwpCiAgfQogIAogICMgQ29udmVydCByb3duYW1lcyB0byBudW1lcmljIGRlY2lsZXMgZm9yIHBsb3R0aW5nCiAgZGZfbGV2ZXJhZ2UkbGV2ZXJhZ2VfZGVjaWxlIDwtIGFzLm51bWVyaWMocm93bmFtZXMoZGZfbGV2ZXJhZ2UpKQogIAogICMgU2VsZWN0IG9ubHkgdGhlIHZhcmlhYmxlcyB3ZSB3YW50IHRvIHBsb3QKICBwbG90X2RhdGEgPC0gZGZfbGV2ZXJhZ2VbYygibGV2ZXJhZ2VfZGVjaWxlIiwgbHlzdCldCiAgCiAgIyBSZW1vdmUgcm93cyB3aXRoIGFsbCBOQSB2YWx1ZXMgaW4gdGhlIHZhcmlhYmxlcyB0byBwbG90CiAgcGxvdF9kYXRhIDwtIHBsb3RfZGF0YVtyb3dTdW1zKGlzLm5hKHBsb3RfZGF0YVtseXN0XSkpIDwgbGVuZ3RoKGx5c3QpLCBdCiAgCiAgaWYgKG5yb3cocGxvdF9kYXRhKSA9PSAwKSB7CiAgICBjYXQoIk5vIHZhbGlkIGRhdGEgdG8gcGxvdCBmb3IgbGV2ZXJhZ2UgZGVjaWxlc1xuIikKICAgIHJldHVybihOVUxMKQogIH0KCiAgIyBSZXNoYXBlIGRhdGEgZm9yIGdncGxvdCAod2lkZSB0byBsb25nIGZvcm1hdCkKICBwbG90X2RhdGFfbG9uZyA8LSBwbG90X2RhdGEgJT4lCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGFsbF9vZihseXN0KSwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLCAKICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICAgIGZpbHRlcighaXMubmEodmFsdWUpKQogIAogICMgQ3JlYXRlIHRoZSBwbG90CiAgcCA8LSBnZ3Bsb3QocGxvdF9kYXRhX2xvbmcsIGFlcyh4ID0gbGV2ZXJhZ2VfZGVjaWxlLCB5ID0gdmFsdWUsIGNvbG9yID0gdmFyaWFibGUpKSArCiAgICBnZW9tX2xpbmUobGluZXdpZHRoID0gMSwgYWxwaGEgPSAwLjgpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDEuMjUsIGFscGhhID0gMC43KSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkNvZWZmaWNpZW50IEV2b2x1dGlvbiBieSBMZXZlcmFnZSBEZWNpbGUiLCB0aXRsZV9zdWZmaXgpLAogICAgICAgICB4ID0gIkxldmVyYWdlIERlY2lsZSIsCiAgICAgICAgIHkgPSAiQ29lZmZpY2llbnQgVmFsdWUiLAogICAgICAgICBjb2xvciA9ICJWYXJpYWJsZSIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41KSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBsaW5ld2lkdGggPSAwLjMpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImRvdHRlZCIsIGxpbmV3aWR0aCA9IDAuMiksCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLAogICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDIwLCAxMCwgMTApCiAgICApICsKICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzb3J0KHVuaXF1ZShwbG90X2RhdGEkbGV2ZXJhZ2VfZGVjaWxlKSksIAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNvcnQodW5pcXVlKHBsb3RfZGF0YSRsZXZlcmFnZV9kZWNpbGUpKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgICB2YWx1ZXMgPSBtYXRwbG90bGliX2NvbG9ycywKICAgICAgbGFiZWxzID0gYygibG5fYWJzX2J2XzBfdXNkIiA9ICJCVl8wIiwKICAgICAgICAgICAgICAgICAibG5fYWJzX25pX3VzZCIgPSAiTkkiLCAKICAgICAgICAgICAgICAgICAibG5fYWJzX290aF91c2QiID0gIk90aGVyIiwKICAgICAgICAgICAgICAgICAic3VtX3Nsb3BlcyIgPSAiU3VtU2xvcGVzIikKICAgICkKICAKICByZXR1cm4ocCkKfQojIEZvciB5b3VyIHNwZWNpZmllZCB2YXJpYWJsZXMgKGlmIHRoZXkgZXhpc3QgaW4gdGhlIGxldmVyYWdlIHJlZ3Jlc3Npb24pCnBsb3RfbGV2ZXJhZ2VfZGVjaWxlcyhkZl9sLCBwbG90X3ZhcmlhYmxlcykKCmBgYAo=