This notebook is to show you how to use the package CytoexploreR to analyze flow cytometry data from the Cytek Aurora. This vignette is worthwhile if you have only one or two flow datasets, like checking samples of GFP-tagged strains for GFP, viability stains of cells, etc. We do not recommend this vignette for timecourse data, ie. multiple subdirectories of flow data, wherein there is one subdirectory for each sampling day. For a vignette that analyzes timecourse flow data, see the our Vignette - Timecourse Flow.

Install CytoExplorer package and requirements. Skip if already installed.

library(BiocManager)
install.packages("cytolib", "flowCore", "flowWorkspace", "openCyto")

Install CytoExploreR from GitHub. Skip is already installed.

library(devtools)
devtools::install_github("DillonHammill/CytoExploreR")

Load required packages

library(CytoExploreR)
library(tidyverse)
library(ggridges)

Vignette Dataset

There were four samples that were analyzed on the Cytek Aurora. One was GAP1 CNV reporter strain (DGY1657), in which a mCitrine gene was inserted upstream of the GAP1 promoter and coding sequence. Additionally, there was one sample each of 3 control strains: zero copy ‘unstained’ control (DGY1) that has no mCitrine, one copy control (DGY500) in which the mCitrine gene was inserted in neutral locus, HO, that doesn’t undergo copy number variation under glutamine-limited growth, and two copy control (DGY1315) containing two inserted copies of mCitrine in neutral loci. Each sample was cultured from a single colony overnight in glutamine-limited media at 30C.

In each sample, 100,000 events were measured on the Cytek Aurora Flow Cytometer for mCitrine fluorescence (excitation wavelength = 516, emission wavelength = 529 ) via the B2-A channel, forward scatter (FSC), and side scatter (SSC). Note that you may have used a different channel depending on your cell stain.

Set working directory

Place your FCS files in a directory. Multiple subdirectories containing FCS files are OK.

Note 1: You can only load in one folder’s worth of FCS files at a time. In this vignette, we put all of our FCS data files in one subdirectory named FCS_files. There is one FSC file per sample. In total, there are 4 FSC files.

Note 2: You should NOT manually rename your FCS files after exporting it from the Cytek. In other words, files should have their original names from the machine. “Renaming the files manually is not recommended as it does not update the associated keywords contained in the FCS files” (Source: https://github.com/DillonHammill/CytoExploreR/issues/85). If files need to renamed, do so on on the Cytek software and re-export the FCS files.

require("knitr")
knitr::opts_knit$set(root.dir = "~/Documents/vignette_simple_data_workingcopy")
getwd()
[1] "/Users/juliechuong/Documents/vignette_simple_data_workingcopy"

Version name

Choose a name to be used for all output files including the gatingTemplate.csv and associated flow data and graphs.
In this vignette, we will set it to my initials, date, and version 1.

version_name = "my_initials_date_v1"

STEP 1: Load in FCS data as a gating set

INSTRUCTIONS:

my_gating_set <- cyto_setup(path = "./FCS_files", restrict=TRUE, select="fcs")

STEP 2: Edit the experimental details to include metadata from the sample sheet

Merge samplesheet with autogenerated experiment details file generated from cyto_setup().
ONLY NEED TO DO ONCE to generate Vignette-Experiment-Details.csv file. Once you have the file, it will automatically be used the next time you load in FCS data as a gating set (STEP 1).

Here we assume the samplesheet metadata file is already in the working directory.
Provide the file path to the sample sheet (metadata).

file.rename(dir(pattern = "Experiment-Details.csv"),"Vignette-Experiment-Details.csv")
exp_details = read_csv(file = paste0(list.files(pattern = "Experiment-Details.csv")))
sample_sheet = read_csv(file = paste0(list.files(pattern = "sample_sheet_vignette_simple.csv")))
experiment_details = left_join(exp_details, sample_sheet) %>%
  write_csv("Vignette-Experiment-Details.csv")

Add the experiment details metadata to the gating set.
ONLY NEED TO ONCE

for(i in 1:length(names(experiment_details))){
  flowWorkspace::pData(my_gating_set)[names(experiment_details[i])]<-experiment_details[i]
}

Check that the experiment details metadata were successfully attached to the gating set.

cyto_details(my_gating_set)

By default the Experiment-Markers.csv file is named with the date created.
Rename the autogenerated experiment-markers.csv file.
ONLY NEED TO DO ONCE

file.rename(dir(pattern = "Experiment-Markers.csv"),"Vignette-Experiment-Markers.csv")

STEP 3: Perform gating on gating set

Gate for 1) Cells, 2) Singlets, 3) CNVS
Results in a gating file and gated data.

Transform the data
Transforming the data makes it easier to interpret visually and well as to draw gates.
For this vignette’s data, when the transformed data are plotted, the zero copy strains take up most of the coordinate plane and the one copy and two copy distributions are very close together. The fluorescence profiles/distributions of the one copy and two copy controls overlap some. They are not mutually exclusive which is a limitation to note.

This is a useful article if you I want think about to choosing different transformations: https://dillonhammill.github.io/CytoExploreR/articles/CytoExploreR-Transformations.html

In our case, it was necessary to use a logicle transformation for the B2-A values and then a log transformation for the other values. I could not use logicle transformation nor could I use the log transformation for all values.

IMPORTANT: If you get an error message*, run the in the Console, not inside the R notebook. Make sure you reset your working directory in the Console.

*Error in plot.new() : QuartzBitmap_Output - unable to open file

GFP_trans <- cyto_transformer_logicle(my_gating_set,
                                      channels = c("B2-A"),
                                      widthBasis = -10
)#returns it as a list

FSC_SSC_trans <- cyto_transformer_log(my_gating_set,
                                      channels = c("FSC-A", "FSC-H", "SSC-A", "SSC-H")
) #log transform the forward and side scatter


combined_trans <- cyto_transformer_combine(GFP_trans,FSC_SSC_trans) #combine both transformations

transformed_gating_set <- cyto_transform(my_gating_set,
                                                   trans = combined_trans) #applies the the transformation and returns it as a gatingSet

Check the transformed data by plotting

cyto_plot_explore(transformed_gating_set, #plots all FCS files
                  channels_x = "FSC-A",
                  channels_y = "B2-A",
                  axes_limits = "data")

Subset rows to plot as desired, using the row assignment in cyto_details()

cyto_details(transformed_gating_set)

cyto_plot_explore(transformed_gating_set[c(2,3)], #optionally subset rows to plot 
                  channels_x = "FSC-A",
                  channels_y = "B2-A",
                  axes_limits = "data")

Draw Gates using Control Strains as a Guide

Note: if you already have a gating template and don’t need to draw gates, skip the cyto_gate_draw() steps.
Instead, use cyto_gatingTemplate_apply() to apply a gatingTemplate.csv to your gating set.

cyto_gatingTemplate_apply(transformed_gating_set, gatingTemplate=paste0( "cytek_gating_",version_name,".csv"))
To Draw Gates

INSTRUCTIONS

  • A new window will pop out.
  • Draw the desired gate.
  • Press esc on keyboard when finished drawing to save the gate.
  • Gating coordinates will be placed in a gatingTemplate.csv file which can be applied (without drawing again) in future code runs.

First we gate for the cells, filtering out cell debris, bacteria, etc.

cyto_gate_draw(transformed_gating_set,
               parent = "root",
               alias = "Cells",
               channels = c("FSC-A","SSC-A"),
               axes_limits = "data",
               gatingTemplate = paste0("cytek_gating_",version_name,".csv")
)

Then we define the singlets based on side-scatter area and height. See other ways to discriminate doublets: https://expert.cheekyscientist.com/how-to-perform-doublet-discrimination-in-flow-cytometry/

cyto_gate_draw(transformed_gating_set,
               parent = "Cells",
               alias = "Single_cells",
               channels = c("SSC-A","SSC-H"),
               axes_limits = "data",
               gatingTemplate = paste0("cytek_gating_",version_name,".csv")
)

Gating for CNVs using the 0,1 and 2 copy controls:
In cyto_extract(), we can subset and extract the rows of interest. Here, we use cyto_extract() to extract the 0, 1, and 2 copy control populations in order to overlay them on a plot when drawing gates. The row assignment correspond to the experimental details associated with the gating set.
In cyto_gate_draw(), choose colors of the parent population and the overlay populations.
In cyto_date_draw(), the alias argument lets you choose gate names.

cyto_details(transformed_gating_set) #View the rows and choose which to extract
zero_ctrl <- cyto_extract(transformed_gating_set, "Single_cells")[c(1)] # row of 0 copy control data

one_ctrl <- cyto_extract(transformed_gating_set, "Single_cells")[c(2)] # row of 1 copy control data

two_ctrl <- cyto_extract(transformed_gating_set, "Single_cells")[c(4)] # row of 2 copy control data

cyto_gate_draw(transformed_gating_set,
               point_col = c("gray", "green", "black", "blue"), #choose colors
               parent = "Single_cells", #maps to first point color
               overlay = c(zero_ctrl, one_ctrl, two_ctrl),#maps to remaining point colors
               alias = c("zero_copy", "one_copy", "two_or_more_copy"), #Name the gate names here
               channels = c("FSC-A","B2-A"),
               axes_limits = "data",
               gatingTemplate = paste0("cytek_gating_",version_name,".csv")
)

STEP 4: Get statistics

Get cell counts in each gate

counts = gs_pop_get_stats(transformed_gating_set, c("Single_cells", "zero_copy", "one_copy", "two_or_more_copy")) %>%
    rename(Gate = pop, name = sample, Count = count) %>%
    left_join(experiment_details) %>%
    write_csv(paste0(version_name,"_counts.csv"))

#counts = read_csv(paste0(version_name,"_counts.csv"))

Get frequency of cells inside each gate

freq = gs_pop_get_stats(transformed_gating_set, c("Single_cells","zero_copy", "one_copy", "two_or_more_copy"), type = "percent") %>%
    rename(Gate = pop, name = sample, Frequency = percent) %>%
    left_join(experiment_details) %>%
    write_csv(paste0(version_name,"_freq.csv"))

#freq = read_csv(paste0(version_name,"_freq.csv"))

Check that there are at least 70,000 single cells per sample

freq_and_counts =
  counts %>% filter(Gate == "Single_cells") %>%
  rename(Parent = Gate) %>%
  left_join(freq) %>%
  filter(!(Gate == "Single_cells")) %>%
  mutate(Frequency = Frequency*100) %>%
  relocate(2:3, .after = Gate)
freq_and_counts %>%
  distinct(Strain, Count)

Plot frequency of cells in gates per sample

freq_bar = freq_and_counts %>%
  mutate(Gate = fct_relevel(Gate, c("zero_copy", "one_copy", "two_or_more_copy")),
         Strain=fct_relevel(Strain, c("DGY1","DGY500","DGY1657","DGY1315")),
         Description=fct_relevel(Description, c("0 copy control", "1 copy control","CNV reporter strain","2 copy control"))
         )%>%
  filter(Count>70000) %>%
  ggplot(aes(Description, Frequency, fill = Gate)) +
  geom_bar(position="dodge", stat="identity") +
  scale_fill_manual(values= c(RColorBrewer::brewer.pal(3, "Greens"))) +
  ylab("% of cells in gate") +
  theme_classic() +
  theme(text = element_text(size=12))

freq_bar

Get raw transformed B2-A and FSC values for every cell

timepoint_raw_list <- cyto_extract(transformed_gating_set, parent = "Single_cells", raw = TRUE, channels = c("FSC-A", "B2-A")) #raw flow data of each cell as a list of matrices
  
sc_distr = map_df(timepoint_raw_list, ~as.data.frame(.x), .id="name") %>% #convert to df, put list name in new column
   left_join(experiment_details) %>% #join by name column to add metadata
   mutate(B2A_FSC = `B2-A`/`FSC-A`) %>% #compute normalized fluorescence for every cell
   write_csv(paste0(version_name,"_SingleCellDistributions.csv"))

sc_distr = read_csv(paste0(version_name,"_SingleCellDistributions.csv"))

STEP 5: Make Plots

Graph single cell fluorescence distributions (ridgeplots) for each sample

sc_distr %>%
ggplot(aes(B2A_FSC, Description, fill = `mCitrine copy number`)) +
  geom_density_ridges(scale = 1) +
  scale_y_discrete(expand = expansion(add = c(0.2, 1.0)))+
   scale_fill_manual(values= c(RColorBrewer::brewer.pal(3, "Greens"))) +
  scale_x_continuous("normalized fluorescence", limits=c(0, 3.0), breaks = c(0, 1, 2, 3), labels = c(0,1,2,3)) +
  theme_classic() +
  theme(
      axis.text.x = element_text(family="Arial", size = 10, color = "black"), #edit x-tick labels
      axis.text.y = element_text(family="Arial", size = 10, color = "black"),
      strip.background = element_blank(), #remove box around facet title
      strip.text = element_text(size=12)
  )
Picking joint bandwidth of 0.0133
Warning: Removed 635 rows containing non-finite values (stat_density_ridges).

Graph boxplots of normalized fluorescence per sample

LS0tCnRpdGxlOiAiVmlnbmV0dGUgLSBTaW1wbGUgRmxvdyIKYXV0aG9yOiAiSnVsaWUgQ2h1b25nIgpkYXRlOiAiNC8zLzIwMjIiCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAotLS0KVGhpcyBub3RlYm9vayBpcyB0byBzaG93IHlvdSBob3cgdG8gdXNlIHRoZSBwYWNrYWdlIEN5dG9leHBsb3JlUiB0byBhbmFseXplIGZsb3cgY3l0b21ldHJ5IGRhdGEgZnJvbSB0aGUgQ3l0ZWsgQXVyb3JhLiAKVGhpcyB2aWduZXR0ZSBpcyB3b3J0aHdoaWxlIGlmIHlvdSBoYXZlIG9ubHkgb25lIG9yIHR3byBmbG93IGRhdGFzZXRzLCBsaWtlIGNoZWNraW5nIHNhbXBsZXMgb2YgR0ZQLXRhZ2dlZCBzdHJhaW5zIGZvciBHRlAsIHZpYWJpbGl0eSBzdGFpbnMgb2YgY2VsbHMsIGV0Yy4gCldlIGRvIG5vdCByZWNvbW1lbmQgdGhpcyB2aWduZXR0ZSBmb3IgdGltZWNvdXJzZSBkYXRhLCBpZS4gbXVsdGlwbGUgc3ViZGlyZWN0b3JpZXMgb2YgZmxvdyBkYXRhLCB3aGVyZWluIHRoZXJlIGlzIG9uZSBzdWJkaXJlY3RvcnkgZm9yIGVhY2ggc2FtcGxpbmcgZGF5LiAKRm9yIGEgdmlnbmV0dGUgdGhhdCBhbmFseXplcyB0aW1lY291cnNlIGZsb3cgZGF0YSwgc2VlIHRoZSBvdXIgVmlnbmV0dGUgLSBUaW1lY291cnNlIEZsb3cuICAKCgojIyMgSW5zdGFsbCBDeXRvRXhwbG9yZXIgcGFja2FnZSBhbmQgcmVxdWlyZW1lbnRzLiBTa2lwIGlmIGFscmVhZHkgaW5zdGFsbGVkLgpgYGB7ciBpbmNsdWRlID0gRkFMU0V9CnJlcXVpcmUoImtuaXRyIikKa25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBGQUxTRSwgZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KEJpb2NNYW5hZ2VyKQppbnN0YWxsLnBhY2thZ2VzKCJjeXRvbGliIiwgImZsb3dDb3JlIiwgImZsb3dXb3Jrc3BhY2UiLCAib3BlbkN5dG8iKQpgYGAKCkluc3RhbGwgQ3l0b0V4cGxvcmVSIGZyb20gR2l0SHViLiBTa2lwIGlzIGFscmVhZHkgaW5zdGFsbGVkLgpgYGB7cn0KbGlicmFyeShkZXZ0b29scykKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJEaWxsb25IYW1taWxsL0N5dG9FeHBsb3JlUiIpCmBgYAoKTG9hZCByZXF1aXJlZCBwYWNrYWdlcwpgYGB7cn0KbGlicmFyeShDeXRvRXhwbG9yZVIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncmlkZ2VzKQpgYGAKCiMjIFZpZ25ldHRlIERhdGFzZXQKClRoZXJlIHdlcmUgZm91ciBzYW1wbGVzIHRoYXQgd2VyZSBhbmFseXplZCBvbiB0aGUgQ3l0ZWsgQXVyb3JhLiAKT25lIHdhcyAqR0FQMSogQ05WIHJlcG9ydGVyIHN0cmFpbiAoREdZMTY1NyksIGluIHdoaWNoIGEgbUNpdHJpbmUgZ2VuZSB3YXMgaW5zZXJ0ZWQgdXBzdHJlYW0gb2YgdGhlICpHQVAxKiBwcm9tb3RlciAKYW5kIGNvZGluZyBzZXF1ZW5jZS4gQWRkaXRpb25hbGx5LCB0aGVyZSB3YXMgb25lIHNhbXBsZSBlYWNoIG9mIDMgY29udHJvbCBzdHJhaW5zOiB6ZXJvIGNvcHkgJ3Vuc3RhaW5lZCcgY29udHJvbCAKKERHWTEpIHRoYXQgaGFzIG5vIG1DaXRyaW5lLCBvbmUgY29weSBjb250cm9sIChER1k1MDApIGluIHdoaWNoIHRoZSBtQ2l0cmluZSBnZW5lIHdhcyBpbnNlcnRlZCBpbiBuZXV0cmFsIGxvY3VzLCBITywKdGhhdCBkb2Vzbid0IHVuZGVyZ28gY29weSBudW1iZXIgdmFyaWF0aW9uIHVuZGVyIGdsdXRhbWluZS1saW1pdGVkIGdyb3d0aCwgYW5kIHR3byBjb3B5IGNvbnRyb2wgKERHWTEzMTUpIGNvbnRhaW5pbmcKdHdvIGluc2VydGVkIGNvcGllcyBvZiBtQ2l0cmluZSBpbiBuZXV0cmFsIGxvY2kuIEVhY2ggc2FtcGxlIHdhcyBjdWx0dXJlZCBmcm9tIGEgc2luZ2xlIGNvbG9ueSBvdmVybmlnaHQgaW4KZ2x1dGFtaW5lLWxpbWl0ZWQgbWVkaWEgYXQgMzBDLiAKCkluIGVhY2ggc2FtcGxlLCAxMDAsMDAwIGV2ZW50cyB3ZXJlIG1lYXN1cmVkIG9uIHRoZSBDeXRlayBBdXJvcmEgRmxvdyBDeXRvbWV0ZXIgZm9yIG1DaXRyaW5lIGZsdW9yZXNjZW5jZQooZXhjaXRhdGlvbiB3YXZlbGVuZ3RoID0gNTE2LCBlbWlzc2lvbiB3YXZlbGVuZ3RoID0gNTI5ICkgdmlhIHRoZSBCMi1BIGNoYW5uZWwsIGZvcndhcmQgc2NhdHRlciAoRlNDKSwgYW5kIApzaWRlIHNjYXR0ZXIgKFNTQykuCk5vdGUgdGhhdCB5b3UgbWF5IGhhdmUgdXNlZCBhIGRpZmZlcmVudCBjaGFubmVsIGRlcGVuZGluZyBvbiB5b3VyIGNlbGwgc3RhaW4uIAoKIyMgU2V0IHdvcmtpbmcgZGlyZWN0b3J5ClBsYWNlIHlvdXIgRkNTIGZpbGVzIGluIGEgZGlyZWN0b3J5LiBNdWx0aXBsZSBzdWJkaXJlY3RvcmllcyBjb250YWluaW5nIEZDUyBmaWxlcyBhcmUgT0suIAoKKipOb3RlIDEqKjogWW91IGNhbiBvbmx5IGxvYWQgaW4gb25lIGZvbGRlcidzIHdvcnRoIG9mIEZDUyBmaWxlcyBhdCBhIHRpbWUuIEluIHRoaXMgdmlnbmV0dGUsIAp3ZSBwdXQgYWxsIG9mIG91ciBGQ1MgZGF0YSBmaWxlcyBpbiBvbmUgc3ViZGlyZWN0b3J5IG5hbWVkIEZDU19maWxlcy4gVGhlcmUgaXMgb25lIEZTQyBmaWxlIHBlciBzYW1wbGUuCkluIHRvdGFsLCB0aGVyZSBhcmUgNCBGU0MgZmlsZXMuIAoKKipOb3RlIDIqKjogWW91IHNob3VsZCAqKk5PVCoqIG1hbnVhbGx5IHJlbmFtZSB5b3VyIEZDUyBmaWxlcyBhZnRlciBleHBvcnRpbmcgaXQgZnJvbSB0aGUgQ3l0ZWsuIApJbiBvdGhlciB3b3JkcywgZmlsZXMgc2hvdWxkIGhhdmUgdGhlaXIgb3JpZ2luYWwgbmFtZXMgZnJvbSB0aGUgbWFjaGluZS4gCiJSZW5hbWluZyB0aGUgZmlsZXMgbWFudWFsbHkgaXMgbm90IHJlY29tbWVuZGVkIGFzIGl0IGRvZXMgbm90IHVwZGF0ZSB0aGUgYXNzb2NpYXRlZCBrZXl3b3JkcyBjb250YWluZWQgaW4gdGhlIEZDUyBmaWxlcyIKKFNvdXJjZTogaHR0cHM6Ly9naXRodWIuY29tL0RpbGxvbkhhbW1pbGwvQ3l0b0V4cGxvcmVSL2lzc3Vlcy84NSkuIElmIGZpbGVzIG5lZWQgdG8gcmVuYW1lZCwgZG8gc28gb24gb24gdGhlIEN5dGVrCnNvZnR3YXJlIGFuZCByZS1leHBvcnQgdGhlIEZDUyBmaWxlcy4gIApgYGB7ciBzZXR1cCwgcmVzdWx0cz0nYXNpcyd9CnJlcXVpcmUoImtuaXRyIikKa25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAifi9Eb2N1bWVudHMvdmlnbmV0dGVfc2ltcGxlX2RhdGFfd29ya2luZ2NvcHkiKQpgYGAKCmBgYHtyIGluY2x1ZGUgPSBUUlVFfQpnZXR3ZCgpCmBgYAoKIyMgVmVyc2lvbiBuYW1lCkNob29zZSBhIG5hbWUgdG8gYmUgdXNlZCBmb3IgYWxsIG91dHB1dCBmaWxlcyBpbmNsdWRpbmcgdGhlIGdhdGluZ1RlbXBsYXRlLmNzdiBhbmQgYXNzb2NpYXRlZCBmbG93IGRhdGEgYW5kIGdyYXBocy4gIApJbiB0aGlzIHZpZ25ldHRlLCB3ZSB3aWxsIHNldCBpdCB0byBteSBpbml0aWFscywgZGF0ZSwgYW5kIHZlcnNpb24gMS4gCmBgYHtyfQp2ZXJzaW9uX25hbWUgPSAibXlfaW5pdGlhbHNfZGF0ZV92MSIKYGBgCgojIyBTVEVQIDE6IExvYWQgaW4gRkNTIGRhdGEgYXMgYSBnYXRpbmcgc2V0ICAKICAKICAqKklOU1RSVUNUSU9OUyoqOiAKCisgQW4gZWRpdGFibGUgbWFya2VycyBzaGVldCB3aWxsIHNob3cgdXAgb24gYFZpZXdlcmAgUGFuZS4KKyBFZGl0IE1hcmtlcnMgb24gVmlld2VyIHBhbmUgYnkgdHlwaW5nIGluICJCMi1BIiBpbiB0aGUgYG1hcmtlcmAgY29sdW1uIG9mIHRoZSBCMi1BIGNoYW5uZWwuIEZTQyBhbmQgU1NDIGFyZSBpbmNsdWRlZCBtYXJrZXJzIGJ5IGRlZmF1bHQgc28geW91IGRvIG5vdCBuZWVkIHRvIGVkaXQgdGhlbS4gIAorIFNjcm9sbCB0byB0aGUgYm90dG9tIG9mIHRoZSBWaWV3ZXIgcGFuZS4gIAorIENsaWNrIGBTYXZlICYgQ2xvc2VgIGJ1dHRvbiBhdCB0aGUgYm90dG9tIG9mIHRoZSBgVmlld2VyYCBwYW5lLiAgCgohW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvMkc4VVAxZ2ZUU0FJSXFNOGZHL2dpcGh5LmdpZikKYGBge3J9Cm15X2dhdGluZ19zZXQgPC0gY3l0b19zZXR1cChwYXRoID0gIi4vRkNTX2ZpbGVzIiwgcmVzdHJpY3Q9VFJVRSwgc2VsZWN0PSJmY3MiKQpgYGAKCgojIyBTVEVQIDI6IEVkaXQgdGhlIGV4cGVyaW1lbnRhbCBkZXRhaWxzIHRvIGluY2x1ZGUgbWV0YWRhdGEgZnJvbSB0aGUgc2FtcGxlIHNoZWV0IApNZXJnZSBzYW1wbGVzaGVldCB3aXRoIGF1dG9nZW5lcmF0ZWQgZXhwZXJpbWVudCBkZXRhaWxzIGZpbGUgZ2VuZXJhdGVkIGZyb20gYGN5dG9fc2V0dXAoKWAuICAKKipPTkxZIE5FRUQgVE8gRE8gT05DRSoqIHRvIGdlbmVyYXRlIGBWaWduZXR0ZS1FeHBlcmltZW50LURldGFpbHMuY3N2YCBmaWxlLiBPbmNlIHlvdSBoYXZlIHRoZSBmaWxlLCBpdCB3aWxsIGF1dG9tYXRpY2FsbHkgYmUgdXNlZCB0aGUgbmV4dCB0aW1lIHlvdSBsb2FkIGluIEZDUyBkYXRhIGFzIGEgZ2F0aW5nIHNldCAoU1RFUCAxKS4KCkhlcmUgd2UgYXNzdW1lIHRoZSBzYW1wbGVzaGVldCBtZXRhZGF0YSBmaWxlIGlzIGFscmVhZHkgaW4gdGhlIHdvcmtpbmcgZGlyZWN0b3J5LiAgIApQcm92aWRlIHRoZSBmaWxlIHBhdGggdG8gdGhlIHNhbXBsZSBzaGVldCAobWV0YWRhdGEpLiAKYGBge3J9CmZpbGUucmVuYW1lKGRpcihwYXR0ZXJuID0gIkV4cGVyaW1lbnQtRGV0YWlscy5jc3YiKSwiVmlnbmV0dGUtRXhwZXJpbWVudC1EZXRhaWxzLmNzdiIpCmV4cF9kZXRhaWxzID0gcmVhZF9jc3YoZmlsZSA9IHBhc3RlMChsaXN0LmZpbGVzKHBhdHRlcm4gPSAiRXhwZXJpbWVudC1EZXRhaWxzLmNzdiIpKSkKc2FtcGxlX3NoZWV0ID0gcmVhZF9jc3YoZmlsZSA9IHBhc3RlMChsaXN0LmZpbGVzKHBhdHRlcm4gPSAic2FtcGxlX3NoZWV0X3ZpZ25ldHRlX3NpbXBsZS5jc3YiKSkpCmV4cGVyaW1lbnRfZGV0YWlscyA9IGxlZnRfam9pbihleHBfZGV0YWlscywgc2FtcGxlX3NoZWV0KSAlPiUKICB3cml0ZV9jc3YoIlZpZ25ldHRlLUV4cGVyaW1lbnQtRGV0YWlscy5jc3YiKQpgYGAKICAKICBBZGQgdGhlIGV4cGVyaW1lbnQgZGV0YWlscyBtZXRhZGF0YSB0byB0aGUgZ2F0aW5nIHNldC4gIAogICoqT05MWSBORUVEIFRPIE9OQ0UqKgpgYGB7cn0KZm9yKGkgaW4gMTpsZW5ndGgobmFtZXMoZXhwZXJpbWVudF9kZXRhaWxzKSkpewogIGZsb3dXb3Jrc3BhY2U6OnBEYXRhKG15X2dhdGluZ19zZXQpW25hbWVzKGV4cGVyaW1lbnRfZGV0YWlsc1tpXSldPC1leHBlcmltZW50X2RldGFpbHNbaV0KfQpgYGAKCgpDaGVjayB0aGF0IHRoZSBleHBlcmltZW50IGRldGFpbHMgbWV0YWRhdGEgd2VyZSBzdWNjZXNzZnVsbHkgYXR0YWNoZWQgdG8gdGhlIGdhdGluZyBzZXQuICAgCmBgYHtyfQpjeXRvX2RldGFpbHMobXlfZ2F0aW5nX3NldCkKYGBgCkJ5IGRlZmF1bHQgdGhlIEV4cGVyaW1lbnQtTWFya2Vycy5jc3YgZmlsZSBpcyBuYW1lZCB3aXRoIHRoZSBkYXRlIGNyZWF0ZWQuICAKUmVuYW1lIHRoZSBhdXRvZ2VuZXJhdGVkIGV4cGVyaW1lbnQtbWFya2Vycy5jc3YgZmlsZS4gIAoqKk9OTFkgTkVFRCBUTyBETyBPTkNFKioKYGBge3J9CmZpbGUucmVuYW1lKGRpcihwYXR0ZXJuID0gIkV4cGVyaW1lbnQtTWFya2Vycy5jc3YiKSwiVmlnbmV0dGUtRXhwZXJpbWVudC1NYXJrZXJzLmNzdiIpCmBgYCAgCiMjIFNURVAgMzogIFBlcmZvcm0gZ2F0aW5nIG9uIGdhdGluZyBzZXQKR2F0ZSBmb3IgMSkgQ2VsbHMsIDIpIFNpbmdsZXRzLCAzKSBDTlZTICAKUmVzdWx0cyBpbiBhIGdhdGluZyBmaWxlIGFuZCBnYXRlZCBkYXRhLiAKCioqVHJhbnNmb3JtIHRoZSBkYXRhKiogIApUcmFuc2Zvcm1pbmcgdGhlIGRhdGEgbWFrZXMgaXQgZWFzaWVyIHRvIGludGVycHJldCB2aXN1YWxseSBhbmQgd2VsbCBhcyB0byBkcmF3IGdhdGVzLiAgCkZvciB0aGlzIHZpZ25ldHRlJ3MgZGF0YSwgd2hlbiB0aGUgdHJhbnNmb3JtZWQgZGF0YSBhcmUgcGxvdHRlZCwgdGhlIHplcm8gY29weSBzdHJhaW5zIHRha2UgdXAgbW9zdCBvZiB0aGUgY29vcmRpbmF0ZSBwbGFuZSBhbmQgdGhlIG9uZSBjb3B5IGFuZCB0d28gY29weSBkaXN0cmlidXRpb25zIGFyZSB2ZXJ5IGNsb3NlIHRvZ2V0aGVyLiBUaGUgZmx1b3Jlc2NlbmNlIHByb2ZpbGVzL2Rpc3RyaWJ1dGlvbnMgb2YgdGhlIG9uZSBjb3B5IGFuZCB0d28gY29weSBjb250cm9scyBvdmVybGFwIHNvbWUuIFRoZXkgYXJlIG5vdCBtdXR1YWxseSBleGNsdXNpdmUgd2hpY2ggaXMgYSBsaW1pdGF0aW9uIHRvIG5vdGUuIAoKVGhpcyBpcyBhIHVzZWZ1bCBhcnRpY2xlIGlmIHlvdSBJIHdhbnQgdGhpbmsgYWJvdXQgdG8gY2hvb3NpbmcgZGlmZmVyZW50IHRyYW5zZm9ybWF0aW9uczoKaHR0cHM6Ly9kaWxsb25oYW1taWxsLmdpdGh1Yi5pby9DeXRvRXhwbG9yZVIvYXJ0aWNsZXMvQ3l0b0V4cGxvcmVSLVRyYW5zZm9ybWF0aW9ucy5odG1sCgpJbiBvdXIgY2FzZSwgaXQgd2FzIG5lY2Vzc2FyeSB0byB1c2UgYSBsb2dpY2xlIHRyYW5zZm9ybWF0aW9uIGZvciB0aGUgYEIyLUFgIHZhbHVlcyBhbmQgdGhlbgphIGxvZyB0cmFuc2Zvcm1hdGlvbiBmb3IgdGhlIG90aGVyIHZhbHVlcy4gSSBjb3VsZCBub3QgdXNlIGBsb2dpY2xlIHRyYW5zZm9ybWF0aW9uYCBub3IgY291bGQgSSB1c2UgdGhlIGBsb2cgdHJhbnNmb3JtYXRpb25gIGZvciBhbGwgdmFsdWVzLiAKCioqSU1QT1JUQU5UKio6IElmIHlvdSBnZXQgYW4gZXJyb3IgbWVzc2FnZSosIHJ1biB0aGUgaW4gdGhlIENvbnNvbGUsIG5vdCBpbnNpZGUgdGhlIFIgbm90ZWJvb2suIE1ha2Ugc3VyZSB5b3UgcmVzZXQgeW91ciB3b3JraW5nIGRpcmVjdG9yeSBpbiB0aGUgQ29uc29sZS4gCgoqRXJyb3IgaW4gcGxvdC5uZXcoKSA6IFF1YXJ0ekJpdG1hcF9PdXRwdXQgLSB1bmFibGUgdG8gb3BlbiBmaWxlIAoKYGBge3J9CkdGUF90cmFucyA8LSBjeXRvX3RyYW5zZm9ybWVyX2xvZ2ljbGUobXlfZ2F0aW5nX3NldCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGFubmVscyA9IGMoIkIyLUEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aEJhc2lzID0gLTEwCikjcmV0dXJucyBpdCBhcyBhIGxpc3QKRlNDX1NTQ190cmFucyA8LSBjeXRvX3RyYW5zZm9ybWVyX2xvZyhteV9nYXRpbmdfc2V0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNoYW5uZWxzID0gYygiRlNDLUEiLCAiRlNDLUgiLCAiU1NDLUEiLCAiU1NDLUgiKQopICNsb2cgdHJhbnNmb3JtIHRoZSBmb3J3YXJkIGFuZCBzaWRlIHNjYXR0ZXIKCmNvbWJpbmVkX3RyYW5zIDwtIGN5dG9fdHJhbnNmb3JtZXJfY29tYmluZShHRlBfdHJhbnMsRlNDX1NTQ190cmFucykgI2NvbWJpbmUgYm90aCB0cmFuc2Zvcm1hdGlvbnMKCnRyYW5zZm9ybWVkX2dhdGluZ19zZXQgPC0gY3l0b190cmFuc2Zvcm0obXlfZ2F0aW5nX3NldCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnMgPSBjb21iaW5lZF90cmFucykgI2FwcGxpZXMgdGhlIHRoZSB0cmFuc2Zvcm1hdGlvbiBhbmQgcmV0dXJucyBpdCBhcyBhIGdhdGluZ1NldApgYGAKCgpDaGVjayB0aGUgdHJhbnNmb3JtZWQgZGF0YSBieSBwbG90dGluZwpgYGB7cn0KY3l0b19wbG90X2V4cGxvcmUodHJhbnNmb3JtZWRfZ2F0aW5nX3NldCwgI3Bsb3RzIGFsbCBGQ1MgZmlsZXMKICAgICAgICAgICAgICAgICAgY2hhbm5lbHNfeCA9ICJGU0MtQSIsCiAgICAgICAgICAgICAgICAgIGNoYW5uZWxzX3kgPSAiQjItQSIsCiAgICAgICAgICAgICAgICAgIGF4ZXNfbGltaXRzID0gImRhdGEiKQpgYGAKCgpTdWJzZXQgcm93cyB0byBwbG90IGFzIGRlc2lyZWQsIHVzaW5nIHRoZSByb3cgYXNzaWdubWVudCBpbiBgY3l0b19kZXRhaWxzKClgCmBgYHtyfQpjeXRvX2RldGFpbHModHJhbnNmb3JtZWRfZ2F0aW5nX3NldCkKCmN5dG9fcGxvdF9leHBsb3JlKHRyYW5zZm9ybWVkX2dhdGluZ19zZXRbYygyLDMpXSwgI2hlcmUgd2Ugc3BlY2lmeSB0byBwbG90IHJvd3MgMiBhbmQgMyAKICAgICAgICAgICAgICAgICAgY2hhbm5lbHNfeCA9ICJGU0MtQSIsCiAgICAgICAgICAgICAgICAgIGNoYW5uZWxzX3kgPSAiQjItQSIsCiAgICAgICAgICAgICAgICAgIGF4ZXNfbGltaXRzID0gImRhdGEiKQpgYGAKCiMjIyBEcmF3IEdhdGVzIHVzaW5nIENvbnRyb2wgU3RyYWlucyBhcyBhIEd1aWRlCgoqKk5vdGUqKjogaWYgeW91IGFscmVhZHkgaGF2ZSBhIGdhdGluZyB0ZW1wbGF0ZSBhbmQgZG9uJ3QgbmVlZCB0byBkcmF3IGdhdGVzLCBza2lwIHRoZSBgY3l0b19nYXRlX2RyYXcoKWAgc3RlcHMuICAKSW5zdGVhZCwgdXNlIGBjeXRvX2dhdGluZ1RlbXBsYXRlX2FwcGx5KClgIHRvIGFwcGx5IGEgYGdhdGluZ1RlbXBsYXRlLmNzdmAgdG8geW91ciBnYXRpbmcgc2V0LiAKYGBge3J9CmN5dG9fZ2F0aW5nVGVtcGxhdGVfYXBwbHkodHJhbnNmb3JtZWRfZ2F0aW5nX3NldCwgZ2F0aW5nVGVtcGxhdGU9cGFzdGUwKCAiY3l0ZWtfZ2F0aW5nXyIsdmVyc2lvbl9uYW1lLCIuY3N2IikpCmBgYAoKCiMjIyMjIFRvIERyYXcgR2F0ZXMKCioqSU5TVFJVQ1RJT05TKiogIAoKKiBBIG5ldyB3aW5kb3cgd2lsbCBwb3Agb3V0LiAgCiogRHJhdyB0aGUgZGVzaXJlZCBnYXRlLiAgCiogUHJlc3MgYGVzY2Agb24ga2V5Ym9hcmQgd2hlbiBmaW5pc2hlZCBkcmF3aW5nIHRvIHNhdmUgdGhlIGdhdGUuICAKKiBHYXRpbmcgY29vcmRpbmF0ZXMgd2lsbCBiZSBwbGFjZWQgaW4gYSBnYXRpbmdUZW1wbGF0ZS5jc3YgZmlsZSB3aGljaCBjYW4gYmUgYXBwbGllZCAod2l0aG91dCBkcmF3aW5nIGFnYWluKSBpbiBmdXR1cmUgY29kZSBydW5zLgoKRmlyc3Qgd2UgZ2F0ZSBmb3IgdGhlIGNlbGxzLCBmaWx0ZXJpbmcgb3V0IGNlbGwgZGVicmlzLCBiYWN0ZXJpYSwgZXRjLiAKYGBge3J9CmN5dG9fZ2F0ZV9kcmF3KHRyYW5zZm9ybWVkX2dhdGluZ19zZXQsCiAgICAgICAgICAgICAgIHBhcmVudCA9ICJyb290IiwKICAgICAgICAgICAgICAgYWxpYXMgPSAiQ2VsbHMiLAogICAgICAgICAgICAgICBjaGFubmVscyA9IGMoIkZTQy1BIiwiU1NDLUEiKSwKICAgICAgICAgICAgICAgYXhlc19saW1pdHMgPSAiZGF0YSIsCiAgICAgICAgICAgICAgIGdhdGluZ1RlbXBsYXRlID0gcGFzdGUwKCJjeXRla19nYXRpbmdfIix2ZXJzaW9uX25hbWUsIi5jc3YiKQopCmBgYAohW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvaW9WdTd3TmRvNFRCUkswVFBFL2dpcGh5LmdpZikKICAKICAKICBUaGVuIHdlIGRlZmluZSB0aGUgc2luZ2xldHMgYmFzZWQgb24gc2lkZS1zY2F0dGVyIGFyZWEgYW5kIGhlaWdodC4KU2VlIG90aGVyIHdheXMgdG8gZGlzY3JpbWluYXRlIGRvdWJsZXRzOiBodHRwczovL2V4cGVydC5jaGVla3lzY2llbnRpc3QuY29tL2hvdy10by1wZXJmb3JtLWRvdWJsZXQtZGlzY3JpbWluYXRpb24taW4tZmxvdy1jeXRvbWV0cnkvIApgYGB7cn0KY3l0b19nYXRlX2RyYXcodHJhbnNmb3JtZWRfZ2F0aW5nX3NldCwKICAgICAgICAgICAgICAgcGFyZW50ID0gIkNlbGxzIiwKICAgICAgICAgICAgICAgYWxpYXMgPSAiU2luZ2xlX2NlbGxzIiwKICAgICAgICAgICAgICAgY2hhbm5lbHMgPSBjKCJTU0MtQSIsIlNTQy1IIiksCiAgICAgICAgICAgICAgIGF4ZXNfbGltaXRzID0gImRhdGEiLAogICAgICAgICAgICAgICBnYXRpbmdUZW1wbGF0ZSA9IHBhc3RlMCgiY3l0ZWtfZ2F0aW5nXyIsdmVyc2lvbl9uYW1lLCIuY3N2IikKKQpgYGAKIVtdKGh0dHBzOi8vbWVkaWEuZ2lwaHkuY29tL21lZGlhL2s3OVRtMTR1WktzRlVPcENXNi9naXBoeS5naWYpCgoKCkdhdGluZyBmb3IgQ05WcyB1c2luZyB0aGUgMCwxIGFuZCAyIGNvcHkgY29udHJvbHM6ICAKSW4gYGN5dG9fZXh0cmFjdCgpYCwgd2UgY2FuIHN1YnNldCBhbmQgZXh0cmFjdCB0aGUgcm93cyBvZiBpbnRlcmVzdC4gSGVyZSwgd2UgdXNlIGBjeXRvX2V4dHJhY3QoKWAgdG8KZXh0cmFjdCB0aGUgMCwgMSwgYW5kIDIgY29weSBjb250cm9sIHBvcHVsYXRpb25zIGluIG9yZGVyIHRvIG92ZXJsYXkgdGhlbSBvbiBhIHBsb3Qgd2hlbiBkcmF3aW5nIGdhdGVzLiAKVGhlIHJvdyBhc3NpZ25tZW50IGNvcnJlc3BvbmQgdG8gdGhlIGV4cGVyaW1lbnRhbCBkZXRhaWxzIGFzc29jaWF0ZWQgd2l0aCB0aGUgZ2F0aW5nIHNldC4gIApJbiBgY3l0b19nYXRlX2RyYXcoKWAsIGNob29zZSBjb2xvcnMgb2YgdGhlIHBhcmVudCBwb3B1bGF0aW9uIGFuZCB0aGUgb3ZlcmxheSBwb3B1bGF0aW9ucy4gIApJbiBgY3l0b19kYXRlX2RyYXcoKWAsIHRoZSBgYWxpYXNgIGFyZ3VtZW50IGxldHMgeW91IGNob29zZSBnYXRlIG5hbWVzLgpgYGB7cn0KY3l0b19kZXRhaWxzKHRyYW5zZm9ybWVkX2dhdGluZ19zZXQpICNWaWV3IHRoZSByb3dzIGFuZCBjaG9vc2Ugd2hpY2ggdG8gZXh0cmFjdApgYGAKCmBgYHtyfQp6ZXJvX2N0cmwgPC0gY3l0b19leHRyYWN0KHRyYW5zZm9ybWVkX2dhdGluZ19zZXQsICJTaW5nbGVfY2VsbHMiKVtjKDEpXSAjIHJvdyBvZiAwIGNvcHkgY29udHJvbCBkYXRhCgpvbmVfY3RybCA8LSBjeXRvX2V4dHJhY3QodHJhbnNmb3JtZWRfZ2F0aW5nX3NldCwgIlNpbmdsZV9jZWxscyIpW2MoMildICMgcm93IG9mIDEgY29weSBjb250cm9sIGRhdGEKCnR3b19jdHJsIDwtIGN5dG9fZXh0cmFjdCh0cmFuc2Zvcm1lZF9nYXRpbmdfc2V0LCAiU2luZ2xlX2NlbGxzIilbYyg0KV0gIyByb3cgb2YgMiBjb3B5IGNvbnRyb2wgZGF0YQoKY3l0b19nYXRlX2RyYXcodHJhbnNmb3JtZWRfZ2F0aW5nX3NldCwKICAgICAgICAgICAgICAgcG9pbnRfY29sID0gYygiZ3JheSIsICJncmVlbiIsICJibGFjayIsICJibHVlIiksICNjaG9vc2UgY29sb3JzCiAgICAgICAgICAgICAgIHBhcmVudCA9ICJTaW5nbGVfY2VsbHMiLCAjbWFwcyB0byBmaXJzdCBwb2ludCBjb2xvcgogICAgICAgICAgICAgICBvdmVybGF5ID0gYyh6ZXJvX2N0cmwsIG9uZV9jdHJsLCB0d29fY3RybCksI21hcHMgdG8gcmVtYWluaW5nIHBvaW50IGNvbG9ycwogICAgICAgICAgICAgICBhbGlhcyA9IGMoInplcm9fY29weSIsICJvbmVfY29weSIsICJ0d29fb3JfbW9yZV9jb3B5IiksICNOYW1lIHRoZSBnYXRlIG5hbWVzIGhlcmUKICAgICAgICAgICAgICAgY2hhbm5lbHMgPSBjKCJGU0MtQSIsIkIyLUEiKSwKICAgICAgICAgICAgICAgYXhlc19saW1pdHMgPSAiZGF0YSIsCiAgICAgICAgICAgICAgIGdhdGluZ1RlbXBsYXRlID0gcGFzdGUwKCJjeXRla19nYXRpbmdfIix2ZXJzaW9uX25hbWUsIi5jc3YiKQopCmBgYAoKCiFbXShodHRwczovL21lZGlhLmdpcGh5LmNvbS9tZWRpYS9GUHg1eXpQcHlEemVreVU2YTAvZ2lwaHkuZ2lmKQoKCiMjIFNURVAgNDogR2V0IHN0YXRpc3RpY3MKCkdldCBjZWxsIGNvdW50cyBpbiBlYWNoIGdhdGUKYGBge3J9CmNvdW50cyA9IGdzX3BvcF9nZXRfc3RhdHModHJhbnNmb3JtZWRfZ2F0aW5nX3NldCwgYygiU2luZ2xlX2NlbGxzIiwgInplcm9fY29weSIsICJvbmVfY29weSIsICJ0d29fb3JfbW9yZV9jb3B5IikpICU+JQogICAgcmVuYW1lKEdhdGUgPSBwb3AsIG5hbWUgPSBzYW1wbGUsIENvdW50ID0gY291bnQpICU+JQogICAgbGVmdF9qb2luKGV4cGVyaW1lbnRfZGV0YWlscykgJT4lCiAgICB3cml0ZV9jc3YocGFzdGUwKHZlcnNpb25fbmFtZSwiX2NvdW50cy5jc3YiKSkKCiNjb3VudHMgPSByZWFkX2NzdihwYXN0ZTAodmVyc2lvbl9uYW1lLCJfY291bnRzLmNzdiIpKQpgYGAKCkdldCBmcmVxdWVuY3kgb2YgY2VsbHMgaW5zaWRlIGVhY2ggZ2F0ZQpgYGB7cn0gIApmcmVxID0gZ3NfcG9wX2dldF9zdGF0cyh0cmFuc2Zvcm1lZF9nYXRpbmdfc2V0LCBjKCJTaW5nbGVfY2VsbHMiLCJ6ZXJvX2NvcHkiLCAib25lX2NvcHkiLCAidHdvX29yX21vcmVfY29weSIpLCB0eXBlID0gInBlcmNlbnQiKSAlPiUKICAgIHJlbmFtZShHYXRlID0gcG9wLCBuYW1lID0gc2FtcGxlLCBGcmVxdWVuY3kgPSBwZXJjZW50KSAlPiUKICAgIGxlZnRfam9pbihleHBlcmltZW50X2RldGFpbHMpICU+JQogICAgd3JpdGVfY3N2KHBhc3RlMCh2ZXJzaW9uX25hbWUsIl9mcmVxLmNzdiIpKQoKI2ZyZXEgPSByZWFkX2NzdihwYXN0ZTAodmVyc2lvbl9uYW1lLCJfZnJlcS5jc3YiKSkKYGBgCgpDaGVjayB0aGF0IHRoZXJlIGFyZSBhdCBsZWFzdCA3MCwwMDAgc2luZ2xlIGNlbGxzIHBlciBzYW1wbGUKYGBge3J9CmZyZXFfYW5kX2NvdW50cyA9CiAgY291bnRzICU+JSBmaWx0ZXIoR2F0ZSA9PSAiU2luZ2xlX2NlbGxzIikgJT4lCiAgcmVuYW1lKFBhcmVudCA9IEdhdGUpICU+JQogIGxlZnRfam9pbihmcmVxKSAlPiUKICBmaWx0ZXIoIShHYXRlID09ICJTaW5nbGVfY2VsbHMiKSkgJT4lCiAgbXV0YXRlKEZyZXF1ZW5jeSA9IEZyZXF1ZW5jeSoxMDApICU+JQogIHJlbG9jYXRlKDI6MywgLmFmdGVyID0gR2F0ZSkKYGBgCgpgYGB7cn0KZnJlcV9hbmRfY291bnRzICU+JQogIGRpc3RpbmN0KFN0cmFpbiwgQ291bnQpCmBgYAoKUGxvdCBmcmVxdWVuY3kgb2YgY2VsbHMgaW4gZ2F0ZXMgcGVyIHNhbXBsZQpgYGB7cn0KZnJlcV9iYXIgPSBmcmVxX2FuZF9jb3VudHMgJT4lCiAgbXV0YXRlKEdhdGUgPSBmY3RfcmVsZXZlbChHYXRlLCBjKCJ6ZXJvX2NvcHkiLCAib25lX2NvcHkiLCAidHdvX29yX21vcmVfY29weSIpKSwKICAgICAgICAgU3RyYWluPWZjdF9yZWxldmVsKFN0cmFpbiwgYygiREdZMSIsIkRHWTUwMCIsIkRHWTE2NTciLCJER1kxMzE1IikpLAogICAgICAgICBEZXNjcmlwdGlvbj1mY3RfcmVsZXZlbChEZXNjcmlwdGlvbiwgYygiMCBjb3B5IGNvbnRyb2wiLCAiMSBjb3B5IGNvbnRyb2wiLCJDTlYgcmVwb3J0ZXIgc3RyYWluIiwiMiBjb3B5IGNvbnRyb2wiKSkKICAgICAgICAgKSU+JQogIGZpbHRlcihDb3VudD43MDAwMCkgJT4lCiAgZ2dwbG90KGFlcyhEZXNjcmlwdGlvbiwgRnJlcXVlbmN5LCBmaWxsID0gR2F0ZSkpICsKICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsICJHcmVlbnMiKSkpICsKICB5bGFiKCIlIG9mIGNlbGxzIGluIGdhdGUiKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKZnJlcV9iYXIKYGBgCkdldCByYXcgdHJhbnNmb3JtZWQgYEIyLUFgIGFuZCBgRlNDYCB2YWx1ZXMgZm9yIGV2ZXJ5IGNlbGwKYGBge3J9CnRpbWVwb2ludF9yYXdfbGlzdCA8LSBjeXRvX2V4dHJhY3QodHJhbnNmb3JtZWRfZ2F0aW5nX3NldCwgcGFyZW50ID0gIlNpbmdsZV9jZWxscyIsIHJhdyA9IFRSVUUsIGNoYW5uZWxzID0gYygiRlNDLUEiLCAiQjItQSIpKSAjcmF3IGZsb3cgZGF0YSBvZiBlYWNoIGNlbGwgYXMgYSBsaXN0IG9mIG1hdHJpY2VzCiAgCnNjX2Rpc3RyID0gbWFwX2RmKHRpbWVwb2ludF9yYXdfbGlzdCwgfmFzLmRhdGEuZnJhbWUoLngpLCAuaWQ9Im5hbWUiKSAlPiUgI2NvbnZlcnQgdG8gZGYsIHB1dCBsaXN0IG5hbWUgaW4gbmV3IGNvbHVtbgogICBsZWZ0X2pvaW4oZXhwZXJpbWVudF9kZXRhaWxzKSAlPiUgI2pvaW4gYnkgbmFtZSBjb2x1bW4gdG8gYWRkIG1ldGFkYXRhCiAgIG11dGF0ZShCMkFfRlNDID0gYEIyLUFgL2BGU0MtQWApICU+JSAjY29tcHV0ZSBub3JtYWxpemVkIGZsdW9yZXNjZW5jZSBmb3IgZXZlcnkgY2VsbAogICB3cml0ZV9jc3YocGFzdGUwKHZlcnNpb25fbmFtZSwiX1NpbmdsZUNlbGxEaXN0cmlidXRpb25zLmNzdiIpKQoKc2NfZGlzdHIgPSByZWFkX2NzdihwYXN0ZTAodmVyc2lvbl9uYW1lLCJfU2luZ2xlQ2VsbERpc3RyaWJ1dGlvbnMuY3N2IikpCmBgYAoKIyMgU1RFUCA1OiBNYWtlIFBsb3RzCioqR3JhcGggc2luZ2xlIGNlbGwgZmx1b3Jlc2NlbmNlIGRpc3RyaWJ1dGlvbnMgKHJpZGdlcGxvdHMpIGZvciBlYWNoIHNhbXBsZSoqCmBgYHtyfQpzY19kaXN0ciA9IHNjX2Rpc3RyICU+JSAKICBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLGFzLmZhY3RvcikgJT4lICNjaGFuZ2UgYWxsIHN0cmluZ3MgY29sdW1ucyB0byBmYWN0b3JzIAogIG11dGF0ZShTdHJhaW49ZmN0X3JlbGV2ZWwoU3RyYWluLGMoIkRHWTEiLCJER1k1MDAiLCJER1kxNjU3IiwiREdZMTMxNSIpKSwKICAgICAgICBgbUNpdHJpbmUgY29weSBudW1iZXJgPWZjdF9yZWxldmVsKGBtQ2l0cmluZSBjb3B5IG51bWJlcmAsIGMoInplcm8gY29weSIsIm9uZSBjb3B5IiwgInR3byBjb3B5IiApKSwKICAgICAgICBEZXNjcmlwdGlvbj1mY3RfcmVsZXZlbChEZXNjcmlwdGlvbiwgYygiMCBjb3B5IGNvbnRyb2wiLCAiMSBjb3B5IGNvbnRyb2wiLCJDTlYgcmVwb3J0ZXIgc3RyYWluIiwiMiBjb3B5IGNvbnRyb2wiKSkKICAgICAgICApCgpzY19kaXN0ciAlPiUKZ2dwbG90KGFlcyhCMkFfRlNDLCBEZXNjcmlwdGlvbiwgZmlsbCA9IGBtQ2l0cmluZSBjb3B5IG51bWJlcmApKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhzY2FsZSA9IDEpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbihhZGQgPSBjKDAuMiwgMS4wKSkpKwogICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9IGMoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsICJHcmVlbnMiKSkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoIm5vcm1hbGl6ZWQgZmx1b3Jlc2NlbmNlIiwgbGltaXRzPWMoMCwgMy4wKSwgYnJlYWtzID0gYygwLCAxLCAyLCAzKSwgbGFiZWxzID0gYygwLDEsMiwzKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhbWlseT0iQXJpYWwiLCBzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIiksICNlZGl0IHgtdGljayBsYWJlbHMKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoZmFtaWx5PSJBcmlhbCIsIHNpemUgPSAxMCwgY29sb3IgPSAiYmxhY2siKSwKICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgI3JlbW92ZSBib3ggYXJvdW5kIGZhY2V0IHRpdGxlCiAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMikKICApCmBgYAoqKkdyYXBoIGJveHBsb3RzIG9mIG5vcm1hbGl6ZWQgZmx1b3Jlc2NlbmNlIHBlciBzYW1wbGUqKgpgYGB7ciBlY2hvPVRSVUV9CmdncGxvdChzY19kaXN0ciwgYWVzKFN0cmFpbiwgQjJBX0ZTQywgZmlsbCA9IGBtQ2l0cmluZSBjb3B5IG51bWJlcmApKSArIAogIGdlb21fYm94cGxvdCgpICsKICB5bGFiKCJub3JtYWxpemVkIGZsdW9yZXNjZW5jZSIpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz0gYyhSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMywgIkdyZWVucyIpKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoCiAgICAgICNsZWdlbmQucG9zaXRpb24gPSAnbm9uZScsICNyZW1vdmUgdGhlIGxlZ2VuZAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYW1pbHk9IkFyaWFsIiwgc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLCAjZWRpdCB4LXRpY2sgbGFiZWxzCiAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhbWlseT0iQXJpYWwiLCBzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIiksCiAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksICNyZW1vdmUgYm94IGFyb3VuZCBmYWNldCB0aXRsZQogICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpCiAgKQpgYGAKCg==