Load Libraries

Purpose: Load required packages for data manipulation, visualization, and multivariate analysis.

Approach: Load tidyverse ecosystem and PCA visualization packages.

Expected outcome: All dependencies available for downstream analysis.

library(dplyr)
library(ggplot2)
library(lubridate)
library(MASS)
library(FactoMineR)
library(factoextra)
library(ggbeeswarm)
library(ggpubr)

Import LI-COR Data

Purpose: Import photosynthesis measurements from local CSV.

Approach: Read PhiPS2 data from exported CSV, parse sample IDs to extract field coordinates.

Expected outcome: Clean dataframe with CLY23_D4 plot IDs and PhiPS2 values.

# Read from local CSV (exported from Google Sheets)
licor_raw <- read.csv("licor_data.csv", skip = 1)

# Parse sample IDs
licor <- stringr::str_split(licor_raw$BZea, "_", simplify = TRUE) %>% 
  as.data.frame()
colnames(licor) <- c("field_year", "section", "CLY23_D4")
licor$CLY23_D4 <- as.numeric(licor$CLY23_D4)
licor$PhiPS2 <- as.numeric(licor_raw$PhiPS2)

# Diagnostics
{
cat("LI-COR records:", nrow(licor), "\n")
cat("PhiPS2 range:", range(licor$PhiPS2, na.rm = TRUE), "\n")
}
## LI-COR records: 2561 
## PhiPS2 range: -0.144505 0.975577

Import and Process CLY23 Field Data

Purpose: Import main phenotype data and calculate derived traits.

Approach: Read field book CSV, calculate flowering time metrics (DTS, DTA, ASI), parse donor identifiers, merge with LI-COR data.

Expected outcome: Complete CLY23 dataframe with all phenotypes and donor metadata.

planting_date <- mdy("4/4/2023")

CLY23 <- read.csv("CLY23_D4_FieldBook.csv") %>%
  rename(silking = "DTS", anthesis = "DTA", pedigree = "Female_genotype") %>%
  mutate(
    DTS = (mdy(silking) - planting_date) %>% as.integer(),
    DTA = (mdy(anthesis) - planting_date) %>% as.integer(),
    dSPAD = SPAD2 - SPAD1,
    donor_pop = gsub("-.*", "", pedigree, perl = TRUE),
    donor_line = gsub("_P.*?$", "", pedigree, perl = TRUE)
  ) %>%
  left_join(licor %>% dplyr::select(CLY23_D4, PhiPS2))

# Clean donor_line strings
CLY23$donor_line <- gsub("^.*?-", "", CLY23$donor_line, perl = TRUE)
CLY23$donor_line <- gsub("-+", "_", CLY23$donor_line, perl = TRUE)
CLY23$donor_line <- gsub("\\(.*", "", CLY23$donor_line, perl = TRUE)
CLY23$donor_line <- gsub("-", "_", CLY23$donor_line, perl = TRUE)
CLY23$donor_line <- gsub(" +$", "", CLY23$donor_line, perl = TRUE)

# Calculate derived traits
CLY23$ASI <- CLY23$DTS - CLY23$DTA
CLY23$LL <- CLY23$SL + CLY23$BL
CLY23$StPu <- as.factor(CLY23$StPu) %>% as.numeric() - 1
CLY23$StPi <- as.factor(CLY23$StPi) %>% as.numeric() - 1
CLY23$donor_line <- factor(CLY23$donor_line)

# Diagnostics
{
cat("CLY23 records:", nrow(CLY23), "\n")
cat("Donor populations:", length(unique(CLY23$donor_pop)), "\n")
cat("Donor lines:", nlevels(CLY23$donor_line), "\n")
cat("Available traits:", paste(colnames(CLY23), collapse = ", "), "\n")
}
## CLY23 records: 4446 
## Donor populations: 10 
## Donor lines: 83 
## Available traits: CLY23_D4, Rep, pedigree, Species, Notes, silking, anthesis, PH, EH, EN, BW, BL, SL, SPAD1, SPAD1_Date, SPAD2, SPAD2_Date, Leaf_Auringle, ST, StPu, StPi, Kinki, Prolif, NBR, LI_COR, LI_COR_Date, SCiO, SCiO_Date, DTS, DTA, dSPAD, donor_pop, donor_line, PhiPS2, ASI, LL

Define Trait Categories

Purpose: Organize phenotypic variables into functional categories for analysis and visualization.

Approach: Create vectors of trait names and corresponding category labels.

Expected outcome: Trait and category vectors for downstream analysis.

vars <- c("DTA", "DTS", "ASI", "PH", "EH", "EN", "BW", "BL", "SL", "LL", 
          "NBR", "SPAD1", "SPAD2", "dSPAD", "PhiPS2", "StPu", "StPi")

var_type <- c("FT", "FT", "FT", "Plant Size", "Ear", "Ear", "Plant Size", 
              "Plant Size", "Plant Size", "Plant Size", "Plant Size", 
              "Photosynthesis", "Photosynthesis", "Photosynthesis", "Photosynthesis", 
              "Highland", "Highland")

var_pal <- c("darkgoldenrod1", "darkred", "darkmagenta", "darkgreen", "burlywood")

# Diagnostics
{
cat("Traits defined:", length(vars), "\n")
cat("Categories:", paste(unique(var_type), collapse = ", "), "\n")
}
## Traits defined: 17 
## Categories: FT, Plant Size, Ear, Photosynthesis, Highland

Prepare PCA Data

Purpose: Aggregate phenotype data to genotype means for PCA.

Approach: Filter out controls, group by pedigree, calculate trait means across replicates.

Expected outcome: Genotype-level means suitable for multivariate analysis.

# Genotype categories for plotting
cats <- CLY23 %>%
  dplyr::filter(donor_pop != "Purple Check") %>%
  droplevels() %>%
  dplyr::select(donor_pop, donor_line, pedigree, Rep) %>%
  group_by(donor_pop, donor_line, pedigree) %>%
  summarize(n = n(), .groups = "drop")

# Genotype means for PCA
to_pca <- CLY23 %>%
  dplyr::select(donor_pop, donor_line, pedigree, Rep, all_of(vars)) %>%
  droplevels() %>%
  dplyr::filter(donor_pop != "Purple Check") %>%
  group_by(pedigree) %>%
  summarise(across(all_of(vars), ~ mean(.x, na.rm = TRUE)), .groups = "drop")

# Diagnostics
{
cat("Genotypes for PCA:", nrow(to_pca), "\n")
cat("Traits with complete data:", sum(colSums(is.na(to_pca[,-1])) == 0), "/", length(vars), "\n")
}
## Genotypes for PCA: 1329 
## Traits with complete data: 2 / 17

Principal Component Analysis

Purpose: Reduce dimensionality and identify major axes of phenotypic variation.

Approach: Run PCA on genotype means, visualize individual and variable contributions.

Expected outcome: PCA plots showing genotype clustering by donor population and trait loadings.

pca <- PCA(to_pca[, -1], graph = FALSE)

# Diagnostics
{
cat("=== PCA Variance Explained ===\n")
cat("PC1:", round(pca$eig[1, 2], 1), "%\n")
cat("PC2:", round(pca$eig[2, 2], 1), "%\n")
cat("Cumulative (PC1-2):", round(pca$eig[2, 3], 1), "%\n")
}
## === PCA Variance Explained ===
## PC1: 21.6 %
## PC2: 16.1 %
## Cumulative (PC1-2): 37.6 %
fviz_pca_ind(
  pca, 
  col.ind = cats$donor_pop, 
  geom = "point", 
  pointsize = 2,
  addEllipses = TRUE
) +
  guides(
    col = guide_legend(title = "Donor", override.aes = aes(label = "")),
    shape = "none", 
    fill = "none"
  ) +
  theme_classic2(base_size = 15)
PCA of genotypes colored by donor population

PCA of genotypes colored by donor population

fviz_pca_var(pca, col.var = as.factor(var_type)) +
  scale_color_manual(values = var_pal) +
  guides(
    col = guide_legend(title = "Type", override.aes = aes(label = ""))
  ) +
  theme_classic2(base_size = 15)
PCA variable loadings by trait category

PCA variable loadings by trait category

Trait Correlations

Purpose: Examine pairwise correlations among phenotypic traits.

Approach: Compute correlation matrix using pairwise complete observations.

Expected outcome: Correlation matrix revealing trait relationships.

cor_matrix <- cor(to_pca[, -1], use = "pairwise.complete.obs")
round(cor_matrix, 2)
##          DTA   DTS   ASI    PH    EH    EN    BW    BL    SL    LL   NBR SPAD1
## DTA     1.00  0.91  0.08  0.18  0.11 -0.18  0.10  0.15  0.17  0.17  0.06 -0.26
## DTS     0.91  1.00  0.44  0.16  0.07 -0.28  0.07  0.13  0.17  0.15  0.07 -0.27
## ASI     0.08  0.44  1.00 -0.03 -0.12 -0.30 -0.04 -0.03  0.06 -0.02 -0.02 -0.15
## PH      0.18  0.16 -0.03  1.00  0.74  0.02  0.28  0.42  0.54  0.48  0.34  0.17
## EH      0.11  0.07 -0.12  0.74  1.00  0.18  0.11  0.15  0.37  0.20  0.22  0.17
## EN     -0.18 -0.28 -0.30  0.02  0.18  1.00 -0.08 -0.10 -0.07 -0.11 -0.04  0.15
## BW      0.10  0.07 -0.04  0.28  0.11 -0.08  1.00  0.62  0.18  0.61  0.29  0.27
## BL      0.15  0.13 -0.03  0.42  0.15 -0.10  0.62  1.00  0.27  0.99  0.36  0.23
## SL      0.17  0.17  0.06  0.54  0.37 -0.07  0.18  0.27  1.00  0.42  0.18  0.05
## LL      0.17  0.15 -0.02  0.48  0.20 -0.11  0.61  0.99  0.42  1.00  0.37  0.22
## NBR     0.06  0.07 -0.02  0.34  0.22 -0.04  0.29  0.36  0.18  0.37  1.00  0.24
## SPAD1  -0.26 -0.27 -0.15  0.17  0.17  0.15  0.27  0.23  0.05  0.22  0.24  1.00
## SPAD2  -0.21 -0.27 -0.17  0.16  0.11  0.11  0.10  0.11  0.05  0.11  0.10  0.40
## dSPAD  -0.15 -0.20 -0.12  0.07  0.02  0.03 -0.01  0.00  0.02  0.00  0.00 -0.07
## PhiPS2 -0.06 -0.08 -0.08  0.12  0.07 -0.01  0.06  0.09  0.10  0.10  0.09  0.17
## StPu   -0.05 -0.05  0.00 -0.03  0.00 -0.02 -0.01 -0.03 -0.02 -0.03  0.06 -0.01
## StPi   -0.14 -0.14 -0.04  0.02  0.06  0.16 -0.08 -0.09 -0.03 -0.09  0.00 -0.06
##        SPAD2 dSPAD PhiPS2  StPu  StPi
## DTA    -0.21 -0.15  -0.06 -0.05 -0.14
## DTS    -0.27 -0.20  -0.08 -0.05 -0.14
## ASI    -0.17 -0.12  -0.08  0.00 -0.04
## PH      0.16  0.07   0.12 -0.03  0.02
## EH      0.11  0.02   0.07  0.00  0.06
## EN      0.11  0.03  -0.01 -0.02  0.16
## BW      0.10 -0.01   0.06 -0.01 -0.08
## BL      0.11  0.00   0.09 -0.03 -0.09
## SL      0.05  0.02   0.10 -0.02 -0.03
## LL      0.11  0.00   0.10 -0.03 -0.09
## NBR     0.10  0.00   0.09  0.06  0.00
## SPAD1   0.40 -0.07   0.17 -0.01 -0.06
## SPAD2   1.00  0.80   0.55 -0.01 -0.02
## dSPAD   0.80  1.00   0.46 -0.02  0.01
## PhiPS2  0.55  0.46   1.00 -0.02 -0.02
## StPu   -0.01 -0.02  -0.02  1.00  0.34
## StPi   -0.02  0.01  -0.02  0.34  1.00

Broad-Sense Heritability by Trait

Purpose: Estimate H² for each phenotypic trait.

Approach: For each trait, subset to genotypes with all 3 replicates, fit one-way ANOVA, partition variance.

Expected outcome: Heritability estimates for all measured traits.

her <- data.frame()

for (trait in vars) {
  # Remove NAs for this trait
  without_NA <- CLY23[!is.na(CLY23[, trait]), ]
  
  # Keep only genotypes with all 3 reps
  with_reps <- as.data.frame(table(line = without_NA$pedigree)) %>%
    filter(Freq == 3) %>%
    pull(line)
  
  to_H2 <- without_NA %>%
    filter(pedigree %in% with_reps)
  
  # Fit ANOVA model
  formula <- as.formula(paste0(trait, " ~ pedigree"))
  mod <- lm(data = to_H2, formula)
  mod_aov <- anova(mod)
  
  # Calculate H2
  her <- rbind(her, data.frame(
    trait = trait,
    H2 = mod_aov$`Sum Sq`[1] / sum(mod_aov$`Sum Sq`),
    n_geno = length(with_reps)
  ))
}

her$type <- var_type

# Diagnostics
{
cat("=== Heritability Estimates ===\n")
print(her %>% arrange(desc(H2)))
}
## === Heritability Estimates ===
##     trait        H2 n_geno           type
## 1      SL 0.7842433     12     Plant Size
## 2      PH 0.7212333   1010     Plant Size
## 3     DTA 0.7084150   1280             FT
## 4     DTS 0.6671166   1277             FT
## 5      EH 0.6432390   1008            Ear
## 6      LL 0.6175722     11     Plant Size
## 7     NBR 0.6163410     12     Plant Size
## 8      BL 0.6141236     11     Plant Size
## 9      EN 0.5899337   1008            Ear
## 10     BW 0.5273850     12     Plant Size
## 11  dSPAD 0.5096379     11 Photosynthesis
## 12  SPAD2 0.5087019    220 Photosynthesis
## 13    ASI 0.5063024   1274             FT
## 14   StPi 0.4515571   1307       Highland
## 15 PhiPS2 0.4506074    217 Photosynthesis
## 16  SPAD1 0.3891292     13 Photosynthesis
## 17   StPu 0.3884873   1307       Highland
her %>%
  mutate(trait = forcats::fct_reorder(trait, H2, .desc = TRUE)) %>%
  ggplot(aes(x = trait, y = H2, fill = type)) +
  geom_col() +
  ylab("Broad-Sense Heritability (H²)") +
  xlab("Trait") +
  scale_fill_manual(values = var_pal) +
  theme_classic2(base_size = 15) +
  theme(
    axis.text.x = element_text(angle = 90, hjust = 1, vjust = 0.5),
    legend.position = "top"
  )
Broad-sense heritability by trait

Broad-sense heritability by trait

Import CLY22 Brace Root Data

Purpose: Import previous year’s brace root data for cross-year comparison.

Approach: Read CLY22 field book, filter to J2Teo project, parse donor identifiers.

Expected outcome: Clean CLY22 dataframe for GxE analysis.

CLY22 <- read.csv("CLY-22-RRA-Field-Book - B6Field.csv") %>%
  dplyr::filter(Project == "J2Teo") %>%
  dplyr::mutate(
    donor_pop = gsub("-.*", "", Description, perl = TRUE),
    donor_line = gsub("_P.*?$", "", Description, perl = TRUE)
  )

CLY22$brace_root <- as.numeric(CLY22$brace_root)

# Clean donor_line strings
CLY22$donor_line <- gsub("^.*?-", "", CLY22$donor_line, perl = TRUE)
CLY22$donor_line <- gsub("-+", "_", CLY22$donor_line, perl = TRUE)
CLY22$donor_line <- gsub("\\(.*", "", CLY22$donor_line, perl = TRUE)
CLY22$donor_line <- gsub("-", "_", CLY22$donor_line, perl = TRUE)
CLY22$donor_line <- gsub(" +$", "", CLY22$donor_line, perl = TRUE)

# Diagnostics
{
cat("CLY22 J2Teo records:", nrow(CLY22), "\n")
cat("Brace root range:", range(CLY22$brace_root, na.rm = TRUE), "\n")
}
## CLY22 J2Teo records: 2408 
## Brace root range: 0 6

Combine Brace Root Data Across Years

Purpose: Merge brace root measurements from CLY22 and CLY23 for GxE analysis.

Approach: Standardize column names, stack datasets with field year indicator.

Expected outcome: Combined dataframe for cross-environment analysis.

BR <- rbind(
  CLY22 %>%
    dplyr::select(donor_line, donor_pop, NBR = brace_root) %>%
    dplyr::filter(donor_pop != "Purple Check") %>%
    mutate(field = "CLY22"),
  
  CLY23 %>%
    dplyr::filter(Rep == 3) %>%
    dplyr::select(donor_line, donor_pop, NBR) %>%
    dplyr::filter(donor_pop != "Purple Check") %>%
    mutate(field = "CLY23")
)

field_mean <- BR %>%
  group_by(field) %>%
  summarise(br_mean = mean(NBR, na.rm = TRUE), .groups = "drop")

# Diagnostics
{
cat("=== Combined Brace Root Data ===\n")
print(table(BR$field, BR$donor_pop))
cat("\nField means:\n")
print(field_mean)
}
## === Combined Brace Root Data ===
##        
##         B73 Bals Chal Dura Hueh Mesa Nabo Zdip Zlux
##   CLY22  17  570  195  359   84  338  212  424  197
##   CLY23 130  363  120  137   55  177  136  244  114
## 
## Field means:
## # A tibble: 2 × 2
##   field br_mean
##   <chr>   <dbl>
## 1 CLY22    1.88
## 2 CLY23    1.17

Brace Root Variation by Population and Year

Purpose: Visualize brace root number variation across donor populations and field years.

Approach: Faceted beeswarm plots with population means and field-level reference lines.

Expected outcome: Visual comparison of NBR distributions across populations and environments.

BR %>%
  ggplot(aes(x = donor_pop, y = NBR, group = donor_pop, col = donor_pop)) +
  geom_quasirandom() +
  geom_hline(
    data = field_mean,
    aes(yintercept = br_mean),
    colour = "black", linetype = 2
  ) +
  stat_summary(
    fun.data = mean_cl_normal,
    geom = "pointrange",
    col = "black"
  ) +
  facet_wrap(~field, ncol = 1) +
  scale_y_continuous(breaks = 0:6) +
  xlab("Donor Population") +
  ylab("Number of Brace Roots") +
  theme_classic2(base_size = 15) +
  theme(
    legend.position = "none",
    strip.background = element_rect(colour = "white", fill = "white"),
    axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)
  )
Brace root number by donor population and field year

Brace root number by donor population and field year

Brace Root Heritability by Year

Purpose: Estimate heritability of brace root number within each field year.

Approach: Fit one-way ANOVA (NBR ~ donor_line) separately for each year.

Expected outcome: Year-specific H² estimates for brace root number.

# CLY22 heritability
br22_aov <- lm(formula = NBR ~ donor_line, data = BR[BR$field == "CLY22", ])
s22 <- summary(br22_aov)

# CLY23 heritability
br23_aov <- lm(formula = NBR ~ donor_line, data = BR[BR$field == "CLY23", ])
s23 <- summary(br23_aov)

# Diagnostics
{
cat("=== Brace Root H² by Year ===\n")
cat("CLY22 H² (R²):", round(s22$r.squared, 3), "\n")
cat("CLY23 H² (R²):", round(s23$r.squared, 3), "\n")
}
## === Brace Root H² by Year ===
## CLY22 H² (R²): 0.257 
## CLY23 H² (R²): 0.064

Genotype × Environment Analysis

Purpose: Partition variance into genotype, environment, and GxE components for brace root.

Approach: Fit two-way ANOVA with interaction (NBR ~ donor_line * field), extract variance components.

Expected outcome: Proportion of variance explained by genotype, field year, and their interaction.

# Full GxE model
br_gxe_mod <- lm(formula = NBR ~ donor_line * field, data = BR)
gxe_aov <- anova(br_gxe_mod)

# Variance components
var_components <- data.frame(
  source = c("Genotype", "Field", "G×E", "Residual"),
  sum_sq = gxe_aov$`Sum Sq`,
  prop_var = gxe_aov$`Sum Sq` / sum(gxe_aov$`Sum Sq`)
)

# Summary table
gxe_summary <- data.frame(
  field = c("CLY22", "CLY23", "Combined"),
  H2_fam = c(
    s22$r.squared, 
    s23$r.squared, 
    gxe_aov$`Sum Sq`[1] / sum(gxe_aov$`Sum Sq`)
  ),
  field_year = c(NA, NA, gxe_aov$`Sum Sq`[2] / sum(gxe_aov$`Sum Sq`)),
  GxFY = c(NA, NA, gxe_aov$`Sum Sq`[3] / sum(gxe_aov$`Sum Sq`))
)

# Diagnostics
{
cat("=== Variance Components ===\n")
print(var_components)
cat("\n=== GxE Summary ===\n")
print(gxe_summary)
}
## === Variance Components ===
##     source    sum_sq   prop_var
## 1 Genotype  209.6703 0.08738041
## 2    Field  464.9371 0.19376325
## 3      G×E  150.4479 0.06269938
## 4 Residual 1574.4560 0.65615696
## 
## === GxE Summary ===
##      field     H2_fam field_year       GxFY
## 1    CLY22 0.25666810         NA         NA
## 2    CLY23 0.06435725         NA         NA
## 3 Combined 0.08738041  0.1937632 0.06269938

Population-Level GxE Analysis

Purpose: Estimate heritability and GxE within each donor population.

Approach: Split data by population, fit GxE models where both years are represented.

Expected outcome: Population-specific variance partitioning.

BR$field_donor <- interaction(factor(BR$field), factor(BR$donor_pop))

# Diagnostic: show donor_line counts per field × donor_pop combination
line_counts <- BR %>%
  group_by(field, donor_pop) %>%
  summarise(
    n_obs = n(),
    n_lines = n_distinct(donor_line),
    .groups = "drop"
  ) %>%
  arrange(n_lines)

# Diagnostics
{
cat("=== Donor Lines per Field × Population ===\n")
cat("(Combinations with n_lines < 2 will be skipped)\n\n")
print(as.data.frame(line_counts))
cat("\nSkipped combinations:", sum(line_counts$n_lines < 2), "\n")
}
## === Donor Lines per Field × Population ===
## (Combinations with n_lines < 2 will be skipped)
## 
##    field donor_pop n_obs n_lines
## 1  CLY22       B73    17       1
## 2  CLY23       B73   130       1
## 3  CLY22      Hueh    84       2
## 4  CLY22      Nabo   212       2
## 5  CLY23      Hueh    55       2
## 6  CLY23      Nabo   136       2
## 7  CLY22      Dura   359       3
## 8  CLY23      Dura   137       3
## 9  CLY22      Zdip   424       4
## 10 CLY23      Zdip   244       4
## 11 CLY22      Zlux   197       5
## 12 CLY23      Zlux   114       5
## 13 CLY22      Chal   195      13
## 14 CLY23      Chal   120      13
## 15 CLY22      Mesa   338      17
## 16 CLY23      Mesa   177      17
## 17 CLY22      Bals   570      35
## 18 CLY23      Bals   363      35
## 
## Skipped combinations: 2
# H2 by population within each field
br_list_field <- split(BR, f = BR$field_donor)

H2_pop <- lapply(names(br_list_field), FUN = function(x) {
  df <- br_list_field[[x]]
  n_lines <- length(unique(df$donor_line))
  if (nrow(df) < 3 || n_lines < 2) return(NULL)
  
  df$donor_line <- factor(df$donor_line)
  lm_aov <- lm(data = df, NBR ~ donor_line) %>% anova()
  
  data.frame(
    field = df$field[1],
    donor_pop = df$donor_pop[1],
    H2_fam = lm_aov$`Sum Sq`[1] / sum(lm_aov$`Sum Sq`)
  )
}) %>% 
  dplyr::bind_rows()

# GxE by population (across fields)
br_list_pop <- split(BR, f = BR$donor_pop)

# Diagnostic: show which populations have data in both fields
pop_field_summary <- BR %>%
  group_by(donor_pop) %>%
  summarise(
    n_fields = n_distinct(field),
    n_lines = n_distinct(donor_line),
    .groups = "drop"
  )

# Diagnostics
{
cat("\n=== Population Summary for GxE Analysis ===\n")
cat("(Populations with n_fields < 2 or n_lines < 2 will be skipped)\n\n")
print(as.data.frame(pop_field_summary))
}
## 
## === Population Summary for GxE Analysis ===
## (Populations with n_fields < 2 or n_lines < 2 will be skipped)
## 
##   donor_pop n_fields n_lines
## 1       B73        2       1
## 2      Bals        2      35
## 3      Chal        2      13
## 4      Dura        2       3
## 5      Hueh        2       2
## 6      Mesa        2      17
## 7      Nabo        2       2
## 8      Zdip        2       4
## 9      Zlux        2       5
H2_pop_gxe <- lapply(names(br_list_pop), FUN = function(x) {
  df <- br_list_pop[[x]]
  field_count <- table(df$donor_line, df$field) %>% as.data.frame()
  n_field <- length(unique(field_count[, 2]))
  n_lines <- length(unique(df$donor_line))
  
  if (n_field < 2 || n_lines < 2) return(NULL)
  
  df$donor_line <- factor(df$donor_line)
  lm_aov <- lm(data = df, NBR ~ donor_line * field) %>% anova()
  
  data.frame(
    donor_pop = df$donor_pop[1],
    H2_fam = lm_aov$`Sum Sq`[1] / sum(lm_aov$`Sum Sq`),
    GxF = lm_aov$`Sum Sq`[3] / sum(lm_aov$`Sum Sq`),
    field = lm_aov$`Sum Sq`[2] / sum(lm_aov$`Sum Sq`),
    residuals = lm_aov$`Sum Sq`[4] / sum(lm_aov$`Sum Sq`)
  )
}) %>% 
  dplyr::bind_rows()

# Diagnostics
{
cat("=== H² by Population and Field ===\n")
print(H2_pop)
cat("\n=== Population-Level GxE ===\n")
print(H2_pop_gxe)
}
## === H² by Population and Field ===
##    field donor_pop      H2_fam
## 1  CLY22      Bals 0.184208997
## 2  CLY23      Bals 0.091356100
## 3  CLY22      Chal 0.142280183
## 4  CLY23      Chal 0.134074433
## 5  CLY22      Dura 0.069233218
## 6  CLY23      Dura 0.011233303
## 7  CLY22      Hueh 0.014584654
## 8  CLY23      Hueh 0.003906250
## 9  CLY22      Mesa 0.366538899
## 10 CLY23      Mesa 0.131949990
## 11 CLY22      Nabo 0.005970679
## 12 CLY23      Nabo 0.005851729
## 13 CLY22      Zdip 0.120948596
## 14 CLY23      Zdip 0.012394894
## 15 CLY22      Zlux 0.201291366
## 16 CLY23      Zlux 0.006585232
## 
## === Population-Level GxE ===
##   donor_pop       H2_fam         GxF     field residuals
## 1      Bals 0.0741531279 0.046077328 0.1749622 0.7048073
## 2      Chal 0.0488590559 0.051865210 0.2526499 0.6466259
## 3      Dura 0.0214560436 0.015308706 0.2347739 0.7284613
## 4      Hueh 0.0053188460 0.004473805 0.4374807 0.5527267
## 5      Mesa 0.1575494230 0.127015582 0.0461216 0.6693134
## 6      Nabo 0.0008535911 0.003171211 0.4405808 0.5553944
## 7      Zdip 0.0275514370 0.039477679 0.1627404 0.7702305
## 8      Zlux 0.0594412897 0.039878067 0.2614525 0.6392282

Session Info

sessionInfo()
## R version 4.5.2 (2025-10-31)
## Platform: aarch64-apple-darwin20
## Running under: macOS Tahoe 26.2
## 
## Matrix products: default
## BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
## 
## locale:
## [1] C.UTF-8/C.UTF-8/C.UTF-8/C/C.UTF-8/C.UTF-8
## 
## time zone: America/New_York
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggpubr_0.6.2     ggbeeswarm_0.7.3 factoextra_1.0.7 FactoMineR_2.13 
## [5] MASS_7.3-65      lubridate_1.9.4  ggplot2_4.0.1    dplyr_1.1.4     
## 
## loaded via a namespace (and not attached):
##  [1] tidyselect_1.2.1     vipor_0.4.7          farver_2.1.2        
##  [4] S7_0.2.1             fastmap_1.2.0        digest_0.6.39       
##  [7] rpart_4.1.24         timechange_0.3.0     estimability_1.5.1  
## [10] lifecycle_1.0.4      cluster_2.1.8.1      multcompView_0.1-10 
## [13] magrittr_2.0.4       compiler_4.5.2       rlang_1.1.6         
## [16] Hmisc_5.2-4          sass_0.4.10          tools_4.5.2         
## [19] utf8_1.2.6           yaml_2.3.12          data.table_1.18.0   
## [22] knitr_1.51           ggsignif_0.6.4       labeling_0.4.3      
## [25] htmlwidgets_1.6.4    scatterplot3d_0.3-44 RColorBrewer_1.1-3  
## [28] abind_1.4-8          withr_3.0.2          foreign_0.8-90      
## [31] purrr_1.2.0          nnet_7.3-20          grid_4.5.2          
## [34] xtable_1.8-4         colorspace_2.1-2     emmeans_2.0.1       
## [37] scales_1.4.0         dichromat_2.0-0.1    flashClust_1.01-2   
## [40] cli_3.6.5            mvtnorm_1.3-3        rmarkdown_2.30      
## [43] generics_0.1.4       otel_0.2.0           rstudioapi_0.17.1   
## [46] cachem_1.1.0         stringr_1.6.0        base64enc_0.1-3     
## [49] vctrs_0.6.5          jsonlite_2.0.0       carData_3.0-5       
## [52] car_3.1-3            rstatix_0.7.3        ggrepel_0.9.6       
## [55] Formula_1.2-5        htmlTable_2.4.3      beeswarm_0.4.0      
## [58] tidyr_1.3.2          jquerylib_0.1.4      glue_1.8.0          
## [61] DT_0.34.0            stringi_1.8.7        gtable_0.3.6        
## [64] tibble_3.3.0         pillar_1.11.1        htmltools_0.5.9     
## [67] R6_2.6.1             evaluate_1.0.5       lattice_0.22-7      
## [70] backports_1.5.0      leaps_3.2            broom_1.0.11        
## [73] bslib_0.9.0          Rcpp_1.1.0           coda_0.19-4.1       
## [76] gridExtra_2.3        checkmate_2.3.3      xfun_0.55           
## [79] forcats_1.0.1        pkgconfig_2.0.3
LS0tCnRpdGxlOiAiQ0xZMjMgUGhlbm90eXBlIEFuYWx5c2lzOiBQQ0EgYW5kIEhlcml0YWJpbGl0eSIKYXV0aG9yOiAiQW5hbHlzaXMgUmVwb3J0IgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGZsYXRseQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsCiAgd2FybmluZyA9IEZBTFNFLAogIG1lc3NhZ2UgPSBGQUxTRSwKICBmaWcud2lkdGggPSAxMCwKICBmaWcuaGVpZ2h0ID0gNwopCgojIFNldCB3b3JraW5nIGRpcmVjdG9yeQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICIvVXNlcnMvZnZyb2RyaWd1ZXovRGVza3RvcC9pbnY0bS9oZXJpdGFiaWxpdHkiKQpgYGAKCiMjIExvYWQgTGlicmFyaWVzCgoqKlB1cnBvc2UqKjogTG9hZCByZXF1aXJlZCBwYWNrYWdlcyBmb3IgZGF0YSBtYW5pcHVsYXRpb24sIHZpc3VhbGl6YXRpb24sIGFuZCBtdWx0aXZhcmlhdGUgYW5hbHlzaXMuCgoqKkFwcHJvYWNoKio6IExvYWQgdGlkeXZlcnNlIGVjb3N5c3RlbSBhbmQgUENBIHZpc3VhbGl6YXRpb24gcGFja2FnZXMuCgoqKkV4cGVjdGVkIG91dGNvbWUqKjogQWxsIGRlcGVuZGVuY2llcyBhdmFpbGFibGUgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCgpgYGB7ciBsb2FkX2xpYnJhcmllc30KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KEZhY3RvTWluZVIpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShnZ2JlZXN3YXJtKQpsaWJyYXJ5KGdncHVicikKYGBgCgojIyBJbXBvcnQgTEktQ09SIERhdGEKCioqUHVycG9zZSoqOiBJbXBvcnQgcGhvdG9zeW50aGVzaXMgbWVhc3VyZW1lbnRzIGZyb20gbG9jYWwgQ1NWLgoKKipBcHByb2FjaCoqOiBSZWFkIFBoaVBTMiBkYXRhIGZyb20gZXhwb3J0ZWQgQ1NWLCBwYXJzZSBzYW1wbGUgSURzIHRvIGV4dHJhY3QgZmllbGQgY29vcmRpbmF0ZXMuCgoqKkV4cGVjdGVkIG91dGNvbWUqKjogQ2xlYW4gZGF0YWZyYW1lIHdpdGggQ0xZMjNfRDQgcGxvdCBJRHMgYW5kIFBoaVBTMiB2YWx1ZXMuCgpgYGB7ciBpbXBvcnRfbGljb3J9CiMgUmVhZCBmcm9tIGxvY2FsIENTViAoZXhwb3J0ZWQgZnJvbSBHb29nbGUgU2hlZXRzKQpsaWNvcl9yYXcgPC0gcmVhZC5jc3YoImxpY29yX2RhdGEuY3N2Iiwgc2tpcCA9IDEpCgojIFBhcnNlIHNhbXBsZSBJRHMKbGljb3IgPC0gc3RyaW5ncjo6c3RyX3NwbGl0KGxpY29yX3JhdyRCWmVhLCAiXyIsIHNpbXBsaWZ5ID0gVFJVRSkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQpjb2xuYW1lcyhsaWNvcikgPC0gYygiZmllbGRfeWVhciIsICJzZWN0aW9uIiwgIkNMWTIzX0Q0IikKbGljb3IkQ0xZMjNfRDQgPC0gYXMubnVtZXJpYyhsaWNvciRDTFkyM19ENCkKbGljb3IkUGhpUFMyIDwtIGFzLm51bWVyaWMobGljb3JfcmF3JFBoaVBTMikKCiMgRGlhZ25vc3RpY3MKewpjYXQoIkxJLUNPUiByZWNvcmRzOiIsIG5yb3cobGljb3IpLCAiXG4iKQpjYXQoIlBoaVBTMiByYW5nZToiLCByYW5nZShsaWNvciRQaGlQUzIsIG5hLnJtID0gVFJVRSksICJcbiIpCn0KYGBgCgojIyBJbXBvcnQgYW5kIFByb2Nlc3MgQ0xZMjMgRmllbGQgRGF0YQoKKipQdXJwb3NlKio6IEltcG9ydCBtYWluIHBoZW5vdHlwZSBkYXRhIGFuZCBjYWxjdWxhdGUgZGVyaXZlZCB0cmFpdHMuCgoqKkFwcHJvYWNoKio6IFJlYWQgZmllbGQgYm9vayBDU1YsIGNhbGN1bGF0ZSBmbG93ZXJpbmcgdGltZSBtZXRyaWNzIChEVFMsIERUQSwgQVNJKSwgcGFyc2UgZG9ub3IgaWRlbnRpZmllcnMsIG1lcmdlIHdpdGggTEktQ09SIGRhdGEuCgoqKkV4cGVjdGVkIG91dGNvbWUqKjogQ29tcGxldGUgQ0xZMjMgZGF0YWZyYW1lIHdpdGggYWxsIHBoZW5vdHlwZXMgYW5kIGRvbm9yIG1ldGFkYXRhLgoKYGBge3IgaW1wb3J0X2NseTIzfQpwbGFudGluZ19kYXRlIDwtIG1keSgiNC80LzIwMjMiKQoKQ0xZMjMgPC0gcmVhZC5jc3YoIkNMWTIzX0Q0X0ZpZWxkQm9vay5jc3YiKSAlPiUKICByZW5hbWUoc2lsa2luZyA9ICJEVFMiLCBhbnRoZXNpcyA9ICJEVEEiLCBwZWRpZ3JlZSA9ICJGZW1hbGVfZ2Vub3R5cGUiKSAlPiUKICBtdXRhdGUoCiAgICBEVFMgPSAobWR5KHNpbGtpbmcpIC0gcGxhbnRpbmdfZGF0ZSkgJT4lIGFzLmludGVnZXIoKSwKICAgIERUQSA9IChtZHkoYW50aGVzaXMpIC0gcGxhbnRpbmdfZGF0ZSkgJT4lIGFzLmludGVnZXIoKSwKICAgIGRTUEFEID0gU1BBRDIgLSBTUEFEMSwKICAgIGRvbm9yX3BvcCA9IGdzdWIoIi0uKiIsICIiLCBwZWRpZ3JlZSwgcGVybCA9IFRSVUUpLAogICAgZG9ub3JfbGluZSA9IGdzdWIoIl9QLio/JCIsICIiLCBwZWRpZ3JlZSwgcGVybCA9IFRSVUUpCiAgKSAlPiUKICBsZWZ0X2pvaW4obGljb3IgJT4lIGRwbHlyOjpzZWxlY3QoQ0xZMjNfRDQsIFBoaVBTMikpCgojIENsZWFuIGRvbm9yX2xpbmUgc3RyaW5ncwpDTFkyMyRkb25vcl9saW5lIDwtIGdzdWIoIl4uKj8tIiwgIiIsIENMWTIzJGRvbm9yX2xpbmUsIHBlcmwgPSBUUlVFKQpDTFkyMyRkb25vcl9saW5lIDwtIGdzdWIoIi0rIiwgIl8iLCBDTFkyMyRkb25vcl9saW5lLCBwZXJsID0gVFJVRSkKQ0xZMjMkZG9ub3JfbGluZSA8LSBnc3ViKCJcXCguKiIsICIiLCBDTFkyMyRkb25vcl9saW5lLCBwZXJsID0gVFJVRSkKQ0xZMjMkZG9ub3JfbGluZSA8LSBnc3ViKCItIiwgIl8iLCBDTFkyMyRkb25vcl9saW5lLCBwZXJsID0gVFJVRSkKQ0xZMjMkZG9ub3JfbGluZSA8LSBnc3ViKCIgKyQiLCAiIiwgQ0xZMjMkZG9ub3JfbGluZSwgcGVybCA9IFRSVUUpCgojIENhbGN1bGF0ZSBkZXJpdmVkIHRyYWl0cwpDTFkyMyRBU0kgPC0gQ0xZMjMkRFRTIC0gQ0xZMjMkRFRBCkNMWTIzJExMIDwtIENMWTIzJFNMICsgQ0xZMjMkQkwKQ0xZMjMkU3RQdSA8LSBhcy5mYWN0b3IoQ0xZMjMkU3RQdSkgJT4lIGFzLm51bWVyaWMoKSAtIDEKQ0xZMjMkU3RQaSA8LSBhcy5mYWN0b3IoQ0xZMjMkU3RQaSkgJT4lIGFzLm51bWVyaWMoKSAtIDEKQ0xZMjMkZG9ub3JfbGluZSA8LSBmYWN0b3IoQ0xZMjMkZG9ub3JfbGluZSkKCiMgRGlhZ25vc3RpY3MKewpjYXQoIkNMWTIzIHJlY29yZHM6IiwgbnJvdyhDTFkyMyksICJcbiIpCmNhdCgiRG9ub3IgcG9wdWxhdGlvbnM6IiwgbGVuZ3RoKHVuaXF1ZShDTFkyMyRkb25vcl9wb3ApKSwgIlxuIikKY2F0KCJEb25vciBsaW5lczoiLCBubGV2ZWxzKENMWTIzJGRvbm9yX2xpbmUpLCAiXG4iKQpjYXQoIkF2YWlsYWJsZSB0cmFpdHM6IiwgcGFzdGUoY29sbmFtZXMoQ0xZMjMpLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQp9CmBgYAoKIyMgRGVmaW5lIFRyYWl0IENhdGVnb3JpZXMKCioqUHVycG9zZSoqOiBPcmdhbml6ZSBwaGVub3R5cGljIHZhcmlhYmxlcyBpbnRvIGZ1bmN0aW9uYWwgY2F0ZWdvcmllcyBmb3IgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24uCgoqKkFwcHJvYWNoKio6IENyZWF0ZSB2ZWN0b3JzIG9mIHRyYWl0IG5hbWVzIGFuZCBjb3JyZXNwb25kaW5nIGNhdGVnb3J5IGxhYmVscy4KCioqRXhwZWN0ZWQgb3V0Y29tZSoqOiBUcmFpdCBhbmQgY2F0ZWdvcnkgdmVjdG9ycyBmb3IgZG93bnN0cmVhbSBhbmFseXNpcy4KCmBgYHtyIGRlZmluZV90cmFpdHN9CnZhcnMgPC0gYygiRFRBIiwgIkRUUyIsICJBU0kiLCAiUEgiLCAiRUgiLCAiRU4iLCAiQlciLCAiQkwiLCAiU0wiLCAiTEwiLCAKICAgICAgICAgICJOQlIiLCAiU1BBRDEiLCAiU1BBRDIiLCAiZFNQQUQiLCAiUGhpUFMyIiwgIlN0UHUiLCAiU3RQaSIpCgp2YXJfdHlwZSA8LSBjKCJGVCIsICJGVCIsICJGVCIsICJQbGFudCBTaXplIiwgIkVhciIsICJFYXIiLCAiUGxhbnQgU2l6ZSIsIAogICAgICAgICAgICAgICJQbGFudCBTaXplIiwgIlBsYW50IFNpemUiLCAiUGxhbnQgU2l6ZSIsICJQbGFudCBTaXplIiwgCiAgICAgICAgICAgICAgIlBob3Rvc3ludGhlc2lzIiwgIlBob3Rvc3ludGhlc2lzIiwgIlBob3Rvc3ludGhlc2lzIiwgIlBob3Rvc3ludGhlc2lzIiwgCiAgICAgICAgICAgICAgIkhpZ2hsYW5kIiwgIkhpZ2hsYW5kIikKCnZhcl9wYWwgPC0gYygiZGFya2dvbGRlbnJvZDEiLCAiZGFya3JlZCIsICJkYXJrbWFnZW50YSIsICJkYXJrZ3JlZW4iLCAiYnVybHl3b29kIikKCiMgRGlhZ25vc3RpY3MKewpjYXQoIlRyYWl0cyBkZWZpbmVkOiIsIGxlbmd0aCh2YXJzKSwgIlxuIikKY2F0KCJDYXRlZ29yaWVzOiIsIHBhc3RlKHVuaXF1ZSh2YXJfdHlwZSksIGNvbGxhcHNlID0gIiwgIiksICJcbiIpCn0KYGBgCgojIyBQcmVwYXJlIFBDQSBEYXRhCgoqKlB1cnBvc2UqKjogQWdncmVnYXRlIHBoZW5vdHlwZSBkYXRhIHRvIGdlbm90eXBlIG1lYW5zIGZvciBQQ0EuCgoqKkFwcHJvYWNoKio6IEZpbHRlciBvdXQgY29udHJvbHMsIGdyb3VwIGJ5IHBlZGlncmVlLCBjYWxjdWxhdGUgdHJhaXQgbWVhbnMgYWNyb3NzIHJlcGxpY2F0ZXMuCgoqKkV4cGVjdGVkIG91dGNvbWUqKjogR2Vub3R5cGUtbGV2ZWwgbWVhbnMgc3VpdGFibGUgZm9yIG11bHRpdmFyaWF0ZSBhbmFseXNpcy4KCmBgYHtyIHByZXBfcGNhfQojIEdlbm90eXBlIGNhdGVnb3JpZXMgZm9yIHBsb3R0aW5nCmNhdHMgPC0gQ0xZMjMgJT4lCiAgZHBseXI6OmZpbHRlcihkb25vcl9wb3AgIT0gIlB1cnBsZSBDaGVjayIpICU+JQogIGRyb3BsZXZlbHMoKSAlPiUKICBkcGx5cjo6c2VsZWN0KGRvbm9yX3BvcCwgZG9ub3JfbGluZSwgcGVkaWdyZWUsIFJlcCkgJT4lCiAgZ3JvdXBfYnkoZG9ub3JfcG9wLCBkb25vcl9saW5lLCBwZWRpZ3JlZSkgJT4lCiAgc3VtbWFyaXplKG4gPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpCgojIEdlbm90eXBlIG1lYW5zIGZvciBQQ0EKdG9fcGNhIDwtIENMWTIzICU+JQogIGRwbHlyOjpzZWxlY3QoZG9ub3JfcG9wLCBkb25vcl9saW5lLCBwZWRpZ3JlZSwgUmVwLCBhbGxfb2YodmFycykpICU+JQogIGRyb3BsZXZlbHMoKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGRvbm9yX3BvcCAhPSAiUHVycGxlIENoZWNrIikgJT4lCiAgZ3JvdXBfYnkocGVkaWdyZWUpICU+JQogIHN1bW1hcmlzZShhY3Jvc3MoYWxsX29mKHZhcnMpLCB+IG1lYW4oLngsIG5hLnJtID0gVFJVRSkpLCAuZ3JvdXBzID0gImRyb3AiKQoKIyBEaWFnbm9zdGljcwp7CmNhdCgiR2Vub3R5cGVzIGZvciBQQ0E6IiwgbnJvdyh0b19wY2EpLCAiXG4iKQpjYXQoIlRyYWl0cyB3aXRoIGNvbXBsZXRlIGRhdGE6Iiwgc3VtKGNvbFN1bXMoaXMubmEodG9fcGNhWywtMV0pKSA9PSAwKSwgIi8iLCBsZW5ndGgodmFycyksICJcbiIpCn0KYGBgCgojIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzCgoqKlB1cnBvc2UqKjogUmVkdWNlIGRpbWVuc2lvbmFsaXR5IGFuZCBpZGVudGlmeSBtYWpvciBheGVzIG9mIHBoZW5vdHlwaWMgdmFyaWF0aW9uLgoKKipBcHByb2FjaCoqOiBSdW4gUENBIG9uIGdlbm90eXBlIG1lYW5zLCB2aXN1YWxpemUgaW5kaXZpZHVhbCBhbmQgdmFyaWFibGUgY29udHJpYnV0aW9ucy4KCioqRXhwZWN0ZWQgb3V0Y29tZSoqOiBQQ0EgcGxvdHMgc2hvd2luZyBnZW5vdHlwZSBjbHVzdGVyaW5nIGJ5IGRvbm9yIHBvcHVsYXRpb24gYW5kIHRyYWl0IGxvYWRpbmdzLgoKYGBge3IgcnVuX3BjYX0KcGNhIDwtIFBDQSh0b19wY2FbLCAtMV0sIGdyYXBoID0gRkFMU0UpCgojIERpYWdub3N0aWNzCnsKY2F0KCI9PT0gUENBIFZhcmlhbmNlIEV4cGxhaW5lZCA9PT1cbiIpCmNhdCgiUEMxOiIsIHJvdW5kKHBjYSRlaWdbMSwgMl0sIDEpLCAiJVxuIikKY2F0KCJQQzI6Iiwgcm91bmQocGNhJGVpZ1syLCAyXSwgMSksICIlXG4iKQpjYXQoIkN1bXVsYXRpdmUgKFBDMS0yKToiLCByb3VuZChwY2EkZWlnWzIsIDNdLCAxKSwgIiVcbiIpCn0KYGBgCgpgYGB7ciBwY2FfaW5kaXZpZHVhbHMsIGZpZy5jYXA9IlBDQSBvZiBnZW5vdHlwZXMgY29sb3JlZCBieSBkb25vciBwb3B1bGF0aW9uIn0KZnZpel9wY2FfaW5kKAogIHBjYSwgCiAgY29sLmluZCA9IGNhdHMkZG9ub3JfcG9wLCAKICBnZW9tID0gInBvaW50IiwgCiAgcG9pbnRzaXplID0gMiwKICBhZGRFbGxpcHNlcyA9IFRSVUUKKSArCiAgZ3VpZGVzKAogICAgY29sID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkRvbm9yIiwgb3ZlcnJpZGUuYWVzID0gYWVzKGxhYmVsID0gIiIpKSwKICAgIHNoYXBlID0gIm5vbmUiLCAKICAgIGZpbGwgPSAibm9uZSIKICApICsKICB0aGVtZV9jbGFzc2ljMihiYXNlX3NpemUgPSAxNSkKYGBgCgpgYGB7ciBwY2FfdmFyaWFibGVzLCBmaWcuY2FwPSJQQ0EgdmFyaWFibGUgbG9hZGluZ3MgYnkgdHJhaXQgY2F0ZWdvcnkifQpmdml6X3BjYV92YXIocGNhLCBjb2wudmFyID0gYXMuZmFjdG9yKHZhcl90eXBlKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSB2YXJfcGFsKSArCiAgZ3VpZGVzKAogICAgY29sID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIlR5cGUiLCBvdmVycmlkZS5hZXMgPSBhZXMobGFiZWwgPSAiIikpCiAgKSArCiAgdGhlbWVfY2xhc3NpYzIoYmFzZV9zaXplID0gMTUpCmBgYAoKIyMgVHJhaXQgQ29ycmVsYXRpb25zCgoqKlB1cnBvc2UqKjogRXhhbWluZSBwYWlyd2lzZSBjb3JyZWxhdGlvbnMgYW1vbmcgcGhlbm90eXBpYyB0cmFpdHMuCgoqKkFwcHJvYWNoKio6IENvbXB1dGUgY29ycmVsYXRpb24gbWF0cml4IHVzaW5nIHBhaXJ3aXNlIGNvbXBsZXRlIG9ic2VydmF0aW9ucy4KICAKKipFeHBlY3RlZCBvdXRjb21lKio6IENvcnJlbGF0aW9uIG1hdHJpeCByZXZlYWxpbmcgdHJhaXQgcmVsYXRpb25zaGlwcy4KCmBgYHtyIHRyYWl0X2NvcnJlbGF0aW9uc30KY29yX21hdHJpeCA8LSBjb3IodG9fcGNhWywgLTFdLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKcm91bmQoY29yX21hdHJpeCwgMikKYGBgCgojIyBCcm9hZC1TZW5zZSBIZXJpdGFiaWxpdHkgYnkgVHJhaXQKCioqUHVycG9zZSoqOiBFc3RpbWF0ZSBIwrIgZm9yIGVhY2ggcGhlbm90eXBpYyB0cmFpdC4KCioqQXBwcm9hY2gqKjogRm9yIGVhY2ggdHJhaXQsIHN1YnNldCB0byBnZW5vdHlwZXMgd2l0aCBhbGwgMyByZXBsaWNhdGVzLCBmaXQgb25lLXdheSBBTk9WQSwgcGFydGl0aW9uIHZhcmlhbmNlLgoKKipFeHBlY3RlZCBvdXRjb21lKio6IEhlcml0YWJpbGl0eSBlc3RpbWF0ZXMgZm9yIGFsbCBtZWFzdXJlZCB0cmFpdHMuCgpgYGB7ciBoZXJpdGFiaWxpdHlfY2FsY3VsYXRpb259CmhlciA8LSBkYXRhLmZyYW1lKCkKCmZvciAodHJhaXQgaW4gdmFycykgewogICMgUmVtb3ZlIE5BcyBmb3IgdGhpcyB0cmFpdAogIHdpdGhvdXRfTkEgPC0gQ0xZMjNbIWlzLm5hKENMWTIzWywgdHJhaXRdKSwgXQogIAogICMgS2VlcCBvbmx5IGdlbm90eXBlcyB3aXRoIGFsbCAzIHJlcHMKICB3aXRoX3JlcHMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShsaW5lID0gd2l0aG91dF9OQSRwZWRpZ3JlZSkpICU+JQogICAgZmlsdGVyKEZyZXEgPT0gMykgJT4lCiAgICBwdWxsKGxpbmUpCiAgCiAgdG9fSDIgPC0gd2l0aG91dF9OQSAlPiUKICAgIGZpbHRlcihwZWRpZ3JlZSAlaW4lIHdpdGhfcmVwcykKICAKICAjIEZpdCBBTk9WQSBtb2RlbAogIGZvcm11bGEgPC0gYXMuZm9ybXVsYShwYXN0ZTAodHJhaXQsICIgfiBwZWRpZ3JlZSIpKQogIG1vZCA8LSBsbShkYXRhID0gdG9fSDIsIGZvcm11bGEpCiAgbW9kX2FvdiA8LSBhbm92YShtb2QpCiAgCiAgIyBDYWxjdWxhdGUgSDIKICBoZXIgPC0gcmJpbmQoaGVyLCBkYXRhLmZyYW1lKAogICAgdHJhaXQgPSB0cmFpdCwKICAgIEgyID0gbW9kX2FvdiRgU3VtIFNxYFsxXSAvIHN1bShtb2RfYW92JGBTdW0gU3FgKSwKICAgIG5fZ2VubyA9IGxlbmd0aCh3aXRoX3JlcHMpCiAgKSkKfQoKaGVyJHR5cGUgPC0gdmFyX3R5cGUKCiMgRGlhZ25vc3RpY3MKewpjYXQoIj09PSBIZXJpdGFiaWxpdHkgRXN0aW1hdGVzID09PVxuIikKcHJpbnQoaGVyICU+JSBhcnJhbmdlKGRlc2MoSDIpKSkKfQpgYGAKCmBgYHtyIGhlcml0YWJpbGl0eV9wbG90LCBmaWcuY2FwPSJCcm9hZC1zZW5zZSBoZXJpdGFiaWxpdHkgYnkgdHJhaXQifQpoZXIgJT4lCiAgbXV0YXRlKHRyYWl0ID0gZm9yY2F0czo6ZmN0X3Jlb3JkZXIodHJhaXQsIEgyLCAuZGVzYyA9IFRSVUUpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB0cmFpdCwgeSA9IEgyLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2NvbCgpICsKICB5bGFiKCJCcm9hZC1TZW5zZSBIZXJpdGFiaWxpdHkgKEjCsikiKSArCiAgeGxhYigiVHJhaXQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdmFyX3BhbCkgKwogIHRoZW1lX2NsYXNzaWMyKGJhc2Vfc2l6ZSA9IDE1KSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiCiAgKQpgYGAKCiMjIEltcG9ydCBDTFkyMiBCcmFjZSBSb290IERhdGEKCioqUHVycG9zZSoqOiBJbXBvcnQgcHJldmlvdXMgeWVhcidzIGJyYWNlIHJvb3QgZGF0YSBmb3IgY3Jvc3MteWVhciBjb21wYXJpc29uLgoKKipBcHByb2FjaCoqOiBSZWFkIENMWTIyIGZpZWxkIGJvb2ssIGZpbHRlciB0byBKMlRlbyBwcm9qZWN0LCBwYXJzZSBkb25vciBpZGVudGlmaWVycy4KCioqRXhwZWN0ZWQgb3V0Y29tZSoqOiBDbGVhbiBDTFkyMiBkYXRhZnJhbWUgZm9yIEd4RSBhbmFseXNpcy4KCmBgYHtyIGltcG9ydF9jbHkyMn0KQ0xZMjIgPC0gcmVhZC5jc3YoIkNMWS0yMi1SUkEtRmllbGQtQm9vayAtIEI2RmllbGQuY3N2IikgJT4lCiAgZHBseXI6OmZpbHRlcihQcm9qZWN0ID09ICJKMlRlbyIpICU+JQogIGRwbHlyOjptdXRhdGUoCiAgICBkb25vcl9wb3AgPSBnc3ViKCItLioiLCAiIiwgRGVzY3JpcHRpb24sIHBlcmwgPSBUUlVFKSwKICAgIGRvbm9yX2xpbmUgPSBnc3ViKCJfUC4qPyQiLCAiIiwgRGVzY3JpcHRpb24sIHBlcmwgPSBUUlVFKQogICkKCkNMWTIyJGJyYWNlX3Jvb3QgPC0gYXMubnVtZXJpYyhDTFkyMiRicmFjZV9yb290KQoKIyBDbGVhbiBkb25vcl9saW5lIHN0cmluZ3MKQ0xZMjIkZG9ub3JfbGluZSA8LSBnc3ViKCJeLio/LSIsICIiLCBDTFkyMiRkb25vcl9saW5lLCBwZXJsID0gVFJVRSkKQ0xZMjIkZG9ub3JfbGluZSA8LSBnc3ViKCItKyIsICJfIiwgQ0xZMjIkZG9ub3JfbGluZSwgcGVybCA9IFRSVUUpCkNMWTIyJGRvbm9yX2xpbmUgPC0gZ3N1YigiXFwoLioiLCAiIiwgQ0xZMjIkZG9ub3JfbGluZSwgcGVybCA9IFRSVUUpCkNMWTIyJGRvbm9yX2xpbmUgPC0gZ3N1YigiLSIsICJfIiwgQ0xZMjIkZG9ub3JfbGluZSwgcGVybCA9IFRSVUUpCkNMWTIyJGRvbm9yX2xpbmUgPC0gZ3N1YigiICskIiwgIiIsIENMWTIyJGRvbm9yX2xpbmUsIHBlcmwgPSBUUlVFKQoKIyBEaWFnbm9zdGljcwp7CmNhdCgiQ0xZMjIgSjJUZW8gcmVjb3JkczoiLCBucm93KENMWTIyKSwgIlxuIikKY2F0KCJCcmFjZSByb290IHJhbmdlOiIsIHJhbmdlKENMWTIyJGJyYWNlX3Jvb3QsIG5hLnJtID0gVFJVRSksICJcbiIpCn0KYGBgCgojIyBDb21iaW5lIEJyYWNlIFJvb3QgRGF0YSBBY3Jvc3MgWWVhcnMKCioqUHVycG9zZSoqOiBNZXJnZSBicmFjZSByb290IG1lYXN1cmVtZW50cyBmcm9tIENMWTIyIGFuZCBDTFkyMyBmb3IgR3hFIGFuYWx5c2lzLgoKKipBcHByb2FjaCoqOiBTdGFuZGFyZGl6ZSBjb2x1bW4gbmFtZXMsIHN0YWNrIGRhdGFzZXRzIHdpdGggZmllbGQgeWVhciBpbmRpY2F0b3IuCgoqKkV4cGVjdGVkIG91dGNvbWUqKjogQ29tYmluZWQgZGF0YWZyYW1lIGZvciBjcm9zcy1lbnZpcm9ubWVudCBhbmFseXNpcy4KCmBgYHtyIGNvbWJpbmVfYnJfZGF0YX0KQlIgPC0gcmJpbmQoCiAgQ0xZMjIgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGRvbm9yX2xpbmUsIGRvbm9yX3BvcCwgTkJSID0gYnJhY2Vfcm9vdCkgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGRvbm9yX3BvcCAhPSAiUHVycGxlIENoZWNrIikgJT4lCiAgICBtdXRhdGUoZmllbGQgPSAiQ0xZMjIiKSwKICAKICBDTFkyMyAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoUmVwID09IDMpICU+JQogICAgZHBseXI6OnNlbGVjdChkb25vcl9saW5lLCBkb25vcl9wb3AsIE5CUikgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGRvbm9yX3BvcCAhPSAiUHVycGxlIENoZWNrIikgJT4lCiAgICBtdXRhdGUoZmllbGQgPSAiQ0xZMjMiKQopCgpmaWVsZF9tZWFuIDwtIEJSICU+JQogIGdyb3VwX2J5KGZpZWxkKSAlPiUKICBzdW1tYXJpc2UoYnJfbWVhbiA9IG1lYW4oTkJSLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gImRyb3AiKQoKIyBEaWFnbm9zdGljcwp7CmNhdCgiPT09IENvbWJpbmVkIEJyYWNlIFJvb3QgRGF0YSA9PT1cbiIpCnByaW50KHRhYmxlKEJSJGZpZWxkLCBCUiRkb25vcl9wb3ApKQpjYXQoIlxuRmllbGQgbWVhbnM6XG4iKQpwcmludChmaWVsZF9tZWFuKQp9CmBgYAoKIyMgQnJhY2UgUm9vdCBWYXJpYXRpb24gYnkgUG9wdWxhdGlvbiBhbmQgWWVhcgoKKipQdXJwb3NlKio6IFZpc3VhbGl6ZSBicmFjZSByb290IG51bWJlciB2YXJpYXRpb24gYWNyb3NzIGRvbm9yIHBvcHVsYXRpb25zIGFuZCBmaWVsZCB5ZWFycy4KCioqQXBwcm9hY2gqKjogRmFjZXRlZCBiZWVzd2FybSBwbG90cyB3aXRoIHBvcHVsYXRpb24gbWVhbnMgYW5kIGZpZWxkLWxldmVsIHJlZmVyZW5jZSBsaW5lcy4KCioqRXhwZWN0ZWQgb3V0Y29tZSoqOiBWaXN1YWwgY29tcGFyaXNvbiBvZiBOQlIgZGlzdHJpYnV0aW9ucyBhY3Jvc3MgcG9wdWxhdGlvbnMgYW5kIGVudmlyb25tZW50cy4KCmBgYHtyIGJyX3BvcHVsYXRpb25fcGxvdCwgZmlnLmNhcD0iQnJhY2Ugcm9vdCBudW1iZXIgYnkgZG9ub3IgcG9wdWxhdGlvbiBhbmQgZmllbGQgeWVhciIsIGZpZy5oZWlnaHQ9OX0KQlIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZG9ub3JfcG9wLCB5ID0gTkJSLCBncm91cCA9IGRvbm9yX3BvcCwgY29sID0gZG9ub3JfcG9wKSkgKwogIGdlb21fcXVhc2lyYW5kb20oKSArCiAgZ2VvbV9obGluZSgKICAgIGRhdGEgPSBmaWVsZF9tZWFuLAogICAgYWVzKHlpbnRlcmNlcHQgPSBicl9tZWFuKSwKICAgIGNvbG91ciA9ICJibGFjayIsIGxpbmV0eXBlID0gMgogICkgKwogIHN0YXRfc3VtbWFyeSgKICAgIGZ1bi5kYXRhID0gbWVhbl9jbF9ub3JtYWwsCiAgICBnZW9tID0gInBvaW50cmFuZ2UiLAogICAgY29sID0gImJsYWNrIgogICkgKwogIGZhY2V0X3dyYXAofmZpZWxkLCBuY29sID0gMSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSAwOjYpICsKICB4bGFiKCJEb25vciBQb3B1bGF0aW9uIikgKwogIHlsYWIoIk51bWJlciBvZiBCcmFjZSBSb290cyIpICsKICB0aGVtZV9jbGFzc2ljMihiYXNlX3NpemUgPSAxNSkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAid2hpdGUiLCBmaWxsID0gIndoaXRlIiksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKQogICkKYGBgCgojIyBCcmFjZSBSb290IEhlcml0YWJpbGl0eSBieSBZZWFyCgoqKlB1cnBvc2UqKjogRXN0aW1hdGUgaGVyaXRhYmlsaXR5IG9mIGJyYWNlIHJvb3QgbnVtYmVyIHdpdGhpbiBlYWNoIGZpZWxkIHllYXIuCgoqKkFwcHJvYWNoKio6IEZpdCBvbmUtd2F5IEFOT1ZBIChOQlIgfiBkb25vcl9saW5lKSBzZXBhcmF0ZWx5IGZvciBlYWNoIHllYXIuCgoqKkV4cGVjdGVkIG91dGNvbWUqKjogWWVhci1zcGVjaWZpYyBIwrIgZXN0aW1hdGVzIGZvciBicmFjZSByb290IG51bWJlci4KCmBgYHtyIGJyX2hlcml0YWJpbGl0eV9ieV95ZWFyfQojIENMWTIyIGhlcml0YWJpbGl0eQpicjIyX2FvdiA8LSBsbShmb3JtdWxhID0gTkJSIH4gZG9ub3JfbGluZSwgZGF0YSA9IEJSW0JSJGZpZWxkID09ICJDTFkyMiIsIF0pCnMyMiA8LSBzdW1tYXJ5KGJyMjJfYW92KQoKIyBDTFkyMyBoZXJpdGFiaWxpdHkKYnIyM19hb3YgPC0gbG0oZm9ybXVsYSA9IE5CUiB+IGRvbm9yX2xpbmUsIGRhdGEgPSBCUltCUiRmaWVsZCA9PSAiQ0xZMjMiLCBdKQpzMjMgPC0gc3VtbWFyeShicjIzX2FvdikKCiMgRGlhZ25vc3RpY3MKewpjYXQoIj09PSBCcmFjZSBSb290IEjCsiBieSBZZWFyID09PVxuIikKY2F0KCJDTFkyMiBIwrIgKFLCsik6Iiwgcm91bmQoczIyJHIuc3F1YXJlZCwgMyksICJcbiIpCmNhdCgiQ0xZMjMgSMKyIChSwrIpOiIsIHJvdW5kKHMyMyRyLnNxdWFyZWQsIDMpLCAiXG4iKQp9CmBgYAoKIyMgR2Vub3R5cGUgw5cgRW52aXJvbm1lbnQgQW5hbHlzaXMKCioqUHVycG9zZSoqOiBQYXJ0aXRpb24gdmFyaWFuY2UgaW50byBnZW5vdHlwZSwgZW52aXJvbm1lbnQsIGFuZCBHeEUgY29tcG9uZW50cyBmb3IgYnJhY2Ugcm9vdC4KCioqQXBwcm9hY2gqKjogRml0IHR3by13YXkgQU5PVkEgd2l0aCBpbnRlcmFjdGlvbiAoTkJSIH4gZG9ub3JfbGluZSAqIGZpZWxkKSwgZXh0cmFjdCB2YXJpYW5jZSBjb21wb25lbnRzLgoKKipFeHBlY3RlZCBvdXRjb21lKio6IFByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGdlbm90eXBlLCBmaWVsZCB5ZWFyLCBhbmQgdGhlaXIgaW50ZXJhY3Rpb24uCgpgYGB7ciBneGVfYW5hbHlzaXN9CiMgRnVsbCBHeEUgbW9kZWwKYnJfZ3hlX21vZCA8LSBsbShmb3JtdWxhID0gTkJSIH4gZG9ub3JfbGluZSAqIGZpZWxkLCBkYXRhID0gQlIpCmd4ZV9hb3YgPC0gYW5vdmEoYnJfZ3hlX21vZCkKCiMgVmFyaWFuY2UgY29tcG9uZW50cwp2YXJfY29tcG9uZW50cyA8LSBkYXRhLmZyYW1lKAogIHNvdXJjZSA9IGMoIkdlbm90eXBlIiwgIkZpZWxkIiwgIkfDl0UiLCAiUmVzaWR1YWwiKSwKICBzdW1fc3EgPSBneGVfYW92JGBTdW0gU3FgLAogIHByb3BfdmFyID0gZ3hlX2FvdiRgU3VtIFNxYCAvIHN1bShneGVfYW92JGBTdW0gU3FgKQopCgojIFN1bW1hcnkgdGFibGUKZ3hlX3N1bW1hcnkgPC0gZGF0YS5mcmFtZSgKICBmaWVsZCA9IGMoIkNMWTIyIiwgIkNMWTIzIiwgIkNvbWJpbmVkIiksCiAgSDJfZmFtID0gYygKICAgIHMyMiRyLnNxdWFyZWQsIAogICAgczIzJHIuc3F1YXJlZCwgCiAgICBneGVfYW92JGBTdW0gU3FgWzFdIC8gc3VtKGd4ZV9hb3YkYFN1bSBTcWApCiAgKSwKICBmaWVsZF95ZWFyID0gYyhOQSwgTkEsIGd4ZV9hb3YkYFN1bSBTcWBbMl0gLyBzdW0oZ3hlX2FvdiRgU3VtIFNxYCkpLAogIEd4RlkgPSBjKE5BLCBOQSwgZ3hlX2FvdiRgU3VtIFNxYFszXSAvIHN1bShneGVfYW92JGBTdW0gU3FgKSkKKQoKIyBEaWFnbm9zdGljcwp7CmNhdCgiPT09IFZhcmlhbmNlIENvbXBvbmVudHMgPT09XG4iKQpwcmludCh2YXJfY29tcG9uZW50cykKY2F0KCJcbj09PSBHeEUgU3VtbWFyeSA9PT1cbiIpCnByaW50KGd4ZV9zdW1tYXJ5KQp9CmBgYAoKIyMgUG9wdWxhdGlvbi1MZXZlbCBHeEUgQW5hbHlzaXMKCioqUHVycG9zZSoqOiBFc3RpbWF0ZSBoZXJpdGFiaWxpdHkgYW5kIEd4RSB3aXRoaW4gZWFjaCBkb25vciBwb3B1bGF0aW9uLgoKKipBcHByb2FjaCoqOiBTcGxpdCBkYXRhIGJ5IHBvcHVsYXRpb24sIGZpdCBHeEUgbW9kZWxzIHdoZXJlIGJvdGggeWVhcnMgYXJlIHJlcHJlc2VudGVkLgoKKipFeHBlY3RlZCBvdXRjb21lKio6IFBvcHVsYXRpb24tc3BlY2lmaWMgdmFyaWFuY2UgcGFydGl0aW9uaW5nLgoKYGBge3IgcG9wdWxhdGlvbl9neGV9CkJSJGZpZWxkX2Rvbm9yIDwtIGludGVyYWN0aW9uKGZhY3RvcihCUiRmaWVsZCksIGZhY3RvcihCUiRkb25vcl9wb3ApKQoKIyBEaWFnbm9zdGljOiBzaG93IGRvbm9yX2xpbmUgY291bnRzIHBlciBmaWVsZCDDlyBkb25vcl9wb3AgY29tYmluYXRpb24KbGluZV9jb3VudHMgPC0gQlIgJT4lCiAgZ3JvdXBfYnkoZmllbGQsIGRvbm9yX3BvcCkgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9vYnMgPSBuKCksCiAgICBuX2xpbmVzID0gbl9kaXN0aW5jdChkb25vcl9saW5lKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIGFycmFuZ2Uobl9saW5lcykKCiMgRGlhZ25vc3RpY3MKewpjYXQoIj09PSBEb25vciBMaW5lcyBwZXIgRmllbGQgw5cgUG9wdWxhdGlvbiA9PT1cbiIpCmNhdCgiKENvbWJpbmF0aW9ucyB3aXRoIG5fbGluZXMgPCAyIHdpbGwgYmUgc2tpcHBlZClcblxuIikKcHJpbnQoYXMuZGF0YS5mcmFtZShsaW5lX2NvdW50cykpCmNhdCgiXG5Ta2lwcGVkIGNvbWJpbmF0aW9uczoiLCBzdW0obGluZV9jb3VudHMkbl9saW5lcyA8IDIpLCAiXG4iKQp9CgojIEgyIGJ5IHBvcHVsYXRpb24gd2l0aGluIGVhY2ggZmllbGQKYnJfbGlzdF9maWVsZCA8LSBzcGxpdChCUiwgZiA9IEJSJGZpZWxkX2Rvbm9yKQoKSDJfcG9wIDwtIGxhcHBseShuYW1lcyhicl9saXN0X2ZpZWxkKSwgRlVOID0gZnVuY3Rpb24oeCkgewogIGRmIDwtIGJyX2xpc3RfZmllbGRbW3hdXQogIG5fbGluZXMgPC0gbGVuZ3RoKHVuaXF1ZShkZiRkb25vcl9saW5lKSkKICBpZiAobnJvdyhkZikgPCAzIHx8IG5fbGluZXMgPCAyKSByZXR1cm4oTlVMTCkKICAKICBkZiRkb25vcl9saW5lIDwtIGZhY3RvcihkZiRkb25vcl9saW5lKQogIGxtX2FvdiA8LSBsbShkYXRhID0gZGYsIE5CUiB+IGRvbm9yX2xpbmUpICU+JSBhbm92YSgpCiAgCiAgZGF0YS5mcmFtZSgKICAgIGZpZWxkID0gZGYkZmllbGRbMV0sCiAgICBkb25vcl9wb3AgPSBkZiRkb25vcl9wb3BbMV0sCiAgICBIMl9mYW0gPSBsbV9hb3YkYFN1bSBTcWBbMV0gLyBzdW0obG1fYW92JGBTdW0gU3FgKQogICkKfSkgJT4lIAogIGRwbHlyOjpiaW5kX3Jvd3MoKQoKIyBHeEUgYnkgcG9wdWxhdGlvbiAoYWNyb3NzIGZpZWxkcykKYnJfbGlzdF9wb3AgPC0gc3BsaXQoQlIsIGYgPSBCUiRkb25vcl9wb3ApCgojIERpYWdub3N0aWM6IHNob3cgd2hpY2ggcG9wdWxhdGlvbnMgaGF2ZSBkYXRhIGluIGJvdGggZmllbGRzCnBvcF9maWVsZF9zdW1tYXJ5IDwtIEJSICU+JQogIGdyb3VwX2J5KGRvbm9yX3BvcCkgJT4lCiAgc3VtbWFyaXNlKAogICAgbl9maWVsZHMgPSBuX2Rpc3RpbmN0KGZpZWxkKSwKICAgIG5fbGluZXMgPSBuX2Rpc3RpbmN0KGRvbm9yX2xpbmUpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCiMgRGlhZ25vc3RpY3MKewpjYXQoIlxuPT09IFBvcHVsYXRpb24gU3VtbWFyeSBmb3IgR3hFIEFuYWx5c2lzID09PVxuIikKY2F0KCIoUG9wdWxhdGlvbnMgd2l0aCBuX2ZpZWxkcyA8IDIgb3Igbl9saW5lcyA8IDIgd2lsbCBiZSBza2lwcGVkKVxuXG4iKQpwcmludChhcy5kYXRhLmZyYW1lKHBvcF9maWVsZF9zdW1tYXJ5KSkKfQoKSDJfcG9wX2d4ZSA8LSBsYXBwbHkobmFtZXMoYnJfbGlzdF9wb3ApLCBGVU4gPSBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYnJfbGlzdF9wb3BbW3hdXQogIGZpZWxkX2NvdW50IDwtIHRhYmxlKGRmJGRvbm9yX2xpbmUsIGRmJGZpZWxkKSAlPiUgYXMuZGF0YS5mcmFtZSgpCiAgbl9maWVsZCA8LSBsZW5ndGgodW5pcXVlKGZpZWxkX2NvdW50WywgMl0pKQogIG5fbGluZXMgPC0gbGVuZ3RoKHVuaXF1ZShkZiRkb25vcl9saW5lKSkKICAKICBpZiAobl9maWVsZCA8IDIgfHwgbl9saW5lcyA8IDIpIHJldHVybihOVUxMKQogIAogIGRmJGRvbm9yX2xpbmUgPC0gZmFjdG9yKGRmJGRvbm9yX2xpbmUpCiAgbG1fYW92IDwtIGxtKGRhdGEgPSBkZiwgTkJSIH4gZG9ub3JfbGluZSAqIGZpZWxkKSAlPiUgYW5vdmEoKQogIAogIGRhdGEuZnJhbWUoCiAgICBkb25vcl9wb3AgPSBkZiRkb25vcl9wb3BbMV0sCiAgICBIMl9mYW0gPSBsbV9hb3YkYFN1bSBTcWBbMV0gLyBzdW0obG1fYW92JGBTdW0gU3FgKSwKICAgIEd4RiA9IGxtX2FvdiRgU3VtIFNxYFszXSAvIHN1bShsbV9hb3YkYFN1bSBTcWApLAogICAgZmllbGQgPSBsbV9hb3YkYFN1bSBTcWBbMl0gLyBzdW0obG1fYW92JGBTdW0gU3FgKSwKICAgIHJlc2lkdWFscyA9IGxtX2FvdiRgU3VtIFNxYFs0XSAvIHN1bShsbV9hb3YkYFN1bSBTcWApCiAgKQp9KSAlPiUgCiAgZHBseXI6OmJpbmRfcm93cygpCgojIERpYWdub3N0aWNzCnsKY2F0KCI9PT0gSMKyIGJ5IFBvcHVsYXRpb24gYW5kIEZpZWxkID09PVxuIikKcHJpbnQoSDJfcG9wKQpjYXQoIlxuPT09IFBvcHVsYXRpb24tTGV2ZWwgR3hFID09PVxuIikKcHJpbnQoSDJfcG9wX2d4ZSkKfQpgYGAKCiMjIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbl9pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=