1 Data preparation and setup

# Load required libraries (consolidated)
library(tidyverse)    # Includes dplyr, ggplot2, tidyr, etc.
library(lavaan)       # For CFA and SEM
library(semTools)     # For measurement invariance testing
library(psych)        # For psychometric analyses
library(knitr)        # For tables
library(kableExtra)   # For enhanced tables
library(emmeans)      # For marginal means in ANOVA
library(effectsize)   # For effect size calculations
library(purrr)        # For functional programming tools
library(openxlsx)     # For Excel export
library(paletteer)    # For color palettes
library(ggraph)       # For graph visualization
library(igraph)       # For network analysis
library(patchwork)    # For combining plots
library(stringr)      # For string manipulation
library(ggridges)     # For ridge plots
library(corrplot)     # For correlation plots
library(reshape2)     # For data reshaping
library(naniar)       # For missing data visualization
library(visdat)       # For data visualization
library(gtsummary)    # For summary tables
library(ragg)         # For high-quality graphic device
library(janitor)      # For cleaning data

# Load the dataset
load("zmsp.RData")

# Define indicator variables by dimension
dimensions <- list(
  bond_class = c('k6_1803', 'k6_1806', 'k6_1809'),
  bond_teacher = c('k6_1802', 'k6_1805', 'k6_1808'),
  future_orientation = c('k6_1811', 'k6_1813', 'k6_1815'),
  school_diff_r = c('k6_1810_r', 'k6_1812_r', 'k6_1814_r'),
  school_commit_r = c('k6_1801', 'k6_1804', 'k6_1807_r')
)

# Flatten the list for convenience
bond_class <- dimensions$bond_class
bond_teacher <- dimensions$bond_teacher
future_orientation <- dimensions$future_orientation
school_diff_r <- dimensions$school_diff_r
school_commit_r <- dimensions$school_commit_r

# All items for the 15-item scale
school_exp_r <- c(bond_class, bond_teacher, school_commit_r, school_diff_r, future_orientation)

# Items for the 12-item scale (without school commitment)
school_exp_12 <- c(bond_class, bond_teacher, school_diff_r, future_orientation)

# Define labels for plots and tables
labels <- c(
  experience = "School Experience",
  class = "Bond to Class",
  teacher = "Bond to Teacher",
  future = "Future Orientation",
  diffic = "School Difficulties",
  k6_1803 = "Sense of community in class",
  k6_1806 = "Get along with classmates",
  k6_1809 = "Classmates are nice to me",
  k6_1802 = "Teacher treats me fairly",
  k6_1805 = "Get along with teacher",
  k6_1808 = "Teacher helps when needed",
  k6_1811 = "Working towards interesting job",
  k6_1813 = "Try hard at school for future job",
  k6_1815 = "Doing well at school is important",
  k6_1801 = "Likes going to school",
  k6_1804 = "Likes doing homework",
  k6_1807_r = "Finds school useless (r)",
  k6_1810_r = "Often has bad grades (r)",
  k6_1812_r = "Makes mistakes in homework (r)",
  k6_1814_r = "Struggles to follow lessons (r)"
)

# Create a subset for latent variable names
labels_latents <- labels[c("experience", "class", "teacher", "future", "diffic")]

# Define the four-factor model with second-order factor
model <- '
  class   =~ k6_1803 + k6_1806 + k6_1809
  teacher =~ k6_1802 + k6_1805 + k6_1808
  diffic  =~ k6_1810_r + k6_1812_r + k6_1814_r
  future  =~ k6_1811 + k6_1813 + k6_1815 
  
  # 2nd order factor
  experience =~ class + teacher + diffic + future
'

# Create subsets for each city
city_data <- list(
  all = zmsp,
  zproso = zmsp %>% filter(city == "Zurich"),
  mproso = zmsp %>% filter(city == "Montevideo"),
  spproso = zmsp %>% filter(city == "São Paulo")
)

# Get city names for iteration
cities <- unique(zmsp$city)

# Define latent variables list for analyses
latent_vars <- c("future", "diffic", "teacher", "class", "experience")

# Define a common theme for visualizations
theme_common <- theme_minimal() +
  theme(
    text = element_text(family = "Helvetica"),
    axis.text.x = element_text(angle = 0, hjust = 0.5),
    strip.text = element_text(face = "bold"),
    strip.background = element_rect(fill = "gray95"),
    panel.spacing = unit(1, "lines"),
    legend.position = "bottom"
  )

2 Descriptive statistics

2.1 Demographic summary

2.1.1 Sex and age

create_demographic_summary <- function(data) {
  # Ensure gender is properly formatted
  data$k6_102 <- factor(data$k6_102, 
                        levels = c("male", "female"), 
                        labels = c("Male", "Female"))
  
  # Create summary statistics
  tbl <- data %>% 
    tbl_summary(
      by = city, 
      include = c(k6_102, k6_ageyears), 
      missing = "no",
      statistic = list(all_continuous() ~ "{mean} ({sd})"),
      label = list(k6_ageyears = "Age (years)", k6_102 = "Sex")
    ) %>%
    add_p() %>%
    modify_spanning_header(c("stat_1", "stat_2", "stat_3") ~ "**City**")
  
  return(tbl)
}

# Function to visualize age by gender and city
plot_age_by_gender_city <- function(data) {
  # Ensure gender is properly formatted
  data$k6_102 <- factor(data$k6_102, 
                        levels = c("male", "female"), 
                        labels = c("Male", "Female"))
  
  # Calculate summary statistics
  summary <- data %>%
    filter(!is.na(k6_102)) %>%
    group_by(city, k6_102) %>%
    summarize(
      mean_age = mean(k6_ageyears, na.rm = TRUE),
      median_age = median(k6_ageyears, na.rm = TRUE),
      min_age = min(k6_ageyears, na.rm = TRUE),
      max_age = max(k6_ageyears, na.rm = TRUE),
      count = n(),
      sd_age = sd(k6_ageyears, na.rm = TRUE),
      se_age = sd_age / sqrt(count),
      ci_lower = mean_age - 1.96 * se_age,
      ci_upper = mean_age + 1.96 * se_age,
      .groups = "drop"
    )
  
  # Create plot
  p <- data %>%
    filter(!is.na(k6_102)) %>%
    ggplot(aes(x = k6_ageyears)) +
    geom_histogram(fill = "#FC8D62FF", alpha = 0.7, binwidth = 1) +
    geom_vline(data = summary, 
               aes(xintercept = mean_age), 
               linetype = "solid", 
               color = "black", 
               linewidth = 0.5) + 
    geom_text(data = summary,
              aes(x = mean_age + 0.3, y = 750, 
                  label = paste("Mean =", round(mean_age, 2))),
              color = "grey30",
              size = 3,
              hjust = 0,
              family = "Helvetica") +
    facet_grid(k6_102 ~ city) +
    scale_x_continuous(breaks = scales::breaks_width(1)) +
    labs(x = "Age (years)", y = "Frequency") +
    ylim(0,800) +
    theme_common
  
  return(p)
}

# Execute
demo_summary <- create_demographic_summary(zmsp)
age_plot <- plot_age_by_gender_city(zmsp)

# View
demo_summary
Characteristic
City
p-value2
São Paulo
N = 2,680
1
Zurich
N = 1,447
1
Montevideo
N = 2,204
1
Sex


0.088
    Male 1,363 (52%) 750 (52%) 1,074 (49%)
    Female 1,247 (48%) 697 (48%) 1,110 (51%)
Age (years) 14.88 (0.69) 15.44 (0.36) 15.15 (0.91) <0.001
1 n (%); Mean (SD)
2 Pearson’s Chi-squared test; Kruskal-Wallis rank sum test
age_plot

2.1.2 São Paulo

# Load required libraries
library(tidyverse)
library(patchwork)
library(ggridges)
library(paletteer)

# Prepare São Paulo data
sp_data <- zmsp %>%
  filter(city == "São Paulo") %>%
  mutate(
    sex = factor(k6_102, 
                 levels = c("male", "female"), 
                 labels = c("Male", "Female")),
    race = factor(q103,
                  levels = c("preta", "branca", "pardo", "amarela", "indigena"),
                  labels = c("Black", "White", "Mixed (Pardo)", "Yellow", "Indigenous")),
    ses = escore_final_pond,
    age = k6_ageyears
  ) %>%
  select(sex, age, race, ses)

# Plot
psp1 <- sp_data %>%
  pivot_longer(cols = c(age, ses), names_to = "variable", values_to = "value") %>%
  mutate(variable = factor(variable, 
                        levels = c("age", "ses"),
                        labels = c("Age (years)", "SES Score"))) %>%
  drop_na(race, sex, value) %>% 
  ggplot(aes(x = value, y = race, fill = sex)) +
  geom_density_ridges(alpha = 0.7, scale = 0.9) +
  facet_wrap(~ variable, scales = "free_x", strip.position = "bottom") +
  scale_fill_manual(values = c("Male" = "#FFC000", "Female" = "#4169E1")) +
  labs(
    x = "",
    y = "Race / Skin color",
    fill = "Sex"
  ) + 
  theme_common

print(psp1)

library(gtsummary)

# gtsummary table of demographic variables

demo_sp <- sp_data %>%
  tbl_summary(
    by = race,
    include = c(sex, age, ses),
    statistic = list(
      all_continuous() ~ "{mean} ({sd})",
      all_categorical() ~ "{n} ({p}%)"
    ),
    digits = all_continuous() ~ 1,
    label = list(
      sex ~ "Sex",
      age ~ "Age (years)",
      ses ~ "SES Score"
    ),
    missing = "no"
  ) %>%
  add_n() %>%
  add_p() %>%
  italicize_levels() %>%
  bold_labels()

# Print the table
demo_sp
Characteristic N Black
N = 340
1
White
N = 1,191
1
Mixed (Pardo)
N = 955
1
Yellow
N = 101
1
Indigenous
N = 70
1
p-value2
Sex 2,588




0.039
    Male
185 (57%) 591 (51%) 503 (54%) 43 (43%) 31 (46%)
    Female
138 (43%) 574 (49%) 429 (46%) 57 (57%) 37 (54%)
Age (years) 2,595 15.1 (0.8) 14.8 (0.7) 14.9 (0.6) 14.8 (0.6) 14.9 (0.7) <0.001
SES Score 2,520 4.6 (3.3) 5.4 (3.1) 4.7 (3.2) 5.1 (3.4) 4.5 (3.0) <0.001
1 n (%); Mean (SD)
2 Pearson’s Chi-squared test; Kruskal-Wallis rank sum test

2.1.3 Zurich

z_data <- zmsp %>%
  filter(city == "Zurich") %>%
  mutate(
    education_group_ses = case_when(
      k5_6_edumax == "incomplete compulsory school" ~ "1_Did Not Complete Compulsory School",

      # 2. Upper Secondary Vocational
      k5_6_edumax %in% c(
        "compulsory school, elementary vocational training",
        "domestic science course, 1-year school of commerce",
        "apprenticeship",
        "full time vocational school"
      ) ~ "2_Upper Secondary Vocational",

      # 3. Upper Secondary Academic (Matura)
      k5_6_edumax == "A-levels" ~ "3_Upper Secondary Academic (Matura)",

      # 4. Tertiary Vocational/Professional
      k5_6_edumax %in% c(
        "vocational high education",
        "technical school or vocational college",
        "vocational high school, higher specialized school"
      ) ~ "4_Tertiary Vocational/Professional",

      # 5. Tertiary Academic/University
      k5_6_edumax == "university, Swiss Federal Institute of Technology\x94" ~ "5_Tertiary Academic/University",

      # Handle "missing" level and true NAs
      k5_6_edumax == "missing" ~ NA_character_, 
      TRUE ~ NA_character_ 
    ),
    education_group_ses = factor(
      education_group_ses,
      levels = c(
        "1_Did Not Complete Compulsory School",
        "2_Upper Secondary Vocational",
        "3_Upper Secondary Academic (Matura)",
        "4_Tertiary Vocational/Professional",
        "5_Tertiary Academic/University"
      ),
        labels = c(
        "Did Not Complete\nCompulsory School",
        "Upper Secondary\nVocational",
        "Upper Secondary\nAcademic (Matura)",
        "Tertiary\nVocational/Professional",
        "Tertiary\nAcademic/University"
      )
    )
  ) %>%
  mutate(
    sex = factor(k6_102, levels = c("male", "female"), labels = c("Male", "Female")),
    migrationback = factor(k5_6_mighh2,
                           labels = c("At least one parent born in Switzerland", "Both parents born abroad")),
    ses = education_group_ses,
    age = k6_ageyears
  )

z_data_for_plot <- z_data %>%
  select(age, ses, sex, migrationback) %>%
  drop_na()

# Plot
pz1 <- z_data_for_plot %>%
  ggplot(aes(x = age, fill = sex)) +
  geom_density(alpha = 0.7) + 
  facet_grid(ses ~ migrationback, 
             scales = "fixed", 
             switch = "y") + # Rows by SES, Cols by Migration
  scale_fill_manual(values = c("Male" = "red", "Female" = "grey90")) +
  labs(
    x = "Age (years)",
    y = "Density",
    fill = "Sex"
  ) + 
  theme_common

print(pz1)

library(gtsummary)

# gtsummary table of demographic variables

demo_z <- z_data %>%
  select(sex, age, migrationback, ses) %>%
  tbl_summary(
    by = migrationback,
    include = c(sex, age, ses),
    statistic = list(
      all_continuous() ~ "{mean} ({sd})",
      all_categorical() ~ "{n} ({p}%)"
    ),
    digits = all_continuous() ~ 2,
    label = list(
      sex ~ "Sex",
      age ~ "Age (years)",
      ses ~ "Family education level"
    ),
    missing = "no"
  ) %>%
  add_n() %>%
  add_p() %>%
  italicize_levels() %>%
  bold_labels()

# Print the table
demo_z
Characteristic N At least one parent born in Switzerland
N = 714
1
Both parents born abroad
N = 697
1
p-value2
Sex 1,411

0.3
    Male
379 (53%) 351 (50%)
    Female
335 (47%) 346 (50%)
Age (years) 1,411 15.43 (0.37) 15.46 (0.36) 0.2
Family education level 1,340

<0.001
    Did Not Complete Compulsory School
10 (1.5%) 78 (12%)
    Upper Secondary Vocational
295 (43%) 350 (53%)
    Upper Secondary Academic (Matura)
105 (15%) 76 (12%)
    Tertiary Vocational/Professional
83 (12%) 60 (9.2%)
    Tertiary Academic/University
192 (28%) 91 (14%)
1 n (%); Mean (SD)
2 Pearson’s Chi-squared test; Wilcoxon rank sum test

2.1.4 Montevideo

zmsp <- zmsp %>%
  mutate(
    education_level_3cat = case_when(
      # Primary education: "Ninguno" (1) and "Escuela Primaria" (2)
      max_educ %in% c(1, 2) ~ "1. Primary",

      # Secondary education: "Curso Técnico de la UTU" (3), "Ciclo Básico" (4), "Bachillerato Secundaria" (5)
      max_educ %in% c(3, 4, 5) ~ "2. Secundary",

      # Tertiary education: "Escuela Departamental" (6) through "Posgrados Universitarios" (10)
      max_educ %in% c(6, 7, 8, 9, 10) ~ "3. Terciary",

      TRUE ~ NA_character_ # Handles any unexpected numeric values, assigning NA
    )
  )

# Table
zmsp$education_level_3cat <- factor(
  zmsp$education_level_3cat,
  levels = c("1. Primary", "2. Secundary", "3. Terciary"),
  labels = c("Primary", "Secondary", "Terciary"),
  ordered = TRUE
)

demo_m <- zmsp %>%
  filter(city == "Montevideo") %>%
  mutate(k6_ageyears = as.numeric(k6_ageyears)) %>%
  select(k6_102, k6_ageyears, education_level_3cat) %>%
  tbl_summary(
    by = k6_102,
    include = c(k6_ageyears, education_level_3cat),
    type = list(k6_ageyears ~ "continuous"),
    statistic = list(
      all_continuous() ~ "{mean} ({sd})",
      all_categorical() ~ "{n} ({p}%)"
    ),
    digits = all_continuous() ~ 1,
    label = list(
      k6_ageyears ~ "Age (years)",
      education_level_3cat ~ "Family education level"
    ),
    missing = "no"
  ) %>%
  add_n() %>%
  add_p() %>%
  italicize_levels() %>%
  bold_labels()

# Print the table
demo_m
Characteristic N male
N = 1,074
1
female
N = 1,110
1
p-value2
Age (years) 2,146 15.2 (0.9) 15.1 (0.9) 0.011
Family education level 2,155

0.060
    Primary
102 (9.6%) 125 (11%)
    Secondary
642 (61%) 609 (56%)
    Terciary
316 (30%) 361 (33%)
1 Mean (SD); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test
# Plot
m_data <- zmsp %>%
  filter(city == "Montevideo") %>%
  select(k6_102, k6_ageyears, education_level_3cat) %>%
  mutate(
    k6_ageyears = as.numeric(k6_ageyears),
    gender = factor(k6_102, 
                    levels = c("male", "female"),
                    labels = c("Male", "Female"))
  ) %>%
  drop_na()

2.2 Item responses

create_long_format_responses <- function(data, vars) {
  # Identify all factor levels across selected columns
  all_levels <- unique(unlist(lapply(data[vars], levels)))
  
  # Create long format data
  long_data <- data %>%
    select(city, all_of(vars)) %>%
    mutate(across(all_of(vars), ~ factor(., levels = all_levels, ordered = TRUE))) %>%
    pivot_longer(
      cols = all_of(vars),
      names_to = "item",
      values_to = "response"
    ) %>%
    mutate(response = factor(response, levels = all_levels, ordered = TRUE))
  
  # Summarize percentages
  result <- long_data %>%
    group_by(city, item, response) %>%
    summarise(n = n(), .groups = "drop") %>%
    group_by(city, item) %>%
    mutate(percent = round(100 * n / sum(n), 1)) %>%
    ungroup()
  
  return(result)
}

# Function to visualize item responses
plot_item_responses <- function(long_data) {
  ggplot(na.omit(long_data), 
         aes(x = city, y = percent, fill = response)) +
    geom_col(position = "fill") +
    scale_fill_paletteer_d(
      palette = "nationalparkcolors::Badlands",
      labels = c("False", "More false than true", "More true than false", "True")
    ) +
    facet_wrap(~ item, labeller = labeller(item = labels), ncol = 5) +
    scale_y_continuous(labels = scales::percent_format(scale = 100)) +
    labs(x = "", y = "", fill = "Response") +
    theme_common +
    theme(axis.text.x = element_text(angle = 0, hjust = 0.5))
}

# Execute
responses_long <- create_long_format_responses(zmsp, school_exp_r)
responses_plot <- plot_item_responses(responses_long)

# View
responses_plot

3 Missing data analysis

# 1. Basic missing data summary
get_missing_summary <- function(data, variables) {
  miss_var_summary(data[variables])
}

# 2. Missing data visualization
plot_missing_pattern <- function(data, variables, labels) {
  vis_data <- data[variables]
  names(vis_data) <- labels[names(vis_data)]
  
  vis_miss(vis_data, cluster = TRUE) +
    scale_fill_manual(
      values = c("TRUE" = "#FC8D62FF", "FALSE" = "grey90"),
      name = "Missing",
      labels = c("TRUE" = "Yes", "FALSE" = "No")
    ) +
    theme_common +
    theme(
      axis.text.x = element_text(angle = 90, hjust = 0, size = 8),
      axis.text.y = element_text(size = 8)
    ) +
    labs(y = "Students")
}

# 3. Participant-level missing data
calculate_participant_missing <- function(data, variables) {
  missing_by_participant <- data.frame(
    participant_id = 1:nrow(data),
    missing_count = rowSums(is.na(data[variables])),
    missing_percent = rowSums(is.na(data[variables])) / length(variables) * 100
  )
  
  # Summary statistics
  missing_stats <- missing_by_participant %>%
    summarise(
      min_missing = min(missing_count),
      max_missing = max(missing_count),
      mean_missing = mean(missing_count),
      median_missing = median(missing_count),
      participants_complete = sum(missing_count == 0),
      participants_complete_pct = participants_complete / n() * 100,
      participants_over_50pct = sum(missing_percent > 50)
    )
  
  list(
    by_participant = missing_by_participant,
    summary_stats = missing_stats
  )
}

# 4. Missing data correlation analysis
calculate_missing_correlations <- function(data, variables) {
  shadow_matrix <- data %>%
    select(all_of(variables)) %>%
    is.na()
  
  cor(as.data.frame(shadow_matrix))
}

# 5. Missing correlation heatmap
plot_missing_correlations <- function(miss_corr_matrix, labels) {
  miss_corr_df <- as.data.frame(as.table(miss_corr_matrix)) %>%
    rename(x = Var1, y = Var2, correlation = Freq) %>%
    filter(x != y) %>%
    mutate(
      x_label = labels[as.character(x)],
      y_label = labels[as.character(y)]
    )
  
  ggplot(miss_corr_df, aes(x = x_label, y = y_label, fill = correlation)) +
    geom_tile(color = "white", linewidth = 0.5) +
    geom_text(
      aes(label = sprintf("%.2f", correlation)),
      color = ifelse(abs(miss_corr_df$correlation) > 0.5, "black", "grey30"),
      size = 2.5
    ) +
    scale_fill_gradient2(
      low = "#E78AC3FF",
      high = "#8DA0CBFF",
      midpoint = 0.5,
      limits = c(0, 1),
      name = "Correlation"
    ) +
    theme_common +
    theme(
      axis.text.y = element_text(face = "bold", size = 8),
      axis.text.x = element_text(angle = 60, hjust = 1, size = 6),
      panel.grid = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank()
    )
}

# Execute analysis step by step
missing_summary <- get_missing_summary(zmsp, school_exp_r)
missing_pattern_plot <- plot_missing_pattern(zmsp, school_exp_r, labels)
participant_missing <- calculate_participant_missing(zmsp, school_exp_r)
missing_correlations <- calculate_missing_correlations(zmsp, school_exp_r)
missing_corr_plot <- plot_missing_correlations(missing_correlations, labels)

# View results

missing_pattern_plot

missing_corr_plot

4 Correlation and reliability analysis

# Function to calculate correlation matrix
calculate_correlation_matrix <- function(data, variables, method = "spearman") {
  corr_matrix <- cor(as.data.frame(lapply(data[, variables], as.numeric)), 
                    method = method, use = "pairwise.complete.obs")
  return(corr_matrix)
}

# Function to visualize correlation matrix
plot_correlation_matrix <- function(corr_matrix, var_labels) {
  # Prepare matrix with proper labels
  labeled_matrix <- corr_matrix
  rownames(labeled_matrix) <- var_labels[rownames(corr_matrix)]
  colnames(labeled_matrix) <- var_labels[colnames(corr_matrix)]
  
  # Function to truncate long names
  smart_truncate <- function(names, max_len = 12) {
    sapply(names, function(n) {
      if (nchar(n) <= max_len) return(n)
      paste0(substr(n, 1, max_len - 3), "...")
    })
  }
  
  colnames(labeled_matrix) <- smart_truncate(colnames(labeled_matrix), max_len = 10)
  
  # Create correlation plot
  corrplot(labeled_matrix, 
          method = "shade", 
          order = "original", 
          tl.col = "black",
          addgrid.col = 'white',
          tl.srt = 45,
          tl.cex = 0.7,
          col = COL2("PuOr", 20),
          col.lim = c(-0.1, 1),
          diag = FALSE)
}

# Function to calculate Cronbach's alpha for subscales
calculate_reliability <- function(data, subscales) {
  # Calculate correlation matrix
  corr_matrix <- calculate_correlation_matrix(data, unlist(subscales))
  
  # Calculate alpha for each subscale
  alphas <- sapply(subscales, function(scale) {
    result <- alpha(corr_matrix[scale, scale])
    return(result$total$raw_alpha)
  })
  
  # Calculate overall alpha
  overall_alpha <- alpha(corr_matrix)$total$raw_alpha
  
  # Create summary dataframe
  alpha_df <- data.frame(
    Subscale = c(names(alphas), "Full Scale"),
    Alpha = c(alphas, overall_alpha)
  )
  
  return(alpha_df)
}

# Function to calculate reliability by city
calculate_reliability_by_city <- function(data, subscales, cities) {
  # Initialize results list
  results <- list()
  
  # Get overall reliability
  overall_corr <- calculate_correlation_matrix(data, unlist(subscales))
  
  # Calculate overall alphas
  overall_alphas <- sapply(subscales, function(scale) {
    alpha(overall_corr[scale, scale])$total$raw_alpha
  })
  
  overall_total_alpha <- alpha(overall_corr)$total$raw_alpha
  
  # Create base dataframe
  alpha_df <- data.frame(
    Subscale = c(names(subscales), "Full Scale"),
    Overall = c(overall_alphas, overall_total_alpha)
  )
  
  # Add city-specific alphas
  for (city_name in cities) {
    city_data <- data[data$city == city_name, ]
    city_corr <- calculate_correlation_matrix(city_data, unlist(subscales))
    
    city_alphas <- sapply(subscales, function(scale) {
      alpha(city_corr[scale, scale])$total$raw_alpha
    })
    
    city_total_alpha <- alpha(city_corr)$total$raw_alpha
    
    # Add to dataframe
    alpha_df[[city_name]] <- c(city_alphas, city_total_alpha)
  }
  
  return(alpha_df)
}

# Function to analyze discriminant validity
analyze_discriminant_validity <- function(data, subscales) {
  # Calculate correlation matrix
  corr_matrix <- calculate_correlation_matrix(data, unlist(subscales))
  
  # Calculate item-total correlations
  item_total_cors <- list()
  
  for (scale_name in names(subscales)) {
    scale_items <- subscales[[scale_name]]
    
    item_total <- data.frame(
      Item = scale_items,
      Label = labels[scale_items],
      ItemTotal = NA
    )
    
    for (item in scale_items) {
      # Calculate correlation between this item and sum of other items in subscale
      other_items <- setdiff(scale_items, item)
      item_cor <- sapply(other_items, function(other) corr_matrix[item, other])
      item_total$ItemTotal[item_total$Item == item] <- mean(item_cor, na.rm = TRUE)
    }
    
    item_total_cors[[scale_name]] <- item_total
  }
  
  # Calculate cross-loadings
  cross_loadings <- list()
  
  for (scale_name in names(subscales)) {
    scale_items <- subscales[[scale_name]]
    
    cross_load_df <- data.frame(
      Item = scale_items,
      Label = labels[scale_items]
    )
    
    # For each other subscale, calculate average correlation
    for (other_scale in setdiff(names(subscales), scale_name)) {
      other_items <- subscales[[other_scale]]
      
      cross_load_df[[other_scale]] <- sapply(scale_items, function(item) {
        mean(corr_matrix[item, other_items], na.rm = TRUE)
      })
    }
    
    cross_loadings[[scale_name]] <- cross_load_df
  }
  
  # Combine item-total correlations and cross-loadings
  item_vs_cross <- data.frame()
  
  for (scale_name in names(subscales)) {
    scale_items <- subscales[[scale_name]]
    
    for (item in scale_items) {
      # Get item-total correlation
      item_total <- item_total_cors[[scale_name]]$ItemTotal[item_total_cors[[scale_name]]$Item == item]
      
      # Get cross-loadings for this item
      cross_loads <- cross_loadings[[scale_name]][cross_loadings[[scale_name]]$Item == item, 
                                                setdiff(names(cross_loadings[[scale_name]]), c("Item", "Label"))]
      
      # Get highest cross-loading
      max_cross <- max(unlist(cross_loads), na.rm = TRUE)
      max_cross_scale <- names(cross_loads)[which.max(unlist(cross_loads))]
      
      # Add to data frame
      item_vs_cross <- rbind(item_vs_cross, data.frame(
        Item = item,
        Label = labels[item],
        Subscale = scale_name,
        ItemTotal = item_total,
        MaxCross = max_cross,
        MaxCrossScale = max_cross_scale,
        Difference = item_total - max_cross
      ))
    }
  }
  
  # Create visualization
  p <- ggplot(item_vs_cross, aes(x = ItemTotal, 
                               y = MaxCross, 
                               color = Subscale, 
                               label = Item)) +
    geom_point(size = 6, alpha = 0.7) +
    geom_abline(intercept = 0, slope = 1, linetype = "dashed") +
    xlim(0, 0.8) +
    ylim(0, 0.8) +
    scale_color_brewer(palette = "Set2") +
    labs(x = "Item-Total Correlation", 
         y = "Highest Cross-Loading") +
    theme_common
  
  return(list(
    item_total = item_total_cors,
    cross_loadings = cross_loadings,
    combined = item_vs_cross,
    plot = p
  ))
}

# Define subscales for correlation and reliability analysis
subscales <- list(
  "Bond to Class" = bond_class,
  "Bond to Teacher" = bond_teacher,
  "School Difficulties" = school_diff_r,
  "Future Orientation" = future_orientation,
  "School Commitment" = school_commit_r
)

# Execute correlation and reliability analyses
corr_matrix <- calculate_correlation_matrix(zmsp, school_exp_r)
reliability_all <- calculate_reliability(zmsp, subscales)
reliability_by_city <- calculate_reliability_by_city(zmsp, subscales, cities)
discriminant_validity <- analyze_discriminant_validity(zmsp, subscales)

# Create visualizations
corr_plot <- plot_correlation_matrix(corr_matrix, labels)

discriminant_plot <- discriminant_validity$plot

# View
discriminant_plot

5 EFA - exploratory factor analysis

# Function to run EFA with different factor solutions
run_efa <- function(data, model_spec, variables, n_factors = 1:6) {
  efa_fit <- efa(model_spec,
                 data = data, 
                 nfactors = n_factors, 
                 ov.names = variables, 
                 output = "efa",
                 std.ov = TRUE,
                 ordered = TRUE)
  
  return(efa_fit)
}

# Function to create scree plot for EFA results
create_scree_plot <- function(eigenvalues_15, eigenvalues_12) {
  factor_num_15 <- 1:length(eigenvalues_15)
  factor_num_12 <- 1:length(eigenvalues_12)
  
  # Combine data
  scree_data <- bind_rows(
    tibble(Eigenvalue = eigenvalues_15, Factor = factor_num_15, Scale = "15-item"),
    tibble(Eigenvalue = eigenvalues_12, Factor = factor_num_12, Scale = "12-item")
  )
  
  # Create scree plot
  set2_colors <- RColorBrewer::brewer.pal(8, "Set2")[1:2]
  
  p <- ggplot(scree_data, aes(x = Factor, y = Eigenvalue, color = Scale, group = Scale)) +
    geom_line(linewidth = 1, 
              alpha = 0.7) +
    geom_point(size = 3) +
    geom_hline(yintercept = 1, linetype = "dashed", color = "darkgray") +
    theme_common +
    labs(x = "Factor Number", y = "Eigenvalue") +
    scale_x_continuous(breaks = 1:15) +
    scale_color_manual(values = set2_colors)
  
  return(p)
}

# Function to visualize factor loadings heatmap
create_factor_loadings_heatmap <- function(loadings_data) {
  # Convert to long format
  loadings_long <- loadings_data %>%
    pivot_longer(cols = -c(Item, Description), 
                names_to = "Factor", 
                values_to = "Loading") %>%
    mutate(Loading = ifelse(is.na(Loading) | Loading < 0.3, NA, Loading))
  
  # Group items by theoretical dimension
  loadings_long <- loadings_long %>%
    mutate(ItemGroup = case_when(
      Item %in% c("k6_1803", "k6_1806", "k6_1809") ~ "Bond to Class",
      Item %in% c("k6_1802", "k6_1805", "k6_1808") ~ "Bond to Teacher",
      Item %in% c("k6_1801", "k6_1804", "k6_1807_r") ~ "School Commitment",
      Item %in% c("k6_1810_r", "k6_1812_r", "k6_1814_r") ~ "School Difficulties",
      Item %in% c("k6_1811", "k6_1813", "k6_1815") ~ "Future Orientation"
    ))
  
  # Create model groups
  loadings_long <- loadings_long %>%
    mutate(Model = case_when(
      grepl("12-4F", Factor) ~ "12-item 4-factor",
      grepl("15-4F", Factor) ~ "15-item 4-factor",
      grepl("15-5F", Factor) ~ "15-item 5-factor"
    ))
  
  # Order factors and items
  loadings_long$ItemGroup <- factor(loadings_long$ItemGroup, 
                                   levels = c("Bond to Class", "Bond to Teacher", 
                                             "School Commitment", "School Difficulties", 
                                             "Future Orientation"))
  
  # Create heatmap
  p <- ggplot(loadings_long, 
             aes(x = Factor, y = Description, fill = Loading)) +
    geom_tile(color = "white") +
    geom_text(aes(label = ifelse(!is.na(Loading), 
                                sprintf("%.2f", Loading), 
                                "")), 
              family = "Helvetica") +
    scale_fill_gradientn(colors = RColorBrewer::brewer.pal(8, "Set2"), 
                        na.value = "white", 
                        limits = c(0, 1.02)) +
    facet_grid(ItemGroup ~ Model, scales = "free", space = "free") +
    theme_common +
    theme(
      axis.text.x = element_blank(),
      axis.text.y = element_text(face = "bold")
    ) +
    labs(x = "", y = "", fill = "Factor Loading")
  
  return(p)
}

# Specify EFA models
efa_model_15 <- paste(school_exp_r, collapse = " + ")
efa_model_12 <- paste(school_exp_12, collapse = " + ")

# Run EFA
efa_fit_15 <- run_efa(zmsp, efa_model_15, school_exp_r)
efa_fit_12 <- run_efa(zmsp, efa_model_12, school_exp_12)

# Use hardcoded eigenvalues
eigenvalues_15 <- c(4.723, 2.013, 1.676, 1.149, 0.896, 0.726, 0.633, 0.539, 0.492, 0.453, 0.410, 0.387, 0.349, 0.319, 0.235)
eigenvalues_12 <- c(4.053, 1.897, 1.499, 1.119, 0.630, 0.547, 0.498, 0.426, 0.413, 0.357, 0.325, 0.238)

# Create scree plot using these values
scree_plot <- create_scree_plot(eigenvalues_15, eigenvalues_12)

# Factor loadings data
loadings_data <- tibble(
  Item = c("k6_1803", "k6_1806", "k6_1809", "k6_1802", "k6_1805", "k6_1808", 
          "k6_1801", "k6_1804", "k6_1807_r", "k6_1810_r", "k6_1812_r", "k6_1814_r",
          "k6_1811", "k6_1813", "k6_1815"),
  Description = c("Sense of community in class", 
                 "Get along with classmates", 
                 "Classmates are nice to me",
                 "Teacher treats me fairly", 
                 "Get along with teacher", 
                 "Teacher helps when needed",
                 "Likes going to school", 
                 "Likes doing homework", 
                 "Finds school useless (r)",
                 "Often has bad grades (r)", 
                 "Makes mistakes in homework (r)", 
                 "Struggles to follow lessons (r)",
                 "Working towards interesting job", 
                 "Try hard at school for future job", 
                 "Doing well at school is important"),
  
  # Add factor loadings for different models
  "12-4F: Bond to Class" = c(0.681, 0.912, 0.795, 0, 0, 0, NA, NA, NA, 0, 0, 0, 0, 0, 0),
  "12-4F: Bond to Teacher" = c(0, 0, 0, 0.784, 0.783, 0.540, NA, NA, NA, 0, 0, 0, 0, 0, 0),
  "12-4F: School Difficulties" = c(0, 0, 0, 0, 0, 0, NA, NA, NA, 0.618, 0.719, 0.749, 0, 0, 0),
  "12-4F: Future Orientation" = c(0, 0, 0, 0, 0, 0, NA, NA, NA, 0, 0, 0, 0.675, 0.864, 0.655),
  
  # Additional model loadings
  "15-4F: Bond to Class" = c(0.667, 0.899, 0.802, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  "15-4F: Bond to Teacher" = c(0, 0, 0, 0.798, 0.770, 0.528, 0.388, 0, 0, 0, 0, 0, 0, 0, 0),
  "15-4F: School Difficulties" = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0.616, 0.721, 0.749, 0, 0, 0),
  "15-4F: Future/Commitment" = c(0, 0, 0, 0, 0, 0, 0.307, 0.378, 0.394, 0, 0, 0, 0.668, 0.813, 0.683),
  
  "15-5F: Bond to Class" = c(0.683, 0.906, 0.803, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  "15-5F: Bond to Teacher" = c(0, 0, 0, 0.732, 0.757, 0.540, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  "15-5F: School Commitment" = c(0, 0, 0, 0, 0, 0, 1.012, 0.322, 0, 0, 0, 0, 0, 0, 0),
  "15-5F: School Difficulties" = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0.614, 0.725, 0.767, 0, 0, 0),
  "15-5F: Future Orientation" = c(0, 0, 0, 0, 0, 0, 0, 0, 0.368, 0, 0, 0, 0.686, 0.830, 0.700)
)

# Create factor loadings heatmap
loadings_heatmap <- create_factor_loadings_heatmap(loadings_data)

# View
loadings_heatmap

6 CFA - confirmatory factor analysis

# Function to run CFA and extract fit measures
run_cfa <- function(data, model_spec, ordered = TRUE) {
  fit <- cfa(model_spec, data = data, ordered = ordered)
  return(fit)
}

# Function to extract and format fit measures
extract_fit_measures <- function(fit) {
  summary(fit, fit.measures = TRUE, standardized = TRUE)
}

# Run CFA for each dataset
cfa_fits <- list(
  all = run_cfa(city_data$all, model),
  zproso = run_cfa(city_data$zproso, model),
  mproso = run_cfa(city_data$mproso, model),
  spproso = run_cfa(city_data$spproso, model)
)

# Get fit measures for each model
fit_summaries <- lapply(cfa_fits, extract_fit_measures)

# Function to create factor loadings visualization
plot_factor_loadings <- function(fit_model) {
  # Extract standardized loadings
  std_est <- standardizedSolution(fit_model) %>%
    filter(op == "=~", !grepl("^experience$", lhs))  # Only first-order factor loadings
  
  # Check if this is a multi-group model
  is_multigroup <- "group" %in% names(std_est)
  
  # Process differently based on whether it's a multi-group model
  if (is_multigroup) {
    # Add group labels for multi-group model
    group_labels <- lavInspect(fit_model, "group.label")
    
    std_est_labeled <- std_est %>%
      mutate(
        group = factor(group, labels = group_labels),
        item_label = labels[rhs],
        factor_label = labels[lhs],
        # Format for better display
        item_label = stringr::str_replace_all(labels[rhs], "(\\w+\\s+\\w+)\\s+", "\\1\n"),
        group = factor(group, levels = c("São Paulo", "Zurich", "Montevideo"), ordered = TRUE)
      )
    
    # Create heatmap for multi-group model
    p <- ggplot(std_est_labeled, aes(x = group, 
                                     y = item_label, 
                                     fill = est.std)) +
      geom_tile() +
      geom_text(aes(label = sprintf("%.2f", est.std)), color = "black", size = 3) +
      facet_wrap(~ factor_label, scales = "free_y") +
      scale_fill_gradient2(
        low = "#E5C494FF", mid = "white", high = "#E78AC3FF", 
        midpoint = 0.75, name = "Standardized\nFactor Loading", limits = c(0.5, 1)
      ) +
      labs(x = "", y = "") +
      theme_common
  } else {
    # For single-group model, create a different visualization
    std_est_labeled <- std_est %>%
      mutate(
        item_label = labels[rhs],
        factor_label = labels[lhs],
        # Format for better display
        item_label = stringr::str_replace_all(labels[rhs], "(\\w+\\s+\\w+)\\s+", "\\1\n")
      )
    
    # Create barplot for single-group model
    p <- ggplot(std_est_labeled, aes(x = item_label, 
                                     y = est.std)) +
      geom_bar(stat = "identity", 
               fill = "#E78AC3FF") +
      geom_text(aes(label = sprintf("%.2f", est.std)), 
                position = position_stack(vjust = 0.5), 
                color = "black", size = 3) +
      facet_wrap(~ factor_label, scales = "free_x") +
      labs(x = "", y = "Standardized Factor Loading") +
      theme_common +
      theme(axis.text.x = element_text(angle = 0, hjust = 0.5))
  }
  
  return(p)
}

# Create visualizations
loadings_plot <- plot_factor_loadings(cfa_fits$all)

# View
loadings_plot

7 MI - measurement invariance

7.1 Run MI tests

invariance_models <- list(
  configural = cfa(model, data = zmsp, ordered = TRUE, group = "city"),
  metric = cfa(model, data = zmsp, ordered = TRUE, group = "city", 
              group.equal = "loadings"),
  scalar = cfa(model, data = zmsp, ordered = TRUE, group = "city", 
              group.equal = c("intercepts", "loadings")),
  thresholds = cfa(model, data = zmsp, ordered = TRUE, group = "city", 
                  group.equal = c("thresholds", "loadings"))
)

7.2 Compare models

invariance_comparisons <- list(
  metric_vs_configural = compareFit(invariance_models$metric, 
                                    invariance_models$configural),
  scalar_vs_metric = compareFit(invariance_models$scalar, 
                               invariance_models$metric),
  full = compareFit(invariance_models$thresholds, 
                   invariance_models$scalar, 
                   invariance_models$metric, 
                   invariance_models$configural)
)

7.3 Functions

# Extract fit measures for all models
invariance_fit_measures <- lapply(invariance_models, fitMeasures)

# Process fit measures for table and plotting
fit_measures_df <- do.call(rbind, invariance_fit_measures) %>%
  as.data.frame() %>%
  mutate(Model = rownames(.)) %>%
  select(Model, chisq, df, cfi.scaled, tli.scaled, rmsea.scaled, srmr) %>%
  setNames(c("Model", "Chi_Square", "df", "CFI", "TLI", "RMSEA", "SRMR")) %>%
  mutate(
    Delta_ChiSq = c(NA, diff(Chi_Square)),
    Delta_CFI = c(NA, diff(CFI)),
    Delta_TLI = c(NA, diff(TLI)),
    Delta_RMSEA = c(NA, diff(RMSEA)),
    Delta_SRMR = c(NA, diff(SRMR))
  )

# Function to create measurement invariance table
create_mi_table <- function(fit_df) {
  kable(fit_df %>%
      mutate(across(c(CFI, TLI, RMSEA, SRMR, Delta_ChiSq, Delta_CFI, Delta_TLI, Delta_RMSEA, Delta_SRMR), 
                    ~ sprintf("%.3f", .x))), 
        caption = "Measurement Invariance Results: 12 items, 2nd order model",
        col.names = c("Model", "χ²", "df", "CFI", "TLI", "RMSEA", "SRMR", 
                     "Δχ²", "ΔCFI", "ΔTLI", "ΔRMSEA", "ΔSRMR")) %>%
    kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
}

# Function to plot fit measure deltas
plot_fit_deltas <- function(fit_df) {
  delta_fit <- fit_df %>%
    select(Model, Delta_CFI, Delta_TLI, Delta_RMSEA, Delta_SRMR) %>%
    tidyr::pivot_longer(-Model, names_to = "Fit_Metric", values_to = "Delta_Value") %>%
    filter(!is.na(Delta_Value))
  
  ggplot(delta_fit, aes(x = Delta_Value, y = Model, fill = Fit_Metric)) +
    geom_bar(stat = "identity", position = "dodge") +
    geom_vline(xintercept = c(-0.01, 0.01, 0.015), 
               linetype = "dashed", color = "black", linewidth = 0.8) +
    scale_fill_paletteer_d("nationalparkcolors::Badlands") +
    scale_x_continuous(limits = c(-0.015, 0.015), 
                       breaks = seq(-0.015, 0.015, by = 0.005)) +
    labs(x = "Model Comparison", y = "Change in Fit Index", fill = "Fit Index") +
    theme_common
}

# Function to plot thresholds
plot_thresholds <- function(fit_model) {
  # Extract threshold parameters
  thresholds_config <- parameterEstimates(fit_model) %>%
    filter(op == "|") %>%
    select(lhs, rhs, group, est) %>%
    mutate(
      lhs = factor(lhs),
      rhs = factor(rhs, levels = c("t1", "t2", "t3")),
      group = factor(group, labels = lavInspect(fit_model, "group.label"))
    )
  
  # Add labels
  levels(thresholds_config$lhs) <- labels[levels(thresholds_config$lhs)]
  
  # Plot
  ggplot(thresholds_config, aes(x = rhs, y = est, color = group, group = group)) +
    geom_point(size = 3) +
    geom_line() +
    facet_wrap(~ lhs, scales = "fixed", labeller = labeller(lhs = labels)) +
    scale_color_brewer(palette = "Set2", name = "City") +
    labs(x = "Threshold", y = "Estimate") + 
    theme_common
}

7.4 Present

# Visualizations and tables
mi_table <- create_mi_table(fit_measures_df)
fit_deltas_plot <- plot_fit_deltas(fit_measures_df)
thresholds_plot <- plot_thresholds(invariance_models$configural)

# Create model structure visualizations
plot_model_structure <- function(invariance_model) {
  # Extract standardized factor loadings by group
  loadings_by_group <- standardizedSolution(invariance_model, type = "std.all") %>%
    filter(op == "=~")
  
  # Create custom node and edge dataframes for network visualization
  nodes <- data.frame(
    id = unique(c(loadings_by_group$lhs, loadings_by_group$rhs)),
    label = sapply(unique(c(loadings_by_group$lhs, loadings_by_group$rhs)), 
                  function(n) ifelse(n %in% names(labels), labels[n], n)),
    type = case_when(
      unique(c(loadings_by_group$lhs, loadings_by_group$rhs)) == "experience" ~ "second_order",
      unique(c(loadings_by_group$lhs, loadings_by_group$rhs)) %in% c("class", "teacher", "diffic", "future") ~ "first_order",
      TRUE ~ "indicator"
    )
  )
  
  edges <- loadings_by_group %>%
    select(from = lhs, to = rhs, weight = est.std, group)
  
  # Create a list to store the individual plots
  city_plots <- list()
  
  # Colors for node types
  node_colors <- c("second_order" = "black", 
                   "first_order" = "grey60", 
                   "indicator" = "white")
  
  # Create separate plots for each city
  for (g in unique(edges$group)) {
    g_edges <- edges %>% filter(group == g)
    g_graph <- graph_from_data_frame(g_edges, vertices = nodes)
    
    city_name <- c("São Paulo", "Zurich", "Montevideo")[g]
    
    # Create a new dataframe with wrapped labels
    node_labels <- nodes$label
    
    # Shorten and wrap indicator labels
    wrapped_labels <- sapply(1:length(node_labels), function(i) {
      label <- node_labels[i]
      node_id <- nodes$id[i]
      node_type <- nodes$type[i]
      
      if (node_type == "indicator") {
        # Abbreviate long labels for indicators
        if (nchar(label) > 20) {
          # First try splitting into multiple lines
          wrapped <- stringr::str_wrap(label, width = 15)
          
          # If still too long, truncate
          if (nchar(wrapped) > 40) {
            wrapped <- paste0(substr(label, 1, 17), "...")
          }
          return(wrapped)
        } else {
          return(label)
        }
      } else {
        # For factor nodes, keep as is but bold
        return(label)
      }
    })
    
    # Add the wrapped labels to the graph
    V(g_graph)$wrapped_label <- wrapped_labels[match(V(g_graph)$name, nodes$id)]
    
    # Set a seed for reproducible layout
    set.seed(42)
    
    # Create the plot
    p <- ggraph(g_graph, layout = "stress", weights = E(g_graph)$weight) +
      geom_edge_link(aes(width = weight, 
                         alpha = weight, 
                         color = weight,
                         label = sprintf("%.2f", weight)),
                    arrow = arrow(length = unit(3, "mm"), type = "closed"), 
                    end_cap = circle(4, "mm"),
                    start_cap = circle(2, "mm"),
                    angle_calc = "along",
                    label_dodge = unit(2.5, "mm"),
                    label_size = 2,
                    label_colour = "black") +
      geom_node_point(aes(fill = type, size = type),
                     shape = 21,  
                     color = "black",  
                     stroke = 1,  
                     show.legend = FALSE) +
      geom_node_text(aes(label = wrapped_label),
                    repel = TRUE,  
                    size = 3,    
                    bg.color = "white", 
                    bg.r = 0.15,
                    segment.color = "gray50",
                    segment.size = 0.4,
                    segment.alpha = 0.6,
                    fontface = ifelse(V(g_graph)$type == "indicator", "plain", "bold"),
                    max.overlaps = 20) +
      scale_edge_width(range = c(0.5, 3), guide = "none") +
      scale_edge_alpha(range = c(0.6, 1), guide = "none") +
      scale_edge_color_steps2(low = "white",
                              mid = "#E5C494FF",
                              high = "#E78AC3FF",
                              name = "Factor Loading",
                              midpoint = 0.5,
                              limits = c(0, 1),
                              guide = if(g < 3) "none" else "legend") +
      scale_fill_manual(values = node_colors, guide = "none") +
      scale_size_manual(values = c("second_order" = 15, 
                                   "first_order" = 10, 
                                   "indicator" = 6),
                       guide = "none") +
      labs(title = paste(city_name)) +
      theme_graph(base_family = "Helvetica") +
      theme(
        text = element_text(family = "Helvetica"),
        legend.position = if(g < 3) "none" else "bottom",
        plot.title = element_text(hjust = 0, size = 14, face = "bold"),
        plot.margin = margin(5, 5, 5, 5)
      ) +
      coord_cartesian(clip = "off") +
      expand_limits(x = c(-1.5, 1.5), y = c(-1.5, 1.5))
    
    # Store the plot
    city_plots[[g]] <- p
  }
  
  # Combine the plots
  combined_plot <- city_plots[[1]] + city_plots[[2]] + city_plots[[3]] + 
    plot_layout(ncol = 3) +
    plot_annotation(
      theme = theme(plot.margin = margin(5, 5, 5, 5))
    )
  
  return(combined_plot)
}

# Execute
model_structure_plot <- plot_model_structure(invariance_models$scalar)

# View
model_structure_plot

8 Latent score analysis

8.1 Functions

# Function to extract latent scores
extract_latent_scores <- function(fit_model) {
  # Get the list of EBM scores (one matrix per city)
  fs_list <- lavPredict(fit_model, type = "lv", method = "EBM")
  
  # Turn each group's matrix into a data frame that includes ID
  fs_dfs <- lapply(seq_along(fs_list), function(g) {
    mat <- fs_list[[g]]  
    idx <- fit_model@Data@case.idx[[g]]
    data.frame(
      ID = zmsp$ID[idx],
      mat,
      check.names = FALSE
    )
  })
  
  # Stack them into one data frame
  fs_df <- bind_rows(fs_dfs)
  
  return(fs_df)
}

# Function to prepare long-format data for latent scores
prepare_latent_scores_long <- function(data) {
  data %>%
    select(ID, city, class, teacher, diffic, future, experience) %>%
    pivot_longer(
      cols = c(class, teacher, diffic, future, experience),
      names_to = "factor",
      values_to = "score"
    ) %>%
    mutate(
      factor_type = ifelse(factor == "experience", "Second-Order", "First-Order"),
      factor = factor(factor, levels = c("experience", "class", "teacher", "diffic", "future")),
      factor_label = case_when(
        factor == "experience" ~ "School Experience",
        factor == "class" ~ "Bond to Class",
        factor == "teacher" ~ "Bond to Teacher",
        factor == "diffic" ~ "School Difficulties (reversed)",
        factor == "future" ~ "Future Orientation"
      )
    )
}

# Function to calculate summary statistics
calculate_factor_summaries <- function(long_data) {
  long_data %>%
    group_by(city, factor, factor_label, factor_type) %>%
    summarize(
      mean_score = mean(score, na.rm = TRUE),
      median_score = median(score, na.rm = TRUE),
      sd_score = sd(score, na.rm = TRUE),
      q25 = quantile(score, 0.25, na.rm = TRUE),
      q75 = quantile(score, 0.75, na.rm = TRUE),
      n = n(),
      # Calculate standard error
      se = sd_score / sqrt(n),
      # Calculate confidence intervals
      ci_lower = mean_score - 1.96 * se,
      ci_upper = mean_score + 1.96 * se,
      .groups = "drop"
    )
}

# Function to run ANOVA and post-hoc tests
analyze_latent_var <- function(var_name, data) {
  # Fit the one-way ANOVA
  fit <- lm(as.formula(paste0(var_name, " ~ city")), data = data)
  
  # ANOVA table
  aov_tab <- anova(fit)
  
  # Compute eta-squared
  es_tab <- eta_squared(fit, partial = TRUE)
  
  # Get the right column name
  es_col <- intersect(c("Eta2_partial", "Eta2"), names(es_tab))[1]
  
  # Extract city effect size
  city_eta2 <- es_tab %>%
    filter(Parameter == "city") %>%
    pull(!!sym(es_col))
  
  # Estimated marginal means and Tukey post-hoc
  ems <- emmeans(fit, ~ city)
  tukey <- pairs(ems, adjust = "tukey")
  
  # Return all results in a list
  list(
    anova = aov_tab,
    eta2 = city_eta2,
    emmeans = ems,
    contrasts = tukey
  )
}

# Function to plot latent means
plot_latent_means <- function(summaries) {
  ggplot(summaries, aes(x = factor_label, y = mean_score, fill = city)) +
    geom_col(position = position_dodge(width = 0.9), width = 0.8, alpha = 0.7) +
    geom_errorbar(
      aes(ymin = ci_lower, ymax = ci_upper),
      position = position_dodge(width = 0.9),
      width = 0.25
    ) +
    facet_grid(. ~ factor_type, space = "free", scales = "free_x") +
    scale_fill_brewer(palette = "Set2") +
    scale_x_discrete(labels = function(x) {
      str_replace_all(x, "(\\w+\\s+\\w+)\\s+", "\\1\n")
    }) +
    labs(x = NULL, y = "Mean Latent Score", fill = "City") +
    theme_common
}

# Function to plot latent score distributions
plot_latent_distributions <- function(long_data) {
  ggplot(long_data, aes(x = score, y = factor_label, fill = city)) +
    geom_density_ridges(alpha = 0.7, scale = 1) +
    facet_grid(factor_type ~ ., scales = "free_y", space = "free") +
    scale_fill_brewer(palette = "Set2") +
    scale_color_brewer(palette = "Set2") +
    guides(color = "none") +
    labs(x = "Latent Score", y = NULL, fill = "City") +
    theme_common
}

# Function to calculate effect sizes between cities
calculate_effect_sizes <- function(long_data) {
  # Group data
  grouped_data <- long_data %>%
    group_by(factor, factor_label, factor_type)
  
  # Get unique groups
  groups <- grouped_data %>%
    group_keys()
  
  # Calculate effect sizes for each group
  effect_sizes <- map_dfr(1:nrow(groups), function(i) {
    # Get current group data
    grp_data <- grouped_data %>%
      filter(factor == groups$factor[i])
    
    # Get data for each city
    city_data <- split(grp_data$score, grp_data$city)
    cities <- names(city_data)
    
    # Calculate Cohen's d for each pair of cities
    result <- map_dfr(1:(length(cities)-1), function(j) {
      map_dfr((j+1):length(cities), function(k) {
        city1 <- cities[j]
        city2 <- cities[k]
        x <- city_data[[city1]]
        y <- city_data[[city2]]
        
        # Calculate means and SDs
        mean_x <- mean(x, na.rm = TRUE)
        mean_y <- mean(y, na.rm = TRUE)
        sd_x <- sd(x, na.rm = TRUE)
        sd_y <- sd(y, na.rm = TRUE)
        
        # Calculate pooled SD
        pooled_sd <- sqrt(((length(x) - 1) * sd_x^2 + (length(y) - 1) * sd_y^2) / 
                          (length(x) + length(y) - 2))
        
        # Calculate Cohen's d
        cohens_d <- (mean_x - mean_y) / pooled_sd
        
        # Return results
        tibble(
          factor = groups$factor[i],
          factor_label = groups$factor_label[i],
          factor_type = groups$factor_type[i],
          comparison = paste(city1, "vs", city2),
          city1 = city1,
          city2 = city2,
          mean_diff = mean_x - mean_y,
          cohens_d = cohens_d
        )
      })
    })
    
    return(result)
  })
  
  # Create effect size heatmap
  effect_size_heatmap <- effect_sizes %>%
    select(factor_label, factor_type, comparison, cohens_d) %>%
    mutate(effect_magnitude = case_when(
      abs(cohens_d) < 0.2 ~ "Negligible",
      abs(cohens_d) < 0.5 ~ "Small",
      abs(cohens_d) < 0.8 ~ "Medium",
      TRUE ~ "Large"
    ))
  
  # Plot effect sizes
  p <- ggplot(effect_size_heatmap, aes(x = comparison, y = factor_label, fill = cohens_d)) +
    geom_tile(color = "white", linewidth = 0.5) +
    geom_text(aes(label = sprintf("%.2f", cohens_d)), color = "black", size = 3) +
    scale_fill_gradient2(
      low = "#E5C494FF",
      mid = "white", 
      high = "#E78AC3FF", 
      midpoint = 0,
      name = "Cohen's d"
    ) +
    facet_grid(factor_type ~ ., scales = "free_y", space = "free") +
    labs(x = NULL, y = NULL) +
    theme_common
  
  return(list(
    effect_sizes = effect_sizes,
    heatmap = effect_size_heatmap,
    plot = p
  ))
}

8.2 Execute latent score analysis

latent_scores <- extract_latent_scores(invariance_models$scalar)
zmsp_with_scores <- zmsp %>% left_join(latent_scores, by = "ID")
latent_scores_long <- prepare_latent_scores_long(zmsp_with_scores)
factor_summaries <- calculate_factor_summaries(latent_scores_long)

# Run ANOVA and post-hoc tests for each latent variable
infer_results <- lapply(latent_vars, analyze_latent_var, data = zmsp_with_scores)
names(infer_results) <- latent_vars

# Calculate effect sizes
effect_size_results <- calculate_effect_sizes(latent_scores_long)

8.3 Present

# Create visualizations
latent_distributions_plot <- plot_latent_distributions(latent_scores_long)
effect_size_plot <- effect_size_results$plot

# Combine plots
effect_size_plot

latent_distributions_plot

9 Notes

AI Assistance Disclosure: We used Claude (Anthropic) as a technical programming assistant primarily for R code development and implementation. The assistance focused on: (1) debugging complex R workflows using lavaan and semTools packages, (2) creating comprehensive data visualization scripts using ggplot2 and related packages, and (3) structuring reproducible analysis workflows and HTML report compilation. All research design decisions, theoretical interpretations, model specifications, and substantive conclusions remain entirely our intellectual contribution, with AI assistance serving as a technical programming tool analogous to advanced statistical software documentation with interactive problem-solving capabilities.

LS0tCnRpdGxlOiAiKipDb21wYXJpbmcgc2Nob29sIGV4cGVyaWVuY2VzIGFjcm9zcyBCcmF6aWxpYW4sIFN3aXNzLCBhbmQgVXJ1Z3VheWFuIGFkb2xlc2NlbnRzOiBhIG1lYXN1cmVtZW50IGludmFyaWFuY2Ugc3R1ZHkqKiIKYXV0aG9yOiAiKkPDqXphciBELiBMdXF1aW5lIEpyLiogWyhPUkNJRCldKGh0dHBzOi8vb3JjaWQub3JnLzAwMDAtMDAwMi01MDM4LTY4MDgpIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiAKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICAKICAgIHRoZW1lOiBzaW1wbGV4CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAKICAgIAogICAgZmlnX3dpZHRoOiAxMgogICAgZmlnX2hlaWdodDogOAogICAgZmlnX3JldGluYTogMgogICAgCmtuaXRyOgogIG9wdHNfY2h1bms6CiAgICB3YXJuaW5nOiBmYWxzZSAgICAgICAKICAgIG1lc3NhZ2U6IGZhbHNlICAgICAgIAogICAgZXJyb3I6IGZhbHNlICAgICAgICAgCiAgICBmaWcuYWxpZ246ICdjZW50ZXInICAKICAgIGNhY2hlOiB0cnVlICAgICAgICAgIAogICAgZHBpOiAzMDAgICAgICAgICAgICAKICAgCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgU2V0IGdsb2JhbCBjaHVuayBvcHRpb25zCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICB3YXJuaW5nID0gRkFMU0UsICAgICAgICAjIEhpZGUgd2FybmluZ3MKICBtZXNzYWdlID0gRkFMU0UsICAgICAgICAjIEhpZGUgbWVzc2FnZXMgIAogIGVycm9yID0gRkFMU0UsICAgICAgICAgICMgSGlkZSBlcnJvcnMKICBmaWcuYWxpZ24gPSAnY2VudGVyJywgICAjIENlbnRlciBhbGwgZmlndXJlcwogIGZpZy53aWR0aCA9IDEyLCAgICAgICAgICMgRGVmYXVsdCBmaWd1cmUgd2lkdGgKICBmaWcuaGVpZ2h0ID0gOCwgICAgICAgICAjIERlZmF1bHQgZmlndXJlIGhlaWdodAogIGZpZy5yZXRpbmEgPSAyLCAgICAgICAgICMgSGlnaCByZXNvbHV0aW9uIGZpZ3VyZXMKICBjYWNoZSA9IEZBTFNFLCAgICAgICAgICAjIFNldCB0byBUUlVFIGZvciBsYXJnZSBhbmFseXNlcwogIGRwaSA9IDMwMCwgICAgICAgICAgICAgIyBIaWdoIERQSSBmb3IgY3Jpc3AgZmlndXJlcwogIG91dC53aWR0aCA9ICIxMDAlIiwgICAgIyBGdWxsIHdpZHRoIGZpZ3VyZXMKICByZXN1bHRzID0gJ2FzaXMnICAgICAgICMgRm9yIGJldHRlciB0YWJsZSByZW5kZXJpbmcKKQpgYGAKCiMgRGF0YSBwcmVwYXJhdGlvbiBhbmQgc2V0dXAKCmBgYHtyIGRhdGFwcmVwfQojIExvYWQgcmVxdWlyZWQgbGlicmFyaWVzIChjb25zb2xpZGF0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKSAgICAjIEluY2x1ZGVzIGRwbHlyLCBnZ3Bsb3QyLCB0aWR5ciwgZXRjLgpsaWJyYXJ5KGxhdmFhbikgICAgICAgIyBGb3IgQ0ZBIGFuZCBTRU0KbGlicmFyeShzZW1Ub29scykgICAgICMgRm9yIG1lYXN1cmVtZW50IGludmFyaWFuY2UgdGVzdGluZwpsaWJyYXJ5KHBzeWNoKSAgICAgICAgIyBGb3IgcHN5Y2hvbWV0cmljIGFuYWx5c2VzCmxpYnJhcnkoa25pdHIpICAgICAgICAjIEZvciB0YWJsZXMKbGlicmFyeShrYWJsZUV4dHJhKSAgICMgRm9yIGVuaGFuY2VkIHRhYmxlcwpsaWJyYXJ5KGVtbWVhbnMpICAgICAgIyBGb3IgbWFyZ2luYWwgbWVhbnMgaW4gQU5PVkEKbGlicmFyeShlZmZlY3RzaXplKSAgICMgRm9yIGVmZmVjdCBzaXplIGNhbGN1bGF0aW9ucwpsaWJyYXJ5KHB1cnJyKSAgICAgICAgIyBGb3IgZnVuY3Rpb25hbCBwcm9ncmFtbWluZyB0b29scwpsaWJyYXJ5KG9wZW54bHN4KSAgICAgIyBGb3IgRXhjZWwgZXhwb3J0CmxpYnJhcnkocGFsZXR0ZWVyKSAgICAjIEZvciBjb2xvciBwYWxldHRlcwpsaWJyYXJ5KGdncmFwaCkgICAgICAgIyBGb3IgZ3JhcGggdmlzdWFsaXphdGlvbgpsaWJyYXJ5KGlncmFwaCkgICAgICAgIyBGb3IgbmV0d29yayBhbmFseXNpcwpsaWJyYXJ5KHBhdGNod29yaykgICAgIyBGb3IgY29tYmluaW5nIHBsb3RzCmxpYnJhcnkoc3RyaW5ncikgICAgICAjIEZvciBzdHJpbmcgbWFuaXB1bGF0aW9uCmxpYnJhcnkoZ2dyaWRnZXMpICAgICAjIEZvciByaWRnZSBwbG90cwpsaWJyYXJ5KGNvcnJwbG90KSAgICAgIyBGb3IgY29ycmVsYXRpb24gcGxvdHMKbGlicmFyeShyZXNoYXBlMikgICAgICMgRm9yIGRhdGEgcmVzaGFwaW5nCmxpYnJhcnkobmFuaWFyKSAgICAgICAjIEZvciBtaXNzaW5nIGRhdGEgdmlzdWFsaXphdGlvbgpsaWJyYXJ5KHZpc2RhdCkgICAgICAgIyBGb3IgZGF0YSB2aXN1YWxpemF0aW9uCmxpYnJhcnkoZ3RzdW1tYXJ5KSAgICAjIEZvciBzdW1tYXJ5IHRhYmxlcwpsaWJyYXJ5KHJhZ2cpICAgICAgICAgIyBGb3IgaGlnaC1xdWFsaXR5IGdyYXBoaWMgZGV2aWNlCmxpYnJhcnkoamFuaXRvcikgICAgICAjIEZvciBjbGVhbmluZyBkYXRhCgojIExvYWQgdGhlIGRhdGFzZXQKbG9hZCgiem1zcC5SRGF0YSIpCgojIERlZmluZSBpbmRpY2F0b3IgdmFyaWFibGVzIGJ5IGRpbWVuc2lvbgpkaW1lbnNpb25zIDwtIGxpc3QoCiAgYm9uZF9jbGFzcyA9IGMoJ2s2XzE4MDMnLCAnazZfMTgwNicsICdrNl8xODA5JyksCiAgYm9uZF90ZWFjaGVyID0gYygnazZfMTgwMicsICdrNl8xODA1JywgJ2s2XzE4MDgnKSwKICBmdXR1cmVfb3JpZW50YXRpb24gPSBjKCdrNl8xODExJywgJ2s2XzE4MTMnLCAnazZfMTgxNScpLAogIHNjaG9vbF9kaWZmX3IgPSBjKCdrNl8xODEwX3InLCAnazZfMTgxMl9yJywgJ2s2XzE4MTRfcicpLAogIHNjaG9vbF9jb21taXRfciA9IGMoJ2s2XzE4MDEnLCAnazZfMTgwNCcsICdrNl8xODA3X3InKQopCgojIEZsYXR0ZW4gdGhlIGxpc3QgZm9yIGNvbnZlbmllbmNlCmJvbmRfY2xhc3MgPC0gZGltZW5zaW9ucyRib25kX2NsYXNzCmJvbmRfdGVhY2hlciA8LSBkaW1lbnNpb25zJGJvbmRfdGVhY2hlcgpmdXR1cmVfb3JpZW50YXRpb24gPC0gZGltZW5zaW9ucyRmdXR1cmVfb3JpZW50YXRpb24Kc2Nob29sX2RpZmZfciA8LSBkaW1lbnNpb25zJHNjaG9vbF9kaWZmX3IKc2Nob29sX2NvbW1pdF9yIDwtIGRpbWVuc2lvbnMkc2Nob29sX2NvbW1pdF9yCgojIEFsbCBpdGVtcyBmb3IgdGhlIDE1LWl0ZW0gc2NhbGUKc2Nob29sX2V4cF9yIDwtIGMoYm9uZF9jbGFzcywgYm9uZF90ZWFjaGVyLCBzY2hvb2xfY29tbWl0X3IsIHNjaG9vbF9kaWZmX3IsIGZ1dHVyZV9vcmllbnRhdGlvbikKCiMgSXRlbXMgZm9yIHRoZSAxMi1pdGVtIHNjYWxlICh3aXRob3V0IHNjaG9vbCBjb21taXRtZW50KQpzY2hvb2xfZXhwXzEyIDwtIGMoYm9uZF9jbGFzcywgYm9uZF90ZWFjaGVyLCBzY2hvb2xfZGlmZl9yLCBmdXR1cmVfb3JpZW50YXRpb24pCgojIERlZmluZSBsYWJlbHMgZm9yIHBsb3RzIGFuZCB0YWJsZXMKbGFiZWxzIDwtIGMoCiAgZXhwZXJpZW5jZSA9ICJTY2hvb2wgRXhwZXJpZW5jZSIsCiAgY2xhc3MgPSAiQm9uZCB0byBDbGFzcyIsCiAgdGVhY2hlciA9ICJCb25kIHRvIFRlYWNoZXIiLAogIGZ1dHVyZSA9ICJGdXR1cmUgT3JpZW50YXRpb24iLAogIGRpZmZpYyA9ICJTY2hvb2wgRGlmZmljdWx0aWVzIiwKICBrNl8xODAzID0gIlNlbnNlIG9mIGNvbW11bml0eSBpbiBjbGFzcyIsCiAgazZfMTgwNiA9ICJHZXQgYWxvbmcgd2l0aCBjbGFzc21hdGVzIiwKICBrNl8xODA5ID0gIkNsYXNzbWF0ZXMgYXJlIG5pY2UgdG8gbWUiLAogIGs2XzE4MDIgPSAiVGVhY2hlciB0cmVhdHMgbWUgZmFpcmx5IiwKICBrNl8xODA1ID0gIkdldCBhbG9uZyB3aXRoIHRlYWNoZXIiLAogIGs2XzE4MDggPSAiVGVhY2hlciBoZWxwcyB3aGVuIG5lZWRlZCIsCiAgazZfMTgxMSA9ICJXb3JraW5nIHRvd2FyZHMgaW50ZXJlc3Rpbmcgam9iIiwKICBrNl8xODEzID0gIlRyeSBoYXJkIGF0IHNjaG9vbCBmb3IgZnV0dXJlIGpvYiIsCiAgazZfMTgxNSA9ICJEb2luZyB3ZWxsIGF0IHNjaG9vbCBpcyBpbXBvcnRhbnQiLAogIGs2XzE4MDEgPSAiTGlrZXMgZ29pbmcgdG8gc2Nob29sIiwKICBrNl8xODA0ID0gIkxpa2VzIGRvaW5nIGhvbWV3b3JrIiwKICBrNl8xODA3X3IgPSAiRmluZHMgc2Nob29sIHVzZWxlc3MgKHIpIiwKICBrNl8xODEwX3IgPSAiT2Z0ZW4gaGFzIGJhZCBncmFkZXMgKHIpIiwKICBrNl8xODEyX3IgPSAiTWFrZXMgbWlzdGFrZXMgaW4gaG9tZXdvcmsgKHIpIiwKICBrNl8xODE0X3IgPSAiU3RydWdnbGVzIHRvIGZvbGxvdyBsZXNzb25zIChyKSIKKQoKIyBDcmVhdGUgYSBzdWJzZXQgZm9yIGxhdGVudCB2YXJpYWJsZSBuYW1lcwpsYWJlbHNfbGF0ZW50cyA8LSBsYWJlbHNbYygiZXhwZXJpZW5jZSIsICJjbGFzcyIsICJ0ZWFjaGVyIiwgImZ1dHVyZSIsICJkaWZmaWMiKV0KCiMgRGVmaW5lIHRoZSBmb3VyLWZhY3RvciBtb2RlbCB3aXRoIHNlY29uZC1vcmRlciBmYWN0b3IKbW9kZWwgPC0gJwogIGNsYXNzICAgPX4gazZfMTgwMyArIGs2XzE4MDYgKyBrNl8xODA5CiAgdGVhY2hlciA9fiBrNl8xODAyICsgazZfMTgwNSArIGs2XzE4MDgKICBkaWZmaWMgID1+IGs2XzE4MTBfciArIGs2XzE4MTJfciArIGs2XzE4MTRfcgogIGZ1dHVyZSAgPX4gazZfMTgxMSArIGs2XzE4MTMgKyBrNl8xODE1IAogIAogICMgMm5kIG9yZGVyIGZhY3RvcgogIGV4cGVyaWVuY2UgPX4gY2xhc3MgKyB0ZWFjaGVyICsgZGlmZmljICsgZnV0dXJlCicKCiMgQ3JlYXRlIHN1YnNldHMgZm9yIGVhY2ggY2l0eQpjaXR5X2RhdGEgPC0gbGlzdCgKICBhbGwgPSB6bXNwLAogIHpwcm9zbyA9IHptc3AgJT4lIGZpbHRlcihjaXR5ID09ICJadXJpY2giKSwKICBtcHJvc28gPSB6bXNwICU+JSBmaWx0ZXIoY2l0eSA9PSAiTW9udGV2aWRlbyIpLAogIHNwcHJvc28gPSB6bXNwICU+JSBmaWx0ZXIoY2l0eSA9PSAiU8OjbyBQYXVsbyIpCikKCiMgR2V0IGNpdHkgbmFtZXMgZm9yIGl0ZXJhdGlvbgpjaXRpZXMgPC0gdW5pcXVlKHptc3AkY2l0eSkKCiMgRGVmaW5lIGxhdGVudCB2YXJpYWJsZXMgbGlzdCBmb3IgYW5hbHlzZXMKbGF0ZW50X3ZhcnMgPC0gYygiZnV0dXJlIiwgImRpZmZpYyIsICJ0ZWFjaGVyIiwgImNsYXNzIiwgImV4cGVyaWVuY2UiKQoKIyBEZWZpbmUgYSBjb21tb24gdGhlbWUgZm9yIHZpc3VhbGl6YXRpb25zCnRoZW1lX2NvbW1vbiA8LSB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVsdmV0aWNhIiksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMC41KSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JheTk1IiksCiAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgxLCAibGluZXMiKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgKQpgYGAKCiMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcwojIyBEZW1vZ3JhcGhpYyBzdW1tYXJ5CiMjIyBTZXggYW5kIGFnZQpgYGB7ciBkZW1vIDF9CgpjcmVhdGVfZGVtb2dyYXBoaWNfc3VtbWFyeSA8LSBmdW5jdGlvbihkYXRhKSB7CiAgIyBFbnN1cmUgZ2VuZGVyIGlzIHByb3Blcmx5IGZvcm1hdHRlZAogIGRhdGEkazZfMTAyIDwtIGZhY3RvcihkYXRhJGs2XzEwMiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIm1hbGUiLCAiZmVtYWxlIiksIAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJNYWxlIiwgIkZlbWFsZSIpKQogIAogICMgQ3JlYXRlIHN1bW1hcnkgc3RhdGlzdGljcwogIHRibCA8LSBkYXRhICU+JSAKICAgIHRibF9zdW1tYXJ5KAogICAgICBieSA9IGNpdHksIAogICAgICBpbmNsdWRlID0gYyhrNl8xMDIsIGs2X2FnZXllYXJzKSwgCiAgICAgIG1pc3NpbmcgPSAibm8iLAogICAgICBzdGF0aXN0aWMgPSBsaXN0KGFsbF9jb250aW51b3VzKCkgfiAie21lYW59ICh7c2R9KSIpLAogICAgICBsYWJlbCA9IGxpc3QoazZfYWdleWVhcnMgPSAiQWdlICh5ZWFycykiLCBrNl8xMDIgPSAiU2V4IikKICAgICkgJT4lCiAgICBhZGRfcCgpICU+JQogICAgbW9kaWZ5X3NwYW5uaW5nX2hlYWRlcihjKCJzdGF0XzEiLCAic3RhdF8yIiwgInN0YXRfMyIpIH4gIioqQ2l0eSoqIikKICAKICByZXR1cm4odGJsKQp9CgojIEZ1bmN0aW9uIHRvIHZpc3VhbGl6ZSBhZ2UgYnkgZ2VuZGVyIGFuZCBjaXR5CnBsb3RfYWdlX2J5X2dlbmRlcl9jaXR5IDwtIGZ1bmN0aW9uKGRhdGEpIHsKICAjIEVuc3VyZSBnZW5kZXIgaXMgcHJvcGVybHkgZm9ybWF0dGVkCiAgZGF0YSRrNl8xMDIgPC0gZmFjdG9yKGRhdGEkazZfMTAyLCAKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygibWFsZSIsICJmZW1hbGUiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk1hbGUiLCAiRmVtYWxlIikpCiAgCiAgIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCiAgc3VtbWFyeSA8LSBkYXRhICU+JQogICAgZmlsdGVyKCFpcy5uYShrNl8xMDIpKSAlPiUKICAgIGdyb3VwX2J5KGNpdHksIGs2XzEwMikgJT4lCiAgICBzdW1tYXJpemUoCiAgICAgIG1lYW5fYWdlID0gbWVhbihrNl9hZ2V5ZWFycywgbmEucm0gPSBUUlVFKSwKICAgICAgbWVkaWFuX2FnZSA9IG1lZGlhbihrNl9hZ2V5ZWFycywgbmEucm0gPSBUUlVFKSwKICAgICAgbWluX2FnZSA9IG1pbihrNl9hZ2V5ZWFycywgbmEucm0gPSBUUlVFKSwKICAgICAgbWF4X2FnZSA9IG1heChrNl9hZ2V5ZWFycywgbmEucm0gPSBUUlVFKSwKICAgICAgY291bnQgPSBuKCksCiAgICAgIHNkX2FnZSA9IHNkKGs2X2FnZXllYXJzLCBuYS5ybSA9IFRSVUUpLAogICAgICBzZV9hZ2UgPSBzZF9hZ2UgLyBzcXJ0KGNvdW50KSwKICAgICAgY2lfbG93ZXIgPSBtZWFuX2FnZSAtIDEuOTYgKiBzZV9hZ2UsCiAgICAgIGNpX3VwcGVyID0gbWVhbl9hZ2UgKyAxLjk2ICogc2VfYWdlLAogICAgICAuZ3JvdXBzID0gImRyb3AiCiAgICApCiAgCiAgIyBDcmVhdGUgcGxvdAogIHAgPC0gZGF0YSAlPiUKICAgIGZpbHRlcighaXMubmEoazZfMTAyKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBrNl9hZ2V5ZWFycykpICsKICAgIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiI0ZDOEQ2MkZGIiwgYWxwaGEgPSAwLjcsIGJpbndpZHRoID0gMSkgKwogICAgZ2VvbV92bGluZShkYXRhID0gc3VtbWFyeSwgCiAgICAgICAgICAgICAgIGFlcyh4aW50ZXJjZXB0ID0gbWVhbl9hZ2UpLCAKICAgICAgICAgICAgICAgbGluZXR5cGUgPSAic29saWQiLCAKICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCAKICAgICAgICAgICAgICAgbGluZXdpZHRoID0gMC41KSArIAogICAgZ2VvbV90ZXh0KGRhdGEgPSBzdW1tYXJ5LAogICAgICAgICAgICAgIGFlcyh4ID0gbWVhbl9hZ2UgKyAwLjMsIHkgPSA3NTAsIAogICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJNZWFuID0iLCByb3VuZChtZWFuX2FnZSwgMikpKSwKICAgICAgICAgICAgICBjb2xvciA9ICJncmV5MzAiLAogICAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICAgIGhqdXN0ID0gMCwKICAgICAgICAgICAgICBmYW1pbHkgPSAiSGVsdmV0aWNhIikgKwogICAgZmFjZXRfZ3JpZChrNl8xMDIgfiBjaXR5KSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2NhbGVzOjpicmVha3Nfd2lkdGgoMSkpICsKICAgIGxhYnMoeCA9ICJBZ2UgKHllYXJzKSIsIHkgPSAiRnJlcXVlbmN5IikgKwogICAgeWxpbSgwLDgwMCkgKwogICAgdGhlbWVfY29tbW9uCiAgCiAgcmV0dXJuKHApCn0KCiMgRXhlY3V0ZQpkZW1vX3N1bW1hcnkgPC0gY3JlYXRlX2RlbW9ncmFwaGljX3N1bW1hcnkoem1zcCkKYWdlX3Bsb3QgPC0gcGxvdF9hZ2VfYnlfZ2VuZGVyX2NpdHkoem1zcCkKCiMgVmlldwpkZW1vX3N1bW1hcnkKYWdlX3Bsb3QKYGBgCgojIyMgU8OjbyBQYXVsbwpgYGB7ciBkZW1vIDJ9CiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdncmlkZ2VzKQpsaWJyYXJ5KHBhbGV0dGVlcikKCiMgUHJlcGFyZSBTw6NvIFBhdWxvIGRhdGEKc3BfZGF0YSA8LSB6bXNwICU+JQogIGZpbHRlcihjaXR5ID09ICJTw6NvIFBhdWxvIikgJT4lCiAgbXV0YXRlKAogICAgc2V4ID0gZmFjdG9yKGs2XzEwMiwgCiAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygibWFsZSIsICJmZW1hbGUiKSwgCiAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTWFsZSIsICJGZW1hbGUiKSksCiAgICByYWNlID0gZmFjdG9yKHExMDMsCiAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoInByZXRhIiwgImJyYW5jYSIsICJwYXJkbyIsICJhbWFyZWxhIiwgImluZGlnZW5hIiksCiAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkJsYWNrIiwgIldoaXRlIiwgIk1peGVkIChQYXJkbykiLCAiWWVsbG93IiwgIkluZGlnZW5vdXMiKSksCiAgICBzZXMgPSBlc2NvcmVfZmluYWxfcG9uZCwKICAgIGFnZSA9IGs2X2FnZXllYXJzCiAgKSAlPiUKICBzZWxlY3Qoc2V4LCBhZ2UsIHJhY2UsIHNlcykKCiMgUGxvdApwc3AxIDwtIHNwX2RhdGEgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGFnZSwgc2VzKSwgbmFtZXNfdG8gPSAidmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICBtdXRhdGUodmFyaWFibGUgPSBmYWN0b3IodmFyaWFibGUsIAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJhZ2UiLCAic2VzIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkFnZSAoeWVhcnMpIiwgIlNFUyBTY29yZSIpKSkgJT4lCiAgZHJvcF9uYShyYWNlLCBzZXgsIHZhbHVlKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHkgPSByYWNlLCBmaWxsID0gc2V4KSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjcsIHNjYWxlID0gMC45KSArCiAgZmFjZXRfd3JhcCh+IHZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZV94Iiwgc3RyaXAucG9zaXRpb24gPSAiYm90dG9tIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk1hbGUiID0gIiNGRkMwMDAiLCAiRmVtYWxlIiA9ICIjNDE2OUUxIikpICsKICBsYWJzKAogICAgeCA9ICIiLAogICAgeSA9ICJSYWNlIC8gU2tpbiBjb2xvciIsCiAgICBmaWxsID0gIlNleCIKICApICsgCiAgdGhlbWVfY29tbW9uCgpwcmludChwc3AxKQoKbGlicmFyeShndHN1bW1hcnkpCgojIGd0c3VtbWFyeSB0YWJsZSBvZiBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMKCmRlbW9fc3AgPC0gc3BfZGF0YSAlPiUKICB0Ymxfc3VtbWFyeSgKICAgIGJ5ID0gcmFjZSwKICAgIGluY2x1ZGUgPSBjKHNleCwgYWdlLCBzZXMpLAogICAgc3RhdGlzdGljID0gbGlzdCgKICAgICAgYWxsX2NvbnRpbnVvdXMoKSB+ICJ7bWVhbn0gKHtzZH0pIiwKICAgICAgYWxsX2NhdGVnb3JpY2FsKCkgfiAie259ICh7cH0lKSIKICAgICksCiAgICBkaWdpdHMgPSBhbGxfY29udGludW91cygpIH4gMSwKICAgIGxhYmVsID0gbGlzdCgKICAgICAgc2V4IH4gIlNleCIsCiAgICAgIGFnZSB+ICJBZ2UgKHllYXJzKSIsCiAgICAgIHNlcyB+ICJTRVMgU2NvcmUiCiAgICApLAogICAgbWlzc2luZyA9ICJubyIKICApICU+JQogIGFkZF9uKCkgJT4lCiAgYWRkX3AoKSAlPiUKICBpdGFsaWNpemVfbGV2ZWxzKCkgJT4lCiAgYm9sZF9sYWJlbHMoKQoKIyBQcmludCB0aGUgdGFibGUKZGVtb19zcApgYGAKCiMjIyBadXJpY2gKCmBgYHtyIGRlbW8gM30KCnpfZGF0YSA8LSB6bXNwICU+JQogIGZpbHRlcihjaXR5ID09ICJadXJpY2giKSAlPiUKICBtdXRhdGUoCiAgICBlZHVjYXRpb25fZ3JvdXBfc2VzID0gY2FzZV93aGVuKAogICAgICBrNV82X2VkdW1heCA9PSAiaW5jb21wbGV0ZSBjb21wdWxzb3J5IHNjaG9vbCIgfiAiMV9EaWQgTm90IENvbXBsZXRlIENvbXB1bHNvcnkgU2Nob29sIiwKCiAgICAgICMgMi4gVXBwZXIgU2Vjb25kYXJ5IFZvY2F0aW9uYWwKICAgICAgazVfNl9lZHVtYXggJWluJSBjKAogICAgICAgICJjb21wdWxzb3J5IHNjaG9vbCwgZWxlbWVudGFyeSB2b2NhdGlvbmFsIHRyYWluaW5nIiwKICAgICAgICAiZG9tZXN0aWMgc2NpZW5jZSBjb3Vyc2UsIDEteWVhciBzY2hvb2wgb2YgY29tbWVyY2UiLAogICAgICAgICJhcHByZW50aWNlc2hpcCIsCiAgICAgICAgImZ1bGwgdGltZSB2b2NhdGlvbmFsIHNjaG9vbCIKICAgICAgKSB+ICIyX1VwcGVyIFNlY29uZGFyeSBWb2NhdGlvbmFsIiwKCiAgICAgICMgMy4gVXBwZXIgU2Vjb25kYXJ5IEFjYWRlbWljIChNYXR1cmEpCiAgICAgIGs1XzZfZWR1bWF4ID09ICJBLWxldmVscyIgfiAiM19VcHBlciBTZWNvbmRhcnkgQWNhZGVtaWMgKE1hdHVyYSkiLAoKICAgICAgIyA0LiBUZXJ0aWFyeSBWb2NhdGlvbmFsL1Byb2Zlc3Npb25hbAogICAgICBrNV82X2VkdW1heCAlaW4lIGMoCiAgICAgICAgInZvY2F0aW9uYWwgaGlnaCBlZHVjYXRpb24iLAogICAgICAgICJ0ZWNobmljYWwgc2Nob29sIG9yIHZvY2F0aW9uYWwgY29sbGVnZSIsCiAgICAgICAgInZvY2F0aW9uYWwgaGlnaCBzY2hvb2wsIGhpZ2hlciBzcGVjaWFsaXplZCBzY2hvb2wiCiAgICAgICkgfiAiNF9UZXJ0aWFyeSBWb2NhdGlvbmFsL1Byb2Zlc3Npb25hbCIsCgogICAgICAjIDUuIFRlcnRpYXJ5IEFjYWRlbWljL1VuaXZlcnNpdHkKICAgICAgazVfNl9lZHVtYXggPT0gInVuaXZlcnNpdHksIFN3aXNzIEZlZGVyYWwgSW5zdGl0dXRlIG9mIFRlY2hub2xvZ3lceDk0IiB+ICI1X1RlcnRpYXJ5IEFjYWRlbWljL1VuaXZlcnNpdHkiLAoKICAgICAgIyBIYW5kbGUgIm1pc3NpbmciIGxldmVsIGFuZCB0cnVlIE5BcwogICAgICBrNV82X2VkdW1heCA9PSAibWlzc2luZyIgfiBOQV9jaGFyYWN0ZXJfLCAKICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8gCiAgICApLAogICAgZWR1Y2F0aW9uX2dyb3VwX3NlcyA9IGZhY3RvcigKICAgICAgZWR1Y2F0aW9uX2dyb3VwX3NlcywKICAgICAgbGV2ZWxzID0gYygKICAgICAgICAiMV9EaWQgTm90IENvbXBsZXRlIENvbXB1bHNvcnkgU2Nob29sIiwKICAgICAgICAiMl9VcHBlciBTZWNvbmRhcnkgVm9jYXRpb25hbCIsCiAgICAgICAgIjNfVXBwZXIgU2Vjb25kYXJ5IEFjYWRlbWljIChNYXR1cmEpIiwKICAgICAgICAiNF9UZXJ0aWFyeSBWb2NhdGlvbmFsL1Byb2Zlc3Npb25hbCIsCiAgICAgICAgIjVfVGVydGlhcnkgQWNhZGVtaWMvVW5pdmVyc2l0eSIKICAgICAgKSwKICAgICAgICBsYWJlbHMgPSBjKAogICAgICAgICJEaWQgTm90IENvbXBsZXRlXG5Db21wdWxzb3J5IFNjaG9vbCIsCiAgICAgICAgIlVwcGVyIFNlY29uZGFyeVxuVm9jYXRpb25hbCIsCiAgICAgICAgIlVwcGVyIFNlY29uZGFyeVxuQWNhZGVtaWMgKE1hdHVyYSkiLAogICAgICAgICJUZXJ0aWFyeVxuVm9jYXRpb25hbC9Qcm9mZXNzaW9uYWwiLAogICAgICAgICJUZXJ0aWFyeVxuQWNhZGVtaWMvVW5pdmVyc2l0eSIKICAgICAgKQogICAgKQogICkgJT4lCiAgbXV0YXRlKAogICAgc2V4ID0gZmFjdG9yKGs2XzEwMiwgbGV2ZWxzID0gYygibWFsZSIsICJmZW1hbGUiKSwgbGFiZWxzID0gYygiTWFsZSIsICJGZW1hbGUiKSksCiAgICBtaWdyYXRpb25iYWNrID0gZmFjdG9yKGs1XzZfbWlnaGgyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBdCBsZWFzdCBvbmUgcGFyZW50IGJvcm4gaW4gU3dpdHplcmxhbmQiLCAiQm90aCBwYXJlbnRzIGJvcm4gYWJyb2FkIikpLAogICAgc2VzID0gZWR1Y2F0aW9uX2dyb3VwX3NlcywKICAgIGFnZSA9IGs2X2FnZXllYXJzCiAgKQoKel9kYXRhX2Zvcl9wbG90IDwtIHpfZGF0YSAlPiUKICBzZWxlY3QoYWdlLCBzZXMsIHNleCwgbWlncmF0aW9uYmFjaykgJT4lCiAgZHJvcF9uYSgpCgojIFBsb3QKcHoxIDwtIHpfZGF0YV9mb3JfcGxvdCAlPiUKICBnZ3Bsb3QoYWVzKHggPSBhZ2UsIGZpbGwgPSBzZXgpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC43KSArIAogIGZhY2V0X2dyaWQoc2VzIH4gbWlncmF0aW9uYmFjaywgCiAgICAgICAgICAgICBzY2FsZXMgPSAiZml4ZWQiLCAKICAgICAgICAgICAgIHN3aXRjaCA9ICJ5IikgKyAjIFJvd3MgYnkgU0VTLCBDb2xzIGJ5IE1pZ3JhdGlvbgogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk1hbGUiID0gInJlZCIsICJGZW1hbGUiID0gImdyZXk5MCIpKSArCiAgbGFicygKICAgIHggPSAiQWdlICh5ZWFycykiLAogICAgeSA9ICJEZW5zaXR5IiwKICAgIGZpbGwgPSAiU2V4IgogICkgKyAKICB0aGVtZV9jb21tb24KCnByaW50KHB6MSkKCmxpYnJhcnkoZ3RzdW1tYXJ5KQoKIyBndHN1bW1hcnkgdGFibGUgb2YgZGVtb2dyYXBoaWMgdmFyaWFibGVzCgpkZW1vX3ogPC0gel9kYXRhICU+JQogIHNlbGVjdChzZXgsIGFnZSwgbWlncmF0aW9uYmFjaywgc2VzKSAlPiUKICB0Ymxfc3VtbWFyeSgKICAgIGJ5ID0gbWlncmF0aW9uYmFjaywKICAgIGluY2x1ZGUgPSBjKHNleCwgYWdlLCBzZXMpLAogICAgc3RhdGlzdGljID0gbGlzdCgKICAgICAgYWxsX2NvbnRpbnVvdXMoKSB+ICJ7bWVhbn0gKHtzZH0pIiwKICAgICAgYWxsX2NhdGVnb3JpY2FsKCkgfiAie259ICh7cH0lKSIKICAgICksCiAgICBkaWdpdHMgPSBhbGxfY29udGludW91cygpIH4gMiwKICAgIGxhYmVsID0gbGlzdCgKICAgICAgc2V4IH4gIlNleCIsCiAgICAgIGFnZSB+ICJBZ2UgKHllYXJzKSIsCiAgICAgIHNlcyB+ICJGYW1pbHkgZWR1Y2F0aW9uIGxldmVsIgogICAgKSwKICAgIG1pc3NpbmcgPSAibm8iCiAgKSAlPiUKICBhZGRfbigpICU+JQogIGFkZF9wKCkgJT4lCiAgaXRhbGljaXplX2xldmVscygpICU+JQogIGJvbGRfbGFiZWxzKCkKCiMgUHJpbnQgdGhlIHRhYmxlCmRlbW9fegoKYGBgCgojIyMgTW9udGV2aWRlbwoKYGBge3IgZGVtbyA0fQoKem1zcCA8LSB6bXNwICU+JQogIG11dGF0ZSgKICAgIGVkdWNhdGlvbl9sZXZlbF8zY2F0ID0gY2FzZV93aGVuKAogICAgICAjIFByaW1hcnkgZWR1Y2F0aW9uOiAiTmluZ3VubyIgKDEpIGFuZCAiRXNjdWVsYSBQcmltYXJpYSIgKDIpCiAgICAgIG1heF9lZHVjICVpbiUgYygxLCAyKSB+ICIxLiBQcmltYXJ5IiwKCiAgICAgICMgU2Vjb25kYXJ5IGVkdWNhdGlvbjogIkN1cnNvIFTDqWNuaWNvIGRlIGxhIFVUVSIgKDMpLCAiQ2ljbG8gQsOhc2ljbyIgKDQpLCAiQmFjaGlsbGVyYXRvIFNlY3VuZGFyaWEiICg1KQogICAgICBtYXhfZWR1YyAlaW4lIGMoMywgNCwgNSkgfiAiMi4gU2VjdW5kYXJ5IiwKCiAgICAgICMgVGVydGlhcnkgZWR1Y2F0aW9uOiAiRXNjdWVsYSBEZXBhcnRhbWVudGFsIiAoNikgdGhyb3VnaCAiUG9zZ3JhZG9zIFVuaXZlcnNpdGFyaW9zIiAoMTApCiAgICAgIG1heF9lZHVjICVpbiUgYyg2LCA3LCA4LCA5LCAxMCkgfiAiMy4gVGVyY2lhcnkiLAoKICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8gIyBIYW5kbGVzIGFueSB1bmV4cGVjdGVkIG51bWVyaWMgdmFsdWVzLCBhc3NpZ25pbmcgTkEKICAgICkKICApCgojIFRhYmxlCnptc3AkZWR1Y2F0aW9uX2xldmVsXzNjYXQgPC0gZmFjdG9yKAogIHptc3AkZWR1Y2F0aW9uX2xldmVsXzNjYXQsCiAgbGV2ZWxzID0gYygiMS4gUHJpbWFyeSIsICIyLiBTZWN1bmRhcnkiLCAiMy4gVGVyY2lhcnkiKSwKICBsYWJlbHMgPSBjKCJQcmltYXJ5IiwgIlNlY29uZGFyeSIsICJUZXJjaWFyeSIpLAogIG9yZGVyZWQgPSBUUlVFCikKCmRlbW9fbSA8LSB6bXNwICU+JQogIGZpbHRlcihjaXR5ID09ICJNb250ZXZpZGVvIikgJT4lCiAgbXV0YXRlKGs2X2FnZXllYXJzID0gYXMubnVtZXJpYyhrNl9hZ2V5ZWFycykpICU+JQogIHNlbGVjdChrNl8xMDIsIGs2X2FnZXllYXJzLCBlZHVjYXRpb25fbGV2ZWxfM2NhdCkgJT4lCiAgdGJsX3N1bW1hcnkoCiAgICBieSA9IGs2XzEwMiwKICAgIGluY2x1ZGUgPSBjKGs2X2FnZXllYXJzLCBlZHVjYXRpb25fbGV2ZWxfM2NhdCksCiAgICB0eXBlID0gbGlzdChrNl9hZ2V5ZWFycyB+ICJjb250aW51b3VzIiksCiAgICBzdGF0aXN0aWMgPSBsaXN0KAogICAgICBhbGxfY29udGludW91cygpIH4gInttZWFufSAoe3NkfSkiLAogICAgICBhbGxfY2F0ZWdvcmljYWwoKSB+ICJ7bn0gKHtwfSUpIgogICAgKSwKICAgIGRpZ2l0cyA9IGFsbF9jb250aW51b3VzKCkgfiAxLAogICAgbGFiZWwgPSBsaXN0KAogICAgICBrNl9hZ2V5ZWFycyB+ICJBZ2UgKHllYXJzKSIsCiAgICAgIGVkdWNhdGlvbl9sZXZlbF8zY2F0IH4gIkZhbWlseSBlZHVjYXRpb24gbGV2ZWwiCiAgICApLAogICAgbWlzc2luZyA9ICJubyIKICApICU+JQogIGFkZF9uKCkgJT4lCiAgYWRkX3AoKSAlPiUKICBpdGFsaWNpemVfbGV2ZWxzKCkgJT4lCiAgYm9sZF9sYWJlbHMoKQoKIyBQcmludCB0aGUgdGFibGUKZGVtb19tCgojIFBsb3QKbV9kYXRhIDwtIHptc3AgJT4lCiAgZmlsdGVyKGNpdHkgPT0gIk1vbnRldmlkZW8iKSAlPiUKICBzZWxlY3QoazZfMTAyLCBrNl9hZ2V5ZWFycywgZWR1Y2F0aW9uX2xldmVsXzNjYXQpICU+JQogIG11dGF0ZSgKICAgIGs2X2FnZXllYXJzID0gYXMubnVtZXJpYyhrNl9hZ2V5ZWFycyksCiAgICBnZW5kZXIgPSBmYWN0b3IoazZfMTAyLCAKICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJtYWxlIiwgImZlbWFsZSIpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk1hbGUiLCAiRmVtYWxlIikpCiAgKSAlPiUKICBkcm9wX25hKCkKCmBgYAoKIyMgSXRlbSByZXNwb25zZXMKYGBge3IgaXRlbX0KCmNyZWF0ZV9sb25nX2Zvcm1hdF9yZXNwb25zZXMgPC0gZnVuY3Rpb24oZGF0YSwgdmFycykgewogICMgSWRlbnRpZnkgYWxsIGZhY3RvciBsZXZlbHMgYWNyb3NzIHNlbGVjdGVkIGNvbHVtbnMKICBhbGxfbGV2ZWxzIDwtIHVuaXF1ZSh1bmxpc3QobGFwcGx5KGRhdGFbdmFyc10sIGxldmVscykpKQogIAogICMgQ3JlYXRlIGxvbmcgZm9ybWF0IGRhdGEKICBsb25nX2RhdGEgPC0gZGF0YSAlPiUKICAgIHNlbGVjdChjaXR5LCBhbGxfb2YodmFycykpICU+JQogICAgbXV0YXRlKGFjcm9zcyhhbGxfb2YodmFycyksIH4gZmFjdG9yKC4sIGxldmVscyA9IGFsbF9sZXZlbHMsIG9yZGVyZWQgPSBUUlVFKSkpICU+JQogICAgcGl2b3RfbG9uZ2VyKAogICAgICBjb2xzID0gYWxsX29mKHZhcnMpLAogICAgICBuYW1lc190byA9ICJpdGVtIiwKICAgICAgdmFsdWVzX3RvID0gInJlc3BvbnNlIgogICAgKSAlPiUKICAgIG11dGF0ZShyZXNwb25zZSA9IGZhY3RvcihyZXNwb25zZSwgbGV2ZWxzID0gYWxsX2xldmVscywgb3JkZXJlZCA9IFRSVUUpKQogIAogICMgU3VtbWFyaXplIHBlcmNlbnRhZ2VzCiAgcmVzdWx0IDwtIGxvbmdfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGNpdHksIGl0ZW0sIHJlc3BvbnNlKSAlPiUKICAgIHN1bW1hcmlzZShuID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICAgIGdyb3VwX2J5KGNpdHksIGl0ZW0pICU+JQogICAgbXV0YXRlKHBlcmNlbnQgPSByb3VuZCgxMDAgKiBuIC8gc3VtKG4pLCAxKSkgJT4lCiAgICB1bmdyb3VwKCkKICAKICByZXR1cm4ocmVzdWx0KQp9CgojIEZ1bmN0aW9uIHRvIHZpc3VhbGl6ZSBpdGVtIHJlc3BvbnNlcwpwbG90X2l0ZW1fcmVzcG9uc2VzIDwtIGZ1bmN0aW9uKGxvbmdfZGF0YSkgewogIGdncGxvdChuYS5vbWl0KGxvbmdfZGF0YSksIAogICAgICAgICBhZXMoeCA9IGNpdHksIHkgPSBwZXJjZW50LCBmaWxsID0gcmVzcG9uc2UpKSArCiAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJmaWxsIikgKwogICAgc2NhbGVfZmlsbF9wYWxldHRlZXJfZCgKICAgICAgcGFsZXR0ZSA9ICJuYXRpb25hbHBhcmtjb2xvcnM6OkJhZGxhbmRzIiwKICAgICAgbGFiZWxzID0gYygiRmFsc2UiLCAiTW9yZSBmYWxzZSB0aGFuIHRydWUiLCAiTW9yZSB0cnVlIHRoYW4gZmFsc2UiLCAiVHJ1ZSIpCiAgICApICsKICAgIGZhY2V0X3dyYXAofiBpdGVtLCBsYWJlbGxlciA9IGxhYmVsbGVyKGl0ZW0gPSBsYWJlbHMpLCBuY29sID0gNSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoc2NhbGUgPSAxMDApKSArCiAgICBsYWJzKHggPSAiIiwgeSA9ICIiLCBmaWxsID0gIlJlc3BvbnNlIikgKwogICAgdGhlbWVfY29tbW9uICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAwLjUpKQp9CgojIEV4ZWN1dGUKcmVzcG9uc2VzX2xvbmcgPC0gY3JlYXRlX2xvbmdfZm9ybWF0X3Jlc3BvbnNlcyh6bXNwLCBzY2hvb2xfZXhwX3IpCnJlc3BvbnNlc19wbG90IDwtIHBsb3RfaXRlbV9yZXNwb25zZXMocmVzcG9uc2VzX2xvbmcpCgojIFZpZXcKcmVzcG9uc2VzX3Bsb3QKYGBgCgojIE1pc3NpbmcgZGF0YSBhbmFseXNpcwoKYGBge3J9CgojIDEuIEJhc2ljIG1pc3NpbmcgZGF0YSBzdW1tYXJ5CmdldF9taXNzaW5nX3N1bW1hcnkgPC0gZnVuY3Rpb24oZGF0YSwgdmFyaWFibGVzKSB7CiAgbWlzc192YXJfc3VtbWFyeShkYXRhW3ZhcmlhYmxlc10pCn0KCiMgMi4gTWlzc2luZyBkYXRhIHZpc3VhbGl6YXRpb24KcGxvdF9taXNzaW5nX3BhdHRlcm4gPC0gZnVuY3Rpb24oZGF0YSwgdmFyaWFibGVzLCBsYWJlbHMpIHsKICB2aXNfZGF0YSA8LSBkYXRhW3ZhcmlhYmxlc10KICBuYW1lcyh2aXNfZGF0YSkgPC0gbGFiZWxzW25hbWVzKHZpc19kYXRhKV0KICAKICB2aXNfbWlzcyh2aXNfZGF0YSwgY2x1c3RlciA9IFRSVUUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKAogICAgICB2YWx1ZXMgPSBjKCJUUlVFIiA9ICIjRkM4RDYyRkYiLCAiRkFMU0UiID0gImdyZXk5MCIpLAogICAgICBuYW1lID0gIk1pc3NpbmciLAogICAgICBsYWJlbHMgPSBjKCJUUlVFIiA9ICJZZXMiLCAiRkFMU0UiID0gIk5vIikKICAgICkgKwogICAgdGhlbWVfY29tbW9uICsKICAgIHRoZW1lKAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDAsIHNpemUgPSA4KSwKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpCiAgICApICsKICAgIGxhYnMoeSA9ICJTdHVkZW50cyIpCn0KCiMgMy4gUGFydGljaXBhbnQtbGV2ZWwgbWlzc2luZyBkYXRhCmNhbGN1bGF0ZV9wYXJ0aWNpcGFudF9taXNzaW5nIDwtIGZ1bmN0aW9uKGRhdGEsIHZhcmlhYmxlcykgewogIG1pc3NpbmdfYnlfcGFydGljaXBhbnQgPC0gZGF0YS5mcmFtZSgKICAgIHBhcnRpY2lwYW50X2lkID0gMTpucm93KGRhdGEpLAogICAgbWlzc2luZ19jb3VudCA9IHJvd1N1bXMoaXMubmEoZGF0YVt2YXJpYWJsZXNdKSksCiAgICBtaXNzaW5nX3BlcmNlbnQgPSByb3dTdW1zKGlzLm5hKGRhdGFbdmFyaWFibGVzXSkpIC8gbGVuZ3RoKHZhcmlhYmxlcykgKiAxMDAKICApCiAgCiAgIyBTdW1tYXJ5IHN0YXRpc3RpY3MKICBtaXNzaW5nX3N0YXRzIDwtIG1pc3NpbmdfYnlfcGFydGljaXBhbnQgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIG1pbl9taXNzaW5nID0gbWluKG1pc3NpbmdfY291bnQpLAogICAgICBtYXhfbWlzc2luZyA9IG1heChtaXNzaW5nX2NvdW50KSwKICAgICAgbWVhbl9taXNzaW5nID0gbWVhbihtaXNzaW5nX2NvdW50KSwKICAgICAgbWVkaWFuX21pc3NpbmcgPSBtZWRpYW4obWlzc2luZ19jb3VudCksCiAgICAgIHBhcnRpY2lwYW50c19jb21wbGV0ZSA9IHN1bShtaXNzaW5nX2NvdW50ID09IDApLAogICAgICBwYXJ0aWNpcGFudHNfY29tcGxldGVfcGN0ID0gcGFydGljaXBhbnRzX2NvbXBsZXRlIC8gbigpICogMTAwLAogICAgICBwYXJ0aWNpcGFudHNfb3Zlcl81MHBjdCA9IHN1bShtaXNzaW5nX3BlcmNlbnQgPiA1MCkKICAgICkKICAKICBsaXN0KAogICAgYnlfcGFydGljaXBhbnQgPSBtaXNzaW5nX2J5X3BhcnRpY2lwYW50LAogICAgc3VtbWFyeV9zdGF0cyA9IG1pc3Npbmdfc3RhdHMKICApCn0KCiMgNC4gTWlzc2luZyBkYXRhIGNvcnJlbGF0aW9uIGFuYWx5c2lzCmNhbGN1bGF0ZV9taXNzaW5nX2NvcnJlbGF0aW9ucyA8LSBmdW5jdGlvbihkYXRhLCB2YXJpYWJsZXMpIHsKICBzaGFkb3dfbWF0cml4IDwtIGRhdGEgJT4lCiAgICBzZWxlY3QoYWxsX29mKHZhcmlhYmxlcykpICU+JQogICAgaXMubmEoKQogIAogIGNvcihhcy5kYXRhLmZyYW1lKHNoYWRvd19tYXRyaXgpKQp9CgojIDUuIE1pc3NpbmcgY29ycmVsYXRpb24gaGVhdG1hcApwbG90X21pc3NpbmdfY29ycmVsYXRpb25zIDwtIGZ1bmN0aW9uKG1pc3NfY29ycl9tYXRyaXgsIGxhYmVscykgewogIG1pc3NfY29ycl9kZiA8LSBhcy5kYXRhLmZyYW1lKGFzLnRhYmxlKG1pc3NfY29ycl9tYXRyaXgpKSAlPiUKICAgIHJlbmFtZSh4ID0gVmFyMSwgeSA9IFZhcjIsIGNvcnJlbGF0aW9uID0gRnJlcSkgJT4lCiAgICBmaWx0ZXIoeCAhPSB5KSAlPiUKICAgIG11dGF0ZSgKICAgICAgeF9sYWJlbCA9IGxhYmVsc1thcy5jaGFyYWN0ZXIoeCldLAogICAgICB5X2xhYmVsID0gbGFiZWxzW2FzLmNoYXJhY3Rlcih5KV0KICAgICkKICAKICBnZ3Bsb3QobWlzc19jb3JyX2RmLCBhZXMoeCA9IHhfbGFiZWwsIHkgPSB5X2xhYmVsLCBmaWxsID0gY29ycmVsYXRpb24pKSArCiAgICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjUpICsKICAgIGdlb21fdGV4dCgKICAgICAgYWVzKGxhYmVsID0gc3ByaW50ZigiJS4yZiIsIGNvcnJlbGF0aW9uKSksCiAgICAgIGNvbG9yID0gaWZlbHNlKGFicyhtaXNzX2NvcnJfZGYkY29ycmVsYXRpb24pID4gMC41LCAiYmxhY2siLCAiZ3JleTMwIiksCiAgICAgIHNpemUgPSAyLjUKICAgICkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIoCiAgICAgIGxvdyA9ICIjRTc4QUMzRkYiLAogICAgICBoaWdoID0gIiM4REEwQ0JGRiIsCiAgICAgIG1pZHBvaW50ID0gMC41LAogICAgICBsaW1pdHMgPSBjKDAsIDEpLAogICAgICBuYW1lID0gIkNvcnJlbGF0aW9uIgogICAgKSArCiAgICB0aGVtZV9jb21tb24gKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSA4KSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgaGp1c3QgPSAxLCBzaXplID0gNiksCiAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpCiAgICApCn0KCiMgRXhlY3V0ZSBhbmFseXNpcyBzdGVwIGJ5IHN0ZXAKbWlzc2luZ19zdW1tYXJ5IDwtIGdldF9taXNzaW5nX3N1bW1hcnkoem1zcCwgc2Nob29sX2V4cF9yKQptaXNzaW5nX3BhdHRlcm5fcGxvdCA8LSBwbG90X21pc3NpbmdfcGF0dGVybih6bXNwLCBzY2hvb2xfZXhwX3IsIGxhYmVscykKcGFydGljaXBhbnRfbWlzc2luZyA8LSBjYWxjdWxhdGVfcGFydGljaXBhbnRfbWlzc2luZyh6bXNwLCBzY2hvb2xfZXhwX3IpCm1pc3NpbmdfY29ycmVsYXRpb25zIDwtIGNhbGN1bGF0ZV9taXNzaW5nX2NvcnJlbGF0aW9ucyh6bXNwLCBzY2hvb2xfZXhwX3IpCm1pc3NpbmdfY29ycl9wbG90IDwtIHBsb3RfbWlzc2luZ19jb3JyZWxhdGlvbnMobWlzc2luZ19jb3JyZWxhdGlvbnMsIGxhYmVscykKCiMgVmlldyByZXN1bHRzCgptaXNzaW5nX3BhdHRlcm5fcGxvdAptaXNzaW5nX2NvcnJfcGxvdApgYGAKCgojIENvcnJlbGF0aW9uIGFuZCByZWxpYWJpbGl0eSBhbmFseXNpcwpgYGB7ciBjb3JyIDF9CiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIGNvcnJlbGF0aW9uIG1hdHJpeApjYWxjdWxhdGVfY29ycmVsYXRpb25fbWF0cml4IDwtIGZ1bmN0aW9uKGRhdGEsIHZhcmlhYmxlcywgbWV0aG9kID0gInNwZWFybWFuIikgewogIGNvcnJfbWF0cml4IDwtIGNvcihhcy5kYXRhLmZyYW1lKGxhcHBseShkYXRhWywgdmFyaWFibGVzXSwgYXMubnVtZXJpYykpLCAKICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSBtZXRob2QsIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKQogIHJldHVybihjb3JyX21hdHJpeCkKfQoKIyBGdW5jdGlvbiB0byB2aXN1YWxpemUgY29ycmVsYXRpb24gbWF0cml4CnBsb3RfY29ycmVsYXRpb25fbWF0cml4IDwtIGZ1bmN0aW9uKGNvcnJfbWF0cml4LCB2YXJfbGFiZWxzKSB7CiAgIyBQcmVwYXJlIG1hdHJpeCB3aXRoIHByb3BlciBsYWJlbHMKICBsYWJlbGVkX21hdHJpeCA8LSBjb3JyX21hdHJpeAogIHJvd25hbWVzKGxhYmVsZWRfbWF0cml4KSA8LSB2YXJfbGFiZWxzW3Jvd25hbWVzKGNvcnJfbWF0cml4KV0KICBjb2xuYW1lcyhsYWJlbGVkX21hdHJpeCkgPC0gdmFyX2xhYmVsc1tjb2xuYW1lcyhjb3JyX21hdHJpeCldCiAgCiAgIyBGdW5jdGlvbiB0byB0cnVuY2F0ZSBsb25nIG5hbWVzCiAgc21hcnRfdHJ1bmNhdGUgPC0gZnVuY3Rpb24obmFtZXMsIG1heF9sZW4gPSAxMikgewogICAgc2FwcGx5KG5hbWVzLCBmdW5jdGlvbihuKSB7CiAgICAgIGlmIChuY2hhcihuKSA8PSBtYXhfbGVuKSByZXR1cm4obikKICAgICAgcGFzdGUwKHN1YnN0cihuLCAxLCBtYXhfbGVuIC0gMyksICIuLi4iKQogICAgfSkKICB9CiAgCiAgY29sbmFtZXMobGFiZWxlZF9tYXRyaXgpIDwtIHNtYXJ0X3RydW5jYXRlKGNvbG5hbWVzKGxhYmVsZWRfbWF0cml4KSwgbWF4X2xlbiA9IDEwKQogIAogICMgQ3JlYXRlIGNvcnJlbGF0aW9uIHBsb3QKICBjb3JycGxvdChsYWJlbGVkX21hdHJpeCwgCiAgICAgICAgICBtZXRob2QgPSAic2hhZGUiLCAKICAgICAgICAgIG9yZGVyID0gIm9yaWdpbmFsIiwgCiAgICAgICAgICB0bC5jb2wgPSAiYmxhY2siLAogICAgICAgICAgYWRkZ3JpZC5jb2wgPSAnd2hpdGUnLAogICAgICAgICAgdGwuc3J0ID0gNDUsCiAgICAgICAgICB0bC5jZXggPSAwLjcsCiAgICAgICAgICBjb2wgPSBDT0wyKCJQdU9yIiwgMjApLAogICAgICAgICAgY29sLmxpbSA9IGMoLTAuMSwgMSksCiAgICAgICAgICBkaWFnID0gRkFMU0UpCn0KCiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIENyb25iYWNoJ3MgYWxwaGEgZm9yIHN1YnNjYWxlcwpjYWxjdWxhdGVfcmVsaWFiaWxpdHkgPC0gZnVuY3Rpb24oZGF0YSwgc3Vic2NhbGVzKSB7CiAgIyBDYWxjdWxhdGUgY29ycmVsYXRpb24gbWF0cml4CiAgY29ycl9tYXRyaXggPC0gY2FsY3VsYXRlX2NvcnJlbGF0aW9uX21hdHJpeChkYXRhLCB1bmxpc3Qoc3Vic2NhbGVzKSkKICAKICAjIENhbGN1bGF0ZSBhbHBoYSBmb3IgZWFjaCBzdWJzY2FsZQogIGFscGhhcyA8LSBzYXBwbHkoc3Vic2NhbGVzLCBmdW5jdGlvbihzY2FsZSkgewogICAgcmVzdWx0IDwtIGFscGhhKGNvcnJfbWF0cml4W3NjYWxlLCBzY2FsZV0pCiAgICByZXR1cm4ocmVzdWx0JHRvdGFsJHJhd19hbHBoYSkKICB9KQogIAogICMgQ2FsY3VsYXRlIG92ZXJhbGwgYWxwaGEKICBvdmVyYWxsX2FscGhhIDwtIGFscGhhKGNvcnJfbWF0cml4KSR0b3RhbCRyYXdfYWxwaGEKICAKICAjIENyZWF0ZSBzdW1tYXJ5IGRhdGFmcmFtZQogIGFscGhhX2RmIDwtIGRhdGEuZnJhbWUoCiAgICBTdWJzY2FsZSA9IGMobmFtZXMoYWxwaGFzKSwgIkZ1bGwgU2NhbGUiKSwKICAgIEFscGhhID0gYyhhbHBoYXMsIG92ZXJhbGxfYWxwaGEpCiAgKQogIAogIHJldHVybihhbHBoYV9kZikKfQoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgcmVsaWFiaWxpdHkgYnkgY2l0eQpjYWxjdWxhdGVfcmVsaWFiaWxpdHlfYnlfY2l0eSA8LSBmdW5jdGlvbihkYXRhLCBzdWJzY2FsZXMsIGNpdGllcykgewogICMgSW5pdGlhbGl6ZSByZXN1bHRzIGxpc3QKICByZXN1bHRzIDwtIGxpc3QoKQogIAogICMgR2V0IG92ZXJhbGwgcmVsaWFiaWxpdHkKICBvdmVyYWxsX2NvcnIgPC0gY2FsY3VsYXRlX2NvcnJlbGF0aW9uX21hdHJpeChkYXRhLCB1bmxpc3Qoc3Vic2NhbGVzKSkKICAKICAjIENhbGN1bGF0ZSBvdmVyYWxsIGFscGhhcwogIG92ZXJhbGxfYWxwaGFzIDwtIHNhcHBseShzdWJzY2FsZXMsIGZ1bmN0aW9uKHNjYWxlKSB7CiAgICBhbHBoYShvdmVyYWxsX2NvcnJbc2NhbGUsIHNjYWxlXSkkdG90YWwkcmF3X2FscGhhCiAgfSkKICAKICBvdmVyYWxsX3RvdGFsX2FscGhhIDwtIGFscGhhKG92ZXJhbGxfY29ycikkdG90YWwkcmF3X2FscGhhCiAgCiAgIyBDcmVhdGUgYmFzZSBkYXRhZnJhbWUKICBhbHBoYV9kZiA8LSBkYXRhLmZyYW1lKAogICAgU3Vic2NhbGUgPSBjKG5hbWVzKHN1YnNjYWxlcyksICJGdWxsIFNjYWxlIiksCiAgICBPdmVyYWxsID0gYyhvdmVyYWxsX2FscGhhcywgb3ZlcmFsbF90b3RhbF9hbHBoYSkKICApCiAgCiAgIyBBZGQgY2l0eS1zcGVjaWZpYyBhbHBoYXMKICBmb3IgKGNpdHlfbmFtZSBpbiBjaXRpZXMpIHsKICAgIGNpdHlfZGF0YSA8LSBkYXRhW2RhdGEkY2l0eSA9PSBjaXR5X25hbWUsIF0KICAgIGNpdHlfY29yciA8LSBjYWxjdWxhdGVfY29ycmVsYXRpb25fbWF0cml4KGNpdHlfZGF0YSwgdW5saXN0KHN1YnNjYWxlcykpCiAgICAKICAgIGNpdHlfYWxwaGFzIDwtIHNhcHBseShzdWJzY2FsZXMsIGZ1bmN0aW9uKHNjYWxlKSB7CiAgICAgIGFscGhhKGNpdHlfY29ycltzY2FsZSwgc2NhbGVdKSR0b3RhbCRyYXdfYWxwaGEKICAgIH0pCiAgICAKICAgIGNpdHlfdG90YWxfYWxwaGEgPC0gYWxwaGEoY2l0eV9jb3JyKSR0b3RhbCRyYXdfYWxwaGEKICAgIAogICAgIyBBZGQgdG8gZGF0YWZyYW1lCiAgICBhbHBoYV9kZltbY2l0eV9uYW1lXV0gPC0gYyhjaXR5X2FscGhhcywgY2l0eV90b3RhbF9hbHBoYSkKICB9CiAgCiAgcmV0dXJuKGFscGhhX2RmKQp9CgojIEZ1bmN0aW9uIHRvIGFuYWx5emUgZGlzY3JpbWluYW50IHZhbGlkaXR5CmFuYWx5emVfZGlzY3JpbWluYW50X3ZhbGlkaXR5IDwtIGZ1bmN0aW9uKGRhdGEsIHN1YnNjYWxlcykgewogICMgQ2FsY3VsYXRlIGNvcnJlbGF0aW9uIG1hdHJpeAogIGNvcnJfbWF0cml4IDwtIGNhbGN1bGF0ZV9jb3JyZWxhdGlvbl9tYXRyaXgoZGF0YSwgdW5saXN0KHN1YnNjYWxlcykpCiAgCiAgIyBDYWxjdWxhdGUgaXRlbS10b3RhbCBjb3JyZWxhdGlvbnMKICBpdGVtX3RvdGFsX2NvcnMgPC0gbGlzdCgpCiAgCiAgZm9yIChzY2FsZV9uYW1lIGluIG5hbWVzKHN1YnNjYWxlcykpIHsKICAgIHNjYWxlX2l0ZW1zIDwtIHN1YnNjYWxlc1tbc2NhbGVfbmFtZV1dCiAgICAKICAgIGl0ZW1fdG90YWwgPC0gZGF0YS5mcmFtZSgKICAgICAgSXRlbSA9IHNjYWxlX2l0ZW1zLAogICAgICBMYWJlbCA9IGxhYmVsc1tzY2FsZV9pdGVtc10sCiAgICAgIEl0ZW1Ub3RhbCA9IE5BCiAgICApCiAgICAKICAgIGZvciAoaXRlbSBpbiBzY2FsZV9pdGVtcykgewogICAgICAjIENhbGN1bGF0ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoaXMgaXRlbSBhbmQgc3VtIG9mIG90aGVyIGl0ZW1zIGluIHN1YnNjYWxlCiAgICAgIG90aGVyX2l0ZW1zIDwtIHNldGRpZmYoc2NhbGVfaXRlbXMsIGl0ZW0pCiAgICAgIGl0ZW1fY29yIDwtIHNhcHBseShvdGhlcl9pdGVtcywgZnVuY3Rpb24ob3RoZXIpIGNvcnJfbWF0cml4W2l0ZW0sIG90aGVyXSkKICAgICAgaXRlbV90b3RhbCRJdGVtVG90YWxbaXRlbV90b3RhbCRJdGVtID09IGl0ZW1dIDwtIG1lYW4oaXRlbV9jb3IsIG5hLnJtID0gVFJVRSkKICAgIH0KICAgIAogICAgaXRlbV90b3RhbF9jb3JzW1tzY2FsZV9uYW1lXV0gPC0gaXRlbV90b3RhbAogIH0KICAKICAjIENhbGN1bGF0ZSBjcm9zcy1sb2FkaW5ncwogIGNyb3NzX2xvYWRpbmdzIDwtIGxpc3QoKQogIAogIGZvciAoc2NhbGVfbmFtZSBpbiBuYW1lcyhzdWJzY2FsZXMpKSB7CiAgICBzY2FsZV9pdGVtcyA8LSBzdWJzY2FsZXNbW3NjYWxlX25hbWVdXQogICAgCiAgICBjcm9zc19sb2FkX2RmIDwtIGRhdGEuZnJhbWUoCiAgICAgIEl0ZW0gPSBzY2FsZV9pdGVtcywKICAgICAgTGFiZWwgPSBsYWJlbHNbc2NhbGVfaXRlbXNdCiAgICApCiAgICAKICAgICMgRm9yIGVhY2ggb3RoZXIgc3Vic2NhbGUsIGNhbGN1bGF0ZSBhdmVyYWdlIGNvcnJlbGF0aW9uCiAgICBmb3IgKG90aGVyX3NjYWxlIGluIHNldGRpZmYobmFtZXMoc3Vic2NhbGVzKSwgc2NhbGVfbmFtZSkpIHsKICAgICAgb3RoZXJfaXRlbXMgPC0gc3Vic2NhbGVzW1tvdGhlcl9zY2FsZV1dCiAgICAgIAogICAgICBjcm9zc19sb2FkX2RmW1tvdGhlcl9zY2FsZV1dIDwtIHNhcHBseShzY2FsZV9pdGVtcywgZnVuY3Rpb24oaXRlbSkgewogICAgICAgIG1lYW4oY29ycl9tYXRyaXhbaXRlbSwgb3RoZXJfaXRlbXNdLCBuYS5ybSA9IFRSVUUpCiAgICAgIH0pCiAgICB9CiAgICAKICAgIGNyb3NzX2xvYWRpbmdzW1tzY2FsZV9uYW1lXV0gPC0gY3Jvc3NfbG9hZF9kZgogIH0KICAKICAjIENvbWJpbmUgaXRlbS10b3RhbCBjb3JyZWxhdGlvbnMgYW5kIGNyb3NzLWxvYWRpbmdzCiAgaXRlbV92c19jcm9zcyA8LSBkYXRhLmZyYW1lKCkKICAKICBmb3IgKHNjYWxlX25hbWUgaW4gbmFtZXMoc3Vic2NhbGVzKSkgewogICAgc2NhbGVfaXRlbXMgPC0gc3Vic2NhbGVzW1tzY2FsZV9uYW1lXV0KICAgIAogICAgZm9yIChpdGVtIGluIHNjYWxlX2l0ZW1zKSB7CiAgICAgICMgR2V0IGl0ZW0tdG90YWwgY29ycmVsYXRpb24KICAgICAgaXRlbV90b3RhbCA8LSBpdGVtX3RvdGFsX2NvcnNbW3NjYWxlX25hbWVdXSRJdGVtVG90YWxbaXRlbV90b3RhbF9jb3JzW1tzY2FsZV9uYW1lXV0kSXRlbSA9PSBpdGVtXQogICAgICAKICAgICAgIyBHZXQgY3Jvc3MtbG9hZGluZ3MgZm9yIHRoaXMgaXRlbQogICAgICBjcm9zc19sb2FkcyA8LSBjcm9zc19sb2FkaW5nc1tbc2NhbGVfbmFtZV1dW2Nyb3NzX2xvYWRpbmdzW1tzY2FsZV9uYW1lXV0kSXRlbSA9PSBpdGVtLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V0ZGlmZihuYW1lcyhjcm9zc19sb2FkaW5nc1tbc2NhbGVfbmFtZV1dKSwgYygiSXRlbSIsICJMYWJlbCIpKV0KICAgICAgCiAgICAgICMgR2V0IGhpZ2hlc3QgY3Jvc3MtbG9hZGluZwogICAgICBtYXhfY3Jvc3MgPC0gbWF4KHVubGlzdChjcm9zc19sb2FkcyksIG5hLnJtID0gVFJVRSkKICAgICAgbWF4X2Nyb3NzX3NjYWxlIDwtIG5hbWVzKGNyb3NzX2xvYWRzKVt3aGljaC5tYXgodW5saXN0KGNyb3NzX2xvYWRzKSldCiAgICAgIAogICAgICAjIEFkZCB0byBkYXRhIGZyYW1lCiAgICAgIGl0ZW1fdnNfY3Jvc3MgPC0gcmJpbmQoaXRlbV92c19jcm9zcywgZGF0YS5mcmFtZSgKICAgICAgICBJdGVtID0gaXRlbSwKICAgICAgICBMYWJlbCA9IGxhYmVsc1tpdGVtXSwKICAgICAgICBTdWJzY2FsZSA9IHNjYWxlX25hbWUsCiAgICAgICAgSXRlbVRvdGFsID0gaXRlbV90b3RhbCwKICAgICAgICBNYXhDcm9zcyA9IG1heF9jcm9zcywKICAgICAgICBNYXhDcm9zc1NjYWxlID0gbWF4X2Nyb3NzX3NjYWxlLAogICAgICAgIERpZmZlcmVuY2UgPSBpdGVtX3RvdGFsIC0gbWF4X2Nyb3NzCiAgICAgICkpCiAgICB9CiAgfQogIAogICMgQ3JlYXRlIHZpc3VhbGl6YXRpb24KICBwIDwtIGdncGxvdChpdGVtX3ZzX2Nyb3NzLCBhZXMoeCA9IEl0ZW1Ub3RhbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gTWF4Q3Jvc3MsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBTdWJzY2FsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEl0ZW0pKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSA2LCBhbHBoYSA9IDAuNykgKwogICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgICB4bGltKDAsIDAuOCkgKwogICAgeWxpbSgwLCAwLjgpICsKICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArCiAgICBsYWJzKHggPSAiSXRlbS1Ub3RhbCBDb3JyZWxhdGlvbiIsIAogICAgICAgICB5ID0gIkhpZ2hlc3QgQ3Jvc3MtTG9hZGluZyIpICsKICAgIHRoZW1lX2NvbW1vbgogIAogIHJldHVybihsaXN0KAogICAgaXRlbV90b3RhbCA9IGl0ZW1fdG90YWxfY29ycywKICAgIGNyb3NzX2xvYWRpbmdzID0gY3Jvc3NfbG9hZGluZ3MsCiAgICBjb21iaW5lZCA9IGl0ZW1fdnNfY3Jvc3MsCiAgICBwbG90ID0gcAogICkpCn0KCiMgRGVmaW5lIHN1YnNjYWxlcyBmb3IgY29ycmVsYXRpb24gYW5kIHJlbGlhYmlsaXR5IGFuYWx5c2lzCnN1YnNjYWxlcyA8LSBsaXN0KAogICJCb25kIHRvIENsYXNzIiA9IGJvbmRfY2xhc3MsCiAgIkJvbmQgdG8gVGVhY2hlciIgPSBib25kX3RlYWNoZXIsCiAgIlNjaG9vbCBEaWZmaWN1bHRpZXMiID0gc2Nob29sX2RpZmZfciwKICAiRnV0dXJlIE9yaWVudGF0aW9uIiA9IGZ1dHVyZV9vcmllbnRhdGlvbiwKICAiU2Nob29sIENvbW1pdG1lbnQiID0gc2Nob29sX2NvbW1pdF9yCikKCiMgRXhlY3V0ZSBjb3JyZWxhdGlvbiBhbmQgcmVsaWFiaWxpdHkgYW5hbHlzZXMKY29ycl9tYXRyaXggPC0gY2FsY3VsYXRlX2NvcnJlbGF0aW9uX21hdHJpeCh6bXNwLCBzY2hvb2xfZXhwX3IpCnJlbGlhYmlsaXR5X2FsbCA8LSBjYWxjdWxhdGVfcmVsaWFiaWxpdHkoem1zcCwgc3Vic2NhbGVzKQpyZWxpYWJpbGl0eV9ieV9jaXR5IDwtIGNhbGN1bGF0ZV9yZWxpYWJpbGl0eV9ieV9jaXR5KHptc3AsIHN1YnNjYWxlcywgY2l0aWVzKQpkaXNjcmltaW5hbnRfdmFsaWRpdHkgPC0gYW5hbHl6ZV9kaXNjcmltaW5hbnRfdmFsaWRpdHkoem1zcCwgc3Vic2NhbGVzKQoKIyBDcmVhdGUgdmlzdWFsaXphdGlvbnMKY29ycl9wbG90IDwtIHBsb3RfY29ycmVsYXRpb25fbWF0cml4KGNvcnJfbWF0cml4LCBsYWJlbHMpCmRpc2NyaW1pbmFudF9wbG90IDwtIGRpc2NyaW1pbmFudF92YWxpZGl0eSRwbG90CgojIFZpZXcKZGlzY3JpbWluYW50X3Bsb3QKYGBgCgojIEVGQSAtIGV4cGxvcmF0b3J5IGZhY3RvciBhbmFseXNpcwpgYGB7ciBlZmF9CiMgRnVuY3Rpb24gdG8gcnVuIEVGQSB3aXRoIGRpZmZlcmVudCBmYWN0b3Igc29sdXRpb25zCnJ1bl9lZmEgPC0gZnVuY3Rpb24oZGF0YSwgbW9kZWxfc3BlYywgdmFyaWFibGVzLCBuX2ZhY3RvcnMgPSAxOjYpIHsKICBlZmFfZml0IDwtIGVmYShtb2RlbF9zcGVjLAogICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhLCAKICAgICAgICAgICAgICAgICBuZmFjdG9ycyA9IG5fZmFjdG9ycywgCiAgICAgICAgICAgICAgICAgb3YubmFtZXMgPSB2YXJpYWJsZXMsIAogICAgICAgICAgICAgICAgIG91dHB1dCA9ICJlZmEiLAogICAgICAgICAgICAgICAgIHN0ZC5vdiA9IFRSVUUsCiAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpCiAgCiAgcmV0dXJuKGVmYV9maXQpCn0KCiMgRnVuY3Rpb24gdG8gY3JlYXRlIHNjcmVlIHBsb3QgZm9yIEVGQSByZXN1bHRzCmNyZWF0ZV9zY3JlZV9wbG90IDwtIGZ1bmN0aW9uKGVpZ2VudmFsdWVzXzE1LCBlaWdlbnZhbHVlc18xMikgewogIGZhY3Rvcl9udW1fMTUgPC0gMTpsZW5ndGgoZWlnZW52YWx1ZXNfMTUpCiAgZmFjdG9yX251bV8xMiA8LSAxOmxlbmd0aChlaWdlbnZhbHVlc18xMikKICAKICAjIENvbWJpbmUgZGF0YQogIHNjcmVlX2RhdGEgPC0gYmluZF9yb3dzKAogICAgdGliYmxlKEVpZ2VudmFsdWUgPSBlaWdlbnZhbHVlc18xNSwgRmFjdG9yID0gZmFjdG9yX251bV8xNSwgU2NhbGUgPSAiMTUtaXRlbSIpLAogICAgdGliYmxlKEVpZ2VudmFsdWUgPSBlaWdlbnZhbHVlc18xMiwgRmFjdG9yID0gZmFjdG9yX251bV8xMiwgU2NhbGUgPSAiMTItaXRlbSIpCiAgKQogIAogICMgQ3JlYXRlIHNjcmVlIHBsb3QKICBzZXQyX2NvbG9ycyA8LSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoOCwgIlNldDIiKVsxOjJdCiAgCiAgcCA8LSBnZ3Bsb3Qoc2NyZWVfZGF0YSwgYWVzKHggPSBGYWN0b3IsIHkgPSBFaWdlbnZhbHVlLCBjb2xvciA9IFNjYWxlLCBncm91cCA9IFNjYWxlKSkgKwogICAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEsIAogICAgICAgICAgICAgIGFscGhhID0gMC43KSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJkYXJrZ3JheSIpICsKICAgIHRoZW1lX2NvbW1vbiArCiAgICBsYWJzKHggPSAiRmFjdG9yIE51bWJlciIsIHkgPSAiRWlnZW52YWx1ZSIpICsKICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjE1KSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2V0Ml9jb2xvcnMpCiAgCiAgcmV0dXJuKHApCn0KCiMgRnVuY3Rpb24gdG8gdmlzdWFsaXplIGZhY3RvciBsb2FkaW5ncyBoZWF0bWFwCmNyZWF0ZV9mYWN0b3JfbG9hZGluZ3NfaGVhdG1hcCA8LSBmdW5jdGlvbihsb2FkaW5nc19kYXRhKSB7CiAgIyBDb252ZXJ0IHRvIGxvbmcgZm9ybWF0CiAgbG9hZGluZ3NfbG9uZyA8LSBsb2FkaW5nc19kYXRhICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtYyhJdGVtLCBEZXNjcmlwdGlvbiksIAogICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiRmFjdG9yIiwgCiAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiTG9hZGluZyIpICU+JQogICAgbXV0YXRlKExvYWRpbmcgPSBpZmVsc2UoaXMubmEoTG9hZGluZykgfCBMb2FkaW5nIDwgMC4zLCBOQSwgTG9hZGluZykpCiAgCiAgIyBHcm91cCBpdGVtcyBieSB0aGVvcmV0aWNhbCBkaW1lbnNpb24KICBsb2FkaW5nc19sb25nIDwtIGxvYWRpbmdzX2xvbmcgJT4lCiAgICBtdXRhdGUoSXRlbUdyb3VwID0gY2FzZV93aGVuKAogICAgICBJdGVtICVpbiUgYygiazZfMTgwMyIsICJrNl8xODA2IiwgIms2XzE4MDkiKSB+ICJCb25kIHRvIENsYXNzIiwKICAgICAgSXRlbSAlaW4lIGMoIms2XzE4MDIiLCAiazZfMTgwNSIsICJrNl8xODA4IikgfiAiQm9uZCB0byBUZWFjaGVyIiwKICAgICAgSXRlbSAlaW4lIGMoIms2XzE4MDEiLCAiazZfMTgwNCIsICJrNl8xODA3X3IiKSB+ICJTY2hvb2wgQ29tbWl0bWVudCIsCiAgICAgIEl0ZW0gJWluJSBjKCJrNl8xODEwX3IiLCAiazZfMTgxMl9yIiwgIms2XzE4MTRfciIpIH4gIlNjaG9vbCBEaWZmaWN1bHRpZXMiLAogICAgICBJdGVtICVpbiUgYygiazZfMTgxMSIsICJrNl8xODEzIiwgIms2XzE4MTUiKSB+ICJGdXR1cmUgT3JpZW50YXRpb24iCiAgICApKQogIAogICMgQ3JlYXRlIG1vZGVsIGdyb3VwcwogIGxvYWRpbmdzX2xvbmcgPC0gbG9hZGluZ3NfbG9uZyAlPiUKICAgIG11dGF0ZShNb2RlbCA9IGNhc2Vfd2hlbigKICAgICAgZ3JlcGwoIjEyLTRGIiwgRmFjdG9yKSB+ICIxMi1pdGVtIDQtZmFjdG9yIiwKICAgICAgZ3JlcGwoIjE1LTRGIiwgRmFjdG9yKSB+ICIxNS1pdGVtIDQtZmFjdG9yIiwKICAgICAgZ3JlcGwoIjE1LTVGIiwgRmFjdG9yKSB+ICIxNS1pdGVtIDUtZmFjdG9yIgogICAgKSkKICAKICAjIE9yZGVyIGZhY3RvcnMgYW5kIGl0ZW1zCiAgbG9hZGluZ3NfbG9uZyRJdGVtR3JvdXAgPC0gZmFjdG9yKGxvYWRpbmdzX2xvbmckSXRlbUdyb3VwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJCb25kIHRvIENsYXNzIiwgIkJvbmQgdG8gVGVhY2hlciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2Nob29sIENvbW1pdG1lbnQiLCAiU2Nob29sIERpZmZpY3VsdGllcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRnV0dXJlIE9yaWVudGF0aW9uIikpCiAgCiAgIyBDcmVhdGUgaGVhdG1hcAogIHAgPC0gZ2dwbG90KGxvYWRpbmdzX2xvbmcsIAogICAgICAgICAgICAgYWVzKHggPSBGYWN0b3IsIHkgPSBEZXNjcmlwdGlvbiwgZmlsbCA9IExvYWRpbmcpKSArCiAgICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKCFpcy5uYShMb2FkaW5nKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ByaW50ZigiJS4yZiIsIExvYWRpbmcpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIikpLCAKICAgICAgICAgICAgICBmYW1pbHkgPSAiSGVsdmV0aWNhIikgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDgsICJTZXQyIiksIAogICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJ3aGl0ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDEuMDIpKSArCiAgICBmYWNldF9ncmlkKEl0ZW1Hcm91cCB+IE1vZGVsLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgICB0aGVtZV9jb21tb24gKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKQogICAgKSArCiAgICBsYWJzKHggPSAiIiwgeSA9ICIiLCBmaWxsID0gIkZhY3RvciBMb2FkaW5nIikKICAKICByZXR1cm4ocCkKfQoKIyBTcGVjaWZ5IEVGQSBtb2RlbHMKZWZhX21vZGVsXzE1IDwtIHBhc3RlKHNjaG9vbF9leHBfciwgY29sbGFwc2UgPSAiICsgIikKZWZhX21vZGVsXzEyIDwtIHBhc3RlKHNjaG9vbF9leHBfMTIsIGNvbGxhcHNlID0gIiArICIpCgojIFJ1biBFRkEKZWZhX2ZpdF8xNSA8LSBydW5fZWZhKHptc3AsIGVmYV9tb2RlbF8xNSwgc2Nob29sX2V4cF9yKQplZmFfZml0XzEyIDwtIHJ1bl9lZmEoem1zcCwgZWZhX21vZGVsXzEyLCBzY2hvb2xfZXhwXzEyKQoKIyBVc2UgaGFyZGNvZGVkIGVpZ2VudmFsdWVzCmVpZ2VudmFsdWVzXzE1IDwtIGMoNC43MjMsIDIuMDEzLCAxLjY3NiwgMS4xNDksIDAuODk2LCAwLjcyNiwgMC42MzMsIDAuNTM5LCAwLjQ5MiwgMC40NTMsIDAuNDEwLCAwLjM4NywgMC4zNDksIDAuMzE5LCAwLjIzNSkKZWlnZW52YWx1ZXNfMTIgPC0gYyg0LjA1MywgMS44OTcsIDEuNDk5LCAxLjExOSwgMC42MzAsIDAuNTQ3LCAwLjQ5OCwgMC40MjYsIDAuNDEzLCAwLjM1NywgMC4zMjUsIDAuMjM4KQoKIyBDcmVhdGUgc2NyZWUgcGxvdCB1c2luZyB0aGVzZSB2YWx1ZXMKc2NyZWVfcGxvdCA8LSBjcmVhdGVfc2NyZWVfcGxvdChlaWdlbnZhbHVlc18xNSwgZWlnZW52YWx1ZXNfMTIpCgojIEZhY3RvciBsb2FkaW5ncyBkYXRhCmxvYWRpbmdzX2RhdGEgPC0gdGliYmxlKAogIEl0ZW0gPSBjKCJrNl8xODAzIiwgIms2XzE4MDYiLCAiazZfMTgwOSIsICJrNl8xODAyIiwgIms2XzE4MDUiLCAiazZfMTgwOCIsIAogICAgICAgICAgIms2XzE4MDEiLCAiazZfMTgwNCIsICJrNl8xODA3X3IiLCAiazZfMTgxMF9yIiwgIms2XzE4MTJfciIsICJrNl8xODE0X3IiLAogICAgICAgICAgIms2XzE4MTEiLCAiazZfMTgxMyIsICJrNl8xODE1IiksCiAgRGVzY3JpcHRpb24gPSBjKCJTZW5zZSBvZiBjb21tdW5pdHkgaW4gY2xhc3MiLCAKICAgICAgICAgICAgICAgICAiR2V0IGFsb25nIHdpdGggY2xhc3NtYXRlcyIsIAogICAgICAgICAgICAgICAgICJDbGFzc21hdGVzIGFyZSBuaWNlIHRvIG1lIiwKICAgICAgICAgICAgICAgICAiVGVhY2hlciB0cmVhdHMgbWUgZmFpcmx5IiwgCiAgICAgICAgICAgICAgICAgIkdldCBhbG9uZyB3aXRoIHRlYWNoZXIiLCAKICAgICAgICAgICAgICAgICAiVGVhY2hlciBoZWxwcyB3aGVuIG5lZWRlZCIsCiAgICAgICAgICAgICAgICAgIkxpa2VzIGdvaW5nIHRvIHNjaG9vbCIsIAogICAgICAgICAgICAgICAgICJMaWtlcyBkb2luZyBob21ld29yayIsIAogICAgICAgICAgICAgICAgICJGaW5kcyBzY2hvb2wgdXNlbGVzcyAocikiLAogICAgICAgICAgICAgICAgICJPZnRlbiBoYXMgYmFkIGdyYWRlcyAocikiLCAKICAgICAgICAgICAgICAgICAiTWFrZXMgbWlzdGFrZXMgaW4gaG9tZXdvcmsgKHIpIiwgCiAgICAgICAgICAgICAgICAgIlN0cnVnZ2xlcyB0byBmb2xsb3cgbGVzc29ucyAocikiLAogICAgICAgICAgICAgICAgICJXb3JraW5nIHRvd2FyZHMgaW50ZXJlc3Rpbmcgam9iIiwgCiAgICAgICAgICAgICAgICAgIlRyeSBoYXJkIGF0IHNjaG9vbCBmb3IgZnV0dXJlIGpvYiIsIAogICAgICAgICAgICAgICAgICJEb2luZyB3ZWxsIGF0IHNjaG9vbCBpcyBpbXBvcnRhbnQiKSwKICAKICAjIEFkZCBmYWN0b3IgbG9hZGluZ3MgZm9yIGRpZmZlcmVudCBtb2RlbHMKICAiMTItNEY6IEJvbmQgdG8gQ2xhc3MiID0gYygwLjY4MSwgMC45MTIsIDAuNzk1LCAwLCAwLCAwLCBOQSwgTkEsIE5BLCAwLCAwLCAwLCAwLCAwLCAwKSwKICAiMTItNEY6IEJvbmQgdG8gVGVhY2hlciIgPSBjKDAsIDAsIDAsIDAuNzg0LCAwLjc4MywgMC41NDAsIE5BLCBOQSwgTkEsIDAsIDAsIDAsIDAsIDAsIDApLAogICIxMi00RjogU2Nob29sIERpZmZpY3VsdGllcyIgPSBjKDAsIDAsIDAsIDAsIDAsIDAsIE5BLCBOQSwgTkEsIDAuNjE4LCAwLjcxOSwgMC43NDksIDAsIDAsIDApLAogICIxMi00RjogRnV0dXJlIE9yaWVudGF0aW9uIiA9IGMoMCwgMCwgMCwgMCwgMCwgMCwgTkEsIE5BLCBOQSwgMCwgMCwgMCwgMC42NzUsIDAuODY0LCAwLjY1NSksCiAgCiAgIyBBZGRpdGlvbmFsIG1vZGVsIGxvYWRpbmdzCiAgIjE1LTRGOiBCb25kIHRvIENsYXNzIiA9IGMoMC42NjcsIDAuODk5LCAwLjgwMiwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCksCiAgIjE1LTRGOiBCb25kIHRvIFRlYWNoZXIiID0gYygwLCAwLCAwLCAwLjc5OCwgMC43NzAsIDAuNTI4LCAwLjM4OCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCksCiAgIjE1LTRGOiBTY2hvb2wgRGlmZmljdWx0aWVzIiA9IGMoMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMC42MTYsIDAuNzIxLCAwLjc0OSwgMCwgMCwgMCksCiAgIjE1LTRGOiBGdXR1cmUvQ29tbWl0bWVudCIgPSBjKDAsIDAsIDAsIDAsIDAsIDAsIDAuMzA3LCAwLjM3OCwgMC4zOTQsIDAsIDAsIDAsIDAuNjY4LCAwLjgxMywgMC42ODMpLAogIAogICIxNS01RjogQm9uZCB0byBDbGFzcyIgPSBjKDAuNjgzLCAwLjkwNiwgMC44MDMsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDApLAogICIxNS01RjogQm9uZCB0byBUZWFjaGVyIiA9IGMoMCwgMCwgMCwgMC43MzIsIDAuNzU3LCAwLjU0MCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCksCiAgIjE1LTVGOiBTY2hvb2wgQ29tbWl0bWVudCIgPSBjKDAsIDAsIDAsIDAsIDAsIDAsIDEuMDEyLCAwLjMyMiwgMCwgMCwgMCwgMCwgMCwgMCwgMCksCiAgIjE1LTVGOiBTY2hvb2wgRGlmZmljdWx0aWVzIiA9IGMoMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMCwgMC42MTQsIDAuNzI1LCAwLjc2NywgMCwgMCwgMCksCiAgIjE1LTVGOiBGdXR1cmUgT3JpZW50YXRpb24iID0gYygwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLCAwLjM2OCwgMCwgMCwgMCwgMC42ODYsIDAuODMwLCAwLjcwMCkKKQoKIyBDcmVhdGUgZmFjdG9yIGxvYWRpbmdzIGhlYXRtYXAKbG9hZGluZ3NfaGVhdG1hcCA8LSBjcmVhdGVfZmFjdG9yX2xvYWRpbmdzX2hlYXRtYXAobG9hZGluZ3NfZGF0YSkKCiMgVmlldwpsb2FkaW5nc19oZWF0bWFwCmBgYAoKIyBDRkEgLSBjb25maXJtYXRvcnkgZmFjdG9yIGFuYWx5c2lzCgpgYGB7ciBjZmF9CiMgRnVuY3Rpb24gdG8gcnVuIENGQSBhbmQgZXh0cmFjdCBmaXQgbWVhc3VyZXMKcnVuX2NmYSA8LSBmdW5jdGlvbihkYXRhLCBtb2RlbF9zcGVjLCBvcmRlcmVkID0gVFJVRSkgewogIGZpdCA8LSBjZmEobW9kZWxfc3BlYywgZGF0YSA9IGRhdGEsIG9yZGVyZWQgPSBvcmRlcmVkKQogIHJldHVybihmaXQpCn0KCiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBhbmQgZm9ybWF0IGZpdCBtZWFzdXJlcwpleHRyYWN0X2ZpdF9tZWFzdXJlcyA8LSBmdW5jdGlvbihmaXQpIHsKICBzdW1tYXJ5KGZpdCwgZml0Lm1lYXN1cmVzID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSkKfQoKIyBSdW4gQ0ZBIGZvciBlYWNoIGRhdGFzZXQKY2ZhX2ZpdHMgPC0gbGlzdCgKICBhbGwgPSBydW5fY2ZhKGNpdHlfZGF0YSRhbGwsIG1vZGVsKSwKICB6cHJvc28gPSBydW5fY2ZhKGNpdHlfZGF0YSR6cHJvc28sIG1vZGVsKSwKICBtcHJvc28gPSBydW5fY2ZhKGNpdHlfZGF0YSRtcHJvc28sIG1vZGVsKSwKICBzcHByb3NvID0gcnVuX2NmYShjaXR5X2RhdGEkc3Bwcm9zbywgbW9kZWwpCikKCiMgR2V0IGZpdCBtZWFzdXJlcyBmb3IgZWFjaCBtb2RlbApmaXRfc3VtbWFyaWVzIDwtIGxhcHBseShjZmFfZml0cywgZXh0cmFjdF9maXRfbWVhc3VyZXMpCgojIEZ1bmN0aW9uIHRvIGNyZWF0ZSBmYWN0b3IgbG9hZGluZ3MgdmlzdWFsaXphdGlvbgpwbG90X2ZhY3Rvcl9sb2FkaW5ncyA8LSBmdW5jdGlvbihmaXRfbW9kZWwpIHsKICAjIEV4dHJhY3Qgc3RhbmRhcmRpemVkIGxvYWRpbmdzCiAgc3RkX2VzdCA8LSBzdGFuZGFyZGl6ZWRTb2x1dGlvbihmaXRfbW9kZWwpICU+JQogICAgZmlsdGVyKG9wID09ICI9fiIsICFncmVwbCgiXmV4cGVyaWVuY2UkIiwgbGhzKSkgICMgT25seSBmaXJzdC1vcmRlciBmYWN0b3IgbG9hZGluZ3MKICAKICAjIENoZWNrIGlmIHRoaXMgaXMgYSBtdWx0aS1ncm91cCBtb2RlbAogIGlzX211bHRpZ3JvdXAgPC0gImdyb3VwIiAlaW4lIG5hbWVzKHN0ZF9lc3QpCiAgCiAgIyBQcm9jZXNzIGRpZmZlcmVudGx5IGJhc2VkIG9uIHdoZXRoZXIgaXQncyBhIG11bHRpLWdyb3VwIG1vZGVsCiAgaWYgKGlzX211bHRpZ3JvdXApIHsKICAgICMgQWRkIGdyb3VwIGxhYmVscyBmb3IgbXVsdGktZ3JvdXAgbW9kZWwKICAgIGdyb3VwX2xhYmVscyA8LSBsYXZJbnNwZWN0KGZpdF9tb2RlbCwgImdyb3VwLmxhYmVsIikKICAgIAogICAgc3RkX2VzdF9sYWJlbGVkIDwtIHN0ZF9lc3QgJT4lCiAgICAgIG11dGF0ZSgKICAgICAgICBncm91cCA9IGZhY3Rvcihncm91cCwgbGFiZWxzID0gZ3JvdXBfbGFiZWxzKSwKICAgICAgICBpdGVtX2xhYmVsID0gbGFiZWxzW3Joc10sCiAgICAgICAgZmFjdG9yX2xhYmVsID0gbGFiZWxzW2xoc10sCiAgICAgICAgIyBGb3JtYXQgZm9yIGJldHRlciBkaXNwbGF5CiAgICAgICAgaXRlbV9sYWJlbCA9IHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbChsYWJlbHNbcmhzXSwgIihcXHcrXFxzK1xcdyspXFxzKyIsICJcXDFcbiIpLAogICAgICAgIGdyb3VwID0gZmFjdG9yKGdyb3VwLCBsZXZlbHMgPSBjKCJTw6NvIFBhdWxvIiwgIlp1cmljaCIsICJNb250ZXZpZGVvIiksIG9yZGVyZWQgPSBUUlVFKQogICAgICApCiAgICAKICAgICMgQ3JlYXRlIGhlYXRtYXAgZm9yIG11bHRpLWdyb3VwIG1vZGVsCiAgICBwIDwtIGdncGxvdChzdGRfZXN0X2xhYmVsZWQsIGFlcyh4ID0gZ3JvdXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGl0ZW1fbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGVzdC5zdGQpKSArCiAgICAgIGdlb21fdGlsZSgpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMmYiLCBlc3Quc3RkKSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICAgICAgZmFjZXRfd3JhcCh+IGZhY3Rvcl9sYWJlbCwgc2NhbGVzID0gImZyZWVfeSIpICsKICAgICAgc2NhbGVfZmlsbF9ncmFkaWVudDIoCiAgICAgICAgbG93ID0gIiNFNUM0OTRGRiIsIG1pZCA9ICJ3aGl0ZSIsIGhpZ2ggPSAiI0U3OEFDM0ZGIiwgCiAgICAgICAgbWlkcG9pbnQgPSAwLjc1LCBuYW1lID0gIlN0YW5kYXJkaXplZFxuRmFjdG9yIExvYWRpbmciLCBsaW1pdHMgPSBjKDAuNSwgMSkKICAgICAgKSArCiAgICAgIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsKICAgICAgdGhlbWVfY29tbW9uCiAgfSBlbHNlIHsKICAgICMgRm9yIHNpbmdsZS1ncm91cCBtb2RlbCwgY3JlYXRlIGEgZGlmZmVyZW50IHZpc3VhbGl6YXRpb24KICAgIHN0ZF9lc3RfbGFiZWxlZCA8LSBzdGRfZXN0ICU+JQogICAgICBtdXRhdGUoCiAgICAgICAgaXRlbV9sYWJlbCA9IGxhYmVsc1tyaHNdLAogICAgICAgIGZhY3Rvcl9sYWJlbCA9IGxhYmVsc1tsaHNdLAogICAgICAgICMgRm9ybWF0IGZvciBiZXR0ZXIgZGlzcGxheQogICAgICAgIGl0ZW1fbGFiZWwgPSBzdHJpbmdyOjpzdHJfcmVwbGFjZV9hbGwobGFiZWxzW3Joc10sICIoXFx3K1xccytcXHcrKVxccysiLCAiXFwxXG4iKQogICAgICApCiAgICAKICAgICMgQ3JlYXRlIGJhcnBsb3QgZm9yIHNpbmdsZS1ncm91cCBtb2RlbAogICAgcCA8LSBnZ3Bsb3Qoc3RkX2VzdF9sYWJlbGVkLCBhZXMoeCA9IGl0ZW1fbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGVzdC5zdGQpKSArCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICAgICAgZmlsbCA9ICIjRTc4QUMzRkYiKSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjJmIiwgZXN0LnN0ZCkpLCAKICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCAKICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICAgICAgZmFjZXRfd3JhcCh+IGZhY3Rvcl9sYWJlbCwgc2NhbGVzID0gImZyZWVfeCIpICsKICAgICAgbGFicyh4ID0gIiIsIHkgPSAiU3RhbmRhcmRpemVkIEZhY3RvciBMb2FkaW5nIikgKwogICAgICB0aGVtZV9jb21tb24gKwogICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMC41KSkKICB9CiAgCiAgcmV0dXJuKHApCn0KCiMgQ3JlYXRlIHZpc3VhbGl6YXRpb25zCmxvYWRpbmdzX3Bsb3QgPC0gcGxvdF9mYWN0b3JfbG9hZGluZ3MoY2ZhX2ZpdHMkYWxsKQoKIyBWaWV3CmxvYWRpbmdzX3Bsb3QKYGBgCgojIE1JIC0gbWVhc3VyZW1lbnQgaW52YXJpYW5jZQojIyBSdW4gTUkgdGVzdHMKYGBge3IgTUkgMX0KaW52YXJpYW5jZV9tb2RlbHMgPC0gbGlzdCgKICBjb25maWd1cmFsID0gY2ZhKG1vZGVsLCBkYXRhID0gem1zcCwgb3JkZXJlZCA9IFRSVUUsIGdyb3VwID0gImNpdHkiKSwKICBtZXRyaWMgPSBjZmEobW9kZWwsIGRhdGEgPSB6bXNwLCBvcmRlcmVkID0gVFJVRSwgZ3JvdXAgPSAiY2l0eSIsIAogICAgICAgICAgICAgIGdyb3VwLmVxdWFsID0gImxvYWRpbmdzIiksCiAgc2NhbGFyID0gY2ZhKG1vZGVsLCBkYXRhID0gem1zcCwgb3JkZXJlZCA9IFRSVUUsIGdyb3VwID0gImNpdHkiLCAKICAgICAgICAgICAgICBncm91cC5lcXVhbCA9IGMoImludGVyY2VwdHMiLCAibG9hZGluZ3MiKSksCiAgdGhyZXNob2xkcyA9IGNmYShtb2RlbCwgZGF0YSA9IHptc3AsIG9yZGVyZWQgPSBUUlVFLCBncm91cCA9ICJjaXR5IiwgCiAgICAgICAgICAgICAgICAgIGdyb3VwLmVxdWFsID0gYygidGhyZXNob2xkcyIsICJsb2FkaW5ncyIpKQopCmBgYAoKIyMgQ29tcGFyZSBtb2RlbHMKYGBge3IgTUkgMn0KaW52YXJpYW5jZV9jb21wYXJpc29ucyA8LSBsaXN0KAogIG1ldHJpY192c19jb25maWd1cmFsID0gY29tcGFyZUZpdChpbnZhcmlhbmNlX21vZGVscyRtZXRyaWMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnZhcmlhbmNlX21vZGVscyRjb25maWd1cmFsKSwKICBzY2FsYXJfdnNfbWV0cmljID0gY29tcGFyZUZpdChpbnZhcmlhbmNlX21vZGVscyRzY2FsYXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW52YXJpYW5jZV9tb2RlbHMkbWV0cmljKSwKICBmdWxsID0gY29tcGFyZUZpdChpbnZhcmlhbmNlX21vZGVscyR0aHJlc2hvbGRzLCAKICAgICAgICAgICAgICAgICAgIGludmFyaWFuY2VfbW9kZWxzJHNjYWxhciwgCiAgICAgICAgICAgICAgICAgICBpbnZhcmlhbmNlX21vZGVscyRtZXRyaWMsIAogICAgICAgICAgICAgICAgICAgaW52YXJpYW5jZV9tb2RlbHMkY29uZmlndXJhbCkKKQpgYGAKCiMjIEZ1bmN0aW9ucwpgYGB7ciBNSSAzfQojIEV4dHJhY3QgZml0IG1lYXN1cmVzIGZvciBhbGwgbW9kZWxzCmludmFyaWFuY2VfZml0X21lYXN1cmVzIDwtIGxhcHBseShpbnZhcmlhbmNlX21vZGVscywgZml0TWVhc3VyZXMpCgojIFByb2Nlc3MgZml0IG1lYXN1cmVzIGZvciB0YWJsZSBhbmQgcGxvdHRpbmcKZml0X21lYXN1cmVzX2RmIDwtIGRvLmNhbGwocmJpbmQsIGludmFyaWFuY2VfZml0X21lYXN1cmVzKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgbXV0YXRlKE1vZGVsID0gcm93bmFtZXMoLikpICU+JQogIHNlbGVjdChNb2RlbCwgY2hpc3EsIGRmLCBjZmkuc2NhbGVkLCB0bGkuc2NhbGVkLCBybXNlYS5zY2FsZWQsIHNybXIpICU+JQogIHNldE5hbWVzKGMoIk1vZGVsIiwgIkNoaV9TcXVhcmUiLCAiZGYiLCAiQ0ZJIiwgIlRMSSIsICJSTVNFQSIsICJTUk1SIikpICU+JQogIG11dGF0ZSgKICAgIERlbHRhX0NoaVNxID0gYyhOQSwgZGlmZihDaGlfU3F1YXJlKSksCiAgICBEZWx0YV9DRkkgPSBjKE5BLCBkaWZmKENGSSkpLAogICAgRGVsdGFfVExJID0gYyhOQSwgZGlmZihUTEkpKSwKICAgIERlbHRhX1JNU0VBID0gYyhOQSwgZGlmZihSTVNFQSkpLAogICAgRGVsdGFfU1JNUiA9IGMoTkEsIGRpZmYoU1JNUikpCiAgKQoKIyBGdW5jdGlvbiB0byBjcmVhdGUgbWVhc3VyZW1lbnQgaW52YXJpYW5jZSB0YWJsZQpjcmVhdGVfbWlfdGFibGUgPC0gZnVuY3Rpb24oZml0X2RmKSB7CiAga2FibGUoZml0X2RmICU+JQogICAgICBtdXRhdGUoYWNyb3NzKGMoQ0ZJLCBUTEksIFJNU0VBLCBTUk1SLCBEZWx0YV9DaGlTcSwgRGVsdGFfQ0ZJLCBEZWx0YV9UTEksIERlbHRhX1JNU0VBLCBEZWx0YV9TUk1SKSwgCiAgICAgICAgICAgICAgICAgICAgfiBzcHJpbnRmKCIlLjNmIiwgLngpKSksIAogICAgICAgIGNhcHRpb24gPSAiTWVhc3VyZW1lbnQgSW52YXJpYW5jZSBSZXN1bHRzOiAxMiBpdGVtcywgMm5kIG9yZGVyIG1vZGVsIiwKICAgICAgICBjb2wubmFtZXMgPSBjKCJNb2RlbCIsICLPh8KyIiwgImRmIiwgIkNGSSIsICJUTEkiLCAiUk1TRUEiLCAiU1JNUiIsIAogICAgICAgICAgICAgICAgICAgICAizpTPh8KyIiwgIs6UQ0ZJIiwgIs6UVExJIiwgIs6UUk1TRUEiLCAizpRTUk1SIikpICU+JQogICAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQp9CgojIEZ1bmN0aW9uIHRvIHBsb3QgZml0IG1lYXN1cmUgZGVsdGFzCnBsb3RfZml0X2RlbHRhcyA8LSBmdW5jdGlvbihmaXRfZGYpIHsKICBkZWx0YV9maXQgPC0gZml0X2RmICU+JQogICAgc2VsZWN0KE1vZGVsLCBEZWx0YV9DRkksIERlbHRhX1RMSSwgRGVsdGFfUk1TRUEsIERlbHRhX1NSTVIpICU+JQogICAgdGlkeXI6OnBpdm90X2xvbmdlcigtTW9kZWwsIG5hbWVzX3RvID0gIkZpdF9NZXRyaWMiLCB2YWx1ZXNfdG8gPSAiRGVsdGFfVmFsdWUiKSAlPiUKICAgIGZpbHRlcighaXMubmEoRGVsdGFfVmFsdWUpKQogIAogIGdncGxvdChkZWx0YV9maXQsIGFlcyh4ID0gRGVsdGFfVmFsdWUsIHkgPSBNb2RlbCwgZmlsbCA9IEZpdF9NZXRyaWMpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC0wLjAxLCAwLjAxLCAwLjAxNSksIAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuOCkgKwogICAgc2NhbGVfZmlsbF9wYWxldHRlZXJfZCgibmF0aW9uYWxwYXJrY29sb3JzOjpCYWRsYW5kcyIpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0wLjAxNSwgMC4wMTUpLCAKICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoLTAuMDE1LCAwLjAxNSwgYnkgPSAwLjAwNSkpICsKICAgIGxhYnMoeCA9ICJNb2RlbCBDb21wYXJpc29uIiwgeSA9ICJDaGFuZ2UgaW4gRml0IEluZGV4IiwgZmlsbCA9ICJGaXQgSW5kZXgiKSArCiAgICB0aGVtZV9jb21tb24KfQoKIyBGdW5jdGlvbiB0byBwbG90IHRocmVzaG9sZHMKcGxvdF90aHJlc2hvbGRzIDwtIGZ1bmN0aW9uKGZpdF9tb2RlbCkgewogICMgRXh0cmFjdCB0aHJlc2hvbGQgcGFyYW1ldGVycwogIHRocmVzaG9sZHNfY29uZmlnIDwtIHBhcmFtZXRlckVzdGltYXRlcyhmaXRfbW9kZWwpICU+JQogICAgZmlsdGVyKG9wID09ICJ8IikgJT4lCiAgICBzZWxlY3QobGhzLCByaHMsIGdyb3VwLCBlc3QpICU+JQogICAgbXV0YXRlKAogICAgICBsaHMgPSBmYWN0b3IobGhzKSwKICAgICAgcmhzID0gZmFjdG9yKHJocywgbGV2ZWxzID0gYygidDEiLCAidDIiLCAidDMiKSksCiAgICAgIGdyb3VwID0gZmFjdG9yKGdyb3VwLCBsYWJlbHMgPSBsYXZJbnNwZWN0KGZpdF9tb2RlbCwgImdyb3VwLmxhYmVsIikpCiAgICApCiAgCiAgIyBBZGQgbGFiZWxzCiAgbGV2ZWxzKHRocmVzaG9sZHNfY29uZmlnJGxocykgPC0gbGFiZWxzW2xldmVscyh0aHJlc2hvbGRzX2NvbmZpZyRsaHMpXQogIAogICMgUGxvdAogIGdncGxvdCh0aHJlc2hvbGRzX2NvbmZpZywgYWVzKHggPSByaHMsIHkgPSBlc3QsIGNvbG9yID0gZ3JvdXAsIGdyb3VwID0gZ3JvdXApKSArCiAgICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBmYWNldF93cmFwKH4gbGhzLCBzY2FsZXMgPSAiZml4ZWQiLCBsYWJlbGxlciA9IGxhYmVsbGVyKGxocyA9IGxhYmVscykpICsKICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiLCBuYW1lID0gIkNpdHkiKSArCiAgICBsYWJzKHggPSAiVGhyZXNob2xkIiwgeSA9ICJFc3RpbWF0ZSIpICsgCiAgICB0aGVtZV9jb21tb24KfQpgYGAKCiMjIFByZXNlbnQKYGBge3IgTUkgNH0KCiMgVmlzdWFsaXphdGlvbnMgYW5kIHRhYmxlcwptaV90YWJsZSA8LSBjcmVhdGVfbWlfdGFibGUoZml0X21lYXN1cmVzX2RmKQpmaXRfZGVsdGFzX3Bsb3QgPC0gcGxvdF9maXRfZGVsdGFzKGZpdF9tZWFzdXJlc19kZikKdGhyZXNob2xkc19wbG90IDwtIHBsb3RfdGhyZXNob2xkcyhpbnZhcmlhbmNlX21vZGVscyRjb25maWd1cmFsKQoKIyBDcmVhdGUgbW9kZWwgc3RydWN0dXJlIHZpc3VhbGl6YXRpb25zCnBsb3RfbW9kZWxfc3RydWN0dXJlIDwtIGZ1bmN0aW9uKGludmFyaWFuY2VfbW9kZWwpIHsKICAjIEV4dHJhY3Qgc3RhbmRhcmRpemVkIGZhY3RvciBsb2FkaW5ncyBieSBncm91cAogIGxvYWRpbmdzX2J5X2dyb3VwIDwtIHN0YW5kYXJkaXplZFNvbHV0aW9uKGludmFyaWFuY2VfbW9kZWwsIHR5cGUgPSAic3RkLmFsbCIpICU+JQogICAgZmlsdGVyKG9wID09ICI9fiIpCiAgCiAgIyBDcmVhdGUgY3VzdG9tIG5vZGUgYW5kIGVkZ2UgZGF0YWZyYW1lcyBmb3IgbmV0d29yayB2aXN1YWxpemF0aW9uCiAgbm9kZXMgPC0gZGF0YS5mcmFtZSgKICAgIGlkID0gdW5pcXVlKGMobG9hZGluZ3NfYnlfZ3JvdXAkbGhzLCBsb2FkaW5nc19ieV9ncm91cCRyaHMpKSwKICAgIGxhYmVsID0gc2FwcGx5KHVuaXF1ZShjKGxvYWRpbmdzX2J5X2dyb3VwJGxocywgbG9hZGluZ3NfYnlfZ3JvdXAkcmhzKSksIAogICAgICAgICAgICAgICAgICBmdW5jdGlvbihuKSBpZmVsc2UobiAlaW4lIG5hbWVzKGxhYmVscyksIGxhYmVsc1tuXSwgbikpLAogICAgdHlwZSA9IGNhc2Vfd2hlbigKICAgICAgdW5pcXVlKGMobG9hZGluZ3NfYnlfZ3JvdXAkbGhzLCBsb2FkaW5nc19ieV9ncm91cCRyaHMpKSA9PSAiZXhwZXJpZW5jZSIgfiAic2Vjb25kX29yZGVyIiwKICAgICAgdW5pcXVlKGMobG9hZGluZ3NfYnlfZ3JvdXAkbGhzLCBsb2FkaW5nc19ieV9ncm91cCRyaHMpKSAlaW4lIGMoImNsYXNzIiwgInRlYWNoZXIiLCAiZGlmZmljIiwgImZ1dHVyZSIpIH4gImZpcnN0X29yZGVyIiwKICAgICAgVFJVRSB+ICJpbmRpY2F0b3IiCiAgICApCiAgKQogIAogIGVkZ2VzIDwtIGxvYWRpbmdzX2J5X2dyb3VwICU+JQogICAgc2VsZWN0KGZyb20gPSBsaHMsIHRvID0gcmhzLCB3ZWlnaHQgPSBlc3Quc3RkLCBncm91cCkKICAKICAjIENyZWF0ZSBhIGxpc3QgdG8gc3RvcmUgdGhlIGluZGl2aWR1YWwgcGxvdHMKICBjaXR5X3Bsb3RzIDwtIGxpc3QoKQogIAogICMgQ29sb3JzIGZvciBub2RlIHR5cGVzCiAgbm9kZV9jb2xvcnMgPC0gYygic2Vjb25kX29yZGVyIiA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgICAgImZpcnN0X29yZGVyIiA9ICJncmV5NjAiLCAKICAgICAgICAgICAgICAgICAgICJpbmRpY2F0b3IiID0gIndoaXRlIikKICAKICAjIENyZWF0ZSBzZXBhcmF0ZSBwbG90cyBmb3IgZWFjaCBjaXR5CiAgZm9yIChnIGluIHVuaXF1ZShlZGdlcyRncm91cCkpIHsKICAgIGdfZWRnZXMgPC0gZWRnZXMgJT4lIGZpbHRlcihncm91cCA9PSBnKQogICAgZ19ncmFwaCA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZ19lZGdlcywgdmVydGljZXMgPSBub2RlcykKICAgIAogICAgY2l0eV9uYW1lIDwtIGMoIlPDo28gUGF1bG8iLCAiWnVyaWNoIiwgIk1vbnRldmlkZW8iKVtnXQogICAgCiAgICAjIENyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCB3cmFwcGVkIGxhYmVscwogICAgbm9kZV9sYWJlbHMgPC0gbm9kZXMkbGFiZWwKICAgIAogICAgIyBTaG9ydGVuIGFuZCB3cmFwIGluZGljYXRvciBsYWJlbHMKICAgIHdyYXBwZWRfbGFiZWxzIDwtIHNhcHBseSgxOmxlbmd0aChub2RlX2xhYmVscyksIGZ1bmN0aW9uKGkpIHsKICAgICAgbGFiZWwgPC0gbm9kZV9sYWJlbHNbaV0KICAgICAgbm9kZV9pZCA8LSBub2RlcyRpZFtpXQogICAgICBub2RlX3R5cGUgPC0gbm9kZXMkdHlwZVtpXQogICAgICAKICAgICAgaWYgKG5vZGVfdHlwZSA9PSAiaW5kaWNhdG9yIikgewogICAgICAgICMgQWJicmV2aWF0ZSBsb25nIGxhYmVscyBmb3IgaW5kaWNhdG9ycwogICAgICAgIGlmIChuY2hhcihsYWJlbCkgPiAyMCkgewogICAgICAgICAgIyBGaXJzdCB0cnkgc3BsaXR0aW5nIGludG8gbXVsdGlwbGUgbGluZXMKICAgICAgICAgIHdyYXBwZWQgPC0gc3RyaW5ncjo6c3RyX3dyYXAobGFiZWwsIHdpZHRoID0gMTUpCiAgICAgICAgICAKICAgICAgICAgICMgSWYgc3RpbGwgdG9vIGxvbmcsIHRydW5jYXRlCiAgICAgICAgICBpZiAobmNoYXIod3JhcHBlZCkgPiA0MCkgewogICAgICAgICAgICB3cmFwcGVkIDwtIHBhc3RlMChzdWJzdHIobGFiZWwsIDEsIDE3KSwgIi4uLiIpCiAgICAgICAgICB9CiAgICAgICAgICByZXR1cm4od3JhcHBlZCkKICAgICAgICB9IGVsc2UgewogICAgICAgICAgcmV0dXJuKGxhYmVsKQogICAgICAgIH0KICAgICAgfSBlbHNlIHsKICAgICAgICAjIEZvciBmYWN0b3Igbm9kZXMsIGtlZXAgYXMgaXMgYnV0IGJvbGQKICAgICAgICByZXR1cm4obGFiZWwpCiAgICAgIH0KICAgIH0pCiAgICAKICAgICMgQWRkIHRoZSB3cmFwcGVkIGxhYmVscyB0byB0aGUgZ3JhcGgKICAgIFYoZ19ncmFwaCkkd3JhcHBlZF9sYWJlbCA8LSB3cmFwcGVkX2xhYmVsc1ttYXRjaChWKGdfZ3JhcGgpJG5hbWUsIG5vZGVzJGlkKV0KICAgIAogICAgIyBTZXQgYSBzZWVkIGZvciByZXByb2R1Y2libGUgbGF5b3V0CiAgICBzZXQuc2VlZCg0MikKICAgIAogICAgIyBDcmVhdGUgdGhlIHBsb3QKICAgIHAgPC0gZ2dyYXBoKGdfZ3JhcGgsIGxheW91dCA9ICJzdHJlc3MiLCB3ZWlnaHRzID0gRShnX2dyYXBoKSR3ZWlnaHQpICsKICAgICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gd2VpZ2h0LCAKICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gd2VpZ2h0LCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gd2VpZ2h0LAogICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzcHJpbnRmKCIlLjJmIiwgd2VpZ2h0KSksCiAgICAgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDMsICJtbSIpLCB0eXBlID0gImNsb3NlZCIpLCAKICAgICAgICAgICAgICAgICAgICBlbmRfY2FwID0gY2lyY2xlKDQsICJtbSIpLAogICAgICAgICAgICAgICAgICAgIHN0YXJ0X2NhcCA9IGNpcmNsZSgyLCAibW0iKSwKICAgICAgICAgICAgICAgICAgICBhbmdsZV9jYWxjID0gImFsb25nIiwKICAgICAgICAgICAgICAgICAgICBsYWJlbF9kb2RnZSA9IHVuaXQoMi41LCAibW0iKSwKICAgICAgICAgICAgICAgICAgICBsYWJlbF9zaXplID0gMiwKICAgICAgICAgICAgICAgICAgICBsYWJlbF9jb2xvdXIgPSAiYmxhY2siKSArCiAgICAgIGdlb21fbm9kZV9wb2ludChhZXMoZmlsbCA9IHR5cGUsIHNpemUgPSB0eXBlKSwKICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAyMSwgIAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsICAKICAgICAgICAgICAgICAgICAgICAgc3Ryb2tlID0gMSwgIAogICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IHdyYXBwZWRfbGFiZWwpLAogICAgICAgICAgICAgICAgICAgIHJlcGVsID0gVFJVRSwgIAogICAgICAgICAgICAgICAgICAgIHNpemUgPSAzLCAgICAKICAgICAgICAgICAgICAgICAgICBiZy5jb2xvciA9ICJ3aGl0ZSIsIAogICAgICAgICAgICAgICAgICAgIGJnLnIgPSAwLjE1LAogICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuY29sb3IgPSAiZ3JheTUwIiwKICAgICAgICAgICAgICAgICAgICBzZWdtZW50LnNpemUgPSAwLjQsCiAgICAgICAgICAgICAgICAgICAgc2VnbWVudC5hbHBoYSA9IDAuNiwKICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9IGlmZWxzZShWKGdfZ3JhcGgpJHR5cGUgPT0gImluZGljYXRvciIsICJwbGFpbiIsICJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gMjApICsKICAgICAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMC41LCAzKSwgZ3VpZGUgPSAibm9uZSIpICsKICAgICAgc2NhbGVfZWRnZV9hbHBoYShyYW5nZSA9IGMoMC42LCAxKSwgZ3VpZGUgPSAibm9uZSIpICsKICAgICAgc2NhbGVfZWRnZV9jb2xvcl9zdGVwczIobG93ID0gIndoaXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gIiNFNUM0OTRGRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiI0U3OEFDM0ZGIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJGYWN0b3IgTG9hZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGlmKGcgPCAzKSAibm9uZSIgZWxzZSAibGVnZW5kIikgKwogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBub2RlX2NvbG9ycywgZ3VpZGUgPSAibm9uZSIpICsKICAgICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYygic2Vjb25kX29yZGVyIiA9IDE1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZmlyc3Rfb3JkZXIiID0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpbmRpY2F0b3IiID0gNiksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAibm9uZSIpICsKICAgICAgbGFicyh0aXRsZSA9IHBhc3RlKGNpdHlfbmFtZSkpICsKICAgICAgdGhlbWVfZ3JhcGgoYmFzZV9mYW1pbHkgPSAiSGVsdmV0aWNhIikgKwogICAgICB0aGVtZSgKICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWx2ZXRpY2EiKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBpZihnIDwgMykgIm5vbmUiIGVsc2UgImJvdHRvbSIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAsIHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oNSwgNSwgNSwgNSkKICAgICAgKSArCiAgICAgIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIpICsKICAgICAgZXhwYW5kX2xpbWl0cyh4ID0gYygtMS41LCAxLjUpLCB5ID0gYygtMS41LCAxLjUpKQogICAgCiAgICAjIFN0b3JlIHRoZSBwbG90CiAgICBjaXR5X3Bsb3RzW1tnXV0gPC0gcAogIH0KICAKICAjIENvbWJpbmUgdGhlIHBsb3RzCiAgY29tYmluZWRfcGxvdCA8LSBjaXR5X3Bsb3RzW1sxXV0gKyBjaXR5X3Bsb3RzW1syXV0gKyBjaXR5X3Bsb3RzW1szXV0gKyAKICAgIHBsb3RfbGF5b3V0KG5jb2wgPSAzKSArCiAgICBwbG90X2Fubm90YXRpb24oCiAgICAgIHRoZW1lID0gdGhlbWUocGxvdC5tYXJnaW4gPSBtYXJnaW4oNSwgNSwgNSwgNSkpCiAgICApCiAgCiAgcmV0dXJuKGNvbWJpbmVkX3Bsb3QpCn0KCiMgRXhlY3V0ZQptb2RlbF9zdHJ1Y3R1cmVfcGxvdCA8LSBwbG90X21vZGVsX3N0cnVjdHVyZShpbnZhcmlhbmNlX21vZGVscyRzY2FsYXIpCgojIFZpZXcKbW9kZWxfc3RydWN0dXJlX3Bsb3QKYGBgCgojIExhdGVudCBzY29yZSBhbmFseXNpcwojIyBGdW5jdGlvbnMKYGBge3Igc2NvcmVzIDF9CiMgRnVuY3Rpb24gdG8gZXh0cmFjdCBsYXRlbnQgc2NvcmVzCmV4dHJhY3RfbGF0ZW50X3Njb3JlcyA8LSBmdW5jdGlvbihmaXRfbW9kZWwpIHsKICAjIEdldCB0aGUgbGlzdCBvZiBFQk0gc2NvcmVzIChvbmUgbWF0cml4IHBlciBjaXR5KQogIGZzX2xpc3QgPC0gbGF2UHJlZGljdChmaXRfbW9kZWwsIHR5cGUgPSAibHYiLCBtZXRob2QgPSAiRUJNIikKICAKICAjIFR1cm4gZWFjaCBncm91cCdzIG1hdHJpeCBpbnRvIGEgZGF0YSBmcmFtZSB0aGF0IGluY2x1ZGVzIElECiAgZnNfZGZzIDwtIGxhcHBseShzZXFfYWxvbmcoZnNfbGlzdCksIGZ1bmN0aW9uKGcpIHsKICAgIG1hdCA8LSBmc19saXN0W1tnXV0gIAogICAgaWR4IDwtIGZpdF9tb2RlbEBEYXRhQGNhc2UuaWR4W1tnXV0KICAgIGRhdGEuZnJhbWUoCiAgICAgIElEID0gem1zcCRJRFtpZHhdLAogICAgICBtYXQsCiAgICAgIGNoZWNrLm5hbWVzID0gRkFMU0UKICAgICkKICB9KQogIAogICMgU3RhY2sgdGhlbSBpbnRvIG9uZSBkYXRhIGZyYW1lCiAgZnNfZGYgPC0gYmluZF9yb3dzKGZzX2RmcykKICAKICByZXR1cm4oZnNfZGYpCn0KCiMgRnVuY3Rpb24gdG8gcHJlcGFyZSBsb25nLWZvcm1hdCBkYXRhIGZvciBsYXRlbnQgc2NvcmVzCnByZXBhcmVfbGF0ZW50X3Njb3Jlc19sb25nIDwtIGZ1bmN0aW9uKGRhdGEpIHsKICBkYXRhICU+JQogICAgc2VsZWN0KElELCBjaXR5LCBjbGFzcywgdGVhY2hlciwgZGlmZmljLCBmdXR1cmUsIGV4cGVyaWVuY2UpICU+JQogICAgcGl2b3RfbG9uZ2VyKAogICAgICBjb2xzID0gYyhjbGFzcywgdGVhY2hlciwgZGlmZmljLCBmdXR1cmUsIGV4cGVyaWVuY2UpLAogICAgICBuYW1lc190byA9ICJmYWN0b3IiLAogICAgICB2YWx1ZXNfdG8gPSAic2NvcmUiCiAgICApICU+JQogICAgbXV0YXRlKAogICAgICBmYWN0b3JfdHlwZSA9IGlmZWxzZShmYWN0b3IgPT0gImV4cGVyaWVuY2UiLCAiU2Vjb25kLU9yZGVyIiwgIkZpcnN0LU9yZGVyIiksCiAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGMoImV4cGVyaWVuY2UiLCAiY2xhc3MiLCAidGVhY2hlciIsICJkaWZmaWMiLCAiZnV0dXJlIikpLAogICAgICBmYWN0b3JfbGFiZWwgPSBjYXNlX3doZW4oCiAgICAgICAgZmFjdG9yID09ICJleHBlcmllbmNlIiB+ICJTY2hvb2wgRXhwZXJpZW5jZSIsCiAgICAgICAgZmFjdG9yID09ICJjbGFzcyIgfiAiQm9uZCB0byBDbGFzcyIsCiAgICAgICAgZmFjdG9yID09ICJ0ZWFjaGVyIiB+ICJCb25kIHRvIFRlYWNoZXIiLAogICAgICAgIGZhY3RvciA9PSAiZGlmZmljIiB+ICJTY2hvb2wgRGlmZmljdWx0aWVzIChyZXZlcnNlZCkiLAogICAgICAgIGZhY3RvciA9PSAiZnV0dXJlIiB+ICJGdXR1cmUgT3JpZW50YXRpb24iCiAgICAgICkKICAgICkKfQoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCmNhbGN1bGF0ZV9mYWN0b3Jfc3VtbWFyaWVzIDwtIGZ1bmN0aW9uKGxvbmdfZGF0YSkgewogIGxvbmdfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGNpdHksIGZhY3RvciwgZmFjdG9yX2xhYmVsLCBmYWN0b3JfdHlwZSkgJT4lCiAgICBzdW1tYXJpemUoCiAgICAgIG1lYW5fc2NvcmUgPSBtZWFuKHNjb3JlLCBuYS5ybSA9IFRSVUUpLAogICAgICBtZWRpYW5fc2NvcmUgPSBtZWRpYW4oc2NvcmUsIG5hLnJtID0gVFJVRSksCiAgICAgIHNkX3Njb3JlID0gc2Qoc2NvcmUsIG5hLnJtID0gVFJVRSksCiAgICAgIHEyNSA9IHF1YW50aWxlKHNjb3JlLCAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICBxNzUgPSBxdWFudGlsZShzY29yZSwgMC43NSwgbmEucm0gPSBUUlVFKSwKICAgICAgbiA9IG4oKSwKICAgICAgIyBDYWxjdWxhdGUgc3RhbmRhcmQgZXJyb3IKICAgICAgc2UgPSBzZF9zY29yZSAvIHNxcnQobiksCiAgICAgICMgQ2FsY3VsYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzCiAgICAgIGNpX2xvd2VyID0gbWVhbl9zY29yZSAtIDEuOTYgKiBzZSwKICAgICAgY2lfdXBwZXIgPSBtZWFuX3Njb3JlICsgMS45NiAqIHNlLAogICAgICAuZ3JvdXBzID0gImRyb3AiCiAgICApCn0KCiMgRnVuY3Rpb24gdG8gcnVuIEFOT1ZBIGFuZCBwb3N0LWhvYyB0ZXN0cwphbmFseXplX2xhdGVudF92YXIgPC0gZnVuY3Rpb24odmFyX25hbWUsIGRhdGEpIHsKICAjIEZpdCB0aGUgb25lLXdheSBBTk9WQQogIGZpdCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlMCh2YXJfbmFtZSwgIiB+IGNpdHkiKSksIGRhdGEgPSBkYXRhKQogIAogICMgQU5PVkEgdGFibGUKICBhb3ZfdGFiIDwtIGFub3ZhKGZpdCkKICAKICAjIENvbXB1dGUgZXRhLXNxdWFyZWQKICBlc190YWIgPC0gZXRhX3NxdWFyZWQoZml0LCBwYXJ0aWFsID0gVFJVRSkKICAKICAjIEdldCB0aGUgcmlnaHQgY29sdW1uIG5hbWUKICBlc19jb2wgPC0gaW50ZXJzZWN0KGMoIkV0YTJfcGFydGlhbCIsICJFdGEyIiksIG5hbWVzKGVzX3RhYikpWzFdCiAgCiAgIyBFeHRyYWN0IGNpdHkgZWZmZWN0IHNpemUKICBjaXR5X2V0YTIgPC0gZXNfdGFiICU+JQogICAgZmlsdGVyKFBhcmFtZXRlciA9PSAiY2l0eSIpICU+JQogICAgcHVsbCghIXN5bShlc19jb2wpKQogIAogICMgRXN0aW1hdGVkIG1hcmdpbmFsIG1lYW5zIGFuZCBUdWtleSBwb3N0LWhvYwogIGVtcyA8LSBlbW1lYW5zKGZpdCwgfiBjaXR5KQogIHR1a2V5IDwtIHBhaXJzKGVtcywgYWRqdXN0ID0gInR1a2V5IikKICAKICAjIFJldHVybiBhbGwgcmVzdWx0cyBpbiBhIGxpc3QKICBsaXN0KAogICAgYW5vdmEgPSBhb3ZfdGFiLAogICAgZXRhMiA9IGNpdHlfZXRhMiwKICAgIGVtbWVhbnMgPSBlbXMsCiAgICBjb250cmFzdHMgPSB0dWtleQogICkKfQoKIyBGdW5jdGlvbiB0byBwbG90IGxhdGVudCBtZWFucwpwbG90X2xhdGVudF9tZWFucyA8LSBmdW5jdGlvbihzdW1tYXJpZXMpIHsKICBnZ3Bsb3Qoc3VtbWFyaWVzLCBhZXMoeCA9IGZhY3Rvcl9sYWJlbCwgeSA9IG1lYW5fc2NvcmUsIGZpbGwgPSBjaXR5KSkgKwogICAgZ2VvbV9jb2wocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHdpZHRoID0gMC44LCBhbHBoYSA9IDAuNykgKwogICAgZ2VvbV9lcnJvcmJhcigKICAgICAgYWVzKHltaW4gPSBjaV9sb3dlciwgeW1heCA9IGNpX3VwcGVyKSwKICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksCiAgICAgIHdpZHRoID0gMC4yNQogICAgKSArCiAgICBmYWNldF9ncmlkKC4gfiBmYWN0b3JfdHlwZSwgc3BhY2UgPSAiZnJlZSIsIHNjYWxlcyA9ICJmcmVlX3giKSArCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHsKICAgICAgc3RyX3JlcGxhY2VfYWxsKHgsICIoXFx3K1xccytcXHcrKVxccysiLCAiXFwxXG4iKQogICAgfSkgKwogICAgbGFicyh4ID0gTlVMTCwgeSA9ICJNZWFuIExhdGVudCBTY29yZSIsIGZpbGwgPSAiQ2l0eSIpICsKICAgIHRoZW1lX2NvbW1vbgp9CgojIEZ1bmN0aW9uIHRvIHBsb3QgbGF0ZW50IHNjb3JlIGRpc3RyaWJ1dGlvbnMKcGxvdF9sYXRlbnRfZGlzdHJpYnV0aW9ucyA8LSBmdW5jdGlvbihsb25nX2RhdGEpIHsKICBnZ3Bsb3QobG9uZ19kYXRhLCBhZXMoeCA9IHNjb3JlLCB5ID0gZmFjdG9yX2xhYmVsLCBmaWxsID0gY2l0eSkpICsKICAgIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjcsIHNjYWxlID0gMSkgKwogICAgZmFjZXRfZ3JpZChmYWN0b3JfdHlwZSB+IC4sIHNjYWxlcyA9ICJmcmVlX3kiLCBzcGFjZSA9ICJmcmVlIikgKwogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsKICAgIGd1aWRlcyhjb2xvciA9ICJub25lIikgKwogICAgbGFicyh4ID0gIkxhdGVudCBTY29yZSIsIHkgPSBOVUxMLCBmaWxsID0gIkNpdHkiKSArCiAgICB0aGVtZV9jb21tb24KfQoKIyBGdW5jdGlvbiB0byBjYWxjdWxhdGUgZWZmZWN0IHNpemVzIGJldHdlZW4gY2l0aWVzCmNhbGN1bGF0ZV9lZmZlY3Rfc2l6ZXMgPC0gZnVuY3Rpb24obG9uZ19kYXRhKSB7CiAgIyBHcm91cCBkYXRhCiAgZ3JvdXBlZF9kYXRhIDwtIGxvbmdfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGZhY3RvciwgZmFjdG9yX2xhYmVsLCBmYWN0b3JfdHlwZSkKICAKICAjIEdldCB1bmlxdWUgZ3JvdXBzCiAgZ3JvdXBzIDwtIGdyb3VwZWRfZGF0YSAlPiUKICAgIGdyb3VwX2tleXMoKQogIAogICMgQ2FsY3VsYXRlIGVmZmVjdCBzaXplcyBmb3IgZWFjaCBncm91cAogIGVmZmVjdF9zaXplcyA8LSBtYXBfZGZyKDE6bnJvdyhncm91cHMpLCBmdW5jdGlvbihpKSB7CiAgICAjIEdldCBjdXJyZW50IGdyb3VwIGRhdGEKICAgIGdycF9kYXRhIDwtIGdyb3VwZWRfZGF0YSAlPiUKICAgICAgZmlsdGVyKGZhY3RvciA9PSBncm91cHMkZmFjdG9yW2ldKQogICAgCiAgICAjIEdldCBkYXRhIGZvciBlYWNoIGNpdHkKICAgIGNpdHlfZGF0YSA8LSBzcGxpdChncnBfZGF0YSRzY29yZSwgZ3JwX2RhdGEkY2l0eSkKICAgIGNpdGllcyA8LSBuYW1lcyhjaXR5X2RhdGEpCiAgICAKICAgICMgQ2FsY3VsYXRlIENvaGVuJ3MgZCBmb3IgZWFjaCBwYWlyIG9mIGNpdGllcwogICAgcmVzdWx0IDwtIG1hcF9kZnIoMToobGVuZ3RoKGNpdGllcyktMSksIGZ1bmN0aW9uKGopIHsKICAgICAgbWFwX2RmcigoaisxKTpsZW5ndGgoY2l0aWVzKSwgZnVuY3Rpb24oaykgewogICAgICAgIGNpdHkxIDwtIGNpdGllc1tqXQogICAgICAgIGNpdHkyIDwtIGNpdGllc1trXQogICAgICAgIHggPC0gY2l0eV9kYXRhW1tjaXR5MV1dCiAgICAgICAgeSA8LSBjaXR5X2RhdGFbW2NpdHkyXV0KICAgICAgICAKICAgICAgICAjIENhbGN1bGF0ZSBtZWFucyBhbmQgU0RzCiAgICAgICAgbWVhbl94IDwtIG1lYW4oeCwgbmEucm0gPSBUUlVFKQogICAgICAgIG1lYW5feSA8LSBtZWFuKHksIG5hLnJtID0gVFJVRSkKICAgICAgICBzZF94IDwtIHNkKHgsIG5hLnJtID0gVFJVRSkKICAgICAgICBzZF95IDwtIHNkKHksIG5hLnJtID0gVFJVRSkKICAgICAgICAKICAgICAgICAjIENhbGN1bGF0ZSBwb29sZWQgU0QKICAgICAgICBwb29sZWRfc2QgPC0gc3FydCgoKGxlbmd0aCh4KSAtIDEpICogc2RfeF4yICsgKGxlbmd0aCh5KSAtIDEpICogc2RfeV4yKSAvIAogICAgICAgICAgICAgICAgICAgICAgICAgIChsZW5ndGgoeCkgKyBsZW5ndGgoeSkgLSAyKSkKICAgICAgICAKICAgICAgICAjIENhbGN1bGF0ZSBDb2hlbidzIGQKICAgICAgICBjb2hlbnNfZCA8LSAobWVhbl94IC0gbWVhbl95KSAvIHBvb2xlZF9zZAogICAgICAgIAogICAgICAgICMgUmV0dXJuIHJlc3VsdHMKICAgICAgICB0aWJibGUoCiAgICAgICAgICBmYWN0b3IgPSBncm91cHMkZmFjdG9yW2ldLAogICAgICAgICAgZmFjdG9yX2xhYmVsID0gZ3JvdXBzJGZhY3Rvcl9sYWJlbFtpXSwKICAgICAgICAgIGZhY3Rvcl90eXBlID0gZ3JvdXBzJGZhY3Rvcl90eXBlW2ldLAogICAgICAgICAgY29tcGFyaXNvbiA9IHBhc3RlKGNpdHkxLCAidnMiLCBjaXR5MiksCiAgICAgICAgICBjaXR5MSA9IGNpdHkxLAogICAgICAgICAgY2l0eTIgPSBjaXR5MiwKICAgICAgICAgIG1lYW5fZGlmZiA9IG1lYW5feCAtIG1lYW5feSwKICAgICAgICAgIGNvaGVuc19kID0gY29oZW5zX2QKICAgICAgICApCiAgICAgIH0pCiAgICB9KQogICAgCiAgICByZXR1cm4ocmVzdWx0KQogIH0pCiAgCiAgIyBDcmVhdGUgZWZmZWN0IHNpemUgaGVhdG1hcAogIGVmZmVjdF9zaXplX2hlYXRtYXAgPC0gZWZmZWN0X3NpemVzICU+JQogICAgc2VsZWN0KGZhY3Rvcl9sYWJlbCwgZmFjdG9yX3R5cGUsIGNvbXBhcmlzb24sIGNvaGVuc19kKSAlPiUKICAgIG11dGF0ZShlZmZlY3RfbWFnbml0dWRlID0gY2FzZV93aGVuKAogICAgICBhYnMoY29oZW5zX2QpIDwgMC4yIH4gIk5lZ2xpZ2libGUiLAogICAgICBhYnMoY29oZW5zX2QpIDwgMC41IH4gIlNtYWxsIiwKICAgICAgYWJzKGNvaGVuc19kKSA8IDAuOCB+ICJNZWRpdW0iLAogICAgICBUUlVFIH4gIkxhcmdlIgogICAgKSkKICAKICAjIFBsb3QgZWZmZWN0IHNpemVzCiAgcCA8LSBnZ3Bsb3QoZWZmZWN0X3NpemVfaGVhdG1hcCwgYWVzKHggPSBjb21wYXJpc29uLCB5ID0gZmFjdG9yX2xhYmVsLCBmaWxsID0gY29oZW5zX2QpKSArCiAgICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCBsaW5ld2lkdGggPSAwLjUpICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjJmIiwgY29oZW5zX2QpKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMykgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIoCiAgICAgIGxvdyA9ICIjRTVDNDk0RkYiLAogICAgICBtaWQgPSAid2hpdGUiLCAKICAgICAgaGlnaCA9ICIjRTc4QUMzRkYiLCAKICAgICAgbWlkcG9pbnQgPSAwLAogICAgICBuYW1lID0gIkNvaGVuJ3MgZCIKICAgICkgKwogICAgZmFjZXRfZ3JpZChmYWN0b3JfdHlwZSB+IC4sIHNjYWxlcyA9ICJmcmVlX3kiLCBzcGFjZSA9ICJmcmVlIikgKwogICAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpICsKICAgIHRoZW1lX2NvbW1vbgogIAogIHJldHVybihsaXN0KAogICAgZWZmZWN0X3NpemVzID0gZWZmZWN0X3NpemVzLAogICAgaGVhdG1hcCA9IGVmZmVjdF9zaXplX2hlYXRtYXAsCiAgICBwbG90ID0gcAogICkpCn0KYGBgCgojIyBFeGVjdXRlIGxhdGVudCBzY29yZSBhbmFseXNpcwpgYGB7ciBzY29yZXMgMn0KbGF0ZW50X3Njb3JlcyA8LSBleHRyYWN0X2xhdGVudF9zY29yZXMoaW52YXJpYW5jZV9tb2RlbHMkc2NhbGFyKQp6bXNwX3dpdGhfc2NvcmVzIDwtIHptc3AgJT4lIGxlZnRfam9pbihsYXRlbnRfc2NvcmVzLCBieSA9ICJJRCIpCmxhdGVudF9zY29yZXNfbG9uZyA8LSBwcmVwYXJlX2xhdGVudF9zY29yZXNfbG9uZyh6bXNwX3dpdGhfc2NvcmVzKQpmYWN0b3Jfc3VtbWFyaWVzIDwtIGNhbGN1bGF0ZV9mYWN0b3Jfc3VtbWFyaWVzKGxhdGVudF9zY29yZXNfbG9uZykKCiMgUnVuIEFOT1ZBIGFuZCBwb3N0LWhvYyB0ZXN0cyBmb3IgZWFjaCBsYXRlbnQgdmFyaWFibGUKaW5mZXJfcmVzdWx0cyA8LSBsYXBwbHkobGF0ZW50X3ZhcnMsIGFuYWx5emVfbGF0ZW50X3ZhciwgZGF0YSA9IHptc3Bfd2l0aF9zY29yZXMpCm5hbWVzKGluZmVyX3Jlc3VsdHMpIDwtIGxhdGVudF92YXJzCgojIENhbGN1bGF0ZSBlZmZlY3Qgc2l6ZXMKZWZmZWN0X3NpemVfcmVzdWx0cyA8LSBjYWxjdWxhdGVfZWZmZWN0X3NpemVzKGxhdGVudF9zY29yZXNfbG9uZykKYGBgCgojIyBQcmVzZW50CmBgYHtyIHNjb3JlcyAzfQoKIyBDcmVhdGUgdmlzdWFsaXphdGlvbnMKbGF0ZW50X2Rpc3RyaWJ1dGlvbnNfcGxvdCA8LSBwbG90X2xhdGVudF9kaXN0cmlidXRpb25zKGxhdGVudF9zY29yZXNfbG9uZykKZWZmZWN0X3NpemVfcGxvdCA8LSBlZmZlY3Rfc2l6ZV9yZXN1bHRzJHBsb3QKCiMgQ29tYmluZSBwbG90cwplZmZlY3Rfc2l6ZV9wbG90CmxhdGVudF9kaXN0cmlidXRpb25zX3Bsb3QKCmBgYAoKIyBOb3RlcwoqKkFJIEFzc2lzdGFuY2UgRGlzY2xvc3VyZSoqOiBXZSB1c2VkIENsYXVkZSAoQW50aHJvcGljKSBhcyBhIHRlY2huaWNhbCBwcm9ncmFtbWluZyBhc3Npc3RhbnQgcHJpbWFyaWx5IGZvciBSIGNvZGUgZGV2ZWxvcG1lbnQgYW5kIGltcGxlbWVudGF0aW9uLiBUaGUgYXNzaXN0YW5jZSBmb2N1c2VkIG9uOiAoMSkgZGVidWdnaW5nIGNvbXBsZXggUiB3b3JrZmxvd3MgdXNpbmcgYGxhdmFhbmAgYW5kIGBzZW1Ub29sc2AgcGFja2FnZXMsICgyKSBjcmVhdGluZyBjb21wcmVoZW5zaXZlIGRhdGEgdmlzdWFsaXphdGlvbiBzY3JpcHRzIHVzaW5nIGBnZ3Bsb3QyYCBhbmQgcmVsYXRlZCBwYWNrYWdlcywgYW5kICgzKSBzdHJ1Y3R1cmluZyByZXByb2R1Y2libGUgYW5hbHlzaXMgd29ya2Zsb3dzIGFuZCBIVE1MIHJlcG9ydCBjb21waWxhdGlvbi4gQWxsIHJlc2VhcmNoIGRlc2lnbiBkZWNpc2lvbnMsIHRoZW9yZXRpY2FsIGludGVycHJldGF0aW9ucywgbW9kZWwgc3BlY2lmaWNhdGlvbnMsIGFuZCBzdWJzdGFudGl2ZSBjb25jbHVzaW9ucyByZW1haW4gZW50aXJlbHkgb3VyIGludGVsbGVjdHVhbCBjb250cmlidXRpb24sIHdpdGggQUkgYXNzaXN0YW5jZSBzZXJ2aW5nIGFzIGEgdGVjaG5pY2FsIHByb2dyYW1taW5nIHRvb2wgYW5hbG9nb3VzIHRvIGFkdmFuY2VkIHN0YXRpc3RpY2FsIHNvZnR3YXJlIGRvY3VtZW50YXRpb24gd2l0aCBpbnRlcmFjdGl2ZSBwcm9ibGVtLXNvbHZpbmcgY2FwYWJpbGl0aWVzLgoK