require(plotly)
require(tidyverse)
require(ggridges)
require(cowplot)
require(RColorBrewer)
old <- theme_set(theme_bw(base_size = 16))

Goal

  • Analyze the full chimera set flow results for PHO5pr-mCherry reporter.
  • Develop an analysis pipeline to perform QC, correction (if needed) and plotting the results.

Data

Import the data

files <- list.files(path = ".", pattern = "*-gated-median-out.txt")
names(files) <- gsub("-gated-median-out.txt", "", files)
raw <- map_dfr(files, ~read_tsv(.x, col_types = cols()), .id = "date") %>% 
  mutate(
    flag = ifelse(is.na(flag), # if no flag was recorded in the original gated median data 
                  ifelse(n_induction < 1000, "fail", "pass"), # fail if fewer than 1000 events
                  flag), # if the original data (one) has flag, use it
    date = format(ymd(date), "%m/%d")
  ) %>% 
  filter(!grepl("bead", sample)) %>% 
  separate(col = sample, sep = "-", into = c("plasmid", "host", "rep"))

Sample information

sample <- read_tsv("../20230208-chimera-Pho4-makeup.txt", col_types = "ccccc")

QC

1. Number of events per sample

Blank wells are indeed blank

raw %>% filter(host == "blank") %>% 
  group_by(date) %>%
  ggplot(aes(x = date, y = n_cells)) + geom_point(aes(shape = well)) +
  stat_summary(fun.data = "mean_se", geom = "pointrange", color = "red", 
               position = position_nudge(x = 0.1)) +
  theme_bw(base_size = 16) + theme(axis.text.x = element_text(size = rel(0.8), angle = 90))

there are some cell like events in the blank wells, especially in one well on 02/21 and another on 3/31. 02/08 and 02/10 also have more events than other days. checking the pre-processing R notebook outputs, I think there may be some carry over between wells. No wide spread contamination though.

Below, we will remove the blank wells from further consideration

tmp <- filter(raw, host != "blank")
tmp %>% 
  ggplot(aes(x = n_induction)) + 
  #geom_histogram(bins = 30, fill = "forestgreen") + facet_grid(host~date) + 
  geom_density_ridges(aes(y = date)) +
  facet_wrap(~host) + theme_minimal(base_size = 14) #+ panel_border()

Majority of the experimental wells (not blank) have > 5000 events, with the exception of a few samples

tmp %>% filter(n_induction < 1000)

for now, I plan to let any sample with > 1000 events pass. This would exclude 20 samples that have extremely low counts, likely indicating issues. later I plan to revisit the remaining low count samples, e.g., n_induction between 1000 and 3000.

Remove low event count and a few experiments with “fail” flag.

dat <- filter(raw, host != "blank", flag == "pass")

2. Background subtraction

Check the background fluorescence levels across different days.

dat %>% filter(host == "156") %>% 
  pivot_longer(BL1.H:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  mutate(parameter = ordered(parameter, levels = c("BL1.H", "YL2.H", "nGFP", "nRFP"))) %>% 
  ggplot(aes(x = date, y = intensity)) + geom_point(aes(shape = well)) +
  stat_summary(fun.data = "mean_se", geom = "pointrange", color = "red", 
               position = position_nudge(x = 0.1)) +
  facet_wrap(~parameter) +
  theme_bw(base_size = 16) + theme(axis.text.x = element_text(size = rel(0.8), angle = 90))

02/18 and 02/21 YFP has relatively high background. I checked the original gated data and found that indeed those samples have a higher RFP. It is not, however, a systematic shift up, as the other samples don’t appear to have higher RFP levels during those runs (days). similarly, 02/10 showed higher BL1.H background, and there is no indication that a systematic shift explains the higher background (which can be caused by a voltage difference).

tmp <- dat %>% filter(host == "156")
lm(BL1.H ~ date, data = tmp) %>% anova()
Analysis of Variance Table

Response: BL1.H
          Df Sum Sq Mean Sq F value    Pr(>F)    
date      12 375979 31331.6  11.525 1.512e-07 ***
Residuals 26  70680  2718.5                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

ANOVA above does reveal significant variation in GFP background fluorescence between yH156 across days.

Is there any relationship between the background BL1.H level from yH156 and mNeon levels from the experimental strains?

test.strain = "194"
tmp <- dat %>% 
  filter(grepl(paste0("NA-156|", test.strain, "-373"), name)) %>% 
  group_by(date, plasmid) %>% 
  summarize(meanGFP = mean(BL1.H), .groups = "drop") %>% 
  pivot_wider(id_cols = date, names_from = plasmid, names_prefix = "p", values_from = meanGFP)

lm(p194 ~ pNA, data = tmp) %>% summary()

Call:
lm(formula = p194 ~ pNA, data = tmp)

Residuals:
    Min      1Q  Median      3Q     Max 
-461.98  -75.55   61.58  162.22  210.34 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) 2250.0374   470.4313   4.783 0.000569 ***
pNA           -0.2333     0.5651  -0.413 0.687636    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 200 on 11 degrees of freedom
Multiple R-squared:  0.01526,   Adjusted R-squared:  -0.07426 
F-statistic: 0.1705 on 1 and 11 DF,  p-value: 0.6876
p <- ggplot(tmp, aes(x = pNA, y = p194)) + 
  geom_point(size = 2) + 
  geom_text(data = filter(tmp, p194 < 1800), aes(label = date), nudge_x = -20) +
  stat_smooth(method = "lm", formula = y~x) +
  xlab("background BL1.H (yH156)") + ylab(paste0("Pho4-mNeon (pH", test.strain, ")")) +
  theme_minimal(base_size = 16)

p

No strong correlation between the background and the mNeon levels

Given the results above, I propose to do the following:

I assume the variation in the non-fluorescent strain's reading is due to idiosynchractic behavior
of that strain and not characteristic of the days. Therefore, I plan to avearage the background
fluorescence to get a more accurate estimate, which implicitly assumes that variation in the
background is minimal between days.

Subtract the background

bg <- dat %>% 
  filter(host == "156") %>% 
  group_by(date) %>% 
  # average based on dates
  summarize(across(BL1.H:nRFP, ~ round(mean(.x),1)), .groups = "drop") %>% 
  # median of day averages
  summarize(across(BL1.H:nRFP, ~median(.))) %>% 
  unlist() # turn into a named vector storing background readings
  #column_to_rownames(var = "date")

bg.rm <- dat %>% 
  select(date:host, events = n_induction, FSC.H:nRFP, flag) %>%
  mutate(
    BL1.H = BL1.H - bg["BL1.H"],
    YL2.H = YL2.H - bg["YL2.H"],
    nGFP = nGFP - bg["nGFP"],
    nRFP = nRFP - bg["nRFP"],
  )

Double check that the subtraction worked correctly

bg.rm %>% filter(host == "156") %>% 
  pivot_longer(BL1.H:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  mutate(parameter = ordered(parameter, levels = c("BL1.H", "YL2.H", "nGFP", "nRFP"))) %>% 
  ggplot(aes(x = date, y = intensity)) + geom_point(aes(shape = well)) +
  stat_summary(fun.data = "mean_se", geom = "pointrange", color = "red", position = position_nudge(x = 0.1)) +
  facet_wrap(~parameter, scale = "free_y") +
  theme_bw(base_size = 14) + theme(axis.text.x = element_text(angle = 90))

Determine if any sample had very low mNeon levels despite having a Pho4 chimera plasmid. I observed such samples when doing the pre-processing, and would like to identify and remove them now.

mycolors = c(brewer.pal(name="Paired", n = 8), brewer.pal(name="Dark2", n = 6))

no.exn.th <- 250
ggplot(bg.rm, aes(x = plasmid, y = abs(BL1.H))) +
  geom_hline(yintercept = no.exn.th, color = "grey50", linetype = 2, size = 0.2) +
  geom_point(aes(color = date)) + 
  scale_y_log10(breaks = c(100, 1000, 10000)) + ylab("BL1.H") +
  scale_color_manual(values = mycolors) + theme_cowplot() + 
  theme(axis.text.x = element_text(angle = 90, size = rel(0.7), vjust = 0.5),
        legend.position = "top")
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

# assign "fail" to the non-expressing ones
bg.rm[bg.rm$plasmid != "NA" & bg.rm$BL1.H < no.exn.th, "flag"] = "fail"
filter(bg.rm, flag == "fail")

the above samples will be exported for archiving, but won’t be used in subsequent analyses.

Lastly, pH231 assayed on 2/18 and 2/19 exhibited abnormal behaviors. pH231 on 2/18, 2/19 The existing one replicate from each day is from biological replicate 2, in which the majority of the cells were not inducing. Lindsey repeated this strain on 3/30 and 3/31, and obtained properly behaving data. Therefore, we will remove the single replicate for pH231 from 2/18 and 2/19

bg.rm[bg.rm$plasmid == "231" & bg.rm$host == "555" & bg.rm$date %in% c("02/18", "02/19"), "flag"] = "fail"
filter(bg.rm, flag == "fail")

Export the background-subtracted values for later use with the shinyapp

# remove the yH156 samples
dat.export <- bg.rm %>% 
  filter(host != "156") %>% 
  mutate(
    host = ordered(host, levels = c("555", "373", "529"), labels = c("PHO2", "pho2∆", "PHO84"))
  )
write_tsv(dat.export, "../20231023-PHO5-bg-subtracted-data.tsv")

3. Consistency across plates.

Check the background-subtracted fluorescence values for the host strains (yH373 and yH555) as well as the positive control strain (pH188 in yH373 or yH555 backgrounds), both of which are present on each plate.

dat.export %>% 
  filter(host != "PHO84", flag == "pass", plasmid %in% c("188", "194", "NA")) %>% 
  pivot_longer(BL1.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  #mutate(parameter = ordered(parameter, levels = c("BL1.H", "YL2.H", "nGFP", "nRFP"))) %>% 
  ggplot(aes(x = plasmid, y = intensity, group = date)) + 
  geom_point(aes(color = date), position = position_dodge(0.7)) + 
  scale_color_manual(values = mycolors) +
  facet_grid(parameter~host, scale = "free_y") +
  theme_bw(base_size = 14)# +

  #theme(axis.text.x = element_text(face = 3))
  1. The host strain fluorescence levels appear to be consistent across the days. Additionally, their GFP levels are zero, as expected.
  2. The positive control strain has strong BL1.H and correspondingly has strong YL2.H.
  3. 02/10 batch has strange BL1 values. Since we have three identical sets (02/10, 11, 16), we can ignore this batch in the downstream analysis
dat.wide.fil <- filter(dat.export, host != "PHO84", flag == "pass", date != "02/10")

Number of replicates left for each sample

expt <- dat.wide.fil %>% 
  filter(host %in% c("PHO2", "pho2∆"), !plasmid %in% c("188", "194")) %>% 
  group_by(date, plasmid, host) %>% 
  summarize(n = n(), .groups = "drop")

expt %>% 
  ggplot(aes(x = plasmid, y = n)) +
  geom_col(aes(fill = host)) + 
  facet_grid(date ~ .) +
  scale_fill_manual(values = c("PHO2" = "gray30", "pho2∆" = "gray70")) +
  theme_minimal() + background_grid(major = "none") + panel_border(size = 0.5) +
  scale_y_continuous(name = "Replicates", breaks = c(0, 3, 6)) + xlab(NULL) +
  theme(axis.text.x = element_text(angle = 90),
        legend.position = "top")

How many plasmid-host combination have < 3 replicates after removing samples with low event counts?

dat.wide.fil %>%
  count(plasmid, host) %>% 
  filter(n < 3)

4. Well position

Rationale

  • In my original plate design, I placed a positive control strain (envisioned CgPho4-mNeon in PHO2 background for example) in the control columns (1, 5, 9) on every other row (A, C, E, G). The reason for this is to use it to identify any well position effect on the fluorescence readings.
  • In Emily’s implementation, she instead put a pair of strains, namely pH188-yH323 and pH188-yH555 in these wells. This means I cannot use the positive control wells exactly the way as I designed them. But I can still use them to spot any trend.
tmp <- dat.wide.fil %>% 
  filter(plasmid == "194", host == "PHO2") %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  pivot_longer(BL1.H:YL2.H, names_to = "parameter", values_to = "intensity")

p0 <- list(
  geom_point(aes(color = date), size = 1), 
  #geom_line(aes(color = date), size = 0.4),
  stat_summary(aes(color = date), geom = "line", fun = mean, linewidth = 0.5),
  scale_color_manual(values = mycolors),
  #scale_color_brewer(type = "qual", palette = 2),
  facet_wrap(~parameter, scales = "free_y"),
  xlab(NULL),
  guides(color = guide_legend(title.position = "left", nrow = 2, byrow = TRUE)),
  theme_bw(base_size = 14),
  theme(legend.text = element_text(size = rel(0.8)))
)

p1 <- ggplot(tmp, aes(x = row, y = intensity, group = date)) + p0 +
  labs(subtitle = "Variation over rows")

p2 <- ggplot(tmp, aes(x = col, y = intensity, group = date)) + p0 +
  labs(subtitle = "Variation over columns")

p.legend <- get_legend(p1)

plot_grid(p1 + guides(color = "none"), p2 + guides(color = "none"), p.legend,
          nrow = 3, rel_heights = c(1,1,0.3))

  • no obivous trend between the rows; there seems to be downward trend for BL1 going from column 1 to 9
  • a clear correlation between YL2.H and BL1.H. at least part of this is due to the cell size differences – see size normalized data below:
  • What we care about is the ratio between Pho4-mNeon and PHO5pr-mCherry
tmp <- dat.wide.fil %>% 
  filter(plasmid == "194", host == "PHO2") %>% 
  separate(well, into = c("row", "col"), sep = 1)

tmp %>% 
  ggplot(aes(x = BL1.H, y = YL2.H)) + 
  stat_smooth(aes(color = date, group = date), 
              method = "lm", formula = y ~ 0 + x, 
              se = FALSE, size = 0.5) +
  geom_point(aes(color = date, shape = col), size = 1.5) + 
  #scale_color_manual(values = mycolors) +
  scale_shape_manual(values = 17:19) +
  facet_wrap(~ date) +
  theme_minimal(base_size = 16) + 
  theme(axis.text = element_blank(),
        legend.position = "right",
        legend.box = "horizontal")

There is variation in BL1.H and correspondingly in YL2.H, but the ratio between the two are very consistent 02/20 data is a bit of an outlier

Let’s check both ScPho4 (pH194) and CgPho4 (pH188) to see if their behaviors are consistent across the days.

dat.wide.fil %>% 
  filter(plasmid %in% c("188", "194"), host != "PHO84") %>% 
  mutate(`YL2.H/BL1.H` = YL2.H/BL1.H,
         Pho4 = factor(plasmid, levels = c("194", "188"), 
                       labels = c("ScPho4", "CgPho4"))) %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  ggplot(aes(x = date, y = `YL2.H/BL1.H`)) + 
  geom_bar(aes(fill = host), stat = "summary", fun = "mean", 
           position = position_dodge(0.9)) +
  geom_point(aes(group = host), size = 1, shape = 3,
             position = position_dodge(width = 0.9)) + 
  scale_fill_manual(values = c("PHO2" = "gray60", "pho2∆" = "orange")) +
  #stat_summary(fun.data = "mean_se", geom = "pointrange", color = "red") +
  facet_grid(Pho4 ~ .) +
  theme_bw(base_size = 16) + background_grid(major = "none") +
  theme(axis.text.x = element_text(size = rel(.8), angle = 45, vjust = 0.5))

2/20 data is more variable (also see the figure above the one immediately above)

5. High variance samples

Summarize the background subtracted data by calculating the means and cv for each strain.

cv <- dat.wide.fil %>% 
  select(-nGFP, -nRFP) %>%
  pivot_longer(FSC.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  group_by(date, plasmid, host, parameter) %>% 
  summarize(
    n = n(),
    mean = mean(intensity),
    cv = sd(intensity)/mean(intensity),
    .groups = "drop"
  ) %>% 
  arrange(desc(cv))
cv %>% 
  filter(plasmid != "NA", parameter != "FSC.H") %>% 
  ggplot(aes(x = cv)) + geom_histogram(aes(y = after_stat(density)/8), bins = 30) + 
  stat_ecdf() + 
  geom_hline(yintercept = 0.8, linetype = 2) +
  geom_vline(xintercept = 0.2, linetype = 3, color = "red3") +
  scale_y_continuous(breaks = seq(0, 1, by = 0.2)) +
  ylab("cumulative density") + 
  facet_wrap(~parameter) +
  theme_cowplot()

The histogram’s y-axis is not shown. The line graph represents the empirical CDF, and the dotted horizontal line is at 80%. GFP is more variable than RFP, likely because the absolute values of the former is lower. For both, ~80% of the samples have a CV < 20%.

Do the same for the cell size-normalized values

cv.n <- dat.wide.fil %>% 
  select(-BL1.H, -YL2.H) %>% 
  pivot_longer(nGFP:nRFP, names_to = "parameter", values_to = "intensity") %>% 
  group_by(date, plasmid, host, parameter) %>% 
  summarize(
    n = n(),
    mean = num(mean(intensity), digits = 0),
    cv = num(sd(intensity)/mean(intensity), digits = 2),
    .groups = "drop"
  ) %>% 
  arrange(desc(cv))
cv.n %>% 
  filter(plasmid != "NA", parameter != "FSC.H") %>% 
  ggplot(aes(x = cv)) + geom_histogram(aes(y = after_stat(density)/10), bins = 30) + 
  stat_ecdf() + 
  geom_hline(yintercept = 0.8, linetype = 2) +
  geom_vline(xintercept = 0.2, linetype = 3, color = "red3") +
  scale_y_continuous(breaks = seq(0, 1, by = 0.2)) +
  ylab("cumulative density") + 
  facet_wrap(~parameter) +
  theme_cowplot()

CV is about the same for the size normalized data.

Identify the high variance samples

high.var.list <- cv %>% 
  mutate(CV = round(abs(cv), 3)) %>% 
  pivot_wider(id_cols = c(date, plasmid, host), 
              names_from = parameter, names_prefix = "CV_",
              values_from = CV) %>%
  filter(CV_BL1.H > 0.2 | CV_YL2.H > 0.2) %>% 
  arrange(date, plasmid, host)
dat.high.var <- left_join(high.var.list, dat.wide.fil) %>% select(-flag) %>% arrange(date, plasmid) 
Joining with `by = join_by(date, plasmid, host)`
write_tsv(dat.high.var, "20231027-high-var-samples.tsv")

dat.high.var %>% filter((CV_BL1.H > 1 & plasmid != "NA") | CV_YL2.H > 1)

plasmid 233 replicates 1 & 2 repeatedly showed no RFP induction, while replicate 3 did show induction.

LS0tCnRpdGxlOiAiRTAxMyBQaG80IGNoaW1lcmEgYWN0aXZpdHkgYW5hbHlzaXMgdXNpbmcgUEhPNSByZXBvcnRlciwgcG9vbCBhbmQgUUMiCmF1dGhvcjogIkJpbiBIZSIKZGF0ZTogIjIwMjMtMTAtMTkgdXBkYXRlZCBgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKYGBge3IgbWVzc2FnZT1GQUxTRX0KcmVxdWlyZShwbG90bHkpCnJlcXVpcmUodGlkeXZlcnNlKQpyZXF1aXJlKGdncmlkZ2VzKQpyZXF1aXJlKGNvd3Bsb3QpCnJlcXVpcmUoUkNvbG9yQnJld2VyKQpgYGAKCmBgYHtyfQpvbGQgPC0gdGhlbWVfc2V0KHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSkKYGBgCgoKIyBHb2FsCi0gQW5hbHl6ZSB0aGUgZnVsbCBjaGltZXJhIHNldCBmbG93IHJlc3VsdHMgZm9yIF9QSE81cHJfLW1DaGVycnkgcmVwb3J0ZXIuCi0gRGV2ZWxvcCBhbiBhbmFseXNpcyBwaXBlbGluZSB0byBwZXJmb3JtIFFDLCBjb3JyZWN0aW9uIChpZiBuZWVkZWQpIGFuZCBwbG90dGluZyB0aGUgcmVzdWx0cy4KCiMgRGF0YQpJbXBvcnQgdGhlIGRhdGEKYGBge3J9CmZpbGVzIDwtIGxpc3QuZmlsZXMocGF0aCA9ICIuIiwgcGF0dGVybiA9ICIqLWdhdGVkLW1lZGlhbi1vdXQudHh0IikKbmFtZXMoZmlsZXMpIDwtIGdzdWIoIi1nYXRlZC1tZWRpYW4tb3V0LnR4dCIsICIiLCBmaWxlcykKcmF3IDwtIG1hcF9kZnIoZmlsZXMsIH5yZWFkX3RzdigueCwgY29sX3R5cGVzID0gY29scygpKSwgLmlkID0gImRhdGUiKSAlPiUgCiAgbXV0YXRlKAogICAgZmxhZyA9IGlmZWxzZShpcy5uYShmbGFnKSwgIyBpZiBubyBmbGFnIHdhcyByZWNvcmRlZCBpbiB0aGUgb3JpZ2luYWwgZ2F0ZWQgbWVkaWFuIGRhdGEgCiAgICAgICAgICAgICAgICAgIGlmZWxzZShuX2luZHVjdGlvbiA8IDEwMDAsICJmYWlsIiwgInBhc3MiKSwgIyBmYWlsIGlmIGZld2VyIHRoYW4gMTAwMCBldmVudHMKICAgICAgICAgICAgICAgICAgZmxhZyksICMgaWYgdGhlIG9yaWdpbmFsIGRhdGEgKG9uZSkgaGFzIGZsYWcsIHVzZSBpdAogICAgZGF0ZSA9IGZvcm1hdCh5bWQoZGF0ZSksICIlbS8lZCIpCiAgKSAlPiUgCiAgZmlsdGVyKCFncmVwbCgiYmVhZCIsIHNhbXBsZSkpICU+JSAKICBzZXBhcmF0ZShjb2wgPSBzYW1wbGUsIHNlcCA9ICItIiwgaW50byA9IGMoInBsYXNtaWQiLCAiaG9zdCIsICJyZXAiKSkKYGBgCgpTYW1wbGUgaW5mb3JtYXRpb24KYGBge3J9CnNhbXBsZSA8LSByZWFkX3RzdigiLi4vMjAyMzAyMDgtY2hpbWVyYS1QaG80LW1ha2V1cC50eHQiLCBjb2xfdHlwZXMgPSAiY2NjY2MiKQpgYGAKCiMgUUMKIyMgMS4gTnVtYmVyIG9mIGV2ZW50cyBwZXIgc2FtcGxlCkJsYW5rIHdlbGxzIGFyZSBpbmRlZWQgYmxhbmsKYGBge3J9CnJhdyAlPiUgZmlsdGVyKGhvc3QgPT0gImJsYW5rIikgJT4lIAogIGdyb3VwX2J5KGRhdGUpICU+JQogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBuX2NlbGxzKSkgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHdlbGwpKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fc2UiLCBnZW9tID0gInBvaW50cmFuZ2UiLCBjb2xvciA9ICJyZWQiLCAKICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4xKSkgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSwgYW5nbGUgPSA5MCkpCmBgYAo+IHRoZXJlIGFyZSBzb21lIGNlbGwgbGlrZSBldmVudHMgaW4gdGhlIGJsYW5rIHdlbGxzLCBlc3BlY2lhbGx5IGluIG9uZSB3ZWxsIG9uIDAyLzIxIGFuZCBhbm90aGVyIG9uIDMvMzEuCj4gMDIvMDggYW5kIDAyLzEwIGFsc28gaGF2ZSBtb3JlIGV2ZW50cyB0aGFuIG90aGVyIGRheXMuIGNoZWNraW5nIHRoZSBwcmUtcHJvY2Vzc2luZyBSIG5vdGVib29rCj4gb3V0cHV0cywgSSB0aGluayB0aGVyZSBtYXkgYmUgc29tZSBjYXJyeSBvdmVyIGJldHdlZW4gd2VsbHMuIE5vIHdpZGUgc3ByZWFkIGNvbnRhbWluYXRpb24gdGhvdWdoLgoKQmVsb3csIHdlIHdpbGwgcmVtb3ZlIHRoZSBibGFuayB3ZWxscyBmcm9tIGZ1cnRoZXIgY29uc2lkZXJhdGlvbgpgYGB7cn0KdG1wIDwtIGZpbHRlcihyYXcsIGhvc3QgIT0gImJsYW5rIikKYGBgCgpgYGB7cn0KdG1wICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBuX2luZHVjdGlvbikpICsgCiAgI2dlb21faGlzdG9ncmFtKGJpbnMgPSAzMCwgZmlsbCA9ICJmb3Jlc3RncmVlbiIpICsgZmFjZXRfZ3JpZChob3N0fmRhdGUpICsgCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoeSA9IGRhdGUpKSArCiAgZmFjZXRfd3JhcCh+aG9zdCkgKyB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE0KSAjKyBwYW5lbF9ib3JkZXIoKQpgYGAKPiBNYWpvcml0eSBvZiB0aGUgZXhwZXJpbWVudGFsIHdlbGxzIChub3QgYmxhbmspIGhhdmUgPiA1MDAwIGV2ZW50cywgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIGEgZmV3IHNhbXBsZXMKCmBgYHtyfQp0bXAgJT4lIGZpbHRlcihuX2luZHVjdGlvbiA8IDEwMDApCmBgYAo+IGZvciBub3csIEkgcGxhbiB0byBsZXQgYW55IHNhbXBsZSB3aXRoID4gMTAwMCBldmVudHMgcGFzcy4gVGhpcyB3b3VsZCBleGNsdWRlIDIwIHNhbXBsZXMgdGhhdCBoYXZlIGV4dHJlbWVseSBsb3cgY291bnRzLCBsaWtlbHkgaW5kaWNhdGluZyBpc3N1ZXMuIGxhdGVyIEkgcGxhbiB0byByZXZpc2l0IHRoZSByZW1haW5pbmcgbG93IGNvdW50IHNhbXBsZXMsIGUuZy4sIG5faW5kdWN0aW9uIGJldHdlZW4gMTAwMCBhbmQgMzAwMC4KClJlbW92ZSBsb3cgZXZlbnQgY291bnQgYW5kIGEgZmV3IGV4cGVyaW1lbnRzIHdpdGggImZhaWwiIGZsYWcuCmBgYHtyfQpkYXQgPC0gZmlsdGVyKHJhdywgaG9zdCAhPSAiYmxhbmsiLCBmbGFnID09ICJwYXNzIikKYGBgCgoKIyMgMi4gQmFja2dyb3VuZCBzdWJ0cmFjdGlvbgoKQ2hlY2sgdGhlIGJhY2tncm91bmQgZmx1b3Jlc2NlbmNlIGxldmVscyBhY3Jvc3MgZGlmZmVyZW50IGRheXMuCmBgYHtyfQpkYXQgJT4lIGZpbHRlcihob3N0ID09ICIxNTYiKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKEJMMS5IOm5SRlAsIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKSAlPiUgCiAgbXV0YXRlKHBhcmFtZXRlciA9IG9yZGVyZWQocGFyYW1ldGVyLCBsZXZlbHMgPSBjKCJCTDEuSCIsICJZTDIuSCIsICJuR0ZQIiwgIm5SRlAiKSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gaW50ZW5zaXR5KSkgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IHdlbGwpKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fc2UiLCBnZW9tID0gInBvaW50cmFuZ2UiLCBjb2xvciA9ICJyZWQiLCAKICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4xKSkgKwogIGZhY2V0X3dyYXAofnBhcmFtZXRlcikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSwgYW5nbGUgPSA5MCkpCmBgYAoKPiAwMi8xOCBhbmQgMDIvMjEgWUZQIGhhcyByZWxhdGl2ZWx5IGhpZ2ggYmFja2dyb3VuZC4gSSBjaGVja2VkIHRoZSBvcmlnaW5hbCBnYXRlZCBkYXRhIGFuZCAKPiBmb3VuZCB0aGF0IGluZGVlZCB0aG9zZSBzYW1wbGVzIGhhdmUgYSBoaWdoZXIgUkZQLiBJdCBpcyBub3QsIGhvd2V2ZXIsIGEgc3lzdGVtYXRpYyBzaGlmdCB1cCwKPiBhcyB0aGUgb3RoZXIgc2FtcGxlcyBkb24ndCBhcHBlYXIgdG8gaGF2ZSBoaWdoZXIgUkZQIGxldmVscyBkdXJpbmcgdGhvc2UgcnVucyAoZGF5cykuCj4gc2ltaWxhcmx5LCAwMi8xMCBzaG93ZWQgaGlnaGVyIEJMMS5IIGJhY2tncm91bmQsIGFuZCB0aGVyZSBpcyBubyBpbmRpY2F0aW9uIHRoYXQgYSBzeXN0ZW1hdGljIAo+IHNoaWZ0IGV4cGxhaW5zIHRoZSBoaWdoZXIgYmFja2dyb3VuZCAod2hpY2ggY2FuIGJlIGNhdXNlZCBieSBhIHZvbHRhZ2UgZGlmZmVyZW5jZSkuCgpgYGB7cn0KdG1wIDwtIGRhdCAlPiUgZmlsdGVyKGhvc3QgPT0gIjE1NiIpCmxtKEJMMS5IIH4gZGF0ZSwgZGF0YSA9IHRtcCkgJT4lIGFub3ZhKCkKYGBgCgo+IEFOT1ZBIGFib3ZlIGRvZXMgcmV2ZWFsIHNpZ25pZmljYW50IHZhcmlhdGlvbiBpbiBHRlAgYmFja2dyb3VuZCBmbHVvcmVzY2VuY2UgYmV0d2VlbiB5SDE1NiBhY3Jvc3MgZGF5cy4KCklzIHRoZXJlIGFueSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgYmFja2dyb3VuZCBCTDEuSCBsZXZlbCBmcm9tIHlIMTU2IGFuZCBtTmVvbiBsZXZlbHMgZnJvbSB0aGUgZXhwZXJpbWVudGFsIHN0cmFpbnM/CmBgYHtyfQp0ZXN0LnN0cmFpbiA9ICIxOTQiCnRtcCA8LSBkYXQgJT4lIAogIGZpbHRlcihncmVwbChwYXN0ZTAoIk5BLTE1NnwiLCB0ZXN0LnN0cmFpbiwgIi0zNzMiKSwgbmFtZSkpICU+JSAKICBncm91cF9ieShkYXRlLCBwbGFzbWlkKSAlPiUgCiAgc3VtbWFyaXplKG1lYW5HRlAgPSBtZWFuKEJMMS5IKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lIAogIHBpdm90X3dpZGVyKGlkX2NvbHMgPSBkYXRlLCBuYW1lc19mcm9tID0gcGxhc21pZCwgbmFtZXNfcHJlZml4ID0gInAiLCB2YWx1ZXNfZnJvbSA9IG1lYW5HRlApCgpsbShwMTk0IH4gcE5BLCBkYXRhID0gdG1wKSAlPiUgc3VtbWFyeSgpCgpwIDwtIGdncGxvdCh0bXAsIGFlcyh4ID0gcE5BLCB5ID0gcDE5NCkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMikgKyAKICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcih0bXAsIHAxOTQgPCAxODAwKSwgYWVzKGxhYmVsID0gZGF0ZSksIG51ZGdlX3ggPSAtMjApICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geX54KSArCiAgeGxhYigiYmFja2dyb3VuZCBCTDEuSCAoeUgxNTYpIikgKyB5bGFiKHBhc3RlMCgiUGhvNC1tTmVvbiAocEgiLCB0ZXN0LnN0cmFpbiwgIikiKSkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpCgpwCgpgYGAKCj4gTm8gc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGJhY2tncm91bmQgYW5kIHRoZSBtTmVvbiBsZXZlbHMKCkdpdmVuIHRoZSByZXN1bHRzIGFib3ZlLCBJIHByb3Bvc2UgdG8gZG8gdGhlIGZvbGxvd2luZzoKCiAgICBJIGFzc3VtZSB0aGUgdmFyaWF0aW9uIGluIHRoZSBub24tZmx1b3Jlc2NlbnQgc3RyYWluJ3MgcmVhZGluZyBpcyBkdWUgdG8gaWRpb3N5bmNocmFjdGljIGJlaGF2aW9yCiAgICBvZiB0aGF0IHN0cmFpbiBhbmQgbm90IGNoYXJhY3RlcmlzdGljIG9mIHRoZSBkYXlzLiBUaGVyZWZvcmUsIEkgcGxhbiB0byBhdmVhcmFnZSB0aGUgYmFja2dyb3VuZAogICAgZmx1b3Jlc2NlbmNlIHRvIGdldCBhIG1vcmUgYWNjdXJhdGUgZXN0aW1hdGUsIHdoaWNoIGltcGxpY2l0bHkgYXNzdW1lcyB0aGF0IHZhcmlhdGlvbiBpbiB0aGUKICAgIGJhY2tncm91bmQgaXMgbWluaW1hbCBiZXR3ZWVuIGRheXMuCgoKU3VidHJhY3QgdGhlIGJhY2tncm91bmQKYGBge3J9CmJnIDwtIGRhdCAlPiUgCiAgZmlsdGVyKGhvc3QgPT0gIjE1NiIpICU+JSAKICBncm91cF9ieShkYXRlKSAlPiUgCiAgIyBhdmVyYWdlIGJhc2VkIG9uIGRhdGVzCiAgc3VtbWFyaXplKGFjcm9zcyhCTDEuSDpuUkZQLCB+IHJvdW5kKG1lYW4oLngpLDEpKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lIAogICMgbWVkaWFuIG9mIGRheSBhdmVyYWdlcwogIHN1bW1hcml6ZShhY3Jvc3MoQkwxLkg6blJGUCwgfm1lZGlhbiguKSkpICU+JSAKICB1bmxpc3QoKSAjIHR1cm4gaW50byBhIG5hbWVkIHZlY3RvciBzdG9yaW5nIGJhY2tncm91bmQgcmVhZGluZ3MKICAjY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJkYXRlIikKCmJnLnJtIDwtIGRhdCAlPiUgCiAgc2VsZWN0KGRhdGU6aG9zdCwgZXZlbnRzID0gbl9pbmR1Y3Rpb24sIEZTQy5IOm5SRlAsIGZsYWcpICU+JQogIG11dGF0ZSgKICAgIEJMMS5IID0gQkwxLkggLSBiZ1siQkwxLkgiXSwKICAgIFlMMi5IID0gWUwyLkggLSBiZ1siWUwyLkgiXSwKICAgIG5HRlAgPSBuR0ZQIC0gYmdbIm5HRlAiXSwKICAgIG5SRlAgPSBuUkZQIC0gYmdbIm5SRlAiXSwKICApCmBgYAoKRG91YmxlIGNoZWNrIHRoYXQgdGhlIHN1YnRyYWN0aW9uIHdvcmtlZCBjb3JyZWN0bHkKYGBge3J9CmJnLnJtICU+JSBmaWx0ZXIoaG9zdCA9PSAiMTU2IikgJT4lIAogIHBpdm90X2xvbmdlcihCTDEuSDpuUkZQLCBuYW1lc190byA9ICJwYXJhbWV0ZXIiLCB2YWx1ZXNfdG8gPSAiaW50ZW5zaXR5IikgJT4lIAogIG11dGF0ZShwYXJhbWV0ZXIgPSBvcmRlcmVkKHBhcmFtZXRlciwgbGV2ZWxzID0gYygiQkwxLkgiLCAiWUwyLkgiLCAibkdGUCIsICJuUkZQIikpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGludGVuc2l0eSkpICsgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSB3ZWxsKSkgKwogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9ICJtZWFuX3NlIiwgZ2VvbSA9ICJwb2ludHJhbmdlIiwgY29sb3IgPSAicmVkIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9udWRnZSh4ID0gMC4xKSkgKwogIGZhY2V0X3dyYXAofnBhcmFtZXRlciwgc2NhbGUgPSAiZnJlZV95IikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE0KSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQpgYGAKCkRldGVybWluZSBpZiBhbnkgc2FtcGxlIGhhZCB2ZXJ5IGxvdyBtTmVvbiBsZXZlbHMgZGVzcGl0ZSBoYXZpbmcgYSBQaG80IGNoaW1lcmEgcGxhc21pZC4gSSBvYnNlcnZlZCBzdWNoIHNhbXBsZXMgd2hlbiBkb2luZyB0aGUgcHJlLXByb2Nlc3NpbmcsIGFuZCB3b3VsZCBsaWtlIHRvIGlkZW50aWZ5IGFuZCByZW1vdmUgdGhlbSBub3cuCgpgYGB7cn0KbXljb2xvcnMgPSBjKGJyZXdlci5wYWwobmFtZT0iUGFpcmVkIiwgbiA9IDgpLCBicmV3ZXIucGFsKG5hbWU9IkRhcmsyIiwgbiA9IDYpKQoKbm8uZXhuLnRoIDwtIDI1MApnZ3Bsb3QoYmcucm0sIGFlcyh4ID0gcGxhc21pZCwgeSA9IGFicyhCTDEuSCkpKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbm8uZXhuLnRoLCBjb2xvciA9ICJncmV5NTAiLCBsaW5ldHlwZSA9IDIsIHNpemUgPSAwLjIpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRhdGUpKSArIAogIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gYygxMDAsIDEwMDAsIDEwMDAwKSkgKyB5bGFiKCJCTDEuSCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXljb2xvcnMpICsgdGhlbWVfY293cGxvdCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgc2l6ZSA9IHJlbCgwLjcpLCB2anVzdCA9IDAuNSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKYGBge3J9CiMgYXNzaWduICJmYWlsIiB0byB0aGUgbm9uLWV4cHJlc3Npbmcgb25lcwpiZy5ybVtiZy5ybSRwbGFzbWlkICE9ICJOQSIgJiBiZy5ybSRCTDEuSCA8IG5vLmV4bi50aCwgImZsYWciXSA9ICJmYWlsIgpmaWx0ZXIoYmcucm0sIGZsYWcgPT0gImZhaWwiKQpgYGAKPiB0aGUgYWJvdmUgc2FtcGxlcyB3aWxsIGJlIGV4cG9ydGVkIGZvciBhcmNoaXZpbmcsIGJ1dCB3b24ndCBiZSB1c2VkIGluIHN1YnNlcXVlbnQgYW5hbHlzZXMuCgpMYXN0bHksIHBIMjMxIGFzc2F5ZWQgb24gMi8xOCBhbmQgMi8xOSBleGhpYml0ZWQgYWJub3JtYWwgYmVoYXZpb3JzLgohW3BIMjMxIG9uIDIvMTgsIDIvMTldKC4uLy4uL2ltZy8yMDIzMTExNi1wSDIzMS1wcm9ibGVtLnBuZykKVGhlIGV4aXN0aW5nIG9uZSByZXBsaWNhdGUgZnJvbSBlYWNoIGRheSBpcyBmcm9tIGJpb2xvZ2ljYWwgcmVwbGljYXRlIDIsIGluIHdoaWNoIHRoZSBtYWpvcml0eSBvZiB0aGUgY2VsbHMgd2VyZSBub3QgaW5kdWNpbmcuIExpbmRzZXkgcmVwZWF0ZWQgdGhpcyBzdHJhaW4gb24gMy8zMCBhbmQgMy8zMSwgYW5kIG9idGFpbmVkIHByb3Blcmx5IGJlaGF2aW5nIGRhdGEuIFRoZXJlZm9yZSwgd2Ugd2lsbCByZW1vdmUgdGhlIHNpbmdsZSByZXBsaWNhdGUgZm9yIHBIMjMxIGZyb20gMi8xOCBhbmQgMi8xOQpgYGB7cn0KYmcucm1bYmcucm0kcGxhc21pZCA9PSAiMjMxIiAmIGJnLnJtJGhvc3QgPT0gIjU1NSIgJiBiZy5ybSRkYXRlICVpbiUgYygiMDIvMTgiLCAiMDIvMTkiKSwgImZsYWciXSA9ICJmYWlsIgpmaWx0ZXIoYmcucm0sIGZsYWcgPT0gImZhaWwiKQpgYGAKCkV4cG9ydCB0aGUgYmFja2dyb3VuZC1zdWJ0cmFjdGVkIHZhbHVlcyBmb3IgbGF0ZXIgdXNlIHdpdGggdGhlIHNoaW55YXBwCmBgYHtyfQojIHJlbW92ZSB0aGUgeUgxNTYgc2FtcGxlcwpkYXQuZXhwb3J0IDwtIGJnLnJtICU+JSAKICBmaWx0ZXIoaG9zdCAhPSAiMTU2IikgJT4lIAogIG11dGF0ZSgKICAgIGhvc3QgPSBvcmRlcmVkKGhvc3QsIGxldmVscyA9IGMoIjU1NSIsICIzNzMiLCAiNTI5IiksIGxhYmVscyA9IGMoIlBITzIiLCAicGhvMuKIhiIsICJQSE84NCIpKQogICkKd3JpdGVfdHN2KGRhdC5leHBvcnQsICIuLi8yMDIzMTAyMy1QSE81LWJnLXN1YnRyYWN0ZWQtZGF0YS50c3YiKQpgYGAKCiMjIDMuIENvbnNpc3RlbmN5IGFjcm9zcyBwbGF0ZXMuCgpDaGVjayB0aGUgYmFja2dyb3VuZC1zdWJ0cmFjdGVkIGZsdW9yZXNjZW5jZSB2YWx1ZXMgZm9yIHRoZSBob3N0IHN0cmFpbnMgKHlIMzczIGFuZCB5SDU1NSkgYXMgd2VsbCBhcyB0aGUgcG9zaXRpdmUgY29udHJvbCBzdHJhaW4gKHBIMTg4IGluIHlIMzczIG9yIHlINTU1IGJhY2tncm91bmRzKSwgYm90aCBvZiB3aGljaCBhcmUgcHJlc2VudCBvbiBlYWNoIHBsYXRlLgoKYGBge3J9CmRhdC5leHBvcnQgJT4lIAogIGZpbHRlcihob3N0ICE9ICJQSE84NCIsIGZsYWcgPT0gInBhc3MiLCBwbGFzbWlkICVpbiUgYygiMTg4IiwgIjE5NCIsICJOQSIpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKEJMMS5IOllMMi5ILCBuYW1lc190byA9ICJwYXJhbWV0ZXIiLCB2YWx1ZXNfdG8gPSAiaW50ZW5zaXR5IikgJT4lIAogICNtdXRhdGUocGFyYW1ldGVyID0gb3JkZXJlZChwYXJhbWV0ZXIsIGxldmVscyA9IGMoIkJMMS5IIiwgIllMMi5IIiwgIm5HRlAiLCAiblJGUCIpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHBsYXNtaWQsIHkgPSBpbnRlbnNpdHksIGdyb3VwID0gZGF0ZSkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBkYXRlKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjcpKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteWNvbG9ycykgKwogIGZhY2V0X2dyaWQocGFyYW1ldGVyfmhvc3QsIHNjYWxlID0gImZyZWVfeSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkjICsKICAjdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZSA9IDMpKQpgYGAKCj4gMS4gVGhlIGhvc3Qgc3RyYWluIGZsdW9yZXNjZW5jZSBsZXZlbHMgYXBwZWFyIHRvIGJlIGNvbnNpc3RlbnQgYWNyb3NzIHRoZSBkYXlzLiBBZGRpdGlvbmFsbHksIHRoZWlyIEdGUCBsZXZlbHMgYXJlIHplcm8sIGFzIGV4cGVjdGVkLgo+IDIuIFRoZSBwb3NpdGl2ZSBjb250cm9sIHN0cmFpbiBoYXMgc3Ryb25nIEJMMS5IIGFuZCBjb3JyZXNwb25kaW5nbHkgaGFzIHN0cm9uZyBZTDIuSC4KPiAzLiAwMi8xMCBiYXRjaCBoYXMgc3RyYW5nZSBCTDEgdmFsdWVzLiBTaW5jZSB3ZSBoYXZlIHRocmVlIGlkZW50aWNhbCBzZXRzICgwMi8xMCwgMTEsIDE2KSwgd2UgY2FuIGlnbm9yZSB0aGlzIGJhdGNoIGluIHRoZSBkb3duc3RyZWFtIGFuYWx5c2lzCgpgYGB7cn0KZGF0LndpZGUuZmlsIDwtIGZpbHRlcihkYXQuZXhwb3J0LCBob3N0ICE9ICJQSE84NCIsIGZsYWcgPT0gInBhc3MiLCBkYXRlICE9ICIwMi8xMCIpCmBgYAoKTnVtYmVyIG9mIHJlcGxpY2F0ZXMgbGVmdCBmb3IgZWFjaCBzYW1wbGUKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQpleHB0IDwtIGRhdC53aWRlLmZpbCAlPiUgCiAgZmlsdGVyKGhvc3QgJWluJSBjKCJQSE8yIiwgInBobzLiiIYiKSwgIXBsYXNtaWQgJWluJSBjKCIxODgiLCAiMTk0IikpICU+JSAKICBncm91cF9ieShkYXRlLCBwbGFzbWlkLCBob3N0KSAlPiUgCiAgc3VtbWFyaXplKG4gPSBuKCksIC5ncm91cHMgPSAiZHJvcCIpCgpleHB0ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBwbGFzbWlkLCB5ID0gbikpICsKICBnZW9tX2NvbChhZXMoZmlsbCA9IGhvc3QpKSArIAogIGZhY2V0X2dyaWQoZGF0ZSB+IC4pICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJQSE8yIiA9ICJncmF5MzAiLCAicGhvMuKIhiIgPSAiZ3JheTcwIikpICsKICB0aGVtZV9taW5pbWFsKCkgKyBiYWNrZ3JvdW5kX2dyaWQobWFqb3IgPSAibm9uZSIpICsgcGFuZWxfYm9yZGVyKHNpemUgPSAwLjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJSZXBsaWNhdGVzIiwgYnJlYWtzID0gYygwLCAzLCA2KSkgKyB4bGFiKE5VTEwpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgpIb3cgbWFueSBwbGFzbWlkLWhvc3QgY29tYmluYXRpb24gaGF2ZSA8IDMgcmVwbGljYXRlcyBhZnRlciByZW1vdmluZyBzYW1wbGVzIHdpdGggbG93IGV2ZW50IGNvdW50cz8KYGBge3J9CmRhdC53aWRlLmZpbCAlPiUKICBjb3VudChwbGFzbWlkLCBob3N0KSAlPiUgCiAgZmlsdGVyKG4gPCAzKQpgYGAKCiMjIDQuIFdlbGwgcG9zaXRpb24KCioqX1JhdGlvbmFsZV8qKgoKLSBJbiBteSBvcmlnaW5hbCBwbGF0ZSBkZXNpZ24sIEkgcGxhY2VkIGEgcG9zaXRpdmUgY29udHJvbCBzdHJhaW4gKGVudmlzaW9uZWQgQ2dQaG80LW1OZW9uIGluIF9QSE8yXyBiYWNrZ3JvdW5kIGZvciBleGFtcGxlKSBpbiB0aGUgY29udHJvbCBjb2x1bW5zICgxLCA1LCA5KSBvbiBldmVyeSBvdGhlciByb3cgKEEsIEMsIEUsIEcpLiBUaGUgcmVhc29uIGZvciB0aGlzIGlzIHRvIHVzZSBpdCB0byBpZGVudGlmeSBhbnkgd2VsbCBwb3NpdGlvbiBlZmZlY3Qgb24gdGhlIGZsdW9yZXNjZW5jZSByZWFkaW5ncy4KLSBJbiBFbWlseSdzIGltcGxlbWVudGF0aW9uLCBzaGUgaW5zdGVhZCBwdXQgKiphIHBhaXIqKiBvZiBzdHJhaW5zLCBuYW1lbHkgcEgxODgteUgzMjMgYW5kIHBIMTg4LXlINTU1IGluIHRoZXNlIHdlbGxzLiBUaGlzIG1lYW5zIEkgY2Fubm90IHVzZSB0aGUgcG9zaXRpdmUgY29udHJvbCB3ZWxscyBleGFjdGx5IHRoZSB3YXkgYXMgSSBkZXNpZ25lZCB0aGVtLiBCdXQgSSBjYW4gc3RpbGwgdXNlIHRoZW0gdG8gc3BvdCBhbnkgdHJlbmQuCiAgICAKYGBge3J9CnRtcCA8LSBkYXQud2lkZS5maWwgJT4lIAogIGZpbHRlcihwbGFzbWlkID09ICIxOTQiLCBob3N0ID09ICJQSE8yIikgJT4lIAogIHNlcGFyYXRlKHdlbGwsIGludG8gPSBjKCJyb3ciLCAiY29sIiksIHNlcCA9IDEpICU+JSAKICBwaXZvdF9sb25nZXIoQkwxLkg6WUwyLkgsIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKQoKcDAgPC0gbGlzdCgKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGRhdGUpLCBzaXplID0gMSksIAogICNnZW9tX2xpbmUoYWVzKGNvbG9yID0gZGF0ZSksIHNpemUgPSAwLjQpLAogIHN0YXRfc3VtbWFyeShhZXMoY29sb3IgPSBkYXRlKSwgZ2VvbSA9ICJsaW5lIiwgZnVuID0gbWVhbiwgbGluZXdpZHRoID0gMC41KSwKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXljb2xvcnMpLAogICNzY2FsZV9jb2xvcl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDIpLAogIGZhY2V0X3dyYXAofnBhcmFtZXRlciwgc2NhbGVzID0gImZyZWVfeSIpLAogIHhsYWIoTlVMTCksCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gImxlZnQiLCBucm93ID0gMiwgYnlyb3cgPSBUUlVFKSksCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTQpLAogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSkpCikKCnAxIDwtIGdncGxvdCh0bXAsIGFlcyh4ID0gcm93LCB5ID0gaW50ZW5zaXR5LCBncm91cCA9IGRhdGUpKSArIHAwICsKICBsYWJzKHN1YnRpdGxlID0gIlZhcmlhdGlvbiBvdmVyIHJvd3MiKQoKcDIgPC0gZ2dwbG90KHRtcCwgYWVzKHggPSBjb2wsIHkgPSBpbnRlbnNpdHksIGdyb3VwID0gZGF0ZSkpICsgcDAgKwogIGxhYnMoc3VidGl0bGUgPSAiVmFyaWF0aW9uIG92ZXIgY29sdW1ucyIpCgpwLmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAxKQoKcGxvdF9ncmlkKHAxICsgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSwgcDIgKyBndWlkZXMoY29sb3IgPSAibm9uZSIpLCBwLmxlZ2VuZCwKICAgICAgICAgIG5yb3cgPSAzLCByZWxfaGVpZ2h0cyA9IGMoMSwxLDAuMykpCmBgYAoKPiAtIG5vIG9iaXZvdXMgdHJlbmQgYmV0d2VlbiB0aGUgcm93czsgdGhlcmUgc2VlbXMgdG8gYmUgZG93bndhcmQgdHJlbmQgZm9yIEJMMSBnb2luZyBmcm9tIGNvbHVtbiAxIHRvIDkKPiAtIGEgY2xlYXIgY29ycmVsYXRpb24gYmV0d2VlbiBZTDIuSCBhbmQgQkwxLkguIGF0IGxlYXN0IHBhcnQgb2YgdGhpcyBpcyBkdWUgdG8gdGhlIGNlbGwgc2l6ZSBkaWZmZXJlbmNlcyAtLSBzZWUgc2l6ZSBub3JtYWxpemVkIGRhdGEgYmVsb3c6CgotIFdoYXQgd2UgY2FyZSBhYm91dCBpcyB0aGUgcmF0aW8gYmV0d2VlbiBQaG80LW1OZW9uIGFuZCBfUEhPNXByXy1tQ2hlcnJ5CgpgYGB7cn0KdG1wIDwtIGRhdC53aWRlLmZpbCAlPiUgCiAgZmlsdGVyKHBsYXNtaWQgPT0gIjE5NCIsIGhvc3QgPT0gIlBITzIiKSAlPiUgCiAgc2VwYXJhdGUod2VsbCwgaW50byA9IGMoInJvdyIsICJjb2wiKSwgc2VwID0gMSkKCnRtcCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gQkwxLkgsIHkgPSBZTDIuSCkpICsgCiAgc3RhdF9zbW9vdGgoYWVzKGNvbG9yID0gZGF0ZSwgZ3JvdXAgPSBkYXRlKSwgCiAgICAgICAgICAgICAgbWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiAwICsgeCwgCiAgICAgICAgICAgICAgc2UgPSBGQUxTRSwgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZGF0ZSwgc2hhcGUgPSBjb2wpLCBzaXplID0gMS41KSArIAogICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXljb2xvcnMpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gMTc6MTkpICsKICBmYWNldF93cmFwKH4gZGF0ZSkgKwogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgbGVnZW5kLmJveCA9ICJob3Jpem9udGFsIikKYGBgCgo+IFRoZXJlIGlzIHZhcmlhdGlvbiBpbiBCTDEuSCBhbmQgY29ycmVzcG9uZGluZ2x5IGluIFlMMi5ILCBidXQgdGhlIHJhdGlvIGJldHdlZW4gdGhlIHR3byBhcmUgdmVyeSBjb25zaXN0ZW50Cj4gMDIvMjAgZGF0YSBpcyBhIGJpdCBvZiBhbiBvdXRsaWVyCgpMZXQncyBjaGVjayBib3RoIFNjUGhvNCAocEgxOTQpIGFuZCBDZ1BobzQgKHBIMTg4KSB0byBzZWUgaWYgdGhlaXIgYmVoYXZpb3JzIGFyZSBjb25zaXN0ZW50IGFjcm9zcyB0aGUgZGF5cy4KYGBge3J9CmRhdC53aWRlLmZpbCAlPiUgCiAgZmlsdGVyKHBsYXNtaWQgJWluJSBjKCIxODgiLCAiMTk0IiksIGhvc3QgIT0gIlBITzg0IikgJT4lIAogIG11dGF0ZShgWUwyLkgvQkwxLkhgID0gWUwyLkgvQkwxLkgsCiAgICAgICAgIFBobzQgPSBmYWN0b3IocGxhc21pZCwgbGV2ZWxzID0gYygiMTk0IiwgIjE4OCIpLCAKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJTY1BobzQiLCAiQ2dQaG80IikpKSAlPiUgCiAgc2VwYXJhdGUod2VsbCwgaW50byA9IGMoInJvdyIsICJjb2wiKSwgc2VwID0gMSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBgWUwyLkgvQkwxLkhgKSkgKyAKICBnZW9tX2JhcihhZXMoZmlsbCA9IGhvc3QpLCBzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIsIAogICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC45KSkgKwogIGdlb21fcG9pbnQoYWVzKGdyb3VwID0gaG9zdCksIHNpemUgPSAxLCBzaGFwZSA9IDMsCiAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSkgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJQSE8yIiA9ICJncmF5NjAiLCAicGhvMuKIhiIgPSAib3JhbmdlIikpICsKICAjc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fc2UiLCBnZW9tID0gInBvaW50cmFuZ2UiLCBjb2xvciA9ICJyZWQiKSArCiAgZmFjZXRfZ3JpZChQaG80IH4gLikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArIGJhY2tncm91bmRfZ3JpZChtYWpvciA9ICJub25lIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoLjgpLCBhbmdsZSA9IDQ1LCB2anVzdCA9IDAuNSkpCmBgYAoKPiAyLzIwIGRhdGEgaXMgbW9yZSB2YXJpYWJsZSAoYWxzbyBzZWUgdGhlIGZpZ3VyZSBhYm92ZSB0aGUgb25lIGltbWVkaWF0ZWx5IGFib3ZlKQoKIyMgNS4gSGlnaCB2YXJpYW5jZSBzYW1wbGVzCgpTdW1tYXJpemUgdGhlIGJhY2tncm91bmQgc3VidHJhY3RlZCBkYXRhIGJ5IGNhbGN1bGF0aW5nIHRoZSBtZWFucyBhbmQgY3YgZm9yIGVhY2ggc3RyYWluLgpgYGB7cn0KY3YgPC0gZGF0LndpZGUuZmlsICU+JSAKICBzZWxlY3QoLW5HRlAsIC1uUkZQKSAlPiUKICBwaXZvdF9sb25nZXIoRlNDLkg6WUwyLkgsIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgcGxhc21pZCwgaG9zdCwgcGFyYW1ldGVyKSAlPiUgCiAgc3VtbWFyaXplKAogICAgbiA9IG4oKSwKICAgIG1lYW4gPSBtZWFuKGludGVuc2l0eSksCiAgICBjdiA9IHNkKGludGVuc2l0eSkvbWVhbihpbnRlbnNpdHkpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lIAogIGFycmFuZ2UoZGVzYyhjdikpCmBgYAoKYGBge3J9CmN2ICU+JSAKICBmaWx0ZXIocGxhc21pZCAhPSAiTkEiLCBwYXJhbWV0ZXIgIT0gIkZTQy5IIikgJT4lIAogIGdncGxvdChhZXMoeCA9IGN2KSkgKyBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkvOCksIGJpbnMgPSAzMCkgKyAKICBzdGF0X2VjZGYoKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuOCwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC4yLCBsaW5ldHlwZSA9IDMsIGNvbG9yID0gInJlZDMiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMikpICsKICB5bGFiKCJjdW11bGF0aXZlIGRlbnNpdHkiKSArIAogIGZhY2V0X3dyYXAofnBhcmFtZXRlcikgKwogIHRoZW1lX2Nvd3Bsb3QoKQpgYGAKCj4gVGhlIGhpc3RvZ3JhbSdzIHktYXhpcyBpcyBub3Qgc2hvd24uIFRoZSBsaW5lIGdyYXBoIHJlcHJlc2VudHMgdGhlIGVtcGlyaWNhbCBDREYsIGFuZCB0aGUgZG90dGVkIGhvcml6b250YWwgbGluZSBpcyBhdCA4MCUuIEdGUCBpcyBtb3JlIHZhcmlhYmxlIHRoYW4gUkZQLCBsaWtlbHkgYmVjYXVzZSB0aGUgYWJzb2x1dGUgdmFsdWVzIG9mIHRoZSBmb3JtZXIgaXMgbG93ZXIuIEZvciBib3RoLCB+ODAlIG9mIHRoZSBzYW1wbGVzIGhhdmUgYSBDViA8IDIwJS4KCkRvIHRoZSBzYW1lIGZvciB0aGUgY2VsbCBzaXplLW5vcm1hbGl6ZWQgdmFsdWVzCmBgYHtyfQpjdi5uIDwtIGRhdC53aWRlLmZpbCAlPiUgCiAgc2VsZWN0KC1CTDEuSCwgLVlMMi5IKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKG5HRlA6blJGUCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gImludGVuc2l0eSIpICU+JSAKICBncm91cF9ieShkYXRlLCBwbGFzbWlkLCBob3N0LCBwYXJhbWV0ZXIpICU+JSAKICBzdW1tYXJpemUoCiAgICBuID0gbigpLAogICAgbWVhbiA9IG51bShtZWFuKGludGVuc2l0eSksIGRpZ2l0cyA9IDApLAogICAgY3YgPSBudW0oc2QoaW50ZW5zaXR5KS9tZWFuKGludGVuc2l0eSksIGRpZ2l0cyA9IDIpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lIAogIGFycmFuZ2UoZGVzYyhjdikpCmBgYAoKYGBge3J9CmN2Lm4gJT4lIAogIGZpbHRlcihwbGFzbWlkICE9ICJOQSIsIHBhcmFtZXRlciAhPSAiRlNDLkgiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gY3YpKSArIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gYWZ0ZXJfc3RhdChkZW5zaXR5KS8xMCksIGJpbnMgPSAzMCkgKyAKICBzdGF0X2VjZGYoKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAuOCwgbGluZXR5cGUgPSAyKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC4yLCBsaW5ldHlwZSA9IDMsIGNvbG9yID0gInJlZDMiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMikpICsKICB5bGFiKCJjdW11bGF0aXZlIGRlbnNpdHkiKSArIAogIGZhY2V0X3dyYXAofnBhcmFtZXRlcikgKwogIHRoZW1lX2Nvd3Bsb3QoKQpgYGAKCj4gQ1YgaXMgYWJvdXQgdGhlIHNhbWUgZm9yIHRoZSBzaXplIG5vcm1hbGl6ZWQgZGF0YS4KCklkZW50aWZ5IHRoZSBoaWdoIHZhcmlhbmNlIHNhbXBsZXMKYGBge3J9CmhpZ2gudmFyLmxpc3QgPC0gY3YgJT4lIAogIG11dGF0ZShDViA9IHJvdW5kKGFicyhjdiksIDMpKSAlPiUgCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IGMoZGF0ZSwgcGxhc21pZCwgaG9zdCksIAogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBwYXJhbWV0ZXIsIG5hbWVzX3ByZWZpeCA9ICJDVl8iLAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gQ1YpICU+JQogIGZpbHRlcihDVl9CTDEuSCA+IDAuMiB8IENWX1lMMi5IID4gMC4yKSAlPiUgCiAgYXJyYW5nZShkYXRlLCBwbGFzbWlkLCBob3N0KQpkYXQuaGlnaC52YXIgPC0gbGVmdF9qb2luKGhpZ2gudmFyLmxpc3QsIGRhdC53aWRlLmZpbCkgJT4lIHNlbGVjdCgtZmxhZykgJT4lIGFycmFuZ2UoZGF0ZSwgcGxhc21pZCkgCgp3cml0ZV90c3YoZGF0LmhpZ2gudmFyLCAiMjAyMzEwMjctaGlnaC12YXItc2FtcGxlcy50c3YiKQoKZGF0LmhpZ2gudmFyICU+JSBmaWx0ZXIoKENWX0JMMS5IID4gMSAmIHBsYXNtaWQgIT0gIk5BIikgfCBDVl9ZTDIuSCA+IDEpCmBgYAoKPiBwbGFzbWlkIDIzMyByZXBsaWNhdGVzIDEgJiAyIHJlcGVhdGVkbHkgc2hvd2VkIG5vIFJGUCBpbmR1Y3Rpb24sIHdoaWxlIHJlcGxpY2F0ZSAzIGRpZCBzaG93IGluZHVjdGlvbi4KCg==